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.
@@ -31,10 +31,10 @@ exports.default = (appInfo) => {
31
31
  },
32
32
  bodyParser: {
33
33
  enableTypes: ['json', 'form', 'text', 'xml'],
34
- formLimit: '1mb',
35
- jsonLimit: '1mb',
36
- textLimit: '1mb',
37
- xmlLimit: '1mb',
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;
@@ -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
- await this.redisCacheService.setJsonObject(toCacheKey(sessionId), sessionInfo, SESSION_KEEP_TIME_SECOND);
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
- const list = await this.getAllWorkbenchInfoList();
90
- for (let i = 0; i < list.length; i++) {
91
- const workbenchInfo = list[i];
92
- if (workbenchInfo.workbench_code === debugWorkbenchCode) {
93
- return workbenchInfo;
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 domainMap = await this.getAllWorkbenchInfoMap();
101
- const workbenchInfo = domainMap[host];
139
+ const workbenchInfo = await this.getWorkbenchInfoByHost(host);
102
140
  if (workbenchInfo) {
103
141
  return workbenchInfo;
104
142
  }
105
- return domainMap['*']; // 最后兜底
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
- fetchPendingTasks(): Promise<void>;
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
- params.runByExportExcel = true;
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: this.inputParams.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);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "midway-fatcms",
3
- "version": "0.0.1-beta.28",
3
+ "version": "0.0.1-beta.29",
4
4
  "description": "This is a midway component sample",
5
5
  "main": "dist/index.js",
6
6
  "typings": "index.d.ts",