@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.
@@ -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 _this2 = this;
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 = _this2.toBookingDateTime(booking.start_date, booking.start_time);
418
- var endAt = _this2.toBookingDateTime(booking.end_date, booking.end_time);
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 _this2.isSameOrBefore(startAt, currentDayEnd) && _this2.isSameOrAfter(endAt, currentDayStart);
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 _this2.toBookingDateTime(booking.start_date, booking.start_time);
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 = _this2.toBookingDateTime(booking.start_date, booking.start_time);
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 = _this2.toBookingDateTime(futureCandidates[0].start_date, futureCandidates[0].start_time).valueOf();
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 = _this2.toBookingDateTime(booking.start_date, booking.start_time);
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 = _this2.toBookingDateTime(booking.start_date, booking.start_time);
457
- var endAt = _this2.toBookingDateTime(booking.end_date, booking.end_time);
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 _this2.isSameOrAfter(current, startAt) && _this2.isSameOrBefore(current, endAt);
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 = _this2.toBookingDateTime(booking.start_date, booking.start_time);
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 = _this2.toBookingDateTime(booking.start_date, booking.start_time);
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 _ref4, _booking$appointment_2;
515
- var appointmentStatus = String((_ref4 = (_booking$appointment_2 = booking.appointment_status) !== null && _booking$appointment_2 !== void 0 ? _booking$appointment_2 : booking.status) !== null && _ref4 !== void 0 ? _ref4 : '');
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
- _this3 = this;
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 = bookingList.map(function (booking) {
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 = _this3.getResourceId(resource);
762
+ var resourceId = _this5.getResourceId(resource);
702
763
  var matchedBookings = bookingMap.get(String(resourceId)) || [];
703
- var bookings = _this3.pickBookingsForCurrentPoint(current, matchedBookings);
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 21:
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 = String(booking.appointment_status ?? booking.status ?? "");
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
- let normalizedBookings = bookingList.map((booking) => this.normalizeMatchedBooking(current, deviceCurrent, booking)).filter((booking) => Boolean(booking)).sort((left, right) => {
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
  }
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "private": false,
3
3
  "name": "@pisell/pisellos",
4
- "version": "2.2.136",
4
+ "version": "2.2.137",
5
5
  "description": "一个可扩展的前端模块化SDK框架,支持插件系统",
6
6
  "main": "dist/index.js",
7
7
  "types": "dist/index.d.ts",