diff --git a/changelogs-v2/2026-07/87_5158_派车按行程日标记车费日期-修改接口-前端待处理-管理后台.md b/changelogs-v2/2026-07/22_5158_派车按行程日标记车费日期-修改接口-管理后台.md similarity index 100% rename from changelogs-v2/2026-07/87_5158_派车按行程日标记车费日期-修改接口-前端待处理-管理后台.md rename to changelogs-v2/2026-07/22_5158_派车按行程日标记车费日期-修改接口-管理后台.md diff --git a/changelogs-v2/2026-07/86_5160_一名司机多辆常驻车-修改接口-前端待处理-管理后台.md b/changelogs-v2/2026-07/22_5160_一名司机多辆常驻车-修改接口-管理后台.md similarity index 100% rename from changelogs-v2/2026-07/86_5160_一名司机多辆常驻车-修改接口-前端待处理-管理后台.md rename to changelogs-v2/2026-07/22_5160_一名司机多辆常驻车-修改接口-管理后台.md diff --git a/changelogs-v2/2026-07/88_5176_房务配房彻底移除成交价历史字段-修改接口-前端待处理-管理后台.md b/changelogs-v2/2026-07/23_5176_房务配房彻底移除成交价历史字段-修改接口-管理后台.md similarity index 100% rename from changelogs-v2/2026-07/88_5176_房务配房彻底移除成交价历史字段-修改接口-前端待处理-管理后台.md rename to changelogs-v2/2026-07/23_5176_房务配房彻底移除成交价历史字段-修改接口-管理后台.md diff --git a/changelogs-v2/2026-07/89_5178_用车手动加急与派车看板状态颜色-新增接口-前端待处理-管理后台.md b/changelogs-v2/2026-07/23_5178_用车手动加急与派车看板状态颜色-新增接口-管理后台.md similarity index 100% rename from changelogs-v2/2026-07/89_5178_用车手动加急与派车看板状态颜色-新增接口-前端待处理-管理后台.md rename to changelogs-v2/2026-07/23_5178_用车手动加急与派车看板状态颜色-新增接口-管理后台.md diff --git a/changelogs-v2/2026-07/90_5193_用车需求增加独立接机送机选择-修改接口-前端待处理-管理后台.md b/changelogs-v2/2026-07/23_5193_用车需求增加独立接机送机选择-修改接口-管理后台.md similarity index 100% rename from changelogs-v2/2026-07/90_5193_用车需求增加独立接机送机选择-修改接口-前端待处理-管理后台.md rename to changelogs-v2/2026-07/23_5193_用车需求增加独立接机送机选择-修改接口-管理后台.md diff --git a/changelogs-v2/2026-07/91_5202_调整订单行程节点时间-修改接口-前端待处理-管理后台.md b/changelogs-v2/2026-07/24_5202_调整订单行程节点时间-修改接口-管理后台.md similarity index 100% rename from changelogs-v2/2026-07/91_5202_调整订单行程节点时间-修改接口-前端待处理-管理后台.md rename to changelogs-v2/2026-07/24_5202_调整订单行程节点时间-修改接口-管理后台.md diff --git a/changelogs-v2/2026-07/24_5216_派车看板补充槽位接送路线与就绪摘要-修改接口-前端待处理-管理后台.md b/changelogs-v2/2026-07/24_5216_派车看板补充槽位接送路线与就绪摘要-修改接口-管理后台.md similarity index 100% rename from changelogs-v2/2026-07/24_5216_派车看板补充槽位接送路线与就绪摘要-修改接口-前端待处理-管理后台.md rename to changelogs-v2/2026-07/24_5216_派车看板补充槽位接送路线与就绪摘要-修改接口-管理后台.md diff --git a/changelogs-v2/2026-07/25_5238_核单门票来源类型统一-修改接口-管理后台.md b/changelogs-v2/2026-07/25_5238_核单门票来源类型统一-修改接口-管理后台.md new file mode 100644 index 0000000..e68c115 --- /dev/null +++ b/changelogs-v2/2026-07/25_5238_核单门票来源类型统一-修改接口-管理后台.md @@ -0,0 +1,382 @@ +# 【修改接口·管理后台】核单门票来源类型统一 (#5238) + +> **PR**: #5242 | **服务**: hl-order-service-v3 | **更新时间**: 2026-07-25 10:03 + +## 1. 接口背景 + +核单 Step2 门票/游玩项目页签中,手工补充的门票行此前在查询出参中使用 `CUSTOM_ASSIGNMENT`。为避免前端按不同 Tab 或来源类型做额外分支,本次将查询出参的手工门票来源统一为 `MANUAL`,中文名统一为 `手工项目`;保存接口同步允许直接提交 `MANUAL`。 + +## 2. 变更清单 + +| # | 接口 | 方法 | 路径 | 变更类型 | 说明 | +|---|------|------|------|----------|------| +| 1 | Step 2 查询门票核单明细 | GET | `/v3/admin/order/{orderId}/settlement/step2` | 修改接口 | 手工/自定义门票行的 `sourceType` 统一返回 `MANUAL`,`sourceTypeName` 返回 `手工项目` | +| 2 | Step 2 录门票核单明细 | PUT | `/v3/admin/order/{orderId}/settlement/step2` | 修改接口 | `items[].sourceType` 新增允许 `MANUAL`;旧 `CUSTOM_ASSIGNMENT` 入参继续兼容 | + +## 3. 接口详情 + +### 3.1 Step 2 查询门票核单明细 + +- **方法**:GET +- **路径**:`/v3/admin/order/{orderId}/settlement/step2` +- **接口名**:`listTicket` +- **ApiOperation**:Step 2 查询门票核单明细 +- **使用场景**:进入核单 Step2 门票/游玩项目页签,或保存成功后回读页面明细。 +- **认证**:需要管理后台 JWT。 +- **幂等性**:幂等,只读查询。 +- **限流**:无单接口额外限流。 +- **响应结构**:`data` 为 `TicketItemVO[]`。 + +### 3.2 Step 2 录门票核单明细 + +- **方法**:PUT +- **路径**:`/v3/admin/order/{orderId}/settlement/step2` +- **接口名**:`saveTicket` +- **ApiOperation**:Step 2 录门票核单明细 +- **使用场景**:保存核单 Step2 门票/游玩项目明细,包含派生门票行和手工补充门票行。 +- **认证**:需要管理后台 JWT。 +- **幂等性**:全量替换保存;同一份 `items` 重复提交后,以最后一次提交结果为准。 +- **限流**:无单接口额外限流。 +- **请求体兼容**:推荐使用 `{ "items": [...] }`;历史数组 body `[...]` 仍兼容。 +- **响应结构**:`data` 为 `SettlementTicketSaveRespVO`。 + +## 4. 接口入参 + +### 4.1 路径参数 / Query 参数 + +| 接口 | 字段 | 类型 | 必填 | 说明 | +|------|------|------|------|------| +| GET / PUT | `orderId` | string | 是 | 订单 ID,长整型字符串 | + +两个接口均无 Query 参数。 + +### 4.2 GET 请求体字段 + +GET 无请求体。 + +### 4.3 PUT 请求体字段 + +| 字段 | 类型 | 必填 | 说明 | 校验规则 | +|------|------|------|------|----------| +| `items` | array | 是 | 门票/游玩项目明细行数组,全量替换保存 | 不允许为 `null` | +| `items[].id` | string | 否 | 已存在行 ID;新增行可不传 | 长整型字符串 | +| `items[].sourceType` | string | 是 | 来源类型;手工门票推荐传 `MANUAL` | `SCENIC_ASSIGNMENT` / `ACTIVITY_ASSIGNMENT` / `MANUAL` / `CUSTOM_ASSIGNMENT` | +| `items[].sourceTypeName` | string | 否 | 来源类型中文名,仅展示字段;保存时可不传 | 最大 32 字符 | +| `items[].scenicAssignmentId` | string/null | 否 | 来源 assignment ID;手工项目传 `null` | 长整型字符串或 `null` | +| `items[].dayNumber` | integer/null | 否 | 行程第几天;保存后以回读值为准 | 从 1 开始 | +| `items[].dayDate` | string | 是 | 行程日期 | `yyyy-MM-dd` | +| `items[].scenicName` | string | 是 | 景区/游玩项目名称 | 1-200 字符 | +| `items[].specName` | string/null | 否 | 规格/票型名称 | 最大 128 字符 | +| `items[].ticketCount` | integer | 是 | 实际购票数量;套餐含门票但无额外成本时可填 0 | 整数 | +| `items[].ticketUnitPrice` | number/null | 否 | 参考成本单价,单位元 | 小数 | +| `items[].sellPrice` | number/null | 否 | 客户成交单价,单位元 | `>= 0` | +| `items[].totalAmount` | number/null | 否 | 客户成交小计,单位元 | `>= 0` | +| `items[].plannedCost` | number | 是 | 计划成本,单位元 | `>= 0` | +| `items[].actualCost` | number | 是 | 实际成本,单位元 | `>= 0` | +| `items[].paymentMethod` | string | 否 | 付款方式;不传时按公司付款处理 | `SIGNED` / `COMPANY_PAID` / `CASH_PAID` | +| `items[].paymentMethodName` | string | 否 | 付款方式中文名,仅展示字段;保存时可不传 | 最大 32 字符 | +| `items[].voucherUrls` | array | 否 | 凭证图片 URL 数组 | 字符串数组 | +| `items[].remark` | string/null | 否 | 备注 | 最大 500 字符 | + +## 5. 出参字段 + +### 5.1 GET 响应字段:`TicketItemVO[]` + +| 字段 | 类型 | 说明 | +|------|------|------| +| `code` | integer | 业务状态码,成功为 `200` | +| `message` | string | 响应消息 | +| `success` | boolean | 是否成功 | +| `data` | array | 门票/游玩项目明细行数组 | +| `data[].id` | string/null | 核单明细行 ID;未持久化派生行可能为 `null` | +| `data[].sourceType` | string | 来源类型;手工/自定义门票行本次统一返回 `MANUAL` | +| `data[].sourceTypeName` | string/null | 来源类型中文名;`MANUAL` 返回 `手工项目` | +| `data[].scenicAssignmentId` | string/null | 来源 assignment ID;手工项目为 `null` | +| `data[].dayNumber` | integer/null | 行程第几天 | +| `data[].dayDate` | string | 行程日期,`yyyy-MM-dd` | +| `data[].scenicName` | string | 景区/游玩项目名称 | +| `data[].specName` | string/null | 规格/票型名称 | +| `data[].ticketCount` | integer | 实际购票数量 | +| `data[].ticketUnitPrice` | number/null | 参考成本单价,单位元 | +| `data[].sellPrice` | number/null | 客户成交单价,单位元 | +| `data[].totalAmount` | number/null | 客户成交小计,单位元 | +| `data[].plannedCost` | number | 计划成本,单位元 | +| `data[].actualCost` | number | 实际成本,单位元 | +| `data[].paymentMethod` | string/null | 付款方式 | +| `data[].paymentMethodName` | string/null | 付款方式中文名 | +| `data[].voucherUrls` | array | 凭证图片 URL 数组 | +| `data[].remark` | string/null | 备注 | + +### 5.2 PUT 响应字段:`SettlementTicketSaveRespVO` + +| 字段 | 类型 | 说明 | +|------|------|------| +| `code` | integer | 业务状态码,成功为 `200` | +| `message` | string | 响应消息 | +| `success` | boolean | 是否成功 | +| `data.addedIds` | string[] | 本次保存新增的核单明细行 ID 列表 | +| `data.updatedIds` | string[] | 本次保存更新的核单明细行 ID 列表;当前全量替换语义下通常为空数组 | +| `data.deletedIds` | string[] | 本次保存删除的核单明细行 ID 列表;当前返回通常为空数组 | +| `data.totalActualCost` | string | 保存后 Step2 实际成本合计,单位元 | + +## 6. 枚举 / 数据字典 + +### 6.1 `sourceType` + +**所属字段**:`items[].sourceType`、`data[].sourceType` | **类型**:String | **PUT 必填**:是 | **GET 必返**:是 + +| 值 | 中文 | 说明 | +|----|------|------| +| `SCENIC_ASSIGNMENT` | 景区 | 景区派生来源行;查询和保存语义不变 | +| `ACTIVITY_ASSIGNMENT` | 游玩项目 | 游玩项目派生来源行;查询和保存语义不变 | +| `MANUAL` | 手工项目 | 本次推荐值;查询手工/自定义门票行统一返回该值,保存接口也允许提交该值 | +| `CUSTOM_ASSIGNMENT` | 手工项目(旧入参兼容) | 仅用于兼容旧保存请求;查询响应不再返回该值 | + +### 6.2 `sourceTypeName` + +**所属字段**:`items[].sourceTypeName`、`data[].sourceTypeName` | **类型**:String | **必填**:否 + +| sourceType | sourceTypeName | 说明 | +|------------|----------------|------| +| `SCENIC_ASSIGNMENT` | `景区` | 景区派生来源行 | +| `ACTIVITY_ASSIGNMENT` | `游玩项目` | 游玩项目派生来源行 | +| `MANUAL` | `手工项目` | 手工/自定义门票行统一展示名 | +| `CUSTOM_ASSIGNMENT` | `手工项目` | 旧保存请求兼容;保存成功后回读为 `MANUAL` / `手工项目` | +| `null` / 未知值 | `null` | 查询行为不变,不新增兜底文案 | + +### 6.3 `paymentMethod` + +**所属字段**:`items[].paymentMethod`、`data[].paymentMethod` | **类型**:String | **必填**:否 + +| 值 | 中文 | 说明 | +|----|------|------| +| `SIGNED` | 签单 | 现场签单 | +| `COMPANY_PAID` | 公司付款 | 公司统一付款;未传 `paymentMethod` 时按该值处理 | +| `CASH_PAID` | 现付 | 现场现金/线下现付 | + +## 7. 错误码 + +| HTTP 状态 / code | 含义 | 触发场景 | +|------------------|------|----------| +| `200` / `200` | 成功 | GET 查询成功或 PUT 保存成功 | +| `200` / `401` | 未授权 | 缺少有效的管理后台 `Authorization` 头 | +| `400` / `400` | 请求参数非法 | `sourceType` 不在 `SCENIC_ASSIGNMENT` / `ACTIVITY_ASSIGNMENT` / `MANUAL` / `CUSTOM_ASSIGNMENT` 内,或请求体结构不符合要求 | +| `200` / `584011` | 当前核单状态不允许录门票核单 | PUT 保存时订单不是可录门票核单的状态 | + +### 7.1 错误结构 + +```json +{ + "code": 400, + "message": "sourceType 必须是 SCENIC_ASSIGNMENT / ACTIVITY_ASSIGNMENT / MANUAL / CUSTOM_ASSIGNMENT 之一", + "data": null, + "success": false +} +``` + +## 8. 示例(3 组:典型 / 边界 / 异常) + +### 8.1 典型成功:GET 返回手工项目为 MANUAL + +**请求**: + +```http +GET /v3/admin/order/2079576729147338754/settlement/step2 +Authorization: Bearer {token} +``` + +GET 无请求体。 + +**响应**: + +```json +{ + "code": 200, + "message": "成功", + "success": true, + "data": [ + { + "id": "2080186487600025601", + "sourceType": "MANUAL", + "sourceTypeName": "手工项目", + "scenicAssignmentId": null, + "dayNumber": 2, + "dayDate": "2026-07-22", + "scenicName": "临时补充门票", + "specName": "成人票", + "ticketCount": 2, + "ticketUnitPrice": 30.00, + "sellPrice": 50.00, + "totalAmount": 100.00, + "plannedCost": 60.00, + "actualCost": 60.00, + "paymentMethod": "COMPANY_PAID", + "paymentMethodName": "公司付款", + "voucherUrls": [], + "remark": "现场补充" + } + ] +} +``` + +### 8.2 边界成功:查询结果原样 PUT + +**场景说明**:前端可把 GET 回来的 `MANUAL` 行原样放入 `items` 后提交;保存成功后再次 GET 仍返回 `MANUAL` / `手工项目`。 + +**请求**: + +```http +PUT /v3/admin/order/2079576729147338754/settlement/step2 +Authorization: Bearer {token} +Content-Type: application/json +``` + +```json +{ + "items": [ + { + "id": "2080186487600025601", + "sourceType": "MANUAL", + "sourceTypeName": "手工项目", + "scenicAssignmentId": null, + "dayNumber": 2, + "dayDate": "2026-07-22", + "scenicName": "临时补充门票", + "specName": "成人票", + "ticketCount": 2, + "ticketUnitPrice": 30.00, + "sellPrice": 50.00, + "totalAmount": 100.00, + "plannedCost": 60.00, + "actualCost": 60.00, + "paymentMethod": "COMPANY_PAID", + "paymentMethodName": "公司付款", + "voucherUrls": [], + "remark": "现场补充" + } + ] +} +``` + +**响应**: + +```json +{ + "code": 200, + "message": "成功", + "success": true, + "data": { + "addedIds": ["2080186500000000001"], + "updatedIds": [], + "deletedIds": [], + "totalActualCost": "60.00" + } +} +``` + +### 8.3 业务失败:非法 sourceType + +**场景说明**:`items[].sourceType` 传入未定义值时仍按参数非法处理。 + +**请求**: + +```http +PUT /v3/admin/order/2079576729147338754/settlement/step2 +Authorization: Bearer {token} +Content-Type: application/json +``` + +```json +{ + "items": [ + { + "sourceType": "TAB_MANUAL", + "scenicAssignmentId": null, + "dayDate": "2026-07-22", + "scenicName": "临时补充门票", + "specName": "成人票", + "ticketCount": 1, + "ticketUnitPrice": 0, + "sellPrice": 0, + "totalAmount": 0, + "plannedCost": 0, + "actualCost": 0, + "paymentMethod": "COMPANY_PAID", + "voucherUrls": [], + "remark": null + } + ] +} +``` + +**响应**: + +```json +{ + "code": 400, + "message": "sourceType 必须是 SCENIC_ASSIGNMENT / ACTIVITY_ASSIGNMENT / MANUAL / CUSTOM_ASSIGNMENT 之一", + "data": null, + "success": false +} +``` + +## 9. 业务边界 + +- **适用场景**:核单 Step2 门票/游玩项目页签查询、保存门票明细时使用。 +- **手工项目保存**:新增或编辑手工门票行时,`items[].sourceType` 推荐传 `MANUAL`,`scenicAssignmentId` 可传 `null`。 +- **旧入参兼容**:旧页面继续传 `CUSTOM_ASSIGNMENT` 仍可保存;保存成功后再次查询会返回 `MANUAL`。 +- **查询结果原样提交**:GET 返回的 `MANUAL` 行可原样进入 PUT 的 `items`。 +- **未变化范围**:`SCENIC_ASSIGNMENT`、`ACTIVITY_ASSIGNMENT` 的查询和保存语义不变;`null` / 未知来源的查询兜底行为不变。 +- **不适用场景**:人员费用、住宿、餐食、其他支出接口没有本次契约变化。 + +## 10. 修改前后对比 + +### 10.1 字段级对比 + +| 字段 | 修改前 | 修改后 | +|------|--------|--------| +| GET `data[].sourceType` | 手工/自定义门票行返回 `CUSTOM_ASSIGNMENT` | 手工/自定义门票行统一返回 `MANUAL` | +| GET `data[].sourceTypeName` | 手工/自定义门票行可能按旧来源展示 | 手工/自定义门票行统一返回 `手工项目` | +| PUT `items[].sourceType` | 允许 `SCENIC_ASSIGNMENT` / `ACTIVITY_ASSIGNMENT` / `CUSTOM_ASSIGNMENT` | 允许 `SCENIC_ASSIGNMENT` / `ACTIVITY_ASSIGNMENT` / `MANUAL` / `CUSTOM_ASSIGNMENT` | + +### 10.2 行为级对比 + +| 行为 | 修改前 | 修改后 | +|------|--------|--------| +| 查询手工门票行 | 前端需要识别 `CUSTOM_ASSIGNMENT` | 前端按 `MANUAL` 识别手工项目 | +| 保存手工门票行 | 前端需要把手工 Tab 转成 `CUSTOM_ASSIGNMENT` | 前端可直接提交 `MANUAL` | +| 查询结果原样保存 | GET 的旧来源值与页面手工 Tab 值可能不一致 | GET 结果可原样 PUT | +| 旧请求兼容 | 旧 `CUSTOM_ASSIGNMENT` 入参可保存 | 继续可保存,回读统一为 `MANUAL` | + +## 11. 影响评估 / 回滚 + +### 11.1 影响评估 + +- **是否破坏向后兼容**:否。PUT 继续兼容旧 `CUSTOM_ASSIGNMENT` 入参;GET 只统一手工门票来源的展示值。 +- **前端是否必须同步上线**:否。旧保存请求仍可用;但前端可清理 `MANUAL` 与 `CUSTOM_ASSIGNMENT` 互转逻辑。 +- **影响已有数据**:不需要前端处理历史数据;页面以后端返回的 `MANUAL` 为准。 + +### 11.2 回滚方案 + +- 如接口回滚,前端需恢复兼容 GET 返回 `CUSTOM_ASSIGNMENT` 的判断。 +- 回滚后不要把 GET 查询结果中的 `sourceType` 假定为一定可原样提交。 + +## 12. 注意事项 + +- 前端不要再按 Tab 名称把手工项目强制转换成 `CUSTOM_ASSIGNMENT`;新增手工行可以直接传 `MANUAL`。 +- 前端如有 `sourceType === "CUSTOM_ASSIGNMENT"` 才展示手工项目的判断,需要同步兼容或改为判断 `MANUAL`。 +- `CUSTOM_ASSIGNMENT` 仅作为旧保存请求兼容值保留,不应再作为新页面查询展示值。 +- `sourceTypeName` 是展示字段,保存时可不传;保存后以再次查询结果为准。 +- 非法 `sourceType` 仍会返回参数非法,不新增兜底保存。 + +## 13. 关联 / 联系人 + +### 13.1 链接 + +- **Issue**: [#5238](https://git.1814.love:8443/wx/HL/issues/5238) +- **PR**: [#5242](https://git.1814.love:8443/wx/HL/pulls/5242) +- **Merge commit**: [bfb28a258](https://git.1814.love:8443/wx/HL/commit/bfb28a258) + +### 13.2 联系人 + +- **后端负责人**: @yst