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