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