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
|
@@ -0,0 +1,193 @@
|
|
|
1
|
+
# CrudPro 内置函数参考
|
|
2
|
+
|
|
3
|
+
CrudPro 的 `IFuncCfgModel` 支持 `functionName` 方式调用内置函数。内置函数由四个工具类注册:`DateTimeUtils`、`CompareUtils`、`MixinUtils`、`TypeUtils`。
|
|
4
|
+
|
|
5
|
+
> 源码位置:`src/libs/crud-pro/services/CrudProServiceBase.ts` → `createBuildInExecuteFuncMap()`
|
|
6
|
+
|
|
7
|
+
## 调用方式
|
|
8
|
+
|
|
9
|
+
`functionName` 是 `IFuncCfgModel` 的属性之一,可在 `updateCfg`、`validateCfg`、`executeWhen`、`validate` 等配置中使用。
|
|
10
|
+
|
|
11
|
+
### 无参调用
|
|
12
|
+
|
|
13
|
+
```typescript
|
|
14
|
+
'data.created_at': { functionName: 'getCurrentTimeString' }
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
### 带参调用
|
|
18
|
+
|
|
19
|
+
```typescript
|
|
20
|
+
'data.order_date': { functionName: 'getCurrentDateFormat', functionParams: ['YYYYMMDD'] }
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
`functionParams` 中的每个元素可以是基础类型(string / number / boolean),也可以嵌套 `IFuncCfgModel` 对象:
|
|
24
|
+
|
|
25
|
+
```typescript
|
|
26
|
+
{
|
|
27
|
+
functionName: 'eq',
|
|
28
|
+
functionParams: [
|
|
29
|
+
{ contextAsBool: 'res.is_exist' },
|
|
30
|
+
{ constBool: false }
|
|
31
|
+
]
|
|
32
|
+
}
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
## DateTimeUtils — 日期时间
|
|
36
|
+
|
|
37
|
+
| 函数名 | 参数 | 返回值 | 说明 |
|
|
38
|
+
|--------|------|--------|------|
|
|
39
|
+
| `getCurrentTimeStampMs` | 无 | `number` | 当前时间戳(毫秒) |
|
|
40
|
+
| `getCurrentTimeStampSecond` | 无 | `number` | 当前时间戳(秒) |
|
|
41
|
+
| `getCurrentTimeString` | 无 | `string` | 当前时间,格式 `YYYY-MM-DD HH:mm:ss` |
|
|
42
|
+
| `getCurrentDateFormat` | `format: string` | `string` | 按指定格式返回当前时间 |
|
|
43
|
+
| `getYesterdayDateFormat` | `format: string` | `string` | 按指定格式返回昨天时间 |
|
|
44
|
+
|
|
45
|
+
**示例:**
|
|
46
|
+
|
|
47
|
+
```typescript
|
|
48
|
+
updateCfg: {
|
|
49
|
+
'data.created_at': { functionName: 'getCurrentTimeString' },
|
|
50
|
+
'data.created_ts': { functionName: 'getCurrentTimeStampMs' },
|
|
51
|
+
'data.order_date': { functionName: 'getCurrentDateFormat', functionParams: ['YYYYMMDD'] },
|
|
52
|
+
'data.yesterday': { functionName: 'getYesterdayDateFormat', functionParams: ['YYYY-MM-DD'] },
|
|
53
|
+
}
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
## CompareUtils — 比较运算
|
|
57
|
+
|
|
58
|
+
| 函数名 | 参数 | 返回值 | 说明 |
|
|
59
|
+
|--------|------|--------|------|
|
|
60
|
+
| `eq` | `a: any, b: any` | `boolean` | 相等比较(`==`,兼容类型转换) |
|
|
61
|
+
| `ne` | `a: any, b: any` | `boolean` | 不相等 |
|
|
62
|
+
| `gt` | `a: any, b: any` | `boolean` | 大于 |
|
|
63
|
+
| `gte` | `a: any, b: any` | `boolean` | 大于等于 |
|
|
64
|
+
| `lt` | `a: any, b: any` | `boolean` | 小于 |
|
|
65
|
+
| `lte` | `a: any, b: any` | `boolean` | 小于等于 |
|
|
66
|
+
|
|
67
|
+
**示例:**
|
|
68
|
+
|
|
69
|
+
```typescript
|
|
70
|
+
// executeWhen:仅当 is_exist 为 false 时执行插入
|
|
71
|
+
executeWhen: {
|
|
72
|
+
functionName: 'eq',
|
|
73
|
+
functionParams: [{ contextAsBool: 'res.is_exist' }, { constBool: false }]
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
// validate:确保数量大于 0
|
|
77
|
+
validate: {
|
|
78
|
+
functionName: 'gt',
|
|
79
|
+
functionParams: [{ context: '$current' }, { constNumber: 0 }],
|
|
80
|
+
message: '数量必须大于0'
|
|
81
|
+
}
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
## MixinUtils — 通用工具
|
|
85
|
+
|
|
86
|
+
| 函数名 | 参数 | 返回值 | 说明 |
|
|
87
|
+
|--------|------|--------|------|
|
|
88
|
+
| `isNil` | `obj: any` | `boolean` | 是否为 null 或 undefined |
|
|
89
|
+
| `isNotNil` | `obj: any` | `boolean` | 不为 null 且不为 undefined |
|
|
90
|
+
| `isEmpty` | `obj: any` | `boolean` | 是否为空(null / undefined / 空字符串 / 空数组 / 空对象) |
|
|
91
|
+
| `isNotEmpty` | `obj: any` | `boolean` | 是否非空 |
|
|
92
|
+
| `equals` | `obj1: any, obj2: any` | `boolean` | 严格相等(`===`) |
|
|
93
|
+
| `equalsIgnoreCase` | `str1: string, str2: string` | `boolean` | 忽略大小写比较字符串 |
|
|
94
|
+
| `startsWith` | `str1: string, str2: string` | `boolean` | 字符串前缀判断 |
|
|
95
|
+
| `isValidFieldName` | `s: string` | `boolean` | 合法字段名(字母开头,仅含字母/数字/下划线) |
|
|
96
|
+
| `selectNotEmpty` | `a: any, b: any` | `any` | 返回第一个非空值 |
|
|
97
|
+
| `hasAny` | `a: string[], b: string[]` | `boolean` | 集合 A 是否包含集合 B 中的任意元素 |
|
|
98
|
+
| `parseValueByType` | `value: any, targetType: string` | `any` | 按类型转换值(string / number / boolean / array / JSONObject) |
|
|
99
|
+
| `sleepMs` | `ms: number` | `Promise` | 延时等待 |
|
|
100
|
+
| `uuid` | 无 | `string` | 生成 UUID v4(优先 `crypto.getRandomValues`,降级 `Math.random`) |
|
|
101
|
+
| `generateSnowflakeId` | `machineId?: number` | `string` | 生成雪花ID(64位整数字符串) |
|
|
102
|
+
|
|
103
|
+
**uuid 和雪花ID 示例:**
|
|
104
|
+
|
|
105
|
+
```typescript
|
|
106
|
+
updateCfg: {
|
|
107
|
+
// UUID v4:适合需要标准唯一标识的场景
|
|
108
|
+
'data.id': { functionName: 'uuid' },
|
|
109
|
+
|
|
110
|
+
// 雪花ID:适合需要趋势递增、时间有序的场景
|
|
111
|
+
'data.order_id': { functionName: 'generateSnowflakeId' },
|
|
112
|
+
|
|
113
|
+
// 雪花ID:指定机器ID(0-1023),多实例部署时区分
|
|
114
|
+
'data.trace_id': { functionName: 'generateSnowflakeId', functionParams: [1] },
|
|
115
|
+
}
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
> **雪花ID machineId 获取优先级**:函数参数 > 环境变量 `SNOWFLAKE_MACHINE_ID` > `process.pid` 低10位。多实例部署时建议设置环境变量 `SNOWFLAKE_MACHINE_ID` 为 0-1023 的唯一值。
|
|
119
|
+
|
|
120
|
+
## TypeUtils — 类型判断
|
|
121
|
+
|
|
122
|
+
| 函数名 | 参数 | 返回值 | 说明 |
|
|
123
|
+
|--------|------|--------|------|
|
|
124
|
+
| `isBasicType` | `value: any` | `boolean` | 基础类型(boolean / string / number / null) |
|
|
125
|
+
| `isBoolean` | `value: any` | `boolean` | 布尔值 |
|
|
126
|
+
| `isString` | `value: any` | `boolean` | 字符串 |
|
|
127
|
+
| `isInteger` | `value: any` | `boolean` | 整数 |
|
|
128
|
+
| `isNumber` | `value: any` | `boolean` | 数字 |
|
|
129
|
+
| `isNumeric` | `value: any` | `boolean` | 数字或可转换数字的字符串 |
|
|
130
|
+
| `isValidName` | `value: any` | `boolean` | 合法命名(字母开头,字母/数字/下划线) |
|
|
131
|
+
| `isChinesePhone` | `value: string` | `boolean` | 中文手机号 |
|
|
132
|
+
| `isEmailStrValid` | `value: string` | `boolean` | 合法邮箱地址 |
|
|
133
|
+
| `pickNumber` | `s: string` | `number \| null` | 从字符串中提取数字部分 |
|
|
134
|
+
|
|
135
|
+
## 自定义函数扩展
|
|
136
|
+
|
|
137
|
+
除内置函数外,可通过覆写 `IExecuteContextFunc.getFunctionMap()` 注册自定义函数。
|
|
138
|
+
|
|
139
|
+
函数查找顺序:`getFunctionMap()` → `buildInExecuteFuncMap`(内置函数),自定义函数优先级更高,可覆盖内置函数。
|
|
140
|
+
|
|
141
|
+
### 注册方式
|
|
142
|
+
|
|
143
|
+
继承 `BaseExecuteContextFunc`,覆写 `getFunctionMap()` 方法返回函数映射表:
|
|
144
|
+
|
|
145
|
+
```typescript
|
|
146
|
+
import { BaseExecuteContextFunc } from './libs/crud-pro/models/ExecuteContextFunc';
|
|
147
|
+
|
|
148
|
+
class MyContextFunc extends BaseExecuteContextFunc {
|
|
149
|
+
|
|
150
|
+
// 覆写此方法,返回自定义函数映射表
|
|
151
|
+
getFunctionMap(): any {
|
|
152
|
+
return {
|
|
153
|
+
// 无参函数
|
|
154
|
+
generateOrderNo: (prefix: string) => {
|
|
155
|
+
return prefix + Date.now().toString(36).toUpperCase();
|
|
156
|
+
},
|
|
157
|
+
|
|
158
|
+
// 带参函数:参数来自 functionParams 配置
|
|
159
|
+
formatAmount: (amount: number, currency: string) => {
|
|
160
|
+
return currency + ' ' + amount.toFixed(2);
|
|
161
|
+
},
|
|
162
|
+
|
|
163
|
+
// 可访问 FuncContext 的函数(通过 this 关键字)
|
|
164
|
+
getVisitorName: function(this: any) {
|
|
165
|
+
return this.reqModel?.visitor?.nickName || 'anonymous';
|
|
166
|
+
},
|
|
167
|
+
};
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
// 创建 CrudPro 实例时注入
|
|
172
|
+
const crudPro = new CrudPro();
|
|
173
|
+
crudPro.contextFunc = new MyContextFunc(/* 构造参数 */);
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
### 在配置中使用
|
|
177
|
+
|
|
178
|
+
注册后即可在 `updateCfg`、`validateCfg`、`functionCfg` 等场景中通过 `functionName` 调用:
|
|
179
|
+
|
|
180
|
+
```typescript
|
|
181
|
+
const cfgJson: IRequestCfgModel = {
|
|
182
|
+
method: 'order.create',
|
|
183
|
+
sqlSimpleName: KeysOfSimpleSQL.SIMPLE_INSERT,
|
|
184
|
+
updateCfg: {
|
|
185
|
+
'data.order_no': { functionName: 'generateOrderNo', functionParams: ['PRE'] },
|
|
186
|
+
},
|
|
187
|
+
};
|
|
188
|
+
```
|
|
189
|
+
|
|
190
|
+
> **注意**:`functionParams` 中配置的参数会按顺序传入函数。如果函数需要访问 `FuncContext`(如 `reqModel`、`resModel`、`currentValue`),
|
|
191
|
+
> 需使用普通 `function`(非箭头函数)并通过 `this` 访问,框架会以 `func.apply(funcContext, params)` 方式调用。
|
|
192
|
+
|
|
193
|
+
> **注意**:`generateOrderNo` 等并非内置函数,需通过 `contextFunc` 扩展注册后才可使用,否则运行时抛出 `RUN_FUNCTION_NOT_FOUND` 异常。
|
|
@@ -49,6 +49,7 @@ export enum Exceptions {
|
|
|
49
49
|
CFG_UNIQUE_COLUMN_COUNT_MUST_ONE = 'CFG_UNIQUE_COLUMN_COUNT_MUST_ONE',
|
|
50
50
|
CFG_ORDER_BY_TYPE_ERROR = 'CFG_ORDER_BY_TYPE_ERROR',
|
|
51
51
|
CFG_ERROR_POSTGRES_COLUMN_NAME_INVALID = 'CFG_ERROR_POSTGRES_COLUMN_NAME_INVALID',
|
|
52
|
+
CFG_INVALID_DATA_TYPE = 'CFG_INVALID_DATA_TYPE',
|
|
52
53
|
|
|
53
54
|
/**
|
|
54
55
|
* 数据传错
|
|
@@ -58,6 +59,7 @@ export enum Exceptions {
|
|
|
58
59
|
DATA_GET_DATA_EMPTY_ON_UPDATE = 'DATA_GET_DATA_EMPTY_ON_UPDATE',
|
|
59
60
|
DATA_GET_DATA_EMPTY_ON_INSERT_KEYS = 'DATA_GET_DATA_EMPTY_ON_INSERT_KEYS',
|
|
60
61
|
DATA_GET_DATA_EMPTY_ON_INSERT_VALUES = 'DATA_GET_DATA_EMPTY_ON_INSERT_VALUES',
|
|
62
|
+
BATCH_INSERT_KEYS_MISMATCH = 'BATCH_INSERT_KEYS_MISMATCH', // 批量插入时字段不一致
|
|
61
63
|
|
|
62
64
|
/**
|
|
63
65
|
* 请求的Method字段为空
|
|
@@ -54,7 +54,7 @@ export interface IRequestModel {
|
|
|
54
54
|
method?: string;
|
|
55
55
|
columns?: string | string[]; // "columns": "id,name,age,sex,addr",
|
|
56
56
|
condition?: Record<string, any>; // 拼接查询条件
|
|
57
|
-
data?: Record<string, any
|
|
57
|
+
data?: Record<string, any> | Record<string, any>[]; // 单条或批量插入/更新的数据部分
|
|
58
58
|
orderBy?: string | IOrderByItem[];
|
|
59
59
|
limit?: number;
|
|
60
60
|
offset?: number;
|
|
@@ -182,6 +182,7 @@ export interface IRequestCfgModel extends IBaseCfgModel {
|
|
|
182
182
|
transactionIsolation?: number; // 事务隔离级别
|
|
183
183
|
sqlSimpleName?: KeysOfSimpleSQL; // 二选一 sqlSimpleName sqlCfgList;; sqlCfgList 优先级高于sqlSimpleName
|
|
184
184
|
sqlCfgList?: ISqlCfgModel[]; // 二选一 sqlSimpleName sqlCfgList;; sqlCfgList 优先级高于sqlSimpleName
|
|
185
|
+
uniqueColumn?: string[]; // PostgreSQL/SQL Server 的唯一键列,用于 INSERT ON CONFLICT / MERGE
|
|
185
186
|
updateCfg?: Record<string, IFuncCfgModel>;
|
|
186
187
|
allowCfg?: Record<string, string[]>;
|
|
187
188
|
rejectCfg?: Record<string, string[]>;
|
|
@@ -223,3 +224,39 @@ export interface ITableMetaQuery {
|
|
|
223
224
|
sqlDbType: SqlDbType;
|
|
224
225
|
sqlSchema?: string;
|
|
225
226
|
}
|
|
227
|
+
|
|
228
|
+
/**
|
|
229
|
+
* 表名列表查询参数
|
|
230
|
+
*/
|
|
231
|
+
export interface ITableNamesQuery {
|
|
232
|
+
sqlDatabase: string;
|
|
233
|
+
sqlDbType: SqlDbType;
|
|
234
|
+
sqlSchema?: string;
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
/**
|
|
238
|
+
* 表名查询缓存选项
|
|
239
|
+
*/
|
|
240
|
+
export interface ITableNamesOptions {
|
|
241
|
+
/** 是否跳过缓存,直接查询数据库 */
|
|
242
|
+
skipCache?: boolean;
|
|
243
|
+
/** 查询后是否更新缓存 */
|
|
244
|
+
refreshCache?: boolean;
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
/**
|
|
248
|
+
* 表信息项(包含表名和类型)
|
|
249
|
+
*/
|
|
250
|
+
export interface ITableInfo {
|
|
251
|
+
name: string; // 表名
|
|
252
|
+
tableType: string; // 类型:'table' | 'view'
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
/**
|
|
256
|
+
* 表信息查询结果
|
|
257
|
+
*/
|
|
258
|
+
export interface ITableListResult {
|
|
259
|
+
tables: ITableInfo[]; // 所有表和视图
|
|
260
|
+
tableNames: string[]; // 仅表名(不含视图)
|
|
261
|
+
viewNames: string[]; // 仅视图名
|
|
262
|
+
}
|
|
@@ -7,7 +7,7 @@ import { Transaction } from './Transaction';
|
|
|
7
7
|
import { ColumnRelation, ICrudProCfg, ILogger, IVisitor } from '../interfaces';
|
|
8
8
|
import { IExecuteContextFunc } from './ExecuteContextFunc';
|
|
9
9
|
import { defaultCrudProCfg } from '../defaultConfigs';
|
|
10
|
-
import { ResModelFlexible, ResModelPageQuery } from './ResModel';
|
|
10
|
+
import { ResModelFlexible, ResModelPageQuery, ResModelRowEntity } from './ResModel';
|
|
11
11
|
|
|
12
12
|
class ExecuteContext {
|
|
13
13
|
// 运行前设置的
|
|
@@ -66,11 +66,14 @@ class ExecuteContext {
|
|
|
66
66
|
return this.resModel;
|
|
67
67
|
}
|
|
68
68
|
|
|
69
|
-
|
|
69
|
+
|
|
70
|
+
// 获取单条数据对象
|
|
71
|
+
getOneObj(): ResModelRowEntity {
|
|
70
72
|
return this.resModel['row'];
|
|
71
73
|
}
|
|
72
74
|
|
|
73
|
-
|
|
75
|
+
// 获取多条数据对象
|
|
76
|
+
getResRows(): ResModelRowEntity[] {
|
|
74
77
|
return this.resModel['rows'];
|
|
75
78
|
}
|
|
76
79
|
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import * as _ from 'lodash';
|
|
2
2
|
import { ILimitOffset, IOrderByItem, IRequestModel, IVisitor } from '../interfaces';
|
|
3
3
|
import { MixinUtils } from '../utils/MixinUtils';
|
|
4
|
+
import { OrderByUtils } from '../utils/OrderByUtils';
|
|
4
5
|
import { CommonException, Exceptions } from '../exceptions';
|
|
5
6
|
import { DEFAULT_LIMIT } from '../defaultConfigs';
|
|
6
7
|
|
|
@@ -10,7 +11,7 @@ class RequestModel {
|
|
|
10
11
|
method: string;
|
|
11
12
|
columns?: string[]; // "columns": "id,name,age,sex,addr",
|
|
12
13
|
condition?: Record<string, any>;
|
|
13
|
-
data?: Record<string, any
|
|
14
|
+
data?: Record<string, any> | Record<string, any>[]; // 支持单条或批量数据
|
|
14
15
|
limit: number;
|
|
15
16
|
offset: number;
|
|
16
17
|
orderBys?: IOrderByItem[];
|
|
@@ -19,75 +20,12 @@ class RequestModel {
|
|
|
19
20
|
Object.assign(this, req);
|
|
20
21
|
this.visitor = visitor;
|
|
21
22
|
this.columns = MixinUtils.parseColumns(req.columns);
|
|
22
|
-
this.orderBys =
|
|
23
|
+
this.orderBys = OrderByUtils.parseOrderBys(req.orderBy);
|
|
23
24
|
const limitOffset = this.parseOffsetList(req);
|
|
24
25
|
this.limit = limitOffset.limit;
|
|
25
26
|
this.offset = limitOffset.offset;
|
|
26
27
|
}
|
|
27
28
|
|
|
28
|
-
private parseOrderBys(orderByStr: any): IOrderByItem[] {
|
|
29
|
-
if (MixinUtils.isEmpty(orderByStr)) {
|
|
30
|
-
return [];
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
if (Array.isArray(orderByStr)) {
|
|
34
|
-
return orderByStr
|
|
35
|
-
.map(obj => {
|
|
36
|
-
if (typeof obj === 'string') {
|
|
37
|
-
return { fieldName: obj, orderType: 'asc' };
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
const { fieldName, orderType = 'asc' } = obj;
|
|
41
|
-
if (fieldName) {
|
|
42
|
-
return { fieldName, orderType };
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
return null;
|
|
46
|
-
})
|
|
47
|
-
.filter(o => {
|
|
48
|
-
return !!o;
|
|
49
|
-
});
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
const orderByResult: IOrderByItem[] = [];
|
|
53
|
-
const orderByArray = orderByStr
|
|
54
|
-
.split(',')
|
|
55
|
-
.map(s => {
|
|
56
|
-
return s.trim();
|
|
57
|
-
})
|
|
58
|
-
.filter(s => !!s);
|
|
59
|
-
|
|
60
|
-
if (MixinUtils.isEmpty(orderByArray)) {
|
|
61
|
-
return orderByResult;
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
for (let i = 0; i < orderByArray.length; i++) {
|
|
65
|
-
const orderByItem = orderByArray[i];
|
|
66
|
-
let orderType = 'asc'; //默认升序
|
|
67
|
-
let fieldName = orderByItem;
|
|
68
|
-
|
|
69
|
-
if (orderByItem.endsWith('+')) {
|
|
70
|
-
// 升序
|
|
71
|
-
fieldName = orderByItem.substring(0, orderByItem.length - 1);
|
|
72
|
-
orderType = 'asc'; //升序
|
|
73
|
-
} else if (orderByItem.endsWith('-')) {
|
|
74
|
-
//降序
|
|
75
|
-
fieldName = orderByItem.substring(0, orderByItem.length - 1);
|
|
76
|
-
orderType = 'desc'; //降序
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
fieldName = fieldName.trim();
|
|
80
|
-
|
|
81
|
-
if (MixinUtils.isEmpty(fieldName) || !MixinUtils.isValidFieldName(fieldName)) {
|
|
82
|
-
throw new CommonException(Exceptions.REQUEST_MODEL_PARSE_ORDER_BY_FAILED, orderByItem);
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
orderByResult.push({ fieldName, orderType });
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
return orderByResult;
|
|
89
|
-
}
|
|
90
|
-
|
|
91
29
|
private parseOffsetList(req: IRequestModel): ILimitOffset {
|
|
92
30
|
const { limit, offset, pageSize, pageNo } = req;
|
|
93
31
|
|
|
@@ -129,6 +67,26 @@ class RequestModel {
|
|
|
129
67
|
throw new CommonException(Exceptions.RUN_GET_COND_OR_DATA_JSONOBJECT, attrName);
|
|
130
68
|
}
|
|
131
69
|
|
|
70
|
+
/**
|
|
71
|
+
* 获取数组类型的数据(用于批量插入)
|
|
72
|
+
* @param attrName
|
|
73
|
+
*/
|
|
74
|
+
getCondOrDataAsArray(attrName: string): Record<string, any>[] {
|
|
75
|
+
if (attrName.startsWith('data')) {
|
|
76
|
+
const dataValue = _.get(this, attrName);
|
|
77
|
+
if (!dataValue) {
|
|
78
|
+
return null;
|
|
79
|
+
}
|
|
80
|
+
// 如果是数组直接返回
|
|
81
|
+
if (Array.isArray(dataValue)) {
|
|
82
|
+
return dataValue;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
throw new CommonException(Exceptions.RUN_GET_COND_OR_DATA_JSONOBJECT, attrName);
|
|
86
|
+
}
|
|
87
|
+
throw new CommonException(Exceptions.RUN_GET_COND_OR_DATA_JSONOBJECT, attrName);
|
|
88
|
+
}
|
|
89
|
+
|
|
132
90
|
/**
|
|
133
91
|
* 获取基本数据类型
|
|
134
92
|
* @param attrName
|
|
@@ -3,17 +3,23 @@ interface ResModelAffected {
|
|
|
3
3
|
affectedRows: number;
|
|
4
4
|
}
|
|
5
5
|
|
|
6
|
+
|
|
7
|
+
// 单条数据对象
|
|
8
|
+
type ResModelRowEntity = Record<string, any>;
|
|
9
|
+
|
|
10
|
+
|
|
6
11
|
interface ResModelStandard {
|
|
7
|
-
row?:
|
|
8
|
-
rows?:
|
|
12
|
+
row?: ResModelRowEntity;
|
|
13
|
+
rows?: ResModelRowEntity[];
|
|
9
14
|
total_count?: number;
|
|
15
|
+
is_exist?: boolean;
|
|
10
16
|
affected?: ResModelAffected;
|
|
11
17
|
insert_affected?: ResModelAffected;
|
|
12
18
|
update_affected?: ResModelAffected;
|
|
13
19
|
}
|
|
14
20
|
|
|
15
21
|
interface ResModelPageQuery {
|
|
16
|
-
rows:
|
|
22
|
+
rows: ResModelRowEntity[];
|
|
17
23
|
total_count: number;
|
|
18
24
|
}
|
|
19
25
|
|
|
@@ -21,4 +27,4 @@ type ResModelFlexible = ResModelStandard & {
|
|
|
21
27
|
[key: string]: any;
|
|
22
28
|
};
|
|
23
29
|
|
|
24
|
-
export { ResModelAffected, ResModelStandard, ResModelFlexible, ResModelPageQuery };
|
|
30
|
+
export { ResModelAffected, ResModelStandard, ResModelFlexible, ResModelPageQuery, ResModelRowEntity };
|
|
@@ -29,4 +29,6 @@ export interface ICurdProServiceHub {
|
|
|
29
29
|
executeFuncCfg(tmpFunCfg: IFuncCfgModel, exeFunCtx: FuncContext): string;
|
|
30
30
|
|
|
31
31
|
getTableMeta(query: ITableMetaQuery): Promise<ITableMeta>;
|
|
32
|
+
|
|
33
|
+
convertDataTypeByTableMeta(reqModel: RequestModel, cfgModel: RequestCfgModel): Promise<void>;
|
|
32
34
|
}
|
|
@@ -57,8 +57,10 @@ export enum KeysOfSimpleSQL {
|
|
|
57
57
|
SIMPLE_QUERY_ONE = 'SIMPLE_QUERY_ONE', //只查询一条 select * from xx where @condition limit 1,
|
|
58
58
|
SIMPLE_QUERY_PAGE = 'SIMPLE_QUERY_PAGE', //分页查询 select * from xx where @condition limit 100 offset 100,
|
|
59
59
|
SIMPLE_QUERY_COUNT = 'SIMPLE_QUERY_COUNT', //查询数量 select count(0) as totalCount from xx where @condition,
|
|
60
|
+
SIMPLE_QUERY_EXIST = 'SIMPLE_QUERY_EXIST', //判断一个元素是否存在
|
|
60
61
|
SIMPLE_UPDATE = 'SIMPLE_UPDATE', // update xx set @data where @condition limit 1
|
|
61
62
|
SIMPLE_INSERT = 'SIMPLE_INSERT', // insert into xx values @datas
|
|
63
|
+
SIMPLE_BATCH_INSERT = 'SIMPLE_BATCH_INSERT', // 批量插入 insert into xx values (...),(...),(...)
|
|
62
64
|
SIMPLE_DELETE = 'SIMPLE_DELETE', // delete from xx values where @condition limit 1
|
|
63
65
|
SIMPLE_INSERT_ON_DUPLICATE_UPDATE = 'SIMPLE_INSERT_ON_DUPLICATE_UPDATE',
|
|
64
66
|
SIMPLE_INSERT_OR_UPDATE = 'SIMPLE_INSERT_OR_UPDATE',
|
|
@@ -70,6 +72,7 @@ export enum KeysOfSqlResPicker {
|
|
|
70
72
|
RESULT_FIRST_ROW = 'RESULT_FIRST_ROW', // $ResultSet[0], 只取第一行的数据返回。返回JSONObject
|
|
71
73
|
RESULT_TOTAL_COUNT = 'RESULT_TOTAL_COUNT', // $ResultSet[0].total_count , 只取第一行的的total_count。返回long
|
|
72
74
|
RESULT_ONE_VALUE = 'RESULT_ONE_VALUE', // $ResultSet[0].total_count , 只取第一行的的total_count。返回long
|
|
75
|
+
RESULT_IS_EXIST = 'RESULT_IS_EXIST', // 返回boolean
|
|
73
76
|
}
|
|
74
77
|
|
|
75
78
|
export const KeysOfCustomSQL = {
|
|
@@ -82,6 +85,8 @@ export const KeysOfCustomSQL = {
|
|
|
82
85
|
SQL_AS_UPDATE: '@@asUpdate:', // @@asUpdate:data, @@asWhere:data_zhangsan
|
|
83
86
|
SQL_AS_INSERT_KEYS: '@@asInsertKeys:', // @@asInsertKeys:data, @@asInsertKeys:data_zhangsan
|
|
84
87
|
SQL_AS_INSERT_VALUES: '@@asInsertValues:', // @@asInsertValues:data, @@asInsertValues:data_zhangsan
|
|
88
|
+
SQL_AS_BATCH_INSERT_KEYS: '@@asBatchInsertKeys:', // 批量插入:列名部分,取第一条数据的keys
|
|
89
|
+
SQL_AS_BATCH_INSERT_VALUES: '@@asBatchInsertValues:', // 批量插入:值部分,生成 (?,?,?),(?,?),...
|
|
85
90
|
|
|
86
91
|
// 从Req中取值:单独取Data中的某个字段:获取单个值。
|
|
87
92
|
SQL_GET_DATA_ATTR_START: '@@data', // // select * from fa_lang_faq where id = @@data.id
|
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
import { CrudProServiceBase } from './CrudProServiceBase';
|
|
2
|
+
import { RequestModel } from '../models/RequestModel';
|
|
3
|
+
import { RequestCfgModel } from '../models/RequestCfgModel';
|
|
4
|
+
import { KeysOfSimpleSQL, SqlDbType } from '../models/keys';
|
|
5
|
+
import { ITableMetaQuery } from '../interfaces';
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* 数据类型转换服务
|
|
9
|
+
* 根据表结构字段类型,在 INSERT/UPDATE 操作前自动转换数据格式,确保数据与数据库方言兼容
|
|
10
|
+
*
|
|
11
|
+
* 当前支持的转换:
|
|
12
|
+
* - PostgreSQL ARRAY 类型:JSON 数组 → PG 数组字面量 {"a","b","c"}
|
|
13
|
+
*/
|
|
14
|
+
class CrudProDataTypeConvertService extends CrudProServiceBase {
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* 根据表结构字段类型自动转换 reqModel.data 中的数据格式
|
|
18
|
+
* 仅对 PostgreSQL 的 INSERT/UPDATE 类型操作生效
|
|
19
|
+
*
|
|
20
|
+
* @param reqModel 请求模型
|
|
21
|
+
* @param cfgModel 配置模型
|
|
22
|
+
*/
|
|
23
|
+
async convertDataTypeByTableMeta(reqModel: RequestModel, cfgModel: RequestCfgModel): Promise<void> {
|
|
24
|
+
if (cfgModel.sqlDbType !== SqlDbType.postgres) {
|
|
25
|
+
return;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
const sqlSimpleName = cfgModel.sqlSimpleName;
|
|
29
|
+
if (!this.isInsertOrUpdateType(sqlSimpleName)) {
|
|
30
|
+
return;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
if (!reqModel.data) {
|
|
34
|
+
return;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
const query: ITableMetaQuery = {
|
|
38
|
+
sqlTable: cfgModel.sqlTable,
|
|
39
|
+
sqlDatabase: cfgModel.sqlDatabase,
|
|
40
|
+
sqlDbType: cfgModel.sqlDbType,
|
|
41
|
+
sqlSchema: cfgModel.sqlSchema,
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
const tableMeta = await this.serviceHub.getTableMeta(query);
|
|
45
|
+
if (!tableMeta || !tableMeta.columnDetails || tableMeta.columnDetails.length === 0) {
|
|
46
|
+
this.logger.warn('CrudProDataTypeConvertService: 无法获取表结构信息,跳过数据类型转换', cfgModel.sqlTable);
|
|
47
|
+
return;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
const arrayFieldSet = new Set<string>();
|
|
51
|
+
for (const col of tableMeta.columnDetails) {
|
|
52
|
+
if (('' + col.type).toUpperCase() === 'ARRAY') {
|
|
53
|
+
arrayFieldSet.add(col.name);
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
if (arrayFieldSet.size === 0) {
|
|
58
|
+
return;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
const data = reqModel.data;
|
|
62
|
+
if (Array.isArray(data)) {
|
|
63
|
+
for (const row of data) {
|
|
64
|
+
this.convertArrayFields(row, arrayFieldSet);
|
|
65
|
+
}
|
|
66
|
+
} else {
|
|
67
|
+
this.convertArrayFields(data, arrayFieldSet);
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* 遍历数据对象中的 ARRAY 类型字段,将 JSON 数组转为 PG 数组字面量格式
|
|
73
|
+
*/
|
|
74
|
+
private convertArrayFields(dataObj: Record<string, any>, arrayFieldSet: Set<string>): void {
|
|
75
|
+
const keys = Object.keys(dataObj);
|
|
76
|
+
for (const key of keys) {
|
|
77
|
+
if (!arrayFieldSet.has(key)) {
|
|
78
|
+
continue;
|
|
79
|
+
}
|
|
80
|
+
const value = dataObj[key];
|
|
81
|
+
const parsed = this.tryParseArray(value);
|
|
82
|
+
if (Array.isArray(parsed)) {
|
|
83
|
+
dataObj[key] = this.toPgArrayLiteral(parsed);
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* 尝试将值解析为数组
|
|
90
|
+
* 支持:数组对象、JSON 数组字符串
|
|
91
|
+
*/
|
|
92
|
+
private tryParseArray(value: any): any {
|
|
93
|
+
if (value === null || value === undefined) {
|
|
94
|
+
return null;
|
|
95
|
+
}
|
|
96
|
+
if (Array.isArray(value)) {
|
|
97
|
+
return value;
|
|
98
|
+
}
|
|
99
|
+
if (typeof value === 'string') {
|
|
100
|
+
const trimmed = value.trim();
|
|
101
|
+
if (trimmed.startsWith('[')) {
|
|
102
|
+
try {
|
|
103
|
+
return JSON.parse(trimmed);
|
|
104
|
+
} catch (e) {
|
|
105
|
+
return null;
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
return null;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
/**
|
|
113
|
+
* 将 JS 数组转为 PostgreSQL 数组字面量格式
|
|
114
|
+
*
|
|
115
|
+
* PG 数组字面量规则:
|
|
116
|
+
* - 字符串:双引号包裹,内部 " 转义为 \",\ 转义为 \\ 例:{"hello","world"}
|
|
117
|
+
* - 数字:不加引号 例:{1,2,3}
|
|
118
|
+
* - 布尔:转为 t/f 不加引号 例:{t,f}
|
|
119
|
+
* - null/undefined:输出 NULL 不加引号 例:{1,NULL,3}
|
|
120
|
+
* - 空数组:输出 {}
|
|
121
|
+
*
|
|
122
|
+
* @param arr JS 数组
|
|
123
|
+
* @returns PG 数组字面量字符串
|
|
124
|
+
*/
|
|
125
|
+
private toPgArrayLiteral(arr: any[]): string {
|
|
126
|
+
if (arr.length === 0) {
|
|
127
|
+
return '{}';
|
|
128
|
+
}
|
|
129
|
+
const items = arr.map(v => this.toPgArrayItem(v));
|
|
130
|
+
return '{' + items.join(',') + '}';
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
/**
|
|
134
|
+
* 将单个 JS 值转为 PG 数组元素的字面量表示
|
|
135
|
+
*/
|
|
136
|
+
private toPgArrayItem(value: any): string {
|
|
137
|
+
if (value === null || value === undefined) {
|
|
138
|
+
return 'NULL';
|
|
139
|
+
}
|
|
140
|
+
if (typeof value === 'number') {
|
|
141
|
+
return '' + value;
|
|
142
|
+
}
|
|
143
|
+
if (typeof value === 'boolean') {
|
|
144
|
+
return value ? 't' : 'f';
|
|
145
|
+
}
|
|
146
|
+
// 字符串及其它类型:转义后用双引号包裹
|
|
147
|
+
const escaped = ('' + value)
|
|
148
|
+
.replace(/\\/g, '\\\\')
|
|
149
|
+
.replace(/"/g, '\\"');
|
|
150
|
+
return '"' + escaped + '"';
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
/**
|
|
154
|
+
* 判断 sqlSimpleName 是否为 INSERT/UPDATE 类型
|
|
155
|
+
*/
|
|
156
|
+
private isInsertOrUpdateType(sqlSimpleName: string): boolean {
|
|
157
|
+
if (!sqlSimpleName) {
|
|
158
|
+
return false;
|
|
159
|
+
}
|
|
160
|
+
const types = [
|
|
161
|
+
KeysOfSimpleSQL.SIMPLE_INSERT,
|
|
162
|
+
KeysOfSimpleSQL.SIMPLE_UPDATE,
|
|
163
|
+
KeysOfSimpleSQL.SIMPLE_INSERT_ON_DUPLICATE_UPDATE,
|
|
164
|
+
KeysOfSimpleSQL.SIMPLE_INSERT_OR_UPDATE,
|
|
165
|
+
KeysOfSimpleSQL.SIMPLE_BATCH_INSERT,
|
|
166
|
+
];
|
|
167
|
+
return types.includes(sqlSimpleName as KeysOfSimpleSQL);
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
export { CrudProDataTypeConvertService };
|