@pisell/pisellos 0.0.569 → 0.10.2
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/core/index.js +10 -6
- package/dist/index.d.ts +0 -1
- package/dist/index.js +0 -3
- package/dist/model/strategy/adapter/promotion/index.js +9 -0
- package/dist/model/strategy/adapter/walletPass/evaluator.js +7 -5
- package/dist/model/strategy/adapter/walletPass/example.js +26 -3
- package/dist/model/strategy/adapter/walletPass/index.js +5 -5
- package/dist/model/strategy/adapter/walletPass/type.d.ts +0 -3
- package/dist/model/strategy/adapter/walletPass/utils.d.ts +0 -26
- package/dist/model/strategy/adapter/walletPass/utils.js +17 -92
- package/dist/model/strategy/strategy-example.js +19 -5
- package/dist/modules/BaseModule.d.ts +1 -1
- package/dist/modules/BaseModule.js +8 -10
- package/dist/modules/Cart/index.js +3 -0
- package/dist/modules/Cart/utils/cartProduct.js +1 -0
- package/dist/modules/Cart/utils/cartResource.js +0 -2
- package/dist/modules/Cart/utils/changePrice.js +9 -16
- package/dist/modules/Date/index.js +5 -1
- package/dist/modules/Discount/index.d.ts +0 -1
- package/dist/modules/Discount/index.js +7 -22
- package/dist/modules/Discount/types.d.ts +0 -1
- package/dist/modules/OpenData/index.js +2 -0
- package/dist/modules/Order/index.js +70 -40
- package/dist/modules/Payment/index.js +162 -97
- package/dist/modules/Payment/types.d.ts +4 -8
- package/dist/modules/Payment/walletpass.d.ts +0 -2
- package/dist/modules/Payment/walletpass.js +5 -4
- package/dist/modules/ProductList/index.d.ts +1 -1
- package/dist/modules/ProductList/index.js +3 -12
- package/dist/modules/ProductList/types.d.ts +0 -1
- package/dist/modules/Rules/index.js +126 -427
- package/dist/modules/SalesSummary/utils.js +1 -2
- package/dist/modules/Schedule/getDateIsInSchedule.js +0 -5
- package/dist/modules/Schedule/index.js +1 -0
- package/dist/modules/Schedule/utils.js +1 -0
- package/dist/modules/Step/index.js +1 -0
- package/dist/modules/Summary/utils.js +22 -43
- package/dist/plugins/request.js +4 -1
- package/dist/plugins/window.js +6 -2
- package/dist/server/index.js +129 -97
- package/dist/server/modules/menu/index.js +41 -32
- package/dist/server/modules/order/index.js +4 -2
- package/dist/server/modules/products/index.js +42 -24
- package/dist/server/modules/quotation/index.js +38 -29
- package/dist/server/modules/resource/index.js +4 -3
- package/dist/server/modules/schedule/index.js +35 -27
- package/dist/server/utils/product.js +1 -0
- package/dist/solution/BaseSales/index.js +41 -38
- package/dist/solution/BookingByStep/index.d.ts +1 -1
- package/dist/solution/BookingByStep/index.js +60 -11
- package/dist/solution/BookingByStep/utils/capacity.js +11 -1
- package/dist/solution/BookingByStep/utils/resources.js +3 -1
- package/dist/solution/BookingByStep/utils/timeslots.js +43 -2
- package/dist/solution/BookingTicket/index.d.ts +1 -1
- package/dist/solution/BookingTicket/index.js +2 -0
- package/dist/solution/BookingTicket/utils/scan/cloudSearch.js +7 -6
- package/dist/solution/BookingTicket/utils/scan/handleScan.js +31 -25
- package/dist/solution/BookingTicket/utils/scan/index.js +10 -6
- package/dist/solution/BuyTickets/index.js +12 -9
- package/dist/solution/Checkout/index.js +252 -177
- package/dist/solution/Checkout/utils/index.js +16 -4
- package/dist/solution/RegisterAndLogin/index.js +76 -30
- package/dist/solution/ScanOrder/index.js +60 -50
- package/dist/solution/ShopDiscount/index.js +14 -15
- package/dist/solution/ShopDiscount/types.d.ts +4 -5
- package/dist/solution/ShopDiscount/utils.d.ts +0 -9
- package/dist/solution/ShopDiscount/utils.js +0 -9
- package/dist/solution/VenueBooking/index.d.ts +3 -31
- package/dist/solution/VenueBooking/index.js +496 -651
- package/dist/solution/VenueBooking/types.d.ts +1 -3
- package/dist/solution/VenueBooking/types.js +0 -1
- package/dist/solution/VenueBooking/utils/dateSummary.d.ts +2 -2
- package/dist/solution/VenueBooking/utils/dateSummary.js +25 -12
- package/dist/solution/VenueBooking/utils/timeSlot.d.ts +0 -2
- package/dist/solution/VenueBooking/utils/timeSlot.js +4 -9
- package/dist/types/index.d.ts +1 -1
- package/dist/utils/task.js +18 -15
- package/dist/utils/watch.js +1 -0
- package/lib/apis/picoding.js +0 -2
- package/lib/core/index.js +132 -130
- package/lib/effects/index.js +57 -46
- package/lib/index.d.ts +0 -1
- package/lib/index.js +50 -92
- package/lib/model/index.js +21 -14
- package/lib/model/strategy/adapter/dataVariant/adapter.js +50 -38
- package/lib/model/strategy/adapter/dataVariant/evaluator.js +236 -162
- package/lib/model/strategy/adapter/dataVariant/examples.js +237 -229
- package/lib/model/strategy/adapter/dataVariant/index.js +34 -39
- package/lib/model/strategy/adapter/dataVariant/type.js +28 -57
- package/lib/model/strategy/adapter/index.js +64 -100
- package/lib/model/strategy/adapter/itemRule/adapter.js +144 -156
- package/lib/model/strategy/adapter/itemRule/evaluator.js +48 -36
- package/lib/model/strategy/adapter/itemRule/examples.js +225 -295
- package/lib/model/strategy/adapter/itemRule/index.js +57 -83
- package/lib/model/strategy/adapter/itemRule/type.js +36 -97
- package/lib/model/strategy/adapter/promotion/adapter.js +63 -84
- package/lib/model/strategy/adapter/promotion/evaluator.js +231 -381
- package/lib/model/strategy/adapter/promotion/examples.js +159 -139
- package/lib/model/strategy/adapter/promotion/index.js +49 -1
- package/lib/model/strategy/adapter/promotion/type.js +35 -199
- package/lib/model/strategy/adapter/type.js +16 -4
- package/lib/model/strategy/adapter/walletPass/evaluator.js +152 -180
- package/lib/model/strategy/adapter/walletPass/example.js +190 -217
- package/lib/model/strategy/adapter/walletPass/index.js +51 -75
- package/lib/model/strategy/adapter/walletPass/locales.js +58 -36
- package/lib/model/strategy/adapter/walletPass/type.d.ts +0 -3
- package/lib/model/strategy/adapter/walletPass/type.js +16 -4
- package/lib/model/strategy/adapter/walletPass/utils.d.ts +0 -26
- package/lib/model/strategy/adapter/walletPass/utils.js +383 -678
- package/lib/model/strategy/engine.js +168 -270
- package/lib/model/strategy/index.js +34 -49
- package/lib/model/strategy/strategy-example.js +239 -243
- package/lib/model/strategy/type.js +40 -100
- package/lib/modules/Account/index.js +53 -39
- package/lib/modules/Account/types.js +33 -20
- package/lib/modules/AccountList/index.js +154 -116
- package/lib/modules/AccountList/types.js +31 -30
- package/lib/modules/AccountList/utils.js +30 -18
- package/lib/modules/BaseModule.d.ts +1 -1
- package/lib/modules/BaseModule.js +46 -25
- package/lib/modules/BookingContext/index.js +136 -158
- package/lib/modules/BookingContext/types.js +32 -46
- package/lib/modules/BookingContext/utils/buildCacheItemFromOrderLine.js +143 -131
- package/lib/modules/BookingContext/utils/buildNormalProductCacheItemFromOrderLine.js +82 -75
- package/lib/modules/BookingContext/utils/cacheItemToBookingInput.js +150 -170
- package/lib/modules/BookingContext/utils/flexible.js +55 -38
- package/lib/modules/BookingContext/utils/formatResourceList.js +34 -19
- package/lib/modules/BookingContext/utils/index.js +41 -124
- package/lib/modules/BookingContext/utils/noResource.js +70 -58
- package/lib/modules/BookingContext/utils/orderLineDisplay.js +41 -14
- package/lib/modules/BookingContext/utils/productExtend.js +155 -202
- package/lib/modules/BookingContext/utils/resourceErrors.js +53 -53
- package/lib/modules/BookingContext/utils/resourceSelection.js +41 -19
- package/lib/modules/BookingContext/utils/resources.js +167 -209
- package/lib/modules/BookingContext/utils/serviceTimes.js +116 -115
- package/lib/modules/BookingContext/utils/timeSlices.js +80 -70
- package/lib/modules/Cart/index.js +124 -170
- package/lib/modules/Cart/types.js +51 -46
- package/lib/modules/Cart/utils/cartAccount.js +39 -40
- package/lib/modules/Cart/utils/cartDate.js +61 -56
- package/lib/modules/Cart/utils/cartDiscount.js +33 -28
- package/lib/modules/Cart/utils/cartNote.js +32 -27
- package/lib/modules/Cart/utils/cartProduct.js +251 -321
- package/lib/modules/Cart/utils/cartRelationForms.js +35 -29
- package/lib/modules/Cart/utils/cartResource.js +78 -72
- package/lib/modules/Cart/utils/changePrice.js +50 -28
- package/lib/modules/Cart/utils/index.js +48 -106
- package/lib/modules/Customer/constants.js +34 -13
- package/lib/modules/Customer/index.js +169 -232
- package/lib/modules/Customer/types.js +35 -38
- package/lib/modules/Date/index.js +115 -116
- package/lib/modules/Date/types.js +28 -16
- package/lib/modules/Date/utils.js +123 -174
- package/lib/modules/Discount/index.d.ts +0 -1
- package/lib/modules/Discount/index.js +128 -130
- package/lib/modules/Discount/types.d.ts +0 -1
- package/lib/modules/Discount/types.js +31 -9
- package/lib/modules/Guests/index.js +56 -30
- package/lib/modules/Guests/types.js +33 -23
- package/lib/modules/Holder/index.js +189 -209
- package/lib/modules/Holder/types.js +16 -4
- package/lib/modules/Holder/utils.js +49 -20
- package/lib/modules/OpenData/index.js +76 -69
- package/lib/modules/OpenData/types.js +16 -4
- package/lib/modules/OpenData/utils.js +78 -44
- package/lib/modules/Order/index.js +1909 -1572
- package/lib/modules/Order/types.js +35 -78
- package/lib/modules/Order/utils/bundleDiscountHydration.js +74 -41
- package/lib/modules/Order/utils/manualProductDiscount.js +123 -124
- package/lib/modules/Order/utils/orderCollectionIdentity.js +210 -148
- package/lib/modules/Order/utils.js +634 -799
- package/lib/modules/Payment/cash.js +33 -20
- package/lib/modules/Payment/cashRecommendationAlgorithm.js +76 -145
- package/lib/modules/Payment/eftpos.js +30 -16
- package/lib/modules/Payment/index.js +378 -518
- package/lib/modules/Payment/mx51.js +0 -1
- package/lib/modules/Payment/types.d.ts +4 -8
- package/lib/modules/Payment/types.js +107 -216
- package/lib/modules/Payment/utils.js +48 -51
- package/lib/modules/Payment/walletpass.d.ts +0 -2
- package/lib/modules/Payment/walletpass.js +211 -226
- package/lib/modules/Product/index.js +46 -51
- package/lib/modules/Product/types.js +16 -4
- package/lib/modules/Product/utils.js +33 -32
- package/lib/modules/ProductList/index.d.ts +1 -1
- package/lib/modules/ProductList/index.js +117 -117
- package/lib/modules/ProductList/types.d.ts +0 -1
- package/lib/modules/ProductList/types.js +31 -9
- package/lib/modules/Quotation/index.js +100 -66
- package/lib/modules/Quotation/types.js +16 -4
- package/lib/modules/Resource/index.js +59 -27
- package/lib/modules/Resource/types.js +32 -22
- package/lib/modules/Resource/utils.js +38 -30
- package/lib/modules/ResourcePlanner/index.js +61 -60
- package/lib/modules/ResourcePlanner/planner.js +384 -346
- package/lib/modules/ResourcePlanner/types.js +16 -4
- package/lib/modules/Rules/index.js +903 -1475
- package/lib/modules/Rules/types.js +40 -17
- package/lib/modules/SalesSummary/index.js +90 -85
- package/lib/modules/SalesSummary/types.js +16 -4
- package/lib/modules/SalesSummary/utils.js +427 -351
- package/lib/modules/ScanOrderLogger/index.js +79 -59
- package/lib/modules/ScanOrderLogger/providers/feishu.js +78 -54
- package/lib/modules/ScanOrderLogger/providers/grafana.js +36 -13
- package/lib/modules/ScanOrderLogger/types.js +16 -4
- package/lib/modules/Schedule/getDateIsInSchedule.js +163 -339
- package/lib/modules/Schedule/index.js +114 -100
- package/lib/modules/Schedule/type.js +16 -4
- package/lib/modules/Schedule/types.js +16 -4
- package/lib/modules/Schedule/utils.js +291 -433
- package/lib/modules/Step/index.js +52 -40
- package/lib/modules/Step/tyeps.js +16 -4
- package/lib/modules/Summary/index.js +71 -66
- package/lib/modules/Summary/types.js +16 -4
- package/lib/modules/Summary/utils.js +418 -791
- package/lib/modules/SurchargeList/index.js +60 -46
- package/lib/modules/SurchargeList/types.js +16 -4
- package/lib/modules/index.js +63 -245
- package/lib/plugins/app.js +16 -4
- package/lib/plugins/index.js +25 -36
- package/lib/plugins/request.js +126 -137
- package/lib/plugins/shopStore.js +16 -4
- package/lib/plugins/user.js +16 -4
- package/lib/plugins/window.js +179 -171
- package/lib/server/index.js +2337 -2675
- package/lib/server/modules/floor-plan/index.js +123 -102
- package/lib/server/modules/floor-plan/types.js +30 -15
- package/lib/server/modules/index.js +68 -106
- package/lib/server/modules/menu/index.js +122 -146
- package/lib/server/modules/menu/types.js +31 -18
- package/lib/server/modules/order/index.js +642 -567
- package/lib/server/modules/order/types.js +32 -117
- package/lib/server/modules/order/utils/filterBookings.js +194 -219
- package/lib/server/modules/order/utils/filterOrders.js +92 -118
- package/lib/server/modules/payment/index.js +83 -59
- package/lib/server/modules/payment/types.js +16 -4
- package/lib/server/modules/products/index.js +459 -575
- package/lib/server/modules/products/types.js +34 -44
- package/lib/server/modules/quotation/index.js +172 -137
- package/lib/server/modules/quotation/types.js +31 -21
- package/lib/server/modules/resource/index.js +184 -213
- package/lib/server/modules/resource/types.js +33 -42
- package/lib/server/modules/schedule/index.js +123 -135
- package/lib/server/modules/schedule/types.js +21 -14
- package/lib/server/modules/schedule/utils.js +163 -334
- package/lib/server/types.js +16 -4
- package/lib/server/utils/index.js +23 -25
- package/lib/server/utils/product.js +139 -196
- package/lib/server/utils/schedule.js +41 -34
- package/lib/server/utils/small-ticket.js +456 -478
- package/lib/server/utils/time.js +49 -40
- package/lib/solution/BaseSales/index.js +1016 -850
- package/lib/solution/BaseSales/types.js +37 -41
- package/lib/solution/BaseSales/utils/cartPromotion.js +460 -611
- package/lib/solution/BaseSales/utils/parseSalesResponse.js +105 -85
- package/lib/solution/BaseSales/utils/quotationPrice.js +114 -56
- package/lib/solution/BaseSales/utils/transformBaseProductToOrderProduct.js +143 -102
- package/lib/solution/BaseSales/utils.js +140 -155
- package/lib/solution/BookingByStep/index.d.ts +1 -1
- package/lib/solution/BookingByStep/index.js +1340 -1530
- package/lib/solution/BookingByStep/types.js +99 -40
- package/lib/solution/BookingByStep/utils/capacity.js +160 -192
- package/lib/solution/BookingByStep/utils/products.js +52 -42
- package/lib/solution/BookingByStep/utils/resources.js +330 -527
- package/lib/solution/BookingByStep/utils/stock.js +44 -65
- package/lib/solution/BookingByStep/utils/timeslots.js +131 -129
- package/lib/solution/BookingTicket/index.d.ts +1 -1
- package/lib/solution/BookingTicket/index.js +459 -543
- package/lib/solution/BookingTicket/types.js +77 -99
- package/lib/solution/BookingTicket/utils/addProductDecision.js +184 -219
- package/lib/solution/BookingTicket/utils/addTimeAvailability.js +89 -58
- package/lib/solution/BookingTicket/utils/bookingStatus.js +78 -107
- package/lib/solution/BookingTicket/utils/cartView.js +94 -110
- package/lib/solution/BookingTicket/utils/exampleData.js +0 -185
- package/lib/solution/BookingTicket/utils/orderCustomer.js +41 -18
- package/lib/solution/BookingTicket/utils/resolveBestAddTimePlan.js +86 -219
- package/lib/solution/BookingTicket/utils/scan/applyGlobalScan.js +93 -144
- package/lib/solution/BookingTicket/utils/scan/cloudSearch.js +91 -79
- package/lib/solution/BookingTicket/utils/scan/formatGlobalScan.js +41 -54
- package/lib/solution/BookingTicket/utils/scan/handleScan.js +81 -84
- package/lib/solution/BookingTicket/utils/scan/index.js +104 -98
- package/lib/solution/BookingTicket/utils/scan/scanCache.js +43 -90
- package/lib/solution/BookingTicket/utils/scan/scanProductValues.js +64 -52
- package/lib/solution/BookingTicket/utils/sessionCatalogStale.js +58 -56
- package/lib/solution/BuyTickets/index.js +70 -67
- package/lib/solution/BuyTickets/types.js +38 -22
- package/lib/solution/Checkout/index.js +1158 -1451
- package/lib/solution/Checkout/types.js +61 -91
- package/lib/solution/Checkout/utils/index.js +156 -228
- package/lib/solution/PlaceOrder/index.js +0 -55
- package/lib/solution/RegisterAndLogin/config.js +460 -493
- package/lib/solution/RegisterAndLogin/index.js +630 -633
- package/lib/solution/RegisterAndLogin/types.js +76 -189
- package/lib/solution/RegisterAndLogin/utils.js +125 -183
- package/lib/solution/Sales/index.js +320 -317
- package/lib/solution/Sales/types.js +33 -27
- package/lib/solution/ScanOrder/index.js +862 -831
- package/lib/solution/ScanOrder/types.js +34 -33
- package/lib/solution/ScanOrder/utils.js +371 -406
- package/lib/solution/ShopDiscount/index.js +233 -235
- package/lib/solution/ShopDiscount/types.d.ts +4 -5
- package/lib/solution/ShopDiscount/types.js +37 -15
- package/lib/solution/ShopDiscount/utils.d.ts +0 -9
- package/lib/solution/ShopDiscount/utils.js +172 -309
- package/lib/solution/UnifiedBookingSales/index.js +405 -379
- package/lib/solution/UnifiedBookingSales/types.js +31 -12
- package/lib/solution/VenueBooking/index.d.ts +3 -31
- package/lib/solution/VenueBooking/index.js +916 -967
- package/lib/solution/VenueBooking/types.d.ts +1 -3
- package/lib/solution/VenueBooking/types.js +39 -20
- package/lib/solution/VenueBooking/utils/dateSummary.d.ts +2 -2
- package/lib/solution/VenueBooking/utils/dateSummary.js +62 -25
- package/lib/solution/VenueBooking/utils/resource.js +54 -31
- package/lib/solution/VenueBooking/utils/slotMerge.js +107 -94
- package/lib/solution/VenueBooking/utils/timeSlot.d.ts +0 -2
- package/lib/solution/VenueBooking/utils/timeSlot.js +133 -138
- package/lib/solution/VenueBooking/utils.js +67 -130
- package/lib/solution/index.js +41 -124
- package/lib/store/createStore.js +29 -32
- package/lib/types/index.d.ts +1 -1
- package/lib/types/index.js +16 -4
- package/lib/utils/task.js +36 -36
- package/lib/utils/watch.js +47 -81
- package/package.json +2 -4
- package/dist/solution/VenueBooking/utils/smartPricing.d.ts +0 -70
- package/dist/solution/VenueBooking/utils/smartPricing.js +0 -233
- package/lib/solution/VenueBooking/utils/smartPricing.d.ts +0 -70
- package/lib/solution/VenueBooking/utils/smartPricing.js +0 -192
|
@@ -1,128 +1,122 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
Object.
|
|
4
|
-
|
|
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/evaluator.ts
|
|
30
|
+
var evaluator_exports = {};
|
|
31
|
+
__export(evaluator_exports, {
|
|
32
|
+
PromotionEvaluator: () => PromotionEvaluator
|
|
5
33
|
});
|
|
6
|
-
exports
|
|
7
|
-
var
|
|
8
|
-
var
|
|
9
|
-
var
|
|
10
|
-
var
|
|
11
|
-
|
|
12
|
-
// ============================================
|
|
13
|
-
// 类型定义
|
|
14
|
-
// ============================================
|
|
15
|
-
|
|
16
|
-
// ============================================
|
|
17
|
-
// 多语言定义
|
|
18
|
-
// ============================================
|
|
19
|
-
|
|
20
|
-
const defaultLocales = {
|
|
34
|
+
module.exports = __toCommonJS(evaluator_exports);
|
|
35
|
+
var import_decimal = __toESM(require("decimal.js"));
|
|
36
|
+
var import_engine = require("../../engine");
|
|
37
|
+
var import_adapter = require("./adapter");
|
|
38
|
+
var import_type = require("./type");
|
|
39
|
+
var defaultLocales = {
|
|
21
40
|
en: {
|
|
22
|
-
no_applicable_promotion:
|
|
23
|
-
promotion_not_in_time_range:
|
|
24
|
-
product_not_in_promotion:
|
|
41
|
+
no_applicable_promotion: "No applicable promotion",
|
|
42
|
+
promotion_not_in_time_range: "Promotion not in valid time range",
|
|
43
|
+
product_not_in_promotion: "Product not in promotion scope"
|
|
25
44
|
},
|
|
26
|
-
|
|
27
|
-
no_applicable_promotion:
|
|
28
|
-
promotion_not_in_time_range:
|
|
29
|
-
product_not_in_promotion:
|
|
45
|
+
"zh-CN": {
|
|
46
|
+
no_applicable_promotion: "无适用的促销活动",
|
|
47
|
+
promotion_not_in_time_range: "促销活动不在有效时间范围内",
|
|
48
|
+
product_not_in_promotion: "商品不在促销范围内"
|
|
30
49
|
},
|
|
31
|
-
|
|
32
|
-
no_applicable_promotion:
|
|
33
|
-
promotion_not_in_time_range:
|
|
34
|
-
product_not_in_promotion:
|
|
50
|
+
"zh-HK": {
|
|
51
|
+
no_applicable_promotion: "無適用的促銷活動",
|
|
52
|
+
promotion_not_in_time_range: "促銷活動不在有效時間範圍內",
|
|
53
|
+
product_not_in_promotion: "商品不在促銷範圍內"
|
|
35
54
|
}
|
|
36
55
|
};
|
|
37
|
-
|
|
38
|
-
// ============================================
|
|
39
|
-
// PromotionEvaluator 实现
|
|
40
|
-
// ============================================
|
|
41
|
-
|
|
42
|
-
/**
|
|
43
|
-
* Promotion 评估器
|
|
44
|
-
*
|
|
45
|
-
* 用于评估商品适用的促销活动
|
|
46
|
-
* 封装了 StrategyEngine 和 PromotionAdapter 的调用逻辑
|
|
47
|
-
*/
|
|
48
|
-
class PromotionEvaluator {
|
|
49
|
-
engine;
|
|
50
|
-
adapter;
|
|
51
|
-
strategyConfigs = [];
|
|
52
|
-
locale = 'en';
|
|
53
|
-
locales = defaultLocales;
|
|
56
|
+
var PromotionEvaluator = class {
|
|
54
57
|
constructor() {
|
|
55
|
-
this.
|
|
58
|
+
this.strategyConfigs = [];
|
|
59
|
+
this.locale = "en";
|
|
60
|
+
this.locales = defaultLocales;
|
|
61
|
+
this.engine = new import_engine.StrategyEngine({
|
|
56
62
|
debug: false,
|
|
57
63
|
enableTrace: false
|
|
58
64
|
});
|
|
59
|
-
this.adapter = new
|
|
65
|
+
this.adapter = new import_adapter.PromotionAdapter();
|
|
60
66
|
}
|
|
61
|
-
|
|
62
67
|
// ============================================
|
|
63
68
|
// 配置管理
|
|
64
69
|
// ============================================
|
|
65
|
-
|
|
66
70
|
/**
|
|
67
71
|
* 设置策略配置列表
|
|
68
72
|
*/
|
|
69
73
|
setStrategyConfigs(strategyConfigs) {
|
|
70
|
-
const newStrategyConfigs = strategyConfigs.filter(item => item.metadata.type ===
|
|
74
|
+
const newStrategyConfigs = strategyConfigs.filter((item) => item.metadata.type === "promotion");
|
|
71
75
|
this.strategyConfigs = newStrategyConfigs;
|
|
72
76
|
}
|
|
73
|
-
|
|
74
77
|
/**
|
|
75
78
|
* 获取策略配置列表
|
|
76
79
|
*/
|
|
77
80
|
getStrategyConfigs() {
|
|
78
81
|
return this.strategyConfigs;
|
|
79
82
|
}
|
|
80
|
-
|
|
81
83
|
/**
|
|
82
84
|
* 添加策略配置
|
|
83
85
|
*/
|
|
84
86
|
addStrategyConfig(strategyConfig) {
|
|
85
87
|
this.strategyConfigs.push(strategyConfig);
|
|
86
88
|
}
|
|
87
|
-
|
|
88
89
|
/**
|
|
89
90
|
* 设置语言
|
|
90
91
|
*/
|
|
91
92
|
setLocale(locale) {
|
|
92
93
|
this.locale = locale;
|
|
93
94
|
}
|
|
94
|
-
|
|
95
95
|
/**
|
|
96
96
|
* 设置自定义多语言文案
|
|
97
97
|
*/
|
|
98
98
|
setLocales(locales) {
|
|
99
|
-
this.locales = {
|
|
100
|
-
...defaultLocales,
|
|
101
|
-
...locales
|
|
102
|
-
};
|
|
99
|
+
this.locales = { ...defaultLocales, ...locales };
|
|
103
100
|
}
|
|
104
|
-
|
|
105
101
|
/**
|
|
106
102
|
* 获取多语言文案
|
|
107
103
|
*/
|
|
108
104
|
getText(key) {
|
|
109
|
-
|
|
105
|
+
var _a, _b;
|
|
106
|
+
return ((_b = (_a = this.locales) == null ? void 0 : _a[this.locale]) == null ? void 0 : _b[key]) || key;
|
|
110
107
|
}
|
|
111
|
-
|
|
112
108
|
/**
|
|
113
109
|
* 获取多语言名称
|
|
114
110
|
*/
|
|
115
111
|
getLocalizedName(name) {
|
|
116
|
-
if (typeof name ===
|
|
112
|
+
if (typeof name === "string") {
|
|
117
113
|
return name;
|
|
118
114
|
}
|
|
119
|
-
return name[this.locale] || name[
|
|
115
|
+
return name[this.locale] || name["en"] || Object.values(name)[0] || "";
|
|
120
116
|
}
|
|
121
|
-
|
|
122
117
|
// ============================================
|
|
123
118
|
// 核心评估方法
|
|
124
119
|
// ============================================
|
|
125
|
-
|
|
126
120
|
/**
|
|
127
121
|
* 评估商品列表
|
|
128
122
|
*
|
|
@@ -133,31 +127,19 @@ class PromotionEvaluator {
|
|
|
133
127
|
* @returns 每个商品的促销评估结果
|
|
134
128
|
*/
|
|
135
129
|
evaluateProducts(input) {
|
|
136
|
-
const {
|
|
137
|
-
products,
|
|
138
|
-
strategyConfigs,
|
|
139
|
-
channel
|
|
140
|
-
} = input;
|
|
130
|
+
const { products, strategyConfigs, channel } = input;
|
|
141
131
|
const configs = strategyConfigs || this.strategyConfigs;
|
|
142
132
|
const results = [];
|
|
143
133
|
for (const product of products) {
|
|
144
134
|
const applicablePromotions = [];
|
|
145
|
-
|
|
146
|
-
// 遍历所有策略,检查此商品是否适用
|
|
147
135
|
for (const config of configs) {
|
|
148
|
-
// 1. 先用 getProductMatchInfo 检查商品是否在策略的适用范围内(包含 bundle 子商品检查)
|
|
149
136
|
const matchInfo = this.getProductMatchInfo(product, config);
|
|
150
137
|
if (!matchInfo.isMatch) {
|
|
151
|
-
// 商品不在策略适用范围内,跳过
|
|
152
138
|
continue;
|
|
153
139
|
}
|
|
154
|
-
|
|
155
|
-
// 2. 准备上下文(如果是 bundle 子商品匹配,需要用 bundle 子商品的 ID)
|
|
156
140
|
let contextProduct = product;
|
|
157
|
-
if (matchInfo.matchedBundleIndex !==
|
|
141
|
+
if (matchInfo.matchedBundleIndex !== void 0 && product.bundle) {
|
|
158
142
|
const bundleItem = product.bundle[matchInfo.matchedBundleIndex];
|
|
159
|
-
// 创建一个临时的"虚拟商品"用于策略引擎评估
|
|
160
|
-
// 这样策略引擎的 product_match 条件能正确通过
|
|
161
143
|
contextProduct = {
|
|
162
144
|
...product,
|
|
163
145
|
product_id: bundleItem._bundle_product_id,
|
|
@@ -169,8 +151,6 @@ class PromotionEvaluator {
|
|
|
169
151
|
currentProduct: contextProduct,
|
|
170
152
|
channel
|
|
171
153
|
});
|
|
172
|
-
|
|
173
|
-
// 3. 评估策略(检查其他条件如时间范围等)
|
|
174
154
|
const result = this.engine.evaluate(config, context);
|
|
175
155
|
if (result.applicable && result.matchedActions.length > 0) {
|
|
176
156
|
const action = result.matchedActions[0];
|
|
@@ -201,7 +181,6 @@ class PromotionEvaluator {
|
|
|
201
181
|
}
|
|
202
182
|
return results;
|
|
203
183
|
}
|
|
204
|
-
|
|
205
184
|
/**
|
|
206
185
|
* 评估购物车
|
|
207
186
|
*
|
|
@@ -212,42 +191,29 @@ class PromotionEvaluator {
|
|
|
212
191
|
* @returns 购物车评估结果
|
|
213
192
|
*/
|
|
214
193
|
evaluateCart(input) {
|
|
215
|
-
const {
|
|
216
|
-
products,
|
|
217
|
-
strategyConfigs,
|
|
218
|
-
channel
|
|
219
|
-
} = input;
|
|
194
|
+
const { products, strategyConfigs, channel } = input;
|
|
220
195
|
const configs = strategyConfigs || this.strategyConfigs;
|
|
221
|
-
|
|
222
|
-
// 1. 先获取每个商品的促销结果
|
|
223
196
|
const productResults = this.evaluateProducts(input);
|
|
224
|
-
|
|
225
|
-
// 2. 收集所有适用的促销(去重)
|
|
226
|
-
const promotionMap = new Map();
|
|
197
|
+
const promotionMap = /* @__PURE__ */ new Map();
|
|
227
198
|
for (const productResult of productResults) {
|
|
228
199
|
for (const promo of productResult.applicablePromotions) {
|
|
229
200
|
const existing = promotionMap.get(promo.strategyId);
|
|
230
201
|
const product = productResult.product;
|
|
231
|
-
|
|
232
|
-
// 计算促销数量:如果是 bundle 子商品匹配,数量 = 主商品数量 × 子商品数量
|
|
233
202
|
let promoQuantity = product.quantity;
|
|
234
|
-
if (promo.matchedBundleIndex !==
|
|
203
|
+
if (promo.matchedBundleIndex !== void 0 && product.bundle) {
|
|
235
204
|
const bundleItem = product.bundle[promo.matchedBundleIndex];
|
|
236
205
|
promoQuantity = product.quantity * (bundleItem.quantity || 1);
|
|
237
206
|
}
|
|
238
207
|
if (existing) {
|
|
239
|
-
// 已存在,添加商品到列表
|
|
240
208
|
existing.applicableProducts.push(product);
|
|
241
209
|
existing.totalQuantity += promoQuantity;
|
|
242
210
|
existing.totalAmount += product.price * product.quantity;
|
|
243
|
-
// 记录每个商品的 matchedBundleIndex
|
|
244
211
|
if (!existing.productMatchedBundleIndexMap) {
|
|
245
|
-
existing.productMatchedBundleIndexMap = new Map();
|
|
212
|
+
existing.productMatchedBundleIndexMap = /* @__PURE__ */ new Map();
|
|
246
213
|
}
|
|
247
214
|
existing.productMatchedBundleIndexMap.set(product.id, promo.matchedBundleIndex);
|
|
248
215
|
} else {
|
|
249
|
-
|
|
250
|
-
const productMatchedBundleIndexMap = new Map();
|
|
216
|
+
const productMatchedBundleIndexMap = /* @__PURE__ */ new Map();
|
|
251
217
|
productMatchedBundleIndexMap.set(product.id, promo.matchedBundleIndex);
|
|
252
218
|
promotionMap.set(promo.strategyId, {
|
|
253
219
|
...promo,
|
|
@@ -260,28 +226,30 @@ class PromotionEvaluator {
|
|
|
260
226
|
}
|
|
261
227
|
}
|
|
262
228
|
const applicablePromotions = Array.from(promotionMap.values());
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
229
|
+
const promotionGroups = applicablePromotions.map(
|
|
230
|
+
(promo) => {
|
|
231
|
+
var _a;
|
|
232
|
+
return {
|
|
233
|
+
strategyId: promo.strategyId,
|
|
234
|
+
strategyName: promo.strategyName,
|
|
235
|
+
strategyMetadata: (_a = promo.strategyConfig) == null ? void 0 : _a.metadata,
|
|
236
|
+
strategyConfig: promo.strategyConfig,
|
|
237
|
+
actionType: promo.actionType,
|
|
238
|
+
actionDetail: promo.actionDetail,
|
|
239
|
+
products: promo.applicableProducts,
|
|
240
|
+
totalQuantity: promo.totalQuantity,
|
|
241
|
+
totalAmount: promo.totalAmount,
|
|
242
|
+
// 传递 productMatchedBundleIndexMap 到 group
|
|
243
|
+
productMatchedBundleIndexMap: promo.productMatchedBundleIndexMap
|
|
244
|
+
};
|
|
245
|
+
}
|
|
246
|
+
);
|
|
278
247
|
return {
|
|
279
248
|
productResults,
|
|
280
249
|
applicablePromotions,
|
|
281
250
|
promotionGroups
|
|
282
251
|
};
|
|
283
252
|
}
|
|
284
|
-
|
|
285
253
|
/**
|
|
286
254
|
* 评估购物车并计算定价
|
|
287
255
|
*
|
|
@@ -293,70 +261,60 @@ class PromotionEvaluator {
|
|
|
293
261
|
* @returns 带定价的购物车评估结果
|
|
294
262
|
*/
|
|
295
263
|
evaluateCartWithPricing(input) {
|
|
296
|
-
const {
|
|
297
|
-
products
|
|
298
|
-
} = input;
|
|
299
|
-
|
|
300
|
-
// 1. 先调用 evaluateCart 获取分组结果
|
|
264
|
+
const { products } = input;
|
|
301
265
|
const cartResult = this.evaluateCart(input);
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
// key: `${product_id}_${product_variant_id}`
|
|
308
|
-
const processedQuantityMap = new Map();
|
|
309
|
-
|
|
310
|
-
// 4. 处理每个促销组,收集结果
|
|
266
|
+
const sortedGroups = this.sortPromotionGroupsByPriority(
|
|
267
|
+
cartResult.promotionGroups,
|
|
268
|
+
cartResult.applicablePromotions
|
|
269
|
+
);
|
|
270
|
+
const processedQuantityMap = /* @__PURE__ */ new Map();
|
|
311
271
|
const pricedProducts = [];
|
|
312
272
|
const gifts = [];
|
|
313
|
-
let totalOriginalAmount = new
|
|
314
|
-
let totalFinalAmount = new
|
|
273
|
+
let totalOriginalAmount = new import_decimal.default(0);
|
|
274
|
+
let totalFinalAmount = new import_decimal.default(0);
|
|
315
275
|
for (const group of sortedGroups) {
|
|
316
|
-
const {
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
const result = this.processXItemsForYPrice(group, processedQuantityMap, matchedBundleIndexMap);
|
|
276
|
+
const { actionType } = group;
|
|
277
|
+
const matchedBundleIndexMap = group.productMatchedBundleIndexMap || /* @__PURE__ */ new Map();
|
|
278
|
+
if (actionType === import_type.PROMOTION_ACTION_TYPES.X_ITEMS_FOR_Y_PRICE) {
|
|
279
|
+
const result = this.processXItemsForYPrice(
|
|
280
|
+
group,
|
|
281
|
+
processedQuantityMap,
|
|
282
|
+
matchedBundleIndexMap
|
|
283
|
+
);
|
|
325
284
|
pricedProducts.push(...result.products);
|
|
326
285
|
totalOriginalAmount = totalOriginalAmount.plus(result.originalAmount);
|
|
327
286
|
totalFinalAmount = totalFinalAmount.plus(result.finalAmount);
|
|
328
|
-
} else if (actionType ===
|
|
329
|
-
|
|
330
|
-
|
|
287
|
+
} else if (actionType === import_type.PROMOTION_ACTION_TYPES.BUY_X_GET_Y_FREE) {
|
|
288
|
+
const result = this.processBuyXGetYFree(
|
|
289
|
+
group,
|
|
290
|
+
processedQuantityMap,
|
|
291
|
+
matchedBundleIndexMap
|
|
292
|
+
);
|
|
331
293
|
pricedProducts.push(...result.products);
|
|
332
294
|
totalOriginalAmount = totalOriginalAmount.plus(result.originalAmount);
|
|
333
295
|
totalFinalAmount = totalFinalAmount.plus(result.finalAmount);
|
|
334
|
-
|
|
335
|
-
// 添加赠品信息
|
|
336
296
|
if (result.giftInfo) {
|
|
337
297
|
gifts.push(result.giftInfo);
|
|
338
298
|
}
|
|
339
299
|
}
|
|
340
300
|
}
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
301
|
+
const remainingProducts = this.getRemainingProducts(
|
|
302
|
+
products,
|
|
303
|
+
processedQuantityMap
|
|
304
|
+
);
|
|
344
305
|
for (const product of remainingProducts) {
|
|
345
|
-
const amount = new
|
|
306
|
+
const amount = new import_decimal.default(product.price).mul(product.quantity);
|
|
346
307
|
totalOriginalAmount = totalOriginalAmount.plus(amount);
|
|
347
308
|
totalFinalAmount = totalFinalAmount.plus(amount);
|
|
348
309
|
}
|
|
349
310
|
pricedProducts.push(...remainingProducts);
|
|
350
|
-
|
|
351
|
-
// 6. 计算总优惠金额
|
|
352
311
|
const totalDiscount = totalOriginalAmount.minus(totalFinalAmount);
|
|
353
312
|
return {
|
|
354
313
|
products: pricedProducts,
|
|
355
314
|
gifts,
|
|
356
|
-
totalDiscount:
|
|
315
|
+
totalDiscount: import_decimal.default.max(0, totalDiscount).toNumber()
|
|
357
316
|
};
|
|
358
317
|
}
|
|
359
|
-
|
|
360
318
|
/**
|
|
361
319
|
* 获取商品适用的促销列表
|
|
362
320
|
*
|
|
@@ -367,13 +325,13 @@ class PromotionEvaluator {
|
|
|
367
325
|
* @returns 适用的促销列表
|
|
368
326
|
*/
|
|
369
327
|
getProductPromotions(product, strategyConfigs) {
|
|
328
|
+
var _a;
|
|
370
329
|
const results = this.evaluateProducts({
|
|
371
330
|
products: [product],
|
|
372
331
|
strategyConfigs
|
|
373
332
|
});
|
|
374
|
-
return results[0]
|
|
333
|
+
return ((_a = results[0]) == null ? void 0 : _a.applicablePromotions) || [];
|
|
375
334
|
}
|
|
376
|
-
|
|
377
335
|
/**
|
|
378
336
|
* 获取商品的促销标签
|
|
379
337
|
*
|
|
@@ -385,13 +343,12 @@ class PromotionEvaluator {
|
|
|
385
343
|
*/
|
|
386
344
|
getProductPromotionTags(product, strategyConfigs) {
|
|
387
345
|
const promotions = this.getProductPromotions(product, strategyConfigs);
|
|
388
|
-
return promotions.filter(promo => promo.display).map(promo => ({
|
|
346
|
+
return promotions.filter((promo) => promo.display).map((promo) => ({
|
|
389
347
|
text: this.getLocalizedName(promo.display.text),
|
|
390
348
|
type: promo.display.type,
|
|
391
349
|
strategyId: promo.strategyId
|
|
392
350
|
}));
|
|
393
351
|
}
|
|
394
|
-
|
|
395
352
|
/**
|
|
396
353
|
* 查找商品适用的策略配置
|
|
397
354
|
*
|
|
@@ -401,9 +358,10 @@ class PromotionEvaluator {
|
|
|
401
358
|
*/
|
|
402
359
|
findApplicableStrategies(product, strategyConfigs) {
|
|
403
360
|
const configs = strategyConfigs || this.strategyConfigs;
|
|
404
|
-
return configs.filter(
|
|
361
|
+
return configs.filter(
|
|
362
|
+
(config) => this.isProductInStrategy(product, config)
|
|
363
|
+
);
|
|
405
364
|
}
|
|
406
|
-
|
|
407
365
|
/**
|
|
408
366
|
* 批量获取商品列表的适用策略信息
|
|
409
367
|
*
|
|
@@ -415,30 +373,23 @@ class PromotionEvaluator {
|
|
|
415
373
|
* @returns 每个商品的适用策略完整数据
|
|
416
374
|
*/
|
|
417
375
|
getProductsApplicableStrategies(input, matchVariant = true) {
|
|
418
|
-
const {
|
|
419
|
-
products,
|
|
420
|
-
strategyConfigs,
|
|
421
|
-
channel
|
|
422
|
-
} = input;
|
|
376
|
+
const { products, strategyConfigs, channel } = input;
|
|
423
377
|
const configs = strategyConfigs || this.strategyConfigs;
|
|
424
378
|
const results = [];
|
|
425
379
|
for (const product of products) {
|
|
426
380
|
const applicableStrategies = [];
|
|
427
|
-
|
|
428
|
-
// 遍历所有策略,检查此商品是否适用
|
|
429
381
|
for (const config of configs) {
|
|
430
382
|
let contextProduct = product;
|
|
431
383
|
if (!matchVariant) {
|
|
432
|
-
// 只匹配 product_id,忽略 product_variant_id
|
|
433
384
|
const productMatchRule = this.findProductMatchRule(config);
|
|
434
385
|
if (productMatchRule) {
|
|
435
386
|
const configProducts = productMatchRule.value;
|
|
436
|
-
const matchedConfig = configProducts.find(
|
|
387
|
+
const matchedConfig = configProducts.find(
|
|
388
|
+
(item) => item.product_id === product.product_id
|
|
389
|
+
);
|
|
437
390
|
if (!matchedConfig) {
|
|
438
|
-
// product_id 不在策略适用范围内,跳过
|
|
439
391
|
continue;
|
|
440
392
|
}
|
|
441
|
-
// 用配置中的 variant_id 构造上下文,确保引擎 product_match 通过
|
|
442
393
|
if (matchedConfig.product_variant_id !== 0 && matchedConfig.product_variant_id !== product.product_variant_id) {
|
|
443
394
|
contextProduct = {
|
|
444
395
|
...product,
|
|
@@ -452,8 +403,6 @@ class PromotionEvaluator {
|
|
|
452
403
|
currentProduct: contextProduct,
|
|
453
404
|
channel
|
|
454
405
|
});
|
|
455
|
-
|
|
456
|
-
// 评估策略
|
|
457
406
|
const result = this.engine.evaluate(config, context);
|
|
458
407
|
if (result.applicable && result.matchedActions.length > 0) {
|
|
459
408
|
const action = result.matchedActions[0];
|
|
@@ -463,11 +412,11 @@ class PromotionEvaluator {
|
|
|
463
412
|
channel
|
|
464
413
|
});
|
|
465
414
|
if (transformedResult.actionDetail) {
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
415
|
+
const { requiredQuantity, eligibleProducts } = this.getPromotionRequirements(
|
|
416
|
+
action.type,
|
|
417
|
+
transformedResult.actionDetail,
|
|
418
|
+
config
|
|
419
|
+
);
|
|
471
420
|
applicableStrategies.push({
|
|
472
421
|
strategyId: config.metadata.id,
|
|
473
422
|
strategyName: config.metadata.name,
|
|
@@ -490,7 +439,6 @@ class PromotionEvaluator {
|
|
|
490
439
|
}
|
|
491
440
|
return results;
|
|
492
441
|
}
|
|
493
|
-
|
|
494
442
|
/**
|
|
495
443
|
* 获取促销所需数量和可参与商品列表
|
|
496
444
|
*
|
|
@@ -501,50 +449,41 @@ class PromotionEvaluator {
|
|
|
501
449
|
*/
|
|
502
450
|
getPromotionRequirements(actionType, actionDetail, config) {
|
|
503
451
|
let requiredQuantity = 0;
|
|
504
|
-
|
|
505
|
-
// 根据促销类型获取所需数量
|
|
506
|
-
if (actionType === 'BUY_X_GET_Y_FREE') {
|
|
452
|
+
if (actionType === "BUY_X_GET_Y_FREE") {
|
|
507
453
|
const detail = actionDetail;
|
|
508
454
|
requiredQuantity = detail.buyQuantity || 0;
|
|
509
|
-
} else if (actionType ===
|
|
455
|
+
} else if (actionType === "X_ITEMS_FOR_Y_PRICE") {
|
|
510
456
|
const detail = actionDetail;
|
|
511
457
|
requiredQuantity = detail.x || 0;
|
|
512
458
|
}
|
|
513
|
-
|
|
514
|
-
// 获取可参与商品列表
|
|
515
459
|
const productMatchRule = this.findProductMatchRule(config);
|
|
516
|
-
const eligibleProducts = productMatchRule
|
|
460
|
+
const eligibleProducts = (productMatchRule == null ? void 0 : productMatchRule.value) || [];
|
|
517
461
|
return {
|
|
518
462
|
requiredQuantity,
|
|
519
463
|
eligibleProducts
|
|
520
464
|
};
|
|
521
465
|
}
|
|
522
|
-
|
|
523
466
|
// ============================================
|
|
524
467
|
// 私有辅助方法
|
|
525
468
|
// ============================================
|
|
526
|
-
|
|
527
469
|
/**
|
|
528
470
|
* 按优先级排序促销组
|
|
529
471
|
* 优先级从 strategyConfig.actions[0].priority 获取,数值越小优先级越高
|
|
530
472
|
*/
|
|
531
473
|
sortPromotionGroupsByPriority(groups, promotions) {
|
|
532
|
-
|
|
533
|
-
const priorityMap = new Map();
|
|
474
|
+
var _a, _b;
|
|
475
|
+
const priorityMap = /* @__PURE__ */ new Map();
|
|
534
476
|
for (const promo of promotions) {
|
|
535
|
-
const actions = promo.strategyConfig
|
|
536
|
-
const priority = actions[0]
|
|
477
|
+
const actions = ((_a = promo.strategyConfig) == null ? void 0 : _a.actions) || [];
|
|
478
|
+
const priority = ((_b = actions[0]) == null ? void 0 : _b.priority) ?? 999;
|
|
537
479
|
priorityMap.set(promo.strategyId, priority);
|
|
538
480
|
}
|
|
539
|
-
|
|
540
|
-
// 按优先级排序(数值越小优先级越高)
|
|
541
481
|
return [...groups].sort((a, b) => {
|
|
542
482
|
const priorityA = priorityMap.get(a.strategyId) ?? 999;
|
|
543
483
|
const priorityB = priorityMap.get(b.strategyId) ?? 999;
|
|
544
484
|
return priorityA - priorityB;
|
|
545
485
|
});
|
|
546
486
|
}
|
|
547
|
-
|
|
548
487
|
/**
|
|
549
488
|
* 处理 X件Y元 促销
|
|
550
489
|
*
|
|
@@ -556,32 +495,19 @@ class PromotionEvaluator {
|
|
|
556
495
|
* 注意:每个商品按 id 独立处理,不会合并不同 id 的商品
|
|
557
496
|
*/
|
|
558
497
|
processXItemsForYPrice(group, processedQuantityMap, matchedBundleIndexMap) {
|
|
559
|
-
const {
|
|
560
|
-
strategyId,
|
|
561
|
-
strategyMetadata,
|
|
562
|
-
actionDetail,
|
|
563
|
-
products: groupProducts
|
|
564
|
-
} = group;
|
|
498
|
+
const { strategyId, strategyMetadata, actionDetail, products: groupProducts } = group;
|
|
565
499
|
const detail = actionDetail;
|
|
566
|
-
const {
|
|
567
|
-
x,
|
|
568
|
-
price: groupPrice,
|
|
569
|
-
cumulative
|
|
570
|
-
} = detail;
|
|
500
|
+
const { x, price: groupPrice, cumulative } = detail;
|
|
571
501
|
const result = [];
|
|
572
|
-
let originalAmount = new
|
|
573
|
-
let finalAmount = new
|
|
574
|
-
|
|
575
|
-
// 1. 展开所有商品为单件列表(排除已处理的数量)
|
|
576
|
-
// 每个单件记录来自哪个原商品(通过 product.id)
|
|
502
|
+
let originalAmount = new import_decimal.default(0);
|
|
503
|
+
let finalAmount = new import_decimal.default(0);
|
|
577
504
|
const unitProducts = [];
|
|
578
505
|
for (const product of groupProducts) {
|
|
579
506
|
const key = this.getProductKey(product);
|
|
580
507
|
const processedQty = processedQuantityMap.get(key) || 0;
|
|
581
508
|
const availableQty = product.quantity - processedQty;
|
|
582
|
-
if (availableQty <= 0)
|
|
583
|
-
|
|
584
|
-
// 展开为单件,每个单件都记录来源商品
|
|
509
|
+
if (availableQty <= 0)
|
|
510
|
+
continue;
|
|
585
511
|
for (let i = 0; i < availableQty; i++) {
|
|
586
512
|
unitProducts.push({
|
|
587
513
|
product,
|
|
@@ -592,67 +518,47 @@ class PromotionEvaluator {
|
|
|
592
518
|
}
|
|
593
519
|
}
|
|
594
520
|
if (unitProducts.length === 0) {
|
|
595
|
-
return {
|
|
596
|
-
products: [],
|
|
597
|
-
originalAmount: 0,
|
|
598
|
-
finalAmount: 0
|
|
599
|
-
};
|
|
521
|
+
return { products: [], originalAmount: 0, finalAmount: 0 };
|
|
600
522
|
}
|
|
601
|
-
|
|
602
|
-
// 2. 排序:优先选择来源数量是 x 倍数的商品(可完整凑组不拆分),然后按价格从高到低
|
|
603
523
|
unitProducts.sort((a, b) => {
|
|
604
|
-
// 优先级1:来源购物车项数量是 x 的倍数的排前面(这些商品可以完整凑组,避免拆分)
|
|
605
524
|
const aIsMultiple = a.sourceAvailableQty > 0 && a.sourceAvailableQty % x === 0 ? 1 : 0;
|
|
606
525
|
const bIsMultiple = b.sourceAvailableQty > 0 && b.sourceAvailableQty % x === 0 ? 1 : 0;
|
|
607
|
-
if (aIsMultiple !== bIsMultiple)
|
|
608
|
-
|
|
526
|
+
if (aIsMultiple !== bIsMultiple)
|
|
527
|
+
return bIsMultiple - aIsMultiple;
|
|
609
528
|
return b.price - a.price;
|
|
610
529
|
});
|
|
611
|
-
|
|
612
|
-
// 3. 计算可以凑成多少组(cumulative 缺省视为 true,与 adapter 一致)
|
|
613
530
|
const totalUnits = unitProducts.length;
|
|
614
531
|
const isCumulative = cumulative !== false;
|
|
615
532
|
const groupCount = isCumulative ? Math.floor(totalUnits / x) : totalUnits >= x ? 1 : 0;
|
|
616
533
|
const promotionUnits = groupCount * x;
|
|
617
|
-
|
|
618
|
-
// 4. 分离参与促销的和不参与的商品
|
|
619
534
|
const inPromotionUnits = unitProducts.slice(0, promotionUnits);
|
|
620
535
|
const notInPromotionUnits = unitProducts.slice(promotionUnits);
|
|
621
|
-
|
|
622
|
-
// 5. 按原商品 id 统计参与促销和不参与的数量
|
|
623
|
-
const promotionQtyByProductId = new Map();
|
|
536
|
+
const promotionQtyByProductId = /* @__PURE__ */ new Map();
|
|
624
537
|
for (const unit of inPromotionUnits) {
|
|
625
538
|
const id = unit.productId;
|
|
626
539
|
promotionQtyByProductId.set(id, (promotionQtyByProductId.get(id) || 0) + 1);
|
|
627
540
|
}
|
|
628
|
-
const remainingQtyByProductId = new Map();
|
|
541
|
+
const remainingQtyByProductId = /* @__PURE__ */ new Map();
|
|
629
542
|
for (const unit of notInPromotionUnits) {
|
|
630
543
|
const id = unit.productId;
|
|
631
544
|
remainingQtyByProductId.set(id, (remainingQtyByProductId.get(id) || 0) + 1);
|
|
632
545
|
}
|
|
633
|
-
|
|
634
|
-
// 6. 为每个参与促销的单件计算均摊价格
|
|
635
|
-
// 按组内原价比例均摊
|
|
636
|
-
// 记录每个原商品的均摊价格累加(用于后续聚合)
|
|
637
|
-
const allocatedPricesByProductId = new Map();
|
|
546
|
+
const allocatedPricesByProductId = /* @__PURE__ */ new Map();
|
|
638
547
|
for (let g = 0; g < groupCount; g++) {
|
|
639
548
|
const startIdx = g * x;
|
|
640
549
|
const endIdx = startIdx + x;
|
|
641
550
|
const groupUnits = inPromotionUnits.slice(startIdx, endIdx);
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
let allocatedSum = new _decimal.default(0);
|
|
551
|
+
const groupOriginalTotal = groupUnits.reduce(
|
|
552
|
+
(sum, u) => sum.plus(u.price),
|
|
553
|
+
new import_decimal.default(0)
|
|
554
|
+
);
|
|
555
|
+
const groupPriceDecimal = new import_decimal.default(groupPrice);
|
|
556
|
+
let allocatedSum = new import_decimal.default(0);
|
|
649
557
|
for (let i = 0; i < groupUnits.length; i++) {
|
|
650
558
|
const unit = groupUnits[i];
|
|
651
559
|
const isLastUnit = i === groupUnits.length - 1;
|
|
652
|
-
const ratio = groupOriginalTotal.eq(0) ? new
|
|
560
|
+
const ratio = groupOriginalTotal.eq(0) ? new import_decimal.default(0) : new import_decimal.default(unit.price).div(groupOriginalTotal);
|
|
653
561
|
const allocatedDecimal = isLastUnit ? groupPriceDecimal.minus(allocatedSum) : groupPriceDecimal.mul(ratio);
|
|
654
|
-
|
|
655
|
-
// 记录这个单件的均摊价格
|
|
656
562
|
const productId = unit.productId;
|
|
657
563
|
const prices = allocatedPricesByProductId.get(productId) || [];
|
|
658
564
|
prices.push(allocatedDecimal);
|
|
@@ -660,25 +566,23 @@ class PromotionEvaluator {
|
|
|
660
566
|
allocatedSum = allocatedSum.plus(allocatedDecimal);
|
|
661
567
|
}
|
|
662
568
|
}
|
|
663
|
-
|
|
664
|
-
// 7. 为每个原商品生成结果
|
|
665
569
|
for (const product of groupProducts) {
|
|
666
570
|
const key = this.getProductKey(product);
|
|
667
571
|
const processedQty = processedQuantityMap.get(key) || 0;
|
|
668
572
|
const availableQty = product.quantity - processedQty;
|
|
669
|
-
if (availableQty <= 0)
|
|
573
|
+
if (availableQty <= 0)
|
|
574
|
+
continue;
|
|
670
575
|
const promoQty = promotionQtyByProductId.get(product.id) || 0;
|
|
671
576
|
const remainingQty = remainingQtyByProductId.get(product.id) || 0;
|
|
672
577
|
const allocatedPrices = allocatedPricesByProductId.get(product.id) || [];
|
|
673
|
-
|
|
674
|
-
// 获取当前商品的 matchedBundleIndex
|
|
675
|
-
const matchedBundleIndex = matchedBundleIndexMap?.get(product.id);
|
|
578
|
+
const matchedBundleIndex = matchedBundleIndexMap == null ? void 0 : matchedBundleIndexMap.get(product.id);
|
|
676
579
|
if (promoQty > 0 && remainingQty === 0) {
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
const
|
|
580
|
+
const totalAllocated = allocatedPrices.reduce(
|
|
581
|
+
(sum, p) => sum.plus(p),
|
|
582
|
+
new import_decimal.default(0)
|
|
583
|
+
);
|
|
584
|
+
const productOriginalAmount = new import_decimal.default(product.price).mul(promoQty);
|
|
585
|
+
const actualTotal = import_decimal.default.min(totalAllocated, productOriginalAmount);
|
|
682
586
|
const finalPricePerUnit = this.formatPrice(actualTotal.div(promoQty));
|
|
683
587
|
result.push({
|
|
684
588
|
...product,
|
|
@@ -693,26 +597,25 @@ class PromotionEvaluator {
|
|
|
693
597
|
originalAmount = originalAmount.plus(productOriginalAmount);
|
|
694
598
|
finalAmount = finalAmount.plus(actualTotal);
|
|
695
599
|
} else if (promoQty === 0 && remainingQty > 0) {
|
|
696
|
-
// 全部不参与促销:保留原 id,finalPrice = 原价
|
|
697
600
|
result.push({
|
|
698
601
|
...product,
|
|
699
602
|
quantity: remainingQty,
|
|
700
603
|
finalPrice: this.formatPrice(product.price),
|
|
701
|
-
strategyId:
|
|
702
|
-
strategyMetadata:
|
|
604
|
+
strategyId: void 0,
|
|
605
|
+
strategyMetadata: void 0,
|
|
703
606
|
inPromotion: false,
|
|
704
607
|
isSplit: false
|
|
705
608
|
});
|
|
706
|
-
const amount = new
|
|
609
|
+
const amount = new import_decimal.default(product.price).mul(remainingQty);
|
|
707
610
|
originalAmount = originalAmount.plus(amount);
|
|
708
611
|
finalAmount = finalAmount.plus(amount);
|
|
709
612
|
} else if (promoQty > 0 && remainingQty > 0) {
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
const promoOriginalAmount = new
|
|
715
|
-
const actualTotal =
|
|
613
|
+
const totalAllocated = allocatedPrices.reduce(
|
|
614
|
+
(sum, p) => sum.plus(p),
|
|
615
|
+
new import_decimal.default(0)
|
|
616
|
+
);
|
|
617
|
+
const promoOriginalAmount = new import_decimal.default(product.price).mul(promoQty);
|
|
618
|
+
const actualTotal = import_decimal.default.min(totalAllocated, promoOriginalAmount);
|
|
716
619
|
const finalPricePerUnit = this.formatPrice(actualTotal.div(promoQty));
|
|
717
620
|
result.push({
|
|
718
621
|
...product,
|
|
@@ -728,50 +631,44 @@ class PromotionEvaluator {
|
|
|
728
631
|
});
|
|
729
632
|
originalAmount = originalAmount.plus(promoOriginalAmount);
|
|
730
633
|
finalAmount = finalAmount.plus(actualTotal);
|
|
731
|
-
|
|
732
|
-
// 不参与促销的部分:保留原 id
|
|
733
634
|
result.push({
|
|
734
635
|
...product,
|
|
735
636
|
quantity: remainingQty,
|
|
736
637
|
finalPrice: this.formatPrice(product.price),
|
|
737
|
-
strategyId:
|
|
738
|
-
strategyMetadata:
|
|
638
|
+
strategyId: void 0,
|
|
639
|
+
strategyMetadata: void 0,
|
|
739
640
|
inPromotion: false,
|
|
740
641
|
isSplit: true
|
|
741
642
|
});
|
|
742
|
-
const remainingAmount = new
|
|
643
|
+
const remainingAmount = new import_decimal.default(product.price).mul(remainingQty);
|
|
743
644
|
originalAmount = originalAmount.plus(remainingAmount);
|
|
744
645
|
finalAmount = finalAmount.plus(remainingAmount);
|
|
745
646
|
}
|
|
746
|
-
|
|
747
|
-
// 更新已处理数量
|
|
748
647
|
processedQuantityMap.set(key, processedQty + availableQty);
|
|
749
648
|
}
|
|
750
|
-
|
|
751
|
-
// 8. 尾差调整:确保促销商品的 finalPrice × quantity 之和精确等于 groupPrice × groupCount
|
|
752
649
|
if (groupCount > 0) {
|
|
753
|
-
const expectedPromoTotal = new
|
|
754
|
-
const promoItems = result.filter(p => p.inPromotion);
|
|
755
|
-
let actualPromoTotal = new
|
|
650
|
+
const expectedPromoTotal = new import_decimal.default(groupPrice).mul(groupCount);
|
|
651
|
+
const promoItems = result.filter((p) => p.inPromotion);
|
|
652
|
+
let actualPromoTotal = new import_decimal.default(0);
|
|
756
653
|
for (const p of promoItems) {
|
|
757
|
-
actualPromoTotal = actualPromoTotal.plus(
|
|
654
|
+
actualPromoTotal = actualPromoTotal.plus(
|
|
655
|
+
new import_decimal.default(p.finalPrice).mul(p.quantity)
|
|
656
|
+
);
|
|
758
657
|
}
|
|
759
658
|
const roundingDiff = actualPromoTotal.minus(expectedPromoTotal);
|
|
760
659
|
if (!roundingDiff.eq(0)) {
|
|
761
|
-
|
|
762
|
-
const adjustTarget = promoItems.find(p => p.quantity === 1);
|
|
660
|
+
const adjustTarget = promoItems.find((p) => p.quantity === 1);
|
|
763
661
|
if (adjustTarget) {
|
|
764
|
-
adjustTarget.finalPrice = this.formatPrice(
|
|
662
|
+
adjustTarget.finalPrice = this.formatPrice(
|
|
663
|
+
new import_decimal.default(adjustTarget.finalPrice).minus(roundingDiff)
|
|
664
|
+
);
|
|
765
665
|
} else if (promoItems.length > 0) {
|
|
766
|
-
// 没有 quantity=1 的促销商品,从最后一个促销商品中拆出1件做尾差调整
|
|
767
666
|
const lastPromo = promoItems[promoItems.length - 1];
|
|
768
|
-
const adjustedUnitPrice = this.formatPrice(
|
|
769
|
-
|
|
770
|
-
|
|
667
|
+
const adjustedUnitPrice = this.formatPrice(
|
|
668
|
+
new import_decimal.default(lastPromo.finalPrice).minus(roundingDiff)
|
|
669
|
+
);
|
|
771
670
|
lastPromo.quantity -= 1;
|
|
772
671
|
lastPromo.isSplit = true;
|
|
773
|
-
|
|
774
|
-
// 拆出1件用调整后的价格
|
|
775
672
|
result.push({
|
|
776
673
|
...lastPromo,
|
|
777
674
|
id: this.generateRandomId(),
|
|
@@ -781,11 +678,11 @@ class PromotionEvaluator {
|
|
|
781
678
|
isSplit: true
|
|
782
679
|
});
|
|
783
680
|
}
|
|
784
|
-
|
|
785
|
-
// 重新计算 finalAmount
|
|
786
|
-
finalAmount = new _decimal.default(0);
|
|
681
|
+
finalAmount = new import_decimal.default(0);
|
|
787
682
|
for (const p of result) {
|
|
788
|
-
finalAmount = finalAmount.plus(
|
|
683
|
+
finalAmount = finalAmount.plus(
|
|
684
|
+
new import_decimal.default(p.finalPrice).mul(p.quantity)
|
|
685
|
+
);
|
|
789
686
|
}
|
|
790
687
|
}
|
|
791
688
|
}
|
|
@@ -795,7 +692,6 @@ class PromotionEvaluator {
|
|
|
795
692
|
finalAmount: finalAmount.toNumber()
|
|
796
693
|
};
|
|
797
694
|
}
|
|
798
|
-
|
|
799
695
|
/**
|
|
800
696
|
* 处理 买X送Y 促销
|
|
801
697
|
*
|
|
@@ -811,56 +707,34 @@ class PromotionEvaluator {
|
|
|
811
707
|
products: groupProducts
|
|
812
708
|
} = group;
|
|
813
709
|
const detail = actionDetail;
|
|
814
|
-
const {
|
|
815
|
-
buyQuantity,
|
|
816
|
-
freeQuantity,
|
|
817
|
-
cumulative,
|
|
818
|
-
giftProducts
|
|
819
|
-
} = detail;
|
|
710
|
+
const { buyQuantity, freeQuantity, cumulative, giftProducts } = detail;
|
|
820
711
|
const result = [];
|
|
821
|
-
let originalAmount = new
|
|
822
|
-
let finalAmount = new
|
|
823
|
-
|
|
824
|
-
// 1. 先计算可用商品总数量(第一次遍历)
|
|
825
|
-
// 对于 bundle 子商品匹配,促销数量 = 主商品数量 × bundle子商品数量
|
|
712
|
+
let originalAmount = new import_decimal.default(0);
|
|
713
|
+
let finalAmount = new import_decimal.default(0);
|
|
826
714
|
let totalPromoQty = 0;
|
|
827
|
-
const productInfoMap = new Map();
|
|
715
|
+
const productInfoMap = /* @__PURE__ */ new Map();
|
|
828
716
|
for (const product of groupProducts) {
|
|
829
717
|
const key = this.getProductKey(product);
|
|
830
718
|
const processedQty = processedQuantityMap.get(key) || 0;
|
|
831
719
|
const availableQty = product.quantity - processedQty;
|
|
832
|
-
if (availableQty <= 0)
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
const matchedBundleIndex = matchedBundleIndexMap?.get(product.id);
|
|
836
|
-
|
|
837
|
-
// 计算用于促销的数量
|
|
720
|
+
if (availableQty <= 0)
|
|
721
|
+
continue;
|
|
722
|
+
const matchedBundleIndex = matchedBundleIndexMap == null ? void 0 : matchedBundleIndexMap.get(product.id);
|
|
838
723
|
let promoQty = availableQty;
|
|
839
|
-
if (matchedBundleIndex !==
|
|
724
|
+
if (matchedBundleIndex !== void 0 && product.bundle) {
|
|
840
725
|
const bundleItem = product.bundle[matchedBundleIndex];
|
|
841
|
-
// 促销数量 = 主商品数量 × bundle子商品数量
|
|
842
726
|
promoQty = availableQty * (bundleItem.quantity || 1);
|
|
843
727
|
}
|
|
844
728
|
totalPromoQty += promoQty;
|
|
845
|
-
productInfoMap.set(key, {
|
|
846
|
-
availableQty,
|
|
847
|
-
promoQty,
|
|
848
|
-
matchedBundleIndex
|
|
849
|
-
});
|
|
729
|
+
productInfoMap.set(key, { availableQty, promoQty, matchedBundleIndex });
|
|
850
730
|
}
|
|
851
|
-
|
|
852
|
-
// 2. 判断是否满足促销条件
|
|
853
731
|
const isFulfilled = totalPromoQty >= buyQuantity;
|
|
854
|
-
|
|
855
|
-
// 3. 生成商品结果(第二次遍历)
|
|
856
732
|
for (const product of groupProducts) {
|
|
857
733
|
const key = this.getProductKey(product);
|
|
858
734
|
const info = productInfoMap.get(key);
|
|
859
|
-
if (!info || info.availableQty <= 0)
|
|
735
|
+
if (!info || info.availableQty <= 0)
|
|
736
|
+
continue;
|
|
860
737
|
const processedQty = processedQuantityMap.get(key) || 0;
|
|
861
|
-
|
|
862
|
-
// 商品本身价格不变,只是触发赠品
|
|
863
|
-
// inPromotion 只有在满足条件时才为 true
|
|
864
738
|
result.push({
|
|
865
739
|
...product,
|
|
866
740
|
quantity: info.availableQty,
|
|
@@ -872,21 +746,15 @@ class PromotionEvaluator {
|
|
|
872
746
|
isSplit: info.availableQty < product.quantity,
|
|
873
747
|
matchedBundleIndex: info.matchedBundleIndex
|
|
874
748
|
});
|
|
875
|
-
const amount = new
|
|
749
|
+
const amount = new import_decimal.default(product.price).mul(info.availableQty);
|
|
876
750
|
originalAmount = originalAmount.plus(amount);
|
|
877
751
|
finalAmount = finalAmount.plus(amount);
|
|
878
|
-
|
|
879
|
-
// 更新已处理数量(使用主商品数量)
|
|
880
752
|
processedQuantityMap.set(key, processedQty + info.availableQty);
|
|
881
753
|
}
|
|
882
|
-
|
|
883
|
-
// 4. 计算赠品数量(使用促销数量)
|
|
884
754
|
let giftCount = 0;
|
|
885
755
|
if (isFulfilled) {
|
|
886
756
|
giftCount = cumulative ? Math.floor(totalPromoQty / buyQuantity) * freeQuantity : freeQuantity;
|
|
887
757
|
}
|
|
888
|
-
|
|
889
|
-
// 5. 构建赠品信息
|
|
890
758
|
const giftInfo = giftCount > 0 ? {
|
|
891
759
|
strategyId,
|
|
892
760
|
strategyName,
|
|
@@ -900,7 +768,6 @@ class PromotionEvaluator {
|
|
|
900
768
|
giftInfo
|
|
901
769
|
};
|
|
902
770
|
}
|
|
903
|
-
|
|
904
771
|
/**
|
|
905
772
|
* 获取未参与任何促销的商品
|
|
906
773
|
*/
|
|
@@ -915,7 +782,7 @@ class PromotionEvaluator {
|
|
|
915
782
|
...product,
|
|
916
783
|
quantity: remainingQty,
|
|
917
784
|
finalPrice: this.formatPrice(product.price),
|
|
918
|
-
strategyId:
|
|
785
|
+
strategyId: void 0,
|
|
919
786
|
inPromotion: false,
|
|
920
787
|
isSplit: remainingQty < product.quantity
|
|
921
788
|
});
|
|
@@ -923,14 +790,12 @@ class PromotionEvaluator {
|
|
|
923
790
|
}
|
|
924
791
|
return result;
|
|
925
792
|
}
|
|
926
|
-
|
|
927
793
|
/**
|
|
928
794
|
* 价格统一保留两位小数
|
|
929
795
|
*/
|
|
930
796
|
formatPrice(value) {
|
|
931
|
-
return new
|
|
797
|
+
return new import_decimal.default(value).toDecimalPlaces(2, import_decimal.default.ROUND_HALF_UP).toNumber();
|
|
932
798
|
}
|
|
933
|
-
|
|
934
799
|
/**
|
|
935
800
|
* 获取商品唯一标识 key
|
|
936
801
|
* 使用商品的 id 作为唯一标识,不同 id 的商品不会被合并
|
|
@@ -938,107 +803,92 @@ class PromotionEvaluator {
|
|
|
938
803
|
getProductKey(product) {
|
|
939
804
|
return String(product.id);
|
|
940
805
|
}
|
|
941
|
-
|
|
942
806
|
/**
|
|
943
807
|
* 生成随机ID
|
|
944
808
|
*/
|
|
945
809
|
generateRandomId() {
|
|
946
|
-
return Math.floor(Math.random() *
|
|
810
|
+
return Math.floor(Math.random() * 1e9) + Date.now();
|
|
947
811
|
}
|
|
948
|
-
|
|
949
812
|
/**
|
|
950
813
|
* 商品匹配结果信息
|
|
951
814
|
*/
|
|
952
815
|
getProductMatchInfo(product, config) {
|
|
953
816
|
const productMatchRule = this.findProductMatchRule(config);
|
|
954
817
|
if (!productMatchRule) {
|
|
955
|
-
|
|
956
|
-
return {
|
|
957
|
-
isMatch: true
|
|
958
|
-
};
|
|
818
|
+
return { isMatch: true };
|
|
959
819
|
}
|
|
960
820
|
const configProducts = productMatchRule.value;
|
|
961
|
-
|
|
962
|
-
// 1. 先检查主商品是否匹配
|
|
963
821
|
if (this.isProductIdMatch(product.product_id, product.product_variant_id, configProducts)) {
|
|
964
|
-
return {
|
|
965
|
-
isMatch: true
|
|
966
|
-
};
|
|
822
|
+
return { isMatch: true };
|
|
967
823
|
}
|
|
968
|
-
|
|
969
|
-
// 2. 主商品不匹配,检查 bundle 子商品
|
|
970
824
|
if (product.bundle && product.bundle.length > 0) {
|
|
971
825
|
for (let i = 0; i < product.bundle.length; i++) {
|
|
972
826
|
const bundleItem = product.bundle[i];
|
|
973
|
-
if (this.isProductIdMatch(
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
|
|
977
|
-
|
|
827
|
+
if (this.isProductIdMatch(
|
|
828
|
+
bundleItem._bundle_product_id,
|
|
829
|
+
bundleItem.product_variant_id,
|
|
830
|
+
configProducts
|
|
831
|
+
)) {
|
|
832
|
+
return { isMatch: true, matchedBundleIndex: i };
|
|
978
833
|
}
|
|
979
834
|
}
|
|
980
835
|
}
|
|
981
|
-
return {
|
|
982
|
-
isMatch: false
|
|
983
|
-
};
|
|
836
|
+
return { isMatch: false };
|
|
984
837
|
}
|
|
985
|
-
|
|
986
838
|
/**
|
|
987
839
|
* 检查商品是否在策略的适用范围内
|
|
988
840
|
*/
|
|
989
841
|
isProductInStrategy(product, config) {
|
|
990
842
|
return this.getProductMatchInfo(product, config).isMatch;
|
|
991
843
|
}
|
|
992
|
-
|
|
993
844
|
/**
|
|
994
845
|
* 查找商品匹配规则
|
|
995
846
|
*/
|
|
996
847
|
findProductMatchRule(config) {
|
|
997
|
-
|
|
848
|
+
var _a;
|
|
849
|
+
if (!((_a = config == null ? void 0 : config.conditions) == null ? void 0 : _a.rules)) {
|
|
998
850
|
return null;
|
|
999
851
|
}
|
|
1000
|
-
return config.conditions.rules.find(
|
|
852
|
+
return config.conditions.rules.find(
|
|
853
|
+
(rule) => rule.field === "productIdAndVariantId" && (rule.operator === "product_match" || rule.operator === "object_in")
|
|
854
|
+
);
|
|
1001
855
|
}
|
|
1002
|
-
|
|
1003
856
|
/**
|
|
1004
857
|
* 检查商品ID是否匹配配置列表
|
|
1005
858
|
*/
|
|
1006
859
|
isProductIdMatch(productId, productVariantId, configProducts) {
|
|
1007
|
-
return configProducts.some(config => {
|
|
1008
|
-
// 首先检查 product_id
|
|
860
|
+
return configProducts.some((config) => {
|
|
1009
861
|
if (config.product_id !== productId) {
|
|
1010
862
|
return false;
|
|
1011
863
|
}
|
|
1012
|
-
|
|
1013
|
-
// 如果配置的 product_variant_id = 0,只需要 product_id 匹配
|
|
1014
864
|
if (config.product_variant_id === 0) {
|
|
1015
865
|
return true;
|
|
1016
866
|
}
|
|
1017
|
-
|
|
1018
|
-
// 否则需要 product_variant_id 精确匹配
|
|
1019
867
|
return config.product_variant_id === productVariantId;
|
|
1020
868
|
});
|
|
1021
869
|
}
|
|
1022
|
-
|
|
1023
870
|
/**
|
|
1024
871
|
* 检查商品是否匹配配置(兼容旧方法)
|
|
1025
872
|
*/
|
|
1026
873
|
isProductMatch(product, configProducts) {
|
|
1027
874
|
return this.isProductIdMatch(product.product_id, product.product_variant_id, configProducts);
|
|
1028
875
|
}
|
|
1029
|
-
|
|
1030
876
|
/**
|
|
1031
877
|
* 获取展示配置
|
|
1032
878
|
*/
|
|
1033
879
|
getDisplayConfig(config) {
|
|
880
|
+
var _a;
|
|
1034
881
|
const custom = config.metadata.custom;
|
|
1035
|
-
if (custom
|
|
882
|
+
if ((_a = custom == null ? void 0 : custom.display) == null ? void 0 : _a.product_card) {
|
|
1036
883
|
return {
|
|
1037
884
|
text: custom.display.product_card.text,
|
|
1038
885
|
type: custom.display.product_card.type
|
|
1039
886
|
};
|
|
1040
887
|
}
|
|
1041
|
-
return
|
|
888
|
+
return void 0;
|
|
1042
889
|
}
|
|
1043
|
-
}
|
|
1044
|
-
|
|
890
|
+
};
|
|
891
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
892
|
+
0 && (module.exports = {
|
|
893
|
+
PromotionEvaluator
|
|
894
|
+
});
|