Browse Source

docs(opportunity): 商机域 spec 定稿收尾(grill 补拍 20260821 回填 PRD/map/待确认清单 + OCR 存档)

- PRD V1.0 冻结态补拍:origin_dept_id、E8/E9/E10 迁移细化、切主客户一致性、待发通知表、
  子表软删复用键 delete_key 约定等 grill 拍板内容
- 实现期回填两处一致性修正(票 03 code-review Spec 轴发现):
  * §2.2 owner_name_snapshot varchar(100)→varchar(64)(对齐票 03 B1「对称 lead」,lead 即 64)
  * §2.2 补登 version 乐观锁行(§7.1/§3.4 已引用但字段表漏登)
- 方案卡模板 OCR 原始存档(1787558225260_*.png + ocr-scheme-card/doc_0.md)
master
luoweijian 2 weeks ago
parent
commit
9a9d6a3270
  1. BIN
      .scratch/opportunity-module/1787558225260_a92d0f4eea8a41d7a958ff78622c232c.png
  2. 2
      .scratch/opportunity-module/map.md
  3. 6
      .scratch/opportunity-module/ocr-scheme-card/doc_0.md
  4. 1
      .scratch/opportunity-module/产品待确认清单.md
  5. 115
      .scratch/opportunity-module/商机业务-PRD.md

BIN
.scratch/opportunity-module/1787558225260_a92d0f4eea8a41d7a958ff78622c232c.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 47 KiB

2
.scratch/opportunity-module/map.md

