midway-fatcms 0.0.5 → 0.0.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.qoder/skills/midway-fatcms-crud/SKILL.md +375 -0
- package/.qoder/skills/midway-fatcms-crud/examples.md +990 -0
- package/.qoder/skills/midway-fatcms-crud/reference.md +568 -0
- package/README.md +377 -134
- package/dist/controller/manage/CrudStandardDesignApi.d.ts +0 -2
- package/dist/controller/manage/CrudStandardDesignApi.js +11 -85
- package/dist/index.d.ts +2 -0
- package/dist/index.js +2 -0
- package/dist/libs/crud-pro/CrudPro.d.ts +9 -1
- package/dist/libs/crud-pro/CrudPro.js +15 -0
- package/dist/libs/crud-pro/README.md +809 -0
- package/dist/libs/crud-pro/README_FUNC.md +193 -0
- package/dist/libs/crud-pro/exceptions.d.ts +2 -0
- package/dist/libs/crud-pro/exceptions.js +2 -0
- package/dist/libs/crud-pro/interfaces.d.ts +34 -1
- package/dist/libs/crud-pro/models/ExecuteContext.d.ts +3 -3
- package/dist/libs/crud-pro/models/ExecuteContext.js +2 -0
- package/dist/libs/crud-pro/models/RequestModel.d.ts +6 -2
- package/dist/libs/crud-pro/models/RequestModel.js +20 -53
- package/dist/libs/crud-pro/models/ResModel.d.ts +6 -4
- package/dist/libs/crud-pro/models/ServiceHub.d.ts +1 -0
- package/dist/libs/crud-pro/models/keys.d.ts +6 -1
- package/dist/libs/crud-pro/models/keys.js +5 -0
- package/dist/libs/crud-pro/services/CrudProDataTypeConvertService.d.ts +52 -0
- package/dist/libs/crud-pro/services/CrudProDataTypeConvertService.js +158 -0
- package/dist/libs/crud-pro/services/CrudProExecuteSqlService.js +20 -1
- package/dist/libs/crud-pro/services/CrudProFieldValidateService.d.ts +7 -0
- package/dist/libs/crud-pro/services/CrudProFieldValidateService.js +32 -0
- package/dist/libs/crud-pro/services/CrudProGenSqlService.d.ts +13 -0
- package/dist/libs/crud-pro/services/CrudProGenSqlService.js +44 -7
- package/dist/libs/crud-pro/services/CrudProOriginToExecuteSql.d.ts +43 -0
- package/dist/libs/crud-pro/services/CrudProOriginToExecuteSql.js +132 -1
- package/dist/libs/crud-pro/services/CrudProTableMetaService.d.ts +15 -1
- package/dist/libs/crud-pro/services/CrudProTableMetaService.js +107 -0
- package/dist/libs/crud-pro/services/CurdProServiceHub.d.ts +5 -1
- package/dist/libs/crud-pro/services/CurdProServiceHub.js +11 -0
- package/dist/libs/crud-pro/utils/DateTimeUtils.d.ts +1 -0
- package/dist/libs/crud-pro/utils/DateTimeUtils.js +3 -0
- package/dist/libs/crud-pro/utils/MixinUtils.d.ts +32 -0
- package/dist/libs/crud-pro/utils/MixinUtils.js +85 -1
- package/dist/libs/crud-pro/utils/OrderByUtils.d.ts +70 -0
- package/dist/libs/crud-pro/utils/OrderByUtils.js +158 -0
- package/dist/libs/crud-pro/utils/ValidateUtils.js +1 -1
- package/dist/libs/crud-sharding/ROUTING_LOGIC.md +944 -0
- package/dist/libs/crud-sharding/ShardingConfig.d.ts +218 -0
- package/dist/libs/crud-sharding/ShardingConfig.js +32 -0
- package/dist/libs/crud-sharding/ShardingCountCache.d.ts +69 -0
- package/dist/libs/crud-sharding/ShardingCountCache.js +160 -0
- package/dist/libs/crud-sharding/ShardingCrudPro.d.ts +363 -0
- package/dist/libs/crud-sharding/ShardingCrudPro.js +675 -0
- package/dist/libs/crud-sharding/ShardingMerger.d.ts +130 -0
- package/dist/libs/crud-sharding/ShardingMerger.js +282 -0
- package/dist/libs/crud-sharding/ShardingRouter.d.ts +69 -0
- package/dist/libs/crud-sharding/ShardingRouter.js +377 -0
- package/dist/libs/crud-sharding/ShardingTableCreator.d.ts +146 -0
- package/dist/libs/crud-sharding/ShardingTableCreator.js +805 -0
- package/dist/libs/crud-sharding/ShardingUtils.d.ts +38 -0
- package/dist/libs/crud-sharding/ShardingUtils.js +77 -0
- package/dist/libs/crud-sharding/index.d.ts +45 -0
- package/dist/libs/crud-sharding/index.js +55 -0
- package/dist/models/StandardColumns.d.ts +71 -0
- package/dist/models/StandardColumns.js +28 -0
- package/dist/service/SysAppService.js +2 -2
- package/dist/service/SysConfigService.js +1 -1
- package/dist/service/SysDictDataService.js +2 -2
- package/dist/service/SysMenuService.js +1 -1
- package/dist/service/UserAccountService.d.ts +1 -1
- package/dist/service/crudstd/CrudStdService.d.ts +0 -1
- package/dist/service/crudstd/CrudStdService.js +0 -27
- package/dist/service/curd/CrudProQuick.d.ts +134 -4
- package/dist/service/curd/CrudProQuick.js +155 -3
- package/dist/service/curd/CurdMixService.d.ts +2 -1
- package/dist/service/curd/CurdMixService.js +5 -1
- package/dist/service/curd/CurdProService.d.ts +44 -2
- package/dist/service/curd/CurdProService.js +53 -1
- package/dist/service/curd/README.md +1100 -0
- package/dist/service/curd/fixSoftDelete.d.ts +14 -0
- package/dist/service/curd/fixSoftDelete.js +29 -11
- package/dist/service/flow/FlowConfigService.js +1 -1
- package/dist/service/flow/FlowInstanceCrudService.js +1 -1
- package/package.json +4 -1
- package/src/controller/gateway/AsyncTaskController.ts +1 -1
- package/src/controller/manage/CrudStandardDesignApi.ts +16 -100
- package/src/index.ts +3 -0
- package/src/libs/crud-pro/CrudPro.ts +19 -1
- package/src/libs/crud-pro/README.md +809 -0
- package/src/libs/crud-pro/README_FUNC.md +193 -0
- package/src/libs/crud-pro/exceptions.ts +2 -0
- package/src/libs/crud-pro/interfaces.ts +38 -1
- package/src/libs/crud-pro/models/ExecuteContext.ts +6 -3
- package/src/libs/crud-pro/models/RequestModel.ts +23 -65
- package/src/libs/crud-pro/models/ResModel.ts +10 -4
- package/src/libs/crud-pro/models/ServiceHub.ts +2 -0
- package/src/libs/crud-pro/models/keys.ts +5 -0
- package/src/libs/crud-pro/services/CrudProDataTypeConvertService.ts +171 -0
- package/src/libs/crud-pro/services/CrudProExecuteSqlService.ts +24 -1
- package/src/libs/crud-pro/services/CrudProFieldValidateService.ts +53 -1
- package/src/libs/crud-pro/services/CrudProGenSqlService.ts +51 -7
- package/src/libs/crud-pro/services/CrudProOriginToExecuteSql.ts +159 -2
- package/src/libs/crud-pro/services/CrudProTableMetaService.ts +139 -1
- package/src/libs/crud-pro/services/CurdProServiceHub.ts +16 -1
- package/src/libs/crud-pro/utils/DateTimeUtils.ts +3 -0
- package/src/libs/crud-pro/utils/MixinUtils.ts +97 -1
- package/src/libs/crud-pro/utils/OrderByUtils.ts +169 -0
- package/src/libs/crud-pro/utils/ValidateUtils.ts +1 -1
- package/src/libs/crud-sharding/ROUTING_LOGIC.md +944 -0
- package/src/libs/crud-sharding/ShardingConfig.ts +240 -0
- package/src/libs/crud-sharding/ShardingCountCache.ts +200 -0
- package/src/libs/crud-sharding/ShardingCrudPro.ts +835 -0
- package/src/libs/crud-sharding/ShardingMerger.ts +384 -0
- package/src/libs/crud-sharding/ShardingRouter.ts +512 -0
- package/src/libs/crud-sharding/ShardingTableCreator.ts +1007 -0
- package/src/libs/crud-sharding/ShardingUtils.ts +84 -0
- package/src/libs/crud-sharding/index.ts +64 -0
- package/src/models/StandardColumns.ts +76 -0
- package/src/service/FileCenterService.ts +1 -1
- package/src/service/SysAppService.ts +2 -2
- package/src/service/SysConfigService.ts +1 -1
- package/src/service/SysDictDataService.ts +2 -2
- package/src/service/SysMenuService.ts +2 -2
- package/src/service/WorkbenchService.ts +1 -1
- package/src/service/anyapi/AnyApiService.ts +1 -1
- package/src/service/asyncTask/AsyncTaskRunnerService.ts +1 -1
- package/src/service/crudstd/CrudStdService.ts +0 -32
- package/src/service/curd/CrudProQuick.ts +164 -5
- package/src/service/curd/CurdMixService.ts +7 -2
- package/src/service/curd/CurdProService.ts +62 -3
- package/src/service/curd/README.md +1100 -0
- package/src/service/curd/fixCfgModel.ts +1 -2
- package/src/service/curd/fixSoftDelete.ts +38 -16
- package/src/service/flow/FlowConfigService.ts +1 -1
- package/src/service/flow/FlowInstanceCrudService.ts +1 -1
|
@@ -201,7 +201,15 @@ class CrudProExecuteSqlService extends CrudProServiceBase {
|
|
|
201
201
|
}
|
|
202
202
|
|
|
203
203
|
private toQueryResByResPicker(rows: any[], originRes: any, sqlCfgModel: SqlCfgModel) {
|
|
204
|
-
|
|
204
|
+
let resPicker = sqlCfgModel.resPicker;
|
|
205
|
+
|
|
206
|
+
// resPicker 未设置时,根据 SQL 类型自动推断
|
|
207
|
+
if (isEmpty(resPicker)) {
|
|
208
|
+
const crudType = sqlCfgModel.getCrudType();
|
|
209
|
+
if (crudType === KeyOfCrudTypes.INSERT || crudType === KeyOfCrudTypes.UPDATE || crudType === KeyOfCrudTypes.DELETE) {
|
|
210
|
+
resPicker = KeysOfSqlResPicker.UPDATE_RESULT;
|
|
211
|
+
}
|
|
212
|
+
}
|
|
205
213
|
|
|
206
214
|
// 返回第一行
|
|
207
215
|
if (KeysOfSqlResPicker.RESULT_FIRST_ROW === resPicker) {
|
|
@@ -220,6 +228,19 @@ class CrudProExecuteSqlService extends CrudProServiceBase {
|
|
|
220
228
|
return Number(map0['total_count'] || 0);
|
|
221
229
|
}
|
|
222
230
|
|
|
231
|
+
// 判断元素是否存在,返回 boolean
|
|
232
|
+
if (KeysOfSqlResPicker.RESULT_IS_EXIST === resPicker) {
|
|
233
|
+
if (isEmpty(rows) || rows.length === 0) {
|
|
234
|
+
return false;
|
|
235
|
+
}
|
|
236
|
+
const val = rows[0]['is_exist'];
|
|
237
|
+
// MySQL/SQLServer 返回 0/1,PostgreSQL 返回 false/true
|
|
238
|
+
if (typeof val === 'boolean') {
|
|
239
|
+
return val;
|
|
240
|
+
}
|
|
241
|
+
return Number(val) > 0;
|
|
242
|
+
}
|
|
243
|
+
|
|
223
244
|
// 增删改res的内容是修改结果:包括: insert\delete\update
|
|
224
245
|
if (KeysOfSqlResPicker.UPDATE_RESULT === resPicker) {
|
|
225
246
|
if (sqlCfgModel.sqlDbType === SqlDbType.postgres) {
|
|
@@ -248,6 +269,8 @@ class CrudProExecuteSqlService extends CrudProServiceBase {
|
|
|
248
269
|
return rows;
|
|
249
270
|
}
|
|
250
271
|
|
|
272
|
+
|
|
273
|
+
|
|
251
274
|
//其他配置:形如: sqlRes[0].total_count
|
|
252
275
|
if (typeof resPicker === 'string') {
|
|
253
276
|
if (resPicker.startsWith('sqlRes')) {
|
|
@@ -2,7 +2,7 @@ import { RequestModel } from '../models/RequestModel';
|
|
|
2
2
|
import { CrudProServiceBase } from './CrudProServiceBase';
|
|
3
3
|
import { RequestCfgModel } from '../models/RequestCfgModel';
|
|
4
4
|
import { MixinUtils } from '../utils/MixinUtils';
|
|
5
|
-
import { KeysOfConditions, KeysOfValidators } from '../models/keys';
|
|
5
|
+
import { KeysOfConditions, KeysOfSimpleSQL, KeysOfValidators } from '../models/keys';
|
|
6
6
|
import { CommonException, Exceptions } from '../exceptions';
|
|
7
7
|
import { ModelUtils } from '../utils/ModelUtils';
|
|
8
8
|
import { ICustomValidateFunc, IFuncCfgModel, IValidatorCfgItem } from '../interfaces';
|
|
@@ -175,6 +175,58 @@ class CrudProFieldValidateService extends CrudProServiceBase {
|
|
|
175
175
|
}
|
|
176
176
|
}
|
|
177
177
|
}
|
|
178
|
+
|
|
179
|
+
/**
|
|
180
|
+
* 校验 data 字段类型是否符合 sqlSimpleName 的要求
|
|
181
|
+
*
|
|
182
|
+
* - SIMPLE_BATCH_INSERT: data 必须是数组
|
|
183
|
+
* - 其他 INSERT/UPDATE 类型: data 必须是普通对象
|
|
184
|
+
*/
|
|
185
|
+
validateDataType(cfgModel: RequestCfgModel, reqModel: RequestModel): void {
|
|
186
|
+
const sqlSimpleName = cfgModel.sqlSimpleName;
|
|
187
|
+
if (!sqlSimpleName) {
|
|
188
|
+
return;
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
const data = reqModel.data;
|
|
192
|
+
|
|
193
|
+
const isArrayData = Array.isArray(data);
|
|
194
|
+
|
|
195
|
+
if (sqlSimpleName === KeysOfSimpleSQL.SIMPLE_BATCH_INSERT) {
|
|
196
|
+
if (!isArrayData || data.length === 0) {
|
|
197
|
+
throw new CommonException(
|
|
198
|
+
Exceptions.CFG_INVALID_DATA_TYPE,
|
|
199
|
+
`${sqlSimpleName} 要求 data 必须是数组。并且不能为空数组。`
|
|
200
|
+
);
|
|
201
|
+
}
|
|
202
|
+
return;
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
const needsObjectData = [
|
|
206
|
+
KeysOfSimpleSQL.SIMPLE_INSERT,
|
|
207
|
+
KeysOfSimpleSQL.SIMPLE_UPDATE,
|
|
208
|
+
KeysOfSimpleSQL.SIMPLE_INSERT_ON_DUPLICATE_UPDATE,
|
|
209
|
+
KeysOfSimpleSQL.SIMPLE_INSERT_OR_UPDATE,
|
|
210
|
+
].includes(sqlSimpleName as KeysOfSimpleSQL);
|
|
211
|
+
|
|
212
|
+
|
|
213
|
+
if(needsObjectData && (!data || Object.keys(data).length === 0)) {
|
|
214
|
+
throw new CommonException(
|
|
215
|
+
Exceptions.CFG_INVALID_DATA_TYPE,
|
|
216
|
+
`${sqlSimpleName} 要求 data 必须是对象,不能为空`
|
|
217
|
+
);
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
if (needsObjectData && isArrayData) {
|
|
221
|
+
throw new CommonException(
|
|
222
|
+
Exceptions.CFG_INVALID_DATA_TYPE,
|
|
223
|
+
`${sqlSimpleName} 要求 data 必须是普通对象,不能是数组`
|
|
224
|
+
);
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
|
|
228
|
+
|
|
229
|
+
}
|
|
178
230
|
}
|
|
179
231
|
|
|
180
232
|
export { CrudProFieldValidateService };
|
|
@@ -39,7 +39,11 @@ class CrudProGenSqlService extends CrudProServiceBase {
|
|
|
39
39
|
let sql1 = null;
|
|
40
40
|
let sql2 = null;
|
|
41
41
|
let sql3 = null;
|
|
42
|
-
|
|
42
|
+
|
|
43
|
+
if (equalsIgnoreCase(KeysOfSimpleSQL.SIMPLE_QUERY_EXIST, simpleSqlName)) {
|
|
44
|
+
sql1 = this.generateOriginSqlForIsExist(cfgModel);
|
|
45
|
+
cfgModel.addSqlCfgModel('is_exist', sql1, KeysOfSqlResPicker.RESULT_IS_EXIST);
|
|
46
|
+
} else if (equalsIgnoreCase(KeysOfSimpleSQL.SIMPLE_QUERY, simpleSqlName)) {
|
|
43
47
|
sql1 = 'select @@columns from @@table where @@asWhere:condition @@orderBys @@offsetLimit';
|
|
44
48
|
cfgModel.addSqlCfgModel('rows', sql1);
|
|
45
49
|
} else if (equalsIgnoreCase(KeysOfSimpleSQL.SIMPLE_QUERY_ONE, simpleSqlName)) {
|
|
@@ -61,6 +65,9 @@ class CrudProGenSqlService extends CrudProServiceBase {
|
|
|
61
65
|
} else if (equalsIgnoreCase(KeysOfSimpleSQL.SIMPLE_INSERT, simpleSqlName)) {
|
|
62
66
|
sql1 = this.generateOriginSqlForInsert(cfgModel);
|
|
63
67
|
cfgModel.addSqlCfgModel('affected', sql1, KeysOfSqlResPicker.UPDATE_RESULT);
|
|
68
|
+
} else if (equalsIgnoreCase(KeysOfSimpleSQL.SIMPLE_BATCH_INSERT, simpleSqlName)) {
|
|
69
|
+
sql1 = this.generateOriginSqlForBatchInsert(cfgModel);
|
|
70
|
+
cfgModel.addSqlCfgModel('affected', sql1, KeysOfSqlResPicker.UPDATE_RESULT);
|
|
64
71
|
} else if (equalsIgnoreCase(KeysOfSimpleSQL.SIMPLE_UPDATE, simpleSqlName)) {
|
|
65
72
|
sql1 = 'update @@table set @@asUpdate:data where @@asWhere:condition ';
|
|
66
73
|
cfgModel.addSqlCfgModel('affected', sql1, KeysOfSqlResPicker.UPDATE_RESULT);
|
|
@@ -68,20 +75,21 @@ class CrudProGenSqlService extends CrudProServiceBase {
|
|
|
68
75
|
sql1 = this.generateOriginSqlForDuplicateInsert(cfgModel);
|
|
69
76
|
cfgModel.addSqlCfgModel('affected', sql1, KeysOfSqlResPicker.UPDATE_RESULT);
|
|
70
77
|
} else if (equalsIgnoreCase(KeysOfSimpleSQL.SIMPLE_INSERT_OR_UPDATE, simpleSqlName)) {
|
|
71
|
-
|
|
72
|
-
|
|
78
|
+
|
|
79
|
+
sql1 = this.generateOriginSqlForIsExist(cfgModel); // 判断元素是否存在
|
|
80
|
+
sql2 = this.generateOriginSqlForInsert(cfgModel); // 插入语句
|
|
73
81
|
sql3 = 'update @@table set @@asUpdate:data where @@asWhere:condition ';
|
|
74
82
|
|
|
75
83
|
const insertWhen2 = {
|
|
76
84
|
functionName: 'eq',
|
|
77
|
-
functionParams: [{
|
|
85
|
+
functionParams: [{ contextAsBool: 'res.is_exist' }, { constBool: false }],
|
|
78
86
|
};
|
|
79
87
|
const updateWhen3 = {
|
|
80
|
-
functionName: '
|
|
81
|
-
functionParams: [{
|
|
88
|
+
functionName: 'eq',
|
|
89
|
+
functionParams: [{ contextAsBool: 'res.is_exist' }, { constBool: true }],
|
|
82
90
|
};
|
|
83
91
|
|
|
84
|
-
cfgModel.addSqlCfgModel('
|
|
92
|
+
cfgModel.addSqlCfgModel('is_exist', sql1, KeysOfSqlResPicker.RESULT_IS_EXIST);
|
|
85
93
|
cfgModel.addSqlCfgModel('insert_affected', sql2, KeysOfSqlResPicker.UPDATE_RESULT, insertWhen2);
|
|
86
94
|
cfgModel.addSqlCfgModel('update_affected', sql3, KeysOfSqlResPicker.UPDATE_RESULT, updateWhen3);
|
|
87
95
|
}
|
|
@@ -91,6 +99,27 @@ class CrudProGenSqlService extends CrudProServiceBase {
|
|
|
91
99
|
}
|
|
92
100
|
}
|
|
93
101
|
|
|
102
|
+
|
|
103
|
+
|
|
104
|
+
|
|
105
|
+
|
|
106
|
+
/**
|
|
107
|
+
* 判断元素是否存在
|
|
108
|
+
* 使用 SELECT EXISTS 子查询,数据库引擎找到第一条匹配记录即停止扫描,比 COUNT(*) 更高效
|
|
109
|
+
* @param cfgModel
|
|
110
|
+
* @private
|
|
111
|
+
*/
|
|
112
|
+
private generateOriginSqlForIsExist(cfgModel: RequestCfgModel): string {
|
|
113
|
+
if (cfgModel.sqlDbType === SqlDbType.postgres) {
|
|
114
|
+
return 'SELECT EXISTS(SELECT 1 FROM @@table WHERE @@asWhere:condition) AS is_exist';
|
|
115
|
+
}
|
|
116
|
+
if (cfgModel.sqlDbType === SqlDbType.sqlserver) {
|
|
117
|
+
return 'SELECT CASE WHEN EXISTS(SELECT 1 FROM @@table WHERE @@asWhere:condition) THEN 1 ELSE 0 END AS is_exist';
|
|
118
|
+
}
|
|
119
|
+
return 'SELECT EXISTS(SELECT 1 FROM @@table WHERE @@asWhere:condition) AS is_exist';
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
|
|
94
123
|
/**
|
|
95
124
|
* 删除语句
|
|
96
125
|
* @param cfgModel
|
|
@@ -157,6 +186,21 @@ class CrudProGenSqlService extends CrudProServiceBase {
|
|
|
157
186
|
return 'insert into @@table ( @@asInsertKeys:data ) values( @@asInsertValues:data )'; // 关键字的前后,必须有空格
|
|
158
187
|
}
|
|
159
188
|
|
|
189
|
+
/**
|
|
190
|
+
* 创建批量插入语句
|
|
191
|
+
* @param cfgModel
|
|
192
|
+
* @private
|
|
193
|
+
*/
|
|
194
|
+
private generateOriginSqlForBatchInsert(cfgModel: RequestCfgModel): string {
|
|
195
|
+
if (cfgModel.sqlDbType === SqlDbType.postgres) {
|
|
196
|
+
return 'insert into @@table ( @@asBatchInsertKeys:data ) values @@asBatchInsertValues:data RETURNING * ';
|
|
197
|
+
}
|
|
198
|
+
if (cfgModel.sqlDbType === SqlDbType.sqlserver) {
|
|
199
|
+
return 'insert into @@table ( @@asBatchInsertKeys:data ) OUTPUT INSERTED.* values @@asBatchInsertValues:data ';
|
|
200
|
+
}
|
|
201
|
+
return 'insert into @@table ( @@asBatchInsertKeys:data ) values @@asBatchInsertValues:data '; // MySQL 默认语法
|
|
202
|
+
}
|
|
203
|
+
|
|
160
204
|
private async generateExecuteSql() {
|
|
161
205
|
const exeCtx = this.getExecuteContext();
|
|
162
206
|
|
|
@@ -3,7 +3,7 @@ import { SqlCfgModel } from '../models/SqlCfgModel';
|
|
|
3
3
|
import { ITableMetaQuery } from '../interfaces';
|
|
4
4
|
import { MixinUtils } from '../utils/MixinUtils';
|
|
5
5
|
import { SqlSegArg } from '../models/SqlSegArg';
|
|
6
|
-
import { KeysOfCustomSQL, SqlDbType } from '../models/keys';
|
|
6
|
+
import { KeysOfCustomSQL, KeyOfCrudTypes, SqlDbType } from '../models/keys';
|
|
7
7
|
import { RequestModel } from '../models/RequestModel';
|
|
8
8
|
import { CommonException, Exceptions } from '../exceptions';
|
|
9
9
|
import { RequestCfgModel } from '../models/RequestCfgModel';
|
|
@@ -73,7 +73,43 @@ class CrudProOriginToExecuteSql extends CrudProServiceBase {
|
|
|
73
73
|
}
|
|
74
74
|
|
|
75
75
|
sqlCfgModel.executeSqlArgs = argList;
|
|
76
|
-
sqlCfgModel.executeSql = executeSql;
|
|
76
|
+
sqlCfgModel.executeSql = this.appendDialectClause(executeSql, sqlCfgModel);
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* 对于自定义 INSERT SQL,自动追加数据库方言子句:
|
|
81
|
+
* - PostgreSQL: RETURNING *
|
|
82
|
+
* - SQL Server: OUTPUT INSERTED.*
|
|
83
|
+
* - MySQL: 无需追加,驱动自动返回 insertId
|
|
84
|
+
*
|
|
85
|
+
* 仅当 originSql 中用户未手动添加这些子句时才追加
|
|
86
|
+
*/
|
|
87
|
+
private appendDialectClause(executeSql: string, sqlCfgModel: SqlCfgModel): string {
|
|
88
|
+
const sqlDbType = sqlCfgModel.sqlDbType;
|
|
89
|
+
const sqlUpper = executeSql.trim().toUpperCase();
|
|
90
|
+
|
|
91
|
+
if (!sqlUpper.startsWith(KeyOfCrudTypes.INSERT)) {
|
|
92
|
+
return executeSql;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
if (sqlDbType === SqlDbType.postgres) {
|
|
96
|
+
if (sqlUpper.indexOf('RETURNING') < 0) {
|
|
97
|
+
return executeSql + ' RETURNING *';
|
|
98
|
+
}
|
|
99
|
+
} else if (sqlDbType === SqlDbType.sqlserver) {
|
|
100
|
+
if (sqlUpper.indexOf('OUTPUT INSERTED') < 0) {
|
|
101
|
+
// SQL Server: OUTPUT INSERTED.* 需放在 VALUES 之前
|
|
102
|
+
// INSERT INTO table (...) OUTPUT INSERTED.* VALUES (...)
|
|
103
|
+
const valuesIndex = sqlUpper.lastIndexOf('VALUES');
|
|
104
|
+
if (valuesIndex > 0) {
|
|
105
|
+
const beforeValues = executeSql.substring(0, valuesIndex);
|
|
106
|
+
const fromValues = executeSql.substring(valuesIndex);
|
|
107
|
+
return beforeValues + ' OUTPUT INSERTED.* ' + fromValues;
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
return executeSql;
|
|
77
113
|
}
|
|
78
114
|
|
|
79
115
|
private async generateWordMap(originSql: string, sqlCfgModel: SqlCfgModel): Promise<Record<string, SqlSegArg>> {
|
|
@@ -123,6 +159,10 @@ class CrudProOriginToExecuteSql extends CrudProServiceBase {
|
|
|
123
159
|
return this.generateDataAsInsertKeys(reqModel, sqlCfg, MixinUtils.removeStringPrefix(word, KeysOfCustomSQL.SQL_AS_INSERT_KEYS));
|
|
124
160
|
} else if (word.startsWith(KeysOfCustomSQL.SQL_AS_INSERT_VALUES)) {
|
|
125
161
|
return this.generateDataAsInsertValues(reqModel, MixinUtils.removeStringPrefix(word, KeysOfCustomSQL.SQL_AS_INSERT_VALUES));
|
|
162
|
+
} else if (word.startsWith(KeysOfCustomSQL.SQL_AS_BATCH_INSERT_KEYS)) {
|
|
163
|
+
return this.generateDataAsBatchInsertKeys(reqModel, sqlCfg, MixinUtils.removeStringPrefix(word, KeysOfCustomSQL.SQL_AS_BATCH_INSERT_KEYS));
|
|
164
|
+
} else if (word.startsWith(KeysOfCustomSQL.SQL_AS_BATCH_INSERT_VALUES)) {
|
|
165
|
+
return this.generateDataAsBatchInsertValues(reqModel, MixinUtils.removeStringPrefix(word, KeysOfCustomSQL.SQL_AS_BATCH_INSERT_VALUES));
|
|
126
166
|
} else if (word.startsWith(KeysOfCustomSQL.SQL_FUNCTION)) {
|
|
127
167
|
return this.generateSqlJavaFunction(reqCfg, MixinUtils.removeStringPrefix(word, KeysOfCustomSQL.SQL_FUNCTION));
|
|
128
168
|
} else {
|
|
@@ -334,6 +374,123 @@ class CrudProOriginToExecuteSql extends CrudProServiceBase {
|
|
|
334
374
|
return new SqlSegArg(sql, argList);
|
|
335
375
|
}
|
|
336
376
|
|
|
377
|
+
/**
|
|
378
|
+
* 获取批量插入的字段名列表
|
|
379
|
+
*
|
|
380
|
+
* 策略:
|
|
381
|
+
* - 如果指定了 columns,直接使用 columns(允许数据行包含额外字段,仅插入指定列)
|
|
382
|
+
* - 否则取第一条数据的 keys,并校验后续行是否一致(严格模式)
|
|
383
|
+
*
|
|
384
|
+
* 严格模式限制:
|
|
385
|
+
* - 所有数据行必须包含完全相同的字段
|
|
386
|
+
* - 字段数量必须一致
|
|
387
|
+
* - 字段名必须一致(顺序可不同)
|
|
388
|
+
* - 若不一致,抛出 BATCH_INSERT_KEYS_MISMATCH 异常
|
|
389
|
+
*
|
|
390
|
+
* @param req RequestModel 请求模型
|
|
391
|
+
* @param dataArray 数据数组
|
|
392
|
+
* @returns 排序后的字段名数组
|
|
393
|
+
*/
|
|
394
|
+
private getBatchInsertKeys(req: RequestModel, dataArray: Record<string, any>[]): string[] {
|
|
395
|
+
// 如果指定了 columns,直接使用
|
|
396
|
+
if (MixinUtils.isNotEmpty(req.columns) && req.columns.length > 0) {
|
|
397
|
+
return [...req.columns].sort();
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
// 严格模式:取第一条数据的 keys,并校验后续行
|
|
401
|
+
const firstRow = dataArray[0];
|
|
402
|
+
const keys = Object.keys(firstRow).sort();
|
|
403
|
+
|
|
404
|
+
for (let i = 1; i < dataArray.length; i++) {
|
|
405
|
+
const row = dataArray[i];
|
|
406
|
+
const rowKeys = Object.keys(row).sort();
|
|
407
|
+
|
|
408
|
+
if (rowKeys.length !== keys.length) {
|
|
409
|
+
throw new CommonException(
|
|
410
|
+
Exceptions.BATCH_INSERT_KEYS_MISMATCH,
|
|
411
|
+
`批量插入第 ${i + 1} 行数据字段数量不一致,期望 ${keys.length} 个,实际 ${rowKeys.length} 个`
|
|
412
|
+
);
|
|
413
|
+
}
|
|
414
|
+
|
|
415
|
+
for (let j = 0; j < keys.length; j++) {
|
|
416
|
+
if (rowKeys[j] !== keys[j]) {
|
|
417
|
+
throw new CommonException(
|
|
418
|
+
Exceptions.BATCH_INSERT_KEYS_MISMATCH,
|
|
419
|
+
`批量插入第 ${i + 1} 行数据字段不一致,缺少字段 "${keys[j]}" 或包含额外字段`
|
|
420
|
+
);
|
|
421
|
+
}
|
|
422
|
+
}
|
|
423
|
+
}
|
|
424
|
+
|
|
425
|
+
return keys;
|
|
426
|
+
}
|
|
427
|
+
|
|
428
|
+
/**
|
|
429
|
+
* 批量插入:生成列名部分
|
|
430
|
+
*
|
|
431
|
+
* 列名获取策略:
|
|
432
|
+
* - 优先使用 req.columns(若指定)
|
|
433
|
+
* - 否则使用严格模式:取第一条数据的 keys,校验后续行字段一致性
|
|
434
|
+
*/
|
|
435
|
+
private generateDataAsBatchInsertKeys(req: RequestModel, sqlCfgModel: SqlCfgModel, dataName: string): SqlSegArg {
|
|
436
|
+
const dataArray = req.getCondOrDataAsArray(dataName);
|
|
437
|
+
|
|
438
|
+
if (MixinUtils.isEmpty(dataArray) || dataArray.length === 0) {
|
|
439
|
+
throw new CommonException(Exceptions.DATA_GET_DATA_EMPTY_ON_INSERT_KEYS, dataName);
|
|
440
|
+
}
|
|
441
|
+
|
|
442
|
+
const keys = this.getBatchInsertKeys(req, dataArray);
|
|
443
|
+
|
|
444
|
+
const columnList = keys.map(columnName => {
|
|
445
|
+
return toSqlColumnName(columnName, sqlCfgModel);
|
|
446
|
+
});
|
|
447
|
+
|
|
448
|
+
const sql = columnList.join(','); // `column1`,`column2`,`column3`
|
|
449
|
+
return new SqlSegArg(sql);
|
|
450
|
+
}
|
|
451
|
+
|
|
452
|
+
/**
|
|
453
|
+
* 批量插入:生成值部分,格式为 (?,?,?),(?,?,?),...
|
|
454
|
+
*
|
|
455
|
+
* 列名获取策略:
|
|
456
|
+
* - 优先使用 req.columns(若指定)
|
|
457
|
+
* - 否则使用严格模式:取第一条数据的 keys,校验后续行字段一致性
|
|
458
|
+
*/
|
|
459
|
+
private generateDataAsBatchInsertValues(req: RequestModel, dataName: string): SqlSegArg {
|
|
460
|
+
const dataArray = req.getCondOrDataAsArray(dataName);
|
|
461
|
+
|
|
462
|
+
if (MixinUtils.isEmpty(dataArray) || dataArray.length === 0) {
|
|
463
|
+
throw new CommonException(Exceptions.DATA_GET_DATA_EMPTY_ON_INSERT_VALUES, dataName);
|
|
464
|
+
}
|
|
465
|
+
|
|
466
|
+
const keys = this.getBatchInsertKeys(req, dataArray);
|
|
467
|
+
|
|
468
|
+
const valueGroups: string[] = [];
|
|
469
|
+
const argList: any[] = [];
|
|
470
|
+
|
|
471
|
+
for (let i = 0; i < dataArray.length; i++) {
|
|
472
|
+
const row = dataArray[i];
|
|
473
|
+
const rowValues: string[] = [];
|
|
474
|
+
|
|
475
|
+
for (let j = 0; j < keys.length; j++) {
|
|
476
|
+
const key = keys[j];
|
|
477
|
+
const arg = row[key];
|
|
478
|
+
|
|
479
|
+
if (sqlFuncUtils.isSqlFuncArg(arg)) {
|
|
480
|
+
rowValues.push(sqlFuncUtils.toFuncSQL(arg));
|
|
481
|
+
} else {
|
|
482
|
+
rowValues.push('?');
|
|
483
|
+
argList.push(arg);
|
|
484
|
+
}
|
|
485
|
+
}
|
|
486
|
+
|
|
487
|
+
valueGroups.push('(' + rowValues.join(',') + ')');
|
|
488
|
+
}
|
|
489
|
+
|
|
490
|
+
const sql = valueGroups.join(','); // "(?,?,?),(?,?,?),..."
|
|
491
|
+
return new SqlSegArg(sql, argList);
|
|
492
|
+
}
|
|
493
|
+
|
|
337
494
|
private generateSqlJavaFunction(requestCfg: RequestCfgModel, tmpFuncName: string): SqlSegArg {
|
|
338
495
|
const exeCtx = this.getExecuteContext();
|
|
339
496
|
|
|
@@ -1,13 +1,16 @@
|
|
|
1
1
|
import { CrudProServiceBase } from './CrudProServiceBase';
|
|
2
|
-
import { IExecuteUnsafeQueryCtx, ITableColumn, ITableMeta, ITableMetaQuery } from '../interfaces';
|
|
2
|
+
import { IExecuteUnsafeQueryCtx, ITableColumn, ITableInfo, ITableListResult, ITableMeta, ITableMetaQuery, ITableNamesQuery, ITableNamesOptions } from '../interfaces';
|
|
3
3
|
import { SqlDbType } from '../models/keys';
|
|
4
4
|
// import { pickAndConvertRowsByMix } from '../utils/sqlConvert/convertMix';
|
|
5
5
|
|
|
6
6
|
class CrudProTableMetaCache {
|
|
7
7
|
private cacheMap: Record<string, ITableMeta> = {};
|
|
8
|
+
private tableInfoCacheMap: Record<string, { tableInfos: ITableListResult; expiredTime: number }> = {};
|
|
9
|
+
|
|
8
10
|
private getCacheKey(query: ITableMetaQuery) {
|
|
9
11
|
return `${query.sqlDatabase}:::${query.sqlSchema}:::${query.sqlDbType}:::${query.sqlTable}`;
|
|
10
12
|
}
|
|
13
|
+
|
|
11
14
|
public getMeta(query: ITableMetaQuery) {
|
|
12
15
|
const cacheKey = this.getCacheKey(query);
|
|
13
16
|
const obj = this.cacheMap[cacheKey];
|
|
@@ -21,6 +24,21 @@ class CrudProTableMetaCache {
|
|
|
21
24
|
const cacheKey = this.getCacheKey(query);
|
|
22
25
|
this.cacheMap[cacheKey] = metaObj;
|
|
23
26
|
}
|
|
27
|
+
|
|
28
|
+
public getTableInfos(cacheKey: string): ITableListResult | null {
|
|
29
|
+
const cached = this.tableInfoCacheMap[cacheKey];
|
|
30
|
+
if (cached && cached.expiredTime > Date.now()) {
|
|
31
|
+
return cached.tableInfos;
|
|
32
|
+
}
|
|
33
|
+
return null;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
public setTableInfos(cacheKey: string, tableInfos: ITableListResult, cacheTime: number): void {
|
|
37
|
+
this.tableInfoCacheMap[cacheKey] = {
|
|
38
|
+
tableInfos,
|
|
39
|
+
expiredTime: Date.now() + cacheTime,
|
|
40
|
+
};
|
|
41
|
+
}
|
|
24
42
|
}
|
|
25
43
|
|
|
26
44
|
const metaCache = new CrudProTableMetaCache();
|
|
@@ -35,6 +53,126 @@ class CrudProTableMetaService extends CrudProServiceBase {
|
|
|
35
53
|
return obj;
|
|
36
54
|
}
|
|
37
55
|
|
|
56
|
+
// ============ 获取表和视图信息(带类型) ============
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* 获取数据库所有表和视图信息(包含类型)
|
|
61
|
+
*
|
|
62
|
+
* @param query 数据库查询参数
|
|
63
|
+
* @param options 缓存控制选项
|
|
64
|
+
* - skipCache: 跳过缓存,直接查询数据库
|
|
65
|
+
* - refreshCache: 查询后更新缓存
|
|
66
|
+
*/
|
|
67
|
+
public async getAllTableInfos(
|
|
68
|
+
query: ITableNamesQuery,
|
|
69
|
+
options: ITableNamesOptions = {}
|
|
70
|
+
): Promise<ITableListResult> {
|
|
71
|
+
const { skipCache = false, refreshCache = false } = options;
|
|
72
|
+
|
|
73
|
+
// 1. 尝试从缓存获取(使用独立的缓存key)
|
|
74
|
+
const cacheKey = this.getTableInfoCacheKey(query);
|
|
75
|
+
if (!skipCache) {
|
|
76
|
+
const cached = metaCache.getTableInfos(cacheKey);
|
|
77
|
+
if (cached) {
|
|
78
|
+
return cached;
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
// 2. 查询数据库
|
|
83
|
+
const result = await this.loadAllTableInfos(query);
|
|
84
|
+
|
|
85
|
+
// 3. 更新缓存
|
|
86
|
+
if (!skipCache || refreshCache) {
|
|
87
|
+
const { tableMetaCacheTime } = this.getContextCfg();
|
|
88
|
+
metaCache.setTableInfos(cacheKey, result, tableMetaCacheTime || 3600000);
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
return result;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
private getTableInfoCacheKey(query: ITableNamesQuery): string {
|
|
95
|
+
return `tableInfo:::${query.sqlDatabase}:::${query.sqlSchema || 'public'}:::${query.sqlDbType}`;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
private async loadAllTableInfos(query: ITableNamesQuery): Promise<ITableListResult> {
|
|
99
|
+
const { sqlDbType } = query;
|
|
100
|
+
|
|
101
|
+
if (sqlDbType === SqlDbType.mysql) {
|
|
102
|
+
return this.loadMySQLAllTableInfos(query);
|
|
103
|
+
} else if (sqlDbType === SqlDbType.postgres) {
|
|
104
|
+
return this.loadPostgreSQLAllTableInfos(query);
|
|
105
|
+
} else if (sqlDbType === SqlDbType.sqlserver) {
|
|
106
|
+
return this.loadSQLServerAllTableInfos(query);
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
throw new Error('暂不支持的数据库类型:' + sqlDbType);
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
private async loadMySQLAllTableInfos(query: ITableNamesQuery): Promise<ITableListResult> {
|
|
113
|
+
const dbUtil = { sqlDatabase: query.sqlDatabase, sqlDbType: query.sqlDbType } as IExecuteUnsafeQueryCtx;
|
|
114
|
+
|
|
115
|
+
// 获取物理表
|
|
116
|
+
const tableSql = `SHOW TABLES FROM ${query.sqlDatabase}`;
|
|
117
|
+
const tableRes = await this.executeUnsafeQuery(dbUtil, tableSql);
|
|
118
|
+
const tableNames: string[] = (tableRes.rows || []).map((row: any) => Object.values(row)[0] as string);
|
|
119
|
+
|
|
120
|
+
// 获取视图
|
|
121
|
+
const viewSql = `SHOW FULL TABLES FROM ${query.sqlDatabase} WHERE Table_type = 'VIEW'`;
|
|
122
|
+
const viewRes = await this.executeUnsafeQuery(dbUtil, viewSql);
|
|
123
|
+
const viewNames: string[] = (viewRes.rows || []).map((row: any) => Object.values(row)[0] as string);
|
|
124
|
+
|
|
125
|
+
const tables: ITableInfo[] = [
|
|
126
|
+
...tableNames.map(name => ({ name, tableType: 'table' })),
|
|
127
|
+
...viewNames.map(name => ({ name, tableType: 'view' })),
|
|
128
|
+
];
|
|
129
|
+
|
|
130
|
+
return { tables, tableNames, viewNames };
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
private async loadPostgreSQLAllTableInfos(query: ITableNamesQuery): Promise<ITableListResult> {
|
|
134
|
+
const schema = query.sqlSchema || 'public';
|
|
135
|
+
const dbUtil = { sqlDatabase: query.sqlDatabase, sqlDbType: query.sqlDbType } as IExecuteUnsafeQueryCtx;
|
|
136
|
+
|
|
137
|
+
// 获取物理表
|
|
138
|
+
const tableSql = `SELECT tablename FROM pg_tables WHERE schemaname = '${schema}' ORDER BY tablename`;
|
|
139
|
+
const tableRes = await this.executeUnsafeQuery(dbUtil, tableSql);
|
|
140
|
+
const tableNames: string[] = (tableRes.rows || []).map((row: any) => row.tablename);
|
|
141
|
+
|
|
142
|
+
// 获取视图
|
|
143
|
+
const viewSql = `SELECT viewname FROM pg_views WHERE schemaname = '${schema}' ORDER BY viewname`;
|
|
144
|
+
const viewRes = await this.executeUnsafeQuery(dbUtil, viewSql);
|
|
145
|
+
const viewNames: string[] = (viewRes.rows || []).map((row: any) => row.viewname);
|
|
146
|
+
|
|
147
|
+
const tables: ITableInfo[] = [
|
|
148
|
+
...tableNames.map(name => ({ name, tableType: 'table' })),
|
|
149
|
+
...viewNames.map(name => ({ name, tableType: 'view' })),
|
|
150
|
+
];
|
|
151
|
+
|
|
152
|
+
return { tables, tableNames, viewNames };
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
private async loadSQLServerAllTableInfos(query: ITableNamesQuery): Promise<ITableListResult> {
|
|
156
|
+
const dbUtil = { sqlDatabase: query.sqlDatabase, sqlDbType: query.sqlDbType } as IExecuteUnsafeQueryCtx;
|
|
157
|
+
|
|
158
|
+
// 获取物理表
|
|
159
|
+
const tableSql = `SELECT name FROM sys.tables`;
|
|
160
|
+
const tableRes = await this.executeUnsafeQuery(dbUtil, tableSql);
|
|
161
|
+
const tableNames: string[] = (tableRes.rows || []).map((row: any) => row.name);
|
|
162
|
+
|
|
163
|
+
// 获取视图
|
|
164
|
+
const viewSql = `SELECT name FROM sys.views`;
|
|
165
|
+
const viewRes = await this.executeUnsafeQuery(dbUtil, viewSql);
|
|
166
|
+
const viewNames: string[] = (viewRes.rows || []).map((row: any) => row.name);
|
|
167
|
+
|
|
168
|
+
const tables: ITableInfo[] = [
|
|
169
|
+
...tableNames.map(name => ({ name, tableType: 'table' })),
|
|
170
|
+
...viewNames.map(name => ({ name, tableType: 'view' })),
|
|
171
|
+
];
|
|
172
|
+
|
|
173
|
+
return { tables, tableNames, viewNames };
|
|
174
|
+
}
|
|
175
|
+
|
|
38
176
|
private async loadTableMeta(query: ITableMetaQuery): Promise<ITableMeta> {
|
|
39
177
|
const { tableMetaCacheTime } = this.getContextCfg();
|
|
40
178
|
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { IFuncCfgModel, IRequestCfgModel, ISqlCfgModel, ITableMeta, ITableMetaQuery } from '../interfaces';
|
|
1
|
+
import { IFuncCfgModel, IRequestCfgModel, ISqlCfgModel, ITableListResult, ITableMeta, ITableMetaQuery, ITableNamesOptions, ITableNamesQuery } from '../interfaces';
|
|
2
2
|
import { RequestModel } from '../models/RequestModel';
|
|
3
3
|
import { CrudProFieldValidateService } from './CrudProFieldValidateService';
|
|
4
4
|
import { CrudProFieldUpdateService } from './CrudProFieldUpdateService';
|
|
@@ -14,6 +14,7 @@ import { FuncContext } from '../models/FuncContext';
|
|
|
14
14
|
import { CrudProExecuteFuncService } from './CrudProExecuteFuncService';
|
|
15
15
|
import { CrudProTableMetaService } from './CrudProTableMetaService';
|
|
16
16
|
import { CrudProDataFilterService } from './CrudProDataFilterService';
|
|
17
|
+
import { CrudProDataTypeConvertService } from './CrudProDataTypeConvertService';
|
|
17
18
|
|
|
18
19
|
class CurdProServiceHub implements ICurdProServiceHub {
|
|
19
20
|
private readonly executeContext: ExecuteContext;
|
|
@@ -27,6 +28,7 @@ class CurdProServiceHub implements ICurdProServiceHub {
|
|
|
27
28
|
private readonly executeFuncService: CrudProExecuteFuncService;
|
|
28
29
|
private readonly tableMetaService: CrudProTableMetaService;
|
|
29
30
|
private readonly dataFilterService: CrudProDataFilterService;
|
|
31
|
+
private readonly dataTypeConvertService: CrudProDataTypeConvertService;
|
|
30
32
|
|
|
31
33
|
constructor(executeContext: ExecuteContext) {
|
|
32
34
|
this.executeContext = executeContext;
|
|
@@ -39,6 +41,7 @@ class CurdProServiceHub implements ICurdProServiceHub {
|
|
|
39
41
|
this.executeFuncService = new CrudProExecuteFuncService(this);
|
|
40
42
|
this.tableMetaService = new CrudProTableMetaService(this);
|
|
41
43
|
this.dataFilterService = new CrudProDataFilterService(this);
|
|
44
|
+
this.dataTypeConvertService = new CrudProDataTypeConvertService(this);
|
|
42
45
|
}
|
|
43
46
|
|
|
44
47
|
getExecuteContext(): ExecuteContext {
|
|
@@ -57,6 +60,10 @@ class CurdProServiceHub implements ICurdProServiceHub {
|
|
|
57
60
|
return this.fieldValidateService.validateByCfg(cfgModel, reqModel);
|
|
58
61
|
}
|
|
59
62
|
|
|
63
|
+
validateDataType(cfgModel: RequestCfgModel, reqModel: RequestModel) {
|
|
64
|
+
return this.fieldValidateService.validateDataType(cfgModel, reqModel);
|
|
65
|
+
}
|
|
66
|
+
|
|
60
67
|
async validateByAuthCfg(cfgModel: RequestCfgModel, reqModel: RequestModel): Promise<any> {
|
|
61
68
|
const contextFunc = this.executeContext.contextFunc;
|
|
62
69
|
if (contextFunc && contextFunc.validateByAuthCfg) {
|
|
@@ -91,9 +98,17 @@ class CurdProServiceHub implements ICurdProServiceHub {
|
|
|
91
98
|
return await this.tableMetaService.getTableMeta(query);
|
|
92
99
|
}
|
|
93
100
|
|
|
101
|
+
async getAllTableInfos(query: ITableNamesQuery, options?: ITableNamesOptions): Promise<ITableListResult> {
|
|
102
|
+
return await this.tableMetaService.getAllTableInfos(query, options);
|
|
103
|
+
}
|
|
104
|
+
|
|
94
105
|
async filterDataByTableMeta(data: Record<string, any>, sqlCfgModel: ISqlCfgModel | SqlCfgModel): Promise<Record<string, any>> {
|
|
95
106
|
return await this.dataFilterService.filterDataByTableMeta(data, sqlCfgModel);
|
|
96
107
|
}
|
|
108
|
+
|
|
109
|
+
async convertDataTypeByTableMeta(reqModel: RequestModel, cfgModel: RequestCfgModel): Promise<void> {
|
|
110
|
+
return await this.dataTypeConvertService.convertDataTypeByTableMeta(reqModel, cfgModel);
|
|
111
|
+
}
|
|
97
112
|
}
|
|
98
113
|
|
|
99
114
|
export { CurdProServiceHub };
|
|
@@ -5,6 +5,9 @@ const DateTimeUtils = {
|
|
|
5
5
|
getCurrentTimeStampMs(): number {
|
|
6
6
|
return Date.now();
|
|
7
7
|
},
|
|
8
|
+
getCurrentTimeString(): string {
|
|
9
|
+
return moment().format('YYYY-MM-DD HH:mm:ss');
|
|
10
|
+
},
|
|
8
11
|
// 以秒为单位的时间戳
|
|
9
12
|
getCurrentTimeStampSecond(): number {
|
|
10
13
|
return Math.floor(Date.now() / 1000);
|