Files
lingxi/docs/开发计划/灵犀_A4数据字典与系统配置_实施报告_V0.1.md
T
Stev_Wang 02c716d6f2 docs(plan): A4 实施报告与计划回写
背景:A4 开发完成,需要回写实施报告、技术架构与开发计划状态。\n改动:新增 A4 实施报告;技术架构补 system_config_versions 与 dict:manage 口径;开发计划 A4 标记完成并回写门禁/遗留;README 索引补 A4 报告。\n影响面:仅文档;不改变需求与技术边界。\n回滚:回滚本提交即可。\n验证:prettier 通过;文档链接已检查。
2026-11-04 11:50:30 +08:00

12 KiB
Raw Blame History

灵犀 A4 数据字典与系统配置 · 实施报告 V0.1

项 内容
实施日期 2026-11-04
批次 A4(数据字典与系统配置)
状态 已完成(开发 + 全部门禁 + 浏览器 e2e 通过;未推送)
依据 开发计划 V0.1 §6.5 / §7.3 / §12.1;PRD V1.6 §5.4.5 / R-01 / R-10 / R-26;技术架构 V0.4 §5.3 / §5.7 / §6.3.7;ADR-001
视觉基线 A0「方向 A · 云白蓝」;用户选定 方案 A · 主从分栏
迁移 prisma/migrations/20261104024511_a4_dict_system_configs/
前端路由 /system/dicts、/system/dicts/:type、/system/settings

1 范围与完成度

任务 内容 状态
A4-BE-01 字典模型与三层合并(dict_types / dict_items,树 / 词表一张表) ✅
A4-BE-02 系统配置与版本(system_configs + system_config_versions,阈值同源) ✅
A4-BE-03 命名规则服务(R-01 占位符 / 白名单 / 同名序号 / ≤100) ✅
A4-BE-04 字典快照(R-10,共享契约 + toDictItemSnapshot) ✅
A4-FE-01 字典管理(方案 A 主从分栏,树 / 词表双视图) ✅
A4-FE-02 系统配置页(6 组 / 命名预览 / 阈值二次确认 / 变更记录) ✅
A4-QA-01 合并 / 快照 / 同源用例 + 浏览器 e2e ✅

权限口径:A2 定稿的 30 个权限点中字典为单个 dict:manage;系统配置为 system:configure。本轮未新增权限点,已同步技术架构 §6.3.7 与开发计划附录 B / C。


2 交付物

2.1 文档与决策

  • 技术架构 §5.3 / §5.7 / §6.3.7 回写:新增 system_config_versions,字典权限统一 dict:manage。
  • 开发计划 §7.3 / 附录 B / 附录 C 权限口径回写。
  • prisma/SEED.md 更新:system_configs 26 项、A4 字典 13 类 / 96 条。
  • 前端原型:design/a4-ui-v1/(三套候选 + 两轮独立评审 + 用户选定 A);实现截图:design/a4-ui-v1/impl/。

2.2 共享契约(packages/shared)

  • schemas/dict.ts:字典类型 / 条目 / 三层来源 / 类型与条目编码不可改 / 查询与写接口 / toDictItemSnapshot。
  • schemas/config.ts:系统配置响应、批量保存(expectedVersion)、变更记录、effective 读取。
  • config/system-configs.ts:26 项配置定义(key / group / label / description / impact / type / default / 约束 / public)与 validateSystemConfigValue;前后端共用,R-26 唯一事实源。
  • naming.ts:NAMING_TOKEN_DEFINITIONS、validateNamingTemplate、resolveNamingTemplate、formatNamingDate、formatNamingSequence;保留既有 dedupeName / sanitizeFilename。
  • schemas/client.ts:CUSTOMER_BASE_FIELD_HARD_LIMIT = 50 作为 DTO 防御性硬上界;实际生效上限由服务端动态读取。
  • 单测:dict.spec.ts / config.spec.ts / naming.spec.ts 覆盖合并口径、快照、配置校验、模板解析、同名序号。

2.3 数据库与种子

  • 迁移 20261104024511_a4_dict_system_configs:
    • dict_types(code unique;group / kind / industry_scoped / builtin / status / sort);
    • dict_items(type_id + code + industry_id;parent_code;dict_items_no_self_parent_check);
    • 部分唯一索引:通用层 (type_id, code) WHERE industry_id IS NULL、行业层 (type_id, code, industry_id) WHERE industry_id IS NOT NULL;
    • system_config_versions(append-only:config_key / version / old_value / new_value / reason / changed_by / changed_at;(config_key, version) 唯一)。
  • 种子:
    • system_configs 26 项,元数据来自 SYSTEM_CONFIG_DEFINITIONS;重复执行不覆盖人工调整值 / 版本;
    • 字典 13 类 / 96 条,行业作用域映射 A3 行业(电商 / 服饰鞋包 / 美妆个护 / 金融 / 信贷 等);内置类型不可删除、条目编码不可改。

