@pisell/pisellos 2.3.57 → 2.3.59

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.
Files changed (34) hide show
  1. package/dist/core/index.d.ts +7 -0
  2. package/dist/core/index.js +109 -65
  3. package/dist/model/strategy/adapter/promotion/index.js +0 -9
  4. package/dist/modules/Quotation/index.d.ts +6 -0
  5. package/dist/modules/Quotation/index.js +75 -19
  6. package/dist/modules/Rules/index.js +14 -7
  7. package/dist/plugins/request.d.ts +1 -0
  8. package/dist/server/index.d.ts +15 -0
  9. package/dist/server/index.js +970 -842
  10. package/dist/server/modules/floor-plan/index.d.ts +4 -0
  11. package/dist/server/modules/floor-plan/index.js +240 -98
  12. package/dist/server/modules/order/index.d.ts +7 -5
  13. package/dist/server/modules/order/index.js +185 -80
  14. package/dist/server/modules/order/types.d.ts +1 -1
  15. package/dist/server/modules/products/index.d.ts +2 -0
  16. package/dist/server/modules/products/index.js +17 -3
  17. package/dist/server/modules/resource/index.js +21 -13
  18. package/lib/core/index.d.ts +7 -0
  19. package/lib/core/index.js +20 -2
  20. package/lib/modules/Quotation/index.d.ts +6 -0
  21. package/lib/modules/Quotation/index.js +49 -5
  22. package/lib/modules/Rules/index.js +20 -5
  23. package/lib/plugins/request.d.ts +1 -0
  24. package/lib/server/index.d.ts +15 -0
  25. package/lib/server/index.js +120 -15
  26. package/lib/server/modules/floor-plan/index.d.ts +4 -0
  27. package/lib/server/modules/floor-plan/index.js +54 -6
  28. package/lib/server/modules/order/index.d.ts +7 -5
  29. package/lib/server/modules/order/index.js +114 -26
  30. package/lib/server/modules/order/types.d.ts +1 -1
  31. package/lib/server/modules/products/index.d.ts +2 -0
  32. package/lib/server/modules/products/index.js +15 -4
  33. package/lib/server/modules/resource/index.js +7 -2
  34. package/package.json +1 -1
@@ -40,6 +40,7 @@ var import_types = require("./types");
40
40
  var import_payment_utils = require("../../../modules/Order/payment-utils");
41
41
  var INDEXDB_STORE_NAME = "orders";
42
42
  var ORDER_LAST_FULL_FETCH_AT_STORAGE_KEY = "server_order_last_full_fetch_at";
43
+ var ORDER_LAST_FULL_FETCH_SHOP_ID_STORAGE_KEY = "server_order_last_full_fetch_shop_id";
43
44
  var ORDER_SYNC_THROTTLE_MS_STORAGE_KEY = "order_sync_throttle_ms";
44
45
  var DEFAULT_ORDER_SYNC_THROTTLE_MS = 2e3;
45
46
  var ORDER_SQLITE_DEDUPE_MS = 15e3;
@@ -524,6 +525,27 @@ var OrderModule = class extends import_BaseModule.BaseModule {
524
525
  return DEFAULT_ORDER_SYNC_THROTTLE_MS;
525
526
  }
