hl-api-changelog/changelogs-v2/2026-07/23_5185_Step2票种规格默认值-修改接口-管理后台.md
yaosutu 8288bfcc7f
所有检测均成功
changelog-filename-gate / validate (push) Successful in 1s
补充Step2票种规格字典与默认值说明
2026-07-23 16:43:36 +08:00

14 KiB

🔧 修改接口·管理后台】Step2 票种规格默认值(#5185

PR: #5191 | 更新时间: 2026-07-23

1. 接口背景

Step2 门票/游玩项目明细原先可能返回或保存空的 specName,前端无法稳定展示票种/规格。现在查询和保存统一补齐“成人票”默认值,同时保留已有的非空规格。

2. 变更清单

# 接口 方法 路径 变更类型 说明
1 Step2 查询门票核单明细 GET /v3/admin/order/{orderId}/settlement/step2 修改接口 无明确规格的明细统一返回 specName=成人票
2 Step2 保存门票核单明细 PUT /v3/admin/order/{orderId}/settlement/step2 修改接口 specNamenull、空串或纯空白时按“成人票”保存

3. 接口详情

3.1 Step2 查询门票核单明细

  • 使用场景:进入或刷新核单 Step2 时查询门票/游玩项目明细。
  • 认证:需要管理后台 JWT。
  • 幂等性:是,只读查询。
  • 限流:无接口专属限流约定。
  • 默认语义:自动生成且无明确规格、或已有明细规格为空时,specName 返回“成人票”。
  • 保留语义:已有非空规格原样返回,例如“骑马体验”。

3.2 Step2 保存门票核单明细

  • 使用场景:全量保存 Step2 门票/游玩项目明细。
  • 认证:需要管理后台 JWT。
  • 幂等性:业务数据为全量替换语义;重复提交相同明细得到相同业务内容,行 ID 可能重新生成。
  • 限流:无接口专属限流约定。
  • 默认语义items[].specNamenull"" 或纯空白时,保存并回读为“成人票”。
  • 保留语义:非空规格原样保存,例如“骑马体验”不会被替换。

4. 接口入参

4.1 路径参数

字段 类型 必填 说明
orderId Long / String 订单 ID,必须大于 0;19 位 ID 建议按字符串拼入路径

GET 无 Query 参数、无请求体。

4.2 PUT 请求体

推荐使用对象形式;接口同时兼容直接提交明细数组。

{
  "items": []
}
字段 类型 必填 说明 校验规则
items Array 门票/游玩项目明细,全量替换 可为空数组;空数组表示清空已保存草稿

4.3 items[] 字段

字段 类型 必填 说明 校验规则
id Long / String 已存在行 ID;新增或自动生成行可为空 19 位 ID 建议使用字符串
sourceType String 来源类型 SCENIC_ASSIGNMENTACTIVITY_ASSIGNMENTCUSTOM_ASSIGNMENT
sourceTypeName String 来源类型中文名 最长 32 字符
scenicAssignmentId Long / String 来源记录 ID;手动补充行为空 19 位 ID 建议使用字符串
dayNumber Integer 行程第几天,从 1 开始;保存时按 dayDate 计算 无需前端计算
dayDate String 项目日期 yyyy-MM-dd,不得早于订单出发日期
scenicName String 景区或游玩项目名称 非空,最长 200 字符
specName String 票种/规格名称 最长 128 字符;null、空串、纯空白统一为“成人票”
ticketCount Integer 实际购票数量 套餐含项目可填 0
ticketUnitPrice Decimal 参考单价,单位元 自费项目可填;包价项目可为 null
sellPrice Decimal 客户成交单价,单位元 大于等于 0
totalAmount Decimal 客户成交小计,单位元 大于等于 0;为空时按 sellPrice × ticketCount 计算
plannedCost Decimal 计划成本,单位元 大于等于 0
actualCost Decimal 实际成本,单位元 大于等于 0
paymentMethod String 付款方式 SIGNEDCOMPANY_PAIDCASH_PAID;为空时为 COMPANY_PAID
paymentMethodName String 付款方式中文名 最长 32 字符
voucherUrls Array<String> 凭证图片 URL 列表 可为空数组
remark String 备注 最长 500 字符

5. 出参(响应)

5.1 统一响应字段

字段 类型 说明
code Integer 业务状态码,成功为 200
message String 响应消息,成功为“成功”
data Object / Array GET 为明细数组,PUT 为保存结果对象
traceId String / null 链路追踪 ID
success Boolean code=200 时为 true

5.2 GET data[]

字段 类型 可为空 说明
id String / null 已保存的 19 位行 ID 按字符串返回;未保存的自动生成行可为 null
sourceType String 来源类型,取值见 §6.2
sourceTypeName String 来源类型中文名
scenicAssignmentId String / null 19 位来源记录 ID 按字符串返回;手动补充行为空
dayNumber Integer 根据项目日期与订单行程计算的天序
dayDate String 项目日期,格式为 yyyy-MM-dd
scenicName String 景区或游玩项目名称
specName String 无明确规格时返回“成人票”;已有非空规格原样返回
ticketCount Integer 实际购票数量
ticketUnitPrice Decimal 参考单价,单位元
sellPrice Decimal 客户成交单价,单位元
totalAmount Decimal 客户成交小计,单位元
plannedCost Decimal 计划成本,单位元
actualCost Decimal 实际成本,单位元
paymentMethod String 付款方式,取值见 §6.3
paymentMethodName String 付款方式中文名
voucherUrls Array<String> 凭证图片 URL 列表
remark String 备注

5.3 PUT data

字段 类型 说明
addedIds Array<String> 本次新增行 ID 列表
updatedIds Array<String> 本次更新行 ID 列表
deletedIds Array<String> 本次删除行 ID 列表
totalActualCost String 保存后实际成本合计,单位元

6. 枚举 / 数据字典

6.1 specName(数据字典 settlement_ticket_spec

所属字段items[].specName | 类型String | 必填:否

中文 说明
成人票 成人票 当前默认票种/规格;字典接口返回的 dictValue

加载选项使用:

GET /admin/dict/data/settlement_ticket_spec

展示使用字典项 dictLabel,提交使用 dictValue。本次没有新增 specCode 字段。

6.2 sourceType

所属字段items[].sourceType | 类型String | 必填:是

中文 说明
SCENIC_ASSIGNMENT 景区 来源于景区项目
ACTIVITY_ASSIGNMENT 游玩项目 来源于游玩项目
CUSTOM_ASSIGNMENT 手动补充 核单时手动新增

6.3 paymentMethod

所属字段items[].paymentMethod | 类型String | 必填:否

中文 说明
SIGNED 签单 供应商签单
COMPANY_PAID 公司付款 未传付款方式时的默认值
CASH_PAID 现付 现场付款,可附凭证

7. 错误码

code 含义 触发场景
200 成功 查询或保存成功
400 请求参数校验失败 必填字段为空、枚举值不合法、金额为负数、字段超长或日期格式错误
401 未认证或认证失效 未携带有效管理后台 JWT
584011 当前核单状态不允许录门票核单 PUT 时订单核单状态不是“待核单”或“核单中”
584017 订单缺出发日期 PUT 时无法根据 dayDate 计算 dayNumber
584018 项目日期早于订单出发日期 PUT 的 items[].dayDate 早于订单出发日期
500 系统异常 查询或保存过程发生未预期异常

8. 示例

8.1 典型成功:查询自动生成明细

请求

GET /v3/admin/order/2079454953641836546/settlement/step2
Authorization: Bearer <admin-token>

无请求体。

响应

{
  "code": 200,
  "message": "成功",
  "data": [
    {
      "id": null,
      "sourceType": "SCENIC_ASSIGNMENT",
      "sourceTypeName": "景区",
      "scenicAssignmentId": "2079454953641837001",
      "dayNumber": 1,
      "dayDate": "2026-07-21",
      "scenicName": "示例景区",
      "specName": "成人票",
      "ticketCount": 2,
      "ticketUnitPrice": 100.00,
      "sellPrice": 120.00,
      "totalAmount": 240.00,
      "plannedCost": 200.00,
      "actualCost": 200.00,
      "paymentMethod": "COMPANY_PAID",
      "paymentMethodName": "公司付款",
      "voucherUrls": [],
      "remark": null
    }
  ],
  "traceId": "a1b2c3d4-e5f6-7890",
  "success": true
}

8.2 典型成功:提交字典选中的“成人票”

请求

PUT /v3/admin/order/2079454953641836546/settlement/step2
Authorization: Bearer <admin-token>
Content-Type: application/json
{
  "items": [
    {
      "id": null,
      "sourceType": "SCENIC_ASSIGNMENT",
      "sourceTypeName": "景区",
      "scenicAssignmentId": "2079454953641837001",
      "dayNumber": 1,
      "dayDate": "2026-07-21",
      "scenicName": "示例景区",
      "specName": "成人票",
      "ticketCount": 2,
      "ticketUnitPrice": 100.00,
      "sellPrice": 120.00,
      "totalAmount": 240.00,
      "plannedCost": 200.00,
      "actualCost": 200.00,
      "paymentMethod": "COMPANY_PAID",
      "paymentMethodName": "公司付款",
      "voucherUrls": [],
      "remark": null
    }
  ]
}

响应

{
  "code": 200,
  "message": "成功",
  "data": {
    "addedIds": ["2079454953641840001"],
    "updatedIds": [],
    "deletedIds": [],
    "totalActualCost": "200.00"
  },
  "traceId": "b2c3d4e5-f6a7-8901",
  "success": true
}

8.3 边界情况:空规格归一为“成人票”

保存请求

PUT /v3/admin/order/2079454953641836546/settlement/step2
Authorization: Bearer <admin-token>
Content-Type: application/json
{
  "items": [
    {
      "sourceType": "ACTIVITY_ASSIGNMENT",
      "sourceTypeName": "游玩项目",
      "scenicAssignmentId": "2079454953641837002",
      "dayDate": "2026-07-22",
      "scenicName": "示例游玩项目",
      "specName": null,
      "ticketCount": 2,
      "ticketUnitPrice": null,
      "sellPrice": 0,
      "totalAmount": 0,
      "plannedCost": 0,
      "actualCost": 0,
      "paymentMethod": "COMPANY_PAID",
      "voucherUrls": [],
      "remark": null
    }
  ]
}

保存响应

{
  "code": 200,
  "message": "成功",
  "data": {
    "addedIds": ["2079454953641840002"],
    "updatedIds": [],
    "deletedIds": [],
    "totalActualCost": "0"
  },
  "traceId": "c3d4e5f6-a7b8-9012",
  "success": true
}

随后 GET 回读时,该行的关键字段为:

{
  "scenicName": "示例游玩项目",
  "specName": "成人票"
}

8.4 业务失败:非法来源类型

请求

PUT /v3/admin/order/2079454953641836546/settlement/step2
Authorization: Bearer <admin-token>
Content-Type: application/json
{
  "items": [
    {
      "sourceType": "UNKNOWN",
      "dayDate": "2026-07-21",
      "scenicName": "示例项目",
      "specName": "成人票",
      "ticketCount": 1,
      "plannedCost": 0,
      "actualCost": 0
    }
  ]
}

响应

{
  "code": 400,
  "message": "sourceType 必须是 SCENIC_ASSIGNMENT / ACTIVITY_ASSIGNMENT / CUSTOM_ASSIGNMENT 之一",
  "data": null,
  "traceId": "d4e5f6a7-b8c9-0123",
  "success": false
}

9. 业务边界

  • GET自动生成明细没有明确规格时,返回 specName=成人票
  • GET历史已保存明细的 specNamenull、空串或纯空白时,也返回“成人票”。
  • GET/PUT已有非空规格保持不变,例如“骑马体验”不会被覆盖。
  • PUTspecName 最大 128 字符;空值会归一为“成人票”而不是报错。
  • PUTitems 为全量数据;遗漏的旧明细不会继续保留。
  • PUT仅订单核单状态为“待核单”或“核单中”时允许保存。
  • 前端从 settlement_ticket_spec 字典读取选项,展示 dictLabel,提交 dictValuespecName

10. 修改前后对比

10.1 字段级对比

字段 改前 改后
items[].specName String,可返回或保存为空 String;查询和保存的空值统一为“成人票”
items[].specCode 不存在 仍不存在,本次未新增

10.2 行为级对比

行为 改前 改后
自动生成明细没有明确规格 specName 可能为空或与项目名重复 specName=成人票
保存 specName=null、空串或纯空白 可能按空值保存和回显 保存、回读均为“成人票”
保存非空自定义规格 原样保存 仍原样保存
接口数量 GET、PUT 两个既有接口 不变,没有新增 Step2 接口

11. 影响评估

  • 是否破坏向后兼容:否,接口路径、请求结构和响应字段均未改变;只收紧了空规格的返回语义。
  • 前端是否必须同步上线:否;前端可逐步接入字典下拉,未接入时也会收到稳定的“成人票”默认值。

12. 注意事项

  • 前端如有 specName || "成人票" 的临时兜底,可在确认接口已覆盖当前环境后移除。
  • 不要新增或提交 specCode;当前契约只使用 specName
  • 选择字典项后提交 dictValue,不要提交 dictLabel 以外的展示元数据或 dictDataId
  • 自定义非空规格可以继续提交,接口不会强制替换为字典当前默认项。

13. 关联 / 联系人

13.1 链接

13.2 联系人

  • 后端负责人: @yst