fix(gxmu): 注册表补 has_college 与团员管理种子,接口返回 DTO 并支持 all=true
- gxmu_module_registry 增加 has_college(4 张在跑表置 1,含已有库补列与补数据语句) - 种子补 gxmu_tygl_form / gxmu_tygl(enabled=0,团员管理,sort 11/12), 同步修正末尾「执行后校验」:12 行未删除、5 行启用、has_college 4 行 - GET /api/gxmu/module-registry/list 支持 all=true(默认只返回启用), 改为返回 ModuleRegistryDTO,不再外泄 tenantId / userId / 时间戳 - ModuleRegistryService:删掉无生产调用的 titleOf(String)/allModules, 标题回退收敛成唯一的静态 titleOf(Map, module);getByModule 改由 hasCollege 使用 - listAll 只保留 SQL 排序(sort_number 升序,NULL 排最前,与 MySQL ASC 口径一致), 删掉内存二次排序 - ModuleRegistryIntegrityChecker 增加反向告警:review_flow_config 里有、注册表里没有的 module 打 warn,不阻断启动 refs #58
This commit is contained in:
@@ -2,7 +2,8 @@
|
|||||||
-- 内容:
|
-- 内容:
|
||||||
-- 1) gxmu_review_flow 增加 reviewer_rule(审批人规则,为空时回退 review_user_id)
|
-- 1) gxmu_review_flow 增加 reviewer_rule(审批人规则,为空时回退 review_user_id)
|
||||||
-- 2) 4 张在跑业务表增加 college_id(学院标识,指向 sys_organization.organization_id)
|
-- 2) 4 张在跑业务表增加 college_id(学院标识,指向 sys_organization.organization_id)
|
||||||
-- 3) 新建 gxmu_module_registry(模块注册表)并写入 10 个模块的初始数据
|
-- 3) 新建 gxmu_module_registry(模块注册表)并写入 12 个模块的初始数据
|
||||||
|
-- 4) 注册表增加 has_college(该模块的业务表是否有 college_id),4 张在跑表置 1
|
||||||
--
|
--
|
||||||
-- 重复执行:加列前用 information_schema 判存(MySQL 5.7 不支持 ADD COLUMN IF NOT EXISTS),
|
-- 重复执行:加列前用 information_schema 判存(MySQL 5.7 不支持 ADD COLUMN IF NOT EXISTS),
|
||||||
-- 建表用 IF NOT EXISTS,初始数据用 INSERT IGNORE;
|
-- 建表用 IF NOT EXISTS,初始数据用 INSERT IGNORE;
|
||||||
@@ -87,6 +88,8 @@ DEALLOCATE PREPARE stmt;
|
|||||||
-- 3) 模块注册表:模块元数据的唯一来源
|
-- 3) 模块注册表:模块元数据的唯一来源
|
||||||
-- module 同时是业务表名,table_name 与 module 不一致会在启动期直接失败(ADR 0006)。
|
-- module 同时是业务表名,table_name 与 module 不一致会在启动期直接失败(ADR 0006)。
|
||||||
-- detail_key 对应 moduleDetailConfig.ts 的配置键;export_template 留空表示走默认导出。
|
-- detail_key 对应 moduleDetailConfig.ts 的配置键;export_template 留空表示走默认导出。
|
||||||
|
-- has_college=1 表示该模块的业务表有 college_id,审批人解析才去读学院上下文;
|
||||||
|
-- 后端不再维护「哪几张表有学院标识」的模块白名单,新增表只改注册表一行。
|
||||||
-- ------------------------------------------------------------------
|
-- ------------------------------------------------------------------
|
||||||
CREATE TABLE IF NOT EXISTS gxmu_module_registry (
|
CREATE TABLE IF NOT EXISTS gxmu_module_registry (
|
||||||
id int NOT NULL AUTO_INCREMENT,
|
id int NOT NULL AUTO_INCREMENT,
|
||||||
@@ -95,6 +98,7 @@ CREATE TABLE IF NOT EXISTS gxmu_module_registry (
|
|||||||
title varchar(100) NOT NULL COMMENT '模块标题',
|
title varchar(100) NOT NULL COMMENT '模块标题',
|
||||||
detail_key varchar(64) NOT NULL COMMENT '详情页配置键(moduleDetailConfig.ts)',
|
detail_key varchar(64) NOT NULL COMMENT '详情页配置键(moduleDetailConfig.ts)',
|
||||||
export_template varchar(100) DEFAULT NULL COMMENT '导出模板标识',
|
export_template varchar(100) DEFAULT NULL COMMENT '导出模板标识',
|
||||||
|
has_college tinyint NOT NULL DEFAULT '0' COMMENT '业务表是否有 college_id:0否 1是',
|
||||||
enabled tinyint NOT NULL DEFAULT '1' COMMENT '是否启用:0否 1是',
|
enabled tinyint NOT NULL DEFAULT '1' COMMENT '是否启用:0否 1是',
|
||||||
sort_number int NOT NULL DEFAULT '0' COMMENT '排序号,小的在前',
|
sort_number int NOT NULL DEFAULT '0' COMMENT '排序号,小的在前',
|
||||||
deleted tinyint NOT NULL DEFAULT '0' COMMENT '删除标记:0未删 1已删',
|
deleted tinyint NOT NULL DEFAULT '0' COMMENT '删除标记:0未删 1已删',
|
||||||
@@ -106,24 +110,48 @@ CREATE TABLE IF NOT EXISTS gxmu_module_registry (
|
|||||||
UNIQUE KEY uk_module (module)
|
UNIQUE KEY uk_module (module)
|
||||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='模块注册表';
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='模块注册表';
|
||||||
|
|
||||||
-- 初始数据:9 个 gxmu 模块 + cms_manuscript。
|
-- 已有库补列:gxmu_module_registry 可能已由上一版脚本建好,CREATE TABLE IF NOT EXISTS 不会补列
|
||||||
|
SET @sql = IF(
|
||||||
|
EXISTS(
|
||||||
|
SELECT 1 FROM information_schema.COLUMNS
|
||||||
|
WHERE TABLE_SCHEMA = @db AND TABLE_NAME = 'gxmu_module_registry' AND COLUMN_NAME = 'has_college'
|
||||||
|
),
|
||||||
|
'SELECT 1',
|
||||||
|
'ALTER TABLE gxmu_module_registry ADD COLUMN has_college tinyint NOT NULL DEFAULT ''0'' COMMENT ''业务表是否有 college_id:0否 1是'''
|
||||||
|
);
|
||||||
|
PREPARE stmt FROM @sql;
|
||||||
|
EXECUTE stmt;
|
||||||
|
DEALLOCATE PREPARE stmt;
|
||||||
|
|
||||||
|
-- 初始数据:11 个 gxmu 模块 + cms_manuscript。
|
||||||
-- title 取自 ReviewFlowConfigServiceImpl.MODULE_NAME_MAP,detail_key 与 moduleDetailConfig.ts 的键一致。
|
-- title 取自 ReviewFlowConfigServiceImpl.MODULE_NAME_MAP,detail_key 与 moduleDetailConfig.ts 的键一致。
|
||||||
-- enabled=1:4 条在跑的 gxmu 审批链 + 稿件(cms_manuscript 有 review_flow_config 配置、当前就在审核下拉里,
|
-- enabled=1(5 行):4 条在跑的 gxmu 审批链 + 稿件(cms_manuscript 有 review_flow_config 配置、当前就在审核下拉里,
|
||||||
-- 停用会让「稿件」从审核筛选里静默消失);5 个「已接线未启用」的 gxmu 模块保持 0(定性见 issue #55)。
|
-- 停用会让「稿件」从审核筛选里静默消失)。
|
||||||
-- sort_number 按 reviewList/components/search.vue 当前下拉顺序,cms_manuscript 排在最后。
|
-- enabled=0(7 行):5 个「已接线未启用」的 gxmu 模块(定性见 issue #55)
|
||||||
|
-- + gxmu_tygl_form / gxmu_tygl(团员管理,有存量数据与 review_flow_config 行,但不开新入口)。
|
||||||
|
-- has_college=1:只有 4 张在跑业务表加了 college_id(见本脚本第 2 段)。
|
||||||
|
-- sort_number 按 reviewList/components/search.vue 当前下拉顺序,cms_manuscript 排在最后,团员管理两行排在其后。
|
||||||
INSERT IGNORE INTO gxmu_module_registry
|
INSERT IGNORE INTO gxmu_module_registry
|
||||||
(module, table_name, title, detail_key, export_template, enabled, sort_number, deleted, tenant_id, user_id)
|
(module, table_name, title, detail_key, export_template, has_college, enabled, sort_number, deleted, tenant_id, user_id)
|
||||||
VALUES
|
VALUES
|
||||||
('gxmu_sjqn', 'gxmu_sjqn', '十佳青年岗位能手', 'gxmu_sjqn', NULL, 1, 1, 0, 1, NULL),
|
('gxmu_sjqn', 'gxmu_sjqn', '十佳青年岗位能手', 'gxmu_sjqn', NULL, 1, 1, 1, 0, 1, NULL),
|
||||||
('gxmu_sjtbzbsj', 'gxmu_sjtbzbsj', '十佳团支部书记', 'gxmu_sjtbzbsj', NULL, 0, 2, 0, 1, NULL),
|
('gxmu_sjtbzbsj', 'gxmu_sjtbzbsj', '十佳团支部书记', 'gxmu_sjtbzbsj', NULL, 0, 0, 2, 0, 1, NULL),
|
||||||
('gxmu_wshqtw', 'gxmu_wshqtw', '五四红旗团委', 'gxmu_wshqtw', NULL, 0, 3, 0, 1, NULL),
|
('gxmu_wshqtw', 'gxmu_wshqtw', '五四红旗团委', 'gxmu_wshqtw', NULL, 0, 0, 3, 0, 1, NULL),
|
||||||
('gxmu_wshqtzb', 'gxmu_wshqtzb', '五四红旗团支部', 'gxmu_wshqtzb', NULL, 0, 4, 0, 1, NULL),
|
('gxmu_wshqtzb', 'gxmu_wshqtzb', '五四红旗团支部', 'gxmu_wshqtzb', NULL, 0, 0, 4, 0, 1, NULL),
|
||||||
('gxmu_yxgqtdgb', 'gxmu_yxgqtdgb', '优秀共青团干部', 'gxmu_yxgqtdgb', NULL, 0, 5, 0, 1, NULL),
|
('gxmu_yxgqtdgb', 'gxmu_yxgqtdgb', '优秀共青团干部', 'gxmu_yxgqtdgb', NULL, 0, 0, 5, 0, 1, NULL),
|
||||||
('gxmu_yxgqty', 'gxmu_yxgqty', '优秀共青团员', 'gxmu_yxgqty', NULL, 0, 6, 0, 1, NULL),
|
('gxmu_yxgqty', 'gxmu_yxgqty', '优秀共青团员', 'gxmu_yxgqty', NULL, 0, 0, 6, 0, 1, NULL),
|
||||||
('gxmu_tzbcy_form', 'gxmu_tzbcy_form', '挑战杯', 'gxmu_tzbcy_form', NULL, 1, 7, 0, 1, NULL),
|
('gxmu_tzbcy_form', 'gxmu_tzbcy_form', '挑战杯', 'gxmu_tzbcy_form', NULL, 1, 1, 7, 0, 1, NULL),
|
||||||
('gxmu_qmgc_form', 'gxmu_qmgc_form', '青马工程', 'gxmu_qmgc_form', NULL, 1, 8, 0, 1, NULL),
|
('gxmu_qmgc_form', 'gxmu_qmgc_form', '青马工程', 'gxmu_qmgc_form', NULL, 1, 1, 8, 0, 1, NULL),
|
||||||
('gxmu_wxxzx_project', 'gxmu_wxxzx_project', '未来学术之星', 'gxmu_wxxzx_project', NULL, 1, 9, 0, 1, NULL),
|
('gxmu_wxxzx_project', 'gxmu_wxxzx_project', '未来学术之星', 'gxmu_wxxzx_project', NULL, 1, 1, 9, 0, 1, NULL),
|
||||||
('cms_manuscript', 'cms_manuscript', '稿件', 'cms_manuscript', NULL, 1, 10, 0, 1, NULL);
|
('cms_manuscript', 'cms_manuscript', '稿件', 'cms_manuscript', NULL, 0, 1, 10, 0, 1, NULL),
|
||||||
|
('gxmu_tygl_form', 'gxmu_tygl_form', '团员管理', 'gxmu_tygl_form', NULL, 0, 0, 11, 0, 1, NULL),
|
||||||
|
('gxmu_tygl', 'gxmu_tygl', '团员管理', 'gxmu_tygl', NULL, 0, 0, 12, 0, 1, NULL);
|
||||||
|
|
||||||
|
-- 已存在库的行补 has_college:INSERT IGNORE 不会改已存在的行,这里只把 4 张在跑表从 0 提到 1。
|
||||||
|
-- 只补 0 → 1,不覆盖后台已改过的值;重复执行是 no-op。
|
||||||
|
UPDATE gxmu_module_registry SET has_college = 1
|
||||||
|
WHERE deleted = 0 AND has_college = 0
|
||||||
|
AND module IN ('gxmu_sjqn', 'gxmu_tzbcy_form', 'gxmu_qmgc_form', 'gxmu_wxxzx_project');
|
||||||
|
|
||||||
-- ------------------------------------------------------------------
|
-- ------------------------------------------------------------------
|
||||||
-- 执行后校验(手工执行;每一段注释里是期望结果)
|
-- 执行后校验(手工执行;每一段注释里是期望结果)
|
||||||
@@ -140,10 +168,18 @@ WHERE TABLE_SCHEMA = DATABASE()
|
|||||||
AND TABLE_NAME IN ('gxmu_sjqn', 'gxmu_tzbcy_form', 'gxmu_qmgc_form', 'gxmu_wxxzx_project')
|
AND TABLE_NAME IN ('gxmu_sjqn', 'gxmu_tzbcy_form', 'gxmu_qmgc_form', 'gxmu_wxxzx_project')
|
||||||
AND COLUMN_NAME = 'college_id' AND IS_NULLABLE = 'YES';
|
AND COLUMN_NAME = 'college_id' AND IS_NULLABLE = 'YES';
|
||||||
|
|
||||||
-- 2) 注册表行数与启用数:期望 10 行未删除,其中 4 行启用
|
-- 2) 注册表行数与启用数:期望 12 行未删除,其中 5 行启用
|
||||||
SELECT COUNT(*) AS registry_rows FROM gxmu_module_registry WHERE deleted = 0;
|
SELECT COUNT(*) AS registry_rows FROM gxmu_module_registry WHERE deleted = 0;
|
||||||
SELECT COUNT(*) AS enabled_rows FROM gxmu_module_registry WHERE deleted = 0 AND enabled = 1;
|
SELECT COUNT(*) AS enabled_rows FROM gxmu_module_registry WHERE deleted = 0 AND enabled = 1;
|
||||||
|
|
||||||
|
-- 2b) has_college:期望 4 行(4 张在跑业务表),且与下面的 college_id 列数一致
|
||||||
|
SELECT COUNT(*) AS has_college_rows FROM gxmu_module_registry WHERE deleted = 0 AND has_college = 1;
|
||||||
|
SELECT r.module
|
||||||
|
FROM gxmu_module_registry r
|
||||||
|
LEFT JOIN information_schema.COLUMNS c
|
||||||
|
ON c.TABLE_SCHEMA = DATABASE() AND c.TABLE_NAME = r.table_name AND c.COLUMN_NAME = 'college_id'
|
||||||
|
WHERE r.deleted = 0 AND r.has_college = 1 AND c.COLUMN_NAME IS NULL;
|
||||||
|
|
||||||
-- 3) 一致性检查:以下查询都应返回空集
|
-- 3) 一致性检查:以下查询都应返回空集
|
||||||
SELECT module FROM gxmu_module_registry WHERE deleted = 0 AND table_name <> module;
|
SELECT module FROM gxmu_module_registry WHERE deleted = 0 AND table_name <> module;
|
||||||
SELECT module FROM gxmu_module_registry WHERE deleted = 0 AND (detail_key IS NULL OR detail_key = '');
|
SELECT module FROM gxmu_module_registry WHERE deleted = 0 AND (detail_key IS NULL OR detail_key = '');
|
||||||
@@ -155,7 +191,7 @@ LEFT JOIN information_schema.TABLES t
|
|||||||
ON t.TABLE_SCHEMA = DATABASE() AND t.TABLE_NAME = r.table_name
|
ON t.TABLE_SCHEMA = DATABASE() AND t.TABLE_NAME = r.table_name
|
||||||
WHERE r.deleted = 0 AND t.TABLE_NAME IS NULL;
|
WHERE r.deleted = 0 AND t.TABLE_NAME IS NULL;
|
||||||
|
|
||||||
-- 4) 启用名单与顺序:期望依次为 gxmu_sjqn / gxmu_tzbcy_form / gxmu_qmgc_form / gxmu_wxxzx_project
|
-- 4) 启用名单与顺序:期望依次为 gxmu_sjqn / gxmu_tzbcy_form / gxmu_qmgc_form / gxmu_wxxzx_project / cms_manuscript(5 行)
|
||||||
SELECT module, title, sort_number
|
SELECT module, title, sort_number
|
||||||
FROM gxmu_module_registry
|
FROM gxmu_module_registry
|
||||||
WHERE deleted = 0 AND enabled = 1
|
WHERE deleted = 0 AND enabled = 1
|
||||||
|
|||||||
@@ -26,6 +26,10 @@ import java.util.Set;
|
|||||||
* <li>注册表引用的业务表在库里真实存在。</li>
|
* <li>注册表引用的业务表在库里真实存在。</li>
|
||||||
* </ol>
|
* </ol>
|
||||||
*
|
*
|
||||||
|
* <p>反方向只告警不阻断:{@code gxmu_review_flow_config} 里有、注册表里没有的 module
|
||||||
|
* 打 warn(这种模块的标题会回退成裸 module 串、也不会出现在模块下拉里,但存量数据仍要能查)。
|
||||||
|
* 不阻断启动是刻意的——存量脏数据不该挡住上线,但必须留下可检索的线索。
|
||||||
|
*
|
||||||
* <p>允许 gxmu.module-registry.strict=false 关闭校验,便于无库本地启动;
|
* <p>允许 gxmu.module-registry.strict=false 关闭校验,便于无库本地启动;
|
||||||
* 生产环境必须保持默认的 true。
|
* 生产环境必须保持默认的 true。
|
||||||
*
|
*
|
||||||
@@ -51,6 +55,34 @@ public class ModuleRegistryIntegrityChecker implements ApplicationRunner {
|
|||||||
check();
|
check();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 反向校验:审批链配置里有、注册表里没有的 module 打 warn,不阻断启动。
|
||||||
|
*
|
||||||
|
* <p>这类模块的标题会回退成裸 module 串、也不会出现在模块下拉里,
|
||||||
|
* 但存量申报记录仍要能查——所以只提示补注册表行,不抛异常。
|
||||||
|
*
|
||||||
|
* @param modules 注册表里的全部模块
|
||||||
|
*/
|
||||||
|
private void warnModulesMissingFromRegistry(List<ModuleRegistry> modules) {
|
||||||
|
Set<String> registered = new HashSet<>();
|
||||||
|
for (ModuleRegistry item : modules) {
|
||||||
|
if (item.getModule() != null) {
|
||||||
|
registered.add(item.getModule());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
List<String> configured = moduleRegistryMapper.selectConfiguredModules();
|
||||||
|
if (configured == null || configured.isEmpty()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
for (String module : configured) {
|
||||||
|
if (module != null && !module.isEmpty() && !registered.contains(module)) {
|
||||||
|
log.warn("gxmu_review_flow_config 里的模块 {} 未在 gxmu_module_registry 登记:"
|
||||||
|
+ "该模块的标题会回退成 module 原串、也不会出现在模块下拉里;"
|
||||||
|
+ "请补一行注册表数据(enabled 按是否新开申报入口决定)", module);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 执行校验;不一致时抛 {@link IllegalStateException} 让启动失败。
|
* 执行校验;不一致时抛 {@link IllegalStateException} 让启动失败。
|
||||||
*/
|
*/
|
||||||
@@ -91,6 +123,7 @@ public class ModuleRegistryIntegrityChecker implements ApplicationRunner {
|
|||||||
throw new IllegalStateException("模块注册表引用的业务表不存在:" + String.join(", ", missing)
|
throw new IllegalStateException("模块注册表引用的业务表不存在:" + String.join(", ", missing)
|
||||||
+ "。请先执行建表脚本或修正注册表数据;本地无库启动时可设置 gxmu.module-registry.strict=false");
|
+ "。请先执行建表脚本或修正注册表数据;本地无库启动时可设置 gxmu.module-registry.strict=false");
|
||||||
}
|
}
|
||||||
|
warnModulesMissingFromRegistry(modules);
|
||||||
log.info("模块注册表一致性校验通过:{} 个模块,{} 张业务表", modules.size(), tableNames.size());
|
log.info("模块注册表一致性校验通过:{} 个模块,{} 张业务表", modules.size(), tableNames.size());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,21 +3,27 @@ package com.gxwebsoft.gxmu.module.controller;
|
|||||||
import com.gxwebsoft.common.core.web.ApiResult;
|
import com.gxwebsoft.common.core.web.ApiResult;
|
||||||
import com.gxwebsoft.common.core.web.BaseController;
|
import com.gxwebsoft.common.core.web.BaseController;
|
||||||
import com.gxwebsoft.gxmu.module.entity.ModuleRegistry;
|
import com.gxwebsoft.gxmu.module.entity.ModuleRegistry;
|
||||||
|
import com.gxwebsoft.gxmu.module.model.ModuleRegistryDTO;
|
||||||
import com.gxwebsoft.gxmu.module.service.ModuleRegistryService;
|
import com.gxwebsoft.gxmu.module.service.ModuleRegistryService;
|
||||||
import io.swagger.annotations.Api;
|
import io.swagger.annotations.Api;
|
||||||
import io.swagger.annotations.ApiOperation;
|
import io.swagger.annotations.ApiOperation;
|
||||||
|
import io.swagger.annotations.ApiParam;
|
||||||
import org.springframework.security.access.prepost.PreAuthorize;
|
import org.springframework.security.access.prepost.PreAuthorize;
|
||||||
import org.springframework.web.bind.annotation.GetMapping;
|
import org.springframework.web.bind.annotation.GetMapping;
|
||||||
import org.springframework.web.bind.annotation.RequestMapping;
|
import org.springframework.web.bind.annotation.RequestMapping;
|
||||||
|
import org.springframework.web.bind.annotation.RequestParam;
|
||||||
import org.springframework.web.bind.annotation.RestController;
|
import org.springframework.web.bind.annotation.RestController;
|
||||||
|
|
||||||
import javax.annotation.Resource;
|
import javax.annotation.Resource;
|
||||||
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 模块注册表控制器
|
* 模块注册表控制器
|
||||||
*
|
*
|
||||||
* <p>前端启动时一次性拉取启用模块清单并缓存,模块下拉、详情页、导出模板都从这里驱动。
|
* <p>前端启动时一次性拉取模块清单并缓存:默认只返回启用模块(新链的模块下拉、申报入口);
|
||||||
|
* {@code all=true} 返回全部模块(含停用,审核列表的模块筛选要能捞出停用模块的存量记录)。
|
||||||
*
|
*
|
||||||
* @author Codex
|
* @author Codex
|
||||||
* @since 2026-11-10
|
* @since 2026-11-10
|
||||||
@@ -30,10 +36,16 @@ public class ModuleRegistryController extends BaseController {
|
|||||||
@Resource
|
@Resource
|
||||||
private ModuleRegistryService moduleRegistryService;
|
private ModuleRegistryService moduleRegistryService;
|
||||||
|
|
||||||
@ApiOperation("启用模块清单")
|
@ApiOperation("模块清单:默认只返回启用模块,all=true 返回全部(含停用)")
|
||||||
@PreAuthorize("isAuthenticated()")
|
@PreAuthorize("isAuthenticated()")
|
||||||
@GetMapping("/list")
|
@GetMapping("/list")
|
||||||
public ApiResult<List<ModuleRegistry>> list() {
|
public ApiResult<List<ModuleRegistryDTO>> list(
|
||||||
return success(moduleRegistryService.listEnabled());
|
@ApiParam("是否返回全部模块(含停用):true 返回全部,默认只返回启用")
|
||||||
|
@RequestParam(value = "all", required = false, defaultValue = "false") boolean all) {
|
||||||
|
List<ModuleRegistry> modules = all ? moduleRegistryService.listAll() : moduleRegistryService.listEnabled();
|
||||||
|
if (modules == null || modules.isEmpty()) {
|
||||||
|
return success(new ArrayList<>());
|
||||||
|
}
|
||||||
|
return success(modules.stream().map(ModuleRegistryDTO::of).collect(Collectors.toList()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -46,6 +46,9 @@ public class ModuleRegistry implements Serializable {
|
|||||||
@ApiModelProperty(value = "导出模板标识,空表示走默认导出")
|
@ApiModelProperty(value = "导出模板标识,空表示走默认导出")
|
||||||
private String exportTemplate;
|
private String exportTemplate;
|
||||||
|
|
||||||
|
@ApiModelProperty(value = "业务表是否有学院标识列:0否 1是(决定审批人解析是否读 college_id)")
|
||||||
|
private Integer hasCollege;
|
||||||
|
|
||||||
@ApiModelProperty(value = "是否启用:0否 1是")
|
@ApiModelProperty(value = "是否启用:0否 1是")
|
||||||
private Integer enabled;
|
private Integer enabled;
|
||||||
|
|
||||||
|
|||||||
@@ -28,4 +28,15 @@ public interface ModuleRegistryMapper extends BaseMapper<ModuleRegistry> {
|
|||||||
+ "<foreach collection='names' item='name' open='(' separator=',' close=')'>#{name}</foreach>"
|
+ "<foreach collection='names' item='name' open='(' separator=',' close=')'>#{name}</foreach>"
|
||||||
+ "</script>")
|
+ "</script>")
|
||||||
List<String> selectExistingTableNames(@Param("names") Collection<String> names);
|
List<String> selectExistingTableNames(@Param("names") Collection<String> names);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 审批链配置里出现过的模块名,用于反向校验「有审批链但注册表没登记」的模块。
|
||||||
|
*
|
||||||
|
* <p>只用于启动期告警,不阻断启动:存量脏数据不该挡住上线。
|
||||||
|
*
|
||||||
|
* @return 审批链配置里的模块名(去重)
|
||||||
|
*/
|
||||||
|
@Select("SELECT DISTINCT module FROM gxmu_review_flow_config "
|
||||||
|
+ "WHERE deleted = 0 AND module IS NOT NULL AND module <> ''")
|
||||||
|
List<String> selectConfiguredModules();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,65 @@
|
|||||||
|
package com.gxwebsoft.gxmu.module.model;
|
||||||
|
|
||||||
|
import com.gxwebsoft.gxmu.module.entity.ModuleRegistry;
|
||||||
|
import io.swagger.annotations.ApiModel;
|
||||||
|
import io.swagger.annotations.ApiModelProperty;
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
import java.io.Serializable;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 模块注册表的对外视图(spec #58 API 契约)。
|
||||||
|
*
|
||||||
|
* <p>只暴露前端需要的元数据;tenantId / userId / createTime / updateTime 是库内字段,
|
||||||
|
* 对前端无意义,不随接口外泄。
|
||||||
|
*
|
||||||
|
* @author Codex
|
||||||
|
* @since 2026-11-11
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
@ApiModel(value = "ModuleRegistryDTO对象", description = "模块注册表对外视图")
|
||||||
|
public class ModuleRegistryDTO implements Serializable {
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
|
@ApiModelProperty(value = "模块标识,同时是业务表名")
|
||||||
|
private String module;
|
||||||
|
|
||||||
|
@ApiModelProperty(value = "业务表名")
|
||||||
|
private String tableName;
|
||||||
|
|
||||||
|
@ApiModelProperty(value = "模块标题")
|
||||||
|
private String title;
|
||||||
|
|
||||||
|
@ApiModelProperty(value = "详情页配置键")
|
||||||
|
private String detailKey;
|
||||||
|
|
||||||
|
@ApiModelProperty(value = "导出模板标识,空表示走默认导出")
|
||||||
|
private String exportTemplate;
|
||||||
|
|
||||||
|
@ApiModelProperty(value = "是否启用:0否 1是")
|
||||||
|
private Integer enabled;
|
||||||
|
|
||||||
|
@ApiModelProperty(value = "排序号,小的在前")
|
||||||
|
private Integer sortNumber;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 由注册表行构造对外视图。
|
||||||
|
*
|
||||||
|
* @param item 注册表行,可为空
|
||||||
|
* @return 对外视图,入参为空时返回 null
|
||||||
|
*/
|
||||||
|
public static ModuleRegistryDTO of(ModuleRegistry item) {
|
||||||
|
if (item == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
ModuleRegistryDTO dto = new ModuleRegistryDTO();
|
||||||
|
dto.setModule(item.getModule());
|
||||||
|
dto.setTableName(item.getTableName());
|
||||||
|
dto.setTitle(item.getTitle());
|
||||||
|
dto.setDetailKey(item.getDetailKey());
|
||||||
|
dto.setExportTemplate(item.getExportTemplate());
|
||||||
|
dto.setEnabled(item.getEnabled());
|
||||||
|
dto.setSortNumber(item.getSortNumber());
|
||||||
|
return dto;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -5,13 +5,12 @@ import com.gxwebsoft.gxmu.module.entity.ModuleRegistry;
|
|||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Set;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 模块注册表服务:模块元数据的唯一读取入口。
|
* 模块注册表服务:模块元数据的唯一读取入口。
|
||||||
*
|
*
|
||||||
* <p>调用方不要再维护模块名常量或标题映射,一律从这里取;
|
* <p>调用方不要再维护模块名常量或标题映射,一律从这里取;
|
||||||
* 注册表里查不到的历史 module 由调用方回退 module 原串,不抛错。
|
* 注册表里查不到的历史 module 由 {@link #titleOf(Map, String)} 回退 module 原串,不抛错。
|
||||||
*
|
*
|
||||||
* @author Codex
|
* @author Codex
|
||||||
* @since 2026-11-10
|
* @since 2026-11-10
|
||||||
@@ -33,21 +32,11 @@ public interface ModuleRegistryService extends IService<ModuleRegistry> {
|
|||||||
*/
|
*/
|
||||||
ModuleRegistry getByModule(String module);
|
ModuleRegistry getByModule(String module);
|
||||||
|
|
||||||
/**
|
|
||||||
* 模块标题;未注册或标题为空时回退 module 原串(module 为 null 时返回 null)。
|
|
||||||
*/
|
|
||||||
String titleOf(String module);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 全部模块的 module -> title 映射(含停用),供列表页一次性填充标题。
|
* 全部模块的 module -> title 映射(含停用),供列表页一次性填充标题。
|
||||||
*/
|
*/
|
||||||
Map<String, String> titleMap();
|
Map<String, String> titleMap();
|
||||||
|
|
||||||
/**
|
|
||||||
* 注册表里存在的全部 module(含停用)。
|
|
||||||
*/
|
|
||||||
Set<String> allModules();
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* module 是否在注册表里存在(含停用)。
|
* module 是否在注册表里存在(含停用)。
|
||||||
*
|
*
|
||||||
@@ -55,4 +44,33 @@ public interface ModuleRegistryService extends IService<ModuleRegistry> {
|
|||||||
* 所以停用模块也必须返回 true。
|
* 所以停用模块也必须返回 true。
|
||||||
*/
|
*/
|
||||||
boolean containsModule(String module);
|
boolean containsModule(String module);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 该模块的业务表是否有 college_id(注册表 has_college = 1)。
|
||||||
|
*
|
||||||
|
* <p>审批人解析要读申报记录的学院标识,但只有部分业务表有这一列;
|
||||||
|
* 哪张表有由注册表决定,后端不再维护模块白名单(ADR 0006:新增表只改注册表一行)。
|
||||||
|
*
|
||||||
|
* @param module 模块编码
|
||||||
|
* @return true 表示可以按 college_id 读取学院上下文
|
||||||
|
*/
|
||||||
|
boolean hasCollege(String module);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 模块标题的唯一回退口径:注册表里有标题用标题,否则回退 module 原串。
|
||||||
|
*
|
||||||
|
* <p>放在接口上是因为注册表是模块元数据的唯一来源,而调用方(审核列表、审批链配置)
|
||||||
|
* 已经批量取了 {@link #titleMap()},不该为了一个标题再回查一次库。
|
||||||
|
*
|
||||||
|
* @param titleMap 模块标题映射,可为空
|
||||||
|
* @param module 模块编码,可为空
|
||||||
|
* @return 展示用标题;module 为空时返回 null
|
||||||
|
*/
|
||||||
|
static String titleOf(Map<String, String> titleMap, String module) {
|
||||||
|
if (module == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
String title = titleMap == null ? null : titleMap.get(module);
|
||||||
|
return title == null || title.isEmpty() ? module : title;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+17
-24
@@ -8,7 +8,6 @@ import com.gxwebsoft.gxmu.module.service.ModuleRegistryService;
|
|||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.Comparator;
|
|
||||||
import java.util.LinkedHashMap;
|
import java.util.LinkedHashMap;
|
||||||
import java.util.LinkedHashSet;
|
import java.util.LinkedHashSet;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
@@ -27,6 +26,7 @@ public class ModuleRegistryServiceImpl extends ServiceImpl<ModuleRegistryMapper,
|
|||||||
implements ModuleRegistryService {
|
implements ModuleRegistryService {
|
||||||
|
|
||||||
private static final int ENABLED = 1;
|
private static final int ENABLED = 1;
|
||||||
|
private static final int HAS_COLLEGE = 1;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public List<ModuleRegistry> listEnabled() {
|
public List<ModuleRegistry> listEnabled() {
|
||||||
@@ -35,6 +35,13 @@ public class ModuleRegistryServiceImpl extends ServiceImpl<ModuleRegistryMapper,
|
|||||||
.collect(Collectors.toList());
|
.collect(Collectors.toList());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 全部未删除模块。
|
||||||
|
*
|
||||||
|
* <p>排序只在 SQL 里做一次:{@code sort_number} 升序(MySQL 把 NULL 排最前,
|
||||||
|
* 与「没设排序号的模块排在最前面」的口径一致),再按 {@code id} 升序保证稳定。
|
||||||
|
* 内存里不再二次排序,避免 SQL 与 Java 两套 NULL 口径(SQL 前、Java 后)互相打架。
|
||||||
|
*/
|
||||||
@Override
|
@Override
|
||||||
public List<ModuleRegistry> listAll() {
|
public List<ModuleRegistry> listAll() {
|
||||||
// 用字符串列名而非 Lambda:注册表服务是纯读路径,且要在无 MyBatis 上下文的单测里可用
|
// 用字符串列名而非 Lambda:注册表服务是纯读路径,且要在无 MyBatis 上下文的单测里可用
|
||||||
@@ -44,7 +51,7 @@ public class ModuleRegistryServiceImpl extends ServiceImpl<ModuleRegistryMapper,
|
|||||||
if (list == null || list.isEmpty()) {
|
if (list == null || list.isEmpty()) {
|
||||||
return Collections.emptyList();
|
return Collections.emptyList();
|
||||||
}
|
}
|
||||||
return list.stream().sorted(sortOrder()).collect(Collectors.toList());
|
return list;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -57,18 +64,6 @@ public class ModuleRegistryServiceImpl extends ServiceImpl<ModuleRegistryMapper,
|
|||||||
.last("limit 1"));
|
.last("limit 1"));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public String titleOf(String module) {
|
|
||||||
if (module == null) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
ModuleRegistry item = getByModule(module);
|
|
||||||
if (item == null || item.getTitle() == null || item.getTitle().isEmpty()) {
|
|
||||||
return module;
|
|
||||||
}
|
|
||||||
return item.getTitle();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Map<String, String> titleMap() {
|
public Map<String, String> titleMap() {
|
||||||
Map<String, String> titleMap = new LinkedHashMap<>();
|
Map<String, String> titleMap = new LinkedHashMap<>();
|
||||||
@@ -82,24 +77,22 @@ public class ModuleRegistryServiceImpl extends ServiceImpl<ModuleRegistryMapper,
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Set<String> allModules() {
|
public boolean containsModule(String module) {
|
||||||
|
if (module == null || module.isEmpty()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
Set<String> modules = new LinkedHashSet<>();
|
Set<String> modules = new LinkedHashSet<>();
|
||||||
for (ModuleRegistry item : listAll()) {
|
for (ModuleRegistry item : listAll()) {
|
||||||
if (item.getModule() != null) {
|
if (item.getModule() != null) {
|
||||||
modules.add(item.getModule());
|
modules.add(item.getModule());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return modules;
|
return modules.contains(module);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean containsModule(String module) {
|
public boolean hasCollege(String module) {
|
||||||
return module != null && !module.isEmpty() && allModules().contains(module);
|
ModuleRegistry item = getByModule(module);
|
||||||
}
|
return item != null && item.getHasCollege() != null && item.getHasCollege() == HAS_COLLEGE;
|
||||||
|
|
||||||
private Comparator<ModuleRegistry> sortOrder() {
|
|
||||||
return Comparator.comparingInt((ModuleRegistry item) ->
|
|
||||||
item.getSortNumber() == null ? Integer.MAX_VALUE : item.getSortNumber())
|
|
||||||
.thenComparingInt(item -> item.getId() == null ? Integer.MAX_VALUE : item.getId());
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -86,6 +86,22 @@ class ModuleRegistryIntegrityCheckerTest {
|
|||||||
assertDoesNotThrow(() -> checker.check());
|
assertDoesNotThrow(() -> checker.check());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 反方向:审批链配置里有、注册表里没有的模块只打 warn,不阻断启动
|
||||||
|
* (存量脏数据不该挡住上线,但必须留线索)。
|
||||||
|
*/
|
||||||
|
@Test
|
||||||
|
void unregisteredFlowConfigModuleOnlyWarns() {
|
||||||
|
when(moduleRegistryService.listAll()).thenReturn(Collections.singletonList(
|
||||||
|
module("gxmu_sjqn", "gxmu_sjqn", 1)));
|
||||||
|
when(moduleRegistryMapper.selectExistingTableNames(any()))
|
||||||
|
.thenReturn(Collections.singletonList("gxmu_sjqn"));
|
||||||
|
when(moduleRegistryMapper.selectConfiguredModules())
|
||||||
|
.thenReturn(Collections.singletonList("gxmu_tygl"));
|
||||||
|
|
||||||
|
assertDoesNotThrow(() -> checker.check());
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void strictFalseSkipsValidationForLocalStartupWithoutDatabase() {
|
void strictFalseSkipsValidationForLocalStartupWithoutDatabase() {
|
||||||
ReflectionTestUtils.setField(checker, "strict", false);
|
ReflectionTestUtils.setField(checker, "strict", false);
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ package com.gxwebsoft.gxmu.module;
|
|||||||
|
|
||||||
import com.gxwebsoft.gxmu.module.entity.ModuleRegistry;
|
import com.gxwebsoft.gxmu.module.entity.ModuleRegistry;
|
||||||
import com.gxwebsoft.gxmu.module.mapper.ModuleRegistryMapper;
|
import com.gxwebsoft.gxmu.module.mapper.ModuleRegistryMapper;
|
||||||
|
import com.gxwebsoft.gxmu.module.service.ModuleRegistryService;
|
||||||
import com.gxwebsoft.gxmu.module.service.impl.ModuleRegistryServiceImpl;
|
import com.gxwebsoft.gxmu.module.service.impl.ModuleRegistryServiceImpl;
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
import org.junit.jupiter.api.extension.ExtendWith;
|
import org.junit.jupiter.api.extension.ExtendWith;
|
||||||
@@ -12,6 +13,7 @@ import org.mockito.junit.jupiter.MockitoSettings;
|
|||||||
import org.mockito.quality.Strictness;
|
import org.mockito.quality.Strictness;
|
||||||
|
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
|
import java.util.HashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
@@ -26,9 +28,10 @@ import static org.mockito.Mockito.when;
|
|||||||
/**
|
/**
|
||||||
* 模块注册表服务的单元测试(mock 掉 Mapper,不启 Spring、不连库)。
|
* 模块注册表服务的单元测试(mock 掉 Mapper,不启 Spring、不连库)。
|
||||||
*
|
*
|
||||||
* <p>注册表是模块元数据的唯一来源,这里锁住三件对外可观察的事:
|
* <p>注册表是模块元数据的唯一来源,这里锁住四件对外可观察的事:
|
||||||
* 启用清单按 sort_number 升序、标题查询在未注册时回退 module 原串、
|
* 启用清单只留启用项且保持 SQL 的排序(排序只在 SQL 里做一次)、
|
||||||
* 模块集合包含已停用模块(停用模块的历史数据仍要能撤回/作废)。
|
* 模块集合包含已停用模块(停用模块的历史数据仍要能撤回/作废)、
|
||||||
|
* has_college 决定审批人解析是否读学院上下文、标题回退只有一份实现。
|
||||||
*
|
*
|
||||||
* @author Codex
|
* @author Codex
|
||||||
* @since 2026-11-10
|
* @since 2026-11-10
|
||||||
@@ -44,11 +47,12 @@ class ModuleRegistryServiceImplTest {
|
|||||||
private ModuleRegistryServiceImpl moduleRegistryService;
|
private ModuleRegistryServiceImpl moduleRegistryService;
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void listEnabledReturnsOnlyEnabledModulesOrderedBySortNumber() {
|
void listEnabledReturnsOnlyEnabledModulesInSqlOrder() {
|
||||||
|
// SQL 已按 sort_number 升序返回;服务只负责过滤,不再二次排序
|
||||||
when(moduleRegistryMapper.selectList(any())).thenReturn(Arrays.asList(
|
when(moduleRegistryMapper.selectList(any())).thenReturn(Arrays.asList(
|
||||||
module("gxmu_qmgc_form", "青马工程", 1, 8),
|
module("gxmu_sjqn", "十佳青年岗位能手", 1, 1, 1),
|
||||||
module("gxmu_sjtbzbsj", "十佳团支部书记", 0, 2),
|
module("gxmu_sjtbzbsj", "十佳团支部书记", 0, 2, 0),
|
||||||
module("gxmu_sjqn", "十佳青年岗位能手", 1, 1)));
|
module("gxmu_qmgc_form", "青马工程", 1, 8, 1)));
|
||||||
|
|
||||||
List<ModuleRegistry> enabled = moduleRegistryService.listEnabled();
|
List<ModuleRegistry> enabled = moduleRegistryService.listEnabled();
|
||||||
|
|
||||||
@@ -56,30 +60,11 @@ class ModuleRegistryServiceImplTest {
|
|||||||
enabled.stream().map(ModuleRegistry::getModule).collect(Collectors.toList()));
|
enabled.stream().map(ModuleRegistry::getModule).collect(Collectors.toList()));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
|
||||||
void titleOfReturnsRegisteredTitle() {
|
|
||||||
when(moduleRegistryMapper.selectOne(any())).thenReturn(module("gxmu_tzbcy_form", "挑战杯", 1, 7));
|
|
||||||
|
|
||||||
assertEquals("挑战杯", moduleRegistryService.titleOf("gxmu_tzbcy_form"));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
void titleOfFallsBackToModuleStringWhenNotRegistered() {
|
|
||||||
when(moduleRegistryMapper.selectOne(any())).thenReturn(null);
|
|
||||||
|
|
||||||
assertEquals("cms_legacy_module", moduleRegistryService.titleOf("cms_legacy_module"));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
void titleOfKeepsNullModuleNull() {
|
|
||||||
assertNull(moduleRegistryService.titleOf(null));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void titleMapCoversDisabledModulesSoHistoricalRowsKeepTitles() {
|
void titleMapCoversDisabledModulesSoHistoricalRowsKeepTitles() {
|
||||||
when(moduleRegistryMapper.selectList(any())).thenReturn(Arrays.asList(
|
when(moduleRegistryMapper.selectList(any())).thenReturn(Arrays.asList(
|
||||||
module("gxmu_sjqn", "十佳青年岗位能手", 1, 1),
|
module("gxmu_sjqn", "十佳青年岗位能手", 1, 1, 1),
|
||||||
module("gxmu_yxgqty", "优秀共青团员", 0, 6)));
|
module("gxmu_yxgqty", "优秀共青团员", 0, 6, 0)));
|
||||||
|
|
||||||
Map<String, String> titles = moduleRegistryService.titleMap();
|
Map<String, String> titles = moduleRegistryService.titleMap();
|
||||||
|
|
||||||
@@ -91,15 +76,49 @@ class ModuleRegistryServiceImplTest {
|
|||||||
@Test
|
@Test
|
||||||
void moduleSetContainsDisabledModules() {
|
void moduleSetContainsDisabledModules() {
|
||||||
when(moduleRegistryMapper.selectList(any())).thenReturn(Arrays.asList(
|
when(moduleRegistryMapper.selectList(any())).thenReturn(Arrays.asList(
|
||||||
module("gxmu_sjqn", "十佳青年岗位能手", 1, 1),
|
module("gxmu_sjqn", "十佳青年岗位能手", 1, 1, 1),
|
||||||
module("gxmu_yxgqty", "优秀共青团员", 0, 6)));
|
module("gxmu_yxgqty", "优秀共青团员", 0, 6, 0)));
|
||||||
|
|
||||||
assertTrue(moduleRegistryService.containsModule("gxmu_yxgqty"));
|
assertTrue(moduleRegistryService.containsModule("gxmu_yxgqty"));
|
||||||
assertFalse(moduleRegistryService.containsModule("gxmu_unknown"));
|
assertFalse(moduleRegistryService.containsModule("gxmu_unknown"));
|
||||||
assertFalse(moduleRegistryService.containsModule(null));
|
assertFalse(moduleRegistryService.containsModule(null));
|
||||||
}
|
}
|
||||||
|
|
||||||
private ModuleRegistry module(String name, String title, int enabled, int sortNumber) {
|
@Test
|
||||||
|
void hasCollegeFollowsTheRegistryFlag() {
|
||||||
|
when(moduleRegistryMapper.selectOne(any())).thenReturn(module("gxmu_sjqn", "十佳青年岗位能手", 1, 1, 1));
|
||||||
|
|
||||||
|
assertTrue(moduleRegistryService.hasCollege("gxmu_sjqn"));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void hasCollegeIsFalseForDisabledOrMissingFlag() {
|
||||||
|
when(moduleRegistryMapper.selectOne(any())).thenReturn(module("gxmu_tygl", "团员管理", 0, 12, 0));
|
||||||
|
|
||||||
|
assertFalse(moduleRegistryService.hasCollege("gxmu_tygl"));
|
||||||
|
assertFalse(moduleRegistryService.hasCollege(null));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void titleOfUsesRegistryTitleWhenPresent() {
|
||||||
|
Map<String, String> titles = new HashMap<>();
|
||||||
|
titles.put("gxmu_tzbcy_form", "挑战杯");
|
||||||
|
|
||||||
|
assertEquals("挑战杯", ModuleRegistryService.titleOf(titles, "gxmu_tzbcy_form"));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void titleOfFallsBackToModuleStringWhenNotRegistered() {
|
||||||
|
assertEquals("cms_legacy_module", ModuleRegistryService.titleOf(new HashMap<>(), "cms_legacy_module"));
|
||||||
|
assertEquals("cms_legacy_module", ModuleRegistryService.titleOf(null, "cms_legacy_module"));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void titleOfKeepsNullModuleNull() {
|
||||||
|
assertNull(ModuleRegistryService.titleOf(new HashMap<>(), null));
|
||||||
|
}
|
||||||
|
|
||||||
|
private ModuleRegistry module(String name, String title, int enabled, int sortNumber, int hasCollege) {
|
||||||
ModuleRegistry item = new ModuleRegistry();
|
ModuleRegistry item = new ModuleRegistry();
|
||||||
item.setModule(name);
|
item.setModule(name);
|
||||||
item.setTableName(name);
|
item.setTableName(name);
|
||||||
@@ -107,6 +126,7 @@ class ModuleRegistryServiceImplTest {
|
|||||||
item.setDetailKey(name);
|
item.setDetailKey(name);
|
||||||
item.setEnabled(enabled);
|
item.setEnabled(enabled);
|
||||||
item.setSortNumber(sortNumber);
|
item.setSortNumber(sortNumber);
|
||||||
|
item.setHasCollege(hasCollege);
|
||||||
item.setDeleted(0);
|
item.setDeleted(0);
|
||||||
return item;
|
return item;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user