--- schema: "hl-changelog/v2" ticket: "7537" title: "管理端展示订单号的响应体统一补团号(teamNo)" consumer: "admin" author: "jw(GIT)" change_type: "修改接口" backend_status: "deployed" gateway_status: "verified" frontend_status: "pending" frontend_owner: "mmg" frontend_ref: "" target_release: "" verified_at: "2026-09-12" status_note: "响应体纯增字段,向后兼容:未消费 teamNo 的页面行为完全不变。前端可按需在退款记录、支付流水、协同工单三块列表/详情补一列「团号」。" updated_at: "2026-09-12" base: "dev-v3" --- # order-v3: 管理端展示订单号的响应体统一补团号(teamNo) **服务**: hl-order-service-v3 **PR**: #7564 **Issue**: #7537 --- ## ⚠️ 关键变化 🔴 **`teamNo` 是订单团号,不是运营团期号。** 系统里有两个都被叫作「团号」的字段: | 字段 | 来源 | 格式 | 生成时点 | 本单 | |---|---|---|---|---| | **`teamNo`** | `order_main.team_no` | `26-0001`(4 位) | **订金支付成功** | ✅ 本单返的是这个 | | `batchNo` | `order_group_batch.batch_no` | `GB-26-0001` | 建团期时 | ❌ 本单不返 | 🔴 **`teamNo` 为 `null` 不代表「不是团期单」。** 生成条件是**订金支付成功**,与订单是不是团期单**完全无关**: - 已付订金的**散客单** → **有**团号 - 未付订金的**团期单** → **没有**团号 前端**不要**用 `teamNo != null` 去判断团期单,那会判错两个方向。 🔴 **团号为空时一律是 `null`**,不是空串、更不会回退成订单号。前端按 `null` 判空即可,不必再区分 `""`。 🔴 **同一团期下的每张子订单团号各不相同**(团号是订单级的,不是团期级的)。别按「同团共用一个号」做归并。 --- ## 一、背景 hl-ui 管理后台的**退款记录、支付流水、协同工单**三块列表/详情只显示订单号, 客服要知道团号得另开一个页面按订单号反查;**订单待办**的 `teamNo` 字段虽然早就在契约里, 但分页路径有值、单对象路径(新建/修改/完成/重开的回包)从来没赋过值,**恒为 `null`**, 前端拿到的数据前后不一致。 本单把这四处补齐:三个 VO 新增 `teamNo` 字段,一个 VO 补上从未执行的填充。 **路径、方法、请求参数一律不变**,只在响应体追加(或补填)一个字段,向后兼容纯增。 --- ## 二、变更接口清单 | # | 接口 | 方法 | 路径 | 变更类型 | 说明 | |---|------|------|------|----------|------| | 1 | 退款详情 | GET | `/v3/admin/refund/{refundId}` | 修改 | 响应体新增 `teamNo` | | 2 | 按订单查退款 | GET | `/v3/admin/refund/order/{orderId}/list` | 修改 | 响应体新增 `teamNo` | | 3 | 支付交易分页 | GET | `/v3/admin/payment/list` | 修改 | 响应体新增 `teamNo` | | 4 | 交易详情 | GET | `/v3/admin/payment/{transactionId}` | 修改 | 响应体新增 `teamNo` | | 5 | 按订单查交易 | GET | `/v3/admin/payment/order/{orderId}` | 修改 | 响应体新增 `teamNo` | | 6 | 工单分页 | GET | `/v3/admin/order/work-order/list` | 修改 | 响应体新增 `teamNo` | | 7 | 工单详情 | GET | `/v3/admin/order/work-order/{workOrderId}` | 修改 | 响应体新增 `teamNo` | | 8 | 手动新建待办 | POST | `/v3/admin/order-todos/manual` | 修改 | `teamNo` 字段已存在,本单补上从未赋值的填充 | | 9 | 修改手动待办 | PUT | `/v3/admin/order-todos/{todoId}` | 修改 | `teamNo` 字段已存在,本单补上从未赋值的填充 | | 10 | 完成待办 | PUT | `/v3/admin/order-todos/{todoId}/complete` | 修改 | `teamNo` 字段已存在,本单补上从未赋值的填充 | | 11 | 重开待办 | PUT | `/v3/admin/order-todos/{todoId}/reopen` | 修改 | `teamNo` 字段已存在,本单补上从未赋值的填充 | --- ## 三、接口详情 ### 1. 退款详情 `GET /v3/admin/refund/{refundId}` **VO**: `RefundRecordVO` #### 使用场景 hl-ui 管理后台查看单笔退款记录的完整信息。**本单起响应体多出 `teamNo`**,退款详情页可直接显示团号,不必再按订单号反查。 #### 入参字段表 | 字段 | 位置 | 类型 | 必填 | 约束 | 说明 | |------|------|------|------|------|------| | refundId | Path | Long | ✅ | 退款记录 ID | 退款 ID | #### 出参字段表 | 字段 | 类型 | 说明 | |------|------|------| | teamNo | String | **本单新增**。团号,4 位,形如 `26-0001`。订金支付成功后生成;未生成、订单不存在或 `orderId` 为空时为 `null`(**不返空串、不回退订单号**) | | (其余字段) | — | **全部不变**,本单不改动任何既有字段的名称、类型与语义 | #### 请求示例 ```http GET /v3/admin/refund/{refundId} Authorization: Bearer ``` #### 响应示例 ```json { "code": 200, "message": "成功", "data": { "refundId": "2098380606701256706", "orderId": "2098372746097348609", "orderNo": "HL20260911192703129", "teamNo": "26-4823", "status": "SUCCESS" }, "success": true } ``` #### 空数据 / 降级响应 订单已被删除、或订金尚未支付成功时,`teamNo` 为 `null`,其余字段照常返回,**不降级、不报错**。 #### 错误响应 | 码 | 符号 | 触发 | |----|------|------| | 不变 | `PAYMENT_REFUND_RECORD_NOT_FOUND` | `refundId` 查不到记录 | ```json { "code": 520009, "message": "退款记录不存在: 999999", "data": null, "traceId": null, "success": false } ``` #### 业务边界 - `teamNo` 是**订单团号**(`order_main.team_no`),不是运营团期号 `batchNo`(`GB-26-0001`),本单不返后者。 - 同一张订单的多条退款记录 `teamNo` 必然相同。 ### 2. 按订单查退款 `GET /v3/admin/refund/order/{orderId}/list` **VO**: `List` #### 使用场景 hl-ui 订单详情页的退款记录列表。**本单起每行多出 `teamNo`**。 #### 入参字段表 | 字段 | 位置 | 类型 | 必填 | 约束 | 说明 | |------|------|------|------|------|------| | orderId | Path | Long | ✅ | 订单 ID | 订单 ID | #### 出参字段表 | 字段 | 类型 | 说明 | |------|------|------| | teamNo | String | **本单新增**。团号,4 位,形如 `26-0001`。订金支付成功后生成;未生成、订单不存在或 `orderId` 为空时为 `null`(**不返空串、不回退订单号**) | | (其余字段) | — | **全部不变**,本单不改动任何既有字段的名称、类型与语义 | #### 请求示例 ```http GET /v3/admin/refund/order/{orderId}/list Authorization: Bearer ``` #### 响应示例 ```json { "code": 200, "message": "成功", "data": [ { "refundId": "2098380606701256706", "orderId": "2098372746097348609", "orderNo": "HL20260911192703129", "teamNo": "26-4823", "status": "SUCCESS" } ], "success": true } ``` #### 空数据 / 降级响应 该订单无退款记录时返回空数组 `[]`(HTTP 200),**不抛错**。空数组时后端不发订单查询。 #### 错误响应 | 码 | 符号 | 触发 | |----|------|------| | 不变 | 无 | 本端点无新增错误码;无记录返空数组 | ```json { "code": 401, "message": "缺少有效的 Authorization 头", "data": null, "traceId": "b8ffe9e9e9f84abe", "success": false } ``` #### 业务边界 - 本列表所有行同属一张订单,团号必然相同;后端仍走批量富化,**整页只发 1 次订单查询**,前端无需担心行数带来的耗时增长。 ### 3. 支付交易分页 `GET /v3/admin/payment/list` **VO**: `PageResult` #### 使用场景 hl-ui 支付管理的交易流水分页列表。**本单起 `records[]` 每行多出 `teamNo`**,可直接做「按团号归并」的展示。 #### 入参字段表 | 字段 | 位置 | 类型 | 必填 | 约束 | 说明 | |------|------|------|------|------|------| | orderNo | Query | String | ❌ | 模糊匹配 | 订单号 | | mchId | Query | String | ❌ | — | 商户号 | | status | Query | String | ❌ | PENDING/SUCCESS/CLOSED/REFUNDED | 交易状态 | | tradeType | Query | String | ❌ | JSAPI/H5 | 交易类型 | | payType | Query | String | ❌ | FULL/DEPOSIT/BALANCE | 支付类型 | | startDate | Query | String | ❌ | `yyyy-MM-dd` | 起始日期(含) | | endDate | Query | String | ❌ | `yyyy-MM-dd` | 结束日期(含) | | page | Query | Integer | ❌ | 默认 1 | 页码 | | pageSize | Query | Integer | ❌ | 默认 20 | 每页条数 | #### 出参字段表 | 字段 | 类型 | 说明 | |------|------|------| | teamNo | String | **本单新增**。团号,4 位,形如 `26-0001`。订金支付成功后生成;未生成、订单不存在或 `orderId` 为空时为 `null`(**不返空串、不回退订单号**) | | (其余字段) | — | **全部不变**,本单不改动任何既有字段的名称、类型与语义 | #### 请求示例 ```http GET /v3/admin/payment/list Authorization: Bearer ``` #### 响应示例 ```json { "code": 200, "message": "成功", "data": { "records": [ { "orderId": "2098381549387882497", "orderNo": "HL20260911200202015", "teamNo": "26-4684" }, { "orderId": "2098381524394024962", "orderNo": "HL20260911200156046", "teamNo": "26-7561" } ], "total": 128, "page": 1, "pageSize": 20 }, "success": true } ``` #### 空数据 / 降级响应 空页(`records: []`)时后端不发订单查询;单行订单已删或订金未付时该行 `teamNo` 为 `null`,**整页照常返回,不会因一条脏数据整页 500**。 #### 错误响应 | 码 | 符号 | 触发 | |----|------|------| | 不变 | `PAYMENT_START_DATE_FORMAT_INVALID` / `PAYMENT_END_DATE_FORMAT_INVALID` | 日期格式非 `yyyy-MM-dd` | ```json { "code": 520007, "message": "开始日期格式错误,应为 yyyy-MM-dd", "data": null, "traceId": null, "success": false } ``` ```json { "code": 520008, "message": "结束日期格式错误,应为 yyyy-MM-dd", "data": null, "traceId": null, "success": false } ``` #### 业务边界 - 同一页里 `teamNo` 有值与为 `null` 的行会并存,属正常:团号只在订金支付成功后生成。 - **整页只发 1 次**订单批量查询(按 distinct `orderId`),不随行数线性增长。 ### 4. 交易详情 `GET /v3/admin/payment/{transactionId}` **VO**: `PaymentTransactionVO` #### 使用场景 hl-ui 查看单笔支付交易详情。**本单起响应体多出 `teamNo`**。 #### 入参字段表 | 字段 | 位置 | 类型 | 必填 | 约束 | 说明 | |------|------|------|------|------|------| | transactionId | Path | Long | ✅ | 交易 ID | 交易 ID | #### 出参字段表 | 字段 | 类型 | 说明 | |------|------|------| | teamNo | String | **本单新增**。团号,4 位,形如 `26-0001`。订金支付成功后生成;未生成、订单不存在或 `orderId` 为空时为 `null`(**不返空串、不回退订单号**) | | (其余字段) | — | **全部不变**,本单不改动任何既有字段的名称、类型与语义 | #### 请求示例 ```http GET /v3/admin/payment/{transactionId} Authorization: Bearer ``` #### 响应示例 ```json { "code": 200, "message": "成功", "data": { "transactionId": "2098382090226581506", "orderId": "2098381549387882497", "orderNo": "HL20260911200202015", "teamNo": "26-4684", "status": "SUCCESS" }, "success": true } ``` #### 空数据 / 降级响应 订金未支付成功 / 订单已删时 `teamNo` 为 `null`,其余字段照常返回。 #### 错误响应 | 码 | 符号 | 触发 | |----|------|------| | 不变 | `PAYMENT_TRANSACTION_NOT_FOUND` | `transactionId` 查不到交易 | ```json { "code": 520005, "message": "交易记录不存在", "data": null, "traceId": null, "success": false } ``` #### 业务边界 - 同 VO 的 `POST /v3/admin/payment/{transactionId}/sync`(同步支付状态)**两个返回分支都已补富化**,响应体同样多出 `teamNo`。 ### 5. 按订单查交易 `GET /v3/admin/payment/order/{orderId}` **VO**: `List` #### 使用场景 hl-ui 订单详情页的支付流水列表。**本单起每行多出 `teamNo`**。 #### 入参字段表 | 字段 | 位置 | 类型 | 必填 | 约束 | 说明 | |------|------|------|------|------|------| | orderId | Path | Long | ✅ | 订单 ID | 订单 ID | #### 出参字段表 | 字段 | 类型 | 说明 | |------|------|------| | teamNo | String | **本单新增**。团号,4 位,形如 `26-0001`。订金支付成功后生成;未生成、订单不存在或 `orderId` 为空时为 `null`(**不返空串、不回退订单号**) | | (其余字段) | — | **全部不变**,本单不改动任何既有字段的名称、类型与语义 | #### 请求示例 ```http GET /v3/admin/payment/order/{orderId} Authorization: Bearer ``` #### 响应示例 ```json { "code": 200, "message": "成功", "data": [ { "orderId": "2098381549387882497", "orderNo": "HL20260911200202015", "teamNo": "26-4684", "payType": "BALANCE" }, { "orderId": "2098381549387882497", "orderNo": "HL20260911200202015", "teamNo": "26-4684", "payType": "DEPOSIT" } ], "success": true } ``` #### 空数据 / 降级响应 该订单无交易记录时返回空数组 `[]`(HTTP 200)。 #### 错误响应 | 码 | 符号 | 触发 | |----|------|------| | 不变 | 无 | 本端点无新增错误码 | ```json { "code": 401, "message": "缺少有效的 Authorization 头", "data": null, "traceId": "b8ffe9e9e9f84abe", "success": false } ``` #### 业务边界 - 本列表所有行同属一张订单,团号必然相同。 ### 6. 工单分页 `GET /v3/admin/order/work-order/list` **VO**: `PageResult` #### 使用场景 hl-ui 协同工单分页列表。**本单起 `records[]` 每行多出 `teamNo`**。 #### 入参字段表 | 字段 | 位置 | 类型 | 必填 | 约束 | 说明 | |------|------|------|------|------|------| | status | Query | String | ❌ | PENDING/PROCESSING/RESOLVED/REJECTED | 工单状态 | | type | Query | String | ❌ | CHANGE/COMPLAINT/SUPPLEMENT/OTHER,取值以字典 `work_order_type` 为准 | 工单类型 | | priority | Query | String | ❌ | — | 优先级 | | orderNo | Query | String | ❌ | — | 关联订单号 | | assigneeAdminId | Query | Long | ❌ | — | 指派人 ID | | page | Query | Integer | ❌ | 默认 1 | 页码 | | pageSize | Query | Integer | ❌ | 默认 20 | 每页条数 | #### 出参字段表 | 字段 | 类型 | 说明 | |------|------|------| | teamNo | String | **本单新增**。团号,4 位,形如 `26-0001`。订金支付成功后生成;未生成、订单不存在或 `orderId` 为空时为 `null`(**不返空串、不回退订单号**) | | (其余字段) | — | **全部不变**,本单不改动任何既有字段的名称、类型与语义 | #### 请求示例 ```http GET /v3/admin/order/work-order/list Authorization: Bearer ``` #### 响应示例 ```json { "code": 200, "message": "成功", "data": { "records": [ { "id": "2098579038820786177", "orderId": "2097500233448448002", "orderNo": "HL20260909093959906", "teamNo": "26-9208" }, { "id": "2098579038493728770", "orderId": "2095549346731757569", "orderNo": "HL20260904002752146", "teamNo": null } ], "total": 12, "page": 1, "pageSize": 20 }, "success": true } ``` #### 空数据 / 降级响应 空页时后端不发订单查询;单行订单已删或 `orderId` 为空时该行 `teamNo` 为 `null`,整页照常返回。 #### 错误响应 | 码 | 符号 | 触发 | |----|------|------| | 不变 | 无 | 本端点无新增错误码 | ```json { "code": 401, "message": "缺少有效的 Authorization 头", "data": null, "traceId": "b8ffe9e9e9f84abe", "success": false } ``` #### 业务边界 - **整页只发 1 次**订单批量查询。 - `teamNo` 与 `typeName`/`statusName`/`priorityName` 三个字典展示名是两条独立填充链路,团号不走字典。 ### 7. 工单详情 `GET /v3/admin/order/work-order/{workOrderId}` **VO**: `WorkOrderRespVO` #### 使用场景 hl-ui 工单详情(含处理记录)。**本单起响应体多出 `teamNo`**。 #### 入参字段表 | 字段 | 位置 | 类型 | 必填 | 约束 | 说明 | |------|------|------|------|------|------| | workOrderId | Path | Long | ✅ | 工单 ID | 工单 ID | #### 出参字段表 | 字段 | 类型 | 说明 | |------|------|------| | teamNo | String | **本单新增**。团号,4 位,形如 `26-0001`。订金支付成功后生成;未生成、订单不存在或 `orderId` 为空时为 `null`(**不返空串、不回退订单号**) | | (其余字段) | — | **全部不变**,本单不改动任何既有字段的名称、类型与语义 | #### 请求示例 ```http GET /v3/admin/order/work-order/{workOrderId} Authorization: Bearer ``` #### 响应示例 ```json { "code": 200, "message": "成功", "data": { "id": "2098579038820786177", "workOrderNo": "WO202609120918017898", "orderId": "2097500233448448002", "orderNo": "HL20260909093959906", "teamNo": "26-9208", "status": "PENDING", "logs": [] }, "success": true } ``` #### 空数据 / 降级响应 订金未支付成功 / 订单已删时 `teamNo` 为 `null`。 #### 错误响应 | 码 | 符号 | 触发 | |----|------|------| | 不变 | `WORK_ORDER_NOT_FOUND` | `workOrderId` 查不到工单 | ```json { "code": 581440, "message": "工单不存在", "data": null, "traceId": null, "success": false } ``` #### 业务边界 - 同 VO 的 4 个写端点**同步补了富化**,响应体同样多出 `teamNo`:`POST /v3/admin/order/work-order`(创建)、`PUT …/{workOrderId}/process`(处理)、`PUT …/{workOrderId}/assign`(指派)、`POST …/{workOrderId}/comment`(备注)。 ### 8. 手动新建待办 `POST /v3/admin/order-todos/manual` **VO**: `OrderTodoRespVO` #### 使用场景 hl-ui 定制师手动给订单加一条待办。`teamNo` 字段**早就在契约里**,但此前单对象返回路径从未赋值、**恒为 `null`**;本单补上填充。 #### 入参字段表 | 字段 | 位置 | 类型 | 必填 | 约束 | 说明 | |------|------|------|------|------|------| | orderId | Body | Long | ✅ | 必须是调用人自己的订单 | 订单 ID | | todoLabel | Body | String | ✅ | 非空白 | 待办标题 | | todoDate | Body | String | ✅ | `yyyy-MM-dd` | 待办日期 | #### 出参字段表 | 字段 | 类型 | 说明 | |------|------|------| | teamNo | String | **本单新增**。团号,4 位,形如 `26-0001`。订金支付成功后生成;未生成、订单不存在或 `orderId` 为空时为 `null`(**不返空串、不回退订单号**) | | (其余字段) | — | **全部不变**,本单不改动任何既有字段的名称、类型与语义 | #### 请求示例 ```http POST /v3/admin/order-todos/manual Authorization: Bearer ``` #### 响应示例 ```json { "code": 200, "message": "成功", "data": { "todoId": "2098579085771923457", "orderId": "2097500233448448002", "orderNo": "HL20260909093959906", "teamNo": "26-9208", "todoSource": "MANUAL", "status": "PENDING" }, "success": true } ``` #### 空数据 / 降级响应 订金未支付成功 / 订单已删时 `teamNo` 为 `null`(**此前是恒 `null`,现在是真实值或 `null`**)。 #### 错误响应 | 码 | 符号 | 触发 | |----|------|------| | 不变 | `TODO_ORDER_REQUIRED` | `orderId` 为空或订单不存在 | | 不变 | `TODO_TITLE_REQUIRED` | `todoLabel` 为空白 | | 不变 | `TODO_DATE_REQUIRED` | `todoDate` 为空 | | 不变 | `TODO_ORDER_CONSULTANT_MISMATCH` | 订单不属于调用人 | ```json { "code": 581702, "message": "待办必须关联订单", "data": null, "traceId": null, "success": false } ``` ```json { "code": 581704, "message": "待办标题不能为空", "data": null, "traceId": null, "success": false } ``` #### 业务边界 - **分页列表 `GET /v3/admin/order-todos` 的 `teamNo` 本来就有值**(走 join 直接带出),本单只补齐单对象路径,两条路径自此一致。 ### 9. 修改手动待办 `PUT /v3/admin/order-todos/{todoId}` **VO**: `OrderTodoRespVO` #### 使用场景 hl-ui 修改手动待办的标题 / 日期。`data.teamNo` 由**恒 `null`**改为真实值。 #### 入参字段表 | 字段 | 位置 | 类型 | 必填 | 约束 | 说明 | |------|------|------|------|------|------| | todoId | Path | Long | ✅ | 必须是调用人自己的手动待办 | 待办 ID | | todoLabel | Body | String | ❌ | 两者都不传则原样返回 | 新标题 | | todoDate | Body | String | ❌ | `yyyy-MM-dd` | 新日期 | #### 出参字段表 | 字段 | 类型 | 说明 | |------|------|------| | teamNo | String | **本单新增**。团号,4 位,形如 `26-0001`。订金支付成功后生成;未生成、订单不存在或 `orderId` 为空时为 `null`(**不返空串、不回退订单号**) | | (其余字段) | — | **全部不变**,本单不改动任何既有字段的名称、类型与语义 | #### 请求示例 ```http PUT /v3/admin/order-todos/{todoId} Authorization: Bearer ``` #### 响应示例 ```json { "code": 200, "message": "成功", "data": { "todoId": "2098579085771923457", "orderId": "2097500233448448002", "orderNo": "HL20260909093959906", "teamNo": "26-9208", "status": "PENDING" }, "success": true } ``` #### 空数据 / 降级响应 订金未支付成功 / 订单已删时 `teamNo` 为 `null`。 #### 错误响应 | 码 | 符号 | 触发 | |----|------|------| | 不变 | `TODO_NOT_FOUND` | `todoId` 查不到 | | 不变 | `SYSTEM_TODO_MANUAL_OPERATION_FORBIDDEN` | 系统生成的待办不允许手动改 | | 不变 | `TODO_FORBIDDEN` | 待办不属于调用人 | ```json { "code": 581700, "message": "待办不存在", "data": null, "traceId": null, "success": false } ``` ```json { "code": 581701, "message": "无权操作该待办", "data": null, "traceId": null, "success": false } ``` #### 业务边界 - 两个 body 字段都不传时,后端原样返回当前待办——此路径同样会填 `teamNo`。 ### 10. 完成待办 `PUT /v3/admin/order-todos/{todoId}/complete` **VO**: `OrderTodoRespVO` #### 使用场景 hl-ui 把手动待办标记为已完成。`data.teamNo` 由**恒 `null`**改为真实值。 #### 入参字段表 | 字段 | 位置 | 类型 | 必填 | 约束 | 说明 | |------|------|------|------|------|------| | todoId | Path | Long | ✅ | 必须是调用人自己的手动待办 | 待办 ID | #### 出参字段表 | 字段 | 类型 | 说明 | |------|------|------| | teamNo | String | **本单新增**。团号,4 位,形如 `26-0001`。订金支付成功后生成;未生成、订单不存在或 `orderId` 为空时为 `null`(**不返空串、不回退订单号**) | | (其余字段) | — | **全部不变**,本单不改动任何既有字段的名称、类型与语义 | #### 请求示例 ```http PUT /v3/admin/order-todos/{todoId}/complete Authorization: Bearer ``` #### 响应示例 ```json { "code": 200, "message": "成功", "data": { "todoId": "2098579085461446657", "orderId": "2095549346731757569", "orderNo": "HL20260904002752146", "teamNo": null, "status": "COMPLETED" }, "success": true } ``` #### 空数据 / 降级响应 上面的响应示例就是订金未支付成功的真实回包:`teamNo` 为 `null`,**不是空串、不是订单号**。 #### 错误响应 | 码 | 符号 | 触发 | |----|------|------| | 不变 | `TODO_NOT_FOUND` | `todoId` 查不到 | | 不变 | `SYSTEM_TODO_MANUAL_OPERATION_FORBIDDEN` | 系统生成的待办不允许手动操作 | | 不变 | `TODO_FORBIDDEN` | 待办不属于调用人 | ```json { "code": 581700, "message": "待办不存在", "data": null, "traceId": null, "success": false } ``` ```json { "code": 581703, "message": "系统待办不允许手动完成或重开", "data": null, "traceId": null, "success": false } ``` #### 业务边界 - 待办已是非 `PENDING` 态时后端不重复写库、原样返回,该短路分支**同样**会填 `teamNo`。 ### 11. 重开待办 `PUT /v3/admin/order-todos/{todoId}/reopen` **VO**: `OrderTodoRespVO` #### 使用场景 hl-ui 把已完成的手动待办重新打开。`data.teamNo` 由**恒 `null`**改为真实值。 #### 入参字段表 | 字段 | 位置 | 类型 | 必填 | 约束 | 说明 | |------|------|------|------|------|------| | todoId | Path | Long | ✅ | 必须是调用人自己的手动待办 | 待办 ID | #### 出参字段表 | 字段 | 类型 | 说明 | |------|------|------| | teamNo | String | **本单新增**。团号,4 位,形如 `26-0001`。订金支付成功后生成;未生成、订单不存在或 `orderId` 为空时为 `null`(**不返空串、不回退订单号**) | | (其余字段) | — | **全部不变**,本单不改动任何既有字段的名称、类型与语义 | #### 请求示例 ```http PUT /v3/admin/order-todos/{todoId}/reopen Authorization: Bearer ``` #### 响应示例 ```json { "code": 200, "message": "成功", "data": { "todoId": "2098579085771923457", "orderId": "2097500233448448002", "orderNo": "HL20260909093959906", "teamNo": "26-9208", "status": "PENDING" }, "success": true } ``` #### 空数据 / 降级响应 订金未支付成功 / 订单已删时 `teamNo` 为 `null`。 #### 错误响应 | 码 | 符号 | 触发 | |----|------|------| | 不变 | `TODO_NOT_FOUND` | `todoId` 查不到 | | 不变 | `SYSTEM_TODO_MANUAL_OPERATION_FORBIDDEN` | 系统生成的待办不允许手动操作 | | 不变 | `TODO_FORBIDDEN` | 待办不属于调用人 | ```json { "code": 581700, "message": "待办不存在", "data": null, "traceId": null, "success": false } ``` ```json { "code": 581703, "message": "系统待办不允许手动完成或重开", "data": null, "traceId": null, "success": false } ``` #### 业务边界 - 待办已是 `PENDING` 态时后端不重复写库、原样返回,该短路分支**同样**会填 `teamNo`。 --- ## 四、契约约束与正确调用方式 - **判空只判 `null`**:`teamNo` 为空时后端返 `null`,**不返空串**。前端 `if (row.teamNo)` 即可,不必再写 `!== ''`。 - **不要拿 `teamNo` 判团期单**:判团期请用团期相关字段(`groupBatchId` 等),不是团号。 已付订金的散客单有团号、未付订金的团期单没有团号,用团号判会两个方向都判错。 - **不要按团号归并同团订单**:同一团期下每张子订单的团号各不相同。要归团请用 `groupBatchId`。 - **不要期待本单返运营团期号**:`batchNo`(`GB-26-0001`)与从订单跳团期所需的 `groupBatchId` **不在本单范围**,另有工单跟进。 - **格式**:`26-0001` —— 年份后两位 + `-` + 4 位序号,`VARCHAR(10)` 字符串,**不是数字**, 不要做数值解析、也不要按 `T` 开头做前缀判断(旧 Swagger `example` 写的 `T2026…` 是错的,本单已一并订正)。 - **分页接口不会因为新增该字段变慢**:整页只发 1 次订单批量查询,不随行数增长。 --- ## 五、数据库行为 **本单零数据库变更**:无新增/修改表、无新增列、无新增索引、无 Flyway 迁移脚本、无 Mapper 改动。 `teamNo` 取的是**既有列** `order_main.team_no`(DDL 注释:「团号(订金支付成功后生成,4位)」), 经既有服务契约 `OrderService.selectBatchByIds(Collection)` 批量读出,**只读、不写**。 清单里的写端点(8/9/10/11 待办写接口,以及工单的创建/处理/指派/备注)**写库行为一行未改**, 本单只是在它们返回前多填了一个展示字段。 --- ## 六、边界行为 | 场景 | 行为 | |---|---| | 订金已支付成功 | `teamNo` 有值,形如 `26-0001` | | 订金未支付成功 | `teamNo` 为 `null` | | 行的 `orderId` 为 `null`(历史脏数据) | 该行 `teamNo` 为 `null`,**接口正常返回,不抛异常** | | 订单已被删除 | 该行 `teamNo` 为 `null`,**接口正常返回,不抛异常** | | 分页里一条脏数据 | **只影响那一行**,整页照常返回,不会整页 500 | | 空列表 / 空页 | 后端不发订单查询 | | 团期子订单 | 与散客单一视同仁,按订金是否付成功决定有无团号 | ## 六.6、修改前后对比 | 维度 | 改前 | 改后 | |---|---|---| | 接口 1~7(退款/支付/工单) | 响应体**没有** `teamNo` 字段 | 响应体**有** `teamNo`,位置紧随 `orderNo` | | 接口 8~11(订单待办) | `teamNo` 字段存在但**恒为 `null`** | `teamNo` 填真实值(无团号时才是 `null`) | | 订单待办分页 `GET /v3/admin/order-todos` | 已有值 | **不变**(本单未动该路径) | | 路径 / 方法 / 请求参数 | — | **完全不变** | | 既有字段的名称/类型/语义 | — | **完全不变** | | 错误码 | — | **无新增、无调整** | ## 六.7、影响评估 - **兼容性**:响应体**纯增**字段,未消费 `teamNo` 的前端页面行为完全不变,**无需任何改动即可继续工作**。 - **需要前端动的**:想显示团号的页面按上表加一列即可;接口 8~11 的调用方若此前把 `teamNo` 恒为 `null` 当成「该字段没用」硬编码隐藏,现在可以打开。 - **性能**:3 个列表/分页端点每页多一次主键 `IN` 批量查询(**整页 1 次,不是每行 1 次**)。 `POST /v3/admin/payment/{transactionId}/sync` 的写事务内多一次同数据源本地读,该端点是管理员手动触发的低频操作。 - **连带生效(无需单独对接,响应体同样多出该字段)**: `POST /v3/admin/refund/orders/{orderId}`、`POST /v3/admin/payment/{transactionId}/sync`、 工单的 `POST /v3/admin/order/work-order`、`PUT …/process`、`PUT …/assign`、`POST …/comment`。 - **`/v3/internal/` 侧连带多出该字段的 4 个端点**(服务间调用,公网网关不放行,前端不对接): `POST /v3/internal/payment/refund`、`GET /v3/internal/payment/status/{orderId}`、 `GET /v3/internal/payment/transactions/{orderId}`、`GET /v3/internal/order/{orderId}/transactions`。 它们复用同一批 VO,属向后兼容纯增。 --- ## 七、不影响范围 - **`GET /v3/internal/payment/pay-info/{orderId}`(`PayInfoVO`)不追加任何字段** —— 非前端可见,本单明确不改。 - **小程序端(mp 域)全部 VO 不动**。 - **团期详情页的 5 个逐行 VO 不动**(`GroupBatchOrderItemRespVO` / `GroupBatchChipItemRespVO` / `GroupBatchContractItemVO` / `GroupBatchFinanceItemVO` / `GroupBatchAdvanceItemVO`),另有工单跟进。 - **`RefundApplicationDetailRespVO.RecordItemVO` 刻意不加**:同一响应体里的 record 行全属同一张订单, 团号与 `orderInfo.teamNo` 必然相同,逐行重复只会让前端猜哪个是权威值。 - **8 个已有 `teamNo` 的 VO 字段声明未被改动**;只订正了 3 处 Swagger `example` 字面量 (`T2026…` → `26-0001`),字段名与文案不动。 - **零改动**:Controller 方法体、Mapper、Entity、Flyway、错误码、hl-gateway 配置、其他微服务。 --- ## 八、测试环境已验证 **部署**:`hl-order-service-v3` @ `feature/7537-team-no-backfill` / `6dcee7d4d`(已合并 dev-v3 = `c798fd5a6`), 双实例滚动重启完成。**全部实测经真实网关 `api.test.1814.love:9443` + Bearer 鉴权**。 | # | 端点 | 已付订金 | 未付订金 | |---|---|---|---| | 1 | `GET /v3/admin/refund/{refundId}` | 200,`teamNo="26-4823"` | 200,`teamNo=null` | | 2 | `GET /v3/admin/refund/order/{orderId}/list` | 200,`teamNo="26-4823"` | 200,`teamNo=null` | | 3 | `GET /v3/admin/payment/list` | 200,`26-4684` / `26-7561` / `26-0438` 三行各自正确 | 同页混合行含 `null` | | 4 | `GET /v3/admin/payment/{transactionId}` | 200,`teamNo="26-4684"` | 200,`teamNo=null` | | 5 | `GET /v3/admin/payment/order/{orderId}` | 200,两行均 `26-4684` | 200,`teamNo=null` | | 6 | `GET /v3/admin/order/work-order/list` | 200,`26-6331`/`26-9208`/`26-1849` | 同页含 `teamNo=null` 行 | | 7 | `GET /v3/admin/order/work-order/{workOrderId}` | 200,`teamNo="26-9208"` | 200,`teamNo=null` | | 10 | `PUT /v3/admin/order-todos/{todoId}/complete` | 200,`teamNo="26-9208"` | 200,`teamNo=null` | **团期子订单也有团号**(挡住「以为只有团期单才有团号」的反向误解): `GET /v3/admin/payment/order/2098372769329598466` → `teamNo="26-9069"`,该单 `group_batch_id` 非空。 **同团多户团号各不相同**:同一 `group_batch_id=2097500233511362561` 下的两张子订单, 经 `GET /v3/admin/order/work-order/{workOrderId}` 分别返 `26-9208` 与 `26-6331`。 **本地全量**:`mvn -o -pl hl-order-service-v3 -am test` → **9925 例 / Failures 0**; 其中新增 4 个测试类 35 例全绿,`RedLineArchTest` / `LayerEnforcementTest` / `MapperBoundaryArchTest` 均通过(非 Skipped)。 --- ## 十、相关文档 - Issue:https://git.1814.love:8443/wx/HL/issues/7537 - PR:https://git.1814.love:8443/wx/HL/pulls/7564 - 字段来源 DDL:`hl-order-service-v3/src/main/resources/db/migration/V20260511_001__init_core_tables.sql:32` - 团号生成器:`hl-order-service-v3/src/main/java/com/hulalv/order/core/service/GroupCodeService.java:11-17,30-37` --- ## 关联 / 联系人 - 后端:jw - 前端(hl-ui 管理后台):mmg - 需求定案:wx(2026-09-11 选项卡)