midway-fatcms 0.0.7 → 0.0.9

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (159) hide show
  1. package/.qoder/skills/midway-fatcms/01-quick-start.md +231 -0
  2. package/.qoder/skills/midway-fatcms/02-crud-quick.md +375 -0
  3. package/.qoder/skills/midway-fatcms/03-crud-sharding.md +489 -0
  4. package/.qoder/skills/midway-fatcms/04-condition-operators.md +93 -0
  5. package/.qoder/skills/midway-fatcms/05-configuration.md +290 -0
  6. package/.qoder/skills/midway-fatcms/06-builtin-functions.md +241 -0
  7. package/.qoder/skills/midway-fatcms/07-examples.md +504 -0
  8. package/.qoder/skills/midway-fatcms/SKILL.md +96 -0
  9. package/README.md +9 -9
  10. package/dist/configuration.d.ts +10 -0
  11. package/dist/configuration.js +26 -0
  12. package/dist/controller/base/BaseApiController.d.ts +1 -2
  13. package/dist/controller/base/BaseApiController.js +0 -4
  14. package/dist/controller/gateway/DocGatewayController.js +1 -1
  15. package/dist/controller/helpers.controller.d.ts +6 -0
  16. package/dist/controller/helpers.controller.js +19 -0
  17. package/dist/controller/manage/FlowConfigManageApi.js +4 -2
  18. package/dist/controller/manage/SysConfigMangeApi.js +6 -1
  19. package/dist/controller/manage/UserAccountManageApi.js +7 -2
  20. package/dist/index.d.ts +2 -2
  21. package/dist/index.js +2 -2
  22. package/dist/libs/crud-pro/CrudPro.d.ts +51 -3
  23. package/dist/libs/crud-pro/CrudPro.js +111 -4
  24. package/dist/libs/crud-pro/exceptions.d.ts +7 -0
  25. package/dist/libs/crud-pro/exceptions.js +7 -0
  26. package/dist/libs/crud-pro/interfaces.d.ts +83 -12
  27. package/dist/libs/crud-pro/models/CrudResult.d.ts +116 -0
  28. package/dist/libs/crud-pro/models/CrudResult.js +126 -0
  29. package/dist/libs/crud-pro/models/RequestModel.d.ts +2 -2
  30. package/dist/libs/crud-pro/models/ServiceHub.d.ts +2 -0
  31. package/dist/libs/crud-pro/services/CrudProDataTypeConvertService.d.ts +70 -2
  32. package/dist/libs/crud-pro/services/CrudProDataTypeConvertService.js +205 -13
  33. package/dist/libs/crud-pro/services/CrudProExecuteSqlService.js +36 -2
  34. package/dist/libs/crud-pro/services/CrudProGenSqlCondition.js +8 -4
  35. package/dist/libs/crud-pro/services/CrudProTableMetaService.d.ts +36 -0
  36. package/dist/libs/crud-pro/services/CrudProTableMetaService.js +97 -4
  37. package/dist/libs/crud-pro/services/CurdProServiceHub.d.ts +2 -0
  38. package/dist/libs/crud-pro/services/CurdProServiceHub.js +6 -0
  39. package/dist/libs/crud-pro-quick/CrudProQuick.d.ts +382 -0
  40. package/dist/libs/crud-pro-quick/CrudProQuick.js +689 -0
  41. package/dist/libs/crud-pro-quick/fixSoftDelete.d.ts +30 -0
  42. package/dist/{service/curd → libs/crud-pro-quick}/fixSoftDelete.js +3 -6
  43. package/dist/libs/crud-pro-quick/index.d.ts +36 -0
  44. package/dist/libs/crud-pro-quick/index.js +49 -0
  45. package/dist/libs/crud-pro-quick/models.d.ts +33 -0
  46. package/dist/libs/crud-pro-quick/models.js +2 -0
  47. package/dist/libs/crud-sharding/ShardingBase.d.ts +78 -0
  48. package/dist/libs/crud-sharding/ShardingBase.js +179 -0
  49. package/dist/libs/crud-sharding/ShardingByCustomCrud.d.ts +35 -0
  50. package/dist/libs/crud-sharding/ShardingByCustomCrud.js +297 -0
  51. package/dist/libs/crud-sharding/ShardingByHashCrud.d.ts +38 -0
  52. package/dist/libs/crud-sharding/ShardingByHashCrud.js +86 -0
  53. package/dist/libs/crud-sharding/ShardingByKeyCrud.d.ts +39 -0
  54. package/dist/libs/crud-sharding/ShardingByKeyCrud.js +74 -0
  55. package/dist/libs/crud-sharding/ShardingByTimeCrud.d.ts +66 -0
  56. package/dist/libs/crud-sharding/ShardingByTimeCrud.js +524 -0
  57. package/dist/libs/crud-sharding/ShardingConfig.d.ts +25 -10
  58. package/dist/libs/crud-sharding/ShardingConfig.js +5 -5
  59. package/dist/libs/crud-sharding/ShardingMerger.d.ts +10 -18
  60. package/dist/libs/crud-sharding/ShardingMerger.js +27 -44
  61. package/dist/libs/crud-sharding/ShardingResult.d.ts +33 -0
  62. package/dist/libs/crud-sharding/ShardingResult.js +16 -0
  63. package/dist/libs/crud-sharding/ShardingTableCreator.d.ts +21 -4
  64. package/dist/libs/crud-sharding/ShardingTableCreator.js +193 -59
  65. package/dist/libs/crud-sharding/ShardingUtils.d.ts +48 -0
  66. package/dist/libs/crud-sharding/ShardingUtils.js +122 -1
  67. package/dist/libs/crud-sharding/TIME_COLUMN_CLEAN_SPEC.md +488 -0
  68. package/dist/libs/crud-sharding/index.d.ts +13 -15
  69. package/dist/libs/crud-sharding/index.js +33 -17
  70. package/dist/models/RedisKeys.d.ts +1 -0
  71. package/dist/models/RedisKeys.js +1 -0
  72. package/dist/models/bizmodels.d.ts +2 -6
  73. package/dist/service/SysAppService.d.ts +2 -2
  74. package/dist/service/SysAppService.js +16 -5
  75. package/dist/service/SysConfigService.d.ts +1 -1
  76. package/dist/service/SysConfigService.js +7 -2
  77. package/dist/service/SysDictDataService.js +14 -4
  78. package/dist/service/SysMenuService.js +7 -2
  79. package/dist/service/TableMetaCacheRedisSubscriber.d.ts +31 -0
  80. package/dist/service/TableMetaCacheRedisSubscriber.js +98 -0
  81. package/dist/service/curd/CurdMixService.d.ts +6 -4
  82. package/dist/service/curd/CurdMixService.js +16 -2
  83. package/dist/service/curd/CurdProService.d.ts +149 -29
  84. package/dist/service/curd/CurdProService.js +157 -38
  85. package/dist/service/flow/FlowConfigService.js +7 -2
  86. package/dist/service/flow/FlowInstanceCrudService.js +22 -19
  87. package/package.json +1 -1
  88. package/src/configuration.ts +27 -0
  89. package/src/controller/base/BaseApiController.ts +0 -5
  90. package/src/controller/gateway/DocGatewayController.ts +1 -1
  91. package/src/controller/helpers.controller.ts +15 -0
  92. package/src/controller/manage/CrudStandardDesignApi.ts +4 -3
  93. package/src/controller/manage/FlowConfigManageApi.ts +4 -2
  94. package/src/controller/manage/SysConfigMangeApi.ts +6 -1
  95. package/src/controller/manage/UserAccountManageApi.ts +7 -2
  96. package/src/index.ts +2 -2
  97. package/src/libs/crud-pro/CrudPro.ts +134 -7
  98. package/src/libs/crud-pro/exceptions.ts +8 -0
  99. package/src/libs/crud-pro/interfaces.ts +111 -15
  100. package/src/libs/crud-pro/models/CrudResult.ts +178 -0
  101. package/src/libs/crud-pro/models/RequestModel.ts +2 -2
  102. package/src/libs/crud-pro/models/ServiceHub.ts +4 -0
  103. package/src/libs/crud-pro/services/CrudProDataTypeConvertService.ts +238 -15
  104. package/src/libs/crud-pro/services/CrudProExecuteSqlService.ts +41 -2
  105. package/src/libs/crud-pro/services/CrudProGenSqlCondition.ts +11 -7
  106. package/src/libs/crud-pro/services/CrudProTableMetaService.ts +110 -3
  107. package/src/libs/crud-pro/services/CurdProServiceHub.ts +8 -0
  108. package/src/libs/crud-pro-quick/CrudProQuick.ts +782 -0
  109. package/src/{service/curd → libs/crud-pro-quick}/fixSoftDelete.ts +23 -13
  110. package/src/libs/crud-pro-quick/index.ts +52 -0
  111. package/src/libs/crud-pro-quick/models.ts +35 -0
  112. package/src/libs/crud-sharding/ShardingBase.ts +256 -0
  113. package/src/libs/crud-sharding/ShardingByCustomCrud.ts +329 -0
  114. package/src/libs/crud-sharding/ShardingByHashCrud.ts +111 -0
  115. package/src/libs/crud-sharding/ShardingByKeyCrud.ts +97 -0
  116. package/src/libs/crud-sharding/ShardingByTimeCrud.ts +628 -0
  117. package/src/libs/crud-sharding/ShardingConfig.ts +28 -10
  118. package/src/libs/crud-sharding/ShardingMerger.ts +35 -63
  119. package/src/libs/crud-sharding/ShardingResult.ts +29 -0
  120. package/src/libs/crud-sharding/ShardingTableCreator.ts +214 -71
  121. package/src/libs/crud-sharding/ShardingUtils.ts +137 -0
  122. package/src/libs/crud-sharding/TIME_COLUMN_CLEAN_SPEC.md +488 -0
  123. package/src/libs/crud-sharding/index.ts +30 -16
  124. package/src/models/RedisKeys.ts +1 -0
  125. package/src/models/bizmodels.ts +4 -7
  126. package/src/service/SysAppService.ts +18 -7
  127. package/src/service/SysConfigService.ts +8 -3
  128. package/src/service/SysDictDataService.ts +14 -4
  129. package/src/service/SysMenuService.ts +7 -2
  130. package/src/service/TableMetaCacheRedisSubscriber.ts +105 -0
  131. package/src/service/crudstd/CrudStdService.ts +2 -2
  132. package/src/service/curd/CurdMixService.ts +26 -5
  133. package/src/service/curd/CurdProService.ts +186 -45
  134. package/src/service/flow/FlowConfigService.ts +7 -2
  135. package/src/service/flow/FlowInstanceCrudService.ts +23 -20
  136. package/.qoder/skills/midway-fatcms-crud/SKILL.md +0 -375
  137. package/.qoder/skills/midway-fatcms-crud/examples.md +0 -990
  138. package/.qoder/skills/midway-fatcms-crud/reference.md +0 -568
  139. package/dist/libs/crud-pro/README.md +0 -809
  140. package/dist/libs/crud-pro/README_FUNC.md +0 -193
  141. package/dist/libs/crud-sharding/ROUTING_LOGIC.md +0 -944
  142. package/dist/libs/crud-sharding/ShardingCrudPro.d.ts +0 -363
  143. package/dist/libs/crud-sharding/ShardingCrudPro.js +0 -675
  144. package/dist/libs/crud-sharding/ShardingRouter.d.ts +0 -69
  145. package/dist/libs/crud-sharding/ShardingRouter.js +0 -377
  146. package/dist/models/StandardColumns.d.ts +0 -71
  147. package/dist/models/StandardColumns.js +0 -28
  148. package/dist/service/curd/CrudProQuick.d.ts +0 -190
  149. package/dist/service/curd/CrudProQuick.js +0 -319
  150. package/dist/service/curd/README.md +0 -1100
  151. package/dist/service/curd/fixSoftDelete.d.ts +0 -20
  152. package/src/libs/crud-pro/README.md +0 -809
  153. package/src/libs/crud-pro/README_FUNC.md +0 -193
  154. package/src/libs/crud-sharding/ROUTING_LOGIC.md +0 -944
  155. package/src/libs/crud-sharding/ShardingCrudPro.ts +0 -835
  156. package/src/libs/crud-sharding/ShardingRouter.ts +0 -512
  157. package/src/models/StandardColumns.ts +0 -76
  158. package/src/service/curd/CrudProQuick.ts +0 -360
  159. package/src/service/curd/README.md +0 -1100
