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
|
@@ -0,0 +1,179 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.ShardingBase = void 0;
|
|
4
|
+
const keys_1 = require("../../libs/crud-pro/models/keys");
|
|
5
|
+
const fixSoftDelete_1 = require("../../libs/crud-pro-quick/fixSoftDelete");
|
|
6
|
+
const ShardingTableCreator_1 = require("./ShardingTableCreator");
|
|
7
|
+
/**
|
|
8
|
+
* 分表 CRUD 基类
|
|
9
|
+
*
|
|
10
|
+
* 提供所有分表子类共用的基础设施方法,不包含任何路由逻辑。
|
|
11
|
+
* 路由逻辑由各子类自行实现。
|
|
12
|
+
*
|
|
13
|
+
* 子类:
|
|
14
|
+
* - ShardingByTimeCrud: 时间分表(YEAR/MONTH/DAY)
|
|
15
|
+
* - ShardingByHashCrud: 哈希分表
|
|
16
|
+
* - ShardingByKeyCrud: 键值分表
|
|
17
|
+
* - ShardingByCustomCrud: 自定义分表
|
|
18
|
+
*/
|
|
19
|
+
class ShardingBase {
|
|
20
|
+
constructor(crudProFactory, config) {
|
|
21
|
+
this.baseCfg = {};
|
|
22
|
+
this.enableSoftDelete = false;
|
|
23
|
+
this.config = config;
|
|
24
|
+
if (typeof crudProFactory === 'function') {
|
|
25
|
+
this.crudProFactory = crudProFactory;
|
|
26
|
+
}
|
|
27
|
+
else {
|
|
28
|
+
throw new Error('[ShardingBase] 请使用 CrudProFactory 工厂函数模式');
|
|
29
|
+
}
|
|
30
|
+
this.tableCreator = new ShardingTableCreator_1.ShardingTableCreator(this.crudProFactory, config);
|
|
31
|
+
}
|
|
32
|
+
// ============ 配置方法 ============
|
|
33
|
+
setBaseCfg(cfg) {
|
|
34
|
+
this.baseCfg = { ...this.baseCfg, ...cfg };
|
|
35
|
+
return this;
|
|
36
|
+
}
|
|
37
|
+
getConfig() {
|
|
38
|
+
return this.config;
|
|
39
|
+
}
|
|
40
|
+
setEnableSoftDelete(enable) {
|
|
41
|
+
this.enableSoftDelete = enable;
|
|
42
|
+
return this;
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* 判断当前路由上下文是否处于「插入阶段」(应从 data 提取分表字段)
|
|
46
|
+
*
|
|
47
|
+
* - INSERT / BATCH_INSERT → 始终从 data
|
|
48
|
+
* - 其余操作(QUERY / UPDATE / DELETE / INSERT_OR_UPDATE)→ 从 condition
|
|
49
|
+
*/
|
|
50
|
+
static isInsertPhase(ctx) {
|
|
51
|
+
return [keys_1.KeysOfSimpleSQL.SIMPLE_INSERT, keys_1.KeysOfSimpleSQL.SIMPLE_BATCH_INSERT].includes(ctx.sqlSimpleName);
|
|
52
|
+
}
|
|
53
|
+
// ============ 执行基础设施 ============
|
|
54
|
+
buildCfg(sqlSimpleName) {
|
|
55
|
+
return {
|
|
56
|
+
method: `ShardingCrudProAnonymous_${sqlSimpleName}`,
|
|
57
|
+
...this.baseCfg,
|
|
58
|
+
sqlTable: this.config.baseTable,
|
|
59
|
+
sqlSimpleName,
|
|
60
|
+
};
|
|
61
|
+
}
|
|
62
|
+
async executeOnTable(table, reqJson, sqlSimpleName, extraCfg) {
|
|
63
|
+
const cfg = this.buildCfg(sqlSimpleName);
|
|
64
|
+
cfg.sqlTable = table;
|
|
65
|
+
cfg.enableSoftDelete = this.enableSoftDelete;
|
|
66
|
+
if (extraCfg) {
|
|
67
|
+
Object.assign(cfg, extraCfg);
|
|
68
|
+
}
|
|
69
|
+
const crudPro = this.crudProFactory();
|
|
70
|
+
(0, fixSoftDelete_1.fixSoftDelete)(sqlSimpleName, cfg, reqJson, crudPro.getVisitor());
|
|
71
|
+
return crudPro.executeCrudByCfg(reqJson, cfg);
|
|
72
|
+
}
|
|
73
|
+
// ============ 单表查询辅助 ============
|
|
74
|
+
async findCountFromTable(table, reqJson) {
|
|
75
|
+
try {
|
|
76
|
+
const ctx = await this.executeOnTable(table, reqJson, keys_1.KeysOfSimpleSQL.SIMPLE_QUERY_COUNT);
|
|
77
|
+
return { count: ctx.getResModelItem('total_count') || 0, ctx };
|
|
78
|
+
}
|
|
79
|
+
catch (e) {
|
|
80
|
+
return { count: 0, ctx: null };
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
async isExistInTable(table, reqJson) {
|
|
84
|
+
try {
|
|
85
|
+
const ctx = await this.executeOnTable(table, reqJson, keys_1.KeysOfSimpleSQL.SIMPLE_QUERY_EXIST);
|
|
86
|
+
return { exists: ctx.getResModelItem('is_exist') === true, ctx };
|
|
87
|
+
}
|
|
88
|
+
catch (e) {
|
|
89
|
+
return { exists: false, ctx: null };
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
// ============ 表管理 ============
|
|
93
|
+
async getExistingTablesSet(skipCache = false) {
|
|
94
|
+
const { sqlDatabase, sqlDbType } = this.baseCfg;
|
|
95
|
+
if (!sqlDatabase || !sqlDbType) {
|
|
96
|
+
throw new Error('[ShardingBase] 未配置 sqlDatabase 或 sqlDbType');
|
|
97
|
+
}
|
|
98
|
+
const { tables } = await this.crudProFactory().getAllTableInfos({ sqlDatabase, sqlDbType: sqlDbType }, { skipCache });
|
|
99
|
+
return new Set(tables.map(t => t.name));
|
|
100
|
+
}
|
|
101
|
+
async isTableExists(tableName) {
|
|
102
|
+
const existingSet = await this.getExistingTablesSet();
|
|
103
|
+
return existingSet.has(tableName);
|
|
104
|
+
}
|
|
105
|
+
async createShardingTableIfNeeded(tableName) {
|
|
106
|
+
if (!this.config.autoCreateTable) {
|
|
107
|
+
if (!(await this.isTableExists(tableName))) {
|
|
108
|
+
throw new Error(`[ShardingBase] 分表 ${tableName} 不存在。请先创建分表,或设置 autoCreateTable: true 自动创建`);
|
|
109
|
+
}
|
|
110
|
+
return;
|
|
111
|
+
}
|
|
112
|
+
if (!this.baseCfg.sqlDatabase || !this.baseCfg.sqlDbType) {
|
|
113
|
+
throw new Error('[ShardingBase] 请先调用 setBaseCfg 设置数据库配置');
|
|
114
|
+
}
|
|
115
|
+
const result = await this.tableCreator.createTableIfNeeded(tableName, {
|
|
116
|
+
sqlDatabase: this.baseCfg.sqlDatabase,
|
|
117
|
+
sqlDbType: this.baseCfg.sqlDbType,
|
|
118
|
+
}, this.config.tableCreateOptions);
|
|
119
|
+
if (!result.success) {
|
|
120
|
+
throw result.error || new Error(`[ShardingBase] 创建分表 ${tableName} 失败`);
|
|
121
|
+
}
|
|
122
|
+
if (result.createSql) {
|
|
123
|
+
await this.getExistingTablesSet(true);
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
// ============ 辅助方法 ============
|
|
127
|
+
buildDebugInfo(sqlTable, condition) {
|
|
128
|
+
const info = {
|
|
129
|
+
sqlDatabase: this.baseCfg.sqlDatabase || 'unknown',
|
|
130
|
+
sqlTable: sqlTable || this.config.baseTable,
|
|
131
|
+
};
|
|
132
|
+
if (condition) {
|
|
133
|
+
info.condition = condition;
|
|
134
|
+
}
|
|
135
|
+
return info;
|
|
136
|
+
}
|
|
137
|
+
formatUniqueError(foundCount, tables, condition, isMultiTable = false) {
|
|
138
|
+
const parts = [
|
|
139
|
+
`[ShardingBase] findUniqueOne 期望唯一一条记录,但查询到 ${foundCount} 条`,
|
|
140
|
+
];
|
|
141
|
+
if (this.baseCfg.sqlDatabase) {
|
|
142
|
+
parts.push(`数据库: ${this.baseCfg.sqlDatabase}`);
|
|
143
|
+
}
|
|
144
|
+
if (isMultiTable && Array.isArray(tables)) {
|
|
145
|
+
parts.push(`基表: ${this.config.baseTable}`);
|
|
146
|
+
parts.push(`分表: [${tables.join(', ')}]`);
|
|
147
|
+
}
|
|
148
|
+
else {
|
|
149
|
+
parts.push(`表: ${typeof tables === 'string' ? tables : tables[0]}`);
|
|
150
|
+
}
|
|
151
|
+
if (condition) {
|
|
152
|
+
try {
|
|
153
|
+
parts.push(`条件: ${JSON.stringify(condition)}`);
|
|
154
|
+
}
|
|
155
|
+
catch (_a) {
|
|
156
|
+
parts.push(`条件: [无法序列化]`);
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
return parts.join(' | ');
|
|
160
|
+
}
|
|
161
|
+
/**
|
|
162
|
+
* 获取所有以 baseTable_ 开头的已存在分表(按字典序排序)
|
|
163
|
+
*/
|
|
164
|
+
async getAllExistingShardingTables() {
|
|
165
|
+
const existingTables = await this.getExistingTablesSet();
|
|
166
|
+
const prefix = `${this.config.baseTable}_`;
|
|
167
|
+
const tables = Array.from(existingTables).filter(t => t.startsWith(prefix));
|
|
168
|
+
tables.sort();
|
|
169
|
+
return tables;
|
|
170
|
+
}
|
|
171
|
+
/**
|
|
172
|
+
* 过滤出真实存在的分表
|
|
173
|
+
*/
|
|
174
|
+
async filterExistingTables(candidateTables) {
|
|
175
|
+
const existingTables = await this.getExistingTablesSet();
|
|
176
|
+
return candidateTables.filter(table => existingTables.has(table));
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
exports.ShardingBase = ShardingBase;
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { IRequestModel } from '../../libs/crud-pro/interfaces';
|
|
2
|
+
import { IShardingConfig, CrudProFactory } from './ShardingConfig';
|
|
3
|
+
import { ShardingBase } from './ShardingBase';
|
|
4
|
+
import { CrudWriteResult, CrudQueryOneResult, CrudQueryListResult, CrudQueryPageResult, CrudExistResult, CrudCountResult, CrudUpsertResult } from '../../libs/crud-pro/models/CrudResult';
|
|
5
|
+
import { ShardingBatchInsertResult } from './ShardingResult';
|
|
6
|
+
/**
|
|
7
|
+
* 自定义分表 CRUD
|
|
8
|
+
*
|
|
9
|
+
* 分表名称完全由用户提供的 customRouter 函数决定。
|
|
10
|
+
* 适用于无法用时间/哈希/键值覆盖的复杂场景(如原 RANGE 分表)。
|
|
11
|
+
*
|
|
12
|
+
* 特性:
|
|
13
|
+
* - 路由:customRouter({ config, data?, condition? }) → 表名或表名数组
|
|
14
|
+
* - 插入:customRouter 返回单个表名
|
|
15
|
+
* - 查询:customRouter 返回表名数组 → 过滤已存在表
|
|
16
|
+
* - insertOrUpdate:统一由 condition 路由定位分表,查找与插入在同一分表
|
|
17
|
+
*/
|
|
18
|
+
export declare class ShardingByCustomCrud extends ShardingBase {
|
|
19
|
+
constructor(crudProFactory: CrudProFactory, config: IShardingConfig);
|
|
20
|
+
private resolveTableForInsert;
|
|
21
|
+
private resolveTableForCondition;
|
|
22
|
+
private resolveFindTables;
|
|
23
|
+
insert(reqJson: IRequestModel): Promise<CrudWriteResult>;
|
|
24
|
+
batchInsert(reqJson: IRequestModel): Promise<ShardingBatchInsertResult>;
|
|
25
|
+
update(reqJson: IRequestModel): Promise<CrudWriteResult>;
|
|
26
|
+
delete(reqJson: IRequestModel): Promise<CrudWriteResult>;
|
|
27
|
+
restore(reqJson: IRequestModel): Promise<CrudWriteResult>;
|
|
28
|
+
insertOrUpdate(reqJson: IRequestModel): Promise<CrudUpsertResult>;
|
|
29
|
+
findOne<T = Record<string, any>>(reqJson: IRequestModel): Promise<CrudQueryOneResult<T>>;
|
|
30
|
+
findUniqueOne<T = Record<string, any>>(reqJson: IRequestModel): Promise<CrudQueryOneResult<T>>;
|
|
31
|
+
findList<T = Record<string, any>>(reqJson: IRequestModel): Promise<CrudQueryListResult<T>>;
|
|
32
|
+
findPage<T = Record<string, any>>(reqJson: IRequestModel): Promise<CrudQueryPageResult<T>>;
|
|
33
|
+
findCount(reqJson: IRequestModel): Promise<CrudCountResult>;
|
|
34
|
+
isExist(reqJson: IRequestModel): Promise<CrudExistResult>;
|
|
35
|
+
}
|
|
@@ -0,0 +1,297 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.ShardingByCustomCrud = void 0;
|
|
4
|
+
const keys_1 = require("../../libs/crud-pro/models/keys");
|
|
5
|
+
const ShardingBase_1 = require("./ShardingBase");
|
|
6
|
+
const CrudResult_1 = require("../../libs/crud-pro/models/CrudResult");
|
|
7
|
+
const ShardingResult_1 = require("./ShardingResult");
|
|
8
|
+
/**
|
|
9
|
+
* 自定义分表 CRUD
|
|
10
|
+
*
|
|
11
|
+
* 分表名称完全由用户提供的 customRouter 函数决定。
|
|
12
|
+
* 适用于无法用时间/哈希/键值覆盖的复杂场景(如原 RANGE 分表)。
|
|
13
|
+
*
|
|
14
|
+
* 特性:
|
|
15
|
+
* - 路由:customRouter({ config, data?, condition? }) → 表名或表名数组
|
|
16
|
+
* - 插入:customRouter 返回单个表名
|
|
17
|
+
* - 查询:customRouter 返回表名数组 → 过滤已存在表
|
|
18
|
+
* - insertOrUpdate:统一由 condition 路由定位分表,查找与插入在同一分表
|
|
19
|
+
*/
|
|
20
|
+
class ShardingByCustomCrud extends ShardingBase_1.ShardingBase {
|
|
21
|
+
constructor(crudProFactory, config) {
|
|
22
|
+
super(crudProFactory, config);
|
|
23
|
+
if (!config.customRouter) {
|
|
24
|
+
throw new Error('[ShardingByCustomCrud] 自定义分表必须提供 customRouter 函数');
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
// ============ 路由 ============
|
|
28
|
+
resolveTableForInsert(data, sqlSimpleName) {
|
|
29
|
+
const context = { config: this.config, data, sqlSimpleName };
|
|
30
|
+
const result = this.config.customRouter(context);
|
|
31
|
+
if (Array.isArray(result)) {
|
|
32
|
+
if (result.length === 1) {
|
|
33
|
+
return result[0];
|
|
34
|
+
}
|
|
35
|
+
throw new Error(`[ShardingByCustomCrud] 插入操作需要 customRouter 返回单个表名,但返回了 ${result.length} 个: ${result.slice(0, 5).join(', ')}`);
|
|
36
|
+
}
|
|
37
|
+
if (typeof result === 'string' && result) {
|
|
38
|
+
return result;
|
|
39
|
+
}
|
|
40
|
+
throw new Error('[ShardingByCustomCrud] 插入操作需要 customRouter 返回单个表名, 实际返回了空值');
|
|
41
|
+
}
|
|
42
|
+
resolveTableForCondition(condition, sqlSimpleName) {
|
|
43
|
+
const context = { config: this.config, condition, sqlSimpleName };
|
|
44
|
+
const result = this.config.customRouter(context);
|
|
45
|
+
if (Array.isArray(result)) {
|
|
46
|
+
if (result.length === 1)
|
|
47
|
+
return result[0];
|
|
48
|
+
throw new Error(`[ShardingByCustomCrud] 写操作需要 customRouter 返回单个表名,但返回了 ${result.length} 个: ${result.slice(0, 5).join(', ')}`);
|
|
49
|
+
}
|
|
50
|
+
return result;
|
|
51
|
+
}
|
|
52
|
+
async resolveFindTables(reqJson, sqlSimpleName) {
|
|
53
|
+
const context = { config: this.config, condition: reqJson.condition, sqlSimpleName };
|
|
54
|
+
const result = this.config.customRouter(context);
|
|
55
|
+
const candidates = Array.isArray(result) ? result : [result];
|
|
56
|
+
return this.filterExistingTables(candidates);
|
|
57
|
+
}
|
|
58
|
+
// ============ 写操作 ============
|
|
59
|
+
async insert(reqJson) {
|
|
60
|
+
const targetTable = this.resolveTableForInsert(reqJson.data, keys_1.KeysOfSimpleSQL.SIMPLE_INSERT);
|
|
61
|
+
await this.createShardingTableIfNeeded(targetTable);
|
|
62
|
+
const ctx = await this.executeOnTable(targetTable, reqJson, keys_1.KeysOfSimpleSQL.SIMPLE_INSERT);
|
|
63
|
+
const affected = ctx.getResModel().affected || { affectedRows: 0 };
|
|
64
|
+
return new CrudResult_1.CrudWriteResult({ affectedRows: affected.affectedRows, insertId: affected.insertId, rawContext: ctx });
|
|
65
|
+
}
|
|
66
|
+
async batchInsert(reqJson) {
|
|
67
|
+
var _a, _b;
|
|
68
|
+
const dataArray = reqJson.data;
|
|
69
|
+
if (!Array.isArray(dataArray) || dataArray.length === 0) {
|
|
70
|
+
throw new Error('[ShardingByCustomCrud] batchInsert requires non-empty data array');
|
|
71
|
+
}
|
|
72
|
+
const groupedData = new Map();
|
|
73
|
+
for (const item of dataArray) {
|
|
74
|
+
const table = this.resolveTableForInsert(item, keys_1.KeysOfSimpleSQL.SIMPLE_BATCH_INSERT);
|
|
75
|
+
if (!groupedData.has(table))
|
|
76
|
+
groupedData.set(table, []);
|
|
77
|
+
groupedData.get(table).push(item);
|
|
78
|
+
}
|
|
79
|
+
for (const tableName of groupedData.keys()) {
|
|
80
|
+
await this.createShardingTableIfNeeded(tableName);
|
|
81
|
+
}
|
|
82
|
+
const results = [];
|
|
83
|
+
let lastContext = null;
|
|
84
|
+
for (const [table, items] of groupedData.entries()) {
|
|
85
|
+
try {
|
|
86
|
+
const ctx = await this.executeOnTable(table, { data: items }, keys_1.KeysOfSimpleSQL.SIMPLE_BATCH_INSERT);
|
|
87
|
+
lastContext = ctx;
|
|
88
|
+
results.push({ table, affected: ((_a = ctx.getResModelItem('affected')) === null || _a === void 0 ? void 0 : _a.affectedRows) || items.length, rowCount: items.length, error: null, context: ctx });
|
|
89
|
+
}
|
|
90
|
+
catch (e) {
|
|
91
|
+
results.push({ table, affected: 0, rowCount: items.length, error: e, context: null });
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
const tableResults = results.map(r => ({ table: r.table, affected: r.affected, rowCount: r.rowCount }));
|
|
95
|
+
const errors = results.filter(r => r.error !== null).map(r => ({ table: r.table, error: r.error }));
|
|
96
|
+
const totalAffected = results.reduce((sum, r) => sum + r.affected, 0);
|
|
97
|
+
const finalContext = lastContext || ((_b = results.find(r => r.context)) === null || _b === void 0 ? void 0 : _b.context);
|
|
98
|
+
if (!finalContext)
|
|
99
|
+
throw new Error('[ShardingByCustomCrud] batchInsert failed - no successful inserts');
|
|
100
|
+
return new ShardingResult_1.ShardingBatchInsertResult({ totalAffected, tableResults, tableCount: groupedData.size, success: errors.length === 0, errors, lastContext: finalContext });
|
|
101
|
+
}
|
|
102
|
+
async update(reqJson) {
|
|
103
|
+
const targetTable = this.resolveTableForCondition(reqJson.condition, keys_1.KeysOfSimpleSQL.SIMPLE_UPDATE);
|
|
104
|
+
const ctx = await this.executeOnTable(targetTable, reqJson, keys_1.KeysOfSimpleSQL.SIMPLE_UPDATE);
|
|
105
|
+
const affected = ctx.getResModel().affected || { affectedRows: 0 };
|
|
106
|
+
return new CrudResult_1.CrudWriteResult({ affectedRows: affected.affectedRows, insertId: affected.insertId, rawContext: ctx });
|
|
107
|
+
}
|
|
108
|
+
async delete(reqJson) {
|
|
109
|
+
const targetTable = this.resolveTableForCondition(reqJson.condition, keys_1.KeysOfSimpleSQL.SIMPLE_DELETE);
|
|
110
|
+
const ctx = await this.executeOnTable(targetTable, reqJson, keys_1.KeysOfSimpleSQL.SIMPLE_DELETE);
|
|
111
|
+
const affected = ctx.getResModel().affected || { affectedRows: 0 };
|
|
112
|
+
return new CrudResult_1.CrudWriteResult({ affectedRows: affected.affectedRows, insertId: affected.insertId, rawContext: ctx });
|
|
113
|
+
}
|
|
114
|
+
async restore(reqJson) {
|
|
115
|
+
const targetTable = this.resolveTableForCondition(reqJson.condition, keys_1.KeysOfSimpleSQL.SIMPLE_UPDATE);
|
|
116
|
+
if (!reqJson.condition || Object.keys(reqJson.condition).length === 0) {
|
|
117
|
+
throw new Error('[ShardingByCustomCrud] restore 操作必须指定恢复条件');
|
|
118
|
+
}
|
|
119
|
+
const restoreReqJson = {
|
|
120
|
+
...reqJson,
|
|
121
|
+
data: { ...reqJson.data, deleted_at: 0, deleted_by: '' },
|
|
122
|
+
};
|
|
123
|
+
const ctx = await this.executeOnTable(targetTable, restoreReqJson, keys_1.KeysOfSimpleSQL.SIMPLE_UPDATE);
|
|
124
|
+
const affected = ctx.getResModel().affected || { affectedRows: 0 };
|
|
125
|
+
return new CrudResult_1.CrudWriteResult({ affectedRows: affected.affectedRows, insertId: affected.insertId, rawContext: ctx });
|
|
126
|
+
}
|
|
127
|
+
async insertOrUpdate(reqJson) {
|
|
128
|
+
const targetTable = this.resolveTableForCondition(reqJson.condition, keys_1.KeysOfSimpleSQL.SIMPLE_INSERT_OR_UPDATE);
|
|
129
|
+
const existCtx = await this.executeOnTable(targetTable, reqJson, keys_1.KeysOfSimpleSQL.SIMPLE_QUERY_EXIST);
|
|
130
|
+
const isExist = existCtx.getResModelItem('is_exist') === true;
|
|
131
|
+
if (isExist) {
|
|
132
|
+
const updateCtx = await this.executeOnTable(targetTable, reqJson, keys_1.KeysOfSimpleSQL.SIMPLE_UPDATE);
|
|
133
|
+
return new CrudResult_1.CrudUpsertResult({ insertAffected: undefined, updateAffected: updateCtx.getResModel().affected, isExist: true, rawContext: updateCtx });
|
|
134
|
+
}
|
|
135
|
+
await this.createShardingTableIfNeeded(targetTable);
|
|
136
|
+
const insertCtx = await this.executeOnTable(targetTable, reqJson, keys_1.KeysOfSimpleSQL.SIMPLE_INSERT);
|
|
137
|
+
return new CrudResult_1.CrudUpsertResult({ insertAffected: insertCtx.getResModel().affected, updateAffected: undefined, isExist: false, rawContext: insertCtx });
|
|
138
|
+
}
|
|
139
|
+
// ============ 查询操作 ============
|
|
140
|
+
async findOne(reqJson) {
|
|
141
|
+
const targetTables = await this.resolveFindTables(reqJson, keys_1.KeysOfSimpleSQL.SIMPLE_QUERY_ONE);
|
|
142
|
+
if (targetTables.length === 0)
|
|
143
|
+
throw new Error('[ShardingByCustomCrud] findOne: no matching tables found');
|
|
144
|
+
let lastCtx = null;
|
|
145
|
+
let lastTable;
|
|
146
|
+
for (const table of targetTables) {
|
|
147
|
+
try {
|
|
148
|
+
const ctx = await this.executeOnTable(table, reqJson, keys_1.KeysOfSimpleSQL.SIMPLE_QUERY_ONE);
|
|
149
|
+
lastCtx = ctx;
|
|
150
|
+
lastTable = table;
|
|
151
|
+
const row = ctx.getOneObj();
|
|
152
|
+
if (row)
|
|
153
|
+
return new CrudResult_1.CrudQueryOneResult({ row: row, rawContext: ctx, debugInfo: this.buildDebugInfo(table, reqJson.condition), fromTable: table });
|
|
154
|
+
}
|
|
155
|
+
catch (e) { /* skip */ }
|
|
156
|
+
}
|
|
157
|
+
return new CrudResult_1.CrudQueryOneResult({ row: null, rawContext: lastCtx, debugInfo: this.buildDebugInfo(targetTables[0], reqJson.condition), fromTable: lastTable });
|
|
158
|
+
}
|
|
159
|
+
async findUniqueOne(reqJson) {
|
|
160
|
+
const targetTables = await this.resolveFindTables(reqJson, keys_1.KeysOfSimpleSQL.SIMPLE_QUERY_ONE);
|
|
161
|
+
if (targetTables.length === 0) {
|
|
162
|
+
return new CrudResult_1.CrudQueryOneResult({ row: null, rawContext: null, debugInfo: this.buildDebugInfo(undefined, reqJson.condition), fromTable: null });
|
|
163
|
+
}
|
|
164
|
+
const allRows = [];
|
|
165
|
+
const foundTables = [];
|
|
166
|
+
let firstCtx = null;
|
|
167
|
+
for (const table of targetTables) {
|
|
168
|
+
try {
|
|
169
|
+
const ctx = await this.executeOnTable(table, reqJson, keys_1.KeysOfSimpleSQL.SIMPLE_QUERY, { maxLimit: 2 });
|
|
170
|
+
if (!firstCtx)
|
|
171
|
+
firstCtx = ctx;
|
|
172
|
+
const rows = ctx.getResRows();
|
|
173
|
+
if (rows.length > 0) {
|
|
174
|
+
allRows.push(...rows);
|
|
175
|
+
foundTables.push(table);
|
|
176
|
+
if (allRows.length > 1)
|
|
177
|
+
throw new Error(this.formatUniqueError(allRows.length, foundTables, reqJson.condition, foundTables.length > 1));
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
catch (e) {
|
|
181
|
+
if (e instanceof Error && e.message.startsWith('[ShardingBase] findUniqueOne'))
|
|
182
|
+
throw e;
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
const fromTable = foundTables[0] || null;
|
|
186
|
+
return new CrudResult_1.CrudQueryOneResult({ row: allRows[0] || null, rawContext: firstCtx, debugInfo: this.buildDebugInfo(fromTable || targetTables[0], reqJson.condition), fromTable: fromTable || targetTables[0] });
|
|
187
|
+
}
|
|
188
|
+
async findList(reqJson) {
|
|
189
|
+
const targetTables = await this.resolveFindTables(reqJson, keys_1.KeysOfSimpleSQL.SIMPLE_QUERY);
|
|
190
|
+
if (targetTables.length === 0)
|
|
191
|
+
return new CrudResult_1.CrudQueryListResult({ rows: [], rawContext: null });
|
|
192
|
+
if (targetTables.length === 1) {
|
|
193
|
+
const ctx = await this.executeOnTable(targetTables[0], reqJson, keys_1.KeysOfSimpleSQL.SIMPLE_QUERY);
|
|
194
|
+
return new CrudResult_1.CrudQueryListResult({ rows: ctx.getResRows(), rawContext: ctx });
|
|
195
|
+
}
|
|
196
|
+
let allRows = [];
|
|
197
|
+
let lastCtx = null;
|
|
198
|
+
for (const table of targetTables) {
|
|
199
|
+
try {
|
|
200
|
+
const ctx = await this.executeOnTable(table, reqJson, keys_1.KeysOfSimpleSQL.SIMPLE_QUERY);
|
|
201
|
+
lastCtx = ctx;
|
|
202
|
+
allRows = allRows.concat(ctx.getResRows() || []);
|
|
203
|
+
}
|
|
204
|
+
catch (e) { /* skip */ }
|
|
205
|
+
}
|
|
206
|
+
return new CrudResult_1.CrudQueryListResult({ rows: allRows, rawContext: lastCtx });
|
|
207
|
+
}
|
|
208
|
+
async findPage(reqJson) {
|
|
209
|
+
const targetTables = await this.resolveFindTables(reqJson, keys_1.KeysOfSimpleSQL.SIMPLE_QUERY_PAGE);
|
|
210
|
+
if (targetTables.length === 0)
|
|
211
|
+
throw new Error('[ShardingByCustomCrud] findPage: no matching tables found');
|
|
212
|
+
if (targetTables.length === 1) {
|
|
213
|
+
const ctx = await this.executeOnTable(targetTables[0], reqJson, keys_1.KeysOfSimpleSQL.SIMPLE_QUERY_PAGE);
|
|
214
|
+
const pageResult = ctx.getResModelForQueryPage();
|
|
215
|
+
return new CrudResult_1.CrudQueryPageResult({ rows: pageResult.rows, totalCount: pageResult.total_count, rawContext: ctx });
|
|
216
|
+
}
|
|
217
|
+
const { pageNo = 1, pageSize = 10 } = reqJson;
|
|
218
|
+
const offset = (pageNo - 1) * pageSize;
|
|
219
|
+
let totalCount = 0;
|
|
220
|
+
const tableCounts = [];
|
|
221
|
+
for (const table of targetTables) {
|
|
222
|
+
const { count } = await this.findCountFromTable(table, reqJson);
|
|
223
|
+
tableCounts.push({ table, count });
|
|
224
|
+
totalCount += count;
|
|
225
|
+
}
|
|
226
|
+
if (totalCount === 0) {
|
|
227
|
+
return new CrudResult_1.CrudQueryPageResult({ rows: [], totalCount: 0, rawContext: null });
|
|
228
|
+
}
|
|
229
|
+
let accumulated = 0;
|
|
230
|
+
let allRows = [];
|
|
231
|
+
let lastCtx = null;
|
|
232
|
+
const targetEnd = offset + pageSize;
|
|
233
|
+
for (const { table, count } of tableCounts) {
|
|
234
|
+
if (count === 0)
|
|
235
|
+
continue;
|
|
236
|
+
const tableEnd = accumulated + count;
|
|
237
|
+
if (tableEnd <= offset) {
|
|
238
|
+
accumulated = tableEnd;
|
|
239
|
+
continue;
|
|
240
|
+
}
|
|
241
|
+
if (accumulated >= targetEnd)
|
|
242
|
+
break;
|
|
243
|
+
const innerOffset = Math.max(0, offset - accumulated);
|
|
244
|
+
const innerLimit = Math.min(count, targetEnd - accumulated) - innerOffset;
|
|
245
|
+
try {
|
|
246
|
+
const ctx = await this.executeOnTable(table, { ...reqJson, offset: innerOffset, limit: innerLimit }, keys_1.KeysOfSimpleSQL.SIMPLE_QUERY);
|
|
247
|
+
lastCtx = ctx;
|
|
248
|
+
allRows = allRows.concat(ctx.getResRows() || []);
|
|
249
|
+
}
|
|
250
|
+
catch (e) { /* skip */ }
|
|
251
|
+
accumulated = tableEnd;
|
|
252
|
+
if (accumulated >= targetEnd)
|
|
253
|
+
break;
|
|
254
|
+
}
|
|
255
|
+
return new CrudResult_1.CrudQueryPageResult({ rows: allRows, totalCount, rawContext: lastCtx });
|
|
256
|
+
}
|
|
257
|
+
async findCount(reqJson) {
|
|
258
|
+
const targetTables = await this.resolveFindTables(reqJson, keys_1.KeysOfSimpleSQL.SIMPLE_QUERY_COUNT);
|
|
259
|
+
if (targetTables.length === 0)
|
|
260
|
+
throw new Error('[ShardingByCustomCrud] findCount: no matching tables found');
|
|
261
|
+
if (targetTables.length === 1) {
|
|
262
|
+
const ctx = await this.executeOnTable(targetTables[0], reqJson, keys_1.KeysOfSimpleSQL.SIMPLE_QUERY_COUNT);
|
|
263
|
+
return new CrudResult_1.CrudCountResult({ count: ctx.getResModelItem('total_count') || 0, rawContext: ctx });
|
|
264
|
+
}
|
|
265
|
+
let totalCount = 0;
|
|
266
|
+
let firstCtx = null;
|
|
267
|
+
for (const table of targetTables) {
|
|
268
|
+
const { count, ctx } = await this.findCountFromTable(table, reqJson);
|
|
269
|
+
totalCount += count;
|
|
270
|
+
if (!firstCtx && ctx)
|
|
271
|
+
firstCtx = ctx;
|
|
272
|
+
}
|
|
273
|
+
return new CrudResult_1.CrudCountResult({ count: totalCount, rawContext: firstCtx });
|
|
274
|
+
}
|
|
275
|
+
async isExist(reqJson) {
|
|
276
|
+
const targetTables = await this.resolveFindTables(reqJson, keys_1.KeysOfSimpleSQL.SIMPLE_QUERY_EXIST);
|
|
277
|
+
if (targetTables.length === 0)
|
|
278
|
+
throw new Error('[ShardingByCustomCrud] isExist: no matching tables found');
|
|
279
|
+
if (targetTables.length === 1) {
|
|
280
|
+
const ctx = await this.executeOnTable(targetTables[0], reqJson, keys_1.KeysOfSimpleSQL.SIMPLE_QUERY_EXIST);
|
|
281
|
+
return new CrudResult_1.CrudExistResult({ exists: ctx.getResModelItem('is_exist') === true, rawContext: ctx });
|
|
282
|
+
}
|
|
283
|
+
let exists = false;
|
|
284
|
+
let firstCtx = null;
|
|
285
|
+
for (const table of targetTables) {
|
|
286
|
+
const result = await this.isExistInTable(table, reqJson);
|
|
287
|
+
if (!firstCtx && result.ctx)
|
|
288
|
+
firstCtx = result.ctx;
|
|
289
|
+
if (result.exists) {
|
|
290
|
+
exists = true;
|
|
291
|
+
break;
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
return new CrudResult_1.CrudExistResult({ exists, rawContext: firstCtx });
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
exports.ShardingByCustomCrud = ShardingByCustomCrud;
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { IRequestModel, IRequestCfgModel } from '../../libs/crud-pro/interfaces';
|
|
2
|
+
import { IShardingConfig, CrudProFactory } from './ShardingConfig';
|
|
3
|
+
import { ShardingBase } from './ShardingBase';
|
|
4
|
+
import { CrudWriteResult, CrudQueryOneResult, CrudQueryListResult, CrudQueryPageResult, CrudExistResult, CrudCountResult, CrudUpsertResult } from '../../libs/crud-pro/models/CrudResult';
|
|
5
|
+
import { ShardingBatchInsertResult } from './ShardingResult';
|
|
6
|
+
/**
|
|
7
|
+
* 哈希分表 CRUD
|
|
8
|
+
*
|
|
9
|
+
* 根据 shardingColumn 的哈希值取模 tableCount,定位到固定数量的分表。
|
|
10
|
+
* 表名格式:baseTable_XX(如 t_order_00 ~ t_order_15)
|
|
11
|
+
*
|
|
12
|
+
* 内部委托 ShardingByCustomCrud 实现所有 CRUD 操作,
|
|
13
|
+
* 通过 customRouter 实现哈希路由逻辑。
|
|
14
|
+
*
|
|
15
|
+
* 规范:所有操作必须提供 shardingColumn,否则抛出异常。
|
|
16
|
+
*
|
|
17
|
+
* 特性:
|
|
18
|
+
* - 路由:hash(shardingColumn) % tableCount → 固定分表
|
|
19
|
+
* - insertOrUpdate:统一由 condition 路由定位分表
|
|
20
|
+
*/
|
|
21
|
+
export declare class ShardingByHashCrud extends ShardingBase {
|
|
22
|
+
private delegate;
|
|
23
|
+
constructor(crudProFactory: CrudProFactory, config: IShardingConfig);
|
|
24
|
+
setBaseCfg(cfg: Partial<IRequestCfgModel>): this;
|
|
25
|
+
setEnableSoftDelete(enable: boolean): this;
|
|
26
|
+
insert(r: IRequestModel): Promise<CrudWriteResult>;
|
|
27
|
+
batchInsert(r: IRequestModel): Promise<ShardingBatchInsertResult>;
|
|
28
|
+
update(r: IRequestModel): Promise<CrudWriteResult>;
|
|
29
|
+
delete(r: IRequestModel): Promise<CrudWriteResult>;
|
|
30
|
+
restore(r: IRequestModel): Promise<CrudWriteResult>;
|
|
31
|
+
insertOrUpdate(r: IRequestModel): Promise<CrudUpsertResult>;
|
|
32
|
+
findOne<T = Record<string, any>>(r: IRequestModel): Promise<CrudQueryOneResult<T>>;
|
|
33
|
+
findUniqueOne<T = Record<string, any>>(r: IRequestModel): Promise<CrudQueryOneResult<T>>;
|
|
34
|
+
findList<T = Record<string, any>>(r: IRequestModel): Promise<CrudQueryListResult<T>>;
|
|
35
|
+
findPage<T = Record<string, any>>(r: IRequestModel): Promise<CrudQueryPageResult<T>>;
|
|
36
|
+
findCount(r: IRequestModel): Promise<CrudCountResult>;
|
|
37
|
+
isExist(r: IRequestModel): Promise<CrudExistResult>;
|
|
38
|
+
}
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.ShardingByHashCrud = void 0;
|
|
4
|
+
const ShardingBase_1 = require("./ShardingBase");
|
|
5
|
+
const ShardingByCustomCrud_1 = require("./ShardingByCustomCrud");
|
|
6
|
+
/**
|
|
7
|
+
* 哈希分表 CRUD
|
|
8
|
+
*
|
|
9
|
+
* 根据 shardingColumn 的哈希值取模 tableCount,定位到固定数量的分表。
|
|
10
|
+
* 表名格式:baseTable_XX(如 t_order_00 ~ t_order_15)
|
|
11
|
+
*
|
|
12
|
+
* 内部委托 ShardingByCustomCrud 实现所有 CRUD 操作,
|
|
13
|
+
* 通过 customRouter 实现哈希路由逻辑。
|
|
14
|
+
*
|
|
15
|
+
* 规范:所有操作必须提供 shardingColumn,否则抛出异常。
|
|
16
|
+
*
|
|
17
|
+
* 特性:
|
|
18
|
+
* - 路由:hash(shardingColumn) % tableCount → 固定分表
|
|
19
|
+
* - insertOrUpdate:统一由 condition 路由定位分表
|
|
20
|
+
*/
|
|
21
|
+
class ShardingByHashCrud extends ShardingBase_1.ShardingBase {
|
|
22
|
+
constructor(crudProFactory, config) {
|
|
23
|
+
super(crudProFactory, config);
|
|
24
|
+
if (!config.shardingColumn) {
|
|
25
|
+
throw new Error('[ShardingByHashCrud] 哈希分表必须指定 shardingColumn');
|
|
26
|
+
}
|
|
27
|
+
if (!config.tableCount || config.tableCount <= 0) {
|
|
28
|
+
throw new Error('[ShardingByHashCrud] 哈希分表必须指定 tableCount(大于 0)');
|
|
29
|
+
}
|
|
30
|
+
const col = config.shardingColumn;
|
|
31
|
+
const count = config.tableCount;
|
|
32
|
+
const baseTable = config.baseTable;
|
|
33
|
+
const fmt = config.suffixFormatter;
|
|
34
|
+
/** 格式化表名:baseTable + _ + 后缀(可选 suffixFormatter) */
|
|
35
|
+
const formatTable = (suffix) => {
|
|
36
|
+
const formatted = fmt ? fmt(suffix) : suffix;
|
|
37
|
+
return `${baseTable}_${formatted}`;
|
|
38
|
+
};
|
|
39
|
+
/** djb2 哈希 → 取模 → 零填充后缀 → 表名 */
|
|
40
|
+
const hashRoute = (value) => {
|
|
41
|
+
const str = String(value);
|
|
42
|
+
let hash = 5381;
|
|
43
|
+
for (let i = 0; i < str.length; i++) {
|
|
44
|
+
hash = ((hash << 5) + hash) + str.charCodeAt(i);
|
|
45
|
+
hash = hash & hash;
|
|
46
|
+
}
|
|
47
|
+
return formatTable(String(Math.abs(hash) % count).padStart(2, '0'));
|
|
48
|
+
};
|
|
49
|
+
const customRouter = (ctx) => {
|
|
50
|
+
var _a, _b;
|
|
51
|
+
// 根据操作类型确定字段来源:insert 类从 data,其余从 condition
|
|
52
|
+
const fromData = ShardingBase_1.ShardingBase.isInsertPhase(ctx);
|
|
53
|
+
const val = fromData ? (_a = ctx.data) === null || _a === void 0 ? void 0 : _a[col] : (_b = ctx.condition) === null || _b === void 0 ? void 0 : _b[col];
|
|
54
|
+
if (val != null && val !== '' && val !== undefined) {
|
|
55
|
+
return hashRoute(val);
|
|
56
|
+
}
|
|
57
|
+
throw new Error(`[ShardingByHashCrud] ${fromData ? 'data' : 'condition'} 必须包含分表字段 '${col}'`);
|
|
58
|
+
};
|
|
59
|
+
this.delegate = new ShardingByCustomCrud_1.ShardingByCustomCrud(crudProFactory, { ...config, customRouter });
|
|
60
|
+
}
|
|
61
|
+
// ============ 状态同步 ============
|
|
62
|
+
setBaseCfg(cfg) {
|
|
63
|
+
super.setBaseCfg(cfg);
|
|
64
|
+
this.delegate.setBaseCfg(cfg);
|
|
65
|
+
return this;
|
|
66
|
+
}
|
|
67
|
+
setEnableSoftDelete(enable) {
|
|
68
|
+
super.setEnableSoftDelete(enable);
|
|
69
|
+
this.delegate.setEnableSoftDelete(enable);
|
|
70
|
+
return this;
|
|
71
|
+
}
|
|
72
|
+
// ============ CRUD 方法:全部委托 ============
|
|
73
|
+
insert(r) { return this.delegate.insert(r); }
|
|
74
|
+
batchInsert(r) { return this.delegate.batchInsert(r); }
|
|
75
|
+
update(r) { return this.delegate.update(r); }
|
|
76
|
+
delete(r) { return this.delegate.delete(r); }
|
|
77
|
+
restore(r) { return this.delegate.restore(r); }
|
|
78
|
+
insertOrUpdate(r) { return this.delegate.insertOrUpdate(r); }
|
|
79
|
+
findOne(r) { return this.delegate.findOne(r); }
|
|
80
|
+
findUniqueOne(r) { return this.delegate.findUniqueOne(r); }
|
|
81
|
+
findList(r) { return this.delegate.findList(r); }
|
|
82
|
+
findPage(r) { return this.delegate.findPage(r); }
|
|
83
|
+
findCount(r) { return this.delegate.findCount(r); }
|
|
84
|
+
isExist(r) { return this.delegate.isExist(r); }
|
|
85
|
+
}
|
|
86
|
+
exports.ShardingByHashCrud = ShardingByHashCrud;
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { IRequestModel, IRequestCfgModel } from '../../libs/crud-pro/interfaces';
|
|
2
|
+
import { IShardingConfig, CrudProFactory } from './ShardingConfig';
|
|
3
|
+
import { ShardingBase } from './ShardingBase';
|
|
4
|
+
import { CrudWriteResult, CrudQueryOneResult, CrudQueryListResult, CrudQueryPageResult, CrudExistResult, CrudCountResult, CrudUpsertResult } from '../../libs/crud-pro/models/CrudResult';
|
|
5
|
+
import { ShardingBatchInsertResult } from './ShardingResult';
|
|
6
|
+
/**
|
|
7
|
+
* 键值分表 CRUD
|
|
8
|
+
*
|
|
9
|
+
* 字段原值即表后缀,一值一表。
|
|
10
|
+
* 表名格式:baseTable_{shardingColumn值}(如 t_log_east, t_log_west)
|
|
11
|
+
*
|
|
12
|
+
* 内部委托 ShardingByCustomCrud 实现所有 CRUD 操作,
|
|
13
|
+
* 通过 customRouter 实现键值路由逻辑。
|
|
14
|
+
*
|
|
15
|
+
* 规范:所有操作必须提供 shardingColumn,否则抛出异常。
|
|
16
|
+
*
|
|
17
|
+
* 特性:
|
|
18
|
+
* - 路由:shardingColumn 的原始值直接作为表后缀
|
|
19
|
+
* - 不预定义 keyValues,表按需动态创建
|
|
20
|
+
* - insertOrUpdate:统一由 condition 路由定位分表
|
|
21
|
+
*/
|
|
22
|
+
export declare class ShardingByKeyCrud extends ShardingBase {
|
|
23
|
+
private delegate;
|
|
24
|
+
constructor(crudProFactory: CrudProFactory, config: IShardingConfig);
|
|
25
|
+
setBaseCfg(cfg: Partial<IRequestCfgModel>): this;
|
|
26
|
+
setEnableSoftDelete(enable: boolean): this;
|
|
27
|
+
insert(r: IRequestModel): Promise<CrudWriteResult>;
|
|
28
|
+
batchInsert(r: IRequestModel): Promise<ShardingBatchInsertResult>;
|
|
29
|
+
update(r: IRequestModel): Promise<CrudWriteResult>;
|
|
30
|
+
delete(r: IRequestModel): Promise<CrudWriteResult>;
|
|
31
|
+
restore(r: IRequestModel): Promise<CrudWriteResult>;
|
|
32
|
+
insertOrUpdate(r: IRequestModel): Promise<CrudUpsertResult>;
|
|
33
|
+
findOne<T = Record<string, any>>(r: IRequestModel): Promise<CrudQueryOneResult<T>>;
|
|
34
|
+
findUniqueOne<T = Record<string, any>>(r: IRequestModel): Promise<CrudQueryOneResult<T>>;
|
|
35
|
+
findList<T = Record<string, any>>(r: IRequestModel): Promise<CrudQueryListResult<T>>;
|
|
36
|
+
findPage<T = Record<string, any>>(r: IRequestModel): Promise<CrudQueryPageResult<T>>;
|
|
37
|
+
findCount(r: IRequestModel): Promise<CrudCountResult>;
|
|
38
|
+
isExist(r: IRequestModel): Promise<CrudExistResult>;
|
|
39
|
+
}
|