---
date: 2026-05-07
type: backend-fix
module: hl-order-service-v2/insurance
priority: high
notify: ["@mmg"]
status: deployed-pending
restart_service: hl-order-service-v2
gitea_pr: 1805
gitea_issue: 1789
related_cases: [BD-001]
breaking_change: false
---

# 投保人证件号字段名改回 V1 契约 + JsonAlias 兼容（保险订单创建修复）

## 现象（BD-001）

```
POST /admin/insurance/purchase
Body: {"insuredList":[{"name":"张三","idType":"ID_CARD","idNo":"110101...",...}],...}
响应: {"code":400,"message":"证件类型不能为空, 证件号码不能为空"}
```

即使前端填了证件号也报必填。

## 根因

V2 重写时单方面把 `PurchaseInsuranceRequest` 的 `idCardType / idCardNo` 改名成 `idType / idNo`。前端按 V1 契约持续传 `idCardType / idCardNo`，Jackson `FAIL_ON_UNKNOWN_PROPERTIES=false` 静默丢字段，双 @NotNull 触发必填。

## 修复

PR #1805 已合并 dev。

1. **DTO 改回 V1 字段名**：`PurchaseInsuranceRequest.idType → idCardType`，`idNo → idCardNo`
2. **加 @JsonAlias 双向兼容**：`@JsonAlias({"idType"})` / `@JsonAlias({"idNo"})` —— 两种字段名都能反序列化
3. service 层 7 处 getter/setter 引用全部跟改（含 PR #1800 拆出的 InsurancePurchaseTxService）

## 前端调用（@mmg）

### 推荐写法（两种都可，建议用 V1 字段名）

```js
// ✅ 推荐：V1 契约字段名
POST /admin/insurance/purchase
{
  "schemeId": "2046840448906940417",
  "orderId": "...",
  "insuredList": [
    {
      "name": "张三",
      "idCardType": "ID_CARD",
      "idCardNo": "110101199001011234",
      "phone": "13800138000",
      "birthday": "1990-01-01",
      "relationship": "SELF"
    }
  ]
}

// ⚠️ 兼容（@JsonAlias 兜底，但建议改用上面的）
{
  "insuredList": [
    {"idType": "ID_CARD", "idNo": "...", ...}
  ]
}
```

### 重要：同时传新旧字段时的优先级

如果前端代码同时传了 `idCardType` 和 `idType`（不推荐），Jackson 用最后出现的覆盖。建议**只传一种**，统一改用 V1 字段名 `idCardType / idCardNo` 跟项目其他 traveler DTO 对齐。

### idCardType 枚举值

```
ID_CARD       身份证
PASSPORT      护照
HONG_KONG     港澳通行证
TAIWAN        台湾通行证
OTHER         其他
```

### 字段加密提醒

`InsuredPerson.idCardNo` 已挂 `EncryptTypeHandler`，落库自动加密、查询自动解密，前端无感。日志全部已脱敏（grep 31 处零打印明文）。

## 重启服务

`hl-order-service-v2` 部署测试服后即可。

## 验收

- [ ] 用 `idCardType / idCardNo` 字段名 POST → 返 code:200 创建保险订单
- [ ] 用 `idType / idNo`（旧字段名兜底）POST → 同样 code:200（@JsonAlias 兼容）
- [ ] 缺失证件号 → 返必填提示
- [ ] DB 中 idCardNo 是加密格式（解密查 InsuredPersonVO 是明文）

## 关联工单
Gitea #1789 已 Closes 关闭。

## 备注
本次修复保留 V2 内部其他字段（V2 重写期间引入的合理改动），仅恢复**对外契约字段名**。前端零改动也能调通（@JsonAlias 兜底），但建议主动改回 V1 字段名以保持代码清晰。
