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.
Files changed (132) hide show
  1. package/.qoder/skills/midway-fatcms-crud/SKILL.md +375 -0
  2. package/.qoder/skills/midway-fatcms-crud/examples.md +990 -0
  3. package/.qoder/skills/midway-fatcms-crud/reference.md +568 -0
  4. package/README.md +377 -134
  5. package/dist/controller/manage/CrudStandardDesignApi.d.ts +0 -2
  6. package/dist/controller/manage/CrudStandardDesignApi.js +11 -85
  7. package/dist/index.d.ts +2 -0
  8. package/dist/index.js +2 -0
  9. package/dist/libs/crud-pro/CrudPro.d.ts +9 -1
  10. package/dist/libs/crud-pro/CrudPro.js +15 -0
  11. package/dist/libs/crud-pro/README.md +809 -0
  12. package/dist/libs/crud-pro/README_FUNC.md +193 -0
  13. package/dist/libs/crud-pro/exceptions.d.ts +2 -0
  14. package/dist/libs/crud-pro/exceptions.js +2 -0
  15. package/dist/libs/crud-pro/interfaces.d.ts +34 -1
  16. package/dist/libs/crud-pro/models/ExecuteContext.d.ts +3 -3
  17. package/dist/libs/crud-pro/models/ExecuteContext.js +2 -0
  18. package/dist/libs/crud-pro/models/RequestModel.d.ts +6 -2
  19. package/dist/libs/crud-pro/models/RequestModel.js +20 -53
  20. package/dist/libs/crud-pro/models/ResModel.d.ts +6 -4
  21. package/dist/libs/crud-pro/models/ServiceHub.d.ts +1 -0
  22. package/dist/libs/crud-pro/models/keys.d.ts +6 -1
  23. package/dist/libs/crud-pro/models/keys.js +5 -0
  24. package/dist/libs/crud-pro/services/CrudProDataTypeConvertService.d.ts +52 -0
  25. package/dist/libs/crud-pro/services/CrudProDataTypeConvertService.js +158 -0
  26. package/dist/libs/crud-pro/services/CrudProExecuteSqlService.js +20 -1
  27. package/dist/libs/crud-pro/services/CrudProFieldValidateService.d.ts +7 -0
  28. package/dist/libs/crud-pro/services/CrudProFieldValidateService.js +32 -0
  29. package/dist/libs/crud-pro/services/CrudProGenSqlService.d.ts +13 -0
  30. package/dist/libs/crud-pro/services/CrudProGenSqlService.js +44 -7
  31. package/dist/libs/crud-pro/services/CrudProOriginToExecuteSql.d.ts +43 -0
  32. package/dist/libs/crud-pro/services/CrudProOriginToExecuteSql.js +132 -1
  33. package/dist/libs/crud-pro/services/CrudProTableMetaService.d.ts +15 -1
  34. package/dist/libs/crud-pro/services/CrudProTableMetaService.js +107 -0
  35. package/dist/libs/crud-pro/services/CurdProServiceHub.d.ts +5 -1
  36. package/dist/libs/crud-pro/services/CurdProServiceHub.js +11 -0
  37. package/dist/libs/crud-pro/utils/DateTimeUtils.d.ts +1 -0
  38. package/dist/libs/crud-pro/utils/DateTimeUtils.js +3 -0
  39. package/dist/libs/crud-pro/utils/MixinUtils.d.ts +32 -0
  40. package/dist/libs/crud-pro/utils/MixinUtils.js +85 -1
  41. package/dist/libs/crud-pro/utils/OrderByUtils.d.ts +70 -0
  42. package/dist/libs/crud-pro/utils/OrderByUtils.js +158 -0
  43. package/dist/libs/crud-pro/utils/ValidateUtils.js +1 -1
  44. package/dist/libs/crud-sharding/ROUTING_LOGIC.md +944 -0
  45. package/dist/libs/crud-sharding/ShardingConfig.d.ts +218 -0
  46. package/dist/libs/crud-sharding/ShardingConfig.js +32 -0
  47. package/dist/libs/crud-sharding/ShardingCountCache.d.ts +69 -0
  48. package/dist/libs/crud-sharding/ShardingCountCache.js +160 -0
  49. package/dist/libs/crud-sharding/ShardingCrudPro.d.ts +363 -0
  50. package/dist/libs/crud-sharding/ShardingCrudPro.js +675 -0
  51. package/dist/libs/crud-sharding/ShardingMerger.d.ts +130 -0
  52. package/dist/libs/crud-sharding/ShardingMerger.js +282 -0
  53. package/dist/libs/crud-sharding/ShardingRouter.d.ts +69 -0
  54. package/dist/libs/crud-sharding/ShardingRouter.js +377 -0
  55. package/dist/libs/crud-sharding/ShardingTableCreator.d.ts +146 -0
  56. package/dist/libs/crud-sharding/ShardingTableCreator.js +805 -0
  57. package/dist/libs/crud-sharding/ShardingUtils.d.ts +38 -0
  58. package/dist/libs/crud-sharding/ShardingUtils.js +77 -0
  59. package/dist/libs/crud-sharding/index.d.ts +45 -0
  60. package/dist/libs/crud-sharding/index.js +55 -0
  61. package/dist/models/StandardColumns.d.ts +71 -0
  62. package/dist/models/StandardColumns.js +28 -0
  63. package/dist/service/SysAppService.js +2 -2
  64. package/dist/service/SysConfigService.js +1 -1
  65. package/dist/service/SysDictDataService.js +2 -2
  66. package/dist/service/SysMenuService.js +1 -1
  67. package/dist/service/UserAccountService.d.ts +1 -1
  68. package/dist/service/crudstd/CrudStdService.d.ts +0 -1
  69. package/dist/service/crudstd/CrudStdService.js +0 -27
  70. package/dist/service/curd/CrudProQuick.d.ts +134 -4
  71. package/dist/service/curd/CrudProQuick.js +155 -3
  72. package/dist/service/curd/CurdMixService.d.ts +2 -1
  73. package/dist/service/curd/CurdMixService.js +5 -1
  74. package/dist/service/curd/CurdProService.d.ts +44 -2
  75. package/dist/service/curd/CurdProService.js +53 -1
  76. package/dist/service/curd/README.md +1100 -0
  77. package/dist/service/curd/fixSoftDelete.d.ts +14 -0
  78. package/dist/service/curd/fixSoftDelete.js +29 -11
  79. package/dist/service/flow/FlowConfigService.js +1 -1
  80. package/dist/service/flow/FlowInstanceCrudService.js +1 -1
  81. package/package.json +4 -1
  82. package/src/controller/gateway/AsyncTaskController.ts +1 -1
  83. package/src/controller/manage/CrudStandardDesignApi.ts +16 -100
  84. package/src/index.ts +3 -0
  85. package/src/libs/crud-pro/CrudPro.ts +19 -1
  86. package/src/libs/crud-pro/README.md +809 -0
  87. package/src/libs/crud-pro/README_FUNC.md +193 -0
  88. package/src/libs/crud-pro/exceptions.ts +2 -0
  89. package/src/libs/crud-pro/interfaces.ts +38 -1
  90. package/src/libs/crud-pro/models/ExecuteContext.ts +6 -3
  91. package/src/libs/crud-pro/models/RequestModel.ts +23 -65
  92. package/src/libs/crud-pro/models/ResModel.ts +10 -4
  93. package/src/libs/crud-pro/models/ServiceHub.ts +2 -0
  94. package/src/libs/crud-pro/models/keys.ts +5 -0
  95. package/src/libs/crud-pro/services/CrudProDataTypeConvertService.ts +171 -0
  96. package/src/libs/crud-pro/services/CrudProExecuteSqlService.ts +24 -1
  97. package/src/libs/crud-pro/services/CrudProFieldValidateService.ts +53 -1
  98. package/src/libs/crud-pro/services/CrudProGenSqlService.ts +51 -7
  99. package/src/libs/crud-pro/services/CrudProOriginToExecuteSql.ts +159 -2
  100. package/src/libs/crud-pro/services/CrudProTableMetaService.ts +139 -1
  101. package/src/libs/crud-pro/services/CurdProServiceHub.ts +16 -1
  102. package/src/libs/crud-pro/utils/DateTimeUtils.ts +3 -0
  103. package/src/libs/crud-pro/utils/MixinUtils.ts +97 -1
  104. package/src/libs/crud-pro/utils/OrderByUtils.ts +169 -0
  105. package/src/libs/crud-pro/utils/ValidateUtils.ts +1 -1
  106. package/src/libs/crud-sharding/ROUTING_LOGIC.md +944 -0
  107. package/src/libs/crud-sharding/ShardingConfig.ts +240 -0
  108. package/src/libs/crud-sharding/ShardingCountCache.ts +200 -0
  109. package/src/libs/crud-sharding/ShardingCrudPro.ts +835 -0
  110. package/src/libs/crud-sharding/ShardingMerger.ts +384 -0
  111. package/src/libs/crud-sharding/ShardingRouter.ts +512 -0
  112. package/src/libs/crud-sharding/ShardingTableCreator.ts +1007 -0
  113. package/src/libs/crud-sharding/ShardingUtils.ts +84 -0
  114. package/src/libs/crud-sharding/index.ts +64 -0
  115. package/src/models/StandardColumns.ts +76 -0
  116. package/src/service/FileCenterService.ts +1 -1
  117. package/src/service/SysAppService.ts +2 -2
  118. package/src/service/SysConfigService.ts +1 -1
  119. package/src/service/SysDictDataService.ts +2 -2
  120. package/src/service/SysMenuService.ts +2 -2
  121. package/src/service/WorkbenchService.ts +1 -1
  122. package/src/service/anyapi/AnyApiService.ts +1 -1
  123. package/src/service/asyncTask/AsyncTaskRunnerService.ts +1 -1
  124. package/src/service/crudstd/CrudStdService.ts +0 -32
  125. package/src/service/curd/CrudProQuick.ts +164 -5
  126. package/src/service/curd/CurdMixService.ts +7 -2
  127. package/src/service/curd/CurdProService.ts +62 -3
  128. package/src/service/curd/README.md +1100 -0
  129. package/src/service/curd/fixCfgModel.ts +1 -2
  130. package/src/service/curd/fixSoftDelete.ts +38 -16
  131. package/src/service/flow/FlowConfigService.ts +1 -1
  132. 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` 异常。
@@ -46,6 +46,7 @@ export declare enum Exceptions {
46
46
  CFG_UNIQUE_COLUMN_COUNT_MUST_ONE = "CFG_UNIQUE_COLUMN_COUNT_MUST_ONE",
47
47
  CFG_ORDER_BY_TYPE_ERROR = "CFG_ORDER_BY_TYPE_ERROR",
48
48
  CFG_ERROR_POSTGRES_COLUMN_NAME_INVALID = "CFG_ERROR_POSTGRES_COLUMN_NAME_INVALID",
49
+ CFG_INVALID_DATA_TYPE = "CFG_INVALID_DATA_TYPE",
49
50
  /**
50
51
  * 数据传错
51
52
  */
@@ -54,6 +55,7 @@ export declare enum Exceptions {
54
55
  DATA_GET_DATA_EMPTY_ON_UPDATE = "DATA_GET_DATA_EMPTY_ON_UPDATE",
55
56
  DATA_GET_DATA_EMPTY_ON_INSERT_KEYS = "DATA_GET_DATA_EMPTY_ON_INSERT_KEYS",
56
57
  DATA_GET_DATA_EMPTY_ON_INSERT_VALUES = "DATA_GET_DATA_EMPTY_ON_INSERT_VALUES",
58
+ BATCH_INSERT_KEYS_MISMATCH = "BATCH_INSERT_KEYS_MISMATCH",
57
59
  /**
58
60
  * 请求的Method字段为空
59
61
  */
@@ -50,6 +50,7 @@ var Exceptions;
50
50
  Exceptions["CFG_UNIQUE_COLUMN_COUNT_MUST_ONE"] = "CFG_UNIQUE_COLUMN_COUNT_MUST_ONE";
51
51
  Exceptions["CFG_ORDER_BY_TYPE_ERROR"] = "CFG_ORDER_BY_TYPE_ERROR";
52
52
  Exceptions["CFG_ERROR_POSTGRES_COLUMN_NAME_INVALID"] = "CFG_ERROR_POSTGRES_COLUMN_NAME_INVALID";
53
+ Exceptions["CFG_INVALID_DATA_TYPE"] = "CFG_INVALID_DATA_TYPE";
53
54
  /**
54
55
  * 数据传错
55
56
  */
@@ -58,6 +59,7 @@ var Exceptions;
58
59
  Exceptions["DATA_GET_DATA_EMPTY_ON_UPDATE"] = "DATA_GET_DATA_EMPTY_ON_UPDATE";
59
60
  Exceptions["DATA_GET_DATA_EMPTY_ON_INSERT_KEYS"] = "DATA_GET_DATA_EMPTY_ON_INSERT_KEYS";
60
61
  Exceptions["DATA_GET_DATA_EMPTY_ON_INSERT_VALUES"] = "DATA_GET_DATA_EMPTY_ON_INSERT_VALUES";
62
+ Exceptions["BATCH_INSERT_KEYS_MISMATCH"] = "BATCH_INSERT_KEYS_MISMATCH";
61
63
  /**
62
64
  * 请求的Method字段为空
63
65
  */
@@ -46,7 +46,7 @@ export interface IRequestModel {
46
46
  method?: string;
47
47
  columns?: string | string[];
48
48
  condition?: Record<string, any>;
49
- data?: Record<string, any>;
49
+ data?: Record<string, any> | Record<string, any>[];
50
50
  orderBy?: string | IOrderByItem[];
51
51
  limit?: number;
52
52
  offset?: number;
@@ -156,6 +156,7 @@ export interface IRequestCfgModel extends IBaseCfgModel {
156
156
  transactionIsolation?: number;
157
157
  sqlSimpleName?: KeysOfSimpleSQL;
158
158
  sqlCfgList?: ISqlCfgModel[];
159
+ uniqueColumn?: string[];
159
160
  updateCfg?: Record<string, IFuncCfgModel>;
160
161
  allowCfg?: Record<string, string[]>;
161
162
  rejectCfg?: Record<string, string[]>;
@@ -192,4 +193,36 @@ export interface ITableMetaQuery {
192
193
  sqlDbType: SqlDbType;
193
194
  sqlSchema?: string;
194
195
  }
196
+ /**
197
+ * 表名列表查询参数
198
+ */
199
+ export interface ITableNamesQuery {
200
+ sqlDatabase: string;
201
+ sqlDbType: SqlDbType;
202
+ sqlSchema?: string;
203
+ }
204
+ /**
205
+ * 表名查询缓存选项
206
+ */
207
+ export interface ITableNamesOptions {
208
+ /** 是否跳过缓存,直接查询数据库 */
209
+ skipCache?: boolean;
210
+ /** 查询后是否更新缓存 */
211
+ refreshCache?: boolean;
212
+ }
213
+ /**
214
+ * 表信息项(包含表名和类型)
215
+ */
216
+ export interface ITableInfo {
217
+ name: string;
218
+ tableType: string;
219
+ }
220
+ /**
221
+ * 表信息查询结果
222
+ */
223
+ export interface ITableListResult {
224
+ tables: ITableInfo[];
225
+ tableNames: string[];
226
+ viewNames: string[];
227
+ }
195
228
  export {};
@@ -4,7 +4,7 @@ import { SqlCfgModel } from './SqlCfgModel';
4
4
  import { Transaction } from './Transaction';
5
5
  import { ColumnRelation, ICrudProCfg, ILogger, IVisitor } from '../interfaces';
6
6
  import { IExecuteContextFunc } from './ExecuteContextFunc';
7
- import { ResModelFlexible, ResModelPageQuery } from './ResModel';
7
+ import { ResModelFlexible, ResModelPageQuery, ResModelRowEntity } from './ResModel';
8
8
  declare class ExecuteContext {
9
9
  private transaction;
10
10
  private logger;
@@ -26,8 +26,8 @@ declare class ExecuteContext {
26
26
  getReqModel(): RequestModel;
27
27
  setReqModel(reqModel: RequestModel): void;
28
28
  getResModel(): ResModelFlexible;
29
- getOneObj(): any;
30
- getResRows(): any[];
29
+ getOneObj(): ResModelRowEntity;
30
+ getResRows(): ResModelRowEntity[];
31
31
  setResModelItem(resName: string, data: any): void;
32
32
  getResModelItem(resName: string): any;
33
33
  getResModelForQueryPage(): ResModelPageQuery;
@@ -48,9 +48,11 @@ class ExecuteContext {
48
48
  getResModel() {
49
49
  return this.resModel;
50
50
  }
51
+ // 获取单条数据对象
51
52
  getOneObj() {
52
53
  return this.resModel['row'];
53
54
  }
55
+ // 获取多条数据对象
54
56
  getResRows() {
55
57
  return this.resModel['rows'];
56
58
  }
@@ -4,18 +4,22 @@ declare class RequestModel {
4
4
  method: string;
5
5
  columns?: string[];
6
6
  condition?: Record<string, any>;
7
- data?: Record<string, any>;
7
+ data?: Record<string, any> | Record<string, any>[];
8
8
  limit: number;
9
9
  offset: number;
10
10
  orderBys?: IOrderByItem[];
11
11
  constructor(req: IRequestModel, visitor: IVisitor);
12
- private parseOrderBys;
13
12
  private parseOffsetList;
14
13
  /**
15
14
  * 获取对象数据类型
16
15
  * @param attrName
17
16
  */
18
17
  getCondOrDataJsonObject(attrName: string): any;
18
+ /**
19
+ * 获取数组类型的数据(用于批量插入)
20
+ * @param attrName
21
+ */
22
+ getCondOrDataAsArray(attrName: string): Record<string, any>[];
19
23
  /**
20
24
  * 获取基本数据类型
21
25
  * @param attrName
@@ -3,6 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.RequestModel = void 0;
4
4
  const _ = require("lodash");
5
5
  const MixinUtils_1 = require("../utils/MixinUtils");
6
+ const OrderByUtils_1 = require("../utils/OrderByUtils");
6
7
  const exceptions_1 = require("../exceptions");
7
8
  const defaultConfigs_1 = require("../defaultConfigs");
8
9
  class RequestModel {
@@ -10,63 +11,11 @@ class RequestModel {
10
11
  Object.assign(this, req);
11
12
  this.visitor = visitor;
12
13
  this.columns = MixinUtils_1.MixinUtils.parseColumns(req.columns);
13
- this.orderBys = this.parseOrderBys(req.orderBy);
14
+ this.orderBys = OrderByUtils_1.OrderByUtils.parseOrderBys(req.orderBy);
14
15
  const limitOffset = this.parseOffsetList(req);
15
16
  this.limit = limitOffset.limit;
16
17
  this.offset = limitOffset.offset;
17
18
  }
18
- parseOrderBys(orderByStr) {
19
- if (MixinUtils_1.MixinUtils.isEmpty(orderByStr)) {
20
- return [];
21
- }
22
- if (Array.isArray(orderByStr)) {
23
- return orderByStr
24
- .map(obj => {
25
- if (typeof obj === 'string') {
26
- return { fieldName: obj, orderType: 'asc' };
27
- }
28
- const { fieldName, orderType = 'asc' } = obj;
29
- if (fieldName) {
30
- return { fieldName, orderType };
31
- }
32
- return null;
33
- })
34
- .filter(o => {
35
- return !!o;
36
- });
37
- }
38
- const orderByResult = [];
39
- const orderByArray = orderByStr
40
- .split(',')
41
- .map(s => {
42
- return s.trim();
43
- })
44
- .filter(s => !!s);
45
- if (MixinUtils_1.MixinUtils.isEmpty(orderByArray)) {
46
- return orderByResult;
47
- }
48
- for (let i = 0; i < orderByArray.length; i++) {
49
- const orderByItem = orderByArray[i];
50
- let orderType = 'asc'; //默认升序
51
- let fieldName = orderByItem;
52
- if (orderByItem.endsWith('+')) {
53
- // 升序
54
- fieldName = orderByItem.substring(0, orderByItem.length - 1);
55
- orderType = 'asc'; //升序
56
- }
57
- else if (orderByItem.endsWith('-')) {
58
- //降序
59
- fieldName = orderByItem.substring(0, orderByItem.length - 1);
60
- orderType = 'desc'; //降序
61
- }
62
- fieldName = fieldName.trim();
63
- if (MixinUtils_1.MixinUtils.isEmpty(fieldName) || !MixinUtils_1.MixinUtils.isValidFieldName(fieldName)) {
64
- throw new exceptions_1.CommonException(exceptions_1.Exceptions.REQUEST_MODEL_PARSE_ORDER_BY_FAILED, orderByItem);
65
- }
66
- orderByResult.push({ fieldName, orderType });
67
- }
68
- return orderByResult;
69
- }
70
19
  parseOffsetList(req) {
71
20
  const { limit, offset, pageSize, pageNo } = req;
72
21
  const limitOffset = {
@@ -102,6 +51,24 @@ class RequestModel {
102
51
  }
103
52
  throw new exceptions_1.CommonException(exceptions_1.Exceptions.RUN_GET_COND_OR_DATA_JSONOBJECT, attrName);
104
53
  }
54
+ /**
55
+ * 获取数组类型的数据(用于批量插入)
56
+ * @param attrName
57
+ */
58
+ getCondOrDataAsArray(attrName) {
59
+ if (attrName.startsWith('data')) {
60
+ const dataValue = _.get(this, attrName);
61
+ if (!dataValue) {
62
+ return null;
63
+ }
64
+ // 如果是数组直接返回
65
+ if (Array.isArray(dataValue)) {
66
+ return dataValue;
67
+ }
68
+ throw new exceptions_1.CommonException(exceptions_1.Exceptions.RUN_GET_COND_OR_DATA_JSONOBJECT, attrName);
69
+ }
70
+ throw new exceptions_1.CommonException(exceptions_1.Exceptions.RUN_GET_COND_OR_DATA_JSONOBJECT, attrName);
71
+ }
105
72
  /**
106
73
  * 获取基本数据类型
107
74
  * @param attrName
@@ -2,19 +2,21 @@ interface ResModelAffected {
2
2
  insertId?: string | number;
3
3
  affectedRows: number;
4
4
  }
5
+ declare type ResModelRowEntity = Record<string, any>;
5
6
  interface ResModelStandard {
6
- row?: any;
7
- rows?: any[];
7
+ row?: ResModelRowEntity;
8
+ rows?: ResModelRowEntity[];
8
9
  total_count?: number;
10
+ is_exist?: boolean;
9
11
  affected?: ResModelAffected;
10
12
  insert_affected?: ResModelAffected;
11
13
  update_affected?: ResModelAffected;
12
14
  }
13
15
  interface ResModelPageQuery {
14
- rows: any[];
16
+ rows: ResModelRowEntity[];
15
17
  total_count: number;
16
18
  }
17
19
  declare type ResModelFlexible = ResModelStandard & {
18
20
  [key: string]: any;
19
21
  };
20
- export { ResModelAffected, ResModelStandard, ResModelFlexible, ResModelPageQuery };
22
+ export { ResModelAffected, ResModelStandard, ResModelFlexible, ResModelPageQuery, ResModelRowEntity };
@@ -17,4 +17,5 @@ export interface ICurdProServiceHub {
17
17
  convertOriginToExecuteSql(sqlCfgModel: SqlCfgModel): Promise<void>;
18
18
  executeFuncCfg(tmpFunCfg: IFuncCfgModel, exeFunCtx: FuncContext): string;
19
19
  getTableMeta(query: ITableMetaQuery): Promise<ITableMeta>;
20
+ convertDataTypeByTableMeta(reqModel: RequestModel, cfgModel: RequestCfgModel): Promise<void>;
20
21
  }
@@ -51,8 +51,10 @@ export declare enum KeysOfSimpleSQL {
51
51
  SIMPLE_QUERY_ONE = "SIMPLE_QUERY_ONE",
52
52
  SIMPLE_QUERY_PAGE = "SIMPLE_QUERY_PAGE",
53
53
  SIMPLE_QUERY_COUNT = "SIMPLE_QUERY_COUNT",
54
+ SIMPLE_QUERY_EXIST = "SIMPLE_QUERY_EXIST",
54
55
  SIMPLE_UPDATE = "SIMPLE_UPDATE",
55
56
  SIMPLE_INSERT = "SIMPLE_INSERT",
57
+ SIMPLE_BATCH_INSERT = "SIMPLE_BATCH_INSERT",
56
58
  SIMPLE_DELETE = "SIMPLE_DELETE",
57
59
  SIMPLE_INSERT_ON_DUPLICATE_UPDATE = "SIMPLE_INSERT_ON_DUPLICATE_UPDATE",
58
60
  SIMPLE_INSERT_OR_UPDATE = "SIMPLE_INSERT_OR_UPDATE",
@@ -62,7 +64,8 @@ export declare enum KeysOfSqlResPicker {
62
64
  UPDATE_RESULT = "UPDATE_RESULT",
63
65
  RESULT_FIRST_ROW = "RESULT_FIRST_ROW",
64
66
  RESULT_TOTAL_COUNT = "RESULT_TOTAL_COUNT",
65
- RESULT_ONE_VALUE = "RESULT_ONE_VALUE"
67
+ RESULT_ONE_VALUE = "RESULT_ONE_VALUE",
68
+ RESULT_IS_EXIST = "RESULT_IS_EXIST"
66
69
  }
67
70
  export declare const KeysOfCustomSQL: {
68
71
  SQL_COLUMNS: string;
@@ -73,6 +76,8 @@ export declare const KeysOfCustomSQL: {
73
76
  SQL_AS_UPDATE: string;
74
77
  SQL_AS_INSERT_KEYS: string;
75
78
  SQL_AS_INSERT_VALUES: string;
79
+ SQL_AS_BATCH_INSERT_KEYS: string;
80
+ SQL_AS_BATCH_INSERT_VALUES: string;
76
81
  SQL_GET_DATA_ATTR_START: string;
77
82
  SQL_GET_CONDITION_ATTR_START: string;
78
83
  SQL_PICK_RES_AS_NUMBER: string;
@@ -61,8 +61,10 @@ var KeysOfSimpleSQL;
61
61
  KeysOfSimpleSQL["SIMPLE_QUERY_ONE"] = "SIMPLE_QUERY_ONE";
62
62
  KeysOfSimpleSQL["SIMPLE_QUERY_PAGE"] = "SIMPLE_QUERY_PAGE";
63
63
  KeysOfSimpleSQL["SIMPLE_QUERY_COUNT"] = "SIMPLE_QUERY_COUNT";
64
+ KeysOfSimpleSQL["SIMPLE_QUERY_EXIST"] = "SIMPLE_QUERY_EXIST";
64
65
  KeysOfSimpleSQL["SIMPLE_UPDATE"] = "SIMPLE_UPDATE";
65
66
  KeysOfSimpleSQL["SIMPLE_INSERT"] = "SIMPLE_INSERT";
67
+ KeysOfSimpleSQL["SIMPLE_BATCH_INSERT"] = "SIMPLE_BATCH_INSERT";
66
68
  KeysOfSimpleSQL["SIMPLE_DELETE"] = "SIMPLE_DELETE";
67
69
  KeysOfSimpleSQL["SIMPLE_INSERT_ON_DUPLICATE_UPDATE"] = "SIMPLE_INSERT_ON_DUPLICATE_UPDATE";
68
70
  KeysOfSimpleSQL["SIMPLE_INSERT_OR_UPDATE"] = "SIMPLE_INSERT_OR_UPDATE";
@@ -74,6 +76,7 @@ var KeysOfSqlResPicker;
74
76
  KeysOfSqlResPicker["RESULT_FIRST_ROW"] = "RESULT_FIRST_ROW";
75
77
  KeysOfSqlResPicker["RESULT_TOTAL_COUNT"] = "RESULT_TOTAL_COUNT";
76
78
  KeysOfSqlResPicker["RESULT_ONE_VALUE"] = "RESULT_ONE_VALUE";
79
+ KeysOfSqlResPicker["RESULT_IS_EXIST"] = "RESULT_IS_EXIST";
77
80
  })(KeysOfSqlResPicker = exports.KeysOfSqlResPicker || (exports.KeysOfSqlResPicker = {}));
78
81
  exports.KeysOfCustomSQL = {
79
82
  SQL_COLUMNS: '@@columns',
@@ -84,6 +87,8 @@ exports.KeysOfCustomSQL = {
84
87
  SQL_AS_UPDATE: '@@asUpdate:',
85
88
  SQL_AS_INSERT_KEYS: '@@asInsertKeys:',
86
89
  SQL_AS_INSERT_VALUES: '@@asInsertValues:',
90
+ SQL_AS_BATCH_INSERT_KEYS: '@@asBatchInsertKeys:',
91
+ SQL_AS_BATCH_INSERT_VALUES: '@@asBatchInsertValues:',
87
92
  // 从Req中取值:单独取Data中的某个字段:获取单个值。
88
93
  SQL_GET_DATA_ATTR_START: '@@data',
89
94
  SQL_GET_CONDITION_ATTR_START: '@@condition',
@@ -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 };