@pisell/pisellos 2.2.94 → 2.2.95
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 +1 -0
- package/dist/core/index.js +7 -0
- package/dist/modules/Customer/index.d.ts +1 -0
- package/dist/modules/Customer/index.js +28 -12
- package/dist/modules/Order/index.d.ts +1 -1
- package/dist/server/index.d.ts +6 -1
- package/dist/server/index.js +40 -17
- package/dist/server/modules/order/types.d.ts +1 -1
- package/dist/server/modules/order/utils/filterBookings.js +1 -1
- package/dist/server/modules/products/index.d.ts +19 -24
- package/dist/server/modules/products/index.js +429 -600
- package/dist/server/modules/products/types.d.ts +1 -0
- package/dist/server/utils/product.d.ts +4 -0
- package/dist/server/utils/product.js +34 -0
- package/dist/solution/BookingByStep/index.d.ts +1 -1
- package/dist/solution/Sales/index.d.ts +1 -1
- package/dist/solution/Sales/index.js +72 -20
- package/dist/solution/Sales/types.d.ts +5 -4
- package/dist/types/index.d.ts +2 -0
- package/lib/core/index.d.ts +1 -0
- package/lib/core/index.js +4 -0
- package/lib/modules/Customer/index.d.ts +1 -0
- package/lib/modules/Customer/index.js +21 -6
- package/lib/modules/Order/index.d.ts +1 -1
- package/lib/server/index.d.ts +6 -1
- package/lib/server/index.js +33 -13
- package/lib/server/modules/order/types.d.ts +1 -1
- package/lib/server/modules/order/utils/filterBookings.js +1 -1
- package/lib/server/modules/products/index.d.ts +19 -24
- package/lib/server/modules/products/index.js +151 -150
- package/lib/server/modules/products/types.d.ts +1 -0
- package/lib/server/utils/product.d.ts +4 -0
- package/lib/server/utils/product.js +27 -0
- package/lib/solution/BookingByStep/index.d.ts +1 -1
- package/lib/solution/Sales/index.d.ts +1 -1
- package/lib/solution/Sales/index.js +67 -12
- package/lib/solution/Sales/types.d.ts +5 -4
- package/lib/types/index.d.ts +2 -0
- package/package.json +1 -1
|
@@ -53,14 +53,20 @@ export declare class ProductsModule extends BaseModule implements Module {
|
|
|
53
53
|
* 缓存的是已经应用了价格的完整商品列表,避免重复转换
|
|
54
54
|
* @param schedule_date 日期
|
|
55
55
|
* @param extraContext 额外的上下文数据(可选,由 Server 层传入)
|
|
56
|
+
* @param options 可选参数
|
|
57
|
+
* @param options.changedIds 变更的商品 IDs,非空时仅对这些商品增量执行 prepare 并更新缓存
|
|
56
58
|
* @returns 应用了价格的商品列表
|
|
57
59
|
*/
|
|
58
|
-
getProductsWithPrice(schedule_date: string, extraContext?: Partial<ProductFormatterContext
|
|
60
|
+
getProductsWithPrice(schedule_date: string, extraContext?: Partial<ProductFormatterContext>, options?: {
|
|
61
|
+
changedIds?: number[];
|
|
62
|
+
}): Promise<ProductData[]>;
|
|
59
63
|
/**
|
|
60
64
|
* 准备带价格的商品数据(通过格式化器流程处理)
|
|
61
65
|
* @param schedule_date 日期
|
|
62
66
|
* @param extraContext 额外的上下文数据(可选)
|
|
63
|
-
* @
|
|
67
|
+
* @param options 可选参数
|
|
68
|
+
* @param options.productIds 指定商品 IDs,仅处理这些商品;不传则处理全量
|
|
69
|
+
* @returns 处理后的商品列表
|
|
64
70
|
* @private
|
|
65
71
|
*/
|
|
66
72
|
private prepareProductsWithPrice;
|
|
@@ -156,11 +162,6 @@ export declare class ProductsModule extends BaseModule implements Module {
|
|
|
156
162
|
* 用于 pubsub 同步 create / update / batch_update 场景
|
|
157
163
|
*/
|
|
158
164
|
refreshProducts(): Promise<ProductData[]>;
|
|
159
|
-
/**
|
|
160
|
-
* 局部更新指定商品的报价单价格
|
|
161
|
-
* 遍历所有已缓存的日期,为目标商品重新获取价格并覆盖到缓存中
|
|
162
|
-
*/
|
|
163
|
-
updateProductPriceByIds(ids: number[]): Promise<void>;
|
|
164
165
|
/**
|
|
165
166
|
* 清空缓存
|
|
166
167
|
*/
|
|
@@ -202,15 +203,18 @@ export declare class ProductsModule extends BaseModule implements Module {
|
|
|
202
203
|
*
|
|
203
204
|
* product 模块:
|
|
204
205
|
* - operation === 'delete' → 本地删除
|
|
205
|
-
* -
|
|
206
|
-
* -
|
|
207
|
-
* -
|
|
206
|
+
* - change_types 包含 price → 仅收集变更 IDs(不拉商品数据)
|
|
207
|
+
* - 有 body → body 完整数据直接覆盖本地
|
|
208
|
+
* - 其他 → SSE 增量拉取
|
|
208
209
|
*
|
|
209
|
-
* product_collection / product_category
|
|
210
|
+
* product_collection / product_category:
|
|
210
211
|
* - 按 relation_product_ids SSE 拉取受影响商品
|
|
211
|
-
* - product_quotation 额外刷新报价单价格缓存
|
|
212
212
|
*
|
|
213
|
-
*
|
|
213
|
+
* product_quotation:
|
|
214
|
+
* - 报价单变更影响范围大,直接清除价格缓存走全量重建
|
|
215
|
+
*
|
|
216
|
+
* 处理完成后 emit onProductsSyncCompleted(携带 changedIds),
|
|
217
|
+
* Server 层监听该事件后对变更商品增量执行 prepareProductsWithPrice 并更新缓存
|
|
214
218
|
*/
|
|
215
219
|
private processProductSyncMessages;
|
|
216
220
|
/**
|
|
@@ -221,7 +225,8 @@ export declare class ProductsModule extends BaseModule implements Module {
|
|
|
221
225
|
/**
|
|
222
226
|
* 将 body 完整数据直接覆盖到本地 store(不调用报价单接口)
|
|
223
227
|
* 已存在的 → 直接替换;不存在的 → 追加
|
|
224
|
-
* 同时更新 Map 缓存、IndexDB
|
|
228
|
+
* 同时更新 Map 缓存、IndexDB,触发 onProductsChanged
|
|
229
|
+
* 价格缓存由 processProductSyncMessages 末尾统一清除
|
|
225
230
|
*/
|
|
226
231
|
private applyBodyUpdatesToStore;
|
|
227
232
|
/**
|
|
@@ -230,16 +235,6 @@ export declare class ProductsModule extends BaseModule implements Module {
|
|
|
230
235
|
* 同时更新 store.map、IndexDB,触发 onProductsChanged
|
|
231
236
|
*/
|
|
232
237
|
private mergeProductsToStore;
|
|
233
|
-
/**
|
|
234
|
-
* 增量更新价格缓存中变更的商品
|
|
235
|
-
* 对每个已缓存的日期 key:替换/追加最新商品数据,重新拉取这些 ID 的价格并应用
|
|
236
|
-
*/
|
|
237
|
-
private updatePriceCacheForProducts;
|
|
238
|
-
/**
|
|
239
|
-
* 全量重新拉取报价单价格并重建价格缓存
|
|
240
|
-
* 遍历当前已缓存的所有日期 key,对每个日期重新调用 loadProductsPrice
|
|
241
|
-
*/
|
|
242
|
-
private refreshAllPriceCache;
|
|
243
238
|
/**
|
|
244
239
|
* 静默全量刷新:后台重新拉取全量 SSE 数据并更新本地
|
|
245
240
|
* 拿到完整数据后一次性替换 store,清除价格缓存,触发 onProductsSyncCompleted
|
|
@@ -177,7 +177,7 @@ var ProductsModule = class extends import_BaseModule.BaseModule {
|
|
|
177
177
|
duration: `${duration}ms`,
|
|
178
178
|
error: errorMessage
|
|
179
179
|
});
|
|
180
|
-
|
|
180
|
+
return [];
|
|
181
181
|
}
|
|
182
182
|
}
|
|
183
183
|
/**
|
|
@@ -186,13 +186,70 @@ var ProductsModule = class extends import_BaseModule.BaseModule {
|
|
|
186
186
|
* 缓存的是已经应用了价格的完整商品列表,避免重复转换
|
|
187
187
|
* @param schedule_date 日期
|
|
188
188
|
* @param extraContext 额外的上下文数据(可选,由 Server 层传入)
|
|
189
|
+
* @param options 可选参数
|
|
190
|
+
* @param options.changedIds 变更的商品 IDs,非空时仅对这些商品增量执行 prepare 并更新缓存
|
|
189
191
|
* @returns 应用了价格的商品列表
|
|
190
192
|
*/
|
|
191
|
-
async getProductsWithPrice(schedule_date, extraContext) {
|
|
193
|
+
async getProductsWithPrice(schedule_date, extraContext, options) {
|
|
192
194
|
const t0 = performance.now();
|
|
193
195
|
const cacheKey = schedule_date;
|
|
196
|
+
const changedIds = options == null ? void 0 : options.changedIds;
|
|
194
197
|
if (this.productsPriceCache.has(cacheKey)) {
|
|
195
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
|
+
}
|
|
196
253
|
(0, import_product.perfMark)("getProductsWithPrice(cacheHit)", performance.now() - t0, {
|
|
197
254
|
cacheKey,
|
|
198
255
|
count: cachedProducts.length
|
|
@@ -221,21 +278,38 @@ var ProductsModule = class extends import_BaseModule.BaseModule {
|
|
|
221
278
|
* 准备带价格的商品数据(通过格式化器流程处理)
|
|
222
279
|
* @param schedule_date 日期
|
|
223
280
|
* @param extraContext 额外的上下文数据(可选)
|
|
224
|
-
* @
|
|
281
|
+
* @param options 可选参数
|
|
282
|
+
* @param options.productIds 指定商品 IDs,仅处理这些商品;不传则处理全量
|
|
283
|
+
* @returns 处理后的商品列表
|
|
225
284
|
* @private
|
|
226
285
|
*/
|
|
227
|
-
async prepareProductsWithPrice(schedule_date, extraContext) {
|
|
286
|
+
async prepareProductsWithPrice(schedule_date, extraContext, options) {
|
|
287
|
+
var _a, _b;
|
|
228
288
|
const tTotal = performance.now();
|
|
229
|
-
|
|
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
|
+
});
|
|
230
296
|
try {
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
ids
|
|
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 });
|
|
237
311
|
}
|
|
238
|
-
|
|
312
|
+
this.logInfo("获取到商品列表", { productCount: products.length });
|
|
239
313
|
const tPrice = performance.now();
|
|
240
314
|
const priceData = await this.loadProductsPrice({
|
|
241
315
|
ids,
|
|
@@ -246,26 +320,31 @@ var ProductsModule = class extends import_BaseModule.BaseModule {
|
|
|
246
320
|
const context = {
|
|
247
321
|
schedule_date,
|
|
248
322
|
priceData,
|
|
323
|
+
locale: (_b = (_a = this.core) == null ? void 0 : _a.context) == null ? void 0 : _b.locale,
|
|
249
324
|
...extraContext
|
|
250
325
|
};
|
|
251
326
|
const tFormat = performance.now();
|
|
252
|
-
const processedProducts = await this.applyFormatters(
|
|
327
|
+
const processedProducts = await this.applyFormatters(products, context);
|
|
253
328
|
(0, import_product.perfMark)("prepareProducts.applyFormatters", performance.now() - tFormat, {
|
|
254
|
-
count:
|
|
329
|
+
count: products.length,
|
|
255
330
|
formatterCount: this.formatters.length
|
|
256
331
|
});
|
|
257
332
|
this.logInfo("prepareProductsWithPrice 处理完成", {
|
|
258
|
-
|
|
333
|
+
mode: isIncremental ? "incremental" : "full",
|
|
334
|
+
originalProductCount: products.length,
|
|
259
335
|
processedProductCount: processedProducts.length,
|
|
260
336
|
formatterCount: this.formatters.length
|
|
261
337
|
});
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
338
|
+
if (!isIncremental) {
|
|
339
|
+
await this.core.effects.emit(
|
|
340
|
+
import_types.ProductsHooks.onProductsPriceApplied,
|
|
341
|
+
processedProducts
|
|
342
|
+
);
|
|
343
|
+
}
|
|
266
344
|
(0, import_product.perfMark)("prepareProductsWithPrice", performance.now() - tTotal, {
|
|
267
|
-
productCount:
|
|
268
|
-
formatterCount: this.formatters.length
|
|
345
|
+
productCount: products.length,
|
|
346
|
+
formatterCount: this.formatters.length,
|
|
347
|
+
mode: isIncremental ? "incremental" : "full"
|
|
269
348
|
});
|
|
270
349
|
return processedProducts;
|
|
271
350
|
} catch (err) {
|
|
@@ -340,10 +419,14 @@ var ProductsModule = class extends import_BaseModule.BaseModule {
|
|
|
340
419
|
console.log(`[ProductsModule] 💰 应用价格数据到 ${products.length} 个商品`);
|
|
341
420
|
return (0, import_product.applyPriceDataToProducts)(products, context.priceData);
|
|
342
421
|
};
|
|
422
|
+
const i18nFormatter = (products, context) => {
|
|
423
|
+
return (0, import_product.applyI18nToProducts)(products, context.locale);
|
|
424
|
+
};
|
|
343
425
|
const detailValueFormatter = (products, context) => {
|
|
344
426
|
return (0, import_product.applyDetailValueToProducts)(products, context);
|
|
345
427
|
};
|
|
346
428
|
this.formatters.unshift(priceFormatter);
|
|
429
|
+
this.formatters.push(i18nFormatter);
|
|
347
430
|
this.formatters.push(detailValueFormatter);
|
|
348
431
|
this.isPriceFormatterRegistered = true;
|
|
349
432
|
console.log("[ProductsModule] ✅ 内置价格格式化器已注册(第 1 个)");
|
|
@@ -604,29 +687,6 @@ var ProductsModule = class extends import_BaseModule.BaseModule {
|
|
|
604
687
|
(0, import_product.perfMark)("refreshProducts", performance.now() - tTotal, { count: this.store.list.length });
|
|
605
688
|
return this.store.list;
|
|
606
689
|
}
|
|
607
|
-
/**
|
|
608
|
-
* 局部更新指定商品的报价单价格
|
|
609
|
-
* 遍历所有已缓存的日期,为目标商品重新获取价格并覆盖到缓存中
|
|
610
|
-
*/
|
|
611
|
-
async updateProductPriceByIds(ids) {
|
|
612
|
-
this.logInfo("updateProductPriceByIds", { ids });
|
|
613
|
-
for (const [dateKey, cachedProducts] of this.productsPriceCache.entries()) {
|
|
614
|
-
try {
|
|
615
|
-
const priceData = await this.loadProductsPrice({
|
|
616
|
-
ids,
|
|
617
|
-
schedule_date: dateKey
|
|
618
|
-
});
|
|
619
|
-
if (priceData && priceData.length > 0) {
|
|
620
|
-
const updatedProducts = (0, import_product.applyPriceDataToProducts)(cachedProducts, priceData);
|
|
621
|
-
this.productsPriceCache.set(dateKey, updatedProducts);
|
|
622
|
-
this.logInfo("updateProductPriceByIds: 缓存已更新", { dateKey, priceDataCount: priceData.length });
|
|
623
|
-
}
|
|
624
|
-
} catch (error) {
|
|
625
|
-
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
626
|
-
this.logError("updateProductPriceByIds: 失败", { dateKey, ids, error: errorMessage });
|
|
627
|
-
}
|
|
628
|
-
}
|
|
629
|
-
}
|
|
630
690
|
/**
|
|
631
691
|
* 清空缓存
|
|
632
692
|
*/
|
|
@@ -688,7 +748,7 @@ var ProductsModule = class extends import_BaseModule.BaseModule {
|
|
|
688
748
|
try {
|
|
689
749
|
const t0 = performance.now();
|
|
690
750
|
await this.dbManager.clear(INDEXDB_STORE_NAME);
|
|
691
|
-
await this.dbManager.
|
|
751
|
+
await this.dbManager.bulkUpdate(INDEXDB_STORE_NAME, products);
|
|
692
752
|
(0, import_product.perfMark)("saveProductsToIndexDB", performance.now() - t0, { count: products.length });
|
|
693
753
|
console.log(
|
|
694
754
|
`[Products] 已将 ${products.length} 个商品平铺保存到 IndexDB`
|
|
@@ -853,18 +913,21 @@ var ProductsModule = class extends import_BaseModule.BaseModule {
|
|
|
853
913
|
*
|
|
854
914
|
* product 模块:
|
|
855
915
|
* - operation === 'delete' → 本地删除
|
|
856
|
-
* -
|
|
857
|
-
* -
|
|
858
|
-
* -
|
|
916
|
+
* - change_types 包含 price → 仅收集变更 IDs(不拉商品数据)
|
|
917
|
+
* - 有 body → body 完整数据直接覆盖本地
|
|
918
|
+
* - 其他 → SSE 增量拉取
|
|
859
919
|
*
|
|
860
|
-
* product_collection / product_category
|
|
920
|
+
* product_collection / product_category:
|
|
861
921
|
* - 按 relation_product_ids SSE 拉取受影响商品
|
|
862
|
-
* - product_quotation 额外刷新报价单价格缓存
|
|
863
922
|
*
|
|
864
|
-
*
|
|
923
|
+
* product_quotation:
|
|
924
|
+
* - 报价单变更影响范围大,直接清除价格缓存走全量重建
|
|
925
|
+
*
|
|
926
|
+
* 处理完成后 emit onProductsSyncCompleted(携带 changedIds),
|
|
927
|
+
* Server 层监听该事件后对变更商品增量执行 prepareProductsWithPrice 并更新缓存
|
|
865
928
|
*/
|
|
866
929
|
async processProductSyncMessages() {
|
|
867
|
-
var _a, _b, _c, _d, _e;
|
|
930
|
+
var _a, _b, _c, _d, _e, _f;
|
|
868
931
|
const messages = [...this.pendingSyncMessages];
|
|
869
932
|
this.pendingSyncMessages = [];
|
|
870
933
|
if (messages.length === 0)
|
|
@@ -874,24 +937,28 @@ var ProductsModule = class extends import_BaseModule.BaseModule {
|
|
|
874
937
|
const bodyUpdates = /* @__PURE__ */ new Map();
|
|
875
938
|
const sseRefreshIds = [];
|
|
876
939
|
const priceRefreshIds = [];
|
|
940
|
+
let shouldClearPriceCache = false;
|
|
877
941
|
for (const msg of messages) {
|
|
878
942
|
const channelKey = msg._channelKey || msg.module || "product";
|
|
879
943
|
if (channelKey === "product") {
|
|
944
|
+
if ((_a = msg.relation_product_ids) == null ? void 0 : _a.length) {
|
|
945
|
+
sseRefreshIds.push(...msg.relation_product_ids);
|
|
946
|
+
}
|
|
880
947
|
if (msg.operation === "delete" || msg.action === "delete") {
|
|
881
|
-
if ((
|
|
948
|
+
if ((_b = msg.ids) == null ? void 0 : _b.length)
|
|
882
949
|
deleteIds.push(...msg.ids);
|
|
883
950
|
else if (msg.id)
|
|
884
951
|
deleteIds.push(msg.id);
|
|
885
952
|
continue;
|
|
886
953
|
}
|
|
887
|
-
if (((_b = msg.change_types) == null ? void 0 : _b.length) && msg.change_types.every((t) => t === "stock")) {
|
|
888
|
-
this.logInfo("跳过仅库存变更", { ids: msg.ids });
|
|
889
|
-
continue;
|
|
890
|
-
}
|
|
891
954
|
if ((_c = msg.change_types) == null ? void 0 : _c.includes("price")) {
|
|
892
955
|
const ids = msg.ids || (msg.id ? [msg.id] : []);
|
|
893
|
-
sseRefreshIds.push(...ids);
|
|
894
956
|
priceRefreshIds.push(...ids);
|
|
957
|
+
if (msg.body) {
|
|
958
|
+
const bodyId = msg.body.id;
|
|
959
|
+
if (bodyId)
|
|
960
|
+
bodyUpdates.set(bodyId, msg.body);
|
|
961
|
+
}
|
|
895
962
|
continue;
|
|
896
963
|
}
|
|
897
964
|
if (msg.body) {
|
|
@@ -905,13 +972,14 @@ var ProductsModule = class extends import_BaseModule.BaseModule {
|
|
|
905
972
|
} else if (msg.id) {
|
|
906
973
|
sseRefreshIds.push(msg.id);
|
|
907
974
|
}
|
|
908
|
-
} else if (
|
|
975
|
+
} else if (channelKey === "product_quotation") {
|
|
976
|
+
shouldClearPriceCache = true;
|
|
909
977
|
if ((_e = msg.relation_product_ids) == null ? void 0 : _e.length) {
|
|
910
978
|
sseRefreshIds.push(...msg.relation_product_ids);
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
|
|
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);
|
|
915
983
|
}
|
|
916
984
|
}
|
|
917
985
|
}
|
|
@@ -928,29 +996,36 @@ var ProductsModule = class extends import_BaseModule.BaseModule {
|
|
|
928
996
|
const freshProducts = await this.fetchProductsBySSE(uniqueSSEIds);
|
|
929
997
|
if (freshProducts.length > 0) {
|
|
930
998
|
await this.mergeProductsToStore(freshProducts);
|
|
931
|
-
await this.updatePriceCacheForProducts(freshProducts);
|
|
932
999
|
}
|
|
933
|
-
this.logInfo("processProductSyncMessages: SSE 增量更新完成", {
|
|
1000
|
+
this.logInfo("processProductSyncMessages: SSE 增量更新完成123", {
|
|
934
1001
|
requestedCount: uniqueSSEIds.length,
|
|
935
1002
|
receivedCount: freshProducts.length
|
|
936
1003
|
});
|
|
937
1004
|
}
|
|
938
|
-
const
|
|
939
|
-
|
|
940
|
-
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
this.logInfo("processProductSyncMessages: 处理完成", {
|
|
1005
|
+
const allChangedIds = [.../* @__PURE__ */ new Set([
|
|
1006
|
+
...Array.from(bodyUpdates.keys()),
|
|
1007
|
+
...uniqueSSEIds,
|
|
1008
|
+
...uniquePriceIds
|
|
1009
|
+
])];
|
|
1010
|
+
this.logInfo("processProductSyncMessages: 处理完成123", {
|
|
944
1011
|
deleteCount: uniqueDeleteIds.length,
|
|
945
1012
|
bodyUpdateCount: bodyUpdates.size,
|
|
946
1013
|
sseRefreshCount: uniqueSSEIds.length,
|
|
947
|
-
priceRefreshCount: uniquePriceIds.length
|
|
1014
|
+
priceRefreshCount: uniquePriceIds.length,
|
|
1015
|
+
allChangedIdsCount: allChangedIds.length,
|
|
1016
|
+
shouldClearPriceCache
|
|
948
1017
|
});
|
|
949
|
-
|
|
1018
|
+
const hasChanges = uniqueDeleteIds.length > 0 || allChangedIds.length > 0 || shouldClearPriceCache;
|
|
1019
|
+
if (!hasChanges) {
|
|
950
1020
|
this.logInfo("processProductSyncMessages: 没有变更,不触发 onProductsSyncCompleted");
|
|
951
1021
|
return;
|
|
952
1022
|
}
|
|
953
|
-
|
|
1023
|
+
if (shouldClearPriceCache) {
|
|
1024
|
+
this.clearPriceCache();
|
|
1025
|
+
}
|
|
1026
|
+
await this.core.effects.emit(import_types.ProductsHooks.onProductsSyncCompleted, {
|
|
1027
|
+
changedIds: allChangedIds
|
|
1028
|
+
});
|
|
954
1029
|
}
|
|
955
1030
|
/**
|
|
956
1031
|
* 通过 SSE 按 ids 增量拉取商品数据
|
|
@@ -984,7 +1059,8 @@ var ProductsModule = class extends import_BaseModule.BaseModule {
|
|
|
984
1059
|
/**
|
|
985
1060
|
* 将 body 完整数据直接覆盖到本地 store(不调用报价单接口)
|
|
986
1061
|
* 已存在的 → 直接替换;不存在的 → 追加
|
|
987
|
-
* 同时更新 Map 缓存、IndexDB
|
|
1062
|
+
* 同时更新 Map 缓存、IndexDB,触发 onProductsChanged
|
|
1063
|
+
* 价格缓存由 processProductSyncMessages 末尾统一清除
|
|
988
1064
|
*/
|
|
989
1065
|
async applyBodyUpdatesToStore(bodyUpdates) {
|
|
990
1066
|
this.logInfo("applyBodyUpdatesToStore: 开始", { count: bodyUpdates.size });
|
|
@@ -1007,7 +1083,6 @@ var ProductsModule = class extends import_BaseModule.BaseModule {
|
|
|
1007
1083
|
}
|
|
1008
1084
|
this.syncProductsMap();
|
|
1009
1085
|
await this.saveProductsToIndexDB(this.store.list);
|
|
1010
|
-
await this.updatePriceCacheForProducts([...bodyUpdates.values()]);
|
|
1011
1086
|
this.core.effects.emit(import_types.ProductsHooks.onProductsChanged, this.store.list);
|
|
1012
1087
|
this.logInfo("applyBodyUpdatesToStore: 完成", {
|
|
1013
1088
|
updatedCount,
|
|
@@ -1048,80 +1123,6 @@ var ProductsModule = class extends import_BaseModule.BaseModule {
|
|
|
1048
1123
|
});
|
|
1049
1124
|
this.core.effects.emit(import_types.ProductsHooks.onProductsChanged, this.store.list);
|
|
1050
1125
|
}
|
|
1051
|
-
/**
|
|
1052
|
-
* 增量更新价格缓存中变更的商品
|
|
1053
|
-
* 对每个已缓存的日期 key:替换/追加最新商品数据,重新拉取这些 ID 的价格并应用
|
|
1054
|
-
*/
|
|
1055
|
-
async updatePriceCacheForProducts(freshProducts) {
|
|
1056
|
-
if (this.productsPriceCache.size === 0)
|
|
1057
|
-
return;
|
|
1058
|
-
const freshIds = freshProducts.map((p) => p.id);
|
|
1059
|
-
const freshMap = /* @__PURE__ */ new Map();
|
|
1060
|
-
for (const p of freshProducts) {
|
|
1061
|
-
freshMap.set(p.id, p);
|
|
1062
|
-
}
|
|
1063
|
-
this.logInfo("updatePriceCacheForProducts: 开始", {
|
|
1064
|
-
freshIds,
|
|
1065
|
-
cachedDateCount: this.productsPriceCache.size
|
|
1066
|
-
});
|
|
1067
|
-
for (const [dateKey, cachedProducts] of this.productsPriceCache.entries()) {
|
|
1068
|
-
try {
|
|
1069
|
-
const updatedList = cachedProducts.map((p) => {
|
|
1070
|
-
const fresh = freshMap.get(p.id);
|
|
1071
|
-
return fresh ? { ...fresh } : p;
|
|
1072
|
-
});
|
|
1073
|
-
const existingIds = new Set(cachedProducts.map((p) => p.id));
|
|
1074
|
-
for (const p of freshProducts) {
|
|
1075
|
-
if (!existingIds.has(p.id)) {
|
|
1076
|
-
updatedList.push({ ...p });
|
|
1077
|
-
}
|
|
1078
|
-
}
|
|
1079
|
-
const priceData = await this.loadProductsPrice({
|
|
1080
|
-
ids: freshIds,
|
|
1081
|
-
schedule_date: dateKey
|
|
1082
|
-
});
|
|
1083
|
-
const result = priceData && priceData.length > 0 ? (0, import_product.applyPriceDataToProducts)(updatedList, priceData) : updatedList;
|
|
1084
|
-
this.productsPriceCache.set(dateKey, result);
|
|
1085
|
-
this.logInfo("updatePriceCacheForProducts: 日期缓存已更新", {
|
|
1086
|
-
dateKey,
|
|
1087
|
-
updatedIds: freshIds
|
|
1088
|
-
});
|
|
1089
|
-
} catch (error) {
|
|
1090
|
-
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
1091
|
-
this.logError("updatePriceCacheForProducts: 失败", { dateKey, error: errorMessage });
|
|
1092
|
-
}
|
|
1093
|
-
}
|
|
1094
|
-
}
|
|
1095
|
-
/**
|
|
1096
|
-
* 全量重新拉取报价单价格并重建价格缓存
|
|
1097
|
-
* 遍历当前已缓存的所有日期 key,对每个日期重新调用 loadProductsPrice
|
|
1098
|
-
*/
|
|
1099
|
-
async refreshAllPriceCache() {
|
|
1100
|
-
const allProducts = this.getProductsRef();
|
|
1101
|
-
if (allProducts.length === 0)
|
|
1102
|
-
return;
|
|
1103
|
-
const ids = allProducts.map((p) => p.id);
|
|
1104
|
-
const dateKeys = Array.from(this.productsPriceCache.keys());
|
|
1105
|
-
this.clearPriceCache();
|
|
1106
|
-
if (dateKeys.length === 0) {
|
|
1107
|
-
this.logInfo("refreshAllPriceCache: 无已缓存日期,跳过");
|
|
1108
|
-
return;
|
|
1109
|
-
}
|
|
1110
|
-
this.logInfo("refreshAllPriceCache: 开始重新拉取", { dateKeys, productCount: ids.length });
|
|
1111
|
-
for (const dateKey of dateKeys) {
|
|
1112
|
-
try {
|
|
1113
|
-
const priceData = await this.loadProductsPrice({ ids, schedule_date: dateKey });
|
|
1114
|
-
if (priceData && priceData.length > 0) {
|
|
1115
|
-
const updatedProducts = (0, import_product.applyPriceDataToProducts)(allProducts, priceData);
|
|
1116
|
-
this.productsPriceCache.set(dateKey, updatedProducts);
|
|
1117
|
-
}
|
|
1118
|
-
this.logInfo("refreshAllPriceCache: 日期缓存已更新", { dateKey });
|
|
1119
|
-
} catch (error) {
|
|
1120
|
-
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
1121
|
-
this.logError("refreshAllPriceCache: 失败", { dateKey, error: errorMessage });
|
|
1122
|
-
}
|
|
1123
|
-
}
|
|
1124
|
-
}
|
|
1125
1126
|
/**
|
|
1126
1127
|
* 静默全量刷新:后台重新拉取全量 SSE 数据并更新本地
|
|
1127
1128
|
* 拿到完整数据后一次性替换 store,清除价格缓存,触发 onProductsSyncCompleted
|
|
@@ -10,6 +10,10 @@ export declare function perfMark(label: string, durationMs: number, meta?: Recor
|
|
|
10
10
|
*/
|
|
11
11
|
export declare function applyPriceDataToProducts(products: ProductData[], priceData: LoadProductsPriceData[]): ProductData[];
|
|
12
12
|
export declare const getIsSessionProduct: (product: ProductData) => boolean;
|
|
13
|
+
/**
|
|
14
|
+
* 根据 locale 将商品的 i18n 字段覆盖到对应原始字段
|
|
15
|
+
*/
|
|
16
|
+
export declare function applyI18nToProducts(products: ProductData[], locale?: string): ProductData[];
|
|
13
17
|
/**
|
|
14
18
|
* 将详情值数据应用到商品列表
|
|
15
19
|
* @param products 商品列表
|
|
@@ -20,6 +20,7 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
|
|
|
20
20
|
var product_exports = {};
|
|
21
21
|
__export(product_exports, {
|
|
22
22
|
applyDetailValueToProducts: () => applyDetailValueToProducts,
|
|
23
|
+
applyI18nToProducts: () => applyI18nToProducts,
|
|
23
24
|
applyPriceDataToProducts: () => applyPriceDataToProducts,
|
|
24
25
|
getIsSessionProduct: () => getIsSessionProduct,
|
|
25
26
|
perfMark: () => perfMark
|
|
@@ -155,6 +156,31 @@ var getIsOpenDetailModal = (product, context) => {
|
|
|
155
156
|
scheduleTimeSlots
|
|
156
157
|
};
|
|
157
158
|
};
|
|
159
|
+
var I18N_FIELD_MAP = {
|
|
160
|
+
title_i18n: "title",
|
|
161
|
+
subtitle_i18n: "subtitle",
|
|
162
|
+
description_i18n: "description",
|
|
163
|
+
cover_i18n: "cover"
|
|
164
|
+
};
|
|
165
|
+
function applyI18nToProducts(products, locale) {
|
|
166
|
+
if (!locale)
|
|
167
|
+
return products;
|
|
168
|
+
return products.map((product) => {
|
|
169
|
+
let patched = null;
|
|
170
|
+
for (const [i18nKey, originalKey] of Object.entries(I18N_FIELD_MAP)) {
|
|
171
|
+
const i18nDict = product[i18nKey];
|
|
172
|
+
if (!i18nDict)
|
|
173
|
+
continue;
|
|
174
|
+
const localizedValue = i18nDict[locale];
|
|
175
|
+
if (localizedValue) {
|
|
176
|
+
if (!patched)
|
|
177
|
+
patched = { ...product };
|
|
178
|
+
patched[originalKey] = localizedValue;
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
return patched ?? product;
|
|
182
|
+
});
|
|
183
|
+
}
|
|
158
184
|
var formatDataKey = (data) => {
|
|
159
185
|
var _a, _b, _c, _d;
|
|
160
186
|
const _data = {
|
|
@@ -258,6 +284,7 @@ function applyDetailValueToProducts(products, context) {
|
|
|
258
284
|
// Annotate the CommonJS export names for ESM import in node:
|
|
259
285
|
0 && (module.exports = {
|
|
260
286
|
applyDetailValueToProducts,
|
|
287
|
+
applyI18nToProducts,
|
|
261
288
|
applyPriceDataToProducts,
|
|
262
289
|
getIsSessionProduct,
|
|
263
290
|
perfMark
|
|
@@ -344,7 +344,7 @@ export declare class BookingByStepImpl extends BaseModule implements Module {
|
|
|
344
344
|
};
|
|
345
345
|
setOtherData(key: string, value: any): void;
|
|
346
346
|
getOtherData(key: string): any;
|
|
347
|
-
getProductTypeById(id: number): Promise<"
|
|
347
|
+
getProductTypeById(id: number): Promise<"duration" | "session" | "normal">;
|
|
348
348
|
/**
|
|
349
349
|
* 提供给 UI 的方法,减轻 UI 层的计算压力,UI 层只需要传递 cartItemId 和 resourceCode 即返回对应的 renderList
|
|
350
350
|
*
|
|
@@ -58,7 +58,7 @@ export declare class SalesImpl extends BaseModule implements Module, SalesModule
|
|
|
58
58
|
* - n = booking 数量
|
|
59
59
|
* - s = 时间片数量
|
|
60
60
|
*/
|
|
61
|
-
getTimelineHighlights(bookingList?: BookingData[]): SalesTimelineHighlights;
|
|
61
|
+
getTimelineHighlights(bookingList?: BookingData[], startDateTime?: string, endDateTime?: string): SalesTimelineHighlights;
|
|
62
62
|
/** dayjs 未启用插件时,手动封装 >= 判断 */
|
|
63
63
|
private isSameOrAfter;
|
|
64
64
|
/** dayjs 未启用插件时,手动封装 <= 判断 */
|