@ray-js/lamp-schedule-core 1.0.5-beta.8 → 1.0.5-beta.9

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.
@@ -1,10 +1,15 @@
1
- import { TConflictRes, ConflictRule, TFoldScheduleList, TSchedule } from './type';
1
+ import { TConflictRes, ConflictRule, TFoldScheduleList, TSchedule, PartialSchedule } from './type';
2
+ import { EScheduleFunctionType } from '../types';
2
3
  /**
3
4
  * 冲突检测
4
5
  */
5
6
  export declare class Conflict {
6
7
  static rule: ConflictRule;
7
8
  static isInit: boolean;
9
+ /**
10
+ * 获取冲突检测数据
11
+ */
12
+ static getData(): Record<string, any>[];
8
13
  /**
9
14
  * 添加冲突规则
10
15
  */
@@ -18,13 +23,18 @@ export declare class Conflict {
18
23
  * conflictList - 具体的冲突检测结果数组
19
24
  */
20
25
  static add(current: TSchedule): TConflictRes;
26
+ /**
27
+ * 删除指定类型的日程
28
+ * @param type - 日程类型
29
+ */
30
+ static removeByType(type: EScheduleFunctionType): void;
21
31
  /**
22
32
  * 删除日程
23
33
  *
24
- * @param current - 待添加的日程对象,类型为 Schedule
34
+ * @param current - 待添加的日程对象,类型为 Schedule, 如果没有 detail 则认为是删除指定类型的日程
25
35
  * @returns void
26
36
  */
27
- static remove(current: TSchedule): void;
37
+ static remove(current: TSchedule | PartialSchedule): void;
28
38
  /**
29
39
  * 更新新日程时检测冲突
30
40
  *
@@ -1,6 +1,7 @@
1
1
  import _objectSpread from "@babel/runtime/helpers/esm/objectSpread2";
2
2
  import "core-js/modules/es.json.stringify.js";
3
3
  import "core-js/modules/esnext.iterator.constructor.js";
4
+ import "core-js/modules/esnext.iterator.filter.js";
4
5
  import "core-js/modules/esnext.iterator.find.js";
5
6
  import { scheduleLogger } from '../utils/ScheduleLogger';
6
7
  import { checkConflicts } from './ConflictResolver';
@@ -12,6 +13,13 @@ import { EScheduleFunctionType } from '../types';
12
13
  * 冲突检测
13
14
  */
14
15
  export class Conflict {
16
+ /**
17
+ * 获取冲突检测数据
18
+ */
19
+ static getData() {
20
+ return ScheduleDataManager.getInstance().getData();
21
+ }
22
+
15
23
  /**
16
24
  * 添加冲突规则
17
25
  */
@@ -50,13 +58,31 @@ export class Conflict {
50
58
  };
51
59
  }
52
60
 
61
+ /**
62
+ * 删除指定类型的日程
63
+ * @param type - 日程类型
64
+ */
65
+ static removeByType(type) {
66
+ const instance = ScheduleDataManager.getInstance();
67
+ const list = instance.getData() || [];
68
+ const filteredList = list.filter(item => item.type !== type);
69
+ ScheduleDataManager.list = filteredList;
70
+ }
71
+
53
72
  /**
54
73
  * 删除日程
55
74
  *
56
- * @param current - 待添加的日程对象,类型为 Schedule
75
+ * @param current - 待添加的日程对象,类型为 Schedule, 如果没有 detail 则认为是删除指定类型的日程
57
76
  * @returns void
58
77
  */
59
78
  static remove(current) {
79
+ if (!current) {
80
+ return;
81
+ }
82
+ if (current.type && !current.detail) {
83
+ this.removeByType(current.type);
84
+ return;
85
+ }
60
86
  const [cur] = transScheduleListToConflictList([current]);
61
87
  scheduleLogger.debug('Conflict.remove cur, current:', cur, current);
62
88
 
@@ -1,3 +1,4 @@
1
+ import "core-js/modules/es.json.stringify.js";
1
2
  import "core-js/modules/esnext.iterator.constructor.js";
2
3
  import "core-js/modules/esnext.iterator.filter.js";
3
4
  import "core-js/modules/esnext.iterator.for-each.js";
@@ -63,10 +64,11 @@ export class ScheduleDataManager {
63
64
  }
64
65
  const idsToAdd = new Set(itemsMap.keys());
65
66
  const filteredList = ScheduleDataManager.list.filter(item => !idsToAdd.has(item.id));
66
- ScheduleDataManager.list = [...filteredList, ...itemsMap.values()].filter(item => {
67
+ const newList = [...filteredList, ...itemsMap.values()].filter(item => {
67
68
  var _item$data;
68
69
  return (item === null || item === void 0 || (_item$data = item.data) === null || _item$data === void 0 ? void 0 : _item$data.status) === true;
69
70
  });
71
+ ScheduleDataManager.list = newList;
70
72
  }
71
73
  clearData() {
72
74
  scheduleLogger.debug('ScheduleDataManager.clearData pre', ScheduleDataManager.list);
@@ -76,12 +78,14 @@ export class ScheduleDataManager {
76
78
  if (!current) {
77
79
  return;
78
80
  }
79
- scheduleLogger.debug('ScheduleDataManager.deleteData pre', current);
81
+ scheduleLogger.debug('ScheduleDataManager.deleteData pre', current, JSON.stringify(ScheduleDataManager.list));
80
82
  const index = ScheduleDataManager.list.findIndex(item => item.id === current.id);
81
83
  if (index !== -1) {
82
84
  ScheduleDataManager.list.splice(index, 1);
85
+ scheduleLogger.debug('ScheduleDataManager.deleteData post', JSON.stringify(ScheduleDataManager.list));
86
+ } else {
87
+ scheduleLogger.debug('ScheduleDataManager.deleteData post not found', JSON.stringify(ScheduleDataManager.list));
83
88
  }
84
- scheduleLogger.debug('ScheduleDataManager.deleteData post', ScheduleDataManager.list);
85
89
  }
86
90
 
87
91
  // 添加remove方法作为deleteData的别名,用于测试
@@ -52,5 +52,9 @@ export type TSchedule = {
52
52
  type: EScheduleFunctionType;
53
53
  detail: ScheduleNodeType[];
54
54
  };
55
+ export type PartialSchedule = {
56
+ type: EScheduleFunctionType;
57
+ detail?: ScheduleNodeType[];
58
+ };
55
59
  export type TFoldScheduleList = TSchedule[];
56
60
  export {};
@@ -121,6 +121,7 @@ describe('SleepSigmesh parser & formatter', () => {
121
121
  index: 0
122
122
  }]
123
123
  });
124
+ // formatter 固定输出 version=1, dataMode=1
124
125
  expect(result).toBe(buildDpStr([{
125
126
  onOff: 1,
126
127
  loops: loopsToHex('1000000'),
@@ -129,7 +130,7 @@ describe('SleepSigmesh parser & formatter', () => {
129
130
  minute: 15,
130
131
  brightness: 90,
131
132
  temperature: 50
132
- }], 1, 3));
133
+ }], 1, 1));
133
134
  });
