@pisell/pisellos 2.2.135 → 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.
@@ -1,6 +1,6 @@
1
1
  import { Module, PisellCore, ModuleOptions } from '../../types';
2
2
  import { BaseModule } from '../BaseModule';
3
- import { OrderModuleAPI, CommitOrderParams, CheckoutOrderParams } from './types';
3
+ import { OrderModuleAPI, CommitOrderParams, CheckoutOrderParams, CancelOrderParams, ChangeBookingStatusParams } from './types';
4
4
  import { CartItem } from '../Cart/types';
5
5
  export declare class OrderModule extends BaseModule implements Module, OrderModuleAPI {
6
6
  protected defaultName: string;
@@ -39,6 +39,8 @@ export declare class OrderModule extends BaseModule implements Module, OrderModu
39
39
  type: 'holder' | 'account';
40
40
  }): boolean;
41
41
  submitOrder(order: CommitOrderParams): Promise<void>;
42
+ cancelOrder(params: CancelOrderParams): Promise<any>;
43
+ changeBookingStatus(params: ChangeBookingStatusParams): Promise<any>;
42
44
  /**
43
45
  * Checkout 专用:创建订单到后端
44
46
  *
@@ -238,6 +238,89 @@ export var OrderModule = /*#__PURE__*/function (_BaseModule) {
238
238
  }
239
239
  return submitOrder;
240
240
  }()