2.4 后端(apps/api)

模块 接口
catalog GET /dicts、GET /dicts/{code}、POST /dicts、PATCH /dicts/{code}、PATCH /dicts/{code}/status、DELETE /dicts/{code}、GET /dicts/{code}/items、POST /dicts/{code}/items、PATCH /dicts/{code}/items/{itemId}
platform GET /system/configs、GET /system/configs/effective、PUT /system/configs、GET /system/configs/history
  • 三层合并为纯函数 dict-merge.ts:通用 0 / 父行业 1 / 本行业 2,同 code 覆盖;返回 source / overridden / layers / effectiveCount / disabledCount。
  • 编码不可改:schema .strict() 不接收 code / kind / industryScoped / parentCode / industryId 更新;冲突 40902;内置类型 / 有子条目删除 40001。
  • 配置保存:事务 + expectedVersion 乐观锁(40901)+ 条件 version+1 + system_config_versions + @Audit('platform.system_config.update') 前后值。
  • 阈值同源:roles / users / customers / customer-fields 已改为 SystemConfigService.getNumber('limit.*');shared 常量只作 fallback。
  • 命名服务:resolveScriptName / resolveImageName / resolveVideoName,读 naming.* + max_length + duplicate_start;缺占位符 40001,非法 / 超长 / 同名过多 42201。
  • 快照:dict.mapper#toDictSnapshot 委托 shared;单测证明改名后历史快照不变(R-10)。
  • 审计:字典类型 / 条目写操作、系统配置保存均 @Audit + 结构化日志前后值(A5 落审计表)。

2.5 前端(apps/web,方案 A)

页面 路由 实现
数据字典 /system/dicts 左 320 类型列表(4 分组折叠、搜索、状态筛选、统计)+ 右类型头 / 行业三层 chips / 树表切换 / 条目表
字典详情 /system/dicts/:type 同一主从骨架深链;默认 script_direction + 电商/服饰鞋包,7 项三层合并
系统配置 /system/settings 左 200 分组导航 + 6 组表单 + 命名占位符 / 实时预览 + 阈值二次确认 + 保存条 + 变更记录抽屉
  • 组件:DictTypeListPanel / DictDetailPanel / DictItemsView / DictItemSheet / DictTypeModal;SettingsPage / SettingsNav / SettingsGroupForm / ConfigRow / NamingAside / ConfigHistorySheet。
  • API / 状态:api/dicts.ts / api/configs.ts,shared Zod parse;use-dicts / use-configs,含 useEffectiveSystemConfigsQuery / useCustomerFieldLimits。
  • 阈值同源:客户列表 / 详情 / 字段审批的 10 / 5 显示与禁用判断改为 effective config;后端最终校验不变。
  • 路由 / 壳层:/system/dicts、/system/dicts/:type、/system/settings 已注册并按 dict:manage / system:configure 守卫;AppLayout 满高 flush;main.tsx 引入 dicts.css / settings.css。
  • 侧边栏已更名「数据字典」(commit 32b44bd)。

3 验证结果

门禁 命令 结果
格式 pnpm format:check ✅
Lint pnpm lint ✅ 0 warning
类型 pnpm typecheck ✅ 10/10 tasks
单测(shared) pnpm --filter @lingxi/shared test ✅ 13 files / 147 tests
单测(api) pnpm --filter @lingxi/api test ✅ 18 files / 137 tests
单测(web) pnpm --filter @lingxi/web test ✅ 29 files / 154 tests
集成 pnpm --filter @lingxi/api test:int ✅ 7 files / 58 tests(新增 catalog 10 + config 9)
构建 pnpm build ✅ 8/8 tasks
OpenAPI pnpm openapi:export + pnpm openapi:diff ✅ 无破坏性变更
浏览器 e2e A4_E2E=1 pnpm exec playwright test e2e/a4-dicts-settings.spec.ts ✅ 7 passed(约 1.1m)
本地 CI pnpm ci:local ✅ 262.3s,全部门禁通过(build + test:int + openapi + smoke e2e)

