befly 3.8.32 → 3.8.34
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/lib/dbHelper.ts +5 -2
- package/lib/redisHelper.ts +12 -46
- package/package.json +3 -3
- package/sync/syncDb/table.ts +1 -1
- package/tests/redisHelper.test.ts +14 -22
- package/types/redis.d.ts +1 -3
package/lib/dbHelper.ts
CHANGED
|
@@ -595,8 +595,11 @@ export class DbHelper {
|
|
|
595
595
|
// 转换表名:小驼峰 → 下划线
|
|
596
596
|
const snakeTable = snakeCase(table);
|
|
597
597
|
|
|
598
|
-
// 批量生成 ID
|
|
599
|
-
const ids =
|
|
598
|
+
// 批量生成 ID(逐个获取)
|
|
599
|
+
const ids: number[] = [];
|
|
600
|
+
for (let i = 0; i < dataList.length; i++) {
|
|
601
|
+
ids.push(await this.befly.redis.genTimeID());
|
|
602
|
+
}
|
|
600
603
|
const now = Date.now();
|
|
601
604
|
|
|
602
605
|
// 处理所有数据(自动添加系统字段)
|
package/lib/redisHelper.ts
CHANGED
|
@@ -80,62 +80,28 @@ export class RedisHelper {
|
|
|
80
80
|
}
|
|
81
81
|
|
|
82
82
|
// ==================== ID 生成 ====================
|
|
83
|
-
// 注意:ID 生成功能强依赖 Redis 原子操作(INCR
|
|
84
|
-
// 主要被 DbHelper.insData
|
|
85
|
-
// 如未来有其他 ID 生成需求,可考虑提取到独立模块
|
|
83
|
+
// 注意:ID 生成功能强依赖 Redis 原子操作(INCR)保证分布式唯一性
|
|
84
|
+
// 主要被 DbHelper.insData 使用
|
|
86
85
|
|
|
87
86
|
/**
|
|
88
87
|
* 生成基于时间的唯一 ID
|
|
89
|
-
* 格式:
|
|
90
|
-
*
|
|
91
|
-
*
|
|
92
|
-
* @returns 唯一 ID (14位纯数字)
|
|
88
|
+
* 格式: 毫秒时间戳(13位) + 3位后缀(100-999) = 16位纯数字
|
|
89
|
+
* 每毫秒起点基于时间戳偏移,后缀分布更均匀
|
|
90
|
+
* @returns 唯一 ID (16位纯数字)
|
|
93
91
|
*/
|
|
94
92
|
async genTimeID(): Promise<number> {
|
|
95
|
-
const timestamp =
|
|
96
|
-
const key = `${this.prefix}
|
|
93
|
+
const timestamp = Date.now();
|
|
94
|
+
const key = `${this.prefix}time_id:${timestamp}`;
|
|
97
95
|
|
|
98
96
|
const counter = await this.client.incr(key);
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
const counterSuffix = (counter % 10000).toString().padStart(4, '0');
|
|
102
|
-
|
|
103
|
-
return Number(`${timestamp}${counterSuffix}`);
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
/**
|
|
107
|
-
* 批量生成基于时间的唯一 ID
|
|
108
|
-
* 格式: 秒级时间戳(10位) + 4位自增 = 14位纯数字
|
|
109
|
-
* @param count - 需要生成的 ID 数量
|
|
110
|
-
* @returns ID 数组 (14位纯数字)
|
|
111
|
-
*/
|
|
112
|
-
async genTimeIDBatch(count: number): Promise<number[]> {
|
|
113
|
-
if (count <= 0) {
|
|
114
|
-
return [];
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
// 限制单次批量生成数量(每秒最多10000个)
|
|
118
|
-
const MAX_BATCH_SIZE = 10000;
|
|
119
|
-
if (count > MAX_BATCH_SIZE) {
|
|
120
|
-
throw new Error(`批量大小 ${count} 超过最大限制 ${MAX_BATCH_SIZE}`);
|
|
97
|
+
if (counter === 1) {
|
|
98
|
+
await this.client.expire(key, 1);
|
|
121
99
|
}
|
|
122
100
|
|
|
123
|
-
|
|
124
|
-
const
|
|
125
|
-
|
|
126
|
-
// 使用 INCRBY 一次性获取 N 个连续计数
|
|
127
|
-
const startCounter = await this.client.incrby(key, count);
|
|
128
|
-
await this.client.expire(key, 1);
|
|
129
|
-
|
|
130
|
-
// 生成 ID 数组
|
|
131
|
-
const ids: number[] = [];
|
|
132
|
-
for (let i = 0; i < count; i++) {
|
|
133
|
-
const counter = startCounter - count + i + 1; // 计算每个 ID 的计数值
|
|
134
|
-
const counterSuffix = (counter % 10000).toString().padStart(4, '0');
|
|
135
|
-
ids.push(Number(`${timestamp}${counterSuffix}`));
|
|
136
|
-
}
|
|
101
|
+
// 基于时间戳偏移起点,后缀 100-999 循环
|
|
102
|
+
const suffix = 100 + (((timestamp % 900) + counter - 1) % 900);
|
|
137
103
|
|
|
138
|
-
return
|
|
104
|
+
return Number(`${timestamp}${suffix}`);
|
|
139
105
|
}
|
|
140
106
|
|
|
141
107
|
/**
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "befly",
|
|
3
|
-
"version": "3.8.
|
|
3
|
+
"version": "3.8.34",
|
|
4
4
|
"description": "Befly - 为 Bun 专属打造的 TypeScript API 接口框架核心引擎",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"private": false,
|
|
@@ -67,13 +67,13 @@
|
|
|
67
67
|
"befly-shared": "^1.1.2",
|
|
68
68
|
"chalk": "^5.6.2",
|
|
69
69
|
"es-toolkit": "^1.42.0",
|
|
70
|
-
"fast-jwt": "^6.0
|
|
70
|
+
"fast-jwt": "^6.1.0",
|
|
71
71
|
"fast-xml-parser": "^5.3.2",
|
|
72
72
|
"pathe": "^2.0.3",
|
|
73
73
|
"pino": "^10.1.0",
|
|
74
74
|
"pino-roll": "^4.0.0"
|
|
75
75
|
},
|
|
76
|
-
"gitHead": "
|
|
76
|
+
"gitHead": "e6528128e0532df2c2f0ad96e32bc73e167aac84",
|
|
77
77
|
"devDependencies": {
|
|
78
78
|
"typescript": "^5.9.3"
|
|
79
79
|
}
|
package/sync/syncDb/table.ts
CHANGED
|
@@ -124,7 +124,7 @@ export async function modifyTable(sql: SQL, tableName: string, fields: Record<st
|
|
|
124
124
|
}
|
|
125
125
|
} else {
|
|
126
126
|
const lenPart = isStringOrArrayType(fieldDef.type) ? ` 长度:${parseInt(String(fieldDef.max))}` : '';
|
|
127
|
-
Logger.debug(` + 新增字段 ${dbFieldName} (${fieldDef.type}${lenPart})`);
|
|
127
|
+
// Logger.debug(` + 新增字段 ${dbFieldName} (${fieldDef.type}${lenPart})`);
|
|
128
128
|
addClauses.push(generateDDLClause(fieldKey, fieldDef, true));
|
|
129
129
|
changed = true;
|
|
130
130
|
}
|
|
@@ -461,34 +461,26 @@ describe('RedisHelper - ID 生成', () => {
|
|
|
461
461
|
expect(typeof id1).toBe('number');
|
|
462
462
|
expect(typeof id2).toBe('number');
|
|
463
463
|
expect(id1).not.toBe(id2);
|
|
464
|
-
expect(id1.toString().length).toBe(
|
|
465
|
-
});
|
|
464
|
+
expect(id1.toString().length).toBe(16);
|
|
466
465
|
|
|
467
|
-
|
|
468
|
-
const
|
|
466
|
+
// 验证后缀在 100-999 范围内
|
|
467
|
+
const suffix1 = id1 % 1000;
|
|
468
|
+
const suffix2 = id2 % 1000;
|
|
469
|
+
expect(suffix1).toBeGreaterThanOrEqual(100);
|
|
470
|
+
expect(suffix1).toBeLessThan(1000);
|
|
471
|
+
expect(suffix2).toBeGreaterThanOrEqual(100);
|
|
472
|
+
expect(suffix2).toBeLessThan(1000);
|
|
473
|
+
});
|
|
469
474
|
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
475
|
+
test('genTimeID - 多次生成保持唯一', async () => {
|
|
476
|
+
const ids: number[] = [];
|
|
477
|
+
for (let i = 0; i < 10; i++) {
|
|
478
|
+
ids.push(await redis.genTimeID());
|
|
479
|
+
}
|
|
473
480
|
|
|
474
|
-
// 验证 ID 唯一性
|
|
475
481
|
const uniqueIds = new Set(ids);
|
|
476
482
|
expect(uniqueIds.size).toBe(10);
|
|
477
483
|
});
|
|
478
|
-
|
|
479
|
-
test('genTimeIDBatch - 空数组', async () => {
|
|
480
|
-
const ids = await redis.genTimeIDBatch(0);
|
|
481
|
-
expect(ids.length).toBe(0);
|
|
482
|
-
});
|
|
483
|
-
|
|
484
|
-
test('genTimeIDBatch - 超过最大限制', async () => {
|
|
485
|
-
try {
|
|
486
|
-
await redis.genTimeIDBatch(10001);
|
|
487
|
-
expect(true).toBe(false); // 不应该执行到这里
|
|
488
|
-
} catch (error: any) {
|
|
489
|
-
expect(error.message).toContain('超过最大限制');
|
|
490
|
-
}
|
|
491
|
-
});
|
|
492
484
|
});
|
|
493
485
|
|
|
494
486
|
describe('RedisHelper - 连接测试', () => {
|
package/types/redis.d.ts
CHANGED
|
@@ -46,10 +46,8 @@ export interface RedisHelper {
|
|
|
46
46
|
ping(): Promise<string>;
|
|
47
47
|
|
|
48
48
|
// ==================== ID 生成 ====================
|
|
49
|
-
/** 生成基于时间的唯一 ID (
|
|
49
|
+
/** 生成基于时间的唯一 ID (16位纯数字: 13位毫秒时间戳 + 3位后缀100-999) */
|
|
50
50
|
genTimeID(): Promise<number>;
|
|
51
|
-
/** 批量生成基于时间的唯一 ID */
|
|
52
|
-
genTimeIDBatch(count: number): Promise<number[]>;
|
|
53
51
|
|
|
54
52
|
// ==================== Set 操作 ====================
|
|
55
53
|
/** 向 Set 中添加一个或多个成员 */
|