@pisell/pisellos 2.3.74 → 2.3.76
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/core/index.js +6 -10
- package/dist/model/strategy/adapter/promotion/index.js +9 -0
- package/dist/model/strategy/adapter/walletPass/example.js +3 -26
- package/dist/model/strategy/strategy-example.js +5 -19
- package/dist/modules/Cart/index.js +0 -3
- package/dist/modules/Cart/utils/cartProduct.js +0 -1
- package/dist/modules/Date/index.js +1 -5
- package/dist/modules/Discount/index.js +2 -4
- package/dist/modules/OpenData/index.js +0 -2
- package/dist/modules/Order/index.d.ts +1 -1
- package/dist/modules/Order/index.js +75 -91
- package/dist/modules/Order/types.d.ts +3 -1
- package/dist/modules/Payment/index.js +97 -162
- package/dist/modules/Rules/index.js +1 -4
- package/dist/modules/Schedule/index.js +0 -1
- package/dist/modules/Schedule/utils.js +0 -1
- package/dist/modules/Step/index.js +0 -1
- package/dist/plugins/request.js +1 -4
- package/dist/plugins/window.js +2 -6
- package/dist/server/index.js +97 -129
- package/dist/server/modules/menu/index.js +32 -41
- package/dist/server/modules/order/index.js +2 -4
- package/dist/server/modules/products/index.js +24 -42
- package/dist/server/modules/quotation/index.js +29 -38
- package/dist/server/modules/resource/index.js +3 -4
- package/dist/server/modules/schedule/index.js +27 -35
- package/dist/server/utils/product.js +0 -1
- package/dist/solution/BaseSales/index.js +100 -72
- package/dist/solution/BaseSales/utils/cartPromotion.d.ts +5 -0
- package/dist/solution/BaseSales/utils/cartPromotion.js +15 -6
- package/dist/solution/BookingByStep/index.js +7 -59
- package/dist/solution/BookingByStep/utils/capacity.js +1 -11
- package/dist/solution/BookingByStep/utils/resources.js +1 -3
- package/dist/solution/BookingByStep/utils/timeslots.js +2 -43
- package/dist/solution/BookingTicket/index.js +0 -2
- package/dist/solution/BookingTicket/utils/scan/cloudSearch.js +6 -7
- package/dist/solution/BookingTicket/utils/scan/handleScan.js +25 -31
- package/dist/solution/BookingTicket/utils/scan/index.js +6 -10
- package/dist/solution/BuyTickets/index.js +9 -12
- package/dist/solution/Checkout/index.js +177 -252
- package/dist/solution/Checkout/utils/index.js +4 -16
- package/dist/solution/RegisterAndLogin/index.js +30 -76
- package/dist/solution/ScanOrder/index.js +50 -60
- package/dist/solution/ShopDiscount/index.js +2 -7
- package/dist/solution/VenueBooking/index.js +45 -55
- package/dist/utils/task.js +15 -18
- package/dist/utils/watch.js +0 -1
- package/lib/apis/picoding.js +2 -0
- package/lib/core/index.js +134 -134
- package/lib/effects/index.js +46 -57
- package/lib/index.js +82 -49
- package/lib/model/index.js +14 -21
- package/lib/model/strategy/adapter/dataVariant/adapter.js +38 -50
- package/lib/model/strategy/adapter/dataVariant/evaluator.js +162 -236
- package/lib/model/strategy/adapter/dataVariant/examples.js +229 -237
- package/lib/model/strategy/adapter/dataVariant/index.js +39 -34
- package/lib/model/strategy/adapter/dataVariant/type.js +57 -28
- package/lib/model/strategy/adapter/index.js +100 -64
- package/lib/model/strategy/adapter/itemRule/adapter.js +156 -144
- package/lib/model/strategy/adapter/itemRule/evaluator.js +36 -48
- package/lib/model/strategy/adapter/itemRule/examples.js +295 -225
- package/lib/model/strategy/adapter/itemRule/index.js +83 -57
- package/lib/model/strategy/adapter/itemRule/type.js +97 -36
- package/lib/model/strategy/adapter/promotion/adapter.js +91 -69
- package/lib/model/strategy/adapter/promotion/evaluator.js +444 -310
- package/lib/model/strategy/adapter/promotion/examples.js +222 -234
- package/lib/model/strategy/adapter/promotion/index.js +1 -0
- package/lib/model/strategy/adapter/promotion/type.js +243 -36
- package/lib/model/strategy/adapter/type.js +4 -16
- package/lib/model/strategy/adapter/walletPass/evaluator.js +180 -150
- package/lib/model/strategy/adapter/walletPass/example.js +217 -190
- package/lib/model/strategy/adapter/walletPass/index.js +75 -51
- package/lib/model/strategy/adapter/walletPass/locales.js +36 -58
- package/lib/model/strategy/adapter/walletPass/type.js +4 -16
- package/lib/model/strategy/adapter/walletPass/utils.js +588 -378
- package/lib/model/strategy/engine.js +270 -168
- package/lib/model/strategy/index.js +49 -34
- package/lib/model/strategy/strategy-example.js +243 -239
- package/lib/model/strategy/type.js +100 -40
- package/lib/modules/Account/index.js +39 -53
- package/lib/modules/Account/types.js +20 -33
- package/lib/modules/AccountList/index.js +116 -154
- package/lib/modules/AccountList/types.js +30 -31
- package/lib/modules/AccountList/utils.js +18 -30
- package/lib/modules/BaseModule.js +23 -42
- package/lib/modules/BookingContext/index.js +158 -136
- package/lib/modules/BookingContext/types.js +46 -32
- package/lib/modules/BookingContext/utils/buildCacheItemFromOrderLine.js +138 -146
- package/lib/modules/BookingContext/utils/buildNormalProductCacheItemFromOrderLine.js +78 -83
- package/lib/modules/BookingContext/utils/cacheItemToBookingInput.js +170 -150
- package/lib/modules/BookingContext/utils/flexible.js +38 -55
- package/lib/modules/BookingContext/utils/formatResourceList.js +19 -34
- package/lib/modules/BookingContext/utils/index.js +124 -41
- package/lib/modules/BookingContext/utils/noResource.js +58 -70
- package/lib/modules/BookingContext/utils/orderLineDisplay.js +14 -41
- package/lib/modules/BookingContext/utils/productExtend.js +202 -155
- package/lib/modules/BookingContext/utils/resourceErrors.js +53 -53
- package/lib/modules/BookingContext/utils/resourceSelection.js +19 -41
- package/lib/modules/BookingContext/utils/resources.js +209 -167
- package/lib/modules/BookingContext/utils/serviceTimes.js +115 -116
- package/lib/modules/BookingContext/utils/timeSlices.js +76 -86
- package/lib/modules/Cart/index.js +170 -124
- package/lib/modules/Cart/types.js +46 -51
- package/lib/modules/Cart/utils/cartAccount.js +40 -39
- package/lib/modules/Cart/utils/cartDate.js +56 -61
- package/lib/modules/Cart/utils/cartDiscount.js +28 -33
- package/lib/modules/Cart/utils/cartNote.js +27 -32
- package/lib/modules/Cart/utils/cartProduct.js +321 -251
- package/lib/modules/Cart/utils/cartRelationForms.js +29 -35
- package/lib/modules/Cart/utils/cartResource.js +70 -78
- package/lib/modules/Cart/utils/changePrice.js +22 -50
- package/lib/modules/Cart/utils/index.js +106 -48
- package/lib/modules/Customer/constants.js +13 -34
- package/lib/modules/Customer/index.js +235 -172
- package/lib/modules/Customer/types.js +38 -35
- package/lib/modules/Date/index.js +116 -115
- package/lib/modules/Date/types.js +16 -28
- package/lib/modules/Date/utils.js +174 -123
- package/lib/modules/Discount/index.js +122 -127
- package/lib/modules/Discount/types.js +9 -31
- package/lib/modules/Guests/index.js +30 -56
- package/lib/modules/Guests/types.js +23 -33
- package/lib/modules/Holder/index.js +209 -189
- package/lib/modules/Holder/types.js +4 -16
- package/lib/modules/Holder/utils.js +20 -49
- package/lib/modules/OpenData/index.js +70 -76
- package/lib/modules/OpenData/types.js +4 -16
- package/lib/modules/OpenData/utils.js +44 -78
- package/lib/modules/Order/index.d.ts +1 -1
- package/lib/modules/Order/index.js +1733 -2121
- package/lib/modules/Order/payment-utils.js +77 -120
- package/lib/modules/Order/types.d.ts +3 -1
- package/lib/modules/Order/types.js +88 -35
- package/lib/modules/Order/utils/bundleDiscountHydration.js +41 -74
- package/lib/modules/Order/utils/discountProductLineIdentity.js +142 -100
- package/lib/modules/Order/utils/manualProductDiscount.js +124 -123
- package/lib/modules/Order/utils/orderCollectionIdentity.js +184 -244
- package/lib/modules/Order/utils.js +814 -647
- package/lib/modules/Payment/cash.js +20 -33
- package/lib/modules/Payment/cashRecommendationAlgorithm.js +145 -76
- package/lib/modules/Payment/eftpos.js +16 -30
- package/lib/modules/Payment/index.js +532 -399
- package/lib/modules/Payment/mx51.js +1 -0
- package/lib/modules/Payment/types.js +230 -108
- package/lib/modules/Payment/utils.js +52 -51
- package/lib/modules/Payment/walletpass.js +485 -660
- package/lib/modules/Product/index.js +51 -46
- package/lib/modules/Product/types.js +4 -16
- package/lib/modules/Product/utils.js +32 -33
- package/lib/modules/ProductList/index.js +113 -123
- package/lib/modules/ProductList/types.js +9 -31
- package/lib/modules/Quotation/index.js +81 -118
- package/lib/modules/Quotation/types.js +4 -16
- package/lib/modules/Resource/index.js +27 -59
- package/lib/modules/Resource/types.js +22 -32
- package/lib/modules/Resource/utils.js +30 -38
- package/lib/modules/ResourcePlanner/index.js +25 -23
- package/lib/modules/ResourcePlanner/localClock.js +54 -119
- package/lib/modules/ResourcePlanner/planner.js +589 -770
- package/lib/modules/ResourcePlanner/types.js +10 -29
- package/lib/modules/Rules/index.js +1218 -959
- package/lib/modules/Rules/types.js +17 -40
- package/lib/modules/SalesSummary/index.js +85 -90
- package/lib/modules/SalesSummary/types.js +4 -16
- package/lib/modules/SalesSummary/utils.js +355 -430
- package/lib/modules/ScanOrderLogger/index.js +59 -79
- package/lib/modules/ScanOrderLogger/providers/feishu.js +54 -78
- package/lib/modules/ScanOrderLogger/providers/grafana.js +13 -36
- package/lib/modules/ScanOrderLogger/types.js +4 -16
- package/lib/modules/Schedule/getDateIsInSchedule.js +334 -163
- package/lib/modules/Schedule/index.js +100 -114
- package/lib/modules/Schedule/type.js +4 -16
- package/lib/modules/Schedule/types.js +4 -16
- package/lib/modules/Schedule/utils.js +433 -291
- package/lib/modules/Step/index.js +40 -52
- package/lib/modules/Step/tyeps.js +4 -16
- package/lib/modules/Summary/index.js +66 -71
- package/lib/modules/Summary/types.js +4 -16
- package/lib/modules/Summary/utils.js +773 -418
- package/lib/modules/SurchargeList/index.js +46 -60
- package/lib/modules/SurchargeList/types.js +4 -16
- package/lib/modules/index.js +245 -63
- package/lib/plugins/app.js +4 -16
- package/lib/plugins/index.js +36 -25
- package/lib/plugins/request.js +137 -126
- package/lib/plugins/shopStore.js +4 -16
- package/lib/plugins/user.js +4 -16
- package/lib/plugins/window.js +171 -179
- package/lib/server/index.js +3050 -2665
- package/lib/server/modules/floor-plan/index.js +118 -134
- package/lib/server/modules/floor-plan/types.js +15 -30
- package/lib/server/modules/index.js +106 -68
- package/lib/server/modules/menu/index.js +142 -122
- package/lib/server/modules/menu/types.js +18 -31
- package/lib/server/modules/order/index.js +784 -1002
- package/lib/server/modules/order/types.js +122 -33
- package/lib/server/modules/order/utils/filterBookings.js +219 -194
- package/lib/server/modules/order/utils/filterOrders.js +118 -92
- package/lib/server/modules/payment/index.js +59 -83
- package/lib/server/modules/payment/types.js +4 -16
- package/lib/server/modules/products/index.js +583 -471
- package/lib/server/modules/products/types.js +44 -34
- package/lib/server/modules/quotation/index.js +137 -172
- package/lib/server/modules/quotation/types.js +21 -31
- package/lib/server/modules/resource/index.js +220 -186
- package/lib/server/modules/resource/types.js +42 -33
- package/lib/server/modules/schedule/index.js +135 -123
- package/lib/server/modules/schedule/types.js +14 -21
- package/lib/server/modules/schedule/utils.js +334 -163
- package/lib/server/types.js +4 -16
- package/lib/server/utils/index.js +25 -23
- package/lib/server/utils/product.js +196 -139
- package/lib/server/utils/schedule.js +34 -41
- package/lib/server/utils/small-ticket.js +479 -456
- package/lib/server/utils/time.js +40 -49
- package/lib/solution/BaseSales/index.js +1220 -1412
- package/lib/solution/BaseSales/types.js +42 -38
- package/lib/solution/BaseSales/utils/cartPromotion.d.ts +5 -0
- package/lib/solution/BaseSales/utils/cartPromotion.js +652 -498
- package/lib/solution/BaseSales/utils/parseSalesResponse.js +85 -105
- package/lib/solution/BaseSales/utils/quotationPrice.js +56 -114
- package/lib/solution/BaseSales/utils/transformBaseProductToOrderProduct.js +108 -145
- package/lib/solution/BaseSales/utils.js +158 -143
- package/lib/solution/BookingByStep/index.js +1527 -1340
- package/lib/solution/BookingByStep/types.js +40 -99
- package/lib/solution/BookingByStep/utils/capacity.js +192 -160
- package/lib/solution/BookingByStep/utils/products.js +42 -52
- package/lib/solution/BookingByStep/utils/resources.js +527 -330
- package/lib/solution/BookingByStep/utils/stock.js +65 -44
- package/lib/solution/BookingByStep/utils/timeslots.js +129 -131
- package/lib/solution/BookingTicket/index.js +607 -522
- package/lib/solution/BookingTicket/types.js +99 -77
- package/lib/solution/BookingTicket/utils/addProductDecision.js +226 -189
- package/lib/solution/BookingTicket/utils/addTimeAvailability.js +58 -89
- package/lib/solution/BookingTicket/utils/bookingStatus.js +107 -78
- package/lib/solution/BookingTicket/utils/cartView.js +114 -99
- package/lib/solution/BookingTicket/utils/exampleData.js +185 -0
- package/lib/solution/BookingTicket/utils/orderCustomer.js +18 -41
- package/lib/solution/BookingTicket/utils/resolveBestAddTimePlan.js +219 -86
- package/lib/solution/BookingTicket/utils/scan/applyGlobalScan.js +168 -116
- package/lib/solution/BookingTicket/utils/scan/cloudSearch.js +79 -91
- package/lib/solution/BookingTicket/utils/scan/formatGlobalScan.js +54 -41
- package/lib/solution/BookingTicket/utils/scan/handleScan.js +84 -81
- package/lib/solution/BookingTicket/utils/scan/index.js +98 -104
- package/lib/solution/BookingTicket/utils/scan/scanCache.js +90 -43
- package/lib/solution/BookingTicket/utils/scan/scanProductValues.js +52 -64
- package/lib/solution/BookingTicket/utils/sessionCatalogStale.js +56 -58
- package/lib/solution/BookingTicket/utils/weighingProductSku.js +34 -48
- package/lib/solution/BuyTickets/index.js +67 -70
- package/lib/solution/BuyTickets/types.js +22 -38
- package/lib/solution/Checkout/index.js +1451 -1158
- package/lib/solution/Checkout/types.js +91 -61
- package/lib/solution/Checkout/utils/index.js +228 -156
- package/lib/solution/PlaceOrder/index.js +55 -0
- package/lib/solution/RegisterAndLogin/config.js +493 -460
- package/lib/solution/RegisterAndLogin/index.js +633 -630
- package/lib/solution/RegisterAndLogin/types.js +189 -76
- package/lib/solution/RegisterAndLogin/utils.js +183 -125
- package/lib/solution/Sales/index.js +328 -324
- package/lib/solution/Sales/types.js +27 -33
- package/lib/solution/ScanOrder/index.js +832 -864
- package/lib/solution/ScanOrder/types.js +33 -34
- package/lib/solution/ScanOrder/utils.js +409 -374
- package/lib/solution/ShopDiscount/index.js +226 -232
- package/lib/solution/ShopDiscount/types.js +15 -37
- package/lib/solution/ShopDiscount/utils.js +300 -172
- package/lib/solution/UnifiedBookingSales/index.js +1035 -1201
- package/lib/solution/UnifiedBookingSales/remoteOccupancyCache.js +99 -159
- package/lib/solution/UnifiedBookingSales/types.js +10 -32
- package/lib/solution/VenueBooking/index.js +811 -879
- package/lib/solution/VenueBooking/types.js +19 -39
- package/lib/solution/VenueBooking/utils/dateSummary.js +27 -56
- package/lib/solution/VenueBooking/utils/resource.js +31 -54
- package/lib/solution/VenueBooking/utils/slotMerge.js +94 -107
- package/lib/solution/VenueBooking/utils/timeSlot.js +132 -132
- package/lib/solution/VenueBooking/utils.js +130 -67
- package/lib/solution/index.js +124 -41
- package/lib/store/createStore.js +32 -29
- package/lib/types/index.js +4 -16
- package/lib/utils/payment-number.js +26 -46
- package/lib/utils/task.js +36 -36
- package/lib/utils/watch.js +81 -47
- package/package.json +2 -1
|
@@ -1,139 +1,164 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
var
|
|
7
|
-
|
|
8
|
-
|
|
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;
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
var _exportNames = {
|
|
7
|
+
SalesImpl: true,
|
|
8
|
+
Sales: true
|
|
18
9
|
};
|
|
19
|
-
|
|
20
|
-
var
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
Sales: () => SalesImpl,
|
|
34
|
-
SalesImpl: () => SalesImpl,
|
|
35
|
-
default: () => Sales_default
|
|
10
|
+
exports.default = exports.SalesImpl = exports.Sales = void 0;
|
|
11
|
+
var _dayjs = _interopRequireDefault(require("dayjs"));
|
|
12
|
+
var _BaseModule = require("../../modules/BaseModule");
|
|
13
|
+
var _types = require("./types");
|
|
14
|
+
Object.keys(_types).forEach(function (key) {
|
|
15
|
+
if (key === "default" || key === "__esModule") return;
|
|
16
|
+
if (Object.prototype.hasOwnProperty.call(_exportNames, key)) return;
|
|
17
|
+
if (key in exports && exports[key] === _types[key]) return;
|
|
18
|
+
Object.defineProperty(exports, key, {
|
|
19
|
+
enumerable: true,
|
|
20
|
+
get: function () {
|
|
21
|
+
return _types[key];
|
|
22
|
+
}
|
|
23
|
+
});
|
|
36
24
|
});
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
25
|
+
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
|
26
|
+
const UNPAID_PAYMENT_STATUSES = new Set(['unpaid', 'partially_paid', 'payment_processing']);
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Sales 解决方案基础骨架
|
|
30
|
+
*
|
|
31
|
+
* 当前阶段仅提供:
|
|
32
|
+
* - solution 目录结构
|
|
33
|
+
* - 标准入口导出
|
|
34
|
+
* - 基础生命周期占位
|
|
35
|
+
*
|
|
36
|
+
* 不提前实现 store 编排、初始化流程细节、状态计算逻辑。
|
|
37
|
+
* TIPS:这块应该改名叫 ReservationModule 更合适一些,目前这边只服务于 Reservation 的 floormap 展示。
|
|
38
|
+
*/
|
|
39
|
+
class SalesImpl extends _BaseModule.BaseModule {
|
|
40
|
+
defaultName = 'sales';
|
|
41
|
+
defaultVersion = '1.0.0';
|
|
42
|
+
defaultTimelineBucketMinutes = 15;
|
|
43
|
+
isSolution = true;
|
|
44
|
+
store;
|
|
45
|
+
options = {};
|
|
46
|
+
otherParams = {};
|
|
47
|
+
request;
|
|
48
|
+
logger; // LoggerManager 实例
|
|
49
|
+
|
|
45
50
|
constructor(name, version) {
|
|
46
51
|
super(name, version);
|
|
47
|
-
this.defaultName = "sales";
|
|
48
|
-
this.defaultVersion = "1.0.0";
|
|
49
|
-
this.defaultTimelineBucketMinutes = 15;
|
|
50
|
-
this.isSolution = true;
|
|
51
|
-
this.options = {};
|
|
52
|
-
this.otherParams = {};
|
|
53
52
|
}
|
|
54
53
|
async initialize(core, options = {}) {
|
|
55
54
|
this.core = core;
|
|
56
55
|
this.options = options;
|
|
57
56
|
this.otherParams = options.otherParams || {};
|
|
58
|
-
const appPlugin = this.core.getPlugin(
|
|
59
|
-
|
|
57
|
+
const appPlugin = this.core.getPlugin('app');
|
|
58
|
+
// @ts-ignore
|
|
59
|
+
this.request = appPlugin?.getApp().request;
|
|
60
60
|
if (!this.request) {
|
|
61
|
-
throw new Error(
|
|
61
|
+
throw new Error('Sales 解决方案需要 request 插件支持');
|
|
62
62
|
}
|
|
63
|
+
|
|
64
|
+
// 初始化 store 状态
|
|
63
65
|
this.store = {
|
|
64
|
-
currentDay: (0,
|
|
66
|
+
currentDay: (0, _dayjs.default)().startOf('day'),
|
|
65
67
|
reservationList: []
|
|
66
68
|
};
|
|
67
|
-
|
|
69
|
+
|
|
70
|
+
// TODO(Rolo): 等领导提供 reservationList 的目标结构后,
|
|
71
|
+
// 在这里补充从 Server/Order 读取 currentDay 原始数据并转换为预约列表。
|
|
72
|
+
|
|
73
|
+
this.logger = appPlugin?.getApp().logger;
|
|
68
74
|
await this.core.effects.emit(`${this.name}:onInited`, {});
|
|
69
75
|
}
|
|
76
|
+
|
|
70
77
|
// -------------------------------------------------------------------------
|
|
71
78
|
// SalesModuleAPI 实现
|
|
72
79
|
// -------------------------------------------------------------------------
|
|
80
|
+
|
|
73
81
|
/** 获取当前 store 快照 */
|
|
74
82
|
getState() {
|
|
75
|
-
return {
|
|
83
|
+
return {
|
|
84
|
+
...this.store
|
|
85
|
+
};
|
|
76
86
|
}
|
|
87
|
+
|
|
77
88
|
/** 获取当前选择日期 */
|
|
78
89
|
getCurrentDay() {
|
|
79
90
|
return this.store.currentDay;
|
|
80
91
|
}
|
|
92
|
+
|
|
81
93
|
/** 设置当前选择日期,会自动归一到当天 00:00:00 */
|
|
82
94
|
setCurrentDay(currentDay) {
|
|
83
|
-
const normalized = (0,
|
|
95
|
+
const normalized = (0, _dayjs.default)(currentDay).startOf('day');
|
|
84
96
|
this.store.currentDay = normalized;
|
|
85
|
-
this.core.effects.emit(
|
|
97
|
+
this.core.effects.emit(_types.SalesHooks.onCurrentDayChanged, {
|
|
98
|
+
currentDay: normalized
|
|
99
|
+
});
|
|
86
100
|
return normalized;
|
|
87
101
|
}
|
|
102
|
+
|
|
88
103
|
/** 获取当前预约列表 */
|
|
89
104
|
getReservationList() {
|
|
90
105
|
return [...this.store.reservationList];
|
|
91
106
|
}
|
|
107
|
+
|
|
92
108
|
/** 覆盖设置当前预约列表 */
|
|
93
109
|
setReservationList(reservationList) {
|
|
94
110
|
this.store.reservationList = reservationList;
|
|
95
|
-
this.core.effects.emit(
|
|
111
|
+
this.core.effects.emit(_types.SalesHooks.onReservationListChanged, {
|
|
112
|
+
reservationList
|
|
113
|
+
});
|
|
96
114
|
return reservationList;
|
|
97
115
|
}
|
|
116
|
+
|
|
98
117
|
/** 重置预约列表为空 */
|
|
99
118
|
resetReservationList() {
|
|
100
119
|
this.store.reservationList = [];
|
|
101
|
-
this.core.effects.emit(
|
|
120
|
+
this.core.effects.emit(_types.SalesHooks.onReservationListChanged, {
|
|
121
|
+
reservationList: []
|
|
122
|
+
});
|
|
102
123
|
return [];
|
|
103
124
|
}
|
|
104
125
|
async cancelOrder(orderIds) {
|
|
105
|
-
const orderModule = this.core.getModule(
|
|
126
|
+
const orderModule = this.core.getModule('order');
|
|
106
127
|
if (!orderModule) {
|
|
107
|
-
throw new Error(
|
|
128
|
+
throw new Error('Sales 取消订单失败:Order 模块未注册');
|
|
108
129
|
}
|
|
109
|
-
return orderModule.cancelOrder({
|
|
130
|
+
return orderModule.cancelOrder({
|
|
131
|
+
order_ids: orderIds
|
|
132
|
+
});
|
|
110
133
|
}
|
|
111
134
|
async changeBookingStatus(params) {
|
|
112
|
-
const orderModule = this.core.getModule(
|
|
135
|
+
const orderModule = this.core.getModule('order');
|
|
113
136
|
if (!orderModule) {
|
|
114
|
-
throw new Error(
|
|
137
|
+
throw new Error('Sales 变更预约状态失败:Order 模块未注册');
|
|
115
138
|
}
|
|
116
139
|
return orderModule.changeBookingStatus(params);
|
|
117
140
|
}
|
|
141
|
+
|
|
118
142
|
// -------------------------------------------------------------------------
|
|
119
143
|
// 生命周期
|
|
120
144
|
// -------------------------------------------------------------------------
|
|
145
|
+
|
|
121
146
|
async destroy() {
|
|
122
147
|
await this.core.effects.emit(`${this.name}:onDestroy`, {});
|
|
123
148
|
super.destroy();
|
|
124
149
|
}
|
|
150
|
+
|
|
125
151
|
/**
|
|
126
152
|
* 时间轴粒度(分钟)
|
|
127
153
|
* - 默认 15 分钟
|
|
128
154
|
* - 可通过 registerModule 时的 options.otherParams.timelineBucketMinutes 覆盖
|
|
129
155
|
*/
|
|
130
156
|
getTimelineBucketMinutes() {
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
if (!Number.isFinite(dynamicBucket) || dynamicBucket <= 0)
|
|
134
|
-
return this.defaultTimelineBucketMinutes;
|
|
157
|
+
const dynamicBucket = Number(this.otherParams?.timelineBucketMinutes);
|
|
158
|
+
if (!Number.isFinite(dynamicBucket) || dynamicBucket <= 0) return this.defaultTimelineBucketMinutes;
|
|
135
159
|
return Math.floor(dynamicBucket);
|
|
136
160
|
}
|
|
161
|
+
|
|
137
162
|
/**
|
|
138
163
|
* 解析营业日统计窗口:
|
|
139
164
|
* - 当 operating_day_boundary.type === 'start_time'
|
|
@@ -141,23 +166,32 @@ var SalesImpl = class extends import_BaseModule.BaseModule {
|
|
|
141
166
|
* - 否则回退自然日(00:00 到次日 00:00)
|
|
142
167
|
*/
|
|
143
168
|
getTimelineRangeByOperatingBoundary() {
|
|
144
|
-
|
|
145
|
-
const
|
|
146
|
-
const
|
|
147
|
-
const
|
|
148
|
-
const
|
|
149
|
-
const
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
169
|
+
const today = (0, _dayjs.default)();
|
|
170
|
+
const defaultStart = today.startOf('day');
|
|
171
|
+
const defaultEndExclusive = defaultStart.add(1, 'day');
|
|
172
|
+
const appPlugin = this.core.getPlugin('app');
|
|
173
|
+
const coreData = appPlugin?.getApp()?.data?.store?.getStore?.().getDataByModel?.('core');
|
|
174
|
+
const operatingDayBoundary = coreData?.core?.operating_day_boundary;
|
|
175
|
+
if (operatingDayBoundary?.type !== 'start_time') {
|
|
176
|
+
return {
|
|
177
|
+
startAt: defaultStart,
|
|
178
|
+
endExclusiveAt: defaultEndExclusive
|
|
179
|
+
};
|
|
153
180
|
}
|
|
154
|
-
const boundaryTime = String(operatingDayBoundary.time ||
|
|
155
|
-
const startAt = (0,
|
|
181
|
+
const boundaryTime = String(operatingDayBoundary.time || '').trim();
|
|
182
|
+
const startAt = (0, _dayjs.default)(`${today.format('YYYY-MM-DD')} ${boundaryTime}`);
|
|
156
183
|
if (!startAt.isValid()) {
|
|
157
|
-
return {
|
|
184
|
+
return {
|
|
185
|
+
startAt: defaultStart,
|
|
186
|
+
endExclusiveAt: defaultEndExclusive
|
|
187
|
+
};
|
|
158
188
|
}
|
|
159
|
-
return {
|
|
189
|
+
return {
|
|
190
|
+
startAt,
|
|
191
|
+
endExclusiveAt: startAt.add(1, 'day')
|
|
192
|
+
};
|
|
160
193
|
}
|
|
194
|
+
|
|
161
195
|
/**
|
|
162
196
|
* 统计时间轴每个时间片的预约数量。
|
|
163
197
|
* 算法使用差分数组,复杂度 O(n + s):
|
|
@@ -166,39 +200,37 @@ var SalesImpl = class extends import_BaseModule.BaseModule {
|
|
|
166
200
|
*/
|
|
167
201
|
getTimelineHighlights(bookingList = [], startDateTime, endDateTime) {
|
|
168
202
|
const bucketMinutes = this.getTimelineBucketMinutes();
|
|
169
|
-
const bucketMs = bucketMinutes * 60 *
|
|
170
|
-
const customStartAt = startDateTime ? (0,
|
|
171
|
-
const customEndExclusiveAt = endDateTime ? (0,
|
|
203
|
+
const bucketMs = bucketMinutes * 60 * 1000;
|
|
204
|
+
const customStartAt = startDateTime ? (0, _dayjs.default)(startDateTime) : (0, _dayjs.default)('');
|
|
205
|
+
const customEndExclusiveAt = endDateTime ? (0, _dayjs.default)(endDateTime) : (0, _dayjs.default)('');
|
|
172
206
|
const hasValidCustomRange = customStartAt.isValid() && customEndExclusiveAt.isValid();
|
|
173
|
-
const {
|
|
207
|
+
const {
|
|
208
|
+
startAt,
|
|
209
|
+
endExclusiveAt
|
|
210
|
+
} = hasValidCustomRange ? {
|
|
211
|
+
startAt: customStartAt,
|
|
212
|
+
endExclusiveAt: customEndExclusiveAt
|
|
213
|
+
} : this.getTimelineRangeByOperatingBoundary();
|
|
174
214
|
const startAtMs = startAt.valueOf();
|
|
175
215
|
const endExclusiveAtMs = endExclusiveAt.valueOf();
|
|
176
|
-
if (!startAt.isValid() || !endExclusiveAt.isValid() || endExclusiveAtMs <= startAtMs)
|
|
177
|
-
return [];
|
|
216
|
+
if (!startAt.isValid() || !endExclusiveAt.isValid() || endExclusiveAtMs <= startAtMs) return [];
|
|
178
217
|
const slotCount = Math.ceil((endExclusiveAtMs - startAtMs) / bucketMs);
|
|
179
|
-
if (slotCount <= 0)
|
|
180
|
-
return [];
|
|
218
|
+
if (slotCount <= 0) return [];
|
|
181
219
|
const diff = new Int32Array(slotCount + 1);
|
|
182
220
|
for (let i = 0; i < bookingList.length; i++) {
|
|
183
221
|
const booking = bookingList[i];
|
|
184
|
-
const appointmentStatus = String(booking.appointment_status ?? booking.status ??
|
|
185
|
-
if (appointmentStatus ===
|
|
186
|
-
continue;
|
|
222
|
+
const appointmentStatus = String(booking.appointment_status ?? booking.status ?? '');
|
|
223
|
+
if (appointmentStatus === 'new' || appointmentStatus === 'rejected' || appointmentStatus === 'cancelled') continue;
|
|
187
224
|
const bookingStartAt = this.toBookingDateTime(booking.start_date, booking.start_time);
|
|
188
225
|
const bookingEndAt = this.toBookingDateTime(booking.end_date, booking.end_time);
|
|
189
|
-
if (!bookingStartAt.isValid() || !bookingEndAt.isValid())
|
|
190
|
-
continue;
|
|
226
|
+
if (!bookingStartAt.isValid() || !bookingEndAt.isValid()) continue;
|
|
191
227
|
const overlapStartMs = Math.max(bookingStartAt.valueOf(), startAtMs);
|
|
228
|
+
// 结束时间按 <= 处理:转成右开区间时 +1ms,保证命中 end_time 所在时间片
|
|
192
229
|
const overlapEndExclusiveMs = Math.min(bookingEndAt.valueOf() + 1, endExclusiveAtMs);
|
|
193
|
-
if (overlapStartMs >= overlapEndExclusiveMs)
|
|
194
|
-
continue;
|
|
230
|
+
if (overlapStartMs >= overlapEndExclusiveMs) continue;
|
|
195
231
|
const startIdx = Math.max(0, Math.ceil((overlapStartMs - startAtMs) / bucketMs));
|
|
196
|
-
const endIdx = Math.min(
|
|
197
|
-
|
|
198
|
-
Math.floor((overlapEndExclusiveMs - 1 - startAtMs) / bucketMs)
|
|
199
|
-
);
|
|
200
|
-
if (startIdx > endIdx)
|
|
201
|
-
continue;
|
|
232
|
+
const endIdx = Math.min(slotCount - 1, Math.floor((overlapEndExclusiveMs - 1 - startAtMs) / bucketMs));
|
|
233
|
+
if (startIdx > endIdx) continue;
|
|
202
234
|
diff[startIdx] += 1;
|
|
203
235
|
diff[endIdx + 1] -= 1;
|
|
204
236
|
}
|
|
@@ -210,14 +242,17 @@ var SalesImpl = class extends import_BaseModule.BaseModule {
|
|
|
210
242
|
}
|
|
211
243
|
return timeline;
|
|
212
244
|
}
|
|
245
|
+
|
|
213
246
|
/** dayjs 未启用插件时,手动封装 >= 判断 */
|
|
214
247
|
isSameOrAfter(left, right) {
|
|
215
248
|
return left.isAfter(right) || left.isSame(right);
|
|
216
249
|
}
|
|
250
|
+
|
|
217
251
|
/** dayjs 未启用插件时,手动封装 <= 判断 */
|
|
218
252
|
isSameOrBefore(left, right) {
|
|
219
253
|
return left.isBefore(right) || left.isSame(right);
|
|
220
254
|
}
|
|
255
|
+
|
|
221
256
|
/**
|
|
222
257
|
* 将 booking 的日期/时间字段统一组装为 dayjs
|
|
223
258
|
* - date + time 都有:按完整时间解析
|
|
@@ -225,66 +260,59 @@ var SalesImpl = class extends import_BaseModule.BaseModule {
|
|
|
225
260
|
* - 都没有:返回 invalid
|
|
226
261
|
*/
|
|
227
262
|
toBookingDateTime(date, time) {
|
|
228
|
-
if (!date && !time)
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
return (0, import_dayjs.default)(`${date} ${time}`);
|
|
232
|
-
return (0, import_dayjs.default)(date || time);
|
|
263
|
+
if (!date && !time) return (0, _dayjs.default)('');
|
|
264
|
+
if (date && time) return (0, _dayjs.default)(`${date} ${time}`);
|
|
265
|
+
return (0, _dayjs.default)(date || time);
|
|
233
266
|
}
|
|
267
|
+
|
|
234
268
|
/**
|
|
235
269
|
* 预约状态映射(面向 UI 的统一状态)
|
|
236
270
|
* 说明:locked 逻辑后续会补业务规则,这里先保留分支占位。
|
|
237
271
|
*/
|
|
238
272
|
getBookingStatus(appointmentStatus) {
|
|
239
|
-
if (appointmentStatus ===
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
return "reserved";
|
|
273
|
+
if (appointmentStatus === 'started') return 'occupied';
|
|
274
|
+
if (appointmentStatus === 'confirmed' || appointmentStatus === 'arrived') {
|
|
275
|
+
return 'reserved';
|
|
243
276
|
}
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
return
|
|
277
|
+
// TODO: locked 的业务规则后续补充。
|
|
278
|
+
if (appointmentStatus === 'locked') return 'locked';
|
|
279
|
+
return undefined;
|
|
247
280
|
}
|
|
248
281
|
getResourceId(resource) {
|
|
249
|
-
return resource.id ?? resource.form_record_id ??
|
|
282
|
+
return resource.id ?? resource.form_record_id ?? '';
|
|
250
283
|
}
|
|
251
284
|
getBookingAppointmentStatus(booking) {
|
|
252
|
-
return String(booking.appointment_status ?? booking.status ??
|
|
285
|
+
return String(booking.appointment_status ?? booking.status ?? '');
|
|
253
286
|
}
|
|
254
287
|
isBookingIncludedInStats(booking) {
|
|
255
288
|
const appointmentStatus = this.getBookingAppointmentStatus(booking);
|
|
256
|
-
return appointmentStatus !==
|
|
289
|
+
return appointmentStatus !== 'new' && appointmentStatus !== 'rejected' && appointmentStatus !== 'cancelled';
|
|
257
290
|
}
|
|
258
291
|
getBookingOrderPaymentStatus(booking) {
|
|
259
|
-
|
|
260
|
-
return String(((_a = booking.order) == null ? void 0 : _a.payment_status) ?? "");
|
|
292
|
+
return String(booking.order?.payment_status ?? '');
|
|
261
293
|
}
|
|
262
294
|
isUnpaidBooking(booking) {
|
|
263
295
|
return UNPAID_PAYMENT_STATUSES.has(this.getBookingOrderPaymentStatus(booking));
|
|
264
296
|
}
|
|
265
297
|
countBookingOrders(bookings) {
|
|
266
|
-
const orderIds =
|
|
267
|
-
bookings.forEach(
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
if (orderId === void 0 || orderId === null)
|
|
271
|
-
return;
|
|
298
|
+
const orderIds = new Set();
|
|
299
|
+
bookings.forEach(booking => {
|
|
300
|
+
const orderId = booking.order_id ?? booking.order?.order_id;
|
|
301
|
+
if (orderId === undefined || orderId === null) return;
|
|
272
302
|
orderIds.add(String(orderId));
|
|
273
303
|
});
|
|
274
304
|
return orderIds.size;
|
|
275
305
|
}
|
|
276
306
|
countCompletedBookings(bookings) {
|
|
277
|
-
return bookings.filter(
|
|
307
|
+
return bookings.filter(booking => this.getBookingAppointmentStatus(booking) === 'completed').length;
|
|
278
308
|
}
|
|
279
309
|
groupBookingsByResource(bookingList) {
|
|
280
|
-
const bookingMap =
|
|
281
|
-
bookingList.forEach(
|
|
282
|
-
if (!Array.isArray(booking.resources))
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
if (relationId === void 0 || relationId === null)
|
|
287
|
-
return;
|
|
310
|
+
const bookingMap = new Map();
|
|
311
|
+
bookingList.forEach(booking => {
|
|
312
|
+
if (!Array.isArray(booking.resources)) return;
|
|
313
|
+
booking.resources.forEach(resource => {
|
|
314
|
+
const relationId = resource?.relation_id;
|
|
315
|
+
if (relationId === undefined || relationId === null) return;
|
|
288
316
|
const key = String(relationId);
|
|
289
317
|
const list = bookingMap.get(key) || [];
|
|
290
318
|
list.push(booking);
|
|
@@ -294,12 +322,10 @@ var SalesImpl = class extends import_BaseModule.BaseModule {
|
|
|
294
322
|
return bookingMap;
|
|
295
323
|
}
|
|
296
324
|
isBookingMatchedResource(booking, resourceId) {
|
|
297
|
-
if (!Array.isArray(booking.resources))
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
if (relationId === void 0 || relationId === null)
|
|
302
|
-
return false;
|
|
325
|
+
if (!Array.isArray(booking.resources)) return false;
|
|
326
|
+
return booking.resources.some(resource => {
|
|
327
|
+
const relationId = resource?.relation_id;
|
|
328
|
+
if (relationId === undefined || relationId === null) return false;
|
|
303
329
|
return String(relationId) === String(resourceId);
|
|
304
330
|
});
|
|
305
331
|
}
|
|
@@ -310,6 +336,7 @@ var SalesImpl = class extends import_BaseModule.BaseModule {
|
|
|
310
336
|
return leftStartAt - rightStartAt;
|
|
311
337
|
});
|
|
312
338
|
}
|
|
339
|
+
|
|
313
340
|
/**
|
|
314
341
|
* 资源下 bookings 的返回裁剪策略:
|
|
315
342
|
* 1) 优先返回当前时刻正在进行中的预约(允许并发返回多条)
|
|
@@ -319,109 +346,96 @@ var SalesImpl = class extends import_BaseModule.BaseModule {
|
|
|
319
346
|
* - includeAllFuture 时,保留选中预约并返回当天全部后续预约;仅详情接口启用。
|
|
320
347
|
*/
|
|
321
348
|
pickBookingsForCurrentPoint(current, bookings, options = {}) {
|
|
322
|
-
if (bookings.length === 0)
|
|
323
|
-
|
|
324
|
-
const
|
|
325
|
-
|
|
326
|
-
|
|
349
|
+
if (bookings.length === 0) return [];
|
|
350
|
+
const currentDayStart = current.startOf('day');
|
|
351
|
+
const currentDayEnd = current.endOf('day');
|
|
352
|
+
|
|
353
|
+
// 仅保留与 current 当天有时间交集的预约,避免跨天误命中
|
|
354
|
+
const dayScopedBookings = bookings.filter(booking => {
|
|
327
355
|
const startAt = this.toBookingDateTime(booking.start_date, booking.start_time);
|
|
328
356
|
const endAt = this.toBookingDateTime(booking.end_date, booking.end_time);
|
|
329
|
-
if (!startAt.isValid() || !endAt.isValid())
|
|
330
|
-
return false;
|
|
357
|
+
if (!startAt.isValid() || !endAt.isValid()) return false;
|
|
331
358
|
return this.isSameOrBefore(startAt, currentDayEnd) && this.isSameOrAfter(endAt, currentDayStart);
|
|
332
359
|
});
|
|
333
|
-
if (dayScopedBookings.length === 0)
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
if (selectedBookings.length === 0)
|
|
337
|
-
return selectedBookings;
|
|
360
|
+
if (dayScopedBookings.length === 0) return [];
|
|
361
|
+
const appendNextStartGroupIfNeeded = selectedBookings => {
|
|
362
|
+
if (selectedBookings.length === 0) return selectedBookings;
|
|
338
363
|
if (options.includeAllFuture) {
|
|
339
|
-
const allFutureBookings = dayScopedBookings.filter(
|
|
364
|
+
const allFutureBookings = dayScopedBookings.filter(booking => {
|
|
340
365
|
const startAt = this.toBookingDateTime(booking.start_date, booking.start_time);
|
|
341
366
|
return startAt.isValid() && startAt.isAfter(current);
|
|
342
367
|
});
|
|
343
368
|
return [...selectedBookings, ...allFutureBookings];
|
|
344
369
|
}
|
|
345
|
-
const shouldAppendNextGroup = selectedBookings.some(
|
|
346
|
-
|
|
347
|
-
);
|
|
348
|
-
|
|
349
|
-
return selectedBookings;
|
|
350
|
-
const selectedStartValues = new Set(
|
|
351
|
-
selectedBookings.map((booking) => this.toBookingDateTime(booking.start_date, booking.start_time)).filter((startAt) => startAt.isValid()).map((startAt) => startAt.valueOf())
|
|
352
|
-
);
|
|
353
|
-
const futureCandidates = dayScopedBookings.filter((booking) => {
|
|
370
|
+
const shouldAppendNextGroup = selectedBookings.some(booking => booking.status === 'reserved' || booking.status === 'occupied');
|
|
371
|
+
if (!shouldAppendNextGroup) return selectedBookings;
|
|
372
|
+
const selectedStartValues = new Set(selectedBookings.map(booking => this.toBookingDateTime(booking.start_date, booking.start_time)).filter(startAt => startAt.isValid()).map(startAt => startAt.valueOf()));
|
|
373
|
+
const futureCandidates = dayScopedBookings.filter(booking => {
|
|
354
374
|
const startAt = this.toBookingDateTime(booking.start_date, booking.start_time);
|
|
355
|
-
if (!startAt.isValid())
|
|
356
|
-
return false;
|
|
375
|
+
if (!startAt.isValid()) return false;
|
|
357
376
|
return startAt.isAfter(current) && !selectedStartValues.has(startAt.valueOf());
|
|
358
377
|
});
|
|
359
|
-
if (futureCandidates.length === 0)
|
|
360
|
-
|
|
361
|
-
const
|
|
362
|
-
futureCandidates[0].start_date,
|
|
363
|
-
futureCandidates[0].start_time
|
|
364
|
-
).valueOf();
|
|
365
|
-
const nextStartGroup = futureCandidates.filter((booking) => {
|
|
378
|
+
if (futureCandidates.length === 0) return selectedBookings;
|
|
379
|
+
const nextStartAt = this.toBookingDateTime(futureCandidates[0].start_date, futureCandidates[0].start_time).valueOf();
|
|
380
|
+
const nextStartGroup = futureCandidates.filter(booking => {
|
|
366
381
|
const startAt = this.toBookingDateTime(booking.start_date, booking.start_time);
|
|
367
382
|
return startAt.isValid() && startAt.valueOf() === nextStartAt;
|
|
368
383
|
});
|
|
369
|
-
if (nextStartGroup.length === 0)
|
|
370
|
-
|
|
371
|
-
nextStartGroup.forEach((booking) => {
|
|
384
|
+
if (nextStartGroup.length === 0) return selectedBookings;
|
|
385
|
+
nextStartGroup.forEach(booking => {
|
|
372
386
|
booking.isNext = true;
|
|
373
387
|
});
|
|
374
388
|
return [...selectedBookings, ...nextStartGroup];
|
|
375
389
|
};
|
|
376
|
-
|
|
390
|
+
|
|
391
|
+
// active: current 处于 [startAt, endAt] 区间
|
|
392
|
+
const activeBookings = dayScopedBookings.filter(booking => {
|
|
377
393
|
const startAt = this.toBookingDateTime(booking.start_date, booking.start_time);
|
|
378
394
|
const endAt = this.toBookingDateTime(booking.end_date, booking.end_time);
|
|
379
|
-
if (!startAt.isValid() || !endAt.isValid())
|
|
380
|
-
return false;
|
|
395
|
+
if (!startAt.isValid() || !endAt.isValid()) return false;
|
|
381
396
|
return this.isSameOrAfter(current, startAt) && this.isSameOrBefore(current, endAt);
|
|
382
397
|
});
|
|
383
|
-
if (activeBookings.length > 0)
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
);
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
const upcoming = dayScopedBookings.filter(
|
|
398
|
+
if (activeBookings.length > 0) return appendNextStartGroupIfNeeded(activeBookings);
|
|
399
|
+
|
|
400
|
+
// timeout occupied: 占用中且超时
|
|
401
|
+
const timeoutOccupied = dayScopedBookings.filter(booking => booking.status === 'occupied' && booking.isTimeout);
|
|
402
|
+
if (timeoutOccupied.length > 0) return appendNextStartGroupIfNeeded(timeoutOccupied);
|
|
403
|
+
|
|
404
|
+
// upcoming: 从未来预约里只取最早那一组(同 start_time)
|
|
405
|
+
const upcoming = dayScopedBookings.filter(booking => {
|
|
391
406
|
const startAt = this.toBookingDateTime(booking.start_date, booking.start_time);
|
|
392
|
-
return startAt.isValid() && startAt.isAfter(current) && startAt.isSame(current,
|
|
407
|
+
return startAt.isValid() && startAt.isAfter(current) && startAt.isSame(current, 'day');
|
|
393
408
|
});
|
|
394
409
|
if (upcoming.length > 0) {
|
|
395
|
-
if (options.includeAllFuture)
|
|
396
|
-
return upcoming;
|
|
410
|
+
if (options.includeAllFuture) return upcoming;
|
|
397
411
|
const firstStartAt = this.toBookingDateTime(upcoming[0].start_date, upcoming[0].start_time).valueOf();
|
|
398
|
-
const upcomingStartGroup = upcoming.filter(
|
|
412
|
+
const upcomingStartGroup = upcoming.filter(booking => {
|
|
399
413
|
const startAt = this.toBookingDateTime(booking.start_date, booking.start_time);
|
|
400
414
|
return startAt.valueOf() === firstStartAt;
|
|
401
415
|
});
|
|
402
416
|
return appendNextStartGroupIfNeeded(upcomingStartGroup);
|
|
403
417
|
}
|
|
418
|
+
|
|
419
|
+
// fallback: 全部是过去且非 timeout 的场景,保留末条便于前端兜底展示
|
|
404
420
|
const fallback = dayScopedBookings[dayScopedBookings.length - 1];
|
|
405
421
|
return fallback ? [fallback] : [];
|
|
406
422
|
}
|
|
423
|
+
|
|
407
424
|
/**
|
|
408
425
|
* 计算 booking 在 current 时刻的执行进度百分比(0-100)。
|
|
409
426
|
* - startAt/endAt 非法或 totalMinutes <= 0 返回 0
|
|
410
427
|
* - current 早于 startAt 返回 0,晚于 endAt 返回 100
|
|
411
428
|
*/
|
|
412
429
|
calcProgressPercent(current, startAt, endAt) {
|
|
413
|
-
if (!startAt.isValid() || !endAt.isValid())
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
if (elapsedMinutes <= 0)
|
|
420
|
-
return 0;
|
|
421
|
-
if (elapsedMinutes >= totalMinutes)
|
|
422
|
-
return 100;
|
|
430
|
+
if (!startAt.isValid() || !endAt.isValid()) return 0;
|
|
431
|
+
const totalMinutes = endAt.diff(startAt, 'minute');
|
|
432
|
+
if (totalMinutes <= 0) return 0;
|
|
433
|
+
const elapsedMinutes = current.diff(startAt, 'minute');
|
|
434
|
+
if (elapsedMinutes <= 0) return 0;
|
|
435
|
+
if (elapsedMinutes >= totalMinutes) return 100;
|
|
423
436
|
return Math.floor(elapsedMinutes / totalMinutes * 100);
|
|
424
437
|
}
|
|
438
|
+
|
|
425
439
|
/**
|
|
426
440
|
* 标准化单条 booking 的分发入口:
|
|
427
441
|
* - 统一过滤 rejected / cancelled(视为没来过)
|
|
@@ -430,11 +444,10 @@ var SalesImpl = class extends import_BaseModule.BaseModule {
|
|
|
430
444
|
*/
|
|
431
445
|
normalizeMatchedBooking(current, deviceCurrent, booking, options = {}) {
|
|
432
446
|
const appointmentStatus = this.getBookingAppointmentStatus(booking);
|
|
433
|
-
if (appointmentStatus ===
|
|
434
|
-
return null;
|
|
447
|
+
if (appointmentStatus === 'rejected' || appointmentStatus === 'cancelled') return null;
|
|
435
448
|
const startAt = this.toBookingDateTime(booking.start_date, booking.start_time);
|
|
436
449
|
const endAt = this.toBookingDateTime(booking.end_date, booking.end_time);
|
|
437
|
-
if (appointmentStatus ===
|
|
450
|
+
if (appointmentStatus === 'completed' && options.includeCompleted) {
|
|
438
451
|
return this.buildCompletedBooking(current, booking, startAt, endAt);
|
|
439
452
|
}
|
|
440
453
|
if (current.isSame(deviceCurrent)) {
|
|
@@ -443,35 +456,35 @@ var SalesImpl = class extends import_BaseModule.BaseModule {
|
|
|
443
456
|
return this.buildSnapshotBooking(current, booking, appointmentStatus, startAt, endAt);
|
|
444
457
|
}
|
|
445
458
|
normalizeResourceBookings(current, deviceCurrent, bookingList, options = {}) {
|
|
446
|
-
const normalizedBookings = bookingList.map(
|
|
459
|
+
const normalizedBookings = bookingList.map(booking => this.normalizeMatchedBooking(current, deviceCurrent, booking, options)).filter(booking => Boolean(booking)).filter(booking => booking.status);
|
|
447
460
|
return this.sortMatchedBookings(normalizedBookings);
|
|
448
461
|
}
|
|
449
462
|
buildCompletedBooking(current, booking, startAt, endAt) {
|
|
450
|
-
if (!startAt.isValid() || !endAt.isValid())
|
|
451
|
-
return null;
|
|
463
|
+
if (!startAt.isValid() || !endAt.isValid()) return null;
|
|
452
464
|
if (current.isBefore(startAt)) {
|
|
453
465
|
return {
|
|
454
466
|
...booking,
|
|
455
|
-
status:
|
|
467
|
+
status: 'reserved',
|
|
456
468
|
isTimeout: false,
|
|
457
|
-
timeoutTime:
|
|
469
|
+
timeoutTime: undefined,
|
|
458
470
|
progressPercent: 0,
|
|
459
|
-
lateTime:
|
|
460
|
-
reserved_status:
|
|
461
|
-
remainingReserveTime: Math.max(startAt.diff(current,
|
|
471
|
+
lateTime: undefined,
|
|
472
|
+
reserved_status: 'not_arrived',
|
|
473
|
+
remainingReserveTime: Math.max(startAt.diff(current, 'minute'), 0)
|
|
462
474
|
};
|
|
463
475
|
}
|
|
464
476
|
return {
|
|
465
477
|
...booking,
|
|
466
|
-
status:
|
|
478
|
+
status: 'occupied',
|
|
467
479
|
isTimeout: false,
|
|
468
|
-
timeoutTime:
|
|
480
|
+
timeoutTime: undefined,
|
|
469
481
|
progressPercent: this.isSameOrAfter(current, endAt) ? 100 : this.calcProgressPercent(current, startAt, endAt),
|
|
470
|
-
lateTime:
|
|
471
|
-
reserved_status:
|
|
472
|
-
remainingReserveTime:
|
|
482
|
+
lateTime: undefined,
|
|
483
|
+
reserved_status: undefined,
|
|
484
|
+
remainingReserveTime: undefined
|
|
473
485
|
};
|
|
474
486
|
}
|
|
487
|
+
|
|
475
488
|
/**
|
|
476
489
|
* 实时模式(current === deviceCurrent):信任 appointment_status
|
|
477
490
|
* - 过滤 new / completed(实时视角下视为非活跃数据)
|
|
@@ -480,22 +493,21 @@ var SalesImpl = class extends import_BaseModule.BaseModule {
|
|
|
480
493
|
* - reserved 且 current < startAt 标记 not_arrived,否则标记 late
|
|
481
494
|
*/
|
|
482
495
|
buildRealtimeBooking(current, booking, appointmentStatus, startAt, endAt) {
|
|
483
|
-
if (appointmentStatus ===
|
|
484
|
-
return null;
|
|
496
|
+
if (appointmentStatus === 'new' || appointmentStatus === 'completed') return null;
|
|
485
497
|
const bookingStatus = this.getBookingStatus(appointmentStatus);
|
|
486
|
-
const isTimeout = bookingStatus ===
|
|
487
|
-
const timeoutTime = isTimeout ? current.diff(endAt,
|
|
488
|
-
const progressPercent = bookingStatus ===
|
|
498
|
+
const isTimeout = bookingStatus === 'occupied' && endAt.isValid() && current.isAfter(endAt);
|
|
499
|
+
const timeoutTime = isTimeout ? current.diff(endAt, 'minute') : undefined;
|
|
500
|
+
const progressPercent = bookingStatus === 'occupied' ? this.calcProgressPercent(current, startAt, endAt) : 0;
|
|
489
501
|
let reservedStatus;
|
|
490
502
|
let lateTime;
|
|
491
503
|
let remainingReserveTime;
|
|
492
|
-
if (bookingStatus ===
|
|
504
|
+
if (bookingStatus === 'reserved' && startAt.isValid()) {
|
|
493
505
|
if (current.isBefore(startAt)) {
|
|
494
|
-
reservedStatus =
|
|
495
|
-
remainingReserveTime = startAt.diff(current,
|
|
506
|
+
reservedStatus = 'not_arrived';
|
|
507
|
+
remainingReserveTime = startAt.diff(current, 'minute');
|
|
496
508
|
} else {
|
|
497
|
-
reservedStatus =
|
|
498
|
-
lateTime = Math.max(current.diff(startAt,
|
|
509
|
+
reservedStatus = 'late';
|
|
510
|
+
lateTime = Math.max(current.diff(startAt, 'minute'), 0);
|
|
499
511
|
}
|
|
500
512
|
}
|
|
501
513
|
return {
|
|
@@ -509,6 +521,7 @@ var SalesImpl = class extends import_BaseModule.BaseModule {
|
|
|
509
521
|
remainingReserveTime
|
|
510
522
|
};
|
|
511
523
|
}
|
|
524
|
+
|
|
512
525
|
/**
|
|
513
526
|
* 快照模式(current !== deviceCurrent,过去或未来):忽略 appointment_status
|
|
514
527
|
* 的实时态差异,仅按 booking 时间窗判定,为 current 提供时间点快照视图。
|
|
@@ -524,72 +537,77 @@ var SalesImpl = class extends import_BaseModule.BaseModule {
|
|
|
524
537
|
* 快照模式下 isTimeout 恒为 false,lateTime / timeoutTime 始终置空。
|
|
525
538
|
*/
|
|
526
539
|
buildSnapshotBooking(current, booking, appointmentStatus, startAt, endAt) {
|
|
527
|
-
if (appointmentStatus ===
|
|
528
|
-
|
|
529
|
-
if (appointmentStatus === "locked") {
|
|
540
|
+
if (appointmentStatus === 'new') return null;
|
|
541
|
+
if (appointmentStatus === 'locked') {
|
|
530
542
|
return {
|
|
531
543
|
...booking,
|
|
532
|
-
status:
|
|
544
|
+
status: 'locked',
|
|
533
545
|
isTimeout: false,
|
|
534
|
-
timeoutTime:
|
|
546
|
+
timeoutTime: undefined,
|
|
535
547
|
progressPercent: 0,
|
|
536
|
-
lateTime:
|
|
537
|
-
reserved_status:
|
|
538
|
-
remainingReserveTime:
|
|
548
|
+
lateTime: undefined,
|
|
549
|
+
reserved_status: undefined,
|
|
550
|
+
remainingReserveTime: undefined
|
|
539
551
|
};
|
|
540
552
|
}
|
|
541
|
-
if (!startAt.isValid() || !endAt.isValid())
|
|
542
|
-
|
|
543
|
-
if (current.isAfter(endAt))
|
|
544
|
-
return null;
|
|
553
|
+
if (!startAt.isValid() || !endAt.isValid()) return null;
|
|
554
|
+
if (current.isAfter(endAt)) return null;
|
|
545
555
|
if (this.isSameOrAfter(current, startAt) && this.isSameOrBefore(current, endAt)) {
|
|
546
556
|
return {
|
|
547
557
|
...booking,
|
|
548
|
-
status:
|
|
558
|
+
status: 'occupied',
|
|
549
559
|
isTimeout: false,
|
|
550
|
-
timeoutTime:
|
|
560
|
+
timeoutTime: undefined,
|
|
551
561
|
progressPercent: this.calcProgressPercent(current, startAt, endAt),
|
|
552
|
-
lateTime:
|
|
553
|
-
reserved_status:
|
|
554
|
-
remainingReserveTime:
|
|
562
|
+
lateTime: undefined,
|
|
563
|
+
reserved_status: undefined,
|
|
564
|
+
remainingReserveTime: undefined
|
|
555
565
|
};
|
|
556
566
|
}
|
|
557
567
|
return {
|
|
558
568
|
...booking,
|
|
559
|
-
status:
|
|
569
|
+
status: 'reserved',
|
|
560
570
|
isTimeout: false,
|
|
561
|
-
timeoutTime:
|
|
571
|
+
timeoutTime: undefined,
|
|
562
572
|
progressPercent: 0,
|
|
563
|
-
lateTime:
|
|
564
|
-
reserved_status:
|
|
565
|
-
remainingReserveTime: Math.max(startAt.diff(current,
|
|
573
|
+
lateTime: undefined,
|
|
574
|
+
reserved_status: 'not_arrived',
|
|
575
|
+
remainingReserveTime: Math.max(startAt.diff(current, 'minute'), 0)
|
|
566
576
|
};
|
|
567
577
|
}
|
|
568
578
|
async getResourceBookingList(currentTime, bookingList = [], deviceTime = currentTime) {
|
|
569
|
-
|
|
570
|
-
const current = (0,
|
|
571
|
-
if (!current.isValid())
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
const resourceResponse = await this.request.get(
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
579
|
+
// currentTime 约定为完整 datetime;非法值直接返回空,避免误判
|
|
580
|
+
const current = (0, _dayjs.default)(currentTime);
|
|
581
|
+
if (!current.isValid()) return [];
|
|
582
|
+
const deviceCurrent = (0, _dayjs.default)(deviceTime);
|
|
583
|
+
if (!deviceCurrent.isValid()) return [];
|
|
584
|
+
|
|
585
|
+
// 资源列表来源:Server 路由 /shop/form/resource/page
|
|
586
|
+
const resourceResponse = await this.request.get('/shop/form/resource/page', {
|
|
587
|
+
skip: 1,
|
|
588
|
+
num: 999
|
|
589
|
+
},
|
|
590
|
+
// @ts-ignore
|
|
591
|
+
{
|
|
592
|
+
osServer: true,
|
|
593
|
+
isShopApi: true
|
|
594
|
+
});
|
|
595
|
+
const resourceList = resourceResponse?.data?.list ?? [];
|
|
596
|
+
if (!Array.isArray(resourceList) || resourceList.length === 0) return [];
|
|
597
|
+
|
|
598
|
+
// 全局先做 booking 标准化 + start_time 升序,后续映射直接复用
|
|
585
599
|
const normalizedBookings = this.normalizeResourceBookings(current, deviceCurrent, bookingList);
|
|
600
|
+
|
|
601
|
+
// 建立 resourceId -> bookings[] 倒排索引,加速资源匹配
|
|
586
602
|
const rawBookingMap = this.groupBookingsByResource(bookingList);
|
|
587
603
|
const bookingMap = this.groupBookingsByResource(normalizedBookings);
|
|
588
|
-
|
|
604
|
+
|
|
605
|
+
// 输出:每个资源都返回;bookings 可能是 0 / 1 / 多条(并发场景)
|
|
606
|
+
const list = resourceList.map(resource => {
|
|
589
607
|
const resourceId = this.getResourceId(resource);
|
|
590
608
|
const rawMatchedBookings = rawBookingMap.get(String(resourceId)) || [];
|
|
591
609
|
const matchedBookings = bookingMap.get(String(resourceId)) || [];
|
|
592
|
-
const statsBookings = rawMatchedBookings.filter(
|
|
610
|
+
const statsBookings = rawMatchedBookings.filter(booking => this.isBookingIncludedInStats(booking));
|
|
593
611
|
const orderCount = this.countBookingOrders(statsBookings);
|
|
594
612
|
const bookingCount = statsBookings.length;
|
|
595
613
|
const compeleteBookingCount = this.countCompletedBookings(statsBookings);
|
|
@@ -604,81 +622,72 @@ var SalesImpl = class extends import_BaseModule.BaseModule {
|
|
|
604
622
|
};
|
|
605
623
|
});
|
|
606
624
|
this.logger.addLog({
|
|
607
|
-
type:
|
|
608
|
-
title:
|
|
625
|
+
type: 'info',
|
|
626
|
+
title: 'getResourceBookingList',
|
|
609
627
|
metadata: {
|
|
610
628
|
currentTime,
|
|
611
|
-
bookingList: bookingList
|
|
629
|
+
bookingList: bookingList?.length,
|
|
612
630
|
deviceTime,
|
|
613
|
-
list: list
|
|
631
|
+
list: list?.length
|
|
614
632
|
}
|
|
615
633
|
});
|
|
616
634
|
return list;
|
|
617
635
|
}
|
|
618
636
|
async getResourceBookingItem(params) {
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
const
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
);
|
|
632
|
-
const resourceList =
|
|
633
|
-
if (!Array.isArray(resourceList) || resourceList.length === 0)
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
(resource) => String(this.getResourceId(resource)) === String(params.resourceId)
|
|
637
|
-
);
|
|
638
|
-
if (!targetResource)
|
|
639
|
-
return null;
|
|
637
|
+
const current = (0, _dayjs.default)(params.currentTime);
|
|
638
|
+
if (!current.isValid()) return null;
|
|
639
|
+
const deviceCurrent = (0, _dayjs.default)(params.deviceTime || params.currentTime);
|
|
640
|
+
if (!deviceCurrent.isValid()) return null;
|
|
641
|
+
const resourceResponse = await this.request.get('/shop/form/resource/page', {
|
|
642
|
+
skip: 1,
|
|
643
|
+
num: 999
|
|
644
|
+
},
|
|
645
|
+
// @ts-ignore
|
|
646
|
+
{
|
|
647
|
+
osServer: true,
|
|
648
|
+
isShopApi: true
|
|
649
|
+
});
|
|
650
|
+
const resourceList = resourceResponse?.data?.list ?? [];
|
|
651
|
+
if (!Array.isArray(resourceList) || resourceList.length === 0) return null;
|
|
652
|
+
const targetResource = resourceList.find(resource => String(this.getResourceId(resource)) === String(params.resourceId));
|
|
653
|
+
if (!targetResource) return null;
|
|
640
654
|
const resourceId = this.getResourceId(targetResource);
|
|
641
|
-
const matchedBookingList = params.bookingList.filter(
|
|
642
|
-
|
|
643
|
-
);
|
|
644
|
-
const
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
);
|
|
648
|
-
const
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
{ includeAllFuture: true }
|
|
653
|
-
);
|
|
654
|
-
const completedBookings = completedBookingList.map((booking) => {
|
|
655
|
+
const matchedBookingList = params.bookingList.filter(booking => this.isBookingMatchedResource(booking, resourceId));
|
|
656
|
+
const statsBookingList = matchedBookingList.filter(booking => this.isBookingIncludedInStats(booking));
|
|
657
|
+
const completedBookingList = matchedBookingList.filter(booking => this.getBookingAppointmentStatus(booking) === 'completed');
|
|
658
|
+
// const unpaidBookingList = matchedBookingList.filter(
|
|
659
|
+
// (booking) => this.getBookingOrderPaymentStatus(booking) === 'unpaid' || this.getBookingOrderPaymentStatus(booking) === 'payment_processing',
|
|
660
|
+
// );
|
|
661
|
+
const unpaidBookingList = matchedBookingList.filter(booking => this.isUnpaidBooking(booking));
|
|
662
|
+
const bookings = this.pickBookingsForCurrentPoint(current, this.normalizeResourceBookings(current, deviceCurrent, matchedBookingList), {
|
|
663
|
+
includeAllFuture: true
|
|
664
|
+
});
|
|
665
|
+
const completedBookings = completedBookingList.map(booking => {
|
|
655
666
|
return {
|
|
656
667
|
...booking,
|
|
657
|
-
status:
|
|
668
|
+
status: 'compelete',
|
|
658
669
|
isTimeout: false,
|
|
659
|
-
timeoutTime:
|
|
670
|
+
timeoutTime: undefined,
|
|
660
671
|
progressPercent: 100,
|
|
661
|
-
lateTime:
|
|
662
|
-
reserved_status:
|
|
663
|
-
remainingReserveTime:
|
|
672
|
+
lateTime: undefined,
|
|
673
|
+
reserved_status: undefined,
|
|
674
|
+
remainingReserveTime: undefined
|
|
664
675
|
};
|
|
665
676
|
});
|
|
666
|
-
const unpaidBookings = this.pickBookingsForCurrentPoint(
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
{ includeAllFuture: true }
|
|
670
|
-
);
|
|
677
|
+
const unpaidBookings = this.pickBookingsForCurrentPoint(current, this.normalizeResourceBookings(current, deviceCurrent, unpaidBookingList), {
|
|
678
|
+
includeAllFuture: true
|
|
679
|
+
});
|
|
671
680
|
const orderCount = this.countBookingOrders(statsBookingList);
|
|
672
681
|
const bookingCount = statsBookingList.length;
|
|
673
682
|
const compeleteBookingCount = this.countCompletedBookings(statsBookingList);
|
|
674
683
|
this.logger.addLog({
|
|
675
|
-
type:
|
|
676
|
-
title:
|
|
684
|
+
type: 'info',
|
|
685
|
+
title: 'getResourceBookingItem',
|
|
677
686
|
metadata: {
|
|
678
687
|
currentTime: params.currentTime,
|
|
679
688
|
deviceTime: params.deviceTime || params.currentTime,
|
|
680
689
|
resourceId,
|
|
681
|
-
bookingList:
|
|
690
|
+
bookingList: params.bookingList?.length,
|
|
682
691
|
bookings: bookings.length,
|
|
683
692
|
completedBookings: completedBookings.length,
|
|
684
693
|
unpaidBookings: unpaidBookings.length,
|
|
@@ -698,11 +707,6 @@ var SalesImpl = class extends import_BaseModule.BaseModule {
|
|
|
698
707
|
unpaidBookings
|
|
699
708
|
};
|
|
700
709
|
}
|
|
701
|
-
}
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
0 && (module.exports = {
|
|
705
|
-
Sales,
|
|
706
|
-
SalesImpl,
|
|
707
|
-
...require("./types")
|
|
708
|
-
});
|
|
710
|
+
}
|
|
711
|
+
exports.Sales = exports.SalesImpl = SalesImpl;
|
|
712
|
+
var _default = exports.default = SalesImpl;
|