@pisell/pisellos 0.0.567 → 0.0.569

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (557) hide show
  1. package/dist/core/index.js +6 -9
  2. package/dist/model/strategy/adapter/promotion/evaluator.js +3 -2
  3. package/dist/model/strategy/adapter/walletPass/evaluator.js +0 -2
  4. package/dist/model/strategy/adapter/walletPass/example.js +3 -26
  5. package/dist/model/strategy/adapter/walletPass/utils.js +7 -7
  6. package/dist/model/strategy/strategy-example.js +5 -19
  7. package/dist/modules/BaseModule.d.ts +2 -2
  8. package/dist/modules/BaseModule.js +4 -4
  9. package/dist/modules/BookingContext/index.d.ts +48 -0
  10. package/dist/modules/BookingContext/index.js +566 -0
  11. package/dist/modules/BookingContext/types.d.ts +102 -0
  12. package/dist/modules/BookingContext/types.js +51 -0
  13. package/dist/modules/BookingContext/utils/buildCacheItemFromOrderLine.d.ts +16 -0
  14. package/dist/modules/BookingContext/utils/buildCacheItemFromOrderLine.js +302 -0
  15. package/dist/modules/BookingContext/utils/buildNormalProductCacheItemFromOrderLine.d.ts +8 -0
  16. package/dist/modules/BookingContext/utils/buildNormalProductCacheItemFromOrderLine.js +149 -0
  17. package/dist/modules/BookingContext/utils/cacheItemToBookingInput.d.ts +76 -0
  18. package/dist/modules/BookingContext/utils/cacheItemToBookingInput.js +249 -0
  19. package/dist/modules/BookingContext/utils/flexible.d.ts +28 -0
  20. package/dist/modules/BookingContext/utils/flexible.js +56 -0
  21. package/dist/modules/BookingContext/utils/formatResourceList.d.ts +8 -0
  22. package/dist/modules/BookingContext/utils/formatResourceList.js +38 -0
  23. package/dist/modules/BookingContext/utils/index.d.ts +11 -0
  24. package/dist/modules/BookingContext/utils/index.js +11 -0
  25. package/dist/modules/BookingContext/utils/noResource.d.ts +13 -0
  26. package/dist/modules/BookingContext/utils/noResource.js +77 -0
  27. package/dist/modules/BookingContext/utils/orderLineDisplay.d.ts +1 -0
  28. package/dist/modules/BookingContext/utils/orderLineDisplay.js +36 -0
  29. package/dist/modules/BookingContext/utils/productExtend.d.ts +72 -0
  30. package/dist/modules/BookingContext/utils/productExtend.js +339 -0
  31. package/dist/modules/BookingContext/utils/resourceErrors.d.ts +41 -0
  32. package/dist/modules/BookingContext/utils/resourceErrors.js +74 -0
  33. package/dist/modules/BookingContext/utils/resourceSelection.d.ts +7 -0
  34. package/dist/modules/BookingContext/utils/resourceSelection.js +24 -0
  35. package/dist/modules/BookingContext/utils/resources.d.ts +80 -0
  36. package/dist/modules/BookingContext/utils/resources.js +486 -0
  37. package/dist/modules/BookingContext/utils/serviceTimes.d.ts +55 -0
  38. package/dist/modules/BookingContext/utils/serviceTimes.js +151 -0
  39. package/dist/modules/BookingContext/utils/timeSlices.d.ts +42 -0
  40. package/dist/modules/BookingContext/utils/timeSlices.js +100 -0
  41. package/dist/modules/Cart/index.js +0 -3
  42. package/dist/modules/Cart/utils/cartProduct.js +26 -6
  43. package/dist/modules/Cart/utils/changePrice.js +27 -20
  44. package/dist/modules/Customer/index.d.ts +20 -0
  45. package/dist/modules/Customer/index.js +172 -83
  46. package/dist/modules/Customer/types.d.ts +2 -0
  47. package/dist/modules/Date/index.js +1 -5
  48. package/dist/modules/Discount/index.d.ts +2 -1
  49. package/dist/modules/Discount/index.js +37 -12
  50. package/dist/modules/Discount/types.d.ts +1 -0
  51. package/dist/modules/OpenData/index.d.ts +8 -0
  52. package/dist/modules/OpenData/index.js +35 -17
  53. package/dist/modules/Order/index.d.ts +449 -24
  54. package/dist/modules/Order/index.js +5437 -608
  55. package/dist/modules/Order/types.d.ts +693 -88
  56. package/dist/modules/Order/types.js +68 -1
  57. package/dist/modules/Order/utils/bundleDiscountHydration.d.ts +5 -0
  58. package/dist/modules/Order/utils/bundleDiscountHydration.js +144 -0
  59. package/dist/modules/Order/utils/manualProductDiscount.d.ts +106 -0
  60. package/dist/modules/Order/utils/manualProductDiscount.js +210 -0
  61. package/dist/modules/Order/utils/orderCollectionIdentity.d.ts +53 -0
  62. package/dist/modules/Order/utils/orderCollectionIdentity.js +405 -0
  63. package/dist/modules/Order/utils.d.ts +167 -18
  64. package/dist/modules/Order/utils.js +960 -165
  65. package/dist/modules/Payment/index.d.ts +10 -9
  66. package/dist/modules/Payment/index.js +508 -874
  67. package/dist/modules/Payment/types.d.ts +40 -16
  68. package/dist/modules/Payment/utils.d.ts +1 -1
  69. package/dist/modules/Payment/utils.js +4 -3
  70. package/dist/modules/Payment/walletpass.js +48 -34
  71. package/dist/modules/Product/types.d.ts +29 -7
  72. package/dist/modules/ProductList/index.d.ts +17 -13
  73. package/dist/modules/ProductList/index.js +184 -74
  74. package/dist/modules/ProductList/types.d.ts +17 -0
  75. package/dist/modules/Quotation/index.d.ts +22 -2
  76. package/dist/modules/Quotation/index.js +108 -42
  77. package/dist/modules/Quotation/types.d.ts +19 -0
  78. package/dist/modules/ResourcePlanner/index.d.ts +24 -0
  79. package/dist/modules/ResourcePlanner/index.js +181 -0
  80. package/dist/modules/ResourcePlanner/planner.d.ts +14 -0
  81. package/dist/modules/ResourcePlanner/planner.js +1069 -0
  82. package/dist/modules/ResourcePlanner/types.d.ts +227 -0
  83. package/dist/modules/ResourcePlanner/types.js +1 -0
  84. package/dist/modules/Rules/index.d.ts +8 -10
  85. package/dist/modules/Rules/index.js +219 -114
  86. package/dist/modules/Rules/types.d.ts +14 -3
  87. package/dist/modules/SalesSummary/index.d.ts +23 -26
  88. package/dist/modules/SalesSummary/index.js +152 -46
  89. package/dist/modules/SalesSummary/types.d.ts +9 -1
  90. package/dist/modules/SalesSummary/utils.d.ts +3 -22
  91. package/dist/modules/SalesSummary/utils.js +803 -163
  92. package/dist/modules/Schedule/getDateIsInSchedule.js +14 -7
  93. package/dist/modules/Schedule/index.d.ts +18 -5
  94. package/dist/modules/Schedule/index.js +78 -24
  95. package/dist/modules/Schedule/types.d.ts +2 -0
  96. package/dist/modules/Schedule/utils.js +0 -1
  97. package/dist/modules/Step/index.js +0 -1
  98. package/dist/modules/Summary/index.js +4 -3
  99. package/dist/modules/SurchargeList/index.d.ts +18 -0
  100. package/dist/modules/SurchargeList/index.js +185 -0
  101. package/dist/modules/SurchargeList/types.d.ts +15 -0
  102. package/dist/modules/SurchargeList/types.js +1 -0
  103. package/dist/modules/index.d.ts +3 -0
  104. package/dist/modules/index.js +4 -1
  105. package/dist/plugins/app.d.ts +2 -0
  106. package/dist/plugins/request.d.ts +1 -0
  107. package/dist/plugins/request.js +1 -4
  108. package/dist/plugins/window.js +2 -6
  109. package/dist/server/index.d.ts +462 -8
  110. package/dist/server/index.js +6137 -714
  111. package/dist/server/modules/floor-plan/index.d.ts +39 -0
  112. package/dist/server/modules/floor-plan/index.js +595 -0
  113. package/dist/server/modules/floor-plan/types.d.ts +43 -0
  114. package/dist/server/modules/floor-plan/types.js +13 -0
  115. package/dist/server/modules/index.d.ts +7 -2
  116. package/dist/server/modules/index.js +6 -0
  117. package/dist/server/modules/menu/index.js +32 -41
  118. package/dist/server/modules/order/index.d.ts +207 -14
  119. package/dist/server/modules/order/index.js +2326 -379
  120. package/dist/server/modules/order/types.d.ts +76 -5
  121. package/dist/server/modules/order/types.js +14 -1
  122. package/dist/server/modules/order/utils/filterBookings.d.ts +7 -1
  123. package/dist/server/modules/order/utils/filterBookings.js +118 -11
  124. package/dist/server/modules/order/utils/filterOrders.js +242 -14
  125. package/dist/server/modules/payment/index.d.ts +28 -0
  126. package/dist/server/modules/payment/index.js +305 -0
  127. package/dist/server/modules/payment/types.d.ts +9 -0
  128. package/dist/server/modules/payment/types.js +1 -0
  129. package/dist/server/modules/products/index.d.ts +105 -14
  130. package/dist/server/modules/products/index.js +1397 -540
  131. package/dist/server/modules/products/types.d.ts +34 -1
  132. package/dist/server/modules/products/types.js +4 -0
  133. package/dist/server/modules/quotation/index.d.ts +39 -0
  134. package/dist/server/modules/quotation/index.js +289 -86
  135. package/dist/server/modules/resource/index.d.ts +2 -3
  136. package/dist/server/modules/resource/index.js +105 -84
  137. package/dist/server/modules/schedule/index.d.ts +6 -4
  138. package/dist/server/modules/schedule/index.js +137 -114
  139. package/dist/server/test.json +713 -0
  140. package/dist/server/types.d.ts +2 -0
  141. package/dist/server/utils/product.js +2 -3
  142. package/dist/server/utils/small-ticket.d.ts +71 -0
  143. package/dist/server/utils/small-ticket.js +963 -0
  144. package/dist/solution/BaseSales/index.d.ts +378 -0
  145. package/dist/solution/BaseSales/index.js +4006 -0
  146. package/dist/solution/BaseSales/types.d.ts +394 -0
  147. package/dist/solution/BaseSales/types.js +40 -0
  148. package/dist/solution/BaseSales/utils/cartPromotion.d.ts +287 -0
  149. package/dist/solution/BaseSales/utils/cartPromotion.js +1450 -0
  150. package/dist/solution/BaseSales/utils/parseSalesResponse.d.ts +144 -0
  151. package/dist/solution/BaseSales/utils/parseSalesResponse.js +157 -0
  152. package/dist/solution/BaseSales/utils/quotationPrice.d.ts +18 -0
  153. package/dist/solution/BaseSales/utils/quotationPrice.js +250 -0
  154. package/dist/solution/BaseSales/utils/transformBaseProductToOrderProduct.d.ts +63 -0
  155. package/dist/solution/BaseSales/utils/transformBaseProductToOrderProduct.js +218 -0
  156. package/dist/solution/BaseSales/utils.d.ts +40 -0
  157. package/dist/solution/BaseSales/utils.js +263 -0
  158. package/dist/solution/BookingByStep/index.js +7 -59
  159. package/dist/solution/BookingByStep/utils/capacity.js +1 -11
  160. package/dist/solution/BookingByStep/utils/resources.js +1 -3
  161. package/dist/solution/BookingByStep/utils/timeslots.js +2 -43
  162. package/dist/solution/BookingTicket/index.d.ts +393 -24
  163. package/dist/solution/BookingTicket/index.js +2506 -445
  164. package/dist/solution/BookingTicket/types.d.ts +191 -6
  165. package/dist/solution/BookingTicket/types.js +83 -10
  166. package/dist/solution/BookingTicket/utils/addProductDecision.d.ts +117 -0
  167. package/dist/solution/BookingTicket/utils/addProductDecision.js +346 -0
  168. package/dist/solution/BookingTicket/utils/addTimeAvailability.d.ts +35 -0
  169. package/dist/solution/BookingTicket/utils/addTimeAvailability.js +95 -0
  170. package/dist/solution/BookingTicket/utils/bookingStatus.d.ts +77 -0
  171. package/dist/solution/BookingTicket/utils/bookingStatus.js +136 -0
  172. package/dist/solution/BookingTicket/utils/cartView.d.ts +29 -0
  173. package/dist/solution/BookingTicket/utils/cartView.js +209 -0
  174. package/dist/solution/BookingTicket/utils/exampleData.d.ts +123 -0
  175. package/dist/solution/BookingTicket/utils/exampleData.js +183 -0
  176. package/dist/solution/BookingTicket/utils/orderCustomer.d.ts +4 -0
  177. package/dist/solution/BookingTicket/utils/orderCustomer.js +31 -0
  178. package/dist/solution/BookingTicket/utils/resolveBestAddTimePlan.d.ts +185 -0
  179. package/dist/solution/BookingTicket/utils/resolveBestAddTimePlan.js +438 -0
  180. package/dist/solution/BookingTicket/utils/scan/applyGlobalScan.d.ts +36 -0
  181. package/dist/solution/BookingTicket/utils/scan/applyGlobalScan.js +400 -0
  182. package/dist/solution/BookingTicket/utils/scan/cloudSearch.d.ts +4 -0
  183. package/dist/solution/BookingTicket/utils/scan/cloudSearch.js +41 -1
  184. package/dist/solution/BookingTicket/utils/scan/formatGlobalScan.d.ts +32 -0
  185. package/dist/solution/BookingTicket/utils/scan/formatGlobalScan.js +56 -0
  186. package/dist/solution/BookingTicket/utils/scan/handleScan.d.ts +3 -1
  187. package/dist/solution/BookingTicket/utils/scan/handleScan.js +36 -34
  188. package/dist/solution/BookingTicket/utils/scan/index.d.ts +5 -0
  189. package/dist/solution/BookingTicket/utils/scan/index.js +26 -18
  190. package/dist/solution/BookingTicket/utils/scan/scanProductValues.d.ts +21 -0
  191. package/dist/solution/BookingTicket/utils/scan/scanProductValues.js +150 -0
  192. package/dist/solution/BookingTicket/utils/sessionCatalogStale.d.ts +23 -0
  193. package/dist/solution/BookingTicket/utils/sessionCatalogStale.js +78 -0
  194. package/dist/solution/BuyTickets/index.js +9 -12
  195. package/dist/solution/Checkout/index.d.ts +1 -0
  196. package/dist/solution/Checkout/index.js +199 -277
  197. package/dist/solution/Checkout/utils/index.js +8 -20
  198. package/dist/solution/RegisterAndLogin/config.js +2 -10
  199. package/dist/solution/RegisterAndLogin/index.js +30 -76
  200. package/dist/solution/Sales/index.d.ts +50 -5
  201. package/dist/solution/Sales/index.js +484 -92
  202. package/dist/solution/Sales/types.d.ts +26 -2
  203. package/dist/solution/ScanOrder/index.d.ts +27 -21
  204. package/dist/solution/ScanOrder/index.js +509 -454
  205. package/dist/solution/ScanOrder/types.d.ts +33 -11
  206. package/dist/solution/ScanOrder/types.js +4 -5
  207. package/dist/solution/ScanOrder/utils.d.ts +4 -1
  208. package/dist/solution/ScanOrder/utils.js +109 -34
  209. package/dist/solution/ShopDiscount/index.js +6 -11
  210. package/dist/solution/UnifiedBookingSales/index.d.ts +183 -0
  211. package/dist/solution/UnifiedBookingSales/index.js +1871 -0
  212. package/dist/solution/UnifiedBookingSales/types.d.ts +143 -0
  213. package/dist/solution/UnifiedBookingSales/types.js +11 -0
  214. package/dist/solution/VenueBooking/index.d.ts +34 -20
  215. package/dist/solution/VenueBooking/index.js +630 -505
  216. package/dist/solution/VenueBooking/sales-section-4-annotated.json +0 -2
  217. package/dist/solution/VenueBooking/utils/slotMerge.d.ts +6 -0
  218. package/dist/solution/VenueBooking/utils/slotMerge.js +9 -3
  219. package/dist/solution/index.d.ts +2 -0
  220. package/dist/solution/index.js +3 -1
  221. package/dist/types/index.d.ts +11 -1
  222. package/dist/utils/task.js +15 -18
  223. package/dist/utils/watch.js +0 -1
  224. package/lib/apis/picoding.js +2 -0
  225. package/lib/core/index.js +130 -131
  226. package/lib/effects/index.js +46 -57
  227. package/lib/index.js +92 -53
  228. package/lib/model/index.js +14 -21
  229. package/lib/model/strategy/adapter/dataVariant/adapter.js +38 -50
  230. package/lib/model/strategy/adapter/dataVariant/evaluator.js +162 -236
  231. package/lib/model/strategy/adapter/dataVariant/examples.js +229 -237
  232. package/lib/model/strategy/adapter/dataVariant/index.js +39 -34
  233. package/lib/model/strategy/adapter/dataVariant/type.js +57 -28
  234. package/lib/model/strategy/adapter/index.js +100 -64
  235. package/lib/model/strategy/adapter/itemRule/adapter.js +156 -144
  236. package/lib/model/strategy/adapter/itemRule/evaluator.js +36 -48
  237. package/lib/model/strategy/adapter/itemRule/examples.js +295 -225
  238. package/lib/model/strategy/adapter/itemRule/index.js +83 -57
  239. package/lib/model/strategy/adapter/itemRule/type.js +97 -36
  240. package/lib/model/strategy/adapter/promotion/adapter.js +84 -63
  241. package/lib/model/strategy/adapter/promotion/evaluator.js +383 -232
  242. package/lib/model/strategy/adapter/promotion/examples.js +139 -159
  243. package/lib/model/strategy/adapter/promotion/index.js +1 -49
  244. package/lib/model/strategy/adapter/promotion/type.js +199 -35
  245. package/lib/model/strategy/adapter/type.js +4 -16
  246. package/lib/model/strategy/adapter/walletPass/evaluator.js +180 -152
  247. package/lib/model/strategy/adapter/walletPass/example.js +217 -190
  248. package/lib/model/strategy/adapter/walletPass/index.js +75 -51
  249. package/lib/model/strategy/adapter/walletPass/locales.js +36 -58
  250. package/lib/model/strategy/adapter/walletPass/type.js +4 -16
  251. package/lib/model/strategy/adapter/walletPass/utils.js +654 -402
  252. package/lib/model/strategy/engine.js +270 -168
  253. package/lib/model/strategy/index.js +49 -34
  254. package/lib/model/strategy/strategy-example.js +243 -239
  255. package/lib/model/strategy/type.js +100 -40
  256. package/lib/modules/Account/index.js +39 -53
  257. package/lib/modules/Account/types.js +20 -33
  258. package/lib/modules/AccountList/index.js +116 -154
  259. package/lib/modules/AccountList/types.js +30 -31
  260. package/lib/modules/AccountList/utils.js +18 -30
  261. package/lib/modules/BaseModule.d.ts +2 -2
  262. package/lib/modules/BaseModule.js +26 -49
  263. package/lib/modules/BookingContext/index.d.ts +48 -0
  264. package/lib/modules/BookingContext/index.js +390 -0
  265. package/lib/modules/BookingContext/types.d.ts +102 -0
  266. package/lib/modules/BookingContext/types.js +48 -0
  267. package/lib/modules/BookingContext/utils/buildCacheItemFromOrderLine.d.ts +16 -0
  268. package/lib/modules/BookingContext/utils/buildCacheItemFromOrderLine.js +286 -0
  269. package/lib/modules/BookingContext/utils/buildNormalProductCacheItemFromOrderLine.d.ts +8 -0
  270. package/lib/modules/BookingContext/utils/buildNormalProductCacheItemFromOrderLine.js +148 -0
  271. package/lib/modules/BookingContext/utils/cacheItemToBookingInput.d.ts +76 -0
  272. package/lib/modules/BookingContext/utils/cacheItemToBookingInput.js +242 -0
  273. package/lib/modules/BookingContext/utils/flexible.d.ts +28 -0
  274. package/lib/modules/BookingContext/utils/flexible.js +63 -0
  275. package/lib/modules/BookingContext/utils/formatResourceList.d.ts +8 -0
  276. package/lib/modules/BookingContext/utils/formatResourceList.js +35 -0
  277. package/lib/modules/BookingContext/utils/index.d.ts +11 -0
  278. package/lib/modules/BookingContext/utils/index.js +126 -0
  279. package/lib/modules/BookingContext/utils/noResource.d.ts +13 -0
  280. package/lib/modules/BookingContext/utils/noResource.js +78 -0
  281. package/lib/modules/BookingContext/utils/orderLineDisplay.d.ts +1 -0
  282. package/lib/modules/BookingContext/utils/orderLineDisplay.js +29 -0
  283. package/lib/modules/BookingContext/utils/productExtend.d.ts +72 -0
  284. package/lib/modules/BookingContext/utils/productExtend.js +330 -0
  285. package/lib/modules/BookingContext/utils/resourceErrors.d.ts +41 -0
  286. package/lib/modules/BookingContext/utils/resourceErrors.js +80 -0
  287. package/lib/modules/BookingContext/utils/resourceSelection.d.ts +7 -0
  288. package/lib/modules/BookingContext/utils/resourceSelection.js +24 -0
  289. package/lib/modules/BookingContext/utils/resources.d.ts +80 -0
  290. package/lib/modules/BookingContext/utils/resources.js +419 -0
  291. package/lib/modules/BookingContext/utils/serviceTimes.d.ts +55 -0
  292. package/lib/modules/BookingContext/utils/serviceTimes.js +161 -0
  293. package/lib/modules/BookingContext/utils/timeSlices.d.ts +42 -0
  294. package/lib/modules/BookingContext/utils/timeSlices.js +114 -0
  295. package/lib/modules/Cart/index.js +170 -124
  296. package/lib/modules/Cart/types.js +46 -51
  297. package/lib/modules/Cart/utils/cartAccount.js +40 -39
  298. package/lib/modules/Cart/utils/cartDate.js +56 -61
  299. package/lib/modules/Cart/utils/cartDiscount.js +28 -33
  300. package/lib/modules/Cart/utils/cartNote.js +27 -32
  301. package/lib/modules/Cart/utils/cartProduct.js +331 -244
  302. package/lib/modules/Cart/utils/cartRelationForms.js +29 -35
  303. package/lib/modules/Cart/utils/cartResource.js +72 -78
  304. package/lib/modules/Cart/utils/changePrice.js +37 -67
  305. package/lib/modules/Cart/utils/index.js +106 -48
  306. package/lib/modules/Customer/constants.js +13 -34
  307. package/lib/modules/Customer/index.d.ts +20 -0
  308. package/lib/modules/Customer/index.js +270 -154
  309. package/lib/modules/Customer/types.d.ts +2 -0
  310. package/lib/modules/Customer/types.js +38 -35
  311. package/lib/modules/Date/index.js +116 -115
  312. package/lib/modules/Date/types.js +16 -28
  313. package/lib/modules/Date/utils.js +174 -123
  314. package/lib/modules/Discount/index.d.ts +2 -1
  315. package/lib/modules/Discount/index.js +133 -113
  316. package/lib/modules/Discount/types.d.ts +1 -0
  317. package/lib/modules/Discount/types.js +9 -31
  318. package/lib/modules/Guests/index.js +30 -56
  319. package/lib/modules/Guests/types.js +23 -33
  320. package/lib/modules/Holder/index.js +209 -189
  321. package/lib/modules/Holder/types.js +4 -16
  322. package/lib/modules/Holder/utils.js +20 -49
  323. package/lib/modules/OpenData/index.d.ts +8 -0
  324. package/lib/modules/OpenData/index.js +81 -74
  325. package/lib/modules/OpenData/types.js +4 -16
  326. package/lib/modules/OpenData/utils.js +44 -78
  327. package/lib/modules/Order/index.d.ts +449 -24
  328. package/lib/modules/Order/index.js +3834 -489
  329. package/lib/modules/Order/types.d.ts +693 -88
  330. package/lib/modules/Order/types.js +78 -33
  331. package/lib/modules/Order/utils/bundleDiscountHydration.d.ts +5 -0
  332. package/lib/modules/Order/utils/bundleDiscountHydration.js +106 -0
  333. package/lib/modules/Order/utils/manualProductDiscount.d.ts +106 -0
  334. package/lib/modules/Order/utils/manualProductDiscount.js +207 -0
  335. package/lib/modules/Order/utils/orderCollectionIdentity.d.ts +53 -0
  336. package/lib/modules/Order/utils/orderCollectionIdentity.js +353 -0
  337. package/lib/modules/Order/utils.d.ts +167 -18
  338. package/lib/modules/Order/utils.js +1247 -345
  339. package/lib/modules/Payment/cash.js +20 -33
  340. package/lib/modules/Payment/cashRecommendationAlgorithm.js +145 -76
  341. package/lib/modules/Payment/eftpos.js +16 -30
  342. package/lib/modules/Payment/index.d.ts +10 -9
  343. package/lib/modules/Payment/index.js +542 -502
  344. package/lib/modules/Payment/mx51.js +1 -0
  345. package/lib/modules/Payment/types.d.ts +40 -16
  346. package/lib/modules/Payment/types.js +216 -107
  347. package/lib/modules/Payment/utils.d.ts +1 -1
  348. package/lib/modules/Payment/utils.js +53 -49
  349. package/lib/modules/Payment/walletpass.js +231 -203
  350. package/lib/modules/Product/index.js +51 -46
  351. package/lib/modules/Product/types.d.ts +29 -7
  352. package/lib/modules/Product/types.js +4 -16
  353. package/lib/modules/Product/utils.js +32 -33
  354. package/lib/modules/ProductList/index.d.ts +17 -13
  355. package/lib/modules/ProductList/index.js +160 -118
  356. package/lib/modules/ProductList/types.d.ts +17 -0
  357. package/lib/modules/ProductList/types.js +9 -31
  358. package/lib/modules/Quotation/index.d.ts +22 -2
  359. package/lib/modules/Quotation/index.js +123 -89
  360. package/lib/modules/Quotation/types.d.ts +19 -0
  361. package/lib/modules/Quotation/types.js +4 -16
  362. package/lib/modules/Resource/index.js +27 -59
  363. package/lib/modules/Resource/types.js +22 -32
  364. package/lib/modules/Resource/utils.js +30 -38
  365. package/lib/modules/ResourcePlanner/index.d.ts +24 -0
  366. package/lib/modules/ResourcePlanner/index.js +147 -0
  367. package/lib/modules/ResourcePlanner/planner.d.ts +14 -0
  368. package/lib/modules/ResourcePlanner/planner.js +895 -0
  369. package/lib/modules/ResourcePlanner/types.d.ts +227 -0
  370. package/lib/modules/ResourcePlanner/types.js +5 -0
  371. package/lib/modules/Rules/index.d.ts +8 -10
  372. package/lib/modules/Rules/index.js +1286 -894
  373. package/lib/modules/Rules/types.d.ts +14 -3
  374. package/lib/modules/Rules/types.js +17 -40
  375. package/lib/modules/SalesSummary/index.d.ts +23 -26
  376. package/lib/modules/SalesSummary/index.js +130 -71
  377. package/lib/modules/SalesSummary/types.d.ts +9 -1
  378. package/lib/modules/SalesSummary/types.js +4 -16
  379. package/lib/modules/SalesSummary/utils.d.ts +3 -22
  380. package/lib/modules/SalesSummary/utils.js +738 -250
  381. package/lib/modules/ScanOrderLogger/index.js +59 -79
  382. package/lib/modules/ScanOrderLogger/providers/feishu.js +54 -78
  383. package/lib/modules/ScanOrderLogger/providers/grafana.js +13 -36
  384. package/lib/modules/ScanOrderLogger/types.js +4 -16
  385. package/lib/modules/Schedule/getDateIsInSchedule.js +345 -171
  386. package/lib/modules/Schedule/index.d.ts +18 -5
  387. package/lib/modules/Schedule/index.js +139 -114
  388. package/lib/modules/Schedule/type.js +4 -16
  389. package/lib/modules/Schedule/types.d.ts +2 -0
  390. package/lib/modules/Schedule/types.js +4 -16
  391. package/lib/modules/Schedule/utils.js +433 -291
  392. package/lib/modules/Step/index.js +40 -52
  393. package/lib/modules/Step/tyeps.js +4 -16
  394. package/lib/modules/Summary/index.js +66 -70
  395. package/lib/modules/Summary/types.js +4 -16
  396. package/lib/modules/Summary/utils.js +782 -429
  397. package/lib/modules/SurchargeList/index.d.ts +18 -0
  398. package/lib/modules/SurchargeList/index.js +95 -0
  399. package/lib/modules/SurchargeList/types.d.ts +15 -0
  400. package/lib/modules/SurchargeList/types.js +5 -0
  401. package/lib/modules/index.d.ts +3 -0
  402. package/lib/modules/index.js +245 -57
  403. package/lib/plugins/app.d.ts +2 -0
  404. package/lib/plugins/app.js +4 -16
  405. package/lib/plugins/index.js +36 -25
  406. package/lib/plugins/request.d.ts +1 -0
  407. package/lib/plugins/request.js +137 -126
  408. package/lib/plugins/shopStore.js +4 -16
  409. package/lib/plugins/user.js +4 -16
  410. package/lib/plugins/window.js +171 -179
  411. package/lib/server/index.d.ts +462 -8
  412. package/lib/server/index.js +4668 -713
  413. package/lib/server/modules/floor-plan/index.d.ts +39 -0
  414. package/lib/server/modules/floor-plan/index.js +306 -0
  415. package/lib/server/modules/floor-plan/types.d.ts +43 -0
  416. package/lib/server/modules/floor-plan/types.js +19 -0
  417. package/lib/server/modules/index.d.ts +7 -2
  418. package/lib/server/modules/index.js +106 -59
  419. package/lib/server/modules/menu/index.js +146 -122
  420. package/lib/server/modules/menu/types.js +18 -31
  421. package/lib/server/modules/order/index.d.ts +207 -14
  422. package/lib/server/modules/order/index.js +1463 -316
  423. package/lib/server/modules/order/types.d.ts +76 -5
  424. package/lib/server/modules/order/types.js +117 -32
  425. package/lib/server/modules/order/utils/filterBookings.d.ts +7 -1
  426. package/lib/server/modules/order/utils/filterBookings.js +288 -146
  427. package/lib/server/modules/order/utils/filterOrders.js +318 -64
  428. package/lib/server/modules/payment/index.d.ts +28 -0
  429. package/lib/server/modules/payment/index.js +168 -0
  430. package/lib/server/modules/payment/types.d.ts +9 -0
  431. package/lib/server/modules/payment/types.js +5 -0
  432. package/lib/server/modules/products/index.d.ts +105 -14
  433. package/lib/server/modules/products/index.js +969 -383
  434. package/lib/server/modules/products/types.d.ts +34 -1
  435. package/lib/server/modules/products/types.js +44 -34
  436. package/lib/server/modules/quotation/index.d.ts +39 -0
  437. package/lib/server/modules/quotation/index.js +220 -77
  438. package/lib/server/modules/quotation/types.js +21 -31
  439. package/lib/server/modules/resource/index.d.ts +2 -3
  440. package/lib/server/modules/resource/index.js +269 -214
  441. package/lib/server/modules/resource/types.js +42 -33
  442. package/lib/server/modules/schedule/index.d.ts +6 -4
  443. package/lib/server/modules/schedule/index.js +158 -132
  444. package/lib/server/modules/schedule/types.js +14 -21
  445. package/lib/server/modules/schedule/utils.js +334 -163
  446. package/lib/server/test.json +713 -0
  447. package/lib/server/types.d.ts +2 -0
  448. package/lib/server/types.js +4 -16
  449. package/lib/server/utils/index.js +25 -23
  450. package/lib/server/utils/product.js +197 -140
  451. package/lib/server/utils/schedule.js +34 -41
  452. package/lib/server/utils/small-ticket.d.ts +71 -0
  453. package/lib/server/utils/small-ticket.js +926 -0
  454. package/lib/server/utils/time.js +40 -49
  455. package/lib/solution/BaseSales/index.d.ts +378 -0
  456. package/lib/solution/BaseSales/index.js +2367 -0
  457. package/lib/solution/BaseSales/types.d.ts +394 -0
  458. package/lib/solution/BaseSales/types.js +47 -0
  459. package/lib/solution/BaseSales/utils/cartPromotion.d.ts +287 -0
  460. package/lib/solution/BaseSales/utils/cartPromotion.js +1155 -0
  461. package/lib/solution/BaseSales/utils/parseSalesResponse.d.ts +144 -0
  462. package/lib/solution/BaseSales/utils/parseSalesResponse.js +140 -0
  463. package/lib/solution/BaseSales/utils/quotationPrice.d.ts +18 -0
  464. package/lib/solution/BaseSales/utils/quotationPrice.js +231 -0
  465. package/lib/solution/BaseSales/utils/transformBaseProductToOrderProduct.d.ts +63 -0
  466. package/lib/solution/BaseSales/utils/transformBaseProductToOrderProduct.js +215 -0
  467. package/lib/solution/BaseSales/utils.d.ts +40 -0
  468. package/lib/solution/BaseSales/utils.js +259 -0
  469. package/lib/solution/BookingByStep/index.js +1530 -1343
  470. package/lib/solution/BookingByStep/types.js +40 -99
  471. package/lib/solution/BookingByStep/utils/capacity.js +192 -160
  472. package/lib/solution/BookingByStep/utils/products.js +42 -52
  473. package/lib/solution/BookingByStep/utils/resources.js +527 -330
  474. package/lib/solution/BookingByStep/utils/stock.js +65 -44
  475. package/lib/solution/BookingByStep/utils/timeslots.js +129 -131
  476. package/lib/solution/BookingTicket/index.d.ts +393 -24
  477. package/lib/solution/BookingTicket/index.js +1459 -160
  478. package/lib/solution/BookingTicket/types.d.ts +191 -6
  479. package/lib/solution/BookingTicket/types.js +101 -66
  480. package/lib/solution/BookingTicket/utils/addProductDecision.d.ts +117 -0
  481. package/lib/solution/BookingTicket/utils/addProductDecision.js +353 -0
  482. package/lib/solution/BookingTicket/utils/addTimeAvailability.d.ts +35 -0
  483. package/lib/solution/BookingTicket/utils/addTimeAvailability.js +94 -0
  484. package/lib/solution/BookingTicket/utils/bookingStatus.d.ts +77 -0
  485. package/lib/solution/BookingTicket/utils/bookingStatus.js +136 -0
  486. package/lib/solution/BookingTicket/utils/cartView.d.ts +29 -0
  487. package/lib/solution/BookingTicket/utils/cartView.js +200 -0
  488. package/lib/solution/BookingTicket/utils/exampleData.d.ts +123 -0
  489. package/lib/solution/BookingTicket/utils/exampleData.js +185 -0
  490. package/lib/solution/BookingTicket/utils/orderCustomer.d.ts +4 -0
  491. package/lib/solution/BookingTicket/utils/orderCustomer.js +35 -0
  492. package/lib/solution/BookingTicket/utils/resolveBestAddTimePlan.d.ts +185 -0
  493. package/lib/solution/BookingTicket/utils/resolveBestAddTimePlan.js +387 -0
  494. package/lib/solution/BookingTicket/utils/scan/applyGlobalScan.d.ts +36 -0
  495. package/lib/solution/BookingTicket/utils/scan/applyGlobalScan.js +220 -0
  496. package/lib/solution/BookingTicket/utils/scan/cloudSearch.d.ts +4 -0
  497. package/lib/solution/BookingTicket/utils/scan/cloudSearch.js +85 -80
  498. package/lib/solution/BookingTicket/utils/scan/formatGlobalScan.d.ts +32 -0
  499. package/lib/solution/BookingTicket/utils/scan/formatGlobalScan.js +64 -0
  500. package/lib/solution/BookingTicket/utils/scan/handleScan.d.ts +3 -1
  501. package/lib/solution/BookingTicket/utils/scan/handleScan.js +84 -80
  502. package/lib/solution/BookingTicket/utils/scan/index.d.ts +5 -0
  503. package/lib/solution/BookingTicket/utils/scan/index.js +103 -95
  504. package/lib/solution/BookingTicket/utils/scan/scanCache.js +90 -43
  505. package/lib/solution/BookingTicket/utils/scan/scanProductValues.d.ts +21 -0
  506. package/lib/solution/BookingTicket/utils/scan/scanProductValues.js +115 -0
  507. package/lib/solution/BookingTicket/utils/sessionCatalogStale.d.ts +23 -0
  508. package/lib/solution/BookingTicket/utils/sessionCatalogStale.js +83 -0
  509. package/lib/solution/BuyTickets/index.js +67 -70
  510. package/lib/solution/BuyTickets/types.js +22 -38
  511. package/lib/solution/Checkout/index.d.ts +1 -0
  512. package/lib/solution/Checkout/index.js +1461 -1169
  513. package/lib/solution/Checkout/types.js +91 -61
  514. package/lib/solution/Checkout/utils/index.js +228 -159
  515. package/lib/solution/PlaceOrder/index.js +55 -0
  516. package/lib/solution/RegisterAndLogin/config.js +495 -470
  517. package/lib/solution/RegisterAndLogin/index.js +633 -630
  518. package/lib/solution/RegisterAndLogin/types.js +189 -76
  519. package/lib/solution/RegisterAndLogin/utils.js +183 -125
  520. package/lib/solution/Sales/index.d.ts +50 -5
  521. package/lib/solution/Sales/index.js +495 -220
  522. package/lib/solution/Sales/types.d.ts +26 -2
  523. package/lib/solution/Sales/types.js +27 -33
  524. package/lib/solution/ScanOrder/index.d.ts +27 -21
  525. package/lib/solution/ScanOrder/index.js +908 -904
  526. package/lib/solution/ScanOrder/types.d.ts +33 -11
  527. package/lib/solution/ScanOrder/types.js +33 -34
  528. package/lib/solution/ScanOrder/utils.d.ts +4 -1
  529. package/lib/solution/ScanOrder/utils.js +457 -344
  530. package/lib/solution/ShopDiscount/index.js +230 -236
  531. package/lib/solution/ShopDiscount/types.js +15 -37
  532. package/lib/solution/ShopDiscount/utils.js +309 -172
  533. package/lib/solution/UnifiedBookingSales/index.d.ts +183 -0
  534. package/lib/solution/UnifiedBookingSales/index.js +1050 -0
  535. package/lib/solution/UnifiedBookingSales/types.d.ts +143 -0
  536. package/lib/solution/UnifiedBookingSales/types.js +14 -0
  537. package/lib/solution/VenueBooking/index.d.ts +34 -20
  538. package/lib/solution/VenueBooking/index.js +982 -950
  539. package/lib/solution/VenueBooking/sales-section-4-annotated.json +0 -2
  540. package/lib/solution/VenueBooking/types.js +20 -40
  541. package/lib/solution/VenueBooking/utils/dateSummary.js +22 -54
  542. package/lib/solution/VenueBooking/utils/resource.js +31 -54
  543. package/lib/solution/VenueBooking/utils/slotMerge.d.ts +6 -0
  544. package/lib/solution/VenueBooking/utils/slotMerge.js +96 -108
  545. package/lib/solution/VenueBooking/utils/smartPricing.js +112 -100
  546. package/lib/solution/VenueBooking/utils/timeSlot.js +133 -132
  547. package/lib/solution/VenueBooking/utils.js +130 -67
  548. package/lib/solution/index.d.ts +2 -0
  549. package/lib/solution/index.js +124 -37
  550. package/lib/store/createStore.js +32 -29
  551. package/lib/types/index.d.ts +11 -1
  552. package/lib/types/index.js +4 -16
  553. package/lib/utils/task.js +36 -36
  554. package/lib/utils/watch.js +81 -47
  555. package/package.json +2 -1
  556. package/dist/solution/Checkout/appointmentDemo.json +0 -1
  557. package/lib/solution/Checkout/appointmentDemo.json +0 -1
