midway-fatcms 0.0.2 → 0.0.4

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 (53) hide show
  1. package/dist/libs/crud-pro/CrudPro.d.ts +14 -0
  2. package/dist/libs/crud-pro/CrudPro.js +61 -0
  3. package/dist/libs/crud-pro/interfaces.d.ts +20 -0
  4. package/dist/libs/crud-pro/models/ServiceHub.d.ts +2 -2
  5. package/dist/libs/crud-pro/models/Transaction.js +6 -1
  6. package/dist/libs/crud-pro/models/keys.d.ts +1 -0
  7. package/dist/libs/crud-pro/models/keys.js +2 -0
  8. package/dist/libs/crud-pro/services/CrudProDataFilterService.d.ts +17 -0
  9. package/dist/libs/crud-pro/services/CrudProDataFilterService.js +53 -0
  10. package/dist/libs/crud-pro/services/CrudProGenSqlCondition.d.ts +1 -0
  11. package/dist/libs/crud-pro/services/CrudProGenSqlCondition.js +30 -0
  12. package/dist/libs/crud-pro/services/CrudProOriginToExecuteSql.js +7 -1
  13. package/dist/libs/crud-pro/services/CrudProTableMetaService.d.ts +7 -4
  14. package/dist/libs/crud-pro/services/CrudProTableMetaService.js +127 -37
  15. package/dist/libs/crud-pro/services/CurdProServiceHub.d.ts +4 -2
  16. package/dist/libs/crud-pro/services/CurdProServiceHub.js +7 -2
  17. package/dist/libs/crud-pro/utils/MixinUtils.js +10 -9
  18. package/dist/middleware/forbidden.middleware.js +13 -2
  19. package/dist/middleware/global.middleware.js +1 -1
  20. package/dist/models/SystemEntities.d.ts +2 -1
  21. package/dist/models/SystemEntities.js +1 -0
  22. package/dist/service/AuthService.js +3 -0
  23. package/dist/service/base/BaseService.d.ts +1 -0
  24. package/dist/service/base/BaseService.js +4 -0
  25. package/dist/service/crudstd/CrudStdService.js +1 -1
  26. package/dist/service/proxyapi/ProxyApiService.js +22 -2
  27. package/dist/service/proxyapi/RouteHandler.d.ts +3 -2
  28. package/dist/service/proxyapi/RouteTrie.d.ts +1 -1
  29. package/dist/service/proxyapi/RouteTrie.js +1 -0
  30. package/dist/service/proxyapi/WeightedRoundRobin.d.ts +1 -1
  31. package/dist/service/proxyapi/WeightedRoundRobin.js +1 -0
  32. package/package.json +1 -1
  33. package/src/libs/crud-pro/CrudPro.ts +71 -0
  34. package/src/libs/crud-pro/interfaces.ts +22 -0
  35. package/src/libs/crud-pro/models/ServiceHub.ts +2 -2
  36. package/src/libs/crud-pro/models/Transaction.ts +5 -1
  37. package/src/libs/crud-pro/models/keys.ts +6 -2
  38. package/src/libs/crud-pro/services/CrudProDataFilterService.ts +58 -0
  39. package/src/libs/crud-pro/services/CrudProGenSqlCondition.ts +37 -0
  40. package/src/libs/crud-pro/services/CrudProOriginToExecuteSql.ts +10 -1
  41. package/src/libs/crud-pro/services/CrudProTableMetaService.ts +145 -40
  42. package/src/libs/crud-pro/services/CurdProServiceHub.ts +10 -3
  43. package/src/libs/crud-pro/utils/MixinUtils.ts +11 -10
  44. package/src/middleware/forbidden.middleware.ts +17 -7
  45. package/src/middleware/global.middleware.ts +1 -1
  46. package/src/models/SystemEntities.ts +1 -0
  47. package/src/service/AuthService.ts +3 -0
  48. package/src/service/base/BaseService.ts +5 -0
  49. package/src/service/crudstd/CrudStdService.ts +2 -1
  50. package/src/service/proxyapi/ProxyApiService.ts +22 -1
  51. package/src/service/proxyapi/RouteHandler.ts +4 -2
  52. package/src/service/proxyapi/RouteTrie.ts +1 -1
  53. package/src/service/proxyapi/WeightedRoundRobin.ts +2 -1