浏览器人工验收(真实 API + PG + seed):

  • /system/dicts 默认脚本方向 / 电商·服饰鞋包,显示三层 chips(通用 5 + 父行业 2 + 本行业 1 = 生效 7),种草短视频「父行业 / 已覆盖」、直播切片「父行业」、穿搭教程「本行业」;
  • 树 / 词表切换、非行业类型「全平台通用」、条目新增 / 编辑(编码只读)/ 停用 / 启用、类型新建 / 编辑(编码只读);
  • /system/settings 6 组 / 26 项、命名占位符与实时预览、阈值旧→新二次确认、保存后版本 +1、变更记录(版本 / 前后值 / 操作人 / 时间 / 原因);
  • 1440×900 与 1024×900 无横向溢出;截图:impl。

4 关键口径与实现说明

  1. 一张表同时表达树与词表:dict_items.parent_code 表达二级树;不另建行业二级表(ADR-002 后行业树由 industries 承载)。
  2. 三层合并:查询取通用 + 父行业 + 本行业;同 code 以更具体层覆盖,返回 source / overridden;非行业作用域类型强制只读通用层。
  3. 编码不可改:类型 code 与条目 code 创建后不可改;更新 DTO .strict() 不接收;前端编辑态只读。
  4. 配置唯一来源:SYSTEM_CONFIG_DEFINITIONS 为元数据 / 默认值 / 校验唯一事实源;system_configs 只存值 / 版本;/system/configs/effective 供前端读取 public 项(上限),前端不再硬编码 10 / 5。
  5. 版本与变更:保存走事务 + 乐观锁;每次变更写 system_config_versions;审计日志记录前后值,A5 落审计表。
  6. 命名规则:shared 纯函数被前端预览与后端 NamingService 共用;字符白名单、占位符、≤100、同名序号一致。
  7. 快照:toDictItemSnapshot 生成填写时 typeCode / code / name / parentCode / industryId / capturedAt;业务记录在 B0 / M1 引用时保存,A4 只提供契约与工具。

5 已知差异与遗留

编号 项 说明 / 建议
1 refCount 固定 0 A4 尚无引用表;B0 / B1 资产与行业包落地后接入真实引用计数
2 配置项 26 项(原型 24 项) 含 A0 已有 2 项 AI 成本比例配置;A7 会提供专门 AI 配置页,A4 暂列在「业务阈值」
3 审计落库 A4 只写结构化 @Audit 日志;审计表 / 检索 / 导出按 A5
4 只读态不可达 路由守卫为 dict:manage / system:configure,无独立 view 权限;页面保留 canWrite=false 防御分支,后续如拆分 view/manage 权限可直接生效
5 详情面包屑 /system/dicts/:type 面包屑仍为「管理中心 / 数据字典」,未展示类型名;breadcrumbs.ts 无类型参数,后续统一处理(与 A3 客户详情同源问题)
6 字典类型分页 当前 13 类,前端一次拉 100 条后本地筛选;类型数 >100 时需补游标分页 UI
7 updatedBy 存储 字典表存操作人姓名;system_configs.updated_by 存 ULID,列表 / 历史批量 join 返回姓名
8 DTO 硬上界 客户基础字段 schema 硬上界 50(防御性);若配置 >50 仍会被 Zod 拦截,需同步调整 shared 硬上界
9 e2e A4_E2E=1 用例已落地并 7/7 通过;默认 skip,不影响 smoke / CI;e2e/README.md 待补登记一行

6 回滚方式

  1. 代码:回滚 A4 提交(catalog / platform 配置与命名 / 前端字典与配置页 / shared 契约 / 路由 / 样式 / 迁移 / seed / 文档)。
  2. 数据库:A4 为新增表,回滚可保留表;彻底回滚删除顺序:system_config_versions → dict_items → dict_types;system_configs 为 A0 既有表,保留。
  3. 配置:删除 A4 新增 system_configs 行或恢复默认值;system_config_versions 清空即可。
  4. 前端:回滚路由 / 导航 / 页面 / 样式;/system/dicts / /system/settings 将回退到 Workspace 占位页(导航保留)。

7 本地运行

pnpm check:dev
pnpm db:init
pnpm dev
# 浏览器 http://127.0.0.1:5173 → 管理中心 / 数据字典、系统配置

A4 e2e(需先起 pnpm dev):

A4_E2E=1 pnpm exec playwright test e2e/a4-dicts-settings.spec.ts --reporter=list