文件
hl-api-changelog/changelogs-v2/2026-10/10_8818_团期核单酒店候选查询-新增接口-管理后台.md
T
Mimingguang dcd914c8a5
changelog-filename-gate / validate (push) Failing after 3s
docs(changelog): #8818 前端标记 implemented
2026-10-10 10:14:05 +08:00

16 KiB
原始文件 Blame 文件历史

schema, ticket, title, consumer, author, change_type, backend_status, gateway_status, frontend_status, frontend_owner, frontend_ref, target_release, verified_at, status_note, updated_at, base
schema ticket title consumer author change_type backend_status gateway_status frontend_status frontend_owner frontend_ref target_release verified_at status_note updated_at base
hl-changelog/v2 8818 团期核单酒店候选查询(无 orderId,住宿行选酒店带协议价/结算价/库存) admin yst 新增接口 merged not_required implemented hl-admin(claude) aaae21f86a012844b41e36141044243628a57bb1 v2.1 2026-10-10 后端 PR #8819 已合并 dev-v3(squash 09f9dc8c4c),并已部署测试服、网关实调验证新端点生效(401 鉴权,非 404)。无 DDL、不动现有 /v3/admin/hotel-candidates 配房接口。 / 住宿 tab 酒店/房型下拉有 stayDate 走 #8818 候选口,选房型按当日协议价带出单价,满房/未开放选项标状态;无日期回退管理域读口;日期后补单价为空白愈回填 2026-10-10 dev-v3

团期核单酒店候选查询(无 orderId)

面向:管理后台前端(hl-admin) 日期:2026-10-10 | 后端:hl-order-service-v3(端口 8086) 范围:纯新增一个酒店候选查询接口,供团期核单「住宿(HOTEL)」明细行选酒店/房型下拉使用。

1. 接口背景

团期核单录入住宿明细行时,前端需要「选酒店 / 选房型的下拉候选」,选中后带出酒店名、房型名、协议价(作为单价)等快照值填进核单行。

现有的酒店候选接口 GET /v3/admin/hotel-candidates 必须传 orderId,它服务的是「给某个订单的某一天配房」场景,返回里带产品池徽章、定制师点名、已配回显、快速配房等配房专属逻辑,且日期可由订单推算。核单站在团 / 资源视角选房,没有也不该伪造 orderId,无法复用该接口。

因此新增一个无需 orderId 的核单酒店候选接口:只按「入住日期 + 城市/关键词 + 需要房数」查在售酒店及当日房型价格库存,数据与配房候选同源(同一资源价格日历),但不含任何配房个性化字段。

2. 变更清单

# 接口 方法 路径 变更类型 说明
1 团期核单酒店候选查询 GET /v3/admin/order/group-batch/hotel-candidates 新增 无 orderId,按入住日查在售酒店 + 当日房型价格库存

3. 接口详情

