@pisell/pisellos 2.2.94 → 2.2.96

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.
Files changed (41) hide show
  1. package/dist/core/index.d.ts +1 -0
  2. package/dist/core/index.js +7 -0
  3. package/dist/modules/Customer/index.d.ts +1 -0
  4. package/dist/modules/Customer/index.js +28 -12
  5. package/dist/modules/Order/index.d.ts +1 -1
  6. package/dist/server/index.d.ts +6 -1
  7. package/dist/server/index.js +40 -17
  8. package/dist/server/modules/order/index.js +1 -1
  9. package/dist/server/modules/order/types.d.ts +1 -1
  10. package/dist/server/modules/order/utils/filterBookings.js +1 -1
  11. package/dist/server/modules/products/index.d.ts +19 -24
  12. package/dist/server/modules/products/index.js +429 -600
  13. package/dist/server/modules/products/types.d.ts +1 -0
  14. package/dist/server/utils/product.d.ts +4 -0
  15. package/dist/server/utils/product.js +34 -0
  16. package/dist/solution/BookingByStep/index.d.ts +1 -1
  17. package/dist/solution/Sales/index.d.ts +1 -1
  18. package/dist/solution/Sales/index.js +79 -23
  19. package/dist/solution/Sales/types.d.ts +6 -4
  20. package/dist/types/index.d.ts +2 -0
  21. package/lib/core/index.d.ts +1 -0
  22. package/lib/core/index.js +4 -0
  23. package/lib/modules/Customer/index.d.ts +1 -0
  24. package/lib/modules/Customer/index.js +21 -6
  25. package/lib/modules/Order/index.d.ts +1 -1
  26. package/lib/server/index.d.ts +6 -1
  27. package/lib/server/index.js +33 -13
  28. package/lib/server/modules/order/index.js +1 -1
  29. package/lib/server/modules/order/types.d.ts +1 -1
  30. package/lib/server/modules/order/utils/filterBookings.js +1 -1
  31. package/lib/server/modules/products/index.d.ts +19 -24
  32. package/lib/server/modules/products/index.js +151 -150
  33. package/lib/server/modules/products/types.d.ts +1 -0
  34. package/lib/server/utils/product.d.ts +4 -0
  35. package/lib/server/utils/product.js +27 -0
  36. package/lib/solution/BookingByStep/index.d.ts +1 -1
  37. package/lib/solution/Sales/index.d.ts +1 -1
  38. package/lib/solution/Sales/index.js +73 -15
  39. package/lib/solution/Sales/types.d.ts +6 -4
  40. package/lib/types/index.d.ts +2 -0
  41. package/package.json +1 -1
@@ -109,6 +109,7 @@ export interface ProductFormatterContext {
109
109
  schedule_date: string;
110
110
  priceData?: LoadProductsPriceData[];
111
111
  scheduleModule?: any;
112
+ locale?: string;
112
113
  }
113
114
  /**
114
115
  * 商品格式化器类型
@@ -10,6 +10,10 @@ export declare function perfMark(label: string, durationMs: number, meta?: Recor
10
10
  */
11
11
  export declare function applyPriceDataToProducts(products: ProductData[], priceData: LoadProductsPriceData[]): ProductData[];
12
12
  export declare const getIsSessionProduct: (product: ProductData) => boolean;