526
527
  async preload() {
528
+ let cachedOrders = null;
529
+ if (this.dbManager) {
530
+ try {
531
+ cachedOrders = await this.loadOrdersFromSQLite({ throwOnError: true });
532
+ } catch (error) {
533
+ this.logWarning("preload-SQLite读取失败,回退远端全量拉取", {
534
+ error: error instanceof Error ? error.message : String(error)
535
+ });
536
+ }
537
+ }
538
+ if (cachedOrders !== null && this.hasPulledOrdersToday()) {
539
+ const memoryOrders = this.normalizeOrdersForMemoryList(
540
+ cachedOrders,
541
+ "preload.sqliteMemory"
542
+ );
543
+ this.store.list = (0, import_lodash_es.cloneDeep)(memoryOrders);
544
+ this.syncOrdersMap();
545
+ await this.core.effects.emit(import_types.OrderHooks.onOrdersLoaded, this.store.list);
546
+ await this.emitOrdersChanged([], "preload.sqlite");
547
+ return;
548
+ }
527
549
  const orders = await this.loadOrdersByServer();
528
550
  this.store.list = (0, import_lodash_es.cloneDeep)(orders);
529
551
  this.syncOrdersMap();
@@ -752,6 +774,7 @@ var OrderModule = class extends import_BaseModule.BaseModule {
752
774
  async loadOrdersByServer() {
753
775
  var _a;
754
776
  let orderList = [];
777
+ let hasFetchedRemoteSnapshot = false;
755
778
  const protectedOrdersBeforeRemoteFetch = (await this.loadOrdersFromSQLite()).filter(
756
779
  (order) => this.shouldProtectLocalOrderFromRemoteSnapshot(order)
757
780
  );
@@ -775,18 +798,27 @@ var OrderModule = class extends import_BaseModule.BaseModule {
775
798
  this.logInfo("loadOrdersByServer-SSE拉取成功", {
776
799
  count: orderList.length
777
800
  });
778
- this.markOrderPulledAtNow();
801
+ hasFetchedRemoteSnapshot = true;
779
802
  this.lastServerFullFetchAtMs = Date.now();
780
803
  } catch {
781
804
  this.logInfo("loadOrdersByServer-SSE拉取失败,保持当前内存订单");
782
805
  return this.store.list;
783
806
  }
784
807
  }
785
- const mergedOrders = await this.replaceOrdersSnapshotInSQLite(
808
+ if (hasFetchedRemoteSnapshot) {
809
+ this.clearOrderSnapshotMarker();
810
+ }
811
+ const {
812
+ orders: mergedOrders,
813
+ persisted: isRemoteSnapshotPersisted
814
+ } = await this.replaceOrdersSnapshotInSQLite(
786
815
  orderList,
787
816
  "loadOrdersByServer",
788
817
  protectedOrdersBeforeRemoteFetch
789
818
  );
819
+ if (hasFetchedRemoteSnapshot && isRemoteSnapshotPersisted) {
820
+ this.markOrderPulledAtNow();
821
+ }
790
822
  const memoryOrders = this.normalizeOrdersForMemoryList(
791
823
  mergedOrders,
792
824
  "loadOrdersByServer.memory"
@@ -920,10 +952,13 @@ var OrderModule = class extends import_BaseModule.BaseModule {
920
952
  */
921
953
  async upsertOrdersFromRemote(freshOrders, source = "upsertOrdersFromRemote") {
922
954
  if (!(freshOrders == null ? void 0 : freshOrders.length)) {
923
- this.logInfo("upsertOrdersFromRemote-订单列表为空", {});
955
+ this.logInfo("upsertOrdersFromRemote-订单列表为空", { source });
924
956
  return;
925
957
  }
926
- this.logInfo("upsertOrdersFromRemote-开始", { count: freshOrders.length, source });
958
+ this.logInfo("upsertOrdersFromRemote-开始", {
959
+ count: freshOrders.length,
960
+ source
961
+ });
927
962
  await this.mergeOrdersToStore(freshOrders, source);
928
963
  }
929
964
  async markOrderSyncedByExternalSaleNumber(params) {
@@ -1685,6 +1720,9 @@ var OrderModule = class extends import_BaseModule.BaseModule {
1685
1720
  isRemoteEchoMergeSource(source) {
1686
1721
  return source === "pubsub.bodyUpsert" || source === "pubsub.httpRefresh";
1687
1722
  }
1723
+ isRemoteProductMembershipChangeSource(source) {
1724
+ return this.isRemoteEchoMergeSource(source) || source === "manual.editOpenRemoteCheck";
1725
+ }
1688
1726
  getProductMembershipKey(product) {
1689
1727
  const persistentId = (0, import_orderCollectionIdentity.getOrderCollectionPersistentId)("product", product);
1690
1728
  if (persistentId)
@@ -1717,7 +1755,7 @@ var OrderModule = class extends import_BaseModule.BaseModule {
1717
1755
  * 对比合并前后的商品成员集合。商品字段修改与数组重排不会产生 diff。
1718
1756
  */
1719
1757
  getRemoteProductMembershipChange(existingOrder, mergedOrder, source) {
1720
- if (!this.isRemoteEchoMergeSource(source))
1758
+ if (!this.isRemoteProductMembershipChangeSource(source))
1721
1759
  return null;
1722
1760
  const previousProducts = Array.isArray(existingOrder.products) ? existingOrder.products : [];
1723
1761
  const currentProducts = Array.isArray(mergedOrder.products) ? mergedOrder.products : [];
@@ -2113,20 +2151,29 @@ var OrderModule = class extends import_BaseModule.BaseModule {
2113
2151
  return { list, removedCount };
2114
2152
  }
2115
2153
  /**
2116
- * 当前订单拉取窗口:
2117
- * - 优先使用 createdAtQuery.sales_time_between(已按营业日边界初始化)
2118
- * - 无法解析时退化为自然日窗口
2154
+ * 当前订单缓存可复用窗口。
2155
+ * start_time 模式使用精确的营业日起止时间;配置缺失或异常时退化为自然日。
2119
2156
  */
2120
- getCurrentPullWindow() {
2121
- var _a, _b;
2157
+ getCurrentOrderCacheWindow() {
2158
+ var _a;
2122
2159
  const now = (0, import_dayjs.default)();
2123
- const range = (_b = (_a = this.store) == null ? void 0 : _a.createdAtQuery) == null ? void 0 : _b.sales_time_between;
2124
- if (Array.isArray(range) && range.length >= 2) {
2125
- const startAt2 = (0, import_dayjs.default)(range[0]);
2126
- const endExclusiveAt = (0, import_dayjs.default)(range[1]);
2127
- if (startAt2.isValid() && endExclusiveAt.isValid() && endExclusiveAt.isAfter(startAt2)) {
2128
- return { startAt: startAt2, endExclusiveAt };
2160
+ try {
2161
+ const appPlugin = this.core.getPlugin("app");
2162
+ const app = appPlugin == null ? void 0 : appPlugin.getApp();
2163
+ const coreData = app == null ? void 0 : app.data.store.getStore().getDataByModel("core");
2164
+ const boundary = (_a = coreData == null ? void 0 : coreData.core) == null ? void 0 : _a.operating_day_boundary;
2165
+ if ((boundary == null ? void 0 : boundary.type) === "start_time") {
2166
+ const boundaryTime = String(boundary.time || "").trim();
2167
+ const todayBoundary = (0, import_dayjs.default)(`${now.format("YYYY-MM-DD")} ${boundaryTime}`);
2168
+ if (todayBoundary.isValid()) {
2169
+ const startAt2 = now.isBefore(todayBoundary) ? todayBoundary.subtract(1, "day") : todayBoundary;
2170
+ return {
2171
+ startAt: startAt2,
2172
+ endExclusiveAt: startAt2.add(1, "day")
2173
+ };
2174
+ }
2129
2175
  }
2176
+ } catch {
2130
2177
  }
2131
2178
  const startAt = now.startOf("day");
2132
2179
  return { startAt, endExclusiveAt: startAt.add(1, "day") };
@@ -2135,17 +2182,51 @@ var OrderModule = class extends import_BaseModule.BaseModule {
2135
2182
  const lastFullFetchAt = this.getStorageItem(ORDER_LAST_FULL_FETCH_AT_STORAGE_KEY);
2136
2183
  if (!lastFullFetchAt)
2137
2184
  return false;
2185
+ const currentShopId = this.getCurrentShopId();
2186
+ const lastFullFetchShopId = this.getStorageItem(
2187
+ ORDER_LAST_FULL_FETCH_SHOP_ID_STORAGE_KEY
2188
+ );
2189
+ if (!currentShopId || !lastFullFetchShopId || currentShopId !== lastFullFetchShopId) {
2190
+ return false;
2191
+ }
2138
2192
  const parsed = (0, import_dayjs.default)(lastFullFetchAt);
2139
2193
  if (!parsed.isValid())
2140
2194
  return false;
2141
- const { startAt, endExclusiveAt } = this.getCurrentPullWindow();
2195
+ const { startAt, endExclusiveAt } = this.getCurrentOrderCacheWindow();
2142
2196
  return (parsed.isAfter(startAt) || parsed.isSame(startAt)) && parsed.isBefore(endExclusiveAt);
2143
2197
  }
2144
2198
  markOrderPulledAtNow() {
2199
+ const currentShopId = this.getCurrentShopId();
2200
+ if (!currentShopId)
2201
+ return;
2145
2202
  this.setStorageItem(
2146
2203
  ORDER_LAST_FULL_FETCH_AT_STORAGE_KEY,
2147
2204
  (0, import_dayjs.default)().format("YYYY-MM-DD HH:mm:ss")
2148
2205
  );
2206
+ this.setStorageItem(
2207
+ ORDER_LAST_FULL_FETCH_SHOP_ID_STORAGE_KEY,
2208
+ currentShopId
2209
+ );
2210
+ }
2211
+ clearOrderSnapshotMarker() {
2212
+ this.setStorageItem(ORDER_LAST_FULL_FETCH_AT_STORAGE_KEY, "");
2213
+ this.setStorageItem(ORDER_LAST_FULL_FETCH_SHOP_ID_STORAGE_KEY, "");
2214
+ }
2215
+ getCurrentShopId() {
2216
+ var _a;
2217
+ try {
2218
+ const appPlugin = this.core.getPlugin("app");
2219
+ const app = appPlugin == null ? void 0 : appPlugin.getApp();
2220
+ if (!app)
2221
+ return null;
2222
+ const coreData = app.data.store.getStore().getDataByModel("core");
2223
+ const shopId = (_a = coreData == null ? void 0 : coreData.core) == null ? void 0 : _a.id;
2224
+ if (shopId === void 0 || shopId === null || shopId === "")
2225
+ return null;
2226
+ return String(shopId);
2227
+ } catch {
2228
+ return null;
2229
+ }
2149
2230
  }
2150
2231
  getStorageItem(key) {
2151
2232
  var _a;
@@ -2172,7 +2253,7 @@ var OrderModule = class extends import_BaseModule.BaseModule {
2172
2253
  } catch {
2173
2254
  }
2174
2255
  }
2175
- async loadOrdersFromSQLite() {
2256
+ async loadOrdersFromSQLite(options = {}) {
2176
2257
  if (!this.dbManager)
2177
2258
  return [];
2178
2259
  try {
@@ -2180,7 +2261,9 @@ var OrderModule = class extends import_BaseModule.BaseModule {
2180
2261
  return (orders || []).map(
2181
2262
  (order) => this.normalizeOrderCollectionsForIngress(order, "loadOrdersFromSQLite")
2182
2263
  );
2183
- } catch {
2264
+ } catch (error) {
2265
+ if (options.throwOnError)
2266
+ throw error;
2184
2267
  return [];
2185
2268
  }
2186
2269
  }
@@ -2402,14 +2485,17 @@ var OrderModule = class extends import_BaseModule.BaseModule {
2402
2485
  * 用于 SSE 全量拉取与营业日窗口裁剪。
2403
2486
  *
2404
2487
  * @example
2405
- * await this.replaceOrdersSnapshotInSQLite(orderList)
2488
+ * const { orders, persisted } = await this.replaceOrdersSnapshotInSQLite(orderList, 'refresh')
2406
2489
  */
2407
2490
  async replaceOrdersSnapshotInSQLite(orderList, source, protectedOrdersBeforeRemoteFetch = []) {
2408
2491
  if (!this.dbManager) {
2409
- return this.compactOrderListByIdentity(
2410
- orderList.map((order) => this.normalizeRemoteSyncedOrder(order)),
2411
- `${source}.snapshotNoDb`
2412
- ).list;
2492
+ return {
2493
+ orders: this.compactOrderListByIdentity(
2494
+ orderList.map((order) => this.normalizeRemoteSyncedOrder(order)),
2495
+ `${source}.snapshotNoDb`
2496
+ ).list,
2497
+ persisted: false
2498
+ };
2413
2499
  }
2414
2500
  const remoteSnapshot = (0, import_lodash_es.cloneDeep)(orderList).map(
2415
2501
  (order) => this.normalizeOrderCollectionsForIngress(order, `${source}.remoteIngress`)
@@ -2418,6 +2504,7 @@ var OrderModule = class extends import_BaseModule.BaseModule {
2418
2504
  remoteSnapshot.map((order) => this.normalizeRemoteSyncedOrder(order)),
2419
2505
  `${source}.remoteSnapshot`
2420
2506
  ).list;
2507
+ let persisted = false;
2421
2508
  try {
2422
2509
  await this.runInOrderSQLiteSaveQueue(async () => {
2423
2510
  const existingOrders = typeof this.dbManager.getAll === "function" ? await this.dbManager.getAll(INDEXDB_STORE_NAME) : [];
@@ -2482,13 +2569,14 @@ var OrderModule = class extends import_BaseModule.BaseModule {
2482
2569
  count: mergedSnapshot.length
2483
2570
  });
2484
2571
  });
2572
+ persisted = true;
2485
2573
  } catch (error) {
2486
2574
  this.logError("全量保存订单到 SQLite 失败", {
2487
2575
  error: error instanceof Error ? error.message : String(error),
2488
2576
  orderList: remoteSnapshot.length
2489
2577
  });
2490
2578
  }
2491
- return mergedSnapshot;
2579
+ return { orders: mergedSnapshot, persisted };
2492
2580
  }
2493
2581
  /**
2494
2582
  * 清空订单模块的内存数据和本地 SQLite 订单表。
@@ -2504,7 +2592,7 @@ var OrderModule = class extends import_BaseModule.BaseModule {
2504
2592
  await this.dbManager.clear(INDEXDB_STORE_NAME);
2505
2593
  });
2506
2594
  }
2507
- this.setStorageItem(ORDER_LAST_FULL_FETCH_AT_STORAGE_KEY, "");
2595
+ this.clearOrderSnapshotMarker();
2508
2596
  this.emitOrdersChanged([], "clear");
2509
2597
  }
2510
2598
  };
@@ -568,7 +568,7 @@ export interface OrderChangedPayload {
568
568
  /** 触发来源,便于订阅者区分 pubsub / 远端覆盖 / 清空等场景 */
569
569
  source?: string;
570
570
  }
571
- export type RemoteProductMembershipChangeSource = 'pubsub.bodyUpsert' | 'pubsub.httpRefresh';
571
+ export type RemoteProductMembershipChangeSource = 'pubsub.bodyUpsert' | 'pubsub.httpRefresh' | 'manual.editOpenRemoteCheck';
572
572
  /**
573
573
  * 远端订单快照相对本地订单产生商品成员增删时的广播载荷。
574
574
  * 只表达商品行成员关系变化,不包含数量、价格、备注等字段级修改。
@@ -25,6 +25,7 @@ export declare class ProductsModule extends BaseModule implements Module {
25
25
  private quotationBridgeLoadedKey?;
26
26
  private quotationBridgeRefreshPromise?;
27
27
  private quotationBridgeRefreshKey?;
28
+ private quotationBridgeUpdatedCallback?;
28
29
  private quotationScheduleCache;
29
30
  private quotationScheduleCacheSource;
30
31
  private quotationScheduleCacheLoaded;
@@ -72,6 +73,7 @@ export declare class ProductsModule extends BaseModule implements Module {
72
73
  scheduleModule?: any;
73
74
  channel?: string;
74
75
  customer_id?: number | string;
76
+ onQuotationUpdated?: () => void;
75
77
  }): Promise<void>;
76
78
  /**
77
79
  * 刷新桥接器内的报价单列表。
@@ -192,7 +192,7 @@ var ProductsModule = class extends import_BaseModule.BaseModule {
192
192
  * Server 层负责传入完整 schedule 模块,Products 只负责把报价单计算结果转成价格响应形状。
193
193
  */
194
194
  async setupQuotationPriceBridge(params) {
195
- var _a;
195
+ var _a, _b, _c;
196
196
  if (!this.core) {
197
197
  this.logWarning("setupQuotationPriceBridge: core 尚未初始化");
198
198
  return;
@@ -211,13 +211,13 @@ var ProductsModule = class extends import_BaseModule.BaseModule {
211
211
  this.quotationScheduleCacheSource = params.scheduleModule;
212
212
  }
213
213
  this.quotationPriceBridge.setScheduleResolver((id) => {
214
- var _a2, _b, _c, _d, _e, _f;
214
+ var _a2, _b2, _c2, _d, _e, _f;
215
215
  const scheduleId = String(id);
216
216
  if (this.quotationScheduleCache.has(scheduleId)) {
217
217
  return this.quotationScheduleCache.get(scheduleId);
218
218
  }
219
219
  if (!this.quotationScheduleCacheLoaded) {
220
- const scheduleList = ((_b = (_a2 = params.scheduleModule).getScheduleList) == null ? void 0 : _b.call(_a2)) || [];
220
+ const scheduleList = ((_b2 = (_a2 = params.scheduleModule).getScheduleList) == null ? void 0 : _b2.call(_a2)) || [];
221
221
  for (const schedule of scheduleList) {
222
222
  this.quotationScheduleCache.set(String(schedule.id), schedule);
223
223
  }
@@ -226,12 +226,21 @@ var ProductsModule = class extends import_BaseModule.BaseModule {
226
226
  if (this.quotationScheduleCache.has(scheduleId)) {
227
227
  return this.quotationScheduleCache.get(scheduleId);
228
228
  }
229
- const schedules = ((_d = (_c = params.scheduleModule).getScheduleListByIds) == null ? void 0 : _d.call(_c, [id])) || ((_f = (_e = params.scheduleModule).getScheduleByIds) == null ? void 0 : _f.call(_e, [id])) || [];
229
+ const schedules = ((_d = (_c2 = params.scheduleModule).getScheduleListByIds) == null ? void 0 : _d.call(_c2, [id])) || ((_f = (_e = params.scheduleModule).getScheduleByIds) == null ? void 0 : _f.call(_e, [id])) || [];
230
230
  if (schedules[0])
231
231
  this.quotationScheduleCache.set(scheduleId, schedules[0]);
232
232
  return schedules[0];
233
233
  });
234
234
  }
235
+ if (params.onQuotationUpdated) {
236
+ this.quotationBridgeUpdatedCallback = params.onQuotationUpdated;
237
+ }
238
+ (_c = (_b = this.quotationPriceBridge).setQuotationListUpdatedListener) == null ? void 0 : _c.call(_b, () => {
239
+ var _a2;
240
+ this.quotationBridgeLoadedKey = this.getQuotationBridgeScopeKey();
241
+ this.clearPriceCache();
242
+ (_a2 = this.quotationBridgeUpdatedCallback) == null ? void 0 : _a2.call(this);
243
+ });
235
244
  await this.refreshQuotationPriceBridge({ customer_id: params.customer_id });
236
245
  }
237
246
  /**
@@ -798,6 +807,8 @@ var ProductsModule = class extends import_BaseModule.BaseModule {
798
807
  */
799
808
  clearPriceCache() {
800
809
  this.productsPriceCache.clear();
810
+ this.quotationScheduleCache.clear();
811
+ this.quotationScheduleCacheLoaded = false;
801
812
  console.log("[ProductsModule] 🗑️ 商品价格缓存已清空");
802
813
  }
803
814
  /**
@@ -88,13 +88,18 @@ var ResourceModule = class extends import_BaseModule.BaseModule {
88
88
  }
89
89
  }
90
90
  async preload() {
91
- await this.setupResourceSync();
92
91
  const cachedResources = await this.loadResourcesFromSQLite();
93
92
  if (cachedResources.length > 0) {
94
93
  this.store.list = (0, import_lodash_es.cloneDeep)(cachedResources).map((item) => this.normalizeResource(item));
95
94
  this.syncResourcesMap();
96
95
  await this.safeEmit(import_types.ResourceHooks.onResourcesChanged, this.store.list);
97
- } else {
96
+ void this.setupResourceSync().catch((error) => {
97
+ this.logError("后台初始化资源同步失败", error);
98
+ });
99
+ return;
100
+ }
101
+ await this.setupResourceSync();
102
+ if (this.store.list.length === 0) {
98
103
  const resources = await this.loadResourcesByServer();
99
104
  if (resources.length > 0) {
100
105
  this.store.list = (0, import_lodash_es.cloneDeep)(resources).map((item) => this.normalizeResource(item));
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "private": false,
3
3
  "name": "@pisell/pisellos",
4
- "version": "2.3.57",
4
+ "version": "2.3.59",
5
5
  "description": "一个可扩展的前端模块化SDK框架,支持插件系统",
6
6
  "main": "dist/index.js",
7
7
  "types": "dist/index.d.ts",