koatty_schedule 3.0.0 → 3.2.0
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/CHANGELOG.md +19 -0
- package/dist/index.d.ts +1 -1
- package/dist/index.js +30 -23
- package/dist/index.mjs +30 -23
- package/dist/package.json +1 -1
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,25 @@
|
|
|
2
2
|
|
|
3
3
|
All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines.
|
|
4
4
|
|
|
5
|
+
## [3.2.0](https://github.com/thinkkoa/koatty_schedule/compare/v3.1.0...v3.2.0) (2025-06-22)
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
### Features
|
|
9
|
+
|
|
10
|
+
* introduce component-specific metadata keys for scheduled and redlock decorators ([803b350](https://github.com/thinkkoa/koatty_schedule/commit/803b3503489c02ab138b3f9f14cb520dd6c7fec4))
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
### Bug Fixes
|
|
14
|
+
|
|
15
|
+
* 修复IOC容器元数据键格式不匹配问题 ([065d456](https://github.com/thinkkoa/koatty_schedule/commit/065d456fc65004e25eb19838da96bf0a52cb2af1))
|
|
16
|
+
|
|
17
|
+
## [3.1.0](https://github.com/thinkkoa/koatty_schedule/compare/v3.0.0...v3.1.0) (2025-06-21)
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
### Features
|
|
21
|
+
|
|
22
|
+
* update decorator types to support symbol property keys and improve IOC container integration ([a60ef3e](https://github.com/thinkkoa/koatty_schedule/commit/a60ef3e361b245f97ba0d6ee51d42efd437a1252))
|
|
23
|
+
|
|
5
24
|
## [3.0.0](https://github.com/thinkkoa/koatty_schedule/compare/v2.1.0...v3.0.0) (2025-06-21)
|
|
6
25
|
|
|
7
26
|
|
package/dist/index.d.ts
CHANGED
package/dist/index.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/*!
|
|
2
2
|
* @Author: richen
|
|
3
|
-
* @Date: 2025-06-
|
|
3
|
+
* @Date: 2025-06-22 20:33:45
|
|
4
4
|
* @License: BSD (3-Clause)
|
|
5
5
|
* @Copyright (c) - <richenlin(at)gmail.com>
|
|
6
6
|
* @HomePage: https://koatty.org/
|
|
@@ -23,6 +23,8 @@ var cron = require('cron');
|
|
|
23
23
|
* @License: BSD (3-Clause)
|
|
24
24
|
* @Copyright (c): <richenlin(at)gmail.com>
|
|
25
25
|
*/
|
|
26
|
+
const COMPONENT_SCHEDULED = 'COMPONENT_SCHEDULED';
|
|
27
|
+
const COMPONENT_REDLOCK = 'COMPONENT_REDLOCK';
|
|
26
28
|
/**
|
|
27
29
|
* Decorator types supported by the system
|
|
28
30
|
*/
|
|
@@ -184,9 +186,9 @@ function getEffectiveRedLockOptions(methodOptions) {
|
|
|
184
186
|
function RedLock(lockName, options) {
|
|
185
187
|
return (target, propertyKey, descriptor) => {
|
|
186
188
|
const methodName = propertyKey.toString();
|
|
187
|
-
//
|
|
188
|
-
const
|
|
189
|
-
const componentType = koatty_container.IOCContainer.getType(
|
|
189
|
+
// 验证装饰器使用的类型(从原型对象获取类构造函数)
|
|
190
|
+
const targetClass = target.constructor;
|
|
191
|
+
const componentType = koatty_container.IOCContainer.getType(targetClass);
|
|
190
192
|
if (componentType !== "SERVICE" && componentType !== "COMPONENT") {
|
|
191
193
|
throw Error("@RedLock decorator can only be used on SERVICE or COMPONENT classes.");
|
|
192
194
|
}
|
|
@@ -208,12 +210,14 @@ function RedLock(lockName, options) {
|
|
|
208
210
|
if (options) {
|
|
209
211
|
validateRedLockMethodOptions(options);
|
|
210
212
|
}
|
|
213
|
+
// 保存类到IOC容器
|
|
214
|
+
koatty_container.IOCContainer.saveClass(COMPONENT_REDLOCK, targetClass, targetClass.name);
|
|
211
215
|
// 保存RedLock元数据到 IOC 容器(lockName已确定)
|
|
212
|
-
koatty_container.IOCContainer.attachClassMetadata(
|
|
216
|
+
koatty_container.IOCContainer.attachClassMetadata(COMPONENT_REDLOCK, DecoratorType.REDLOCK, {
|
|
213
217
|
method: methodName,
|
|
214
218
|
name: lockName, // 确定的锁名称,不会为undefined
|
|
215
219
|
options
|
|
216
|
-
},
|
|
220
|
+
}, target, methodName);
|
|
217
221
|
};
|
|
218
222
|
}
|
|
219
223
|
|
|
@@ -261,26 +265,29 @@ function Scheduled(cron, timezone) {
|
|
|
261
265
|
throw Error("Timezone must be a string");
|
|
262
266
|
}
|
|
263
267
|
return (target, propertyKey, descriptor) => {
|
|
264
|
-
//
|
|
265
|
-
const
|
|
266
|
-
const componentType = koatty_container.IOCContainer.getType(
|
|
268
|
+
// 验证装饰器使用的类型(从原型对象获取类构造函数)
|
|
269
|
+
const targetClass = target.constructor;
|
|
270
|
+
const componentType = koatty_container.IOCContainer.getType(targetClass);
|
|
267
271
|
if (componentType !== "SERVICE" && componentType !== "COMPONENT") {
|
|
268
272
|
throw Error("@Scheduled decorator can only be used on SERVICE or COMPONENT classes.");
|
|
269
273
|
}
|
|
270
274
|
// 验证方法名
|
|
271
|
-
|
|
275
|
+
const methodName = propertyKey.toString();
|
|
276
|
+
if (!methodName || typeof methodName !== 'string') {
|
|
272
277
|
throw Error("Method name is required for @Scheduled decorator");
|
|
273
278
|
}
|
|
274
279
|
// 验证方法描述符
|
|
275
280
|
if (!descriptor || typeof descriptor.value !== 'function') {
|
|
276
281
|
throw Error("@Scheduled decorator can only be applied to methods");
|
|
277
282
|
}
|
|
278
|
-
//
|
|
279
|
-
koatty_container.IOCContainer.
|
|
280
|
-
|
|
283
|
+
// 保存类到IOC容器
|
|
284
|
+
koatty_container.IOCContainer.saveClass(COMPONENT_SCHEDULED, targetClass, targetClass.name);
|
|
285
|
+
// 保存调度元数据到 IOC 容器
|
|
286
|
+
koatty_container.IOCContainer.attachClassMetadata(COMPONENT_SCHEDULED, DecoratorType.SCHEDULED, {
|
|
287
|
+
method: methodName,
|
|
281
288
|
cron,
|
|
282
289
|
timezone // 保存用户指定的值,可能为undefined
|
|
283
|
-
},
|
|
290
|
+
}, target, methodName);
|
|
284
291
|
};
|
|
285
292
|
}
|
|
286
293
|
|
|
@@ -709,12 +716,12 @@ async function initRedLock(options, app) {
|
|
|
709
716
|
* @param {RedLockOptions} options - RedLock 配置选项
|
|
710
717
|
* @param {Koatty} app - Koatty 应用实例
|
|
711
718
|
*/
|
|
712
|
-
async function injectRedLock(
|
|
719
|
+
async function injectRedLock(_options, _app) {
|
|
713
720
|
try {
|
|
714
721
|
koatty_logger.DefaultLogger.Debug('Starting batch RedLock injection...');
|
|
715
|
-
const componentList = koatty_container.IOCContainer.listClass(
|
|
722
|
+
const componentList = koatty_container.IOCContainer.listClass(COMPONENT_REDLOCK);
|
|
716
723
|
for (const component of componentList) {
|
|
717
|
-
const classMetadata = koatty_container.IOCContainer.getClassMetadata(
|
|
724
|
+
const classMetadata = koatty_container.IOCContainer.getClassMetadata(COMPONENT_REDLOCK, DecoratorType.REDLOCK, component.target);
|
|
718
725
|
if (!classMetadata) {
|
|
719
726
|
continue;
|
|
720
727
|
}
|
|
@@ -727,7 +734,7 @@ async function injectRedLock(options, app) {
|
|
|
727
734
|
}
|
|
728
735
|
// 查找所有RedLock方法的元数据
|
|
729
736
|
for (const [key, value] of Object.entries(metadata)) {
|
|
730
|
-
if (key.startsWith('REDLOCK
|
|
737
|
+
if (key.startsWith('REDLOCK')) {
|
|
731
738
|
const redlockData = value;
|
|
732
739
|
const targetMethod = instance[redlockData.method];
|
|
733
740
|
if (!koatty_lib.Helper.isFunction(targetMethod)) {
|
|
@@ -895,12 +902,12 @@ function redLockerDescriptor(descriptor, name, method, methodOptions) {
|
|
|
895
902
|
* @param {RedLockOptions} options - RedLock 配置选项
|
|
896
903
|
* @param {Koatty} app - Koatty 应用实例
|
|
897
904
|
*/
|
|
898
|
-
async function injectSchedule(
|
|
905
|
+
async function injectSchedule(_options, _app) {
|
|
899
906
|
try {
|
|
900
907
|
koatty_logger.DefaultLogger.Debug('Starting batch schedule injection...');
|
|
901
|
-
const componentList = koatty_container.IOCContainer.listClass(
|
|
908
|
+
const componentList = koatty_container.IOCContainer.listClass(COMPONENT_SCHEDULED);
|
|
902
909
|
for (const component of componentList) {
|
|
903
|
-
const classMetadata = koatty_container.IOCContainer.getClassMetadata(
|
|
910
|
+
const classMetadata = koatty_container.IOCContainer.getClassMetadata(COMPONENT_SCHEDULED, DecoratorType.SCHEDULED, component.target);
|
|
904
911
|
if (!classMetadata) {
|
|
905
912
|
continue;
|
|
906
913
|
}
|
|
@@ -913,7 +920,7 @@ async function injectSchedule(options, app) {
|
|
|
913
920
|
}
|
|
914
921
|
// 查找所有调度方法的元数据
|
|
915
922
|
for (const [key, value] of Object.entries(metadata)) {
|
|
916
|
-
if (key.startsWith('SCHEDULED
|
|
923
|
+
if (key.startsWith('SCHEDULED')) {
|
|
917
924
|
const scheduleData = value;
|
|
918
925
|
const targetMethod = instance[scheduleData.method];
|
|
919
926
|
if (!koatty_lib.Helper.isFunction(targetMethod)) {
|
|
@@ -922,7 +929,7 @@ async function injectSchedule(options, app) {
|
|
|
922
929
|
}
|
|
923
930
|
const taskName = `${className}_${scheduleData.method}`;
|
|
924
931
|
const tz = getEffectiveTimezone(scheduleData.timezone);
|
|
925
|
-
|
|
932
|
+
new cron.CronJob(scheduleData.cron, () => {
|
|
926
933
|
koatty_logger.DefaultLogger.Debug(`The schedule job ${taskName} started.`);
|
|
927
934
|
Promise.resolve(targetMethod.call(instance))
|
|
928
935
|
.then(() => {
|
package/dist/index.mjs
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/*!
|
|
2
2
|
* @Author: richen
|
|
3
|
-
* @Date: 2025-06-
|
|
3
|
+
* @Date: 2025-06-22 20:33:45
|
|
4
4
|
* @License: BSD (3-Clause)
|
|
5
5
|
* @Copyright (c) - <richenlin(at)gmail.com>
|
|
6
6
|
* @HomePage: https://koatty.org/
|
|
@@ -21,6 +21,8 @@ import { CronJob } from 'cron';
|
|
|
21
21
|
* @License: BSD (3-Clause)
|
|
22
22
|
* @Copyright (c): <richenlin(at)gmail.com>
|
|
23
23
|
*/
|
|
24
|
+
const COMPONENT_SCHEDULED = 'COMPONENT_SCHEDULED';
|
|
25
|
+
const COMPONENT_REDLOCK = 'COMPONENT_REDLOCK';
|
|
24
26
|
/**
|
|
25
27
|
* Decorator types supported by the system
|
|
26
28
|
*/
|
|
@@ -182,9 +184,9 @@ function getEffectiveRedLockOptions(methodOptions) {
|
|
|
182
184
|
function RedLock(lockName, options) {
|
|
183
185
|
return (target, propertyKey, descriptor) => {
|
|
184
186
|
const methodName = propertyKey.toString();
|
|
185
|
-
//
|
|
186
|
-
const
|
|
187
|
-
const componentType = IOCContainer.getType(
|
|
187
|
+
// 验证装饰器使用的类型(从原型对象获取类构造函数)
|
|
188
|
+
const targetClass = target.constructor;
|
|
189
|
+
const componentType = IOCContainer.getType(targetClass);
|
|
188
190
|
if (componentType !== "SERVICE" && componentType !== "COMPONENT") {
|
|
189
191
|
throw Error("@RedLock decorator can only be used on SERVICE or COMPONENT classes.");
|
|
190
192
|
}
|
|
@@ -206,12 +208,14 @@ function RedLock(lockName, options) {
|
|
|
206
208
|
if (options) {
|
|
207
209
|
validateRedLockMethodOptions(options);
|
|
208
210
|
}
|
|
211
|
+
// 保存类到IOC容器
|
|
212
|
+
IOCContainer.saveClass(COMPONENT_REDLOCK, targetClass, targetClass.name);
|
|
209
213
|
// 保存RedLock元数据到 IOC 容器(lockName已确定)
|
|
210
|
-
IOCContainer.attachClassMetadata(
|
|
214
|
+
IOCContainer.attachClassMetadata(COMPONENT_REDLOCK, DecoratorType.REDLOCK, {
|
|
211
215
|
method: methodName,
|
|
212
216
|
name: lockName, // 确定的锁名称,不会为undefined
|
|
213
217
|
options
|
|
214
|
-
},
|
|
218
|
+
}, target, methodName);
|
|
215
219
|
};
|
|
216
220
|
}
|
|
217
221
|
|
|
@@ -259,26 +263,29 @@ function Scheduled(cron, timezone) {
|
|
|
259
263
|
throw Error("Timezone must be a string");
|
|
260
264
|
}
|
|
261
265
|
return (target, propertyKey, descriptor) => {
|
|
262
|
-
//
|
|
263
|
-
const
|
|
264
|
-
const componentType = IOCContainer.getType(
|
|
266
|
+
// 验证装饰器使用的类型(从原型对象获取类构造函数)
|
|
267
|
+
const targetClass = target.constructor;
|
|
268
|
+
const componentType = IOCContainer.getType(targetClass);
|
|
265
269
|
if (componentType !== "SERVICE" && componentType !== "COMPONENT") {
|
|
266
270
|
throw Error("@Scheduled decorator can only be used on SERVICE or COMPONENT classes.");
|
|
267
271
|
}
|
|
268
272
|
// 验证方法名
|
|
269
|
-
|
|
273
|
+
const methodName = propertyKey.toString();
|
|
274
|
+
if (!methodName || typeof methodName !== 'string') {
|
|
270
275
|
throw Error("Method name is required for @Scheduled decorator");
|
|
271
276
|
}
|
|
272
277
|
// 验证方法描述符
|
|
273
278
|
if (!descriptor || typeof descriptor.value !== 'function') {
|
|
274
279
|
throw Error("@Scheduled decorator can only be applied to methods");
|
|
275
280
|
}
|
|
276
|
-
//
|
|
277
|
-
IOCContainer.
|
|
278
|
-
|
|
281
|
+
// 保存类到IOC容器
|
|
282
|
+
IOCContainer.saveClass(COMPONENT_SCHEDULED, targetClass, targetClass.name);
|
|
283
|
+
// 保存调度元数据到 IOC 容器
|
|
284
|
+
IOCContainer.attachClassMetadata(COMPONENT_SCHEDULED, DecoratorType.SCHEDULED, {
|
|
285
|
+
method: methodName,
|
|
279
286
|
cron,
|
|
280
287
|
timezone // 保存用户指定的值,可能为undefined
|
|
281
|
-
},
|
|
288
|
+
}, target, methodName);
|
|
282
289
|
};
|
|
283
290
|
}
|
|
284
291
|
|
|
@@ -707,12 +714,12 @@ async function initRedLock(options, app) {
|
|
|
707
714
|
* @param {RedLockOptions} options - RedLock 配置选项
|
|
708
715
|
* @param {Koatty} app - Koatty 应用实例
|
|
709
716
|
*/
|
|
710
|
-
async function injectRedLock(
|
|
717
|
+
async function injectRedLock(_options, _app) {
|
|
711
718
|
try {
|
|
712
719
|
DefaultLogger.Debug('Starting batch RedLock injection...');
|
|
713
|
-
const componentList = IOCContainer.listClass(
|
|
720
|
+
const componentList = IOCContainer.listClass(COMPONENT_REDLOCK);
|
|
714
721
|
for (const component of componentList) {
|
|
715
|
-
const classMetadata = IOCContainer.getClassMetadata(
|
|
722
|
+
const classMetadata = IOCContainer.getClassMetadata(COMPONENT_REDLOCK, DecoratorType.REDLOCK, component.target);
|
|
716
723
|
if (!classMetadata) {
|
|
717
724
|
continue;
|
|
718
725
|
}
|
|
@@ -725,7 +732,7 @@ async function injectRedLock(options, app) {
|
|
|
725
732
|
}
|
|
726
733
|
// 查找所有RedLock方法的元数据
|
|
727
734
|
for (const [key, value] of Object.entries(metadata)) {
|
|
728
|
-
if (key.startsWith('REDLOCK
|
|
735
|
+
if (key.startsWith('REDLOCK')) {
|
|
729
736
|
const redlockData = value;
|
|
730
737
|
const targetMethod = instance[redlockData.method];
|
|
731
738
|
if (!Helper.isFunction(targetMethod)) {
|
|
@@ -893,12 +900,12 @@ function redLockerDescriptor(descriptor, name, method, methodOptions) {
|
|
|
893
900
|
* @param {RedLockOptions} options - RedLock 配置选项
|
|
894
901
|
* @param {Koatty} app - Koatty 应用实例
|
|
895
902
|
*/
|
|
896
|
-
async function injectSchedule(
|
|
903
|
+
async function injectSchedule(_options, _app) {
|
|
897
904
|
try {
|
|
898
905
|
DefaultLogger.Debug('Starting batch schedule injection...');
|
|
899
|
-
const componentList = IOCContainer.listClass(
|
|
906
|
+
const componentList = IOCContainer.listClass(COMPONENT_SCHEDULED);
|
|
900
907
|
for (const component of componentList) {
|
|
901
|
-
const classMetadata = IOCContainer.getClassMetadata(
|
|
908
|
+
const classMetadata = IOCContainer.getClassMetadata(COMPONENT_SCHEDULED, DecoratorType.SCHEDULED, component.target);
|
|
902
909
|
if (!classMetadata) {
|
|
903
910
|
continue;
|
|
904
911
|
}
|
|
@@ -911,7 +918,7 @@ async function injectSchedule(options, app) {
|
|
|
911
918
|
}
|
|
912
919
|
// 查找所有调度方法的元数据
|
|
913
920
|
for (const [key, value] of Object.entries(metadata)) {
|
|
914
|
-
if (key.startsWith('SCHEDULED
|
|
921
|
+
if (key.startsWith('SCHEDULED')) {
|
|
915
922
|
const scheduleData = value;
|
|
916
923
|
const targetMethod = instance[scheduleData.method];
|
|
917
924
|
if (!Helper.isFunction(targetMethod)) {
|
|
@@ -920,7 +927,7 @@ async function injectSchedule(options, app) {
|
|
|
920
927
|
}
|
|
921
928
|
const taskName = `${className}_${scheduleData.method}`;
|
|
922
929
|
const tz = getEffectiveTimezone(scheduleData.timezone);
|
|
923
|
-
|
|
930
|
+
new CronJob(scheduleData.cron, () => {
|
|
924
931
|
DefaultLogger.Debug(`The schedule job ${taskName} started.`);
|
|
925
932
|
Promise.resolve(targetMethod.call(instance))
|
|
926
933
|
.then(() => {
|
package/dist/package.json
CHANGED