@@ -1,675 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.ShardingCrudPro = void 0;
4
- const OrderByUtils_1 = require("../../libs/crud-pro/utils/OrderByUtils");
5
- const keys_1 = require("../../libs/crud-pro/models/keys");
6
- const ShardingConfig_1 = require("./ShardingConfig");
7
- const ShardingRouter_1 = require("./ShardingRouter");
8
- const ShardingMerger_1 = require("./ShardingMerger");
9
- const ShardingTableCreator_1 = require("./ShardingTableCreator");
10
- const ShardingCountCache_1 = require("./ShardingCountCache");
11
- /**
12
- * 分表 CRUD 操作封装器
13
- *
14
- * 在 CrudPro 之上封装分表功能,不修改 CrudPro 内部代码。
15
- * 提供透明的分表路由和多表查询结果合并能力。
16
- *
17
- * 支持的分表策略:
18
- * - YEAR: 按年分表,如 order_2024, order_2025
19
- * - MONTH: 按月分表,如 order_202401, order_202402
20
- * - DAY: 按日分表,如 order_20240101
21
- * - RANGE: 按范围分表,如 user_0 ~ user_99
22
- * - HASH: 按哈希分表,如 order_01 ~ order_16
23
- * - CUSTOM: 自定义分表规则
24
- *
25
- * @example
26
- * // 按月分表
27
- * const sharding = new ShardingCrudPro(crudPro, {
28
- * type: ShardingType.MONTH,
29
- * baseTable: 't_order',
30
- * timeColumn: 'created_at',
31
- * });
32
- *
33
- * // 插入数据(自动路由到正确分表)
34
- * await sharding.insert({ data: { order_id: '001', amount: 100, created_at: '2024-03-15' } });
35
- *
36
- * // 分页查询(自动合并多表结果)
37
- * const result = await sharding.queryPage({
38
- * condition: { created_at: { $gte: '2024-01-01', $lte: '2024-03-31' } },
39
- * pageNo: 1,
40
- * pageSize: 10,
41
- * });
42
- */
43
- class ShardingCrudPro {
44
- constructor(crudPro, config) {
45
- this.baseCfg = {};
46
- this.crudPro = crudPro;
47
- this.config = config;
48
- // 时间分表必须显式指定 timeColumn
49
- if ([ShardingConfig_1.ShardingType.YEAR, ShardingConfig_1.ShardingType.MONTH, ShardingConfig_1.ShardingType.DAY].includes(config.type)) {
50
- if (!config.timeColumn) {
51
- throw new Error('[ShardingCrudPro] 时间分表(YEAR/MONTH/DAY)必须显式指定 timeColumn 字段');
52
- }
53
- }
54
- this.router = new ShardingRouter_1.ShardingRouter();
55
- this.merger = new ShardingMerger_1.ShardingMerger();
56
- this.tableCreator = new ShardingTableCreator_1.ShardingTableCreator(crudPro, config);
57
- // 初始化 COUNT 缓存(如果配置了)
58
- if (config.countCache) {
59
- const countCache = new ShardingCountCache_1.ShardingCountCache(config.type, config.baseTable, config.countCache.ttlSeconds, config.countCache.maxSize);
60
- this.merger.setCountCache(countCache);
61
- }
62
- }
63
- // ============ 配置方法 ============
64
- /**
65
- * 设置基础配置(会应用到所有操作)
66
- *
67
- * @param cfg 配置项,如 sqlDatabase、sqlDbType 等
68
- * @returns this,支持链式调用
69
- *
70
- * @example
71
- * sharding.setBaseCfg({
72
- * sqlDatabase: 'mydb',
73
- * sqlDbType: SqlDbType.mysql,
74
- * });
75
- */
76
- setBaseCfg(cfg) {
77
- this.baseCfg = { ...this.baseCfg, ...cfg };
78
- return this;
79
- }
80
- /**
81
- * 获取分表配置
82
- */
83
- getConfig() {
84
- return this.config;
85
- }
86
- // ============ 写操作(单表路由) ============
87
- /**
88
- * 插入数据
89
- *
90
- * 根据分表规则自动路由到目标分表。
91
- * 对于时间分表,需要确保 data 中包含时间字段。
92
- * 对于哈希/范围分表,需要确保 data 中包含分表字段。
93
- *
94
- * 如果目标分表不存在且配置了 autoCreateTable,会自动创建分表。
95
- *
96
- * @param reqJson 请求参数,data 为要插入的数据
97
- * @returns 执行上下文
98
- *
99
- * @example
100
- * await sharding.insert({
101
- * data: {
102
- * order_id: 'ORD001',
103
- * amount: 100,
104
- * created_at: '2024-03-15 10:00:00',
105
- * },
106
- * });
107
- */
108
- async insert(reqJson) {
109
- const targetTable = this.resolveSingleTable(reqJson, 'insert');
110
- // 确保分表存在(根据配置决定是否自动创建)
111
- await this.createShardingTableIfNeeded(targetTable);
112
- return this.executeOnTable(targetTable, reqJson, keys_1.KeysOfSimpleSQL.SIMPLE_INSERT);
113
- }
114
- /**
115
- * 批量插入数据(支持跨分表)
116
- *
117
- * 自动将数据按分表分组,并行插入到对应的分表。
118
- *
119
- * @param reqJson 请求参数,data 为要插入的数据数组
120
- * @returns 批量插入结果
121
- *
122
- * 执行流程:
123
- * 1. 遍历所有数据,根据分表字段计算目标分表
124
- * 2. 将数据按分表分组
125
- * 3. 并行执行各分表的批量插入
126
- * 4. 汇总结果返回
127
- *
128
- * @example
129
- * // 数据分布在不同月份的分表
130
- * const result = await sharding.batchInsert({
131
- * data: [
132
- * { order_id: '001', amount: 100, created_at: '2024-01-15' }, // -> t_order_202401
133
- * { order_id: '002', amount: 200, created_at: '2024-01-20' }, // -> t_order_202401
134
- * { order_id: '003', amount: 150, created_at: '2024-02-10' }, // -> t_order_202402
135
- * { order_id: '004', amount: 300, created_at: '2024-03-05' }, // -> t_order_202403
136
- * ],
137
- * });
138
- *
139
- * console.log(result.totalAffected); // 4
140
- * console.log(result.tableCount); // 3
141
- * console.log(result.tableResults);
142
- * // [
143
- * // { table: 't_order_202401', affected: 2, rowCount: 2 },
144
- * // { table: 't_order_202402', affected: 1, rowCount: 1 },
145
- * // { table: 't_order_202403', affected: 1, rowCount: 1 },
146
- * // ]
147
- */
148
- async batchInsert(reqJson) {
149
- const dataArray = reqJson.data;
150
- if (!Array.isArray(dataArray) || dataArray.length === 0) {
151
- return {
152
- totalAffected: 0,
153
- tableResults: [],
154
- tableCount: 0,
155
- success: true,
156
- errors: [],
157
- };
158
- }
159
- // 按分表分组
160
- const groupedData = this.batchInsertGroupDataByTable(dataArray);
161
- // 确保所有目标分表存在(根据配置决定是否自动创建)
162
- const tableNames = Array.from(groupedData.keys());
163
- for (let i = 0; i < tableNames.length; i++) {
164
- await this.createShardingTableIfNeeded(tableNames[i]);
165
- }
166
- // 并行执行各分表的批量插入
167
- const insertPromises = Array.from(groupedData.entries()).map(async ([table, items]) => {
168
- try {
169
- const ctx = await this.executeOnTable(table, { data: items }, keys_1.KeysOfSimpleSQL.SIMPLE_BATCH_INSERT);
170
- return {
171
- table,
172
- affected: ctx.getResModelItem('affected') || items.length,
173
- rowCount: items.length,
174
- error: null,
175
- };
176
- }
177
- catch (e) {
178
- return {
179
- table,
180
- affected: 0,
181
- rowCount: items.length,
182
- error: e,
183
- };
184
- }
185
- });
186
- const results = await Promise.all(insertPromises);
187
- // 汇总结果
188
- const tableResults = results.map(r => ({
189
- table: r.table,
190
- affected: r.affected,
191
- rowCount: r.rowCount,
192
- }));
193
- const errors = results
194
- .filter(r => r.error !== null)
195
- .map(r => ({ table: r.table, error: r.error }));
196
- const totalAffected = results.reduce((sum, r) => sum + r.affected, 0);
197
- return {
198
- totalAffected,
199
- tableResults,
200
- tableCount: groupedData.size,
201
- success: errors.length === 0,
202
- errors,
203
- };
204
- }
205
- /**
206
- * 更新数据
207
- *
208
- * 根据条件中的分表字段自动路由到目标分表。
209
- *
210
- * @param reqJson 请求参数,condition 为更新条件,data 为更新数据
211
- * @returns 执行上下文
212
- *
213
- * @example
214
- * await sharding.update({
215
- * condition: { order_id: 'ORD001', created_at: '2024-03-15' },
216
- * data: { amount: 200 },
217
- * });
218
- */
219
- async update(reqJson) {
220
- const targetTable = this.resolveSingleTable(reqJson, 'update');
221
- return this.executeOnTable(targetTable, reqJson, keys_1.KeysOfSimpleSQL.SIMPLE_UPDATE);
222
- }
223
- /**
224
- * 删除数据
225
- *
226
- * 根据条件中的分表字段自动路由到目标分表。
227
- *
228
- * @param reqJson 请求参数,condition 为删除条件
229
- * @returns 执行上下文
230
- */
231
- async delete(reqJson) {
232
- const targetTable = this.resolveSingleTable(reqJson, 'delete');
233
- return this.executeOnTable(targetTable, reqJson, keys_1.KeysOfSimpleSQL.SIMPLE_DELETE);
234
- }
235
- /**
236
- * 插入或更新(存在则更新,不存在则插入)
237
- *
238
- * 路由逻辑:根据 condition 路由(先查询是否存在)
239
- *
240
- * @param reqJson 请求参数
241
- * @returns 执行上下文
242
- */
243
- async insertOrUpdate(reqJson) {
244
- const targetTable = this.resolveSingleTable(reqJson, 'update'); // 使用 condition 路由
245
- return this.executeOnTable(targetTable, reqJson, keys_1.KeysOfSimpleSQL.SIMPLE_INSERT_OR_UPDATE);
246
- }
247
- /**
248
- * 插入或更新(ON DUPLICATE KEY UPDATE)
249
- *
250
- * 路由逻辑:根据 condition 路由(先查询是否存在)
251
- *
252
- * @param reqJson 请求参数
253
- * @returns 执行上下文
254
- */
255
- async insertOnDuplicateUpdate(reqJson) {
256
- const targetTable = this.resolveSingleTable(reqJson, 'update'); // 使用 condition 路由
257
- return this.executeOnTable(targetTable, reqJson, keys_1.KeysOfSimpleSQL.SIMPLE_INSERT_ON_DUPLICATE_UPDATE);
258
- }
259
- // ============ 查询操作(可能多表) ============
260
- /**
261
- * 查询单条记录
262
- *
263
- * 如果能根据条件确定单一分表,则查询单表;
264
- * 否则按顺序查询各分表,返回第一条匹配记录。
265
- *
266
- * @param reqJson 请求参数,condition 为查询条件
267
- * @returns 单条记录,未找到返回 null
268
- *
269
- * @example
270
- * const order = await sharding.queryOne({
271
- * condition: { order_id: 'ORD001' },
272
- * });
273
- */
274
- async queryOne(reqJson) {
275
- const targetTables = await this.resolveQueryTables(reqJson);
276
- if (targetTables.length === 0) {
277
- return null; // 无匹配表
278
- }
279
- // 多表:顺序查询,找到即返回
280
- for (const table of targetTables) {
281
- const result = await this.queryOneFromTable(table, reqJson);
282
- if (result)
283
- return result;
284
- }
285
- return null;
286
- }
287
- /**
288
- * 查询列表
289
- *
290
- * 可能涉及多个分表,结果会自动合并。
291
- *
292
- * 使用约束:
293
- * - 必须传 orderBy 参数
294
- * - orderBy 必须为 timeColumn DESC 或 timeColumn ASC(如 'created_at DESC' / 'created_at ASC')
295
- *
296
- * @param reqJson 请求参数
297
- * @returns 数据列表
298
- *
299
- * @example
300
- * const orders = await sharding.query({
301
- * condition: { created_at: { $gte: '2024-01-01', $lte: '2024-03-31' } },
302
- * orderBy: 'created_at DESC', // 或 'created_at ASC'
303
- * });
304
- */
305
- async query(reqJson) {
306
- const targetTables = await this.resolveQueryTables(reqJson);
307
- if (targetTables.length === 0) {
308
- return []; // 无匹配表,返回空结果
309
- }
310
- if (targetTables.length === 1) {
311
- const ctx = await this.executeOnTable(targetTables[0], reqJson, keys_1.KeysOfSimpleSQL.SIMPLE_QUERY);
312
- return ctx.getResRows();
313
- }
314
- // 多表查询时,需要参数校验
315
- this.validateQueryOrderBy(reqJson);
316
- // 根据排序方向确定表顺序:DESC 新→旧,ASC 旧→新
317
- const tablesForMerge = this.sortTablesForOrderBy(targetTables, reqJson);
318
- // 多表查询合并
319
- return this.merger.mergeQuery(this.crudPro, tablesForMerge, reqJson, this.buildCfg(keys_1.KeysOfSimpleSQL.SIMPLE_QUERY));
320
- }
321
- /**
322
- * 分页查询
323
- *
324
- * 自动处理跨分表的分页:
325
- * 1. 顺序累计查询各分表
326
- * 2. 截取目标数据(无需排序)
327
- *
328
- * 使用约束:
329
- * - 必须传 orderBy 参数
330
- * - orderBy 必须为 timeColumn DESC 或 timeColumn ASC(如 'created_at DESC' / 'created_at ASC')
331
- *
332
- * @param reqJson 请求参数,包含 pageNo、pageSize 和 orderBy
333
- * @returns 分页结果,包含 rows 和 total_count
334
- *
335
- * @example
336
- * const result = await sharding.queryPage({
337
- * condition: { created_at: { $gte: '2024-01-01', $lte: '2024-03-31' } },
338
- * pageNo: 1,
339
- * pageSize: 10,
340
- * orderBy: 'created_at DESC', // 或 'created_at ASC'
341
- * });
342
- * console.log(result.rows, result.total_count);
343
- */
344
- async queryPage(reqJson) {
345
- const targetTables = await this.resolveQueryTables(reqJson);
346
- if (targetTables.length === 0) {
347
- return { rows: [], total_count: 0 }; // 无匹配表
348
- }
349
- if (targetTables.length === 1) {
350
- const ctx = await this.executeOnTable(targetTables[0], reqJson, keys_1.KeysOfSimpleSQL.SIMPLE_QUERY_PAGE);
351
- return ctx.getResModelForQueryPage();
352
- }
353
- // 多表查询时,需要参数校验
354
- this.validateQueryOrderBy(reqJson);
355
- // 根据排序方向确定表顺序:DESC 新→旧,ASC 旧→新
356
- const tablesForMerge = this.sortTablesForOrderBy(targetTables, reqJson);
357
- // 多表分页查询:顺序累计
358
- return this.merger.mergePageQuery(this.crudPro, tablesForMerge, reqJson, this.buildCfg(keys_1.KeysOfSimpleSQL.SIMPLE_QUERY_PAGE));
359
- }
360
- /**
361
- * 查询记录总数
362
- *
363
- * 如果涉及多个分表,会并行查询各分表并汇总。
364
- *
365
- * @param reqJson 请求参数
366
- * @returns 记录总数
367
- */
368
- async queryCount(reqJson) {
369
- const targetTables = await this.resolveQueryTables(reqJson);
370
- if (targetTables.length === 0) {
371
- return 0; // 无匹配表
372
- }
373
- if (targetTables.length === 1) {
374
- const ctx = await this.executeOnTable(targetTables[0], reqJson, keys_1.KeysOfSimpleSQL.SIMPLE_QUERY_COUNT);
375
- return ctx.getResModelItem('total_count') || 0;
376
- }
377
- // 多表统计:并行查询后求和
378
- const countPromises = targetTables.map(table => this.queryCountFromTable(table, reqJson));
379
- const counts = await Promise.all(countPromises);
380
- return counts.reduce((sum, c) => sum + c, 0);
381
- }
382
- /**
383
- * 判断记录是否存在
384
- *
385
- * 如果涉及多个分表,会并行查询,任一分表存在即返回 true。
386
- *
387
- * @param reqJson 请求参数
388
- * @returns 是否存在
389
- */
390
- async isExist(reqJson) {
391
- const targetTables = await this.resolveQueryTables(reqJson);
392
- if (targetTables.length === 0) {
393
- return false; // 无匹配表
394
- }
395
- if (targetTables.length === 1) {
396
- const ctx = await this.executeOnTable(targetTables[0], reqJson, keys_1.KeysOfSimpleSQL.SIMPLE_QUERY_EXIST);
397
- return ctx.getResModelItem('is_exist') === true;
398
- }
399
- // 多表判断:任一表存在即返回 true
400
- const promises = targetTables.map(table => this.isExistInTable(table, reqJson));
401
- const results = await Promise.all(promises);
402
- return results.some(exists => exists);
403
- }
404
- // ============ 私有方法:分表路由 ============
405
- /**
406
- * 【batchInsert 专用】按分表对数据进行分组
407
- *
408
- * 仅用于 batchInsert 方法,遍历所有数据项,
409
- * 根据分表规则计算每条数据的目标分表,将相同分表的数据归为一组。
410
- *
411
- * @param dataArray 数据数组
412
- * @returns 分表 -> 数据列表 的映射
413
- */
414
- batchInsertGroupDataByTable(dataArray) {
415
- const groupedData = new Map();
416
- for (const item of dataArray) {
417
- const targetTable = this.batchInsertResolveTableForData(item);
418
- if (!groupedData.has(targetTable)) {
419
- groupedData.set(targetTable, []);
420
- }
421
- groupedData.get(targetTable).push(item);
422
- }
423
- return groupedData;
424
- }
425
- batchInsertResolveTableForData(item) {
426
- const context = {
427
- config: this.config,
428
- data: item,
429
- };
430
- // 批量插入使用 resolveForInsert,确保返回单一表名
431
- return this.router.resolveForInsert(this.config, context);
432
- }
433
- /**
434
- * 解析单个目标表
435
- *
436
- * 写操作必须确定单一目标表,否则抛出异常。
437
- */
438
- resolveSingleTable(reqJson, operation) {
439
- // insert 操作使用 resolveForInsert(从 data 提取字段)
440
- if (operation === 'insert') {
441
- const context = {
442
- config: this.config,
443
- data: reqJson.data,
444
- };
445
- return this.router.resolveForInsert(this.config, context);
446
- }
447
- // 其他操作使用 resolveForCondition(从 condition 提取字段)
448
- const context = {
449
- config: this.config,
450
- condition: reqJson.condition,
451
- };
452
- const result = this.router.resolveForCondition(this.config, context);
453
- if (Array.isArray(result)) {
454
- throw new Error(`[ShardingCrudPro] ${operation} 操作必须能确定单一目标表,` +
455
- `但分表规则返回了多个表: ${result.slice(0, 5).join(', ')}${result.length > 5 ? '...' : ''}。` +
456
- `请检查 condition 中是否包含分表字段(${this.getShardingColumnHint()})。`);
457
- }
458
- return result;
459
- }
460
- /**
461
- * 获取分表字段提示
462
- */
463
- getShardingColumnHint() {
464
- if (this.config.type === ShardingConfig_1.ShardingType.RANGE || this.config.type === ShardingConfig_1.ShardingType.HASH) {
465
- return this.config.shardingColumn || '分表字段';
466
- }
467
- if ([ShardingConfig_1.ShardingType.YEAR, ShardingConfig_1.ShardingType.MONTH, ShardingConfig_1.ShardingType.DAY].includes(this.config.type)) {
468
- return this.config.timeColumn || 'created_at';
469
- }
470
- return '分表字段';
471
- }
472
- // ============ 私有方法:配置构建 ============
473
- /**
474
- * 构建配置
475
- */
476
- buildCfg(sqlSimpleName) {
477
- return {
478
- ...this.baseCfg,
479
- sqlTable: this.config.baseTable,
480
- sqlSimpleName,
481
- };
482
- }
483
- // ============ 私有方法:执行操作 ============
484
- /**
485
- * 在指定表上执行操作
486
- */
487
- async executeOnTable(table, reqJson, sqlSimpleName) {
488
- const cfg = this.buildCfg(sqlSimpleName);
489
- cfg.sqlTable = table; // 替换为实际分表名
490
- return this.crudPro.executeCrudByCfg(reqJson, cfg);
491
- }
492
- /**
493
- * 从指定表查询单条
494
- */
495
- async queryOneFromTable(table, reqJson) {
496
- try {
497
- const ctx = await this.executeOnTable(table, reqJson, keys_1.KeysOfSimpleSQL.SIMPLE_QUERY_ONE);
498
- return ctx.getOneObj();
499
- }
500
- catch (e) {
501
- // 表不存在时返回 null
502
- return null;
503
- }
504
- }
505
- /**
506
- * 从指定表查询数量
507
- */
508
- async queryCountFromTable(table, reqJson) {
509
- try {
510
- const ctx = await this.executeOnTable(table, reqJson, keys_1.KeysOfSimpleSQL.SIMPLE_QUERY_COUNT);
511
- return ctx.getResModelItem('total_count') || 0;
512
- }
513
- catch (e) {
514
- // 表不存在时返回 0
515
- return 0;
516
- }
517
- }
518
- /**
519
- * 判断指定表中是否存在记录
520
- */
521
- async isExistInTable(table, reqJson) {
522
- try {
523
- const ctx = await this.executeOnTable(table, reqJson, keys_1.KeysOfSimpleSQL.SIMPLE_QUERY_EXIST);
524
- return ctx.getResModelItem('is_exist') === true;
525
- }
526
- catch (e) {
527
- // 表不存在时返回 false
528
- return false;
529
- }
530
- }
531
- // ============ 表存在性检查和自动创建 ============
532
- /**
533
- * 获取数据库中真实存在的表名集合
534
- */
535
- async getExistingTablesSet() {
536
- const { sqlDatabase, sqlDbType } = this.baseCfg;
537
- if (!sqlDatabase || !sqlDbType) {
538
- throw new Error('[ShardingCrudPro] 未配置 sqlDatabase 或 sqlDbType');
539
- }
540
- const { tables } = await this.crudPro.getAllTableInfos({
541
- sqlDatabase,
542
- sqlDbType: sqlDbType,
543
- });
544
- return new Set(tables.map(t => t.name));
545
- }
546
- /**
547
- * 检查表是否存在
548
- */
549
- async isTableExists(tableName) {
550
- const existingSet = await this.getExistingTablesSet();
551
- return existingSet.has(tableName);
552
- }
553
- /**
554
- * 创建分表(如果需要)
555
- *
556
- * 根据配置检查分表是否存在:
557
- * - 如果分表已存在,直接返回
558
- * - 如果分表不存在且 autoCreateTable=true,自动创建
559
- * - 如果分表不存在且 autoCreateTable=false,抛出异常
560
- *
561
- * @param tableName 目标分表名
562
- */
563
- async createShardingTableIfNeeded(tableName) {
564
- // 检查表是否已存在
565
- if (await this.isTableExists(tableName)) {
566
- return;
567
- }
568
- // 根据配置决定是否自动创建
569
- if (!this.config.autoCreateTable) {
570
- throw new Error(`[ShardingCrudPro] 分表 ${tableName} 不存在。请先创建分表,或设置 autoCreateTable: true 自动创建`);
571
- }
572
- // 检查数据库配置
573
- if (!this.baseCfg.sqlDatabase || !this.baseCfg.sqlDbType) {
574
- throw new Error('[ShardingCrudPro] 请先调用 setBaseCfg 设置数据库配置');
575
- }
576
- // 执行创建
577
- const result = await this.tableCreator.createTableIfNeeded(tableName, {
578
- sqlDatabase: this.baseCfg.sqlDatabase,
579
- sqlDbType: this.baseCfg.sqlDbType,
580
- }, this.config.tableCreateOptions);
581
- if (!result.success) {
582
- throw result.error || new Error(`[ShardingCrudPro] 创建分表 ${tableName} 失败`);
583
- }
584
- }
585
- // ============ 参数校验 ============
586
- /**
587
- * 校验查询参数的 orderBy 是否符合约束
588
- *
589
- * 约束条件:
590
- * - 必须传 orderBy 参数
591
- * - orderBy 必须为 timeColumn DESC 或 timeColumn ASC(如 'created_at DESC' / 'created_at ASC')
592
- *
593
- * 这是时间分表查询的核心约束:
594
- * - 排序字段必须是分表字段(timeColumn),不允许其他字段
595
- * - DESC 时表顺序为 新→旧,ASC 时表顺序为 旧→新(由调用方反转)
596
- * - 这样无需内存排序,直接按表顺序拼接即可
597
- *
598
- * @param reqJson 请求参数
599
- */
600
- validateQueryOrderBy(reqJson) {
601
- // 非时间分表不强制 orderBy 约束(多表合并排序由调用方自行保证)
602
- const timeColumn = this.config.timeColumn;
603
- if (!timeColumn) {
604
- return;
605
- }
606
- const orderBy = reqJson.orderBy;
607
- // 1. 必须传 orderBy
608
- if (!orderBy) {
609
- throw new Error(`[ShardingCrudPro] 查询操作必须传 orderBy 参数。` +
610
- `期望值: '${timeColumn} DESC' 或 '${timeColumn} ASC'`);
611
- }
612
- // 2. 使用工具类解析并校验首个排序字段是否为 timeColumn
613
- const firstOrderBy = OrderByUtils_1.OrderByUtils.getFirstOrderBy(orderBy);
614
- if (!firstOrderBy) {
615
- throw new Error(`[ShardingCrudPro] orderBy 参数格式错误,无法解析。` +
616
- `期望值: '${timeColumn} DESC' 或 '${timeColumn} ASC'`);
617
- }
618
- if (firstOrderBy.fieldName !== timeColumn) {
619
- throw new Error(`[ShardingCrudPro] orderBy 首个排序字段必须为 '${timeColumn}',` +
620
- `当前值: '${firstOrderBy.fieldName}'`);
621
- }
622
- // 校验排序方向是否为 ASC 或 DESC
623
- const orderType = firstOrderBy.orderType.toUpperCase();
624
- if (orderType !== 'ASC' && orderType !== 'DESC') {
625
- throw new Error(`[ShardingCrudPro] orderBy 排序方向必须是 'ASC' 或 'DESC',` +
626
- `当前值: '${firstOrderBy.orderType}'`);
627
- }
628
- }
629
- /**
630
- * 判断 orderBy 是否为 ASC 方向
631
- *
632
- * 前提:validateQueryOrderBy 已通过校验,orderBy 格式合法
633
- *
634
- * @param reqJson 请求参数
635
- * @returns true 表示 ASC,false 表示 DESC
636
- */
637
- isAscOrderBy(reqJson) {
638
- return OrderByUtils_1.OrderByUtils.isFirstOrderByAsc(reqJson.orderBy);
639
- }
640
- /**
641
- * 根据排序方向对分表列表进行排序
642
- *
643
- * 分表名后缀为时间格式(如 202403、20240101),字典序即时间序。
644
- * - DESC:降序排列(新→旧)
645
- * - ASC:升序排列(旧→新)
646
- *
647
- * @param tables 分表列表
648
- * @param reqJson 请求参数
649
- * @returns 排序后的分表列表(新数组,不修改原数组)
650
- */
651
- sortTablesForOrderBy(tables, reqJson) {
652
- const isAsc = this.isAscOrderBy(reqJson);
653
- return [...tables].sort((a, b) => isAsc ? a.localeCompare(b) : b.localeCompare(a));
654
- }
655
- // ============ 查询表解析(委托给 ShardingRouter) ============
656
- /**
657
- * 解析查询表(带真实表过滤)
658
- *
659
- * 将查询路由委托给 ShardingRouter.resolveQuery,
660
- * 由 ShardingRouter 统一处理所有查询路由逻辑。
661
- */
662
- async resolveQueryTables(reqJson) {
663
- const context = {
664
- config: this.config,
665
- condition: reqJson.condition,
666
- };
667
- // 创建表信息提供者
668
- const tableInfoProvider = {
669
- getExistingTables: () => this.getExistingTablesSet(),
670
- };
671
- // 委托给 ShardingRouter 处理查询路由
672
- return this.router.resolveQuery(this.config, context, tableInfoProvider);
673
- }
674
- }
675
- exports.ShardingCrudPro = ShardingCrudPro;