You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 

14 KiB

商机模块(opportunity-module)接口验收报告(重写版)

本文替代旧版 verify-report.WRONG.bak.md 旧版核心结论大面积错误,重写说明见文末「附录:为何重写」。

  • 验收方式:原型驱动 + 黑盒实测。后端 localhost:8080(远程 DB 8.129.84.155),调试用户 userId=739564171091247104
  • 实测脚本e2e_test.py(29 条主链)+ supplement_test.py(49 条补测)+ 手工 curl(状态机 9 步完整流转、批量流转成功路径)。
  • 路由发现:一次性读取 4 个商机 Controller + 5 个规则/字典/偏好 Controller 的 @*Mapping(仅用于发现待测路由,验收结论一律以 HTTP 实测为准)。
  • 判定图例 存在且业务可用|⚠ 存在但有契约差异/边界问题| 真实缺失或真实 bug。

一、总体结论

后端接口整体已实现,可用率极高。全量实测未发现任何真正的 404(路由缺失)。 报告范围内 85 个后端路由全部存在并正确响应。

唯一的致命缺陷是新建商机接口必然 500。其余问题均为「前端对接需注意的契约差异」,非缺失。

维度 结论
路由缺失(真 404) 0 个
致命 bug 1 个POST /api/opportunity 新建必 500
契约差异(需前端适配) 6 类(分页字段、参数名、路径风格、字典编码、视图路径、region 参数)
状态机 9 条流转 全部实测通过(单条 + 批量成功路径均验证)

二、致命缺陷( 必须修)

D-01 新建商机 POST /api/opportunity 必然 500

  • 实测POST /api/opportunity,body opportunityName=xxx&oppSource=opp_source_02HTTP 500 code:50001 系统内部错误。多次、多参数组合稳定复现。
  • 后端日志根因logs/crm-app.log):
    DataIntegrityViolationException: Field 'current_stage_id' doesn't have a default value
    INSERT INTO opportunity (... 无 current_stage_id ...) VALUES (...)
    at OpportunityIntakeImpl.open(OpportunityIntakeImpl.java:73)
    at OpportunityCreateServiceImpl.createOpportunity(OpportunityCreateServiceImpl.java:79)
    
  • 定性OpportunityIntakeImpl.open() 组装 INSERT 时未给 current_stage_id 赋值,而 DB 该列 NOT NULL 且无默认值。
  • 影响:新建商机 100% 失败,是商机业务链的入口——最高优先级
  • 修复方向:入库前按「适用范围命中的阶段模板」解析出初始阶段并写入 current_stage_id;若允许无阶段初始态,则给该列加默认值 / 允许 NULL。
  • :旧报告 W-02 把字段名写成 bid_form(错误),但「新建必 500」的现象判断是对的。实际肇因列是 current_stage_id

三、状态机族(旧报告全判 ,实测全部

已存在的推进中商机748098957468499968 等)跑通完整流转,避开 D-01 的新建 500。

流转 接口 单条实测 批量接口 批量实测
领取 POST /api/opportunity/claim 200 code:0 POST /claim-batch 成功路径 code:0
分配 POST /api/opportunity/assign 200 code:0 POST /assign-batch 成功路径 code:0
抛公海 POST /api/opportunity/release-pool 200 code:0
暂缓 POST /api/opportunity/pause 200 code:0
恢复 POST /api/opportunity/resume 200 code:0
关闭 POST /api/opportunity/close 200 code:0 POST /close-batch code:0
重开 POST /api/opportunity/reopen 200 code:0
移交 POST /api/opportunity/handover 200 code:0
  • 完整链路实测pause → resume → handover → release-pool → claim → release-pool → assign → close → reopen,9 步全 200 code:0。
  • 状态守卫正确:对源状态不匹配的流转(如「推进中」再 claim)返 code:66003 当前状态不允许此操作——是正确的状态机守卫,非缺失。
  • 路径风格:后端为 flat 风格/api/opportunity/claim,id 走 body),不是 RESTful /api/opportunity/{id}/claim。旧报告只测了 RESTful 路径得到 404 → 误判「状态机全缺」。

⚠ 契约差异:状态机全部走 POST + form bodyidpauseReasonpoolReasoncloseReasonreopenReasonnewOwnerUserId/expectedOwnerUserIduserIdids),前端不能用 RESTful 路径拼 id。


