midway-fatcms 0.0.7 → 0.0.9
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/01-quick-start.md +231 -0
- package/.qoder/skills/midway-fatcms/02-crud-quick.md +375 -0
- package/.qoder/skills/midway-fatcms/03-crud-sharding.md +489 -0
- package/.qoder/skills/midway-fatcms/04-condition-operators.md +93 -0
- package/.qoder/skills/midway-fatcms/05-configuration.md +290 -0
- package/.qoder/skills/midway-fatcms/06-builtin-functions.md +241 -0
- package/.qoder/skills/midway-fatcms/07-examples.md +504 -0
- package/.qoder/skills/midway-fatcms/SKILL.md +96 -0
- package/README.md +9 -9
- package/dist/configuration.d.ts +10 -0
- package/dist/configuration.js +26 -0
- package/dist/controller/base/BaseApiController.d.ts +1 -2
- package/dist/controller/base/BaseApiController.js +0 -4
- package/dist/controller/gateway/DocGatewayController.js +1 -1
- package/dist/controller/helpers.controller.d.ts +6 -0
- package/dist/controller/helpers.controller.js +19 -0
- package/dist/controller/manage/FlowConfigManageApi.js +4 -2
- package/dist/controller/manage/SysConfigMangeApi.js +6 -1
- package/dist/controller/manage/UserAccountManageApi.js +7 -2
- package/dist/index.d.ts +2 -2
- package/dist/index.js +2 -2
- package/dist/libs/crud-pro/CrudPro.d.ts +51 -3
- package/dist/libs/crud-pro/CrudPro.js +111 -4
- package/dist/libs/crud-pro/exceptions.d.ts +7 -0
- package/dist/libs/crud-pro/exceptions.js +7 -0
- package/dist/libs/crud-pro/interfaces.d.ts +83 -12
- package/dist/libs/crud-pro/models/CrudResult.d.ts +116 -0
- package/dist/libs/crud-pro/models/CrudResult.js +126 -0
- package/dist/libs/crud-pro/models/RequestModel.d.ts +2 -2
- package/dist/libs/crud-pro/models/ServiceHub.d.ts +2 -0
- package/dist/libs/crud-pro/services/CrudProDataTypeConvertService.d.ts +70 -2
- package/dist/libs/crud-pro/services/CrudProDataTypeConvertService.js +205 -13
- package/dist/libs/crud-pro/services/CrudProExecuteSqlService.js +36 -2
- package/dist/libs/crud-pro/services/CrudProGenSqlCondition.js +8 -4
- package/dist/libs/crud-pro/services/CrudProTableMetaService.d.ts +36 -0
- package/dist/libs/crud-pro/services/CrudProTableMetaService.js +97 -4
- package/dist/libs/crud-pro/services/CurdProServiceHub.d.ts +2 -0
- package/dist/libs/crud-pro/services/CurdProServiceHub.js +6 -0
- package/dist/libs/crud-pro-quick/CrudProQuick.d.ts +382 -0
- package/dist/libs/crud-pro-quick/CrudProQuick.js +689 -0
- package/dist/libs/crud-pro-quick/fixSoftDelete.d.ts +30 -0
- package/dist/{service/curd → libs/crud-pro-quick}/fixSoftDelete.js +3 -6
- package/dist/libs/crud-pro-quick/index.d.ts +36 -0
- package/dist/libs/crud-pro-quick/index.js +49 -0
- package/dist/libs/crud-pro-quick/models.d.ts +33 -0
- package/dist/libs/crud-pro-quick/models.js +2 -0
- package/dist/libs/crud-sharding/ShardingBase.d.ts +78 -0
- package/dist/libs/crud-sharding/ShardingBase.js +179 -0
- package/dist/libs/crud-sharding/ShardingByCustomCrud.d.ts +35 -0
- package/dist/libs/crud-sharding/ShardingByCustomCrud.js +297 -0
- package/dist/libs/crud-sharding/ShardingByHashCrud.d.ts +38 -0
- package/dist/libs/crud-sharding/ShardingByHashCrud.js +86 -0
- package/dist/libs/crud-sharding/ShardingByKeyCrud.d.ts +39 -0
- package/dist/libs/crud-sharding/ShardingByKeyCrud.js +74 -0
- package/dist/libs/crud-sharding/ShardingByTimeCrud.d.ts +66 -0
- package/dist/libs/crud-sharding/ShardingByTimeCrud.js +524 -0
- package/dist/libs/crud-sharding/ShardingConfig.d.ts +25 -10
- package/dist/libs/crud-sharding/ShardingConfig.js +5 -5
- package/dist/libs/crud-sharding/ShardingMerger.d.ts +10 -18
- package/dist/libs/crud-sharding/ShardingMerger.js +27 -44
- package/dist/libs/crud-sharding/ShardingResult.d.ts +33 -0
- package/dist/libs/crud-sharding/ShardingResult.js +16 -0
- package/dist/libs/crud-sharding/ShardingTableCreator.d.ts +21 -4
- package/dist/libs/crud-sharding/ShardingTableCreator.js +193 -59
- package/dist/libs/crud-sharding/ShardingUtils.d.ts +48 -0
- package/dist/libs/crud-sharding/ShardingUtils.js +122 -1
- package/dist/libs/crud-sharding/TIME_COLUMN_CLEAN_SPEC.md +488 -0
- package/dist/libs/crud-sharding/index.d.ts +13 -15
- package/dist/libs/crud-sharding/index.js +33 -17
- package/dist/models/RedisKeys.d.ts +1 -0
- package/dist/models/RedisKeys.js +1 -0
- package/dist/models/bizmodels.d.ts +2 -6
- package/dist/service/SysAppService.d.ts +2 -2
- package/dist/service/SysAppService.js +16 -5
- package/dist/service/SysConfigService.d.ts +1 -1
- package/dist/service/SysConfigService.js +7 -2
- package/dist/service/SysDictDataService.js +14 -4
- package/dist/service/SysMenuService.js +7 -2
- package/dist/service/TableMetaCacheRedisSubscriber.d.ts +31 -0
- package/dist/service/TableMetaCacheRedisSubscriber.js +98 -0
- package/dist/service/curd/CurdMixService.d.ts +6 -4
- package/dist/service/curd/CurdMixService.js +16 -2
- package/dist/service/curd/CurdProService.d.ts +149 -29
- package/dist/service/curd/CurdProService.js +157 -38
- package/dist/service/flow/FlowConfigService.js +7 -2
- package/dist/service/flow/FlowInstanceCrudService.js +22 -19
- package/package.json +1 -1
- package/src/configuration.ts +27 -0
- package/src/controller/base/BaseApiController.ts +0 -5
- package/src/controller/gateway/DocGatewayController.ts +1 -1
- package/src/controller/helpers.controller.ts +15 -0
- package/src/controller/manage/CrudStandardDesignApi.ts +4 -3
- package/src/controller/manage/FlowConfigManageApi.ts +4 -2
- package/src/controller/manage/SysConfigMangeApi.ts +6 -1
- package/src/controller/manage/UserAccountManageApi.ts +7 -2
- package/src/index.ts +2 -2
- package/src/libs/crud-pro/CrudPro.ts +134 -7
- package/src/libs/crud-pro/exceptions.ts +8 -0
- package/src/libs/crud-pro/interfaces.ts +111 -15
- package/src/libs/crud-pro/models/CrudResult.ts +178 -0
- package/src/libs/crud-pro/models/RequestModel.ts +2 -2
- package/src/libs/crud-pro/models/ServiceHub.ts +4 -0
- package/src/libs/crud-pro/services/CrudProDataTypeConvertService.ts +238 -15
- package/src/libs/crud-pro/services/CrudProExecuteSqlService.ts +41 -2
- package/src/libs/crud-pro/services/CrudProGenSqlCondition.ts +11 -7
- package/src/libs/crud-pro/services/CrudProTableMetaService.ts +110 -3
- package/src/libs/crud-pro/services/CurdProServiceHub.ts +8 -0
- package/src/libs/crud-pro-quick/CrudProQuick.ts +782 -0
- package/src/{service/curd → libs/crud-pro-quick}/fixSoftDelete.ts +23 -13
- package/src/libs/crud-pro-quick/index.ts +52 -0
- package/src/libs/crud-pro-quick/models.ts +35 -0
- package/src/libs/crud-sharding/ShardingBase.ts +256 -0
- package/src/libs/crud-sharding/ShardingByCustomCrud.ts +329 -0
- package/src/libs/crud-sharding/ShardingByHashCrud.ts +111 -0
- package/src/libs/crud-sharding/ShardingByKeyCrud.ts +97 -0
- package/src/libs/crud-sharding/ShardingByTimeCrud.ts +628 -0
- package/src/libs/crud-sharding/ShardingConfig.ts +28 -10
- package/src/libs/crud-sharding/ShardingMerger.ts +35 -63
- package/src/libs/crud-sharding/ShardingResult.ts +29 -0
- package/src/libs/crud-sharding/ShardingTableCreator.ts +214 -71
- package/src/libs/crud-sharding/ShardingUtils.ts +137 -0
- package/src/libs/crud-sharding/TIME_COLUMN_CLEAN_SPEC.md +488 -0
- package/src/libs/crud-sharding/index.ts +30 -16
- package/src/models/RedisKeys.ts +1 -0
- package/src/models/bizmodels.ts +4 -7
- package/src/service/SysAppService.ts +18 -7
- package/src/service/SysConfigService.ts +8 -3
- package/src/service/SysDictDataService.ts +14 -4
- package/src/service/SysMenuService.ts +7 -2
- package/src/service/TableMetaCacheRedisSubscriber.ts +105 -0
- package/src/service/crudstd/CrudStdService.ts +2 -2
- package/src/service/curd/CurdMixService.ts +26 -5
- package/src/service/curd/CurdProService.ts +186 -45
- package/src/service/flow/FlowConfigService.ts +7 -2
- package/src/service/flow/FlowInstanceCrudService.ts +23 -20
- package/.qoder/skills/midway-fatcms-crud/SKILL.md +0 -375
- package/.qoder/skills/midway-fatcms-crud/examples.md +0 -990
- package/.qoder/skills/midway-fatcms-crud/reference.md +0 -568
- package/dist/libs/crud-pro/README.md +0 -809
- package/dist/libs/crud-pro/README_FUNC.md +0 -193
- package/dist/libs/crud-sharding/ROUTING_LOGIC.md +0 -944
- package/dist/libs/crud-sharding/ShardingCrudPro.d.ts +0 -363
- package/dist/libs/crud-sharding/ShardingCrudPro.js +0 -675
- package/dist/libs/crud-sharding/ShardingRouter.d.ts +0 -69
- package/dist/libs/crud-sharding/ShardingRouter.js +0 -377
- package/dist/models/StandardColumns.d.ts +0 -71
- package/dist/models/StandardColumns.js +0 -28
- package/dist/service/curd/CrudProQuick.d.ts +0 -190
- package/dist/service/curd/CrudProQuick.js +0 -319
- package/dist/service/curd/README.md +0 -1100
- package/dist/service/curd/fixSoftDelete.d.ts +0 -20
- package/src/libs/crud-pro/README.md +0 -809
- package/src/libs/crud-pro/README_FUNC.md +0 -193
- package/src/libs/crud-sharding/ROUTING_LOGIC.md +0 -944
- package/src/libs/crud-sharding/ShardingCrudPro.ts +0 -835
- package/src/libs/crud-sharding/ShardingRouter.ts +0 -512
- package/src/models/StandardColumns.ts +0 -76
- package/src/service/curd/CrudProQuick.ts +0 -360
- package/src/service/curd/README.md +0 -1100
|
@@ -3,12 +3,35 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.CrudProDataTypeConvertService = void 0;
|
|
4
4
|
const CrudProServiceBase_1 = require("./CrudProServiceBase");
|
|
5
5
|
const keys_1 = require("../models/keys");
|
|
6
|
+
/** 预编译正则:整数类型(MySQL/PostgreSQL/SQL Server) */
|
|
7
|
+
const REGEX_INTEGER_TYPE = /^(int|bigint|smallint|tinyint|mediumint|integer|serial|bigserial)/;
|
|
8
|
+
/** 预编译正则:字符串类型(MySQL/PostgreSQL/SQL Server) */
|
|
9
|
+
const REGEX_STRING_TYPE = /^(varchar|char|text|longtext|mediumtext|tinytext|nvarchar|nchar|ntext|character varying|character)/;
|
|
10
|
+
/** 包含 WHERE 条件的操作类型集合(查询/删除/更新) */
|
|
11
|
+
const CONDITION_TYPES = new Set([
|
|
12
|
+
keys_1.KeysOfSimpleSQL.SIMPLE_QUERY,
|
|
13
|
+
keys_1.KeysOfSimpleSQL.SIMPLE_QUERY_ONE,
|
|
14
|
+
keys_1.KeysOfSimpleSQL.SIMPLE_QUERY_PAGE,
|
|
15
|
+
keys_1.KeysOfSimpleSQL.SIMPLE_QUERY_COUNT,
|
|
16
|
+
keys_1.KeysOfSimpleSQL.SIMPLE_QUERY_EXIST,
|
|
17
|
+
keys_1.KeysOfSimpleSQL.SIMPLE_DELETE,
|
|
18
|
+
keys_1.KeysOfSimpleSQL.SIMPLE_UPDATE,
|
|
19
|
+
]);
|
|
20
|
+
/** INSERT/UPDATE 操作类型集合 */
|
|
21
|
+
const INSERT_OR_UPDATE_TYPES = new Set([
|
|
22
|
+
keys_1.KeysOfSimpleSQL.SIMPLE_INSERT,
|
|
23
|
+
keys_1.KeysOfSimpleSQL.SIMPLE_UPDATE,
|
|
24
|
+
keys_1.KeysOfSimpleSQL.SIMPLE_INSERT_ON_DUPLICATE_UPDATE,
|
|
25
|
+
keys_1.KeysOfSimpleSQL.SIMPLE_INSERT_OR_UPDATE,
|
|
26
|
+
keys_1.KeysOfSimpleSQL.SIMPLE_BATCH_INSERT,
|
|
27
|
+
]);
|
|
6
28
|
/**
|
|
7
29
|
* 数据类型转换服务
|
|
8
|
-
*
|
|
30
|
+
* 根据表结构字段类型,自动转换数据格式,确保数据与数据库方言兼容,防止隐式类型转换导致索引失效
|
|
9
31
|
*
|
|
10
32
|
* 当前支持的转换:
|
|
11
|
-
* - PostgreSQL ARRAY
|
|
33
|
+
* - PostgreSQL ARRAY 类型(INSERT/UPDATE data):JSON 数组 → PG 数组字面量 {"a","b","c"}
|
|
34
|
+
* - 查询条件类型强转(SELECT/DELETE/UPDATE condition):字符串 → 数字/布尔,防止隐式类型转换导致索引失效
|
|
12
35
|
*/
|
|
13
36
|
class CrudProDataTypeConvertService extends CrudProServiceBase_1.CrudProServiceBase {
|
|
14
37
|
/**
|
|
@@ -138,21 +161,190 @@ class CrudProDataTypeConvertService extends CrudProServiceBase_1.CrudProServiceB
|
|
|
138
161
|
.replace(/"/g, '\\"');
|
|
139
162
|
return '"' + escaped + '"';
|
|
140
163
|
}
|
|
164
|
+
/**
|
|
165
|
+
* 查询时:根据表结构字段类型,自动转换 condition 中的数据格式
|
|
166
|
+
* 防止数据库隐式类型转换导致索引失效
|
|
167
|
+
*
|
|
168
|
+
* 实现步骤:
|
|
169
|
+
* 1. 判断操作类型是否为查询/删除/更新(这些操作包含 WHERE 条件)
|
|
170
|
+
* 2. 获取表元数据,构建字段名→类型映射
|
|
171
|
+
* 3. 递归遍历 condition(支持 $or/$and 嵌套),按字段类型转换值
|
|
172
|
+
*
|
|
173
|
+
* 转换规则(双向保护,仅处理最常建索引的列类型):
|
|
174
|
+
* - 整数列(int/bigint/smallint/tinyint/mediumint) + 字符串值 → number
|
|
175
|
+
* - 整数列 + 布尔值 → 0/1
|
|
176
|
+
* - 字符串列(varchar/char/text等) + 数字值 → string
|
|
177
|
+
* - 字符串列 + 布尔值 → string
|
|
178
|
+
*
|
|
179
|
+
* @param reqModel 请求模型
|
|
180
|
+
* @param cfgModel 配置模型
|
|
181
|
+
*/
|
|
182
|
+
async convertConditionTypeByTableMeta(reqModel, cfgModel) {
|
|
183
|
+
const sqlSimpleName = cfgModel.sqlSimpleName;
|
|
184
|
+
if (!this.isConditionType(sqlSimpleName)) {
|
|
185
|
+
return;
|
|
186
|
+
}
|
|
187
|
+
if (!reqModel.condition || Object.keys(reqModel.condition).length === 0) {
|
|
188
|
+
return;
|
|
189
|
+
}
|
|
190
|
+
const query = {
|
|
191
|
+
sqlTable: cfgModel.sqlTable,
|
|
192
|
+
sqlDatabase: cfgModel.sqlDatabase,
|
|
193
|
+
sqlDbType: cfgModel.sqlDbType,
|
|
194
|
+
sqlSchema: cfgModel.sqlSchema,
|
|
195
|
+
};
|
|
196
|
+
const tableMeta = await this.serviceHub.getTableMeta(query);
|
|
197
|
+
if (!tableMeta || !tableMeta.columnDetails || tableMeta.columnDetails.length === 0) {
|
|
198
|
+
this.logger.warn('CrudProDataTypeConvertService: 无法获取表结构信息,跳过条件类型转换', cfgModel.sqlTable);
|
|
199
|
+
return;
|
|
200
|
+
}
|
|
201
|
+
// 构建字段名→类型映射(全小写便于匹配)
|
|
202
|
+
const typeMap = new Map();
|
|
203
|
+
for (const col of tableMeta.columnDetails) {
|
|
204
|
+
typeMap.set(col.name, ('' + col.type).toLowerCase());
|
|
205
|
+
}
|
|
206
|
+
this.convertConditionFields(reqModel.condition, typeMap, cfgModel.sqlDbType);
|
|
207
|
+
}
|
|
208
|
+
/**
|
|
209
|
+
* 递归遍历 condition,对每个字段值做类型转换
|
|
210
|
+
*
|
|
211
|
+
* 实现步骤:
|
|
212
|
+
* 1. 遍历 condition 的每个 key
|
|
213
|
+
* 2. 若 key 为 $or/$and 逻辑操作符,递归处理子条件数组
|
|
214
|
+
* 3. 若 key 为字段名且值为基本类型,直接转换
|
|
215
|
+
* 4. 若 key 为字段名且值为比较操作符对象($gt/$in等),遍历操作符逐值转换
|
|
216
|
+
*
|
|
217
|
+
* @param condition 查询条件对象
|
|
218
|
+
* @param typeMap 字段名→类型映射
|
|
219
|
+
* @param sqlDbType 数据库类型
|
|
220
|
+
*/
|
|
221
|
+
convertConditionFields(condition, typeMap, sqlDbType) {
|
|
222
|
+
if (!condition || typeof condition !== 'object') {
|
|
223
|
+
return;
|
|
224
|
+
}
|
|
225
|
+
for (const key of Object.keys(condition)) {
|
|
226
|
+
const value = condition[key];
|
|
227
|
+
// $or / $and 逻辑操作符:递归处理每个子条件
|
|
228
|
+
if (key === keys_1.KeysOfConditions.$OR || key === keys_1.KeysOfConditions.$AND) {
|
|
229
|
+
if (Array.isArray(value)) {
|
|
230
|
+
for (const subCondition of value) {
|
|
231
|
+
this.convertConditionFields(subCondition, typeMap, sqlDbType);
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
continue;
|
|
235
|
+
}
|
|
236
|
+
// 如果 key 是比较操作符(出现在内层),跳过
|
|
237
|
+
if (keys_1.KeysOfConditions.COMPARE_KEYS.has(key)) {
|
|
238
|
+
continue;
|
|
239
|
+
}
|
|
240
|
+
// key 是字段名
|
|
241
|
+
const fieldType = typeMap.get(key);
|
|
242
|
+
if (!fieldType) {
|
|
243
|
+
continue; // condition 中引用的字段不在表结构中,不做转换
|
|
244
|
+
}
|
|
245
|
+
if (this.isBasicType(value)) {
|
|
246
|
+
// 简单等值条件:{ age: "18" } → { age: 18 }
|
|
247
|
+
condition[key] = this.convertValueByType(value, fieldType, sqlDbType);
|
|
248
|
+
}
|
|
249
|
+
else if (typeof value === 'object' && value !== null && !Array.isArray(value)) {
|
|
250
|
+
// 比较操作符条件:{ age: { "$gt": "18", "$lt": "30" } }
|
|
251
|
+
for (const opKey of Object.keys(value)) {
|
|
252
|
+
if (keys_1.KeysOfConditions.COMPARE_KEYS.has(opKey)) {
|
|
253
|
+
const opValue = value[opKey];
|
|
254
|
+
if (opKey === keys_1.KeysOfConditions.$IN || opKey === keys_1.KeysOfConditions.$NIN) {
|
|
255
|
+
// $in/$nin 的值是数组,逐个转换
|
|
256
|
+
if (Array.isArray(opValue)) {
|
|
257
|
+
value[opKey] = opValue.map(v => this.convertValueByType(v, fieldType, sqlDbType));
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
else if (opKey === keys_1.KeysOfConditions.$RANGE) {
|
|
261
|
+
// $range 的值是 [min, max]
|
|
262
|
+
if (Array.isArray(opValue)) {
|
|
263
|
+
value[opKey] = opValue.map(v => this.convertValueByType(v, fieldType, sqlDbType));
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
else {
|
|
267
|
+
value[opKey] = this.convertValueByType(opValue, fieldType, sqlDbType);
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
/**
|
|
275
|
+
* 根据字段类型转换单个值,防止隐式类型转换导致索引失效
|
|
276
|
+
*
|
|
277
|
+
* 转换规则(双向保护):
|
|
278
|
+
* - 整数列 + 字符串值 → number(WHERE int_col = '123' 索引失效)
|
|
279
|
+
* - 整数列 + 布尔值 → 0/1
|
|
280
|
+
* - 字符串列 + 数字值 → string(WHERE varchar_col = 123 索引失效)
|
|
281
|
+
* - 字符串列 + 布尔值 → string
|
|
282
|
+
*
|
|
283
|
+
* @param value 原始值
|
|
284
|
+
* @param fieldType 字段类型(小写)
|
|
285
|
+
* @param sqlDbType 数据库类型
|
|
286
|
+
* @returns 转换后的值
|
|
287
|
+
*/
|
|
288
|
+
convertValueByType(value, fieldType, sqlDbType) {
|
|
289
|
+
if (value === null || value === undefined) {
|
|
290
|
+
return value;
|
|
291
|
+
}
|
|
292
|
+
const valueType = typeof value;
|
|
293
|
+
// 整数列:string → number, boolean → 0/1
|
|
294
|
+
if (this.isIntegerType(fieldType)) {
|
|
295
|
+
if (valueType === 'string') {
|
|
296
|
+
const num = Number(value);
|
|
297
|
+
if (!isNaN(num) && Number.isInteger(num)) {
|
|
298
|
+
return num;
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
if (valueType === 'boolean') {
|
|
302
|
+
return value ? 1 : 0;
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
// 字符串列:number → string, boolean → string
|
|
306
|
+
if (this.isStringType(fieldType)) {
|
|
307
|
+
if (valueType === 'number') {
|
|
308
|
+
return '' + value;
|
|
309
|
+
}
|
|
310
|
+
if (valueType === 'boolean') {
|
|
311
|
+
return value ? 'true' : 'false';
|
|
312
|
+
}
|
|
313
|
+
}
|
|
314
|
+
return value;
|
|
315
|
+
}
|
|
316
|
+
/**
|
|
317
|
+
* 判断字段类型是否为整数类型
|
|
318
|
+
* 覆盖 MySQL/PostgreSQL/SQL Server 三种数据库的整数类型名
|
|
319
|
+
*/
|
|
320
|
+
isIntegerType(type) {
|
|
321
|
+
return REGEX_INTEGER_TYPE.test(type);
|
|
322
|
+
}
|
|
323
|
+
/**
|
|
324
|
+
* 判断字段类型是否为字符串类型
|
|
325
|
+
* 覆盖 MySQL/PostgreSQL/SQL Server 三种数据库的字符串类型名
|
|
326
|
+
*/
|
|
327
|
+
isStringType(type) {
|
|
328
|
+
return REGEX_STRING_TYPE.test(type);
|
|
329
|
+
}
|
|
330
|
+
/**
|
|
331
|
+
* 判断 sqlSimpleName 是否为包含 WHERE 条件的操作类型(查询/删除/更新)
|
|
332
|
+
* 这些操作的 condition 会参与 WHERE 子句,索引失效问题同样存在
|
|
333
|
+
*/
|
|
334
|
+
isConditionType(sqlSimpleName) {
|
|
335
|
+
return !!sqlSimpleName && CONDITION_TYPES.has(sqlSimpleName);
|
|
336
|
+
}
|
|
337
|
+
/**
|
|
338
|
+
* 判断值是否为基本类型(string | number | boolean)
|
|
339
|
+
*/
|
|
340
|
+
isBasicType(value) {
|
|
341
|
+
return typeof value === 'string' || typeof value === 'number' || typeof value === 'boolean';
|
|
342
|
+
}
|
|
141
343
|
/**
|
|
142
344
|
* 判断 sqlSimpleName 是否为 INSERT/UPDATE 类型
|
|
143
345
|
*/
|
|
144
346
|
isInsertOrUpdateType(sqlSimpleName) {
|
|
145
|
-
|
|
146
|
-
return false;
|
|
147
|
-
}
|
|
148
|
-
const types = [
|
|
149
|
-
keys_1.KeysOfSimpleSQL.SIMPLE_INSERT,
|
|
150
|
-
keys_1.KeysOfSimpleSQL.SIMPLE_UPDATE,
|
|
151
|
-
keys_1.KeysOfSimpleSQL.SIMPLE_INSERT_ON_DUPLICATE_UPDATE,
|
|
152
|
-
keys_1.KeysOfSimpleSQL.SIMPLE_INSERT_OR_UPDATE,
|
|
153
|
-
keys_1.KeysOfSimpleSQL.SIMPLE_BATCH_INSERT,
|
|
154
|
-
];
|
|
155
|
-
return types.includes(sqlSimpleName);
|
|
347
|
+
return !!sqlSimpleName && INSERT_OR_UPDATE_TYPES.has(sqlSimpleName);
|
|
156
348
|
}
|
|
157
349
|
}
|
|
158
350
|
exports.CrudProDataTypeConvertService = CrudProDataTypeConvertService;
|
|
@@ -16,6 +16,7 @@ class CrudProExecuteSqlService extends CrudProServiceBase_1.CrudProServiceBase {
|
|
|
16
16
|
async executeSqlCfgModels() {
|
|
17
17
|
const exeCtx = this.getExecuteContext();
|
|
18
18
|
const sqlCfgModels = exeCtx.getSqlCfgModels();
|
|
19
|
+
console.log("CrudProExecuteSqlService===> " + JSON.stringify(sqlCfgModels));
|
|
19
20
|
for (let i = 0; i < sqlCfgModels.length; i++) {
|
|
20
21
|
const sqlCfgModel = sqlCfgModels[i];
|
|
21
22
|
const checkResult = this.executeSqlCfgModelPreCheck(sqlCfgModel);
|
|
@@ -73,9 +74,42 @@ class CrudProExecuteSqlService extends CrudProServiceBase_1.CrudProServiceBase {
|
|
|
73
74
|
// 允许空SQL,没有需要执行的正常。
|
|
74
75
|
return false;
|
|
75
76
|
}
|
|
77
|
+
const errorMsg = `禁止执行的SQL: ${sqlCfgModel.executeSql}`;
|
|
76
78
|
if (keys_1.KeyOfCrudTypes.NOT_CRUD === crudType) {
|
|
77
|
-
//
|
|
78
|
-
|
|
79
|
+
// 在 isNativeSQL 模式下,允许特定的 DDL 操作(CREATE TABLE、CREATE INDEX 等),但禁止 DROP TABLE
|
|
80
|
+
if (sqlCfgModel.isNativeSQL) {
|
|
81
|
+
// 禁止的 DDL 操作(破坏性操作)
|
|
82
|
+
const forbiddenPatterns = [
|
|
83
|
+
/^\s*DROP\s+TABLE/i,
|
|
84
|
+
/^\s*DROP\s+DATABASE/i,
|
|
85
|
+
/^\s*DROP\s+SCHEMA/i,
|
|
86
|
+
/^\s*TRUNCATE\s+TABLE/i,
|
|
87
|
+
];
|
|
88
|
+
for (const pattern of forbiddenPatterns) {
|
|
89
|
+
if (pattern.test(sqlCfgModel.executeSql)) {
|
|
90
|
+
throw new exceptions_1.CommonException(exceptions_1.Exceptions.CFG_NOT_SUPPORT_THE_SQL, errorMsg);
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
// 允许的 DDL 操作白名单
|
|
94
|
+
const allowedPatterns = [
|
|
95
|
+
/^\s*CREATE\s+TABLE/i,
|
|
96
|
+
/^\s*CREATE\s+INDEX/i,
|
|
97
|
+
/^\s*CREATE\s+UNIQUE\s+INDEX/i,
|
|
98
|
+
/^\s*ALTER\s+TABLE/i,
|
|
99
|
+
];
|
|
100
|
+
const isAllowed = allowedPatterns.some(pattern => pattern.test(sqlCfgModel.executeSql));
|
|
101
|
+
if (isAllowed) {
|
|
102
|
+
// 允许的 DDL 操作,继续执行后续校验
|
|
103
|
+
}
|
|
104
|
+
else {
|
|
105
|
+
// 非允许的 DDL 操作,仍然抛出异常
|
|
106
|
+
throw new exceptions_1.CommonException(exceptions_1.Exceptions.CFG_NOT_SUPPORT_THE_SQL, errorMsg);
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
else {
|
|
110
|
+
// 非 isNativeSQL 模式下,只支持增删改查,其他语句不支持。
|
|
111
|
+
throw new exceptions_1.CommonException(exceptions_1.Exceptions.CFG_NOT_SUPPORT_THE_SQL, errorMsg);
|
|
112
|
+
}
|
|
79
113
|
}
|
|
80
114
|
// 此SQL不需要执行
|
|
81
115
|
if (!this.isNeedExecute(sqlCfgModel)) {
|
|
@@ -174,12 +174,16 @@ class CrudProGenSqlCondition {
|
|
|
174
174
|
const value0 = map[compare];
|
|
175
175
|
let tmpSql = '';
|
|
176
176
|
if (equalsIgnoreCase(keys_1.KeysOfConditions.$NULL, compare)) {
|
|
177
|
-
|
|
178
|
-
|
|
177
|
+
if (value0 === true) {
|
|
178
|
+
tmpSql = `${toSqlColumnName(key)} is null `;
|
|
179
|
+
hasNoArgSql = true;
|
|
180
|
+
}
|
|
179
181
|
}
|
|
180
182
|
else if (equalsIgnoreCase(keys_1.KeysOfConditions.$NOT_NULL, compare)) {
|
|
181
|
-
|
|
182
|
-
|
|
183
|
+
if (value0 === true) {
|
|
184
|
+
tmpSql = `${toSqlColumnName(key)} is not null `;
|
|
185
|
+
hasNoArgSql = true;
|
|
186
|
+
}
|
|
183
187
|
}
|
|
184
188
|
else if (equalsIgnoreCase(keys_1.KeysOfConditions.$GT, compare)) {
|
|
185
189
|
tmpArgList.push(value0);
|
|
@@ -2,6 +2,30 @@ import { CrudProServiceBase } from './CrudProServiceBase';
|
|
|
2
2
|
import { ITableListResult, ITableMeta, ITableMetaQuery, ITableNamesQuery, ITableNamesOptions } from '../interfaces';
|
|
3
3
|
declare class CrudProTableMetaService extends CrudProServiceBase {
|
|
4
4
|
getTableMeta(query: ITableMetaQuery): Promise<ITableMeta>;
|
|
5
|
+
/**
|
|
6
|
+
* 清空所有表元数据缓存
|
|
7
|
+
*
|
|
8
|
+
* 清空后,下次请求会自动从数据库重新加载。
|
|
9
|
+
* 适用于定时刷新或手动触发缓存失效的场景。
|
|
10
|
+
*/
|
|
11
|
+
clearAllCache(): void;
|
|
12
|
+
/**
|
|
13
|
+
* 获取缓存统计信息
|
|
14
|
+
*/
|
|
15
|
+
getCacheStats(): {
|
|
16
|
+
metaCacheCount: number;
|
|
17
|
+
tableInfoCacheCount: number;
|
|
18
|
+
};
|
|
19
|
+
/**
|
|
20
|
+
* 获取表的主键列名列表
|
|
21
|
+
*
|
|
22
|
+
* 复用 getTableMeta 的缓存机制,从 columnDetails 中筛选 isPrimaryKey 的列,
|
|
23
|
+
* 避免重复查询数据库。
|
|
24
|
+
*
|
|
25
|
+
* @param query 表元数据查询参数
|
|
26
|
+
* @returns 主键列名数组
|
|
27
|
+
*/
|
|
28
|
+
getPrimaryKeyColumns(query: ITableMetaQuery): Promise<string[]>;
|
|
5
29
|
/**
|
|
6
30
|
* 获取数据库所有表和视图信息(包含类型)
|
|
7
31
|
*
|
|
@@ -21,6 +45,18 @@ declare class CrudProTableMetaService extends CrudProServiceBase {
|
|
|
21
45
|
private loadMySQLColumnDetails;
|
|
22
46
|
private loadPostgreSQLColumnDetails;
|
|
23
47
|
private loadPostgreSQLColumnComments;
|
|
48
|
+
private loadPostgreSQLPrimaryKeys;
|
|
24
49
|
private loadSQLServerColumnDetails;
|
|
50
|
+
private loadSQLServerPrimaryKeys;
|
|
25
51
|
}
|
|
26
52
|
export { CrudProTableMetaService };
|
|
53
|
+
/**
|
|
54
|
+
* 清空所有表元数据缓存(独立函数,无需实例化 Service 即可调用)
|
|
55
|
+
*
|
|
56
|
+
* 适用于定时任务等无法通过 Service 实例访问的场景。
|
|
57
|
+
* 清空后,下次请求会自动从数据库重新加载(懒加载刷新)。
|
|
58
|
+
*/
|
|
59
|
+
export declare function clearTableMetaCache(): {
|
|
60
|
+
metaCacheCount: number;
|
|
61
|
+
tableInfoCacheCount: number;
|
|
62
|
+
};
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.CrudProTableMetaService = void 0;
|
|
3
|
+
exports.clearTableMetaCache = exports.CrudProTableMetaService = void 0;
|
|
4
4
|
const CrudProServiceBase_1 = require("./CrudProServiceBase");
|
|
5
5
|
const keys_1 = require("../models/keys");
|
|
6
6
|
// import { pickAndConvertRowsByMix } from '../utils/sqlConvert/convertMix';
|
|
@@ -37,6 +37,22 @@ class CrudProTableMetaCache {
|
|
|
37
37
|
expiredTime: Date.now() + cacheTime,
|
|
38
38
|
};
|
|
39
39
|
}
|
|
40
|
+
/**
|
|
41
|
+
* 清空所有缓存
|
|
42
|
+
*/
|
|
43
|
+
clearAll() {
|
|
44
|
+
this.cacheMap = {};
|
|
45
|
+
this.tableInfoCacheMap = {};
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* 获取缓存统计信息
|
|
49
|
+
*/
|
|
50
|
+
getCacheStats() {
|
|
51
|
+
return {
|
|
52
|
+
metaCacheCount: Object.keys(this.cacheMap).length,
|
|
53
|
+
tableInfoCacheCount: Object.keys(this.tableInfoCacheMap).length,
|
|
54
|
+
};
|
|
55
|
+
}
|
|
40
56
|
}
|
|
41
57
|
const metaCache = new CrudProTableMetaCache();
|
|
42
58
|
class CrudProTableMetaService extends CrudProServiceBase_1.CrudProServiceBase {
|
|
@@ -48,6 +64,37 @@ class CrudProTableMetaService extends CrudProServiceBase_1.CrudProServiceBase {
|
|
|
48
64
|
}
|
|
49
65
|
return obj;
|
|
50
66
|
}
|
|
67
|
+
/**
|
|
68
|
+
* 清空所有表元数据缓存
|
|
69
|
+
*
|
|
70
|
+
* 清空后,下次请求会自动从数据库重新加载。
|
|
71
|
+
* 适用于定时刷新或手动触发缓存失效的场景。
|
|
72
|
+
*/
|
|
73
|
+
clearAllCache() {
|
|
74
|
+
metaCache.clearAll();
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* 获取缓存统计信息
|
|
78
|
+
*/
|
|
79
|
+
getCacheStats() {
|
|
80
|
+
return metaCache.getCacheStats();
|
|
81
|
+
}
|
|
82
|
+
/**
|
|
83
|
+
* 获取表的主键列名列表
|
|
84
|
+
*
|
|
85
|
+
* 复用 getTableMeta 的缓存机制,从 columnDetails 中筛选 isPrimaryKey 的列,
|
|
86
|
+
* 避免重复查询数据库。
|
|
87
|
+
*
|
|
88
|
+
* @param query 表元数据查询参数
|
|
89
|
+
* @returns 主键列名数组
|
|
90
|
+
*/
|
|
91
|
+
async getPrimaryKeyColumns(query) {
|
|
92
|
+
const meta = await this.getTableMeta(query);
|
|
93
|
+
const pks = (meta.columnDetails || [])
|
|
94
|
+
.filter(col => col.isPrimaryKey)
|
|
95
|
+
.map(col => col.name);
|
|
96
|
+
return pks;
|
|
97
|
+
}
|
|
51
98
|
// ============ 获取表和视图信息(带类型) ============
|
|
52
99
|
/**
|
|
53
100
|
* 获取数据库所有表和视图信息(包含类型)
|
|
@@ -71,8 +118,7 @@ class CrudProTableMetaService extends CrudProServiceBase_1.CrudProServiceBase {
|
|
|
71
118
|
const result = await this.loadAllTableInfos(query);
|
|
72
119
|
// 3. 更新缓存
|
|
73
120
|
if (!skipCache || refreshCache) {
|
|
74
|
-
|
|
75
|
-
metaCache.setTableInfos(cacheKey, result, tableMetaCacheTime || 3600000);
|
|
121
|
+
metaCache.setTableInfos(cacheKey, result, 1000 * 3600 * 24 * 365);
|
|
76
122
|
}
|
|
77
123
|
return result;
|
|
78
124
|
}
|
|
@@ -202,12 +248,15 @@ class CrudProTableMetaService extends CrudProServiceBase_1.CrudProServiceBase {
|
|
|
202
248
|
const columnArray = queryRes.rows || [];
|
|
203
249
|
// 获取字段注释
|
|
204
250
|
const commentMap = await this.loadPostgreSQLColumnComments(baseInfo, schemaname);
|
|
251
|
+
// 获取主键列
|
|
252
|
+
const pkSet = await this.loadPostgreSQLPrimaryKeys(baseInfo, schemaname);
|
|
205
253
|
return columnArray.map((columnObj) => {
|
|
206
254
|
const { column_name, data_type, is_nullable, column_default, character_maximum_length } = columnObj;
|
|
207
255
|
return {
|
|
208
256
|
name: column_name,
|
|
209
257
|
type: data_type,
|
|
210
258
|
isNullable: is_nullable === 'YES',
|
|
259
|
+
isPrimaryKey: pkSet.has(column_name),
|
|
211
260
|
defaultValue: column_default,
|
|
212
261
|
maxLength: character_maximum_length,
|
|
213
262
|
comment: commentMap[column_name],
|
|
@@ -237,6 +286,22 @@ class CrudProTableMetaService extends CrudProServiceBase_1.CrudProServiceBase {
|
|
|
237
286
|
});
|
|
238
287
|
return map;
|
|
239
288
|
}
|
|
289
|
+
async loadPostgreSQLPrimaryKeys(baseInfo, schemaname) {
|
|
290
|
+
const pkSql = `
|
|
291
|
+
SELECT a.attname
|
|
292
|
+
FROM pg_index i
|
|
293
|
+
JOIN pg_attribute a ON a.attrelid = i.indrelid AND a.attnum = ANY(i.indkey)
|
|
294
|
+
JOIN pg_class c ON c.oid = i.indrelid
|
|
295
|
+
JOIN pg_namespace n ON n.oid = c.relnamespace
|
|
296
|
+
WHERE n.nspname = '${schemaname}'
|
|
297
|
+
AND c.relname = '${baseInfo.sqlTable}'
|
|
298
|
+
AND i.indisprimary
|
|
299
|
+
ORDER BY array_position(i.indkey, a.attnum);
|
|
300
|
+
`.trim();
|
|
301
|
+
const queryRes = await this.executeUnsafeQuery(baseInfo, pkSql);
|
|
302
|
+
const pkRows = queryRes.rows || [];
|
|
303
|
+
return new Set(pkRows.map((row) => row.attname));
|
|
304
|
+
}
|
|
240
305
|
async loadSQLServerColumnDetails(baseInfo) {
|
|
241
306
|
const columnArraySql = `
|
|
242
307
|
SELECT
|
|
@@ -258,18 +323,46 @@ class CrudProTableMetaService extends CrudProServiceBase_1.CrudProServiceBase {
|
|
|
258
323
|
`.trim();
|
|
259
324
|
const queryRes = await this.executeUnsafeQuery(baseInfo, columnArraySql);
|
|
260
325
|
const columnArray = queryRes.rows || [];
|
|
326
|
+
// 获取主键列
|
|
327
|
+
const pkSet = await this.loadSQLServerPrimaryKeys(baseInfo);
|
|
261
328
|
return columnArray.map((columnObj) => {
|
|
262
329
|
const { column_name, data_type, is_nullable, column_default, column_comment, max_length, is_identity } = columnObj;
|
|
263
330
|
return {
|
|
264
331
|
name: column_name,
|
|
265
332
|
type: data_type,
|
|
266
333
|
isNullable: is_nullable,
|
|
334
|
+
isPrimaryKey: pkSet.has(column_name),
|
|
267
335
|
defaultValue: column_default,
|
|
268
336
|
comment: column_comment,
|
|
269
337
|
maxLength: max_length,
|
|
270
|
-
|
|
338
|
+
isIdentity: is_identity,
|
|
271
339
|
};
|
|
272
340
|
});
|
|
273
341
|
}
|
|
342
|
+
async loadSQLServerPrimaryKeys(baseInfo) {
|
|
343
|
+
const pkSql = `
|
|
344
|
+
SELECT COL_NAME(ic.object_id, ic.column_id) AS column_name
|
|
345
|
+
FROM sys.indexes i
|
|
346
|
+
INNER JOIN sys.index_columns ic ON i.object_id = ic.object_id AND i.index_id = ic.index_id
|
|
347
|
+
WHERE i.object_id = OBJECT_ID('${baseInfo.sqlTable}')
|
|
348
|
+
AND i.is_primary_key = 1
|
|
349
|
+
ORDER BY ic.key_ordinal;
|
|
350
|
+
`.trim();
|
|
351
|
+
const queryRes = await this.executeUnsafeQuery(baseInfo, pkSql);
|
|
352
|
+
const pkRows = queryRes.rows || [];
|
|
353
|
+
return new Set(pkRows.map((row) => row.column_name));
|
|
354
|
+
}
|
|
274
355
|
}
|
|
275
356
|
exports.CrudProTableMetaService = CrudProTableMetaService;
|
|
357
|
+
/**
|
|
358
|
+
* 清空所有表元数据缓存(独立函数,无需实例化 Service 即可调用)
|
|
359
|
+
*
|
|
360
|
+
* 适用于定时任务等无法通过 Service 实例访问的场景。
|
|
361
|
+
* 清空后,下次请求会自动从数据库重新加载(懒加载刷新)。
|
|
362
|
+
*/
|
|
363
|
+
function clearTableMetaCache() {
|
|
364
|
+
const stats = metaCache.getCacheStats();
|
|
365
|
+
metaCache.clearAll();
|
|
366
|
+
return stats;
|
|
367
|
+
}
|
|
368
|
+
exports.clearTableMetaCache = clearTableMetaCache;
|
|
@@ -31,8 +31,10 @@ declare class CurdProServiceHub implements ICurdProServiceHub {
|
|
|
31
31
|
convertOriginToExecuteSql(sqlCfgModel: SqlCfgModel): Promise<void>;
|
|
32
32
|
executeFuncCfg(funCfg: IFuncCfgModel, funcContext: FuncContext): any;
|
|
33
33
|
getTableMeta(query: ITableMetaQuery): Promise<ITableMeta>;
|
|
34
|
+
getPrimaryKeyColumns(query: ITableMetaQuery): Promise<string[]>;
|
|
34
35
|
getAllTableInfos(query: ITableNamesQuery, options?: ITableNamesOptions): Promise<ITableListResult>;
|
|
35
36
|
filterDataByTableMeta(data: Record<string, any>, sqlCfgModel: ISqlCfgModel | SqlCfgModel): Promise<Record<string, any>>;
|
|
36
37
|
convertDataTypeByTableMeta(reqModel: RequestModel, cfgModel: RequestCfgModel): Promise<void>;
|
|
38
|
+
convertConditionTypeByTableMeta(reqModel: RequestModel, cfgModel: RequestCfgModel): Promise<void>;
|
|
37
39
|
}
|
|
38
40
|
export { CurdProServiceHub };
|
|
@@ -67,6 +67,9 @@ class CurdProServiceHub {
|
|
|
67
67
|
async getTableMeta(query) {
|
|
68
68
|
return await this.tableMetaService.getTableMeta(query);
|
|
69
69
|
}
|
|
70
|
+
async getPrimaryKeyColumns(query) {
|
|
71
|
+
return await this.tableMetaService.getPrimaryKeyColumns(query);
|
|
72
|
+
}
|
|
70
73
|
async getAllTableInfos(query, options) {
|
|
71
74
|
return await this.tableMetaService.getAllTableInfos(query, options);
|
|
72
75
|
}
|
|
@@ -76,5 +79,8 @@ class CurdProServiceHub {
|
|
|
76
79
|
async convertDataTypeByTableMeta(reqModel, cfgModel) {
|
|
77
80
|
return await this.dataTypeConvertService.convertDataTypeByTableMeta(reqModel, cfgModel);
|
|
78
81
|
}
|
|
82
|
+
async convertConditionTypeByTableMeta(reqModel, cfgModel) {
|
|
83
|
+
return await this.dataTypeConvertService.convertConditionTypeByTableMeta(reqModel, cfgModel);
|
|
84
|
+
}
|
|
79
85
|
}
|
|
80
86
|
exports.CurdProServiceHub = CurdProServiceHub;
|