@pisell/pisellos 2.3.30 → 2.3.32
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/model/strategy/adapter/dataVariant/adapter.d.ts +50 -0
- package/dist/model/strategy/adapter/dataVariant/adapter.js +167 -0
- package/dist/model/strategy/adapter/dataVariant/evaluator.d.ts +89 -0
- package/dist/model/strategy/adapter/dataVariant/evaluator.js +780 -0
- package/dist/model/strategy/adapter/dataVariant/examples.d.ts +39 -0
- package/dist/model/strategy/adapter/dataVariant/examples.js +277 -0
- package/dist/model/strategy/adapter/dataVariant/index.d.ts +4 -0
- package/dist/model/strategy/adapter/dataVariant/index.js +4 -0
- package/dist/model/strategy/adapter/dataVariant/type.d.ts +148 -0
- package/dist/model/strategy/adapter/dataVariant/type.js +54 -0
- package/dist/model/strategy/adapter/index.d.ts +4 -0
- package/dist/model/strategy/adapter/index.js +5 -1
- package/dist/model/strategy/adapter/promotion/index.js +0 -9
- package/dist/modules/Discount/index.d.ts +5 -2
- package/dist/modules/Discount/index.js +30 -7
- package/dist/modules/Discount/types.d.ts +4 -0
- package/dist/modules/Order/index.d.ts +2 -1
- package/dist/modules/Order/index.js +25 -11
- package/dist/modules/Order/types.d.ts +2 -0
- package/dist/modules/Product/index.d.ts +1 -1
- package/dist/modules/Product/types.d.ts +22 -0
- package/dist/modules/ProductList/index.d.ts +1 -1
- package/dist/modules/ProductList/index.js +4 -2
- package/dist/modules/ProductList/types.d.ts +2 -0
- package/dist/server/index.d.ts +50 -0
- package/dist/server/index.js +1095 -849
- package/dist/server/modules/order/index.d.ts +8 -3
- package/dist/server/modules/order/index.js +765 -675
- package/dist/server/modules/products/index.d.ts +26 -7
- package/dist/server/modules/products/index.js +167 -76
- package/dist/server/modules/products/types.d.ts +14 -0
- package/dist/solution/BaseSales/index.d.ts +2 -0
- package/dist/solution/BaseSales/index.js +23 -13
- package/dist/solution/BookingByStep/index.d.ts +1 -1
- package/dist/solution/BookingTicket/types.d.ts +2 -0
- package/dist/solution/ShopDiscount/index.js +15 -14
- package/lib/model/strategy/adapter/dataVariant/adapter.d.ts +50 -0
- package/lib/model/strategy/adapter/dataVariant/adapter.js +149 -0
- package/lib/model/strategy/adapter/dataVariant/evaluator.d.ts +89 -0
- package/lib/model/strategy/adapter/dataVariant/evaluator.js +583 -0
- package/lib/model/strategy/adapter/dataVariant/examples.d.ts +39 -0
- package/lib/model/strategy/adapter/dataVariant/examples.js +293 -0
- package/lib/model/strategy/adapter/dataVariant/index.d.ts +4 -0
- package/lib/model/strategy/adapter/dataVariant/index.js +38 -0
- package/lib/model/strategy/adapter/dataVariant/type.d.ts +148 -0
- package/lib/model/strategy/adapter/dataVariant/type.js +31 -0
- package/lib/model/strategy/adapter/index.d.ts +4 -0
- package/lib/model/strategy/adapter/index.js +13 -2
- package/lib/model/strategy/adapter/promotion/index.js +0 -49
- package/lib/modules/Discount/index.d.ts +5 -2
- package/lib/modules/Discount/index.js +23 -3
- package/lib/modules/Discount/types.d.ts +4 -0
- package/lib/modules/Order/index.d.ts +2 -1
- package/lib/modules/Order/index.js +13 -1
- package/lib/modules/Order/types.d.ts +2 -0
- package/lib/modules/Product/index.d.ts +1 -1
- package/lib/modules/Product/types.d.ts +22 -0
- package/lib/modules/ProductList/index.d.ts +1 -1
- package/lib/modules/ProductList/index.js +4 -2
- package/lib/modules/ProductList/types.d.ts +2 -0
- package/lib/server/index.d.ts +50 -0
- package/lib/server/index.js +206 -29
- package/lib/server/modules/order/index.d.ts +8 -3
- package/lib/server/modules/order/index.js +152 -43
- package/lib/server/modules/products/index.d.ts +26 -7
- package/lib/server/modules/products/index.js +114 -35
- package/lib/server/modules/products/types.d.ts +14 -0
- package/lib/solution/BaseSales/index.d.ts +2 -0
- package/lib/solution/BaseSales/index.js +12 -3
- package/lib/solution/BookingByStep/index.d.ts +1 -1
- package/lib/solution/BookingTicket/types.d.ts +2 -0
- package/lib/solution/ShopDiscount/index.js +2 -1
- package/package.json +1 -1
|
@@ -68,6 +68,10 @@ export interface DiscountWithReason {
|
|
|
68
68
|
discountList: Discount[];
|
|
69
69
|
unavailableReasonKey: DiscountFilterRejectKey | null;
|
|
70
70
|
}
|
|
71
|
+
export interface DiscountPrepareConfigResult {
|
|
72
|
+
discountList: Discount[];
|
|
73
|
+
availableWalletIds: number[];
|
|
74
|
+
}
|
|
71
75
|
interface ExtensionData {
|
|
72
76
|
id: number;
|
|
73
77
|
shop_id: number;
|
|
@@ -76,6 +76,7 @@ export declare class OrderModule extends BaseModule implements Module, OrderModu
|
|
|
76
76
|
apply?: boolean;
|
|
77
77
|
}): Promise<Discount[]>;
|
|
78
78
|
getDiscountList(): Discount[];
|
|
79
|
+
getAvailableWalletIds(): number[];
|
|
79
80
|
private syncScannedDiscountsToOriginalDiscountList;
|
|
80
81
|
private ensureEditDiscountConfigForProductChange;
|
|
81
82
|
scanCode(code: string, customerId?: number): Promise<{
|
|
@@ -484,7 +485,7 @@ export declare class OrderModule extends BaseModule implements Module, OrderModu
|
|
|
484
485
|
generateIdempotencyToken(): string;
|
|
485
486
|
syncPaymentsToOrder<T = any>(params: SyncPaymentsToOrderParams): Promise<SyncPaymentsToOrderResult<T>>;
|
|
486
487
|
createOrder(params: CommitOrderParams['query']): {
|
|
487
|
-
type: "
|
|
488
|
+
type: "appointment_booking" | "virtual";
|
|
488
489
|
platform: string;
|
|
489
490
|
sales_channel: string;
|
|
490
491
|
order_sales_channel: string;
|
|
@@ -311,6 +311,9 @@ var OrderModule = class extends import_BaseModule.BaseModule {
|
|
|
311
311
|
if (!this.store.summary) {
|
|
312
312
|
this.store.summary = (0, import_utils.createEmptySummary)();
|
|
313
313
|
}
|
|
314
|
+
if (!Array.isArray(this.store.availableWalletIds)) {
|
|
315
|
+
this.store.availableWalletIds = [];
|
|
316
|
+
}
|
|
314
317
|
this.request = this.core.getPlugin("request");
|
|
315
318
|
this.appPlugin = this.core.getPlugin("app");
|
|
316
319
|
if (!this.appPlugin) {
|
|
@@ -438,15 +441,19 @@ var OrderModule = class extends import_BaseModule.BaseModule {
|
|
|
438
441
|
async loadDiscountConfig(params) {
|
|
439
442
|
var _a, _b, _c, _d, _e, _f, _g;
|
|
440
443
|
const orderId = params.orderId || ((_a = this.store.tempOrder) == null ? void 0 : _a.order_id) || void 0;
|
|
441
|
-
const
|
|
444
|
+
const prepareConfigResult = await ((_b = this.store.discount) == null ? void 0 : _b.loadPrepareConfig({
|
|
442
445
|
customer_id: params.customerId,
|
|
443
446
|
action: params.action || (orderId ? "edit" : "create"),
|
|
444
447
|
with_good_pass: 1,
|
|
445
448
|
with_discount_card: 1,
|
|
446
449
|
with_wallet_pass_holder: 1,
|
|
450
|
+
with_available_wallet_flag: 1,
|
|
451
|
+
with_available_wallet_pass_flag: 1,
|
|
447
452
|
request_timezone: Intl.DateTimeFormat().resolvedOptions().timeZone,
|
|
448
453
|
...orderId ? { order_id: orderId } : {}
|
|
449
454
|
}));
|
|
455
|
+
const discountList = prepareConfigResult == null ? void 0 : prepareConfigResult.discountList;
|
|
456
|
+
this.store.availableWalletIds = (prepareConfigResult == null ? void 0 : prepareConfigResult.availableWalletIds) || [];
|
|
450
457
|
if (discountList) {
|
|
451
458
|
const currentDiscountList = ((_c = this.store.discount) == null ? void 0 : _c.getDiscountList()) || [];
|
|
452
459
|
const currentEditDiscounts = currentDiscountList.filter((discount) => discount == null ? void 0 : discount.isEditMode);
|
|
@@ -480,6 +487,9 @@ var OrderModule = class extends import_BaseModule.BaseModule {
|
|
|
480
487
|
var _a;
|
|
481
488
|
return ((_a = this.store.discount) == null ? void 0 : _a.getDiscountList()) || [];
|
|
482
489
|
}
|
|
490
|
+
getAvailableWalletIds() {
|
|
491
|
+
return Array.isArray(this.store.availableWalletIds) ? [...this.store.availableWalletIds] : [];
|
|
492
|
+
}
|
|
483
493
|
async syncScannedDiscountsToOriginalDiscountList(discountList) {
|
|
484
494
|
var _a, _b, _c, _d;
|
|
485
495
|
const scanDiscounts = (discountList || []).filter((discount) => discount == null ? void 0 : discount.isScan);
|
|
@@ -2427,6 +2437,7 @@ var OrderModule = class extends import_BaseModule.BaseModule {
|
|
|
2427
2437
|
}
|
|
2428
2438
|
const nextCustomerId = tempOrder.customer_id ?? null;
|
|
2429
2439
|
if (String(previousCustomerId ?? "") !== String(nextCustomerId ?? "")) {
|
|
2440
|
+
this.store.availableWalletIds = [];
|
|
2430
2441
|
(0, import_utils.clearTempOrderHolderAssignments)(tempOrder);
|
|
2431
2442
|
this.clearCustomerBoundDiscounts(tempOrder);
|
|
2432
2443
|
this.applyDiscount();
|
|
@@ -2466,6 +2477,7 @@ var OrderModule = class extends import_BaseModule.BaseModule {
|
|
|
2466
2477
|
tempOrder.customer = null;
|
|
2467
2478
|
if (tempOrder._extend)
|
|
2468
2479
|
delete tempOrder._extend.customerSnapshot;
|
|
2480
|
+
this.store.availableWalletIds = [];
|
|
2469
2481
|
this.clearCustomerBoundDiscounts(tempOrder);
|
|
2470
2482
|
this.persistTempOrder();
|
|
2471
2483
|
this.saveDraftInBackground();
|
|
@@ -309,6 +309,7 @@ export interface OrderState {
|
|
|
309
309
|
lastOrderInfo?: Record<string, any>;
|
|
310
310
|
syncState?: OrderSyncState;
|
|
311
311
|
discountList: any[];
|
|
312
|
+
availableWalletIds?: number[];
|
|
312
313
|
discount: DiscountModule | null;
|
|
313
314
|
rules: RulesModule | null;
|
|
314
315
|
}
|
|
@@ -807,6 +808,7 @@ export interface OrderModuleAPI {
|
|
|
807
808
|
apply?: boolean;
|
|
808
809
|
}) => Promise<Discount[]>;
|
|
809
810
|
getDiscountList: () => Discount[];
|
|
811
|
+
getAvailableWalletIds: () => number[];
|
|
810
812
|
applyDiscount: () => void;
|
|
811
813
|
/**
|
|
812
814
|
* 注入促销评估器。
|
|
@@ -49,5 +49,5 @@ export declare class Product extends BaseModule implements Module {
|
|
|
49
49
|
getCategories(): ProductCategory[];
|
|
50
50
|
setOtherParams(key: string, value: any): void;
|
|
51
51
|
getOtherParams(): any;
|
|
52
|
-
getProductType(): "
|
|
52
|
+
getProductType(): "normal" | "duration" | "session";
|
|
53
53
|
}
|
|
@@ -9,6 +9,26 @@ export interface ProductCollection {
|
|
|
9
9
|
/** 商品集合封面 */
|
|
10
10
|
cover: string;
|
|
11
11
|
}
|
|
12
|
+
/**
|
|
13
|
+
* 商品 Data Variant 记录,结构与后端 data_variant 表对齐。
|
|
14
|
+
*/
|
|
15
|
+
export interface ProductDataVariant {
|
|
16
|
+
/** 变体记录 id */
|
|
17
|
+
id: number;
|
|
18
|
+
/** 店铺 id */
|
|
19
|
+
shop_id: number;
|
|
20
|
+
/** 所属模块,商品场景固定为 product */
|
|
21
|
+
module: string;
|
|
22
|
+
/** 模块数据 id,商品场景对应 product.id */
|
|
23
|
+
module_data_id: number;
|
|
24
|
+
/** 关联 data_variant_rule.id */
|
|
25
|
+
data_variant_rule_id: number;
|
|
26
|
+
/** 命中策略后覆盖到商品上的字段 */
|
|
27
|
+
data: Record<string, any>;
|
|
28
|
+
created_at: string | null;
|
|
29
|
+
updated_at: string | null;
|
|
30
|
+
deleted_at: string | null;
|
|
31
|
+
}
|
|
12
32
|
/**
|
|
13
33
|
* 商品基本信息接口
|
|
14
34
|
*/
|
|
@@ -214,6 +234,8 @@ export interface ProductData {
|
|
|
214
234
|
bundle_group_count: number;
|
|
215
235
|
/** 选项组数量 */
|
|
216
236
|
option_group_count: number;
|
|
237
|
+
/** 智能定价变体列表,/product/query with dataVariants 返回 */
|
|
238
|
+
data_variants?: ProductDataVariant[];
|
|
217
239
|
/** 服务时长 */
|
|
218
240
|
service_times?: any;
|
|
219
241
|
}
|
|
@@ -26,7 +26,7 @@ export declare class ProductList extends BaseModule implements Module {
|
|
|
26
26
|
* const products = await productList.getAddTimeProducts({ schedule_date: '2026-06-16' });
|
|
27
27
|
*/
|
|
28
28
|
getAddTimeProducts(params?: ProductListLoadProductsParams): Promise<any>;
|
|
29
|
-
loadProducts({ category_ids, product_ids, collection, menu_list_ids, customer_id: paramsCustomerId, with_count, schedule_datetime, schedule_date, cacheId, with_schedule, extension_type, status, }?: ProductListLoadProductsParams, options?: {
|
|
29
|
+
loadProducts({ category_ids, product_ids, collection, menu_list_ids, customer_id: paramsCustomerId, with_count, schedule_datetime, schedule_date, cacheId, with_schedule, extension_type, status, strategy_context, }?: ProductListLoadProductsParams, options?: {
|
|
30
30
|
callback?: (result: any) => void;
|
|
31
31
|
subscriberId?: string;
|
|
32
32
|
}): Promise<any>;
|
|
@@ -95,7 +95,8 @@ var ProductList = class extends import_BaseModule.BaseModule {
|
|
|
95
95
|
cacheId,
|
|
96
96
|
with_schedule,
|
|
97
97
|
extension_type,
|
|
98
|
-
status = "published"
|
|
98
|
+
status = "published",
|
|
99
|
+
strategy_context
|
|
99
100
|
} = {}, options) {
|
|
100
101
|
var _a, _b;
|
|
101
102
|
let userPlugin = this.core.getPlugin("user");
|
|
@@ -135,7 +136,8 @@ var ProductList = class extends import_BaseModule.BaseModule {
|
|
|
135
136
|
is_eject: 1,
|
|
136
137
|
with_schedule,
|
|
137
138
|
schedule_datetime,
|
|
138
|
-
extension_type
|
|
139
|
+
extension_type,
|
|
140
|
+
strategy_context
|
|
139
141
|
},
|
|
140
142
|
{ osServer: true, callback: options == null ? void 0 : options.callback, subscriberId: options == null ? void 0 : options.subscriberId, customToast: () => {
|
|
141
143
|
} }
|
|
@@ -12,6 +12,8 @@ export interface ProductListLoadProductsParams {
|
|
|
12
12
|
with_schedule?: number;
|
|
13
13
|
extension_type?: string[];
|
|
14
14
|
status?: string;
|
|
15
|
+
/** 智能定价条件上下文,仅影响策略命中,不参与商品集合筛选 */
|
|
16
|
+
strategy_context?: Record<string, any>;
|
|
15
17
|
}
|
|
16
18
|
export interface ProductListData {
|
|
17
19
|
list: ProductData[];
|
package/lib/server/index.d.ts
CHANGED
|
@@ -47,6 +47,8 @@ declare class Server {
|
|
|
47
47
|
private prefixRouterGet;
|
|
48
48
|
router: Router;
|
|
49
49
|
private productQuerySubscribers;
|
|
50
|
+
/** subscriberId → 智能定价变价时间点定时器句柄(与 subscriber 分离存储,便于 clear) */
|
|
51
|
+
private productQueryScheduleTimers;
|
|
50
52
|
private orderQuerySubscribers;
|
|
51
53
|
private bookingQuerySubscribers;
|
|
52
54
|
private bookingRemoteQuerySubscribers;
|
|
@@ -211,6 +213,48 @@ declare class Server {
|
|
|
211
213
|
* 根据 subscriberId 移除商品查询订阅者
|
|
212
214
|
*/
|
|
213
215
|
removeProductQuerySubscriber(subscriberId?: string): void;
|
|
216
|
+
/**
|
|
217
|
+
* 构建商品 query 响应 data 段,附带智能定价 schedule_time_points 元数据。
|
|
218
|
+
*/
|
|
219
|
+
private buildProductQueryResultPayload;
|
|
220
|
+
/**
|
|
221
|
+
* 是否启用商品 query 订阅的 schedule 时间点定时重算。
|
|
222
|
+
* strategy_context.enable_schedule_reload === false 时关闭。
|
|
223
|
+
*/
|
|
224
|
+
private shouldScheduleProductQueryReload;
|
|
225
|
+
/**
|
|
226
|
+
* 清除指定 subscriber 的全部 schedule 定时器,防止重复 query 或 unsubscribe 后泄漏。
|
|
227
|
+
*/
|
|
228
|
+
private clearSubscriberScheduleTimers;
|
|
229
|
+
/**
|
|
230
|
+
* 从 productQueryScheduleTimers 移除已触发的 timer 引用。
|
|
231
|
+
*/
|
|
232
|
+
private removeScheduleTimerRef;
|
|
233
|
+
/**
|
|
234
|
+
* 计算 schedule 变价时间点到当前时刻的延迟(ms)。已过期返回 -1。
|
|
235
|
+
*/
|
|
236
|
+
private computeScheduleTimePointDelayMs;
|
|
237
|
+
/**
|
|
238
|
+
* 定时重算前刷新 subscriber schedule 上下文:使用执行瞬间时间,禁止沿用首次 query 的 schedule_datetime。
|
|
239
|
+
*/
|
|
240
|
+
private refreshSubscriberScheduleAtExecution;
|
|
241
|
+
/**
|
|
242
|
+
* 为 subscriber 注册智能定价 scheduleTimePoints 定时重算。
|
|
243
|
+
* 必须先 clearSubscriberScheduleTimers,再调用本方法。
|
|
244
|
+
*/
|
|
245
|
+
private scheduleSubscriberTimePointReloads;
|
|
246
|
+
/**
|
|
247
|
+
* schedule 变价时间点定时器触发:刷新 schedule_datetime 后重算并 callback。
|
|
248
|
+
*/
|
|
249
|
+
private onScheduleTimePointTimerFire;
|
|
250
|
+
/**
|
|
251
|
+
* 对单个商品 query 订阅者重算并推送(定时路径;不新建 schedule timer)。
|
|
252
|
+
*/
|
|
253
|
+
private recomputeAndNotifySubscriber;
|
|
254
|
+
/**
|
|
255
|
+
* query 完成后为 subscriber 同步 schedule 定时器(先 clear 再 schedule)。
|
|
256
|
+
*/
|
|
257
|
+
private syncProductQueryScheduleTimers;
|
|
214
258
|
/**
|
|
215
259
|
* 处理商品查询请求
|
|
216
260
|
* 存储订阅者信息,便于数据变更时推送最新结果
|
|
@@ -584,6 +628,12 @@ declare class Server {
|
|
|
584
628
|
* @param options.changedIds 变更的商品 IDs,用于增量更新价格缓存
|
|
585
629
|
*/
|
|
586
630
|
private computeProductQueryResult;
|
|
631
|
+
/**
|
|
632
|
+
* 构建商品格式化上下文。
|
|
633
|
+
*
|
|
634
|
+
* 智能定价条件使用全量 menuList/scheduleList;Product Query 顶层参数只保留筛选商品集合所需字段。
|
|
635
|
+
*/
|
|
636
|
+
private buildProductFormatterContext;
|
|
587
637
|
/**
|
|
588
638
|
* 数据变更后,遍历所有订阅者重新计算查询结果并通过 callback 推送
|
|
589
639
|
* 由 ProductsModule 的 onProductsSyncCompleted 事件触发
|
package/lib/server/index.js
CHANGED
|
@@ -78,6 +78,8 @@ var Server = class {
|
|
|
78
78
|
};
|
|
79
79
|
// ---- 商品查询订阅者 ----
|
|
80
80
|
this.productQuerySubscribers = /* @__PURE__ */ new Map();
|
|
81
|
+
/** subscriberId → 智能定价变价时间点定时器句柄(与 subscriber 分离存储,便于 clear) */
|
|
82
|
+
this.productQueryScheduleTimers = /* @__PURE__ */ new Map();
|
|
81
83
|
// ---- 订单 / 预约列表查询订阅者 ----
|
|
82
84
|
this.orderQuerySubscribers = /* @__PURE__ */ new Map();
|
|
83
85
|
this.bookingQuerySubscribers = /* @__PURE__ */ new Map();
|
|
@@ -211,7 +213,7 @@ var Server = class {
|
|
|
211
213
|
*/
|
|
212
214
|
this.handleProductQuery = async ({ url, method, data, config }) => {
|
|
213
215
|
console.log("[Server] handleProductQuery:", url, method, data, config);
|
|
214
|
-
const { menu_list_ids, schedule_datetime, schedule_date, customer_id, ids, extension_type } = data;
|
|
216
|
+
const { menu_list_ids, schedule_datetime, schedule_date, customer_id, ids, extension_type, strategy_context } = data;
|
|
215
217
|
const { callback, subscriberId } = config || {};
|
|
216
218
|
this.logInfo("handleProductQuery: 开始处理商品查询请求", {
|
|
217
219
|
menu_list_ids,
|
|
@@ -219,19 +221,32 @@ var Server = class {
|
|
|
219
221
|
schedule_datetime,
|
|
220
222
|
schedule_date,
|
|
221
223
|
customer_id,
|
|
222
|
-
extension_type
|
|
224
|
+
extension_type,
|
|
225
|
+
strategyContextKeys: strategy_context ? Object.keys(strategy_context) : []
|
|
223
226
|
});
|
|
224
227
|
if (subscriberId && typeof callback === "function") {
|
|
225
228
|
this.productQuerySubscribers.set(subscriberId, {
|
|
226
229
|
callback,
|
|
227
|
-
context: { menu_list_ids, ids, schedule_date, schedule_datetime, customer_id, extension_type }
|
|
230
|
+
context: { menu_list_ids, ids, schedule_date, schedule_datetime, customer_id, extension_type, strategy_context }
|
|
228
231
|
});
|
|
229
232
|
this.logInfo("handleProductQuery: 已注册订阅者", {
|
|
230
233
|
subscriberId,
|
|
231
234
|
totalSubscribers: this.productQuerySubscribers.size
|
|
232
235
|
});
|
|
233
236
|
}
|
|
234
|
-
|
|
237
|
+
const result = await this.computeProductQueryResult({
|
|
238
|
+
menu_list_ids,
|
|
239
|
+
ids,
|
|
240
|
+
schedule_date,
|
|
241
|
+
schedule_datetime,
|
|
242
|
+
customer_id,
|
|
243
|
+
extension_type,
|
|
244
|
+
strategy_context
|
|
245
|
+
});
|
|
246
|
+
if (subscriberId && typeof callback === "function") {
|
|
247
|
+
this.syncProductQueryScheduleTimers(subscriberId, strategy_context);
|
|
248
|
+
}
|
|
249
|
+
return result;
|
|
235
250
|
};
|
|
236
251
|
/**
|
|
237
252
|
* 按商品 id 查询单条(GET /shop/product/query/:productId)
|
|
@@ -2054,6 +2069,7 @@ var Server = class {
|
|
|
2054
2069
|
*/
|
|
2055
2070
|
removeProductQuerySubscriber(subscriberId) {
|
|
2056
2071
|
if (subscriberId) {
|
|
2072
|
+
this.clearSubscriberScheduleTimers(subscriberId);
|
|
2057
2073
|
this.productQuerySubscribers.delete(subscriberId);
|
|
2058
2074
|
this.logInfo("removeProductQuerySubscriber: 已移除订阅者", {
|
|
2059
2075
|
subscriberId,
|
|
@@ -2061,6 +2077,156 @@ var Server = class {
|
|
|
2061
2077
|
});
|
|
2062
2078
|
}
|
|
2063
2079
|
}
|
|
2080
|
+
/**
|
|
2081
|
+
* 构建商品 query 响应 data 段,附带智能定价 schedule_time_points 元数据。
|
|
2082
|
+
*/
|
|
2083
|
+
buildProductQueryResultPayload(list, count) {
|
|
2084
|
+
var _a, _b;
|
|
2085
|
+
return {
|
|
2086
|
+
list,
|
|
2087
|
+
count: count ?? list.length,
|
|
2088
|
+
schedule_time_points: ((_b = (_a = this.products) == null ? void 0 : _a.getLastScheduleTimePoints) == null ? void 0 : _b.call(_a)) ?? []
|
|
2089
|
+
};
|
|
2090
|
+
}
|
|
2091
|
+
/**
|
|
2092
|
+
* 是否启用商品 query 订阅的 schedule 时间点定时重算。
|
|
2093
|
+
* strategy_context.enable_schedule_reload === false 时关闭。
|
|
2094
|
+
*/
|
|
2095
|
+
shouldScheduleProductQueryReload(strategy_context) {
|
|
2096
|
+
return (strategy_context == null ? void 0 : strategy_context.enable_schedule_reload) !== false;
|
|
2097
|
+
}
|
|
2098
|
+
/**
|
|
2099
|
+
* 清除指定 subscriber 的全部 schedule 定时器,防止重复 query 或 unsubscribe 后泄漏。
|
|
2100
|
+
*/
|
|
2101
|
+
clearSubscriberScheduleTimers(subscriberId) {
|
|
2102
|
+
const timers = this.productQueryScheduleTimers.get(subscriberId);
|
|
2103
|
+
if (timers == null ? void 0 : timers.length) {
|
|
2104
|
+
timers.forEach((timerId) => clearTimeout(timerId));
|
|
2105
|
+
}
|
|
2106
|
+
this.productQueryScheduleTimers.delete(subscriberId);
|
|
2107
|
+
const subscriber = this.productQuerySubscribers.get(subscriberId);
|
|
2108
|
+
if (subscriber) {
|
|
2109
|
+
subscriber.scheduleTimerIds = [];
|
|
2110
|
+
}
|
|
2111
|
+
}
|
|
2112
|
+
/**
|
|
2113
|
+
* 从 productQueryScheduleTimers 移除已触发的 timer 引用。
|
|
2114
|
+
*/
|
|
2115
|
+
removeScheduleTimerRef(subscriberId, timerId) {
|
|
2116
|
+
const timers = this.productQueryScheduleTimers.get(subscriberId);
|
|
2117
|
+
if (!(timers == null ? void 0 : timers.length))
|
|
2118
|
+
return;
|
|
2119
|
+
const next = timers.filter((id) => id !== timerId);
|
|
2120
|
+
if (next.length === 0) {
|
|
2121
|
+
this.productQueryScheduleTimers.delete(subscriberId);
|
|
2122
|
+
} else {
|
|
2123
|
+
this.productQueryScheduleTimers.set(subscriberId, next);
|
|
2124
|
+
}
|
|
2125
|
+
const subscriber = this.productQuerySubscribers.get(subscriberId);
|
|
2126
|
+
if (subscriber) {
|
|
2127
|
+
subscriber.scheduleTimerIds = next;
|
|
2128
|
+
}
|
|
2129
|
+
}
|
|
2130
|
+
/**
|
|
2131
|
+
* 计算 schedule 变价时间点到当前时刻的延迟(ms)。已过期返回 -1。
|
|
2132
|
+
*/
|
|
2133
|
+
computeScheduleTimePointDelayMs(scheduleDate, timePoint) {
|
|
2134
|
+
const parts = timePoint.split(":");
|
|
2135
|
+
if (parts.length < 2)
|
|
2136
|
+
return -1;
|
|
2137
|
+
const hours = parseInt(parts[0], 10);
|
|
2138
|
+
const minutes = parseInt(parts[1], 10);
|
|
2139
|
+
if (!Number.isFinite(hours) || !Number.isFinite(minutes))
|
|
2140
|
+
return -1;
|
|
2141
|
+
const target = (0, import_dayjs.default)(scheduleDate).hour(hours).minute(minutes).second(4).millisecond(0);
|
|
2142
|
+
const delayMs = target.diff((0, import_dayjs.default)());
|
|
2143
|
+
return delayMs >= 0 ? delayMs : -1;
|
|
2144
|
+
}
|
|
2145
|
+
/**
|
|
2146
|
+
* 定时重算前刷新 subscriber schedule 上下文:使用执行瞬间时间,禁止沿用首次 query 的 schedule_datetime。
|
|
2147
|
+
*/
|
|
2148
|
+
refreshSubscriberScheduleAtExecution(subscriber) {
|
|
2149
|
+
const executedAt = (0, import_dayjs.default)();
|
|
2150
|
+
const bookingDate = subscriber.context.schedule_date || executedAt.format("YYYY-MM-DD");
|
|
2151
|
+
subscriber.context.schedule_date = bookingDate;
|
|
2152
|
+
subscriber.context.schedule_datetime = `${bookingDate} ${executedAt.format("HH:mm:ss")}`;
|
|
2153
|
+
}
|
|
2154
|
+
/**
|
|
2155
|
+
* 为 subscriber 注册智能定价 scheduleTimePoints 定时重算。
|
|
2156
|
+
* 必须先 clearSubscriberScheduleTimers,再调用本方法。
|
|
2157
|
+
*/
|
|
2158
|
+
scheduleSubscriberTimePointReloads(subscriberId, timePoints) {
|
|
2159
|
+
const subscriber = this.productQuerySubscribers.get(subscriberId);
|
|
2160
|
+
if (!subscriber || !timePoints.length)
|
|
2161
|
+
return;
|
|
2162
|
+
const scheduleDate = subscriber.context.schedule_date || (0, import_dayjs.default)().format("YYYY-MM-DD");
|
|
2163
|
+
const timerIds = [];
|
|
2164
|
+
for (const timePoint of timePoints) {
|
|
2165
|
+
const delayMs = this.computeScheduleTimePointDelayMs(scheduleDate, timePoint);
|
|
2166
|
+
if (delayMs < 0)
|
|
2167
|
+
continue;
|
|
2168
|
+
const timerId = setTimeout(() => {
|
|
2169
|
+
void this.onScheduleTimePointTimerFire(subscriberId, timerId);
|
|
2170
|
+
}, delayMs);
|
|
2171
|
+
timerIds.push(timerId);
|
|
2172
|
+
}
|
|
2173
|
+
if (timerIds.length > 0) {
|
|
2174
|
+
this.productQueryScheduleTimers.set(subscriberId, timerIds);
|
|
2175
|
+
subscriber.scheduleTimerIds = timerIds;
|
|
2176
|
+
this.logInfo("scheduleSubscriberTimePointReloads: 已注册定时器", {
|
|
2177
|
+
subscriberId,
|
|
2178
|
+
timerCount: timerIds.length,
|
|
2179
|
+
timePoints
|
|
2180
|
+
});
|
|
2181
|
+
}
|
|
2182
|
+
}
|
|
2183
|
+
/**
|
|
2184
|
+
* schedule 变价时间点定时器触发:刷新 schedule_datetime 后重算并 callback。
|
|
2185
|
+
*/
|
|
2186
|
+
async onScheduleTimePointTimerFire(subscriberId, timerId) {
|
|
2187
|
+
this.removeScheduleTimerRef(subscriberId, timerId);
|
|
2188
|
+
if (!this.productQuerySubscribers.has(subscriberId))
|
|
2189
|
+
return;
|
|
2190
|
+
const subscriber = this.productQuerySubscribers.get(subscriberId);
|
|
2191
|
+
this.refreshSubscriberScheduleAtExecution(subscriber);
|
|
2192
|
+
await this.recomputeAndNotifySubscriber(subscriberId);
|
|
2193
|
+
}
|
|
2194
|
+
/**
|
|
2195
|
+
* 对单个商品 query 订阅者重算并推送(定时路径;不新建 schedule timer)。
|
|
2196
|
+
*/
|
|
2197
|
+
async recomputeAndNotifySubscriber(subscriberId) {
|
|
2198
|
+
const subscriber = this.productQuerySubscribers.get(subscriberId);
|
|
2199
|
+
if (!subscriber)
|
|
2200
|
+
return;
|
|
2201
|
+
try {
|
|
2202
|
+
const result = await this.computeProductQueryResult(subscriber.context);
|
|
2203
|
+
subscriber.callback(result);
|
|
2204
|
+
this.logInfo("recomputeAndNotifySubscriber: 已推送", {
|
|
2205
|
+
subscriberId,
|
|
2206
|
+
schedule_datetime: subscriber.context.schedule_datetime
|
|
2207
|
+
});
|
|
2208
|
+
} catch (error) {
|
|
2209
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
2210
|
+
this.logError("recomputeAndNotifySubscriber: 推送失败", {
|
|
2211
|
+
subscriberId,
|
|
2212
|
+
error: errorMessage
|
|
2213
|
+
});
|
|
2214
|
+
}
|
|
2215
|
+
}
|
|
2216
|
+
/**
|
|
2217
|
+
* query 完成后为 subscriber 同步 schedule 定时器(先 clear 再 schedule)。
|
|
2218
|
+
*/
|
|
2219
|
+
syncProductQueryScheduleTimers(subscriberId, strategy_context) {
|
|
2220
|
+
var _a, _b;
|
|
2221
|
+
this.clearSubscriberScheduleTimers(subscriberId);
|
|
2222
|
+
if (!this.shouldScheduleProductQueryReload(strategy_context)) {
|
|
2223
|
+
return;
|
|
2224
|
+
}
|
|
2225
|
+
const timePoints = ((_b = (_a = this.products) == null ? void 0 : _a.getLastScheduleTimePoints) == null ? void 0 : _b.call(_a)) ?? [];
|
|
2226
|
+
if (timePoints.length > 0) {
|
|
2227
|
+
this.scheduleSubscriberTimePointReloads(subscriberId, timePoints);
|
|
2228
|
+
}
|
|
2229
|
+
}
|
|
2064
2230
|
/**
|
|
2065
2231
|
* 执行 sales 搜索并登记可见订单号。
|
|
2066
2232
|
* 搜索参数完全透传后端,OS 不做过滤、筛选、排序。
|
|
@@ -4707,8 +4873,14 @@ var Server = class {
|
|
|
4707
4873
|
*/
|
|
4708
4874
|
async computeProductQueryResult(context, options) {
|
|
4709
4875
|
const tTotal = performance.now();
|
|
4710
|
-
const { menu_list_ids, ids, schedule_date, schedule_datetime, customer_id, extension_type, product_id } = context;
|
|
4876
|
+
const { menu_list_ids, ids, schedule_date, schedule_datetime, customer_id, extension_type, product_id, strategy_context } = context;
|
|
4711
4877
|
const queryIds = Array.isArray(ids) ? ids.map((id) => Number(id)).filter((id) => Number.isFinite(id)) : [];
|
|
4878
|
+
const formatterContext = this.buildProductFormatterContext({
|
|
4879
|
+
schedule_date,
|
|
4880
|
+
schedule_datetime,
|
|
4881
|
+
customer_id,
|
|
4882
|
+
strategy_context
|
|
4883
|
+
});
|
|
4712
4884
|
this.logInfo("computeProductQueryResult 开始", {
|
|
4713
4885
|
menuListIdsCount: (menu_list_ids == null ? void 0 : menu_list_ids.length) ?? 0,
|
|
4714
4886
|
ids: queryIds,
|
|
@@ -4717,20 +4889,17 @@ var Server = class {
|
|
|
4717
4889
|
customer_id,
|
|
4718
4890
|
extension_type,
|
|
4719
4891
|
product_id,
|
|
4892
|
+
strategyContextKeys: strategy_context ? Object.keys(strategy_context) : [],
|
|
4720
4893
|
changedIds: options == null ? void 0 : options.changedIds
|
|
4721
4894
|
});
|
|
4722
4895
|
if (!this.products) {
|
|
4723
4896
|
this.logError("computeProductQueryResult: Products 模块未注册");
|
|
4724
|
-
return { message: "Products 模块未注册", data:
|
|
4897
|
+
return { message: "Products 模块未注册", data: this.buildProductQueryResultPayload([], 0) };
|
|
4725
4898
|
}
|
|
4726
4899
|
if (queryIds.length > 0) {
|
|
4727
4900
|
const uniqueIds = Array.from(new Set(queryIds));
|
|
4728
4901
|
const tPrice2 = performance.now();
|
|
4729
|
-
const productsWithPrice = await this.products.getProductsWithPrice(schedule_date, {
|
|
4730
|
-
scheduleModule: this.getSchedule(),
|
|
4731
|
-
schedule_datetime,
|
|
4732
|
-
customer_id
|
|
4733
|
-
}, {
|
|
4902
|
+
const productsWithPrice = await this.products.getProductsWithPrice(schedule_date, formatterContext, {
|
|
4734
4903
|
changedIds: options == null ? void 0 : options.changedIds,
|
|
4735
4904
|
productIds: uniqueIds
|
|
4736
4905
|
});
|
|
@@ -4751,7 +4920,7 @@ var Server = class {
|
|
|
4751
4920
|
});
|
|
4752
4921
|
return {
|
|
4753
4922
|
code: 200,
|
|
4754
|
-
data:
|
|
4923
|
+
data: this.buildProductQueryResultPayload(extensionFilteredProducts),
|
|
4755
4924
|
message: "",
|
|
4756
4925
|
status: true
|
|
4757
4926
|
};
|
|
@@ -4759,9 +4928,7 @@ var Server = class {
|
|
|
4759
4928
|
if (product_id != null && Number.isFinite(Number(product_id))) {
|
|
4760
4929
|
const pid = Number(product_id);
|
|
4761
4930
|
const tPrice2 = performance.now();
|
|
4762
|
-
const allProductsWithPrice = await this.products.getProductsWithPrice(schedule_date, {
|
|
4763
|
-
scheduleModule: this.getSchedule()
|
|
4764
|
-
}, {
|
|
4931
|
+
const allProductsWithPrice = await this.products.getProductsWithPrice(schedule_date, formatterContext, {
|
|
4765
4932
|
changedIds: options == null ? void 0 : options.changedIds
|
|
4766
4933
|
});
|
|
4767
4934
|
(0, import_product.perfMark)("computeQuery.getProductsWithPrice(single)", performance.now() - tPrice2, {
|
|
@@ -4791,11 +4958,7 @@ var Server = class {
|
|
|
4791
4958
|
}
|
|
4792
4959
|
if (this.shouldQueryProductsByExtensionTypes(extension_type)) {
|
|
4793
4960
|
const tPrice2 = performance.now();
|
|
4794
|
-
let filteredProducts2 = await this.products.getProductsWithPrice(schedule_date, {
|
|
4795
|
-
scheduleModule: this.getSchedule(),
|
|
4796
|
-
schedule_datetime,
|
|
4797
|
-
customer_id
|
|
4798
|
-
}, {
|
|
4961
|
+
let filteredProducts2 = await this.products.getProductsWithPrice(schedule_date, formatterContext, {
|
|
4799
4962
|
changedIds: options == null ? void 0 : options.changedIds
|
|
4800
4963
|
});
|
|
4801
4964
|
(0, import_product.perfMark)("computeQuery.getProductsWithPrice(extensionType)", performance.now() - tPrice2, {
|
|
@@ -4835,18 +4998,18 @@ var Server = class {
|
|
|
4835
4998
|
});
|
|
4836
4999
|
return {
|
|
4837
5000
|
code: 200,
|
|
4838
|
-
data:
|
|
5001
|
+
data: this.buildProductQueryResultPayload(filteredProducts2),
|
|
4839
5002
|
message: "",
|
|
4840
5003
|
status: true
|
|
4841
5004
|
};
|
|
4842
5005
|
}
|
|
4843
5006
|
if (!this.menu) {
|
|
4844
5007
|
this.logError("computeProductQueryResult: Menu 模块未注册");
|
|
4845
|
-
return { message: "Menu 模块未注册", data:
|
|
5008
|
+
return { message: "Menu 模块未注册", data: this.buildProductQueryResultPayload([], 0) };
|
|
4846
5009
|
}
|
|
4847
5010
|
if (!this.schedule) {
|
|
4848
5011
|
this.logError("computeProductQueryResult: Schedule 模块未注册");
|
|
4849
|
-
return { message: "Schedule 模块未注册", data:
|
|
5012
|
+
return { message: "Schedule 模块未注册", data: this.buildProductQueryResultPayload([], 0) };
|
|
4850
5013
|
}
|
|
4851
5014
|
let activeMenuList = [];
|
|
4852
5015
|
if (menu_list_ids && Array.isArray(menu_list_ids) && menu_list_ids.length > 0) {
|
|
@@ -4870,11 +5033,7 @@ var Server = class {
|
|
|
4870
5033
|
isAllProducts: !!productScope.isAllProducts
|
|
4871
5034
|
});
|
|
4872
5035
|
const tPrice = performance.now();
|
|
4873
|
-
let filteredProducts = scopedProductIds.length > 0 ? await this.products.getProductsWithPrice(schedule_date, {
|
|
4874
|
-
scheduleModule: this.getSchedule(),
|
|
4875
|
-
schedule_datetime,
|
|
4876
|
-
customer_id
|
|
4877
|
-
}, {
|
|
5036
|
+
let filteredProducts = scopedProductIds.length > 0 ? await this.products.getProductsWithPrice(schedule_date, formatterContext, {
|
|
4878
5037
|
changedIds: options == null ? void 0 : options.changedIds,
|
|
4879
5038
|
productIds: productScope.isAllProducts ? void 0 : scopedProductIds
|
|
4880
5039
|
}) : [];
|
|
@@ -4917,11 +5076,29 @@ var Server = class {
|
|
|
4917
5076
|
});
|
|
4918
5077
|
return {
|
|
4919
5078
|
code: 200,
|
|
4920
|
-
data:
|
|
5079
|
+
data: this.buildProductQueryResultPayload(filteredProducts),
|
|
4921
5080
|
message: "",
|
|
4922
5081
|
status: true
|
|
4923
5082
|
};
|
|
4924
5083
|
}
|
|
5084
|
+
/**
|
|
5085
|
+
* 构建商品格式化上下文。
|
|
5086
|
+
*
|
|
5087
|
+
* 智能定价条件使用全量 menuList/scheduleList;Product Query 顶层参数只保留筛选商品集合所需字段。
|
|
5088
|
+
*/
|
|
5089
|
+
buildProductFormatterContext(context) {
|
|
5090
|
+
var _a, _b, _c, _d;
|
|
5091
|
+
const scheduleModule = this.getSchedule();
|
|
5092
|
+
return {
|
|
5093
|
+
scheduleModule,
|
|
5094
|
+
schedule_datetime: context.schedule_datetime,
|
|
5095
|
+
customer_id: context.customer_id,
|
|
5096
|
+
strategy_context: context.strategy_context,
|
|
5097
|
+
menuList: ((_b = (_a = this.menu) == null ? void 0 : _a.getMenuList) == null ? void 0 : _b.call(_a)) || [],
|
|
5098
|
+
scheduleList: ((_c = scheduleModule == null ? void 0 : scheduleModule.getScheduleList) == null ? void 0 : _c.call(scheduleModule)) || [],
|
|
5099
|
+
dataVariantEvaluator: (_d = this.core.context) == null ? void 0 : _d.dataVariantEvaluator
|
|
5100
|
+
};
|
|
5101
|
+
}
|
|
4925
5102
|
/**
|
|
4926
5103
|
* 数据变更后,遍历所有订阅者重新计算查询结果并通过 callback 推送
|
|
4927
5104
|
* 由 ProductsModule 的 onProductsSyncCompleted 事件触发
|
|
@@ -123,6 +123,9 @@ export declare class OrderModule extends BaseModule implements Module {
|
|
|
123
123
|
*/
|
|
124
124
|
silentRefresh(): Promise<OrderData[]>;
|
|
125
125
|
private normalizeOrderForMemoryList;
|
|
126
|
+
private normalizeOrdersForMemoryList;
|
|
127
|
+
private hasRealOrderId;
|
|
128
|
+
private shouldKeepOrderInMemory;
|
|
126
129
|
private withoutSyntheticDraftOrderId;
|
|
127
130
|
/**
|
|
128
131
|
* 仅覆盖本地已存在的订单;不存在则直接跳过,不落库、不 emit。
|
|
@@ -151,8 +154,8 @@ export declare class OrderModule extends BaseModule implements Module {
|
|
|
151
154
|
throwOnError?: boolean;
|
|
152
155
|
}): Promise<void>;
|
|
153
156
|
/**
|
|
154
|
-
* 将 POS 草稿订单按 SQLite storage key
|
|
155
|
-
*
|
|
157
|
+
* 将 POS 草稿订单按 SQLite storage key 落库。
|
|
158
|
+
* 只有具备真实 order_id 的草稿进入 store;其余草稿仅供本地恢复查询。
|
|
156
159
|
*/
|
|
157
160
|
upsertLocalDraftOrders(draftOrders: OrderData[]): Promise<void>;
|
|
158
161
|
/**
|
|
@@ -276,6 +279,7 @@ export declare class OrderModule extends BaseModule implements Module {
|
|
|
276
279
|
private isDraftOrder;
|
|
277
280
|
private shouldProtectDraftOrderFromIncoming;
|
|
278
281
|
private normalizeRemoteSyncedOrder;
|
|
282
|
+
private mergeRemoteIdentityIntoDraftOrder;
|
|
279
283
|
private mergeRemoteSnapshotWithPendingOrders;
|
|
280
284
|
/**
|
|
281
285
|
* 串行执行订单 SQLite 写入任务,避免快照替换与增量 upsert 并发交错。
|
|
@@ -301,7 +305,8 @@ export declare class OrderModule extends BaseModule implements Module {
|
|
|
301
305
|
*/
|
|
302
306
|
private updateOrderInSQLite;
|
|
303
307
|
/**
|
|
304
|
-
* 全量快照替换 orders 表(clear + bulkAdd
|
|
308
|
+
* 全量快照替换 orders 表(clear + bulkAdd),并保留本地 draft / need_sync 订单。
|
|
309
|
+
* 用于 SSE 全量拉取与营业日窗口裁剪。
|
|
305
310
|
*
|
|
306
311
|
* @example
|
|
307
312
|
* await this.replaceOrdersSnapshotInSQLite(orderList)
|