@pisell/pisellos 2.2.204 → 2.2.206
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/promotion/index.js +0 -9
- package/dist/modules/OpenData/index.d.ts +1 -0
- package/dist/modules/OpenData/index.js +21 -16
- package/dist/modules/Order/index.d.ts +9 -0
- package/dist/modules/Order/index.js +237 -146
- package/dist/modules/Order/types.d.ts +10 -0
- package/dist/modules/Order/utils.js +7 -5
- package/dist/modules/Payment/index.d.ts +0 -13
- package/dist/modules/Payment/index.js +449 -833
- package/dist/plugins/request.d.ts +1 -0
- package/dist/server/index.d.ts +142 -0
- package/dist/server/index.js +2208 -824
- package/dist/server/modules/index.d.ts +2 -0
- package/dist/server/modules/index.js +2 -0
- package/dist/server/modules/order/index.d.ts +7 -0
- package/dist/server/modules/order/index.js +375 -337
- package/dist/server/modules/order/types.d.ts +15 -0
- package/dist/server/modules/order/types.js +8 -0
- package/dist/server/modules/payment/index.d.ts +28 -0
- package/dist/server/modules/payment/index.js +305 -0
- package/dist/server/modules/payment/types.d.ts +9 -0
- package/dist/server/modules/payment/types.js +1 -0
- package/dist/server/utils/small-ticket.d.ts +69 -0
- package/dist/server/utils/small-ticket.js +528 -0
- package/dist/solution/BaseSales/index.d.ts +10 -3
- package/dist/solution/BaseSales/index.js +307 -198
- package/dist/solution/BookingByStep/index.d.ts +1 -1
- package/dist/solution/BookingTicket/index.d.ts +8 -1
- package/dist/solution/BookingTicket/index.js +444 -264
- package/dist/solution/BookingTicket/types.d.ts +2 -1
- package/dist/solution/BookingTicket/types.js +3 -1
- package/dist/solution/BookingTicket/utils/scan/applyGlobalScan.js +51 -52
- package/dist/solution/BookingTicket/utils/scan/index.d.ts +5 -0
- package/dist/solution/BookingTicket/utils/scan/index.js +20 -8
- package/lib/model/strategy/adapter/promotion/index.js +49 -0
- package/lib/modules/OpenData/index.d.ts +1 -0
- package/lib/modules/OpenData/index.js +6 -4
- package/lib/modules/Order/index.d.ts +9 -0
- package/lib/modules/Order/index.js +64 -4
- package/lib/modules/Order/types.d.ts +10 -0
- package/lib/modules/Order/utils.js +4 -3
- package/lib/modules/Payment/index.d.ts +0 -13
- package/lib/modules/Payment/index.js +6 -149
- package/lib/plugins/request.d.ts +1 -0
- package/lib/server/index.d.ts +142 -0
- package/lib/server/index.js +867 -49
- package/lib/server/modules/index.d.ts +2 -0
- package/lib/server/modules/index.js +3 -0
- package/lib/server/modules/order/index.d.ts +7 -0
- package/lib/server/modules/order/index.js +21 -7
- package/lib/server/modules/order/types.d.ts +15 -0
- package/lib/server/modules/payment/index.d.ts +28 -0
- package/lib/server/modules/payment/index.js +192 -0
- package/lib/server/modules/payment/types.d.ts +9 -0
- package/lib/server/modules/payment/types.js +17 -0
- package/lib/server/utils/small-ticket.d.ts +69 -0
- package/lib/server/utils/small-ticket.js +555 -0
- package/lib/solution/BaseSales/index.d.ts +10 -3
- package/lib/solution/BaseSales/index.js +98 -6
- package/lib/solution/BookingByStep/index.d.ts +1 -1
- package/lib/solution/BookingTicket/index.d.ts +8 -1
- package/lib/solution/BookingTicket/index.js +109 -2
- package/lib/solution/BookingTicket/types.d.ts +2 -1
- package/lib/solution/BookingTicket/types.js +5 -0
- package/lib/solution/BookingTicket/utils/scan/applyGlobalScan.js +1 -2
- package/lib/solution/BookingTicket/utils/scan/index.d.ts +5 -0
- package/lib/solution/BookingTicket/utils/scan/index.js +17 -3
- package/package.json +1 -1
package/lib/server/index.js
CHANGED
|
@@ -41,6 +41,7 @@ var import_schedule = require("./modules/schedule");
|
|
|
41
41
|
var import_resource = require("./modules/resource");
|
|
42
42
|
var import_floor_plan = require("./modules/floor-plan");
|
|
43
43
|
var import_types = require("./modules/floor-plan/types");
|
|
44
|
+
var import_payment = require("./modules/payment");
|
|
44
45
|
var import_schedule2 = require("./utils/schedule");
|
|
45
46
|
var import_types2 = require("./modules/products/types");
|
|
46
47
|
var import_product = require("./utils/product");
|
|
@@ -48,6 +49,7 @@ var import_order = require("./modules/order");
|
|
|
48
49
|
var import_types3 = require("./modules/order/types");
|
|
49
50
|
var import_filterOrders = require("./modules/order/utils/filterOrders");
|
|
50
51
|
var import_filterBookings = require("./modules/order/utils/filterBookings");
|
|
52
|
+
var import_small_ticket = require("./utils/small-ticket");
|
|
51
53
|
__reExport(server_exports, require("./modules"), module.exports);
|
|
52
54
|
var Server = class {
|
|
53
55
|
constructor(core) {
|
|
@@ -69,6 +71,8 @@ var Server = class {
|
|
|
69
71
|
this.bookingRemoteCache = /* @__PURE__ */ new Map();
|
|
70
72
|
this.BOOKING_REMOTE_CACHE_TTL_MS = 5 * 60 * 1e3;
|
|
71
73
|
this.BOOKING_REMOTE_CACHE_MAX_ENTRIES = 80;
|
|
74
|
+
this.salesSearchSubscribers = /* @__PURE__ */ new Map();
|
|
75
|
+
this.deviceTaskActionsRegistered = false;
|
|
72
76
|
this.floorPlanQuerySubscribers = /* @__PURE__ */ new Map();
|
|
73
77
|
// 模块注册表 - 定义所有可用的模块配置
|
|
74
78
|
this.moduleRegistry = {
|
|
@@ -120,6 +124,15 @@ var Server = class {
|
|
|
120
124
|
list: []
|
|
121
125
|
}
|
|
122
126
|
},
|
|
127
|
+
payment: {
|
|
128
|
+
name: "payment",
|
|
129
|
+
moduleClass: import_payment.PaymentServerModule,
|
|
130
|
+
moduleName: "server_payment",
|
|
131
|
+
version: "1.0.0",
|
|
132
|
+
defaultStore: {
|
|
133
|
+
payMethods: []
|
|
134
|
+
}
|
|
135
|
+
},
|
|
123
136
|
resource: {
|
|
124
137
|
name: "resource",
|
|
125
138
|
moduleClass: import_resource.ResourceModule,
|
|
@@ -143,6 +156,17 @@ var Server = class {
|
|
|
143
156
|
}
|
|
144
157
|
}
|
|
145
158
|
};
|
|
159
|
+
this.handlePaymentMethods = async ({ data, config }) => {
|
|
160
|
+
const payment = await this.getPaymentRouteModule();
|
|
161
|
+
const payMethods = await payment.getPayMethodListAsync({
|
|
162
|
+
shouldFilter: (data == null ? void 0 : data.filterPaymentMethods) === true || (config == null ? void 0 : config.filterPaymentMethods) === true
|
|
163
|
+
});
|
|
164
|
+
return {
|
|
165
|
+
code: 200,
|
|
166
|
+
status: true,
|
|
167
|
+
data: payMethods
|
|
168
|
+
};
|
|
169
|
+
};
|
|
146
170
|
/**
|
|
147
171
|
* 处理商品查询请求
|
|
148
172
|
* 存储订阅者信息,便于数据变更时推送最新结果
|
|
@@ -270,6 +294,41 @@ var Server = class {
|
|
|
270
294
|
}
|
|
271
295
|
return { code: 200, message: "ok", status: true };
|
|
272
296
|
};
|
|
297
|
+
/**
|
|
298
|
+
* 处理 sales 单模块搜索:请求透传后端,仅在 OS 层维护已返回订单号集合。
|
|
299
|
+
*
|
|
300
|
+
* @example
|
|
301
|
+
* await this.handleSalesSearchModule({ url: '/shop/es/search/sales', method: 'post', data: { keyword: 'A' } })
|
|
302
|
+
*/
|
|
303
|
+
this.handleSalesSearchModule = async ({ data, config }) => {
|
|
304
|
+
return this.handleSalesSearchRequest("single", "/shop/es/search/sales", data, config);
|
|
305
|
+
};
|
|
306
|
+
/**
|
|
307
|
+
* 处理 sales 聚合搜索:请求透传后端,仅订阅 sales 分组的已返回订单号。
|
|
308
|
+
*
|
|
309
|
+
* @example
|
|
310
|
+
* await this.handleSalesSearchAggregate({ url: '/shop/es/search', method: 'post', data: { modules: [] } })
|
|
311
|
+
*/
|
|
312
|
+
this.handleSalesSearchAggregate = async ({ data, config }) => {
|
|
313
|
+
return this.handleSalesSearchRequest("aggregate", "/shop/es/search", data, config);
|
|
314
|
+
};
|
|
315
|
+
/**
|
|
316
|
+
* 取消 sales 搜索订阅。
|
|
317
|
+
*
|
|
318
|
+
* @example
|
|
319
|
+
* await this.handleUnsubscribeSalesSearch({ data: { subscriberId: 'global-search-order' } } as any)
|
|
320
|
+
*/
|
|
321
|
+
this.handleUnsubscribeSalesSearch = async ({ data }) => {
|
|
322
|
+
const { subscriberId } = data || {};
|
|
323
|
+
if (subscriberId) {
|
|
324
|
+
this.salesSearchSubscribers.delete(subscriberId);
|
|
325
|
+
this.logInfo("handleUnsubscribeSalesSearch: 已移除订阅者", {
|
|
326
|
+
subscriberId,
|
|
327
|
+
remaining: this.salesSearchSubscribers.size
|
|
328
|
+
});
|
|
329
|
+
}
|
|
330
|
+
return { code: 200, message: "ok", status: true };
|
|
331
|
+
};
|
|
273
332
|
/**
|
|
274
333
|
* 日历(compact_fields)场景下要直接在 SSE 层排除的订单类型:
|
|
275
334
|
* 这些类型不会出现在日历上,拉取它们只会浪费带宽并增加前端过滤成本。
|
|
@@ -466,6 +525,54 @@ var Server = class {
|
|
|
466
525
|
title: "handleOrderCheckout"
|
|
467
526
|
});
|
|
468
527
|
};
|
|
528
|
+
this.handleLocalPrintOrder = async ({ data }) => {
|
|
529
|
+
try {
|
|
530
|
+
const inputOrder = (data == null ? void 0 : data.order) && typeof data.order === "object" ? data.order : data;
|
|
531
|
+
if (!inputOrder || typeof inputOrder !== "object") {
|
|
532
|
+
return {
|
|
533
|
+
code: 400,
|
|
534
|
+
status: false,
|
|
535
|
+
message: "订单数据不能为空",
|
|
536
|
+
data: { printed: false, order: null }
|
|
537
|
+
};
|
|
538
|
+
}
|
|
539
|
+
const checkoutData = await this.ensureCheckoutOrderNumbers(
|
|
540
|
+
{
|
|
541
|
+
...inputOrder,
|
|
542
|
+
small_ticket_data_flag: 1
|
|
543
|
+
},
|
|
544
|
+
"handleLocalPrintOrder"
|
|
545
|
+
);
|
|
546
|
+
const printableOrder = {
|
|
547
|
+
...checkoutData,
|
|
548
|
+
need_sync: checkoutData.need_sync ?? 1
|
|
549
|
+
};
|
|
550
|
+
const printResult = await this.dispatchLocalReceiptPrint({
|
|
551
|
+
checkoutData,
|
|
552
|
+
order: printableOrder,
|
|
553
|
+
response: null,
|
|
554
|
+
requireFullyPaid: false
|
|
555
|
+
});
|
|
556
|
+
return {
|
|
557
|
+
code: 200,
|
|
558
|
+
status: true,
|
|
559
|
+
message: "",
|
|
560
|
+
data: {
|
|
561
|
+
printed: printResult.printed,
|
|
562
|
+
order: printResult.order
|
|
563
|
+
}
|
|
564
|
+
};
|
|
565
|
+
} catch (error) {
|
|
566
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
567
|
+
this.logError("handleLocalPrintOrder: 本地打印小票失败", { error: message });
|
|
568
|
+
return {
|
|
569
|
+
code: 500,
|
|
570
|
+
status: false,
|
|
571
|
+
message,
|
|
572
|
+
data: { printed: false, order: null }
|
|
573
|
+
};
|
|
574
|
+
}
|
|
575
|
+
};
|
|
469
576
|
this.handleOrderSalesDetail = async ({ url, data, path }) => {
|
|
470
577
|
var _a;
|
|
471
578
|
const requestPath = this.parseRequestPath(url, path);
|
|
@@ -912,6 +1019,7 @@ var Server = class {
|
|
|
912
1019
|
moduleConfigs
|
|
913
1020
|
});
|
|
914
1021
|
this.registerServerRoutes();
|
|
1022
|
+
this.registerDeviceTaskActions();
|
|
915
1023
|
const registeredModules = await this.registerModules(moduleConfigs);
|
|
916
1024
|
if (autoPreload) {
|
|
917
1025
|
await this.preloadModulesData(registeredModules, options);
|
|
@@ -927,10 +1035,11 @@ var Server = class {
|
|
|
927
1035
|
this.core.effects.on(import_types.FloorPlanHooks.onFloorPlansChanged, () => {
|
|
928
1036
|
void this.recomputeAndNotifyFloorPlanQuery();
|
|
929
1037
|
});
|
|
930
|
-
this.core.effects.on(import_types3.OrderHooks.onOrdersChanged, () => {
|
|
1038
|
+
this.core.effects.on(import_types3.OrderHooks.onOrdersChanged, (payload) => {
|
|
931
1039
|
this.recomputeAndNotifyOrderQuery();
|
|
932
1040
|
this.recomputeAndNotifyBookingQuery();
|
|
933
1041
|
this.syncBookingRemoteCacheFromOrderChanges();
|
|
1042
|
+
this.notifySalesSearchSubscribers(payload);
|
|
934
1043
|
});
|
|
935
1044
|
const duration = Date.now() - startTime;
|
|
936
1045
|
this.logInfo("Server 初始化完成", {
|
|
@@ -939,6 +1048,114 @@ var Server = class {
|
|
|
939
1048
|
});
|
|
940
1049
|
return registeredModules;
|
|
941
1050
|
}
|
|
1051
|
+
registerDeviceTaskActions() {
|
|
1052
|
+
if (this.deviceTaskActionsRegistered)
|
|
1053
|
+
return;
|
|
1054
|
+
const deviceTask = this.getDeviceTaskPlugin();
|
|
1055
|
+
if (!(deviceTask == null ? void 0 : deviceTask.registerAction)) {
|
|
1056
|
+
this.logWarning("registerDeviceTaskActions: deviceTask 插件不可用");
|
|
1057
|
+
return;
|
|
1058
|
+
}
|
|
1059
|
+
const registeredActions = new Set(
|
|
1060
|
+
typeof deviceTask.getActions === "function" ? (deviceTask.getActions() || []).map((action) => action == null ? void 0 : action.action).filter(Boolean) : []
|
|
1061
|
+
);
|
|
1062
|
+
const register = (definition) => {
|
|
1063
|
+
var _a;
|
|
1064
|
+
if (registeredActions.has(definition.action))
|
|
1065
|
+
return;
|
|
1066
|
+
(_a = deviceTask.registerAction) == null ? void 0 : _a.call(deviceTask, definition);
|
|
1067
|
+
registeredActions.add(definition.action);
|
|
1068
|
+
};
|
|
1069
|
+
register({
|
|
1070
|
+
action: "sync_sales",
|
|
1071
|
+
name: "同步订单",
|
|
1072
|
+
validate: (payload) => (payload == null ? void 0 : payload.backendPath) && (payload == null ? void 0 : payload.data) ? null : "缺少 backendPath 或 data",
|
|
1073
|
+
handler: (ctx) => this.handleSyncSalesTask(ctx)
|
|
1074
|
+
});
|
|
1075
|
+
this.deviceTaskActionsRegistered = true;
|
|
1076
|
+
this.logInfo("DeviceTask actions 已注册", {
|
|
1077
|
+
actions: ["sync_sales"]
|
|
1078
|
+
});
|
|
1079
|
+
}
|
|
1080
|
+
async handleSyncSalesTask(ctx) {
|
|
1081
|
+
var _a, _b, _c;
|
|
1082
|
+
const payload = ((_a = ctx.task) == null ? void 0 : _a.payload) || {};
|
|
1083
|
+
const backendPath = payload.backendPath;
|
|
1084
|
+
const data = payload.data;
|
|
1085
|
+
const title = payload.title || "sync_sales";
|
|
1086
|
+
if (!backendPath || !data) {
|
|
1087
|
+
return this.taskFail("INVALID_PAYLOAD", "缺少 backendPath 或 data");
|
|
1088
|
+
}
|
|
1089
|
+
if (!((_b = this.app) == null ? void 0 : _b.request)) {
|
|
1090
|
+
this.logError(`${title}: sync_sales app.request 不可用`);
|
|
1091
|
+
return this.taskTimeout("app.request 不可用");
|
|
1092
|
+
}
|
|
1093
|
+
try {
|
|
1094
|
+
const response = await this.app.request.post("/shop/order/sales/checkout", data, {
|
|
1095
|
+
isShopApi: true,
|
|
1096
|
+
customToast: () => {
|
|
1097
|
+
}
|
|
1098
|
+
});
|
|
1099
|
+
if (this.isCheckoutResponseRejected(response)) {
|
|
1100
|
+
const message = (response == null ? void 0 : response.message) || "后端明确拒绝 checkout";
|
|
1101
|
+
this.logError(`${title}: sync_sales 后端明确失败`, {
|
|
1102
|
+
backendPath,
|
|
1103
|
+
response
|
|
1104
|
+
});
|
|
1105
|
+
return this.taskFail("BACKEND_REJECTED", message, response);
|
|
1106
|
+
}
|
|
1107
|
+
const fresh = this.extractOrderDataFromCheckoutResponse(response);
|
|
1108
|
+
if (!fresh) {
|
|
1109
|
+
this.logWarning(`${title}: sync_sales 返回未包含可同步订单`, {
|
|
1110
|
+
backendPath,
|
|
1111
|
+
response
|
|
1112
|
+
});
|
|
1113
|
+
return this.taskTimeout("checkout 返回未包含可同步订单");
|
|
1114
|
+
}
|
|
1115
|
+
const syncedOrder = { ...fresh, need_sync: 0 };
|
|
1116
|
+
if (this.order) {
|
|
1117
|
+
await this.order.upsertOrdersFromRemote([syncedOrder]);
|
|
1118
|
+
this.logInfo(`${title}: sync_sales 订单已同步到本地`, {
|
|
1119
|
+
order_id: fresh.order_id,
|
|
1120
|
+
external_sale_number: fresh.external_sale_number,
|
|
1121
|
+
order_number: fresh.order_number
|
|
1122
|
+
});
|
|
1123
|
+
} else {
|
|
1124
|
+
this.logWarning(`${title}: sync_sales Order 模块未注册`, {
|
|
1125
|
+
backendPath,
|
|
1126
|
+
order_id: fresh.order_id
|
|
1127
|
+
});
|
|
1128
|
+
}
|
|
1129
|
+
if (this.shouldPrintSyncedOrder({ checkoutData: data, syncedOrder })) {
|
|
1130
|
+
await this.dispatchPrintOtherReceiptTask({
|
|
1131
|
+
checkoutData: data,
|
|
1132
|
+
syncedOrder,
|
|
1133
|
+
response,
|
|
1134
|
+
deviceId: (_c = ctx.task) == null ? void 0 : _c.device_id
|
|
1135
|
+
});
|
|
1136
|
+
}
|
|
1137
|
+
return this.taskOk({
|
|
1138
|
+
order: syncedOrder,
|
|
1139
|
+
response: this.normalizeCheckoutResponse(response)
|
|
1140
|
+
});
|
|
1141
|
+
} catch (error) {
|
|
1142
|
+
const backendError = this.extractBackendErrorResponse(error);
|
|
1143
|
+
if (backendError) {
|
|
1144
|
+
const message = (backendError == null ? void 0 : backendError.message) || "后端明确拒绝 checkout";
|
|
1145
|
+
this.logError(`${title}: sync_sales 后端明确失败`, {
|
|
1146
|
+
backendPath,
|
|
1147
|
+
backendError
|
|
1148
|
+
});
|
|
1149
|
+
return this.taskFail("BACKEND_REJECTED", message, backendError);
|
|
1150
|
+
}
|
|
1151
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
1152
|
+
this.logError(`${title}: sync_sales 请求未明确成功`, {
|
|
1153
|
+
backendPath,
|
|
1154
|
+
error: errorMessage
|
|
1155
|
+
});
|
|
1156
|
+
return this.taskTimeout(errorMessage);
|
|
1157
|
+
}
|
|
1158
|
+
}
|
|
942
1159
|
/**
|
|
943
1160
|
* 将普通层 QuotationModule 的报价单计算能力桥接到 Server Products 模块。
|
|
944
1161
|
*/
|
|
@@ -1242,6 +1459,26 @@ var Server = class {
|
|
|
1242
1459
|
path: "/shop/order/v2/list/unsubscribe",
|
|
1243
1460
|
handler: this.handleUnsubscribeOrderQuery.bind(this)
|
|
1244
1461
|
},
|
|
1462
|
+
{
|
|
1463
|
+
method: "post",
|
|
1464
|
+
path: "/shop/es/search/sales",
|
|
1465
|
+
handler: this.handleSalesSearchModule.bind(this)
|
|
1466
|
+
},
|
|
1467
|
+
{
|
|
1468
|
+
method: "post",
|
|
1469
|
+
path: "/shop/es/search/sales/unsubscribe",
|
|
1470
|
+
handler: this.handleUnsubscribeSalesSearch.bind(this)
|
|
1471
|
+
},
|
|
1472
|
+
{
|
|
1473
|
+
method: "post",
|
|
1474
|
+
path: "/shop/es/search",
|
|
1475
|
+
handler: this.handleSalesSearchAggregate.bind(this)
|
|
1476
|
+
},
|
|
1477
|
+
{
|
|
1478
|
+
method: "post",
|
|
1479
|
+
path: "/shop/es/search/unsubscribe",
|
|
1480
|
+
handler: this.handleUnsubscribeSalesSearch.bind(this)
|
|
1481
|
+
},
|
|
1245
1482
|
{
|
|
1246
1483
|
method: "get",
|
|
1247
1484
|
path: "/shop/schedule/booking",
|
|
@@ -1262,6 +1499,11 @@ var Server = class {
|
|
|
1262
1499
|
path: "/update/localOrder",
|
|
1263
1500
|
handler: this.handleUpdateLocalOrder.bind(this)
|
|
1264
1501
|
},
|
|
1502
|
+
{
|
|
1503
|
+
method: "get",
|
|
1504
|
+
path: "/shop/pay/custom-payment/all",
|
|
1505
|
+
handler: this.handlePaymentMethods.bind(this)
|
|
1506
|
+
},
|
|
1265
1507
|
{
|
|
1266
1508
|
method: "post",
|
|
1267
1509
|
path: "/shop/order/sales/checkout",
|
|
@@ -1269,8 +1511,13 @@ var Server = class {
|
|
|
1269
1511
|
},
|
|
1270
1512
|
{
|
|
1271
1513
|
method: "post",
|
|
1272
|
-
path: "/
|
|
1514
|
+
path: "/order/checkout",
|
|
1273
1515
|
handler: this.handleOrderCheckout.bind(this)
|
|
1516
|
+
},
|
|
1517
|
+
{
|
|
1518
|
+
method: "post",
|
|
1519
|
+
path: "/local/print-order",
|
|
1520
|
+
handler: this.handleLocalPrintOrder.bind(this)
|
|
1274
1521
|
}
|
|
1275
1522
|
]);
|
|
1276
1523
|
this.registerPrefixRoutes([
|
|
@@ -1286,6 +1533,25 @@ var Server = class {
|
|
|
1286
1533
|
}
|
|
1287
1534
|
]);
|
|
1288
1535
|
}
|
|
1536
|
+
async getPaymentRouteModule() {
|
|
1537
|
+
if (this.payment)
|
|
1538
|
+
return this.payment;
|
|
1539
|
+
if (this.paymentRouteModule)
|
|
1540
|
+
return this.paymentRouteModule;
|
|
1541
|
+
if (this.paymentRouteModuleInFlight)
|
|
1542
|
+
return this.paymentRouteModuleInFlight;
|
|
1543
|
+
this.paymentRouteModuleInFlight = (async () => {
|
|
1544
|
+
const module2 = new import_payment.PaymentServerModule("server_payment_route", "1.0.0");
|
|
1545
|
+
await module2.initialize(this.core, {
|
|
1546
|
+
store: { payMethods: [] }
|
|
1547
|
+
});
|
|
1548
|
+
this.paymentRouteModule = module2;
|
|
1549
|
+
return module2;
|
|
1550
|
+
})().finally(() => {
|
|
1551
|
+
this.paymentRouteModuleInFlight = void 0;
|
|
1552
|
+
});
|
|
1553
|
+
return this.paymentRouteModuleInFlight;
|
|
1554
|
+
}
|
|
1289
1555
|
/**
|
|
1290
1556
|
* 根据 subscriberId 移除商品查询订阅者
|
|
1291
1557
|
*/
|
|
@@ -1298,6 +1564,162 @@ var Server = class {
|
|
|
1298
1564
|
});
|
|
1299
1565
|
}
|
|
1300
1566
|
}
|
|
1567
|
+
/**
|
|
1568
|
+
* 执行 sales 搜索并登记可见订单号。
|
|
1569
|
+
* 搜索参数完全透传后端,OS 不做过滤、筛选、排序。
|
|
1570
|
+
*
|
|
1571
|
+
* @example
|
|
1572
|
+
* const result = await this.handleSalesSearchRequest('single', '/shop/es/search/sales', { keyword: '1001' }, {})
|
|
1573
|
+
*/
|
|
1574
|
+
async handleSalesSearchRequest(kind, backendPath, data, config) {
|
|
1575
|
+
const queryPayload = data && typeof data === "object" ? { ...data } : {};
|
|
1576
|
+
const result = await this.fetchSalesSearchFromAPI(backendPath, queryPayload);
|
|
1577
|
+
const { callback, subscriberId, resetVisibleOrderIds } = config || {};
|
|
1578
|
+
if (subscriberId && typeof callback === "function") {
|
|
1579
|
+
const contextSignature = this.buildSalesSearchSignature(kind, queryPayload);
|
|
1580
|
+
const existing = this.salesSearchSubscribers.get(subscriberId);
|
|
1581
|
+
const shouldReset = resetVisibleOrderIds === true || !existing || existing.kind !== kind || existing.contextSignature !== contextSignature;
|
|
1582
|
+
const visibleOrderIds = shouldReset ? /* @__PURE__ */ new Set() : new Set(existing.visibleOrderIds);
|
|
1583
|
+
for (const orderId of this.extractSalesSearchOrderIds(kind, result)) {
|
|
1584
|
+
visibleOrderIds.add(orderId);
|
|
1585
|
+
}
|
|
1586
|
+
this.salesSearchSubscribers.set(subscriberId, {
|
|
1587
|
+
callback,
|
|
1588
|
+
kind,
|
|
1589
|
+
contextSignature,
|
|
1590
|
+
visibleOrderIds,
|
|
1591
|
+
lastResponseData: this.extractSalesSearchResponseData(result)
|
|
1592
|
+
});
|
|
1593
|
+
this.logInfo("handleSalesSearchRequest: 已注册/更新订阅者", {
|
|
1594
|
+
subscriberId,
|
|
1595
|
+
kind,
|
|
1596
|
+
shouldReset,
|
|
1597
|
+
visibleCount: visibleOrderIds.size
|
|
1598
|
+
});
|
|
1599
|
+
}
|
|
1600
|
+
return result;
|
|
1601
|
+
}
|
|
1602
|
+
/**
|
|
1603
|
+
* 透传调用后端 sales 搜索接口。
|
|
1604
|
+
*
|
|
1605
|
+
* @example
|
|
1606
|
+
* const res = await this.fetchSalesSearchFromAPI('/shop/es/search/sales', { keyword: '1001' })
|
|
1607
|
+
*/
|
|
1608
|
+
async fetchSalesSearchFromAPI(backendPath, data) {
|
|
1609
|
+
var _a;
|
|
1610
|
+
if (!((_a = this.app) == null ? void 0 : _a.request)) {
|
|
1611
|
+
this.logError("fetchSalesSearchFromAPI: app.request 不可用", { backendPath });
|
|
1612
|
+
return {
|
|
1613
|
+
code: 500,
|
|
1614
|
+
message: "app.request 不可用",
|
|
1615
|
+
data: backendPath === "/shop/es/search" ? { results: [] } : { list: [], count: 0 },
|
|
1616
|
+
status: false
|
|
1617
|
+
};
|
|
1618
|
+
}
|
|
1619
|
+
try {
|
|
1620
|
+
const response = await this.app.request.post(backendPath, data, {
|
|
1621
|
+
isShopApi: true
|
|
1622
|
+
});
|
|
1623
|
+
const payload = (response == null ? void 0 : response.data) ?? response;
|
|
1624
|
+
return {
|
|
1625
|
+
code: (response == null ? void 0 : response.code) ?? 200,
|
|
1626
|
+
status: (response == null ? void 0 : response.status) ?? true,
|
|
1627
|
+
message: (response == null ? void 0 : response.message) ?? "",
|
|
1628
|
+
data: payload
|
|
1629
|
+
};
|
|
1630
|
+
} catch (error) {
|
|
1631
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
1632
|
+
this.logError("fetchSalesSearchFromAPI: 请求失败", {
|
|
1633
|
+
backendPath,
|
|
1634
|
+
error: errorMessage
|
|
1635
|
+
});
|
|
1636
|
+
return {
|
|
1637
|
+
code: 500,
|
|
1638
|
+
message: errorMessage,
|
|
1639
|
+
data: backendPath === "/shop/es/search" ? { results: [] } : { list: [], count: 0 },
|
|
1640
|
+
status: false
|
|
1641
|
+
};
|
|
1642
|
+
}
|
|
1643
|
+
}
|
|
1644
|
+
/**
|
|
1645
|
+
* 构建搜索订阅签名;页码变化不重置,可继续累计 visibleOrderIds。
|
|
1646
|
+
*
|
|
1647
|
+
* @example
|
|
1648
|
+
* const signature = this.buildSalesSearchSignature('single', { keyword: 'A', page: 2 })
|
|
1649
|
+
*/
|
|
1650
|
+
buildSalesSearchSignature(kind, payload) {
|
|
1651
|
+
return this.stableSerialize({
|
|
1652
|
+
kind,
|
|
1653
|
+
payload: this.stripSalesSearchPageParams(payload)
|
|
1654
|
+
});
|
|
1655
|
+
}
|
|
1656
|
+
/**
|
|
1657
|
+
* 移除页码参数,保留 page size、keyword、filters、sort 等查询语义参数。
|
|
1658
|
+
*
|
|
1659
|
+
* @example
|
|
1660
|
+
* this.stripSalesSearchPageParams({ page: 2, per_page: 20, keyword: 'A' })
|
|
1661
|
+
*/
|
|
1662
|
+
stripSalesSearchPageParams(value) {
|
|
1663
|
+
if (Array.isArray(value)) {
|
|
1664
|
+
return value.map((item) => this.stripSalesSearchPageParams(item));
|
|
1665
|
+
}
|
|
1666
|
+
if (!value || typeof value !== "object")
|
|
1667
|
+
return value;
|
|
1668
|
+
const next = {};
|
|
1669
|
+
for (const [key, item] of Object.entries(value)) {
|
|
1670
|
+
if (key === "page" || key === "skip")
|
|
1671
|
+
continue;
|
|
1672
|
+
next[key] = this.stripSalesSearchPageParams(item);
|
|
1673
|
+
}
|
|
1674
|
+
return next;
|
|
1675
|
+
}
|
|
1676
|
+
/**
|
|
1677
|
+
* 提取响应 data,兼容 OS 包装结构与后端直接响应结构。
|
|
1678
|
+
*
|
|
1679
|
+
* @example
|
|
1680
|
+
* const data = this.extractSalesSearchResponseData(result)
|
|
1681
|
+
*/
|
|
1682
|
+
extractSalesSearchResponseData(result) {
|
|
1683
|
+
return (result == null ? void 0 : result.data) ?? result;
|
|
1684
|
+
}
|
|
1685
|
+
/**
|
|
1686
|
+
* 从搜索响应中提取 sales 命中的订单号。
|
|
1687
|
+
*
|
|
1688
|
+
* @example
|
|
1689
|
+
* const ids = this.extractSalesSearchOrderIds('single', result)
|
|
1690
|
+
*/
|
|
1691
|
+
extractSalesSearchOrderIds(kind, result) {
|
|
1692
|
+
var _a;
|
|
1693
|
+
const data = this.extractSalesSearchResponseData(result);
|
|
1694
|
+
const list = kind === "aggregate" ? (_a = this.findSalesSearchAggregateGroup(data)) == null ? void 0 : _a.list : data == null ? void 0 : data.list;
|
|
1695
|
+
if (!Array.isArray(list))
|
|
1696
|
+
return [];
|
|
1697
|
+
return list.map((item) => this.getSalesSearchOrderId(item)).filter((id) => !!id);
|
|
1698
|
+
}
|
|
1699
|
+
/**
|
|
1700
|
+
* 查找聚合搜索响应中的 sales 分组。
|
|
1701
|
+
*
|
|
1702
|
+
* @example
|
|
1703
|
+
* const group = this.findSalesSearchAggregateGroup({ results: [{ module: 'sales' }] })
|
|
1704
|
+
*/
|
|
1705
|
+
findSalesSearchAggregateGroup(data) {
|
|
1706
|
+
const results = Array.isArray(data == null ? void 0 : data.results) ? data.results : [];
|
|
1707
|
+
return results.find((item) => (item == null ? void 0 : item.module) === "sales") || null;
|
|
1708
|
+
}
|
|
1709
|
+
/**
|
|
1710
|
+
* 从订单或搜索命中项中取订单号。
|
|
1711
|
+
*
|
|
1712
|
+
* @example
|
|
1713
|
+
* const id = this.getSalesSearchOrderId({ order_id: 1001 })
|
|
1714
|
+
*/
|
|
1715
|
+
getSalesSearchOrderId(order) {
|
|
1716
|
+
if (!order || typeof order !== "object")
|
|
1717
|
+
return null;
|
|
1718
|
+
const raw = order.order_id ?? order.id;
|
|
1719
|
+
if (raw === void 0 || raw === null || raw === "")
|
|
1720
|
+
return null;
|
|
1721
|
+
return String(raw);
|
|
1722
|
+
}
|
|
1301
1723
|
/**
|
|
1302
1724
|
* 从查询值中提取 YYYY-MM-DD 日期串。
|
|
1303
1725
|
*
|
|
@@ -1738,8 +2160,203 @@ var Server = class {
|
|
|
1738
2160
|
};
|
|
1739
2161
|
}
|
|
1740
2162
|
}
|
|
1741
|
-
|
|
2163
|
+
getDeviceTaskPlugin() {
|
|
2164
|
+
var _a, _b;
|
|
2165
|
+
return ((_b = (_a = this.app) == null ? void 0 : _a.getPlugin) == null ? void 0 : _b.call(_a, "deviceTask")) || null;
|
|
2166
|
+
}
|
|
2167
|
+
getIdGeneratorPlugin() {
|
|
2168
|
+
var _a, _b;
|
|
2169
|
+
return ((_b = (_a = this.app) == null ? void 0 : _a.getPlugin) == null ? void 0 : _b.call(_a, "idGenerator")) || null;
|
|
2170
|
+
}
|
|
2171
|
+
getAppData(key) {
|
|
2172
|
+
var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l, _m, _n, _o, _p, _q, _r, _s, _t, _u, _v;
|
|
2173
|
+
const getData = (_b = (_a = this.app) == null ? void 0 : _a.getData) == null ? void 0 : _b.call(_a);
|
|
2174
|
+
if (typeof getData === "function")
|
|
2175
|
+
return getData(key);
|
|
2176
|
+
const directValue = (_d = (_c = this.app) == null ? void 0 : _c.getData) == null ? void 0 : _d.call(_c, key);
|
|
2177
|
+
if (directValue !== void 0)
|
|
2178
|
+
return directValue;
|
|
2179
|
+
const coreData = ((_i = (_g = (_f = (_e = this.app) == null ? void 0 : _e.models) == null ? void 0 : _f.getStore) == null ? void 0 : (_h = _g.call(_f)).getDataByModel) == null ? void 0 : _i.call(_h, "core", "core")) || ((_o = (_m = (_l = (_k = (_j = this.app) == null ? void 0 : _j.data) == null ? void 0 : _k.store) == null ? void 0 : _l.getStore) == null ? void 0 : (_n = _m.call(_l)).getDataByModel) == null ? void 0 : _o.call(_n, "core", "core")) || ((_v = (_u = (_s = (_r = (_q = (_p = this.app) == null ? void 0 : _p.data) == null ? void 0 : _q.store) == null ? void 0 : _r.getStore) == null ? void 0 : (_t = _s.call(_r)).getDataByModel) == null ? void 0 : _u.call(_t, "core")) == null ? void 0 : _v.core);
|
|
2180
|
+
return coreData == null ? void 0 : coreData[key];
|
|
2181
|
+
}
|
|
2182
|
+
getSmallTicketShopInfo() {
|
|
2183
|
+
const shopInfo = this.getAppData("shop_info") || {};
|
|
2184
|
+
return {
|
|
2185
|
+
...shopInfo,
|
|
2186
|
+
currency_symbol: shopInfo.currency_symbol || this.getAppData("shop_symbol") || this.getAppData("currency_symbol"),
|
|
2187
|
+
currency_code: shopInfo.currency_code || this.getAppData("shop_currency_code") || this.getAppData("currency_code"),
|
|
2188
|
+
tax_title: shopInfo.tax_title || this.getAppData("tax_title"),
|
|
2189
|
+
tax_country_code: shopInfo.tax_country_code || this.getAppData("tax_country_code"),
|
|
2190
|
+
is_price_include_tax: shopInfo.is_price_include_tax ?? this.getAppData("is_price_include_tax")
|
|
2191
|
+
};
|
|
2192
|
+
}
|
|
2193
|
+
taskOk(result) {
|
|
2194
|
+
return { status: "success", result };
|
|
2195
|
+
}
|
|
2196
|
+
taskFail(code, message, result) {
|
|
2197
|
+
return {
|
|
2198
|
+
status: "failed",
|
|
2199
|
+
error: { code, message },
|
|
2200
|
+
...result !== void 0 ? { result } : {}
|
|
2201
|
+
};
|
|
2202
|
+
}
|
|
2203
|
+
taskTimeout(message) {
|
|
2204
|
+
return {
|
|
2205
|
+
status: "timeout",
|
|
2206
|
+
error: { code: "TIMEOUT", message }
|
|
2207
|
+
};
|
|
2208
|
+
}
|
|
2209
|
+
resolveCheckoutTaskDeviceId(_data) {
|
|
2210
|
+
return this.getAppData("device_id") || 0;
|
|
2211
|
+
}
|
|
2212
|
+
buildCheckoutTaskIdempotencyKey(data) {
|
|
2213
|
+
const identity = (data == null ? void 0 : data.request_unique_idempotency_token) || (data == null ? void 0 : data.external_sale_number) || (data == null ? void 0 : data.order_id) || (data == null ? void 0 : data.shop_order_number) || (data == null ? void 0 : data.shop_full_order_number);
|
|
2214
|
+
if (identity === void 0 || identity === null || identity === "")
|
|
2215
|
+
return void 0;
|
|
2216
|
+
return `sync_sales:${identity}`;
|
|
2217
|
+
}
|
|
2218
|
+
async ensureCheckoutOrderNumbers(data, title) {
|
|
2219
|
+
if (!data || typeof data !== "object")
|
|
2220
|
+
return data;
|
|
2221
|
+
const next = { ...data };
|
|
2222
|
+
const needsShopOrderNumber = next.shop_order_number === void 0 || next.shop_order_number === null || next.shop_order_number === "";
|
|
2223
|
+
const needsShopFullOrderNumber = next.shop_full_order_number === void 0 || next.shop_full_order_number === null || next.shop_full_order_number === "";
|
|
2224
|
+
if (!needsShopOrderNumber && !needsShopFullOrderNumber)
|
|
2225
|
+
return next;
|
|
2226
|
+
const idGenerator = this.getIdGeneratorPlugin();
|
|
2227
|
+
if (!idGenerator) {
|
|
2228
|
+
this.logWarning(`${title}: idGenerator 插件不可用,无法生成订单号`, {
|
|
2229
|
+
needsShopOrderNumber,
|
|
2230
|
+
needsShopFullOrderNumber
|
|
2231
|
+
});
|
|
2232
|
+
return next;
|
|
2233
|
+
}
|
|
2234
|
+
try {
|
|
2235
|
+
if (needsShopOrderNumber && idGenerator.generateShopOrderNumber) {
|
|
2236
|
+
next.shop_order_number = await idGenerator.generateShopOrderNumber({
|
|
2237
|
+
biz: next.business_code || "ticket"
|
|
2238
|
+
});
|
|
2239
|
+
}
|
|
2240
|
+
if (needsShopFullOrderNumber && idGenerator.generateReceiptId) {
|
|
2241
|
+
next.shop_full_order_number = await idGenerator.generateReceiptId({
|
|
2242
|
+
biz: next.business_code || "ticket"
|
|
2243
|
+
});
|
|
2244
|
+
}
|
|
2245
|
+
this.logInfo(`${title}: checkout 订单号已准备`, {
|
|
2246
|
+
shop_order_number: next.shop_order_number,
|
|
2247
|
+
shop_full_order_number: next.shop_full_order_number
|
|
2248
|
+
});
|
|
2249
|
+
} catch (error) {
|
|
2250
|
+
this.logError(`${title}: 生成 checkout 订单号失败`, {
|
|
2251
|
+
error: error instanceof Error ? error.message : String(error)
|
|
2252
|
+
});
|
|
2253
|
+
}
|
|
2254
|
+
return next;
|
|
2255
|
+
}
|
|
2256
|
+
async dispatchCheckoutSyncTask(params) {
|
|
2257
|
+
var _a, _b, _c;
|
|
2258
|
+
this.registerDeviceTaskActions();
|
|
2259
|
+
const deviceTask = this.getDeviceTaskPlugin();
|
|
2260
|
+
const debugDeviceId = this.resolveCheckoutTaskDeviceId(params.data);
|
|
2261
|
+
const debugIdempotencyKey = this.buildCheckoutTaskIdempotencyKey(params.data);
|
|
2262
|
+
if (!(deviceTask == null ? void 0 : deviceTask.dispatch)) {
|
|
2263
|
+
this.logError(`${params.title}: deviceTask.dispatch 不可用`);
|
|
2264
|
+
return;
|
|
2265
|
+
}
|
|
2266
|
+
try {
|
|
2267
|
+
const result = await deviceTask.dispatch({
|
|
2268
|
+
action: "sync_sales",
|
|
2269
|
+
device_id: debugDeviceId,
|
|
2270
|
+
payload: {
|
|
2271
|
+
backendPath: params.backendPath,
|
|
2272
|
+
data: params.data,
|
|
2273
|
+
title: params.title
|
|
2274
|
+
},
|
|
2275
|
+
retry_limit: 5,
|
|
2276
|
+
idempotency_key: debugIdempotencyKey,
|
|
2277
|
+
name: "同步订单",
|
|
2278
|
+
source: "server",
|
|
2279
|
+
source_type: "order",
|
|
2280
|
+
source_id: ((_a = params.data) == null ? void 0 : _a.order_id) || ((_b = params.data) == null ? void 0 : _b.external_sale_number) || ((_c = params.data) == null ? void 0 : _c.shop_order_number)
|
|
2281
|
+
});
|
|
2282
|
+
this.logInfo(`${params.title}: sync_sales 任务已派发`, {
|
|
2283
|
+
uuid: result == null ? void 0 : result.uuid,
|
|
2284
|
+
status: result == null ? void 0 : result.status,
|
|
2285
|
+
reused: result == null ? void 0 : result.reused
|
|
2286
|
+
});
|
|
2287
|
+
} catch (error) {
|
|
2288
|
+
this.logError(`${params.title}: 派发 sync_sales 任务失败`, {
|
|
2289
|
+
error: error instanceof Error ? error.message : String(error)
|
|
2290
|
+
});
|
|
2291
|
+
}
|
|
2292
|
+
}
|
|
2293
|
+
async dispatchPrintOtherReceiptTask(params) {
|
|
2294
|
+
var _a, _b, _c, _d, _e;
|
|
2295
|
+
const deviceTask = this.getDeviceTaskPlugin();
|
|
2296
|
+
if (!(deviceTask == null ? void 0 : deviceTask.dispatch)) {
|
|
2297
|
+
this.logWarning("dispatchPrintOtherReceiptTask: deviceTask.dispatch 不可用");
|
|
2298
|
+
return;
|
|
2299
|
+
}
|
|
2300
|
+
try {
|
|
2301
|
+
const printIdentity = this.getPrintOrderIdentity(params.syncedOrder);
|
|
2302
|
+
await deviceTask.dispatch({
|
|
2303
|
+
action: "print_all",
|
|
2304
|
+
device_id: params.deviceId ?? this.resolveCheckoutTaskDeviceId(params.checkoutData),
|
|
2305
|
+
payload: {
|
|
2306
|
+
type: params.receiptOnly ? "0" : "99",
|
|
2307
|
+
data: params.receiptOnly ? {
|
|
2308
|
+
order_id: printIdentity,
|
|
2309
|
+
small_ticket_data: (_b = (_a = params.syncedOrder) == null ? void 0 : _a.payment_info) == null ? void 0 : _b.small_ticket_data
|
|
2310
|
+
} : {
|
|
2311
|
+
order_id: printIdentity,
|
|
2312
|
+
machine_code_print_info: (_d = (_c = params.syncedOrder) == null ? void 0 : _c.payment_info) == null ? void 0 : _d.machine_code_print_info,
|
|
2313
|
+
machine_code_print_info_v2: (_e = params.syncedOrder) == null ? void 0 : _e.machine_code_print_info_v2,
|
|
2314
|
+
skip_receipt: true
|
|
2315
|
+
}
|
|
2316
|
+
},
|
|
2317
|
+
idempotency_key: this.buildPrintOtherReceiptIdempotencyKey(params.syncedOrder, {
|
|
2318
|
+
receiptOnly: params.receiptOnly
|
|
2319
|
+
}),
|
|
2320
|
+
name: "打印转发",
|
|
2321
|
+
source: "server",
|
|
2322
|
+
source_type: "order",
|
|
2323
|
+
source_id: printIdentity
|
|
2324
|
+
});
|
|
2325
|
+
} catch (error) {
|
|
2326
|
+
this.logError("dispatchPrintOtherReceiptTask: 派发失败", {
|
|
2327
|
+
error: error instanceof Error ? error.message : String(error)
|
|
2328
|
+
});
|
|
2329
|
+
}
|
|
2330
|
+
}
|
|
2331
|
+
buildPrintOtherReceiptIdempotencyKey(order, options) {
|
|
2332
|
+
const identity = (order == null ? void 0 : order.external_sale_number) || (order == null ? void 0 : order.shop_order_number) || (order == null ? void 0 : order.shop_full_order_number) || (order == null ? void 0 : order.order_id);
|
|
2333
|
+
if (identity === void 0 || identity === null || identity === "")
|
|
2334
|
+
return void 0;
|
|
2335
|
+
return `${(options == null ? void 0 : options.receiptOnly) ? "print_receipt" : "print_voucher"}:${identity}`;
|
|
2336
|
+
}
|
|
2337
|
+
getPrintOrderIdentity(order) {
|
|
2338
|
+
return (order == null ? void 0 : order.order_id) || (order == null ? void 0 : order.external_sale_number) || (order == null ? void 0 : order.shop_order_number) || (order == null ? void 0 : order.shop_full_order_number);
|
|
2339
|
+
}
|
|
2340
|
+
async dispatchLocalReceiptPrint(params) {
|
|
1742
2341
|
var _a;
|
|
2342
|
+
if (params.requireFullyPaid && !this.isSalesOrderFullyPaid(params.order)) {
|
|
2343
|
+
return { printed: false, order: params.order };
|
|
2344
|
+
}
|
|
2345
|
+
const printableOrder = await this.withLocalSmallTicketData(params.order, {
|
|
2346
|
+
requireFullyPaid: params.requireFullyPaid
|
|
2347
|
+
});
|
|
2348
|
+
if (!(0, import_small_ticket.hasSmallTicketData)((_a = printableOrder.payment_info) == null ? void 0 : _a.small_ticket_data)) {
|
|
2349
|
+
return { printed: false, order: printableOrder };
|
|
2350
|
+
}
|
|
2351
|
+
await this.dispatchPrintOtherReceiptTask({
|
|
2352
|
+
checkoutData: params.checkoutData,
|
|
2353
|
+
syncedOrder: printableOrder,
|
|
2354
|
+
response: params.response,
|
|
2355
|
+
receiptOnly: true
|
|
2356
|
+
});
|
|
2357
|
+
return { printed: true, order: printableOrder };
|
|
2358
|
+
}
|
|
2359
|
+
async handleOrderCheckoutSubmit(params) {
|
|
1743
2360
|
const { backendPath, data, title } = params;
|
|
1744
2361
|
this.logInfo(`${title}: 开始处理`, {
|
|
1745
2362
|
backendPath,
|
|
@@ -1747,54 +2364,34 @@ var Server = class {
|
|
|
1747
2364
|
external_sale_number: data == null ? void 0 : data.external_sale_number,
|
|
1748
2365
|
order_number: data == null ? void 0 : data.order_number
|
|
1749
2366
|
});
|
|
1750
|
-
|
|
1751
|
-
|
|
1752
|
-
|
|
1753
|
-
|
|
1754
|
-
|
|
1755
|
-
|
|
1756
|
-
|
|
1757
|
-
|
|
1758
|
-
|
|
1759
|
-
|
|
1760
|
-
|
|
1761
|
-
|
|
1762
|
-
|
|
1763
|
-
|
|
1764
|
-
|
|
1765
|
-
const fresh = this.extractOrderDataFromCheckoutResponse(response);
|
|
1766
|
-
if (fresh && this.order) {
|
|
1767
|
-
const syncedOrder = { ...fresh, need_sync: 0 };
|
|
1768
|
-
await this.order.upsertOrdersFromRemote([syncedOrder]);
|
|
1769
|
-
this.logInfo(`${title}: 订单已同步到本地`, {
|
|
1770
|
-
order_id: fresh.order_id,
|
|
1771
|
-
external_sale_number: fresh.external_sale_number,
|
|
1772
|
-
order_number: fresh.order_number
|
|
1773
|
-
});
|
|
1774
|
-
} else {
|
|
1775
|
-
this.logWarning(`${title}: checkout 返回未包含可同步订单`, {
|
|
1776
|
-
hasOrderModule: !!this.order,
|
|
1777
|
-
backendPath
|
|
2367
|
+
const checkoutData = await this.ensureCheckoutOrderNumbers(data, title);
|
|
2368
|
+
const pendingResult = await this.handlePendingSyncCheckoutOrder({
|
|
2369
|
+
backendPath,
|
|
2370
|
+
data: checkoutData,
|
|
2371
|
+
title,
|
|
2372
|
+
reason: "checkout 已转入设备任务同步"
|
|
2373
|
+
});
|
|
2374
|
+
if ((pendingResult == null ? void 0 : pendingResult.status) === true) {
|
|
2375
|
+
const pendingOrder = pendingResult.data;
|
|
2376
|
+
if (pendingOrder && this.shouldBuildSmallTicketData(checkoutData) && this.isSalesOrderFullyPaid(pendingOrder)) {
|
|
2377
|
+
await this.dispatchLocalReceiptPrint({
|
|
2378
|
+
checkoutData,
|
|
2379
|
+
order: pendingOrder,
|
|
2380
|
+
response: pendingResult,
|
|
2381
|
+
requireFullyPaid: true
|
|
1778
2382
|
});
|
|
1779
2383
|
}
|
|
1780
|
-
|
|
1781
|
-
} catch (error) {
|
|
1782
|
-
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
1783
|
-
this.logError(`${title}: 请求失败`, { backendPath, error: errorMessage });
|
|
1784
|
-
const backendError = this.extractBackendErrorResponse(error);
|
|
1785
|
-
if (backendError)
|
|
1786
|
-
return backendError;
|
|
1787
|
-
return this.handlePendingSyncCheckoutOrder({
|
|
2384
|
+
await this.dispatchCheckoutSyncTask({
|
|
1788
2385
|
backendPath,
|
|
1789
|
-
data,
|
|
1790
|
-
title
|
|
1791
|
-
reason: errorMessage
|
|
2386
|
+
data: checkoutData,
|
|
2387
|
+
title
|
|
1792
2388
|
});
|
|
1793
2389
|
}
|
|
2390
|
+
return pendingResult;
|
|
1794
2391
|
}
|
|
1795
2392
|
async handlePendingSyncCheckoutOrder(params) {
|
|
1796
2393
|
const { backendPath, data, title, reason } = params;
|
|
1797
|
-
const pendingOrder = this.buildPendingSyncCheckoutOrder(data);
|
|
2394
|
+
const pendingOrder = await this.buildPendingSyncCheckoutOrder(data);
|
|
1798
2395
|
if (!pendingOrder) {
|
|
1799
2396
|
this.logError(`${title}: checkout 失败且订单缺少本地存储标识`, {
|
|
1800
2397
|
backendPath,
|
|
@@ -1821,7 +2418,7 @@ var Server = class {
|
|
|
1821
2418
|
}
|
|
1822
2419
|
try {
|
|
1823
2420
|
await this.order.upsertPendingSyncOrders([pendingOrder]);
|
|
1824
|
-
this.logWarning(`${title}:
|
|
2421
|
+
this.logWarning(`${title}: 订单已写入本地待同步`, {
|
|
1825
2422
|
backendPath,
|
|
1826
2423
|
reason,
|
|
1827
2424
|
order_id: pendingOrder.order_id,
|
|
@@ -1844,16 +2441,135 @@ var Server = class {
|
|
|
1844
2441
|
return { code: 500, status: false, message: errorMessage, data: null };
|
|
1845
2442
|
}
|
|
1846
2443
|
}
|
|
1847
|
-
buildPendingSyncCheckoutOrder(data) {
|
|
2444
|
+
async buildPendingSyncCheckoutOrder(data) {
|
|
1848
2445
|
if (!data || typeof data !== "object")
|
|
1849
2446
|
return null;
|
|
1850
2447
|
const hasStorageKey = data.external_sale_number !== void 0 && data.external_sale_number !== null && data.external_sale_number !== "" ? true : data.order_id !== void 0 && data.order_id !== null && data.order_id !== "";
|
|
1851
2448
|
if (!hasStorageKey)
|
|
1852
2449
|
return null;
|
|
1853
|
-
return
|
|
1854
|
-
|
|
1855
|
-
|
|
1856
|
-
|
|
2450
|
+
return this.withLocalSmallTicketData(
|
|
2451
|
+
{
|
|
2452
|
+
...data,
|
|
2453
|
+
need_sync: 1
|
|
2454
|
+
},
|
|
2455
|
+
{ requireFullyPaid: true }
|
|
2456
|
+
);
|
|
2457
|
+
}
|
|
2458
|
+
shouldBuildSmallTicketData(order) {
|
|
2459
|
+
var _a;
|
|
2460
|
+
if (!order || typeof order !== "object")
|
|
2461
|
+
return false;
|
|
2462
|
+
if ((0, import_small_ticket.hasSmallTicketData)((_a = order.payment_info) == null ? void 0 : _a.small_ticket_data))
|
|
2463
|
+
return false;
|
|
2464
|
+
return Number(order.small_ticket_data_flag ?? order.smallTicketDataFlag ?? 0) === 1;
|
|
2465
|
+
}
|
|
2466
|
+
shouldPrintSyncedOrder(params) {
|
|
2467
|
+
if (this.hasPaymentStatus(params.syncedOrder)) {
|
|
2468
|
+
return this.isSalesOrderFullyPaid(params.syncedOrder);
|
|
2469
|
+
}
|
|
2470
|
+
return this.isSalesOrderFullyPaid(params.checkoutData);
|
|
2471
|
+
}
|
|
2472
|
+
hasPaymentStatus(order) {
|
|
2473
|
+
return (order == null ? void 0 : order.payment_status) !== void 0 && (order == null ? void 0 : order.payment_status) !== null && (order == null ? void 0 : order.payment_status) !== "";
|
|
2474
|
+
}
|
|
2475
|
+
isSalesOrderFullyPaid(order) {
|
|
2476
|
+
var _a, _b, _c;
|
|
2477
|
+
if (!order || typeof order !== "object")
|
|
2478
|
+
return false;
|
|
2479
|
+
const paymentStatus = String(order.payment_status || "").toLowerCase();
|
|
2480
|
+
if (paymentStatus === "paid" || paymentStatus === "finished")
|
|
2481
|
+
return true;
|
|
2482
|
+
if (paymentStatus === "partially_paid" || paymentStatus === "payment_processing" || paymentStatus === "pending" || paymentStatus === "unpaid") {
|
|
2483
|
+
return false;
|
|
2484
|
+
}
|
|
2485
|
+
const amountGap = ((_a = order.summary) == null ? void 0 : _a.amount_gap) ?? order.amount_gap;
|
|
2486
|
+
if (amountGap !== void 0 && amountGap !== null && amountGap !== "") {
|
|
2487
|
+
return this.toAmountNumber(amountGap) <= 0;
|
|
2488
|
+
}
|
|
2489
|
+
const paidAmount = this.sumPaidOrderPayments(order.payments);
|
|
2490
|
+
const targetAmount = this.toAmountNumber(
|
|
2491
|
+
((_b = order.summary) == null ? void 0 : _b.expect_amount) ?? ((_c = order.summary) == null ? void 0 : _c.total_amount) ?? order.expect_amount ?? order.total_amount
|
|
2492
|
+
);
|
|
2493
|
+
if (targetAmount <= 0)
|
|
2494
|
+
return paidAmount > 0;
|
|
2495
|
+
return paidAmount >= targetAmount;
|
|
2496
|
+
}
|
|
2497
|
+
sumPaidOrderPayments(payments) {
|
|
2498
|
+
if (!Array.isArray(payments))
|
|
2499
|
+
return 0;
|
|
2500
|
+
return payments.reduce((sum, payment) => {
|
|
2501
|
+
const status = String((payment == null ? void 0 : payment.status) || "").toLowerCase();
|
|
2502
|
+
if (status === "voided" || status === "failed" || status === "cancelled")
|
|
2503
|
+
return sum;
|
|
2504
|
+
return sum + this.toAmountNumber((payment == null ? void 0 : payment.amount) ?? (payment == null ? void 0 : payment.origin_amount));
|
|
2505
|
+
}, 0);
|
|
2506
|
+
}
|
|
2507
|
+
toAmountNumber(value) {
|
|
2508
|
+
if (typeof value === "number")
|
|
2509
|
+
return Number.isFinite(value) ? value : 0;
|
|
2510
|
+
if (typeof value !== "string")
|
|
2511
|
+
return 0;
|
|
2512
|
+
const parsed = Number(value.replace(/[^\d.-]/g, ""));
|
|
2513
|
+
return Number.isFinite(parsed) ? parsed : 0;
|
|
2514
|
+
}
|
|
2515
|
+
async buildSmallTicketProductMap(order) {
|
|
2516
|
+
var _a;
|
|
2517
|
+
const products = Array.isArray(order == null ? void 0 : order.products) ? order.products : [];
|
|
2518
|
+
const productIds = Array.from(
|
|
2519
|
+
new Set(
|
|
2520
|
+
products.map((product) => (product == null ? void 0 : product.product_id) ?? (product == null ? void 0 : product.id)).filter((id) => id !== void 0 && id !== null && id !== "")
|
|
2521
|
+
)
|
|
2522
|
+
);
|
|
2523
|
+
if (!productIds.length || typeof ((_a = this.products) == null ? void 0 : _a.getProductById) !== "function")
|
|
2524
|
+
return {};
|
|
2525
|
+
const entries = await Promise.all(
|
|
2526
|
+
productIds.map(async (id) => {
|
|
2527
|
+
var _a2, _b;
|
|
2528
|
+
try {
|
|
2529
|
+
const product = await ((_b = (_a2 = this.products) == null ? void 0 : _a2.getProductById) == null ? void 0 : _b.call(_a2, Number(id)));
|
|
2530
|
+
return product ? [String(id), product] : null;
|
|
2531
|
+
} catch (error) {
|
|
2532
|
+
this.logWarning("buildSmallTicketProductMap: 查询本地商品失败", {
|
|
2533
|
+
product_id: id,
|
|
2534
|
+
error: error instanceof Error ? error.message : String(error)
|
|
2535
|
+
});
|
|
2536
|
+
return null;
|
|
2537
|
+
}
|
|
2538
|
+
})
|
|
2539
|
+
);
|
|
2540
|
+
return entries.reduce((map, entry) => {
|
|
2541
|
+
if (!entry)
|
|
2542
|
+
return map;
|
|
2543
|
+
const [id, product] = entry;
|
|
2544
|
+
map[id] = product;
|
|
2545
|
+
return map;
|
|
2546
|
+
}, {});
|
|
2547
|
+
}
|
|
2548
|
+
async withLocalSmallTicketData(order, options) {
|
|
2549
|
+
if (!this.shouldBuildSmallTicketData(order))
|
|
2550
|
+
return order;
|
|
2551
|
+
if ((options == null ? void 0 : options.requireFullyPaid) && !this.isSalesOrderFullyPaid(order))
|
|
2552
|
+
return order;
|
|
2553
|
+
try {
|
|
2554
|
+
const productMap = await this.buildSmallTicketProductMap(order);
|
|
2555
|
+
const smallTicketData = (0, import_small_ticket.buildSmallTicketData)({
|
|
2556
|
+
order,
|
|
2557
|
+
shopInfo: this.getSmallTicketShopInfo(),
|
|
2558
|
+
productMap
|
|
2559
|
+
});
|
|
2560
|
+
return {
|
|
2561
|
+
...order,
|
|
2562
|
+
payment_info: {
|
|
2563
|
+
...order.payment_info || {},
|
|
2564
|
+
small_ticket_data: smallTicketData
|
|
2565
|
+
}
|
|
2566
|
+
};
|
|
2567
|
+
} catch (error) {
|
|
2568
|
+
this.logError("withLocalSmallTicketData: 生成本地小票数据失败", {
|
|
2569
|
+
error: error instanceof Error ? error.message : String(error)
|
|
2570
|
+
});
|
|
2571
|
+
return order;
|
|
2572
|
+
}
|
|
1857
2573
|
}
|
|
1858
2574
|
/**
|
|
1859
2575
|
* 解析 order_ids 入参,兼容 url query / data 字段、字符串(csv) / 数组两种形态。
|
|
@@ -2058,6 +2774,13 @@ var Server = class {
|
|
|
2058
2774
|
data: response
|
|
2059
2775
|
};
|
|
2060
2776
|
}
|
|
2777
|
+
isCheckoutResponseRejected(response) {
|
|
2778
|
+
const normalized = this.normalizeCheckoutResponse(response);
|
|
2779
|
+
const isErrorCode = (normalized == null ? void 0 : normalized.code) !== void 0 && normalized.code !== 200 && normalized.code !== "200";
|
|
2780
|
+
const isErrorStatus = (normalized == null ? void 0 : normalized.status) === false || typeof (normalized == null ? void 0 : normalized.status) === "number" && normalized.status >= 400;
|
|
2781
|
+
const isErrorSuccess = (normalized == null ? void 0 : normalized.success) === false;
|
|
2782
|
+
return isErrorCode || isErrorStatus || isErrorSuccess;
|
|
2783
|
+
}
|
|
2061
2784
|
extractBackendErrorResponse(error) {
|
|
2062
2785
|
var _a, _b;
|
|
2063
2786
|
const candidates = [
|
|
@@ -2469,6 +3192,101 @@ var Server = class {
|
|
|
2469
3192
|
totalNotifyDurationMs: notifyEndAt - notifyStartAt
|
|
2470
3193
|
});
|
|
2471
3194
|
}
|
|
3195
|
+
/**
|
|
3196
|
+
* 订单变更后通知 sales 搜索订阅者。
|
|
3197
|
+
* 仅回传当前查询已返回订单号集合中的变更订单。
|
|
3198
|
+
*
|
|
3199
|
+
* @example
|
|
3200
|
+
* this.notifySalesSearchSubscribers({ list: [], changedOrders: [order], source: 'pubsub.bodyUpsert' })
|
|
3201
|
+
*/
|
|
3202
|
+
notifySalesSearchSubscribers(payload) {
|
|
3203
|
+
if (this.salesSearchSubscribers.size === 0)
|
|
3204
|
+
return;
|
|
3205
|
+
const changedOrders = this.extractChangedOrdersFromPayload(payload);
|
|
3206
|
+
if (changedOrders.length === 0) {
|
|
3207
|
+
return;
|
|
3208
|
+
}
|
|
3209
|
+
for (const [subscriberId, subscriber] of this.salesSearchSubscribers.entries()) {
|
|
3210
|
+
try {
|
|
3211
|
+
const changedVisibleOrders = changedOrders.filter((order) => {
|
|
3212
|
+
const orderId = this.getSalesSearchOrderId(order);
|
|
3213
|
+
return !!orderId && subscriber.visibleOrderIds.has(orderId);
|
|
3214
|
+
});
|
|
3215
|
+
if (changedVisibleOrders.length === 0) {
|
|
3216
|
+
continue;
|
|
3217
|
+
}
|
|
3218
|
+
subscriber.callback(
|
|
3219
|
+
this.buildSalesSearchDeltaResponse(subscriber, changedVisibleOrders)
|
|
3220
|
+
);
|
|
3221
|
+
this.logInfo("notifySalesSearchSubscribers: 已推送 delta", {
|
|
3222
|
+
subscriberId,
|
|
3223
|
+
kind: subscriber.kind,
|
|
3224
|
+
count: changedVisibleOrders.length
|
|
3225
|
+
});
|
|
3226
|
+
} catch (error) {
|
|
3227
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
3228
|
+
this.logError("notifySalesSearchSubscribers: 推送失败", {
|
|
3229
|
+
subscriberId,
|
|
3230
|
+
error: errorMessage
|
|
3231
|
+
});
|
|
3232
|
+
}
|
|
3233
|
+
}
|
|
3234
|
+
}
|
|
3235
|
+
/**
|
|
3236
|
+
* 从订单变更事件中提取明确 delta。
|
|
3237
|
+
*
|
|
3238
|
+
* @example
|
|
3239
|
+
* const list = this.extractChangedOrdersFromPayload(payload)
|
|
3240
|
+
*/
|
|
3241
|
+
extractChangedOrdersFromPayload(payload) {
|
|
3242
|
+
if (!payload || Array.isArray(payload))
|
|
3243
|
+
return [];
|
|
3244
|
+
return Array.isArray(payload.changedOrders) ? payload.changedOrders : [];
|
|
3245
|
+
}
|
|
3246
|
+
/**
|
|
3247
|
+
* 构建与当前搜索接口一致的 delta 响应。
|
|
3248
|
+
*
|
|
3249
|
+
* @example
|
|
3250
|
+
* const response = this.buildSalesSearchDeltaResponse(subscriber, [order])
|
|
3251
|
+
*/
|
|
3252
|
+
buildSalesSearchDeltaResponse(subscriber, changedOrders) {
|
|
3253
|
+
if (subscriber.kind === "aggregate") {
|
|
3254
|
+
return this.buildAggregateSalesSearchDeltaResponse(subscriber, changedOrders);
|
|
3255
|
+
}
|
|
3256
|
+
const data = subscriber.lastResponseData || {};
|
|
3257
|
+
return {
|
|
3258
|
+
code: 200,
|
|
3259
|
+
status: true,
|
|
3260
|
+
message: "",
|
|
3261
|
+
data: {
|
|
3262
|
+
...data,
|
|
3263
|
+
list: changedOrders
|
|
3264
|
+
}
|
|
3265
|
+
};
|
|
3266
|
+
}
|
|
3267
|
+
/**
|
|
3268
|
+
* 构建聚合搜索接口的 sales 分组 delta 响应。
|
|
3269
|
+
*
|
|
3270
|
+
* @example
|
|
3271
|
+
* const response = this.buildAggregateSalesSearchDeltaResponse(subscriber, [order])
|
|
3272
|
+
*/
|
|
3273
|
+
buildAggregateSalesSearchDeltaResponse(subscriber, changedOrders) {
|
|
3274
|
+
const data = subscriber.lastResponseData || {};
|
|
3275
|
+
const results = Array.isArray(data == null ? void 0 : data.results) ? data.results : [];
|
|
3276
|
+
const hasSalesGroup = results.some((item) => (item == null ? void 0 : item.module) === "sales");
|
|
3277
|
+
const nextResults = hasSalesGroup ? results.map(
|
|
3278
|
+
(item) => (item == null ? void 0 : item.module) === "sales" ? { ...item, list: changedOrders } : item
|
|
3279
|
+
) : [{ module: "sales", list: changedOrders, count: changedOrders.length }];
|
|
3280
|
+
return {
|
|
3281
|
+
code: 200,
|
|
3282
|
+
status: true,
|
|
3283
|
+
message: "",
|
|
3284
|
+
data: {
|
|
3285
|
+
...data,
|
|
3286
|
+
results: nextResults
|
|
3287
|
+
}
|
|
3288
|
+
};
|
|
3289
|
+
}
|
|
2472
3290
|
/**
|
|
2473
3291
|
* 预约数据变更后,遍历订阅者重新计算并通过 callback 推送
|
|
2474
3292
|
*/
|