[changelogs-v2/2026-05] #2586 合同/保险接通后 4 处 VO/字段不明 #3

已关闭
mmg2026-05-19 16:42:51 +08:00创建 · 1 评论
管理员

前端按 #2586 接入合同/保险 v3 完成(commit 08359913),但 changelog 未列 VO 完整字段表与若干字段约束,前端目前用「宽容渲染 + 占位字段名」过渡,需后端补齐契约。

一、待确认

1) ContractVO 完整字段

GET /v3/admin/contract/list?orderId= 出参 list 元素字段未列。前端当前读:schemeName / version / contractNo / status / signedAt / signUrl / pdfUrl(沿用老 mock 名)。请确认真实字段名(特别是 contractNo 是否就叫这个 / pdfUrl 还是 contractFileUrl)。

2) InsuranceOrderVO 完整字段

同上,GET /v3/admin/insurance/orders 出参字段未列。前端当前读:productName / policyNo / status / amount / coveragePeriod / appliedAt / policyUrl

3) ContractSchemeVO / InsuranceSchemeVO 字段

方案下拉用,前端只用 id / name,确认即可;如有 disabled / enabled 之类的状态字段也请列。

4) createContractByScheme / purchaseInsurance body 完整字段

changelog 写 {orderId, schemeId, ...} 省略号未展开。请列必填/选填全字段(特别是手动投保是否需要前端传 travelerIds / coverageStartDate 等)。

5) triggeredEvents 是否稳定契约字段

POST /v3/admin/order/{id}/transition 同步返回的 triggeredEvents: ["ASYNC_CONTRACT_GENERATE", "ASYNC_INSURANCE_ISSUE"] 是约定字段吗?前端按 changelog 描述直接 parse 这个数组,命中则 toast 异步生成提示。如果是临时调试字段需要换其他识别方式。

6) OrderMainVO 6 个新 tab 状态字段

前端 v3Adapter 假设 OrderMainVO 包含 contractStatus / insuranceStatus / refundStatus / hasRefund / hasServiceStandard / hasFinanceDetail 用于侧边 Tab 红点判断,但 §1.2 当前 OrderMainVO 字段表未列。请确认是否已加 / 计划加 / 走另一接口聚合。

二、低优 / 仅文档建议

  • 投保按钮置灰条件(orderStatus + payStatus 白名单)现在写死在前端,能否将白名单也作为业务码 540022 的 message 附带返回前端,便于后端规则变更时前端零改动?

阻塞情况

不阻塞:前端已用宽容渲染上线,等真测时按真实字段名微调。仅在前端联调时若字段名不对,需要回此 issue 同步真名。

前端按 #2586 接入合同/保险 v3 完成(commit 08359913),但 changelog 未列 VO 完整字段表与若干字段约束,前端目前用「宽容渲染 + 占位字段名」过渡,需后端补齐契约。 ## 一、待确认 ### 1) ContractVO 完整字段 `GET /v3/admin/contract/list?orderId=` 出参 list 元素字段未列。前端当前读:`schemeName / version / contractNo / status / signedAt / signUrl / pdfUrl`(沿用老 mock 名)。请确认真实字段名(特别是 `contractNo` 是否就叫这个 / `pdfUrl` 还是 `contractFileUrl`)。 ### 2) InsuranceOrderVO 完整字段 同上,`GET /v3/admin/insurance/orders` 出参字段未列。前端当前读:`productName / policyNo / status / amount / coveragePeriod / appliedAt / policyUrl`。 ### 3) ContractSchemeVO / InsuranceSchemeVO 字段 方案下拉用,前端只用 `id / name`,确认即可;如有 `disabled` / `enabled` 之类的状态字段也请列。 ### 4) `createContractByScheme` / `purchaseInsurance` body 完整字段 changelog 写 `{orderId, schemeId, ...}` 省略号未展开。请列必填/选填全字段(特别是手动投保是否需要前端传 travelerIds / coverageStartDate 等)。 ### 5) `triggeredEvents` 是否稳定契约字段 `POST /v3/admin/order/{id}/transition` 同步返回的 `triggeredEvents: ["ASYNC_CONTRACT_GENERATE", "ASYNC_INSURANCE_ISSUE"]` 是约定字段吗?前端按 changelog 描述直接 parse 这个数组,命中则 toast 异步生成提示。如果是临时调试字段需要换其他识别方式。 ### 6) OrderMainVO 6 个新 tab 状态字段 前端 v3Adapter 假设 OrderMainVO 包含 `contractStatus / insuranceStatus / refundStatus / hasRefund / hasServiceStandard / hasFinanceDetail` 用于侧边 Tab 红点判断,但 §1.2 当前 OrderMainVO 字段表未列。请确认是否已加 / 计划加 / 走另一接口聚合。 ## 二、低优 / 仅文档建议 - 投保按钮置灰条件(orderStatus + payStatus 白名单)现在写死在前端,能否将白名单也作为业务码 540022 的 message 附带返回前端,便于后端规则变更时前端零改动? ## 阻塞情况 不阻塞:前端已用宽容渲染上线,等真测时按真实字段名微调。仅在前端联调时若字段名不对,需要回此 issue 同步真名。
协作者

