@pisell/pisellos 2.2.121 → 2.2.123

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.
@@ -38,13 +38,16 @@ var import_BaseModule = require("../../../modules/BaseModule");
38
38
  var import_types = require("./types");
39
39
  var INDEXDB_STORE_NAME = "orders";
40
40
  var ORDER_LAST_FULL_FETCH_AT_STORAGE_KEY = "server_order_last_full_fetch_at";
41
+ var ORDER_SYNC_THROTTLE_MS_STORAGE_KEY = "order_sync_throttle_ms";
42
+ var DEFAULT_ORDER_SYNC_THROTTLE_MS = 2e3;
41
43
  var OrderModule = class extends import_BaseModule.BaseModule {
42
44
  constructor(name, version) {
43
45
  super(name, version);
44
46
  this.defaultName = "order";
45
47
  this.defaultVersion = "1.0.0";
46
48
  this.pendingSyncMessages = [];
47
- this.ORDER_SYNC_DEBOUNCE_MS = 5e3;
49
+ this.isProcessingSyncBatch = false;
50
+ this.isIdlePhase = true;
48
51
  // Map<resource_id, OrderId[]> 资源到订单的倒排索引
49
52
  this.resourceIdIndex = /* @__PURE__ */ new Map();
50
53
  }
@@ -149,6 +152,15 @@ var OrderModule = class extends import_BaseModule.BaseModule {
149
152
  } catch {
150
153
  }
151
154
  }
