--- schema: "hl-changelog/v2" ticket: "5463" title: "派单司机/员工手机号密文解密失败降级容错" consumer: "admin" change_type: "修改接口" backend_status: "deployed" gateway_status: "verified" frontend_status: "verified" frontend_owner: "Pi" frontend_ref: "hl-admin@4c8bdb97c89fa399e5fbb13240ea082f64fefc59" target_release: "v2.1" verified_at: "2026-08-04" status_note: "订单详情/行程接口对手机号密文解密失败(keyVersion 缺失/密文损坏)不再 500,降级返回空手机号;后端 warn 日志含 keyVersion 与密文指纹,不含明文。" updated_at: "2026-08-04" base: "dev-v3" --- # 【修改接口·管理后台】派单司机/员工手机号密文解密失败降级容错 (#5463) > **PR**: #5464 | **服务**: order-v3 | **更新时间**: 2026-08-04 ## 1. 接口背景 订单详情/行程接口对含派单司机/员工手机号密文的记录,在密钥版本缺失或密文损坏时返回 500(`assignment phone key version is unavailable`),用户端订单详情页直接报错不可用。本次改为解密失败降级:仅手机号字段返回空,订单其余字段正常展示。 ## 变更接口清单 | # | 接口名 | 方法 | 路径 | 变更类型 | 说明 | |---|--------|------|------|----------|------| | 1 | 订单行程(v2 详情页) | GET | `/v3/admin/order/{id}/itinerary` | 修改 | 手机号解密失败不再 500,降级为空展示 | | 2 | 订单详情(9 Tab 聚合) | GET | `/v3/admin/order/{id}` | 修改 | 车辆/人员手机号解密失败不再 500,降级为空展示 | ## 2. 行为变化 | 场景 | 原来 | 现在 | |------|------|------| | 派单/员工手机号密文 keyVersion 缺失(如配置未下发) | 整接口 500 | 200,手机号字段为 `null`/空,其余字段正常 | | 密文损坏 / Base64 非法 / AAD 不匹配 | 整接口 500 | 200,手机号字段为 `null`/空,其余字段正常 | | 手机号无密文(明文快照或空) | 正常展示 | 不变 | | 密钥配置齐全且密文正常 | 正常解密展示 | 不变 | **涉及字段**(响应中车辆/人员相关节点): | 字段 | 类型 | 变化 | |------|------|------| | 车辆行 `driverPhone`(订单行程/详情车辆列表) | string | 解密失败时为 `null`(原来导致整接口 500) | | 签收凭证 `driverPhone` / `guidePhone` / `leaderPhone` | string | 解密失败时为 `null`(原来导致整接口 500) | ## 3. 接口详情 ### 3.1 订单行程 - **方法/路径**:`GET /v3/admin/order/{id}/itinerary` - **认证**:管理后台登录态(定制师/车务均可) - **无请求体**;`id` 为订单雪花 ID。 - **典型成功示例**(手机号解密失败时,其余字段完整返回): ``` GET /v3/admin/order/2084280253366124546/itinerary Authorization: Bearer (无请求体) ``` ```json { "code": 200, "message": "成功", "data": { "orderId": "2084280253366124546", "orderNo": "HL20260803220000001", "vehicles": [ { "assignmentId": "2084280300000000001", "driverName": "张师傅", "driverPhone": null, "licensePlate": "蒙A-12345", "serviceStartDate": "2026-08-03", "serviceEndDate": "2026-08-05" } ] } } ``` ### 3.2 订单详情 - **方法/路径**:`GET /v3/admin/order/{id}` - **行为**:与 3.1 一致;聚合内所有来源自 `driver_phone_ciphertext` / `staff_phone_ciphertext` 的手机号字段在解密失败时均为 `null`,不再触发整接口 500。 ## 4. 错误码 无新增错误码。解密失败不再返回 500,改为 200 + 字段 `null`;后端以 warn 日志记录(`assignment phone decrypt degraded: ... keyVersion=... envelopeFingerprint=...`),日志不含明文与密钥。 ## 5. 前端需要做什么 - 无需代码改动即可恢复可用(页面从 500 变为正常打开)。 - 请确认车辆/人员手机号为空(`null`)时页面展示兜底(如显示 `-` 或留空),避免出现 `undefined` 文案。 - 前端遇到 `driverPhone` 为空时,不要向用户提示“数据异常”,按“暂无/未填写”处理即可。 ## 6. 后端测试、部署与网关验证 - 单元测试:`AssignmentPhoneCryptoTest` 10 个(正常解密 / keyVersion 缺失 / 损坏密文 / Base64 非法 / AAD 不匹配 / AAD 身份缺失 / 加密 fail-fast / previous-keys 轮换)全部通过。 - 模块验证:`mvn -pl hl-order-service-v3 -am verify` 7414 测试 0 失败。 - 部署:TEST 环境 hl-order-service-v3 已部署。 - 网关验证:原 500 订单(`2084280253366124546` / `2084280407016062978`)`GET /v3/admin/order/{id}/itinerary` 恢复 200,手机号字段降级为空,其余字段正常。 ## 验证证据 - 单元测试:`AssignmentPhoneCryptoTest` 10 个用例(正常 v1/v2 往返、keyVersion 缺失降级 null、损坏密文降级 null、Base64 非法降级 null、AAD 不匹配降级 null、AAD 身份缺失降级 null、加密 fail-fast、previous-keys 轮换)全通过。 - 模块验证:`mvn -pl hl-order-service-v3 -am verify` 7414 测试 0 失败。 - 部署:TEST 环境 hl-order-service-v3 rolling 部署完成(dev-v3 @ fcde24ec,双实例 UP)。 - 网关验证(wx=CUSTOMIZER):原 500 订单 `2084280253366124546` / `2084280407016062978` / `2084278362947158017` 的 `GET /v3/admin/order/{id}/itinerary` 全部恢复 200;含 v1 密文的订单 `2084266179660013569` 车辆行 `driverPhoneMasked` 正常掩码回显(测试服已补配 v1 密钥,存量 414 条密文可解密;无密钥时走降级路径返回空,由单元测试覆盖)。 ## 关联 / 联系人 ### 联系人 - **后端负责人**: @wx