# 12: 商机详情工作计划 Tab(精简 A1X:workplan 四端点) `Type: task` `Status: resolved` `Blocked by: 无(graduate 自票 07 拍板 A,20260831)` ## 任务 票 07 拍板 A:全量 A1X 挂起(P1-4 维持),graduate 精简 Tab——补齐预留子表 `opportunity_work_plan` 的 API(纯子表 CRUD,0 新表),闭环「有表无 API」欠账(`OpportunitySubController` 六子域无 workplan 端点): 1. **四端点**(挂 `OpportunitySubController` 新增「工作计划 Tab」节,flat 风格与 team 域同构,ADR-0017 IO 约定): - `GET /api/opportunity/workplan/list?oppId=` — 存活计划列表(deleteKey=0,createTime 升序,返回实体列表对齐 team/list) - `POST /api/opportunity/workplan/add` — `OpportunityWorkPlanAddDTO`(oppId 必填 / planContent 必填 ≤1000 / deadline 可空) - `POST /api/opportunity/workplan/update` — `OpportunityWorkPlanUpdateDTO`(id 必填;planContent/deadline/planStatus 可选部分更新);**登记完成回填**:planStatus 0→1 服务端写 `finishTime=now`,1→0 清 `finishTime`;planStatus 只允许 0/1(值域封闭 66001) - `POST /api/opportunity/workplan/delete?id=` — 软删 `deleteKey=id + deleted=1` + ROW_DELETE 操作日志(对齐 follow/team 删除形态) 2. **域纪律**(同构既有子域):写路径 `requireOpp` + `actionGuard.ensureNotPaused`(D-07 暂缓禁写 66003);**逾期实时计算不落库**(语义=未完成且 now>deadline,实体注释已声明,列表返回原始字段由前端实时判)。 3. **单测**:`OpportunitySubServiceImplTest` 补 workplan 组(@Mock `OpportunityWorkPlanMapper` + `@BeforeAll` TableInfoHelper 注册 `OpportunityWorkPlan`);主路径 + 负路径(记录不存在 / 暂缓 66003 / planContent 必填 / planStatus 值域)。 4. **数据与文档并入**(票 07 拍板指定): - 造数并入票 11 seed:`seed-opportunity.py` 补工作计划样例(推进中商机挂计划:未完成+逾期 / 未完成+临期 / 已完成三种形态),重跑幂等。 - 文档并入票 10 bruno collection:商机详情页面文件夹补 4 端点 .bru 契约(bruno-sync,只碰 meta 含 generated)。 ## 约束 - 不建新表、不动主表结构;全量 A1X(工作日历/出差拜访/多态关联)仍挂起,不在本票。 - 运行时验证脚本一律 Python requests(中文 body mojibake 纪律)。 - update 部分更新用 LambdaUpdateWrapper 显式 set(票 06 实体 `= ""` 初始化 + NOT_NULL 策略清字段 bug 的既有教训)。 ## 验收 - crm-opportunity 单测全绿;BOM 扫描干净。 - 运行时验证脚本全绿:list 空→add→update 改内容/登记完成(finishTime 回填)→取消完成(finishTime 清空)→delete 后 list 不可见 + 暂缓商机写路径 66003。 - seed 重跑幂等含工作计划样例;bruno collection 补 4 端点且对账通过。 ## Answer **已完成**(20260831)。四端点落地 + 单测 30/0 + 运行时 17/17 + seed/bruno 并入全绿,P1-4 精简 Tab 口径闭环。 ### 交付内容 1. **四端点**(`OpportunitySubController` 新增「工作计划 Tab」节,flat 风格同构 team 域):`GET workplan/list`(deleteKey=0、createTime 升序);`POST workplan/add`(`OpportunityWorkPlanAddDTO.toEntity()` 显式映射);`POST workplan/update`(`OpportunityWorkPlanUpdateDTO` 部分更新,LambdaUpdateWrapper 显式 set——票 06 清字段 bug 教训;**登记完成回填**:planStatus 0→1 写 finishTime=now、1→0 清空;值域封闭 0/1 拒 66001);`POST workplan/delete`(软删 delete_key=id + ROW_DELETE 日志)。DTO deadline 加 `@DateTimeFormat(pattern="yyyy-MM-dd HH:mm:ss")`——契约第 8 条;无全局 LocalDateTime 表单转换器,不加注解 ISO 形态带 T 会 400(预判踩坑)。 2. **域纪律同构既有子域**:三写入口全接线 `requireOpp`(66002) + `actionGuard.ensureNotPaused`(66003,D-07 暂缓禁写);add 写 ROW_ADD 日志(entityName=opportunity_work_plan);update 不写行日志(OplogKind 无 ROW_CHANGE 值,字段级模型不适用——留后续可议);逾期实时计算不落库(实体注释口径保持,前端按 planStatus+deadline 实时判)。 3. **单测**:`OpportunitySubServiceImplTest` 18→**30**(新增 12:主路径/66001×3/66003/66002×2/回填与清空(wrapper sqlSet+params 断言)/部分更新不触碰状态列/列表);`@BeforeAll` TableInfoHelper 注册 `OpportunityWorkPlan`(纯 Mockito lambda cache 坑,既有记忆)。 4. **运行时验证** `t12-verify.py`:**17/17 全绿**(L 清理/A add 含 66001×2+ROW_ADD/U 部分更新+回填实测 finishTime=09:39:21+清空+66001+66002/D 软删不可见+ROW_DELETE+重复删 66002/P 暂缓三入口 66003——暂缓行由 DB 直插造(API 被守卫挡住属豁免口径),验后硬删)。 5. **seed 并入**(票 11 脚本):a2 工作计划三形态(e2e-wp- 前缀:逾期/临期/已完成,已完成走 update 登记完成回填);幂等=list 按内容精确匹配复用 + deadline/状态校准(--skip-db-clean 不重复 add);三遍实跑(全量→skip→全量)缺陷 0,终态断言 `t12-wpcheck.py` a2 恰 3 行 0/0/1、仅已完成行有 finish_time;manifest 三件套刷新。插曲:`--skip-db-clean` 模式本身会全量再造商机(既有行为非本票引入,12→24),已跑全量恢复 12 条基线(MANAGE=12/PUBLIC_POOL=2/A_MINE=4/B_MINE=4/C_MINE=2 与票 11 一致)。 6. **bruno**:`商机模块/商机详情/工作计划/` 新建 folder.bru(seq=90,对齐 Tab seq 数轴 10~80)+4 端点 .bru(meta 含 generated、docs 首行对账钥匙、list/add 用 20260831 实测响应摘录、结构表只列业务字段对齐客户列表先例);与客户/团队 Tab 同构落位(先例=票 04/10 的人为结构,源码类级 @Tag 未映射目录树)。未做 git 操作,待人 diff 提交。 ### 验收对照 - ✅ 单测全绿:crm-opportunity 209/0/0(surefire:test;SubServiceImplTest 30/0);BOM 扫描 6 文件全净。 - ✅ 运行时验证全绿:t12-verify.py 17/17(L/A/U/D/P 五段)。 - ✅ seed 重跑幂等含工作计划样例;bruno collection 补 4 端点且与既有 Tab 同构。 - ⚠ 透明记录:**crm-auth `ResourceServiceImplTest.save_editCatalog_noButtonFields` 既有红灯**(expected null but was "unused-perm",工作区干净、单跑必现、与票 12 零关联)——不扩票,留裁决。 ### 备注 - 全量 A1X 仍挂起(P1-4 维持):本票只做商机子表 Tab,0 新表。 - fat jar 打包复刻票 04 坑:jar 插件静默 up-to-date 跳过 → repackage 拿旧货仍报 BUILD SUCCESS;clean package 后嵌套 jar 才含新类(抽取嵌套 jar 实锤核查)。