@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
|
@@ -1,1069 +1,2222 @@
|
|
|
1
|
-
function
|
|
2
|
-
function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? ownKeys(Object(t), !0).forEach(function (r) { _defineProperty(e, r, t[r]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function (r) { Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)); }); } return e; }
|
|
3
|
-
function _defineProperty(obj, key, value) { key = _toPropertyKey(key); if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }
|
|
4
|
-
function _toPropertyKey(t) { var i = _toPrimitive(t, "string"); return "symbol" == _typeof(i) ? i : String(i); }
|
|
5
|
-
function _toPrimitive(t, r) { if ("object" != _typeof(t) || !t) return t; var e = t[Symbol.toPrimitive]; if (void 0 !== e) { var i = e.call(t, r || "default"); if ("object" != _typeof(i)) return i; throw new TypeError("@@toPrimitive must return a primitive value."); } return ("string" === r ? String : Number)(t); }
|
|
1
|
+
function _typeof(o) { "@babel/helpers - typeof"; return _typeof = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function (o) { return typeof o; } : function (o) { return o && "function" == typeof Symbol && o.constructor === Symbol && o !== Symbol.prototype ? "symbol" : typeof o; }, _typeof(o); }
|
|
6
2
|
function _toConsumableArray(arr) { return _arrayWithoutHoles(arr) || _iterableToArray(arr) || _unsupportedIterableToArray(arr) || _nonIterableSpread(); }
|
|
7
3
|
function _nonIterableSpread() { throw new TypeError("Invalid attempt to spread non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); }
|
|
4
|
+
function _unsupportedIterableToArray(o, minLen) { if (!o) return; if (typeof o === "string") return _arrayLikeToArray(o, minLen); var n = Object.prototype.toString.call(o).slice(8, -1); if (n === "Object" && o.constructor) n = o.constructor.name; if (n === "Map" || n === "Set") return Array.from(o); if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _arrayLikeToArray(o, minLen); }
|
|
8
5
|
function _iterableToArray(iter) { if (typeof Symbol !== "undefined" && iter[Symbol.iterator] != null || iter["@@iterator"] != null) return Array.from(iter); }
|
|
9
6
|
function _arrayWithoutHoles(arr) { if (Array.isArray(arr)) return _arrayLikeToArray(arr); }
|
|
10
|
-
function _createForOfIteratorHelper(o, allowArrayLike) { var it = typeof Symbol !== "undefined" && o[Symbol.iterator] || o["@@iterator"]; if (!it) { if (Array.isArray(o) || (it = _unsupportedIterableToArray(o)) || allowArrayLike && o && typeof o.length === "number") { if (it) o = it; var i = 0; var F = function F() {}; return { s: F, n: function n() { if (i >= o.length) return { done: true }; return { done: false, value: o[i++] }; }, e: function e(_e) { throw _e; }, f: F }; } throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); } var normalCompletion = true, didErr = false, err; return { s: function s() { it = it.call(o); }, n: function n() { var step = it.next(); normalCompletion = step.done; return step; }, e: function e(_e2) { didErr = true; err = _e2; }, f: function f() { try { if (!normalCompletion && it.return != null) it.return(); } finally { if (didErr) throw err; } } }; }
|
|
11
|
-
function _unsupportedIterableToArray(o, minLen) { if (!o) return; if (typeof o === "string") return _arrayLikeToArray(o, minLen); var n = Object.prototype.toString.call(o).slice(8, -1); if (n === "Object" && o.constructor) n = o.constructor.name; if (n === "Map" || n === "Set") return Array.from(o); if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _arrayLikeToArray(o, minLen); }
|
|
12
7
|
function _arrayLikeToArray(arr, len) { if (len == null || len > arr.length) len = arr.length; for (var i = 0, arr2 = new Array(len); i < len; i++) arr2[i] = arr[i]; return arr2; }
|
|
13
|
-
function
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
productIds: [],
|
|
25
|
-
resourceIds: [],
|
|
26
|
-
assignments: []
|
|
8
|
+
function ownKeys(e, r) { var t = Object.keys(e); if (Object.getOwnPropertySymbols) { var o = Object.getOwnPropertySymbols(e); r && (o = o.filter(function (r) { return Object.getOwnPropertyDescriptor(e, r).enumerable; })), t.push.apply(t, o); } return t; }
|
|
9
|
+
function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? ownKeys(Object(t), !0).forEach(function (r) { _defineProperty(e, r, t[r]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function (r) { Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)); }); } return e; }
|
|
10
|
+
function _defineProperty(obj, key, value) { key = _toPropertyKey(key); if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }
|
|
11
|
+
function _toPropertyKey(t) { var i = _toPrimitive(t, "string"); return "symbol" == _typeof(i) ? i : String(i); }
|
|
12
|
+
function _toPrimitive(t, r) { if ("object" != _typeof(t) || !t) return t; var e = t[Symbol.toPrimitive]; if (void 0 !== e) { var i = e.call(t, r || "default"); if ("object" != _typeof(i)) return i; throw new TypeError("@@toPrimitive must return a primitive value."); } return ("string" === r ? String : Number)(t); }
|
|
13
|
+
import { AvailabilityError } from "./types";
|
|
14
|
+
import { addLocalCalendarDays, addLocalHours, addLocalMinutes, captureRuntimeOffsetMinutes, ceilLocalScalarToTenMinutes, dateOrdinalFromLocalDate, extractFixedOffsetMinutes, formatLocalDate, formatLocalDateTime, formatLocalTime, localDateTimeToScalar, localDateToScalar, localDateToScalarStart, nextDate as getNextLocalDate, parseLocalDate, parseLocalDateTime, weekdayFromLocalDate } from "./localClock";
|
|
15
|
+
var DEFAULT_SLOT_STEP_MINUTES = 30;
|
|
16
|
+
var DEFAULT_BUSINESS_HOURS = {
|
|
17
|
+
startTime: '00:00',
|
|
18
|
+
endTime: '00:00'
|
|
27
19
|
};
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
return {
|
|
31
|
-
products: Array.isArray(input.products) ? input.products : [],
|
|
32
|
-
resources: Array.isArray(input.resources) ? input.resources : [],
|
|
33
|
-
externalEvents: Array.isArray(input.externalEvents) ? input.externalEvents : [],
|
|
34
|
-
slotDurationMinutes: input.slotDurationMinutes || DEFAULT_RESOURCE_PLANNER_CONTEXT.slotDurationMinutes,
|
|
35
|
-
businessStartTime: input.businessStartTime || DEFAULT_RESOURCE_PLANNER_CONTEXT.businessStartTime,
|
|
36
|
-
businessEndTime: input.businessEndTime || DEFAULT_RESOURCE_PLANNER_CONTEXT.businessEndTime
|
|
37
|
-
};
|
|
20
|
+
function availabilityError(code, message, details) {
|
|
21
|
+
return new AvailabilityError(code, message, details);
|
|
38
22
|
}
|
|
39
23
|
function sameId(left, right) {
|
|
24
|
+
if (left === undefined || left === null || right === undefined || right === null) return false;
|
|
40
25
|
return String(left) === String(right);
|
|
41
26
|
}
|
|
42
|
-
function
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
function
|
|
46
|
-
|
|
27
|
+
function uniqById() {
|
|
28
|
+
var values = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : [];
|
|
29
|
+
var map = new Map();
|
|
30
|
+
values.forEach(function (value) {
|
|
31
|
+
map.set(String(value), value);
|
|
32
|
+
});
|
|
33
|
+
return Array.from(map.values());
|
|
47
34
|
}
|
|
48
|
-
function
|
|
49
|
-
if (
|
|
50
|
-
|
|
35
|
+
function normalizePartySize(partySize) {
|
|
36
|
+
if (partySize === undefined || partySize === null) return undefined;
|
|
37
|
+
var normalized = Number(partySize);
|
|
38
|
+
if (!Number.isInteger(normalized) || normalized <= 0) {
|
|
39
|
+
throw availabilityError('INVALID_TARGET_FILTER', 'partySize 必须是正整数', {
|
|
40
|
+
partySize: partySize
|
|
41
|
+
});
|
|
42
|
+
}
|
|
43
|
+
return normalized;
|
|
51
44
|
}
|
|
52
|
-
function
|
|
53
|
-
|
|
45
|
+
function normalizeBookingCount(bookingCount) {
|
|
46
|
+
if (bookingCount === undefined || bookingCount === null) return 1;
|
|
47
|
+
var normalized = Number(bookingCount);
|
|
48
|
+
if (!Number.isInteger(normalized) || normalized <= 0) {
|
|
49
|
+
throw availabilityError('INVALID_TARGET_FILTER', 'bookingCount 必须是正整数', {
|
|
50
|
+
bookingCount: bookingCount
|
|
51
|
+
});
|
|
52
|
+
}
|
|
53
|
+
return normalized;
|
|
54
54
|
}
|
|
55
|
-
function
|
|
56
|
-
|
|
55
|
+
function resolveRequestedDurationMinutes(product, request) {
|
|
56
|
+
var _request$durationMinu;
|
|
57
|
+
var configured = Math.max(1, Number(product.durationMinutes || 30) || 30);
|
|
58
|
+
if (product.kind !== 'duration') return configured;
|
|
59
|
+
var override = (_request$durationMinu = request.durationMinutesByProductId) === null || _request$durationMinu === void 0 ? void 0 : _request$durationMinu[String(product.id)];
|
|
60
|
+
var normalized = Number(override);
|
|
61
|
+
return Number.isInteger(normalized) && normalized > 0 ? normalized : configured;
|
|
57
62
|
}
|
|
58
|
-
function
|
|
59
|
-
|
|
63
|
+
function resolveRequestedDurationStartTimes(product, request) {
|
|
64
|
+
var _request$durationStar;
|
|
65
|
+
if (product.kind !== 'duration') return [];
|
|
66
|
+
var values = (_request$durationStar = request.durationStartTimesByProductId) === null || _request$durationStar === void 0 ? void 0 : _request$durationStar[String(product.id)];
|
|
67
|
+
if (!Array.isArray(values)) return [];
|
|
68
|
+
return Array.from(new Set(values.filter(function (value) {
|
|
69
|
+
return typeof value === 'string' && /^([01]\d|2[0-3]):[0-5]\d$/.test(value);
|
|
70
|
+
})));
|
|
60
71
|
}
|
|
61
|
-
function
|
|
62
|
-
|
|
72
|
+
function toDateTime(value, _timezoneName) {
|
|
73
|
+
try {
|
|
74
|
+
return localDateTimeToScalar(parseLocalDateTime(value));
|
|
75
|
+
} catch (_error) {
|
|
76
|
+
return Number.NaN;
|
|
77
|
+
}
|
|
63
78
|
}
|
|
64
|
-
function
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
result.push(cursor.format('YYYY-MM-DD'));
|
|
70
|
-
cursor = cursor.add(1, 'day');
|
|
79
|
+
function toDateStart(value, _timezoneName) {
|
|
80
|
+
try {
|
|
81
|
+
return localDateToScalarStart(value);
|
|
82
|
+
} catch (_error) {
|
|
83
|
+
return Number.NaN;
|
|
71
84
|
}
|
|
72
|
-
return result;
|
|
73
|
-
}
|
|
74
|
-
function getQueryProducts(products, query) {
|
|
75
|
-
var _query$productIds;
|
|
76
|
-
if (!((_query$productIds = query.productIds) !== null && _query$productIds !== void 0 && _query$productIds.length)) return products;
|
|
77
|
-
return products.filter(function (product) {
|
|
78
|
-
return query.productIds.some(function (id) {
|
|
79
|
-
return sameId(id, product.id);
|
|
80
|
-
});
|
|
81
|
-
});
|
|
82
85
|
}
|
|
83
|
-
function
|
|
84
|
-
var
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
});
|
|
90
|
-
});
|
|
86
|
+
function toDateEnd(value, _timezoneName) {
|
|
87
|
+
var start = toDateStart(value);
|
|
88
|
+
return Number.isFinite(start) ? addLocalCalendarDays(start, 1) - 1 : Number.NaN;
|
|
89
|
+
}
|
|
90
|
+
function toDateTimeInDate(date, time, _timezoneName) {
|
|
91
|
+
return toDateTime("".concat(date, "T").concat(time.length === 5 ? "".concat(time, ":00") : time));
|
|
91
92
|
}
|
|
92
|
-
function
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
93
|
+
function addMinutesZoned(value, minutes, _timezoneName) {
|
|
94
|
+
return addLocalMinutes(value, minutes);
|
|
95
|
+
}
|
|
96
|
+
function ceilToTenMinutes(value, _timezoneName) {
|
|
97
|
+
return ceilLocalScalarToTenMinutes(value);
|
|
98
|
+
}
|
|
99
|
+
function floorToMinute(value) {
|
|
100
|
+
return Math.floor(value / 60000) * 60000;
|
|
101
|
+
}
|
|
102
|
+
function nextDate(value, _timezoneName) {
|
|
103
|
+
return getNextLocalDate(value);
|
|
104
|
+
}
|
|
105
|
+
function formatDateTime(value, _timezoneName, fixedOffsetMinutes) {
|
|
106
|
+
return formatLocalDateTime(value, fixedOffsetMinutes !== null && fixedOffsetMinutes !== void 0 ? fixedOffsetMinutes : captureRuntimeOffsetMinutes());
|
|
107
|
+
}
|
|
108
|
+
function formatDate(value, _timezoneName) {
|
|
109
|
+
return formatLocalDate(value);
|
|
110
|
+
}
|
|
111
|
+
function formatTime(value, _timezoneName) {
|
|
112
|
+
return formatLocalTime(value, false);
|
|
113
|
+
}
|
|
114
|
+
function normalizeAbsoluteRange(range, _timezoneName) {
|
|
115
|
+
var start = toDateTime(range.startAt);
|
|
116
|
+
var end = toDateTime(range.endAt);
|
|
117
|
+
if (!Number.isFinite(start) || !Number.isFinite(end) || end <= start) return null;
|
|
118
|
+
return {
|
|
119
|
+
start: start,
|
|
120
|
+
end: end
|
|
121
|
+
};
|
|
122
|
+
}
|
|
123
|
+
function getCandidateBookingRange(candidate) {
|
|
124
|
+
return {
|
|
125
|
+
// Runtime fallback keeps older caller-created candidates fail-safe during migration.
|
|
126
|
+
startAt: candidate.bookingStartAt || candidate.startAt,
|
|
127
|
+
endAt: candidate.bookingEndAt || candidate.endAt
|
|
128
|
+
};
|
|
129
|
+
}
|
|
130
|
+
function getCandidateRangeCacheKey(candidate) {
|
|
131
|
+
var bookingRange = getCandidateBookingRange(candidate);
|
|
132
|
+
return "".concat(candidate.key, ":").concat(bookingRange.startAt, ":").concat(bookingRange.endAt);
|
|
133
|
+
}
|
|
134
|
+
function getProductTimeRangeIdentity(range) {
|
|
135
|
+
var bookingRange = getCandidateBookingRange(range);
|
|
136
|
+
return [String(range.productId), range.startAt, range.endAt, bookingRange.startAt, bookingRange.endAt].join(':');
|
|
137
|
+
}
|
|
138
|
+
function toIndexedRange(range) {
|
|
139
|
+
return _objectSpread(_objectSpread({}, range), {}, {
|
|
140
|
+
startMs: range.start,
|
|
141
|
+
endMs: range.end
|
|
98
142
|
});
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
if (
|
|
107
|
-
return
|
|
143
|
+
}
|
|
144
|
+
function normalizeNonNegativeNumber(value) {
|
|
145
|
+
var normalized = Number(value);
|
|
146
|
+
if (!Number.isFinite(normalized) || normalized < 0) return null;
|
|
147
|
+
return normalized;
|
|
148
|
+
}
|
|
149
|
+
function resolveCutOffPolicyIssue(policy) {
|
|
150
|
+
if (policy.type === 'invalid') {
|
|
151
|
+
return policy.reason || 'invalid_cutoff_policy';
|
|
108
152
|
}
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
var intervals = [];
|
|
115
|
-
var _iterator = _createForOfIteratorHelper(selectedProducts),
|
|
116
|
-
_step;
|
|
117
|
-
try {
|
|
118
|
-
for (_iterator.s(); !(_step = _iterator.n()).done;) {
|
|
119
|
-
var product = _step.value;
|
|
120
|
-
var quantity = Math.max(1, Number(product.quantity || 1));
|
|
121
|
-
var capacityRequired = Math.max(1, Number(query.pax || product.capacityRequired || 1));
|
|
122
|
-
var requirements = product.resourceRequirements || [];
|
|
123
|
-
if (product.kind === 'session' || product.kind === 'venue_slot') {
|
|
124
|
-
var schedules = product.schedule || [];
|
|
125
|
-
var _iterator2 = _createForOfIteratorHelper(schedules),
|
|
126
|
-
_step2;
|
|
127
|
-
try {
|
|
128
|
-
for (_iterator2.s(); !(_step2 = _iterator2.n()).done;) {
|
|
129
|
-
var schedule = _step2.value;
|
|
130
|
-
var scheduleDate = dateOf(schedule.start_at);
|
|
131
|
-
if (dates.length > 0 && !dates.includes(scheduleDate)) continue;
|
|
132
|
-
var start = dayjs(schedule.start_at);
|
|
133
|
-
var end = dayjs(schedule.end_at);
|
|
134
|
-
intervals.push({
|
|
135
|
-
id: "".concat(product.id, ":").concat(start.toISOString(), ":").concat(end.toISOString()),
|
|
136
|
-
productId: product.id,
|
|
137
|
-
source: product.kind,
|
|
138
|
-
date: dateOf(schedule.start_at),
|
|
139
|
-
start_at: start.toISOString(),
|
|
140
|
-
end_at: end.toISOString(),
|
|
141
|
-
durationMinutes: Math.max(1, end.diff(start, 'minute')),
|
|
142
|
-
quantity: quantity,
|
|
143
|
-
capacityRequired: capacityRequired,
|
|
144
|
-
resourceRequirements: requirements
|
|
145
|
-
});
|
|
146
|
-
}
|
|
147
|
-
} catch (err) {
|
|
148
|
-
_iterator2.e(err);
|
|
149
|
-
} finally {
|
|
150
|
-
_iterator2.f();
|
|
151
|
-
}
|
|
152
|
-
continue;
|
|
153
|
-
}
|
|
154
|
-
if (product.kind === 'duration') {
|
|
155
|
-
var duration = Math.max(1, Number(product.durationMinutes || 30));
|
|
156
|
-
var _iterator3 = _createForOfIteratorHelper(dates),
|
|
157
|
-
_step3;
|
|
158
|
-
try {
|
|
159
|
-
for (_iterator3.s(); !(_step3 = _iterator3.n()).done;) {
|
|
160
|
-
var currentDate = _step3.value;
|
|
161
|
-
var _start = query.startTime ? toDateTime(currentDate, query.startTime) : toDateTime(currentDate, (context === null || context === void 0 ? void 0 : context.businessStartTime) || DEFAULT_RESOURCE_PLANNER_CONTEXT.businessStartTime);
|
|
162
|
-
var _end = query.startTime && query.endTime ? toDateTime(currentDate, query.endTime) : query.startTime ? _start.add(duration, 'minute') : toDateTime(currentDate, (context === null || context === void 0 ? void 0 : context.businessEndTime) || DEFAULT_RESOURCE_PLANNER_CONTEXT.businessEndTime);
|
|
163
|
-
if (!_end.isAfter(_start)) _end = _end.add(1, 'day');
|
|
164
|
-
intervals.push({
|
|
165
|
-
id: "".concat(product.id, ":").concat(currentDate, ":").concat(query.startTime || 'range'),
|
|
166
|
-
productId: product.id,
|
|
167
|
-
source: 'duration',
|
|
168
|
-
date: currentDate,
|
|
169
|
-
start_at: _start.toISOString(),
|
|
170
|
-
end_at: _end.toISOString(),
|
|
171
|
-
durationMinutes: duration,
|
|
172
|
-
quantity: quantity,
|
|
173
|
-
capacityRequired: capacityRequired,
|
|
174
|
-
resourceRequirements: requirements
|
|
175
|
-
});
|
|
176
|
-
}
|
|
177
|
-
} catch (err) {
|
|
178
|
-
_iterator3.e(err);
|
|
179
|
-
} finally {
|
|
180
|
-
_iterator3.f();
|
|
181
|
-
}
|
|
182
|
-
}
|
|
183
|
-
}
|
|
184
|
-
} catch (err) {
|
|
185
|
-
_iterator.e(err);
|
|
186
|
-
} finally {
|
|
187
|
-
_iterator.f();
|
|
153
|
+
if ('futureDay' in policy && policy.futureDay !== undefined && normalizeNonNegativeNumber(policy.futureDay) === null) {
|
|
154
|
+
return 'invalid_future_day';
|
|
155
|
+
}
|
|
156
|
+
if (policy.type === 'ongoing' && policy.mode === 'after_start' && normalizeNonNegativeNumber(policy.graceMinutes) === null) {
|
|
157
|
+
return 'invalid_grace_minutes';
|
|
188
158
|
}
|
|
189
|
-
|
|
159
|
+
if (policy.type === 'advance') {
|
|
160
|
+
if (normalizeNonNegativeNumber(policy.unit) === null) return 'invalid_advance_unit';
|
|
161
|
+
if (!['days', 'hours', 'minutes'].includes(policy.unitType)) return 'invalid_advance_unit_type';
|
|
162
|
+
}
|
|
163
|
+
return null;
|
|
190
164
|
}
|
|
191
|
-
function
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
var
|
|
202
|
-
|
|
203
|
-
eventsByResource.set(String(resource.id), getEventsForResource(resource, contextEvents));
|
|
204
|
-
});
|
|
205
|
-
return eventsByResource;
|
|
206
|
-
}
|
|
207
|
-
function getWorkingSegmentsForInterval(resource, interval) {
|
|
208
|
-
var intervalStart = dayjs(interval.start_at);
|
|
209
|
-
var intervalEnd = dayjs(interval.end_at);
|
|
210
|
-
return resource.times.map(function (time) {
|
|
211
|
-
var start = maxDayjs(dayjs(time.start_at), intervalStart);
|
|
212
|
-
var end = minDayjs(dayjs(time.end_at), intervalEnd);
|
|
165
|
+
function addLeadWindow(base, unit, unitType, _timezoneName) {
|
|
166
|
+
if (unitType === 'days') return addLocalCalendarDays(base, unit);
|
|
167
|
+
if (unitType === 'hours') return addLocalHours(base, unit);
|
|
168
|
+
return addLocalMinutes(base, unit);
|
|
169
|
+
}
|
|
170
|
+
function evaluateCandidateTimeGate(params) {
|
|
171
|
+
var product = params.product,
|
|
172
|
+
candidateRange = params.candidateRange,
|
|
173
|
+
evaluationContext = params.evaluationContext,
|
|
174
|
+
timezoneName = params.timezoneName;
|
|
175
|
+
var now = evaluationContext.evaluatedAt;
|
|
176
|
+
if (candidateRange.endMs <= evaluationContext.evaluatedAtMs) {
|
|
213
177
|
return {
|
|
214
|
-
|
|
215
|
-
|
|
178
|
+
status: 'past',
|
|
179
|
+
reasonCodes: ['past'],
|
|
180
|
+
conflictCode: 'past'
|
|
216
181
|
};
|
|
217
|
-
}
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
}
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
var
|
|
230
|
-
if (
|
|
231
|
-
previous.end = maxDayjs(previous.end, segment.end);
|
|
232
|
-
} else {
|
|
233
|
-
merged.push(_objectSpread({}, segment));
|
|
234
|
-
}
|
|
235
|
-
});
|
|
236
|
-
return merged;
|
|
237
|
-
}
|
|
238
|
-
function subtractBusyIntervals(workingSegments, events) {
|
|
239
|
-
var available = [];
|
|
240
|
-
workingSegments.forEach(function (working) {
|
|
241
|
-
var busySegments = mergeBusySegments(events.filter(function (event) {
|
|
242
|
-
return overlaps(working.start, working.end, dayjs(event.start_at), dayjs(event.end_at));
|
|
243
|
-
}).map(function (event) {
|
|
182
|
+
}
|
|
183
|
+
var policy = product.cutOffPolicy;
|
|
184
|
+
if (!policy) return null;
|
|
185
|
+
if (resolveCutOffPolicyIssue(policy)) {
|
|
186
|
+
return {
|
|
187
|
+
status: 'unavailable',
|
|
188
|
+
reasonCodes: ['booking_cutoff_invalid'],
|
|
189
|
+
conflictCode: 'booking_cutoff'
|
|
190
|
+
};
|
|
191
|
+
}
|
|
192
|
+
if ('futureDay' in policy && policy.futureDay !== undefined) {
|
|
193
|
+
var futureDay = normalizeNonNegativeNumber(policy.futureDay);
|
|
194
|
+
var lastAllowedDateOrdinal = dateOrdinalFromLocalDate(formatDate(now)) + futureDay;
|
|
195
|
+
if (dateOrdinalFromLocalDate(formatDate(candidateRange.start)) > lastAllowedDateOrdinal) {
|
|
244
196
|
return {
|
|
245
|
-
|
|
246
|
-
|
|
197
|
+
status: 'unavailable',
|
|
198
|
+
reasonCodes: ['booking_cutoff_future_day'],
|
|
199
|
+
conflictCode: 'booking_cutoff'
|
|
247
200
|
};
|
|
248
|
-
}));
|
|
249
|
-
var cursor = working.start;
|
|
250
|
-
busySegments.forEach(function (busy) {
|
|
251
|
-
if (busy.start.isAfter(cursor)) {
|
|
252
|
-
available.push({
|
|
253
|
-
start: cursor,
|
|
254
|
-
end: busy.start
|
|
255
|
-
});
|
|
256
|
-
}
|
|
257
|
-
if (busy.end.isAfter(cursor)) cursor = busy.end;
|
|
258
|
-
});
|
|
259
|
-
if (working.end.isAfter(cursor)) {
|
|
260
|
-
available.push({
|
|
261
|
-
start: cursor,
|
|
262
|
-
end: working.end
|
|
263
|
-
});
|
|
264
|
-
}
|
|
265
|
-
});
|
|
266
|
-
return available;
|
|
267
|
-
}
|
|
268
|
-
function buildCapacityRanges(params) {
|
|
269
|
-
var workingSegments = params.workingSegments,
|
|
270
|
-
events = params.events,
|
|
271
|
-
maxCapacity = params.maxCapacity,
|
|
272
|
-
requiredCapacity = params.requiredCapacity;
|
|
273
|
-
var ranges = [];
|
|
274
|
-
workingSegments.forEach(function (working) {
|
|
275
|
-
var boundaries = new Map([[working.start.valueOf(), working.start], [working.end.valueOf(), working.end]]);
|
|
276
|
-
events.forEach(function (event) {
|
|
277
|
-
var eventStart = dayjs(event.start_at);
|
|
278
|
-
var eventEnd = dayjs(event.end_at);
|
|
279
|
-
if (!overlaps(working.start, working.end, eventStart, eventEnd)) return;
|
|
280
|
-
var clippedStart = maxDayjs(eventStart, working.start);
|
|
281
|
-
var clippedEnd = minDayjs(eventEnd, working.end);
|
|
282
|
-
boundaries.set(clippedStart.valueOf(), clippedStart);
|
|
283
|
-
boundaries.set(clippedEnd.valueOf(), clippedEnd);
|
|
284
|
-
});
|
|
285
|
-
var points = Array.from(boundaries.values()).sort(function (left, right) {
|
|
286
|
-
return left.valueOf() - right.valueOf();
|
|
287
|
-
});
|
|
288
|
-
var _loop = function _loop() {
|
|
289
|
-
var start = points[index];
|
|
290
|
-
var end = points[index + 1];
|
|
291
|
-
if (!end.isAfter(start)) return 0; // continue
|
|
292
|
-
var usedCapacity = events.filter(function (event) {
|
|
293
|
-
return overlaps(start, end, dayjs(event.start_at), dayjs(event.end_at));
|
|
294
|
-
}).reduce(function (sum, event) {
|
|
295
|
-
return sum + Math.max(1, Number(event.pax || 1));
|
|
296
|
-
}, 0);
|
|
297
|
-
var remainingCapacity = Math.max(0, maxCapacity - usedCapacity);
|
|
298
|
-
if (remainingCapacity < requiredCapacity) return 0; // continue
|
|
299
|
-
var status = remainingCapacity < maxCapacity ? 'limited' : 'available';
|
|
300
|
-
var previous = ranges[ranges.length - 1];
|
|
301
|
-
if (previous && previous.end.isSame(start) && previous.remainingCapacity === remainingCapacity && previous.maxCapacity === maxCapacity && previous.status === status) {
|
|
302
|
-
previous.end = end;
|
|
303
|
-
} else {
|
|
304
|
-
ranges.push({
|
|
305
|
-
start: start,
|
|
306
|
-
end: end,
|
|
307
|
-
remainingCapacity: remainingCapacity,
|
|
308
|
-
maxCapacity: maxCapacity,
|
|
309
|
-
status: status
|
|
310
|
-
});
|
|
311
|
-
}
|
|
312
|
-
},
|
|
313
|
-
_ret;
|
|
314
|
-
for (var index = 0; index < points.length - 1; index += 1) {
|
|
315
|
-
_ret = _loop();
|
|
316
|
-
if (_ret === 0) continue;
|
|
317
201
|
}
|
|
318
|
-
}
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
202
|
+
}
|
|
203
|
+
if (policy.type === 'ongoing') {
|
|
204
|
+
if (policy.mode === 'before_end') return null;
|
|
205
|
+
var graceMinutes = normalizeNonNegativeNumber(policy.graceMinutes);
|
|
206
|
+
var graceDeadline = addMinutesZoned(candidateRange.start, graceMinutes, timezoneName);
|
|
207
|
+
if (now < graceDeadline) return null;
|
|
208
|
+
return {
|
|
209
|
+
status: 'unavailable',
|
|
210
|
+
reasonCodes: ['booking_cutoff_after_start'],
|
|
211
|
+
conflictCode: 'booking_cutoff'
|
|
212
|
+
};
|
|
213
|
+
}
|
|
214
|
+
if (policy.type === 'before_start') {
|
|
215
|
+
if (now < candidateRange.start) return null;
|
|
216
|
+
return {
|
|
217
|
+
status: 'unavailable',
|
|
218
|
+
reasonCodes: ['booking_cutoff_before_start'],
|
|
219
|
+
conflictCode: 'booking_cutoff'
|
|
220
|
+
};
|
|
221
|
+
}
|
|
222
|
+
if (policy.type === 'advance') {
|
|
223
|
+
var leadWindow = addLeadWindow(now, normalizeNonNegativeNumber(policy.unit), policy.unitType, timezoneName);
|
|
224
|
+
if (leadWindow <= candidateRange.start) return null;
|
|
225
|
+
return {
|
|
226
|
+
status: 'unavailable',
|
|
227
|
+
reasonCodes: ['booking_cutoff_advance'],
|
|
228
|
+
conflictCode: 'booking_cutoff'
|
|
229
|
+
};
|
|
230
|
+
}
|
|
332
231
|
return {
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
date: interval.date,
|
|
337
|
-
start_at: segment.start.toISOString(),
|
|
338
|
-
end_at: segment.end.toISOString(),
|
|
339
|
-
startTime: segment.start.format('HH:mm'),
|
|
340
|
-
endTime: segment.end.format('HH:mm'),
|
|
341
|
-
durationMinutes: interval.durationMinutes,
|
|
342
|
-
cellType: 'availability_range',
|
|
343
|
-
status: status,
|
|
344
|
-
remainingCapacity: remainingCapacity,
|
|
345
|
-
maxCapacity: maxCapacity,
|
|
346
|
-
reasonCodes: [],
|
|
347
|
-
conflicts: []
|
|
232
|
+
status: 'unavailable',
|
|
233
|
+
reasonCodes: ['booking_cutoff_invalid'],
|
|
234
|
+
conflictCode: 'booking_cutoff'
|
|
348
235
|
};
|
|
349
236
|
}
|
|
350
|
-
function
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
var
|
|
358
|
-
var
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
237
|
+
function overlaps(left, right) {
|
|
238
|
+
return left.start < right.end && left.end > right.start;
|
|
239
|
+
}
|
|
240
|
+
function contains(outer, inner) {
|
|
241
|
+
return inner.start >= outer.start && inner.end <= outer.end;
|
|
242
|
+
}
|
|
243
|
+
function clipRange(base, clip) {
|
|
244
|
+
var start = Math.max(base.start, clip.start);
|
|
245
|
+
var end = Math.min(base.end, clip.end);
|
|
246
|
+
if (end <= start) return null;
|
|
247
|
+
return {
|
|
248
|
+
start: start,
|
|
249
|
+
end: end
|
|
250
|
+
};
|
|
251
|
+
}
|
|
252
|
+
function sortRanges(ranges) {
|
|
253
|
+
return _toConsumableArray(ranges).sort(function (left, right) {
|
|
254
|
+
return left.start - right.start;
|
|
366
255
|
});
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
256
|
+
}
|
|
257
|
+
function mergeRanges(ranges) {
|
|
258
|
+
var sorted = sortRanges(ranges);
|
|
259
|
+
var merged = [];
|
|
260
|
+
sorted.forEach(function (range) {
|
|
261
|
+
var last = merged[merged.length - 1];
|
|
262
|
+
if (!last) {
|
|
263
|
+
merged.push(range);
|
|
264
|
+
return;
|
|
265
|
+
}
|
|
266
|
+
if (range.start <= last.end) {
|
|
267
|
+
last.end = Math.max(range.end, last.end);
|
|
268
|
+
return;
|
|
269
|
+
}
|
|
270
|
+
merged.push(range);
|
|
378
271
|
});
|
|
379
|
-
return
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
272
|
+
return merged;
|
|
273
|
+
}
|
|
274
|
+
function intersectRangeLists(left, right) {
|
|
275
|
+
var results = [];
|
|
276
|
+
left.forEach(function (leftRange) {
|
|
277
|
+
right.forEach(function (rightRange) {
|
|
278
|
+
var clipped = clipRange(leftRange, rightRange);
|
|
279
|
+
if (clipped) results.push(clipped);
|
|
386
280
|
});
|
|
387
281
|
});
|
|
282
|
+
return mergeRanges(results);
|
|
388
283
|
}
|
|
389
|
-
|
|
390
|
-
var
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
var conflicts = [];
|
|
396
|
-
var reasonCodes = [];
|
|
397
|
-
var maxCapacity = Math.max(1, Number(resource.capacity || 1));
|
|
398
|
-
var requiredCapacity = Math.max(1, Number(interval.capacityRequired || 1));
|
|
399
|
-
if (start.isBefore(dayjs())) {
|
|
400
|
-
reasonCodes.push('past');
|
|
401
|
-
conflicts.push({
|
|
402
|
-
type: 'past',
|
|
403
|
-
message: '时间已过期',
|
|
404
|
-
resourceId: resource.id,
|
|
405
|
-
productId: interval.productId
|
|
406
|
-
});
|
|
284
|
+
function enumerateDates(startDate, endDate) {
|
|
285
|
+
var dates = [];
|
|
286
|
+
var cursor = startDate;
|
|
287
|
+
while (cursor <= endDate) {
|
|
288
|
+
dates.push(cursor);
|
|
289
|
+
cursor = nextDate(cursor);
|
|
407
290
|
}
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
291
|
+
return dates;
|
|
292
|
+
}
|
|
293
|
+
function normalizeCoverage(input) {
|
|
294
|
+
var startDate = input.coverage.startDate;
|
|
295
|
+
var endDate = input.coverage.endDate;
|
|
296
|
+
var isCanonicalDate = function isCanonicalDate(value) {
|
|
297
|
+
try {
|
|
298
|
+
return formatLocalDate(parseLocalDate(value)) === value;
|
|
299
|
+
} catch (_error) {
|
|
300
|
+
return false;
|
|
301
|
+
}
|
|
302
|
+
};
|
|
303
|
+
if (!startDate || !endDate || !isCanonicalDate(startDate) || !isCanonicalDate(endDate)) {
|
|
304
|
+
throw availabilityError('INVALID_DATE_RANGE', 'coverage.startDate / coverage.endDate 非法', {
|
|
305
|
+
coverage: input.coverage
|
|
418
306
|
});
|
|
419
307
|
}
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
});
|
|
424
|
-
var usedCapacity = resource.resourceType === 'single' ? overlapping.length : overlapping.reduce(function (sum, event) {
|
|
425
|
-
return sum + Math.max(1, Number(event.pax || 1));
|
|
426
|
-
}, 0);
|
|
427
|
-
var remainingCapacity = resource.resourceType === 'single' ? overlapping.length > 0 ? 0 : 1 : Math.max(0, maxCapacity - usedCapacity);
|
|
428
|
-
if (overlapping.length > 0 && resource.resourceType === 'single') {
|
|
429
|
-
reasonCodes.push('remote_event_overlap');
|
|
430
|
-
conflicts.push.apply(conflicts, _toConsumableArray(overlapping.map(function (event) {
|
|
431
|
-
return {
|
|
432
|
-
type: 'remote_event_overlap',
|
|
433
|
-
message: '资源时间被已有预约占用',
|
|
434
|
-
event: event,
|
|
435
|
-
resourceId: resource.id,
|
|
436
|
-
productId: interval.productId
|
|
437
|
-
};
|
|
438
|
-
})));
|
|
439
|
-
}
|
|
440
|
-
if (resource.resourceType !== 'single' && remainingCapacity < requiredCapacity) {
|
|
441
|
-
reasonCodes.push('capacity_full');
|
|
442
|
-
conflicts.push({
|
|
443
|
-
type: 'capacity_full',
|
|
444
|
-
message: '资源剩余容量不足',
|
|
445
|
-
resourceId: resource.id,
|
|
446
|
-
productId: interval.productId
|
|
308
|
+
if (endDate < startDate) {
|
|
309
|
+
throw availabilityError('INVALID_DATE_RANGE', 'coverage.endDate 不能早于 startDate', {
|
|
310
|
+
coverage: input.coverage
|
|
447
311
|
});
|
|
448
312
|
}
|
|
449
|
-
var status = 'available';
|
|
450
|
-
if (reasonCodes.includes('past')) status = 'past';else if (reasonCodes.includes('outside_resource_time')) status = 'unavailable';else if (reasonCodes.includes('remote_event_overlap') || reasonCodes.includes('capacity_full')) {
|
|
451
|
-
status = 'full';
|
|
452
|
-
} else if (remainingCapacity < maxCapacity) {
|
|
453
|
-
status = 'limited';
|
|
454
|
-
}
|
|
455
313
|
return {
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
durationMinutes: interval.durationMinutes,
|
|
465
|
-
cellType: status === 'available' || status === 'limited' ? 'fixed_interval' : 'unavailable',
|
|
466
|
-
status: status,
|
|
467
|
-
remainingCapacity: remainingCapacity,
|
|
468
|
-
maxCapacity: maxCapacity,
|
|
469
|
-
reasonCodes: reasonCodes,
|
|
470
|
-
conflicts: conflicts
|
|
314
|
+
dateRange: {
|
|
315
|
+
startDate: startDate,
|
|
316
|
+
endDate: endDate
|
|
317
|
+
},
|
|
318
|
+
absolute: {
|
|
319
|
+
start: toDateStart(startDate, input.timezone),
|
|
320
|
+
end: toDateEnd(nextDate(endDate, input.timezone), input.timezone)
|
|
321
|
+
}
|
|
471
322
|
};
|
|
472
323
|
}
|
|
473
|
-
function
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
return
|
|
480
|
-
}))
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
})) return 'past';
|
|
484
|
-
if (cells.some(function (cell) {
|
|
485
|
-
return cell.status === 'full';
|
|
486
|
-
})) return 'full';
|
|
487
|
-
if (cells.some(function (cell) {
|
|
488
|
-
return cell.status === 'conflict';
|
|
489
|
-
})) return 'conflict';
|
|
490
|
-
return 'unavailable';
|
|
324
|
+
function normalizeResourceWindow(resource, coverage, timezoneName) {
|
|
325
|
+
return mergeRanges((resource.windows || []).map(function (window) {
|
|
326
|
+
return normalizeAbsoluteRange(window, timezoneName);
|
|
327
|
+
}).filter(function (window) {
|
|
328
|
+
return Boolean(window);
|
|
329
|
+
}).map(function (window) {
|
|
330
|
+
return clipRange(window, coverage);
|
|
331
|
+
}).filter(function (window) {
|
|
332
|
+
return Boolean(window);
|
|
333
|
+
}));
|
|
491
334
|
}
|
|
492
|
-
function
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
335
|
+
function normalizeOccupancy(occupancy, coverage, timezoneName, fixedOffsetMinutes) {
|
|
336
|
+
var normalized = normalizeAbsoluteRange(occupancy, timezoneName);
|
|
337
|
+
if (!normalized) return null;
|
|
338
|
+
var clipped = clipRange(normalized, coverage);
|
|
339
|
+
if (!clipped) return null;
|
|
340
|
+
return _objectSpread(_objectSpread({}, occupancy), {}, {
|
|
341
|
+
startAt: formatDateTime(clipped.start, timezoneName, fixedOffsetMinutes),
|
|
342
|
+
endAt: formatDateTime(clipped.end, timezoneName, fixedOffsetMinutes),
|
|
343
|
+
pax: Math.max(1, Number(occupancy.pax || 1) || 1)
|
|
344
|
+
});
|
|
496
345
|
}
|
|
497
|
-
function
|
|
498
|
-
var
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
346
|
+
function normalizeRequirementGroup(product, requirementGroup) {
|
|
347
|
+
var _requirementGroup$for;
|
|
348
|
+
var resourceType = 'single';
|
|
349
|
+
var required = requirementGroup.required !== false;
|
|
350
|
+
var min = required ? 1 : 0;
|
|
351
|
+
return _objectSpread(_objectSpread({}, requirementGroup), {}, {
|
|
352
|
+
id: requirementGroup.id || "group:".concat(String(product.id), ":").concat(String((_requirementGroup$for = requirementGroup.formId) !== null && _requirementGroup$for !== void 0 ? _requirementGroup$for : resourceType)),
|
|
353
|
+
resourceType: resourceType,
|
|
354
|
+
required: required,
|
|
355
|
+
min: min,
|
|
356
|
+
max: 1,
|
|
357
|
+
resourceIds: uniqById(requirementGroup.resourceIds || []),
|
|
358
|
+
capacityRequired: Math.max(1, Number(requirementGroup.capacityRequired || product.capacityRequired || 1) || 1)
|
|
510
359
|
});
|
|
511
360
|
}
|
|
512
|
-
function
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
361
|
+
function normalizeProducts(products) {
|
|
362
|
+
return products.map(function (product) {
|
|
363
|
+
return _objectSpread(_objectSpread({}, product), {}, {
|
|
364
|
+
quantity: Math.max(1, Number(product.quantity || 1) || 1),
|
|
365
|
+
capacityRequired: Math.max(1, Number(product.capacityRequired || 1) || 1),
|
|
366
|
+
durationMinutes: Math.max(1, Number(product.durationMinutes || 30) || 30),
|
|
367
|
+
cutOffPolicy: product.cutOffPolicy ? _objectSpread({}, product.cutOffPolicy) : undefined,
|
|
368
|
+
requirementGroups: (product.requirementGroups || []).map(function (group) {
|
|
369
|
+
return normalizeRequirementGroup(product, group);
|
|
370
|
+
}),
|
|
371
|
+
sessionRules: (product.sessionRules || []).map(function (rule) {
|
|
372
|
+
return _objectSpread(_objectSpread({}, rule), {}, {
|
|
373
|
+
weekdays: rule.weekdays ? _toConsumableArray(rule.weekdays) : undefined,
|
|
374
|
+
dates: rule.dates ? _toConsumableArray(rule.dates) : undefined,
|
|
375
|
+
includeDates: rule.includeDates ? _toConsumableArray(rule.includeDates) : undefined,
|
|
376
|
+
excludeDates: rule.excludeDates ? _toConsumableArray(rule.excludeDates) : undefined,
|
|
377
|
+
scheduleRef: rule.scheduleRef ? _objectSpread({}, rule.scheduleRef) : undefined
|
|
378
|
+
});
|
|
379
|
+
}),
|
|
380
|
+
sessionRanges: (product.sessionRanges || []).map(function (range) {
|
|
381
|
+
var _range$scheduleRefs;
|
|
382
|
+
return _objectSpread(_objectSpread({}, range), {}, {
|
|
383
|
+
scheduleRefs: (_range$scheduleRefs = range.scheduleRefs) === null || _range$scheduleRefs === void 0 ? void 0 : _range$scheduleRefs.map(function (scheduleRef) {
|
|
384
|
+
return _objectSpread({}, scheduleRef);
|
|
385
|
+
})
|
|
386
|
+
});
|
|
387
|
+
})
|
|
517
388
|
});
|
|
518
|
-
var bestCell = resourceCells.find(function (cell) {
|
|
519
|
-
return cell.status === 'available';
|
|
520
|
-
}) || resourceCells.find(function (cell) {
|
|
521
|
-
return cell.status === 'limited';
|
|
522
|
-
}) || resourceCells[0];
|
|
523
|
-
return {
|
|
524
|
-
resourceId: resource.id,
|
|
525
|
-
resourceName: resource.name,
|
|
526
|
-
resourceFormId: resource.formId,
|
|
527
|
-
resourceType: resource.resourceType,
|
|
528
|
-
status: resourceCells.length ? aggregateStatus(resourceCells) : 'unavailable',
|
|
529
|
-
remainingCapacity: bestCell === null || bestCell === void 0 ? void 0 : bestCell.remainingCapacity,
|
|
530
|
-
maxCapacity: (bestCell === null || bestCell === void 0 ? void 0 : bestCell.maxCapacity) || resource.capacity,
|
|
531
|
-
reasonCodes: uniqueReasonCodes(resourceCells)
|
|
532
|
-
};
|
|
533
389
|
});
|
|
534
390
|
}
|
|
535
|
-
function
|
|
536
|
-
|
|
537
|
-
return
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
391
|
+
function normalizeResources(resources) {
|
|
392
|
+
return resources.map(function (resource) {
|
|
393
|
+
return _objectSpread(_objectSpread({}, resource), {}, {
|
|
394
|
+
resourceType: 'single',
|
|
395
|
+
capacity: Math.max(1, Number(resource.capacity || 1) || 1),
|
|
396
|
+
windows: (resource.windows || []).map(function (window) {
|
|
397
|
+
return _objectSpread({}, window);
|
|
398
|
+
})
|
|
542
399
|
});
|
|
543
|
-
var availableCount = dateCells.filter(function (cell) {
|
|
544
|
-
return cell.status === 'available' || cell.status === 'limited';
|
|
545
|
-
}).length;
|
|
546
|
-
return {
|
|
547
|
-
date: date,
|
|
548
|
-
status: dateCells.length ? aggregateStatus(dateCells) : 'unavailable',
|
|
549
|
-
availableCount: availableCount,
|
|
550
|
-
totalCount: dateCells.length
|
|
551
|
-
};
|
|
552
400
|
});
|
|
553
401
|
}
|
|
554
|
-
function
|
|
555
|
-
|
|
556
|
-
var
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
}
|
|
567
|
-
function buildGridSlotInterval(product, date, label, context, query) {
|
|
568
|
-
var slotMinutes = product.kind === 'duration' ? Math.max(1, Number(product.durationMinutes || query.slotDurationMinutes || context.slotDurationMinutes)) : query.slotDurationMinutes || context.slotDurationMinutes;
|
|
569
|
-
var start = toDateTime(date, label);
|
|
570
|
-
var end = start.add(slotMinutes, 'minute');
|
|
571
|
-
return {
|
|
572
|
-
id: "".concat(product.id, ":").concat(date, ":").concat(label),
|
|
573
|
-
productId: product.id,
|
|
574
|
-
source: product.kind === 'session' ? 'session' : product.kind === 'venue_slot' ? 'venue_slot' : 'duration',
|
|
575
|
-
date: date,
|
|
576
|
-
start_at: start.toISOString(),
|
|
577
|
-
end_at: end.toISOString(),
|
|
578
|
-
durationMinutes: slotMinutes,
|
|
579
|
-
quantity: Math.max(1, Number(product.quantity || 1)),
|
|
580
|
-
capacityRequired: Math.max(1, Number(query.pax || product.capacityRequired || 1)),
|
|
581
|
-
resourceRequirements: product.resourceRequirements || []
|
|
582
|
-
};
|
|
583
|
-
}
|
|
584
|
-
function buildTimeGrid(context, query, cells) {
|
|
585
|
-
var _query$dateRange;
|
|
586
|
-
var date = query.date || ((_query$dateRange = query.dateRange) === null || _query$dateRange === void 0 ? void 0 : _query$dateRange.start);
|
|
587
|
-
var products = getQueryProducts(context.products, query).filter(function (product) {
|
|
588
|
-
return product.kind !== 'normal';
|
|
402
|
+
function normalizeOperatingHours(params) {
|
|
403
|
+
var _params$input, _params$input2, _params$input3;
|
|
404
|
+
var fallbackWindows = enumerateDates(params.dateRange.startDate, params.dateRange.endDate).map(function (date) {
|
|
405
|
+
var start = toDateTimeInDate(date, params.businessHours.startTime, params.timezoneName);
|
|
406
|
+
var end = toDateTimeInDate(date, params.businessHours.endTime, params.timezoneName);
|
|
407
|
+
if (end <= start) {
|
|
408
|
+
end = toDateTimeInDate(nextDate(date), params.businessHours.endTime, params.timezoneName);
|
|
409
|
+
}
|
|
410
|
+
return {
|
|
411
|
+
start: start,
|
|
412
|
+
end: end
|
|
413
|
+
};
|
|
589
414
|
});
|
|
590
|
-
var
|
|
591
|
-
var timeLabels = buildTimeLabels(date, context, query);
|
|
592
|
-
if (!date || !products.length || !resources.length) {
|
|
415
|
+
var sourceWindows = params.input ? params.input.windows : fallbackWindows.map(function (range) {
|
|
593
416
|
return {
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
resources: []
|
|
417
|
+
startAt: formatDateTime(range.start, params.timezoneName, params.fixedOffsetMinutes),
|
|
418
|
+
endAt: formatDateTime(range.end, params.timezoneName, params.fixedOffsetMinutes)
|
|
597
419
|
};
|
|
598
|
-
}
|
|
599
|
-
var
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
return {
|
|
609
|
-
startTime: label,
|
|
610
|
-
endTime: dayjs(interval.end_at).format('HH:mm'),
|
|
611
|
-
status: (cell === null || cell === void 0 ? void 0 : cell.status) || 'unavailable',
|
|
612
|
-
resourceId: resource.id,
|
|
613
|
-
productId: product.id,
|
|
614
|
-
remainingCapacity: cell === null || cell === void 0 ? void 0 : cell.remainingCapacity,
|
|
615
|
-
maxCapacity: (cell === null || cell === void 0 ? void 0 : cell.maxCapacity) || resource.capacity,
|
|
616
|
-
reasonCodes: (cell === null || cell === void 0 ? void 0 : cell.reasonCodes) || ['outside_available_range']
|
|
617
|
-
};
|
|
618
|
-
});
|
|
420
|
+
});
|
|
421
|
+
var windows = mergeRanges(sourceWindows.map(function (window) {
|
|
422
|
+
return normalizeAbsoluteRange(window, params.timezoneName);
|
|
423
|
+
}).filter(function (window) {
|
|
424
|
+
return Boolean(window);
|
|
425
|
+
}).map(function (window) {
|
|
426
|
+
return clipRange(window, params.coverage);
|
|
427
|
+
}).filter(function (window) {
|
|
428
|
+
return Boolean(window);
|
|
429
|
+
})).map(function (range) {
|
|
619
430
|
return {
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
resourceFormId: resource.formId,
|
|
623
|
-
slots: slots
|
|
431
|
+
startAt: formatDateTime(range.start, params.timezoneName, params.fixedOffsetMinutes),
|
|
432
|
+
endAt: formatDateTime(range.end, params.timezoneName, params.fixedOffsetMinutes)
|
|
624
433
|
};
|
|
625
434
|
});
|
|
626
435
|
return {
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
436
|
+
source: ((_params$input = params.input) === null || _params$input === void 0 ? void 0 : _params$input.source) || params.fallbackSource,
|
|
437
|
+
scheduleIds: uniqById(((_params$input2 = params.input) === null || _params$input2 === void 0 ? void 0 : _params$input2.scheduleIds) || []),
|
|
438
|
+
missingScheduleIds: uniqById(((_params$input3 = params.input) === null || _params$input3 === void 0 ? void 0 : _params$input3.missingScheduleIds) || []),
|
|
439
|
+
windows: windows
|
|
630
440
|
};
|
|
631
441
|
}
|
|
632
|
-
|
|
633
|
-
var
|
|
634
|
-
var
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
442
|
+
function matchesRequirementGroup(requirementGroup, resource, product) {
|
|
443
|
+
var _requirementGroup$res;
|
|
444
|
+
var partySize = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : 1;
|
|
445
|
+
if (requirementGroup.formId !== undefined && resource.formId !== undefined && !sameId(requirementGroup.formId, resource.formId)) {
|
|
446
|
+
return false;
|
|
447
|
+
}
|
|
448
|
+
if ((_requirementGroup$res = requirementGroup.resourceIds) !== null && _requirementGroup$res !== void 0 && _requirementGroup$res.length && !requirementGroup.resourceIds.some(function (resourceId) {
|
|
449
|
+
return sameId(resourceId, resource.id);
|
|
450
|
+
})) {
|
|
451
|
+
return false;
|
|
452
|
+
}
|
|
453
|
+
return resource.capacity >= resolveRequiredCapacity(requirementGroup, product, partySize);
|
|
454
|
+
}
|
|
455
|
+
function resolveRequiredCapacity(requirementGroup, product, partySize) {
|
|
456
|
+
var _ref, _ref2;
|
|
457
|
+
return Math.max(1, Number((_ref = (_ref2 = partySize !== null && partySize !== void 0 ? partySize : requirementGroup.capacityRequired) !== null && _ref2 !== void 0 ? _ref2 : product.capacityRequired) !== null && _ref !== void 0 ? _ref : 1) || 1);
|
|
458
|
+
}
|
|
459
|
+
function buildCellKey(params) {
|
|
460
|
+
return [String(params.productId), params.groupId, String(params.resourceId), params.start, params.end].join(':');
|
|
461
|
+
}
|
|
462
|
+
function dedupeOccupancyRefKey(occupancy) {
|
|
463
|
+
if (occupancy.scheduleEventId !== undefined && occupancy.scheduleEventId !== null) {
|
|
464
|
+
return "scheduleEvent:".concat(String(occupancy.resourceId), ":").concat(String(occupancy.scheduleEventId));
|
|
465
|
+
}
|
|
466
|
+
if (occupancy.bookingUid !== undefined && occupancy.bookingUid !== null) {
|
|
467
|
+
return "bookingUid:".concat(String(occupancy.resourceId), ":").concat(String(occupancy.bookingUid));
|
|
468
|
+
}
|
|
469
|
+
if (occupancy.productUid !== undefined && occupancy.productUid !== null) {
|
|
470
|
+
return "productUid:".concat(String(occupancy.resourceId), ":").concat(String(occupancy.productUid));
|
|
471
|
+
}
|
|
472
|
+
return "range:".concat(String(occupancy.resourceId), ":").concat(occupancy.startAt, ":").concat(occupancy.endAt, ":").concat(String(occupancy.pax || 1));
|
|
473
|
+
}
|
|
474
|
+
function buildCells(projection) {
|
|
475
|
+
var _extractFixedOffsetMi;
|
|
476
|
+
var coverage = {
|
|
477
|
+
start: toDateTime(projection.meta.coverage.startAt, projection.meta.timezone),
|
|
478
|
+
end: toDateTime(projection.meta.coverage.endAt, projection.meta.timezone)
|
|
479
|
+
};
|
|
638
480
|
var cells = [];
|
|
639
|
-
var
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
481
|
+
var timezoneName = projection.meta.timezone;
|
|
482
|
+
var fixedOffsetMinutes = (_extractFixedOffsetMi = extractFixedOffsetMinutes(projection.meta.now)) !== null && _extractFixedOffsetMi !== void 0 ? _extractFixedOffsetMi : captureRuntimeOffsetMinutes();
|
|
483
|
+
var projectionNow = toDateTime(projection.meta.now);
|
|
484
|
+
projection.products.forEach(function (product) {
|
|
485
|
+
(product.requirementGroups || []).forEach(function (requirementGroup) {
|
|
486
|
+
projection.resources.filter(function (resource) {
|
|
487
|
+
return matchesRequirementGroup(requirementGroup, resource, product);
|
|
488
|
+
}).forEach(function (resource) {
|
|
489
|
+
var windows = normalizeResourceWindow(resource, coverage, timezoneName);
|
|
490
|
+
var occupancies = projection.resourceOccupancies.filter(function (occupancy) {
|
|
491
|
+
return sameId(occupancy.resourceId, resource.id);
|
|
492
|
+
}).map(function (occupancy) {
|
|
493
|
+
return {
|
|
494
|
+
occupancy: occupancy,
|
|
495
|
+
range: normalizeAbsoluteRange(occupancy, timezoneName)
|
|
496
|
+
};
|
|
497
|
+
}).filter(function (item) {
|
|
498
|
+
return Boolean(item.range);
|
|
499
|
+
}).map(function (item) {
|
|
500
|
+
return _objectSpread(_objectSpread({}, item), {}, {
|
|
501
|
+
range: clipRange(item.range, coverage)
|
|
502
|
+
});
|
|
503
|
+
}).filter(function (item) {
|
|
504
|
+
return Boolean(item.range);
|
|
649
505
|
});
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
productId: interval.productId,
|
|
655
|
-
date: interval.date,
|
|
656
|
-
start_at: interval.start_at,
|
|
657
|
-
end_at: interval.end_at,
|
|
658
|
-
startTime: timeOf(interval.start_at),
|
|
659
|
-
endTime: timeOf(interval.end_at),
|
|
660
|
-
durationMinutes: interval.durationMinutes,
|
|
661
|
-
cellType: 'unavailable',
|
|
662
|
-
status: 'unavailable',
|
|
663
|
-
reasonCodes: ['missing_resource'],
|
|
664
|
-
conflicts: [{
|
|
665
|
-
type: 'missing_resource',
|
|
666
|
-
message: '没有匹配商品需求的资源',
|
|
667
|
-
productId: interval.productId
|
|
668
|
-
}]
|
|
506
|
+
var boundaries = new Set([coverage.start, coverage.end]);
|
|
507
|
+
windows.forEach(function (window) {
|
|
508
|
+
boundaries.add(window.start);
|
|
509
|
+
boundaries.add(window.end);
|
|
669
510
|
});
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
511
|
+
occupancies.forEach(function (_ref3) {
|
|
512
|
+
var range = _ref3.range;
|
|
513
|
+
boundaries.add(range.start);
|
|
514
|
+
boundaries.add(range.end);
|
|
515
|
+
});
|
|
516
|
+
var sortedBoundaries = Array.from(boundaries.values()).sort(function (left, right) {
|
|
517
|
+
return left - right;
|
|
518
|
+
});
|
|
519
|
+
var _loop = function _loop() {
|
|
520
|
+
var start = sortedBoundaries[index];
|
|
521
|
+
var end = sortedBoundaries[index + 1];
|
|
522
|
+
if (end <= start) return 1; // continue
|
|
523
|
+
var segment = {
|
|
524
|
+
start: start,
|
|
525
|
+
end: end
|
|
526
|
+
};
|
|
527
|
+
var isInsideWorkingWindow = windows.some(function (window) {
|
|
528
|
+
return contains(window, segment);
|
|
529
|
+
});
|
|
530
|
+
var segmentOccupancies = isInsideWorkingWindow ? occupancies.filter(function (_ref4) {
|
|
531
|
+
var range = _ref4.range;
|
|
532
|
+
return overlaps(range, segment);
|
|
533
|
+
}).map(function (_ref5) {
|
|
534
|
+
var occupancy = _ref5.occupancy;
|
|
535
|
+
return occupancy;
|
|
536
|
+
}) : [];
|
|
537
|
+
var usedCapacity = segmentOccupancies.reduce(function (sum, occupancy) {
|
|
538
|
+
return sum + Math.max(1, Number(occupancy.pax || 1) || 1);
|
|
539
|
+
}, 0);
|
|
540
|
+
var remainingCapacity = Math.max(0, resource.capacity - usedCapacity);
|
|
541
|
+
var requiredCapacity = resolveRequiredCapacity(requirementGroup, product);
|
|
542
|
+
var reasonCodes = Array.from(new Set(segmentOccupancies.flatMap(function (occupancy) {
|
|
543
|
+
var source = occupancy.source || 'remote';
|
|
544
|
+
if (source === 'selection') return ['selection_occupied'];
|
|
545
|
+
if (source === 'cart') return ['cart_occupied'];
|
|
546
|
+
return ['resource_occupied'];
|
|
678
547
|
})));
|
|
679
|
-
|
|
548
|
+
var status = 'available';
|
|
549
|
+
if (end <= projectionNow) {
|
|
550
|
+
status = 'past';
|
|
551
|
+
reasonCodes.push('past');
|
|
552
|
+
} else if (!isInsideWorkingWindow) {
|
|
553
|
+
status = 'unavailable';
|
|
554
|
+
reasonCodes.push('outside_resource_window');
|
|
555
|
+
} else if (remainingCapacity < requiredCapacity) {
|
|
556
|
+
status = 'full';
|
|
557
|
+
reasonCodes.push('capacity_full');
|
|
558
|
+
} else if (remainingCapacity < resource.capacity) {
|
|
559
|
+
status = 'limited';
|
|
560
|
+
}
|
|
561
|
+
cells.push({
|
|
562
|
+
key: buildCellKey({
|
|
563
|
+
productId: product.id,
|
|
564
|
+
groupId: requirementGroup.id,
|
|
565
|
+
resourceId: resource.id,
|
|
566
|
+
start: start,
|
|
567
|
+
end: end
|
|
568
|
+
}),
|
|
569
|
+
productId: product.id,
|
|
570
|
+
requirementGroupId: requirementGroup.id,
|
|
571
|
+
requirementFormId: requirementGroup.formId,
|
|
572
|
+
resourceId: resource.id,
|
|
573
|
+
resourceName: resource.name,
|
|
574
|
+
resourceType: resource.resourceType,
|
|
575
|
+
startAt: formatDateTime(start, timezoneName, fixedOffsetMinutes),
|
|
576
|
+
endAt: formatDateTime(end, timezoneName, fixedOffsetMinutes),
|
|
577
|
+
date: formatDate(start, timezoneName),
|
|
578
|
+
startTime: formatTime(start, timezoneName),
|
|
579
|
+
endTime: formatTime(end, timezoneName),
|
|
580
|
+
timezone: projection.meta.timezone,
|
|
581
|
+
status: status,
|
|
582
|
+
// 资源组当前统一单选;具体资源仍用 capacity 表达可承载的预约数量。
|
|
583
|
+
remainingCapacity: remainingCapacity,
|
|
584
|
+
maxCapacity: resource.capacity,
|
|
585
|
+
reasonCodes: Array.from(new Set(reasonCodes)),
|
|
586
|
+
occupancyRefs: segmentOccupancies.map(function (occupancy) {
|
|
587
|
+
return {
|
|
588
|
+
id: occupancy.id,
|
|
589
|
+
resourceId: occupancy.resourceId,
|
|
590
|
+
source: occupancy.source,
|
|
591
|
+
serverEventId: occupancy.serverEventId,
|
|
592
|
+
bookingUid: occupancy.bookingUid,
|
|
593
|
+
productUid: occupancy.productUid,
|
|
594
|
+
scheduleEventId: occupancy.scheduleEventId
|
|
595
|
+
};
|
|
596
|
+
})
|
|
597
|
+
});
|
|
598
|
+
};
|
|
599
|
+
for (var index = 0; index < sortedBoundaries.length - 1; index += 1) {
|
|
600
|
+
if (_loop()) continue;
|
|
680
601
|
}
|
|
681
|
-
cells.push(evaluateResourceForInterval({
|
|
682
|
-
resource: resource,
|
|
683
|
-
interval: interval,
|
|
684
|
-
context: context
|
|
685
|
-
}));
|
|
686
602
|
});
|
|
687
|
-
};
|
|
688
|
-
|
|
689
|
-
|
|
603
|
+
});
|
|
604
|
+
});
|
|
605
|
+
return compressCells(cells);
|
|
606
|
+
}
|
|
607
|
+
function compressCells(cells) {
|
|
608
|
+
var sorted = _toConsumableArray(cells).sort(function (left, right) {
|
|
609
|
+
var keyLeft = [String(left.productId), String(left.requirementGroupId), String(left.resourceId), left.startAt].join(':');
|
|
610
|
+
var keyRight = [String(right.productId), String(right.requirementGroupId), String(right.resourceId), right.startAt].join(':');
|
|
611
|
+
return keyLeft.localeCompare(keyRight);
|
|
612
|
+
});
|
|
613
|
+
return sorted.reduce(function (result, cell) {
|
|
614
|
+
var last = result[result.length - 1];
|
|
615
|
+
if (last && sameId(last.productId, cell.productId) && sameId(last.requirementGroupId, cell.requirementGroupId) && sameId(last.resourceId, cell.resourceId) && last.status === cell.status && last.remainingCapacity === cell.remainingCapacity && last.maxCapacity === cell.maxCapacity && JSON.stringify(last.reasonCodes) === JSON.stringify(cell.reasonCodes) && JSON.stringify(last.occupancyRefs) === JSON.stringify(cell.occupancyRefs) && last.endAt === cell.startAt) {
|
|
616
|
+
last.endAt = cell.endAt;
|
|
617
|
+
last.endTime = cell.endTime;
|
|
618
|
+
return result;
|
|
690
619
|
}
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
}
|
|
694
|
-
|
|
695
|
-
|
|
620
|
+
result.push(_objectSpread({}, cell));
|
|
621
|
+
return result;
|
|
622
|
+
}, []);
|
|
623
|
+
}
|
|
624
|
+
function makeCoverageAbsolute(dateRange, timezoneName, fixedOffsetMinutes) {
|
|
625
|
+
var guardEndDate = nextDate(dateRange.endDate, timezoneName);
|
|
696
626
|
return {
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
productOptions: buildProductOptions(context.products, cells, query),
|
|
700
|
-
resourceOptions: buildResourceOptions(context.resources, cells, query),
|
|
701
|
-
dateSummary: buildDateSummary(cells, query),
|
|
702
|
-
timeGrid: buildTimeGrid(context, query, cells),
|
|
703
|
-
cells: cells
|
|
627
|
+
startAt: formatDateTime(toDateStart(dateRange.startDate, timezoneName), timezoneName, fixedOffsetMinutes),
|
|
628
|
+
endAt: formatDateTime(toDateEnd(guardEndDate, timezoneName), timezoneName, fixedOffsetMinutes)
|
|
704
629
|
};
|
|
705
630
|
}
|
|
706
|
-
function
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
required: true
|
|
710
|
-
}];
|
|
631
|
+
function normalizeProjectionInstant(value, fallbackScalar, timezoneName, fixedOffsetMinutes) {
|
|
632
|
+
var scalar = value ? toDateTime(value) : fallbackScalar;
|
|
633
|
+
return formatDateTime(Number.isFinite(scalar) ? scalar : fallbackScalar, timezoneName, fixedOffsetMinutes);
|
|
711
634
|
}
|
|
712
|
-
function
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
var conflicts = [];
|
|
717
|
-
var groups = getIntervalRequirements(interval).map(function (requirement) {
|
|
718
|
-
var candidateCells = cells.filter(function (cell) {
|
|
719
|
-
if (!sameId(cell.productId, interval.productId)) return false;
|
|
720
|
-
if (cell.date !== interval.date) return false;
|
|
721
|
-
if (cell.resourceId === undefined) return false;
|
|
722
|
-
if (cell.status !== 'available' && cell.status !== 'limited') return false;
|
|
723
|
-
var cellStart = dayjs(cell.start_at);
|
|
724
|
-
var cellEnd = dayjs(cell.end_at);
|
|
725
|
-
var intervalStart = dayjs(interval.start_at);
|
|
726
|
-
var intervalEnd = dayjs(interval.end_at);
|
|
727
|
-
return containsRange(cellStart, cellEnd, intervalStart, intervalEnd) || interval.source === 'duration';
|
|
728
|
-
});
|
|
729
|
-
return candidateCells.flatMap(function (cell) {
|
|
730
|
-
var resource = resources.find(function (item) {
|
|
731
|
-
return sameId(item.id, cell.resourceId);
|
|
732
|
-
});
|
|
733
|
-
if (!resource || !matchesResourceRequirement(resource, requirement)) return [];
|
|
734
|
-
return [{
|
|
735
|
-
requirement: requirement,
|
|
736
|
-
resource: resource,
|
|
737
|
-
cell: cell
|
|
738
|
-
}];
|
|
635
|
+
function cloneSnapshotMetadataValue(value) {
|
|
636
|
+
if (Array.isArray(value)) {
|
|
637
|
+
return value.map(function (item) {
|
|
638
|
+
return cloneSnapshotMetadataValue(item);
|
|
739
639
|
});
|
|
640
|
+
}
|
|
641
|
+
if (!value || _typeof(value) !== 'object') return value;
|
|
642
|
+
return Object.keys(value).reduce(function (result, key) {
|
|
643
|
+
result[key] = cloneSnapshotMetadataValue(value[key]);
|
|
644
|
+
return result;
|
|
645
|
+
}, {});
|
|
646
|
+
}
|
|
647
|
+
function deepFreeze(value) {
|
|
648
|
+
if (!value || _typeof(value) !== 'object' || Object.isFrozen(value)) return value;
|
|
649
|
+
Object.freeze(value);
|
|
650
|
+
Object.getOwnPropertyNames(value).forEach(function (key) {
|
|
651
|
+
// `raw` is an explicit escape hatch to the backend/catalog record. It is not owned by the
|
|
652
|
+
// projection and must not freeze mutable ProductList / Order data shared elsewhere.
|
|
653
|
+
if (key === 'raw') return;
|
|
654
|
+
var child = value[key];
|
|
655
|
+
if (child && (_typeof(child) === 'object' || typeof child === 'function')) {
|
|
656
|
+
deepFreeze(child);
|
|
657
|
+
}
|
|
740
658
|
});
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
659
|
+
return value;
|
|
660
|
+
}
|
|
661
|
+
export function createAvailabilityProjection(input) {
|
|
662
|
+
var _input$businessHours, _input$businessHours2;
|
|
663
|
+
var coverage = normalizeCoverage(input);
|
|
664
|
+
var fixedOffsetMinutes = captureRuntimeOffsetMinutes();
|
|
665
|
+
var runtimeNow = localDateToScalar(new Date());
|
|
666
|
+
var products = normalizeProducts(input.products || []);
|
|
667
|
+
var resources = normalizeResources(input.resources || []);
|
|
668
|
+
var resourceOccupancies = uniqByOccupancy((input.resourceOccupancies || []).map(function (occupancy) {
|
|
669
|
+
return normalizeOccupancy(occupancy, coverage.absolute, input.timezone, fixedOffsetMinutes);
|
|
670
|
+
}).filter(function (occupancy) {
|
|
671
|
+
return Boolean(occupancy);
|
|
672
|
+
}));
|
|
673
|
+
var preparedAt = normalizeProjectionInstant(input.preparedAt, runtimeNow, input.timezone, fixedOffsetMinutes);
|
|
674
|
+
var now = normalizeProjectionInstant(input.now, runtimeNow, input.timezone, fixedOffsetMinutes);
|
|
675
|
+
var durationCandidateAnchorAt = normalizeProjectionInstant(input.durationCandidateAnchorAt || now, runtimeNow, input.timezone, fixedOffsetMinutes);
|
|
676
|
+
var businessHours = {
|
|
677
|
+
startTime: ((_input$businessHours = input.businessHours) === null || _input$businessHours === void 0 ? void 0 : _input$businessHours.startTime) || DEFAULT_BUSINESS_HOURS.startTime,
|
|
678
|
+
endTime: ((_input$businessHours2 = input.businessHours) === null || _input$businessHours2 === void 0 ? void 0 : _input$businessHours2.endTime) || DEFAULT_BUSINESS_HOURS.endTime
|
|
679
|
+
};
|
|
680
|
+
var operatingHours = normalizeOperatingHours({
|
|
681
|
+
input: input.operatingHours,
|
|
682
|
+
businessHours: businessHours,
|
|
683
|
+
coverage: coverage.absolute,
|
|
684
|
+
dateRange: coverage.dateRange,
|
|
685
|
+
timezoneName: input.timezone,
|
|
686
|
+
fixedOffsetMinutes: fixedOffsetMinutes,
|
|
687
|
+
fallbackSource: input.businessHours ? 'prepare_business_hours' : 'default_business_hours'
|
|
750
688
|
});
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
689
|
+
var meta = {
|
|
690
|
+
snapshotId: input.snapshotId || "availability-".concat(Date.now(), "-").concat(Math.random().toString(36).slice(2, 8)),
|
|
691
|
+
preparedAt: preparedAt,
|
|
692
|
+
timezone: input.timezone,
|
|
693
|
+
orderRevision: input.orderRevision,
|
|
694
|
+
mode: input.mode || 'appointment',
|
|
695
|
+
coverage: _objectSpread(_objectSpread({}, coverage.dateRange), makeCoverageAbsolute(coverage.dateRange, input.timezone, fixedOffsetMinutes)),
|
|
696
|
+
defaults: {
|
|
697
|
+
slotStepMinutes: Math.max(1, Number(input.slotStepMinutes || DEFAULT_SLOT_STEP_MINUTES) || DEFAULT_SLOT_STEP_MINUTES),
|
|
698
|
+
businessHours: _objectSpread({}, businessHours),
|
|
699
|
+
operatingHours: operatingHours
|
|
700
|
+
},
|
|
701
|
+
now: now,
|
|
702
|
+
durationCandidateAnchorAt: durationCandidateAnchorAt,
|
|
703
|
+
anchors: input.anchors ? cloneSnapshotMetadataValue(input.anchors) : undefined,
|
|
704
|
+
prepareParams: input.prepareParams ? cloneSnapshotMetadataValue(input.prepareParams) : undefined
|
|
705
|
+
};
|
|
706
|
+
var projectionBase = {
|
|
707
|
+
meta: meta,
|
|
708
|
+
products: products,
|
|
709
|
+
resources: resources,
|
|
710
|
+
resourceOccupancies: resourceOccupancies
|
|
754
711
|
};
|
|
712
|
+
return deepFreeze(_objectSpread(_objectSpread({}, projectionBase), {}, {
|
|
713
|
+
cells: buildCells(projectionBase)
|
|
714
|
+
}));
|
|
755
715
|
}
|
|
756
|
-
function
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
return groups.reduce(function (combinations, group) {
|
|
761
|
-
return combinations.flatMap(function (combination) {
|
|
762
|
-
return group.map(function (candidate) {
|
|
763
|
-
return [].concat(_toConsumableArray(combination), [candidate]);
|
|
764
|
-
});
|
|
765
|
-
});
|
|
766
|
-
}, [[]]);
|
|
767
|
-
}
|
|
768
|
-
function buildSlotFromCandidates(params) {
|
|
769
|
-
var interval = params.interval,
|
|
770
|
-
candidates = params.candidates,
|
|
771
|
-
start = params.start,
|
|
772
|
-
end = params.end;
|
|
773
|
-
if (!end.isAfter(start)) return null;
|
|
774
|
-
var status = candidates.some(function (candidate) {
|
|
775
|
-
return candidate.cell.status === 'limited';
|
|
776
|
-
}) ? 'limited' : 'available';
|
|
777
|
-
var assignments = candidates.map(function (candidate) {
|
|
778
|
-
return {
|
|
779
|
-
productId: interval.productId,
|
|
780
|
-
resourceId: candidate.resource.id,
|
|
781
|
-
start_at: start.toISOString(),
|
|
782
|
-
end_at: end.toISOString(),
|
|
783
|
-
capacityRequired: Math.max(1, Number(candidate.requirement.capacityRequired || interval.capacityRequired || 1))
|
|
784
|
-
};
|
|
716
|
+
function uniqByOccupancy(occupancies) {
|
|
717
|
+
var map = new Map();
|
|
718
|
+
occupancies.forEach(function (occupancy) {
|
|
719
|
+
map.set(dedupeOccupancyRefKey(occupancy), occupancy);
|
|
785
720
|
});
|
|
786
|
-
return
|
|
787
|
-
key: [interval.productId, start.toISOString(), end.toISOString()].concat(_toConsumableArray(candidates.map(function (candidate) {
|
|
788
|
-
return candidate.resource.id;
|
|
789
|
-
}))).join(':'),
|
|
790
|
-
productId: interval.productId,
|
|
791
|
-
date: interval.date,
|
|
792
|
-
start_at: start.toISOString(),
|
|
793
|
-
end_at: end.toISOString(),
|
|
794
|
-
startTime: start.format('HH:mm'),
|
|
795
|
-
endTime: end.format('HH:mm'),
|
|
796
|
-
durationMinutes: Math.max(1, end.diff(start, 'minute')),
|
|
797
|
-
status: status,
|
|
798
|
-
resources: candidates.map(function (candidate) {
|
|
799
|
-
return {
|
|
800
|
-
resourceId: candidate.resource.id,
|
|
801
|
-
resourceName: candidate.resource.name,
|
|
802
|
-
resourceFormId: candidate.resource.formId,
|
|
803
|
-
resourceType: candidate.resource.resourceType,
|
|
804
|
-
capacityRequired: Math.max(1, Number(candidate.requirement.capacityRequired || interval.capacityRequired || 1)),
|
|
805
|
-
remainingCapacity: candidate.cell.remainingCapacity,
|
|
806
|
-
maxCapacity: candidate.cell.maxCapacity
|
|
807
|
-
};
|
|
808
|
-
}),
|
|
809
|
-
assignments: assignments,
|
|
810
|
-
conflicts: []
|
|
811
|
-
};
|
|
721
|
+
return Array.from(map.values());
|
|
812
722
|
}
|
|
813
|
-
function
|
|
814
|
-
var
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
var end = candidates.reduce(function (current, candidate) {
|
|
822
|
-
return minDayjs(current, dayjs(candidate.cell.end_at));
|
|
823
|
-
}, dayjs(interval.end_at));
|
|
824
|
-
var durationMinutes = Math.max(1, Number(interval.durationMinutes || 1));
|
|
825
|
-
if (interval.source !== 'duration' || query.startTime) {
|
|
826
|
-
var fixedStart = dayjs(interval.start_at);
|
|
827
|
-
var fixedEnd = dayjs(interval.end_at);
|
|
828
|
-
if (containsRange(start, end, fixedStart, fixedEnd) || candidates.every(function (candidate) {
|
|
829
|
-
return candidate.cell.cellType === 'fixed_interval';
|
|
830
|
-
})) {
|
|
831
|
-
var slot = buildSlotFromCandidates({
|
|
832
|
-
interval: interval,
|
|
833
|
-
candidates: candidates,
|
|
834
|
-
start: fixedStart,
|
|
835
|
-
end: fixedEnd
|
|
836
|
-
});
|
|
837
|
-
return slot ? [slot] : [];
|
|
723
|
+
function resolveProjectionDateRange(projection, requestDateRange) {
|
|
724
|
+
var startDate = (requestDateRange === null || requestDateRange === void 0 ? void 0 : requestDateRange.startDate) || projection.meta.coverage.startDate;
|
|
725
|
+
var endDate = (requestDateRange === null || requestDateRange === void 0 ? void 0 : requestDateRange.endDate) || projection.meta.coverage.endDate;
|
|
726
|
+
var isCanonicalDate = function isCanonicalDate(value) {
|
|
727
|
+
try {
|
|
728
|
+
return formatLocalDate(parseLocalDate(value)) === value;
|
|
729
|
+
} catch (_error) {
|
|
730
|
+
return false;
|
|
838
731
|
}
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
var cursor = start;
|
|
844
|
-
while (!cursor.add(durationMinutes, 'minute').isAfter(end)) {
|
|
845
|
-
var slotEnd = cursor.add(durationMinutes, 'minute');
|
|
846
|
-
var _slot = buildSlotFromCandidates({
|
|
847
|
-
interval: interval,
|
|
848
|
-
candidates: candidates,
|
|
849
|
-
start: cursor,
|
|
850
|
-
end: slotEnd
|
|
732
|
+
};
|
|
733
|
+
if (!isCanonicalDate(startDate) || !isCanonicalDate(endDate) || endDate < startDate) {
|
|
734
|
+
throw availabilityError('INVALID_DATE_RANGE', '查询日期范围必须是有效的 YYYY-MM-DD,且结束日期不能早于开始日期', {
|
|
735
|
+
requested: requestDateRange
|
|
851
736
|
});
|
|
852
|
-
if (_slot) slots.push(_slot);
|
|
853
|
-
cursor = cursor.add(stepMinutes, 'minute');
|
|
854
737
|
}
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
var context = normalizePlannerContext(contextInput);
|
|
860
|
-
var projection = queryPlannerAvailability(context, query);
|
|
861
|
-
var resources = getQueryResources(context.resources, query);
|
|
862
|
-
var slotsByKey = new Map();
|
|
863
|
-
var conflicts = [];
|
|
864
|
-
projection.demandIntervals.forEach(function (interval) {
|
|
865
|
-
var _buildAssignableCandi = buildAssignableCandidateGroups({
|
|
866
|
-
interval: interval,
|
|
867
|
-
cells: projection.cells,
|
|
868
|
-
resources: resources
|
|
869
|
-
}),
|
|
870
|
-
groups = _buildAssignableCandi.groups,
|
|
871
|
-
groupConflicts = _buildAssignableCandi.conflicts;
|
|
872
|
-
conflicts.push.apply(conflicts, _toConsumableArray(groupConflicts));
|
|
873
|
-
combineCandidateGroups(groups).forEach(function (candidates) {
|
|
874
|
-
buildSlotsForInterval({
|
|
875
|
-
interval: interval,
|
|
876
|
-
candidates: candidates,
|
|
877
|
-
query: query,
|
|
878
|
-
context: context
|
|
879
|
-
}).forEach(function (slot) {
|
|
880
|
-
if (!slotsByKey.has(slot.key)) slotsByKey.set(slot.key, slot);
|
|
881
|
-
});
|
|
738
|
+
if (startDate < projection.meta.coverage.startDate || endDate > projection.meta.coverage.endDate) {
|
|
739
|
+
throw availabilityError('OUT_OF_PROJECTION_COVERAGE', '查询范围超出 projection.coverage', {
|
|
740
|
+
requested: requestDateRange,
|
|
741
|
+
coverage: projection.meta.coverage
|
|
882
742
|
});
|
|
883
|
-
}
|
|
743
|
+
}
|
|
884
744
|
return {
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
conflicts: conflicts
|
|
745
|
+
startDate: startDate,
|
|
746
|
+
endDate: endDate
|
|
888
747
|
};
|
|
889
748
|
}
|
|
890
|
-
|
|
891
|
-
var
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
|
|
930
|
-
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
|
|
937
|
-
|
|
749
|
+
function buildOperatingRanges(projection, dateRange) {
|
|
750
|
+
var coverage = {
|
|
751
|
+
start: toDateStart(dateRange.startDate, projection.meta.timezone),
|
|
752
|
+
end: toDateEnd(nextDate(dateRange.endDate), projection.meta.timezone)
|
|
753
|
+
};
|
|
754
|
+
return mergeRanges(projection.meta.defaults.operatingHours.windows.map(function (window) {
|
|
755
|
+
return normalizeAbsoluteRange(window, projection.meta.timezone);
|
|
756
|
+
}).filter(function (window) {
|
|
757
|
+
return Boolean(window);
|
|
758
|
+
}).map(function (window) {
|
|
759
|
+
return clipRange(window, coverage);
|
|
760
|
+
}).filter(function (window) {
|
|
761
|
+
return Boolean(window);
|
|
762
|
+
}));
|
|
763
|
+
}
|
|
764
|
+
function buildResourceWindowIndex(projection) {
|
|
765
|
+
var coverage = {
|
|
766
|
+
start: toDateTime(projection.meta.coverage.startAt, projection.meta.timezone),
|
|
767
|
+
end: toDateTime(projection.meta.coverage.endAt, projection.meta.timezone)
|
|
768
|
+
};
|
|
769
|
+
return new Map(projection.resources.map(function (resource) {
|
|
770
|
+
return [String(resource.id), normalizeResourceWindow(resource, coverage, projection.meta.timezone)];
|
|
771
|
+
}));
|
|
772
|
+
}
|
|
773
|
+
function buildGroupWorkingRanges(params) {
|
|
774
|
+
var _requirementGroup$min;
|
|
775
|
+
var projection = params.projection,
|
|
776
|
+
requirementGroup = params.requirementGroup,
|
|
777
|
+
dateRange = params.dateRange,
|
|
778
|
+
resourceIds = params.resourceIds,
|
|
779
|
+
partySize = params.partySize;
|
|
780
|
+
var guardEndDate = nextDate(dateRange.endDate, projection.meta.timezone);
|
|
781
|
+
var coverage = {
|
|
782
|
+
start: toDateStart(dateRange.startDate, projection.meta.timezone),
|
|
783
|
+
end: toDateEnd(guardEndDate, projection.meta.timezone)
|
|
784
|
+
};
|
|
785
|
+
var filteredResourceIdSet = resourceIds !== null && resourceIds !== void 0 && resourceIds.length ? new Set(resourceIds.map(String)) : null;
|
|
786
|
+
var resourceWindows = projection.resources.filter(function (resource) {
|
|
787
|
+
return matchesRequirementGroup(requirementGroup, resource, params.product, partySize);
|
|
788
|
+
}).filter(function (resource) {
|
|
789
|
+
return !filteredResourceIdSet || filteredResourceIdSet.has(String(resource.id));
|
|
790
|
+
}).map(function (resource) {
|
|
791
|
+
var _params$resourceWindo;
|
|
792
|
+
var windows = ((_params$resourceWindo = params.resourceWindowIndex) === null || _params$resourceWindo === void 0 ? void 0 : _params$resourceWindo.get(String(resource.id))) || normalizeResourceWindow(resource, coverage, projection.meta.timezone);
|
|
793
|
+
return windows.map(function (window) {
|
|
794
|
+
return clipRange(window, coverage);
|
|
795
|
+
}).filter(function (window) {
|
|
796
|
+
return Boolean(window);
|
|
797
|
+
});
|
|
798
|
+
});
|
|
799
|
+
var requiredConcurrentResources = Math.max(requirementGroup.required === false ? 0 : 1, Number((_requirementGroup$min = requirementGroup.min) !== null && _requirementGroup$min !== void 0 ? _requirementGroup$min : requirementGroup.required === false ? 0 : 1) || 0);
|
|
800
|
+
if (requiredConcurrentResources <= 1) {
|
|
801
|
+
return mergeRanges(resourceWindows.flatMap(function (windows) {
|
|
802
|
+
return windows;
|
|
803
|
+
}));
|
|
804
|
+
}
|
|
805
|
+
var boundaries = new Set();
|
|
806
|
+
resourceWindows.forEach(function (windows) {
|
|
807
|
+
windows.forEach(function (window) {
|
|
808
|
+
boundaries.add(window.start);
|
|
809
|
+
boundaries.add(window.end);
|
|
810
|
+
});
|
|
811
|
+
});
|
|
812
|
+
var sortedBoundaries = Array.from(boundaries.values()).sort(function (left, right) {
|
|
813
|
+
return left - right;
|
|
814
|
+
});
|
|
815
|
+
var satisfiedRanges = [];
|
|
816
|
+
var _loop2 = function _loop2() {
|
|
817
|
+
var start = sortedBoundaries[index];
|
|
818
|
+
var end = sortedBoundaries[index + 1];
|
|
819
|
+
if (end <= start) return 1; // continue
|
|
820
|
+
var segment = {
|
|
821
|
+
start: start,
|
|
822
|
+
end: end
|
|
823
|
+
};
|
|
824
|
+
var activeResources = resourceWindows.reduce(function (count, windows) {
|
|
825
|
+
return windows.some(function (window) {
|
|
826
|
+
return contains(window, segment);
|
|
827
|
+
}) ? count + 1 : count;
|
|
828
|
+
}, 0);
|
|
829
|
+
if (activeResources >= requiredConcurrentResources) {
|
|
830
|
+
satisfiedRanges.push(segment);
|
|
831
|
+
}
|
|
832
|
+
};
|
|
833
|
+
for (var index = 0; index < sortedBoundaries.length - 1; index += 1) {
|
|
834
|
+
if (_loop2()) continue;
|
|
835
|
+
}
|
|
836
|
+
return mergeRanges(satisfiedRanges);
|
|
837
|
+
}
|
|
838
|
+
function materializeScheduleRules(rules, dateRange, timezoneName, fixedOffsetMinutes) {
|
|
839
|
+
var dates = enumerateDates(dateRange.startDate, dateRange.endDate);
|
|
840
|
+
var seen = new Map();
|
|
841
|
+
rules.forEach(function (rule) {
|
|
842
|
+
dates.forEach(function (date) {
|
|
843
|
+
if (!matchesScheduleRuleDate(rule, date, timezoneName)) return;
|
|
844
|
+
var start = toDateTimeInDate(date, rule.startTime, timezoneName);
|
|
845
|
+
var end = toDateTimeInDate(date, rule.endTime, timezoneName);
|
|
846
|
+
if (end <= start) end = toDateTimeInDate(nextDate(date, timezoneName), rule.endTime, timezoneName);
|
|
847
|
+
var key = "".concat(date, ":").concat(start, ":").concat(end);
|
|
848
|
+
if (!seen.has(key)) {
|
|
849
|
+
seen.set(key, {
|
|
850
|
+
key: key,
|
|
851
|
+
productId: '',
|
|
852
|
+
source: 'session_schedule',
|
|
853
|
+
startAt: formatDateTime(start, timezoneName, fixedOffsetMinutes),
|
|
854
|
+
endAt: formatDateTime(end, timezoneName, fixedOffsetMinutes),
|
|
855
|
+
bookingStartAt: formatDateTime(start, timezoneName, fixedOffsetMinutes),
|
|
856
|
+
bookingEndAt: formatDateTime(end, timezoneName, fixedOffsetMinutes),
|
|
857
|
+
date: formatDate(start, timezoneName),
|
|
858
|
+
startTime: formatTime(start, timezoneName),
|
|
859
|
+
endTime: formatTime(end, timezoneName),
|
|
860
|
+
timezone: timezoneName,
|
|
861
|
+
scheduleRefs: []
|
|
862
|
+
});
|
|
863
|
+
}
|
|
864
|
+
var current = seen.get(key);
|
|
865
|
+
var scheduleRef = _objectSpread(_objectSpread({}, rule.scheduleRef || {}), {}, {
|
|
866
|
+
ruleId: rule.id
|
|
867
|
+
});
|
|
868
|
+
current.scheduleRefs = mergeScheduleRefs(current.scheduleRefs, [scheduleRef]);
|
|
869
|
+
if (!current.primaryScheduleRef) current.primaryScheduleRef = scheduleRef;
|
|
870
|
+
});
|
|
871
|
+
});
|
|
872
|
+
return Array.from(seen.values());
|
|
873
|
+
}
|
|
874
|
+
function matchesScheduleRuleDate(rule, date, _timezoneName) {
|
|
875
|
+
var _rule$includeDates, _rule$excludeDates;
|
|
876
|
+
if ((_rule$includeDates = rule.includeDates) !== null && _rule$includeDates !== void 0 && _rule$includeDates.includes(date)) return true;
|
|
877
|
+
if ((_rule$excludeDates = rule.excludeDates) !== null && _rule$excludeDates !== void 0 && _rule$excludeDates.includes(date)) return false;
|
|
878
|
+
if (rule.startDate && date < rule.startDate) return false;
|
|
879
|
+
if (rule.endDate && date > rule.endDate) return false;
|
|
880
|
+
if (rule.mode === 'designation') {
|
|
881
|
+
var _rule$dates;
|
|
882
|
+
if ((_rule$dates = rule.dates) !== null && _rule$dates !== void 0 && _rule$dates.includes(date)) return true;
|
|
883
|
+
return false;
|
|
884
|
+
}
|
|
885
|
+
if (rule.mode === 'weekly') {
|
|
886
|
+
var _rule$weekdays;
|
|
887
|
+
if (!((_rule$weekdays = rule.weekdays) !== null && _rule$weekdays !== void 0 && _rule$weekdays.length)) return true;
|
|
888
|
+
var freq = Math.max(1, Number(rule.frequency || 1) || 1);
|
|
889
|
+
if (!rule.weekdays.includes(weekdayFromLocalDate(date))) return false;
|
|
890
|
+
if (!rule.startDate) return true;
|
|
891
|
+
var targetWeekStart = dateOrdinalFromLocalDate(date) - weekdayFromLocalDate(date);
|
|
892
|
+
var ruleWeekStart = dateOrdinalFromLocalDate(rule.startDate) - weekdayFromLocalDate(rule.startDate);
|
|
893
|
+
return Math.floor((targetWeekStart - ruleWeekStart) / 7) % freq === 0;
|
|
894
|
+
}
|
|
895
|
+
if (rule.mode === 'daily' && rule.startDate) {
|
|
896
|
+
var _freq = Math.max(1, Number(rule.frequency || 1) || 1);
|
|
897
|
+
return (dateOrdinalFromLocalDate(date) - dateOrdinalFromLocalDate(rule.startDate)) % _freq === 0;
|
|
898
|
+
}
|
|
899
|
+
return true;
|
|
900
|
+
}
|
|
901
|
+
function mergeScheduleRefs(base, next) {
|
|
902
|
+
var map = new Map();
|
|
903
|
+
[].concat(_toConsumableArray(base), _toConsumableArray(next)).forEach(function (ref) {
|
|
904
|
+
var _ref$scheduleId, _ref$timeSlotId, _ref$ruleId, _ref$source, _ref$label;
|
|
905
|
+
var key = [(_ref$scheduleId = ref.scheduleId) !== null && _ref$scheduleId !== void 0 ? _ref$scheduleId : '', (_ref$timeSlotId = ref.timeSlotId) !== null && _ref$timeSlotId !== void 0 ? _ref$timeSlotId : '', (_ref$ruleId = ref.ruleId) !== null && _ref$ruleId !== void 0 ? _ref$ruleId : '', (_ref$source = ref.source) !== null && _ref$source !== void 0 ? _ref$source : '', (_ref$label = ref.label) !== null && _ref$label !== void 0 ? _ref$label : ''].join(':');
|
|
906
|
+
map.set(key, ref);
|
|
907
|
+
});
|
|
908
|
+
return Array.from(map.values());
|
|
909
|
+
}
|
|
910
|
+
function buildProductIndex(projection) {
|
|
911
|
+
return new Map(projection.products.map(function (product) {
|
|
912
|
+
return [String(product.id), product];
|
|
913
|
+
}));
|
|
914
|
+
}
|
|
915
|
+
function buildGroupResourceIndex(params) {
|
|
916
|
+
var _params$resourceIds;
|
|
917
|
+
var resourceIdSet = (_params$resourceIds = params.resourceIds) !== null && _params$resourceIds !== void 0 && _params$resourceIds.length ? new Set(params.resourceIds.map(String)) : null;
|
|
918
|
+
var index = new Map();
|
|
919
|
+
params.projection.products.forEach(function (product) {
|
|
920
|
+
(product.requirementGroups || []).forEach(function (requirementGroup) {
|
|
921
|
+
var key = [String(product.id), requirementGroup.id].join(':');
|
|
922
|
+
index.set(key, params.projection.resources.filter(function (resource) {
|
|
923
|
+
return matchesRequirementGroup(requirementGroup, resource, product, params.partySize);
|
|
924
|
+
}).filter(function (resource) {
|
|
925
|
+
return !resourceIdSet || resourceIdSet.has(String(resource.id));
|
|
926
|
+
}));
|
|
927
|
+
});
|
|
928
|
+
});
|
|
929
|
+
return index;
|
|
930
|
+
}
|
|
931
|
+
function getSessionRangesForProduct(projection, product, dateRange) {
|
|
932
|
+
var _extractFixedOffsetMi2;
|
|
933
|
+
var fixedOffsetMinutes = (_extractFixedOffsetMi2 = extractFixedOffsetMinutes(projection.meta.now)) !== null && _extractFixedOffsetMi2 !== void 0 ? _extractFixedOffsetMi2 : captureRuntimeOffsetMinutes();
|
|
934
|
+
var ranges = (product.sessionRanges || []).map(function (range) {
|
|
935
|
+
var _range$scheduleRefs2;
|
|
936
|
+
var normalized = normalizeAbsoluteRange(range, projection.meta.timezone);
|
|
937
|
+
if (!normalized) return null;
|
|
938
|
+
var startDate = formatDate(normalized.start, projection.meta.timezone);
|
|
939
|
+
if (startDate < dateRange.startDate || startDate > dateRange.endDate) {
|
|
940
|
+
return null;
|
|
941
|
+
}
|
|
942
|
+
return {
|
|
943
|
+
key: "".concat(String(product.id), ":").concat(normalized.start, ":").concat(normalized.end),
|
|
944
|
+
productId: product.id,
|
|
945
|
+
source: 'session_schedule',
|
|
946
|
+
startAt: formatDateTime(normalized.start, projection.meta.timezone, fixedOffsetMinutes),
|
|
947
|
+
endAt: formatDateTime(normalized.end, projection.meta.timezone, fixedOffsetMinutes),
|
|
948
|
+
bookingStartAt: formatDateTime(normalized.start, projection.meta.timezone, fixedOffsetMinutes),
|
|
949
|
+
bookingEndAt: formatDateTime(normalized.end, projection.meta.timezone, fixedOffsetMinutes),
|
|
950
|
+
date: formatDate(normalized.start, projection.meta.timezone),
|
|
951
|
+
startTime: formatTime(normalized.start, projection.meta.timezone),
|
|
952
|
+
endTime: formatTime(normalized.end, projection.meta.timezone),
|
|
953
|
+
timezone: projection.meta.timezone,
|
|
954
|
+
scheduleRefs: range.scheduleRefs || [],
|
|
955
|
+
primaryScheduleRef: (_range$scheduleRefs2 = range.scheduleRefs) === null || _range$scheduleRefs2 === void 0 ? void 0 : _range$scheduleRefs2[0]
|
|
956
|
+
};
|
|
957
|
+
}).filter(Boolean);
|
|
958
|
+
var ruleRanges = materializeScheduleRules(product.sessionRules || [], dateRange, projection.meta.timezone, fixedOffsetMinutes);
|
|
959
|
+
return dedupeProductRanges([].concat(_toConsumableArray(ranges), _toConsumableArray(ruleRanges.map(function (range) {
|
|
960
|
+
return _objectSpread(_objectSpread({}, range), {}, {
|
|
961
|
+
key: "".concat(String(product.id), ":").concat(range.startAt, ":").concat(range.endAt),
|
|
962
|
+
productId: product.id,
|
|
963
|
+
timezone: projection.meta.timezone
|
|
964
|
+
});
|
|
965
|
+
}))));
|
|
966
|
+
}
|
|
967
|
+
function dedupeProductRanges(ranges) {
|
|
968
|
+
var map = new Map();
|
|
969
|
+
ranges.forEach(function (range) {
|
|
970
|
+
var key = getProductTimeRangeIdentity(range);
|
|
971
|
+
if (!map.has(key)) {
|
|
972
|
+
map.set(key, _objectSpread(_objectSpread({}, range), {}, {
|
|
973
|
+
key: key
|
|
974
|
+
}));
|
|
975
|
+
return;
|
|
976
|
+
}
|
|
977
|
+
var existing = map.get(key);
|
|
978
|
+
existing.scheduleRefs = mergeScheduleRefs(existing.scheduleRefs, range.scheduleRefs || []);
|
|
979
|
+
if (!existing.primaryScheduleRef) existing.primaryScheduleRef = range.primaryScheduleRef;
|
|
980
|
+
});
|
|
981
|
+
return Array.from(map.values()).sort(function (left, right) {
|
|
982
|
+
return left.startAt.localeCompare(right.startAt);
|
|
983
|
+
});
|
|
984
|
+
}
|
|
985
|
+
function createDurationTimeRange(product, start, durationMinutes, projection, fixedOffsetMinutes) {
|
|
986
|
+
var bookingEnd = addMinutesZoned(start, durationMinutes, projection.meta.timezone);
|
|
987
|
+
return createDurationTimeRangeWithEnd(product, start, bookingEnd, projection, fixedOffsetMinutes);
|
|
988
|
+
}
|
|
989
|
+
function createDurationTimeRangeWithEnd(product, start, bookingEnd, projection, fixedOffsetMinutes) {
|
|
990
|
+
return {
|
|
991
|
+
key: "".concat(String(product.id), ":").concat(start, ":").concat(bookingEnd),
|
|
992
|
+
productId: product.id,
|
|
993
|
+
source: 'duration_window',
|
|
994
|
+
startAt: formatDateTime(start, projection.meta.timezone, fixedOffsetMinutes),
|
|
995
|
+
endAt: formatDateTime(bookingEnd, projection.meta.timezone, fixedOffsetMinutes),
|
|
996
|
+
bookingStartAt: formatDateTime(start, projection.meta.timezone, fixedOffsetMinutes),
|
|
997
|
+
bookingEndAt: formatDateTime(bookingEnd, projection.meta.timezone, fixedOffsetMinutes),
|
|
998
|
+
date: formatDate(start, projection.meta.timezone),
|
|
999
|
+
startTime: formatTime(start, projection.meta.timezone),
|
|
1000
|
+
endTime: formatTime(bookingEnd, projection.meta.timezone),
|
|
1001
|
+
timezone: projection.meta.timezone,
|
|
1002
|
+
scheduleRefs: []
|
|
1003
|
+
};
|
|
1004
|
+
}
|
|
1005
|
+
export function getProductTimeRanges(projection) {
|
|
1006
|
+
var _extractFixedOffsetMi3, _request$productIds;
|
|
1007
|
+
var request = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
|
|
1008
|
+
var runtimeOptions = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {};
|
|
1009
|
+
var normalizedPartySize = normalizePartySize(request.partySize);
|
|
1010
|
+
var dateRange = resolveProjectionDateRange(projection, request.dateRange);
|
|
1011
|
+
var slotStepMinutes = projection.meta.defaults.slotStepMinutes;
|
|
1012
|
+
var fixedOffsetMinutes = (_extractFixedOffsetMi3 = extractFixedOffsetMinutes(projection.meta.now)) !== null && _extractFixedOffsetMi3 !== void 0 ? _extractFixedOffsetMi3 : captureRuntimeOffsetMinutes();
|
|
1013
|
+
var resourceWindowIndex = buildResourceWindowIndex(projection);
|
|
1014
|
+
var filteredProductIdSet = (_request$productIds = request.productIds) !== null && _request$productIds !== void 0 && _request$productIds.length ? new Set(request.productIds.map(String)) : null;
|
|
1015
|
+
return projection.products.filter(function (product) {
|
|
1016
|
+
return !filteredProductIdSet || filteredProductIdSet.has(String(product.id));
|
|
1017
|
+
}).map(function (product) {
|
|
1018
|
+
if (product.kind === 'normal') {
|
|
1019
|
+
return {
|
|
1020
|
+
productId: product.id,
|
|
1021
|
+
title: product.title,
|
|
1022
|
+
kind: product.kind,
|
|
1023
|
+
slotStepMinutes: slotStepMinutes,
|
|
1024
|
+
ranges: []
|
|
1025
|
+
};
|
|
1026
|
+
}
|
|
1027
|
+
if (product.kind === 'session' || product.kind === 'venue_slot') {
|
|
1028
|
+
return {
|
|
1029
|
+
productId: product.id,
|
|
1030
|
+
title: product.title,
|
|
1031
|
+
kind: product.kind,
|
|
1032
|
+
slotStepMinutes: slotStepMinutes,
|
|
1033
|
+
ranges: getSessionRangesForProduct(projection, product, dateRange)
|
|
1034
|
+
};
|
|
1035
|
+
}
|
|
1036
|
+
var requiredGroups = (product.requirementGroups || []).filter(function (group) {
|
|
1037
|
+
return group.required !== false;
|
|
1038
|
+
});
|
|
1039
|
+
var operatingRanges = buildOperatingRanges(projection, dateRange);
|
|
1040
|
+
var resourceRanges = requiredGroups.length ? requiredGroups.map(function (group) {
|
|
1041
|
+
return buildGroupWorkingRanges({
|
|
1042
|
+
projection: projection,
|
|
1043
|
+
product: product,
|
|
1044
|
+
requirementGroup: group,
|
|
1045
|
+
dateRange: dateRange,
|
|
1046
|
+
resourceIds: request.resourceIds,
|
|
1047
|
+
partySize: normalizedPartySize,
|
|
1048
|
+
resourceWindowIndex: resourceWindowIndex
|
|
1049
|
+
});
|
|
1050
|
+
}).reduce(function (current, ranges) {
|
|
1051
|
+
return current === null ? ranges : intersectRangeLists(current, ranges);
|
|
1052
|
+
}, null) || [] : null;
|
|
1053
|
+
var baseRanges = resourceRanges === null ? operatingRanges : intersectRangeLists(resourceRanges, operatingRanges);
|
|
1054
|
+
var durationMinutes = resolveRequestedDurationMinutes(product, request);
|
|
1055
|
+
var flexibleStartAt = function () {
|
|
1056
|
+
if (!product.isFlexibleDuration) return undefined;
|
|
1057
|
+
var evaluatedAt = toDateTime(runtimeOptions.evaluatedAt || projection.meta.durationCandidateAnchorAt, projection.meta.timezone);
|
|
1058
|
+
if (!Number.isFinite(evaluatedAt)) {
|
|
1059
|
+
throw availabilityError('INVALID_TARGET_FILTER', 'runtimeOptions.evaluatedAt 必须是有效时间', {
|
|
1060
|
+
evaluatedAt: runtimeOptions.evaluatedAt
|
|
1061
|
+
});
|
|
1062
|
+
}
|
|
1063
|
+
return floorToMinute(evaluatedAt);
|
|
1064
|
+
}();
|
|
1065
|
+
var durationAnchor = ceilToTenMinutes(toDateTime(projection.meta.durationCandidateAnchorAt, projection.meta.timezone), projection.meta.timezone);
|
|
1066
|
+
var requestedStartTimes = resolveRequestedDurationStartTimes(product, request);
|
|
1067
|
+
var ranges = [];
|
|
1068
|
+
baseRanges.forEach(function (range) {
|
|
1069
|
+
if (product.isFlexibleDuration && flexibleStartAt !== undefined) {
|
|
1070
|
+
var start = Math.max(range.start, flexibleStartAt);
|
|
1071
|
+
if (start >= range.end) return;
|
|
1072
|
+
var bookingDate = formatDate(start);
|
|
1073
|
+
if (bookingDate >= dateRange.startDate && bookingDate <= dateRange.endDate) {
|
|
1074
|
+
ranges.push(createDurationTimeRangeWithEnd(product, start, range.end, projection, fixedOffsetMinutes));
|
|
938
1075
|
}
|
|
939
|
-
|
|
940
|
-
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
|
|
944
|
-
|
|
1076
|
+
return;
|
|
1077
|
+
}
|
|
1078
|
+
if (range.end <= durationAnchor) return;
|
|
1079
|
+
var cursor = range.start;
|
|
1080
|
+
if (cursor < durationAnchor) {
|
|
1081
|
+
cursor = durationAnchor;
|
|
1082
|
+
}
|
|
1083
|
+
while (true) {
|
|
1084
|
+
var bookingEnd = addMinutesZoned(cursor, durationMinutes, projection.meta.timezone);
|
|
1085
|
+
if (bookingEnd > range.end) break;
|
|
1086
|
+
var _bookingDate = formatDate(cursor);
|
|
1087
|
+
if (_bookingDate >= dateRange.startDate && _bookingDate <= dateRange.endDate) {
|
|
1088
|
+
ranges.push(createDurationTimeRange(product, cursor, durationMinutes, projection, fixedOffsetMinutes));
|
|
1089
|
+
}
|
|
1090
|
+
cursor = addMinutesZoned(cursor, slotStepMinutes, projection.meta.timezone);
|
|
1091
|
+
}
|
|
1092
|
+
});
|
|
1093
|
+
if (dateRange.startDate === dateRange.endDate && requestedStartTimes.length > 0) {
|
|
1094
|
+
requestedStartTimes.forEach(function (startTime) {
|
|
1095
|
+
var start = toDateTimeInDate(dateRange.startDate, startTime, projection.meta.timezone);
|
|
1096
|
+
if (!Number.isFinite(start)) return;
|
|
1097
|
+
var end = addMinutesZoned(start, durationMinutes, projection.meta.timezone);
|
|
1098
|
+
var startsInOperatingRange = operatingRanges.some(function (range) {
|
|
1099
|
+
return range.start <= start && start < range.end;
|
|
945
1100
|
});
|
|
946
|
-
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
|
|
950
|
-
|
|
1101
|
+
if (!startsInOperatingRange && !request.allowOutsideOperatingHoursForRequestedStartTimes) {
|
|
1102
|
+
return;
|
|
1103
|
+
}
|
|
1104
|
+
ranges.push(createDurationTimeRange(product, start, durationMinutes, projection, fixedOffsetMinutes));
|
|
1105
|
+
});
|
|
1106
|
+
}
|
|
1107
|
+
return {
|
|
1108
|
+
productId: product.id,
|
|
1109
|
+
title: product.title,
|
|
1110
|
+
kind: product.kind,
|
|
1111
|
+
slotStepMinutes: slotStepMinutes,
|
|
1112
|
+
ranges: dedupeProductRanges(ranges)
|
|
1113
|
+
};
|
|
1114
|
+
});
|
|
1115
|
+
}
|
|
1116
|
+
function aggregateMatchedCells(productId, requirementGroupId, resourceId, candidateRange, indexedCells) {
|
|
1117
|
+
var _extractFixedOffsetMi4, _extractFixedOffsetMi5;
|
|
1118
|
+
var candidateStartMs = candidateRange.start;
|
|
1119
|
+
var candidateEndMs = candidateRange.end;
|
|
1120
|
+
var left = 0;
|
|
1121
|
+
var right = indexedCells.length;
|
|
1122
|
+
while (left < right) {
|
|
1123
|
+
var middle = Math.floor((left + right) / 2);
|
|
1124
|
+
if (indexedCells[middle].range.endMs <= candidateStartMs) {
|
|
1125
|
+
left = middle + 1;
|
|
1126
|
+
} else {
|
|
1127
|
+
right = middle;
|
|
1128
|
+
}
|
|
1129
|
+
}
|
|
1130
|
+
var overlapped = [];
|
|
1131
|
+
for (var index = Math.max(0, left - 1); index < indexedCells.length; index += 1) {
|
|
1132
|
+
var _indexedCells$index = indexedCells[index],
|
|
1133
|
+
cell = _indexedCells$index.cell,
|
|
1134
|
+
range = _indexedCells$index.range;
|
|
1135
|
+
if (range.startMs >= candidateEndMs) break;
|
|
1136
|
+
if (range.endMs <= candidateStartMs) continue;
|
|
1137
|
+
var clipped = clipRange(range, candidateRange);
|
|
1138
|
+
if (clipped) overlapped.push({
|
|
1139
|
+
cell: cell,
|
|
1140
|
+
range: clipped
|
|
1141
|
+
});
|
|
1142
|
+
}
|
|
1143
|
+
if (!overlapped.length) return undefined;
|
|
1144
|
+
var cursor = candidateRange.start;
|
|
1145
|
+
for (var _i = 0, _overlapped = overlapped; _i < _overlapped.length; _i++) {
|
|
1146
|
+
var item = _overlapped[_i];
|
|
1147
|
+
if (item.range.start > cursor) return undefined;
|
|
1148
|
+
if (item.range.end > cursor) {
|
|
1149
|
+
cursor = item.range.end;
|
|
1150
|
+
}
|
|
1151
|
+
if (cursor >= candidateRange.end) break;
|
|
1152
|
+
}
|
|
1153
|
+
if (cursor < candidateRange.end) return undefined;
|
|
1154
|
+
var cells = overlapped.map(function (item) {
|
|
1155
|
+
return item.cell;
|
|
1156
|
+
});
|
|
1157
|
+
var statuses = cells.map(function (cell) {
|
|
1158
|
+
return cell.status;
|
|
1159
|
+
});
|
|
1160
|
+
var nonPastStatuses = statuses.filter(function (status) {
|
|
1161
|
+
return status !== 'past';
|
|
1162
|
+
});
|
|
1163
|
+
var aggregatedStatus = function () {
|
|
1164
|
+
if (nonPastStatuses.some(function (status) {
|
|
1165
|
+
return status === 'unavailable';
|
|
1166
|
+
})) return 'unavailable';
|
|
1167
|
+
if (nonPastStatuses.some(function (status) {
|
|
1168
|
+
return status === 'full';
|
|
1169
|
+
})) return 'full';
|
|
1170
|
+
if (nonPastStatuses.some(function (status) {
|
|
1171
|
+
return status === 'limited';
|
|
1172
|
+
})) return 'limited';
|
|
1173
|
+
if (nonPastStatuses.every(function (status) {
|
|
1174
|
+
return status === 'available';
|
|
1175
|
+
})) return 'available';
|
|
1176
|
+
if (statuses.every(function (status) {
|
|
1177
|
+
return status === 'past';
|
|
1178
|
+
})) return 'past';
|
|
1179
|
+
return 'available';
|
|
1180
|
+
}();
|
|
1181
|
+
var capacityCells = cells.filter(function (cell) {
|
|
1182
|
+
return cell.remainingCapacity !== undefined || cell.maxCapacity !== undefined;
|
|
1183
|
+
});
|
|
1184
|
+
var reasonCodes = Array.from(new Set(cells.flatMap(function (cell) {
|
|
1185
|
+
return cell.reasonCodes;
|
|
1186
|
+
})));
|
|
1187
|
+
var occupancyRefs = Array.from(new Map(cells.flatMap(function (cell) {
|
|
1188
|
+
return cell.occupancyRefs || [];
|
|
1189
|
+
}).map(function (ref) {
|
|
1190
|
+
var _ref$id, _ref$resourceId, _ref$serverEventId, _ref$bookingUid, _ref$productUid, _ref$scheduleEventId, _ref$source2;
|
|
1191
|
+
return [[(_ref$id = ref.id) !== null && _ref$id !== void 0 ? _ref$id : '', (_ref$resourceId = ref.resourceId) !== null && _ref$resourceId !== void 0 ? _ref$resourceId : '', (_ref$serverEventId = ref.serverEventId) !== null && _ref$serverEventId !== void 0 ? _ref$serverEventId : '', (_ref$bookingUid = ref.bookingUid) !== null && _ref$bookingUid !== void 0 ? _ref$bookingUid : '', (_ref$productUid = ref.productUid) !== null && _ref$productUid !== void 0 ? _ref$productUid : '', (_ref$scheduleEventId = ref.scheduleEventId) !== null && _ref$scheduleEventId !== void 0 ? _ref$scheduleEventId : '', (_ref$source2 = ref.source) !== null && _ref$source2 !== void 0 ? _ref$source2 : ''].join(':'), ref];
|
|
1192
|
+
})).values());
|
|
1193
|
+
var firstCell = cells[0];
|
|
1194
|
+
return _objectSpread(_objectSpread({}, firstCell), {}, {
|
|
1195
|
+
key: buildCellKey({
|
|
1196
|
+
productId: productId,
|
|
1197
|
+
groupId: requirementGroupId,
|
|
1198
|
+
resourceId: resourceId,
|
|
1199
|
+
start: candidateRange.start,
|
|
1200
|
+
end: candidateRange.end
|
|
1201
|
+
}),
|
|
1202
|
+
startAt: formatDateTime(candidateRange.start, firstCell.timezone, (_extractFixedOffsetMi4 = extractFixedOffsetMinutes(firstCell.startAt)) !== null && _extractFixedOffsetMi4 !== void 0 ? _extractFixedOffsetMi4 : captureRuntimeOffsetMinutes()),
|
|
1203
|
+
endAt: formatDateTime(candidateRange.end, firstCell.timezone, (_extractFixedOffsetMi5 = extractFixedOffsetMinutes(firstCell.startAt)) !== null && _extractFixedOffsetMi5 !== void 0 ? _extractFixedOffsetMi5 : captureRuntimeOffsetMinutes()),
|
|
1204
|
+
date: formatDate(candidateRange.start, firstCell.timezone),
|
|
1205
|
+
startTime: formatTime(candidateRange.start, firstCell.timezone),
|
|
1206
|
+
endTime: formatTime(candidateRange.end, firstCell.timezone),
|
|
1207
|
+
status: aggregatedStatus,
|
|
1208
|
+
remainingCapacity: capacityCells.length ? Math.min.apply(Math, _toConsumableArray(capacityCells.map(function (cell) {
|
|
1209
|
+
var _cell$remainingCapaci;
|
|
1210
|
+
return (_cell$remainingCapaci = cell.remainingCapacity) !== null && _cell$remainingCapaci !== void 0 ? _cell$remainingCapaci : Number.MAX_SAFE_INTEGER;
|
|
1211
|
+
}))) : undefined,
|
|
1212
|
+
maxCapacity: capacityCells.length ? Math.min.apply(Math, _toConsumableArray(capacityCells.map(function (cell) {
|
|
1213
|
+
var _cell$maxCapacity;
|
|
1214
|
+
return (_cell$maxCapacity = cell.maxCapacity) !== null && _cell$maxCapacity !== void 0 ? _cell$maxCapacity : Number.MAX_SAFE_INTEGER;
|
|
1215
|
+
}))) : undefined,
|
|
1216
|
+
reasonCodes: reasonCodes,
|
|
1217
|
+
occupancyRefs: occupancyRefs
|
|
1218
|
+
});
|
|
1219
|
+
}
|
|
1220
|
+
function aggregateMatchedCellSummary(candidateRange, indexedCells) {
|
|
1221
|
+
var candidateStartMs = candidateRange.startMs;
|
|
1222
|
+
var candidateEndMs = candidateRange.endMs;
|
|
1223
|
+
var left = 0;
|
|
1224
|
+
var right = indexedCells.length;
|
|
1225
|
+
while (left < right) {
|
|
1226
|
+
var middle = Math.floor((left + right) / 2);
|
|
1227
|
+
if (indexedCells[middle].range.endMs <= candidateStartMs) {
|
|
1228
|
+
left = middle + 1;
|
|
1229
|
+
} else {
|
|
1230
|
+
right = middle;
|
|
951
1231
|
}
|
|
952
|
-
} catch (err) {
|
|
953
|
-
_iterator5.e(err);
|
|
954
|
-
} finally {
|
|
955
|
-
_iterator5.f();
|
|
956
1232
|
}
|
|
1233
|
+
var cells = [];
|
|
1234
|
+
var cursorMs = candidateStartMs;
|
|
1235
|
+
for (var index = Math.max(0, left - 1); index < indexedCells.length; index += 1) {
|
|
1236
|
+
var _indexedCells$index2 = indexedCells[index],
|
|
1237
|
+
cell = _indexedCells$index2.cell,
|
|
1238
|
+
range = _indexedCells$index2.range;
|
|
1239
|
+
if (range.startMs >= candidateEndMs) break;
|
|
1240
|
+
if (range.endMs <= candidateStartMs) continue;
|
|
1241
|
+
var clippedStartMs = Math.max(range.startMs, candidateStartMs);
|
|
1242
|
+
var clippedEndMs = Math.min(range.endMs, candidateEndMs);
|
|
1243
|
+
if (clippedEndMs <= clippedStartMs) continue;
|
|
1244
|
+
if (clippedStartMs > cursorMs) return undefined;
|
|
1245
|
+
cells.push(cell);
|
|
1246
|
+
if (clippedEndMs > cursorMs) cursorMs = clippedEndMs;
|
|
1247
|
+
if (cursorMs >= candidateEndMs) break;
|
|
1248
|
+
}
|
|
1249
|
+
if (!cells.length || cursorMs < candidateEndMs) return undefined;
|
|
1250
|
+
var statuses = cells.map(function (cell) {
|
|
1251
|
+
return cell.status;
|
|
1252
|
+
});
|
|
1253
|
+
var nonPastStatuses = statuses.filter(function (status) {
|
|
1254
|
+
return status !== 'past';
|
|
1255
|
+
});
|
|
1256
|
+
var status = function () {
|
|
1257
|
+
if (nonPastStatuses.some(function (cellStatus) {
|
|
1258
|
+
return cellStatus === 'unavailable';
|
|
1259
|
+
})) return 'unavailable';
|
|
1260
|
+
if (nonPastStatuses.some(function (cellStatus) {
|
|
1261
|
+
return cellStatus === 'full';
|
|
1262
|
+
})) return 'full';
|
|
1263
|
+
if (nonPastStatuses.some(function (cellStatus) {
|
|
1264
|
+
return cellStatus === 'limited';
|
|
1265
|
+
})) return 'limited';
|
|
1266
|
+
if (nonPastStatuses.every(function (cellStatus) {
|
|
1267
|
+
return cellStatus === 'available';
|
|
1268
|
+
})) return 'available';
|
|
1269
|
+
if (statuses.every(function (cellStatus) {
|
|
1270
|
+
return cellStatus === 'past';
|
|
1271
|
+
})) return 'past';
|
|
1272
|
+
return 'available';
|
|
1273
|
+
}();
|
|
1274
|
+
var capacityCells = cells.filter(function (cell) {
|
|
1275
|
+
return cell.remainingCapacity !== undefined || cell.maxCapacity !== undefined;
|
|
1276
|
+
});
|
|
1277
|
+
return {
|
|
1278
|
+
status: status,
|
|
1279
|
+
remainingCapacity: capacityCells.length ? Math.min.apply(Math, _toConsumableArray(capacityCells.map(function (cell) {
|
|
1280
|
+
var _cell$remainingCapaci2;
|
|
1281
|
+
return (_cell$remainingCapaci2 = cell.remainingCapacity) !== null && _cell$remainingCapaci2 !== void 0 ? _cell$remainingCapaci2 : Number.MAX_SAFE_INTEGER;
|
|
1282
|
+
}))) : undefined,
|
|
1283
|
+
maxCapacity: capacityCells.length ? Math.min.apply(Math, _toConsumableArray(capacityCells.map(function (cell) {
|
|
1284
|
+
var _cell$maxCapacity2;
|
|
1285
|
+
return (_cell$maxCapacity2 = cell.maxCapacity) !== null && _cell$maxCapacity2 !== void 0 ? _cell$maxCapacity2 : Number.MAX_SAFE_INTEGER;
|
|
1286
|
+
}))) : undefined,
|
|
1287
|
+
reasonCodes: Array.from(new Set(cells.flatMap(function (cell) {
|
|
1288
|
+
return cell.reasonCodes;
|
|
1289
|
+
})))
|
|
1290
|
+
};
|
|
1291
|
+
}
|
|
1292
|
+
function buildAvailabilityCellIndex(projection) {
|
|
1293
|
+
var index = new Map();
|
|
1294
|
+
projection.cells.forEach(function (cell) {
|
|
1295
|
+
if (cell.requirementGroupId == null || cell.resourceId == null) return;
|
|
1296
|
+
var key = [String(cell.productId), cell.requirementGroupId, String(cell.resourceId)].join(':');
|
|
1297
|
+
var entries = index.get(key) || [];
|
|
1298
|
+
entries.push({
|
|
1299
|
+
cell: cell,
|
|
1300
|
+
range: toIndexedRange({
|
|
1301
|
+
start: toDateTime(cell.startAt, projection.meta.timezone),
|
|
1302
|
+
end: toDateTime(cell.endAt, projection.meta.timezone)
|
|
1303
|
+
})
|
|
1304
|
+
});
|
|
1305
|
+
index.set(key, entries);
|
|
1306
|
+
});
|
|
1307
|
+
index.forEach(function (entries, key) {
|
|
1308
|
+
index.set(key, entries.sort(function (left, right) {
|
|
1309
|
+
return left.range.startMs - right.range.startMs;
|
|
1310
|
+
}));
|
|
1311
|
+
});
|
|
1312
|
+
return index;
|
|
1313
|
+
}
|
|
1314
|
+
function buildEvaluationContext(params) {
|
|
1315
|
+
var _params$resourceIds2;
|
|
1316
|
+
var evaluatedAt = toDateTime(params.evaluatedAt || params.projection.meta.now, params.projection.meta.timezone);
|
|
1317
|
+
var selectionsByGroupKey = new Map();
|
|
1318
|
+
(params.requirementSelections || []).forEach(function (selection) {
|
|
1319
|
+
var _selection$formId;
|
|
1320
|
+
var key = selection.requirementGroupId || String((_selection$formId = selection.formId) !== null && _selection$formId !== void 0 ? _selection$formId : '');
|
|
1321
|
+
selectionsByGroupKey.set(key, selection);
|
|
1322
|
+
});
|
|
1323
|
+
return {
|
|
1324
|
+
evaluatedAt: evaluatedAt,
|
|
1325
|
+
evaluatedAtMs: evaluatedAt,
|
|
1326
|
+
resourceIdSet: (_params$resourceIds2 = params.resourceIds) !== null && _params$resourceIds2 !== void 0 && _params$resourceIds2.length ? new Set(params.resourceIds.map(String)) : null,
|
|
1327
|
+
selectionsByGroupKey: selectionsByGroupKey
|
|
1328
|
+
};
|
|
1329
|
+
}
|
|
1330
|
+
function summarizeStatuses(statuses) {
|
|
1331
|
+
if (!statuses.length) return 'unavailable';
|
|
1332
|
+
if (statuses.every(function (status) {
|
|
1333
|
+
return status === 'past';
|
|
1334
|
+
})) return 'past';
|
|
1335
|
+
if (statuses.some(function (status) {
|
|
1336
|
+
return status === 'unavailable';
|
|
1337
|
+
})) return 'unavailable';
|
|
1338
|
+
if (statuses.some(function (status) {
|
|
1339
|
+
return status === 'full';
|
|
1340
|
+
})) return 'full';
|
|
1341
|
+
if (statuses.some(function (status) {
|
|
1342
|
+
return status === 'limited';
|
|
1343
|
+
})) return 'limited';
|
|
1344
|
+
if (statuses.every(function (status) {
|
|
1345
|
+
return status === 'available';
|
|
1346
|
+
})) return 'available';
|
|
1347
|
+
if (statuses.some(function (status) {
|
|
1348
|
+
return status === 'past';
|
|
1349
|
+
})) return 'past';
|
|
1350
|
+
return statuses[0];
|
|
1351
|
+
}
|
|
1352
|
+
function createStatusAggregate() {
|
|
1353
|
+
return {
|
|
1354
|
+
count: 0,
|
|
1355
|
+
hasAvailable: false,
|
|
1356
|
+
hasLimited: false,
|
|
1357
|
+
hasFull: false,
|
|
1358
|
+
allPast: true
|
|
1359
|
+
};
|
|
1360
|
+
}
|
|
1361
|
+
function pushStatusAggregate(aggregate, status) {
|
|
1362
|
+
aggregate.count += 1;
|
|
1363
|
+
if (status !== 'past') aggregate.allPast = false;
|
|
1364
|
+
if (status === 'available') aggregate.hasAvailable = true;
|
|
1365
|
+
if (status === 'limited') aggregate.hasLimited = true;
|
|
1366
|
+
if (status === 'full') aggregate.hasFull = true;
|
|
1367
|
+
}
|
|
1368
|
+
function summarizeStatusAggregate(aggregate) {
|
|
1369
|
+
if (aggregate.count === 0) return 'unavailable';
|
|
1370
|
+
if (aggregate.hasAvailable) return 'available';
|
|
1371
|
+
if (aggregate.hasLimited) return 'limited';
|
|
1372
|
+
if (aggregate.hasFull) return 'full';
|
|
1373
|
+
if (aggregate.allPast) return 'past';
|
|
1374
|
+
return 'unavailable';
|
|
1375
|
+
}
|
|
1376
|
+
function evaluateMatchedResourceSummary(params) {
|
|
1377
|
+
var _ref6, _params$matchedCell$m;
|
|
1378
|
+
var isPast = params.candidateRange.endMs <= params.evaluationContext.evaluatedAtMs;
|
|
1379
|
+
if (!params.matchedCell) {
|
|
1380
|
+
return {
|
|
1381
|
+
status: isPast ? 'past' : 'unavailable',
|
|
1382
|
+
maxPartySize: 0,
|
|
1383
|
+
remainingCapacity: 0,
|
|
1384
|
+
maxCapacity: 0,
|
|
1385
|
+
reasonCodes: isPast ? ['past'] : ['outside_resource_window']
|
|
1386
|
+
};
|
|
1387
|
+
}
|
|
1388
|
+
var remainingCapacity = Math.max(0, params.matchedCell.remainingCapacity || 0);
|
|
1389
|
+
var maxCapacity = Math.max(0, (_ref6 = (_params$matchedCell$m = params.matchedCell.maxCapacity) !== null && _params$matchedCell$m !== void 0 ? _params$matchedCell$m : params.resourceCapacity) !== null && _ref6 !== void 0 ? _ref6 : 0);
|
|
1390
|
+
var maxPartySize = isPast ? 0 : remainingCapacity;
|
|
1391
|
+
var status = function () {
|
|
1392
|
+
if (isPast) return 'past';
|
|
1393
|
+
if (params.matchedCell.status === 'unavailable') return 'unavailable';
|
|
1394
|
+
if (remainingCapacity < params.requiredCapacity) return 'full';
|
|
1395
|
+
if (remainingCapacity < maxCapacity) return 'limited';
|
|
1396
|
+
return 'available';
|
|
1397
|
+
}();
|
|
1398
|
+
return {
|
|
1399
|
+
status: status,
|
|
1400
|
+
remainingCapacity: remainingCapacity,
|
|
1401
|
+
maxCapacity: maxCapacity,
|
|
1402
|
+
maxPartySize: maxPartySize,
|
|
1403
|
+
reasonCodes: isPast ? Array.from(new Set([].concat(_toConsumableArray(params.matchedCell.reasonCodes || []), ['past']))) : params.matchedCell.reasonCodes
|
|
1404
|
+
};
|
|
1405
|
+
}
|
|
1406
|
+
function evaluateRequirementGroupSummary(params) {
|
|
1407
|
+
var _requirementGroup$min2, _requirementGroup$max, _params$groupResource;
|
|
1408
|
+
var projection = params.projection,
|
|
1409
|
+
product = params.product,
|
|
1410
|
+
requirementGroup = params.requirementGroup,
|
|
1411
|
+
candidateRange = params.candidateRange,
|
|
1412
|
+
requestedPartySize = params.requestedPartySize,
|
|
1413
|
+
evaluationContext = params.evaluationContext;
|
|
1414
|
+
var required = requirementGroup.required !== false;
|
|
1415
|
+
var minimum = Math.max(required ? 1 : 0, Number((_requirementGroup$min2 = requirementGroup.min) !== null && _requirementGroup$min2 !== void 0 ? _requirementGroup$min2 : required ? 1 : 0) || 0);
|
|
1416
|
+
var maximum = Math.max(minimum, Number((_requirementGroup$max = requirementGroup.max) !== null && _requirementGroup$max !== void 0 ? _requirementGroup$max : Math.max(minimum, 1)) || Math.max(minimum, 1));
|
|
1417
|
+
var matchedResources = ((_params$groupResource = params.groupResourceIndex) === null || _params$groupResource === void 0 ? void 0 : _params$groupResource.get([String(product.id), requirementGroup.id].join(':'))) || projection.resources.filter(function (resource) {
|
|
1418
|
+
return matchesRequirementGroup(requirementGroup, resource, product, requestedPartySize !== null && requestedPartySize !== void 0 ? requestedPartySize : 1);
|
|
1419
|
+
}).filter(function (resource) {
|
|
1420
|
+
return !evaluationContext.resourceIdSet || evaluationContext.resourceIdSet.has(String(resource.id));
|
|
1421
|
+
});
|
|
1422
|
+
var requiredCapacity = resolveRequiredCapacity(requirementGroup, product, requestedPartySize);
|
|
1423
|
+
var optionStatusAggregate = createStatusAggregate();
|
|
1424
|
+
var reasonCodeSet = new Set();
|
|
1425
|
+
var totalResourceCount = 0;
|
|
1426
|
+
var availableResourceCount = 0;
|
|
1427
|
+
var groupMaxPartySize = 0;
|
|
1428
|
+
var groupRemainingCapacity = 0;
|
|
1429
|
+
var groupMaxCapacity = 0;
|
|
1430
|
+
matchedResources.forEach(function (resource) {
|
|
1431
|
+
var _params$cellIndex;
|
|
1432
|
+
totalResourceCount += 1;
|
|
1433
|
+
var matchedCell = aggregateMatchedCellSummary(candidateRange, ((_params$cellIndex = params.cellIndex) === null || _params$cellIndex === void 0 ? void 0 : _params$cellIndex.get([String(product.id), requirementGroup.id, String(resource.id)].join(':'))) || []);
|
|
1434
|
+
var option = evaluateMatchedResourceSummary({
|
|
1435
|
+
matchedCell: matchedCell,
|
|
1436
|
+
resourceCapacity: resource.capacity,
|
|
1437
|
+
requiredCapacity: requiredCapacity,
|
|
1438
|
+
candidateRange: candidateRange,
|
|
1439
|
+
evaluationContext: evaluationContext
|
|
1440
|
+
});
|
|
1441
|
+
pushStatusAggregate(optionStatusAggregate, option.status);
|
|
1442
|
+
option.reasonCodes.forEach(function (reasonCode) {
|
|
1443
|
+
return reasonCodeSet.add(reasonCode);
|
|
1444
|
+
});
|
|
1445
|
+
groupRemainingCapacity = Math.max(groupRemainingCapacity, option.remainingCapacity);
|
|
1446
|
+
groupMaxCapacity = Math.max(groupMaxCapacity, option.maxCapacity);
|
|
1447
|
+
if (option.status === 'available' || option.status === 'limited') {
|
|
1448
|
+
availableResourceCount += 1;
|
|
1449
|
+
groupMaxPartySize = Math.max(groupMaxPartySize, option.maxPartySize);
|
|
1450
|
+
}
|
|
1451
|
+
});
|
|
1452
|
+
var status = 'available';
|
|
1453
|
+
if (candidateRange.endMs <= evaluationContext.evaluatedAtMs) {
|
|
1454
|
+
status = 'past';
|
|
1455
|
+
} else if (availableResourceCount < minimum) {
|
|
1456
|
+
status = optionStatusAggregate.hasAvailable || optionStatusAggregate.hasLimited || optionStatusAggregate.hasFull ? 'full' : 'unavailable';
|
|
1457
|
+
} else if (availableResourceCount === totalResourceCount && !optionStatusAggregate.hasLimited && !optionStatusAggregate.hasFull && !optionStatusAggregate.allPast) {
|
|
1458
|
+
status = 'available';
|
|
1459
|
+
} else {
|
|
1460
|
+
status = 'limited';
|
|
1461
|
+
}
|
|
1462
|
+
return {
|
|
1463
|
+
required: required,
|
|
1464
|
+
min: minimum,
|
|
1465
|
+
max: maximum,
|
|
1466
|
+
status: status,
|
|
1467
|
+
maxPartySize: groupMaxPartySize,
|
|
1468
|
+
remainingCapacity: groupRemainingCapacity,
|
|
1469
|
+
maxCapacity: groupMaxCapacity,
|
|
1470
|
+
reasonCodes: Array.from(reasonCodeSet)
|
|
1471
|
+
};
|
|
1472
|
+
}
|
|
1473
|
+
function evaluateCandidateSummary(projection, candidate, request) {
|
|
1474
|
+
var _request$productIndex, _request$candidateRan;
|
|
1475
|
+
var product = ((_request$productIndex = request.productIndex) === null || _request$productIndex === void 0 ? void 0 : _request$productIndex.get(String(candidate.productId))) || projection.products.find(function (item) {
|
|
1476
|
+
return sameId(item.id, candidate.productId);
|
|
1477
|
+
});
|
|
1478
|
+
if (!product) {
|
|
1479
|
+
throw availabilityError('INVALID_TARGET_FILTER', 'candidate.productId 不存在于 projection.products', {
|
|
1480
|
+
candidate: candidate
|
|
1481
|
+
});
|
|
1482
|
+
}
|
|
1483
|
+
var candidateRangeCacheKey = getCandidateRangeCacheKey(candidate);
|
|
1484
|
+
var candidateRange = ((_request$candidateRan = request.candidateRangeCache) === null || _request$candidateRan === void 0 ? void 0 : _request$candidateRan.get(candidateRangeCacheKey)) || function () {
|
|
1485
|
+
var normalized = normalizeAbsoluteRange(getCandidateBookingRange(candidate), projection.meta.timezone);
|
|
1486
|
+
return normalized ? toIndexedRange(normalized) : null;
|
|
1487
|
+
}();
|
|
1488
|
+
if (!candidateRange) {
|
|
1489
|
+
throw availabilityError('INVALID_TARGET_FILTER', 'candidate 时间范围非法', {
|
|
1490
|
+
candidate: candidate
|
|
1491
|
+
});
|
|
1492
|
+
}
|
|
1493
|
+
if (request.candidateRangeCache && !request.candidateRangeCache.has(candidateRangeCacheKey)) {
|
|
1494
|
+
request.candidateRangeCache.set(candidateRangeCacheKey, candidateRange);
|
|
1495
|
+
}
|
|
1496
|
+
var evaluationContext = request.evaluationContext || buildEvaluationContext({
|
|
1497
|
+
projection: projection,
|
|
1498
|
+
evaluatedAt: request.evaluatedAt,
|
|
1499
|
+
resourceIds: request.resourceIds
|
|
1500
|
+
});
|
|
1501
|
+
var requestedPartySize = normalizePartySize(request.partySize);
|
|
1502
|
+
var requiredGroups = (product.requirementGroups || []).filter(function (group) {
|
|
1503
|
+
return group.required !== false;
|
|
1504
|
+
}).map(function (requirementGroup) {
|
|
1505
|
+
return evaluateRequirementGroupSummary({
|
|
1506
|
+
projection: projection,
|
|
1507
|
+
product: product,
|
|
1508
|
+
requirementGroup: requirementGroup,
|
|
1509
|
+
candidateRange: candidateRange,
|
|
1510
|
+
requestedPartySize: requestedPartySize,
|
|
1511
|
+
evaluationContext: evaluationContext,
|
|
1512
|
+
cellIndex: request.cellIndex,
|
|
1513
|
+
groupResourceIndex: request.groupResourceIndex
|
|
1514
|
+
});
|
|
1515
|
+
});
|
|
1516
|
+
var gate = evaluateCandidateTimeGate({
|
|
1517
|
+
product: product,
|
|
1518
|
+
candidateRange: candidateRange,
|
|
1519
|
+
evaluationContext: evaluationContext,
|
|
1520
|
+
timezoneName: projection.meta.timezone
|
|
1521
|
+
});
|
|
1522
|
+
var candidateStatus = requiredGroups.length ? summarizeStatuses(requiredGroups.map(function (group) {
|
|
1523
|
+
return group.status;
|
|
1524
|
+
})) : candidateRange.endMs <= evaluationContext.evaluatedAtMs ? 'past' : 'available';
|
|
1525
|
+
var maxPartySize = requiredGroups.length ? requiredGroups.reduce(function (min, group) {
|
|
1526
|
+
return Math.min(min, group.maxPartySize);
|
|
1527
|
+
}, Number.MAX_SAFE_INTEGER) : 0;
|
|
1528
|
+
var remainingCapacity = requiredGroups.length ? requiredGroups.reduce(function (min, group) {
|
|
1529
|
+
return Math.min(min, group.remainingCapacity);
|
|
1530
|
+
}, Number.MAX_SAFE_INTEGER) : 0;
|
|
1531
|
+
var maxCapacity = requiredGroups.length ? requiredGroups.reduce(function (min, group) {
|
|
1532
|
+
return Math.min(min, group.maxCapacity);
|
|
1533
|
+
}, Number.MAX_SAFE_INTEGER) : 0;
|
|
1534
|
+
var status = (gate === null || gate === void 0 ? void 0 : gate.status) || candidateStatus;
|
|
957
1535
|
return {
|
|
958
|
-
|
|
959
|
-
|
|
1536
|
+
status: status,
|
|
1537
|
+
maxPartySize: gate ? 0 : Number.isFinite(maxPartySize) ? maxPartySize : 0,
|
|
1538
|
+
remainingCapacity: status === 'past' || status === 'unavailable' ? 0 : Number.isFinite(remainingCapacity) ? remainingCapacity : 0,
|
|
1539
|
+
maxCapacity: Number.isFinite(maxCapacity) ? maxCapacity : 0,
|
|
1540
|
+
reasonCodes: (gate === null || gate === void 0 ? void 0 : gate.reasonCodes) || Array.from(new Set(requiredGroups.flatMap(function (group) {
|
|
1541
|
+
return group.reasonCodes;
|
|
1542
|
+
})))
|
|
960
1543
|
};
|
|
961
1544
|
}
|
|
962
|
-
function
|
|
963
|
-
|
|
964
|
-
|
|
965
|
-
|
|
1545
|
+
function evaluateCandidate(projection, candidate, request) {
|
|
1546
|
+
var _request$productIndex2, _request$candidateRan2;
|
|
1547
|
+
var product = ((_request$productIndex2 = request.productIndex) === null || _request$productIndex2 === void 0 ? void 0 : _request$productIndex2.get(String(candidate.productId))) || projection.products.find(function (item) {
|
|
1548
|
+
return sameId(item.id, candidate.productId);
|
|
1549
|
+
});
|
|
1550
|
+
if (!product) {
|
|
1551
|
+
throw availabilityError('INVALID_TARGET_FILTER', 'candidate.productId 不存在于 projection.products', {
|
|
1552
|
+
candidate: candidate
|
|
1553
|
+
});
|
|
1554
|
+
}
|
|
1555
|
+
var candidateRangeCacheKey = getCandidateRangeCacheKey(candidate);
|
|
1556
|
+
var candidateRange = ((_request$candidateRan2 = request.candidateRangeCache) === null || _request$candidateRan2 === void 0 ? void 0 : _request$candidateRan2.get(candidateRangeCacheKey)) || function () {
|
|
1557
|
+
var normalized = normalizeAbsoluteRange(getCandidateBookingRange(candidate), projection.meta.timezone);
|
|
1558
|
+
return normalized ? toIndexedRange(normalized) : null;
|
|
1559
|
+
}();
|
|
1560
|
+
if (!candidateRange) {
|
|
1561
|
+
throw availabilityError('INVALID_TARGET_FILTER', 'candidate 时间范围非法', {
|
|
1562
|
+
candidate: candidate
|
|
1563
|
+
});
|
|
1564
|
+
}
|
|
1565
|
+
if (request.candidateRangeCache && !request.candidateRangeCache.has(candidateRangeCacheKey)) {
|
|
1566
|
+
request.candidateRangeCache.set(candidateRangeCacheKey, candidateRange);
|
|
1567
|
+
}
|
|
1568
|
+
var evaluationContext = request.evaluationContext || buildEvaluationContext({
|
|
1569
|
+
projection: projection,
|
|
1570
|
+
evaluatedAt: request.evaluatedAt,
|
|
1571
|
+
resourceIds: request.resourceIds,
|
|
1572
|
+
requirementSelections: request.requirementSelections
|
|
1573
|
+
});
|
|
1574
|
+
var requestedPartySize = normalizePartySize(request.partySize);
|
|
1575
|
+
var partySize = requestedPartySize !== null && requestedPartySize !== void 0 ? requestedPartySize : 1;
|
|
1576
|
+
var selectionsByGroupKey = evaluationContext.selectionsByGroupKey;
|
|
1577
|
+
var requirementGroups = (product.requirementGroups || []).map(function (requirementGroup) {
|
|
1578
|
+
var _requirementGroup$min3, _requirementGroup$max2, _requirementGroup$for2, _request$groupResourc;
|
|
1579
|
+
var required = requirementGroup.required !== false;
|
|
1580
|
+
var minimum = Math.max(required ? 1 : 0, Number((_requirementGroup$min3 = requirementGroup.min) !== null && _requirementGroup$min3 !== void 0 ? _requirementGroup$min3 : required ? 1 : 0) || 0);
|
|
1581
|
+
var maximum = Math.max(minimum, Number((_requirementGroup$max2 = requirementGroup.max) !== null && _requirementGroup$max2 !== void 0 ? _requirementGroup$max2 : Math.max(minimum, 1)) || Math.max(minimum, 1));
|
|
1582
|
+
var selection = selectionsByGroupKey.get(requirementGroup.id) || selectionsByGroupKey.get(String((_requirementGroup$for2 = requirementGroup.formId) !== null && _requirementGroup$for2 !== void 0 ? _requirementGroup$for2 : ''));
|
|
1583
|
+
var matchedResources = (((_request$groupResourc = request.groupResourceIndex) === null || _request$groupResourc === void 0 ? void 0 : _request$groupResourc.get([String(product.id), requirementGroup.id].join(':'))) || projection.resources.filter(function (resource) {
|
|
1584
|
+
return matchesRequirementGroup(requirementGroup, resource, product, partySize);
|
|
1585
|
+
}).filter(function (resource) {
|
|
1586
|
+
return !evaluationContext.resourceIdSet || evaluationContext.resourceIdSet.has(String(resource.id));
|
|
1587
|
+
})).filter(function (resource) {
|
|
1588
|
+
return !selection || selection.resourceIds.some(function (resourceId) {
|
|
1589
|
+
return sameId(resourceId, resource.id);
|
|
1590
|
+
});
|
|
1591
|
+
});
|
|
1592
|
+
var options = matchedResources.map(function (resource) {
|
|
1593
|
+
var _request$cellIndex, _resource$raw2;
|
|
1594
|
+
var matchedCell = aggregateMatchedCells(product.id, requirementGroup.id, resource.id, candidateRange, ((_request$cellIndex = request.cellIndex) === null || _request$cellIndex === void 0 ? void 0 : _request$cellIndex.get([String(product.id), requirementGroup.id, String(resource.id)].join(':'))) || []);
|
|
1595
|
+
if (!matchedCell) {
|
|
1596
|
+
var _resource$raw;
|
|
1597
|
+
var _isPast = candidateRange.endMs <= evaluationContext.evaluatedAtMs;
|
|
1598
|
+
return {
|
|
1599
|
+
resourceId: resource.id,
|
|
1600
|
+
resourceName: resource.name,
|
|
1601
|
+
resourceFormId: resource.formId,
|
|
1602
|
+
policyId: (_resource$raw = resource.raw) === null || _resource$raw === void 0 ? void 0 : _resource$raw.policy_id,
|
|
1603
|
+
resourceType: resource.resourceType,
|
|
1604
|
+
status: _isPast ? 'past' : 'unavailable',
|
|
1605
|
+
maxPartySize: 0,
|
|
1606
|
+
reasonCodes: _isPast ? ['past'] : ['outside_resource_window']
|
|
1607
|
+
};
|
|
1608
|
+
}
|
|
1609
|
+
var remainingCapacity = matchedCell.remainingCapacity;
|
|
1610
|
+
var maxCapacity = matchedCell.maxCapacity;
|
|
1611
|
+
var isPast = candidateRange.endMs <= evaluationContext.evaluatedAtMs;
|
|
1612
|
+
var maxPartySize = isPast ? 0 : Math.max(0, remainingCapacity || 0);
|
|
1613
|
+
var requiredCapacity = resolveRequiredCapacity(requirementGroup, product, requestedPartySize);
|
|
1614
|
+
var status = function () {
|
|
1615
|
+
if (isPast) return 'past';
|
|
1616
|
+
if (matchedCell.status === 'unavailable') return 'unavailable';
|
|
1617
|
+
if ((remainingCapacity || 0) < requiredCapacity) return 'full';
|
|
1618
|
+
if ((remainingCapacity || 0) < (maxCapacity || resource.capacity)) return 'limited';
|
|
1619
|
+
return 'available';
|
|
1620
|
+
}();
|
|
966
1621
|
return {
|
|
967
|
-
resourceId:
|
|
968
|
-
|
|
969
|
-
|
|
970
|
-
|
|
971
|
-
|
|
1622
|
+
resourceId: resource.id,
|
|
1623
|
+
resourceName: resource.name,
|
|
1624
|
+
resourceFormId: resource.formId,
|
|
1625
|
+
policyId: (_resource$raw2 = resource.raw) === null || _resource$raw2 === void 0 ? void 0 : _resource$raw2.policy_id,
|
|
1626
|
+
resourceType: resource.resourceType,
|
|
1627
|
+
status: status,
|
|
1628
|
+
remainingCapacity: remainingCapacity,
|
|
1629
|
+
maxCapacity: maxCapacity,
|
|
1630
|
+
maxPartySize: maxPartySize,
|
|
1631
|
+
reasonCodes: isPast ? Array.from(new Set([].concat(_toConsumableArray(matchedCell.reasonCodes), ['past']))) : matchedCell.reasonCodes,
|
|
1632
|
+
matchedCell: matchedCell
|
|
972
1633
|
};
|
|
973
|
-
})
|
|
1634
|
+
});
|
|
1635
|
+
var availableOptions = options.filter(function (option) {
|
|
1636
|
+
return option.status === 'available' || option.status === 'limited';
|
|
1637
|
+
});
|
|
1638
|
+
var totalResourceCount = options.length;
|
|
1639
|
+
var availableResourceCount = availableOptions.length;
|
|
1640
|
+
var groupMaxPartySize = availableOptions.reduce(function (max, option) {
|
|
1641
|
+
return Math.max(max, option.maxPartySize);
|
|
1642
|
+
}, 0);
|
|
1643
|
+
var status = 'available';
|
|
1644
|
+
if (candidateRange.endMs <= evaluationContext.evaluatedAtMs) {
|
|
1645
|
+
status = 'past';
|
|
1646
|
+
} else if (availableResourceCount < minimum) {
|
|
1647
|
+
status = options.some(function (option) {
|
|
1648
|
+
return option.status === 'full' || option.status === 'limited' || option.status === 'available';
|
|
1649
|
+
}) ? 'full' : 'unavailable';
|
|
1650
|
+
} else if (options.every(function (option) {
|
|
1651
|
+
return option.status === 'available';
|
|
1652
|
+
})) {
|
|
1653
|
+
status = 'available';
|
|
1654
|
+
} else {
|
|
1655
|
+
status = 'limited';
|
|
1656
|
+
}
|
|
1657
|
+
return {
|
|
1658
|
+
groupId: requirementGroup.id,
|
|
1659
|
+
formId: requirementGroup.formId,
|
|
1660
|
+
title: requirementGroup.title,
|
|
1661
|
+
code: requirementGroup.code,
|
|
1662
|
+
resourceType: requirementGroup.resourceType,
|
|
1663
|
+
required: required,
|
|
1664
|
+
min: minimum,
|
|
1665
|
+
max: maximum,
|
|
1666
|
+
status: status,
|
|
1667
|
+
availableResourceCount: availableResourceCount,
|
|
1668
|
+
totalResourceCount: totalResourceCount,
|
|
1669
|
+
maxPartySize: groupMaxPartySize,
|
|
1670
|
+
remainingCapacity: Math.max.apply(Math, _toConsumableArray(availableOptions.map(function (option) {
|
|
1671
|
+
return option.remainingCapacity || 0;
|
|
1672
|
+
})).concat([0])),
|
|
1673
|
+
maxCapacity: Math.max.apply(Math, _toConsumableArray(availableOptions.map(function (option) {
|
|
1674
|
+
return option.maxCapacity || 0;
|
|
1675
|
+
})).concat([0])),
|
|
1676
|
+
reasonCodes: Array.from(new Set(options.flatMap(function (option) {
|
|
1677
|
+
return option.reasonCodes;
|
|
1678
|
+
}))),
|
|
1679
|
+
options: options
|
|
1680
|
+
};
|
|
1681
|
+
});
|
|
1682
|
+
var requiredGroups = requirementGroups.filter(function (group) {
|
|
1683
|
+
return group.required;
|
|
1684
|
+
});
|
|
1685
|
+
var requiredStatuses = requiredGroups.map(function (group) {
|
|
1686
|
+
return group.status;
|
|
1687
|
+
});
|
|
1688
|
+
var gate = evaluateCandidateTimeGate({
|
|
1689
|
+
product: product,
|
|
1690
|
+
candidateRange: candidateRange,
|
|
1691
|
+
evaluationContext: evaluationContext,
|
|
1692
|
+
timezoneName: projection.meta.timezone
|
|
974
1693
|
});
|
|
1694
|
+
var candidateStatus = requiredGroups.length ? summarizeStatuses(requiredStatuses) : candidateRange.endMs <= evaluationContext.evaluatedAtMs ? 'past' : 'available';
|
|
1695
|
+
var maxPartySize = requiredGroups.length ? requiredGroups.reduce(function (min, group) {
|
|
1696
|
+
return Math.min(min, group.maxPartySize);
|
|
1697
|
+
}, Number.MAX_SAFE_INTEGER) : 0;
|
|
1698
|
+
var selectedAssignments = requirementGroups.flatMap(function (group) {
|
|
1699
|
+
var _group$formId;
|
|
1700
|
+
var selection = selectionsByGroupKey.get(group.groupId) || selectionsByGroupKey.get(String((_group$formId = group.formId) !== null && _group$formId !== void 0 ? _group$formId : ''));
|
|
1701
|
+
if (!selection && !(request.allowImplicitSelection && group.required)) return [];
|
|
1702
|
+
var availableOptions = group.options.filter(function (option) {
|
|
1703
|
+
return option.status === 'available' || option.status === 'limited';
|
|
1704
|
+
});
|
|
1705
|
+
var selectedOptions = selection ? availableOptions.slice(0, group.max) : availableOptions.slice(0, Math.max(group.min, 1));
|
|
1706
|
+
var originalGroup = (product.requirementGroups || []).find(function (item) {
|
|
1707
|
+
return item.id === group.groupId;
|
|
1708
|
+
}) || {
|
|
1709
|
+
id: group.groupId,
|
|
1710
|
+
resourceType: group.resourceType,
|
|
1711
|
+
min: group.min,
|
|
1712
|
+
max: group.max
|
|
1713
|
+
};
|
|
1714
|
+
return selectedOptions.map(function (selectedOption) {
|
|
1715
|
+
var _candidate$primarySch, _candidate$primarySch2;
|
|
1716
|
+
return {
|
|
1717
|
+
productId: product.id,
|
|
1718
|
+
resourceId: selectedOption.resourceId,
|
|
1719
|
+
formId: group.formId,
|
|
1720
|
+
startAt: candidate.bookingStartAt || candidate.startAt,
|
|
1721
|
+
endAt: candidate.bookingEndAt || candidate.endAt,
|
|
1722
|
+
capacityRequired: resolveRequiredCapacity(originalGroup, product, requestedPartySize),
|
|
1723
|
+
scheduleId: (_candidate$primarySch = candidate.primaryScheduleRef) === null || _candidate$primarySch === void 0 ? void 0 : _candidate$primarySch.scheduleId,
|
|
1724
|
+
timeSlotId: (_candidate$primarySch2 = candidate.primaryScheduleRef) === null || _candidate$primarySch2 === void 0 ? void 0 : _candidate$primarySch2.timeSlotId
|
|
1725
|
+
};
|
|
1726
|
+
});
|
|
1727
|
+
});
|
|
1728
|
+
return {
|
|
1729
|
+
item: _objectSpread(_objectSpread({}, candidate), {}, {
|
|
1730
|
+
status: (gate === null || gate === void 0 ? void 0 : gate.status) || candidateStatus,
|
|
1731
|
+
maxPartySize: gate ? 0 : Number.isFinite(maxPartySize) ? maxPartySize : 0,
|
|
1732
|
+
reasonCodes: (gate === null || gate === void 0 ? void 0 : gate.reasonCodes) || Array.from(new Set(requiredGroups.flatMap(function (group) {
|
|
1733
|
+
return group.reasonCodes;
|
|
1734
|
+
}))),
|
|
1735
|
+
requirementGroups: requirementGroups
|
|
1736
|
+
}),
|
|
1737
|
+
assignments: gate ? [] : selectedAssignments,
|
|
1738
|
+
gate: gate || undefined
|
|
1739
|
+
};
|
|
975
1740
|
}
|
|
976
|
-
|
|
977
|
-
if (
|
|
978
|
-
|
|
979
|
-
|
|
980
|
-
|
|
981
|
-
|
|
982
|
-
|
|
983
|
-
|
|
984
|
-
|
|
985
|
-
|
|
986
|
-
|
|
1741
|
+
function assertTimeCandidates(request) {
|
|
1742
|
+
if (request.target === 'time' && (!Array.isArray(request.candidates) || request.candidates.length === 0)) {
|
|
1743
|
+
throw availabilityError('MISSING_TIME_CANDIDATES', 'target=time 必须传非空 candidates', {
|
|
1744
|
+
request: request
|
|
1745
|
+
});
|
|
1746
|
+
}
|
|
1747
|
+
}
|
|
1748
|
+
function assertSelectedRanges(request) {
|
|
1749
|
+
if (request.target === 'resource' && (!Array.isArray(request.selectedRanges) || request.selectedRanges.length === 0)) {
|
|
1750
|
+
throw availabilityError('MISSING_SELECTED_RANGE', 'target=resource 必须传非空 selectedRanges', {
|
|
1751
|
+
request: request
|
|
1752
|
+
});
|
|
1753
|
+
}
|
|
1754
|
+
}
|
|
1755
|
+
function assertRangesWithinProjection(projection, ranges) {
|
|
1756
|
+
var coverage = {
|
|
1757
|
+
start: toDateTime(projection.meta.coverage.startAt, projection.meta.timezone),
|
|
1758
|
+
end: toDateTime(projection.meta.coverage.endAt, projection.meta.timezone)
|
|
1759
|
+
};
|
|
1760
|
+
ranges.forEach(function (range) {
|
|
1761
|
+
var displayRange = normalizeAbsoluteRange(range, projection.meta.timezone);
|
|
1762
|
+
var bookingRange = normalizeAbsoluteRange(getCandidateBookingRange(range), projection.meta.timezone);
|
|
1763
|
+
if (!displayRange || !bookingRange) {
|
|
1764
|
+
throw availabilityError('INVALID_TARGET_FILTER', '候选时间范围非法', {
|
|
1765
|
+
range: range
|
|
1766
|
+
});
|
|
1767
|
+
}
|
|
1768
|
+
if (!contains(coverage, displayRange) || !contains(coverage, bookingRange)) {
|
|
1769
|
+
throw availabilityError('OUT_OF_PROJECTION_COVERAGE', '候选时间范围超出 projection.coverage', {
|
|
1770
|
+
range: range,
|
|
1771
|
+
coverage: projection.meta.coverage
|
|
1772
|
+
});
|
|
1773
|
+
}
|
|
1774
|
+
});
|
|
1775
|
+
}
|
|
1776
|
+
export function queryAvailabilityProjection(projection, request) {
|
|
1777
|
+
var _extractFixedOffsetMi6, _request$productIds2;
|
|
1778
|
+
var normalizedPartySize = normalizePartySize(request.partySize);
|
|
1779
|
+
var evaluatedAt = formatDateTime(localDateToScalar(new Date()), projection.meta.timezone, (_extractFixedOffsetMi6 = extractFixedOffsetMinutes(projection.meta.now)) !== null && _extractFixedOffsetMi6 !== void 0 ? _extractFixedOffsetMi6 : captureRuntimeOffsetMinutes());
|
|
1780
|
+
if (request.target === 'time') {
|
|
1781
|
+
assertTimeCandidates(request);
|
|
1782
|
+
assertRangesWithinProjection(projection, request.candidates);
|
|
1783
|
+
}
|
|
1784
|
+
if (request.target === 'resource') {
|
|
1785
|
+
assertSelectedRanges(request);
|
|
1786
|
+
assertRangesWithinProjection(projection, request.selectedRanges);
|
|
1787
|
+
}
|
|
1788
|
+
var productIdSet = (_request$productIds2 = request.productIds) !== null && _request$productIds2 !== void 0 && _request$productIds2.length ? new Set(request.productIds.map(String)) : null;
|
|
1789
|
+
var productIndex = buildProductIndex(projection);
|
|
1790
|
+
var cellIndex = buildAvailabilityCellIndex(projection);
|
|
1791
|
+
var groupResourceIndex = buildGroupResourceIndex({
|
|
1792
|
+
projection: projection,
|
|
1793
|
+
resourceIds: request.resourceIds,
|
|
1794
|
+
partySize: normalizedPartySize
|
|
1795
|
+
});
|
|
1796
|
+
var candidateRangeCache = new Map();
|
|
1797
|
+
var evaluationContext = buildEvaluationContext({
|
|
1798
|
+
projection: projection,
|
|
1799
|
+
evaluatedAt: evaluatedAt,
|
|
1800
|
+
resourceIds: request.resourceIds
|
|
1801
|
+
});
|
|
1802
|
+
if (request.target === 'time') {
|
|
1803
|
+
var candidates = dedupeProductRanges(request.candidates);
|
|
1804
|
+
var candidatesByProduct = new Map();
|
|
1805
|
+
candidates.forEach(function (candidate) {
|
|
1806
|
+
var key = String(candidate.productId);
|
|
1807
|
+
candidatesByProduct.set(key, [].concat(_toConsumableArray(candidatesByProduct.get(key) || []), [candidate]));
|
|
1808
|
+
});
|
|
1809
|
+
var _data = projection.products.filter(function (product) {
|
|
1810
|
+
return !productIdSet || productIdSet.has(String(product.id));
|
|
1811
|
+
}).map(function (product) {
|
|
1812
|
+
return {
|
|
1813
|
+
productId: product.id,
|
|
1814
|
+
title: product.title,
|
|
1815
|
+
times: (candidatesByProduct.get(String(product.id)) || []).map(function (candidate) {
|
|
1816
|
+
return evaluateCandidate(projection, candidate, {
|
|
1817
|
+
partySize: normalizedPartySize,
|
|
1818
|
+
resourceIds: request.resourceIds,
|
|
1819
|
+
evaluatedAt: evaluatedAt,
|
|
1820
|
+
cellIndex: cellIndex,
|
|
1821
|
+
productIndex: productIndex,
|
|
1822
|
+
groupResourceIndex: groupResourceIndex,
|
|
1823
|
+
candidateRangeCache: candidateRangeCache,
|
|
1824
|
+
evaluationContext: evaluationContext
|
|
1825
|
+
}).item;
|
|
1826
|
+
})
|
|
1827
|
+
};
|
|
1828
|
+
}).filter(function (group) {
|
|
1829
|
+
return group.times.length > 0;
|
|
1830
|
+
});
|
|
1831
|
+
return {
|
|
1832
|
+
target: 'time',
|
|
1833
|
+
evaluatedAt: evaluatedAt,
|
|
1834
|
+
snapshotId: projection.meta.snapshotId,
|
|
1835
|
+
data: _data
|
|
1836
|
+
};
|
|
1837
|
+
}
|
|
1838
|
+
if (request.target === 'resource') {
|
|
1839
|
+
var selectedRanges = dedupeProductRanges(request.selectedRanges);
|
|
1840
|
+
var rangesByProduct = new Map();
|
|
1841
|
+
selectedRanges.forEach(function (candidate) {
|
|
1842
|
+
var key = String(candidate.productId);
|
|
1843
|
+
rangesByProduct.set(key, [].concat(_toConsumableArray(rangesByProduct.get(key) || []), [candidate]));
|
|
1844
|
+
});
|
|
1845
|
+
var _data2 = projection.products.filter(function (product) {
|
|
1846
|
+
return !productIdSet || productIdSet.has(String(product.id));
|
|
1847
|
+
}).map(function (product) {
|
|
1848
|
+
return {
|
|
1849
|
+
productId: product.id,
|
|
1850
|
+
title: product.title,
|
|
1851
|
+
ranges: (rangesByProduct.get(String(product.id)) || []).map(function (candidate) {
|
|
1852
|
+
var evaluated = evaluateCandidate(projection, candidate, {
|
|
1853
|
+
partySize: normalizedPartySize,
|
|
1854
|
+
resourceIds: request.resourceIds,
|
|
1855
|
+
evaluatedAt: evaluatedAt,
|
|
1856
|
+
cellIndex: cellIndex,
|
|
1857
|
+
productIndex: productIndex,
|
|
1858
|
+
groupResourceIndex: groupResourceIndex,
|
|
1859
|
+
candidateRangeCache: candidateRangeCache,
|
|
1860
|
+
evaluationContext: evaluationContext
|
|
1861
|
+
}).item;
|
|
1862
|
+
return {
|
|
1863
|
+
range: candidate,
|
|
1864
|
+
status: evaluated.status,
|
|
1865
|
+
maxPartySize: evaluated.maxPartySize,
|
|
1866
|
+
reasonCodes: evaluated.reasonCodes,
|
|
1867
|
+
requirementGroups: evaluated.requirementGroups
|
|
1868
|
+
};
|
|
1869
|
+
})
|
|
1870
|
+
};
|
|
1871
|
+
}).filter(function (group) {
|
|
1872
|
+
return group.ranges.length > 0;
|
|
1873
|
+
});
|
|
1874
|
+
return {
|
|
1875
|
+
target: 'resource',
|
|
1876
|
+
evaluatedAt: evaluatedAt,
|
|
1877
|
+
snapshotId: projection.meta.snapshotId,
|
|
1878
|
+
data: _data2
|
|
1879
|
+
};
|
|
1880
|
+
}
|
|
1881
|
+
var dateRange = resolveProjectionDateRange(projection, request.dateRange);
|
|
1882
|
+
var candidateGroups = getProductTimeRanges(projection, {
|
|
1883
|
+
productIds: request.productIds,
|
|
1884
|
+
dateRange: dateRange,
|
|
1885
|
+
resourceIds: request.resourceIds,
|
|
1886
|
+
partySize: normalizedPartySize
|
|
1887
|
+
}, {
|
|
1888
|
+
evaluatedAt: evaluatedAt
|
|
1889
|
+
});
|
|
1890
|
+
var candidateGroupMap = new Map(candidateGroups.map(function (group) {
|
|
1891
|
+
return [String(group.productId), group];
|
|
1892
|
+
}));
|
|
1893
|
+
if (request.target === 'date') {
|
|
1894
|
+
var _data3 = projection.products.filter(function (product) {
|
|
1895
|
+
return !productIdSet || productIdSet.has(String(product.id));
|
|
1896
|
+
}).map(function (product) {
|
|
1897
|
+
var group = candidateGroupMap.get(String(product.id)) || {
|
|
1898
|
+
productId: product.id,
|
|
1899
|
+
title: product.title,
|
|
1900
|
+
kind: product.kind,
|
|
1901
|
+
ranges: []
|
|
1902
|
+
};
|
|
1903
|
+
var byDate = new Map();
|
|
1904
|
+
group.ranges.forEach(function (candidate) {
|
|
1905
|
+
var evaluated = evaluateCandidateSummary(projection, candidate, {
|
|
1906
|
+
partySize: normalizedPartySize,
|
|
1907
|
+
resourceIds: request.resourceIds,
|
|
1908
|
+
evaluatedAt: evaluatedAt,
|
|
1909
|
+
cellIndex: cellIndex,
|
|
1910
|
+
productIndex: productIndex,
|
|
1911
|
+
groupResourceIndex: groupResourceIndex,
|
|
1912
|
+
candidateRangeCache: candidateRangeCache,
|
|
1913
|
+
evaluationContext: evaluationContext
|
|
987
1914
|
});
|
|
988
|
-
|
|
989
|
-
|
|
990
|
-
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
|
|
996
|
-
if (!resource) {
|
|
997
|
-
_conflicts.push({
|
|
998
|
-
type: 'missing_resource',
|
|
999
|
-
message: 'assignment 对应的资源不存在',
|
|
1000
|
-
productId: assignment.productId,
|
|
1001
|
-
resourceId: assignment.resourceId
|
|
1002
|
-
});
|
|
1003
|
-
return 0; // continue
|
|
1004
|
-
}
|
|
1005
|
-
var start = dayjs(assignment.start_at);
|
|
1006
|
-
var end = dayjs(assignment.end_at);
|
|
1007
|
-
var interval = {
|
|
1008
|
-
id: "".concat(assignment.productId, ":").concat(assignment.resourceId, ":").concat(assignment.start_at, ":").concat(assignment.end_at),
|
|
1009
|
-
productId: assignment.productId,
|
|
1010
|
-
source: product.kind === 'session' ? 'session' : product.kind === 'venue_slot' ? 'venue_slot' : 'duration',
|
|
1011
|
-
date: start.format('YYYY-MM-DD'),
|
|
1012
|
-
start_at: assignment.start_at,
|
|
1013
|
-
end_at: assignment.end_at,
|
|
1014
|
-
durationMinutes: Math.max(1, end.diff(start, 'minute')),
|
|
1015
|
-
quantity: Math.max(1, Number(product.quantity || 1)),
|
|
1016
|
-
capacityRequired: Math.max(1, Number(assignment.capacityRequired || product.capacityRequired || 1)),
|
|
1017
|
-
resourceRequirements: product.resourceRequirements || []
|
|
1915
|
+
var aggregate = byDate.get(candidate.date) || {
|
|
1916
|
+
availableCount: 0,
|
|
1917
|
+
totalCount: 0,
|
|
1918
|
+
maxPartySize: 0,
|
|
1919
|
+
remainingCapacity: 0,
|
|
1920
|
+
maxCapacity: 0,
|
|
1921
|
+
reasonCodeSet: new Set(),
|
|
1922
|
+
statusAggregate: createStatusAggregate()
|
|
1018
1923
|
};
|
|
1019
|
-
|
|
1020
|
-
|
|
1021
|
-
|
|
1022
|
-
context: context
|
|
1023
|
-
});
|
|
1024
|
-
if (cell.status !== 'available' && cell.status !== 'limited') {
|
|
1025
|
-
_conflicts.push.apply(_conflicts, _toConsumableArray(cell.conflicts));
|
|
1924
|
+
aggregate.totalCount += 1;
|
|
1925
|
+
if (evaluated.status === 'available' || evaluated.status === 'limited') {
|
|
1926
|
+
aggregate.availableCount += 1;
|
|
1026
1927
|
}
|
|
1027
|
-
|
|
1028
|
-
|
|
1029
|
-
|
|
1030
|
-
|
|
1031
|
-
|
|
1928
|
+
aggregate.maxPartySize = Math.max(aggregate.maxPartySize, evaluated.maxPartySize);
|
|
1929
|
+
aggregate.remainingCapacity += evaluated.remainingCapacity;
|
|
1930
|
+
aggregate.maxCapacity += evaluated.maxCapacity;
|
|
1931
|
+
pushStatusAggregate(aggregate.statusAggregate, evaluated.status);
|
|
1932
|
+
evaluated.reasonCodes.forEach(function (reasonCode) {
|
|
1933
|
+
return aggregate.reasonCodeSet.add(reasonCode);
|
|
1032
1934
|
});
|
|
1033
|
-
|
|
1034
|
-
|
|
1035
|
-
|
|
1036
|
-
|
|
1037
|
-
|
|
1038
|
-
|
|
1039
|
-
|
|
1040
|
-
|
|
1041
|
-
|
|
1042
|
-
|
|
1043
|
-
|
|
1044
|
-
|
|
1045
|
-
|
|
1046
|
-
|
|
1935
|
+
byDate.set(candidate.date, aggregate);
|
|
1936
|
+
});
|
|
1937
|
+
var dates = enumerateDates(dateRange.startDate, dateRange.endDate).map(function (date) {
|
|
1938
|
+
var aggregate = byDate.get(date);
|
|
1939
|
+
return {
|
|
1940
|
+
date: date,
|
|
1941
|
+
status: aggregate ? summarizeStatusAggregate(aggregate.statusAggregate) : 'unavailable',
|
|
1942
|
+
availableCount: (aggregate === null || aggregate === void 0 ? void 0 : aggregate.availableCount) || 0,
|
|
1943
|
+
totalCount: (aggregate === null || aggregate === void 0 ? void 0 : aggregate.totalCount) || 0,
|
|
1944
|
+
maxPartySize: (aggregate === null || aggregate === void 0 ? void 0 : aggregate.maxPartySize) || 0,
|
|
1945
|
+
remainingCapacity: (aggregate === null || aggregate === void 0 ? void 0 : aggregate.remainingCapacity) || 0,
|
|
1946
|
+
maxCapacity: (aggregate === null || aggregate === void 0 ? void 0 : aggregate.maxCapacity) || 0,
|
|
1947
|
+
reasonCodes: aggregate ? Array.from(aggregate.reasonCodeSet) : []
|
|
1948
|
+
};
|
|
1949
|
+
});
|
|
1950
|
+
return {
|
|
1951
|
+
productId: product.id,
|
|
1952
|
+
title: product.title,
|
|
1953
|
+
dates: dates
|
|
1954
|
+
};
|
|
1955
|
+
});
|
|
1047
1956
|
return {
|
|
1048
|
-
|
|
1049
|
-
|
|
1957
|
+
target: 'date',
|
|
1958
|
+
evaluatedAt: evaluatedAt,
|
|
1959
|
+
snapshotId: projection.meta.snapshotId,
|
|
1960
|
+
data: _data3
|
|
1050
1961
|
};
|
|
1051
1962
|
}
|
|
1052
|
-
var
|
|
1053
|
-
|
|
1054
|
-
|
|
1055
|
-
|
|
1056
|
-
|
|
1057
|
-
|
|
1058
|
-
|
|
1963
|
+
var data = projection.products.filter(function (product) {
|
|
1964
|
+
return !productIdSet || productIdSet.has(String(product.id));
|
|
1965
|
+
}).map(function (product) {
|
|
1966
|
+
var group = candidateGroupMap.get(String(product.id)) || {
|
|
1967
|
+
productId: product.id,
|
|
1968
|
+
title: product.title,
|
|
1969
|
+
kind: product.kind,
|
|
1970
|
+
ranges: []
|
|
1971
|
+
};
|
|
1972
|
+
var productStatusAggregate = createStatusAggregate();
|
|
1973
|
+
var productReasonCodeSet = new Set();
|
|
1974
|
+
var dateStatusMap = new Map();
|
|
1975
|
+
var availableTimeCount = 0;
|
|
1976
|
+
var totalTimeCount = 0;
|
|
1977
|
+
var maxPartySize = 0;
|
|
1978
|
+
group.ranges.forEach(function (candidate) {
|
|
1979
|
+
var evaluated = evaluateCandidateSummary(projection, candidate, {
|
|
1980
|
+
partySize: normalizedPartySize,
|
|
1981
|
+
resourceIds: request.resourceIds,
|
|
1982
|
+
evaluatedAt: evaluatedAt,
|
|
1983
|
+
cellIndex: cellIndex,
|
|
1984
|
+
productIndex: productIndex,
|
|
1985
|
+
groupResourceIndex: groupResourceIndex,
|
|
1986
|
+
candidateRangeCache: candidateRangeCache,
|
|
1987
|
+
evaluationContext: evaluationContext
|
|
1988
|
+
});
|
|
1989
|
+
totalTimeCount += 1;
|
|
1990
|
+
if (evaluated.status === 'available' || evaluated.status === 'limited') {
|
|
1991
|
+
availableTimeCount += 1;
|
|
1992
|
+
}
|
|
1993
|
+
maxPartySize = Math.max(maxPartySize, evaluated.maxPartySize);
|
|
1994
|
+
pushStatusAggregate(productStatusAggregate, evaluated.status);
|
|
1995
|
+
evaluated.reasonCodes.forEach(function (reasonCode) {
|
|
1996
|
+
return productReasonCodeSet.add(reasonCode);
|
|
1997
|
+
});
|
|
1998
|
+
var dateAggregate = dateStatusMap.get(candidate.date) || createStatusAggregate();
|
|
1999
|
+
pushStatusAggregate(dateAggregate, evaluated.status);
|
|
2000
|
+
dateStatusMap.set(candidate.date, dateAggregate);
|
|
2001
|
+
});
|
|
2002
|
+
var availableDateCount = Array.from(dateStatusMap.values()).filter(function (aggregate) {
|
|
2003
|
+
var status = summarizeStatusAggregate(aggregate);
|
|
2004
|
+
return status === 'available' || status === 'limited';
|
|
2005
|
+
}).length;
|
|
2006
|
+
var totalDateCount = dateStatusMap.size;
|
|
2007
|
+
var status = summarizeStatusAggregate(productStatusAggregate);
|
|
2008
|
+
var item = {
|
|
2009
|
+
productId: group.productId,
|
|
2010
|
+
title: group.title,
|
|
2011
|
+
status: status,
|
|
2012
|
+
reasonCodes: Array.from(productReasonCodeSet),
|
|
2013
|
+
availableDateCount: availableDateCount,
|
|
2014
|
+
totalDateCount: totalDateCount,
|
|
2015
|
+
availableTimeCount: availableTimeCount,
|
|
2016
|
+
totalTimeCount: totalTimeCount,
|
|
2017
|
+
maxPartySize: maxPartySize
|
|
2018
|
+
};
|
|
2019
|
+
return {
|
|
2020
|
+
productId: product.id,
|
|
2021
|
+
title: product.title,
|
|
2022
|
+
item: item
|
|
2023
|
+
};
|
|
1059
2024
|
});
|
|
1060
|
-
|
|
1061
|
-
|
|
1062
|
-
|
|
1063
|
-
|
|
2025
|
+
return {
|
|
2026
|
+
target: 'product',
|
|
2027
|
+
evaluatedAt: evaluatedAt,
|
|
2028
|
+
snapshotId: projection.meta.snapshotId,
|
|
2029
|
+
data: data
|
|
2030
|
+
};
|
|
2031
|
+
}
|
|
2032
|
+
function summarizeDateStatus(statuses) {
|
|
2033
|
+
if (!statuses.length) return 'unavailable';
|
|
2034
|
+
if (statuses.some(function (status) {
|
|
2035
|
+
return status === 'available';
|
|
2036
|
+
})) return 'available';
|
|
2037
|
+
if (statuses.some(function (status) {
|
|
2038
|
+
return status === 'limited';
|
|
2039
|
+
})) return 'limited';
|
|
2040
|
+
if (statuses.some(function (status) {
|
|
2041
|
+
return status === 'full';
|
|
2042
|
+
})) return 'full';
|
|
2043
|
+
if (statuses.every(function (status) {
|
|
2044
|
+
return status === 'past';
|
|
2045
|
+
})) return 'past';
|
|
2046
|
+
return 'unavailable';
|
|
2047
|
+
}
|
|
2048
|
+
function summarizeProductStatus(statuses) {
|
|
2049
|
+
if (!statuses.length) return 'unavailable';
|
|
2050
|
+
if (statuses.some(function (status) {
|
|
2051
|
+
return status === 'available';
|
|
2052
|
+
})) return 'available';
|
|
2053
|
+
if (statuses.some(function (status) {
|
|
2054
|
+
return status === 'limited';
|
|
2055
|
+
})) return 'limited';
|
|
2056
|
+
if (statuses.some(function (status) {
|
|
2057
|
+
return status === 'full';
|
|
2058
|
+
})) return 'full';
|
|
2059
|
+
if (statuses.every(function (status) {
|
|
2060
|
+
return status === 'past';
|
|
2061
|
+
})) return 'past';
|
|
2062
|
+
return 'unavailable';
|
|
2063
|
+
}
|
|
2064
|
+
export function validateAvailabilitySelection(projection, params) {
|
|
2065
|
+
var _extractFixedOffsetMi7;
|
|
2066
|
+
var normalizedPartySize = normalizePartySize(params.partySize);
|
|
2067
|
+
var normalizedBookingCount = normalizeBookingCount(params.bookingCount);
|
|
2068
|
+
assertRangesWithinProjection(projection, [params.candidate]);
|
|
2069
|
+
var productIndex = buildProductIndex(projection);
|
|
2070
|
+
var evaluatedAt = formatDateTime(localDateToScalar(new Date()), projection.meta.timezone, (_extractFixedOffsetMi7 = extractFixedOffsetMinutes(projection.meta.now)) !== null && _extractFixedOffsetMi7 !== void 0 ? _extractFixedOffsetMi7 : captureRuntimeOffsetMinutes());
|
|
2071
|
+
var evaluated = evaluateCandidate(projection, params.candidate, {
|
|
2072
|
+
partySize: normalizedPartySize,
|
|
2073
|
+
evaluatedAt: evaluatedAt,
|
|
2074
|
+
requirementSelections: params.requirementSelections,
|
|
2075
|
+
allowImplicitSelection: false,
|
|
2076
|
+
cellIndex: buildAvailabilityCellIndex(projection),
|
|
2077
|
+
productIndex: productIndex,
|
|
2078
|
+
groupResourceIndex: buildGroupResourceIndex({
|
|
2079
|
+
projection: projection,
|
|
2080
|
+
partySize: normalizedPartySize
|
|
2081
|
+
}),
|
|
2082
|
+
candidateRangeCache: new Map(),
|
|
2083
|
+
evaluationContext: buildEvaluationContext({
|
|
2084
|
+
projection: projection,
|
|
2085
|
+
evaluatedAt: evaluatedAt,
|
|
2086
|
+
requirementSelections: params.requirementSelections
|
|
2087
|
+
})
|
|
2088
|
+
});
|
|
2089
|
+
var conflicts = [];
|
|
2090
|
+
if (evaluated.gate) {
|
|
2091
|
+
conflicts.push({
|
|
2092
|
+
code: evaluated.gate.conflictCode,
|
|
2093
|
+
message: evaluated.gate.status === 'past' ? '候选时间已过期' : '候选时间不满足预订时间门禁',
|
|
2094
|
+
reasonCodes: evaluated.gate.reasonCodes,
|
|
2095
|
+
productId: params.candidate.productId,
|
|
2096
|
+
candidateKey: params.candidate.key
|
|
2097
|
+
});
|
|
2098
|
+
return {
|
|
2099
|
+
ok: false,
|
|
2100
|
+
status: evaluated.gate.status === 'past' ? 'past' : 'conflict',
|
|
2101
|
+
assignments: [],
|
|
2102
|
+
conflicts: conflicts,
|
|
2103
|
+
availability: evaluated.item
|
|
2104
|
+
};
|
|
2105
|
+
}
|
|
2106
|
+
var requiredGroups = evaluated.item.requirementGroups.filter(function (group) {
|
|
2107
|
+
return group.required;
|
|
2108
|
+
});
|
|
2109
|
+
var findSelectionForGroup = function findSelectionForGroup(group) {
|
|
2110
|
+
return params.requirementSelections.find(function (selection) {
|
|
2111
|
+
return selection.requirementGroupId && selection.requirementGroupId === group.groupId || selection.formId !== undefined && sameId(selection.formId, group.formId);
|
|
2112
|
+
});
|
|
2113
|
+
};
|
|
2114
|
+
requiredGroups.forEach(function (group) {
|
|
2115
|
+
var selection = findSelectionForGroup(group);
|
|
2116
|
+
var selectedCount = new Set(((selection === null || selection === void 0 ? void 0 : selection.resourceIds) || []).map(String)).size;
|
|
2117
|
+
if (selectedCount < group.min) {
|
|
2118
|
+
conflicts.push({
|
|
2119
|
+
code: 'missing_resource',
|
|
2120
|
+
message: "\u8D44\u6E90\u7EC4 ".concat(group.groupId, " \u7F3A\u5C11\u5DF2\u9009\u8D44\u6E90"),
|
|
2121
|
+
productId: params.candidate.productId,
|
|
2122
|
+
requirementGroupId: group.groupId,
|
|
2123
|
+
formId: group.formId,
|
|
2124
|
+
candidateKey: params.candidate.key
|
|
2125
|
+
});
|
|
2126
|
+
return;
|
|
2127
|
+
}
|
|
2128
|
+
if (selectedCount > group.max) {
|
|
2129
|
+
conflicts.push({
|
|
2130
|
+
code: 'invalid_selection_count',
|
|
2131
|
+
message: "\u8D44\u6E90\u7EC4 ".concat(group.groupId, " \u5DF2\u9009\u8D44\u6E90\u6570\u91CF\u8D85\u51FA\u4E0A\u9650"),
|
|
2132
|
+
productId: params.candidate.productId,
|
|
2133
|
+
requirementGroupId: group.groupId,
|
|
2134
|
+
formId: group.formId,
|
|
2135
|
+
candidateKey: params.candidate.key
|
|
2136
|
+
});
|
|
2137
|
+
return;
|
|
2138
|
+
}
|
|
2139
|
+
if (group.status === 'available' || group.status === 'limited') return;
|
|
2140
|
+
conflicts.push({
|
|
2141
|
+
code: group.status === 'past' ? 'past' : group.status === 'full' ? 'resource_full' : 'missing_resource',
|
|
2142
|
+
message: "\u8D44\u6E90\u7EC4 ".concat(group.groupId, " \u5F53\u524D\u4E0D\u53EF\u63D0\u4EA4"),
|
|
2143
|
+
productId: params.candidate.productId,
|
|
2144
|
+
requirementGroupId: group.groupId,
|
|
2145
|
+
formId: group.formId,
|
|
2146
|
+
candidateKey: params.candidate.key
|
|
2147
|
+
});
|
|
2148
|
+
});
|
|
2149
|
+
params.requirementSelections.forEach(function (selection) {
|
|
2150
|
+
var matchedGroup = evaluated.item.requirementGroups.find(function (group) {
|
|
2151
|
+
return selection.requirementGroupId ? group.groupId === selection.requirementGroupId : sameId(group.formId, selection.formId);
|
|
2152
|
+
});
|
|
2153
|
+
if (!matchedGroup) {
|
|
2154
|
+
conflicts.push({
|
|
2155
|
+
code: 'invalid_requirement_group',
|
|
2156
|
+
message: '选择的资源组不属于当前商品',
|
|
2157
|
+
productId: params.candidate.productId,
|
|
2158
|
+
requirementGroupId: selection.requirementGroupId,
|
|
2159
|
+
formId: selection.formId,
|
|
2160
|
+
candidateKey: params.candidate.key
|
|
2161
|
+
});
|
|
2162
|
+
return;
|
|
2163
|
+
}
|
|
2164
|
+
var selectedCount = new Set(selection.resourceIds.map(String)).size;
|
|
2165
|
+
if (!matchedGroup.required && (selectedCount < matchedGroup.min || selectedCount > matchedGroup.max)) {
|
|
2166
|
+
conflicts.push({
|
|
2167
|
+
code: 'invalid_selection_count',
|
|
2168
|
+
message: "\u8D44\u6E90\u7EC4 ".concat(matchedGroup.groupId, " \u5DF2\u9009\u8D44\u6E90\u6570\u91CF\u4E0D\u6EE1\u8DB3 ").concat(matchedGroup.min, "-").concat(matchedGroup.max),
|
|
2169
|
+
productId: params.candidate.productId,
|
|
2170
|
+
requirementGroupId: matchedGroup.groupId,
|
|
2171
|
+
formId: matchedGroup.formId,
|
|
2172
|
+
candidateKey: params.candidate.key
|
|
2173
|
+
});
|
|
2174
|
+
}
|
|
2175
|
+
selection.resourceIds.forEach(function (resourceId) {
|
|
2176
|
+
var option = matchedGroup === null || matchedGroup === void 0 ? void 0 : matchedGroup.options.find(function (item) {
|
|
2177
|
+
return sameId(item.resourceId, resourceId);
|
|
2178
|
+
});
|
|
2179
|
+
var matchedCell = option === null || option === void 0 ? void 0 : option.matchedCell;
|
|
2180
|
+
var remainingCapacity = matchedCell === null || matchedCell === void 0 ? void 0 : matchedCell.remainingCapacity;
|
|
2181
|
+
if (option && (option.status === 'available' || option.status === 'limited') && remainingCapacity !== undefined) {
|
|
2182
|
+
var capacityPerBooking = normalizedPartySize !== null && normalizedPartySize !== void 0 ? normalizedPartySize : 1;
|
|
2183
|
+
var availableQuantity = Math.max(0, Math.floor(remainingCapacity / capacityPerBooking));
|
|
2184
|
+
if (normalizedBookingCount > availableQuantity) {
|
|
2185
|
+
conflicts.push({
|
|
2186
|
+
code: 'resource_capacity_exceeded',
|
|
2187
|
+
message: "\u6240\u9009\u8D44\u6E90\u300C".concat(option.resourceName || String(resourceId), "\u300D\u5F53\u524D\u6700\u591A\u53EF\u9884\u8BA2 ").concat(availableQuantity, " \u4E2A"),
|
|
2188
|
+
productId: params.candidate.productId,
|
|
2189
|
+
requirementGroupId: matchedGroup === null || matchedGroup === void 0 ? void 0 : matchedGroup.groupId,
|
|
2190
|
+
formId: matchedGroup === null || matchedGroup === void 0 ? void 0 : matchedGroup.formId,
|
|
2191
|
+
resourceId: resourceId,
|
|
2192
|
+
resourceName: option.resourceName,
|
|
2193
|
+
candidateKey: params.candidate.key,
|
|
2194
|
+
requestedQuantity: normalizedBookingCount,
|
|
2195
|
+
availableQuantity: availableQuantity,
|
|
2196
|
+
remainingCapacity: remainingCapacity,
|
|
2197
|
+
capacityPerBooking: capacityPerBooking
|
|
2198
|
+
});
|
|
2199
|
+
return;
|
|
2200
|
+
}
|
|
2201
|
+
}
|
|
2202
|
+
if (!option || option.status !== 'available' && option.status !== 'limited') {
|
|
2203
|
+
conflicts.push({
|
|
2204
|
+
code: 'resource_unavailable',
|
|
2205
|
+
message: "\u8D44\u6E90 ".concat(String(resourceId), " \u5F53\u524D\u4E0D\u53EF\u63D0\u4EA4"),
|
|
2206
|
+
productId: params.candidate.productId,
|
|
2207
|
+
requirementGroupId: matchedGroup === null || matchedGroup === void 0 ? void 0 : matchedGroup.groupId,
|
|
2208
|
+
formId: matchedGroup === null || matchedGroup === void 0 ? void 0 : matchedGroup.formId,
|
|
2209
|
+
resourceId: resourceId,
|
|
2210
|
+
candidateKey: params.candidate.key
|
|
2211
|
+
});
|
|
2212
|
+
}
|
|
2213
|
+
});
|
|
1064
2214
|
});
|
|
1065
2215
|
return {
|
|
1066
|
-
ok: conflicts.length === 0,
|
|
1067
|
-
conflicts:
|
|
2216
|
+
ok: conflicts.length === 0 && (evaluated.item.status === 'available' || evaluated.item.status === 'limited'),
|
|
2217
|
+
status: conflicts.length === 0 ? evaluated.item.status : 'conflict',
|
|
2218
|
+
assignments: evaluated.assignments,
|
|
2219
|
+
conflicts: conflicts,
|
|
2220
|
+
availability: evaluated.item
|
|
1068
2221
|
};
|
|
1069
2222
|
}
|