@ruiapp/rapid-core 0.8.5 → 0.8.7

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.
@@ -28,8 +28,8 @@ export default class EntityManager<TEntity = any> {
28
28
  constructor(server: IRpdServer, dataAccessor: IRpdDataAccessor);
29
29
  getModel(): RpdDataModel;
30
30
  findEntities(options: FindEntityOptions): Promise<TEntity[]>;
31
- findEntity(options: FindEntityOptions): Promise<TEntity | null>;
32
- findById(options: FindEntityByIdOptions | string | number): Promise<TEntity | null>;
31
+ findEntity(options: FindEntityOptions): Promise<TEntity | undefined>;
32
+ findById(options: FindEntityByIdOptions | string | number): Promise<TEntity | undefined>;
33
33
  createEntity(options: CreateEntityOptions, plugin?: RapidPlugin): Promise<TEntity>;
34
34
  updateEntityById(options: UpdateEntityByIdOptions, plugin?: RapidPlugin): Promise<TEntity>;
35
35
  count(options: CountEntityOptions): Promise<number>;
@@ -0,0 +1,3 @@
1
+ import { IRpdServer } from "../core/server";
2
+ import { RpdDataModel } from "../types";
3
+ export declare function validateEntity(server: IRpdServer, model: RpdDataModel, entity: any): Promise<any>;
@@ -1,3 +1,3 @@
1
1
  import { IRpdServer } from "../core/server";
2
2
  import { RpdDataModel } from "../types";
3
- export declare function getEntityPartChanges(server: IRpdServer, model: RpdDataModel, before: any, after: any): Record<string, any> | null;
3
+ export declare function detectChangedFieldsOfEntity(server: IRpdServer, model: RpdDataModel, before: any, after: any): Record<string, any> | null;
@@ -5,6 +5,13 @@ export declare function isOneRelationProperty(property: RpdDataModelProperty): b
5
5
  export declare function isManyRelationProperty(property: RpdDataModelProperty): boolean;
6
6
  export declare function getEntityProperties(server: IRpdServer, model: RpdDataModel, predicate?: (item: RpdDataModelProperty) => boolean): RpdDataModelProperty[];
7
7
  export declare function getEntityPropertiesIncludingBase(server: IRpdServer, model: RpdDataModel, predicate?: (item: RpdDataModelProperty) => boolean): RpdDataModelProperty[];
8
+ /**
9
+ * 根据 code 获取实体属性信息。如实体为派生实体,可能会返回基础实体中的属性信息。
10
+ * @param server
11
+ * @param model
12
+ * @param propertyCode
13
+ * @returns
14
+ */
8
15
  export declare function getEntityPropertyByCode(server: IRpdServer, model: RpdDataModel, propertyCode: string): RpdDataModelProperty | undefined;
9
16
  export declare function getEntityProperty(server: IRpdServer, model: RpdDataModel, predicate: (item: RpdDataModelProperty) => boolean): RpdDataModelProperty | undefined;
10
17
  export declare function getEntityOwnPropertyByCode(model: RpdDataModel, propertyCode: string): RpdDataModelProperty | undefined;
package/dist/index.d.ts CHANGED
@@ -15,6 +15,7 @@ export * from "./utilities/accessControlUtility";
15
15
  export * from "./utilities/entityUtility";
16
16
  export * from "./utilities/jwtUtility";
17
17
  export * from "./utilities/timeUtility";
18
+ export * from "./helpers/entityHelpers";
18
19
  export * from "./helpers/licenseHelper";
19
20
  export * from "./deno-std/http/cookie";
20
21
  export { mapDbRowToEntity } from "./dataAccess/entityMapper";
package/dist/index.js CHANGED
@@ -2425,6 +2425,13 @@ function getEntityPropertiesIncludingBase(server, model, predicate) {
2425
2425
  }
2426
2426
  return [...baseProperties, ...properties];
2427
2427
  }
2428
+ /**
2429
+ * 根据 code 获取实体属性信息。如实体为派生实体,可能会返回基础实体中的属性信息。
2430
+ * @param server
2431
+ * @param model
2432
+ * @param propertyCode
2433
+ * @returns
2434
+ */
2428
2435
  function getEntityPropertyByCode(server, model, propertyCode) {
2429
2436
  return getEntityProperty(server, model, (e) => e.code === propertyCode);
2430
2437
  }
