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.
- package/.qoder/skills/midway-fatcms/01-quick-start.md +231 -0
- package/.qoder/skills/midway-fatcms/02-crud-quick.md +375 -0
- package/.qoder/skills/midway-fatcms/03-crud-sharding.md +489 -0
- package/.qoder/skills/midway-fatcms/04-condition-operators.md +93 -0
- package/.qoder/skills/midway-fatcms/05-configuration.md +290 -0
- package/.qoder/skills/midway-fatcms/06-builtin-functions.md +241 -0
- package/.qoder/skills/midway-fatcms/07-examples.md +504 -0
- package/.qoder/skills/midway-fatcms/SKILL.md +96 -0
- package/README.md +9 -9
- package/dist/configuration.d.ts +10 -0
- package/dist/configuration.js +26 -0
- package/dist/controller/base/BaseApiController.d.ts +1 -2
- package/dist/controller/base/BaseApiController.js +0 -4
- package/dist/controller/gateway/DocGatewayController.js +1 -1
- package/dist/controller/helpers.controller.d.ts +6 -0
- package/dist/controller/helpers.controller.js +19 -0
- package/dist/controller/manage/FlowConfigManageApi.js +4 -2
- package/dist/controller/manage/SysConfigMangeApi.js +6 -1
- package/dist/controller/manage/UserAccountManageApi.js +7 -2
- package/dist/index.d.ts +2 -2
- package/dist/index.js +2 -2
- package/dist/libs/crud-pro/CrudPro.d.ts +51 -3
- package/dist/libs/crud-pro/CrudPro.js +111 -4
- package/dist/libs/crud-pro/exceptions.d.ts +7 -0
- package/dist/libs/crud-pro/exceptions.js +7 -0
- package/dist/libs/crud-pro/interfaces.d.ts +83 -12
- package/dist/libs/crud-pro/models/CrudResult.d.ts +116 -0
- package/dist/libs/crud-pro/models/CrudResult.js +126 -0
- package/dist/libs/crud-pro/models/RequestModel.d.ts +2 -2
- package/dist/libs/crud-pro/models/ServiceHub.d.ts +2 -0
- package/dist/libs/crud-pro/services/CrudProDataTypeConvertService.d.ts +70 -2
- package/dist/libs/crud-pro/services/CrudProDataTypeConvertService.js +205 -13
- package/dist/libs/crud-pro/services/CrudProExecuteSqlService.js +36 -2
- package/dist/libs/crud-pro/services/CrudProGenSqlCondition.js +8 -4
- package/dist/libs/crud-pro/services/CrudProTableMetaService.d.ts +36 -0
- package/dist/libs/crud-pro/services/CrudProTableMetaService.js +97 -4
- package/dist/libs/crud-pro/services/CurdProServiceHub.d.ts +2 -0
- package/dist/libs/crud-pro/services/CurdProServiceHub.js +6 -0
- package/dist/libs/crud-pro-quick/CrudProQuick.d.ts +382 -0
- package/dist/libs/crud-pro-quick/CrudProQuick.js +689 -0
- package/dist/libs/crud-pro-quick/fixSoftDelete.d.ts +30 -0
- package/dist/{service/curd → libs/crud-pro-quick}/fixSoftDelete.js +3 -6
- package/dist/libs/crud-pro-quick/index.d.ts +36 -0
- package/dist/libs/crud-pro-quick/index.js +49 -0
- package/dist/libs/crud-pro-quick/models.d.ts +33 -0
- package/dist/libs/crud-pro-quick/models.js +2 -0
- package/dist/libs/crud-sharding/ShardingBase.d.ts +78 -0
- package/dist/libs/crud-sharding/ShardingBase.js +179 -0
- package/dist/libs/crud-sharding/ShardingByCustomCrud.d.ts +35 -0
- package/dist/libs/crud-sharding/ShardingByCustomCrud.js +297 -0
- package/dist/libs/crud-sharding/ShardingByHashCrud.d.ts +38 -0
- package/dist/libs/crud-sharding/ShardingByHashCrud.js +86 -0
- package/dist/libs/crud-sharding/ShardingByKeyCrud.d.ts +39 -0
- package/dist/libs/crud-sharding/ShardingByKeyCrud.js +74 -0
- package/dist/libs/crud-sharding/ShardingByTimeCrud.d.ts +66 -0
- package/dist/libs/crud-sharding/ShardingByTimeCrud.js +524 -0
- package/dist/libs/crud-sharding/ShardingConfig.d.ts +25 -10
- package/dist/libs/crud-sharding/ShardingConfig.js +5 -5
- package/dist/libs/crud-sharding/ShardingMerger.d.ts +10 -18
- package/dist/libs/crud-sharding/ShardingMerger.js +27 -44
- package/dist/libs/crud-sharding/ShardingResult.d.ts +33 -0
- package/dist/libs/crud-sharding/ShardingResult.js +16 -0
- package/dist/libs/crud-sharding/ShardingTableCreator.d.ts +21 -4
- package/dist/libs/crud-sharding/ShardingTableCreator.js +193 -59
- package/dist/libs/crud-sharding/ShardingUtils.d.ts +48 -0
- package/dist/libs/crud-sharding/ShardingUtils.js +122 -1
- package/dist/libs/crud-sharding/TIME_COLUMN_CLEAN_SPEC.md +488 -0
- package/dist/libs/crud-sharding/index.d.ts +13 -15
- package/dist/libs/crud-sharding/index.js +33 -17
- package/dist/models/RedisKeys.d.ts +1 -0
- package/dist/models/RedisKeys.js +1 -0
- package/dist/models/bizmodels.d.ts +2 -6
- package/dist/service/SysAppService.d.ts +2 -2
- package/dist/service/SysAppService.js +16 -5
- package/dist/service/SysConfigService.d.ts +1 -1
- package/dist/service/SysConfigService.js +7 -2
- package/dist/service/SysDictDataService.js +14 -4
- package/dist/service/SysMenuService.js +7 -2
- package/dist/service/TableMetaCacheRedisSubscriber.d.ts +31 -0
- package/dist/service/TableMetaCacheRedisSubscriber.js +98 -0
- package/dist/service/curd/CurdMixService.d.ts +6 -4
- package/dist/service/curd/CurdMixService.js +16 -2
- package/dist/service/curd/CurdProService.d.ts +149 -29
- package/dist/service/curd/CurdProService.js +157 -38
- package/dist/service/flow/FlowConfigService.js +7 -2
- package/dist/service/flow/FlowInstanceCrudService.js +22 -19
- package/package.json +1 -1
- package/src/configuration.ts +27 -0
- package/src/controller/base/BaseApiController.ts +0 -5
- package/src/controller/gateway/DocGatewayController.ts +1 -1
- package/src/controller/helpers.controller.ts +15 -0
- package/src/controller/manage/CrudStandardDesignApi.ts +4 -3
- package/src/controller/manage/FlowConfigManageApi.ts +4 -2
- package/src/controller/manage/SysConfigMangeApi.ts +6 -1
- package/src/controller/manage/UserAccountManageApi.ts +7 -2
- package/src/index.ts +2 -2
- package/src/libs/crud-pro/CrudPro.ts +134 -7
- package/src/libs/crud-pro/exceptions.ts +8 -0
- package/src/libs/crud-pro/interfaces.ts +111 -15
- package/src/libs/crud-pro/models/CrudResult.ts +178 -0
- package/src/libs/crud-pro/models/RequestModel.ts +2 -2
- package/src/libs/crud-pro/models/ServiceHub.ts +4 -0
- package/src/libs/crud-pro/services/CrudProDataTypeConvertService.ts +238 -15
- package/src/libs/crud-pro/services/CrudProExecuteSqlService.ts +41 -2
- package/src/libs/crud-pro/services/CrudProGenSqlCondition.ts +11 -7
- package/src/libs/crud-pro/services/CrudProTableMetaService.ts +110 -3
- package/src/libs/crud-pro/services/CurdProServiceHub.ts +8 -0
- package/src/libs/crud-pro-quick/CrudProQuick.ts +782 -0
- package/src/{service/curd → libs/crud-pro-quick}/fixSoftDelete.ts +23 -13
- package/src/libs/crud-pro-quick/index.ts +52 -0
- package/src/libs/crud-pro-quick/models.ts +35 -0
- package/src/libs/crud-sharding/ShardingBase.ts +256 -0
- package/src/libs/crud-sharding/ShardingByCustomCrud.ts +329 -0
- package/src/libs/crud-sharding/ShardingByHashCrud.ts +111 -0
- package/src/libs/crud-sharding/ShardingByKeyCrud.ts +97 -0
- package/src/libs/crud-sharding/ShardingByTimeCrud.ts +628 -0
- package/src/libs/crud-sharding/ShardingConfig.ts +28 -10
- package/src/libs/crud-sharding/ShardingMerger.ts +35 -63
- package/src/libs/crud-sharding/ShardingResult.ts +29 -0
- package/src/libs/crud-sharding/ShardingTableCreator.ts +214 -71
- package/src/libs/crud-sharding/ShardingUtils.ts +137 -0
- package/src/libs/crud-sharding/TIME_COLUMN_CLEAN_SPEC.md +488 -0
- package/src/libs/crud-sharding/index.ts +30 -16
- package/src/models/RedisKeys.ts +1 -0
- package/src/models/bizmodels.ts +4 -7
- package/src/service/SysAppService.ts +18 -7
- package/src/service/SysConfigService.ts +8 -3
- package/src/service/SysDictDataService.ts +14 -4
- package/src/service/SysMenuService.ts +7 -2
- package/src/service/TableMetaCacheRedisSubscriber.ts +105 -0
- package/src/service/crudstd/CrudStdService.ts +2 -2
- package/src/service/curd/CurdMixService.ts +26 -5
- package/src/service/curd/CurdProService.ts +186 -45
- package/src/service/flow/FlowConfigService.ts +7 -2
- package/src/service/flow/FlowInstanceCrudService.ts +23 -20
- package/.qoder/skills/midway-fatcms-crud/SKILL.md +0 -375
- package/.qoder/skills/midway-fatcms-crud/examples.md +0 -990
- package/.qoder/skills/midway-fatcms-crud/reference.md +0 -568
- package/dist/libs/crud-pro/README.md +0 -809
- package/dist/libs/crud-pro/README_FUNC.md +0 -193
- package/dist/libs/crud-sharding/ROUTING_LOGIC.md +0 -944
- package/dist/libs/crud-sharding/ShardingCrudPro.d.ts +0 -363
- package/dist/libs/crud-sharding/ShardingCrudPro.js +0 -675
- package/dist/libs/crud-sharding/ShardingRouter.d.ts +0 -69
- package/dist/libs/crud-sharding/ShardingRouter.js +0 -377
- package/dist/models/StandardColumns.d.ts +0 -71
- package/dist/models/StandardColumns.js +0 -28
- package/dist/service/curd/CrudProQuick.d.ts +0 -190
- package/dist/service/curd/CrudProQuick.js +0 -319
- package/dist/service/curd/README.md +0 -1100
- package/dist/service/curd/fixSoftDelete.d.ts +0 -20
- package/src/libs/crud-pro/README.md +0 -809
- package/src/libs/crud-pro/README_FUNC.md +0 -193
- package/src/libs/crud-sharding/ROUTING_LOGIC.md +0 -944
- package/src/libs/crud-sharding/ShardingCrudPro.ts +0 -835
- package/src/libs/crud-sharding/ShardingRouter.ts +0 -512
- package/src/models/StandardColumns.ts +0 -76
- package/src/service/curd/CrudProQuick.ts +0 -360
- package/src/service/curd/README.md +0 -1100
|
@@ -1,512 +0,0 @@
|
|
|
1
|
-
import * as moment from 'moment';
|
|
2
|
-
import { ShardingType, IShardingConfig, IShardingRouterContext, IShardingTimeRange } from './ShardingConfig';
|
|
3
|
-
import { ShardingTimeGranularity, getTimeSuffix } from './ShardingUtils';
|
|
4
|
-
|
|
5
|
-
/**
|
|
6
|
-
* 空值合并(兼容低版本 Node.js)
|
|
7
|
-
* 返回第一个非 null/undefined 的值
|
|
8
|
-
*/
|
|
9
|
-
function coalesce(a: any, b: any): any {
|
|
10
|
-
return a !== null && a !== undefined ? a : b;
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
/**
|
|
14
|
-
* 表信息提供者接口
|
|
15
|
-
* 用于获取数据库中真实存在的表
|
|
16
|
-
*/
|
|
17
|
-
export interface ITableInfoProvider {
|
|
18
|
-
/**
|
|
19
|
-
* 获取所有真实存在的表名
|
|
20
|
-
* @returns 表名集合
|
|
21
|
-
*/
|
|
22
|
-
getExistingTables(): Promise<Set<string>>;
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
/**
|
|
26
|
-
* 分表路由器
|
|
27
|
-
*/
|
|
28
|
-
export class ShardingRouter {
|
|
29
|
-
// ============ Public 方法 ============
|
|
30
|
-
|
|
31
|
-
/**
|
|
32
|
-
* 插入类操作专用路由(从 data 提取字段)
|
|
33
|
-
*/
|
|
34
|
-
public resolveForInsert(
|
|
35
|
-
config: IShardingConfig,
|
|
36
|
-
context: IShardingRouterContext
|
|
37
|
-
): string {
|
|
38
|
-
this.validateConfig(config);
|
|
39
|
-
|
|
40
|
-
switch (config.type) {
|
|
41
|
-
case ShardingType.YEAR:
|
|
42
|
-
return this.resolveTimeForInsert(config, context, 'year');
|
|
43
|
-
case ShardingType.MONTH:
|
|
44
|
-
return this.resolveTimeForInsert(config, context, 'month');
|
|
45
|
-
case ShardingType.DAY:
|
|
46
|
-
return this.resolveTimeForInsert(config, context, 'day');
|
|
47
|
-
case ShardingType.RANGE:
|
|
48
|
-
return this.resolveRangeForInsert(config, context);
|
|
49
|
-
case ShardingType.HASH:
|
|
50
|
-
return this.resolveHashForInsert(config, context);
|
|
51
|
-
case ShardingType.CUSTOM:
|
|
52
|
-
return this.resolveCustomForInsert(config, context);
|
|
53
|
-
default:
|
|
54
|
-
return config.baseTable;
|
|
55
|
-
}
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
/**
|
|
59
|
-
* 条件类操作专用路由(从 condition 提取字段)
|
|
60
|
-
*/
|
|
61
|
-
public resolveForCondition(
|
|
62
|
-
config: IShardingConfig,
|
|
63
|
-
context: IShardingRouterContext
|
|
64
|
-
): string | string[] {
|
|
65
|
-
this.validateConfig(config);
|
|
66
|
-
|
|
67
|
-
switch (config.type) {
|
|
68
|
-
case ShardingType.YEAR:
|
|
69
|
-
return this.resolveTimeForCondition(config, context, 'year');
|
|
70
|
-
case ShardingType.MONTH:
|
|
71
|
-
return this.resolveTimeForCondition(config, context, 'month');
|
|
72
|
-
case ShardingType.DAY:
|
|
73
|
-
return this.resolveTimeForCondition(config, context, 'day');
|
|
74
|
-
case ShardingType.RANGE:
|
|
75
|
-
return this.resolveRangeForCondition(config, context);
|
|
76
|
-
case ShardingType.HASH:
|
|
77
|
-
return this.resolveHashForCondition(config, context);
|
|
78
|
-
case ShardingType.CUSTOM:
|
|
79
|
-
return this.resolveCustomForCondition(config, context);
|
|
80
|
-
default:
|
|
81
|
-
return config.baseTable;
|
|
82
|
-
}
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
/**
|
|
86
|
-
* 查询专用路由(带表存在性过滤)
|
|
87
|
-
*/
|
|
88
|
-
public async resolveQuery(
|
|
89
|
-
config: IShardingConfig,
|
|
90
|
-
context: IShardingRouterContext,
|
|
91
|
-
tableInfoProvider: ITableInfoProvider
|
|
92
|
-
): Promise<string[]> {
|
|
93
|
-
const existingTables = await tableInfoProvider.getExistingTables();
|
|
94
|
-
|
|
95
|
-
// 时间分表:从真实存在的表中取最近N张,再与条件取交集
|
|
96
|
-
if (this.isTimeSharding(config)) {
|
|
97
|
-
return this.getRecentExistingTablesFromAll(existingTables, config, context.condition);
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
// 非时间分表:先计算候选表,再过滤存在的表
|
|
101
|
-
const candidateResult = this.resolveForCondition(config, context);
|
|
102
|
-
const candidateTables = Array.isArray(candidateResult)
|
|
103
|
-
? candidateResult
|
|
104
|
-
: [candidateResult];
|
|
105
|
-
|
|
106
|
-
return this.filterExistingTables(candidateTables, existingTables);
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
/**
|
|
110
|
-
* 过滤出真实存在的分表
|
|
111
|
-
*/
|
|
112
|
-
public filterExistingTables(
|
|
113
|
-
candidateTables: string | string[],
|
|
114
|
-
existingTables: Set<string>
|
|
115
|
-
): string[] {
|
|
116
|
-
const candidates = Array.isArray(candidateTables) ? candidateTables : [candidateTables];
|
|
117
|
-
return candidates.filter(table => existingTables.has(table));
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
// ============ 配置校验 ============
|
|
121
|
-
|
|
122
|
-
private validateConfig(config: IShardingConfig): void {
|
|
123
|
-
if (!config.baseTable) {
|
|
124
|
-
throw new Error('[ShardingRouter] baseTable 不能为空');
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
if ((config.type === ShardingType.RANGE || config.type === ShardingType.HASH) && !config.shardingColumn) {
|
|
128
|
-
throw new Error(`[ShardingRouter] ${config.type} 分表必须指定 shardingColumn`);
|
|
129
|
-
}
|
|
130
|
-
|
|
131
|
-
if (config.type === ShardingType.CUSTOM && !config.customRouter) {
|
|
132
|
-
throw new Error('[ShardingRouter] CUSTOM 分表必须提供 customRouter 函数');
|
|
133
|
-
}
|
|
134
|
-
}
|
|
135
|
-
|
|
136
|
-
// ============ 时间分表路由 ============
|
|
137
|
-
|
|
138
|
-
private resolveTimeForInsert(
|
|
139
|
-
config: IShardingConfig,
|
|
140
|
-
context: IShardingRouterContext,
|
|
141
|
-
granularity: ShardingTimeGranularity
|
|
142
|
-
): string {
|
|
143
|
-
const { baseTable, timeColumn } = config;
|
|
144
|
-
const timeValue = this.extractTimeFromData(context.data, timeColumn!);
|
|
145
|
-
const suffix = getTimeSuffix(timeValue, granularity);
|
|
146
|
-
return this.formatTableName(baseTable, suffix, config);
|
|
147
|
-
}
|
|
148
|
-
|
|
149
|
-
private resolveTimeForCondition(
|
|
150
|
-
config: IShardingConfig,
|
|
151
|
-
context: IShardingRouterContext,
|
|
152
|
-
granularity: ShardingTimeGranularity
|
|
153
|
-
): string | string[] {
|
|
154
|
-
const { baseTable, timeColumn } = config;
|
|
155
|
-
const condition = context.condition || {};
|
|
156
|
-
|
|
157
|
-
const timeRange = this.extractTimeRange(condition, timeColumn!);
|
|
158
|
-
const singleTimeValue = this.tryExtractTimeFromCondition(condition, timeColumn!);
|
|
159
|
-
|
|
160
|
-
if (timeRange) {
|
|
161
|
-
return this.generateTablesInRange(baseTable, timeRange.start, timeRange.end, granularity, config);
|
|
162
|
-
}
|
|
163
|
-
|
|
164
|
-
if (singleTimeValue) {
|
|
165
|
-
const suffix = getTimeSuffix(singleTimeValue, granularity);
|
|
166
|
-
return this.formatTableName(baseTable, suffix, config);
|
|
167
|
-
}
|
|
168
|
-
|
|
169
|
-
const recentCount = this.getRecentCountByGranularity(granularity, config);
|
|
170
|
-
return this.getRecentTables(baseTable, granularity, recentCount, config);
|
|
171
|
-
}
|
|
172
|
-
|
|
173
|
-
// ============ 范围分表路由 ============
|
|
174
|
-
|
|
175
|
-
private resolveRangeForInsert(config: IShardingConfig, context: IShardingRouterContext): string {
|
|
176
|
-
const { baseTable, tableCount = 10, shardingColumn } = config;
|
|
177
|
-
const value = this.extractColumnFromData(context.data, shardingColumn);
|
|
178
|
-
const index = this.calculateRangeIndex(value, tableCount);
|
|
179
|
-
return this.formatTableName(baseTable, String(index), config);
|
|
180
|
-
}
|
|
181
|
-
|
|
182
|
-
private resolveRangeForCondition(config: IShardingConfig, context: IShardingRouterContext): string {
|
|
183
|
-
const { baseTable, tableCount = 10, shardingColumn } = config;
|
|
184
|
-
const value = this.extractColumnFromCondition(context.condition, shardingColumn);
|
|
185
|
-
const index = this.calculateRangeIndex(value, tableCount);
|
|
186
|
-
return this.formatTableName(baseTable, String(index), config);
|
|
187
|
-
}
|
|
188
|
-
|
|
189
|
-
// ============ 哈希分表路由 ============
|
|
190
|
-
|
|
191
|
-
private resolveHashForInsert(config: IShardingConfig, context: IShardingRouterContext): string {
|
|
192
|
-
const { baseTable, tableCount = 16, shardingColumn } = config;
|
|
193
|
-
const value = this.extractColumnFromData(context.data, shardingColumn);
|
|
194
|
-
const index = this.calculateHash(value, tableCount);
|
|
195
|
-
return this.formatTableName(baseTable, String(index).padStart(2, '0'), config);
|
|
196
|
-
}
|
|
197
|
-
|
|
198
|
-
private resolveHashForCondition(config: IShardingConfig, context: IShardingRouterContext): string {
|
|
199
|
-
const { baseTable, tableCount = 16, shardingColumn } = config;
|
|
200
|
-
const value = this.extractColumnFromCondition(context.condition, shardingColumn);
|
|
201
|
-
const index = this.calculateHash(value, tableCount);
|
|
202
|
-
return this.formatTableName(baseTable, String(index).padStart(2, '0'), config);
|
|
203
|
-
}
|
|
204
|
-
|
|
205
|
-
// ============ 自定义分表路由 ============
|
|
206
|
-
|
|
207
|
-
private resolveCustomForInsert(config: IShardingConfig, context: IShardingRouterContext): string {
|
|
208
|
-
const result = config.customRouter!(context);
|
|
209
|
-
return Array.isArray(result) ? result[0] : result;
|
|
210
|
-
}
|
|
211
|
-
|
|
212
|
-
private resolveCustomForCondition(config: IShardingConfig, context: IShardingRouterContext): string | string[] {
|
|
213
|
-
return config.customRouter!(context);
|
|
214
|
-
}
|
|
215
|
-
|
|
216
|
-
// ============ 查询相关私有方法 ============
|
|
217
|
-
|
|
218
|
-
private isTimeSharding(config: IShardingConfig): boolean {
|
|
219
|
-
return [ShardingType.YEAR, ShardingType.MONTH, ShardingType.DAY].includes(config.type);
|
|
220
|
-
}
|
|
221
|
-
|
|
222
|
-
private getRecentExistingTablesFromAll(
|
|
223
|
-
existingTables: Set<string>,
|
|
224
|
-
config: IShardingConfig,
|
|
225
|
-
condition?: Record<string, any>
|
|
226
|
-
): string[] {
|
|
227
|
-
const baseTablePrefix = `${config.baseTable}_`;
|
|
228
|
-
const shardingTables = Array.from(existingTables).filter(t =>
|
|
229
|
-
t.startsWith(baseTablePrefix)
|
|
230
|
-
);
|
|
231
|
-
|
|
232
|
-
if (shardingTables.length === 0) {
|
|
233
|
-
return [];
|
|
234
|
-
}
|
|
235
|
-
|
|
236
|
-
shardingTables.sort();
|
|
237
|
-
|
|
238
|
-
// 配置了 recentTableCount:取最近N张,再与条件取交集
|
|
239
|
-
if (config.recentTableCount !== undefined) {
|
|
240
|
-
const recentTables = shardingTables.slice(-config.recentTableCount);
|
|
241
|
-
return this.intersectWithCondition(recentTables, config, condition);
|
|
242
|
-
}
|
|
243
|
-
|
|
244
|
-
// 未配置 recentTableCount:所有表与条件取交集,最多取3张
|
|
245
|
-
const intersected = this.intersectWithCondition(shardingTables, config, condition);
|
|
246
|
-
return intersected.slice(0, 3);
|
|
247
|
-
}
|
|
248
|
-
|
|
249
|
-
private intersectWithCondition(
|
|
250
|
-
tables: string[],
|
|
251
|
-
config: IShardingConfig,
|
|
252
|
-
condition?: Record<string, any>
|
|
253
|
-
): string[] {
|
|
254
|
-
if (!condition) {
|
|
255
|
-
return this.sortTablesByTimeDesc(tables);
|
|
256
|
-
}
|
|
257
|
-
|
|
258
|
-
const timeColumn = config.timeColumn!;
|
|
259
|
-
const timeRange = this.extractTimeRange(condition, timeColumn);
|
|
260
|
-
if (timeRange) {
|
|
261
|
-
return this.intersectTablesByTimeRange(tables, timeRange, config);
|
|
262
|
-
}
|
|
263
|
-
|
|
264
|
-
const singleTimeValue = this.tryExtractTimeFromCondition(condition, timeColumn);
|
|
265
|
-
if (singleTimeValue) {
|
|
266
|
-
const suffix = getTimeSuffix(singleTimeValue, config.type as any);
|
|
267
|
-
const targetTable = `${config.baseTable}_${suffix}`;
|
|
268
|
-
return tables.includes(targetTable) ? [targetTable] : [];
|
|
269
|
-
}
|
|
270
|
-
|
|
271
|
-
return this.sortTablesByTimeDesc(tables);
|
|
272
|
-
}
|
|
273
|
-
|
|
274
|
-
private intersectTablesByTimeRange(
|
|
275
|
-
tables: string[],
|
|
276
|
-
timeRange: IShardingTimeRange,
|
|
277
|
-
config: IShardingConfig
|
|
278
|
-
): string[] {
|
|
279
|
-
const startSuffix = getTimeSuffix(timeRange.start, config.type as any);
|
|
280
|
-
const endSuffix = getTimeSuffix(timeRange.end, config.type as any);
|
|
281
|
-
const baseTablePrefix = `${config.baseTable}_`;
|
|
282
|
-
|
|
283
|
-
const prefixLen = baseTablePrefix.length;
|
|
284
|
-
return this.sortTablesByTimeDesc(tables.filter(table => {
|
|
285
|
-
const suffix = table.substring(prefixLen);
|
|
286
|
-
return suffix >= startSuffix && suffix <= endSuffix;
|
|
287
|
-
}));
|
|
288
|
-
}
|
|
289
|
-
|
|
290
|
-
// ============ 排序 ============
|
|
291
|
-
|
|
292
|
-
/**
|
|
293
|
-
* 将分表按时间后缀降序排列(新→旧)
|
|
294
|
-
*
|
|
295
|
-
* 分表名格式为 baseTable_suffix,后缀为时间格式(如 202403、20240101),
|
|
296
|
-
* 字典序即时间序,降序即新→旧。
|
|
297
|
-
*/
|
|
298
|
-
private sortTablesByTimeDesc(tables: string[]): string[] {
|
|
299
|
-
return [...tables].sort((a, b) => b.localeCompare(a));
|
|
300
|
-
}
|
|
301
|
-
|
|
302
|
-
// ============ 值提取:基础方法 ============
|
|
303
|
-
|
|
304
|
-
private extractFromData(data: any, column: string): any {
|
|
305
|
-
return data?.[column];
|
|
306
|
-
}
|
|
307
|
-
|
|
308
|
-
private extractFromCondition(condition: any, column: string): any {
|
|
309
|
-
return condition?.[column];
|
|
310
|
-
}
|
|
311
|
-
|
|
312
|
-
private parseTimeValue(value: any, source: string, operation: string): Date {
|
|
313
|
-
if (value === undefined || value === null) {
|
|
314
|
-
throw new Error(`[ShardingRouter] ${operation} 操作无法从 ${source} 中获取时间分表字段的值`);
|
|
315
|
-
}
|
|
316
|
-
|
|
317
|
-
const m = moment(value);
|
|
318
|
-
if (!m.isValid()) {
|
|
319
|
-
throw new Error(`[ShardingRouter] 无法解析时间分表字段的值:${value}`);
|
|
320
|
-
}
|
|
321
|
-
return m.toDate();
|
|
322
|
-
}
|
|
323
|
-
|
|
324
|
-
private parseColumnValue(value: any, source: string, operation: string): any {
|
|
325
|
-
if (value === undefined || value === null) {
|
|
326
|
-
throw new Error(`[ShardingRouter] ${operation} 操作无法从 ${source} 中获取分表字段的值`);
|
|
327
|
-
}
|
|
328
|
-
return value;
|
|
329
|
-
}
|
|
330
|
-
|
|
331
|
-
// ============ 值提取:时间字段 ============
|
|
332
|
-
|
|
333
|
-
private extractTimeFromData(data: any, column: string): Date {
|
|
334
|
-
const value = this.extractFromData(data, column);
|
|
335
|
-
return this.parseTimeValue(value, 'data', 'insert');
|
|
336
|
-
}
|
|
337
|
-
|
|
338
|
-
private tryExtractTimeFromCondition(condition: any, column: string): Date | null {
|
|
339
|
-
const value = this.extractFromCondition(condition, column);
|
|
340
|
-
if (value === undefined || value === null) {
|
|
341
|
-
return null;
|
|
342
|
-
}
|
|
343
|
-
|
|
344
|
-
const m = moment(value);
|
|
345
|
-
if (!m.isValid()) {
|
|
346
|
-
return null;
|
|
347
|
-
}
|
|
348
|
-
return m.toDate();
|
|
349
|
-
}
|
|
350
|
-
|
|
351
|
-
private extractTimeRange(condition: any, column: string): IShardingTimeRange | null {
|
|
352
|
-
const colCond = condition[column];
|
|
353
|
-
if (!colCond || typeof colCond !== 'object') return null;
|
|
354
|
-
|
|
355
|
-
const start = coalesce(colCond.$gte, colCond.$gt);
|
|
356
|
-
const end = coalesce(colCond.$lte, colCond.$lt);
|
|
357
|
-
|
|
358
|
-
if (start && end) {
|
|
359
|
-
const startMoment = moment(start);
|
|
360
|
-
const endMoment = moment(end);
|
|
361
|
-
if (!startMoment.isValid() || !endMoment.isValid()) {
|
|
362
|
-
return null;
|
|
363
|
-
}
|
|
364
|
-
return {
|
|
365
|
-
start: startMoment.toDate(),
|
|
366
|
-
end: endMoment.toDate(),
|
|
367
|
-
};
|
|
368
|
-
}
|
|
369
|
-
|
|
370
|
-
if (start) {
|
|
371
|
-
const startMoment = moment(start);
|
|
372
|
-
if (!startMoment.isValid()) {
|
|
373
|
-
return null;
|
|
374
|
-
}
|
|
375
|
-
return {
|
|
376
|
-
start: startMoment.toDate(),
|
|
377
|
-
end: new Date(),
|
|
378
|
-
};
|
|
379
|
-
}
|
|
380
|
-
|
|
381
|
-
if (end) {
|
|
382
|
-
const endMoment = moment(end);
|
|
383
|
-
if (!endMoment.isValid()) {
|
|
384
|
-
return null;
|
|
385
|
-
}
|
|
386
|
-
return {
|
|
387
|
-
start: new Date(0),
|
|
388
|
-
end: endMoment.toDate(),
|
|
389
|
-
};
|
|
390
|
-
}
|
|
391
|
-
|
|
392
|
-
return null;
|
|
393
|
-
}
|
|
394
|
-
|
|
395
|
-
// ============ 值提取:分表字段 ============
|
|
396
|
-
|
|
397
|
-
private extractColumnFromData(data: any, column: string): any {
|
|
398
|
-
const value = this.extractFromData(data, column);
|
|
399
|
-
return this.parseColumnValue(value, 'data', 'insert');
|
|
400
|
-
}
|
|
401
|
-
|
|
402
|
-
private extractColumnFromCondition(condition: any, column: string): any {
|
|
403
|
-
const value = this.extractFromCondition(condition, column);
|
|
404
|
-
return this.parseColumnValue(value, 'condition', '查询/更新/删除');
|
|
405
|
-
}
|
|
406
|
-
|
|
407
|
-
// ============ 时间处理 ============
|
|
408
|
-
|
|
409
|
-
private generateTablesInRange(
|
|
410
|
-
baseTable: string,
|
|
411
|
-
start: Date,
|
|
412
|
-
end: Date,
|
|
413
|
-
granularity: ShardingTimeGranularity,
|
|
414
|
-
config: IShardingConfig
|
|
415
|
-
): string[] {
|
|
416
|
-
const tables: string[] = [];
|
|
417
|
-
const current = moment(start);
|
|
418
|
-
const maxTables = 100;
|
|
419
|
-
|
|
420
|
-
while (current.toDate() <= end && tables.length < maxTables) {
|
|
421
|
-
const suffix = getTimeSuffix(current.toDate(), granularity);
|
|
422
|
-
tables.push(this.formatTableName(baseTable, suffix, config));
|
|
423
|
-
|
|
424
|
-
switch (granularity) {
|
|
425
|
-
case 'year':
|
|
426
|
-
current.add(1, 'year');
|
|
427
|
-
break;
|
|
428
|
-
case 'month':
|
|
429
|
-
current.add(1, 'month');
|
|
430
|
-
break;
|
|
431
|
-
case 'day':
|
|
432
|
-
current.add(1, 'day');
|
|
433
|
-
break;
|
|
434
|
-
}
|
|
435
|
-
}
|
|
436
|
-
|
|
437
|
-
return tables;
|
|
438
|
-
}
|
|
439
|
-
|
|
440
|
-
private getRecentTables(
|
|
441
|
-
baseTable: string,
|
|
442
|
-
granularity: ShardingTimeGranularity,
|
|
443
|
-
count: number,
|
|
444
|
-
config: IShardingConfig
|
|
445
|
-
): string[] {
|
|
446
|
-
const tables: string[] = [];
|
|
447
|
-
const current = moment();
|
|
448
|
-
|
|
449
|
-
for (let i = 0; i < count; i++) {
|
|
450
|
-
const suffix = getTimeSuffix(current.toDate(), granularity);
|
|
451
|
-
tables.unshift(this.formatTableName(baseTable, suffix, config));
|
|
452
|
-
|
|
453
|
-
switch (granularity) {
|
|
454
|
-
case 'year':
|
|
455
|
-
current.subtract(1, 'year');
|
|
456
|
-
break;
|
|
457
|
-
case 'month':
|
|
458
|
-
current.subtract(1, 'month');
|
|
459
|
-
break;
|
|
460
|
-
case 'day':
|
|
461
|
-
current.subtract(1, 'day');
|
|
462
|
-
break;
|
|
463
|
-
}
|
|
464
|
-
}
|
|
465
|
-
|
|
466
|
-
return tables;
|
|
467
|
-
}
|
|
468
|
-
|
|
469
|
-
private getRecentCountByGranularity(granularity: ShardingTimeGranularity, config: IShardingConfig): number {
|
|
470
|
-
if (config.recentTableCount !== undefined && config.recentTableCount > 0) {
|
|
471
|
-
return config.recentTableCount;
|
|
472
|
-
}
|
|
473
|
-
|
|
474
|
-
switch (granularity) {
|
|
475
|
-
case 'year':
|
|
476
|
-
return 3;
|
|
477
|
-
case 'month':
|
|
478
|
-
return 12;
|
|
479
|
-
case 'day':
|
|
480
|
-
return 7;
|
|
481
|
-
}
|
|
482
|
-
}
|
|
483
|
-
|
|
484
|
-
// ============ 计算相关 ============
|
|
485
|
-
|
|
486
|
-
private calculateRangeIndex(value: any, count: number): number {
|
|
487
|
-
if (typeof value === 'number') {
|
|
488
|
-
return Math.abs(value) % count;
|
|
489
|
-
}
|
|
490
|
-
return Math.abs(this.simpleHash(String(value))) % count;
|
|
491
|
-
}
|
|
492
|
-
|
|
493
|
-
private calculateHash(value: any, count: number): number {
|
|
494
|
-
return Math.abs(this.simpleHash(String(value))) % count;
|
|
495
|
-
}
|
|
496
|
-
|
|
497
|
-
private simpleHash(str: string): number {
|
|
498
|
-
let hash = 5381;
|
|
499
|
-
for (let i = 0; i < str.length; i++) {
|
|
500
|
-
hash = ((hash << 5) + hash) + str.charCodeAt(i);
|
|
501
|
-
hash = hash & hash;
|
|
502
|
-
}
|
|
503
|
-
return hash;
|
|
504
|
-
}
|
|
505
|
-
|
|
506
|
-
// ============ 格式化 ============
|
|
507
|
-
|
|
508
|
-
private formatTableName(baseTable: string, suffix: string, config: IShardingConfig): string {
|
|
509
|
-
const formattedSuffix = config.suffixFormatter ? config.suffixFormatter(suffix) : suffix;
|
|
510
|
-
return `${baseTable}_${formattedSuffix}`;
|
|
511
|
-
}
|
|
512
|
-
}
|
|
@@ -1,76 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* 标准表结构字段类型定义
|
|
3
|
-
*
|
|
4
|
-
* 常用标准字段(推荐所有表都包含):
|
|
5
|
-
* - deleted_at: 软删除标记,0=未删除,时间戳=已删除
|
|
6
|
-
* - deleted_by: 删除人ID
|
|
7
|
-
* - created_by: 创建人ID
|
|
8
|
-
* - created_at: 创建时间
|
|
9
|
-
* - modified_by: 最后修改人ID
|
|
10
|
-
* - modified_at: 最后修改时间
|
|
11
|
-
*
|
|
12
|
-
* 完整标准字段(需要记录详细用户信息时使用):
|
|
13
|
-
* - created_account_type: 创建人账号类型
|
|
14
|
-
* - created_avatar: 创建人头像
|
|
15
|
-
* - created_nickname: 创建人昵称
|
|
16
|
-
* - modified_account_type: 修改人账号类型
|
|
17
|
-
* - modified_avatar: 修改人头像
|
|
18
|
-
* - modified_nickname: 修改人昵称
|
|
19
|
-
*/
|
|
20
|
-
|
|
21
|
-
/**
|
|
22
|
-
* 常用标准表结构字段(6个字段)
|
|
23
|
-
*
|
|
24
|
-
* 推荐所有业务表都包含这些字段:
|
|
25
|
-
* - deleted_at: 删除时间戳,0=未删除
|
|
26
|
-
* - deleted_by: 删除人ID
|
|
27
|
-
* - created_by: 创建人ID
|
|
28
|
-
* - created_at: 创建时间(毫秒时间戳)
|
|
29
|
-
* - modified_by: 修改人ID
|
|
30
|
-
* - modified_at: 修改时间(毫秒时间戳)
|
|
31
|
-
*/
|
|
32
|
-
export interface ICommonStandardColumns {
|
|
33
|
-
deleted_at?: number;
|
|
34
|
-
deleted_by?: string;
|
|
35
|
-
created_by?: string;
|
|
36
|
-
created_at?: number;
|
|
37
|
-
modified_by?: string;
|
|
38
|
-
modified_at?: number;
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
/**
|
|
42
|
-
* 完整标准表结构字段(12个字段)
|
|
43
|
-
*
|
|
44
|
-
* 在常用字段基础上增加用户详细信息:
|
|
45
|
-
* - created_account_type: 创建人账号类型
|
|
46
|
-
* - created_avatar: 创建人头像URL
|
|
47
|
-
* - created_nickname: 创建人昵称
|
|
48
|
-
* - modified_account_type: 修改人账号类型
|
|
49
|
-
* - modified_avatar: 修改人头像URL
|
|
50
|
-
* - modified_nickname: 修改人昵称
|
|
51
|
-
*/
|
|
52
|
-
export interface IFullStandardColumns extends ICommonStandardColumns {
|
|
53
|
-
created_account_type?: string;
|
|
54
|
-
created_avatar?: string;
|
|
55
|
-
created_nickname?: string;
|
|
56
|
-
modified_account_type?: string;
|
|
57
|
-
modified_avatar?: string;
|
|
58
|
-
modified_nickname?: string;
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
/**
|
|
62
|
-
* 包含常用标准字段的数据模型
|
|
63
|
-
*/
|
|
64
|
-
export type StandardDataModel<T = {}> = T & ICommonStandardColumns;
|
|
65
|
-
|
|
66
|
-
/**
|
|
67
|
-
* 包含完整标准字段的数据模型
|
|
68
|
-
*/
|
|
69
|
-
export type FullStandardDataModel<T = {}> = T & IFullStandardColumns;
|
|
70
|
-
|
|
71
|
-
/**
|
|
72
|
-
* 未删除查询条件
|
|
73
|
-
*/
|
|
74
|
-
export const NotDeletedCondition: { deleted_at: 0 } = {
|
|
75
|
-
deleted_at: 0,
|
|
76
|
-
};
|