@pisell/pisellos 2.2.291 → 2.2.293
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/core/index.d.ts +7 -0
- package/dist/core/index.js +108 -65
- package/dist/model/strategy/adapter/itemRule/adapter.d.ts +4 -1
- package/dist/model/strategy/adapter/itemRule/adapter.js +135 -35
- package/dist/model/strategy/adapter/itemRule/evaluator.d.ts +8 -1
- package/dist/model/strategy/adapter/itemRule/evaluator.js +25 -1
- package/dist/model/strategy/adapter/itemRule/type.d.ts +44 -0
- package/dist/model/strategy/adapter/itemRule/type.js +19 -1
- package/dist/model/strategy/adapter/promotion/index.js +0 -9
- package/dist/modules/BookingContext/index.d.ts +3 -0
- package/dist/modules/BookingContext/index.js +42 -13
- package/dist/modules/BookingContext/types.d.ts +4 -4
- package/dist/modules/BookingContext/types.js +3 -3
- package/dist/modules/OpenData/types.d.ts +1 -0
- package/dist/modules/Order/index.d.ts +18 -3
- package/dist/modules/Order/index.js +885 -485
- package/dist/modules/Order/salesNotes.d.ts +31 -0
- package/dist/modules/Order/salesNotes.js +492 -0
- package/dist/modules/Order/types.d.ts +93 -5
- package/dist/modules/Order/types.js +5 -0
- package/dist/modules/Order/utils.d.ts +12 -0
- package/dist/modules/Order/utils.js +96 -31
- package/dist/modules/Product/types.d.ts +2 -0
- package/dist/modules/ProductList/clientDataVariants.d.ts +50 -0
- package/dist/modules/ProductList/clientDataVariants.js +216 -0
- package/dist/modules/ProductList/index.d.ts +18 -3
- package/dist/modules/ProductList/index.js +220 -38
- package/dist/modules/ProductList/types.d.ts +10 -0
- package/dist/modules/Quotation/index.d.ts +22 -0
- package/dist/modules/Quotation/index.js +293 -28
- package/dist/modules/ResourcePlanner/index.d.ts +0 -22
- package/dist/modules/ResourcePlanner/index.js +1 -180
- package/dist/modules/ResourcePlanner/localClock.d.ts +33 -0
- package/dist/modules/ResourcePlanner/localClock.js +183 -0
- package/dist/modules/ResourcePlanner/planner.d.ts +5 -14
- package/dist/modules/ResourcePlanner/planner.js +2111 -958
- package/dist/modules/ResourcePlanner/types.d.ts +359 -187
- package/dist/modules/ResourcePlanner/types.js +33 -1
- package/dist/plugins/request.d.ts +1 -0
- package/dist/server/index.d.ts +17 -2
- package/dist/server/index.js +1134 -932
- package/dist/server/modules/floor-plan/index.d.ts +4 -0
- package/dist/server/modules/floor-plan/index.js +240 -98
- package/dist/server/modules/menu/index.js +48 -23
- package/dist/server/modules/order/index.d.ts +17 -7
- package/dist/server/modules/order/index.js +419 -207
- package/dist/server/modules/products/index.d.ts +10 -4
- package/dist/server/modules/products/index.js +177 -84
- package/dist/server/modules/resource/index.js +21 -13
- package/dist/server/utils/product.d.ts +1 -1
- package/dist/server/utils/product.js +186 -22
- package/dist/server/utils/small-ticket.d.ts +9 -1
- package/dist/server/utils/small-ticket.js +131 -84
- package/dist/solution/BaseSales/index.d.ts +26 -3
- package/dist/solution/BaseSales/index.js +1384 -905
- package/dist/solution/BaseSales/types.d.ts +2 -0
- package/dist/solution/BaseSales/utils/transformBaseProductToOrderProduct.d.ts +3 -0
- package/dist/solution/BaseSales/utils/transformBaseProductToOrderProduct.js +10 -3
- package/dist/solution/BaseSales/utils.js +6 -3
- package/dist/solution/BookingByStep/index.d.ts +4 -4
- package/dist/solution/BookingTicket/index.d.ts +1 -1
- package/dist/solution/BookingTicket/index.js +4 -2
- package/dist/solution/BookingTicket/utils/addProductDecision.d.ts +2 -1
- package/dist/solution/BookingTicket/utils/addProductDecision.js +6 -3
- package/dist/solution/BookingTicket/utils/orderCustomer.js +3 -2
- package/dist/solution/RegisterAndLogin/config.d.ts +9 -3
- package/dist/solution/RegisterAndLogin/config.js +95 -40
- package/dist/solution/RegisterAndLogin/index.js +4 -1
- package/dist/solution/ScanOrder/index.d.ts +1 -0
- package/dist/solution/ScanOrder/index.js +31 -27
- package/dist/solution/ScanOrder/types.d.ts +2 -0
- package/dist/solution/ScanOrder/utils.d.ts +1 -0
- package/dist/solution/ScanOrder/utils.js +8 -3
- package/dist/solution/UnifiedBookingSales/index.d.ts +128 -74
- package/dist/solution/UnifiedBookingSales/index.js +3877 -963
- package/dist/solution/UnifiedBookingSales/remoteOccupancyCache.d.ts +49 -0
- package/dist/solution/UnifiedBookingSales/remoteOccupancyCache.js +430 -0
- package/dist/solution/UnifiedBookingSales/types.d.ts +161 -77
- package/dist/solution/UnifiedBookingSales/types.js +4 -9
- package/dist/solution/VenueBooking/index.d.ts +3 -0
- package/dist/solution/VenueBooking/index.js +549 -497
- package/dist/solution/VenueBooking/utils/dateSummary.d.ts +1 -0
- package/dist/solution/VenueBooking/utils/dateSummary.js +3 -1
- package/dist/solution/VenueBooking/utils/slotMerge.d.ts +2 -0
- package/dist/solution/VenueBooking/utils/slotMerge.js +3 -1
- package/dist/utils/platform.d.ts +8 -0
- package/dist/utils/platform.js +15 -0
- package/lib/core/index.d.ts +7 -0
- package/lib/core/index.js +20 -2
- package/lib/model/strategy/adapter/itemRule/adapter.d.ts +4 -1
- package/lib/model/strategy/adapter/itemRule/adapter.js +73 -4
- package/lib/model/strategy/adapter/itemRule/evaluator.d.ts +8 -1
- package/lib/model/strategy/adapter/itemRule/evaluator.js +23 -1
- package/lib/model/strategy/adapter/itemRule/type.d.ts +44 -0
- package/lib/model/strategy/adapter/itemRule/type.js +19 -1
- package/lib/modules/BookingContext/index.d.ts +3 -0
- package/lib/modules/BookingContext/index.js +35 -8
- package/lib/modules/BookingContext/types.d.ts +4 -4
- package/lib/modules/BookingContext/types.js +3 -3
- package/lib/modules/OpenData/types.d.ts +1 -0
- package/lib/modules/Order/index.d.ts +18 -3
- package/lib/modules/Order/index.js +325 -28
- package/lib/modules/Order/salesNotes.d.ts +31 -0
- package/lib/modules/Order/salesNotes.js +382 -0
- package/lib/modules/Order/types.d.ts +93 -5
- package/lib/modules/Order/types.js +4 -0
- package/lib/modules/Order/utils.d.ts +12 -0
- package/lib/modules/Order/utils.js +85 -16
- package/lib/modules/Product/types.d.ts +2 -0
- package/lib/modules/ProductList/clientDataVariants.d.ts +50 -0
- package/lib/modules/ProductList/clientDataVariants.js +204 -0
- package/lib/modules/ProductList/index.d.ts +18 -3
- package/lib/modules/ProductList/index.js +137 -8
- package/lib/modules/ProductList/types.d.ts +10 -0
- package/lib/modules/Quotation/index.d.ts +22 -0
- package/lib/modules/Quotation/index.js +158 -11
- package/lib/modules/ResourcePlanner/index.d.ts +0 -22
- package/lib/modules/ResourcePlanner/index.js +9 -129
- package/lib/modules/ResourcePlanner/localClock.d.ts +33 -0
- package/lib/modules/ResourcePlanner/localClock.js +205 -0
- package/lib/modules/ResourcePlanner/planner.d.ts +5 -14
- package/lib/modules/ResourcePlanner/planner.js +1807 -783
- package/lib/modules/ResourcePlanner/types.d.ts +359 -187
- package/lib/modules/ResourcePlanner/types.js +13 -1
- package/lib/plugins/request.d.ts +1 -0
- package/lib/server/index.d.ts +17 -2
- package/lib/server/index.js +149 -21
- package/lib/server/modules/floor-plan/index.d.ts +4 -0
- package/lib/server/modules/floor-plan/index.js +60 -7
- package/lib/server/modules/menu/index.js +26 -4
- package/lib/server/modules/order/index.d.ts +17 -7
- package/lib/server/modules/order/index.js +194 -57
- package/lib/server/modules/products/index.d.ts +10 -4
- package/lib/server/modules/products/index.js +93 -23
- package/lib/server/modules/resource/index.js +12 -2
- package/lib/server/utils/product.d.ts +1 -1
- package/lib/server/utils/product.js +163 -3
- package/lib/server/utils/small-ticket.d.ts +9 -1
- package/lib/server/utils/small-ticket.js +123 -79
- package/lib/solution/BaseSales/index.d.ts +26 -3
- package/lib/solution/BaseSales/index.js +396 -70
- package/lib/solution/BaseSales/types.d.ts +2 -0
- package/lib/solution/BaseSales/utils/transformBaseProductToOrderProduct.d.ts +3 -0
- package/lib/solution/BaseSales/utils/transformBaseProductToOrderProduct.js +9 -2
- package/lib/solution/BaseSales/utils.js +3 -0
- package/lib/solution/BookingByStep/index.d.ts +4 -4
- package/lib/solution/BookingTicket/index.d.ts +1 -1
- package/lib/solution/BookingTicket/index.js +2 -1
- package/lib/solution/BookingTicket/utils/addProductDecision.d.ts +2 -1
- package/lib/solution/BookingTicket/utils/addProductDecision.js +5 -2
- package/lib/solution/BookingTicket/utils/orderCustomer.js +2 -1
- package/lib/solution/RegisterAndLogin/config.d.ts +9 -3
- package/lib/solution/RegisterAndLogin/config.js +49 -8
- package/lib/solution/RegisterAndLogin/index.js +4 -1
- package/lib/solution/ScanOrder/index.d.ts +1 -0
- package/lib/solution/ScanOrder/index.js +5 -1
- package/lib/solution/ScanOrder/types.d.ts +2 -0
- package/lib/solution/ScanOrder/utils.d.ts +1 -0
- package/lib/solution/ScanOrder/utils.js +4 -0
- package/lib/solution/UnifiedBookingSales/index.d.ts +128 -74
- package/lib/solution/UnifiedBookingSales/index.js +2592 -424
- package/lib/solution/UnifiedBookingSales/remoteOccupancyCache.d.ts +49 -0
- package/lib/solution/UnifiedBookingSales/remoteOccupancyCache.js +316 -0
- package/lib/solution/UnifiedBookingSales/types.d.ts +161 -77
- package/lib/solution/UnifiedBookingSales/types.js +4 -6
- package/lib/solution/VenueBooking/index.d.ts +3 -0
- package/lib/solution/VenueBooking/index.js +33 -9
- package/lib/solution/VenueBooking/utils/dateSummary.d.ts +1 -0
- package/lib/solution/VenueBooking/utils/dateSummary.js +3 -1
- package/lib/solution/VenueBooking/utils/slotMerge.d.ts +2 -0
- package/lib/solution/VenueBooking/utils/slotMerge.js +3 -1
- package/lib/utils/platform.d.ts +8 -0
- package/lib/utils/platform.js +22 -0
- package/package.json +1 -1
package/lib/server/index.js
CHANGED
|
@@ -27,6 +27,7 @@ var _filterBookings = require("./modules/order/utils/filterBookings");
|
|
|
27
27
|
var _smallTicket = require("./utils/small-ticket");
|
|
28
28
|
var _BaseSales = require("../solution/BaseSales");
|
|
29
29
|
var _paymentUtils = require("../modules/Order/payment-utils");
|
|
30
|
+
var _salesNotes = require("../modules/Order/salesNotes");
|
|
30
31
|
var _modules = require("./modules");
|
|
31
32
|
Object.keys(_modules).forEach(function (key) {
|
|
32
33
|
if (key === "default" || key === "__esModule") return;
|
|
@@ -99,6 +100,8 @@ class Server {
|
|
|
99
100
|
productQueryScheduleTimers = new Map();
|
|
100
101
|
/** 餐牌筛选为空时才触发的菜单/日程缓存自愈,避免每次商品查询都请求远端。 */
|
|
101
102
|
menuScheduleRefreshInFlight = null;
|
|
103
|
+
/** 启动阶段模块预加载任务;预渲染商品查询复用该任务,避免缓存尚未 ready 时误触发远端自愈。 */
|
|
104
|
+
startupModulePreloadInFlight = null;
|
|
102
105
|
lastMenuScheduleRefreshAt = 0;
|
|
103
106
|
MENU_SCHEDULE_REFRESH_COOLDOWN_MS = 30 * 1000;
|
|
104
107
|
|
|
@@ -501,7 +504,15 @@ class Server {
|
|
|
501
504
|
|
|
502
505
|
// 自动预加载数据
|
|
503
506
|
if (autoPreload) {
|
|
504
|
-
|
|
507
|
+
const preloadTask = this.preloadModulesData(registeredModules, options);
|
|
508
|
+
this.startupModulePreloadInFlight = preloadTask;
|
|
509
|
+
try {
|
|
510
|
+
await preloadTask;
|
|
511
|
+
} finally {
|
|
512
|
+
if (this.startupModulePreloadInFlight === preloadTask) {
|
|
513
|
+
this.startupModulePreloadInFlight = null;
|
|
514
|
+
}
|
|
515
|
+
}
|
|
505
516
|
} else {
|
|
506
517
|
this.logInfo('跳过自动预加载', {
|
|
507
518
|
autoPreload
|
|
@@ -619,12 +630,16 @@ class Server {
|
|
|
619
630
|
const numberedData = await this.ensureCheckoutOrderNumbers(normalizedData, title);
|
|
620
631
|
const checkoutData = this.applyCheckoutFulfillmentBuzzer(numberedData, title);
|
|
621
632
|
const remoteCheckoutData = this.buildRemoteCheckoutData(checkoutData, title);
|
|
633
|
+
const deviceHeaders = await this.buildCheckoutDeviceHeaders(title);
|
|
622
634
|
if (!this.app?.request?.post) {
|
|
623
635
|
throw new Error('app.request 不可用');
|
|
624
636
|
}
|
|
625
637
|
const response = await this.app.request.post(backendPath, remoteCheckoutData, {
|
|
626
638
|
isShopApi: true,
|
|
627
|
-
customToast: () => {}
|
|
639
|
+
customToast: () => {},
|
|
640
|
+
...(Object.keys(deviceHeaders).length > 0 ? {
|
|
641
|
+
headers: deviceHeaders
|
|
642
|
+
} : {})
|
|
628
643
|
});
|
|
629
644
|
if (this.isCheckoutResponseRejected(response)) {
|
|
630
645
|
return {
|
|
@@ -687,6 +702,7 @@ class Server {
|
|
|
687
702
|
if (!next || typeof next !== 'object') return next;
|
|
688
703
|
const record = next;
|
|
689
704
|
delete record[OS_FULFILLMENT_REF_MODE_FIELD];
|
|
705
|
+
this.applyKioskCheckoutOperator(record);
|
|
690
706
|
if (!Array.isArray(record.payments)) return next;
|
|
691
707
|
const originalPayments = record.payments;
|
|
692
708
|
const filteredPayments = originalPayments.filter(payment => !(0, _paymentUtils.isPendingOrderPayment)(payment));
|
|
@@ -701,6 +717,44 @@ class Server {
|
|
|
701
717
|
});
|
|
702
718
|
return next;
|
|
703
719
|
}
|
|
720
|
+
async buildCheckoutDeviceHeaders(title) {
|
|
721
|
+
const getAsyncIotDeviceInfo = this.getAppData('async_iot_device_info');
|
|
722
|
+
if (!getAsyncIotDeviceInfo) return {};
|
|
723
|
+
let deviceInfo;
|
|
724
|
+
try {
|
|
725
|
+
deviceInfo = typeof getAsyncIotDeviceInfo === 'function' ? await getAsyncIotDeviceInfo() : getAsyncIotDeviceInfo;
|
|
726
|
+
} catch (error) {
|
|
727
|
+
this.logWarning(`${title}: 读取 async_iot_device_info 失败,跳过 checkout 设备请求头`, {
|
|
728
|
+
error: this.getUnknownErrorMessage(error)
|
|
729
|
+
});
|
|
730
|
+
return {};
|
|
731
|
+
}
|
|
732
|
+
if (!deviceInfo || typeof deviceInfo !== 'object') return {};
|
|
733
|
+
const headers = {};
|
|
734
|
+
const assignHeader = (key, value) => {
|
|
735
|
+
if (this.isBlankCheckoutValue(value)) return;
|
|
736
|
+
headers[key] = String(value);
|
|
737
|
+
};
|
|
738
|
+
assignHeader('Device-ID', deviceInfo.id);
|
|
739
|
+
assignHeader('Device-Type-Code', deviceInfo.type_code);
|
|
740
|
+
assignHeader('Device-Model-Code', deviceInfo.model_code);
|
|
741
|
+
return headers;
|
|
742
|
+
}
|
|
743
|
+
applyKioskCheckoutOperator(record) {
|
|
744
|
+
if (record.platform !== 'kiosk') return;
|
|
745
|
+
record.operator_type = 'customer';
|
|
746
|
+
record.operator_id = this.resolveKioskCheckoutOperatorId(record.customer_id);
|
|
747
|
+
}
|
|
748
|
+
resolveKioskCheckoutOperatorId(customerId) {
|
|
749
|
+
if (customerId === undefined || customerId === null) return 1;
|
|
750
|
+
if (typeof customerId === 'string') {
|
|
751
|
+
const trimmed = customerId.trim();
|
|
752
|
+
if (trimmed === '' || trimmed === '0' || trimmed === '1') return 1;
|
|
753
|
+
return customerId;
|
|
754
|
+
}
|
|
755
|
+
if (customerId === 0 || customerId === 1) return 1;
|
|
756
|
+
return customerId;
|
|
757
|
+
}
|
|
704
758
|
mergeCheckoutSyncPayments(checkoutData, fresh, title) {
|
|
705
759
|
const localPayments = Array.isArray(checkoutData?.payments) ? checkoutData.payments : [];
|
|
706
760
|
const freshRecord = fresh;
|
|
@@ -802,11 +856,24 @@ class Server {
|
|
|
802
856
|
return;
|
|
803
857
|
}
|
|
804
858
|
try {
|
|
805
|
-
await this.schedule.loadAllSchedule();
|
|
806
859
|
this.products.clearPriceCache();
|
|
807
860
|
await this.products.setupQuotationPriceBridge({
|
|
808
861
|
scheduleModule: this.schedule,
|
|
809
|
-
channel: this.core.context?.channel
|
|
862
|
+
channel: this.core.context?.channel,
|
|
863
|
+
onQuotationUpdated: () => {
|
|
864
|
+
void this.recomputeAndNotifyProductQuery();
|
|
865
|
+
}
|
|
866
|
+
});
|
|
867
|
+
|
|
868
|
+
// Schedule 已在模块 preload 阶段加载完成。价格桥接先使用本地数据,
|
|
869
|
+
// 远端刷新转为后台校准,避免二次启动重复阻塞请求。
|
|
870
|
+
void this.schedule.loadAllSchedule().then(() => {
|
|
871
|
+
this.products?.clearPriceCache();
|
|
872
|
+
void this.recomputeAndNotifyProductQuery();
|
|
873
|
+
}).catch(error => {
|
|
874
|
+
this.logWarning('Products 报价单价格桥接后台刷新 Schedule 失败,继续使用本地数据', {
|
|
875
|
+
error: error instanceof Error ? error.message : String(error)
|
|
876
|
+
});
|
|
810
877
|
});
|
|
811
878
|
this.logInfo('Products 报价单价格桥接初始化完成');
|
|
812
879
|
} catch (error) {
|
|
@@ -865,17 +932,23 @@ class Server {
|
|
|
865
932
|
* 重新拉取全量 SSE 接口,更新本地数据后触发 onProductsSyncCompleted
|
|
866
933
|
* 不影响当前界面展示,适用于切回前台、定时刷新等场景
|
|
867
934
|
*/
|
|
868
|
-
async refreshProductsInBackground() {
|
|
935
|
+
async refreshProductsInBackground(options = {}) {
|
|
869
936
|
if (!this.products) {
|
|
870
937
|
this.logWarning('refreshProductsInBackground: Products 模块未注册');
|
|
871
938
|
return;
|
|
872
939
|
}
|
|
873
|
-
|
|
940
|
+
const trigger = options.trigger || 'background';
|
|
941
|
+
this.logInfo('refreshProductsInBackground 开始', {
|
|
942
|
+
trigger
|
|
943
|
+
});
|
|
874
944
|
const startTime = Date.now();
|
|
875
945
|
try {
|
|
876
|
-
await this.products.silentRefresh(
|
|
946
|
+
await this.products.silentRefresh({
|
|
947
|
+
trigger
|
|
948
|
+
});
|
|
877
949
|
const duration = Date.now() - startTime;
|
|
878
950
|
this.logInfo('refreshProductsInBackground 完成', {
|
|
951
|
+
trigger,
|
|
879
952
|
duration: `${duration}ms`
|
|
880
953
|
});
|
|
881
954
|
} catch (error) {
|
|
@@ -883,6 +956,7 @@ class Server {
|
|
|
883
956
|
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
884
957
|
console.error('[Server] refreshProductsInBackground 失败:', error);
|
|
885
958
|
this.logError('refreshProductsInBackground 失败', {
|
|
959
|
+
trigger,
|
|
886
960
|
duration: `${duration}ms`,
|
|
887
961
|
error: errorMessage
|
|
888
962
|
});
|
|
@@ -894,17 +968,23 @@ class Server {
|
|
|
894
968
|
* 重新拉取全量 SSE 接口,更新本地数据后触发 onOrdersSyncCompleted
|
|
895
969
|
* 不影响当前界面展示,适用于切回前台、定时刷新等场景
|
|
896
970
|
*/
|
|
897
|
-
async refreshOrdersInBackground() {
|
|
971
|
+
async refreshOrdersInBackground(options = {}) {
|
|
898
972
|
if (!this.order) {
|
|
899
973
|
this.logWarning('refreshOrdersInBackground: Order 模块未注册');
|
|
900
974
|
return;
|
|
901
975
|
}
|
|
902
|
-
|
|
976
|
+
const trigger = options.trigger || 'background';
|
|
977
|
+
this.logInfo('refreshOrdersInBackground 开始', {
|
|
978
|
+
trigger
|
|
979
|
+
});
|
|
903
980
|
const startTime = Date.now();
|
|
904
981
|
try {
|
|
905
|
-
await this.order.silentRefresh(
|
|
982
|
+
await this.order.silentRefresh({
|
|
983
|
+
trigger
|
|
984
|
+
});
|
|
906
985
|
const duration = Date.now() - startTime;
|
|
907
986
|
this.logInfo('refreshOrdersInBackground 完成', {
|
|
987
|
+
trigger,
|
|
908
988
|
duration: `${duration}ms`
|
|
909
989
|
});
|
|
910
990
|
} catch (error) {
|
|
@@ -912,6 +992,7 @@ class Server {
|
|
|
912
992
|
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
913
993
|
console.error('[Server] refreshOrdersInBackground 失败:', error);
|
|
914
994
|
this.logError('refreshOrdersInBackground 失败', {
|
|
995
|
+
trigger,
|
|
915
996
|
duration: `${duration}ms`,
|
|
916
997
|
error: errorMessage
|
|
917
998
|
});
|
|
@@ -1448,7 +1529,7 @@ class Server {
|
|
|
1448
1529
|
// timerIds.push(timerId);
|
|
1449
1530
|
// }
|
|
1450
1531
|
setInterval(() => {
|
|
1451
|
-
const currentTime = (0, _dayjs.default)().format('
|
|
1532
|
+
const currentTime = (0, _dayjs.default)().format('HH:mm');
|
|
1452
1533
|
if (timePoints.includes(currentTime)) {
|
|
1453
1534
|
this.onScheduleTimePointTimerFire(subscriberId);
|
|
1454
1535
|
}
|
|
@@ -3507,14 +3588,16 @@ class Server {
|
|
|
3507
3588
|
};
|
|
3508
3589
|
const needsShopOrderNumber = next.shop_order_number === undefined || next.shop_order_number === null || next.shop_order_number === '';
|
|
3509
3590
|
const needsShopFullOrderNumber = next.shop_full_order_number === undefined || next.shop_full_order_number === null || next.shop_full_order_number === '';
|
|
3510
|
-
if (!needsShopOrderNumber && !needsShopFullOrderNumber)
|
|
3591
|
+
if (!needsShopOrderNumber && !needsShopFullOrderNumber) {
|
|
3592
|
+
return this.finalizeCheckoutSalesNotes(next, title);
|
|
3593
|
+
}
|
|
3511
3594
|
const idGenerator = this.getIdGeneratorPlugin();
|
|
3512
3595
|
if (!idGenerator) {
|
|
3513
3596
|
this.logWarning(`${title}: idGenerator 插件不可用,无法生成订单号`, {
|
|
3514
3597
|
needsShopOrderNumber,
|
|
3515
3598
|
needsShopFullOrderNumber
|
|
3516
3599
|
});
|
|
3517
|
-
return next;
|
|
3600
|
+
return this.finalizeCheckoutSalesNotes(next, title);
|
|
3518
3601
|
}
|
|
3519
3602
|
try {
|
|
3520
3603
|
if (needsShopOrderNumber && idGenerator.generateShopOrderNumber) {
|
|
@@ -3536,7 +3619,38 @@ class Server {
|
|
|
3536
3619
|
error: error instanceof Error ? error.message : String(error)
|
|
3537
3620
|
});
|
|
3538
3621
|
}
|
|
3539
|
-
return next;
|
|
3622
|
+
return this.finalizeCheckoutSalesNotes(next, title);
|
|
3623
|
+
}
|
|
3624
|
+
|
|
3625
|
+
/**
|
|
3626
|
+
* Notes 允许在本地草稿阶段暂存空订单目标;checkout 的统一出口负责用最终
|
|
3627
|
+
* shop_order_number 完成绑定并校验,保证后端请求、pending、设备任务和打印
|
|
3628
|
+
* 都使用同一份完整快照。
|
|
3629
|
+
*/
|
|
3630
|
+
finalizeCheckoutSalesNotes(data, title) {
|
|
3631
|
+
if (!data || typeof data !== 'object') return data;
|
|
3632
|
+
if (!Object.prototype.hasOwnProperty.call(data, 'notes')) return data;
|
|
3633
|
+
if (data.notes === undefined || data.notes === null) return data;
|
|
3634
|
+
if (!Array.isArray(data.notes)) {
|
|
3635
|
+
throw new Error('[Order Notes] checkout notes 必须为数组');
|
|
3636
|
+
}
|
|
3637
|
+
if (data.notes.length === 0) return data;
|
|
3638
|
+
const productTargetUuids = (Array.isArray(data.products) ? data.products : []).map(product => product?.metadata?.unique_identification_number ?? product?.unique_identification_number).filter(targetUuid => targetUuid !== undefined && targetUuid !== null && String(targetUuid).trim().length > 0).map(targetUuid => String(targetUuid));
|
|
3639
|
+
const shopOrderNumber = String(data.shop_order_number ?? '').trim();
|
|
3640
|
+
const notes = (0, _salesNotes.finalizeSalesNotesForCheckout)({
|
|
3641
|
+
notes: data.notes,
|
|
3642
|
+
shopOrderNumber,
|
|
3643
|
+
productTargetUuids
|
|
3644
|
+
});
|
|
3645
|
+
this.logInfo(`${title}: checkout Notes 已绑定订单号`, {
|
|
3646
|
+
shop_order_number: shopOrderNumber,
|
|
3647
|
+
note_count: notes.length
|
|
3648
|
+
});
|
|
3649
|
+
return {
|
|
3650
|
+
...data,
|
|
3651
|
+
shop_order_number: shopOrderNumber,
|
|
3652
|
+
notes
|
|
3653
|
+
};
|
|
3540
3654
|
}
|
|
3541
3655
|
async dispatchCheckoutSyncTask(params) {
|
|
3542
3656
|
this.registerDeviceTaskActions();
|
|
@@ -3593,7 +3707,8 @@ class Server {
|
|
|
3593
3707
|
order_id: printIdentity,
|
|
3594
3708
|
small_ticket_data: params.syncedOrder?.payment_info?.small_ticket_data,
|
|
3595
3709
|
skip_wristband: true,
|
|
3596
|
-
skip_voucher: true
|
|
3710
|
+
skip_voucher: true,
|
|
3711
|
+
skip_discount: true
|
|
3597
3712
|
} : {
|
|
3598
3713
|
order_id: printIdentity,
|
|
3599
3714
|
machine_code_print_info: params.syncedOrder?.payment_info?.machine_code_print_info,
|
|
@@ -5387,12 +5502,6 @@ class Server {
|
|
|
5387
5502
|
strategy_context
|
|
5388
5503
|
} = context;
|
|
5389
5504
|
const queryIds = Array.isArray(ids) ? ids.map(id => Number(id)).filter(id => Number.isFinite(id)) : [];
|
|
5390
|
-
const formatterContext = this.buildProductFormatterContext({
|
|
5391
|
-
schedule_date,
|
|
5392
|
-
schedule_datetime,
|
|
5393
|
-
customer_id,
|
|
5394
|
-
strategy_context
|
|
5395
|
-
});
|
|
5396
5505
|
this.logInfo('computeProductQueryResult 开始', {
|
|
5397
5506
|
menuListIdsCount: menu_list_ids?.length ?? 0,
|
|
5398
5507
|
ids: queryIds,
|
|
@@ -5404,6 +5513,23 @@ class Server {
|
|
|
5404
5513
|
strategyContextKeys: strategy_context ? Object.keys(strategy_context) : [],
|
|
5405
5514
|
changedIds: options?.changedIds
|
|
5406
5515
|
});
|
|
5516
|
+
|
|
5517
|
+
// 预渲染可能在 PisellOS 顺序 preload 尚未完成时发起商品查询。此时 Menu/Schedule
|
|
5518
|
+
// 暂时为空不代表缓存失效;等待当前 preload 后再按原逻辑查询,避免并发远端刷新先 clear
|
|
5519
|
+
// SQLite/IndexDB,造成 preload 读到空数据并重复请求。正常运行期该字段为 null,不增加等待。
|
|
5520
|
+
const startupPreloadTask = this.startupModulePreloadInFlight;
|
|
5521
|
+
if (startupPreloadTask) {
|
|
5522
|
+
await startupPreloadTask;
|
|
5523
|
+
}
|
|
5524
|
+
|
|
5525
|
+
// Menu/Schedule preload 会替换 store 中的数组;必须在等待完成后再读取,
|
|
5526
|
+
// 避免 formatterContext 持有预加载前的空数组快照。
|
|
5527
|
+
const formatterContext = this.buildProductFormatterContext({
|
|
5528
|
+
schedule_date,
|
|
5529
|
+
schedule_datetime,
|
|
5530
|
+
customer_id,
|
|
5531
|
+
strategy_context
|
|
5532
|
+
});
|
|
5407
5533
|
if (!this.products) {
|
|
5408
5534
|
this.logError('computeProductQueryResult: Products 模块未注册');
|
|
5409
5535
|
return {
|
|
@@ -5616,6 +5742,8 @@ class Server {
|
|
|
5616
5742
|
schedule_datetime: context.schedule_datetime,
|
|
5617
5743
|
customer_id: context.customer_id,
|
|
5618
5744
|
strategy_context: context.strategy_context,
|
|
5745
|
+
// 固定本次商品查询使用的语言,确保格式化与缓存使用同一份 locale 快照。
|
|
5746
|
+
locale: this.core.context?.locale,
|
|
5619
5747
|
menuList: this.menu?.getMenuList?.() || [],
|
|
5620
5748
|
scheduleList: scheduleModule?.getScheduleList?.() || [],
|
|
5621
5749
|
dataVariantEvaluator: this.core.context?.dataVariantEvaluator
|
|
@@ -10,6 +10,7 @@ export declare class FloorPlanModule extends BaseModule implements Module {
|
|
|
10
10
|
private store;
|
|
11
11
|
private request;
|
|
12
12
|
private logger;
|
|
13
|
+
private dbManager;
|
|
13
14
|
private floorPlanDataSource;
|
|
14
15
|
private pendingSyncMessages;
|
|
15
16
|
private syncTimer?;
|
|
@@ -28,6 +29,9 @@ export declare class FloorPlanModule extends BaseModule implements Module {
|
|
|
28
29
|
private mergeItems;
|
|
29
30
|
private removeByIds;
|
|
30
31
|
private loadListFromServer;
|
|
32
|
+
private loadListFromSQLite;
|
|
33
|
+
private saveListToSQLite;
|
|
34
|
+
private refreshListFromServer;
|
|
31
35
|
fetchByIdFromServer(id: number | string): Promise<FloorPlanItem | null>;
|
|
32
36
|
fetchByCodeFromServer(code: string): Promise<FloorPlanItem | null>;
|
|
33
37
|
preload(): Promise<void>;
|
|
@@ -7,6 +7,7 @@ exports.FloorPlanModule = void 0;
|
|
|
7
7
|
var _BaseModule = require("../../../modules/BaseModule");
|
|
8
8
|
var _types = require("./types");
|
|
9
9
|
const FLOOR_PLAN_SYNC_DEBOUNCE_MS = 3000;
|
|
10
|
+
const FLOOR_PLAN_STORE_NAME = 'floor_plans';
|
|
10
11
|
|
|
11
12
|
/**
|
|
12
13
|
* 平面图模块:预加载列表、pubsub 同步、供 OS Server 路由读本地 store
|
|
@@ -17,6 +18,7 @@ class FloorPlanModule extends _BaseModule.BaseModule {
|
|
|
17
18
|
store;
|
|
18
19
|
request;
|
|
19
20
|
logger;
|
|
21
|
+
dbManager;
|
|
20
22
|
floorPlanDataSource;
|
|
21
23
|
pendingSyncMessages = [];
|
|
22
24
|
syncTimer;
|
|
@@ -40,6 +42,7 @@ class FloorPlanModule extends _BaseModule.BaseModule {
|
|
|
40
42
|
if (appPlugin) {
|
|
41
43
|
const app = appPlugin.getApp();
|
|
42
44
|
this.logger = app.logger;
|
|
45
|
+
this.dbManager = app.sqlite || app.dbManager;
|
|
43
46
|
}
|
|
44
47
|
this.initFloorPlanDataSource();
|
|
45
48
|
this.setupFloorPlanSync();
|
|
@@ -147,6 +150,45 @@ class FloorPlanModule extends _BaseModule.BaseModule {
|
|
|
147
150
|
return [];
|
|
148
151
|
}
|
|
149
152
|
}
|
|
153
|
+
async loadListFromSQLite() {
|
|
154
|
+
if (!this.dbManager) return [];
|
|
155
|
+
try {
|
|
156
|
+
const list = await this.dbManager.getAll(FLOOR_PLAN_STORE_NAME);
|
|
157
|
+
return Array.isArray(list) ? list : [];
|
|
158
|
+
} catch (error) {
|
|
159
|
+
this.logWarning('从 SQLite 读取平面图失败', {
|
|
160
|
+
error: error instanceof Error ? error.message : String(error)
|
|
161
|
+
});
|
|
162
|
+
return [];
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
async saveListToSQLite(list) {
|
|
166
|
+
if (!this.dbManager) return;
|
|
167
|
+
try {
|
|
168
|
+
await this.dbManager.clear(FLOOR_PLAN_STORE_NAME);
|
|
169
|
+
if (list.length > 0) {
|
|
170
|
+
await this.dbManager.bulkAdd(FLOOR_PLAN_STORE_NAME, list);
|
|
171
|
+
}
|
|
172
|
+
} catch (error) {
|
|
173
|
+
this.logWarning('保存平面图到 SQLite 失败', {
|
|
174
|
+
count: list.length,
|
|
175
|
+
error: error instanceof Error ? error.message : String(error)
|
|
176
|
+
});
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
async refreshListFromServer() {
|
|
180
|
+
const list = await this.loadListFromServer();
|
|
181
|
+
if (list.length === 0) {
|
|
182
|
+
this.logWarning('远端平面图刷新完成但列表为空');
|
|
183
|
+
return;
|
|
184
|
+
}
|
|
185
|
+
this.replaceStore(list);
|
|
186
|
+
await this.saveListToSQLite(list);
|
|
187
|
+
this.core.effects.emit(_types.FloorPlanHooks.onFloorPlansChanged, this.getSortedList());
|
|
188
|
+
this.logInfo('远端平面图刷新完成', {
|
|
189
|
+
count: list.length
|
|
190
|
+
});
|
|
191
|
+
}
|
|
150
192
|
async fetchByIdFromServer(id) {
|
|
151
193
|
try {
|
|
152
194
|
const res = await this.request.get(`/schedule/floor-plan/${id}`, {}, {
|
|
@@ -174,16 +216,25 @@ class FloorPlanModule extends _BaseModule.BaseModule {
|
|
|
174
216
|
}
|
|
175
217
|
async preload() {
|
|
176
218
|
this.logInfo('开始预加载平面图列表');
|
|
177
|
-
const
|
|
178
|
-
if (
|
|
179
|
-
this.replaceStore(
|
|
219
|
+
const cachedList = await this.loadListFromSQLite();
|
|
220
|
+
if (cachedList.length > 0) {
|
|
221
|
+
this.replaceStore(cachedList);
|
|
180
222
|
this.core.effects.emit(_types.FloorPlanHooks.onFloorPlansChanged, this.getSortedList());
|
|
181
|
-
this.logInfo('
|
|
182
|
-
count:
|
|
223
|
+
this.logInfo('预加载完成(SQLite)', {
|
|
224
|
+
count: cachedList.length
|
|
183
225
|
});
|
|
184
|
-
|
|
185
|
-
|
|
226
|
+
|
|
227
|
+
// 二次启动立即使用本地平面图,远端刷新完成后自动更新 store 与订阅者。
|
|
228
|
+
void this.refreshListFromServer().catch(error => {
|
|
229
|
+
this.logWarning('后台刷新平面图失败,继续使用本地数据', {
|
|
230
|
+
error: error instanceof Error ? error.message : String(error)
|
|
231
|
+
});
|
|
232
|
+
});
|
|
233
|
+
return;
|
|
186
234
|
}
|
|
235
|
+
|
|
236
|
+
// 首次启动没有缓存时仍等待一次远端请求,并为后续启动建立缓存。
|
|
237
|
+
await this.refreshListFromServer();
|
|
187
238
|
}
|
|
188
239
|
initFloorPlanDataSource() {
|
|
189
240
|
const Ctor = this.core.serverOptions?.All_DATA_SOURCES?.FloorPlanDataSource;
|
|
@@ -282,6 +333,7 @@ class FloorPlanModule extends _BaseModule.BaseModule {
|
|
|
282
333
|
}
|
|
283
334
|
}
|
|
284
335
|
if (changed) {
|
|
336
|
+
await this.saveListToSQLite(this.store.list);
|
|
285
337
|
this.core.effects.emit(_types.FloorPlanHooks.onFloorPlansChanged, this.getSortedList());
|
|
286
338
|
this.core.effects.emit(_types.FloorPlanHooks.onFloorPlanSyncCompleted, null);
|
|
287
339
|
}
|
|
@@ -290,6 +342,7 @@ class FloorPlanModule extends _BaseModule.BaseModule {
|
|
|
290
342
|
this.store.list = [];
|
|
291
343
|
this.store.map.clear();
|
|
292
344
|
this.store.codeMap.clear();
|
|
345
|
+
await this.saveListToSQLite([]);
|
|
293
346
|
}
|
|
294
347
|
destroy() {
|
|
295
348
|
if (this.syncTimer) {
|
|
@@ -190,17 +190,20 @@ class MenuModule extends _BaseModule.BaseModule {
|
|
|
190
190
|
this.logWarning('loadMenuFromIndexDB: dbManager 不可用');
|
|
191
191
|
return [];
|
|
192
192
|
}
|
|
193
|
+
const startedAt = performance.now();
|
|
193
194
|
try {
|
|
194
195
|
// 直接获取所有餐牌记录
|
|
195
196
|
const menuList = await this.dbManager.getAll(INDEXDB_STORE_NAME);
|
|
196
197
|
this.logInfo('从 IndexDB 加载餐牌数据', {
|
|
197
|
-
menuCount: menuList?.length ?? 0
|
|
198
|
+
menuCount: menuList?.length ?? 0,
|
|
199
|
+
durationMs: +(performance.now() - startedAt).toFixed(2)
|
|
198
200
|
});
|
|
199
201
|
return menuList || [];
|
|
200
202
|
} catch (error) {
|
|
201
203
|
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
202
204
|
console.error('[Menu] 从 IndexDB 读取数据失败:', error);
|
|
203
205
|
this.logError('从 IndexDB 读取数据失败', {
|
|
206
|
+
durationMs: +(performance.now() - startedAt).toFixed(2),
|
|
204
207
|
error: errorMessage
|
|
205
208
|
});
|
|
206
209
|
return [];
|
|
@@ -216,24 +219,43 @@ class MenuModule extends _BaseModule.BaseModule {
|
|
|
216
219
|
this.logWarning('saveMenuToIndexDB: dbManager 不可用');
|
|
217
220
|
return;
|
|
218
221
|
}
|
|
222
|
+
const startedAt = performance.now();
|
|
223
|
+
let clearDurationMs = 0;
|
|
224
|
+
let writeDurationMs = 0;
|
|
219
225
|
this.logInfo('开始保存餐牌数据到 IndexDB', {
|
|
220
226
|
menuCount: menuList.length
|
|
221
227
|
});
|
|
222
228
|
try {
|
|
223
229
|
// 先清空现有的餐牌数据
|
|
224
|
-
|
|
230
|
+
const clearStartedAt = performance.now();
|
|
231
|
+
try {
|
|
232
|
+
await this.dbManager.clear(INDEXDB_STORE_NAME);
|
|
233
|
+
} finally {
|
|
234
|
+
clearDurationMs = +(performance.now() - clearStartedAt).toFixed(2);
|
|
235
|
+
}
|
|
225
236
|
|
|
226
237
|
// 逐个保存餐牌(每个餐牌是独立的记录)
|
|
238
|
+
const writeStartedAt = performance.now();
|
|
227
239
|
const savePromises = menuList.map(menu => this.dbManager.add(INDEXDB_STORE_NAME, menu));
|
|
228
|
-
|
|
240
|
+
try {
|
|
241
|
+
await Promise.all(savePromises);
|
|
242
|
+
} finally {
|
|
243
|
+
writeDurationMs = +(performance.now() - writeStartedAt).toFixed(2);
|
|
244
|
+
}
|
|
229
245
|
this.logInfo('餐牌数据已保存到 IndexDB', {
|
|
230
|
-
menuCount: menuList.length
|
|
246
|
+
menuCount: menuList.length,
|
|
247
|
+
clearDurationMs,
|
|
248
|
+
writeDurationMs,
|
|
249
|
+
totalDurationMs: +(performance.now() - startedAt).toFixed(2)
|
|
231
250
|
});
|
|
232
251
|
} catch (error) {
|
|
233
252
|
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
234
253
|
console.error('[Menu] 保存数据到 IndexDB 失败:', error);
|
|
235
254
|
this.logError('保存数据到 IndexDB 失败', {
|
|
236
255
|
menuCount: menuList.length,
|
|
256
|
+
clearDurationMs,
|
|
257
|
+
writeDurationMs,
|
|
258
|
+
totalDurationMs: +(performance.now() - startedAt).toFixed(2),
|
|
237
259
|
error: errorMessage
|
|
238
260
|
});
|
|
239
261
|
}
|
|
@@ -127,14 +127,23 @@ export declare class OrderModule extends BaseModule implements Module {
|
|
|
127
127
|
payment: Record<string, any>;
|
|
128
128
|
previousPayment: Record<string, any>;
|
|
129
129
|
} | null>;
|
|
130
|
-
loadOrdersByServer(
|
|
130
|
+
loadOrdersByServer(options?: {
|
|
131
|
+
trigger?: string;
|
|
132
|
+
}): Promise<OrderData[]>;
|
|
131
133
|
/**
|
|
132
134
|
* 静默全量刷新:后台重新拉取全量 SSE 订单数据并更新本地
|
|
133
135
|
* 拿到完整数据后一次性替换 store,触发订单变更与同步完成事件
|
|
134
136
|
* @returns 刷新后的订单列表
|
|
135
137
|
*/
|
|
136
|
-
silentRefresh(
|
|
138
|
+
silentRefresh(options?: {
|
|
139
|
+
trigger?: string;
|
|
140
|
+
}): Promise<OrderData[]>;
|
|
137
141
|
private normalizeOrderForMemoryList;
|
|
142
|
+
/**
|
|
143
|
+
* 将已经完成集合规范化的订单转换为内存身份,不重复扫描 products/bookings/payments。
|
|
144
|
+
*/
|
|
145
|
+
private prepareNormalizedOrderForMemoryList;
|
|
146
|
+
private prepareNormalizedOrdersForMemoryList;
|
|
138
147
|
private normalizeOrdersForMemoryList;
|
|
139
148
|
private hasRealOrderId;
|
|
140
149
|
private shouldKeepOrderInMemory;
|
|
@@ -299,13 +308,14 @@ export declare class OrderModule extends BaseModule implements Module {
|
|
|
299
308
|
private logDuplicateOrders;
|
|
300
309
|
private compactOrderListByIdentity;
|
|
301
310
|
/**
|
|
302
|
-
*
|
|
303
|
-
*
|
|
304
|
-
* - 无法解析时退化为自然日窗口
|
|
311
|
+
* 当前订单缓存可复用窗口。
|
|
312
|
+
* start_time 模式使用精确的营业日起止时间;配置缺失或异常时退化为自然日。
|
|
305
313
|
*/
|
|
306
|
-
private
|
|
314
|
+
private getCurrentOrderCacheWindow;
|
|
307
315
|
private hasPulledOrdersToday;
|
|
308
316
|
private markOrderPulledAtNow;
|
|
317
|
+
private clearOrderSnapshotMarker;
|
|
318
|
+
private getCurrentShopId;
|
|
309
319
|
private getStorageItem;
|
|
310
320
|
private setStorageItem;
|
|
311
321
|
private loadOrdersFromSQLite;
|
|
@@ -343,7 +353,7 @@ export declare class OrderModule extends BaseModule implements Module {
|
|
|
343
353
|
* 用于 SSE 全量拉取与营业日窗口裁剪。
|
|
344
354
|
*
|
|
345
355
|
* @example
|
|
346
|
-
* await this.replaceOrdersSnapshotInSQLite(orderList)
|
|
356
|
+
* const { orders, persisted } = await this.replaceOrdersSnapshotInSQLite(orderList, 'refresh')
|
|
347
357
|
*/
|
|
348
358
|
private replaceOrdersSnapshotInSQLite;
|
|
349
359
|
/**
|