@pisell/pisellos 2.2.190 → 2.2.191
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/model/strategy/adapter/promotion/index.js +0 -9
- package/dist/modules/BookingContext/utils/buildCacheItemFromOrderLine.js +2 -1
- package/dist/modules/BookingContext/utils/buildNormalProductCacheItemFromOrderLine.js +2 -7
- package/dist/modules/Cart/utils/cartProduct.js +13 -2
- package/dist/modules/Order/index.d.ts +1 -3
- package/dist/modules/Order/index.js +69 -95
- package/dist/modules/Order/types.d.ts +9 -6
- package/dist/modules/Order/utils.d.ts +9 -2
- package/dist/modules/Order/utils.js +36 -13
- package/dist/modules/SalesSummary/types.d.ts +3 -1
- package/dist/modules/SalesSummary/utils.js +7 -3
- package/dist/server/modules/order/index.d.ts +126 -6
- package/dist/server/modules/order/index.js +1204 -285
- package/dist/server/modules/order/types.d.ts +0 -2
- package/dist/server/modules/products/index.d.ts +90 -6
- package/dist/server/modules/products/index.js +1017 -361
- package/dist/solution/BaseSales/index.js +13 -7
- package/dist/solution/BaseSales/types.d.ts +8 -5
- package/dist/solution/BaseSales/types.js +2 -3
- package/dist/solution/BaseSales/utils/cartPromotion.js +7 -7
- package/dist/solution/BaseSales/utils/transformBaseProductToOrderProduct.d.ts +1 -0
- package/dist/solution/BaseSales/utils/transformBaseProductToOrderProduct.js +16 -13
- package/dist/solution/BaseSales/utils.d.ts +1 -1
- package/dist/solution/BaseSales/utils.js +22 -19
- package/dist/solution/BookingByStep/index.d.ts +1 -1
- package/dist/solution/BookingTicket/index.d.ts +1 -1
- package/dist/solution/ScanOrder/index.js +7 -5
- package/dist/solution/ScanOrder/types.d.ts +8 -5
- package/dist/solution/ScanOrder/types.js +2 -3
- package/dist/solution/ScanOrder/utils.d.ts +1 -1
- package/dist/solution/ScanOrder/utils.js +26 -23
- package/dist/solution/VenueBooking/index.js +5 -3
- package/dist/solution/VenueBooking/sales-section-4-annotated.json +0 -2
- package/lib/model/strategy/adapter/promotion/index.js +0 -49
- package/lib/modules/BookingContext/utils/buildCacheItemFromOrderLine.js +2 -1
- package/lib/modules/BookingContext/utils/buildNormalProductCacheItemFromOrderLine.js +2 -1
- package/lib/modules/Cart/utils/cartProduct.js +9 -2
- package/lib/modules/Order/index.d.ts +1 -3
- package/lib/modules/Order/index.js +45 -58
- package/lib/modules/Order/types.d.ts +9 -6
- package/lib/modules/Order/utils.d.ts +9 -2
- package/lib/modules/Order/utils.js +41 -10
- package/lib/modules/SalesSummary/types.d.ts +3 -1
- package/lib/modules/SalesSummary/utils.js +5 -3
- package/lib/server/modules/order/index.d.ts +126 -6
- package/lib/server/modules/order/index.js +722 -42
- package/lib/server/modules/order/types.d.ts +0 -2
- package/lib/server/modules/products/index.d.ts +90 -6
- package/lib/server/modules/products/index.js +492 -30
- package/lib/solution/BaseSales/index.js +14 -5
- package/lib/solution/BaseSales/types.d.ts +8 -5
- package/lib/solution/BaseSales/utils/cartPromotion.js +6 -6
- package/lib/solution/BaseSales/utils/transformBaseProductToOrderProduct.d.ts +1 -0
- package/lib/solution/BaseSales/utils/transformBaseProductToOrderProduct.js +21 -14
- package/lib/solution/BaseSales/utils.d.ts +1 -1
- package/lib/solution/BaseSales/utils.js +11 -7
- package/lib/solution/BookingByStep/index.d.ts +1 -1
- package/lib/solution/BookingTicket/index.d.ts +1 -1
- package/lib/solution/ScanOrder/index.js +4 -4
- package/lib/solution/ScanOrder/types.d.ts +8 -5
- package/lib/solution/ScanOrder/utils.d.ts +1 -1
- package/lib/solution/ScanOrder/utils.js +12 -8
- package/lib/solution/VenueBooking/index.js +2 -2
- package/lib/solution/VenueBooking/sales-section-4-annotated.json +0 -2
- package/package.json +1 -1
- package/dist/solution/Checkout/appointmentDemo.json +0 -1
- package/lib/solution/Checkout/appointmentDemo.json +0 -1
|
@@ -27,7 +27,11 @@ var import_Quotation = require("../../../modules/Quotation");
|
|
|
27
27
|
var import_types = require("./types");
|
|
28
28
|
var import_product = require("../../utils/product");
|
|
29
29
|
var INDEXDB_STORE_NAME = "products";
|
|
30
|
+
var PRODUCT_FLOW_LOG_WINDOW_KEY = "__productModuleFlowLogs";
|
|
31
|
+
var PRODUCT_WRITE_STATS_WINDOW_KEY = "__productModuleWriteStats";
|
|
32
|
+
var PRODUCT_FLOW_LOG_MAX_LENGTH = 1e3;
|
|
30
33
|
var PRODUCT_SYNC_DEBOUNCE_MS = 1e4;
|
|
34
|
+
var PRODUCT_SILENT_REFRESH_MIN_INTERVAL_MS = 3e4;
|
|
31
35
|
var ProductsModule = class extends import_BaseModule.BaseModule {
|
|
32
36
|
constructor(name, version) {
|
|
33
37
|
super(name, version);
|
|
@@ -46,6 +50,14 @@ var ProductsModule = class extends import_BaseModule.BaseModule {
|
|
|
46
50
|
this.isPriceFormatterRegistered = false;
|
|
47
51
|
this.quotationBridgeLoadedKey = "";
|
|
48
52
|
this.pendingSyncMessages = [];
|
|
53
|
+
/** IndexDB 写入序号,便于日志关联 */
|
|
54
|
+
this.productFlowWriteSeq = 0;
|
|
55
|
+
/** 最近一次 SSE 全量拉取完成时间 */
|
|
56
|
+
this.lastServerFullFetchAtMs = 0;
|
|
57
|
+
/** 最近一次 preload 完成时间(含 IndexDB 命中) */
|
|
58
|
+
this.lastPreloadCompletedAtMs = 0;
|
|
59
|
+
/** IndexDB 写入串行队列,避免快照与 patch 并发交错 */
|
|
60
|
+
this.productIndexDBSaveQueue = Promise.resolve();
|
|
49
61
|
}
|
|
50
62
|
async initialize(core, options) {
|
|
51
63
|
var _a;
|
|
@@ -75,6 +87,12 @@ var ProductsModule = class extends import_BaseModule.BaseModule {
|
|
|
75
87
|
this.registerBuiltinPriceFormatter();
|
|
76
88
|
this.initProductDataSource();
|
|
77
89
|
this.setupProductSync();
|
|
90
|
+
this.initProductFlowLogs();
|
|
91
|
+
this.pushProductFlowLog("initialize.complete", {
|
|
92
|
+
hasDbManager: !!this.dbManager,
|
|
93
|
+
hasLogger: !!this.logger,
|
|
94
|
+
initialProductCount: this.store.list.length
|
|
95
|
+
});
|
|
78
96
|
this.logInfo("模块初始化完成", {
|
|
79
97
|
hasDbManager: !!this.dbManager,
|
|
80
98
|
hasLogger: !!this.logger,
|
|
@@ -131,6 +149,138 @@ var ProductsModule = class extends import_BaseModule.BaseModule {
|
|
|
131
149
|
}
|
|
132
150
|
} catch {
|
|
133
151
|
}
|
|
152
|
+
this.pushProductFlowLog(`error.${title}`, metadata);
|
|
153
|
+
}
|
|
154
|
+
/**
|
|
155
|
+
* 初始化 window 上的商品流程调试日志。
|
|
156
|
+
*
|
|
157
|
+
* @example
|
|
158
|
+
* window.clearProductModuleFlowLogs()
|
|
159
|
+
* copy(window.exportProductModuleFlowLogs?.() || '[]')
|
|
160
|
+
*/
|
|
161
|
+
initProductFlowLogs() {
|
|
162
|
+
try {
|
|
163
|
+
const win = typeof globalThis !== "undefined" ? globalThis.window : void 0;
|
|
164
|
+
if (!win)
|
|
165
|
+
return;
|
|
166
|
+
win[PRODUCT_FLOW_LOG_WINDOW_KEY] = [];
|
|
167
|
+
win[PRODUCT_WRITE_STATS_WINDOW_KEY] = {};
|
|
168
|
+
win.clearProductModuleFlowLogs = () => {
|
|
169
|
+
win[PRODUCT_FLOW_LOG_WINDOW_KEY] = [];
|
|
170
|
+
win[PRODUCT_WRITE_STATS_WINDOW_KEY] = {};
|
|
171
|
+
this.productFlowWriteSeq = 0;
|
|
172
|
+
this.pushProductFlowLog("debug.clear", {});
|
|
173
|
+
};
|
|
174
|
+
win.exportProductModuleFlowLogs = () => {
|
|
175
|
+
const logs = win[PRODUCT_FLOW_LOG_WINDOW_KEY] || [];
|
|
176
|
+
return JSON.stringify(logs, null, 2);
|
|
177
|
+
};
|
|
178
|
+
win.exportProductModuleWriteStats = () => {
|
|
179
|
+
const stats = win[PRODUCT_WRITE_STATS_WINDOW_KEY] || {};
|
|
180
|
+
return JSON.stringify(stats, null, 2);
|
|
181
|
+
};
|
|
182
|
+
win.summarizeProductModuleWrites = (topN = 20) => {
|
|
183
|
+
const stats = win[PRODUCT_WRITE_STATS_WINDOW_KEY] || {};
|
|
184
|
+
return Object.entries(stats).map(([productId, entries]) => {
|
|
185
|
+
const list = entries || [];
|
|
186
|
+
const sources = [...new Set(list.map((item) => item.source))];
|
|
187
|
+
const last = list[list.length - 1];
|
|
188
|
+
return {
|
|
189
|
+
productId: Number(productId),
|
|
190
|
+
writeCount: list.length,
|
|
191
|
+
sources,
|
|
192
|
+
lastWriteAt: (last == null ? void 0 : last.ts) || ""
|
|
193
|
+
};
|
|
194
|
+
}).sort((a, b) => b.writeCount - a.writeCount).slice(0, topN);
|
|
195
|
+
};
|
|
196
|
+
this.pushProductFlowLog("debug.init", {
|
|
197
|
+
logKey: PRODUCT_FLOW_LOG_WINDOW_KEY,
|
|
198
|
+
writeStatsKey: PRODUCT_WRITE_STATS_WINDOW_KEY,
|
|
199
|
+
indexDbStrategy: "全量 replaceProductsSnapshotInIndexDB + 增量 patchProductsInIndexDB",
|
|
200
|
+
helpers: [
|
|
201
|
+
"clearProductModuleFlowLogs",
|
|
202
|
+
"exportProductModuleFlowLogs",
|
|
203
|
+
"exportProductModuleWriteStats",
|
|
204
|
+
"summarizeProductModuleWrites"
|
|
205
|
+
]
|
|
206
|
+
});
|
|
207
|
+
} catch {
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
/**
|
|
211
|
+
* 写入 window 商品流程调试日志。
|
|
212
|
+
*
|
|
213
|
+
* @example
|
|
214
|
+
* this.pushProductFlowLog('mergeProductsToStore.start', { count: 3 })
|
|
215
|
+
*/
|
|
216
|
+
pushProductFlowLog(step, metadata) {
|
|
217
|
+
try {
|
|
218
|
+
const win = typeof globalThis !== "undefined" ? globalThis.window : void 0;
|
|
219
|
+
if (!win)
|
|
220
|
+
return;
|
|
221
|
+
if (!Array.isArray(win[PRODUCT_FLOW_LOG_WINDOW_KEY])) {
|
|
222
|
+
win[PRODUCT_FLOW_LOG_WINDOW_KEY] = [];
|
|
223
|
+
}
|
|
224
|
+
const entry = {
|
|
225
|
+
ts: (/* @__PURE__ */ new Date()).toISOString(),
|
|
226
|
+
step,
|
|
227
|
+
metadata: metadata || {}
|
|
228
|
+
};
|
|
229
|
+
win[PRODUCT_FLOW_LOG_WINDOW_KEY].push(entry);
|
|
230
|
+
const logs = win[PRODUCT_FLOW_LOG_WINDOW_KEY];
|
|
231
|
+
if (logs.length > PRODUCT_FLOW_LOG_MAX_LENGTH) {
|
|
232
|
+
logs.splice(0, logs.length - PRODUCT_FLOW_LOG_MAX_LENGTH);
|
|
233
|
+
}
|
|
234
|
+
console.log(`[ProductFlow] ${step}`, metadata || {});
|
|
235
|
+
} catch {
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
/** 当前内存 store 快照,供流程日志使用 */
|
|
239
|
+
getProductStoreSnapshot() {
|
|
240
|
+
return {
|
|
241
|
+
storeListCount: this.store.list.length,
|
|
242
|
+
storeMapCount: this.store.map.size,
|
|
243
|
+
pendingSyncCount: this.pendingSyncMessages.length
|
|
244
|
+
};
|
|
245
|
+
}
|
|
246
|
+
/**
|
|
247
|
+
* 记录 IndexDB 写入统计(按 productId 聚合)。
|
|
248
|
+
*
|
|
249
|
+
* @example
|
|
250
|
+
* this.trackProductWrite('pubsub.mergeProductsToStore', 'clear+bulkUpdate', products, { 1: 'update' })
|
|
251
|
+
*/
|
|
252
|
+
trackProductWrite(source, writeMethod, products, mergeActions = {}, defaultMergeAction = "update") {
|
|
253
|
+
this.productFlowWriteSeq += 1;
|
|
254
|
+
const writeSeq = this.productFlowWriteSeq;
|
|
255
|
+
try {
|
|
256
|
+
const win = typeof globalThis !== "undefined" ? globalThis.window : void 0;
|
|
257
|
+
if (!win)
|
|
258
|
+
return writeSeq;
|
|
259
|
+
if (!win[PRODUCT_WRITE_STATS_WINDOW_KEY]) {
|
|
260
|
+
win[PRODUCT_WRITE_STATS_WINDOW_KEY] = {};
|
|
261
|
+
}
|
|
262
|
+
const stats = win[PRODUCT_WRITE_STATS_WINDOW_KEY];
|
|
263
|
+
for (const product of products) {
|
|
264
|
+
const productId = product == null ? void 0 : product.id;
|
|
265
|
+
if (productId === void 0 || productId === null)
|
|
266
|
+
continue;
|
|
267
|
+
const mergeAction = mergeActions[productId] || defaultMergeAction;
|
|
268
|
+
const entry = {
|
|
269
|
+
ts: (/* @__PURE__ */ new Date()).toISOString(),
|
|
270
|
+
writeSeq,
|
|
271
|
+
source,
|
|
272
|
+
writeMethod,
|
|
273
|
+
productId,
|
|
274
|
+
mergeAction
|
|
275
|
+
};
|
|
276
|
+
const key = String(productId);
|
|
277
|
+
if (!stats[key])
|
|
278
|
+
stats[key] = [];
|
|
279
|
+
stats[key].push(entry);
|
|
280
|
+
}
|
|
281
|
+
} catch {
|
|
282
|
+
}
|
|
283
|
+
return writeSeq;
|
|
134
284
|
}
|
|
135
285
|
/**
|
|
136
286
|
* 加载商品价格(原始方法,不带缓存)
|
|
@@ -703,6 +853,7 @@ var ProductsModule = class extends import_BaseModule.BaseModule {
|
|
|
703
853
|
this.logWarning("loadProductsByServer: ProductDataSource 不可用");
|
|
704
854
|
return [];
|
|
705
855
|
}
|
|
856
|
+
this.pushProductFlowLog("loadProductsByServer.start", this.getProductStoreSnapshot());
|
|
706
857
|
this.logInfo("开始通过 DataSource SSE 加载商品列表");
|
|
707
858
|
const t0 = performance.now();
|
|
708
859
|
try {
|
|
@@ -710,15 +861,24 @@ var ProductsModule = class extends import_BaseModule.BaseModule {
|
|
|
710
861
|
const productList = await this.productDataSource.run({ sse: {} });
|
|
711
862
|
const list = productList || [];
|
|
712
863
|
(0, import_product.perfMark)("loadProductsByServer.SSE", performance.now() - tSSE, { count: list.length });
|
|
864
|
+
this.pushProductFlowLog("loadProductsByServer.sseSuccess", { count: list.length });
|
|
713
865
|
this.logInfo("通过 DataSource SSE 加载商品列表成功", {
|
|
714
866
|
productCount: list.length,
|
|
715
867
|
duration: `${Math.round(performance.now() - t0)}ms`
|
|
716
868
|
});
|
|
717
|
-
await this.
|
|
869
|
+
await this.replaceProductsSnapshotInIndexDB(list, "loadProductsByServer");
|
|
870
|
+
this.lastServerFullFetchAtMs = Date.now();
|
|
718
871
|
await this.core.effects.emit(import_types.ProductsHooks.onProductsLoaded, list);
|
|
872
|
+
this.pushProductFlowLog("loadProductsByServer.done", {
|
|
873
|
+
count: list.length,
|
|
874
|
+
...this.getProductStoreSnapshot()
|
|
875
|
+
});
|
|
719
876
|
(0, import_product.perfMark)("loadProductsByServer", performance.now() - t0, { count: list.length });
|
|
720
877
|
return list;
|
|
721
878
|
} catch (error) {
|
|
879
|
+
this.pushProductFlowLog("loadProductsByServer.failed", {
|
|
880
|
+
error: error instanceof Error ? error.message : String(error)
|
|
881
|
+
});
|
|
722
882
|
const duration = Math.round(performance.now() - t0);
|
|
723
883
|
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
724
884
|
console.error("[Products] 加载商品数据失败:", error);
|
|
@@ -811,11 +971,20 @@ var ProductsModule = class extends import_BaseModule.BaseModule {
|
|
|
811
971
|
* @param params 查询参数
|
|
812
972
|
*/
|
|
813
973
|
async loadProductsByServerHttp(params) {
|
|
974
|
+
this.pushProductFlowLog("loadProductsByServerHttp.start", {
|
|
975
|
+
params: params || {},
|
|
976
|
+
...this.getProductStoreSnapshot()
|
|
977
|
+
});
|
|
814
978
|
const productList = await this.fetchProductsByHttp(params);
|
|
815
979
|
if (productList.length > 0) {
|
|
816
|
-
await this.
|
|
980
|
+
await this.replaceProductsSnapshotInIndexDB(productList, "loadProductsByServerHttp");
|
|
981
|
+
this.lastServerFullFetchAtMs = Date.now();
|
|
817
982
|
await this.core.effects.emit(import_types.ProductsHooks.onProductsLoaded, productList);
|
|
818
983
|
}
|
|
984
|
+
this.pushProductFlowLog("loadProductsByServerHttp.done", {
|
|
985
|
+
count: productList.length,
|
|
986
|
+
...this.getProductStoreSnapshot()
|
|
987
|
+
});
|
|
819
988
|
return productList;
|
|
820
989
|
}
|
|
821
990
|
/**
|
|
@@ -860,6 +1029,12 @@ var ProductsModule = class extends import_BaseModule.BaseModule {
|
|
|
860
1029
|
*/
|
|
861
1030
|
async removeProductsByIds(ids) {
|
|
862
1031
|
const idSet = new Set(ids);
|
|
1032
|
+
this.pushProductFlowLog("removeProductsByIds.start", {
|
|
1033
|
+
ids,
|
|
1034
|
+
count: ids.length,
|
|
1035
|
+
writeStrategy: "delete-by-id",
|
|
1036
|
+
...this.getProductStoreSnapshot()
|
|
1037
|
+
});
|
|
863
1038
|
this.logInfo("removeProductsByIds", { ids, count: ids.length });
|
|
864
1039
|
this.store.list = this.store.list.filter((p) => !idSet.has(p.id));
|
|
865
1040
|
for (const id of ids) {
|
|
@@ -870,6 +1045,13 @@ var ProductsModule = class extends import_BaseModule.BaseModule {
|
|
|
870
1045
|
for (const id of ids) {
|
|
871
1046
|
await this.dbManager.delete(INDEXDB_STORE_NAME, id);
|
|
872
1047
|
}
|
|
1048
|
+
this.trackProductWrite(
|
|
1049
|
+
"pubsub.removeProductsByIds",
|
|
1050
|
+
"delete",
|
|
1051
|
+
ids.map((id) => ({ id })),
|
|
1052
|
+
Object.fromEntries(ids.map((id) => [id, "delete"])),
|
|
1053
|
+
"delete"
|
|
1054
|
+
);
|
|
873
1055
|
} catch (error) {
|
|
874
1056
|
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
875
1057
|
this.logError("removeProductsByIds: IndexDB 删除失败", { ids, error: errorMessage });
|
|
@@ -882,6 +1064,10 @@ var ProductsModule = class extends import_BaseModule.BaseModule {
|
|
|
882
1064
|
);
|
|
883
1065
|
}
|
|
884
1066
|
this.core.effects.emit(import_types.ProductsHooks.onProductsChanged, this.store.list);
|
|
1067
|
+
this.pushProductFlowLog("removeProductsByIds.done", {
|
|
1068
|
+
ids,
|
|
1069
|
+
...this.getProductStoreSnapshot()
|
|
1070
|
+
});
|
|
885
1071
|
this.logInfo("removeProductsByIds 完成", { remaining: this.store.list.length });
|
|
886
1072
|
}
|
|
887
1073
|
/**
|
|
@@ -890,6 +1076,7 @@ var ProductsModule = class extends import_BaseModule.BaseModule {
|
|
|
890
1076
|
*/
|
|
891
1077
|
async refreshProducts() {
|
|
892
1078
|
const tTotal = performance.now();
|
|
1079
|
+
this.pushProductFlowLog("refreshProducts.start", this.getProductStoreSnapshot());
|
|
893
1080
|
this.logInfo("refreshProducts 开始");
|
|
894
1081
|
const products = await this.loadProductsByServer();
|
|
895
1082
|
if (products && products.length > 0) {
|
|
@@ -901,6 +1088,11 @@ var ProductsModule = class extends import_BaseModule.BaseModule {
|
|
|
901
1088
|
this.logWarning("refreshProducts: 服务器未返回数据");
|
|
902
1089
|
}
|
|
903
1090
|
(0, import_product.perfMark)("refreshProducts", performance.now() - tTotal, { count: this.store.list.length });
|
|
1091
|
+
this.pushProductFlowLog("refreshProducts.done", {
|
|
1092
|
+
count: this.store.list.length,
|
|
1093
|
+
durationMs: Math.round(performance.now() - tTotal),
|
|
1094
|
+
...this.getProductStoreSnapshot()
|
|
1095
|
+
});
|
|
904
1096
|
return this.store.list;
|
|
905
1097
|
}
|
|
906
1098
|
/**
|
|
@@ -952,31 +1144,144 @@ var ProductsModule = class extends import_BaseModule.BaseModule {
|
|
|
952
1144
|
}
|
|
953
1145
|
}
|
|
954
1146
|
/**
|
|
955
|
-
*
|
|
956
|
-
*
|
|
1147
|
+
* 串行执行商品 IndexDB 写入,避免快照替换与增量 patch 并发交错。
|
|
1148
|
+
*
|
|
1149
|
+
* @example
|
|
1150
|
+
* await this.runInProductIndexDBSaveQueue(async () => {
|
|
1151
|
+
* await this.dbManager.bulkUpdate(INDEXDB_STORE_NAME, products)
|
|
1152
|
+
* })
|
|
957
1153
|
*/
|
|
958
|
-
async
|
|
1154
|
+
async runInProductIndexDBSaveQueue(task) {
|
|
1155
|
+
const queuedTask = this.productIndexDBSaveQueue.then(task, task);
|
|
1156
|
+
this.productIndexDBSaveQueue = queuedTask.then(() => void 0, () => void 0);
|
|
1157
|
+
return queuedTask;
|
|
1158
|
+
}
|
|
1159
|
+
/**
|
|
1160
|
+
* 全量快照写入 IndexDB:clear + bulkUpdate,仅用于 SSE/HTTP 全量拉取。
|
|
1161
|
+
*
|
|
1162
|
+
* @example
|
|
1163
|
+
* await this.replaceProductsSnapshotInIndexDB(list, 'loadProductsByServer')
|
|
1164
|
+
*/
|
|
1165
|
+
async replaceProductsSnapshotInIndexDB(products, source) {
|
|
959
1166
|
if (!this.dbManager) {
|
|
960
|
-
this.
|
|
1167
|
+
this.pushProductFlowLog("replaceProductsSnapshotInIndexDB.skip", {
|
|
1168
|
+
source,
|
|
1169
|
+
reason: "no_dbManager",
|
|
1170
|
+
inputCount: products.length
|
|
1171
|
+
});
|
|
1172
|
+
this.logWarning("replaceProductsSnapshotInIndexDB: dbManager 不可用");
|
|
961
1173
|
return;
|
|
962
1174
|
}
|
|
963
|
-
this.logInfo("开始保存商品数据到 IndexDB", { productCount: products.length });
|
|
964
1175
|
try {
|
|
965
|
-
|
|
966
|
-
|
|
967
|
-
|
|
968
|
-
|
|
969
|
-
|
|
970
|
-
|
|
971
|
-
|
|
972
|
-
|
|
1176
|
+
await this.runInProductIndexDBSaveQueue(async () => {
|
|
1177
|
+
this.pushProductFlowLog("replaceProductsSnapshotInIndexDB.start", {
|
|
1178
|
+
source,
|
|
1179
|
+
writeStrategy: "clear+bulkUpdate",
|
|
1180
|
+
inputCount: products.length,
|
|
1181
|
+
storeListCount: this.store.list.length
|
|
1182
|
+
});
|
|
1183
|
+
this.logInfo("replaceProductsSnapshotInIndexDB 开始", { source, productCount: products.length });
|
|
1184
|
+
const t0 = performance.now();
|
|
1185
|
+
await this.dbManager.clear(INDEXDB_STORE_NAME);
|
|
1186
|
+
this.pushProductFlowLog("replaceProductsSnapshotInIndexDB.cleared", {
|
|
1187
|
+
source,
|
|
1188
|
+
inputCount: products.length
|
|
1189
|
+
});
|
|
1190
|
+
if (products.length > 0) {
|
|
1191
|
+
await this.dbManager.bulkUpdate(INDEXDB_STORE_NAME, products);
|
|
1192
|
+
}
|
|
1193
|
+
const writeSeq = this.trackProductWrite(
|
|
1194
|
+
source,
|
|
1195
|
+
"clear+bulkUpdate",
|
|
1196
|
+
products,
|
|
1197
|
+
{},
|
|
1198
|
+
"snapshot"
|
|
1199
|
+
);
|
|
1200
|
+
(0, import_product.perfMark)("replaceProductsSnapshotInIndexDB", performance.now() - t0, { count: products.length });
|
|
1201
|
+
this.pushProductFlowLog("replaceProductsSnapshotInIndexDB.done", {
|
|
1202
|
+
source,
|
|
1203
|
+
writeSeq,
|
|
1204
|
+
count: products.length
|
|
1205
|
+
});
|
|
1206
|
+
this.logInfo("replaceProductsSnapshotInIndexDB 完成", { source, productCount: products.length });
|
|
1207
|
+
});
|
|
973
1208
|
} catch (error) {
|
|
974
1209
|
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
975
|
-
|
|
976
|
-
|
|
1210
|
+
this.pushProductFlowLog("replaceProductsSnapshotInIndexDB.error", {
|
|
1211
|
+
source,
|
|
977
1212
|
productCount: products.length,
|
|
978
1213
|
error: errorMessage
|
|
979
1214
|
});
|
|
1215
|
+
this.logError("全量保存商品到 IndexDB 失败", {
|
|
1216
|
+
source,
|
|
1217
|
+
productCount: products.length,
|
|
1218
|
+
error: errorMessage
|
|
1219
|
+
});
|
|
1220
|
+
}
|
|
1221
|
+
}
|
|
1222
|
+
/**
|
|
1223
|
+
* 增量 upsert 商品到 IndexDB,不清空表;用于 pubsub 批次合并后的变更商品。
|
|
1224
|
+
*
|
|
1225
|
+
* @example
|
|
1226
|
+
* await this.patchProductsInIndexDB(changedProducts, 'pubsub.processProductSyncMessages', mergeActions)
|
|
1227
|
+
*/
|
|
1228
|
+
async patchProductsInIndexDB(products, source, mergeActions = {}) {
|
|
1229
|
+
if (!this.dbManager || products.length === 0) {
|
|
1230
|
+
this.pushProductFlowLog("patchProductsInIndexDB.skip", {
|
|
1231
|
+
source,
|
|
1232
|
+
hasDbManager: !!this.dbManager,
|
|
1233
|
+
inputCount: products.length
|
|
1234
|
+
});
|
|
1235
|
+
return;
|
|
1236
|
+
}
|
|
1237
|
+
const validProducts = products.filter((p) => (p == null ? void 0 : p.id) !== void 0 && (p == null ? void 0 : p.id) !== null);
|
|
1238
|
+
if (validProducts.length === 0) {
|
|
1239
|
+
this.pushProductFlowLog("patchProductsInIndexDB.skipNoId", { source, inputCount: products.length });
|
|
1240
|
+
return;
|
|
1241
|
+
}
|
|
1242
|
+
try {
|
|
1243
|
+
await this.runInProductIndexDBSaveQueue(async () => {
|
|
1244
|
+
let writeMethod = "none";
|
|
1245
|
+
this.pushProductFlowLog("patchProductsInIndexDB.start", {
|
|
1246
|
+
source,
|
|
1247
|
+
writeStrategy: "bulkUpdate-patch",
|
|
1248
|
+
count: validProducts.length,
|
|
1249
|
+
productIds: validProducts.map((p) => p.id),
|
|
1250
|
+
storeListCount: this.store.list.length
|
|
1251
|
+
});
|
|
1252
|
+
this.logInfo("patchProductsInIndexDB 开始", { source, count: validProducts.length });
|
|
1253
|
+
const t0 = performance.now();
|
|
1254
|
+
if (typeof this.dbManager.bulkUpdate === "function") {
|
|
1255
|
+
writeMethod = "bulkUpdate";
|
|
1256
|
+
await this.dbManager.bulkUpdate(INDEXDB_STORE_NAME, validProducts);
|
|
1257
|
+
} else if (typeof this.dbManager.update === "function") {
|
|
1258
|
+
writeMethod = "update";
|
|
1259
|
+
for (const product of validProducts) {
|
|
1260
|
+
await this.dbManager.update(INDEXDB_STORE_NAME, product);
|
|
1261
|
+
}
|
|
1262
|
+
}
|
|
1263
|
+
const writeSeq = this.trackProductWrite(source, writeMethod, validProducts, mergeActions);
|
|
1264
|
+
(0, import_product.perfMark)("patchProductsInIndexDB", performance.now() - t0, { count: validProducts.length });
|
|
1265
|
+
this.pushProductFlowLog("patchProductsInIndexDB.done", {
|
|
1266
|
+
source,
|
|
1267
|
+
writeSeq,
|
|
1268
|
+
writeMethod,
|
|
1269
|
+
count: validProducts.length
|
|
1270
|
+
});
|
|
1271
|
+
this.logInfo("patchProductsInIndexDB 完成", { source, count: validProducts.length });
|
|
1272
|
+
});
|
|
1273
|
+
} catch (error) {
|
|
1274
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
1275
|
+
this.pushProductFlowLog("patchProductsInIndexDB.error", {
|
|
1276
|
+
source,
|
|
1277
|
+
error: errorMessage,
|
|
1278
|
+
productCount: validProducts.length
|
|
1279
|
+
});
|
|
1280
|
+
this.logError("增量保存商品到 IndexDB 失败", {
|
|
1281
|
+
source,
|
|
1282
|
+
error: errorMessage,
|
|
1283
|
+
productCount: validProducts.length
|
|
1284
|
+
});
|
|
980
1285
|
}
|
|
981
1286
|
}
|
|
982
1287
|
/**
|
|
@@ -999,6 +1304,7 @@ var ProductsModule = class extends import_BaseModule.BaseModule {
|
|
|
999
1304
|
async preload() {
|
|
1000
1305
|
console.log("[Products] 开始预加载数据...");
|
|
1001
1306
|
const tTotal = performance.now();
|
|
1307
|
+
this.pushProductFlowLog("preload.start", this.getProductStoreSnapshot());
|
|
1002
1308
|
this.logInfo("开始预加载数据");
|
|
1003
1309
|
try {
|
|
1004
1310
|
const tIndexDB = performance.now();
|
|
@@ -1023,6 +1329,12 @@ var ProductsModule = class extends import_BaseModule.BaseModule {
|
|
|
1023
1329
|
duration: `${Math.round(performance.now() - tTotal)}ms`,
|
|
1024
1330
|
source: "IndexDB"
|
|
1025
1331
|
});
|
|
1332
|
+
this.pushProductFlowLog("preload.done", {
|
|
1333
|
+
source: "IndexDB",
|
|
1334
|
+
loadedCount: cachedData.length,
|
|
1335
|
+
...this.getProductStoreSnapshot()
|
|
1336
|
+
});
|
|
1337
|
+
this.lastPreloadCompletedAtMs = Date.now();
|
|
1026
1338
|
return;
|
|
1027
1339
|
}
|
|
1028
1340
|
console.log("[Products] IndexDB 中没有缓存数据,从服务器加载...");
|
|
@@ -1050,7 +1362,14 @@ var ProductsModule = class extends import_BaseModule.BaseModule {
|
|
|
1050
1362
|
duration: `${Math.round(performance.now() - tTotal)}ms`,
|
|
1051
1363
|
source: "Server"
|
|
1052
1364
|
});
|
|
1365
|
+
this.pushProductFlowLog("preload.done", {
|
|
1366
|
+
source: "Server",
|
|
1367
|
+
loadedCount: products.length,
|
|
1368
|
+
...this.getProductStoreSnapshot()
|
|
1369
|
+
});
|
|
1370
|
+
this.lastPreloadCompletedAtMs = Date.now();
|
|
1053
1371
|
} else {
|
|
1372
|
+
this.pushProductFlowLog("preload.empty", this.getProductStoreSnapshot());
|
|
1054
1373
|
(0, import_product.perfMark)("preload(empty)", performance.now() - tTotal, { source: "empty" });
|
|
1055
1374
|
this.logWarning("预加载完成但未获取到数据", {
|
|
1056
1375
|
duration: `${Math.round(performance.now() - tTotal)}ms`
|
|
@@ -1096,6 +1415,17 @@ var ProductsModule = class extends import_BaseModule.BaseModule {
|
|
|
1096
1415
|
action_filed: data.action_filed
|
|
1097
1416
|
});
|
|
1098
1417
|
this.pendingSyncMessages.push({ ...data, _channelKey: channelKey });
|
|
1418
|
+
this.pushProductFlowLog("pubsub.messageEnqueued", {
|
|
1419
|
+
channelKey,
|
|
1420
|
+
action: data.action,
|
|
1421
|
+
operation: data.operation,
|
|
1422
|
+
id: data.id,
|
|
1423
|
+
ids: data.ids,
|
|
1424
|
+
hasBody: !!data.body,
|
|
1425
|
+
changeTypes: data.change_types,
|
|
1426
|
+
pendingSyncCount: this.pendingSyncMessages.length,
|
|
1427
|
+
debounceMs: PRODUCT_SYNC_DEBOUNCE_MS
|
|
1428
|
+
});
|
|
1099
1429
|
if (this.syncTimer) {
|
|
1100
1430
|
clearTimeout(this.syncTimer);
|
|
1101
1431
|
}
|
|
@@ -1148,6 +1478,10 @@ var ProductsModule = class extends import_BaseModule.BaseModule {
|
|
|
1148
1478
|
this.pendingSyncMessages = [];
|
|
1149
1479
|
if (messages.length === 0)
|
|
1150
1480
|
return;
|
|
1481
|
+
this.pushProductFlowLog("processProductSyncMessages.start", {
|
|
1482
|
+
messageCount: messages.length,
|
|
1483
|
+
...this.getProductStoreSnapshot()
|
|
1484
|
+
});
|
|
1151
1485
|
this.logInfo("processProductSyncMessages: 开始处理", { count: messages.length });
|
|
1152
1486
|
const deleteIds = [];
|
|
1153
1487
|
const bodyUpdates = /* @__PURE__ */ new Map();
|
|
@@ -1202,22 +1536,64 @@ var ProductsModule = class extends import_BaseModule.BaseModule {
|
|
|
1202
1536
|
const uniqueDeleteIds = [...new Set(deleteIds)];
|
|
1203
1537
|
const uniqueSSEIds = [...new Set(sseRefreshIds)];
|
|
1204
1538
|
const uniquePriceIds = [...new Set(priceRefreshIds)];
|
|
1539
|
+
this.pushProductFlowLog("processProductSyncMessages.parsed", {
|
|
1540
|
+
deleteIds: uniqueDeleteIds,
|
|
1541
|
+
bodyUpdateIds: [...bodyUpdates.keys()],
|
|
1542
|
+
sseRefreshIds: uniqueSSEIds,
|
|
1543
|
+
priceRefreshIds: uniquePriceIds,
|
|
1544
|
+
shouldClearPriceCache
|
|
1545
|
+
});
|
|
1205
1546
|
if (uniqueDeleteIds.length > 0) {
|
|
1206
1547
|
await this.removeProductsByIds(uniqueDeleteIds);
|
|
1207
1548
|
}
|
|
1549
|
+
const patchProductMap = /* @__PURE__ */ new Map();
|
|
1550
|
+
const patchMergeActions = {};
|
|
1551
|
+
let storeMutated = uniqueDeleteIds.length > 0;
|
|
1208
1552
|
if (bodyUpdates.size > 0) {
|
|
1209
|
-
await this.applyBodyUpdatesToStore(bodyUpdates
|
|
1553
|
+
const bodyResult = await this.applyBodyUpdatesToStore(bodyUpdates, {
|
|
1554
|
+
skipIndexDB: true,
|
|
1555
|
+
skipEmit: true
|
|
1556
|
+
});
|
|
1557
|
+
for (const product of bodyResult.changedProducts) {
|
|
1558
|
+
patchProductMap.set(product.id, product);
|
|
1559
|
+
patchMergeActions[product.id] = bodyResult.mergeActions[product.id];
|
|
1560
|
+
}
|
|
1561
|
+
storeMutated = true;
|
|
1210
1562
|
}
|
|
1563
|
+
let freshProducts = [];
|
|
1211
1564
|
if (uniqueSSEIds.length > 0) {
|
|
1212
|
-
|
|
1565
|
+
freshProducts = await this.fetchProductsBySSE(uniqueSSEIds);
|
|
1213
1566
|
if (freshProducts.length > 0) {
|
|
1214
|
-
await this.mergeProductsToStore(freshProducts
|
|
1567
|
+
const mergeResult = await this.mergeProductsToStore(freshProducts, {
|
|
1568
|
+
skipIndexDB: true,
|
|
1569
|
+
skipEmit: true
|
|
1570
|
+
});
|
|
1571
|
+
for (const product of mergeResult.changedProducts) {
|
|
1572
|
+
patchProductMap.set(product.id, product);
|
|
1573
|
+
patchMergeActions[product.id] = mergeResult.mergeActions[product.id];
|
|
1574
|
+
}
|
|
1575
|
+
storeMutated = true;
|
|
1215
1576
|
}
|
|
1216
1577
|
this.logInfo("processProductSyncMessages: SSE 增量更新完成123", {
|
|
1217
1578
|
requestedCount: uniqueSSEIds.length,
|
|
1218
1579
|
receivedCount: freshProducts.length
|
|
1219
1580
|
});
|
|
1220
1581
|
}
|
|
1582
|
+
const patchProducts = [...patchProductMap.values()];
|
|
1583
|
+
if (patchProducts.length > 0) {
|
|
1584
|
+
await this.patchProductsInIndexDB(
|
|
1585
|
+
patchProducts,
|
|
1586
|
+
"pubsub.processProductSyncMessages",
|
|
1587
|
+
patchMergeActions
|
|
1588
|
+
);
|
|
1589
|
+
this.pushProductFlowLog("processProductSyncMessages.patchDone", {
|
|
1590
|
+
patchCount: patchProducts.length,
|
|
1591
|
+
patchProductIds: patchProducts.map((p) => p.id)
|
|
1592
|
+
});
|
|
1593
|
+
}
|
|
1594
|
+
if (storeMutated) {
|
|
1595
|
+
this.core.effects.emit(import_types.ProductsHooks.onProductsChanged, this.store.list);
|
|
1596
|
+
}
|
|
1221
1597
|
const allChangedIds = [.../* @__PURE__ */ new Set([
|
|
1222
1598
|
...Array.from(bodyUpdates.keys()),
|
|
1223
1599
|
...uniqueSSEIds,
|
|
@@ -1233,6 +1609,7 @@ var ProductsModule = class extends import_BaseModule.BaseModule {
|
|
|
1233
1609
|
});
|
|
1234
1610
|
const hasChanges = uniqueDeleteIds.length > 0 || allChangedIds.length > 0 || shouldClearPriceCache;
|
|
1235
1611
|
if (!hasChanges) {
|
|
1612
|
+
this.pushProductFlowLog("processProductSyncMessages.noChanges", {});
|
|
1236
1613
|
this.logInfo("processProductSyncMessages: 没有变更,不触发 onProductsSyncCompleted");
|
|
1237
1614
|
return;
|
|
1238
1615
|
}
|
|
@@ -1248,6 +1625,14 @@ var ProductsModule = class extends import_BaseModule.BaseModule {
|
|
|
1248
1625
|
await this.core.effects.emit(import_types.ProductsHooks.onProductsSyncCompleted, {
|
|
1249
1626
|
changedIds: allChangedIds
|
|
1250
1627
|
});
|
|
1628
|
+
this.pushProductFlowLog("processProductSyncMessages.done", {
|
|
1629
|
+
deleteCount: uniqueDeleteIds.length,
|
|
1630
|
+
bodyUpdateCount: bodyUpdates.size,
|
|
1631
|
+
sseRefreshCount: uniqueSSEIds.length,
|
|
1632
|
+
allChangedIds,
|
|
1633
|
+
shouldClearPriceCache,
|
|
1634
|
+
...this.getProductStoreSnapshot()
|
|
1635
|
+
});
|
|
1251
1636
|
}
|
|
1252
1637
|
/**
|
|
1253
1638
|
* 通过 SSE 按 ids 增量拉取商品数据
|
|
@@ -1281,19 +1666,32 @@ var ProductsModule = class extends import_BaseModule.BaseModule {
|
|
|
1281
1666
|
/**
|
|
1282
1667
|
* 将 body 完整数据直接覆盖到本地 store(不调用报价单接口)
|
|
1283
1668
|
* 已存在的 → 直接替换;不存在的 → 追加
|
|
1284
|
-
*
|
|
1285
|
-
*
|
|
1669
|
+
*
|
|
1670
|
+
* @example
|
|
1671
|
+
* await this.applyBodyUpdatesToStore(bodyUpdates, { skipIndexDB: true })
|
|
1286
1672
|
*/
|
|
1287
|
-
async applyBodyUpdatesToStore(bodyUpdates) {
|
|
1673
|
+
async applyBodyUpdatesToStore(bodyUpdates, options) {
|
|
1674
|
+
const changedProductIds = [...bodyUpdates.keys()];
|
|
1675
|
+
this.pushProductFlowLog("applyBodyUpdatesToStore.start", {
|
|
1676
|
+
changedProductIds,
|
|
1677
|
+
count: bodyUpdates.size,
|
|
1678
|
+
skipIndexDB: !!(options == null ? void 0 : options.skipIndexDB),
|
|
1679
|
+
...this.getProductStoreSnapshot()
|
|
1680
|
+
});
|
|
1288
1681
|
this.logInfo("applyBodyUpdatesToStore: 开始", { count: bodyUpdates.size });
|
|
1289
1682
|
let updatedCount = 0;
|
|
1290
1683
|
let newCount = 0;
|
|
1291
1684
|
const appliedIds = /* @__PURE__ */ new Set();
|
|
1685
|
+
const mergeActions = {};
|
|
1686
|
+
const changedProducts = [];
|
|
1292
1687
|
this.store.list = this.store.list.map((p) => {
|
|
1293
1688
|
if (bodyUpdates.has(p.id)) {
|
|
1294
1689
|
updatedCount++;
|
|
1295
1690
|
appliedIds.add(p.id);
|
|
1296
|
-
|
|
1691
|
+
mergeActions[p.id] = "update";
|
|
1692
|
+
const next = bodyUpdates.get(p.id);
|
|
1693
|
+
changedProducts.push(next);
|
|
1694
|
+
return next;
|
|
1297
1695
|
}
|
|
1298
1696
|
return p;
|
|
1299
1697
|
});
|
|
@@ -1301,31 +1699,51 @@ var ProductsModule = class extends import_BaseModule.BaseModule {
|
|
|
1301
1699
|
if (!appliedIds.has(id)) {
|
|
1302
1700
|
this.store.list.push(body);
|
|
1303
1701
|
newCount++;
|
|
1702
|
+
mergeActions[id] = "insert";
|
|
1703
|
+
changedProducts.push(body);
|
|
1304
1704
|
}
|
|
1305
1705
|
}
|
|
1306
1706
|
this.syncProductsMap();
|
|
1307
|
-
|
|
1308
|
-
|
|
1707
|
+
if (!(options == null ? void 0 : options.skipIndexDB)) {
|
|
1708
|
+
await this.patchProductsInIndexDB(changedProducts, "pubsub.bodyUpdate", mergeActions);
|
|
1709
|
+
}
|
|
1710
|
+
if (!(options == null ? void 0 : options.skipEmit)) {
|
|
1711
|
+
this.core.effects.emit(import_types.ProductsHooks.onProductsChanged, this.store.list);
|
|
1712
|
+
}
|
|
1713
|
+
this.pushProductFlowLog("applyBodyUpdatesToStore.done", {
|
|
1714
|
+
changedProductIds,
|
|
1715
|
+
updatedCount,
|
|
1716
|
+
newCount,
|
|
1717
|
+
totalCount: this.store.list.length,
|
|
1718
|
+
indexDbPatchCount: (options == null ? void 0 : options.skipIndexDB) ? 0 : changedProducts.length
|
|
1719
|
+
});
|
|
1309
1720
|
this.logInfo("applyBodyUpdatesToStore: 完成", {
|
|
1310
1721
|
updatedCount,
|
|
1311
1722
|
newCount,
|
|
1312
1723
|
totalCount: this.store.list.length
|
|
1313
1724
|
});
|
|
1725
|
+
return { changedProducts, mergeActions, updatedCount, newCount };
|
|
1314
1726
|
}
|
|
1315
1727
|
/**
|
|
1316
1728
|
* 将增量拉取的商品合并到 store
|
|
1317
1729
|
* 已存在的 → 替换;新的 → 追加
|
|
1318
|
-
*
|
|
1730
|
+
*
|
|
1731
|
+
* @example
|
|
1732
|
+
* await this.mergeProductsToStore(freshProducts, { skipIndexDB: true })
|
|
1319
1733
|
*/
|
|
1320
|
-
async mergeProductsToStore(freshProducts) {
|
|
1734
|
+
async mergeProductsToStore(freshProducts, options) {
|
|
1321
1735
|
const freshMap = /* @__PURE__ */ new Map();
|
|
1322
1736
|
for (const p of freshProducts) {
|
|
1323
1737
|
freshMap.set(p.id, p);
|
|
1324
1738
|
}
|
|
1739
|
+
const mergeActions = {};
|
|
1740
|
+
const changedProducts = [];
|
|
1325
1741
|
const updatedList = this.store.list.map((p) => {
|
|
1326
1742
|
if (freshMap.has(p.id)) {
|
|
1327
1743
|
const fresh = freshMap.get(p.id);
|
|
1328
1744
|
freshMap.delete(p.id);
|
|
1745
|
+
mergeActions[p.id] = "update";
|
|
1746
|
+
changedProducts.push(fresh);
|
|
1329
1747
|
return fresh;
|
|
1330
1748
|
}
|
|
1331
1749
|
return p;
|
|
@@ -1333,17 +1751,38 @@ var ProductsModule = class extends import_BaseModule.BaseModule {
|
|
|
1333
1751
|
const newCount = freshMap.size;
|
|
1334
1752
|
for (const p of freshMap.values()) {
|
|
1335
1753
|
updatedList.push(p);
|
|
1754
|
+
mergeActions[p.id] = "insert";
|
|
1755
|
+
changedProducts.push(p);
|
|
1336
1756
|
}
|
|
1337
1757
|
const updatedCount = freshProducts.length - newCount;
|
|
1758
|
+
const changedProductIds = freshProducts.map((p) => p.id);
|
|
1759
|
+
this.pushProductFlowLog("mergeProductsToStore.start", {
|
|
1760
|
+
changedProductIds,
|
|
1761
|
+
incomingCount: freshProducts.length,
|
|
1762
|
+
skipIndexDB: !!(options == null ? void 0 : options.skipIndexDB),
|
|
1763
|
+
...this.getProductStoreSnapshot()
|
|
1764
|
+
});
|
|
1338
1765
|
this.store.list = updatedList;
|
|
1339
1766
|
this.syncProductsMap();
|
|
1340
|
-
|
|
1767
|
+
if (!(options == null ? void 0 : options.skipIndexDB)) {
|
|
1768
|
+
await this.patchProductsInIndexDB(changedProducts, "pubsub.mergeProductsToStore", mergeActions);
|
|
1769
|
+
}
|
|
1770
|
+
if (!(options == null ? void 0 : options.skipEmit)) {
|
|
1771
|
+
this.core.effects.emit(import_types.ProductsHooks.onProductsChanged, this.store.list);
|
|
1772
|
+
}
|
|
1773
|
+
this.pushProductFlowLog("mergeProductsToStore.done", {
|
|
1774
|
+
changedProductIds,
|
|
1775
|
+
updatedCount,
|
|
1776
|
+
newCount,
|
|
1777
|
+
totalCount: this.store.list.length,
|
|
1778
|
+
indexDbPatchCount: (options == null ? void 0 : options.skipIndexDB) ? 0 : changedProducts.length
|
|
1779
|
+
});
|
|
1341
1780
|
this.logInfo("mergeProductsToStore: 合并完成", {
|
|
1342
1781
|
updatedCount,
|
|
1343
1782
|
newCount,
|
|
1344
1783
|
totalCount: this.store.list.length
|
|
1345
1784
|
});
|
|
1346
|
-
|
|
1785
|
+
return { changedProducts, mergeActions, updatedCount, newCount };
|
|
1347
1786
|
}
|
|
1348
1787
|
/**
|
|
1349
1788
|
* 静默全量刷新:后台重新拉取全量 SSE 数据并更新本地
|
|
@@ -1352,6 +1791,22 @@ var ProductsModule = class extends import_BaseModule.BaseModule {
|
|
|
1352
1791
|
*/
|
|
1353
1792
|
async silentRefresh() {
|
|
1354
1793
|
const t0 = performance.now();
|
|
1794
|
+
const now = Date.now();
|
|
1795
|
+
const elapsedSincePreload = now - this.lastPreloadCompletedAtMs;
|
|
1796
|
+
const elapsedSinceFullFetch = now - this.lastServerFullFetchAtMs;
|
|
1797
|
+
if (this.lastPreloadCompletedAtMs > 0 && elapsedSincePreload < PRODUCT_SILENT_REFRESH_MIN_INTERVAL_MS || this.lastServerFullFetchAtMs > 0 && elapsedSinceFullFetch < PRODUCT_SILENT_REFRESH_MIN_INTERVAL_MS) {
|
|
1798
|
+
this.pushProductFlowLog("silentRefresh.skipped", {
|
|
1799
|
+
elapsedSincePreloadMs: this.lastPreloadCompletedAtMs > 0 ? elapsedSincePreload : null,
|
|
1800
|
+
elapsedSinceFullFetchMs: this.lastServerFullFetchAtMs > 0 ? elapsedSinceFullFetch : null,
|
|
1801
|
+
minIntervalMs: PRODUCT_SILENT_REFRESH_MIN_INTERVAL_MS,
|
|
1802
|
+
...this.getProductStoreSnapshot()
|
|
1803
|
+
});
|
|
1804
|
+
return this.store.list;
|
|
1805
|
+
}
|
|
1806
|
+
this.pushProductFlowLog("silentRefresh.start", {
|
|
1807
|
+
note: "会调用 loadProductsByServer → replaceProductsSnapshotInIndexDB",
|
|
1808
|
+
...this.getProductStoreSnapshot()
|
|
1809
|
+
});
|
|
1355
1810
|
this.logInfo("silentRefresh 开始");
|
|
1356
1811
|
try {
|
|
1357
1812
|
const products = await this.loadProductsByServer();
|
|
@@ -1361,17 +1816,24 @@ var ProductsModule = class extends import_BaseModule.BaseModule {
|
|
|
1361
1816
|
this.clearPriceCache();
|
|
1362
1817
|
this.core.effects.emit(import_types.ProductsHooks.onProductsChanged, this.store.list);
|
|
1363
1818
|
await this.core.effects.emit(import_types.ProductsHooks.onProductsSyncCompleted, null);
|
|
1819
|
+
this.pushProductFlowLog("silentRefresh.done", {
|
|
1820
|
+
productCount: products.length,
|
|
1821
|
+
durationMs: Math.round(performance.now() - t0),
|
|
1822
|
+
...this.getProductStoreSnapshot()
|
|
1823
|
+
});
|
|
1364
1824
|
this.logInfo("silentRefresh 完成", {
|
|
1365
1825
|
productCount: products.length,
|
|
1366
1826
|
duration: `${Math.round(performance.now() - t0)}ms`
|
|
1367
1827
|
});
|
|
1368
1828
|
} else {
|
|
1829
|
+
this.pushProductFlowLog("silentRefresh.empty", {});
|
|
1369
1830
|
this.logWarning("silentRefresh: 服务器未返回数据");
|
|
1370
1831
|
}
|
|
1371
1832
|
(0, import_product.perfMark)("silentRefresh", performance.now() - t0, { count: this.store.list.length });
|
|
1372
1833
|
return this.store.list;
|
|
1373
1834
|
} catch (error) {
|
|
1374
1835
|
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
1836
|
+
this.pushProductFlowLog("silentRefresh.failed", { error: errorMessage });
|
|
1375
1837
|
this.logError("silentRefresh 失败", {
|
|
1376
1838
|
duration: `${Math.round(performance.now() - t0)}ms`,
|
|
1377
1839
|
error: errorMessage
|