midway-fatcms 0.0.4 → 0.0.6
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/README.md +635 -352
- 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 +41 -1
- package/dist/libs/crud-pro/models/RequestModel.js +103 -39
- 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/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 +699 -0
- package/dist/libs/crud-sharding/ShardingMerger.d.ts +130 -0
- package/dist/libs/crud-sharding/ShardingMerger.js +280 -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 +1001 -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 +3 -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 +108 -44
- 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/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 +856 -0
- package/src/libs/crud-sharding/ShardingMerger.ts +382 -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 +1001 -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
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import { CrudProServiceBase } from './CrudProServiceBase';
|
|
2
|
+
import { RequestModel } from '../models/RequestModel';
|
|
3
|
+
import { RequestCfgModel } from '../models/RequestCfgModel';
|
|
4
|
+
/**
|
|
5
|
+
* 数据类型转换服务
|
|
6
|
+
* 根据表结构字段类型,在 INSERT/UPDATE 操作前自动转换数据格式,确保数据与数据库方言兼容
|
|
7
|
+
*
|
|
8
|
+
* 当前支持的转换:
|
|
9
|
+
* - PostgreSQL ARRAY 类型:JSON 数组 → PG 数组字面量 {"a","b","c"}
|
|
10
|
+
*/
|
|
11
|
+
declare class CrudProDataTypeConvertService extends CrudProServiceBase {
|
|
12
|
+
/**
|
|
13
|
+
* 根据表结构字段类型自动转换 reqModel.data 中的数据格式
|
|
14
|
+
* 仅对 PostgreSQL 的 INSERT/UPDATE 类型操作生效
|
|
15
|
+
*
|
|
16
|
+
* @param reqModel 请求模型
|
|
17
|
+
* @param cfgModel 配置模型
|
|
18
|
+
*/
|
|
19
|
+
convertDataTypeByTableMeta(reqModel: RequestModel, cfgModel: RequestCfgModel): Promise<void>;
|
|
20
|
+
/**
|
|
21
|
+
* 遍历数据对象中的 ARRAY 类型字段,将 JSON 数组转为 PG 数组字面量格式
|
|
22
|
+
*/
|
|
23
|
+
private convertArrayFields;
|
|
24
|
+
/**
|
|
25
|
+
* 尝试将值解析为数组
|
|
26
|
+
* 支持:数组对象、JSON 数组字符串
|
|
27
|
+
*/
|
|
28
|
+
private tryParseArray;
|
|
29
|
+
/**
|
|
30
|
+
* 将 JS 数组转为 PostgreSQL 数组字面量格式
|
|
31
|
+
*
|
|
32
|
+
* PG 数组字面量规则:
|
|
33
|
+
* - 字符串:双引号包裹,内部 " 转义为 \",\ 转义为 \\ 例:{"hello","world"}
|
|
34
|
+
* - 数字:不加引号 例:{1,2,3}
|
|
35
|
+
* - 布尔:转为 t/f 不加引号 例:{t,f}
|
|
36
|
+
* - null/undefined:输出 NULL 不加引号 例:{1,NULL,3}
|
|
37
|
+
* - 空数组:输出 {}
|
|
38
|
+
*
|
|
39
|
+
* @param arr JS 数组
|
|
40
|
+
* @returns PG 数组字面量字符串
|
|
41
|
+
*/
|
|
42
|
+
private toPgArrayLiteral;
|
|
43
|
+
/**
|
|
44
|
+
* 将单个 JS 值转为 PG 数组元素的字面量表示
|
|
45
|
+
*/
|
|
46
|
+
private toPgArrayItem;
|
|
47
|
+
/**
|
|
48
|
+
* 判断 sqlSimpleName 是否为 INSERT/UPDATE 类型
|
|
49
|
+
*/
|
|
50
|
+
private isInsertOrUpdateType;
|
|
51
|
+
}
|
|
52
|
+
export { CrudProDataTypeConvertService };
|
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.CrudProDataTypeConvertService = void 0;
|
|
4
|
+
const CrudProServiceBase_1 = require("./CrudProServiceBase");
|
|
5
|
+
const keys_1 = require("../models/keys");
|
|
6
|
+
/**
|
|
7
|
+
* 数据类型转换服务
|
|
8
|
+
* 根据表结构字段类型,在 INSERT/UPDATE 操作前自动转换数据格式,确保数据与数据库方言兼容
|
|
9
|
+
*
|
|
10
|
+
* 当前支持的转换:
|
|
11
|
+
* - PostgreSQL ARRAY 类型:JSON 数组 → PG 数组字面量 {"a","b","c"}
|
|
12
|
+
*/
|
|
13
|
+
class CrudProDataTypeConvertService extends CrudProServiceBase_1.CrudProServiceBase {
|
|
14
|
+
/**
|
|
15
|
+
* 根据表结构字段类型自动转换 reqModel.data 中的数据格式
|
|
16
|
+
* 仅对 PostgreSQL 的 INSERT/UPDATE 类型操作生效
|
|
17
|
+
*
|
|
18
|
+
* @param reqModel 请求模型
|
|
19
|
+
* @param cfgModel 配置模型
|
|
20
|
+
*/
|
|
21
|
+
async convertDataTypeByTableMeta(reqModel, cfgModel) {
|
|
22
|
+
if (cfgModel.sqlDbType !== keys_1.SqlDbType.postgres) {
|
|
23
|
+
return;
|
|
24
|
+
}
|
|
25
|
+
const sqlSimpleName = cfgModel.sqlSimpleName;
|
|
26
|
+
if (!this.isInsertOrUpdateType(sqlSimpleName)) {
|
|
27
|
+
return;
|
|
28
|
+
}
|
|
29
|
+
if (!reqModel.data) {
|
|
30
|
+
return;
|
|
31
|
+
}
|
|
32
|
+
const query = {
|
|
33
|
+
sqlTable: cfgModel.sqlTable,
|
|
34
|
+
sqlDatabase: cfgModel.sqlDatabase,
|
|
35
|
+
sqlDbType: cfgModel.sqlDbType,
|
|
36
|
+
sqlSchema: cfgModel.sqlSchema,
|
|
37
|
+
};
|
|
38
|
+
const tableMeta = await this.serviceHub.getTableMeta(query);
|
|
39
|
+
if (!tableMeta || !tableMeta.columnDetails || tableMeta.columnDetails.length === 0) {
|
|
40
|
+
this.logger.warn('CrudProDataTypeConvertService: 无法获取表结构信息,跳过数据类型转换', cfgModel.sqlTable);
|
|
41
|
+
return;
|
|
42
|
+
}
|
|
43
|
+
const arrayFieldSet = new Set();
|
|
44
|
+
for (const col of tableMeta.columnDetails) {
|
|
45
|
+
if (('' + col.type).toUpperCase() === 'ARRAY') {
|
|
46
|
+
arrayFieldSet.add(col.name);
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
if (arrayFieldSet.size === 0) {
|
|
50
|
+
return;
|
|
51
|
+
}
|
|
52
|
+
const data = reqModel.data;
|
|
53
|
+
if (Array.isArray(data)) {
|
|
54
|
+
for (const row of data) {
|
|
55
|
+
this.convertArrayFields(row, arrayFieldSet);
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
else {
|
|
59
|
+
this.convertArrayFields(data, arrayFieldSet);
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* 遍历数据对象中的 ARRAY 类型字段,将 JSON 数组转为 PG 数组字面量格式
|
|
64
|
+
*/
|
|
65
|
+
convertArrayFields(dataObj, arrayFieldSet) {
|
|
66
|
+
const keys = Object.keys(dataObj);
|
|
67
|
+
for (const key of keys) {
|
|
68
|
+
if (!arrayFieldSet.has(key)) {
|
|
69
|
+
continue;
|
|
70
|
+
}
|
|
71
|
+
const value = dataObj[key];
|
|
72
|
+
const parsed = this.tryParseArray(value);
|
|
73
|
+
if (Array.isArray(parsed)) {
|
|
74
|
+
dataObj[key] = this.toPgArrayLiteral(parsed);
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
/**
|
|
79
|
+
* 尝试将值解析为数组
|
|
80
|
+
* 支持:数组对象、JSON 数组字符串
|
|
81
|
+
*/
|
|
82
|
+
tryParseArray(value) {
|
|
83
|
+
if (value === null || value === undefined) {
|
|
84
|
+
return null;
|
|
85
|
+
}
|
|
86
|
+
if (Array.isArray(value)) {
|
|
87
|
+
return value;
|
|
88
|
+
}
|
|
89
|
+
if (typeof value === 'string') {
|
|
90
|
+
const trimmed = value.trim();
|
|
91
|
+
if (trimmed.startsWith('[')) {
|
|
92
|
+
try {
|
|
93
|
+
return JSON.parse(trimmed);
|
|
94
|
+
}
|
|
95
|
+
catch (e) {
|
|
96
|
+
return null;
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
return null;
|
|
101
|
+
}
|
|
102
|
+
/**
|
|
103
|
+
* 将 JS 数组转为 PostgreSQL 数组字面量格式
|
|
104
|
+
*
|
|
105
|
+
* PG 数组字面量规则:
|
|
106
|
+
* - 字符串:双引号包裹,内部 " 转义为 \",\ 转义为 \\ 例:{"hello","world"}
|
|
107
|
+
* - 数字:不加引号 例:{1,2,3}
|
|
108
|
+
* - 布尔:转为 t/f 不加引号 例:{t,f}
|
|
109
|
+
* - null/undefined:输出 NULL 不加引号 例:{1,NULL,3}
|
|
110
|
+
* - 空数组:输出 {}
|
|
111
|
+
*
|
|
112
|
+
* @param arr JS 数组
|
|
113
|
+
* @returns PG 数组字面量字符串
|
|
114
|
+
*/
|
|
115
|
+
toPgArrayLiteral(arr) {
|
|
116
|
+
if (arr.length === 0) {
|
|
117
|
+
return '{}';
|
|
118
|
+
}
|
|
119
|
+
const items = arr.map(v => this.toPgArrayItem(v));
|
|
120
|
+
return '{' + items.join(',') + '}';
|
|
121
|
+
}
|
|
122
|
+
/**
|
|
123
|
+
* 将单个 JS 值转为 PG 数组元素的字面量表示
|
|
124
|
+
*/
|
|
125
|
+
toPgArrayItem(value) {
|
|
126
|
+
if (value === null || value === undefined) {
|
|
127
|
+
return 'NULL';
|
|
128
|
+
}
|
|
129
|
+
if (typeof value === 'number') {
|
|
130
|
+
return '' + value;
|
|
131
|
+
}
|
|
132
|
+
if (typeof value === 'boolean') {
|
|
133
|
+
return value ? 't' : 'f';
|
|
134
|
+
}
|
|
135
|
+
// 字符串及其它类型:转义后用双引号包裹
|
|
136
|
+
const escaped = ('' + value)
|
|
137
|
+
.replace(/\\/g, '\\\\')
|
|
138
|
+
.replace(/"/g, '\\"');
|
|
139
|
+
return '"' + escaped + '"';
|
|
140
|
+
}
|
|
141
|
+
/**
|
|
142
|
+
* 判断 sqlSimpleName 是否为 INSERT/UPDATE 类型
|
|
143
|
+
*/
|
|
144
|
+
isInsertOrUpdateType(sqlSimpleName) {
|
|
145
|
+
if (!sqlSimpleName) {
|
|
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);
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
exports.CrudProDataTypeConvertService = CrudProDataTypeConvertService;
|
|
@@ -169,7 +169,14 @@ class CrudProExecuteSqlService extends CrudProServiceBase_1.CrudProServiceBase {
|
|
|
169
169
|
return result === true;
|
|
170
170
|
}
|
|
171
171
|
toQueryResByResPicker(rows, originRes, sqlCfgModel) {
|
|
172
|
-
|
|
172
|
+
let resPicker = sqlCfgModel.resPicker;
|
|
173
|
+
// resPicker 未设置时,根据 SQL 类型自动推断
|
|
174
|
+
if (isEmpty(resPicker)) {
|
|
175
|
+
const crudType = sqlCfgModel.getCrudType();
|
|
176
|
+
if (crudType === keys_1.KeyOfCrudTypes.INSERT || crudType === keys_1.KeyOfCrudTypes.UPDATE || crudType === keys_1.KeyOfCrudTypes.DELETE) {
|
|
177
|
+
resPicker = keys_1.KeysOfSqlResPicker.UPDATE_RESULT;
|
|
178
|
+
}
|
|
179
|
+
}
|
|
173
180
|
// 返回第一行
|
|
174
181
|
if (keys_1.KeysOfSqlResPicker.RESULT_FIRST_ROW === resPicker) {
|
|
175
182
|
if (rows && rows.length > 0) {
|
|
@@ -185,6 +192,18 @@ class CrudProExecuteSqlService extends CrudProServiceBase_1.CrudProServiceBase {
|
|
|
185
192
|
const map0 = rows[0];
|
|
186
193
|
return Number(map0['total_count'] || 0);
|
|
187
194
|
}
|
|
195
|
+
// 判断元素是否存在,返回 boolean
|
|
196
|
+
if (keys_1.KeysOfSqlResPicker.RESULT_IS_EXIST === resPicker) {
|
|
197
|
+
if (isEmpty(rows) || rows.length === 0) {
|
|
198
|
+
return false;
|
|
199
|
+
}
|
|
200
|
+
const val = rows[0]['is_exist'];
|
|
201
|
+
// MySQL/SQLServer 返回 0/1,PostgreSQL 返回 false/true
|
|
202
|
+
if (typeof val === 'boolean') {
|
|
203
|
+
return val;
|
|
204
|
+
}
|
|
205
|
+
return Number(val) > 0;
|
|
206
|
+
}
|
|
188
207
|
// 增删改res的内容是修改结果:包括: insert\delete\update
|
|
189
208
|
if (keys_1.KeysOfSqlResPicker.UPDATE_RESULT === resPicker) {
|
|
190
209
|
if (sqlCfgModel.sqlDbType === keys_1.SqlDbType.postgres) {
|
|
@@ -9,5 +9,12 @@ declare class CrudProFieldValidateService extends CrudProServiceBase {
|
|
|
9
9
|
private validateByCfgString;
|
|
10
10
|
private validateByCfgFunc;
|
|
11
11
|
private validateRequired;
|
|
12
|
+
/**
|
|
13
|
+
* 校验 data 字段类型是否符合 sqlSimpleName 的要求
|
|
14
|
+
*
|
|
15
|
+
* - SIMPLE_BATCH_INSERT: data 必须是数组
|
|
16
|
+
* - 其他 INSERT/UPDATE 类型: data 必须是普通对象
|
|
17
|
+
*/
|
|
18
|
+
validateDataType(cfgModel: RequestCfgModel, reqModel: RequestModel): void;
|
|
12
19
|
}
|
|
13
20
|
export { CrudProFieldValidateService };
|
|
@@ -144,5 +144,37 @@ class CrudProFieldValidateService extends CrudProServiceBase_1.CrudProServiceBas
|
|
|
144
144
|
}
|
|
145
145
|
}
|
|
146
146
|
}
|
|
147
|
+
/**
|
|
148
|
+
* 校验 data 字段类型是否符合 sqlSimpleName 的要求
|
|
149
|
+
*
|
|
150
|
+
* - SIMPLE_BATCH_INSERT: data 必须是数组
|
|
151
|
+
* - 其他 INSERT/UPDATE 类型: data 必须是普通对象
|
|
152
|
+
*/
|
|
153
|
+
validateDataType(cfgModel, reqModel) {
|
|
154
|
+
const sqlSimpleName = cfgModel.sqlSimpleName;
|
|
155
|
+
if (!sqlSimpleName) {
|
|
156
|
+
return;
|
|
157
|
+
}
|
|
158
|
+
const data = reqModel.data;
|
|
159
|
+
const isArrayData = Array.isArray(data);
|
|
160
|
+
if (sqlSimpleName === keys_1.KeysOfSimpleSQL.SIMPLE_BATCH_INSERT) {
|
|
161
|
+
if (!isArrayData || data.length === 0) {
|
|
162
|
+
throw new exceptions_1.CommonException(exceptions_1.Exceptions.CFG_INVALID_DATA_TYPE, `${sqlSimpleName} 要求 data 必须是数组。并且不能为空数组。`);
|
|
163
|
+
}
|
|
164
|
+
return;
|
|
165
|
+
}
|
|
166
|
+
const needsObjectData = [
|
|
167
|
+
keys_1.KeysOfSimpleSQL.SIMPLE_INSERT,
|
|
168
|
+
keys_1.KeysOfSimpleSQL.SIMPLE_UPDATE,
|
|
169
|
+
keys_1.KeysOfSimpleSQL.SIMPLE_INSERT_ON_DUPLICATE_UPDATE,
|
|
170
|
+
keys_1.KeysOfSimpleSQL.SIMPLE_INSERT_OR_UPDATE,
|
|
171
|
+
].includes(sqlSimpleName);
|
|
172
|
+
if (needsObjectData && (!data || Object.keys(data).length === 0)) {
|
|
173
|
+
throw new exceptions_1.CommonException(exceptions_1.Exceptions.CFG_INVALID_DATA_TYPE, `${sqlSimpleName} 要求 data 必须是对象,不能为空`);
|
|
174
|
+
}
|
|
175
|
+
if (needsObjectData && isArrayData) {
|
|
176
|
+
throw new exceptions_1.CommonException(exceptions_1.Exceptions.CFG_INVALID_DATA_TYPE, `${sqlSimpleName} 要求 data 必须是普通对象,不能是数组`);
|
|
177
|
+
}
|
|
178
|
+
}
|
|
147
179
|
}
|
|
148
180
|
exports.CrudProFieldValidateService = CrudProFieldValidateService;
|
|
@@ -2,6 +2,13 @@ import { CrudProServiceBase } from './CrudProServiceBase';
|
|
|
2
2
|
declare class CrudProGenSqlService extends CrudProServiceBase {
|
|
3
3
|
generateSQLList(): Promise<void>;
|
|
4
4
|
private generateOriginSql;
|
|
5
|
+
/**
|
|
6
|
+
* 判断元素是否存在
|
|
7
|
+
* 使用 SELECT EXISTS 子查询,数据库引擎找到第一条匹配记录即停止扫描,比 COUNT(*) 更高效
|
|
8
|
+
* @param cfgModel
|
|
9
|
+
* @private
|
|
10
|
+
*/
|
|
11
|
+
private generateOriginSqlForIsExist;
|
|
5
12
|
/**
|
|
6
13
|
* 删除语句
|
|
7
14
|
* @param cfgModel
|
|
@@ -20,6 +27,12 @@ declare class CrudProGenSqlService extends CrudProServiceBase {
|
|
|
20
27
|
* @private
|
|
21
28
|
*/
|
|
22
29
|
private generateOriginSqlForInsert;
|
|
30
|
+
/**
|
|
31
|
+
* 创建批量插入语句
|
|
32
|
+
* @param cfgModel
|
|
33
|
+
* @private
|
|
34
|
+
*/
|
|
35
|
+
private generateOriginSqlForBatchInsert;
|
|
23
36
|
private generateExecuteSql;
|
|
24
37
|
}
|
|
25
38
|
export { CrudProGenSqlService };
|
|
@@ -31,7 +31,11 @@ class CrudProGenSqlService extends CrudProServiceBase_1.CrudProServiceBase {
|
|
|
31
31
|
let sql1 = null;
|
|
32
32
|
let sql2 = null;
|
|
33
33
|
let sql3 = null;
|
|
34
|
-
if (equalsIgnoreCase(keys_1.KeysOfSimpleSQL.
|
|
34
|
+
if (equalsIgnoreCase(keys_1.KeysOfSimpleSQL.SIMPLE_QUERY_EXIST, simpleSqlName)) {
|
|
35
|
+
sql1 = this.generateOriginSqlForIsExist(cfgModel);
|
|
36
|
+
cfgModel.addSqlCfgModel('is_exist', sql1, keys_1.KeysOfSqlResPicker.RESULT_IS_EXIST);
|
|
37
|
+
}
|
|
38
|
+
else if (equalsIgnoreCase(keys_1.KeysOfSimpleSQL.SIMPLE_QUERY, simpleSqlName)) {
|
|
35
39
|
sql1 = 'select @@columns from @@table where @@asWhere:condition @@orderBys @@offsetLimit';
|
|
36
40
|
cfgModel.addSqlCfgModel('rows', sql1);
|
|
37
41
|
}
|
|
@@ -57,6 +61,10 @@ class CrudProGenSqlService extends CrudProServiceBase_1.CrudProServiceBase {
|
|
|
57
61
|
sql1 = this.generateOriginSqlForInsert(cfgModel);
|
|
58
62
|
cfgModel.addSqlCfgModel('affected', sql1, keys_1.KeysOfSqlResPicker.UPDATE_RESULT);
|
|
59
63
|
}
|
|
64
|
+
else if (equalsIgnoreCase(keys_1.KeysOfSimpleSQL.SIMPLE_BATCH_INSERT, simpleSqlName)) {
|
|
65
|
+
sql1 = this.generateOriginSqlForBatchInsert(cfgModel);
|
|
66
|
+
cfgModel.addSqlCfgModel('affected', sql1, keys_1.KeysOfSqlResPicker.UPDATE_RESULT);
|
|
67
|
+
}
|
|
60
68
|
else if (equalsIgnoreCase(keys_1.KeysOfSimpleSQL.SIMPLE_UPDATE, simpleSqlName)) {
|
|
61
69
|
sql1 = 'update @@table set @@asUpdate:data where @@asWhere:condition ';
|
|
62
70
|
cfgModel.addSqlCfgModel('affected', sql1, keys_1.KeysOfSqlResPicker.UPDATE_RESULT);
|
|
@@ -66,18 +74,18 @@ class CrudProGenSqlService extends CrudProServiceBase_1.CrudProServiceBase {
|
|
|
66
74
|
cfgModel.addSqlCfgModel('affected', sql1, keys_1.KeysOfSqlResPicker.UPDATE_RESULT);
|
|
67
75
|
}
|
|
68
76
|
else if (equalsIgnoreCase(keys_1.KeysOfSimpleSQL.SIMPLE_INSERT_OR_UPDATE, simpleSqlName)) {
|
|
69
|
-
sql1 =
|
|
70
|
-
sql2 =
|
|
77
|
+
sql1 = this.generateOriginSqlForIsExist(cfgModel); // 判断元素是否存在
|
|
78
|
+
sql2 = this.generateOriginSqlForInsert(cfgModel); // 插入语句
|
|
71
79
|
sql3 = 'update @@table set @@asUpdate:data where @@asWhere:condition ';
|
|
72
80
|
const insertWhen2 = {
|
|
73
81
|
functionName: 'eq',
|
|
74
|
-
functionParams: [{
|
|
82
|
+
functionParams: [{ contextAsBool: 'res.is_exist' }, { constBool: false }],
|
|
75
83
|
};
|
|
76
84
|
const updateWhen3 = {
|
|
77
|
-
functionName: '
|
|
78
|
-
functionParams: [{
|
|
85
|
+
functionName: 'eq',
|
|
86
|
+
functionParams: [{ contextAsBool: 'res.is_exist' }, { constBool: true }],
|
|
79
87
|
};
|
|
80
|
-
cfgModel.addSqlCfgModel('
|
|
88
|
+
cfgModel.addSqlCfgModel('is_exist', sql1, keys_1.KeysOfSqlResPicker.RESULT_IS_EXIST);
|
|
81
89
|
cfgModel.addSqlCfgModel('insert_affected', sql2, keys_1.KeysOfSqlResPicker.UPDATE_RESULT, insertWhen2);
|
|
82
90
|
cfgModel.addSqlCfgModel('update_affected', sql3, keys_1.KeysOfSqlResPicker.UPDATE_RESULT, updateWhen3);
|
|
83
91
|
}
|
|
@@ -85,6 +93,21 @@ class CrudProGenSqlService extends CrudProServiceBase_1.CrudProServiceBase {
|
|
|
85
93
|
throw new exceptions_1.CommonException(exceptions_1.Exceptions.CFG_NOT_SUPPORT_THE_SIMPLE_SQL, simpleSqlName);
|
|
86
94
|
}
|
|
87
95
|
}
|
|
96
|
+
/**
|
|
97
|
+
* 判断元素是否存在
|
|
98
|
+
* 使用 SELECT EXISTS 子查询,数据库引擎找到第一条匹配记录即停止扫描,比 COUNT(*) 更高效
|
|
99
|
+
* @param cfgModel
|
|
100
|
+
* @private
|
|
101
|
+
*/
|
|
102
|
+
generateOriginSqlForIsExist(cfgModel) {
|
|
103
|
+
if (cfgModel.sqlDbType === keys_1.SqlDbType.postgres) {
|
|
104
|
+
return 'SELECT EXISTS(SELECT 1 FROM @@table WHERE @@asWhere:condition) AS is_exist';
|
|
105
|
+
}
|
|
106
|
+
if (cfgModel.sqlDbType === keys_1.SqlDbType.sqlserver) {
|
|
107
|
+
return 'SELECT CASE WHEN EXISTS(SELECT 1 FROM @@table WHERE @@asWhere:condition) THEN 1 ELSE 0 END AS is_exist';
|
|
108
|
+
}
|
|
109
|
+
return 'SELECT EXISTS(SELECT 1 FROM @@table WHERE @@asWhere:condition) AS is_exist';
|
|
110
|
+
}
|
|
88
111
|
/**
|
|
89
112
|
* 删除语句
|
|
90
113
|
* @param cfgModel
|
|
@@ -143,6 +166,20 @@ class CrudProGenSqlService extends CrudProServiceBase_1.CrudProServiceBase {
|
|
|
143
166
|
}
|
|
144
167
|
return 'insert into @@table ( @@asInsertKeys:data ) values( @@asInsertValues:data )'; // 关键字的前后,必须有空格
|
|
145
168
|
}
|
|
169
|
+
/**
|
|
170
|
+
* 创建批量插入语句
|
|
171
|
+
* @param cfgModel
|
|
172
|
+
* @private
|
|
173
|
+
*/
|
|
174
|
+
generateOriginSqlForBatchInsert(cfgModel) {
|
|
175
|
+
if (cfgModel.sqlDbType === keys_1.SqlDbType.postgres) {
|
|
176
|
+
return 'insert into @@table ( @@asBatchInsertKeys:data ) values @@asBatchInsertValues:data RETURNING * ';
|
|
177
|
+
}
|
|
178
|
+
if (cfgModel.sqlDbType === keys_1.SqlDbType.sqlserver) {
|
|
179
|
+
return 'insert into @@table ( @@asBatchInsertKeys:data ) OUTPUT INSERTED.* values @@asBatchInsertValues:data ';
|
|
180
|
+
}
|
|
181
|
+
return 'insert into @@table ( @@asBatchInsertKeys:data ) values @@asBatchInsertValues:data '; // MySQL 默认语法
|
|
182
|
+
}
|
|
146
183
|
async generateExecuteSql() {
|
|
147
184
|
const exeCtx = this.getExecuteContext();
|
|
148
185
|
const reqCfgModel = exeCtx.getCfgModel();
|
|
@@ -2,6 +2,15 @@ import { CrudProServiceBase } from './CrudProServiceBase';
|
|
|
2
2
|
import { SqlCfgModel } from '../models/SqlCfgModel';
|
|
3
3
|
declare class CrudProOriginToExecuteSql extends CrudProServiceBase {
|
|
4
4
|
convertOriginToExecuteSql(sqlCfgModel: SqlCfgModel): Promise<void>;
|
|
5
|
+
/**
|
|
6
|
+
* 对于自定义 INSERT SQL,自动追加数据库方言子句:
|
|
7
|
+
* - PostgreSQL: RETURNING *
|
|
8
|
+
* - SQL Server: OUTPUT INSERTED.*
|
|
9
|
+
* - MySQL: 无需追加,驱动自动返回 insertId
|
|
10
|
+
*
|
|
11
|
+
* 仅当 originSql 中用户未手动添加这些子句时才追加
|
|
12
|
+
*/
|
|
13
|
+
private appendDialectClause;
|
|
5
14
|
private generateWordMap;
|
|
6
15
|
private generateWord;
|
|
7
16
|
private generateDataAsInsertKeys;
|
|
@@ -21,6 +30,40 @@ declare class CrudProOriginToExecuteSql extends CrudProServiceBase {
|
|
|
21
30
|
private generateGetAttr;
|
|
22
31
|
private generateDataUpdate;
|
|
23
32
|
private generateDataAsInsertValues;
|
|
33
|
+
/**
|
|
34
|
+
* 获取批量插入的字段名列表
|
|
35
|
+
*
|
|
36
|
+
* 策略:
|
|
37
|
+
* - 如果指定了 columns,直接使用 columns(允许数据行包含额外字段,仅插入指定列)
|
|
38
|
+
* - 否则取第一条数据的 keys,并校验后续行是否一致(严格模式)
|
|
39
|
+
*
|
|
40
|
+
* 严格模式限制:
|
|
41
|
+
* - 所有数据行必须包含完全相同的字段
|
|
42
|
+
* - 字段数量必须一致
|
|
43
|
+
* - 字段名必须一致(顺序可不同)
|
|
44
|
+
* - 若不一致,抛出 BATCH_INSERT_KEYS_MISMATCH 异常
|
|
45
|
+
*
|
|
46
|
+
* @param req RequestModel 请求模型
|
|
47
|
+
* @param dataArray 数据数组
|
|
48
|
+
* @returns 排序后的字段名数组
|
|
49
|
+
*/
|
|
50
|
+
private getBatchInsertKeys;
|
|
51
|
+
/**
|
|
52
|
+
* 批量插入:生成列名部分
|
|
53
|
+
*
|
|
54
|
+
* 列名获取策略:
|
|
55
|
+
* - 优先使用 req.columns(若指定)
|
|
56
|
+
* - 否则使用严格模式:取第一条数据的 keys,校验后续行字段一致性
|
|
57
|
+
*/
|
|
58
|
+
private generateDataAsBatchInsertKeys;
|
|
59
|
+
/**
|
|
60
|
+
* 批量插入:生成值部分,格式为 (?,?,?),(?,?,?),...
|
|
61
|
+
*
|
|
62
|
+
* 列名获取策略:
|
|
63
|
+
* - 优先使用 req.columns(若指定)
|
|
64
|
+
* - 否则使用严格模式:取第一条数据的 keys,校验后续行字段一致性
|
|
65
|
+
*/
|
|
66
|
+
private generateDataAsBatchInsertValues;
|
|
24
67
|
private generateSqlJavaFunction;
|
|
25
68
|
/**
|
|
26
69
|
* 不会返回空。
|
|
@@ -60,7 +60,40 @@ class CrudProOriginToExecuteSql extends CrudProServiceBase_1.CrudProServiceBase
|
|
|
60
60
|
});
|
|
61
61
|
}
|
|
62
62
|
sqlCfgModel.executeSqlArgs = argList;
|
|
63
|
-
sqlCfgModel.executeSql = executeSql;
|
|
63
|
+
sqlCfgModel.executeSql = this.appendDialectClause(executeSql, sqlCfgModel);
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* 对于自定义 INSERT SQL,自动追加数据库方言子句:
|
|
67
|
+
* - PostgreSQL: RETURNING *
|
|
68
|
+
* - SQL Server: OUTPUT INSERTED.*
|
|
69
|
+
* - MySQL: 无需追加,驱动自动返回 insertId
|
|
70
|
+
*
|
|
71
|
+
* 仅当 originSql 中用户未手动添加这些子句时才追加
|
|
72
|
+
*/
|
|
73
|
+
appendDialectClause(executeSql, sqlCfgModel) {
|
|
74
|
+
const sqlDbType = sqlCfgModel.sqlDbType;
|
|
75
|
+
const sqlUpper = executeSql.trim().toUpperCase();
|
|
76
|
+
if (!sqlUpper.startsWith(keys_1.KeyOfCrudTypes.INSERT)) {
|
|
77
|
+
return executeSql;
|
|
78
|
+
}
|
|
79
|
+
if (sqlDbType === keys_1.SqlDbType.postgres) {
|
|
80
|
+
if (sqlUpper.indexOf('RETURNING') < 0) {
|
|
81
|
+
return executeSql + ' RETURNING *';
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
else if (sqlDbType === keys_1.SqlDbType.sqlserver) {
|
|
85
|
+
if (sqlUpper.indexOf('OUTPUT INSERTED') < 0) {
|
|
86
|
+
// SQL Server: OUTPUT INSERTED.* 需放在 VALUES 之前
|
|
87
|
+
// INSERT INTO table (...) OUTPUT INSERTED.* VALUES (...)
|
|
88
|
+
const valuesIndex = sqlUpper.lastIndexOf('VALUES');
|
|
89
|
+
if (valuesIndex > 0) {
|
|
90
|
+
const beforeValues = executeSql.substring(0, valuesIndex);
|
|
91
|
+
const fromValues = executeSql.substring(valuesIndex);
|
|
92
|
+
return beforeValues + ' OUTPUT INSERTED.* ' + fromValues;
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
return executeSql;
|
|
64
97
|
}
|
|
65
98
|
async generateWordMap(originSql, sqlCfgModel) {
|
|
66
99
|
// ['@@columns', '@@table', '@@asWhere:condition', '@@orderBys']
|
|
@@ -115,6 +148,12 @@ class CrudProOriginToExecuteSql extends CrudProServiceBase_1.CrudProServiceBase
|
|
|
115
148
|
else if (word.startsWith(keys_1.KeysOfCustomSQL.SQL_AS_INSERT_VALUES)) {
|
|
116
149
|
return this.generateDataAsInsertValues(reqModel, MixinUtils_1.MixinUtils.removeStringPrefix(word, keys_1.KeysOfCustomSQL.SQL_AS_INSERT_VALUES));
|
|
117
150
|
}
|
|
151
|
+
else if (word.startsWith(keys_1.KeysOfCustomSQL.SQL_AS_BATCH_INSERT_KEYS)) {
|
|
152
|
+
return this.generateDataAsBatchInsertKeys(reqModel, sqlCfg, MixinUtils_1.MixinUtils.removeStringPrefix(word, keys_1.KeysOfCustomSQL.SQL_AS_BATCH_INSERT_KEYS));
|
|
153
|
+
}
|
|
154
|
+
else if (word.startsWith(keys_1.KeysOfCustomSQL.SQL_AS_BATCH_INSERT_VALUES)) {
|
|
155
|
+
return this.generateDataAsBatchInsertValues(reqModel, MixinUtils_1.MixinUtils.removeStringPrefix(word, keys_1.KeysOfCustomSQL.SQL_AS_BATCH_INSERT_VALUES));
|
|
156
|
+
}
|
|
118
157
|
else if (word.startsWith(keys_1.KeysOfCustomSQL.SQL_FUNCTION)) {
|
|
119
158
|
return this.generateSqlJavaFunction(reqCfg, MixinUtils_1.MixinUtils.removeStringPrefix(word, keys_1.KeysOfCustomSQL.SQL_FUNCTION));
|
|
120
159
|
}
|
|
@@ -287,6 +326,98 @@ class CrudProOriginToExecuteSql extends CrudProServiceBase_1.CrudProServiceBase
|
|
|
287
326
|
const sql = sqlList.join(','); // "?,?,?,?"
|
|
288
327
|
return new SqlSegArg_1.SqlSegArg(sql, argList);
|
|
289
328
|
}
|
|
329
|
+
/**
|
|
330
|
+
* 获取批量插入的字段名列表
|
|
331
|
+
*
|
|
332
|
+
* 策略:
|
|
333
|
+
* - 如果指定了 columns,直接使用 columns(允许数据行包含额外字段,仅插入指定列)
|
|
334
|
+
* - 否则取第一条数据的 keys,并校验后续行是否一致(严格模式)
|
|
335
|
+
*
|
|
336
|
+
* 严格模式限制:
|
|
337
|
+
* - 所有数据行必须包含完全相同的字段
|
|
338
|
+
* - 字段数量必须一致
|
|
339
|
+
* - 字段名必须一致(顺序可不同)
|
|
340
|
+
* - 若不一致,抛出 BATCH_INSERT_KEYS_MISMATCH 异常
|
|
341
|
+
*
|
|
342
|
+
* @param req RequestModel 请求模型
|
|
343
|
+
* @param dataArray 数据数组
|
|
344
|
+
* @returns 排序后的字段名数组
|
|
345
|
+
*/
|
|
346
|
+
getBatchInsertKeys(req, dataArray) {
|
|
347
|
+
// 如果指定了 columns,直接使用
|
|
348
|
+
if (MixinUtils_1.MixinUtils.isNotEmpty(req.columns) && req.columns.length > 0) {
|
|
349
|
+
return [...req.columns].sort();
|
|
350
|
+
}
|
|
351
|
+
// 严格模式:取第一条数据的 keys,并校验后续行
|
|
352
|
+
const firstRow = dataArray[0];
|
|
353
|
+
const keys = Object.keys(firstRow).sort();
|
|
354
|
+
for (let i = 1; i < dataArray.length; i++) {
|
|
355
|
+
const row = dataArray[i];
|
|
356
|
+
const rowKeys = Object.keys(row).sort();
|
|
357
|
+
if (rowKeys.length !== keys.length) {
|
|
358
|
+
throw new exceptions_1.CommonException(exceptions_1.Exceptions.BATCH_INSERT_KEYS_MISMATCH, `批量插入第 ${i + 1} 行数据字段数量不一致,期望 ${keys.length} 个,实际 ${rowKeys.length} 个`);
|
|
359
|
+
}
|
|
360
|
+
for (let j = 0; j < keys.length; j++) {
|
|
361
|
+
if (rowKeys[j] !== keys[j]) {
|
|
362
|
+
throw new exceptions_1.CommonException(exceptions_1.Exceptions.BATCH_INSERT_KEYS_MISMATCH, `批量插入第 ${i + 1} 行数据字段不一致,缺少字段 "${keys[j]}" 或包含额外字段`);
|
|
363
|
+
}
|
|
364
|
+
}
|
|
365
|
+
}
|
|
366
|
+
return keys;
|
|
367
|
+
}
|
|
368
|
+
/**
|
|
369
|
+
* 批量插入:生成列名部分
|
|
370
|
+
*
|
|
371
|
+
* 列名获取策略:
|
|
372
|
+
* - 优先使用 req.columns(若指定)
|
|
373
|
+
* - 否则使用严格模式:取第一条数据的 keys,校验后续行字段一致性
|
|
374
|
+
*/
|
|
375
|
+
generateDataAsBatchInsertKeys(req, sqlCfgModel, dataName) {
|
|
376
|
+
const dataArray = req.getCondOrDataAsArray(dataName);
|
|
377
|
+
if (MixinUtils_1.MixinUtils.isEmpty(dataArray) || dataArray.length === 0) {
|
|
378
|
+
throw new exceptions_1.CommonException(exceptions_1.Exceptions.DATA_GET_DATA_EMPTY_ON_INSERT_KEYS, dataName);
|
|
379
|
+
}
|
|
380
|
+
const keys = this.getBatchInsertKeys(req, dataArray);
|
|
381
|
+
const columnList = keys.map(columnName => {
|
|
382
|
+
return (0, convertColumnName_1.toSqlColumnName)(columnName, sqlCfgModel);
|
|
383
|
+
});
|
|
384
|
+
const sql = columnList.join(','); // `column1`,`column2`,`column3`
|
|
385
|
+
return new SqlSegArg_1.SqlSegArg(sql);
|
|
386
|
+
}
|
|
387
|
+
/**
|
|
388
|
+
* 批量插入:生成值部分,格式为 (?,?,?),(?,?,?),...
|
|
389
|
+
*
|
|
390
|
+
* 列名获取策略:
|
|
391
|
+
* - 优先使用 req.columns(若指定)
|
|
392
|
+
* - 否则使用严格模式:取第一条数据的 keys,校验后续行字段一致性
|
|
393
|
+
*/
|
|
394
|
+
generateDataAsBatchInsertValues(req, dataName) {
|
|
395
|
+
const dataArray = req.getCondOrDataAsArray(dataName);
|
|
396
|
+
if (MixinUtils_1.MixinUtils.isEmpty(dataArray) || dataArray.length === 0) {
|
|
397
|
+
throw new exceptions_1.CommonException(exceptions_1.Exceptions.DATA_GET_DATA_EMPTY_ON_INSERT_VALUES, dataName);
|
|
398
|
+
}
|
|
399
|
+
const keys = this.getBatchInsertKeys(req, dataArray);
|
|
400
|
+
const valueGroups = [];
|
|
401
|
+
const argList = [];
|
|
402
|
+
for (let i = 0; i < dataArray.length; i++) {
|
|
403
|
+
const row = dataArray[i];
|
|
404
|
+
const rowValues = [];
|
|
405
|
+
for (let j = 0; j < keys.length; j++) {
|
|
406
|
+
const key = keys[j];
|
|
407
|
+
const arg = row[key];
|
|
408
|
+
if (SqlFuncUtils_1.sqlFuncUtils.isSqlFuncArg(arg)) {
|
|
409
|
+
rowValues.push(SqlFuncUtils_1.sqlFuncUtils.toFuncSQL(arg));
|
|
410
|
+
}
|
|
411
|
+
else {
|
|
412
|
+
rowValues.push('?');
|
|
413
|
+
argList.push(arg);
|
|
414
|
+
}
|
|
415
|
+
}
|
|
416
|
+
valueGroups.push('(' + rowValues.join(',') + ')');
|
|
417
|
+
}
|
|
418
|
+
const sql = valueGroups.join(','); // "(?,?,?),(?,?,?),..."
|
|
419
|
+
return new SqlSegArg_1.SqlSegArg(sql, argList);
|
|
420
|
+
}
|
|
290
421
|
generateSqlJavaFunction(requestCfg, tmpFuncName) {
|
|
291
422
|
const exeCtx = this.getExecuteContext();
|
|
292
423
|
const functionCfg = requestCfg.functionCfg;
|
|
@@ -1,7 +1,21 @@
|
|
|
1
1
|
import { CrudProServiceBase } from './CrudProServiceBase';
|
|
2
|
-
import { ITableMeta, ITableMetaQuery } from '../interfaces';
|
|
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
|
+
* @param query 数据库查询参数
|
|
9
|
+
* @param options 缓存控制选项
|
|
10
|
+
* - skipCache: 跳过缓存,直接查询数据库
|
|
11
|
+
* - refreshCache: 查询后更新缓存
|
|
12
|
+
*/
|
|
13
|
+
getAllTableInfos(query: ITableNamesQuery, options?: ITableNamesOptions): Promise<ITableListResult>;
|
|
14
|
+
private getTableInfoCacheKey;
|
|
15
|
+
private loadAllTableInfos;
|
|
16
|
+
private loadMySQLAllTableInfos;
|
|
17
|
+
private loadPostgreSQLAllTableInfos;
|
|
18
|
+
private loadSQLServerAllTableInfos;
|
|
5
19
|
private loadTableMeta;
|
|
6
20
|
private loadTableColumnDetails;
|
|
7
21
|
private loadMySQLColumnDetails;
|