midway-fatcms 0.0.1-beta.13 → 0.0.1-beta.16
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/README.md +7 -0
- package/dist/configuration.d.ts +1 -0
- package/dist/configuration.js +21 -8
- package/dist/controller/base/BaseApiController.d.ts +1 -1
- package/dist/controller/gateway/AsyncTaskController.js +17 -6
- package/dist/controller/gateway/CrudMtdGatewayController.d.ts +3 -3
- package/dist/controller/gateway/CrudMtdGatewayController.js +5 -5
- package/dist/index.d.ts +7 -0
- package/dist/index.js +7 -0
- package/dist/libs/crud-pro/CrudPro.d.ts +1 -0
- package/dist/libs/crud-pro/CrudPro.js +5 -0
- package/dist/libs/crud-pro/defaultConfigs.js +2 -0
- package/dist/libs/crud-pro/interfaces.d.ts +6 -0
- package/dist/libs/crud-pro/models/ExecuteContext.d.ts +2 -1
- package/dist/libs/crud-pro/models/ExecuteContextFunc.d.ts +8 -1
- package/dist/libs/crud-pro/models/ExecuteContextFunc.js +8 -0
- package/dist/libs/crud-pro/models/ResModel.d.ts +16 -0
- package/dist/libs/crud-pro/models/ResModel.js +2 -0
- package/dist/libs/crud-pro/services/CrudProCachedCfgService.d.ts +1 -0
- package/dist/libs/crud-pro/services/CrudProCachedCfgService.js +22 -11
- package/dist/libs/crud-pro/services/CrudProServiceBase.d.ts +2 -6
- package/dist/libs/crud-pro/services/CrudProServiceBase.js +3 -2
- package/dist/libs/crud-pro/services/CrudProTableMetaService.d.ts +1 -0
- package/dist/libs/crud-pro/services/CrudProTableMetaService.js +31 -7
- package/dist/libs/crud-pro/utils/DatabaseName.js +24 -3
- package/dist/libs/global-config/global-config.d.ts +6 -0
- package/dist/libs/global-config/global-config.js +1 -0
- package/dist/models/AsyncTaskModel.d.ts +13 -11
- package/dist/models/AsyncTaskModel.js +23 -23
- package/dist/schedule/anonymousContext.d.ts +13 -0
- package/dist/schedule/anonymousContext.js +59 -0
- package/dist/schedule/index.d.ts +4 -4
- package/dist/schedule/index.js +9 -68
- package/dist/schedule/runSchedule.d.ts +6 -0
- package/dist/schedule/runSchedule.js +34 -0
- package/dist/schedule/scheduleNames.d.ts +8 -0
- package/dist/schedule/scheduleNames.js +17 -0
- package/dist/service/asyncTask/AsyncTaskRunnerService.d.ts +14 -0
- package/dist/service/asyncTask/AsyncTaskRunnerService.js +66 -16
- package/dist/service/asyncTask/AsyncTaskService.js +2 -1
- package/dist/service/crudstd/CrudStdService.d.ts +1 -0
- package/dist/service/crudstd/CrudStdService.js +27 -0
- package/dist/service/curd/CrudProQuick.d.ts +24 -0
- package/dist/service/curd/CrudProQuick.js +105 -0
- package/dist/service/curd/CurdMixByLinkToCustomService.d.ts +10 -1
- package/dist/service/curd/CurdMixByLinkToCustomService.js +71 -23
- package/dist/service/curd/CurdMixService.d.ts +1 -1
- package/dist/service/curd/CurdMixService.js +2 -2
- package/dist/service/curd/CurdMixUtils.js +35 -28
- package/dist/service/curd/CurdProService.d.ts +2 -10
- package/dist/service/curd/CurdProService.js +28 -143
- package/dist/service/curd/fixCfgModel.d.ts +3 -0
- package/dist/service/curd/fixCfgModel.js +107 -0
- package/package.json +1 -1
- package/src/configuration.ts +30 -10
- package/src/controller/gateway/AnyApiGatewayController.ts +1 -1
- package/src/controller/gateway/AsyncTaskController.ts +21 -8
- package/src/controller/gateway/CrudMtdGatewayController.ts +5 -5
- package/src/controller/manage/DocManageApi.ts +5 -5
- package/src/index.ts +7 -0
- package/src/libs/crud-pro/CrudPro.ts +8 -0
- package/src/libs/crud-pro/defaultConfigs.ts +2 -0
- package/src/libs/crud-pro/interfaces.ts +10 -1
- package/src/libs/crud-pro/models/ExecuteContext.ts +5 -2
- package/src/libs/crud-pro/models/ExecuteContextFunc.ts +12 -1
- package/src/libs/crud-pro/models/ResModel.ts +24 -0
- package/src/libs/crud-pro/services/CrudProCachedCfgService.ts +26 -17
- package/src/libs/crud-pro/services/CrudProServiceBase.ts +5 -8
- package/src/libs/crud-pro/services/CrudProTableMetaService.ts +43 -9
- package/src/libs/crud-pro/utils/DatabaseName.ts +35 -15
- package/src/libs/global-config/global-config.ts +12 -1
- package/src/models/AsyncTaskModel.ts +14 -11
- package/src/schedule/anonymousContext.ts +79 -0
- package/src/schedule/index.ts +13 -72
- package/src/schedule/runSchedule.ts +35 -0
- package/src/schedule/scheduleNames.ts +20 -0
- package/src/service/asyncTask/AsyncTaskRunnerService.ts +91 -24
- package/src/service/asyncTask/AsyncTaskService.ts +3 -2
- package/src/service/base/BaseService.ts +2 -2
- package/src/service/crudstd/CrudStdService.ts +34 -6
- package/src/service/curd/CrudProQuick.ts +137 -0
- package/src/service/curd/CurdMixByLinkToCustomService.ts +100 -49
- package/src/service/curd/CurdMixService.ts +3 -3
- package/src/service/curd/CurdMixUtils.ts +51 -38
- package/src/service/curd/CurdProService.ts +42 -186
- package/src/service/curd/fixCfgModel.ts +139 -0
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import { CrudProServiceBase } from './CrudProServiceBase';
|
|
2
2
|
import { SqlCfgModel } from '../models/SqlCfgModel';
|
|
3
|
-
import { ITableMeta } from '../interfaces';
|
|
3
|
+
import { IExecuteUnsafeQueryCtx, ITableMeta } from '../interfaces';
|
|
4
|
+
import { SqlDbType } from '../models/keys';
|
|
5
|
+
import { pickAndConvertPgRows } from '../utils/sqlConvert/convertPgType';
|
|
4
6
|
|
|
5
7
|
class CrudProTableMetaCache {
|
|
6
8
|
private cacheMap: Record<string, ITableMeta> = {};
|
|
@@ -42,18 +44,50 @@ class CrudProTableMetaService extends CrudProServiceBase {
|
|
|
42
44
|
tableColumns: [],
|
|
43
45
|
};
|
|
44
46
|
|
|
45
|
-
const baseInfo = {
|
|
46
|
-
|
|
47
|
-
|
|
47
|
+
const baseInfo: IExecuteUnsafeQueryCtx = {
|
|
48
|
+
sqlTable: sqlCfgModel.sqlTable,
|
|
49
|
+
sqlDatabase: sqlCfgModel.sqlDatabase,
|
|
50
|
+
sqlDbType: sqlCfgModel.sqlDbType
|
|
48
51
|
};
|
|
49
52
|
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
obj.tableColumns = tableDescribe2.map(fieldObj => {
|
|
53
|
-
return fieldObj['Field'];
|
|
54
|
-
});
|
|
53
|
+
obj.tableColumns = await this.loadTableColumnInfo(baseInfo);
|
|
54
|
+
|
|
55
55
|
return obj;
|
|
56
56
|
}
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
private async loadTableColumnInfo(baseInfo: IExecuteUnsafeQueryCtx): Promise<string[]>{
|
|
61
|
+
|
|
62
|
+
if(baseInfo.sqlDbType === SqlDbType.mysql) {
|
|
63
|
+
const [tableDescribe] = await this.executeUnsafeQuery(baseInfo, 'describe ' + baseInfo.sqlTable);
|
|
64
|
+
const tableDescribe2 = JSON.parse(JSON.stringify(tableDescribe));
|
|
65
|
+
return tableDescribe2.map(fieldObj => {
|
|
66
|
+
return fieldObj['Field'];
|
|
67
|
+
});
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
else if(baseInfo.sqlDbType === SqlDbType.postgres) {
|
|
71
|
+
const schemaname = 'public';
|
|
72
|
+
const columnArraySql = `
|
|
73
|
+
SELECT
|
|
74
|
+
*
|
|
75
|
+
FROM information_schema.columns
|
|
76
|
+
WHERE table_schema = '${schemaname}' and table_name = '${baseInfo.sqlTable}'
|
|
77
|
+
ORDER BY ordinal_position;
|
|
78
|
+
`.trim();
|
|
79
|
+
const queryRes = await this.executeUnsafeQuery(baseInfo, columnArraySql);
|
|
80
|
+
const rows = pickAndConvertPgRows(queryRes)
|
|
81
|
+
return rows.map(fieldObj => {
|
|
82
|
+
return fieldObj['column_name'];
|
|
83
|
+
});
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
throw new Error("暂不支持的数据库类型:" + baseInfo.sqlDbType)
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
|
|
57
91
|
}
|
|
58
92
|
|
|
59
93
|
export { CrudProTableMetaService };
|
|
@@ -1,19 +1,45 @@
|
|
|
1
|
-
import { SqlDbType } from '../models/keys'
|
|
1
|
+
import { SqlDbType } from '../models/keys';
|
|
2
2
|
|
|
3
3
|
interface IDbTypeAndName {
|
|
4
|
-
dbType: SqlDbType
|
|
4
|
+
dbType: SqlDbType;
|
|
5
5
|
dbName: string;
|
|
6
6
|
}
|
|
7
7
|
|
|
8
|
-
const SPLIT_CONST =
|
|
8
|
+
const SPLIT_CONST = '_________';
|
|
9
|
+
|
|
10
|
+
const PREFIX_MYSQL = `${SqlDbType.mysql}${SPLIT_CONST}`;
|
|
11
|
+
const PREFIX_POSTGRES = `${SqlDbType.postgres}${SPLIT_CONST}`;
|
|
12
|
+
const PREFIX_SQLSERVER = `${SqlDbType.sqlserver}${SPLIT_CONST}`;
|
|
9
13
|
|
|
10
14
|
function parseDatabaseName(databaseName: string): IDbTypeAndName {
|
|
11
15
|
const dbNameArr = databaseName.split(SPLIT_CONST);
|
|
16
|
+
|
|
17
|
+
if (databaseName.startsWith(PREFIX_MYSQL)) {
|
|
18
|
+
return {
|
|
19
|
+
dbType: SqlDbType.mysql,
|
|
20
|
+
dbName: dbNameArr[1],
|
|
21
|
+
};
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
if (databaseName.startsWith(PREFIX_POSTGRES)) {
|
|
25
|
+
return {
|
|
26
|
+
dbType: SqlDbType.postgres,
|
|
27
|
+
dbName: dbNameArr[1],
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
if (databaseName.startsWith(PREFIX_SQLSERVER)) {
|
|
32
|
+
return {
|
|
33
|
+
dbType: SqlDbType.sqlserver,
|
|
34
|
+
dbName: dbNameArr[1],
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
|
|
12
38
|
if (dbNameArr.length === 1) {
|
|
13
39
|
return {
|
|
14
40
|
dbType: SqlDbType.mysql, // 默认
|
|
15
|
-
dbName: dbNameArr[0]
|
|
16
|
-
}
|
|
41
|
+
dbName: dbNameArr[0],
|
|
42
|
+
};
|
|
17
43
|
}
|
|
18
44
|
|
|
19
45
|
if (dbNameArr.length > 1) {
|
|
@@ -21,20 +47,14 @@ function parseDatabaseName(databaseName: string): IDbTypeAndName {
|
|
|
21
47
|
const dbType = dbType0 as any;
|
|
22
48
|
return {
|
|
23
49
|
dbType,
|
|
24
|
-
dbName
|
|
25
|
-
}
|
|
50
|
+
dbName,
|
|
51
|
+
};
|
|
26
52
|
}
|
|
27
53
|
return null;
|
|
28
54
|
}
|
|
29
55
|
|
|
30
|
-
|
|
31
56
|
function toDatabaseNameStr(dbType: SqlDbType, dbName: string): string {
|
|
32
|
-
return [dbType, dbName].join(SPLIT_CONST)
|
|
57
|
+
return [dbType, dbName].join(SPLIT_CONST);
|
|
33
58
|
}
|
|
34
59
|
|
|
35
|
-
|
|
36
|
-
export {
|
|
37
|
-
SPLIT_CONST,
|
|
38
|
-
parseDatabaseName,
|
|
39
|
-
toDatabaseNameStr
|
|
40
|
-
}
|
|
60
|
+
export { SPLIT_CONST, parseDatabaseName, toDatabaseNameStr };
|
|
@@ -20,9 +20,19 @@ interface IGlobalStaticConfig {
|
|
|
20
20
|
bizUpdateCfgModelForCrudMtd(reqJson: any, cfgModel: any, methodInfo: any, ctx: any): Promise<any>;
|
|
21
21
|
|
|
22
22
|
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* CrudPro: 业务系统自定义的修改cfgModel。这是最底层的!!全局的!!全局的!!全局的!!!
|
|
26
|
+
* @param reqJson
|
|
27
|
+
* @param cfgModel
|
|
28
|
+
*/
|
|
29
|
+
bizUpdateCfgModelForCrudPro(reqJson: any, cfgModel: any): Promise<any>;
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
|
|
23
33
|
/**
|
|
24
34
|
* 生成用户的账号ID,业务系统可以生成类似工号的ID
|
|
25
|
-
* @param userSubmitData
|
|
35
|
+
* @param userSubmitData
|
|
26
36
|
*/
|
|
27
37
|
generateUserAccountId(userSubmitData: any): Promise<string>;
|
|
28
38
|
|
|
@@ -47,6 +57,7 @@ class GlobalStaticConfig {
|
|
|
47
57
|
this.configObject = {
|
|
48
58
|
bizUpdateCfgModelForCrudStd: noop,
|
|
49
59
|
bizUpdateCfgModelForCrudMtd: noop,
|
|
60
|
+
bizUpdateCfgModelForCrudPro: noop,
|
|
50
61
|
generateUserAccountId: null,
|
|
51
62
|
toFatcmsUserAccountId: (bizTableUserId: any) => {
|
|
52
63
|
return bizTableUserId;
|
|
@@ -4,16 +4,16 @@ export interface SysAsyncTaskEntity {
|
|
|
4
4
|
task_name: string;
|
|
5
5
|
task_description: string | null;
|
|
6
6
|
task_type: string;
|
|
7
|
-
task_status:
|
|
7
|
+
task_status: SysAsyncTaskStatus;
|
|
8
8
|
input_params: any;
|
|
9
9
|
output_result: any | null;
|
|
10
10
|
|
|
11
11
|
input_file_path: string | null;
|
|
12
|
-
input_file_format:
|
|
12
|
+
input_file_format: SysAsyncFileFormat | null;
|
|
13
13
|
input_file_size: number | null;
|
|
14
14
|
|
|
15
15
|
output_file_path: string | null;
|
|
16
|
-
output_file_format:
|
|
16
|
+
output_file_format: SysAsyncFileFormat | null;
|
|
17
17
|
output_file_size: number | null;
|
|
18
18
|
|
|
19
19
|
input_total_records: number | null;
|
|
@@ -26,15 +26,17 @@ export interface SysAsyncTaskEntity {
|
|
|
26
26
|
priority: number;
|
|
27
27
|
retry_count: number;
|
|
28
28
|
max_retries: number;
|
|
29
|
+
|
|
29
30
|
parent_task_id: number | null;
|
|
30
31
|
created_at: Date;
|
|
31
32
|
started_at: Date | null;
|
|
32
|
-
completed_at: Date | null;
|
|
33
33
|
updated_at: Date;
|
|
34
34
|
expired_at: Date | null;
|
|
35
|
+
completed_at: Date | null;
|
|
36
|
+
created_user_session: string;
|
|
35
37
|
}
|
|
36
38
|
|
|
37
|
-
export enum
|
|
39
|
+
export enum SysAsyncTaskStatus {
|
|
38
40
|
PENDING = 'PENDING', // 等待中,还未进入排队
|
|
39
41
|
RUNNING = 'RUNNING', // 运行中
|
|
40
42
|
SUCCEEDED = 'SUCCEEDED',
|
|
@@ -43,7 +45,7 @@ export enum TaskStatus {
|
|
|
43
45
|
PAUSED = 'PAUSED', // 暂停
|
|
44
46
|
}
|
|
45
47
|
|
|
46
|
-
export enum
|
|
48
|
+
export enum SysAsyncFileFormat {
|
|
47
49
|
XLSX = 'xlsx',
|
|
48
50
|
CSV = 'csv',
|
|
49
51
|
JSON = 'json',
|
|
@@ -57,17 +59,18 @@ export enum FileFormat {
|
|
|
57
59
|
}
|
|
58
60
|
|
|
59
61
|
|
|
60
|
-
export interface
|
|
61
|
-
execute(
|
|
62
|
+
export interface ISysAsyncTaskHandler {
|
|
63
|
+
execute(asyncTaskContext: SysAsyncTaskContext): Promise<void>;
|
|
62
64
|
}
|
|
63
65
|
|
|
64
|
-
export interface
|
|
66
|
+
export interface SysAsyncTaskContext {
|
|
65
67
|
task: SysAsyncTaskEntity;
|
|
68
|
+
updateTaskStatus: ()=>any;
|
|
66
69
|
}
|
|
67
70
|
|
|
68
|
-
export interface
|
|
71
|
+
export interface SysAsyncTaskHandlerConfig {
|
|
69
72
|
taskType: string;
|
|
70
|
-
handler:
|
|
73
|
+
handler: ISysAsyncTaskHandler;
|
|
71
74
|
maxRetries?: number;
|
|
72
75
|
priority?: number;
|
|
73
76
|
expireSeconds?: number;
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
import * as koa from '@midwayjs/koa';
|
|
2
|
+
import {ContextLogger} from "@/models/contextLogger";
|
|
3
|
+
import {Transaction} from "@/libs/crud-pro/models/Transaction";
|
|
4
|
+
import {ISessionInfo, SYS_ACCOUNT_TYPE, UserSessionInfo} from "@/models/userSession";
|
|
5
|
+
import {IWorkbenchEntity} from "@/models/SystemEntities";
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
const anonymousUserInfo:ISessionInfo = {
|
|
9
|
+
nickName: 'anonymous_user',
|
|
10
|
+
avatar: '',
|
|
11
|
+
roleCodes: [],
|
|
12
|
+
functionCodes: [],
|
|
13
|
+
loginName: 'anonymous_user',
|
|
14
|
+
sessionId: '',
|
|
15
|
+
accountId: '',
|
|
16
|
+
workbenchCode: '',
|
|
17
|
+
accountType: SYS_ACCOUNT_TYPE,
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
const anonymousWorkbenchInfo: IWorkbenchEntity = {
|
|
21
|
+
id: 0,
|
|
22
|
+
workbench_code: 'anonymous_workbench',
|
|
23
|
+
workbench_name: 'anonymous_workbench',
|
|
24
|
+
workbench_domain: 'anonymous_workbench',
|
|
25
|
+
workbench_desc: 'anonymous_workbench',
|
|
26
|
+
html_content: 'anonymous_workbench',
|
|
27
|
+
status: 1,
|
|
28
|
+
workbench_type: 0,
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
export type RunServiceAtAnonymousContextRunner = (ctx: koa.IMidwayKoaContext)=>any;
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
class AnonymousContext {
|
|
36
|
+
private app: koa.Application;
|
|
37
|
+
|
|
38
|
+
setApp(app: koa.Application){
|
|
39
|
+
this.app = app;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
getApp(){
|
|
43
|
+
return this.app;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
async runServiceAtAnonymousContext( runner: RunServiceAtAnonymousContextRunner) {
|
|
48
|
+
const app = this.app;
|
|
49
|
+
const ctx: koa.IMidwayKoaContext = app.createAnonymousContext();
|
|
50
|
+
|
|
51
|
+
ctx.app = app;
|
|
52
|
+
ctx.contextLogger = new ContextLogger(ctx as any);
|
|
53
|
+
ctx.transaction = new Transaction();
|
|
54
|
+
ctx.userSession = new UserSessionInfo(anonymousUserInfo, true);
|
|
55
|
+
ctx.workbenchInfo = anonymousWorkbenchInfo;
|
|
56
|
+
|
|
57
|
+
let result: any = null;
|
|
58
|
+
let error: any = null;
|
|
59
|
+
try {
|
|
60
|
+
result = await runner(ctx);
|
|
61
|
+
} catch (e) {
|
|
62
|
+
error = e;
|
|
63
|
+
ctx.logger.error(`runServiceAtAnonymousContext error `, e);
|
|
64
|
+
} finally {
|
|
65
|
+
await ctx.transaction.releaseTx();
|
|
66
|
+
}
|
|
67
|
+
return {result: result, error: error};
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
const ANONYMOUS_CONTEXT = new AnonymousContext()
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
export {
|
|
78
|
+
ANONYMOUS_CONTEXT
|
|
79
|
+
}
|
package/src/schedule/index.ts
CHANGED
|
@@ -1,73 +1,14 @@
|
|
|
1
|
-
import
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
sessionId: '',
|
|
15
|
-
accountId: '',
|
|
16
|
-
workbenchCode: '',
|
|
17
|
-
accountType: SYS_ACCOUNT_TYPE,
|
|
18
|
-
};
|
|
19
|
-
|
|
20
|
-
const scheduleWorkbenchInfo: IWorkbenchEntity = {
|
|
21
|
-
id: 0,
|
|
22
|
-
workbench_code: 'schedule_workbench',
|
|
23
|
-
workbench_name: 'schedule_workbench',
|
|
24
|
-
workbench_domain: 'schedule_workbench',
|
|
25
|
-
workbench_desc: 'schedule_workbench',
|
|
26
|
-
html_content: 'schedule_workbench',
|
|
27
|
-
status: 1,
|
|
28
|
-
workbench_type: 0,
|
|
29
|
-
};
|
|
30
|
-
|
|
31
|
-
async function runScheduleService(app: koa.Application, serviceName: string) {
|
|
32
|
-
const ctx = app.createAnonymousContext();
|
|
33
|
-
|
|
34
|
-
ctx.app = app;
|
|
35
|
-
ctx.contextLogger = new ContextLogger(ctx as any);
|
|
36
|
-
ctx.transaction = new Transaction();
|
|
37
|
-
ctx.userSession = new UserSessionInfo(scheduleUserInfo, true);
|
|
38
|
-
ctx.workbenchInfo = scheduleWorkbenchInfo;
|
|
39
|
-
|
|
40
|
-
try {
|
|
41
|
-
const serviceObject: IScheduleService = await ctx.requestContext.getAsync(serviceName);
|
|
42
|
-
if (serviceObject && serviceObject.runBySchedule) {
|
|
43
|
-
await serviceObject.runBySchedule();
|
|
44
|
-
} else {
|
|
45
|
-
ctx.logger.error(`runSchedule error , serviceName = ${serviceName}, 没有找到服务`);
|
|
46
|
-
}
|
|
47
|
-
} catch (e) {
|
|
48
|
-
ctx.logger.error(`runSchedule error , serviceName = ${serviceName}`, e);
|
|
49
|
-
} finally {
|
|
50
|
-
await ctx.transaction.releaseTx();
|
|
51
|
-
}
|
|
1
|
+
import {startScheduleLoop, runScheduleTaskOnce} from './runSchedule'
|
|
2
|
+
import {ANONYMOUS_CONTEXT} from './anonymousContext'
|
|
3
|
+
import {
|
|
4
|
+
ALL_SCHEDULE_NAMES,
|
|
5
|
+
SCHEDULE_NAMES,
|
|
6
|
+
} from './scheduleNames'
|
|
7
|
+
|
|
8
|
+
export {
|
|
9
|
+
ALL_SCHEDULE_NAMES,
|
|
10
|
+
SCHEDULE_NAMES,
|
|
11
|
+
ANONYMOUS_CONTEXT,
|
|
12
|
+
startScheduleLoop,
|
|
13
|
+
runScheduleTaskOnce
|
|
52
14
|
}
|
|
53
|
-
|
|
54
|
-
async function runSchedule(app: koa.Application, serviceList: string[]) {
|
|
55
|
-
for (let i = 0; i < serviceList.length; i++) {
|
|
56
|
-
const serviceName = serviceList[i];
|
|
57
|
-
try {
|
|
58
|
-
await runScheduleService(app, serviceName);
|
|
59
|
-
} catch (e) {
|
|
60
|
-
const ctx = app.createAnonymousContext();
|
|
61
|
-
ctx.logger.error(`runSchedule error , serviceName = ${serviceName}`, e);
|
|
62
|
-
}
|
|
63
|
-
}
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
async function startSchedule(app: koa.Application, serviceList: string[]) {
|
|
67
|
-
await runSchedule(app, serviceList);
|
|
68
|
-
setInterval(() => {
|
|
69
|
-
runSchedule(app, serviceList);
|
|
70
|
-
}, 2 * 60 * 1000);
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
export { startSchedule, runSchedule };
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import * as koa from '@midwayjs/koa';
|
|
2
|
+
import { IScheduleService } from '@/interface';
|
|
3
|
+
import { ANONYMOUS_CONTEXT } from "./anonymousContext";
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
async function runScheduleTaskOnce(serviceName: string) {
|
|
7
|
+
return await ANONYMOUS_CONTEXT.runServiceAtAnonymousContext(async (ctx: koa.IMidwayKoaContext)=>{
|
|
8
|
+
const serviceObject: IScheduleService = await ctx.requestContext.getAsync(serviceName);
|
|
9
|
+
if (serviceObject && serviceObject.runBySchedule) {
|
|
10
|
+
await serviceObject.runBySchedule();
|
|
11
|
+
} else {
|
|
12
|
+
ctx.logger.error(`runSchedule error , serviceName = ${serviceName}, 没有找到服务`);
|
|
13
|
+
}
|
|
14
|
+
});
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
async function runScheduleServiceList(serviceList: string[]) {
|
|
18
|
+
for (let i = 0; i < serviceList.length; i++) {
|
|
19
|
+
const serviceName = serviceList[i];
|
|
20
|
+
try {
|
|
21
|
+
await runScheduleTaskOnce(serviceName);
|
|
22
|
+
} catch (e) {
|
|
23
|
+
ANONYMOUS_CONTEXT.getApp().getCoreLogger().error(`runSchedule error , serviceName = ${serviceName}`, e);
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
async function startScheduleLoop(serviceList: string[]) {
|
|
29
|
+
await runScheduleServiceList(serviceList);
|
|
30
|
+
setInterval(() => {
|
|
31
|
+
runScheduleServiceList(serviceList);
|
|
32
|
+
}, 2 * 60 * 1000);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export { startScheduleLoop, runScheduleTaskOnce };
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
|
|
2
|
+
|
|
3
|
+
const SCHEDULE_NAMES = {
|
|
4
|
+
proxyApiLoadService: 'proxyApiLoadService',
|
|
5
|
+
workbenchService: 'workbenchService',
|
|
6
|
+
visitStatService: 'visitStatService',
|
|
7
|
+
asyncTaskRunnerService: 'asyncTaskRunnerService',
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
const ALL_SCHEDULE_NAMES = [
|
|
11
|
+
SCHEDULE_NAMES.proxyApiLoadService,
|
|
12
|
+
SCHEDULE_NAMES.workbenchService,
|
|
13
|
+
SCHEDULE_NAMES.visitStatService,
|
|
14
|
+
SCHEDULE_NAMES.asyncTaskRunnerService
|
|
15
|
+
];
|
|
16
|
+
|
|
17
|
+
export {
|
|
18
|
+
ALL_SCHEDULE_NAMES,
|
|
19
|
+
SCHEDULE_NAMES,
|
|
20
|
+
}
|
|
@@ -1,36 +1,47 @@
|
|
|
1
1
|
import {Inject, Provide} from '@midwayjs/core';
|
|
2
|
-
import {Context} from '@midwayjs/koa';
|
|
2
|
+
import {Context, IMidwayKoaContext} from '@midwayjs/koa';
|
|
3
3
|
import {BaseService} from "@/service/base/BaseService";
|
|
4
4
|
import {IScheduleService} from "@/interface";
|
|
5
5
|
import {CurdProService} from "@/service/curd/CurdProService";
|
|
6
|
-
import {
|
|
6
|
+
import {ISysAsyncTaskHandler, SysAsyncTaskEntity, SysAsyncTaskStatus} from "@/models/AsyncTaskModel";
|
|
7
7
|
import {GLOBAL_STATIC_CONFIG} from "@/libs/global-config/global-config";
|
|
8
8
|
import {SystemTables} from "@/models/SystemTables";
|
|
9
9
|
import {KeysOfSimpleSQL} from "@/libs/crud-pro/models/keys";
|
|
10
10
|
import {errorToString} from "@/libs/utils/errorToString";
|
|
11
|
+
import {ANONYMOUS_CONTEXT} from "@/schedule";
|
|
11
12
|
|
|
12
13
|
|
|
13
|
-
class AsyncTaskRunner
|
|
14
|
+
class AsyncTaskRunner {
|
|
14
15
|
isBusy: boolean = false;
|
|
15
|
-
taskHandlerMap: Map<string,
|
|
16
|
+
taskHandlerMap: Map<string, ISysAsyncTaskHandler> = new Map();
|
|
16
17
|
|
|
17
18
|
async executeTaskList(taskList: SysAsyncTaskEntity[]) {
|
|
18
19
|
if (!taskList || taskList.length === 0) {
|
|
19
20
|
return;
|
|
20
21
|
}
|
|
22
|
+
|
|
21
23
|
this.isBusy = true;
|
|
22
24
|
|
|
23
25
|
for (let i = 0; i < taskList.length; i++) {
|
|
24
26
|
const taskElement = taskList[i];
|
|
27
|
+
|
|
25
28
|
try {
|
|
29
|
+
// 任务状态的设置在自定义的执行器里面。
|
|
26
30
|
await this.executeTask(taskElement);
|
|
27
|
-
taskElement.task_status = TaskStatus.SUCCEEDED;
|
|
28
31
|
} catch (error) {
|
|
29
|
-
taskElement.task_status =
|
|
32
|
+
taskElement.task_status = SysAsyncTaskStatus.FAILED;
|
|
30
33
|
taskElement.error_message = errorToString(error)
|
|
31
34
|
}
|
|
35
|
+
|
|
36
|
+
try {
|
|
37
|
+
await this.updateTaskStatus(taskElement);
|
|
38
|
+
} catch (error) {
|
|
39
|
+
ANONYMOUS_CONTEXT.getApp().getCoreLogger().error("executeTaskList error", error);
|
|
40
|
+
}
|
|
32
41
|
}
|
|
33
42
|
|
|
43
|
+
this.isBusy = false;
|
|
44
|
+
|
|
34
45
|
}
|
|
35
46
|
|
|
36
47
|
private async executeTask(taskElement: SysAsyncTaskEntity) {
|
|
@@ -39,18 +50,52 @@ class AsyncTaskRunner {
|
|
|
39
50
|
if (!taskHandler) {
|
|
40
51
|
throw new Error('TaskHandler not found , taskType = ' + taskType);
|
|
41
52
|
}
|
|
42
|
-
|
|
53
|
+
const updateTaskStatus = () => {
|
|
54
|
+
return this.updateTaskStatus(taskElement);
|
|
55
|
+
}
|
|
56
|
+
await taskHandler.execute({task: taskElement, updateTaskStatus});
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
private async updateTaskStatus(taskElement: SysAsyncTaskEntity) {
|
|
61
|
+
return await ANONYMOUS_CONTEXT.runServiceAtAnonymousContext(async (ctx: IMidwayKoaContext) => {
|
|
62
|
+
const curdProService: CurdProService = await ctx.requestContext.getAsync("curdProService");
|
|
63
|
+
const {SystemDbName, SystemDbType} = GLOBAL_STATIC_CONFIG.getConfig();
|
|
64
|
+
|
|
65
|
+
const {id, ...otherTaskProps} = taskElement;
|
|
66
|
+
|
|
67
|
+
const res = await curdProService.executeCrudByCfg({
|
|
68
|
+
condition: {
|
|
69
|
+
id: id
|
|
70
|
+
},
|
|
71
|
+
data: otherTaskProps
|
|
72
|
+
}, {
|
|
73
|
+
sqlTable: SystemTables.sys_async_tasks,
|
|
74
|
+
sqlSimpleName: KeysOfSimpleSQL.SIMPLE_UPDATE,
|
|
75
|
+
sqlDatabase: SystemDbName,
|
|
76
|
+
sqlDbType: SystemDbType,
|
|
77
|
+
});
|
|
78
|
+
return res.getResModel().affected;
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
|
|
43
82
|
}
|
|
44
83
|
}
|
|
45
84
|
|
|
46
85
|
|
|
47
|
-
|
|
86
|
+
/**
|
|
87
|
+
* 业务可以扩展
|
|
88
|
+
*/
|
|
89
|
+
export const ASYNC_TASK_RUNNER = new AsyncTaskRunner();
|
|
48
90
|
|
|
49
91
|
|
|
92
|
+
/**
|
|
93
|
+
* Redis锁
|
|
94
|
+
*/
|
|
50
95
|
const LOCK_KEY_ASYNC_TASK_RUNNER = "LOCK_KEY_ASYNC_TASK_RUNNER"
|
|
51
96
|
|
|
52
97
|
@Provide()
|
|
53
|
-
export class AsyncTaskRunnerService extends BaseService implements IScheduleService{
|
|
98
|
+
export class AsyncTaskRunnerService extends BaseService implements IScheduleService {
|
|
54
99
|
|
|
55
100
|
@Inject()
|
|
56
101
|
protected ctx: Context;
|
|
@@ -58,31 +103,25 @@ export class AsyncTaskRunnerService extends BaseService implements IScheduleServ
|
|
|
58
103
|
@Inject()
|
|
59
104
|
private curdProService: CurdProService;
|
|
60
105
|
|
|
61
|
-
async runBySchedule () {
|
|
62
|
-
if (ASYNC_TASK_RUNNER.isBusy) {
|
|
63
|
-
return Promise.resolve();
|
|
64
|
-
}
|
|
65
106
|
|
|
66
|
-
|
|
67
|
-
if (lock !== 'OK') {
|
|
68
|
-
return Promise.resolve();
|
|
69
|
-
}
|
|
107
|
+
async fetchPendingTasks(): Promise<void> {
|
|
70
108
|
|
|
71
|
-
|
|
72
|
-
const { SystemDbName, SystemDbType } = GLOBAL_STATIC_CONFIG.getConfig();
|
|
109
|
+
const {SystemDbName, SystemDbType} = GLOBAL_STATIC_CONFIG.getConfig();
|
|
73
110
|
|
|
74
111
|
const queryRes = await this.curdProService.executeCrudByCfg({
|
|
75
112
|
condition: {
|
|
76
|
-
task_status:
|
|
113
|
+
task_status: SysAsyncTaskStatus.PENDING
|
|
77
114
|
},
|
|
115
|
+
orderBy: 'id+',
|
|
78
116
|
limit: 10
|
|
79
|
-
},{
|
|
117
|
+
}, {
|
|
80
118
|
sqlTable: SystemTables.sys_async_tasks,
|
|
81
119
|
sqlSimpleName: KeysOfSimpleSQL.SIMPLE_QUERY_PAGE,
|
|
82
120
|
sqlDatabase: SystemDbName,
|
|
83
121
|
sqlDbType: SystemDbType,
|
|
84
122
|
});
|
|
85
123
|
|
|
124
|
+
|
|
86
125
|
const taskList = queryRes.getResRows();
|
|
87
126
|
|
|
88
127
|
if (taskList.length === 0) {
|
|
@@ -98,9 +137,9 @@ export class AsyncTaskRunnerService extends BaseService implements IScheduleServ
|
|
|
98
137
|
}
|
|
99
138
|
},
|
|
100
139
|
data: {
|
|
101
|
-
task_status:
|
|
140
|
+
task_status: SysAsyncTaskStatus.RUNNING
|
|
102
141
|
}
|
|
103
|
-
},{
|
|
142
|
+
}, {
|
|
104
143
|
sqlTable: SystemTables.sys_async_tasks,
|
|
105
144
|
sqlSimpleName: KeysOfSimpleSQL.SIMPLE_UPDATE,
|
|
106
145
|
sqlDatabase: SystemDbName,
|
|
@@ -108,7 +147,35 @@ export class AsyncTaskRunnerService extends BaseService implements IScheduleServ
|
|
|
108
147
|
});
|
|
109
148
|
|
|
110
149
|
|
|
111
|
-
ASYNC_TASK_RUNNER.executeTaskList(taskList)
|
|
150
|
+
ASYNC_TASK_RUNNER.executeTaskList(taskList).then(() => {
|
|
151
|
+
console.log("ASYNC_TASK_RUNNER finished taskIds ==> " + JSON.stringify(taskIds))
|
|
152
|
+
})
|
|
153
|
+
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
|
|
157
|
+
async runBySchedule() {
|
|
158
|
+
if (ASYNC_TASK_RUNNER.isBusy) {
|
|
159
|
+
return Promise.resolve();
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
// 这里的过期时间1分钟即可。fetchPendingTasks函数不可能超过一分钟。
|
|
163
|
+
// 因为这里只是从数据库中获取一批任务,放到自己的任务队列里。还没触发执行。
|
|
164
|
+
const lock = await this.redisService.set(LOCK_KEY_ASYNC_TASK_RUNNER, 1, "EX", 60, "NX");
|
|
165
|
+
if (lock !== 'OK') {
|
|
166
|
+
return Promise.resolve();
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
try {
|
|
170
|
+
await this.fetchPendingTasks();
|
|
171
|
+
} catch (e) {
|
|
172
|
+
console.error("fetchPendingTasks error", errorToString(e));
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
await this.redisService.del(LOCK_KEY_ASYNC_TASK_RUNNER);
|
|
176
|
+
|
|
112
177
|
return Promise.resolve();
|
|
113
178
|
}
|
|
179
|
+
|
|
180
|
+
|
|
114
181
|
}
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import {Inject, Provide} from '@midwayjs/core';
|
|
2
2
|
import {Context} from '@midwayjs/koa';
|
|
3
3
|
import {BaseService} from "@/service/base/BaseService";
|
|
4
|
-
import {
|
|
4
|
+
import {runScheduleTaskOnce} from "@/schedule";
|
|
5
|
+
import {SCHEDULE_NAMES} from "@/schedule/scheduleNames";
|
|
5
6
|
|
|
6
7
|
@Provide()
|
|
7
8
|
export class AsyncTaskService extends BaseService {
|
|
@@ -9,7 +10,7 @@ export class AsyncTaskService extends BaseService {
|
|
|
9
10
|
protected ctx: Context;
|
|
10
11
|
|
|
11
12
|
async startTask() {
|
|
12
|
-
|
|
13
|
+
runScheduleTaskOnce(SCHEDULE_NAMES.asyncTaskRunnerService).then(schedule => {
|
|
13
14
|
console.log(schedule);
|
|
14
15
|
})
|
|
15
16
|
}
|