@@ -29,6 +29,20 @@ declare class CrudPro {
29
29
  */
30
30
  executeCrudByCfg(reqJson: IRequestModel, cfgJson: IRequestCfgModel): Promise<ExecuteContext>;
31
31
  getCachedCfgByMethod(method: string, isEnableCache: boolean): Promise<IRequestCfgModel>;
32
+ /**
33
+ * 如果是 INSERT/UPDATE 操作(sqlSimpleName 模式),根据真实表结构过滤 reqModel.data 中的字段
34
+ * 避免传入不存在的字段导致 SQL 执行报错
35
+ * 注意:只处理 sqlSimpleName 模式,不处理 sqlCfgList 模式
36
+ * @param reqModel 请求模型
37
+ * @param cfgModel 配置模型
38
+ */
39
+ private filterDataByTableMetaIfNeeded;
40
+ /**
41
+ * 判断 sqlSimpleName 是否为 INSERT/UPDATE 类型
42
+ * @param sqlSimpleName 简单 SQL 名称
43
+ * @returns 是否为 INSERT/UPDATE 类型
44
+ */
45
+ private isSimpleInsertOrUpdateType;
32
46
  private executeSQLList;
33
47
  private parseRunSqlException;
34
48
  private afterExecuteSQLList;
@@ -7,6 +7,7 @@ const CurdProServiceHub_1 = require("./services/CurdProServiceHub");
7
7
  const exceptions_1 = require("./exceptions");
8
8
  const RequestCfgModel_1 = require("./models/RequestCfgModel");
9
9
  const MixinUtils_1 = require("./utils/MixinUtils");
10
+ const keys_1 = require("./models/keys");
10
11
  class CrudPro {
11
12
  constructor() {
12
13
  this.executeContext = new ExecuteContext_1.ExecuteContext();
@@ -86,6 +87,8 @@ class CrudPro {
86
87
  const cfgModel = new RequestCfgModel_1.RequestCfgModel(cfgJson);
87
88
  exeCtx.setReqModel(reqModel);
88
89
  exeCtx.setCfgModel(cfgModel);
90
+ // 如果是 sqlSimpleName模式的 update/insert,则需要将 reqJson.data 部分根据真实的表结构进行过滤
91
+ await this.filterDataByTableMetaIfNeeded(reqModel, cfgModel);
89
92
  // 参数校验
90
93
  this.serviceHub.validateByAllow(cfgModel, reqModel);
91
94
  this.serviceHub.validateByReject(cfgModel, reqModel);
@@ -115,6 +118,64 @@ class CrudPro {
115
118
  async getCachedCfgByMethod(method, isEnableCache) {
116
119
  return this.serviceHub.getCachedCfgByMethod(method, isEnableCache);
117
120
  }
121
+ /**
122
+ * 如果是 INSERT/UPDATE 操作(sqlSimpleName 模式),根据真实表结构过滤 reqModel.data 中的字段
123
+ * 避免传入不存在的字段导致 SQL 执行报错
124
+ * 注意:只处理 sqlSimpleName 模式,不处理 sqlCfgList 模式
125
+ * @param reqModel 请求模型
126
+ * @param cfgModel 配置模型
127
+ */
128
+ async filterDataByTableMetaIfNeeded(reqModel, cfgModel) {
129
+ // 只有在有 data 数据时才需要过滤
130
+ if (!reqModel.data || Object.keys(reqModel.data).length === 0) {
131
+ return;
132
+ }
133
+ // 只处理 sqlSimpleName 模式
134
+ const sqlSimpleName = cfgModel.sqlSimpleName;
135
+ if (!sqlSimpleName) {
136
+ return;
137
+ }
138
+ // 判断是否为 INSERT/UPDATE 类型的简单 SQL
139
+ if (!this.isSimpleInsertOrUpdateType(sqlSimpleName)) {
140
+ return;
141
+ }
142
+ // 构建 SqlCfgModel 用于获取表结构
143
+ const sqlCfgModel = {
144
+ sqlTable: cfgModel.sqlTable,
145
+ sqlSchema: cfgModel.sqlSchema,
146
+ sqlDatabase: cfgModel.sqlDatabase,
147
+ sqlDbType: cfgModel.sqlDbType,
148
+ columns: cfgModel.columns,
149
+ columnsRelation: cfgModel.columnsRelation,
150
+ };
151
+ const filteredData = await this.serviceHub.filterDataByTableMeta(reqModel.data, sqlCfgModel);
152
+ // 记录被过滤的字段
153
+ const originalKeys = Object.keys(reqModel.data);
154
+ const filteredKeys = Object.keys(filteredData);
155
+ const removedKeys = originalKeys.filter(key => !filteredKeys.includes(key));
156
+ if (removedKeys.length > 0) {
157
+ this.executeContext.getLogger().info('CrudPro.filterDataByTableMetaIfNeeded: 过滤掉表中不存在的字段', {
158
+ table: cfgModel.sqlTable,
159
+ removedFields: removedKeys,
160
+ });
161
+ }
162
+ // 直接修改 reqModel.data,只保留表中存在的字段
163
+ reqModel.data = filteredData;
164
+ }
165
+ /**
166
+ * 判断 sqlSimpleName 是否为 INSERT/UPDATE 类型
167
+ * @param sqlSimpleName 简单 SQL 名称
168
+ * @returns 是否为 INSERT/UPDATE 类型
169
+ */
170
+ isSimpleInsertOrUpdateType(sqlSimpleName) {
171
+ const insertOrUpdateTypes = [
172
+ keys_1.KeysOfSimpleSQL.SIMPLE_INSERT,
173
+ keys_1.KeysOfSimpleSQL.SIMPLE_UPDATE,
174
+ keys_1.KeysOfSimpleSQL.SIMPLE_INSERT_ON_DUPLICATE_UPDATE,
175
+ keys_1.KeysOfSimpleSQL.SIMPLE_INSERT_OR_UPDATE,
176
+ ];
177
+ return insertOrUpdateTypes.includes(sqlSimpleName);
178
+ }
118
179
  async executeSQLList() {
119
180
  try {
120
181
  await this.serviceHub.executeSqlCfgModels();
@@ -8,9 +8,19 @@ export interface ICrudProCfg {
8
8
  sysConfigTableName?: string;
9
9
  tableMetaCacheTime?: number;
10
10
  }
11
+ export interface ITableColumn {
12
+ name: string;
13
+ type: string;
14
+ isNullable: boolean;
15
+ isPrimaryKey?: boolean;
16
+ defaultValue?: any;
17
+ maxLength?: number;
18
+ comment?: string;
19
+ }
11
20
  export interface ITableMeta {
12
21
  expiredTime: number;
13
22
  tableColumns: string[];
23
+ columnDetails?: ITableColumn[];
14
24
  }
15
25
  export interface IConnectionPool {
16
26
  dbType: SqlDbType;
@@ -171,5 +181,15 @@ export interface IExecuteUnsafeQueryCtx {
171
181
  sqlTable: string;
172
182
  sqlDatabase: string;
173
183
  sqlDbType: SqlDbType;
184
+ sqlSchema?: string;
185
+ }
186
+ /**
187
+ * 表结构元数据查询参数
188
+ */
189
+ export interface ITableMetaQuery {
190
+ sqlTable: string;
191
+ sqlDatabase: string;
192
+ sqlDbType: SqlDbType;
193
+ sqlSchema?: string;
174
194
  }
175
195
  export {};
@@ -2,7 +2,7 @@ import { RequestCfgModel } from './RequestCfgModel';
2
2
  import { RequestModel } from './RequestModel';
3
3
  import { SqlCfgModel } from './SqlCfgModel';
4
4
  import { ExecuteContext } from './ExecuteContext';
5
- import { IFuncCfgModel, IRequestCfgModel, ITableMeta } from '../interfaces';
5
+ import { IFuncCfgModel, IRequestCfgModel, ITableMeta, ITableMetaQuery } from '../interfaces';
6
6
  import { FuncContext } from './FuncContext';
7
7
  export interface ICurdProServiceHub {
8
8
  getExecuteContext(): ExecuteContext;
@@ -16,5 +16,5 @@ export interface ICurdProServiceHub {
16
16
  executeSqlCfgModels(exeCtx: ExecuteContext): Promise<void>;
17
17
  convertOriginToExecuteSql(sqlCfgModel: SqlCfgModel): Promise<void>;
18
18
  executeFuncCfg(tmpFunCfg: IFuncCfgModel, exeFunCtx: FuncContext): string;
19
- getTableMeta(sqlCfgModel: SqlCfgModel): Promise<ITableMeta>;
19
+ getTableMeta(query: ITableMetaQuery): Promise<ITableMeta>;
20
20
  }
@@ -139,7 +139,12 @@ class Transaction {
139
139
  const connections = this.connectionList;
140
140
  for (let i = 0; i < connections.length; i++) {
141
141
  const connection = connections[i];
142
- await connection.rollback();
142
+ try {
143
+ await connection.rollback();
144
+ }
145
+ catch (e) {
146
+ console.error('[crud-pro] rollbackTx error', e);
147
+ }
143
148
  }
144
149
  }
145
150
  /**
@@ -85,6 +85,7 @@ export declare const KeysOfCustomSQL: {
85
85
  */
86
86
  export declare const KeysOfConditions: {
87
87
  $OR: string;
88
+ $AND: string;
88
89
  $NE: string;
89
90
  $LT: string;
90
91
  $LTE: string;
@@ -99,6 +99,7 @@ exports.KeysOfCustomSQL = {
99
99
  */
100
100
  exports.KeysOfConditions = {
101
101
  $OR: '$or',
102
+ $AND: '$and',
102
103
  $NE: '$ne',
103
104
  $LT: '$lt',
104
105
  $LTE: '$lte',
@@ -150,5 +151,6 @@ function initKeysOfConditions() {
150
151
  // 所有操作符
151
152
  exports.KeysOfConditions.ALL_KEYS = new Set([...exports.KeysOfConditions.COMPARE_KEYS]);
152
153
  addIgnoreCase(exports.KeysOfConditions.ALL_KEYS, exports.KeysOfConditions.$OR);
154
+ addIgnoreCase(exports.KeysOfConditions.ALL_KEYS, exports.KeysOfConditions.$AND);
153
155
  }
154
156
  initKeysOfConditions();
@@ -0,0 +1,17 @@
1
+ import { CrudProServiceBase } from './CrudProServiceBase';
2
+ import { ISqlCfgModel } from '../interfaces';
3
+ /**
4
+ * 数据过滤服务
5
+ * 用于在 INSERT/UPDATE 操作前,根据真实表结构过滤 data 对象中的字段
6
+ */
7
+ declare class CrudProDataFilterService extends CrudProServiceBase {
8
+ /**
9
+ * 根据表结构过滤 data 对象
10
+ * 只保留表中存在的字段
11
+ * @param data 原始 data 对象
12
+ * @param sqlCfgModel SQL 配置(可以是 ISqlCfgModel 接口或 SqlCfgModel 对象)
13
+ * @returns 过滤后的 data 对象
14
+ */
15
+ filterDataByTableMeta(data: Record<string, any>, sqlCfgModel: ISqlCfgModel): Promise<Record<string, any>>;
16
+ }
17
+ export { CrudProDataFilterService };
@@ -0,0 +1,53 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.CrudProDataFilterService = void 0;
4
+ const CrudProServiceBase_1 = require("./CrudProServiceBase");
5
+ /**
6
+ * 数据过滤服务
7
+ * 用于在 INSERT/UPDATE 操作前,根据真实表结构过滤 data 对象中的字段
8
+ */
9
+ class CrudProDataFilterService extends CrudProServiceBase_1.CrudProServiceBase {
10
+ /**
11
+ * 根据表结构过滤 data 对象
12
+ * 只保留表中存在的字段
13
+ * @param data 原始 data 对象
14
+ * @param sqlCfgModel SQL 配置(可以是 ISqlCfgModel 接口或 SqlCfgModel 对象)
15
+ * @returns 过滤后的 data 对象
16
+ */
17
+ async filterDataByTableMeta(data, sqlCfgModel) {
18
+ if (!data || Object.keys(data).length === 0) {
19
+ return data;
20
+ }
21
+ // 确保 sqlTable 存在
22
+ if (!sqlCfgModel.sqlTable) {
23
+ this.logger.warn('CrudProDataFilterService: sqlTable 为空,跳过过滤');
24
+ return data;
25
+ }
26
+ const query = {
27
+ sqlTable: sqlCfgModel.sqlTable,
28
+ sqlDatabase: sqlCfgModel.sqlDatabase,
29
+ sqlDbType: sqlCfgModel.sqlDbType,
30
+ sqlSchema: sqlCfgModel.sqlSchema
31
+ };
32
+ const tableMeta = await this.serviceHub.getTableMeta(query);
33
+ if (!tableMeta || !tableMeta.tableColumns || tableMeta.tableColumns.length === 0) {
34
+ this.logger.warn('CrudProDataFilterService: 无法获取表结构信息,跳过过滤', sqlCfgModel.sqlTable);
35
+ return data;
36
+ }
37
+ const validColumns = new Set(tableMeta.tableColumns);
38
+ const filteredData = {};
39
+ for (const [key, value] of Object.entries(data)) {
40
+ if (validColumns.has(key)) {
41
+ filteredData[key] = value;
42
+ }
43
+ else {
44
+ this.logger.debug('CrudProDataFilterService: 过滤掉不存在的字段', {
45
+ table: sqlCfgModel.sqlTable,
46
+ field: key,
47
+ });
48
+ }
49
+ }
50
+ return filteredData;
51
+ }
52
+ }
53
+ exports.CrudProDataFilterService = CrudProDataFilterService;
@@ -11,6 +11,7 @@ declare class CrudProGenSqlCondition {
11
11
  * @private
12
12
  */
13
13
  private generateByLogicalKey;
14
+ private generateByLogicalAnd;
14
15
  private generateByLogicalOR;
15
16
  private generateByCompareKey;
16
17
  /**
@@ -82,6 +82,17 @@ class CrudProGenSqlCondition {
82
82
  }
83
83
  }
84
84
  // AND
85
+ if (equalsIgnoreCase(keys_1.KeysOfConditions.$AND, key)) {
86
+ TypeUtils_1.TypeUtils.assertJsonArray(value, exceptions_1.Exceptions.REQUEST_OR_PARAM_MUST_ARRAY);
87
+ const a = this.generateByLogicalAnd(value);
88
+ if (a != null) {
89
+ tmpSqlList.push(a.sql);
90
+ if (isNotEmpty(a.args)) {
91
+ tmpArgList.push(...a.args);
92
+ }
93
+ }
94
+ }
95
+ // AND
85
96
  else if (TypeUtils_1.TypeUtils.isBasicType(value)) {
86
97
  tmpSqlList.push(toSqlColumnName(key) + '= ?');
87
98
  tmpArgList.push(value);
@@ -105,6 +116,25 @@ class CrudProGenSqlCondition {
105
116
  const sql = '(' + tmpSqlList.join(' AND ') + ')';
106
117
  return new SqlSegArg_1.SqlSegArg(sql, tmpArgList);
107
118
  }
119
+ generateByLogicalAnd(jsonArray) {
120
+ if (MixinUtils_1.MixinUtils.isEmpty(jsonArray)) {
121
+ return null;
122
+ }
123
+ const tmpSqlList = [];
124
+ const tmpArgList = [];
125
+ for (let i = 0; i < jsonArray.length; i++) {
126
+ const jsonObject = jsonArray[i];
127
+ const a = this.generateByLogicalKey(jsonObject);
128
+ if (a != null) {
129
+ tmpSqlList.push(a.sql);
130
+ if (MixinUtils_1.MixinUtils.isNotEmpty(a.args)) {
131
+ tmpArgList.push(...a.args);
132
+ }
133
+ }
134
+ }
135
+ const sql = '(' + tmpSqlList.join(' AND ') + ')';
136
+ return new SqlSegArg_1.SqlSegArg(sql, tmpArgList);
137
+ }
108
138
  generateByLogicalOR(jsonArray) {
109
139
  if (MixinUtils_1.MixinUtils.isEmpty(jsonArray)) {
110
140
  return null;
@@ -310,7 +310,13 @@ class CrudProOriginToExecuteSql extends CrudProServiceBase_1.CrudProServiceBase
310
310
  * @private
311
311
  */
312
312
  async generateCfgColumns(sqlCfgModel) {
313
- const tableMeta = await this.serviceHub.getTableMeta(sqlCfgModel);
313
+ const query = {
314
+ sqlTable: sqlCfgModel.sqlTable,
315
+ sqlDatabase: sqlCfgModel.sqlDatabase,
316
+ sqlDbType: sqlCfgModel.sqlDbType,
317
+ sqlSchema: sqlCfgModel.sqlSchema
318
+ };
319
+ const tableMeta = await this.serviceHub.getTableMeta(query);
314
320
  const tableColumns = tableMeta.tableColumns;
315
321
  const cfgColumns = sqlCfgModel.columns || [];
316
322
  // 没有配置: 全量
@@ -1,9 +1,12 @@
1
1
  import { CrudProServiceBase } from './CrudProServiceBase';
2
- import { SqlCfgModel } from '../models/SqlCfgModel';
3
- import { ITableMeta } from '../interfaces';
2
+ import { ITableMeta, ITableMetaQuery } from '../interfaces';
4
3
  declare class CrudProTableMetaService extends CrudProServiceBase {
5
- getTableMeta(sqlCfgModel: SqlCfgModel): Promise<ITableMeta>;
4
+ getTableMeta(query: ITableMetaQuery): Promise<ITableMeta>;
6
5
  private loadTableMeta;
7
- private loadTableColumnInfo;
6
+ private loadTableColumnDetails;
7
+ private loadMySQLColumnDetails;
8
+ private loadPostgreSQLColumnDetails;
9
+ private loadPostgreSQLColumnComments;
10
+ private loadSQLServerColumnDetails;
8
11
  }
9
12
  export { CrudProTableMetaService };
@@ -8,71 +8,161 @@ class CrudProTableMetaCache {
8
8
  constructor() {
9
9
  this.cacheMap = {};
10
10
  }
11
- getCacheKey(sqlCfgModel) {
12
- return `${sqlCfgModel.sqlDatabase}:::${sqlCfgModel.sqlSchema}:::${sqlCfgModel.sqlTable}`;
11
+ getCacheKey(query) {
12
+ return `${query.sqlDatabase}:::${query.sqlSchema}:::${query.sqlDbType}:::${query.sqlTable}`;
13
13
  }
14
- getMeta(sqlCfgModel) {
15
- const cacheKey = this.getCacheKey(sqlCfgModel);
14
+ getMeta(query) {
15
+ const cacheKey = this.getCacheKey(query);
16
16
  const obj = this.cacheMap[cacheKey];
17
- if (obj && obj.expiredTime < Date.now()) {
17
+ if (obj && obj.expiredTime > Date.now()) {
18
18
  return obj;
19
19
  }
20
20
  return null;
21
21
  }
22
- setMeta(sqlCfgModel, metaObj) {
23
- const cacheKey = this.getCacheKey(sqlCfgModel);
22
+ setMeta(query, metaObj) {
23
+ const cacheKey = this.getCacheKey(query);
24
24
  this.cacheMap[cacheKey] = metaObj;
25
25
  }
26
26
  }
27
27
  const metaCache = new CrudProTableMetaCache();
28
28
  class CrudProTableMetaService extends CrudProServiceBase_1.CrudProServiceBase {
29
- async getTableMeta(sqlCfgModel) {
30
- let obj = metaCache.getMeta(sqlCfgModel);
29
+ async getTableMeta(query) {
30
+ let obj = metaCache.getMeta(query);
31
31
  if (!obj) {
32
- obj = await this.loadTableMeta(sqlCfgModel);
33
- metaCache.setMeta(sqlCfgModel, obj);
32
+ obj = await this.loadTableMeta(query);
33
+ metaCache.setMeta(query, obj);
34
34
  }
35
35
  return obj;
36
36
  }
37
- async loadTableMeta(sqlCfgModel) {
37
+ async loadTableMeta(query) {
38
38
  const { tableMetaCacheTime } = this.getContextCfg();
39
39
  const obj = {
40
- expiredTime: Date.now() + tableMetaCacheTime || 1000 * 3600 * 24 * 365,
40
+ expiredTime: Date.now() + (tableMetaCacheTime || 1000 * 3600 * 24 * 365),
41
41
  tableColumns: [],
42
+ columnDetails: [],
42
43
  };
43
44
  const baseInfo = {
44
- sqlTable: sqlCfgModel.sqlTable,
45
- sqlDatabase: sqlCfgModel.sqlDatabase,
46
- sqlDbType: sqlCfgModel.sqlDbType,
45
+ sqlTable: query.sqlTable,
46
+ sqlDatabase: query.sqlDatabase,
47
+ sqlDbType: query.sqlDbType,
47
48
  };
48
- obj.tableColumns = await this.loadTableColumnInfo(baseInfo);
49
+ obj.columnDetails = await this.loadTableColumnDetails(baseInfo);
50
+ obj.tableColumns = obj.columnDetails.map(col => col.name);
49
51
  return obj;
50
52
  }
51
- async loadTableColumnInfo(baseInfo) {
53
+ async loadTableColumnDetails(baseInfo) {
52
54
  if (baseInfo.sqlDbType === keys_1.SqlDbType.mysql) {
53
- const queryRes = await this.executeUnsafeQuery(baseInfo, 'describe ' + baseInfo.sqlTable);
54
- const tableDescribe = queryRes.rows || []; //pickAndConvertRowsByMix(queryRes, baseInfo.sqlDbType);
55
- const tableDescribe2 = JSON.parse(JSON.stringify(tableDescribe));
56
- return tableDescribe2.map(fieldObj => {
57
- return fieldObj['Field'];
58
- });
55
+ return this.loadMySQLColumnDetails(baseInfo);
59
56
  }
60
57
  else if (baseInfo.sqlDbType === keys_1.SqlDbType.postgres) {
61
- const schemaname = 'public';
62
- const columnArraySql = `
63
- SELECT
64
- *
65
- FROM information_schema.columns
66
- WHERE table_schema = '${schemaname}' and table_name = '${baseInfo.sqlTable}'
67
- ORDER BY ordinal_position;
68
- `.trim();
69
- const queryRes = await this.executeUnsafeQuery(baseInfo, columnArraySql);
70
- const tableDescribe = queryRes.rows || [];
71
- return tableDescribe.map(fieldObj => {
72
- return fieldObj['column_name'];
73
- });
58
+ return this.loadPostgreSQLColumnDetails(baseInfo);
59
+ }
60
+ else if (baseInfo.sqlDbType === keys_1.SqlDbType.sqlserver) {
61
+ return this.loadSQLServerColumnDetails(baseInfo);
74
62
  }
75
63
  throw new Error('暂不支持的数据库类型:' + baseInfo.sqlDbType);
76
64
  }
65
+ async loadMySQLColumnDetails(baseInfo) {
66
+ const queryRes = await this.executeUnsafeQuery(baseInfo, 'DESCRIBE ' + baseInfo.sqlTable);
67
+ const tableDescribe = queryRes.rows || [];
68
+ return tableDescribe.map((fieldObj) => {
69
+ const { Field, Type, Null, Key, Default, Extra } = fieldObj;
70
+ return {
71
+ name: Field,
72
+ type: Type,
73
+ isNullable: Null === 'YES',
74
+ isPrimaryKey: Key === 'PRI',
75
+ defaultValue: Default,
76
+ comment: Extra,
77
+ };
78
+ });
79
+ }
80
+ async loadPostgreSQLColumnDetails(baseInfo) {
81
+ const schemaname = baseInfo.sqlSchema || 'public';
82
+ // 获取列基本信息
83
+ const columnArraySql = `
84
+ SELECT
85
+ column_name,
86
+ data_type,
87
+ is_nullable,
88
+ column_default,
89
+ character_maximum_length
90
+ FROM information_schema.columns
91
+ WHERE table_schema = '${schemaname}' AND table_name = '${baseInfo.sqlTable}'
92
+ ORDER BY ordinal_position;
93
+ `.trim();
94
+ const queryRes = await this.executeUnsafeQuery(baseInfo, columnArraySql);
95
+ const columnArray = queryRes.rows || [];
96
+ // 获取字段注释
97
+ const commentMap = await this.loadPostgreSQLColumnComments(baseInfo, schemaname);
98
+ return columnArray.map((columnObj) => {
99
+ const { column_name, data_type, is_nullable, column_default, character_maximum_length } = columnObj;
100
+ return {
101
+ name: column_name,
102
+ type: data_type,
103
+ isNullable: is_nullable === 'YES',
104
+ defaultValue: column_default,
105
+ maxLength: character_maximum_length,
106
+ comment: commentMap[column_name],
107
+ };
108
+ });
109
+ }
110
+ async loadPostgreSQLColumnComments(baseInfo, schemaname) {
111
+ const commentArraySql = `
112
+ SELECT
113
+ a.attname AS column_name,
114
+ d.description AS column_comment
115
+ FROM pg_class c
116
+ JOIN pg_namespace n ON c.relnamespace = n.oid
117
+ JOIN pg_attribute a ON c.oid = a.attrelid
118
+ LEFT JOIN pg_description d ON c.oid = d.objoid AND a.attnum = d.objsubid
119
+ WHERE n.nspname = '${schemaname}'
120
+ AND c.relname = '${baseInfo.sqlTable}'
121
+ AND a.attnum > 0
122
+ ORDER BY a.attnum;
123
+ `.trim();
124
+ const queryRes = await this.executeUnsafeQuery(baseInfo, commentArraySql);
125
+ const commentArray = queryRes.rows || [];
126
+ const map = {};
127
+ commentArray.forEach((commentObj) => {
128
+ const { column_name, column_comment } = commentObj;
129
+ map[column_name] = column_comment || '';
130
+ });
131
+ return map;
132
+ }
133
+ async loadSQLServerColumnDetails(baseInfo) {
134
+ const columnArraySql = `
135
+ SELECT
136
+ c.name AS column_name,
137
+ t.name AS data_type,
138
+ c.max_length,
139
+ c.precision,
140
+ c.scale,
141
+ c.is_nullable,
142
+ c.is_identity,
143
+ dc.definition AS column_default,
144
+ ep.value AS column_comment
145
+ FROM sys.columns c
146
+ JOIN sys.types t ON c.user_type_id = t.user_type_id
147
+ LEFT JOIN sys.default_constraints dc ON c.default_object_id = dc.object_id
148
+ LEFT JOIN sys.extended_properties ep ON c.object_id = ep.major_id AND c.column_id = ep.minor_id
149
+ WHERE c.object_id = OBJECT_ID('${baseInfo.sqlTable}')
150
+ ORDER BY c.column_id;
151
+ `.trim();
152
+ const queryRes = await this.executeUnsafeQuery(baseInfo, columnArraySql);
153
+ const columnArray = queryRes.rows || [];
154
+ return columnArray.map((columnObj) => {
155
+ const { column_name, data_type, is_nullable, column_default, column_comment, max_length, is_identity } = columnObj;
156
+ return {
157
+ name: column_name,
158
+ type: data_type,
159
+ isNullable: is_nullable,
160
+ defaultValue: column_default,
161
+ comment: column_comment,
162
+ maxLength: max_length,
163
+ isPrimaryKey: is_identity,
164
+ };
165
+ });
166
+ }
77
167
  }
78
168
  exports.CrudProTableMetaService = CrudProTableMetaService;
@@ -1,4 +1,4 @@
1
- import { IFuncCfgModel, IRequestCfgModel, ITableMeta } from '../interfaces';
1
+ import { IFuncCfgModel, IRequestCfgModel, ISqlCfgModel, ITableMeta, ITableMetaQuery } from '../interfaces';
2
2
  import { RequestModel } from '../models/RequestModel';
3
3
  import { ExecuteContext } from '../models/ExecuteContext';
4
4
  import { SqlCfgModel } from '../models/SqlCfgModel';
@@ -15,6 +15,7 @@ declare class CurdProServiceHub implements ICurdProServiceHub {
15
15
  private readonly originToExecuteSql;
16
16
  private readonly executeFuncService;
17
17
  private readonly tableMetaService;
18
+ private readonly dataFilterService;
18
19
  constructor(executeContext: ExecuteContext);
19
20
  getExecuteContext(): ExecuteContext;
20
21
  validateByAllow(cfgModel: RequestCfgModel, reqModel: RequestModel): void;
@@ -27,6 +28,7 @@ declare class CurdProServiceHub implements ICurdProServiceHub {
27
28
  executeSqlCfgModels(): Promise<void>;
28
29
  convertOriginToExecuteSql(sqlCfgModel: SqlCfgModel): Promise<void>;
29
30
  executeFuncCfg(funCfg: IFuncCfgModel, funcContext: FuncContext): any;
30
- getTableMeta(sqlCfgModel: SqlCfgModel): Promise<ITableMeta>;
31
+ getTableMeta(query: ITableMetaQuery): Promise<ITableMeta>;
32
+ filterDataByTableMeta(data: Record<string, any>, sqlCfgModel: ISqlCfgModel | SqlCfgModel): Promise<Record<string, any>>;
31
33
  }
32
34
  export { CurdProServiceHub };
@@ -9,6 +9,7 @@ const CrudProGenSqlService_1 = require("./CrudProGenSqlService");
9
9
  const CrudProOriginToExecuteSql_1 = require("./CrudProOriginToExecuteSql");
10
10
  const CrudProExecuteFuncService_1 = require("./CrudProExecuteFuncService");
11
11
  const CrudProTableMetaService_1 = require("./CrudProTableMetaService");
12
+ const CrudProDataFilterService_1 = require("./CrudProDataFilterService");
12
13
  class CurdProServiceHub {
13
14
  constructor(executeContext) {
14
15
  this.executeContext = executeContext;
@@ -20,6 +21,7 @@ class CurdProServiceHub {
20
21
  this.originToExecuteSql = new CrudProOriginToExecuteSql_1.CrudProOriginToExecuteSql(this);
21
22
  this.executeFuncService = new CrudProExecuteFuncService_1.CrudProExecuteFuncService(this);
22
23
  this.tableMetaService = new CrudProTableMetaService_1.CrudProTableMetaService(this);
24
+ this.dataFilterService = new CrudProDataFilterService_1.CrudProDataFilterService(this);
23
25
  }
24
26
  getExecuteContext() {
25
27
  return this.executeContext;
@@ -57,8 +59,11 @@ class CurdProServiceHub {
57
59
  executeFuncCfg(funCfg, funcContext) {
58
60
  return this.executeFuncService.executeFuncCfg(funCfg, funcContext);
59
61
  }
60
- async getTableMeta(sqlCfgModel) {
61
- return await this.tableMetaService.getTableMeta(sqlCfgModel);
62
+ async getTableMeta(query) {
63
+ return await this.tableMetaService.getTableMeta(query);
64
+ }
65
+ async filterDataByTableMeta(data, sqlCfgModel) {
66
+ return await this.dataFilterService.filterDataByTableMeta(data, sqlCfgModel);
62
67
  }
63
68
  }
64
69
  exports.CurdProServiceHub = CurdProServiceHub;
@@ -176,6 +176,16 @@ const MixinUtils = {
176
176
  if (!obj || !objTravelCallback) {
177
177
  return;
178
178
  }
179
+ if (Array.isArray(obj)) {
180
+ const collection = obj;
181
+ for (let i = 0; i < collection.length; i++) {
182
+ const value = collection[i];
183
+ const curKeyPath = keyPath + '[' + i + ']';
184
+ objTravelCallback(value, null, i, curKeyPath);
185
+ MixinUtils.deepTravelObject(value, objTravelCallback, curKeyPath);
186
+ }
187
+ return;
188
+ }
179
189
  if (typeof obj === 'object') {
180
190
  const keys = Object.keys(obj);
181
191
  for (let i = 0; i < keys.length; i++) {
@@ -187,15 +197,6 @@ const MixinUtils = {
187
197
  MixinUtils.deepTravelObject(value, objTravelCallback, curKeyPath);
188
198
  }
189
199
  }
190
- if (Array.isArray(obj)) {
191
- const collection = obj;
192
- for (let i = 0; i < collection.length; i++) {
193
- const value = collection[i];
194
- const curKeyPath = keyPath + '[' + i + ']';
195
- objTravelCallback(value, null, i, curKeyPath);
196
- MixinUtils.deepTravelObject(value, objTravelCallback, curKeyPath);
197
- }
198
- }
199
200
  },
200
201
  removeEmptyAttrs(obj) {
201
202
  const result = {};