241
+ }, {
242
+ key: "cancelOrder",
243
+ value: function () {
244
+ var _cancelOrder = _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime().mark(function _callee3(params) {
245
+ var payload;
246
+ return _regeneratorRuntime().wrap(function _callee3$(_context3) {
247
+ while (1) switch (_context3.prev = _context3.next) {
248
+ case 0:
249
+ if (!(!Array.isArray(params.order_ids) || params.order_ids.length === 0)) {
250
+ _context3.next = 2;
251
+ break;
252
+ }
253
+ throw new Error('取消订单失败:order_ids 不能为空');
254
+ case 2:
255
+ payload = {
256
+ order_ids: params.order_ids,
257
+ status: 'cancelled',
258
+ change_schedule_event_appointment_status: 1,
259
+ is_void: 1
260
+ };
261
+ this.logInfo('cancelOrder called', {
262
+ url: '/order/update-status',
263
+ method: 'PUT',
264
+ orderIdsCount: params.order_ids.length
265
+ });
266
+ return _context3.abrupt("return", this.request.put('/order/update-status', payload, {
267
+ isShopApi: true
268
+ }));
269
+ case 5:
270
+ case "end":
271
+ return _context3.stop();
272
+ }
273
+ }, _callee3, this);
274
+ }));
275
+ function cancelOrder(_x4) {
276
+ return _cancelOrder.apply(this, arguments);
277
+ }
278
+ return cancelOrder;
279
+ }()
280
+ }, {
281
+ key: "changeBookingStatus",
282
+ value: function () {
283
+ var _changeBookingStatus = _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime().mark(function _callee4(params) {
284
+ var url, payload;
285
+ return _regeneratorRuntime().wrap(function _callee4$(_context4) {
286
+ while (1) switch (_context4.prev = _context4.next) {
287
+ case 0:
288
+ if (params.booking_id) {
289
+ _context4.next = 2;
290
+ break;
291
+ }
292
+ throw new Error('变更预约状态失败:booking_id 不能为空');
293
+ case 2:
294
+ if (params.appointment_status) {
295
+ _context4.next = 4;
296
+ break;
297
+ }
298
+ throw new Error('变更预约状态失败:appointment_status 不能为空');
299
+ case 4:
300
+ url = "/schedule/booking/appointment-status/".concat(params.booking_id);
301
+ payload = {
302
+ appointment_status: params.appointment_status
303
+ };
304
+ this.logInfo('changeBookingStatus called', {
305
+ url: url,
306
+ method: 'PUT',
307
+ bookingId: params.booking_id,
308
+ appointmentStatus: params.appointment_status
309
+ });
310
+ return _context4.abrupt("return", this.request.put(url, payload, {
311
+ isShopApi: true
312
+ }));
313
+ case 8:
314
+ case "end":
315
+ return _context4.stop();
316
+ }
317
+ }, _callee4, this);
318
+ }));
319
+ function changeBookingStatus(_x5) {
320
+ return _changeBookingStatus.apply(this, arguments);
321
+ }
322
+ return changeBookingStatus;
323
+ }()
241
324
  /**
242
325
  * Checkout 专用:创建订单到后端
243
326
  *
@@ -251,11 +334,11 @@ export var OrderModule = /*#__PURE__*/function (_BaseModule) {
251
334
  }, {
252
335
  key: "createOrderByCheckout",
253
336
  value: (function () {
254
- var _createOrderByCheckout = _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime().mark(function _callee3(params) {
337
+ var _createOrderByCheckout = _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime().mark(function _callee5(params) {
255
338
  var _params$payments, _params$bookings2, _params$relation_prod2, _params$payments2, _params$payments3, _params$bookings3, _params$relation_prod3, _params$payments4;
256
339
  var onlineStorePaymentCodeList, _orderData$payments, _orderData$bookings, _orderData$relation_p, _orderData$payments2, _orderData$payments3, _orderData$bookings2, _orderData$relation_p2, _orderData$payments4, _orderData$payments5, _response$data, orderData, response;
257
- return _regeneratorRuntime().wrap(function _callee3$(_context3) {
258
- while (1) switch (_context3.prev = _context3.next) {
340
+ return _regeneratorRuntime().wrap(function _callee5$(_context5) {
341
+ while (1) switch (_context5.prev = _context5.next) {
259
342
  case 0:
260
343
  // 过滤掉由在线店铺下单的 payment 支付数据
261
344
  onlineStorePaymentCodeList = ['WXPAY', 'WECHAT', 'ALIPAY', 'APPLE_PAY', 'CREDIT_CARD_3DS', 'CREDIT_CARD_TOKEN', 'GOOGLE_PAY', 'GOOGLE_PAY_3DS', 'CREDIT_CARD'];
@@ -289,7 +372,7 @@ export var OrderModule = /*#__PURE__*/function (_BaseModule) {
289
372
  relationProductsCount: ((_params$relation_prod3 = params.relation_products) === null || _params$relation_prod3 === void 0 ? void 0 : _params$relation_prod3.length) || 0,
290
373
  paymentsCount: ((_params$payments4 = params.payments) === null || _params$payments4 === void 0 ? void 0 : _params$payments4.length) || 0
291
374
  });
292
- _context3.prev = 4;
375
+ _context5.prev = 4;
293
376
  // 构建订单数据,设置默认值并允许 params 覆盖
294
377
  orderData = _objectSpread({
295
378
  sales_channel: 'my_pisel',
@@ -353,10 +436,10 @@ export var OrderModule = /*#__PURE__*/function (_BaseModule) {
353
436
  });
354
437
 
355
438
  // 调用后端接口
356
- _context3.next = 12;
439
+ _context5.next = 12;
357
440
  return this.request.post('/order/checkout', orderData);
358
441
  case 12:
359
- response = _context3.sent;
442
+ response = _context5.sent;
360
443
  this.logInfo('Order API called successfully', {
361
444
  response: response
362
445
  });
@@ -364,22 +447,22 @@ export var OrderModule = /*#__PURE__*/function (_BaseModule) {
364
447
  success: !!response,
365
448
  hasOrderId: !!(response !== null && response !== void 0 && (_response$data = response.data) !== null && _response$data !== void 0 && _response$data.order_id || response !== null && response !== void 0 && response.order_id)
366
449
  });
367
- return _context3.abrupt("return", response);
450
+ return _context5.abrupt("return", response);
368
451
  case 18:
369
- _context3.prev = 18;
370
- _context3.t0 = _context3["catch"](4);
371
- console.error('[Order] createOrderByCheckout 创建订单失败:', _context3.t0);
452
+ _context5.prev = 18;
453
+ _context5.t0 = _context5["catch"](4);
454
+ console.error('[Order] createOrderByCheckout 创建订单失败:', _context5.t0);
372
455
  this.logInfo('Order API called failed', {
373
- error: _context3.t0 instanceof Error ? _context3.t0.message : String(_context3.t0)
456
+ error: _context5.t0 instanceof Error ? _context5.t0.message : String(_context5.t0)
374
457
  });
375
- throw _context3.t0;
458
+ throw _context5.t0;
376
459
  case 23:
377
460
  case "end":
378
- return _context3.stop();
461
+ return _context5.stop();
379
462
  }
380
- }, _callee3, this, [[4, 18]]);
463
+ }, _callee5, this, [[4, 18]]);
381
464
  }));
382
- function createOrderByCheckout(_x4) {
465
+ function createOrderByCheckout(_x6) {
383
466
  return _createOrderByCheckout.apply(this, arguments);
384
467
  }
385
468
  return createOrderByCheckout;
@@ -58,6 +58,19 @@ export interface CheckoutOrderParams {
58
58
  small_ticket_data_flag?: number;
59
59
  [key: string]: any;
60
60
  }
61
+ /**
62
+ * 取消订单参数
63
+ */
64
+ export interface CancelOrderParams {
65
+ order_ids: Array<number | string>;
66
+ }
67
+ /**
68
+ * 变更预约状态参数
69
+ */
70
+ export interface ChangeBookingStatusParams {
71
+ booking_id: number | string;
72
+ appointment_status: string;
73
+ }
61
74
  /**
62
75
  * 订单模块 API
63
76
  */
@@ -86,4 +99,16 @@ export interface OrderModuleAPI {
86
99
  * @returns 后端返回的订单数据
87
100
  */
88
101
  createOrderByCheckout: (params: CheckoutOrderParams) => Promise<any>;
102
+ /**
103
+ * 取消订单
104
+ * @param params 取消订单参数
105
+ * @returns 后端返回结果
106
+ */
107
+ cancelOrder: (params: CancelOrderParams) => Promise<any>;
108
+ /**
109
+ * 变更预约状态
110
+ * @param params 预约状态参数
111
+ * @returns 后端返回结果
112
+ */
113
+ changeBookingStatus: (params: ChangeBookingStatusParams) => Promise<any>;
89
114
  }
@@ -18,6 +18,14 @@ export var OrderHooks = /*#__PURE__*/function (OrderHooks) {
18
18
  * Checkout 专用订单创建参数
19
19
  */
20
20
 
21
+ /**
22
+ * 取消订单参数
23
+ */
24
+
25
+ /**
26
+ * 变更预约状态参数
27
+ */
28
+
21
29
  /**
22
30
  * 订单模块 API
23
31
  */
@@ -49,5 +49,5 @@ export declare class Product extends BaseModule implements Module {
49
49
  getCategories(): ProductCategory[];
50
50
  setOtherParams(key: string, value: any): void;
51
51
  getOtherParams(): any;
52
- getProductType(): "duration" | "session" | "normal";
52
+ getProductType(): "normal" | "duration" | "session";
53
53
  }
@@ -136,7 +136,7 @@ export declare class BookingTicketImpl extends BaseModule implements Module {
136
136
  * 获取当前的客户搜索条件
137
137
  * @returns 当前搜索条件
138
138
  */
139
- getCurrentCustomerSearchParams(): Omit<import("../../modules").ShopGetCustomerListParams, "num" | "skip">;
139
+ getCurrentCustomerSearchParams(): Omit<import("../../modules").ShopGetCustomerListParams, "skip" | "num">;
140
140
  /**
141
141
  * 获取客户列表状态(包含滚动加载相关状态)
142
142
  * @returns 客户状态
@@ -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 } 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
  /**
@@ -39,6 +39,8 @@ export declare class SalesImpl extends BaseModule implements Module, SalesModule
39
39
  setReservationList(reservationList: Reservation[]): Reservation[];
40
40
  /** 重置预约列表为空 */
41
41
  resetReservationList(): Reservation[];
42
+ cancelOrder(orderIds: Array<number | string>): Promise<any>;
43
+ changeBookingStatus(params: ChangeBookingStatusParams): Promise<any>;
42
44
  destroy(): Promise<void>;
43
45
  /**
44
46
  * 时间轴粒度(分钟)
@@ -77,6 +79,10 @@ export declare class SalesImpl extends BaseModule implements Module, SalesModule
77
79
  */
78
80
  private getBookingStatus;
79
81
  private getResourceId;
82
+ private getBookingAppointmentStatus;
83
+ private getBookingOrderPaymentStatus;
84
+ private isBookingMatchedResource;
85
+ private sortMatchedBookings;
80
86
  /**
81
87
  * 资源下 bookings 的返回裁剪策略:
82
88
  * 1) 优先返回当前时刻正在进行中的预约(允许并发返回多条)
@@ -98,6 +104,8 @@ export declare class SalesImpl extends BaseModule implements Module, SalesModule
98
104
  * - 否则走快照分支(纯时间窗判定,忽略 appointment_status 的 arrived/started/completed 差异)
99
105
  */
100
106
  private normalizeMatchedBooking;
107
+ private normalizeResourceBookings;
108
+ private buildCompletedBooking;
101
109
  /**
102
110
  * 实时模式(current === deviceCurrent):信任 appointment_status
103
111
  * - 过滤 new / completed(实时视角下视为非活跃数据)
@@ -122,6 +130,7 @@ export declare class SalesImpl extends BaseModule implements Module, SalesModule
122
130
  */
123
131
  private buildSnapshotBooking;
124
132
  getResourceBookingList(currentTime: string, bookingList?: BookingData[], deviceTime?: string): Promise<SalesResourceBookingItem[]>;
133
+ getResourceBookingItem(params: GetResourceBookingItemParams): Promise<SalesResourceBookingItemWithGroups | null>;
125
134
  }
126
135
  export { SalesImpl as Sales };
127
136
  export default SalesImpl;
@@ -161,26 +161,80 @@ export var SalesImpl = /*#__PURE__*/function (_BaseModule) {
161
161
  });
162
162
  return [];
163
163
  }
164
-
165
- // -------------------------------------------------------------------------
164
+ }, {
165
+ key: "cancelOrder",
166
+ value: function () {
167
+ var _cancelOrder = _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime().mark(function _callee2(orderIds) {
168
+ var orderModule;
169
+ return _regeneratorRuntime().wrap(function _callee2$(_context2) {
170
+ while (1) switch (_context2.prev = _context2.next) {
171
+ case 0:
172
+ orderModule = this.core.getModule('order');
173
+ if (orderModule) {
174
+ _context2.next = 3;
175
+ break;
176
+ }
177
+ throw new Error('Sales 取消订单失败:Order 模块未注册');
178
+ case 3:
179
+ return _context2.abrupt("return", orderModule.cancelOrder({
180
+ order_ids: orderIds
181
+ }));
182
+ case 4:
183
+ case "end":
184
+ return _context2.stop();
185
+ }
186
+ }, _callee2, this);
187
+ }));
188
+ function cancelOrder(_x2) {
189
+ return _cancelOrder.apply(this, arguments);
190
+ }
191
+ return cancelOrder;
192
+ }()
193
+ }, {
194
+ key: "changeBookingStatus",
195
+ value: function () {
196
+ var _changeBookingStatus = _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime().mark(function _callee3(params) {
197
+ var orderModule;
198
+ return _regeneratorRuntime().wrap(function _callee3$(_context3) {
199
+ while (1) switch (_context3.prev = _context3.next) {
200
+ case 0:
201
+ orderModule = this.core.getModule('order');
202
+ if (orderModule) {
203
+ _context3.next = 3;
204
+ break;
205
+ }
206
+ throw new Error('Sales 变更预约状态失败:Order 模块未注册');
207
+ case 3:
208
+ return _context3.abrupt("return", orderModule.changeBookingStatus(params));
209
+ case 4:
210
+ case "end":
211
+ return _context3.stop();
212
+ }
213
+ }, _callee3, this);
214
+ }));
215
+ function changeBookingStatus(_x3) {
216
+ return _changeBookingStatus.apply(this, arguments);
217
+ }
218
+ return changeBookingStatus;
219
+ }() // -------------------------------------------------------------------------
166
220
  // 生命周期
167
221
  // -------------------------------------------------------------------------
168
222
  }, {
169
223
  key: "destroy",
170
224
  value: function () {
171
- var _destroy = _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime().mark(function _callee2() {
172
- return _regeneratorRuntime().wrap(function _callee2$(_context2) {
173
- while (1) switch (_context2.prev = _context2.next) {
225
+ var _destroy = _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime().mark(function _callee4() {
226
+ return _regeneratorRuntime().wrap(function _callee4$(_context4) {
227
+ while (1) switch (_context4.prev = _context4.next) {
174
228
  case 0:
175
- _context2.next = 2;
229
+ _context4.next = 2;
176
230
  return this.core.effects.emit("".concat(this.name, ":onDestroy"), {});
177
231
  case 2:
178
232
  _get(_getPrototypeOf(SalesImpl.prototype), "destroy", this).call(this);
179
233
  case 3:
180
234
  case "end":
181
- return _context2.stop();
235
+ return _context4.stop();
182
236
  }
183
- }, _callee2, this);
237
+ }, _callee4, this);
184
238
  }));
185
239
  function destroy() {
186
240
  return _destroy.apply(this, arguments);
@@ -342,6 +396,38 @@ export var SalesImpl = /*#__PURE__*/function (_BaseModule) {
342
396
  var _ref3, _resource$id;
343
397
  return (_ref3 = (_resource$id = resource.id) !== null && _resource$id !== void 0 ? _resource$id : resource.form_record_id) !== null && _ref3 !== void 0 ? _ref3 : '';
344
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
+ }
345
431
 
346
432
  /**
347
433
  * 资源下 bookings 的返回裁剪策略:
@@ -353,17 +439,17 @@ export var SalesImpl = /*#__PURE__*/function (_BaseModule) {
353
439
  }, {
354
440
  key: "pickBookingsForCurrentPoint",
355
441
  value: function pickBookingsForCurrentPoint(current, bookings) {
356
- var _this2 = this;
442
+ var _this3 = this;
357
443
  if (bookings.length === 0) return [];
358
444
  var currentDayStart = current.startOf('day');
359
445
  var currentDayEnd = current.endOf('day');
360
446
 
361
447
  // 仅保留与 current 当天有时间交集的预约,避免跨天误命中
362
448
  var dayScopedBookings = bookings.filter(function (booking) {
363
- var startAt = _this2.toBookingDateTime(booking.start_date, booking.start_time);
364
- 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);
365
451
  if (!startAt.isValid() || !endAt.isValid()) return false;
366
- return _this2.isSameOrBefore(startAt, currentDayEnd) && _this2.isSameOrAfter(endAt, currentDayStart);
452
+ return _this3.isSameOrBefore(startAt, currentDayEnd) && _this3.isSameOrAfter(endAt, currentDayStart);
367
453
  });
368
454
  if (dayScopedBookings.length === 0) return [];
369
455
  var appendNextStartGroupIfNeeded = function appendNextStartGroupIfNeeded(selectedBookings) {
@@ -373,21 +459,21 @@ export var SalesImpl = /*#__PURE__*/function (_BaseModule) {
373
459
  });
374
460
  if (!shouldAppendNextGroup) return selectedBookings;
375
461
  var selectedStartValues = new Set(selectedBookings.map(function (booking) {
376
- return _this2.toBookingDateTime(booking.start_date, booking.start_time);
462
+ return _this3.toBookingDateTime(booking.start_date, booking.start_time);
377
463
  }).filter(function (startAt) {
378
464
  return startAt.isValid();
379
465
  }).map(function (startAt) {
380
466
  return startAt.valueOf();
381
467
  }));
382
468
  var futureCandidates = dayScopedBookings.filter(function (booking) {
383
- var startAt = _this2.toBookingDateTime(booking.start_date, booking.start_time);
469
+ var startAt = _this3.toBookingDateTime(booking.start_date, booking.start_time);
384
470
  if (!startAt.isValid()) return false;
385
471
  return startAt.isAfter(current) && !selectedStartValues.has(startAt.valueOf());
386
472
  });
387
473
  if (futureCandidates.length === 0) return selectedBookings;
388
- 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();
389
475
  var nextStartGroup = futureCandidates.filter(function (booking) {
390
- var startAt = _this2.toBookingDateTime(booking.start_date, booking.start_time);
476
+ var startAt = _this3.toBookingDateTime(booking.start_date, booking.start_time);
391
477
  return startAt.isValid() && startAt.valueOf() === nextStartAt;
392
478
  });
393
479
  if (nextStartGroup.length === 0) return selectedBookings;
@@ -399,10 +485,10 @@ export var SalesImpl = /*#__PURE__*/function (_BaseModule) {
399
485
 
400
486
  // active: current 处于 [startAt, endAt] 区间
401
487
  var activeBookings = dayScopedBookings.filter(function (booking) {
402
- var startAt = _this2.toBookingDateTime(booking.start_date, booking.start_time);
403
- 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);
404
490
  if (!startAt.isValid() || !endAt.isValid()) return false;
405
- return _this2.isSameOrAfter(current, startAt) && _this2.isSameOrBefore(current, endAt);
491
+ return _this3.isSameOrAfter(current, startAt) && _this3.isSameOrBefore(current, endAt);
406
492
  });
407
493
  if (activeBookings.length > 0) return appendNextStartGroupIfNeeded(activeBookings);
408
494
 
@@ -414,13 +500,13 @@ export var SalesImpl = /*#__PURE__*/function (_BaseModule) {
414
500
 
415
501
  // upcoming: 从未来预约里只取最早那一组(同 start_time)
416
502
  var upcoming = dayScopedBookings.filter(function (booking) {
417
- var startAt = _this2.toBookingDateTime(booking.start_date, booking.start_time);
503
+ var startAt = _this3.toBookingDateTime(booking.start_date, booking.start_time);
418
504
  return startAt.isValid() && startAt.isAfter(current) && startAt.isSame(current, 'day');
419
505
  });
420
506
  if (upcoming.length > 0) {
421
507
  var firstStartAt = this.toBookingDateTime(upcoming[0].start_date, upcoming[0].start_time).valueOf();
422
508
  var upcomingStartGroup = upcoming.filter(function (booking) {
423
- var startAt = _this2.toBookingDateTime(booking.start_date, booking.start_time);
509
+ var startAt = _this3.toBookingDateTime(booking.start_date, booking.start_time);
424
510
  return startAt.valueOf() === firstStartAt;
425
511
  });
426
512
  return appendNextStartGroupIfNeeded(upcomingStartGroup);
@@ -457,16 +543,58 @@ export var SalesImpl = /*#__PURE__*/function (_BaseModule) {
457
543
  }, {
458
544
  key: "normalizeMatchedBooking",
459
545
  value: function normalizeMatchedBooking(current, deviceCurrent, booking) {
460
- var _ref4, _booking$appointment_2;
461
- 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);
462
548
  if (appointmentStatus === 'rejected' || appointmentStatus === 'cancelled') return null;
463
549
  var startAt = this.toBookingDateTime(booking.start_date, booking.start_time);
464
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
+ }
465
554
  if (current.isSame(deviceCurrent)) {
466
555
  return this.buildRealtimeBooking(current, booking, appointmentStatus, startAt, endAt);
467
556
  }
468
557
  return this.buildSnapshotBooking(current, booking, appointmentStatus, startAt, endAt);
469
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
+ }
470
598
 
471
599
  /**
472
600
  * 实时模式(current === deviceCurrent):信任 appointment_status
@@ -561,10 +689,10 @@ export var SalesImpl = /*#__PURE__*/function (_BaseModule) {
561
689
  }, {
562
690
  key: "getResourceBookingList",
563
691
  value: function () {
564
- var _getResourceBookingList = _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime().mark(function _callee3(currentTime) {
692
+ var _getResourceBookingList = _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime().mark(function _callee5(currentTime) {
565
693
  var _resourceResponse$dat,
566
694
  _resourceResponse$dat2,
567
- _this3 = this;
695
+ _this5 = this;
568
696
  var bookingList,
569
697
  deviceTime,
570
698
  current,
@@ -574,28 +702,28 @@ export var SalesImpl = /*#__PURE__*/function (_BaseModule) {
574
702
  normalizedBookings,
575
703
  bookingMap,
576
704
  list,
577
- _args3 = arguments;
578
- return _regeneratorRuntime().wrap(function _callee3$(_context3) {
579
- while (1) switch (_context3.prev = _context3.next) {
705
+ _args5 = arguments;
706
+ return _regeneratorRuntime().wrap(function _callee5$(_context5) {
707
+ while (1) switch (_context5.prev = _context5.next) {
580
708
  case 0:
581
- bookingList = _args3.length > 1 && _args3[1] !== undefined ? _args3[1] : [];
582
- deviceTime = _args3.length > 2 && _args3[2] !== undefined ? _args3[2] : currentTime;
709
+ bookingList = _args5.length > 1 && _args5[1] !== undefined ? _args5[1] : [];
710
+ deviceTime = _args5.length > 2 && _args5[2] !== undefined ? _args5[2] : currentTime;
583
711
  // currentTime 约定为完整 datetime;非法值直接返回空,避免误判
584
712
  current = dayjs(currentTime);
585
713
  if (current.isValid()) {
586
- _context3.next = 5;
714
+ _context5.next = 5;
587
715
  break;
588
716
  }
589
- return _context3.abrupt("return", []);
717
+ return _context5.abrupt("return", []);
590
718
  case 5:
591
719
  deviceCurrent = dayjs(deviceTime);
592
720
  if (deviceCurrent.isValid()) {
593
- _context3.next = 8;
721
+ _context5.next = 8;
594
722
  break;
595
723
  }
596
- return _context3.abrupt("return", []);
724
+ return _context5.abrupt("return", []);
597
725
  case 8:
598
- _context3.next = 10;
726
+ _context5.next = 10;
599
727
  return this.request.get('/shop/form/resource/page', {
600
728
  skip: 1,
601
729
  num: 999
@@ -606,29 +734,16 @@ export var SalesImpl = /*#__PURE__*/function (_BaseModule) {
606
734
  isShopApi: true
607
735
  });
608
736
  case 10:
609
- resourceResponse = _context3.sent;
737
+ resourceResponse = _context5.sent;
610
738
  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 : [];
611
739
  if (!(!Array.isArray(resourceList) || resourceList.length === 0)) {
612
- _context3.next = 14;
740
+ _context5.next = 14;
613
741
  break;
614
742
  }
615
- return _context3.abrupt("return", []);
743
+ return _context5.abrupt("return", []);
616
744
  case 14:
617
745
  // 全局先做 booking 标准化 + start_time 升序,后续映射直接复用
618
- normalizedBookings = bookingList.map(function (booking) {
619
- return _this3.normalizeMatchedBooking(current, deviceCurrent, booking);
620
- }).filter(function (booking) {
621
- return Boolean(booking);
622
- }).sort(function (left, right) {
623
- var leftStartAt = _this3.toBookingDateTime(left.start_date, left.start_time).valueOf();
624
- var rightStartAt = _this3.toBookingDateTime(right.start_date, right.start_time).valueOf();
625
- return leftStartAt - rightStartAt;
626
- }); // 过滤掉没有 status 的 booking
627
- normalizedBookings = normalizedBookings.filter(function (booking) {
628
- return booking.status;
629
- });
630
-
631
- // 建立 resourceId -> bookings[] 倒排索引,加速资源匹配
746
+ normalizedBookings = this.normalizeResourceBookings(current, deviceCurrent, bookingList); // 建立 resourceId -> bookings[] 倒排索引,加速资源匹配
632
747
  bookingMap = new Map();
633
748
  normalizedBookings.forEach(function (booking) {
634
749
  if (!Array.isArray(booking.resources)) return;
@@ -644,9 +759,9 @@ export var SalesImpl = /*#__PURE__*/function (_BaseModule) {
644
759
 
645
760
  // 输出:每个资源都返回;bookings 可能是 0 / 1 / 多条(并发场景)
646
761
  list = resourceList.map(function (resource) {
647
- var resourceId = _this3.getResourceId(resource);
762
+ var resourceId = _this5.getResourceId(resource);
648
763
  var matchedBookings = bookingMap.get(String(resourceId)) || [];
649
- var bookings = _this3.pickBookingsForCurrentPoint(current, matchedBookings);
764
+ var bookings = _this5.pickBookingsForCurrentPoint(current, matchedBookings);
650
765
  return _objectSpread(_objectSpread({}, resource), {}, {
651
766
  resource_id: resourceId,
652
767
  bookings: bookings
@@ -662,18 +777,117 @@ export var SalesImpl = /*#__PURE__*/function (_BaseModule) {
662
777
  list: list === null || list === void 0 ? void 0 : list.length
663
778
  }
664
779
  });
665
- return _context3.abrupt("return", list);
666
- case 21:
780
+ return _context5.abrupt("return", list);
781
+ case 20:
667
782
  case "end":
668
- return _context3.stop();
783
+ return _context5.stop();
669
784
  }
670
- }, _callee3, this);
785
+ }, _callee5, this);
671
786
  }));
672
- function getResourceBookingList(_x2) {
787
+ function getResourceBookingList(_x4) {
673
788
  return _getResourceBookingList.apply(this, arguments);
674
789
  }
675
790
  return getResourceBookingList;
676
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
+ }()
677
891
  }]);
678
892
  return SalesImpl;
679
893
  }(BaseModule);
@@ -32,7 +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[];
40
+ export interface ChangeBookingStatusParams {
41
+ booking_id: number | string;
42
+ appointment_status: string;
43
+ }
44
+ export interface GetResourceBookingItemParams {
45
+ resourceId: ResourceId;
46
+ currentTime: string;
47
+ bookingList: BookingData[];
48
+ deviceTime?: string;
49
+ }
36
50
  /**
37
51
  * Sales 解决方案状态
38
52
  *
@@ -61,8 +75,14 @@ export interface SalesModuleAPI {
61
75
  setReservationList: (reservationList: Reservation[]) => Reservation[];
62
76
  /** 重置预约列表为空 */
63
77
  resetReservationList: () => Reservation[];
78
+ /** 取消订单 */
79
+ cancelOrder: (orderIds: Array<number | string>) => Promise<any>;
80
+ /** 变更预约状态 */
81
+ changeBookingStatus: (params: ChangeBookingStatusParams) => Promise<any>;
64
82
  /** 获取资源维度的预约占用列表 */
65
83
  getResourceBookingList: (currentTime: string, bookingList: BookingData[], deviceTime?: string) => Promise<SalesResourceBookingItem[]>;
84
+ /** 获取单个资源维度的预约占用、已完成、未支付列表 */
85
+ getResourceBookingItem: (params: GetResourceBookingItemParams) => Promise<SalesResourceBookingItemWithGroups | null>;
66
86
  /** 获取时间轴每个时间片的预约数量 */
67
87
  getTimelineHighlights: (bookingList: BookingData[], startDateTime?: string, endDateTime?: string) => SalesTimelineHighlights;
68
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
- });
@@ -1,6 +1,6 @@
1
1
  import { Module, PisellCore, ModuleOptions } from '../../types';
2
2
  import { BaseModule } from '../BaseModule';
3
- import { OrderModuleAPI, CommitOrderParams, CheckoutOrderParams } from './types';
3
+ import { OrderModuleAPI, CommitOrderParams, CheckoutOrderParams, CancelOrderParams, ChangeBookingStatusParams } from './types';
4
4
  import { CartItem } from '../Cart/types';
5
5
  export declare class OrderModule extends BaseModule implements Module, OrderModuleAPI {
6
6
  protected defaultName: string;
@@ -39,6 +39,8 @@ export declare class OrderModule extends BaseModule implements Module, OrderModu
39
39
  type: 'holder' | 'account';
40
40
  }): boolean;
41
41
  submitOrder(order: CommitOrderParams): Promise<void>;
42
+ cancelOrder(params: CancelOrderParams): Promise<any>;
43
+ changeBookingStatus(params: ChangeBookingStatusParams): Promise<any>;
42
44
  /**
43
45
  * Checkout 专用:创建订单到后端
44
46
  *
@@ -182,6 +182,42 @@ var OrderModule = class extends import_BaseModule.BaseModule {
182
182
  });
183
183
  return this.request.post(fetchUrl, params);
184
184
  }
185
+ async cancelOrder(params) {
186
+ if (!Array.isArray(params.order_ids) || params.order_ids.length === 0) {
187
+ throw new Error("取消订单失败:order_ids 不能为空");
188
+ }
189
+ const payload = {
190
+ order_ids: params.order_ids,
191
+ status: "cancelled",
192
+ change_schedule_event_appointment_status: 1,
193
+ is_void: 1
194
+ };
195
+ this.logInfo("cancelOrder called", {
196
+ url: "/order/update-status",
197
+ method: "PUT",
198
+ orderIdsCount: params.order_ids.length
199
+ });
200
+ return this.request.put("/order/update-status", payload, { isShopApi: true });
201
+ }
202
+ async changeBookingStatus(params) {
203
+ if (!params.booking_id) {
204
+ throw new Error("变更预约状态失败:booking_id 不能为空");
205
+ }
206
+ if (!params.appointment_status) {
207
+ throw new Error("变更预约状态失败:appointment_status 不能为空");
208
+ }
209
+ const url = `/schedule/booking/appointment-status/${params.booking_id}`;
210
+ const payload = {
211
+ appointment_status: params.appointment_status
212
+ };
213
+ this.logInfo("changeBookingStatus called", {
214
+ url,
215
+ method: "PUT",
216
+ bookingId: params.booking_id,
217
+ appointmentStatus: params.appointment_status
218
+ });
219
+ return this.request.put(url, payload, { isShopApi: true });
220
+ }
185
221
  /**
186
222
  * Checkout 专用:创建订单到后端
187
223
  *
@@ -58,6 +58,19 @@ export interface CheckoutOrderParams {
58
58
  small_ticket_data_flag?: number;
59
59
  [key: string]: any;
60
60
  }
61
+ /**
62
+ * 取消订单参数
63
+ */
64
+ export interface CancelOrderParams {
65
+ order_ids: Array<number | string>;
66
+ }
67
+ /**
68
+ * 变更预约状态参数
69
+ */
70
+ export interface ChangeBookingStatusParams {
71
+ booking_id: number | string;
72
+ appointment_status: string;
73
+ }
61
74
  /**
62
75
  * 订单模块 API
63
76
  */
@@ -86,4 +99,16 @@ export interface OrderModuleAPI {
86
99
  * @returns 后端返回的订单数据
87
100
  */
88
101
  createOrderByCheckout: (params: CheckoutOrderParams) => Promise<any>;
102
+ /**
103
+ * 取消订单
104
+ * @param params 取消订单参数
105
+ * @returns 后端返回结果
106
+ */
107
+ cancelOrder: (params: CancelOrderParams) => Promise<any>;
108
+ /**
109
+ * 变更预约状态
110
+ * @param params 预约状态参数
111
+ * @returns 后端返回结果
112
+ */
113
+ changeBookingStatus: (params: ChangeBookingStatusParams) => Promise<any>;
89
114
  }
@@ -49,5 +49,5 @@ export declare class Product extends BaseModule implements Module {
49
49
  getCategories(): ProductCategory[];
50
50
  setOtherParams(key: string, value: any): void;
51
51
  getOtherParams(): any;
52
- getProductType(): "duration" | "session" | "normal";
52
+ getProductType(): "normal" | "duration" | "session";
53
53
  }
@@ -136,7 +136,7 @@ export declare class BookingTicketImpl extends BaseModule implements Module {
136
136
  * 获取当前的客户搜索条件
137
137
  * @returns 当前搜索条件
138
138
  */
139
- getCurrentCustomerSearchParams(): Omit<import("../../modules").ShopGetCustomerListParams, "num" | "skip">;
139
+ getCurrentCustomerSearchParams(): Omit<import("../../modules").ShopGetCustomerListParams, "skip" | "num">;
140
140
  /**
141
141
  * 获取客户列表状态(包含滚动加载相关状态)
142
142
  * @returns 客户状态
@@ -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 } 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
  /**
@@ -39,6 +39,8 @@ export declare class SalesImpl extends BaseModule implements Module, SalesModule
39
39
  setReservationList(reservationList: Reservation[]): Reservation[];
40
40
  /** 重置预约列表为空 */
41
41
  resetReservationList(): Reservation[];
42
+ cancelOrder(orderIds: Array<number | string>): Promise<any>;
43
+ changeBookingStatus(params: ChangeBookingStatusParams): Promise<any>;
42
44
  destroy(): Promise<void>;
43
45
  /**
44
46
  * 时间轴粒度(分钟)
@@ -77,6 +79,10 @@ export declare class SalesImpl extends BaseModule implements Module, SalesModule
77
79
  */
78
80
  private getBookingStatus;
79
81
  private getResourceId;
82
+ private getBookingAppointmentStatus;
83
+ private getBookingOrderPaymentStatus;
84
+ private isBookingMatchedResource;
85
+ private sortMatchedBookings;
80
86
  /**
81
87
  * 资源下 bookings 的返回裁剪策略:
82
88
  * 1) 优先返回当前时刻正在进行中的预约(允许并发返回多条)
@@ -98,6 +104,8 @@ export declare class SalesImpl extends BaseModule implements Module, SalesModule
98
104
  * - 否则走快照分支(纯时间窗判定,忽略 appointment_status 的 arrived/started/completed 差异)
99
105
  */
100
106
  private normalizeMatchedBooking;
107
+ private normalizeResourceBookings;
108
+ private buildCompletedBooking;
101
109
  /**
102
110
  * 实时模式(current === deviceCurrent):信任 appointment_status
103
111
  * - 过滤 new / completed(实时视角下视为非活跃数据)
@@ -122,6 +130,7 @@ export declare class SalesImpl extends BaseModule implements Module, SalesModule
122
130
  */
123
131
  private buildSnapshotBooking;
124
132
  getResourceBookingList(currentTime: string, bookingList?: BookingData[], deviceTime?: string): Promise<SalesResourceBookingItem[]>;
133
+ getResourceBookingItem(params: GetResourceBookingItemParams): Promise<SalesResourceBookingItemWithGroups | null>;
125
134
  }
126
135
  export { SalesImpl as Sales };
127
136
  export default SalesImpl;
@@ -100,6 +100,20 @@ var SalesImpl = class extends import_BaseModule.BaseModule {
100
100
  this.core.effects.emit(import_types.SalesHooks.onReservationListChanged, { reservationList: [] });
101
101
  return [];
102
102
  }
103
+ async cancelOrder(orderIds) {
104
+ const orderModule = this.core.getModule("order");
105
+ if (!orderModule) {
106
+ throw new Error("Sales 取消订单失败:Order 模块未注册");
107
+ }
108
+ return orderModule.cancelOrder({ order_ids: orderIds });
109
+ }
110
+ async changeBookingStatus(params) {
111
+ const orderModule = this.core.getModule("order");
112
+ if (!orderModule) {
113
+ throw new Error("Sales 变更预约状态失败:Order 模块未注册");
114
+ }
115
+ return orderModule.changeBookingStatus(params);
116
+ }
103
117
  // -------------------------------------------------------------------------
104
118
  // 生命周期
105
119
  // -------------------------------------------------------------------------
@@ -233,6 +247,30 @@ var SalesImpl = class extends import_BaseModule.BaseModule {
233
247
  getResourceId(resource) {
234
248
  return resource.id ?? resource.form_record_id ?? "";
235
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
+ }
236
274
  /**
237
275
  * 资源下 bookings 的返回裁剪策略:
238
276
  * 1) 优先返回当前时刻正在进行中的预约(允许并发返回多条)
@@ -341,17 +379,50 @@ var SalesImpl = class extends import_BaseModule.BaseModule {
341
379
  * - current === deviceCurrent 走实时分支(信任 appointment_status)
342
380
  * - 否则走快照分支(纯时间窗判定,忽略 appointment_status 的 arrived/started/completed 差异)
343
381
  */
344
- normalizeMatchedBooking(current, deviceCurrent, booking) {
345
- const appointmentStatus = String(booking.appointment_status ?? booking.status ?? "");
382
+ normalizeMatchedBooking(current, deviceCurrent, booking, options = {}) {
383
+ const appointmentStatus = this.getBookingAppointmentStatus(booking);
346
384
  if (appointmentStatus === "rejected" || appointmentStatus === "cancelled")
347
385
  return null;
348
386
  const startAt = this.toBookingDateTime(booking.start_date, booking.start_time);
349
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
+ }
350
391
  if (current.isSame(deviceCurrent)) {
351
392
  return this.buildRealtimeBooking(current, booking, appointmentStatus, startAt, endAt);
352
393
  }
353
394
  return this.buildSnapshotBooking(current, booking, appointmentStatus, startAt, endAt);
354
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
+ }
355
426
  /**
356
427
  * 实时模式(current === deviceCurrent):信任 appointment_status
357
428
  * - 过滤 new / completed(实时视角下视为非活跃数据)
@@ -462,12 +533,7 @@ var SalesImpl = class extends import_BaseModule.BaseModule {
462
533
  const resourceList = ((_a = resourceResponse == null ? void 0 : resourceResponse.data) == null ? void 0 : _a.list) ?? [];
463
534
  if (!Array.isArray(resourceList) || resourceList.length === 0)
464
535
  return [];
465
- let normalizedBookings = bookingList.map((booking) => this.normalizeMatchedBooking(current, deviceCurrent, booking)).filter((booking) => Boolean(booking)).sort((left, right) => {
466
- const leftStartAt = this.toBookingDateTime(left.start_date, left.start_time).valueOf();
467
- const rightStartAt = this.toBookingDateTime(right.start_date, right.start_time).valueOf();
468
- return leftStartAt - rightStartAt;
469
- });
470
- normalizedBookings = normalizedBookings.filter((booking) => booking.status);
536
+ const normalizedBookings = this.normalizeResourceBookings(current, deviceCurrent, bookingList);
471
537
  const bookingMap = /* @__PURE__ */ new Map();
472
538
  normalizedBookings.forEach((booking) => {
473
539
  if (!Array.isArray(booking.resources))
@@ -504,6 +570,73 @@ var SalesImpl = class extends import_BaseModule.BaseModule {
504
570
  });
505
571
  return list;
506
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
+ }
507
640
  };
508
641
  var Sales_default = SalesImpl;
509
642
  // Annotate the CommonJS export names for ESM import in node:
@@ -32,7 +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[];
40
+ export interface ChangeBookingStatusParams {
41
+ booking_id: number | string;
42
+ appointment_status: string;
43
+ }
44
+ export interface GetResourceBookingItemParams {
45
+ resourceId: ResourceId;
46
+ currentTime: string;
47
+ bookingList: BookingData[];
48
+ deviceTime?: string;
49
+ }
36
50
  /**
37
51
  * Sales 解决方案状态
38
52
  *
@@ -61,8 +75,14 @@ export interface SalesModuleAPI {
61
75
  setReservationList: (reservationList: Reservation[]) => Reservation[];
62
76
  /** 重置预约列表为空 */
63
77
  resetReservationList: () => Reservation[];
78
+ /** 取消订单 */
79
+ cancelOrder: (orderIds: Array<number | string>) => Promise<any>;
80
+ /** 变更预约状态 */
81
+ changeBookingStatus: (params: ChangeBookingStatusParams) => Promise<any>;
64
82
  /** 获取资源维度的预约占用列表 */
65
83
  getResourceBookingList: (currentTime: string, bookingList: BookingData[], deviceTime?: string) => Promise<SalesResourceBookingItem[]>;
84
+ /** 获取单个资源维度的预约占用、已完成、未支付列表 */
85
+ getResourceBookingItem: (params: GetResourceBookingItemParams) => Promise<SalesResourceBookingItemWithGroups | null>;
66
86
  /** 获取时间轴每个时间片的预约数量 */
67
87
  getTimelineHighlights: (bookingList: BookingData[], startDateTime?: string, endDateTime?: string) => SalesTimelineHighlights;
68
88
  }
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "private": false,
3
3
  "name": "@pisell/pisellos",
4
- "version": "2.2.135",
4
+ "version": "2.2.137",
5
5
  "description": "一个可扩展的前端模块化SDK框架,支持插件系统",
6
6
  "main": "dist/index.js",
7
7
  "types": "dist/index.d.ts",