midway-fatcms 0.0.6 → 0.0.8

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 (121) hide show
  1. package/.qoder/skills/midway-fatcms/01-quick-start.md +231 -0
  2. package/.qoder/skills/midway-fatcms/02-crud-quick.md +337 -0
  3. package/.qoder/skills/midway-fatcms/03-crud-sharding.md +488 -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 +500 -0
  8. package/.qoder/skills/midway-fatcms/SKILL.md +96 -0
  9. package/README.md +9 -9
  10. package/dist/controller/base/BaseApiController.d.ts +1 -2
  11. package/dist/controller/base/BaseApiController.js +0 -4
  12. package/dist/controller/gateway/DocGatewayController.js +1 -1
  13. package/dist/controller/manage/FlowConfigManageApi.js +4 -2
  14. package/dist/controller/manage/SysConfigMangeApi.js +6 -1
  15. package/dist/controller/manage/UserAccountManageApi.js +7 -2
  16. package/dist/index.d.ts +2 -2
  17. package/dist/index.js +2 -2
  18. package/dist/libs/crud-pro/CrudPro.d.ts +23 -2
  19. package/dist/libs/crud-pro/CrudPro.js +53 -2
  20. package/dist/libs/crud-pro/interfaces.d.ts +82 -12
  21. package/dist/libs/crud-pro/models/CrudResult.d.ts +115 -0
  22. package/dist/libs/crud-pro/models/CrudResult.js +126 -0
  23. package/dist/libs/crud-pro/models/RequestModel.d.ts +2 -38
  24. package/dist/libs/crud-pro/models/RequestModel.js +2 -99
  25. package/dist/libs/crud-pro/services/CrudProExecuteSqlService.js +36 -2
  26. package/dist/libs/crud-pro/services/CrudProGenSqlCondition.js +8 -4
  27. package/dist/libs/crud-pro/services/CrudProTableMetaService.js +1 -2
  28. package/dist/libs/crud-pro/utils/OrderByUtils.d.ts +70 -0
  29. package/dist/libs/crud-pro/utils/OrderByUtils.js +158 -0
  30. package/dist/libs/crud-pro-quick/CrudProQuick.d.ts +295 -0
  31. package/dist/libs/crud-pro-quick/CrudProQuick.js +529 -0
  32. package/dist/libs/crud-pro-quick/fixSoftDelete.d.ts +30 -0
  33. package/dist/{service/curd → libs/crud-pro-quick}/fixSoftDelete.js +3 -6
  34. package/dist/libs/crud-pro-quick/index.d.ts +36 -0
  35. package/dist/libs/crud-pro-quick/index.js +49 -0
  36. package/dist/libs/crud-pro-quick/models.d.ts +33 -0
  37. package/dist/libs/crud-pro-quick/models.js +2 -0
  38. package/dist/libs/crud-sharding/ShardingConfig.d.ts +15 -2
  39. package/dist/libs/crud-sharding/ShardingConfig.js +2 -2
  40. package/dist/libs/crud-sharding/ShardingCrudPro.d.ts +119 -274
  41. package/dist/libs/crud-sharding/ShardingCrudPro.js +559 -379
  42. package/dist/libs/crud-sharding/ShardingMerger.d.ts +12 -20
  43. package/dist/libs/crud-sharding/ShardingMerger.js +36 -51
  44. package/dist/libs/crud-sharding/ShardingResult.d.ts +33 -0
  45. package/dist/libs/crud-sharding/ShardingResult.js +16 -0
  46. package/dist/libs/crud-sharding/ShardingRouter.d.ts +1 -0
  47. package/dist/libs/crud-sharding/ShardingRouter.js +25 -6
  48. package/dist/libs/crud-sharding/ShardingTableCreator.d.ts +21 -4
  49. package/dist/libs/crud-sharding/ShardingTableCreator.js +193 -59
  50. package/dist/libs/crud-sharding/ShardingUtils.d.ts +48 -0
  51. package/dist/libs/crud-sharding/ShardingUtils.js +122 -1
  52. package/dist/libs/crud-sharding/TIME_COLUMN_CLEAN_SPEC.md +488 -0
  53. package/dist/libs/crud-sharding/index.d.ts +4 -3
  54. package/dist/libs/crud-sharding/index.js +14 -2
  55. package/dist/models/bizmodels.d.ts +2 -6
  56. package/dist/service/SysAppService.d.ts +2 -2
  57. package/dist/service/SysAppService.js +16 -5
  58. package/dist/service/SysConfigService.d.ts +1 -1
  59. package/dist/service/SysConfigService.js +7 -2
  60. package/dist/service/SysDictDataService.js +14 -4
  61. package/dist/service/SysMenuService.js +7 -2
  62. package/dist/service/curd/CurdMixService.d.ts +6 -4
  63. package/dist/service/curd/CurdMixService.js +16 -2
  64. package/dist/service/curd/CurdProService.d.ts +43 -27
  65. package/dist/service/curd/CurdProService.js +32 -33
  66. package/dist/service/flow/FlowConfigService.js +7 -2
  67. package/dist/service/flow/FlowInstanceCrudService.js +22 -19
  68. package/package.json +2 -1
  69. package/src/controller/base/BaseApiController.ts +0 -5
  70. package/src/controller/gateway/DocGatewayController.ts +1 -1
  71. package/src/controller/manage/CrudStandardDesignApi.ts +4 -3
  72. package/src/controller/manage/FlowConfigManageApi.ts +4 -2
  73. package/src/controller/manage/SysConfigMangeApi.ts +6 -1
  74. package/src/controller/manage/UserAccountManageApi.ts +7 -2
  75. package/src/index.ts +2 -2
  76. package/src/libs/crud-pro/CrudPro.ts +62 -4
  77. package/src/libs/crud-pro/interfaces.ts +110 -15
  78. package/src/libs/crud-pro/models/CrudResult.ts +178 -0
  79. package/src/libs/crud-pro/models/RequestModel.ts +4 -110
  80. package/src/libs/crud-pro/services/CrudProExecuteSqlService.ts +41 -2
  81. package/src/libs/crud-pro/services/CrudProGenSqlCondition.ts +11 -7
  82. package/src/libs/crud-pro/services/CrudProTableMetaService.ts +1 -2
  83. package/src/libs/crud-pro/utils/OrderByUtils.ts +169 -0
  84. package/src/libs/crud-pro-quick/CrudProQuick.ts +594 -0
  85. package/src/{service/curd → libs/crud-pro-quick}/fixSoftDelete.ts +23 -13
  86. package/src/libs/crud-pro-quick/index.ts +52 -0
  87. package/src/libs/crud-pro-quick/models.ts +35 -0
  88. package/src/libs/crud-sharding/ShardingConfig.ts +18 -2
  89. package/src/libs/crud-sharding/ShardingCrudPro.ts +689 -440
  90. package/src/libs/crud-sharding/ShardingMerger.ts +47 -73
  91. package/src/libs/crud-sharding/ShardingResult.ts +29 -0
  92. package/src/libs/crud-sharding/ShardingRouter.ts +27 -6
  93. package/src/libs/crud-sharding/ShardingTableCreator.ts +214 -71
  94. package/src/libs/crud-sharding/ShardingUtils.ts +137 -0
  95. package/src/libs/crud-sharding/TIME_COLUMN_CLEAN_SPEC.md +488 -0
  96. package/src/libs/crud-sharding/index.ts +14 -3
  97. package/src/models/bizmodels.ts +4 -7
  98. package/src/service/SysAppService.ts +18 -7
  99. package/src/service/SysConfigService.ts +8 -3
  100. package/src/service/SysDictDataService.ts +14 -4
  101. package/src/service/SysMenuService.ts +7 -2
  102. package/src/service/crudstd/CrudStdService.ts +2 -2
  103. package/src/service/curd/CurdMixService.ts +26 -5
  104. package/src/service/curd/CurdProService.ts +58 -39
  105. package/src/service/flow/FlowConfigService.ts +7 -2
  106. package/src/service/flow/FlowInstanceCrudService.ts +23 -20
  107. package/dist/libs/crud-pro/README.md +0 -809
  108. package/dist/libs/crud-pro/README_FUNC.md +0 -193
  109. package/dist/libs/crud-sharding/ROUTING_LOGIC.md +0 -944
  110. package/dist/models/StandardColumns.d.ts +0 -71
  111. package/dist/models/StandardColumns.js +0 -28
  112. package/dist/service/curd/CrudProQuick.d.ts +0 -190
  113. package/dist/service/curd/CrudProQuick.js +0 -319
  114. package/dist/service/curd/README.md +0 -1001
  115. package/dist/service/curd/fixSoftDelete.d.ts +0 -20
  116. package/src/libs/crud-pro/README.md +0 -809
  117. package/src/libs/crud-pro/README_FUNC.md +0 -193
  118. package/src/libs/crud-sharding/ROUTING_LOGIC.md +0 -944
  119. package/src/models/StandardColumns.ts +0 -76
  120. package/src/service/curd/CrudProQuick.ts +0 -360
  121. package/src/service/curd/README.md +0 -1001
