@pisell/pisellos 3.0.58 → 3.0.60
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/modules/Date/index.d.ts +17 -0
- package/dist/modules/Date/index.js +92 -4
- package/dist/solution/BookingByStep/utils/timeslots.js +161 -69
- package/lib/modules/Date/index.d.ts +17 -0
- package/lib/modules/Date/index.js +64 -0
- package/lib/solution/BookingByStep/utils/timeslots.js +103 -49
- package/package.json +1 -1
|
@@ -17,6 +17,23 @@ export declare class DateModule extends BaseModule implements Module, DateModule
|
|
|
17
17
|
getDateList(): ITime[];
|
|
18
18
|
setDateList(dateList: ITime[]): void;
|
|
19
19
|
fetchResourceDates(params: IGetAvailableTimeListParams): Promise<any>;
|
|
20
|
+
/**
|
|
21
|
+
* 将时间向上取整到下一个10分钟整数
|
|
22
|
+
*
|
|
23
|
+
* @param time dayjs 时间对象
|
|
24
|
+
* @returns 向上取整后的时间字符串 (YYYY-MM-DD HH:mm 格式)
|
|
25
|
+
*/
|
|
26
|
+
private roundUpToNext10Minutes;
|
|
27
|
+
/**
|
|
28
|
+
* 校正资源时间段数据
|
|
29
|
+
*
|
|
30
|
+
* 如果时间段的 start_at 早于资源的 start_time,将其同步为 start_time 的下一个10分钟整数
|
|
31
|
+
* 如果修正后 end_at 也早于修正后的 start_time,则删除该时间段
|
|
32
|
+
*
|
|
33
|
+
* @param resourcesData 资源数据数组
|
|
34
|
+
* @returns 校正后的资源数据数组
|
|
35
|
+
*/
|
|
36
|
+
private correctResourceTimeSlots;
|
|
20
37
|
getResourceAvailableTimeList(params: IGetAvailableTimeListParams): Promise<ITime[]>;
|
|
21
38
|
clearDateRange(): void;
|
|
22
39
|
storeChange(): void;
|
|
@@ -1,4 +1,6 @@
|
|
|
1
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); }
|
|
2
|
+
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; }
|
|
3
|
+
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; }
|
|
2
4
|
function _toConsumableArray(arr) { return _arrayWithoutHoles(arr) || _iterableToArray(arr) || _unsupportedIterableToArray(arr) || _nonIterableSpread(); }
|
|
3
5
|
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
6
|
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); }
|
|
@@ -21,6 +23,7 @@ function _getPrototypeOf(o) { _getPrototypeOf = Object.setPrototypeOf ? Object.g
|
|
|
21
23
|
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; }
|
|
22
24
|
function _toPropertyKey(t) { var i = _toPrimitive(t, "string"); return "symbol" == _typeof(i) ? i : String(i); }
|
|
23
25
|
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); }
|
|
26
|
+
import dayjs from 'dayjs';
|
|
24
27
|
import { BaseModule } from "../BaseModule";
|
|
25
28
|
import { disableAllDates, generateMonthDates, handleAvailableDateByResource, handleAvailableDatesByRules } from "./utils";
|
|
26
29
|
import cloneDeep from 'lodash-es/cloneDeep';
|
|
@@ -204,22 +207,107 @@ export var DateModule = /*#__PURE__*/function (_BaseModule) {
|
|
|
204
207
|
});
|
|
205
208
|
case 6:
|
|
206
209
|
res = _context3.sent;
|
|
210
|
+
// 校正资源时间段数据
|
|
211
|
+
if (res !== null && res !== void 0 && res.data && Array.isArray(res.data)) {
|
|
212
|
+
res.data = this.correctResourceTimeSlots(res.data);
|
|
213
|
+
}
|
|
207
214
|
return _context3.abrupt("return", res);
|
|
208
|
-
case
|
|
209
|
-
_context3.prev =
|
|
215
|
+
case 11:
|
|
216
|
+
_context3.prev = 11;
|
|
210
217
|
_context3.t0 = _context3["catch"](3);
|
|
211
218
|
console.error(_context3.t0);
|
|
212
|
-
case
|
|
219
|
+
case 14:
|
|
213
220
|
case "end":
|
|
214
221
|
return _context3.stop();
|
|
215
222
|
}
|
|
216
|
-
}, _callee3, this, [[3,
|
|
223
|
+
}, _callee3, this, [[3, 11]]);
|
|
217
224
|
}));
|
|
218
225
|
function fetchResourceDates(_x4) {
|
|
219
226
|
return _fetchResourceDates.apply(this, arguments);
|
|
220
227
|
}
|
|
221
228
|
return fetchResourceDates;
|
|
222
229
|
}()
|
|
230
|
+
/**
|
|
231
|
+
* 将时间向上取整到下一个10分钟整数
|
|
232
|
+
*
|
|
233
|
+
* @param time dayjs 时间对象
|
|
234
|
+
* @returns 向上取整后的时间字符串 (YYYY-MM-DD HH:mm 格式)
|
|
235
|
+
*/
|
|
236
|
+
}, {
|
|
237
|
+
key: "roundUpToNext10Minutes",
|
|
238
|
+
value: function roundUpToNext10Minutes(time) {
|
|
239
|
+
var minutes = time.minute();
|
|
240
|
+
var remainder = minutes % 10;
|
|
241
|
+
if (remainder === 0) {
|
|
242
|
+
// 如果已经是10分钟的整数,保持不变
|
|
243
|
+
return time.format('YYYY-MM-DD HH:mm');
|
|
244
|
+
} else {
|
|
245
|
+
// 向上取整到下一个10分钟
|
|
246
|
+
var minutesToAdd = 10 - remainder;
|
|
247
|
+
var roundedTime = time.add(minutesToAdd, 'minute');
|
|
248
|
+
return roundedTime.format('YYYY-MM-DD HH:mm');
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
/**
|
|
253
|
+
* 校正资源时间段数据
|
|
254
|
+
*
|
|
255
|
+
* 如果时间段的 start_at 早于资源的 start_time,将其同步为 start_time 的下一个10分钟整数
|
|
256
|
+
* 如果修正后 end_at 也早于修正后的 start_time,则删除该时间段
|
|
257
|
+
*
|
|
258
|
+
* @param resourcesData 资源数据数组
|
|
259
|
+
* @returns 校正后的资源数据数组
|
|
260
|
+
*/
|
|
261
|
+
}, {
|
|
262
|
+
key: "correctResourceTimeSlots",
|
|
263
|
+
value: function correctResourceTimeSlots(resourcesData) {
|
|
264
|
+
var _this2 = this;
|
|
265
|
+
return resourcesData.map(function (resource) {
|
|
266
|
+
// 检查资源是否有 times 数组和 start_time
|
|
267
|
+
if (!resource.times || !Array.isArray(resource.times) || !resource.start_time) {
|
|
268
|
+
return resource;
|
|
269
|
+
}
|
|
270
|
+
var resourceStartTime = dayjs(resource.start_time);
|
|
271
|
+
|
|
272
|
+
// 过滤和修正时间段
|
|
273
|
+
var correctedTimes = resource.times.map(function (timeSlot) {
|
|
274
|
+
if (!timeSlot.start_at || !timeSlot.end_at) {
|
|
275
|
+
return timeSlot;
|
|
276
|
+
}
|
|
277
|
+
var startAt = dayjs(timeSlot.start_at);
|
|
278
|
+
var endAt = dayjs(timeSlot.end_at);
|
|
279
|
+
|
|
280
|
+
// 如果 start_at 早于资源的 start_time,需要修正
|
|
281
|
+
if (startAt.isBefore(resourceStartTime)) {
|
|
282
|
+
// 将 start_time 向上取整到下一个10分钟整数
|
|
283
|
+
var roundedStartTime = _this2.roundUpToNext10Minutes(resourceStartTime);
|
|
284
|
+
var roundedStartTimeDayjs = dayjs(roundedStartTime);
|
|
285
|
+
console.log("[DateModule] \u4FEE\u6B63\u65F6\u95F4\u6BB5\u5F00\u59CB\u65F6\u95F4: ".concat(timeSlot.start_at, " -> ").concat(roundedStartTime, " (\u8D44\u6E90ID: ").concat(resource.id, ", \u539F\u59CBstart_time: ").concat(resource.start_time, ")"));
|
|
286
|
+
|
|
287
|
+
// 将 start_at 修正为向上取整后的时间
|
|
288
|
+
var correctedTimeSlot = _objectSpread(_objectSpread({}, timeSlot), {}, {
|
|
289
|
+
start_at: roundedStartTime
|
|
290
|
+
});
|
|
291
|
+
|
|
292
|
+
// 检查修正后的 end_at 是否也早于修正后的 start_time
|
|
293
|
+
if (endAt.isBefore(roundedStartTimeDayjs)) {
|
|
294
|
+
console.log("[DateModule] \u65F6\u95F4\u6BB5\u65E0\u6548\uFF0C\u5C06\u88AB\u5220\u9664: ".concat(timeSlot.start_at, " - ").concat(timeSlot.end_at, " (\u8D44\u6E90ID: ").concat(resource.id, ", \u4FEE\u6B63\u540Estart_time: ").concat(roundedStartTime, ")"));
|
|
295
|
+
return null; // 标记为删除
|
|
296
|
+
}
|
|
297
|
+
return correctedTimeSlot;
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
// 不需要修正的时间段直接返回
|
|
301
|
+
return timeSlot;
|
|
302
|
+
}).filter(function (timeSlot) {
|
|
303
|
+
return timeSlot !== null;
|
|
304
|
+
}); // 过滤掉被删除的时间段
|
|
305
|
+
|
|
306
|
+
return _objectSpread(_objectSpread({}, resource), {}, {
|
|
307
|
+
times: correctedTimes
|
|
308
|
+
});
|
|
309
|
+
});
|
|
310
|
+
}
|
|
223
311
|
}, {
|
|
224
312
|
key: "getResourceAvailableTimeList",
|
|
225
313
|
value: function () {
|
|
@@ -95,6 +95,12 @@ export function findFastestAvailableResource(_ref2) {
|
|
|
95
95
|
var currentTime = dayjs();
|
|
96
96
|
var fastestTime = null;
|
|
97
97
|
var fastestResources = [];
|
|
98
|
+
console.log('[TimeslotUtils] 查找最快可用资源:', {
|
|
99
|
+
currentTime: currentTime.format('YYYY-MM-DD HH:mm:ss'),
|
|
100
|
+
resourceCount: resources.length,
|
|
101
|
+
currentCapacity: currentCapacity,
|
|
102
|
+
countMap: countMap
|
|
103
|
+
});
|
|
98
104
|
|
|
99
105
|
// 遍历所有资源,找到最快可用的时间点
|
|
100
106
|
var _iterator2 = _createForOfIteratorHelper(resources),
|
|
@@ -102,75 +108,120 @@ export function findFastestAvailableResource(_ref2) {
|
|
|
102
108
|
try {
|
|
103
109
|
for (_iterator2.s(); !(_step2 = _iterator2.n()).done;) {
|
|
104
110
|
var _resource = _step2.value;
|
|
105
|
-
//
|
|
111
|
+
// 获取资源当天且还在工作时间内的时间段
|
|
106
112
|
var todayTimes = _resource.times.filter(function (time) {
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
// 按开始时间排序
|
|
112
|
-
todayTimes.sort(function (a, b) {
|
|
113
|
-
return dayjs(a.start_at).diff(dayjs(b.start_at));
|
|
113
|
+
var isToday = dayjs(time.start_at).isSame(currentTime, 'day');
|
|
114
|
+
var isStillWorking = dayjs(time.end_at).isAfter(currentTime);
|
|
115
|
+
return isToday && isStillWorking;
|
|
114
116
|
});
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
117
|
+
if (todayTimes.length === 0) {
|
|
118
|
+
console.log("[TimeslotUtils] \u8D44\u6E90 ".concat(_resource.id, "(").concat(_resource.main_field, ") \u4ECA\u65E5\u65E0\u53EF\u7528\u65F6\u95F4\u6BB5"));
|
|
119
|
+
continue;
|
|
120
|
+
}
|
|
121
|
+
var _iterator4 = _createForOfIteratorHelper(todayTimes),
|
|
122
|
+
_step4;
|
|
119
123
|
try {
|
|
120
124
|
var _loop = function _loop() {
|
|
121
|
-
var
|
|
122
|
-
var
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
125
|
+
var _time$event_list, _time$event_list2;
|
|
126
|
+
var time = _step4.value;
|
|
127
|
+
var workStartTime = dayjs(time.start_at);
|
|
128
|
+
var workEndTime = dayjs(time.end_at);
|
|
129
|
+
|
|
130
|
+
// 确定检查的起始时间(当前时间 vs 工作开始时间)
|
|
131
|
+
var nextAvailableTime = currentTime.isBefore(workStartTime) ? workStartTime : currentTime;
|
|
132
|
+
console.log("[TimeslotUtils] \u68C0\u67E5\u8D44\u6E90 ".concat(_resource.id, "(").concat(_resource.main_field, "):"), {
|
|
133
|
+
workTime: "".concat(workStartTime.format('HH:mm'), "-").concat(workEndTime.format('HH:mm')),
|
|
134
|
+
checkStartTime: nextAvailableTime.format('HH:mm:ss'),
|
|
135
|
+
eventCount: ((_time$event_list = time.event_list) === null || _time$event_list === void 0 ? void 0 : _time$event_list.length) || 0
|
|
136
|
+
});
|
|
137
|
+
|
|
138
|
+
// 获取所有影响的预约事件(当前时间之后的)
|
|
139
|
+
var relevantEvents = ((_time$event_list2 = time.event_list) === null || _time$event_list2 === void 0 ? void 0 : _time$event_list2.filter(function (event) {
|
|
140
|
+
var eventEnd = dayjs(event.end_at);
|
|
141
|
+
return eventEnd.isAfter(nextAvailableTime);
|
|
142
|
+
})) || [];
|
|
143
|
+
|
|
144
|
+
// 按开始时间排序
|
|
145
|
+
relevantEvents.sort(function (a, b) {
|
|
146
|
+
return dayjs(a.start_at).diff(dayjs(b.start_at));
|
|
147
|
+
});
|
|
148
|
+
|
|
149
|
+
// 检查从 nextAvailableTime 开始的可用性
|
|
150
|
+
var finalAvailableTime = nextAvailableTime;
|
|
151
|
+
var _iterator5 = _createForOfIteratorHelper(relevantEvents),
|
|
152
|
+
_step5;
|
|
153
|
+
try {
|
|
154
|
+
for (_iterator5.s(); !(_step5 = _iterator5.n()).done;) {
|
|
155
|
+
var _event = _step5.value;
|
|
156
|
+
var _eventStart = dayjs(_event.start_at);
|
|
157
|
+
var _eventEnd = dayjs(_event.end_at);
|
|
158
|
+
console.log("[TimeslotUtils] \u68C0\u67E5\u4E8B\u4EF6\u51B2\u7A81:", {
|
|
159
|
+
resourceId: _resource.id,
|
|
160
|
+
eventTime: "".concat(_eventStart.format('HH:mm'), "-").concat(_eventEnd.format('HH:mm')),
|
|
161
|
+
checkTime: finalAvailableTime.format('HH:mm:ss'),
|
|
162
|
+
pax: _event.pax || 1
|
|
163
|
+
});
|
|
164
|
+
|
|
165
|
+
// 检查资源类型和容量
|
|
166
|
+
if (_resource.resourceType === 'single' || (_resource.capacity || 1) === 1) {
|
|
167
|
+
// 单人预约资源:检查时间冲突
|
|
168
|
+
if (finalAvailableTime.isBefore(_eventEnd) && _eventStart.isBefore(finalAvailableTime.add(30, 'minute'))) {
|
|
169
|
+
// 有冲突,使用事件结束时间
|
|
170
|
+
finalAvailableTime = _eventEnd;
|
|
171
|
+
console.log("[TimeslotUtils] \u53D1\u73B0\u65F6\u95F4\u51B2\u7A81\uFF0C\u8C03\u6574\u5230: ".concat(finalAvailableTime.format('HH:mm:ss')));
|
|
172
|
+
}
|
|
173
|
+
} else {
|
|
174
|
+
// 多人预约资源:检查容量
|
|
175
|
+
var totalCapacity = _resource.capacity || 0;
|
|
176
|
+
var currentUsedCapacity = countMap[_resource.id] || 0;
|
|
177
|
+
var eventUsedCapacity = _event.pax || 1;
|
|
178
|
+
|
|
179
|
+
// 如果在事件时间范围内,检查容量是否足够
|
|
180
|
+
if (finalAvailableTime.isBefore(_eventEnd) && _eventStart.isBefore(finalAvailableTime.add(30, 'minute'))) {
|
|
181
|
+
var totalRequiredCapacity = currentUsedCapacity + currentCapacity + eventUsedCapacity;
|
|
182
|
+
if (totalRequiredCapacity > totalCapacity) {
|
|
183
|
+
// 容量不足,使用事件结束时间
|
|
184
|
+
finalAvailableTime = _eventEnd;
|
|
185
|
+
console.log("[TimeslotUtils] \u5BB9\u91CF\u4E0D\u8DB3\uFF0C\u8C03\u6574\u5230: ".concat(finalAvailableTime.format('HH:mm:ss')));
|
|
186
|
+
}
|
|
187
|
+
}
|
|
159
188
|
}
|
|
160
|
-
return 1; // break
|
|
161
189
|
}
|
|
190
|
+
|
|
191
|
+
// 确保可用时间在工作时间内
|
|
192
|
+
} catch (err) {
|
|
193
|
+
_iterator5.e(err);
|
|
194
|
+
} finally {
|
|
195
|
+
_iterator5.f();
|
|
196
|
+
}
|
|
197
|
+
if (finalAvailableTime.isAfter(workEndTime)) {
|
|
198
|
+
console.log("[TimeslotUtils] \u8D44\u6E90 ".concat(_resource.id, " \u53EF\u7528\u65F6\u95F4\u8D85\u51FA\u5DE5\u4F5C\u65F6\u95F4\uFF0C\u8DF3\u8FC7"));
|
|
199
|
+
return 0; // continue
|
|
162
200
|
}
|
|
201
|
+
console.log("[TimeslotUtils] \u8D44\u6E90 ".concat(_resource.id, "(").concat(_resource.main_field, ") \u6700\u5FEB\u53EF\u7528\u65F6\u95F4: ").concat(finalAvailableTime.format('HH:mm:ss')));
|
|
202
|
+
|
|
203
|
+
// 更新最快可用时间和资源
|
|
204
|
+
if (!fastestTime || finalAvailableTime.isBefore(fastestTime)) {
|
|
205
|
+
fastestTime = finalAvailableTime;
|
|
206
|
+
fastestResources = [_resource];
|
|
207
|
+
console.log("[TimeslotUtils] \u66F4\u65B0\u6700\u5FEB\u65F6\u95F4: ".concat(fastestTime.format('HH:mm:ss'), ", \u8D44\u6E90: ").concat(_resource.main_field));
|
|
208
|
+
} else if (finalAvailableTime.isSame(fastestTime)) {
|
|
209
|
+
fastestResources.push(_resource);
|
|
210
|
+
console.log("[TimeslotUtils] \u6DFB\u52A0\u76F8\u540C\u65F6\u95F4\u8D44\u6E90: ".concat(_resource.main_field));
|
|
211
|
+
}
|
|
212
|
+
return 1; // break
|
|
213
|
+
// 每个资源只需要计算一次
|
|
163
214
|
},
|
|
164
215
|
_ret;
|
|
165
|
-
for (
|
|
216
|
+
for (_iterator4.s(); !(_step4 = _iterator4.n()).done;) {
|
|
166
217
|
_ret = _loop();
|
|
167
218
|
if (_ret === 0) continue;
|
|
168
219
|
if (_ret === 1) break;
|
|
169
220
|
}
|
|
170
221
|
} catch (err) {
|
|
171
|
-
|
|
222
|
+
_iterator4.e(err);
|
|
172
223
|
} finally {
|
|
173
|
-
|
|
224
|
+
_iterator4.f();
|
|
174
225
|
}
|
|
175
226
|
}
|
|
176
227
|
|
|
@@ -180,31 +231,72 @@ export function findFastestAvailableResource(_ref2) {
|
|
|
180
231
|
} finally {
|
|
181
232
|
_iterator2.f();
|
|
182
233
|
}
|
|
183
|
-
if (!fastestTime || fastestResources.length === 0)
|
|
234
|
+
if (!fastestTime || fastestResources.length === 0) {
|
|
235
|
+
console.log('[TimeslotUtils] 未找到可用资源');
|
|
236
|
+
return null;
|
|
237
|
+
}
|
|
238
|
+
console.log("[TimeslotUtils] \u627E\u5230 ".concat(fastestResources.length, " \u4E2A\u6700\u5FEB\u53EF\u7528\u8D44\u6E90\uFF0C\u65F6\u95F4: ").concat(fastestTime.format('HH:mm:ss')));
|
|
184
239
|
|
|
185
240
|
// 如果只有一个最快可用的资源,直接返回
|
|
186
|
-
if (fastestResources.length === 1)
|
|
241
|
+
if (fastestResources.length === 1) {
|
|
242
|
+
console.log("[TimeslotUtils] \u8FD4\u56DE\u552F\u4E00\u6700\u5FEB\u8D44\u6E90: ".concat(fastestResources[0].main_field));
|
|
243
|
+
return fastestResources[0];
|
|
244
|
+
}
|
|
187
245
|
|
|
188
|
-
//
|
|
246
|
+
// 如果有多个最快可用的资源,选择空闲时间最长的资源
|
|
247
|
+
var selectedResource = fastestResources[0];
|
|
189
248
|
var maxIdleTime = 0;
|
|
190
|
-
|
|
249
|
+
console.log("[TimeslotUtils] \u6BD4\u8F83 ".concat(fastestResources.length, " \u4E2A\u8D44\u6E90\u7684\u7A7A\u95F2\u65F6\u95F4:"));
|
|
191
250
|
for (var _i2 = 0, _fastestResources = fastestResources; _i2 < _fastestResources.length; _i2++) {
|
|
251
|
+
var _workingTime$event_li;
|
|
192
252
|
var resource = _fastestResources[_i2];
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
253
|
+
// 找到该资源当天且还在工作的时间段
|
|
254
|
+
var workingTime = resource.times.find(function (time) {
|
|
255
|
+
var isToday = dayjs(time.start_at).isSame(fastestTime, 'day');
|
|
256
|
+
var isStillWorking = dayjs(time.end_at).isAfter(fastestTime);
|
|
257
|
+
return isToday && isStillWorking;
|
|
258
|
+
});
|
|
259
|
+
if (!workingTime) continue;
|
|
260
|
+
var workEndTime = dayjs(workingTime.end_at);
|
|
261
|
+
|
|
262
|
+
// 计算从最快可用时间到工作结束时间的空闲时长(分钟)
|
|
263
|
+
var totalIdleTime = workEndTime.diff(fastestTime, 'minute');
|
|
264
|
+
|
|
265
|
+
// 减去已有预约占用的时间
|
|
266
|
+
var relevantEvents = ((_workingTime$event_li = workingTime.event_list) === null || _workingTime$event_li === void 0 ? void 0 : _workingTime$event_li.filter(function (event) {
|
|
267
|
+
var eventStart = dayjs(event.start_at);
|
|
268
|
+
var eventEnd = dayjs(event.end_at);
|
|
269
|
+
// 只计算在最快可用时间之后的预约
|
|
270
|
+
return eventEnd.isAfter(fastestTime) && eventStart.isAfter(fastestTime);
|
|
271
|
+
})) || [];
|
|
272
|
+
var _iterator3 = _createForOfIteratorHelper(relevantEvents),
|
|
273
|
+
_step3;
|
|
274
|
+
try {
|
|
275
|
+
for (_iterator3.s(); !(_step3 = _iterator3.n()).done;) {
|
|
276
|
+
var event = _step3.value;
|
|
277
|
+
var eventStart = dayjs(event.start_at);
|
|
278
|
+
var eventEnd = dayjs(event.end_at);
|
|
279
|
+
var eventDuration = eventEnd.diff(eventStart, 'minute');
|
|
280
|
+
totalIdleTime -= eventDuration;
|
|
281
|
+
}
|
|
282
|
+
} catch (err) {
|
|
283
|
+
_iterator3.e(err);
|
|
284
|
+
} finally {
|
|
285
|
+
_iterator3.f();
|
|
286
|
+
}
|
|
287
|
+
console.log("[TimeslotUtils] \u8D44\u6E90 ".concat(resource.id, "(").concat(resource.main_field, "):"), {
|
|
288
|
+
工作结束时间: workEndTime.format('HH:mm'),
|
|
289
|
+
总工作时长: workEndTime.diff(fastestTime, 'minute') + '分钟',
|
|
290
|
+
预约占用时长: workEndTime.diff(fastestTime, 'minute') - totalIdleTime + '分钟',
|
|
291
|
+
实际空闲时长: totalIdleTime + '分钟'
|
|
202
292
|
});
|
|
203
|
-
if (
|
|
204
|
-
maxIdleTime =
|
|
293
|
+
if (totalIdleTime > maxIdleTime) {
|
|
294
|
+
maxIdleTime = totalIdleTime;
|
|
205
295
|
selectedResource = resource;
|
|
296
|
+
console.log("[TimeslotUtils] \u66F4\u65B0\u6700\u4F73\u9009\u62E9: ".concat(resource.main_field, " (\u7A7A\u95F2").concat(totalIdleTime, "\u5206\u949F)"));
|
|
206
297
|
}
|
|
207
298
|
}
|
|
299
|
+
console.log("[TimeslotUtils] \u6700\u7EC8\u9009\u62E9\u8D44\u6E90: ".concat(selectedResource.main_field, " (\u6700\u957F\u7A7A\u95F2").concat(maxIdleTime, "\u5206\u949F)"));
|
|
208
300
|
return selectedResource;
|
|
209
301
|
}
|
|
210
302
|
|
|
@@ -17,6 +17,23 @@ export declare class DateModule extends BaseModule implements Module, DateModule
|
|
|
17
17
|
getDateList(): ITime[];
|
|
18
18
|
setDateList(dateList: ITime[]): void;
|
|
19
19
|
fetchResourceDates(params: IGetAvailableTimeListParams): Promise<any>;
|
|
20
|
+
/**
|
|
21
|
+
* 将时间向上取整到下一个10分钟整数
|
|
22
|
+
*
|
|
23
|
+
* @param time dayjs 时间对象
|
|
24
|
+
* @returns 向上取整后的时间字符串 (YYYY-MM-DD HH:mm 格式)
|
|
25
|
+
*/
|
|
26
|
+
private roundUpToNext10Minutes;
|
|
27
|
+
/**
|
|
28
|
+
* 校正资源时间段数据
|
|
29
|
+
*
|
|
30
|
+
* 如果时间段的 start_at 早于资源的 start_time,将其同步为 start_time 的下一个10分钟整数
|
|
31
|
+
* 如果修正后 end_at 也早于修正后的 start_time,则删除该时间段
|
|
32
|
+
*
|
|
33
|
+
* @param resourcesData 资源数据数组
|
|
34
|
+
* @returns 校正后的资源数据数组
|
|
35
|
+
*/
|
|
36
|
+
private correctResourceTimeSlots;
|
|
20
37
|
getResourceAvailableTimeList(params: IGetAvailableTimeListParams): Promise<ITime[]>;
|
|
21
38
|
clearDateRange(): void;
|
|
22
39
|
storeChange(): void;
|
|
@@ -32,6 +32,7 @@ __export(Date_exports, {
|
|
|
32
32
|
DateModule: () => DateModule
|
|
33
33
|
});
|
|
34
34
|
module.exports = __toCommonJS(Date_exports);
|
|
35
|
+
var import_dayjs = __toESM(require("dayjs"));
|
|
35
36
|
var import_BaseModule = require("../BaseModule");
|
|
36
37
|
var import_utils = require("./utils");
|
|
37
38
|
var import_cloneDeep = __toESM(require("lodash-es/cloneDeep"));
|
|
@@ -138,11 +139,74 @@ var DateModule = class extends import_BaseModule.BaseModule {
|
|
|
138
139
|
}, {
|
|
139
140
|
useCache
|
|
140
141
|
});
|
|
142
|
+
if ((res == null ? void 0 : res.data) && Array.isArray(res.data)) {
|
|
143
|
+
res.data = this.correctResourceTimeSlots(res.data);
|
|
144
|
+
}
|
|
141
145
|
return res;
|
|
142
146
|
} catch (error) {
|
|
143
147
|
console.error(error);
|
|
144
148
|
}
|
|
145
149
|
}
|
|
150
|
+
/**
|
|
151
|
+
* 将时间向上取整到下一个10分钟整数
|
|
152
|
+
*
|
|
153
|
+
* @param time dayjs 时间对象
|
|
154
|
+
* @returns 向上取整后的时间字符串 (YYYY-MM-DD HH:mm 格式)
|
|
155
|
+
*/
|
|
156
|
+
roundUpToNext10Minutes(time) {
|
|
157
|
+
const minutes = time.minute();
|
|
158
|
+
const remainder = minutes % 10;
|
|
159
|
+
if (remainder === 0) {
|
|
160
|
+
return time.format("YYYY-MM-DD HH:mm");
|
|
161
|
+
} else {
|
|
162
|
+
const minutesToAdd = 10 - remainder;
|
|
163
|
+
const roundedTime = time.add(minutesToAdd, "minute");
|
|
164
|
+
return roundedTime.format("YYYY-MM-DD HH:mm");
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
/**
|
|
168
|
+
* 校正资源时间段数据
|
|
169
|
+
*
|
|
170
|
+
* 如果时间段的 start_at 早于资源的 start_time,将其同步为 start_time 的下一个10分钟整数
|
|
171
|
+
* 如果修正后 end_at 也早于修正后的 start_time,则删除该时间段
|
|
172
|
+
*
|
|
173
|
+
* @param resourcesData 资源数据数组
|
|
174
|
+
* @returns 校正后的资源数据数组
|
|
175
|
+
*/
|
|
176
|
+
correctResourceTimeSlots(resourcesData) {
|
|
177
|
+
return resourcesData.map((resource) => {
|
|
178
|
+
if (!resource.times || !Array.isArray(resource.times) || !resource.start_time) {
|
|
179
|
+
return resource;
|
|
180
|
+
}
|
|
181
|
+
const resourceStartTime = (0, import_dayjs.default)(resource.start_time);
|
|
182
|
+
const correctedTimes = resource.times.map((timeSlot) => {
|
|
183
|
+
if (!timeSlot.start_at || !timeSlot.end_at) {
|
|
184
|
+
return timeSlot;
|
|
185
|
+
}
|
|
186
|
+
const startAt = (0, import_dayjs.default)(timeSlot.start_at);
|
|
187
|
+
const endAt = (0, import_dayjs.default)(timeSlot.end_at);
|
|
188
|
+
if (startAt.isBefore(resourceStartTime)) {
|
|
189
|
+
const roundedStartTime = this.roundUpToNext10Minutes(resourceStartTime);
|
|
190
|
+
const roundedStartTimeDayjs = (0, import_dayjs.default)(roundedStartTime);
|
|
191
|
+
console.log(`[DateModule] 修正时间段开始时间: ${timeSlot.start_at} -> ${roundedStartTime} (资源ID: ${resource.id}, 原始start_time: ${resource.start_time})`);
|
|
192
|
+
const correctedTimeSlot = {
|
|
193
|
+
...timeSlot,
|
|
194
|
+
start_at: roundedStartTime
|
|
195
|
+
};
|
|
196
|
+
if (endAt.isBefore(roundedStartTimeDayjs)) {
|
|
197
|
+
console.log(`[DateModule] 时间段无效,将被删除: ${timeSlot.start_at} - ${timeSlot.end_at} (资源ID: ${resource.id}, 修正后start_time: ${roundedStartTime})`);
|
|
198
|
+
return null;
|
|
199
|
+
}
|
|
200
|
+
return correctedTimeSlot;
|
|
201
|
+
}
|
|
202
|
+
return timeSlot;
|
|
203
|
+
}).filter((timeSlot) => timeSlot !== null);
|
|
204
|
+
return {
|
|
205
|
+
...resource,
|
|
206
|
+
times: correctedTimes
|
|
207
|
+
};
|
|
208
|
+
});
|
|
209
|
+
}
|
|
146
210
|
async getResourceAvailableTimeList(params) {
|
|
147
211
|
var _a;
|
|
148
212
|
const { query, rules, type } = params;
|
|
@@ -79,78 +79,132 @@ function findFastestAvailableResource({
|
|
|
79
79
|
currentCapacity = 1,
|
|
80
80
|
countMap = {}
|
|
81
81
|
}) {
|
|
82
|
-
var _a, _b;
|
|
82
|
+
var _a, _b, _c;
|
|
83
83
|
const currentTime = (0, import_dayjs.default)();
|
|
84
84
|
let fastestTime = null;
|
|
85
85
|
let fastestResources = [];
|
|
86
|
+
console.log("[TimeslotUtils] 查找最快可用资源:", {
|
|
87
|
+
currentTime: currentTime.format("YYYY-MM-DD HH:mm:ss"),
|
|
88
|
+
resourceCount: resources.length,
|
|
89
|
+
currentCapacity,
|
|
90
|
+
countMap
|
|
91
|
+
});
|
|
86
92
|
for (const resource of resources) {
|
|
87
93
|
const todayTimes = resource.times.filter((time) => {
|
|
88
|
-
|
|
94
|
+
const isToday = (0, import_dayjs.default)(time.start_at).isSame(currentTime, "day");
|
|
95
|
+
const isStillWorking = (0, import_dayjs.default)(time.end_at).isAfter(currentTime);
|
|
96
|
+
return isToday && isStillWorking;
|
|
89
97
|
});
|
|
90
|
-
if (todayTimes.length === 0)
|
|
98
|
+
if (todayTimes.length === 0) {
|
|
99
|
+
console.log(`[TimeslotUtils] 资源 ${resource.id}(${resource.main_field}) 今日无可用时间段`);
|
|
91
100
|
continue;
|
|
92
|
-
|
|
93
|
-
return (0, import_dayjs.default)(a.start_at).diff((0, import_dayjs.default)(b.start_at));
|
|
94
|
-
});
|
|
101
|
+
}
|
|
95
102
|
for (const time of todayTimes) {
|
|
96
|
-
const
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
103
|
+
const workStartTime = (0, import_dayjs.default)(time.start_at);
|
|
104
|
+
const workEndTime = (0, import_dayjs.default)(time.end_at);
|
|
105
|
+
let nextAvailableTime = currentTime.isBefore(workStartTime) ? workStartTime : currentTime;
|
|
106
|
+
console.log(`[TimeslotUtils] 检查资源 ${resource.id}(${resource.main_field}):`, {
|
|
107
|
+
workTime: `${workStartTime.format("HH:mm")}-${workEndTime.format("HH:mm")}`,
|
|
108
|
+
checkStartTime: nextAvailableTime.format("HH:mm:ss"),
|
|
109
|
+
eventCount: ((_a = time.event_list) == null ? void 0 : _a.length) || 0
|
|
110
|
+
});
|
|
111
|
+
const relevantEvents = ((_b = time.event_list) == null ? void 0 : _b.filter((event) => {
|
|
112
|
+
const eventEnd = (0, import_dayjs.default)(event.end_at);
|
|
113
|
+
return eventEnd.isAfter(nextAvailableTime);
|
|
114
|
+
})) || [];
|
|
115
|
+
relevantEvents.sort(
|
|
116
|
+
(a, b) => (0, import_dayjs.default)(a.start_at).diff((0, import_dayjs.default)(b.start_at))
|
|
117
|
+
);
|
|
118
|
+
let finalAvailableTime = nextAvailableTime;
|
|
119
|
+
for (const event of relevantEvents) {
|
|
120
|
+
const eventStart = (0, import_dayjs.default)(event.start_at);
|
|
121
|
+
const eventEnd = (0, import_dayjs.default)(event.end_at);
|
|
122
|
+
console.log(`[TimeslotUtils] 检查事件冲突:`, {
|
|
123
|
+
resourceId: resource.id,
|
|
124
|
+
eventTime: `${eventStart.format("HH:mm")}-${eventEnd.format("HH:mm")}`,
|
|
125
|
+
checkTime: finalAvailableTime.format("HH:mm:ss"),
|
|
126
|
+
pax: event.pax || 1
|
|
102
127
|
});
|
|
103
|
-
if (
|
|
104
|
-
if (
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
} else if (startTime.isBefore(fastestTime)) {
|
|
108
|
-
fastestTime = startTime;
|
|
109
|
-
fastestResources = [resource];
|
|
128
|
+
if (resource.resourceType === "single" || (resource.capacity || 1) === 1) {
|
|
129
|
+
if (finalAvailableTime.isBefore(eventEnd) && eventStart.isBefore(finalAvailableTime.add(30, "minute"))) {
|
|
130
|
+
finalAvailableTime = eventEnd;
|
|
131
|
+
console.log(`[TimeslotUtils] 发现时间冲突,调整到: ${finalAvailableTime.format("HH:mm:ss")}`);
|
|
110
132
|
}
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
fastestTime = startTime;
|
|
122
|
-
fastestResources.push(resource);
|
|
123
|
-
} else if (startTime.isBefore(fastestTime)) {
|
|
124
|
-
fastestTime = startTime;
|
|
125
|
-
fastestResources = [resource];
|
|
133
|
+
} else {
|
|
134
|
+
const totalCapacity = resource.capacity || 0;
|
|
135
|
+
const currentUsedCapacity = countMap[resource.id] || 0;
|
|
136
|
+
const eventUsedCapacity = event.pax || 1;
|
|
137
|
+
if (finalAvailableTime.isBefore(eventEnd) && eventStart.isBefore(finalAvailableTime.add(30, "minute"))) {
|
|
138
|
+
const totalRequiredCapacity = currentUsedCapacity + currentCapacity + eventUsedCapacity;
|
|
139
|
+
if (totalRequiredCapacity > totalCapacity) {
|
|
140
|
+
finalAvailableTime = eventEnd;
|
|
141
|
+
console.log(`[TimeslotUtils] 容量不足,调整到: ${finalAvailableTime.format("HH:mm:ss")}`);
|
|
142
|
+
}
|
|
126
143
|
}
|
|
127
|
-
break;
|
|
128
144
|
}
|
|
129
145
|
}
|
|
146
|
+
if (finalAvailableTime.isAfter(workEndTime)) {
|
|
147
|
+
console.log(`[TimeslotUtils] 资源 ${resource.id} 可用时间超出工作时间,跳过`);
|
|
148
|
+
continue;
|
|
149
|
+
}
|
|
150
|
+
console.log(`[TimeslotUtils] 资源 ${resource.id}(${resource.main_field}) 最快可用时间: ${finalAvailableTime.format("HH:mm:ss")}`);
|
|
151
|
+
if (!fastestTime || finalAvailableTime.isBefore(fastestTime)) {
|
|
152
|
+
fastestTime = finalAvailableTime;
|
|
153
|
+
fastestResources = [resource];
|
|
154
|
+
console.log(`[TimeslotUtils] 更新最快时间: ${fastestTime.format("HH:mm:ss")}, 资源: ${resource.main_field}`);
|
|
155
|
+
} else if (finalAvailableTime.isSame(fastestTime)) {
|
|
156
|
+
fastestResources.push(resource);
|
|
157
|
+
console.log(`[TimeslotUtils] 添加相同时间资源: ${resource.main_field}`);
|
|
158
|
+
}
|
|
159
|
+
break;
|
|
130
160
|
}
|
|
131
161
|
}
|
|
132
|
-
if (!fastestTime || fastestResources.length === 0)
|
|
162
|
+
if (!fastestTime || fastestResources.length === 0) {
|
|
163
|
+
console.log("[TimeslotUtils] 未找到可用资源");
|
|
133
164
|
return null;
|
|
134
|
-
|
|
165
|
+
}
|
|
166
|
+
console.log(`[TimeslotUtils] 找到 ${fastestResources.length} 个最快可用资源,时间: ${fastestTime.format("HH:mm:ss")}`);
|
|
167
|
+
if (fastestResources.length === 1) {
|
|
168
|
+
console.log(`[TimeslotUtils] 返回唯一最快资源: ${fastestResources[0].main_field}`);
|
|
135
169
|
return fastestResources[0];
|
|
170
|
+
}
|
|
171
|
+
let selectedResource = fastestResources[0];
|
|
136
172
|
let maxIdleTime = 0;
|
|
137
|
-
|
|
173
|
+
console.log(`[TimeslotUtils] 比较 ${fastestResources.length} 个资源的空闲时间:`);
|
|
138
174
|
for (const resource of fastestResources) {
|
|
139
|
-
const
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
175
|
+
const workingTime = resource.times.find((time) => {
|
|
176
|
+
const isToday = (0, import_dayjs.default)(time.start_at).isSame(fastestTime, "day");
|
|
177
|
+
const isStillWorking = (0, import_dayjs.default)(time.end_at).isAfter(fastestTime);
|
|
178
|
+
return isToday && isStillWorking;
|
|
179
|
+
});
|
|
180
|
+
if (!workingTime)
|
|
181
|
+
continue;
|
|
182
|
+
const workEndTime = (0, import_dayjs.default)(workingTime.end_at);
|
|
183
|
+
let totalIdleTime = workEndTime.diff(fastestTime, "minute");
|
|
184
|
+
const relevantEvents = ((_c = workingTime.event_list) == null ? void 0 : _c.filter((event) => {
|
|
185
|
+
const eventStart = (0, import_dayjs.default)(event.start_at);
|
|
186
|
+
const eventEnd = (0, import_dayjs.default)(event.end_at);
|
|
187
|
+
return eventEnd.isAfter(fastestTime) && eventStart.isAfter(fastestTime);
|
|
188
|
+
})) || [];
|
|
189
|
+
for (const event of relevantEvents) {
|
|
190
|
+
const eventStart = (0, import_dayjs.default)(event.start_at);
|
|
191
|
+
const eventEnd = (0, import_dayjs.default)(event.end_at);
|
|
192
|
+
const eventDuration = eventEnd.diff(eventStart, "minute");
|
|
193
|
+
totalIdleTime -= eventDuration;
|
|
194
|
+
}
|
|
195
|
+
console.log(`[TimeslotUtils] 资源 ${resource.id}(${resource.main_field}):`, {
|
|
196
|
+
工作结束时间: workEndTime.format("HH:mm"),
|
|
197
|
+
总工作时长: workEndTime.diff(fastestTime, "minute") + "分钟",
|
|
198
|
+
预约占用时长: workEndTime.diff(fastestTime, "minute") - totalIdleTime + "分钟",
|
|
199
|
+
实际空闲时长: totalIdleTime + "分钟"
|
|
148
200
|
});
|
|
149
|
-
if (
|
|
150
|
-
maxIdleTime =
|
|
201
|
+
if (totalIdleTime > maxIdleTime) {
|
|
202
|
+
maxIdleTime = totalIdleTime;
|
|
151
203
|
selectedResource = resource;
|
|
204
|
+
console.log(`[TimeslotUtils] 更新最佳选择: ${resource.main_field} (空闲${totalIdleTime}分钟)`);
|
|
152
205
|
}
|
|
153
206
|
}
|
|
207
|
+
console.log(`[TimeslotUtils] 最终选择资源: ${selectedResource.main_field} (最长空闲${maxIdleTime}分钟)`);
|
|
154
208
|
return selectedResource;
|
|
155
209
|
}
|
|
156
210
|
function filterConditionTimeSlots(times, startTime, endTime) {
|