134
135
  it('round-trips dp string through parser and formatter', () => {
135
136
  const nodes = [{
@@ -144,7 +145,8 @@ describe('SleepSigmesh parser & formatter', () => {
144
145
  const dpStr = buildDpStr(nodes, 2, 3);
145
146
  const parsed = sleepParserSigmesh.parser(dpStr);
146
147
  const formatted = sleepParserSigmesh.formatter(parsed);
147
- expect(formatted).toBe(dpStr);
148
+ // formatter 固定输出 version=1, dataMode=1,故 round-trip 后与原始 dpStr 不同
149
+ expect(formatted).toBe(buildDpStr(nodes, 1, 1));
148
150
  });
149
151
  it('handles multiple nodes correctly', () => {
150
152
  const nodes = [{
@@ -97,6 +97,7 @@ describe('WakeUpSigmesh parser & formatter', () => {
97
97
  index: 0
98
98
  }]
99
99
  });
100
+ // formatter 固定输出 version=1, dataMode=1(与 parser 可解析的格式一致)
100
101
  expect(result).toBe(buildDpStr([{
101
102
  onOff: 1,
102
103
  loops: loopsToHex('1000000'),
@@ -106,7 +107,7 @@ describe('WakeUpSigmesh parser & formatter', () => {
106
107
  brightness: 90,
107
108
  temperature: 50,
108
109
  duration: 20
109
- }], 1, 3));
110
+ }], 1, 1));
110
111
  });
111
112
  it('round-trips dp string through parser and formatter', () => {
112
113
  const nodes = [{
@@ -122,6 +123,7 @@ describe('WakeUpSigmesh parser & formatter', () => {
122
123
  const dpStr = buildDpStr(nodes, 2, 3);
123
124
  const parsed = wakeupParserSigmesh.parser(dpStr);
124
125
  const formatted = wakeupParserSigmesh.formatter(parsed);
125
- expect(formatted).toBe(dpStr);
126
+ // formatter 固定输出 version=1, dataMode=1,故 round-trip 后与原始 dpStr 不同
127
+ expect(formatted).toBe(buildDpStr(nodes, 1, 1));
126
128
  });
127
129
  });
@@ -2,10 +2,12 @@ import "core-js/modules/es.json.stringify.js";
2
2
  import { renderHook, act } from '@testing-library/react-hooks';
3
3
  import { useTimerDp } from '../useTimerDp';
4
4
  import { useBaseLightDp } from '../useBaseLightDp';
5
+ import { useSupport } from '../useCommonSupport';
5
6
  import { scheduleDpCodes } from '../../config/dpCodes';
6
7
  import { rtcTimerParser } from '../../dpParser/rtcTimer';
7
8
  // Mock dependencies
8
9
  jest.mock('../useBaseLightDp');
10
+ jest.mock('../useCommonSupport');
9
11
  jest.mock('../../dpParser/rtcTimer');
10
12
  describe('useTimerDp', () => {
11
13
  const mockTimerData = {
@@ -27,6 +29,9 @@ describe('useTimerDp', () => {
27
29
  };
28
30
  beforeEach(() => {
29
31
  jest.clearAllMocks();
32
+ useSupport.mockReturnValue({
33
+ isSupportDp: jest.fn(code => code === scheduleDpCodes.RTC_TIMER)
34
+ });
30
35
  useBaseLightDp.mockReturnValue({
31
36
  dpValue: mockTimerData,
32
37
  updateDp: jest.fn()
@@ -72,9 +77,4 @@ describe('useTimerDp', () => {
72
77
  renderHook(() => useTimerDp());
73
78
  expect(useBaseLightDp).toHaveBeenCalledWith(scheduleDpCodes.RTC_TIMER);
74
79
  });
75
- it('should handle custom dpCode', () => {
76
- const customDpCode = scheduleDpCodes.RTC_TIMER;
77
- renderHook(() => useTimerDp(customDpCode));
78
- expect(useBaseLightDp).toHaveBeenCalledWith(customDpCode);
79
- });
80
80
  });
@@ -129,11 +129,11 @@ export class Support {
129
129
  // 群组中为空
130
130
  groupId && (devIdOrGroupIdCache === null || devIdOrGroupIdCache === void 0 ? void 0 : devIdOrGroupIdCache.set('', groupId));
131
131
  const dpMap = {};
132
- scheduleLogger.debug(firstDevDps, hasDps, 'group and devInfo merge: firstDevDps ==== ');
133
- scheduleLogger.debug(res, 'group and devInfo merge: resresres ==== ');
134
- scheduleLogger.debug(groupRes, 'resresres groupRes ==== ');
135
- scheduleLogger.debug(deviceInfoRes, 'resresres deviceInfoRes ==== ');
136
- scheduleLogger.debug(filteredDeviceInfo, 'resresres filteredDeviceInfo ==== ');
132
+ scheduleLogger.debug('group and devInfo merge: firstDevDps ==== ', firstDevDps, hasDps);
133
+ scheduleLogger.debug('group and devInfo merge: resresres ==== ', res);
134
+ scheduleLogger.debug('resresres groupRes ==== ', groupRes);
135
+ scheduleLogger.debug('resresres deviceInfoRes ==== ', deviceInfoRes);
136
+ scheduleLogger.debug('resresres filteredDeviceInfo ==== ', filteredDeviceInfo);
137
137
  Object.keys(res.dps).forEach(dpId => {
138
138
  const dpCode = getDpCodeByDpId(dpId);
139
139
  dpCode && setDpState(dpCode, res.dps[dpId]);
@@ -3,6 +3,7 @@ declare const mockNavigateTo: jest.Mock<any, any, any>;
3
3
  declare const mockUsePageInstance: jest.Mock<any, any, any>;
4
4
  declare const mockLogger: {
5
5
  debug: jest.Mock<any, any, any>;
6
+ info: jest.Mock<any, any, any>;
6
7
  error: jest.Mock<any, any, any>;
7
8
  };
8
9
  declare let navigateToSchedule: typeof import('../ScheduleDataSync').navigateToSchedule;
@@ -2,6 +2,7 @@ const mockNavigateTo = jest.fn();
2
2
  const mockUsePageInstance = jest.fn();
3
3
  const mockLogger = {
4
4
  debug: jest.fn(),
5
+ info: jest.fn(),
5
6
  error: jest.fn()
6
7
  };
7
8
  let navigateToSchedule;
@@ -21,6 +22,7 @@ describe('ScheduleDataSync utilities', () => {
21
22
  mockNavigateTo.mockReset();
22
23
  mockUsePageInstance.mockReset();
23
24
  mockLogger.debug.mockReset();
25
+ mockLogger.info.mockReset();
24
26
  mockLogger.error.mockReset();
25
27
  global.ty = {
26
28
  presetFunctionalData: jest.fn()
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,98 @@
1
+ import { objectToId } from '../objectToId';
2
+ describe('objectToId', () => {
3
+ it('相同内容、不同 key 顺序的对象应得到相同 id', () => {
4
+ expect(objectToId({
5
+ z: [1, 2, 3],
6
+ a: 2,
7
+ m: 3
8
+ })).toBe(objectToId({
9
+ m: 3,
10
+ z: [1, 2, 3],
11
+ a: 2
12
+ }));
13
+ });
14
+ it('不同内容的对象应得到不同 id', () => {
15
+ expect(objectToId({
16
+ a: 1
17
+ })).not.toBe(objectToId({
18
+ a: 2
19
+ }));
20
+ expect(objectToId({
21
+ a: 1
22
+ })).not.toBe(objectToId({
23
+ b: 1
24
+ }));
25
+ expect(objectToId({})).not.toBe(objectToId({
26
+ a: 1
27
+ }));
28
+ });
29
+ it('空对象应返回稳定的 id', () => {
30
+ const id = objectToId({});
31
+ expect(typeof id).toBe('string');
32
+ expect(id).toMatch(/^[a-z0-9]+$/);
33
+ expect(objectToId({})).toBe(objectToId({}));
34
+ });
35
+ it('带 prefix 时应在 id 前拼接 prefix', () => {
36
+ const id = objectToId({
37
+ x: 1
38
+ });
39
+ const withPrefix = objectToId({
40
+ x: 1
41
+ }, 'prefix_');
42
+ expect(withPrefix).toBe('prefix_' + id);
43
+ expect(objectToId({}, 'id-')).toMatch(/^id-[a-z0-9]+$/);
44
+ });
45
+ it('嵌套对象按 key 排序后一致则 id 相同', () => {
46
+ expect(objectToId({
47
+ outer: {
48
+ z: 1,
49
+ a: 2
50
+ }
51
+ })).toBe(objectToId({
52
+ outer: {
53
+ a: 2,
54
+ z: 1
55
+ }
56
+ }));
57
+ });
58
+ it('数组内容与顺序会影响 id', () => {
59
+ expect(objectToId([1, 2])).not.toBe(objectToId([2, 1]));
60
+ expect(objectToId([1, 2])).toBe(objectToId([1, 2]));
61
+ });
62
+ it('返回值应为非空字符串且为 36 进制形式', () => {
63
+ const id = objectToId({
64
+ foo: 'bar'
65
+ });
66
+ expect(id.length).toBeGreaterThan(0);
67
+ expect(id).toMatch(/^[a-z0-9]+$/);
68
+ });
69
+ it('对 null 输入应返回稳定 id', () => {
70
+ const id = objectToId(null);
71
+ expect(typeof id).toBe('string');
72
+ expect(objectToId(null)).toBe(id);
73
+ });
74
+ it('对原始类型输入应返回稳定 id', () => {
75
+ const numId = objectToId(42);
76
+ const strId = objectToId('hello');
77
+ expect(typeof numId).toBe('string');
78
+ expect(typeof strId).toBe('string');
79
+ expect(numId).not.toBe(strId);
80
+ });
81
+ it('深层嵌套对象 key 顺序不影响 id', () => {
82
+ expect(objectToId({
83
+ a: {
84
+ b: {
85
+ c: 1,
86
+ d: 2
87
+ }
88
+ }
89
+ })).toBe(objectToId({
90
+ a: {
91
+ b: {
92
+ d: 2,
93
+ c: 1
94
+ }
95
+ }
96
+ }));
97
+ });
98
+ });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ray-js/lamp-schedule-core",
3
- "version": "1.0.5-beta.8",
3
+ "version": "1.0.5-beta.9",
4
4
  "description": "照明计划模块核心能力",
5
5
  "main": "./lib/index.js",
6
6
  "files": [