@@ -2463,6 +2470,16 @@ function getEntityPropertyByFieldName(server, model, fieldName) {
2463
2470
  return property;
2464
2471
  }
2465
2472
 
2473
+ function getNowString() {
2474
+ return dayjs__default["default"]().format("YYYY-MM-DD HH:mm:ss.SSS");
2475
+ }
2476
+ function getNowStringWithTimezone() {
2477
+ return dayjs__default["default"]().format("YYYY-MM-DD HH:mm:ss.SSSZ");
2478
+ }
2479
+ function getDateString(timeString) {
2480
+ return dayjs__default["default"](timeString).format("YYYY-MM-DD");
2481
+ }
2482
+
2466
2483
  // TODO Generate mapper and cache it.
2467
2484
  function mapDbRowToEntity(server, model, row, keepNonPropertyFields) {
2468
2485
  if (!row) {
@@ -2505,8 +2522,16 @@ function mapDbRowToEntity(server, model, row, keepNonPropertyFields) {
2505
2522
  }
2506
2523
  }
2507
2524
  else {
2525
+ let fieldValue = row[columnName];
2526
+ if (property) {
2527
+ if (property.type === "date") {
2528
+ if (fieldValue) {
2529
+ fieldValue = getDateString(fieldValue);
2530
+ }
2531
+ }
2532
+ }
2508
2533
  if (!result[propertyName]) {
2509
- result[propertyName] = row[columnName];
2534
+ result[propertyName] = fieldValue;
2510
2535
  }
2511
2536
  }
2512
2537
  });
@@ -2566,7 +2591,7 @@ function mapPropertyNameToColumnName(server, model, propertyName) {
2566
2591
  return property.columnName || property.code;
2567
2592
  }
2568
2593
 
