docs(plan): A4 实施报告与计划回写
背景:A4 开发完成,需要回写实施报告、技术架构与开发计划状态。\n改动:新增 A4 实施报告;技术架构补 system_config_versions 与 dict:manage 口径;开发计划 A4 标记完成并回写门禁/遗留;README 索引补 A4 报告。\n影响面:仅文档;不改变需求与技术边界。\n回滚:回滚本提交即可。\n验证:prettier 通过;文档链接已检查。
This commit is contained in:
1 parent
f16d04fe02
commit
02c716d6f2
4 files changed
+179
-7
No files matched your search
@@ -0,0 +1,165 @@
|
||||
# 灵犀 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](../../design/a4-ui-v1/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 本地运行
|
||||
|
||||
```bash
|
||||
pnpm check:dev
|
||||
pnpm db:init
|
||||
pnpm dev
|
||||
# 浏览器 http://127.0.0.1:5173 → 管理中心 / 数据字典、系统配置
|
||||
```
|
||||
|
||||
A4 e2e(需先起 `pnpm dev`):
|
||||
|
||||
```bash
|
||||
A4_E2E=1 pnpm exec playwright test e2e/a4-dicts-settings.spec.ts --reporter=list
|
||||
```
|
||||
Reference in new issue
Block a user