四、列表族(旧报告部分判 ,实测全部

页面 接口 实测 说明
销售机会列表(3 视图) POST /api/opportunity/pageviewType 区分) 200 code:0 分页字段 content/total(见 C-01)
商机公海列表 POST /api/opportunity/page?viewType=pool 200 code:0 同上
看板-阶段汇总 POST /api/opportunity/board/stage-summary 200 code:0 旧报告判 M-02 ,误判
看板-单阶段卡片 POST /api/opportunity/board/cards 200 code:0 旧报告判 M-03 ,误判
最近访问-上报 POST /api/opportunity/view-touch 200 code:0 后端以 view-touch 承担「访问上报」,非 spec 的 recent-visits/touch(旧报告 M-01 误判为缺失)
可转商机线索 GET /api/opportunity/convertible-leads 200 code:0 新建页关联线索来源

⚠ C-01 分页字段:后端为 Spring Data / MyBatis-Plus 风格 data.content + data.total(另有 size/current/pages/empty),不是 data.records。旧报告 W-01 判断正确。


五、详情族(旧报告全判 ,实测全部

后端为 flat 风格,id/oppId 走 query 或 body, RESTful /{id}/xxx

功能 接口 实测
商机详情 GET /api/opportunity/detail?id= 200 code:0,返回完整字段
编辑商机 POST /api/opportunity/edit 200 code:0
关联客户列表 GET /api/opportunity/customer/list?oppId= 200 code:0
方案卡列表 GET /api/opportunity/scheme-card/list?oppId= 200 code:0
阶段进度 GET /api/opportunity/stage/progress?oppId= 200 code:0
现场勘察分页 POST /api/opportunity/site-survey/page 200 code:0(content/total
相关附件列表 GET /api/opportunity/attachment/list?oppId= 200 code:0
团队成员列表 GET /api/opportunity/team/list?oppId= 200 code:0
操作日志分页 POST /api/opportunity/oplog/page 200 code:0(content/total
关注 / 取消关注 POST /api/opportunity/focus/unfocus 200 code:0

⚠ C-02 参数名:详情/子表全部用 oppId(非 spec 的 opportunityId)、详情主体用 id。旧报告 W-03/04/05 判断正确。

5.1 详情子操作(补测)

功能 接口 实测 说明
写跟进 POST /api/opportunity/follow/add 200 code:0 写入后 last_valid_follow_time 正确刷新(有效跟进定义生效)
跟进分页 POST /api/opportunity/follow/page 200 code:0 content/total
删除跟进 POST /api/opportunity/follow/delete 200 code:0 删除写 ROW_DELETE 操作日志(联动正确)
新增勘察 POST /api/opportunity/site-survey/add ⚠ 需 engineerUserId 等必填 路由存在,缺参返 40001 缺少必填参数:engineerUserId(正确校验)
删除勘察 POST /api/opportunity/site-survey/delete 路由存在
附件新增 POST /api/opportunity/attachment/add 路由存在 10 个上限校验正确:超限返 60001 跟进附件最多 10 个
附件删除 POST /api/opportunity/attachment/delete 200 code:0
方案卡保存 POST /api/opportunity/scheme-card/save 路由存在 业务守卫正确:无客户归属返 66007 关联客户不属于本商机
方案卡提交 POST /api/opportunity/scheme-card/submit 路由存在
方案卡详情 GET /api/opportunity/scheme-card/detail?id= 路由存在 不存在 id 返 66008 方案卡不存在(正确)
可选模板 GET /api/opportunity/scheme-card/selectable-templates?oppId= 200 code:0

六、规则族(A7-3-2,旧报告"路径变更",实测全部

后端统一 /api/rule/opp-*,方法统一 POST(分页/操作)+ GET(详情/版本), spec RESTful /{id}/versions 等。旧报告 M-04/05/06 把 /versions 判为缺失,实测均存在

子域 分页 详情 版本 复制 发布 停用 删除
阶段模板 opp-stage-template
方案卡模板 opp-scheme-template
公海规则 opp-pool-rule
  • 分页POST /api/rule/opp-{stage-template|scheme-template|pool-rule}/page 200 code:0,content/total
  • 详情/版本GET .../detail?id=GET .../versions?id=(阶段、方案卡实测 code:0)。
  • 复制POST .../copy?id=(阶段、方案卡 code:0)。
  • 停用POST .../disable?id=(阶段、方案卡 code:0)。
  • 发布 publish / 删除 delete / 存草稿 save-draft:路由均存在,实测返业务校验错(非缺失):
    • publish模板名称/规则名称不能为空(需带完整模板体)
    • delete仅草稿版本可删除(对已发布版本的正确守卫)
    • save-draft适用范围参数非法(需合法 applyScope
  • 公海规则 detail/versions/copy/disable/delete 补测未带 id40001 缺少必填参数:id——路由存在,测试用例未准备 pool-rule 种子 id,非缺失。
  • 方案卡模板另有 GET .../field-defs?templateId= 200 code:0。

旧报告在规则族的「路径变更」结论是对的(因为它这一段确实用了 flat 路径判定)。


七、字典 / 视图 / 地区族(补测,均

功能 接口 实测 说明
字典分组分页 POST /api/dict/group/page 200 code:0
字典分组启用列表 GET /api/dict/group/enabled-list 200 code:0
字典项分页 POST /api/dict/item/page 200 code:0
字典项启用列表 GET /api/dict/item/enabled-list?groupCode= 200 code:0 商机各枚举取值来源
列偏好读取/保存 GET /api/preference/getPOST /api/preference/save 200 code:0 自定义列
保存视图列表 GET /api/preference/view/list?scopeKey= 200 code:0
保存视图-保存 POST /api/preference/view/save ⚠ 需 JSON body form 提交返 40001 请求格式不支持,请使用 JSON
保存视图-删除/置默认 POST /api/preference/view/delete/set-default 路由存在 scopeKey 参数
地区列表 GET /api/rule/region/list 200 code:0
地区分级 GET /api/rule/region/level?level= ⚠ 需 level 参数 缺参返 40001 缺少必填参数:level(正确校验)

⚠ C-03 字典编码:商机字典 groupCode 为 opp_stage / opp_status / opp_pause_reason / opp_close_reasonopp_ 前缀), spec 里写的 opportunity_stage 等。前端取字典需用实际编码。 ⚠ C-04 视图保存 POST /api/preference/view/save 必须 Content-Type: application/json,不接受 form。


八、契约差异清单(前端对接必读,非 bug)

编号 差异 spec 预期 实际
C-01 分页字段 data.records data.content + data.total(+size/current/pages/empty)
C-02 详情/子表参数名 opportunityId oppId(详情主体 id
C-03 商机字典 groupCode opportunity_stage opp_stage / opp_status / opp_pause_reason / opp_close_reason
C-04 视图保存请求体 form 必须 JSON
C-05 状态机/规则族路径风格 RESTful /{id}/xxx flat:id 走 body/query,/api/opportunity/claim/api/rule/opp-*
C-06 地区分级 无参 必传 level

九、范围外(未测/未报,遵循锁定范围)

一键拉群 / 推送方案(A3-1-1-2-5 + A7-3-2-3)/ 督办 / 转项目(A3-1-1-2-10 + ConvertToProjectCmd)/ 工作计划联动(A3-1-1-1-7)/ 项目查重规则(A7-3-2-4)。


附录:为何重写(旧报告 verify-report.WRONG.bak.md 的问题)

  1. 根本错误——测错了路径:旧报告按 spec-endpoints.md 推断的 RESTful 路径GET /{id}POST /{id}/claimGET /{id}/versions …)去打,后端实际是 flat 风格,于是几乎每条都得到 404,被误判为「接口未实现」。共约 39 条 属误判(状态机 13、详情 5、跟进 3、勘察 2、附件 4、团队 2、日志 1、协作 3、看板 3、规则 versions 3 等)。
  2. 未参考同目录实测产物e2e_test.py / 03-list.html 就在同一目录、用的是正确的 flat 路径,但旧报告完全没引用。
  3. W-02 字段名错误:新建 500 的肇因列是 current_stage_id 而非 bid_form(现象判断对,归因错)。
  4. e2e_test.py 自身的"37/37 OK"也不可信:其状态机段依赖新建返回的 id,而新建必 500 → id 为 None → 8 条状态机被 SKIP(重跑实为 OK=28 / FAIL=1 / SKIP=8)。本报告改用已存在商机实测状态机,才拿到真实的全通过证据。

本报告的证据基础e2e_test.py(重跑)+ supplement_test.py(49 条补测)+ 手工 curl 完整状态机链 + 后端日志根因定位 + Controller 路由清单交叉核对。