diff --git a/sql/clear_cms_data.sql b/sql/clear_cms_data.sql index f38350f..a4be86a 100644 --- a/sql/clear_cms_data.sql +++ b/sql/clear_cms_data.sql @@ -1,13 +1,13 @@ -- ============================================================ -- bsch_cms 全量清库脚本(清测试数据,全流程重走) -- 目标库:jdbc:mysql://localhost:3306/bsch_cms(root) --- 范围:cms_* 六表全空(站点/栏目/文章/轮播/友链/附件),AUTO_INCREMENT 归位 +-- 范围:cms_* 八表全空(站点/栏目/文章/轮播/友链/附件/事项/事项字典),AUTO_INCREMENT 归位 -- 不动的东西: -- * 平台侧账号(cmssuper/cmsschool 等在 userbase 平台库,不在本库) -- * 主题包(${user.dir}/data/themes,含 .meta 覆盖层;要重置主题请手工清该目录) -- * 上传的物理文件(D:/bsch/cms/upload/,见文件尾可选清理说明) -- 表结构由 Hibernate ddl-auto=update 维护,本脚本只清数据不删表。 --- 重灌演示数据:执行同目录 cms_seed_local.sql(其前置即"六表为空",本脚本正好满足)。 +-- 重灌演示数据:执行同目录 cms_seed_local.sql(其前置即"各表为空",本脚本正好满足)。 -- ============================================================ SET NAMES utf8mb4; @@ -18,17 +18,21 @@ TRUNCATE TABLE cms_banner; TRUNCATE TABLE cms_link; TRUNCATE TABLE cms_article; TRUNCATE TABLE cms_category; +TRUNCATE TABLE cms_service_item; +TRUNCATE TABLE cms_service_dict; TRUNCATE TABLE cms_site; SET FOREIGN_KEY_CHECKS = 1; --- 验证:六表应全为 0 -SELECT 'cms_site' AS tbl, COUNT(*) AS remain FROM cms_site -UNION ALL SELECT 'cms_category', COUNT(*) FROM cms_category -UNION ALL SELECT 'cms_article', COUNT(*) FROM cms_article -UNION ALL SELECT 'cms_banner', COUNT(*) FROM cms_banner -UNION ALL SELECT 'cms_link', COUNT(*) FROM cms_link -UNION ALL SELECT 'cms_attachment', COUNT(*) FROM cms_attachment; +-- 验证:八表应全为 0 +SELECT 'cms_site' AS tbl, COUNT(*) AS remain FROM cms_site +UNION ALL SELECT 'cms_category', COUNT(*) FROM cms_category +UNION ALL SELECT 'cms_article', COUNT(*) FROM cms_article +UNION ALL SELECT 'cms_banner', COUNT(*) FROM cms_banner +UNION ALL SELECT 'cms_link', COUNT(*) FROM cms_link +UNION ALL SELECT 'cms_attachment', COUNT(*) FROM cms_attachment +UNION ALL SELECT 'cms_service_item', COUNT(*) FROM cms_service_item +UNION ALL SELECT 'cms_service_dict', COUNT(*) FROM cms_service_dict; -- ------------------------------------------------------------ -- 可选(不在 SQL 内):清掉测试期上传的物理文件,只留种子静态资产。 diff --git a/sql/upgrade_svcitem_r2.sql b/sql/upgrade_svcitem_r2.sql new file mode 100644 index 0000000..cc7c5cd --- /dev/null +++ b/sql/upgrade_svcitem_r2.sql @@ -0,0 +1,138 @@ +-- ============================================================ +-- R2 升级脚本:事项库两表(事项库与办事大厅详细设计 §2.5,幂等) +-- 目标库:存量 bsch_cms(生产/预发);干净环境直接用 cms_init.sql 无需本脚本 +-- 顺序:加表(CREATE IF NOT EXISTS 幂等)→ 索引守卫补建(幂等)→ 校验 +-- 说明:MySQL 无 ADD INDEX IF NOT EXISTS,建表内联索引;存量库若只建了表缺索引 +-- (含 r2 首版之后新增的 idx_svc_item_portal),由第 3 段守卫块自动补建,可反复重跑。 +-- ============================================================ + +-- 1) 事项主表 +CREATE TABLE IF NOT EXISTS `cms_service_item` ( + `id` INT NOT NULL AUTO_INCREMENT COMMENT '主键', + `site_id` INT NOT NULL COMMENT '所属站点', + `item_code` VARCHAR(64) NOT NULL COMMENT '事项编码(站内唯一,跨服务键的一半)', + `title` VARCHAR(200) NOT NULL COMMENT '事项名称', + `summary` VARCHAR(500) NULL COMMENT '一句话简介(列表副标题/快照摘要)', + `object_key` VARCHAR(64) NULL COMMENT '服务对象(→ cms_service_dict.dict_key, dict_type=object)', + `category_key` VARCHAR(64) NULL COMMENT '事项类别(→ cms_service_dict.dict_key, dict_type=category)', + `dept_group_id` INT NULL COMMENT '服务部门(UserBase 组树节点 id 快照)', + `dept_name` VARCHAR(128) NULL COMMENT '部门名快照(改名不自动同步)', + `handle_mode` TINYINT NOT NULL DEFAULT 1 COMMENT '办理方式:1=线上 2=线下 3=线上线下', + `online_flag` TINYINT NOT NULL DEFAULT 1 COMMENT '是否可在线办理:1=是 0=否("在线办理"筛选维度)', + `handle_place` VARCHAR(200) NULL COMMENT '办理地点', + `handle_time_limit` VARCHAR(64) NULL COMMENT '办理时限(文本,如"3 个工作日")', + `promise_time_limit` VARCHAR(64) NULL COMMENT '承诺时限(文本)', + `contact_phone` VARCHAR(64) NULL COMMENT '咨询电话', + `supervise_phone` VARCHAR(64) NULL COMMENT '监督投诉电话', + `process_html` LONGTEXT NULL COMMENT '办理流程 HTML(保存时 jsoup 清洗)', + `material_html` LONGTEXT NULL COMMENT '所需材料 HTML(同上)', + `notice_html` LONGTEXT NULL COMMENT '注意事项 HTML(同上)', + `faq_category_id` INT NULL COMMENT '关联栏目:常见问题(逻辑外键 → cms_category.id)', + `notice_category_id` INT NULL COMMENT '关联栏目:公告信息(同上)', + `rule_category_id` INT NULL COMMENT '关联栏目:文件规定(同上)', + `cover_image` VARCHAR(500) NULL COMMENT '事项图标/封面(上传相对路径)', + `sort_no` INT NOT NULL DEFAULT 0 COMMENT '管理端自定义排序(升序,列表第一优先级)', + `status` TINYINT NOT NULL DEFAULT 0 COMMENT '状态:0=草稿 1=已发布 2=已下架', + `publish_time` DATETIME NULL COMMENT '发布时间', + `offline_time` DATETIME NULL COMMENT '下架时间', + `view_count` INT NOT NULL DEFAULT 0 COMMENT '指南页浏览量(异步累加,不承诺精确)', + `ext_json` JSON NULL COMMENT '扩展(一期仅 apply_url:"我要办理"外链)', + `create_by` VARCHAR(64) NULL, + `create_time` DATETIME NULL, + `update_by` VARCHAR(64) NULL, + `update_time` DATETIME NULL, + PRIMARY KEY (`id`), + UNIQUE KEY `uk_svc_item_code` (`site_id`, `item_code`), + KEY `idx_svc_item_list` (`site_id`, `status`, `online_flag`, `sort_no`), + KEY `idx_svc_item_obj` (`site_id`, `status`, `object_key`), + KEY `idx_svc_item_cat` (`site_id`, `status`, `category_key`), + KEY `idx_svc_item_dept` (`site_id`, `status`, `dept_group_id`), + KEY `idx_svc_item_portal` (`site_id`, `status`, `sort_no` ASC, `publish_time` DESC, `id` DESC) +) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4 COMMENT = 'CMS 事项(L1 事项库主表)'; + +-- 2) 事项字典表 +CREATE TABLE IF NOT EXISTS `cms_service_dict` ( + `id` INT NOT NULL AUTO_INCREMENT COMMENT '主键', + `site_id` INT NOT NULL COMMENT '所属站点', + `dict_type` VARCHAR(32) NOT NULL COMMENT '类型:object=服务对象 category=事项类别', + `dict_key` VARCHAR(64) NOT NULL COMMENT '稳定键(站内同类型唯一;被事项引用后不可改)', + `dict_name` VARCHAR(128) NOT NULL COMMENT '展示名', + `sort_no` INT NOT NULL DEFAULT 0 COMMENT '排序(升序)', + `status` TINYINT NOT NULL DEFAULT 1 COMMENT '状态:0=停用 1=启用(停用不出现在筛选,存量引用仍可渲染)', + `ext_json` JSON NULL COMMENT '预留扩展(颜色/图标等)', + `create_by` VARCHAR(64) NULL, + `create_time` DATETIME NULL, + `update_by` VARCHAR(64) NULL, + `update_time` DATETIME NULL, + PRIMARY KEY (`id`), + UNIQUE KEY `uk_svc_dict` (`site_id`, `dict_type`, `dict_key`), + KEY `idx_svc_dict_list` (`site_id`, `dict_type`, `status`, `sort_no`) +) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4 COMMENT = 'CMS 事项字典(服务对象/事项类别)'; + +-- 3) 索引守卫补建(仅当表已存在但缺索引时生效;幂等,可反复重跑) +-- idx_svc_item_portal:门户服务中心列表 ORDER BY sort_no ASC, publish_time DESC, id DESC 的对症索引 +-- (原 idx_svc_item_list 前导列含门户未过滤的 online_flag,无法用于该排序 → 必然 filesort) +SET @db := DATABASE(); + +SET @sql := IF((SELECT COUNT(*) FROM information_schema.statistics + WHERE table_schema = @db AND table_name = 'cms_service_item' + AND index_name = 'uk_svc_item_code') = 0, + 'ALTER TABLE cms_service_item ADD UNIQUE KEY uk_svc_item_code (site_id, item_code)', 'DO 0'); +PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; + +SET @sql := IF((SELECT COUNT(*) FROM information_schema.statistics + WHERE table_schema = @db AND table_name = 'cms_service_item' + AND index_name = 'idx_svc_item_list') = 0, + 'ALTER TABLE cms_service_item ADD KEY idx_svc_item_list (site_id, status, online_flag, sort_no)', 'DO 0'); +PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; + +SET @sql := IF((SELECT COUNT(*) FROM information_schema.statistics + WHERE table_schema = @db AND table_name = 'cms_service_item' + AND index_name = 'idx_svc_item_obj') = 0, + 'ALTER TABLE cms_service_item ADD KEY idx_svc_item_obj (site_id, status, object_key)', 'DO 0'); +PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; + +SET @sql := IF((SELECT COUNT(*) FROM information_schema.statistics + WHERE table_schema = @db AND table_name = 'cms_service_item' + AND index_name = 'idx_svc_item_cat') = 0, + 'ALTER TABLE cms_service_item ADD KEY idx_svc_item_cat (site_id, status, category_key)', 'DO 0'); +PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; + +SET @sql := IF((SELECT COUNT(*) FROM information_schema.statistics + WHERE table_schema = @db AND table_name = 'cms_service_item' + AND index_name = 'idx_svc_item_dept') = 0, + 'ALTER TABLE cms_service_item ADD KEY idx_svc_item_dept (site_id, status, dept_group_id)', 'DO 0'); +PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; + +SET @sql := IF((SELECT COUNT(*) FROM information_schema.statistics + WHERE table_schema = @db AND table_name = 'cms_service_item' + AND index_name = 'idx_svc_item_portal') = 0, + 'ALTER TABLE cms_service_item ADD KEY idx_svc_item_portal (site_id, status, sort_no ASC, publish_time DESC, id DESC)', 'DO 0'); +PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; + +SET @sql := IF((SELECT COUNT(*) FROM information_schema.statistics + WHERE table_schema = @db AND table_name = 'cms_service_dict' + AND index_name = 'uk_svc_dict') = 0, + 'ALTER TABLE cms_service_dict ADD UNIQUE KEY uk_svc_dict (site_id, dict_type, dict_key)', 'DO 0'); +PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; + +SET @sql := IF((SELECT COUNT(*) FROM information_schema.statistics + WHERE table_schema = @db AND table_name = 'cms_service_dict' + AND index_name = 'idx_svc_dict_list') = 0, + 'ALTER TABLE cms_service_dict ADD KEY idx_svc_dict_list (site_id, dict_type, status, sort_no)', 'DO 0'); +PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; + +-- 4) 校验 +-- 4.1) 应返回 0 行(两表存在且注释非空) +SELECT TABLE_NAME FROM information_schema.TABLES + WHERE TABLE_SCHEMA = DATABASE() + AND TABLE_NAME IN ('cms_service_item', 'cms_service_dict') + AND TABLE_COMMENT = ''; + +-- 4.2) 应返回 10 行(两表全部索引就位:主键 2 + 唯一键 2 + 普通索引 6) +SELECT table_name, index_name, GROUP_CONCAT(column_name ORDER BY seq_in_index) AS cols + FROM information_schema.statistics + WHERE table_schema = DATABASE() + AND table_name IN ('cms_service_item', 'cms_service_dict') + GROUP BY table_name, index_name + ORDER BY table_name, index_name; diff --git a/src/main/java/com/yunhai/cms/config/CacheConfig.java b/src/main/java/com/yunhai/cms/config/CacheConfig.java index 1066beb..61c1813 100644 --- a/src/main/java/com/yunhai/cms/config/CacheConfig.java +++ b/src/main/java/com/yunhai/cms/config/CacheConfig.java @@ -51,7 +51,8 @@ public class CacheConfig implements CachingConfigurer { manager.setCaches(Arrays.asList( new CaffeineCache(CacheNames.SITE, caffeine(Duration.ofMinutes(30)).build()), new CaffeineCache(CacheNames.ARTICLE, caffeine(Duration.ofMinutes(5)).build()), - new CaffeineCache(CacheNames.LIST, caffeine(Duration.ofMinutes(5)).build()))); + new CaffeineCache(CacheNames.LIST, caffeine(Duration.ofMinutes(5)).build()), + new CaffeineCache(CacheNames.SERVICE_ITEM, caffeine(Duration.ofMinutes(5)).build()))); return manager; } diff --git a/src/main/java/com/yunhai/cms/config/CacheNames.java b/src/main/java/com/yunhai/cms/config/CacheNames.java index b35948a..aaaa007 100644 --- a/src/main/java/com/yunhai/cms/config/CacheNames.java +++ b/src/main/java/com/yunhai/cms/config/CacheNames.java @@ -10,6 +10,10 @@ package com.yunhai.cms.config; * 失效事件 = 文章发布/修改/下架(ArticleChangedEvent / ArticlePublishedEvent) *