@@ -1,137 +1,127 @@
1
- var __create = Object.create;
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);
28
-
29
- // src/server/modules/order/index.ts
30
- var order_exports = {};
31
- __export(order_exports, {
32
- OrderModule: () => OrderModule
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
33
5
  });
34
- module.exports = __toCommonJS(order_exports);
35
- var import_lodash_es = require("lodash-es");
36
- var import_dayjs = __toESM(require("dayjs"));
37
- var import_BaseModule = require("../../../modules/BaseModule");
38
- var import_types = require("./types");
39
- var INDEXDB_STORE_NAME = "orders";
40
- var ORDER_LAST_FULL_FETCH_AT_STORAGE_KEY = "server_order_last_full_fetch_at";
41
- var OrderModule = class extends import_BaseModule.BaseModule {
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
+ function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
13
+ /**
14
+ * SQLite 存储名称
15
+ */
16
+ const INDEXDB_STORE_NAME = 'orders';
17
+ const ORDER_LAST_FULL_FETCH_AT_STORAGE_KEY = 'server_order_last_full_fetch_at';
18
+ const ORDER_SYNC_THROTTLE_MS_STORAGE_KEY = 'order_sync_throttle_ms';
19
+ const DEFAULT_ORDER_SYNC_THROTTLE_MS = 2_000;
20
+ /** pubsub 回声写入去重窗口:业务 API 刚写入后,跳过同单 SQLite patch */
21
+ const ORDER_SQLITE_DEDUPE_MS = 15_000;
22
+ /** silentRefresh 最小间隔,避免 preload 后立即全量重拉 */
23
+ const ORDER_SILENT_REFRESH_MIN_INTERVAL_MS = 30_000;
24
+ /** 视为「业务侧刚写入」的来源,pubsub 回声可跳过 SQLite */
25
+ const ORDER_BUSINESS_WRITE_SOURCES = new Set(['upsertOrdersFromRemote', 'upsertPendingSyncOrders', 'overwriteExistingOrder', 'markOrderSyncedByExternalSaleNumber']);
26
+
27
+ /**
28
+ * 合并热路径全表去重自检开关:默认关闭。
29
+ * 开启后每次合并会额外对整张 store 做一次 O(n^2) 重复检测并告警,
30
+ * 仅用于排查增量身份索引是否遗漏了重复订单,不应在生产常开。
31
+ */
32
+ const ORDER_MERGE_SELF_CHECK = false;
33
+ const ORDER_AUTHORITATIVE_COLLECTION_FIELDS = ['products', 'bookings', 'payments'];
34
+ const ORDER_COLLECTION_KIND_BY_FIELD = {
35
+ products: 'product',
36
+ bookings: 'booking',
37
+ payments: 'payment'
38
+ };
39
+
40
+ /**
41
+ * Order 模块 - 基础框架
42
+ */
43
+ class OrderModule extends _BaseModule.BaseModule {
44
+ defaultName = 'order';
45
+ defaultVersion = '1.0.0';
46
+ store;
47
+ dbManager;
48
+ orderDataSource;
49
+ pendingSyncMessages = [];
50
+ syncTimer;
51
+ isProcessingSyncBatch = false;
52
+ isIdlePhase = true;
53
+ logger; // LoggerManager 实例
54
+ storage;
55
+ orderSQLiteSaveQueue = Promise.resolve(); /** 按 storageKey 记录最近一次 SQLite 写入来源与时间,用于去重 */
56
+ recentSqliteWriteByStorageKey = new Map();
57
+ /** 最近一次 SSE 全量拉取完成时间 */
58
+ lastServerFullFetchAtMs = 0;
42
59
  constructor(name, version) {
43
60
  super(name, version);
44
- this.defaultName = "order";
45
- this.defaultVersion = "1.0.0";
46
- this.pendingSyncMessages = [];
47
- this.ORDER_SYNC_DEBOUNCE_MS = 5e3;
48
- // Map<resource_id, OrderId[]> 资源到订单的倒排索引
49
- this.resourceIdIndex = /* @__PURE__ */ new Map();
61
+ }
62
+
63
+ /**
64
+ * 广播订单变更事件,兼容全量列表订阅并额外携带本次变更订单。
65
+ *
66
+ * @example
67
+ * await this.emitOrdersChanged([freshOrder], 'pubsub.bodyUpsert')
68
+ */
69
+ async emitOrdersChanged(changedOrders = [], source) {
70
+ const payload = {
71
+ list: this.store.list,
72
+ changedOrders: changedOrders.map(order => this.normalizeOrderForMemoryList(order)),
73
+ source
74
+ };
75
+ await this.core.effects.emit(_types.OrderHooks.onOrdersChanged, payload);
50
76
  }
51
77
  async initialize(core, options) {
52
- var _a, _b;
53
78
  this.core = core;
54
- this.store = options == null ? void 0 : options.store;
55
- if (!this.store.map)
56
- this.store.map = /* @__PURE__ */ new Map();
57
- const appPlugin = core.getPlugin("app");
58
- const app = appPlugin == null ? void 0 : appPlugin.getApp();
79
+ this.store = options?.store;
80
+ if (!this.store.map) this.store.map = new Map();
81
+ const appPlugin = core.getPlugin('app');
82
+ const app = appPlugin?.getApp();
59
83
  if (app) {
60
84
  this.dbManager = app.sqlite;
61
85
  this.logger = app.logger;
62
86
  this.storage = app.storage;
63
- const coreData = app.data.store.getStore().getDataByModel("core");
64
- if (!((_a = this.store.createdAtQuery) == null ? void 0 : _a.sales_time_between) && (coreData == null ? void 0 : coreData.core)) {
65
- const today = (0, import_dayjs.default)();
66
- if (coreData == null ? void 0 : coreData.core.operating_day_boundary) {
67
- const operatingDayBoundary = coreData.core.operating_day_boundary;
68
- if (operatingDayBoundary.type === "start_time") {
69
- const boundaryTime = String(operatingDayBoundary.time || "").trim();
70
- const boundaryStart = (0, import_dayjs.default)(`${today.format("YYYY-MM-DD")} ${boundaryTime}`);
71
- const boundaryEnd = boundaryStart.add(1, "day");
72
- if (boundaryStart.isValid()) {
73
- this.store.createdAtQuery = {
74
- sales_time_between: [
75
- boundaryStart.format("YYYY-MM-DD HH:mm:ss"),
76
- boundaryEnd.format("YYYY-MM-DD HH:mm:ss")
77
- ]
78
- };
79
- } else {
80
- this.store.createdAtQuery = {
81
- sales_time_between: [
82
- today.startOf("day").format("YYYY-MM-DD HH:mm:ss"),
83
- today.endOf("day").format("YYYY-MM-DD HH:mm:ss")
84
- ]
85
- };
86
- }
87
- } else {
88
- this.store.createdAtQuery = {
89
- sales_time_between: [
90
- today.startOf("day").format("YYYY-MM-DD HH:mm:ss"),
91
- today.endOf("day").format("YYYY-MM-DD HH:mm:ss")
92
- ]
93
- };
94
- }
95
- } else {
96
- this.store.createdAtQuery = {
97
- sales_time_between: [today.startOf("day").format("YYYY-MM-DD HH:mm:ss"), today.endOf("day").format("YYYY-MM-DD HH:mm:ss")]
98
- };
99
- }
100
- }
87
+ this.updateCreateStore();
101
88
  }
102
- if (Array.isArray((_b = options == null ? void 0 : options.initialState) == null ? void 0 : _b.list)) {
103
- this.store.list = options.initialState.list;
89
+ if (Array.isArray(options?.initialState?.list)) {
90
+ this.store.list = options.initialState.list.map(order => this.normalizeOrderForMemoryList(order));
104
91
  this.syncOrdersMap();
105
- this.core.effects.emit(import_types.OrderHooks.onOrdersChanged, this.store.list);
92
+ this.emitOrdersChanged([], 'initialState');
106
93
  } else {
107
94
  this.store.list = [];
108
- this.store.map = /* @__PURE__ */ new Map();
95
+ this.store.map = new Map();
109
96
  }
110
97
  this.initOrderDataSource();
111
98
  this.setupOrderSync();
112
- this.logInfo("OrderServer模块初始化完成", {
99
+ this.logInfo('OrderServer模块初始化完成', {
113
100
  hasDbManager: !!this.dbManager,
114
101
  hasLogger: !!this.logger,
115
102
  initialOrderCount: this.store.list.length
116
103
  });
117
104
  }
105
+
118
106
  /**
119
- * 记录信息日志
120
- * @param title 日志标题
121
- * @param metadata 日志元数据
122
- */
107
+ * 记录信息日志
108
+ * @param title 日志标题
109
+ * @param metadata 日志元数据
110
+ */
123
111
  logInfo(title, metadata) {
124
112
  try {
125
113
  if (this.logger) {
126
114
  this.logger.addLog({
127
- type: "info",
115
+ type: 'info',
128
116
  title: `[OrderServerModule] ${title}`,
129
117
  metadata: metadata || {}
130
118
  });
131
119
  }
132
120
  } catch {
121
+ // 日志记录失败不影响主流程
133
122
  }
134
123
  }
124
+
135
125
  /**
136
126
  * 记录错误日志
137
127
  * @param title 日志标题
@@ -141,21 +131,348 @@ var OrderModule = class extends import_BaseModule.BaseModule {
141
131
  try {
142
132
  if (this.logger) {
143
133
  this.logger.addLog({
144
- type: "error",
134
+ type: 'error',
135
+ title: `[OrderServerModule] ${title}`,
136
+ metadata: metadata || {}
137
+ });
138
+ }
139
+ } catch {
140
+ // 日志记录失败不影响主流程
141
+ }
142
+ }
143
+ logWarning(title, metadata) {
144
+ try {
145
+ if (this.logger) {
146
+ this.logger.addLog({
147
+ type: 'warning',
145
148
  title: `[OrderServerModule] ${title}`,
146
149
  metadata: metadata || {}
147
150
  });
148
151
  }
149
152
  } catch {
153
+ // 日志记录失败不影响主流程
154
+ }
155
+ }
156
+
157
+ /**
158
+ * 在订单进入内存或 SQLite 前统一压缩子项强身份重复并补齐协议 UID。
159
+ * 仅记录计数与订单标识,不输出商品、预约、支付或客户明细。
160
+ */
161
+ normalizeOrderCollectionsForIngress(order, source) {
162
+ const result = (0, _orderCollectionIdentity.normalizeOrderCollections)(order);
163
+ const kinds = ['product', 'booking', 'payment'];
164
+ const hasChanges = kinds.some(kind => {
165
+ const stats = result.stats[kind];
166
+ return stats.backfilledUidCount > 0 || stats.collapsedDuplicateCount > 0 || stats.uidConflictCount > 0 || stats.anonymousRowCount > 0;
167
+ });
168
+ if (hasChanges) {
169
+ const summarize = stats => ({
170
+ backfilled_uid_count: stats.backfilledUidCount,
171
+ collapsed_duplicate_count: stats.collapsedDuplicateCount,
172
+ uid_conflict_count: stats.uidConflictCount,
173
+ anonymous_row_count: stats.anonymousRowCount
174
+ });
175
+ const context = {
176
+ source,
177
+ order_id: order?.order_id ?? null,
178
+ external_sale_number: order?.external_sale_number ?? null,
179
+ products: summarize(result.stats.product),
180
+ bookings: summarize(result.stats.booking),
181
+ payments: summarize(result.stats.payment)
182
+ };
183
+ const hasUnsafeIdentity = kinds.some(kind => {
184
+ const stats = result.stats[kind];
185
+ return stats.uidConflictCount > 0 || stats.anonymousRowCount > 0;
186
+ });
187
+ if (hasUnsafeIdentity) {
188
+ this.logWarning('订单子项身份存在冲突或匿名行', context);
189
+ } else {
190
+ this.logInfo('订单子项身份已规范化', context);
191
+ }
192
+ }
193
+ return result.order;
194
+ }
195
+
196
+ /**
197
+ * incoming 决定权威成员集合;同一持久化行仍从 existing 补齐 incoming 省略的本地展示字段。
198
+ */
199
+ mergeAuthoritativeOrderCollection(field, existing, incoming, uidRemaps = []) {
200
+ const kind = ORDER_COLLECTION_KIND_BY_FIELD[field];
201
+ const existingItems = Array.isArray(existing) ? existing : [];
202
+ return incoming.map(incomingItem => {
203
+ const existingItem = existingItems.find(item => (0, _orderCollectionIdentity.isSameOrderCollectionItem)(kind, item, incomingItem));
204
+ if (!existingItem) return (0, _lodashEs.cloneDeep)(incomingItem);
205
+ const mergedItem = (0, _orderCollectionIdentity.mergeOrderCollectionItems)(kind, existingItem, incomingItem);
206
+ const mergedUid = (0, _orderCollectionIdentity.getOrderCollectionUid)(kind, mergedItem);
207
+ if (mergedUid) {
208
+ for (const from of new Set([(0, _orderCollectionIdentity.getOrderCollectionReferenceUid)(kind, existingItem), (0, _orderCollectionIdentity.getOrderCollectionReferenceUid)(kind, incomingItem)])) {
209
+ if (!from || from === mergedUid) continue;
210
+ uidRemaps.push({
211
+ kind,
212
+ from,
213
+ to: mergedUid,
214
+ persistentId: (0, _orderCollectionIdentity.getOrderCollectionPersistentId)(kind, incomingItem) || (0, _orderCollectionIdentity.getOrderCollectionPersistentId)(kind, existingItem)
215
+ });
216
+ }
217
+ }
218
+ return mergedItem;
219
+ });
220
+ }
221
+ reconcileOverwriteOrderCollections(existing, incoming) {
222
+ const reconciled = (0, _lodashEs.cloneDeep)(incoming);
223
+ const existingRecord = existing;
224
+ const incomingRecord = incoming;
225
+ const uidRemaps = [];
226
+ for (const field of ORDER_AUTHORITATIVE_COLLECTION_FIELDS) {
227
+ if (!Array.isArray(incomingRecord[field])) continue;
228
+ reconciled[field] = this.mergeAuthoritativeOrderCollection(field, existingRecord[field], incomingRecord[field], uidRemaps);
229
+ }
230
+ return this.normalizeOrderCollectionsForIngress((0, _orderCollectionIdentity.applyOrderCollectionUidRemaps)(reconciled, uidRemaps), 'overwriteExistingOrder.reconciled');
231
+ }
232
+
233
+ /**
234
+ * 记录订单最近一次 SQLite 写入,供 pubsub 回声去重使用。
235
+ *
236
+ * @example
237
+ * this.recordRecentSqliteWrite('upsertOrdersFromRemote', orders)
238
+ */
239
+ recordRecentSqliteWrite(source, orders) {
240
+ const now = Date.now();
241
+ for (const order of orders) {
242
+ const storageKey = this.getOrderStorageKey(order);
243
+ if (!storageKey) continue;
244
+ this.recentSqliteWriteByStorageKey.set(storageKey, {
245
+ source,
246
+ ts: now
247
+ });
248
+ }
249
+ }
250
+
251
+ /**
252
+ * 过滤 pubsub 触发的冗余 SQLite patch(业务 API 刚写入过的 update 回声)。
253
+ *
254
+ * @example
255
+ * const { toWrite, skipped } = this.filterRedundantPatchOrders('pubsub.bodyUpsert', orders, mergeActions)
256
+ */
257
+ filterRedundantPatchOrders(source, orders, mergeActions) {
258
+ const eligibleSkipSources = new Set(['pubsub.bodyUpsert', 'pubsub.httpRefresh']);
259
+ if (!eligibleSkipSources.has(source)) {
260
+ return {
261
+ toWrite: orders,
262
+ skipped: []
263
+ };
264
+ }
265
+ const now = Date.now();
266
+ const toWrite = [];
267
+ const skipped = [];
268
+ for (const order of orders) {
269
+ const storageKey = this.getOrderStorageKey(order);
270
+ if (!storageKey) {
271
+ toWrite.push(order);
272
+ continue;
273
+ }
274
+ const orderKey = order.order_id !== undefined && order.order_id !== null ? this.getIdKey(order.order_id) : '';
275
+ const mergeAction = orderKey ? mergeActions[orderKey] : undefined;
276
+
277
+ // 新增单必须落库,不参与去重
278
+ if (mergeAction === 'insert') {
279
+ toWrite.push(order);
280
+ continue;
281
+ }
282
+ const recent = this.recentSqliteWriteByStorageKey.get(storageKey);
283
+ if (recent && ORDER_BUSINESS_WRITE_SOURCES.has(recent.source) && now - recent.ts < ORDER_SQLITE_DEDUPE_MS) {
284
+ skipped.push({
285
+ orderId: order.order_id ?? null,
286
+ storageKey,
287
+ reason: 'skip_pubsub_echo_after_business_write',
288
+ lastSource: recent.source,
289
+ elapsedMs: now - recent.ts
290
+ });
291
+ continue;
292
+ }
293
+ toWrite.push(order);
294
+ }
295
+ return {
296
+ toWrite,
297
+ skipped
298
+ };
299
+ }
300
+
301
+ /**
302
+ * 过滤 pubsub HTTP 增量拉取 ids:本地刚由业务 API 写入的单可跳过 HTTP。
303
+ *
304
+ * @example
305
+ * const ids = this.filterRedundantHttpRefreshIds([99331, 99332])
306
+ */
307
+ filterRedundantHttpRefreshIds(ids) {
308
+ const now = Date.now();
309
+ const toFetch = [];
310
+ const skipped = [];
311
+ for (const id of ids) {
312
+ const existing = this.getOrderByOrderId(id);
313
+ if (!existing) {
314
+ toFetch.push(id);
315
+ continue;
316
+ }
317
+ const storageKey = this.getOrderStorageKey(existing);
318
+ const recent = storageKey ? this.recentSqliteWriteByStorageKey.get(storageKey) : undefined;
319
+ if (recent && recent.source === 'upsertOrdersFromRemote' && now - recent.ts < ORDER_SQLITE_DEDUPE_MS) {
320
+ skipped.push({
321
+ orderId: id,
322
+ lastSource: recent.source,
323
+ elapsedMs: now - recent.ts
324
+ });
325
+ continue;
326
+ }
327
+ toFetch.push(id);
328
+ }
329
+ return {
330
+ toFetch,
331
+ skipped
332
+ };
333
+ }
334
+
335
+ /**
336
+ * 解析 pubsub 消息会走哪条同步分支,便于对照为何触发 HTTP 重拉或 body 直写。
337
+ *
338
+ * @example
339
+ * this.resolveSyncMessageRoute(msg)
340
+ * // => { route: 'httpRefresh.batchIds', ... }
341
+ */
342
+ resolveSyncMessageRoute(msg) {
343
+ const hasBatchIds = Array.isArray(msg.ids) && msg.ids.length > 0;
344
+ const singleIdRaw = msg.id ?? msg.order_id;
345
+ const hasSingleId = !hasBatchIds && singleIdRaw !== undefined && singleIdRaw !== null;
346
+ const normalizedBody = this.normalizeOrderSyncPayload(msg.body || msg.data);
347
+ if (hasBatchIds) {
348
+ return {
349
+ route: 'httpRefresh.batchIds',
350
+ hasBatchIds: true,
351
+ hasSingleId: false,
352
+ hasNormalizedBody: !!normalizedBody,
353
+ singleId: null,
354
+ batchIds: msg.ids || [],
355
+ bodyOrderId: normalizedBody?.order_id ?? null
356
+ };
357
+ }
358
+ if (hasSingleId && normalizedBody) {
359
+ return {
360
+ route: 'bodyUpsert',
361
+ hasBatchIds: false,
362
+ hasSingleId: true,
363
+ hasNormalizedBody: true,
364
+ singleId: singleIdRaw,
365
+ batchIds: [],
366
+ bodyOrderId: normalizedBody.order_id ?? null
367
+ };
368
+ }
369
+ if (hasSingleId) {
370
+ return {
371
+ route: 'httpRefresh.singleId',
372
+ hasBatchIds: false,
373
+ hasSingleId: true,
374
+ hasNormalizedBody: !!normalizedBody,
375
+ singleId: singleIdRaw,
376
+ batchIds: [],
377
+ bodyOrderId: normalizedBody?.order_id ?? null
378
+ };
379
+ }
380
+ if (msg.relation_order_ids?.length) {
381
+ return {
382
+ route: 'httpRefresh.relationIds',
383
+ hasBatchIds: false,
384
+ hasSingleId: false,
385
+ hasNormalizedBody: !!normalizedBody,
386
+ singleId: null,
387
+ batchIds: msg.relation_order_ids,
388
+ bodyOrderId: normalizedBody?.order_id ?? null
389
+ };
390
+ }
391
+ return {
392
+ route: 'noop',
393
+ hasBatchIds: false,
394
+ hasSingleId: false,
395
+ hasNormalizedBody: !!normalizedBody,
396
+ singleId: null,
397
+ batchIds: [],
398
+ bodyOrderId: normalizedBody?.order_id ?? null
399
+ };
400
+ }
401
+ updateCreateStore() {
402
+ const appPlugin = this.core.getPlugin('app');
403
+ const app = appPlugin?.getApp();
404
+ if (app) {
405
+ const coreData = app.data.store.getStore().getDataByModel('core');
406
+ if (coreData?.core) {
407
+ const today = (0, _dayjs.default)();
408
+ if (coreData?.core.operating_day_boundary) {
409
+ const operatingDayBoundary = coreData.core.operating_day_boundary;
410
+ if (operatingDayBoundary.type === 'start_time') {
411
+ const boundaryTime = String(operatingDayBoundary.time || '').trim();
412
+ const boundaryStart = (0, _dayjs.default)(`${today.format('YYYY-MM-DD')} ${boundaryTime}`);
413
+ if (boundaryStart.isValid()) {
414
+ const isAfterBoundary = today.isAfter(boundaryStart);
415
+ const queryStart = isAfterBoundary ? today.startOf('day') : today.subtract(1, 'day').startOf('day');
416
+ const queryEnd = isAfterBoundary ? today.add(1, 'day').endOf('day') : today.endOf('day');
417
+ this.store.createdAtQuery = {
418
+ sales_time_between: [queryStart.format('YYYY-MM-DD HH:mm:ss'), queryEnd.format('YYYY-MM-DD HH:mm:ss')]
419
+ };
420
+ } else {
421
+ // 边界时间异常时回退到自然日,避免 createdAtQuery 为空导致后续查询异常
422
+ this.store.createdAtQuery = {
423
+ sales_time_between: [today.startOf('day').format('YYYY-MM-DD HH:mm:ss'), today.endOf('day').format('YYYY-MM-DD HH:mm:ss')]
424
+ };
425
+ }
426
+ } else {
427
+ this.store.createdAtQuery = {
428
+ sales_time_between: [today.startOf('day').format('YYYY-MM-DD HH:mm:ss'), today.endOf('day').format('YYYY-MM-DD HH:mm:ss')]
429
+ };
430
+ }
431
+ } else {
432
+ // TODO: 这里后续可能需要使用店铺营业时间查询
433
+ this.store.createdAtQuery = {
434
+ sales_time_between: [today.startOf('day').format('YYYY-MM-DD HH:mm:ss'), today.endOf('day').format('YYYY-MM-DD HH:mm:ss')]
435
+ };
436
+ }
437
+ }
438
+ }
439
+ }
440
+ getOrderSyncThrottleMs() {
441
+ const raw = localStorage.getItem(ORDER_SYNC_THROTTLE_MS_STORAGE_KEY);
442
+ if (raw !== null) {
443
+ const parsed = Number(raw);
444
+ if (Number.isFinite(parsed) && parsed >= 0) return parsed;
150
445
  }
446
+ return DEFAULT_ORDER_SYNC_THROTTLE_MS;
151
447
  }
152
448
  async preload() {
153
- const orders = await this.loadOrdersByServer();
154
- if (orders.length === 0)
155
- return;
156
- this.store.list = (0, import_lodash_es.cloneDeep)(orders);
157
- this.syncOrdersMap();
158
- this.core.effects.emit(import_types.OrderHooks.onOrdersChanged, this.store.list);
449
+ // const hasPulledInCurrentWindow = this.hasPulledOrdersToday()
450
+ // // 仅当缓存属于当前统计窗口时才直接复用,避免跨天/跨营业日窗口继续使用旧数据
451
+ // if (cachedOrders.length > 0 && hasPulledInCurrentWindow) {
452
+ // this.store.list = cloneDeep(cachedOrders)
453
+ // this.syncOrdersMap()
454
+ // this.core.effects.emit(OrderHooks.onOrdersChanged, this.store.list)
455
+ // return
456
+ // }
457
+
458
+ // if (hasPulledInCurrentWindow) {
459
+ // this.logInfo('当天已完成全量订单拉取,跳过本次全量拉取', {
460
+ // lastFullFetchAt: this.getStorageItem(ORDER_LAST_FULL_FETCH_AT_STORAGE_KEY),
461
+ // })
462
+ // return
463
+ // }
464
+
465
+ const getData = async () => {
466
+ const orders = await this.loadOrdersByServer();
467
+ if (orders.length === 0) {
468
+ return;
469
+ }
470
+ this.store.list = (0, _lodashEs.cloneDeep)(orders);
471
+ this.syncOrdersMap();
472
+ this.emitOrdersChanged([], 'preload');
473
+ this.core.effects.emit(_types.OrderHooks.onOrdersSyncCompleted, null);
474
+ };
475
+ getData();
159
476
  }
160
477
  getOrders() {
161
478
  return this.store.list;
@@ -163,204 +480,578 @@ var OrderModule = class extends import_BaseModule.BaseModule {
163
480
  getOrderById(id) {
164
481
  return this.store.map.get(this.getIdKey(id));
165
482
  }
166
- /** 按 order_id 查询(推荐) */
483
+
484
+ /** 按 order_id 查询内存订单。 */
167
485
  getOrderByOrderId(orderId) {
168
486
  return this.store.map.get(this.getIdKey(orderId));
169
487
  }
488
+ getLocalOrderFromMemoryByLookup(lookup) {
489
+ const key = this.getIdKey(lookup);
490
+ const memoryOrderById = this.store.map.get(key);
491
+ if (memoryOrderById) return memoryOrderById;
492
+ const memoryOrder = this.store.list.find(order => this.isOrderLookupMatch(order, key));
493
+ return memoryOrder || null;
494
+ }
495
+ async getLocalOrderByLookup(lookup) {
496
+ const key = this.getIdKey(lookup);
497
+ const memoryOrderById = this.store.map.get(key);
498
+ if (memoryOrderById) return memoryOrderById;
499
+ const memoryOrder = this.store.list.find(order => this.isOrderLookupMatch(order, key));
500
+ if (memoryOrder) return memoryOrder;
501
+ const orders = await this.loadOrdersFromSQLite();
502
+ const localOrder = orders.find(order => this.isOrderLookupMatch(order, key));
503
+ if (!localOrder) return null;
504
+ this.store.list = orders.map(order => this.normalizeOrderForMemoryList(order));
505
+ this.syncOrdersMap();
506
+ return this.normalizeOrderForMemoryList(localOrder);
507
+ }
170
508
  async loadOrdersByServer() {
171
509
  let orderList = [];
510
+ this.logInfo('loadOrdersByServer-开始', {
511
+ hasOrderDataSource: !!this.orderDataSource,
512
+ query: this.store?.createdAtQuery || null
513
+ });
172
514
  if (this.orderDataSource) {
173
515
  try {
174
516
  const data = await this.orderDataSource.run({
175
- http: {
517
+ sse: {
176
518
  query: this.store.createdAtQuery
177
519
  }
178
520
  });
179
521
  orderList = data || [];
522
+ this.logInfo('loadOrdersByServer-SSE拉取成功', {
523
+ count: orderList.length
524
+ });
180
525
  this.markOrderPulledAtNow();
526
+ this.lastServerFullFetchAtMs = Date.now();
181
527
  } catch {
182
- orderList = [];
528
+ this.logInfo('loadOrdersByServer-SSE拉取失败,保持当前内存订单');
529
+ return this.store.list;
530
+ }
531
+ }
532
+ const mergedOrders = await this.replaceOrdersSnapshotInSQLite(orderList, 'loadOrdersByServer');
533
+ const memoryOrders = mergedOrders.map(order => this.normalizeOrderForMemoryList(order));
534
+ this.logInfo('loadOrdersByServer-结束', {
535
+ count: memoryOrders.length
536
+ });
537
+ await this.core.effects.emit(_types.OrderHooks.onOrdersLoaded, memoryOrders);
538
+ return memoryOrders;
539
+ }
540
+
541
+ /**
542
+ * 静默全量刷新:后台重新拉取全量 SSE 订单数据并更新本地
543
+ * 拿到完整数据后一次性替换 store,触发订单变更与同步完成事件
544
+ * @returns 刷新后的订单列表
545
+ */
546
+ async silentRefresh() {
547
+ const startTime = Date.now();
548
+ const elapsedSinceFullFetch = Date.now() - this.lastServerFullFetchAtMs;
549
+ if (this.lastServerFullFetchAtMs > 0 && elapsedSinceFullFetch < ORDER_SILENT_REFRESH_MIN_INTERVAL_MS) {
550
+ return this.store.list;
551
+ }
552
+ this.logInfo('silentRefresh 开始');
553
+ try {
554
+ // 刷新前,需要更新当前营业日边界(store.createdAtQuery)
555
+ this.updateCreateStore();
556
+ const orders = await this.loadOrdersByServer();
557
+ if (orders.length > 0) {
558
+ this.store.list = (0, _lodashEs.cloneDeep)(orders);
559
+ this.syncOrdersMap();
560
+ this.emitOrdersChanged([], 'silentRefresh');
561
+ await this.core.effects.emit(_types.OrderHooks.onOrdersSyncCompleted, null);
562
+ this.logInfo('silentRefresh 完成', {
563
+ orderCount: orders.length,
564
+ duration: `${Date.now() - startTime}ms`
565
+ });
566
+ } else {
567
+ this.logInfo('silentRefresh: 服务器未返回数据');
568
+ }
569
+ return this.store.list;
570
+ } catch (error) {
571
+ const errorMessage = error instanceof Error ? error.message : String(error);
572
+ this.logError('silentRefresh 失败', {
573
+ duration: `${Date.now() - startTime}ms`,
574
+ error: errorMessage
575
+ });
576
+ return this.store.list;
577
+ }
578
+ }
579
+ normalizeOrderForMemoryList(order, source = 'normalizeOrderForMemoryList') {
580
+ const normalizedOrder = this.normalizeOrderCollectionsForIngress(order, source);
581
+ const externalSaleNumber = normalizedOrder?.external_sale_number;
582
+ if (normalizedOrder?.order_id !== undefined && normalizedOrder?.order_id !== null && normalizedOrder?.order_id !== '') {
583
+ return normalizedOrder;
584
+ }
585
+ if (externalSaleNumber === undefined || externalSaleNumber === null || externalSaleNumber === '') {
586
+ return normalizedOrder;
587
+ }
588
+ return {
589
+ ...normalizedOrder,
590
+ order_id: externalSaleNumber
591
+ };
592
+ }
593
+
594
+ /**
595
+ * 仅覆盖本地已存在的订单;不存在则直接跳过,不落库、不 emit。
596
+ * 适用于"按 order_id 主动刷新本地订单详情"的代理场景。
597
+ */
598
+ async overwriteExistingOrder(fresh) {
599
+ const orderId = fresh?.order_id;
600
+ if (orderId === undefined || orderId === null) {
601
+ this.logError('overwriteExistingOrder-入参缺少 order_id');
602
+ return {
603
+ overwritten: false
604
+ };
605
+ }
606
+ const key = this.getIdKey(orderId);
607
+ if (!this.store.map.has(key)) {
608
+ this.logInfo('overwriteExistingOrder-本地不存在该订单,跳过', {
609
+ orderId
610
+ });
611
+ return {
612
+ overwritten: false
613
+ };
614
+ }
615
+ this.logInfo('overwriteExistingOrder-开始覆盖', {
616
+ orderId,
617
+ storeOrderCountBefore: this.store.list.length
618
+ });
619
+ const normalizedFresh = this.normalizeOrderForMemoryList(fresh, 'overwriteExistingOrder.incoming');
620
+ const existingOrder = this.store.map.get(key);
621
+ const reconciledFresh = this.reconcileOverwriteOrderCollections(existingOrder, normalizedFresh);
622
+ this.store.list = this.store.list.map(order => {
623
+ const id = order?.order_id;
624
+ if (id === undefined || id === null) {
625
+ return this.normalizeOrderForMemoryList(order, 'overwriteExistingOrder.existingMemory');
626
+ }
627
+ return this.getIdKey(id) === key ? reconciledFresh : this.normalizeOrderForMemoryList(order, 'overwriteExistingOrder.existingMemory');
628
+ });
629
+ this.syncOrdersMap();
630
+ await this.updateOrderInSQLite(reconciledFresh, 'overwriteExistingOrder');
631
+ this.logInfo('overwriteExistingOrder-结束', {
632
+ orderId,
633
+ storeOrderCountAfter: this.store.list.length
634
+ });
635
+ await this.emitOrdersChanged([reconciledFresh], 'overwriteExistingOrder');
636
+ return {
637
+ overwritten: true
638
+ };
639
+ }
640
+
641
+ /**
642
+ * 将远端拉取的订单合并进本地 store(已存在则更新,不存在则追加),落库并 emit onOrdersChanged。
643
+ * 供 /update/localOrder 等在「本地尚无该单」时写入新建单。
644
+ */
645
+ async upsertOrdersFromRemote(freshOrders) {
646
+ if (!freshOrders?.length) {
647
+ this.logInfo('upsertOrdersFromRemote-订单列表为空', {});
648
+ return;
649
+ }
650
+ this.logInfo('upsertOrdersFromRemote-开始', {
651
+ count: freshOrders.length
652
+ });
653
+ await this.mergeOrdersToStore(freshOrders, 'upsertOrdersFromRemote');
654
+ }
655
+ async markOrderSyncedByExternalSaleNumber(params) {
656
+ const externalSaleNumber = params.externalSaleNumber;
657
+ if (externalSaleNumber === undefined || externalSaleNumber === null || externalSaleNumber === '') {
658
+ this.logError('markOrderSyncedByExternalSaleNumber-缺少 external_sale_number');
659
+ return {
660
+ updated: false
661
+ };
662
+ }
663
+ const key = this.getIdKey(externalSaleNumber);
664
+ const targetIndex = this.store.list.findIndex(order => {
665
+ const value = order?.external_sale_number;
666
+ if (value === undefined || value === null || value === '') return false;
667
+ return this.getIdKey(value) === key;
668
+ });
669
+ if (targetIndex < 0) {
670
+ this.logInfo('markOrderSyncedByExternalSaleNumber-本地订单不存在,跳过', {
671
+ external_sale_number: externalSaleNumber
672
+ });
673
+ return {
674
+ updated: false
675
+ };
676
+ }
677
+ const existing = this.store.list[targetIndex];
678
+ const nextOrder = this.normalizeRemoteSyncedOrder({
679
+ ...existing,
680
+ ...(params.patch || {}),
681
+ external_sale_number: existing.external_sale_number ?? externalSaleNumber,
682
+ need_sync: 0
683
+ });
684
+ const storageKey = this.getOrderStorageKey(nextOrder) || key;
685
+ this.store.list = this.store.list.map((order, index) => {
686
+ if (index !== targetIndex) return order;
687
+ return nextOrder;
688
+ });
689
+ this.syncOrdersMap();
690
+ await this.patchOrdersInSQLite([nextOrder], 'markOrderSyncedByExternalSaleNumber', {
691
+ [storageKey]: 'update'
692
+ });
693
+ this.logInfo('markOrderSyncedByExternalSaleNumber-完成', {
694
+ external_sale_number: externalSaleNumber,
695
+ order_id: nextOrder.order_id ?? null
696
+ });
697
+ await this.emitOrdersChanged([nextOrder], 'markOrderSyncedByExternalSaleNumber');
698
+ return {
699
+ updated: true,
700
+ order: nextOrder
701
+ };
702
+ }
703
+
704
+ /**
705
+ * 将本地待同步订单按 SQLite storage key 合并进 store 并落库。
706
+ * checkout 离线兜底可能没有 order_id,但通常会有 external_sale_number。
707
+ */
708
+ async upsertPendingSyncOrders(pendingOrders) {
709
+ if (!pendingOrders?.length) {
710
+ this.logInfo('upsertPendingSyncOrders-订单列表为空', {});
711
+ return;
712
+ }
713
+ const pendingMap = new Map();
714
+ const memoryPendingMap = new Map();
715
+ for (const order of pendingOrders) {
716
+ const normalizedOrder = this.normalizeOrderCollectionsForIngress(order, 'upsertPendingSyncOrders.incoming');
717
+ const storageKey = this.getOrderStorageKey(normalizedOrder);
718
+ if (!storageKey) continue;
719
+ pendingMap.set(storageKey, normalizedOrder);
720
+ memoryPendingMap.set(storageKey, this.normalizeOrderForMemoryList(normalizedOrder, 'upsertPendingSyncOrders.memory'));
721
+ }
722
+ if (pendingMap.size === 0) {
723
+ this.logError('upsertPendingSyncOrders-订单缺少可落库标识', {
724
+ count: pendingOrders.length
725
+ });
726
+ return;
727
+ }
728
+ this.logInfo('upsertPendingSyncOrders-开始', {
729
+ count: pendingMap.size
730
+ });
731
+ const mergeActions = {};
732
+ const updatedList = this.store.list.map(order => this.normalizeOrderForMemoryList(order, 'upsertPendingSyncOrders.existingMemory'));
733
+
734
+ // 与 mergeOrdersToStore 一致:基于身份键(field:value)建立索引,把「查找已存在订单」
735
+ // 从 O(n) 线性扫描降为 O(1),整体复杂度由 O(n^2) 降为 O(n + m)。
736
+ const identityKeyToIndex = new Map();
737
+ for (let index = 0; index < updatedList.length; index += 1) {
738
+ for (const key of this.getOrderIdentityMatchKeys(updatedList[index])) {
739
+ // 同一 key 命中多张时保留最靠前的下标,与原 findIndex「取首个匹配」一致
740
+ if (!identityKeyToIndex.has(key)) identityKeyToIndex.set(key, index);
741
+ }
742
+ }
743
+ for (const [storageKey, pendingOrder] of memoryPendingMap.entries()) {
744
+ const matchIndex = this.lookupOrderIndexByIdentityKeys(identityKeyToIndex, this.getOrderIdentityMatchKeys(pendingOrder));
745
+ if (matchIndex >= 0) {
746
+ const existingOrder = updatedList[matchIndex];
747
+ const mergedOrder = this.mergeOrderRecords(existingOrder, pendingOrder);
748
+ updatedList[matchIndex] = mergedOrder;
749
+ const pendingSnapshot = pendingMap.get(storageKey);
750
+ if (pendingSnapshot) {
751
+ pendingMap.set(storageKey, this.reconcileOverwriteOrderCollections(existingOrder, pendingSnapshot));
752
+ }
753
+ // 合并后身份字段可能新增(如挂单拿到 order_id),同步登记,供同批后续 pending 命中同一槽位
754
+ this.registerOrderIdentityKeys(identityKeyToIndex, mergedOrder, matchIndex);
755
+ mergeActions[storageKey] = 'update';
756
+ continue;
183
757
  }
758
+ const insertIndex = updatedList.length;
759
+ updatedList.push(pendingOrder);
760
+ this.registerOrderIdentityKeys(identityKeyToIndex, pendingOrder, insertIndex);
761
+ mergeActions[storageKey] = 'insert';
184
762
  }
185
- await this.saveOrdersToSQLite(orderList);
186
- await this.core.effects.emit(import_types.OrderHooks.onOrdersLoaded, orderList);
187
- return orderList;
763
+ const patchedOrders = [...pendingMap.values()];
764
+ // 增量索引已保证 store 去重不变量,无需再对全表做 O(n^2) 压缩;仅自检开关开启时兜底。
765
+ this.store.list = this.runOrderStoreSelfCheck(updatedList, 'upsertPendingSyncOrders.store');
766
+ this.syncOrdersMap();
767
+ await this.patchOrdersInSQLite(patchedOrders, 'upsertPendingSyncOrders', mergeActions);
768
+ this.logInfo('upsertPendingSyncOrders-结束', {
769
+ count: patchedOrders.length,
770
+ storeOrderCountAfter: this.store.list.length
771
+ });
772
+ await this.emitOrdersChanged(patchedOrders, 'upsertPendingSyncOrders');
188
773
  }
189
- getOrdersByResourceId(resourceId) {
190
- const ids = this.resourceIdIndex.get(String(resourceId)) || [];
191
- return ids.map((id) => this.store.map.get(id)).filter((order) => !!order);
774
+
775
+ /**
776
+ * 通过 SSE 按自定义 query 拉取订单(支持 select/with 精简字段)
777
+ */
778
+ async fetchOrdersBySSE(query = {}) {
779
+ if (!this.orderDataSource) return [];
780
+ try {
781
+ const data = await this.orderDataSource.run({
782
+ sse: {
783
+ query
784
+ }
785
+ });
786
+ return data || [];
787
+ } catch (error) {
788
+ this.logError('fetchOrdersBySSE-失败', {
789
+ error: error instanceof Error ? error.message : String(error)
790
+ });
791
+ return [];
792
+ }
192
793
  }
193
794
  getRoutes() {
194
795
  return [];
195
796
  }
196
797
  destroy() {
197
- var _a;
198
798
  if (this.syncTimer) {
199
799
  clearTimeout(this.syncTimer);
200
- this.syncTimer = void 0;
800
+ this.syncTimer = undefined;
201
801
  }
802
+ this.isProcessingSyncBatch = false;
202
803
  this.pendingSyncMessages = [];
203
- if ((_a = this.orderDataSource) == null ? void 0 : _a.destroy)
204
- this.orderDataSource.destroy();
804
+ if (this.orderDataSource?.destroy) this.orderDataSource.destroy();
205
805
  super.destroy();
206
806
  }
207
807
  syncOrdersMap() {
208
808
  this.store.map.clear();
209
- this.resourceIdIndex.clear();
210
809
  for (const order of this.store.list) {
211
- const id = order.order_id;
212
- if (id === void 0 || id === null)
213
- continue;
214
- this.store.map.set(this.getIdKey(id), order);
215
- const resourceIds = this.extractResourceIds(order);
216
- for (const resourceId of resourceIds) {
217
- const key = String(resourceId);
218
- const existing = this.resourceIdIndex.get(key) || [];
219
- if (existing.some((eid) => this.getIdKey(eid) === this.getIdKey(id)))
220
- continue;
221
- existing.push(id);
222
- this.resourceIdIndex.set(key, existing);
223
- }
810
+ const primaryIdentity = this.getOrderMapKey(order);
811
+ if (!primaryIdentity) continue;
812
+ this.store.map.set(primaryIdentity, order);
224
813
  }
225
814
  }
226
- extractResourceIds(order) {
227
- const ids = /* @__PURE__ */ new Set();
228
- const visitResources = (resources) => {
229
- if (!Array.isArray(resources))
230
- return;
231
- resources.forEach((resource) => {
232
- const rid = resource == null ? void 0 : resource.relation_id;
233
- if (rid !== void 0 && rid !== null && rid !== "")
234
- ids.add(String(rid));
235
- if (Array.isArray(resource == null ? void 0 : resource.children))
236
- visitResources(resource.children);
815
+ getOrderMapKey(order) {
816
+ if (!order) return '';
817
+ if (!this.isBlankIdentityValue(order.order_id)) {
818
+ return this.getIdKey(order.order_id);
819
+ }
820
+ const externalSaleNumber = order.external_sale_number;
821
+ if (!this.isBlankIdentityValue(externalSaleNumber)) {
822
+ return this.getIdKey(externalSaleNumber);
823
+ }
824
+ return '';
825
+ }
826
+ isBlankIdentityValue(value) {
827
+ return value === undefined || value === null || value === '';
828
+ }
829
+ getOrderIdentityEntries(order) {
830
+ if (!order) return [];
831
+ const record = order;
832
+ const candidates = [{
833
+ field: 'order_id',
834
+ value: order.order_id
835
+ }, {
836
+ field: 'external_sale_number',
837
+ value: record.external_sale_number
838
+ }, {
839
+ field: 'order_number',
840
+ value: record.order_number
841
+ }, {
842
+ field: 'shop_order_number',
843
+ value: record.shop_order_number
844
+ }];
845
+ const entries = [];
846
+ for (const candidate of candidates) {
847
+ if (this.isBlankIdentityValue(candidate.value)) continue;
848
+ const key = this.getIdKey(candidate.value);
849
+ if (entries.some(entry => entry.field === candidate.field && entry.key === key)) continue;
850
+ entries.push({
851
+ field: candidate.field,
852
+ key
237
853
  });
238
- };
239
- if (Array.isArray(order == null ? void 0 : order.bookings)) {
240
- order.bookings.forEach((booking) => visitResources((booking == null ? void 0 : booking.resources) || []));
241
854
  }
242
- return [...ids];
855
+ return entries;
856
+ }
857
+ getOrderIdentityKeys(order) {
858
+ const keys = [];
859
+ for (const entry of this.getOrderIdentityEntries(order)) {
860
+ if (!keys.includes(entry.key)) keys.push(entry.key);
861
+ }
862
+ return keys;
243
863
  }
244
864
  getOrderDateKey(order) {
245
- var _a, _b, _c, _d;
246
- const candidates = [
247
- order == null ? void 0 : order.schedule_date,
248
- order == null ? void 0 : order.create_date,
249
- order == null ? void 0 : order.created_at,
250
- (_b = (_a = order == null ? void 0 : order.bookings) == null ? void 0 : _a[0]) == null ? void 0 : _b.start_date,
251
- (_d = (_c = order == null ? void 0 : order.bookings) == null ? void 0 : _c[0]) == null ? void 0 : _d.select_date
252
- ];
865
+ const candidates = [order?.schedule_date, order?.create_date, order?.created_at, order?.bookings?.[0]?.start_date, order?.bookings?.[0]?.select_date];
253
866
  for (const v of candidates) {
254
- if (!v)
255
- continue;
867
+ if (!v) continue;
256
868
  const text = String(v);
257
- if (!text)
258
- continue;
259
- return text.split("T")[0].split(" ")[0];
869
+ if (!text) continue;
870
+ return text.split('T')[0].split(' ')[0];
260
871
  }
261
- return void 0;
872
+ return undefined;
262
873
  }
874
+
263
875
  /**
264
876
  * 初始化 OrderDataSource 实例
265
877
  */
266
878
  initOrderDataSource() {
267
- var _a, _b;
268
- const OrderDataSourceClass = (_b = (_a = this.core.serverOptions) == null ? void 0 : _a.All_DATA_SOURCES) == null ? void 0 : _b.OrderDataSource;
269
- if (!OrderDataSourceClass)
270
- return;
879
+ const OrderDataSourceClass = this.core.serverOptions?.All_DATA_SOURCES?.OrderDataSource;
880
+ if (!OrderDataSourceClass) return;
271
881
  this.orderDataSource = new OrderDataSourceClass();
272
882
  }
883
+
273
884
  /**
274
- * 初始化 pubsub 订阅,监听订单变更
885
+ * 初始化 pubsub 订阅,监听订单变更(纯订阅,不拉数据;全量 SSE 由 loadOrdersByServer 负责)
275
886
  */
276
887
  setupOrderSync() {
277
- if (!this.orderDataSource)
278
- return;
279
- const shouldSkipHttpPull = this.hasPulledOrdersToday();
280
- const syncPayload = {
888
+ if (!this.orderDataSource) return;
889
+ this.orderDataSource.run({
281
890
  pubsub: {
282
- callback: (res) => {
891
+ callback: res => {
892
+ const pubsubReceivedAt = Date.now();
283
893
  const message = this.normalizeOrderSyncMessage(res);
284
- if (!message)
285
- return;
894
+ if (!message) return;
895
+ message._pubsubReceivedAt = pubsubReceivedAt;
286
896
  this.pendingSyncMessages.push(message);
287
- if (this.syncTimer)
288
- clearTimeout(this.syncTimer);
289
- this.syncTimer = setTimeout(() => {
290
- this.processOrderSyncMessages();
291
- }, this.ORDER_SYNC_DEBOUNCE_MS);
897
+ const throttleMs = this.getOrderSyncThrottleMs();
898
+ const routeInfo = this.resolveSyncMessageRoute(message);
899
+ this.logInfo('orderSync-收到消息并入队', {
900
+ id: message.id ?? null,
901
+ order_id: message.order_id ?? null,
902
+ type: message.type ?? null,
903
+ pendingCount: this.pendingSyncMessages.length,
904
+ throttleMs,
905
+ pubsubReceivedAt: new Date(pubsubReceivedAt).toISOString(),
906
+ fullMessage: JSON.stringify(message)
907
+ });
908
+ this.scheduleOrderSyncThrottle();
292
909
  }
293
910
  }
294
- };
295
- syncPayload.http = {
296
- query: this.store.createdAtQuery,
297
- skipHttp: shouldSkipHttpPull
298
- };
299
- this.orderDataSource.run(syncPayload).then(() => {
300
- if (!shouldSkipHttpPull)
301
- this.markOrderPulledAtNow();
302
911
  }).catch(() => {
912
+ // 忽略同步初始化异常,避免影响主流程
303
913
  });
304
914
  }
915
+
916
+ /**
917
+ * 调度订单同步节流窗口(leading-edge throttle):
918
+ * - 空闲状态下收到的第一条消息立即触发批处理,无需等待;
919
+ * - 首条处理后进入冷却窗口,窗口期间的后续消息聚合,窗口结束后批量处理;
920
+ * - 若当前批处理执行中,则由批处理结束后决定是否开启下一轮窗口。
921
+ */
922
+ scheduleOrderSyncThrottle() {
923
+ if (this.isProcessingSyncBatch) return;
924
+ if (this.isIdlePhase) {
925
+ this.isIdlePhase = false;
926
+ this.logInfo('orderSync-首条消息立即触发处理');
927
+ void this.flushOrderSyncMessagesByThrottle();
928
+ return;
929
+ }
930
+ if (this.syncTimer) return;
931
+ const throttleMs = this.getOrderSyncThrottleMs();
932
+ this.syncTimer = setTimeout(() => {
933
+ this.syncTimer = undefined;
934
+ void this.flushOrderSyncMessagesByThrottle();
935
+ }, throttleMs);
936
+ }
937
+
938
+ /**
939
+ * 执行一次节流批处理并在需要时续约下一轮窗口。
940
+ * 批处理完成且无后续消息时恢复空闲状态,下一条消息将立即触发。
941
+ */
942
+ async flushOrderSyncMessagesByThrottle() {
943
+ if (this.isProcessingSyncBatch) return;
944
+ if (this.pendingSyncMessages.length === 0) {
945
+ this.isIdlePhase = true;
946
+ return;
947
+ }
948
+ this.isProcessingSyncBatch = true;
949
+ try {
950
+ await this.processOrderSyncMessages();
951
+ } finally {
952
+ this.isProcessingSyncBatch = false;
953
+ if (this.pendingSyncMessages.length > 0) {
954
+ this.scheduleOrderSyncThrottle();
955
+ } else {
956
+ this.isIdlePhase = true;
957
+ }
958
+ }
959
+ }
960
+
305
961
  /**
306
- * 处理防抖后的同步消息批次
962
+ * 处理节流窗口内聚合后的同步消息批次
307
963
  *
308
964
  * 后端统一发送 change 消息,不再区分新增/编辑/删除。
309
965
  * - 单条(id/order_id):若携带 body/data 则直接 upsert 到本地
310
966
  * - 批量(ids):通过 HTTP 按 ids 增量拉取,再 merge 到本地
967
+ *
968
+ * @example
969
+ * // pending 队列中存在 8 条消息时,仅处理一次批次并统一 merge
970
+ * await this.processOrderSyncMessages()
311
971
  */
312
972
  async processOrderSyncMessages() {
313
- var _a;
314
973
  const messages = [...this.pendingSyncMessages];
315
974
  this.pendingSyncMessages = [];
316
- if (messages.length === 0)
317
- return;
318
- const upsertOrders = /* @__PURE__ */ new Map();
975
+ if (messages.length === 0) return;
976
+ const batchProcessStartAt = Date.now();
977
+ const earliestReceivedAt = Math.min(...messages.map(m => m._pubsubReceivedAt ?? batchProcessStartAt));
978
+ this.logInfo('processOrderSyncMessages-开始', {
979
+ messageCount: messages.length,
980
+ earliestReceivedAt: new Date(earliestReceivedAt).toISOString(),
981
+ batchProcessStartAt: new Date(batchProcessStartAt).toISOString(),
982
+ waitDurationMs: batchProcessStartAt - earliestReceivedAt
983
+ });
984
+ const upsertOrders = new Map();
319
985
  const refreshIds = [];
320
- for (const msg of messages) {
321
- const hasBatchIds = Array.isArray(msg.ids) && msg.ids.length > 0;
322
- const singleId = msg.id ?? msg.order_id;
323
- const hasSingleId = !hasBatchIds && singleId !== void 0 && singleId !== null;
324
- const rawBody = msg.body || msg.data;
325
- const normalizedBody = this.normalizeOrderSyncPayload(rawBody);
326
- if (hasSingleId && normalizedBody) {
986
+ messages.forEach((msg, msgIndex) => {
987
+ const routeInfo = this.resolveSyncMessageRoute(msg);
988
+ const hasBatchIds = routeInfo.hasBatchIds;
989
+ const singleId = routeInfo.singleId;
990
+ const hasSingleId = routeInfo.hasSingleId;
991
+ const normalizedBody = this.normalizeOrderSyncPayload(msg.body || msg.data);
992
+ if (routeInfo.route === 'bodyUpsert' && normalizedBody) {
327
993
  upsertOrders.set(this.getIdKey(normalizedBody.order_id), normalizedBody);
328
- continue;
994
+ return;
329
995
  }
330
- if (hasBatchIds)
331
- refreshIds.push(...msg.ids);
332
- else if (hasSingleId)
333
- refreshIds.push(singleId);
334
- else if ((_a = msg.relation_order_ids) == null ? void 0 : _a.length)
335
- refreshIds.push(...msg.relation_order_ids);
336
- }
996
+ 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);
997
+ });
337
998
  const uniqueRefreshIds = this.uniqueOrderIds(refreshIds);
338
999
  const upsertList = [...upsertOrders.values()];
339
- if (upsertList.length > 0)
340
- await this.mergeOrdersToStore(upsertList);
1000
+ this.logInfo('processOrderSyncMessages-去重完成', {
1001
+ upsertCount: upsertList.length,
1002
+ refreshIdCount: uniqueRefreshIds.length
1003
+ });
1004
+ if (upsertList.length > 0) {
1005
+ await this.mergeOrdersToStore(upsertList, 'pubsub.bodyUpsert');
1006
+ }
341
1007
  if (uniqueRefreshIds.length > 0) {
342
- const freshOrders = await this.fetchOrdersByHttp(uniqueRefreshIds);
343
- if (freshOrders.length > 0)
344
- await this.mergeOrdersToStore(freshOrders);
1008
+ const {
1009
+ toFetch,
1010
+ skipped
1011
+ } = this.filterRedundantHttpRefreshIds(uniqueRefreshIds);
1012
+ if (skipped.length > 0) {}
1013
+ if (toFetch.length > 0) {
1014
+ const freshOrders = await this.fetchOrdersByHttp(toFetch);
1015
+ if (freshOrders.length > 0) {
1016
+ await this.mergeOrdersToStore(freshOrders, 'pubsub.httpRefresh');
1017
+ } else {}
1018
+ }
345
1019
  }
346
- if (upsertList.length === 0 && uniqueRefreshIds.length === 0)
1020
+ if (upsertList.length === 0 && uniqueRefreshIds.length === 0) {
347
1021
  return;
348
- await this.core.effects.emit(import_types.OrderHooks.onOrdersSyncCompleted, null);
1022
+ }
1023
+ const batchProcessEndAt = Date.now();
1024
+ this.logInfo('processOrderSyncMessages-结束', {
1025
+ upsertCount: upsertList.length,
1026
+ refreshIdCount: uniqueRefreshIds.length,
1027
+ batchProcessDurationMs: batchProcessEndAt - batchProcessStartAt,
1028
+ totalFromPubsubToProcessedMs: batchProcessEndAt - earliestReceivedAt
1029
+ });
1030
+ await this.core.effects.emit(_types.OrderHooks.onOrdersSyncCompleted, null);
349
1031
  }
1032
+
350
1033
  /**
351
- * 通过 HTTP 按 ids 增量拉取订单
1034
+ * 通过 HTTP 按 ids 批量拉取订单详情(走 orderDataSource)。
1035
+ *
1036
+ * - 优先使用 `orderDataSource.fetchOrdersByIds`(如适配器实现了该方法)
1037
+ * - 否则回退到 `orderDataSource.run({ http: { query: { ids } } })`
1038
+ * - 失败统一返回空数组(由调用方按"返回缺失的 id" 判断单笔失败)
1039
+ *
1040
+ * @example
1041
+ * const fresh = await orderModule.fetchOrdersByHttp(['96971', '96972'])
352
1042
  */
353
1043
  async fetchOrdersByHttp(ids) {
354
- if (!this.orderDataSource || ids.length === 0)
355
- return [];
1044
+ if (!this.orderDataSource || ids.length === 0) return [];
356
1045
  try {
357
- if (typeof this.orderDataSource.fetchOrdersByIds === "function") {
358
- const orderList2 = await this.orderDataSource.fetchOrdersByIds(ids);
359
- return orderList2 || [];
1046
+ if (typeof this.orderDataSource.fetchOrdersByIds === 'function') {
1047
+ const orderList = await this.orderDataSource.fetchOrdersByIds(ids);
1048
+ return orderList || [];
360
1049
  }
361
1050
  const orderList = await this.orderDataSource.run({
362
1051
  http: {
363
- query: { ids }
1052
+ query: {
1053
+ ids
1054
+ }
364
1055
  }
365
1056
  });
366
1057
  return orderList || [];
@@ -368,71 +1059,178 @@ var OrderModule = class extends import_BaseModule.BaseModule {
368
1059
  return [];
369
1060
  }
370
1061
  }
1062
+
371
1063
  /**
372
1064
  * 将增量订单合并到 store
373
1065
  */
374
- async mergeOrdersToStore(freshOrders) {
375
- const freshMap = /* @__PURE__ */ new Map();
1066
+ async mergeOrdersToStore(freshOrders, source) {
1067
+ const storeOrderCountBefore = this.store.list.length;
1068
+ this.logInfo('mergeOrdersToStore-开始', {
1069
+ freshOrderCount: freshOrders.length,
1070
+ storeOrderCountBefore,
1071
+ source
1072
+ });
1073
+ this.logDuplicateOrders(`${source}.beforeMerge`, this.store.list);
1074
+ const normalizedFreshOrders = [];
376
1075
  for (const order of freshOrders) {
377
- const id = order == null ? void 0 : order.order_id;
378
- if (id === void 0 || id === null)
1076
+ const id = order?.order_id;
1077
+ if (id === undefined || id === null) continue;
1078
+ normalizedFreshOrders.push(this.normalizeRemoteSyncedOrder(order));
1079
+ }
1080
+ // 先对本批新单做去重(批内规模 m 很小),保证每条 fresh 身份唯一
1081
+ const uniqueFreshOrders = this.compactOrderListByIdentity(normalizedFreshOrders, `${source}.incoming`).list;
1082
+ const uniqueFreshCount = uniqueFreshOrders.length;
1083
+ const patchedOrders = [];
1084
+ const mergeActions = {};
1085
+ const updatedList = this.store.list.map(order => this.normalizeOrderForMemoryList(order, `${source}.existingMemory`));
1086
+
1087
+ // 基于身份键(field:value)建立索引,把「查找已存在订单」从 O(n) 线性扫描降为 O(1),
1088
+ // 整体合并复杂度由 O(n^2) 降为 O(n + m)。索引语义与 doOrdersShareIdentity 完全一致:
1089
+ // 任一身份字段(order_id/external_sale_number/order_number/shop_order_number)同值即同一单。
1090
+ const identityKeyToIndex = new Map();
1091
+ for (let index = 0; index < updatedList.length; index += 1) {
1092
+ const identityKeys = this.getOrderIdentityMatchKeys(updatedList[index]);
1093
+ for (const key of identityKeys) {
1094
+ // 同一 key 命中多张时保留最靠前的下标,与原 findIndex「取首个匹配」一致
1095
+ if (!identityKeyToIndex.has(key)) identityKeyToIndex.set(key, index);
1096
+ }
1097
+ }
1098
+ for (const fresh of uniqueFreshOrders) {
1099
+ const freshIdentityKeys = this.getOrderIdentityMatchKeys(fresh);
1100
+ const matchIndex = this.lookupOrderIndexByIdentityKeys(identityKeyToIndex, freshIdentityKeys);
1101
+ const storageKey = this.getOrderStorageKey(fresh);
1102
+ const mergeKey = storageKey || (fresh.order_id !== undefined && fresh.order_id !== null ? this.getIdKey(fresh.order_id) : '');
1103
+ if (matchIndex >= 0) {
1104
+ const existingOrder = updatedList[matchIndex];
1105
+ if (this.isPendingSyncOrder(existingOrder) && this.isRemoteEchoMergeSource(source)) {
1106
+ this.logInfo('mergeOrdersToStore-跳过 pending 订单远端覆盖', {
1107
+ source,
1108
+ order_id: existingOrder.order_id ?? fresh.order_id ?? null,
1109
+ external_sale_number: existingOrder.external_sale_number ?? fresh.external_sale_number ?? null,
1110
+ local_payment_status: existingOrder.payment_status ?? null,
1111
+ remote_payment_status: fresh.payment_status ?? null,
1112
+ local_need_sync: existingOrder.need_sync ?? null,
1113
+ remote_need_sync: fresh.need_sync ?? null,
1114
+ reason: 'protect_pending_sync_order_from_remote_echo'
1115
+ });
1116
+ continue;
1117
+ }
1118
+ const mergedOrder = this.mergeOrderRecords(existingOrder, fresh);
1119
+ updatedList[matchIndex] = mergedOrder;
1120
+ // 合并后身份字段可能新增(如挂单拿到 order_id),同步登记到索引,
1121
+ // 供同批后续 fresh 命中同一槽位,避免重复插入
1122
+ this.registerOrderIdentityKeys(identityKeyToIndex, mergedOrder, matchIndex);
1123
+ patchedOrders.push(mergedOrder);
1124
+ if (mergeKey) mergeActions[mergeKey] = 'update';
379
1125
  continue;
380
- freshMap.set(this.getIdKey(id), order);
381
- }
382
- const updatedList = this.store.list.map((order) => {
383
- const id = order.order_id;
384
- if (id === void 0 || id === null)
385
- return order;
386
- const key = this.getIdKey(id);
387
- if (!freshMap.has(key))
388
- return order;
389
- const fresh = freshMap.get(key);
390
- freshMap.delete(key);
391
- return fresh;
392
- });
393
- for (const order of freshMap.values()) {
394
- updatedList.push(order);
1126
+ }
1127
+ const insertIndex = updatedList.length;
1128
+ updatedList.push(fresh);
1129
+ this.registerOrderIdentityKeys(identityKeyToIndex, fresh, insertIndex);
1130
+ patchedOrders.push(fresh);
1131
+ if (mergeKey) mergeActions[mergeKey] = 'insert';
395
1132
  }
396
- this.store.list = updatedList;
1133
+ const insertCount = Object.values(mergeActions).filter(v => v === 'insert').length;
1134
+ const updateCount = Object.values(mergeActions).filter(v => v === 'update').length;
1135
+ this.logInfo('mergeOrdersToStore-合并完成', {
1136
+ insertCount,
1137
+ updateCount,
1138
+ storeOrderCountAfter: this.store.list.length
1139
+ });
1140
+ // 增量索引已保证 store 去重不变量,无需再对全表做 O(n^2) 压缩;
1141
+ // 仅在自检开关开启时做一次兜底校验。
1142
+ this.store.list = this.runOrderStoreSelfCheck(updatedList, `${source}.store`);
397
1143
  this.syncOrdersMap();
398
- await this.saveOrdersToSQLite(this.store.list);
399
- this.core.effects.emit(import_types.OrderHooks.onOrdersChanged, this.store.list);
1144
+ await this.patchOrdersInSQLite(patchedOrders, source, mergeActions);
1145
+ this.logInfo('mergeOrdersToStore-结束', {
1146
+ uniqueFreshCount,
1147
+ storeOrderCountAfter: this.store.list.length,
1148
+ insertCount,
1149
+ updateCount
1150
+ });
1151
+ await this.emitOrdersChanged(patchedOrders, source);
1152
+ }
1153
+
1154
+ /**
1155
+ * 在身份索引中查找与目标订单共享任一身份键的已存在订单下标。
1156
+ * 返回所有命中键里最小的下标,等价于原 findOrderIndexByIdentity 的「取首个匹配」语义。
1157
+ *
1158
+ * @example
1159
+ * const idx = this.lookupOrderIndexByIdentityKeys(identityKeyToIndex, freshIdentityKeys)
1160
+ */
1161
+ lookupOrderIndexByIdentityKeys(identityKeyToIndex, identityKeys) {
1162
+ let matchIndex = -1;
1163
+ for (const key of identityKeys) {
1164
+ const index = identityKeyToIndex.get(key);
1165
+ if (index === undefined) continue;
1166
+ if (matchIndex < 0 || index < matchIndex) matchIndex = index;
1167
+ }
1168
+ return matchIndex;
1169
+ }
1170
+ isRemoteEchoMergeSource(source) {
1171
+ return source === 'pubsub.bodyUpsert' || source === 'pubsub.httpRefresh';
1172
+ }
1173
+
1174
+ /**
1175
+ * 将订单的全部身份键登记到索引并指向其在列表中的下标。
1176
+ * 合并后订单新增的身份字段(如挂单同步后拿到 order_id)也会被登记。
1177
+ *
1178
+ * @example
1179
+ * this.registerOrderIdentityKeys(identityKeyToIndex, mergedOrder, matchIndex)
1180
+ */
1181
+ registerOrderIdentityKeys(identityKeyToIndex, order, index) {
1182
+ const identityKeys = this.getOrderIdentityMatchKeys(order);
1183
+ for (const key of identityKeys) {
1184
+ identityKeyToIndex.set(key, index);
1185
+ }
1186
+ }
1187
+
1188
+ /**
1189
+ * 合并热路径去重兜底:默认直接返回原列表(增量索引已保证无重复)。
1190
+ * 仅当 ORDER_MERGE_SELF_CHECK 开启时,额外做一次全表压缩并在发现残留重复时告警。
1191
+ */
1192
+ runOrderStoreSelfCheck(orders, source) {
1193
+ if (!ORDER_MERGE_SELF_CHECK) return orders;
1194
+ const {
1195
+ list,
1196
+ removedCount
1197
+ } = this.compactOrderListByIdentity(orders, source);
1198
+ if (removedCount > 0) {
1199
+ this.logWarning('合并自检发现残留重复订单,已压缩', {
1200
+ source,
1201
+ removedCount
1202
+ });
1203
+ }
1204
+ return list;
400
1205
  }
401
1206
  normalizeOrderSyncMessage(res) {
402
- const data = (res == null ? void 0 : res.data) || res;
403
- if (!data)
404
- return null;
405
- const payload = (data == null ? void 0 : data.data) && typeof data.data === "object" ? data.data : void 0;
1207
+ const data = res?.data || res;
1208
+ if (!data) return null;
1209
+ const payload = data?.data && typeof data.data === 'object' ? data.data : undefined;
406
1210
  const normalized = {
407
1211
  ...data,
408
- _channelKey: data.module || "sales_sync"
1212
+ _channelKey: data.module || 'sales_sync'
409
1213
  };
410
- if (!normalized.type && typeof data.event === "string")
411
- normalized.type = data.event;
412
- if (!normalized.id && data.id !== void 0)
413
- normalized.id = data.id;
414
- if (!normalized.id && data.order_id !== void 0)
415
- normalized.id = data.order_id;
416
- if (!normalized.order_id && data.order_id !== void 0)
417
- normalized.order_id = data.order_id;
418
- if (!normalized.order_id && (payload == null ? void 0 : payload.order_id) !== void 0)
419
- normalized.order_id = payload.order_id;
420
- if (!normalized.body && (payload == null ? void 0 : payload.order_id) !== void 0)
421
- normalized.body = payload;
422
- if (!normalized.data && (payload == null ? void 0 : payload.order_id) !== void 0)
423
- normalized.data = payload;
1214
+ if (!normalized.type && typeof data.event === 'string') normalized.type = data.event;
1215
+ if (!normalized.id && data.id !== undefined) normalized.id = data.id;
1216
+ if (!normalized.id && data.order_id !== undefined) normalized.id = data.order_id;
1217
+ if (!normalized.order_id && data.order_id !== undefined) normalized.order_id = data.order_id;
1218
+ if (!normalized.order_id && payload?.order_id !== undefined) normalized.order_id = payload.order_id;
1219
+ if (!normalized.body && payload?.order_id !== undefined) normalized.body = payload;
1220
+ if (!normalized.data && payload?.order_id !== undefined) normalized.data = payload;
424
1221
  return normalized;
425
1222
  }
426
1223
  normalizeOrderSyncPayload(payload) {
427
- if (!payload)
428
- return null;
1224
+ if (!payload) return null;
429
1225
  const orderId = payload.order_id;
430
- if (orderId === void 0 || orderId === null)
431
- return null;
432
- return { ...payload, order_id: orderId };
1226
+ if (orderId === undefined || orderId === null) return null;
1227
+ return {
1228
+ ...payload,
1229
+ order_id: orderId
1230
+ };
433
1231
  }
434
1232
  uniqueOrderIds(ids) {
435
- const idMap = /* @__PURE__ */ new Map();
1233
+ const idMap = new Map();
436
1234
  for (const id of ids) {
437
1235
  idMap.set(this.getIdKey(id), id);
438
1236
  }
@@ -441,103 +1239,452 @@ var OrderModule = class extends import_BaseModule.BaseModule {
441
1239
  getIdKey(id) {
442
1240
  return String(id);
443
1241
  }
1242
+ isOrderLookupMatch(order, lookupKey) {
1243
+ if (!order || !lookupKey) return false;
1244
+ const candidates = [order.order_id, order.external_sale_number, order.order_number, order.shop_order_number, order.shop_full_order_number];
1245
+ return candidates.some(value => {
1246
+ if (value === undefined || value === null || value === '') return false;
1247
+ return this.getIdKey(value) === lookupKey;
1248
+ });
1249
+ }
1250
+
1251
+ /**
1252
+ * 解析订单 SQLite 主键,规则与宿主 OrderDataSource.getOrderStorageKey 一致。
1253
+ *
1254
+ * @example
1255
+ * const key = this.getOrderStorageKey({ order_id: 1001, external_sale_number: 'S-1001' })
1256
+ * // => 'S-1001'
1257
+ */
1258
+ getOrderStorageKey(order) {
1259
+ const externalSaleNumber = order?.external_sale_number;
1260
+ if (externalSaleNumber !== undefined && externalSaleNumber !== null && externalSaleNumber !== '') {
1261
+ return String(externalSaleNumber);
1262
+ }
1263
+ const orderId = order?.order_id;
1264
+ if (orderId !== undefined && orderId !== null && orderId !== '') {
1265
+ return String(orderId);
1266
+ }
1267
+ return '';
1268
+ }
1269
+ getOrderIdentityMatchKeys(order) {
1270
+ return this.getOrderIdentityEntries(order).map(entry => `${entry.field}:${entry.key}`);
1271
+ }
1272
+ doOrdersShareIdentity(left, right) {
1273
+ const leftKeys = new Set(this.getOrderIdentityMatchKeys(left));
1274
+ if (leftKeys.size === 0) return false;
1275
+ return this.getOrderIdentityMatchKeys(right).some(key => leftKeys.has(key));
1276
+ }
1277
+ findOrderIndexByIdentity(orders, target) {
1278
+ if (this.getOrderIdentityMatchKeys(target).length === 0) return -1;
1279
+ return orders.findIndex(order => this.doOrdersShareIdentity(order, target));
1280
+ }
1281
+ getOrderCompletenessScore(order) {
1282
+ const record = order;
1283
+ let score = this.isPendingSyncOrder(order) ? 0 : 100;
1284
+ if (!this.isBlankIdentityValue(order.order_id) && order.order_id !== record.external_sale_number) score += 30;
1285
+ score += this.getOrderIdentityMatchKeys(order).length * 5;
1286
+ if (Array.isArray(order.products)) score += Math.min(order.products.length, 10);
1287
+ if (Array.isArray(order.bookings)) score += Math.min(order.bookings.length, 10);
1288
+ if (Array.isArray(order.payments)) score += Math.min(order.payments.length, 10);
1289
+ if (record.updated_at) score += 1;
1290
+ return score;
1291
+ }
1292
+ pickPreferredOrder(left, right) {
1293
+ const leftScore = this.getOrderCompletenessScore(left);
1294
+ const rightScore = this.getOrderCompletenessScore(right);
1295
+ if (rightScore >= leftScore) return right;
1296
+ return left;
1297
+ }
1298
+ mergeOrderRecords(existing, incoming) {
1299
+ const preferred = this.pickPreferredOrder(existing, incoming);
1300
+ const secondary = preferred === existing ? incoming : existing;
1301
+ const merged = {
1302
+ ...secondary,
1303
+ ...preferred
1304
+ };
1305
+ const fieldsToPreserve = ['order_id', 'external_sale_number', 'order_number', 'shop_order_number', 'shop_full_order_number'];
1306
+ 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'];
1307
+ const mergedRecord = merged;
1308
+ const preferredRecord = preferred;
1309
+ const secondaryRecord = secondary;
1310
+ const existingRecord = existing;
1311
+ const incomingRecord = incoming;
1312
+ const childUidRemaps = [];
1313
+ for (const field of fieldsToPreserve) {
1314
+ if (!this.isBlankIdentityValue(preferredRecord[field])) {
1315
+ mergedRecord[field] = preferredRecord[field];
1316
+ continue;
1317
+ }
1318
+ if (!this.isBlankIdentityValue(secondaryRecord[field])) {
1319
+ mergedRecord[field] = secondaryRecord[field];
1320
+ }
1321
+ }
1322
+ for (const field of incomingPatchFields) {
1323
+ if (!Object.prototype.hasOwnProperty.call(incomingRecord, field)) continue;
1324
+ if (incomingRecord[field] === undefined || incomingRecord[field] === null) continue;
1325
+ if (typeof incomingRecord[field] === 'string' && incomingRecord[field] === '') continue;
1326
+ mergedRecord[field] = (0, _lodashEs.cloneDeep)(incomingRecord[field]);
1327
+ }
1328
+
1329
+ // 子项数组遵循完整快照语义:incoming 自有且为数组(包括 [])时权威替换;
1330
+ // 字段缺席或不是数组时视为稀疏 patch,继续保留 existing。
1331
+ for (const field of ORDER_AUTHORITATIVE_COLLECTION_FIELDS) {
1332
+ if (Array.isArray(incomingRecord[field])) {
1333
+ mergedRecord[field] = this.mergeAuthoritativeOrderCollection(field, existingRecord[field], incomingRecord[field], childUidRemaps);
1334
+ continue;
1335
+ }
1336
+ if (Object.prototype.hasOwnProperty.call(existingRecord, field)) {
1337
+ mergedRecord[field] = (0, _lodashEs.cloneDeep)(existingRecord[field]);
1338
+ } else {
1339
+ delete mergedRecord[field];
1340
+ }
1341
+ }
1342
+ if (!this.isPendingSyncOrder(preferred) || !this.isPendingSyncOrder(secondary)) {
1343
+ merged.need_sync = 0;
1344
+ }
1345
+ return this.normalizeOrderCollectionsForIngress((0, _orderCollectionIdentity.applyOrderCollectionUidRemaps)(merged, childUidRemaps), 'mergeOrderRecords');
1346
+ }
1347
+ summarizeDuplicateOrders(orders) {
1348
+ const groups = new Map();
1349
+ for (const order of orders) {
1350
+ for (const key of this.getOrderIdentityMatchKeys(order)) {
1351
+ const group = groups.get(key) || [];
1352
+ group.push(order);
1353
+ groups.set(key, group);
1354
+ }
1355
+ }
1356
+ return [...groups.entries()].filter(([, group]) => group.length > 1).slice(0, 20).map(([key, group]) => ({
1357
+ key,
1358
+ count: group.length,
1359
+ orders: group.map(order => ({
1360
+ order_id: order.order_id ?? null,
1361
+ external_sale_number: order.external_sale_number ?? null,
1362
+ shop_order_number: order.shop_order_number ?? null,
1363
+ shop_full_order_number: order.shop_full_order_number ?? null,
1364
+ need_sync: order.need_sync ?? null
1365
+ }))
1366
+ }));
1367
+ }
1368
+ logDuplicateOrders(source, orders) {
1369
+ const duplicateGroups = this.summarizeDuplicateOrders(orders);
1370
+ if (duplicateGroups.length === 0) return;
1371
+ this.logWarning('检测到重复订单身份键', {
1372
+ source,
1373
+ duplicateGroupCount: duplicateGroups.length,
1374
+ duplicateGroups
1375
+ });
1376
+ }
1377
+ compactOrderListByIdentity(orders, source) {
1378
+ const list = [];
1379
+ let removedCount = 0;
1380
+ for (const order of orders) {
1381
+ const normalizedOrder = this.normalizeOrderCollectionsForIngress(order, `${source}.order`);
1382
+ const matchIndex = this.findOrderIndexByIdentity(list, normalizedOrder);
1383
+ if (matchIndex < 0) {
1384
+ list.push(normalizedOrder);
1385
+ continue;
1386
+ }
1387
+ list[matchIndex] = this.mergeOrderRecords(list[matchIndex], normalizedOrder);
1388
+ removedCount += 1;
1389
+ }
1390
+ if (removedCount > 0) {
1391
+ this.logWarning('订单列表重复项已压缩', {
1392
+ source,
1393
+ beforeCount: orders.length,
1394
+ afterCount: list.length,
1395
+ removedCount
1396
+ });
1397
+ } else {
1398
+ this.logDuplicateOrders(source, orders);
1399
+ }
1400
+ return {
1401
+ list,
1402
+ removedCount
1403
+ };
1404
+ }
1405
+
444
1406
  /**
445
1407
  * 当前订单拉取窗口:
446
1408
  * - 优先使用 createdAtQuery.sales_time_between(已按营业日边界初始化)
447
1409
  * - 无法解析时退化为自然日窗口
448
1410
  */
449
1411
  getCurrentPullWindow() {
450
- var _a, _b;
451
- const now = (0, import_dayjs.default)();
452
- const range = (_b = (_a = this.store) == null ? void 0 : _a.createdAtQuery) == null ? void 0 : _b.sales_time_between;
1412
+ const now = (0, _dayjs.default)();
1413
+ const range = this.store?.createdAtQuery?.sales_time_between;
453
1414
  if (Array.isArray(range) && range.length >= 2) {
454
- const startAt2 = (0, import_dayjs.default)(range[0]);
455
- const endExclusiveAt = (0, import_dayjs.default)(range[1]);
456
- if (startAt2.isValid() && endExclusiveAt.isValid() && endExclusiveAt.isAfter(startAt2)) {
457
- return { startAt: startAt2, endExclusiveAt };
1415
+ const startAt = (0, _dayjs.default)(range[0]);
1416
+ const endExclusiveAt = (0, _dayjs.default)(range[1]);
1417
+ if (startAt.isValid() && endExclusiveAt.isValid() && endExclusiveAt.isAfter(startAt)) {
1418
+ return {
1419
+ startAt,
1420
+ endExclusiveAt
1421
+ };
458
1422
  }
459
1423
  }
460
- const startAt = now.startOf("day");
461
- return { startAt, endExclusiveAt: startAt.add(1, "day") };
1424
+ const startAt = now.startOf('day');
1425
+ return {
1426
+ startAt,
1427
+ endExclusiveAt: startAt.add(1, 'day')
1428
+ };
462
1429
  }
463
1430
  hasPulledOrdersToday() {
464
1431
  const lastFullFetchAt = this.getStorageItem(ORDER_LAST_FULL_FETCH_AT_STORAGE_KEY);
465
- if (!lastFullFetchAt)
466
- return false;
467
- const parsed = (0, import_dayjs.default)(lastFullFetchAt);
468
- if (!parsed.isValid())
469
- return false;
470
- const { startAt, endExclusiveAt } = this.getCurrentPullWindow();
1432
+ if (!lastFullFetchAt) return false;
1433
+ const parsed = (0, _dayjs.default)(lastFullFetchAt);
1434
+ if (!parsed.isValid()) return false;
1435
+
1436
+ // 按当前拉取窗口判断,而不是自然日判断。
1437
+ // 这样在营业日跨日边界(start_time)场景下也能正确判定是否需要重新拉取。
1438
+ const {
1439
+ startAt,
1440
+ endExclusiveAt
1441
+ } = this.getCurrentPullWindow();
471
1442
  return (parsed.isAfter(startAt) || parsed.isSame(startAt)) && parsed.isBefore(endExclusiveAt);
472
1443
  }
473
1444
  markOrderPulledAtNow() {
474
- this.setStorageItem(
475
- ORDER_LAST_FULL_FETCH_AT_STORAGE_KEY,
476
- (0, import_dayjs.default)().format("YYYY-MM-DD HH:mm:ss")
477
- );
1445
+ this.setStorageItem(ORDER_LAST_FULL_FETCH_AT_STORAGE_KEY, (0, _dayjs.default)().format('YYYY-MM-DD HH:mm:ss'));
478
1446
  }
479
1447
  getStorageItem(key) {
480
- var _a;
481
1448
  try {
482
- if ((_a = this.storage) == null ? void 0 : _a.getStorage)
483
- return this.storage.getStorage(key);
484
- if (typeof globalThis !== "undefined" && (globalThis == null ? void 0 : globalThis.localStorage)) {
1449
+ if (this.storage?.getStorage) return this.storage.getStorage(key);
1450
+ if (typeof globalThis !== 'undefined' && globalThis?.localStorage) {
485
1451
  return globalThis.localStorage.getItem(key);
486
1452
  }
487
1453
  } catch {
1454
+ // 忽略存储异常,避免影响主流程
488
1455
  }
489
1456
  return null;
490
1457
  }
491
1458
  setStorageItem(key, value) {
492
- var _a;
493
1459
  try {
494
- if ((_a = this.storage) == null ? void 0 : _a.setStorage) {
1460
+ if (this.storage?.setStorage) {
495
1461
  this.storage.setStorage(key, value);
496
1462
  return;
497
1463
  }
498
- if (typeof globalThis !== "undefined" && (globalThis == null ? void 0 : globalThis.localStorage)) {
1464
+ if (typeof globalThis !== 'undefined' && globalThis?.localStorage) {
499
1465
  globalThis.localStorage.setItem(key, value);
500
1466
  }
501
1467
  } catch {
1468
+ // 忽略存储异常,避免影响主流程
502
1469
  }
503
1470
  }
504
1471
  async loadOrdersFromSQLite() {
505
- if (!this.dbManager)
506
- return [];
1472
+ if (!this.dbManager) return [];
507
1473
  try {
508
1474
  const orders = await this.dbManager.getAll(INDEXDB_STORE_NAME);
509
- return orders || [];
1475
+ return (orders || []).map(order => this.normalizeOrderCollectionsForIngress(order, 'loadOrdersFromSQLite'));
510
1476
  } catch {
511
1477
  return [];
512
1478
  }
513
1479
  }
514
- async saveOrdersToSQLite(orderList) {
515
- if (!this.dbManager)
1480
+ isPendingSyncOrder(order) {
1481
+ return Number(order?.need_sync) === 1;
1482
+ }
1483
+ normalizeRemoteSyncedOrder(order) {
1484
+ const normalizedOrder = this.normalizeOrderCollectionsForIngress(order, 'normalizeRemoteSyncedOrder');
1485
+ if (this.isPendingSyncOrder(normalizedOrder)) return normalizedOrder;
1486
+ return {
1487
+ ...normalizedOrder,
1488
+ need_sync: 0
1489
+ };
1490
+ }
1491
+ mergeRemoteSnapshotWithPendingOrders(remoteOrders, existingOrders) {
1492
+ const merged = remoteOrders.map(order => this.normalizeRemoteSyncedOrder(order));
1493
+ for (const rawOrder of existingOrders) {
1494
+ const order = this.normalizeOrderCollectionsForIngress(rawOrder, 'mergeRemoteSnapshotWithPendingOrders.existingSQLite');
1495
+ if (!this.isPendingSyncOrder(order)) continue;
1496
+ const matchIndex = this.findOrderIndexByIdentity(merged, order);
1497
+ if (matchIndex >= 0) {
1498
+ merged[matchIndex] = this.mergeOrderRecords(order, merged[matchIndex]);
1499
+ continue;
1500
+ }
1501
+ if (!this.getOrderStorageKey(order)) continue;
1502
+ merged.push(order);
1503
+ }
1504
+ return this.compactOrderListByIdentity(merged, 'mergeRemoteSnapshotWithPendingOrders').list;
1505
+ }
1506
+
1507
+ /**
1508
+ * 串行执行订单 SQLite 写入任务,避免快照替换与增量 upsert 并发交错。
1509
+ *
1510
+ * @example
1511
+ * await this.runInOrderSQLiteSaveQueue(async () => {
1512
+ * await this.dbManager.bulkUpdate(INDEXDB_STORE_NAME, orders)
1513
+ * })
1514
+ */
1515
+ async runInOrderSQLiteSaveQueue(task) {
1516
+ const queuedTask = this.orderSQLiteSaveQueue.then(task, task);
1517
+ this.orderSQLiteSaveQueue = queuedTask.then(() => undefined, () => undefined);
1518
+ return queuedTask;
1519
+ }
1520
+
1521
+ /**
1522
+ * 批量增量 upsert 订单到 SQLite,不清空表;用于 pubsub / HTTP 增量合并。
1523
+ *
1524
+ * @example
1525
+ * await this.patchOrdersInSQLite(freshOrders)
1526
+ */
1527
+ async patchOrdersInSQLite(orders, source, mergeActions = {}) {
1528
+ if (!this.dbManager || orders.length === 0) {
516
1529
  return;
1530
+ }
1531
+ const ordersSnapshot = (0, _lodashEs.cloneDeep)(orders).map(order => this.normalizeOrderCollectionsForIngress(order, `${source}.sqliteIngress`)).filter(order => this.getOrderStorageKey(order));
1532
+ if (ordersSnapshot.length === 0) {
1533
+ return;
1534
+ }
1535
+ const {
1536
+ toWrite,
1537
+ skipped
1538
+ } = this.filterRedundantPatchOrders(source, ordersSnapshot, mergeActions);
1539
+ if (toWrite.length === 0) {
1540
+ return;
1541
+ }
1542
+ const compactedToWrite = this.compactOrderListByIdentity(toWrite, `${source}.sqlitePatch`).list;
1543
+ if (compactedToWrite.length === 0) {
1544
+ return;
1545
+ }
517
1546
  try {
518
- await this.dbManager.clear(INDEXDB_STORE_NAME);
519
- if (orderList.length === 0)
520
- return;
521
- await this.dbManager.bulkAdd(INDEXDB_STORE_NAME, orderList);
1547
+ await this.runInOrderSQLiteSaveQueue(async () => {
1548
+ let writeMethod = 'none';
1549
+ this.logInfo('patchOrdersInSQLite-开始', {
1550
+ source,
1551
+ count: compactedToWrite.length,
1552
+ dedupedCount: skipped.length
1553
+ });
1554
+ if (typeof this.dbManager.bulkUpdate === 'function') {
1555
+ writeMethod = 'bulkUpdate';
1556
+ await this.dbManager.bulkUpdate(INDEXDB_STORE_NAME, compactedToWrite);
1557
+ } else if (typeof this.dbManager.bulkAdd === 'function') {
1558
+ writeMethod = 'bulkAdd';
1559
+ await this.dbManager.bulkAdd(INDEXDB_STORE_NAME, compactedToWrite);
1560
+ } else if (typeof this.dbManager.update === 'function') {
1561
+ writeMethod = 'update';
1562
+ for (const order of compactedToWrite) {
1563
+ await this.dbManager.update(INDEXDB_STORE_NAME, order);
1564
+ }
1565
+ }
1566
+ this.recordRecentSqliteWrite(source, compactedToWrite);
1567
+ this.logInfo('patchOrdersInSQLite-完成', {
1568
+ source,
1569
+ count: compactedToWrite.length,
1570
+ writeMethod,
1571
+ dedupedCount: skipped.length
1572
+ });
1573
+ });
1574
+ } catch (error) {
1575
+ this.logError('增量保存订单到 SQLite 失败', {
1576
+ error: error instanceof Error ? error.message : String(error),
1577
+ orderCount: ordersSnapshot.length
1578
+ });
1579
+ }
1580
+ }
1581
+
1582
+ /**
1583
+ * 单条 upsert 订单到 SQLite;用于 overwriteExistingOrder 等单条覆盖场景。
1584
+ *
1585
+ * @example
1586
+ * await this.updateOrderInSQLite(freshOrder)
1587
+ */
1588
+ async updateOrderInSQLite(order, source) {
1589
+ if (!this.dbManager || !this.getOrderStorageKey(order)) {
1590
+ return;
1591
+ }
1592
+ const orderSnapshot = this.normalizeOrderCollectionsForIngress((0, _lodashEs.cloneDeep)(order), `${source}.sqliteIngress`);
1593
+ try {
1594
+ await this.runInOrderSQLiteSaveQueue(async () => {
1595
+ let writeMethod = 'none';
1596
+ this.logInfo('updateOrderInSQLite-开始', {
1597
+ source,
1598
+ orderId: orderSnapshot.order_id ?? null,
1599
+ storageKey: this.getOrderStorageKey(orderSnapshot)
1600
+ });
1601
+ if (typeof this.dbManager.update === 'function') {
1602
+ writeMethod = 'update';
1603
+ await this.dbManager.update(INDEXDB_STORE_NAME, orderSnapshot);
1604
+ } else if (typeof this.dbManager.bulkUpdate === 'function') {
1605
+ writeMethod = 'bulkUpdate';
1606
+ await this.dbManager.bulkUpdate(INDEXDB_STORE_NAME, [orderSnapshot]);
1607
+ } else if (typeof this.dbManager.bulkAdd === 'function') {
1608
+ writeMethod = 'bulkAdd';
1609
+ await this.dbManager.bulkAdd(INDEXDB_STORE_NAME, [orderSnapshot]);
1610
+ }
1611
+ this.recordRecentSqliteWrite(source, [orderSnapshot]);
1612
+ this.logInfo('updateOrderInSQLite-完成', {
1613
+ source,
1614
+ orderId: orderSnapshot.order_id ?? null,
1615
+ writeMethod
1616
+ });
1617
+ });
1618
+ } catch (error) {
1619
+ this.logError('单条保存订单到 SQLite 失败', {
1620
+ error: error instanceof Error ? error.message : String(error),
1621
+ orderId: orderSnapshot.order_id ?? null
1622
+ });
1623
+ }
1624
+ }
1625
+
1626
+ /**
1627
+ * 全量快照替换 orders 表(clear + bulkAdd),用于 SSE 全量拉取与营业日窗口裁剪。
1628
+ *
1629
+ * @example
1630
+ * await this.replaceOrdersSnapshotInSQLite(orderList)
1631
+ */
1632
+ async replaceOrdersSnapshotInSQLite(orderList, source) {
1633
+ if (!this.dbManager) {
1634
+ return this.compactOrderListByIdentity(orderList.map(order => this.normalizeRemoteSyncedOrder(order)), `${source}.snapshotNoDb`).list;
1635
+ }
1636
+ const remoteSnapshot = (0, _lodashEs.cloneDeep)(orderList).map(order => this.normalizeOrderCollectionsForIngress(order, `${source}.remoteIngress`));
1637
+ let mergedSnapshot = this.compactOrderListByIdentity(remoteSnapshot.map(order => this.normalizeRemoteSyncedOrder(order)), `${source}.remoteSnapshot`).list;
1638
+ try {
1639
+ await this.runInOrderSQLiteSaveQueue(async () => {
1640
+ const existingOrders = typeof this.dbManager.getAll === 'function' ? await this.dbManager.getAll(INDEXDB_STORE_NAME) : [];
1641
+ const orderListSnapshot = this.mergeRemoteSnapshotWithPendingOrders(remoteSnapshot, existingOrders || []);
1642
+ mergedSnapshot = this.compactOrderListByIdentity(orderListSnapshot, `${source}.sqliteSnapshot`).list;
1643
+ this.logInfo('replaceOrdersSnapshotInSQLite-开始', {
1644
+ source,
1645
+ count: mergedSnapshot.length,
1646
+ remoteCount: remoteSnapshot.length,
1647
+ preservedPendingCount: mergedSnapshot.length - remoteSnapshot.length
1648
+ });
1649
+ await this.dbManager.clear(INDEXDB_STORE_NAME);
1650
+ this.logInfo('replaceOrdersSnapshotInSQLite-clear完成', {
1651
+ count: mergedSnapshot.length
1652
+ });
1653
+ if (mergedSnapshot.length === 0) {
1654
+ return;
1655
+ }
1656
+ await this.dbManager.bulkAdd(INDEXDB_STORE_NAME, mergedSnapshot);
1657
+ this.recordRecentSqliteWrite(source, mergedSnapshot);
1658
+ this.logInfo('replaceOrdersSnapshotInSQLite-bulkAdd完成', {
1659
+ source,
1660
+ count: mergedSnapshot.length
1661
+ });
1662
+ });
522
1663
  } catch (error) {
523
- this.logError("保存订单到 SQLite 失败", {
1664
+ this.logError('全量保存订单到 SQLite 失败', {
524
1665
  error: error instanceof Error ? error.message : String(error),
525
- orderList: orderList.length
1666
+ orderList: remoteSnapshot.length
526
1667
  });
527
1668
  }
1669
+ return mergedSnapshot;
528
1670
  }
1671
+
1672
+ /**
1673
+ * 清空订单模块的内存数据和本地 SQLite 订单表。
1674
+ *
1675
+ * @example
1676
+ * await orderModule.clear()
1677
+ */
529
1678
  async clear() {
530
1679
  this.store.list = [];
531
- this.store.map = /* @__PURE__ */ new Map();
532
- this.resourceIdIndex.clear();
1680
+ this.store.map = new Map();
533
1681
  if (this.dbManager) {
534
- await this.dbManager.clear(INDEXDB_STORE_NAME);
1682
+ await this.runInOrderSQLiteSaveQueue(async () => {
1683
+ await this.dbManager.clear(INDEXDB_STORE_NAME);
1684
+ });
535
1685
  }
536
- this.setStorageItem(ORDER_LAST_FULL_FETCH_AT_STORAGE_KEY, "");
537
- this.core.effects.emit(import_types.OrderHooks.onOrdersChanged, this.store.list);
1686
+ this.setStorageItem(ORDER_LAST_FULL_FETCH_AT_STORAGE_KEY, '');
1687
+ this.emitOrdersChanged([], 'clear');
538
1688
  }
539
- };
540
- // Annotate the CommonJS export names for ESM import in node:
541
- 0 && (module.exports = {
542
- OrderModule
543
- });
1689
+ }
1690
+ exports.OrderModule = OrderModule;