@ray-js/lamp-schedule-core 1.0.4-beta-6 → 1.0.4-beta-8

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.
@@ -82,9 +82,11 @@ function doRangesOverlap(aStart, aEnd, bStart, bEnd) {
82
82
  }
83
83
 
84
84
  // 情况4: 标准区间重叠判断
85
- // 使用标准区间重叠判断: [a1,a2] 和 [b1,b2] 重叠当且仅当 a2 > b1 && b2 > a1
85
+ // 使用标准区间重叠判断: [a1,a2] 和 [b1,b2] 重叠当且仅当 a2 >= b1 && b2 >= a1
86
86
  // 注意:这里已经处理了跨天情况(通过标准化为连续分钟数)
87
- return aEndMin > bStartMin && bEndMin > aStartMin;
87
+ // 使用 >= 而不是 >,因为时间段是闭区间,边界值相等时也应该认为有重叠
88
+ // 例如:[10:00, 23:00] 和 [23:00, 23:05] 在 23:00 这个时间点重叠
89
+ return aEndMin >= bStartMin && bEndMin >= aStartMin;
88
90
  }
89
91
 
90
92
  // 辅助函数
@@ -228,5 +228,47 @@ describe('ConflictResolver', () => {
228
228
  const conflicts = checkConflicts([rhythmSchedule], timerSchedule);
229
229
  expect(conflicts.length).toBe(1);
230
230
  });
231
+
232
+ // 测试边界值重叠问题:灯光助眠 23:00-23:05 与 灯光看家 10:00-23:00
233
+ // 这两个时间段在 23:00 这个时间点应该被认为有重叠
234
+ it('should detect conflicts when one schedule ends exactly when another starts (boundary overlap)', () => {
235
+ const homeSchedule = {
236
+ id: 'random_1',
237
+ type: EScheduleFunctionType.RANDOM,
238
+ // 灯光看家
239
+ data: {
240
+ status: true,
241
+ weeks: [1, 0, 0, 0, 0, 0, 0],
242
+ // 周日
243
+ startTime: '10:00',
244
+ endTime: '23:00' // 结束于 23:00
245
+ }
246
+ };
247
+ const sleepSchedule = {
248
+ id: 'sleep_1',
249
+ type: EScheduleFunctionType.SLEEP,
250
+ // 灯光助眠
251
+ data: {
252
+ status: true,
253
+ weeks: [1, 0, 0, 0, 0, 0, 0],
254
+ // 周日
255
+ startTime: '23:00',
256
+ // 开始于 23:00
257
+ endTime: '23:05'
258
+ }
259
+ };
260
+
261
+ // 灯光看家 vs 灯光助眠
262
+ const conflicts1 = checkConflicts([homeSchedule], sleepSchedule);
263
+ expect(conflicts1.length).toBe(1);
264
+ expect(conflicts1[0].current).toBe(sleepSchedule);
265
+ expect(conflicts1[0].other).toBe(homeSchedule);
266
+
267
+ // 灯光助眠 vs 灯光看家(反向测试)
268
+ const conflicts2 = checkConflicts([sleepSchedule], homeSchedule);
269
+ expect(conflicts2.length).toBe(1);
270
+ expect(conflicts2[0].current).toBe(homeSchedule);
271
+ expect(conflicts2[0].other).toBe(sleepSchedule);
272
+ });
231
273
  });
232
274
  });
