hotel.js 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544
  1. // 酒店相关接口
  2. // 磐河旅行开放平台 - 酒店模块
  3. // AppKey: 34c130be1d611fd21c9f35488c689a0d
  4. // SecretKey: 766067b446871059606ec130cc33bbb6
  5. // 接口网关域名:http://api.panhe.net
  6. import { get, post } from '@/core/http/http-client'
  7. import { buildOssUrl } from '@/core/media/oss-url'
  8. import { digestHex } from '@/core/infra/hash-digest'
  9. // 磐河旅行 API 基础配置
  10. const PANHE_API_BASE = 'http://api.panhe.net'
  11. /**
  12. * 搜索酒店列表
  13. * 接口地址:http://api.panhe.net/hotel/searchHotelList
  14. * @param {Object} params
  15. * @param {string} params.cityName - 城市名称(与cityCode必填一个)
  16. * @param {string} params.cityCode - 城市code(与cityName必填一个)
  17. * @param {string} params.checkInDate - 入住日期(格式:yyyy-MM-dd)
  18. * @param {string} params.checkOutDate - 离店日期(格式:yyyy-MM-dd)
  19. * @param {number} params.pageIndex - 页码(从1开始)
  20. * @param {number} params.pageSize - 每页请求酒店数量(取值范围:1-20,默认为10)
  21. * @param {string} params.queryText - 查询关键词(可选)
  22. * @param {string} params.hotelStars - 酒店星级(2,3,4,5),多个以逗号分隔(可选)
  23. * @param {number} params.minPrice - 最小价格(可选)
  24. * @param {number} params.maxPrice - 最大价格(可选)
  25. * @param {number} params.longitude - 经度(可选)
  26. * @param {number} params.latitude - 纬度(可选)
  27. * @param {number} params.range - 方圆公里数(可选,默认100KM)
  28. * @param {string} params.districtName - 行政区名称(可选)
  29. * @param {string} params.brands - 酒店品牌,多个以逗号分隔(可选)
  30. * @param {number} params.sort - 排序类型:0=默认,1=价格升序,2=价格降序,3=评分降序,4=距离升序(可选)
  31. * @param {number} params.availableSaleStatus - 可售状态:1=仅查询有可售房间的酒店(可选)
  32. */
  33. export async function pullHotelList({
  34. cityName = '',
  35. cityCode = '',
  36. checkInDate = '',
  37. checkOutDate = '',
  38. pageIndex = 1,
  39. pageSize = 10,
  40. queryText = '',
  41. hotelStars = '',
  42. minPrice = null,
  43. maxPrice = null,
  44. longitude = null,
  45. latitude = null,
  46. range = 0,
  47. districtName = '',
  48. brands = '',
  49. sort = 0,
  50. availableSaleStatus = null
  51. } = {}) {
  52. const params = {
  53. cityName,
  54. cityCode,
  55. checkInDate,
  56. checkOutDate,
  57. pageIndex,
  58. pageSize,
  59. queryText,
  60. hotelStars,
  61. minPrice,
  62. maxPrice,
  63. longitude,
  64. latitude,
  65. range,
  66. districtName,
  67. brands,
  68. sort,
  69. availableSaleStatus
  70. }
  71. const { data } = await get(`${PANHE_API_BASE}/hotel/searchHotelList`, params)
  72. const body = data || {}
  73. const hotelList = (Array.isArray(body.hotelList) ? body.hotelList : []).map(item => {
  74. if (!item || item.hotelID == null) return null
  75. return {
  76. id: item.hotelID,
  77. name: item.hotelName || '',
  78. cityName: item.cityName || '',
  79. address: item.address || '',
  80. hotelStar: item.hotelStar || 0,
  81. isGovStar: item.isGovStar,
  82. hotelStarName: item.hotelStarName || '',
  83. avgScore: item.avgScore || 0,
  84. googleLat: item.googleLat || 0,
  85. googleLon: item.googleLon || 0,
  86. distance: item.distance || 0,
  87. image: buildOssUrl(item.mainImage),
  88. startingPrice: item.startingPrice || 0,
  89. facilities: Array.isArray(item.lstServiceFacilities) ? item.lstServiceFacilities : [],
  90. raw: item
  91. }
  92. }).filter(Boolean)
  93. return {
  94. hotelList,
  95. count: Number(body.count || hotelList.length),
  96. pageIndex,
  97. pageSize
  98. }
  99. }
  100. /**
  101. * 获取酒店详情
  102. * 接口地址:http://api.panhe.net/hotel/getHotelDetail
  103. * @param {Object} params
  104. * @param {string|number} params.hotelID - 酒店ID(必填)
  105. * @param {string} params.checkInDate - 入住日期(格式:yyyy-MM-dd)(必填)
  106. * @param {string} params.checkOutDate - 离店日期(格式:yyyy-MM-dd)(必填)
  107. */
  108. export async function pullHotelDetail({ hotelID, checkInDate, checkOutDate } = {}) {
  109. const { data } = await get(`${PANHE_API_BASE}/hotel/getHotelDetail`, {
  110. hotelID,
  111. checkInDate,
  112. checkOutDate
  113. })
  114. if (!data) return null
  115. // 处理酒店图片列表
  116. if (Array.isArray(data.pictures)) {
  117. data.pictures = data.pictures.map(pic => ({
  118. path: buildOssUrl(pic.path),
  119. name: pic.name || ''
  120. }))
  121. }
  122. // 处理主图
  123. if (data.mainImage) {
  124. data.mainImage = buildOssUrl(data.mainImage)
  125. }
  126. // 处理服务列表
  127. if (Array.isArray(data.services)) {
  128. data.services = data.services.map(service => ({
  129. name: service.name || '',
  130. status: service.status || '0' // 0=无,1=有,2=不确定,3=部分有
  131. }))
  132. }
  133. // 处理设施列表
  134. if (Array.isArray(data.facilities)) {
  135. data.facilities = data.facilities.map(facility => ({
  136. name: facility.name || '',
  137. status: facility.status || '0' // 0=无,1=有,2=不确定,3=部分有
  138. }))
  139. }
  140. return {
  141. hotelID: data.hotelID,
  142. hotelName: data.hotelName || '',
  143. cityName: data.cityName || '',
  144. cityCode: data.cityCode,
  145. areaName: data.areaName || '',
  146. areaCode: data.areaCode,
  147. address: data.address || '',
  148. hotelStar: data.hotelStar || 0,
  149. isGovStar: data.isGovStar,
  150. hotelStarName: data.hotelStarName || '',
  151. phone: data.phone || '',
  152. openDate: data.openDate || '',
  153. decorationDate: data.decorationDate || '',
  154. description: data.description || '',
  155. avgScore: data.avgScore || 0,
  156. googleLat: data.googleLat || 0,
  157. googleLon: data.googleLon || 0,
  158. pictures: data.pictures || [],
  159. services: data.services || [],
  160. facilities: data.facilities || [],
  161. raw: data
  162. }
  163. }
  164. /**
  165. * 查询酒店房型、价格、库存
  166. * 接口地址:http://api.panhe.net/hotel/queryRoomAndRatePlan
  167. * @param {Object} params
  168. * @param {string|number} params.hotelID - 酒店ID(必填)
  169. * @param {string} params.checkInDate - 入住日期(格式:yyyy-MM-dd)(必填)
  170. * @param {string} params.checkOutDate - 离店日期(格式:yyyy-MM-dd)(必填)
  171. * @param {number} params.maxWaitSeconds - 最大等待时长(可选,默认5秒,最小1秒)
  172. */
  173. export async function pullHotelRooms({ hotelID, checkInDate, checkOutDate, maxWaitSeconds = 5 } = {}) {
  174. const { data } = await get(`${PANHE_API_BASE}/hotel/queryRoomAndRatePlan`, {
  175. hotelID,
  176. checkInDate,
  177. checkOutDate,
  178. maxWaitSeconds
  179. })
  180. if (!data || !Array.isArray(data.rooms)) {
  181. return { rooms: [] }
  182. }
  183. const rooms = data.rooms.map(room => {
  184. // 处理房型图片
  185. const pictures = Array.isArray(room.pictures)
  186. ? room.pictures.map(pic => ({
  187. path: buildOssUrl(pic.path),
  188. name: pic.name || ''
  189. }))
  190. : []
  191. // 处理房型设施
  192. const facilities = Array.isArray(room.facilities)
  193. ? room.facilities.map(facility => ({
  194. name: facility.name || '',
  195. status: facility.status || '0'
  196. }))
  197. : []
  198. // 处理价格方案
  199. const ratePlans = Array.isArray(room.ratePlans)
  200. ? room.ratePlans.map(plan => ({
  201. ratePlanId: plan.ratePlanId || '',
  202. ratePlanName: plan.ratePlanName || '',
  203. paymentType: plan.paymentType || 0, // 0:预付,1:现付
  204. averagePrice: plan.averagePrice || 0,
  205. pricePerDay: plan.pricePerDay || '', // 每日价格,以"|"分隔
  206. stockPerDay: plan.stockPerDay || '', // 库存量,以"|"分隔
  207. confirmType: plan.confirmType || 0, // 0:不支持即时确认 1:支持即时确认
  208. breakfast: plan.breakfast || '', // 早餐规则
  209. cancelRule: {
  210. name: plan.cancelRule?.name || '',
  211. type: plan.cancelRule?.type || 1, // 1:不可取消, 2:限时取消, 3:收费取消
  212. desc: plan.cancelRule?.desc || '',
  213. freeCancelLatestTime: plan.cancelRule?.freeCancelLatestTime || null
  214. }
  215. }))
  216. : []
  217. return {
  218. roomId: room.roomId || 0,
  219. roomName: room.roomName || '',
  220. useableArea: room.useableArea || '',
  221. capacity: room.capacity || '',
  222. floor: room.floor || '',
  223. bedType: room.bedType || '',
  224. windowType: room.windowType || '',
  225. pictures,
  226. facilities,
  227. ratePlans
  228. }
  229. })
  230. return { rooms }
  231. }
  232. /**
  233. * 创建酒店订单
  234. * 接口地址:http://api.panhe.net/hotel/createOrder
  235. * 请求方法:POST
  236. * @param {Object} params
  237. * @param {string} params.customerOrderNo - 商户订单号(必填,必须唯一)
  238. * @param {string|number} params.hotelID - 酒店ID(必填)
  239. * @param {string} params.ratePlanID - 产品(价格计划)ID(必填)
  240. * @param {number} params.roomNum - 房间数量(必填)
  241. * @param {string} params.checkInDate - 入住日期(格式:yyyy-MM-dd)(必填)
  242. * @param {string} params.checkOutDate - 离店日期(格式:yyyy-MM-dd)(必填)
  243. * @param {string[]} params.guestNames - 房客姓名数组(必填,长度需和房间数量保持一致)
  244. * @param {string} params.arriveTime - 最晚到店时间(可选,如:2020-07-27 16:00)
  245. * @param {number} params.orderAmount - 订单总金额(必填)
  246. * @param {string} params.contactName - 订单联系人姓名(必填)
  247. * @param {string} params.contactMobile - 订单联系人手机号(必填)
  248. * @param {string} params.contactEmail - 订单联系人邮箱(可选)
  249. * @param {string} params.orderRemark - 用户备注信息(可选)
  250. * @param {string} params.callBackUrl - 订单状态变更异步回调地址(可选)
  251. */
  252. export async function postHotelBook(params) {
  253. const {
  254. customerOrderNo,
  255. hotelID,
  256. ratePlanID,
  257. roomNum,
  258. checkInDate,
  259. checkOutDate,
  260. guestNames,
  261. arriveTime,
  262. orderAmount,
  263. contactName,
  264. contactMobile,
  265. contactEmail,
  266. orderRemark,
  267. callBackUrl
  268. } = params
  269. const requestData = {
  270. customerOrderNo,
  271. hotelID,
  272. ratePlanID,
  273. roomNum,
  274. checkInDate,
  275. checkOutDate,
  276. guestNames,
  277. arriveTime,
  278. orderAmount,
  279. contactName,
  280. contactMobile,
  281. contactEmail,
  282. orderRemark,
  283. callBackUrl
  284. }
  285. const { data } = await post(`${PANHE_API_BASE}/hotel/createOrder`, requestData)
  286. if (!data) {
  287. return { success: false, msg: '创建订单失败' }
  288. }
  289. return {
  290. success: true,
  291. orderNo: data.orderNo || '',
  292. msg: '订单创建成功'
  293. }
  294. }
  295. /**
  296. * 酒店订单支付(代扣支付)
  297. * 接口地址:http://api.panhe.net/hotel/autoPay
  298. * 请求方法:POST
  299. * @param {Object} params
  300. * @param {string} params.orderNo - 酒店订单号(必填)
  301. * @param {number} params.payAmount - 订单支付金额(可选,传值时将和订单金额进行比对)
  302. * @param {string} params.appKey - 应用AppKey(必填)
  303. * @param {string} params.secretKey - 应用SecretKey(必填)
  304. */
  305. export async function postHotelPay({ orderNo, payAmount, appKey, secretKey } = {}) {
  306. // 生成签名:md5(secretKey + orderNo + payAmount + secretKey)
  307. // 注意:payAmount为null时不参与签名
  308. let signStr = ''
  309. if (payAmount != null) {
  310. signStr = `${secretKey}${orderNo}${payAmount}${secretKey}`
  311. } else {
  312. signStr = `${secretKey}${orderNo}${secretKey}`
  313. }
  314. // 使用项目自带的MD5工具加密
  315. const sign = digestHex(signStr)
  316. const requestData = {
  317. appKey,
  318. orderNo,
  319. payAmount,
  320. sign
  321. }
  322. const { data } = await post(`${PANHE_API_BASE}/hotel/autoPay`, requestData)
  323. return {
  324. success: data === true,
  325. msg: data === true ? '支付成功' : '支付失败'
  326. }
  327. }
  328. /**
  329. * 获取酒店订单列表(暂时留着)
  330. * @param {Object} params
  331. * @param {number} params.state - 订单状态 (0=全部, 1=待付款, 2=待入住, 3=已入住, 4=已完成, 5=已取消)
  332. * @param {number} params.page - 页码
  333. */
  334. export async function pullHotelOrderList({ state = 0, page = 1 } = {}) {
  335. const { data } = await get(`${PANHE_API_BASE}/hotel/getOrderList`, { state, page })
  336. return {
  337. rows: Array.isArray(data.data) ? data.data : [],
  338. currentPage: data.current_page || 1,
  339. lastPage: data.last_page || 1,
  340. total: data.total || 0
  341. }
  342. }
  343. /**
  344. * 获取酒店订单详情
  345. * 接口地址:http://api.panhe.net/hotel/getOrderDetail
  346. * @param {Object} params
  347. * @param {string} params.orderNo - 酒店订单号(必填)
  348. */
  349. export async function pullHotelOrderDetail({ orderNo } = {}) {
  350. const { data } = await get(`${PANHE_API_BASE}/hotel/getOrderDetail`, { orderNo })
  351. if (!data) return null
  352. // 订单状态映射
  353. const orderStatusMap = {
  354. 1: '待付款',
  355. 2: '待确认',
  356. 3: '已确认',
  357. 4: '确认失败',
  358. 5: '已完成',
  359. 6: '取消中',
  360. 7: '取消失败',
  361. 8: '已取消'
  362. }
  363. // 支付状态映射
  364. const payStatusMap = {
  365. 0: '待支付',
  366. 1: '已支付'
  367. }
  368. // 支付方式映射
  369. const paymentTypeMap = {
  370. 0: '未支付',
  371. 1: '虚拟钱包支付',
  372. 2: '支付宝',
  373. 3: '微信'
  374. }
  375. // 取消政策类型映射
  376. const cancelPolicyTypeMap = {
  377. 1: '不可取消',
  378. 2: '限时取消',
  379. 3: '收费取消'
  380. }
  381. return {
  382. orderNo: data.orderNo || '',
  383. customerOrderNo: data.customerOrderNo || '',
  384. numberOfRooms: data.numberOfRooms || 0,
  385. numberOfNights: data.numberOfNights || 0,
  386. arriveTime: data.arriveTime || '',
  387. checkInDate: data.checkInDate || '',
  388. checkOutDate: data.checkOutDate || '',
  389. guestNames: data.guestNames || '',
  390. remark: data.remark || '',
  391. orderAmount: data.orderAmount || 0,
  392. orderStatus: data.orderStatus || 0,
  393. orderStatusText: orderStatusMap[data.orderStatus] || '未知',
  394. payStatus: data.payStatus || 0,
  395. payStatusText: payStatusMap[data.payStatus] || '未知',
  396. payTime: data.payTime || '',
  397. paymentType: data.paymentType || 0,
  398. paymentTypeText: paymentTypeMap[data.paymentType] || '未知',
  399. transactionNo: data.transactionNo || '',
  400. refundAmount: data.refundAmount || 0,
  401. refundSuccessTime: data.refundSuccessTime || '',
  402. refundTransactionNo: data.refundTransactionNo || '',
  403. refundTransactionMethods: data.refundTransactionMethods || 0,
  404. hotelId: data.hotelId || 0,
  405. hotelName: data.hotelName || '',
  406. hotelAddress: data.hotelAddress || '',
  407. roomTypeName: data.roomTypeName || '',
  408. bedTypeName: data.bedTypeName || '',
  409. ratePlanId: data.ratePlanId || '',
  410. pricePerDay: data.pricePerDay || '',
  411. breakfastDesc: data.breakfastDesc || '',
  412. cancelPolicyType: data.cancelPolicyType || 0,
  413. cancelPolicyTypeText: cancelPolicyTypeMap[data.cancelPolicyType] || '未知',
  414. cancelDesc: data.cancelDesc || '',
  415. freeCancelLatestTime: data.freeCancelLatestTime || '',
  416. confirmationNumber: data.confirmationNumber || '',
  417. contactName: data.contactName || '',
  418. contactMobile: data.contactMobile || '',
  419. contactEmail: data.contactEmail || '',
  420. payLimitTime: data.payLimitTime || '',
  421. unFinishedReason: data.unFinishedReason || '',
  422. createTime: data.createTime || '',
  423. raw: data
  424. }
  425. }
  426. /**
  427. * 取消酒店订单
  428. * 接口地址:http://api.panhe.net/hotel/cancelOrder
  429. * @param {Object} params
  430. * @param {string} params.orderNo - 酒店订单号(必填)
  431. */
  432. export async function postHotelOrderCancel({ orderNo } = {}) {
  433. const { data } = await get(`${PANHE_API_BASE}/hotel/cancelOrder`, { orderNo })
  434. if (!data) {
  435. return {
  436. success: false,
  437. msg: '取消失败'
  438. }
  439. }
  440. return {
  441. success: data === true,
  442. msg: data === true ? '取消成功' : '取消失败'
  443. }
  444. }
  445. /**
  446. * 获取热门酒店推荐(暂时留着)
  447. * @param {Object} params
  448. * @param {string} params.cityName - 城市名称
  449. * @param {number} params.pageIndex - 页码
  450. */
  451. export async function pullHotelRecommend({ cityName = '', pageIndex = 1 } = {}) {
  452. const { data } = await get(`${PANHE_API_BASE}/hotel/getRecommendHotels`, { cityName, pageIndex })
  453. const hotelList = (Array.isArray(data.hotelList) ? data.hotelList : []).map(item => {
  454. if (!item || item.hotelID == null) return null
  455. return {
  456. id: item.hotelID,
  457. name: item.hotelName || '',
  458. cityName: item.cityName || '',
  459. address: item.address || '',
  460. hotelStar: item.hotelStar || 0,
  461. avgScore: item.avgScore || 0,
  462. image: buildOssUrl(item.mainImage),
  463. startingPrice: item.startingPrice || 0,
  464. raw: item
  465. }
  466. }).filter(Boolean)
  467. return {
  468. hotelList,
  469. count: data.count || 0,
  470. pageIndex,
  471. pageSize: 10
  472. }
  473. }
  474. /**
  475. * 获取酒店评价列表(暂时留着)
  476. * @param {string|number} hotelId - 酒店ID
  477. * @param {number} pageIndex - 页码
  478. */
  479. export async function pullHotelReviews({ hotelId, pageIndex = 1 } = {}) {
  480. const { data } = await get(`${PANHE_API_BASE}/hotel/getHotelReviews`, { hotelId, pageIndex })
  481. const reviews = Array.isArray(data.data) ? data.data : []
  482. return {
  483. rows: reviews.map(review => ({
  484. id: review.id,
  485. userName: review.user_name || review.nickname || '匿名用户',
  486. userAvatar: buildOssUrl(review.user_avatar || review.avatar),
  487. rating: Number(review.rating || 0),
  488. content: review.content || '',
  489. images: Array.isArray(review.images) ? review.images.map(img => buildOssUrl(img)) : [],
  490. createTime: review.create_time || '',
  491. raw: review
  492. })),
  493. total: data.total || 0,
  494. currentPage: data.current_page || 1,
  495. lastPage: data.last_page || 1
  496. }
  497. }