diff --git a/changelogs-v2/2026-07/18_5049_核单Step2景区游玩项目字段-修改接口-管理后台.md b/changelogs-v2/2026-07/18_5049_核单Step2景区游玩项目字段-修改接口-管理后台.md new file mode 100644 index 0000000..f905764 --- /dev/null +++ b/changelogs-v2/2026-07/18_5049_核单Step2景区游玩项目字段-修改接口-管理后台.md @@ -0,0 +1,306 @@ +# 【修改接口·管理后台】核单 Step2 景区游玩项目字段 (#5049) + +> **PR**: #5051 | **服务**: hl-order-service-v3 | **更新时间**: 2026-07-18 16:40 + +## 1. 接口背景 + +核单 Step2 的景区/游玩项目核算明细需要展示来源、行程天数、规格/票型、销售单价、销售小计和付款方式中文名。现有接口保留原路径,在原 `GET/PUT /v3/admin/order/{orderId}/settlement/step2` 上做兼容增强。 + +## 2. 变更清单 + +| # | 接口 | 方法 | 路径 | 变更类型 | 说明 | +|---|------|------|------|----------|------| +| 1 | 查询门票/游玩项目核算明细 | GET | `/v3/admin/order/{orderId}/settlement/step2` | 修改接口 | `TicketItemVO` 出参新增 6 个字段 | +| 2 | 保存门票/游玩项目核算明细 | PUT | `/v3/admin/order/{orderId}/settlement/step2` | 修改接口 | `sourceType` 新增 `CUSTOM_ASSIGNMENT`,入参支持保存规格、销售单价、销售小计 | + +## 3. 接口详情 + +### 3.1 查询门票/游玩项目核算明细 + +- **使用场景**:进入核单 Step2 景区/游玩项目页签时查询明细。 +- **认证**:需要管理后台 JWT。 +- **幂等性**:幂等,只读查询。 +- **响应结构**:`data` 为 `TicketItemVO[]` 数组。 + +### 3.2 保存门票/游玩项目核算明细 + +- **使用场景**:保存核单 Step2 景区/游玩项目核算明细。 +- **认证**:需要管理后台 JWT。 +- **幂等性**:全量替换保存;同一份 `items` 重复提交后,以最后一次提交为准。 +- **请求体兼容**:推荐使用 `{ "items": [...] }`;历史数组 body `[...]` 仍兼容。 + +## 4. 接口入参 + +### 4.1 路径参数 + +| 字段 | 类型 | 必填 | 说明 | +|------|------|------|------| +| `orderId` | string | 是 | 订单 ID,长整型字符串 | + +### 4.2 PUT 请求体字段 + +| 字段 | 类型 | 必填 | 说明 | 校验规则 | +|------|------|------|------|----------| +| `items` | array | 是 | 门票/游玩项目明细行数组,全量替换保存 | 不允许为 `null` | +| `items[].id` | string | 否 | 已存在行 ID;全量替换保存时可不传 | 长整型字符串 | +| `items[].sourceType` | string | 是 | 来源类型 | `SCENIC_ASSIGNMENT` / `ACTIVITY_ASSIGNMENT` / `CUSTOM_ASSIGNMENT` | +| `items[].sourceTypeName` | string | 否 | 来源类型中文名,仅展示字段;保存时可不传 | 最大 32 字符 | +| `items[].scenicAssignmentId` | string | 否 | 来源 assignment ID;手工项目传 `null` | 长整型字符串或 `null` | +| `items[].dayNumber` | integer | 否 | 行程第几天;保存时以后端根据 `dayDate` 计算后的值为准 | 从 1 开始 | +| `items[].dayDate` | string | 是 | 行程日期 | `yyyy-MM-dd`,不能早于订单出发日 | +| `items[].scenicName` | string | 是 | 景区/游玩项目名称 | 1-200 字符 | +| `items[].specName` | string | 否 | 规格/票型名称 | 最大 128 字符 | +| `items[].ticketCount` | integer | 是 | 实际购票数量 | 建议非负整数 | +| `items[].ticketUnitPrice` | number | 否 | 参考成本单价,单位元 | 小数 | +| `items[].sellPrice` | number | 否 | 客户成交单价,单位元 | `>= 0` | +| `items[].totalAmount` | number | 否 | 客户成交小计,单位元;为空且有 `sellPrice` 时后端按 `sellPrice * ticketCount` 降级计算 | `>= 0` | +| `items[].plannedCost` | number | 是 | 计划成本,单位元 | `>= 0` | +| `items[].actualCost` | number | 是 | 实际成本,单位元 | `>= 0` | +| `items[].paymentMethod` | string | 否 | 付款方式;为空时默认 `COMPANY_PAID` | `SIGNED` / `COMPANY_PAID` / `CASH_PAID` | +| `items[].paymentMethodName` | string | 否 | 付款方式中文名,仅展示字段;保存时可不传 | 最大 32 字符 | +| `items[].voucherUrls` | array | 否 | 凭证图片 URL 数组 | 字符串数组 | +| `items[].remark` | string | 否 | 备注 | 最大 500 字符 | + +## 5. 出参 + +### 5.1 GET 响应字段:`TicketItemVO` + +| 字段 | 类型 | 说明 | +|------|------|------| +| `id` | string | 核单明细行 ID | +| `sourceType` | string | 来源类型 | +| `sourceTypeName` | string | 来源类型中文名;本次新增 | +| `scenicAssignmentId` | string/null | 来源 assignment ID;手工项目为 `null` | +| `dayNumber` | integer/null | 行程第几天;本次新增 | +| `dayDate` | string | 行程日期,`yyyy-MM-dd` | +| `scenicName` | string | 景区/游玩项目名称 | +| `specName` | string/null | 规格/票型名称;本次新增 | +| `ticketCount` | integer | 实际购票数量 | +| `ticketUnitPrice` | number/null | 参考成本单价 | +| `sellPrice` | number/null | 客户成交单价;本次新增 | +| `totalAmount` | number/null | 客户成交小计;本次新增 | +| `plannedCost` | number | 计划成本 | +| `actualCost` | number | 实际成本 | +| `paymentMethod` | string | 付款方式 | +| `paymentMethodName` | string/null | 付款方式中文名;本次新增 | +| `voucherUrls` | array | 凭证图片 URL 数组 | +| `remark` | string/null | 备注 | + +### 5.2 PUT 响应字段:`SettlementTicketSaveRespVO` + +| 字段 | 类型 | 说明 | +|------|------|------| +| `addedIds` | string[] | 本次保存新增的核单明细行 ID 列表 | +| `updatedIds` | string[] | 本次保存更新的核单明细行 ID 列表;当前全量替换语义下通常为空数组 | +| `deletedIds` | string[] | 本次保存删除的核单明细行 ID 列表;当前返回通常为空数组 | +| `totalActualCost` | string | 保存后 Step2 实际成本合计,单位元 | + +## 6. 枚举 / 数据字典 + +### 6.1 `sourceType` + +**所属字段**:`items[].sourceType`、`data[].sourceType` | **类型**:String | **必填**:是 + +| 值 | 中文 | 说明 | +|----|------|------| +| `SCENIC_ASSIGNMENT` | 景区 | 景区来源行 | +| `ACTIVITY_ASSIGNMENT` | 游玩项目 | 游玩项目来源行 | +| `CUSTOM_ASSIGNMENT` | 手工项目 | 本次新增;核单手工补充行,`scenicAssignmentId` 可为 `null` | + +### 6.2 `paymentMethod` + +**所属字段**:`items[].paymentMethod`、`data[].paymentMethod` | **类型**:String | **必填**:否 + +| 值 | 中文 | 说明 | +|----|------|------| +| `SIGNED` | 签单 | 现场签单 | +| `COMPANY_PAID` | 公司付款 | 公司统一付款;未传 `paymentMethod` 时默认该值 | +| `CASH_PAID` | 现付 | 现场现金/线下现付 | + +## 7. 错误码 + +| code | 含义 | 触发场景 | +|------|------|----------| +| `584011` | 当前核单状态不允许录门票核单 | PUT 保存时,订单不是「待核单」或「核单中」 | +| `100001` | 参数非法 | 字段格式不符合校验,例如 `sourceType` 不在允许枚举内、金额小于 0 | + +## 8. 示例 + +### 8.1 典型成功:GET 查询 + +**请求** + +```http +GET /v3/admin/order/2077233855281971202/settlement/step2 +Authorization: Bearer {token} +``` + +**响应** + +```json +{ + "code": 200, + "message": "成功", + "success": true, + "data": [ + { + "id": "2077328317421187073", + "sourceType": "SCENIC_ASSIGNMENT", + "sourceTypeName": "景区", + "scenicAssignmentId": "2077233886282088450", + "dayNumber": 5, + "dayDate": "2026-07-18", + "scenicName": "呼和诺尔草原旅游区", + "specName": null, + "ticketCount": 1, + "ticketUnitPrice": 59.00, + "sellPrice": null, + "totalAmount": null, + "plannedCost": 59.00, + "actualCost": 59.00, + "paymentMethod": "COMPANY_PAID", + "paymentMethodName": "公司付款", + "voucherUrls": [], + "remark": null + } + ] +} +``` + +### 8.2 边界成功:PUT 保存手工项目 + +**请求** + +```http +PUT /v3/admin/order/2077233855281971202/settlement/step2 +Authorization: Bearer {token} +Content-Type: application/json +``` + +```json +{ + "items": [ + { + "sourceType": "CUSTOM_ASSIGNMENT", + "scenicAssignmentId": null, + "dayDate": "2026-07-18", + "scenicName": "临时补充游玩项目", + "specName": "成人票", + "ticketCount": 2, + "ticketUnitPrice": 12.34, + "sellPrice": 56.78, + "totalAmount": 113.56, + "plannedCost": 24.68, + "actualCost": 24.68, + "paymentMethod": "COMPANY_PAID", + "voucherUrls": [], + "remark": "核单临时补充" + } + ] +} +``` + +**响应** + +```json +{ + "code": 200, + "message": "成功", + "success": true, + "data": { + "addedIds": ["2078398065684692994"], + "updatedIds": [], + "deletedIds": [], + "totalActualCost": "24.68" + } +} +``` + +### 8.3 业务失败:已核单订单禁止保存 + +**请求** + +```http +PUT /v3/admin/order/2077233886248534018/settlement/step2 +Authorization: Bearer {token} +Content-Type: application/json +``` + +```json +{ + "items": [] +} +``` + +**响应** + +```json +{ + "code": 584011, + "message": "当前核单状态为「已核单」,不允许录门票核单,必须为「待核单」或「核单中」", + "data": null, + "success": false +} +``` + +## 9. 业务边界 + +- `PUT` 是全量替换保存;前端保存时应提交页面当前完整明细列表。 +- `CUSTOM_ASSIGNMENT` 表示核单手工补充项目,`scenicAssignmentId` 可以为 `null`。 +- `dayNumber` 保存时以后端根据 `dayDate` 和订单出发日计算的结果为准。 +- `totalAmount` 为空且 `sellPrice` 有值时,后端会按 `sellPrice * ticketCount` 降级计算。 +- 未传 `paymentMethod` 时,后端默认使用 `COMPANY_PAID`。 +- 订单核单状态必须是「待核单」或「核单中」才允许保存 Step2。 + +## 10. 修改前后对比 + +### 10.1 字段级对比 + +| 字段 | 修改前 | 修改后 | +|------|--------|--------| +| `sourceType` | `SCENIC_ASSIGNMENT` / `ACTIVITY_ASSIGNMENT` | 新增 `CUSTOM_ASSIGNMENT` | +| `sourceTypeName` | 无 | 新增,返回来源中文名 | +| `dayNumber` | 无 | 新增,返回行程第几天 | +| `specName` | 无 | 新增,返回/保存规格或票型名称 | +| `sellPrice` | 无 | 新增,返回/保存客户成交单价 | +| `totalAmount` | 无 | 新增,返回/保存客户成交小计 | +| `paymentMethodName` | 无 | 新增,返回付款方式中文名 | + +### 10.2 行为级对比 + +| 行为 | 修改前 | 修改后 | +|------|--------|--------| +| 手工补充项目来源 | 只能用既有来源类型兜底表达 | 可明确传 `CUSTOM_ASSIGNMENT` | +| 手工项目 assignment ID | 前端容易误以为必须有来源 ID | `CUSTOM_ASSIGNMENT` 下 `scenicAssignmentId` 可为 `null` | +| 销售金额展示 | 只能展示成本字段 | 可展示 `sellPrice` / `totalAmount` | + +## 11. 影响评估 / 回滚 + +### 11.1 影响评估 + +- **是否破坏向后兼容**:否。新增字段为兼容性新增;旧字段继续保留。 +- **前端是否必须同步上线**:否。旧页面可继续按原字段展示;需要原型新增列时再读取新字段。 +- **影响已有数据**:不需要前端做数据迁移;历史行新字段可能为 `null`。 + +### 11.2 回滚方案 + +- 回滚接口代码后,前端不要再依赖 `CUSTOM_ASSIGNMENT` 和新增字段。 +- 如已保存手工项目,回滚前应确认旧版本是否能识别该来源类型。 + +## 12. 注意事项 + +- 前端不要把 `sourceTypeName`、`paymentMethodName` 当作提交必填项;它们是展示字段。 +- 前端保存时建议保留并回传用户编辑后的 `specName`、`sellPrice`、`totalAmount`。 +- 已核单订单保存 Step2 会返回 `584011`,这不是接口异常。 + +## 13. 关联 / 联系人 + +### 13.1 链接 + +- **Issue**: [#5049](https://git.1814.love:8443/wx/HL/issues/5049) +- **PR**: [#5051](https://git.1814.love:8443/wx/HL/pulls/5051) +- **Merge commit**: [d60324fde](https://git.1814.love:8443/wx/HL/commit/d60324fde228b50c7ba70d1f39a641e851dc351d) + +### 13.2 联系人 + +- **后端负责人**: @yst