@ -43,7 +43,7 @@
- **商机主表 Amendment 1:新增 `project_amount`、【关联线索】入口扩展(产品口径变更)** — 主表新增 `project_amount`(decimal(14,2),选填,项目总包金额),推翻票 03 原「不建预计金额」前一半;预计成交时间仍不建。**【关联线索】复用 `source_lead_id`**,商机侧新增入口下、商机来源=「线索转入」时必填,候选 = 线索状态已领取/跟进中(与线索侧转商机准入对称);唯一约束靠已有 `UNIQUE(source_lead_id)`(票 02),一条线索只能关联一条商机。方案预算 = 方案卡子表字段(票 07),列表 join 展示,主表不冗余。拆出 **票 12(商机侧新增入口关联线索校验)**。CONTEXT.md 【来源线索】术语扩写 + 新增【项目金额】/【方案预算】两术语。详见 `issues/03` Amendment 1 + `issues/12`
- **商机公海与提醒规则已定稿(票 05 resolved,原型 A7-3-2-5 三页正文已拉取核实)** — ⚠ **推翻初稿**:初稿照搬线索 `lead_pool` 池实体做对称推定,方向错了。原型确认商机公海规则是**版本化规则配置**(`opportunity_pool_rule`:`rule_code`+`version_no`+草稿/发布中/已停用,与票 06/07 同族),**非**「一部门一池」实体、**无**省/市/区地理维度、**无**领取/持有上限。适用范围=所有部门通用/对应部门专用+默认规则兜底;五配置项:`allow_manual_pool`/`auto_recycle_enabled`+`recycle_days`/`allow_free_claim`/`recycle_remind_enabled`+`remind_days`(<recycle_days)。回收锚点=**最近有效跟进时间**(有效跟进=跟进/附件/字段编辑),非 claim_time。**商机公海=`opp_status=1 owner ` 的逻辑视图**,无池实体表,`owner_dept_id`+匹配规则适用范围定可见性。**回填票 03**: `pool_id`新增 `last_valid_follow_time`撤销 `recycle_deadline`;**回填票 04**:`executeRecycle` 触发改 `now−last_valid_follow_time≥recycle_days`抛公海无回池目标( owner+opp_status=1),抛公海入口受 `allow_manual_pool` 控制规则匹配以**最低层级规则**为准产品拍板),发布约束保证一个部门只被一条发布中规则覆盖故命中唯一;**暂缓期间冻结回收计时**(产品拍板,`executeRecycle` 不扫暂缓中取消暂缓后按剩余天数续算)。存档 `research-a7325-pool-remind-rules.md`详见 `issues/05`
- **商机阶段模板已定稿(票 06 resolved,原型 A7-3-2-1 三页正文已拉取核实,存档 `research-a7321-stage-template.md`)** — 轴2 业务阶段配置。**收敛「版本化配置」公共约定 V-CONFIG**(票 05/06/07 共享:`code`+`version_no`+草稿/发布中/已停用;同 code 仅一草稿一发布中;发布使原发布中转停用;默认唯一;编辑发布中/停用→生新草稿;复制→新 code 新主体;仅草稿可删;列表按版本维度一版一行;**绑定实体锁具体版本、发新版不迁移**)。表:`crm-rule` `opportunity_stage_template`(头,`template_code`/`version_no`/`status`/`apply_scope`(所有商机/指定部门 两档单选,本期不做指定业务类型)/`is_default`) + `_dept`(适用对象) + `opportunity_stage_node`(有序节点:`seq_no`/`stage_dict_code` 引用「商机阶段」字典/`custom_node_name`/`work_goal`/`is_fixed`);`crm-opportunity` `opportunity_stage_history`(推进快照)。**阶段推进(产品拍板)**:除「已转项目」外其余阶段可**自由切换(连跳+回退)**,每次写 history;「已转项目」=固定末节点(`is_fixed`,不可删/不可手动点),仅由**商机下任一方案卡转项目创建成功**触发,同时驱动轴1 status=5(A5 联动)。概览页(A3-1-1-1-1)推进指引区=三态进度展示,无独立推进按钮(原型标"后续迭代")。**回填票 03**:`stage_template_id`=版本行 id 锁版本,`stage_template_version` int→varchar 冗余版本号,`current_stage_id`→节点 id 新建落首节点。**传出**:票 05 版本状态机=V-CONFIG(引用不重述);票 07 方案卡模板复用 V-CONFIG;「商机阶段」字典分组→字典清单。✅ apply_scope 两档单选(产品拍板:本期仅所有商机/指定部门,不做指定业务类型、不交叉;原型三档→两档,删 `_biztype` 子表)。详见 `issues/06`
- **方案卡已定稿(票 07 resolved,原型 A7-3-2-2 模板三页 + A3-1-1-1-3 运行时两页均拉取核实)** — 两层:**方案卡模板**(`crm-rule` `opportunity_scheme_card_template`,V-CONFIG 第三实例)+ **运行时**(`crm-opportunity` `opportunity_scheme_card`)。模板 = 版本化**字段集配置**(选字段/顺序/显示/必填),字段来自**系统字段库非自由造**(删字段不删系统字段定义);字段类型 = 关联对象/单选下拉/自动回填/是否/附件上传/金额/单行文本/多行文本。**适用范围两档**(所有部门通用/对应部门专用,同票 05、异于票 06 三档——V-CONFIG 抽版本状态机不抽适用范围档数)。运行时:**一商机可多张**,每张必关联商机下一个关联客户(无孤立卡),字段值由绑定模板版本驱动(结构化字段提主表 + 动态字段进 `_value` 表),单卡状态 `草稿1/已提交2/已推送3`(未创建=0卡展示态)。**一客户一卡** `UNIQUE(opportunity_id,customer_id)`。**方预算 `scheme_budget` 落方案卡主表**,商机列表「方案预算/方案卡状态」列 = 取**主要意向客户**(票 13)那张卡(**不做主卡 `is_primary`**)。**联动**:方案卡转项目创建成功 = 票 06「已转项目」联动触发源。**边界**:推送方案(A7-3-2-3)+查重(A7-3-2-4) 不在本票。存档 `research-a7322-scheme-card-template.md` / `research-a31113-scheme-card-runtime.md`。详见 `issues/07`。✅ P0-3 字段定义表=自建 `opportunity_scheme_field_def`(下拉选项集引用 crm-dict)。✅ P0-4 状态枚举 `草稿1/已提交2/已推送3`+未创建为0卡展示态;**不做主卡**,列表方案预算/状态列取主要意向客户卡(票13);一客户一卡。⚠ 遗留:提交/推送后锁字段集(不阻塞)。
- **方案卡已定稿(票 07 resolved,原型 A7-3-2-2 模板三页 + A3-1-1-1-3 运行时两页均拉取核实)** — 两层:**方案卡模板**(`crm-rule` `opportunity_scheme_card_template`,V-CONFIG 第三实例)+ **运行时**(`crm-opportunity` `opportunity_scheme_card`)。模板 = 版本化**字段集配置**(选字段/顺序/显示/必填),字段来自**系统字段库非自由造**(删字段不删系统字段定义);字段类型 = 关联对象/单选下拉/自动回填/是否/附件上传/金额/单行文本/多行文本。**适用范围两档**(所有部门通用/对应部门专用,同票 05、异于票 06 三档——V-CONFIG 抽版本状态机不抽适用范围档数)。运行时:**一商机可多张**,每张必关联商机下一个关联客户(无孤立卡),字段值由绑定模板版本驱动(结构化字段提主表 + 动态字段进 `_value` 表),单卡状态 `草稿1/已提交2/已推送3`(未创建=0卡展示态)。**一客户一卡** `UNIQUE(opportunity_id,customer_id)`。**方预算 `scheme_budget` 落方案卡主表**,商机列表「方案预算/方案卡状态」列 = 取**主要意向客户**(票 13)那张卡(**不做主卡 `is_primary`**)。**联动**:方案卡转项目创建成功 = 票 06「已转项目」联动触发源(**推送链路本期 spin-out、不可达**;本期 E8 仅 A5 回调特殊路径可达)。**边界**:推送方案(A7-3-2-3)+查重(A7-3-2-4) 不在本票。存档 `research-a7322-scheme-card-template.md` / `research-a31113-scheme-card-runtime.md`。详见 `issues/07`。✅ P0-3 字段定义表=自建 `opportunity_scheme_field_def`(下拉选项集引用 crm-dict)。✅ P0-4 状态枚举 `草稿1/已提交2/已推送3`+未创建为0卡展示态;**不做主卡**,列表方案预算/状态列取主要意向客户卡(票13);一客户一卡。⚠ 遗留:提交/推送后锁字段集(不阻塞)。
- **现场勘察已定稿(票 08 resolved,原型 A3-1-1-1-4 + A3-1-1-2-10 均拉取核实)**`crm-opportunity` `opportunity_site_survey`。**一商机可多条**(保留历史不覆盖),**无状态机**(异于方案卡),**不绑阶段**(自由记录)。字段:申请方式(字典)/勘察次数(字典手选四档:第一/二/三次+三次以上)/**现场勘察日期 `survey_date`(必填)**/申请单号(可空)/**主要工程师**(`engineer_user_id` 引用 crm-auth SysUser,按姓名/部门/职位查询选人、可跨部门)/勘察补充说明(长文本)。附件**复用票 09 商机附件子表**(`biz_type=SITE_SURVEY`+`biz_id`定位具体勘察),不独立建表。权限同阶段/方案卡(@DataScope+团队白名单无 owner guard)。**传出**:票 09 附件 `biz_type` 枚举加 SITE_SURVEY;crm-dict 加「申请方式」「勘察次数」分组。存档 `research-a31114-site-survey.md`。详见 `issues/08`。✅ P1-2 次数=手选四档(字典);P1-3 勘察补充说明=选填(原型无*);现场勘察日期=必填。
- **商机子表族已定稿(票 09 resolved,原型 A3-1-1-1-5/6/7/9 + 写跟进 A3-1-1-2-2 均拉取核实)** — 四张子表:**P2 跟进记录** `opportunity_follow`(**无草稿态**异于线索反馈;跟进内容/时间/方式/关联客户/结果标签/下次跟进;**保存→刷新 `last_valid_follow_time`** 回填票 05/03;与阶段不联动);**P4 附件** `opportunity_attachment`(**一表 `biz_type`+`biz_id` 定位**,服务通用/跟进/勘察/方案卡——票 07/08 附件契约落点;资料类型走 crm-dict「商机资料类型」原型明要做字典;文件本体 crm-file);**P5 工作计划** `opportunity_work_plan`(结构 only:内容/截止/plan_status 未完成0/已完成1,逾期实时派生不存态;**执行状态精确枚举归 A1X 独立模块,本期挂起,P1-4 拍板**);**P6 操作日志** `opportunity_oplog`——**口径纠偏:字段级变更审计**(实体/字段/旧值/新值/来源系统自动 vs 人工),**非线索 `lead_history` 的动作枚举模型**;状态/阶段迁移另有专用快照表并存不重。**传出**:crm-dict 加跟进方式/结果标签/商机资料类型三分组。存档 `research-a3111-followup-log.md` / `research-a3111-attachment-workplan.md`。详见 `issues/09`。✅ P1-4 工作计划挂起归 A1X(本期只留子表结构);✅ P1-5 操作日志=默认全记+短黑名单排除(非白名单)。
- **四视图查询口径已定稿(票 10 resolved,原型 A3-1-1/A3-2-1/A3-3-1/A3-0-1 均拉取核实)** — 数据范围:**个人** `owner=当前用户` / **管理**`@DataScope`(复用 crm-auth) / **公海** `opp_status=1 且 owner 空` 逻辑视图(票 05) / **最近访问** 打开过详情按时间倒序去重。共用列 + 计算列不冗余存(节点停留=今−进节点日、最近跟进=最新 follow、预计金额=`project_amount`)。差异:个人有+新增商机/当前负责人隐;管理有分配/催办/关闭/批量/领导释放;公海有**入池前工作节点/进入公海原因/领取/关注**。详情三皮肤(个人/管理/公海)=同聚合按视角展不同操作区非三套。**传出新事实**:回填票 04/05 新增 `pool_reason`(销售主动释放/特殊原因暂停/领导释放/超期回收),「领导释放」=管理侧抢公海;入池前节点快照。**✅ 口径冲突已解(产品二次拍板)**:「预计成交时间」列从商机列表删除、不上主表,仅方案卡内部字段(票 07)。存档 `research-a3xx-four-views.md`。详见 `issues/10`。✅ P0-4 方案卡状态/方案预算列取主要意向客户卡(票13)。⚠ 待核:最近访问去重上限 / 领导视角 DataScope 映射。

6
.scratch/opportunity-module/ocr-scheme-card/doc_0.md

@ -0,0 +1,6 @@
## 阶段节点配置
预览填写页效果>
<table border=1 style='margin: auto; word-wrap: break-word;'><tr><td style='text-align: center; word-wrap: break-word;'>序号</td><td style='text-align: center; word-wrap: break-word;'>字段名称</td><td style='text-align: center; word-wrap: break-word;'>字段类型</td><td style='text-align: center; word-wrap: break-word;'>显示</td><td style='text-align: center; word-wrap: break-word;'>必填</td></tr><tr><td style='text-align: center; word-wrap: break-word;'>1</td><td style='text-align: center; word-wrap: break-word;'>客户名称</td><td style='text-align: center; word-wrap: break-word;'>关联对象</td><td style='text-align: center; word-wrap: break-word;'></td><td style='text-align: center; word-wrap: break-word;'></td></tr><tr><td style='text-align: center; word-wrap: break-word;'>2</td><td style='text-align: center; word-wrap: break-word;'>客户类型</td><td style='text-align: center; word-wrap: break-word;'>单选下拉</td><td style='text-align: center; word-wrap: break-word;'></td><td style='text-align: center; word-wrap: break-word;'></td></tr><tr><td style='text-align: center; word-wrap: break-word;'>3</td><td style='text-align: center; word-wrap: break-word;'>客户联系人</td><td style='text-align: center; word-wrap: break-word;'>关联对象</td><td style='text-align: center; word-wrap: break-word;'></td><td style='text-align: center; word-wrap: break-word;'></td></tr><tr><td style='text-align: center; word-wrap: break-word;'>4</td><td style='text-align: center; word-wrap: break-word;'>客户联系电话</td><td style='text-align: center; word-wrap: break-word;'>自动回填</td><td style='text-align: center; word-wrap: break-word;'></td><td style='text-align: center; word-wrap: break-word;'></td></tr><tr><td style='text-align: center; word-wrap: break-word;'>5</td><td style='text-align: center; word-wrap: break-word;'>项目阶段</td><td style='text-align: center; word-wrap: break-word;'>单选下拉</td><td style='text-align: center; word-wrap: break-word;'></td><td style='text-align: center; word-wrap: break-word;'></td></tr><tr><td style='text-align: center; word-wrap: break-word;'>6</td><td style='text-align: center; word-wrap: break-word;'>招标形式</td><td style='text-align: center; word-wrap: break-word;'>单选下拉</td><td style='text-align: center; word-wrap: break-word;'></td><td style='text-align: center; word-wrap: break-word;'></td></tr><tr><td style='text-align: center; word-wrap: break-word;'>7</td><td style='text-align: center; word-wrap: break-word;'>是否有看现场</td><td style='text-align: center; word-wrap: break-word;'>是/否</td><td style='text-align: center; word-wrap: break-word;'></td><td style='text-align: center; word-wrap: break-word;'></td></tr><tr><td style='text-align: center; word-wrap: break-word;'>8</td><td style='text-align: center; word-wrap: break-word;'>现场勘察资料</td><td style='text-align: center; word-wrap: break-word;'>附件上传</td><td style='text-align: center; word-wrap: break-word;'></td><td style='text-align: center; word-wrap: break-word;'></td></tr><tr><td style='text-align: center; word-wrap: break-word;'>9</td><td style='text-align: center; word-wrap: break-word;'>方案预算金额</td><td style='text-align: center; word-wrap: break-word;'>金额</td><td style='text-align: center; word-wrap: break-word;'></td><td style='text-align: center; word-wrap: break-word;'></td></tr><tr><td style='text-align: center; word-wrap: break-word;'>10</td><td style='text-align: center; word-wrap: break-word;'>设计配置</td><td style='text-align: center; word-wrap: break-word;'>单选下拉</td><td style='text-align: center; word-wrap: break-word;'></td><td style='text-align: center; word-wrap: break-word;'></td></tr><tr><td style='text-align: center; word-wrap: break-word;'>11</td><td style='text-align: center; word-wrap: break-word;'>客户需求描述</td><td style='text-align: center; word-wrap: break-word;'>多行文本</td><td style='text-align: center; word-wrap: break-word;'></td><td style='text-align: center; word-wrap: break-word;'></td></tr></table>

1
.scratch/opportunity-module/产品待确认清单.md

@ -154,6 +154,7 @@
### 票 11 — CreateOpportunityCmd 新增 customerId + 线索侧意向客户下拉
- **性质**:改代码实现票(决策已在票 02 钉死)。
- **契约现状(grill 补拍 20260821)**:代码中 `CreateOpportunityCmd` **尚未含 `customerId`**,且字段命名(`intendedCustomer`/`regionCode`/`industryCode`)与主表 §2.1 不对齐。PRD 描述目标契约,本票 = 差量:① 新增 `customerId`;② `regionCode`→`province_code`/`city_code`;③ `intendedCustomer`→`customerId`+客户名快照。
- **阻塞**:**依赖 A4 客户模块(未建)**——「下拉选客户」需线索侧能查客户列表,A4 到达前无法完整落地。
### 票 12 — 商机侧新增入口:关联线索校验

115
.scratch/opportunity-module/商机业务-PRD.md

@ -40,7 +40,7 @@
| 角色 | 定义 | 字段 |
|---|---|---|
| **负责人(领取人)** | 当前持有该商机的销售,归属 + 默认可见性双重含义 | `owner_user_id` |
| **团队成员** | 负责人拉入协作的人员,带项目角色(商机负责人 / 方案工程师 / 招标专员 / 现场工程师等)与权限(读写 / 只读 / 可写跟进 / 可推进节点等);是行级授权白名单,绕开 DataScope 部门天花板——**生效边界 = 详情/子表读 + 个人工作区视图;管理/公海工作区严格按 DataScope 不混白名单**(grill 补拍 20260821) | `opportunity_team`(关联表) |
| **团队成员** | 负责人拉入协作的人员,带**项目角色(纯回显标签:商机负责人 / 方案工程师 / 招标专员 / 现场工程师等,不带任何读写权限,grill 拍板 20260821**与权限(读写 / 只读 / 可写跟进 / 可推进节点等,**权限与角色无关、单独配**);是行级授权白名单,绕开 DataScope 部门天花板——**生效边界 = 详情/子表读 + 个人工作区视图;管理/公海工作区严格按 DataScope 不混白名单**(grill 补拍 20260821) | `opportunity_team`(关联表) |
| **创建人** | 全生命周期不变 | `creator_user_id` |
- 抛公海只清空负责人,**不动团队成员**(产品明确,票 01)。
@ -70,8 +70,8 @@ crm-opportunity → crm-rule(阶段模板 / 方案卡模板 / 公海规则
| 归属流转 | 领取(公海)/ 分配(管理)/ 抛公海(个人主动)/ 领导释放(管理强制)/ 暂缓 / 取消暂缓 / 移交 |
| 跟进 | 写跟进 / 删除跟进 |
| 方案卡 | 新增 / 编辑 / 提交 / 推送 |
| 团队成员 | 添加 / 调整角色与权限 / 移除(成员行上的读写/只读/可写跟进/可推进节点 = **行级角色配置,不走全局权限点**) |
| 关联客户 | 新增 / 编辑 / 删除 |
| 团队成员 | 添加 / 调整角色与权限 / 移除(成员行上的读写/只读/可写跟进/可推进节点 = **行级权限配置,不走全局权限点**;**项目角色纯回显标签、不带权限,与权限列各自独立配置**,grill 拍板 20260821) |
| 关联客户 | 新增 / 编辑(改客户角色、切主要意向标记)——**无删除动作**(原型/票 13 无删除,grill 纠偏 20260821) |
| 现场勘察 | 新增 / 编辑 / 删除 |
| 附件 | 上传 / 删除 |
| 视图 | 关注 / 取关;自定义视图新增/编辑/删除;批量领取/分配/关注/领导释放(同单条动作权限点,批量不加新点) |
@ -111,8 +111,9 @@ crm-opportunity → crm-rule(阶段模板 / 方案卡模板 / 公海规则
|---|---|---|
| `opp_status` | tinyint | 归属流转状态(轴 1),见 §3 |
| `owner_user_id` 负责人 | bigint | 抛公海后清空 |
| `owner_name_snapshot` 负责人姓名快照 | varchar(100) | 调岗不自动更新 |
| `owner_name_snapshot` 负责人姓名快照 | varchar(64) | 调岗不自动更新(长度对齐票 03 B1「对称 lead」;此处原误记 varchar(100),回填 20260824) |
| `owner_dept_id` 负责人部门快照 | bigint | 公海可见范围锚点;调岗不变 |
| `origin_dept_id` 原始血缘部门 | bigint | **创建时写入 = 首个负责人部门,此后永不变**(移交/抛公海/回收/领取/分配均不动;grill 拍板 20260821);仅审计/血缘追溯用(列表可选筛选/oplog 展示),**不参与公海规则匹配、不参与 DataScope 可见性**(匹配/可见性仍锚 `owner_dept_id`) |
| `creator_user_id` 创建人 | bigint | 全生命周期不变 |
| `claim_time` 领取时间 | datetime | 领取/分配/两入口创建时=now(「进入名下时间」,P1-6 拍板) |
| `last_valid_follow_time` 最近有效跟进时间 | datetime | 自动回收计时锚点(票 05);领取/分配**重置**=now;两入口创建以进入名下时间(=创建时刻)为起点;有效跟进时刷新(§5.3 定义,配置化 P1-8) |
@ -123,6 +124,11 @@ crm-opportunity → crm-rule(阶段模板 / 方案卡模板 / 公海规则
| `pool_reason` 进入公海原因 | varchar(32) | 抛公海/回收时写(见 §3.2);公海列表筛选 |
| `primary_customer_id` 主要意向客户 id | bigint | 冗余快查,= 关联客户表 `is_primary_intended=1` 那条 |
| `primary_customer_name_snapshot` 主客户名快照 | varchar(200) | 列表/详情顶部免 join |
| `pause_time` 暂缓开始时刻 | datetime | E4 暂缓写=now,E5 取消暂缓清空;取消暂缓时算 `now−pause_time` 顺推 `last_valid_follow_time`(冲突8) |
| `pause_reason` 暂缓原因 | varchar(32) | 字典码;E4 写、E5 清空 |
| `pause_expected_restart_date` 预计重启日期 | date | 必填≥今天;E4 写、E5 清空 |
| `pause_remark` 暂缓补充说明 | varchar(500) | E4 写、E5 清空 |
| `version` 乐观锁版本号 | int not null default 0 | 商机级写并发防护:状态迁移 CAS(§3.4,票 04)与切主客户(§7.1,票 13)复用;票 03 落列,生效接线归后续票(回填 20260824,补登 §7.1/§3.4 已引用的列) |
继承 `BaseEntity`(id / create_time / update_time / creator / updater / 逻辑删除)。
@ -170,12 +176,12 @@ crm-opportunity → crm-rule(阶段模板 / 方案卡模板 / 公海规则
| E1 | 领取 | `ClaimCmd` | 1→2 | 设 owner + 部门/姓名快照 + `claim_time`;刷新 `last_valid_follow_time`=now | A3-3-1-1(公海领取) |
| E2 | 分配 | `AssignToUserCmd` | 1→2 | 设 owner=指派对象 + 快照 + `claim_time`;重置 `last_valid_follow_time`=now(P1-6 拍板) | A3-2-1(管理页分配) |
| E3 | 抛公海 | `ReleaseToPoolCmd` | 2→1 | 清 `owner_user_id` + `owner_name_snapshot` + `claim_time`;**`owner_dept_id` 保留**(公海可见范围锚点,票 05);记 `pool_reason`(销售主动释放/特殊原因暂停/领导释放);保留 `current_stage_id` 供公海展示;**不动团队成员**;入口受规则 `allow_manual_pool` 开关控制 | A3-1-1-2-7 |
| E4 | 暂缓 | `PauseCmd` | 2→3 | 保留 owner + 团队;记暂缓原因(字典) + 预计重启日期(必填≥今天) + 补充说明 | A3-1-1-2-6 |
| E5 | 取消暂缓 | `ResumeCmd` | 3→2 | 保留 owner;回暂缓前阶段(阶段字段未动);清暂缓附加字段 | A3-1-1-2-6(取消按钮) |
| E4 | 暂缓 | `PauseCmd` | 2→3 | 保留 owner + 团队;`pause_time`=now + `pause_reason`(字典) + `pause_expected_restart_date`(必填≥今天) + `pause_remark` | A3-1-1-2-6 |
| E5 | 取消暂缓 | `ResumeCmd` | 3→2 | 保留 owner;回暂缓前阶段(阶段字段未动);清暂缓附加字段(`pause_time`/`pause_reason`/`pause_expected_restart_date`/`pause_remark`);**回收锚点顺推(grill 拍板 20260821)**:`last_valid_follow_time += now−pause_time`(把暂缓时长从未跟进时长中扣除,等价「暂缓期间冻结回收」);此后回收判定回归无状态实时比较 | A3-1-1-2-6(取消按钮) |
| E6 | 关闭 | `CloseCmd` | 2,3→4 | 保留 owner(只读锁定);记关闭原因(字典) + 补充说明(选「其他」时必填) + 确认勾选 | A3-1-1-2-9 |
| E7 | 重启 | `ReopenCmd` | 4→2 | 保留 owner;记重开原因(必填);统一回「推进中」 | A3-1-1(重新开启入口) |
| E8 | 转项目 | `ConvertToProjectCmd` | 2→5 | **触发源 = ① 标准路径:推送方案卡成功(系统自动创建项目);② 特殊路径:项目侧建项目关联商机后回调**(grill 补拍 20260821),无独立「转项目」动作;保留 owner(锁死);对接 A5 项目模块(本模块只占端口);列表「转项目」按钮 = 推送流程快捷入口 | A3-1-1-2-x(转项目) |
| E9 | 移交 | `HandoverCmd` | 2,3→自环 | 换 owner = 移交对象 + 姓名/部门快照同步刷新(`owner_dept_id` 换为移交对象部门,公海规则匹配锚点跟人走,grill 补拍 20260821);`claim_time` 不重置;入口在团队成员页 | A3-1-1-1-8(团队成员 Tab) |
| E8 | 转项目 | `ConvertToProjectCmd` | 2→5 | **本期可达触发源 = 仅特殊路径:项目侧建项目关联商机后回调(A5 调 `OpportunityConvertPort`)**;① 标准路径(推送方案卡→系统建项目)**本期不可达、随推送票 spin-out**(grill 拍板 20260821);无独立「转项目」动作;保留 owner(锁死);列表「转项目」按钮 = 推送流程快捷入口(推送票落地后启用) | A3-1-1-2-x(转项目) |
| E9 | 移交 | `HandoverCmd` | 2,3→自环 | 换 owner = 移交对象 + 姓名/部门快照同步刷新(`owner_dept_id` 换为移交对象部门,公海规则匹配锚点跟人走,grill 补拍 20260821);`claim_time` 不重置;**同步维护 team 表(grill 拍板 20260821)**:① 新负责人 upsert 一条 team 行、角色标签=「商机负责人」(已是成员则改标签);② 原负责人 team 行角色标签从「商机负责人」改为普通协作(保留其权限列不动——角色不带权限,原负责人是否还能写取决于其 team 行原本权限),**留在团队继续协作、落入「我参与的」**;入口在团队成员页 | A3-1-1-1-8(团队成员 Tab) |
| E10 | 超时回收 | `executeRecycle` | 2→1(批量) | 清 `owner_user_id` + `owner_name_snapshot` + `claim_time`;`owner_dept_id` 保留定公海可见范围;`pool_reason`=超期回收;保留 `current_stage_id` 快照;暂缓中(3)**不参与回收**;实际执行的规则版本快照写 `opportunity_oplog`(§5.4) | **crm-opportunity 自建 `@Scheduled` 单 Job**(先提醒后回收串行,对齐线索域 ADR-0019 竞态规避;cron 配置占位符默认每日凌晨,grill 补拍 20260821) |
> **「领导释放」**(E3 变体)= 管理侧强制抛公海,操作人 ≠ owner,`pool_reason`=领导释放(原型 A3-2-1 管理页)。
@ -245,7 +251,7 @@ crm-opportunity → crm-rule(阶段模板 / 方案卡模板 / 公海规则
### 4.4 「已转项目」固定节点 × 轴 1 联动
- 「已转项目」是每个模板的**固定节点**(`is_fixed=1`,字典值固定 `OPP_STAGE_05`),配模板时自带、**不可删**(原型校验「除固定节点外至少创建一个阶段节点」),且**不可拖到非末位**(语义要求,落地校验)。
- **推送方案卡成功(系统自动创建项目)→ 触发轴 1 转项目终态(E8)+ 轴 2 落到固定节点**(标准路径,grill 拍 20260821);无独立「转项目」动作,列表「转项目」按钮 = 推送流程快捷入口。
- **推送方案卡成功(系统自动创建项目)→ 触发轴 1 转项目终态(E8)+ 轴 2 落到固定节点**(标准路径,**本期不可达、随推送票 spin-out**,grill 拍 20260821);本期 E8 仅由特殊路径(A5 回调)触发。无独立「转项目」动作,列表「转项目」按钮 = 推送流程快捷入口(推送票落地后启用)
### 4.5 绑定与不迁移(V-CONFIG 12)
@ -291,8 +297,8 @@ crm-opportunity → crm-rule(阶段模板 / 方案卡模板 / 公海规则
| `allow_free_claim=1` | 适用范围内且具备公海权限的销售可自领;领取成功 owner=领取人,记录领取时间/人/规则版本(E1) |
| `allow_free_claim=0` | 无自领入口,只能管理员分配(E2) |
| `auto_recycle_enabled=1` | 回收锚点 = `last_valid_follow_time`;连续未有效跟进天数 = now − 锚点,≥ `recycle_days` 触发 E10 自动回收 |
| 暂缓中(3) | **不参与回收**,回收计时暂停;取消暂缓回推进中后按剩余天数续算 |
| `recycle_remind_enabled=1` | 提醒时间 = 预计回收时间 − `remind_days`;**由回收 Job 同一 `@Scheduled` 内先扫提醒再扫回收**(提醒先于回收,对齐线索域 ADR-0019;grill 补拍 20260821) |
| 暂缓中(3) | **不参与回收**(回收 Job 跳过暂缓中);取消暂缓时把 `last_valid_follow_time` 顺推暂缓时长(E5)→ 回收判定为无状态实时比较 `now − last_valid_follow_time ≥ 当前匹配规则.recycle_days`,**不存「剩余天数」伪状态**;移交换部门后续算自然用新部门当前规则(grill 拍板 20260821) |
| `recycle_remind_enabled=1` | 提醒时间 = 预计回收时间 − `remind_days`;**由回收 Job 同一 `@Scheduled` 内先扫提醒再扫回收**(提醒先于回收,对齐线索域 ADR-0019;grill 补拍 20260821)。**提醒只落待发记录、不直接发送**(grill 拍板 20260821):Job 扫到「预计回收时间 − remind_days ≤ now 且尚无待发记录」的商机 → upsert 一条 `opportunity_pending_notice`(`notice_type=RECYCLE_REMIND`,见 §7.10);实际发送走平台通知能力(spin-out 留白,与暂缓到期提醒/催办统一挂起) |
- **有效跟进**(刷新锚点,P1-8 拍板)= **商机状态为推进中(2) 且动作 = 写跟进**(本期唯一动作;上附件/编辑字段/方案卡/勘察等不计,产品二次修订 20260821)。动作类型集合是**配置项**:落 `crm-rule` 第四族「有效跟进规则」`opportunity_follow_rule`(V-CONFIG 同族,动作类型多选子表,本期默认仅「写跟进」,以后经配置新增),回收 Job 按商机 `owner_dept_id` 实时匹配当前发布中规则拿动作集合(与公海规则同匹配引擎)。领取/分配重置锚点=now;两入口创建锚点=进入名下时间(P1-6)。
@ -340,7 +346,9 @@ crm-opportunity → crm-rule(阶段模板 / 方案卡模板 / 公海规则
> **公海商机在管理视图的可见性**(grill 补拍 20260821):owner 空但 `owner_dept_id` 保留作 DataScope 部门锚点,按查看者档位自然判定——全部档可见;部门档看 `owner_dept_id` 是否在其可见部门集合;仅本人档永不可见(owner 空 ≠ 本人)。故 E2 分配入口(管理页)对全部/部门档管理员数据可达,无需去公海页。
> **详情聚合接口可见性**(grill 补拍 20260821)= **DataScope 可见 OR 团队成员白名单命中**——保证「我参与的」视图点进详情链路闭环;写操作再按成员自身权限点校验。
> **详情聚合接口可见性**(grill 补拍 20260821)= **DataScope 可见 OR 团队成员白名单命中(仅存活行 `deleted=0`)**——保证「我参与的」视图点进详情链路闭环;写操作再按成员自身权限点校验。
> **视图非权限旁路(通则,grill 拍板 20260821)**:任何视图(最近访问/我参与的/我关注的/自定义检索)**只决定可见入口,不豁免任何动作校验**。例:被回收的商机仍留在原负责人「最近访问」里(团队白名单命中可进详情),但「领取」按钮仍走 E1 完整校验(`allow_free_claim` + 适用部门 + 权限点 + 状态 CAS),与从公海页领取同一代码路径。
> 视图不是状态切片:同一商机可同时出现在多视图(如自己负责 + 最近访问过)。
@ -350,7 +358,7 @@ crm-opportunity → crm-rule(阶段模板 / 方案卡模板 / 公海规则
|---|---|---|
| `recent` | 最近访问 | 当前用户打开过详情的商机,访问时间倒序、按商机 id 去重留最新,上限可配置(本期默认 50);「访问」= 调用详情聚合接口,任意皮肤(个人/管理/公海)都算(grill 补拍 20260821);**落库 `opportunity_view_log`**(§7.9,访问即 upsert) |
| `mine` | 我负责的销售机会 | `owner_user_id = 当前用户`;**不按状态切片**(已关闭/已转项目仍在列——原型样例首行即「已转项目」商机;需只看活跃的用自定义视图按状态过滤,grill 补拍 20260821) |
| `participated` | 我参与的销售机会 | **团队成员且非负责人**:`EXISTS(opportunity_team WHERE opportunity_id=? AND user_id=当前用户) AND owner_user_id <> 当前用户`;与 `mine` 互斥 |
| `participated` | 我参与的销售机会 | **团队成员且非负责人**:`EXISTS(opportunity_team WHERE opportunity_id=? AND user_id=当前用户 AND deleted=0) AND owner_user_id <> 当前用户`;与 `mine` 互斥。**成员身份判定一律带存活过滤 `deleted=0`**(grill 拍板 20260821):移除团队成员=软删 team 行(deleted 0→1),被移除后立即从本视图与详情白名单消失,仅 oplog/成员历史留痕;移交降级的旧负责人是存活行(只改角色)仍命中。 |
| `followed` | 我关注的销售机会 | 当前用户关注表 `opportunity_focus` 命中(§7.8) |
### 6.3 销售机会工作区 — 自定义视图(「自定义检索」Tab)
@ -437,6 +445,18 @@ crm-opportunity → crm-rule(阶段模板 / 方案卡模板 / 公海规则
## §7 详情子表族
> **子表删除与唯一索引通用约定(grill 拍板 20260821,对齐 `crm-rule` `LeadPool` 既有实践)**:带业务字段/需审计的子表走**软删除 + 唯一索引复用键 `delete_key`**(存活 `delete_key=0`,逻辑删除时 `delete_key=id`;唯一索引挂 `delete_key` 而非裸列,保证同键可重新添加)。纯行为/收藏表走物理硬删。
>
> | 表 | 删除方式 | 唯一索引 |
> |---|---|---|
> | `opportunity_customer` 关联客户 | 无独立删除,仅随商机级联逻辑删 | `UNIQUE(opportunity_id, customer_id)` |
> | `opportunity_scheme_card` 方案卡 | 软删 + `delete_key` | `UNIQUE(opportunity_id, customer_id, delete_key)` |
> | `opportunity_team` 团队成员 | 软删 + `delete_key`(移交/移除保留历史) | `UNIQUE(opportunity_id, user_id, delete_key)` |
> | `opportunity_focus` 关注 | 物理硬删 | `UNIQUE(opportunity_id, user_id)` |
> | `opportunity_view_log` 最近访问 | 物理硬删/裁剪 | `UNIQUE(user_id, opportunity_id)` |
>
> **硬删表的排查日志(grill 拍板 20260821,选项 C)**:`focus`/`view_log` 为纯行为/收藏流水,产品日志页无关注/浏览类目→**不入 oplog、不软删**;关注/取关/访问裁剪动作发生时**往应用日志(logback)打一条 INFO**(谁/何时/哪条商机/什么动作)供线上排查(时序完整、零表膨胀)——排查靠应用日志而非 DB 软删墓碎行。
### 7.1 关联客户 `opportunity_customer`
**出处**:票 13 `opportunity-customer-relation`,原型 A3-1-1-2-4。
@ -449,7 +469,9 @@ crm-opportunity → crm-rule(阶段模板 / 方案卡模板 / 公海规则
| `is_primary_intended` | tinyint 0/1 | ✅ | 是否主要意向客户,一商机唯一 =1 |
- 线索转商机填的「意向客户」→ 落 `is_primary_intended=1`;商机侧添加普通关联客户 → `is_primary_intended=0`
- **无删除动作(grill 纠偏 20260821,原型/票 13 均无删除)**:关联客户一律不可单独删(仅随商机级联清理,软删口径随商机)。要更换主要意向客户走「改主要意向客户」切标记(事务内旧主→0、新主→1),不删行。因无单独删 → 不存「删一客户又重加同 customer_id」场景,唯一索引无需 `delete_key`
- 约束:`UNIQUE(opportunity_id, customer_id)`(一客户一商机只关联一次);主要意向客户唯一(应用层事务 + 部分唯一索引)。
- **「改主要意向客户」一致性(grill 拍板 20260821)**:一个事务内三步原子完成——① 旧主 `is_primary_intended` 1→0;② 新主 0→1;③ 刷主表冗余 `primary_customer_id`/`primary_customer_name_snapshot`=新主(主表冗余与子表标记永不中间不一致)。**并发防护 = 复用商机主表乐观锁 `version`**(切主客户=商机级写,CAS `WHERE opportunity_id=? AND version=?`,并发切换仅一胜出、其余版本失配重试/报错);子表部分唯一索引 `UNIQUE(opportunity_id) WHERE is_primary_intended=1` 作 DB 最后防线。
### 7.2 方案卡 `opportunity_scheme_card`
@ -457,9 +479,10 @@ crm-opportunity → crm-rule(阶段模板 / 方案卡模板 / 公海规则
- **一客户一卡**:`UNIQUE(opportunity_id, customer_id)`;每张卡必挂一个当前商机下的关联客户(无孤立卡)。
- **状态机**:`草稿1 / 已提交2 / 已推送3`(「未创建」= 0 卡展示态,不入库)。
- 保存草稿 → 草稿;确认 → 已提交(核心字段锁);推送 A7-3-2-3 → 已推送(**本期可达**,推送方案补票,grill 补拍 20260821)。
- 保存草稿 → 草稿;确认 → 已提交(核心字段锁);推送 A7-3-2-3 → 已推送(**本期不可达**,推送链路随推送票 spin-out;本期卡最高至已提交 2,grill 拍板 20260821)。
- **推送成功 → 系统自动创建项目 → 商机转终态 5 + 落「已转项目」固定节点**(E8 触发源,grill 补拍 20260821)。
- **可编辑性**:草稿(1)/已提交(2) 均可改全部字段;仅已推送(3) 整卡只读(`card_status < 3` 可编辑),要改新建卡不做字段级锁
- **终态优先级高于方案卡自身状态机(grill 补拍 20260821)**:**任一方案卡推送成功即触发整商机转项目终态(E8,2→5)**——一商机多客户多卡,只要其中一张推送成功、系统建项目,整商机立刻进 `STATUS_CONVERTED(5)` 锁死。此后**其余草稿/已提交卡一律冻结为只读**(终态压制方案卡自身「草稿/已提交可编辑」口径)。故上一行「`card_status<3` 可编辑的前提是**商机未处于关闭(4)/终态(5)**;商机一旦关闭或转项目全部卡只读
| 关键字段 | 说明 |
|---|---|
@ -476,6 +499,8 @@ crm-opportunity → crm-rule(阶段模板 / 方案卡模板 / 公海规则
- 创建方案卡时**手动选模板版本**(不自动匹配,产品明确)。
- 候选范围 = 模板适用范围命中**商机 `owner_dept_id`**(所有商机档 + 指定部门命中档)的**「发布中」版本**;草稿/停用版本不可选。
- 选定后卡存 `card_template_id`(版本行 id)**锁版本**,模板发新版后在途卡继续用原版(V-CONFIG 7),不自动迁移。
- **无发布中模板→ 不能创建方案卡(grill 拍板 20260821)**:创建入口置灰,提示「当前无可用方案卡模板,请联系管理员」。候选匹配 = **专用优先、通用兵底**(命中本部门专用发布中版本 ∪ 所有部门通用发布中版本,与公海规则引擎口径统一),两者皆无才置灰。
- **系统初始化种子模板(grill 拍板 20260821,由 `crm-rule` seed 植入)**:初始化时种一条**「全公司通用」发布中方案卡模板作兵底(适用范围=所有部门),保证「无发布中→不能建卡」实践上几不触发。兵底模板可被管理员停用(二次确认警示,不硬锁)。字段集见附录 A。
### 7.3 现场勘察 `opportunity_site_survey`
@ -520,8 +545,12 @@ crm-opportunity → crm-rule(阶段模板 / 方案卡模板 / 公海规则
**出处**:票 09 P6,原型 A3-1-1-1-9。
- **字段级变更审计**:实体对象/字段名称/旧值/新值/操作说明/来源(系统自动 vs 人工)/时间/操作人。普通用户只读。
- **与线索 `lead_history` 口径不同**:线索是动作枚举+JSON,商机是字段级旧新值审计。
- **双维度模型(grill 拍板 20260821)**:一条日志由两个正交枚举描述。
- **`op_kind` 记录形态**(怎么存):`FIELD_CHANGE`(有旧值/新值)/ `ROW_ADD`(整行新增,无旧值)/ `ROW_DELETE`(整行删除,无新值,带 `biz_ref` 被删行业务标识 + 快照摘要 JSON)。子表整行增删(关联客户/团队成员/勘察等)走 `ROW_ADD`/`ROW_DELETE`,日志可读为「删除关联客户:上海XX公司」而非 `deleted 0→1` 废记录。
- **`log_type` 业务类别**(原型 line 1745,决定前端日志页是否展示):`FIELD_CHANGE` 字段变更 / `STATUS_FLOW` 状态流转 / `STAGE_FLOW` 阶段流转 / `ATTACHMENT` 附件上传 / `SCHEME_PUSH` 方案推送 / `ROW_CHANGE` 子表增删(以上前端日志页可见)/ `SYSTEM_INTERNAL` 系统内部(**记库、前端日志页过滤不显**)。**枚举不含浏览/关注**(冲突6 定为硬删+logback、不入 oplog)。
- **记/不记口径(P1-5 + grill 20260821)**:默认全记 + 短黑名单排除(直接不记)。黑名单 = 纯技术字段(`create_time`/`update_time`/乐观锁列)+ 无业务含义的系统自动刷新(`last_valid_follow_time`)→ 连 `SYSTEM_INTERNAL` 都不落;`SYSTEM_INTERNAL` 只给「有排查价值、不给终端用户看」的少数系统动作(如 executeRecycle 系统侧写、推送联动终态),记库而不显(黑名单 ∩ SYSTEM_INTERNAL = ∅)。
- **删除留痕**:软删子表的删除动作记一条 `ROW_DELETE` oplog(给人看轨迹)+ 依赖 BaseEntity `updater_id`/`update_time`(给排查看谁在何时删),**不单加 `deleted_by`/`deleted_time`**。
- **与线索 `lead_history` 口径不同**:线索是动作枚举+JSON,商机是字段级旧新值审计 + 双维度分类。
- 状态/阶段迁移另有专用快照表 `opportunity_status_history` / `opportunity_stage_history`,与本通用审计并存不重。
### 7.8 关注 `opportunity_focus`
@ -543,6 +572,26 @@ crm-opportunity → crm-rule(阶段模板 / 方案卡模板 / 公海规则
- 裁剪:recent 视图只取上限条数(本期默认 50),表内历史行靠后台 Job 按容量/时间裁剪(超出视图范围的历史无业务用途)。
- 「最近访问」视图 = 该表命中当前用户,按 `last_view_time` 倒序取上限。
### 7.10 待发通知 `opportunity_pending_notice`
**出处**:grill 拍板 20260821(回收提醒需落地、但平台通知能力未建)。
**通用待发通知表**——承接三处「向负责人发通知」需求(回收前提醒/暂缓到期提醒/催办)。本期只落待发记录,发送等平台通知能力就绪后由另一消费者补发。
| 字段 | 类型 | 说明 |
|---|---|---|
| `opportunity_id` | bigint | 所属商机 |
| `notice_type` | varchar(32) | `RECYCLE_REMIND` 回收前提醒(本期唯一实际写入);预留 `PAUSE_EXPIRE` 暂缓到期 / `URGE` 催办 |
| `target_user_id` | bigint | 应通知对象快照 = 当前负责人 |
| `plan_notify_time` | datetime | 应发提醒时刻 |
| `payload_json` | text | 提醒上下文(预计回收日/`recycle_days`/已无跟进天数等,**无「剩余天数」伪状态**) |
| `notice_status` | tinyint | 待发0 / 已发1 / 已失效2 |
- **幂等**:`UNIQUE(opportunity_id, notice_type, 批次键)` 防重复堆积;回收 Job upsert 待发记录。
- **补发**:平台通知能力就绪后,另起消费者扫 `notice_status=待发` 发出、置「已发」。
- **失效**:提醒发出前商机被回收/领取/暂缓/关闭 → 对应待发记录置「已失效」(不再补发过期提醒)。
- 本期只 `RECYCLE_REMIND` 真实写入;`PAUSE_EXPIRE`/`URGE` 随对应 spin-out 功能就绪时接入(不重建表)。
---
## §8 上下游集成
@ -552,7 +601,8 @@ crm-opportunity → crm-rule(阶段模板 / 方案卡模板 / 公海规则
**出处**:票 02 `lead-port-consumer-contract`,原型 A3-1-1(线索转商机)。
- 线索侧提供 `CreateOpportunityCmd`(out-port),商机模块消费,转商机时自动带 `source_lead_id`
- **契约变更(票 02 拍板,票 11 实现)**:`CreateOpportunityCmd` 新增 `customerId`(Long,**必填**);`intendedCustomer` 保留为客户名快照(走法 B:引用 + 快照并存)。
- **契约现状 vs 目标(grill 补拍 20260821,定性)**:本 PRD 描述的是**目标契约**;`crm-lead` 侧代码 `CreateOpportunityCmd` 是**现状**(尚未含 `customerId`,字段命名为 `intendedCustomer`/`regionCode`/`industryCode`);**票 11 = 差量**(把代码改齐目标)。实现方勿把本节读成「代码已如此」。
- **契约变更(票 02 拍板,票 11 实现)**:目标 = `CreateOpportunityCmd` 新增 `customerId`(Long,**必填**);`intendedCustomer` 保留为客户名快照(走法 B:引用 + 快照并存)。**字段命名以主表 §2.1 为准**——票 11 落地时做一次字段对齐:`regionCode`→拆 `province_code`/`city_code`;`intendedCustomer`→拆 `customerId`(引用)+ 客户名快照。
- 线索转商机弹窗:意向客户改为**下拉选客户**(选中拿到 `customer_id`);线索转商机前置要求「必须有意向客户」。
- 转商机时商机侧动作:建主表 + 建团队成员一条(领取人=商机负责人)+ 建关联客户一条(`is_primary_intended=1`,`customer_id`=cmd.customerId)。
- **幂等**:`UNIQUE(source_lead_id)` 保证一条线索只能关联一条商机。
@ -569,9 +619,10 @@ crm-opportunity → crm-rule(阶段模板 / 方案卡模板 / 公海规则
### 8.3 商机 → 项目(转项目端口占位)
- E8 转项目:对接 A5 项目模块(本模块只占端口,转项目后商机锁死终态 5)。
- **标准路径(grill 补拍 20260821)**:创建商机 → 填写方案卡 → **推送方案卡 → 系统自动创建项目** → 商机触发轴 1 终态 + 轴 2 固定节点「已转项目」。列表「转项目」按钮 = 推送流程快捷入口。
- **标准路径(grill 补拍 20260821;本期 spin-out 不可达)**:创建商机 → 填写方案卡 → **推送方案卡 → 系统自动创建项目** → 商机触发轴 1 终态 + 轴 2 固定节点「已转项目」。列表「转项目」按钮 = 推送流程快捷入口(推送票未拆,本期不可达)
- **特殊路径(grill 补拍 20260821)**:跳过商机录入,项目侧直接创建项目,可关联方案卡(选填)——归 A5 项目模块定义。**关联即联动(grill 补拍 20260821)**:项目创建成功且关联了商机(或商机下的方案卡)时,A5 **回调商机模块触发 E8**(2→5)+ 落「已转项目」固定节点;本模块暴露回调端口(入参 = 商机 id 或方案卡 id),校验关联合法性后执行 E8 侧效(幂等:商机已终态则拒绝/跳过)。
- **推送方案本期做(grill 补拍 20260821,票 07 边界修订)**:推送流程页 A3-1-1-2-5 + 方案推送规则 A7-3-2-3 + **项目查重规则 A7-3-2-4 一并纳入**,**补一张推送票**(流程/规则细则待该票定;原型推送链路 = 选卡 → 查重校验 → 创建项目);推送成功 → A5 建项目 → 触发 E8。E8 两个触发源(标准/特殊)本期均真实可达。
- **转项目回调端口契约定死(grill 拍板 20260821,对称 lead→opp port)**——本期就出 interface,不留给 A5:① **定义方 = 商机侧 inbound port**(`crm-opportunity/port/inbound`,命名 `OpportunityConvertPort`),A5 调用(与 lead→opp 方向相反、范式一致);② **入参 record `ConvertToProjectCmd(opportunityId, schemeCardId, projectId)`**:`opportunityId`(Long 必填、幂等主键)/ `schemeCardId`(Long 选填、标哪张卡触发、无则 null)/ `projectId`(Long 已建项目 id,写快照/oplog)——“商机 id 或方案卡 id”含糊收敛为:商机 id 必填做幂等键、方案卡 id 选填标来源;③ **幂等命中(已终态 5)= 安静跳过返回**(对齐 lead port 口径,不抛异常),**校验失败(不存在/非可转态)= 抛 `OpportunityConvertException`**(对称 `OpportunityCreationException`);④ 本模块出 interface + record + exception 三件(对称 lead port 三件)。
- **推送方案本期 spin-out(grill 拍板 20260821,翻转 20260821 初拍“本期做”)**:推送流程页 A3-1-1-2-5 + 方案推送规则 A7-3-2-3 + 项目查重规则 A7-3-2-4 整体随一张**推送票 spin-out**(尚未拆)。理由:推送链路(选卡→查重→建项目)依赖 A5 项目模块 + 查重规则,与 A5 同为未建下游,硬说“本期可达”是给实现方埋雷。**本期 E8 仅特殊路径(A5 回调 `OpportunityConvertPort`)真实可达**;标准路径推送不可达。
### 8.4 商机 ↔ 客户(A4 客户模块)
@ -633,6 +684,32 @@ crm-opportunity → crm-rule(阶段模板 / 方案卡模板 / 公海规则
---
## 附录 A 方案卡种子模板「全公司通用」字段集(grill 拍板 20260821)
**来源**:原型 A7-3-2-2 预览填写页(OCR 校准 `1787558225260_*.png`)。由 `crm-rule` 初始化 seed 植入一条适用范围=所有部门、状态=发布中的模板,作为建卡兵底(冲突11)。`crm-opportunity` 只消费不负责 seed。
| 序 | 字段名称 | 字段类型 | 显示 | 必填 | A4 依赖 | 本期 seed | 备注 |
|---|---|---|---|---|---|---|---|
| 1 | 客户名称 | 关联对象 | 是 | 是 | ✅依赖 A4 | ⛳暂缓植入 | 从卡所挂关联客户引用带出(非卡冗余存储) |
| 2 | 客户类型 | 单选下拉 | 是 | 是 | — | ✅植入 | 选项可 `option_source` 引 crm-dict「客户角色/类型」分组 |
| 3 | 客户联系人 | 关联对象 | 是 | 是 | ✅依赖 A4 | ⛳暂缓植入 | 引用带出(依赖 A4) |
| 4 | 客户联系电话 | 自动回填 | 是 | 否 | ✅依赖 A4 | ⛳暂缓植入 | `autofill_source`=联系人电话 |
| 5 | 项目阶段 | 单选下拉 | 是 | 是 | — | ✅植入 | |
| 6 | 招标形式 | 单选下拉 | 是 | 是 | — | ✅植入 | `option_source` 引字典「招标形式」 |
| 7 | 是否有看现场 | 是/否 | 是 | 否 | — | ✅植入 | 布尔型 |
| 8 | 现场勘察资料 | 附件上传 | 是 | 否 | — | ✅植入 | crm-file |
| 9 | 方案预算金额 | 金额 | 是 | 否 | — | ✅植入 | = `scheme_budget`,列表「方案预算」列取主客户卡此值 |
| 10 | 设计配置 | 单选下拉 | 是 | 否 | — | ✅植入 | |
| 11 | 客户需求描述 | 多行文本 | 是 | 是 | — | ✅植入 | |
**A4 缺席期的最小可跑子集(grill 拍板 20260821,选项 A)**:本期 `crm-rule` seed **只植入不依赖 A4 的字段**(序 2/5/6/7/8/9/10/11),保证 A4 缺席时建卡链路真可跑。依赖 A4 的 3 字段(序 1/3/4)**字段定义保留于 `opportunity_scheme_field_def`、但本期不进 seed 模板字段集**(标「A4 上线后启用」);A4 到达后管理员发新版模板把这 3 字段加回即可(V-CONFIG 发新版,无需改表)。
**字段类型初始枚举(`opportunity_scheme_field_def.field_type`,P0-3 自建字段库)**:关联对象 / 单选下拉 / 自动回填 / 是/否 / 附件上传 / 金额 / 多行文本(取自原型 11 字段,grill 20260821)。
对齐说明:① 前 4 字段(客户名称/类型/联系人/电话)为关联客户引用带出的展示字段,非卡新存业务数据;② 原型本版未列「预计成交时间」(P0-1 它是方案卡内部字段、可由模板配置,不强塞进种子模板)。
---
## §11 待确认清单索引(实现前须产品拍板)
> 完整版见 `.scratch/opportunity-module/产品待确认清单.md`。以下均**不阻塞建模/表结构**(各票已给默认处置)。

Loading…
Cancel
Save