# Map: 商机模块接口整改 `wayfinder:map` > **2026-08-27 重大修订**:本 effort 原 destination/整改清单建立在 `verify-report` **旧版(已作废)** 之上,旧版把大量**其实已实现**的接口误判为 ❌ 缺失(详情主体、编辑、状态机 8 Cmd、6 个 tab、协作、看板、访问上报、规则 versions)。经重新黑盒实测(见重写版 `.scratch/opportunity-verify/verify-report.md` + `supplement_test.py`),**这些接口全部存在且业务可用**。整改范围随之坍缩为**唯一一件真事:D-01 新建商机 500**。原整改清单作废,见下。 ## Destination **修复商机模块唯一的真实致命缺陷 D-01:`POST /api/opportunity` 新建商机必然 500**,使商机业务链的入口可用。 - 合法入参新建返回 `code:0` + 新商机 id - 新建商机 `current_stage_id` 非空,能进列表、看板、阶段进度 - 回归:`opportunity-verify/e2e_test.py` 重跑,新建条转 `[OK]`,原被 SKIP 的 8 条状态机全部 `[OK]` ## 作废的原整改清单(旧报告误判,实测均已实现,不再整改) | 原列"待补齐" | 实测真相 | 实际路径 | |--------------|---------|---------| | `GET /api/opportunity/{id}` 详情主体 | ✅ 已实现 | `GET /api/opportunity/detail?id=` | | `PUT /api/opportunity/{id}` 编辑 | ✅ 已实现 | `POST /api/opportunity/edit` | | 状态机 8 Cmd Controller | ✅ 已实现(+批量) | `POST /api/opportunity/{claim,assign,release-pool,pause,resume,close,reopen,handover}` | | 6 个 tab 子域 | ✅ 全实现 | `/customer/list`、`/follow/*`、`/site-survey/*`、`/attachment/*`、`/team/list`、`/oplog/page` | | 协作 关注/取关 | ✅ 已实现 | `/focus`、`/unfocus`、`/focus-batch` | | 看板 stage-summary/cards | ✅ 已实现 | `/board/stage-summary`、`/board/cards` | | 访问上报 | ✅ 已实现 | `/view-touch`(非 spec 的 recent-visits/touch) | | 规则族 /versions ×3 | ✅ 全实现 | `GET /api/rule/opp-*/versions?id=` | > 上述均为**路径风格差异(flat vs RESTful)**导致旧报告误判,属前端对接契约问题,**不改后端**。契约差异清单见重写版报告第八节 C-01~C-06。 **out-of-scope(沿用锁定范围,不改)**:W-01 分页字段 content / C-02 参数名 oppId / C-03~C-06 契约差异 / 一键拉群 / 推送方案 / 督办 / 转项目 / 工作计划联动 / 项目查重规则。 ## 唯一整改项(P0) - **D-01**:`POST /api/opportunity` 新建 500。**真实肇因 = `current_stage_id` NOT NULL 无默认值**(旧报告 W-02 误写为 `bid_form`),`OpportunityIntakeImpl.buildOpportunity` 未给该列赋值。详见 bug 票 `opportunity-verify/issues/07-bug-create-opportunity-500-current-stage-id.md`。 ## 票依赖图 ``` 01 (P0: 修 D-01 新建 current_stage_id) ──→ 02 (回归验证 + 关闭 effort) ``` ## Tickets - **01**(P0): 修 D-01——新建商机入库前赋 `current_stage_id`(按适用范围解析初始阶段),使 `POST /api/opportunity` 可用 = open - **02**(回归): 重跑 `e2e_test.py`,确认新建 + 8 条状态机全绿,关闭 effort = open,blocked by 01 ## Notes ### 编码约定 - 薄适配层:Controller 只做入参绑定 + 调 Service/Domain - 参数名沿用现有 flat 风格(`oppId` 不改) - 编码 UTF-8 无 BOM - 构建验证:`mvn compile -pl crm-opportunity -am -q` ### D-01 白盒线索 - `crm-opportunity/.../intake/impl/OpportunityIntakeImpl.java`:`buildOpportunity(spec)` 组装 Opportunity 实体时未 set `currentStageId` → insert 命中 DB `NOT NULL` 无默认值 - 初始阶段来源:应按商机「适用范围命中的阶段模板」的首个阶段确定(对齐原型「新建即进首阶段」语义) - 相关:`OpportunityCreateServiceImpl.createOpportunity`、阶段模板域 `crm-rule` 的 `opp-stage-template` ## 收尾(2026-08-27 effort 已关闭) - 票 01 resolved:修 `OpportunityIntakeImpl.applyInitialStage` 无模板兜底回填零值,根因 = 全库无发布中阶段模板(默认模板被停用)+ 三列 NOT NULL。 - 票 02 resolved:`e2e_test.py` 回归 **OK=37 / FAIL=0 / SKIP=0**。 - 商机模块唯一真实致命缺陷 D-01 已修复,业务主链路全通。其余「待补齐」皆旧报告误判,实测本就可用,未改后端。 ## 最终定案(2026-08-27,策略 B + 预设保护) - 代码撤销零值兜底,改 fail-fast:无发布中默认阶段模板 → 新建报友好业务错(code 60001,非 500)。 - 后台阶段配置加系统预设保护:`SEED_STAGE_TPL_CODE=OPP_STAGE_TPL_01`(客户圈定/关系摸排/资料采集/方案卡/已转项目)不可删除、不可停用(错误码 64018),保证「始终有一个发布中默认模板」不变式。 - 数据已恢复预设发布中态。e2e OK=37/0/0。 - 改动 3 文件:OpportunityIntakeImpl / OpportunityRuleConstants / OpportunityStageTemplateServiceImpl,均 UTF-8 无 BOM。