name: uuid description: ‘UUID生成技能 - 用于实体ID的通用唯一标识符v4和v7版本。适用于ng-events建筑工地进度跟踪系统。’
UUID - 通用唯一标识符
触发模式:“UUID”、“唯一ID”、“标识符”、“v4”、“v7”、“uuidv4”、“uuidv7”
概述
用于在JavaScript/TypeScript应用程序中生成符合RFC9562标准的唯一标识符的UUID库。
包: uuid@13.0.0
标准: RFC9562(原RFC4122)
核心功能
1. v4() - 随机UUID(最常用)
使用加密安全随机值生成版本4 UUID。
import { v4 as uuidv4 } from 'uuid';
// 生成随机UUID
const taskId = uuidv4();
// '9b1deb4d-3b7d-4bad-9bdd-2b0d7b3dcb6d'
// 在实体创建中使用
interface Task {
id: string;
title: string;
createdAt: Date;
}
function createTask(title: string): Task {
return {
id: uuidv4(),
title,
createdAt: new Date()
};
}
使用场景:
- 实体ID(任务、用户、蓝图)
- 会话ID
- 请求跟踪ID
- 文件上传ID
- 任何需要唯一标识符的场景
2. v7() - 基于时间戳的UUID(可排序)
生成版本7 UUID,包含Unix时间戳,支持自然时间顺序排序。
import { v7 as uuidv7 } from 'uuid';
// 生成基于时间戳的UUID
const orderId = uuidv7();
// '019a26ab-9a66-71a9-a89e-63c35fce4a5a'
// 多个UUID自然按时间排序
const ids = Array.from({ length: 5 }, () => uuidv7());
// 所有ID将按时间顺序排列
// 用于数据库主键
interface Order {
id: string; // v7 UUID - 按创建时间排序
customerId: string;
createdAt: Date;
}
使用场景:
- 需要时间顺序排序的数据库主键
- 时间序列数据中的事件ID
- 日志条目ID
- 审计跟踪记录
- 任何时间顺序重要的场景
优势:
- 按创建时间自然排序
- 更好的数据库索引性能
- 减少B树索引碎片
- 在存储/API中与UUID v4兼容
实际应用示例
使用UUID的任务仓库
import { Injectable, inject } from '@angular/core';
import { Firestore, collection, doc, setDoc, getDoc } from '@angular/fire/firestore';
import { v4 as uuidv4 } from 'uuid';
export interface Task {
id: string;
blueprintId: string;
title: string;
description: string;
status: 'pending' | 'in-progress' | 'completed';
createdAt: Date;
updatedAt: Date;
}
@Injectable({ providedIn: 'root' })
export class TaskRepository {
private firestore = inject(Firestore);
private tasksCollection = collection(this.firestore, 'tasks');
/**
* 使用UUID v4创建任务
*/
async create(task: Omit<Task, 'id' | 'createdAt' | 'updatedAt'>): Promise<Task> {
const id = uuidv4(); // 生成唯一ID
const now = new Date();
const newTask: Task = {
...task,
id,
createdAt: now,
updatedAt: now
};
const docRef = doc(this.tasksCollection, id);
await setDoc(docRef, newTask);
return newTask;
}
/**
* 通过UUID查找任务
*/
async findById(id: string): Promise<Task | null> {
const docRef = doc(this.tasksCollection, id);
const snapshot = await getDoc(docRef);
if (!snapshot.exists()) {
return null;
}
return { id: snapshot.id, ...snapshot.data() } as Task;
}
}
使用UUID v7的审计日志
import { Injectable, inject } from '@angular/core';
import { Firestore, collection, doc, setDoc } from '@angular/fire/firestore';
import { v7 as uuidv7 } from 'uuid';
export interface AuditLog {
id: string; // 用于时间排序的v7 UUID
userId: string;
action: string;
resource: string;
resourceId: string;
timestamp: Date;
metadata?: Record<string, any>;
}
@Injectable({ providedIn: 'root' })
export class AuditLogRepository {
private firestore = inject(Firestore);
private logsCollection = collection(this.firestore, 'auditLogs');
/**
* 使用v7 UUID创建审计日志(按时间排序)
*/
async log(
userId: string,
action: string,
resource: string,
resourceId: string,
metadata?: Record<string, any>
): Promise<AuditLog> {
const id = uuidv7(); // 基于时间戳的UUID
const log: AuditLog = {
id,
userId,
action,
resource,
resourceId,
timestamp: new Date(),
metadata
};
const docRef = doc(this.logsCollection, id);
await setDoc(docRef, log);
return log;
}
}
会话管理
import { Injectable } from '@angular/core';
import { v4 as uuidv4 } from 'uuid';
export interface Session {
id: string;
userId: string;
token: string;
createdAt: Date;
expiresAt: Date;
}
@Injectable({ providedIn: 'root' })
export class SessionService {
private sessions = new Map<string, Session>();
/**
* 使用UUID创建新会话
*/
createSession(userId: string, expiresInMs: number = 3600000): Session {
const sessionId = uuidv4();
const now = new Date();
const session: Session = {
id: sessionId,
userId,
token: this.generateToken(),
createdAt: now,
expiresAt: new Date(now.getTime() + expiresInMs)
};
this.sessions.set(sessionId, session);
return session;
}
/**
* 通过ID获取会话
*/
getSession(sessionId: string): Session | null {
return this.sessions.get(sessionId) || null;
}
private generateToken(): string {
return uuidv4(); // 使用UUID作为令牌
}
}
文件上传跟踪
import { Injectable, signal } from '@angular/core';
import { v4 as uuidv4 } from 'uuid';
export interface FileUpload {
id: string;
fileName: string;
fileSize: number;
uploadedBy: string;
uploadedAt: Date;
status: 'pending' | 'uploading' | 'completed' | 'failed';
progress: number;
url?: string;
}
@Injectable({ providedIn: 'root' })
export class FileUploadService {
private uploads = signal<Map<string, FileUpload>>(new Map());
/**
* 使用UUID跟踪开始文件上传
*/
startUpload(file: File, userId: string): string {
const uploadId = uuidv4();
const upload: FileUpload = {
id: uploadId,
fileName: file.name,
fileSize: file.size,
uploadedBy: userId,
uploadedAt: new Date(),
status: 'pending',
progress: 0
};
this.uploads.update(map => {
map.set(uploadId, upload);
return new Map(map);
});
return uploadId;
}
/**
* 更新上传进度
*/
updateProgress(uploadId: string, progress: number): void {
this.uploads.update(map => {
const upload = map.get(uploadId);
if (upload) {
upload.progress = progress;
upload.status = progress === 100 ? 'completed' : 'uploading';
map.set(uploadId, upload);
}
return new Map(map);
});
}
/**
* 通过ID获取上传信息
*/
getUpload(uploadId: string): FileUpload | undefined {
return this.uploads().get(uploadId);
}
}
最佳实践
1. 通用用途用v4,时间序列用v7
✅ 应该: 根据使用场景选择
// 通用实体ID - 使用v4
const taskId = uuidv4();
const userId = uuidv4();
// 时间序列或可排序ID - 使用v7
const logId = uuidv7();
const eventId = uuidv7();
2. 使用TypeScript类型
✅ 应该: 定义UUID品牌类型以确保安全
type UUID = string & { readonly __brand: unique symbol };
interface Task {
id: UUID;
title: string;
}
function createTaskId(): UUID {
return uuidv4() as UUID;
}
3. 验证UUID
✅ 应该: 验证UUID格式
import { validate as uuidValidate, version as uuidVersion } from 'uuid';
function isValidUUID(id: string): boolean {
return uuidValidate(id);
}
function isV4UUID(id: string): boolean {
return uuidValidate(id) && uuidVersion(id) === 4;
}
function isV7UUID(id: string): boolean {
return uuidValidate(id) && uuidVersion(id) === 7;
}
4. 不要将UUID存储为二进制(Firestore)
✅ 应该: 在Firestore中存储为字符串
// Firestore自动索引字符串ID
await setDoc(doc(collection, taskId), { /* 数据 */ });
❌ 不应该: 在Firestore中转换为二进制
// Firestore中不必要的复杂性
const binaryId = Buffer.from(taskId.replace(/-/g, ''), 'hex');
性能考虑
- 生成速度: v4比v7稍快
- 索引性能: v7提供更好的数据库索引局部性
- 存储: 两者都需要36字节作为字符串(128位+连字符)
- 碰撞概率: 两个版本实际上为零
CLI使用
# 生成v4 UUID
$ npx uuid
ddeb27fb-d9a0-4624-be4d-4615062daed4
# 生成v7 UUID
$ npx uuid v7
019a26ab-9a66-71a9-a89e-63c35fce4a5a
# 生成多个UUID
$ npx uuid && npx uuid && npx uuid
集成检查清单
- [ ] 安装 uuid@13.0.0
- [ ] 根据使用场景导入v4或v7
- [ ] 在仓库中用于实体ID
- [ ] 从外部源接收时验证UUID
- [ ] 在Firestore中存储为字符串
- [ ] 添加TypeScript类型以确保类型安全
- [ ] 在代码注释中记录UUID版本选择
反模式
❌ 在分布式系统中使用顺序ID:
let counter = 0;
const id = `task-${++counter}`; // 竞态条件,非全局唯一
✅ 使用UUID:
const id = uuidv4(); // 全局唯一,无需协调
❌ 手动解析UUID部分:
const timestamp = parseInt(uuid.substring(0, 8), 16); // 脆弱
✅ 使用库函数:
import { parse, version } from 'uuid';
const ver = version(uuid); // 正确解析
❌ 为安全关键操作在客户端生成UUID:
const sessionToken = uuidv4(); // 如果未正确种子化,可预测
✅ 在服务器端生成安全令牌:
// Firebase Auth安全处理令牌生成
const token = await auth.currentUser.getIdToken();
交叉引用
- firebase-repository - 实体ID的UUID
- blueprint-integration - 蓝图和成员ID
- event-bus-integration - 事件ID生成
- angular-component - 组件状态中的UUID
包信息
- 版本: 13.0.0
- 仓库: https://github.com/uuidjs/uuid
- RFC: RFC9562(UUID v7),RFC4122(UUID v4)
版本: 1.0
创建时间: 2025-12-25
维护者: ng-events(GigHub) 开发团队