From e027b5abc468c5cb1ff49929e3cb7db4d8ae84f9 Mon Sep 17 00:00:00 2001 From: zlwu <704541015@qq.com> Date: Wed, 7 Oct 2026 15:52:29 +0800 Subject: [PATCH] docs: add customer family protocol link design. Record agreed model for mother/father/child customer IDs on protocol parties, save-time sync, id-card merge, and customer detail relations. Co-authored-by: Cursor --- ...07-customer-family-protocol-link-design.md | 210 ++++++++++++++++++ 1 file changed, 210 insertions(+) create mode 100644 docs/superpowers/specs/2026-10-07-customer-family-protocol-link-design.md diff --git a/docs/superpowers/specs/2026-10-07-customer-family-protocol-link-design.md b/docs/superpowers/specs/2026-10-07-customer-family-protocol-link-design.md new file mode 100644 index 00000000..51b60c19 --- /dev/null +++ b/docs/superpowers/specs/2026-10-07-customer-family-protocol-link-design.md @@ -0,0 +1,210 @@ +# 客户管理 · 协议家庭成员关联设计 + +> 日期:2026-10-07 +> 状态:已确认(2026-10-07) +> 范围:市场基础信息「客户管理」+ 细胞存储采集协议单 + 科研捐赠协议单 + +--- + +## 1. 背景 + +`biz_customer` 是独立客户主数据。采集协议保存时,目前只把**母亲**同步进客户表,并写到当事人表唯一的 `customer_id`。父亲、子女姓名等信息只存在当事人子表字段上,没有客户 ID。客户详情只展示本人字段,看不到同协议其他人和关联协议。 + +产品要求: + +- 母亲、父亲、子女只要有姓名即可写入客户管理(有证、无证均可)。 +- 同一张协议上的三人通过协议当事人上的三个客户 ID 关联。 +- 点开任一客户详情:上方按协议看同单其他人,下方看关联协议摘要;点协议号弹出协议只读详情。 +- 一人可对应多张协议,详情按协议分组列出。 +- 细胞存储与科研捐赠一起做。 + +--- + +## 2. 目标与非目标 + +### 2.1 目标 + +- 当事人表增加父亲、子女客户 ID;母亲继续用已有 `customer_id`。 +- 保存细胞存储 / 科研捐赠协议时,对本单三个角色走同一套 upsert 同步(仅当前协议,不全库)。 +- 上线跑一次幂等迁移,把存量当事人补建客户并回写 ID;之后保存继续同步。 +- 补身份证撞到已有客户时:合并有用字段 → 全部引用改挂 → 软删临时客户。 +- 客户详情提供「关联客户」「关联协议」;点协议号打开对应查看弹窗。 + +### 2.2 非目标 + +- 不新建「协议–客户–角色」关系表。 +- 不把客户管理改成协议列表的派生视图。 +- 不在客户详情内嵌完整协议表单,只弹现有只读查看。 +- 不自动因协议软删而删除客户。 +- 不把客户管理里的手工改名回写到协议当事人字段。 +- 本期不改中试申请患者 ↔ 客户逻辑。 + +--- + +## 3. 数据模型 + +不改 `biz_customer` 结构。扩展两张当事人表: + +### 3.1 `biz_protocol_party_customer` + +| 字段 | 说明 | +|------|------| +| `customer_id` | 已有,**母亲**客户 ID | +| `father_customer_id` | 新增 VARCHAR(64) NULL,父亲客户 ID | +| `newborn_customer_id` | 新增 VARCHAR(64) NULL,子女客户 ID | + +### 3.2 `biz_research_donation_party` + +同样增加 `father_customer_id`、`newborn_customer_id`;已有 `customer_id` 仍表示母亲。 + +不把 `customer_id` 改名为 `mother_customer_id`,避免无谓迁移;代码与 API 注释写明「母亲」。 + +SQL 放在 `docs/sql/migrations/`(例如 `2026-10-07-party-father-newborn-customer-id.sql`),并同步 SQLAlchemy 模型。 + +关联约定:同一行上的三个 ID 即同一协议家庭;查询某客户相关协议时: + +```text +customer_id = :id +OR father_customer_id = :id +OR newborn_customer_id = :id +``` + +细胞存储与科研捐赠各查一次,按租户、`del_flag=0` 过滤。 + +--- + +## 4. 同步规则(保存当前协议) + +抽公共函数(建议 `backend/app/services/party_customer_sync.py`),细胞存储与科研捐赠保存当事人时调用。**范围仅当前这一张协议的三个角色。** + +每个角色(母亲 / 父亲 / 子女)独立处理: + +1. **无姓名**:不同步客户;该角色客户 ID 置空(不删已有 `biz_customer` 记录)。 +2. **有姓名、无身份证**:新建客户,回写本单对应客户 ID。不按姓名去重(允许重名)。 +3. **有姓名、有身份证**:同租户未删除客户中按身份证查找(排除软删)。 + - 未找到:若本单该角色已有客户 ID,则**不换 ID**,把身份证等写到该客户;若无客户 ID,则新建并回写。 + - 找到且就是本单已挂的客户:更新该客户可写字段(姓名、电话等按下方合并策略)。 + - 找到且是**另一条**客户:进入第 5 节合并改挂。 + +性别默认:母亲 `1`,父亲 `0`,子女用当事人 `newborn_sex`,缺省 `2`。 + +年龄:能从身份证推算则写入,否则空。 + +协议当事人上的母亲/父亲/子女姓名电话等业务字段仍照旧写入子表,客户表只存客户主数据字段。 + +--- + +## 5. 补身份证撞库:合并后软删 + +当本单某角色「当前挂着的客户 A」与「身份证已存在的客户 B」且 `A.id != B.id`: + +1. 把 A 的有用信息合并进 B: + - `phone_no`、`age`、`sex`(仅当目标为空或未知 `2` 时用源值)、`remark`:**目标为空才补,不覆盖已有**。 + - `customer_tags`:按逗号拆开后做并集再写回 B。 + - `customer_name`:**保留 B**(身份证命中的已有客户为准)。 +2. 将细胞存储当事人、科研捐赠当事人中,三个客户 ID 字段里所有等于 A 的值,改为 B(避免 A 仍被其它协议引用)。 +3. 本单该角色 ID 写为 B。 +4. 软删 A(`del_flag=1`)。 +5. 客户管理列表默认不展示 A。 + +若身份证不撞库:不换 ID,只更新当前客户。 + +--- + +## 6. 存量迁移 + +上线脚本(Python 或 SQL + 服务函数均可,须幂等): + +- 扫描未删除的 `biz_protocol_party_customer`、`biz_research_donation_party`。 +- 对已有角色姓名、却缺少对应客户 ID 的记录,调用与保存相同的同步函数。 +- 已有正确客户 ID 的跳过。 +- 可重复执行,结果一致。 + +之后日常只在保存**当前协议**时同步,不再全库重跑。 + +--- + +## 7. 客户详情 + +### 7.1 界面 + +客户管理查看弹窗自上而下: + +1. **基本信息**:现有字段不变。 +2. **关联客户**:按该客户出现过的协议分组(协议 `create_time` 倒序)。每组展示: + - 类型(细胞存储 / 科研捐赠)、协议号、本客户角色(母亲 / 父亲 / 子女) + - 同单已填角色(含本人,标记「本人」):角色、姓名、电话、身份证;非本人点姓名打开对方客户详情 + - 未填角色不展示 +3. **关联协议**:摘要列表字段:协议号、类型、状态、日期、本客户角色。 + 日期:细胞存储用 `register_date`,科研捐赠用 `donation_date`,接口统一字段名 `registerDate`。 + **点击协议号**弹出只读详情: + - 细胞存储 → `ProtocolViewDialog` + - 科研捐赠 → `DonationViewDialog` + +无关联协议时,关联客户、关联协议区块展示空态文案。 + +### 7.2 API + +`GET /api/admin/customers/{id}/relations`(需客户查看权限,租户隔离): + +```text +{ + customer: { ...与客户详情一致 }, + relatedGroups: [ + { + bizType, // cell_storage | research_donation + protocolId, + protocolNo, // 细胞存储 protocol_no;科研捐赠 donation_no + protocolStatus, // protocol_status / donation_status + registerDate, // register_date / donation_date + myRole, // mother | father | newborn + members: [ + { role, customerId, name, phone, idCardNo } + ] + } + ], + protocols: [ + { bizType, protocolId, protocolNo, protocolStatus, registerDate, myRole } + ] +} +``` + +`relatedGroups` 与 `protocols` 同源:每张相关协议一组;`members` 含本客户及同单其他人(有客户 ID 或至少有姓名的角色)。`protocols` 便于下方摘要表直接绑定。 + +打开协议详情:前端复用现有协议/捐赠详情接口与弹窗。无对应协议查看权限时:协议号不可点或点击后提示无权限(与现有权限码一致,不新造权限)。 + +--- + +## 8. 列表与其它入口 + +- 客户列表仍是 `biz_customer` 全量未删除记录;父亲、子女会出现为独立行。 +- 协议表单「选择客户」仍选自 `biz_customer`;保存后按第 4 节回写三个 ID。 +- 客户管理手工新增/编辑/删除行为不变;删除客户不级联改协议(协议上可能留下已删 ID;详情关联客户对该 ID 显示姓名占位或「客户已删除」)。 + +--- + +## 9. 测试要点 + +- 无证三人保存 → 客户表 3 条,当事人三个 ID 均有值。 +- 后补子女身份证撞已有客户 → 字段合并、所有引用改挂、临时户软删、详情指向 B。 +- 后补身份证不撞库 → 客户 ID 不变,身份证写到原客户。 +- 同一母亲两张协议 → 详情两组关联客户 + 两条协议摘要。 +- 点协议号分别打开细胞存储 / 科研捐赠只读弹窗。 +- 仅填母亲 → 只同步母亲;关联客户组内无父/子女行。 +- 迁移脚本跑两遍结果一致。 +- 科研捐赠保存与细胞存储同一套规则。 + +--- + +## 10. 已确认决策 + +| 项 | 结论 | +|----|------| +| 关联落点 | 当事人表三个客户 ID,不新建关系表 | +| 母亲 ID | 沿用已有 `customer_id` | +| 业务范围 | 细胞存储 + 科研捐赠 | +| 无证 | 有姓名即可建客户 | +| 补证撞库 | 合并后软删临时客户,协议改挂已有客户 | +| 存量 | 上线全量脚本 + 之后仅保存当前单时同步 | +| 详情布局 | 上:按协议分组的关联客户;下:协议摘要;点协议号弹详情 | +| 中试 | 本期不动 |