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.
- package/.qoder/skills/midway-fatcms/02-crud-quick.md +38 -0
- package/.qoder/skills/midway-fatcms/03-crud-sharding.md +37 -36
- package/.qoder/skills/midway-fatcms/07-examples.md +4 -0
- package/dist/configuration.d.ts +10 -0
- package/dist/configuration.js +26 -0
- package/dist/controller/helpers.controller.d.ts +6 -0
- package/dist/controller/helpers.controller.js +19 -0
- package/dist/libs/crud-pro/CrudPro.d.ts +29 -2
- package/dist/libs/crud-pro/CrudPro.js +58 -2
- 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 +1 -0
- package/dist/libs/crud-pro/models/CrudResult.d.ts +3 -2
- package/dist/libs/crud-pro/models/CrudResult.js +1 -1
- 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/CrudProTableMetaService.d.ts +36 -0
- package/dist/libs/crud-pro/services/CrudProTableMetaService.js +97 -3
- 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 +93 -6
- package/dist/libs/crud-pro-quick/CrudProQuick.js +192 -32
- 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 +10 -8
- package/dist/libs/crud-sharding/ShardingConfig.js +3 -3
- package/dist/libs/crud-sharding/TIME_COLUMN_CLEAN_SPEC.md +1 -1
- package/dist/libs/crud-sharding/index.d.ts +10 -13
- package/dist/libs/crud-sharding/index.js +21 -17
- package/dist/models/RedisKeys.d.ts +1 -0
- package/dist/models/RedisKeys.js +1 -0
- package/dist/service/TableMetaCacheRedisSubscriber.d.ts +31 -0
- package/dist/service/TableMetaCacheRedisSubscriber.js +98 -0
- package/dist/service/curd/CurdMixService.d.ts +2 -2
- package/dist/service/curd/CurdProService.d.ts +109 -5
- package/dist/service/curd/CurdProService.js +127 -7
- package/package.json +1 -1
- package/src/configuration.ts +27 -0
- package/src/controller/helpers.controller.ts +15 -0
- package/src/libs/crud-pro/CrudPro.ts +73 -4
- package/src/libs/crud-pro/exceptions.ts +8 -0
- package/src/libs/crud-pro/interfaces.ts +1 -0
- package/src/libs/crud-pro/models/CrudResult.ts +5 -5
- 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/CrudProTableMetaService.ts +110 -2
- package/src/libs/crud-pro/services/CurdProServiceHub.ts +8 -0
- package/src/libs/crud-pro-quick/CrudProQuick.ts +234 -46
- 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 +10 -8
- package/src/libs/crud-sharding/TIME_COLUMN_CLEAN_SPEC.md +1 -1
- package/src/libs/crud-sharding/index.ts +17 -14
- package/src/models/RedisKeys.ts +1 -0
- package/src/service/TableMetaCacheRedisSubscriber.ts +105 -0
- package/src/service/curd/CurdMixService.ts +2 -2
- package/src/service/curd/CurdProService.ts +131 -9
- package/dist/libs/crud-sharding/ShardingCrudPro.d.ts +0 -208
- package/dist/libs/crud-sharding/ShardingCrudPro.js +0 -879
- package/dist/libs/crud-sharding/ShardingRouter.d.ts +0 -70
- package/dist/libs/crud-sharding/ShardingRouter.js +0 -396
- package/src/libs/crud-sharding/ShardingCrudPro.ts +0 -1105
- package/src/libs/crud-sharding/ShardingRouter.ts +0 -533
|
@@ -1,70 +0,0 @@
|
|
|
1
|
-
import { IShardingConfig, IShardingRouterContext } from './ShardingConfig';
|
|
2
|
-
/**
|
|
3
|
-
* 表信息提供者接口
|
|
4
|
-
* 用于获取数据库中真实存在的表
|
|
5
|
-
*/
|
|
6
|
-
export interface ITableInfoProvider {
|
|
7
|
-
/**
|
|
8
|
-
* 获取所有真实存在的表名
|
|
9
|
-
* @returns 表名集合
|
|
10
|
-
*/
|
|
11
|
-
getExistingTables(): Promise<Set<string>>;
|
|
12
|
-
}
|
|
13
|
-
/**
|
|
14
|
-
* 分表路由器
|
|
15
|
-
*/
|
|
16
|
-
export declare class ShardingRouter {
|
|
17
|
-
/**
|
|
18
|
-
* 插入类操作专用路由(从 data 提取字段)
|
|
19
|
-
*/
|
|
20
|
-
resolveForInsert(config: IShardingConfig, context: IShardingRouterContext): string;
|
|
21
|
-
/**
|
|
22
|
-
* 条件类操作专用路由(从 condition 提取字段)
|
|
23
|
-
*/
|
|
24
|
-
resolveForCondition(config: IShardingConfig, context: IShardingRouterContext): string | string[];
|
|
25
|
-
/**
|
|
26
|
-
* 查询专用路由(带表存在性过滤)
|
|
27
|
-
*/
|
|
28
|
-
resolveQuery(config: IShardingConfig, context: IShardingRouterContext, tableInfoProvider: ITableInfoProvider): Promise<string[]>;
|
|
29
|
-
/**
|
|
30
|
-
* 过滤出真实存在的分表
|
|
31
|
-
*/
|
|
32
|
-
filterExistingTables(candidateTables: string | string[], existingTables: Set<string>): string[];
|
|
33
|
-
private validateConfig;
|
|
34
|
-
private resolveTimeForInsert;
|
|
35
|
-
private resolveTimeForCondition;
|
|
36
|
-
private resolveRangeForInsert;
|
|
37
|
-
private resolveRangeForCondition;
|
|
38
|
-
private resolveHashForInsert;
|
|
39
|
-
private resolveHashForCondition;
|
|
40
|
-
private resolveCustomForInsert;
|
|
41
|
-
private resolveCustomForCondition;
|
|
42
|
-
private isTimeSharding;
|
|
43
|
-
private getRecentExistingTablesFromAll;
|
|
44
|
-
private intersectWithCondition;
|
|
45
|
-
private intersectTablesByTimeRange;
|
|
46
|
-
/**
|
|
47
|
-
* 将分表按时间后缀降序排列(新→旧)
|
|
48
|
-
*
|
|
49
|
-
* 分表名格式为 baseTable_suffix,后缀为时间格式(如 202403、20240101),
|
|
50
|
-
* 字典序即时间序,降序即新→旧。
|
|
51
|
-
*/
|
|
52
|
-
private sortTablesByTimeDesc;
|
|
53
|
-
private extractFromData;
|
|
54
|
-
private extractFromCondition;
|
|
55
|
-
private parseTimeValue;
|
|
56
|
-
private parseColumnValue;
|
|
57
|
-
private extractTimeFromData;
|
|
58
|
-
private tryExtractTimeFromCondition;
|
|
59
|
-
private extractTimeRange;
|
|
60
|
-
private extractColumnFromData;
|
|
61
|
-
private extractColumnFromCondition;
|
|
62
|
-
private generateTablesInRange;
|
|
63
|
-
private getRecentTables;
|
|
64
|
-
private getRecentCountByGranularity;
|
|
65
|
-
private getDefaultRecentCount;
|
|
66
|
-
private calculateRangeIndex;
|
|
67
|
-
private calculateHash;
|
|
68
|
-
private simpleHash;
|
|
69
|
-
private formatTableName;
|
|
70
|
-
}
|
|
@@ -1,396 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.ShardingRouter = void 0;
|
|
4
|
-
const moment = require("moment");
|
|
5
|
-
const ShardingConfig_1 = require("./ShardingConfig");
|
|
6
|
-
const ShardingUtils_1 = require("./ShardingUtils");
|
|
7
|
-
/**
|
|
8
|
-
* 空值合并(兼容低版本 Node.js)
|
|
9
|
-
* 返回第一个非 null/undefined 的值
|
|
10
|
-
*/
|
|
11
|
-
function coalesce(a, b) {
|
|
12
|
-
return a !== null && a !== undefined ? a : b;
|
|
13
|
-
}
|
|
14
|
-
/**
|
|
15
|
-
* 分表路由器
|
|
16
|
-
*/
|
|
17
|
-
class ShardingRouter {
|
|
18
|
-
// ============ Public 方法 ============
|
|
19
|
-
/**
|
|
20
|
-
* 插入类操作专用路由(从 data 提取字段)
|
|
21
|
-
*/
|
|
22
|
-
resolveForInsert(config, context) {
|
|
23
|
-
this.validateConfig(config);
|
|
24
|
-
switch (config.type) {
|
|
25
|
-
case ShardingConfig_1.ShardingType.YEAR:
|
|
26
|
-
return this.resolveTimeForInsert(config, context, 'year');
|
|
27
|
-
case ShardingConfig_1.ShardingType.MONTH:
|
|
28
|
-
return this.resolveTimeForInsert(config, context, 'month');
|
|
29
|
-
case ShardingConfig_1.ShardingType.DAY:
|
|
30
|
-
return this.resolveTimeForInsert(config, context, 'day');
|
|
31
|
-
case ShardingConfig_1.ShardingType.RANGE:
|
|
32
|
-
return this.resolveRangeForInsert(config, context);
|
|
33
|
-
case ShardingConfig_1.ShardingType.HASH:
|
|
34
|
-
return this.resolveHashForInsert(config, context);
|
|
35
|
-
case ShardingConfig_1.ShardingType.CUSTOM:
|
|
36
|
-
return this.resolveCustomForInsert(config, context);
|
|
37
|
-
default:
|
|
38
|
-
return config.baseTable;
|
|
39
|
-
}
|
|
40
|
-
}
|
|
41
|
-
/**
|
|
42
|
-
* 条件类操作专用路由(从 condition 提取字段)
|
|
43
|
-
*/
|
|
44
|
-
resolveForCondition(config, context) {
|
|
45
|
-
this.validateConfig(config);
|
|
46
|
-
switch (config.type) {
|
|
47
|
-
case ShardingConfig_1.ShardingType.YEAR:
|
|
48
|
-
return this.resolveTimeForCondition(config, context, 'year');
|
|
49
|
-
case ShardingConfig_1.ShardingType.MONTH:
|
|
50
|
-
return this.resolveTimeForCondition(config, context, 'month');
|
|
51
|
-
case ShardingConfig_1.ShardingType.DAY:
|
|
52
|
-
return this.resolveTimeForCondition(config, context, 'day');
|
|
53
|
-
case ShardingConfig_1.ShardingType.RANGE:
|
|
54
|
-
return this.resolveRangeForCondition(config, context);
|
|
55
|
-
case ShardingConfig_1.ShardingType.HASH:
|
|
56
|
-
return this.resolveHashForCondition(config, context);
|
|
57
|
-
case ShardingConfig_1.ShardingType.CUSTOM:
|
|
58
|
-
return this.resolveCustomForCondition(config, context);
|
|
59
|
-
default:
|
|
60
|
-
return config.baseTable;
|
|
61
|
-
}
|
|
62
|
-
}
|
|
63
|
-
/**
|
|
64
|
-
* 查询专用路由(带表存在性过滤)
|
|
65
|
-
*/
|
|
66
|
-
async resolveQuery(config, context, tableInfoProvider) {
|
|
67
|
-
const existingTables = await tableInfoProvider.getExistingTables();
|
|
68
|
-
// 时间分表:从真实存在的表中取最近N张,再与条件取交集
|
|
69
|
-
if (this.isTimeSharding(config)) {
|
|
70
|
-
return this.getRecentExistingTablesFromAll(existingTables, config, context.condition);
|
|
71
|
-
}
|
|
72
|
-
// 非时间分表:先计算候选表,再过滤存在的表
|
|
73
|
-
const candidateResult = this.resolveForCondition(config, context);
|
|
74
|
-
const candidateTables = Array.isArray(candidateResult)
|
|
75
|
-
? candidateResult
|
|
76
|
-
: [candidateResult];
|
|
77
|
-
return this.filterExistingTables(candidateTables, existingTables);
|
|
78
|
-
}
|
|
79
|
-
/**
|
|
80
|
-
* 过滤出真实存在的分表
|
|
81
|
-
*/
|
|
82
|
-
filterExistingTables(candidateTables, existingTables) {
|
|
83
|
-
const candidates = Array.isArray(candidateTables) ? candidateTables : [candidateTables];
|
|
84
|
-
return candidates.filter(table => existingTables.has(table));
|
|
85
|
-
}
|
|
86
|
-
// ============ 配置校验 ============
|
|
87
|
-
validateConfig(config) {
|
|
88
|
-
if (!config.baseTable) {
|
|
89
|
-
throw new Error('[ShardingRouter] baseTable 不能为空');
|
|
90
|
-
}
|
|
91
|
-
if ((config.type === ShardingConfig_1.ShardingType.RANGE || config.type === ShardingConfig_1.ShardingType.HASH) && !config.shardingColumn) {
|
|
92
|
-
throw new Error(`[ShardingRouter] ${config.type} 分表必须指定 shardingColumn`);
|
|
93
|
-
}
|
|
94
|
-
if (config.type === ShardingConfig_1.ShardingType.CUSTOM && !config.customRouter) {
|
|
95
|
-
throw new Error('[ShardingRouter] CUSTOM 分表必须提供 customRouter 函数');
|
|
96
|
-
}
|
|
97
|
-
}
|
|
98
|
-
// ============ 时间分表路由 ============
|
|
99
|
-
resolveTimeForInsert(config, context, granularity) {
|
|
100
|
-
const { baseTable, timeColumn } = config;
|
|
101
|
-
const timeValue = this.extractTimeFromData(context.data, timeColumn);
|
|
102
|
-
const suffix = (0, ShardingUtils_1.getTimeSuffix)(timeValue, granularity);
|
|
103
|
-
return this.formatTableName(baseTable, suffix, config);
|
|
104
|
-
}
|
|
105
|
-
resolveTimeForCondition(config, context, granularity) {
|
|
106
|
-
const { baseTable, timeColumn } = config;
|
|
107
|
-
const condition = context.condition || {};
|
|
108
|
-
const timeRange = this.extractTimeRange(condition, timeColumn);
|
|
109
|
-
const singleTimeValue = this.tryExtractTimeFromCondition(condition, timeColumn);
|
|
110
|
-
if (timeRange) {
|
|
111
|
-
return this.generateTablesInRange(baseTable, timeRange.start, timeRange.end, granularity, config);
|
|
112
|
-
}
|
|
113
|
-
if (singleTimeValue) {
|
|
114
|
-
const suffix = (0, ShardingUtils_1.getTimeSuffix)(singleTimeValue, granularity);
|
|
115
|
-
return this.formatTableName(baseTable, suffix, config);
|
|
116
|
-
}
|
|
117
|
-
// 时间分表写操作必须包含时间字段
|
|
118
|
-
// 但查询操作允许回退到 recentTableCount
|
|
119
|
-
const recentCount = this.getRecentCountByGranularity(granularity, config);
|
|
120
|
-
return this.getRecentTables(baseTable, granularity, recentCount, config);
|
|
121
|
-
}
|
|
122
|
-
// ============ 范围分表路由 ============
|
|
123
|
-
resolveRangeForInsert(config, context) {
|
|
124
|
-
const { baseTable, tableCount = 10, shardingColumn } = config;
|
|
125
|
-
const value = this.extractColumnFromData(context.data, shardingColumn);
|
|
126
|
-
const index = this.calculateRangeIndex(value, tableCount);
|
|
127
|
-
return this.formatTableName(baseTable, String(index), config);
|
|
128
|
-
}
|
|
129
|
-
resolveRangeForCondition(config, context) {
|
|
130
|
-
const { baseTable, tableCount = 10, shardingColumn } = config;
|
|
131
|
-
const value = this.extractColumnFromCondition(context.condition, shardingColumn);
|
|
132
|
-
const index = this.calculateRangeIndex(value, tableCount);
|
|
133
|
-
return this.formatTableName(baseTable, String(index), config);
|
|
134
|
-
}
|
|
135
|
-
// ============ 哈希分表路由 ============
|
|
136
|
-
resolveHashForInsert(config, context) {
|
|
137
|
-
const { baseTable, tableCount = 16, shardingColumn } = config;
|
|
138
|
-
const value = this.extractColumnFromData(context.data, shardingColumn);
|
|
139
|
-
const index = this.calculateHash(value, tableCount);
|
|
140
|
-
return this.formatTableName(baseTable, String(index).padStart(2, '0'), config);
|
|
141
|
-
}
|
|
142
|
-
resolveHashForCondition(config, context) {
|
|
143
|
-
const { baseTable, tableCount = 16, shardingColumn } = config;
|
|
144
|
-
const value = this.extractColumnFromCondition(context.condition, shardingColumn);
|
|
145
|
-
const index = this.calculateHash(value, tableCount);
|
|
146
|
-
return this.formatTableName(baseTable, String(index).padStart(2, '0'), config);
|
|
147
|
-
}
|
|
148
|
-
// ============ 自定义分表路由 ============
|
|
149
|
-
resolveCustomForInsert(config, context) {
|
|
150
|
-
const result = config.customRouter(context);
|
|
151
|
-
return Array.isArray(result) ? result[0] : result;
|
|
152
|
-
}
|
|
153
|
-
resolveCustomForCondition(config, context) {
|
|
154
|
-
return config.customRouter(context);
|
|
155
|
-
}
|
|
156
|
-
// ============ 查询相关私有方法 ============
|
|
157
|
-
isTimeSharding(config) {
|
|
158
|
-
return [ShardingConfig_1.ShardingType.YEAR, ShardingConfig_1.ShardingType.MONTH, ShardingConfig_1.ShardingType.DAY].includes(config.type);
|
|
159
|
-
}
|
|
160
|
-
getRecentExistingTablesFromAll(existingTables, config, condition) {
|
|
161
|
-
const baseTablePrefix = `${config.baseTable}_`;
|
|
162
|
-
const shardingTables = Array.from(existingTables).filter(t => t.startsWith(baseTablePrefix));
|
|
163
|
-
if (shardingTables.length === 0) {
|
|
164
|
-
return [];
|
|
165
|
-
}
|
|
166
|
-
shardingTables.sort();
|
|
167
|
-
// 配置了 recentTableCount:取最近N张,再与条件取交集
|
|
168
|
-
if (config.recentTableCount !== undefined) {
|
|
169
|
-
const recentTables = shardingTables.slice(-config.recentTableCount);
|
|
170
|
-
return this.intersectWithCondition(recentTables, config, condition);
|
|
171
|
-
}
|
|
172
|
-
// 未配置 recentTableCount:所有表与条件取交集,最多取默认数量
|
|
173
|
-
const intersected = this.intersectWithCondition(shardingTables, config, condition);
|
|
174
|
-
const defaultCount = this.getDefaultRecentCount(config);
|
|
175
|
-
return intersected.slice(0, defaultCount);
|
|
176
|
-
}
|
|
177
|
-
intersectWithCondition(tables, config, condition) {
|
|
178
|
-
if (!condition) {
|
|
179
|
-
return this.sortTablesByTimeDesc(tables);
|
|
180
|
-
}
|
|
181
|
-
const timeColumn = config.timeColumn;
|
|
182
|
-
const timeRange = this.extractTimeRange(condition, timeColumn);
|
|
183
|
-
if (timeRange) {
|
|
184
|
-
return this.intersectTablesByTimeRange(tables, timeRange, config);
|
|
185
|
-
}
|
|
186
|
-
const singleTimeValue = this.tryExtractTimeFromCondition(condition, timeColumn);
|
|
187
|
-
if (singleTimeValue) {
|
|
188
|
-
const suffix = (0, ShardingUtils_1.getTimeSuffix)(singleTimeValue, config.type);
|
|
189
|
-
const targetTable = `${config.baseTable}_${suffix}`;
|
|
190
|
-
return tables.includes(targetTable) ? [targetTable] : [];
|
|
191
|
-
}
|
|
192
|
-
// condition 存在但不包含时间字段,返回所有表(按时间降序)
|
|
193
|
-
return this.sortTablesByTimeDesc(tables);
|
|
194
|
-
}
|
|
195
|
-
intersectTablesByTimeRange(tables, timeRange, config) {
|
|
196
|
-
const startSuffix = (0, ShardingUtils_1.getTimeSuffix)(timeRange.start, config.type);
|
|
197
|
-
const endSuffix = (0, ShardingUtils_1.getTimeSuffix)(timeRange.end, config.type);
|
|
198
|
-
const baseTablePrefix = `${config.baseTable}_`;
|
|
199
|
-
const prefixLen = baseTablePrefix.length;
|
|
200
|
-
return this.sortTablesByTimeDesc(tables.filter(table => {
|
|
201
|
-
const suffix = table.substring(prefixLen);
|
|
202
|
-
return suffix >= startSuffix && suffix <= endSuffix;
|
|
203
|
-
}));
|
|
204
|
-
}
|
|
205
|
-
// ============ 排序 ============
|
|
206
|
-
/**
|
|
207
|
-
* 将分表按时间后缀降序排列(新→旧)
|
|
208
|
-
*
|
|
209
|
-
* 分表名格式为 baseTable_suffix,后缀为时间格式(如 202403、20240101),
|
|
210
|
-
* 字典序即时间序,降序即新→旧。
|
|
211
|
-
*/
|
|
212
|
-
sortTablesByTimeDesc(tables) {
|
|
213
|
-
return [...tables].sort((a, b) => b.localeCompare(a));
|
|
214
|
-
}
|
|
215
|
-
// ============ 值提取:基础方法 ============
|
|
216
|
-
extractFromData(data, column) {
|
|
217
|
-
return data === null || data === void 0 ? void 0 : data[column];
|
|
218
|
-
}
|
|
219
|
-
extractFromCondition(condition, column) {
|
|
220
|
-
return condition === null || condition === void 0 ? void 0 : condition[column];
|
|
221
|
-
}
|
|
222
|
-
parseTimeValue(value, source, operation) {
|
|
223
|
-
if (value === undefined || value === null) {
|
|
224
|
-
throw new Error(`[ShardingRouter] ${operation} 操作无法从 ${source} 中获取时间分表字段的值`);
|
|
225
|
-
}
|
|
226
|
-
const m = moment(value);
|
|
227
|
-
if (!m.isValid()) {
|
|
228
|
-
throw new Error(`[ShardingRouter] 无法解析时间分表字段的值:${value}`);
|
|
229
|
-
}
|
|
230
|
-
return m.toDate();
|
|
231
|
-
}
|
|
232
|
-
parseColumnValue(value, source, operation) {
|
|
233
|
-
if (value === undefined || value === null) {
|
|
234
|
-
throw new Error(`[ShardingRouter] ${operation} 操作无法从 ${source} 中获取分表字段的值`);
|
|
235
|
-
}
|
|
236
|
-
return value;
|
|
237
|
-
}
|
|
238
|
-
// ============ 值提取:时间字段 ============
|
|
239
|
-
extractTimeFromData(data, column) {
|
|
240
|
-
const value = this.extractFromData(data, column);
|
|
241
|
-
return this.parseTimeValue(value, 'data', 'insert');
|
|
242
|
-
}
|
|
243
|
-
tryExtractTimeFromCondition(condition, column) {
|
|
244
|
-
const value = this.extractFromCondition(condition, column);
|
|
245
|
-
if (value === undefined || value === null) {
|
|
246
|
-
return null;
|
|
247
|
-
}
|
|
248
|
-
const m = moment(value);
|
|
249
|
-
if (!m.isValid()) {
|
|
250
|
-
return null;
|
|
251
|
-
}
|
|
252
|
-
return m.toDate();
|
|
253
|
-
}
|
|
254
|
-
extractTimeRange(condition, column) {
|
|
255
|
-
const colCond = condition[column];
|
|
256
|
-
if (!colCond || typeof colCond !== 'object')
|
|
257
|
-
return null;
|
|
258
|
-
const start = coalesce(colCond.$gte, colCond.$gt);
|
|
259
|
-
const end = coalesce(colCond.$lte, colCond.$lt);
|
|
260
|
-
if (start && end) {
|
|
261
|
-
const startMoment = moment(start);
|
|
262
|
-
const endMoment = moment(end);
|
|
263
|
-
if (!startMoment.isValid() || !endMoment.isValid()) {
|
|
264
|
-
return null;
|
|
265
|
-
}
|
|
266
|
-
return {
|
|
267
|
-
start: startMoment.toDate(),
|
|
268
|
-
end: endMoment.toDate(),
|
|
269
|
-
};
|
|
270
|
-
}
|
|
271
|
-
if (start) {
|
|
272
|
-
const startMoment = moment(start);
|
|
273
|
-
if (!startMoment.isValid()) {
|
|
274
|
-
return null;
|
|
275
|
-
}
|
|
276
|
-
return {
|
|
277
|
-
start: startMoment.toDate(),
|
|
278
|
-
end: new Date(),
|
|
279
|
-
};
|
|
280
|
-
}
|
|
281
|
-
if (end) {
|
|
282
|
-
const endMoment = moment(end);
|
|
283
|
-
if (!endMoment.isValid()) {
|
|
284
|
-
return null;
|
|
285
|
-
}
|
|
286
|
-
return {
|
|
287
|
-
start: new Date(0),
|
|
288
|
-
end: endMoment.toDate(),
|
|
289
|
-
};
|
|
290
|
-
}
|
|
291
|
-
return null;
|
|
292
|
-
}
|
|
293
|
-
// ============ 值提取:分表字段 ============
|
|
294
|
-
extractColumnFromData(data, column) {
|
|
295
|
-
const value = this.extractFromData(data, column);
|
|
296
|
-
return this.parseColumnValue(value, 'data', 'insert');
|
|
297
|
-
}
|
|
298
|
-
extractColumnFromCondition(condition, column) {
|
|
299
|
-
const value = this.extractFromCondition(condition, column);
|
|
300
|
-
return this.parseColumnValue(value, 'condition', '查询/更新/删除');
|
|
301
|
-
}
|
|
302
|
-
// ============ 时间处理 ============
|
|
303
|
-
generateTablesInRange(baseTable, start, end, granularity, config) {
|
|
304
|
-
const tables = [];
|
|
305
|
-
const current = moment(start);
|
|
306
|
-
const maxTables = 100;
|
|
307
|
-
while (current.toDate() <= end && tables.length < maxTables) {
|
|
308
|
-
const suffix = (0, ShardingUtils_1.getTimeSuffix)(current.toDate(), granularity);
|
|
309
|
-
tables.push(this.formatTableName(baseTable, suffix, config));
|
|
310
|
-
switch (granularity) {
|
|
311
|
-
case 'year':
|
|
312
|
-
current.add(1, 'year');
|
|
313
|
-
break;
|
|
314
|
-
case 'month':
|
|
315
|
-
current.add(1, 'month');
|
|
316
|
-
break;
|
|
317
|
-
case 'day':
|
|
318
|
-
current.add(1, 'day');
|
|
319
|
-
break;
|
|
320
|
-
}
|
|
321
|
-
}
|
|
322
|
-
return tables;
|
|
323
|
-
}
|
|
324
|
-
// ============ 格式化 ============
|
|
325
|
-
getRecentTables(baseTable, granularity, count, config) {
|
|
326
|
-
const tables = [];
|
|
327
|
-
const current = moment();
|
|
328
|
-
for (let i = 0; i < count; i++) {
|
|
329
|
-
const suffix = (0, ShardingUtils_1.getTimeSuffix)(current.toDate(), granularity);
|
|
330
|
-
tables.unshift(this.formatTableName(baseTable, suffix, config));
|
|
331
|
-
switch (granularity) {
|
|
332
|
-
case 'year':
|
|
333
|
-
current.subtract(1, 'year');
|
|
334
|
-
break;
|
|
335
|
-
case 'month':
|
|
336
|
-
current.subtract(1, 'month');
|
|
337
|
-
break;
|
|
338
|
-
case 'day':
|
|
339
|
-
current.subtract(1, 'day');
|
|
340
|
-
break;
|
|
341
|
-
}
|
|
342
|
-
}
|
|
343
|
-
return tables;
|
|
344
|
-
}
|
|
345
|
-
getRecentCountByGranularity(granularity, config) {
|
|
346
|
-
if (config.recentTableCount !== undefined && config.recentTableCount > 0) {
|
|
347
|
-
return config.recentTableCount;
|
|
348
|
-
}
|
|
349
|
-
switch (granularity) {
|
|
350
|
-
case 'year':
|
|
351
|
-
return 2;
|
|
352
|
-
case 'month':
|
|
353
|
-
return 3;
|
|
354
|
-
case 'day':
|
|
355
|
-
return 7;
|
|
356
|
-
}
|
|
357
|
-
}
|
|
358
|
-
getDefaultRecentCount(config) {
|
|
359
|
-
if (config.recentTableCount !== undefined && config.recentTableCount > 0) {
|
|
360
|
-
return config.recentTableCount;
|
|
361
|
-
}
|
|
362
|
-
switch (config.type) {
|
|
363
|
-
case ShardingConfig_1.ShardingType.YEAR:
|
|
364
|
-
return 2;
|
|
365
|
-
case ShardingConfig_1.ShardingType.MONTH:
|
|
366
|
-
return 3;
|
|
367
|
-
case ShardingConfig_1.ShardingType.DAY:
|
|
368
|
-
return 7;
|
|
369
|
-
default:
|
|
370
|
-
return 3;
|
|
371
|
-
}
|
|
372
|
-
}
|
|
373
|
-
calculateRangeIndex(value, count) {
|
|
374
|
-
if (typeof value === 'number') {
|
|
375
|
-
return Math.abs(value) % count;
|
|
376
|
-
}
|
|
377
|
-
return Math.abs(this.simpleHash(String(value))) % count;
|
|
378
|
-
}
|
|
379
|
-
calculateHash(value, count) {
|
|
380
|
-
return Math.abs(this.simpleHash(String(value))) % count;
|
|
381
|
-
}
|
|
382
|
-
simpleHash(str) {
|
|
383
|
-
let hash = 5381;
|
|
384
|
-
for (let i = 0; i < str.length; i++) {
|
|
385
|
-
hash = ((hash << 5) + hash) + str.charCodeAt(i);
|
|
386
|
-
hash = hash & hash;
|
|
387
|
-
}
|
|
388
|
-
return hash;
|
|
389
|
-
}
|
|
390
|
-
// ============ 表名格式化 ============
|
|
391
|
-
formatTableName(baseTable, suffix, config) {
|
|
392
|
-
const formattedSuffix = config.suffixFormatter ? config.suffixFormatter(suffix) : suffix;
|
|
393
|
-
return `${baseTable}_${formattedSuffix}`;
|
|
394
|
-
}
|
|
395
|
-
}
|
|
396
|
-
exports.ShardingRouter = ShardingRouter;
|