@pisell/pisellos 1.0.56 → 1.0.58

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.
@@ -20,8 +20,9 @@ export declare class DiscountModule extends BaseModule implements Module, Discou
20
20
  with_discount_card: 0 | 1;
21
21
  customer_id: number;
22
22
  }): Promise<Discount[]>;
23
- batchSearch(code: string): Promise<Discount[]>;
23
+ batchSearch(code: string, customerId?: number): Promise<Discount[]>;
24
24
  filterEnabledDiscountList(discountList: Discount[]): Discount[];
25
+ private checkUsageCreditsLimit;
25
26
  uniqueByProductId(discountList: Discount[]): Discount[];
26
27
  filterDiscountListByProductIds(discountList: Discount[], productIds: number[]): Discount[];
27
28
  calcDiscountApplicableProductTotalPrice(discount: Discount): number | undefined;
@@ -145,7 +145,7 @@ export var DiscountModule = /*#__PURE__*/function (_BaseModule) {
145
145
  }, {
146
146
  key: "batchSearch",
147
147
  value: function () {
148
- var _batchSearch = _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime().mark(function _callee4(code) {
148
+ var _batchSearch = _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime().mark(function _callee4(code, customerId) {
149
149
  var result, resultDiscountList;
150
150
  return _regeneratorRuntime().wrap(function _callee4$(_context4) {
151
151
  while (1) switch (_context4.prev = _context4.next) {
@@ -156,7 +156,10 @@ export var DiscountModule = /*#__PURE__*/function (_BaseModule) {
156
156
  translate_flag: 1,
157
157
  tags: ['good_pass', 'product_discount_card'],
158
158
  available: 1,
159
- relation_product: 1
159
+ relation_product: 1,
160
+ with: ['extensionData'],
161
+ order_behavior_count: 1,
162
+ order_behavior_count_customer_id: customerId || 1
160
163
  });
161
164
  case 2:
162
165
  result = _context4.sent;
@@ -168,7 +171,7 @@ export var DiscountModule = /*#__PURE__*/function (_BaseModule) {
168
171
  }
169
172
  }, _callee4, this);
170
173
  }));
171
- function batchSearch(_x5) {
174
+ function batchSearch(_x5, _x6) {
172
175
  return _batchSearch.apply(this, arguments);
173
176
  }
174
177
  return batchSearch;
@@ -176,11 +179,71 @@ export var DiscountModule = /*#__PURE__*/function (_BaseModule) {
176
179
  }, {
177
180
  key: "filterEnabledDiscountList",
178
181
  value: function filterEnabledDiscountList(discountList) {
182
+ var _this2 = this;
179
183
  return discountList.filter(function (discount) {
180
- return discount.limit_status === 'enable' && new Decimal((discount === null || discount === void 0 ? void 0 : discount.par_value) || 0).minus(new Decimal((discount === null || discount === void 0 ? void 0 : discount.used_par_value) || 0)).greaterThan(0);
184
+ return discount.limit_status === 'enable' && new Decimal((discount === null || discount === void 0 ? void 0 : discount.par_value) || 0).minus(new Decimal((discount === null || discount === void 0 ? void 0 : discount.used_par_value) || 0)).greaterThan(0) && _this2.checkUsageCreditsLimit(discount);
181
185
  });
182
186
  }
183
187
 
188
+ // 检查使用次数限制
189
+ }, {
190
+ key: "checkUsageCreditsLimit",
191
+ value: function checkUsageCreditsLimit(discount) {
192
+ // 如果没有 extension_data,则不需要限制
193
+ if (!discount.extension_data || discount.extension_data.length === 0) {
194
+ return true;
195
+ }
196
+
197
+ // 查找 field_key 为 usage_credits 的数据
198
+ var usageCreditsData = discount.extension_data.find(function (data) {
199
+ return data.field_key === 'usage_credits';
200
+ });
201
+
202
+ // 如果没有找到 usage_credits 数据,则不需要限制
203
+ if (!usageCreditsData) {
204
+ return true;
205
+ }
206
+ var value = usageCreditsData.value;
207
+
208
+ // 检查总次数限制
209
+ if (value.total_credits && value.total_credits > 0) {
210
+ if ((discount.total_order_behavior_count || 0) >= value.total_credits) {
211
+ return false;
212
+ }
213
+ }
214
+
215
+ // 检查单用户限制
216
+ if (value.per_user_limit && value.per_user_limit > 0) {
217
+ if ((discount.customer_order_behavior_count || 0) >= value.per_user_limit) {
218
+ return false;
219
+ }
220
+ }
221
+
222
+ // 检查单日限制
223
+ if (value.max_per_day && value.max_per_day > 0) {
224
+ if ((discount.today_order_behavior_count || 0) >= value.max_per_day) {
225
+ return false;
226
+ }
227
+ }
228
+
229
+ // 检查单周限制
230
+ if (value.max_per_week && value.max_per_week > 0) {
231
+ if ((discount.week_order_behavior_count || 0) >= value.max_per_week) {
232
+ return false;
233
+ }
234
+ }
235
+
236
+ // 检查单月限制
237
+ if (value.max_per_month && value.max_per_month > 0) {
238
+ if ((discount.month_order_behavior_count || 0) >= value.max_per_month) {
239
+ return false;
240
+ }
241
+ }
242
+
243
+ // 所有检查都通过
244
+ return true;
245
+ }
246
+
184
247
  // 根据productIds去重
185
248
  }, {
186
249
  key: "uniqueByProductId",
@@ -36,6 +36,25 @@ interface ApplicableProductDetails {
36
36
  title?: string;
37
37
  };
38
38
  }
39
+ interface UsageCreditsValue {
40
+ total_credits: number;
41
+ per_user_limit: number;
42
+ max_per_day: number;
43
+ max_per_week: number;
44
+ max_per_month: number;
45
+ }
46
+ interface ExtensionData {
47
+ id: number;
48
+ shop_id: number;
49
+ product_id: number;
50
+ field_id: number;
51
+ field_key: string;
52
+ type: string;
53
+ value: UsageCreditsValue | Record<string, any> | string | number | boolean | any[];
54
+ created_at: string | null;
55
+ updated_at: string | null;
56
+ deleted_at: string | null;
57
+ }
39
58
  export interface Discount {
40
59
  id: number;
41
60
  product_name: string;
@@ -70,6 +89,12 @@ export interface Discount {
70
89
  appliedProductDetails: ApplicableProductDetails[];
71
90
  isDisabledForProductUsed?: boolean;
72
91
  amount?: number;
92
+ extension_data?: ExtensionData[];
93
+ total_order_behavior_count?: number;
94
+ today_order_behavior_count?: number;
95
+ week_order_behavior_count?: number;
96
+ month_order_behavior_count?: number;
97
+ customer_order_behavior_count?: number;
73
98
  }
74
99
  export interface DiscountState {
75
100
  discountList: Discount[];
@@ -1273,7 +1273,7 @@ export var BookingByStepImpl = /*#__PURE__*/function (_BaseModule) {
1273
1273
  // 如果此时 resources 为空,视作购物车里已经有了 dateRange 数据,此时 dateList 里明确就是那一天的数据
1274
1274
  if (!allOriginResources.length) {
1275
1275
  var dateList = this.store.date.getDateList();
1276
- dateList.forEach(function (n) {
1276
+ dateList === null || dateList === void 0 || dateList.forEach(function (n) {
1277
1277
  if (n.resource) allOriginResources.push.apply(allOriginResources, _toConsumableArray(n.resource));
1278
1278
  });
1279
1279
  }
@@ -95,71 +95,122 @@ export function findFastestAvailableResource(_ref2) {
95
95
  var currentTime = dayjs();
96
96
  var fastestTime = null;
97
97
  var fastestResources = [];
98
+ console.log('[TimeslotUtils] 查找最快可用资源:', {
99
+ currentTime: currentTime.format('YYYY-MM-DD HH:mm:ss'),
100
+ resourceCount: resources.length,
101
+ currentCapacity: currentCapacity,
102
+ countMap: countMap
103
+ });
98
104
 
99
105
  // 遍历所有资源,找到最快可用的时间点
100
106
  var _iterator2 = _createForOfIteratorHelper(resources),
101
107
  _step2;
102
108
  try {
103
109
  for (_iterator2.s(); !(_step2 = _iterator2.n()).done;) {
104
- var _resource = _step2.value;
105
- // 获取资源当天的时间段
106
- var todayTimes = _resource.times.filter(function (time) {
107
- return dayjs(time.start_at).isSame(currentTime, 'day');
108
- });
109
- if (todayTimes.length === 0) continue;
110
-
111
- // 按开始时间排序
112
- todayTimes.sort(function (a, b) {
113
- return dayjs(a.start_at).diff(dayjs(b.start_at));
110
+ var resource = _step2.value;
111
+ // 获取资源当天且还在工作时间内的时间段
112
+ var todayTimes = resource.times.filter(function (time) {
113
+ var isToday = dayjs(time.start_at).isSame(currentTime, 'day');
114
+ var isStillWorking = dayjs(time.end_at).isAfter(currentTime);
115
+ return isToday && isStillWorking;
114
116
  });
115
-
116
- // 找到第一个可用的时间段
117
+ if (todayTimes.length === 0) {
118
+ console.log("[TimeslotUtils] \u8D44\u6E90 ".concat(resource.id, "(").concat(resource.main_field, ") \u4ECA\u65E5\u65E0\u53EF\u7528\u65F6\u95F4\u6BB5"));
119
+ continue;
120
+ }
117
121
  var _iterator3 = _createForOfIteratorHelper(todayTimes),
118
122
  _step3;
119
123
  try {
120
124
  var _loop = function _loop() {
125
+ var _time$event_list, _time$event_list2;
121
126
  var time = _step3.value;
122
- var startTime = dayjs(time.start_at);
123
-
124
- // 如果开始时间在当前时间之前,跳过
125
- if (startTime.isBefore(currentTime)) return 0; // continue
126
-
127
- // 检查这个时间段是否可用
128
- if (_resource.resourceType === 'single') {
129
- var _time$event_list;
130
- // 单个预约类型:检查是否有预约
131
- var hasBooking = (_time$event_list = time.event_list) === null || _time$event_list === void 0 ? void 0 : _time$event_list.some(function (event) {
132
- return dayjs(event.start_at).isSame(startTime);
133
- });
134
- if (!hasBooking) {
135
- if (!fastestTime || startTime.isSame(fastestTime)) {
136
- fastestTime = startTime;
137
- fastestResources.push(_resource);
138
- } else if (startTime.isBefore(fastestTime)) {
139
- fastestTime = startTime;
140
- fastestResources = [_resource];
141
- }
142
- return 1; // break
143
- }
144
- } else {
145
- var _time$event_list2;
146
- // 多个预约类型:检查容量
147
- var totalCapacity = _resource.capacity || 0;
148
- var usedCapacity = ((_time$event_list2 = time.event_list) === null || _time$event_list2 === void 0 ? void 0 : _time$event_list2.reduce(function (sum, event) {
149
- return dayjs(event.start_at).isSame(startTime) ? sum + (event.pax || 0) : sum;
150
- }, 0)) || 0;
151
- var remainingCapacity = totalCapacity - usedCapacity;
152
- if (remainingCapacity >= (countMap[_resource.id] || 0) + currentCapacity) {
153
- if (!fastestTime || startTime.isSame(fastestTime)) {
154
- fastestTime = startTime;
155
- fastestResources.push(_resource);
156
- } else if (startTime.isBefore(fastestTime)) {
157
- fastestTime = startTime;
158
- fastestResources = [_resource];
127
+ var workStartTime = dayjs(time.start_at);
128
+ var workEndTime = dayjs(time.end_at);
129
+
130
+ // 确定检查的起始时间(当前时间 vs 工作开始时间)
131
+ var nextAvailableTime = currentTime.isBefore(workStartTime) ? workStartTime : currentTime;
132
+ console.log("[TimeslotUtils] \u68C0\u67E5\u8D44\u6E90 ".concat(resource.id, "(").concat(resource.main_field, "):"), {
133
+ workTime: "".concat(workStartTime.format('HH:mm'), "-").concat(workEndTime.format('HH:mm')),
134
+ checkStartTime: nextAvailableTime.format('HH:mm:ss'),
135
+ eventCount: ((_time$event_list = time.event_list) === null || _time$event_list === void 0 ? void 0 : _time$event_list.length) || 0
136
+ });
137
+
138
+ // 获取所有影响的预约事件(当前时间之后的)
139
+ var relevantEvents = ((_time$event_list2 = time.event_list) === null || _time$event_list2 === void 0 ? void 0 : _time$event_list2.filter(function (event) {
140
+ var eventEnd = dayjs(event.end_at);
141
+ return eventEnd.isAfter(nextAvailableTime);
142
+ })) || [];
143
+
144
+ // 按开始时间排序
145
+ relevantEvents.sort(function (a, b) {
146
+ return dayjs(a.start_at).diff(dayjs(b.start_at));
147
+ });
148
+
149
+ // 检查从 nextAvailableTime 开始的可用性
150
+ var finalAvailableTime = nextAvailableTime;
151
+ var _iterator4 = _createForOfIteratorHelper(relevantEvents),
152
+ _step4;
153
+ try {
154
+ for (_iterator4.s(); !(_step4 = _iterator4.n()).done;) {
155
+ var event = _step4.value;
156
+ var eventStart = dayjs(event.start_at);
157
+ var eventEnd = dayjs(event.end_at);
158
+ console.log("[TimeslotUtils] \u68C0\u67E5\u4E8B\u4EF6\u51B2\u7A81:", {
159
+ resourceId: resource.id,
160
+ eventTime: "".concat(eventStart.format('HH:mm'), "-").concat(eventEnd.format('HH:mm')),
161
+ checkTime: finalAvailableTime.format('HH:mm:ss'),
162
+ pax: event.pax || 1
163
+ });
164
+
165
+ // 检查资源类型和容量
166
+ if (resource.resourceType === 'single' || (resource.capacity || 1) === 1) {
167
+ // 单人预约资源:检查时间冲突
168
+ if (finalAvailableTime.isBefore(eventEnd) && eventStart.isBefore(finalAvailableTime.add(30, 'minute'))) {
169
+ // 有冲突,使用事件结束时间
170
+ finalAvailableTime = eventEnd;
171
+ console.log("[TimeslotUtils] \u53D1\u73B0\u65F6\u95F4\u51B2\u7A81\uFF0C\u8C03\u6574\u5230: ".concat(finalAvailableTime.format('HH:mm:ss')));
172
+ }
173
+ } else {
174
+ // 多人预约资源:检查容量
175
+ var totalCapacity = resource.capacity || 0;
176
+ var currentUsedCapacity = countMap[resource.id] || 0;
177
+ var eventUsedCapacity = event.pax || 1;
178
+
179
+ // 如果在事件时间范围内,检查容量是否足够
180
+ if (finalAvailableTime.isBefore(eventEnd) && eventStart.isBefore(finalAvailableTime.add(30, 'minute'))) {
181
+ var totalRequiredCapacity = currentUsedCapacity + currentCapacity + eventUsedCapacity;
182
+ if (totalRequiredCapacity > totalCapacity) {
183
+ // 容量不足,使用事件结束时间
184
+ finalAvailableTime = eventEnd;
185
+ console.log("[TimeslotUtils] \u5BB9\u91CF\u4E0D\u8DB3\uFF0C\u8C03\u6574\u5230: ".concat(finalAvailableTime.format('HH:mm:ss')));
186
+ }
187
+ }
159
188
  }
160
- return 1; // break
161
189
  }
190
+
191
+ // 确保可用时间在工作时间内
192
+ } catch (err) {
193
+ _iterator4.e(err);
194
+ } finally {
195
+ _iterator4.f();
196
+ }
197
+ if (finalAvailableTime.isAfter(workEndTime)) {
198
+ console.log("[TimeslotUtils] \u8D44\u6E90 ".concat(resource.id, " \u53EF\u7528\u65F6\u95F4\u8D85\u51FA\u5DE5\u4F5C\u65F6\u95F4\uFF0C\u8DF3\u8FC7"));
199
+ return 0; // continue
162
200
  }
201
+ console.log("[TimeslotUtils] \u8D44\u6E90 ".concat(resource.id, "(").concat(resource.main_field, ") \u6700\u5FEB\u53EF\u7528\u65F6\u95F4: ").concat(finalAvailableTime.format('HH:mm:ss')));
202
+
203
+ // 更新最快可用时间和资源
204
+ if (!fastestTime || finalAvailableTime.isBefore(fastestTime)) {
205
+ fastestTime = finalAvailableTime;
206
+ fastestResources = [resource];
207
+ console.log("[TimeslotUtils] \u66F4\u65B0\u6700\u5FEB\u65F6\u95F4: ".concat(fastestTime.format('HH:mm:ss'), ", \u8D44\u6E90: ").concat(resource.main_field));
208
+ } else if (finalAvailableTime.isSame(fastestTime)) {
209
+ fastestResources.push(resource);
210
+ console.log("[TimeslotUtils] \u6DFB\u52A0\u76F8\u540C\u65F6\u95F4\u8D44\u6E90: ".concat(resource.main_field));
211
+ }
212
+ return 1; // break
213
+ // 每个资源只需要计算一次
163
214
  },
164
215
  _ret;
165
216
  for (_iterator3.s(); !(_step3 = _iterator3.n()).done;) {
@@ -180,31 +231,22 @@ export function findFastestAvailableResource(_ref2) {
180
231
  } finally {
181
232
  _iterator2.f();
182
233
  }
183
- if (!fastestTime || fastestResources.length === 0) return null;
234
+ if (!fastestTime || fastestResources.length === 0) {
235
+ console.log('[TimeslotUtils] 未找到可用资源');
236
+ return null;
237
+ }
238
+ console.log("[TimeslotUtils] \u627E\u5230 ".concat(fastestResources.length, " \u4E2A\u6700\u5FEB\u53EF\u7528\u8D44\u6E90\uFF0C\u65F6\u95F4: ").concat(fastestTime.format('HH:mm:ss')));
184
239
 
185
240
  // 如果只有一个最快可用的资源,直接返回
186
- if (fastestResources.length === 1) return fastestResources[0];
187
-
188
- // 如果有多个最快可用的资源,比较它们的空闲时间
189
- var maxIdleTime = 0;
190
- var selectedResource = null;
191
- for (var _i2 = 0, _fastestResources = fastestResources; _i2 < _fastestResources.length; _i2++) {
192
- var resource = _fastestResources[_i2];
193
- var idleTime = calculateResourceAvailableTime({
194
- resource: resource,
195
- timeSlots: {
196
- start_time: fastestTime.format('HH:mm'),
197
- end_time: fastestTime.format('HH:mm'),
198
- start_at: fastestTime,
199
- end_at: fastestTime
200
- },
201
- currentCapacity: (countMap[resource.id] || 0) + currentCapacity
202
- });
203
- if (idleTime > maxIdleTime) {
204
- maxIdleTime = idleTime;
205
- selectedResource = resource;
206
- }
241
+ if (fastestResources.length === 1) {
242
+ console.log("[TimeslotUtils] \u8FD4\u56DE\u552F\u4E00\u6700\u5FEB\u8D44\u6E90: ".concat(fastestResources[0].main_field));
243
+ return fastestResources[0];
207
244
  }
245
+
246
+ // 如果有多个最快可用的资源,选择第一个或根据其他逻辑选择
247
+ // 这里简化处理,直接返回第一个
248
+ var selectedResource = fastestResources[0];
249
+ console.log("[TimeslotUtils] \u8FD4\u56DE\u591A\u4E2A\u8D44\u6E90\u4E2D\u7684\u7B2C\u4E00\u4E2A: ".concat(selectedResource.main_field));
208
250
  return selectedResource;
209
251
  }
210
252
 
@@ -111,7 +111,7 @@ export declare class BookingTicketImpl extends BaseModule implements Module {
111
111
  * 获取当前的客户搜索条件
112
112
  * @returns 当前搜索条件
113
113
  */
114
- getCurrentCustomerSearchParams(): Omit<import("../../modules").ShopGetCustomerListParams, "skip" | "num">;
114
+ getCurrentCustomerSearchParams(): Omit<import("../../modules").ShopGetCustomerListParams, "num" | "skip">;
115
115
  /**
116
116
  * 获取客户列表状态(包含滚动加载相关状态)
117
117
  * @returns 客户状态
@@ -34,7 +34,7 @@ export declare class ShopDiscountImpl extends BaseModule implements Module {
34
34
  discountList: Discount[];
35
35
  };
36
36
  setProductList(productList: Record<string, any>[]): void;
37
- scanCode(code: string): Promise<{
37
+ scanCode(code: string, customerId?: number): Promise<{
38
38
  isAvailable: boolean;
39
39
  productList: Record<string, any>[];
40
40
  discountList: Discount[];
@@ -305,14 +305,14 @@ export var ShopDiscountImpl = /*#__PURE__*/function (_BaseModule) {
305
305
  }, {
306
306
  key: "scanCode",
307
307
  value: function () {
308
- var _scanCode = _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime().mark(function _callee5(code) {
308
+ var _scanCode = _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime().mark(function _callee5(code, customerId) {
309
309
  var _this$store$discount3, resultDiscountList, rulesModule, withScanList, _ref3, newProductList, newDiscountList, isAvailable, _this$options$otherPa6;
310
310
  return _regeneratorRuntime().wrap(function _callee5$(_context5) {
311
311
  while (1) switch (_context5.prev = _context5.next) {
312
312
  case 0:
313
313
  _context5.prev = 0;
314
314
  _context5.next = 3;
315
- return (_this$store$discount3 = this.store.discount) === null || _this$store$discount3 === void 0 ? void 0 : _this$store$discount3.batchSearch(code);
315
+ return (_this$store$discount3 = this.store.discount) === null || _this$store$discount3 === void 0 ? void 0 : _this$store$discount3.batchSearch(code, customerId);
316
316
  case 3:
317
317
  _context5.t0 = _context5.sent;
318
318
  if (_context5.t0) {
@@ -394,7 +394,7 @@ export var ShopDiscountImpl = /*#__PURE__*/function (_BaseModule) {
394
394
  }
395
395
  }, _callee5, this, [[0, 23]]);
396
396
  }));
397
- function scanCode(_x4) {
397
+ function scanCode(_x4, _x5) {
398
398
  return _scanCode.apply(this, arguments);
399
399
  }
400
400
  return scanCode;
@@ -439,7 +439,7 @@ export var ShopDiscountImpl = /*#__PURE__*/function (_BaseModule) {
439
439
  }
440
440
  }, _callee6, this);
441
441
  }));
442
- function emitDiscountListChange(_x5) {
442
+ function emitDiscountListChange(_x6) {
443
443
  return _emitDiscountListChange.apply(this, arguments);
444
444
  }
445
445
  return emitDiscountListChange;
@@ -576,7 +576,7 @@ export var ShopDiscountImpl = /*#__PURE__*/function (_BaseModule) {
576
576
  }
577
577
  }, _callee7, this, [[0, 11]]);
578
578
  }));
579
- function getCustomerWallet(_x6) {
579
+ function getCustomerWallet(_x7) {
580
580
  return _getCustomerWallet.apply(this, arguments);
581
581
  }
582
582
  return getCustomerWallet;
@@ -636,7 +636,7 @@ export var ShopDiscountImpl = /*#__PURE__*/function (_BaseModule) {
636
636
  }
637
637
  }, _callee8, this, [[0, 18]]);
638
638
  }));
639
- function loadPrepareConfig(_x7) {
639
+ function loadPrepareConfig(_x8) {
640
640
  return _loadPrepareConfig.apply(this, arguments);
641
641
  }
642
642
  return loadPrepareConfig;
@@ -20,8 +20,9 @@ export declare class DiscountModule extends BaseModule implements Module, Discou
20
20
  with_discount_card: 0 | 1;
21
21
  customer_id: number;
22
22
  }): Promise<Discount[]>;
23
- batchSearch(code: string): Promise<Discount[]>;
23
+ batchSearch(code: string, customerId?: number): Promise<Discount[]>;
24
24
  filterEnabledDiscountList(discountList: Discount[]): Discount[];
25
+ private checkUsageCreditsLimit;
25
26
  uniqueByProductId(discountList: Discount[]): Discount[];
26
27
  filterDiscountListByProductIds(discountList: Discount[], productIds: number[]): Discount[];
27
28
  calcDiscountApplicableProductTotalPrice(discount: Discount): number | undefined;
@@ -89,22 +89,64 @@ var DiscountModule = class extends import_BaseModule.BaseModule {
89
89
  ) || [];
90
90
  return goodPassList;
91
91
  }
92
- async batchSearch(code) {
92
+ async batchSearch(code, customerId) {
93
93
  const result = await this.request.get(`/machinecode/batch-search`, {
94
94
  code,
95
95
  translate_flag: 1,
96
96
  tags: ["good_pass", "product_discount_card"],
97
97
  available: 1,
98
- relation_product: 1
98
+ relation_product: 1,
99
+ with: ["extensionData"],
100
+ order_behavior_count: 1,
101
+ order_behavior_count_customer_id: customerId || 1
99
102
  });
100
103
  const resultDiscountList = this.filterEnabledDiscountList((result == null ? void 0 : result.data) || []) || [];
101
104
  return resultDiscountList;
102
105
  }
103
106
  filterEnabledDiscountList(discountList) {
104
107
  return discountList.filter(
105
- (discount) => discount.limit_status === "enable" && new import_decimal.default((discount == null ? void 0 : discount.par_value) || 0).minus(new import_decimal.default((discount == null ? void 0 : discount.used_par_value) || 0)).greaterThan(0)
108
+ (discount) => discount.limit_status === "enable" && new import_decimal.default((discount == null ? void 0 : discount.par_value) || 0).minus(new import_decimal.default((discount == null ? void 0 : discount.used_par_value) || 0)).greaterThan(0) && this.checkUsageCreditsLimit(discount)
106
109
  );
107
110
  }
111
+ // 检查使用次数限制
112
+ checkUsageCreditsLimit(discount) {
113
+ if (!discount.extension_data || discount.extension_data.length === 0) {
114
+ return true;
115
+ }
116
+ const usageCreditsData = discount.extension_data.find(
117
+ (data) => data.field_key === "usage_credits"
118
+ );
119
+ if (!usageCreditsData) {
120
+ return true;
121
+ }
122
+ const value = usageCreditsData.value;
123
+ if (value.total_credits && value.total_credits > 0) {
124
+ if ((discount.total_order_behavior_count || 0) >= value.total_credits) {
125
+ return false;
126
+ }
127
+ }
128
+ if (value.per_user_limit && value.per_user_limit > 0) {
129
+ if ((discount.customer_order_behavior_count || 0) >= value.per_user_limit) {
130
+ return false;
131
+ }
132
+ }
133
+ if (value.max_per_day && value.max_per_day > 0) {
134
+ if ((discount.today_order_behavior_count || 0) >= value.max_per_day) {
135
+ return false;
136
+ }
137
+ }
138
+ if (value.max_per_week && value.max_per_week > 0) {
139
+ if ((discount.week_order_behavior_count || 0) >= value.max_per_week) {
140
+ return false;
141
+ }
142
+ }
143
+ if (value.max_per_month && value.max_per_month > 0) {
144
+ if ((discount.month_order_behavior_count || 0) >= value.max_per_month) {
145
+ return false;
146
+ }
147
+ }
148
+ return true;
149
+ }
108
150
  // 根据productIds去重
109
151
  uniqueByProductId(discountList) {
110
152
  return (0, import_utils.uniqueById)(discountList, "product_id");
@@ -36,6 +36,25 @@ interface ApplicableProductDetails {
36
36
  title?: string;
37
37
  };
38
38
  }
39
+ interface UsageCreditsValue {
40
+ total_credits: number;
41
+ per_user_limit: number;
42
+ max_per_day: number;
43
+ max_per_week: number;
44
+ max_per_month: number;
45
+ }
46
+ interface ExtensionData {
47
+ id: number;
48
+ shop_id: number;
49
+ product_id: number;
50
+ field_id: number;
51
+ field_key: string;
52
+ type: string;
53
+ value: UsageCreditsValue | Record<string, any> | string | number | boolean | any[];
54
+ created_at: string | null;
55
+ updated_at: string | null;
56
+ deleted_at: string | null;
57
+ }
39
58
  export interface Discount {
40
59
  id: number;
41
60
  product_name: string;
@@ -70,6 +89,12 @@ export interface Discount {
70
89
  appliedProductDetails: ApplicableProductDetails[];
71
90
  isDisabledForProductUsed?: boolean;
72
91
  amount?: number;
92
+ extension_data?: ExtensionData[];
93
+ total_order_behavior_count?: number;
94
+ today_order_behavior_count?: number;
95
+ week_order_behavior_count?: number;
96
+ month_order_behavior_count?: number;
97
+ customer_order_behavior_count?: number;
73
98
  }
74
99
  export interface DiscountState {
75
100
  discountList: Discount[];
@@ -694,7 +694,7 @@ var BookingByStepImpl = class extends import_BaseModule.BaseModule {
694
694
  }
695
695
  if (!allOriginResources.length) {
696
696
  const dateList = this.store.date.getDateList();
697
- dateList.forEach((n) => {
697
+ dateList == null ? void 0 : dateList.forEach((n) => {
698
698
  if (n.resource)
699
699
  allOriginResources.push(...n.resource);
700
700
  });
@@ -83,74 +83,93 @@ function findFastestAvailableResource({
83
83
  const currentTime = (0, import_dayjs.default)();
84
84
  let fastestTime = null;
85
85
  let fastestResources = [];
86
+ console.log("[TimeslotUtils] 查找最快可用资源:", {
87
+ currentTime: currentTime.format("YYYY-MM-DD HH:mm:ss"),
88
+ resourceCount: resources.length,
89
+ currentCapacity,
90
+ countMap
91
+ });
86
92
  for (const resource of resources) {
87
93
  const todayTimes = resource.times.filter((time) => {
88
- return (0, import_dayjs.default)(time.start_at).isSame(currentTime, "day");
94
+ const isToday = (0, import_dayjs.default)(time.start_at).isSame(currentTime, "day");
95
+ const isStillWorking = (0, import_dayjs.default)(time.end_at).isAfter(currentTime);
96
+ return isToday && isStillWorking;
89
97
  });
90
- if (todayTimes.length === 0)
98
+ if (todayTimes.length === 0) {
99
+ console.log(`[TimeslotUtils] 资源 ${resource.id}(${resource.main_field}) 今日无可用时间段`);
91
100
  continue;
92
- todayTimes.sort((a, b) => {
93
- return (0, import_dayjs.default)(a.start_at).diff((0, import_dayjs.default)(b.start_at));
94
- });
101
+ }
95
102
  for (const time of todayTimes) {
96
- const startTime = (0, import_dayjs.default)(time.start_at);
97
- if (startTime.isBefore(currentTime))
98
- continue;
99
- if (resource.resourceType === "single") {
100
- const hasBooking = (_a = time.event_list) == null ? void 0 : _a.some((event) => {
101
- return (0, import_dayjs.default)(event.start_at).isSame(startTime);
103
+ const workStartTime = (0, import_dayjs.default)(time.start_at);
104
+ const workEndTime = (0, import_dayjs.default)(time.end_at);
105
+ let nextAvailableTime = currentTime.isBefore(workStartTime) ? workStartTime : currentTime;
106
+ console.log(`[TimeslotUtils] 检查资源 ${resource.id}(${resource.main_field}):`, {
107
+ workTime: `${workStartTime.format("HH:mm")}-${workEndTime.format("HH:mm")}`,
108
+ checkStartTime: nextAvailableTime.format("HH:mm:ss"),
109
+ eventCount: ((_a = time.event_list) == null ? void 0 : _a.length) || 0
110
+ });
111
+ const relevantEvents = ((_b = time.event_list) == null ? void 0 : _b.filter((event) => {
112
+ const eventEnd = (0, import_dayjs.default)(event.end_at);
113
+ return eventEnd.isAfter(nextAvailableTime);
114
+ })) || [];
115
+ relevantEvents.sort(
116
+ (a, b) => (0, import_dayjs.default)(a.start_at).diff((0, import_dayjs.default)(b.start_at))
117
+ );
118
+ let finalAvailableTime = nextAvailableTime;
119
+ for (const event of relevantEvents) {
120
+ const eventStart = (0, import_dayjs.default)(event.start_at);
121
+ const eventEnd = (0, import_dayjs.default)(event.end_at);
122
+ console.log(`[TimeslotUtils] 检查事件冲突:`, {
123
+ resourceId: resource.id,
124
+ eventTime: `${eventStart.format("HH:mm")}-${eventEnd.format("HH:mm")}`,
125
+ checkTime: finalAvailableTime.format("HH:mm:ss"),
126
+ pax: event.pax || 1
102
127
  });
103
- if (!hasBooking) {
104
- if (!fastestTime || startTime.isSame(fastestTime)) {
105
- fastestTime = startTime;
106
- fastestResources.push(resource);
107
- } else if (startTime.isBefore(fastestTime)) {
108
- fastestTime = startTime;
109
- fastestResources = [resource];
128
+ if (resource.resourceType === "single" || (resource.capacity || 1) === 1) {
129
+ if (finalAvailableTime.isBefore(eventEnd) && eventStart.isBefore(finalAvailableTime.add(30, "minute"))) {
130
+ finalAvailableTime = eventEnd;
131
+ console.log(`[TimeslotUtils] 发现时间冲突,调整到: ${finalAvailableTime.format("HH:mm:ss")}`);
110
132
  }
111
- break;
112
- }
113
- } else {
114
- const totalCapacity = resource.capacity || 0;
115
- const usedCapacity = ((_b = time.event_list) == null ? void 0 : _b.reduce((sum, event) => {
116
- return (0, import_dayjs.default)(event.start_at).isSame(startTime) ? sum + (event.pax || 0) : sum;
117
- }, 0)) || 0;
118
- const remainingCapacity = totalCapacity - usedCapacity;
119
- if (remainingCapacity >= (countMap[resource.id] || 0) + currentCapacity) {
120
- if (!fastestTime || startTime.isSame(fastestTime)) {
121
- fastestTime = startTime;
122
- fastestResources.push(resource);
123
- } else if (startTime.isBefore(fastestTime)) {
124
- fastestTime = startTime;
125
- fastestResources = [resource];
133
+ } else {
134
+ const totalCapacity = resource.capacity || 0;
135
+ const currentUsedCapacity = countMap[resource.id] || 0;
136
+ const eventUsedCapacity = event.pax || 1;
137
+ if (finalAvailableTime.isBefore(eventEnd) && eventStart.isBefore(finalAvailableTime.add(30, "minute"))) {
138
+ const totalRequiredCapacity = currentUsedCapacity + currentCapacity + eventUsedCapacity;
139
+ if (totalRequiredCapacity > totalCapacity) {
140
+ finalAvailableTime = eventEnd;
141
+ console.log(`[TimeslotUtils] 容量不足,调整到: ${finalAvailableTime.format("HH:mm:ss")}`);
142
+ }
126
143
  }
127
- break;
128
144
  }
129
145
  }
146
+ if (finalAvailableTime.isAfter(workEndTime)) {
147
+ console.log(`[TimeslotUtils] 资源 ${resource.id} 可用时间超出工作时间,跳过`);
148
+ continue;
149
+ }
150
+ console.log(`[TimeslotUtils] 资源 ${resource.id}(${resource.main_field}) 最快可用时间: ${finalAvailableTime.format("HH:mm:ss")}`);
151
+ if (!fastestTime || finalAvailableTime.isBefore(fastestTime)) {
152
+ fastestTime = finalAvailableTime;
153
+ fastestResources = [resource];
154
+ console.log(`[TimeslotUtils] 更新最快时间: ${fastestTime.format("HH:mm:ss")}, 资源: ${resource.main_field}`);
155
+ } else if (finalAvailableTime.isSame(fastestTime)) {
156
+ fastestResources.push(resource);
157
+ console.log(`[TimeslotUtils] 添加相同时间资源: ${resource.main_field}`);
158
+ }
159
+ break;
130
160
  }
131
161
  }
132
- if (!fastestTime || fastestResources.length === 0)
162
+ if (!fastestTime || fastestResources.length === 0) {
163
+ console.log("[TimeslotUtils] 未找到可用资源");
133
164
  return null;
134
- if (fastestResources.length === 1)
165
+ }
166
+ console.log(`[TimeslotUtils] 找到 ${fastestResources.length} 个最快可用资源,时间: ${fastestTime.format("HH:mm:ss")}`);
167
+ if (fastestResources.length === 1) {
168
+ console.log(`[TimeslotUtils] 返回唯一最快资源: ${fastestResources[0].main_field}`);
135
169
  return fastestResources[0];
136
- let maxIdleTime = 0;
137
- let selectedResource = null;
138
- for (const resource of fastestResources) {
139
- const idleTime = calculateResourceAvailableTime({
140
- resource,
141
- timeSlots: {
142
- start_time: fastestTime.format("HH:mm"),
143
- end_time: fastestTime.format("HH:mm"),
144
- start_at: fastestTime,
145
- end_at: fastestTime
146
- },
147
- currentCapacity: (countMap[resource.id] || 0) + currentCapacity
148
- });
149
- if (idleTime > maxIdleTime) {
150
- maxIdleTime = idleTime;
151
- selectedResource = resource;
152
- }
153
170
  }
171
+ const selectedResource = fastestResources[0];
172
+ console.log(`[TimeslotUtils] 返回多个资源中的第一个: ${selectedResource.main_field}`);
154
173
  return selectedResource;
155
174
  }
156
175
  function filterConditionTimeSlots(times, startTime, endTime) {
@@ -111,7 +111,7 @@ export declare class BookingTicketImpl extends BaseModule implements Module {
111
111
  * 获取当前的客户搜索条件
112
112
  * @returns 当前搜索条件
113
113
  */
114
- getCurrentCustomerSearchParams(): Omit<import("../../modules").ShopGetCustomerListParams, "skip" | "num">;
114
+ getCurrentCustomerSearchParams(): Omit<import("../../modules").ShopGetCustomerListParams, "num" | "skip">;
115
115
  /**
116
116
  * 获取客户列表状态(包含滚动加载相关状态)
117
117
  * @returns 客户状态
@@ -34,7 +34,7 @@ export declare class ShopDiscountImpl extends BaseModule implements Module {
34
34
  discountList: Discount[];
35
35
  };
36
36
  setProductList(productList: Record<string, any>[]): void;
37
- scanCode(code: string): Promise<{
37
+ scanCode(code: string, customerId?: number): Promise<{
38
38
  isAvailable: boolean;
39
39
  productList: Record<string, any>[];
40
40
  discountList: Discount[];
@@ -199,10 +199,10 @@ var ShopDiscountImpl = class extends import_BaseModule.BaseModule {
199
199
  this.store.productList = productList;
200
200
  }
201
201
  // 扫码输入code
202
- async scanCode(code) {
202
+ async scanCode(code, customerId) {
203
203
  var _a, _b;
204
204
  try {
205
- const resultDiscountList = await ((_a = this.store.discount) == null ? void 0 : _a.batchSearch(code)) || [];
205
+ const resultDiscountList = await ((_a = this.store.discount) == null ? void 0 : _a.batchSearch(code, customerId)) || [];
206
206
  const rulesModule = this.store.rules;
207
207
  if (!rulesModule) {
208
208
  return {
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "private": false,
3
3
  "name": "@pisell/pisellos",
4
- "version": "1.0.56",
4
+ "version": "1.0.58",
5
5
  "description": "一个可扩展的前端模块化SDK框架,支持插件系统",
6
6
  "main": "dist/index.js",
7
7
  "types": "dist/index.d.ts",