16 KiB
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 链接
- Issue: #8818
- PR: #8819
- Merge commit: 09f9dc8c4c
13.2 联系人
- 后端负责人: @yst