修改原因:hl-ui changelog loop 需要把前端消费进度回写到契约源,避免领取、实现状态与实际交付脱节。 修改内容:将 frontend_status 与已有 legacy frontend 同步为 implemented,记录负责人 hl-ui-codex,并关联 mmg/hl-ui@adff10ea74198f4e89a1488e631463bedbcd4eea;发布和验收字段保持不变。 实际验证:回写器已校验目标文件、状态单调性、提交范围和 Front Matter 内容,提交只包含当前 changelog。 Frontend-Status: tools/mcp-api-sync/.changelog-repo/changelogs-v2/2026-07/25_5237_酒店候选补齐房型结算价-修改接口-管理后台.md
17 KiB
frontend_status, frontend_owner, frontend_ref, updated_at
| frontend_status | frontend_owner | frontend_ref | updated_at |
|---|---|---|---|
| implemented | hl-ui-codex | mmg/hl-ui@adff10ea74 | 2026-07-25T03:37:10.934Z |
【修改接口·管理后台】酒店候选补齐房型结算价 (#5237)
PR: #5240 | 服务: hl-order-service-v3 | 更新时间: 2026-07-25 10:58
1. 接口背景
管理后台酒店候选列表原来只在候选酒店顶层返回 protoPrice,前端无法确认这个价格来自哪个真实房型,也拿不到同一房型同一天的结算价。配房时如果只看房型列表或自行匹配最低价,容易把协议价和结算价口径拆到不同房型。
本次在候选酒店顶层补齐:
protoPriceRoomTypeId:产生顶层protoPrice的真实房型 ID。settlementPrice:与protoPriceRoomTypeId同一房型、同一天的结算价。
顶层 protoPrice、protoPriceRoomTypeId、settlementPrice 是同一代表房型口径。未维护结算价时 settlementPrice = null,不会用协议价兜底。
2. 变更清单
| # | 接口 | 方法 | 路径 | 变更类型 | 说明 |
|---|---|---|---|---|---|
| 1 | 查询酒店候选(4 场景统一入口) | GET | /v3/admin/hotel-candidates |
修改接口 | 候选酒店项新增 protoPriceRoomTypeId、settlementPrice 两个出参字段;入参不变。 |
3. 接口详情
3.1 查询酒店候选(4 场景统一入口)
- 使用场景:管理后台在订单维度查询某一晚的候选酒店,用于配房选酒店、回显当前已配酒店、按产品池/定制师点名/资源库候选排序。
- 认证:需要管理后台 JWT。
- 幂等性:只读查询,幂等。
- 限流:无接口级特殊限流;受网关与服务通用限流策略约束。
4. 接口入参
4.1 路径参数 / Query 参数
| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
orderId |
String | 是 | 订单 ID。后端 Long,JSON/Query 建议按字符串传,避免长 ID 精度问题。 |
dayNumber |
Integer | 否 | 第几天,从 1 开始;用于推算 stayDate = departDate + dayNumber - 1。最小值 1。 |
stayDate |
String | 否 | 入住日期,格式 yyyy-MM-dd;直接指定时优先于 dayNumber 推算。 |
city |
String | 否 | 城市代码或城市名;未传且非关键词模式时默认不按城市限制。 |
keyword |
String | 否 | 关键词;非空时跨城/省匹配酒店名、城市、省份、地址,此时 city 可不传。 |
limit |
Integer | 否 | 返回候选条数上限,默认 30,最小 1,最大 50。 |
roomCategory |
String | 否 | 房型字典 code。 |
roomCount |
Integer | 否 | 需要的房间数;最小 1。 |
preferredHotelId |
String | 否 | 定制师指定的优先酒店 ID。后端 Long,建议字符串传。 |
requirementId |
String | 否 | 用房需求 ID;传入后将该需求 days JSON 中当前天的酒店候选作为定制师指定候选。后端 Long,建议字符串传。 |
4.2 请求体字段
GET 接口无请求体。
5. 出参字段
统一响应结构:
| 字段 | 类型 | 说明 |
|---|---|---|
code |
Integer | 业务状态码,成功为 200。 |
message |
String | 响应消息,成功为 成功。 |
data |
Object | 酒店候选查询出参。 |
traceId |
String | 链路追踪 ID,可能为空。 |
success |
Boolean | code == 200 时为 true。 |
data 字段:
| 字段 | 类型 | 说明 |
|---|---|---|
stayDate |
String | 入住日期,格式 yyyy-MM-dd。 |
city |
String / null | 本次查询使用的城市;关键词模式或默认不限城市时可为 null。 |
productType |
String | 产品类型:CORE / GROUP / CUSTOM。 |
candidates |
Array | 候选酒店列表,已按产品类型分流排序。 |
data.candidates[] 字段:
| 字段 | 类型 | 说明 |
|---|---|---|
hotelId |
String | 酒店 ID。 |
hotelName |
String | 酒店名称。 |
level |
String / null | 酒店等级。 |
form |
String / null | 住宿形态。 |
address |
String / null | 地址。 |
tags |
Array | 运营标签;无标签时为空数组或 null。 |
contactPerson |
String / null | 联系人。 |
contactWechat |
String / null | 联系微信。 |
settleType |
String / null | 结算类型,取值见 §6.1。 |
city |
String / null | 酒店所在城市。 |
district |
String / null | 酒店所在区/县。 |
roomTypes |
Array | 该酒店当日真实房型列表;无房型数据时为空数组。 |
protoPrice |
String / null | 代表房型协议价。与 protoPriceRoomTypeId、顶层 settlementPrice 同一房型同一天。 |
protoPriceRoomTypeId |
String / null | 产生顶层 protoPrice 的真实房型 ID。无有效可售协议价时为 null。 |
settlementPrice |
String / null | 与 protoPriceRoomTypeId 同一房型、同一天的结算价。未维护时为 null,不会用 protoPrice 兜底。 |
todayAvailable |
Integer / null | 今日全房型可用房数合计。 |
availFreshness |
String / null | 可用数数据时效:fresh / stale / never_checked。 |
lastCheckedAt |
String / null | 最近一次核房时间,格式 yyyy-MM-dd'T'HH:mm:ss。 |
matchedRoomTypeAvailable |
Integer / null | 匹配房型今日可用数。 |
matchedRoomTypeId |
String / null | 匹配的房型 ID。 |
matchedRoomTypeLabel |
String / null | 匹配的房型中文。 |
quickPickEnabled |
Boolean / null | 是否支持快速配房。 |
quickPickDisabledReason |
String / null | 置灰原因。 |
isPoolMatch |
Boolean / null | 是否产品池内。 |
poolMatchBadge |
Object / null | 产品池内徽章。 |
isConsultantRecommended |
Boolean / null | 是否被定制师点名。 |
consultantRecommendBadge |
Object / null | 定制师点名徽章。 |
historyMatchScore |
Number / null | 历史匹配度,范围 0-1。 |
score |
Number / null | 排序分数。 |
recommendation |
String / null | 推荐理由。 |
recommended |
Boolean / null | 是否为推荐候选。 |
recommendSource |
String / null | 推荐来源,见 §6.4。 |
historyScoreStub |
Boolean / null | 历史命中分数是否为 stub。 |
isCurrentlyAssigned |
Boolean / null | 是否为本天当前已配酒店。 |
assignedRoomTypeId |
String / null | 本天当前已配的房型 ID;isCurrentlyAssigned=true 时可用于预填原房型。 |
data.candidates[].roomTypes[] 字段:
| 字段 | 类型 | 说明 |
|---|---|---|
roomTypeId |
String | 房型 ID。 |
name |
String / null | 房型名称。 |
roomCategory |
String / null | 房型分类字典值。 |
bedType |
String / null | 床型,已按字典尽量翻译;字典缺失时可回退为 code。 |
maxOccupancy |
Integer / null | 最大入住人数。 |
available |
Integer / null | 今日可用房数;unlimited=true 时为 null,语义为不限。 |
unlimited |
Boolean | 是否不限库存。 |
stock |
Integer / null | 当前可用房;unlimited=true 时为 null。 |
protocolPrice |
String / null | 该房型当日协议价。 |
settlementPrice |
String / null | 该房型当日结算价。 |
basePrice |
String / null | 标价/挂牌价。 |
inventoryStatus |
String | 库存状态,见 §6.2。 |
徽章对象字段:
| 字段 | 类型 | 说明 |
|---|---|---|
label |
String | 中文徽章文字。 |
color |
String | 徽章色,见 §6.5。 |
tooltip |
String | 悬浮提示。 |
6. 枚举 / 数据字典
6.1 settleType
所属字段:data.candidates[].settleType | 类型:String | 必填:否
| 值 | 中文 | 说明 |
|---|---|---|
cash |
现付 | 到店或线下现金类结算。 |
sign |
签单 | 供应商签单结算。 |
company |
公司付 | 公司统一付款结算。 |
6.2 inventoryStatus
所属字段:data.candidates[].roomTypes[].inventoryStatus | 类型:String | 必填:是
| 值 | 中文 | 说明 |
|---|---|---|
AVAILABLE |
可售 | 有余量,或 unlimited=true 不限库存。 |
FULL |
满房 | 有日历记录,但库存为 0。 |
CLOSED |
未开放 | 无该日价格日历记录。 |
6.3 availFreshness
所属字段:data.candidates[].availFreshness | 类型:String | 必填:否
| 值 | 中文 | 说明 |
|---|---|---|
fresh |
最新 | 可用于快速配房判断。 |
stale |
过期 | 核房数据过期。 |
never_checked |
从未核房 | 无可用核房数据。 |
6.4 recommendSource
所属字段:data.candidates[].recommendSource | 类型:String | 必填:否
| 值 | 中文 | 说明 |
|---|---|---|
PRODUCT_POOL |
产品池 | 来自产品池候选。 |
CONSULTANT |
定制师点名 | 来自定制师指定候选。 |
RESOURCE_LIB |
资源库 | 来自资源库候选。 |
6.5 Badge.color
所属字段:poolMatchBadge.color / consultantRecommendBadge.color | 类型:String | 必填:否
| 值 | 中文 | 说明 |
|---|---|---|
blue |
蓝色 | 普通推荐或池内标识。 |
gold |
金色 | 高优先级推荐标识。 |
gray |
灰色 | 弱提示标识。 |
7. 错误码
| code | 含义 | 触发场景 |
|---|---|---|
200 |
成功 | 查询成功。 |
400 |
参数错误 | orderId 为空、dayNumber < 1、limit 超出 1-50、roomCount < 1、日期格式不是 yyyy-MM-dd 等参数绑定或校验失败。 |
401 |
未认证 | JWT 缺失或无效。 |
403 |
无权限 | 当前账号无权访问该管理后台接口或订单数据。 |
581007 |
订单不存在 | orderId 对应订单不存在。 |
500 |
服务内部错误 | 非预期异常。 |
8. 示例(3 组:典型 / 边界 / 异常)
8.1 典型成功
请求:
GET /v3/admin/hotel-candidates?orderId=100001&dayNumber=1&stayDate=2026-07-25&limit=30&roomCount=2 HTTP/1.1
Authorization: Bearer <admin-jwt>
无请求体。
响应:
{
"code": 200,
"message": "成功",
"data": {
"stayDate": "2026-07-25",
"city": null,
"productType": "CORE",
"candidates": [
{
"hotelId": "2023714929877450753",
"hotelName": "测试酒店",
"level": "舒适型",
"form": "HOTEL",
"address": "呼伦贝尔市海拉尔区测试路 1 号",
"tags": ["协议酒店"],
"contactPerson": "张经理",
"contactWechat": "hotel_mgr",
"settleType": "sign",
"city": "呼伦贝尔市",
"district": "海拉尔区",
"protoPrice": "280.00",
"protoPriceRoomTypeId": "2023727403196502017",
"settlementPrice": "279.00",
"todayAvailable": 7,
"availFreshness": "fresh",
"lastCheckedAt": null,
"matchedRoomTypeAvailable": 7,
"matchedRoomTypeId": "2023727403196502017",
"matchedRoomTypeLabel": "豪华大床房",
"quickPickEnabled": true,
"quickPickDisabledReason": null,
"isPoolMatch": true,
"poolMatchBadge": {
"label": "产品池内",
"color": "blue",
"tooltip": "本酒店在产品池内,优先推荐"
},
"isConsultantRecommended": false,
"consultantRecommendBadge": null,
"historyMatchScore": 0.85,
"score": 1185.0,
"recommendation": "池内 · 历史合作 8 单成功率 95%",
"recommended": true,
"recommendSource": "PRODUCT_POOL",
"historyScoreStub": true,
"isCurrentlyAssigned": false,
"assignedRoomTypeId": null,
"roomTypes": [
{
"roomTypeId": "2023727403196502017",
"name": "豪华大床房",
"roomCategory": "KING",
"bedType": "大床",
"maxOccupancy": 2,
"available": 7,
"unlimited": false,
"stock": 7,
"protocolPrice": "280.00",
"settlementPrice": "279.00",
"basePrice": "568.00",
"inventoryStatus": "AVAILABLE"
}
]
}
]
},
"traceId": "trace-20260725-0001",
"success": true
}
8.2 边界情况
场景说明:代表房型有协议价但未维护结算价,顶层 settlementPrice 返回 null,不使用 protoPrice 兜底。
请求:
GET /v3/admin/hotel-candidates?orderId=100001&stayDate=2026-07-25&keyword=%E6%B5%B7%E6%8B%89%E5%B0%94&limit=1 HTTP/1.1
Authorization: Bearer <admin-jwt>
无请求体。
响应:
{
"code": 200,
"message": "成功",
"data": {
"stayDate": "2026-07-25",
"city": null,
"productType": "CUSTOM",
"candidates": [
{
"hotelId": "2023714929877450753",
"hotelName": "测试酒店",
"settleType": "cash",
"protoPrice": "280.00",
"protoPriceRoomTypeId": "2023727403196502017",
"settlementPrice": null,
"roomTypes": [
{
"roomTypeId": "2023727403196502017",
"name": "豪华大床房",
"available": 7,
"unlimited": false,
"protocolPrice": "280.00",
"settlementPrice": null,
"basePrice": "568.00",
"inventoryStatus": "AVAILABLE"
}
],
"quickPickEnabled": true,
"recommended": true,
"recommendSource": "RESOURCE_LIB"
}
]
},
"traceId": "trace-20260725-0002",
"success": true
}
8.3 业务失败(异常)
场景说明:orderId 未传,触发参数校验失败。
请求:
GET /v3/admin/hotel-candidates?stayDate=2026-07-25 HTTP/1.1
Authorization: Bearer <admin-jwt>
无请求体。
响应:
{
"code": 400,
"message": "orderId 不能为空",
"data": null,
"traceId": "trace-20260725-0003",
"success": false
}
9. 业务边界
- 适用场景:管理后台按订单和入住日查询酒店候选;
stayDate可直接传,也可通过dayNumber和订单出发日推算。 - 不适用场景:不用于前端直接查询内部资源服务;本文只描述管理后台
/v3/admin/hotel-candidates。 - 特殊边界:顶层
protoPrice、protoPriceRoomTypeId、settlementPrice必须按同一代表房型理解;settlementPrice = null表示该代表房型当天未维护结算价。 - 特殊边界:
roomTypes[].settlementPrice是每个房型自己的当日结算价;顶层settlementPrice只对应protoPriceRoomTypeId指向的代表房型。
10. 修改前后对比
10.1 字段级对比
| 字段 | 改前 | 改后 |
|---|---|---|
data.candidates[].protoPriceRoomTypeId |
不返回 | 返回产生顶层 protoPrice 的真实房型 ID;无有效可售协议价为 null。 |
data.candidates[].settlementPrice |
不返回 | 返回与 protoPriceRoomTypeId 同一房型、同一天的结算价;未维护为 null。 |
data.candidates[].protoPrice |
已返回,但无法判断来自哪个房型 | 仍返回原字段,并与新增的 protoPriceRoomTypeId、顶层 settlementPrice 组成同一代表房型口径。 |
10.2 行为级对比
| 行为 | 改前 | 改后 |
|---|---|---|
| 候选酒店顶层价格展示 | 只能拿到代表协议价 protoPrice。 |
可同时拿到代表协议价、代表房型 ID、该代表房型结算价。 |
| 结算价为空 | 顶层没有结算价字段。 | 顶层 settlementPrice 返回 null;不使用 protoPrice 兜底。 |
11. 影响评估 / 回滚
11.1 影响评估
- 是否破坏向后兼容:否。只新增出参字段,已有字段名、类型、入参不变。
- 前端是否必须同步上线:否。老前端可忽略新增字段;需要展示或回填结算价的页面可读取新增字段。
- 影响已有数据:无数据迁移要求;历史未维护结算价的房型按
settlementPrice = null返回。
11.2 回滚方案
- 回滚方式:回滚 PR #5240 后,顶层新增字段不再返回。
- 回滚后清理:无前端数据清理要求。
- 回滚耗时:按常规服务回滚流程处理。
12. 注意事项
- 前端读取顶层
settlementPrice时,不要把null当作protoPrice;null表示未维护结算价。 - 如需定位价格来自哪个房型,使用顶层
protoPriceRoomTypeId去匹配roomTypes[].roomTypeId。 - 金额和长 ID 在响应 JSON 中按字符串处理,例如
"280.00"、"2023727403196502017"。
13. 关联 / 联系人
13.1 链接
13.2 联系人
- 后端负责人: @yst