@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.
Files changed (68) hide show
  1. package/dist/model/strategy/adapter/promotion/index.js +0 -9
  2. package/dist/modules/OpenData/index.d.ts +1 -0
  3. package/dist/modules/OpenData/index.js +21 -16
  4. package/dist/modules/Order/index.d.ts +9 -0
  5. package/dist/modules/Order/index.js +237 -146
  6. package/dist/modules/Order/types.d.ts +10 -0
  7. package/dist/modules/Order/utils.js +7 -5
  8. package/dist/modules/Payment/index.d.ts +0 -13
  9. package/dist/modules/Payment/index.js +449 -833
  10. package/dist/plugins/request.d.ts +1 -0
  11. package/dist/server/index.d.ts +142 -0
  12. package/dist/server/index.js +2208 -824
  13. package/dist/server/modules/index.d.ts +2 -0
  14. package/dist/server/modules/index.js +2 -0
  15. package/dist/server/modules/order/index.d.ts +7 -0
  16. package/dist/server/modules/order/index.js +375 -337
  17. package/dist/server/modules/order/types.d.ts +15 -0
  18. package/dist/server/modules/order/types.js +8 -0
  19. package/dist/server/modules/payment/index.d.ts +28 -0
  20. package/dist/server/modules/payment/index.js +305 -0
  21. package/dist/server/modules/payment/types.d.ts +9 -0
  22. package/dist/server/modules/payment/types.js +1 -0
  23. package/dist/server/utils/small-ticket.d.ts +69 -0
  24. package/dist/server/utils/small-ticket.js +528 -0
  25. package/dist/solution/BaseSales/index.d.ts +10 -3
  26. package/dist/solution/BaseSales/index.js +307 -198
  27. package/dist/solution/BookingByStep/index.d.ts +1 -1
  28. package/dist/solution/BookingTicket/index.d.ts +8 -1
  29. package/dist/solution/BookingTicket/index.js +444 -264
  30. package/dist/solution/BookingTicket/types.d.ts +2 -1
  31. package/dist/solution/BookingTicket/types.js +3 -1
  32. package/dist/solution/BookingTicket/utils/scan/applyGlobalScan.js +51 -52
  33. package/dist/solution/BookingTicket/utils/scan/index.d.ts +5 -0
  34. package/dist/solution/BookingTicket/utils/scan/index.js +20 -8
  35. package/lib/model/strategy/adapter/promotion/index.js +49 -0
  36. package/lib/modules/OpenData/index.d.ts +1 -0
  37. package/lib/modules/OpenData/index.js +6 -4
  38. package/lib/modules/Order/index.d.ts +9 -0
  39. package/lib/modules/Order/index.js +64 -4
  40. package/lib/modules/Order/types.d.ts +10 -0
  41. package/lib/modules/Order/utils.js +4 -3
  42. package/lib/modules/Payment/index.d.ts +0 -13
  43. package/lib/modules/Payment/index.js +6 -149
  44. package/lib/plugins/request.d.ts +1 -0
  45. package/lib/server/index.d.ts +142 -0
  46. package/lib/server/index.js +867 -49
  47. package/lib/server/modules/index.d.ts +2 -0
  48. package/lib/server/modules/index.js +3 -0
  49. package/lib/server/modules/order/index.d.ts +7 -0
  50. package/lib/server/modules/order/index.js +21 -7
  51. package/lib/server/modules/order/types.d.ts +15 -0
  52. package/lib/server/modules/payment/index.d.ts +28 -0
  53. package/lib/server/modules/payment/index.js +192 -0
  54. package/lib/server/modules/payment/types.d.ts +9 -0
  55. package/lib/server/modules/payment/types.js +17 -0
  56. package/lib/server/utils/small-ticket.d.ts +69 -0
  57. package/lib/server/utils/small-ticket.js +555 -0
  58. package/lib/solution/BaseSales/index.d.ts +10 -3
  59. package/lib/solution/BaseSales/index.js +98 -6
  60. package/lib/solution/BookingByStep/index.d.ts +1 -1
  61. package/lib/solution/BookingTicket/index.d.ts +8 -1
  62. package/lib/solution/BookingTicket/index.js +109 -2
  63. package/lib/solution/BookingTicket/types.d.ts +2 -1
  64. package/lib/solution/BookingTicket/types.js +5 -0
  65. package/lib/solution/BookingTicket/utils/scan/applyGlobalScan.js +1 -2
  66. package/lib/solution/BookingTicket/utils/scan/index.d.ts +5 -0
  67. package/lib/solution/BookingTicket/utils/scan/index.js +17 -3
  68. package/package.json +1 -1
@@ -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: "/shop/order/checkout",
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
- async handleOrderCheckoutSubmit(params) {
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
- if (!((_a = this.app) == null ? void 0 : _a.request)) {
1751
- this.logError(`${title}: app.request 不可用`);
1752
- return this.handlePendingSyncCheckoutOrder({
1753
- backendPath,
1754
- data,
1755
- title,
1756
- reason: "app.request 不可用"
1757
- });
1758
- }
1759
- try {
1760
- const response = await this.app.request.post(backendPath, data, {
1761
- isShopApi: true,
1762
- customToast: () => {
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
- return this.normalizeCheckoutResponse(response);
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
- ...data,
1855
- need_sync: 1
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
  */