@pisell/pisellos 2.2.153 → 2.2.154

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 (80) hide show
  1. package/dist/core/index.js +1 -0
  2. package/dist/model/strategy/adapter/promotion/index.js +9 -0
  3. package/dist/modules/Cart/utils/cartProduct.js +1 -1
  4. package/dist/modules/Order/index.d.ts +2 -0
  5. package/dist/modules/Order/index.js +231 -160
  6. package/dist/modules/Order/types.d.ts +19 -0
  7. package/dist/modules/Order/utils.js +7 -6
  8. package/dist/modules/Payment/index.d.ts +4 -0
  9. package/dist/modules/Payment/index.js +567 -505
  10. package/dist/modules/Payment/types.d.ts +29 -9
  11. package/dist/modules/SalesSummary/index.d.ts +7 -5
  12. package/dist/modules/SalesSummary/index.js +35 -11
  13. package/dist/plugins/app.d.ts +2 -0
  14. package/dist/server/index.d.ts +2 -5
  15. package/dist/server/index.js +58 -66
  16. package/dist/server/modules/index.d.ts +1 -1
  17. package/dist/server/modules/products/index.d.ts +12 -1
  18. package/dist/server/modules/products/index.js +169 -78
  19. package/dist/server/modules/products/types.d.ts +11 -0
  20. package/dist/server/modules/products/types.js +4 -0
  21. package/dist/server/utils/product.js +2 -2
  22. package/dist/solution/BaseSales/index.d.ts +26 -2
  23. package/dist/solution/BaseSales/index.js +342 -112
  24. package/dist/solution/BaseSales/types.d.ts +4 -4
  25. package/dist/solution/BaseSales/types.js +4 -4
  26. package/dist/solution/BaseSales/utils/transformBaseProductToOrderProduct.d.ts +62 -0
  27. package/dist/solution/BaseSales/utils/transformBaseProductToOrderProduct.js +116 -0
  28. package/dist/solution/BaseSales/utils.js +3 -3
  29. package/dist/solution/BookingTicket/index.d.ts +11 -13
  30. package/dist/solution/BookingTicket/index.js +23 -37
  31. package/dist/solution/BookingTicket/types.d.ts +9 -6
  32. package/dist/solution/BookingTicket/types.js +6 -3
  33. package/dist/solution/BookingTicket/utils/cartView.d.ts +7 -12
  34. package/dist/solution/BookingTicket/utils/cartView.js +52 -14
  35. package/dist/solution/BookingTicket/utils/orderCustomer.d.ts +4 -0
  36. package/dist/solution/BookingTicket/utils/orderCustomer.js +31 -0
  37. package/dist/solution/Sales/index.d.ts +3 -0
  38. package/dist/solution/Sales/index.js +85 -43
  39. package/dist/solution/Sales/types.d.ts +3 -0
  40. package/dist/solution/ScanOrder/types.d.ts +4 -4
  41. package/dist/solution/ScanOrder/types.js +4 -4
  42. package/dist/solution/ScanOrder/utils.js +3 -3
  43. package/lib/core/index.js +1 -0
  44. package/lib/modules/Cart/utils/cartProduct.js +1 -1
  45. package/lib/modules/Order/index.d.ts +2 -0
  46. package/lib/modules/Order/index.js +39 -7
  47. package/lib/modules/Order/types.d.ts +19 -0
  48. package/lib/modules/Order/utils.js +4 -2
  49. package/lib/modules/Payment/index.d.ts +4 -0
  50. package/lib/modules/Payment/index.js +26 -2
  51. package/lib/modules/Payment/types.d.ts +29 -9
  52. package/lib/modules/SalesSummary/index.d.ts +7 -5
  53. package/lib/modules/SalesSummary/index.js +29 -9
  54. package/lib/plugins/app.d.ts +2 -0
  55. package/lib/server/index.d.ts +2 -5
  56. package/lib/server/index.js +38 -58
  57. package/lib/server/modules/index.d.ts +1 -1
  58. package/lib/server/modules/products/index.d.ts +12 -1
  59. package/lib/server/modules/products/index.js +69 -9
  60. package/lib/server/modules/products/types.d.ts +11 -0
  61. package/lib/server/utils/product.js +2 -2
  62. package/lib/solution/BaseSales/index.d.ts +26 -2
  63. package/lib/solution/BaseSales/index.js +169 -3
  64. package/lib/solution/BaseSales/types.d.ts +4 -4
  65. package/lib/solution/BaseSales/utils/transformBaseProductToOrderProduct.d.ts +62 -0
  66. package/lib/solution/BaseSales/utils/transformBaseProductToOrderProduct.js +155 -0
  67. package/lib/solution/BaseSales/utils.js +3 -3
  68. package/lib/solution/BookingTicket/index.d.ts +11 -13
  69. package/lib/solution/BookingTicket/index.js +17 -27
  70. package/lib/solution/BookingTicket/types.d.ts +9 -6
  71. package/lib/solution/BookingTicket/utils/cartView.d.ts +7 -12
  72. package/lib/solution/BookingTicket/utils/cartView.js +44 -18
  73. package/lib/solution/BookingTicket/utils/orderCustomer.d.ts +4 -0
  74. package/lib/solution/BookingTicket/utils/orderCustomer.js +58 -0
  75. package/lib/solution/Sales/index.d.ts +3 -0
  76. package/lib/solution/Sales/index.js +50 -15
  77. package/lib/solution/Sales/types.d.ts +3 -0
  78. package/lib/solution/ScanOrder/types.d.ts +4 -4
  79. package/lib/solution/ScanOrder/utils.js +3 -3
  80. package/package.json +1 -1