13
+ /**
14
+ * 根据 locale 将商品的 i18n 字段覆盖到对应原始字段
15
+ */
16
+ export declare function applyI18nToProducts(products: ProductData[], locale?: string): ProductData[];
13
17
  /**
14
18
  * 将详情值数据应用到商品列表
15
19
  * @param products 商品列表
@@ -1,7 +1,11 @@
1
1
  function _typeof(o) { "@babel/helpers - typeof"; return _typeof = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function (o) { return typeof o; } : function (o) { return o && "function" == typeof Symbol && o.constructor === Symbol && o !== Symbol.prototype ? "symbol" : typeof o; }, _typeof(o); }
2
2
  function _createForOfIteratorHelper(o, allowArrayLike) { var it = typeof Symbol !== "undefined" && o[Symbol.iterator] || o["@@iterator"]; if (!it) { if (Array.isArray(o) || (it = _unsupportedIterableToArray(o)) || allowArrayLike && o && typeof o.length === "number") { if (it) o = it; var i = 0; var F = function F() {}; return { s: F, n: function n() { if (i >= o.length) return { done: true }; return { done: false, value: o[i++] }; }, e: function e(_e) { throw _e; }, f: F }; } throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); } var normalCompletion = true, didErr = false, err; return { s: function s() { it = it.call(o); }, n: function n() { var step = it.next(); normalCompletion = step.done; return step; }, e: function e(_e2) { didErr = true; err = _e2; }, f: function f() { try { if (!normalCompletion && it.return != null) it.return(); } finally { if (didErr) throw err; } } }; }
3
+ function _slicedToArray(arr, i) { return _arrayWithHoles(arr) || _iterableToArrayLimit(arr, i) || _unsupportedIterableToArray(arr, i) || _nonIterableRest(); }
4
+ function _nonIterableRest() { throw new TypeError("Invalid attempt to destructure non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); }
3
5
  function _unsupportedIterableToArray(o, minLen) { if (!o) return; if (typeof o === "string") return _arrayLikeToArray(o, minLen); var n = Object.prototype.toString.call(o).slice(8, -1); if (n === "Object" && o.constructor) n = o.constructor.name; if (n === "Map" || n === "Set") return Array.from(o); if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _arrayLikeToArray(o, minLen); }
4
6
  function _arrayLikeToArray(arr, len) { if (len == null || len > arr.length) len = arr.length; for (var i = 0, arr2 = new Array(len); i < len; i++) arr2[i] = arr[i]; return arr2; }
7
+ function _iterableToArrayLimit(r, l) { var t = null == r ? null : "undefined" != typeof Symbol && r[Symbol.iterator] || r["@@iterator"]; if (null != t) { var e, n, i, u, a = [], f = !0, o = !1; try { if (i = (t = t.call(r)).next, 0 === l) { if (Object(t) !== t) return; f = !1; } else for (; !(f = (e = i.call(t)).done) && (a.push(e.value), a.length !== l); f = !0); } catch (r) { o = !0, n = r; } finally { try { if (!f && null != t.return && (u = t.return(), Object(u) !== u)) return; } finally { if (o) throw n; } } return a; } }
8
+ function _arrayWithHoles(arr) { if (Array.isArray(arr)) return arr; }
5
9
  function ownKeys(e, r) { var t = Object.keys(e); if (Object.getOwnPropertySymbols) { var o = Object.getOwnPropertySymbols(e); r && (o = o.filter(function (r) { return Object.getOwnPropertyDescriptor(e, r).enumerable; })), t.push.apply(t, o); } return t; }
6
10
  function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? ownKeys(Object(t), !0).forEach(function (r) { _defineProperty(e, r, t[r]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function (r) { Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)); }); } return e; }
7
11
  function _defineProperty(obj, key, value) { key = _toPropertyKey(key); if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }
@@ -186,6 +190,36 @@ var getIsOpenDetailModal = function getIsOpenDetailModal(product, context) {
186
190
  scheduleTimeSlots: scheduleTimeSlots
187
191
  };
188
192
  };
193
+ var I18N_FIELD_MAP = {
194
+ title_i18n: 'title',
195
+ subtitle_i18n: 'subtitle',
196
+ description_i18n: 'description',
197
+ cover_i18n: 'cover'
198
+ };
199
+
200
+ /**
201
+ * 根据 locale 将商品的 i18n 字段覆盖到对应原始字段
202
+ */
203
+ export function applyI18nToProducts(products, locale) {
204
+ if (!locale) return products;
205
+ return products.map(function (product) {
206
+ var _patched;
207
+ var patched = null;
208
+ for (var _i = 0, _Object$entries = Object.entries(I18N_FIELD_MAP); _i < _Object$entries.length; _i++) {
209
+ var _Object$entries$_i = _slicedToArray(_Object$entries[_i], 2),
210
+ i18nKey = _Object$entries$_i[0],
211
+ originalKey = _Object$entries$_i[1];
212
+ var i18nDict = product[i18nKey];
213
+ if (!i18nDict) continue;
214
+ var localizedValue = i18nDict[locale];
215
+ if (localizedValue) {
216
+ if (!patched) patched = _objectSpread({}, product);
217
+ patched[originalKey] = localizedValue;
218
+ }
219
+ }
220
+ return (_patched = patched) !== null && _patched !== void 0 ? _patched : product;
221
+ });
222
+ }
189
223
  var formatDataKey = function formatDataKey(data) {
190
224
  var _data$option, _data$bundle;
191
225
  var _data = _objectSpread({}, data);
@@ -344,7 +344,7 @@ export declare class BookingByStepImpl extends BaseModule implements Module {
344
344
  };
345
345
  setOtherData(key: string, value: any): void;
346
346
  getOtherData(key: string): any;
347
- getProductTypeById(id: number): Promise<"normal" | "duration" | "session">;
347
+ getProductTypeById(id: number): Promise<"duration" | "session" | "normal">;
348
348
  /**
349
349
  * 提供给 UI 的方法,减轻 UI 层的计算压力,UI 层只需要传递 cartItemId 和 resourceCode 即返回对应的 renderList
350
350
  *
@@ -58,7 +58,7 @@ export declare class SalesImpl extends BaseModule implements Module, SalesModule
58
58
  * - n = booking 数量
59
59
  * - s = 时间片数量
60
60
  */
61
- getTimelineHighlights(bookingList?: BookingData[]): SalesTimelineHighlights;
61
+ getTimelineHighlights(bookingList?: BookingData[], startDateTime?: string, endDateTime?: string): SalesTimelineHighlights;
62
62
  /** dayjs 未启用插件时,手动封装 >= 判断 */
63
63
  private isSameOrAfter;
64
64
  /** dayjs 未启用插件时,手动封装 <= 判断 */
@@ -242,16 +242,24 @@ export var SalesImpl = /*#__PURE__*/function (_BaseModule) {
242
242
  key: "getTimelineHighlights",
243
243
  value: function getTimelineHighlights() {
244
244
  var bookingList = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : [];
245
+ var startDateTime = arguments.length > 1 ? arguments[1] : undefined;
246
+ var endDateTime = arguments.length > 2 ? arguments[2] : undefined;
245
247
  var bucketMinutes = this.getTimelineBucketMinutes();
246
248
  var bucketMs = bucketMinutes * 60 * 1000;
247
- var _this$getTimelineRang = this.getTimelineRangeByOperatingBoundary(),
248
- startAt = _this$getTimelineRang.startAt,
249
- endExclusiveAt = _this$getTimelineRang.endExclusiveAt;
249
+ var customStartAt = startDateTime ? dayjs(startDateTime) : dayjs('');
250
+ var customEndExclusiveAt = endDateTime ? dayjs(endDateTime) : dayjs('');
251
+ var hasValidCustomRange = customStartAt.isValid() && customEndExclusiveAt.isValid();
252
+ var _ref = hasValidCustomRange ? {
253
+ startAt: customStartAt,
254
+ endExclusiveAt: customEndExclusiveAt
255
+ } : this.getTimelineRangeByOperatingBoundary(),
256
+ startAt = _ref.startAt,
257
+ endExclusiveAt = _ref.endExclusiveAt;
250
258
  var startAtMs = startAt.valueOf();
251
259
  var endExclusiveAtMs = endExclusiveAt.valueOf();
252
- if (!startAt.isValid() || !endExclusiveAt.isValid() || endExclusiveAtMs <= startAtMs) return {};
260
+ if (!startAt.isValid() || !endExclusiveAt.isValid() || endExclusiveAtMs <= startAtMs) return [];
253
261
  var slotCount = Math.ceil((endExclusiveAtMs - startAtMs) / bucketMs);
254
- if (slotCount <= 0) return {};
262
+ if (slotCount <= 0) return [];
255
263
  var diff = new Int32Array(slotCount + 1);
256
264
  for (var i = 0; i < bookingList.length; i++) {
257
265
  var booking = bookingList[i];
@@ -268,12 +276,11 @@ export var SalesImpl = /*#__PURE__*/function (_BaseModule) {
268
276
  diff[startIdx] += 1;
269
277
  diff[endIdx + 1] -= 1;
270
278
  }
271
- var timeline = {};
279
+ var timeline = [];
272
280
  var runningCount = 0;
273
281
  for (var _i = 0; _i < slotCount; _i++) {
274
282
  runningCount += diff[_i];
275
- var label = startAt.add(_i * bucketMinutes, 'minute').format('HH:mm');
276
- timeline[label] = runningCount;
283
+ timeline.push(runningCount);
277
284
  }
278
285
  return timeline;
279
286
  }
@@ -324,8 +331,8 @@ export var SalesImpl = /*#__PURE__*/function (_BaseModule) {
324
331
  }, {
325
332
  key: "getResourceId",
326
333
  value: function getResourceId(resource) {
327
- var _ref, _resource$id;
328
- return (_ref = (_resource$id = resource.id) !== null && _resource$id !== void 0 ? _resource$id : resource.form_record_id) !== null && _ref !== void 0 ? _ref : '';
334
+ var _ref2, _resource$id;
335
+ return (_ref2 = (_resource$id = resource.id) !== null && _resource$id !== void 0 ? _resource$id : resource.form_record_id) !== null && _ref2 !== void 0 ? _ref2 : '';
329
336
  }
330
337
 
331
338
  /**
@@ -351,6 +358,33 @@ export var SalesImpl = /*#__PURE__*/function (_BaseModule) {
351
358
  return _this2.isSameOrBefore(startAt, currentDayEnd) && _this2.isSameOrAfter(endAt, currentDayStart);
352
359
  });
353
360
  if (dayScopedBookings.length === 0) return [];
361
+ var appendNextStartGroupIfNeeded = function appendNextStartGroupIfNeeded(selectedBookings) {
362
+ if (selectedBookings.length === 0) return selectedBookings;
363
+ var shouldAppendNextGroup = selectedBookings.some(function (booking) {
364
+ return booking.status === 'reserved' || booking.status === 'occupied';
365
+ });
366
+ if (!shouldAppendNextGroup) return selectedBookings;
367
+ var selectedStartValues = new Set(selectedBookings.map(function (booking) {
368
+ return _this2.toBookingDateTime(booking.start_date, booking.start_time);
369
+ }).filter(function (startAt) {
370
+ return startAt.isValid();
371
+ }).map(function (startAt) {
372
+ return startAt.valueOf();
373
+ }));
374
+ var futureCandidates = dayScopedBookings.filter(function (booking) {
375
+ var startAt = _this2.toBookingDateTime(booking.start_date, booking.start_time);
376
+ if (!startAt.isValid()) return false;
377
+ return startAt.isAfter(current) && !selectedStartValues.has(startAt.valueOf());
378
+ });
379
+ if (futureCandidates.length === 0) return selectedBookings;
380
+ var nextStartAt = _this2.toBookingDateTime(futureCandidates[0].start_date, futureCandidates[0].start_time).valueOf();
381
+ var nextStartGroup = futureCandidates.filter(function (booking) {
382
+ var startAt = _this2.toBookingDateTime(booking.start_date, booking.start_time);
383
+ return startAt.isValid() && startAt.valueOf() === nextStartAt;
384
+ });
385
+ if (nextStartGroup.length === 0) return selectedBookings;
386
+ return [].concat(_toConsumableArray(selectedBookings), _toConsumableArray(nextStartGroup));
387
+ };
354
388
 
355
389
  // active: current 处于 [startAt, endAt] 区间
356
390
  var activeBookings = dayScopedBookings.filter(function (booking) {
@@ -359,13 +393,13 @@ export var SalesImpl = /*#__PURE__*/function (_BaseModule) {
359
393
  if (!startAt.isValid() || !endAt.isValid()) return false;
360
394
  return _this2.isSameOrAfter(current, startAt) && _this2.isSameOrBefore(current, endAt);
361
395
  });
362
- if (activeBookings.length > 0) return activeBookings;
396
+ if (activeBookings.length > 0) return appendNextStartGroupIfNeeded(activeBookings);
363
397
 
364
398
  // timeout occupied: 占用中且超时
365
399
  var timeoutOccupied = dayScopedBookings.filter(function (booking) {
366
400
  return booking.status === 'occupied' && booking.isTimeout;
367
401
  });
368
- if (timeoutOccupied.length > 0) return timeoutOccupied;
402
+ if (timeoutOccupied.length > 0) return appendNextStartGroupIfNeeded(timeoutOccupied);
369
403
 
370
404
  // upcoming: 从未来预约里只取最早那一组(同 start_time)
371
405
  var upcoming = dayScopedBookings.filter(function (booking) {
@@ -374,10 +408,11 @@ export var SalesImpl = /*#__PURE__*/function (_BaseModule) {
374
408
  });
375
409
  if (upcoming.length > 0) {
376
410
  var firstStartAt = this.toBookingDateTime(upcoming[0].start_date, upcoming[0].start_time).valueOf();
377
- return upcoming.filter(function (booking) {
411
+ var upcomingStartGroup = upcoming.filter(function (booking) {
378
412
  var startAt = _this2.toBookingDateTime(booking.start_date, booking.start_time);
379
413
  return startAt.valueOf() === firstStartAt;
380
414
  });
415
+ return appendNextStartGroupIfNeeded(upcomingStartGroup);
381
416
  }
382
417
 
383
418
  // fallback: 全部是过去且非 timeout 的场景,保留末条便于前端兜底展示
@@ -393,8 +428,8 @@ export var SalesImpl = /*#__PURE__*/function (_BaseModule) {
393
428
  }, {
394
429
  key: "normalizeMatchedBooking",
395
430
  value: function normalizeMatchedBooking(current, booking) {
396
- var _ref2, _booking$appointment_;
397
- var appointmentStatus = String((_ref2 = (_booking$appointment_ = booking.appointment_status) !== null && _booking$appointment_ !== void 0 ? _booking$appointment_ : booking.status) !== null && _ref2 !== void 0 ? _ref2 : '');
431
+ var _ref3, _booking$appointment_;
432
+ var appointmentStatus = String((_ref3 = (_booking$appointment_ = booking.appointment_status) !== null && _booking$appointment_ !== void 0 ? _booking$appointment_ : booking.status) !== null && _ref3 !== void 0 ? _ref3 : '');
398
433
  if (appointmentStatus === 'rejected' || appointmentStatus === 'cancelled' || appointmentStatus === 'completed') {
399
434
  return null;
400
435
  }
@@ -402,15 +437,37 @@ export var SalesImpl = /*#__PURE__*/function (_BaseModule) {
402
437
  var endAt = this.toBookingDateTime(booking.end_date, booking.end_time);
403
438
  var startAt = this.toBookingDateTime(booking.start_date, booking.start_time);
404
439
  var isTimeout = bookingStatus === 'occupied' && endAt.isValid() && current.isAfter(endAt);
440
+ var timeoutTime = isTimeout ? current.diff(endAt, 'minute') : undefined;
441
+ var progressPercent = function () {
442
+ if (bookingStatus !== 'occupied') return 0;
443
+ if (!startAt.isValid() || !endAt.isValid()) return 0;
444
+ var totalMinutes = endAt.diff(startAt, 'minute');
445
+ if (totalMinutes <= 0) return 0;
446
+ var elapsedMinutes = current.diff(startAt, 'minute');
447
+ if (elapsedMinutes <= 0) return 0;
448
+ if (elapsedMinutes >= totalMinutes) return 100;
449
+ return Math.floor(elapsedMinutes / totalMinutes * 100);
450
+ }();
405
451
  var reservedStatus;
406
- if (bookingStatus === 'reserved' && startAt.isValid() && endAt.isValid()) {
407
- if (current.isBefore(startAt)) reservedStatus = 'not_arrived';
408
- if (current.isAfter(endAt)) reservedStatus = 'late';
452
+ var lateTime;
453
+ var remainingReserveTime;
454
+ if (bookingStatus === 'reserved' && startAt.isValid()) {
455
+ if (current.isBefore(startAt)) {
456
+ reservedStatus = 'not_arrived';
457
+ remainingReserveTime = startAt.diff(current, 'minute');
458
+ } else {
459
+ reservedStatus = 'late';
460
+ lateTime = Math.max(current.diff(startAt, 'minute'), 0);
461
+ }
409
462
  }
410
463
  return _objectSpread(_objectSpread({}, booking), {}, {
411
464
  status: bookingStatus,
412
465
  isTimeout: isTimeout,
413
- reserved_status: reservedStatus
466
+ timeoutTime: timeoutTime,
467
+ progressPercent: progressPercent,
468
+ lateTime: lateTime,
469
+ reserved_status: reservedStatus,
470
+ remainingReserveTime: remainingReserveTime
414
471
  });
415
472
  }
416
473
  }, {
@@ -452,13 +509,12 @@ export var SalesImpl = /*#__PURE__*/function (_BaseModule) {
452
509
  case 6:
453
510
  resourceResponse = _context3.sent;
454
511
  resourceList = (_resourceResponse$dat = resourceResponse === null || resourceResponse === void 0 || (_resourceResponse$dat2 = resourceResponse.data) === null || _resourceResponse$dat2 === void 0 ? void 0 : _resourceResponse$dat2.list) !== null && _resourceResponse$dat !== void 0 ? _resourceResponse$dat : [];
455
- console.log(1111, currentTime, resourceList, bookingList);
456
512
  if (!(!Array.isArray(resourceList) || resourceList.length === 0)) {
457
- _context3.next = 11;
513
+ _context3.next = 10;
458
514
  break;
459
515
  }
460
516
  return _context3.abrupt("return", []);
461
- case 11:
517
+ case 10:
462
518
  // 全局先做 booking 标准化 + start_time 升序,后续映射直接复用
463
519
  normalizedBookings = bookingList.map(function (booking) {
464
520
  return _this3.normalizeMatchedBooking(current, booking);
@@ -492,7 +548,7 @@ export var SalesImpl = /*#__PURE__*/function (_BaseModule) {
492
548
  bookings: bookings
493
549
  });
494
550
  }));
495
- case 15:
551
+ case 14:
496
552
  case "end":
497
553
  return _context3.stop();
498
554
  }
@@ -21,15 +21,17 @@ export type SalesReservedStatus = 'not_arrived' | 'late' | undefined;
21
21
  export interface SalesResourceMatchedBooking extends Omit<BookingData, 'status'> {
22
22
  status: SalesBookingStatus;
23
23
  isTimeout: boolean;
24
+ timeoutTime?: number;
25
+ progressPercent?: number;
26
+ lateTime?: number;
27
+ remainingReserveTime?: number;
24
28
  reserved_status: SalesReservedStatus;
25
29
  }
26
30
  export interface SalesResourceBookingItem extends Omit<ResourceData, 'bookings'> {
27
31
  resource_id: ResourceId;
28
32
  bookings: SalesResourceMatchedBooking[];
29
33
  }
30
- export interface SalesTimelineHighlights {
31
- [time: string]: number;
32
- }
34
+ export type SalesTimelineHighlights = number[];
33
35
  /**
34
36
  * Sales 解决方案状态
35
37
  *
@@ -61,5 +63,5 @@ export interface SalesModuleAPI {
61
63
  /** 获取资源维度的预约占用列表 */
62
64
  getResourceBookingList: (currentTime: string, bookingList: BookingData[]) => Promise<SalesResourceBookingItem[]>;
63
65
  /** 获取时间轴每个时间片的预约数量 */
64
- getTimelineHighlights: (bookingList: BookingData[]) => SalesTimelineHighlights;
66
+ getTimelineHighlights: (bookingList: BookingData[], startDateTime?: string, endDateTime?: string) => SalesTimelineHighlights;
65
67
  }
@@ -52,6 +52,7 @@ export interface PisellCore {
52
52
  validateContext: (config: ModuleContextConfig) => boolean;
53
53
  serverOptions?: ServerOptions;
54
54
  server?: any;
55
+ setContext: (ctx: Partial<BusinessContext>) => void;
55
56
  }
56
57
  /**
57
58
  * 业务上下文接口
@@ -59,6 +60,7 @@ export interface PisellCore {
59
60
  export interface BusinessContext {
60
61
  userId?: string;
61
62
  companyId?: string;
63
+ locale?: string;
62
64
  [key: string]: any;
63
65
  }
64
66
  /**
@@ -29,6 +29,7 @@ declare class PisellOSCore implements PisellCore {
29
29
  getModuleExports<T = any>(name: string): T | null;
30
30
  hasModule(name: string): boolean;
31
31
  destroy(): Promise<void>;
32
+ setContext(ctx: Partial<BusinessContext>): void;
32
33
  /**
33
34
  * 验证上下文参数
34
35
  */
package/lib/core/index.js CHANGED
@@ -235,6 +235,10 @@ var PisellOSCore = class {
235
235
  this.plugins.clear();
236
236
  this.log("PisellOS 核心已销毁");
237
237
  }
238
+ setContext(ctx) {
239
+ this.context = { ...this.context, ...ctx };
240
+ this.log(`上下文已更新: ${JSON.stringify(Object.keys(ctx))}`);
241
+ }
238
242
  /**
239
243
  * 验证上下文参数
240
244
  */
@@ -9,6 +9,7 @@ export declare class CustomerModule extends BaseModule implements Module, Custom
9
9
  private cacheId;
10
10
  private openCache;
11
11
  private fatherModule;
12
+ private otherParams;
12
13
  constructor(name?: string, version?: string);
13
14
  initialize(core: PisellCore, options?: ModuleOptions): Promise<void>;
14
15
  /**
@@ -46,12 +46,14 @@ var CustomerModule = class extends import_BaseModule.BaseModule {
46
46
  searchParams: {}
47
47
  };
48
48
  this.openCache = false;
49
+ this.otherParams = {};
49
50
  }
50
51
  async initialize(core, options) {
51
52
  var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j;
52
53
  this.core = core;
53
54
  this.store = options == null ? void 0 : options.store;
54
55
  this.request = this.core.getPlugin("request");
56
+ this.otherParams = (options == null ? void 0 : options.otherParams) || {};
55
57
  if (Array.isArray((_a = options == null ? void 0 : options.initialState) == null ? void 0 : _a.customerList)) {
56
58
  const customerList = (0, import_lodash_es.cloneDeep)(options.initialState.customerList || []);
57
59
  this.store.customerList = customerList;
@@ -95,9 +97,12 @@ var CustomerModule = class extends import_BaseModule.BaseModule {
95
97
  * @returns API响应数据
96
98
  */
97
99
  async fetchCustomerListData(params = {}) {
98
- var _a, _b;
99
- const url = "/customer/select-list";
100
- const { skip = 1, num = import_constants.DEFAULT_PAGE_SIZE, search, ...otherParams } = params;
100
+ var _a, _b, _c, _d;
101
+ let url = "/customer/select-list";
102
+ if ((_a = this.otherParams) == null ? void 0 : _a.isFranchisee) {
103
+ url = "/franchisee/customer/select-list";
104
+ }
105
+ const { skip = 1, num = import_constants.DEFAULT_PAGE_SIZE, search, ..._otherParams } = params;
101
106
  const queryParams = {
102
107
  skip,
103
108
  num,
@@ -106,7 +111,7 @@ var CustomerModule = class extends import_BaseModule.BaseModule {
106
111
  search_wallet_flag: 1,
107
112
  search_wallet_pass_flag: 1,
108
113
  ...search && { search },
109
- ...otherParams
114
+ ..._otherParams
110
115
  };
111
116
  const res = await this.request.get(url, queryParams, {
112
117
  cache: {
@@ -117,9 +122,19 @@ var CustomerModule = class extends import_BaseModule.BaseModule {
117
122
  if ((res == null ? void 0 : res.code) !== 200) {
118
123
  throw new Error(res == null ? void 0 : res.message);
119
124
  }
125
+ let _customerList = ((_b = res == null ? void 0 : res.data) == null ? void 0 : _b.list) || [];
126
+ if (((_c = this.otherParams) == null ? void 0 : _c.isFranchisee) && (_customerList == null ? void 0 : _customerList.length) > 0) {
127
+ _customerList = _customerList == null ? void 0 : _customerList.map((item) => {
128
+ return {
129
+ ...item || {},
130
+ is_franchisor_customer: 1
131
+ // 标识是总店的客户
132
+ };
133
+ });
134
+ }
120
135
  return {
121
- customerList: ((_a = res == null ? void 0 : res.data) == null ? void 0 : _a.list) || [],
122
- total: ((_b = res == null ? void 0 : res.data) == null ? void 0 : _b.count) || 0,
136
+ customerList: _customerList,
137
+ total: ((_d = res == null ? void 0 : res.data) == null ? void 0 : _d.count) || 0,
123
138
  page: skip,
124
139
  pageSize: num
125
140
  };
@@ -23,7 +23,7 @@ export declare class OrderModule extends BaseModule implements Module, OrderModu
23
23
  */
24
24
  private logError;
25
25
  createOrder(params: CommitOrderParams['query']): {
26
- type: "virtual" | "appointment_booking";
26
+ type: "appointment_booking" | "virtual";
27
27
  platform: string;
28
28
  sales_channel: string;
29
29
  order_sales_channel: string;
@@ -167,7 +167,7 @@ declare class Server {
167
167
  */
168
168
  private handleUnsubscribeOrderQuery;
169
169
  /**
170
- * 判断预约查询的 es_start_datetime_between 起始日期是否为今天
170
+ * 判断预约查询的 sales_time_between 起始日期是否为今天
171
171
  */
172
172
  private isBookingQueryForToday;
173
173
  /**
@@ -201,11 +201,16 @@ declare class Server {
201
201
  /**
202
202
  * 商品查询的核心计算逻辑(编排 Products、Menu、Schedule 模块)
203
203
  * 供 handleProductQuery 首次返回及 pubsub 变更推送复用
204
+ * @param context 查询上下文
205
+ * @param options 可选参数
206
+ * @param options.changedIds 变更的商品 IDs,用于增量更新价格缓存
204
207
  */
205
208
  private computeProductQueryResult;
206
209
  /**
207
210
  * 数据变更后,遍历所有订阅者重新计算查询结果并通过 callback 推送
208
211
  * 由 ProductsModule 的 onProductsSyncCompleted 事件触发
212
+ * @param options 可选参数
213
+ * @param options.changedIds 变更的商品 IDs,用于增量更新价格缓存
209
214
  */
210
215
  private recomputeAndNotifyProductQuery;
211
216
  /**
@@ -557,8 +557,10 @@ var Server = class {
557
557
  } else {
558
558
  this.logInfo("跳过自动预加载", { autoPreload });
559
559
  }
560
- this.core.effects.on(import_types.ProductsHooks.onProductsSyncCompleted, () => {
561
- this.recomputeAndNotifyProductQuery();
560
+ this.core.effects.on(import_types.ProductsHooks.onProductsSyncCompleted, (payload) => {
561
+ this.recomputeAndNotifyProductQuery({
562
+ changedIds: payload == null ? void 0 : payload.changedIds
563
+ });
562
564
  });
563
565
  this.core.effects.on(import_types2.OrderHooks.onOrdersChanged, () => {
564
566
  this.recomputeAndNotifyOrderQuery();
@@ -834,10 +836,10 @@ var Server = class {
834
836
  }
835
837
  }
836
838
  /**
837
- * 判断预约查询的 es_start_datetime_between 起始日期是否为今天
839
+ * 判断预约查询的 sales_time_between 起始日期是否为今天
838
840
  */
839
841
  isBookingQueryForToday(data) {
840
- const range = data == null ? void 0 : data.es_start_datetime_between;
842
+ const range = data == null ? void 0 : data.sales_time_between;
841
843
  if (!Array.isArray(range) || range.length < 1)
842
844
  return true;
843
845
  const startDateStr = String(range[0]).split("T")[0].split(" ")[0];
@@ -859,18 +861,18 @@ var Server = class {
859
861
  };
860
862
  }
861
863
  try {
862
- const response = await this.app.request.get("/shop/order/sales", { ...data, with: ["all"] }, {
864
+ const response = await this.app.request.get("/shop/order/sales", { ...data, form_record_ids: void 0, with: ["all"] }, {
863
865
  isShopApi: true
864
866
  });
865
867
  const rawList = ((_b = response == null ? void 0 : response.data) == null ? void 0 : _b.list) ?? (response == null ? void 0 : response.list) ?? [];
866
- const list = (0, import_filterBookings.flattenOrdersToBookings)(rawList);
868
+ const list = (0, import_filterBookings.filterBookingsFromOrders)(rawList, data);
867
869
  this.logInfo("fetchBookingListFromAPI: API 返回并拆分完成", {
868
870
  rawCount: rawList.length,
869
- flattenedCount: list.length
871
+ flattenedCount: list.count
870
872
  });
871
873
  return {
872
874
  code: 200,
873
- data: { ...response.data, list },
875
+ data: { ...response.data, list: (list == null ? void 0 : list.list) || [] },
874
876
  message: "",
875
877
  status: true
876
878
  };
@@ -950,14 +952,18 @@ var Server = class {
950
952
  /**
951
953
  * 商品查询的核心计算逻辑(编排 Products、Menu、Schedule 模块)
952
954
  * 供 handleProductQuery 首次返回及 pubsub 变更推送复用
955
+ * @param context 查询上下文
956
+ * @param options 可选参数
957
+ * @param options.changedIds 变更的商品 IDs,用于增量更新价格缓存
953
958
  */
954
- async computeProductQueryResult(context) {
959
+ async computeProductQueryResult(context, options) {
955
960
  const tTotal = performance.now();
956
961
  const { menu_list_ids, schedule_date, schedule_datetime } = context;
957
962
  this.logInfo("computeProductQueryResult 开始", {
958
963
  menuListIdsCount: (menu_list_ids == null ? void 0 : menu_list_ids.length) ?? 0,
959
964
  schedule_datetime,
960
- schedule_date
965
+ schedule_date,
966
+ changedIds: options == null ? void 0 : options.changedIds
961
967
  });
962
968
  if (!this.products) {
963
969
  this.logError("computeProductQueryResult: Products 模块未注册");
@@ -987,6 +993,8 @@ var Server = class {
987
993
  const tPrice = performance.now();
988
994
  const allProductsWithPrice = await this.products.getProductsWithPrice(schedule_date, {
989
995
  scheduleModule: this.getSchedule()
996
+ }, {
997
+ changedIds: options == null ? void 0 : options.changedIds
990
998
  });
991
999
  (0, import_product.perfMark)("computeQuery.getProductsWithPrice", performance.now() - tPrice, {
992
1000
  count: allProductsWithPrice.length
@@ -997,6 +1005,13 @@ var Server = class {
997
1005
  before: allProductsWithPrice.length,
998
1006
  after: filteredProducts.length
999
1007
  });
1008
+ const tStatus = performance.now();
1009
+ const beforeStatusCount = filteredProducts.length;
1010
+ filteredProducts = filteredProducts.filter((p) => ((p == null ? void 0 : p.status) || "published") === "published");
1011
+ (0, import_product.perfMark)("computeQuery.filterByStatus", performance.now() - tStatus, {
1012
+ before: beforeStatusCount,
1013
+ after: filteredProducts.length
1014
+ });
1000
1015
  const tSort = performance.now();
1001
1016
  filteredProducts = filteredProducts.sort((a, b) => {
1002
1017
  const sortDiff = Number(b.sort) - Number(a.sort);
@@ -1025,16 +1040,21 @@ var Server = class {
1025
1040
  /**
1026
1041
  * 数据变更后,遍历所有订阅者重新计算查询结果并通过 callback 推送
1027
1042
  * 由 ProductsModule 的 onProductsSyncCompleted 事件触发
1043
+ * @param options 可选参数
1044
+ * @param options.changedIds 变更的商品 IDs,用于增量更新价格缓存
1028
1045
  */
1029
- async recomputeAndNotifyProductQuery() {
1046
+ async recomputeAndNotifyProductQuery(options) {
1030
1047
  if (this.productQuerySubscribers.size === 0)
1031
1048
  return;
1032
1049
  this.logInfo("recomputeAndNotifyProductQuery: 开始推送", {
1033
- subscriberCount: this.productQuerySubscribers.size
1050
+ subscriberCount: this.productQuerySubscribers.size,
1051
+ changedIds: options == null ? void 0 : options.changedIds
1034
1052
  });
1035
1053
  for (const [subscriberId, subscriber] of this.productQuerySubscribers.entries()) {
1036
1054
  try {
1037
- const result = await this.computeProductQueryResult(subscriber.context);
1055
+ const result = await this.computeProductQueryResult(subscriber.context, {
1056
+ changedIds: options == null ? void 0 : options.changedIds
1057
+ });
1038
1058
  subscriber.callback(result);
1039
1059
  this.logInfo("recomputeAndNotifyProductQuery: 已推送", { subscriberId });
1040
1060
  } catch (error) {
@@ -44,7 +44,7 @@ var OrderModule = class extends import_BaseModule.BaseModule {
44
44
  this.defaultName = "order";
45
45
  this.defaultVersion = "1.0.0";
46
46
  this.pendingSyncMessages = [];
47
- this.ORDER_SYNC_DEBOUNCE_MS = 1e4;
47
+ this.ORDER_SYNC_DEBOUNCE_MS = 5e3;
48
48
  // Map<resource_id, OrderId[]> 资源到订单的倒排索引
49
49
  this.resourceIdIndex = /* @__PURE__ */ new Map();
50
50
  }
@@ -438,7 +438,7 @@ export interface BookingFilters {
438
438
  payment_method?: string[];
439
439
  business_code?: string;
440
440
  appointment_status?: string[];
441
- es_start_datetime_between?: string[];
441
+ sales_time_between?: string[];
442
442
  /**
443
443
  * 仅保留「预约开始时刻」大于等于该时间的条目。
444
444
  * 预约时刻由 start_date 与 start_time 拼接解析(与 order_by start_at 一致)。
@@ -75,7 +75,7 @@ function prepareFilters(f) {
75
75
  customerIdStr: hasCustomerId ? String(f.customer_id) : "",
76
76
  hasBusinessCode: !!f.business_code,
77
77
  businessCode: f.business_code,
78
- bookingTimeRange: parseDateRange(f.es_start_datetime_between),
78
+ bookingTimeRange: parseDateRange(f.sales_time_between),
79
79
  createdAtRange: parseDateRange(f.created_at_between),
80
80
  updatedAtRange: parseDateRange(f.updated_at_between),
81
81
  numberRange: parseNumberRange(f.number_between),