@@ -15,8 +15,8 @@ const keys_1 = require("../../libs/crud-pro/models/keys");
15
15
  * });
16
16
  */
17
17
  class ShardingTableCreator {
18
- constructor(crudPro, config) {
19
- this.crudPro = crudPro;
18
+ constructor(crudProFactory, config) {
19
+ this.crudProFactory = crudProFactory;
20
20
  this.config = config;
21
21
  this.validateIdentifier(this.config.baseTable, 'baseTable');
22
22
  }
@@ -46,28 +46,50 @@ class ShardingTableCreator {
46
46
  // 校验分表名格式,防止 SQL 注入
47
47
  this.validateIdentifier(tableName, 'tableName');
48
48
  const { copyIndexes = false } = options;
49
- try {
50
- // 1. 获取基表元信息
51
- const tableMeta = await this.getBaseTableMeta(baseCfg);
52
- if (!tableMeta || !tableMeta.columnDetails || tableMeta.columnDetails.length === 0) {
53
- throw new Error(`[ShardingTableCreator] 无法获取基表 ${this.config.baseTable} 的结构信息`);
54
- }
55
- // 2. 生成建表 SQL
56
- const createSql = this.generateCreateTableSql(tableName, tableMeta.columnDetails, baseCfg.sqlDbType, options);
57
- // 3. 执行建表
58
- await this.executeCreateSql(baseCfg, createSql);
59
- // 4. 可选:复制索引
60
- if (copyIndexes) {
49
+ // 0. 先检查表是否已存在
50
+ const tableExists = await this.checkTableExists(tableName, baseCfg);
51
+ if (tableExists) {
52
+ // 表已存在,跳过创建和索引复制
53
+ return { success: true, tableName, createSql: '' };
54
+ }
55
+ // 1. 获取基表元信息
56
+ const tableMeta = await this.getBaseTableMeta(baseCfg);
57
+ if (!tableMeta || !tableMeta.columnDetails || tableMeta.columnDetails.length === 0) {
58
+ throw new Error(`[ShardingTableCreator] 无法获取基表 ${this.config.baseTable} 的结构信息`);
59
+ }
60
+ // 2. 生成建表 SQL
61
+ const createSql = this.generateCreateTableSql(tableName, tableMeta.columnDetails, baseCfg.sqlDbType, options);
62
+ // 3. 执行建表
63
+ await this.executeCreateSql(baseCfg, createSql);
64
+ // 4. 可选:复制索引(只在新建表时执行)
65
+ if (copyIndexes) {
66
+ try {
61
67
  await this.copyIndexes(tableName, baseCfg);
62
68
  }
63
- return { success: true, tableName, createSql };
69
+ catch (e) {
70
+ console.error("copyIndexes", e);
71
+ }
72
+ }
73
+ return { success: true, tableName, createSql };
74
+ }
75
+ /**
76
+ * 检查表是否已存在
77
+ * 复用 CrudPro.getAllTableInfos 接口,使用缓存提升性能
78
+ * DDL 操作本身具有幂等性(CREATE TABLE IF NOT EXISTS),缓存短暂延迟可接受
79
+ */
80
+ async checkTableExists(tableName, baseCfg) {
81
+ try {
82
+ const { tables } = await this.crudProFactory().getAllTableInfos({
83
+ sqlDatabase: baseCfg.sqlDatabase,
84
+ sqlDbType: baseCfg.sqlDbType,
85
+ }, { skipCache: false } // 使用缓存,避免每次建表都直查数据库
86
+ );
87
+ return tables.some(t => t.name === tableName);
64
88
  }
65
89
  catch (error) {
66
- return {
67
- success: false,
68
- tableName,
69
- error: error instanceof Error ? error : new Error(String(error)),
70
- };
90
+ console.warn('[ShardingTableCreator] 检查表存在性失败:', error);
91
+ // 检查失败时,假设表不存在,继续尝试创建
92
+ return false;
71
93
  }
72
94
  }
73
95
  /**
@@ -107,20 +129,19 @@ class ShardingTableCreator {
107
129
  */
108
130
  async loadMySQLColumns(baseCfg) {
109
131
  const sql = `
110
- SELECT
111
- COLUMN_NAME, COLUMN_TYPE, IS_NULLABLE, COLUMN_KEY,
132
+ SELECT
133
+ COLUMN_NAME, COLUMN_TYPE, IS_NULLABLE, COLUMN_KEY,
112
134
  COLUMN_DEFAULT, COLUMN_COMMENT, EXTRA
113
135
  FROM information_schema.COLUMNS
114
136
  WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = '${this.config.baseTable}'
115
137
  ORDER BY ORDINAL_POSITION
116
138
  `.trim();
117
- const result = await this.crudPro.executeSQL({
139
+ const rows = await this.crudProFactory().executeSQL({
118
140
  isNativeSQL: true,
119
141
  sqlDatabase: baseCfg.sqlDatabase,
120
142
  sqlDbType: baseCfg.sqlDbType,
121
143
  executeSql: sql,
122
- });
123
- const rows = (result === null || result === void 0 ? void 0 : result.rows) || [];
144
+ }) || [];
124
145
  return rows.map((row) => {
125
146
  var _a;
126
147
  return ({
@@ -150,13 +171,12 @@ class ShardingTableCreator {
150
171
  WHERE table_schema = '${schema}' AND table_name = '${this.config.baseTable}'
151
172
  ORDER BY ordinal_position
152
173
  `.trim();
153
- const result = await this.crudPro.executeSQL({
174
+ const rows = await this.crudProFactory().executeSQL({
154
175
  isNativeSQL: true,
155
176
  sqlDatabase: baseCfg.sqlDatabase,
156
177
  sqlDbType: baseCfg.sqlDbType,
157
178
  executeSql: sql,
158
- });
159
- const rows = (result === null || result === void 0 ? void 0 : result.rows) || [];
179
+ }) || [];
160
180
  // 获取主键列
161
181
  const pkColumns = await this.getPostgreSQLPrimaryKeys(baseCfg, schema);
162
182
  return rows.map((row) => {
@@ -186,13 +206,13 @@ class ShardingTableCreator {
186
206
  AND c.relname = '${this.config.baseTable}'
187
207
  AND i.indisprimary
188
208
  `.trim();
189
- const result = await this.crudPro.executeSQL({
209
+ const result = await this.crudProFactory().executeSQL({
190
210
  isNativeSQL: true,
191
211
  sqlDatabase: baseCfg.sqlDatabase,
192
212
  sqlDbType: baseCfg.sqlDbType,
193
213
  executeSql: sql,
194
- });
195
- return ((result === null || result === void 0 ? void 0 : result.rows) || []).map((row) => row.attname);
214
+ }) || [];
215
+ return result.map((row) => row.attname);
196
216
  }
197
217
  /**
198
218
  * 加载 SQL Server 表列信息
@@ -212,13 +232,12 @@ class ShardingTableCreator {
212
232
  WHERE c.object_id = OBJECT_ID('${this.config.baseTable}')
213
233
  ORDER BY c.column_id
214
234
  `.trim();
215
- const result = await this.crudPro.executeSQL({
235
+ const rows = await this.crudProFactory().executeSQL({
216
236
  isNativeSQL: true,
217
237
  sqlDatabase: baseCfg.sqlDatabase,
218
238
  sqlDbType: baseCfg.sqlDbType,
219
239
  executeSql: sql,
220
- });
221
- const rows = (result === null || result === void 0 ? void 0 : result.rows) || [];
240
+ }) || [];
222
241
  // 获取主键列
223
242
  const pkColumns = await this.getSQLServerPrimaryKeys(baseCfg);
224
243
  return rows.map((row) => {
@@ -248,13 +267,13 @@ class ShardingTableCreator {
248
267
  WHERE i.object_id = OBJECT_ID('${this.config.baseTable}')
249
268
  AND i.is_primary_key = 1
250
269
  `.trim();
251
- const result = await this.crudPro.executeSQL({
270
+ const result = await this.crudProFactory().executeSQL({
252
271
  isNativeSQL: true,
253
272
  sqlDatabase: baseCfg.sqlDatabase,
254
273
  sqlDbType: baseCfg.sqlDbType,
255
274
  executeSql: sql,
256
- });
257
- return ((result === null || result === void 0 ? void 0 : result.rows) || []).map((row) => row.name);
275
+ }) || [];
276
+ return result.map((row) => row.name);
258
277
  }
259
278
  /**
260
279
  * 生成建表 SQL
@@ -560,14 +579,44 @@ class ShardingTableCreator {
560
579
  /**
561
580
  * 执行建表 SQL
562
581
  */
563
- async executeCreateSql(baseCfg, createSql) {
582
+ async executeCreateSql(baseCfg, createSql, options = {}) {
564
583
  const sqlCfgModel = {
565
584
  isNativeSQL: true,
566
585
  sqlDatabase: baseCfg.sqlDatabase,
567
586
  sqlDbType: baseCfg.sqlDbType,
568
587
  executeSql: createSql,
569
588
  };
570
- await this.crudPro.executeSQL(sqlCfgModel);
589
+ try {
590
+ await this.crudProFactory().executeSQL(sqlCfgModel);
591
+ }
592
+ catch (error) {
593
+ // 静默处理 DDL "已存在"类错误(表已存在、索引已存在等)
594
+ if (options.silentDuplicate && this.isDuplicateError(error)) {
595
+ return;
596
+ }
597
+ throw error;
598
+ }
599
+ }
600
+ /**
601
+ * 判断是否为"已存在"类错误(表已存在、索引已存在等)
602
+ */
603
+ isDuplicateError(error) {
604
+ if (!error)
605
+ return false;
606
+ const code = error.code || error.errno;
607
+ const message = error.message || '';
608
+ // MySQL: ER_TABLE_EXISTS_ERROR (1050), ER_DUP_KEYNAME (1061)
609
+ // PostgreSQL: 42P07 (duplicate_table), 42P06 (duplicate_schema)
610
+ // SQL Server: 错误消息包含 "already exists"
611
+ return code === 'ER_TABLE_EXISTS_ERROR' ||
612
+ code === 'ER_DUP_KEYNAME' ||
613
+ code === 1050 ||
614
+ code === 1061 ||
615
+ code === '42P07' ||
616
+ code === '42P06' ||
617
+ message.includes('already exists') ||
618
+ message.includes('Duplicate') ||
619
+ message.includes('Table') && message.includes('already exists');
571
620
  }
572
621
  /**
573
622
  * 复制索引(可选功能)
@@ -576,23 +625,111 @@ class ShardingTableCreator {
576
625
  * 注意:主键索引已在建表时创建,此处只复制普通索引。
577
626
  */
578
627
  async copyIndexes(tableName, baseCfg) {
579
- try {
580
- const indexDefs = await this.getIndexDefinitions(baseCfg);
581
- for (const indexDef of indexDefs) {
582
- // 跳过主键索引(已创建)
583
- if (indexDef.isPrimary || !indexDef.createSql)
584
- continue;
585
- // 替换表名(支持不同数据库的标识符语法)
628
+ const indexDefs = await this.getIndexDefinitions(baseCfg);
629
+ // 过滤掉主键索引和空定义
630
+ const validIndexDefs = indexDefs.filter(def => !def.isPrimary && def.createSql);
631
+ if (validIndexDefs.length === 0) {
632
+ return;
633
+ }
634
+ // 一次性获取目标表已存在的所有索引名
635
+ const existingIndexNames = await this.getExistingIndexNames(tableName, baseCfg);
636
+ // 用于去重的 Set,防止同一请求中重复创建同名索引
637
+ const createdIndexNames = new Set();
638
+ for (const indexDef of validIndexDefs) {
639
+ try {
640
+ // 替换表名
586
641
  const indexSql = this.replaceTableNameInIndexSql(indexDef.createSql, tableName, baseCfg.sqlDbType);
587
- // 重命名索引以避免冲突
642
+ // 重命名索引
588
643
  const renamedSql = this.renameIndex(indexSql, tableName, baseCfg.sqlDbType);
589
- await this.executeCreateSql(baseCfg, renamedSql);
644
+ // 提取新索引名
645
+ const newIndexName = this.extractIndexName(renamedSql, baseCfg.sqlDbType);
646
+ // 如果索引已存在(数据库中或本次请求已创建),跳过创建
647
+ if (existingIndexNames.has(newIndexName) || createdIndexNames.has(newIndexName)) {
648
+ continue;
649
+ }
650
+ console.info('[ShardingTableCreator] 准备创建索引 =>' + newIndexName + " ===> renamedSql" + renamedSql);
651
+ // 使用 silentDuplicate 静默处理"索引已存在"错误
652
+ await this.executeCreateSql(baseCfg, renamedSql, { silentDuplicate: true });
653
+ // 创建成功后添加到已创建列表,防止重复创建
654
+ createdIndexNames.add(newIndexName);
655
+ }
656
+ catch (error) {
657
+ // 非预期错误,记录日志
658
+ console.warn('[ShardingTableCreator] 创建索引失败:', (error === null || error === void 0 ? void 0 : error.message) || error);
659
+ }
660
+ }
661
+ }
662
+ /**
663
+ * 获取目标表已存在的所有索引名
664
+ */
665
+ async getExistingIndexNames(tableName, baseCfg) {
666
+ const indexNames = new Set();
667
+ try {
668
+ if (baseCfg.sqlDbType === keys_1.SqlDbType.mysql) {
669
+ const sql = `
670
+ SELECT INDEX_NAME
671
+ FROM information_schema.STATISTICS
672
+ WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = '${tableName}'
673
+ `;
674
+ const result = await this.crudProFactory().executeSQL({
675
+ isNativeSQL: true,
676
+ sqlDatabase: baseCfg.sqlDatabase,
677
+ sqlDbType: baseCfg.sqlDbType,
678
+ executeSql: sql,
679
+ });
680
+ result.forEach(row => indexNames.add(row.INDEX_NAME));
681
+ }
682
+ else if (baseCfg.sqlDbType === keys_1.SqlDbType.postgres) {
683
+ const sql = `
684
+ SELECT indexname
685
+ FROM pg_indexes
686
+ WHERE schemaname = 'public' AND tablename = '${tableName}'
687
+ `;
688
+ const result = await this.crudProFactory().executeSQL({
689
+ isNativeSQL: true,
690
+ sqlDatabase: baseCfg.sqlDatabase,
691
+ sqlDbType: baseCfg.sqlDbType,
692
+ executeSql: sql,
693
+ });
694
+ result.forEach(row => indexNames.add(row.indexname));
695
+ }
696
+ else if (baseCfg.sqlDbType === keys_1.SqlDbType.sqlserver) {
697
+ const sql = `
698
+ SELECT name
699
+ FROM sys.indexes
700
+ WHERE object_id = OBJECT_ID('${tableName}')
701
+ `;
702
+ const result = await this.crudProFactory().executeSQL({
703
+ isNativeSQL: true,
704
+ sqlDatabase: baseCfg.sqlDatabase,
705
+ sqlDbType: baseCfg.sqlDbType,
706
+ executeSql: sql,
707
+ });
708
+ result.forEach(row => indexNames.add(row.name));
590
709
  }
591
710
  }
592
711
  catch (error) {
593
- console.warn('[ShardingTableCreator] 复制索引失败:', error);
594
- // 索引复制失败不影响主流程
712
+ console.warn('[ShardingTableCreator] 获取已存在索引列表失败:', error);
595
713
  }
714
+ return indexNames;
715
+ }
716
+ /**
717
+ * 从 CREATE INDEX 语句中提取索引名
718
+ */
719
+ extractIndexName(indexSql, sqlDbType) {
720
+ if (sqlDbType === keys_1.SqlDbType.mysql) {
721
+ const match = indexSql.match(/INDEX `([^`]+)`/i);
722
+ return match ? match[1] : '';
723
+ }
724
+ else if (sqlDbType === keys_1.SqlDbType.postgres) {
725
+ const match = indexSql.match(/INDEX "([^"]+)"/i);
726
+ return match ? match[1] : '';
727
+ }
728
+ else if (sqlDbType === keys_1.SqlDbType.sqlserver) {
729
+ const match = indexSql.match(/INDEX \[([^\]]+)\]/i);
730
+ return match ? match[1] : '';
731
+ }
732
+ return '';
596
733
  }
597
734
  /**
598
735
  * 在索引 SQL 中替换表名
@@ -654,13 +791,12 @@ class ShardingTableCreator {
654
791
  AND TABLE_NAME = '${this.config.baseTable}'
655
792
  ORDER BY INDEX_NAME, SEQ_IN_INDEX
656
793
  `;
657
- const result = await this.crudPro.executeSQL({
794
+ const rows = await this.crudProFactory().executeSQL({
658
795
  isNativeSQL: true,
659
796
  sqlDatabase: baseCfg.sqlDatabase,
660
797
  sqlDbType: baseCfg.sqlDbType,
661
798
  executeSql: sql,
662
- });
663
- const rows = (result === null || result === void 0 ? void 0 : result.rows) || [];
799
+ }) || [];
664
800
  // 按索引名分组
665
801
  const indexMap = new Map();
666
802
  for (const row of rows) {
@@ -708,13 +844,12 @@ class ShardingTableCreator {
708
844
  WHERE n.nspname = '${schema}'
709
845
  AND t.relname = '${this.config.baseTable}'
710
846
  `;
711
- const result = await this.crudPro.executeSQL({
847
+ const rows = await this.crudProFactory().executeSQL({
712
848
  isNativeSQL: true,
713
849
  sqlDatabase: baseCfg.sqlDatabase,
714
850
  sqlDbType: baseCfg.sqlDbType,
715
851
  executeSql: sql,
716
- });
717
- const rows = (result === null || result === void 0 ? void 0 : result.rows) || [];
852
+ }) || [];
718
853
  return rows.map((row) => ({
719
854
  isPrimary: row.is_primary,
720
855
  createSql: row.index_def,
@@ -742,13 +877,12 @@ class ShardingTableCreator {
742
877
  WHERE i.object_id = OBJECT_ID('${this.config.baseTable}')
743
878
  AND i.name IS NOT NULL
744
879
  `;
745
- const result = await this.crudPro.executeSQL({
880
+ const rows = await this.crudProFactory().executeSQL({
746
881
  isNativeSQL: true,
747
882
  sqlDatabase: baseCfg.sqlDatabase,
748
883
  sqlDbType: baseCfg.sqlDbType,
749
884
  executeSql: sql,
750
- });
751
- const rows = (result === null || result === void 0 ? void 0 : result.rows) || [];
885
+ }) || [];
752
886
  return rows.map((row) => {
753
887
  const isPrimary = row.is_primary;
754
888
  const isUnique = row.is_unique;
@@ -36,3 +36,51 @@ export declare function getCurrentSuffix(shardingType: ShardingType | undefined)
36
36
  * @returns 是否为当前表
37
37
  */
38
38
  export declare function isCurrentTable(tableName: string, baseTable: string, shardingType: ShardingType | undefined): boolean;
39
+ /**
40
+ * 日期字符串的粒度类型
41
+ */
42
+ export declare type DateStringGranularity = 'year' | 'month' | 'day' | 'datetime' | null;
43
+ /**
44
+ * 判断日期字符串的粒度
45
+ *
46
+ * 根据字符串格式推断精度:
47
+ * - 4位纯年:'2024' → 'year'
48
+ * - 7位年-月:'2024-01' → 'month'
49
+ * - 10位年-月-日:'2024-01-15' → 'day'
50
+ * - 带时分秒:'2024-01-15 10:30:00' → 'datetime'(精度已足够,不需要转换)
51
+ *
52
+ * @param value 待判断的值
53
+ * @returns 粒度类型,非日期字符串返回 null
54
+ */
55
+ export declare function detectDateStringGranularity(value: string): DateStringGranularity;
56
+ /**
57
+ * 判断值是否为时间操作符表达式(ICompareCondition / ILogicalCondition)
58
+ *
59
+ * 排除 Date 对象后,typeof === 'object' 且非 null 即为操作符表达式。
60
+ * 判断优先级:`value instanceof Date` → 先于 `typeof === 'object'` 判断。
61
+ *
62
+ * @param value 待判断的值
63
+ * @returns 是否为操作符表达式
64
+ */
65
+ export declare function isOperatorExpression(value: unknown): boolean;
66
+ /**
67
+ * 将日期字符串按粒度转换为 $gte/$lte 范围表达式
68
+ *
69
+ * 仅对 year/month/day 粒度的字符串进行转换,
70
+ * datetime 粒度和非日期字符串不转换返回 null。
71
+ * 闰年2月自动处理(28/29天)。
72
+ *
73
+ * @param value 日期字符串
74
+ * @returns 范围表达式对象,不需要转换时返回 null
75
+ *
76
+ * @example
77
+ * expandDateToRange('2024')
78
+ * // → { $gte: '2024-01-01 00:00:00', $lte: '2024-12-31 23:59:59' }
79
+ *
80
+ * expandDateToRange('2024-01')
81
+ * // → { $gte: '2024-01-01 00:00:00', $lte: '2024-01-31 23:59:59' }
82
+ *
83
+ * expandDateToRange('2024-01-15')
84
+ * // → { $gte: '2024-01-15 00:00:00', $lte: '2024-01-15 23:59:59' }
85
+ */
86
+ export declare function expandDateToRange(value: string): Record<string, string> | null;
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.isCurrentTable = exports.getCurrentSuffix = exports.getTimeSuffix = exports.shardingTypeToGranularity = void 0;
3
+ exports.expandDateToRange = exports.isOperatorExpression = exports.detectDateStringGranularity = exports.isCurrentTable = exports.getCurrentSuffix = exports.getTimeSuffix = exports.shardingTypeToGranularity = void 0;
4
4
  const ShardingConfig_1 = require("./ShardingConfig");
5
5
  /**
6
6
  * 将 ShardingType 转换为时间粒度
@@ -75,3 +75,124 @@ function isCurrentTable(tableName, baseTable, shardingType) {
75
75
  return tableName === expectedTableName;
76
76
  }
77
77
  exports.isCurrentTable = isCurrentTable;
78
+ /**
79
+ * 判断日期字符串的粒度
80
+ *
81
+ * 根据字符串格式推断精度:
82
+ * - 4位纯年:'2024' → 'year'
83
+ * - 7位年-月:'2024-01' → 'month'
84
+ * - 10位年-月-日:'2024-01-15' → 'day'
85
+ * - 带时分秒:'2024-01-15 10:30:00' → 'datetime'(精度已足够,不需要转换)
86
+ *
87
+ * @param value 待判断的值
88
+ * @returns 粒度类型,非日期字符串返回 null
89
+ */
90
+ function detectDateStringGranularity(value) {
91
+ const trimmed = value.trim();
92
+ // 4位纯年:'2024'
93
+ if (/^\d{4}$/.test(trimmed)) {
94
+ return 'year';
95
+ }
96
+ // 7位年-月:'2024-01'
97
+ if (/^\d{4}-\d{2}$/.test(trimmed)) {
98
+ return 'month';
99
+ }
100
+ // 10位年-月-日:'2024-01-15'
101
+ if (/^\d{4}-\d{2}-\d{2}$/.test(trimmed)) {
102
+ return 'day';
103
+ }
104
+ // 带时分秒的完整时间字符串(精度已足够,不需要转换)
105
+ if (/^\d{4}-\d{2}-\d{2}[T ]\d{1,2}/.test(trimmed)) {
106
+ return 'datetime';
107
+ }
108
+ return null;
109
+ }
110
+ exports.detectDateStringGranularity = detectDateStringGranularity;
111
+ /**
112
+ * 判断值是否为时间操作符表达式(ICompareCondition / ILogicalCondition)
113
+ *
114
+ * 排除 Date 对象后,typeof === 'object' 且非 null 即为操作符表达式。
115
+ * 判断优先级:`value instanceof Date` → 先于 `typeof === 'object'` 判断。
116
+ *
117
+ * @param value 待判断的值
118
+ * @returns 是否为操作符表达式
119
+ */
120
+ function isOperatorExpression(value) {
121
+ // Date 对象是精确值,不是操作符
122
+ if (value instanceof Date) {
123
+ return false;
124
+ }
125
+ // 非-null 对象视为操作符表达式(如 { $gte: ... }, { $or: ... })
126
+ return typeof value === 'object' && value !== null && Object.keys(value).length > 0;
127
+ }
128
+ exports.isOperatorExpression = isOperatorExpression;
129
+ /**
130
+ * 获取指定年月的最后一天日期
131
+ *
132
+ * @param year 年份
133
+ * @param month 月份(1-12)
134
+ * @returns 最后一天的日号(28/29/30/31)
135
+ */
136
+ function getLastDayOfMonth(year, month) {
137
+ // new Date(year, month, 0) 返回上个月最后一天
138
+ return new Date(year, month, 0).getDate();
139
+ }
140
+ /**
141
+ * 将日期字符串按粒度转换为 $gte/$lte 范围表达式
142
+ *
143
+ * 仅对 year/month/day 粒度的字符串进行转换,
144
+ * datetime 粒度和非日期字符串不转换返回 null。
145
+ * 闰年2月自动处理(28/29天)。
146
+ *
147
+ * @param value 日期字符串
148
+ * @returns 范围表达式对象,不需要转换时返回 null
149
+ *
150
+ * @example
151
+ * expandDateToRange('2024')
152
+ * // → { $gte: '2024-01-01 00:00:00', $lte: '2024-12-31 23:59:59' }
153
+ *
154
+ * expandDateToRange('2024-01')
155
+ * // → { $gte: '2024-01-01 00:00:00', $lte: '2024-01-31 23:59:59' }
156
+ *
157
+ * expandDateToRange('2024-01-15')
158
+ * // → { $gte: '2024-01-15 00:00:00', $lte: '2024-01-15 23:59:59' }
159
+ */
160
+ function expandDateToRange(value) {
161
+ const granularity = detectDateStringGranularity(value);
162
+ if (!granularity || granularity === 'datetime') {
163
+ return null;
164
+ }
165
+ const trimmed = value.trim();
166
+ switch (granularity) {
167
+ case 'year': {
168
+ // '2024' → 2024-01-01 ~ 2024-12-31
169
+ const year = parseInt(trimmed, 10);
170
+ return {
171
+ $gte: `${year}-01-01 00:00:00`,
172
+ $lte: `${year}-12-31 23:59:59`,
173
+ };
174
+ }
175
+ case 'month': {
176
+ // '2024-01' → 2024-01-01 ~ 2024-01-31
177
+ const [yearStr, monthStr] = trimmed.split('-');
178
+ const year = parseInt(yearStr, 10);
179
+ const month = parseInt(monthStr, 10);
180
+ const lastDay = getLastDayOfMonth(year, month);
181
+ const monthPadded = monthStr.padStart(2, '0');
182
+ return {
183
+ $gte: `${year}-${monthPadded}-01 00:00:00`,
184
+ $lte: `${year}-${monthPadded}-${String(lastDay).padStart(2, '0')} 23:59:59`,
185
+ };
186
+ }
187
+ case 'day': {
188
+ // '2024-01-15' → 2024-01-15 00:00:00 ~ 2024-01-15 23:59:59
189
+ return {
190
+ $gte: `${trimmed} 00:00:00`,
191
+ $lte: `${trimmed} 23:59:59`,
192
+ };
193
+ }
194
+ default:
195
+ return null;
196
+ }
197
+ }
198
+ exports.expandDateToRange = expandDateToRange;