@pisell/pisellos 2.2.78 → 2.2.80
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/core/index.d.ts +2 -2
- package/dist/modules/ProductList/index.d.ts +3 -0
- package/dist/modules/ProductList/index.js +9 -7
- package/dist/plugins/request.d.ts +2 -0
- package/dist/server/index.d.ts +27 -2
- package/dist/server/index.js +499 -295
- package/dist/server/modules/products/index.d.ts +101 -3
- package/dist/server/modules/products/index.js +1381 -242
- package/dist/server/modules/products/types.d.ts +24 -1
- package/dist/server/modules/products/types.js +3 -0
- package/dist/server/utils/product.d.ts +1 -0
- package/dist/server/utils/product.js +35 -25
- package/dist/solution/BookingTicket/index.d.ts +9 -1
- package/dist/solution/BookingTicket/index.js +41 -28
- package/dist/types/index.d.ts +3 -0
- package/lib/core/index.d.ts +2 -2
- package/lib/modules/ProductList/index.d.ts +3 -0
- package/lib/modules/ProductList/index.js +2 -2
- package/lib/plugins/request.d.ts +2 -0
- package/lib/server/index.d.ts +27 -2
- package/lib/server/index.js +172 -95
- package/lib/server/modules/products/index.d.ts +101 -3
- package/lib/server/modules/products/index.js +596 -52
- package/lib/server/modules/products/types.d.ts +24 -1
- package/lib/server/modules/products/types.js +1 -0
- package/lib/server/utils/product.d.ts +1 -0
- package/lib/server/utils/product.js +27 -11
- package/lib/solution/BookingTicket/index.d.ts +9 -1
- package/lib/solution/BookingTicket/index.js +10 -2
- package/lib/types/index.d.ts +3 -0
- package/package.json +1 -1
package/lib/server/index.js
CHANGED
|
@@ -28,6 +28,8 @@ var import_menu = require("./modules/menu");
|
|
|
28
28
|
var import_quotation = require("./modules/quotation");
|
|
29
29
|
var import_schedule = require("./modules/schedule");
|
|
30
30
|
var import_schedule2 = require("./utils/schedule");
|
|
31
|
+
var import_types = require("./modules/products/types");
|
|
32
|
+
var import_product = require("./utils/product");
|
|
31
33
|
__reExport(server_exports, require("./modules"), module.exports);
|
|
32
34
|
var Server = class {
|
|
33
35
|
constructor(core) {
|
|
@@ -38,6 +40,8 @@ var Server = class {
|
|
|
38
40
|
put: {},
|
|
39
41
|
remove: {}
|
|
40
42
|
};
|
|
43
|
+
// ---- 商品查询订阅者 ----
|
|
44
|
+
this.productQuerySubscribers = /* @__PURE__ */ new Map();
|
|
41
45
|
// 模块注册表 - 定义所有可用的模块配置
|
|
42
46
|
this.moduleRegistry = {
|
|
43
47
|
products: {
|
|
@@ -81,109 +85,37 @@ var Server = class {
|
|
|
81
85
|
}
|
|
82
86
|
};
|
|
83
87
|
/**
|
|
84
|
-
*
|
|
85
|
-
*
|
|
88
|
+
* 处理商品查询请求
|
|
89
|
+
* 存储订阅者信息,便于数据变更时推送最新结果
|
|
86
90
|
*/
|
|
87
91
|
this.handleProductQuery = async ({ url, method, data, config }) => {
|
|
88
92
|
console.log("[Server] handleProductQuery:", url, method, data, config);
|
|
89
93
|
const { menu_list_ids, schedule_datetime, schedule_date } = data;
|
|
90
|
-
|
|
91
|
-
|
|
94
|
+
const { callback, subscriberId } = config || {};
|
|
95
|
+
this.logInfo("handleProductQuery: 开始处理商品查询请求", {
|
|
96
|
+
menu_list_ids,
|
|
92
97
|
schedule_datetime,
|
|
93
|
-
schedule_date
|
|
94
|
-
menu_list_ids
|
|
98
|
+
schedule_date
|
|
95
99
|
});
|
|
96
|
-
if (
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
message: "Products 模块未注册",
|
|
101
|
-
data: { list: [], count: 0 }
|
|
102
|
-
};
|
|
103
|
-
}
|
|
104
|
-
if (!this.menu) {
|
|
105
|
-
console.error("[Server] Menu 模块未注册");
|
|
106
|
-
this.logError("handleProductQuery: Menu 模块未注册");
|
|
107
|
-
return {
|
|
108
|
-
message: "Menu 模块未注册",
|
|
109
|
-
data: { list: [], count: 0 }
|
|
110
|
-
};
|
|
111
|
-
}
|
|
112
|
-
if (!this.schedule) {
|
|
113
|
-
console.error("[Server] Schedule 模块未注册");
|
|
114
|
-
this.logError("handleProductQuery: Schedule 模块未注册");
|
|
115
|
-
return {
|
|
116
|
-
message: "Schedule 模块未注册",
|
|
117
|
-
data: { list: [], count: 0 }
|
|
118
|
-
};
|
|
119
|
-
}
|
|
120
|
-
let activeMenuList = [];
|
|
121
|
-
if (menu_list_ids && Array.isArray(menu_list_ids) && menu_list_ids.length > 0) {
|
|
122
|
-
console.log("[Server] 获取餐牌详情,IDs:", menu_list_ids);
|
|
123
|
-
this.logInfo("handleProductQuery: 获取餐牌详情", { menuListIdsCount: menu_list_ids.length, menu_list_ids });
|
|
124
|
-
const menuList = this.menu.getMenuByIds(menu_list_ids);
|
|
125
|
-
this.logInfo("handleProductQuery: 获取到餐牌列表", {
|
|
126
|
-
requestedCount: menu_list_ids.length,
|
|
127
|
-
foundCount: menuList.length,
|
|
128
|
-
menu_list_ids,
|
|
129
|
-
menuList
|
|
100
|
+
if (subscriberId && typeof callback === "function") {
|
|
101
|
+
this.productQuerySubscribers.set(subscriberId, {
|
|
102
|
+
callback,
|
|
103
|
+
context: { menu_list_ids, schedule_date, schedule_datetime }
|
|
130
104
|
});
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
return isInSchedule;
|
|
105
|
+
this.logInfo("handleProductQuery: 已注册订阅者", {
|
|
106
|
+
subscriberId,
|
|
107
|
+
totalSubscribers: this.productQuerySubscribers.size
|
|
135
108
|
});
|
|
136
|
-
this.logInfo("handleProductQuery: 过滤生效餐牌", {
|
|
137
|
-
totalMenuCount: menuList.length,
|
|
138
|
-
activeMenuCount: activeMenuList.length,
|
|
139
|
-
schedule_datetime,
|
|
140
|
-
menuList,
|
|
141
|
-
activeMenuList
|
|
142
|
-
});
|
|
143
|
-
} else {
|
|
144
|
-
this.logWarning("handleProductQuery: 未提供有效的 menu_list_ids", { menuListIdsCount: (menu_list_ids == null ? void 0 : menu_list_ids.length) ?? 0, menu_list_ids });
|
|
145
109
|
}
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
}
|
|
155
|
-
console.log(allProductsWithPrice, "allProductsWithPrice");
|
|
156
|
-
this.logInfo("handleProductQuery: 开始按餐牌配置过滤商品", {
|
|
157
|
-
totalProducts: allProductsWithPrice.length,
|
|
158
|
-
activeMenuCount: activeMenuList.length
|
|
159
|
-
});
|
|
160
|
-
let filteredProducts = this.filterProductsByMenuConfig(allProductsWithPrice, activeMenuList);
|
|
161
|
-
filteredProducts = filteredProducts.sort((a, b) => {
|
|
162
|
-
const sortDiff = Number(b.sort) - Number(a.sort);
|
|
163
|
-
if (sortDiff !== 0) {
|
|
164
|
-
return sortDiff;
|
|
165
|
-
}
|
|
166
|
-
return (a.title || "").localeCompare(b.title || "");
|
|
167
|
-
});
|
|
168
|
-
console.log("[Server] 原始商品数量:", allProductsWithPrice.length);
|
|
169
|
-
console.log("[Server] 过滤后商品数量:", filteredProducts.length);
|
|
170
|
-
console.log(filteredProducts, "filteredProducts");
|
|
171
|
-
this.logInfo("handleProductQuery 处理完成", {
|
|
172
|
-
originalProductCount: allProductsWithPrice.length,
|
|
173
|
-
filteredProductCount: filteredProducts.length,
|
|
174
|
-
activeMenuCount: activeMenuList.length,
|
|
175
|
-
schedule_date,
|
|
176
|
-
schedule_datetime
|
|
177
|
-
});
|
|
178
|
-
return {
|
|
179
|
-
code: 200,
|
|
180
|
-
data: {
|
|
181
|
-
list: filteredProducts,
|
|
182
|
-
count: filteredProducts.length
|
|
183
|
-
},
|
|
184
|
-
message: "",
|
|
185
|
-
status: true
|
|
186
|
-
};
|
|
110
|
+
return this.computeProductQueryResult({ menu_list_ids, schedule_date, schedule_datetime });
|
|
111
|
+
};
|
|
112
|
+
/**
|
|
113
|
+
* 取消商品查询订阅(HTTP 路由入口)
|
|
114
|
+
*/
|
|
115
|
+
this.handleUnsubscribeProductQuery = async ({ data }) => {
|
|
116
|
+
const { subscriberId } = data || {};
|
|
117
|
+
this.removeProductQuerySubscriber(subscriberId);
|
|
118
|
+
return { code: 200, message: "ok", status: true };
|
|
187
119
|
};
|
|
188
120
|
/**
|
|
189
121
|
* 处理获取日程时间段点的请求
|
|
@@ -286,7 +218,7 @@ var Server = class {
|
|
|
286
218
|
const appPlugin = core.getPlugin("app");
|
|
287
219
|
this.app = (appPlugin == null ? void 0 : appPlugin.getApp()) || null;
|
|
288
220
|
this.logger = ((_a = this.app) == null ? void 0 : _a.logger) || null;
|
|
289
|
-
console.log("[Server] Server 初始化");
|
|
221
|
+
console.log("[Server] Server 初始化", this.core);
|
|
290
222
|
this.logInfo("Server 初始化", {
|
|
291
223
|
hasApp: !!this.app,
|
|
292
224
|
hasLogger: !!this.logger
|
|
@@ -483,6 +415,9 @@ var Server = class {
|
|
|
483
415
|
} else {
|
|
484
416
|
this.logInfo("跳过自动预加载", { autoPreload });
|
|
485
417
|
}
|
|
418
|
+
this.core.effects.on(import_types.ProductsHooks.onProductsSyncCompleted, () => {
|
|
419
|
+
this.recomputeAndNotifyProductQuery();
|
|
420
|
+
});
|
|
486
421
|
const duration = Date.now() - startTime;
|
|
487
422
|
this.logInfo("Server 初始化完成", {
|
|
488
423
|
duration: `${duration}ms`,
|
|
@@ -529,6 +464,32 @@ var Server = class {
|
|
|
529
464
|
modules.push("schedule");
|
|
530
465
|
return modules;
|
|
531
466
|
}
|
|
467
|
+
/**
|
|
468
|
+
* 后台静默刷新商品数据
|
|
469
|
+
* 重新拉取全量 SSE 接口,更新本地数据后触发 onProductsSyncCompleted
|
|
470
|
+
* 不影响当前界面展示,适用于切回前台、定时刷新等场景
|
|
471
|
+
*/
|
|
472
|
+
async refreshProductsInBackground() {
|
|
473
|
+
if (!this.products) {
|
|
474
|
+
this.logWarning("refreshProductsInBackground: Products 模块未注册");
|
|
475
|
+
return;
|
|
476
|
+
}
|
|
477
|
+
this.logInfo("refreshProductsInBackground 开始");
|
|
478
|
+
const startTime = Date.now();
|
|
479
|
+
try {
|
|
480
|
+
await this.products.silentRefresh();
|
|
481
|
+
const duration = Date.now() - startTime;
|
|
482
|
+
this.logInfo("refreshProductsInBackground 完成", { duration: `${duration}ms` });
|
|
483
|
+
} catch (error) {
|
|
484
|
+
const duration = Date.now() - startTime;
|
|
485
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
486
|
+
console.error("[Server] refreshProductsInBackground 失败:", error);
|
|
487
|
+
this.logError("refreshProductsInBackground 失败", {
|
|
488
|
+
duration: `${duration}ms`,
|
|
489
|
+
error: errorMessage
|
|
490
|
+
});
|
|
491
|
+
}
|
|
492
|
+
}
|
|
532
493
|
/**
|
|
533
494
|
* 清空所有server模块的IndexedDB缓存
|
|
534
495
|
* @returns Promise<void>
|
|
@@ -666,6 +627,11 @@ var Server = class {
|
|
|
666
627
|
path: "/shop/product/query",
|
|
667
628
|
handler: this.handleProductQuery.bind(this)
|
|
668
629
|
},
|
|
630
|
+
{
|
|
631
|
+
method: "post",
|
|
632
|
+
path: "/shop/product/query/unsubscribe",
|
|
633
|
+
handler: this.handleUnsubscribeProductQuery.bind(this)
|
|
634
|
+
},
|
|
669
635
|
{
|
|
670
636
|
method: "post",
|
|
671
637
|
path: "/shop/menu/schedule-time-points",
|
|
@@ -673,6 +639,117 @@ var Server = class {
|
|
|
673
639
|
}
|
|
674
640
|
]);
|
|
675
641
|
}
|
|
642
|
+
/**
|
|
643
|
+
* 根据 subscriberId 移除商品查询订阅者
|
|
644
|
+
*/
|
|
645
|
+
removeProductQuerySubscriber(subscriberId) {
|
|
646
|
+
if (subscriberId) {
|
|
647
|
+
this.productQuerySubscribers.delete(subscriberId);
|
|
648
|
+
this.logInfo("removeProductQuerySubscriber: 已移除订阅者", {
|
|
649
|
+
subscriberId,
|
|
650
|
+
remaining: this.productQuerySubscribers.size
|
|
651
|
+
});
|
|
652
|
+
}
|
|
653
|
+
}
|
|
654
|
+
/**
|
|
655
|
+
* 商品查询的核心计算逻辑(编排 Products、Menu、Schedule 模块)
|
|
656
|
+
* 供 handleProductQuery 首次返回及 pubsub 变更推送复用
|
|
657
|
+
*/
|
|
658
|
+
async computeProductQueryResult(context) {
|
|
659
|
+
const tTotal = performance.now();
|
|
660
|
+
const { menu_list_ids, schedule_date, schedule_datetime } = context;
|
|
661
|
+
this.logInfo("computeProductQueryResult 开始", {
|
|
662
|
+
menuListIdsCount: (menu_list_ids == null ? void 0 : menu_list_ids.length) ?? 0,
|
|
663
|
+
schedule_datetime,
|
|
664
|
+
schedule_date
|
|
665
|
+
});
|
|
666
|
+
if (!this.products) {
|
|
667
|
+
this.logError("computeProductQueryResult: Products 模块未注册");
|
|
668
|
+
return { message: "Products 模块未注册", data: { list: [], count: 0 } };
|
|
669
|
+
}
|
|
670
|
+
if (!this.menu) {
|
|
671
|
+
this.logError("computeProductQueryResult: Menu 模块未注册");
|
|
672
|
+
return { message: "Menu 模块未注册", data: { list: [], count: 0 } };
|
|
673
|
+
}
|
|
674
|
+
if (!this.schedule) {
|
|
675
|
+
this.logError("computeProductQueryResult: Schedule 模块未注册");
|
|
676
|
+
return { message: "Schedule 模块未注册", data: { list: [], count: 0 } };
|
|
677
|
+
}
|
|
678
|
+
let activeMenuList = [];
|
|
679
|
+
if (menu_list_ids && Array.isArray(menu_list_ids) && menu_list_ids.length > 0) {
|
|
680
|
+
const tMenu = performance.now();
|
|
681
|
+
const menuList = this.menu.getMenuByIds(menu_list_ids);
|
|
682
|
+
activeMenuList = menuList.filter((menu) => {
|
|
683
|
+
var _a;
|
|
684
|
+
return ((_a = this.schedule) == null ? void 0 : _a.getDateIsInSchedule(schedule_datetime, menu.schedule)) || false;
|
|
685
|
+
});
|
|
686
|
+
(0, import_product.perfMark)("computeQuery.filterActiveMenu", performance.now() - tMenu, {
|
|
687
|
+
totalMenu: menuList.length,
|
|
688
|
+
activeMenu: activeMenuList.length
|
|
689
|
+
});
|
|
690
|
+
}
|
|
691
|
+
const tPrice = performance.now();
|
|
692
|
+
const allProductsWithPrice = await this.products.getProductsWithPrice(schedule_date, {
|
|
693
|
+
scheduleModule: this.getSchedule()
|
|
694
|
+
});
|
|
695
|
+
(0, import_product.perfMark)("computeQuery.getProductsWithPrice", performance.now() - tPrice, {
|
|
696
|
+
count: allProductsWithPrice.length
|
|
697
|
+
});
|
|
698
|
+
const tFilter = performance.now();
|
|
699
|
+
let filteredProducts = this.filterProductsByMenuConfig(allProductsWithPrice, activeMenuList);
|
|
700
|
+
(0, import_product.perfMark)("computeQuery.filterByMenu", performance.now() - tFilter, {
|
|
701
|
+
before: allProductsWithPrice.length,
|
|
702
|
+
after: filteredProducts.length
|
|
703
|
+
});
|
|
704
|
+
const tSort = performance.now();
|
|
705
|
+
filteredProducts = filteredProducts.sort((a, b) => {
|
|
706
|
+
const sortDiff = Number(b.sort) - Number(a.sort);
|
|
707
|
+
if (sortDiff !== 0)
|
|
708
|
+
return sortDiff;
|
|
709
|
+
return (a.title || "").localeCompare(b.title || "");
|
|
710
|
+
});
|
|
711
|
+
(0, import_product.perfMark)("computeQuery.sort", performance.now() - tSort, { count: filteredProducts.length });
|
|
712
|
+
(0, import_product.perfMark)("computeProductQueryResult", performance.now() - tTotal, {
|
|
713
|
+
originalCount: allProductsWithPrice.length,
|
|
714
|
+
filteredCount: filteredProducts.length,
|
|
715
|
+
activeMenuCount: activeMenuList.length
|
|
716
|
+
});
|
|
717
|
+
this.logInfo("computeProductQueryResult 完成", {
|
|
718
|
+
originalCount: allProductsWithPrice.length,
|
|
719
|
+
filteredCount: filteredProducts.length,
|
|
720
|
+
activeMenuCount: activeMenuList.length
|
|
721
|
+
});
|
|
722
|
+
return {
|
|
723
|
+
code: 200,
|
|
724
|
+
data: { list: filteredProducts, count: filteredProducts.length },
|
|
725
|
+
message: "",
|
|
726
|
+
status: true
|
|
727
|
+
};
|
|
728
|
+
}
|
|
729
|
+
/**
|
|
730
|
+
* 数据变更后,遍历所有订阅者重新计算查询结果并通过 callback 推送
|
|
731
|
+
* 由 ProductsModule 的 onProductsSyncCompleted 事件触发
|
|
732
|
+
*/
|
|
733
|
+
async recomputeAndNotifyProductQuery() {
|
|
734
|
+
if (this.productQuerySubscribers.size === 0)
|
|
735
|
+
return;
|
|
736
|
+
this.logInfo("recomputeAndNotifyProductQuery: 开始推送", {
|
|
737
|
+
subscriberCount: this.productQuerySubscribers.size
|
|
738
|
+
});
|
|
739
|
+
for (const [subscriberId, subscriber] of this.productQuerySubscribers.entries()) {
|
|
740
|
+
try {
|
|
741
|
+
const result = await this.computeProductQueryResult(subscriber.context);
|
|
742
|
+
subscriber.callback(result);
|
|
743
|
+
this.logInfo("recomputeAndNotifyProductQuery: 已推送", { subscriberId });
|
|
744
|
+
} catch (error) {
|
|
745
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
746
|
+
this.logError("recomputeAndNotifyProductQuery: 推送失败", {
|
|
747
|
+
subscriberId,
|
|
748
|
+
error: errorMessage
|
|
749
|
+
});
|
|
750
|
+
}
|
|
751
|
+
}
|
|
752
|
+
}
|
|
676
753
|
/**
|
|
677
754
|
* 根据餐牌配置过滤商品
|
|
678
755
|
* @param products 所有商品列表
|
|
@@ -19,6 +19,9 @@ export declare class ProductsModule extends BaseModule implements Module {
|
|
|
19
19
|
private readonly CACHE_MAX_DAYS;
|
|
20
20
|
private formatters;
|
|
21
21
|
private isPriceFormatterRegistered;
|
|
22
|
+
private productDataSource;
|
|
23
|
+
private pendingSyncMessages;
|
|
24
|
+
private syncTimer?;
|
|
22
25
|
constructor(name?: string, version?: string);
|
|
23
26
|
initialize(core: PisellCore, options: any): Promise<void>;
|
|
24
27
|
/**
|
|
@@ -107,26 +110,57 @@ export declare class ProductsModule extends BaseModule implements Module {
|
|
|
107
110
|
* 可用于手动刷新价格数据
|
|
108
111
|
*/
|
|
109
112
|
clearPriceCache(): void;
|
|
113
|
+
/**
|
|
114
|
+
* 通过 ProductDataSource SSE 加载完整商品列表
|
|
115
|
+
*/
|
|
116
|
+
loadProductsByServer(): Promise<any>;
|
|
117
|
+
/**
|
|
118
|
+
* 纯请求方法:通过 HTTP 接口获取商品列表(无副作用,不触发事件、不写 IndexDB)
|
|
119
|
+
* @param params 查询参数
|
|
120
|
+
* @returns 商品列表
|
|
121
|
+
*/
|
|
122
|
+
private fetchProductsByHttp;
|
|
110
123
|
/**
|
|
111
124
|
* 加载完整商品列表通过接口(包含所有详细数据)
|
|
125
|
+
* 包含副作用:保存到 IndexDB + 触发 onProductsLoaded 事件
|
|
112
126
|
* @param params 查询参数
|
|
113
127
|
*/
|
|
114
|
-
|
|
128
|
+
loadProductsByServerHttp(params?: {
|
|
115
129
|
category_ids?: number[];
|
|
116
130
|
product_ids?: number[];
|
|
117
131
|
collection?: number | string[];
|
|
118
132
|
customer_id?: number;
|
|
119
133
|
cacheId?: string;
|
|
120
|
-
}): Promise<
|
|
134
|
+
}): Promise<ProductData[]>;
|
|
121
135
|
/**
|
|
122
|
-
*
|
|
136
|
+
* 获取商品列表(深拷贝,供外部安全使用)
|
|
123
137
|
*/
|
|
124
138
|
getProducts(): Promise<ProductData[]>;
|
|
139
|
+
/**
|
|
140
|
+
* 内部获取商品列表的直接引用(无拷贝)
|
|
141
|
+
* 仅供内部 formatter 流程使用,因为 formatter 会创建新对象
|
|
142
|
+
*/
|
|
143
|
+
private getProductsRef;
|
|
125
144
|
/**
|
|
126
145
|
* 根据ID获取单个商品(从内存缓存)
|
|
127
146
|
* 使用 Map 快速查询,时间复杂度 O(1)
|
|
128
147
|
*/
|
|
129
148
|
getProductById(id: number): Promise<ProductData | undefined>;
|
|
149
|
+
/**
|
|
150
|
+
* 根据 ID 列表删除商品(用于 pubsub 同步删除场景)
|
|
151
|
+
* 同时更新 store.list、store.map、IndexDB 和价格缓存
|
|
152
|
+
*/
|
|
153
|
+
removeProductsByIds(ids: number[]): Promise<void>;
|
|
154
|
+
/**
|
|
155
|
+
* 重新从服务器加载全量商品列表并更新本地 store
|
|
156
|
+
* 用于 pubsub 同步 create / update / batch_update 场景
|
|
157
|
+
*/
|
|
158
|
+
refreshProducts(): Promise<ProductData[]>;
|
|
159
|
+
/**
|
|
160
|
+
* 局部更新指定商品的报价单价格
|
|
161
|
+
* 遍历所有已缓存的日期,为目标商品重新获取价格并覆盖到缓存中
|
|
162
|
+
*/
|
|
163
|
+
updateProductPriceByIds(ids: number[]): Promise<void>;
|
|
130
164
|
/**
|
|
131
165
|
* 清空缓存
|
|
132
166
|
*/
|
|
@@ -152,6 +186,70 @@ export declare class ProductsModule extends BaseModule implements Module {
|
|
|
152
186
|
* 在模块注册后自动调用
|
|
153
187
|
*/
|
|
154
188
|
preload(): Promise<void>;
|
|
189
|
+
/**
|
|
190
|
+
* 初始化 ProductDataSource 实例
|
|
191
|
+
* 与 pubsub 订阅和数据获取分开,仅负责创建实例
|
|
192
|
+
*/
|
|
193
|
+
private initProductDataSource;
|
|
194
|
+
/**
|
|
195
|
+
* 初始化 pubsub 订阅,监听管理后台商品变更
|
|
196
|
+
* 仅负责订阅 product / product_quotation 频道,消息通过防抖合并后批量处理
|
|
197
|
+
* 数据获取由 loadProductsByServer 单独负责
|
|
198
|
+
*/
|
|
199
|
+
private setupProductSync;
|
|
200
|
+
/**
|
|
201
|
+
* 处理防抖后的同步消息批次
|
|
202
|
+
*
|
|
203
|
+
* product 模块:
|
|
204
|
+
* - operation === 'delete' → 本地删除
|
|
205
|
+
* - 有 body(无 price change_types) → body 完整数据直接覆盖本地
|
|
206
|
+
* - change_types 包含 price → SSE 增量拉取 + 刷新报价单价格缓存
|
|
207
|
+
* - change_types 仅 stock → 跳过(暂不响应)
|
|
208
|
+
*
|
|
209
|
+
* product_collection / product_category / product_quotation:
|
|
210
|
+
* - 按 relation_product_ids SSE 拉取受影响商品
|
|
211
|
+
* - product_quotation 额外刷新报价单价格缓存
|
|
212
|
+
*
|
|
213
|
+
* 处理完成后 emit onProductsSyncCompleted 通知 Server 层
|
|
214
|
+
*/
|
|
215
|
+
private processProductSyncMessages;
|
|
216
|
+
/**
|
|
217
|
+
* 通过 SSE 按 ids 增量拉取商品数据
|
|
218
|
+
* 请求 GET /shop/core/stream?type=product&ids={ids}
|
|
219
|
+
*/
|
|
220
|
+
private fetchProductsBySSE;
|
|
221
|
+
/**
|
|
222
|
+
* 将 body 完整数据直接覆盖到本地 store(不调用报价单接口)
|
|
223
|
+
* 已存在的 → 直接替换;不存在的 → 追加
|
|
224
|
+
* 同时更新 Map 缓存、IndexDB,清空价格缓存,触发 onProductsChanged
|
|
225
|
+
*/
|
|
226
|
+
private applyBodyUpdatesToStore;
|
|
227
|
+
/**
|
|
228
|
+
* 将增量拉取的商品合并到 store
|
|
229
|
+
* 已存在的 → 替换;新的 → 追加
|
|
230
|
+
* 同时更新 store.map、IndexDB,触发 onProductsChanged
|
|
231
|
+
*/
|
|
232
|
+
private mergeProductsToStore;
|
|
233
|
+
/**
|
|
234
|
+
* 增量更新价格缓存中变更的商品
|
|
235
|
+
* 对每个已缓存的日期 key:替换/追加最新商品数据,重新拉取这些 ID 的价格并应用
|
|
236
|
+
*/
|
|
237
|
+
private updatePriceCacheForProducts;
|
|
238
|
+
/**
|
|
239
|
+
* 全量重新拉取报价单价格并重建价格缓存
|
|
240
|
+
* 遍历当前已缓存的所有日期 key,对每个日期重新调用 loadProductsPrice
|
|
241
|
+
*/
|
|
242
|
+
private refreshAllPriceCache;
|
|
243
|
+
/**
|
|
244
|
+
* 静默全量刷新:后台重新拉取全量 SSE 数据并更新本地
|
|
245
|
+
* 拿到完整数据后一次性替换 store,清除价格缓存,触发 onProductsSyncCompleted
|
|
246
|
+
* @returns 刷新后的商品列表
|
|
247
|
+
*/
|
|
248
|
+
silentRefresh(): Promise<ProductData[]>;
|
|
249
|
+
/**
|
|
250
|
+
* 销毁同步资源(取消 pubsub 订阅、清除定时器)
|
|
251
|
+
*/
|
|
252
|
+
destroyProductSync(): void;
|
|
155
253
|
/**
|
|
156
254
|
* 获取模块的路由定义
|
|
157
255
|
* Products 模块暂不提供路由,由 Server 层统一处理
|