midway-fatcms 0.0.1-beta.28 → 0.0.1-beta.29
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/config/config.default.js +6 -4
- package/dist/controller/manage/CrudStandardDesignApi.js +8 -8
- package/dist/middleware/global.middleware.js +22 -9
- package/dist/models/AsyncTaskModel.d.ts +2 -0
- package/dist/models/AsyncTaskModel.js +3 -1
- package/dist/models/RedisKeys.d.ts +2 -0
- package/dist/models/RedisKeys.js +2 -0
- package/dist/service/UserSessionService.d.ts +21 -0
- package/dist/service/UserSessionService.js +71 -1
- package/dist/service/WorkbenchService.d.ts +32 -0
- package/dist/service/WorkbenchService.js +54 -11
- package/dist/service/anyapi/AnyApiSandboxService.js +12 -12
- package/dist/service/asyncTask/AsyncTaskRunnerService.d.ts +14 -1
- package/dist/service/asyncTask/AsyncTaskRunnerService.js +70 -25
- package/dist/service/asyncTask/handler/ExportExcelByInnerHttpHandler.js +11 -2
- package/dist/service/base/RedisCacheService.d.ts +7 -0
- package/dist/service/base/RedisCacheService.js +7 -0
- package/package.json +1 -1
|
@@ -31,10 +31,10 @@ exports.default = (appInfo) => {
|
|
|
31
31
|
},
|
|
32
32
|
bodyParser: {
|
|
33
33
|
enableTypes: ['json', 'form', 'text', 'xml'],
|
|
34
|
-
formLimit: '
|
|
35
|
-
jsonLimit: '
|
|
36
|
-
textLimit: '
|
|
37
|
-
xmlLimit: '
|
|
34
|
+
formLimit: '10mb',
|
|
35
|
+
jsonLimit: '10mb',
|
|
36
|
+
textLimit: '10mb',
|
|
37
|
+
xmlLimit: '10mb',
|
|
38
38
|
ignore: [
|
|
39
39
|
'/ns/gw/proxy/*',
|
|
40
40
|
'/ns/gw/file/uploadFile',
|
|
@@ -179,6 +179,8 @@ exports.default = (appInfo) => {
|
|
|
179
179
|
headerKey: 'fatcmsSAEnable',
|
|
180
180
|
headerSecret: '222',
|
|
181
181
|
},
|
|
182
|
+
// 用户会话保持时间,单位秒
|
|
183
|
+
fatcmsUserSessionKeepTimeSecond: 3600 * 24 * 30,
|
|
182
184
|
// 是否启用内置的定时任务
|
|
183
185
|
fatcmsScheduleService: true,
|
|
184
186
|
// Excel导出文件的临时目录
|
|
@@ -254,14 +254,14 @@ let CrudStandardDesignApi = class CrudStandardDesignApi extends BaseApiControlle
|
|
|
254
254
|
throw new exceptions_1.CommonException('DB_NOT_FOUND', '数据库配置没有找到:' + dbName);
|
|
255
255
|
}
|
|
256
256
|
const schemaname = 'public';
|
|
257
|
-
const columnArraySql = `
|
|
258
|
-
|
|
259
|
-
SELECT
|
|
260
|
-
*
|
|
261
|
-
FROM information_schema.columns
|
|
262
|
-
WHERE table_schema = $1 and table_name = $2
|
|
263
|
-
ORDER BY ordinal_position;
|
|
264
|
-
|
|
257
|
+
const columnArraySql = `
|
|
258
|
+
|
|
259
|
+
SELECT
|
|
260
|
+
*
|
|
261
|
+
FROM information_schema.columns
|
|
262
|
+
WHERE table_schema = $1 and table_name = $2
|
|
263
|
+
ORDER BY ordinal_position;
|
|
264
|
+
|
|
265
265
|
`.trim();
|
|
266
266
|
const dbType = keys_1.SqlDbType.postgres;
|
|
267
267
|
const columnArray = await this.curdMixService.executeSQL({
|
|
@@ -37,6 +37,15 @@ function isBlob(obj) {
|
|
|
37
37
|
function isStream(obj) {
|
|
38
38
|
return obj && obj instanceof node_stream_1.Stream;
|
|
39
39
|
}
|
|
40
|
+
async function getUserSessionService(ctx) {
|
|
41
|
+
return ctx.requestContext.getAsync(UserSessionService_1.UserSessionService);
|
|
42
|
+
}
|
|
43
|
+
async function getWorkbenchService(ctx) {
|
|
44
|
+
return ctx.requestContext.getAsync(WorkbenchService_1.WorkbenchService);
|
|
45
|
+
}
|
|
46
|
+
async function getVisitStatService(ctx) {
|
|
47
|
+
return ctx.requestContext.getAsync(VisitStatService_1.VisitStatService);
|
|
48
|
+
}
|
|
40
49
|
/**
|
|
41
50
|
* 专门对ExecuteSqlContext对象进行处理
|
|
42
51
|
* @param res
|
|
@@ -121,15 +130,6 @@ function handleDebugRes(beforeRes, ctx, originRes) {
|
|
|
121
130
|
nextRes.debugInfo = debugInfo;
|
|
122
131
|
return nextRes;
|
|
123
132
|
}
|
|
124
|
-
async function getUserSessionService(ctx) {
|
|
125
|
-
return ctx.requestContext.getAsync(UserSessionService_1.UserSessionService);
|
|
126
|
-
}
|
|
127
|
-
async function getWorkbenchService(ctx) {
|
|
128
|
-
return ctx.requestContext.getAsync(WorkbenchService_1.WorkbenchService);
|
|
129
|
-
}
|
|
130
|
-
async function getVisitStatService(ctx) {
|
|
131
|
-
return ctx.requestContext.getAsync(VisitStatService_1.VisitStatService);
|
|
132
|
-
}
|
|
133
133
|
/**
|
|
134
134
|
* 检查是否是超级管理员
|
|
135
135
|
* @param ctx
|
|
@@ -155,6 +155,12 @@ function checkIsSuperAdmin(ctx, sessionInfo) {
|
|
|
155
155
|
}
|
|
156
156
|
return false;
|
|
157
157
|
}
|
|
158
|
+
/**
|
|
159
|
+
* 处理调试错误
|
|
160
|
+
* @param e
|
|
161
|
+
* @param ctx
|
|
162
|
+
* @returns
|
|
163
|
+
*/
|
|
158
164
|
function handleDebugError(e, ctx) {
|
|
159
165
|
ctx.logger.error('GlobalMiddleware', e);
|
|
160
166
|
let errorStack = null;
|
|
@@ -177,6 +183,10 @@ function handleDebugError(e, ctx) {
|
|
|
177
183
|
}
|
|
178
184
|
return res;
|
|
179
185
|
}
|
|
186
|
+
/**
|
|
187
|
+
* 数据埋点
|
|
188
|
+
* @param ctx
|
|
189
|
+
*/
|
|
180
190
|
async function trackRequest(ctx) {
|
|
181
191
|
try {
|
|
182
192
|
const visitStatService = await getVisitStatService(ctx);
|
|
@@ -186,6 +196,9 @@ async function trackRequest(ctx) {
|
|
|
186
196
|
console.error('trackRequestError', e);
|
|
187
197
|
}
|
|
188
198
|
}
|
|
199
|
+
/**
|
|
200
|
+
* 这些路径不需要配置用户信息等操作
|
|
201
|
+
*/
|
|
189
202
|
const excludePathPrefix = ['/ns/static/', '/ns/api/helpers'];
|
|
190
203
|
/**
|
|
191
204
|
* 全局中间件
|
|
@@ -70,3 +70,5 @@ export interface SysAsyncTaskHandlerConfig {
|
|
|
70
70
|
priority?: number;
|
|
71
71
|
expireSeconds?: number;
|
|
72
72
|
}
|
|
73
|
+
export declare const HEADER_KEY_RUN_BY_ASYNC_TASK_ID = "run-by-async-task-id";
|
|
74
|
+
export declare const HEADER_KEY_RUN_BY_ASYNC_TASK_WORKBENCH_CODE = "run-by-async-task-workbenchcode";
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.SysAsyncFileFormat = exports.SysAsyncTaskStatus = void 0;
|
|
3
|
+
exports.HEADER_KEY_RUN_BY_ASYNC_TASK_WORKBENCH_CODE = exports.HEADER_KEY_RUN_BY_ASYNC_TASK_ID = exports.SysAsyncFileFormat = exports.SysAsyncTaskStatus = void 0;
|
|
4
4
|
var SysAsyncTaskStatus;
|
|
5
5
|
(function (SysAsyncTaskStatus) {
|
|
6
6
|
SysAsyncTaskStatus["PENDING"] = "PENDING";
|
|
@@ -27,3 +27,5 @@ var SysAsyncFileFormat;
|
|
|
27
27
|
SysAsyncFileFormat["PNG"] = "png";
|
|
28
28
|
SysAsyncFileFormat["JPG"] = "jpg";
|
|
29
29
|
})(SysAsyncFileFormat = exports.SysAsyncFileFormat || (exports.SysAsyncFileFormat = {}));
|
|
30
|
+
exports.HEADER_KEY_RUN_BY_ASYNC_TASK_ID = 'run-by-async-task-id';
|
|
31
|
+
exports.HEADER_KEY_RUN_BY_ASYNC_TASK_WORKBENCH_CODE = 'run-by-async-task-workbenchcode';
|
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
declare const RedisKeys: {
|
|
2
2
|
ASYNC_TASK_LOCK: string;
|
|
3
|
+
ASYNC_TASK_TIMEOUT_LOCK: string;
|
|
3
4
|
ASYNC_TASK_UPDATE_TIME: string;
|
|
4
5
|
USER_SESSION_PREFIX: string;
|
|
6
|
+
USER_SESSION_ASYNC_TASK_ID_PREFIX: string;
|
|
5
7
|
USER_ACTION_TIME_LIMIT_PREFIX: string;
|
|
6
8
|
VISIT_STAT_LOCK_PREFIX: string;
|
|
7
9
|
VISIT_STAT_DATE_PREFIX: string;
|
package/dist/models/RedisKeys.js
CHANGED
|
@@ -3,8 +3,10 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.RedisKeys = void 0;
|
|
4
4
|
const RedisKeys = {
|
|
5
5
|
ASYNC_TASK_LOCK: 'FatAsyncTaskLock',
|
|
6
|
+
ASYNC_TASK_TIMEOUT_LOCK: 'FatAsyncTaskTimeoutLock',
|
|
6
7
|
ASYNC_TASK_UPDATE_TIME: 'FatAsyncTaskUpdateTime',
|
|
7
8
|
USER_SESSION_PREFIX: 'FatUserSession_',
|
|
9
|
+
USER_SESSION_ASYNC_TASK_ID_PREFIX: 'FatUserSessionAsyncTaskId_',
|
|
8
10
|
USER_ACTION_TIME_LIMIT_PREFIX: 'FatUserActionTimeLimit_',
|
|
9
11
|
VISIT_STAT_LOCK_PREFIX: 'FatVsLock_',
|
|
10
12
|
VISIT_STAT_DATE_PREFIX: 'FatVsDate_',
|
|
@@ -2,13 +2,34 @@ import { ISessionInfo } from '../models/userSession';
|
|
|
2
2
|
export declare class UserSessionService {
|
|
3
3
|
private ctx;
|
|
4
4
|
private redisCacheService;
|
|
5
|
+
private curdProService;
|
|
6
|
+
private fatcmsUserSessionKeepTimeSecond;
|
|
7
|
+
/**
|
|
8
|
+
* 获取用户会话保持时间: 单位秒
|
|
9
|
+
* @returns
|
|
10
|
+
*/
|
|
11
|
+
private getSessionKeepTimeSecond;
|
|
12
|
+
/**
|
|
13
|
+
* 保存用户会话信息
|
|
14
|
+
* @param sessionInfo
|
|
15
|
+
*/
|
|
5
16
|
saveUserSession(sessionInfo: ISessionInfo): Promise<void>;
|
|
6
17
|
/**
|
|
7
18
|
* 移除Session
|
|
8
19
|
* @param sessionId
|
|
9
20
|
*/
|
|
10
21
|
removeUserSession(sessionId: string): Promise<any>;
|
|
22
|
+
/**
|
|
23
|
+
* 获取用户会话信息
|
|
24
|
+
* @param sessionId
|
|
25
|
+
* @returns
|
|
26
|
+
*/
|
|
11
27
|
private getCurrentUserSessionForMiddleware1;
|
|
28
|
+
/**
|
|
29
|
+
* 从异步任务队列中获取当时用户的Session信息
|
|
30
|
+
* @param asyncTaskId
|
|
31
|
+
*/
|
|
32
|
+
private getCurrentUserSessionByAsyncTaskId;
|
|
12
33
|
/**
|
|
13
34
|
* 专门给中间件点调用的
|
|
14
35
|
*/
|
|
@@ -15,6 +15,11 @@ const functions_1 = require("../libs/utils/functions");
|
|
|
15
15
|
const userSession_1 = require("../models/userSession");
|
|
16
16
|
const RedisCacheService_1 = require("./base/RedisCacheService");
|
|
17
17
|
const RedisKeys_1 = require("../models/RedisKeys");
|
|
18
|
+
const AsyncTaskModel_1 = require("../models/AsyncTaskModel");
|
|
19
|
+
const CurdProService_1 = require("./curd/CurdProService");
|
|
20
|
+
const keys_1 = require("../libs/crud-pro/models/keys");
|
|
21
|
+
const SystemTables_1 = require("../models/SystemTables");
|
|
22
|
+
const global_config_1 = require("../libs/global-config/global-config");
|
|
18
23
|
function pickUserAvatar(avatar) {
|
|
19
24
|
if (!avatar) {
|
|
20
25
|
return null;
|
|
@@ -45,10 +50,25 @@ const toCacheKey = (sessionId) => {
|
|
|
45
50
|
return `${RedisKeys_1.RedisKeys.USER_SESSION_PREFIX}${sessionId}`;
|
|
46
51
|
};
|
|
47
52
|
let UserSessionService = class UserSessionService {
|
|
53
|
+
/**
|
|
54
|
+
* 获取用户会话保持时间: 单位秒
|
|
55
|
+
* @returns
|
|
56
|
+
*/
|
|
57
|
+
getSessionKeepTimeSecond() {
|
|
58
|
+
if (typeof this.fatcmsUserSessionKeepTimeSecond === 'number' && this.fatcmsUserSessionKeepTimeSecond > 0) {
|
|
59
|
+
return this.fatcmsUserSessionKeepTimeSecond;
|
|
60
|
+
}
|
|
61
|
+
return SESSION_KEEP_TIME_SECOND;
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* 保存用户会话信息
|
|
65
|
+
* @param sessionInfo
|
|
66
|
+
*/
|
|
48
67
|
async saveUserSession(sessionInfo) {
|
|
49
68
|
sessionInfo.avatar = pickUserAvatar(sessionInfo.avatar);
|
|
50
69
|
const sessionId = sessionInfo.sessionId;
|
|
51
|
-
|
|
70
|
+
const keepTime = this.getSessionKeepTimeSecond();
|
|
71
|
+
await this.redisCacheService.setJsonObject(toCacheKey(sessionId), sessionInfo, keepTime);
|
|
52
72
|
}
|
|
53
73
|
/**
|
|
54
74
|
* 移除Session
|
|
@@ -57,16 +77,58 @@ let UserSessionService = class UserSessionService {
|
|
|
57
77
|
async removeUserSession(sessionId) {
|
|
58
78
|
return await this.redisCacheService.removeItem(toCacheKey(sessionId));
|
|
59
79
|
}
|
|
80
|
+
/**
|
|
81
|
+
* 获取用户会话信息
|
|
82
|
+
* @param sessionId
|
|
83
|
+
* @returns
|
|
84
|
+
*/
|
|
60
85
|
async getCurrentUserSessionForMiddleware1(sessionId) {
|
|
61
86
|
if (!sessionId) {
|
|
62
87
|
return null;
|
|
63
88
|
}
|
|
64
89
|
return await this.redisCacheService.getJsonObject(toCacheKey(sessionId));
|
|
65
90
|
}
|
|
91
|
+
/**
|
|
92
|
+
* 从异步任务队列中获取当时用户的Session信息
|
|
93
|
+
* @param asyncTaskId
|
|
94
|
+
*/
|
|
95
|
+
async getCurrentUserSessionByAsyncTaskId(asyncTaskId) {
|
|
96
|
+
const taskSessionCacheKey = `${RedisKeys_1.RedisKeys.USER_SESSION_ASYNC_TASK_ID_PREFIX}${asyncTaskId}`;
|
|
97
|
+
const loadSessionInfo = async () => {
|
|
98
|
+
const { SystemDbName, SystemDbType } = global_config_1.GLOBAL_STATIC_CONFIG.getConfig();
|
|
99
|
+
const asyncTaskRes = await this.curdProService.executeCrudByCfg({
|
|
100
|
+
columns: ['id', 'created_user_session'],
|
|
101
|
+
condition: {
|
|
102
|
+
id: asyncTaskId,
|
|
103
|
+
}
|
|
104
|
+
}, {
|
|
105
|
+
sqlSimpleName: keys_1.KeysOfSimpleSQL.SIMPLE_QUERY_ONE,
|
|
106
|
+
sqlTable: SystemTables_1.SystemTables.sys_async_tasks,
|
|
107
|
+
sqlDatabase: SystemDbName,
|
|
108
|
+
sqlDbType: SystemDbType,
|
|
109
|
+
});
|
|
110
|
+
const asyncTaskObj = asyncTaskRes.getOneObj();
|
|
111
|
+
if (!asyncTaskObj) {
|
|
112
|
+
return null;
|
|
113
|
+
}
|
|
114
|
+
return (0, functions_1.parseJsonObject)(asyncTaskObj.created_user_session);
|
|
115
|
+
};
|
|
116
|
+
let sessionInfo1 = await this.redisCacheService.getJsonObject(taskSessionCacheKey);
|
|
117
|
+
if (!sessionInfo1) {
|
|
118
|
+
sessionInfo1 = await loadSessionInfo();
|
|
119
|
+
await this.redisCacheService.setJsonObject(taskSessionCacheKey, sessionInfo1, 3600); // 只缓存一小时即可
|
|
120
|
+
}
|
|
121
|
+
return sessionInfo1;
|
|
122
|
+
}
|
|
66
123
|
/**
|
|
67
124
|
* 专门给中间件点调用的
|
|
68
125
|
*/
|
|
69
126
|
async getCurrentUserSessionForMiddleware() {
|
|
127
|
+
// 先从Header中获取:异步任务队列的任务ID
|
|
128
|
+
const asyncTaskId = this.ctx.headers[AsyncTaskModel_1.HEADER_KEY_RUN_BY_ASYNC_TASK_ID];
|
|
129
|
+
if (asyncTaskId && typeof asyncTaskId === 'string') {
|
|
130
|
+
return this.getCurrentUserSessionByAsyncTaskId(asyncTaskId);
|
|
131
|
+
}
|
|
70
132
|
const cookies = this.ctx.cookies;
|
|
71
133
|
const sessionId = cookies.get(userSession_1.SESSION_ID_KEY, userSession_1.sessionCookieCfg);
|
|
72
134
|
return this.getCurrentUserSessionForMiddleware1(sessionId);
|
|
@@ -80,6 +142,14 @@ __decorate([
|
|
|
80
142
|
(0, core_1.Inject)(),
|
|
81
143
|
__metadata("design:type", RedisCacheService_1.RedisCacheService)
|
|
82
144
|
], UserSessionService.prototype, "redisCacheService", void 0);
|
|
145
|
+
__decorate([
|
|
146
|
+
(0, core_1.Inject)(),
|
|
147
|
+
__metadata("design:type", CurdProService_1.CurdProService)
|
|
148
|
+
], UserSessionService.prototype, "curdProService", void 0);
|
|
149
|
+
__decorate([
|
|
150
|
+
(0, core_1.Config)('fatcmsUserSessionKeepTimeSecond'),
|
|
151
|
+
__metadata("design:type", Number)
|
|
152
|
+
], UserSessionService.prototype, "fatcmsUserSessionKeepTimeSecond", void 0);
|
|
83
153
|
UserSessionService = __decorate([
|
|
84
154
|
(0, core_1.Provide)()
|
|
85
155
|
], UserSessionService);
|
|
@@ -7,13 +7,45 @@ export declare class WorkbenchService extends BaseService implements IScheduleSe
|
|
|
7
7
|
private curdProService;
|
|
8
8
|
clearCache(): void;
|
|
9
9
|
runBySchedule(): Promise<void>;
|
|
10
|
+
/**
|
|
11
|
+
* 获取所有站点列表
|
|
12
|
+
* @param isForceRefresh 是否强制刷新缓存
|
|
13
|
+
* @returns
|
|
14
|
+
*/
|
|
10
15
|
getAllWorkbenchInfoList(isForceRefresh?: boolean): Promise<IWorkbenchEntity[]>;
|
|
16
|
+
/**
|
|
17
|
+
* 获取所有站点列表,以map的形式返回。其中key为站点域名,value为站点信息
|
|
18
|
+
* @param isForceRefresh 是否强制刷新缓存
|
|
19
|
+
* @returns
|
|
20
|
+
*/
|
|
11
21
|
getAllWorkbenchInfoMap(isForceRefresh?: boolean): Promise<Record<any, IWorkbenchEntity>>;
|
|
22
|
+
/**
|
|
23
|
+
* 检查站点编码是否匹配
|
|
24
|
+
* @param workbench_code_array 站点编码数组
|
|
25
|
+
* @param workbench_code 站点编码
|
|
26
|
+
* @returns
|
|
27
|
+
*/
|
|
12
28
|
isMatchWorkbenchCode(workbench_code_array: string | string[], workbench_code: string): boolean;
|
|
29
|
+
/**
|
|
30
|
+
* 根据站点编码获取站点信息
|
|
31
|
+
* @param workbenchCode 站点编码
|
|
32
|
+
* @returns
|
|
33
|
+
*/
|
|
34
|
+
private getWorkbenchInfoByCode;
|
|
35
|
+
/**
|
|
36
|
+
* 根据域名获取站点信息
|
|
37
|
+
* @param host 域名
|
|
38
|
+
* @returns
|
|
39
|
+
*/
|
|
40
|
+
private getWorkbenchInfoByHost;
|
|
13
41
|
/**
|
|
14
42
|
* 获取当前请求的域名所对应的站点信息
|
|
15
43
|
*/
|
|
16
44
|
getCurrentHostWorkbenchInfo(): Promise<IWorkbenchEntity>;
|
|
45
|
+
/**
|
|
46
|
+
* 获取当前请求的域名
|
|
47
|
+
* @returns
|
|
48
|
+
*/
|
|
17
49
|
private getCurrentHost;
|
|
18
50
|
isSupportCurrentWorkbench(workbench_code_array: string | string[]): Promise<boolean>;
|
|
19
51
|
isSupportCurrentHostForForMiddleware(): Promise<string | boolean>;
|
|
@@ -21,6 +21,7 @@ const functions_1 = require("../libs/utils/functions");
|
|
|
21
21
|
const fatcms_request_1 = require("../libs/utils/fatcms-request");
|
|
22
22
|
const MixinUtils_1 = require("../libs/crud-pro/utils/MixinUtils");
|
|
23
23
|
const global_config_1 = require("../libs/global-config/global-config");
|
|
24
|
+
const AsyncTaskModel_1 = require("../models/AsyncTaskModel");
|
|
24
25
|
//
|
|
25
26
|
// const lruCache = new LRUCache<string, any>({
|
|
26
27
|
// max: 500,
|
|
@@ -38,6 +39,11 @@ let WorkbenchService = class WorkbenchService extends BaseService_1.BaseService
|
|
|
38
39
|
async runBySchedule() {
|
|
39
40
|
await this.getAllWorkbenchInfoMap(true);
|
|
40
41
|
}
|
|
42
|
+
/**
|
|
43
|
+
* 获取所有站点列表
|
|
44
|
+
* @param isForceRefresh 是否强制刷新缓存
|
|
45
|
+
* @returns
|
|
46
|
+
*/
|
|
41
47
|
async getAllWorkbenchInfoList(isForceRefresh) {
|
|
42
48
|
const { SystemDbName, SystemDbType } = global_config_1.GLOBAL_STATIC_CONFIG.getConfig();
|
|
43
49
|
let listData = lruCache.get(CACHE_KEY_WORKBENCH_LIST);
|
|
@@ -54,6 +60,11 @@ let WorkbenchService = class WorkbenchService extends BaseService_1.BaseService
|
|
|
54
60
|
}
|
|
55
61
|
return listData;
|
|
56
62
|
}
|
|
63
|
+
/**
|
|
64
|
+
* 获取所有站点列表,以map的形式返回。其中key为站点域名,value为站点信息
|
|
65
|
+
* @param isForceRefresh 是否强制刷新缓存
|
|
66
|
+
* @returns
|
|
67
|
+
*/
|
|
57
68
|
async getAllWorkbenchInfoMap(isForceRefresh) {
|
|
58
69
|
let listData;
|
|
59
70
|
let domainMap = lruCache.get(CACHE_KEY_DOMAIN_MAP);
|
|
@@ -67,6 +78,12 @@ let WorkbenchService = class WorkbenchService extends BaseService_1.BaseService
|
|
|
67
78
|
}
|
|
68
79
|
return domainMap;
|
|
69
80
|
}
|
|
81
|
+
/**
|
|
82
|
+
* 检查站点编码是否匹配
|
|
83
|
+
* @param workbench_code_array 站点编码数组
|
|
84
|
+
* @param workbench_code 站点编码
|
|
85
|
+
* @returns
|
|
86
|
+
*/
|
|
70
87
|
isMatchWorkbenchCode(workbench_code_array, workbench_code) {
|
|
71
88
|
if (workbench_code_array === '*') {
|
|
72
89
|
return true;
|
|
@@ -76,6 +93,30 @@ let WorkbenchService = class WorkbenchService extends BaseService_1.BaseService
|
|
|
76
93
|
.filter(Boolean);
|
|
77
94
|
return supportWorkbenchArray.includes('*') || supportWorkbenchArray.includes(workbench_code);
|
|
78
95
|
}
|
|
96
|
+
/**
|
|
97
|
+
* 根据站点编码获取站点信息
|
|
98
|
+
* @param workbenchCode 站点编码
|
|
99
|
+
* @returns
|
|
100
|
+
*/
|
|
101
|
+
async getWorkbenchInfoByCode(workbenchCode) {
|
|
102
|
+
const list = await this.getAllWorkbenchInfoList();
|
|
103
|
+
for (let i = 0; i < list.length; i++) {
|
|
104
|
+
const workbenchInfo = list[i];
|
|
105
|
+
if (workbenchInfo.workbench_code === workbenchCode) {
|
|
106
|
+
return workbenchInfo;
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
return null;
|
|
110
|
+
}
|
|
111
|
+
/**
|
|
112
|
+
* 根据域名获取站点信息
|
|
113
|
+
* @param host 域名
|
|
114
|
+
* @returns
|
|
115
|
+
*/
|
|
116
|
+
async getWorkbenchInfoByHost(host) {
|
|
117
|
+
const domainMap = await this.getAllWorkbenchInfoMap();
|
|
118
|
+
return domainMap[host];
|
|
119
|
+
}
|
|
79
120
|
/**
|
|
80
121
|
* 获取当前请求的域名所对应的站点信息
|
|
81
122
|
*/
|
|
@@ -86,24 +127,26 @@ let WorkbenchService = class WorkbenchService extends BaseService_1.BaseService
|
|
|
86
127
|
if (!debugWorkbenchCode) {
|
|
87
128
|
throw new exceptions_1.CommonException('本地环境调试时需要使用ModHeader浏览器插件,指定站点编码');
|
|
88
129
|
}
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
}
|
|
96
|
-
return null;
|
|
130
|
+
return await this.getWorkbenchInfoByCode(debugWorkbenchCode);
|
|
131
|
+
}
|
|
132
|
+
// 内部异步任务,指定了workbench code
|
|
133
|
+
const asyncTaskWorkbenchCode = this.ctx.headers[AsyncTaskModel_1.HEADER_KEY_RUN_BY_ASYNC_TASK_WORKBENCH_CODE];
|
|
134
|
+
if (asyncTaskWorkbenchCode && typeof asyncTaskWorkbenchCode === 'string') {
|
|
135
|
+
return await this.getWorkbenchInfoByCode(asyncTaskWorkbenchCode);
|
|
97
136
|
}
|
|
98
137
|
//正式环境: 根据域名获取站点配置
|
|
99
138
|
const host = this.getCurrentHost();
|
|
100
|
-
const
|
|
101
|
-
const workbenchInfo = domainMap[host];
|
|
139
|
+
const workbenchInfo = await this.getWorkbenchInfoByHost(host);
|
|
102
140
|
if (workbenchInfo) {
|
|
103
141
|
return workbenchInfo;
|
|
104
142
|
}
|
|
105
|
-
|
|
143
|
+
// 最后兜底
|
|
144
|
+
return await this.getWorkbenchInfoByHost("*");
|
|
106
145
|
}
|
|
146
|
+
/**
|
|
147
|
+
* 获取当前请求的域名
|
|
148
|
+
* @returns
|
|
149
|
+
*/
|
|
107
150
|
getCurrentHost() {
|
|
108
151
|
const host = this.ctx.request.host;
|
|
109
152
|
if (!host) {
|
|
@@ -74,18 +74,18 @@ let AnyApiSandboxService = class AnyApiSandboxService extends BaseService_1.Base
|
|
|
74
74
|
};
|
|
75
75
|
const nextLine100 = new Array(10).fill('\n').join('');
|
|
76
76
|
const bindCode = this.toBindCodeString('httpController', newContext, 'sandbox');
|
|
77
|
-
const code = `
|
|
78
|
-
(async () => {
|
|
79
|
-
${nextLine100}${func_code}; // 必须有一个 class HttpController { async handleRequest(params){} }
|
|
80
|
-
${nextLine100}
|
|
81
|
-
try {
|
|
82
|
-
const httpController = new HttpController();
|
|
83
|
-
${bindCode}
|
|
84
|
-
const res = await httpController.handleRequest(sandbox.allParams.params);
|
|
85
|
-
sandbox.returnSuccess(res);
|
|
86
|
-
} catch (e) {
|
|
87
|
-
sandbox.returnError(e)
|
|
88
|
-
}
|
|
77
|
+
const code = `
|
|
78
|
+
(async () => {
|
|
79
|
+
${nextLine100}${func_code}; // 必须有一个 class HttpController { async handleRequest(params){} }
|
|
80
|
+
${nextLine100}
|
|
81
|
+
try {
|
|
82
|
+
const httpController = new HttpController();
|
|
83
|
+
${bindCode}
|
|
84
|
+
const res = await httpController.handleRequest(sandbox.allParams.params);
|
|
85
|
+
sandbox.returnSuccess(res);
|
|
86
|
+
} catch (e) {
|
|
87
|
+
sandbox.returnError(e)
|
|
88
|
+
}
|
|
89
89
|
})(); `;
|
|
90
90
|
try {
|
|
91
91
|
const script = getCompiledScript(anyApi, code);
|
|
@@ -26,8 +26,21 @@ export declare const ASYNC_TASK_RUNNER: AsyncTaskRunner;
|
|
|
26
26
|
export declare class AsyncTaskRunnerService extends BaseService implements IScheduleService {
|
|
27
27
|
protected ctx: Context;
|
|
28
28
|
private curdProService;
|
|
29
|
-
|
|
29
|
+
/**
|
|
30
|
+
* 定时任务执行
|
|
31
|
+
*/
|
|
30
32
|
runBySchedule(): Promise<void>;
|
|
33
|
+
/**
|
|
34
|
+
* 更新超时的任务状态
|
|
35
|
+
* @returns
|
|
36
|
+
*/
|
|
37
|
+
private runByScheduleForTimeoutTasks;
|
|
38
|
+
/**
|
|
39
|
+
* 检查是否有新任务。如果有,就从数据库中获取一批任务,放到自己的任务队列里。
|
|
40
|
+
* @returns
|
|
41
|
+
*/
|
|
42
|
+
private runByScheduleForPendingTasks;
|
|
43
|
+
fetchPendingTasks(): Promise<void>;
|
|
31
44
|
/**
|
|
32
45
|
* 是否存在新任务
|
|
33
46
|
* @private
|
|
@@ -10,6 +10,7 @@ var __metadata = (this && this.__metadata) || function (k, v) {
|
|
|
10
10
|
};
|
|
11
11
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
12
|
exports.AsyncTaskRunnerService = exports.ASYNC_TASK_RUNNER = void 0;
|
|
13
|
+
const moment = require("moment");
|
|
13
14
|
const core_1 = require("@midwayjs/core");
|
|
14
15
|
const BaseService_1 = require("../../service/base/BaseService");
|
|
15
16
|
const CurdProService_1 = require("../../service/curd/CurdProService");
|
|
@@ -93,6 +94,8 @@ class AsyncTaskRunner {
|
|
|
93
94
|
return await schedule_1.ANONYMOUS_CONTEXT.runServiceAtAnonymousContext(async (ctx) => {
|
|
94
95
|
const curdProService = await ctx.requestContext.getAsync('curdProService');
|
|
95
96
|
const { SystemDbName, SystemDbType } = global_config_1.GLOBAL_STATIC_CONFIG.getConfig();
|
|
97
|
+
// 一定要有一个更新时间字段
|
|
98
|
+
updatePartials.updated_at = (0, functions_1.getCurrentFullMoment)();
|
|
96
99
|
const res = await curdProService.executeCrudByCfg({
|
|
97
100
|
condition: {
|
|
98
101
|
id: taskElement.id,
|
|
@@ -125,6 +128,7 @@ exports.ASYNC_TASK_RUNNER.taskHandlerMap.set('EXCEL_EXPORT', new ExportExcelAsyn
|
|
|
125
128
|
* Redis锁
|
|
126
129
|
*/
|
|
127
130
|
const ASYNC_TASK_LOCK = RedisKeys_1.RedisKeys.ASYNC_TASK_LOCK;
|
|
131
|
+
const ASYNC_TASK_TIMEOUT_LOCK = RedisKeys_1.RedisKeys.ASYNC_TASK_TIMEOUT_LOCK;
|
|
128
132
|
/**
|
|
129
133
|
* 上次检查异步任务的时间点
|
|
130
134
|
*/
|
|
@@ -132,6 +136,72 @@ const ASYNC_TASK_RUNTIME_OBJ = {
|
|
|
132
136
|
LAST_CHECK_ASYNC_TASK_UPDATE_TIME: 0,
|
|
133
137
|
};
|
|
134
138
|
let AsyncTaskRunnerService = class AsyncTaskRunnerService extends BaseService_1.BaseService {
|
|
139
|
+
/**
|
|
140
|
+
* 定时任务执行
|
|
141
|
+
*/
|
|
142
|
+
async runBySchedule() {
|
|
143
|
+
await this.runByScheduleForPendingTasks();
|
|
144
|
+
await this.runByScheduleForTimeoutTasks();
|
|
145
|
+
}
|
|
146
|
+
/**
|
|
147
|
+
* 更新超时的任务状态
|
|
148
|
+
* @returns
|
|
149
|
+
*/
|
|
150
|
+
async runByScheduleForTimeoutTasks() {
|
|
151
|
+
const { SystemDbName, SystemDbType } = global_config_1.GLOBAL_STATIC_CONFIG.getConfig();
|
|
152
|
+
// 每隔一小时执行一次,即可。
|
|
153
|
+
const lock = await this.redisService.set(ASYNC_TASK_TIMEOUT_LOCK, 1, 'EX', 60 * 60, 'NX');
|
|
154
|
+
if (lock !== 'OK') {
|
|
155
|
+
return Promise.resolve();
|
|
156
|
+
}
|
|
157
|
+
const timeoutTime = moment().subtract(24, 'hours').format("YYYY-MM-DD HH:mm:ss.SSS");
|
|
158
|
+
this.curdProService.executeCrudByCfg({
|
|
159
|
+
condition: {
|
|
160
|
+
task_status: AsyncTaskModel_1.SysAsyncTaskStatus.RUNNING,
|
|
161
|
+
updated_at: {
|
|
162
|
+
$lt: timeoutTime,
|
|
163
|
+
}
|
|
164
|
+
},
|
|
165
|
+
data: {
|
|
166
|
+
task_status: AsyncTaskModel_1.SysAsyncTaskStatus.FAILED,
|
|
167
|
+
error_message: '任务超时',
|
|
168
|
+
}
|
|
169
|
+
}, {
|
|
170
|
+
sqlTable: SystemTables_1.SystemTables.sys_async_tasks,
|
|
171
|
+
sqlSimpleName: keys_1.KeysOfSimpleSQL.SIMPLE_UPDATE,
|
|
172
|
+
sqlDatabase: SystemDbName,
|
|
173
|
+
sqlDbType: SystemDbType,
|
|
174
|
+
});
|
|
175
|
+
}
|
|
176
|
+
/**
|
|
177
|
+
* 检查是否有新任务。如果有,就从数据库中获取一批任务,放到自己的任务队列里。
|
|
178
|
+
* @returns
|
|
179
|
+
*/
|
|
180
|
+
async runByScheduleForPendingTasks() {
|
|
181
|
+
// 1. 当前很忙
|
|
182
|
+
if (exports.ASYNC_TASK_RUNNER.isBusy) {
|
|
183
|
+
return Promise.resolve();
|
|
184
|
+
}
|
|
185
|
+
// 2. 发现了新任务
|
|
186
|
+
const isExistNewTask = await this.isExistNewTask();
|
|
187
|
+
if (!isExistNewTask) {
|
|
188
|
+
return Promise.resolve();
|
|
189
|
+
}
|
|
190
|
+
// 这里的过期时间1分钟即可。fetchPendingTasks函数不可能超过一分钟。
|
|
191
|
+
// 因为这里只是从数据库中获取一批任务,放到自己的任务队列里。还没触发执行。
|
|
192
|
+
const lock = await this.redisService.set(ASYNC_TASK_LOCK, 1, 'EX', 60, 'NX');
|
|
193
|
+
if (lock !== 'OK') {
|
|
194
|
+
return Promise.resolve();
|
|
195
|
+
}
|
|
196
|
+
try {
|
|
197
|
+
await this.fetchPendingTasks();
|
|
198
|
+
}
|
|
199
|
+
catch (e) {
|
|
200
|
+
console.error('[AsyncTaskRunnerService] fetchPendingTasks error', (0, errorToString_1.errorToString)(e));
|
|
201
|
+
}
|
|
202
|
+
await this.redisService.del(ASYNC_TASK_LOCK);
|
|
203
|
+
return Promise.resolve();
|
|
204
|
+
}
|
|
135
205
|
async fetchPendingTasks() {
|
|
136
206
|
const { SystemDbName, SystemDbType } = global_config_1.GLOBAL_STATIC_CONFIG.getConfig();
|
|
137
207
|
// 只获取本进程能够处理的任务类型。
|
|
@@ -181,31 +251,6 @@ let AsyncTaskRunnerService = class AsyncTaskRunnerService extends BaseService_1.
|
|
|
181
251
|
console.log('[AsyncTaskRunnerService] ASYNC_TASK_RUNNER finished taskIds ==> ' + JSON.stringify(taskIds));
|
|
182
252
|
});
|
|
183
253
|
}
|
|
184
|
-
async runBySchedule() {
|
|
185
|
-
// 1. 当前很忙
|
|
186
|
-
if (exports.ASYNC_TASK_RUNNER.isBusy) {
|
|
187
|
-
return Promise.resolve();
|
|
188
|
-
}
|
|
189
|
-
// 2. 发现了新任务
|
|
190
|
-
const isExistNewTask = await this.isExistNewTask();
|
|
191
|
-
if (!isExistNewTask) {
|
|
192
|
-
return Promise.resolve();
|
|
193
|
-
}
|
|
194
|
-
// 这里的过期时间1分钟即可。fetchPendingTasks函数不可能超过一分钟。
|
|
195
|
-
// 因为这里只是从数据库中获取一批任务,放到自己的任务队列里。还没触发执行。
|
|
196
|
-
const lock = await this.redisService.set(ASYNC_TASK_LOCK, 1, 'EX', 60, 'NX');
|
|
197
|
-
if (lock !== 'OK') {
|
|
198
|
-
return Promise.resolve();
|
|
199
|
-
}
|
|
200
|
-
try {
|
|
201
|
-
await this.fetchPendingTasks();
|
|
202
|
-
}
|
|
203
|
-
catch (e) {
|
|
204
|
-
console.error('[AsyncTaskRunnerService] fetchPendingTasks error', (0, errorToString_1.errorToString)(e));
|
|
205
|
-
}
|
|
206
|
-
await this.redisService.del(ASYNC_TASK_LOCK);
|
|
207
|
-
return Promise.resolve();
|
|
208
|
-
}
|
|
209
254
|
/**
|
|
210
255
|
* 是否存在新任务
|
|
211
256
|
* @private
|
|
@@ -3,7 +3,9 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.ExportExcelByInnerHttpHandler = void 0;
|
|
4
4
|
const _ = require("lodash");
|
|
5
5
|
const axios_1 = require("axios");
|
|
6
|
+
const AsyncTaskModel_1 = require("../../../models/AsyncTaskModel");
|
|
6
7
|
const schedule_1 = require("../../../schedule");
|
|
8
|
+
const functions_1 = require("../../../libs/utils/functions");
|
|
7
9
|
function pickData(a, key) {
|
|
8
10
|
if (a && typeof a.getResModel === 'function') {
|
|
9
11
|
const s = a.getResModel();
|
|
@@ -63,12 +65,19 @@ class ExportExcelByInnerHttpHandler {
|
|
|
63
65
|
if (!queryApi.startsWith('/')) {
|
|
64
66
|
queryApi = '/' + queryApi;
|
|
65
67
|
}
|
|
66
|
-
|
|
68
|
+
const asyncTaskId = this.asyncTaskContext.task.id;
|
|
69
|
+
const createdUserSession = (0, functions_1.parseJsonObject)(this.asyncTaskContext.task.created_user_session);
|
|
70
|
+
const workbenchCode = createdUserSession.workbenchCode;
|
|
71
|
+
params[AsyncTaskModel_1.HEADER_KEY_RUN_BY_ASYNC_TASK_ID] = asyncTaskId;
|
|
72
|
+
params[AsyncTaskModel_1.HEADER_KEY_RUN_BY_ASYNC_TASK_WORKBENCH_CODE] = workbenchCode;
|
|
73
|
+
const headers = this.inputParams.headers || {};
|
|
74
|
+
headers[AsyncTaskModel_1.HEADER_KEY_RUN_BY_ASYNC_TASK_ID] = asyncTaskId;
|
|
75
|
+
headers[AsyncTaskModel_1.HEADER_KEY_RUN_BY_ASYNC_TASK_WORKBENCH_CODE] = workbenchCode;
|
|
67
76
|
const response = await (0, axios_1.default)({
|
|
68
77
|
method: 'POST',
|
|
69
78
|
url: `http://127.0.0.1:${port}${queryApi}`,
|
|
70
79
|
data: params,
|
|
71
|
-
headers:
|
|
80
|
+
headers: headers,
|
|
72
81
|
responseType: 'json',
|
|
73
82
|
validateStatus: () => true,
|
|
74
83
|
});
|
|
@@ -8,6 +8,13 @@ export declare class RedisCacheService {
|
|
|
8
8
|
protected redisService: RedisService;
|
|
9
9
|
protected ossServiceFactory: OSSServiceFactory;
|
|
10
10
|
getJsonObject(key: string): Promise<any>;
|
|
11
|
+
/**
|
|
12
|
+
* 设置缓存对象
|
|
13
|
+
* @param key
|
|
14
|
+
* @param obj
|
|
15
|
+
* @param expireSecond 过期时间,单位秒
|
|
16
|
+
* @returns
|
|
17
|
+
*/
|
|
11
18
|
setJsonObject(key: string, obj: any, expireSecond: number): Promise<any>;
|
|
12
19
|
removeItem(key: string): Promise<any>;
|
|
13
20
|
}
|
|
@@ -20,6 +20,13 @@ let RedisCacheService = class RedisCacheService {
|
|
|
20
20
|
const str = await this.redisService.get(key);
|
|
21
21
|
return (0, functions_1.parseJsonObject)(str);
|
|
22
22
|
}
|
|
23
|
+
/**
|
|
24
|
+
* 设置缓存对象
|
|
25
|
+
* @param key
|
|
26
|
+
* @param obj
|
|
27
|
+
* @param expireSecond 过期时间,单位秒
|
|
28
|
+
* @returns
|
|
29
|
+
*/
|
|
23
30
|
async setJsonObject(key, obj, expireSecond) {
|
|
24
31
|
const str = JSON.stringify(obj);
|
|
25
32
|
return this.redisService.set(key, str, 'EX', expireSecond);
|