midway-fatcms 0.0.8 → 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 (75) hide show
  1. package/.qoder/skills/midway-fatcms/02-crud-quick.md +38 -0
  2. package/.qoder/skills/midway-fatcms/03-crud-sharding.md +37 -36
  3. package/.qoder/skills/midway-fatcms/07-examples.md +4 -0
  4. package/dist/configuration.d.ts +10 -0
  5. package/dist/configuration.js +26 -0
  6. package/dist/controller/helpers.controller.d.ts +6 -0
  7. package/dist/controller/helpers.controller.js +19 -0
  8. package/dist/libs/crud-pro/CrudPro.d.ts +29 -2
  9. package/dist/libs/crud-pro/CrudPro.js +58 -2
  10. package/dist/libs/crud-pro/exceptions.d.ts +7 -0
  11. package/dist/libs/crud-pro/exceptions.js +7 -0
  12. package/dist/libs/crud-pro/interfaces.d.ts +1 -0
  13. package/dist/libs/crud-pro/models/CrudResult.d.ts +3 -2
  14. package/dist/libs/crud-pro/models/CrudResult.js +1 -1
  15. package/dist/libs/crud-pro/models/ServiceHub.d.ts +2 -0
  16. package/dist/libs/crud-pro/services/CrudProDataTypeConvertService.d.ts +70 -2
  17. package/dist/libs/crud-pro/services/CrudProDataTypeConvertService.js +205 -13
  18. package/dist/libs/crud-pro/services/CrudProTableMetaService.d.ts +36 -0
  19. package/dist/libs/crud-pro/services/CrudProTableMetaService.js +97 -3
  20. package/dist/libs/crud-pro/services/CurdProServiceHub.d.ts +2 -0
  21. package/dist/libs/crud-pro/services/CurdProServiceHub.js +6 -0
  22. package/dist/libs/crud-pro-quick/CrudProQuick.d.ts +93 -6
  23. package/dist/libs/crud-pro-quick/CrudProQuick.js +192 -32
  24. package/dist/libs/crud-sharding/ShardingBase.d.ts +78 -0
  25. package/dist/libs/crud-sharding/ShardingBase.js +179 -0
  26. package/dist/libs/crud-sharding/ShardingByCustomCrud.d.ts +35 -0
  27. package/dist/libs/crud-sharding/ShardingByCustomCrud.js +297 -0
  28. package/dist/libs/crud-sharding/ShardingByHashCrud.d.ts +38 -0
  29. package/dist/libs/crud-sharding/ShardingByHashCrud.js +86 -0
  30. package/dist/libs/crud-sharding/ShardingByKeyCrud.d.ts +39 -0
  31. package/dist/libs/crud-sharding/ShardingByKeyCrud.js +74 -0
  32. package/dist/libs/crud-sharding/ShardingByTimeCrud.d.ts +66 -0
  33. package/dist/libs/crud-sharding/ShardingByTimeCrud.js +524 -0
  34. package/dist/libs/crud-sharding/ShardingConfig.d.ts +10 -8
  35. package/dist/libs/crud-sharding/ShardingConfig.js +3 -3
  36. package/dist/libs/crud-sharding/TIME_COLUMN_CLEAN_SPEC.md +1 -1
  37. package/dist/libs/crud-sharding/index.d.ts +10 -13
  38. package/dist/libs/crud-sharding/index.js +21 -17
  39. package/dist/models/RedisKeys.d.ts +1 -0
  40. package/dist/models/RedisKeys.js +1 -0
  41. package/dist/service/TableMetaCacheRedisSubscriber.d.ts +31 -0
  42. package/dist/service/TableMetaCacheRedisSubscriber.js +98 -0
  43. package/dist/service/curd/CurdMixService.d.ts +2 -2
  44. package/dist/service/curd/CurdProService.d.ts +109 -5
  45. package/dist/service/curd/CurdProService.js +127 -7
  46. package/package.json +1 -1
  47. package/src/configuration.ts +27 -0
  48. package/src/controller/helpers.controller.ts +15 -0
  49. package/src/libs/crud-pro/CrudPro.ts +73 -4
  50. package/src/libs/crud-pro/exceptions.ts +8 -0
  51. package/src/libs/crud-pro/interfaces.ts +1 -0
  52. package/src/libs/crud-pro/models/CrudResult.ts +5 -5
  53. package/src/libs/crud-pro/models/ServiceHub.ts +4 -0
  54. package/src/libs/crud-pro/services/CrudProDataTypeConvertService.ts +238 -15
  55. package/src/libs/crud-pro/services/CrudProTableMetaService.ts +110 -2
  56. package/src/libs/crud-pro/services/CurdProServiceHub.ts +8 -0
  57. package/src/libs/crud-pro-quick/CrudProQuick.ts +234 -46
  58. package/src/libs/crud-sharding/ShardingBase.ts +256 -0
  59. package/src/libs/crud-sharding/ShardingByCustomCrud.ts +329 -0
  60. package/src/libs/crud-sharding/ShardingByHashCrud.ts +111 -0
  61. package/src/libs/crud-sharding/ShardingByKeyCrud.ts +97 -0
  62. package/src/libs/crud-sharding/ShardingByTimeCrud.ts +628 -0
  63. package/src/libs/crud-sharding/ShardingConfig.ts +10 -8
  64. package/src/libs/crud-sharding/TIME_COLUMN_CLEAN_SPEC.md +1 -1
  65. package/src/libs/crud-sharding/index.ts +17 -14
  66. package/src/models/RedisKeys.ts +1 -0
  67. package/src/service/TableMetaCacheRedisSubscriber.ts +105 -0
  68. package/src/service/curd/CurdMixService.ts +2 -2
  69. package/src/service/curd/CurdProService.ts +131 -9
  70. package/dist/libs/crud-sharding/ShardingCrudPro.d.ts +0 -208
  71. package/dist/libs/crud-sharding/ShardingCrudPro.js +0 -879
  72. package/dist/libs/crud-sharding/ShardingRouter.d.ts +0 -70
  73. package/dist/libs/crud-sharding/ShardingRouter.js +0 -396
  74. package/src/libs/crud-sharding/ShardingCrudPro.ts +0 -1105
  75. package/src/libs/crud-sharding/ShardingRouter.ts +0 -533
