@pisell/pisellos 0.0.567 → 0.0.568
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 +6 -9
- package/dist/model/strategy/adapter/walletPass/evaluator.js +0 -2
- package/dist/model/strategy/adapter/walletPass/example.js +3 -26
- package/dist/model/strategy/adapter/walletPass/utils.js +0 -3
- package/dist/model/strategy/strategy-example.js +5 -19
- package/dist/modules/Cart/index.js +0 -3
- package/dist/modules/Cart/utils/cartProduct.js +0 -1
- package/dist/modules/Date/index.js +1 -5
- package/dist/modules/Discount/index.js +1 -4
- package/dist/modules/OpenData/index.js +0 -2
- package/dist/modules/Order/index.d.ts +1 -1
- package/dist/modules/Order/index.js +14 -41
- package/dist/modules/Payment/index.js +142 -270
- package/dist/modules/Payment/walletpass.js +4 -5
- package/dist/modules/Rules/index.js +0 -7
- package/dist/modules/Schedule/utils.js +0 -1
- package/dist/modules/Step/index.js +0 -1
- package/dist/plugins/request.js +1 -4
- package/dist/plugins/window.js +2 -6
- package/dist/server/index.js +88 -121
- package/dist/server/modules/menu/index.js +32 -41
- package/dist/server/modules/products/index.js +29 -49
- package/dist/server/modules/quotation/index.js +29 -38
- package/dist/server/modules/resource/index.js +3 -4
- package/dist/server/modules/schedule/index.js +32 -40
- package/dist/server/utils/product.js +0 -1
- package/dist/solution/BookingByStep/index.d.ts +1 -1
- package/dist/solution/BookingByStep/index.js +7 -59
- package/dist/solution/BookingByStep/utils/capacity.js +1 -11
- package/dist/solution/BookingByStep/utils/resources.js +1 -3
- package/dist/solution/BookingByStep/utils/timeslots.js +2 -43
- package/dist/solution/BookingTicket/index.d.ts +1 -1
- package/dist/solution/BookingTicket/index.js +5 -7
- package/dist/solution/BookingTicket/utils/scan/handleScan.js +25 -31
- package/dist/solution/BookingTicket/utils/scan/index.js +6 -10
- package/dist/solution/BuyTickets/index.js +9 -12
- package/dist/solution/Checkout/index.js +177 -246
- package/dist/solution/Checkout/utils/index.js +4 -16
- package/dist/solution/RegisterAndLogin/index.js +30 -76
- package/dist/solution/ScanOrder/index.js +50 -60
- package/dist/solution/ShopDiscount/index.js +2 -7
- package/dist/solution/VenueBooking/index.d.ts +5 -0
- package/dist/solution/VenueBooking/index.js +364 -327
- package/dist/utils/task.js +15 -18
- package/dist/utils/watch.js +0 -1
- package/lib/apis/picoding.js +2 -0
- package/lib/core/index.js +130 -131
- package/lib/effects/index.js +46 -57
- package/lib/index.js +92 -53
- package/lib/model/index.js +14 -21
- package/lib/model/strategy/adapter/dataVariant/adapter.js +38 -50
- package/lib/model/strategy/adapter/dataVariant/evaluator.js +162 -236
- package/lib/model/strategy/adapter/dataVariant/examples.js +229 -237
- package/lib/model/strategy/adapter/dataVariant/index.js +39 -34
- package/lib/model/strategy/adapter/dataVariant/type.js +57 -28
- package/lib/model/strategy/adapter/index.js +100 -64
- package/lib/model/strategy/adapter/itemRule/adapter.js +156 -144
- package/lib/model/strategy/adapter/itemRule/evaluator.js +36 -48
- package/lib/model/strategy/adapter/itemRule/examples.js +295 -225
- package/lib/model/strategy/adapter/itemRule/index.js +83 -57
- package/lib/model/strategy/adapter/itemRule/type.js +97 -36
- package/lib/model/strategy/adapter/promotion/adapter.js +84 -63
- package/lib/model/strategy/adapter/promotion/evaluator.js +381 -231
- package/lib/model/strategy/adapter/promotion/examples.js +139 -159
- package/lib/model/strategy/adapter/promotion/index.js +1 -49
- package/lib/model/strategy/adapter/promotion/type.js +199 -35
- package/lib/model/strategy/adapter/type.js +4 -16
- package/lib/model/strategy/adapter/walletPass/evaluator.js +180 -152
- package/lib/model/strategy/adapter/walletPass/example.js +217 -190
- package/lib/model/strategy/adapter/walletPass/index.js +75 -51
- package/lib/model/strategy/adapter/walletPass/locales.js +36 -58
- package/lib/model/strategy/adapter/walletPass/type.js +4 -16
- package/lib/model/strategy/adapter/walletPass/utils.js +652 -402
- package/lib/model/strategy/engine.js +270 -168
- package/lib/model/strategy/index.js +49 -34
- package/lib/model/strategy/strategy-example.js +243 -239
- package/lib/model/strategy/type.js +100 -40
- package/lib/modules/Account/index.js +39 -53
- package/lib/modules/Account/types.js +20 -33
- package/lib/modules/AccountList/index.js +116 -154
- package/lib/modules/AccountList/types.js +30 -31
- package/lib/modules/AccountList/utils.js +18 -30
- package/lib/modules/BaseModule.js +22 -45
- package/lib/modules/Cart/index.js +170 -124
- package/lib/modules/Cart/types.js +46 -51
- package/lib/modules/Cart/utils/cartAccount.js +40 -39
- package/lib/modules/Cart/utils/cartDate.js +56 -61
- package/lib/modules/Cart/utils/cartDiscount.js +28 -33
- package/lib/modules/Cart/utils/cartNote.js +27 -32
- package/lib/modules/Cart/utils/cartProduct.js +306 -241
- package/lib/modules/Cart/utils/cartRelationForms.js +29 -35
- package/lib/modules/Cart/utils/cartResource.js +72 -78
- package/lib/modules/Cart/utils/changePrice.js +26 -62
- package/lib/modules/Cart/utils/index.js +106 -48
- package/lib/modules/Customer/constants.js +13 -34
- package/lib/modules/Customer/index.js +216 -153
- package/lib/modules/Customer/types.js +38 -35
- package/lib/modules/Date/index.js +116 -115
- package/lib/modules/Date/types.js +16 -28
- package/lib/modules/Date/utils.js +174 -123
- package/lib/modules/Discount/index.js +117 -113
- package/lib/modules/Discount/types.js +9 -31
- package/lib/modules/Guests/index.js +30 -56
- package/lib/modules/Guests/types.js +23 -33
- package/lib/modules/Holder/index.js +209 -189
- package/lib/modules/Holder/types.js +4 -16
- package/lib/modules/Holder/utils.js +20 -49
- package/lib/modules/OpenData/index.js +63 -74
- package/lib/modules/OpenData/types.js +4 -16
- package/lib/modules/OpenData/utils.js +44 -78
- package/lib/modules/Order/index.d.ts +1 -1
- package/lib/modules/Order/index.js +332 -320
- package/lib/modules/Order/types.js +24 -33
- package/lib/modules/Order/utils.js +350 -284
- package/lib/modules/Payment/cash.js +20 -33
- package/lib/modules/Payment/cashRecommendationAlgorithm.js +145 -76
- package/lib/modules/Payment/eftpos.js +16 -30
- package/lib/modules/Payment/index.js +566 -432
- package/lib/modules/Payment/mx51.js +1 -0
- package/lib/modules/Payment/types.js +216 -107
- package/lib/modules/Payment/utils.js +50 -47
- package/lib/modules/Payment/walletpass.js +221 -203
- package/lib/modules/Product/index.js +51 -46
- package/lib/modules/Product/types.js +4 -16
- package/lib/modules/Product/utils.js +32 -33
- package/lib/modules/ProductList/index.js +111 -110
- package/lib/modules/ProductList/types.js +9 -31
- package/lib/modules/Quotation/index.js +60 -82
- package/lib/modules/Quotation/types.js +4 -16
- package/lib/modules/Resource/index.js +27 -59
- package/lib/modules/Resource/types.js +22 -32
- package/lib/modules/Resource/utils.js +30 -38
- package/lib/modules/Rules/index.js +1172 -872
- package/lib/modules/Rules/types.js +17 -40
- package/lib/modules/SalesSummary/index.js +59 -68
- package/lib/modules/SalesSummary/types.js +4 -16
- package/lib/modules/SalesSummary/utils.js +176 -193
- package/lib/modules/ScanOrderLogger/index.js +59 -79
- package/lib/modules/ScanOrderLogger/providers/feishu.js +54 -78
- package/lib/modules/ScanOrderLogger/providers/grafana.js +13 -36
- package/lib/modules/ScanOrderLogger/types.js +4 -16
- package/lib/modules/Schedule/getDateIsInSchedule.js +334 -167
- package/lib/modules/Schedule/index.js +97 -107
- package/lib/modules/Schedule/type.js +4 -16
- package/lib/modules/Schedule/types.js +4 -16
- package/lib/modules/Schedule/utils.js +433 -291
- package/lib/modules/Step/index.js +40 -52
- package/lib/modules/Step/tyeps.js +4 -16
- package/lib/modules/Summary/index.js +66 -70
- package/lib/modules/Summary/types.js +4 -16
- package/lib/modules/Summary/utils.js +782 -429
- package/lib/modules/index.js +212 -57
- package/lib/plugins/app.js +4 -16
- package/lib/plugins/index.js +36 -25
- package/lib/plugins/request.js +137 -126
- package/lib/plugins/shopStore.js +4 -16
- package/lib/plugins/user.js +4 -16
- package/lib/plugins/window.js +171 -179
- package/lib/server/index.js +882 -623
- package/lib/server/modules/index.js +85 -59
- package/lib/server/modules/menu/index.js +146 -122
- package/lib/server/modules/menu/types.js +18 -31
- package/lib/server/modules/order/index.js +195 -231
- package/lib/server/modules/order/types.js +107 -32
- package/lib/server/modules/order/utils/filterBookings.js +174 -140
- package/lib/server/modules/order/utils/filterOrders.js +89 -60
- package/lib/server/modules/products/index.js +430 -335
- package/lib/server/modules/products/types.js +41 -34
- package/lib/server/modules/quotation/index.js +76 -77
- package/lib/server/modules/quotation/types.js +21 -31
- package/lib/server/modules/resource/index.js +216 -187
- package/lib/server/modules/resource/types.js +42 -33
- package/lib/server/modules/schedule/index.js +134 -119
- package/lib/server/modules/schedule/types.js +14 -21
- package/lib/server/modules/schedule/utils.js +334 -163
- package/lib/server/types.js +4 -16
- package/lib/server/utils/index.js +25 -23
- package/lib/server/utils/product.js +196 -139
- package/lib/server/utils/schedule.js +34 -41
- package/lib/server/utils/time.js +40 -49
- package/lib/solution/BookingByStep/index.d.ts +1 -1
- package/lib/solution/BookingByStep/index.js +1530 -1343
- package/lib/solution/BookingByStep/types.js +40 -99
- package/lib/solution/BookingByStep/utils/capacity.js +192 -160
- package/lib/solution/BookingByStep/utils/products.js +42 -52
- package/lib/solution/BookingByStep/utils/resources.js +527 -330
- package/lib/solution/BookingByStep/utils/stock.js +65 -44
- package/lib/solution/BookingByStep/utils/timeslots.js +129 -131
- package/lib/solution/BookingTicket/index.d.ts +1 -1
- package/lib/solution/BookingTicket/index.js +147 -122
- package/lib/solution/BookingTicket/types.js +38 -64
- package/lib/solution/BookingTicket/utils/scan/cloudSearch.js +64 -80
- package/lib/solution/BookingTicket/utils/scan/handleScan.js +82 -80
- package/lib/solution/BookingTicket/utils/scan/index.js +90 -94
- package/lib/solution/BookingTicket/utils/scan/scanCache.js +90 -43
- package/lib/solution/BuyTickets/index.js +67 -70
- package/lib/solution/BuyTickets/types.js +22 -38
- package/lib/solution/Checkout/index.js +1451 -1158
- package/lib/solution/Checkout/types.js +91 -61
- package/lib/solution/Checkout/utils/index.js +227 -158
- package/lib/solution/PlaceOrder/index.js +55 -0
- package/lib/solution/RegisterAndLogin/config.js +493 -460
- package/lib/solution/RegisterAndLogin/index.js +633 -630
- package/lib/solution/RegisterAndLogin/types.js +189 -76
- package/lib/solution/RegisterAndLogin/utils.js +183 -125
- package/lib/solution/Sales/index.js +218 -200
- package/lib/solution/Sales/types.js +27 -33
- package/lib/solution/ScanOrder/index.js +803 -840
- package/lib/solution/ScanOrder/types.js +34 -34
- package/lib/solution/ScanOrder/utils.js +372 -331
- package/lib/solution/ShopDiscount/index.js +230 -236
- package/lib/solution/ShopDiscount/types.js +15 -37
- package/lib/solution/ShopDiscount/utils.js +309 -172
- package/lib/solution/VenueBooking/index.d.ts +5 -0
- package/lib/solution/VenueBooking/index.js +886 -929
- package/lib/solution/VenueBooking/types.js +20 -40
- package/lib/solution/VenueBooking/utils/dateSummary.js +22 -54
- package/lib/solution/VenueBooking/utils/resource.js +31 -54
- package/lib/solution/VenueBooking/utils/slotMerge.js +88 -106
- package/lib/solution/VenueBooking/utils/smartPricing.js +112 -100
- package/lib/solution/VenueBooking/utils/timeSlot.js +133 -132
- package/lib/solution/VenueBooking/utils.js +130 -67
- package/lib/solution/index.js +102 -37
- package/lib/store/createStore.js +32 -29
- package/lib/types/index.js +4 -16
- package/lib/utils/task.js +36 -36
- package/lib/utils/watch.js +81 -47
- package/package.json +2 -1
|
@@ -1,85 +1,94 @@
|
|
|
1
|
-
|
|
2
|
-
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
3
|
-
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
4
|
-
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
5
|
-
var __export = (target, all) => {
|
|
6
|
-
for (var name in all)
|
|
7
|
-
__defProp(target, name, { get: all[name], enumerable: true });
|
|
8
|
-
};
|
|
9
|
-
var __copyProps = (to, from, except, desc) => {
|
|
10
|
-
if (from && typeof from === "object" || typeof from === "function") {
|
|
11
|
-
for (let key of __getOwnPropNames(from))
|
|
12
|
-
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
13
|
-
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
14
|
-
}
|
|
15
|
-
return to;
|
|
16
|
-
};
|
|
17
|
-
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
1
|
+
"use strict";
|
|
18
2
|
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
__export(products_exports, {
|
|
22
|
-
ProductsModule: () => ProductsModule
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
23
5
|
});
|
|
24
|
-
|
|
25
|
-
var
|
|
26
|
-
var
|
|
27
|
-
var
|
|
28
|
-
var
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
6
|
+
exports.ProductsModule = void 0;
|
|
7
|
+
var _BaseModule = require("../../../modules/BaseModule");
|
|
8
|
+
var _plugins = require("../../../plugins");
|
|
9
|
+
var _types = require("./types");
|
|
10
|
+
var _product = require("../../utils/product");
|
|
11
|
+
/**
|
|
12
|
+
* IndexDB 存储名称
|
|
13
|
+
*/
|
|
14
|
+
const INDEXDB_STORE_NAME = 'products';
|
|
15
|
+
|
|
16
|
+
/** 商品同步防抖时间(毫秒) */
|
|
17
|
+
const PRODUCT_SYNC_DEBOUNCE_MS = 10_000;
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Products 模块 - 用于获取和管理完整的商品详细数据
|
|
21
|
+
* 相比 ProductList 模块,Products 会获取所有详细信息并缓存
|
|
22
|
+
*/
|
|
23
|
+
class ProductsModule extends _BaseModule.BaseModule {
|
|
24
|
+
defaultName = 'products';
|
|
25
|
+
defaultVersion = '1.0.0';
|
|
26
|
+
store;
|
|
27
|
+
request;
|
|
28
|
+
dbManager; // IndexDBManager 实例
|
|
29
|
+
logger; // LoggerManager 实例
|
|
30
|
+
otherParams = {};
|
|
31
|
+
|
|
32
|
+
// 商品价格缓存:Map<日期, 应用了价格的商品列表>
|
|
33
|
+
productsPriceCache = new Map();
|
|
34
|
+
|
|
35
|
+
// 缓存配置
|
|
36
|
+
CACHE_MAX_DAYS = 7; // 最多缓存7天
|
|
37
|
+
|
|
38
|
+
// 商品格式化器列表
|
|
39
|
+
formatters = [];
|
|
40
|
+
|
|
41
|
+
// 是否已注册内置价格格式化器
|
|
42
|
+
isPriceFormatterRegistered = false;
|
|
43
|
+
|
|
44
|
+
// ---- 商品数据同步相关 ----
|
|
45
|
+
productDataSource;
|
|
46
|
+
pendingSyncMessages = [];
|
|
47
|
+
syncTimer;
|
|
32
48
|
constructor(name, version) {
|
|
33
49
|
super(name, version);
|
|
34
|
-
this.defaultName = "products";
|
|
35
|
-
this.defaultVersion = "1.0.0";
|
|
36
|
-
// LoggerManager 实例
|
|
37
|
-
this.otherParams = {};
|
|
38
|
-
// 商品价格缓存:Map<日期, 应用了价格的商品列表>
|
|
39
|
-
this.productsPriceCache = /* @__PURE__ */ new Map();
|
|
40
|
-
// 缓存配置
|
|
41
|
-
this.CACHE_MAX_DAYS = 7;
|
|
42
|
-
// 最多缓存7天
|
|
43
|
-
// 商品格式化器列表
|
|
44
|
-
this.formatters = [];
|
|
45
|
-
// 是否已注册内置价格格式化器
|
|
46
|
-
this.isPriceFormatterRegistered = false;
|
|
47
|
-
this.pendingSyncMessages = [];
|
|
48
50
|
}
|
|
49
51
|
async initialize(core, options) {
|
|
50
|
-
var _a;
|
|
51
52
|
this.core = core;
|
|
52
53
|
this.store = options.store;
|
|
53
54
|
this.otherParams = options.otherParams || {};
|
|
54
|
-
if (Array.isArray(
|
|
55
|
+
if (Array.isArray(options.initialState?.list)) {
|
|
55
56
|
this.store.list = options.initialState.list;
|
|
57
|
+
// 同步更新 Map 缓存
|
|
56
58
|
this.syncProductsMap();
|
|
57
|
-
this.core.effects.emit(
|
|
59
|
+
this.core.effects.emit(_types.ProductsHooks.onProductsChanged, this.store.list);
|
|
58
60
|
} else {
|
|
59
61
|
this.store.list = [];
|
|
60
|
-
this.store.map =
|
|
62
|
+
this.store.map = new Map();
|
|
61
63
|
}
|
|
62
|
-
this.request = core.getPlugin(
|
|
63
|
-
|
|
64
|
+
this.request = core.getPlugin('request');
|
|
65
|
+
|
|
66
|
+
// 获取并存储 dbManager 和 logger
|
|
67
|
+
const appPlugin = core.getPlugin('app');
|
|
64
68
|
if (appPlugin) {
|
|
65
69
|
const app = appPlugin.getApp();
|
|
66
70
|
this.dbManager = app.dbManager;
|
|
67
71
|
this.logger = app.logger;
|
|
68
|
-
if (this.dbManager) {
|
|
69
|
-
console.
|
|
70
|
-
} else {
|
|
71
|
-
console.warn("[Products] IndexDB Manager 未找到");
|
|
72
|
+
if (this.dbManager) {} else {
|
|
73
|
+
console.warn('[Products] IndexDB Manager 未找到');
|
|
72
74
|
}
|
|
73
75
|
}
|
|
76
|
+
|
|
77
|
+
// 注册内置的价格格式化器(作为第一个格式化器)
|
|
74
78
|
this.registerBuiltinPriceFormatter();
|
|
79
|
+
|
|
80
|
+
// 初始化 ProductDataSource 实例
|
|
75
81
|
this.initProductDataSource();
|
|
82
|
+
|
|
83
|
+
// 初始化商品数据同步(pubsub 订阅)
|
|
76
84
|
this.setupProductSync();
|
|
77
|
-
this.logInfo(
|
|
85
|
+
this.logInfo('模块初始化完成', {
|
|
78
86
|
hasDbManager: !!this.dbManager,
|
|
79
87
|
hasLogger: !!this.logger,
|
|
80
88
|
initialProductCount: this.store.list.length
|
|
81
89
|
});
|
|
82
90
|
}
|
|
91
|
+
|
|
83
92
|
/**
|
|
84
93
|
* 记录信息日志
|
|
85
94
|
* @param title 日志标题
|
|
@@ -89,14 +98,16 @@ var ProductsModule = class extends import_BaseModule.BaseModule {
|
|
|
89
98
|
try {
|
|
90
99
|
if (this.logger) {
|
|
91
100
|
this.logger.addLog({
|
|
92
|
-
type:
|
|
101
|
+
type: 'info',
|
|
93
102
|
title: `[ProductsModule] ${title}`,
|
|
94
103
|
metadata: metadata || {}
|
|
95
104
|
});
|
|
96
105
|
}
|
|
97
106
|
} catch {
|
|
107
|
+
// 日志记录失败不影响主流程
|
|
98
108
|
}
|
|
99
109
|
}
|
|
110
|
+
|
|
100
111
|
/**
|
|
101
112
|
* 记录警告日志
|
|
102
113
|
* @param title 日志标题
|
|
@@ -106,14 +117,16 @@ var ProductsModule = class extends import_BaseModule.BaseModule {
|
|
|
106
117
|
try {
|
|
107
118
|
if (this.logger) {
|
|
108
119
|
this.logger.addLog({
|
|
109
|
-
type:
|
|
120
|
+
type: 'warning',
|
|
110
121
|
title: `[ProductsModule] ${title}`,
|
|
111
122
|
metadata: metadata || {}
|
|
112
123
|
});
|
|
113
124
|
}
|
|
114
125
|
} catch {
|
|
126
|
+
// 日志记录失败不影响主流程
|
|
115
127
|
}
|
|
116
128
|
}
|
|
129
|
+
|
|
117
130
|
/**
|
|
118
131
|
* 记录错误日志
|
|
119
132
|
* @param title 日志标题
|
|
@@ -123,14 +136,16 @@ var ProductsModule = class extends import_BaseModule.BaseModule {
|
|
|
123
136
|
try {
|
|
124
137
|
if (this.logger) {
|
|
125
138
|
this.logger.addLog({
|
|
126
|
-
type:
|
|
139
|
+
type: 'error',
|
|
127
140
|
title: `[ProductsModule] ${title}`,
|
|
128
141
|
metadata: metadata || {}
|
|
129
142
|
});
|
|
130
143
|
}
|
|
131
144
|
} catch {
|
|
145
|
+
// 日志记录失败不影响主流程
|
|
132
146
|
}
|
|
133
147
|
}
|
|
148
|
+
|
|
134
149
|
/**
|
|
135
150
|
* 加载商品价格(原始方法,不带缓存)
|
|
136
151
|
* @private
|
|
@@ -140,39 +155,34 @@ var ProductsModule = class extends import_BaseModule.BaseModule {
|
|
|
140
155
|
customer_id,
|
|
141
156
|
schedule_date
|
|
142
157
|
}) {
|
|
143
|
-
|
|
144
|
-
this.logInfo("开始加载商品价格", {
|
|
158
|
+
this.logInfo('开始加载商品价格', {
|
|
145
159
|
productCount: ids.length,
|
|
146
160
|
schedule_date,
|
|
147
161
|
customer_id
|
|
148
162
|
});
|
|
149
163
|
const startTime = Date.now();
|
|
150
164
|
try {
|
|
151
|
-
const productsData = await this.request.post(
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
cache: {
|
|
160
|
-
mode: import_plugins.RequestModeENUM.REMOTE_LOCAL,
|
|
161
|
-
type: "memory"
|
|
162
|
-
}
|
|
165
|
+
const productsData = await this.request.post(`/product/query/price`, {
|
|
166
|
+
ids,
|
|
167
|
+
customer_id,
|
|
168
|
+
schedule_date
|
|
169
|
+
}, {
|
|
170
|
+
cache: {
|
|
171
|
+
mode: _plugins.RequestModeENUM.REMOTE_LOCAL,
|
|
172
|
+
type: "memory"
|
|
163
173
|
}
|
|
164
|
-
);
|
|
174
|
+
});
|
|
165
175
|
const duration = Date.now() - startTime;
|
|
166
|
-
this.logInfo(
|
|
176
|
+
this.logInfo('加载商品价格成功', {
|
|
167
177
|
productCount: ids.length,
|
|
168
|
-
priceDataCount:
|
|
178
|
+
priceDataCount: productsData.data?.length ?? 0,
|
|
169
179
|
duration: `${duration}ms`
|
|
170
180
|
});
|
|
171
181
|
return productsData.data;
|
|
172
182
|
} catch (error) {
|
|
173
183
|
const duration = Date.now() - startTime;
|
|
174
184
|
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
175
|
-
this.logError(
|
|
185
|
+
this.logError('加载商品价格失败', {
|
|
176
186
|
productCount: ids.length,
|
|
177
187
|
duration: `${duration}ms`,
|
|
178
188
|
error: errorMessage
|
|
@@ -180,6 +190,7 @@ var ProductsModule = class extends import_BaseModule.BaseModule {
|
|
|
180
190
|
return [];
|
|
181
191
|
}
|
|
182
192
|
}
|
|
193
|
+
|
|
183
194
|
/**
|
|
184
195
|
* 获取应用了价格的商品列表(带缓存)
|
|
185
196
|
* 使用日期作为缓存 key,同一天的商品价格会被缓存
|
|
@@ -193,23 +204,24 @@ var ProductsModule = class extends import_BaseModule.BaseModule {
|
|
|
193
204
|
async getProductsWithPrice(schedule_date, extraContext, options) {
|
|
194
205
|
const t0 = performance.now();
|
|
195
206
|
const cacheKey = schedule_date;
|
|
196
|
-
const changedIds = options
|
|
207
|
+
const changedIds = options?.changedIds;
|
|
197
208
|
if (this.productsPriceCache.has(cacheKey)) {
|
|
198
209
|
const cachedProducts = this.productsPriceCache.get(cacheKey);
|
|
210
|
+
|
|
211
|
+
// 有变更的商品 → 增量 prepare 后 merge 回缓存
|
|
199
212
|
if (changedIds && changedIds.length > 0) {
|
|
200
|
-
this.logInfo(
|
|
213
|
+
this.logInfo('商品价格缓存命中,增量更新变更商品', {
|
|
201
214
|
cacheKey,
|
|
202
215
|
changedIds,
|
|
203
216
|
cachedProductCount: cachedProducts.length
|
|
204
217
|
});
|
|
205
218
|
try {
|
|
206
|
-
const updatedProducts = await this.prepareProductsWithPrice(
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
{ productIds: changedIds }
|
|
210
|
-
);
|
|
219
|
+
const updatedProducts = await this.prepareProductsWithPrice(schedule_date, extraContext, {
|
|
220
|
+
productIds: changedIds
|
|
221
|
+
});
|
|
211
222
|
if (updatedProducts.length > 0) {
|
|
212
|
-
|
|
223
|
+
// merge:用更新结果替换缓存中对应的商品,新增的追加
|
|
224
|
+
const updatedMap = new Map(updatedProducts.map(p => [p.id, p]));
|
|
213
225
|
const mergedCache = [];
|
|
214
226
|
for (const p of cachedProducts) {
|
|
215
227
|
if (updatedMap.has(p.id)) {
|
|
@@ -219,16 +231,17 @@ var ProductsModule = class extends import_BaseModule.BaseModule {
|
|
|
219
231
|
mergedCache.push(p);
|
|
220
232
|
}
|
|
221
233
|
}
|
|
234
|
+
// 剩余的是新增商品(create 场景)
|
|
222
235
|
for (const p of updatedMap.values()) {
|
|
223
236
|
mergedCache.push(p);
|
|
224
237
|
}
|
|
225
238
|
this.productsPriceCache.set(cacheKey, mergedCache);
|
|
226
|
-
this.logInfo(
|
|
239
|
+
this.logInfo('增量更新完成', {
|
|
227
240
|
cacheKey,
|
|
228
241
|
changedCount: updatedProducts.length,
|
|
229
242
|
totalCount: mergedCache.length
|
|
230
243
|
});
|
|
231
|
-
(0,
|
|
244
|
+
(0, _product.perfMark)('getProductsWithPrice(incrementalUpdate)', performance.now() - t0, {
|
|
232
245
|
cacheKey,
|
|
233
246
|
changedCount: changedIds.length,
|
|
234
247
|
count: mergedCache.length
|
|
@@ -237,43 +250,46 @@ var ProductsModule = class extends import_BaseModule.BaseModule {
|
|
|
237
250
|
}
|
|
238
251
|
} catch (error) {
|
|
239
252
|
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
240
|
-
this.logError(
|
|
253
|
+
this.logError('增量更新失败,返回现有缓存', {
|
|
241
254
|
cacheKey,
|
|
242
255
|
changedIds,
|
|
243
256
|
error: errorMessage
|
|
244
257
|
});
|
|
245
258
|
}
|
|
246
|
-
(0,
|
|
259
|
+
(0, _product.perfMark)('getProductsWithPrice(incrementalUpdate)', performance.now() - t0, {
|
|
247
260
|
cacheKey,
|
|
248
261
|
changedCount: changedIds.length,
|
|
249
262
|
count: cachedProducts.length
|
|
250
263
|
});
|
|
251
264
|
return cachedProducts;
|
|
252
265
|
}
|
|
253
|
-
(0,
|
|
266
|
+
(0, _product.perfMark)('getProductsWithPrice(cacheHit)', performance.now() - t0, {
|
|
254
267
|
cacheKey,
|
|
255
268
|
count: cachedProducts.length
|
|
256
269
|
});
|
|
257
|
-
this.logInfo(
|
|
270
|
+
this.logInfo('商品价格缓存命中', {
|
|
258
271
|
cacheKey,
|
|
259
272
|
productCount: cachedProducts.length
|
|
260
273
|
});
|
|
261
274
|
return cachedProducts;
|
|
262
275
|
}
|
|
263
|
-
this.logInfo(
|
|
276
|
+
this.logInfo('商品价格缓存未命中,准备获取', {
|
|
277
|
+
cacheKey
|
|
278
|
+
});
|
|
264
279
|
const result = await this.prepareProductsWithPrice(schedule_date, extraContext);
|
|
265
280
|
this.productsPriceCache.set(cacheKey, result);
|
|
266
|
-
this.logInfo(
|
|
281
|
+
this.logInfo('商品价格已缓存', {
|
|
267
282
|
cacheKey,
|
|
268
283
|
productCount: result.length
|
|
269
284
|
});
|
|
270
285
|
this.cleanExpiredPriceCache();
|
|
271
|
-
(0,
|
|
286
|
+
(0, _product.perfMark)('getProductsWithPrice(cacheMiss)', performance.now() - t0, {
|
|
272
287
|
cacheKey,
|
|
273
288
|
count: result.length
|
|
274
289
|
});
|
|
275
290
|
return result;
|
|
276
291
|
}
|
|
292
|
+
|
|
277
293
|
/**
|
|
278
294
|
* 准备带价格的商品数据(通过格式化器流程处理)
|
|
279
295
|
* @param schedule_date 日期
|
|
@@ -284,22 +300,22 @@ var ProductsModule = class extends import_BaseModule.BaseModule {
|
|
|
284
300
|
* @private
|
|
285
301
|
*/
|
|
286
302
|
async prepareProductsWithPrice(schedule_date, extraContext, options) {
|
|
287
|
-
var _a, _b;
|
|
288
303
|
const tTotal = performance.now();
|
|
289
|
-
const targetIds = options
|
|
304
|
+
const targetIds = options?.productIds;
|
|
290
305
|
const isIncremental = targetIds && targetIds.length > 0;
|
|
291
|
-
this.logInfo(
|
|
306
|
+
this.logInfo('prepareProductsWithPrice 开始处理', {
|
|
292
307
|
schedule_date,
|
|
293
|
-
mode: isIncremental ?
|
|
294
|
-
targetIdsCount: targetIds
|
|
308
|
+
mode: isIncremental ? 'incremental' : 'full',
|
|
309
|
+
targetIdsCount: targetIds?.length
|
|
295
310
|
});
|
|
296
311
|
try {
|
|
312
|
+
// 1. 获取商品列表:指定 IDs 时从 store 取子集,否则取全量
|
|
297
313
|
let products;
|
|
298
314
|
let ids;
|
|
299
315
|
if (isIncremental) {
|
|
300
316
|
const idSet = new Set(targetIds);
|
|
301
|
-
products = this.getProductsRef().filter(
|
|
302
|
-
ids = products.map(
|
|
317
|
+
products = this.getProductsRef().filter(p => idSet.has(p.id));
|
|
318
|
+
ids = products.map(p => p.id);
|
|
303
319
|
} else {
|
|
304
320
|
products = this.getProductsRef();
|
|
305
321
|
const tIds = performance.now();
|
|
@@ -307,56 +323,65 @@ var ProductsModule = class extends import_BaseModule.BaseModule {
|
|
|
307
323
|
for (let i = 0; i < products.length; i++) {
|
|
308
324
|
ids[i] = products[i].id;
|
|
309
325
|
}
|
|
310
|
-
(0,
|
|
326
|
+
(0, _product.perfMark)('prepareProducts.extractIds', performance.now() - tIds, {
|
|
327
|
+
count: ids.length
|
|
328
|
+
});
|
|
311
329
|
}
|
|
312
|
-
this.logInfo(
|
|
330
|
+
this.logInfo('获取到商品列表', {
|
|
331
|
+
productCount: products.length
|
|
332
|
+
});
|
|
333
|
+
|
|
334
|
+
// 2. 获取价格数据(网络请求)
|
|
313
335
|
const tPrice = performance.now();
|
|
314
336
|
const priceData = await this.loadProductsPrice({
|
|
315
337
|
ids,
|
|
316
338
|
schedule_date
|
|
317
339
|
});
|
|
318
|
-
(0,
|
|
319
|
-
|
|
340
|
+
(0, _product.perfMark)('prepareProducts.loadPrice', performance.now() - tPrice, {
|
|
341
|
+
count: ids.length
|
|
342
|
+
});
|
|
343
|
+
this.logInfo('获取商品报价单价格成功', {
|
|
344
|
+
priceDataCount: priceData?.length ?? 0
|
|
345
|
+
});
|
|
320
346
|
const context = {
|
|
321
347
|
schedule_date,
|
|
322
348
|
priceData,
|
|
323
|
-
locale:
|
|
349
|
+
locale: this.core?.context?.locale,
|
|
324
350
|
...extraContext
|
|
325
351
|
};
|
|
352
|
+
|
|
353
|
+
// 3. 通过格式化器流程处理商品
|
|
326
354
|
const tFormat = performance.now();
|
|
327
355
|
const processedProducts = await this.applyFormatters(products, context);
|
|
328
|
-
(0,
|
|
356
|
+
(0, _product.perfMark)('prepareProducts.applyFormatters', performance.now() - tFormat, {
|
|
329
357
|
count: products.length,
|
|
330
358
|
formatterCount: this.formatters.length
|
|
331
359
|
});
|
|
332
|
-
this.logInfo(
|
|
333
|
-
mode: isIncremental ?
|
|
360
|
+
this.logInfo('prepareProductsWithPrice 处理完成', {
|
|
361
|
+
mode: isIncremental ? 'incremental' : 'full',
|
|
334
362
|
originalProductCount: products.length,
|
|
335
363
|
processedProductCount: processedProducts.length,
|
|
336
364
|
formatterCount: this.formatters.length
|
|
337
365
|
});
|
|
338
366
|
if (!isIncremental) {
|
|
339
|
-
await this.core.effects.emit(
|
|
340
|
-
import_types.ProductsHooks.onProductsPriceApplied,
|
|
341
|
-
processedProducts
|
|
342
|
-
);
|
|
367
|
+
await this.core.effects.emit(_types.ProductsHooks.onProductsPriceApplied, processedProducts);
|
|
343
368
|
}
|
|
344
|
-
(0,
|
|
369
|
+
(0, _product.perfMark)('prepareProductsWithPrice', performance.now() - tTotal, {
|
|
345
370
|
productCount: products.length,
|
|
346
371
|
formatterCount: this.formatters.length,
|
|
347
|
-
mode: isIncremental ?
|
|
372
|
+
mode: isIncremental ? 'incremental' : 'full'
|
|
348
373
|
});
|
|
349
374
|
return processedProducts;
|
|
350
375
|
} catch (err) {
|
|
351
376
|
const errorMessage = err instanceof Error ? err.message : String(err);
|
|
352
|
-
|
|
353
|
-
this.logError("prepareProductsWithPrice 处理失败", {
|
|
377
|
+
this.logError('prepareProductsWithPrice 处理失败', {
|
|
354
378
|
schedule_date,
|
|
355
379
|
error: errorMessage
|
|
356
380
|
});
|
|
357
381
|
}
|
|
358
382
|
return [];
|
|
359
383
|
}
|
|
384
|
+
|
|
360
385
|
/**
|
|
361
386
|
* 应用所有已注册的格式化器
|
|
362
387
|
* @param products 商品列表
|
|
@@ -367,11 +392,13 @@ var ProductsModule = class extends import_BaseModule.BaseModule {
|
|
|
367
392
|
async applyFormatters(products, context) {
|
|
368
393
|
let result = products;
|
|
369
394
|
if (this.formatters.length === 0) {
|
|
370
|
-
console.warn(
|
|
371
|
-
this.logWarning(
|
|
395
|
+
console.warn('[ProductsModule] ⚠️ 没有注册任何格式化器');
|
|
396
|
+
this.logWarning('没有注册任何格式化器', {
|
|
397
|
+
productCount: products.length
|
|
398
|
+
});
|
|
372
399
|
return result;
|
|
373
400
|
}
|
|
374
|
-
this.logInfo(
|
|
401
|
+
this.logInfo('开始应用格式化器', {
|
|
375
402
|
productCount: products.length,
|
|
376
403
|
formatterCount: this.formatters.length
|
|
377
404
|
});
|
|
@@ -380,7 +407,7 @@ var ProductsModule = class extends import_BaseModule.BaseModule {
|
|
|
380
407
|
try {
|
|
381
408
|
const tF = performance.now();
|
|
382
409
|
result = await formatter(result, context);
|
|
383
|
-
(0,
|
|
410
|
+
(0, _product.perfMark)(`applyFormatters[${i}]`, performance.now() - tF, {
|
|
384
411
|
index: i,
|
|
385
412
|
total: this.formatters.length,
|
|
386
413
|
productCount: result.length
|
|
@@ -395,12 +422,13 @@ var ProductsModule = class extends import_BaseModule.BaseModule {
|
|
|
395
422
|
});
|
|
396
423
|
}
|
|
397
424
|
}
|
|
398
|
-
this.logInfo(
|
|
425
|
+
this.logInfo('所有格式化器已应用', {
|
|
399
426
|
formatterCount: this.formatters.length,
|
|
400
427
|
resultProductCount: result.length
|
|
401
428
|
});
|
|
402
429
|
return result;
|
|
403
430
|
}
|
|
431
|
+
|
|
404
432
|
/**
|
|
405
433
|
* 注册内置的价格格式化器
|
|
406
434
|
* 这个格式化器负责将价格数据应用到商品上
|
|
@@ -408,29 +436,31 @@ var ProductsModule = class extends import_BaseModule.BaseModule {
|
|
|
408
436
|
*/
|
|
409
437
|
registerBuiltinPriceFormatter() {
|
|
410
438
|
if (this.isPriceFormatterRegistered) {
|
|
411
|
-
console.warn(
|
|
439
|
+
console.warn('[ProductsModule] 内置价格格式化器已注册,跳过');
|
|
412
440
|
return;
|
|
413
441
|
}
|
|
442
|
+
|
|
443
|
+
// 创建价格格式化器
|
|
414
444
|
const priceFormatter = (products, context) => {
|
|
415
445
|
if (!context.priceData || context.priceData.length === 0) {
|
|
416
|
-
console.log("[ProductsModule] 💰 没有价格数据,跳过价格应用");
|
|
417
446
|
return products;
|
|
418
447
|
}
|
|
419
|
-
|
|
420
|
-
return (0, import_product.applyPriceDataToProducts)(products, context.priceData);
|
|
448
|
+
return (0, _product.applyPriceDataToProducts)(products, context.priceData);
|
|
421
449
|
};
|
|
422
450
|
const i18nFormatter = (products, context) => {
|
|
423
|
-
return (0,
|
|
451
|
+
return (0, _product.applyI18nToProducts)(products, context.locale);
|
|
424
452
|
};
|
|
425
453
|
const detailValueFormatter = (products, context) => {
|
|
426
|
-
return (0,
|
|
454
|
+
return (0, _product.applyDetailValueToProducts)(products, context);
|
|
427
455
|
};
|
|
456
|
+
|
|
457
|
+
// 将价格格式化器注册为第一个格式化器
|
|
428
458
|
this.formatters.unshift(priceFormatter);
|
|
429
459
|
this.formatters.push(i18nFormatter);
|
|
430
460
|
this.formatters.push(detailValueFormatter);
|
|
431
461
|
this.isPriceFormatterRegistered = true;
|
|
432
|
-
console.log("[ProductsModule] ✅ 内置价格格式化器已注册(第 1 个)");
|
|
433
462
|
}
|
|
463
|
+
|
|
434
464
|
/**
|
|
435
465
|
* 注册商品格式化器
|
|
436
466
|
* 格式化器会按注册顺序依次执行(内置价格格式化器始终是第一个)
|
|
@@ -448,28 +478,29 @@ var ProductsModule = class extends import_BaseModule.BaseModule {
|
|
|
448
478
|
*/
|
|
449
479
|
registerFormatter(formatter, prepend = false) {
|
|
450
480
|
if (prepend) {
|
|
481
|
+
// 插入到价格格式化器之后(索引 1 的位置)
|
|
451
482
|
const insertIndex = this.isPriceFormatterRegistered ? 1 : 0;
|
|
452
483
|
this.formatters.splice(insertIndex, 0, formatter);
|
|
453
|
-
console.log(`[ProductsModule] 📌 已在位置 ${insertIndex + 1} 插入格式化器,当前共 ${this.formatters.length} 个`);
|
|
454
484
|
} else {
|
|
485
|
+
// 追加到末尾
|
|
455
486
|
this.formatters.push(formatter);
|
|
456
|
-
console.log(`[ProductsModule] 📌 已注册格式化器,当前共 ${this.formatters.length} 个`);
|
|
457
487
|
}
|
|
458
488
|
}
|
|
489
|
+
|
|
459
490
|
/**
|
|
460
491
|
* 清空所有格式化器(包括内置价格格式化器)
|
|
461
492
|
* @param keepBuiltin 是否保留内置价格格式化器(默认 true)
|
|
462
493
|
*/
|
|
463
494
|
clearFormatters(keepBuiltin = true) {
|
|
464
495
|
if (keepBuiltin && this.isPriceFormatterRegistered) {
|
|
496
|
+
// 只保留第一个格式化器(内置价格格式化器)
|
|
465
497
|
this.formatters = [this.formatters[0]];
|
|
466
|
-
console.log("[ProductsModule] 🧹 已清空自定义格式化器,保留内置价格格式化器");
|
|
467
498
|
} else {
|
|
468
499
|
this.formatters = [];
|
|
469
500
|
this.isPriceFormatterRegistered = false;
|
|
470
|
-
console.log("[ProductsModule] 🧹 已清空所有格式化器");
|
|
471
501
|
}
|
|
472
502
|
}
|
|
503
|
+
|
|
473
504
|
/**
|
|
474
505
|
* 清理过期的价格缓存
|
|
475
506
|
* 当缓存数量超过限制时,删除最老的缓存
|
|
@@ -480,58 +511,64 @@ var ProductsModule = class extends import_BaseModule.BaseModule {
|
|
|
480
511
|
const keys = Array.from(this.productsPriceCache.keys());
|
|
481
512
|
const oldestKey = keys[0];
|
|
482
513
|
this.productsPriceCache.delete(oldestKey);
|
|
483
|
-
console.log(`[ProductsModule] 🧹 清理过期商品价格缓存: ${oldestKey}`);
|
|
484
514
|
}
|
|
485
515
|
}
|
|
516
|
+
|
|
486
517
|
/**
|
|
487
518
|
* 清空商品价格缓存
|
|
488
519
|
* 可用于手动刷新价格数据
|
|
489
520
|
*/
|
|
490
521
|
clearPriceCache() {
|
|
491
522
|
this.productsPriceCache.clear();
|
|
492
|
-
console.log("[ProductsModule] 🗑️ 商品价格缓存已清空");
|
|
493
523
|
}
|
|
524
|
+
|
|
494
525
|
/**
|
|
495
526
|
* 通过 ProductDataSource SSE 加载完整商品列表
|
|
496
527
|
*/
|
|
497
528
|
async loadProductsByServer() {
|
|
498
529
|
if (!this.productDataSource) {
|
|
499
|
-
this.logWarning(
|
|
530
|
+
this.logWarning('loadProductsByServer: ProductDataSource 不可用');
|
|
500
531
|
return [];
|
|
501
532
|
}
|
|
502
|
-
this.logInfo(
|
|
533
|
+
this.logInfo('开始通过 DataSource SSE 加载商品列表');
|
|
503
534
|
const t0 = performance.now();
|
|
504
535
|
try {
|
|
505
536
|
const tSSE = performance.now();
|
|
506
|
-
const productList = await this.productDataSource.run({
|
|
537
|
+
const productList = await this.productDataSource.run({
|
|
538
|
+
sse: {}
|
|
539
|
+
});
|
|
507
540
|
const list = productList || [];
|
|
508
|
-
(0,
|
|
509
|
-
|
|
541
|
+
(0, _product.perfMark)('loadProductsByServer.SSE', performance.now() - tSSE, {
|
|
542
|
+
count: list.length
|
|
543
|
+
});
|
|
544
|
+
this.logInfo('通过 DataSource SSE 加载商品列表成功', {
|
|
510
545
|
productCount: list.length,
|
|
511
546
|
duration: `${Math.round(performance.now() - t0)}ms`
|
|
512
547
|
});
|
|
513
548
|
await this.saveProductsToIndexDB(list);
|
|
514
|
-
await this.core.effects.emit(
|
|
515
|
-
(0,
|
|
549
|
+
await this.core.effects.emit(_types.ProductsHooks.onProductsLoaded, list);
|
|
550
|
+
(0, _product.perfMark)('loadProductsByServer', performance.now() - t0, {
|
|
551
|
+
count: list.length
|
|
552
|
+
});
|
|
516
553
|
return list;
|
|
517
554
|
} catch (error) {
|
|
518
555
|
const duration = Math.round(performance.now() - t0);
|
|
519
556
|
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
520
|
-
console.error(
|
|
521
|
-
this.logError(
|
|
557
|
+
console.error('[Products] 加载商品数据失败:', error);
|
|
558
|
+
this.logError('通过 DataSource SSE 加载商品列表失败', {
|
|
522
559
|
duration: `${duration}ms`,
|
|
523
560
|
error: errorMessage
|
|
524
561
|
});
|
|
525
562
|
return [];
|
|
526
563
|
}
|
|
527
564
|
}
|
|
565
|
+
|
|
528
566
|
/**
|
|
529
567
|
* 纯请求方法:通过 HTTP 接口获取商品列表(无副作用,不触发事件、不写 IndexDB)
|
|
530
568
|
* @param params 查询参数
|
|
531
569
|
* @returns 商品列表
|
|
532
570
|
*/
|
|
533
571
|
async fetchProductsByHttp(params) {
|
|
534
|
-
var _a, _b;
|
|
535
572
|
const {
|
|
536
573
|
category_ids = [],
|
|
537
574
|
product_ids = [],
|
|
@@ -539,7 +576,7 @@ var ProductsModule = class extends import_BaseModule.BaseModule {
|
|
|
539
576
|
customer_id,
|
|
540
577
|
cacheId
|
|
541
578
|
} = params || {};
|
|
542
|
-
this.logInfo(
|
|
579
|
+
this.logInfo('fetchProductsByHttp: 开始请求', {
|
|
543
580
|
categoryIdsCount: category_ids.length,
|
|
544
581
|
productIdsCount: product_ids.length,
|
|
545
582
|
collectionCount: Array.isArray(collection) ? collection.length : collection ? 1 : 0,
|
|
@@ -547,45 +584,29 @@ var ProductsModule = class extends import_BaseModule.BaseModule {
|
|
|
547
584
|
});
|
|
548
585
|
const startTime = Date.now();
|
|
549
586
|
try {
|
|
550
|
-
const productsData = await this.request.post(
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
open_deposit: 1,
|
|
572
|
-
is_append_product_description: 1,
|
|
573
|
-
with_count: ["bundleGroup", "optionGroup"],
|
|
574
|
-
with_category_tree: 1,
|
|
575
|
-
status: "published",
|
|
576
|
-
num: 500,
|
|
577
|
-
skip: 1,
|
|
578
|
-
category_ids,
|
|
579
|
-
ids: product_ids,
|
|
580
|
-
collection,
|
|
581
|
-
front_end_cache_id: cacheId,
|
|
582
|
-
application_code: (_a = this.otherParams) == null ? void 0 : _a.channel
|
|
583
|
-
},
|
|
584
|
-
{ cache: void 0 }
|
|
585
|
-
);
|
|
586
|
-
const productList = ((_b = productsData == null ? void 0 : productsData.data) == null ? void 0 : _b.list) || [];
|
|
587
|
+
const productsData = await this.request.post(`/product/query`, {
|
|
588
|
+
open_bundle: 1,
|
|
589
|
+
exclude_extension_type: ['product_party', 'product_event', 'product_series_event', 'product_package_ticket', 'ticket', 'event_item'],
|
|
590
|
+
force_ignore_cache_flag: 1,
|
|
591
|
+
with: ['category', 'collection', 'resourceRelation', 'bundleGroup.bundleItem', 'optionGroup.optionItem', 'variantGroup.variantItem'],
|
|
592
|
+
open_deposit: 1,
|
|
593
|
+
is_append_product_description: 1,
|
|
594
|
+
with_count: ['bundleGroup', 'optionGroup'],
|
|
595
|
+
with_category_tree: 1,
|
|
596
|
+
status: 'published',
|
|
597
|
+
num: 500,
|
|
598
|
+
skip: 1,
|
|
599
|
+
category_ids,
|
|
600
|
+
ids: product_ids,
|
|
601
|
+
collection,
|
|
602
|
+
front_end_cache_id: cacheId,
|
|
603
|
+
application_code: this.otherParams?.channel
|
|
604
|
+
}, {
|
|
605
|
+
cache: undefined
|
|
606
|
+
});
|
|
607
|
+
const productList = productsData?.data?.list || [];
|
|
587
608
|
const duration = Date.now() - startTime;
|
|
588
|
-
this.logInfo(
|
|
609
|
+
this.logInfo('fetchProductsByHttp: 请求成功', {
|
|
589
610
|
productCount: productList.length,
|
|
590
611
|
duration: `${duration}ms`
|
|
591
612
|
});
|
|
@@ -593,14 +614,15 @@ var ProductsModule = class extends import_BaseModule.BaseModule {
|
|
|
593
614
|
} catch (error) {
|
|
594
615
|
const duration = Date.now() - startTime;
|
|
595
616
|
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
596
|
-
console.error(
|
|
597
|
-
this.logError(
|
|
617
|
+
console.error('[Products] fetchProductsByHttp 失败:', error);
|
|
618
|
+
this.logError('fetchProductsByHttp: 请求失败', {
|
|
598
619
|
duration: `${duration}ms`,
|
|
599
620
|
error: errorMessage
|
|
600
621
|
});
|
|
601
622
|
return [];
|
|
602
623
|
}
|
|
603
624
|
}
|
|
625
|
+
|
|
604
626
|
/**
|
|
605
627
|
* 加载完整商品列表通过接口(包含所有详细数据)
|
|
606
628
|
* 包含副作用:保存到 IndexDB + 触发 onProductsLoaded 事件
|
|
@@ -610,19 +632,23 @@ var ProductsModule = class extends import_BaseModule.BaseModule {
|
|
|
610
632
|
const productList = await this.fetchProductsByHttp(params);
|
|
611
633
|
if (productList.length > 0) {
|
|
612
634
|
await this.saveProductsToIndexDB(productList);
|
|
613
|
-
await this.core.effects.emit(
|
|
635
|
+
await this.core.effects.emit(_types.ProductsHooks.onProductsLoaded, productList);
|
|
614
636
|
}
|
|
615
637
|
return productList;
|
|
616
638
|
}
|
|
639
|
+
|
|
617
640
|
/**
|
|
618
641
|
* 获取商品列表(深拷贝,供外部安全使用)
|
|
619
642
|
*/
|
|
620
643
|
async getProducts() {
|
|
621
644
|
const t0 = performance.now();
|
|
622
645
|
const result = structuredClone(this.store.list);
|
|
623
|
-
(0,
|
|
646
|
+
(0, _product.perfMark)('ProductsModule.getProducts(structuredClone)', performance.now() - t0, {
|
|
647
|
+
count: result.length
|
|
648
|
+
});
|
|
624
649
|
return result;
|
|
625
650
|
}
|
|
651
|
+
|
|
626
652
|
/**
|
|
627
653
|
* 内部获取商品列表的直接引用(无拷贝)
|
|
628
654
|
* 仅供内部 formatter 流程使用,因为 formatter 会创建新对象
|
|
@@ -630,22 +656,27 @@ var ProductsModule = class extends import_BaseModule.BaseModule {
|
|
|
630
656
|
getProductsRef() {
|
|
631
657
|
return this.store.list;
|
|
632
658
|
}
|
|
659
|
+
|
|
633
660
|
/**
|
|
634
661
|
* 根据ID获取单个商品(从内存缓存)
|
|
635
662
|
* 使用 Map 快速查询,时间复杂度 O(1)
|
|
636
663
|
*/
|
|
637
664
|
async getProductById(id) {
|
|
638
665
|
const product = this.store.map.get(id);
|
|
639
|
-
return product ? structuredClone(product) :
|
|
666
|
+
return product ? structuredClone(product) : undefined;
|
|
640
667
|
}
|
|
668
|
+
|
|
641
669
|
/**
|
|
642
670
|
* 根据 ID 列表删除商品(用于 pubsub 同步删除场景)
|
|
643
671
|
* 同时更新 store.list、store.map、IndexDB 和价格缓存
|
|
644
672
|
*/
|
|
645
673
|
async removeProductsByIds(ids) {
|
|
646
674
|
const idSet = new Set(ids);
|
|
647
|
-
this.logInfo(
|
|
648
|
-
|
|
675
|
+
this.logInfo('removeProductsByIds', {
|
|
676
|
+
ids,
|
|
677
|
+
count: ids.length
|
|
678
|
+
});
|
|
679
|
+
this.store.list = this.store.list.filter(p => !idSet.has(p.id));
|
|
649
680
|
for (const id of ids) {
|
|
650
681
|
this.store.map.delete(id);
|
|
651
682
|
}
|
|
@@ -656,113 +687,134 @@ var ProductsModule = class extends import_BaseModule.BaseModule {
|
|
|
656
687
|
}
|
|
657
688
|
} catch (error) {
|
|
658
689
|
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
659
|
-
this.logError(
|
|
690
|
+
this.logError('removeProductsByIds: IndexDB 删除失败', {
|
|
691
|
+
ids,
|
|
692
|
+
error: errorMessage
|
|
693
|
+
});
|
|
660
694
|
}
|
|
661
695
|
}
|
|
662
696
|
for (const [dateKey, cachedProducts] of this.productsPriceCache.entries()) {
|
|
663
|
-
this.productsPriceCache.set(
|
|
664
|
-
dateKey,
|
|
665
|
-
cachedProducts.filter((p) => !idSet.has(p.id))
|
|
666
|
-
);
|
|
697
|
+
this.productsPriceCache.set(dateKey, cachedProducts.filter(p => !idSet.has(p.id)));
|
|
667
698
|
}
|
|
668
|
-
this.core.effects.emit(
|
|
669
|
-
this.logInfo(
|
|
699
|
+
this.core.effects.emit(_types.ProductsHooks.onProductsChanged, this.store.list);
|
|
700
|
+
this.logInfo('removeProductsByIds 完成', {
|
|
701
|
+
remaining: this.store.list.length
|
|
702
|
+
});
|
|
670
703
|
}
|
|
704
|
+
|
|
671
705
|
/**
|
|
672
706
|
* 重新从服务器加载全量商品列表并更新本地 store
|
|
673
707
|
* 用于 pubsub 同步 create / update / batch_update 场景
|
|
674
708
|
*/
|
|
675
709
|
async refreshProducts() {
|
|
676
710
|
const tTotal = performance.now();
|
|
677
|
-
this.logInfo(
|
|
711
|
+
this.logInfo('refreshProducts 开始');
|
|
678
712
|
const products = await this.loadProductsByServer();
|
|
679
713
|
if (products && products.length > 0) {
|
|
714
|
+
// 服务器返回的数据已经是全新的,无需 cloneDeep
|
|
680
715
|
this.store.list = products;
|
|
681
716
|
this.syncProductsMap();
|
|
682
|
-
this.core.effects.emit(
|
|
683
|
-
this.logInfo(
|
|
717
|
+
this.core.effects.emit(_types.ProductsHooks.onProductsChanged, this.store.list);
|
|
718
|
+
this.logInfo('refreshProducts 完成', {
|
|
719
|
+
productCount: products.length
|
|
720
|
+
});
|
|
684
721
|
} else {
|
|
685
|
-
this.logWarning(
|
|
722
|
+
this.logWarning('refreshProducts: 服务器未返回数据');
|
|
686
723
|
}
|
|
687
|
-
(0,
|
|
724
|
+
(0, _product.perfMark)('refreshProducts', performance.now() - tTotal, {
|
|
725
|
+
count: this.store.list.length
|
|
726
|
+
});
|
|
688
727
|
return this.store.list;
|
|
689
728
|
}
|
|
729
|
+
|
|
690
730
|
/**
|
|
691
731
|
* 清空缓存
|
|
692
732
|
*/
|
|
693
733
|
async clear() {
|
|
694
|
-
this.logInfo(
|
|
734
|
+
this.logInfo('开始清空缓存', {
|
|
695
735
|
currentProductCount: this.store.list.length,
|
|
696
736
|
priceCacheCount: this.productsPriceCache.size
|
|
697
737
|
});
|
|
698
738
|
this.store.list = [];
|
|
699
739
|
this.store.map.clear();
|
|
740
|
+
|
|
741
|
+
// 同时清空 IndexDB 中的所有数据(包括商品和元数据)
|
|
700
742
|
if (this.dbManager) {
|
|
701
743
|
try {
|
|
702
744
|
await this.dbManager.clear(INDEXDB_STORE_NAME);
|
|
703
|
-
|
|
704
|
-
this.logInfo("IndexDB 缓存已清空");
|
|
745
|
+
this.logInfo('IndexDB 缓存已清空');
|
|
705
746
|
} catch (error) {
|
|
706
747
|
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
707
|
-
console.error(
|
|
708
|
-
this.logError(
|
|
748
|
+
console.error('[Products] 清空 IndexDB 缓存失败:', error);
|
|
749
|
+
this.logError('清空 IndexDB 缓存失败', {
|
|
750
|
+
error: errorMessage
|
|
751
|
+
});
|
|
709
752
|
}
|
|
710
753
|
}
|
|
711
|
-
|
|
712
|
-
this.logInfo("缓存清空完成");
|
|
754
|
+
this.logInfo('缓存清空完成');
|
|
713
755
|
}
|
|
756
|
+
|
|
714
757
|
/**
|
|
715
758
|
* 从 IndexDB 加载商品数据
|
|
716
759
|
* @private
|
|
717
760
|
*/
|
|
718
761
|
async loadProductsFromIndexDB() {
|
|
719
762
|
if (!this.dbManager) {
|
|
720
|
-
this.logWarning(
|
|
763
|
+
this.logWarning('loadProductsFromIndexDB: dbManager 不可用');
|
|
721
764
|
return [];
|
|
722
765
|
}
|
|
723
766
|
try {
|
|
724
767
|
const t0 = performance.now();
|
|
725
768
|
const products = await this.dbManager.getAll(INDEXDB_STORE_NAME);
|
|
726
|
-
(0,
|
|
727
|
-
|
|
728
|
-
|
|
769
|
+
(0, _product.perfMark)('loadProductsFromIndexDB', performance.now() - t0, {
|
|
770
|
+
count: products?.length ?? 0
|
|
771
|
+
});
|
|
772
|
+
this.logInfo('从 IndexDB 加载商品数据', {
|
|
773
|
+
productCount: products?.length ?? 0
|
|
729
774
|
});
|
|
730
775
|
return products || [];
|
|
731
776
|
} catch (error) {
|
|
732
777
|
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
733
|
-
console.error(
|
|
734
|
-
this.logError(
|
|
778
|
+
console.error('[Products] 从 IndexDB 读取数据失败:', error);
|
|
779
|
+
this.logError('从 IndexDB 读取数据失败', {
|
|
780
|
+
error: errorMessage
|
|
781
|
+
});
|
|
735
782
|
return [];
|
|
736
783
|
}
|
|
737
784
|
}
|
|
785
|
+
|
|
738
786
|
/**
|
|
739
787
|
* 保存商品数据到 IndexDB(平铺存储)
|
|
740
788
|
* @private
|
|
741
789
|
*/
|
|
742
790
|
async saveProductsToIndexDB(products) {
|
|
743
791
|
if (!this.dbManager) {
|
|
744
|
-
this.logWarning(
|
|
792
|
+
this.logWarning('saveProductsToIndexDB: dbManager 不可用');
|
|
745
793
|
return;
|
|
746
794
|
}
|
|
747
|
-
this.logInfo(
|
|
795
|
+
this.logInfo('开始保存商品数据到 IndexDB', {
|
|
796
|
+
productCount: products.length
|
|
797
|
+
});
|
|
748
798
|
try {
|
|
749
799
|
const t0 = performance.now();
|
|
750
800
|
await this.dbManager.clear(INDEXDB_STORE_NAME);
|
|
751
801
|
await this.dbManager.bulkUpdate(INDEXDB_STORE_NAME, products);
|
|
752
|
-
(0,
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
802
|
+
(0, _product.perfMark)('saveProductsToIndexDB', performance.now() - t0, {
|
|
803
|
+
count: products.length
|
|
804
|
+
});
|
|
805
|
+
this.logInfo('商品数据已保存到 IndexDB', {
|
|
806
|
+
productCount: products.length
|
|
807
|
+
});
|
|
757
808
|
} catch (error) {
|
|
758
809
|
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
759
|
-
console.error(
|
|
760
|
-
this.logError(
|
|
810
|
+
console.error('[Products] 保存数据到 IndexDB 失败:', error);
|
|
811
|
+
this.logError('保存数据到 IndexDB 失败', {
|
|
761
812
|
productCount: products.length,
|
|
762
813
|
error: errorMessage
|
|
763
814
|
});
|
|
764
815
|
}
|
|
765
816
|
}
|
|
817
|
+
|
|
766
818
|
/**
|
|
767
819
|
* 同步更新商品 Map 缓存
|
|
768
820
|
* 将 list 中的商品同步到 map,以 id 为 key
|
|
@@ -774,90 +826,103 @@ var ProductsModule = class extends import_BaseModule.BaseModule {
|
|
|
774
826
|
for (const product of this.store.list) {
|
|
775
827
|
this.store.map.set(product.id, product);
|
|
776
828
|
}
|
|
777
|
-
(0,
|
|
829
|
+
(0, _product.perfMark)('syncProductsMap', performance.now() - t0, {
|
|
830
|
+
count: this.store.map.size
|
|
831
|
+
});
|
|
778
832
|
}
|
|
833
|
+
|
|
779
834
|
/**
|
|
780
835
|
* 预加载模块数据(统一接口)
|
|
781
836
|
* 在模块注册后自动调用
|
|
782
837
|
*/
|
|
783
838
|
async preload() {
|
|
784
|
-
console.log("[Products] 开始预加载数据...");
|
|
785
839
|
const tTotal = performance.now();
|
|
786
|
-
this.logInfo(
|
|
840
|
+
this.logInfo('开始预加载数据');
|
|
787
841
|
try {
|
|
788
842
|
const tIndexDB = performance.now();
|
|
789
843
|
const cachedData = await this.loadProductsFromIndexDB();
|
|
790
|
-
(0,
|
|
844
|
+
(0, _product.perfMark)('preload.loadFromIndexDB', performance.now() - tIndexDB, {
|
|
845
|
+
count: cachedData?.length ?? 0
|
|
846
|
+
});
|
|
791
847
|
if (cachedData && cachedData.length > 0) {
|
|
792
|
-
|
|
848
|
+
// IndexDB 反序列化的数据已经是全新的,无需 cloneDeep
|
|
793
849
|
this.store.list = cachedData;
|
|
794
850
|
const tSync = performance.now();
|
|
795
851
|
this.syncProductsMap();
|
|
796
|
-
(0,
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
)
|
|
801
|
-
(0, import_product.perfMark)("preload(IndexDB)", performance.now() - tTotal, {
|
|
852
|
+
(0, _product.perfMark)('preload.syncProductsMap', performance.now() - tSync, {
|
|
853
|
+
count: cachedData.length
|
|
854
|
+
});
|
|
855
|
+
this.core.effects.emit(_types.ProductsHooks.onProductsChanged, this.store.list);
|
|
856
|
+
(0, _product.perfMark)('preload(IndexDB)', performance.now() - tTotal, {
|
|
802
857
|
count: cachedData.length,
|
|
803
|
-
source:
|
|
858
|
+
source: 'IndexDB'
|
|
804
859
|
});
|
|
805
|
-
this.logInfo(
|
|
860
|
+
this.logInfo('预加载完成(从 IndexDB)', {
|
|
806
861
|
productCount: cachedData.length,
|
|
807
862
|
duration: `${Math.round(performance.now() - tTotal)}ms`,
|
|
808
|
-
source:
|
|
863
|
+
source: 'IndexDB'
|
|
809
864
|
});
|
|
810
865
|
return;
|
|
811
866
|
}
|
|
812
|
-
|
|
813
|
-
this.logInfo("IndexDB 中没有缓存数据,准备从服务器加载");
|
|
867
|
+
this.logInfo('IndexDB 中没有缓存数据,准备从服务器加载');
|
|
814
868
|
} catch (error) {
|
|
815
869
|
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
816
|
-
console.warn(
|
|
817
|
-
this.logWarning(
|
|
870
|
+
console.warn('[Products] 从 IndexDB 加载数据失败:', error);
|
|
871
|
+
this.logWarning('从 IndexDB 加载数据失败,准备从服务器加载', {
|
|
872
|
+
error: errorMessage
|
|
873
|
+
});
|
|
818
874
|
}
|
|
819
875
|
const tServer = performance.now();
|
|
820
876
|
const products = await this.loadProductsByServer();
|
|
821
|
-
(0,
|
|
877
|
+
(0, _product.perfMark)('preload.loadFromServer', performance.now() - tServer, {
|
|
878
|
+
count: products?.length ?? 0
|
|
879
|
+
});
|
|
822
880
|
if (products && products.length > 0) {
|
|
881
|
+
// 服务器返回的数据已经是全新的,无需 cloneDeep
|
|
823
882
|
this.store.list = products;
|
|
824
883
|
const tSync = performance.now();
|
|
825
884
|
this.syncProductsMap();
|
|
826
|
-
(0,
|
|
827
|
-
|
|
828
|
-
|
|
885
|
+
(0, _product.perfMark)('preload.syncProductsMap', performance.now() - tSync, {
|
|
886
|
+
count: products.length
|
|
887
|
+
});
|
|
888
|
+
this.core.effects.emit(_types.ProductsHooks.onProductsChanged, this.store.list);
|
|
889
|
+
(0, _product.perfMark)('preload(Server)', performance.now() - tTotal, {
|
|
829
890
|
count: products.length,
|
|
830
|
-
source:
|
|
891
|
+
source: 'Server'
|
|
831
892
|
});
|
|
832
|
-
this.logInfo(
|
|
893
|
+
this.logInfo('预加载完成(从服务器)', {
|
|
833
894
|
productCount: products.length,
|
|
834
895
|
duration: `${Math.round(performance.now() - tTotal)}ms`,
|
|
835
|
-
source:
|
|
896
|
+
source: 'Server'
|
|
836
897
|
});
|
|
837
898
|
} else {
|
|
838
|
-
(0,
|
|
839
|
-
|
|
899
|
+
(0, _product.perfMark)('preload(empty)', performance.now() - tTotal, {
|
|
900
|
+
source: 'empty'
|
|
901
|
+
});
|
|
902
|
+
this.logWarning('预加载完成但未获取到数据', {
|
|
840
903
|
duration: `${Math.round(performance.now() - tTotal)}ms`
|
|
841
904
|
});
|
|
842
905
|
}
|
|
843
906
|
}
|
|
907
|
+
|
|
844
908
|
// =============================================
|
|
845
909
|
// 商品数据同步(pubsub)
|
|
846
910
|
// =============================================
|
|
911
|
+
|
|
847
912
|
/**
|
|
848
913
|
* 初始化 ProductDataSource 实例
|
|
849
914
|
* 与 pubsub 订阅和数据获取分开,仅负责创建实例
|
|
850
915
|
*/
|
|
851
916
|
initProductDataSource() {
|
|
852
|
-
|
|
853
|
-
const ProductDataSourceClass = (_b = (_a = this.core.serverOptions) == null ? void 0 : _a.All_DATA_SOURCES) == null ? void 0 : _b.ProductDataSource;
|
|
917
|
+
const ProductDataSourceClass = this.core.serverOptions?.All_DATA_SOURCES?.ProductDataSource;
|
|
854
918
|
if (!ProductDataSourceClass) {
|
|
855
|
-
this.logWarning(
|
|
919
|
+
this.logWarning('initProductDataSource: ProductDataSource 不可用');
|
|
856
920
|
return;
|
|
857
921
|
}
|
|
858
922
|
this.productDataSource = new ProductDataSourceClass();
|
|
859
|
-
this.logInfo(
|
|
923
|
+
this.logInfo('ProductDataSource 实例已创建');
|
|
860
924
|
}
|
|
925
|
+
|
|
861
926
|
/**
|
|
862
927
|
* 初始化 pubsub 订阅,监听管理后台商品变更
|
|
863
928
|
* 仅负责订阅 product / product_quotation 频道,消息通过防抖合并后批量处理
|
|
@@ -865,21 +930,22 @@ var ProductsModule = class extends import_BaseModule.BaseModule {
|
|
|
865
930
|
*/
|
|
866
931
|
setupProductSync() {
|
|
867
932
|
if (!this.productDataSource) {
|
|
868
|
-
this.logWarning(
|
|
933
|
+
this.logWarning('setupProductSync: ProductDataSource 不可用,跳过同步初始化');
|
|
869
934
|
return;
|
|
870
935
|
}
|
|
871
|
-
const createHandler =
|
|
872
|
-
const data =
|
|
873
|
-
if (!data)
|
|
874
|
-
|
|
875
|
-
console.log(`[ProductsModule] 收到同步消息 [${channelKey}]:`, data);
|
|
876
|
-
this.logInfo("收到同步消息", {
|
|
936
|
+
const createHandler = channelKey => message => {
|
|
937
|
+
const data = message?.data || message;
|
|
938
|
+
if (!data) return;
|
|
939
|
+
this.logInfo('收到同步消息', {
|
|
877
940
|
channelKey,
|
|
878
941
|
action: data.action,
|
|
879
942
|
id: data.id,
|
|
880
943
|
action_filed: data.action_filed
|
|
881
944
|
});
|
|
882
|
-
this.pendingSyncMessages.push({
|
|
945
|
+
this.pendingSyncMessages.push({
|
|
946
|
+
...data,
|
|
947
|
+
_channelKey: channelKey
|
|
948
|
+
});
|
|
883
949
|
if (this.syncTimer) {
|
|
884
950
|
clearTimeout(this.syncTimer);
|
|
885
951
|
}
|
|
@@ -889,25 +955,24 @@ var ProductsModule = class extends import_BaseModule.BaseModule {
|
|
|
889
955
|
};
|
|
890
956
|
this.productDataSource.run({
|
|
891
957
|
pubsub: {
|
|
892
|
-
callback:
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
data: res == null ? void 0 : res.data
|
|
958
|
+
callback: res => {
|
|
959
|
+
this.logInfo('sse_products_callback: 收到同步消息', {
|
|
960
|
+
data: res?.data
|
|
896
961
|
});
|
|
897
|
-
if (!
|
|
898
|
-
return;
|
|
962
|
+
if (!res?.data) return;
|
|
899
963
|
const data = res.data;
|
|
900
|
-
const channelKey = data.module ||
|
|
964
|
+
const channelKey = data.module || 'product';
|
|
901
965
|
createHandler(channelKey)(data);
|
|
902
966
|
}
|
|
903
967
|
}
|
|
904
|
-
}).catch(
|
|
905
|
-
this.logError(
|
|
968
|
+
}).catch(err => {
|
|
969
|
+
this.logError('setupProductSync: DataSource run 出错', {
|
|
906
970
|
error: err instanceof Error ? err.message : String(err)
|
|
907
971
|
});
|
|
908
972
|
});
|
|
909
|
-
this.logInfo(
|
|
973
|
+
this.logInfo('setupProductSync: pubsub 订阅已建立');
|
|
910
974
|
}
|
|
975
|
+
|
|
911
976
|
/**
|
|
912
977
|
* 处理防抖后的同步消息批次
|
|
913
978
|
*
|
|
@@ -927,58 +992,61 @@ var ProductsModule = class extends import_BaseModule.BaseModule {
|
|
|
927
992
|
* Server 层监听该事件后对变更商品增量执行 prepareProductsWithPrice 并更新缓存
|
|
928
993
|
*/
|
|
929
994
|
async processProductSyncMessages() {
|
|
930
|
-
var _a, _b, _c, _d, _e, _f;
|
|
931
995
|
const messages = [...this.pendingSyncMessages];
|
|
932
996
|
this.pendingSyncMessages = [];
|
|
933
|
-
if (messages.length === 0)
|
|
934
|
-
|
|
935
|
-
|
|
997
|
+
if (messages.length === 0) return;
|
|
998
|
+
this.logInfo('processProductSyncMessages: 开始处理', {
|
|
999
|
+
count: messages.length
|
|
1000
|
+
});
|
|
936
1001
|
const deleteIds = [];
|
|
937
|
-
const bodyUpdates =
|
|
1002
|
+
const bodyUpdates = new Map();
|
|
938
1003
|
const sseRefreshIds = [];
|
|
939
1004
|
const priceRefreshIds = [];
|
|
940
1005
|
let shouldClearPriceCache = false;
|
|
941
1006
|
for (const msg of messages) {
|
|
942
|
-
const channelKey = msg._channelKey || msg.module ||
|
|
943
|
-
if (channelKey ===
|
|
944
|
-
if (
|
|
1007
|
+
const channelKey = msg._channelKey || msg.module || 'product';
|
|
1008
|
+
if (channelKey === 'product') {
|
|
1009
|
+
if (msg.relation_product_ids?.length) {
|
|
945
1010
|
sseRefreshIds.push(...msg.relation_product_ids);
|
|
946
1011
|
}
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
|
|
950
|
-
else if (msg.id)
|
|
951
|
-
deleteIds.push(msg.id);
|
|
1012
|
+
// 1. 删除场景:operation === 'delete' 或 action === 'delete'
|
|
1013
|
+
if (msg.operation === 'delete' || msg.action === 'delete') {
|
|
1014
|
+
if (msg.ids?.length) deleteIds.push(...msg.ids);else if (msg.id) deleteIds.push(msg.id);
|
|
952
1015
|
continue;
|
|
953
1016
|
}
|
|
954
|
-
|
|
1017
|
+
|
|
1018
|
+
// 2. change_types 包含 price → 仅收集需要刷新价格的 IDs(不走 SSE 拉取)
|
|
1019
|
+
if (msg.change_types?.includes('price')) {
|
|
955
1020
|
const ids = msg.ids || (msg.id ? [msg.id] : []);
|
|
956
1021
|
priceRefreshIds.push(...ids);
|
|
957
1022
|
if (msg.body) {
|
|
958
1023
|
const bodyId = msg.body.id;
|
|
959
|
-
if (bodyId)
|
|
960
|
-
bodyUpdates.set(bodyId, msg.body);
|
|
1024
|
+
if (bodyId) bodyUpdates.set(bodyId, msg.body);
|
|
961
1025
|
}
|
|
962
1026
|
continue;
|
|
963
1027
|
}
|
|
1028
|
+
|
|
1029
|
+
// 3. 有 body → 完整数据直接覆盖本地
|
|
964
1030
|
if (msg.body) {
|
|
965
1031
|
const bodyId = msg.body.id || msg.id;
|
|
966
|
-
if (bodyId)
|
|
967
|
-
bodyUpdates.set(bodyId, msg.body);
|
|
1032
|
+
if (bodyId) bodyUpdates.set(bodyId, msg.body);
|
|
968
1033
|
continue;
|
|
969
1034
|
}
|
|
970
|
-
|
|
1035
|
+
|
|
1036
|
+
// 4. 其他情况(有 ids 无 body 无 change_types)→ SSE 拉取
|
|
1037
|
+
if (msg.ids?.length) {
|
|
971
1038
|
sseRefreshIds.push(...msg.ids);
|
|
972
1039
|
} else if (msg.id) {
|
|
973
1040
|
sseRefreshIds.push(msg.id);
|
|
974
1041
|
}
|
|
975
|
-
} else if (channelKey ===
|
|
1042
|
+
} else if (channelKey === 'product_quotation') {
|
|
1043
|
+
// 报价单变更影响范围大,标记清除全量价格缓存
|
|
976
1044
|
shouldClearPriceCache = true;
|
|
977
|
-
if (
|
|
1045
|
+
if (msg.relation_product_ids?.length) {
|
|
978
1046
|
sseRefreshIds.push(...msg.relation_product_ids);
|
|
979
1047
|
}
|
|
980
|
-
} else if ([
|
|
981
|
-
if (
|
|
1048
|
+
} else if (['product_collection', 'product_category'].includes(channelKey)) {
|
|
1049
|
+
if (msg.relation_product_ids?.length) {
|
|
982
1050
|
sseRefreshIds.push(...msg.relation_product_ids);
|
|
983
1051
|
}
|
|
984
1052
|
}
|
|
@@ -986,28 +1054,32 @@ var ProductsModule = class extends import_BaseModule.BaseModule {
|
|
|
986
1054
|
const uniqueDeleteIds = [...new Set(deleteIds)];
|
|
987
1055
|
const uniqueSSEIds = [...new Set(sseRefreshIds)];
|
|
988
1056
|
const uniquePriceIds = [...new Set(priceRefreshIds)];
|
|
1057
|
+
|
|
1058
|
+
// 1. 处理删除
|
|
989
1059
|
if (uniqueDeleteIds.length > 0) {
|
|
990
1060
|
await this.removeProductsByIds(uniqueDeleteIds);
|
|
991
1061
|
}
|
|
1062
|
+
|
|
1063
|
+
// 2. 处理 body 直接覆盖(store.list 更新,价格缓存不清除)
|
|
992
1064
|
if (bodyUpdates.size > 0) {
|
|
993
1065
|
await this.applyBodyUpdatesToStore(bodyUpdates);
|
|
994
1066
|
}
|
|
1067
|
+
|
|
1068
|
+
// 3. 处理 SSE 增量拉取(store.list 更新,价格缓存不清除)
|
|
995
1069
|
if (uniqueSSEIds.length > 0) {
|
|
996
1070
|
const freshProducts = await this.fetchProductsBySSE(uniqueSSEIds);
|
|
997
1071
|
if (freshProducts.length > 0) {
|
|
998
1072
|
await this.mergeProductsToStore(freshProducts);
|
|
999
1073
|
}
|
|
1000
|
-
this.logInfo(
|
|
1074
|
+
this.logInfo('processProductSyncMessages: SSE 增量更新完成123', {
|
|
1001
1075
|
requestedCount: uniqueSSEIds.length,
|
|
1002
1076
|
receivedCount: freshProducts.length
|
|
1003
1077
|
});
|
|
1004
1078
|
}
|
|
1005
|
-
|
|
1006
|
-
|
|
1007
|
-
|
|
1008
|
-
|
|
1009
|
-
])];
|
|
1010
|
-
this.logInfo("processProductSyncMessages: 处理完成123", {
|
|
1079
|
+
|
|
1080
|
+
// 收集所有非 delete 的变更 IDs(body + SSE + price),用于增量更新价格缓存
|
|
1081
|
+
const allChangedIds = [...new Set([...Array.from(bodyUpdates.keys()), ...uniqueSSEIds, ...uniquePriceIds])];
|
|
1082
|
+
this.logInfo('processProductSyncMessages: 处理完成123', {
|
|
1011
1083
|
deleteCount: uniqueDeleteIds.length,
|
|
1012
1084
|
bodyUpdateCount: bodyUpdates.size,
|
|
1013
1085
|
sseRefreshCount: uniqueSSEIds.length,
|
|
@@ -1017,34 +1089,47 @@ var ProductsModule = class extends import_BaseModule.BaseModule {
|
|
|
1017
1089
|
});
|
|
1018
1090
|
const hasChanges = uniqueDeleteIds.length > 0 || allChangedIds.length > 0 || shouldClearPriceCache;
|
|
1019
1091
|
if (!hasChanges) {
|
|
1020
|
-
this.logInfo(
|
|
1092
|
+
this.logInfo('processProductSyncMessages: 没有变更,不触发 onProductsSyncCompleted');
|
|
1021
1093
|
return;
|
|
1022
1094
|
}
|
|
1095
|
+
|
|
1096
|
+
// 报价单变更 → 清除全量价格缓存,后续走全量重建
|
|
1023
1097
|
if (shouldClearPriceCache) {
|
|
1024
1098
|
this.clearPriceCache();
|
|
1025
1099
|
}
|
|
1026
|
-
await this.core.effects.emit(
|
|
1100
|
+
await this.core.effects.emit(_types.ProductsHooks.onProductsSyncCompleted, {
|
|
1027
1101
|
changedIds: allChangedIds
|
|
1028
1102
|
});
|
|
1029
1103
|
}
|
|
1104
|
+
|
|
1030
1105
|
/**
|
|
1031
1106
|
* 通过 SSE 按 ids 增量拉取商品数据
|
|
1032
1107
|
* 请求 GET /shop/core/stream?type=product&ids={ids}
|
|
1033
1108
|
*/
|
|
1034
1109
|
async fetchProductsBySSE(ids) {
|
|
1035
1110
|
if (!this.productDataSource) {
|
|
1036
|
-
this.logWarning(
|
|
1111
|
+
this.logWarning('fetchProductsBySSE: ProductDataSource 不可用');
|
|
1037
1112
|
return [];
|
|
1038
1113
|
}
|
|
1039
|
-
this.logInfo(
|
|
1114
|
+
this.logInfo('fetchProductsBySSE: 开始', {
|
|
1115
|
+
ids,
|
|
1116
|
+
count: ids.length
|
|
1117
|
+
});
|
|
1040
1118
|
const t0 = performance.now();
|
|
1041
1119
|
try {
|
|
1042
1120
|
const productList = await this.productDataSource.run({
|
|
1043
|
-
sse: {
|
|
1121
|
+
sse: {
|
|
1122
|
+
query: {
|
|
1123
|
+
type: 'product',
|
|
1124
|
+
ids
|
|
1125
|
+
}
|
|
1126
|
+
}
|
|
1044
1127
|
});
|
|
1045
1128
|
const list = productList || [];
|
|
1046
|
-
(0,
|
|
1047
|
-
|
|
1129
|
+
(0, _product.perfMark)('fetchProductsBySSE', performance.now() - t0, {
|
|
1130
|
+
count: list.length
|
|
1131
|
+
});
|
|
1132
|
+
this.logInfo('fetchProductsBySSE: 成功', {
|
|
1048
1133
|
requestedCount: ids.length,
|
|
1049
1134
|
receivedCount: list.length,
|
|
1050
1135
|
duration: `${Math.round(performance.now() - t0)}ms`
|
|
@@ -1052,10 +1137,14 @@ var ProductsModule = class extends import_BaseModule.BaseModule {
|
|
|
1052
1137
|
return list;
|
|
1053
1138
|
} catch (error) {
|
|
1054
1139
|
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
1055
|
-
this.logError(
|
|
1140
|
+
this.logError('fetchProductsBySSE: 失败', {
|
|
1141
|
+
ids,
|
|
1142
|
+
error: errorMessage
|
|
1143
|
+
});
|
|
1056
1144
|
return [];
|
|
1057
1145
|
}
|
|
1058
1146
|
}
|
|
1147
|
+
|
|
1059
1148
|
/**
|
|
1060
1149
|
* 将 body 完整数据直接覆盖到本地 store(不调用报价单接口)
|
|
1061
1150
|
* 已存在的 → 直接替换;不存在的 → 追加
|
|
@@ -1063,11 +1152,13 @@ var ProductsModule = class extends import_BaseModule.BaseModule {
|
|
|
1063
1152
|
* 价格缓存由 processProductSyncMessages 末尾统一清除
|
|
1064
1153
|
*/
|
|
1065
1154
|
async applyBodyUpdatesToStore(bodyUpdates) {
|
|
1066
|
-
this.logInfo(
|
|
1155
|
+
this.logInfo('applyBodyUpdatesToStore: 开始', {
|
|
1156
|
+
count: bodyUpdates.size
|
|
1157
|
+
});
|
|
1067
1158
|
let updatedCount = 0;
|
|
1068
1159
|
let newCount = 0;
|
|
1069
|
-
const appliedIds =
|
|
1070
|
-
this.store.list = this.store.list.map(
|
|
1160
|
+
const appliedIds = new Set();
|
|
1161
|
+
this.store.list = this.store.list.map(p => {
|
|
1071
1162
|
if (bodyUpdates.has(p.id)) {
|
|
1072
1163
|
updatedCount++;
|
|
1073
1164
|
appliedIds.add(p.id);
|
|
@@ -1083,24 +1174,25 @@ var ProductsModule = class extends import_BaseModule.BaseModule {
|
|
|
1083
1174
|
}
|
|
1084
1175
|
this.syncProductsMap();
|
|
1085
1176
|
await this.saveProductsToIndexDB(this.store.list);
|
|
1086
|
-
this.core.effects.emit(
|
|
1087
|
-
this.logInfo(
|
|
1177
|
+
this.core.effects.emit(_types.ProductsHooks.onProductsChanged, this.store.list);
|
|
1178
|
+
this.logInfo('applyBodyUpdatesToStore: 完成', {
|
|
1088
1179
|
updatedCount,
|
|
1089
1180
|
newCount,
|
|
1090
1181
|
totalCount: this.store.list.length
|
|
1091
1182
|
});
|
|
1092
1183
|
}
|
|
1184
|
+
|
|
1093
1185
|
/**
|
|
1094
1186
|
* 将增量拉取的商品合并到 store
|
|
1095
1187
|
* 已存在的 → 替换;新的 → 追加
|
|
1096
1188
|
* 同时更新 store.map、IndexDB,触发 onProductsChanged
|
|
1097
1189
|
*/
|
|
1098
1190
|
async mergeProductsToStore(freshProducts) {
|
|
1099
|
-
const freshMap =
|
|
1191
|
+
const freshMap = new Map();
|
|
1100
1192
|
for (const p of freshProducts) {
|
|
1101
1193
|
freshMap.set(p.id, p);
|
|
1102
1194
|
}
|
|
1103
|
-
const updatedList = this.store.list.map(
|
|
1195
|
+
const updatedList = this.store.list.map(p => {
|
|
1104
1196
|
if (freshMap.has(p.id)) {
|
|
1105
1197
|
const fresh = freshMap.get(p.id);
|
|
1106
1198
|
freshMap.delete(p.id);
|
|
@@ -1108,6 +1200,8 @@ var ProductsModule = class extends import_BaseModule.BaseModule {
|
|
|
1108
1200
|
}
|
|
1109
1201
|
return p;
|
|
1110
1202
|
});
|
|
1203
|
+
|
|
1204
|
+
// freshMap 中剩余的是新商品(create 场景)
|
|
1111
1205
|
const newCount = freshMap.size;
|
|
1112
1206
|
for (const p of freshMap.values()) {
|
|
1113
1207
|
updatedList.push(p);
|
|
@@ -1116,13 +1210,14 @@ var ProductsModule = class extends import_BaseModule.BaseModule {
|
|
|
1116
1210
|
this.store.list = updatedList;
|
|
1117
1211
|
this.syncProductsMap();
|
|
1118
1212
|
await this.saveProductsToIndexDB(this.store.list);
|
|
1119
|
-
this.logInfo(
|
|
1213
|
+
this.logInfo('mergeProductsToStore: 合并完成', {
|
|
1120
1214
|
updatedCount,
|
|
1121
1215
|
newCount,
|
|
1122
1216
|
totalCount: this.store.list.length
|
|
1123
1217
|
});
|
|
1124
|
-
this.core.effects.emit(
|
|
1218
|
+
this.core.effects.emit(_types.ProductsHooks.onProductsChanged, this.store.list);
|
|
1125
1219
|
}
|
|
1220
|
+
|
|
1126
1221
|
/**
|
|
1127
1222
|
* 静默全量刷新:后台重新拉取全量 SSE 数据并更新本地
|
|
1128
1223
|
* 拿到完整数据后一次性替换 store,清除价格缓存,触发 onProductsSyncCompleted
|
|
@@ -1130,48 +1225,51 @@ var ProductsModule = class extends import_BaseModule.BaseModule {
|
|
|
1130
1225
|
*/
|
|
1131
1226
|
async silentRefresh() {
|
|
1132
1227
|
const t0 = performance.now();
|
|
1133
|
-
this.logInfo(
|
|
1228
|
+
this.logInfo('silentRefresh 开始');
|
|
1134
1229
|
try {
|
|
1135
1230
|
const products = await this.loadProductsByServer();
|
|
1136
1231
|
if (products && products.length > 0) {
|
|
1137
1232
|
this.store.list = products;
|
|
1138
1233
|
this.syncProductsMap();
|
|
1139
1234
|
this.clearPriceCache();
|
|
1140
|
-
this.core.effects.emit(
|
|
1141
|
-
await this.core.effects.emit(
|
|
1142
|
-
this.logInfo(
|
|
1235
|
+
this.core.effects.emit(_types.ProductsHooks.onProductsChanged, this.store.list);
|
|
1236
|
+
await this.core.effects.emit(_types.ProductsHooks.onProductsSyncCompleted, null);
|
|
1237
|
+
this.logInfo('silentRefresh 完成', {
|
|
1143
1238
|
productCount: products.length,
|
|
1144
1239
|
duration: `${Math.round(performance.now() - t0)}ms`
|
|
1145
1240
|
});
|
|
1146
1241
|
} else {
|
|
1147
|
-
this.logWarning(
|
|
1242
|
+
this.logWarning('silentRefresh: 服务器未返回数据');
|
|
1148
1243
|
}
|
|
1149
|
-
(0,
|
|
1244
|
+
(0, _product.perfMark)('silentRefresh', performance.now() - t0, {
|
|
1245
|
+
count: this.store.list.length
|
|
1246
|
+
});
|
|
1150
1247
|
return this.store.list;
|
|
1151
1248
|
} catch (error) {
|
|
1152
1249
|
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
1153
|
-
this.logError(
|
|
1250
|
+
this.logError('silentRefresh 失败', {
|
|
1154
1251
|
duration: `${Math.round(performance.now() - t0)}ms`,
|
|
1155
1252
|
error: errorMessage
|
|
1156
1253
|
});
|
|
1157
1254
|
return this.store.list;
|
|
1158
1255
|
}
|
|
1159
1256
|
}
|
|
1257
|
+
|
|
1160
1258
|
/**
|
|
1161
1259
|
* 销毁同步资源(取消 pubsub 订阅、清除定时器)
|
|
1162
1260
|
*/
|
|
1163
1261
|
destroyProductSync() {
|
|
1164
|
-
var _a;
|
|
1165
1262
|
if (this.syncTimer) {
|
|
1166
1263
|
clearTimeout(this.syncTimer);
|
|
1167
|
-
this.syncTimer =
|
|
1264
|
+
this.syncTimer = undefined;
|
|
1168
1265
|
}
|
|
1169
|
-
if (
|
|
1266
|
+
if (this.productDataSource?.destroy) {
|
|
1170
1267
|
this.productDataSource.destroy();
|
|
1171
1268
|
}
|
|
1172
1269
|
this.pendingSyncMessages = [];
|
|
1173
|
-
this.logInfo(
|
|
1270
|
+
this.logInfo('destroyProductSync: 同步资源已销毁');
|
|
1174
1271
|
}
|
|
1272
|
+
|
|
1175
1273
|
/**
|
|
1176
1274
|
* 获取模块的路由定义
|
|
1177
1275
|
* Products 模块暂不提供路由,由 Server 层统一处理
|
|
@@ -1179,8 +1277,5 @@ var ProductsModule = class extends import_BaseModule.BaseModule {
|
|
|
1179
1277
|
getRoutes() {
|
|
1180
1278
|
return [];
|
|
1181
1279
|
}
|
|
1182
|
-
}
|
|
1183
|
-
|
|
1184
|
-
0 && (module.exports = {
|
|
1185
|
-
ProductsModule
|
|
1186
|
-
});
|
|
1280
|
+
}
|
|
1281
|
+
exports.ProductsModule = ProductsModule;
|