midway-fatcms 0.0.11 → 0.0.12

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.
Files changed (36) hide show
  1. package/dist/configuration.d.ts +0 -2
  2. package/dist/configuration.js +14 -12
  3. package/dist/controller/gateway/CrudMtdGatewayController.js +2 -1
  4. package/dist/controller/gateway/StaticController.d.ts +0 -2
  5. package/dist/controller/gateway/StaticController.js +4 -39
  6. package/dist/controller/manage/AccountRoleManageApi.d.ts +2 -1
  7. package/dist/controller/manage/AccountRoleManageApi.js +34 -0
  8. package/dist/controller/manage/UserAccountManageApi.js +4 -3
  9. package/dist/libs/crud-pro/models/keys.d.ts +1 -0
  10. package/dist/libs/crud-pro/models/keys.js +1 -0
  11. package/dist/libs/crud-pro/services/CrudProExecuteFuncService.js +2 -2
  12. package/dist/libs/crud-pro/services/CrudProFieldUpdateService.js +3 -0
  13. package/dist/libs/crud-pro/services/CrudProTableMetaService.js +2 -2
  14. package/dist/libs/crud-pro/utils/DatabaseName.js +11 -0
  15. package/dist/libs/crud-pro/utils/DateTimeUtils.js +1 -1
  16. package/dist/libs/utils/functions.d.ts +8 -1
  17. package/dist/libs/utils/functions.js +26 -1
  18. package/dist/service/AuthService.js +1 -1
  19. package/dist/service/crudstd/CrudStdService.js +6 -1
  20. package/dist/service/curd/fixCfgModel.js +3 -1
  21. package/package.json +1 -1
  22. package/src/configuration.ts +18 -11
  23. package/src/controller/gateway/CrudMtdGatewayController.ts +2 -1
  24. package/src/controller/gateway/StaticController.ts +11 -40
  25. package/src/controller/manage/AccountRoleManageApi.ts +38 -0
  26. package/src/controller/manage/UserAccountManageApi.ts +4 -3
  27. package/src/libs/crud-pro/models/keys.ts +1 -0
  28. package/src/libs/crud-pro/services/CrudProExecuteFuncService.ts +3 -2
  29. package/src/libs/crud-pro/services/CrudProFieldUpdateService.ts +5 -0
  30. package/src/libs/crud-pro/services/CrudProTableMetaService.ts +2 -2
  31. package/src/libs/crud-pro/utils/DatabaseName.ts +12 -0
  32. package/src/libs/crud-pro/utils/DateTimeUtils.ts +1 -1
  33. package/src/libs/utils/functions.ts +34 -2
  34. package/src/service/AuthService.ts +2 -2
  35. package/src/service/crudstd/CrudStdService.ts +6 -1
  36. package/src/service/curd/fixCfgModel.ts +3 -1
@@ -1,8 +1,6 @@
1
1
  import * as koa from '@midwayjs/koa';
2
- import { RedisService } from '@midwayjs/redis';
3
2
  export declare class ContainerLifeCycle {
4
3
  app: koa.Application;
5
- redisService: RedisService;
6
4
  onConfigLoad(): Promise<any>;
7
5
  onReady(): Promise<void>;
8
6
  /**
@@ -89,14 +89,20 @@ let ContainerLifeCycle = class ContainerLifeCycle {
89
89
  * 会通过 Redis 广播通知所有节点刷新缓存。
90
90
  */
91
91
  async initCacheRefreshRedis() {
92
- try {
93
- (0, CacheRefreshRedisSubscriber_1.initCacheRefreshPublishClient)(this.redisService);
94
- (0, CacheRefreshRedisSubscriber_1.initCacheRefreshSubscriber)(this.redisService);
95
- }
96
- catch (e) {
97
- // Redis 未配置或不可用时,降级为单节点模式(仅刷新本节点缓存)
98
- this.app.getLogger().warn('ContainerLifeCycle ==> initCacheRefreshRedis Redis不可用,降级为单节点模式:', e.message);
99
- }
92
+ setTimeout(async () => {
93
+ const app = this.app;
94
+ const ctx = app.createAnonymousContext();
95
+ const redisService = await ctx.requestContext.getAsync(redis_1.RedisService);
96
+ try {
97
+ (0, CacheRefreshRedisSubscriber_1.initCacheRefreshPublishClient)(redisService);
98
+ (0, CacheRefreshRedisSubscriber_1.initCacheRefreshSubscriber)(redisService);
99
+ this.app.getLogger().info('ContainerLifeCycle ==> initCacheRefreshRedis success');
100
+ }
101
+ catch (e) {
102
+ // Redis 未配置或不可用时,降级为单节点模式(仅刷新本节点缓存)
103
+ this.app.getLogger().warn('ContainerLifeCycle ==> initCacheRefreshRedis Redis不可用,降级为单节点模式:', e.message);
104
+ }
105
+ }, 1000 * 60);
100
106
  }
101
107
  /**
102
108
  * 注册各类型缓存的刷新器到 CacheRefreshHub
@@ -159,10 +165,6 @@ __decorate([
159
165
  (0, core_1.App)(),
160
166
  __metadata("design:type", Object)
161
167
  ], ContainerLifeCycle.prototype, "app", void 0);
162
- __decorate([
163
- (0, core_1.Inject)(),
164
- __metadata("design:type", redis_1.RedisService)
165
- ], ContainerLifeCycle.prototype, "redisService", void 0);
166
168
  ContainerLifeCycle = __decorate([
167
169
  (0, core_1.Configuration)({
168
170
  // namespace: 'fatcms',
@@ -23,6 +23,7 @@ const CurdProService_1 = require("../../service/curd/CurdProService");
23
23
  const CurdMixService_1 = require("../../service/curd/CurdMixService");
24
24
  const DatabaseName_1 = require("../../libs/crud-pro/utils/DatabaseName");
25
25
  const global_config_1 = require("../../libs/global-config/global-config");
26
+ const functions_1 = require("../../libs/utils/functions");
26
27
  const QUERY_AS_LIST = ['condition', 'data'];
27
28
  function pickAsQuery(query_as_pick, query) {
28
29
  if (query_as_pick && typeof query_as_pick === 'string') {
@@ -48,7 +49,7 @@ let CrudMtdGatewayController = class CrudMtdGatewayController extends ApiBaseSer
48
49
  return common_dto_1.CommonResult.errorRes('method参数不存在');
49
50
  }
50
51
  const methodInfo = (await this.curdProService.getCachedCfgByMethod(methodCode));
51
- if (!methodInfo || methodInfo.status !== 1 || methodInfo.deletedAt || methodInfo.deleted_at) {
52
+ if (!methodInfo || !(0, functions_1.isEntityOK)(methodInfo)) {
52
53
  return common_dto_1.CommonResult.errorRes('接口不存在或已下线: ' + methodCode);
53
54
  }
54
55
  this.logInfo('methodInfo=== ', methodInfo);
@@ -12,10 +12,8 @@ interface HttpGetRes {
12
12
  export declare class StaticController extends BaseApiController {
13
13
  protected ctx: Context;
14
14
  private getLocalPaths;
15
- private initNotFoundList;
16
15
  private responseLocalFile;
17
16
  proxyStaticFile(ossName: string, relativePath: string): Promise<string>;
18
- saveNotFoundList(): Promise<void>;
19
17
  httpsGet(url: string): Promise<HttpGetRes>;
20
18
  writeResponseToFile(httpsGetRes: HttpGetRes, localFilePath: string, localHeaderPath: string): Promise<void>;
21
19
  getMimeType(filePath: string): any;
@@ -37,9 +37,8 @@ function getPathConfig(ossName) {
37
37
  remoteBaseUrl,
38
38
  };
39
39
  }
40
- // 404列表缓存
40
+ // 404列表缓存: 只再内存中记录。重启后继续
41
41
  let notFoundList = new Set();
42
- let notFoundListIsInit = false;
43
42
  /**
44
43
  * 静态文件代理功能
45
44
  */
@@ -48,31 +47,10 @@ let StaticController = class StaticController extends BaseApiController_1.BaseAp
48
47
  const app = schedule_1.ANONYMOUS_CONTEXT.getApp();
49
48
  const appDir = app.getAppDir();
50
49
  const localDir = path.join(appDir, './public/static'); // 本地文件存储目录
51
- const notFoundListFile = path.join(localDir, '__404__list.json'); // 404列表文件
52
50
  return {
53
51
  localDir,
54
- notFoundListFile,
55
52
  };
56
53
  }
