cursor-init/src/utils/snowflake.ts
expressgy 9a76d91307 feat: 实现用户注册接口 (任务1.0)
- 新增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完成
2025-07-06 01:21:09 +08:00

245 lines
7.2 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/**
* @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);
}