- 新增auth.schema.ts: 用户注册请求参数Schema定义和类型导出 - 新增auth.response.ts: 注册成功/失败响应格式定义 - 新增auth.service.ts: 注册业务逻辑,包含验证码验证、用户名邮箱唯一性检查、密码加密 - 新增auth.controller.ts: POST /api/auth/register路由实现,包含错误处理 - 新增auth.test.ts: 完整测试用例覆盖正常、异常、边界场景(14个测试全通过) - 修复数据库日期时间插入问题和路由路径重复问题 关联PRD: M2-基础用户系统,任务1.0完成
245 lines
7.2 KiB
TypeScript
245 lines
7.2 KiB
TypeScript
/**
|
||
* @file 雪花ID生成器
|
||
* @author AI助手 <ai@example.com>
|
||
* @date 2024-12-30
|
||
* @lastEditor AI助手
|
||
* @lastEditTime 2024-12-30
|
||
* @description 基于Twitter雪花算法的分布式ID生成器,支持自定义workerId和datacenterId
|
||
*/
|
||
|
||
/**
|
||
* 雪花ID生成器配置接口
|
||
* @property {number} workerId - 工作机器ID (0-31)
|
||
* @property {number} datacenterId - 数据中心ID (0-31)
|
||
* @property {number} sequence - 序列号起始值
|
||
* @property {number} epoch - 起始时间戳 (毫秒)
|
||
*/
|
||
export interface SnowflakeConfig {
|
||
/** 工作机器ID,范围0-31 */
|
||
workerId: number;
|
||
/** 数据中心ID,范围0-31 */
|
||
datacenterId: number;
|
||
/** 序列号起始值,默认0 */
|
||
sequence?: number;
|
||
/** 起始时间戳,默认2020-01-01 00:00:00 UTC */
|
||
epoch?: number;
|
||
}
|
||
|
||
/**
|
||
* 雪花ID生成器类
|
||
* 生成64位长整型ID,格式:时间戳(41位) + 数据中心ID(5位) + 工作机器ID(5位) + 序列号(12位)
|
||
*/
|
||
export class Snowflake {
|
||
/** 工作机器ID位数 */
|
||
private static readonly WORKER_ID_BITS = 5;
|
||
/** 数据中心ID位数 */
|
||
private static readonly DATACENTER_ID_BITS = 5;
|
||
/** 序列号位数 */
|
||
private static readonly SEQUENCE_BITS = 12;
|
||
|
||
/** 最大工作机器ID */
|
||
private static readonly MAX_WORKER_ID = (1 << Snowflake.WORKER_ID_BITS) - 1;
|
||
/** 最大数据中心ID */
|
||
private static readonly MAX_DATACENTER_ID = (1 << Snowflake.DATACENTER_ID_BITS) - 1;
|
||
/** 最大序列号 */
|
||
private static readonly MAX_SEQUENCE = (1 << Snowflake.SEQUENCE_BITS) - 1;
|
||
|
||
/** 工作机器ID左移位数 */
|
||
private static readonly WORKER_ID_SHIFT = Snowflake.SEQUENCE_BITS;
|
||
/** 数据中心ID左移位数 */
|
||
private static readonly DATACENTER_ID_SHIFT = Snowflake.SEQUENCE_BITS + Snowflake.WORKER_ID_BITS;
|
||
/** 时间戳左移位数 */
|
||
private static readonly TIMESTAMP_LEFT_SHIFT = Snowflake.SEQUENCE_BITS + Snowflake.WORKER_ID_BITS + Snowflake.DATACENTER_ID_BITS;
|
||
|
||
/** 工作机器ID */
|
||
private readonly workerId: number;
|
||
/** 数据中心ID */
|
||
private readonly datacenterId: number;
|
||
/** 起始时间戳 */
|
||
private readonly epoch: number;
|
||
/** 当前序列号 */
|
||
private sequence: number;
|
||
/** 上次生成ID的时间戳 */
|
||
private lastTimestamp: number;
|
||
|
||
/**
|
||
* 构造函数
|
||
* @param config 雪花ID配置
|
||
* @throws {Error} 当workerId或datacenterId超出范围时抛出错误
|
||
*/
|
||
constructor(config: SnowflakeConfig) {
|
||
// 验证workerId范围
|
||
if (config.workerId < 0 || config.workerId > Snowflake.MAX_WORKER_ID) {
|
||
throw new Error(`Worker ID must be between 0 and ${Snowflake.MAX_WORKER_ID}`);
|
||
}
|
||
|
||
// 验证datacenterId范围
|
||
if (config.datacenterId < 0 || config.datacenterId > Snowflake.MAX_DATACENTER_ID) {
|
||
throw new Error(`Datacenter ID must be between 0 and ${Snowflake.MAX_DATACENTER_ID}`);
|
||
}
|
||
|
||
this.workerId = config.workerId;
|
||
this.datacenterId = config.datacenterId;
|
||
this.sequence = config.sequence || 0;
|
||
this.epoch = config.epoch || 1577836800000; // 2020-01-01 00:00:00 UTC
|
||
this.lastTimestamp = -1;
|
||
}
|
||
|
||
/**
|
||
* 生成下一个雪花ID
|
||
* @returns {bigint} 64位雪花ID
|
||
* @throws {Error} 当系统时钟回拨时抛出错误
|
||
*/
|
||
public nextId(): bigint {
|
||
let timestamp = this.getCurrentTimestamp();
|
||
|
||
// 检查时钟回拨
|
||
if (timestamp < this.lastTimestamp) {
|
||
const timeDiff = this.lastTimestamp - timestamp;
|
||
throw new Error(`Clock moved backwards. Refusing to generate id for ${timeDiff} milliseconds`);
|
||
}
|
||
|
||
// 如果是同一毫秒内,递增序列号
|
||
if (timestamp === this.lastTimestamp) {
|
||
this.sequence = (this.sequence + 1) & Snowflake.MAX_SEQUENCE;
|
||
|
||
// 如果序列号溢出,等待下一毫秒
|
||
if (this.sequence === 0) {
|
||
timestamp = this.waitNextMillis(this.lastTimestamp);
|
||
}
|
||
} else {
|
||
// 不同毫秒,重置序列号
|
||
this.sequence = 0;
|
||
}
|
||
|
||
this.lastTimestamp = timestamp;
|
||
|
||
// 生成雪花ID
|
||
const id = ((BigInt(timestamp - this.epoch) << BigInt(Snowflake.TIMESTAMP_LEFT_SHIFT)) |
|
||
(BigInt(this.datacenterId) << BigInt(Snowflake.DATACENTER_ID_SHIFT)) |
|
||
(BigInt(this.workerId) << BigInt(Snowflake.WORKER_ID_SHIFT)) |
|
||
BigInt(this.sequence));
|
||
|
||
return id;
|
||
}
|
||
|
||
/**
|
||
* 解析雪花ID,返回各个组成部分
|
||
* @param id 雪花ID
|
||
* @returns {object} 解析结果
|
||
*/
|
||
public static parseId(id: bigint): {
|
||
timestamp: number;
|
||
datacenterId: number;
|
||
workerId: number;
|
||
sequence: number;
|
||
createdAt: Date;
|
||
} {
|
||
const timestamp = Number((id >> BigInt(Snowflake.TIMESTAMP_LEFT_SHIFT)) & BigInt((1 << 41) - 1));
|
||
const datacenterId = Number((id >> BigInt(Snowflake.DATACENTER_ID_SHIFT)) & BigInt(Snowflake.MAX_DATACENTER_ID));
|
||
const workerId = Number((id >> BigInt(Snowflake.WORKER_ID_SHIFT)) & BigInt(Snowflake.MAX_WORKER_ID));
|
||
const sequence = Number(id & BigInt(Snowflake.MAX_SEQUENCE));
|
||
|
||
// 计算创建时间(使用默认epoch)
|
||
const epoch = 1577836800000; // 2020-01-01 00:00:00 UTC
|
||
const createdAt = new Date(epoch + timestamp);
|
||
|
||
return {
|
||
timestamp,
|
||
datacenterId,
|
||
workerId,
|
||
sequence,
|
||
createdAt,
|
||
};
|
||
}
|
||
|
||
/**
|
||
* 获取当前时间戳(毫秒)
|
||
* @returns {number} 当前时间戳
|
||
*/
|
||
private getCurrentTimestamp(): number {
|
||
return Date.now();
|
||
}
|
||
|
||
/**
|
||
* 等待下一毫秒
|
||
* @param lastTimestamp 上次时间戳
|
||
* @returns {number} 新的时间戳
|
||
*/
|
||
private waitNextMillis(lastTimestamp: number): number {
|
||
let timestamp = this.getCurrentTimestamp();
|
||
while (timestamp <= lastTimestamp) {
|
||
timestamp = this.getCurrentTimestamp();
|
||
}
|
||
return timestamp;
|
||
}
|
||
|
||
/**
|
||
* 获取配置信息
|
||
* @returns {object} 当前配置
|
||
*/
|
||
public getConfig(): {
|
||
workerId: number;
|
||
datacenterId: number;
|
||
sequence: number;
|
||
epoch: number;
|
||
lastTimestamp: number;
|
||
} {
|
||
return {
|
||
workerId: this.workerId,
|
||
datacenterId: this.datacenterId,
|
||
sequence: this.sequence,
|
||
epoch: this.epoch,
|
||
lastTimestamp: this.lastTimestamp,
|
||
};
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 雪花ID生成器单例实例
|
||
* 使用默认配置:workerId=1, datacenterId=1
|
||
*/
|
||
let snowflakeInstance: Snowflake | null = null;
|
||
|
||
/**
|
||
* 获取雪花ID生成器单例实例
|
||
* @param config 可选配置,如果不提供则使用默认配置
|
||
* @returns {Snowflake} 雪花ID生成器实例
|
||
*/
|
||
export function getSnowflake(config?: Partial<SnowflakeConfig>): Snowflake {
|
||
if (!snowflakeInstance) {
|
||
const defaultConfig: SnowflakeConfig = {
|
||
workerId: 1,
|
||
datacenterId: 1,
|
||
...config,
|
||
};
|
||
snowflakeInstance = new Snowflake(defaultConfig);
|
||
}
|
||
return snowflakeInstance;
|
||
}
|
||
|
||
/**
|
||
* 生成下一个雪花ID(便捷方法)
|
||
* @returns {bigint} 64位雪花ID
|
||
*/
|
||
export function nextId(): bigint {
|
||
return getSnowflake().nextId();
|
||
}
|
||
|
||
/**
|
||
* 解析雪花ID(便捷方法)
|
||
* @param id 雪花ID
|
||
* @returns {object} 解析结果
|
||
*/
|
||
export function parseId(id: bigint) {
|
||
return Snowflake.parseId(id);
|
||
}
|
||
|
||
/**
|
||
* 创建自定义配置的雪花ID生成器
|
||
* @param config 雪花ID配置
|
||
* @returns {Snowflake} 新的雪花ID生成器实例
|
||
*/
|
||
export function createSnowflake(config: SnowflakeConfig): Snowflake {
|
||
return new Snowflake(config);
|
||
}
|