hl-api-changelog/changelogs-v2/2026-08/21_6117_导游摄影核单确认状态统一-修改接口-管理后台.md
Mimingguang d2eb4cb817
一些检查失败了
changelog-filename-gate / validate (push) Failing after 2s
docs(changelog): #6117 前端 verified(mmg, ref ec7f2a9a)
2026-08-21 12:00:11 +08:00

29 KiB

schema, ticket, title, consumer, change_type, author, backend_status, gateway_status, frontend_status, frontend_owner, frontend_ref, target_release, verified_at, status_note, updated_at, base
schema ticket title consumer change_type author backend_status gateway_status frontend_status frontend_owner frontend_ref target_release verified_at status_note updated_at base
hl-changelog/v2 6117 导游/摄影核单去 EXCLUDED + 全量替换,确认状态统一 settlementConfirmStatus admin 修改接口 yst deployed verified verified mmg ec7f2a9a 2026-08-21 PR #6119 已合并 dev-v3merge commit 27ac77b500,测试服已验证。破坏性变化导游/摄影核单废弃「按天候选 + EXCLUDED」机制改全量替换语义;保存入参删 candidateKey/completionState/sourceResolution/excludedCandidateKeys,传旧字段一律 400584128;查询出参删 candidateKey/completionState/candidateResolution/sourceActive/pendingCandidateCount;确认状态统一 settlementConfirmStatusUNCONFIRMED/CONFIRMED 二值;blockReasonCode 删 SOURCE_INACTIVE/CANDIDATES_UNRESOLVED 两值。无 DDL。 2026-08-21 dev-v3

【修改接口·管理后台】导游/摄影核单确认状态统一——去 EXCLUDED 改全量替换(#6117

PR: #6119 | 服务: hl-order-service-v3 | 更新时间: 2026-08-21

1. 接口背景

订单核单页「导游」「摄影」两个费用 tab 此前使用「按天候选 + EXCLUDED」机制后端按订单行程天数预生成候选行,前端要在「纳入INCLUDED/ 排除EXCLUDED/ 未处理UNRESOLVED」三种候选处理结果之间来回切换,还要单独维护一份 excludedCandidateKeys 排除清单。这套机制字段多、状态绕,和酒店/门票 tab 的「全量保存 + 确认状态」模型完全不一致,前端两套交互逻辑要分别维护。

本次变更把导游/摄影 tab 拉齐到酒店/门票同款模型:

  • 废弃候选机制,保存接口改全量替换语义——传当前应存在的全部行,没传的未确认行即删除;
  • 确认状态统一为每行一个 settlementConfirmStatusUNCONFIRMED / 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 费用明细 IDLong 序列化为字符串);未落库的预填草稿行为 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 凭证 URL;无凭证返回空数组

错误码:见 §7 全组共用错误码表。

业务边界:首次查询(订单尚无该角色核单行)时,返回按订单人员分配预填的草稿行(不落库,行特征:id 为 null、settlementConfirmStatus=UNCONFIRMED);这些预填行会被计入 unconfirmedCount,影响 settlementReady 展示口径。

示例(典型成功)

请求:

GET /v3/admin/order/12345/settlement/guide-fees
Authorization: Bearer {admin-token}
(无请求体)

响应:

{
  "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

响应:

{
  "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 重放结果一致)
  • 限流:无

入参

路径参数:orderIdLong,必填,订单 ID

请求体字段:

字段 类型 必填 说明 校验规则
items Array 导游费用明细全量集合(当前应存在的全部行) 最多 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 凭证 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 行为一致)。

示例(典型成功:两行全量保存,一行直接置已确认)

请求:

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=200data 为保存后的最新全量(结构同 3.1 响应示例)。

示例(业务失败:误传已删除的旧字段)

场景说明:前端未清理旧逻辑,行内仍带 candidateKey

请求:

PUT /v3/admin/order/12345/settlement/guide-fees

{
  "items": [
    {
      "candidateKey": "2026-08-03#11",
      "name": "导游甲",
      "amount": "500.00"
    }
  ]
}

响应:

{
  "code": 584128,
  "data": null,
  "msg": "导游或摄影费用请求字段不合法:导游费用明细不支持字段: candidateKey",
  "success": false
}

示例(业务失败:已确认行被全量替换遗漏)

场景说明:库中行 9001 已 CONFIRMED,本次 items 只传了行 9002,相当于要删掉 9001。

响应:

{
  "code": 584120,
  "data": null,
  "msg": "已确认的有效费用不能直接删除,请先进入编辑状态",
  "success": false
}

3.3 确认导游单层费用明细POST guide-fees/confirm

  • 使用场景:勾选若干未确认行后点「确认」,将这批行批量置为已确认
  • 认证:管理后台 JWT
  • 幂等性:是(对已确认行重复确认无副作用)
  • 限流:无

入参

路径参数:orderIdLong,必填

请求体字段:

字段 类型 必填 说明 校验规则
itemIds Array 待确认的费用明细 ID 列表,JSON 中每项为字符串 1 至 200 条;ID 不存在或不属于本订单及导游角色报 584111

同样走严格模式,多传字段报 584128msg 形如 导游费用确认请求不支持字段: xxx)。