字段实证(基于 dev-v3 HEAD 后端代码)

逐条回复,所有字段名按 com.hulalv.{module}.vo.* 真实代码核验。


1️⃣ ContractVO 字段映射 + 完整清单

接口GET /v3/admin/contract/listGET /v3/admin/contract/by-order/{orderId}GET /v3/admin/contract/active-by-order/{orderId}

字段映射(前端假设 → 真名)

前端假设 后端真实字段
schemeName 不存在;用 templateName
version 不存在
contractNo contractNumber
status status (+ statusLabel 中文)
signedAt 不存在;签署时间从 ContractDetailVO.statusLogs[] 取最后一条 SIGNED 状态时间,或用 createTime 占位
signUrl signUrl
pdfUrl fileUrl

ContractVO 完整 27 字段

contractId / orderId / schemeId / orderNo / templateCode / templateName / contractNumber / platform / contractType / contractTypeLabel / mode / modeLabel / status / statusLabel / signUrl / qrCodeUrl / fileUrl / agencyCode / travelAgencyName / destination / departureDate / returnDate / totalAmount / touristCount / contactName / contactPhone / createTime

ContractDetailVO(GET /v3/admin/contract/{id})= ContractVO + 3 字段

  • supplementaryClause(补充约定)
  • travelers[](出行人列表:travelerId/name/idCardType/idCardTypeLabel/idCardNo/phone/isSigner,证件号和手机号是明文,由前端处理脱敏展示)
  • statusLogs[](状态变更日志)

2️⃣ InsuranceOrderVO 字段映射 + 完整清单

接口GET /v3/admin/insurance/ordersGET /v3/admin/insurance/orders/by-order/{orderId}

字段映射

前端假设 后端真实字段
productName productName
policyNo extPolicyNo(推荐)
status status (+ statusLabel 中文)
amount premium
coveragePeriod coverageStartDate + coverageEndDate
appliedAt createTime
policyUrl policyPdfUrl

⚠️ policyNo 字段虽然还在(兼容字段,与 extPolicyNo 同值),但 ApiModelProperty 已标注"后续版本将废弃,前端请改用 extPolicyNo"。新代码请用 extPolicyNo

InsuranceOrderVO 完整 16 字段

insuranceOrderId / orderId / planId / extOrderNo / extPolicyNo / policyNo(deprecated) / productName / planName / premium / insuredCount / coverageStartDate / coverageEndDate / status / statusLabel / policyPdfUrl / createTime

InsuranceOrderDetailVO(GET /v3/admin/insurance/orders/{id})= 上面 + 补丁字段

schemeId / schemeName / insuranceProductId / productId(v1兼容) / totalPremium(=premium v1兼容) / startDate/endDate(v1兼容) / policyHolderName / entityCode / insuredPersons[] / coverages[] / remark / thirdPartyPolicyId / updateTime


3️⃣ Scheme VO 字段(下拉用)

ContractSchemeVO(合同方案下拉):

字段 类型 说明
schemeId String 方案 ID(⚠️ 字符串,不是 Long)
name String 方案名称
description String 方案描述
contractPlatform String 12301 / LOCAL / TENCENT_ESIGN
contractTemplateName String 模板名称(回显)
status String ACTIVE / INACTIVE(用这个判可用)