3.1 团期核单酒店候选查询

  • 使用场景:团期核单录入住宿(HOTEL)明细行时,前端弹层/下拉加载可选酒店列表,选中某酒店某房型后取快照值(酒店名、房型名、协议价作单价)填行。
  • 认证:需要管理后台 JWT(/v3/admin/* 走网关鉴权),需具备团期查看权限(group-batch:view)。
  • 幂等性:是(只读查询,无副作用)。
  • 限流:沿用网关统一限流,无单独 QPS 限制。
  • HTTP 方法 / 路径:GET /v3/admin/order/group-batch/hotel-candidates
  • 请求方式:Query 参数,无请求体。

4. 接口入参

4.1 Query 参数

字段 类型 必填 说明
stayDate String(yyyy-MM-dd) ✅ 是 入住日期。价格 / 库存都按这一天取(当天酒店价格日历)。缺参返回 400。
city String 否 城市名(前缀模糊,如「呼伦贝尔」命中「呼伦贝尔市」)。空 = 查全部在售酒店,不限城市。
keyword String 否 关键词搜索。非空时突破单城限制,跨城/省按关键词匹配酒店名/城市/省份/地址,此时 city 被忽略可不传。
hotelIds Long[] 否 酒店 ID 白名单(逗号分隔多个)。传入则只查这批酒店(如限定某个池子内的酒店);不传 = 不限定。
roomCount Integer 否 需要的房间数(≥1,默认 1)。用于过滤「够房」的房型;不传按 1 间处理。
roomCategory String 否 房型大类(字典 room_category 的 value,如 STANDARD/TWIN/KING/SUITE/FAMILY)。传入后只在同大类房型内选取匹配项;不传不限大类。
page Integer 否 页码,从 1 开始,默认 1。
pageSize Integer 否 每页条数,默认 50,最大 200(超过按 200 计)。

city 与 keyword 二选一生效:keyword 非空时走跨地区关键词搜(忽略 city);keyword 为空时按 city 筛(city 也空 = 全部在售)。

5. 出参(响应)

响应为统一分页包装 PageResult,结构:{ code, message, success, data: { list: [...], total, pageNo, pageSize } }。 data.list 每个元素为一家酒店候选,字段如下。

5.1 酒店候选字段(data.list[])

字段 类型 说明
hotelId String 酒店 ID(Long 序列化为 String,防 JS 精度丢失)
hotelName String 酒店名
starLevel String 星级(如 FIVE_STAR/FOUR_STAR,字典值)
city String 城市
district String 区/县
address String 详细地址
hotelType String 住宿形态(字典 hotel_type,见 §6.1)
tags String[] 运营标签名列表(如 ["协议酒店","近景区"];无标签为空数组)
contactPerson String 联系人姓名
contactWechat String 联系人微信号
settleType String 结算类型(字典 resource_settle_type,见 §6.2)
coverMaterialId String 封面素材 ID(前端可取封面图;无则为 null)
protoPrice String 酒店代表价(协议价,元/间·晚)= 当日可订房型里最低协议价;当天无可订协议价为 null。金额 BigDecimal 序列化为 String。
protoPriceRoomTypeId String 产生 protoPrice 的那个房型 ID;无有效可售协议价为 null
settlementPrice String 与 protoPriceRoomTypeId 同一房型、同一天的结算价;未维护为 null
roomTypes Object[] 该酒店当日房型列表(含每房型库存/价格,见 §5.2);无房型数据为空数组

5.2 房型字段(data.list[].roomTypes[])

字段 类型 说明
roomTypeId String 房型 ID
name String 房型名称(如「豪华大床房」)——核单行 roomTypeName 快照取这里
description String 房型描述
maxOccupancy Integer 最大入住人数
bedType String 床型(如 KING/TWIN)
roomCategory String 房型大类(字典 room_category 的 value,如 TWIN/KING)
stock Integer 当前可用房(库存);无当日价格日历记录为 null
stockUsed Integer 已占用数;无记录为 null(仅记录,不参与 available 计算)
available Integer 当日可订房数;unlimited=true(不限库存)时为 null(语义「不限」而非具体数);无记录为 null
unlimited Boolean 是否不限库存(资源侧库存列留空 = 永远可售);true 时 available=null、inventoryStatus=AVAILABLE,前端展示「不限」
protocolPrice String 该房型当日协议价(元/间·晚)——核单行 unitPrice(单价)建议取这里;无当日记录为 null
settlementPrice String 该房型当日结算价;无记录为 null
basePrice String 标价/挂牌价;当日无记录时回退到房型基础价
inventoryStatus String 库存状态,见 §6.3

核单行快照取值建议:选中某酒店某房型后,取 hotelId + hotelName + roomTypes[].roomTypeId + roomTypes[].name(房型名)+ roomTypes[].protocolPrice(单价)填入核单住宿行对应快照字段。

6. 枚举 / 数据字典

6.1 hotelType(字典 hotel_type,酒店住宿形态)

所属字段:data.list[].hotelType | 类型:String | 必填:❌(可空)

值 中文 说明
HOTEL 酒店 标准酒店
GUESTHOUSE 客栈/宾馆 —
RESORT 度假村 —
CAMP 营地 —
HOMESTAY 民宿 —

6.2 settleType(字典 resource_settle_type,结算类型)

所属字段:data.list[].settleType | 类型:String | 必填:❌(可空)

值 中文 说明
cash 现结 现场现金/即时结算
sign 签单 挂账签单,周期结算
company 对公 对公转账结算

6.3 inventoryStatus(库存状态,资源价格日历派生)

所属字段:data.list[].roomTypes[].inventoryStatus | 类型:String | 必填:✅

值 中文 说明
AVAILABLE 可订 当日价格日历可售且有余量,或 unlimited=true 不限库存
FULL 满房 当日可售但余量为 0,或当日已售罄
CLOSED 关闭 未配置当日价格日历(漏配)、当日已关闭、或状态异常

FULL / CLOSED 时 available=0、unlimited=false,但价格字段照常返回(前端可展示「满房」「未开放」并 still 显示参考价)。

7. 错误码

code 含义 触发场景
400 参数校验失败 stayDate 缺失(必填)、pageSize/roomCount/page 超出范围(如 pageSize>200 之外非法值、roomCount<1)
401 未登录 / 缺少 Authorization 头 未带管理后台 JWT 或 token 过期
200(空列表) 查询成功但无候选 当日该城市/关键词下无在售可订酒店,或资源侧暂无数据 —— 资源服务不可用时本接口降级返回空列表(不报错),前端据此展示「暂无可选酒店」

本接口是查询类,资源服务故障时降级返回空列表(HTTP 200 + 空 data.list),不会抛 5xx 阻断核单页面。前端需能区分「真的没酒店」和「服务暂不可用」(两者都返回空列表,后者可稍候重试)。

8. 示例

8.1 典型成功(按城市 + 入住日查)

请求:

GET /v3/admin/order/group-batch/hotel-candidates?stayDate=2026-10-27&city=呼伦贝尔&roomCount=2&page=1&pageSize=20
Authorization: Bearer {admin-token}
(无请求体)

响应:

{
  "code": 200,
  "message": "ok",
  "success": true,
  "data": {
    "total": 3,
    "pageNo": 1,
    "pageSize": 20,
    "list": [
      {
        "hotelId": "70123",
        "hotelName": "海拉尔金鼎大酒店",
        "starLevel": "FOUR_STAR",
        "city": "呼伦贝尔市",
        "district": "海拉尔区",
        "address": "海拉尔区中央大街123号",
        "hotelType": "HOTEL",
        "tags": ["协议酒店", "近景区"],
        "contactPerson": "张经理",
        "contactWechat": "zhang_mgr",
        "settleType": "sign",
        "coverMaterialId": "90001",
        "protoPrice": "328.00",
        "protoPriceRoomTypeId": "80125",
        "settlementPrice": "300.00",
        "roomTypes": [
          {
            "roomTypeId": "80125",
            "name": "豪华大床房",
            "description": "含双早",
            "maxOccupancy": 2,
            "bedType": "KING",
            "roomCategory": "KING",
            "stock": 10,
            "stockUsed": 3,
            "available": 7,
            "unlimited": false,
            "protocolPrice": "328.00",
            "settlementPrice": "300.00",
            "basePrice": "568.00",
            "inventoryStatus": "AVAILABLE"
          }
        ]
      }
    ]
  }
}

8.2 边界情况(关键词跨地区搜 + 不限库存房型)

场景说明:用 keyword 跨城搜索;命中酒店含「不限库存」房型(unlimited=true、available=null)。

请求:

GET /v3/admin/order/group-batch/hotel-candidates?stayDate=2026-10-27&keyword=蒙古包&roomCount=1
Authorization: Bearer {admin-token}
(无请求体)

响应:

{
  "code": 200,
  "message": "ok",
  "success": true,
  "data": {
    "total": 1,
    "pageNo": 1,
    "pageSize": 50,
    "list": [
      {
        "hotelId": "70200",
        "hotelName": "图嘎营地",
        "starLevel": null,
        "city": "陈巴尔虎旗",
        "district": null,
        "address": "呼和诺尔湖畔",
        "hotelType": "CAMP",
        "tags": [],
        "contactPerson": null,
        "contactWechat": null,
        "settleType": "cash",
        "coverMaterialId": null,
        "protoPrice": "380.00",
        "protoPriceRoomTypeId": "80210",
        "settlementPrice": "350.00",
        "roomTypes": [
          {
            "roomTypeId": "80210",
            "name": "蒙古包",
            "description": null,
            "maxOccupancy": 4,
            "bedType": null,
            "roomCategory": "YURT",
            "stock": null,
            "stockUsed": null,
            "available": null,
            "unlimited": true,
            "protocolPrice": "380.00",
            "settlementPrice": "350.00",
            "basePrice": null,
            "inventoryStatus": "AVAILABLE"
          }
        ]
      }
    ]
  }
}

8.3 业务失败(缺 stayDate)

场景说明:必填参数 stayDate 未传。

请求:

GET /v3/admin/order/group-batch/hotel-candidates?city=呼伦贝尔
Authorization: Bearer {admin-token}
(无请求体)

响应:

{
  "code": 400,
  "message": "stayDate 不能为空",
  "success": false,
  "data": null
}

9. 业务边界

  • ✅ 适用场景:团期核单住宿行选酒店;任何「无需订单上下文、按入住日查在售酒店价格库存」的管理后台场景。
  • ✅ 价格口径:所有价格(protoPrice/settlementPrice/protocolPrice/basePrice)都按 stayDate 当天的价格日历取值,换一天价格可能不同——必须传准确的入住日期。
  • ⚠️ 降级:资源服务不可用时返回空列表(HTTP 200),不抛错——前端展示「暂无可选酒店」并可提示稍候重试。
  • ⚠️ 不限库存:unlimited=true 的房型 available=null(表示「不限」),前端不要用 available==null 判满房,应看 inventoryStatus。
  • ❌ 不适用:需要订单个性化推荐(产品池/定制师点名/已配回显/快速配房)的配房场景 → 请用 GET /v3/admin/hotel-candidates(需 orderId)。

12. 注意事项

  • 纯新增接口,不影响任何现有接口;/v3/admin/hotel-candidates(配房候选)保持原样。
  • 已部署测试服(order-v3,squash commit 09f9dc8c4c),前端可直接联调。
  • 所有 hotelId/roomTypeId/protoPriceRoomTypeId/coverMaterialId 为 String(Long 序列化),所有金额字段为 String(BigDecimal 序列化),前端按字符串接收即可。
  • 本接口只读、幂等,可在下拉搜索框高频调用(建议前端做防抖)。

12.1 核单行「付款方式」取值约定(关联提示)

选中酒店后,核单住宿行保存时需填「付款方式」paymentMethod(保存走核单明细接口,不在本候选接口范围)。该字段约定如下,单体核单与团期核单完全同一套:

  • 走数据字典取值:字典类型 settlement_payment_method,前端调字典接口取下拉选项(不要前端硬编码三个值)。
  • 提交:把字典的 value 填进 paymentMethod。
  • 展示:列表/详情用后端带回的 paymentMethodName(中文名后端已拼好,前端不必再翻译)。

字典 settlement_payment_method 标准值(3 个):

value 中文 说明
CASH_PAID 现金已付 已现金/即时付掉
COMPANY_PAID 公司支付 已对公转账付掉
SIGNED 签单 挂账,周期与酒店/供应商结算

⚠️ 与候选接口 settleType 的区别(勿混用):本候选接口带回的 settleType(值域 cash/sign/company,字典 resource_settle_type)是酒店主数据上的习惯结算方式,与核单行 paymentMethod(本次实际付款动作)是两个字典、两套值。前端可用 settleType 给 paymentMethod 做默认推荐(如酒店 settleType=sign 默认选中 SIGNED),但必须把值做映射后再填,不能直接拿 settleType 的值塞进 paymentMethod(cash≠CASH_PAID,值域不同)。

13. 关联 / 联系人

13.1 链接

13.2 联系人

  • 后端负责人: @yst