729 行
29 KiB
Markdown
729 行
29 KiB
Markdown
---
|
||
schema: "hl-changelog/v2"
|
||
ticket: "6117"
|
||
title: "导游/摄影核单去 EXCLUDED + 全量替换,确认状态统一 settlementConfirmStatus"
|
||
consumer: "admin"
|
||
change_type: "修改接口"
|
||
author: "yst"
|
||
backend_status: "deployed"
|
||
gateway_status: "verified"
|
||
frontend_status: "pending"
|
||
frontend_owner: ""
|
||
frontend_ref: ""
|
||
target_release: ""
|
||
verified_at: ""
|
||
status_note: "PR #6119 已合并 dev-v3(merge commit 27ac77b500),测试服已验证。破坏性变化:导游/摄影核单废弃「按天候选 + EXCLUDED」机制改全量替换语义;保存入参删 candidateKey/completionState/sourceResolution/excludedCandidateKeys,传旧字段一律 400(584128);查询出参删 candidateKey/completionState/candidateResolution/sourceActive/pendingCandidateCount;确认状态统一 settlementConfirmStatus(UNCONFIRMED/CONFIRMED 二值);blockReasonCode 删 SOURCE_INACTIVE/CANDIDATES_UNRESOLVED 两值。无 DDL。"
|
||
updated_at: "2026-08-21"
|
||
base: "dev-v3"
|
||
---
|
||
|
||
# 【修改接口·管理后台】导游/摄影核单确认状态统一——去 EXCLUDED 改全量替换(#6117)
|
||
|
||
> **PR**: #6119 | **服务**: hl-order-service-v3 | **更新时间**: 2026-08-21
|
||
|
||
## 1. 接口背景
|
||
|
||
订单核单页「导游」「摄影」两个费用 tab 此前使用「按天候选 + EXCLUDED」机制:后端按订单行程天数预生成候选行,前端要在「纳入(INCLUDED)/ 排除(EXCLUDED)/ 未处理(UNRESOLVED)」三种候选处理结果之间来回切换,还要单独维护一份 `excludedCandidateKeys` 排除清单。这套机制字段多、状态绕,和酒店/门票 tab 的「全量保存 + 确认状态」模型完全不一致,前端两套交互逻辑要分别维护。
|
||
|
||
本次变更把导游/摄影 tab 拉齐到酒店/门票同款模型:
|
||
|
||
- 废弃候选机制,保存接口改**全量替换**语义——传当前应存在的全部行,没传的未确认行即删除;
|
||
- 确认状态统一为每行一个 `settlementConfirmStatus`(`UNCONFIRMED` / `CONFIRMED` 二值),保存时可直接把行置为已确认;
|
||
- 已确认行受保护:不能被全量替换顺手删掉,编辑业务字段会自动退回未确认、需重新确认。
|
||
|
||
涉及导游(guide-fees)与摄影(photographer-fees)两组共 6 个接口,两组结构完全同构,仅字段名有差异(导游用 `name`/`serviceType`,摄影用 `photographerName`/`feeType`)。
|
||
|
||
## 2. 变更清单
|
||
|
||
| # | 接口 | 方法 | 路径 | 变更类型 | 说明 |
|
||
|---|------|------|------|----------|------|
|
||
| 1 | 查询导游单层费用明细 | GET | `/v3/admin/order/{orderId}/settlement/guide-fees` | 修改接口 | 出参删 candidateKey/completionState/candidateResolution/sourceActive/pendingCandidateCount |
|
||
| 2 | 全量保存导游单层费用明细 | PUT | `/v3/admin/order/{orderId}/settlement/guide-fees` | 修改接口 | 入参删 candidateKey/completionState/sourceResolution/excludedCandidateKeys,新增 settlementConfirmStatus;改全量替换语义 |
|
||
| 3 | 确认导游单层费用明细 | POST | `/v3/admin/order/{orderId}/settlement/guide-fees/confirm` | 修改接口 | 签名不变(itemIds),确认语义对齐新模型 |
|
||
| 4 | 查询摄影单层费用明细 | GET | `/v3/admin/order/{orderId}/settlement/photographer-fees` | 修改接口 | 同 #1 |
|
||
| 5 | 全量保存摄影单层费用明细 | PUT | `/v3/admin/order/{orderId}/settlement/photographer-fees` | 修改接口 | 同 #2 |
|
||
| 6 | 确认摄影单层费用明细 | POST | `/v3/admin/order/{orderId}/settlement/photographer-fees/confirm` | 修改接口 | 同 #3 |
|
||
|
||
## 3. 接口详情
|
||
|
||
### 3.1 查询导游单层费用明细(GET guide-fees)
|
||
|
||
- **使用场景**:打开核单页「导游」tab 时加载费用明细列表与分类汇总
|
||
- **认证**:管理后台 JWT(房务角色只读拦截,返回 403)
|
||
- **幂等性**:是(只读)
|
||
- **限流**:无
|
||
|
||
**入参**
|
||
|
||
| 字段 | 类型 | 必填 | 说明 |
|
||
|------|------|------|------|
|
||
| orderId | Long(路径参数) | ✅ | 订单 ID,必须大于 0 |
|
||
|
||
**出参**(`Result<SettlementGuideFeesRespVO>`)
|
||
|
||
响应级字段:
|
||
|
||
| 字段 | 类型 | 说明 |
|
||
|------|------|------|
|
||
| category | String | 核算分类,恒为 `GUIDE` |
|
||
| totalAmount | String | 纳入核算的费用合计,金额字符串,如 `500.00` |
|
||
| cashPaidAmount | String | 现付费用合计,金额字符串 |
|
||
| unconfirmedCount | Integer | 未确认明细数(全行口径,见 §10.2) |
|
||
| settlementReady | Boolean | 是否满足本分类提交核单条件 |
|
||
| blockReasonCode | String | 阻断原因码;无阻断时为 null,取值见 §6.4 |
|
||
| items | Array | 费用明细数组;无数据返回空数组 |
|
||
| editable | Boolean | 当前订单是否允许编辑本分类 |
|
||
| readOnlyReasonCode | String | 只读原因码;可编辑时为 null |
|
||
|
||
items[] 行字段:
|
||
|
||
| 字段 | 类型 | 说明 |
|
||
|------|------|------|
|
||
| id | String | 费用明细 ID(Long 序列化为字符串);未落库的预填草稿行为 null |
|
||
| staffAssignmentId | String | 人员分配 ID;手工新增行为 null |
|
||
| serviceDate | String | 服务日期 `YYYY-MM-DD`;预填草稿行为 null |
|
||
| name | String | 导游姓名 |
|
||
| serviceType | String | 服务类型,取值见 §6.1 |
|
||
| serviceTypeName | String | 服务类型中文名;serviceType 为 null 时为 null |
|
||
| paymentMethod | String | 付款方式,取值见 §6.3 |
|
||
| paymentMethodName | String | 付款方式中文名;paymentMethod 为 null 时为 null |
|
||
| amount | String | 金额字符串;预填草稿行为 null |
|
||
| settlementConfirmStatus | String | 核单确认状态,取值见 §6.5 |
|
||
| settlementConfirmStatusName | String | 核单确认状态中文名 |
|
||
| remark | String | 备注;无备注为 null |
|
||
| sourceType | String | 来源类型:`STAFF_ASSIGNMENT` 人员安排 / `MANUAL` 手工 / `SYSTEM` 系统 |
|
||
| sourceTypeName | String | 来源类型中文名 |
|
||
| voucherUrls | Array<String> | 凭证 URL;无凭证返回空数组 |
|
||
|
||
**错误码**:见 §7 全组共用错误码表。
|
||
|
||
**业务边界**:首次查询(订单尚无该角色核单行)时,返回按订单人员分配预填的草稿行(不落库,行特征:`id` 为 null、`settlementConfirmStatus=UNCONFIRMED`);这些预填行会被计入 `unconfirmedCount`,影响 `settlementReady` 展示口径。
|
||
|
||
**示例(典型成功)**
|
||
|
||
请求:
|
||
```
|
||
GET /v3/admin/order/12345/settlement/guide-fees
|
||
Authorization: Bearer {admin-token}
|
||
(无请求体)
|
||
```
|
||
|
||
响应:
|
||
```json
|
||
{
|
||
"code": 200,
|
||
"data": {
|
||
"category": "GUIDE",
|
||
"totalAmount": "800.00",
|
||
"cashPaidAmount": "300.00",
|
||
"unconfirmedCount": 1,
|
||
"settlementReady": false,
|
||
"blockReasonCode": "ITEMS_UNCONFIRMED",
|
||
"editable": true,
|
||
"readOnlyReasonCode": null,
|
||
"items": [
|
||
{
|
||
"id": "9001",
|
||
"staffAssignmentId": "11",
|
||
"serviceDate": "2026-08-03",
|
||
"name": "导游甲",
|
||
"serviceType": "FULL_COURSE_GUIDE",
|
||
"serviceTypeName": "全陪导游",
|
||
"paymentMethod": "COMPANY_PAID",
|
||
"paymentMethodName": "公司支付",
|
||
"amount": "500.00",
|
||
"settlementConfirmStatus": "CONFIRMED",
|
||
"settlementConfirmStatusName": "已确认",
|
||
"remark": null,
|
||
"sourceType": "STAFF_ASSIGNMENT",
|
||
"sourceTypeName": "人员安排",
|
||
"voucherUrls": []
|
||
},
|
||
{
|
||
"id": "9002",
|
||
"staffAssignmentId": null,
|
||
"serviceDate": "2026-08-04",
|
||
"name": "导游乙",
|
||
"serviceType": "LOCAL_GUIDE",
|
||
"serviceTypeName": "地接导游",
|
||
"paymentMethod": "CASH_PAID",
|
||
"paymentMethodName": "现付",
|
||
"amount": "300.00",
|
||
"settlementConfirmStatus": "UNCONFIRMED",
|
||
"settlementConfirmStatusName": "未确认",
|
||
"remark": "现场临时请的地陪",
|
||
"sourceType": "MANUAL",
|
||
"sourceTypeName": "手工",
|
||
"voucherUrls": ["https://oss.example.com/voucher/a.jpg"]
|
||
}
|
||
]
|
||
},
|
||
"msg": "",
|
||
"success": true
|
||
}
|
||
```
|
||
|
||
**示例(边界:首次查询返回预填草稿行)**
|
||
|
||
场景说明:订单分配了 1 名导游但从未保存过核单行,GET 返回不落库的预填草稿(`id`/`serviceDate`/`amount` 为 null)。
|
||
|
||
响应:
|
||
```json
|
||
{
|
||
"code": 200,
|
||
"data": {
|
||
"category": "GUIDE",
|
||
"totalAmount": "0.00",
|
||
"cashPaidAmount": "0.00",
|
||
"unconfirmedCount": 1,
|
||
"settlementReady": false,
|
||
"blockReasonCode": "ITEMS_UNCONFIRMED",
|
||
"editable": true,
|
||
"readOnlyReasonCode": null,
|
||
"items": [
|
||
{
|
||
"id": null,
|
||
"staffAssignmentId": "11",
|
||
"serviceDate": null,
|
||
"name": "导游甲",
|
||
"serviceType": null,
|
||
"serviceTypeName": null,
|
||
"paymentMethod": null,
|
||
"paymentMethodName": null,
|
||
"amount": null,
|
||
"settlementConfirmStatus": "UNCONFIRMED",
|
||
"settlementConfirmStatusName": "未确认",
|
||
"remark": null,
|
||
"sourceType": "STAFF_ASSIGNMENT",
|
||
"sourceTypeName": "人员安排",
|
||
"voucherUrls": []
|
||
}
|
||
]
|
||
},
|
||
"msg": "",
|
||
"success": true
|
||
}
|
||
```
|
||
|
||
### 3.2 全量保存导游单层费用明细(PUT guide-fees)
|
||
|
||
- **使用场景**:在「导游」tab 编辑完费用明细后整体保存(新增 / 修改 / 删除行都通过本接口一次性提交)
|
||
- **认证**:管理后台 JWT
|
||
- **幂等性**:是(全量替换语义,同一 body 重放结果一致)
|
||
- **限流**:无
|
||
|
||
**入参**
|
||
|
||
路径参数:`orderId`(Long,必填,订单 ID)。
|
||
|
||
请求体字段:
|
||
|
||
| 字段 | 类型 | 必填 | 说明 | 校验规则 |
|
||
|------|------|------|------|----------|
|
||
| items | Array<ItemVO> | ✅ | 导游费用明细**全量集合**(当前应存在的全部行) | 最多 200 条,超出报 584121 |
|
||
|
||
ItemVO 字段:
|
||
|
||
| 字段 | 类型 | 必填 | 说明 | 校验规则 |
|
||
|------|------|------|------|----------|
|
||
| id | String | ❌ | 费用明细 ID;已存在行必传,新增行不传 | 必须传字符串形式 |
|
||
| staffAssignmentId | String | ❌ | 人员分配 ID;关联人员分配的行传 | 非空时必须属本单且角色为导游,否则报 584115 |
|
||
| serviceDate | String | ❌ | 服务日期 `YYYY-MM-DD` | 必须在订单行程范围内,否则报 584112;订单出发/返程日期缺失时报 584123 |
|
||
| name | String | ❌ | 导游姓名 | 最长 64 字符 |
|
||
| serviceType | String | ❌ | 服务类型 | 取值见 §6.1,非法值报 584113 |
|
||
| paymentMethod | String | ❌ | 付款方式 | 取值见 §6.3 |
|
||
| amount | String | ❌ | 金额字符串 | 0 至 99999999.99 且最多两位小数,否则报 584114 |
|
||
| remark | String | ❌ | 备注;无备注传 null | 最长 500 字符 |
|
||
| voucherUrls | Array<String> | ❌ | 凭证 URL | 最多 9 个,单条最长 1024;重复值去重并保持首次出现顺序 |
|
||
| settlementConfirmStatus | String | ❌ | 核单确认状态;**缺省/null 按 UNCONFIRMED 处理**,保存时可直接置 CONFIRMED | 仅允许 `UNCONFIRMED` / `CONFIRMED`,其他值报 584128 |
|
||
|
||
**严格模式**:请求体(顶层或行内)出现任何未定义字段一律 400。改前旧字段 `candidateKey` / `completionState` / `sourceResolution` / `excludedCandidateKeys` 现已删除,**传了同样 400**(错误码 584128,msg 形如 `导游或摄影费用请求字段不合法:导游费用明细不支持字段: candidateKey`)。
|
||
|
||
**出参**:同 3.1 的响应结构(保存成功后返回最新全量)。
|
||
|
||
**错误码**:见 §7。
|
||
|
||
**业务边界**(全量替换语义):
|
||
|
||
- 本次请求传入的 `items` 即保存后的全部行;**库里存在但未传入的未确认行会被删除**;
|
||
- **已 CONFIRMED 行不可删除**——若库里某行已确认但本次未传,报 584120「已确认的有效费用不能直接删除,请先进入编辑状态」,整单保存失败;
|
||
- 已 CONFIRMED 行若本次修改了业务字段(姓名/金额/日期/类型/付款方式等),保存后自动重置回 `UNCONFIRMED`,需重新确认;
|
||
- 新增行可在保存时直接置 `CONFIRMED`(与酒店/门票 tab 行为一致)。
|
||
|
||
**示例(典型成功:两行全量保存,一行直接置已确认)**
|
||
|
||
请求:
|
||
```json
|
||
PUT /v3/admin/order/12345/settlement/guide-fees
|
||
Authorization: Bearer {admin-token}
|
||
|
||
{
|
||
"items": [
|
||
{
|
||
"id": "9001",
|
||
"staffAssignmentId": "11",
|
||
"serviceDate": "2026-08-03",
|
||
"name": "导游甲",
|
||
"serviceType": "FULL_COURSE_GUIDE",
|
||
"paymentMethod": "COMPANY_PAID",
|
||
"amount": "500.00",
|
||
"remark": null,
|
||
"voucherUrls": [],
|
||
"settlementConfirmStatus": "CONFIRMED"
|
||
},
|
||
{
|
||
"serviceDate": "2026-08-04",
|
||
"name": "导游乙",
|
||
"serviceType": "LOCAL_GUIDE",
|
||
"paymentMethod": "CASH_PAID",
|
||
"amount": "300.00",
|
||
"remark": "现场临时请的地陪",
|
||
"voucherUrls": ["https://oss.example.com/voucher/a.jpg"]
|
||
}
|
||
]
|
||
}
|
||
```
|
||
|
||
响应:`code=200`,`data` 为保存后的最新全量(结构同 3.1 响应示例)。
|
||
|
||
**示例(业务失败:误传已删除的旧字段)**
|
||
|
||
场景说明:前端未清理旧逻辑,行内仍带 `candidateKey`。
|
||
|
||
请求:
|
||
```json
|
||
PUT /v3/admin/order/12345/settlement/guide-fees
|
||
|
||
{
|
||
"items": [
|
||
{
|
||
"candidateKey": "2026-08-03#11",
|
||
"name": "导游甲",
|
||
"amount": "500.00"
|
||
}
|
||
]
|
||
}
|
||
```
|
||
|
||
响应:
|
||
```json
|
||
{
|
||
"code": 584128,
|
||
"data": null,
|
||
"msg": "导游或摄影费用请求字段不合法:导游费用明细不支持字段: candidateKey",
|
||
"success": false
|
||
}
|
||
```
|
||
|
||
**示例(业务失败:已确认行被全量替换遗漏)**
|
||
|
||
场景说明:库中行 9001 已 CONFIRMED,本次 items 只传了行 9002,相当于要删掉 9001。
|
||
|
||
响应:
|
||
```json
|
||
{
|
||
"code": 584120,
|
||
"data": null,
|
||
"msg": "已确认的有效费用不能直接删除,请先进入编辑状态",
|
||
"success": false
|
||
}
|
||
```
|
||
|
||
### 3.3 确认导游单层费用明细(POST guide-fees/confirm)
|
||
|
||
- **使用场景**:勾选若干未确认行后点「确认」,将这批行批量置为已确认
|
||
- **认证**:管理后台 JWT
|
||
- **幂等性**:是(对已确认行重复确认无副作用)
|
||
- **限流**:无
|
||
|
||
**入参**
|
||
|
||
路径参数:`orderId`(Long,必填)。
|
||
|
||
请求体字段:
|
||
|
||
| 字段 | 类型 | 必填 | 说明 | 校验规则 |
|
||
|------|------|------|------|----------|
|
||
| itemIds | Array<String> | ✅ | 待确认的费用明细 ID 列表,JSON 中每项为字符串 | 1 至 200 条;ID 不存在或不属于本订单及导游角色报 584111 |
|
||
|
||
同样走严格模式,多传字段报 584128(msg 形如 `导游费用确认请求不支持字段: xxx`)。
|
||
|
||
**出参**:同 3.1 的响应结构(确认后的最新全量)。
|
||
|
||
**示例(典型成功)**
|
||
|
||
请求:
|
||
```json
|
||
POST /v3/admin/order/12345/settlement/guide-fees/confirm
|
||
Authorization: Bearer {admin-token}
|
||
|
||
{
|
||
"itemIds": ["9002"]
|
||
}
|
||
```
|
||
|
||
响应:
|
||
```json
|
||
{
|
||
"code": 200,
|
||
"data": {
|
||
"category": "GUIDE",
|
||
"totalAmount": "800.00",
|
||
"cashPaidAmount": "300.00",
|
||
"unconfirmedCount": 0,
|
||
"settlementReady": true,
|
||
"blockReasonCode": null,
|
||
"editable": true,
|
||
"readOnlyReasonCode": null,
|
||
"items": [
|
||
{
|
||
"id": "9002",
|
||
"staffAssignmentId": null,
|
||
"serviceDate": "2026-08-04",
|
||
"name": "导游乙",
|
||
"serviceType": "LOCAL_GUIDE",
|
||
"serviceTypeName": "地接导游",
|
||
"paymentMethod": "CASH_PAID",
|
||
"paymentMethodName": "现付",
|
||
"amount": "300.00",
|
||
"settlementConfirmStatus": "CONFIRMED",
|
||
"settlementConfirmStatusName": "已确认",
|
||
"remark": "现场临时请的地陪",
|
||
"sourceType": "MANUAL",
|
||
"sourceTypeName": "手工",
|
||
"voucherUrls": ["https://oss.example.com/voucher/a.jpg"]
|
||
}
|
||
]
|
||
},
|
||
"msg": "",
|
||
"success": true
|
||
}
|
||
```
|
||
|
||
### 3.4 查询摄影单层费用明细(GET photographer-fees)
|
||
|
||
- **使用场景**:打开核单页「摄影」tab 时加载费用明细列表与分类汇总
|
||
- **认证 / 幂等 / 限流**:同 3.1
|
||
|
||
**入参**:同 3.1(`orderId` 路径参数)。
|
||
|
||
**出参**(`Result<SettlementPhotographerFeesRespVO>`):响应级字段与 3.1 完全一致(`category` 恒为 `PHOTOGRAPHER`),items[] 行字段仅以下差异,其余字段同 3.1:
|
||
|
||
| 字段 | 类型 | 说明 |
|
||
|------|------|------|
|
||
| photographerName | String | 摄影姓名(对应导游组的 `name`) |
|
||
| feeType | String | 摄影费用类型,取值见 §6.2 |
|
||
| feeTypeName | String | 摄影费用类型中文名;feeType 为 null 时为 null |
|
||
|
||
(行内不再有 `name` / `serviceType` / `serviceTypeName`。)
|
||
|
||
**示例(典型成功)**
|
||
|
||
请求:
|
||
```
|
||
GET /v3/admin/order/12345/settlement/photographer-fees
|
||
Authorization: Bearer {admin-token}
|
||
(无请求体)
|
||
```
|
||
|
||
响应:
|
||
```json
|
||
{
|
||
"code": 200,
|
||
"data": {
|
||
"category": "PHOTOGRAPHER",
|
||
"totalAmount": "300.00",
|
||
"cashPaidAmount": "0.00",
|
||
"unconfirmedCount": 0,
|
||
"settlementReady": true,
|
||
"blockReasonCode": null,
|
||
"editable": true,
|
||
"readOnlyReasonCode": null,
|
||
"items": [
|
||
{
|
||
"id": "9101",
|
||
"staffAssignmentId": "12",
|
||
"serviceDate": "2026-08-03",
|
||
"photographerName": "摄影甲",
|
||
"feeType": "FOLLOW_SHOOT",
|
||
"feeTypeName": "跟拍",
|
||
"paymentMethod": "SIGNED",
|
||
"paymentMethodName": "签单",
|
||
"amount": "300.00",
|
||
"settlementConfirmStatus": "CONFIRMED",
|
||
"settlementConfirmStatusName": "已确认",
|
||
"remark": null,
|
||
"sourceType": "STAFF_ASSIGNMENT",
|
||
"sourceTypeName": "人员安排",
|
||
"voucherUrls": []
|
||
}
|
||
]
|
||
},
|
||
"msg": "",
|
||
"success": true
|
||
}
|
||
```
|
||
|
||
首次查询同样返回按人员分配预填的草稿行(不落库,`UNCONFIRMED`),行为同 3.1 边界示例。
|
||
|
||
### 3.5 全量保存摄影单层费用明细(PUT photographer-fees)
|
||
|
||
- **使用场景 / 认证 / 幂等 / 限流**:同 3.2
|
||
|
||
**入参**:结构同 3.2,ItemVO 字段仅以下差异,其余(含 `settlementConfirmStatus`、严格模式、全量替换语义、584120 保护、已确认行编辑自动退回未确认)完全一致:
|
||
|
||
| 字段 | 类型 | 必填 | 说明 | 校验规则 |
|
||
|------|------|------|------|----------|
|
||
| photographerName | String | ❌ | 摄影姓名 | 最长 64 字符 |
|
||
| feeType | String | ❌ | 摄影费用类型 | 取值见 §6.2,非法值报 584113 |
|
||
|
||
(不再有 `name` / `serviceType`;`staffAssignmentId` 非空时角色须为摄影,否则报 584115。)
|
||
|
||
**示例(典型成功)**
|
||
|
||
请求:
|
||
```json
|
||
PUT /v3/admin/order/12345/settlement/photographer-fees
|
||
Authorization: Bearer {admin-token}
|
||
|
||
{
|
||
"items": [
|
||
{
|
||
"id": "9101",
|
||
"staffAssignmentId": "12",
|
||
"serviceDate": "2026-08-03",
|
||
"photographerName": "摄影甲",
|
||
"feeType": "FOLLOW_SHOOT",
|
||
"paymentMethod": "SIGNED",
|
||
"amount": "300.00",
|
||
"settlementConfirmStatus": "CONFIRMED"
|
||
}
|
||
]
|
||
}
|
||
```
|
||
|
||
响应:`code=200`,`data` 为保存后的最新全量(结构同 3.4 响应示例)。
|
||
|
||
**示例(业务失败:金额非法)**
|
||
|
||
请求:
|
||
```json
|
||
PUT /v3/admin/order/12345/settlement/photographer-fees
|
||
|
||
{
|
||
"items": [
|
||
{
|
||
"photographerName": "摄影甲",
|
||
"feeType": "FOLLOW_SHOOT",
|
||
"amount": "-50.00"
|
||
}
|
||
]
|
||
}
|
||
```
|
||
|
||
响应:
|
||
```json
|
||
{
|
||
"code": 584114,
|
||
"data": null,
|
||
"msg": "金额必须为 0 至 99999999.99 且最多两位小数",
|
||
"success": false
|
||
}
|
||
```
|
||
|
||
### 3.6 确认摄影单层费用明细(POST photographer-fees/confirm)
|
||
|
||
- **使用场景 / 认证 / 幂等 / 限流**:同 3.3
|
||
|
||
**入参**:同 3.3(`itemIds` 字符串数组,1-200 条;ID 不存在或不属于本订单及摄影角色报 584111;多传字段报 584128,msg 形如 `摄影费用确认请求不支持字段: xxx`)。
|
||
|
||
**出参**:同 3.4 的响应结构。
|
||
|
||
**示例(典型成功)**
|
||
|
||
请求:
|
||
```json
|
||
POST /v3/admin/order/12345/settlement/photographer-fees/confirm
|
||
Authorization: Bearer {admin-token}
|
||
|
||
{
|
||
"itemIds": ["9101"]
|
||
}
|
||
```
|
||
|
||
响应:`code=200`,`data` 中该行 `settlementConfirmStatus` 变为 `CONFIRMED`、`unconfirmedCount` 相应减少(结构同 3.4 响应示例)。
|
||
|
||
## 4. 接口入参
|
||
|
||
已按接口分散在 §3.1 ~ §3.6 各自小节内(本组为多接口 changelog,入参不单独集中成节)。
|
||
|
||
## 5. 出参(响应)
|
||
|
||
已按接口分散在 §3.1 ~ §3.6 各自小节内。
|
||
|
||
## 6. 枚举 / 数据字典
|
||
|
||
### 6.1 serviceType(导游服务类型)
|
||
|
||
**所属字段**:导游组 ItemVO 的 `serviceType` | **类型**:`String` | **必填**:❌
|
||
|
||
| 值 | 中文 | 说明 |
|
||
|----|------|------|
|
||
| `FULL_COURSE_GUIDE` | 全陪导游 | — |
|
||
| `LOCAL_GUIDE` | 地接导游 | — |
|
||
| `COMMENTARY_SERVICE` | 讲解服务 | — |
|
||
| `TEMPORARY_SUPPLEMENT` | 临时补录 | — |
|
||
|
||
### 6.2 feeType(摄影费用类型)
|
||
|
||
**所属字段**:摄影组 ItemVO 的 `feeType` | **类型**:`String` | **必填**:❌
|
||
|
||
| 值 | 中文 | 说明 |
|
||
|----|------|------|
|
||
| `FOLLOW_SHOOT` | 跟拍 | — |
|
||
| `PORTRAIT` | 写真 | — |
|
||
| `AERIAL_SHOOT` | 航拍 | — |
|
||
| `EDITING_DELIVERY` | 剪辑出片 | — |
|
||
| `CAMERA_DRONE` | 相机/无人机 | — |
|
||
| `OTHER` | 其他 | — |
|
||
|
||
### 6.3 paymentMethod(付款方式)
|
||
|
||
**所属字段**:两组 ItemVO 的 `paymentMethod` | **类型**:`String` | **必填**:❌
|
||
|
||
| 值 | 中文 | 说明 |
|
||
|----|------|------|
|
||
| `COMPANY_PAID` | 公司支付 | — |
|
||
| `CASH_PAID` | 现付 | 计入 `cashPaidAmount` |
|
||
| `SIGNED` | 签单 | — |
|
||
|
||
### 6.4 blockReasonCode(阻断原因码)
|
||
|
||
**所属字段**:响应级 `blockReasonCode` | **类型**:`String` | **必填**:❌(无阻断时为 null)
|
||
|
||
| 值 | 中文 | 说明 |
|
||
|----|------|------|
|
||
| `ORDER_DATE_INCOMPLETE` | 订单日期不完整 | 订单出发或返程日期缺失,无法校验服务日期 |
|
||
| `ITEMS_UNCONFIRMED` | 存在未确认明细 | 存在 `UNCONFIRMED` 行(含预填草稿行),不满足提交核单条件 |
|
||
|
||
> ⚠️ 本次变更**删除**两个旧值:`SOURCE_INACTIVE`、`CANDIDATES_UNRESOLVED`,后端不再返回。
|
||
|
||
### 6.5 settlementConfirmStatus(核单确认状态)
|
||
|
||
**所属字段**:两组 ItemVO 的 `settlementConfirmStatus`(入参 + 出参) | **类型**:`String` | **必填**:❌(入参缺省按 UNCONFIRMED)
|
||
|
||
| 值 | 中文 | 说明 |
|
||
|----|------|------|
|
||
| `UNCONFIRMED` | 未确认 | 可被全量替换删除 |
|
||
| `CONFIRMED` | 已确认 | 不可删除;编辑业务字段自动退回 UNCONFIRMED |
|
||
|
||
## 7. 错误码
|
||
|
||
| code | 含义 | 触发场景 |
|
||
|------|------|----------|
|
||
| 584111 | 费用明细不存在或不属于当前订单及角色 | confirm 的 itemIds 含不存在/他单/角色不符的行 |
|
||
| 584112 | 服务日期不在订单行程范围内 | 保存时 serviceDate 超出订单出发~返程日期 |
|
||
| 584113 | 服务类型或摄影费用类型不合法 | serviceType / feeType 传了枚举外值 |
|
||
| 584114 | 金额必须为 0 至 99999999.99 且最多两位小数 | amount 为负数、超限或小数位超过 2 位 |
|
||
| 584115 | 人员分配不存在、角色不匹配或来源已失效 | staffAssignmentId 非空但不属本单,或角色不匹配(导游组传了摄影人员等) |
|
||
| 584120 | 已确认的有效费用不能直接删除,请先进入编辑状态 | 全量保存时遗漏了库里已 CONFIRMED 的行 |
|
||
| 584121 | 单个导游或摄影分类最多 200 条明细 | items 超过 200 条 |
|
||
| 584123 | 订单出发或返程日期缺失,无法校验服务日期 | 保存时订单日期不完整且传了 serviceDate |
|
||
| 584125 | 导游或摄影费用请求字段不合法 | 请求体缺失或整体解析失败(无具体字段原因时) |
|
||
| 584128 | 导游或摄影费用请求字段不合法:{0} | 请求体(顶层或行内)出现未定义字段(含已删除的旧字段 candidateKey / completionState / sourceResolution / excludedCandidateKeys);msg 带具体字段名 |
|
||
|
||
## 8. 示例(典型 / 边界 / 异常)
|
||
|
||
已按接口分散在 §3.1 ~ §3.6 各自小节内,每组示例均含请求 + 响应:
|
||
|
||
- 典型成功:§3.1 / §3.2 / §3.3 / §3.4 / §3.5 / §3.6
|
||
- 边界(首次查询预填草稿行,id/serviceDate/amount 为 null):§3.1 第二组示例
|
||
- 业务失败(584128 旧字段误传 / 584120 已确认行误删 / 584114 金额非法):§3.2 / §3.5
|
||
|
||
## 9. 业务边界
|
||
|
||
- ✅ **适用场景**:订单核单进行中(editable=true)时可调保存/确认接口;查询接口在核单各阶段均可调(只读阶段 editable=false + readOnlyReasonCode 指明原因)
|
||
- ❌ **不适用场景**:订单已提交核单终态后,保存/确认接口被拒(editable=false 时调用按只读规则拦截);房务角色(house)调本组接口返回 403
|
||
- ⚠️ **特殊边界 1(预填草稿行)**:首次 GET 返回的草稿行不落库(id 为 null),但计入 `unconfirmedCount` 影响 `settlementReady` 展示;而「完成核单」的提交门禁只查已落库行,两个口径有意分离(展示口径 ≠ 提交口径)
|
||
- ⚠️ **特殊边界 2(已确认行保护)**:已 CONFIRMED 行想删除,必须先修改该行业务字段使其退回 UNCONFIRMED(或走反确认流程),再在下一轮全量保存中不传该行
|
||
- ⚠️ **特殊边界 3(金额 0)**:amount 允许 `0.00`,不被非负校验拦截
|
||
|
||
## 10. 修改前后对比
|
||
|
||
### 10.1 字段级对比
|
||
|
||
保存入参(PUT,ItemVO 行内):
|
||
|
||
| 字段 | 改前 | 改后 |
|
||
|------|------|------|
|
||
| candidateKey | 有,候选行标识 | **删除**,传了报 400(584128) |
|
||
| completionState | 有(NEEDS_INPUT/COMPLETE/EXCLUDED) | **删除**,传了报 400(584128) |
|
||
| sourceResolution | 有,来源处理结果 | **删除**,传了报 400(584128) |
|
||
| settlementConfirmStatus | 无 | **新增**,非必填,UNCONFIRMED/CONFIRMED,缺省按 UNCONFIRMED |
|
||
|
||
保存入参(PUT,请求体顶层):
|
||
|
||
| 字段 | 改前 | 改后 |
|
||
|------|------|------|
|
||
| excludedCandidateKeys | 有,排除候选 key 清单 | **删除**,传了报 400(584128) |
|
||
|
||
查询出参(GET,ItemVO 行内):
|
||
|
||
| 字段 | 改前 | 改后 |
|
||
|------|------|------|
|
||
| candidateKey | 有 | **删除**,不再下发 |
|
||
| completionState | 有 | **删除**,不再下发 |
|
||
| candidateResolution | 有(INCLUDED/EXCLUDED/UNRESOLVED) | **删除**,不再下发 |
|
||
| sourceActive | 有 | **删除**,不再下发 |
|
||
| settlementConfirmStatus / settlementConfirmStatusName | 有(仅展示) | 保留,语义不变 |
|
||
|
||
查询出参(GET,响应级):
|
||
|
||
| 字段 | 改前 | 改后 |
|
||
|------|------|------|
|
||
| pendingCandidateCount | 有,未处理候选数 | **删除**,不再下发 |
|
||
| blockReasonCode 值域 | 含 `SOURCE_INACTIVE`、`CANDIDATES_UNRESOLVED` | **删除这两个值**,只保留 `ORDER_DATE_INCOMPLETE`、`ITEMS_UNCONFIRMED` |
|
||
|
||
### 10.2 行为级对比
|
||
|
||
| 行为 | 改前 | 改后 |
|
||
|------|------|------|
|
||
| 保存语义 | 候选机制:行按 candidateKey 对齐候选,配合 excludedCandidateKeys 声明排除 | **全量替换**:items 即保存后的全部行,未传的未确认行删除 |
|
||
| 确认入口 | 只能通过 confirm 接口把 INCLUDED 行置已确认 | confirm 接口保留;**保存时也可直接置 CONFIRMED**(settlementConfirmStatus 入参) |
|
||
| 已确认行删除 | 通过 excludedCandidateKeys 排除 | 不可删:全量保存遗漏已确认行报 584120 |
|
||
| 已确认行编辑 | 编辑后走候选重算 | 编辑业务字段**自动退回 UNCONFIRMED**,需重新确认 |
|
||
| unconfirmedCount 口径 | 未确认的 INCLUDED 明细数 | 未确认明细数(全行口径,含预填草稿行) |
|
||
| 未处理候选 | 前端要处理 UNRESOLVED 候选(pendingCandidateCount > 0 阻断提交) | 机制删除,无此概念 |
|
||
| 旧字段容错 | 未知字段按 Jackson 默认处理 | **严格模式**:任何未定义字段(含全部旧字段)一律 400 |
|
||
|
||
## 11. 影响评估 / 回滚
|
||
|
||
### 11.1 影响评估
|
||
|
||
- **是否破坏向后兼容**:**是**。保存接口传旧字段(candidateKey / completionState / sourceResolution / excludedCandidateKeys)从「生效」变为「一律 400」;查询出参删 5 个字段;blockReasonCode 删 2 个枚举值。
|
||
- **前端是否必须同步上线**:**是**。导游/摄影 tab 的保存请求必须清掉旧字段,否则保存全部 400;出参侧引用 candidateKey / completionState / candidateResolution / sourceActive / pendingCandidateCount 的渲染与逻辑必须同步删除。
|
||
|
||
### 11.2 回滚方案
|
||
|
||
- **回滚方式**:后端 revert PR #6119 即可恢复旧候选机制契约;无 DDL、无数据迁移,回滚不涉及数据清理
|
||
- **前端配合**:前端若已按新契约上线,后端回滚时需同步回退前端版本(新旧契约互不兼容)
|
||
|
||
## 12. 注意事项
|
||
|
||
- **前端 workaround 清理点**:此前为候选机制写的 workaround 可全部删除——按 candidateKey 对齐行的本地映射、excludedCandidateKeys 的收集逻辑、对 candidateResolution=UNRESOLVED 行的特殊渲染、等待 pendingCandidateCount 归零的轮询/重试逻辑
|
||
- **行内确认状态直接用 settlementConfirmStatus**:新模型下「行是否已确认」只看 `settlementConfirmStatus`,不要再拼接 completionState + candidateResolution 推断
|
||
- **保存即全量**:局部更新场景也必须先 GET 拿全量、改完整体 PUT,缺行等于删行(未确认行)
|
||
- **ID 一律字符串**:入参 id / staffAssignmentId / itemIds 均传字符串形式;出参 id / staffAssignmentId 也是字符串,不要按 Number 解析
|
||
|
||
## 13. 关联 / 联系人
|
||
|
||
### 13.1 链接
|
||
|
||
- **Issue**: [#6117](https://git.1814.love:8443/wx/HL/issues/6117)
|
||
- **PR**: [#6119](https://git.1814.love:8443/wx/HL/pulls/6119)
|
||
- **Merge commit**: [27ac77b500](https://git.1814.love:8443/wx/HL/commit/27ac77b500ff53496432a0e6e869dc96669e8f24)
|
||
|
||
### 13.2 联系人
|
||
|
||
- **后端负责人**: @yst(腰苏图)
|