完整字段另含:vendorCode / channel / contractTemplateCode / templateCode / contractMode / signatoryMode / agencyCode / agencyName / supplementaryClause / transactorName / transactorPhone / sortOrder / createTime

InsuranceSchemeVO(保险方案下拉):

字段 类型 说明
schemeId Long 方案 ID(⚠️ Long 类型,与 contract 不一致)
schemeName String 方案名称(推荐)
name String 同 schemeName,兼容字段
description String 方案描述
enabled Boolean 是否启用
status String ACTIVE / INACTIVE(与 enabled 并存,含义相同)
autoInsure Boolean 是否自动投保
isOverseas Boolean 是否境外
totalDays Integer 适用行程天数
insuranceProductId Long 保险产品 ID
productName String 保险产品名称
planId Long 保险计划 ID
planName String 保险计划名称
segments[] List 适用行程段列表

完整字段另含:sortOrder / adminId / segmentCount / createTime / updateTime

⚠️ 类型不一致:contract 的 schemeIdString,insurance 的 schemeIdLong。v3Adapter 注意。


4️⃣ 创建合同 / 投保 入参完整字段

POST /v3/admin/contract/create-by-scheme

CreateContractBySchemeRequest只有 2 个字段):

{
  "orderId": 1001,
  "schemeId": 1
}

两个都必填。后端从订单 + 方案自动装配出行人 / 日期 / 联系人 / 合同金额等所有细节。

如果要前端手动填全部细节,走 POST /v3/admin/contract/createCreateContractRequest40+ 字段:templateCode / destination / routeName / departureDate / returnDate / signatoryName / signatoryPhone / signatoryIdNumber / contactName / contactPhone / totalAmount / adultCost / childCost / travelers[] 等)。

POST /v3/admin/insurance/purchase(手动投保)

PurchaseInsuranceRequest

字段 必填 类型 说明
orderId Long 订单 ID
schemeId Long 方案 ID(优先使用;为空则用 productId+planId)
insuranceProductId 条件 Long schemeId 为空时必填
planId 条件 Long schemeId 为空时必填
coverageStartDate LocalDate 为空时按订单 startDate 自动填
coverageEndDate LocalDate 为空时按订单 endDate 自动填
insuredPersons[] List 被保人列表(不是 travelerIds),为空时按订单出行人自动填
remark String 备注

