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