@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.
- package/dist/dataAccess/entityManager.d.ts +2 -2
- package/dist/dataAccess/entityValidator.d.ts +3 -0
- package/dist/helpers/entityHelpers.d.ts +1 -1
- package/dist/helpers/metaHelper.d.ts +7 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +84 -26
- package/dist/plugins/cronJob/CronJobPluginTypes.d.ts +1 -0
- package/dist/plugins/cronJob/services/CronJobService.d.ts +3 -2
- package/dist/types/cron-job-types.d.ts +7 -0
- package/dist/utilities/timeUtility.d.ts +1 -0
- package/package.json +1 -1
- package/src/core/actionHandler.ts +1 -0
- package/src/dataAccess/entityManager.ts +12 -7
- package/src/dataAccess/entityMapper.ts +12 -1
- package/src/dataAccess/entityValidator.ts +26 -0
- package/src/helpers/entityHelpers.ts +1 -1
- package/src/helpers/metaHelper.ts +7 -0
- package/src/index.ts +1 -0
- package/src/plugins/cronJob/CronJobPluginTypes.ts +1 -0
- package/src/plugins/cronJob/actionHandlers/runCronJob.ts +9 -1
- package/src/plugins/cronJob/services/CronJobService.ts +23 -15
- package/src/types/cron-job-types.ts +8 -0
- package/src/utilities/timeUtility.ts +4 -0
|
@@ -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 |
|
|
32
|
-
findById(options: FindEntityByIdOptions | string | number): Promise<TEntity |
|
|
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>;
|
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
import { IRpdServer } from "../core/server";
|
|
2
2
|
import { RpdDataModel } from "../types";
|
|
3
|
-
export declare function
|
|
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] =
|
|
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
|
|
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
|
|
2646
|
-
|
|
2647
|
-
|
|
2648
|
-
|
|
2649
|
-
|
|
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
|
-
|
|
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
|
|
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 =
|
|
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
|
-
|
|
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(
|
|
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(
|
|
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
|
|
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;
|
|
@@ -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(
|
|
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
|
/**
|
package/package.json
CHANGED
|
@@ -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 {
|
|
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
|
-
|
|
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
|
|
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 =
|
|
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 |
|
|
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 |
|
|
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] =
|
|
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
|
|
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";
|
|
@@ -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
|
-
|
|
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(
|
|
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(
|
|
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
|
|
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 {
|