@curless/hotel-storefront 0.27.9 → 0.27.10
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/huazhu/sandbox-photos.d.ts +25 -0
- package/dist/huazhu/sandbox-photos.d.ts.map +1 -0
- package/dist/huazhu/sandbox-photos.js +514 -0
- package/dist/huazhu/sandbox-photos.js.map +1 -0
- package/dist/huazhu/sandbox.d.ts +4 -2
- package/dist/huazhu/sandbox.d.ts.map +1 -1
- package/dist/huazhu/sandbox.js +763 -45
- package/dist/huazhu/sandbox.js.map +1 -1
- package/dist/index.js +0 -0
- package/dist/legal.d.ts +1 -0
- package/dist/legal.d.ts.map +1 -1
- package/dist/legal.js +55 -6
- package/dist/legal.js.map +1 -1
- package/dist/server.d.ts +1 -1
- package/dist/server.d.ts.map +1 -1
- package/dist/server.js +1 -1
- package/dist/server.js.map +1 -1
- package/package.json +10 -11
package/dist/huazhu/sandbox.js
CHANGED
|
@@ -4,6 +4,81 @@ import { pathToFileURL } from 'node:url';
|
|
|
4
4
|
import { addDays } from '../dates.js';
|
|
5
5
|
import { SIMULATED_HEADER } from './client.js';
|
|
6
6
|
import { createFakeHuazhu, fakeHotel, product } from './fake.js';
|
|
7
|
+
import { KING_ROOMS, LEADS, SUITES, TWIN_ROOMS, creditOf, stockUrl, } from './sandbox-photos.js';
|
|
8
|
+
const room = (roomTypeCode, roomTypeName, hotelArea, bed, maxOccupancy, isHasWindow = '1') => ({
|
|
9
|
+
roomTypeCode,
|
|
10
|
+
roomTypeName,
|
|
11
|
+
hotelArea,
|
|
12
|
+
isHasWindow,
|
|
13
|
+
maxOccupancy,
|
|
14
|
+
status: 1,
|
|
15
|
+
bedTypeList: [{ bedTypeCode: bed[0], bedSpecNo: '-1', bedTypeName: bed[1], bedSpec: bed[2] }],
|
|
16
|
+
});
|
|
17
|
+
const KING = ['A', '大床', '1.8m'];
|
|
18
|
+
const KING_WIDE = ['A', '大床', '2.0m'];
|
|
19
|
+
const TWIN = ['B', '双床', '1.2m'];
|
|
20
|
+
const TWIN_WIDE = ['B', '双床', '1.35m'];
|
|
21
|
+
/**
|
|
22
|
+
* What each part of the range sells: its rooms, rooms a night, the breakfast
|
|
23
|
+
* rate's surcharge and meals, and the price of the second bed.
|
|
24
|
+
*/
|
|
25
|
+
const TIERS = {
|
|
26
|
+
economy: {
|
|
27
|
+
rooms: [room('DR1', '大床房', 16, KING, 2), room('TR1', '双床房', 18, TWIN, 2, '3')],
|
|
28
|
+
inventory: 40,
|
|
29
|
+
breakfast: 25,
|
|
30
|
+
meals: 1,
|
|
31
|
+
twin: 10,
|
|
32
|
+
weekend: 1.15,
|
|
33
|
+
},
|
|
34
|
+
midscale: {
|
|
35
|
+
rooms: [room('DR1', '高级大床房', 24, KING, 2), room('TR1', '高级双床房', 26, TWIN_WIDE, 2)],
|
|
36
|
+
inventory: 30,
|
|
37
|
+
breakfast: 40,
|
|
38
|
+
meals: 1,
|
|
39
|
+
twin: 20,
|
|
40
|
+
weekend: 1.15,
|
|
41
|
+
},
|
|
42
|
+
upscale: {
|
|
43
|
+
rooms: [
|
|
44
|
+
room('DR1', '豪华大床房', 30, KING, 2),
|
|
45
|
+
room('TR1', '豪华双床房', 32, TWIN_WIDE, 2),
|
|
46
|
+
room('SU1', '行政套房', 48, KING_WIDE, 3),
|
|
47
|
+
],
|
|
48
|
+
inventory: 20,
|
|
49
|
+
suites: 6,
|
|
50
|
+
breakfast: 68,
|
|
51
|
+
meals: 2,
|
|
52
|
+
twin: 30,
|
|
53
|
+
weekend: 1.15,
|
|
54
|
+
},
|
|
55
|
+
luxury: {
|
|
56
|
+
rooms: [
|
|
57
|
+
room('DR1', '豪华大床房', 38, KING_WIDE, 2),
|
|
58
|
+
room('TR1', '豪华双床房', 40, TWIN_WIDE, 2),
|
|
59
|
+
room('SU1', '豪华套房', 68, KING_WIDE, 3),
|
|
60
|
+
],
|
|
61
|
+
inventory: 12,
|
|
62
|
+
suites: 4,
|
|
63
|
+
breakfast: 98,
|
|
64
|
+
meals: 2,
|
|
65
|
+
twin: 50,
|
|
66
|
+
weekend: 1.2,
|
|
67
|
+
},
|
|
68
|
+
boutique: {
|
|
69
|
+
rooms: [
|
|
70
|
+
room('DR1', '庭院大床房', 32, KING_WIDE, 2),
|
|
71
|
+
room('TR1', '庭院双床房', 34, TWIN_WIDE, 2),
|
|
72
|
+
room('SU1', '院落套房', 58, KING_WIDE, 3),
|
|
73
|
+
],
|
|
74
|
+
inventory: 8,
|
|
75
|
+
suites: 3,
|
|
76
|
+
breakfast: 88,
|
|
77
|
+
meals: 2,
|
|
78
|
+
twin: 40,
|
|
79
|
+
weekend: 1.25,
|
|
80
|
+
},
|
|
81
|
+
};
|
|
7
82
|
const SEEDS = [
|
|
8
83
|
{
|
|
9
84
|
id: '1001',
|
|
@@ -121,6 +196,7 @@ const SEEDS = [
|
|
|
121
196
|
[75, '水疗'],
|
|
122
197
|
],
|
|
123
198
|
base: 899,
|
|
199
|
+
style: 'courtyard',
|
|
124
200
|
},
|
|
125
201
|
{
|
|
126
202
|
id: '6001',
|
|
@@ -157,64 +233,706 @@ const SEEDS = [
|
|
|
157
233
|
base: 439,
|
|
158
234
|
prepaidOnly: true,
|
|
159
235
|
},
|
|
236
|
+
// ---- 2026-10-07: more of Huazhu's range and of China (robin): from a
|
|
237
|
+
// roadside 海友 to a 花间堂 in an old town, every brand priced, roomed and
|
|
238
|
+
// stocked as its part of the range would be (`TIERS`).
|
|
239
|
+
{
|
|
240
|
+
id: '1003',
|
|
241
|
+
name: '海友酒店(上海虹桥火车站店)',
|
|
242
|
+
en: 'Hi Inn Shanghai Hongqiao Railway Station',
|
|
243
|
+
brand: '海友',
|
|
244
|
+
brandCode: 1,
|
|
245
|
+
province: ['310000', '上海市'],
|
|
246
|
+
city: ['310100', '上海市'],
|
|
247
|
+
county: '闵行区',
|
|
248
|
+
address: '上海市闵行区申虹路518号',
|
|
249
|
+
addressEn: '518 Shenhong Road, Minhang District, Shanghai',
|
|
250
|
+
near: '虹桥火车站',
|
|
251
|
+
services: [
|
|
252
|
+
[85, '自助洗衣房'],
|
|
253
|
+
[95, '行李寄存'],
|
|
254
|
+
],
|
|
255
|
+
base: 219,
|
|
256
|
+
tier: 'economy',
|
|
257
|
+
},
|
|
258
|
+
{
|
|
259
|
+
id: '1004',
|
|
260
|
+
name: '禧玥酒店(上海陆家嘴店)',
|
|
261
|
+
en: 'Joya Hotel Shanghai Lujiazui',
|
|
262
|
+
brand: '禧玥',
|
|
263
|
+
brandCode: 18,
|
|
264
|
+
province: ['310000', '上海市'],
|
|
265
|
+
city: ['310100', '上海市'],
|
|
266
|
+
county: '浦东新区',
|
|
267
|
+
address: '上海市浦东新区世纪大道1288号',
|
|
268
|
+
addressEn: '1288 Century Avenue, Pudong New Area, Shanghai',
|
|
269
|
+
near: '陆家嘴',
|
|
270
|
+
services: [
|
|
271
|
+
[71, '健身房'],
|
|
272
|
+
[73, '室内游泳池'],
|
|
273
|
+
[49, '中餐厅'],
|
|
274
|
+
[90, '会议室'],
|
|
275
|
+
[60, '停车场'],
|
|
276
|
+
],
|
|
277
|
+
base: 1199,
|
|
278
|
+
tier: 'luxury',
|
|
279
|
+
advance: true,
|
|
280
|
+
},
|
|
281
|
+
{
|
|
282
|
+
id: '1005',
|
|
283
|
+
name: '桔子水晶酒店(上海静安寺店)',
|
|
284
|
+
en: "Crystal Orange Hotel Shanghai Jing'an Temple",
|
|
285
|
+
brand: '桔子水晶',
|
|
286
|
+
brandCode: 8,
|
|
287
|
+
province: ['310000', '上海市'],
|
|
288
|
+
city: ['310100', '上海市'],
|
|
289
|
+
county: '静安区',
|
|
290
|
+
address: '上海市静安区南京西路1966号',
|
|
291
|
+
addressEn: "1966 West Nanjing Road, Jing'an District, Shanghai",
|
|
292
|
+
near: '静安寺',
|
|
293
|
+
services: [
|
|
294
|
+
[71, '健身房'],
|
|
295
|
+
[49, '中餐厅'],
|
|
296
|
+
[90, '会议室'],
|
|
297
|
+
],
|
|
298
|
+
base: 679,
|
|
299
|
+
tier: 'upscale',
|
|
300
|
+
advance: true,
|
|
301
|
+
},
|
|
302
|
+
{
|
|
303
|
+
id: '2003',
|
|
304
|
+
name: '汉庭酒店(北京王府井店)',
|
|
305
|
+
en: 'Hanting Hotel Beijing Wangfujing',
|
|
306
|
+
brand: '汉庭',
|
|
307
|
+
brandCode: 2,
|
|
308
|
+
province: ['110000', '北京市'],
|
|
309
|
+
city: ['110100', '北京市'],
|
|
310
|
+
county: '东城区',
|
|
311
|
+
address: '北京市东城区王府井大街216号',
|
|
312
|
+
addressEn: '216 Wangfujing Street, Dongcheng District, Beijing',
|
|
313
|
+
near: '王府井',
|
|
314
|
+
services: [
|
|
315
|
+
[85, '自助洗衣房'],
|
|
316
|
+
[95, '行李寄存'],
|
|
317
|
+
],
|
|
318
|
+
base: 329,
|
|
319
|
+
tier: 'economy',
|
|
320
|
+
},
|
|
321
|
+
{
|
|
322
|
+
id: '2004',
|
|
323
|
+
name: '美仑酒店(北京金融街店)',
|
|
324
|
+
en: 'Meilun Hotel Beijing Financial Street',
|
|
325
|
+
brand: '美仑',
|
|
326
|
+
brandCode: 15,
|
|
327
|
+
province: ['110000', '北京市'],
|
|
328
|
+
city: ['110100', '北京市'],
|
|
329
|
+
county: '西城区',
|
|
330
|
+
address: '北京市西城区金融大街36号',
|
|
331
|
+
addressEn: '36 Financial Street, Xicheng District, Beijing',
|
|
332
|
+
near: '金融街',
|
|
333
|
+
services: [
|
|
334
|
+
[71, '健身房'],
|
|
335
|
+
[73, '室内游泳池'],
|
|
336
|
+
[49, '中餐厅'],
|
|
337
|
+
[90, '会议室'],
|
|
338
|
+
[60, '停车场'],
|
|
339
|
+
],
|
|
340
|
+
base: 899,
|
|
341
|
+
tier: 'luxury',
|
|
342
|
+
advance: true,
|
|
343
|
+
},
|
|
344
|
+
{
|
|
345
|
+
id: '2005',
|
|
346
|
+
name: '全季酒店(北京中关村店)',
|
|
347
|
+
en: 'JI Hotel Beijing Zhongguancun',
|
|
348
|
+
brand: '全季',
|
|
349
|
+
brandCode: 3,
|
|
350
|
+
province: ['110000', '北京市'],
|
|
351
|
+
city: ['110100', '北京市'],
|
|
352
|
+
county: '海淀区',
|
|
353
|
+
address: '北京市海淀区中关村大街56号',
|
|
354
|
+
addressEn: '56 Zhongguancun Street, Haidian District, Beijing',
|
|
355
|
+
near: '中关村',
|
|
356
|
+
services: [
|
|
357
|
+
[71, '健身房'],
|
|
358
|
+
[85, '自助洗衣房'],
|
|
359
|
+
],
|
|
360
|
+
base: 459,
|
|
361
|
+
tier: 'midscale',
|
|
362
|
+
},
|
|
363
|
+
{
|
|
364
|
+
id: '3002',
|
|
365
|
+
name: '花间堂(杭州西溪店)',
|
|
366
|
+
en: 'Blossom House Hangzhou Xixi Wetland',
|
|
367
|
+
brand: '花间堂',
|
|
368
|
+
brandCode: 21,
|
|
369
|
+
province: ['330000', '浙江省'],
|
|
370
|
+
city: ['330100', '杭州市'],
|
|
371
|
+
county: '西湖区',
|
|
372
|
+
address: '浙江省杭州市西湖区天目山路658号',
|
|
373
|
+
addressEn: '658 Tianmushan Road, Xihu District, Hangzhou',
|
|
374
|
+
near: '西溪湿地',
|
|
375
|
+
services: [
|
|
376
|
+
[49, '中餐厅'],
|
|
377
|
+
[97, '茶室'],
|
|
378
|
+
[98, '庭院'],
|
|
379
|
+
],
|
|
380
|
+
base: 1399,
|
|
381
|
+
tier: 'boutique',
|
|
382
|
+
style: 'courtyard',
|
|
383
|
+
},
|
|
384
|
+
{
|
|
385
|
+
id: '3003',
|
|
386
|
+
name: '全季酒店(杭州东站店)',
|
|
387
|
+
en: 'JI Hotel Hangzhou East Railway Station',
|
|
388
|
+
brand: '全季',
|
|
389
|
+
brandCode: 3,
|
|
390
|
+
province: ['330000', '浙江省'],
|
|
391
|
+
city: ['330100', '杭州市'],
|
|
392
|
+
county: '上城区',
|
|
393
|
+
address: '浙江省杭州市上城区天城路188号',
|
|
394
|
+
addressEn: '188 Tiancheng Road, Shangcheng District, Hangzhou',
|
|
395
|
+
near: '杭州东站',
|
|
396
|
+
services: [
|
|
397
|
+
[71, '健身房'],
|
|
398
|
+
[85, '自助洗衣房'],
|
|
399
|
+
],
|
|
400
|
+
base: 399,
|
|
401
|
+
tier: 'midscale',
|
|
402
|
+
advance: true,
|
|
403
|
+
},
|
|
404
|
+
{
|
|
405
|
+
id: '4003',
|
|
406
|
+
name: '桔子酒店(深圳华强北店)',
|
|
407
|
+
en: 'Orange Hotel Shenzhen Huaqiangbei',
|
|
408
|
+
brand: '桔子',
|
|
409
|
+
brandCode: 7,
|
|
410
|
+
province: ['440000', '广东省'],
|
|
411
|
+
city: ['440300', '深圳市'],
|
|
412
|
+
county: '福田区',
|
|
413
|
+
address: '广东省深圳市福田区华强北路1018号',
|
|
414
|
+
addressEn: '1018 Huaqiang North Road, Futian District, Shenzhen',
|
|
415
|
+
near: '华强北',
|
|
416
|
+
services: [
|
|
417
|
+
[71, '健身房'],
|
|
418
|
+
[85, '自助洗衣房'],
|
|
419
|
+
],
|
|
420
|
+
base: 419,
|
|
421
|
+
tier: 'midscale',
|
|
422
|
+
},
|
|
423
|
+
{
|
|
424
|
+
id: '5002',
|
|
425
|
+
name: '全季酒店(成都春熙路店)',
|
|
426
|
+
en: 'JI Hotel Chengdu Chunxi Road',
|
|
427
|
+
brand: '全季',
|
|
428
|
+
brandCode: 3,
|
|
429
|
+
province: ['510000', '四川省'],
|
|
430
|
+
city: ['510100', '成都市'],
|
|
431
|
+
county: '锦江区',
|
|
432
|
+
address: '四川省成都市锦江区红星路三段99号',
|
|
433
|
+
addressEn: '99 Section 3 Hongxing Road, Jinjiang District, Chengdu',
|
|
434
|
+
near: '春熙路',
|
|
435
|
+
services: [
|
|
436
|
+
[71, '健身房'],
|
|
437
|
+
[85, '自助洗衣房'],
|
|
438
|
+
],
|
|
439
|
+
base: 429,
|
|
440
|
+
tier: 'midscale',
|
|
441
|
+
advance: true,
|
|
442
|
+
},
|
|
443
|
+
{
|
|
444
|
+
id: '6002',
|
|
445
|
+
name: '汉庭酒店(广州北京路店)',
|
|
446
|
+
en: 'Hanting Hotel Guangzhou Beijing Road',
|
|
447
|
+
brand: '汉庭',
|
|
448
|
+
brandCode: 2,
|
|
449
|
+
province: ['440000', '广东省'],
|
|
450
|
+
city: ['440100', '广州市'],
|
|
451
|
+
county: '越秀区',
|
|
452
|
+
address: '广东省广州市越秀区北京路318号',
|
|
453
|
+
addressEn: '318 Beijing Road, Yuexiu District, Guangzhou',
|
|
454
|
+
near: '北京路步行街',
|
|
455
|
+
services: [
|
|
456
|
+
[85, '自助洗衣房'],
|
|
457
|
+
[95, '行李寄存'],
|
|
458
|
+
],
|
|
459
|
+
base: 259,
|
|
460
|
+
tier: 'economy',
|
|
461
|
+
},
|
|
462
|
+
{
|
|
463
|
+
id: '6003',
|
|
464
|
+
name: '城际酒店(广州天河体育中心店)',
|
|
465
|
+
en: 'Intercity Hotel Guangzhou Tianhe Sports Centre',
|
|
466
|
+
brand: '城际',
|
|
467
|
+
brandCode: 12,
|
|
468
|
+
province: ['440000', '广东省'],
|
|
469
|
+
city: ['440100', '广州市'],
|
|
470
|
+
county: '天河区',
|
|
471
|
+
address: '广东省广州市天河区体育西路191号',
|
|
472
|
+
addressEn: '191 Tiyu West Road, Tianhe District, Guangzhou',
|
|
473
|
+
near: '天河体育中心',
|
|
474
|
+
services: [
|
|
475
|
+
[71, '健身房'],
|
|
476
|
+
[49, '中餐厅'],
|
|
477
|
+
[90, '会议室'],
|
|
478
|
+
],
|
|
479
|
+
base: 569,
|
|
480
|
+
tier: 'upscale',
|
|
481
|
+
advance: true,
|
|
482
|
+
},
|
|
483
|
+
{
|
|
484
|
+
id: '7001',
|
|
485
|
+
name: '全季酒店(西安钟楼店)',
|
|
486
|
+
en: "JI Hotel Xi'an Bell Tower",
|
|
487
|
+
brand: '全季',
|
|
488
|
+
brandCode: 3,
|
|
489
|
+
province: ['610000', '陕西省'],
|
|
490
|
+
city: ['610100', '西安市'],
|
|
491
|
+
county: '碑林区',
|
|
492
|
+
address: '陕西省西安市碑林区东大街288号',
|
|
493
|
+
addressEn: "288 East Street, Beilin District, Xi'an",
|
|
494
|
+
near: '钟楼',
|
|
495
|
+
services: [
|
|
496
|
+
[71, '健身房'],
|
|
497
|
+
[85, '自助洗衣房'],
|
|
498
|
+
],
|
|
499
|
+
base: 399,
|
|
500
|
+
tier: 'midscale',
|
|
501
|
+
},
|
|
502
|
+
{
|
|
503
|
+
id: '7002',
|
|
504
|
+
name: '漫心酒店(西安大雁塔店)',
|
|
505
|
+
en: "Manxin Hotel Xi'an Big Wild Goose Pagoda",
|
|
506
|
+
brand: '漫心',
|
|
507
|
+
brandCode: 11,
|
|
508
|
+
province: ['610000', '陕西省'],
|
|
509
|
+
city: ['610100', '西安市'],
|
|
510
|
+
county: '雁塔区',
|
|
511
|
+
address: '陕西省西安市雁塔区慈恩路66号',
|
|
512
|
+
addressEn: "66 Ci'en Road, Yanta District, Xi'an",
|
|
513
|
+
near: '大雁塔',
|
|
514
|
+
services: [
|
|
515
|
+
[71, '健身房'],
|
|
516
|
+
[49, '中餐厅'],
|
|
517
|
+
[90, '会议室'],
|
|
518
|
+
],
|
|
519
|
+
base: 629,
|
|
520
|
+
tier: 'upscale',
|
|
521
|
+
style: 'courtyard',
|
|
522
|
+
},
|
|
523
|
+
{
|
|
524
|
+
id: '7101',
|
|
525
|
+
name: '全季酒店(南京新街口店)',
|
|
526
|
+
en: 'JI Hotel Nanjing Xinjiekou',
|
|
527
|
+
brand: '全季',
|
|
528
|
+
brandCode: 3,
|
|
529
|
+
province: ['320000', '江苏省'],
|
|
530
|
+
city: ['320100', '南京市'],
|
|
531
|
+
county: '秦淮区',
|
|
532
|
+
address: '江苏省南京市秦淮区中山南路88号',
|
|
533
|
+
addressEn: '88 Zhongshan South Road, Qinhuai District, Nanjing',
|
|
534
|
+
near: '新街口',
|
|
535
|
+
services: [
|
|
536
|
+
[71, '健身房'],
|
|
537
|
+
[85, '自助洗衣房'],
|
|
538
|
+
],
|
|
539
|
+
base: 419,
|
|
540
|
+
tier: 'midscale',
|
|
541
|
+
advance: true,
|
|
542
|
+
},
|
|
543
|
+
{
|
|
544
|
+
id: '7102',
|
|
545
|
+
name: '星程酒店(南京夫子庙店)',
|
|
546
|
+
en: 'Starway Hotel Nanjing Confucius Temple',
|
|
547
|
+
brand: '星程',
|
|
548
|
+
brandCode: 5,
|
|
549
|
+
province: ['320000', '江苏省'],
|
|
550
|
+
city: ['320100', '南京市'],
|
|
551
|
+
county: '秦淮区',
|
|
552
|
+
address: '江苏省南京市秦淮区贡院街158号',
|
|
553
|
+
addressEn: '158 Gongyuan Street, Qinhuai District, Nanjing',
|
|
554
|
+
near: '夫子庙',
|
|
555
|
+
services: [
|
|
556
|
+
[71, '健身房'],
|
|
557
|
+
[85, '自助洗衣房'],
|
|
558
|
+
],
|
|
559
|
+
base: 299,
|
|
560
|
+
tier: 'midscale',
|
|
561
|
+
},
|
|
562
|
+
{
|
|
563
|
+
id: '7201',
|
|
564
|
+
name: '花间堂(苏州平江路店)',
|
|
565
|
+
en: 'Blossom House Suzhou Pingjiang Road',
|
|
566
|
+
brand: '花间堂',
|
|
567
|
+
brandCode: 21,
|
|
568
|
+
province: ['320000', '江苏省'],
|
|
569
|
+
city: ['320500', '苏州市'],
|
|
570
|
+
county: '姑苏区',
|
|
571
|
+
address: '江苏省苏州市姑苏区平江路238号',
|
|
572
|
+
addressEn: '238 Pingjiang Road, Gusu District, Suzhou',
|
|
573
|
+
near: '平江路',
|
|
574
|
+
services: [
|
|
575
|
+
[49, '中餐厅'],
|
|
576
|
+
[97, '茶室'],
|
|
577
|
+
[98, '庭院'],
|
|
578
|
+
],
|
|
579
|
+
base: 1299,
|
|
580
|
+
tier: 'boutique',
|
|
581
|
+
style: 'courtyard',
|
|
582
|
+
},
|
|
583
|
+
{
|
|
584
|
+
id: '7202',
|
|
585
|
+
name: '汉庭酒店(苏州观前街店)',
|
|
586
|
+
en: 'Hanting Hotel Suzhou Guanqian Street',
|
|
587
|
+
brand: '汉庭',
|
|
588
|
+
brandCode: 2,
|
|
589
|
+
province: ['320000', '江苏省'],
|
|
590
|
+
city: ['320500', '苏州市'],
|
|
591
|
+
county: '姑苏区',
|
|
592
|
+
address: '江苏省苏州市姑苏区人民路1188号',
|
|
593
|
+
addressEn: '1188 Renmin Road, Gusu District, Suzhou',
|
|
594
|
+
near: '观前街',
|
|
595
|
+
services: [
|
|
596
|
+
[85, '自助洗衣房'],
|
|
597
|
+
[95, '行李寄存'],
|
|
598
|
+
],
|
|
599
|
+
base: 239,
|
|
600
|
+
tier: 'economy',
|
|
601
|
+
},
|
|
602
|
+
{
|
|
603
|
+
id: '7301',
|
|
604
|
+
name: '桔子酒店(武汉江汉路店)',
|
|
605
|
+
en: 'Orange Hotel Wuhan Jianghan Road',
|
|
606
|
+
brand: '桔子',
|
|
607
|
+
brandCode: 7,
|
|
608
|
+
province: ['420000', '湖北省'],
|
|
609
|
+
city: ['420100', '武汉市'],
|
|
610
|
+
county: '江汉区',
|
|
611
|
+
address: '湖北省武汉市江汉区中山大道868号',
|
|
612
|
+
addressEn: '868 Zhongshan Avenue, Jianghan District, Wuhan',
|
|
613
|
+
near: '江汉路步行街',
|
|
614
|
+
services: [
|
|
615
|
+
[71, '健身房'],
|
|
616
|
+
[85, '自助洗衣房'],
|
|
617
|
+
],
|
|
618
|
+
base: 379,
|
|
619
|
+
tier: 'midscale',
|
|
620
|
+
},
|
|
621
|
+
{
|
|
622
|
+
id: '7302',
|
|
623
|
+
name: '美仑酒店(武汉光谷店)',
|
|
624
|
+
en: 'Meilun Hotel Wuhan Optics Valley',
|
|
625
|
+
brand: '美仑',
|
|
626
|
+
brandCode: 15,
|
|
627
|
+
province: ['420000', '湖北省'],
|
|
628
|
+
city: ['420100', '武汉市'],
|
|
629
|
+
county: '洪山区',
|
|
630
|
+
address: '湖北省武汉市洪山区珞喻路766号',
|
|
631
|
+
addressEn: '766 Luoyu Road, Hongshan District, Wuhan',
|
|
632
|
+
near: '光谷广场',
|
|
633
|
+
services: [
|
|
634
|
+
[71, '健身房'],
|
|
635
|
+
[73, '室内游泳池'],
|
|
636
|
+
[49, '中餐厅'],
|
|
637
|
+
[90, '会议室'],
|
|
638
|
+
[60, '停车场'],
|
|
639
|
+
],
|
|
640
|
+
base: 699,
|
|
641
|
+
tier: 'luxury',
|
|
642
|
+
advance: true,
|
|
643
|
+
},
|
|
644
|
+
{
|
|
645
|
+
id: '7401',
|
|
646
|
+
name: '漫心酒店(重庆解放碑店)',
|
|
647
|
+
en: 'Manxin Hotel Chongqing Jiefangbei',
|
|
648
|
+
brand: '漫心',
|
|
649
|
+
brandCode: 11,
|
|
650
|
+
province: ['500000', '重庆市'],
|
|
651
|
+
city: ['500100', '重庆市'],
|
|
652
|
+
county: '渝中区',
|
|
653
|
+
address: '重庆市渝中区民族路166号',
|
|
654
|
+
addressEn: '166 Minzu Road, Yuzhong District, Chongqing',
|
|
655
|
+
near: '解放碑',
|
|
656
|
+
services: [
|
|
657
|
+
[71, '健身房'],
|
|
658
|
+
[49, '中餐厅'],
|
|
659
|
+
[90, '会议室'],
|
|
660
|
+
],
|
|
661
|
+
base: 599,
|
|
662
|
+
tier: 'upscale',
|
|
663
|
+
},
|
|
664
|
+
{
|
|
665
|
+
id: '7501',
|
|
666
|
+
name: '全季酒店(厦门中山路店)',
|
|
667
|
+
en: 'JI Hotel Xiamen Zhongshan Road',
|
|
668
|
+
brand: '全季',
|
|
669
|
+
brandCode: 3,
|
|
670
|
+
province: ['350000', '福建省'],
|
|
671
|
+
city: ['350200', '厦门市'],
|
|
672
|
+
county: '思明区',
|
|
673
|
+
address: '福建省厦门市思明区中山路218号',
|
|
674
|
+
addressEn: '218 Zhongshan Road, Siming District, Xiamen',
|
|
675
|
+
near: '中山路步行街',
|
|
676
|
+
services: [
|
|
677
|
+
[71, '健身房'],
|
|
678
|
+
[85, '自助洗衣房'],
|
|
679
|
+
],
|
|
680
|
+
base: 459,
|
|
681
|
+
tier: 'midscale',
|
|
682
|
+
},
|
|
683
|
+
{
|
|
684
|
+
id: '7601',
|
|
685
|
+
name: '禧玥酒店(三亚亚龙湾店)',
|
|
686
|
+
en: 'Joya Hotel Sanya Yalong Bay',
|
|
687
|
+
brand: '禧玥',
|
|
688
|
+
brandCode: 18,
|
|
689
|
+
province: ['460000', '海南省'],
|
|
690
|
+
city: ['460200', '三亚市'],
|
|
691
|
+
county: '吉阳区',
|
|
692
|
+
address: '海南省三亚市吉阳区亚龙湾路99号',
|
|
693
|
+
addressEn: '99 Yalong Bay Road, Jiyang District, Sanya',
|
|
694
|
+
near: '亚龙湾',
|
|
695
|
+
services: [
|
|
696
|
+
[71, '健身房'],
|
|
697
|
+
[73, '室内游泳池'],
|
|
698
|
+
[49, '中餐厅'],
|
|
699
|
+
[90, '会议室'],
|
|
700
|
+
[60, '停车场'],
|
|
701
|
+
[99, '私人海滩'],
|
|
702
|
+
],
|
|
703
|
+
base: 1299,
|
|
704
|
+
tier: 'luxury',
|
|
705
|
+
style: 'resort',
|
|
706
|
+
advance: true,
|
|
707
|
+
},
|
|
708
|
+
{
|
|
709
|
+
id: '7701',
|
|
710
|
+
name: '城际酒店(青岛五四广场店)',
|
|
711
|
+
en: 'Intercity Hotel Qingdao May Fourth Square',
|
|
712
|
+
brand: '城际',
|
|
713
|
+
brandCode: 12,
|
|
714
|
+
province: ['370000', '山东省'],
|
|
715
|
+
city: ['370200', '青岛市'],
|
|
716
|
+
county: '市南区',
|
|
717
|
+
address: '山东省青岛市市南区香港中路36号',
|
|
718
|
+
addressEn: '36 Hong Kong Middle Road, Shinan District, Qingdao',
|
|
719
|
+
near: '五四广场',
|
|
720
|
+
services: [
|
|
721
|
+
[71, '健身房'],
|
|
722
|
+
[49, '中餐厅'],
|
|
723
|
+
[90, '会议室'],
|
|
724
|
+
],
|
|
725
|
+
base: 529,
|
|
726
|
+
tier: 'upscale',
|
|
727
|
+
},
|
|
728
|
+
{
|
|
729
|
+
id: '7801',
|
|
730
|
+
name: '汉庭酒店(长沙五一广场店)',
|
|
731
|
+
en: 'Hanting Hotel Changsha Wuyi Square',
|
|
732
|
+
brand: '汉庭',
|
|
733
|
+
brandCode: 2,
|
|
734
|
+
province: ['430000', '湖南省'],
|
|
735
|
+
city: ['430100', '长沙市'],
|
|
736
|
+
county: '芙蓉区',
|
|
737
|
+
address: '湖南省长沙市芙蓉区五一大道388号',
|
|
738
|
+
addressEn: '388 Wuyi Avenue, Furong District, Changsha',
|
|
739
|
+
near: '五一广场',
|
|
740
|
+
services: [
|
|
741
|
+
[85, '自助洗衣房'],
|
|
742
|
+
[95, '行李寄存'],
|
|
743
|
+
],
|
|
744
|
+
base: 229,
|
|
745
|
+
tier: 'economy',
|
|
746
|
+
},
|
|
747
|
+
{
|
|
748
|
+
id: '7901',
|
|
749
|
+
name: '花间堂(丽江古城店)',
|
|
750
|
+
en: 'Blossom House Lijiang Old Town',
|
|
751
|
+
brand: '花间堂',
|
|
752
|
+
brandCode: 21,
|
|
753
|
+
province: ['530000', '云南省'],
|
|
754
|
+
city: ['530700', '丽江市'],
|
|
755
|
+
county: '古城区',
|
|
756
|
+
address: '云南省丽江市古城区新华街黄山上段18号',
|
|
757
|
+
addressEn: '18 Upper Huangshan Section, Xinhua Street, Gucheng District, Lijiang',
|
|
758
|
+
near: '丽江古城',
|
|
759
|
+
services: [
|
|
760
|
+
[49, '中餐厅'],
|
|
761
|
+
[97, '茶室'],
|
|
762
|
+
[98, '庭院'],
|
|
763
|
+
],
|
|
764
|
+
base: 999,
|
|
765
|
+
tier: 'boutique',
|
|
766
|
+
style: 'courtyard',
|
|
767
|
+
},
|
|
160
768
|
];
|
|
161
|
-
/**
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
}));
|
|
184
|
-
/**
|
|
769
|
+
/**
|
|
770
|
+
* Each hotel's lead picture, from the pool its style asks for - a garden hotel
|
|
771
|
+
* leads with a garden - and never one another hotel already has.
|
|
772
|
+
*/
|
|
773
|
+
const leadOf = (() => {
|
|
774
|
+
const used = new Set();
|
|
775
|
+
const want = (s) => s.style === 'courtyard'
|
|
776
|
+
? ['courtyard', 'lobby']
|
|
777
|
+
: s.style === 'resort'
|
|
778
|
+
? ['resort', 'lobby']
|
|
779
|
+
: ['lobby', 'exterior', 'resort', 'courtyard'];
|
|
780
|
+
const chosen = new Map();
|
|
781
|
+
for (const s of SEEDS) {
|
|
782
|
+
const kinds = want(s);
|
|
783
|
+
const p = kinds.flatMap((k) => LEADS.filter((x) => x.kind === k && !used.has(x.id)))[0] ??
|
|
784
|
+
LEADS.find((x) => !used.has(x.id));
|
|
785
|
+
if (!p)
|
|
786
|
+
throw new Error(`sandbox: more hotels than lead pictures (${SEEDS.length} > ${LEADS.length})`);
|
|
787
|
+
used.add(p.id);
|
|
788
|
+
chosen.set(s.id, p);
|
|
789
|
+
}
|
|
790
|
+
return (s) => chosen.get(s.id);
|
|
791
|
+
})();
|
|
792
|
+
/** One photograph, as Huazhu's /hotel lists it, credited. */
|
|
793
|
+
const photo = (p, width, over) => ({
|
|
794
|
+
fileUrl: stockUrl(p, width),
|
|
795
|
+
fileFormat: 'jpg',
|
|
796
|
+
fileTitle: `Photo by ${p.author} on Unsplash`,
|
|
797
|
+
descript: creditOf(p),
|
|
798
|
+
status: 1,
|
|
799
|
+
...over,
|
|
800
|
+
});
|
|
801
|
+
/** The rooms a hotel has: its tier's, or the original nine's two. */
|
|
802
|
+
const roomsOf = (s) => s.tier ? TIERS[s.tier].rooms : (fakeHotel(s.id).hotelRoomTypeList ?? []);
|
|
803
|
+
/**
|
|
804
|
+
* The invented hotels, shaped as Huazhu's /hotel answers them. Their pictures
|
|
805
|
+
* are stock photographs - see `sandbox-photos.ts` - since none of them exists.
|
|
806
|
+
*/
|
|
807
|
+
export const SANDBOX_HOTELS = SEEDS.map((s, i) => {
|
|
808
|
+
const rooms = roomsOf(s);
|
|
809
|
+
const lead = leadOf(s);
|
|
810
|
+
const forRoom = (code) => code === 'SU1'
|
|
811
|
+
? SUITES[i % SUITES.length]
|
|
812
|
+
: code === 'TR1'
|
|
813
|
+
? TWIN_ROOMS[(i * 7) % TWIN_ROOMS.length]
|
|
814
|
+
: KING_ROOMS[i % KING_ROOMS.length];
|
|
815
|
+
return fakeHotel(s.id, {
|
|
816
|
+
hotelName: `【模拟】${s.name}`,
|
|
817
|
+
hotelNameEn: `[Sandbox] ${s.en}`,
|
|
818
|
+
brandCode: s.brandCode,
|
|
819
|
+
brandName: s.brand,
|
|
820
|
+
provinceNo: s.province[0],
|
|
821
|
+
provinceName: s.province[1],
|
|
822
|
+
cityNo: s.city[0],
|
|
823
|
+
cityName: s.city[1],
|
|
824
|
+
countyName: s.county,
|
|
825
|
+
hotelAddress: s.address,
|
|
826
|
+
hotelAddressEn: s.addressEn,
|
|
827
|
+
hotelWordIntroduce: '模拟数据:这家酒店不存在,只用于测试连接器。图片是图库照片,不是这家酒店。',
|
|
828
|
+
hotelServiceList: s.services.map(([childClassCode, childClassName]) => ({
|
|
829
|
+
childClassCode,
|
|
830
|
+
childClassName,
|
|
831
|
+
})),
|
|
832
|
+
hotelRoomTypeList: rooms,
|
|
833
|
+
hotelPhotoList: [
|
|
834
|
+
photo(lead, 1200, { typeCode: 'ImagePhoto', sortId: 1 }),
|
|
835
|
+
...rooms.map((r, j) => photo(forRoom(r.roomTypeCode), 900, {
|
|
836
|
+
typeCode: 'RoomType',
|
|
837
|
+
typeMappingCode: r.roomTypeCode,
|
|
838
|
+
sortId: 10 + j,
|
|
839
|
+
})),
|
|
840
|
+
],
|
|
841
|
+
hotelTrafficList: [
|
|
842
|
+
{ destinationType: '9', destinationName: s.near, destinationDistance: '500m' },
|
|
843
|
+
],
|
|
844
|
+
});
|
|
845
|
+
});
|
|
846
|
+
/** Friday and Saturday nights cost more, as they do at a city hotel - more again by the sea or in an old town. */
|
|
185
847
|
const weekend = (d) => {
|
|
186
848
|
const day = new Date(`${d}T00:00:00Z`).getUTCDay();
|
|
187
849
|
return day === 5 || day === 6;
|
|
188
850
|
};
|
|
189
|
-
const priced = (base) => (d) => Math.round(base * (weekend(d) ?
|
|
190
|
-
/**
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
851
|
+
const priced = (base, rise = 1.15) => (d) => Math.round(base * (weekend(d) ? rise : 1));
|
|
852
|
+
/**
|
|
853
|
+
* What a hotel sells. The original nine: three rates, fifty rooms of each a
|
|
854
|
+
* night - enough for many test bookings, since these never lapse. The rest:
|
|
855
|
+
* their tier's rooms and stock, a breakfast rate, a suite where the tier has
|
|
856
|
+
* one, and at some a cheaper rate booked three days ahead that cannot be
|
|
857
|
+
* cancelled.
|
|
858
|
+
*/
|
|
859
|
+
const ratesOf = (s) => {
|
|
860
|
+
const prepaid = s.prepaidOnly ? { guarantee: 'PN' } : {};
|
|
861
|
+
if (!s.tier) {
|
|
862
|
+
return [
|
|
863
|
+
{
|
|
864
|
+
product: product('DR1', 'BAR', { rateCodeName: '门市价' }),
|
|
865
|
+
price: priced(s.base),
|
|
866
|
+
inventory: () => 50,
|
|
867
|
+
cancel: '6PM1D1N_100P',
|
|
868
|
+
...prepaid,
|
|
869
|
+
},
|
|
870
|
+
{
|
|
871
|
+
product: product('DR1', 'BARB', { rateCodeName: '门市价含早' }),
|
|
872
|
+
price: priced(s.base + 40),
|
|
873
|
+
inventory: () => 50,
|
|
874
|
+
meal: { type: 'BB', count: 1 },
|
|
875
|
+
cancel: '6PM1D1N_100P',
|
|
876
|
+
...prepaid,
|
|
877
|
+
},
|
|
878
|
+
{
|
|
879
|
+
product: product('TR1', 'BAR', { rateCodeName: '门市价' }),
|
|
880
|
+
price: priced(s.base + 20),
|
|
881
|
+
inventory: () => 50,
|
|
882
|
+
cancel: 'AD0_0',
|
|
883
|
+
...prepaid,
|
|
884
|
+
},
|
|
885
|
+
];
|
|
886
|
+
}
|
|
887
|
+
const t = TIERS[s.tier];
|
|
888
|
+
const names = (code) => t.rooms.find((r) => r.roomTypeCode === code)?.roomTypeName ?? code;
|
|
889
|
+
const rates = [
|
|
194
890
|
{
|
|
195
|
-
product: product('DR1', 'BAR', { rateCodeName: '门市价' }),
|
|
196
|
-
price: priced(s.base),
|
|
197
|
-
inventory: () =>
|
|
891
|
+
product: product('DR1', 'BAR', { rateCodeName: '门市价', roomTypeName: names('DR1') }),
|
|
892
|
+
price: priced(s.base, t.weekend),
|
|
893
|
+
inventory: () => t.inventory,
|
|
198
894
|
cancel: '6PM1D1N_100P',
|
|
199
|
-
...(s.prepaidOnly ? { guarantee: 'PN' } : {}),
|
|
200
895
|
},
|
|
201
896
|
{
|
|
202
|
-
product: product('DR1', 'BARB', { rateCodeName: '门市价含早' }),
|
|
203
|
-
price: priced(s.base +
|
|
204
|
-
inventory: () =>
|
|
205
|
-
meal: { type: 'BB', count:
|
|
897
|
+
product: product('DR1', 'BARB', { rateCodeName: '门市价含早', roomTypeName: names('DR1') }),
|
|
898
|
+
price: priced(s.base + t.breakfast, t.weekend),
|
|
899
|
+
inventory: () => t.inventory,
|
|
900
|
+
meal: { type: 'BB', count: t.meals },
|
|
206
901
|
cancel: '6PM1D1N_100P',
|
|
207
|
-
...(s.prepaidOnly ? { guarantee: 'PN' } : {}),
|
|
208
902
|
},
|
|
209
903
|
{
|
|
210
|
-
product: product('TR1', 'BAR', { rateCodeName: '门市价' }),
|
|
211
|
-
price: priced(s.base +
|
|
212
|
-
inventory: () =>
|
|
213
|
-
|
|
214
|
-
|
|
904
|
+
product: product('TR1', 'BAR', { rateCodeName: '门市价', roomTypeName: names('TR1') }),
|
|
905
|
+
price: priced(s.base + t.twin, t.weekend),
|
|
906
|
+
inventory: () => t.inventory,
|
|
907
|
+
// The cheaper the hotel, the later a twin room can still be let go.
|
|
908
|
+
cancel: s.tier === 'economy' || s.tier === 'midscale' ? 'AD0_0' : '6PM1D1N_100P',
|
|
215
909
|
},
|
|
216
|
-
]
|
|
217
|
-
|
|
910
|
+
];
|
|
911
|
+
if (s.advance) {
|
|
912
|
+
rates.push({
|
|
913
|
+
product: product('DR1', 'ADV3', {
|
|
914
|
+
rateCodeName: '提前3天预订特惠(不可取消)',
|
|
915
|
+
roomTypeName: names('DR1'),
|
|
916
|
+
}),
|
|
917
|
+
price: priced(Math.round(s.base * 0.88), t.weekend),
|
|
918
|
+
inventory: () => Math.ceil(t.inventory / 2),
|
|
919
|
+
cancel: 'AD100P_100P',
|
|
920
|
+
restriction: { minAdvanceDay: () => 3 },
|
|
921
|
+
});
|
|
922
|
+
}
|
|
923
|
+
if (t.suites) {
|
|
924
|
+
rates.push({
|
|
925
|
+
product: product('SU1', 'BARB', { rateCodeName: '套房含早', roomTypeName: names('SU1') }),
|
|
926
|
+
price: priced(Math.round(s.base * 1.9 + t.breakfast * 2), t.weekend),
|
|
927
|
+
inventory: () => t.suites,
|
|
928
|
+
adults: 3,
|
|
929
|
+
meal: { type: 'BB', count: 2 },
|
|
930
|
+
cancel: '6PM1D1N_100P',
|
|
931
|
+
});
|
|
932
|
+
}
|
|
933
|
+
return rates;
|
|
934
|
+
};
|
|
935
|
+
export const SANDBOX_RATES = Object.fromEntries(SEEDS.map((s) => [s.id, ratesOf(s)]));
|
|
218
936
|
/** How long a booking is kept after its check-out, as the connector keeps its own copy. */
|
|
219
937
|
const KEEP_DAYS = 90;
|
|
220
938
|
export const startSandbox = (o = {}) => {
|