feat(client): A3 客户与行业目录(两级行业、客户组、字段审批)
背景:A3 需把行业 / 客户 / 客户组 / 客户级字段立为资产根节点;用户确认两级行业树与混搭 UI(行业 B / 客户列表 A / 详情 A / 新建 B / 客户组 B / 字段 A)。 改动: - 新增 ADR-002,行业改为两级树,客户挂末级行业;同步 PRD / 技术架构 / 开发计划。 - 新增 client 模块:industries / customers / customer-groups / customer-fields 接口、真实 PrismaCustomerDirectory、客户状态机与客户转交、客户组数据范围解析、跨客户 404。 - 新增迁移与幂等种子(电商/金融两级行业、2 客户、1 客户组、4 条字段)。 - 前端新增行业/客户/客户组/字段审批页面与路由、导航更名(客户管理 / 行业管理 / 客户组),复用现有 token。 - 补充 shared 契约单测、client 集成用例、A3 浏览器 e2e。 影响面:client 模块与 client 表、shared 契约、web 路由/导航、ScopeContext 数据范围、OpenAPI 与种子;A1 客户转交占位由 client 实现替换。 回滚:回滚本提交;数据库保留新增表或按报告执行 down SQL;CUSTOMER_DIRECTORY 可切回空集实现。 验证:format:check / lint / typecheck / test 全绿(shared 131 / api 90 / web 125,共 346);test:int 5 文件 39 条;A3 e2e 1/1;build 8/8;真实浏览器六页验证通过。
This commit is contained in:
1 parent
8626996bb1
commit
140dc386c1
54 files changed
+9018
-147
No files matched your search
@@ -0,0 +1,158 @@
|
||||
# 灵犀 A3 客户与行业目录 · 实施报告 V0.1
|
||||
|
||||
| 项 | 内容 |
|
||||
| --- | --- |
|
||||
| 实施日期 | 2026-11-03 |
|
||||
| 批次 | A3(客户与行业目录) |
|
||||
| 状态 | 已完成,门禁通过;未推送 |
|
||||
| 依据 | 开发计划 V0.1 §6.4 / §7.4 / §12.1 / 附录 C;PRD V1.6 §5.4.2 / §5.4.9、R-07 / R-20;技术架构 V0.4 §5.3 / §5.4.1 / §6.3.2;ADR-002 |
|
||||
| 决策记录 | [ADR-002 两级行业目录](../adr/ADR-002-两级行业目录.md) |
|
||||
| 视觉基线 | A0「方向 A · 云白蓝」;A3 用户选定混搭:**行业管理 B / 客户列表 A / 客户详情 A / 新建客户 B / 客户组 B / 字段审批 A** |
|
||||
|
||||
---
|
||||
|
||||
## 1 范围与完成度
|
||||
|
||||
| 任务 | 内容 | 状态 |
|
||||
| --- | --- | --- |
|
||||
| A3-BE-01 | 两级行业树(一级 → 二级)、名称唯一、停用/删除引用保护 | ✅ |
|
||||
| A3-BE-02 | 客户档案、简称必填、同末级行业唯一、状态机(在服务 / 暂停 / 结束) | ✅ |
|
||||
| A3-BE-03 | 客户组与成员,客户组作为数据范围最小单位 | ✅ |
|
||||
| A3-BE-04 | 客户级字段申请 / 审批,进入生成 ≤ 5,超限 40001 | ✅ |
|
||||
| A3-BE-05 | 客户上下文与归属校验,跨客户 404;真实 `CustomerDirectory` | ✅ |
|
||||
| A3-FE-01 | 行业管理(B:左两级树 + 右详情) | ✅ |
|
||||
| A3-FE-02 | 客户列表 / 详情(A)、四步新建(B) | ✅ |
|
||||
| A3-FE-03 | 客户组(B:左列表 + 右成员) | ✅ |
|
||||
| A3-FE-04 | 字段申请与审批(A:全宽表格 + 审批弹窗) | ✅ |
|
||||
| A3-QA-01 | 唯一性 / 状态 / 范围 / 字段上限用例 + 浏览器 e2e | ✅ |
|
||||
|
||||
> 用户确认口径:两级行业树、客户挂末级行业、一级全局唯一 + 二级同父唯一、只停用 + 引用保护、字段双层、资产包留入口、客户转交纳入 A3、复用现有 30 个权限点。
|
||||
|
||||
---
|
||||
|
||||
## 2 交付物
|
||||
|
||||
### 2.1 文档与决策
|
||||
|
||||
- 新增 [ADR-002 两级行业目录](../adr/ADR-002-两级行业目录.md) 并登记 ADR 索引;含 PRD / 技术架构 / 开发计划回写清单。
|
||||
- 同步 PRD R-20 / §5.4.2 / §5.4.5、技术架构 §5.3 / §5.4.1 / §6.3.2、开发计划 §6.4 / §7.4 / §12.1 / 附录 C。
|
||||
- 前端原型:`design/a3-ui-v1/`(三套候选 + 独立评审 + 用户选定混搭);实现截图:`design/a3-ui-v1/impl/`。
|
||||
|
||||
### 2.2 共享契约(`packages/shared`)
|
||||
|
||||
- `schemas/client.ts`:行业两级树 / 引用、客户档案与状态、客户组、客户级字段、转交、列表 query;写接口 `.strict()`。
|
||||
- 上限常量 `CUSTOMER_BASE_FIELD_LIMIT = 10`、`CUSTOMER_GEN_FIELD_LIMIT = 5`(A4 迁入 `system_configs`,R-26)。
|
||||
- `schemas/client.spec.ts`:行业 / 客户 / 客户组 / 字段契约覆盖。
|
||||
|
||||
### 2.3 数据库与种子
|
||||
|
||||
- 迁移 `prisma/migrations/20261103130048_a3_client_industry_customer/`(expand):
|
||||
- `industries`:`parent_id` / `level` / `sort_order` / `status` / `remark`;部分唯一索引(一级全局唯一、二级同父唯一);两级 CHECK 与非自引用 CHECK;
|
||||
- `customers`:`industry_id`、`short_name`、状态、负责人、对接人、偏好 JSONB、基础字段 JSONB、软删除;
|
||||
- `customer_groups` / `customer_group_members`;
|
||||
- `customer_fields`:进入生成与审批状态、申请人 / 审批人、驳回原因。
|
||||
- `prisma/seed.ts`:幂等写入 电商(服饰鞋包 / 美妆个护)、金融(信贷)、2 个客户、1 个客户组、4 条客户字段(1 条待审批)。
|
||||
- `prisma/SEED.md` 待同步(见遗留)。
|
||||
|
||||
### 2.4 后端(`apps/api/src/modules/client`)
|
||||
|
||||
| 接口 | 说明 |
|
||||
| --- | --- |
|
||||
| `GET/POST /industries`、`GET/PATCH/DELETE /industries/{id}`、`PATCH /industries/{id}/status` | 两级树、引用、停用/删除保护 |
|
||||
| `GET/POST /customers`、`GET/PATCH /customers/{id}`、`PATCH /customers/{id}/status` | 列表(ScopeContext 客户维度)、详情(跨客户 404)、状态机 |
|
||||
| `POST /customers/{id}/transfer`、`PUT /customers/{id}/groups`、`POST /customers/{id}/apply-industry-pack` | 转交、客户组、B0 空态 |
|
||||
| `GET/POST /customer-groups`、`GET/PATCH/DELETE /customer-groups/{id}`、`POST /customer-groups/{id}/members` | 客户组与成员幂等 |
|
||||
| `GET /customer-fields`、`POST /customer-fields/{id}/approve`、`GET/POST /customers/{id}/fields` | 审批队列、通过/驳回(驳回原因必填) |
|
||||
| `POST /users/{id}/transfer-customers` | 保留 A1 路径,由 client 模块落地批量转交 |
|
||||
|
||||
- 真实 `PrismaCustomerDirectory` 替换 `EmptyCustomerDirectory`;六类数据范围解析接入客户 / 客户组 / 部门 / 创建人。
|
||||
- 客户上下文:所有客户读写携带 `ScopeContext`;跨客户统一 404;客户组删除前校验 `data_scopes` 引用。
|
||||
- 审计:行业 / 客户 / 客户组 / 字段 / 转交均带 `@Audit()` 结构化日志(A5 落库)。
|
||||
- 测试:`client.int.spec.ts` 6 条(行业唯一/停用停用、客户唯一/跨客户/状态机、客户组范围、字段审批/超限、转交、状态只读)。
|
||||
|
||||
### 2.5 前端(`apps/web`)
|
||||
|
||||
| 页面 | 路由 | 选定布局 |
|
||||
| --- | --- | --- |
|
||||
| 行业管理 | `/system/industries` | B:左两级树 + 右详情(白底卡片) |
|
||||
| 客户列表 | `/system/customers` | A:全宽表格 + 筛选条 |
|
||||
| 客户详情 | `/system/customers/:id` | A:左锚点 + 中列 + 右侧状态 |
|
||||
| 新建客户 | `/system/customers/new` | B:左侧步骤 + 右侧表单,四步可回退 |
|
||||
| 客户组 | `/system/customer-groups` | B:左列表 + 右成员 |
|
||||
| 客户字段审批 | `/system/customer-fields/approvals` | A:全宽表格 + 通过/驳回弹窗 |
|
||||
|
||||
- 侧边栏更名:「客户」→「客户管理」、「行业树」→「行业管理」;新增「客户组」;「客户字段」指向审批页,权限 `customer_field:approve`。
|
||||
- 样式:`styles/client.css` 复用 `--lx-*` token;未新增依赖、未内联 SVG(图标走 `@lingxi/ui/icons`)。
|
||||
- API:`api/customers.ts` + `hooks/queries/use-client.ts`,全部用 shared Zod schema 解析。
|
||||
|
||||
---
|
||||
|
||||
## 3 验证结果
|
||||
|
||||
| 门禁 | 命令 | 结果 |
|
||||
| --- | --- | --- |
|
||||
| 格式 | `pnpm format:check` | ✅ |
|
||||
| Lint | `pnpm lint` | ✅ 0 warning |
|
||||
| 类型 | `pnpm typecheck` | ✅ 10/10 tasks |
|
||||
| 单测 | `pnpm test` | ✅ shared 131 / api 90 / web 125,共 346 |
|
||||
| 集成 | `pnpm test:int` | ✅ 5 文件 / 39 条(含 A3 client 6 条) |
|
||||
| 浏览器 e2e | `A3_E2E=1 pnpm exec playwright test e2e/a3-client.spec.ts` | ✅ 1 passed(11.6s) |
|
||||
| 构建 | `pnpm build` | ✅ 8/8 tasks |
|
||||
| OpenAPI | `pnpm openapi:export` | ✅ 新增 industries / customers / customer-groups / customer-fields 路径 |
|
||||
|
||||
浏览器验证(真实 API + PG + Chrome):登录 → 行业管理树/详情 → 客户列表 → 客户详情 → 新建客户 → 客户组 → 字段审批六页均正常渲染;控制台仅 favicon 404。截图:`design/a3-ui-v1/impl/`。
|
||||
|
||||
---
|
||||
|
||||
## 4 关键口径与实现说明
|
||||
|
||||
1. **两级行业**:`level` 由 `parent_id` 推导;数据库 CHECK 保证「parent 为空 ⇔ level=1」,服务层禁止二级下再建子级;名称唯一通过部分唯一索引兜底。
|
||||
2. **客户挂末级**:`assertLeafIndustry` 校验;客户更新不接收 `industryId`,跨行业移动走停用 + 新建。
|
||||
3. **状态机**:在服务 ⇄ 暂停;在服务 / 暂停 → 结束;结束不可逆。暂停 / 结束客户更新、字段申请、转交均返回 40903。
|
||||
4. **字段双层**:基础字段在 `customers.custom_fields` JSONB(≤10,免审批);`customer_fields` 只承载结构化 / 进入生成字段,进入生成需审批,按「占用槽位」计数(含待审批、不含已驳回)。
|
||||
5. **数据范围**:`CustomerDirectory` 真实实现;客户列表按 `customerIds` 过滤;单条跨客户读统一 404;客户组数据范围用例已验证。
|
||||
6. **客户转交**:`/customers/{id}/transfer` 需 `customer:transfer`;`/users/{id}/transfer-customers` 保留 A1 路径,允许 `customer:transfer` 或 `user:manage`。
|
||||
7. **资产包**:`POST /customers/{id}/apply-industry-pack` 当前返回 42201「B0 批次提供」,前端显示空态,不伪造数据。
|
||||
|
||||
---
|
||||
|
||||
## 5 已知差异与遗留
|
||||
|
||||
| 编号 | 项 | 说明 / 建议 |
|
||||
| --- | --- | --- |
|
||||
| 1 | 行业资产包套用 | 依赖 B0/B1,当前接口返回明确业务提示 + 前端空态;B0 落地后接通。 |
|
||||
| 2 | 审计落库 | A3 变更走 `@Audit()` 结构化 Pino 日志;审计表、检索、导出按 A5。 |
|
||||
| 3 | 字段上限配置 | 共享常量 `10 / 5`;A4 `system_configs` 落地后迁入,前端上限读配置接口(R-26)。 |
|
||||
| 4 | 客户级字段类型 | 仅白名单 文本 / 数字 / 日期 / 单选;选项在原型中简化,完整枚举来源随 A4 字典。 |
|
||||
| 5 | 行业资产数 | `assetCount` 固定 0;B1 资产表落地后接入。 |
|
||||
| 6 | 客户列表分页 | 前端一次拉 100 条(A3 规模);B2 后按大数据量补游标分页 UI。 |
|
||||
| 7 | 客户详情面包屑 | 由导航派生,末级显示「客户管理」而非客户名;与 A2 角色编辑器同源问题,后续统一。 |
|
||||
| 8 | 状态截图 | 原型阶段 A/B/C 状态图已覆盖;实现截图以主态 + e2e 为准,暂停/结束状态由 API int + e2e 覆盖。 |
|
||||
| 9 | `prisma/SEED.md` | 需补 A3 行业 / 客户 / 客户组 / 字段种子说明(本次未改)。 |
|
||||
| 10 | `AppModule` 的 worker | `pnpm dev` 下 worker 因 `REDIS_URL` 缺失退出,API/Web 正常;A0 遗留,非 A3 范围。 |
|
||||
|
||||
---
|
||||
|
||||
## 6 回滚方式
|
||||
|
||||
1. 代码:回滚本批 commit(client 模块、前端页面、shared schema、路由 / 导航、迁移文件、seed、ADR 与文档)。
|
||||
2. 数据库:A3 为新增表,回滚可保留表(不影响 A1/A2);如需彻底回滚,执行 down SQL 删除 `customer_fields → customer_group_members → customer_groups → customers → industries`(先备份种子数据)。
|
||||
3. 配置:`CUSTOMER_DIRECTORY` 改回 `EmptyCustomerDirectory` 即恢复 A2 空客户集行为(需同时回滚 client 模块 provider)。
|
||||
4. 前端:回滚路由 / 导航与 `pages/system/{industries,customers,customer-groups,customer-fields}`,恢复「客户 / 行业树」旧菜单。
|
||||
|
||||
---
|
||||
|
||||
## 7 本地运行
|
||||
|
||||
```bash
|
||||
pnpm check:dev
|
||||
pnpm db:init
|
||||
pnpm dev
|
||||
# 浏览器 http://127.0.0.1:5173 → 管理中心 / 行业管理、客户管理、客户组、客户字段
|
||||
```
|
||||
|
||||
A3 e2e(需先起 `pnpm dev`):
|
||||
|
||||
```bash
|
||||
A3_E2E=1 pnpm exec playwright test e2e/a3-client.spec.ts --reporter=list
|
||||
```
|
||||
Reference in new issue
Block a user