@@ -7,15 +7,15 @@
7
7
  * - YEAR: 按年分表,如 order_2024, order_2025
8
8
  * - MONTH: 按月分表,如 order_202401, order_202402
9
9
  * - DAY: 按日分表,如 order_20240101
10
- * - RANGE: 按范围分表,如 user_0 ~ user_99
11
10
  * - HASH: 按哈希分表,如 order_01 ~ order_16
11
+ * - KEY: 按键值分表,字段原值=表后缀,如 order_east, order_west
12
12
  * - CUSTOM: 自定义分表规则
13
13
  *
14
14
  * @example
15
- * import { ShardingCrudPro, ShardingType } from '@/libs/crud-sharding';
15
+ * import { ShardingByTimeCrud, ShardingType } from '@/libs/crud-sharding';
16
16
  *
17
- * // 创建分表操作器
18
- * const sharding = new ShardingCrudPro(crudPro, {
17
+ * // 创建时间分表操作器
18
+ * const sharding = new ShardingByTimeCrud(crudProFactory, {
19
19
  * type: ShardingType.MONTH,
20
20
  * baseTable: 't_order',
21
21
  * timeColumn: 'created_at',
@@ -25,18 +25,21 @@
25
25
  * await sharding.insert({
26
26
  * data: { order_id: '001', amount: 100, created_at: '2024-03-15' },
27
27
  * });
28
- *
29
- * // 分页查询(自动合并多表结果)
30
- * const result = await sharding.findPage({
31
- * condition: { created_at: { $gte: '2024-01-01', $lte: '2024-03-31' } },
32
- * pageNo: 1,
33
- * pageSize: 10,
34
- * });
35
28
  */
36
29
 
37
- // 核心类
38
- export { ShardingCrudPro } from './ShardingCrudPro';
39
- export { ShardingRouter } from './ShardingRouter';
30
+ // 基类
31
+ export { ShardingBase } from './ShardingBase';
32
+
33
+ // 四个子类
34
+ export { ShardingByTimeCrud } from './ShardingByTimeCrud';
35
+ export { ShardingByHashCrud } from './ShardingByHashCrud';
36
+ export { ShardingByKeyCrud } from './ShardingByKeyCrud';
37
+ export { ShardingByCustomCrud } from './ShardingByCustomCrud';
38
+
39
+ // 兼容旧代码:ShardingCrudPro → ShardingBase
40
+ export { ShardingBase as ShardingCrudPro } from './ShardingBase';
41
+
42
+ // 合并器(仅 Time 使用)
40
43
  export { ShardingMerger } from './ShardingMerger';
41
44
 
42
45
  // 配置和类型
@@ -11,6 +11,7 @@ const RedisKeys = {
11
11
  CURD_MIX_BY_DICT_PREFIX: 'FatCurdMixByDict@@',
12
12
  QUERY_ENUM_INFO_PREFIX: 'FatQueryEnum@@',
13
13
  REDIS_LOCK_MIDDILEWARE: 'FatRedisLock@@',
14
+ TABLE_META_CACHE_INVALIDATE_CHANNEL: 'FatTableMetaCacheInvalidate',
14
15
  };
15
16
 
16
17
  export { RedisKeys };
@@ -0,0 +1,105 @@
1
+ import { RedisService } from '@midwayjs/redis';
2
+ import { clearTableMetaCache } from '@/libs/crud-pro/services/CrudProTableMetaService';
3
+ import { RedisKeys } from '@/models/RedisKeys';
4
+
5
+ const CHANNEL = RedisKeys.TABLE_META_CACHE_INVALIDATE_CHANNEL;
6
+
7
+ /**
8
+ * 本节点唯一标识,用于区分是自己发出的广播还是其他节点发出的
9
+ * 避免重复清空(本节点已在发布前清空过)
10
+ */
11
+ const NODE_ID = `node_${process.pid}_${Date.now()}`;
12
+
13
+ /**
14
+ * 用于发布消息的 Redis 客户端(复用 @midwayjs/redis 注入的实例)
15
+ */
16
+ let publishClient: RedisService | null = null;
17
+
18
+ /**
19
+ * 用于订阅的 Redis 客户端(独立连接,ioredis 规定订阅连接不能执行普通命令)
20
+ */
21
+ let subscribeClient: RedisService | null = null;
22
+
23
+ /**
24
+ * 初始化发布客户端
25
+ *
26
+ * 在 Midway 容器初始化完成后调用,注入已有的 RedisService 实例
27
+ * @param client RedisService 实例
28
+ */
29
+ export function initTableMetaCachePublishClient(client: RedisService): void {
30
+ publishClient = client;
31
+ }
32
+
33
+ /**
34
+ * 初始化 Redis 订阅,监听缓存失效广播
35
+ *
36
+ * 使用 RedisService.duplicate() 创建独立的订阅连接。
37
+ * 收到广播后自动清空本节点的本地缓存。
38
+ *
39
+ * @param mainClient 已有的 RedisService 实例,用于 duplicate 出订阅连接
40
+ */
41
+ export function initTableMetaCacheSubscriber(mainClient: RedisService): void {
42
+ if (subscribeClient) {
43
+ return; // 防止重复初始化
44
+ }
45
+
46
+ // duplicate() 创建一个同配置的独立连接
47
+ subscribeClient = mainClient.duplicate() as RedisService;
48
+
49
+ subscribeClient.subscribe(CHANNEL).catch((err: Error) => {
50
+ console.error('[TableMetaCacheRedis] subscribe failed:', err.message);
51
+ });
52
+
53
+ subscribeClient.on('message', (channel: string, message: string) => {
54
+ if (channel !== CHANNEL) return;
55
+
56
+ try {
57
+ const data = JSON.parse(message);
58
+ // 跳过自己发出的广播(本节点已在发布前清空过)
59
+ if (data.nodeId === NODE_ID) return;
60
+
61
+ const stats = clearTableMetaCache();
62
+ console.info(`[TableMetaCacheRedis] 收到其他节点(${data.nodeId})的缓存清空广播,已清空本地缓存:`, stats);
63
+ } catch (e) {
64
+ console.error('[TableMetaCacheRedis] 处理缓存失效广播异常:', e);
65
+ }
66
+ });
67
+
68
+ subscribeClient.on('error', (err: Error) => {
69
+ console.error('[TableMetaCacheRedis] subscriber connection error:', err.message);
70
+ });
71
+
72
+ console.info('[TableMetaCacheRedis] 订阅已启动,监听频道:', CHANNEL);
73
+ }
74
+
75
+ /**
76
+ * 清空本节点缓存并广播通知集群中其他节点
77
+ *
78
+ * 调用流程:
79
+ * 1. 清空本节点本地缓存
80
+ * 2. 通过 Redis PUBLISH 广播消息
81
+ * 3. 其他节点收到消息后清空各自的本地缓存
82
+ *
83
+ * @returns 清空前的缓存统计
84
+ */
85
+ export function clearTableMetaCacheWithBroadcast(): { metaCacheCount: number; tableInfoCacheCount: number } {
86
+ // 1. 清空本节点缓存
87
+ const stats = clearTableMetaCache();
88
+
89
+ // 2. 广播通知其他节点
90
+ if (publishClient && typeof publishClient.publish === 'function') {
91
+ const message = JSON.stringify({
92
+ nodeId: NODE_ID,
93
+ timestamp: Date.now(),
94
+ beforeClear: stats,
95
+ });
96
+ publishClient.publish(CHANNEL, message).catch((err: Error) => {
97
+ console.error('[TableMetaCacheRedis] publish failed:', err.message);
98
+ });
99
+ } else {
100
+ // 没有 Redis 连接时仅清空本地(单节点部署降级)
101
+ console.info('[TableMetaCacheRedis] 无 Redis 发布客户端,仅清空本节点缓存');
102
+ }
103
+
104
+ return stats;
105
+ }
@@ -10,7 +10,7 @@ import { IRequestCfgModel2 } from '@/models/bizmodels';
10
10
  import { CurdMixByLinkToCustomService } from './CurdMixByLinkToCustomService';
11
11
  // import { SqlDbType } from '@/libs/crud-pro/models/keys';
12
12
  import { ExecuteContext, IExecuteContextCustom } from '@/libs/crud-pro/models/ExecuteContext';
13
- import { ShardingCrudPro } from "@/libs/crud-sharding";
13
+ import { ShardingBase } from "@/libs/crud-sharding";
14
14
  import { IGetQuickCrudParam, IGetShardingCrudParam } from './CurdProService';
15
15
 
16
16
  export interface ILinkColumnRelationParam {
@@ -68,7 +68,7 @@ export class CurdMixService {
68
68
  }
69
69
 
70
70
 
71
- public getShardingCrud(param: IGetShardingCrudParam): ShardingCrudPro {
71
+ public getShardingCrud(param: IGetShardingCrudParam): ShardingBase {
72
72
  this.prepare();
73
73
  return this.curdProService.getShardingCrud(param);
74
74
  }
@@ -17,7 +17,7 @@ import { BaseService } from '../base/BaseService';
17
17
  import { IRequestCfgModel2, IVisitorExt } from '@/models/bizmodels';
18
18
  import { IWorkbenchEntity } from '@/models/SystemEntities';
19
19
  import { CrudProQuick } from '@/libs/crud-pro-quick';
20
- import { ShardingCrudPro, IShardingConfig } from '@/libs/crud-sharding';
20
+ import { ShardingBase, ShardingByTimeCrud, ShardingByHashCrud, ShardingByKeyCrud, ShardingByCustomCrud, ShardingType, IShardingConfig } from '@/libs/crud-sharding';
21
21
  import { fixCfgModel } from './fixCfgModel';
22
22
  import { GLOBAL_STATIC_CONFIG } from '@/libs/global-config/global-config';
23
23
  import { fixSoftDelete } from "@/libs/crud-pro-quick/fixSoftDelete";
@@ -172,10 +172,40 @@ export class CurdProService extends BaseService {
172
172
 
173
173
  private responseCfgHandlers: Record<RelatedType, IExecuteContextHandler> = {} as any;
174
174
 
175
+ /**
176
+ * 注册“响应配置处理器”(response cfg handler)。
177
+ *
178
+ * 这些处理器会在一次 CRUD/SQL 执行完成后被调用,用于对 `ExecuteContext` 做统一的二次加工,
179
+ * 例如:追加扩展数据、做字段脱敏、统一包装返回结构等。
180
+ *
181
+ * @param key 处理器类型标识(业务自定义枚举/常量)
182
+ * @param handler 处理器实现
183
+ *
184
+ * @example
185
+ * curdProService.setResponseCfgHandlers(RelatedType.USER, {
186
+ * async handleExecuteContextPrepare(ctx) {
187
+ * // 可在执行结果处理前做准备
188
+ * },
189
+ * async handleExecuteContext(ctx) {
190
+ * // 对 ctx.result / ctx.data 等进行二次加工
191
+ * },
192
+ * });
193
+ */
175
194
  public setResponseCfgHandlers(key: RelatedType, handler: IExecuteContextHandler) {
176
195
  this.responseCfgHandlers[key] = handler;
177
196
  }
178
197
 
198
+ /**
199
+ * 获取当前已注册的“响应配置处理器”集合。
200
+ *
201
+ * 一般用于调试或在外层框架中统一检查/复用已有 handler。
202
+ *
203
+ * @returns 以 `RelatedType` 为 key 的处理器映射表
204
+ *
205
+ * @example
206
+ * const handlers = curdProService.getResponseCfgHandlers();
207
+ * const userHandler = handlers[RelatedType.USER];
208
+ */
179
209
  public getResponseCfgHandlers(): Record<RelatedType, IExecuteContextHandler> {
180
210
  return this.responseCfgHandlers;
181
211
  }
@@ -213,8 +243,21 @@ export class CurdProService extends BaseService {
213
243
  }
214
244
 
215
245
  /**
216
- * 依赖缓存中与加载的配置
217
- * @param reqJson
246
+ * 通过 `method` 走配置化 CRUD 执行(从缓存/加载的配置中解析)。
247
+ *
248
+ * 适用于:前端/调用方只传 `method` + 入参,由服务端根据 `method` 找到对应的 CRUD 配置并执行。
249
+ * 在非调试模式下会启用配置缓存(调试模式关闭缓存,便于开发即时生效)。
250
+ *
251
+ * @param reqJson 请求模型,必须包含 `method`
252
+ * @returns `ExecuteContext`(包含执行过程与结果数据)
253
+ *
254
+ * @example
255
+ * const ctx = await curdProService.executeCrud({
256
+ * method: 'user.list',
257
+ * pageNo: 1,
258
+ * pageSize: 20,
259
+ * });
260
+ * console.log(ctx.result);
218
261
  */
219
262
  async executeCrud(reqJson: IRequestModel): Promise<ExecuteContext> {
220
263
  if (!reqJson.method) {
@@ -228,13 +271,46 @@ export class CurdProService extends BaseService {
228
271
  return curdPro.executeCrud(reqJson, isEnableCache);
229
272
  }
230
273
 
231
- // 直接执行一个SQL
274
+ /**
275
+ * 直接执行一段 SQL(不依赖 `method` 配置)。
276
+ *
277
+ * 适用于:后台管理/运维脚本/内部工具需要直接跑 SQL 的场景(仍会复用本服务的连接池、日志、visitor、事务等上下文)。
278
+ *
279
+ * @param sqlCfgModel SQL 执行配置(数据库、类型、SQL、参数等)
280
+ * @returns SQL 执行结果
281
+ *
282
+ * @example
283
+ * const res = await curdProService.executeSQL({
284
+ * sqlDbType: SqlDbType.mysql,
285
+ * sqlDatabase: 'mydb',
286
+ * executeSql: 'select * from t_user where id = ?',
287
+ * executeSqlArgs: [1],
288
+ * });
289
+ */
232
290
  public async executeSQL(sqlCfgModel: ISqlCfgModel): Promise<ExecuteSQLResult> {
233
291
  const curdPro = this.getCrudPro();
234
292
  return curdPro.executeSQL(sqlCfgModel);
235
293
  }
236
294
 
237
- // 直接执行一个请求
295
+ /**
296
+ * 直接按指定配置执行一次 CRUD(不走缓存查找)。
297
+ *
298
+ * 适用于:调用方已经拿到了/动态生成了本次要执行的 CRUD 配置,想直接执行并得到结果。
299
+ * 该方法会对 `cfgModel` 做两类标准化处理:
300
+ * - 统一修正/补全配置(`fixCfgModel`)
301
+ * - 应用软删除规则(`fixSoftDelete`)
302
+ *
303
+ * @param reqJson 本次请求入参
304
+ * @param cfgModel 本次请求对应的 CRUD 配置
305
+ * @returns `ExecuteContext`
306
+ *
307
+ * @example
308
+ * const cfg = await curdProService.getCachedCfgByMethod('user.list');
309
+ * const ctx = await curdProService.executeCrudByCfg(
310
+ * { method: 'user.list', pageNo: 1, pageSize: 20 },
311
+ * cfg as any
312
+ * );
313
+ */
238
314
  public async executeCrudByCfg(reqJson: IRequestModel, cfgModel: IRequestCfgModel2): Promise<ExecuteContext> {
239
315
  this.logInfo('executeCrudByCfg', cfgModel);
240
316
  const curdPro = this.getCrudPro();
@@ -277,7 +353,7 @@ export class CurdProService extends BaseService {
277
353
  * 获取分表 CRUD 操作器
278
354
  *
279
355
  * @param param 参数对象
280
- * @returns ShardingCrudPro 实例
356
+ * @returns ShardingBase 实例(实际类型根据 shardingConfig.type 决定)
281
357
  *
282
358
  * @example
283
359
  * const sharding = curdProService.getShardingCrud({
@@ -290,20 +366,66 @@ export class CurdProService extends BaseService {
290
366
  * },
291
367
  * });
292
368
  */
293
- public getShardingCrud(param: IGetShardingCrudParam): ShardingCrudPro {
369
+ public getShardingCrud(param: IGetShardingCrudParam): ShardingBase {
294
370
  const { sqlDatabase, sqlDbType, shardingConfig } = param;
295
- // 传入工厂函数,每次需要时创建新的 CrudPro 实例
296
- const sharding = new ShardingCrudPro(() => this.getCrudPro(), shardingConfig);
371
+ const factory = () => this.getCrudPro();
372
+
373
+ let sharding: ShardingBase;
374
+ switch (shardingConfig.type) {
375
+ case ShardingType.YEAR:
376
+ case ShardingType.MONTH:
377
+ case ShardingType.DAY:
378
+ sharding = new ShardingByTimeCrud(factory, shardingConfig);
379
+ break;
380
+ case ShardingType.HASH:
381
+ sharding = new ShardingByHashCrud(factory, shardingConfig);
382
+ break;
383
+ case ShardingType.KEY:
384
+ sharding = new ShardingByKeyCrud(factory, shardingConfig);
385
+ break;
386
+ case ShardingType.CUSTOM:
387
+ sharding = new ShardingByCustomCrud(factory, shardingConfig);
388
+ break;
389
+ default:
390
+ throw new Error(`[CurdProService] 不支持的分表类型: ${shardingConfig.type}`);
391
+ }
392
+
297
393
  sharding.setBaseCfg({ sqlDatabase, sqlDbType });
298
394
  return sharding;
299
395
  }
300
396
 
397
+ /**
398
+ * 根据 `method` 获取(可能来自缓存的)CRUD 配置。
399
+ *
400
+ * 适用于:你只想先拿到配置做检查/二次加工/审计,再决定是否执行。
401
+ * 非调试模式会启用缓存(调试模式关闭缓存)。
402
+ *
403
+ * @param method 配置方法名,例如:`user.list`
404
+ * @returns CRUD 配置模型
405
+ *
406
+ * @example
407
+ * const cfg = await curdProService.getCachedCfgByMethod('user.list');
408
+ * console.log(cfg.sqlSimpleName);
409
+ */
301
410
  public async getCachedCfgByMethod(method: string): Promise<IRequestCfgModel> {
302
411
  const curdPro = this.getCrudPro();
303
412
  const isEnableCache = !this.isEnableDebug(); // 开发环境不使用缓存
304
413
  return curdPro.getCachedCfgByMethod(method, isEnableCache);
305
414
  }
306
415
 
416
+ /**
417
+ * 获取数据库的表/视图等结构信息列表(用于数据源探测、表选择器、代码生成等场景)。
418
+ *
419
+ * @param query 查询条件(例如:指定数据库、关键字过滤、分页等)
420
+ * @param options 可选项(例如:是否包含视图/系统表、大小写处理等,具体以接口定义为准)
421
+ * @returns 表信息列表结果
422
+ *
423
+ * @example
424
+ * const tables = await curdProService.getAllTableInfos(
425
+ * { sqlDbType: SqlDbType.mysql, sqlDatabase: 'mydb' },
426
+ * { skipCache: true }
427
+ * );
428
+ */
307
429
  public async getAllTableInfos(query: ITableNamesQuery, options?: ITableNamesOptions): Promise<ITableListResult> {
308
430
  const curdPro = this.getCrudPro();
309
431
  return curdPro.getAllTableInfos(query, options);
@@ -1,208 +0,0 @@
1
- import { IRequestModel, IRequestCfgModel } from '../../libs/crud-pro/interfaces';
2
- import { IShardingConfig, CrudProFactory } from './ShardingConfig';
3
- import { CrudWriteResult, CrudQueryOneResult, CrudQueryListResult, CrudQueryPageResult, CrudExistResult, CrudCountResult, CrudUpsertResult } from '../../libs/crud-pro/models/CrudResult';
4
- import { ShardingBatchInsertResult } from './ShardingResult';
5
- /**
6
- * 分表 CRUD 操作封装器
7
- *
8
- * 在 CrudPro 之上封装分表功能,不修改 CrudPro 内部代码。
9
- * 提供透明的分表路由和多表查询结果合并能力。
10
- *
11
- * 支持的分表策略:
12
- * - YEAR: 按年分表,如 order_2024, order_2025
13
- * - MONTH: 按月分表,如 order_202401, order_202402
14
- * - DAY: 按日分表,如 order_20240101
15
- * - RANGE: 按范围分表,如 user_0 ~ user_99
16
- * - HASH: 按哈希分表,如 order_01 ~ order_16
17
- * - CUSTOM: 自定义分表规则
18
- *
19
- * @example
20
- * // 按月分表
21
- * const sharding = new ShardingCrudPro(crudPro, {
22
- * type: ShardingType.MONTH,
23
- * baseTable: 't_order',
24
- * timeColumn: 'created_at',
25
- * });
26
- *
27
- * // 插入数据(自动路由到正确分表)
28
- * await sharding.insert({ data: { order_id: '001', amount: 100, created_at: '2024-03-15' } });
29
- *
30
- * // 分页查询(自动合并多表结果)
31
- * const result = await sharding.findPage({
32
- * condition: { created_at: { $gte: '2024-01-01', $lte: '2024-03-31' } },
33
- * pageNo: 1,
34
- * pageSize: 10,
35
- * });
36
- */
37
- export declare class ShardingCrudPro {
38
- private readonly crudProFactory;
39
- private readonly config;
40
- private readonly router;
41
- private readonly merger;
42
- private readonly tableCreator;
43
- private baseCfg;
44
- private enableSoftDelete;
45
- constructor(crudProFactory: CrudProFactory, config: IShardingConfig);
46
- setBaseCfg(cfg: Partial<IRequestCfgModel>): this;
47
- getConfig(): IShardingConfig;
48
- /**
49
- * 设置是否启用软删除
50
- * @param enable 是否启用软删除
51
- */
52
- setEnableSoftDelete(enable: boolean): this;
53
- insert(reqJson: IRequestModel): Promise<CrudWriteResult>;
54
- batchInsert(reqJson: IRequestModel): Promise<ShardingBatchInsertResult>;
55
- update(reqJson: IRequestModel): Promise<CrudWriteResult>;
56
- delete(reqJson: IRequestModel): Promise<CrudWriteResult>;
57
- /**
58
- * 恢复软删除的记录
59
- * 将 deleted_at 字段重置为 0,deleted_by 重置为空字符串
60
- *
61
- * @param reqJson 请求参数,通过 condition 指定要恢复的记录
62
- * @returns CrudWriteResult 包含 affectedRows 和 getRawContext()
63
- *
64
- * @example
65
- * // 恢复指定 ID 的记录
66
- * await sharding.restore({ condition: { id: 1 } });
67
- *
68
- * // 恢复多条记录(使用 $in 操作符)
69
- * await sharding.restore({ condition: { id: { $in: [1, 2, 3] } } });
70
- *
71
- * // 按条件批量恢复记录
72
- * await sharding.restore({ condition: { status: 'deleted' } });
73
- */
74
- restore(reqJson: IRequestModel): Promise<CrudWriteResult>;
75
- insertOrUpdate(reqJson: IRequestModel): Promise<CrudUpsertResult>;
76
- insertOnDuplicateUpdate(reqJson: IRequestModel): Promise<CrudWriteResult>;
77
- findOne<T = Record<string, any>>(reqJson: IRequestModel): Promise<CrudQueryOneResult<T>>;
78
- /**
79
- * 查询唯一单条记录(期望结果为 0 条或 1 条)
80
- *
81
- * 与 findOne 不同,此方法会校验查询结果的唯一性:
82
- * - 如果查到 0 条:返回 row = null
83
- * - 如果查到 1 条:正常返回
84
- * - 如果查到多条:抛出异常,包含详细的定位信息(含分表名列表)
85
- *
86
- * 分表场景的特殊处理:
87
- * - 单分表:直接查询并校验唯一性
88
- * - 多分表:顺序查询各分表,累计找到的数量,超过 1 条立即抛异常
89
- *
90
- * @param reqJson 请求参数
91
- * @returns CrudQueryOneResult 包含 row、found 和 getRawContext()
92
- * @throws Error 如果查询到多条记录
93
- *
94
- * @example
95
- * // 根据唯一索引查询单条
96
- * const result = await sharding.findUniqueOne({ condition: { order_id: 'ORD001' } });
97
- * if (result.found) {
98
- * console.log(result.row);
99
- * }
100
- */
101
- findUniqueOne<T = Record<string, any>>(reqJson: IRequestModel): Promise<CrudQueryOneResult<T>>;
102
- findList<T = Record<string, any>>(reqJson: IRequestModel): Promise<CrudQueryListResult<T>>;
103
- findPage<T = Record<string, any>>(reqJson: IRequestModel): Promise<CrudQueryPageResult<T>>;
104
- findCount(reqJson: IRequestModel): Promise<CrudCountResult>;
105
- isExist(reqJson: IRequestModel): Promise<CrudExistResult>;
106
- private batchInsertGroupDataByTable;
107
- private batchInsertResolveTableForData;
108
- private resolveSingleTable;
109
- private getShardingColumnHint;
110
- private buildCfg;
111
- private executeOnTable;
112
- private findCountFromTable;
113
- private isExistInTable;
114
- private getExistingTablesSet;
115
- private isTableExists;
116
- private createShardingTableIfNeeded;
117
- /**
118
- * 判断是否为时间分表类型
119
- */
120
- private isTimeSharding;
121
- /**
122
- * 校验时间分表插入操作的 data 必须包含 timeColumn
123
- *
124
- * 时间分表需要根据时间字段路由到正确的分表,
125
- * 如果 data 中缺少时间字段,将无法确定数据应插入哪个分表。
126
- *
127
- * @param reqJson 请求参数
128
- * @param operation 操作名称(用于错误提示)
129
- * @throws Error 如果 data 中缺少时间字段
130
- */
131
- private validateTimeColumnForData;
132
- /**
133
- * 校验时间分表批量插入操作的每条 data 必须包含 timeColumn
134
- *
135
- * @param dataArray 数据数组
136
- * @param operation 操作名称(用于错误提示)
137
- * @throws Error 如果任一条 data 中缺少时间字段
138
- */
139
- private validateTimeColumnForBatchData;
140
- /**
141
- * 校验时间分表写操作的 condition 必须包含路由字段(timeColumn)
142
- *
143
- * 时间分表需要根据时间字段路由到正确的分表,
144
- * 如果 condition 中缺少时间字段,将无法确定操作哪个分表。
145
- *
146
- * @param reqJson 请求参数
147
- * @param operation 操作名称(用于错误提示)
148
- * @throws Error 如果 condition 中缺少时间字段
149
- */
150
- private validateRoutingFieldForCondition;
151
- /**
152
- * 智能处理时间分表场景下的时间字段
153
- *
154
- * **问题背景**:
155
- * 在时间分表(YEAR/MONTH/DAY)场景中,timeColumn 既是路由键也是查询条件。
156
- * 用户传入的时间值可能精度不一致(如传 '2024-01-15' 但数据库存的是毫秒时间戳),
157
- * 直接作为 WHERE 条件可能导致匹配失败。
158
- *
159
- * **处理规则**(详见 TIME_COLUMN_CLEAN_SPEC.md):
160
- * 1. 操作符表达式($gte/$lte/$range/$in/$null 等)→ 始终保留,不做处理
161
- * 2. 精确值 + 有 primaryKey → 清理(从 WHERE 移除,仅用于路由)
162
- * 3. 精确值 + 无 primaryKey + 日期粒度字符串(年/月/日)→ 转换为 $gte/$lte 范围
163
- * 4. 精确值 + 无 primaryKey + 其他(时间戳/datetime/null等)→ 保留原值
164
- *
165
- * **示例**:
166
- * ```typescript
167
- * // 有主键 + 精确值 → 清理
168
- * { order_id: 'ORD001', created_at: '2024-01-15' }
169
- * → { order_id: 'ORD001' }
170
- *
171
- * // 无主键 + 日期粒度字符串 → 转换为范围
172
- * { status: 'paid', created_at: '2024-01' }
173
- * → { status: 'paid', created_at: { $gte: '2024-01-01 00:00:00', $lte: '2024-01-31 23:59:59' } }
174
- *
175
- * // 操作符表达式 → 保留
176
- * { order_id: 'ORD001', created_at: { $gte: '2024-01', $lte: '2024-06' } }
177
- * → 不做任何处理
178
- * ```
179
- *
180
- * @param reqJson 请求参数
181
- * @returns 处理后的请求参数
182
- */
183
- private cleanTimeColumnForSingleTableQuery;
184
- /**
185
- * 校验时间操作符的值必须精确到秒
186
- *
187
- * MySQL 中 '2026-04-30' 等价于 '2026-04-30 00:00:00',
188
- * 导致 $lte: '2026-04-30' 会丢失当天所有数据。
189
- * 如果用户需要查整月/整天数据,应传粒度字符串由系统自动转换,而非手动写 $gte/$lte。
190
- *
191
- * @param operatorValue 操作符表达式的值
192
- * @param timeColumn 时间字段名
193
- * @throws Error 如果操作符值缺少时间部分
194
- */
195
- private validateTimeOperatorPrecision;
196
- private validateFindOrderBy;
197
- private isAscOrderBy;
198
- private sortTablesForOrderBy;
199
- /**
200
- * 构建辅助定位信息
201
- */
202
- private buildDebugInfo;
203
- /**
204
- * 格式化唯一性错误消息
205
- */
206
- private formatUniqueError;
207
- private resolveFindTables;
208
- }