@pisell/pisellos 2.2.190 → 2.2.192
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 +60 -7
- package/dist/server/modules/order/index.js +670 -178
- package/dist/server/modules/order/types.d.ts +0 -2
- package/dist/server/modules/products/index.d.ts +33 -6
- package/dist/server/modules/products/index.js +528 -231
- 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 +60 -7
- package/lib/server/modules/order/index.js +350 -40
- package/lib/server/modules/order/types.d.ts +0 -2
- package/lib/server/modules/products/index.d.ts +33 -6
- package/lib/server/modules/products/index.js +159 -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
|
@@ -40,6 +40,12 @@ var INDEXDB_STORE_NAME = "orders";
|
|
|
40
40
|
var ORDER_LAST_FULL_FETCH_AT_STORAGE_KEY = "server_order_last_full_fetch_at";
|
|
41
41
|
var ORDER_SYNC_THROTTLE_MS_STORAGE_KEY = "order_sync_throttle_ms";
|
|
42
42
|
var DEFAULT_ORDER_SYNC_THROTTLE_MS = 2e3;
|
|
43
|
+
var ORDER_SQLITE_DEDUPE_MS = 15e3;
|
|
44
|
+
var ORDER_SILENT_REFRESH_MIN_INTERVAL_MS = 3e4;
|
|
45
|
+
var ORDER_BUSINESS_WRITE_SOURCES = /* @__PURE__ */ new Set([
|
|
46
|
+
"upsertOrdersFromRemote",
|
|
47
|
+
"overwriteExistingOrder"
|
|
48
|
+
]);
|
|
43
49
|
var OrderModule = class extends import_BaseModule.BaseModule {
|
|
44
50
|
constructor(name, version) {
|
|
45
51
|
super(name, version);
|
|
@@ -51,6 +57,10 @@ var OrderModule = class extends import_BaseModule.BaseModule {
|
|
|
51
57
|
// Map<resource_id, OrderId[]> 资源到订单的倒排索引
|
|
52
58
|
this.resourceIdIndex = /* @__PURE__ */ new Map();
|
|
53
59
|
this.orderSQLiteSaveQueue = Promise.resolve();
|
|
60
|
+
/** 按 storageKey 记录最近一次 SQLite 写入来源与时间,用于去重 */
|
|
61
|
+
this.recentSqliteWriteByStorageKey = /* @__PURE__ */ new Map();
|
|
62
|
+
/** 最近一次 SSE 全量拉取完成时间 */
|
|
63
|
+
this.lastServerFullFetchAtMs = 0;
|
|
54
64
|
}
|
|
55
65
|
async initialize(core, options) {
|
|
56
66
|
var _a, _b;
|
|
@@ -155,6 +165,159 @@ var OrderModule = class extends import_BaseModule.BaseModule {
|
|
|
155
165
|
} catch {
|
|
156
166
|
}
|
|
157
167
|
}
|
|
168
|
+
/**
|
|
169
|
+
* 记录订单最近一次 SQLite 写入,供 pubsub 回声去重使用。
|
|
170
|
+
*
|
|
171
|
+
* @example
|
|
172
|
+
* this.recordRecentSqliteWrite('upsertOrdersFromRemote', orders)
|
|
173
|
+
*/
|
|
174
|
+
recordRecentSqliteWrite(source, orders) {
|
|
175
|
+
const now = Date.now();
|
|
176
|
+
for (const order of orders) {
|
|
177
|
+
const storageKey = this.getOrderStorageKey(order);
|
|
178
|
+
if (!storageKey)
|
|
179
|
+
continue;
|
|
180
|
+
this.recentSqliteWriteByStorageKey.set(storageKey, { source, ts: now });
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
/**
|
|
184
|
+
* 过滤 pubsub 触发的冗余 SQLite patch(业务 API 刚写入过的 update 回声)。
|
|
185
|
+
*
|
|
186
|
+
* @example
|
|
187
|
+
* const { toWrite, skipped } = this.filterRedundantPatchOrders('pubsub.bodyUpsert', orders, mergeActions)
|
|
188
|
+
*/
|
|
189
|
+
filterRedundantPatchOrders(source, orders, mergeActions) {
|
|
190
|
+
const eligibleSkipSources = /* @__PURE__ */ new Set(["pubsub.bodyUpsert", "pubsub.httpRefresh"]);
|
|
191
|
+
if (!eligibleSkipSources.has(source)) {
|
|
192
|
+
return { toWrite: orders, skipped: [] };
|
|
193
|
+
}
|
|
194
|
+
const now = Date.now();
|
|
195
|
+
const toWrite = [];
|
|
196
|
+
const skipped = [];
|
|
197
|
+
for (const order of orders) {
|
|
198
|
+
const storageKey = this.getOrderStorageKey(order);
|
|
199
|
+
if (!storageKey) {
|
|
200
|
+
toWrite.push(order);
|
|
201
|
+
continue;
|
|
202
|
+
}
|
|
203
|
+
const orderKey = order.order_id !== void 0 && order.order_id !== null ? this.getIdKey(order.order_id) : "";
|
|
204
|
+
const mergeAction = orderKey ? mergeActions[orderKey] : void 0;
|
|
205
|
+
if (mergeAction === "insert") {
|
|
206
|
+
toWrite.push(order);
|
|
207
|
+
continue;
|
|
208
|
+
}
|
|
209
|
+
const recent = this.recentSqliteWriteByStorageKey.get(storageKey);
|
|
210
|
+
if (recent && ORDER_BUSINESS_WRITE_SOURCES.has(recent.source) && now - recent.ts < ORDER_SQLITE_DEDUPE_MS) {
|
|
211
|
+
skipped.push({
|
|
212
|
+
orderId: order.order_id ?? null,
|
|
213
|
+
storageKey,
|
|
214
|
+
reason: "skip_pubsub_echo_after_business_write",
|
|
215
|
+
lastSource: recent.source,
|
|
216
|
+
elapsedMs: now - recent.ts
|
|
217
|
+
});
|
|
218
|
+
continue;
|
|
219
|
+
}
|
|
220
|
+
toWrite.push(order);
|
|
221
|
+
}
|
|
222
|
+
return { toWrite, skipped };
|
|
223
|
+
}
|
|
224
|
+
/**
|
|
225
|
+
* 过滤 pubsub HTTP 增量拉取 ids:本地刚由业务 API 写入的单可跳过 HTTP。
|
|
226
|
+
*
|
|
227
|
+
* @example
|
|
228
|
+
* const ids = this.filterRedundantHttpRefreshIds([99331, 99332])
|
|
229
|
+
*/
|
|
230
|
+
filterRedundantHttpRefreshIds(ids) {
|
|
231
|
+
const now = Date.now();
|
|
232
|
+
const toFetch = [];
|
|
233
|
+
const skipped = [];
|
|
234
|
+
for (const id of ids) {
|
|
235
|
+
const existing = this.getOrderByOrderId(id);
|
|
236
|
+
if (!existing) {
|
|
237
|
+
toFetch.push(id);
|
|
238
|
+
continue;
|
|
239
|
+
}
|
|
240
|
+
const storageKey = this.getOrderStorageKey(existing);
|
|
241
|
+
const recent = storageKey ? this.recentSqliteWriteByStorageKey.get(storageKey) : void 0;
|
|
242
|
+
if (recent && recent.source === "upsertOrdersFromRemote" && now - recent.ts < ORDER_SQLITE_DEDUPE_MS) {
|
|
243
|
+
skipped.push({
|
|
244
|
+
orderId: id,
|
|
245
|
+
lastSource: recent.source,
|
|
246
|
+
elapsedMs: now - recent.ts
|
|
247
|
+
});
|
|
248
|
+
continue;
|
|
249
|
+
}
|
|
250
|
+
toFetch.push(id);
|
|
251
|
+
}
|
|
252
|
+
return { toFetch, skipped };
|
|
253
|
+
}
|
|
254
|
+
/**
|
|
255
|
+
* 解析 pubsub 消息会走哪条同步分支,便于对照为何触发 HTTP 重拉或 body 直写。
|
|
256
|
+
*
|
|
257
|
+
* @example
|
|
258
|
+
* this.resolveSyncMessageRoute(msg)
|
|
259
|
+
* // => { route: 'httpRefresh.batchIds', ... }
|
|
260
|
+
*/
|
|
261
|
+
resolveSyncMessageRoute(msg) {
|
|
262
|
+
var _a;
|
|
263
|
+
const hasBatchIds = Array.isArray(msg.ids) && msg.ids.length > 0;
|
|
264
|
+
const singleIdRaw = msg.id ?? msg.order_id;
|
|
265
|
+
const hasSingleId = !hasBatchIds && singleIdRaw !== void 0 && singleIdRaw !== null;
|
|
266
|
+
const normalizedBody = this.normalizeOrderSyncPayload(msg.body || msg.data);
|
|
267
|
+
if (hasBatchIds) {
|
|
268
|
+
return {
|
|
269
|
+
route: "httpRefresh.batchIds",
|
|
270
|
+
hasBatchIds: true,
|
|
271
|
+
hasSingleId: false,
|
|
272
|
+
hasNormalizedBody: !!normalizedBody,
|
|
273
|
+
singleId: null,
|
|
274
|
+
batchIds: msg.ids || [],
|
|
275
|
+
bodyOrderId: (normalizedBody == null ? void 0 : normalizedBody.order_id) ?? null
|
|
276
|
+
};
|
|
277
|
+
}
|
|
278
|
+
if (hasSingleId && normalizedBody) {
|
|
279
|
+
return {
|
|
280
|
+
route: "bodyUpsert",
|
|
281
|
+
hasBatchIds: false,
|
|
282
|
+
hasSingleId: true,
|
|
283
|
+
hasNormalizedBody: true,
|
|
284
|
+
singleId: singleIdRaw,
|
|
285
|
+
batchIds: [],
|
|
286
|
+
bodyOrderId: normalizedBody.order_id ?? null
|
|
287
|
+
};
|
|
288
|
+
}
|
|
289
|
+
if (hasSingleId) {
|
|
290
|
+
return {
|
|
291
|
+
route: "httpRefresh.singleId",
|
|
292
|
+
hasBatchIds: false,
|
|
293
|
+
hasSingleId: true,
|
|
294
|
+
hasNormalizedBody: !!normalizedBody,
|
|
295
|
+
singleId: singleIdRaw,
|
|
296
|
+
batchIds: [],
|
|
297
|
+
bodyOrderId: (normalizedBody == null ? void 0 : normalizedBody.order_id) ?? null
|
|
298
|
+
};
|
|
299
|
+
}
|
|
300
|
+
if ((_a = msg.relation_order_ids) == null ? void 0 : _a.length) {
|
|
301
|
+
return {
|
|
302
|
+
route: "httpRefresh.relationIds",
|
|
303
|
+
hasBatchIds: false,
|
|
304
|
+
hasSingleId: false,
|
|
305
|
+
hasNormalizedBody: !!normalizedBody,
|
|
306
|
+
singleId: null,
|
|
307
|
+
batchIds: msg.relation_order_ids,
|
|
308
|
+
bodyOrderId: (normalizedBody == null ? void 0 : normalizedBody.order_id) ?? null
|
|
309
|
+
};
|
|
310
|
+
}
|
|
311
|
+
return {
|
|
312
|
+
route: "noop",
|
|
313
|
+
hasBatchIds: false,
|
|
314
|
+
hasSingleId: false,
|
|
315
|
+
hasNormalizedBody: !!normalizedBody,
|
|
316
|
+
singleId: null,
|
|
317
|
+
batchIds: [],
|
|
318
|
+
bodyOrderId: (normalizedBody == null ? void 0 : normalizedBody.order_id) ?? null
|
|
319
|
+
};
|
|
320
|
+
}
|
|
158
321
|
getOrderSyncThrottleMs() {
|
|
159
322
|
const raw = localStorage.getItem(ORDER_SYNC_THROTTLE_MS_STORAGE_KEY);
|
|
160
323
|
if (raw !== null) {
|
|
@@ -167,8 +330,9 @@ var OrderModule = class extends import_BaseModule.BaseModule {
|
|
|
167
330
|
async preload() {
|
|
168
331
|
const getData = async () => {
|
|
169
332
|
const orders = await this.loadOrdersByServer();
|
|
170
|
-
if (orders.length === 0)
|
|
333
|
+
if (orders.length === 0) {
|
|
171
334
|
return;
|
|
335
|
+
}
|
|
172
336
|
this.store.list = (0, import_lodash_es.cloneDeep)(orders);
|
|
173
337
|
this.syncOrdersMap();
|
|
174
338
|
this.core.effects.emit(import_types.OrderHooks.onOrdersChanged, this.store.list);
|
|
@@ -189,8 +353,9 @@ var OrderModule = class extends import_BaseModule.BaseModule {
|
|
|
189
353
|
async getLocalOrderByOrderId(orderId) {
|
|
190
354
|
const key = this.getIdKey(orderId);
|
|
191
355
|
const memoryOrder = this.store.map.get(key);
|
|
192
|
-
if (memoryOrder)
|
|
356
|
+
if (memoryOrder) {
|
|
193
357
|
return memoryOrder;
|
|
358
|
+
}
|
|
194
359
|
const orders = await this.loadOrdersFromSQLite();
|
|
195
360
|
const localOrder = orders.find((order) => {
|
|
196
361
|
const shopOrderNumber = order == null ? void 0 : order.shop_order_number;
|
|
@@ -226,12 +391,13 @@ var OrderModule = class extends import_BaseModule.BaseModule {
|
|
|
226
391
|
count: orderList.length
|
|
227
392
|
});
|
|
228
393
|
this.markOrderPulledAtNow();
|
|
394
|
+
this.lastServerFullFetchAtMs = Date.now();
|
|
229
395
|
} catch {
|
|
230
396
|
orderList = [];
|
|
231
397
|
this.logInfo("loadOrdersByServer-SSE拉取失败,回退为空数组");
|
|
232
398
|
}
|
|
233
399
|
}
|
|
234
|
-
await this.
|
|
400
|
+
await this.replaceOrdersSnapshotInSQLite(orderList, "loadOrdersByServer");
|
|
235
401
|
this.logInfo("loadOrdersByServer-结束", {
|
|
236
402
|
count: orderList.length
|
|
237
403
|
});
|
|
@@ -245,6 +411,10 @@ var OrderModule = class extends import_BaseModule.BaseModule {
|
|
|
245
411
|
*/
|
|
246
412
|
async silentRefresh() {
|
|
247
413
|
const startTime = Date.now();
|
|
414
|
+
const elapsedSinceFullFetch = Date.now() - this.lastServerFullFetchAtMs;
|
|
415
|
+
if (this.lastServerFullFetchAtMs > 0 && elapsedSinceFullFetch < ORDER_SILENT_REFRESH_MIN_INTERVAL_MS) {
|
|
416
|
+
return this.store.list;
|
|
417
|
+
}
|
|
248
418
|
this.logInfo("silentRefresh 开始");
|
|
249
419
|
try {
|
|
250
420
|
const orders = await this.loadOrdersByServer();
|
|
@@ -300,7 +470,7 @@ var OrderModule = class extends import_BaseModule.BaseModule {
|
|
|
300
470
|
return this.getIdKey(id) === key ? fresh : order;
|
|
301
471
|
});
|
|
302
472
|
this.syncOrdersMap();
|
|
303
|
-
await this.
|
|
473
|
+
await this.updateOrderInSQLite(fresh, "overwriteExistingOrder");
|
|
304
474
|
this.logInfo("overwriteExistingOrder-结束", {
|
|
305
475
|
orderId,
|
|
306
476
|
storeOrderCountAfter: this.store.list.length
|
|
@@ -318,7 +488,7 @@ var OrderModule = class extends import_BaseModule.BaseModule {
|
|
|
318
488
|
return;
|
|
319
489
|
}
|
|
320
490
|
this.logInfo("upsertOrdersFromRemote-开始", { count: freshOrders.length });
|
|
321
|
-
await this.mergeOrdersToStore(freshOrders);
|
|
491
|
+
await this.mergeOrdersToStore(freshOrders, "upsertOrdersFromRemote");
|
|
322
492
|
}
|
|
323
493
|
/**
|
|
324
494
|
* 通过 SSE 按自定义 query 拉取订单(支持 select/with 精简字段)
|
|
@@ -426,7 +596,7 @@ var OrderModule = class extends import_BaseModule.BaseModule {
|
|
|
426
596
|
this.orderDataSource.run({
|
|
427
597
|
pubsub: {
|
|
428
598
|
callback: (res) => {
|
|
429
|
-
console.log("orderDataSource");
|
|
599
|
+
console.log("orderDataSource", res);
|
|
430
600
|
const pubsubReceivedAt = Date.now();
|
|
431
601
|
const message = this.normalizeOrderSyncMessage(res);
|
|
432
602
|
if (!message)
|
|
@@ -434,6 +604,7 @@ var OrderModule = class extends import_BaseModule.BaseModule {
|
|
|
434
604
|
message._pubsubReceivedAt = pubsubReceivedAt;
|
|
435
605
|
this.pendingSyncMessages.push(message);
|
|
436
606
|
const throttleMs = this.getOrderSyncThrottleMs();
|
|
607
|
+
const routeInfo = this.resolveSyncMessageRoute(message);
|
|
437
608
|
this.logInfo("orderSync-收到消息并入队", {
|
|
438
609
|
id: message.id ?? null,
|
|
439
610
|
order_id: message.order_id ?? null,
|
|
@@ -506,7 +677,6 @@ var OrderModule = class extends import_BaseModule.BaseModule {
|
|
|
506
677
|
* await this.processOrderSyncMessages()
|
|
507
678
|
*/
|
|
508
679
|
async processOrderSyncMessages() {
|
|
509
|
-
var _a;
|
|
510
680
|
const messages = [...this.pendingSyncMessages];
|
|
511
681
|
this.pendingSyncMessages = [];
|
|
512
682
|
if (messages.length === 0)
|
|
@@ -523,38 +693,47 @@ var OrderModule = class extends import_BaseModule.BaseModule {
|
|
|
523
693
|
});
|
|
524
694
|
const upsertOrders = /* @__PURE__ */ new Map();
|
|
525
695
|
const refreshIds = [];
|
|
526
|
-
|
|
527
|
-
const
|
|
528
|
-
const
|
|
529
|
-
const
|
|
530
|
-
const
|
|
531
|
-
const normalizedBody = this.normalizeOrderSyncPayload(
|
|
532
|
-
if (
|
|
696
|
+
messages.forEach((msg, msgIndex) => {
|
|
697
|
+
const routeInfo = this.resolveSyncMessageRoute(msg);
|
|
698
|
+
const hasBatchIds = routeInfo.hasBatchIds;
|
|
699
|
+
const singleId = routeInfo.singleId;
|
|
700
|
+
const hasSingleId = routeInfo.hasSingleId;
|
|
701
|
+
const normalizedBody = this.normalizeOrderSyncPayload(msg.body || msg.data);
|
|
702
|
+
if (routeInfo.route === "bodyUpsert" && normalizedBody) {
|
|
533
703
|
upsertOrders.set(this.getIdKey(normalizedBody.order_id), normalizedBody);
|
|
534
|
-
|
|
704
|
+
return;
|
|
535
705
|
}
|
|
536
706
|
if (hasBatchIds)
|
|
537
|
-
refreshIds.push(...
|
|
538
|
-
else if (
|
|
707
|
+
refreshIds.push(...routeInfo.batchIds);
|
|
708
|
+
else if (routeInfo.route === "httpRefresh.singleId" && singleId !== null)
|
|
539
709
|
refreshIds.push(singleId);
|
|
540
|
-
else if (
|
|
541
|
-
refreshIds.push(...
|
|
542
|
-
}
|
|
710
|
+
else if (routeInfo.route === "httpRefresh.relationIds")
|
|
711
|
+
refreshIds.push(...routeInfo.batchIds);
|
|
712
|
+
});
|
|
543
713
|
const uniqueRefreshIds = this.uniqueOrderIds(refreshIds);
|
|
544
714
|
const upsertList = [...upsertOrders.values()];
|
|
545
715
|
this.logInfo("processOrderSyncMessages-去重完成", {
|
|
546
716
|
upsertCount: upsertList.length,
|
|
547
717
|
refreshIdCount: uniqueRefreshIds.length
|
|
548
718
|
});
|
|
549
|
-
if (upsertList.length > 0)
|
|
550
|
-
await this.mergeOrdersToStore(upsertList);
|
|
719
|
+
if (upsertList.length > 0) {
|
|
720
|
+
await this.mergeOrdersToStore(upsertList, "pubsub.bodyUpsert");
|
|
721
|
+
}
|
|
551
722
|
if (uniqueRefreshIds.length > 0) {
|
|
552
|
-
const
|
|
553
|
-
if (
|
|
554
|
-
|
|
723
|
+
const { toFetch, skipped } = this.filterRedundantHttpRefreshIds(uniqueRefreshIds);
|
|
724
|
+
if (skipped.length > 0) {
|
|
725
|
+
}
|
|
726
|
+
if (toFetch.length > 0) {
|
|
727
|
+
const freshOrders = await this.fetchOrdersByHttp(toFetch);
|
|
728
|
+
if (freshOrders.length > 0) {
|
|
729
|
+
await this.mergeOrdersToStore(freshOrders, "pubsub.httpRefresh");
|
|
730
|
+
} else {
|
|
731
|
+
}
|
|
732
|
+
}
|
|
555
733
|
}
|
|
556
|
-
if (upsertList.length === 0 && uniqueRefreshIds.length === 0)
|
|
734
|
+
if (upsertList.length === 0 && uniqueRefreshIds.length === 0) {
|
|
557
735
|
return;
|
|
736
|
+
}
|
|
558
737
|
const batchProcessEndAt = Date.now();
|
|
559
738
|
this.logInfo("processOrderSyncMessages-结束", {
|
|
560
739
|
upsertCount: upsertList.length,
|
|
@@ -595,7 +774,7 @@ var OrderModule = class extends import_BaseModule.BaseModule {
|
|
|
595
774
|
/**
|
|
596
775
|
* 将增量订单合并到 store
|
|
597
776
|
*/
|
|
598
|
-
async mergeOrdersToStore(freshOrders) {
|
|
777
|
+
async mergeOrdersToStore(freshOrders, source) {
|
|
599
778
|
this.logInfo("mergeOrdersToStore-开始", {
|
|
600
779
|
freshOrderCount: freshOrders.length,
|
|
601
780
|
storeOrderCountBefore: this.store.list.length
|
|
@@ -608,6 +787,8 @@ var OrderModule = class extends import_BaseModule.BaseModule {
|
|
|
608
787
|
freshMap.set(this.getIdKey(id), order);
|
|
609
788
|
}
|
|
610
789
|
const uniqueFreshCount = freshMap.size;
|
|
790
|
+
const patchedOrders = [...freshMap.values()];
|
|
791
|
+
const mergeActions = {};
|
|
611
792
|
const updatedList = this.store.list.map((order) => {
|
|
612
793
|
const id = order.order_id;
|
|
613
794
|
if (id === void 0 || id === null)
|
|
@@ -617,14 +798,21 @@ var OrderModule = class extends import_BaseModule.BaseModule {
|
|
|
617
798
|
return order;
|
|
618
799
|
const fresh = freshMap.get(key);
|
|
619
800
|
freshMap.delete(key);
|
|
801
|
+
mergeActions[key] = "update";
|
|
620
802
|
return fresh;
|
|
621
803
|
});
|
|
622
804
|
for (const order of freshMap.values()) {
|
|
805
|
+
const id = order.order_id;
|
|
806
|
+
if (id !== void 0 && id !== null) {
|
|
807
|
+
mergeActions[this.getIdKey(id)] = "insert";
|
|
808
|
+
}
|
|
623
809
|
updatedList.push(order);
|
|
624
810
|
}
|
|
811
|
+
const insertCount = Object.values(mergeActions).filter((v) => v === "insert").length;
|
|
812
|
+
const updateCount = Object.values(mergeActions).filter((v) => v === "update").length;
|
|
625
813
|
this.store.list = updatedList;
|
|
626
814
|
this.syncOrdersMap();
|
|
627
|
-
await this.
|
|
815
|
+
await this.patchOrdersInSQLite(patchedOrders, source, mergeActions);
|
|
628
816
|
this.logInfo("mergeOrdersToStore-结束", {
|
|
629
817
|
uniqueFreshCount,
|
|
630
818
|
storeOrderCountAfter: this.store.list.length
|
|
@@ -674,6 +862,24 @@ var OrderModule = class extends import_BaseModule.BaseModule {
|
|
|
674
862
|
getIdKey(id) {
|
|
675
863
|
return String(id);
|
|
676
864
|
}
|
|
865
|
+
/**
|
|
866
|
+
* 解析订单 SQLite 主键,规则与宿主 OrderDataSource.getOrderStorageKey 一致。
|
|
867
|
+
*
|
|
868
|
+
* @example
|
|
869
|
+
* const key = this.getOrderStorageKey({ order_id: 1001, external_sale_number: 'S-1001' })
|
|
870
|
+
* // => 'S-1001'
|
|
871
|
+
*/
|
|
872
|
+
getOrderStorageKey(order) {
|
|
873
|
+
const externalSaleNumber = order == null ? void 0 : order.external_sale_number;
|
|
874
|
+
if (externalSaleNumber !== void 0 && externalSaleNumber !== null && externalSaleNumber !== "") {
|
|
875
|
+
return String(externalSaleNumber);
|
|
876
|
+
}
|
|
877
|
+
const orderId = order == null ? void 0 : order.order_id;
|
|
878
|
+
if (orderId !== void 0 && orderId !== null && orderId !== "") {
|
|
879
|
+
return String(orderId);
|
|
880
|
+
}
|
|
881
|
+
return "";
|
|
882
|
+
}
|
|
677
883
|
/**
|
|
678
884
|
* 当前订单拉取窗口:
|
|
679
885
|
* - 优先使用 createdAtQuery.sales_time_between(已按营业日边界初始化)
|
|
@@ -745,12 +951,11 @@ var OrderModule = class extends import_BaseModule.BaseModule {
|
|
|
745
951
|
}
|
|
746
952
|
}
|
|
747
953
|
/**
|
|
748
|
-
* 串行执行订单 SQLite
|
|
954
|
+
* 串行执行订单 SQLite 写入任务,避免快照替换与增量 upsert 并发交错。
|
|
749
955
|
*
|
|
750
956
|
* @example
|
|
751
957
|
* await this.runInOrderSQLiteSaveQueue(async () => {
|
|
752
|
-
* await this.dbManager.
|
|
753
|
-
* await this.dbManager.bulkAdd(INDEXDB_STORE_NAME, orders)
|
|
958
|
+
* await this.dbManager.bulkUpdate(INDEXDB_STORE_NAME, orders)
|
|
754
959
|
* })
|
|
755
960
|
*/
|
|
756
961
|
async runInOrderSQLiteSaveQueue(task) {
|
|
@@ -759,33 +964,138 @@ var OrderModule = class extends import_BaseModule.BaseModule {
|
|
|
759
964
|
return queuedTask;
|
|
760
965
|
}
|
|
761
966
|
/**
|
|
762
|
-
*
|
|
967
|
+
* 批量增量 upsert 订单到 SQLite,不清空表;用于 pubsub / HTTP 增量合并。
|
|
763
968
|
*
|
|
764
969
|
* @example
|
|
765
|
-
* await this.
|
|
970
|
+
* await this.patchOrdersInSQLite(freshOrders)
|
|
766
971
|
*/
|
|
767
|
-
async
|
|
768
|
-
if (!this.dbManager)
|
|
972
|
+
async patchOrdersInSQLite(orders, source, mergeActions = {}) {
|
|
973
|
+
if (!this.dbManager || orders.length === 0) {
|
|
974
|
+
return;
|
|
975
|
+
}
|
|
976
|
+
const ordersSnapshot = (0, import_lodash_es.cloneDeep)(orders).filter((order) => this.getOrderStorageKey(order));
|
|
977
|
+
if (ordersSnapshot.length === 0) {
|
|
978
|
+
return;
|
|
979
|
+
}
|
|
980
|
+
const { toWrite, skipped } = this.filterRedundantPatchOrders(
|
|
981
|
+
source,
|
|
982
|
+
ordersSnapshot,
|
|
983
|
+
mergeActions
|
|
984
|
+
);
|
|
985
|
+
if (toWrite.length === 0) {
|
|
769
986
|
return;
|
|
987
|
+
}
|
|
988
|
+
try {
|
|
989
|
+
await this.runInOrderSQLiteSaveQueue(async () => {
|
|
990
|
+
let writeMethod = "none";
|
|
991
|
+
this.logInfo("patchOrdersInSQLite-开始", {
|
|
992
|
+
source,
|
|
993
|
+
count: toWrite.length,
|
|
994
|
+
dedupedCount: skipped.length
|
|
995
|
+
});
|
|
996
|
+
if (typeof this.dbManager.bulkUpdate === "function") {
|
|
997
|
+
writeMethod = "bulkUpdate";
|
|
998
|
+
await this.dbManager.bulkUpdate(INDEXDB_STORE_NAME, toWrite);
|
|
999
|
+
} else if (typeof this.dbManager.bulkAdd === "function") {
|
|
1000
|
+
writeMethod = "bulkAdd";
|
|
1001
|
+
await this.dbManager.bulkAdd(INDEXDB_STORE_NAME, toWrite);
|
|
1002
|
+
} else if (typeof this.dbManager.update === "function") {
|
|
1003
|
+
writeMethod = "update";
|
|
1004
|
+
for (const order of toWrite) {
|
|
1005
|
+
await this.dbManager.update(INDEXDB_STORE_NAME, order);
|
|
1006
|
+
}
|
|
1007
|
+
}
|
|
1008
|
+
this.recordRecentSqliteWrite(source, toWrite);
|
|
1009
|
+
this.logInfo("patchOrdersInSQLite-完成", {
|
|
1010
|
+
source,
|
|
1011
|
+
count: toWrite.length,
|
|
1012
|
+
writeMethod,
|
|
1013
|
+
dedupedCount: skipped.length
|
|
1014
|
+
});
|
|
1015
|
+
});
|
|
1016
|
+
} catch (error) {
|
|
1017
|
+
this.logError("增量保存订单到 SQLite 失败", {
|
|
1018
|
+
error: error instanceof Error ? error.message : String(error),
|
|
1019
|
+
orderCount: ordersSnapshot.length
|
|
1020
|
+
});
|
|
1021
|
+
}
|
|
1022
|
+
}
|
|
1023
|
+
/**
|
|
1024
|
+
* 单条 upsert 订单到 SQLite;用于 overwriteExistingOrder 等单条覆盖场景。
|
|
1025
|
+
*
|
|
1026
|
+
* @example
|
|
1027
|
+
* await this.updateOrderInSQLite(freshOrder)
|
|
1028
|
+
*/
|
|
1029
|
+
async updateOrderInSQLite(order, source) {
|
|
1030
|
+
if (!this.dbManager || !this.getOrderStorageKey(order)) {
|
|
1031
|
+
return;
|
|
1032
|
+
}
|
|
1033
|
+
const orderSnapshot = (0, import_lodash_es.cloneDeep)(order);
|
|
1034
|
+
try {
|
|
1035
|
+
await this.runInOrderSQLiteSaveQueue(async () => {
|
|
1036
|
+
let writeMethod = "none";
|
|
1037
|
+
this.logInfo("updateOrderInSQLite-开始", {
|
|
1038
|
+
source,
|
|
1039
|
+
orderId: orderSnapshot.order_id ?? null,
|
|
1040
|
+
storageKey: this.getOrderStorageKey(orderSnapshot)
|
|
1041
|
+
});
|
|
1042
|
+
if (typeof this.dbManager.update === "function") {
|
|
1043
|
+
writeMethod = "update";
|
|
1044
|
+
await this.dbManager.update(INDEXDB_STORE_NAME, orderSnapshot);
|
|
1045
|
+
} else if (typeof this.dbManager.bulkUpdate === "function") {
|
|
1046
|
+
writeMethod = "bulkUpdate";
|
|
1047
|
+
await this.dbManager.bulkUpdate(INDEXDB_STORE_NAME, [orderSnapshot]);
|
|
1048
|
+
} else if (typeof this.dbManager.bulkAdd === "function") {
|
|
1049
|
+
writeMethod = "bulkAdd";
|
|
1050
|
+
await this.dbManager.bulkAdd(INDEXDB_STORE_NAME, [orderSnapshot]);
|
|
1051
|
+
}
|
|
1052
|
+
this.recordRecentSqliteWrite(source, [orderSnapshot]);
|
|
1053
|
+
this.logInfo("updateOrderInSQLite-完成", {
|
|
1054
|
+
source,
|
|
1055
|
+
orderId: orderSnapshot.order_id ?? null,
|
|
1056
|
+
writeMethod
|
|
1057
|
+
});
|
|
1058
|
+
});
|
|
1059
|
+
} catch (error) {
|
|
1060
|
+
this.logError("单条保存订单到 SQLite 失败", {
|
|
1061
|
+
error: error instanceof Error ? error.message : String(error),
|
|
1062
|
+
orderId: orderSnapshot.order_id ?? null
|
|
1063
|
+
});
|
|
1064
|
+
}
|
|
1065
|
+
}
|
|
1066
|
+
/**
|
|
1067
|
+
* 全量快照替换 orders 表(clear + bulkAdd),用于 SSE 全量拉取与营业日窗口裁剪。
|
|
1068
|
+
*
|
|
1069
|
+
* @example
|
|
1070
|
+
* await this.replaceOrdersSnapshotInSQLite(orderList)
|
|
1071
|
+
*/
|
|
1072
|
+
async replaceOrdersSnapshotInSQLite(orderList, source) {
|
|
1073
|
+
if (!this.dbManager) {
|
|
1074
|
+
return;
|
|
1075
|
+
}
|
|
770
1076
|
const orderListSnapshot = (0, import_lodash_es.cloneDeep)(orderList);
|
|
771
1077
|
try {
|
|
772
1078
|
await this.runInOrderSQLiteSaveQueue(async () => {
|
|
773
|
-
this.logInfo("
|
|
1079
|
+
this.logInfo("replaceOrdersSnapshotInSQLite-开始", {
|
|
1080
|
+
source,
|
|
774
1081
|
count: orderListSnapshot.length
|
|
775
1082
|
});
|
|
776
1083
|
await this.dbManager.clear(INDEXDB_STORE_NAME);
|
|
777
|
-
this.logInfo("
|
|
1084
|
+
this.logInfo("replaceOrdersSnapshotInSQLite-clear完成", {
|
|
778
1085
|
count: orderListSnapshot.length
|
|
779
1086
|
});
|
|
780
|
-
if (orderListSnapshot.length === 0)
|
|
1087
|
+
if (orderListSnapshot.length === 0) {
|
|
781
1088
|
return;
|
|
1089
|
+
}
|
|
782
1090
|
await this.dbManager.bulkAdd(INDEXDB_STORE_NAME, orderListSnapshot);
|
|
783
|
-
this.
|
|
1091
|
+
this.recordRecentSqliteWrite(source, orderListSnapshot);
|
|
1092
|
+
this.logInfo("replaceOrdersSnapshotInSQLite-bulkAdd完成", {
|
|
1093
|
+
source,
|
|
784
1094
|
count: orderListSnapshot.length
|
|
785
1095
|
});
|
|
786
1096
|
});
|
|
787
1097
|
} catch (error) {
|
|
788
|
-
this.logError("
|
|
1098
|
+
this.logError("全量保存订单到 SQLite 失败", {
|
|
789
1099
|
error: error instanceof Error ? error.message : String(error),
|
|
790
1100
|
orderList: orderListSnapshot.length
|
|
791
1101
|
});
|
|
@@ -27,6 +27,12 @@ export declare class ProductsModule extends BaseModule implements Module {
|
|
|
27
27
|
private productDataSource;
|
|
28
28
|
private pendingSyncMessages;
|
|
29
29
|
private syncTimer?;
|
|
30
|
+
/** 最近一次 SSE 全量拉取完成时间 */
|
|
31
|
+
private lastServerFullFetchAtMs;
|
|
32
|
+
/** 最近一次 preload 完成时间(含 IndexDB 命中) */
|
|
33
|
+
private lastPreloadCompletedAtMs;
|
|
34
|
+
/** IndexDB 写入串行队列,避免快照与 patch 并发交错 */
|
|
35
|
+
private productIndexDBSaveQueue;
|
|
30
36
|
constructor(name?: string, version?: string);
|
|
31
37
|
initialize(core: PisellCore, options: any): Promise<void>;
|
|
32
38
|
/**
|
|
@@ -209,10 +215,28 @@ export declare class ProductsModule extends BaseModule implements Module {
|
|
|
209
215
|
*/
|
|
210
216
|
private loadProductsFromIndexDB;
|
|
211
217
|
/**
|
|
212
|
-
*
|
|
213
|
-
*
|
|
218
|
+
* 串行执行商品 IndexDB 写入,避免快照替换与增量 patch 并发交错。
|
|
219
|
+
*
|
|
220
|
+
* @example
|
|
221
|
+
* await this.runInProductIndexDBSaveQueue(async () => {
|
|
222
|
+
* await this.dbManager.bulkUpdate(INDEXDB_STORE_NAME, products)
|
|
223
|
+
* })
|
|
224
|
+
*/
|
|
225
|
+
private runInProductIndexDBSaveQueue;
|
|
226
|
+
/**
|
|
227
|
+
* 全量快照写入 IndexDB:clear + bulkUpdate,仅用于 SSE/HTTP 全量拉取。
|
|
228
|
+
*
|
|
229
|
+
* @example
|
|
230
|
+
* await this.replaceProductsSnapshotInIndexDB(list, 'loadProductsByServer')
|
|
214
231
|
*/
|
|
215
|
-
private
|
|
232
|
+
private replaceProductsSnapshotInIndexDB;
|
|
233
|
+
/**
|
|
234
|
+
* 增量 upsert 商品到 IndexDB,不清空表;用于 pubsub 批次合并后的变更商品。
|
|
235
|
+
*
|
|
236
|
+
* @example
|
|
237
|
+
* await this.patchProductsInIndexDB(changedProducts, 'pubsub.processProductSyncMessages', mergeActions)
|
|
238
|
+
*/
|
|
239
|
+
private patchProductsInIndexDB;
|
|
216
240
|
/**
|
|
217
241
|
* 同步更新商品 Map 缓存
|
|
218
242
|
* 将 list 中的商品同步到 map,以 id 为 key
|
|
@@ -262,14 +286,17 @@ export declare class ProductsModule extends BaseModule implements Module {
|
|
|
262
286
|
/**
|
|
263
287
|
* 将 body 完整数据直接覆盖到本地 store(不调用报价单接口)
|
|
264
288
|
* 已存在的 → 直接替换;不存在的 → 追加
|
|
265
|
-
*
|
|
266
|
-
*
|
|
289
|
+
*
|
|
290
|
+
* @example
|
|
291
|
+
* await this.applyBodyUpdatesToStore(bodyUpdates, { skipIndexDB: true })
|
|
267
292
|
*/
|
|
268
293
|
private applyBodyUpdatesToStore;
|
|
269
294
|
/**
|
|
270
295
|
* 将增量拉取的商品合并到 store
|
|
271
296
|
* 已存在的 → 替换;新的 → 追加
|
|
272
|
-
*
|
|
297
|
+
*
|
|
298
|
+
* @example
|
|
299
|
+
* await this.mergeProductsToStore(freshProducts, { skipIndexDB: true })
|
|
273
300
|
*/
|
|
274
301
|
private mergeProductsToStore;
|
|
275
302
|
/**
|