@@ -22,9 +22,15 @@ export declare const useFetchDpDataByMesh: (dpCodes: string[]) => {
22
22
  /**
23
23
  * 主动拉取 dp 数据, 不限制协议
24
24
  * @param dpCodes dpCode 数组
25
+ * @param options 拉取配置
26
+ * @param options.initFetch 是否初始化拉取
27
+ * @param options.offsetTime 延迟时间 ms
25
28
  * @returns refresh 刷新 dp 数据
26
29
  */
27
- export declare const useFetchDpData: (dpCodes: string[]) => {
30
+ export declare const useFetchDpData: (dpCodes: string[], options?: {
31
+ initFetch: boolean;
32
+ offsetTime: number;
33
+ }) => {
28
34
  refresh: () => void;
29
35
  };
30
36
  export {};
@@ -2,7 +2,7 @@ import "core-js/modules/esnext.iterator.constructor.js";
2
2
  import "core-js/modules/esnext.iterator.filter.js";
3
3
  import "core-js/modules/esnext.iterator.map.js";
4
4
  /* eslint-disable no-console */
5
- import { useState, useEffect, useCallback, useRef } from 'react';
5
+ import { useState, useEffect, useCallback, useRef, useMemo } from 'react';
6
6
  import { isEqual } from 'lodash-es';
7
7
  import { queryDps } from '@ray-js/ray';
8
8
  import { scheduleLogger } from '../utils/ScheduleLogger';
@@ -212,17 +212,44 @@ export const useFetchDpDataByMesh = dpCodes => {
212
212
  /**
213
213
  * 主动拉取 dp 数据, 不限制协议
214
214
  * @param dpCodes dpCode 数组
215
+ * @param options 拉取配置
216
+ * @param options.initFetch 是否初始化拉取
217
+ * @param options.offsetTime 延迟时间 ms
215
218
  * @returns refresh 刷新 dp 数据
216
219
  */
217
- export const useFetchDpData = dpCodes => {
218
- const getDpDataFn = () => {
219
- scheduleLogger.debug('getDpDataFn get:', dpCodes);
220
- fetchDpData(dpCodes);
220
+ export const useFetchDpData = function (dpCodes) {
221
+ let options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {
222
+ initFetch: true,
223
+ offsetTime: 0
221
224
  };
225
+ // 使用 useMemo 稳定 dpCodes 的字符串表示,避免因数组引用变化导致重复执行
226
+ // 注意:这里依赖 dpCodes 数组本身,但由于调用方已使用 useMemo 稳定引用,所以是安全的
227
+ const dpCodesKey = useMemo(() => dpCodes.join(','), [dpCodes]);
228
+ const getDpDataFn = useCallback(() => {
229
+ scheduleLogger.debug('useFetchDpData getDpDataFn get1:', dpCodes);
230
+ fetchDpData(dpCodes);
231
+ }, [dpCodes]);
232
+
233
+ // 使用 useRef 跟踪上一次的 dpCodesKey,避免重复执行
234
+ const prevDpCodesKeyRef = useRef(null);
222
235
  useEffect(() => {
223
- scheduleLogger.debug('useFetchDpData get:', dpCodes);
224
- getDpDataFn();
225
- }, [dpCodes.join(',')]);
236
+ if (!options.initFetch) {
237
+ return () => null;
238
+ }
239
+ // 只有当 dpCodesKey 真正变化时才执行
240
+ if (prevDpCodesKeyRef.current === dpCodesKey) {
241
+ scheduleLogger.debug('useFetchDpData useEffect: dpCodesKey unchanged', dpCodesKey);
242
+ return () => null;
243
+ }
244
+ prevDpCodesKeyRef.current = dpCodesKey;
245
+ const timer = setTimeout(() => {
246
+ scheduleLogger.debug('useFetchDpData useEffect get:', dpCodes);
247
+ getDpDataFn();
248
+ }, options.offsetTime || 0);
249
+ return () => {
250
+ clearTimeout(timer);
251
+ };
252
+ }, [dpCodesKey, getDpDataFn, dpCodes, options.initFetch, options.offsetTime]);
226
253
  return {
227
254
  refresh: getDpDataFn
228
255
  };
@@ -6,8 +6,14 @@ export declare function useCountdownDp(dpCode?: "countdown"): {
6
6
  /**
7
7
  * 主动拉取倒计时dp数据
8
8
  * @param countdownCode 倒计时dpCode
9
+ * @param options 拉取配置
10
+ * @param options.initFetch 是否初始化拉取
11
+ * @param options.offsetTime 延迟时间 ms
9
12
  * @returns refresh 函数 刷新dp数据
10
13
  */
11
- export declare const useCountdownDpPull: (countdownCode?: "countdown") => {
14
+ export declare const useCountdownDpPull: (countdownCode?: "countdown", options?: {
15
+ initFetch: boolean;
16
+ offsetTime: number;
17
+ }) => {
12
18
  refresh: () => void;
13
19
  };
@@ -1,3 +1,4 @@
1
+ import { useMemo } from 'react';
1
2
  import { useBaseLightDp, useFetchDpData } from './useBaseLightDp';
2
3
  import { scheduleDpCodes } from '../config/dpCodes';
3
4
  export function useCountdownDp() {
@@ -15,13 +16,19 @@ export function useCountdownDp() {
15
16
  /**
16
17
  * 主动拉取倒计时dp数据
17
18
  * @param countdownCode 倒计时dpCode
19
+ * @param options 拉取配置
20
+ * @param options.initFetch 是否初始化拉取
21
+ * @param options.offsetTime 延迟时间 ms
18
22
  * @returns refresh 函数 刷新dp数据
19
23
  */
20
24
  export const useCountdownDpPull = function () {
21
25
  let countdownCode = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : scheduleDpCodes.COUNTDOWN;
26
+ let options = arguments.length > 1 ? arguments[1] : undefined;
27
+ // 使用 useMemo 稳定数组引用,避免每次渲染都创建新数组导致重复执行
28
+ const dpCodes = useMemo(() => [countdownCode], [countdownCode]);
22
29
  const {
23
30
  refresh
24
- } = useFetchDpData([countdownCode]);
31
+ } = useFetchDpData(dpCodes, options);
25
32
  return {
26
33
  refresh
27
34
  };
@@ -8,8 +8,14 @@ export declare function useTimerReportDp(dpCode?: "timer_report"): {
8
8
  * 主动拉取定时 timer_report dp 数据
9
9
  * 由于本地定时数据不会主动上报,所以需要主动拉取用来同步定时数据
10
10
  * @param timerReport 定时上报 dpCode
11
+ * @param options 拉取配置
12
+ * @param options.initFetch 是否初始化拉取
13
+ * @param options.offsetTime 延迟时间 ms
11
14
  * @returns refresh 刷新 dp 数据
12
15
  */
13
- export declare const useTimerReportDpPull: (timerReport?: "timer_report") => {
16
+ export declare const useTimerReportDpPull: (timerReport?: "timer_report", options?: {
17
+ initFetch: boolean;
18
+ offsetTime: number;
19
+ }) => {
14
20
  refresh: () => void;
15
21
  };
@@ -34,14 +34,20 @@ export function useTimerReportDp() {
34
34
  * 主动拉取定时 timer_report dp 数据
35
35
  * 由于本地定时数据不会主动上报,所以需要主动拉取用来同步定时数据
36
36
  * @param timerReport 定时上报 dpCode
37
+ * @param options 拉取配置
38
+ * @param options.initFetch 是否初始化拉取
39
+ * @param options.offsetTime 延迟时间 ms
37
40
  * @returns refresh 刷新 dp 数据
38
41
  */
39
42
  export const useTimerReportDpPull = function () {
40
43
  let timerReport = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : scheduleDpCodes.TIMER_REPORT;
44
+ let options = arguments.length > 1 ? arguments[1] : undefined;
41
45
  ScheduleLogger.debug('useTimerReportDpPull 执行');
46
+ // 使用 useMemo 稳定数组引用,避免每次渲染都创建新数组导致重复执行
47
+ const dpCodes = useMemo(() => [timerReport], [timerReport]);
42
48
  const {
43
49
  refresh
44
- } = useFetchDpData([timerReport]);
50
+ } = useFetchDpData(dpCodes, options);
45
51
  return {
46
52
  refresh
47
53
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ray-js/lamp-schedule-core",
3
- "version": "1.0.4-beta-6",
3
+ "version": "1.0.4-beta-8",
4
4
  "description": "照明计划模块核心能力",
5
5
  "main": "./lib/index.js",
6
6
  "files": [