# 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` | 字段 | 类型 | 说明 | |------|------|------| | successCount | Integer | 成功解析并落库的数量(dryRun=true 时为"预期会落库的数量") | | failCount | Integer | 失败片段数量 | | successList | List | 成功的出行人列表(返脱敏后的预览字段,**不返明文**) | | failures | List | 失败片段列表 | | failures[].maskedSnippet | String | 失败片段(idNo 前6后4、phone 前3后4 脱敏) | | failures[].reason | String | 失败原因(中文) | #### 请求示例 ```json { "rawText": "张三 110101199001011234 13800138000\n李四 身份证: 320101198502028765 手机 13900139000", "dryRun": false } ``` #### 响应示例 ```json { "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 } ``` #### 失败片段响应示例 ```json { "code": 200, "data": { "successCount": 1, "failCount": 1, "successList": [...], "failures": [ { "maskedSnippet": "王五 1101******1234 1380***0000", "reason": "身份证号校验位错误" } ] }, "success": true } ``` #### 错误响应 ```json { "code": 581131, "message": "原文本过长,最多 5000 字符", "success": false } ``` ```json { "code": 581132, "message": "原文本为空或无法识别任何出行人", "success": false } ``` ```json { "code": 581134, "message": "订单当前状态不允许批量导入出行人", "success": false } ``` ```json { "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](https://git.1814.love:8443/wx/HL/issues/2526) - 关联 PR: [wx/HL#2554](https://git.1814.love:8443/wx/HL/pulls/2554) - commit: `f8a34607` - 设计文档: `docs/order-v3/V5.48 §2.8 智能批量解析`