@pisell/pisellos 2.3.61 → 2.3.62

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 (32) hide show
  1. package/dist/core/index.d.ts +7 -0
  2. package/dist/core/index.js +109 -66
  3. package/dist/modules/Quotation/index.d.ts +6 -0
  4. package/dist/modules/Quotation/index.js +75 -19
  5. package/dist/plugins/request.d.ts +1 -0
  6. package/dist/server/index.d.ts +6 -2
  7. package/dist/server/index.js +130 -90
  8. package/dist/server/modules/floor-plan/index.d.ts +4 -0
  9. package/dist/server/modules/floor-plan/index.js +240 -98
  10. package/dist/server/modules/menu/index.js +48 -23
  11. package/dist/server/modules/order/index.d.ts +12 -7
  12. package/dist/server/modules/order/index.js +248 -96
  13. package/dist/server/modules/products/index.d.ts +8 -2
  14. package/dist/server/modules/products/index.js +167 -75
  15. package/dist/server/modules/resource/index.js +21 -13
  16. package/lib/core/index.d.ts +7 -0
  17. package/lib/core/index.js +20 -3
  18. package/lib/model/strategy/adapter/promotion/index.js +51 -0
  19. package/lib/modules/Quotation/index.d.ts +6 -0
  20. package/lib/modules/Quotation/index.js +49 -5
  21. package/lib/plugins/request.d.ts +1 -0
  22. package/lib/server/index.d.ts +6 -2
  23. package/lib/server/index.js +18 -8
  24. package/lib/server/modules/floor-plan/index.d.ts +4 -0
  25. package/lib/server/modules/floor-plan/index.js +54 -6
  26. package/lib/server/modules/menu/index.js +31 -5
  27. package/lib/server/modules/order/index.d.ts +12 -7
  28. package/lib/server/modules/order/index.js +143 -28
  29. package/lib/server/modules/products/index.d.ts +8 -2
  30. package/lib/server/modules/products/index.js +97 -24
  31. package/lib/server/modules/resource/index.js +7 -2
  32. package/package.json +1 -1
