hl-api-changelog/changelogs-v2/2026-05/18_#2526_traveler-smart-parse.md
API Changelog Bot d366a0790c changelog(order-v3): 出行人 3 接口 (#2525/#2526/#2527)
- #2525 Feign /internal/order/orders/{orderId}/travelers + 解密审计表 + 589100/589101
- #2526 smart-parse 智能批量解析 + 581131-581134 + P0 审计红线 5 合规
- #2527 validate 字段对齐 v2(6→4 必填) + 581102 复用

测试服 9443 真测全  (commit f8a34607,含 31 Flyway migrations)
#2525 经 SSH 内网 8086 真测(internal 路径 9443 返 403 是预期)
#2526 异常路径全过,正常落库待前端联调补做(代码层 IT 已覆盖)

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

7.7 KiB

order-v3 出行人模块: 新增智能批量解析接口 smart-parse

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

服务: hl-order-v3 (端口 8086) PR: #2554 Issue: #2526 日期: 2026-05-18 影响范围: 管理后台订单详情"批量导入出行人"功能 → 新增接口,前端必新对接


⚠️ 关键变化

  • 新增接口 POST /v3/admin/order/{id}/traveler/smart-parse,前端需新对接,不是改造现有接口
  • 支持 dryRun=true 预览解析结果不落库,dryRun=false 真正写入。
  • P0 审计安全 5 红线全合规:不挂 @OperationLog 避免明文落审计 / failures[].maskedSnippet 脱敏 / 限流 / Lock4j / status_log 仅写聚合统计不写明文。

一、背景

V5.48 §2.8 定义"智能批量解析"。客服收到客户微信发来的一大段文字(姓名+身份证+手机号混排),要求自动解析后批量录入出行人。

风险点 = 审计安全:身份证/手机号是 P0 敏感字段,若任何中间环节(@OperationLog / 日志 / status_log)落了明文,即一次安全事故。本接口的关键设计就是 正常落库走加密 TypeHandler,失败片段返脱敏


二、变更接口清单

# 接口 方法 路径 变更类型 说明
1 智能批量解析出行人 POST /v3/admin/order/{id}/traveler/smart-parse 新增 前端必新对接

三、接口详情

1. 智能批量解析 POST /v3/admin/order/{id}/traveler/smart-parse

VO: TravelerSmartParseReqVO / TravelerSmartParseRespVO

入参

字段 位置 类型 必填 约束 说明
id Path Long 雪花 ID 订单 ID
rawText Body String 非空,长度 ≤ 5000 客户原始文本(姓名+身份证+手机号混排)
dryRun Body Boolean 默认 false true=只预览不落库,false=真正写入

出参 Result<TravelerSmartParseRespVO>

字段 类型 说明
successCount Integer 成功解析并落库的数量(dryRun=true 时为"预期会落库的数量")
failCount Integer 失败片段数量
successList List 成功的出行人列表(返脱敏后的预览字段,不返明文)
failures List 失败片段列表
failures[].maskedSnippet String 失败片段(idNo 前6后4、phone 前3后4 脱敏)
failures[].reason String 失败原因(中文)

请求示例

{
  "rawText": "张三 110101199001011234 13800138000\n李四 身份证: 320101198502028765 手机 13900139000",
  "dryRun": false
}

响应示例

{
  "code": 200,
  "message": "成功",
  "data": {
    "successCount": 2,
    "failCount": 0,
    "successList": [
      { "travelerId": 9023400777, "name": "张三", "idNo": "110101******1234", "phone": "138****8000" },
      { "travelerId": 9023400778, "name": "李四", "idNo": "320101******8765", "phone": "139****9000" }
    ],
    "failures": []
  },
  "success": true
}

失败片段响应示例

{
  "code": 200,
  "data": {
    "successCount": 1,
    "failCount": 1,
    "successList": [...],
    "failures": [
      {
        "maskedSnippet": "王五 1101******1234 1380***0000",
        "reason": "身份证号校验位错误"
      }
    ]
  },
  "success": true
}

错误响应

{ "code": 581131, "message": "原文本过长,最多 5000 字符", "success": false }
{ "code": 581132, "message": "原文本为空或无法识别任何出行人", "success": false }
{ "code": 581134, "message": "订单当前状态不允许批量导入出行人", "success": false }
{ "code": 100501, "message": "请求过于频繁,请稍后再试", "success": false }

四、契约约束

约束 说明
dryRun=true 仅解析返回预览,不落 DB,不写 status_log,不锁订单
dryRun=false 同事务批量 insert 出行人 + 写 status_log + 加 @Lock4j 锁订单
@Lock4j key=#id,expire=30000ms,同订单串行
@RateLimiter count=10, time=60(每用户 60s 内最多 10 次)
订单状态 仅"待出行/已支付"等状态允许,其它 → 581134
文本长度 > 5000 字符 → 581131
无可解析片段 整段 0 个能识别 → 581132

五、数据库行为

dryRun=false 写入

  • order_traveler: 批量 insert,idNo/phone 走 EncryptTypeHandler 加密落库
  • order_status_log: 写 1 行 reason="批量导入出行人 N 人"(不含任何明文)

dryRun=true

  • 无任何写操作

失败片段

  • 不落 DB
  • 仅出现在响应 failures[],带脱敏 snippet

六、P0 审计安全红线(本接口最核心设计)

# 红线 实现
1 不挂 @OperationLog Controller 方法没有此注解,避免 idNo/phone 通过 requestParams 落 audit_log
2 failures[].maskedSnippet 脱敏 idNo 前 6 后 4(110101******1234),phone 前 3 后 4(138****8000)
3 日志仅聚合统计 log.info("smart-parse orderId={} successCount={} failCount={}"),绝不打印 rawText / idNo / phone
4 限流 @RateLimiter(count=10, time=60),防御暴力扫描
5 status_log 不写明文 reason 只写 "批量导入出行人 N 人",不带任何字段值

七、边界行为

  • 未登录 → 401(网关)
  • 订单不存在 → 404
  • 订单状态非法 → 581134
  • 限流触发 → 100501(框架统一限流码,非 581133)
  • rawText 含全空白 → 581132
  • 部分成功部分失败 → 200,在 successList / failures 中体现

八、不影响范围

  • 仅影响: 管理后台订单详情页"批量导入出行人"入口
  • 零影响:
    • 现有 /v3/admin/order/{id}/traveler/add(单人新增)
    • 现有 /v3/admin/order/{id}/traveler/edit(批量编辑)
    • 现有 /v3/admin/order/{id}/traveler/validate(完整性校验,见 #2527)
    • 现有 /v3/internal/order/orders/{orderId}/travelers(Feign 解密,见 #2525)
    • 订单创建/详情/列表
    • 小程序所有接口

九、测试环境已验证

经 9443 网关真 admin token 测试:

POST /v3/admin/order/{id}/traveler/smart-parse  rawText 超 5000 字符
→ 581131 SMART_PARSE_RAW_TEXT_TOO_LONG ✓

POST /v3/admin/order/{id}/traveler/smart-parse  订单状态不允许
→ 581134 SMART_PARSE_ORDER_STATUS_FORBID ✓

POST /v3/admin/order/{id}/traveler/smart-parse  连续 11 次调用
→ 11 次返 100501 限流 ✓

POST /v3/admin/order/{id}/traveler/smart-parse  正常 dryRun=true
→ ⚠️ 测试服无 PENDING 订单,正常路径未在 9443 真测
→ 代码层 UnitTest + IntegrationTest 已覆盖
→ **前端联调阶段补做正常路径回归**

十、错误码段位说明(段位让位,与文档不一致)

V5.48 §2.8 文档期望段位被 baseline 已占用,本 PR 实际段位如下,调用方按本表为准:

错误码 含义 文档期望 实际 原因
581131 SMART_PARSE_RAW_TEXT_TOO_LONG 581120 581131 581120 已被 TRANSPORT_PLAN_NOT_FOUND 占用
581132 SMART_PARSE_RAW_TEXT_EMPTY_OR_UNPARSABLE 581121 581132 同段位让位
581133 SMART_PARSE_RATE_LIMITED 581122 doc-only(实际抛 100501) 框架统一限流码
581134 SMART_PARSE_ORDER_STATUS_FORBID 581123 581134 同段位让位

后续文档侧 follow-up 修正文档段位与代码对齐(本 PR 不改文档)。


十一、相关文档

  • 关联 Issue: wx/HL#2526
  • 关联 PR: wx/HL#2554
  • commit: f8a34607
  • 设计文档: docs/order-v3/V5.48 §2.8 智能批量解析