155
+ getOrderSyncThrottleMs() {
156
+ const raw = localStorage.getItem(ORDER_SYNC_THROTTLE_MS_STORAGE_KEY);
157
+ if (raw !== null) {
158
+ const parsed = Number(raw);
159
+ if (Number.isFinite(parsed) && parsed >= 0)
160
+ return parsed;
161
+ }
162
+ return DEFAULT_ORDER_SYNC_THROTTLE_MS;
163
+ }
152
164
  async preload() {
153
165
  const getData = async () => {
154
166
  const orders = await this.loadOrdersByServer();
@@ -181,18 +193,18 @@ var OrderModule = class extends import_BaseModule.BaseModule {
181
193
  if (this.orderDataSource) {
182
194
  try {
183
195
  const data = await this.orderDataSource.run({
184
- http: {
196
+ sse: {
185
197
  query: this.store.createdAtQuery
186
198
  }
187
199
  });
188
200
  orderList = data || [];
189
- this.logInfo("loadOrdersByServer-拉取成功", {
201
+ this.logInfo("loadOrdersByServer-SSE拉取成功", {
190
202
  count: orderList.length
191
203
  });
192
204
  this.markOrderPulledAtNow();
193
205
  } catch {
194
206
  orderList = [];
195
- this.logInfo("loadOrdersByServer-拉取失败,回退为空数组");
207
+ this.logInfo("loadOrdersByServer-SSE拉取失败,回退为空数组");
196
208
  }
197
209
  }
198
210
  await this.saveOrdersToSQLite(orderList);
@@ -206,6 +218,22 @@ var OrderModule = class extends import_BaseModule.BaseModule {
206
218
  const ids = this.resourceIdIndex.get(String(resourceId)) || [];
207
219
  return ids.map((id) => this.store.map.get(id)).filter((order) => !!order);
208
220
  }
221
+ /**
222
+ * 通过 SSE 按自定义 query 拉取订单(支持 select/with 精简字段)
223
+ */
224
+ async fetchOrdersBySSE(query = {}) {
225
+ if (!this.orderDataSource)
226
+ return [];
227
+ try {
228
+ const data = await this.orderDataSource.run({ sse: { query } });
229
+ return data || [];
230
+ } catch (error) {
231
+ this.logError("fetchOrdersBySSE-失败", {
232
+ error: error instanceof Error ? error.message : String(error)
233
+ });
234
+ return [];
235
+ }
236
+ }
209
237
  getRoutes() {
210
238
  return [];
211
239
  }
@@ -215,6 +243,7 @@ var OrderModule = class extends import_BaseModule.BaseModule {
215
243
  clearTimeout(this.syncTimer);
216
244
  this.syncTimer = void 0;
217
245
  }
246
+ this.isProcessingSyncBatch = false;
218
247
  this.pendingSyncMessages = [];
219
248
  if ((_a = this.orderDataSource) == null ? void 0 : _a.destroy)
220
249
  this.orderDataSource.destroy();
@@ -287,50 +316,92 @@ var OrderModule = class extends import_BaseModule.BaseModule {
287
316
  this.orderDataSource = new OrderDataSourceClass();
288
317
  }
289
318
  /**
290
- * 初始化 pubsub 订阅,监听订单变更
319
+ * 初始化 pubsub 订阅,监听订单变更(纯订阅,不拉数据;全量 SSE 由 loadOrdersByServer 负责)
291
320
  */
292
321
  setupOrderSync() {
293
322
  if (!this.orderDataSource)
294
323
  return;
295
- const shouldSkipHttpPull = this.hasPulledOrdersToday();
296
- const syncPayload = {
324
+ this.orderDataSource.run({
297
325
  pubsub: {
298
326
  callback: (res) => {
327
+ console.log("orderDataSource");
328
+ const pubsubReceivedAt = Date.now();
299
329
  const message = this.normalizeOrderSyncMessage(res);
300
330
  if (!message)
301
331
  return;
332
+ message._pubsubReceivedAt = pubsubReceivedAt;
302
333
  this.pendingSyncMessages.push(message);
334
+ const throttleMs = this.getOrderSyncThrottleMs();
303
335
  this.logInfo("orderSync-收到消息并入队", {
304
336
  id: message.id ?? null,
305
337
  order_id: message.order_id ?? null,
306
338
  type: message.type ?? null,
307
339
  pendingCount: this.pendingSyncMessages.length,
308
- debounceMs: this.ORDER_SYNC_DEBOUNCE_MS
340
+ throttleMs,
341
+ pubsubReceivedAt: new Date(pubsubReceivedAt).toISOString()
309
342
  });
310
- if (this.syncTimer)
311
- clearTimeout(this.syncTimer);
312
- this.syncTimer = setTimeout(() => {
313
- this.processOrderSyncMessages();
314
- }, this.ORDER_SYNC_DEBOUNCE_MS);
343
+ this.scheduleOrderSyncThrottle();
315
344
  }
316
345
  }
317
- };
318
- syncPayload.http = {
319
- query: this.store.createdAtQuery,
320
- skipHttp: shouldSkipHttpPull
321
- };
322
- this.orderDataSource.run(syncPayload).then(() => {
323
- if (!shouldSkipHttpPull)
324
- this.markOrderPulledAtNow();
325
346
  }).catch(() => {
326
347
  });
327
348
  }
328
349
  /**
329
- * 处理防抖后的同步消息批次
350
+ * 调度订单同步节流窗口(leading-edge throttle):
351
+ * - 空闲状态下收到的第一条消息立即触发批处理,无需等待;
352
+ * - 首条处理后进入冷却窗口,窗口期间的后续消息聚合,窗口结束后批量处理;
353
+ * - 若当前批处理执行中,则由批处理结束后决定是否开启下一轮窗口。
354
+ */
355
+ scheduleOrderSyncThrottle() {
356
+ if (this.isProcessingSyncBatch)
357
+ return;
358
+ if (this.isIdlePhase) {
359
+ this.isIdlePhase = false;
360
+ this.logInfo("orderSync-首条消息立即触发处理");
361
+ void this.flushOrderSyncMessagesByThrottle();
362
+ return;
363
+ }
364
+ if (this.syncTimer)
365
+ return;
366
+ const throttleMs = this.getOrderSyncThrottleMs();
367
+ this.syncTimer = setTimeout(() => {
368
+ this.syncTimer = void 0;
369
+ void this.flushOrderSyncMessagesByThrottle();
370
+ }, throttleMs);
371
+ }
372
+ /**
373
+ * 执行一次节流批处理并在需要时续约下一轮窗口。
374
+ * 批处理完成且无后续消息时恢复空闲状态,下一条消息将立即触发。
375
+ */
376
+ async flushOrderSyncMessagesByThrottle() {
377
+ if (this.isProcessingSyncBatch)
378
+ return;
379
+ if (this.pendingSyncMessages.length === 0) {
380
+ this.isIdlePhase = true;
381
+ return;
382
+ }
383
+ this.isProcessingSyncBatch = true;
384
+ try {
385
+ await this.processOrderSyncMessages();
386
+ } finally {
387
+ this.isProcessingSyncBatch = false;
388
+ if (this.pendingSyncMessages.length > 0) {
389
+ this.scheduleOrderSyncThrottle();
390
+ } else {
391
+ this.isIdlePhase = true;
392
+ }
393
+ }
394
+ }
395
+ /**
396
+ * 处理节流窗口内聚合后的同步消息批次
330
397
  *
331
398
  * 后端统一发送 change 消息,不再区分新增/编辑/删除。
332
399
  * - 单条(id/order_id):若携带 body/data 则直接 upsert 到本地
333
400
  * - 批量(ids):通过 HTTP 按 ids 增量拉取,再 merge 到本地
401
+ *
402
+ * @example
403
+ * // pending 队列中存在 8 条消息时,仅处理一次批次并统一 merge
404
+ * await this.processOrderSyncMessages()
334
405
  */
335
406
  async processOrderSyncMessages() {
336
407
  var _a;
@@ -338,8 +409,15 @@ var OrderModule = class extends import_BaseModule.BaseModule {
338
409
  this.pendingSyncMessages = [];
339
410
  if (messages.length === 0)
340
411
  return;
412
+ const batchProcessStartAt = Date.now();
413
+ const earliestReceivedAt = Math.min(
414
+ ...messages.map((m) => m._pubsubReceivedAt ?? batchProcessStartAt)
415
+ );
341
416
  this.logInfo("processOrderSyncMessages-开始", {
342
- messageCount: messages.length
417
+ messageCount: messages.length,
418
+ earliestReceivedAt: new Date(earliestReceivedAt).toISOString(),
419
+ batchProcessStartAt: new Date(batchProcessStartAt).toISOString(),
420
+ waitDurationMs: batchProcessStartAt - earliestReceivedAt
343
421
  });
344
422
  const upsertOrders = /* @__PURE__ */ new Map();
345
423
  const refreshIds = [];
@@ -375,9 +453,12 @@ var OrderModule = class extends import_BaseModule.BaseModule {
375
453
  }
376
454
  if (upsertList.length === 0 && uniqueRefreshIds.length === 0)
377
455
  return;
456
+ const batchProcessEndAt = Date.now();
378
457
  this.logInfo("processOrderSyncMessages-结束", {
379
458
  upsertCount: upsertList.length,
380
- refreshIdCount: uniqueRefreshIds.length
459
+ refreshIdCount: uniqueRefreshIds.length,
460
+ batchProcessDurationMs: batchProcessEndAt - batchProcessStartAt,
461
+ totalFromPubsubToProcessedMs: batchProcessEndAt - earliestReceivedAt
381
462
  });
382
463
  await this.core.effects.emit(import_types.OrderHooks.onOrdersSyncCompleted, null);
383
464
  }
@@ -88,7 +88,7 @@ var searchProduct = async (request, code) => {
88
88
  skip: 1,
89
89
  num: 20,
90
90
  status: "published",
91
- with: ["variantGroup"],
91
+ with: ["variantGroup.variantItem", "optionGroup.optionItem"],
92
92
  with_count: ["bundleGroup", "optionGroup"],
93
93
  exclude_extension_type: [
94
94
  "product_party",
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "private": false,
3
3
  "name": "@pisell/pisellos",
4
- "version": "2.2.121",
4
+ "version": "2.2.123",
5
5
  "description": "一个可扩展的前端模块化SDK框架,支持插件系统",
6
6
  "main": "dist/index.js",
7
7
  "types": "dist/index.d.ts",