@@ -9,6 +9,8 @@ export declare class QuotationModule extends BaseModule implements Module {
9
9
  private request;
10
10
  private store;
11
11
  private scheduleResolver?;
12
+ private app;
13
+ private quotationListUpdatedListener?;
12
14
  constructor(name?: string, version?: string);
13
15
  initialize(core: PisellCore, options: ModuleOptions): Promise<void>;
14
16
  loadQuotations(params?: {
@@ -16,6 +18,7 @@ export declare class QuotationModule extends BaseModule implements Module {
16
18
  customer_id?: number | string;
17
19
  }): Promise<void>;
18
20
  getQuotationList(): QuotationItem[];
21
+ setQuotationListUpdatedListener(listener?: () => void): void;
19
22
  getQuotationCustomerScopeInfo(): QuotationCustomerScopeInfo;
20
23
  /**
21
24
  * Look up the quotation price for a specific product (+ optional variant) at a given datetime.
@@ -60,6 +63,9 @@ export declare class QuotationModule extends BaseModule implements Module {
60
63
  customer_id?: number | string;
61
64
  }): Map<string, string | null>;
62
65
  setScheduleResolver(resolver: (id: number) => ScheduleItem | undefined): void;
66
+ private applyQuotationResponse;
67
+ private notifyQuotationListUpdated;
68
+ private getQuotationCacheKeyData;
63
69
  private isQuotationVisibleForCustomer;
64
70
  private hasValidCustomerId;
65
71
  private normalizeCustomerId;
@@ -32,32 +32,49 @@ var QuotationModule = class extends import_BaseModule.BaseModule {
32
32
  this.store = { list: [] };
33
33
  }
34
34
  async initialize(core, options) {
35
+ var _a, _b;
35
36
  this.core = core;
36
37
  this.request = core.getPlugin("request");
37
38
  if (!this.request)
38
39
  throw new Error("QuotationModule 需要 request 插件支持");
40
+ this.app = (_b = (_a = core.getPlugin("app")) == null ? void 0 : _a.getApp) == null ? void 0 : _b.call(_a);
39
41
  this.store = { list: [] };
40
42
  }
41
43
  async loadQuotations(params) {
42
- var _a;
43
44
  const query = {};
44
45
  if (params == null ? void 0 : params.channel)
45
46
  query.channel = params.channel;
46
47
  if ((params == null ? void 0 : params.channel) === "online_store") {
47
48
  query.channel = "online-store";
48
49
  }
50
+ let initialResultApplied = false;
51
+ const applyRemoteUpdate = (response) => {
52
+ this.applyQuotationResponse(response);
53
+ if (initialResultApplied) {
54
+ this.notifyQuotationListUpdated();
55
+ }
56
+ };
49
57
  const res = await this.request.get(
50
58
  "/quotation/available",
51
59
  query,
52
- { useCache: false, osServer: true }
60
+ {
61
+ cache: {
62
+ type: "indexDB",
63
+ updateCache: true,
64
+ cacheKeyData: this.getQuotationCacheKeyData(query)
65
+ },
66
+ cacheUpdateChange: applyRemoteUpdate
67
+ }
53
68
  );
54
- const list = ((_a = res == null ? void 0 : res.data) == null ? void 0 : _a.list) || (res == null ? void 0 : res.list) || [];
55
- list.sort((a, b) => b.sort - a.sort);
56
- this.store.list = list;
69
+ this.applyQuotationResponse(res);
70
+ initialResultApplied = true;
57
71
  }
58
72
  getQuotationList() {
59
73
  return this.store.list;
60
74
  }
75
+ setQuotationListUpdatedListener(listener) {
76
+ this.quotationListUpdatedListener = listener;
77
+ }
61
78
  getQuotationCustomerScopeInfo() {
62
79
  const customerById = /* @__PURE__ */ new Map();
63
80
  const quotationScopes = this.store.list.filter((quotation) => Array.isArray(quotation.customer) && quotation.customer.length > 0).map((quotation) => {
@@ -156,6 +173,33 @@ var QuotationModule = class extends import_BaseModule.BaseModule {
156
173
  setScheduleResolver(resolver) {
157
174
  this.scheduleResolver = resolver;
158
175
  }
176
+ applyQuotationResponse(response) {
177
+ var _a;
178
+ const source = ((_a = response == null ? void 0 : response.data) == null ? void 0 : _a.list) || (response == null ? void 0 : response.list) || [];
179
+ this.store.list = [...source].sort((a, b) => Number(b.sort || 0) - Number(a.sort || 0));
180
+ }
181
+ notifyQuotationListUpdated() {
182
+ var _a;
183
+ try {
184
+ (_a = this.quotationListUpdatedListener) == null ? void 0 : _a.call(this);
185
+ } catch (error) {
186
+ console.warn("[QuotationModule] 报价单后台更新回调失败", error);
187
+ }
188
+ }
189
+ getQuotationCacheKeyData(query) {
190
+ var _a, _b, _c;
191
+ let shopId = "";
192
+ try {
193
+ const storedCore = (_c = (_b = (_a = this.app) == null ? void 0 : _a.storage) == null ? void 0 : _b.getStorage) == null ? void 0 : _c.call(_b, "core");
194
+ const coreInfo = typeof storedCore === "string" ? JSON.parse(storedCore || "{}") : storedCore || {};
195
+ shopId = String((coreInfo == null ? void 0 : coreInfo.id) || "");
196
+ } catch {
197
+ }
198
+ return {
199
+ ...query,
200
+ shop_id: shopId
201
+ };
202
+ }
159
203
  isQuotationVisibleForCustomer(quotation, customerId) {
160
204
  const customers = quotation.customer || [];
161
205
  if (customers.length === 0)
@@ -68,6 +68,7 @@ export interface RequestOptions {
68
68
  useCache?: boolean;
69
69
  customToast?: () => void;
70
70
  cache?: CacheProps;
71
+ cacheUpdateChange?: (data: any) => void;
71
72
  osServer?: boolean;
72
73
  prefix?: boolean;
73
74
  callback?: (res: any) => void;
@@ -157,13 +157,17 @@ declare class Server {
157
157
  * 重新拉取全量 SSE 接口,更新本地数据后触发 onProductsSyncCompleted
158
158
  * 不影响当前界面展示,适用于切回前台、定时刷新等场景
159
159
  */
160
- refreshProductsInBackground(): Promise<void>;
160
+ refreshProductsInBackground(options?: {
161
+ trigger?: string;
162
+ }): Promise<void>;
161
163
  /**
162
164
  * 后台静默刷新订单数据
163
165
  * 重新拉取全量 SSE 接口,更新本地数据后触发 onOrdersSyncCompleted
164
166
  * 不影响当前界面展示,适用于切回前台、定时刷新等场景
165
167
  */
166
- refreshOrdersInBackground(): Promise<void>;
168
+ refreshOrdersInBackground(options?: {
169
+ trigger?: string;
170
+ }): Promise<void>;
167
171
  /**
168
172
  * 编辑正式订单时强制查询 Server 最新快照并覆盖本地缓存。
169
173
  * 商品成员增删由 OrderModule 在 SQLite/Store 更新完成后广播,
@@ -1761,22 +1761,27 @@ var Server = class {
1761
1761
  * 重新拉取全量 SSE 接口,更新本地数据后触发 onProductsSyncCompleted
1762
1762
  * 不影响当前界面展示,适用于切回前台、定时刷新等场景
1763
1763
  */
1764
- async refreshProductsInBackground() {
1764
+ async refreshProductsInBackground(options = {}) {
1765
1765
  if (!this.products) {
1766
1766
  this.logWarning("refreshProductsInBackground: Products 模块未注册");
1767
1767
  return;
1768
1768
  }
1769
- this.logInfo("refreshProductsInBackground 开始");
1769
+ const trigger = options.trigger || "background";
1770
+ this.logInfo("refreshProductsInBackground 开始", { trigger });
1770
1771
  const startTime = Date.now();
1771
1772
  try {
1772
- await this.products.silentRefresh();
1773
+ await this.products.silentRefresh({ trigger });
1773
1774
  const duration = Date.now() - startTime;
1774
- this.logInfo("refreshProductsInBackground 完成", { duration: `${duration}ms` });
1775
+ this.logInfo("refreshProductsInBackground 完成", {
1776
+ trigger,
1777
+ duration: `${duration}ms`
1778
+ });
1775
1779
  } catch (error) {
1776
1780
  const duration = Date.now() - startTime;
1777
1781
  const errorMessage = error instanceof Error ? error.message : String(error);
1778
1782
  console.error("[Server] refreshProductsInBackground 失败:", error);
1779
1783
  this.logError("refreshProductsInBackground 失败", {
1784
+ trigger,
1780
1785
  duration: `${duration}ms`,
1781
1786
  error: errorMessage
1782
1787
  });
@@ -1787,22 +1792,27 @@ var Server = class {
1787
1792
  * 重新拉取全量 SSE 接口,更新本地数据后触发 onOrdersSyncCompleted
1788
1793
  * 不影响当前界面展示,适用于切回前台、定时刷新等场景
1789
1794
  */
1790
- async refreshOrdersInBackground() {
1795
+ async refreshOrdersInBackground(options = {}) {
1791
1796
  if (!this.order) {
1792
1797
  this.logWarning("refreshOrdersInBackground: Order 模块未注册");
1793
1798
  return;
1794
1799
  }
1795
- this.logInfo("refreshOrdersInBackground 开始");
1800
+ const trigger = options.trigger || "background";
1801
+ this.logInfo("refreshOrdersInBackground 开始", { trigger });
1796
1802
  const startTime = Date.now();
1797
1803
  try {
1798
- await this.order.silentRefresh();
1804
+ await this.order.silentRefresh({ trigger });
1799
1805
  const duration = Date.now() - startTime;
1800
- this.logInfo("refreshOrdersInBackground 完成", { duration: `${duration}ms` });
1806
+ this.logInfo("refreshOrdersInBackground 完成", {
1807
+ trigger,
1808
+ duration: `${duration}ms`
1809
+ });
1801
1810
  } catch (error) {
1802
1811
  const duration = Date.now() - startTime;
1803
1812
  const errorMessage = error instanceof Error ? error.message : String(error);
1804
1813
  console.error("[Server] refreshOrdersInBackground 失败:", error);
1805
1814
  this.logError("refreshOrdersInBackground 失败", {
1815
+ trigger,
1806
1816
  duration: `${duration}ms`,
1807
1817
  error: errorMessage
1808
1818
  });
@@ -10,6 +10,7 @@ export declare class FloorPlanModule extends BaseModule implements Module {
10
10
  private store;
11
11
  private request;
12
12
  private logger;
13
+ private dbManager;
13
14
  private floorPlanDataSource;
14
15
  private pendingSyncMessages;
15
16
  private syncTimer?;
@@ -28,6 +29,9 @@ export declare class FloorPlanModule extends BaseModule implements Module {
28
29
  private mergeItems;
29
30
  private removeByIds;
30
31
  private loadListFromServer;
32
+ private loadListFromSQLite;
33
+ private saveListToSQLite;
34
+ private refreshListFromServer;
31
35
  fetchByIdFromServer(id: number | string): Promise<FloorPlanItem | null>;
32
36
  fetchByCodeFromServer(code: string): Promise<FloorPlanItem | null>;
33
37
  preload(): Promise<void>;
@@ -25,6 +25,7 @@ module.exports = __toCommonJS(floor_plan_exports);
25
25
  var import_BaseModule = require("../../../modules/BaseModule");
26
26
  var import_types = require("./types");
27
27
  var FLOOR_PLAN_SYNC_DEBOUNCE_MS = 3e3;
28
+ var FLOOR_PLAN_STORE_NAME = "floor_plans";
28
29
  var FloorPlanModule = class extends import_BaseModule.BaseModule {
29
30
  constructor(name, version) {
30
31
  super(name, version);
@@ -50,6 +51,7 @@ var FloorPlanModule = class extends import_BaseModule.BaseModule {
50
51
  if (appPlugin) {
51
52
  const app = appPlugin.getApp();
52
53
  this.logger = app.logger;
54
+ this.dbManager = app.sqlite || app.dbManager;
53
55
  }
54
56
  this.initFloorPlanDataSource();
55
57
  this.setupFloorPlanSync();
@@ -159,6 +161,45 @@ var FloorPlanModule = class extends import_BaseModule.BaseModule {
159
161
  return [];
160
162
  }
161
163
  }
164
+ async loadListFromSQLite() {
165
+ if (!this.dbManager)
166
+ return [];
167
+ try {
168
+ const list = await this.dbManager.getAll(FLOOR_PLAN_STORE_NAME);
169
+ return Array.isArray(list) ? list : [];
170
+ } catch (error) {
171
+ this.logWarning("从 SQLite 读取平面图失败", {
172
+ error: error instanceof Error ? error.message : String(error)
173
+ });
174
+ return [];
175
+ }
176
+ }
177
+ async saveListToSQLite(list) {
178
+ if (!this.dbManager)
179
+ return;
180
+ try {
181
+ await this.dbManager.clear(FLOOR_PLAN_STORE_NAME);
182
+ if (list.length > 0) {
183
+ await this.dbManager.bulkAdd(FLOOR_PLAN_STORE_NAME, list);
184
+ }
185
+ } catch (error) {
186
+ this.logWarning("保存平面图到 SQLite 失败", {
187
+ count: list.length,
188
+ error: error instanceof Error ? error.message : String(error)
189
+ });
190
+ }
191
+ }
192
+ async refreshListFromServer() {
193
+ const list = await this.loadListFromServer();
194
+ if (list.length === 0) {
195
+ this.logWarning("远端平面图刷新完成但列表为空");
196
+ return;
197
+ }
198
+ this.replaceStore(list);
199
+ await this.saveListToSQLite(list);
200
+ this.core.effects.emit(import_types.FloorPlanHooks.onFloorPlansChanged, this.getSortedList());
201
+ this.logInfo("远端平面图刷新完成", { count: list.length });
202
+ }
162
203
  async fetchByIdFromServer(id) {
163
204
  try {
164
205
  const res = await this.request.get(
@@ -190,14 +231,19 @@ var FloorPlanModule = class extends import_BaseModule.BaseModule {
190
231
  }
191
232
  async preload() {
192
233
  this.logInfo("开始预加载平面图列表");
193
- const list = await this.loadListFromServer();
194
- if (list.length > 0) {
195
- this.replaceStore(list);
234
+ const cachedList = await this.loadListFromSQLite();
235
+ if (cachedList.length > 0) {
236
+ this.replaceStore(cachedList);
196
237
  this.core.effects.emit(import_types.FloorPlanHooks.onFloorPlansChanged, this.getSortedList());
197
- this.logInfo("预加载完成", { count: list.length });
198
- } else {
199
- this.logWarning("预加载完成但列表为空");
238
+ this.logInfo("预加载完成(SQLite)", { count: cachedList.length });
239
+ void this.refreshListFromServer().catch((error) => {
240
+ this.logWarning("后台刷新平面图失败,继续使用本地数据", {
241
+ error: error instanceof Error ? error.message : String(error)
242
+ });
243
+ });
244
+ return;
200
245
  }
246
+ await this.refreshListFromServer();
201
247
  }
202
248
  initFloorPlanDataSource() {
203
249
  var _a, _b;
@@ -299,6 +345,7 @@ var FloorPlanModule = class extends import_BaseModule.BaseModule {
299
345
  }
300
346
  }
301
347
  if (changed) {
348
+ await this.saveListToSQLite(this.store.list);
302
349
  this.core.effects.emit(import_types.FloorPlanHooks.onFloorPlansChanged, this.getSortedList());
303
350
  this.core.effects.emit(import_types.FloorPlanHooks.onFloorPlanSyncCompleted, null);
304
351
  }
@@ -307,6 +354,7 @@ var FloorPlanModule = class extends import_BaseModule.BaseModule {
307
354
  this.store.list = [];
308
355
  this.store.map.clear();
309
356
  this.store.codeMap.clear();
357
+ await this.saveListToSQLite([]);
310
358
  }
311
359
  destroy() {
312
360
  var _a;
@@ -185,16 +185,21 @@ var MenuModule = class extends import_BaseModule.BaseModule {
185
185
  this.logWarning("loadMenuFromIndexDB: dbManager 不可用");
186
186
  return [];
187
187
  }
188
+ const startedAt = performance.now();
188
189
  try {
189
190
  const menuList = await this.dbManager.getAll(INDEXDB_STORE_NAME);
190
191
  this.logInfo("从 IndexDB 加载餐牌数据", {
191
- menuCount: (menuList == null ? void 0 : menuList.length) ?? 0
192
+ menuCount: (menuList == null ? void 0 : menuList.length) ?? 0,
193
+ durationMs: +(performance.now() - startedAt).toFixed(2)
192
194
  });
193
195
  return menuList || [];
194
196
  } catch (error) {
195
197
  const errorMessage = error instanceof Error ? error.message : String(error);
196
198
  console.error("[Menu] 从 IndexDB 读取数据失败:", error);
197
- this.logError("从 IndexDB 读取数据失败", { error: errorMessage });
199
+ this.logError("从 IndexDB 读取数据失败", {
200
+ durationMs: +(performance.now() - startedAt).toFixed(2),
201
+ error: errorMessage
202
+ });
198
203
  return [];
199
204
  }
200
205
  }
@@ -207,20 +212,41 @@ var MenuModule = class extends import_BaseModule.BaseModule {
207
212
  this.logWarning("saveMenuToIndexDB: dbManager 不可用");
208
213
  return;
209
214
  }
215
+ const startedAt = performance.now();
216
+ let clearDurationMs = 0;
217
+ let writeDurationMs = 0;
210
218
  this.logInfo("开始保存餐牌数据到 IndexDB", { menuCount: menuList.length });
211
219
  try {
212
- await this.dbManager.clear(INDEXDB_STORE_NAME);
220
+ const clearStartedAt = performance.now();
221
+ try {
222
+ await this.dbManager.clear(INDEXDB_STORE_NAME);
223
+ } finally {
224
+ clearDurationMs = +(performance.now() - clearStartedAt).toFixed(2);
225
+ }
226
+ const writeStartedAt = performance.now();
213
227
  const savePromises = menuList.map(
214
228
  (menu) => this.dbManager.add(INDEXDB_STORE_NAME, menu)
215
229
  );
216
- await Promise.all(savePromises);
230
+ try {
231
+ await Promise.all(savePromises);
232
+ } finally {
233
+ writeDurationMs = +(performance.now() - writeStartedAt).toFixed(2);
234
+ }
217
235
  console.log(`[Menu] 已将 ${menuList.length} 条餐牌数据保存到 IndexDB`);
218
- this.logInfo("餐牌数据已保存到 IndexDB", { menuCount: menuList.length });
236
+ this.logInfo("餐牌数据已保存到 IndexDB", {
237
+ menuCount: menuList.length,
238
+ clearDurationMs,
239
+ writeDurationMs,
240
+ totalDurationMs: +(performance.now() - startedAt).toFixed(2)
241
+ });
219
242
  } catch (error) {
220
243
  const errorMessage = error instanceof Error ? error.message : String(error);
221
244
  console.error("[Menu] 保存数据到 IndexDB 失败:", error);
222
245
  this.logError("保存数据到 IndexDB 失败", {
223
246
  menuCount: menuList.length,
247
+ clearDurationMs,
248
+ writeDurationMs,
249
+ totalDurationMs: +(performance.now() - startedAt).toFixed(2),
224
250
  error: errorMessage
225
251
  });
226
252
  }
@@ -119,13 +119,17 @@ export declare class OrderModule extends BaseModule implements Module {
119
119
  payment: Record<string, any>;
120
120
  previousPayment: Record<string, any>;
121
121
  } | null>;
122
- loadOrdersByServer(): Promise<OrderData[]>;
122
+ loadOrdersByServer(options?: {
123
+ trigger?: string;
124
+ }): Promise<OrderData[]>;
123
125
  /**
124
126
  * 静默全量刷新:后台重新拉取全量 SSE 订单数据并更新本地
125
127
  * 拿到完整数据后一次性替换 store,触发订单变更与同步完成事件
126
128
  * @returns 刷新后的订单列表
127
129
  */
128
- silentRefresh(): Promise<OrderData[]>;
130
+ silentRefresh(options?: {
131
+ trigger?: string;
132
+ }): Promise<OrderData[]>;
129
133
  private normalizeOrderForMemoryList;
130
134
  private normalizeOrdersForMemoryList;
131
135
  private hasRealOrderId;
@@ -290,13 +294,14 @@ export declare class OrderModule extends BaseModule implements Module {
290
294
  private logDuplicateOrders;
291
295
  private compactOrderListByIdentity;
292
296
  /**
293
- * 当前订单拉取窗口:
294
- * - 优先使用 createdAtQuery.sales_time_between(已按营业日边界初始化)
295
- * - 无法解析时退化为自然日窗口
297
+ * 当前订单缓存可复用窗口。
298
+ * start_time 模式使用精确的营业日起止时间;配置缺失或异常时退化为自然日。
296
299
  */
297
- private getCurrentPullWindow;
300
+ private getCurrentOrderCacheWindow;
298
301
  private hasPulledOrdersToday;
299
302
  private markOrderPulledAtNow;
303
+ private clearOrderSnapshotMarker;
304
+ private getCurrentShopId;
300
305
  private getStorageItem;
301
306
  private setStorageItem;
302
307
  private loadOrdersFromSQLite;
@@ -334,7 +339,7 @@ export declare class OrderModule extends BaseModule implements Module {
334
339
  * 用于 SSE 全量拉取与营业日窗口裁剪。
335
340
  *
336
341
  * @example
337
- * await this.replaceOrdersSnapshotInSQLite(orderList)
342
+ * const { orders, persisted } = await this.replaceOrdersSnapshotInSQLite(orderList, 'refresh')
338
343
  */
339
344
  private replaceOrdersSnapshotInSQLite;
340
345
  /**