57
- async initNotFoundList() {
58
- if (notFoundListIsInit) {
59
- return;
60
- }
61
- notFoundListIsInit = true;
62
- const { localDir, notFoundListFile } = this.getLocalPaths();
63
- try {
64
- // 确保本地存储目录存在
65
- await fs.mkdir(localDir, { recursive: true });
66
- const data = await fs.readFile(notFoundListFile, 'utf8');
67
- notFoundList = new Set(JSON.parse(data));
68
- console.log(`已加载${notFoundList.size}个404记录`);
69
- }
70
- catch (error) {
71
- if (error.code !== 'ENOENT') {
72
- console.error('读取404列表失败:', error);
73
- }
74
- }
75
- }
76
54
  async responseLocalFile(localFilePath, localHeaderPath, relativePath) {
77
55
  const headers = {
78
56
  'Cache-Control': 'public, max-age=31536000',
@@ -84,8 +62,10 @@ let StaticController = class StaticController extends BaseApiController_1.BaseAp
84
62
  if (stats.isFile()) {
85
63
  const headerDataStr = await fs.readFile(localHeaderPath, 'utf8');
86
64
  const headerData = (0, functions_1.parseJsonObject)(headerDataStr);
87
- if (headerData) {
65
+ if (headerData && headerData['last-modified']) {
88
66
  headers['last-modified'] = headerData['last-modified'];
67
+ }
68
+ if (headerData && headerData['etag']) {
89
69
  headers['etag'] = headerData['etag'];
90
70
  }
91
71
  }
@@ -117,7 +97,6 @@ let StaticController = class StaticController extends BaseApiController_1.BaseAp
117
97
  return false;
118
98
  }
119
99
  async proxyStaticFile(ossName, relativePath) {
120
- await this.initNotFoundList();
121
100
  const { localDir } = this.getLocalPaths();
122
101
  const PATH_CONFIG = getPathConfig(ossName);
123
102
  // 构建本地路径和远程路径
@@ -149,7 +128,6 @@ let StaticController = class StaticController extends BaseApiController_1.BaseAp
149
128
  else if (remoteRes.statusCode === 404) {
150
129
  // 记录404并返回
151
130
  notFoundList.add(relativePath);
152
- await this.saveNotFoundList();
153
131
  this.logInfo(`[StaticController]远程文件不存在,已记录404: ${relativePath}`);
154
132
  this.ctx.status = 404;
155
133
  return this.ctx.render('500', { errorMsg: ' 远程文件不存在,已记录404' });
@@ -171,19 +149,6 @@ let StaticController = class StaticController extends BaseApiController_1.BaseAp
171
149
  this.ctx.status = 500;
172
150
  return this.ctx.render('500', { errorMsg: '未知异常' });
173
151
  }
174
- // 保存404列表
175
- async saveNotFoundList() {
176
- const { notFoundListFile } = this.getLocalPaths();
177
- try {
178
- // 确保存储404列表的目录存在
179
- const dir = path.dirname(notFoundListFile);
180
- await fs.mkdir(dir, { recursive: true });
181
- await fs.writeFile(notFoundListFile, JSON.stringify(Array.from(notFoundList), null), 'utf8');
182
- }
183
- catch (error) {
184
- this.logError('[StaticController]保存404列表失败:', error);
185
- }
186
- }
187
152
  // 封装https请求为异步函数
188
153
  async httpsGet(url) {
189
154
  return new Promise((resolve, reject) => {
@@ -1,10 +1,11 @@
1
1
  import { Context } from '@midwayjs/koa';
2
2
  import { BaseApiController } from '../base/BaseApiController';
3
+ import { CommonResult } from '../../libs/utils/common-dto';
3
4
  export declare class AccountRoleManageApi extends BaseApiController {
4
5
  protected ctx: Context;
5
6
  getAccountRoleList(): Promise<import("../../libs/crud-pro/models/ExecuteContext").ExecuteContext>;
6
7
  getAccountRoleOne(): Promise<import("../../libs/crud-pro/models/ExecuteContext").ExecuteContext>;
7
- createAccountRole(): Promise<import("../../libs/crud-pro/models/ExecuteContext").ExecuteContext>;
8
+ createAccountRole(): Promise<import("../../libs/crud-pro/models/ExecuteContext").ExecuteContext | CommonResult>;
8
9
  updateAccountRole(): Promise<import("../../libs/crud-pro/models/ExecuteContext").ExecuteContext>;
9
10
  deleteAccountRole(): Promise<import("../../libs/crud-pro/models/ExecuteContext").ExecuteContext>;
10
11
  }
@@ -18,6 +18,9 @@ const cacherefresh_middleware_1 = require("../../middleware/cacherefresh.middlew
18
18
  const bizmodels_1 = require("../../models/bizmodels");
19
19
  const SystemPerm_1 = require("../../models/SystemPerm");
20
20
  const CurdMixUtils_1 = require("../../service/curd/CurdMixUtils");
21
+ const SystemTables_1 = require("../../models/SystemTables");
22
+ const global_config_1 = require("../../libs/global-config/global-config");
23
+ const common_dto_1 = require("../../libs/utils/common-dto");
21
24
  let AccountRoleManageApi = class AccountRoleManageApi extends BaseApiController_1.BaseApiController {
22
25
  async getAccountRoleList() {
23
26
  return this.executeSysSimpleSQL('sys_perm_user_role', keys_1.KeysOfSimpleSQL.SIMPLE_QUERY_PAGE, {
@@ -40,6 +43,37 @@ let AccountRoleManageApi = class AccountRoleManageApi extends BaseApiController_
40
43
  return this.executeSysSimpleSQL('sys_perm_user_role', keys_1.KeysOfSimpleSQL.SIMPLE_QUERY_ONE);
41
44
  }
42
45
  async createAccountRole() {
46
+ var _a, _b;
47
+ const { SystemDbName, SystemDbType } = global_config_1.GLOBAL_STATIC_CONFIG.getConfig();
48
+ const body = this.ctx.request.body;
49
+ const accountId = (_a = body.data) === null || _a === void 0 ? void 0 : _a.account_id;
50
+ const roleCode = (_b = body.data) === null || _b === void 0 ? void 0 : _b.role_code;
51
+ // 校验 account_id 是否存在
52
+ if (accountId) {
53
+ const accountInfoRes = await this.curdMixService.getQuickCrud({
54
+ sqlDatabase: SystemDbName,
55
+ sqlDbType: SystemDbType,
56
+ sqlTable: SystemTables_1.SystemTables.sys_user_account,
57
+ }).isExist({
58
+ condition: { account_id: accountId }
59
+ });
60
+ if (!accountInfoRes.exists) {
61
+ return common_dto_1.CommonResult.errorRes(`账户ID '${accountId}' 不存在`);
62
+ }
63
+ }
64
+ // 校验 role_code 是否存在
65
+ if (roleCode) {
66
+ const roleInfoRes = await this.curdMixService.getQuickCrud({
67
+ sqlDatabase: SystemDbName,
68
+ sqlDbType: SystemDbType,
69
+ sqlTable: SystemTables_1.SystemTables.sys_perm_role,
70
+ }).isExist({
71
+ condition: { role_code: roleCode }
72
+ });
73
+ if (!roleInfoRes.exists) {
74
+ return common_dto_1.CommonResult.errorRes(`角色编码 '${roleCode}' 不存在`);
75
+ }
76
+ }
43
77
  return this.executeSysSimpleSQL('sys_perm_user_role', keys_1.KeysOfSimpleSQL.SIMPLE_INSERT, {
44
78
  validateCfg: {
45
79
  'data.account_id': [keys_1.KeysOfValidators.REQUIRED, keys_1.KeysOfValidators.STRING],
@@ -13,13 +13,13 @@ exports.UserAccountManageApi = void 0;
13
13
  const core_1 = require("@midwayjs/core");
14
14
  const keys_1 = require("../../libs/crud-pro/models/keys");
15
15
  const BaseApiController_1 = require("../base/BaseApiController");
16
- const functions_1 = require("../../libs/utils/functions");
17
16
  const permission_middleware_1 = require("../../middleware/permission.middleware");
18
17
  const SystemPerm_1 = require("../../models/SystemPerm");
19
18
  const exceptions_1 = require("../../libs/crud-pro/exceptions");
20
19
  const SystemTables_1 = require("../../models/SystemTables");
21
20
  const global_config_1 = require("../../libs/global-config/global-config");
22
21
  const common_dto_1 = require("../../libs/utils/common-dto");
22
+ const MixinUtils_1 = require("../../libs/crud-pro/utils/MixinUtils");
23
23
  const accountNameBlacklist = new Set(['sa', 'root', 'admin', 'superadmin', 'administrator', 'sys', 'sysop', 'schedule_user']);
24
24
  function checkAccountCreateBlacklist(value) {
25
25
  if (!value) {
@@ -73,16 +73,17 @@ let UserAccountManageApi = class UserAccountManageApi extends BaseApiController_
73
73
  }
74
74
  async createUserAccount() {
75
75
  const body = this.ctx.request.body;
76
- body.data.account_id = (0, functions_1.createUniqueId)();
77
76
  const { generateUserAccountId } = global_config_1.GLOBAL_STATIC_CONFIG.getConfig();
78
77
  if (typeof generateUserAccountId === 'function') {
79
78
  body.data.account_id = await generateUserAccountId(body.data);
80
79
  }
80
+ else {
81
+ body.data.account_id = MixinUtils_1.MixinUtils.generateSnowflakeId();
82
+ }
81
83
  const result = await this.executeSysSimpleSQL(SystemTables_1.SystemTables.sys_user_account, keys_1.KeysOfSimpleSQL.SIMPLE_INSERT, {
82
84
  validateCfg: {
83
85
  'data.login_name': LOGIN_NAME_VALIDATE,
84
86
  'data.nick_name': [keys_1.KeysOfValidators.REQUIRED, keys_1.KeysOfValidators.STRING, 'length:2,20', checkAccountCreateBlacklist],
85
- 'data.avatar': [keys_1.KeysOfValidators.REQUIRED, keys_1.KeysOfValidators.STRING],
86
87
  },
87
88
  });
88
89
  return result;
@@ -8,6 +8,7 @@ export declare enum KeysOfFunCtx {
8
8
  CURRENT_VALUE = "$current",
9
9
  VISITOR = "visitor",
10
10
  VISITOR_ATTR = "visitor.",
11
+ VISITOR_AVATAR = "visitor.avatar",
11
12
  REQUEST = "req.",
12
13
  RESULT = "res."
13
14
  }
@@ -13,6 +13,7 @@ var KeysOfFunCtx;
13
13
  KeysOfFunCtx["CURRENT_VALUE"] = "$current";
14
14
  KeysOfFunCtx["VISITOR"] = "visitor";
15
15
  KeysOfFunCtx["VISITOR_ATTR"] = "visitor.";
16
+ KeysOfFunCtx["VISITOR_AVATAR"] = "visitor.avatar";
16
17
  KeysOfFunCtx["REQUEST"] = "req.";
17
18
  KeysOfFunCtx["RESULT"] = "res.";
18
19
  })(KeysOfFunCtx = exports.KeysOfFunCtx || (exports.KeysOfFunCtx = {}));
@@ -82,8 +82,8 @@ class CrudProExecuteFuncService extends CrudProServiceBase_1.CrudProServiceBase
82
82
  if (pickString.startsWith(keys_1.KeysOfFunCtx.VISITOR_ATTR)) {
83
83
  const reqModel = funcCtx === null || funcCtx === void 0 ? void 0 : funcCtx.reqModel;
84
84
  const attrValue = _.get(reqModel, pickString); // visitor.nickName
85
- if (isNil(attrValue)) {
86
- throw new exceptions_1.CommonException(exceptions_1.Exceptions.RUN_PICK_ERR_VISITOR_NULL, '获取用户ID失败, 可能用户没有登陆');
85
+ if (isNil(attrValue) && ![`${keys_1.KeysOfFunCtx.VISITOR_AVATAR}`].includes(pickString)) {
86
+ throw new exceptions_1.CommonException(exceptions_1.Exceptions.RUN_PICK_ERR_VISITOR_NULL, '获取用户字段${pickString}失败, 可能用户没有登陆');
87
87
  }
88
88
  return attrValue;
89
89
  }
@@ -43,6 +43,9 @@ class CrudProFieldUpdateService extends CrudProServiceBase_1.CrudProServiceBase
43
43
  }
44
44
  catch (e) {
45
45
  this.logger.error('getUpdateNewValueByCfg->executeFuncCfg', e);
46
+ if (e instanceof exceptions_1.CommonException) {
47
+ throw e;
48
+ }
46
49
  throw new exceptions_1.CommonException(exceptions_1.Exceptions.UPDATE_EXCEPTION, key + '.' + e);
47
50
  }
48
51
  return result;
@@ -142,11 +142,11 @@ class CrudProTableMetaService extends CrudProServiceBase_1.CrudProServiceBase {
142
142
  async loadMySQLAllTableInfos(query) {
143
143
  const dbUtil = { sqlDatabase: query.sqlDatabase, sqlDbType: query.sqlDbType };
144
144
  // 获取物理表
145
- const tableSql = `SHOW TABLES FROM ${query.sqlDatabase}`;
145
+ const tableSql = `SHOW TABLES`;
146
146
  const tableRes = await this.executeUnsafeQuery(dbUtil, tableSql);
147
147
  const tableNames = (tableRes.rows || []).map((row) => Object.values(row)[0]);
148
148
  // 获取视图
149
- const viewSql = `SHOW FULL TABLES FROM ${query.sqlDatabase} WHERE Table_type = 'VIEW'`;
149
+ const viewSql = `SHOW FULL TABLES WHERE Table_type = 'VIEW'`;
150
150
  const viewRes = await this.executeUnsafeQuery(dbUtil, viewSql);
151
151
  const viewNames = (viewRes.rows || []).map((row) => Object.values(row)[0]);
152
152
  const tables = [
@@ -2,6 +2,7 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.toDatabaseNameStr = exports.parseDatabaseName = exports.SPLIT_CONST = void 0;
4
4
  const keys_1 = require("../models/keys");
5
+ const global_config_1 = require("../../global-config/global-config");
5
6
  const SPLIT_CONST = '_________';
6
7
  exports.SPLIT_CONST = SPLIT_CONST;
7
8
  const PREFIX_MYSQL = `${keys_1.SqlDbType.mysql}${SPLIT_CONST}`;
@@ -27,6 +28,16 @@ function parseDatabaseName(databaseName) {
27
28
  dbName: dbNameArr[1],
28
29
  };
29
30
  }
31
+ const gConfig = global_config_1.GLOBAL_STATIC_CONFIG.getConfig();
32
+ // 访问系统库
33
+ if (databaseName === 'fatcms' || databaseName === gConfig.SystemDbName) {
34
+ if (dbNameArr.length === 1) {
35
+ return {
36
+ dbType: gConfig.SystemDbType,
37
+ dbName: dbNameArr[0],
38
+ };
39
+ }
40
+ }
30
41
  if (dbNameArr.length === 1) {
31
42
  return {
32
43
  dbType: keys_1.SqlDbType.mysql,
@@ -8,7 +8,7 @@ const DateTimeUtils = {
8
8
  return Date.now();
9
9
  },
10
10
  getCurrentTimeString() {
11
- return moment().format('YYYY-MM-DD HH:mm:ss');
11
+ return moment().format('YYYY-MM-DD HH:mm:ss.SSS');
12
12
  },
13
13
  // 以秒为单位的时间戳
14
14
  getCurrentTimeStampSecond() {
@@ -8,4 +8,11 @@ declare function getCurrentFullMoment(): string;
8
8
  * @returns 仅包含 ASCII 字符返回 true,否则返回 false;非字符串输入返回 false
9
9
  */
10
10
  declare function isOnlyAscii(str: string): boolean;
11
- export { createUniqueId, parseJsonObject, parseStringTrimArray, getCurrentFullMoment, isOnlyAscii };
11
+ /**
12
+ * 如果entity不存在删除属性或属性为0,表示未删除
13
+ * @param entity
14
+ * @returns
15
+ */
16
+ declare function isEntityDeleted(entity: any): boolean;
17
+ declare function isEntityOK(entity: any): boolean;
18
+ export { createUniqueId, parseJsonObject, parseStringTrimArray, getCurrentFullMoment, isOnlyAscii, isEntityDeleted, isEntityOK };
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.isOnlyAscii = exports.getCurrentFullMoment = exports.parseStringTrimArray = exports.parseJsonObject = exports.createUniqueId = void 0;
3
+ exports.isEntityOK = exports.isEntityDeleted = exports.isOnlyAscii = exports.getCurrentFullMoment = exports.parseStringTrimArray = exports.parseJsonObject = exports.createUniqueId = void 0;
4
4
  const md5 = require("md5");
5
5
  const moment = require("moment");
6
6
  let createUniqueIdIndex = 0;
@@ -97,3 +97,28 @@ function isOnlyAscii(str) {
97
97
  return true;
98
98
  }
99
99
  exports.isOnlyAscii = isOnlyAscii;
100
+ /**
101
+ * 如果entity不存在删除属性或属性为0,表示未删除
102
+ * @param entity
103
+ * @returns
104
+ */
105
+ function isEntityDeleted(entity) {
106
+ if (!entity) {
107
+ return true;
108
+ }
109
+ const deletedAt = Number(entity.deleted_at);
110
+ if (deletedAt && deletedAt > 0) {
111
+ return true;
112
+ }
113
+ const deletedAt2 = Number(entity.deletedAt);
114
+ if (deletedAt2 && deletedAt2 > 0) {
115
+ return true;
116
+ }
117
+ return false;
118
+ }
119
+ exports.isEntityDeleted = isEntityDeleted;
120
+ function isEntityOK(entity) {
121
+ const isOK = (entity === null || entity === void 0 ? void 0 : entity.status) === 1 || (entity === null || entity === void 0 ? void 0 : entity.status) === '1';
122
+ return isOK && !isEntityDeleted(entity);
123
+ }
124
+ exports.isEntityOK = isEntityOK;
@@ -119,7 +119,7 @@ let AuthService = class AuthService extends BaseService_1.BaseService {
119
119
  const { publicKey, privateKey } = await AsymmetricCrypto_1.AsymmetricCrypto.generateKeyPair();
120
120
  const sessionInfo = {
121
121
  nickName: userAccount.nick_name,
122
- avatar: userAccount.avatar,
122
+ avatar: userAccount.avatar || "",
123
123
  roleCodes,
124
124
  functionCodes,
125
125
  loginName,
@@ -68,6 +68,9 @@ let CrudStdService = class CrudStdService extends ApiBaseService_1.ApiBaseServic
68
68
  const stdCrudCfgObj = appInfo.stdCrudCfgObj;
69
69
  //删除策略
70
70
  const deleteStrategy = _.get(stdCrudCfgObj, 'othersSetting.values.deleteStrategy');
71
+ // 自动
72
+ const enableStandardUpdateCfg = _.get(stdCrudCfgObj, 'othersSetting.values.enableStandardUpdateCfg');
73
+ const enableStandardUpdateCfgCondition = _.get(stdCrudCfgObj, 'othersSetting.values.enableStandardUpdateCfgCondition');
71
74
  const databaseName = stdCrudCfgObj.tableBaseInfo.databaseName;
72
75
  const { dbType, dbName } = (0, DatabaseName_1.parseDatabaseName)(databaseName);
73
76
  const cfgModel = {
@@ -76,7 +79,9 @@ let CrudStdService = class CrudStdService extends ApiBaseService_1.ApiBaseServic
76
79
  sqlTable: getExecuteTableNameBySettingKey(appInfo, stdAction, sqlSimpleName),
77
80
  sqlSimpleName,
78
81
  updateCfg: {},
79
- enableSoftDelete: deleteStrategy === 'soft' // 软删除
82
+ enableSoftDelete: deleteStrategy === 'soft',
83
+ enableStandardUpdateCfg: Array.isArray(enableStandardUpdateCfg) && enableStandardUpdateCfg.length > 0 ? enableStandardUpdateCfg : ['by', 'at'],
84
+ enableStandardUpdateCfgCondition: Array.isArray(enableStandardUpdateCfgCondition) && enableStandardUpdateCfgCondition.length > 0 ? enableStandardUpdateCfgCondition : false, // 默认为false
80
85
  };
81
86
  // 接口返回最大值
82
87
  const maxLimit = _.get(stdCrudCfgObj, 'othersSetting.values.maxLimit');
@@ -30,7 +30,7 @@ function fixCfgModel(cfgModel) {
30
30
  return enableStandardUpdateCfg;
31
31
  }
32
32
  // update 、insert 添加 by
33
- return ['by']; // 创建/修改语句默认添加by
33
+ return ['by', 'at']; // 创建/修改语句默认添加by
34
34
  };
35
35
  const getConditionCfgArray = () => {
36
36
  // 明确有配置
@@ -47,10 +47,12 @@ function fixCfgModel(cfgModel) {
47
47
  const mapping = {
48
48
  'condition.created_by': { contextAsString: bizmodels_1.CTX_VISITOR_ID },
49
49
  'condition.created_account_type': { contextAsString: bizmodels_1.CTX_VISITOR_ACCOUNT_TYPE },
50
+ 'data.created_at': { functionName: 'getCurrentTimeString' },
50
51
  'data.created_by': { contextAsString: bizmodels_1.CTX_VISITOR_ID },
51
52
  'data.created_avatar': { contextAsString: bizmodels_1.CTX_VISITOR_AVATAR },
52
53
  'data.created_nickname': { contextAsString: bizmodels_1.CTX_VISITOR_NICKNAME },
53
54
  'data.created_account_type': { contextAsString: bizmodels_1.CTX_VISITOR_ACCOUNT_TYPE },
55
+ 'data.modified_at': { functionName: 'getCurrentTimeString' },
54
56
  'data.modified_by': { contextAsString: bizmodels_1.CTX_VISITOR_ID },
55
57
  'data.modified_avatar': { contextAsString: bizmodels_1.CTX_VISITOR_AVATAR },
56
58
  'data.modified_nickname': { contextAsString: bizmodels_1.CTX_VISITOR_NICKNAME },
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "midway-fatcms",
3
- "version": "0.0.11",
3
+ "version": "0.0.12",
4
4
  "description": "This is a midway component sample",
5
5
  "main": "dist/index.js",
6
6
  "typings": "index.d.ts",
@@ -1,4 +1,4 @@
1
- import { Configuration, App, Inject } from '@midwayjs/core';
1
+ import { Configuration, App } from '@midwayjs/core';
2
2
  import * as koa from '@midwayjs/koa';
3
3
  import * as validate from '@midwayjs/validate';
4
4
  import * as info from '@midwayjs/info';
@@ -45,9 +45,6 @@ export class ContainerLifeCycle {
45
45
  @App()
46
46
  app: koa.Application;
47
47
 
48
- @Inject()
49
- redisService: RedisService;
50
-
51
48
  async onConfigLoad(): Promise<any> {
52
49
  const config = this.app.getConfig();
53
50
  const logger = this.app.getLogger();
@@ -110,13 +107,23 @@ export class ContainerLifeCycle {
110
107
  * 会通过 Redis 广播通知所有节点刷新缓存。
111
108
  */
112
109
  private async initCacheRefreshRedis() {
113
- try {
114
- initCacheRefreshPublishClient(this.redisService);
115
- initCacheRefreshSubscriber(this.redisService);
116
- } catch (e) {
117
- // Redis 未配置或不可用时,降级为单节点模式(仅刷新本节点缓存)
118
- this.app.getLogger().warn('ContainerLifeCycle ==> initCacheRefreshRedis Redis不可用,降级为单节点模式:', e.message);
119
- }
110
+ setTimeout(async () => {
111
+
112
+ const app = this.app;
113
+ const ctx: koa.IMidwayKoaContext = app.createAnonymousContext();
114
+ const redisService = await ctx.requestContext.getAsync(RedisService);
115
+
116
+ try {
117
+ initCacheRefreshPublishClient(redisService);
118
+ initCacheRefreshSubscriber(redisService);
119
+ this.app.getLogger().info('ContainerLifeCycle ==> initCacheRefreshRedis success');
120
+ } catch (e) {
121
+ // Redis 未配置或不可用时,降级为单节点模式(仅刷新本节点缓存)
122
+ this.app.getLogger().warn('ContainerLifeCycle ==> initCacheRefreshRedis Redis不可用,降级为单节点模式:', e.message);
123
+ }
124
+
125
+ }, 1000 * 60);
126
+
120
127
  }
121
128
 
122
129
  /**
@@ -10,6 +10,7 @@ import { CurdMixService } from '@/service/curd/CurdMixService';
10
10
  import { IRequestCfgModel2 } from '@/models/bizmodels';
11
11
  import { parseDatabaseName } from '@/libs/crud-pro/utils/DatabaseName';
12
12
  import { GLOBAL_STATIC_CONFIG } from '@/libs/global-config/global-config';
13
+ import { isEntityOK } from '@/libs/utils/functions';
13
14
 
14
15
  const QUERY_AS_LIST = ['condition', 'data'];
15
16
 
@@ -53,7 +54,7 @@ export class CrudMtdGatewayController extends ApiBaseService {
53
54
  }
54
55
 
55
56
  const methodInfo = (await this.curdProService.getCachedCfgByMethod(methodCode)) as any;
56
- if (!methodInfo || methodInfo.status !== 1 || methodInfo.deletedAt || methodInfo.deleted_at) {
57
+ if (!methodInfo || !isEntityOK(methodInfo)) {
57
58
  return CommonResult.errorRes('接口不存在或已下线: ' + methodCode);
58
59
  }
59
60
 
@@ -31,9 +31,8 @@ function getPathConfig(ossName: string) {
31
31
  };
32
32
  }
33
33
 
34
- // 404列表缓存
34
+ // 404列表缓存: 只再内存中记录。重启后继续
35
35
  let notFoundList = new Set();
36
- let notFoundListIsInit = false;
37
36
 
38
37
  /**
39
38
  * 静态文件代理功能
@@ -47,33 +46,11 @@ export class StaticController extends BaseApiController {
47
46
  const app = ANONYMOUS_CONTEXT.getApp();
48
47
  const appDir = app.getAppDir();
49
48
  const localDir = path.join(appDir, './public/static'); // 本地文件存储目录
50
- const notFoundListFile = path.join(localDir, '__404__list.json'); // 404列表文件
51
49
  return {
52
50
  localDir,
53
- notFoundListFile,
54
51
  };
55
52
  }
56
53
 
57
- private async initNotFoundList() {
58
- if (notFoundListIsInit) {
59
- return;
60
- }
61
- notFoundListIsInit = true;
62
-
63
- const { localDir, notFoundListFile } = this.getLocalPaths();
64
- try {
65
- // 确保本地存储目录存在
66
- await fs.mkdir(localDir, { recursive: true });
67
- const data = await fs.readFile(notFoundListFile, 'utf8');
68
-
69
- notFoundList = new Set(JSON.parse(data));
70
- console.log(`已加载${notFoundList.size}个404记录`);
71
- } catch (error) {
72
- if (error.code !== 'ENOENT') {
73
- console.error('读取404列表失败:', error);
74
- }
75
- }
76
- }
77
54
 
78
55
  private async responseLocalFile(localFilePath: string, localHeaderPath: string, relativePath: string): Promise<boolean> {
79
56
  const headers = {
@@ -87,13 +64,18 @@ export class StaticController extends BaseApiController {
87
64
  if (stats.isFile()) {
88
65
  const headerDataStr = await fs.readFile(localHeaderPath, 'utf8');
89
66
  const headerData = parseJsonObject(headerDataStr);
90
- if (headerData) {
67
+
68
+ if (headerData && headerData['last-modified']) {
91
69
  headers['last-modified'] = headerData['last-modified'];
70
+ }
71
+
72
+ if (headerData && headerData['etag']) {
92
73
  headers['etag'] = headerData['etag'];
93
74
  }
75
+
94
76
  }
95
77
  } catch (error) {
96
- this.logDebug('[StaticController]responseLocalFile not found header file => ' + localHeaderPath );
78
+ this.logDebug('[StaticController]responseLocalFile not found header file => ' + localHeaderPath);
97
79
  }
98
80
 
99
81
  try {
@@ -125,7 +107,7 @@ export class StaticController extends BaseApiController {
125
107
 
126
108
  @All('/:ossName/:relativePath+')
127
109
  async proxyStaticFile(@Param('ossName') ossName: string, @Param('relativePath') relativePath: string) {
128
- await this.initNotFoundList();
110
+
129
111
  const { localDir } = this.getLocalPaths();
130
112
  const PATH_CONFIG = getPathConfig(ossName);
131
113
 
@@ -161,9 +143,10 @@ export class StaticController extends BaseApiController {
161
143
  return;
162
144
  }
163
145
  } else if (remoteRes.statusCode === 404) {
146
+
164
147
  // 记录404并返回
165
148
  notFoundList.add(relativePath);
166
- await this.saveNotFoundList();
149
+
167
150
  this.logInfo(`[StaticController]远程文件不存在,已记录404: ${relativePath}`);
168
151
  this.ctx.status = 404;
169
152
  return this.ctx.render('500', { errorMsg: ' 远程文件不存在,已记录404' });
@@ -185,19 +168,7 @@ export class StaticController extends BaseApiController {
185
168
  return this.ctx.render('500', { errorMsg: '未知异常' });
186
169
  }
187
170
 
188
- // 保存404列表
189
- async saveNotFoundList() {
190
- const { notFoundListFile } = this.getLocalPaths();
191
- try {
192
- // 确保存储404列表的目录存在
193
- const dir = path.dirname(notFoundListFile);
194
- await fs.mkdir(dir, { recursive: true });
195
171
 
196
- await fs.writeFile(notFoundListFile, JSON.stringify(Array.from(notFoundList), null), 'utf8');
197
- } catch (error) {
198
- this.logError('[StaticController]保存404列表失败:', error);
199
- }
200
- }
201
172
 
202
173
  // 封装https请求为异步函数
203
174
  async httpsGet(url: string): Promise<HttpGetRes> {
@@ -7,6 +7,9 @@ import { refreshCache } from '@/middleware/cacherefresh.middleware';
7
7
  import { CacheNameEnum } from '@/models/bizmodels';
8
8
  import { SystemFuncCode } from '@/models/SystemPerm';
9
9
  import { RelatedType } from '@/service/curd/CurdMixUtils';
10
+ import { SystemTables } from '@/models/SystemTables';
11
+ import { GLOBAL_STATIC_CONFIG } from '@/libs/global-config/global-config';
12
+ import { CommonResult } from '@/libs/utils/common-dto';
10
13
 
11
14
  @Controller('/ns/api/manage/accountRole', { middleware: [checkPermission(SystemFuncCode.AccountRoleManageRead)] })
12
15
  export class AccountRoleManageApi extends BaseApiController {
@@ -39,6 +42,41 @@ export class AccountRoleManageApi extends BaseApiController {
39
42
 
40
43
  @Post('/createAccountRole', { middleware: [checkPermission(SystemFuncCode.AccountRoleManageWrite), refreshCache(CacheNameEnum.CurdMixByAccount)] })
41
44
  async createAccountRole() {
45
+ const { SystemDbName, SystemDbType } = GLOBAL_STATIC_CONFIG.getConfig();
46
+ const body = this.ctx.request.body as any;
47
+ const accountId = body.data?.account_id;
48
+ const roleCode = body.data?.role_code;
49
+
50
+ // 校验 account_id 是否存在
51
+ if (accountId) {
52
+ const accountInfoRes = await this.curdMixService.getQuickCrud({
53
+ sqlDatabase: SystemDbName,
54
+ sqlDbType: SystemDbType,
55
+ sqlTable: SystemTables.sys_user_account,
56
+ }).isExist({
57
+ condition: { account_id: accountId }
58
+ });
59
+
60
+ if (!accountInfoRes.exists) {
61
+ return CommonResult.errorRes(`账户ID '${accountId}' 不存在`);
62
+ }
63
+ }
64
+
65
+ // 校验 role_code 是否存在
66
+ if (roleCode) {
67
+ const roleInfoRes = await this.curdMixService.getQuickCrud({
68
+ sqlDatabase: SystemDbName,
69
+ sqlDbType: SystemDbType,
70
+ sqlTable: SystemTables.sys_perm_role,
71
+ }).isExist({
72
+ condition: { role_code: roleCode }
73
+ });
74
+
75
+ if (!roleInfoRes.exists) {
76
+ return CommonResult.errorRes(`角色编码 '${roleCode}' 不存在`);
77
+ }
78
+ }
79
+
42
80
  return this.executeSysSimpleSQL('sys_perm_user_role', KeysOfSimpleSQL.SIMPLE_INSERT, {
43
81
  validateCfg: {
44
82
  'data.account_id': [KeysOfValidators.REQUIRED, KeysOfValidators.STRING],
@@ -2,13 +2,13 @@ import { Controller, Inject, Post } from '@midwayjs/core';
2
2
  import { Context } from '@midwayjs/koa';
3
3
  import { KeysOfSimpleSQL, KeysOfValidators } from '../../libs/crud-pro/models/keys';
4
4
  import { BaseApiController } from '../base/BaseApiController';
5
- import { createUniqueId } from '../../libs/utils/functions';
6
5
  import { checkPermission } from '../../middleware/permission.middleware';
7
6
  import { SystemFuncCode } from '../../models/SystemPerm';
8
7
  import { CommonException, Exceptions } from '../../libs/crud-pro/exceptions';
9
8
  import { SystemTables } from '../../models/SystemTables';
10
9
  import { GLOBAL_STATIC_CONFIG } from '@/libs/global-config/global-config';
11
10
  import { CommonResult } from "@/libs/utils/common-dto";
11
+ import { MixinUtils } from '@/libs/crud-pro/utils/MixinUtils';
12
12
 
13
13
  const accountNameBlacklist = new Set(['sa', 'root', 'admin', 'superadmin', 'administrator', 'sys', 'sysop', 'schedule_user']);
14
14
  function checkAccountCreateBlacklist(value: string) {
@@ -80,23 +80,24 @@ export class UserAccountManageApi extends BaseApiController {
80
80
  @Post('/createUserAccount', { middleware: [checkPermission(SystemFuncCode.UserAccountMangeWrite)] })
81
81
  async createUserAccount() {
82
82
  const body = this.ctx.request.body as any;
83
- body.data.account_id = createUniqueId();
84
83
 
85
84
  const { generateUserAccountId } = GLOBAL_STATIC_CONFIG.getConfig();
86
85
  if (typeof generateUserAccountId === 'function') {
87
86
  body.data.account_id = await generateUserAccountId(body.data);
87
+ } else {
88
+ body.data.account_id = MixinUtils.generateSnowflakeId();
88
89
  }
89
90
 
90
91
  const result = await this.executeSysSimpleSQL(SystemTables.sys_user_account, KeysOfSimpleSQL.SIMPLE_INSERT, {
91
92
  validateCfg: {
92
93
  'data.login_name': LOGIN_NAME_VALIDATE,
93
94
  'data.nick_name': [KeysOfValidators.REQUIRED, KeysOfValidators.STRING, 'length:2,20', checkAccountCreateBlacklist],
94
- 'data.avatar': [KeysOfValidators.REQUIRED, KeysOfValidators.STRING],
95
95
  },
96
96
  });
97
97
  return result;
98
98
  }
99
99
 
100
+
100
101
  @Post('/deleteUserAccount', { middleware: [checkPermission(SystemFuncCode.UserAccountMangeWrite)] })
101
102
  async deleteUserAccount() {
102
103
  const result = await this.executeSysSimpleSQL(SystemTables.sys_user_account, KeysOfSimpleSQL.SIMPLE_DELETE, {
@@ -9,6 +9,7 @@ export enum KeysOfFunCtx {
9
9
  CURRENT_VALUE = '$current',
10
10
  VISITOR = 'visitor',
11
11
  VISITOR_ATTR = 'visitor.',
12
+ VISITOR_AVATAR = 'visitor.avatar',
12
13
  REQUEST = 'req.',
13
14
  RESULT = 'res.',
14
15
  }
@@ -95,8 +95,9 @@ class CrudProExecuteFuncService extends CrudProServiceBase {
95
95
  if (pickString.startsWith(KeysOfFunCtx.VISITOR_ATTR)) {
96
96
  const reqModel = funcCtx?.reqModel;
97
97
  const attrValue = _.get(reqModel, pickString); // visitor.nickName
98
- if (isNil(attrValue)) {
99
- throw new CommonException(Exceptions.RUN_PICK_ERR_VISITOR_NULL, '获取用户ID失败, 可能用户没有登陆');
98
+
99
+ if (isNil(attrValue) && ![`${KeysOfFunCtx.VISITOR_AVATAR}`].includes(pickString)) {
100
+ throw new CommonException(Exceptions.RUN_PICK_ERR_VISITOR_NULL, '获取用户字段${pickString}失败, 可能用户没有登陆');
100
101
  }
101
102
  return attrValue;
102
103
  }
@@ -49,7 +49,12 @@ class CrudProFieldUpdateService extends CrudProServiceBase {
49
49
 
50
50
  result = this.serviceHub.executeFuncCfg(updateCfg, exeFunCtx);
51
51
  } catch (e) {
52
+
52
53
  this.logger.error('getUpdateNewValueByCfg->executeFuncCfg', e);
54
+ if (e instanceof CommonException) {
55
+ throw e;
56
+ }
57
+
53
58
  throw new CommonException(Exceptions.UPDATE_EXCEPTION, key + '.' + e);
54
59
  }
55
60
 
@@ -166,12 +166,12 @@ class CrudProTableMetaService extends CrudProServiceBase {
166
166
  const dbUtil = { sqlDatabase: query.sqlDatabase, sqlDbType: query.sqlDbType } as IExecuteUnsafeQueryCtx;
167
167
 
168
168
  // 获取物理表
169
- const tableSql = `SHOW TABLES FROM ${query.sqlDatabase}`;
169
+ const tableSql = `SHOW TABLES`;
170
170
  const tableRes = await this.executeUnsafeQuery(dbUtil, tableSql);
171
171
  const tableNames: string[] = (tableRes.rows || []).map((row: any) => Object.values(row)[0] as string);
172
172
 
173
173
  // 获取视图
174
- const viewSql = `SHOW FULL TABLES FROM ${query.sqlDatabase} WHERE Table_type = 'VIEW'`;
174
+ const viewSql = `SHOW FULL TABLES WHERE Table_type = 'VIEW'`;
175
175
  const viewRes = await this.executeUnsafeQuery(dbUtil, viewSql);
176
176
  const viewNames: string[] = (viewRes.rows || []).map((row: any) => Object.values(row)[0] as string);
177
177
 
@@ -1,4 +1,5 @@
1
1
  import { SqlDbType } from '../models/keys';
2
+ import { GLOBAL_STATIC_CONFIG } from '../../global-config/global-config'
2
3
 
3
4
  interface IDbTypeAndName {
4
5
  dbType: SqlDbType;
@@ -35,6 +36,17 @@ function parseDatabaseName(databaseName: string): IDbTypeAndName {
35
36
  };
36
37
  }
37
38
 
39
+ const gConfig = GLOBAL_STATIC_CONFIG.getConfig();
40
+ // 访问系统库
41
+ if (databaseName === 'fatcms' || databaseName === gConfig.SystemDbName) {
42
+ if (dbNameArr.length === 1) {
43
+ return {
44
+ dbType: gConfig.SystemDbType,
45
+ dbName: dbNameArr[0],
46
+ };
47
+ }
48
+ }
49
+
38
50
  if (dbNameArr.length === 1) {
39
51
  return {
40
52
  dbType: SqlDbType.mysql, // 默认
@@ -6,7 +6,7 @@ const DateTimeUtils = {
6
6
  return Date.now();
7
7
  },
8
8
  getCurrentTimeString(): string {
9
- return moment().format('YYYY-MM-DD HH:mm:ss');
9
+ return moment().format('YYYY-MM-DD HH:mm:ss.SSS');
10
10
  },
11
11
  // 以秒为单位的时间戳
12
12
  getCurrentTimeStampSecond(): number {
@@ -7,7 +7,7 @@ function createUniqueId(): string {
7
7
  if (createUniqueIdIndex > Date.now()) {
8
8
  createUniqueIdIndex = 0;
9
9
  }
10
- const p1 = md5(Date.now() + '' + Math.random() + '_' + createUniqueIdIndex + '_' + Math.random());
10
+ const p1 = md5(Date.now() + '' + Math.random() + '_' + createUniqueIdIndex + '_' + Math.random());
11
11
  const p2 = `${Date.now().toString(32)}`.padStart(10, '0');
12
12
  const p3 = `${createUniqueIdIndex.toString(32)}`.padStart(10, '0');
13
13
  return p1 + p2 + p3;
@@ -106,4 +106,36 @@ function isOnlyAscii(str: string): boolean {
106
106
  }
107
107
 
108
108
 
109
- export { createUniqueId, parseJsonObject, parseStringTrimArray, getCurrentFullMoment , isOnlyAscii };
109
+ /**
110
+ * 如果entity不存在删除属性或属性为0,表示未删除
111
+ * @param entity
112
+ * @returns
113
+ */
114
+ function isEntityDeleted(entity: any): boolean {
115
+ if (!entity) {
116
+ return true;
117
+ }
118
+
119
+ const deletedAt = Number(entity.deleted_at);
120
+ if (deletedAt && deletedAt > 0) {
121
+ return true;
122
+ }
123
+
124
+ const deletedAt2 = Number(entity.deletedAt);
125
+ if (deletedAt2 && deletedAt2 > 0) {
126
+ return true;
127
+ }
128
+ return false;
129
+ }
130
+
131
+
132
+ function isEntityOK(entity: any): boolean {
133
+ const isOK = entity?.status === 1 || entity?.status === '1';
134
+ return isOK && !isEntityDeleted(entity);
135
+ }
136
+
137
+
138
+
139
+
140
+
141
+ export { createUniqueId, parseJsonObject, parseStringTrimArray, getCurrentFullMoment, isOnlyAscii, isEntityDeleted, isEntityOK };
@@ -142,8 +142,8 @@ export class AuthService extends BaseService {
142
142
  const { publicKey, privateKey } = await AsymmetricCrypto.generateKeyPair();
143
143
 
144
144
  const sessionInfo: ISessionInfo = {
145
- nickName: userAccount.nick_name,
146
- avatar: userAccount.avatar,
145
+ nickName: userAccount.nick_name ,
146
+ avatar: userAccount.avatar || "",
147
147
  roleCodes,
148
148
  functionCodes,
149
149
  loginName,
@@ -103,6 +103,9 @@ export class CrudStdService extends ApiBaseService {
103
103
 
104
104
  //删除策略
105
105
  const deleteStrategy = _.get(stdCrudCfgObj, 'othersSetting.values.deleteStrategy');
106
+ // 自动
107
+ const enableStandardUpdateCfg: string[] = _.get(stdCrudCfgObj, 'othersSetting.values.enableStandardUpdateCfg');
108
+ const enableStandardUpdateCfgCondition: string[] = _.get(stdCrudCfgObj, 'othersSetting.values.enableStandardUpdateCfgCondition');
106
109
 
107
110
  const databaseName = stdCrudCfgObj.tableBaseInfo.databaseName;
108
111
 
@@ -114,7 +117,9 @@ export class CrudStdService extends ApiBaseService {
114
117
  sqlTable: getExecuteTableNameBySettingKey(appInfo, stdAction, sqlSimpleName),
115
118
  sqlSimpleName,
116
119
  updateCfg: {},
117
- enableSoftDelete: deleteStrategy === 'soft' // 软删除
120
+ enableSoftDelete: deleteStrategy === 'soft', // 软删除
121
+ enableStandardUpdateCfg: Array.isArray(enableStandardUpdateCfg) && enableStandardUpdateCfg.length > 0 ? enableStandardUpdateCfg : ['by', 'at'], // 默认为 ['by', 'at']
122
+ enableStandardUpdateCfgCondition: Array.isArray(enableStandardUpdateCfgCondition) && enableStandardUpdateCfgCondition.length > 0 ? enableStandardUpdateCfgCondition : false, // 默认为false
118
123
  };
119
124
 
120
125
  // 接口返回最大值
@@ -34,7 +34,7 @@ function fixCfgModel(cfgModel: IRequestCfgModel2) {
34
34
  return enableStandardUpdateCfg;
35
35
  }
36
36
  // update 、insert 添加 by
37
- return ['by']; // 创建/修改语句默认添加by
37
+ return ['by', 'at']; // 创建/修改语句默认添加by
38
38
  };
39
39
 
40
40
  const getConditionCfgArray = () => {
@@ -55,11 +55,13 @@ function fixCfgModel(cfgModel: IRequestCfgModel2) {
55
55
  'condition.created_by': { contextAsString: CTX_VISITOR_ID },
56
56
  'condition.created_account_type': { contextAsString: CTX_VISITOR_ACCOUNT_TYPE },
57
57
 
58
+ 'data.created_at': { functionName: 'getCurrentTimeString' },
58
59
  'data.created_by': { contextAsString: CTX_VISITOR_ID },
59
60
  'data.created_avatar': { contextAsString: CTX_VISITOR_AVATAR },
60
61
  'data.created_nickname': { contextAsString: CTX_VISITOR_NICKNAME },
61
62
  'data.created_account_type': { contextAsString: CTX_VISITOR_ACCOUNT_TYPE },
62
63
 
64
+ 'data.modified_at': { functionName: 'getCurrentTimeString' },
63
65
  'data.modified_by': { contextAsString: CTX_VISITOR_ID },
64
66
  'data.modified_avatar': { contextAsString: CTX_VISITOR_AVATAR },
65
67
  'data.modified_nickname': { contextAsString: CTX_VISITOR_NICKNAME },