@ruiapp/rapid-core 0.8.9 → 0.8.11
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/core/routeContext.d.ts +3 -1
- package/dist/index.js +364 -42
- package/dist/plugins/cronJob/CronJobPluginTypes.d.ts +26 -0
- package/dist/plugins/cronJob/entityWatchers/cronJobEntityWatchers.d.ts +7 -0
- package/dist/plugins/cronJob/entityWatchers/index.d.ts +6 -0
- package/dist/plugins/cronJob/models/CronJob.d.ts +3 -0
- package/dist/plugins/cronJob/models/index.d.ts +2 -0
- package/dist/plugins/cronJob/services/CronJobService.d.ts +4 -1
- package/dist/utilities/timeUtility.d.ts +1 -0
- package/package.json +1 -1
- package/src/core/routeContext.ts +34 -6
- package/src/helpers/runCollectionEntityActionHandler.ts +2 -1
- package/src/plugins/cronJob/CronJobPlugin.ts +30 -5
- package/src/plugins/cronJob/CronJobPluginTypes.ts +29 -0
- package/src/plugins/cronJob/entityWatchers/cronJobEntityWatchers.ts +24 -0
- package/src/plugins/cronJob/entityWatchers/index.ts +4 -0
- package/src/plugins/cronJob/models/CronJob.ts +129 -0
- package/src/plugins/cronJob/models/index.ts +3 -0
- package/src/plugins/cronJob/services/CronJobService.ts +168 -24
- package/src/utilities/timeUtility.ts +4 -0
|
@@ -4,6 +4,7 @@ import { HttpStatus } from "./http-types";
|
|
|
4
4
|
import { IRpdServer } from "./server";
|
|
5
5
|
import { IDatabaseAccessor, IDatabaseClient } from "../types";
|
|
6
6
|
export type Next = () => Promise<void>;
|
|
7
|
+
export type TransactionState = "uninited" | "inited" | "started";
|
|
7
8
|
export declare class RouteContext {
|
|
8
9
|
#private;
|
|
9
10
|
readonly request: RapidRequest;
|
|
@@ -22,7 +23,8 @@ export declare class RouteContext {
|
|
|
22
23
|
json(obj: any, status?: HttpStatus, headers?: HeadersInit): void;
|
|
23
24
|
redirect(url: string, status?: HttpStatus): void;
|
|
24
25
|
getDbTransactionClient(): IDatabaseClient | undefined;
|
|
25
|
-
|
|
26
|
+
initDbTransactionClient(): Promise<IDatabaseClient>;
|
|
27
|
+
beginDbTransaction(): Promise<void>;
|
|
26
28
|
commitDbTransaction(): Promise<void>;
|
|
27
29
|
rollbackDbTransaction(): Promise<void>;
|
|
28
30
|
}
|
package/dist/index.js
CHANGED
|
@@ -1052,12 +1052,14 @@ class RouteContext {
|
|
|
1052
1052
|
routeConfig;
|
|
1053
1053
|
#server;
|
|
1054
1054
|
#dbTransactionClient;
|
|
1055
|
+
#dbTransactionState;
|
|
1055
1056
|
static newSystemOperationContext(server) {
|
|
1056
1057
|
return new RouteContext(server);
|
|
1057
1058
|
}
|
|
1058
1059
|
constructor(server, request) {
|
|
1059
1060
|
this.#server = server;
|
|
1060
1061
|
this.databaseAccessor = server.getDatabaseAccessor();
|
|
1062
|
+
this.#dbTransactionState = "uninited";
|
|
1061
1063
|
this.request = request;
|
|
1062
1064
|
this.state = {};
|
|
1063
1065
|
this.response = new RapidResponse();
|
|
@@ -1091,27 +1093,45 @@ class RouteContext {
|
|
|
1091
1093
|
getDbTransactionClient() {
|
|
1092
1094
|
return this.#dbTransactionClient;
|
|
1093
1095
|
}
|
|
1094
|
-
async
|
|
1096
|
+
async initDbTransactionClient() {
|
|
1095
1097
|
let dbClient = this.#dbTransactionClient;
|
|
1096
1098
|
if (dbClient) {
|
|
1097
|
-
|
|
1099
|
+
return dbClient;
|
|
1098
1100
|
}
|
|
1099
1101
|
dbClient = await this.databaseAccessor.getClient();
|
|
1100
|
-
|
|
1102
|
+
this.#dbTransactionState = "inited";
|
|
1101
1103
|
this.#dbTransactionClient = dbClient;
|
|
1102
1104
|
return dbClient;
|
|
1103
1105
|
}
|
|
1106
|
+
async beginDbTransaction() {
|
|
1107
|
+
if (!this.#dbTransactionClient) {
|
|
1108
|
+
throw new Error("Database transaction has not been inited. You should call initDbTransactionClient() first.");
|
|
1109
|
+
}
|
|
1110
|
+
if (this.#dbTransactionState === "started") {
|
|
1111
|
+
throw new Error("Database transaction has been started. You can not begin a new transaction before you commit or rollback it.");
|
|
1112
|
+
}
|
|
1113
|
+
await this.databaseAccessor.queryDatabaseObject("BEGIN", [], this.#dbTransactionClient);
|
|
1114
|
+
this.#dbTransactionState = "started";
|
|
1115
|
+
}
|
|
1104
1116
|
async commitDbTransaction() {
|
|
1105
1117
|
if (!this.#dbTransactionClient) {
|
|
1118
|
+
throw new Error("Database transaction has not been inited. You should call initDbTransactionClient() first.");
|
|
1119
|
+
}
|
|
1120
|
+
if (this.#dbTransactionState !== "started") {
|
|
1106
1121
|
throw new Error("Database transaction has not been started. You should call beginDbTransaction() first.");
|
|
1107
1122
|
}
|
|
1108
1123
|
await this.databaseAccessor.queryDatabaseObject("COMMIT", [], this.#dbTransactionClient);
|
|
1124
|
+
this.#dbTransactionState = "inited";
|
|
1109
1125
|
}
|
|
1110
1126
|
async rollbackDbTransaction() {
|
|
1111
1127
|
if (!this.#dbTransactionClient) {
|
|
1128
|
+
throw new Error("Database transaction has not been inited. You should call initDbTransactionClient() first.");
|
|
1129
|
+
}
|
|
1130
|
+
if (this.#dbTransactionState !== "started") {
|
|
1112
1131
|
throw new Error("Database transaction has not been started. You should call beginDbTransaction() first.");
|
|
1113
1132
|
}
|
|
1114
1133
|
await this.databaseAccessor.queryDatabaseObject("ROLLBACK", [], this.#dbTransactionClient);
|
|
1134
|
+
this.#dbTransactionState = "inited";
|
|
1115
1135
|
}
|
|
1116
1136
|
}
|
|
1117
1137
|
|
|
@@ -2478,6 +2498,9 @@ function getNowStringWithTimezone() {
|
|
|
2478
2498
|
}
|
|
2479
2499
|
function getDateString(timeString) {
|
|
2480
2500
|
return dayjs__default["default"](timeString).format("YYYY-MM-DD");
|
|
2501
|
+
}
|
|
2502
|
+
function formatDateTimeWithTimezone(source) {
|
|
2503
|
+
return dayjs__default["default"](source).format("YYYY-MM-DD HH:mm:ss.SSSZ");
|
|
2481
2504
|
}
|
|
2482
2505
|
|
|
2483
2506
|
// TODO Generate mapper and cache it.
|
|
@@ -5593,7 +5616,8 @@ async function runCollectionEntityActionHandler(ctx, options, code, autoMergeInp
|
|
|
5593
5616
|
if (runInTransaction) {
|
|
5594
5617
|
let transactionDbClient;
|
|
5595
5618
|
try {
|
|
5596
|
-
transactionDbClient = await routeContext.
|
|
5619
|
+
transactionDbClient = await routeContext.initDbTransactionClient();
|
|
5620
|
+
await routeContext.beginDbTransaction();
|
|
5597
5621
|
const result = handleEntityAction(entityManager, autoMergeInput ? mergedInput : input);
|
|
5598
5622
|
if (result instanceof Promise) {
|
|
5599
5623
|
ctx.output = await result;
|
|
@@ -6239,7 +6263,7 @@ var SequenceAutoIncrementRecord = {
|
|
|
6239
6263
|
],
|
|
6240
6264
|
};
|
|
6241
6265
|
|
|
6242
|
-
var pluginModels$
|
|
6266
|
+
var pluginModels$7 = [SequenceRule, SequenceAutoIncrementRecord];
|
|
6243
6267
|
|
|
6244
6268
|
var generateSn = {
|
|
6245
6269
|
namespace: "svc",
|
|
@@ -6478,7 +6502,7 @@ class SequencePlugin$1 {
|
|
|
6478
6502
|
}
|
|
6479
6503
|
}
|
|
6480
6504
|
async configureModels(server, applicationConfig) {
|
|
6481
|
-
server.appendApplicationConfig({ models: pluginModels$
|
|
6505
|
+
server.appendApplicationConfig({ models: pluginModels$7 });
|
|
6482
6506
|
}
|
|
6483
6507
|
async configureServices(server, applicationConfig) {
|
|
6484
6508
|
this.#sequenceService = new SequenceService(server);
|
|
@@ -6962,7 +6986,7 @@ var AccessToken = {
|
|
|
6962
6986
|
],
|
|
6963
6987
|
};
|
|
6964
6988
|
|
|
6965
|
-
var pluginModels$
|
|
6989
|
+
var pluginModels$6 = [AccessToken];
|
|
6966
6990
|
|
|
6967
6991
|
var changePassword = {
|
|
6968
6992
|
namespace: "auth",
|
|
@@ -7082,7 +7106,7 @@ class AuthPlugin {
|
|
|
7082
7106
|
}
|
|
7083
7107
|
}
|
|
7084
7108
|
async configureModels(server, applicationConfig) {
|
|
7085
|
-
server.appendApplicationConfig({ models: pluginModels$
|
|
7109
|
+
server.appendApplicationConfig({ models: pluginModels$6 });
|
|
7086
7110
|
}
|
|
7087
7111
|
async configureServices(server, applicationConfig) {
|
|
7088
7112
|
this.#authService = new AuthService(server, server.config.jwtKey);
|
|
@@ -7357,7 +7381,7 @@ var getLicense$1 = /*#__PURE__*/Object.freeze({
|
|
|
7357
7381
|
|
|
7358
7382
|
var pluginActionHandlers$6 = [getLicense$1];
|
|
7359
7383
|
|
|
7360
|
-
var pluginModels$
|
|
7384
|
+
var pluginModels$5 = [];
|
|
7361
7385
|
|
|
7362
7386
|
var getLicense = {
|
|
7363
7387
|
namespace: "svc",
|
|
@@ -7528,7 +7552,7 @@ class LicensePlugin {
|
|
|
7528
7552
|
}
|
|
7529
7553
|
}
|
|
7530
7554
|
async configureModels(server, applicationConfig) {
|
|
7531
|
-
server.appendApplicationConfig({ models: pluginModels$
|
|
7555
|
+
server.appendApplicationConfig({ models: pluginModels$5 });
|
|
7532
7556
|
}
|
|
7533
7557
|
async configureServices(server, applicationConfig) {
|
|
7534
7558
|
this.#licenseService = new LicenseService(server, this.#encryptionKey);
|
|
@@ -7544,7 +7568,7 @@ class LicensePlugin {
|
|
|
7544
7568
|
|
|
7545
7569
|
var pluginActionHandlers$5 = [];
|
|
7546
7570
|
|
|
7547
|
-
var pluginModels$
|
|
7571
|
+
var pluginModels$4 = [];
|
|
7548
7572
|
|
|
7549
7573
|
var pluginRoutes$4 = [];
|
|
7550
7574
|
|
|
@@ -7610,7 +7634,7 @@ class MailPlugin {
|
|
|
7610
7634
|
}
|
|
7611
7635
|
}
|
|
7612
7636
|
async configureModels(server, applicationConfig) {
|
|
7613
|
-
server.appendApplicationConfig({ models: pluginModels$
|
|
7637
|
+
server.appendApplicationConfig({ models: pluginModels$4 });
|
|
7614
7638
|
}
|
|
7615
7639
|
async configureServices(server, applicationConfig) {
|
|
7616
7640
|
this.#mailService = new MailService(server, this.#config.smtpServer);
|
|
@@ -7683,7 +7707,7 @@ var Notification = {
|
|
|
7683
7707
|
],
|
|
7684
7708
|
};
|
|
7685
7709
|
|
|
7686
|
-
var pluginModels$
|
|
7710
|
+
var pluginModels$3 = [Notification];
|
|
7687
7711
|
|
|
7688
7712
|
var pluginRoutes$3 = [];
|
|
7689
7713
|
|
|
@@ -7735,7 +7759,7 @@ class SequencePlugin {
|
|
|
7735
7759
|
}
|
|
7736
7760
|
}
|
|
7737
7761
|
async configureModels(server, applicationConfig) {
|
|
7738
|
-
server.appendApplicationConfig({ models: pluginModels$
|
|
7762
|
+
server.appendApplicationConfig({ models: pluginModels$3 });
|
|
7739
7763
|
}
|
|
7740
7764
|
async configureServices(server, applicationConfig) {
|
|
7741
7765
|
this.#notificationService = new NotificationService(server);
|
|
@@ -8253,7 +8277,7 @@ var UserSettingItemSetting = {
|
|
|
8253
8277
|
],
|
|
8254
8278
|
};
|
|
8255
8279
|
|
|
8256
|
-
var pluginModels$
|
|
8280
|
+
var pluginModels$2 = [SystemSettingGroupSetting, SystemSettingItem, SystemSettingItemSetting, UserSettingGroupSetting, UserSettingItem, UserSettingItemSetting];
|
|
8257
8281
|
|
|
8258
8282
|
var getUserSettingValues = {
|
|
8259
8283
|
namespace: "svc",
|
|
@@ -8508,7 +8532,7 @@ class SettingPlugin {
|
|
|
8508
8532
|
}
|
|
8509
8533
|
}
|
|
8510
8534
|
async configureModels(server, applicationConfig) {
|
|
8511
|
-
server.appendApplicationConfig({ models: pluginModels$
|
|
8535
|
+
server.appendApplicationConfig({ models: pluginModels$2 });
|
|
8512
8536
|
}
|
|
8513
8537
|
async configureServices(server, applicationConfig) {
|
|
8514
8538
|
this.#settingService = new SettingService(server);
|
|
@@ -8520,6 +8544,136 @@ class SettingPlugin {
|
|
|
8520
8544
|
async onApplicationLoaded(server, applicationConfig) { }
|
|
8521
8545
|
}
|
|
8522
8546
|
|
|
8547
|
+
var CronJob = {
|
|
8548
|
+
maintainedBy: "cronJob",
|
|
8549
|
+
namespace: "sys",
|
|
8550
|
+
name: "sys_cron_job",
|
|
8551
|
+
singularCode: "sys_cron_job",
|
|
8552
|
+
pluralCode: "sys_cron_jobs",
|
|
8553
|
+
schema: "public",
|
|
8554
|
+
tableName: "sys_cron_jobs",
|
|
8555
|
+
properties: [
|
|
8556
|
+
{
|
|
8557
|
+
name: "id",
|
|
8558
|
+
code: "id",
|
|
8559
|
+
columnName: "id",
|
|
8560
|
+
type: "integer",
|
|
8561
|
+
required: true,
|
|
8562
|
+
autoIncrement: true,
|
|
8563
|
+
},
|
|
8564
|
+
{
|
|
8565
|
+
name: "code",
|
|
8566
|
+
code: "code",
|
|
8567
|
+
columnName: "code",
|
|
8568
|
+
type: "text",
|
|
8569
|
+
required: true,
|
|
8570
|
+
},
|
|
8571
|
+
{
|
|
8572
|
+
name: "description",
|
|
8573
|
+
code: "description",
|
|
8574
|
+
columnName: "description",
|
|
8575
|
+
type: "text",
|
|
8576
|
+
required: false,
|
|
8577
|
+
},
|
|
8578
|
+
{
|
|
8579
|
+
name: "cronTime",
|
|
8580
|
+
code: "cronTime",
|
|
8581
|
+
columnName: "cron_time",
|
|
8582
|
+
type: "text",
|
|
8583
|
+
required: true,
|
|
8584
|
+
},
|
|
8585
|
+
{
|
|
8586
|
+
name: "disabled",
|
|
8587
|
+
code: "disabled",
|
|
8588
|
+
columnName: "disabled",
|
|
8589
|
+
type: "boolean",
|
|
8590
|
+
required: true,
|
|
8591
|
+
defaultValue: "false",
|
|
8592
|
+
},
|
|
8593
|
+
{
|
|
8594
|
+
name: "jobOptions",
|
|
8595
|
+
code: "jobOptions",
|
|
8596
|
+
columnName: "job_options",
|
|
8597
|
+
type: "json",
|
|
8598
|
+
required: false,
|
|
8599
|
+
},
|
|
8600
|
+
{
|
|
8601
|
+
name: "isRunning",
|
|
8602
|
+
code: "isRunning",
|
|
8603
|
+
columnName: "is_running",
|
|
8604
|
+
type: "boolean",
|
|
8605
|
+
required: true,
|
|
8606
|
+
defaultValue: "false",
|
|
8607
|
+
},
|
|
8608
|
+
{
|
|
8609
|
+
name: "nextRunningTime",
|
|
8610
|
+
code: "nextRunningTime",
|
|
8611
|
+
columnName: "next_running_time",
|
|
8612
|
+
type: "datetime",
|
|
8613
|
+
required: false,
|
|
8614
|
+
},
|
|
8615
|
+
{
|
|
8616
|
+
name: "lastRunningTime",
|
|
8617
|
+
code: "lastRunningTime",
|
|
8618
|
+
columnName: "last_running_time",
|
|
8619
|
+
type: "datetime",
|
|
8620
|
+
required: false,
|
|
8621
|
+
},
|
|
8622
|
+
// success, failed, error
|
|
8623
|
+
{
|
|
8624
|
+
name: "lastRunningResult",
|
|
8625
|
+
code: "lastRunningResult",
|
|
8626
|
+
columnName: "last_running_result",
|
|
8627
|
+
type: "text",
|
|
8628
|
+
required: false,
|
|
8629
|
+
},
|
|
8630
|
+
{
|
|
8631
|
+
name: "lastErrorMessage",
|
|
8632
|
+
code: "lastErrorMessage",
|
|
8633
|
+
columnName: "last_error_message",
|
|
8634
|
+
type: "text",
|
|
8635
|
+
required: false,
|
|
8636
|
+
},
|
|
8637
|
+
{
|
|
8638
|
+
name: "lastErrorStack",
|
|
8639
|
+
code: "lastErrorStack",
|
|
8640
|
+
columnName: "last_error_stack",
|
|
8641
|
+
type: "text",
|
|
8642
|
+
required: false,
|
|
8643
|
+
},
|
|
8644
|
+
{
|
|
8645
|
+
name: "actionHandlerCode",
|
|
8646
|
+
code: "actionHandlerCode",
|
|
8647
|
+
columnName: "action_handler_code",
|
|
8648
|
+
type: "text",
|
|
8649
|
+
required: false,
|
|
8650
|
+
},
|
|
8651
|
+
{
|
|
8652
|
+
name: "handler",
|
|
8653
|
+
code: "handler",
|
|
8654
|
+
columnName: "handler",
|
|
8655
|
+
type: "text",
|
|
8656
|
+
required: false,
|
|
8657
|
+
},
|
|
8658
|
+
{
|
|
8659
|
+
name: "handleOptions",
|
|
8660
|
+
code: "handleOptions",
|
|
8661
|
+
columnName: "handle_options",
|
|
8662
|
+
type: "json",
|
|
8663
|
+
required: false,
|
|
8664
|
+
},
|
|
8665
|
+
{
|
|
8666
|
+
name: "onError",
|
|
8667
|
+
code: "onError",
|
|
8668
|
+
columnName: "on_error",
|
|
8669
|
+
type: "text",
|
|
8670
|
+
required: false,
|
|
8671
|
+
},
|
|
8672
|
+
],
|
|
8673
|
+
};
|
|
8674
|
+
|
|
8675
|
+
var pluginModels$1 = [CronJob];
|
|
8676
|
+
|
|
8523
8677
|
const code$1 = "runCronJob";
|
|
8524
8678
|
async function handler$1(plugin, ctx, options) {
|
|
8525
8679
|
const { server, routerContext } = ctx;
|
|
@@ -8572,9 +8726,30 @@ var runCronJob = {
|
|
|
8572
8726
|
|
|
8573
8727
|
var pluginRoutes$1 = [runCronJob];
|
|
8574
8728
|
|
|
8729
|
+
var cronJobEntityWatchers = [
|
|
8730
|
+
{
|
|
8731
|
+
eventName: "entity.update",
|
|
8732
|
+
modelSingularCode: "sys_cron_job",
|
|
8733
|
+
handler: async (ctx) => {
|
|
8734
|
+
const { server, payload, routerContext: routeContext } = ctx;
|
|
8735
|
+
const cronJobService = server.getService("cronJobService");
|
|
8736
|
+
const changes = payload.changes;
|
|
8737
|
+
const after = payload.after;
|
|
8738
|
+
await cronJobService.updateJobConfig(routeContext, {
|
|
8739
|
+
code: after.code,
|
|
8740
|
+
cronTime: changes.cronTime,
|
|
8741
|
+
disabled: changes.disabled,
|
|
8742
|
+
jobOptions: changes.jobOptions,
|
|
8743
|
+
});
|
|
8744
|
+
},
|
|
8745
|
+
},
|
|
8746
|
+
];
|
|
8747
|
+
|
|
8748
|
+
var pluginEntityWatchers = [...cronJobEntityWatchers];
|
|
8749
|
+
|
|
8575
8750
|
class CronJobService {
|
|
8576
8751
|
#server;
|
|
8577
|
-
#
|
|
8752
|
+
#namedJobInstances;
|
|
8578
8753
|
constructor(server) {
|
|
8579
8754
|
this.#server = server;
|
|
8580
8755
|
}
|
|
@@ -8586,32 +8761,80 @@ class CronJobService {
|
|
|
8586
8761
|
getJobConfigurationByCode(code) {
|
|
8587
8762
|
return lodash.find(this.#server.listCronJobs(), (job) => job.code === code);
|
|
8588
8763
|
}
|
|
8764
|
+
#createJobInstance(job) {
|
|
8765
|
+
return cron.CronJob.from({
|
|
8766
|
+
...(job.jobOptions || {}),
|
|
8767
|
+
cronTime: job.cronTime,
|
|
8768
|
+
onTick: async () => {
|
|
8769
|
+
await this.tryExecuteJob(job);
|
|
8770
|
+
},
|
|
8771
|
+
});
|
|
8772
|
+
}
|
|
8773
|
+
async #startJobInstance(routeContext, jobConfiguration, jobInstance) {
|
|
8774
|
+
const server = this.#server;
|
|
8775
|
+
const jobCode = jobConfiguration.code;
|
|
8776
|
+
const cronJobManager = server.getEntityManager("sys_cron_job");
|
|
8777
|
+
const cronJobInDb = await cronJobManager.findEntity({
|
|
8778
|
+
routeContext,
|
|
8779
|
+
filters: [{ operator: "eq", field: "code", value: jobCode }],
|
|
8780
|
+
});
|
|
8781
|
+
if (cronJobInDb) {
|
|
8782
|
+
let nextRunningTime;
|
|
8783
|
+
nextRunningTime = formatDateTimeWithTimezone(jobInstance.nextDate().toISO());
|
|
8784
|
+
await cronJobManager.updateEntityById({
|
|
8785
|
+
routeContext,
|
|
8786
|
+
id: cronJobInDb.id,
|
|
8787
|
+
entityToSave: {
|
|
8788
|
+
nextRunningTime,
|
|
8789
|
+
},
|
|
8790
|
+
});
|
|
8791
|
+
}
|
|
8792
|
+
jobInstance.start();
|
|
8793
|
+
}
|
|
8794
|
+
async #setJobNextRunningTime(routeContext, jobCode, nextRunningTime) {
|
|
8795
|
+
const server = this.#server;
|
|
8796
|
+
const cronJobManager = server.getEntityManager("sys_cron_job");
|
|
8797
|
+
const cronJobInDb = await cronJobManager.findEntity({
|
|
8798
|
+
routeContext,
|
|
8799
|
+
filters: [{ operator: "eq", field: "code", value: jobCode }],
|
|
8800
|
+
});
|
|
8801
|
+
await cronJobManager.updateEntityById({
|
|
8802
|
+
routeContext,
|
|
8803
|
+
id: cronJobInDb.id,
|
|
8804
|
+
entityToSave: {
|
|
8805
|
+
nextRunningTime,
|
|
8806
|
+
},
|
|
8807
|
+
});
|
|
8808
|
+
}
|
|
8589
8809
|
/**
|
|
8590
8810
|
* 重新加载定时任务
|
|
8591
8811
|
*/
|
|
8592
|
-
reloadJobs() {
|
|
8812
|
+
async reloadJobs() {
|
|
8593
8813
|
const server = this.#server;
|
|
8594
|
-
|
|
8595
|
-
|
|
8814
|
+
const routeContext = RouteContext.newSystemOperationContext(server);
|
|
8815
|
+
if (this.#namedJobInstances) {
|
|
8816
|
+
for (const job of this.#namedJobInstances) {
|
|
8596
8817
|
job.instance.stop();
|
|
8597
8818
|
}
|
|
8598
8819
|
}
|
|
8599
|
-
this.#
|
|
8600
|
-
const
|
|
8601
|
-
|
|
8602
|
-
|
|
8820
|
+
this.#namedJobInstances = [];
|
|
8821
|
+
const cronJobManager = server.getEntityManager("sys_cron_job");
|
|
8822
|
+
const cronJobConfigurationsInDb = await cronJobManager.findEntities({ routeContext });
|
|
8823
|
+
const cronJobConfigurations = server.listCronJobs();
|
|
8824
|
+
for (const cronJobConfig of cronJobConfigurations) {
|
|
8825
|
+
const jobCode = cronJobConfig.code;
|
|
8826
|
+
const jobConfigInDb = lodash.find(cronJobConfigurationsInDb, { code: jobCode });
|
|
8827
|
+
if (jobConfigInDb) {
|
|
8828
|
+
overrideJobConfig(cronJobConfig, jobConfigInDb);
|
|
8829
|
+
}
|
|
8830
|
+
if (cronJobConfig.disabled) {
|
|
8831
|
+
await this.#setJobNextRunningTime(routeContext, jobCode, null);
|
|
8603
8832
|
continue;
|
|
8604
8833
|
}
|
|
8605
|
-
const jobInstance =
|
|
8606
|
-
|
|
8607
|
-
|
|
8608
|
-
|
|
8609
|
-
await this.tryExecuteJob(job);
|
|
8610
|
-
},
|
|
8611
|
-
});
|
|
8612
|
-
jobInstance.start();
|
|
8613
|
-
this.#jobInstances.push({
|
|
8614
|
-
code: job.code,
|
|
8834
|
+
const jobInstance = this.#createJobInstance(cronJobConfig);
|
|
8835
|
+
await this.#startJobInstance(routeContext, cronJobConfig, jobInstance);
|
|
8836
|
+
this.#namedJobInstances.push({
|
|
8837
|
+
code: jobCode,
|
|
8615
8838
|
instance: jobInstance,
|
|
8616
8839
|
});
|
|
8617
8840
|
}
|
|
@@ -8619,7 +8842,8 @@ class CronJobService {
|
|
|
8619
8842
|
async tryExecuteJob(job) {
|
|
8620
8843
|
const server = this.#server;
|
|
8621
8844
|
const logger = server.getLogger();
|
|
8622
|
-
|
|
8845
|
+
const jobCode = job.code;
|
|
8846
|
+
logger.info(`Executing cron job '${jobCode}'...`);
|
|
8623
8847
|
let handlerContext = {
|
|
8624
8848
|
logger,
|
|
8625
8849
|
routerContext: RouteContext.newSystemOperationContext(server),
|
|
@@ -8628,21 +8852,54 @@ class CronJobService {
|
|
|
8628
8852
|
applicationConfig: null,
|
|
8629
8853
|
input: null,
|
|
8630
8854
|
};
|
|
8855
|
+
let result;
|
|
8856
|
+
let lastErrorMessage;
|
|
8857
|
+
let lastErrorStack;
|
|
8631
8858
|
try {
|
|
8632
8859
|
await this.executeJob(handlerContext, job);
|
|
8633
|
-
|
|
8860
|
+
result = "success";
|
|
8861
|
+
logger.info(`Completed cron job '${jobCode}'...`);
|
|
8634
8862
|
}
|
|
8635
8863
|
catch (ex) {
|
|
8636
|
-
logger.error('Cron job "%s" execution error: %s',
|
|
8864
|
+
logger.error('Cron job "%s" execution error: %s', jobCode, ex.message);
|
|
8865
|
+
if (lodash.isString(ex)) {
|
|
8866
|
+
lastErrorMessage = ex;
|
|
8867
|
+
}
|
|
8868
|
+
else {
|
|
8869
|
+
lastErrorMessage = ex.message;
|
|
8870
|
+
lastErrorStack = ex.stack;
|
|
8871
|
+
}
|
|
8872
|
+
result = "failed";
|
|
8637
8873
|
if (job.onError) {
|
|
8638
8874
|
try {
|
|
8639
8875
|
await job.onError(handlerContext, ex);
|
|
8640
8876
|
}
|
|
8641
8877
|
catch (ex) {
|
|
8642
|
-
logger.error('Error handler of cron job "%s" execution failed: %s',
|
|
8878
|
+
logger.error('Error handler of cron job "%s" execution failed: %s', jobCode, ex.message);
|
|
8643
8879
|
}
|
|
8644
8880
|
}
|
|
8645
8881
|
}
|
|
8882
|
+
const cronJobManager = server.getEntityManager("sys_cron_job");
|
|
8883
|
+
const cronJobInDb = await cronJobManager.findEntity({
|
|
8884
|
+
filters: [{ operator: "eq", field: "code", value: jobCode }],
|
|
8885
|
+
});
|
|
8886
|
+
if (cronJobInDb) {
|
|
8887
|
+
let nextRunningTime;
|
|
8888
|
+
const namedJobInstance = lodash.find(this.#namedJobInstances, { code: jobCode });
|
|
8889
|
+
if (namedJobInstance && namedJobInstance.instance) {
|
|
8890
|
+
nextRunningTime = formatDateTimeWithTimezone(namedJobInstance.instance.nextDate().toISO());
|
|
8891
|
+
}
|
|
8892
|
+
await cronJobManager.updateEntityById({
|
|
8893
|
+
id: cronJobInDb.id,
|
|
8894
|
+
entityToSave: {
|
|
8895
|
+
nextRunningTime,
|
|
8896
|
+
lastRunningResult: result,
|
|
8897
|
+
lastRunningTime: getNowStringWithTimezone(),
|
|
8898
|
+
lastErrorMessage,
|
|
8899
|
+
lastErrorStack,
|
|
8900
|
+
},
|
|
8901
|
+
});
|
|
8902
|
+
}
|
|
8646
8903
|
}
|
|
8647
8904
|
/**
|
|
8648
8905
|
* 执行指定任务
|
|
@@ -8659,6 +8916,50 @@ class CronJobService {
|
|
|
8659
8916
|
await job.handler(handlerContext, job.handleOptions);
|
|
8660
8917
|
}
|
|
8661
8918
|
}
|
|
8919
|
+
async updateJobConfig(routeContext, options) {
|
|
8920
|
+
const server = this.#server;
|
|
8921
|
+
const cronJobs = server.listCronJobs();
|
|
8922
|
+
const jobCode = options.code;
|
|
8923
|
+
if (!jobCode) {
|
|
8924
|
+
throw new Error(`options.code is required.`);
|
|
8925
|
+
}
|
|
8926
|
+
const cronJobConfig = lodash.find(cronJobs, { code: jobCode });
|
|
8927
|
+
if (!cronJobConfig) {
|
|
8928
|
+
throw new Error(`Cron job with code "${jobCode}" not found.`);
|
|
8929
|
+
}
|
|
8930
|
+
if (!["cronTime", "disabled", "jobOptions"].some((field) => !lodash.isNil(options[field]))) {
|
|
8931
|
+
return;
|
|
8932
|
+
}
|
|
8933
|
+
overrideJobConfig(cronJobConfig, options);
|
|
8934
|
+
const namedJobInstance = lodash.find(this.#namedJobInstances, { code: jobCode });
|
|
8935
|
+
if (namedJobInstance && namedJobInstance.instance) {
|
|
8936
|
+
namedJobInstance.instance.stop();
|
|
8937
|
+
namedJobInstance.instance = null;
|
|
8938
|
+
}
|
|
8939
|
+
if (cronJobConfig.disabled) {
|
|
8940
|
+
await this.#setJobNextRunningTime(routeContext, jobCode, null);
|
|
8941
|
+
}
|
|
8942
|
+
else {
|
|
8943
|
+
const jobInstance = this.#createJobInstance(cronJobConfig);
|
|
8944
|
+
await this.#startJobInstance(routeContext, cronJobConfig, jobInstance);
|
|
8945
|
+
if (namedJobInstance) {
|
|
8946
|
+
namedJobInstance.instance = jobInstance;
|
|
8947
|
+
}
|
|
8948
|
+
else {
|
|
8949
|
+
this.#namedJobInstances.push({
|
|
8950
|
+
code: cronJobConfig.code,
|
|
8951
|
+
instance: jobInstance,
|
|
8952
|
+
});
|
|
8953
|
+
}
|
|
8954
|
+
}
|
|
8955
|
+
}
|
|
8956
|
+
}
|
|
8957
|
+
function overrideJobConfig(original, overrides) {
|
|
8958
|
+
["cronTime", "disabled", "jobOptions"].forEach((field) => {
|
|
8959
|
+
if (!lodash.isNil(overrides[field])) {
|
|
8960
|
+
original[field] = overrides[field];
|
|
8961
|
+
}
|
|
8962
|
+
});
|
|
8662
8963
|
}
|
|
8663
8964
|
|
|
8664
8965
|
class CronJobPlugin {
|
|
@@ -8689,11 +8990,16 @@ class CronJobPlugin {
|
|
|
8689
8990
|
server.registerActionHandler(this, actionHandler);
|
|
8690
8991
|
}
|
|
8691
8992
|
}
|
|
8692
|
-
async registerEventHandlers(server) {
|
|
8993
|
+
async registerEventHandlers(server) {
|
|
8994
|
+
for (const entityWatcher of pluginEntityWatchers)
|
|
8995
|
+
server.registerEntityWatcher(entityWatcher);
|
|
8996
|
+
}
|
|
8693
8997
|
async registerMessageHandlers(server) { }
|
|
8694
8998
|
async registerTaskProcessors(server) { }
|
|
8695
8999
|
async onLoadingApplication(server, applicationConfig) { }
|
|
8696
|
-
async configureModels(server, applicationConfig) {
|
|
9000
|
+
async configureModels(server, applicationConfig) {
|
|
9001
|
+
server.appendApplicationConfig({ models: pluginModels$1 });
|
|
9002
|
+
}
|
|
8697
9003
|
async configureModelProperties(server, applicationConfig) { }
|
|
8698
9004
|
async configureServices(server, applicationConfig) {
|
|
8699
9005
|
this.#cronJobService = new CronJobService(server);
|
|
@@ -8702,9 +9008,24 @@ class CronJobPlugin {
|
|
|
8702
9008
|
async configureRoutes(server, applicationConfig) {
|
|
8703
9009
|
server.appendApplicationConfig({ routes: pluginRoutes$1 });
|
|
8704
9010
|
}
|
|
8705
|
-
async onApplicationLoaded(server, applicationConfig) {
|
|
9011
|
+
async onApplicationLoaded(server, applicationConfig) {
|
|
9012
|
+
await saveCronJobsToDatabase(server);
|
|
9013
|
+
}
|
|
8706
9014
|
async onApplicationReady(server, applicationConfig) {
|
|
8707
|
-
this.#cronJobService.reloadJobs();
|
|
9015
|
+
await this.#cronJobService.reloadJobs();
|
|
9016
|
+
}
|
|
9017
|
+
}
|
|
9018
|
+
async function saveCronJobsToDatabase(server) {
|
|
9019
|
+
const cronJobManager = server.getEntityManager("sys_cron_job");
|
|
9020
|
+
for (const cronJobToSave of server.listCronJobs()) {
|
|
9021
|
+
const currentCronJob = await cronJobManager.findEntity({
|
|
9022
|
+
filters: [{ operator: "eq", field: "code", value: cronJobToSave.code }],
|
|
9023
|
+
});
|
|
9024
|
+
if (!currentCronJob) {
|
|
9025
|
+
await cronJobManager.createEntity({
|
|
9026
|
+
entity: lodash.pick(cronJobToSave, ["code", "description", "cronTime", "disabled"]),
|
|
9027
|
+
});
|
|
9028
|
+
}
|
|
8708
9029
|
}
|
|
8709
9030
|
}
|
|
8710
9031
|
|
|
@@ -9120,6 +9441,7 @@ exports.createJwt = createJwt;
|
|
|
9120
9441
|
exports.decodeJwt = decodeJwt;
|
|
9121
9442
|
exports.deleteCookie = deleteCookie;
|
|
9122
9443
|
exports.detectChangedFieldsOfEntity = detectChangedFieldsOfEntity;
|
|
9444
|
+
exports.formatDateTimeWithTimezone = formatDateTimeWithTimezone;
|
|
9123
9445
|
exports.generateJwtSecretKey = generateJwtSecretKey;
|
|
9124
9446
|
exports.getCookies = getCookies;
|
|
9125
9447
|
exports.getDateString = getDateString;
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { CronJob } from "cron";
|
|
2
|
+
import { CronJobConfiguration } from "../../types/cron-job-types";
|
|
2
3
|
export type RunCronJobActionHandlerOptions = {
|
|
3
4
|
code?: string;
|
|
4
5
|
};
|
|
@@ -10,3 +11,28 @@ export type NamedCronJobInstance = {
|
|
|
10
11
|
code: string;
|
|
11
12
|
instance: CronJob;
|
|
12
13
|
};
|
|
14
|
+
export type JobRunningResult = "success" | "failed" | "error";
|
|
15
|
+
export type SysCronJob = {
|
|
16
|
+
id: number;
|
|
17
|
+
code: string;
|
|
18
|
+
description: string;
|
|
19
|
+
cronTime: string;
|
|
20
|
+
disabled: boolean;
|
|
21
|
+
jobOptions: CronJobConfiguration["jobOptions"];
|
|
22
|
+
isRunning: boolean;
|
|
23
|
+
nextRunningTime: string;
|
|
24
|
+
lastRunningTime: string;
|
|
25
|
+
lastRunningResult?: JobRunningResult;
|
|
26
|
+
lastErrorMessage?: string;
|
|
27
|
+
lastErrorStack?: string;
|
|
28
|
+
actionHandlerCode?: string;
|
|
29
|
+
handler?: string;
|
|
30
|
+
handleOptions?: Record<string, any>;
|
|
31
|
+
onError?: string;
|
|
32
|
+
};
|
|
33
|
+
export type UpdateJobConfigOptions = {
|
|
34
|
+
code: string;
|
|
35
|
+
cronTime?: string;
|
|
36
|
+
disabled?: boolean;
|
|
37
|
+
jobOptions?: CronJobConfiguration["jobOptions"];
|
|
38
|
+
};
|