出参:同 3.1 的响应结构(确认后的最新全量)。

示例(典型成功)

请求:

POST /v3/admin/order/12345/settlement/guide-fees/confirm
Authorization: Bearer {admin-token}

{
  "itemIds": ["9002"]
}

响应:

{
  "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.1orderId 路径参数)。

出参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}
(无请求体)

响应:

{
  "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 / serviceTypestaffAssignmentId 非空时角色须为摄影,否则报 584115。

示例(典型成功)

请求:

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=200data 为保存后的最新全量(结构同 3.4 响应示例)。

示例(业务失败:金额非法)

请求:

PUT /v3/admin/order/12345/settlement/photographer-fees

{
  "items": [
    {
      "photographerName": "摄影甲",
      "feeType": "FOLLOW_SHOOT",
      "amount": "-50.00"
    }
  ]
}

响应:

{
  "code": 584114,
  "data": null,
  "msg": "金额必须为 0 至 99999999.99 且最多两位小数",
  "success": false
}

3.6 确认摄影单层费用明细POST photographer-fees/confirm

  • 使用场景 / 认证 / 幂等 / 限流:同 3.3

入参:同 3.3itemIds 字符串数组,1-200 条;ID 不存在或不属于本订单及摄影角色报 584111;多传字段报 584128,msg 形如 摄影费用确认请求不支持字段: xxx)。

出参:同 3.4 的响应结构。

示例(典型成功)

请求:

POST /v3/admin/order/12345/settlement/photographer-fees/confirm
Authorization: Bearer {admin-token}

{
  "itemIds": ["9101"]
}

响应:code=200data 中该行 settlementConfirmStatus 变为 CONFIRMEDunconfirmedCount 相应减少(结构同 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_INACTIVECANDIDATES_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金额 0amount 允许 0.00,不被非负校验拦截

10. 修改前后对比

10.1 字段级对比

保存入参PUT,ItemVO 行内):

字段 改前 改后
candidateKey 有,候选行标识 删除,传了报 400584128
completionState NEEDS_INPUT/COMPLETE/EXCLUDED 删除,传了报 400584128
sourceResolution 有,来源处理结果 删除,传了报 400584128
settlementConfirmStatus 新增,非必填,UNCONFIRMED/CONFIRMED,缺省按 UNCONFIRMED

保存入参PUT,请求体顶层

字段 改前 改后
excludedCandidateKeys 有,排除候选 key 清单 删除,传了报 400584128

查询出参GET,ItemVO 行内):

字段 改前 改后
candidateKey 删除,不再下发
completionState 删除,不再下发
candidateResolution INCLUDED/EXCLUDED/UNRESOLVED 删除,不再下发
sourceActive 删除,不再下发
settlementConfirmStatus / settlementConfirmStatusName 有(仅展示) 保留,语义不变

查询出参GET,响应级

字段 改前 改后
pendingCandidateCount 有,未处理候选数 删除,不再下发
blockReasonCode 值域 SOURCE_INACTIVECANDIDATES_UNRESOLVED 删除这两个值,只保留 ORDER_DATE_INCOMPLETEITEMS_UNCONFIRMED

10.2 行为级对比

行为 改前 改后
保存语义 候选机制:行按 candidateKey 对齐候选,配合 excludedCandidateKeys 声明排除 全量替换items 即保存后的全部行,未传的未确认行删除
确认入口 只能通过 confirm 接口把 INCLUDED 行置已确认 confirm 接口保留;保存时也可直接置 CONFIRMEDsettlementConfirmStatus 入参)
已确认行删除 通过 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 链接

13.2 联系人

  • 后端负责人: @yst腰苏图