@@ -349,6 +349,18 @@ export interface EftposPayment {
349
349
  /**
350
350
  * 钱包支付接口
351
351
  */
352
+ export interface WalletInitializationResult {
353
+ walletRecommendList: WalletRecommendItem[];
354
+ userIdentificationCodes: UserIdentificationCodeItem[];
355
+ transformList: any[];
356
+ noApplicableVoucher: any[];
357
+ products: any[];
358
+ }
359
+ export interface WalletStoredInitData {
360
+ transformList: any[];
361
+ noApplicableVoucher: any[];
362
+ products: any[];
363
+ }
352
364
  export interface WalletPassPayment {
353
365
  /** 生成钱包API默认参数 */
354
366
  generateWalletParams: (businessData: WalletInitBusinessData) => WalletDeductionRecommendParams;
@@ -357,21 +369,25 @@ export interface WalletPassPayment {
357
369
  /** 清理已存储的钱包参数 */
358
370
  clearStoredWalletParams: () => void;
359
371
  /** 初始化钱包数据(从业务数据开始,内部生成参数) */
360
- initializeWalletDataFromBusinessAsync: (businessData: WalletInitBusinessData) => Promise<{
361
- walletRecommendList: WalletRecommendItem[];
362
- userIdentificationCodes: UserIdentificationCodeItem[];
363
- }>;
372
+ initializeWalletDataFromBusinessAsync: (businessData: WalletInitBusinessData) => Promise<WalletInitializationResult>;
364
373
  /** 初始化钱包数据(先获取推荐列表,再获取用户识别码列表) */
365
- initializeWalletDataAsync: (baseParams: WalletDeductionRecommendParams) => Promise<{
366
- walletRecommendList: WalletRecommendItem[];
367
- userIdentificationCodes: UserIdentificationCodeItem[];
368
- }>;
374
+ initializeWalletDataAsync: (baseParams: WalletDeductionRecommendParams) => Promise<WalletInitializationResult>;
369
375
  /** 获取钱包推荐扣款列表(异步获取并缓存) */
370
376
  getWalletPassRecommendListAsync: (params: WalletDeductionRecommendParams) => Promise<WalletRecommendItem[]>;
371
377
  /** 查询用户识别码列表(异步获取并缓存) */
372
378
  getUserIdentificationCodeListAsync: (params: UserIdentificationCodeParams) => Promise<UserIdentificationCodeItem[]>;
373
379
  /** 搜索识别码信息 */
374
- searchIdentificationCodeAsync: (params: SearchIdentificationCodeParams) => Promise<SearchIdentificationCodeResult>;
380
+ searchIdentificationCodeAsync: (params: SearchIdentificationCodeParams, config?: {
381
+ noCache?: boolean;
382
+ }) => Promise<SearchIdentificationCodeResult>;
383
+ /** 将推荐钱包列表转换为 prepare_payments */
384
+ formatWalletPassList2PreparePayments: (list: WalletRecommendItem[]) => {
385
+ voucher_id: number;
386
+ amount: number;
387
+ tag: string;
388
+ wallet_pass_usage_unit?: unknown;
389
+ wallet_pass_use_value?: string | number | null;
390
+ }[];
375
391
  /** 获取缓存的搜索结果列表 */
376
392
  getSearchResults: () => SearchIdentificationCodeItem[];
377
393
  /** 根据识别码查找搜索结果 */
@@ -384,6 +400,10 @@ export interface WalletPassPayment {
384
400
  clearWalletRecommendList: () => void;
385
401
  /** 清除用户识别码列表 */
386
402
  clearUserIdentificationCodes: () => void;
403
+ /** 获取缓存的钱包初始化数据 */
404
+ getStoredWalletInitData: () => WalletStoredInitData | null;
405
+ /** 清除缓存的钱包初始化数据 */
406
+ clearStoredWalletInitData: () => void;
387
407
  /** 清除搜索结果缓存 */
388
408
  clearSearchResults: () => void;
389
409
  /** 清除所有缓存数据 */
@@ -5,20 +5,22 @@ export * from './types';
5
5
  export declare class SalesSummaryModule extends BaseModule implements Module, SalesSummaryModuleAPI {
6
6
  protected defaultName: string;
7
7
  protected defaultVersion: string;
8
- private shopStore;
8
+ private appPlugin;
9
9
  private store;
10
10
  private request;
11
11
  constructor(name?: string, version?: string);
12
12
  initialize(core: PisellCore, options: ModuleOptions): Promise<void>;
13
13
  getSurchargeList(): Promise<void>;
14
+ private getAppData;
15
+ private getTaxConfig;
14
16
  getSummary(params: {
15
17
  products: any[];
16
18
  isPriceIncludeTax?: number;
17
19
  taxRate?: number;
18
20
  shopDiscount?: string | number;
19
21
  }): Promise<{
20
- tax_title: any;
21
- tax_rate: any;
22
+ tax_title: string;
23
+ tax_rate: number | undefined;
22
24
  product_quantity: number;
23
25
  product_original_amount: string;
24
26
  product_amount: string;
@@ -42,8 +44,8 @@ export declare class SalesSummaryModule extends BaseModule implements Module, Sa
42
44
  pay_service_charge_amount: string;
43
45
  is_price_include_tax?: number | undefined;
44
46
  } | {
45
- tax_title: any;
46
- tax_rate: any;
47
+ tax_title: string;
48
+ tax_rate: number | undefined;
47
49
  product_quantity: number;
48
50
  product_original_amount: string;
49
51
  product_amount: string;
@@ -27,6 +27,11 @@ var import_BaseModule = require("../BaseModule");
27
27
  var import_Schedule = require("../Schedule");
28
28
  var import_utils = require("./utils");
29
29
  __reExport(SalesSummary_exports, require("./types"), module.exports);
30
+ var salesSummaryDataKeys = {
31
+ isPriceIncludeTax: "is_price_include_tax",
32
+ taxRate: "tax_rate",
33
+ taxTitle: "tax_title"
34
+ };
30
35
  var SalesSummaryModule = class extends import_BaseModule.BaseModule {
31
36
  constructor(name, version) {
32
37
  super(name, version);
@@ -37,12 +42,12 @@ var SalesSummaryModule = class extends import_BaseModule.BaseModule {
37
42
  var _a, _b;
38
43
  this.core = core;
39
44
  this.store = options.store;
40
- this.shopStore = this.core.getPlugin("shopStore");
45
+ this.appPlugin = this.core.getPlugin("app");
41
46
  this.request = this.core.getPlugin("request");
42
47
  this.store.surchargeList = ((_a = options.initialState) == null ? void 0 : _a.surchargeList) || [];
43
48
  this.store.summary = ((_b = options.initialState) == null ? void 0 : _b.summary) || (0, import_utils.createEmptySalesSummary)();
44
- if (!this.shopStore) {
45
- throw new Error("SalesSummaryModule 需要 shopStore 插件支持");
49
+ if (!this.appPlugin) {
50
+ throw new Error("SalesSummaryModule 需要 app 插件支持");
46
51
  }
47
52
  if (!this.request) {
48
53
  throw new Error("SalesSummaryModule 需要 request 插件支持");
@@ -66,10 +71,25 @@ var SalesSummaryModule = class extends import_BaseModule.BaseModule {
66
71
  this.store.surchargeList = [];
67
72
  }
68
73
  }
74
+ getAppData(key) {
75
+ var _a, _b, _c, _d, _e, _f;
76
+ const getData = (_b = (_a = this.appPlugin).getData) == null ? void 0 : _b.call(_a);
77
+ if (typeof getData === "function")
78
+ return getData(key);
79
+ const app = this.appPlugin.getApp();
80
+ const coreData = (_f = (_d = (_c = app == null ? void 0 : app.models) == null ? void 0 : _c.getStore) == null ? void 0 : (_e = _d.call(_c)).getDataByModel) == null ? void 0 : _f.call(_e, "core", "core");
81
+ return coreData == null ? void 0 : coreData[key];
82
+ }
83
+ getTaxConfig() {
84
+ return {
85
+ isPriceIncludeTax: this.getAppData(salesSummaryDataKeys.isPriceIncludeTax),
86
+ taxRate: this.getAppData(salesSummaryDataKeys.taxRate),
87
+ taxTitle: this.getAppData(salesSummaryDataKeys.taxTitle)
88
+ };
89
+ }
69
90
  async getSummary(params) {
70
- var _a, _b;
71
91
  const { products = [], isPriceIncludeTax, taxRate, shopDiscount } = params;
72
- const shopInfo = ((_b = (_a = this.shopStore.get("core")) == null ? void 0 : _a.core) == null ? void 0 : _b.shop) || {};
92
+ const taxConfig = this.getTaxConfig();
73
93
  const summarySchedule = this.core.getModule(
74
94
  `${this.name.split("_")[0]}_schedule`
75
95
  );
@@ -83,8 +103,8 @@ var SalesSummaryModule = class extends import_BaseModule.BaseModule {
83
103
  }
84
104
  const summary = (0, import_utils.calculateSalesSummary)({
85
105
  products,
86
- isPriceIncludeTax: isPriceIncludeTax ?? (shopInfo == null ? void 0 : shopInfo.is_price_include_tax) ?? 1,
87
- taxRate: taxRate ?? (shopInfo == null ? void 0 : shopInfo.tax_rate),
106
+ isPriceIncludeTax: isPriceIncludeTax ?? taxConfig.isPriceIncludeTax ?? 1,
107
+ taxRate: taxRate ?? taxConfig.taxRate,
88
108
  surchargeList: this.store.surchargeList,
89
109
  scheduleById,
90
110
  isInScheduleByDate: import_Schedule.ScheduleModule.isInScheduleByDate,
@@ -92,8 +112,8 @@ var SalesSummaryModule = class extends import_BaseModule.BaseModule {
92
112
  });
93
113
  const summaryWithTaxMeta = {
94
114
  ...summary,
95
- tax_title: (shopInfo == null ? void 0 : shopInfo.tax_title) || "",
96
- tax_rate: taxRate ?? (shopInfo == null ? void 0 : shopInfo.tax_rate)
115
+ tax_title: taxConfig.taxTitle || "",
116
+ tax_rate: taxRate ?? taxConfig.taxRate
97
117
  };
98
118
  this.store.summary = summaryWithTaxMeta;
99
119
  return summaryWithTaxMeta;
@@ -1,8 +1,10 @@
1
1
  import { Plugin } from '../types';
2
2
  import App from './app-types/app';
3
+ export type AppDataGetter = (key: string) => any;
3
4
  /**
4
5
  * AppPlugin 接口定义
5
6
  */
6
7
  export interface AppPlugin extends Plugin {
7
8
  getApp: () => App;
9
+ getData?: () => AppDataGetter;
8
10
  }
@@ -372,13 +372,10 @@ declare class Server {
372
372
  */
373
373
  private recomputeAndNotifyBookingQuery;
374
374
  /**
375
- * 根据餐牌配置过滤商品
376
- * @param products 所有商品列表
377
- * @param activeMenuList 生效的餐牌列表
378
- * @returns 过滤后的商品列表
375
+ * 根据餐牌配置提取轻量商品范围。
379
376
  * @private
380
377
  */
381
- private filterProductsByMenuConfig;
378
+ private getProductScopeByMenuConfig;
382
379
  /**
383
380
  * 记录信息日志
384
381
  * @param title 日志标题
@@ -2060,16 +2060,16 @@ var Server = class {
2060
2060
  if (product_id != null && Number.isFinite(Number(product_id))) {
2061
2061
  const pid = Number(product_id);
2062
2062
  const tPrice2 = performance.now();
2063
- const allProductsWithPrice2 = await this.products.getProductsWithPrice(schedule_date, {
2063
+ const allProductsWithPrice = await this.products.getProductsWithPrice(schedule_date, {
2064
2064
  scheduleModule: this.getSchedule()
2065
2065
  }, {
2066
2066
  changedIds: options == null ? void 0 : options.changedIds
2067
2067
  });
2068
2068
  (0, import_product.perfMark)("computeQuery.getProductsWithPrice(single)", performance.now() - tPrice2, {
2069
- count: allProductsWithPrice2.length,
2069
+ count: allProductsWithPrice.length,
2070
2070
  productId: pid
2071
2071
  });
2072
- const published = allProductsWithPrice2.filter((p) => ((p == null ? void 0 : p.status) || "published") === "published");
2072
+ const published = allProductsWithPrice.filter((p) => ((p == null ? void 0 : p.status) || "published") === "published");
2073
2073
  const item = published.find((p) => Number(p.id) === pid) ?? null;
2074
2074
  (0, import_product.perfMark)("computeProductQueryResult", performance.now() - tTotal, {
2075
2075
  mode: "single",
@@ -2108,22 +2108,26 @@ var Server = class {
2108
2108
  activeMenu: activeMenuList.length
2109
2109
  });
2110
2110
  }
2111
+ const tFilter = performance.now();
2112
+ const productScope = this.getProductScopeByMenuConfig(activeMenuList);
2113
+ const scopedProductIds = this.products.getProductIdsByScope(productScope);
2114
+ (0, import_product.perfMark)("computeQuery.filterByMenu", performance.now() - tFilter, {
2115
+ scopedProductCount: scopedProductIds.length,
2116
+ activeMenuCount: activeMenuList.length,
2117
+ isAllProducts: !!productScope.isAllProducts
2118
+ });
2111
2119
  const tPrice = performance.now();
2112
- const allProductsWithPrice = await this.products.getProductsWithPrice(schedule_date, {
2120
+ let filteredProducts = scopedProductIds.length > 0 ? await this.products.getProductsWithPrice(schedule_date, {
2113
2121
  scheduleModule: this.getSchedule(),
2114
2122
  schedule_datetime,
2115
2123
  customer_id
2116
2124
  }, {
2117
- changedIds: options == null ? void 0 : options.changedIds
2118
- });
2125
+ changedIds: options == null ? void 0 : options.changedIds,
2126
+ productIds: productScope.isAllProducts ? void 0 : scopedProductIds
2127
+ }) : [];
2119
2128
  (0, import_product.perfMark)("computeQuery.getProductsWithPrice", performance.now() - tPrice, {
2120
- count: allProductsWithPrice.length
2121
- });
2122
- const tFilter = performance.now();
2123
- let filteredProducts = this.filterProductsByMenuConfig(allProductsWithPrice, activeMenuList);
2124
- (0, import_product.perfMark)("computeQuery.filterByMenu", performance.now() - tFilter, {
2125
- before: allProductsWithPrice.length,
2126
- after: filteredProducts.length
2129
+ count: filteredProducts.length,
2130
+ scopedProductCount: scopedProductIds.length
2127
2131
  });
2128
2132
  const tStatus = performance.now();
2129
2133
  const beforeStatusCount = filteredProducts.length;
@@ -2141,12 +2145,12 @@ var Server = class {
2141
2145
  });
2142
2146
  (0, import_product.perfMark)("computeQuery.sort", performance.now() - tSort, { count: filteredProducts.length });
2143
2147
  (0, import_product.perfMark)("computeProductQueryResult", performance.now() - tTotal, {
2144
- originalCount: allProductsWithPrice.length,
2148
+ scopedProductCount: scopedProductIds.length,
2145
2149
  filteredCount: filteredProducts.length,
2146
2150
  activeMenuCount: activeMenuList.length
2147
2151
  });
2148
2152
  this.logInfo("computeProductQueryResult 完成", {
2149
- originalCount: allProductsWithPrice.length,
2153
+ scopedProductCount: scopedProductIds.length,
2150
2154
  filteredCount: filteredProducts.length,
2151
2155
  activeMenuCount: activeMenuList.length
2152
2156
  });
@@ -2249,29 +2253,25 @@ var Server = class {
2249
2253
  }
2250
2254
  }
2251
2255
  /**
2252
- * 根据餐牌配置过滤商品
2253
- * @param products 所有商品列表
2254
- * @param activeMenuList 生效的餐牌列表
2255
- * @returns 过滤后的商品列表
2256
+ * 根据餐牌配置提取轻量商品范围。
2256
2257
  * @private
2257
2258
  */
2258
- filterProductsByMenuConfig(products, activeMenuList) {
2259
+ getProductScopeByMenuConfig(activeMenuList) {
2259
2260
  if (!activeMenuList || activeMenuList.length === 0) {
2260
2261
  console.log("[Server] 没有生效的餐牌,返回空数组");
2261
- this.logInfo("filterProductsByMenuConfig: 没有生效的餐牌,返回空数组", {
2262
- productCount: products.length,
2262
+ this.logInfo("getProductScopeByMenuConfig: 没有生效的餐牌,返回空数组", {
2263
2263
  activeMenuCount: 0
2264
2264
  });
2265
- return [];
2265
+ return { productIds: [], collectionIds: [] };
2266
2266
  }
2267
- let allowedProductIds = /* @__PURE__ */ new Set();
2268
- let allowedCollectionIds = /* @__PURE__ */ new Set();
2267
+ const allowedProductIds = /* @__PURE__ */ new Set();
2268
+ const allowedCollectionIds = /* @__PURE__ */ new Set();
2269
2269
  let hasProductAll = false;
2270
2270
  for (const menu of activeMenuList) {
2271
2271
  const config = menu.partyroom_package;
2272
2272
  if (!config) {
2273
2273
  console.warn("[Server] 餐牌缺少 partyroom_package 配置:", menu);
2274
- this.logWarning("filterProductsByMenuConfig: 餐牌缺少 partyroom_package 配置", {
2274
+ this.logWarning("getProductScopeByMenuConfig: 餐牌缺少 partyroom_package 配置", {
2275
2275
  menuId: menu.form_record_id,
2276
2276
  menuName: menu.name
2277
2277
  });
@@ -2279,7 +2279,7 @@ var Server = class {
2279
2279
  }
2280
2280
  if (config.type === "product_all") {
2281
2281
  console.log("[Server] 餐牌允许所有商品:", menu.name);
2282
- this.logInfo("filterProductsByMenuConfig: 餐牌允许所有商品", {
2282
+ this.logInfo("getProductScopeByMenuConfig: 餐牌允许所有商品", {
2283
2283
  menuId: menu.form_record_id,
2284
2284
  menuName: menu.name,
2285
2285
  configType: config.type
@@ -2291,7 +2291,7 @@ var Server = class {
2291
2291
  const productIds = config.products.map((item) => item.product_id);
2292
2292
  productIds.forEach((id) => id && allowedProductIds.add(id));
2293
2293
  console.log("[Server] 餐牌允许指定商品 (product_ids):", menu.name, productIds);
2294
- this.logInfo("filterProductsByMenuConfig: 餐牌允许指定商品", {
2294
+ this.logInfo("getProductScopeByMenuConfig: 餐牌允许指定商品", {
2295
2295
  menuId: menu.form_record_id,
2296
2296
  menuName: menu.name,
2297
2297
  configType: config.type,
@@ -2302,7 +2302,7 @@ var Server = class {
2302
2302
  const collectionIds = config.products.map((item) => item.product_collection_id);
2303
2303
  collectionIds.forEach((id) => id && allowedCollectionIds.add(id));
2304
2304
  console.log("[Server] 餐牌允许商品集合 (product_collection):", menu.name, collectionIds);
2305
- this.logInfo("filterProductsByMenuConfig: 餐牌允许商品集合", {
2305
+ this.logInfo("getProductScopeByMenuConfig: 餐牌允许商品集合", {
2306
2306
  menuId: menu.form_record_id,
2307
2307
  menuName: menu.name,
2308
2308
  configType: config.type,
@@ -2311,48 +2311,28 @@ var Server = class {
2311
2311
  }
2312
2312
  }
2313
2313
  if (hasProductAll) {
2314
- this.logInfo("filterProductsByMenuConfig: 返回所有商品(product_all)", {
2315
- productCount: products.length
2316
- });
2317
- return products;
2314
+ this.logInfo("getProductScopeByMenuConfig: 返回所有商品 scope(product_all)");
2315
+ return { isAllProducts: true };
2318
2316
  }
2319
2317
  if (allowedProductIds.size === 0 && allowedCollectionIds.size === 0) {
2320
2318
  console.log("[Server] 没有允许的商品和集合");
2321
- this.logWarning("filterProductsByMenuConfig: 没有允许的商品和集合", {
2322
- productCount: products.length,
2319
+ this.logWarning("getProductScopeByMenuConfig: 没有允许的商品和集合", {
2323
2320
  activeMenuCount: activeMenuList.length
2324
2321
  });
2325
- return [];
2322
+ return { productIds: [], collectionIds: [] };
2326
2323
  }
2327
2324
  const allowedProductIdsArray = Array.from(allowedProductIds);
2328
2325
  const allowedCollectionIdsArray = Array.from(allowedCollectionIds);
2329
2326
  console.log("[Server] 允许的商品 IDs:", allowedProductIdsArray);
2330
2327
  console.log("[Server] 允许的集合 IDs:", allowedCollectionIdsArray);
2331
- this.logInfo("filterProductsByMenuConfig: 收集到的允许规则", {
2328
+ this.logInfo("getProductScopeByMenuConfig: 收集到的允许规则", {
2332
2329
  allowedProductCount: allowedProductIds.size,
2333
2330
  allowedCollectionCount: allowedCollectionIds.size
2334
2331
  });
2335
- const filteredProducts = products.filter((product) => {
2336
- if (allowedProductIds.has(product.id)) {
2337
- return true;
2338
- }
2339
- if (allowedCollectionIds.size > 0 && Array.isArray(product.collection)) {
2340
- const hasMatchingCollection = product.collection.some(
2341
- (col) => allowedCollectionIds.has(col.id)
2342
- );
2343
- if (hasMatchingCollection) {
2344
- return true;
2345
- }
2346
- }
2347
- return false;
2348
- });
2349
- console.log("[Server] 过滤结果: 原始 %d 个,过滤后 %d 个", products.length, filteredProducts.length);
2350
- this.logInfo("filterProductsByMenuConfig: 过滤完成", {
2351
- originalCount: products.length,
2352
- filteredCount: filteredProducts.length,
2353
- removedCount: products.length - filteredProducts.length
2354
- });
2355
- return filteredProducts;
2332
+ return {
2333
+ productIds: allowedProductIdsArray,
2334
+ collectionIds: allowedCollectionIdsArray
2335
+ };
2356
2336
  }
2357
2337
  /**
2358
2338
  * 记录信息日志
@@ -3,7 +3,7 @@
3
3
  * 包含新增的 Products、Menu、Quotation 和扩展的 Schedule 模块
4
4
  */
5
5
  export { ProductsModule } from './products';
6
- export type { ProductsState } from './products/types';
6
+ export type { ProductIdScope, ProductsState } from './products/types';
7
7
  export { ProductsHooks } from './products/types';
8
8
  export { MenuModule } from './menu';
9
9
  export type { MenuState, MenuData } from './menu/types';
@@ -2,7 +2,7 @@ import { Module, PisellCore } from '../../../types';
2
2
  import { BaseModule } from '../../../modules/BaseModule';
3
3
  import { ProductData } from '../../../modules/Product/types';
4
4
  import type { RouteDefinition } from '../../types';
5
- import { ProductFormatter, ProductFormatterContext } from './types';
5
+ import { ProductIdScope, ProductFormatter, ProductFormatterContext } from './types';
6
6
  /**
7
7
  * Products 模块 - 用于获取和管理完整的商品详细数据
8
8
  * 相比 ProductList 模块,Products 会获取所有详细信息并缓存
@@ -80,10 +80,12 @@ export declare class ProductsModule extends BaseModule implements Module {
80
80
  * @param extraContext 额外的上下文数据(可选,由 Server 层传入)
81
81
  * @param options 可选参数
82
82
  * @param options.changedIds 变更的商品 IDs,非空时仅对这些商品增量执行 prepare 并更新缓存
83
+ * @param options.productIds 指定商品 IDs,仅对这些商品执行价格计算;不传则处理全量
83
84
  * @returns 应用了价格的商品列表
84
85
  */
85
86
  getProductsWithPrice(schedule_date: string, extraContext?: Partial<ProductFormatterContext>, options?: {
86
87
  changedIds?: number[];
88
+ productIds?: number[];
87
89
  }): Promise<ProductData[]>;
88
90
  /**
89
91
  * 准备带价格的商品数据(通过格式化器流程处理)
@@ -96,6 +98,11 @@ export declare class ProductsModule extends BaseModule implements Module {
96
98
  */
97
99
  private prepareProductsWithPrice;
98
100
  private getProductsPriceCacheKey;
101
+ private normalizeProductIds;
102
+ /**
103
+ * 按轻量范围返回商品 ID,不做 structuredClone。
104
+ */
105
+ getProductIdsByScope(scope: ProductIdScope): number[];
99
106
  /**
100
107
  * 应用所有已注册的格式化器
101
108
  * @param products 商品列表
@@ -173,6 +180,10 @@ export declare class ProductsModule extends BaseModule implements Module {
173
180
  * 仅供内部 formatter 流程使用,因为 formatter 会创建新对象
174
181
  */
175
182
  private getProductsRef;
183
+ /**
184
+ * 按 ID 从 Map 取商品引用,供内部 formatter 流程使用。
185
+ */
186
+ private getProductsRefByIds;
176
187
  /**
177
188
  * 根据ID获取单个商品(从内存缓存)
178
189
  * 使用 Map 快速查询,时间复杂度 O(1)
@@ -326,12 +326,16 @@ var ProductsModule = class extends import_BaseModule.BaseModule {
326
326
  * @param extraContext 额外的上下文数据(可选,由 Server 层传入)
327
327
  * @param options 可选参数
328
328
  * @param options.changedIds 变更的商品 IDs,非空时仅对这些商品增量执行 prepare 并更新缓存
329
+ * @param options.productIds 指定商品 IDs,仅对这些商品执行价格计算;不传则处理全量
329
330
  * @returns 应用了价格的商品列表
330
331
  */
331
332
  async getProductsWithPrice(schedule_date, extraContext, options) {
333
+ var _a;
332
334
  const t0 = performance.now();
333
- const cacheKey = this.getProductsPriceCacheKey(schedule_date, extraContext);
334
- const changedIds = options == null ? void 0 : options.changedIds;
335
+ const scopedProductIds = this.normalizeProductIds(options == null ? void 0 : options.productIds);
336
+ const cacheKey = this.getProductsPriceCacheKey(schedule_date, extraContext, scopedProductIds);
337
+ const scopedProductIdSet = scopedProductIds ? new Set(scopedProductIds) : void 0;
338
+ const changedIds = scopedProductIdSet ? (_a = options == null ? void 0 : options.changedIds) == null ? void 0 : _a.filter((id) => scopedProductIdSet.has(id)) : options == null ? void 0 : options.changedIds;
335
339
  if (this.productsPriceCache.has(cacheKey)) {
336
340
  const cachedProducts = this.productsPriceCache.get(cacheKey);
337
341
  if (changedIds && changedIds.length > 0) {
@@ -398,8 +402,15 @@ var ProductsModule = class extends import_BaseModule.BaseModule {
398
402
  });
399
403
  return cachedProducts;
400
404
  }
401
- this.logInfo("商品价格缓存未命中,准备获取", { cacheKey });
402
- const result = await this.prepareProductsWithPrice(schedule_date, extraContext);
405
+ this.logInfo("商品价格缓存未命中,准备获取", {
406
+ cacheKey,
407
+ scopedProductCount: scopedProductIds == null ? void 0 : scopedProductIds.length
408
+ });
409
+ const result = await this.prepareProductsWithPrice(
410
+ schedule_date,
411
+ extraContext,
412
+ scopedProductIds ? { productIds: scopedProductIds } : void 0
413
+ );
403
414
  this.productsPriceCache.set(cacheKey, result);
404
415
  this.logInfo("商品价格已缓存", {
405
416
  cacheKey,
@@ -425,7 +436,7 @@ var ProductsModule = class extends import_BaseModule.BaseModule {
425
436
  var _a, _b;
426
437
  const tTotal = performance.now();
427
438
  const targetIds = options == null ? void 0 : options.productIds;
428
- const isIncremental = targetIds && targetIds.length > 0;
439
+ const isIncremental = Array.isArray(targetIds);
429
440
  this.logInfo("prepareProductsWithPrice 开始处理", {
430
441
  schedule_date,
431
442
  mode: isIncremental ? "incremental" : "full",
@@ -435,8 +446,7 @@ var ProductsModule = class extends import_BaseModule.BaseModule {
435
446
  let products;
436
447
  let ids;
437
448
  if (isIncremental) {
438
- const idSet = new Set(targetIds);
439
- products = this.getProductsRef().filter((p) => idSet.has(p.id));
449
+ products = this.getProductsRefByIds(targetIds);
440
450
  ids = products.map((p) => p.id);
441
451
  } else {
442
452
  products = this.getProductsRef();
@@ -499,9 +509,47 @@ var ProductsModule = class extends import_BaseModule.BaseModule {
499
509
  }
500
510
  return [];
501
511
  }
502
- getProductsPriceCacheKey(schedule_date, extraContext) {
512
+ getProductsPriceCacheKey(schedule_date, extraContext, productIds) {
503
513
  const datetime = (extraContext == null ? void 0 : extraContext.schedule_datetime) || schedule_date;
504
- return `${datetime}:customer:${(extraContext == null ? void 0 : extraContext.customer_id) ?? ""}`;
514
+ const productScope = productIds ? `:products:${productIds.join(",")}` : "";
515
+ return `${datetime}:customer:${(extraContext == null ? void 0 : extraContext.customer_id) ?? ""}${productScope}`;
516
+ }
517
+ normalizeProductIds(productIds) {
518
+ if (!Array.isArray(productIds))
519
+ return void 0;
520
+ return Array.from(new Set(
521
+ productIds.map((id) => Number(id)).filter((id) => Number.isFinite(id))
522
+ )).sort((a, b) => a - b);
523
+ }
524
+ /**
525
+ * 按轻量范围返回商品 ID,不做 structuredClone。
526
+ */
527
+ getProductIdsByScope(scope) {
528
+ const productIds = this.normalizeProductIds(scope.productIds) || [];
529
+ const collectionIds = this.normalizeProductIds(scope.collectionIds) || [];
530
+ if (scope.isAllProducts) {
531
+ return this.getProductsRef().map((product) => product.id);
532
+ }
533
+ if (productIds.length === 0 && collectionIds.length === 0)
534
+ return [];
535
+ const result = /* @__PURE__ */ new Set();
536
+ for (const id of productIds) {
537
+ if (this.store.map.has(id))
538
+ result.add(id);
539
+ }
540
+ if (collectionIds.length === 0)
541
+ return Array.from(result);
542
+ const collectionIdSet = new Set(collectionIds);
543
+ for (const product of this.getProductsRef()) {
544
+ if (result.has(product.id))
545
+ continue;
546
+ if (!Array.isArray(product.collection))
547
+ continue;
548
+ const isInCollection = product.collection.some((collection) => collectionIdSet.has(collection.id));
549
+ if (isInCollection)
550
+ result.add(product.id);
551
+ }
552
+ return Array.from(result);
505
553
  }
506
554
  /**
507
555
  * 应用所有已注册的格式化器
@@ -776,6 +824,18 @@ var ProductsModule = class extends import_BaseModule.BaseModule {
776
824
  getProductsRef() {
777
825
  return this.store.list;
778
826
  }
827
+ /**
828
+ * 按 ID 从 Map 取商品引用,供内部 formatter 流程使用。
829
+ */
830
+ getProductsRefByIds(ids) {
831
+ const products = [];
832
+ for (const id of ids) {
833
+ const product = this.store.map.get(id);
834
+ if (product)
835
+ products.push(product);
836
+ }
837
+ return products;
838
+ }
779
839
  /**
780
840
  * 根据ID获取单个商品(从内存缓存)
781
841
  * 使用 Map 快速查询,时间复杂度 O(1)
@@ -17,6 +17,17 @@ export interface FormattedProductData extends ProductData {
17
17
  /** 购物车详情值 */
18
18
  cartDetailValue?: any;
19
19
  }
20
+ /**
21
+ * 商品轻量查询范围
22
+ */
23
+ export interface ProductIdScope {
24
+ /** 是否返回全部商品 ID */
25
+ isAllProducts?: boolean;
26
+ /** 指定商品 ID */
27
+ productIds?: number[];
28
+ /** 指定商品集合 ID */
29
+ collectionIds?: number[];
30
+ }
20
31
  /**
21
32
  * 加载报价单商品价格参数
22
33
  */
@@ -195,7 +195,7 @@ var formatDataKey = (data) => {
195
195
  }
196
196
  if ((_a = _data == null ? void 0 : _data.option) == null ? void 0 : _a.length) {
197
197
  for (const item of _data.option) {
198
- let str = `_${item.option_group_id}_${item.product_option_item_id}`;
198
+ let str = `_${item.option_group_id}_${item.option_group_item_id}`;
199
199
  key += str;
200
200
  rowKey += `${str}_${item.quantity || item.num}`;
201
201
  }
@@ -207,7 +207,7 @@ var formatDataKey = (data) => {
207
207
  rowKey += `${str}_${item.quantity || item.num}`;
208
208
  if (item == null ? void 0 : item.option) {
209
209
  for (const opt of item.option) {
210
- let str2 = `_${opt.option_group_id}_${opt.product_option_item_id}`;
210
+ let str2 = `_${opt.option_group_id}_${opt.option_group_item_id}`;
211
211
  key += str2;
212
212
  rowKey += `${str2}_${opt.quantity || opt.num}`;
213
213
  }