@pisell/pisellos 2.2.283 → 2.2.285
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/core/index.js +6 -10
- package/dist/model/strategy/adapter/walletPass/example.js +3 -26
- package/dist/model/strategy/strategy-example.js +5 -19
- package/dist/modules/Cart/index.js +0 -3
- package/dist/modules/Cart/utils/cartProduct.js +0 -1
- package/dist/modules/Date/index.js +1 -5
- package/dist/modules/Discount/index.js +2 -4
- package/dist/modules/OpenData/index.js +0 -2
- package/dist/modules/Order/index.d.ts +1 -1
- package/dist/modules/Order/index.js +64 -86
- package/dist/modules/Order/types.d.ts +3 -1
- package/dist/modules/Order/types.js +1 -0
- package/dist/modules/Payment/index.js +97 -162
- package/dist/modules/ProductList/index.js +91 -65
- package/dist/modules/Rules/index.js +1 -4
- package/dist/modules/Schedule/index.js +0 -1
- package/dist/modules/Schedule/utils.js +0 -1
- package/dist/modules/Step/index.js +0 -1
- package/dist/plugins/request.js +1 -4
- package/dist/plugins/window.js +2 -6
- package/dist/server/index.js +97 -129
- package/dist/server/modules/menu/index.js +32 -41
- package/dist/server/modules/order/index.js +2 -4
- package/dist/server/modules/products/index.js +24 -42
- package/dist/server/modules/quotation/index.js +29 -38
- package/dist/server/modules/resource/index.js +3 -4
- package/dist/server/modules/schedule/index.js +27 -35
- package/dist/server/utils/product.js +0 -1
- package/dist/solution/BaseSales/index.js +115 -76
- package/dist/solution/BookingByStep/index.d.ts +1 -1
- package/dist/solution/BookingByStep/index.js +7 -59
- package/dist/solution/BookingByStep/utils/capacity.js +1 -11
- package/dist/solution/BookingByStep/utils/resources.js +1 -3
- package/dist/solution/BookingByStep/utils/timeslots.js +2 -43
- package/dist/solution/BookingTicket/index.d.ts +6 -16
- package/dist/solution/BookingTicket/index.js +13 -6
- package/dist/solution/BookingTicket/utils/scan/cloudSearch.js +6 -7
- package/dist/solution/BookingTicket/utils/scan/handleScan.js +25 -31
- package/dist/solution/BookingTicket/utils/scan/index.js +6 -10
- package/dist/solution/BuyTickets/index.js +9 -12
- package/dist/solution/Checkout/index.js +177 -252
- package/dist/solution/Checkout/utils/index.js +4 -16
- package/dist/solution/RegisterAndLogin/index.js +30 -76
- package/dist/solution/ScanOrder/index.js +50 -60
- package/dist/solution/ShopDiscount/index.js +2 -7
- package/dist/solution/VenueBooking/index.js +45 -55
- package/dist/utils/task.js +15 -18
- package/dist/utils/watch.js +0 -1
- package/lib/apis/picoding.js +2 -0
- package/lib/core/index.js +131 -132
- package/lib/effects/index.js +46 -57
- package/lib/index.js +82 -49
- package/lib/model/index.js +14 -21
- package/lib/model/strategy/adapter/dataVariant/adapter.js +38 -50
- package/lib/model/strategy/adapter/dataVariant/evaluator.js +162 -236
- package/lib/model/strategy/adapter/dataVariant/examples.js +229 -237
- package/lib/model/strategy/adapter/dataVariant/index.js +39 -34
- package/lib/model/strategy/adapter/dataVariant/type.js +57 -28
- package/lib/model/strategy/adapter/index.js +100 -64
- package/lib/model/strategy/adapter/itemRule/adapter.js +156 -144
- package/lib/model/strategy/adapter/itemRule/evaluator.js +36 -48
- package/lib/model/strategy/adapter/itemRule/examples.js +295 -225
- package/lib/model/strategy/adapter/itemRule/index.js +83 -57
- package/lib/model/strategy/adapter/itemRule/type.js +97 -36
- package/lib/model/strategy/adapter/promotion/adapter.js +91 -69
- package/lib/model/strategy/adapter/promotion/evaluator.js +444 -310
- package/lib/model/strategy/adapter/promotion/examples.js +222 -234
- package/lib/model/strategy/adapter/promotion/index.js +46 -0
- package/lib/model/strategy/adapter/promotion/type.js +243 -36
- package/lib/model/strategy/adapter/type.js +4 -16
- package/lib/model/strategy/adapter/walletPass/evaluator.js +180 -150
- package/lib/model/strategy/adapter/walletPass/example.js +217 -190
- package/lib/model/strategy/adapter/walletPass/index.js +75 -51
- package/lib/model/strategy/adapter/walletPass/locales.js +36 -58
- package/lib/model/strategy/adapter/walletPass/type.js +4 -16
- package/lib/model/strategy/adapter/walletPass/utils.js +588 -378
- package/lib/model/strategy/engine.js +270 -168
- package/lib/model/strategy/index.js +49 -34
- package/lib/model/strategy/strategy-example.js +243 -239
- package/lib/model/strategy/type.js +100 -40
- package/lib/modules/Account/index.js +39 -53
- package/lib/modules/Account/types.js +20 -33
- package/lib/modules/AccountList/index.js +116 -154
- package/lib/modules/AccountList/types.js +30 -31
- package/lib/modules/AccountList/utils.js +18 -30
- package/lib/modules/BaseModule.js +23 -42
- package/lib/modules/BookingContext/index.js +158 -136
- package/lib/modules/BookingContext/types.js +46 -32
- package/lib/modules/BookingContext/utils/buildCacheItemFromOrderLine.js +138 -146
- package/lib/modules/BookingContext/utils/buildNormalProductCacheItemFromOrderLine.js +78 -83
- package/lib/modules/BookingContext/utils/cacheItemToBookingInput.js +170 -150
- package/lib/modules/BookingContext/utils/flexible.js +38 -55
- package/lib/modules/BookingContext/utils/formatResourceList.js +19 -34
- package/lib/modules/BookingContext/utils/index.js +124 -41
- package/lib/modules/BookingContext/utils/noResource.js +58 -70
- package/lib/modules/BookingContext/utils/orderLineDisplay.js +14 -41
- package/lib/modules/BookingContext/utils/productExtend.js +202 -155
- package/lib/modules/BookingContext/utils/resourceErrors.js +53 -53
- package/lib/modules/BookingContext/utils/resourceSelection.js +19 -41
- package/lib/modules/BookingContext/utils/resources.js +209 -167
- package/lib/modules/BookingContext/utils/serviceTimes.js +115 -116
- package/lib/modules/BookingContext/utils/timeSlices.js +76 -86
- package/lib/modules/Cart/index.js +170 -124
- package/lib/modules/Cart/types.js +46 -51
- package/lib/modules/Cart/utils/cartAccount.js +40 -39
- package/lib/modules/Cart/utils/cartDate.js +56 -61
- package/lib/modules/Cart/utils/cartDiscount.js +28 -33
- package/lib/modules/Cart/utils/cartNote.js +27 -32
- package/lib/modules/Cart/utils/cartProduct.js +321 -251
- package/lib/modules/Cart/utils/cartRelationForms.js +29 -35
- package/lib/modules/Cart/utils/cartResource.js +70 -78
- package/lib/modules/Cart/utils/changePrice.js +22 -50
- package/lib/modules/Cart/utils/index.js +106 -48
- package/lib/modules/Customer/constants.js +13 -34
- package/lib/modules/Customer/index.js +235 -172
- package/lib/modules/Customer/types.js +38 -35
- package/lib/modules/Date/index.js +116 -115
- package/lib/modules/Date/types.js +16 -28
- package/lib/modules/Date/utils.js +174 -123
- package/lib/modules/Discount/index.js +122 -127
- package/lib/modules/Discount/types.js +9 -31
- package/lib/modules/Guests/index.js +30 -56
- package/lib/modules/Guests/types.js +23 -33
- package/lib/modules/Holder/index.js +209 -189
- package/lib/modules/Holder/types.js +4 -16
- package/lib/modules/Holder/utils.js +20 -49
- package/lib/modules/OpenData/index.js +70 -76
- package/lib/modules/OpenData/types.js +4 -16
- package/lib/modules/OpenData/utils.js +44 -78
- package/lib/modules/Order/index.d.ts +1 -1
- package/lib/modules/Order/index.js +1728 -2123
- package/lib/modules/Order/payment-utils.js +77 -120
- package/lib/modules/Order/types.d.ts +3 -1
- package/lib/modules/Order/types.js +88 -35
- package/lib/modules/Order/utils/bundleDiscountHydration.js +41 -74
- package/lib/modules/Order/utils/discountProductLineIdentity.js +142 -100
- package/lib/modules/Order/utils/manualProductDiscount.js +124 -123
- package/lib/modules/Order/utils/orderCollectionIdentity.js +184 -244
- package/lib/modules/Order/utils.js +814 -647
- package/lib/modules/Payment/cash.js +20 -33
- package/lib/modules/Payment/cashRecommendationAlgorithm.js +145 -76
- package/lib/modules/Payment/eftpos.js +16 -30
- package/lib/modules/Payment/index.js +532 -399
- package/lib/modules/Payment/mx51.js +1 -0
- package/lib/modules/Payment/types.js +230 -108
- package/lib/modules/Payment/utils.js +52 -51
- package/lib/modules/Payment/walletpass.js +485 -660
- package/lib/modules/Product/index.js +51 -46
- package/lib/modules/Product/types.js +4 -16
- package/lib/modules/Product/utils.js +32 -33
- package/lib/modules/ProductList/index.js +119 -123
- package/lib/modules/ProductList/types.js +9 -31
- package/lib/modules/Quotation/index.js +66 -100
- package/lib/modules/Quotation/types.js +4 -16
- package/lib/modules/Resource/index.js +27 -59
- package/lib/modules/Resource/types.js +22 -32
- package/lib/modules/Resource/utils.js +30 -38
- package/lib/modules/ResourcePlanner/index.js +60 -61
- package/lib/modules/ResourcePlanner/planner.js +346 -384
- package/lib/modules/ResourcePlanner/types.js +4 -16
- package/lib/modules/Rules/index.js +1218 -959
- package/lib/modules/Rules/types.js +17 -40
- package/lib/modules/SalesSummary/index.js +85 -90
- package/lib/modules/SalesSummary/types.js +4 -16
- package/lib/modules/SalesSummary/utils.js +355 -430
- package/lib/modules/ScanOrderLogger/index.js +59 -79
- package/lib/modules/ScanOrderLogger/providers/feishu.js +54 -78
- package/lib/modules/ScanOrderLogger/providers/grafana.js +13 -36
- package/lib/modules/ScanOrderLogger/types.js +4 -16
- package/lib/modules/Schedule/getDateIsInSchedule.js +334 -163
- package/lib/modules/Schedule/index.js +100 -114
- package/lib/modules/Schedule/type.js +4 -16
- package/lib/modules/Schedule/types.js +4 -16
- package/lib/modules/Schedule/utils.js +433 -291
- package/lib/modules/Step/index.js +40 -52
- package/lib/modules/Step/tyeps.js +4 -16
- package/lib/modules/Summary/index.js +66 -71
- package/lib/modules/Summary/types.js +4 -16
- package/lib/modules/Summary/utils.js +773 -418
- package/lib/modules/SurchargeList/index.js +46 -60
- package/lib/modules/SurchargeList/types.js +4 -16
- package/lib/modules/index.js +245 -63
- package/lib/plugins/app.js +4 -16
- package/lib/plugins/index.js +36 -25
- package/lib/plugins/request.js +137 -126
- package/lib/plugins/shopStore.js +4 -16
- package/lib/plugins/user.js +4 -16
- package/lib/plugins/window.js +171 -179
- package/lib/server/index.js +3027 -2655
- package/lib/server/modules/floor-plan/index.js +102 -123
- package/lib/server/modules/floor-plan/types.js +15 -30
- package/lib/server/modules/index.js +106 -68
- package/lib/server/modules/menu/index.js +146 -122
- package/lib/server/modules/menu/types.js +18 -31
- package/lib/server/modules/order/index.js +758 -956
- package/lib/server/modules/order/types.js +122 -33
- package/lib/server/modules/order/utils/filterBookings.js +219 -194
- package/lib/server/modules/order/utils/filterOrders.js +118 -92
- package/lib/server/modules/payment/index.js +59 -83
- package/lib/server/modules/payment/types.js +4 -16
- package/lib/server/modules/products/index.js +575 -459
- package/lib/server/modules/products/types.js +44 -34
- package/lib/server/modules/quotation/index.js +137 -172
- package/lib/server/modules/quotation/types.js +21 -31
- package/lib/server/modules/resource/index.js +213 -184
- package/lib/server/modules/resource/types.js +42 -33
- package/lib/server/modules/schedule/index.js +135 -123
- package/lib/server/modules/schedule/types.js +14 -21
- package/lib/server/modules/schedule/utils.js +334 -163
- package/lib/server/types.js +4 -16
- package/lib/server/utils/index.js +25 -23
- package/lib/server/utils/product.js +196 -139
- package/lib/server/utils/schedule.js +34 -41
- package/lib/server/utils/small-ticket.js +479 -456
- package/lib/server/utils/time.js +40 -49
- package/lib/solution/BaseSales/index.js +1220 -1412
- package/lib/solution/BaseSales/types.js +42 -38
- package/lib/solution/BaseSales/utils/cartPromotion.js +650 -502
- package/lib/solution/BaseSales/utils/parseSalesResponse.js +85 -105
- package/lib/solution/BaseSales/utils/quotationPrice.js +56 -114
- package/lib/solution/BaseSales/utils/transformBaseProductToOrderProduct.js +108 -145
- package/lib/solution/BaseSales/utils.js +158 -143
- package/lib/solution/BookingByStep/index.d.ts +1 -1
- package/lib/solution/BookingByStep/index.js +1527 -1340
- package/lib/solution/BookingByStep/types.js +40 -99
- package/lib/solution/BookingByStep/utils/capacity.js +192 -160
- package/lib/solution/BookingByStep/utils/products.js +42 -52
- package/lib/solution/BookingByStep/utils/resources.js +527 -330
- package/lib/solution/BookingByStep/utils/stock.js +65 -44
- package/lib/solution/BookingByStep/utils/timeslots.js +129 -131
- package/lib/solution/BookingTicket/index.d.ts +6 -16
- package/lib/solution/BookingTicket/index.js +608 -517
- package/lib/solution/BookingTicket/types.js +99 -77
- package/lib/solution/BookingTicket/utils/addProductDecision.js +225 -187
- package/lib/solution/BookingTicket/utils/addTimeAvailability.js +58 -89
- package/lib/solution/BookingTicket/utils/bookingStatus.js +107 -78
- package/lib/solution/BookingTicket/utils/cartView.js +114 -99
- package/lib/solution/BookingTicket/utils/exampleData.js +185 -0
- package/lib/solution/BookingTicket/utils/orderCustomer.js +18 -41
- package/lib/solution/BookingTicket/utils/resolveBestAddTimePlan.js +219 -86
- package/lib/solution/BookingTicket/utils/scan/applyGlobalScan.js +168 -116
- package/lib/solution/BookingTicket/utils/scan/cloudSearch.js +79 -91
- package/lib/solution/BookingTicket/utils/scan/formatGlobalScan.js +54 -41
- package/lib/solution/BookingTicket/utils/scan/handleScan.js +84 -81
- package/lib/solution/BookingTicket/utils/scan/index.js +98 -104
- package/lib/solution/BookingTicket/utils/scan/scanCache.js +90 -43
- package/lib/solution/BookingTicket/utils/scan/scanProductValues.js +52 -64
- package/lib/solution/BookingTicket/utils/sessionCatalogStale.js +56 -58
- package/lib/solution/BookingTicket/utils/weighingProductSku.js +34 -48
- package/lib/solution/BuyTickets/index.js +67 -70
- package/lib/solution/BuyTickets/types.js +22 -38
- package/lib/solution/Checkout/index.js +1451 -1158
- package/lib/solution/Checkout/types.js +91 -61
- package/lib/solution/Checkout/utils/index.js +228 -156
- package/lib/solution/PlaceOrder/index.js +55 -0
- package/lib/solution/RegisterAndLogin/config.js +493 -460
- package/lib/solution/RegisterAndLogin/index.js +633 -630
- package/lib/solution/RegisterAndLogin/types.js +189 -76
- package/lib/solution/RegisterAndLogin/utils.js +183 -125
- package/lib/solution/Sales/index.js +328 -324
- package/lib/solution/Sales/types.js +27 -33
- package/lib/solution/ScanOrder/index.js +832 -864
- package/lib/solution/ScanOrder/types.js +33 -34
- package/lib/solution/ScanOrder/utils.js +409 -374
- package/lib/solution/ShopDiscount/index.js +226 -232
- package/lib/solution/ShopDiscount/types.js +15 -37
- package/lib/solution/ShopDiscount/utils.js +300 -172
- package/lib/solution/UnifiedBookingSales/index.js +437 -487
- package/lib/solution/UnifiedBookingSales/types.js +12 -31
- package/lib/solution/VenueBooking/index.js +811 -879
- package/lib/solution/VenueBooking/types.js +19 -39
- package/lib/solution/VenueBooking/utils/dateSummary.js +27 -56
- package/lib/solution/VenueBooking/utils/resource.js +31 -54
- package/lib/solution/VenueBooking/utils/slotMerge.js +94 -107
- package/lib/solution/VenueBooking/utils/timeSlot.js +132 -132
- package/lib/solution/VenueBooking/utils.js +130 -67
- package/lib/solution/index.js +124 -41
- package/lib/store/createStore.js +32 -29
- package/lib/types/index.js +4 -16
- package/lib/utils/payment-number.js +26 -46
- package/lib/utils/task.js +36 -36
- package/lib/utils/watch.js +81 -47
- package/package.json +2 -1
|
@@ -1,80 +1,67 @@
|
|
|
1
|
-
|
|
2
|
-
var __defProp = Object.defineProperty;
|
|
3
|
-
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
-
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
-
var __getProtoOf = Object.getPrototypeOf;
|
|
6
|
-
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
7
|
-
var __export = (target, all) => {
|
|
8
|
-
for (var name in all)
|
|
9
|
-
__defProp(target, name, { get: all[name], enumerable: true });
|
|
10
|
-
};
|
|
11
|
-
var __copyProps = (to, from, except, desc) => {
|
|
12
|
-
if (from && typeof from === "object" || typeof from === "function") {
|
|
13
|
-
for (let key of __getOwnPropNames(from))
|
|
14
|
-
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
15
|
-
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
16
|
-
}
|
|
17
|
-
return to;
|
|
18
|
-
};
|
|
19
|
-
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
20
|
-
// If the importer is in node compatibility mode or this is not an ESM
|
|
21
|
-
// file that has been converted to a CommonJS file using a Babel-
|
|
22
|
-
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
23
|
-
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
24
|
-
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
25
|
-
mod
|
|
26
|
-
));
|
|
27
|
-
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
1
|
+
"use strict";
|
|
28
2
|
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
__export(order_exports, {
|
|
32
|
-
OrderModule: () => OrderModule
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
33
5
|
});
|
|
34
|
-
|
|
35
|
-
var
|
|
36
|
-
var
|
|
37
|
-
var
|
|
38
|
-
var
|
|
39
|
-
var
|
|
40
|
-
var
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
]);
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
6
|
+
exports.OrderModule = void 0;
|
|
7
|
+
var _lodashEs = require("lodash-es");
|
|
8
|
+
var _dayjs = _interopRequireDefault(require("dayjs"));
|
|
9
|
+
var _BaseModule = require("../../../modules/BaseModule");
|
|
10
|
+
var _orderCollectionIdentity = require("../../../modules/Order/utils/orderCollectionIdentity");
|
|
11
|
+
var _types = require("./types");
|
|
12
|
+
var _paymentUtils = require("../../../modules/Order/payment-utils");
|
|
13
|
+
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
|
14
|
+
/**
|
|
15
|
+
* SQLite 存储名称
|
|
16
|
+
*/
|
|
17
|
+
const INDEXDB_STORE_NAME = 'orders';
|
|
18
|
+
const ORDER_STORAGE_KEY_FIELD = '__order_storage_key';
|
|
19
|
+
const ORDER_LAST_FULL_FETCH_AT_STORAGE_KEY = 'server_order_last_full_fetch_at';
|
|
20
|
+
const ORDER_SYNC_THROTTLE_MS_STORAGE_KEY = 'order_sync_throttle_ms';
|
|
21
|
+
const DEFAULT_ORDER_SYNC_THROTTLE_MS = 2_000;
|
|
22
|
+
/** pubsub 回声写入去重窗口:业务 API 刚写入后,跳过同单 SQLite patch */
|
|
23
|
+
const ORDER_SQLITE_DEDUPE_MS = 15_000;
|
|
24
|
+
/** silentRefresh 最小间隔,避免 preload 后立即全量重拉 */
|
|
25
|
+
const ORDER_SILENT_REFRESH_MIN_INTERVAL_MS = 30_000;
|
|
26
|
+
/** 视为「业务侧刚写入」的来源,pubsub 回声可跳过 SQLite */
|
|
27
|
+
const ORDER_BUSINESS_WRITE_SOURCES = new Set(['upsertOrdersFromRemote', 'upsertPendingSyncOrders', 'upsertLocalDraftOrders', 'overwriteExistingOrder', 'markOrderSyncedByExternalSaleNumber', 'updateLocalPayment']);
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* 合并热路径全表去重自检开关:默认关闭。
|
|
31
|
+
* 开启后每次合并会额外对整张 store 做一次 O(n^2) 重复检测并告警,
|
|
32
|
+
* 仅用于排查增量身份索引是否遗漏了重复订单,不应在生产常开。
|
|
33
|
+
*/
|
|
34
|
+
const ORDER_MERGE_SELF_CHECK = false;
|
|
35
|
+
const PENDING_PAYMENT_WITHOUT_UNIQUE_MARKER = '__pisell_os_pending_payment_without_unique_number__';
|
|
36
|
+
const ORDER_AUTHORITATIVE_COLLECTION_FIELDS = ['products', 'bookings', 'payments'];
|
|
37
|
+
const ORDER_COLLECTION_KIND_BY_FIELD = {
|
|
38
|
+
products: 'product',
|
|
39
|
+
bookings: 'booking',
|
|
40
|
+
payments: 'payment'
|
|
63
41
|
};
|
|
64
|
-
|
|
42
|
+
/**
|
|
43
|
+
* Order 模块 - 基础框架
|
|
44
|
+
*/
|
|
45
|
+
class OrderModule extends _BaseModule.BaseModule {
|
|
46
|
+
defaultName = 'order';
|
|
47
|
+
defaultVersion = '1.0.0';
|
|
48
|
+
store;
|
|
49
|
+
dbManager;
|
|
50
|
+
orderDataSource;
|
|
51
|
+
pendingSyncMessages = [];
|
|
52
|
+
syncTimer;
|
|
53
|
+
isProcessingSyncBatch = false;
|
|
54
|
+
isIdlePhase = true;
|
|
55
|
+
logger; // LoggerManager 实例
|
|
56
|
+
storage;
|
|
57
|
+
orderSQLiteSaveQueue = Promise.resolve(); /** 按 storageKey 记录最近一次 SQLite 写入来源与时间,用于去重 */
|
|
58
|
+
recentSqliteWriteByStorageKey = new Map();
|
|
59
|
+
/** 最近一次 SSE 全量拉取完成时间 */
|
|
60
|
+
lastServerFullFetchAtMs = 0;
|
|
65
61
|
constructor(name, version) {
|
|
66
62
|
super(name, version);
|
|
67
|
-
this.defaultName = "order";
|
|
68
|
-
this.defaultVersion = "1.0.0";
|
|
69
|
-
this.pendingSyncMessages = [];
|
|
70
|
-
this.isProcessingSyncBatch = false;
|
|
71
|
-
this.isIdlePhase = true;
|
|
72
|
-
this.orderSQLiteSaveQueue = Promise.resolve();
|
|
73
|
-
/** 按 storageKey 记录最近一次 SQLite 写入来源与时间,用于去重 */
|
|
74
|
-
this.recentSqliteWriteByStorageKey = /* @__PURE__ */ new Map();
|
|
75
|
-
/** 最近一次 SSE 全量拉取完成时间 */
|
|
76
|
-
this.lastServerFullFetchAtMs = 0;
|
|
77
63
|
}
|
|
64
|
+
|
|
78
65
|
/**
|
|
79
66
|
* 广播订单变更事件,兼容全量列表订阅并额外携带本次变更订单。
|
|
80
67
|
*
|
|
@@ -82,23 +69,23 @@ var OrderModule = class extends import_BaseModule.BaseModule {
|
|
|
82
69
|
* await this.emitOrdersChanged([freshOrder], 'pubsub.bodyUpsert')
|
|
83
70
|
*/
|
|
84
71
|
async emitOrdersChanged(changedOrders = [], source) {
|
|
85
|
-
console.log("触发emitOrdersChanged");
|
|
86
72
|
const payload = {
|
|
87
73
|
list: this.store.list,
|
|
88
|
-
changedOrders: changedOrders.map(
|
|
74
|
+
changedOrders: changedOrders.map(order => this.normalizeOrderForMemoryList(order)),
|
|
89
75
|
source
|
|
90
76
|
};
|
|
91
|
-
await this.core.effects.emit(
|
|
77
|
+
await this.core.effects.emit(_types.OrderHooks.onOrdersChanged, payload);
|
|
92
78
|
}
|
|
79
|
+
|
|
93
80
|
/**
|
|
94
81
|
* 广播远端商品成员增删。监听器异常必须与订单落库和原有订单广播隔离。
|
|
95
82
|
*/
|
|
96
83
|
async emitRemoteProductMembershipChanges(changes) {
|
|
97
84
|
for (const payload of changes) {
|
|
98
85
|
try {
|
|
99
|
-
await this.core.effects.emit(
|
|
86
|
+
await this.core.effects.emit(_types.OrderHooks.onRemoteProductMembershipChanged, payload);
|
|
100
87
|
} catch (error) {
|
|
101
|
-
this.logError(
|
|
88
|
+
this.logError('广播远端订单商品成员变更失败', {
|
|
102
89
|
order_id: payload.order_id,
|
|
103
90
|
source: payload.source,
|
|
104
91
|
added_product_count: payload.added_product_keys.length,
|
|
@@ -109,38 +96,34 @@ var OrderModule = class extends import_BaseModule.BaseModule {
|
|
|
109
96
|
}
|
|
110
97
|
}
|
|
111
98
|
async initialize(core, options) {
|
|
112
|
-
var _a;
|
|
113
99
|
this.core = core;
|
|
114
|
-
this.store = options
|
|
115
|
-
if (!this.store.map)
|
|
116
|
-
|
|
117
|
-
const
|
|
118
|
-
const app = appPlugin == null ? void 0 : appPlugin.getApp();
|
|
100
|
+
this.store = options?.store;
|
|
101
|
+
if (!this.store.map) this.store.map = new Map();
|
|
102
|
+
const appPlugin = core.getPlugin('app');
|
|
103
|
+
const app = appPlugin?.getApp();
|
|
119
104
|
if (app) {
|
|
120
105
|
this.dbManager = app.sqlite;
|
|
121
106
|
this.logger = app.logger;
|
|
122
107
|
this.storage = app.storage;
|
|
123
108
|
this.updateCreateStore();
|
|
124
109
|
}
|
|
125
|
-
if (Array.isArray(
|
|
126
|
-
this.store.list = this.normalizeOrdersForMemoryList(
|
|
127
|
-
options.initialState.list,
|
|
128
|
-
"initialState"
|
|
129
|
-
);
|
|
110
|
+
if (Array.isArray(options?.initialState?.list)) {
|
|
111
|
+
this.store.list = this.normalizeOrdersForMemoryList(options.initialState.list, 'initialState');
|
|
130
112
|
this.syncOrdersMap();
|
|
131
|
-
this.emitOrdersChanged([],
|
|
113
|
+
this.emitOrdersChanged([], 'initialState');
|
|
132
114
|
} else {
|
|
133
115
|
this.store.list = [];
|
|
134
|
-
this.store.map =
|
|
116
|
+
this.store.map = new Map();
|
|
135
117
|
}
|
|
136
118
|
this.initOrderDataSource();
|
|
137
119
|
this.setupOrderSync();
|
|
138
|
-
this.logInfo(
|
|
120
|
+
this.logInfo('OrderServer模块初始化完成', {
|
|
139
121
|
hasDbManager: !!this.dbManager,
|
|
140
122
|
hasLogger: !!this.logger,
|
|
141
123
|
initialOrderCount: this.store.list.length
|
|
142
124
|
});
|
|
143
125
|
}
|
|
126
|
+
|
|
144
127
|
/**
|
|
145
128
|
* 记录信息日志
|
|
146
129
|
* @param title 日志标题
|
|
@@ -150,14 +133,16 @@ var OrderModule = class extends import_BaseModule.BaseModule {
|
|
|
150
133
|
try {
|
|
151
134
|
if (this.logger) {
|
|
152
135
|
this.logger.addLog({
|
|
153
|
-
type:
|
|
136
|
+
type: 'info',
|
|
154
137
|
title: `[OrderServerModule] ${title}`,
|
|
155
138
|
metadata: metadata || {}
|
|
156
139
|
});
|
|
157
140
|
}
|
|
158
141
|
} catch {
|
|
142
|
+
// 日志记录失败不影响主流程
|
|
159
143
|
}
|
|
160
144
|
}
|
|
145
|
+
|
|
161
146
|
/**
|
|
162
147
|
* 记录错误日志
|
|
163
148
|
* @param title 日志标题
|
|
@@ -167,36 +152,39 @@ var OrderModule = class extends import_BaseModule.BaseModule {
|
|
|
167
152
|
try {
|
|
168
153
|
if (this.logger) {
|
|
169
154
|
this.logger.addLog({
|
|
170
|
-
type:
|
|
155
|
+
type: 'error',
|
|
171
156
|
title: `[OrderServerModule] ${title}`,
|
|
172
157
|
metadata: metadata || {}
|
|
173
158
|
});
|
|
174
159
|
}
|
|
175
160
|
} catch {
|
|
161
|
+
// 日志记录失败不影响主流程
|
|
176
162
|
}
|
|
177
163
|
}
|
|
178
164
|
logWarning(title, metadata) {
|
|
179
165
|
try {
|
|
180
166
|
if (this.logger) {
|
|
181
167
|
this.logger.addLog({
|
|
182
|
-
type:
|
|
168
|
+
type: 'warning',
|
|
183
169
|
title: `[OrderServerModule] ${title}`,
|
|
184
170
|
metadata: metadata || {}
|
|
185
171
|
});
|
|
186
172
|
}
|
|
187
173
|
} catch {
|
|
174
|
+
// 日志记录失败不影响主流程
|
|
188
175
|
}
|
|
189
176
|
}
|
|
177
|
+
|
|
190
178
|
/**
|
|
191
179
|
* 在订单进入内存或 SQLite 前统一压缩子项强身份重复并补齐协议 UID。
|
|
192
180
|
* 仅记录计数与订单标识,不输出商品、预约、支付或客户明细。
|
|
193
181
|
*/
|
|
194
182
|
normalizeOrderCollectionsForIngress(order, source) {
|
|
195
|
-
const sourceOrder = (0,
|
|
183
|
+
const sourceOrder = (0, _lodashEs.cloneDeep)(order);
|
|
196
184
|
const sourceOrderRecord = sourceOrder;
|
|
197
185
|
if (Array.isArray(sourceOrderRecord.payments)) {
|
|
198
|
-
sourceOrderRecord.payments = sourceOrderRecord.payments.map(
|
|
199
|
-
if (!(0,
|
|
186
|
+
sourceOrderRecord.payments = sourceOrderRecord.payments.map(payment => {
|
|
187
|
+
if (!(0, _paymentUtils.isPendingOrderPayment)(payment) || (0, _paymentUtils.getOrderPaymentIdentityKeys)(payment).some(key => key.startsWith('unique_payment_number:'))) {
|
|
200
188
|
return payment;
|
|
201
189
|
}
|
|
202
190
|
return {
|
|
@@ -205,29 +193,30 @@ var OrderModule = class extends import_BaseModule.BaseModule {
|
|
|
205
193
|
};
|
|
206
194
|
});
|
|
207
195
|
}
|
|
208
|
-
const result = (0,
|
|
196
|
+
const result = (0, _orderCollectionIdentity.normalizeOrderCollections)(sourceOrder);
|
|
209
197
|
const normalizedOrderRecord = result.order;
|
|
210
198
|
if (Array.isArray(normalizedOrderRecord.payments)) {
|
|
211
|
-
normalizedOrderRecord.payments = normalizedOrderRecord.payments.map(
|
|
212
|
-
(payment)
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
199
|
+
normalizedOrderRecord.payments = normalizedOrderRecord.payments.map(payment => {
|
|
200
|
+
if (!payment?.[PENDING_PAYMENT_WITHOUT_UNIQUE_MARKER]) return payment;
|
|
201
|
+
const nextPayment = {
|
|
202
|
+
...payment
|
|
203
|
+
};
|
|
204
|
+
const metadata = {
|
|
205
|
+
...(nextPayment.metadata || {})
|
|
206
|
+
};
|
|
207
|
+
delete metadata.unique_payment_number;
|
|
208
|
+
delete nextPayment[PENDING_PAYMENT_WITHOUT_UNIQUE_MARKER];
|
|
209
|
+
nextPayment.metadata = metadata;
|
|
210
|
+
return nextPayment;
|
|
211
|
+
});
|
|
223
212
|
}
|
|
224
|
-
const kinds = [
|
|
225
|
-
const hasChanges = kinds.some(
|
|
213
|
+
const kinds = ['product', 'booking', 'payment'];
|
|
214
|
+
const hasChanges = kinds.some(kind => {
|
|
226
215
|
const stats = result.stats[kind];
|
|
227
216
|
return stats.backfilledUidCount > 0 || stats.collapsedDuplicateCount > 0 || stats.uidConflictCount > 0 || stats.anonymousRowCount > 0;
|
|
228
217
|
});
|
|
229
218
|
if (hasChanges) {
|
|
230
|
-
const summarize =
|
|
219
|
+
const summarize = stats => ({
|
|
231
220
|
backfilled_uid_count: stats.backfilledUidCount,
|
|
232
221
|
collapsed_duplicate_count: stats.collapsedDuplicateCount,
|
|
233
222
|
uid_conflict_count: stats.uidConflictCount,
|
|
@@ -235,57 +224,49 @@ var OrderModule = class extends import_BaseModule.BaseModule {
|
|
|
235
224
|
});
|
|
236
225
|
const context = {
|
|
237
226
|
source,
|
|
238
|
-
order_id:
|
|
239
|
-
external_sale_number:
|
|
227
|
+
order_id: order?.order_id ?? null,
|
|
228
|
+
external_sale_number: order?.external_sale_number ?? null,
|
|
240
229
|
products: summarize(result.stats.product),
|
|
241
230
|
bookings: summarize(result.stats.booking),
|
|
242
231
|
payments: summarize(result.stats.payment)
|
|
243
232
|
};
|
|
244
|
-
const hasUnsafeIdentity = kinds.some(
|
|
233
|
+
const hasUnsafeIdentity = kinds.some(kind => {
|
|
245
234
|
const stats = result.stats[kind];
|
|
246
235
|
return stats.uidConflictCount > 0 || stats.anonymousRowCount > 0;
|
|
247
236
|
});
|
|
248
237
|
if (hasUnsafeIdentity) {
|
|
249
|
-
this.logWarning(
|
|
238
|
+
this.logWarning('订单子项身份存在冲突或匿名行', context);
|
|
250
239
|
} else {
|
|
251
|
-
this.logInfo(
|
|
240
|
+
this.logInfo('订单子项身份已规范化', context);
|
|
252
241
|
}
|
|
253
242
|
}
|
|
254
243
|
return result.order;
|
|
255
244
|
}
|
|
245
|
+
|
|
256
246
|
/**
|
|
257
247
|
* incoming 决定权威成员集合;同一持久化行仍从 existing 补齐 incoming 省略的本地展示字段。
|
|
258
248
|
*/
|
|
259
249
|
mergeAuthoritativeOrderCollection(field, existing, incoming, uidRemaps = [], options = {}) {
|
|
260
250
|
const kind = ORDER_COLLECTION_KIND_BY_FIELD[field];
|
|
261
251
|
const existingItems = Array.isArray(existing) ? existing : [];
|
|
262
|
-
if (field ===
|
|
263
|
-
return (0,
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
const
|
|
271
|
-
|
|
272
|
-
);
|
|
273
|
-
if (!existingItem)
|
|
274
|
-
return (0, import_lodash_es.cloneDeep)(incomingItem);
|
|
275
|
-
const mergedItem = (0, import_orderCollectionIdentity.mergeOrderCollectionItems)(kind, existingItem, incomingItem);
|
|
276
|
-
const mergedUid = (0, import_orderCollectionIdentity.getOrderCollectionUid)(kind, mergedItem);
|
|
252
|
+
if (field === 'payments' && options.preserveIdentifiedPendingPayments) {
|
|
253
|
+
return (0, _paymentUtils.mergeOrderPaymentListsByIdentity)(existingItems, incoming, {
|
|
254
|
+
preserveUnmatchedExistingWhen: _paymentUtils.isIdentifiedPendingOrderPayment
|
|
255
|
+
});
|
|
256
|
+
}
|
|
257
|
+
return incoming.map(incomingItem => {
|
|
258
|
+
const existingItem = existingItems.find(item => (0, _orderCollectionIdentity.isSameOrderCollectionItem)(kind, item, incomingItem));
|
|
259
|
+
if (!existingItem) return (0, _lodashEs.cloneDeep)(incomingItem);
|
|
260
|
+
const mergedItem = (0, _orderCollectionIdentity.mergeOrderCollectionItems)(kind, existingItem, incomingItem);
|
|
261
|
+
const mergedUid = (0, _orderCollectionIdentity.getOrderCollectionUid)(kind, mergedItem);
|
|
277
262
|
if (mergedUid) {
|
|
278
|
-
for (const from of
|
|
279
|
-
(
|
|
280
|
-
(0, import_orderCollectionIdentity.getOrderCollectionReferenceUid)(kind, incomingItem)
|
|
281
|
-
])) {
|
|
282
|
-
if (!from || from === mergedUid)
|
|
283
|
-
continue;
|
|
263
|
+
for (const from of new Set([(0, _orderCollectionIdentity.getOrderCollectionReferenceUid)(kind, existingItem), (0, _orderCollectionIdentity.getOrderCollectionReferenceUid)(kind, incomingItem)])) {
|
|
264
|
+
if (!from || from === mergedUid) continue;
|
|
284
265
|
uidRemaps.push({
|
|
285
266
|
kind,
|
|
286
267
|
from,
|
|
287
268
|
to: mergedUid,
|
|
288
|
-
persistentId: (0,
|
|
269
|
+
persistentId: (0, _orderCollectionIdentity.getOrderCollectionPersistentId)(kind, incomingItem) || (0, _orderCollectionIdentity.getOrderCollectionPersistentId)(kind, existingItem)
|
|
289
270
|
});
|
|
290
271
|
}
|
|
291
272
|
}
|
|
@@ -293,25 +274,17 @@ var OrderModule = class extends import_BaseModule.BaseModule {
|
|
|
293
274
|
});
|
|
294
275
|
}
|
|
295
276
|
reconcileOverwriteOrderCollections(existing, incoming) {
|
|
296
|
-
const reconciled = (0,
|
|
277
|
+
const reconciled = (0, _lodashEs.cloneDeep)(incoming);
|
|
297
278
|
const existingRecord = existing;
|
|
298
279
|
const incomingRecord = incoming;
|
|
299
280
|
const uidRemaps = [];
|
|
300
281
|
for (const field of ORDER_AUTHORITATIVE_COLLECTION_FIELDS) {
|
|
301
|
-
if (!Array.isArray(incomingRecord[field]))
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
existingRecord[field],
|
|
306
|
-
incomingRecord[field],
|
|
307
|
-
uidRemaps
|
|
308
|
-
);
|
|
309
|
-
}
|
|
310
|
-
return this.normalizeOrderCollectionsForIngress(
|
|
311
|
-
(0, import_orderCollectionIdentity.applyOrderCollectionUidRemaps)(reconciled, uidRemaps),
|
|
312
|
-
"overwriteExistingOrder.reconciled"
|
|
313
|
-
);
|
|
282
|
+
if (!Array.isArray(incomingRecord[field])) continue;
|
|
283
|
+
reconciled[field] = this.mergeAuthoritativeOrderCollection(field, existingRecord[field], incomingRecord[field], uidRemaps);
|
|
284
|
+
}
|
|
285
|
+
return this.normalizeOrderCollectionsForIngress((0, _orderCollectionIdentity.applyOrderCollectionUidRemaps)(reconciled, uidRemaps), 'overwriteExistingOrder.reconciled');
|
|
314
286
|
}
|
|
287
|
+
|
|
315
288
|
/**
|
|
316
289
|
* 记录订单最近一次 SQLite 写入,供 pubsub 回声去重使用。
|
|
317
290
|
*
|
|
@@ -322,21 +295,27 @@ var OrderModule = class extends import_BaseModule.BaseModule {
|
|
|
322
295
|
const now = Date.now();
|
|
323
296
|
for (const order of orders) {
|
|
324
297
|
const storageKey = this.getOrderStorageKey(order);
|
|
325
|
-
if (!storageKey)
|
|
326
|
-
|
|
327
|
-
|
|
298
|
+
if (!storageKey) continue;
|
|
299
|
+
this.recentSqliteWriteByStorageKey.set(storageKey, {
|
|
300
|
+
source,
|
|
301
|
+
ts: now
|
|
302
|
+
});
|
|
328
303
|
}
|
|
329
304
|
}
|
|
305
|
+
|
|
330
306
|
/**
|
|
331
307
|
* 过滤 pubsub 触发的冗余 SQLite patch(业务 API 刚写入过的 update 回声)。
|
|
332
308
|
*
|
|
333
309
|
* @example
|
|
334
310
|
* const { toWrite, skipped } = this.filterRedundantPatchOrders('pubsub.bodyUpsert', orders, mergeActions)
|
|
335
311
|
*/
|
|
336
|
-
filterRedundantPatchOrders(source, orders, mergeActions, forceWriteStorageKeys =
|
|
337
|
-
const eligibleSkipSources =
|
|
312
|
+
filterRedundantPatchOrders(source, orders, mergeActions, forceWriteStorageKeys = new Set()) {
|
|
313
|
+
const eligibleSkipSources = new Set(['pubsub.bodyUpsert', 'pubsub.httpRefresh']);
|
|
338
314
|
if (!eligibleSkipSources.has(source)) {
|
|
339
|
-
return {
|
|
315
|
+
return {
|
|
316
|
+
toWrite: orders,
|
|
317
|
+
skipped: []
|
|
318
|
+
};
|
|
340
319
|
}
|
|
341
320
|
const now = Date.now();
|
|
342
321
|
const toWrite = [];
|
|
@@ -347,12 +326,16 @@ var OrderModule = class extends import_BaseModule.BaseModule {
|
|
|
347
326
|
toWrite.push(order);
|
|
348
327
|
continue;
|
|
349
328
|
}
|
|
350
|
-
const orderKey = order.order_id !==
|
|
351
|
-
const mergeAction = orderKey ? mergeActions[orderKey] :
|
|
352
|
-
|
|
329
|
+
const orderKey = order.order_id !== undefined && order.order_id !== null ? this.getIdKey(order.order_id) : '';
|
|
330
|
+
const mergeAction = orderKey ? mergeActions[orderKey] : undefined;
|
|
331
|
+
|
|
332
|
+
// 新增单必须落库,不参与去重
|
|
333
|
+
if (mergeAction === 'insert') {
|
|
353
334
|
toWrite.push(order);
|
|
354
335
|
continue;
|
|
355
336
|
}
|
|
337
|
+
|
|
338
|
+
// 远端商品成员确实发生增删时必须落库,避免内存已更新但 SQLite 仍被回声窗口跳过。
|
|
356
339
|
if (forceWriteStorageKeys.has(storageKey)) {
|
|
357
340
|
toWrite.push(order);
|
|
358
341
|
continue;
|
|
@@ -362,7 +345,7 @@ var OrderModule = class extends import_BaseModule.BaseModule {
|
|
|
362
345
|
skipped.push({
|
|
363
346
|
orderId: order.order_id ?? null,
|
|
364
347
|
storageKey,
|
|
365
|
-
reason:
|
|
348
|
+
reason: 'skip_pubsub_echo_after_business_write',
|
|
366
349
|
lastSource: recent.source,
|
|
367
350
|
elapsedMs: now - recent.ts
|
|
368
351
|
});
|
|
@@ -370,8 +353,12 @@ var OrderModule = class extends import_BaseModule.BaseModule {
|
|
|
370
353
|
}
|
|
371
354
|
toWrite.push(order);
|
|
372
355
|
}
|
|
373
|
-
return {
|
|
356
|
+
return {
|
|
357
|
+
toWrite,
|
|
358
|
+
skipped
|
|
359
|
+
};
|
|
374
360
|
}
|
|
361
|
+
|
|
375
362
|
/**
|
|
376
363
|
* 过滤 pubsub HTTP 增量拉取 ids:本地刚由业务 API 写入的单可跳过 HTTP。
|
|
377
364
|
*
|
|
@@ -389,8 +376,8 @@ var OrderModule = class extends import_BaseModule.BaseModule {
|
|
|
389
376
|
continue;
|
|
390
377
|
}
|
|
391
378
|
const storageKey = this.getOrderStorageKey(existing);
|
|
392
|
-
const recent = storageKey ? this.recentSqliteWriteByStorageKey.get(storageKey) :
|
|
393
|
-
if (recent && recent.source ===
|
|
379
|
+
const recent = storageKey ? this.recentSqliteWriteByStorageKey.get(storageKey) : undefined;
|
|
380
|
+
if (recent && recent.source === 'upsertOrdersFromRemote' && now - recent.ts < ORDER_SQLITE_DEDUPE_MS) {
|
|
394
381
|
skipped.push({
|
|
395
382
|
orderId: id,
|
|
396
383
|
lastSource: recent.source,
|
|
@@ -400,8 +387,12 @@ var OrderModule = class extends import_BaseModule.BaseModule {
|
|
|
400
387
|
}
|
|
401
388
|
toFetch.push(id);
|
|
402
389
|
}
|
|
403
|
-
return {
|
|
390
|
+
return {
|
|
391
|
+
toFetch,
|
|
392
|
+
skipped
|
|
393
|
+
};
|
|
404
394
|
}
|
|
395
|
+
|
|
405
396
|
/**
|
|
406
397
|
* 解析 pubsub 消息会走哪条同步分支,便于对照为何触发 HTTP 重拉或 body 直写。
|
|
407
398
|
*
|
|
@@ -410,25 +401,24 @@ var OrderModule = class extends import_BaseModule.BaseModule {
|
|
|
410
401
|
* // => { route: 'httpRefresh.batchIds', ... }
|
|
411
402
|
*/
|
|
412
403
|
resolveSyncMessageRoute(msg) {
|
|
413
|
-
var _a;
|
|
414
404
|
const hasBatchIds = Array.isArray(msg.ids) && msg.ids.length > 0;
|
|
415
405
|
const singleIdRaw = msg.id ?? msg.order_id;
|
|
416
|
-
const hasSingleId = !hasBatchIds && singleIdRaw !==
|
|
406
|
+
const hasSingleId = !hasBatchIds && singleIdRaw !== undefined && singleIdRaw !== null;
|
|
417
407
|
const normalizedBody = this.normalizeOrderSyncPayload(msg.body || msg.data);
|
|
418
408
|
if (hasBatchIds) {
|
|
419
409
|
return {
|
|
420
|
-
route:
|
|
410
|
+
route: 'httpRefresh.batchIds',
|
|
421
411
|
hasBatchIds: true,
|
|
422
412
|
hasSingleId: false,
|
|
423
413
|
hasNormalizedBody: !!normalizedBody,
|
|
424
414
|
singleId: null,
|
|
425
415
|
batchIds: msg.ids || [],
|
|
426
|
-
bodyOrderId:
|
|
416
|
+
bodyOrderId: normalizedBody?.order_id ?? null
|
|
427
417
|
};
|
|
428
418
|
}
|
|
429
419
|
if (hasSingleId && normalizedBody) {
|
|
430
420
|
return {
|
|
431
|
-
route:
|
|
421
|
+
route: 'bodyUpsert',
|
|
432
422
|
hasBatchIds: false,
|
|
433
423
|
hasSingleId: true,
|
|
434
424
|
hasNormalizedBody: true,
|
|
@@ -439,77 +429,70 @@ var OrderModule = class extends import_BaseModule.BaseModule {
|
|
|
439
429
|
}
|
|
440
430
|
if (hasSingleId) {
|
|
441
431
|
return {
|
|
442
|
-
route:
|
|
432
|
+
route: 'httpRefresh.singleId',
|
|
443
433
|
hasBatchIds: false,
|
|
444
434
|
hasSingleId: true,
|
|
445
435
|
hasNormalizedBody: !!normalizedBody,
|
|
446
436
|
singleId: singleIdRaw,
|
|
447
437
|
batchIds: [],
|
|
448
|
-
bodyOrderId:
|
|
438
|
+
bodyOrderId: normalizedBody?.order_id ?? null
|
|
449
439
|
};
|
|
450
440
|
}
|
|
451
|
-
if (
|
|
441
|
+
if (msg.relation_order_ids?.length) {
|
|
452
442
|
return {
|
|
453
|
-
route:
|
|
443
|
+
route: 'httpRefresh.relationIds',
|
|
454
444
|
hasBatchIds: false,
|
|
455
445
|
hasSingleId: false,
|
|
456
446
|
hasNormalizedBody: !!normalizedBody,
|
|
457
447
|
singleId: null,
|
|
458
448
|
batchIds: msg.relation_order_ids,
|
|
459
|
-
bodyOrderId:
|
|
449
|
+
bodyOrderId: normalizedBody?.order_id ?? null
|
|
460
450
|
};
|
|
461
451
|
}
|
|
462
452
|
return {
|
|
463
|
-
route:
|
|
453
|
+
route: 'noop',
|
|
464
454
|
hasBatchIds: false,
|
|
465
455
|
hasSingleId: false,
|
|
466
456
|
hasNormalizedBody: !!normalizedBody,
|
|
467
457
|
singleId: null,
|
|
468
458
|
batchIds: [],
|
|
469
|
-
bodyOrderId:
|
|
459
|
+
bodyOrderId: normalizedBody?.order_id ?? null
|
|
470
460
|
};
|
|
471
461
|
}
|
|
472
462
|
updateCreateStore() {
|
|
473
|
-
const appPlugin = this.core.getPlugin(
|
|
474
|
-
const app = appPlugin
|
|
463
|
+
const appPlugin = this.core.getPlugin('app');
|
|
464
|
+
const app = appPlugin?.getApp();
|
|
475
465
|
if (app) {
|
|
476
|
-
const coreData = app.data.store.getStore().getDataByModel(
|
|
477
|
-
if (coreData
|
|
478
|
-
const today = (0,
|
|
479
|
-
if (coreData
|
|
466
|
+
const coreData = app.data.store.getStore().getDataByModel('core');
|
|
467
|
+
if (coreData?.core) {
|
|
468
|
+
const today = (0, _dayjs.default)();
|
|
469
|
+
if (coreData?.core.operating_day_boundary) {
|
|
480
470
|
const operatingDayBoundary = coreData.core.operating_day_boundary;
|
|
481
|
-
if (operatingDayBoundary.type ===
|
|
482
|
-
const boundaryTime = String(operatingDayBoundary.time ||
|
|
483
|
-
const boundaryStart = (0,
|
|
471
|
+
if (operatingDayBoundary.type === 'start_time') {
|
|
472
|
+
const boundaryTime = String(operatingDayBoundary.time || '').trim();
|
|
473
|
+
const boundaryStart = (0, _dayjs.default)(`${today.format('YYYY-MM-DD')} ${boundaryTime}`);
|
|
484
474
|
if (boundaryStart.isValid()) {
|
|
485
475
|
const isAfterBoundary = today.isAfter(boundaryStart);
|
|
486
|
-
const queryStart = isAfterBoundary ? today.startOf(
|
|
487
|
-
const queryEnd = isAfterBoundary ? today.add(1,
|
|
476
|
+
const queryStart = isAfterBoundary ? today.startOf('day') : today.subtract(1, 'day').startOf('day');
|
|
477
|
+
const queryEnd = isAfterBoundary ? today.add(1, 'day').endOf('day') : today.endOf('day');
|
|
488
478
|
this.store.createdAtQuery = {
|
|
489
|
-
sales_time_between: [
|
|
490
|
-
queryStart.format("YYYY-MM-DD HH:mm:ss"),
|
|
491
|
-
queryEnd.format("YYYY-MM-DD HH:mm:ss")
|
|
492
|
-
]
|
|
479
|
+
sales_time_between: [queryStart.format('YYYY-MM-DD HH:mm:ss'), queryEnd.format('YYYY-MM-DD HH:mm:ss')]
|
|
493
480
|
};
|
|
494
481
|
} else {
|
|
482
|
+
// 边界时间异常时回退到自然日,避免 createdAtQuery 为空导致后续查询异常
|
|
495
483
|
this.store.createdAtQuery = {
|
|
496
|
-
sales_time_between: [
|
|
497
|
-
today.startOf("day").format("YYYY-MM-DD HH:mm:ss"),
|
|
498
|
-
today.endOf("day").format("YYYY-MM-DD HH:mm:ss")
|
|
499
|
-
]
|
|
484
|
+
sales_time_between: [today.startOf('day').format('YYYY-MM-DD HH:mm:ss'), today.endOf('day').format('YYYY-MM-DD HH:mm:ss')]
|
|
500
485
|
};
|
|
501
486
|
}
|
|
502
487
|
} else {
|
|
503
488
|
this.store.createdAtQuery = {
|
|
504
|
-
sales_time_between: [
|
|
505
|
-
today.startOf("day").format("YYYY-MM-DD HH:mm:ss"),
|
|
506
|
-
today.endOf("day").format("YYYY-MM-DD HH:mm:ss")
|
|
507
|
-
]
|
|
489
|
+
sales_time_between: [today.startOf('day').format('YYYY-MM-DD HH:mm:ss'), today.endOf('day').format('YYYY-MM-DD HH:mm:ss')]
|
|
508
490
|
};
|
|
509
491
|
}
|
|
510
492
|
} else {
|
|
493
|
+
// TODO: 这里后续可能需要使用店铺营业时间查询
|
|
511
494
|
this.store.createdAtQuery = {
|
|
512
|
-
sales_time_between: [today.startOf(
|
|
495
|
+
sales_time_between: [today.startOf('day').format('YYYY-MM-DD HH:mm:ss'), today.endOf('day').format('YYYY-MM-DD HH:mm:ss')]
|
|
513
496
|
};
|
|
514
497
|
}
|
|
515
498
|
}
|
|
@@ -519,17 +502,32 @@ var OrderModule = class extends import_BaseModule.BaseModule {
|
|
|
519
502
|
const raw = localStorage.getItem(ORDER_SYNC_THROTTLE_MS_STORAGE_KEY);
|
|
520
503
|
if (raw !== null) {
|
|
521
504
|
const parsed = Number(raw);
|
|
522
|
-
if (Number.isFinite(parsed) && parsed >= 0)
|
|
523
|
-
return parsed;
|
|
505
|
+
if (Number.isFinite(parsed) && parsed >= 0) return parsed;
|
|
524
506
|
}
|
|
525
507
|
return DEFAULT_ORDER_SYNC_THROTTLE_MS;
|
|
526
508
|
}
|
|
527
509
|
async preload() {
|
|
510
|
+
// const hasPulledInCurrentWindow = this.hasPulledOrdersToday()
|
|
511
|
+
// // 仅当缓存属于当前统计窗口时才直接复用,避免跨天/跨营业日窗口继续使用旧数据
|
|
512
|
+
// if (cachedOrders.length > 0 && hasPulledInCurrentWindow) {
|
|
513
|
+
// this.store.list = cloneDeep(cachedOrders)
|
|
514
|
+
// this.syncOrdersMap()
|
|
515
|
+
// this.core.effects.emit(OrderHooks.onOrdersChanged, this.store.list)
|
|
516
|
+
// return
|
|
517
|
+
// }
|
|
518
|
+
|
|
519
|
+
// if (hasPulledInCurrentWindow) {
|
|
520
|
+
// this.logInfo('当天已完成全量订单拉取,跳过本次全量拉取', {
|
|
521
|
+
// lastFullFetchAt: this.getStorageItem(ORDER_LAST_FULL_FETCH_AT_STORAGE_KEY),
|
|
522
|
+
// })
|
|
523
|
+
// return
|
|
524
|
+
// }
|
|
525
|
+
|
|
528
526
|
const orders = await this.loadOrdersByServer();
|
|
529
|
-
this.store.list = (0,
|
|
527
|
+
this.store.list = (0, _lodashEs.cloneDeep)(orders);
|
|
530
528
|
this.syncOrdersMap();
|
|
531
|
-
await this.emitOrdersChanged([],
|
|
532
|
-
await this.core.effects.emit(
|
|
529
|
+
await this.emitOrdersChanged([], 'preload');
|
|
530
|
+
await this.core.effects.emit(_types.OrderHooks.onOrdersSyncCompleted, null);
|
|
533
531
|
}
|
|
534
532
|
getOrders() {
|
|
535
533
|
return this.store.list;
|
|
@@ -537,13 +535,13 @@ var OrderModule = class extends import_BaseModule.BaseModule {
|
|
|
537
535
|
getOrderById(id) {
|
|
538
536
|
return this.store.map.get(this.getIdKey(id));
|
|
539
537
|
}
|
|
538
|
+
|
|
540
539
|
/** 按 order_id 查询内存订单。 */
|
|
541
540
|
getOrderByOrderId(orderId) {
|
|
542
541
|
return this.store.map.get(this.getIdKey(orderId));
|
|
543
542
|
}
|
|
544
543
|
buildLocalPaymentOrderLogContext(order) {
|
|
545
|
-
if (!order)
|
|
546
|
-
return {};
|
|
544
|
+
if (!order) return {};
|
|
547
545
|
return {
|
|
548
546
|
order_id: order.order_id ?? null,
|
|
549
547
|
external_sale_number: order.external_sale_number ?? null,
|
|
@@ -565,9 +563,8 @@ var OrderModule = class extends import_BaseModule.BaseModule {
|
|
|
565
563
|
getLocalOrderFromMemoryByLookup(lookup) {
|
|
566
564
|
const key = this.getIdKey(lookup);
|
|
567
565
|
const memoryOrderById = this.store.map.get(key);
|
|
568
|
-
if (memoryOrderById)
|
|
569
|
-
|
|
570
|
-
const memoryOrder = this.store.list.find((order) => this.isOrderLookupMatch(order, key));
|
|
566
|
+
if (memoryOrderById) return memoryOrderById;
|
|
567
|
+
const memoryOrder = this.store.list.find(order => this.isOrderLookupMatch(order, key));
|
|
571
568
|
return memoryOrder || null;
|
|
572
569
|
}
|
|
573
570
|
async getLocalOrderByLookup(lookup, options = {}) {
|
|
@@ -578,22 +575,22 @@ var OrderModule = class extends import_BaseModule.BaseModule {
|
|
|
578
575
|
memory_order_count: this.store.list.length
|
|
579
576
|
};
|
|
580
577
|
if (options.operationId) {
|
|
581
|
-
this.logInfo(
|
|
578
|
+
this.logInfo('getLocalOrderByLookup-localPayment-开始', logContext);
|
|
582
579
|
}
|
|
583
580
|
const memoryOrderById = this.store.map.get(key);
|
|
584
581
|
if (memoryOrderById) {
|
|
585
582
|
if (options.operationId) {
|
|
586
|
-
this.logInfo(
|
|
583
|
+
this.logInfo('getLocalOrderByLookup-localPayment-命中内存映射', {
|
|
587
584
|
...logContext,
|
|
588
585
|
...this.buildLocalPaymentOrderLogContext(memoryOrderById)
|
|
589
586
|
});
|
|
590
587
|
}
|
|
591
588
|
return memoryOrderById;
|
|
592
589
|
}
|
|
593
|
-
const memoryOrder = this.store.list.find(
|
|
590
|
+
const memoryOrder = this.store.list.find(order => this.isOrderLookupMatch(order, key));
|
|
594
591
|
if (memoryOrder) {
|
|
595
592
|
if (options.operationId) {
|
|
596
|
-
this.logInfo(
|
|
593
|
+
this.logInfo('getLocalOrderByLookup-localPayment-命中内存列表', {
|
|
597
594
|
...logContext,
|
|
598
595
|
...this.buildLocalPaymentOrderLogContext(memoryOrder)
|
|
599
596
|
});
|
|
@@ -601,27 +598,24 @@ var OrderModule = class extends import_BaseModule.BaseModule {
|
|
|
601
598
|
return memoryOrder;
|
|
602
599
|
}
|
|
603
600
|
if (options.operationId) {
|
|
604
|
-
this.logInfo(
|
|
601
|
+
this.logInfo('getLocalOrderByLookup-localPayment-开始读取SQLite', logContext);
|
|
605
602
|
}
|
|
606
603
|
const orders = await this.loadOrdersFromSQLite();
|
|
607
|
-
const localOrder = orders.find(
|
|
604
|
+
const localOrder = orders.find(order => this.isOrderLookupMatch(order, key));
|
|
608
605
|
if (!localOrder) {
|
|
609
606
|
if (options.operationId) {
|
|
610
|
-
this.logWarning(
|
|
607
|
+
this.logWarning('getLocalOrderByLookup-localPayment-SQLite未命中', {
|
|
611
608
|
...logContext,
|
|
612
609
|
sqlite_order_count: orders.length
|
|
613
610
|
});
|
|
614
611
|
}
|
|
615
612
|
return null;
|
|
616
613
|
}
|
|
617
|
-
this.store.list = this.normalizeOrdersForMemoryList(
|
|
618
|
-
orders,
|
|
619
|
-
"getLocalOrderByLookup.sqliteMemory"
|
|
620
|
-
);
|
|
614
|
+
this.store.list = this.normalizeOrdersForMemoryList(orders, 'getLocalOrderByLookup.sqliteMemory');
|
|
621
615
|
this.syncOrdersMap();
|
|
622
616
|
const normalizedOrder = this.normalizeOrderForMemoryList(localOrder);
|
|
623
617
|
if (options.operationId) {
|
|
624
|
-
this.logInfo(
|
|
618
|
+
this.logInfo('getLocalOrderByLookup-localPayment-SQLite命中', {
|
|
625
619
|
...logContext,
|
|
626
620
|
sqlite_order_count: orders.length,
|
|
627
621
|
...this.buildLocalPaymentOrderLogContext(normalizedOrder)
|
|
@@ -635,30 +629,26 @@ var OrderModule = class extends import_BaseModule.BaseModule {
|
|
|
635
629
|
sale_id: params.saleId,
|
|
636
630
|
payment_number: params.paymentNumber
|
|
637
631
|
};
|
|
638
|
-
this.logInfo(
|
|
632
|
+
this.logInfo('getLocalPayment-开始', logContext);
|
|
639
633
|
try {
|
|
640
634
|
const order = await this.getLocalOrderByLookup(params.saleId, {
|
|
641
635
|
operationId: params.operationId
|
|
642
636
|
});
|
|
643
637
|
if (!order) {
|
|
644
|
-
this.logWarning(
|
|
638
|
+
this.logWarning('getLocalPayment-订单未命中', logContext);
|
|
645
639
|
return null;
|
|
646
640
|
}
|
|
647
641
|
const payments = order.payments || [];
|
|
648
|
-
const match = (0,
|
|
649
|
-
payments,
|
|
650
|
-
params.paymentNumber,
|
|
651
|
-
"compat"
|
|
652
|
-
);
|
|
642
|
+
const match = (0, _paymentUtils.resolveOrderPaymentIdentity)(payments, params.paymentNumber, 'compat');
|
|
653
643
|
if (!match) {
|
|
654
|
-
this.logWarning(
|
|
644
|
+
this.logWarning('getLocalPayment-支付项未命中', {
|
|
655
645
|
...logContext,
|
|
656
646
|
...this.buildLocalPaymentOrderLogContext(order),
|
|
657
647
|
payments
|
|
658
648
|
});
|
|
659
649
|
return null;
|
|
660
650
|
}
|
|
661
|
-
this.logInfo(
|
|
651
|
+
this.logInfo('getLocalPayment-支付项命中', {
|
|
662
652
|
...logContext,
|
|
663
653
|
...this.buildLocalPaymentOrderLogContext(order),
|
|
664
654
|
matched_by: match.matchedBy,
|
|
@@ -672,7 +662,7 @@ var OrderModule = class extends import_BaseModule.BaseModule {
|
|
|
672
662
|
matchedBy: match.matchedBy
|
|
673
663
|
};
|
|
674
664
|
} catch (error) {
|
|
675
|
-
this.logError(
|
|
665
|
+
this.logError('getLocalPayment-查询失败', {
|
|
676
666
|
...logContext,
|
|
677
667
|
error: error instanceof Error ? error.message : String(error)
|
|
678
668
|
});
|
|
@@ -686,21 +676,21 @@ var OrderModule = class extends import_BaseModule.BaseModule {
|
|
|
686
676
|
payment_number: params.paymentNumber,
|
|
687
677
|
incoming_payment_update: params.updates
|
|
688
678
|
};
|
|
689
|
-
this.logInfo(
|
|
679
|
+
this.logInfo('updateLocalPayment-开始', logContext);
|
|
690
680
|
const found = await this.getLocalPayment(params);
|
|
691
681
|
if (!found) {
|
|
692
|
-
this.logWarning(
|
|
682
|
+
this.logWarning('updateLocalPayment-支付项未命中', logContext);
|
|
693
683
|
return null;
|
|
694
684
|
}
|
|
695
|
-
const previousPayment = (0,
|
|
696
|
-
const nextPayment = (0,
|
|
685
|
+
const previousPayment = (0, _lodashEs.cloneDeep)(found.payment);
|
|
686
|
+
const nextPayment = (0, _paymentUtils.mergeOrderPaymentRecord)(found.payment, {
|
|
697
687
|
...params.updates,
|
|
698
|
-
updated_at: params.updates.updated_at || (0,
|
|
688
|
+
updated_at: params.updates.updated_at || (0, _dayjs.default)().format('YYYY-MM-DD HH:mm:ss')
|
|
699
689
|
});
|
|
700
690
|
const nextOrder = {
|
|
701
691
|
...found.order,
|
|
702
692
|
payments: (found.order.payments || []).map((payment, index) => index === found.paymentIndex ? nextPayment : payment),
|
|
703
|
-
updated_at: (0,
|
|
693
|
+
updated_at: (0, _dayjs.default)().format('YYYY-MM-DD HH:mm:ss')
|
|
704
694
|
};
|
|
705
695
|
const paymentContext = {
|
|
706
696
|
...logContext,
|
|
@@ -710,11 +700,11 @@ var OrderModule = class extends import_BaseModule.BaseModule {
|
|
|
710
700
|
previous_payment: previousPayment,
|
|
711
701
|
payment: nextPayment
|
|
712
702
|
};
|
|
713
|
-
this.logInfo(
|
|
703
|
+
this.logInfo('updateLocalPayment-支付项合并完成', paymentContext);
|
|
714
704
|
const targetIndex = this.findOrderIndexByIdentity(this.store.list, found.order);
|
|
715
705
|
const shouldKeepInMemory = this.shouldKeepOrderInMemory(nextOrder);
|
|
716
706
|
if (targetIndex < 0 && shouldKeepInMemory) {
|
|
717
|
-
this.logError(
|
|
707
|
+
this.logError('updateLocalPayment-订单Store索引未命中', paymentContext);
|
|
718
708
|
return null;
|
|
719
709
|
}
|
|
720
710
|
const previousList = this.store.list;
|
|
@@ -722,19 +712,21 @@ var OrderModule = class extends import_BaseModule.BaseModule {
|
|
|
722
712
|
this.syncOrdersMap();
|
|
723
713
|
const storageOrder = this.withoutSyntheticDraftOrderId(nextOrder);
|
|
724
714
|
try {
|
|
725
|
-
this.logInfo(
|
|
715
|
+
this.logInfo('updateLocalPayment-SQLite写入开始', {
|
|
726
716
|
...paymentContext,
|
|
727
717
|
storage_order_id: storageOrder.order_id ?? null
|
|
728
718
|
});
|
|
729
|
-
await this.updateOrderInSQLite(storageOrder,
|
|
730
|
-
|
|
719
|
+
await this.updateOrderInSQLite(storageOrder, 'updateLocalPayment', {
|
|
720
|
+
throwOnError: true
|
|
721
|
+
});
|
|
722
|
+
this.logInfo('updateLocalPayment-SQLite写入完成', {
|
|
731
723
|
...paymentContext,
|
|
732
724
|
storage_order_id: storageOrder.order_id ?? null
|
|
733
725
|
});
|
|
734
726
|
} catch (error) {
|
|
735
727
|
this.store.list = previousList;
|
|
736
728
|
this.syncOrdersMap();
|
|
737
|
-
this.logError(
|
|
729
|
+
this.logError('updateLocalPayment-SQLite写入失败并回滚内存', {
|
|
738
730
|
...paymentContext,
|
|
739
731
|
storage_order_id: storageOrder.order_id ?? null,
|
|
740
732
|
error: error instanceof Error ? error.message : String(error)
|
|
@@ -742,28 +734,23 @@ var OrderModule = class extends import_BaseModule.BaseModule {
|
|
|
742
734
|
throw error;
|
|
743
735
|
}
|
|
744
736
|
if (shouldKeepInMemory || targetIndex >= 0) {
|
|
745
|
-
await this.emitOrdersChanged(
|
|
746
|
-
shouldKeepInMemory ? [nextOrder] : [],
|
|
747
|
-
shouldKeepInMemory ? "updateLocalPayment" : "updateLocalPayment.sqliteOnlyDraft"
|
|
748
|
-
);
|
|
737
|
+
await this.emitOrdersChanged(shouldKeepInMemory ? [nextOrder] : [], shouldKeepInMemory ? 'updateLocalPayment' : 'updateLocalPayment.sqliteOnlyDraft');
|
|
749
738
|
}
|
|
750
|
-
this.logInfo(
|
|
751
|
-
return {
|
|
739
|
+
this.logInfo('updateLocalPayment-变更事件完成', paymentContext);
|
|
740
|
+
return {
|
|
741
|
+
order: nextOrder,
|
|
742
|
+
payment: nextPayment,
|
|
743
|
+
previousPayment
|
|
744
|
+
};
|
|
752
745
|
}
|
|
753
746
|
async loadOrdersByServer() {
|
|
754
|
-
var _a;
|
|
755
747
|
let orderList = [];
|
|
756
|
-
const protectedOrdersBeforeRemoteFetch = (await this.loadOrdersFromSQLite()).filter(
|
|
757
|
-
|
|
758
|
-
);
|
|
759
|
-
this.logInfo("loadOrdersByServer-开始", {
|
|
748
|
+
const protectedOrdersBeforeRemoteFetch = (await this.loadOrdersFromSQLite()).filter(order => this.shouldProtectLocalOrderFromRemoteSnapshot(order));
|
|
749
|
+
this.logInfo('loadOrdersByServer-开始', {
|
|
760
750
|
hasOrderDataSource: !!this.orderDataSource,
|
|
761
|
-
query:
|
|
751
|
+
query: this.store?.createdAtQuery || null,
|
|
762
752
|
protectedLocalOrderCount: protectedOrdersBeforeRemoteFetch.length,
|
|
763
|
-
protectedPendingPaymentCount: protectedOrdersBeforeRemoteFetch.reduce(
|
|
764
|
-
(count, order) => count + this.getIdentifiedPendingPayments(order).length,
|
|
765
|
-
0
|
|
766
|
-
)
|
|
753
|
+
protectedPendingPaymentCount: protectedOrdersBeforeRemoteFetch.reduce((count, order) => count + this.getIdentifiedPendingPayments(order).length, 0)
|
|
767
754
|
});
|
|
768
755
|
if (this.orderDataSource) {
|
|
769
756
|
try {
|
|
@@ -773,33 +760,27 @@ var OrderModule = class extends import_BaseModule.BaseModule {
|
|
|
773
760
|
}
|
|
774
761
|
});
|
|
775
762
|
orderList = data || [];
|
|
776
|
-
this.logInfo(
|
|
763
|
+
this.logInfo('loadOrdersByServer-SSE拉取成功', {
|
|
777
764
|
count: orderList.length
|
|
778
765
|
});
|
|
779
766
|
this.markOrderPulledAtNow();
|
|
780
767
|
this.lastServerFullFetchAtMs = Date.now();
|
|
781
768
|
} catch {
|
|
782
|
-
this.logInfo(
|
|
769
|
+
this.logInfo('loadOrdersByServer-SSE拉取失败,保持当前内存订单');
|
|
783
770
|
return this.store.list;
|
|
784
771
|
}
|
|
785
772
|
}
|
|
786
|
-
const mergedOrders = await this.replaceOrdersSnapshotInSQLite(
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
protectedOrdersBeforeRemoteFetch
|
|
790
|
-
);
|
|
791
|
-
const memoryOrders = this.normalizeOrdersForMemoryList(
|
|
792
|
-
mergedOrders,
|
|
793
|
-
"loadOrdersByServer.memory"
|
|
794
|
-
);
|
|
795
|
-
this.logInfo("loadOrdersByServer-结束", {
|
|
773
|
+
const mergedOrders = await this.replaceOrdersSnapshotInSQLite(orderList, 'loadOrdersByServer', protectedOrdersBeforeRemoteFetch);
|
|
774
|
+
const memoryOrders = this.normalizeOrdersForMemoryList(mergedOrders, 'loadOrdersByServer.memory');
|
|
775
|
+
this.logInfo('loadOrdersByServer-结束', {
|
|
796
776
|
count: memoryOrders.length,
|
|
797
777
|
persistedCount: mergedOrders.length,
|
|
798
778
|
sqliteOnlyDraftCount: mergedOrders.length - memoryOrders.length
|
|
799
779
|
});
|
|
800
|
-
await this.core.effects.emit(
|
|
780
|
+
await this.core.effects.emit(_types.OrderHooks.onOrdersLoaded, memoryOrders);
|
|
801
781
|
return memoryOrders;
|
|
802
782
|
}
|
|
783
|
+
|
|
803
784
|
/**
|
|
804
785
|
* 静默全量刷新:后台重新拉取全量 SSE 订单数据并更新本地
|
|
805
786
|
* 拿到完整数据后一次性替换 store,触发订单变更与同步完成事件
|
|
@@ -811,38 +792,39 @@ var OrderModule = class extends import_BaseModule.BaseModule {
|
|
|
811
792
|
if (this.lastServerFullFetchAtMs > 0 && elapsedSinceFullFetch < ORDER_SILENT_REFRESH_MIN_INTERVAL_MS) {
|
|
812
793
|
return this.store.list;
|
|
813
794
|
}
|
|
814
|
-
this.logInfo(
|
|
795
|
+
this.logInfo('silentRefresh 开始');
|
|
815
796
|
try {
|
|
797
|
+
// 刷新前,需要更新当前营业日边界(store.createdAtQuery)
|
|
816
798
|
this.updateCreateStore();
|
|
817
799
|
const orders = await this.loadOrdersByServer();
|
|
818
|
-
this.store.list = (0,
|
|
800
|
+
this.store.list = (0, _lodashEs.cloneDeep)(orders);
|
|
819
801
|
this.syncOrdersMap();
|
|
820
|
-
await this.emitOrdersChanged([],
|
|
821
|
-
await this.core.effects.emit(
|
|
822
|
-
this.logInfo(
|
|
802
|
+
await this.emitOrdersChanged([], 'silentRefresh');
|
|
803
|
+
await this.core.effects.emit(_types.OrderHooks.onOrdersSyncCompleted, null);
|
|
804
|
+
this.logInfo('silentRefresh 完成', {
|
|
823
805
|
orderCount: orders.length,
|
|
824
806
|
duration: `${Date.now() - startTime}ms`
|
|
825
807
|
});
|
|
826
808
|
return this.store.list;
|
|
827
809
|
} catch (error) {
|
|
828
810
|
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
829
|
-
this.logError(
|
|
811
|
+
this.logError('silentRefresh 失败', {
|
|
830
812
|
duration: `${Date.now() - startTime}ms`,
|
|
831
813
|
error: errorMessage
|
|
832
814
|
});
|
|
833
815
|
return this.store.list;
|
|
834
816
|
}
|
|
835
817
|
}
|
|
836
|
-
normalizeOrderForMemoryList(order, source =
|
|
818
|
+
normalizeOrderForMemoryList(order, source = 'normalizeOrderForMemoryList') {
|
|
837
819
|
const normalizedOrder = this.normalizeOrderCollectionsForIngress(order, source);
|
|
838
|
-
const externalSaleNumber = normalizedOrder
|
|
820
|
+
const externalSaleNumber = normalizedOrder?.external_sale_number;
|
|
839
821
|
if (this.isDraftOrder(normalizedOrder) && !this.hasRealOrderId(normalizedOrder)) {
|
|
840
822
|
return normalizedOrder;
|
|
841
823
|
}
|
|
842
|
-
if (
|
|
824
|
+
if (normalizedOrder?.order_id !== undefined && normalizedOrder?.order_id !== null && normalizedOrder?.order_id !== '') {
|
|
843
825
|
return normalizedOrder;
|
|
844
826
|
}
|
|
845
|
-
if (externalSaleNumber ===
|
|
827
|
+
if (externalSaleNumber === undefined || externalSaleNumber === null || externalSaleNumber === '') {
|
|
846
828
|
return normalizedOrder;
|
|
847
829
|
}
|
|
848
830
|
return {
|
|
@@ -851,11 +833,10 @@ var OrderModule = class extends import_BaseModule.BaseModule {
|
|
|
851
833
|
};
|
|
852
834
|
}
|
|
853
835
|
normalizeOrdersForMemoryList(orders, source) {
|
|
854
|
-
return orders.map(
|
|
836
|
+
return orders.map(order => this.normalizeOrderForMemoryList(order, source)).filter(order => this.shouldKeepOrderInMemory(order));
|
|
855
837
|
}
|
|
856
838
|
hasRealOrderId(order) {
|
|
857
|
-
if (!order || this.isBlankIdentityValue(order.order_id))
|
|
858
|
-
return false;
|
|
839
|
+
if (!order || this.isBlankIdentityValue(order.order_id)) return false;
|
|
859
840
|
const externalSaleNumber = order.external_sale_number;
|
|
860
841
|
return !(this.isDraftOrder(order) && !this.isBlankIdentityValue(externalSaleNumber) && String(order.order_id) === String(externalSaleNumber));
|
|
861
842
|
}
|
|
@@ -864,67 +845,75 @@ var OrderModule = class extends import_BaseModule.BaseModule {
|
|
|
864
845
|
}
|
|
865
846
|
withoutSyntheticDraftOrderId(order) {
|
|
866
847
|
const externalSaleNumber = order.external_sale_number;
|
|
867
|
-
if (Number(order.is_draft_order || 0) !== 1 || order.order_id ===
|
|
848
|
+
if (Number(order.is_draft_order || 0) !== 1 || order.order_id === undefined || externalSaleNumber === undefined || String(order.order_id) !== String(externalSaleNumber)) {
|
|
868
849
|
return order;
|
|
869
850
|
}
|
|
870
|
-
const next = {
|
|
851
|
+
const next = {
|
|
852
|
+
...order
|
|
853
|
+
};
|
|
871
854
|
delete next.order_id;
|
|
872
855
|
return next;
|
|
873
856
|
}
|
|
857
|
+
|
|
874
858
|
/**
|
|
875
859
|
* 仅覆盖本地已存在的订单;不存在则直接跳过,不落库、不 emit。
|
|
876
860
|
* 适用于"按 order_id 主动刷新本地订单详情"的代理场景。
|
|
877
861
|
*/
|
|
878
862
|
async overwriteExistingOrder(fresh) {
|
|
879
|
-
const orderId = fresh
|
|
880
|
-
if (orderId ===
|
|
881
|
-
this.logError(
|
|
882
|
-
return {
|
|
863
|
+
const orderId = fresh?.order_id;
|
|
864
|
+
if (orderId === undefined || orderId === null) {
|
|
865
|
+
this.logError('overwriteExistingOrder-入参缺少 order_id');
|
|
866
|
+
return {
|
|
867
|
+
overwritten: false
|
|
868
|
+
};
|
|
883
869
|
}
|
|
884
870
|
const key = this.getIdKey(orderId);
|
|
885
871
|
if (!this.store.map.has(key)) {
|
|
886
|
-
this.logInfo(
|
|
887
|
-
|
|
872
|
+
this.logInfo('overwriteExistingOrder-本地不存在该订单,跳过', {
|
|
873
|
+
orderId
|
|
874
|
+
});
|
|
875
|
+
return {
|
|
876
|
+
overwritten: false
|
|
877
|
+
};
|
|
888
878
|
}
|
|
889
|
-
this.logInfo(
|
|
879
|
+
this.logInfo('overwriteExistingOrder-开始覆盖', {
|
|
890
880
|
orderId,
|
|
891
881
|
storeOrderCountBefore: this.store.list.length
|
|
892
882
|
});
|
|
893
|
-
const normalizedFresh = this.normalizeOrderForMemoryList(
|
|
894
|
-
fresh,
|
|
895
|
-
"overwriteExistingOrder.incoming"
|
|
896
|
-
);
|
|
883
|
+
const normalizedFresh = this.normalizeOrderForMemoryList(fresh, 'overwriteExistingOrder.incoming');
|
|
897
884
|
const existingOrder = this.store.map.get(key);
|
|
898
|
-
const reconciledFresh = this.reconcileOverwriteOrderCollections(
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
const id = order == null ? void 0 : order.order_id;
|
|
904
|
-
if (id === void 0 || id === null) {
|
|
905
|
-
return this.normalizeOrderForMemoryList(order, "overwriteExistingOrder.existingMemory");
|
|
885
|
+
const reconciledFresh = this.reconcileOverwriteOrderCollections(existingOrder, normalizedFresh);
|
|
886
|
+
this.store.list = this.store.list.map(order => {
|
|
887
|
+
const id = order?.order_id;
|
|
888
|
+
if (id === undefined || id === null) {
|
|
889
|
+
return this.normalizeOrderForMemoryList(order, 'overwriteExistingOrder.existingMemory');
|
|
906
890
|
}
|
|
907
|
-
return this.getIdKey(id) === key ? reconciledFresh : this.normalizeOrderForMemoryList(order,
|
|
891
|
+
return this.getIdKey(id) === key ? reconciledFresh : this.normalizeOrderForMemoryList(order, 'overwriteExistingOrder.existingMemory');
|
|
908
892
|
});
|
|
909
893
|
this.syncOrdersMap();
|
|
910
|
-
await this.updateOrderInSQLite(reconciledFresh,
|
|
911
|
-
this.logInfo(
|
|
894
|
+
await this.updateOrderInSQLite(reconciledFresh, 'overwriteExistingOrder');
|
|
895
|
+
this.logInfo('overwriteExistingOrder-结束', {
|
|
912
896
|
orderId,
|
|
913
897
|
storeOrderCountAfter: this.store.list.length
|
|
914
898
|
});
|
|
915
|
-
await this.emitOrdersChanged([reconciledFresh],
|
|
916
|
-
return {
|
|
899
|
+
await this.emitOrdersChanged([reconciledFresh], 'overwriteExistingOrder');
|
|
900
|
+
return {
|
|
901
|
+
overwritten: true
|
|
902
|
+
};
|
|
917
903
|
}
|
|
904
|
+
|
|
918
905
|
/**
|
|
919
906
|
* 将远端拉取的订单合并进本地 store(已存在则更新,不存在则追加),落库并 emit onOrdersChanged。
|
|
920
907
|
* 供 /update/localOrder 等在「本地尚无该单」时写入新建单。
|
|
921
908
|
*/
|
|
922
|
-
async upsertOrdersFromRemote(freshOrders, source =
|
|
923
|
-
if (!
|
|
924
|
-
this.logInfo(
|
|
909
|
+
async upsertOrdersFromRemote(freshOrders, source = 'upsertOrdersFromRemote') {
|
|
910
|
+
if (!freshOrders?.length) {
|
|
911
|
+
this.logInfo('upsertOrdersFromRemote-订单列表为空', {
|
|
912
|
+
source
|
|
913
|
+
});
|
|
925
914
|
return;
|
|
926
915
|
}
|
|
927
|
-
this.logInfo(
|
|
916
|
+
this.logInfo('upsertOrdersFromRemote-开始', {
|
|
928
917
|
count: freshOrders.length,
|
|
929
918
|
source
|
|
930
919
|
});
|
|
@@ -932,106 +921,100 @@ var OrderModule = class extends import_BaseModule.BaseModule {
|
|
|
932
921
|
}
|
|
933
922
|
async markOrderSyncedByExternalSaleNumber(params) {
|
|
934
923
|
const externalSaleNumber = params.externalSaleNumber;
|
|
935
|
-
if (externalSaleNumber ===
|
|
936
|
-
this.logError(
|
|
937
|
-
return {
|
|
924
|
+
if (externalSaleNumber === undefined || externalSaleNumber === null || externalSaleNumber === '') {
|
|
925
|
+
this.logError('markOrderSyncedByExternalSaleNumber-缺少 external_sale_number');
|
|
926
|
+
return {
|
|
927
|
+
updated: false
|
|
928
|
+
};
|
|
938
929
|
}
|
|
939
930
|
const key = this.getIdKey(externalSaleNumber);
|
|
940
|
-
const targetIndex = this.store.list.findIndex(
|
|
941
|
-
const value = order
|
|
942
|
-
if (value ===
|
|
943
|
-
return false;
|
|
931
|
+
const targetIndex = this.store.list.findIndex(order => {
|
|
932
|
+
const value = order?.external_sale_number;
|
|
933
|
+
if (value === undefined || value === null || value === '') return false;
|
|
944
934
|
return this.getIdKey(value) === key;
|
|
945
935
|
});
|
|
946
936
|
if (targetIndex < 0) {
|
|
947
|
-
this.logInfo(
|
|
937
|
+
this.logInfo('markOrderSyncedByExternalSaleNumber-本地订单不存在,跳过', {
|
|
948
938
|
external_sale_number: externalSaleNumber
|
|
949
939
|
});
|
|
950
|
-
return {
|
|
940
|
+
return {
|
|
941
|
+
updated: false
|
|
942
|
+
};
|
|
951
943
|
}
|
|
952
944
|
const existing = this.store.list[targetIndex];
|
|
953
945
|
const nextOrder = this.normalizeRemoteSyncedOrder({
|
|
954
946
|
...existing,
|
|
955
|
-
...params.patch || {},
|
|
947
|
+
...(params.patch || {}),
|
|
956
948
|
external_sale_number: existing.external_sale_number ?? externalSaleNumber,
|
|
957
949
|
need_sync: 0,
|
|
958
950
|
is_draft_order: 0
|
|
959
951
|
});
|
|
960
952
|
const storageKey = this.getOrderStorageKey(nextOrder) || key;
|
|
961
953
|
this.store.list = this.store.list.map((order, index) => {
|
|
962
|
-
if (index !== targetIndex)
|
|
963
|
-
return order;
|
|
954
|
+
if (index !== targetIndex) return order;
|
|
964
955
|
return nextOrder;
|
|
965
956
|
});
|
|
966
957
|
this.syncOrdersMap();
|
|
967
|
-
await this.patchOrdersInSQLite(
|
|
968
|
-
[
|
|
969
|
-
|
|
970
|
-
|
|
971
|
-
);
|
|
972
|
-
this.logInfo("markOrderSyncedByExternalSaleNumber-完成", {
|
|
958
|
+
await this.patchOrdersInSQLite([nextOrder], 'markOrderSyncedByExternalSaleNumber', {
|
|
959
|
+
[storageKey]: 'update'
|
|
960
|
+
});
|
|
961
|
+
this.logInfo('markOrderSyncedByExternalSaleNumber-完成', {
|
|
973
962
|
external_sale_number: externalSaleNumber,
|
|
974
963
|
order_id: nextOrder.order_id ?? null
|
|
975
964
|
});
|
|
976
|
-
await this.emitOrdersChanged([nextOrder],
|
|
977
|
-
return {
|
|
965
|
+
await this.emitOrdersChanged([nextOrder], 'markOrderSyncedByExternalSaleNumber');
|
|
966
|
+
return {
|
|
967
|
+
updated: true,
|
|
968
|
+
order: nextOrder
|
|
969
|
+
};
|
|
978
970
|
}
|
|
971
|
+
|
|
979
972
|
/**
|
|
980
973
|
* 将本地待同步订单按 SQLite storage key 合并进 store 并落库。
|
|
981
974
|
* checkout 离线兜底可能没有 order_id,但通常会有 external_sale_number。
|
|
982
975
|
*/
|
|
983
976
|
async upsertPendingSyncOrders(pendingOrders, options = {}) {
|
|
984
|
-
if (!
|
|
985
|
-
this.logInfo(
|
|
977
|
+
if (!pendingOrders?.length) {
|
|
978
|
+
this.logInfo('upsertPendingSyncOrders-订单列表为空', {});
|
|
986
979
|
return;
|
|
987
980
|
}
|
|
988
|
-
const pendingMap =
|
|
989
|
-
const memoryPendingMap =
|
|
981
|
+
const pendingMap = new Map();
|
|
982
|
+
const memoryPendingMap = new Map();
|
|
990
983
|
for (const order of pendingOrders) {
|
|
991
|
-
const normalizedOrder = this.normalizeOrderCollectionsForIngress(
|
|
992
|
-
order,
|
|
993
|
-
"upsertPendingSyncOrders.incoming"
|
|
994
|
-
);
|
|
984
|
+
const normalizedOrder = this.normalizeOrderCollectionsForIngress(order, 'upsertPendingSyncOrders.incoming');
|
|
995
985
|
const pendingOrder = {
|
|
996
986
|
...normalizedOrder,
|
|
997
987
|
need_sync: 1,
|
|
998
988
|
is_draft_order: 0
|
|
999
989
|
};
|
|
1000
990
|
const storageKey = this.getOrderStorageKey(pendingOrder);
|
|
1001
|
-
if (!storageKey)
|
|
1002
|
-
continue;
|
|
991
|
+
if (!storageKey) continue;
|
|
1003
992
|
pendingMap.set(storageKey, pendingOrder);
|
|
1004
|
-
memoryPendingMap.set(
|
|
1005
|
-
storageKey,
|
|
1006
|
-
this.normalizeOrderForMemoryList(
|
|
1007
|
-
pendingOrder,
|
|
1008
|
-
"upsertPendingSyncOrders.memory"
|
|
1009
|
-
)
|
|
1010
|
-
);
|
|
993
|
+
memoryPendingMap.set(storageKey, this.normalizeOrderForMemoryList(pendingOrder, 'upsertPendingSyncOrders.memory'));
|
|
1011
994
|
}
|
|
1012
995
|
if (pendingMap.size === 0) {
|
|
1013
|
-
this.logError(
|
|
996
|
+
this.logError('upsertPendingSyncOrders-订单缺少可落库标识', {
|
|
1014
997
|
count: pendingOrders.length
|
|
1015
998
|
});
|
|
1016
999
|
return;
|
|
1017
1000
|
}
|
|
1018
|
-
this.logInfo(
|
|
1001
|
+
this.logInfo('upsertPendingSyncOrders-开始', {
|
|
1002
|
+
count: pendingMap.size
|
|
1003
|
+
});
|
|
1019
1004
|
const mergeActions = {};
|
|
1020
|
-
const updatedList = this.store.list.map(
|
|
1021
|
-
|
|
1022
|
-
|
|
1023
|
-
|
|
1005
|
+
const updatedList = this.store.list.map(order => this.normalizeOrderForMemoryList(order, 'upsertPendingSyncOrders.existingMemory'));
|
|
1006
|
+
|
|
1007
|
+
// 与 mergeOrdersToStore 一致:基于身份键(field:value)建立索引,把「查找已存在订单」
|
|
1008
|
+
// 从 O(n) 线性扫描降为 O(1),整体复杂度由 O(n^2) 降为 O(n + m)。
|
|
1009
|
+
const identityKeyToIndex = new Map();
|
|
1024
1010
|
for (let index = 0; index < updatedList.length; index += 1) {
|
|
1025
1011
|
for (const key of this.getOrderIdentityMatchKeys(updatedList[index])) {
|
|
1026
|
-
|
|
1027
|
-
|
|
1012
|
+
// 同一 key 命中多张时保留最靠前的下标,与原 findIndex「取首个匹配」一致
|
|
1013
|
+
if (!identityKeyToIndex.has(key)) identityKeyToIndex.set(key, index);
|
|
1028
1014
|
}
|
|
1029
1015
|
}
|
|
1030
1016
|
for (const [storageKey, pendingOrder] of memoryPendingMap.entries()) {
|
|
1031
|
-
const matchIndex = this.lookupOrderIndexByIdentityKeys(
|
|
1032
|
-
identityKeyToIndex,
|
|
1033
|
-
this.getOrderIdentityMatchKeys(pendingOrder)
|
|
1034
|
-
);
|
|
1017
|
+
const matchIndex = this.lookupOrderIndexByIdentityKeys(identityKeyToIndex, this.getOrderIdentityMatchKeys(pendingOrder));
|
|
1035
1018
|
if (matchIndex >= 0) {
|
|
1036
1019
|
const existingOrder = updatedList[matchIndex];
|
|
1037
1020
|
const mergedOrder = this.mergeOrderRecords(existingOrder, pendingOrder);
|
|
@@ -1043,53 +1026,48 @@ var OrderModule = class extends import_BaseModule.BaseModule {
|
|
|
1043
1026
|
updatedList[matchIndex] = nextOrder;
|
|
1044
1027
|
const pendingSnapshot = pendingMap.get(storageKey);
|
|
1045
1028
|
if (pendingSnapshot) {
|
|
1046
|
-
pendingMap.set(
|
|
1047
|
-
storageKey,
|
|
1048
|
-
this.reconcileOverwriteOrderCollections(existingOrder, pendingSnapshot)
|
|
1049
|
-
);
|
|
1029
|
+
pendingMap.set(storageKey, this.reconcileOverwriteOrderCollections(existingOrder, pendingSnapshot));
|
|
1050
1030
|
}
|
|
1031
|
+
// 合并后身份字段可能新增(如挂单拿到 order_id),同步登记,供同批后续 pending 命中同一槽位
|
|
1051
1032
|
this.registerOrderIdentityKeys(identityKeyToIndex, nextOrder, matchIndex);
|
|
1052
|
-
mergeActions[storageKey] =
|
|
1033
|
+
mergeActions[storageKey] = 'update';
|
|
1053
1034
|
continue;
|
|
1054
1035
|
}
|
|
1055
1036
|
const insertIndex = updatedList.length;
|
|
1056
1037
|
updatedList.push(pendingOrder);
|
|
1057
1038
|
this.registerOrderIdentityKeys(identityKeyToIndex, pendingOrder, insertIndex);
|
|
1058
|
-
mergeActions[storageKey] =
|
|
1039
|
+
mergeActions[storageKey] = 'insert';
|
|
1059
1040
|
}
|
|
1060
1041
|
const patchedOrders = [...pendingMap.values()];
|
|
1042
|
+
// 增量索引已保证 store 去重不变量,无需再对全表做 O(n^2) 压缩;仅自检开关开启时兜底。
|
|
1061
1043
|
const previousList = this.store.list;
|
|
1062
|
-
this.store.list = this.runOrderStoreSelfCheck(updatedList,
|
|
1044
|
+
this.store.list = this.runOrderStoreSelfCheck(updatedList, 'upsertPendingSyncOrders.store');
|
|
1063
1045
|
this.syncOrdersMap();
|
|
1064
1046
|
try {
|
|
1065
|
-
await this.patchOrdersInSQLite(
|
|
1066
|
-
patchedOrders,
|
|
1067
|
-
"upsertPendingSyncOrders",
|
|
1068
|
-
mergeActions,
|
|
1069
|
-
options
|
|
1070
|
-
);
|
|
1047
|
+
await this.patchOrdersInSQLite(patchedOrders, 'upsertPendingSyncOrders', mergeActions, options);
|
|
1071
1048
|
} catch (error) {
|
|
1072
1049
|
this.store.list = previousList;
|
|
1073
1050
|
this.syncOrdersMap();
|
|
1074
1051
|
throw error;
|
|
1075
1052
|
}
|
|
1076
|
-
this.logInfo(
|
|
1053
|
+
this.logInfo('upsertPendingSyncOrders-结束', {
|
|
1077
1054
|
count: patchedOrders.length,
|
|
1078
1055
|
storeOrderCountAfter: this.store.list.length
|
|
1079
1056
|
});
|
|
1080
|
-
await this.emitOrdersChanged(patchedOrders,
|
|
1057
|
+
await this.emitOrdersChanged(patchedOrders, 'upsertPendingSyncOrders');
|
|
1081
1058
|
}
|
|
1059
|
+
|
|
1082
1060
|
/**
|
|
1083
1061
|
* 将 POS 草稿订单按 SQLite storage key 落库。
|
|
1084
1062
|
* 只有具备真实 order_id 的草稿进入 store;其余草稿仅供本地恢复查询。
|
|
1085
1063
|
*/
|
|
1086
1064
|
async upsertLocalDraftOrders(draftOrders) {
|
|
1087
|
-
if (!
|
|
1088
|
-
this.logInfo(
|
|
1065
|
+
if (!draftOrders?.length) {
|
|
1066
|
+
this.logInfo('upsertLocalDraftOrders-订单列表为空', {});
|
|
1089
1067
|
return;
|
|
1090
1068
|
}
|
|
1091
|
-
const draftMap =
|
|
1092
|
-
const memoryDraftMap =
|
|
1069
|
+
const draftMap = new Map();
|
|
1070
|
+
const memoryDraftMap = new Map();
|
|
1093
1071
|
for (const order of draftOrders) {
|
|
1094
1072
|
const draftOrder = {
|
|
1095
1073
|
...order,
|
|
@@ -1097,8 +1075,7 @@ var OrderModule = class extends import_BaseModule.BaseModule {
|
|
|
1097
1075
|
is_draft_order: 1
|
|
1098
1076
|
};
|
|
1099
1077
|
const storageKey = this.getOrderStorageKey(draftOrder);
|
|
1100
|
-
if (!storageKey)
|
|
1101
|
-
continue;
|
|
1078
|
+
if (!storageKey) continue;
|
|
1102
1079
|
draftMap.set(storageKey, draftOrder);
|
|
1103
1080
|
const memoryDraftOrder = this.normalizeOrderForMemoryList(draftOrder);
|
|
1104
1081
|
if (this.shouldKeepOrderInMemory(memoryDraftOrder)) {
|
|
@@ -1106,36 +1083,29 @@ var OrderModule = class extends import_BaseModule.BaseModule {
|
|
|
1106
1083
|
}
|
|
1107
1084
|
}
|
|
1108
1085
|
if (draftMap.size === 0) {
|
|
1109
|
-
this.logError(
|
|
1086
|
+
this.logError('upsertLocalDraftOrders-订单缺少可落库标识', {
|
|
1110
1087
|
count: draftOrders.length
|
|
1111
1088
|
});
|
|
1112
1089
|
return;
|
|
1113
1090
|
}
|
|
1114
|
-
this.logInfo(
|
|
1091
|
+
this.logInfo('upsertLocalDraftOrders-开始', {
|
|
1115
1092
|
count: draftMap.size,
|
|
1116
1093
|
memoryDraftCount: memoryDraftMap.size,
|
|
1117
1094
|
sqliteOnlyDraftCount: draftMap.size - memoryDraftMap.size
|
|
1118
1095
|
});
|
|
1119
1096
|
const mergeActions = {};
|
|
1120
|
-
const updatedList = this.store.list.filter(
|
|
1121
|
-
const identityKeyToIndex =
|
|
1097
|
+
const updatedList = this.store.list.filter(order => this.shouldKeepOrderInMemory(order));
|
|
1098
|
+
const identityKeyToIndex = new Map();
|
|
1122
1099
|
for (let index = 0; index < updatedList.length; index += 1) {
|
|
1123
1100
|
for (const key of this.getOrderIdentityMatchKeys(updatedList[index])) {
|
|
1124
|
-
if (!identityKeyToIndex.has(key))
|
|
1125
|
-
identityKeyToIndex.set(key, index);
|
|
1101
|
+
if (!identityKeyToIndex.has(key)) identityKeyToIndex.set(key, index);
|
|
1126
1102
|
}
|
|
1127
1103
|
}
|
|
1128
1104
|
for (const [storageKey, draftOrder] of memoryDraftMap.entries()) {
|
|
1129
|
-
const matchIndex = this.lookupOrderIndexByIdentityKeys(
|
|
1130
|
-
identityKeyToIndex,
|
|
1131
|
-
this.getOrderIdentityMatchKeys(draftOrder)
|
|
1132
|
-
);
|
|
1105
|
+
const matchIndex = this.lookupOrderIndexByIdentityKeys(identityKeyToIndex, this.getOrderIdentityMatchKeys(draftOrder));
|
|
1133
1106
|
if (matchIndex >= 0) {
|
|
1134
1107
|
const existingOrder = updatedList[matchIndex];
|
|
1135
|
-
const reconciledDraftOrder = this.preserveExistingBusinessCodeForDraft(
|
|
1136
|
-
existingOrder,
|
|
1137
|
-
draftOrder
|
|
1138
|
-
);
|
|
1108
|
+
const reconciledDraftOrder = this.preserveExistingBusinessCodeForDraft(existingOrder, draftOrder);
|
|
1139
1109
|
const mergedOrder = this.mergeOrderRecords(existingOrder, reconciledDraftOrder);
|
|
1140
1110
|
const nextOrder = {
|
|
1141
1111
|
...mergedOrder,
|
|
@@ -1143,57 +1113,54 @@ var OrderModule = class extends import_BaseModule.BaseModule {
|
|
|
1143
1113
|
is_draft_order: 1
|
|
1144
1114
|
};
|
|
1145
1115
|
updatedList[matchIndex] = nextOrder;
|
|
1116
|
+
// SQLite 必须与内存中的草稿保持同一份 business_code,避免下次 hydrate 再次丢失分类。
|
|
1146
1117
|
draftMap.set(storageKey, reconciledDraftOrder);
|
|
1147
1118
|
this.registerOrderIdentityKeys(identityKeyToIndex, nextOrder, matchIndex);
|
|
1148
|
-
mergeActions[storageKey] =
|
|
1119
|
+
mergeActions[storageKey] = 'update';
|
|
1149
1120
|
continue;
|
|
1150
1121
|
}
|
|
1151
1122
|
const insertIndex = updatedList.length;
|
|
1152
1123
|
updatedList.push(draftOrder);
|
|
1153
1124
|
this.registerOrderIdentityKeys(identityKeyToIndex, draftOrder, insertIndex);
|
|
1154
|
-
mergeActions[storageKey] =
|
|
1125
|
+
mergeActions[storageKey] = 'insert';
|
|
1155
1126
|
}
|
|
1156
1127
|
const patchedOrders = [...draftMap.values()];
|
|
1157
1128
|
const previousList = this.store.list;
|
|
1158
|
-
this.store.list = this.runOrderStoreSelfCheck(updatedList,
|
|
1129
|
+
this.store.list = this.runOrderStoreSelfCheck(updatedList, 'upsertLocalDraftOrders.store');
|
|
1159
1130
|
this.syncOrdersMap();
|
|
1160
1131
|
try {
|
|
1161
|
-
await this.patchOrdersInSQLite(
|
|
1162
|
-
|
|
1163
|
-
|
|
1164
|
-
mergeActions,
|
|
1165
|
-
{ throwOnError: true }
|
|
1166
|
-
);
|
|
1132
|
+
await this.patchOrdersInSQLite(patchedOrders, 'upsertLocalDraftOrders', mergeActions, {
|
|
1133
|
+
throwOnError: true
|
|
1134
|
+
});
|
|
1167
1135
|
} catch (error) {
|
|
1168
1136
|
this.store.list = previousList;
|
|
1169
1137
|
this.syncOrdersMap();
|
|
1170
1138
|
throw error;
|
|
1171
1139
|
}
|
|
1172
|
-
this.logInfo(
|
|
1140
|
+
this.logInfo('upsertLocalDraftOrders-结束', {
|
|
1173
1141
|
count: patchedOrders.length,
|
|
1174
1142
|
storeOrderCountAfter: this.store.list.length
|
|
1175
1143
|
});
|
|
1176
|
-
const removedLegacyMemoryDraft = previousList.some(
|
|
1177
|
-
(order) => !this.shouldKeepOrderInMemory(order)
|
|
1178
|
-
);
|
|
1144
|
+
const removedLegacyMemoryDraft = previousList.some(order => !this.shouldKeepOrderInMemory(order));
|
|
1179
1145
|
if (memoryDraftMap.size > 0 || removedLegacyMemoryDraft) {
|
|
1180
|
-
await this.emitOrdersChanged(
|
|
1181
|
-
patchedOrders.filter((order) => this.shouldKeepOrderInMemory(order)),
|
|
1182
|
-
"upsertLocalDraftOrders"
|
|
1183
|
-
);
|
|
1146
|
+
await this.emitOrdersChanged(patchedOrders.filter(order => this.shouldKeepOrderInMemory(order)), 'upsertLocalDraftOrders');
|
|
1184
1147
|
}
|
|
1185
1148
|
}
|
|
1149
|
+
|
|
1186
1150
|
/**
|
|
1187
1151
|
* 通过 SSE 按自定义 query 拉取订单(支持 select/with 精简字段)
|
|
1188
1152
|
*/
|
|
1189
1153
|
async fetchOrdersBySSE(query = {}) {
|
|
1190
|
-
if (!this.orderDataSource)
|
|
1191
|
-
return [];
|
|
1154
|
+
if (!this.orderDataSource) return [];
|
|
1192
1155
|
try {
|
|
1193
|
-
const data = await this.orderDataSource.run({
|
|
1156
|
+
const data = await this.orderDataSource.run({
|
|
1157
|
+
sse: {
|
|
1158
|
+
query
|
|
1159
|
+
}
|
|
1160
|
+
});
|
|
1194
1161
|
return data || [];
|
|
1195
1162
|
} catch (error) {
|
|
1196
|
-
this.logError(
|
|
1163
|
+
this.logError('fetchOrdersBySSE-失败', {
|
|
1197
1164
|
error: error instanceof Error ? error.message : String(error)
|
|
1198
1165
|
});
|
|
1199
1166
|
return [];
|
|
@@ -1203,30 +1170,26 @@ var OrderModule = class extends import_BaseModule.BaseModule {
|
|
|
1203
1170
|
return [];
|
|
1204
1171
|
}
|
|
1205
1172
|
destroy() {
|
|
1206
|
-
var _a;
|
|
1207
1173
|
if (this.syncTimer) {
|
|
1208
1174
|
clearTimeout(this.syncTimer);
|
|
1209
|
-
this.syncTimer =
|
|
1175
|
+
this.syncTimer = undefined;
|
|
1210
1176
|
}
|
|
1211
1177
|
this.isProcessingSyncBatch = false;
|
|
1212
1178
|
this.pendingSyncMessages = [];
|
|
1213
|
-
if (
|
|
1214
|
-
this.orderDataSource.destroy();
|
|
1179
|
+
if (this.orderDataSource?.destroy) this.orderDataSource.destroy();
|
|
1215
1180
|
super.destroy();
|
|
1216
1181
|
}
|
|
1217
1182
|
syncOrdersMap() {
|
|
1218
|
-
this.store.list = this.store.list.filter(
|
|
1183
|
+
this.store.list = this.store.list.filter(order => this.shouldKeepOrderInMemory(order));
|
|
1219
1184
|
this.store.map.clear();
|
|
1220
1185
|
for (const order of this.store.list) {
|
|
1221
1186
|
const primaryIdentity = this.getOrderMapKey(order);
|
|
1222
|
-
if (!primaryIdentity)
|
|
1223
|
-
continue;
|
|
1187
|
+
if (!primaryIdentity) continue;
|
|
1224
1188
|
this.store.map.set(primaryIdentity, order);
|
|
1225
1189
|
}
|
|
1226
1190
|
}
|
|
1227
1191
|
getOrderMapKey(order) {
|
|
1228
|
-
if (!order)
|
|
1229
|
-
return "";
|
|
1192
|
+
if (!order) return '';
|
|
1230
1193
|
if (!this.isBlankIdentityValue(order.order_id)) {
|
|
1231
1194
|
return this.getIdKey(order.order_id);
|
|
1232
1195
|
}
|
|
@@ -1234,11 +1197,12 @@ var OrderModule = class extends import_BaseModule.BaseModule {
|
|
|
1234
1197
|
if (!this.isBlankIdentityValue(externalSaleNumber)) {
|
|
1235
1198
|
return this.getIdKey(externalSaleNumber);
|
|
1236
1199
|
}
|
|
1237
|
-
return
|
|
1200
|
+
return '';
|
|
1238
1201
|
}
|
|
1239
1202
|
isBlankIdentityValue(value) {
|
|
1240
|
-
return value ===
|
|
1203
|
+
return value === undefined || value === null || value === '';
|
|
1241
1204
|
}
|
|
1205
|
+
|
|
1242
1206
|
/**
|
|
1243
1207
|
* `/sales/draft` 的旧调用方可能把未设置的 business_code 传成空串。
|
|
1244
1208
|
* 对同一真实订单,空值不能覆盖已有的分类,否则 booking 查询会把该订单从 Party 列表过滤掉。
|
|
@@ -1246,92 +1210,85 @@ var OrderModule = class extends import_BaseModule.BaseModule {
|
|
|
1246
1210
|
preserveExistingBusinessCodeForDraft(existingOrder, draftOrder) {
|
|
1247
1211
|
const existingBusinessCode = existingOrder.business_code;
|
|
1248
1212
|
const draftBusinessCode = draftOrder.business_code;
|
|
1249
|
-
const hasExistingBusinessCode = typeof existingBusinessCode ===
|
|
1250
|
-
const hasDraftBusinessCode = typeof draftBusinessCode ===
|
|
1251
|
-
if (!hasExistingBusinessCode || hasDraftBusinessCode)
|
|
1252
|
-
return draftOrder;
|
|
1213
|
+
const hasExistingBusinessCode = typeof existingBusinessCode === 'string' && existingBusinessCode.trim() !== '';
|
|
1214
|
+
const hasDraftBusinessCode = typeof draftBusinessCode === 'string' && draftBusinessCode.trim() !== '';
|
|
1215
|
+
if (!hasExistingBusinessCode || hasDraftBusinessCode) return draftOrder;
|
|
1253
1216
|
return {
|
|
1254
1217
|
...draftOrder,
|
|
1255
1218
|
business_code: existingBusinessCode
|
|
1256
1219
|
};
|
|
1257
1220
|
}
|
|
1258
1221
|
getOrderIdentityEntries(order) {
|
|
1259
|
-
if (!order)
|
|
1260
|
-
return [];
|
|
1222
|
+
if (!order) return [];
|
|
1261
1223
|
const record = order;
|
|
1262
|
-
const candidates = [
|
|
1263
|
-
|
|
1264
|
-
|
|
1265
|
-
|
|
1266
|
-
|
|
1267
|
-
|
|
1224
|
+
const candidates = [{
|
|
1225
|
+
field: 'order_id',
|
|
1226
|
+
value: order.order_id
|
|
1227
|
+
}, {
|
|
1228
|
+
field: 'external_sale_number',
|
|
1229
|
+
value: record.external_sale_number
|
|
1230
|
+
}, {
|
|
1231
|
+
field: 'order_number',
|
|
1232
|
+
value: record.order_number
|
|
1233
|
+
}, {
|
|
1234
|
+
field: 'shop_order_number',
|
|
1235
|
+
value: record.shop_order_number
|
|
1236
|
+
}];
|
|
1268
1237
|
const entries = [];
|
|
1269
1238
|
for (const candidate of candidates) {
|
|
1270
|
-
if (this.isBlankIdentityValue(candidate.value))
|
|
1271
|
-
continue;
|
|
1239
|
+
if (this.isBlankIdentityValue(candidate.value)) continue;
|
|
1272
1240
|
const key = this.getIdKey(candidate.value);
|
|
1273
|
-
if (entries.some(
|
|
1274
|
-
|
|
1275
|
-
|
|
1241
|
+
if (entries.some(entry => entry.field === candidate.field && entry.key === key)) continue;
|
|
1242
|
+
entries.push({
|
|
1243
|
+
field: candidate.field,
|
|
1244
|
+
key
|
|
1245
|
+
});
|
|
1276
1246
|
}
|
|
1277
1247
|
return entries;
|
|
1278
1248
|
}
|
|
1279
1249
|
getOrderIdentityKeys(order) {
|
|
1280
1250
|
const keys = [];
|
|
1281
1251
|
for (const entry of this.getOrderIdentityEntries(order)) {
|
|
1282
|
-
if (!keys.includes(entry.key))
|
|
1283
|
-
keys.push(entry.key);
|
|
1252
|
+
if (!keys.includes(entry.key)) keys.push(entry.key);
|
|
1284
1253
|
}
|
|
1285
1254
|
return keys;
|
|
1286
1255
|
}
|
|
1287
1256
|
getOrderDateKey(order) {
|
|
1288
|
-
|
|
1289
|
-
const candidates = [
|
|
1290
|
-
order == null ? void 0 : order.schedule_date,
|
|
1291
|
-
order == null ? void 0 : order.create_date,
|
|
1292
|
-
order == null ? void 0 : order.created_at,
|
|
1293
|
-
(_b = (_a = order == null ? void 0 : order.bookings) == null ? void 0 : _a[0]) == null ? void 0 : _b.start_date,
|
|
1294
|
-
(_d = (_c = order == null ? void 0 : order.bookings) == null ? void 0 : _c[0]) == null ? void 0 : _d.select_date
|
|
1295
|
-
];
|
|
1257
|
+
const candidates = [order?.schedule_date, order?.create_date, order?.created_at, order?.bookings?.[0]?.start_date, order?.bookings?.[0]?.select_date];
|
|
1296
1258
|
for (const v of candidates) {
|
|
1297
|
-
if (!v)
|
|
1298
|
-
continue;
|
|
1259
|
+
if (!v) continue;
|
|
1299
1260
|
const text = String(v);
|
|
1300
|
-
if (!text)
|
|
1301
|
-
|
|
1302
|
-
return text.split("T")[0].split(" ")[0];
|
|
1261
|
+
if (!text) continue;
|
|
1262
|
+
return text.split('T')[0].split(' ')[0];
|
|
1303
1263
|
}
|
|
1304
|
-
return
|
|
1264
|
+
return undefined;
|
|
1305
1265
|
}
|
|
1266
|
+
|
|
1306
1267
|
/**
|
|
1307
1268
|
* 初始化 OrderDataSource 实例
|
|
1308
1269
|
*/
|
|
1309
1270
|
initOrderDataSource() {
|
|
1310
|
-
|
|
1311
|
-
|
|
1312
|
-
if (!OrderDataSourceClass)
|
|
1313
|
-
return;
|
|
1271
|
+
const OrderDataSourceClass = this.core.serverOptions?.All_DATA_SOURCES?.OrderDataSource;
|
|
1272
|
+
if (!OrderDataSourceClass) return;
|
|
1314
1273
|
this.orderDataSource = new OrderDataSourceClass();
|
|
1315
1274
|
}
|
|
1275
|
+
|
|
1316
1276
|
/**
|
|
1317
1277
|
* 初始化 pubsub 订阅,监听订单变更(纯订阅,不拉数据;全量 SSE 由 loadOrdersByServer 负责)
|
|
1318
1278
|
*/
|
|
1319
1279
|
setupOrderSync() {
|
|
1320
|
-
if (!this.orderDataSource)
|
|
1321
|
-
return;
|
|
1280
|
+
if (!this.orderDataSource) return;
|
|
1322
1281
|
this.orderDataSource.run({
|
|
1323
1282
|
pubsub: {
|
|
1324
|
-
callback:
|
|
1325
|
-
console.log("orderDataSource", res);
|
|
1283
|
+
callback: res => {
|
|
1326
1284
|
const pubsubReceivedAt = Date.now();
|
|
1327
1285
|
const message = this.normalizeOrderSyncMessage(res);
|
|
1328
|
-
if (!message)
|
|
1329
|
-
return;
|
|
1286
|
+
if (!message) return;
|
|
1330
1287
|
message._pubsubReceivedAt = pubsubReceivedAt;
|
|
1331
1288
|
this.pendingSyncMessages.push(message);
|
|
1332
1289
|
const throttleMs = this.getOrderSyncThrottleMs();
|
|
1333
1290
|
const routeInfo = this.resolveSyncMessageRoute(message);
|
|
1334
|
-
this.logInfo(
|
|
1291
|
+
this.logInfo('orderSync-收到消息并入队', {
|
|
1335
1292
|
id: message.id ?? null,
|
|
1336
1293
|
order_id: message.order_id ?? null,
|
|
1337
1294
|
type: message.type ?? null,
|
|
@@ -1344,8 +1301,10 @@ var OrderModule = class extends import_BaseModule.BaseModule {
|
|
|
1344
1301
|
}
|
|
1345
1302
|
}
|
|
1346
1303
|
}).catch(() => {
|
|
1304
|
+
// 忽略同步初始化异常,避免影响主流程
|
|
1347
1305
|
});
|
|
1348
1306
|
}
|
|
1307
|
+
|
|
1349
1308
|
/**
|
|
1350
1309
|
* 调度订单同步节流窗口(leading-edge throttle):
|
|
1351
1310
|
* - 空闲状态下收到的第一条消息立即触发批处理,无需等待;
|
|
@@ -1353,29 +1312,27 @@ var OrderModule = class extends import_BaseModule.BaseModule {
|
|
|
1353
1312
|
* - 若当前批处理执行中,则由批处理结束后决定是否开启下一轮窗口。
|
|
1354
1313
|
*/
|
|
1355
1314
|
scheduleOrderSyncThrottle() {
|
|
1356
|
-
if (this.isProcessingSyncBatch)
|
|
1357
|
-
return;
|
|
1315
|
+
if (this.isProcessingSyncBatch) return;
|
|
1358
1316
|
if (this.isIdlePhase) {
|
|
1359
1317
|
this.isIdlePhase = false;
|
|
1360
|
-
this.logInfo(
|
|
1318
|
+
this.logInfo('orderSync-首条消息立即触发处理');
|
|
1361
1319
|
void this.flushOrderSyncMessagesByThrottle();
|
|
1362
1320
|
return;
|
|
1363
1321
|
}
|
|
1364
|
-
if (this.syncTimer)
|
|
1365
|
-
return;
|
|
1322
|
+
if (this.syncTimer) return;
|
|
1366
1323
|
const throttleMs = this.getOrderSyncThrottleMs();
|
|
1367
1324
|
this.syncTimer = setTimeout(() => {
|
|
1368
|
-
this.syncTimer =
|
|
1325
|
+
this.syncTimer = undefined;
|
|
1369
1326
|
void this.flushOrderSyncMessagesByThrottle();
|
|
1370
1327
|
}, throttleMs);
|
|
1371
1328
|
}
|
|
1329
|
+
|
|
1372
1330
|
/**
|
|
1373
1331
|
* 执行一次节流批处理并在需要时续约下一轮窗口。
|
|
1374
1332
|
* 批处理完成且无后续消息时恢复空闲状态,下一条消息将立即触发。
|
|
1375
1333
|
*/
|
|
1376
1334
|
async flushOrderSyncMessagesByThrottle() {
|
|
1377
|
-
if (this.isProcessingSyncBatch)
|
|
1378
|
-
return;
|
|
1335
|
+
if (this.isProcessingSyncBatch) return;
|
|
1379
1336
|
if (this.pendingSyncMessages.length === 0) {
|
|
1380
1337
|
this.isIdlePhase = true;
|
|
1381
1338
|
return;
|
|
@@ -1392,6 +1349,7 @@ var OrderModule = class extends import_BaseModule.BaseModule {
|
|
|
1392
1349
|
}
|
|
1393
1350
|
}
|
|
1394
1351
|
}
|
|
1352
|
+
|
|
1395
1353
|
/**
|
|
1396
1354
|
* 处理节流窗口内聚合后的同步消息批次
|
|
1397
1355
|
*
|
|
@@ -1406,19 +1364,16 @@ var OrderModule = class extends import_BaseModule.BaseModule {
|
|
|
1406
1364
|
async processOrderSyncMessages() {
|
|
1407
1365
|
const messages = [...this.pendingSyncMessages];
|
|
1408
1366
|
this.pendingSyncMessages = [];
|
|
1409
|
-
if (messages.length === 0)
|
|
1410
|
-
return;
|
|
1367
|
+
if (messages.length === 0) return;
|
|
1411
1368
|
const batchProcessStartAt = Date.now();
|
|
1412
|
-
const earliestReceivedAt = Math.min(
|
|
1413
|
-
|
|
1414
|
-
);
|
|
1415
|
-
this.logInfo("processOrderSyncMessages-开始", {
|
|
1369
|
+
const earliestReceivedAt = Math.min(...messages.map(m => m._pubsubReceivedAt ?? batchProcessStartAt));
|
|
1370
|
+
this.logInfo('processOrderSyncMessages-开始', {
|
|
1416
1371
|
messageCount: messages.length,
|
|
1417
1372
|
earliestReceivedAt: new Date(earliestReceivedAt).toISOString(),
|
|
1418
1373
|
batchProcessStartAt: new Date(batchProcessStartAt).toISOString(),
|
|
1419
1374
|
waitDurationMs: batchProcessStartAt - earliestReceivedAt
|
|
1420
1375
|
});
|
|
1421
|
-
const upsertOrders =
|
|
1376
|
+
const upsertOrders = new Map();
|
|
1422
1377
|
const refreshIds = [];
|
|
1423
1378
|
messages.forEach((msg, msgIndex) => {
|
|
1424
1379
|
const routeInfo = this.resolveSyncMessageRoute(msg);
|
|
@@ -1426,50 +1381,47 @@ var OrderModule = class extends import_BaseModule.BaseModule {
|
|
|
1426
1381
|
const singleId = routeInfo.singleId;
|
|
1427
1382
|
const hasSingleId = routeInfo.hasSingleId;
|
|
1428
1383
|
const normalizedBody = this.normalizeOrderSyncPayload(msg.body || msg.data);
|
|
1429
|
-
if (routeInfo.route ===
|
|
1384
|
+
if (routeInfo.route === 'bodyUpsert' && normalizedBody) {
|
|
1430
1385
|
upsertOrders.set(this.getIdKey(normalizedBody.order_id), normalizedBody);
|
|
1431
1386
|
return;
|
|
1432
1387
|
}
|
|
1433
|
-
if (hasBatchIds)
|
|
1434
|
-
refreshIds.push(...routeInfo.batchIds);
|
|
1435
|
-
else if (routeInfo.route === "httpRefresh.singleId" && singleId !== null)
|
|
1436
|
-
refreshIds.push(singleId);
|
|
1437
|
-
else if (routeInfo.route === "httpRefresh.relationIds")
|
|
1438
|
-
refreshIds.push(...routeInfo.batchIds);
|
|
1388
|
+
if (hasBatchIds) refreshIds.push(...routeInfo.batchIds);else if (routeInfo.route === 'httpRefresh.singleId' && singleId !== null) refreshIds.push(singleId);else if (routeInfo.route === 'httpRefresh.relationIds') refreshIds.push(...routeInfo.batchIds);
|
|
1439
1389
|
});
|
|
1440
1390
|
const uniqueRefreshIds = this.uniqueOrderIds(refreshIds);
|
|
1441
1391
|
const upsertList = [...upsertOrders.values()];
|
|
1442
|
-
this.logInfo(
|
|
1392
|
+
this.logInfo('processOrderSyncMessages-去重完成', {
|
|
1443
1393
|
upsertCount: upsertList.length,
|
|
1444
1394
|
refreshIdCount: uniqueRefreshIds.length
|
|
1445
1395
|
});
|
|
1446
1396
|
if (upsertList.length > 0) {
|
|
1447
|
-
await this.mergeOrdersToStore(upsertList,
|
|
1397
|
+
await this.mergeOrdersToStore(upsertList, 'pubsub.bodyUpsert');
|
|
1448
1398
|
}
|
|
1449
1399
|
if (uniqueRefreshIds.length > 0) {
|
|
1450
|
-
const {
|
|
1451
|
-
|
|
1452
|
-
|
|
1400
|
+
const {
|
|
1401
|
+
toFetch,
|
|
1402
|
+
skipped
|
|
1403
|
+
} = this.filterRedundantHttpRefreshIds(uniqueRefreshIds);
|
|
1404
|
+
if (skipped.length > 0) {}
|
|
1453
1405
|
if (toFetch.length > 0) {
|
|
1454
1406
|
const freshOrders = await this.fetchOrdersByHttp(toFetch);
|
|
1455
1407
|
if (freshOrders.length > 0) {
|
|
1456
|
-
await this.mergeOrdersToStore(freshOrders,
|
|
1457
|
-
} else {
|
|
1458
|
-
}
|
|
1408
|
+
await this.mergeOrdersToStore(freshOrders, 'pubsub.httpRefresh');
|
|
1409
|
+
} else {}
|
|
1459
1410
|
}
|
|
1460
1411
|
}
|
|
1461
1412
|
if (upsertList.length === 0 && uniqueRefreshIds.length === 0) {
|
|
1462
1413
|
return;
|
|
1463
1414
|
}
|
|
1464
1415
|
const batchProcessEndAt = Date.now();
|
|
1465
|
-
this.logInfo(
|
|
1416
|
+
this.logInfo('processOrderSyncMessages-结束', {
|
|
1466
1417
|
upsertCount: upsertList.length,
|
|
1467
1418
|
refreshIdCount: uniqueRefreshIds.length,
|
|
1468
1419
|
batchProcessDurationMs: batchProcessEndAt - batchProcessStartAt,
|
|
1469
1420
|
totalFromPubsubToProcessedMs: batchProcessEndAt - earliestReceivedAt
|
|
1470
1421
|
});
|
|
1471
|
-
await this.core.effects.emit(
|
|
1422
|
+
await this.core.effects.emit(_types.OrderHooks.onOrdersSyncCompleted, null);
|
|
1472
1423
|
}
|
|
1424
|
+
|
|
1473
1425
|
/**
|
|
1474
1426
|
* 通过 HTTP 按 ids 批量拉取订单详情(走 orderDataSource)。
|
|
1475
1427
|
*
|
|
@@ -1481,16 +1433,17 @@ var OrderModule = class extends import_BaseModule.BaseModule {
|
|
|
1481
1433
|
* const fresh = await orderModule.fetchOrdersByHttp(['96971', '96972'])
|
|
1482
1434
|
*/
|
|
1483
1435
|
async fetchOrdersByHttp(ids) {
|
|
1484
|
-
if (!this.orderDataSource || ids.length === 0)
|
|
1485
|
-
return [];
|
|
1436
|
+
if (!this.orderDataSource || ids.length === 0) return [];
|
|
1486
1437
|
try {
|
|
1487
|
-
if (typeof this.orderDataSource.fetchOrdersByIds ===
|
|
1488
|
-
const
|
|
1489
|
-
return
|
|
1438
|
+
if (typeof this.orderDataSource.fetchOrdersByIds === 'function') {
|
|
1439
|
+
const orderList = await this.orderDataSource.fetchOrdersByIds(ids);
|
|
1440
|
+
return orderList || [];
|
|
1490
1441
|
}
|
|
1491
1442
|
const orderList = await this.orderDataSource.run({
|
|
1492
1443
|
http: {
|
|
1493
|
-
query: {
|
|
1444
|
+
query: {
|
|
1445
|
+
ids
|
|
1446
|
+
}
|
|
1494
1447
|
}
|
|
1495
1448
|
});
|
|
1496
1449
|
return orderList || [];
|
|
@@ -1498,70 +1451,63 @@ var OrderModule = class extends import_BaseModule.BaseModule {
|
|
|
1498
1451
|
return [];
|
|
1499
1452
|
}
|
|
1500
1453
|
}
|
|
1454
|
+
|
|
1501
1455
|
/**
|
|
1502
1456
|
* 将增量订单合并到 store
|
|
1503
1457
|
*/
|
|
1504
1458
|
async mergeOrdersToStore(freshOrders, source) {
|
|
1505
1459
|
const storeOrderCountBefore = this.store.list.length;
|
|
1506
|
-
this.logInfo(
|
|
1460
|
+
this.logInfo('mergeOrdersToStore-开始', {
|
|
1507
1461
|
freshOrderCount: freshOrders.length,
|
|
1508
1462
|
storeOrderCountBefore,
|
|
1509
1463
|
source
|
|
1510
1464
|
});
|
|
1511
1465
|
this.logDuplicateOrders(`${source}.beforeMerge`, this.store.list);
|
|
1512
1466
|
const normalizedFreshOrders = [];
|
|
1513
|
-
const incomingProductsHaveStableIdentity =
|
|
1467
|
+
const incomingProductsHaveStableIdentity = new Map();
|
|
1514
1468
|
for (const order of freshOrders) {
|
|
1515
|
-
const id = order
|
|
1516
|
-
if (id ===
|
|
1517
|
-
continue;
|
|
1469
|
+
const id = order?.order_id;
|
|
1470
|
+
if (id === undefined || id === null) continue;
|
|
1518
1471
|
const rawProducts = order.products;
|
|
1519
1472
|
if (Array.isArray(rawProducts)) {
|
|
1520
1473
|
const allProductsHaveStableIdentity = this.doProductsHaveStableMembershipIdentity(rawProducts);
|
|
1521
1474
|
const orderIdKey = this.getIdKey(id);
|
|
1522
|
-
incomingProductsHaveStableIdentity.set(
|
|
1523
|
-
orderIdKey,
|
|
1524
|
-
(incomingProductsHaveStableIdentity.get(orderIdKey) ?? true) && allProductsHaveStableIdentity
|
|
1525
|
-
);
|
|
1475
|
+
incomingProductsHaveStableIdentity.set(orderIdKey, (incomingProductsHaveStableIdentity.get(orderIdKey) ?? true) && allProductsHaveStableIdentity);
|
|
1526
1476
|
}
|
|
1527
1477
|
normalizedFreshOrders.push(this.normalizeRemoteSyncedOrder(order));
|
|
1528
1478
|
}
|
|
1529
|
-
|
|
1530
|
-
|
|
1531
|
-
`${source}.incoming`
|
|
1532
|
-
).list;
|
|
1479
|
+
// 先对本批新单做去重(批内规模 m 很小),保证每条 fresh 身份唯一
|
|
1480
|
+
const uniqueFreshOrders = this.compactOrderListByIdentity(normalizedFreshOrders, `${source}.incoming`).list;
|
|
1533
1481
|
const uniqueFreshCount = uniqueFreshOrders.length;
|
|
1534
1482
|
const patchedOrders = [];
|
|
1535
1483
|
const remoteProductMembershipChanges = [];
|
|
1536
|
-
const forceWriteStorageKeys =
|
|
1484
|
+
const forceWriteStorageKeys = new Set();
|
|
1537
1485
|
const storageKeyMigrations = [];
|
|
1538
1486
|
const mergeActions = {};
|
|
1539
|
-
const existingProductsHaveStableIdentity = this.store.list.map(
|
|
1540
|
-
|
|
1541
|
-
|
|
1542
|
-
|
|
1543
|
-
)
|
|
1544
|
-
|
|
1545
|
-
|
|
1546
|
-
);
|
|
1547
|
-
const identityKeyToIndex = /* @__PURE__ */ new Map();
|
|
1487
|
+
const existingProductsHaveStableIdentity = this.store.list.map(order => this.doProductsHaveStableMembershipIdentity(order.products));
|
|
1488
|
+
const updatedList = this.store.list.map(order => this.normalizeOrderForMemoryList(order, `${source}.existingMemory`));
|
|
1489
|
+
|
|
1490
|
+
// 基于身份键(field:value)建立索引,把「查找已存在订单」从 O(n) 线性扫描降为 O(1),
|
|
1491
|
+
// 整体合并复杂度由 O(n^2) 降为 O(n + m)。索引语义与 doOrdersShareIdentity 完全一致:
|
|
1492
|
+
// 任一身份字段(order_id/external_sale_number/order_number/shop_order_number)同值即同一单。
|
|
1493
|
+
const identityKeyToIndex = new Map();
|
|
1548
1494
|
for (let index = 0; index < updatedList.length; index += 1) {
|
|
1549
1495
|
const identityKeys = this.getOrderIdentityMatchKeys(updatedList[index]);
|
|
1550
1496
|
for (const key of identityKeys) {
|
|
1551
|
-
|
|
1552
|
-
|
|
1497
|
+
// 同一 key 命中多张时保留最靠前的下标,与原 findIndex「取首个匹配」一致
|
|
1498
|
+
if (!identityKeyToIndex.has(key)) identityKeyToIndex.set(key, index);
|
|
1553
1499
|
}
|
|
1554
1500
|
}
|
|
1555
1501
|
for (const fresh of uniqueFreshOrders) {
|
|
1556
1502
|
const freshIdentityKeys = this.getOrderIdentityMatchKeys(fresh);
|
|
1557
1503
|
const matchIndex = this.lookupOrderIndexByIdentityKeys(identityKeyToIndex, freshIdentityKeys);
|
|
1558
1504
|
const storageKey = this.getOrderStorageKey(fresh);
|
|
1559
|
-
const mergeKey = storageKey || (fresh.order_id !==
|
|
1505
|
+
const mergeKey = storageKey || (fresh.order_id !== undefined && fresh.order_id !== null ? this.getIdKey(fresh.order_id) : '');
|
|
1560
1506
|
if (matchIndex >= 0) {
|
|
1561
1507
|
const existingOrder = updatedList[matchIndex];
|
|
1562
1508
|
const previousStorageKey = this.getPersistedOrderStorageKey(existingOrder);
|
|
1563
1509
|
if (this.isPendingSyncOrder(existingOrder) && this.isRemoteEchoMergeSource(source)) {
|
|
1564
|
-
this.logInfo(
|
|
1510
|
+
this.logInfo('mergeOrdersToStore-跳过 pending 订单远端覆盖', {
|
|
1565
1511
|
source,
|
|
1566
1512
|
order_id: existingOrder.order_id ?? fresh.order_id ?? null,
|
|
1567
1513
|
external_sale_number: existingOrder.external_sale_number ?? fresh.external_sale_number ?? null,
|
|
@@ -1569,39 +1515,34 @@ var OrderModule = class extends import_BaseModule.BaseModule {
|
|
|
1569
1515
|
remote_payment_status: fresh.payment_status ?? null,
|
|
1570
1516
|
local_need_sync: existingOrder.need_sync ?? null,
|
|
1571
1517
|
remote_need_sync: fresh.need_sync ?? null,
|
|
1572
|
-
reason:
|
|
1518
|
+
reason: 'protect_pending_sync_order_from_remote_echo'
|
|
1573
1519
|
});
|
|
1574
1520
|
continue;
|
|
1575
1521
|
}
|
|
1576
1522
|
if (this.isDraftOrder(existingOrder) && this.shouldProtectDraftOrderFromIncoming(source)) {
|
|
1577
|
-
this.logInfo(
|
|
1523
|
+
this.logInfo('mergeOrdersToStore-跳过 draft 订单远端覆盖', {
|
|
1578
1524
|
source,
|
|
1579
1525
|
order_id: existingOrder.order_id ?? fresh.order_id ?? null,
|
|
1580
1526
|
external_sale_number: existingOrder.external_sale_number ?? fresh.external_sale_number ?? null,
|
|
1581
1527
|
local_is_draft_order: existingOrder.is_draft_order ?? null,
|
|
1582
1528
|
remote_is_draft_order: fresh.is_draft_order ?? null,
|
|
1583
|
-
reason:
|
|
1529
|
+
reason: 'protect_draft_order_from_remote_echo'
|
|
1584
1530
|
});
|
|
1585
1531
|
continue;
|
|
1586
1532
|
}
|
|
1587
1533
|
let mergedOrder = this.mergeOrderRecords(existingOrder, fresh);
|
|
1588
1534
|
const freshOrderId = fresh.order_id;
|
|
1589
|
-
const incomingProductsAreStable = freshOrderId !==
|
|
1535
|
+
const incomingProductsAreStable = freshOrderId !== undefined && freshOrderId !== null && incomingProductsHaveStableIdentity.get(this.getIdKey(freshOrderId)) !== false;
|
|
1590
1536
|
let membershipChange = null;
|
|
1591
|
-
if (Array.isArray(fresh.products) && freshOrderId !==
|
|
1592
|
-
membershipChange = this.getRemoteProductMembershipChange(
|
|
1593
|
-
existingOrder,
|
|
1594
|
-
mergedOrder,
|
|
1595
|
-
source
|
|
1596
|
-
);
|
|
1537
|
+
if (Array.isArray(fresh.products) && freshOrderId !== undefined && freshOrderId !== null && existingProductsHaveStableIdentity[matchIndex] !== false && incomingProductsAreStable) {
|
|
1538
|
+
membershipChange = this.getRemoteProductMembershipChange(existingOrder, mergedOrder, source);
|
|
1597
1539
|
}
|
|
1598
1540
|
let mergedProductsHaveStableIdentity = existingProductsHaveStableIdentity[matchIndex] !== false && incomingProductsAreStable;
|
|
1599
1541
|
let adjustedMatchIndex = matchIndex;
|
|
1600
|
-
const duplicateIndexes =
|
|
1542
|
+
const duplicateIndexes = new Set();
|
|
1601
1543
|
for (const key of freshIdentityKeys) {
|
|
1602
1544
|
const duplicateIndex = identityKeyToIndex.get(key);
|
|
1603
|
-
if (duplicateIndex ===
|
|
1604
|
-
continue;
|
|
1545
|
+
if (duplicateIndex === undefined || duplicateIndex === matchIndex) continue;
|
|
1605
1546
|
duplicateIndexes.add(duplicateIndex);
|
|
1606
1547
|
}
|
|
1607
1548
|
if (duplicateIndexes.size > 0) {
|
|
@@ -1610,14 +1551,12 @@ var OrderModule = class extends import_BaseModule.BaseModule {
|
|
|
1610
1551
|
mergedProductsHaveStableIdentity = mergedProductsHaveStableIdentity && existingProductsHaveStableIdentity[duplicateIndex] !== false;
|
|
1611
1552
|
updatedList.splice(duplicateIndex, 1);
|
|
1612
1553
|
existingProductsHaveStableIdentity.splice(duplicateIndex, 1);
|
|
1613
|
-
if (duplicateIndex < adjustedMatchIndex)
|
|
1614
|
-
adjustedMatchIndex -= 1;
|
|
1554
|
+
if (duplicateIndex < adjustedMatchIndex) adjustedMatchIndex -= 1;
|
|
1615
1555
|
}
|
|
1616
1556
|
identityKeyToIndex.clear();
|
|
1617
1557
|
for (let index = 0; index < updatedList.length; index += 1) {
|
|
1618
1558
|
for (const key of this.getOrderIdentityMatchKeys(updatedList[index])) {
|
|
1619
|
-
if (!identityKeyToIndex.has(key))
|
|
1620
|
-
identityKeyToIndex.set(key, index);
|
|
1559
|
+
if (!identityKeyToIndex.has(key)) identityKeyToIndex.set(key, index);
|
|
1621
1560
|
}
|
|
1622
1561
|
}
|
|
1623
1562
|
}
|
|
@@ -1634,42 +1573,37 @@ var OrderModule = class extends import_BaseModule.BaseModule {
|
|
|
1634
1573
|
if (membershipChange) {
|
|
1635
1574
|
remoteProductMembershipChanges.push(membershipChange);
|
|
1636
1575
|
const mergedStorageKey = this.getOrderStorageKey(mergedOrder);
|
|
1637
|
-
if (mergedStorageKey)
|
|
1638
|
-
forceWriteStorageKeys.add(mergedStorageKey);
|
|
1576
|
+
if (mergedStorageKey) forceWriteStorageKeys.add(mergedStorageKey);
|
|
1639
1577
|
}
|
|
1578
|
+
// 合并后身份字段可能新增(如挂单拿到 order_id),同步登记到索引,
|
|
1579
|
+
// 供同批后续 fresh 命中同一槽位,避免重复插入
|
|
1640
1580
|
this.registerOrderIdentityKeys(identityKeyToIndex, mergedOrder, adjustedMatchIndex);
|
|
1641
1581
|
patchedOrders.push(mergedOrder);
|
|
1642
|
-
if (mergeKey)
|
|
1643
|
-
mergeActions[mergeKey] = "update";
|
|
1582
|
+
if (mergeKey) mergeActions[mergeKey] = 'update';
|
|
1644
1583
|
continue;
|
|
1645
1584
|
}
|
|
1646
1585
|
const insertIndex = updatedList.length;
|
|
1647
1586
|
updatedList.push(fresh);
|
|
1648
|
-
existingProductsHaveStableIdentity.push(
|
|
1649
|
-
fresh.order_id !== void 0 && fresh.order_id !== null && incomingProductsHaveStableIdentity.get(this.getIdKey(fresh.order_id)) !== false
|
|
1650
|
-
);
|
|
1587
|
+
existingProductsHaveStableIdentity.push(fresh.order_id !== undefined && fresh.order_id !== null && incomingProductsHaveStableIdentity.get(this.getIdKey(fresh.order_id)) !== false);
|
|
1651
1588
|
this.registerOrderIdentityKeys(identityKeyToIndex, fresh, insertIndex);
|
|
1652
1589
|
patchedOrders.push(fresh);
|
|
1653
|
-
if (mergeKey)
|
|
1654
|
-
mergeActions[mergeKey] = "insert";
|
|
1590
|
+
if (mergeKey) mergeActions[mergeKey] = 'insert';
|
|
1655
1591
|
}
|
|
1656
|
-
const insertCount = Object.values(mergeActions).filter(
|
|
1657
|
-
const updateCount = Object.values(mergeActions).filter(
|
|
1658
|
-
this.logInfo(
|
|
1592
|
+
const insertCount = Object.values(mergeActions).filter(v => v === 'insert').length;
|
|
1593
|
+
const updateCount = Object.values(mergeActions).filter(v => v === 'update').length;
|
|
1594
|
+
this.logInfo('mergeOrdersToStore-合并完成', {
|
|
1659
1595
|
insertCount,
|
|
1660
1596
|
updateCount,
|
|
1661
1597
|
storeOrderCountAfter: this.store.list.length
|
|
1662
1598
|
});
|
|
1599
|
+
// 增量索引已保证 store 去重不变量,无需再对全表做 O(n^2) 压缩;
|
|
1600
|
+
// 仅在自检开关开启时做一次兜底校验。
|
|
1663
1601
|
this.store.list = this.runOrderStoreSelfCheck(updatedList, `${source}.store`);
|
|
1664
1602
|
this.syncOrdersMap();
|
|
1665
|
-
await this.patchOrdersInSQLite(
|
|
1666
|
-
|
|
1667
|
-
|
|
1668
|
-
|
|
1669
|
-
{ storageKeyMigrations },
|
|
1670
|
-
forceWriteStorageKeys
|
|
1671
|
-
);
|
|
1672
|
-
this.logInfo("mergeOrdersToStore-结束", {
|
|
1603
|
+
await this.patchOrdersInSQLite(patchedOrders, source, mergeActions, {
|
|
1604
|
+
storageKeyMigrations
|
|
1605
|
+
}, forceWriteStorageKeys);
|
|
1606
|
+
this.logInfo('mergeOrdersToStore-结束', {
|
|
1673
1607
|
uniqueFreshCount,
|
|
1674
1608
|
storeOrderCountAfter: this.store.list.length,
|
|
1675
1609
|
insertCount,
|
|
@@ -1678,6 +1612,7 @@ var OrderModule = class extends import_BaseModule.BaseModule {
|
|
|
1678
1612
|
await this.emitRemoteProductMembershipChanges(remoteProductMembershipChanges);
|
|
1679
1613
|
await this.emitOrdersChanged(patchedOrders, source);
|
|
1680
1614
|
}
|
|
1615
|
+
|
|
1681
1616
|
/**
|
|
1682
1617
|
* 在身份索引中查找与目标订单共享任一身份键的已存在订单下标。
|
|
1683
1618
|
* 返回所有命中键里最小的下标,等价于原 findOrderIndexByIdentity 的「取首个匹配」语义。
|
|
@@ -1689,76 +1624,64 @@ var OrderModule = class extends import_BaseModule.BaseModule {
|
|
|
1689
1624
|
let matchIndex = -1;
|
|
1690
1625
|
for (const key of identityKeys) {
|
|
1691
1626
|
const index = identityKeyToIndex.get(key);
|
|
1692
|
-
if (index ===
|
|
1693
|
-
|
|
1694
|
-
if (matchIndex < 0 || index < matchIndex)
|
|
1695
|
-
matchIndex = index;
|
|
1627
|
+
if (index === undefined) continue;
|
|
1628
|
+
if (matchIndex < 0 || index < matchIndex) matchIndex = index;
|
|
1696
1629
|
}
|
|
1697
1630
|
return matchIndex;
|
|
1698
1631
|
}
|
|
1699
1632
|
isRemoteEchoMergeSource(source) {
|
|
1700
|
-
return source ===
|
|
1633
|
+
return source === 'pubsub.bodyUpsert' || source === 'pubsub.httpRefresh';
|
|
1701
1634
|
}
|
|
1702
1635
|
isRemoteProductMembershipChangeSource(source) {
|
|
1703
|
-
return this.isRemoteEchoMergeSource(source) || source ===
|
|
1636
|
+
return this.isRemoteEchoMergeSource(source) || source === 'manual.editOpenRemoteCheck';
|
|
1704
1637
|
}
|
|
1705
1638
|
getProductMembershipKey(product) {
|
|
1706
|
-
const persistentId = (0,
|
|
1707
|
-
if (persistentId)
|
|
1708
|
-
|
|
1709
|
-
|
|
1710
|
-
return uid ? `unique_identification_number:${uid}` : "";
|
|
1639
|
+
const persistentId = (0, _orderCollectionIdentity.getOrderCollectionPersistentId)('product', product);
|
|
1640
|
+
if (persistentId) return `order_detail_id:${persistentId}`;
|
|
1641
|
+
const uid = (0, _orderCollectionIdentity.getOrderCollectionUid)('product', product);
|
|
1642
|
+
return uid ? `unique_identification_number:${uid}` : '';
|
|
1711
1643
|
}
|
|
1712
1644
|
doProductsHaveStableMembershipIdentity(products) {
|
|
1713
|
-
if (!Array.isArray(products))
|
|
1714
|
-
|
|
1715
|
-
return products.every(
|
|
1716
|
-
(product) => Boolean(
|
|
1717
|
-
(0, import_orderCollectionIdentity.getOrderCollectionPersistentId)("product", product) || (0, import_orderCollectionIdentity.getOrderCollectionUid)("product", product)
|
|
1718
|
-
)
|
|
1719
|
-
);
|
|
1645
|
+
if (!Array.isArray(products)) return true;
|
|
1646
|
+
return products.every(product => Boolean((0, _orderCollectionIdentity.getOrderCollectionPersistentId)('product', product) || (0, _orderCollectionIdentity.getOrderCollectionUid)('product', product)));
|
|
1720
1647
|
}
|
|
1648
|
+
|
|
1721
1649
|
/**
|
|
1722
1650
|
* 冲突提示关注逻辑商品行,而不是服务端 checkout 过程中可能被重建的 detail 记录。
|
|
1723
1651
|
* 通用集合合并仍以 order_detail_id 为事实主键;这里只允许相同稳定 UID 跨 detail id 对齐,
|
|
1724
1652
|
* 避免本机 checkout 回声被误判成“一删一增”。
|
|
1725
1653
|
*/
|
|
1726
1654
|
isSameProductMembershipItem(left, right) {
|
|
1727
|
-
if ((0,
|
|
1728
|
-
|
|
1729
|
-
const
|
|
1730
|
-
const rightUid = (0, import_orderCollectionIdentity.getOrderCollectionUid)("product", right);
|
|
1655
|
+
if ((0, _orderCollectionIdentity.isSameOrderCollectionItem)('product', left, right)) return true;
|
|
1656
|
+
const leftUid = (0, _orderCollectionIdentity.getOrderCollectionUid)('product', left);
|
|
1657
|
+
const rightUid = (0, _orderCollectionIdentity.getOrderCollectionUid)('product', right);
|
|
1731
1658
|
return Boolean(leftUid && rightUid && leftUid === rightUid);
|
|
1732
1659
|
}
|
|
1660
|
+
|
|
1733
1661
|
/**
|
|
1734
1662
|
* 对比合并前后的商品成员集合。商品字段修改与数组重排不会产生 diff。
|
|
1735
1663
|
*/
|
|
1736
1664
|
getRemoteProductMembershipChange(existingOrder, mergedOrder, source) {
|
|
1737
|
-
if (!this.isRemoteProductMembershipChangeSource(source))
|
|
1738
|
-
return null;
|
|
1665
|
+
if (!this.isRemoteProductMembershipChangeSource(source)) return null;
|
|
1739
1666
|
const previousProducts = Array.isArray(existingOrder.products) ? existingOrder.products : [];
|
|
1740
1667
|
const currentProducts = Array.isArray(mergedOrder.products) ? mergedOrder.products : [];
|
|
1741
1668
|
const unmatchedCurrentIndexes = new Set(currentProducts.map((_, index) => index));
|
|
1742
1669
|
const removedProductKeys = [];
|
|
1743
1670
|
for (const previousProduct of previousProducts) {
|
|
1744
|
-
const matchedIndex = currentProducts.findIndex(
|
|
1745
|
-
(currentProduct, index) => unmatchedCurrentIndexes.has(index) && this.isSameProductMembershipItem(previousProduct, currentProduct)
|
|
1746
|
-
);
|
|
1671
|
+
const matchedIndex = currentProducts.findIndex((currentProduct, index) => unmatchedCurrentIndexes.has(index) && this.isSameProductMembershipItem(previousProduct, currentProduct));
|
|
1747
1672
|
if (matchedIndex >= 0) {
|
|
1748
1673
|
unmatchedCurrentIndexes.delete(matchedIndex);
|
|
1749
1674
|
continue;
|
|
1750
1675
|
}
|
|
1751
1676
|
const productKey = this.getProductMembershipKey(previousProduct);
|
|
1752
|
-
if (productKey)
|
|
1753
|
-
removedProductKeys.push(productKey);
|
|
1677
|
+
if (productKey) removedProductKeys.push(productKey);
|
|
1754
1678
|
}
|
|
1755
|
-
const addedProductKeys = [...unmatchedCurrentIndexes].map(
|
|
1679
|
+
const addedProductKeys = [...unmatchedCurrentIndexes].map(index => this.getProductMembershipKey(currentProducts[index])).filter(Boolean);
|
|
1756
1680
|
if (addedProductKeys.length === 0 && removedProductKeys.length === 0) {
|
|
1757
1681
|
return null;
|
|
1758
1682
|
}
|
|
1759
1683
|
const orderId = mergedOrder.order_id ?? existingOrder.order_id;
|
|
1760
|
-
if (orderId ===
|
|
1761
|
-
return null;
|
|
1684
|
+
if (orderId === undefined || orderId === null) return null;
|
|
1762
1685
|
return {
|
|
1763
1686
|
order_id: orderId,
|
|
1764
1687
|
source,
|
|
@@ -1768,6 +1691,7 @@ var OrderModule = class extends import_BaseModule.BaseModule {
|
|
|
1768
1691
|
current_product_count: currentProducts.length
|
|
1769
1692
|
};
|
|
1770
1693
|
}
|
|
1694
|
+
|
|
1771
1695
|
/**
|
|
1772
1696
|
* 将订单的全部身份键登记到索引并指向其在列表中的下标。
|
|
1773
1697
|
* 合并后订单新增的身份字段(如挂单同步后拿到 order_id)也会被登记。
|
|
@@ -1781,54 +1705,53 @@ var OrderModule = class extends import_BaseModule.BaseModule {
|
|
|
1781
1705
|
identityKeyToIndex.set(key, index);
|
|
1782
1706
|
}
|
|
1783
1707
|
}
|
|
1708
|
+
|
|
1784
1709
|
/**
|
|
1785
1710
|
* 合并热路径去重兜底:默认直接返回原列表(增量索引已保证无重复)。
|
|
1786
1711
|
* 仅当 ORDER_MERGE_SELF_CHECK 开启时,额外做一次全表压缩并在发现残留重复时告警。
|
|
1787
1712
|
*/
|
|
1788
1713
|
runOrderStoreSelfCheck(orders, source) {
|
|
1789
|
-
if (!ORDER_MERGE_SELF_CHECK)
|
|
1790
|
-
|
|
1791
|
-
|
|
1714
|
+
if (!ORDER_MERGE_SELF_CHECK) return orders;
|
|
1715
|
+
const {
|
|
1716
|
+
list,
|
|
1717
|
+
removedCount
|
|
1718
|
+
} = this.compactOrderListByIdentity(orders, source);
|
|
1792
1719
|
if (removedCount > 0) {
|
|
1793
|
-
this.logWarning(
|
|
1720
|
+
this.logWarning('合并自检发现残留重复订单,已压缩', {
|
|
1721
|
+
source,
|
|
1722
|
+
removedCount
|
|
1723
|
+
});
|
|
1794
1724
|
}
|
|
1795
1725
|
return list;
|
|
1796
1726
|
}
|
|
1797
1727
|
normalizeOrderSyncMessage(res) {
|
|
1798
|
-
const data =
|
|
1799
|
-
if (!data)
|
|
1800
|
-
|
|
1801
|
-
const payload = (data == null ? void 0 : data.data) && typeof data.data === "object" ? data.data : void 0;
|
|
1728
|
+
const data = res?.data || res;
|
|
1729
|
+
if (!data) return null;
|
|
1730
|
+
const payload = data?.data && typeof data.data === 'object' ? data.data : undefined;
|
|
1802
1731
|
const normalized = {
|
|
1803
1732
|
...data,
|
|
1804
|
-
_channelKey: data.module ||
|
|
1733
|
+
_channelKey: data.module || 'sales_sync'
|
|
1805
1734
|
};
|
|
1806
|
-
if (!normalized.type && typeof data.event ===
|
|
1807
|
-
|
|
1808
|
-
if (!normalized.id && data.
|
|
1809
|
-
|
|
1810
|
-
if (!normalized.
|
|
1811
|
-
|
|
1812
|
-
if (!normalized.
|
|
1813
|
-
normalized.order_id = data.order_id;
|
|
1814
|
-
if (!normalized.order_id && (payload == null ? void 0 : payload.order_id) !== void 0)
|
|
1815
|
-
normalized.order_id = payload.order_id;
|
|
1816
|
-
if (!normalized.body && (payload == null ? void 0 : payload.order_id) !== void 0)
|
|
1817
|
-
normalized.body = payload;
|
|
1818
|
-
if (!normalized.data && (payload == null ? void 0 : payload.order_id) !== void 0)
|
|
1819
|
-
normalized.data = payload;
|
|
1735
|
+
if (!normalized.type && typeof data.event === 'string') normalized.type = data.event;
|
|
1736
|
+
if (!normalized.id && data.id !== undefined) normalized.id = data.id;
|
|
1737
|
+
if (!normalized.id && data.order_id !== undefined) normalized.id = data.order_id;
|
|
1738
|
+
if (!normalized.order_id && data.order_id !== undefined) normalized.order_id = data.order_id;
|
|
1739
|
+
if (!normalized.order_id && payload?.order_id !== undefined) normalized.order_id = payload.order_id;
|
|
1740
|
+
if (!normalized.body && payload?.order_id !== undefined) normalized.body = payload;
|
|
1741
|
+
if (!normalized.data && payload?.order_id !== undefined) normalized.data = payload;
|
|
1820
1742
|
return normalized;
|
|
1821
1743
|
}
|
|
1822
1744
|
normalizeOrderSyncPayload(payload) {
|
|
1823
|
-
if (!payload)
|
|
1824
|
-
return null;
|
|
1745
|
+
if (!payload) return null;
|
|
1825
1746
|
const orderId = payload.order_id;
|
|
1826
|
-
if (orderId ===
|
|
1827
|
-
|
|
1828
|
-
|
|
1747
|
+
if (orderId === undefined || orderId === null) return null;
|
|
1748
|
+
return {
|
|
1749
|
+
...payload,
|
|
1750
|
+
order_id: orderId
|
|
1751
|
+
};
|
|
1829
1752
|
}
|
|
1830
1753
|
uniqueOrderIds(ids) {
|
|
1831
|
-
const idMap =
|
|
1754
|
+
const idMap = new Map();
|
|
1832
1755
|
for (const id of ids) {
|
|
1833
1756
|
idMap.set(this.getIdKey(id), id);
|
|
1834
1757
|
}
|
|
@@ -1838,21 +1761,14 @@ var OrderModule = class extends import_BaseModule.BaseModule {
|
|
|
1838
1761
|
return String(id);
|
|
1839
1762
|
}
|
|
1840
1763
|
isOrderLookupMatch(order, lookupKey) {
|
|
1841
|
-
if (!order || !lookupKey)
|
|
1842
|
-
|
|
1843
|
-
|
|
1844
|
-
|
|
1845
|
-
order.external_sale_number,
|
|
1846
|
-
order.order_number,
|
|
1847
|
-
order.shop_order_number,
|
|
1848
|
-
order.shop_full_order_number
|
|
1849
|
-
];
|
|
1850
|
-
return candidates.some((value) => {
|
|
1851
|
-
if (value === void 0 || value === null || value === "")
|
|
1852
|
-
return false;
|
|
1764
|
+
if (!order || !lookupKey) return false;
|
|
1765
|
+
const candidates = [order.order_id, order.external_sale_number, order.order_number, order.shop_order_number, order.shop_full_order_number];
|
|
1766
|
+
return candidates.some(value => {
|
|
1767
|
+
if (value === undefined || value === null || value === '') return false;
|
|
1853
1768
|
return this.getIdKey(value) === lookupKey;
|
|
1854
1769
|
});
|
|
1855
1770
|
}
|
|
1771
|
+
|
|
1856
1772
|
/**
|
|
1857
1773
|
* 解析订单 SQLite 主键,规则与宿主 OrderDataSource.getOrderStorageKey 一致。
|
|
1858
1774
|
*
|
|
@@ -1861,114 +1777,94 @@ var OrderModule = class extends import_BaseModule.BaseModule {
|
|
|
1861
1777
|
* // => 'S-1001'
|
|
1862
1778
|
*/
|
|
1863
1779
|
getOrderStorageKey(order) {
|
|
1864
|
-
const externalSaleNumber = order
|
|
1865
|
-
if (externalSaleNumber !==
|
|
1780
|
+
const externalSaleNumber = order?.external_sale_number;
|
|
1781
|
+
if (externalSaleNumber !== undefined && externalSaleNumber !== null && externalSaleNumber !== '') {
|
|
1866
1782
|
return String(externalSaleNumber);
|
|
1867
1783
|
}
|
|
1868
|
-
const orderId = order
|
|
1869
|
-
if (orderId !==
|
|
1784
|
+
const orderId = order?.order_id;
|
|
1785
|
+
if (orderId !== undefined && orderId !== null && orderId !== '') {
|
|
1870
1786
|
return String(orderId);
|
|
1871
1787
|
}
|
|
1872
|
-
return
|
|
1788
|
+
return '';
|
|
1873
1789
|
}
|
|
1874
1790
|
getPersistedOrderStorageKey(order) {
|
|
1875
|
-
const persistedStorageKey = order
|
|
1876
|
-
if (persistedStorageKey !==
|
|
1791
|
+
const persistedStorageKey = order?.[ORDER_STORAGE_KEY_FIELD];
|
|
1792
|
+
if (persistedStorageKey !== undefined && persistedStorageKey !== null && persistedStorageKey !== '') {
|
|
1877
1793
|
return String(persistedStorageKey);
|
|
1878
1794
|
}
|
|
1879
1795
|
return this.getOrderStorageKey(order);
|
|
1880
1796
|
}
|
|
1881
1797
|
getOrderIdentityMatchKeys(order) {
|
|
1882
|
-
return this.getOrderIdentityEntries(order).map(
|
|
1798
|
+
return this.getOrderIdentityEntries(order).map(entry => `${entry.field}:${entry.key}`);
|
|
1883
1799
|
}
|
|
1884
1800
|
doOrdersShareIdentity(left, right) {
|
|
1885
1801
|
const leftKeys = new Set(this.getOrderIdentityMatchKeys(left));
|
|
1886
|
-
if (leftKeys.size === 0)
|
|
1887
|
-
|
|
1888
|
-
return this.getOrderIdentityMatchKeys(right).some((key) => leftKeys.has(key));
|
|
1802
|
+
if (leftKeys.size === 0) return false;
|
|
1803
|
+
return this.getOrderIdentityMatchKeys(right).some(key => leftKeys.has(key));
|
|
1889
1804
|
}
|
|
1890
1805
|
findOrderIndexByIdentity(orders, target) {
|
|
1891
|
-
if (this.getOrderIdentityMatchKeys(target).length === 0)
|
|
1892
|
-
|
|
1893
|
-
return orders.findIndex((order) => this.doOrdersShareIdentity(order, target));
|
|
1806
|
+
if (this.getOrderIdentityMatchKeys(target).length === 0) return -1;
|
|
1807
|
+
return orders.findIndex(order => this.doOrdersShareIdentity(order, target));
|
|
1894
1808
|
}
|
|
1895
1809
|
getOrderCompletenessScore(order) {
|
|
1896
1810
|
const record = order;
|
|
1897
1811
|
let score = this.isPendingSyncOrder(order) ? 0 : 100;
|
|
1898
|
-
if (!this.isBlankIdentityValue(order.order_id) && order.order_id !== record.external_sale_number)
|
|
1899
|
-
score += 30;
|
|
1812
|
+
if (!this.isBlankIdentityValue(order.order_id) && order.order_id !== record.external_sale_number) score += 30;
|
|
1900
1813
|
score += this.getOrderIdentityMatchKeys(order).length * 5;
|
|
1901
|
-
if (Array.isArray(order.products))
|
|
1902
|
-
|
|
1903
|
-
if (Array.isArray(order.
|
|
1904
|
-
|
|
1905
|
-
if (Array.isArray(order.payments))
|
|
1906
|
-
score += Math.min(order.payments.length, 10);
|
|
1907
|
-
if (record.updated_at)
|
|
1908
|
-
score += 1;
|
|
1814
|
+
if (Array.isArray(order.products)) score += Math.min(order.products.length, 10);
|
|
1815
|
+
if (Array.isArray(order.bookings)) score += Math.min(order.bookings.length, 10);
|
|
1816
|
+
if (Array.isArray(order.payments)) score += Math.min(order.payments.length, 10);
|
|
1817
|
+
if (record.updated_at) score += 1;
|
|
1909
1818
|
return score;
|
|
1910
1819
|
}
|
|
1911
1820
|
pickPreferredOrder(left, right) {
|
|
1912
1821
|
const leftScore = this.getOrderCompletenessScore(left);
|
|
1913
1822
|
const rightScore = this.getOrderCompletenessScore(right);
|
|
1914
|
-
if (rightScore >= leftScore)
|
|
1915
|
-
return right;
|
|
1823
|
+
if (rightScore >= leftScore) return right;
|
|
1916
1824
|
return left;
|
|
1917
1825
|
}
|
|
1918
1826
|
getProductIdentityKeys(product) {
|
|
1919
|
-
var _a;
|
|
1920
1827
|
const record = product;
|
|
1921
|
-
if (!record)
|
|
1922
|
-
return [];
|
|
1828
|
+
if (!record) return [];
|
|
1923
1829
|
const keys = [];
|
|
1924
|
-
const metadataUid =
|
|
1925
|
-
if (!this.isBlankIdentityValue(metadataUid))
|
|
1926
|
-
keys.push(`uid:${String(metadataUid)}`);
|
|
1830
|
+
const metadataUid = record.metadata?.unique_identification_number;
|
|
1831
|
+
if (!this.isBlankIdentityValue(metadataUid)) keys.push(`uid:${String(metadataUid)}`);
|
|
1927
1832
|
const topLevelUid = record.unique_identification_number;
|
|
1928
|
-
if (!this.isBlankIdentityValue(topLevelUid))
|
|
1929
|
-
keys.push(`uid:${String(topLevelUid)}`);
|
|
1833
|
+
if (!this.isBlankIdentityValue(topLevelUid)) keys.push(`uid:${String(topLevelUid)}`);
|
|
1930
1834
|
const orderDetailId = record.order_detail_id;
|
|
1931
|
-
if (!this.isBlankIdentityValue(orderDetailId))
|
|
1932
|
-
keys.push(`detail:${String(orderDetailId)}`);
|
|
1835
|
+
if (!this.isBlankIdentityValue(orderDetailId)) keys.push(`detail:${String(orderDetailId)}`);
|
|
1933
1836
|
const bookingUid = record.booking_uid;
|
|
1934
|
-
if (!this.isBlankIdentityValue(bookingUid))
|
|
1935
|
-
keys.push(`booking:${String(bookingUid)}`);
|
|
1837
|
+
if (!this.isBlankIdentityValue(bookingUid)) keys.push(`booking:${String(bookingUid)}`);
|
|
1936
1838
|
return keys;
|
|
1937
1839
|
}
|
|
1938
1840
|
mergeOrderProductLists(preferred, secondary) {
|
|
1939
1841
|
const preferredList = Array.isArray(preferred) ? preferred : [];
|
|
1940
1842
|
const secondaryList = Array.isArray(secondary) ? secondary : [];
|
|
1941
|
-
if (secondaryList.length === 0)
|
|
1942
|
-
|
|
1943
|
-
|
|
1944
|
-
return secondaryList;
|
|
1945
|
-
const seenKeys = /* @__PURE__ */ new Set();
|
|
1843
|
+
if (secondaryList.length === 0) return preferredList;
|
|
1844
|
+
if (preferredList.length === 0) return secondaryList;
|
|
1845
|
+
const seenKeys = new Set();
|
|
1946
1846
|
for (const item of preferredList) {
|
|
1947
1847
|
const keys = this.getProductIdentityKeys(item);
|
|
1948
|
-
for (const key of keys)
|
|
1949
|
-
seenKeys.add(key);
|
|
1848
|
+
for (const key of keys) seenKeys.add(key);
|
|
1950
1849
|
}
|
|
1951
1850
|
const result = [...preferredList];
|
|
1952
1851
|
for (const item of secondaryList) {
|
|
1953
1852
|
const keys = this.getProductIdentityKeys(item);
|
|
1954
|
-
if (keys.length === 0 || keys.some(
|
|
1955
|
-
|
|
1956
|
-
|
|
1957
|
-
seenKeys.add(key);
|
|
1958
|
-
result.push((0, import_lodash_es.cloneDeep)(item));
|
|
1853
|
+
if (keys.length === 0 || keys.some(key => seenKeys.has(key))) continue;
|
|
1854
|
+
for (const key of keys) seenKeys.add(key);
|
|
1855
|
+
result.push((0, _lodashEs.cloneDeep)(item));
|
|
1959
1856
|
}
|
|
1960
1857
|
return result;
|
|
1961
1858
|
}
|
|
1962
1859
|
mergeOrderPaymentLists(existing, incoming) {
|
|
1963
1860
|
const existingList = Array.isArray(existing) ? existing : [];
|
|
1964
1861
|
const incomingList = Array.isArray(incoming) ? incoming : [];
|
|
1965
|
-
if (incomingList.length === 0)
|
|
1966
|
-
|
|
1967
|
-
return (0, import_payment_utils.mergeOrderPaymentListsByIdentity)(existingList, incomingList);
|
|
1862
|
+
if (incomingList.length === 0) return (0, _lodashEs.cloneDeep)(existingList);
|
|
1863
|
+
return (0, _paymentUtils.mergeOrderPaymentListsByIdentity)(existingList, incomingList);
|
|
1968
1864
|
}
|
|
1969
1865
|
getIdentifiedPendingPayments(order) {
|
|
1970
|
-
const payments = Array.isArray(order
|
|
1971
|
-
return payments.filter(
|
|
1866
|
+
const payments = Array.isArray(order?.payments) ? order.payments : [];
|
|
1867
|
+
return payments.filter(payment => (0, _paymentUtils.isIdentifiedPendingOrderPayment)(payment));
|
|
1972
1868
|
}
|
|
1973
1869
|
shouldProtectLocalOrderFromRemoteSnapshot(order) {
|
|
1974
1870
|
return this.isPendingSyncOrder(order) || this.isDraftOrder(order) || this.getIdentifiedPendingPayments(order).length > 0;
|
|
@@ -1976,10 +1872,8 @@ var OrderModule = class extends import_BaseModule.BaseModule {
|
|
|
1976
1872
|
getUnmatchedIdentifiedPendingPayments(existing, incoming) {
|
|
1977
1873
|
const existingPayments = Array.isArray(existing) ? existing : [];
|
|
1978
1874
|
const incomingPayments = Array.isArray(incoming) ? incoming : [];
|
|
1979
|
-
const incomingIdentityKeys = new Set(
|
|
1980
|
-
|
|
1981
|
-
);
|
|
1982
|
-
return existingPayments.filter((payment) => (0, import_payment_utils.isIdentifiedPendingOrderPayment)(payment) && !(0, import_payment_utils.getOrderPaymentIdentityKeys)(payment).some((key) => incomingIdentityKeys.has(key)));
|
|
1875
|
+
const incomingIdentityKeys = new Set(incomingPayments.flatMap(payment => (0, _paymentUtils.getOrderPaymentIdentityKeys)(payment)));
|
|
1876
|
+
return existingPayments.filter(payment => (0, _paymentUtils.isIdentifiedPendingOrderPayment)(payment) && !(0, _paymentUtils.getOrderPaymentIdentityKeys)(payment).some(key => incomingIdentityKeys.has(key)));
|
|
1983
1877
|
}
|
|
1984
1878
|
mergeOrderRecords(existing, incoming) {
|
|
1985
1879
|
const preferred = this.pickPreferredOrder(existing, incoming);
|
|
@@ -1988,31 +1882,8 @@ var OrderModule = class extends import_BaseModule.BaseModule {
|
|
|
1988
1882
|
...secondary,
|
|
1989
1883
|
...preferred
|
|
1990
1884
|
};
|
|
1991
|
-
const fieldsToPreserve = [
|
|
1992
|
-
|
|
1993
|
-
"external_sale_number",
|
|
1994
|
-
"order_number",
|
|
1995
|
-
"shop_order_number",
|
|
1996
|
-
"shop_full_order_number"
|
|
1997
|
-
];
|
|
1998
|
-
const incomingPatchFields = [
|
|
1999
|
-
"payment_status",
|
|
2000
|
-
"status",
|
|
2001
|
-
"shipping_status",
|
|
2002
|
-
"updated_at",
|
|
2003
|
-
"shop_discount",
|
|
2004
|
-
"summary",
|
|
2005
|
-
"note",
|
|
2006
|
-
"customer_id",
|
|
2007
|
-
"customer_name",
|
|
2008
|
-
"customer",
|
|
2009
|
-
"holder",
|
|
2010
|
-
"contacts_info",
|
|
2011
|
-
"phone",
|
|
2012
|
-
"email",
|
|
2013
|
-
"relation_forms",
|
|
2014
|
-
"is_draft_order"
|
|
2015
|
-
];
|
|
1885
|
+
const fieldsToPreserve = ['order_id', 'external_sale_number', 'order_number', 'shop_order_number', 'shop_full_order_number'];
|
|
1886
|
+
const incomingPatchFields = ['payment_status', 'status', 'shipping_status', 'updated_at', 'shop_discount', 'summary', 'note', 'customer_id', 'customer_name', 'customer', 'holder', 'contacts_info', 'phone', 'email', 'relation_forms', 'is_draft_order'];
|
|
2016
1887
|
const mergedRecord = merged;
|
|
2017
1888
|
const preferredRecord = preferred;
|
|
2018
1889
|
const secondaryRecord = secondary;
|
|
@@ -2029,41 +1900,34 @@ var OrderModule = class extends import_BaseModule.BaseModule {
|
|
|
2029
1900
|
}
|
|
2030
1901
|
}
|
|
2031
1902
|
for (const field of incomingPatchFields) {
|
|
2032
|
-
if (!Object.prototype.hasOwnProperty.call(incomingRecord, field))
|
|
2033
|
-
|
|
2034
|
-
if (incomingRecord[field] ===
|
|
2035
|
-
|
|
2036
|
-
if (typeof incomingRecord[field] === "string" && incomingRecord[field] === "")
|
|
2037
|
-
continue;
|
|
2038
|
-
mergedRecord[field] = (0, import_lodash_es.cloneDeep)(incomingRecord[field]);
|
|
1903
|
+
if (!Object.prototype.hasOwnProperty.call(incomingRecord, field)) continue;
|
|
1904
|
+
if (incomingRecord[field] === undefined || incomingRecord[field] === null) continue;
|
|
1905
|
+
if (typeof incomingRecord[field] === 'string' && incomingRecord[field] === '') continue;
|
|
1906
|
+
mergedRecord[field] = (0, _lodashEs.cloneDeep)(incomingRecord[field]);
|
|
2039
1907
|
}
|
|
1908
|
+
|
|
1909
|
+
// 子项数组遵循完整快照语义:incoming 自有且为数组(包括 [])时权威替换;
|
|
1910
|
+
// 字段缺席或不是数组时视为稀疏 patch,继续保留 existing。
|
|
2040
1911
|
for (const field of ORDER_AUTHORITATIVE_COLLECTION_FIELDS) {
|
|
2041
1912
|
if (Array.isArray(incomingRecord[field])) {
|
|
2042
|
-
if (field ===
|
|
2043
|
-
const preservedPending = this.getUnmatchedIdentifiedPendingPayments(
|
|
2044
|
-
existingRecord[field],
|
|
2045
|
-
incomingRecord[field]
|
|
2046
|
-
);
|
|
1913
|
+
if (field === 'payments') {
|
|
1914
|
+
const preservedPending = this.getUnmatchedIdentifiedPendingPayments(existingRecord[field], incomingRecord[field]);
|
|
2047
1915
|
if (preservedPending.length > 0) {
|
|
2048
|
-
this.logInfo(
|
|
1916
|
+
this.logInfo('mergeOrderRecords-保留本地 pending 支付项', {
|
|
2049
1917
|
order_id: existing.order_id ?? incoming.order_id ?? null,
|
|
2050
1918
|
external_sale_number: existingRecord.external_sale_number ?? incomingRecord.external_sale_number ?? null,
|
|
2051
1919
|
preserved_pending_count: preservedPending.length,
|
|
2052
|
-
preserved_payment_identities: preservedPending.map(
|
|
1920
|
+
preserved_payment_identities: preservedPending.map(payment => (0, _paymentUtils.getOrderPaymentIdentityKeys)(payment))
|
|
2053
1921
|
});
|
|
2054
1922
|
}
|
|
2055
1923
|
}
|
|
2056
|
-
mergedRecord[field] = this.mergeAuthoritativeOrderCollection(
|
|
2057
|
-
field
|
|
2058
|
-
|
|
2059
|
-
incomingRecord[field],
|
|
2060
|
-
childUidRemaps,
|
|
2061
|
-
{ preserveIdentifiedPendingPayments: field === "payments" }
|
|
2062
|
-
);
|
|
1924
|
+
mergedRecord[field] = this.mergeAuthoritativeOrderCollection(field, existingRecord[field], incomingRecord[field], childUidRemaps, {
|
|
1925
|
+
preserveIdentifiedPendingPayments: field === 'payments'
|
|
1926
|
+
});
|
|
2063
1927
|
continue;
|
|
2064
1928
|
}
|
|
2065
1929
|
if (Object.prototype.hasOwnProperty.call(existingRecord, field)) {
|
|
2066
|
-
mergedRecord[field] = (0,
|
|
1930
|
+
mergedRecord[field] = (0, _lodashEs.cloneDeep)(existingRecord[field]);
|
|
2067
1931
|
} else {
|
|
2068
1932
|
delete mergedRecord[field];
|
|
2069
1933
|
}
|
|
@@ -2071,13 +1935,10 @@ var OrderModule = class extends import_BaseModule.BaseModule {
|
|
|
2071
1935
|
if (!this.isPendingSyncOrder(preferred) || !this.isPendingSyncOrder(secondary)) {
|
|
2072
1936
|
merged.need_sync = 0;
|
|
2073
1937
|
}
|
|
2074
|
-
return this.normalizeOrderCollectionsForIngress(
|
|
2075
|
-
(0, import_orderCollectionIdentity.applyOrderCollectionUidRemaps)(merged, childUidRemaps),
|
|
2076
|
-
"mergeOrderRecords"
|
|
2077
|
-
);
|
|
1938
|
+
return this.normalizeOrderCollectionsForIngress((0, _orderCollectionIdentity.applyOrderCollectionUidRemaps)(merged, childUidRemaps), 'mergeOrderRecords');
|
|
2078
1939
|
}
|
|
2079
1940
|
summarizeDuplicateOrders(orders) {
|
|
2080
|
-
const groups =
|
|
1941
|
+
const groups = new Map();
|
|
2081
1942
|
for (const order of orders) {
|
|
2082
1943
|
for (const key of this.getOrderIdentityMatchKeys(order)) {
|
|
2083
1944
|
const group = groups.get(key) || [];
|
|
@@ -2088,7 +1949,7 @@ var OrderModule = class extends import_BaseModule.BaseModule {
|
|
|
2088
1949
|
return [...groups.entries()].filter(([, group]) => group.length > 1).slice(0, 20).map(([key, group]) => ({
|
|
2089
1950
|
key,
|
|
2090
1951
|
count: group.length,
|
|
2091
|
-
orders: group.map(
|
|
1952
|
+
orders: group.map(order => ({
|
|
2092
1953
|
order_id: order.order_id ?? null,
|
|
2093
1954
|
external_sale_number: order.external_sale_number ?? null,
|
|
2094
1955
|
shop_order_number: order.shop_order_number ?? null,
|
|
@@ -2100,9 +1961,8 @@ var OrderModule = class extends import_BaseModule.BaseModule {
|
|
|
2100
1961
|
}
|
|
2101
1962
|
logDuplicateOrders(source, orders) {
|
|
2102
1963
|
const duplicateGroups = this.summarizeDuplicateOrders(orders);
|
|
2103
|
-
if (duplicateGroups.length === 0)
|
|
2104
|
-
|
|
2105
|
-
this.logWarning("检测到重复订单身份键", {
|
|
1964
|
+
if (duplicateGroups.length === 0) return;
|
|
1965
|
+
this.logWarning('检测到重复订单身份键', {
|
|
2106
1966
|
source,
|
|
2107
1967
|
duplicateGroupCount: duplicateGroups.length,
|
|
2108
1968
|
duplicateGroups
|
|
@@ -2112,10 +1972,7 @@ var OrderModule = class extends import_BaseModule.BaseModule {
|
|
|
2112
1972
|
const list = [];
|
|
2113
1973
|
let removedCount = 0;
|
|
2114
1974
|
for (const order of orders) {
|
|
2115
|
-
const normalizedOrder = this.normalizeOrderCollectionsForIngress(
|
|
2116
|
-
order,
|
|
2117
|
-
`${source}.order`
|
|
2118
|
-
);
|
|
1975
|
+
const normalizedOrder = this.normalizeOrderCollectionsForIngress(order, `${source}.order`);
|
|
2119
1976
|
const matchIndex = this.findOrderIndexByIdentity(list, normalizedOrder);
|
|
2120
1977
|
if (matchIndex < 0) {
|
|
2121
1978
|
list.push(normalizedOrder);
|
|
@@ -2125,7 +1982,7 @@ var OrderModule = class extends import_BaseModule.BaseModule {
|
|
|
2125
1982
|
removedCount += 1;
|
|
2126
1983
|
}
|
|
2127
1984
|
if (removedCount > 0) {
|
|
2128
|
-
this.logWarning(
|
|
1985
|
+
this.logWarning('订单列表重复项已压缩', {
|
|
2129
1986
|
source,
|
|
2130
1987
|
beforeCount: orders.length,
|
|
2131
1988
|
afterCount: list.length,
|
|
@@ -2134,99 +1991,101 @@ var OrderModule = class extends import_BaseModule.BaseModule {
|
|
|
2134
1991
|
} else {
|
|
2135
1992
|
this.logDuplicateOrders(source, orders);
|
|
2136
1993
|
}
|
|
2137
|
-
return {
|
|
1994
|
+
return {
|
|
1995
|
+
list,
|
|
1996
|
+
removedCount
|
|
1997
|
+
};
|
|
2138
1998
|
}
|
|
1999
|
+
|
|
2139
2000
|
/**
|
|
2140
2001
|
* 当前订单拉取窗口:
|
|
2141
2002
|
* - 优先使用 createdAtQuery.sales_time_between(已按营业日边界初始化)
|
|
2142
2003
|
* - 无法解析时退化为自然日窗口
|
|
2143
2004
|
*/
|
|
2144
2005
|
getCurrentPullWindow() {
|
|
2145
|
-
|
|
2146
|
-
const
|
|
2147
|
-
const range = (_b = (_a = this.store) == null ? void 0 : _a.createdAtQuery) == null ? void 0 : _b.sales_time_between;
|
|
2006
|
+
const now = (0, _dayjs.default)();
|
|
2007
|
+
const range = this.store?.createdAtQuery?.sales_time_between;
|
|
2148
2008
|
if (Array.isArray(range) && range.length >= 2) {
|
|
2149
|
-
const
|
|
2150
|
-
const endExclusiveAt = (0,
|
|
2151
|
-
if (
|
|
2152
|
-
return {
|
|
2009
|
+
const startAt = (0, _dayjs.default)(range[0]);
|
|
2010
|
+
const endExclusiveAt = (0, _dayjs.default)(range[1]);
|
|
2011
|
+
if (startAt.isValid() && endExclusiveAt.isValid() && endExclusiveAt.isAfter(startAt)) {
|
|
2012
|
+
return {
|
|
2013
|
+
startAt,
|
|
2014
|
+
endExclusiveAt
|
|
2015
|
+
};
|
|
2153
2016
|
}
|
|
2154
2017
|
}
|
|
2155
|
-
const startAt = now.startOf(
|
|
2156
|
-
return {
|
|
2018
|
+
const startAt = now.startOf('day');
|
|
2019
|
+
return {
|
|
2020
|
+
startAt,
|
|
2021
|
+
endExclusiveAt: startAt.add(1, 'day')
|
|
2022
|
+
};
|
|
2157
2023
|
}
|
|
2158
2024
|
hasPulledOrdersToday() {
|
|
2159
2025
|
const lastFullFetchAt = this.getStorageItem(ORDER_LAST_FULL_FETCH_AT_STORAGE_KEY);
|
|
2160
|
-
if (!lastFullFetchAt)
|
|
2161
|
-
|
|
2162
|
-
|
|
2163
|
-
|
|
2164
|
-
|
|
2165
|
-
|
|
2026
|
+
if (!lastFullFetchAt) return false;
|
|
2027
|
+
const parsed = (0, _dayjs.default)(lastFullFetchAt);
|
|
2028
|
+
if (!parsed.isValid()) return false;
|
|
2029
|
+
|
|
2030
|
+
// 按当前拉取窗口判断,而不是自然日判断。
|
|
2031
|
+
// 这样在营业日跨日边界(start_time)场景下也能正确判定是否需要重新拉取。
|
|
2032
|
+
const {
|
|
2033
|
+
startAt,
|
|
2034
|
+
endExclusiveAt
|
|
2035
|
+
} = this.getCurrentPullWindow();
|
|
2166
2036
|
return (parsed.isAfter(startAt) || parsed.isSame(startAt)) && parsed.isBefore(endExclusiveAt);
|
|
2167
2037
|
}
|
|
2168
2038
|
markOrderPulledAtNow() {
|
|
2169
|
-
this.setStorageItem(
|
|
2170
|
-
ORDER_LAST_FULL_FETCH_AT_STORAGE_KEY,
|
|
2171
|
-
(0, import_dayjs.default)().format("YYYY-MM-DD HH:mm:ss")
|
|
2172
|
-
);
|
|
2039
|
+
this.setStorageItem(ORDER_LAST_FULL_FETCH_AT_STORAGE_KEY, (0, _dayjs.default)().format('YYYY-MM-DD HH:mm:ss'));
|
|
2173
2040
|
}
|
|
2174
2041
|
getStorageItem(key) {
|
|
2175
|
-
var _a;
|
|
2176
2042
|
try {
|
|
2177
|
-
if (
|
|
2178
|
-
|
|
2179
|
-
if (typeof globalThis !== "undefined" && (globalThis == null ? void 0 : globalThis.localStorage)) {
|
|
2043
|
+
if (this.storage?.getStorage) return this.storage.getStorage(key);
|
|
2044
|
+
if (typeof globalThis !== 'undefined' && globalThis?.localStorage) {
|
|
2180
2045
|
return globalThis.localStorage.getItem(key);
|
|
2181
2046
|
}
|
|
2182
2047
|
} catch {
|
|
2048
|
+
// 忽略存储异常,避免影响主流程
|
|
2183
2049
|
}
|
|
2184
2050
|
return null;
|
|
2185
2051
|
}
|
|
2186
2052
|
setStorageItem(key, value) {
|
|
2187
|
-
var _a;
|
|
2188
2053
|
try {
|
|
2189
|
-
if (
|
|
2054
|
+
if (this.storage?.setStorage) {
|
|
2190
2055
|
this.storage.setStorage(key, value);
|
|
2191
2056
|
return;
|
|
2192
2057
|
}
|
|
2193
|
-
if (typeof globalThis !==
|
|
2058
|
+
if (typeof globalThis !== 'undefined' && globalThis?.localStorage) {
|
|
2194
2059
|
globalThis.localStorage.setItem(key, value);
|
|
2195
2060
|
}
|
|
2196
2061
|
} catch {
|
|
2062
|
+
// 忽略存储异常,避免影响主流程
|
|
2197
2063
|
}
|
|
2198
2064
|
}
|
|
2199
2065
|
async loadOrdersFromSQLite() {
|
|
2200
|
-
if (!this.dbManager)
|
|
2201
|
-
return [];
|
|
2066
|
+
if (!this.dbManager) return [];
|
|
2202
2067
|
try {
|
|
2203
2068
|
const orders = await this.dbManager.getAll(INDEXDB_STORE_NAME);
|
|
2204
|
-
return (orders || []).map(
|
|
2205
|
-
(order) => this.normalizeOrderCollectionsForIngress(order, "loadOrdersFromSQLite")
|
|
2206
|
-
);
|
|
2069
|
+
return (orders || []).map(order => this.normalizeOrderCollectionsForIngress(order, 'loadOrdersFromSQLite'));
|
|
2207
2070
|
} catch {
|
|
2208
2071
|
return [];
|
|
2209
2072
|
}
|
|
2210
2073
|
}
|
|
2211
2074
|
isPendingSyncOrder(order) {
|
|
2212
|
-
return Number(order
|
|
2075
|
+
return Number(order?.need_sync) === 1;
|
|
2213
2076
|
}
|
|
2214
2077
|
isDraftOrder(order) {
|
|
2215
|
-
return Number(order
|
|
2078
|
+
return Number(order?.is_draft_order) === 1;
|
|
2216
2079
|
}
|
|
2217
2080
|
shouldProtectDraftOrderFromIncoming(source) {
|
|
2218
|
-
return source !==
|
|
2081
|
+
return source !== 'checkoutSync';
|
|
2219
2082
|
}
|
|
2220
2083
|
normalizeRemoteSyncedOrder(order) {
|
|
2221
|
-
const normalizedOrder = this.normalizeOrderCollectionsForIngress(
|
|
2222
|
-
|
|
2223
|
-
|
|
2224
|
-
|
|
2225
|
-
|
|
2226
|
-
return {
|
|
2227
|
-
...normalizedOrder,
|
|
2228
|
-
is_draft_order: 0
|
|
2229
|
-
};
|
|
2084
|
+
const normalizedOrder = this.normalizeOrderCollectionsForIngress(order, 'normalizeRemoteSyncedOrder');
|
|
2085
|
+
if (this.isPendingSyncOrder(normalizedOrder)) return {
|
|
2086
|
+
...normalizedOrder,
|
|
2087
|
+
is_draft_order: 0
|
|
2088
|
+
};
|
|
2230
2089
|
return {
|
|
2231
2090
|
...normalizedOrder,
|
|
2232
2091
|
need_sync: 0,
|
|
@@ -2238,48 +2097,33 @@ var OrderModule = class extends import_BaseModule.BaseModule {
|
|
|
2238
2097
|
...this.withoutSyntheticDraftOrderId(draftOrder)
|
|
2239
2098
|
};
|
|
2240
2099
|
const remote = remoteOrder;
|
|
2241
|
-
for (const field of [
|
|
2242
|
-
"order_id",
|
|
2243
|
-
"order_number",
|
|
2244
|
-
"shop_order_number",
|
|
2245
|
-
"shop_full_order_number"
|
|
2246
|
-
]) {
|
|
2100
|
+
for (const field of ['order_id', 'order_number', 'shop_order_number', 'shop_full_order_number']) {
|
|
2247
2101
|
if (this.isBlankIdentityValue(merged[field]) && !this.isBlankIdentityValue(remote[field])) {
|
|
2248
2102
|
merged[field] = remote[field];
|
|
2249
2103
|
}
|
|
2250
2104
|
}
|
|
2251
|
-
return this.normalizeOrderCollectionsForIngress(
|
|
2252
|
-
merged,
|
|
2253
|
-
"mergeRemoteIdentityIntoDraftOrder"
|
|
2254
|
-
);
|
|
2105
|
+
return this.normalizeOrderCollectionsForIngress(merged, 'mergeRemoteIdentityIntoDraftOrder');
|
|
2255
2106
|
}
|
|
2256
2107
|
mergeRemoteSnapshotWithPendingOrders(remoteOrders, existingOrders) {
|
|
2257
|
-
const merged = remoteOrders.map(
|
|
2108
|
+
const merged = remoteOrders.map(order => this.normalizeRemoteSyncedOrder(order));
|
|
2258
2109
|
for (const rawOrder of existingOrders) {
|
|
2259
|
-
const order = this.normalizeOrderCollectionsForIngress(
|
|
2260
|
-
|
|
2261
|
-
"mergeRemoteSnapshotWithPendingOrders.existingSQLite"
|
|
2262
|
-
);
|
|
2263
|
-
if (!this.shouldProtectLocalOrderFromRemoteSnapshot(order))
|
|
2264
|
-
continue;
|
|
2110
|
+
const order = this.normalizeOrderCollectionsForIngress(rawOrder, 'mergeRemoteSnapshotWithPendingOrders.existingSQLite');
|
|
2111
|
+
if (!this.shouldProtectLocalOrderFromRemoteSnapshot(order)) continue;
|
|
2265
2112
|
const matchIndex = this.findOrderIndexByIdentity(merged, order);
|
|
2266
2113
|
if (matchIndex >= 0) {
|
|
2267
2114
|
if (this.isDraftOrder(order)) {
|
|
2268
|
-
merged[matchIndex] = this.mergeRemoteIdentityIntoDraftOrder(
|
|
2269
|
-
order,
|
|
2270
|
-
merged[matchIndex]
|
|
2271
|
-
);
|
|
2115
|
+
merged[matchIndex] = this.mergeRemoteIdentityIntoDraftOrder(order, merged[matchIndex]);
|
|
2272
2116
|
} else {
|
|
2273
2117
|
merged[matchIndex] = this.mergeOrderRecords(order, merged[matchIndex]);
|
|
2274
2118
|
}
|
|
2275
2119
|
continue;
|
|
2276
2120
|
}
|
|
2277
|
-
if (!this.getOrderStorageKey(order))
|
|
2278
|
-
continue;
|
|
2121
|
+
if (!this.getOrderStorageKey(order)) continue;
|
|
2279
2122
|
merged.push(order);
|
|
2280
2123
|
}
|
|
2281
|
-
return this.compactOrderListByIdentity(merged,
|
|
2124
|
+
return this.compactOrderListByIdentity(merged, 'mergeRemoteSnapshotWithPendingOrders').list;
|
|
2282
2125
|
}
|
|
2126
|
+
|
|
2283
2127
|
/**
|
|
2284
2128
|
* 串行执行订单 SQLite 写入任务,避免快照替换与增量 upsert 并发交错。
|
|
2285
2129
|
*
|
|
@@ -2290,45 +2134,40 @@ var OrderModule = class extends import_BaseModule.BaseModule {
|
|
|
2290
2134
|
*/
|
|
2291
2135
|
async runInOrderSQLiteSaveQueue(task) {
|
|
2292
2136
|
const queuedTask = this.orderSQLiteSaveQueue.then(task, task);
|
|
2293
|
-
this.orderSQLiteSaveQueue = queuedTask.then(() =>
|
|
2137
|
+
this.orderSQLiteSaveQueue = queuedTask.then(() => undefined, () => undefined);
|
|
2294
2138
|
return queuedTask;
|
|
2295
2139
|
}
|
|
2140
|
+
|
|
2296
2141
|
/**
|
|
2297
2142
|
* 批量增量 upsert 订单到 SQLite,不清空表;用于 pubsub / HTTP 增量合并。
|
|
2298
2143
|
*
|
|
2299
2144
|
* @example
|
|
2300
2145
|
* await this.patchOrdersInSQLite(freshOrders)
|
|
2301
2146
|
*/
|
|
2302
|
-
async patchOrdersInSQLite(orders, source, mergeActions = {}, options = {}, forceWriteStorageKeys =
|
|
2147
|
+
async patchOrdersInSQLite(orders, source, mergeActions = {}, options = {}, forceWriteStorageKeys = new Set()) {
|
|
2303
2148
|
if (orders.length === 0) {
|
|
2304
2149
|
return;
|
|
2305
2150
|
}
|
|
2306
2151
|
if (!this.dbManager) {
|
|
2307
|
-
if (options.throwOnError)
|
|
2308
|
-
throw new Error("订单 SQLite 数据库未初始化");
|
|
2152
|
+
if (options.throwOnError) throw new Error('订单 SQLite 数据库未初始化');
|
|
2309
2153
|
return;
|
|
2310
2154
|
}
|
|
2311
|
-
const migrationTargetStorageKeys = new Set(
|
|
2312
|
-
|
|
2313
|
-
);
|
|
2314
|
-
const ordersSnapshot = (0, import_lodash_es.cloneDeep)(orders).map((order) => this.normalizeOrderCollectionsForIngress(order, `${source}.sqliteIngress`)).map((order) => {
|
|
2155
|
+
const migrationTargetStorageKeys = new Set((options.storageKeyMigrations || []).map(migration => migration.to));
|
|
2156
|
+
const ordersSnapshot = (0, _lodashEs.cloneDeep)(orders).map(order => this.normalizeOrderCollectionsForIngress(order, `${source}.sqliteIngress`)).map(order => {
|
|
2315
2157
|
const storageKey = this.getOrderStorageKey(order);
|
|
2316
|
-
if (!storageKey || !migrationTargetStorageKeys.has(storageKey))
|
|
2317
|
-
return order;
|
|
2158
|
+
if (!storageKey || !migrationTargetStorageKeys.has(storageKey)) return order;
|
|
2318
2159
|
return {
|
|
2319
2160
|
...order,
|
|
2320
2161
|
[ORDER_STORAGE_KEY_FIELD]: storageKey
|
|
2321
2162
|
};
|
|
2322
|
-
}).filter(
|
|
2163
|
+
}).filter(order => this.getOrderStorageKey(order));
|
|
2323
2164
|
if (ordersSnapshot.length === 0) {
|
|
2324
2165
|
return;
|
|
2325
2166
|
}
|
|
2326
|
-
const {
|
|
2327
|
-
|
|
2328
|
-
|
|
2329
|
-
|
|
2330
|
-
forceWriteStorageKeys
|
|
2331
|
-
);
|
|
2167
|
+
const {
|
|
2168
|
+
toWrite,
|
|
2169
|
+
skipped
|
|
2170
|
+
} = this.filterRedundantPatchOrders(source, ordersSnapshot, mergeActions, forceWriteStorageKeys);
|
|
2332
2171
|
if (toWrite.length === 0) {
|
|
2333
2172
|
return;
|
|
2334
2173
|
}
|
|
@@ -2336,55 +2175,49 @@ var OrderModule = class extends import_BaseModule.BaseModule {
|
|
|
2336
2175
|
if (compactedToWrite.length === 0) {
|
|
2337
2176
|
return;
|
|
2338
2177
|
}
|
|
2339
|
-
const writtenStorageKeys = new Set(
|
|
2340
|
-
|
|
2341
|
-
);
|
|
2342
|
-
const storageKeyMigrations = /* @__PURE__ */ new Map();
|
|
2178
|
+
const writtenStorageKeys = new Set(compactedToWrite.map(order => this.getOrderStorageKey(order)).filter(Boolean));
|
|
2179
|
+
const storageKeyMigrations = new Map();
|
|
2343
2180
|
for (const migration of options.storageKeyMigrations || []) {
|
|
2344
|
-
if (!writtenStorageKeys.has(migration.to))
|
|
2345
|
-
|
|
2346
|
-
if (writtenStorageKeys.has(migration.from))
|
|
2347
|
-
continue;
|
|
2181
|
+
if (!writtenStorageKeys.has(migration.to)) continue;
|
|
2182
|
+
if (writtenStorageKeys.has(migration.from)) continue;
|
|
2348
2183
|
storageKeyMigrations.set(migration.from, migration);
|
|
2349
2184
|
}
|
|
2350
2185
|
try {
|
|
2351
2186
|
await this.runInOrderSQLiteSaveQueue(async () => {
|
|
2352
|
-
let writeMethod =
|
|
2353
|
-
this.logInfo(
|
|
2187
|
+
let writeMethod = 'none';
|
|
2188
|
+
this.logInfo('patchOrdersInSQLite-开始', {
|
|
2354
2189
|
source,
|
|
2355
2190
|
count: compactedToWrite.length,
|
|
2356
2191
|
dedupedCount: skipped.length
|
|
2357
2192
|
});
|
|
2358
|
-
if (typeof this.dbManager.bulkUpdate ===
|
|
2359
|
-
writeMethod =
|
|
2193
|
+
if (typeof this.dbManager.bulkUpdate === 'function') {
|
|
2194
|
+
writeMethod = 'bulkUpdate';
|
|
2360
2195
|
await this.dbManager.bulkUpdate(INDEXDB_STORE_NAME, compactedToWrite);
|
|
2361
|
-
} else if (typeof this.dbManager.bulkAdd ===
|
|
2362
|
-
writeMethod =
|
|
2196
|
+
} else if (typeof this.dbManager.bulkAdd === 'function') {
|
|
2197
|
+
writeMethod = 'bulkAdd';
|
|
2363
2198
|
await this.dbManager.bulkAdd(INDEXDB_STORE_NAME, compactedToWrite);
|
|
2364
|
-
} else if (typeof this.dbManager.update ===
|
|
2365
|
-
writeMethod =
|
|
2199
|
+
} else if (typeof this.dbManager.update === 'function') {
|
|
2200
|
+
writeMethod = 'update';
|
|
2366
2201
|
for (const order of compactedToWrite) {
|
|
2367
2202
|
await this.dbManager.update(INDEXDB_STORE_NAME, order);
|
|
2368
2203
|
}
|
|
2369
2204
|
} else if (options.throwOnError) {
|
|
2370
|
-
throw new Error(
|
|
2205
|
+
throw new Error('订单 SQLite 数据库不支持写入');
|
|
2371
2206
|
}
|
|
2372
2207
|
this.recordRecentSqliteWrite(source, compactedToWrite);
|
|
2373
|
-
if (writeMethod !==
|
|
2208
|
+
if (writeMethod !== 'none') {
|
|
2374
2209
|
for (const migration of storageKeyMigrations.values()) {
|
|
2375
2210
|
try {
|
|
2376
2211
|
await this.dbManager.delete(INDEXDB_STORE_NAME, migration.from);
|
|
2377
2212
|
this.recentSqliteWriteByStorageKey.delete(migration.from);
|
|
2378
2213
|
for (const order of orders) {
|
|
2379
|
-
if (this.getOrderStorageKey(order) !== migration.to)
|
|
2380
|
-
continue;
|
|
2214
|
+
if (this.getOrderStorageKey(order) !== migration.to) continue;
|
|
2381
2215
|
const orderRecord = order;
|
|
2382
|
-
if (String(orderRecord[ORDER_STORAGE_KEY_FIELD] ??
|
|
2383
|
-
continue;
|
|
2216
|
+
if (String(orderRecord[ORDER_STORAGE_KEY_FIELD] ?? '') !== migration.from) continue;
|
|
2384
2217
|
orderRecord[ORDER_STORAGE_KEY_FIELD] = migration.to;
|
|
2385
2218
|
}
|
|
2386
2219
|
} catch (error) {
|
|
2387
|
-
this.logError(
|
|
2220
|
+
this.logError('删除订单旧 SQLite storage key 失败', {
|
|
2388
2221
|
source,
|
|
2389
2222
|
order_id: migration.orderId,
|
|
2390
2223
|
oldStorageKey: migration.from,
|
|
@@ -2394,7 +2227,7 @@ var OrderModule = class extends import_BaseModule.BaseModule {
|
|
|
2394
2227
|
}
|
|
2395
2228
|
}
|
|
2396
2229
|
}
|
|
2397
|
-
this.logInfo(
|
|
2230
|
+
this.logInfo('patchOrdersInSQLite-完成', {
|
|
2398
2231
|
source,
|
|
2399
2232
|
count: compactedToWrite.length,
|
|
2400
2233
|
writeMethod,
|
|
@@ -2402,14 +2235,14 @@ var OrderModule = class extends import_BaseModule.BaseModule {
|
|
|
2402
2235
|
});
|
|
2403
2236
|
});
|
|
2404
2237
|
} catch (error) {
|
|
2405
|
-
this.logError(
|
|
2238
|
+
this.logError('增量保存订单到 SQLite 失败', {
|
|
2406
2239
|
error: error instanceof Error ? error.message : String(error),
|
|
2407
2240
|
orderCount: ordersSnapshot.length
|
|
2408
2241
|
});
|
|
2409
|
-
if (options.throwOnError)
|
|
2410
|
-
throw error;
|
|
2242
|
+
if (options.throwOnError) throw error;
|
|
2411
2243
|
}
|
|
2412
2244
|
}
|
|
2245
|
+
|
|
2413
2246
|
/**
|
|
2414
2247
|
* 单条 upsert 订单到 SQLite;用于 overwriteExistingOrder 等单条覆盖场景。
|
|
2415
2248
|
*
|
|
@@ -2418,55 +2251,50 @@ var OrderModule = class extends import_BaseModule.BaseModule {
|
|
|
2418
2251
|
*/
|
|
2419
2252
|
async updateOrderInSQLite(order, source, options = {}) {
|
|
2420
2253
|
if (!this.getOrderStorageKey(order)) {
|
|
2421
|
-
if (options.throwOnError)
|
|
2422
|
-
throw new Error("订单缺少可用的 SQLite 存储主键");
|
|
2254
|
+
if (options.throwOnError) throw new Error('订单缺少可用的 SQLite 存储主键');
|
|
2423
2255
|
return;
|
|
2424
2256
|
}
|
|
2425
2257
|
if (!this.dbManager) {
|
|
2426
|
-
if (options.throwOnError)
|
|
2427
|
-
throw new Error("订单 SQLite 数据库未初始化");
|
|
2258
|
+
if (options.throwOnError) throw new Error('订单 SQLite 数据库未初始化');
|
|
2428
2259
|
return;
|
|
2429
2260
|
}
|
|
2430
|
-
const orderSnapshot = this.normalizeOrderCollectionsForIngress(
|
|
2431
|
-
(0, import_lodash_es.cloneDeep)(order),
|
|
2432
|
-
`${source}.sqliteIngress`
|
|
2433
|
-
);
|
|
2261
|
+
const orderSnapshot = this.normalizeOrderCollectionsForIngress((0, _lodashEs.cloneDeep)(order), `${source}.sqliteIngress`);
|
|
2434
2262
|
try {
|
|
2435
2263
|
await this.runInOrderSQLiteSaveQueue(async () => {
|
|
2436
|
-
let writeMethod =
|
|
2437
|
-
this.logInfo(
|
|
2264
|
+
let writeMethod = 'none';
|
|
2265
|
+
this.logInfo('updateOrderInSQLite-开始', {
|
|
2438
2266
|
source,
|
|
2439
2267
|
orderId: orderSnapshot.order_id ?? null,
|
|
2440
2268
|
storageKey: this.getOrderStorageKey(orderSnapshot)
|
|
2441
2269
|
});
|
|
2442
|
-
if (typeof this.dbManager.update ===
|
|
2443
|
-
writeMethod =
|
|
2270
|
+
if (typeof this.dbManager.update === 'function') {
|
|
2271
|
+
writeMethod = 'update';
|
|
2444
2272
|
await this.dbManager.update(INDEXDB_STORE_NAME, orderSnapshot);
|
|
2445
|
-
} else if (typeof this.dbManager.bulkUpdate ===
|
|
2446
|
-
writeMethod =
|
|
2273
|
+
} else if (typeof this.dbManager.bulkUpdate === 'function') {
|
|
2274
|
+
writeMethod = 'bulkUpdate';
|
|
2447
2275
|
await this.dbManager.bulkUpdate(INDEXDB_STORE_NAME, [orderSnapshot]);
|
|
2448
|
-
} else if (typeof this.dbManager.bulkAdd ===
|
|
2449
|
-
writeMethod =
|
|
2276
|
+
} else if (typeof this.dbManager.bulkAdd === 'function') {
|
|
2277
|
+
writeMethod = 'bulkAdd';
|
|
2450
2278
|
await this.dbManager.bulkAdd(INDEXDB_STORE_NAME, [orderSnapshot]);
|
|
2451
2279
|
} else if (options.throwOnError) {
|
|
2452
|
-
throw new Error(
|
|
2280
|
+
throw new Error('订单 SQLite 数据库不支持写入');
|
|
2453
2281
|
}
|
|
2454
2282
|
this.recordRecentSqliteWrite(source, [orderSnapshot]);
|
|
2455
|
-
this.logInfo(
|
|
2283
|
+
this.logInfo('updateOrderInSQLite-完成', {
|
|
2456
2284
|
source,
|
|
2457
2285
|
orderId: orderSnapshot.order_id ?? null,
|
|
2458
2286
|
writeMethod
|
|
2459
2287
|
});
|
|
2460
2288
|
});
|
|
2461
2289
|
} catch (error) {
|
|
2462
|
-
this.logError(
|
|
2290
|
+
this.logError('单条保存订单到 SQLite 失败', {
|
|
2463
2291
|
error: error instanceof Error ? error.message : String(error),
|
|
2464
2292
|
orderId: orderSnapshot.order_id ?? null
|
|
2465
2293
|
});
|
|
2466
|
-
if (options.throwOnError)
|
|
2467
|
-
throw error;
|
|
2294
|
+
if (options.throwOnError) throw error;
|
|
2468
2295
|
}
|
|
2469
2296
|
}
|
|
2297
|
+
|
|
2470
2298
|
/**
|
|
2471
2299
|
* 全量快照替换 orders 表(clear + bulkAdd),并保留本地 draft / need_sync / pending 支付订单。
|
|
2472
2300
|
* 用于 SSE 全量拉取与营业日窗口裁剪。
|
|
@@ -2476,34 +2304,17 @@ var OrderModule = class extends import_BaseModule.BaseModule {
|
|
|
2476
2304
|
*/
|
|
2477
2305
|
async replaceOrdersSnapshotInSQLite(orderList, source, protectedOrdersBeforeRemoteFetch = []) {
|
|
2478
2306
|
if (!this.dbManager) {
|
|
2479
|
-
return this.compactOrderListByIdentity(
|
|
2480
|
-
|
|
2481
|
-
|
|
2482
|
-
|
|
2483
|
-
}
|
|
2484
|
-
const remoteSnapshot = (0, import_lodash_es.cloneDeep)(orderList).map(
|
|
2485
|
-
(order) => this.normalizeOrderCollectionsForIngress(order, `${source}.remoteIngress`)
|
|
2486
|
-
);
|
|
2487
|
-
let mergedSnapshot = this.compactOrderListByIdentity(
|
|
2488
|
-
remoteSnapshot.map((order) => this.normalizeRemoteSyncedOrder(order)),
|
|
2489
|
-
`${source}.remoteSnapshot`
|
|
2490
|
-
).list;
|
|
2307
|
+
return this.compactOrderListByIdentity(orderList.map(order => this.normalizeRemoteSyncedOrder(order)), `${source}.snapshotNoDb`).list;
|
|
2308
|
+
}
|
|
2309
|
+
const remoteSnapshot = (0, _lodashEs.cloneDeep)(orderList).map(order => this.normalizeOrderCollectionsForIngress(order, `${source}.remoteIngress`));
|
|
2310
|
+
let mergedSnapshot = this.compactOrderListByIdentity(remoteSnapshot.map(order => this.normalizeRemoteSyncedOrder(order)), `${source}.remoteSnapshot`).list;
|
|
2491
2311
|
try {
|
|
2492
2312
|
await this.runInOrderSQLiteSaveQueue(async () => {
|
|
2493
|
-
const existingOrders = typeof this.dbManager.getAll ===
|
|
2494
|
-
const protectedLocalOrders = protectedOrdersBeforeRemoteFetch.map((order) => this.
|
|
2495
|
-
order,
|
|
2496
|
-
`${source}.protectedBeforeRemoteFetch`
|
|
2497
|
-
)).filter((order) => this.shouldProtectLocalOrderFromRemoteSnapshot(order));
|
|
2313
|
+
const existingOrders = typeof this.dbManager.getAll === 'function' ? await this.dbManager.getAll(INDEXDB_STORE_NAME) : [];
|
|
2314
|
+
const protectedLocalOrders = protectedOrdersBeforeRemoteFetch.map(order => this.normalizeOrderCollectionsForIngress(order, `${source}.protectedBeforeRemoteFetch`)).filter(order => this.shouldProtectLocalOrderFromRemoteSnapshot(order));
|
|
2498
2315
|
for (const rawCurrentOrder of existingOrders || []) {
|
|
2499
|
-
const currentOrder = this.normalizeOrderCollectionsForIngress(
|
|
2500
|
-
|
|
2501
|
-
`${source}.currentSQLite`
|
|
2502
|
-
);
|
|
2503
|
-
const protectedIndex = this.findOrderIndexByIdentity(
|
|
2504
|
-
protectedLocalOrders,
|
|
2505
|
-
currentOrder
|
|
2506
|
-
);
|
|
2316
|
+
const currentOrder = this.normalizeOrderCollectionsForIngress(rawCurrentOrder, `${source}.currentSQLite`);
|
|
2317
|
+
const protectedIndex = this.findOrderIndexByIdentity(protectedLocalOrders, currentOrder);
|
|
2507
2318
|
const currentNeedsProtection = this.shouldProtectLocalOrderFromRemoteSnapshot(currentOrder);
|
|
2508
2319
|
if (protectedIndex >= 0) {
|
|
2509
2320
|
if (currentNeedsProtection) {
|
|
@@ -2513,33 +2324,26 @@ var OrderModule = class extends import_BaseModule.BaseModule {
|
|
|
2513
2324
|
}
|
|
2514
2325
|
continue;
|
|
2515
2326
|
}
|
|
2516
|
-
if (currentNeedsProtection)
|
|
2517
|
-
protectedLocalOrders.push(currentOrder);
|
|
2327
|
+
if (currentNeedsProtection) protectedLocalOrders.push(currentOrder);
|
|
2518
2328
|
}
|
|
2519
|
-
const orderListSnapshot = this.mergeRemoteSnapshotWithPendingOrders(
|
|
2520
|
-
remoteSnapshot,
|
|
2521
|
-
protectedLocalOrders
|
|
2522
|
-
);
|
|
2329
|
+
const orderListSnapshot = this.mergeRemoteSnapshotWithPendingOrders(remoteSnapshot, protectedLocalOrders);
|
|
2523
2330
|
mergedSnapshot = this.compactOrderListByIdentity(orderListSnapshot, `${source}.sqliteSnapshot`).list;
|
|
2524
|
-
this.logInfo(
|
|
2331
|
+
this.logInfo('replaceOrdersSnapshotInSQLite-开始', {
|
|
2525
2332
|
source,
|
|
2526
2333
|
count: mergedSnapshot.length,
|
|
2527
2334
|
remoteCount: remoteSnapshot.length,
|
|
2528
2335
|
protectedBeforeRemoteFetchCount: protectedOrdersBeforeRemoteFetch.length,
|
|
2529
2336
|
protectedLocalOrderCount: protectedLocalOrders.length,
|
|
2530
2337
|
preservedLocalUnsyncedCount: mergedSnapshot.length - remoteSnapshot.length,
|
|
2531
|
-
protectedPendingPaymentCount: protectedLocalOrders.reduce(
|
|
2532
|
-
|
|
2533
|
-
0
|
|
2534
|
-
),
|
|
2535
|
-
protectedPendingPaymentIdentities: protectedLocalOrders.flatMap((order) => this.getIdentifiedPendingPayments(order).map((payment) => ({
|
|
2338
|
+
protectedPendingPaymentCount: protectedLocalOrders.reduce((count, order) => count + this.getIdentifiedPendingPayments(order).length, 0),
|
|
2339
|
+
protectedPendingPaymentIdentities: protectedLocalOrders.flatMap(order => this.getIdentifiedPendingPayments(order).map(payment => ({
|
|
2536
2340
|
order_id: order.order_id ?? null,
|
|
2537
2341
|
external_sale_number: order.external_sale_number ?? null,
|
|
2538
|
-
payment_identities: (0,
|
|
2342
|
+
payment_identities: (0, _paymentUtils.getOrderPaymentIdentityKeys)(payment)
|
|
2539
2343
|
})))
|
|
2540
2344
|
});
|
|
2541
2345
|
await this.dbManager.clear(INDEXDB_STORE_NAME);
|
|
2542
|
-
this.logInfo(
|
|
2346
|
+
this.logInfo('replaceOrdersSnapshotInSQLite-clear完成', {
|
|
2543
2347
|
count: mergedSnapshot.length
|
|
2544
2348
|
});
|
|
2545
2349
|
if (mergedSnapshot.length === 0) {
|
|
@@ -2547,19 +2351,20 @@ var OrderModule = class extends import_BaseModule.BaseModule {
|
|
|
2547
2351
|
}
|
|
2548
2352
|
await this.dbManager.bulkAdd(INDEXDB_STORE_NAME, mergedSnapshot);
|
|
2549
2353
|
this.recordRecentSqliteWrite(source, mergedSnapshot);
|
|
2550
|
-
this.logInfo(
|
|
2354
|
+
this.logInfo('replaceOrdersSnapshotInSQLite-bulkAdd完成', {
|
|
2551
2355
|
source,
|
|
2552
2356
|
count: mergedSnapshot.length
|
|
2553
2357
|
});
|
|
2554
2358
|
});
|
|
2555
2359
|
} catch (error) {
|
|
2556
|
-
this.logError(
|
|
2360
|
+
this.logError('全量保存订单到 SQLite 失败', {
|
|
2557
2361
|
error: error instanceof Error ? error.message : String(error),
|
|
2558
2362
|
orderList: remoteSnapshot.length
|
|
2559
2363
|
});
|
|
2560
2364
|
}
|
|
2561
2365
|
return mergedSnapshot;
|
|
2562
2366
|
}
|
|
2367
|
+
|
|
2563
2368
|
/**
|
|
2564
2369
|
* 清空订单模块的内存数据和本地 SQLite 订单表。
|
|
2565
2370
|
*
|
|
@@ -2568,17 +2373,14 @@ var OrderModule = class extends import_BaseModule.BaseModule {
|
|
|
2568
2373
|
*/
|
|
2569
2374
|
async clear() {
|
|
2570
2375
|
this.store.list = [];
|
|
2571
|
-
this.store.map =
|
|
2376
|
+
this.store.map = new Map();
|
|
2572
2377
|
if (this.dbManager) {
|
|
2573
2378
|
await this.runInOrderSQLiteSaveQueue(async () => {
|
|
2574
2379
|
await this.dbManager.clear(INDEXDB_STORE_NAME);
|
|
2575
2380
|
});
|
|
2576
2381
|
}
|
|
2577
|
-
this.setStorageItem(ORDER_LAST_FULL_FETCH_AT_STORAGE_KEY,
|
|
2578
|
-
this.emitOrdersChanged([],
|
|
2382
|
+
this.setStorageItem(ORDER_LAST_FULL_FETCH_AT_STORAGE_KEY, '');
|
|
2383
|
+
this.emitOrdersChanged([], 'clear');
|
|
2579
2384
|
}
|
|
2580
|
-
}
|
|
2581
|
-
|
|
2582
|
-
0 && (module.exports = {
|
|
2583
|
-
OrderModule
|
|
2584
|
-
});
|
|
2385
|
+
}
|
|
2386
|
+
exports.OrderModule = OrderModule;
|