Files
lingxi/docs/开发计划/灵犀_A3客户与行业目录_实施报告_V0.1.md
Stev_Wang 3e4bf48841
ci / 质量门禁 (push) Waiting to run
fix(web): A3 页面对齐选定原型(行业 Tab / 新建向导 / 筛选条)
背景:A3 首版实现把行业管理简化为普通树 + 简化详情,新建客户用 Semi 默认 Steps + 下拉框,与用户选定的原型 B 结构差异较大;客户管理筛选区也缺少白底与统一图标间距。

改动:
- 行业管理(B):补头像 + 状态/层级标签、基本信息/子行业/引用 Tab、最近变更时间线、子行业与引用客户列表,右栏白底卡片,删除入口。
- 新建客户(B):补左侧「创建客户/共 4 步」步骤导航(标题 + 说明 + 当前/完成态)、两级行业卡片选择、表单卡头/底部操作条。
- 客户组(B):成员改为「客户/所属行业/状态/负责人/操作」表格并支持移除。
- 客户列表(A):补表头统计、筛选区白底卡片与搜索图标间距。
- 客户详情(A):补右栏快捷操作;字段审批(A):补状态分段、中文类型。
- 主操作 theme=solid、次操作 theme=outline,与原型按钮层级一致。
- 更新 A3 实施报告与设计说明。

影响面:仅 apps/web A3 页面、client.css 与文档;接口、数据、权限不变。
验证:lint / format:check / typecheck / test(shared 131 / api 90 / web 125)/ build 全绿;真实浏览器六页重拍通过。截图:design/a3-ui-v1/impl/。
2026-11-04 08:04:14 +08:00

11 KiB
Raw Permalink Blame History

灵犀 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 两级行业目录
视觉基线 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 索引;含 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。
  • 2026-11-03 UI 二次对齐(用户反馈行业管理 / 新建客户与原型差距大):
    • 行业管理补回原型 B 的头像 + 状态/层级标签 + Tab(基本信息 / 子行业 / 引用)+ 最近变更时间线 + 子行业列表 / 引用客户列表;右栏白底卡片。
    • 新建客户补回原型 B 的左侧「创建客户 / 共 4 步」步骤导航(标题 + 说明 + 当前/完成态)与两级行业卡片选择(一级分组、二级单选、客户数),替换原 Semi 默认 Steps + 下拉框。
    • 客户组补成员表(客户 / 所属行业 / 状态 / 负责人 / 操作);客户列表补表头统计;客户详情补右栏快捷操作;字段审批补分段状态与中文类型。
    • 主操作按钮统一 theme="solid";副操作保持描边。截图已重拍至 design/a3-ui-v1/impl/。
    • 客户管理筛选区补白底卡片与搜索图标/占位间距,与用户管理、角色管理的筛选条口径一致(2026-11-04)。
  • 样式: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 本地运行

pnpm check:dev
pnpm db:init
pnpm dev
# 浏览器 http://127.0.0.1:5173 → 管理中心 / 行业管理、客户管理、客户组、客户字段

A3 e2e(需先起 pnpm dev):

A3_E2E=1 pnpm exec playwright test e2e/a3-client.spec.ts --reporter=list