@pisell/pisellos 2.2.136 → 2.2.137
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/solution/Sales/index.d.ts +8 -1
- package/dist/solution/Sales/index.js +193 -33
- package/dist/solution/Sales/types.d.ts +12 -0
- package/lib/model/strategy/adapter/promotion/index.js +0 -49
- package/lib/solution/Sales/index.d.ts +8 -1
- package/lib/solution/Sales/index.js +127 -8
- package/lib/solution/Sales/types.d.ts +12 -0
- package/package.json +1 -1
|
@@ -2,7 +2,7 @@ import dayjs from 'dayjs';
|
|
|
2
2
|
import { Module, ModuleOptions, PisellCore } from '../../types';
|
|
3
3
|
import { BaseModule } from '../../modules/BaseModule';
|
|
4
4
|
import type { RequestPlugin } from '../../plugins/request';
|
|
5
|
-
import { SalesState, SalesModuleAPI, Reservation, SalesResourceBookingItem, SalesTimelineHighlights, ChangeBookingStatusParams } from './types';
|
|
5
|
+
import { SalesState, SalesModuleAPI, Reservation, SalesResourceBookingItem, SalesResourceBookingItemWithGroups, SalesTimelineHighlights, ChangeBookingStatusParams, GetResourceBookingItemParams } from './types';
|
|
6
6
|
import type { BookingData } from '../../server/modules/order/types';
|
|
7
7
|
export * from './types';
|
|
8
8
|
/**
|
|
@@ -79,6 +79,10 @@ export declare class SalesImpl extends BaseModule implements Module, SalesModule
|
|
|
79
79
|
*/
|
|
80
80
|
private getBookingStatus;
|
|
81
81
|
private getResourceId;
|
|
82
|
+
private getBookingAppointmentStatus;
|
|
83
|
+
private getBookingOrderPaymentStatus;
|
|
84
|
+
private isBookingMatchedResource;
|
|
85
|
+
private sortMatchedBookings;
|
|
82
86
|
/**
|
|
83
87
|
* 资源下 bookings 的返回裁剪策略:
|
|
84
88
|
* 1) 优先返回当前时刻正在进行中的预约(允许并发返回多条)
|
|
@@ -100,6 +104,8 @@ export declare class SalesImpl extends BaseModule implements Module, SalesModule
|
|
|
100
104
|
* - 否则走快照分支(纯时间窗判定,忽略 appointment_status 的 arrived/started/completed 差异)
|
|
101
105
|
*/
|
|
102
106
|
private normalizeMatchedBooking;
|
|
107
|
+
private normalizeResourceBookings;
|
|
108
|
+
private buildCompletedBooking;
|
|
103
109
|
/**
|
|
104
110
|
* 实时模式(current === deviceCurrent):信任 appointment_status
|
|
105
111
|
* - 过滤 new / completed(实时视角下视为非活跃数据)
|
|
@@ -124,6 +130,7 @@ export declare class SalesImpl extends BaseModule implements Module, SalesModule
|
|
|
124
130
|
*/
|
|
125
131
|
private buildSnapshotBooking;
|
|
126
132
|
getResourceBookingList(currentTime: string, bookingList?: BookingData[], deviceTime?: string): Promise<SalesResourceBookingItem[]>;
|
|
133
|
+
getResourceBookingItem(params: GetResourceBookingItemParams): Promise<SalesResourceBookingItemWithGroups | null>;
|
|
127
134
|
}
|
|
128
135
|
export { SalesImpl as Sales };
|
|
129
136
|
export default SalesImpl;
|
|
@@ -396,6 +396,38 @@ export var SalesImpl = /*#__PURE__*/function (_BaseModule) {
|
|
|
396
396
|
var _ref3, _resource$id;
|
|
397
397
|
return (_ref3 = (_resource$id = resource.id) !== null && _resource$id !== void 0 ? _resource$id : resource.form_record_id) !== null && _ref3 !== void 0 ? _ref3 : '';
|
|
398
398
|
}
|
|
399
|
+
}, {
|
|
400
|
+
key: "getBookingAppointmentStatus",
|
|
401
|
+
value: function getBookingAppointmentStatus(booking) {
|
|
402
|
+
var _ref4, _booking$appointment_2;
|
|
403
|
+
return String((_ref4 = (_booking$appointment_2 = booking.appointment_status) !== null && _booking$appointment_2 !== void 0 ? _booking$appointment_2 : booking.status) !== null && _ref4 !== void 0 ? _ref4 : '');
|
|
404
|
+
}
|
|
405
|
+
}, {
|
|
406
|
+
key: "getBookingOrderPaymentStatus",
|
|
407
|
+
value: function getBookingOrderPaymentStatus(booking) {
|
|
408
|
+
var _booking$order$paymen, _booking$order;
|
|
409
|
+
return String((_booking$order$paymen = (_booking$order = booking.order) === null || _booking$order === void 0 ? void 0 : _booking$order.payment_status) !== null && _booking$order$paymen !== void 0 ? _booking$order$paymen : '');
|
|
410
|
+
}
|
|
411
|
+
}, {
|
|
412
|
+
key: "isBookingMatchedResource",
|
|
413
|
+
value: function isBookingMatchedResource(booking, resourceId) {
|
|
414
|
+
if (!Array.isArray(booking.resources)) return false;
|
|
415
|
+
return booking.resources.some(function (resource) {
|
|
416
|
+
var relationId = resource === null || resource === void 0 ? void 0 : resource.relation_id;
|
|
417
|
+
if (relationId === undefined || relationId === null) return false;
|
|
418
|
+
return String(relationId) === String(resourceId);
|
|
419
|
+
});
|
|
420
|
+
}
|
|
421
|
+
}, {
|
|
422
|
+
key: "sortMatchedBookings",
|
|
423
|
+
value: function sortMatchedBookings(bookings) {
|
|
424
|
+
var _this2 = this;
|
|
425
|
+
return _toConsumableArray(bookings).sort(function (left, right) {
|
|
426
|
+
var leftStartAt = _this2.toBookingDateTime(left.start_date, left.start_time).valueOf();
|
|
427
|
+
var rightStartAt = _this2.toBookingDateTime(right.start_date, right.start_time).valueOf();
|
|
428
|
+
return leftStartAt - rightStartAt;
|
|
429
|
+
});
|
|
430
|
+
}
|
|
399
431
|
|
|
400
432
|
/**
|
|
401
433
|
* 资源下 bookings 的返回裁剪策略:
|
|
@@ -407,17 +439,17 @@ export var SalesImpl = /*#__PURE__*/function (_BaseModule) {
|
|
|
407
439
|
}, {
|
|
408
440
|
key: "pickBookingsForCurrentPoint",
|
|
409
441
|
value: function pickBookingsForCurrentPoint(current, bookings) {
|
|
410
|
-
var
|
|
442
|
+
var _this3 = this;
|
|
411
443
|
if (bookings.length === 0) return [];
|
|
412
444
|
var currentDayStart = current.startOf('day');
|
|
413
445
|
var currentDayEnd = current.endOf('day');
|
|
414
446
|
|
|
415
447
|
// 仅保留与 current 当天有时间交集的预约,避免跨天误命中
|
|
416
448
|
var dayScopedBookings = bookings.filter(function (booking) {
|
|
417
|
-
var startAt =
|
|
418
|
-
var endAt =
|
|
449
|
+
var startAt = _this3.toBookingDateTime(booking.start_date, booking.start_time);
|
|
450
|
+
var endAt = _this3.toBookingDateTime(booking.end_date, booking.end_time);
|
|
419
451
|
if (!startAt.isValid() || !endAt.isValid()) return false;
|
|
420
|
-
return
|
|
452
|
+
return _this3.isSameOrBefore(startAt, currentDayEnd) && _this3.isSameOrAfter(endAt, currentDayStart);
|
|
421
453
|
});
|
|
422
454
|
if (dayScopedBookings.length === 0) return [];
|
|
423
455
|
var appendNextStartGroupIfNeeded = function appendNextStartGroupIfNeeded(selectedBookings) {
|
|
@@ -427,21 +459,21 @@ export var SalesImpl = /*#__PURE__*/function (_BaseModule) {
|
|
|
427
459
|
});
|
|
428
460
|
if (!shouldAppendNextGroup) return selectedBookings;
|
|
429
461
|
var selectedStartValues = new Set(selectedBookings.map(function (booking) {
|
|
430
|
-
return
|
|
462
|
+
return _this3.toBookingDateTime(booking.start_date, booking.start_time);
|
|
431
463
|
}).filter(function (startAt) {
|
|
432
464
|
return startAt.isValid();
|
|
433
465
|
}).map(function (startAt) {
|
|
434
466
|
return startAt.valueOf();
|
|
435
467
|
}));
|
|
436
468
|
var futureCandidates = dayScopedBookings.filter(function (booking) {
|
|
437
|
-
var startAt =
|
|
469
|
+
var startAt = _this3.toBookingDateTime(booking.start_date, booking.start_time);
|
|
438
470
|
if (!startAt.isValid()) return false;
|
|
439
471
|
return startAt.isAfter(current) && !selectedStartValues.has(startAt.valueOf());
|
|
440
472
|
});
|
|
441
473
|
if (futureCandidates.length === 0) return selectedBookings;
|
|
442
|
-
var nextStartAt =
|
|
474
|
+
var nextStartAt = _this3.toBookingDateTime(futureCandidates[0].start_date, futureCandidates[0].start_time).valueOf();
|
|
443
475
|
var nextStartGroup = futureCandidates.filter(function (booking) {
|
|
444
|
-
var startAt =
|
|
476
|
+
var startAt = _this3.toBookingDateTime(booking.start_date, booking.start_time);
|
|
445
477
|
return startAt.isValid() && startAt.valueOf() === nextStartAt;
|
|
446
478
|
});
|
|
447
479
|
if (nextStartGroup.length === 0) return selectedBookings;
|
|
@@ -453,10 +485,10 @@ export var SalesImpl = /*#__PURE__*/function (_BaseModule) {
|
|
|
453
485
|
|
|
454
486
|
// active: current 处于 [startAt, endAt] 区间
|
|
455
487
|
var activeBookings = dayScopedBookings.filter(function (booking) {
|
|
456
|
-
var startAt =
|
|
457
|
-
var endAt =
|
|
488
|
+
var startAt = _this3.toBookingDateTime(booking.start_date, booking.start_time);
|
|
489
|
+
var endAt = _this3.toBookingDateTime(booking.end_date, booking.end_time);
|
|
458
490
|
if (!startAt.isValid() || !endAt.isValid()) return false;
|
|
459
|
-
return
|
|
491
|
+
return _this3.isSameOrAfter(current, startAt) && _this3.isSameOrBefore(current, endAt);
|
|
460
492
|
});
|
|
461
493
|
if (activeBookings.length > 0) return appendNextStartGroupIfNeeded(activeBookings);
|
|
462
494
|
|
|
@@ -468,13 +500,13 @@ export var SalesImpl = /*#__PURE__*/function (_BaseModule) {
|
|
|
468
500
|
|
|
469
501
|
// upcoming: 从未来预约里只取最早那一组(同 start_time)
|
|
470
502
|
var upcoming = dayScopedBookings.filter(function (booking) {
|
|
471
|
-
var startAt =
|
|
503
|
+
var startAt = _this3.toBookingDateTime(booking.start_date, booking.start_time);
|
|
472
504
|
return startAt.isValid() && startAt.isAfter(current) && startAt.isSame(current, 'day');
|
|
473
505
|
});
|
|
474
506
|
if (upcoming.length > 0) {
|
|
475
507
|
var firstStartAt = this.toBookingDateTime(upcoming[0].start_date, upcoming[0].start_time).valueOf();
|
|
476
508
|
var upcomingStartGroup = upcoming.filter(function (booking) {
|
|
477
|
-
var startAt =
|
|
509
|
+
var startAt = _this3.toBookingDateTime(booking.start_date, booking.start_time);
|
|
478
510
|
return startAt.valueOf() === firstStartAt;
|
|
479
511
|
});
|
|
480
512
|
return appendNextStartGroupIfNeeded(upcomingStartGroup);
|
|
@@ -511,16 +543,58 @@ export var SalesImpl = /*#__PURE__*/function (_BaseModule) {
|
|
|
511
543
|
}, {
|
|
512
544
|
key: "normalizeMatchedBooking",
|
|
513
545
|
value: function normalizeMatchedBooking(current, deviceCurrent, booking) {
|
|
514
|
-
var
|
|
515
|
-
var appointmentStatus =
|
|
546
|
+
var options = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : {};
|
|
547
|
+
var appointmentStatus = this.getBookingAppointmentStatus(booking);
|
|
516
548
|
if (appointmentStatus === 'rejected' || appointmentStatus === 'cancelled') return null;
|
|
517
549
|
var startAt = this.toBookingDateTime(booking.start_date, booking.start_time);
|
|
518
550
|
var endAt = this.toBookingDateTime(booking.end_date, booking.end_time);
|
|
551
|
+
if (appointmentStatus === 'completed' && options.includeCompleted) {
|
|
552
|
+
return this.buildCompletedBooking(current, booking, startAt, endAt);
|
|
553
|
+
}
|
|
519
554
|
if (current.isSame(deviceCurrent)) {
|
|
520
555
|
return this.buildRealtimeBooking(current, booking, appointmentStatus, startAt, endAt);
|
|
521
556
|
}
|
|
522
557
|
return this.buildSnapshotBooking(current, booking, appointmentStatus, startAt, endAt);
|
|
523
558
|
}
|
|
559
|
+
}, {
|
|
560
|
+
key: "normalizeResourceBookings",
|
|
561
|
+
value: function normalizeResourceBookings(current, deviceCurrent, bookingList) {
|
|
562
|
+
var _this4 = this;
|
|
563
|
+
var options = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : {};
|
|
564
|
+
var normalizedBookings = bookingList.map(function (booking) {
|
|
565
|
+
return _this4.normalizeMatchedBooking(current, deviceCurrent, booking, options);
|
|
566
|
+
}).filter(function (booking) {
|
|
567
|
+
return Boolean(booking);
|
|
568
|
+
}).filter(function (booking) {
|
|
569
|
+
return booking.status;
|
|
570
|
+
});
|
|
571
|
+
return this.sortMatchedBookings(normalizedBookings);
|
|
572
|
+
}
|
|
573
|
+
}, {
|
|
574
|
+
key: "buildCompletedBooking",
|
|
575
|
+
value: function buildCompletedBooking(current, booking, startAt, endAt) {
|
|
576
|
+
if (!startAt.isValid() || !endAt.isValid()) return null;
|
|
577
|
+
if (current.isBefore(startAt)) {
|
|
578
|
+
return _objectSpread(_objectSpread({}, booking), {}, {
|
|
579
|
+
status: 'reserved',
|
|
580
|
+
isTimeout: false,
|
|
581
|
+
timeoutTime: undefined,
|
|
582
|
+
progressPercent: 0,
|
|
583
|
+
lateTime: undefined,
|
|
584
|
+
reserved_status: 'not_arrived',
|
|
585
|
+
remainingReserveTime: Math.max(startAt.diff(current, 'minute'), 0)
|
|
586
|
+
});
|
|
587
|
+
}
|
|
588
|
+
return _objectSpread(_objectSpread({}, booking), {}, {
|
|
589
|
+
status: 'occupied',
|
|
590
|
+
isTimeout: false,
|
|
591
|
+
timeoutTime: undefined,
|
|
592
|
+
progressPercent: this.isSameOrAfter(current, endAt) ? 100 : this.calcProgressPercent(current, startAt, endAt),
|
|
593
|
+
lateTime: undefined,
|
|
594
|
+
reserved_status: undefined,
|
|
595
|
+
remainingReserveTime: undefined
|
|
596
|
+
});
|
|
597
|
+
}
|
|
524
598
|
|
|
525
599
|
/**
|
|
526
600
|
* 实时模式(current === deviceCurrent):信任 appointment_status
|
|
@@ -618,7 +692,7 @@ export var SalesImpl = /*#__PURE__*/function (_BaseModule) {
|
|
|
618
692
|
var _getResourceBookingList = _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime().mark(function _callee5(currentTime) {
|
|
619
693
|
var _resourceResponse$dat,
|
|
620
694
|
_resourceResponse$dat2,
|
|
621
|
-
|
|
695
|
+
_this5 = this;
|
|
622
696
|
var bookingList,
|
|
623
697
|
deviceTime,
|
|
624
698
|
current,
|
|
@@ -669,20 +743,7 @@ export var SalesImpl = /*#__PURE__*/function (_BaseModule) {
|
|
|
669
743
|
return _context5.abrupt("return", []);
|
|
670
744
|
case 14:
|
|
671
745
|
// 全局先做 booking 标准化 + start_time 升序,后续映射直接复用
|
|
672
|
-
normalizedBookings =
|
|
673
|
-
return _this3.normalizeMatchedBooking(current, deviceCurrent, booking);
|
|
674
|
-
}).filter(function (booking) {
|
|
675
|
-
return Boolean(booking);
|
|
676
|
-
}).sort(function (left, right) {
|
|
677
|
-
var leftStartAt = _this3.toBookingDateTime(left.start_date, left.start_time).valueOf();
|
|
678
|
-
var rightStartAt = _this3.toBookingDateTime(right.start_date, right.start_time).valueOf();
|
|
679
|
-
return leftStartAt - rightStartAt;
|
|
680
|
-
}); // 过滤掉没有 status 的 booking
|
|
681
|
-
normalizedBookings = normalizedBookings.filter(function (booking) {
|
|
682
|
-
return booking.status;
|
|
683
|
-
});
|
|
684
|
-
|
|
685
|
-
// 建立 resourceId -> bookings[] 倒排索引,加速资源匹配
|
|
746
|
+
normalizedBookings = this.normalizeResourceBookings(current, deviceCurrent, bookingList); // 建立 resourceId -> bookings[] 倒排索引,加速资源匹配
|
|
686
747
|
bookingMap = new Map();
|
|
687
748
|
normalizedBookings.forEach(function (booking) {
|
|
688
749
|
if (!Array.isArray(booking.resources)) return;
|
|
@@ -698,9 +759,9 @@ export var SalesImpl = /*#__PURE__*/function (_BaseModule) {
|
|
|
698
759
|
|
|
699
760
|
// 输出:每个资源都返回;bookings 可能是 0 / 1 / 多条(并发场景)
|
|
700
761
|
list = resourceList.map(function (resource) {
|
|
701
|
-
var resourceId =
|
|
762
|
+
var resourceId = _this5.getResourceId(resource);
|
|
702
763
|
var matchedBookings = bookingMap.get(String(resourceId)) || [];
|
|
703
|
-
var bookings =
|
|
764
|
+
var bookings = _this5.pickBookingsForCurrentPoint(current, matchedBookings);
|
|
704
765
|
return _objectSpread(_objectSpread({}, resource), {}, {
|
|
705
766
|
resource_id: resourceId,
|
|
706
767
|
bookings: bookings
|
|
@@ -717,7 +778,7 @@ export var SalesImpl = /*#__PURE__*/function (_BaseModule) {
|
|
|
717
778
|
}
|
|
718
779
|
});
|
|
719
780
|
return _context5.abrupt("return", list);
|
|
720
|
-
case
|
|
781
|
+
case 20:
|
|
721
782
|
case "end":
|
|
722
783
|
return _context5.stop();
|
|
723
784
|
}
|
|
@@ -728,6 +789,105 @@ export var SalesImpl = /*#__PURE__*/function (_BaseModule) {
|
|
|
728
789
|
}
|
|
729
790
|
return getResourceBookingList;
|
|
730
791
|
}()
|
|
792
|
+
}, {
|
|
793
|
+
key: "getResourceBookingItem",
|
|
794
|
+
value: function () {
|
|
795
|
+
var _getResourceBookingItem = _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime().mark(function _callee6(params) {
|
|
796
|
+
var _resourceResponse$dat3,
|
|
797
|
+
_resourceResponse$dat4,
|
|
798
|
+
_this6 = this,
|
|
799
|
+
_params$bookingList;
|
|
800
|
+
var current, deviceCurrent, resourceResponse, resourceList, targetResource, resourceId, matchedBookingList, completedBookingList, unpaidBookingList, bookings, completedBookings, unpaidBookings;
|
|
801
|
+
return _regeneratorRuntime().wrap(function _callee6$(_context6) {
|
|
802
|
+
while (1) switch (_context6.prev = _context6.next) {
|
|
803
|
+
case 0:
|
|
804
|
+
current = dayjs(params.currentTime);
|
|
805
|
+
if (current.isValid()) {
|
|
806
|
+
_context6.next = 3;
|
|
807
|
+
break;
|
|
808
|
+
}
|
|
809
|
+
return _context6.abrupt("return", null);
|
|
810
|
+
case 3:
|
|
811
|
+
deviceCurrent = dayjs(params.deviceTime || params.currentTime);
|
|
812
|
+
if (deviceCurrent.isValid()) {
|
|
813
|
+
_context6.next = 6;
|
|
814
|
+
break;
|
|
815
|
+
}
|
|
816
|
+
return _context6.abrupt("return", null);
|
|
817
|
+
case 6:
|
|
818
|
+
_context6.next = 8;
|
|
819
|
+
return this.request.get('/shop/form/resource/page', {
|
|
820
|
+
skip: 1,
|
|
821
|
+
num: 999
|
|
822
|
+
},
|
|
823
|
+
// @ts-ignore
|
|
824
|
+
{
|
|
825
|
+
osServer: true,
|
|
826
|
+
isShopApi: true
|
|
827
|
+
});
|
|
828
|
+
case 8:
|
|
829
|
+
resourceResponse = _context6.sent;
|
|
830
|
+
resourceList = (_resourceResponse$dat3 = resourceResponse === null || resourceResponse === void 0 || (_resourceResponse$dat4 = resourceResponse.data) === null || _resourceResponse$dat4 === void 0 ? void 0 : _resourceResponse$dat4.list) !== null && _resourceResponse$dat3 !== void 0 ? _resourceResponse$dat3 : [];
|
|
831
|
+
if (!(!Array.isArray(resourceList) || resourceList.length === 0)) {
|
|
832
|
+
_context6.next = 12;
|
|
833
|
+
break;
|
|
834
|
+
}
|
|
835
|
+
return _context6.abrupt("return", null);
|
|
836
|
+
case 12:
|
|
837
|
+
targetResource = resourceList.find(function (resource) {
|
|
838
|
+
return String(_this6.getResourceId(resource)) === String(params.resourceId);
|
|
839
|
+
});
|
|
840
|
+
if (targetResource) {
|
|
841
|
+
_context6.next = 15;
|
|
842
|
+
break;
|
|
843
|
+
}
|
|
844
|
+
return _context6.abrupt("return", null);
|
|
845
|
+
case 15:
|
|
846
|
+
resourceId = this.getResourceId(targetResource);
|
|
847
|
+
matchedBookingList = params.bookingList.filter(function (booking) {
|
|
848
|
+
return _this6.isBookingMatchedResource(booking, resourceId);
|
|
849
|
+
});
|
|
850
|
+
completedBookingList = matchedBookingList.filter(function (booking) {
|
|
851
|
+
return _this6.getBookingAppointmentStatus(booking) === 'completed';
|
|
852
|
+
});
|
|
853
|
+
unpaidBookingList = matchedBookingList.filter(function (booking) {
|
|
854
|
+
return _this6.getBookingOrderPaymentStatus(booking) === 'unpaid';
|
|
855
|
+
});
|
|
856
|
+
bookings = this.pickBookingsForCurrentPoint(current, this.normalizeResourceBookings(current, deviceCurrent, matchedBookingList));
|
|
857
|
+
completedBookings = this.pickBookingsForCurrentPoint(current, this.normalizeResourceBookings(current, deviceCurrent, completedBookingList, {
|
|
858
|
+
includeCompleted: true
|
|
859
|
+
}));
|
|
860
|
+
unpaidBookings = this.pickBookingsForCurrentPoint(current, this.normalizeResourceBookings(current, deviceCurrent, unpaidBookingList));
|
|
861
|
+
this.logger.addLog({
|
|
862
|
+
type: 'info',
|
|
863
|
+
title: 'getResourceBookingItem',
|
|
864
|
+
metadata: {
|
|
865
|
+
currentTime: params.currentTime,
|
|
866
|
+
deviceTime: params.deviceTime || params.currentTime,
|
|
867
|
+
resourceId: resourceId,
|
|
868
|
+
bookingList: (_params$bookingList = params.bookingList) === null || _params$bookingList === void 0 ? void 0 : _params$bookingList.length,
|
|
869
|
+
bookings: bookings.length,
|
|
870
|
+
completedBookings: completedBookings.length,
|
|
871
|
+
unpaidBookings: unpaidBookings.length
|
|
872
|
+
}
|
|
873
|
+
});
|
|
874
|
+
return _context6.abrupt("return", _objectSpread(_objectSpread({}, targetResource), {}, {
|
|
875
|
+
resource_id: resourceId,
|
|
876
|
+
bookings: bookings,
|
|
877
|
+
completedBookings: completedBookings,
|
|
878
|
+
unpaidBookings: unpaidBookings
|
|
879
|
+
}));
|
|
880
|
+
case 24:
|
|
881
|
+
case "end":
|
|
882
|
+
return _context6.stop();
|
|
883
|
+
}
|
|
884
|
+
}, _callee6, this);
|
|
885
|
+
}));
|
|
886
|
+
function getResourceBookingItem(_x5) {
|
|
887
|
+
return _getResourceBookingItem.apply(this, arguments);
|
|
888
|
+
}
|
|
889
|
+
return getResourceBookingItem;
|
|
890
|
+
}()
|
|
731
891
|
}]);
|
|
732
892
|
return SalesImpl;
|
|
733
893
|
}(BaseModule);
|
|
@@ -32,11 +32,21 @@ export interface SalesResourceBookingItem extends Omit<ResourceData, 'bookings'>
|
|
|
32
32
|
resource_id: ResourceId;
|
|
33
33
|
bookings: SalesResourceMatchedBooking[];
|
|
34
34
|
}
|
|
35
|
+
export interface SalesResourceBookingItemWithGroups extends SalesResourceBookingItem {
|
|
36
|
+
completedBookings: SalesResourceMatchedBooking[];
|
|
37
|
+
unpaidBookings: SalesResourceMatchedBooking[];
|
|
38
|
+
}
|
|
35
39
|
export type SalesTimelineHighlights = number[];
|
|
36
40
|
export interface ChangeBookingStatusParams {
|
|
37
41
|
booking_id: number | string;
|
|
38
42
|
appointment_status: string;
|
|
39
43
|
}
|
|
44
|
+
export interface GetResourceBookingItemParams {
|
|
45
|
+
resourceId: ResourceId;
|
|
46
|
+
currentTime: string;
|
|
47
|
+
bookingList: BookingData[];
|
|
48
|
+
deviceTime?: string;
|
|
49
|
+
}
|
|
40
50
|
/**
|
|
41
51
|
* Sales 解决方案状态
|
|
42
52
|
*
|
|
@@ -71,6 +81,8 @@ export interface SalesModuleAPI {
|
|
|
71
81
|
changeBookingStatus: (params: ChangeBookingStatusParams) => Promise<any>;
|
|
72
82
|
/** 获取资源维度的预约占用列表 */
|
|
73
83
|
getResourceBookingList: (currentTime: string, bookingList: BookingData[], deviceTime?: string) => Promise<SalesResourceBookingItem[]>;
|
|
84
|
+
/** 获取单个资源维度的预约占用、已完成、未支付列表 */
|
|
85
|
+
getResourceBookingItem: (params: GetResourceBookingItemParams) => Promise<SalesResourceBookingItemWithGroups | null>;
|
|
74
86
|
/** 获取时间轴每个时间片的预约数量 */
|
|
75
87
|
getTimelineHighlights: (bookingList: BookingData[], startDateTime?: string, endDateTime?: string) => SalesTimelineHighlights;
|
|
76
88
|
}
|
|
@@ -1,49 +0,0 @@
|
|
|
1
|
-
var __create = Object.create;
|
|
2
|
-
var __defProp = Object.defineProperty;
|
|
3
|
-
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
-
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
-
var __getProtoOf = Object.getPrototypeOf;
|
|
6
|
-
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
7
|
-
var __export = (target, all) => {
|
|
8
|
-
for (var name in all)
|
|
9
|
-
__defProp(target, name, { get: all[name], enumerable: true });
|
|
10
|
-
};
|
|
11
|
-
var __copyProps = (to, from, except, desc) => {
|
|
12
|
-
if (from && typeof from === "object" || typeof from === "function") {
|
|
13
|
-
for (let key of __getOwnPropNames(from))
|
|
14
|
-
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
15
|
-
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
16
|
-
}
|
|
17
|
-
return to;
|
|
18
|
-
};
|
|
19
|
-
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
20
|
-
// If the importer is in node compatibility mode or this is not an ESM
|
|
21
|
-
// file that has been converted to a CommonJS file using a Babel-
|
|
22
|
-
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
23
|
-
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
24
|
-
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
25
|
-
mod
|
|
26
|
-
));
|
|
27
|
-
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
28
|
-
|
|
29
|
-
// src/model/strategy/adapter/promotion/index.ts
|
|
30
|
-
var promotion_exports = {};
|
|
31
|
-
__export(promotion_exports, {
|
|
32
|
-
BUY_X_GET_Y_FREE_STRATEGY: () => import_examples.BUY_X_GET_Y_FREE_STRATEGY,
|
|
33
|
-
PromotionAdapter: () => import_adapter.PromotionAdapter,
|
|
34
|
-
PromotionEvaluator: () => import_evaluator.PromotionEvaluator,
|
|
35
|
-
X_ITEMS_FOR_Y_PRICE_STRATEGY: () => import_examples.X_ITEMS_FOR_Y_PRICE_STRATEGY,
|
|
36
|
-
default: () => import_adapter2.default
|
|
37
|
-
});
|
|
38
|
-
module.exports = __toCommonJS(promotion_exports);
|
|
39
|
-
var import_evaluator = require("./evaluator");
|
|
40
|
-
var import_adapter = require("./adapter");
|
|
41
|
-
var import_adapter2 = __toESM(require("./adapter"));
|
|
42
|
-
var import_examples = require("./examples");
|
|
43
|
-
// Annotate the CommonJS export names for ESM import in node:
|
|
44
|
-
0 && (module.exports = {
|
|
45
|
-
BUY_X_GET_Y_FREE_STRATEGY,
|
|
46
|
-
PromotionAdapter,
|
|
47
|
-
PromotionEvaluator,
|
|
48
|
-
X_ITEMS_FOR_Y_PRICE_STRATEGY
|
|
49
|
-
});
|
|
@@ -2,7 +2,7 @@ import dayjs from 'dayjs';
|
|
|
2
2
|
import { Module, ModuleOptions, PisellCore } from '../../types';
|
|
3
3
|
import { BaseModule } from '../../modules/BaseModule';
|
|
4
4
|
import type { RequestPlugin } from '../../plugins/request';
|
|
5
|
-
import { SalesState, SalesModuleAPI, Reservation, SalesResourceBookingItem, SalesTimelineHighlights, ChangeBookingStatusParams } from './types';
|
|
5
|
+
import { SalesState, SalesModuleAPI, Reservation, SalesResourceBookingItem, SalesResourceBookingItemWithGroups, SalesTimelineHighlights, ChangeBookingStatusParams, GetResourceBookingItemParams } from './types';
|
|
6
6
|
import type { BookingData } from '../../server/modules/order/types';
|
|
7
7
|
export * from './types';
|
|
8
8
|
/**
|
|
@@ -79,6 +79,10 @@ export declare class SalesImpl extends BaseModule implements Module, SalesModule
|
|
|
79
79
|
*/
|
|
80
80
|
private getBookingStatus;
|
|
81
81
|
private getResourceId;
|
|
82
|
+
private getBookingAppointmentStatus;
|
|
83
|
+
private getBookingOrderPaymentStatus;
|
|
84
|
+
private isBookingMatchedResource;
|
|
85
|
+
private sortMatchedBookings;
|
|
82
86
|
/**
|
|
83
87
|
* 资源下 bookings 的返回裁剪策略:
|
|
84
88
|
* 1) 优先返回当前时刻正在进行中的预约(允许并发返回多条)
|
|
@@ -100,6 +104,8 @@ export declare class SalesImpl extends BaseModule implements Module, SalesModule
|
|
|
100
104
|
* - 否则走快照分支(纯时间窗判定,忽略 appointment_status 的 arrived/started/completed 差异)
|
|
101
105
|
*/
|
|
102
106
|
private normalizeMatchedBooking;
|
|
107
|
+
private normalizeResourceBookings;
|
|
108
|
+
private buildCompletedBooking;
|
|
103
109
|
/**
|
|
104
110
|
* 实时模式(current === deviceCurrent):信任 appointment_status
|
|
105
111
|
* - 过滤 new / completed(实时视角下视为非活跃数据)
|
|
@@ -124,6 +130,7 @@ export declare class SalesImpl extends BaseModule implements Module, SalesModule
|
|
|
124
130
|
*/
|
|
125
131
|
private buildSnapshotBooking;
|
|
126
132
|
getResourceBookingList(currentTime: string, bookingList?: BookingData[], deviceTime?: string): Promise<SalesResourceBookingItem[]>;
|
|
133
|
+
getResourceBookingItem(params: GetResourceBookingItemParams): Promise<SalesResourceBookingItemWithGroups | null>;
|
|
127
134
|
}
|
|
128
135
|
export { SalesImpl as Sales };
|
|
129
136
|
export default SalesImpl;
|
|
@@ -247,6 +247,30 @@ var SalesImpl = class extends import_BaseModule.BaseModule {
|
|
|
247
247
|
getResourceId(resource) {
|
|
248
248
|
return resource.id ?? resource.form_record_id ?? "";
|
|
249
249
|
}
|
|
250
|
+
getBookingAppointmentStatus(booking) {
|
|
251
|
+
return String(booking.appointment_status ?? booking.status ?? "");
|
|
252
|
+
}
|
|
253
|
+
getBookingOrderPaymentStatus(booking) {
|
|
254
|
+
var _a;
|
|
255
|
+
return String(((_a = booking.order) == null ? void 0 : _a.payment_status) ?? "");
|
|
256
|
+
}
|
|
257
|
+
isBookingMatchedResource(booking, resourceId) {
|
|
258
|
+
if (!Array.isArray(booking.resources))
|
|
259
|
+
return false;
|
|
260
|
+
return booking.resources.some((resource) => {
|
|
261
|
+
const relationId = resource == null ? void 0 : resource.relation_id;
|
|
262
|
+
if (relationId === void 0 || relationId === null)
|
|
263
|
+
return false;
|
|
264
|
+
return String(relationId) === String(resourceId);
|
|
265
|
+
});
|
|
266
|
+
}
|
|
267
|
+
sortMatchedBookings(bookings) {
|
|
268
|
+
return [...bookings].sort((left, right) => {
|
|
269
|
+
const leftStartAt = this.toBookingDateTime(left.start_date, left.start_time).valueOf();
|
|
270
|
+
const rightStartAt = this.toBookingDateTime(right.start_date, right.start_time).valueOf();
|
|
271
|
+
return leftStartAt - rightStartAt;
|
|
272
|
+
});
|
|
273
|
+
}
|
|
250
274
|
/**
|
|
251
275
|
* 资源下 bookings 的返回裁剪策略:
|
|
252
276
|
* 1) 优先返回当前时刻正在进行中的预约(允许并发返回多条)
|
|
@@ -355,17 +379,50 @@ var SalesImpl = class extends import_BaseModule.BaseModule {
|
|
|
355
379
|
* - current === deviceCurrent 走实时分支(信任 appointment_status)
|
|
356
380
|
* - 否则走快照分支(纯时间窗判定,忽略 appointment_status 的 arrived/started/completed 差异)
|
|
357
381
|
*/
|
|
358
|
-
normalizeMatchedBooking(current, deviceCurrent, booking) {
|
|
359
|
-
const appointmentStatus =
|
|
382
|
+
normalizeMatchedBooking(current, deviceCurrent, booking, options = {}) {
|
|
383
|
+
const appointmentStatus = this.getBookingAppointmentStatus(booking);
|
|
360
384
|
if (appointmentStatus === "rejected" || appointmentStatus === "cancelled")
|
|
361
385
|
return null;
|
|
362
386
|
const startAt = this.toBookingDateTime(booking.start_date, booking.start_time);
|
|
363
387
|
const endAt = this.toBookingDateTime(booking.end_date, booking.end_time);
|
|
388
|
+
if (appointmentStatus === "completed" && options.includeCompleted) {
|
|
389
|
+
return this.buildCompletedBooking(current, booking, startAt, endAt);
|
|
390
|
+
}
|
|
364
391
|
if (current.isSame(deviceCurrent)) {
|
|
365
392
|
return this.buildRealtimeBooking(current, booking, appointmentStatus, startAt, endAt);
|
|
366
393
|
}
|
|
367
394
|
return this.buildSnapshotBooking(current, booking, appointmentStatus, startAt, endAt);
|
|
368
395
|
}
|
|
396
|
+
normalizeResourceBookings(current, deviceCurrent, bookingList, options = {}) {
|
|
397
|
+
const normalizedBookings = bookingList.map((booking) => this.normalizeMatchedBooking(current, deviceCurrent, booking, options)).filter((booking) => Boolean(booking)).filter((booking) => booking.status);
|
|
398
|
+
return this.sortMatchedBookings(normalizedBookings);
|
|
399
|
+
}
|
|
400
|
+
buildCompletedBooking(current, booking, startAt, endAt) {
|
|
401
|
+
if (!startAt.isValid() || !endAt.isValid())
|
|
402
|
+
return null;
|
|
403
|
+
if (current.isBefore(startAt)) {
|
|
404
|
+
return {
|
|
405
|
+
...booking,
|
|
406
|
+
status: "reserved",
|
|
407
|
+
isTimeout: false,
|
|
408
|
+
timeoutTime: void 0,
|
|
409
|
+
progressPercent: 0,
|
|
410
|
+
lateTime: void 0,
|
|
411
|
+
reserved_status: "not_arrived",
|
|
412
|
+
remainingReserveTime: Math.max(startAt.diff(current, "minute"), 0)
|
|
413
|
+
};
|
|
414
|
+
}
|
|
415
|
+
return {
|
|
416
|
+
...booking,
|
|
417
|
+
status: "occupied",
|
|
418
|
+
isTimeout: false,
|
|
419
|
+
timeoutTime: void 0,
|
|
420
|
+
progressPercent: this.isSameOrAfter(current, endAt) ? 100 : this.calcProgressPercent(current, startAt, endAt),
|
|
421
|
+
lateTime: void 0,
|
|
422
|
+
reserved_status: void 0,
|
|
423
|
+
remainingReserveTime: void 0
|
|
424
|
+
};
|
|
425
|
+
}
|
|
369
426
|
/**
|
|
370
427
|
* 实时模式(current === deviceCurrent):信任 appointment_status
|
|
371
428
|
* - 过滤 new / completed(实时视角下视为非活跃数据)
|
|
@@ -476,12 +533,7 @@ var SalesImpl = class extends import_BaseModule.BaseModule {
|
|
|
476
533
|
const resourceList = ((_a = resourceResponse == null ? void 0 : resourceResponse.data) == null ? void 0 : _a.list) ?? [];
|
|
477
534
|
if (!Array.isArray(resourceList) || resourceList.length === 0)
|
|
478
535
|
return [];
|
|
479
|
-
|
|
480
|
-
const leftStartAt = this.toBookingDateTime(left.start_date, left.start_time).valueOf();
|
|
481
|
-
const rightStartAt = this.toBookingDateTime(right.start_date, right.start_time).valueOf();
|
|
482
|
-
return leftStartAt - rightStartAt;
|
|
483
|
-
});
|
|
484
|
-
normalizedBookings = normalizedBookings.filter((booking) => booking.status);
|
|
536
|
+
const normalizedBookings = this.normalizeResourceBookings(current, deviceCurrent, bookingList);
|
|
485
537
|
const bookingMap = /* @__PURE__ */ new Map();
|
|
486
538
|
normalizedBookings.forEach((booking) => {
|
|
487
539
|
if (!Array.isArray(booking.resources))
|
|
@@ -518,6 +570,73 @@ var SalesImpl = class extends import_BaseModule.BaseModule {
|
|
|
518
570
|
});
|
|
519
571
|
return list;
|
|
520
572
|
}
|
|
573
|
+
async getResourceBookingItem(params) {
|
|
574
|
+
var _a, _b;
|
|
575
|
+
const current = (0, import_dayjs.default)(params.currentTime);
|
|
576
|
+
if (!current.isValid())
|
|
577
|
+
return null;
|
|
578
|
+
const deviceCurrent = (0, import_dayjs.default)(params.deviceTime || params.currentTime);
|
|
579
|
+
if (!deviceCurrent.isValid())
|
|
580
|
+
return null;
|
|
581
|
+
const resourceResponse = await this.request.get(
|
|
582
|
+
"/shop/form/resource/page",
|
|
583
|
+
{ skip: 1, num: 999 },
|
|
584
|
+
// @ts-ignore
|
|
585
|
+
{ osServer: true, isShopApi: true }
|
|
586
|
+
);
|
|
587
|
+
const resourceList = ((_a = resourceResponse == null ? void 0 : resourceResponse.data) == null ? void 0 : _a.list) ?? [];
|
|
588
|
+
if (!Array.isArray(resourceList) || resourceList.length === 0)
|
|
589
|
+
return null;
|
|
590
|
+
const targetResource = resourceList.find(
|
|
591
|
+
(resource) => String(this.getResourceId(resource)) === String(params.resourceId)
|
|
592
|
+
);
|
|
593
|
+
if (!targetResource)
|
|
594
|
+
return null;
|
|
595
|
+
const resourceId = this.getResourceId(targetResource);
|
|
596
|
+
const matchedBookingList = params.bookingList.filter(
|
|
597
|
+
(booking) => this.isBookingMatchedResource(booking, resourceId)
|
|
598
|
+
);
|
|
599
|
+
const completedBookingList = matchedBookingList.filter(
|
|
600
|
+
(booking) => this.getBookingAppointmentStatus(booking) === "completed"
|
|
601
|
+
);
|
|
602
|
+
const unpaidBookingList = matchedBookingList.filter(
|
|
603
|
+
(booking) => this.getBookingOrderPaymentStatus(booking) === "unpaid"
|
|
604
|
+
);
|
|
605
|
+
const bookings = this.pickBookingsForCurrentPoint(
|
|
606
|
+
current,
|
|
607
|
+
this.normalizeResourceBookings(current, deviceCurrent, matchedBookingList)
|
|
608
|
+
);
|
|
609
|
+
const completedBookings = this.pickBookingsForCurrentPoint(
|
|
610
|
+
current,
|
|
611
|
+
this.normalizeResourceBookings(current, deviceCurrent, completedBookingList, {
|
|
612
|
+
includeCompleted: true
|
|
613
|
+
})
|
|
614
|
+
);
|
|
615
|
+
const unpaidBookings = this.pickBookingsForCurrentPoint(
|
|
616
|
+
current,
|
|
617
|
+
this.normalizeResourceBookings(current, deviceCurrent, unpaidBookingList)
|
|
618
|
+
);
|
|
619
|
+
this.logger.addLog({
|
|
620
|
+
type: "info",
|
|
621
|
+
title: "getResourceBookingItem",
|
|
622
|
+
metadata: {
|
|
623
|
+
currentTime: params.currentTime,
|
|
624
|
+
deviceTime: params.deviceTime || params.currentTime,
|
|
625
|
+
resourceId,
|
|
626
|
+
bookingList: (_b = params.bookingList) == null ? void 0 : _b.length,
|
|
627
|
+
bookings: bookings.length,
|
|
628
|
+
completedBookings: completedBookings.length,
|
|
629
|
+
unpaidBookings: unpaidBookings.length
|
|
630
|
+
}
|
|
631
|
+
});
|
|
632
|
+
return {
|
|
633
|
+
...targetResource,
|
|
634
|
+
resource_id: resourceId,
|
|
635
|
+
bookings,
|
|
636
|
+
completedBookings,
|
|
637
|
+
unpaidBookings
|
|
638
|
+
};
|
|
639
|
+
}
|
|
521
640
|
};
|
|
522
641
|
var Sales_default = SalesImpl;
|
|
523
642
|
// Annotate the CommonJS export names for ESM import in node:
|
|
@@ -32,11 +32,21 @@ export interface SalesResourceBookingItem extends Omit<ResourceData, 'bookings'>
|
|
|
32
32
|
resource_id: ResourceId;
|
|
33
33
|
bookings: SalesResourceMatchedBooking[];
|
|
34
34
|
}
|
|
35
|
+
export interface SalesResourceBookingItemWithGroups extends SalesResourceBookingItem {
|
|
36
|
+
completedBookings: SalesResourceMatchedBooking[];
|
|
37
|
+
unpaidBookings: SalesResourceMatchedBooking[];
|
|
38
|
+
}
|
|
35
39
|
export type SalesTimelineHighlights = number[];
|
|
36
40
|
export interface ChangeBookingStatusParams {
|
|
37
41
|
booking_id: number | string;
|
|
38
42
|
appointment_status: string;
|
|
39
43
|
}
|
|
44
|
+
export interface GetResourceBookingItemParams {
|
|
45
|
+
resourceId: ResourceId;
|
|
46
|
+
currentTime: string;
|
|
47
|
+
bookingList: BookingData[];
|
|
48
|
+
deviceTime?: string;
|
|
49
|
+
}
|
|
40
50
|
/**
|
|
41
51
|
* Sales 解决方案状态
|
|
42
52
|
*
|
|
@@ -71,6 +81,8 @@ export interface SalesModuleAPI {
|
|
|
71
81
|
changeBookingStatus: (params: ChangeBookingStatusParams) => Promise<any>;
|
|
72
82
|
/** 获取资源维度的预约占用列表 */
|
|
73
83
|
getResourceBookingList: (currentTime: string, bookingList: BookingData[], deviceTime?: string) => Promise<SalesResourceBookingItem[]>;
|
|
84
|
+
/** 获取单个资源维度的预约占用、已完成、未支付列表 */
|
|
85
|
+
getResourceBookingItem: (params: GetResourceBookingItemParams) => Promise<SalesResourceBookingItemWithGroups | null>;
|
|
74
86
|
/** 获取时间轴每个时间片的预约数量 */
|
|
75
87
|
getTimelineHighlights: (bookingList: BookingData[], startDateTime?: string, endDateTime?: string) => SalesTimelineHighlights;
|
|
76
88
|
}
|