fix: NUXT_PUBLIC_APP_ID 改为数字主键,接口基址切正式域名

1. appId 语义修正
   NUXT_PUBLIC_APP_ID 之前填的是 productCode('website'),但下游拿它去请求
   /api/app/product/detail/{productId},后端按 Integer 解析主键,于是转换失败。
   改为 app_product.product_id 数字主键(种子数据为 42);nuxt.config.ts 的
   缺省值同步去掉 'website' 回退——留空时下游按空值降级,绝不回退到编码值。

2. 接口基址切 hjc-api.sitelnk.cn
   .env.example / .env.production / nuxt.config.ts 的缺省值与 preconnect、
   dns-prefetch 一并从 hjc-api.websoft.top 改为正式域名;scripts/ 与
   outputs/ 文档里的旧地址同步更新。

注:本提交含若干文件的 CRLF/LF 行尾归一,实际内容改动见 git diff --ignore-cr-at-eol。
This commit is contained in:
2026-09-25 05:22:58 +08:00
parent 3d3a377221
commit 4a5a8f306a
12 changed files with 582 additions and 564 deletions
+141 -141
View File
@@ -1,141 +1,141 @@
#!/usr/bin/env node
/**
* CMS 内容批量发布脚本
* ============================================================================
* 背景(2026-08-05):
* website-admin 后台的状态枚举与 CMS 后端语义相反——后台点「发布 / 上架」写入
* status=1,而 CMS 权威语义是 0=已发布、1=待审核、2=已下线,
* C 端 website-template(server/api/{article,product,case}/list.get.ts)只查
* status=0,于是后台明明「已发布」,官网却一条都不显示。
*
* 后台枚举已在本次一并修正,但存量数据仍停在 status=1,需要用本脚本批量刷成 0。
*
* 用法:
* # 1) 先空跑,只打印将要修改的内容,不写任何数据
* ADMIN_TOKEN="eyJ..." TENANT_ID=10546 node scripts/publish-cms-content.mjs
*
* # 2) 确认无误后真正执行
* ADMIN_TOKEN="eyJ..." TENANT_ID=10546 APPLY=1 node scripts/publish-cms-content.mjs
*
* # 只处理文章
* ADMIN_TOKEN="eyJ..." TENANT_ID=10546 MODULES=article APPLY=1 node scripts/publish-cms-content.mjs
*
* token 获取:后台 DevTools → Network → 任意 XHR → Request Headers → Authorization
* (连同 "Bearer " 前缀一起复制即可,脚本会自动补全)
* ============================================================================
*/
const API_BASE = process.env.CMS_API_BASE || 'https://hjc-api.websoft.top/api'
const TOKEN_RAW = process.env.ADMIN_TOKEN || ''
const TENANT_ID = process.env.TENANT_ID || '10546'
const APPLY = process.env.APPLY === '1'
const MODULES = (process.env.MODULES || 'article,product,case')
.split(',')
.map((s) => s.trim())
.filter(Boolean)
/** CMS 权威语义:0=已发布 / 在售(C 端可见) */
const PUBLISHED = 0
/** 各模块的接口路径与主键字段 */
const MODULE_CONF = {
article: { path: 'cms-article', idKey: 'articleId', titleKey: 'title' },
product: { path: 'cms-product', idKey: 'productId', titleKey: 'productName' },
case: { path: 'cms-case', idKey: 'caseId', titleKey: 'title' }
}
if (!TOKEN_RAW) {
console.error('❌ 缺少 ADMIN_TOKEN。')
console.error(' ADMIN_TOKEN="eyJ..." TENANT_ID=10546 node scripts/publish-cms-content.mjs')
process.exit(1)
}
const TOKEN = TOKEN_RAW.startsWith('Bearer ') ? TOKEN_RAW : `Bearer ${TOKEN_RAW}`
const headers = {
'Content-Type': 'application/json',
Authorization: TOKEN,
TenantId: String(TENANT_ID)
}
async function fetchList(conf) {
const url = `${API_BASE}/cms/${conf.path}/page?page=1&limit=200`
const res = await fetch(url, { headers })
const json = await res.json().catch(() => null)
if (!json || json.code !== 0) {
throw new Error(`拉取列表失败 (${res.status}): ${JSON.stringify(json)?.slice(0, 200)}`)
}
return json.data?.list || []
}
async function updateStatus(conf, id) {
const url = `${API_BASE}/cms/${conf.path}/status`
const res = await fetch(url, {
method: 'PUT',
headers,
body: JSON.stringify({ [conf.idKey]: id, status: PUBLISHED })
})
const text = await res.text()
let json = null
try { json = JSON.parse(text) } catch { /* 非 JSON 响应 */ }
return { ok: res.ok && (json === null || json.code === 0), status: res.status, body: text.slice(0, 200) }
}
async function run() {
console.log(`租户 ${TENANT_ID} | 模块 ${MODULES.join(', ')} | 模式 ${APPLY ? '实际执行' : '空跑预演'}`)
console.log('─'.repeat(70))
let totalPending = 0
let totalOk = 0
let totalFail = 0
for (const mod of MODULES) {
const conf = MODULE_CONF[mod]
if (!conf) {
console.log(`跳过未知模块 ${mod}`)
continue
}
let list
try {
list = await fetchList(conf)
} catch (e) {
console.log(`\n[${mod}] 拉取失败:${e.message}`)
continue
}
const pending = list.filter((it) => Number(it.status) !== PUBLISHED)
console.log(`\n[${mod}] 共 ${list.length} 条,其中 ${pending.length} 条未发布(status !== 0)`)
totalPending += pending.length
for (const it of pending) {
const id = it[conf.idKey] ?? it.id
const title = String(it[conf.titleKey] || it.title || '(无标题)').slice(0, 30)
if (!APPLY) {
console.log(` · [预演] ${id} status=${it.status} → 0 | ${title}`)
continue
}
const r = await updateStatus(conf, id)
if (r.ok) {
totalOk++
console.log(` ✓ ${id} → 已发布 | ${title}`)
} else {
totalFail++
console.log(` ✗ ${id} 失败 HTTP ${r.status} ${r.body} | ${title}`)
}
}
}
console.log('\n' + '─'.repeat(70))
if (APPLY) {
console.log(`完成:成功 ${totalOk} 条,失败 ${totalFail} 条`)
console.log('前台若有缓存,等待缓存过期或重启 Node 进程后即可看到内容。')
} else {
console.log(`空跑结束:共 ${totalPending} 条待发布。加 APPLY=1 重新运行即可实际写入。`)
}
}
run().catch((e) => {
console.error('执行出错:', e)
process.exit(1)
})
#!/usr/bin/env node
/**
* CMS 内容批量发布脚本
* ============================================================================
* 背景(2026-08-05):
* website-admin 后台的状态枚举与 CMS 后端语义相反——后台点「发布 / 上架」写入
* status=1,而 CMS 权威语义是 0=已发布、1=待审核、2=已下线,
* C 端 website-template(server/api/{article,product,case}/list.get.ts)只查
* status=0,于是后台明明「已发布」,官网却一条都不显示。
*
* 后台枚举已在本次一并修正,但存量数据仍停在 status=1,需要用本脚本批量刷成 0。
*
* 用法:
* # 1) 先空跑,只打印将要修改的内容,不写任何数据
* ADMIN_TOKEN="eyJ..." TENANT_ID=10546 node scripts/publish-cms-content.mjs
*
* # 2) 确认无误后真正执行
* ADMIN_TOKEN="eyJ..." TENANT_ID=10546 APPLY=1 node scripts/publish-cms-content.mjs
*
* # 只处理文章
* ADMIN_TOKEN="eyJ..." TENANT_ID=10546 MODULES=article APPLY=1 node scripts/publish-cms-content.mjs
*
* token 获取:后台 DevTools → Network → 任意 XHR → Request Headers → Authorization
* (连同 "Bearer " 前缀一起复制即可,脚本会自动补全)
* ============================================================================
*/
const API_BASE = process.env.CMS_API_BASE || 'https://hjc-api.sitelnk.cn/api'
const TOKEN_RAW = process.env.ADMIN_TOKEN || ''
const TENANT_ID = process.env.TENANT_ID || '10546'
const APPLY = process.env.APPLY === '1'
const MODULES = (process.env.MODULES || 'article,product,case')
.split(',')
.map((s) => s.trim())
.filter(Boolean)
/** CMS 权威语义:0=已发布 / 在售(C 端可见) */
const PUBLISHED = 0
/** 各模块的接口路径与主键字段 */
const MODULE_CONF = {
article: { path: 'cms-article', idKey: 'articleId', titleKey: 'title' },
product: { path: 'cms-product', idKey: 'productId', titleKey: 'productName' },
case: { path: 'cms-case', idKey: 'caseId', titleKey: 'title' }
}
if (!TOKEN_RAW) {
console.error('❌ 缺少 ADMIN_TOKEN。')
console.error(' ADMIN_TOKEN="eyJ..." TENANT_ID=10546 node scripts/publish-cms-content.mjs')
process.exit(1)
}
const TOKEN = TOKEN_RAW.startsWith('Bearer ') ? TOKEN_RAW : `Bearer ${TOKEN_RAW}`
const headers = {
'Content-Type': 'application/json',
Authorization: TOKEN,
TenantId: String(TENANT_ID)
}
async function fetchList(conf) {
const url = `${API_BASE}/cms/${conf.path}/page?page=1&limit=200`
const res = await fetch(url, { headers })
const json = await res.json().catch(() => null)
if (!json || json.code !== 0) {
throw new Error(`拉取列表失败 (${res.status}): ${JSON.stringify(json)?.slice(0, 200)}`)
}
return json.data?.list || []
}
async function updateStatus(conf, id) {
const url = `${API_BASE}/cms/${conf.path}/status`
const res = await fetch(url, {
method: 'PUT',
headers,
body: JSON.stringify({ [conf.idKey]: id, status: PUBLISHED })
})
const text = await res.text()
let json = null
try { json = JSON.parse(text) } catch { /* 非 JSON 响应 */ }
return { ok: res.ok && (json === null || json.code === 0), status: res.status, body: text.slice(0, 200) }
}
async function run() {
console.log(`租户 ${TENANT_ID} | 模块 ${MODULES.join(', ')} | 模式 ${APPLY ? '实际执行' : '空跑预演'}`)
console.log('─'.repeat(70))
let totalPending = 0
let totalOk = 0
let totalFail = 0
for (const mod of MODULES) {
const conf = MODULE_CONF[mod]
if (!conf) {
console.log(`跳过未知模块 ${mod}`)
continue
}
let list
try {
list = await fetchList(conf)
} catch (e) {
console.log(`\n[${mod}] 拉取失败:${e.message}`)
continue
}
const pending = list.filter((it) => Number(it.status) !== PUBLISHED)
console.log(`\n[${mod}] 共 ${list.length} 条,其中 ${pending.length} 条未发布(status !== 0)`)
totalPending += pending.length
for (const it of pending) {
const id = it[conf.idKey] ?? it.id
const title = String(it[conf.titleKey] || it.title || '(无标题)').slice(0, 30)
if (!APPLY) {
console.log(` · [预演] ${id} status=${it.status} → 0 | ${title}`)
continue
}
const r = await updateStatus(conf, id)
if (r.ok) {
totalOk++
console.log(` ✓ ${id} → 已发布 | ${title}`)
} else {
totalFail++
console.log(` ✗ ${id} 失败 HTTP ${r.status} ${r.body} | ${title}`)
}
}
}
console.log('\n' + '─'.repeat(70))
if (APPLY) {
console.log(`完成:成功 ${totalOk} 条,失败 ${totalFail} 条`)
console.log('前台若有缓存,等待缓存过期或重启 Node 进程后即可看到内容。')
} else {
console.log(`空跑结束:共 ${totalPending} 条待发布。加 APPLY=1 重新运行即可实际写入。`)
}
}
run().catch((e) => {
console.error('执行出错:', e)
process.exit(1)
})