2569
- function getEntityPartChanges(server, model, before, after) {
2594
+ function detectChangedFieldsOfEntity(server, model, before, after) {
2570
2595
  if (!before) {
2571
2596
  throw new Error("Argument 'before' can not be null.");
2572
2597
  }
@@ -2642,11 +2667,23 @@ function getEntityPartChanges(server, model, before, after) {
2642
2667
  return null;
2643
2668
  }
2644
2669
 
2645
- function getNowString() {
2646
- return dayjs__default["default"]().format("YYYY-MM-DD HH:mm:ss.SSS");
2647
- }
2648
- function getNowStringWithTimezone() {
2649
- return dayjs__default["default"]().format("YYYY-MM-DD HH:mm:ss.SSSZ");
2670
+ async function validateEntity(server, model, entity) {
2671
+ for (const propCode in entity) {
2672
+ let prop = getEntityPropertyByCode(server, model, propCode);
2673
+ if (!prop) {
2674
+ getEntityPropertyByFieldName(server, model, propCode);
2675
+ }
2676
+ if (!prop) {
2677
+ continue;
2678
+ }
2679
+ if (prop.type === "date") {
2680
+ const originValue = entity[propCode];
2681
+ if (originValue) {
2682
+ entity[propCode] = getDateString(originValue);
2683
+ }
2684
+ }
2685
+ }
2686
+ return entity;
2650
2687
  }
2651
2688
 
2652
2689
  function convertEntityOrderByToRowOrderBy(server, model, baseModel, orderByList) {
@@ -3286,7 +3323,9 @@ async function createEntity(server, dataAccessor, options, plugin) {
3286
3323
  if (model.derivedTypePropertyCode) {
3287
3324
  throw newEntityOperationError("Create base entity directly is not allowed.");
3288
3325
  }
3289
- const { entity, routeContext } = options;
3326
+ let { entity } = options;
3327
+ entity = await validateEntity(server, model, entity);
3328
+ const { routeContext } = options;
3290
3329
  const userId = options.routeContext?.state?.userId;
3291
3330
  if (userId) {
3292
3331
  const createdByProperty = getEntityPropertyByCode(server, model, "createdBy");
@@ -3537,6 +3576,8 @@ async function updateEntityById(server, dataAccessor, options, plugin) {
3537
3576
  if (!id) {
3538
3577
  throw new Error("Id is required when updating an entity.");
3539
3578
  }
3579
+ let { entityToSave } = options;
3580
+ entityToSave = await validateEntity(server, model, entityToSave);
3540
3581
  const entity = await findById(server, dataAccessor, {
3541
3582
  routeContext,
3542
3583
  id,
@@ -3545,8 +3586,7 @@ async function updateEntityById(server, dataAccessor, options, plugin) {
3545
3586
  if (!entity) {
3546
3587
  throw new Error(`${model.namespace}.${model.singularCode} with id "${id}" was not found.`);
3547
3588
  }
3548
- let { entityToSave } = options;
3549
- let changes = getEntityPartChanges(server, model, entity, entityToSave);
3589
+ let changes = detectChangedFieldsOfEntity(server, model, entity, entityToSave);
3550
3590
  if (!changes && !options.operation) {
3551
3591
  return entity;
3552
3592
  }
@@ -3576,7 +3616,7 @@ async function updateEntityById(server, dataAccessor, options, plugin) {
3576
3616
  sender: plugin,
3577
3617
  routeContext: options.routeContext,
3578
3618
  });
3579
- changes = getEntityPartChanges(server, model, entity, entityToSave);
3619
+ changes = detectChangedFieldsOfEntity(server, model, entity, entityToSave);
3580
3620
  // check readonly properties
3581
3621
  Object.keys(changes).forEach((propertyName) => {
3582
3622
  let isReadonlyProperty = false;
@@ -8472,7 +8512,15 @@ async function handler$1(plugin, ctx, options) {
8472
8512
  if (!job) {
8473
8513
  throw new Error(`Cron job with code '${input.code}' was not found.`);
8474
8514
  }
8475
- await cronJobService.executeJob(job);
8515
+ let jobExecutionContext = {
8516
+ logger: server.getLogger(),
8517
+ routerContext,
8518
+ next: null,
8519
+ server,
8520
+ applicationConfig: null,
8521
+ input: input.input,
8522
+ };
8523
+ await cronJobService.executeJob(jobExecutionContext, job);
8476
8524
  response.json({});
8477
8525
  }
8478
8526
 
@@ -8534,7 +8582,7 @@ class CronJobService {
8534
8582
  ...(job.jobOptions || {}),
8535
8583
  cronTime: job.cronTime,
8536
8584
  onTick: async () => {
8537
- await this.tryExecuteJob(server, job);
8585
+ await this.tryExecuteJob(job);
8538
8586
  },
8539
8587
  });
8540
8588
  jobInstance.start();
@@ -8544,33 +8592,41 @@ class CronJobService {
8544
8592
  });
8545
8593
  }
8546
8594
  }
8547
- async tryExecuteJob(server, job) {
8595
+ async tryExecuteJob(job) {
8596
+ const server = this.#server;
8548
8597
  const logger = server.getLogger();
8549
8598
  logger.info(`Executing cron job '${job.code}'...`);
8599
+ let handlerContext = {
8600
+ logger,
8601
+ routerContext: RouteContext.newSystemOperationContext(server),
8602
+ next: null,
8603
+ server,
8604
+ applicationConfig: null,
8605
+ input: null,
8606
+ };
8550
8607
  try {
8551
- await this.executeJob(job);
8608
+ await this.executeJob(handlerContext, job);
8552
8609
  logger.info(`Completed cron job '${job.code}'...`);
8553
8610
  }
8554
8611
  catch (ex) {
8555
- logger.error('Cron job "%s" execution error: %s', job.code, ex.message, { cronJobCode: job.code });
8612
+ logger.error('Cron job "%s" execution error: %s', job.code, ex.message);
8613
+ if (job.onError) {
8614
+ try {
8615
+ await job.onError(handlerContext, ex);
8616
+ }
8617
+ catch (ex) {
8618
+ logger.error('Error handler of cron job "%s" execution failed: %s', job.code, ex.message);
8619
+ }
8620
+ }
8556
8621
  }
8557
8622
  }
8558
8623
  /**
8559
8624
  * 执行指定任务
8560
8625
  * @param job
8561
8626
  */
8562
- async executeJob(job) {
8627
+ async executeJob(handlerContext, job) {
8563
8628
  const server = this.#server;
8564
- const logger = server.getLogger();
8565
8629
  validateLicense(server);
8566
- let handlerContext = {
8567
- logger,
8568
- routerContext: RouteContext.newSystemOperationContext(server),
8569
- next: null,
8570
- server,
8571
- applicationConfig: null,
8572
- input: null,
8573
- };
8574
8630
  if (job.actionHandlerCode) {
8575
8631
  const actionHandler = server.getActionHandlerByCode(job.code);
8576
8632
  await actionHandler(handlerContext, job.handleOptions);
@@ -9039,8 +9095,10 @@ exports.bootstrapApplicationConfig = bootstrapApplicationConfig$1;
9039
9095
  exports.createJwt = createJwt;
9040
9096
  exports.decodeJwt = decodeJwt;
9041
9097
  exports.deleteCookie = deleteCookie;
9098
+ exports.detectChangedFieldsOfEntity = detectChangedFieldsOfEntity;
9042
9099
  exports.generateJwtSecretKey = generateJwtSecretKey;
9043
9100
  exports.getCookies = getCookies;
9101
+ exports.getDateString = getDateString;
9044
9102
  exports.getEntityRelationTargetId = getEntityRelationTargetId;
9045
9103
  exports.getNowString = getNowString;
9046
9104
  exports.getNowStringWithTimezone = getNowStringWithTimezone;
@@ -4,6 +4,7 @@ export type RunCronJobActionHandlerOptions = {
4
4
  };
5
5
  export type RunCronJobInput = {
6
6
  code?: string;
7
+ input?: any;
7
8
  };
8
9
  export type NamedCronJobInstance = {
9
10
  code: string;
@@ -1,5 +1,6 @@
1
1
  import { IRpdServer } from "../../../core/server";
2
2
  import { CronJobConfiguration } from "../../../types/cron-job-types";
3
+ import { ActionHandlerContext } from "../../../core/actionHandler";
3
4
  export default class CronJobService {
4
5
  #private;
5
6
  constructor(server: IRpdServer);
@@ -13,10 +14,10 @@ export default class CronJobService {
13
14
  * 重新加载定时任务
14
15
  */
15
16
  reloadJobs(): void;
16
- tryExecuteJob(server: IRpdServer, job: CronJobConfiguration): Promise<void>;
17
+ tryExecuteJob(job: CronJobConfiguration): Promise<void>;
17
18
  /**
18
19
  * 执行指定任务
19
20
  * @param job
20
21
  */
21
- executeJob(job: CronJobConfiguration): Promise<void>;
22
+ executeJob(handlerContext: ActionHandlerContext, job: CronJobConfiguration): Promise<void>;
22
23
  }
@@ -35,6 +35,13 @@ export interface CronJobConfiguration {
35
35
  * 处理定时任务时的设置选项
36
36
  */
37
37
  handleOptions?: any;
38
+ /**
39
+ * 定时任务执行失败时的处理
40
+ * @param ctx
41
+ * @param error
42
+ * @returns
43
+ */
44
+ onError?: (ctx: ActionHandlerContext, error: any) => Promise<void>;
38
45
  }
39
46
  export interface CronJobOptions {
40
47
  /**
@@ -1,2 +1,3 @@
1
1
  export declare function getNowString(): string;
2
2
  export declare function getNowStringWithTimezone(): string;
3
+ export declare function getDateString(timeString: any): string;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ruiapp/rapid-core",
3
- "version": "0.8.5",
3
+ "version": "0.8.7",
4
4
  "description": "",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -2,6 +2,7 @@ import { RpdApplicationConfig } from "~/types";
2
2
  import { IRpdServer, RapidPlugin } from "./server";
3
3
  import { Next, RouteContext } from "./routeContext";
4
4
  import { Logger } from "~/facilities/log/LogFacility";
5
+ import { CronJobConfiguration } from "~/types/cron-job-types";
5
6
 
6
7
  export interface ActionHandlerContext {
7
8
  logger: Logger;
@@ -23,7 +23,7 @@ import { isNullOrUndefined } from "~/utilities/typeUtility";
23
23
  import { mapDbRowToEntity, mapEntityToDbRow } from "./entityMapper";
24
24
  import { mapPropertyNameToColumnName } from "./propertyMapper";
25
25
  import { IRpdServer, RapidPlugin } from "~/core/server";
26
- import { getEntityPartChanges } from "~/helpers/entityHelpers";
26
+ import { detectChangedFieldsOfEntity } from "~/helpers/entityHelpers";
27
27
  import {
28
28
  cloneDeep,
29
29
  concat,
@@ -58,6 +58,7 @@ import { ColumnSelectOptions, CountRowOptions, FindRowOptions, FindRowOrderByOpt
58
58
  import { newEntityOperationError } from "~/utilities/errorUtility";
59
59
  import { getNowStringWithTimezone } from "~/utilities/timeUtility";
60
60
  import { RouteContext } from "~/core/routeContext";
61
+ import { validateEntity } from "./entityValidator";
61
62
 
62
63
  export type FindOneRelationEntitiesOptions = {
63
64
  server: IRpdServer;
@@ -777,8 +778,10 @@ async function createEntity(server: IRpdServer, dataAccessor: IRpdDataAccessor,
777
778
  throw newEntityOperationError("Create base entity directly is not allowed.");
778
779
  }
779
780
 
780
- const { entity, routeContext } = options;
781
+ let { entity } = options;
782
+ entity = await validateEntity(server, model, entity);
781
783
 
784
+ const { routeContext } = options;
782
785
  const userId = options.routeContext?.state?.userId;
783
786
  if (userId) {
784
787
  const createdByProperty = getEntityPropertyByCode(server, model, "createdBy");
@@ -1051,6 +1054,9 @@ async function updateEntityById(server: IRpdServer, dataAccessor: IRpdDataAccess
1051
1054
  throw new Error("Id is required when updating an entity.");
1052
1055
  }
1053
1056
 
1057
+ let { entityToSave } = options;
1058
+ entityToSave = await validateEntity(server, model, entityToSave);
1059
+
1054
1060
  const entity = await findById(server, dataAccessor, {
1055
1061
  routeContext,
1056
1062
  id,
@@ -1060,8 +1066,7 @@ async function updateEntityById(server: IRpdServer, dataAccessor: IRpdDataAccess
1060
1066
  throw new Error(`${model.namespace}.${model.singularCode} with id "${id}" was not found.`);
1061
1067
  }
1062
1068
 
1063
- let { entityToSave } = options;
1064
- let changes = getEntityPartChanges(server, model, entity, entityToSave);
1069
+ let changes = detectChangedFieldsOfEntity(server, model, entity, entityToSave);
1065
1070
  if (!changes && !options.operation) {
1066
1071
  return entity;
1067
1072
  }
@@ -1096,7 +1101,7 @@ async function updateEntityById(server: IRpdServer, dataAccessor: IRpdDataAccess
1096
1101
  routeContext: options.routeContext,
1097
1102
  });
1098
1103
 
1099
- changes = getEntityPartChanges(server, model, entity, entityToSave);
1104
+ changes = detectChangedFieldsOfEntity(server, model, entity, entityToSave);
1100
1105
 
1101
1106
  // check readonly properties
1102
1107
  Object.keys(changes).forEach((propertyName) => {
@@ -1787,11 +1792,11 @@ export default class EntityManager<TEntity = any> {
1787
1792
  return await findEntities(this.#server, this.#dataAccessor, options);
1788
1793
  }
1789
1794
 
1790
- async findEntity(options: FindEntityOptions): Promise<TEntity | null> {
1795
+ async findEntity(options: FindEntityOptions): Promise<TEntity | undefined> {
1791
1796
  return await findEntity(this.#server, this.#dataAccessor, options);
1792
1797
  }
1793
1798
 
1794
- async findById(options: FindEntityByIdOptions | string | number): Promise<TEntity | null> {
1799
+ async findById(options: FindEntityByIdOptions | string | number): Promise<TEntity | undefined> {
1795
1800
  // options is id
1796
1801
  if (!isObject(options)) {
1797
1802
  options = {
@@ -1,6 +1,7 @@
1
1
  import type { IRpdServer } from "~/core/server";
2
2
  import { RpdDataModel } from "~/types";
3
3
  import { getEntityProperty, getEntityPropertyByCode, isRelationProperty } from "../helpers/metaHelper";
4
+ import { getDateString } from "~/utilities/timeUtility";
4
5
 
5
6
  // TODO Generate mapper and cache it.
6
7
 
@@ -45,8 +46,18 @@ export function mapDbRowToEntity(server: IRpdServer, model: RpdDataModel, row: a
45
46
  result[columnName] = row[columnName];
46
47
  }
47
48
  } else {
49
+ let fieldValue = row[columnName];
50
+
51
+ if (property) {
52
+ if (property.type === "date") {
53
+ if (fieldValue) {
54
+ fieldValue = getDateString(fieldValue);
55
+ }
56
+ }
57
+ }
58
+
48
59
  if (!result[propertyName]) {
49
- result[propertyName] = row[columnName];
60
+ result[propertyName] = fieldValue;
50
61
  }
51
62
  }
52
63
  });
@@ -0,0 +1,26 @@
1
+ import { IRpdServer } from "~/core/server";
2
+ import { getEntityPropertyByCode, getEntityPropertyByFieldName } from "~/helpers/metaHelper";
3
+ import { RpdDataModel } from "~/types";
4
+ import { getDateString } from "~/utilities/timeUtility";
5
+
6
+ export async function validateEntity(server: IRpdServer, model: RpdDataModel, entity: any) {
7
+ for (const propCode in entity) {
8
+ let prop = getEntityPropertyByCode(server, model, propCode);
9
+ if (!prop) {
10
+ getEntityPropertyByFieldName(server, model, propCode);
11
+ }
12
+
13
+ if (!prop) {
14
+ continue;
15
+ }
16
+
17
+ if (prop.type === "date") {
18
+ const originValue = entity[propCode];
19
+ if (originValue) {
20
+ entity[propCode] = getDateString(originValue);
21
+ }
22
+ }
23
+ }
24
+
25
+ return entity;
26
+ }
@@ -3,7 +3,7 @@ import { IRpdServer } from "~/core/server";
3
3
  import { getEntityPropertyByCode, isOneRelationProperty } from "~/helpers/metaHelper";
4
4
  import { RpdDataModel } from "~/types";
5
5
 
6
- export function getEntityPartChanges(server: IRpdServer, model: RpdDataModel, before: any, after: any): Record<string, any> | null {
6
+ export function detectChangedFieldsOfEntity(server: IRpdServer, model: RpdDataModel, before: any, after: any): Record<string, any> | null {
7
7
  if (!before) {
8
8
  throw new Error("Argument 'before' can not be null.");
9
9
  }
@@ -53,6 +53,13 @@ export function getEntityPropertiesIncludingBase(server: IRpdServer, model: RpdD
53
53
  return [...baseProperties, ...properties];
54
54
  }
55
55
 
56
+ /**
57
+ * 根据 code 获取实体属性信息。如实体为派生实体,可能会返回基础实体中的属性信息。
58
+ * @param server
59
+ * @param model
60
+ * @param propertyCode
61
+ * @returns
62
+ */
56
63
  export function getEntityPropertyByCode(server: IRpdServer, model: RpdDataModel, propertyCode: string): RpdDataModelProperty | undefined {
57
64
  return getEntityProperty(server, model, (e) => e.code === propertyCode);
58
65
  }
package/src/index.ts CHANGED
@@ -23,6 +23,7 @@ export * from "./utilities/entityUtility";
23
23
  export * from "./utilities/jwtUtility";
24
24
  export * from "./utilities/timeUtility";
25
25
 
26
+ export * from "./helpers/entityHelpers";
26
27
  export * from "./helpers/licenseHelper";
27
28
 
28
29
  export * from "./deno-std/http/cookie";
@@ -6,6 +6,7 @@ export type RunCronJobActionHandlerOptions = {
6
6
 
7
7
  export type RunCronJobInput = {
8
8
  code?: string;
9
+ input?: any;
9
10
  };
10
11
 
11
12
  export type NamedCronJobInstance = {
@@ -25,7 +25,15 @@ export async function handler(plugin: CronJobPlugin, ctx: ActionHandlerContext,
25
25
  throw new Error(`Cron job with code '${input.code}' was not found.`);
26
26
  }
27
27
 
28
- await cronJobService.executeJob(job);
28
+ let jobExecutionContext: ActionHandlerContext = {
29
+ logger: server.getLogger(),
30
+ routerContext,
31
+ next: null,
32
+ server,
33
+ applicationConfig: null,
34
+ input: input.input,
35
+ };
36
+ await cronJobService.executeJob(jobExecutionContext, job);
29
37
 
30
38
  response.json({});
31
39
  }
@@ -48,7 +48,7 @@ export default class CronJobService {
48
48
  ...(job.jobOptions || {}),
49
49
  cronTime: job.cronTime,
50
50
  onTick: async () => {
51
- await this.tryExecuteJob(server, job);
51
+ await this.tryExecuteJob(job);
52
52
  },
53
53
  });
54
54
  jobInstance.start();
@@ -60,15 +60,33 @@ export default class CronJobService {
60
60
  }
61
61
  }
62
62
 
63
- async tryExecuteJob(server: IRpdServer, job: CronJobConfiguration) {
63
+ async tryExecuteJob(job: CronJobConfiguration) {
64
+ const server = this.#server;
64
65
  const logger = server.getLogger();
65
66
  logger.info(`Executing cron job '${job.code}'...`);
66
67
 
68
+ let handlerContext: ActionHandlerContext = {
69
+ logger,
70
+ routerContext: RouteContext.newSystemOperationContext(server),
71
+ next: null,
72
+ server,
73
+ applicationConfig: null,
74
+ input: null,
75
+ };
76
+
67
77
  try {
68
- await this.executeJob(job);
78
+ await this.executeJob(handlerContext, job);
69
79
  logger.info(`Completed cron job '${job.code}'...`);
70
80
  } catch (ex: any) {
71
- logger.error('Cron job "%s" execution error: %s', job.code, ex.message, { cronJobCode: job.code });
81
+ logger.error('Cron job "%s" execution error: %s', job.code, ex.message);
82
+
83
+ if (job.onError) {
84
+ try {
85
+ await job.onError(handlerContext, ex);
86
+ } catch (ex) {
87
+ logger.error('Error handler of cron job "%s" execution failed: %s', job.code, ex.message);
88
+ }
89
+ }
72
90
  }
73
91
  }
74
92
 
@@ -76,20 +94,10 @@ export default class CronJobService {
76
94
  * 执行指定任务
77
95
  * @param job
78
96
  */
79
- async executeJob(job: CronJobConfiguration) {
97
+ async executeJob(handlerContext: ActionHandlerContext, job: CronJobConfiguration) {
80
98
  const server = this.#server;
81
- const logger = server.getLogger();
82
99
  validateLicense(server);
83
100
 
84
- let handlerContext: ActionHandlerContext = {
85
- logger,
86
- routerContext: RouteContext.newSystemOperationContext(server),
87
- next: null,
88
- server,
89
- applicationConfig: null,
90
- input: null,
91
- };
92
-
93
101
  if (job.actionHandlerCode) {
94
102
  const actionHandler = server.getActionHandlerByCode(job.code);
95
103
  await actionHandler(handlerContext, job.handleOptions);
@@ -43,6 +43,14 @@ export interface CronJobConfiguration {
43
43
  * 处理定时任务时的设置选项
44
44
  */
45
45
  handleOptions?: any;
46
+
47
+ /**
48
+ * 定时任务执行失败时的处理
49
+ * @param ctx
50
+ * @param error
51
+ * @returns
52
+ */
53
+ onError?: (ctx: ActionHandlerContext, error: any) => Promise<void>;
46
54
  }
47
55
 
48
56
  export interface CronJobOptions {
@@ -7,3 +7,7 @@ export function getNowString() {
7
7
  export function getNowStringWithTimezone() {
8
8
  return dayjs().format("YYYY-MM-DD HH:mm:ss.SSSZ");
9
9
  }
10
+
11
+ export function getDateString(timeString) {
12
+ return dayjs(timeString).format("YYYY-MM-DD");
13
+ }