hl-api-changelog/changelogs-v2/2026-05/19_#2586_contract-insurance-fillin.md
API Changelog Bot 1e141e445c docs(changelog): #2586 合同/保险模块 TODO 接通 — 从占位 → 完整可用
v3 contract / insurance 三 PR 累计变更(#2590 + #2591 + #2603)。
前端关键变化 4 点:
1. 合同/保险全部接口从占位/抛错转为真实可用
2. POST /v3/admin/insurance/purchase 新增订单状态+支付状态双重校验
3. POST /v3/admin/order/{id}/transition CONFIRM 异步触发自动签约/投保
4. 出行人变更触发已签约订单作废重做(状态白名单过滤)

新增字段:ContractScheme.defaultDestination / defaultDepartureCity
新增错误码:540022 ORDER_NOT_CONFIRMED_FOR_INSURANCE / 510206 合同方案未配置模板
新增异步事件:ChecklistConfirmedEvent / TravelerChangedEvent

测试服真测通过:单测 1096 全绿 + 9443 网关 + 真 admin token + P0-1 正反例齐全。

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-19 16:16:23 +08:00

12 KiB

合同/保险模块 TODO 接通 — 从占位 → 完整可用 (工单 #2586)

存放目录: 二期(v3, order-v3 标签)→ changelogs-v2/2026-05/

服务: hl-order-v3 (端口 8084 / 测试服 9443 网关 + /v3/admin/...) PR: #2590 (主体) + #2591 (CR 跟进) + #2603 (P2/P3 + Publisher 接入) Issue: #2586 日期: 2026-05-19 影响范围: 管理后台「合同」「保险」两大模块全部接口 + 「订单状态机 CONFIRM 转换」副作用


⚠️ 关键变化(前端必看 4 点)

1. 合同 / 保险模块从「占位返空 / 抛异常」转为「完整可用」(行为变化,非破坏性)

PR #2590 之前合同 / 保险服务的所有写接口都返 UnsupportedOperationException 占位,查询接口返空列表 + log.warn TODO。本次合并后全部真实可用

  • POST /v3/admin/contract/create-by-scheme 合同按方案创建 — 真实创建并接入第三方合同平台12301 / 腾讯电子签)
  • POST /v3/admin/insurance/purchase 手动投保 — 真实写 insurance_order + 调宝游 API
  • POST /v3/admin/insurance/auto-purchase / auto-purchase-by-scheme 自动投保 — 真实链路
  • POST /v3/admin/contract/resend-sign-sms 重发签署短信 — 真实做用户归属校验
  • POST /v3/admin/contract/void/{id} 作废 — 真实状态流转
  • 列表 / 详情 / scheme 列表 — 全部返真实数据

前端可解除任何针对这些接口的"待后端实现"占位提示,按真实响应处理。

2. POST /v3/admin/insurance/purchase 新增订单状态校验(业务规则收紧)

只允许以下组合的订单投保:

维度 允许值
orderStatus PENDING_DEPARTURE / PENDING_BALANCE / TRAVELLING
payStatus DEPOSIT_PAID / FULLY_PAID

任一不符 → 返业务码 540022 ORDER_NOT_CONFIRMED_FOR_INSURANCE 文案 "请先确认订单后再配置保险(当前订单状态不允许投保)"

前端处理建议

  • 投保按钮的可点击态可加前置判断(不必依赖后端报错才提示)
  • 若直接拦不到,把 540022 错误码处理成 toast 弹该文案即可

3. POST /v3/admin/order/{id}/transition event=CONFIRM 自动触发合同 / 保险创建(异步)

订单状态机 CUSTOMIZING → PENDING_DEPARTURE 的 CONFIRM 转换会异步触发:

  • 自动创建合同(前提:contract_scheme.status=ACTIVEcontract_template_code 配置过)
  • 自动投保(前提:insurance_scheme.status=ACTIVE

接口同步返回 triggeredEvents: ["ASYNC_CONTRACT_GENERATE", "ASYNC_INSURANCE_ISSUE"]真正写库发生在事务 commit 后异步线程@TransactionalEventListener(AFTER_COMMIT))。

前端表现

  • CONFIRM 转换 200 返回后,不要立刻显示「合同已生成 / 保险已购买」(异步还没跑完)
  • 建议 transition 200 后 toast 提示「订单已确认,合同/保险将在 1 分钟内自动生成」,然后等用户主动刷新合同列表 / 保险列表看到记录
  • 自动签约失败时合同/保险域有内部紧急待办 + 通知中心推送(通知中心走 NotificationDispatcher,渠道在 notification_event_config 配)

4. 出行人变更触发合同 / 保险作废+重做(已签约订单)

TravelerService 的 5 个写方法(batchEdit / add / delete / addByInternal / updateByInternal)末尾按状态白名单异步发布 TravelerChangedEvent

订单 orderStatus 是否触发作废重做
PENDING_DEPARTURE / PENDING_BALANCE / TRAVELLING 触发:作废旧合同 + 重新签约;作废旧保单 + 重投
CUSTOMIZING / PENDING_PAY / PENDING_COMPLETE 等未签约 不触发(避免事件风暴)
REVIEWING / SETTLED / CANCELLED 不触发

前端表现:已签约订单编辑出行人后,合同 / 保险记录会自动作废并重新生成(异步),用户应感知到合同 PDF / 保单号变化。建议在出行人编辑成功后给一个 toast「已签约订单编辑出行人将触发合同/保险重新生成」。


一、背景

订单 v3 的 contract / insurance 域骨架在 yst 早期 PR 已从 v2 搬运,但接通订单核心域(OrderInfoMapper / OrderTravelerMapper)的位置全部以 TODO(PR-order-core) 占位,方法体抛 UnsupportedOperationException 或返 Collections.emptyList()

PR #2552-2554 出行人三连完成后,OrderInfoMapper / OrderTravelerMapper 已在 v3 落位,本工单收尾 — 把 contract / insurance 完整对接、激活 publisher、补全单测、修复 CR 三轮深审找出的 3 P0 + 5 P1 + 10 P2/P3 + 3 P3-NEW。


二、变更接口清单

合同模块admin

# 接口 方法 路径 变更类型
1 合同方案列表(启用) GET /v3/admin/contract/scheme/list 行为变化(返真实数据)
2 合同列表 GET /v3/admin/contract/list 行为变化
3 按方案创建合同 POST /v3/admin/contract/create-by-scheme 行为变化(真实写库 + 第三方调用)
4 重发签署短信 POST /v3/admin/contract/resend-sign-sms 行为变化(接入用户归属校验)
5 作废合同 POST /v3/admin/contract/void/{contractId} 行为变化
6 合同方案 CRUD POST /v3/admin/contract/scheme/* 新增 defaultDestination / defaultDepartureCity 字段

合同模块mp 内部)

# 接口 方法 路径 变更类型
7 用户合同列表 GET /internal/mp/contract/list 行为变化(按 userId 反查 orderIds
8 用户合同详情 GET /internal/mp/contract/detail/{contractId} 行为变化(接入归属校验)

保险模块admin

# 接口 方法 路径 变更类型
9 保险方案列表(启用) GET /v3/admin/insurance/scheme/list 行为变化
10 保单列表 GET /v3/admin/insurance/orders 行为变化
11 手动投保 POST /v3/admin/insurance/purchase 行为变化 + 状态校验新增
12 自动投保 POST /v3/admin/insurance/auto-purchase 行为变化
13 按方案自动投保 POST /v3/admin/insurance/auto-purchase-by-scheme 行为变化
14 取消保险 POST /v3/admin/insurance/cancel 行为变化
15 投保预览 POST /v3/admin/insurance/preview-apply 行为变化

订单状态机副作用

# 接口 方法 路径 变更类型
16 订单状态流转 POST /v3/admin/order/{id}/transition CONFIRM 事件副作用扩展:异步触发自动签约/投保
17 出行人 batchEdit POST /v3/admin/order/{id}/traveler/batch-edit 已签约阶段触发作废重做
18 出行人 add POST /v3/admin/order/{id}/traveler/add 同上
19 出行人 delete POST /v3/admin/order/{id}/traveler/delete/{travelerId} 同上

三、字段变更(合同方案)

ContractScheme 实体新增 2 个字段(对应表 contract_scheme

字段名 类型 含义 业务规则
defaultDestination varchar(64) NULL 方案默认目的地 createByScheme 时取值优先级scheme.defaultDestination → productName 关键字提取(呼伦贝尔/阿尔山/新疆/内蒙古/青海/西藏等) → 全局兜底「内蒙古呼伦贝尔」
defaultDepartureCity varchar(64) NULL 方案默认出发地 同上,全局兜底「呼和浩特」

前端影响admin 合同方案管理表单加 2 个文本输入框(选填)。


四、错误码新增

业务码 文案 触发条件
540022 请先确认订单后再配置保险(当前订单状态不允许投保) /v3/admin/insurance/purchase 时订单状态 / 支付状态不符合白名单
510206 合同方案未配置模板 /v3/admin/contract/create-by-schemescheme.contractTemplateCode = null

其他既有错误码不变。


五、Publisher 异步链路

ChecklistConfirmedEvent

  • 发布点OrderService.transition() 守卫 from=CUSTOMIZING && to=PENDING_DEPARTURE && event=CONFIRM
  • 携带orderId / orderNo / operatorId / operatorType (ADMIN/SYSTEM)
  • operatorId 来源AdminContextUtil.getAdminId()(请求上下文)→ 无则 null + SYSTEM
  • 监听者ContractEventListener + InsuranceEventListener 都接 @TransactionalEventListener(AFTER_COMMIT, fallbackExecution=true)
  • 下游行为listener 走 contract.createByScheme / insurance.autoPurchase,失败有紧急待办 + 通知中心降级通知

TravelerChangedEvent

  • 发布点TravelerService 5 个写方法末尾
  • 守卫:仅在订单 orderStatus ∈ {PENDING_DEPARTURE, PENDING_BALANCE, TRAVELLING} 时发布
  • 携带orderId / orderNo / changeType (ADD/MODIFY/REMOVE) / affectedTravelerIds
  • 下游行为listener 走 contract.invalidateByOrderId + createByScheme(作废重签)、insurance.cancelByOrderId + autoPurchase(退旧重投)

六、数据库迁移

文件 内容
V20260519_004__create_order_todo.sql 新增 order_todo 表(订单内部待办,自动签约/投保失败时生成紧急待办)
V20260519_005__contract_scheme_add_default_destination.sql contract_schemedefault_destination + default_departure_city 两列

七、测试服真测结论

部署

  • 测试服 v3 已部署 dev-v3 最新 HEAD af7dd70 双实例 8086+8186
  • Flyway 应用 V20260519_004 + V20260519_005 成功
  • Nacos test namespace 注册健康,9443 网关路由通

API 真测test_admin token 通过 trusted device 通路)

接口 结果
GET 合同 scheme list / 合同 list / 保险 scheme list / 保单 list 200 真实数据
POST insurance.purchase CUSTOMIZING+UNPAID 抛 540022
POST insurance.purchase PENDING_DEPARTURE+UNPAID 抛 540022证明 pay_status 单独校验起作用
POST insurance.purchase PENDING_DEPARTURE+DEPOSIT_PAID 状态校验过,进入 planId 校验阶段
POST order.transition CONFIRM 200,triggeredEvents 含 ASYNC_CONTRACT_GENERATE + ASYNC_INSURANCE_ISSUE,listener 真收到(业务数据 scheme 缺所以 fail-soft

单测

  • mvn -pl hl-order-service-v3 testTests run 1096 / Failures 0 / Errors 0(基线 1069 → +27

八、已知限制(非缺陷)

  1. 测试服当前 active contract_scheme 都没填 contract_template_code,所以 transition CONFIRM 异步自动签约会 short-circuit「无可用合同方案」并发紧急待办。需要在 admin 后台进 /v3/admin/contract/scheme/* 表单配置 contract_template_code 后才能完整测端到端。
  2. 测试服当前没有 active insurance_scheme,同上。
  3. 自动签约时按 v3 当前 list 第一个 ACTIVE 方案挑选(暂无 mchId/productId 路由),后续多公司多产品场景需要在 ContractSchememchId / productId 字段后过滤。

九、关联

  • 主 PR#2590 (wx/HL#2590)
  • CR 跟进:#2591
  • 最终零遗留:#2603
  • 工单:#2586

前端 mmg:请同步检查投保 / 合同管理 / 出行人编辑 / 订单确认 4 个页面的 UI 提示逻辑,重点是「投保按钮置灰条件」+「订单确认后异步合同/保险生成提示」+「已签约后编辑出行人的告知 toast」。如有疑问随时找。