InsuredPersonItem 子结构:

  • name(必填)
  • idCardType(必填,ID_CARD / PASSPORT / OTHER,JsonAlias 兼容 idType
  • idCardNo(必填,JsonAlias 兼容 idNo
  • birthday(可选)
  • phone(可选)

POST /v3/admin/insurance/purchase-by-scheme(按方案自动投保)

AutoPurchaseRequest只有 2 字段):

{
  "orderId": 1001,
  "schemeId": 100
}

📌 回答 issue #4 的具体问题:手动投保不需要传 travelerIds,需要传完整 insuredPersons[](或留空让后端按订单出行人自动填)。coverageStartDate / coverageEndDate 也是可选,留空走订单日期。


5️⃣ triggeredEvents 是稳定契约字段

接口POST /v3/admin/order/{id}/transition

返回 VOOrderTransitionRespVO

字段 类型 说明
success Boolean 是否成功
oldStatus / newStatus String 变更前后粗状态(中文,如"待出行")
oldFlowStatus / newFlowStatus String 变更前后细状态(中文)
triggeredEvents List<String> 副作用事件列表,例如 ["ASYNC_CONTRACT_GENERATE","ASYNC_INSURANCE_ISSUE"]

triggeredEvents@ApiModelProperty Swagger 文档 + example,是稳定契约字段,不是临时调试字段

前端 parse 这个数组做 toast 提示(如"合同正在异步生成")是合理设计,可以放心依赖。新增的事件枚举会在 changelog 里同步。


6️⃣ OrderMainVO 6 个 tab 状态字段 — 全部已存在

接口:订单详情 main Tab(具体路径在 OrderDetail Controller,需要时补对照)

OrderMainVO 已有的 6 个 tab 字段

字段 类型 取值 / 语义
contractStatus String NONE / GENERATING / GENERATED / SIGNED / VOIDED / RESIGNING(直接读主表 contractStatus)
insuranceStatus String NONE / ISSUING / ISSUED / CANCELLED / FAILED(直接读主表 insuranceStatus)
refundStatus String NONE / PROCESSING / COMPLETED(派生,见 OrderMainRefundStatus)
hasRefund Boolean refundedAmount > 0
hasServiceStandard Boolean order_product_snapshot 存在且非空
hasFinanceDetail Boolean discountAmount > 0 或 surchargeAmount > 0(派生,无 SQL)

前端 v3Adapter 直接读这 6 个字段做 tab 红点判断即可,不需要走另一接口聚合

另外 OrderMainVO 还有一个相关字段:

  • exceptionBadges(Map<String, Boolean>):异常态横条 9 类标识,key 包括 contractFail / insuranceFail / refundAbnormal / grabTimeout / hotelPending / vehiclePending / travelerIncomplete / longUnpaid / awaitingCustomerConfirm(用于"业务异常徽标",跟 6 个 tab 状态字段语义不同)。

7️⃣ 关于 540022 携带白名单的建议

合理但不在本 issue 范围,需要后端动错误码定义。当前 message 是写死字符串:

"请先确认订单后再配置保险(当前订单状态不允许投保)"

未带白名单状态。我会另起一张工单跟进(错误码语义增强)。


后续动作

后端这边会另起一个 changelog 文件 changelogs-v2/2026-05/{今天日期}_*_合同保险VO字段补全-修改接口-管理后台.md,把上面 6 节全字段表自包含归档。


本 issue 不阻塞前端联调:按上面 6 个映射表改 v3Adapter,可直接对接真实接口。

## 字段实证(基于 dev-v3 HEAD 后端代码) 逐条回复,所有字段名按 `com.hulalv.{module}.vo.*` 真实代码核验。 --- ### 1️⃣ ContractVO 字段映射 + 完整清单 **接口**:`GET /v3/admin/contract/list`、`GET /v3/admin/contract/by-order/{orderId}`、`GET /v3/admin/contract/active-by-order/{orderId}` **字段映射(前端假设 → 真名)**: | 前端假设 | 后端真实字段 | |---|---| | `schemeName` | ❌ 不存在;用 `templateName` | | `version` | ❌ 不存在 | | `contractNo` | `contractNumber` | | `status` | `status` ✅(+ `statusLabel` 中文) | | `signedAt` | ❌ 不存在;签署时间从 `ContractDetailVO.statusLogs[]` 取最后一条 `SIGNED` 状态时间,或用 `createTime` 占位 | | `signUrl` | `signUrl` ✅ | | `pdfUrl` | `fileUrl` | **ContractVO 完整 27 字段**: `contractId / orderId / schemeId / orderNo / templateCode / templateName / contractNumber / platform / contractType / contractTypeLabel / mode / modeLabel / status / statusLabel / signUrl / qrCodeUrl / fileUrl / agencyCode / travelAgencyName / destination / departureDate / returnDate / totalAmount / touristCount / contactName / contactPhone / createTime` **ContractDetailVO(GET /v3/admin/contract/{id})= ContractVO + 3 字段**: - `supplementaryClause`(补充约定) - `travelers[]`(出行人列表:travelerId/name/idCardType/idCardTypeLabel/idCardNo/phone/isSigner,证件号和手机号是明文,由前端处理脱敏展示) - `statusLogs[]`(状态变更日志) --- ### 2️⃣ InsuranceOrderVO 字段映射 + 完整清单 **接口**:`GET /v3/admin/insurance/orders`、`GET /v3/admin/insurance/orders/by-order/{orderId}` **字段映射**: | 前端假设 | 后端真实字段 | |---|---| | `productName` | `productName` ✅ | | `policyNo` | `extPolicyNo`(推荐) | | `status` | `status` ✅(+ `statusLabel` 中文) | | `amount` | `premium` | | `coveragePeriod` | 拆 `coverageStartDate` + `coverageEndDate` | | `appliedAt` | `createTime` | | `policyUrl` | `policyPdfUrl` | ⚠️ **policyNo 字段虽然还在**(兼容字段,与 `extPolicyNo` 同值),但 ApiModelProperty 已标注"后续版本将废弃,前端请改用 extPolicyNo"。**新代码请用 `extPolicyNo`**。 **InsuranceOrderVO 完整 16 字段**: `insuranceOrderId / orderId / planId / extOrderNo / extPolicyNo / policyNo(deprecated) / productName / planName / premium / insuredCount / coverageStartDate / coverageEndDate / status / statusLabel / policyPdfUrl / createTime` **InsuranceOrderDetailVO(GET /v3/admin/insurance/orders/{id})= 上面 + 补丁字段**: `schemeId / schemeName / insuranceProductId / productId(v1兼容) / totalPremium(=premium v1兼容) / startDate/endDate(v1兼容) / policyHolderName / entityCode / insuredPersons[] / coverages[] / remark / thirdPartyPolicyId / updateTime` --- ### 3️⃣ Scheme VO 字段(下拉用) **ContractSchemeVO**(合同方案下拉): | 字段 | 类型 | 说明 | |---|---|---| | **`schemeId`** | **String** | 方案 ID(⚠️ 字符串,不是 Long) | | `name` | String | 方案名称 | | `description` | String | 方案描述 | | `contractPlatform` | String | `12301` / `LOCAL` / `TENCENT_ESIGN` | | `contractTemplateName` | String | 模板名称(回显) | | **`status`** | **String** | `ACTIVE` / `INACTIVE`(用这个判可用) | 完整字段另含:`vendorCode / channel / contractTemplateCode / templateCode / contractMode / signatoryMode / agencyCode / agencyName / supplementaryClause / transactorName / transactorPhone / sortOrder / createTime` **InsuranceSchemeVO**(保险方案下拉): | 字段 | 类型 | 说明 | |---|---|---| | **`schemeId`** | **Long** | 方案 ID(⚠️ Long 类型,与 contract 不一致) | | `schemeName` | String | 方案名称(推荐) | | `name` | String | 同 schemeName,兼容字段 | | `description` | String | 方案描述 | | **`enabled`** | **Boolean** | 是否启用 | | **`status`** | **String** | `ACTIVE` / `INACTIVE`(与 enabled 并存,含义相同) | | `autoInsure` | Boolean | 是否自动投保 | | `isOverseas` | Boolean | 是否境外 | | `totalDays` | Integer | 适用行程天数 | | `insuranceProductId` | Long | 保险产品 ID | | `productName` | String | 保险产品名称 | | `planId` | Long | 保险计划 ID | | `planName` | String | 保险计划名称 | | `segments[]` | List | 适用行程段列表 | 完整字段另含:`sortOrder / adminId / segmentCount / createTime / updateTime` ⚠️ **类型不一致**:contract 的 `schemeId` 是 **String**,insurance 的 `schemeId` 是 **Long**。v3Adapter 注意。 --- ### 4️⃣ 创建合同 / 投保 入参完整字段 #### `POST /v3/admin/contract/create-by-scheme` `CreateContractBySchemeRequest`(**只有 2 个字段**): ```json { "orderId": 1001, "schemeId": 1 } ``` 两个都必填。后端从订单 + 方案自动装配出行人 / 日期 / 联系人 / 合同金额等所有细节。 如果要前端手动填全部细节,走 `POST /v3/admin/contract/create` → `CreateContractRequest`(**40+ 字段**:templateCode / destination / routeName / departureDate / returnDate / signatoryName / signatoryPhone / signatoryIdNumber / contactName / contactPhone / totalAmount / adultCost / childCost / travelers[] 等)。 #### `POST /v3/admin/insurance/purchase`(手动投保) `PurchaseInsuranceRequest`: | 字段 | 必填 | 类型 | 说明 | |---|---|---|---| | `orderId` | ✅ | Long | 订单 ID | | `schemeId` | ❌ | Long | 方案 ID(优先使用;为空则用 productId+planId) | | `insuranceProductId` | 条件 | Long | schemeId 为空时必填 | | `planId` | 条件 | Long | schemeId 为空时必填 | | `coverageStartDate` | ❌ | LocalDate | 为空时按订单 startDate 自动填 | | `coverageEndDate` | ❌ | LocalDate | 为空时按订单 endDate 自动填 | | `insuredPersons[]` | ❌ | List | 被保人列表(**不是 travelerIds**),为空时按订单出行人自动填 | | `remark` | ❌ | String | 备注 | `InsuredPersonItem` 子结构: - `name`(必填) - `idCardType`(必填,`ID_CARD` / `PASSPORT` / `OTHER`,JsonAlias 兼容 `idType`) - `idCardNo`(必填,JsonAlias 兼容 `idNo`) - `birthday`(可选) - `phone`(可选) #### `POST /v3/admin/insurance/purchase-by-scheme`(按方案自动投保) `AutoPurchaseRequest`(**只有 2 字段**): ```json { "orderId": 1001, "schemeId": 100 } ``` 📌 **回答 issue #4 的具体问题**:手动投保**不需要传 travelerIds**,需要传完整 `insuredPersons[]`(或留空让后端按订单出行人自动填)。`coverageStartDate / coverageEndDate` 也是可选,留空走订单日期。 --- ### 5️⃣ `triggeredEvents` 是稳定契约字段 ✅ **接口**:`POST /v3/admin/order/{id}/transition` **返回 VO**:`OrderTransitionRespVO` | 字段 | 类型 | 说明 | |---|---|---| | `success` | Boolean | 是否成功 | | `oldStatus` / `newStatus` | String | 变更前后粗状态(中文,如"待出行") | | `oldFlowStatus` / `newFlowStatus` | String | 变更前后细状态(中文) | | **`triggeredEvents`** | List\<String\> | 副作用事件列表,例如 `["ASYNC_CONTRACT_GENERATE","ASYNC_INSURANCE_ISSUE"]` | `triggeredEvents` 带 `@ApiModelProperty` Swagger 文档 + example,**是稳定契约字段,不是临时调试字段**。 前端 parse 这个数组做 toast 提示(如"合同正在异步生成")是合理设计,可以放心依赖。新增的事件枚举会在 changelog 里同步。 --- ### 6️⃣ OrderMainVO 6 个 tab 状态字段 — 全部已存在 ✅ **接口**:订单详情 main Tab(具体路径在 OrderDetail Controller,需要时补对照) **OrderMainVO 已有的 6 个 tab 字段**: | 字段 | 类型 | 取值 / 语义 | |---|---|---| | `contractStatus` | String | `NONE` / `GENERATING` / `GENERATED` / `SIGNED` / `VOIDED` / `RESIGNING`(直接读主表 contractStatus) | | `insuranceStatus` | String | `NONE` / `ISSUING` / `ISSUED` / `CANCELLED` / `FAILED`(直接读主表 insuranceStatus) | | `refundStatus` | String | `NONE` / `PROCESSING` / `COMPLETED`(派生,见 OrderMainRefundStatus) | | `hasRefund` | Boolean | `refundedAmount > 0` | | `hasServiceStandard` | Boolean | `order_product_snapshot` 存在且非空 | | `hasFinanceDetail` | Boolean | `discountAmount > 0 或 surchargeAmount > 0`(派生,无 SQL) | 前端 v3Adapter 直接读这 6 个字段做 tab 红点判断即可,**不需要走另一接口聚合**。 另外 OrderMainVO 还有一个相关字段: - `exceptionBadges`(Map\<String, Boolean\>):异常态横条 9 类标识,key 包括 `contractFail` / `insuranceFail` / `refundAbnormal` / `grabTimeout` / `hotelPending` / `vehiclePending` / `travelerIncomplete` / `longUnpaid` / `awaitingCustomerConfirm`(用于"业务异常徽标",跟 6 个 tab 状态字段语义不同)。 --- ### 7️⃣ 关于 540022 携带白名单的建议 合理但**不在本 issue 范围**,需要后端动错误码定义。当前 message 是写死字符串: ``` "请先确认订单后再配置保险(当前订单状态不允许投保)" ``` 未带白名单状态。我会另起一张工单跟进(错误码语义增强)。 --- ## 后续动作 后端这边会另起一个 changelog 文件 `changelogs-v2/2026-05/{今天日期}_*_合同保险VO字段补全-修改接口-管理后台.md`,把上面 6 节全字段表自包含归档。 --- **本 issue 不阻塞前端联调**:按上面 6 个映射表改 v3Adapter,可直接对接真实接口。
wx2026-06-05 16:05:41 +08:00 关闭此工单
登录 并参与到对话中。
未选择标签
2 名参与者
通知
到期时间
未设置到期时间。
依赖工单

没有设置依赖项。

参考:wx/hl-api-changelog#3
没有提供说明。