@pisell/pisellos 0.0.270 → 0.0.272
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/modules/Payment/index.js +42 -79
- package/dist/modules/Rules/index.js +2 -0
- package/dist/solution/BookingByStep/index.d.ts +9 -4
- package/dist/solution/BookingByStep/index.js +362 -106
- package/dist/solution/BookingByStep/utils/capacity.d.ts +23 -0
- package/dist/solution/BookingByStep/utils/capacity.js +189 -1
- package/dist/solution/BookingByStep/utils/stock.d.ts +29 -0
- package/dist/solution/BookingByStep/utils/stock.js +126 -0
- package/dist/solution/Checkout/index.js +36 -16
- package/lib/modules/Payment/index.js +0 -30
- package/lib/modules/Rules/index.js +2 -0
- package/lib/solution/BookingByStep/index.d.ts +9 -4
- package/lib/solution/BookingByStep/index.js +185 -8
- package/lib/solution/BookingByStep/utils/capacity.d.ts +23 -0
- package/lib/solution/BookingByStep/utils/capacity.js +136 -0
- package/lib/solution/BookingByStep/utils/stock.d.ts +29 -0
- package/lib/solution/BookingByStep/utils/stock.js +89 -0
- package/lib/solution/Checkout/index.js +14 -0
- package/package.json +1 -1
|
@@ -45,3 +45,26 @@ export declare const checkSubResourcesCapacity: (resource: ResourceItem) => void
|
|
|
45
45
|
* @returns 如果资源可以容纳额外的容量则返回 true
|
|
46
46
|
*/
|
|
47
47
|
export declare const checkResourceCanUseByCapacity: (currentCapacity: number, requiredCapacity: number, maxCapacity: number) => boolean;
|
|
48
|
+
/**
|
|
49
|
+
* 计算按资源类型分组的容量占用情况
|
|
50
|
+
*
|
|
51
|
+
* @param {CartItem[]} cartItems 购物车商品列表
|
|
52
|
+
* @param {string} timeSlotStart 时间段开始时间
|
|
53
|
+
* @param {string} timeSlotEnd 时间段结束时间
|
|
54
|
+
* @param {ResourceItem[]} allProductResources 当前商品的所有资源列表
|
|
55
|
+
* @return {Record<string, number>} 返回每种资源类型(form_id)的容量占用
|
|
56
|
+
*/
|
|
57
|
+
export declare function calculateCartItemsCapacityUsageByResourceType({ cartItems, timeSlotStart, timeSlotEnd, allProductResources }: {
|
|
58
|
+
cartItems: CartItem[];
|
|
59
|
+
timeSlotStart: string;
|
|
60
|
+
timeSlotEnd: string;
|
|
61
|
+
allProductResources: ResourceItem[];
|
|
62
|
+
}): Record<string, number>;
|
|
63
|
+
/**
|
|
64
|
+
* 获取商品的资源ID列表
|
|
65
|
+
*/
|
|
66
|
+
export declare function getResourcesIdsByProduct(product: any): number[];
|
|
67
|
+
/**
|
|
68
|
+
* 检查特定时间段的容量是否足够
|
|
69
|
+
*/
|
|
70
|
+
export declare function checkTimeSlotCapacity(timeSlotStart: string, timeSlotEnd: string, cartItems: CartItem[], allResources: ResourceItem[]): boolean;
|
|
@@ -1,6 +1,16 @@
|
|
|
1
|
+
function _slicedToArray(arr, i) { return _arrayWithHoles(arr) || _iterableToArrayLimit(arr, i) || _unsupportedIterableToArray(arr, i) || _nonIterableRest(); }
|
|
2
|
+
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
|
+
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; } }
|
|
4
|
+
function _arrayWithHoles(arr) { if (Array.isArray(arr)) return arr; }
|
|
5
|
+
function _toConsumableArray(arr) { return _arrayWithoutHoles(arr) || _iterableToArray(arr) || _unsupportedIterableToArray(arr) || _nonIterableSpread(); }
|
|
6
|
+
function _nonIterableSpread() { throw new TypeError("Invalid attempt to spread non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); }
|
|
7
|
+
function _iterableToArray(iter) { if (typeof Symbol !== "undefined" && iter[Symbol.iterator] != null || iter["@@iterator"] != null) return Array.from(iter); }
|
|
8
|
+
function _arrayWithoutHoles(arr) { if (Array.isArray(arr)) return _arrayLikeToArray(arr); }
|
|
1
9
|
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; } } }; }
|
|
2
10
|
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); }
|
|
3
11
|
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; }
|
|
12
|
+
import dayjs from 'dayjs';
|
|
13
|
+
|
|
4
14
|
/**
|
|
5
15
|
* @title: 基于选择的商品格式化容量
|
|
6
16
|
* @description:
|
|
@@ -129,4 +139,182 @@ export var checkResourceCanUseByCapacity = function checkResourceCanUseByCapacit
|
|
|
129
139
|
return false;
|
|
130
140
|
}
|
|
131
141
|
return currentCapacity + requiredCapacity <= maxCapacity;
|
|
132
|
-
};
|
|
142
|
+
};
|
|
143
|
+
|
|
144
|
+
/**
|
|
145
|
+
* 计算按资源类型分组的容量占用情况
|
|
146
|
+
*
|
|
147
|
+
* @param {CartItem[]} cartItems 购物车商品列表
|
|
148
|
+
* @param {string} timeSlotStart 时间段开始时间
|
|
149
|
+
* @param {string} timeSlotEnd 时间段结束时间
|
|
150
|
+
* @param {ResourceItem[]} allProductResources 当前商品的所有资源列表
|
|
151
|
+
* @return {Record<string, number>} 返回每种资源类型(form_id)的容量占用
|
|
152
|
+
*/
|
|
153
|
+
export function calculateCartItemsCapacityUsageByResourceType(_ref3) {
|
|
154
|
+
var cartItems = _ref3.cartItems,
|
|
155
|
+
timeSlotStart = _ref3.timeSlotStart,
|
|
156
|
+
timeSlotEnd = _ref3.timeSlotEnd,
|
|
157
|
+
allProductResources = _ref3.allProductResources;
|
|
158
|
+
// 按 form_id 分组当前商品的资源
|
|
159
|
+
var resourceTypeMap = {};
|
|
160
|
+
allProductResources.forEach(function (resource) {
|
|
161
|
+
var _resource$form_id;
|
|
162
|
+
var formId = ((_resource$form_id = resource.form_id) === null || _resource$form_id === void 0 ? void 0 : _resource$form_id.toString()) || 'default';
|
|
163
|
+
if (!resourceTypeMap[formId]) {
|
|
164
|
+
resourceTypeMap[formId] = [];
|
|
165
|
+
}
|
|
166
|
+
resourceTypeMap[formId].push(resource);
|
|
167
|
+
});
|
|
168
|
+
|
|
169
|
+
// 计算每种资源类型的容量占用
|
|
170
|
+
var capacityUsageByType = {};
|
|
171
|
+
Object.keys(resourceTypeMap).forEach(function (formId) {
|
|
172
|
+
var totalUsage = 0;
|
|
173
|
+
var resourcesInThisType = resourceTypeMap[formId];
|
|
174
|
+
var resourceIdsInThisType = resourcesInThisType.map(function (r) {
|
|
175
|
+
return r.id;
|
|
176
|
+
});
|
|
177
|
+
cartItems.forEach(function (cartItem) {
|
|
178
|
+
// 检查该商品是否已经选定了时间
|
|
179
|
+
if (!cartItem.start_time || !cartItem.end_time) return;
|
|
180
|
+
|
|
181
|
+
// 构建该商品的时间段
|
|
182
|
+
var itemStart = "".concat(cartItem.start_date, " ").concat(cartItem.start_time);
|
|
183
|
+
var itemEnd = "".concat(cartItem.end_date || cartItem.start_date, " ").concat(cartItem.end_time);
|
|
184
|
+
|
|
185
|
+
// 检查时间段是否有重叠
|
|
186
|
+
var hasTimeOverlap = !(dayjs(itemEnd).isBefore(dayjs(timeSlotStart)) || dayjs(itemStart).isAfter(dayjs(timeSlotEnd)));
|
|
187
|
+
if (!hasTimeOverlap) return;
|
|
188
|
+
|
|
189
|
+
// 检查该商品的资源配置中是否需要这种类型的资源
|
|
190
|
+
// 由于购物车商品只选定了时间,没有选定具体资源,我们需要看商品的资源配置
|
|
191
|
+
var productResourceIds = getResourcesIdsByProduct(cartItem._productOrigin);
|
|
192
|
+
var hasResourceTypeOverlap = productResourceIds.some(function (id) {
|
|
193
|
+
return resourceIdsInThisType.includes(id);
|
|
194
|
+
});
|
|
195
|
+
if (!hasResourceTypeOverlap) return;
|
|
196
|
+
|
|
197
|
+
// 计算该商品的容量占用
|
|
198
|
+
var _getCapacityInfoByCar = getCapacityInfoByCartItem(cartItem),
|
|
199
|
+
currentCapacity = _getCapacityInfoByCar.currentCapacity;
|
|
200
|
+
totalUsage += currentCapacity;
|
|
201
|
+
});
|
|
202
|
+
capacityUsageByType[formId] = totalUsage;
|
|
203
|
+
});
|
|
204
|
+
return capacityUsageByType;
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
/**
|
|
208
|
+
* 获取商品的资源ID列表
|
|
209
|
+
*/
|
|
210
|
+
export function getResourcesIdsByProduct(product) {
|
|
211
|
+
var _product$product_reso, _product$product_reso2;
|
|
212
|
+
var tempResourceIds = [];
|
|
213
|
+
product === null || product === void 0 || (_product$product_reso = product.product_resource) === null || _product$product_reso === void 0 || (_product$product_reso = _product$product_reso.resources) === null || _product$product_reso === void 0 || (_product$product_reso2 = _product$product_reso.forEach) === null || _product$product_reso2 === void 0 || _product$product_reso2.call(_product$product_reso, function (resource) {
|
|
214
|
+
if ((resource === null || resource === void 0 ? void 0 : resource.status) == 1) {
|
|
215
|
+
var _resource$default_res, _resource$optional_re;
|
|
216
|
+
if (resource !== null && resource !== void 0 && (_resource$default_res = resource.default_resource) !== null && _resource$default_res !== void 0 && _resource$default_res.length) {
|
|
217
|
+
tempResourceIds.push.apply(tempResourceIds, _toConsumableArray(resource === null || resource === void 0 ? void 0 : resource.default_resource));
|
|
218
|
+
} else if (resource !== null && resource !== void 0 && (_resource$optional_re = resource.optional_resource) !== null && _resource$optional_re !== void 0 && _resource$optional_re.length) {
|
|
219
|
+
tempResourceIds.push.apply(tempResourceIds, _toConsumableArray(resource === null || resource === void 0 ? void 0 : resource.optional_resource));
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
});
|
|
223
|
+
return tempResourceIds;
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
/**
|
|
227
|
+
* 检查特定时间段的容量是否足够
|
|
228
|
+
*/
|
|
229
|
+
export function checkTimeSlotCapacity(timeSlotStart, timeSlotEnd, cartItems, allResources) {
|
|
230
|
+
// 按资源类型分组
|
|
231
|
+
var resourceTypeMap = {};
|
|
232
|
+
allResources.forEach(function (resource) {
|
|
233
|
+
var _resource$form_id2;
|
|
234
|
+
var formId = ((_resource$form_id2 = resource.form_id) === null || _resource$form_id2 === void 0 ? void 0 : _resource$form_id2.toString()) || 'default';
|
|
235
|
+
if (!resourceTypeMap[formId]) {
|
|
236
|
+
resourceTypeMap[formId] = [];
|
|
237
|
+
}
|
|
238
|
+
resourceTypeMap[formId].push(resource);
|
|
239
|
+
});
|
|
240
|
+
|
|
241
|
+
// 计算每种资源类型需要的总容量
|
|
242
|
+
var requiredCapacityByType = {};
|
|
243
|
+
cartItems.forEach(function (cartItem) {
|
|
244
|
+
var productResourceIds = getResourcesIdsByProduct(cartItem._productOrigin);
|
|
245
|
+
var _getCapacityInfoByCar2 = getCapacityInfoByCartItem(cartItem),
|
|
246
|
+
currentCapacity = _getCapacityInfoByCar2.currentCapacity;
|
|
247
|
+
Object.keys(resourceTypeMap).forEach(function (formId) {
|
|
248
|
+
var resourcesInType = resourceTypeMap[formId];
|
|
249
|
+
var resourceIdsInType = resourcesInType.map(function (r) {
|
|
250
|
+
return r.id;
|
|
251
|
+
});
|
|
252
|
+
|
|
253
|
+
// 检查该商品是否需要这种类型的资源
|
|
254
|
+
var needsThisResourceType = productResourceIds.some(function (id) {
|
|
255
|
+
return resourceIdsInType.includes(id);
|
|
256
|
+
});
|
|
257
|
+
if (needsThisResourceType) {
|
|
258
|
+
requiredCapacityByType[formId] = (requiredCapacityByType[formId] || 0) + currentCapacity;
|
|
259
|
+
}
|
|
260
|
+
});
|
|
261
|
+
});
|
|
262
|
+
|
|
263
|
+
// 检查每种资源类型是否有足够的容量
|
|
264
|
+
var _loop = function _loop() {
|
|
265
|
+
var _Object$entries$_i = _slicedToArray(_Object$entries[_i], 2),
|
|
266
|
+
formId = _Object$entries$_i[0],
|
|
267
|
+
requiredCapacity = _Object$entries$_i[1];
|
|
268
|
+
var resourcesInType = resourceTypeMap[formId];
|
|
269
|
+
if (resourcesInType.length === 0) return 0; // continue
|
|
270
|
+
|
|
271
|
+
// 确定这种资源类型的预约类型
|
|
272
|
+
var firstResource = resourcesInType[0];
|
|
273
|
+
var isMultipleBooking = firstResource.resourceType === 'multiple';
|
|
274
|
+
if (isMultipleBooking) {
|
|
275
|
+
// 多个预约:计算总可用容量
|
|
276
|
+
var totalAvailableCapacity = 0;
|
|
277
|
+
resourcesInType.forEach(function (resource) {
|
|
278
|
+
// 过滤出在时间段内的资源时间片
|
|
279
|
+
var availableTimes = resource.times.filter(function (time) {
|
|
280
|
+
return !dayjs(time.start_at).isAfter(dayjs(timeSlotStart), 'minute') && !dayjs(time.end_at).isBefore(dayjs(timeSlotEnd), 'minute') || dayjs(time.start_at).isBefore(dayjs(timeSlotEnd), 'minute') && dayjs(time.end_at).isAfter(dayjs(timeSlotStart), 'minute');
|
|
281
|
+
});
|
|
282
|
+
if (availableTimes.length > 0) {
|
|
283
|
+
// 简化逻辑:如果资源在时间段内有可用时间,就计算其容量
|
|
284
|
+
totalAvailableCapacity += resource.capacity || 0;
|
|
285
|
+
}
|
|
286
|
+
});
|
|
287
|
+
if (totalAvailableCapacity < requiredCapacity) {
|
|
288
|
+
console.log("\u8D44\u6E90\u7C7B\u578B ".concat(formId, " \u5BB9\u91CF\u4E0D\u8DB3: \u9700\u8981 ").concat(requiredCapacity, ", \u53EF\u7528 ").concat(totalAvailableCapacity));
|
|
289
|
+
return {
|
|
290
|
+
v: false
|
|
291
|
+
};
|
|
292
|
+
}
|
|
293
|
+
} else {
|
|
294
|
+
// 单个预约:计算可用资源数量
|
|
295
|
+
var availableResourceCount = 0;
|
|
296
|
+
resourcesInType.forEach(function (resource) {
|
|
297
|
+
// 过滤出在时间段内的资源时间片
|
|
298
|
+
var availableTimes = resource.times.filter(function (time) {
|
|
299
|
+
return !dayjs(time.start_at).isAfter(dayjs(timeSlotStart), 'minute') && !dayjs(time.end_at).isBefore(dayjs(timeSlotEnd), 'minute') || dayjs(time.start_at).isBefore(dayjs(timeSlotEnd), 'minute') && dayjs(time.end_at).isAfter(dayjs(timeSlotStart), 'minute');
|
|
300
|
+
});
|
|
301
|
+
if (availableTimes.length > 0) {
|
|
302
|
+
availableResourceCount++;
|
|
303
|
+
}
|
|
304
|
+
});
|
|
305
|
+
if (availableResourceCount < requiredCapacity) {
|
|
306
|
+
console.log("\u8D44\u6E90\u7C7B\u578B ".concat(formId, " \u6570\u91CF\u4E0D\u8DB3: \u9700\u8981 ").concat(requiredCapacity, ", \u53EF\u7528 ").concat(availableResourceCount));
|
|
307
|
+
return {
|
|
308
|
+
v: false
|
|
309
|
+
};
|
|
310
|
+
}
|
|
311
|
+
}
|
|
312
|
+
},
|
|
313
|
+
_ret;
|
|
314
|
+
for (var _i = 0, _Object$entries = Object.entries(requiredCapacityByType); _i < _Object$entries.length; _i++) {
|
|
315
|
+
_ret = _loop();
|
|
316
|
+
if (_ret === 0) continue;
|
|
317
|
+
if (_ret) return _ret.v;
|
|
318
|
+
}
|
|
319
|
+
return true;
|
|
320
|
+
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { CartItem } from '../../../modules';
|
|
2
|
+
/**
|
|
3
|
+
* 检测商品库存是否足够
|
|
4
|
+
*
|
|
5
|
+
* 只有同时满足以下条件时才会进行库存检测:
|
|
6
|
+
* - is_track 开启(值为 1 或 true)
|
|
7
|
+
* - over_sold 为 0(不允许超卖)
|
|
8
|
+
*
|
|
9
|
+
* 对于多规格商品:
|
|
10
|
+
* - 如果有 product_variant_id,则从 productData.variant 数组中查找对应规格
|
|
11
|
+
* - 使用规格的 is_track, over_sold, stock_quantity 而不是主商品的
|
|
12
|
+
*
|
|
13
|
+
* @param productData 商品数据(需包含 is_track, over_sold, stock_quantity 字段,多规格商品需包含 variant 数组)
|
|
14
|
+
* @param product_variant_id 商品变体ID,如果存在则为多规格商品
|
|
15
|
+
* @param quantity 需要添加的数量
|
|
16
|
+
* @param bundle 套餐配置(子商品需包含 is_track, over_sold, stock_quantity 字段)
|
|
17
|
+
* @param currentCartItems 当前购物车商品列表
|
|
18
|
+
* @returns 库存检测结果
|
|
19
|
+
*/
|
|
20
|
+
export declare function checkProductStock({ productData, product_variant_id, quantity, bundle, currentCartItems }: {
|
|
21
|
+
productData: any;
|
|
22
|
+
product_variant_id?: any;
|
|
23
|
+
quantity: number;
|
|
24
|
+
bundle?: any[];
|
|
25
|
+
currentCartItems: CartItem[];
|
|
26
|
+
}): {
|
|
27
|
+
success: boolean;
|
|
28
|
+
errorCode?: string;
|
|
29
|
+
};
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
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; } } }; }
|
|
2
|
+
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); }
|
|
3
|
+
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; }
|
|
4
|
+
/**
|
|
5
|
+
* 检测商品库存是否足够
|
|
6
|
+
*
|
|
7
|
+
* 只有同时满足以下条件时才会进行库存检测:
|
|
8
|
+
* - is_track 开启(值为 1 或 true)
|
|
9
|
+
* - over_sold 为 0(不允许超卖)
|
|
10
|
+
*
|
|
11
|
+
* 对于多规格商品:
|
|
12
|
+
* - 如果有 product_variant_id,则从 productData.variant 数组中查找对应规格
|
|
13
|
+
* - 使用规格的 is_track, over_sold, stock_quantity 而不是主商品的
|
|
14
|
+
*
|
|
15
|
+
* @param productData 商品数据(需包含 is_track, over_sold, stock_quantity 字段,多规格商品需包含 variant 数组)
|
|
16
|
+
* @param product_variant_id 商品变体ID,如果存在则为多规格商品
|
|
17
|
+
* @param quantity 需要添加的数量
|
|
18
|
+
* @param bundle 套餐配置(子商品需包含 is_track, over_sold, stock_quantity 字段)
|
|
19
|
+
* @param currentCartItems 当前购物车商品列表
|
|
20
|
+
* @returns 库存检测结果
|
|
21
|
+
*/
|
|
22
|
+
export function checkProductStock(_ref) {
|
|
23
|
+
var productData = _ref.productData,
|
|
24
|
+
product_variant_id = _ref.product_variant_id,
|
|
25
|
+
quantity = _ref.quantity,
|
|
26
|
+
bundle = _ref.bundle,
|
|
27
|
+
currentCartItems = _ref.currentCartItems;
|
|
28
|
+
// 1. 检测主商品库存
|
|
29
|
+
// 处理多规格商品:如果有product_variant_id,则从variant数组中查找对应的规格配置
|
|
30
|
+
var mainProductConfig = productData;
|
|
31
|
+
if (product_variant_id && productData.variant && Array.isArray(productData.variant)) {
|
|
32
|
+
var variant = productData.variant.find(function (v) {
|
|
33
|
+
return v.id === product_variant_id;
|
|
34
|
+
});
|
|
35
|
+
if (variant) {
|
|
36
|
+
mainProductConfig = variant; // 使用规格的配置替换主商品配置
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
// 只有开启库存控制且不允许超卖时才需要检测主商品库存
|
|
41
|
+
var isMainProductTrackingEnabled = mainProductConfig.is_track === 1 || mainProductConfig.is_track === true;
|
|
42
|
+
var isMainProductOverSoldDisabled = mainProductConfig.over_sold === 0;
|
|
43
|
+
if (isMainProductTrackingEnabled && isMainProductOverSoldDisabled) {
|
|
44
|
+
var existingQuantity = currentCartItems.reduce(function (total, cartItem) {
|
|
45
|
+
var _cartItem$_productOri, _cartItem$_productOri2, _cartItem$_productOri3;
|
|
46
|
+
// 检查是否为相同商品(比较商品ID和变体ID)
|
|
47
|
+
var isSameProduct = ((_cartItem$_productOri = cartItem._productOrigin) === null || _cartItem$_productOri === void 0 ? void 0 : _cartItem$_productOri.id) === productData.id;
|
|
48
|
+
var isSameVariant = !product_variant_id && !((_cartItem$_productOri2 = cartItem._productOrigin) !== null && _cartItem$_productOri2 !== void 0 && _cartItem$_productOri2.product_variant_id) || ((_cartItem$_productOri3 = cartItem._productOrigin) === null || _cartItem$_productOri3 === void 0 ? void 0 : _cartItem$_productOri3.product_variant_id) === product_variant_id;
|
|
49
|
+
if (isSameProduct && isSameVariant) {
|
|
50
|
+
return total + (cartItem.num || 0);
|
|
51
|
+
}
|
|
52
|
+
return total;
|
|
53
|
+
}, 0);
|
|
54
|
+
var totalQuantity = existingQuantity + quantity;
|
|
55
|
+
var stockQuantity = mainProductConfig.stock_quantity;
|
|
56
|
+
|
|
57
|
+
// 检查主商品库存是否足够
|
|
58
|
+
if (stockQuantity !== undefined && stockQuantity !== null && totalQuantity > stockQuantity) {
|
|
59
|
+
return {
|
|
60
|
+
success: false,
|
|
61
|
+
errorCode: 'not_enough_stock'
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
// 2. 检测套餐商品库存
|
|
67
|
+
if (bundle && Array.isArray(bundle)) {
|
|
68
|
+
// 直接遍历套餐商品数组
|
|
69
|
+
var _iterator = _createForOfIteratorHelper(bundle),
|
|
70
|
+
_step;
|
|
71
|
+
try {
|
|
72
|
+
var _loop = function _loop() {
|
|
73
|
+
var bundleItem = _step.value;
|
|
74
|
+
var bundleProductId = bundleItem.bundle_product_id;
|
|
75
|
+
var bundleStockQuantity = bundleItem.stock_quantity;
|
|
76
|
+
var bundleRequiredQuantity = (bundleItem.num || 1) * quantity; // 子商品需求数量 = 子商品配置数量 * 主商品购买数量
|
|
77
|
+
|
|
78
|
+
// 检查套餐子商品是否需要进行库存控制
|
|
79
|
+
var isBundleTrackingEnabled = bundleItem.is_track === 1 || bundleItem.is_track === true;
|
|
80
|
+
var isBundleOverSoldDisabled = bundleItem.over_sold === 0;
|
|
81
|
+
|
|
82
|
+
// 跳过没有开启库存控制或允许超卖的子商品
|
|
83
|
+
if (!isBundleTrackingEnabled || !isBundleOverSoldDisabled) return 0; // continue
|
|
84
|
+
|
|
85
|
+
// 跳过没有库存配置的子商品
|
|
86
|
+
if (bundleStockQuantity === undefined || bundleStockQuantity === null) return 0; // continue
|
|
87
|
+
|
|
88
|
+
// 计算购物车中已有的相同子商品数量
|
|
89
|
+
var existingBundleQuantity = currentCartItems.reduce(function (total, cartItem) {
|
|
90
|
+
// 检查购物车中商品的套餐配置
|
|
91
|
+
if (!cartItem._bundleOrigin || !Array.isArray(cartItem._bundleOrigin)) return total;
|
|
92
|
+
cartItem._bundleOrigin.forEach(function (cartBundleItem) {
|
|
93
|
+
if (cartBundleItem.bundle_product_id === bundleProductId) {
|
|
94
|
+
total += (cartBundleItem.num || 1) * (cartItem.num || 1);
|
|
95
|
+
}
|
|
96
|
+
});
|
|
97
|
+
return total;
|
|
98
|
+
}, 0);
|
|
99
|
+
var totalBundleQuantity = existingBundleQuantity + bundleRequiredQuantity;
|
|
100
|
+
|
|
101
|
+
// 检查子商品库存是否足够
|
|
102
|
+
if (totalBundleQuantity > bundleStockQuantity) {
|
|
103
|
+
return {
|
|
104
|
+
v: {
|
|
105
|
+
success: false,
|
|
106
|
+
errorCode: 'not_enough_stock'
|
|
107
|
+
}
|
|
108
|
+
};
|
|
109
|
+
}
|
|
110
|
+
},
|
|
111
|
+
_ret;
|
|
112
|
+
for (_iterator.s(); !(_step = _iterator.n()).done;) {
|
|
113
|
+
_ret = _loop();
|
|
114
|
+
if (_ret === 0) continue;
|
|
115
|
+
if (_ret) return _ret.v;
|
|
116
|
+
}
|
|
117
|
+
} catch (err) {
|
|
118
|
+
_iterator.e(err);
|
|
119
|
+
} finally {
|
|
120
|
+
_iterator.f();
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
return {
|
|
124
|
+
success: true
|
|
125
|
+
};
|
|
126
|
+
}
|
|
@@ -1284,7 +1284,7 @@ export var CheckoutImpl = /*#__PURE__*/function (_BaseModule) {
|
|
|
1284
1284
|
value: (function () {
|
|
1285
1285
|
var _updateVoucherPaymentItemsAsync = _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime().mark(function _callee17(voucherPaymentItems) {
|
|
1286
1286
|
var _this3 = this;
|
|
1287
|
-
var orderPaymentType, voucherPaymentItemsWithType, currentOrderId, isCurrentOrderReal, updatedOrder;
|
|
1287
|
+
var remainingAmount, remainingValue, isOrderSynced, orderPaymentType, voucherPaymentItemsWithType, currentOrderId, isCurrentOrderReal, updatedOrder;
|
|
1288
1288
|
return _regeneratorRuntime().wrap(function _callee17$(_context17) {
|
|
1289
1289
|
while (1) switch (_context17.prev = _context17.next) {
|
|
1290
1290
|
case 0:
|
|
@@ -1295,6 +1295,26 @@ export var CheckoutImpl = /*#__PURE__*/function (_BaseModule) {
|
|
|
1295
1295
|
}
|
|
1296
1296
|
throw createCheckoutError(CheckoutErrorType.ValidationFailed, '当前没有活跃订单,无法更新代金券支付项');
|
|
1297
1297
|
case 3:
|
|
1298
|
+
_context17.next = 5;
|
|
1299
|
+
return this.calculateRemainingAmountAsync();
|
|
1300
|
+
case 5:
|
|
1301
|
+
remainingAmount = _context17.sent;
|
|
1302
|
+
remainingValue = new Decimal(remainingAmount);
|
|
1303
|
+
isOrderSynced = this.store.isOrderSynced;
|
|
1304
|
+
if (!(remainingValue.lte(0) && isOrderSynced && voucherPaymentItems.length === 0)) {
|
|
1305
|
+
_context17.next = 11;
|
|
1306
|
+
break;
|
|
1307
|
+
}
|
|
1308
|
+
this.logInfo('订单已同步且支付完成,跳过清空代金券操作避免重复同步:', {
|
|
1309
|
+
orderUuid: this.store.currentOrder.uuid,
|
|
1310
|
+
orderId: this.store.currentOrder.order_id,
|
|
1311
|
+
remainingAmount: remainingAmount,
|
|
1312
|
+
isOrderSynced: isOrderSynced,
|
|
1313
|
+
voucherPaymentItemsCount: voucherPaymentItems.length,
|
|
1314
|
+
reason: 'Order synced and payment completed, skip clear vouchers to avoid duplicate sync'
|
|
1315
|
+
});
|
|
1316
|
+
return _context17.abrupt("return");
|
|
1317
|
+
case 11:
|
|
1298
1318
|
this.logInfo('开始批量更新代金券支付项:', {
|
|
1299
1319
|
voucherPaymentItems: voucherPaymentItems
|
|
1300
1320
|
});
|
|
@@ -1316,15 +1336,15 @@ export var CheckoutImpl = /*#__PURE__*/function (_BaseModule) {
|
|
|
1316
1336
|
metadata: metadata
|
|
1317
1337
|
});
|
|
1318
1338
|
}); // 调用 Payment 模块的批量更新方法
|
|
1319
|
-
_context17.next =
|
|
1339
|
+
_context17.next = 16;
|
|
1320
1340
|
return this.payment.updateVoucherPaymentItemsAsync(this.store.currentOrder.uuid, voucherPaymentItemsWithType);
|
|
1321
|
-
case
|
|
1341
|
+
case 16:
|
|
1322
1342
|
// 重新从Payment模块获取最新的订单数据,确保支付项同步
|
|
1323
1343
|
currentOrderId = this.store.currentOrder.order_id; // 保存当前的订单ID
|
|
1324
1344
|
isCurrentOrderReal = currentOrderId && !isVirtualOrderId(currentOrderId);
|
|
1325
|
-
_context17.next =
|
|
1345
|
+
_context17.next = 20;
|
|
1326
1346
|
return this.payment.getPaymentOrderByUuidAsync(this.store.currentOrder.uuid);
|
|
1327
|
-
case
|
|
1347
|
+
case 20:
|
|
1328
1348
|
updatedOrder = _context17.sent;
|
|
1329
1349
|
if (updatedOrder) {
|
|
1330
1350
|
// 如果当前订单ID是真实ID,但获取到的订单ID是虚拟ID,需要保护真实ID
|
|
@@ -1339,10 +1359,10 @@ export var CheckoutImpl = /*#__PURE__*/function (_BaseModule) {
|
|
|
1339
1359
|
}
|
|
1340
1360
|
|
|
1341
1361
|
// 更新 stateAmount 为剩余未支付金额
|
|
1342
|
-
_context17.next =
|
|
1362
|
+
_context17.next = 24;
|
|
1343
1363
|
return this.updateStateAmountToRemaining();
|
|
1344
|
-
case
|
|
1345
|
-
_context17.next =
|
|
1364
|
+
case 24:
|
|
1365
|
+
_context17.next = 26;
|
|
1346
1366
|
return this.core.effects.emit(CheckoutHooks.OnPaymentStarted, {
|
|
1347
1367
|
orderUuid: this.store.currentOrder.uuid,
|
|
1348
1368
|
paymentMethodCode: 'VOUCHER_BATCH',
|
|
@@ -1351,23 +1371,23 @@ export var CheckoutImpl = /*#__PURE__*/function (_BaseModule) {
|
|
|
1351
1371
|
}, 0).toFixed(2),
|
|
1352
1372
|
timestamp: Date.now()
|
|
1353
1373
|
});
|
|
1354
|
-
case
|
|
1374
|
+
case 26:
|
|
1355
1375
|
this.logInfo('代金券支付项批量更新成功');
|
|
1356
|
-
_context17.next =
|
|
1376
|
+
_context17.next = 35;
|
|
1357
1377
|
break;
|
|
1358
|
-
case
|
|
1359
|
-
_context17.prev =
|
|
1378
|
+
case 29:
|
|
1379
|
+
_context17.prev = 29;
|
|
1360
1380
|
_context17.t0 = _context17["catch"](0);
|
|
1361
1381
|
this.logError('[Checkout] 批量更新代金券支付项失败:', _context17.t0);
|
|
1362
|
-
_context17.next =
|
|
1382
|
+
_context17.next = 34;
|
|
1363
1383
|
return this.handleError(_context17.t0, CheckoutErrorType.PaymentFailed);
|
|
1364
|
-
case
|
|
1384
|
+
case 34:
|
|
1365
1385
|
throw _context17.t0;
|
|
1366
|
-
case
|
|
1386
|
+
case 35:
|
|
1367
1387
|
case "end":
|
|
1368
1388
|
return _context17.stop();
|
|
1369
1389
|
}
|
|
1370
|
-
}, _callee17, this, [[0,
|
|
1390
|
+
}, _callee17, this, [[0, 29]]);
|
|
1371
1391
|
}));
|
|
1372
1392
|
function updateVoucherPaymentItemsAsync(_x16) {
|
|
1373
1393
|
return _updateVoucherPaymentItemsAsync.apply(this, arguments);
|
|
@@ -670,36 +670,6 @@ var PaymentModule = class extends import_BaseModule.BaseModule {
|
|
|
670
670
|
if (!order) {
|
|
671
671
|
throw new Error(`订单不存在: ${orderUuid}`);
|
|
672
672
|
}
|
|
673
|
-
const expectAmount = new import_decimal.Decimal(order.expect_amount);
|
|
674
|
-
if (expectAmount.lte(0) && voucherPaymentItems.length === 0) {
|
|
675
|
-
console.warn("[PaymentModule] Skipping voucher update - order already fully paid:", {
|
|
676
|
-
orderUuid,
|
|
677
|
-
expectAmount: order.expect_amount,
|
|
678
|
-
attemptedOperation: "clear_vouchers",
|
|
679
|
-
reason: "Order payment completed, no need to clear vouchers"
|
|
680
|
-
});
|
|
681
|
-
this.logInfo("updateVoucherPaymentItemsAsync skipped - order already paid", {
|
|
682
|
-
orderUuid,
|
|
683
|
-
expectAmount: order.expect_amount,
|
|
684
|
-
voucherCount: voucherPaymentItems.length
|
|
685
|
-
});
|
|
686
|
-
return;
|
|
687
|
-
}
|
|
688
|
-
if (expectAmount.lte(0) && voucherPaymentItems.length > 0) {
|
|
689
|
-
const warningMessage = `订单 ${orderUuid} 已完成支付,不允许添加代金券支付项`;
|
|
690
|
-
console.warn("[PaymentModule] Voucher update blocked - order already fully paid:", {
|
|
691
|
-
orderUuid,
|
|
692
|
-
expectAmount: order.expect_amount,
|
|
693
|
-
attemptedVoucherCount: voucherPaymentItems.length,
|
|
694
|
-
reason: "Order already fully paid"
|
|
695
|
-
});
|
|
696
|
-
this.logError("updateVoucherPaymentItemsAsync blocked", new Error(warningMessage), {
|
|
697
|
-
orderUuid,
|
|
698
|
-
expectAmount: order.expect_amount,
|
|
699
|
-
voucherPaymentItems
|
|
700
|
-
});
|
|
701
|
-
throw new Error(warningMessage);
|
|
702
|
-
}
|
|
703
673
|
const existingVoucherItems = order.payment.filter(
|
|
704
674
|
(payment) => payment.voucher_id && payment.status !== "voided"
|
|
705
675
|
);
|
|
@@ -212,6 +212,7 @@ var RulesModule = class extends import_BaseModule.BaseModule {
|
|
|
212
212
|
tag: discountType,
|
|
213
213
|
discount: {
|
|
214
214
|
discount_card_type: (_d = discount == null ? void 0 : discount.metadata) == null ? void 0 : _d.discount_card_type,
|
|
215
|
+
fixed_amount: product.price,
|
|
215
216
|
resource_id: discount.id,
|
|
216
217
|
title: discount.format_title,
|
|
217
218
|
original_amount: product.origin_total,
|
|
@@ -346,6 +347,7 @@ var RulesModule = class extends import_BaseModule.BaseModule {
|
|
|
346
347
|
type: discountType,
|
|
347
348
|
discount: {
|
|
348
349
|
discount_card_type: (_j = selectedDiscount2 == null ? void 0 : selectedDiscount2.metadata) == null ? void 0 : _j.discount_card_type,
|
|
350
|
+
fixed_amount: new import_decimal.default(productOriginTotal).minus(new import_decimal.default(targetProductTotal)).toNumber(),
|
|
349
351
|
resource_id: selectedDiscount2.id,
|
|
350
352
|
title: selectedDiscount2.format_title,
|
|
351
353
|
original_amount: productOriginTotal,
|
|
@@ -180,7 +180,10 @@ export declare class BookingByStepImpl extends BaseModule implements Module {
|
|
|
180
180
|
* @param {ProductData} productData
|
|
181
181
|
* @memberof BookingByStepImpl
|
|
182
182
|
*/
|
|
183
|
-
storeProduct(productData: ProductData):
|
|
183
|
+
storeProduct(productData: ProductData): {
|
|
184
|
+
success: boolean;
|
|
185
|
+
errorCode?: string;
|
|
186
|
+
};
|
|
184
187
|
/**
|
|
185
188
|
* 往购物车加商品数据
|
|
186
189
|
*
|
|
@@ -203,7 +206,10 @@ export declare class BookingByStepImpl extends BaseModule implements Module {
|
|
|
203
206
|
endTime: string;
|
|
204
207
|
} | null;
|
|
205
208
|
account?: Account | null;
|
|
206
|
-
}):
|
|
209
|
+
}): {
|
|
210
|
+
success: boolean;
|
|
211
|
+
errorCode?: string;
|
|
212
|
+
};
|
|
207
213
|
/**
|
|
208
214
|
* 添加完购物车以后做的一些检测,比如日期是否在同一天
|
|
209
215
|
*
|
|
@@ -294,8 +300,6 @@ export declare class BookingByStepImpl extends BaseModule implements Module {
|
|
|
294
300
|
};
|
|
295
301
|
getTimeSlotByAllResources(resources_code: string): any[];
|
|
296
302
|
submitTimeSlot(timeSlots: TimeSliceItem): void;
|
|
297
|
-
clearCache(): void;
|
|
298
|
-
clearCacheByModule(module: string): void;
|
|
299
303
|
private getScheduleDataByIds;
|
|
300
304
|
openProductDetail(productId: number): Promise<void>;
|
|
301
305
|
closeProductDetail(): void;
|
|
@@ -312,6 +316,7 @@ export declare class BookingByStepImpl extends BaseModule implements Module {
|
|
|
312
316
|
count: number;
|
|
313
317
|
left: number;
|
|
314
318
|
}[];
|
|
319
|
+
checkMaxDurationCapacity(): boolean;
|
|
315
320
|
setOtherData(key: string, value: any): void;
|
|
316
321
|
getOtherData(key: string): any;
|
|
317
322
|
getProductTypeById(id: number): Promise<"normal" | "duration" | "session">;
|