9.7 KiB
入口模块租户配置表迁移方案
适用范围:首页入口模块、入口模块管理页、首页可见性判定、交叉评查入口等依赖
leaudit_entry_modules.areas的场景
文档定位:把当前入口模块的areas JSONB从自由字符串配置升级为“租户关系表 + 主数据校验”的落地方案。
1. 结论先行
当前入口模块是整个“地区租户化”里最明显、最优先、最适合先落地的切入点。
原因有 4 个:
- 问题最直观,用户已经明确感知到“新租户没法分配入口”
- 当前设计最脆弱,
areas JSONB没有主数据约束 - 前后端都存在固定地区假设
- 首页和交叉评查都依赖它做可见性判断
因此建议先把入口模块从:
leaudit_entry_modules + areas JSONB
升级为:
leaudit_entry_modulesleaudit_entry_module_tenantssys_tenants
2. 当前问题深挖
2.1 现有表结构问题
当前表:
leaudit_entry_modules(
id,
name,
description,
path,
icon_path,
areas JSONB,
sort_order,
is_enabled
)
问题:
areas是自由 JSONarea值没有外键约束enabled/sort_order与租户主数据重复- 无法防止写入不存在的租户
- 无法直接做关系查询和索引优化
2.2 后端当前风险
当前 EntryModuleAdminServiceImpl.py:
- 创建和更新时直接把前端传入
areas序列化成 JSON - 没有合法租户校验
- 没有和用户租户主数据对齐
当前 homeServiceImpl.py:
- 直接按
user_area = area_item->>'area' - 支持
default兜底 super_admin特判 bypass
这意味着:
- 可见性规则绑定在脏字符串上
- 新租户即使进库,首页也可能因为字符串不一致看不到
2.3 前端当前风险
当前 EntryModuleNewClient.tsx:
- 地区候选列表硬编码为固定 5 项
当前 cross-checking-access.ts:
- 用固定地区别名数组判断入口可见性
provincial_admin自动补省局
这意味着:
- 后端就算支持新租户,前端也无法选择
- 首页入口和交叉评查入口会出现不一致
3. 目标数据模型
3.1 保留主表
保留:
leaudit_entry_modules
用于表达模块本身:
- 名称
- 描述
- 跳转路径
- 图标
- 排序
- 启用状态
3.2 新增关系表
建议新增:
CREATE TABLE IF NOT EXISTS leaudit_entry_module_tenants (
id BIGSERIAL PRIMARY KEY,
entry_module_id BIGINT NOT NULL REFERENCES leaudit_entry_modules(id),
tenant_code VARCHAR(64) NOT NULL,
is_enabled BOOLEAN NOT NULL DEFAULT TRUE,
sort_order INT NOT NULL DEFAULT 0,
visibility_scope VARCHAR(32) NOT NULL DEFAULT 'TENANT',
ext JSONB NOT NULL DEFAULT '{}'::jsonb,
created_at TIMESTAMP NOT NULL DEFAULT NOW(),
updated_at TIMESTAMP NOT NULL DEFAULT NOW(),
deleted_at TIMESTAMP NULL,
UNIQUE (entry_module_id, tenant_code)
);
3.3 为什么关系表足够
关系表解决了当前 JSON 方案的关键缺陷:
- 可按
tenant_code精确筛选 - 可直接做唯一约束
- 可扩展更多可见性字段
- 可做索引和分页查询
- 能和租户主数据自然联表
4. 字段设计建议
4.1 tenant_code
必须来自 sys_tenants.tenant_code,不允许任意字符串。
4.2 is_enabled
表示该入口是否对该租户生效。
说明:
- 模块总开关在
leaudit_entry_modules.is_enabled - 租户粒度开关在
leaudit_entry_module_tenants.is_enabled
4.3 sort_order
表示该模块在该租户视角下的局部排序。
如果当前不需要租户级排序,也建议先保留,因为后续:
- 不同租户首页布局可能不同
- 试点租户可能需要不同优先级
4.4 visibility_scope
建议预留:
TENANTPUBLICHEADQUARTER_ONLY
当前先以 TENANT 为主,但保留扩展空间,避免以后再次改表。
5. 接口改造建议
5.1 管理端列表接口
当前:
GET /api/v3/entry-modules?area=...
建议后续改为:
GET /api/v3/entry-modules?tenant_code=...
兼容期:
area仍可保留,但后端先归一成tenant_code
5.2 管理端创建/更新接口
当前请求体:
{
"name": "交叉评查",
"route_path": "/cross-checking",
"areas": [
{"area": "梅州", "enabled": true, "sort_order": 1}
]
}
建议升级为:
{
"name": "交叉评查",
"route_path": "/cross-checking",
"tenants": [
{"tenant_code": "MZ", "enabled": true, "sort_order": 1}
]
}
兼容策略:
- 后端 DTO 同时接收
areas和tenants - 兼容期内优先使用
tenants areas走别名归一后再写关系表
5.3 首页获取入口接口
当前:
HomeServiceImpl.GetEntryModules(UserId)
建议内部改造为:
- 先解析用户
tenant_code - 再根据
leaudit_entry_module_tenants判断可见性
SQL 方向应改成:
- 联表
leaudit_entry_module_tenants - 不再扫描
jsonb_array_elements
6. 前端改造建议
6.1 入口模块新建页
当前问题:
AREA_OPTIONS写死
建议:
- 页面加载时调用租户主数据接口
- 用
tenant_code作为 value - 用
tenant_name作为 label - 表单字段从
selectedAreas改为selectedTenantCodes
6.2 入口模块列表页筛选
建议:
- 筛选条件改为租户下拉
- 后端接收
tenant_code - 列表展示使用租户名称而非裸字符串
6.3 交叉评查入口判定
当前 cross-checking-access.ts 不应再:
- 猜测地区别名
includes("省")- 针对
provincial_admin自动补省局
建议:
- 登录态透出
tenant_code - 首页接口直接返回“当前用户可见模块”
- 前端不再做租户匹配推断
7. 迁移策略
7.1 第一步:建新表,不改老表
先新增:
leaudit_entry_module_tenants
此时:
- 旧
areas JSONB继续存在 - 新服务层支持双写
7.2 第二步:把历史 JSON 展开入新表
迁移逻辑:
- 读取
leaudit_entry_modules.areas - 对每个
area执行租户归一 - 写入
leaudit_entry_module_tenants - 对映射失败的值输出复核清单
7.3 第三步:服务层读新表
顺序建议:
- 管理端详情/列表先从新表组装返回
- 首页可见性改为读新表
- 交叉评查入口依赖首页接口结果,不再本地推断
7.4 第四步:前端提交新结构
前端提交统一改成 tenant_code 后:
areas字段只保留兼容- 新表成为唯一写入目标
7.5 第五步:下线旧 JSON
待所有读写都切到新表后:
areas字段可以保留为快照- 或在后续版本彻底废弃
8. 兼容期设计
8.1 DTO 兼容
建议 DTO 增加:
tenants: list[EntryModuleTenantDTO] | None
保留:
areas: list[EntryModuleAreaDTO] | None
优先级:
tenants优先areas仅用于历史前端兼容
8.2 VO 兼容
接口返回建议同时包含:
tenantsareas
其中:
tenants是标准结构areas是兼容展示结构
避免一次性打崩旧页面。
8.3 SQL seed 兼容
当前:
seed_home_entry_modules.sqlseed_govdoc_entry_module.sql
都直接写死 areas JSONB。
建议:
- 先保留旧插入
- 新增对应关系表 seed
- 后续把内置入口的租户分配从 JSON 迁到关系表
9. 需要同步改的代码点
后端重点:
entryModuleDto.pyentryModuleAdminServiceImpl.pyhomeServiceImpl.pyentryModuleController.py- 相关 VO 定义
前端重点:
EntryModuleNewClient.tsxentry-modulesAPI 封装- 首页入口 hooks
cross-checking-access.ts
数据重点:
schema_v2_add_evaluation_tables.sqlseed_home_entry_modules.sqlseed_govdoc_entry_module.sql
10. 风险清单
10.1 首页入口可能瞬时丢失
如果首页先切新表,但历史数据尚未迁入新关系表,会导致所有入口不可见。
所以顺序必须是:
- 先迁数据
- 再切读逻辑
10.2 新旧字段并存期可能双写不一致
必须规定:
- 标准源为新关系表
- 旧
areas只做兼容输出或快照
10.3 交叉评查入口可能与首页结果不一致
因为当前它自己做了一套前端地区推断。
必须同步下线这套本地逻辑。
10.4 自定义新租户仍可能被旧 seed 覆盖
如果后续还执行写死地区的 seed,会把新租户体系重新拉回旧模型。
11. 验收标准
入口模块迁移完成后,至少应满足:
- 新增租户后,无需改前端常量即可在管理页被选中
- 管理端不能给不存在的租户分配入口
- 首页入口仅按
tenant_code判断,不再比对中文地区字符串 - 交叉评查入口不再依赖固定地区别名数组
- 历史入口模块在旧租户下可见性与迁移前一致
- 新租户创建后,可以直接分配现有入口模块
12. 本文档解决什么问题
本文档主要解决:
- 入口模块为什么是租户化第一改造点
- 当前
areas JSONB设计具体哪里不够 - 关系表应该怎么建
- 首页和交叉评查为什么会被连带影响
- 迁移顺序应该怎么安排
建议联动阅读: