feat(app): 添加多模板关于我们页面及相关路由和404页面

- 新增404页面,优化未找到页面体验,避免被搜索引擎索引
- 增加文件代理接口,隐藏真实文件服务器地址,支持文件请求代理
- 实现/article、/case、/product及/page动态路由兼容列表与详情展示
- 添加动态CMS页面兼容入口处理旧式路径,统一路由与SEO设置
- 新增模板1、模板7、模板2、模板3关于我们页面,实现多模板支持
- 模板增强支持CMS单页内容加载及SEO信息动态设置
- 配置环境变量及Git忽略文件规则辅助开发和构建环境管理
This commit is contained in:
2026-09-08 12:13:44 +08:00
commit 2b69686795
381 changed files with 59891 additions and 0 deletions
+26
View File
@@ -0,0 +1,26 @@
-- =============================================================
-- cms_website_setting 扩展列:首页「优势模块」配置
--
-- 背景:
-- 前台 website-template 的 FeatureSection(我们的优势/核心业务卡片区)
-- 原为各模板硬编码。现改为后台可配置:标题、副标题、最多 4 张卡片
-- (标题/描述/图标名)。配置存于 cms_website_setting.featuresJSON 字符串)。
--
-- 注意:cms_website.setting 是 @TableField(exist=false) 的 transient 字段,
-- 走 PUT /api/cms/cms-website 写不进库;故必须用 cms_website_setting 表。
--
-- 前端读取路径:
-- GET /api/site/info → /cms/cms-website/getSiteInfo
-- 后端需把 cms_website_setting.features 合并进返回体的 setting 字段
-- (与 searchBtn / loginBtn 等现有开关同口径),前台 useFeatures() 解析。
-- =============================================================
ALTER TABLE cms_website_setting
ADD COLUMN features TEXT NULL
COMMENT '首页优势模块配置(JSON): {enabled,title,subtitle,items:[{title,desc,icon}]}';
-- 兼容回填(可选):若老数据曾存进 cms_website.setting 的 features,可迁移:
-- UPDATE cms_website_setting s
-- JOIN cms_website w ON s.website_id = w.website_id
-- SET s.features = JSON_UNQUOTE(JSON_EXTRACT(w.setting, '$.features'))
-- WHERE JSON_EXTRACT(w.setting, '$.features') IS NOT NULL;
+68
View File
@@ -0,0 +1,68 @@
# 首页优势模块 - 后端对接说明
前端(website-template)与后台(website-admin)已改造完成,数据通过 `cms_website_setting.features` 字段贯通。
本文件说明 **Java 后端** 需要补齐的两处改动,否则后台配置无法真正持久化 / 下发给前台。
## 1. 数据库
执行 `database/add-website-setting-features.sql`
```sql
ALTER TABLE cms_website_setting
ADD COLUMN features TEXT NULL
COMMENT '首页优势模块配置(JSON)';
```
## 2. 实体 `CmsWebsiteSetting`
`com.gxwebsoft.cms.entity.CmsWebsiteSetting` 增加字段(MyBatis-Plus 自动建表/映射):
```java
@TableField("features")
private String features; // 首页优势模块配置 JSON 字符串
```
并在对应的 VO / 返回对象中保留该字段。
## 3. `getSiteInfo` 合并 features 到 setting
`getSiteInfo` 当前已把 `cms_website_setting``searchBtn / loginBtn / ...` 合并进返回体的 `setting`
(前台 `siteInfo.setting` 即来源)。**新增**:把 `features` 也合并进去。
伪代码:
```java
// 在组装 setting 对象处,追加
setting.put("features", websiteSetting != null ? websiteSetting.getFeatures() : null);
```
前端 `useFeatures()` 读取 `siteInfo.setting.features``JSON.parse`
若后端未合并,前台拿不到 `features` → 自动回退各模板默认数据(不影响页面展示,仅配置不生效)。
## 4. 数据格式约定
`features` 为 JSON 字符串,结构:
```json
{
"enabled": true,
"title": "我们的优势",
"subtitle": "为企业提供从品牌展示到客户转化的全链路官网解决方案",
"items": [
{ "title": "企业优势", "desc": "...", "icon": "building" },
{ "title": "核心产品", "desc": "...", "icon": "box" },
{ "title": "客户案例", "desc": "...", "icon": "case" },
{ "title": "在线留言", "desc": "...", "icon": "message" }
]
}
```
- `icon` 为图标名,取值范围为前台 `FeatureIcon.vue` 的 36 个名称,后台下拉已内置。
- `items` 最多 4 个(前后台均做了截断)。
- `enabled=false` 时前台隐藏整块。
## 5. 验证清单
1. 后台「网站设置 → 功能开关 → 首页优势模块」修改标题 / 卡片并保存;
2.`GET /api/cms/cms-website/getSiteInfo` 确认返回 `setting.features` 非空;
3. 前台对应租户首页「优势卡片区」内容随之变化;关闭「启用模块」后整块消失。
@@ -0,0 +1,141 @@
-- =====================================================================
-- 模板主键与模板目录错位订正脚本
-- 事故:https://gxhrtc.shoplnk.cn/ 渲染成 template-09 墨绿出版风,
-- 实际应为 template-08 金棕企业风(广西恒瑞天诚,tenantId=10140
-- 日期:2026-08-03
--
-- 【根因】
-- 前端模板目录名曾按 app_template 自增主键补零推导(9 → template-09)。
-- 但后台 app_template 主键跳号(id=8 缺失),从第 8 套模板起主键与前端目录整体错位一位,
-- 且 code 字段当初也是跟着主键生成的,同样错位:
--
-- app_template 现状 前端 app/templates/ 实际内容
-- id=9 code=template-09 恒瑞天诚 ← 内容是金棕企业风,对应 template-08
-- id=10 code=template-10 墨绿出版模板 ← 对应 template-09
-- id=11 code=template-11 哈哈333 ← 测试脏数据,前端无此目录
--
-- 【配套代码改动(已完成,见 website-template 仓库)】
-- 前端解析改为「code 优先、主键兜底」:
-- - app/utils/index.ts 新增 resolveTemplateKey(code, id)
-- - server/utils/template-map.ts SSR 侧按 templateId 反查 app_template 拿权威 code
-- - server/middleware/tenant.ts 回填 siteInfo.templateCode
-- 因此只要本脚本把 code 修正到位,前端无需重新部署数据即可正确解析。
--
-- 【库分布】同一 MySQL 实例
-- db_websopy.app_template 模板库
-- modules.cms_website 站点表(template_id 指向 app_template.id
--
-- 【执行建议】
-- Step 1 必做、低风险,执行完 gxhrtc 即恢复正常(无需清 Redis)。
-- Step 2 可选、需停机窗口,执行后【必须清除 Redis 缓存】,否则会二次错位。
-- =====================================================================
-- ---------------------------------------------------------------------
-- Step 0. 备份(必做)
-- ---------------------------------------------------------------------
CREATE TABLE IF NOT EXISTS db_websopy.app_template_bak_20260803
AS SELECT * FROM db_websopy.app_template;
CREATE TABLE IF NOT EXISTS modules.cms_website_tplid_bak_20260803
AS SELECT website_id, tenant_id, website_name, template_id
FROM modules.cms_website
WHERE template_id IS NOT NULL;
-- 执行前先看一眼现状
SELECT id, code, name, comments FROM db_websopy.app_template ORDER BY id;
SELECT template_id, COUNT(*) AS site_count
FROM modules.cms_website
WHERE template_id IS NOT NULL
GROUP BY template_id ORDER BY template_id;
-- ---------------------------------------------------------------------
-- Step 1. 【必做】修正 code,使其与前端模板目录一一对应
--
-- ⚠️ 执行顺序不可颠倒:若 code 上有唯一索引,先腾空 template-09 再占用,
-- 否则 id=10 改为 template-09 时会与 id=9 的旧值冲突。
-- ---------------------------------------------------------------------
START TRANSACTION;
-- 1.1 id=9「恒瑞天诚」实为金棕企业模板 → 让出 template-09,改挂 template-08
-- 顺带把客户名改回模板语义名(模板库不应以单个客户命名;如需保留客户名可删掉 name/comments 两行)
UPDATE db_websopy.app_template
SET code = 'template-08',
name = '金棕企业模板',
comments = '金棕企业风,适合招标公告、工程建设、咨询服务类企业官网'
WHERE id = 9;
-- 1.2 id=10「墨绿出版模板」→ 接手已腾空的 template-09
UPDATE db_websopy.app_template
SET code = 'template-09',
comments = '墨绿文化出版风,适合出版社、文化传媒、图书发行、教育机构类官网'
WHERE id = 10;
-- 1.3 清理测试脏数据(逻辑删除,保留可恢复性;确认无用后再物理删除)
UPDATE db_websopy.app_template SET deleted = 1 WHERE id = 11 AND name = '哈哈333';
COMMIT;
-- 校验:code 应与前端 app/templates/ 目录一一对应(template-01 ~ template-09,无重复、无缺号)
SELECT id, code, name FROM db_websopy.app_template WHERE deleted = 0 ORDER BY code;
-- 到此为止,gxhrtccms_website.template_id = 9)经前端反查得到 code = 'template-08'
-- 渲染恢复为金棕企业风。SiteInfo Redis 缓存存的是 template_id 主键(未变),故无需清缓存;
-- 前端 SSR 侧模板映射有 10 分钟内存缓存,等待 10 分钟或重启 Node 进程即可生效。
-- ---------------------------------------------------------------------
-- Step 2.【可选】重排主键,使 id 数字 == code 数字(彻底消除歧义)
--
-- ⚠️⚠️ 高风险操作,务必在停机窗口执行,且严格按顺序:
-- 先改 cms_website 引用,再改 app_template 主键,否则中间态会指向错误模板。
-- ⚠️⚠️ 执行完必须清除 Redis 缓存 SiteInfo:*,否则站点仍返回旧 template_id
-- 反查新表会得到错误的 code(旧 9 → 新 9 = 墨绿),造成二次错位。
-- ---------------------------------------------------------------------
-- START TRANSACTION;
--
-- -- 2.1 先迁移站点引用(顺序:9→8 腾空 9,再 10→9)
-- UPDATE modules.cms_website SET template_id = 8 WHERE template_id = 9;
-- UPDATE modules.cms_website SET template_id = 9 WHERE template_id = 10;
-- UPDATE modules.cms_website SET template_id = NULL WHERE template_id = 11; -- 脏模板引用清空,回退默认模板
--
-- -- 2.2 再重排模板主键(同样先 9→8 腾空,再 10→9)
-- UPDATE db_websopy.app_template SET id = 8 WHERE id = 9;
-- UPDATE db_websopy.app_template SET id = 9 WHERE id = 10;
-- DELETE FROM db_websopy.app_template WHERE id = 11 AND name = '哈哈333';
--
-- -- 2.3 复位自增起点,避免后续新增再次跳号
-- ALTER TABLE db_websopy.app_template AUTO_INCREMENT = 10;
--
-- COMMIT;
--
-- -- 2.4 清除站点信息缓存(在服务器上执行,Redis 键前缀见 CmsWebsiteServiceImpl.SITE_INFO_KEY_PREFIX
-- -- redis-cli --scan --pattern 'SiteInfo:*' | xargs -r redis-cli del
--
-- -- 2.5 校验:id 数字必须等于 code 数字
-- SELECT id, code, name,
-- CASE WHEN id = CAST(SUBSTRING(code, 10) AS UNSIGNED) THEN 'OK' ELSE 'MISMATCH' END AS aligned
-- FROM db_websopy.app_template WHERE deleted = 0 ORDER BY id;
-- ---------------------------------------------------------------------
-- 回滚
-- ---------------------------------------------------------------------
-- Step 1 回滚:
-- UPDATE db_websopy.app_template t
-- JOIN db_websopy.app_template_bak_20260803 b ON b.id = t.id
-- SET t.code = b.code, t.name = b.name, t.comments = b.comments, t.deleted = b.deleted;
--
-- Step 2 回滚:先从 app_template_bak_20260803 整表还原 app_template
-- 再按 cms_website_tplid_bak_20260803 还原各站点的 template_id,最后清 Redis SiteInfo:*。
-- ---------------------------------------------------------------------
-- 【后续防复发】
-- 1. 新增模板时手工指定主键,强制 id 数字 == code 数字,禁止依赖自增跳号。
-- 2. 上游 getSiteInfo 建议增返 templateCode 字段(关联 app_template.code),
-- 前端 server/utils/template-map.ts 的反查会自动跳过,少一次上游请求。
-- 3. website-admin 的模板管理页「预览」按 code 解析、「选用」却提交 id,
-- 两者口径不一致是本次事故的放大器;建议选用也改为提交 code。
-- =====================================================================
@@ -0,0 +1,43 @@
-- ============================================================
-- 迁移:cms_website.template_id 字段语义变更
-- ------------------------------------------------------------
-- 旧语义:template_id 存的是「克隆来源租户UID」(如 10257),
-- 由 CmsWebsiteServiceImpl.create() 写入并用作建站克隆来源。
-- 新语义:template_id = 真正的模板IDcms_template.id1~6),
-- 由 CmsTemplateController.use() 按租户写入,前端据此渲染 template-XX。
-- 克隆来源改由新字段 clone_tenant_id 承载,与模板ID彻底解耦。
-- ============================================================
-- 1) 将原 template_id 列重命名为 clone_tenant_id(保留克隆来源,建站克隆逻辑依赖它)
ALTER TABLE cms_website
CHANGE COLUMN template_id clone_tenant_id INT(11) DEFAULT NULL
COMMENT '克隆来源租户UID(原 template_id 语义)';
-- 2) 新增真正的模板ID列(cms_template.id1~6
ALTER TABLE cms_website
ADD COLUMN template_id INT(11) DEFAULT NULL
COMMENT '模板ID(cms_template.id1~6,对应前端 template-XX)';
-- 3) 迁移存量数据:克隆来源租户UID → 对应 cms_template.id
-- 已知映射(各模板的克隆参考租户;如与实际库不符请按实调整):
UPDATE cms_website SET template_id = 5 WHERE clone_tenant_id = 10257; -- 10257 = template-05 参考租户
-- UPDATE cms_website SET template_id = ? WHERE clone_tenant_id = ?; -- 其余模板参考租户 → 对应 cms_template.id
-- 4) 核查:列出尚未迁移的存量行(template_id 为空但 clone_tenant_id 非空)
-- 这些站点在管理员于后台「模板选择器」重新选用一次后会自动写入正确 template_id
-- 在重新选用前会临时回落到默认模板(template-01),不会丢数据。
-- SELECT website_id, tenant_id, clone_tenant_id
-- FROM cms_website
-- WHERE template_id IS NULL AND clone_tenant_id IS NOT NULL;
-- ============================================================
-- 配套代码改动(已完成,供复核):
-- - CmsWebsite.java:新增 cloneTenantId 字段;templateId 注释更正为「模板ID」
-- - CmsWebsiteServiceImpl.create():克隆来源改用 cloneTenantId
-- 新站点 template_id 取克隆来源站点自身已选定的模板(迁移后= cms_template.id)
-- - CmsTemplateController.use()/current():改为读写 cms_website.template_id(按租户隔离),
-- 不再写 app_product.template_id
-- - 前端 useTemplate.ts:解析优先级调整为 站点(cms_website) > 应用(app_product)
-- 避免共享产品级残留值盖掉租户级选择
-- - 前端 utils/index.ts:移除临时 TEMPLATE_ID_MAPtoTemplateKey 直接支持 1~6
-- ============================================================
+181
View File
@@ -0,0 +1,181 @@
-- 满坛记导航栏 写入 cms_navigation (tenant=10619, top=0)
-- 生成时间 2026-07-18 13:38:21.516015
START TRANSACTION;
-- 顶级: 首页
INSERT INTO cms_navigation (type,title,parent_id,model,code,path,component,target,icon,banner,color,hide,permission,password,position,top,bottom,active,meta,style,parent_path,parent_name,model_name,page_id,item_id,lang_category_id,is_mp_weixin,gutter,span,read_num,user_id,merchant_id,lang,home,recommend,sort_number,comments,deleted,status,tenant_id) VALUES (0,'首页',0,'index','','/','/pages/index.vue','_self',NULL,NULL,NULL,0,0,NULL,2,0,1,NULL,'',NULL,NULL,NULL,'',0,0,0,0,0,24,10,0,0,'zh_CN',1,0,10,'',0,0,10619);
SET @t0 = LAST_INSERT_ID();
-- 顶级: 关于我们
INSERT INTO cms_navigation (type,title,parent_id,model,code,path,component,target,icon,banner,color,hide,permission,password,position,top,bottom,active,meta,style,parent_path,parent_name,model_name,page_id,item_id,lang_category_id,is_mp_weixin,gutter,span,read_num,user_id,merchant_id,lang,home,recommend,sort_number,comments,deleted,status,tenant_id) VALUES (0,'关于我们',0,'page','','/page/__ID__','/pages/page/[id].vue','_self',NULL,NULL,NULL,0,0,NULL,2,0,1,NULL,'',NULL,NULL,NULL,'',0,0,0,0,0,24,10,0,0,'zh_CN',0,0,20,'',0,0,10619);
SET @t1 = LAST_INSERT_ID();
UPDATE cms_navigation SET path=REPLACE(path,'__ID__',@t1) WHERE navigation_id=@t1;
-- └ 关于满坛记
INSERT INTO cms_navigation (type,title,parent_id,model,code,path,component,target,icon,banner,color,hide,permission,password,position,top,bottom,active,meta,style,parent_path,parent_name,model_name,page_id,item_id,lang_category_id,is_mp_weixin,gutter,span,read_num,user_id,merchant_id,lang,home,recommend,sort_number,comments,deleted,status,tenant_id) VALUES (0,'关于满坛记','@t1','page','','/page/__ID__','/pages/page/[id].vue','_self',NULL,NULL,NULL,0,0,NULL,2,0,1,NULL,'',NULL,NULL,NULL,'',0,0,0,0,0,24,10,0,0,'zh_CN',0,0,100,'',0,0,10619);
SET @cid = LAST_INSERT_ID();
UPDATE cms_navigation SET path=REPLACE(path,'__ID__',@cid) WHERE navigation_id=@cid;
-- └ 企业文化
INSERT INTO cms_navigation (type,title,parent_id,model,code,path,component,target,icon,banner,color,hide,permission,password,position,top,bottom,active,meta,style,parent_path,parent_name,model_name,page_id,item_id,lang_category_id,is_mp_weixin,gutter,span,read_num,user_id,merchant_id,lang,home,recommend,sort_number,comments,deleted,status,tenant_id) VALUES (0,'企业文化','@t1','page','','/page/__ID__','/pages/page/[id].vue','_self',NULL,NULL,NULL,0,0,NULL,2,0,1,NULL,'',NULL,NULL,NULL,'',0,0,0,0,0,24,10,0,0,'zh_CN',0,0,110,'',0,0,10619);
SET @cid = LAST_INSERT_ID();
UPDATE cms_navigation SET path=REPLACE(path,'__ID__',@cid) WHERE navigation_id=@cid;
-- └ 企业荣誉
INSERT INTO cms_navigation (type,title,parent_id,model,code,path,component,target,icon,banner,color,hide,permission,password,position,top,bottom,active,meta,style,parent_path,parent_name,model_name,page_id,item_id,lang_category_id,is_mp_weixin,gutter,span,read_num,user_id,merchant_id,lang,home,recommend,sort_number,comments,deleted,status,tenant_id) VALUES (0,'企业荣誉','@t1','page','','/page/__ID__','/pages/page/[id].vue','_self',NULL,NULL,NULL,0,0,NULL,2,0,1,NULL,'',NULL,NULL,NULL,'',0,0,0,0,0,24,10,0,0,'zh_CN',0,0,120,'',0,0,10619);
SET @cid = LAST_INSERT_ID();
UPDATE cms_navigation SET path=REPLACE(path,'__ID__',@cid) WHERE navigation_id=@cid;
-- └ 发展历程
INSERT INTO cms_navigation (type,title,parent_id,model,code,path,component,target,icon,banner,color,hide,permission,password,position,top,bottom,active,meta,style,parent_path,parent_name,model_name,page_id,item_id,lang_category_id,is_mp_weixin,gutter,span,read_num,user_id,merchant_id,lang,home,recommend,sort_number,comments,deleted,status,tenant_id) VALUES (0,'发展历程','@t1','page','','/page/__ID__','/pages/page/[id].vue','_self',NULL,NULL,NULL,0,0,NULL,2,0,1,NULL,'',NULL,NULL,NULL,'',0,0,0,0,0,24,10,0,0,'zh_CN',0,0,130,'',0,0,10619);
SET @cid = LAST_INSERT_ID();
UPDATE cms_navigation SET path=REPLACE(path,'__ID__',@cid) WHERE navigation_id=@cid;
-- 顶级: 品牌产品
INSERT INTO cms_navigation (type,title,parent_id,model,code,path,component,target,icon,banner,color,hide,permission,password,position,top,bottom,active,meta,style,parent_path,parent_name,model_name,page_id,item_id,lang_category_id,is_mp_weixin,gutter,span,read_num,user_id,merchant_id,lang,home,recommend,sort_number,comments,deleted,status,tenant_id) VALUES (0,'品牌产品',0,'product','','/products','/pages/products/index.vue','_self',NULL,NULL,NULL,0,0,NULL,2,0,1,NULL,'',NULL,NULL,NULL,'',0,0,0,0,0,24,10,0,0,'zh_CN',0,0,30,'',0,0,10619);
SET @t6 = LAST_INSERT_ID();
-- └ 国葵类
INSERT INTO cms_navigation (type,title,parent_id,model,code,path,component,target,icon,banner,color,hide,permission,password,position,top,bottom,active,meta,style,parent_path,parent_name,model_name,page_id,item_id,lang_category_id,is_mp_weixin,gutter,span,read_num,user_id,merchant_id,lang,home,recommend,sort_number,comments,deleted,status,tenant_id) VALUES (0,'国葵类','@t6','product','','/product/__ID__','/pages/product/[id].vue','_self',NULL,NULL,NULL,0,0,NULL,2,0,1,NULL,'',NULL,NULL,NULL,'',0,0,0,0,0,24,10,0,0,'zh_CN',0,0,100,'',0,0,10619);
SET @cid = LAST_INSERT_ID();
UPDATE cms_navigation SET path=REPLACE(path,'__ID__',@cid) WHERE navigation_id=@cid;
-- └ 坚果类
INSERT INTO cms_navigation (type,title,parent_id,model,code,path,component,target,icon,banner,color,hide,permission,password,position,top,bottom,active,meta,style,parent_path,parent_name,model_name,page_id,item_id,lang_category_id,is_mp_weixin,gutter,span,read_num,user_id,merchant_id,lang,home,recommend,sort_number,comments,deleted,status,tenant_id) VALUES (0,'坚果类','@t6','product','','/product/__ID__','/pages/product/[id].vue','_self',NULL,NULL,NULL,0,0,NULL,2,0,1,NULL,'',NULL,NULL,NULL,'',0,0,0,0,0,24,10,0,0,'zh_CN',0,0,110,'',0,0,10619);
SET @cid = LAST_INSERT_ID();
UPDATE cms_navigation SET path=REPLACE(path,'__ID__',@cid) WHERE navigation_id=@cid;
-- └ 休闲食品类
INSERT INTO cms_navigation (type,title,parent_id,model,code,path,component,target,icon,banner,color,hide,permission,password,position,top,bottom,active,meta,style,parent_path,parent_name,model_name,page_id,item_id,lang_category_id,is_mp_weixin,gutter,span,read_num,user_id,merchant_id,lang,home,recommend,sort_number,comments,deleted,status,tenant_id) VALUES (0,'休闲食品类','@t6','product','','/product/__ID__','/pages/product/[id].vue','_self',NULL,NULL,NULL,0,0,NULL,2,0,1,NULL,'',NULL,NULL,NULL,'',0,0,0,0,0,24,10,0,0,'zh_CN',0,0,120,'',0,0,10619);
SET @cid = LAST_INSERT_ID();
UPDATE cms_navigation SET path=REPLACE(path,'__ID__',@cid) WHERE navigation_id=@cid;
-- └ 电商产品
INSERT INTO cms_navigation (type,title,parent_id,model,code,path,component,target,icon,banner,color,hide,permission,password,position,top,bottom,active,meta,style,parent_path,parent_name,model_name,page_id,item_id,lang_category_id,is_mp_weixin,gutter,span,read_num,user_id,merchant_id,lang,home,recommend,sort_number,comments,deleted,status,tenant_id) VALUES (0,'电商产品','@t6','product','','/product/__ID__','/pages/product/[id].vue','_self',NULL,NULL,NULL,0,0,NULL,2,0,1,NULL,'',NULL,NULL,NULL,'',0,0,0,0,0,24,10,0,0,'zh_CN',0,0,130,'',0,0,10619);
SET @cid = LAST_INSERT_ID();
UPDATE cms_navigation SET path=REPLACE(path,'__ID__',@cid) WHERE navigation_id=@cid;
-- └ 满坛记礼盒
INSERT INTO cms_navigation (type,title,parent_id,model,code,path,component,target,icon,banner,color,hide,permission,password,position,top,bottom,active,meta,style,parent_path,parent_name,model_name,page_id,item_id,lang_category_id,is_mp_weixin,gutter,span,read_num,user_id,merchant_id,lang,home,recommend,sort_number,comments,deleted,status,tenant_id) VALUES (0,'满坛记礼盒','@t6','product','','/product/__ID__','/pages/product/[id].vue','_self',NULL,NULL,NULL,0,0,NULL,2,0,1,NULL,'',NULL,NULL,NULL,'',0,0,0,0,0,24,10,0,0,'zh_CN',0,0,140,'',0,0,10619);
SET @cid = LAST_INSERT_ID();
UPDATE cms_navigation SET path=REPLACE(path,'__ID__',@cid) WHERE navigation_id=@cid;
-- └ 跨界周边
INSERT INTO cms_navigation (type,title,parent_id,model,code,path,component,target,icon,banner,color,hide,permission,password,position,top,bottom,active,meta,style,parent_path,parent_name,model_name,page_id,item_id,lang_category_id,is_mp_weixin,gutter,span,read_num,user_id,merchant_id,lang,home,recommend,sort_number,comments,deleted,status,tenant_id) VALUES (0,'跨界周边','@t6','product','','/product/__ID__','/pages/product/[id].vue','_self',NULL,NULL,NULL,0,0,NULL,2,0,1,NULL,'',NULL,NULL,NULL,'',0,0,0,0,0,24,10,0,0,'zh_CN',0,0,150,'',0,0,10619);
SET @cid = LAST_INSERT_ID();
UPDATE cms_navigation SET path=REPLACE(path,'__ID__',@cid) WHERE navigation_id=@cid;
-- └ 海外产品
INSERT INTO cms_navigation (type,title,parent_id,model,code,path,component,target,icon,banner,color,hide,permission,password,position,top,bottom,active,meta,style,parent_path,parent_name,model_name,page_id,item_id,lang_category_id,is_mp_weixin,gutter,span,read_num,user_id,merchant_id,lang,home,recommend,sort_number,comments,deleted,status,tenant_id) VALUES (0,'海外产品','@t6','product','','/product/__ID__','/pages/product/[id].vue','_self',NULL,NULL,NULL,0,0,NULL,2,0,1,NULL,'',NULL,NULL,NULL,'',0,0,0,0,0,24,10,0,0,'zh_CN',0,0,160,'',0,0,10619);
SET @cid = LAST_INSERT_ID();
UPDATE cms_navigation SET path=REPLACE(path,'__ID__',@cid) WHERE navigation_id=@cid;
-- 顶级: 满坛记质造
INSERT INTO cms_navigation (type,title,parent_id,model,code,path,component,target,icon,banner,color,hide,permission,password,position,top,bottom,active,meta,style,parent_path,parent_name,model_name,page_id,item_id,lang_category_id,is_mp_weixin,gutter,span,read_num,user_id,merchant_id,lang,home,recommend,sort_number,comments,deleted,status,tenant_id) VALUES (0,'满坛记质造',0,'page','','/page/__ID__','/pages/page/[id].vue','_self',NULL,NULL,NULL,0,0,NULL,2,0,1,NULL,'',NULL,NULL,NULL,'',0,0,0,0,0,24,10,0,0,'zh_CN',0,0,40,'',0,0,10619);
SET @t14 = LAST_INSERT_ID();
UPDATE cms_navigation SET path=REPLACE(path,'__ID__',@t14) WHERE navigation_id=@t14;
-- └ 瓜子质造
INSERT INTO cms_navigation (type,title,parent_id,model,code,path,component,target,icon,banner,color,hide,permission,password,position,top,bottom,active,meta,style,parent_path,parent_name,model_name,page_id,item_id,lang_category_id,is_mp_weixin,gutter,span,read_num,user_id,merchant_id,lang,home,recommend,sort_number,comments,deleted,status,tenant_id) VALUES (0,'瓜子质造','@t14','page','','/page/__ID__','/pages/page/[id].vue','_self',NULL,NULL,NULL,0,0,NULL,2,0,1,NULL,'',NULL,NULL,NULL,'',0,0,0,0,0,24,10,0,0,'zh_CN',0,0,100,'',0,0,10619);
SET @cid = LAST_INSERT_ID();
UPDATE cms_navigation SET path=REPLACE(path,'__ID__',@cid) WHERE navigation_id=@cid;
-- └ 坚果制造
INSERT INTO cms_navigation (type,title,parent_id,model,code,path,component,target,icon,banner,color,hide,permission,password,position,top,bottom,active,meta,style,parent_path,parent_name,model_name,page_id,item_id,lang_category_id,is_mp_weixin,gutter,span,read_num,user_id,merchant_id,lang,home,recommend,sort_number,comments,deleted,status,tenant_id) VALUES (0,'坚果制造','@t14','page','','/page/__ID__','/pages/page/[id].vue','_self',NULL,NULL,NULL,0,0,NULL,2,0,1,NULL,'',NULL,NULL,NULL,'',0,0,0,0,0,24,10,0,0,'zh_CN',0,0,110,'',0,0,10619);
SET @cid = LAST_INSERT_ID();
UPDATE cms_navigation SET path=REPLACE(path,'__ID__',@cid) WHERE navigation_id=@cid;
-- 顶级: 坚果营养知多少
INSERT INTO cms_navigation (type,title,parent_id,model,code,path,component,target,icon,banner,color,hide,permission,password,position,top,bottom,active,meta,style,parent_path,parent_name,model_name,page_id,item_id,lang_category_id,is_mp_weixin,gutter,span,read_num,user_id,merchant_id,lang,home,recommend,sort_number,comments,deleted,status,tenant_id) VALUES (0,'坚果营养知多少',0,'page','','/page/__ID__','/pages/page/[id].vue','_self',NULL,NULL,NULL,0,0,NULL,2,0,1,NULL,'',NULL,NULL,NULL,'',0,0,0,0,0,24,10,0,0,'zh_CN',0,0,50,'',0,0,10619);
SET @t17 = LAST_INSERT_ID();
UPDATE cms_navigation SET path=REPLACE(path,'__ID__',@t17) WHERE navigation_id=@t17;
-- └ 坚果营养小知识
INSERT INTO cms_navigation (type,title,parent_id,model,code,path,component,target,icon,banner,color,hide,permission,password,position,top,bottom,active,meta,style,parent_path,parent_name,model_name,page_id,item_id,lang_category_id,is_mp_weixin,gutter,span,read_num,user_id,merchant_id,lang,home,recommend,sort_number,comments,deleted,status,tenant_id) VALUES (0,'坚果营养小知识','@t17','page','','/page/__ID__','/pages/page/[id].vue','_self',NULL,NULL,NULL,0,0,NULL,2,0,1,NULL,'',NULL,NULL,NULL,'',0,0,0,0,0,24,10,0,0,'zh_CN',0,0,100,'',0,0,10619);
SET @cid = LAST_INSERT_ID();
UPDATE cms_navigation SET path=REPLACE(path,'__ID__',@cid) WHERE navigation_id=@cid;
-- └ 坚果功效小科普
INSERT INTO cms_navigation (type,title,parent_id,model,code,path,component,target,icon,banner,color,hide,permission,password,position,top,bottom,active,meta,style,parent_path,parent_name,model_name,page_id,item_id,lang_category_id,is_mp_weixin,gutter,span,read_num,user_id,merchant_id,lang,home,recommend,sort_number,comments,deleted,status,tenant_id) VALUES (0,'坚果功效小科普','@t17','page','','/page/__ID__','/pages/page/[id].vue','_self',NULL,NULL,NULL,0,0,NULL,2,0,1,NULL,'',NULL,NULL,NULL,'',0,0,0,0,0,24,10,0,0,'zh_CN',0,0,110,'',0,0,10619);
SET @cid = LAST_INSERT_ID();
UPDATE cms_navigation SET path=REPLACE(path,'__ID__',@cid) WHERE navigation_id=@cid;
-- 顶级: 新闻中心
INSERT INTO cms_navigation (type,title,parent_id,model,code,path,component,target,icon,banner,color,hide,permission,password,position,top,bottom,active,meta,style,parent_path,parent_name,model_name,page_id,item_id,lang_category_id,is_mp_weixin,gutter,span,read_num,user_id,merchant_id,lang,home,recommend,sort_number,comments,deleted,status,tenant_id) VALUES (0,'新闻中心',0,'article','','/news','/pages/news/index.vue','_self',NULL,NULL,NULL,0,0,NULL,2,0,1,NULL,'',NULL,NULL,NULL,'',0,0,0,0,0,24,10,0,0,'zh_CN',0,0,60,'',0,0,10619);
SET @t20 = LAST_INSERT_ID();
-- └ 新闻资讯
INSERT INTO cms_navigation (type,title,parent_id,model,code,path,component,target,icon,banner,color,hide,permission,password,position,top,bottom,active,meta,style,parent_path,parent_name,model_name,page_id,item_id,lang_category_id,is_mp_weixin,gutter,span,read_num,user_id,merchant_id,lang,home,recommend,sort_number,comments,deleted,status,tenant_id) VALUES (0,'新闻资讯','@t20','article','','/article/__ID__','/pages/article/[id].vue','_self',NULL,NULL,NULL,0,0,NULL,2,0,1,NULL,'',NULL,NULL,NULL,'',0,0,0,0,0,24,10,0,0,'zh_CN',0,0,100,'',0,0,10619);
SET @cid = LAST_INSERT_ID();
UPDATE cms_navigation SET path=REPLACE(path,'__ID__',@cid) WHERE navigation_id=@cid;
-- └ 企业视频
INSERT INTO cms_navigation (type,title,parent_id,model,code,path,component,target,icon,banner,color,hide,permission,password,position,top,bottom,active,meta,style,parent_path,parent_name,model_name,page_id,item_id,lang_category_id,is_mp_weixin,gutter,span,read_num,user_id,merchant_id,lang,home,recommend,sort_number,comments,deleted,status,tenant_id) VALUES (0,'企业视频','@t20','article','','/article/__ID__','/pages/article/[id].vue','_self',NULL,NULL,NULL,0,0,NULL,2,0,1,NULL,'',NULL,NULL,NULL,'',0,0,0,0,0,24,10,0,0,'zh_CN',0,0,110,'',0,0,10619);
SET @cid = LAST_INSERT_ID();
UPDATE cms_navigation SET path=REPLACE(path,'__ID__',@cid) WHERE navigation_id=@cid;
-- └ 招投标公告
INSERT INTO cms_navigation (type,title,parent_id,model,code,path,component,target,icon,banner,color,hide,permission,password,position,top,bottom,active,meta,style,parent_path,parent_name,model_name,page_id,item_id,lang_category_id,is_mp_weixin,gutter,span,read_num,user_id,merchant_id,lang,home,recommend,sort_number,comments,deleted,status,tenant_id) VALUES (0,'招投标公告','@t20','article','','/article/__ID__','/pages/article/[id].vue','_self',NULL,NULL,NULL,0,0,NULL,2,0,1,NULL,'',NULL,NULL,NULL,'',0,0,0,0,0,24,10,0,0,'zh_CN',0,0,120,'',0,0,10619);
SET @cid = LAST_INSERT_ID();
UPDATE cms_navigation SET path=REPLACE(path,'__ID__',@cid) WHERE navigation_id=@cid;
-- 顶级: 社会责任
INSERT INTO cms_navigation (type,title,parent_id,model,code,path,component,target,icon,banner,color,hide,permission,password,position,top,bottom,active,meta,style,parent_path,parent_name,model_name,page_id,item_id,lang_category_id,is_mp_weixin,gutter,span,read_num,user_id,merchant_id,lang,home,recommend,sort_number,comments,deleted,status,tenant_id) VALUES (0,'社会责任',0,'page','','/page/__ID__','/pages/page/[id].vue','_self',NULL,NULL,NULL,0,0,NULL,2,0,1,NULL,'',NULL,NULL,NULL,'',0,0,0,0,0,24,10,0,0,'zh_CN',0,0,70,'',0,0,10619);
SET @t24 = LAST_INSERT_ID();
UPDATE cms_navigation SET path=REPLACE(path,'__ID__',@t24) WHERE navigation_id=@t24;
-- └ 社会责任新闻
INSERT INTO cms_navigation (type,title,parent_id,model,code,path,component,target,icon,banner,color,hide,permission,password,position,top,bottom,active,meta,style,parent_path,parent_name,model_name,page_id,item_id,lang_category_id,is_mp_weixin,gutter,span,read_num,user_id,merchant_id,lang,home,recommend,sort_number,comments,deleted,status,tenant_id) VALUES (0,'社会责任新闻','@t24','page','','/page/__ID__','/pages/page/[id].vue','_self',NULL,NULL,NULL,0,0,NULL,2,0,1,NULL,'',NULL,NULL,NULL,'',0,0,0,0,0,24,10,0,0,'zh_CN',0,0,100,'',0,0,10619);
SET @cid = LAST_INSERT_ID();
UPDATE cms_navigation SET path=REPLACE(path,'__ID__',@cid) WHERE navigation_id=@cid;
-- └ 社会责任报告
INSERT INTO cms_navigation (type,title,parent_id,model,code,path,component,target,icon,banner,color,hide,permission,password,position,top,bottom,active,meta,style,parent_path,parent_name,model_name,page_id,item_id,lang_category_id,is_mp_weixin,gutter,span,read_num,user_id,merchant_id,lang,home,recommend,sort_number,comments,deleted,status,tenant_id) VALUES (0,'社会责任报告','@t24','page','','/page/__ID__','/pages/page/[id].vue','_self',NULL,NULL,NULL,0,0,NULL,2,0,1,NULL,'',NULL,NULL,NULL,'',0,0,0,0,0,24,10,0,0,'zh_CN',0,0,110,'',0,0,10619);
SET @cid = LAST_INSERT_ID();
UPDATE cms_navigation SET path=REPLACE(path,'__ID__',@cid) WHERE navigation_id=@cid;
-- 顶级: 投资者关系
INSERT INTO cms_navigation (type,title,parent_id,model,code,path,component,target,icon,banner,color,hide,permission,password,position,top,bottom,active,meta,style,parent_path,parent_name,model_name,page_id,item_id,lang_category_id,is_mp_weixin,gutter,span,read_num,user_id,merchant_id,lang,home,recommend,sort_number,comments,deleted,status,tenant_id) VALUES (0,'投资者关系',0,'page','','/page/__ID__','/pages/page/[id].vue','_self',NULL,NULL,NULL,0,0,NULL,2,0,1,NULL,'',NULL,NULL,NULL,'',0,0,0,0,0,24,10,0,0,'zh_CN',0,0,80,'',0,0,10619);
SET @t27 = LAST_INSERT_ID();
UPDATE cms_navigation SET path=REPLACE(path,'__ID__',@t27) WHERE navigation_id=@t27;
-- └ 投资者沟通
INSERT INTO cms_navigation (type,title,parent_id,model,code,path,component,target,icon,banner,color,hide,permission,password,position,top,bottom,active,meta,style,parent_path,parent_name,model_name,page_id,item_id,lang_category_id,is_mp_weixin,gutter,span,read_num,user_id,merchant_id,lang,home,recommend,sort_number,comments,deleted,status,tenant_id) VALUES (0,'投资者沟通','@t27','page','','/page/__ID__','/pages/page/[id].vue','_self',NULL,NULL,NULL,0,0,NULL,2,0,1,NULL,'',NULL,NULL,NULL,'',0,0,0,0,0,24,10,0,0,'zh_CN',0,0,100,'',0,0,10619);
SET @cid = LAST_INSERT_ID();
UPDATE cms_navigation SET path=REPLACE(path,'__ID__',@cid) WHERE navigation_id=@cid;
-- └ 定期报告
INSERT INTO cms_navigation (type,title,parent_id,model,code,path,component,target,icon,banner,color,hide,permission,password,position,top,bottom,active,meta,style,parent_path,parent_name,model_name,page_id,item_id,lang_category_id,is_mp_weixin,gutter,span,read_num,user_id,merchant_id,lang,home,recommend,sort_number,comments,deleted,status,tenant_id) VALUES (0,'定期报告','@t27','page','','/page/__ID__','/pages/page/[id].vue','_self',NULL,NULL,NULL,0,0,NULL,2,0,1,NULL,'',NULL,NULL,NULL,'',0,0,0,0,0,24,10,0,0,'zh_CN',0,0,110,'',0,0,10619);
SET @cid = LAST_INSERT_ID();
UPDATE cms_navigation SET path=REPLACE(path,'__ID__',@cid) WHERE navigation_id=@cid;
-- 顶级: 满坛记透明工厂
INSERT INTO cms_navigation (type,title,parent_id,model,code,path,component,target,icon,banner,color,hide,permission,password,position,top,bottom,active,meta,style,parent_path,parent_name,model_name,page_id,item_id,lang_category_id,is_mp_weixin,gutter,span,read_num,user_id,merchant_id,lang,home,recommend,sort_number,comments,deleted,status,tenant_id) VALUES (0,'满坛记透明工厂',0,'page','','/page/__ID__','/pages/page/[id].vue','_self',NULL,NULL,NULL,0,0,NULL,2,0,1,NULL,'',NULL,NULL,NULL,'',0,0,0,0,0,24,10,0,0,'zh_CN',0,0,90,'',0,0,10619);
SET @t30 = LAST_INSERT_ID();
UPDATE cms_navigation SET path=REPLACE(path,'__ID__',@t30) WHERE navigation_id=@t30;
-- └ 透明工厂视频集锦
INSERT INTO cms_navigation (type,title,parent_id,model,code,path,component,target,icon,banner,color,hide,permission,password,position,top,bottom,active,meta,style,parent_path,parent_name,model_name,page_id,item_id,lang_category_id,is_mp_weixin,gutter,span,read_num,user_id,merchant_id,lang,home,recommend,sort_number,comments,deleted,status,tenant_id) VALUES (0,'透明工厂视频集锦','@t30','page','','/page/__ID__','/pages/page/[id].vue','_self',NULL,NULL,NULL,0,0,NULL,2,0,1,NULL,'',NULL,NULL,NULL,'',0,0,0,0,0,24,10,0,0,'zh_CN',0,0,100,'',0,0,10619);
SET @cid = LAST_INSERT_ID();
UPDATE cms_navigation SET path=REPLACE(path,'__ID__',@cid) WHERE navigation_id=@cid;
-- 顶级: 联系我们
INSERT INTO cms_navigation (type,title,parent_id,model,code,path,component,target,icon,banner,color,hide,permission,password,position,top,bottom,active,meta,style,parent_path,parent_name,model_name,page_id,item_id,lang_category_id,is_mp_weixin,gutter,span,read_num,user_id,merchant_id,lang,home,recommend,sort_number,comments,deleted,status,tenant_id) VALUES (0,'联系我们',0,'page','','/contact','/pages/contact.vue','_self',NULL,NULL,NULL,0,0,NULL,2,0,1,NULL,'',NULL,NULL,NULL,'',0,0,0,0,0,24,10,0,0,'zh_CN',0,0,100,'',0,0,10619);
SET @t32 = LAST_INSERT_ID();
-- └ 联系我们
INSERT INTO cms_navigation (type,title,parent_id,model,code,path,component,target,icon,banner,color,hide,permission,password,position,top,bottom,active,meta,style,parent_path,parent_name,model_name,page_id,item_id,lang_category_id,is_mp_weixin,gutter,span,read_num,user_id,merchant_id,lang,home,recommend,sort_number,comments,deleted,status,tenant_id) VALUES (0,'联系我们','@t32','page','','/page/__ID__','/pages/page/[id].vue','_self',NULL,NULL,NULL,0,0,NULL,2,0,1,NULL,'',NULL,NULL,NULL,'',0,0,0,0,0,24,10,0,0,'zh_CN',0,0,100,'',0,0,10619);
SET @cid = LAST_INSERT_ID();
UPDATE cms_navigation SET path=REPLACE(path,'__ID__',@cid) WHERE navigation_id=@cid;
-- └ 大客户团购
INSERT INTO cms_navigation (type,title,parent_id,model,code,path,component,target,icon,banner,color,hide,permission,password,position,top,bottom,active,meta,style,parent_path,parent_name,model_name,page_id,item_id,lang_category_id,is_mp_weixin,gutter,span,read_num,user_id,merchant_id,lang,home,recommend,sort_number,comments,deleted,status,tenant_id) VALUES (0,'大客户团购','@t32','page','','/page/__ID__','/pages/page/[id].vue','_self',NULL,NULL,NULL,0,0,NULL,2,0,1,NULL,'',NULL,NULL,NULL,'',0,0,0,0,0,24,10,0,0,'zh_CN',0,0,110,'',0,0,10619);
SET @cid = LAST_INSERT_ID();
UPDATE cms_navigation SET path=REPLACE(path,'__ID__',@cid) WHERE navigation_id=@cid;
-- └ 加入我们
INSERT INTO cms_navigation (type,title,parent_id,model,code,path,component,target,icon,banner,color,hide,permission,password,position,top,bottom,active,meta,style,parent_path,parent_name,model_name,page_id,item_id,lang_category_id,is_mp_weixin,gutter,span,read_num,user_id,merchant_id,lang,home,recommend,sort_number,comments,deleted,status,tenant_id) VALUES (0,'加入我们','@t32','page','','/page/__ID__','/pages/page/[id].vue','_self',NULL,NULL,NULL,0,0,NULL,2,0,1,NULL,'',NULL,NULL,NULL,'',0,0,0,0,0,24,10,0,0,'zh_CN',0,0,120,'',0,0,10619);
SET @cid = LAST_INSERT_ID();
UPDATE cms_navigation SET path=REPLACE(path,'__ID__',@cid) WHERE navigation_id=@cid;
COMMIT;
+70
View File
@@ -0,0 +1,70 @@
# 模板切换功能闭环 — 改动与验证清单
> 目标:后台 `/templates` 选用模板 → 公开站 `website-template` 首页(及各页)自动渲染所选模板。
> 闭环真相源:`cms_website.template_id`(按租户),后台写、前台读。
## 〇、架构路由(关键:写/读分属两个后端,共享 Redis)
- **写路径** `POST /api/cms/cms-template/use {templateId}``GET /current`
后台 website-admin `_websopy` 代理 → `websopy-api.websoft.top` = **websopy-java**
`CmsTemplateController.use()``loginUser.getTenantId()``cms_website.template_id`
- **读路径** `GET /cms/cms-website/getSiteInfo`
公开站 `modulesApiBase` 与 后台 `_cms` 代理 → `cms-api.websoft.top` = **mp-java**
返回 `ShopVo`(站点信息),被 Redis 缓存 1 天(key `SiteInfo:{tenantId}`)。
- 两后端**共享同一 Redis**prod `1Panel-redis-Q1LE`dev `47.119.165.234`,同密码),
故 websopy-java 的 `use()` 清缓存能清掉 mp-java 读的 `SiteInfo:{tenantId}`
## 一、已实施的代码改动
### 读路径(mp-java / cms-api,必须部署)
**A. `mp-java/src/main/java/com/gxwebsoft/shop/vo/ShopVo.java`** — 新增字段
```java
@Schema(description = "模板ID(cms_template.id1~6,对应前端 template-XX),由后台模板选择器选用写入")
private Integer templateId;
```
> 修复「断链」:`getSiteInfo` 返回体原本不带 `templateId`,前台永远拿不到所选模板。
**B. `mp-java/src/main/java/com/gxwebsoft/cms/service/impl/CmsWebsiteServiceImplHelper.java` 的 `convertToVO()`** — 搬运字段
```java
// 搬运所选模板ID:后台模板选择器(/use, websopy-java)写入 cms_website.template_id,前台据此渲染 template-XX
vo.setTemplateId(website.getTemplateId());
```
### 写路径(websopy-java / websopy-api
**C. `websopy-java/src/main/java/com/gxwebsoft/cms/controller/CmsTemplateController.java``use()`** — 选用后清缓存
```java
website.setTemplateId(templateId);
if (cmsWebsiteService.updateById(website)) {
// 选用成功后清除站点信息缓存,使前台 getSiteInfo 立即返回新模板
// (否则 mp-java "SiteInfo:{tenantId}" 缓存 1 天内不刷新,首页不会自动切换;两后端共享 Redis)
cmsWebsiteService.clearSiteInfoCache(loginUser.getTenantId());
return success("选用成功");
}
```
> 修复「时效」:mp-java 的 `getSiteInfo` 用 Redis 缓存 1 天;`use()` 清缓存使前台立即生效。
## 二、必须完成的数据库前提(线上 CMS 库,mp-java 使用)
迁移脚本:`website-template/database/migrate-cms-website-template-id.sql`
作用:`cms_website` 新增 `template_id` 列(真正模板ID);原 `template_id` 列改名 `clone_tenant_id`(克隆来源)。
核查:
```sql
SHOW COLUMNS FROM cms_website LIKE 'template_id';
SHOW COLUMNS FROM cms_website LIKE 'clone_tenant_id';
```
存量 `template_id` 为 NULL 的站点 → 前台回落默认 `template-01`;管理员在 `/templates` 重新选用一次即写入正确值,不丢数据。
## 三、前端改动(website-template,已加临时日志)
- `app/composables/useTemplate.ts``loadTemplate()` 增加仅 dev 日志,打印 `resolvedTemplateId`。**验证通过后删除该日志块。**
- 部署环境 `.env` 必须确认**未设置** `NUXT_PUBLIC_FORCE_TEMPLATE_ID`(本地 `.env` 已注释 OK;该变量优先级最高,会盖掉后台选用)。
## 四、端到端验证
1. 构建部署 **mp-java**(含 A、B+ **websopy-java**(含 C);线上 CMS 库执行迁移。
2. website-template:本地 dev 设 `NUXT_PUBLIC_TENANT_ID=目标租户``pnpm dev`;已部署按域名自动解析。
3. 后台 `/templates``template-03` → 提示成功,卡片高亮「当前使用」。
4. 公开站首页**硬刷** → 渲染 `template-03`DevTools Console 见
`[useTemplate] loadTemplate id= undefined | resolvedTemplateId= template-03`
5. 后台切 `template-05` → 首页再次硬刷 → **立刻**渲染 `template-05`(缓存已清,无需等 1 天)。
6. 验证 `/about``/product``/article``/case` 等页也同步切换(全站 13 处共用 `useTemplate` 状态)。
7. 验证无误后,删除 `useTemplate.ts` 中的临时日志块。
## 五、回滚
A、B 在 mp-javaC 在 websopy-java,均为增量、向后兼容(ShopVo 仅多一个可选字段)。
需回滚时各自 `git revert` 对应提交即可。