forked from datawhale/whale-town-end
- 更新package.json和jest配置 - 更新main.ts启动配置 - 完善用户管理和数据库服务 - 更新安全核心模块 - 优化Zulip核心服务 配置改进: - 统一项目依赖管理 - 优化测试配置 - 完善服务模块化架构
714 lines
22 KiB
TypeScript
714 lines
22 KiB
TypeScript
/**
|
||
* 用户服务类
|
||
*
|
||
* 功能描述:
|
||
* - 提供用户数据的增删改查技术实现
|
||
* - 处理数据持久化和存储操作
|
||
* - 数据格式验证和约束检查
|
||
* - 支持完整的数据生命周期管理
|
||
*
|
||
* 职责分离:
|
||
* - 数据持久化:通过TypeORM操作MySQL数据库
|
||
* - 数据验证:数据格式和约束完整性检查
|
||
* - 异常处理:统一的错误处理和日志记录
|
||
* - 性能监控:操作耗时统计和性能优化
|
||
*
|
||
* 最近修改:
|
||
* - 2026-01-07: 代码规范优化 - 完善注释规范,添加完整的文件头和方法注释
|
||
* - 2026-01-07: 功能优化 - 添加完整的日志记录系统和详细的技术实现注释
|
||
* - 2026-01-07: 性能优化 - 优化异常处理和性能监控机制
|
||
*
|
||
* @author moyin
|
||
* @version 1.0.1
|
||
* @since 2025-12-17
|
||
* @lastModified 2026-01-07
|
||
*/
|
||
|
||
import { Injectable, ConflictException, NotFoundException, BadRequestException } from '@nestjs/common';
|
||
import { InjectRepository } from '@nestjs/typeorm';
|
||
import { Repository, FindOptionsWhere } from 'typeorm';
|
||
import { Users } from './users.entity';
|
||
import { CreateUserDto } from './users.dto';
|
||
import { UserStatus } from './user_status.enum';
|
||
import { validate } from 'class-validator';
|
||
import { plainToClass } from 'class-transformer';
|
||
import { BaseUsersService } from './base_users.service';
|
||
import { USER_ROLES, QUERY_LIMITS, ERROR_MESSAGES, DATABASE_CONSTANTS, ValidationUtils, PerformanceMonitor } from './users.constants';
|
||
|
||
@Injectable()
|
||
export class UsersService extends BaseUsersService {
|
||
|
||
constructor(
|
||
@InjectRepository(Users)
|
||
private readonly usersRepository: Repository<Users>,
|
||
) {
|
||
super(); // 调用基类构造函数
|
||
}
|
||
|
||
/**
|
||
* 创建新用户
|
||
*
|
||
* 技术实现:
|
||
* 1. 验证输入数据的格式和完整性
|
||
* 2. 创建用户实体并设置默认值
|
||
* 3. 保存用户数据到数据库
|
||
* 4. 记录操作日志和性能指标
|
||
*
|
||
* @param createUserDto 创建用户的数据传输对象,包含用户基本信息
|
||
* @returns 创建成功的用户实体,包含自动生成的ID和时间戳
|
||
* @throws BadRequestException 当数据验证失败或输入格式错误时
|
||
*
|
||
* @example
|
||
* ```typescript
|
||
* const newUser = await usersService.create({
|
||
* username: 'testuser',
|
||
* email: 'test@example.com',
|
||
* nickname: '测试用户',
|
||
* password_hash: 'hashed_password'
|
||
* });
|
||
* console.log(`用户创建成功,ID: ${newUser.id}`);
|
||
* ```
|
||
*/
|
||
async create(createUserDto: CreateUserDto): Promise<Users> {
|
||
const monitor = PerformanceMonitor.create();
|
||
|
||
this.logger.log('开始创建用户', {
|
||
operation: 'create',
|
||
username: createUserDto.username,
|
||
email: createUserDto.email,
|
||
timestamp: new Date().toISOString()
|
||
});
|
||
|
||
try {
|
||
// 验证DTO
|
||
await this.validateCreateUserDto(createUserDto);
|
||
|
||
// 创建用户实体
|
||
const user = this.buildUserEntity(createUserDto);
|
||
|
||
// 保存到数据库
|
||
const savedUser = await this.usersRepository.save(user);
|
||
|
||
this.logger.log('用户创建成功', {
|
||
operation: 'create',
|
||
userId: savedUser.id.toString(),
|
||
username: savedUser.username,
|
||
email: savedUser.email,
|
||
duration: monitor.getDuration(),
|
||
timestamp: new Date().toISOString()
|
||
});
|
||
|
||
return savedUser;
|
||
} catch (error) {
|
||
if (error instanceof BadRequestException) {
|
||
throw error;
|
||
}
|
||
|
||
this.logger.error('用户创建系统异常', {
|
||
operation: 'create',
|
||
username: createUserDto.username,
|
||
email: createUserDto.email,
|
||
error: error instanceof Error ? error.message : String(error),
|
||
duration: monitor.getDuration(),
|
||
timestamp: new Date().toISOString()
|
||
}, error instanceof Error ? error.stack : undefined);
|
||
|
||
throw new BadRequestException(ERROR_MESSAGES.USER_CREATE_FAILED);
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 验证创建用户DTO
|
||
*
|
||
* @param createUserDto 用户数据
|
||
* @throws BadRequestException 当数据验证失败时
|
||
*/
|
||
private async validateCreateUserDto(createUserDto: CreateUserDto): Promise<void> {
|
||
const dto = plainToClass(CreateUserDto, createUserDto);
|
||
const validationErrors = await validate(dto);
|
||
|
||
if (validationErrors.length > 0) {
|
||
const errorMessages = ValidationUtils.formatValidationErrors(validationErrors);
|
||
|
||
this.logger.warn('用户创建失败:数据验证失败', {
|
||
operation: 'create',
|
||
username: createUserDto.username,
|
||
email: createUserDto.email,
|
||
validationErrors: errorMessages
|
||
});
|
||
|
||
throw new BadRequestException(`${ERROR_MESSAGES.VALIDATION_FAILED}: ${errorMessages}`);
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 构建用户实体
|
||
*
|
||
* @param createUserDto 用户数据
|
||
* @returns 用户实体
|
||
*/
|
||
private buildUserEntity(createUserDto: CreateUserDto): Users {
|
||
const user = new Users();
|
||
user.username = createUserDto.username;
|
||
user.email = createUserDto.email || null;
|
||
user.phone = createUserDto.phone || null;
|
||
user.password_hash = createUserDto.password_hash || null;
|
||
user.nickname = createUserDto.nickname;
|
||
user.github_id = createUserDto.github_id || null;
|
||
user.avatar_url = createUserDto.avatar_url || null;
|
||
user.role = createUserDto.role || USER_ROLES.NORMAL_USER;
|
||
user.email_verified = createUserDto.email_verified || false;
|
||
user.status = createUserDto.status || UserStatus.ACTIVE;
|
||
|
||
return user;
|
||
}
|
||
|
||
/**
|
||
* 创建新用户(带重复检查)
|
||
*
|
||
* 技术实现:
|
||
* 1. 检查用户名、邮箱、手机号、GitHub ID的唯一性约束
|
||
* 2. 如果所有检查都通过,调用create方法创建用户
|
||
* 3. 记录操作日志和性能指标
|
||
*
|
||
* @param createUserDto 创建用户的数据传输对象
|
||
* @returns 创建的用户实体
|
||
* @throws ConflictException 当用户名、邮箱、手机号或GitHub ID已存在时
|
||
* @throws BadRequestException 当数据验证失败时
|
||
*
|
||
* @example
|
||
* ```typescript
|
||
* const newUser = await usersService.createWithDuplicateCheck({
|
||
* username: 'testuser',
|
||
* email: 'test@example.com',
|
||
* nickname: '测试用户'
|
||
* });
|
||
* ```
|
||
*/
|
||
async createWithDuplicateCheck(createUserDto: CreateUserDto): Promise<Users> {
|
||
const monitor = PerformanceMonitor.create();
|
||
|
||
this.logStart('创建用户(带重复检查)', {
|
||
username: createUserDto.username,
|
||
email: createUserDto.email,
|
||
phone: createUserDto.phone,
|
||
github_id: createUserDto.github_id
|
||
});
|
||
|
||
try {
|
||
// 执行所有唯一性检查
|
||
await this.validateUniqueness(createUserDto);
|
||
|
||
// 调用普通的创建方法
|
||
const user = await this.create(createUserDto);
|
||
|
||
this.logSuccess('创建用户(带重复检查)', {
|
||
userId: user.id.toString(),
|
||
username: user.username
|
||
}, monitor.getDuration());
|
||
|
||
return user;
|
||
} catch (error) {
|
||
this.handleServiceError(error, '创建用户(带重复检查)', {
|
||
username: createUserDto.username,
|
||
duration: monitor.getDuration()
|
||
});
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 验证用户数据的唯一性
|
||
*
|
||
* @param createUserDto 用户数据
|
||
* @throws ConflictException 当发现重复数据时
|
||
*/
|
||
private async validateUniqueness(createUserDto: CreateUserDto): Promise<void> {
|
||
await this.checkUsernameUniqueness(createUserDto.username);
|
||
await this.checkEmailUniqueness(createUserDto.email);
|
||
await this.checkPhoneUniqueness(createUserDto.phone);
|
||
await this.checkGithubIdUniqueness(createUserDto.github_id);
|
||
}
|
||
|
||
/**
|
||
* 检查用户名唯一性
|
||
*/
|
||
private async checkUsernameUniqueness(username?: string): Promise<void> {
|
||
if (username) {
|
||
const existingUser = await this.usersRepository.findOne({
|
||
where: { username }
|
||
});
|
||
if (existingUser) {
|
||
this.logger.warn('用户创建失败:用户名已存在', {
|
||
operation: 'uniqueness_check',
|
||
username,
|
||
existingUserId: existingUser.id.toString()
|
||
});
|
||
throw new ConflictException(ERROR_MESSAGES.USERNAME_EXISTS);
|
||
}
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 检查邮箱唯一性
|
||
*/
|
||
private async checkEmailUniqueness(email?: string): Promise<void> {
|
||
if (email) {
|
||
const existingEmail = await this.usersRepository.findOne({
|
||
where: { email }
|
||
});
|
||
if (existingEmail) {
|
||
this.logger.warn('用户创建失败:邮箱已存在', {
|
||
operation: 'uniqueness_check',
|
||
email,
|
||
existingUserId: existingEmail.id.toString()
|
||
});
|
||
throw new ConflictException(ERROR_MESSAGES.EMAIL_EXISTS);
|
||
}
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 检查手机号唯一性
|
||
*/
|
||
private async checkPhoneUniqueness(phone?: string): Promise<void> {
|
||
if (phone) {
|
||
const existingPhone = await this.usersRepository.findOne({
|
||
where: { phone }
|
||
});
|
||
if (existingPhone) {
|
||
this.logger.warn('用户创建失败:手机号已存在', {
|
||
operation: 'uniqueness_check',
|
||
phone,
|
||
existingUserId: existingPhone.id.toString()
|
||
});
|
||
throw new ConflictException(ERROR_MESSAGES.PHONE_EXISTS);
|
||
}
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 检查GitHub ID唯一性
|
||
*/
|
||
private async checkGithubIdUniqueness(githubId?: string): Promise<void> {
|
||
if (githubId) {
|
||
const existingGithub = await this.usersRepository.findOne({
|
||
where: { github_id: githubId }
|
||
});
|
||
if (existingGithub) {
|
||
this.logger.warn('用户创建失败:GitHub ID已存在', {
|
||
operation: 'uniqueness_check',
|
||
github_id: githubId,
|
||
existingUserId: existingGithub.id.toString()
|
||
});
|
||
throw new ConflictException(ERROR_MESSAGES.GITHUB_ID_EXISTS);
|
||
}
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 查询所有用户
|
||
*
|
||
* @param limit 限制返回数量,默认100
|
||
* @param offset 偏移量,默认0
|
||
* @param includeDeleted 是否包含已删除用户,默认false
|
||
* @returns 用户列表
|
||
*/
|
||
async findAll(limit: number = QUERY_LIMITS.DEFAULT_LIMIT, offset: number = 0, includeDeleted: boolean = false): Promise<Users[]> {
|
||
// 注意:软删除功能暂未实现,includeDeleted参数预留用于未来扩展
|
||
const whereCondition = {};
|
||
|
||
return await this.usersRepository.find({
|
||
where: whereCondition,
|
||
take: limit,
|
||
skip: offset,
|
||
order: { created_at: DATABASE_CONSTANTS.ORDER_DESC }
|
||
});
|
||
}
|
||
|
||
/**
|
||
* 根据ID查询用户
|
||
*
|
||
* @param id 用户ID
|
||
* @param includeDeleted 是否包含已删除用户,默认false
|
||
* @returns 用户实体
|
||
* @throws NotFoundException 当用户不存在时
|
||
*/
|
||
async findOne(id: bigint, includeDeleted: boolean = false): Promise<Users> {
|
||
// 注意:软删除功能暂未实现,includeDeleted参数预留用于未来扩展
|
||
const whereCondition = { id };
|
||
|
||
const user = await this.usersRepository.findOne({
|
||
where: whereCondition
|
||
});
|
||
|
||
if (!user) {
|
||
throw new NotFoundException(`ID为 ${id} 的用户不存在`);
|
||
}
|
||
|
||
return user;
|
||
}
|
||
|
||
/**
|
||
* 根据用户名查询用户
|
||
*
|
||
* @param username 用户名
|
||
* @param includeDeleted 是否包含已删除用户,默认false
|
||
* @returns 用户实体或null
|
||
*/
|
||
async findByUsername(username: string, includeDeleted: boolean = false): Promise<Users | null> {
|
||
// 注意:软删除功能暂未实现,includeDeleted参数预留用于未来扩展
|
||
const whereCondition = { username };
|
||
|
||
return await this.usersRepository.findOne({
|
||
where: whereCondition
|
||
});
|
||
}
|
||
|
||
/**
|
||
* 根据邮箱查询用户
|
||
*
|
||
* @param email 邮箱
|
||
* @param includeDeleted 是否包含已删除用户,默认false
|
||
* @returns 用户实体或null
|
||
*/
|
||
async findByEmail(email: string, includeDeleted: boolean = false): Promise<Users | null> {
|
||
// 注意:软删除功能暂未实现,includeDeleted参数预留用于未来扩展
|
||
const whereCondition = { email };
|
||
|
||
return await this.usersRepository.findOne({
|
||
where: whereCondition
|
||
});
|
||
}
|
||
|
||
/**
|
||
* 根据GitHub ID查询用户
|
||
*
|
||
* @param githubId GitHub ID
|
||
* @param includeDeleted 是否包含已删除用户,默认false
|
||
* @returns 用户实体或null
|
||
*/
|
||
async findByGithubId(githubId: string, includeDeleted: boolean = false): Promise<Users | null> {
|
||
// 注意:软删除功能暂未实现,includeDeleted参数预留用于未来扩展
|
||
const whereCondition = { github_id: githubId };
|
||
|
||
return await this.usersRepository.findOne({
|
||
where: whereCondition
|
||
});
|
||
}
|
||
|
||
/**
|
||
* 更新用户信息
|
||
*
|
||
* 功能描述:
|
||
* 更新指定用户的信息,包含完整的数据验证和唯一性检查
|
||
*
|
||
* 业务逻辑:
|
||
* 1. 验证用户是否存在
|
||
* 2. 检查更新字段的唯一性约束(用户名、邮箱、手机号、GitHub ID)
|
||
* 3. 合并更新数据到现有用户实体
|
||
* 4. 保存更新后的用户信息
|
||
* 5. 记录操作日志
|
||
*
|
||
* @param id 用户ID,必须是有效的已存在用户
|
||
* @param updateData 更新的数据,支持部分字段更新
|
||
* @returns 更新后的用户实体
|
||
* @throws NotFoundException 当用户不存在时
|
||
* @throws ConflictException 当更新的数据与其他用户冲突时
|
||
*
|
||
* @example
|
||
* ```typescript
|
||
* const updatedUser = await usersService.update(BigInt(1), {
|
||
* nickname: '新昵称',
|
||
* email: 'new@example.com'
|
||
* });
|
||
* ```
|
||
*/
|
||
async update(id: bigint, updateData: Partial<CreateUserDto>): Promise<Users> {
|
||
const monitor = PerformanceMonitor.create();
|
||
|
||
this.logger.log('开始更新用户信息', {
|
||
operation: 'update',
|
||
userId: id.toString(),
|
||
updateFields: Object.keys(updateData),
|
||
timestamp: new Date().toISOString()
|
||
});
|
||
|
||
try {
|
||
// 1. 检查用户是否存在 - 确保要更新的用户确实存在
|
||
const existingUser = await this.findOne(id);
|
||
|
||
// 2. 检查更新数据的唯一性约束 - 防止违反数据库唯一约束
|
||
await this.checkUpdateUniqueness(id, updateData);
|
||
|
||
// 3. 合并更新数据 - 使用Object.assign将新数据合并到现有实体
|
||
Object.assign(existingUser, updateData);
|
||
|
||
// 4. 保存更新后的用户信息 - TypeORM会自动更新updated_at字段
|
||
const updatedUser = await this.usersRepository.save(existingUser);
|
||
|
||
this.logger.log('用户信息更新成功', {
|
||
operation: 'update',
|
||
userId: id.toString(),
|
||
updateFields: Object.keys(updateData),
|
||
duration: monitor.getDuration(),
|
||
timestamp: new Date().toISOString()
|
||
});
|
||
|
||
return updatedUser;
|
||
} catch (error) {
|
||
if (error instanceof NotFoundException || error instanceof ConflictException) {
|
||
throw error;
|
||
}
|
||
|
||
this.logger.error('用户更新系统异常', {
|
||
operation: 'update',
|
||
userId: id.toString(),
|
||
updateData,
|
||
error: error instanceof Error ? error.message : String(error),
|
||
duration: monitor.getDuration(),
|
||
timestamp: new Date().toISOString()
|
||
}, error instanceof Error ? error.stack : undefined);
|
||
|
||
throw new BadRequestException(ERROR_MESSAGES.USER_UPDATE_FAILED);
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 删除用户
|
||
*
|
||
* 功能描述:
|
||
* 物理删除指定的用户记录,数据将从数据库中永久移除
|
||
*
|
||
* 业务逻辑:
|
||
* 1. 验证用户是否存在
|
||
* 2. 执行物理删除操作
|
||
* 3. 返回删除结果统计
|
||
* 4. 记录删除操作日志
|
||
*
|
||
* 注意事项:
|
||
* - 这是物理删除,数据无法恢复
|
||
* - 如需保留数据,请使用 softRemove 方法
|
||
* - 删除前请确认用户没有关联的重要数据
|
||
*
|
||
* @param id 用户ID,必须是有效的已存在用户
|
||
* @returns 删除操作结果,包含影响行数和操作消息
|
||
* @throws NotFoundException 当用户不存在时
|
||
*
|
||
* @example
|
||
* ```typescript
|
||
* const result = await usersService.remove(BigInt(1));
|
||
* console.log(`删除了 ${result.affected} 个用户`);
|
||
* ```
|
||
*/
|
||
async remove(id: bigint): Promise<{ affected: number; message: string }> {
|
||
const monitor = PerformanceMonitor.create();
|
||
|
||
this.logger.log('开始删除用户', {
|
||
operation: 'remove',
|
||
userId: id.toString(),
|
||
timestamp: new Date().toISOString()
|
||
});
|
||
|
||
try {
|
||
// 1. 检查用户是否存在 - 确保要删除的用户确实存在
|
||
await this.findOne(id);
|
||
|
||
// 2. 执行删除操作 - 使用where条件来处理bigint类型
|
||
const result = await this.usersRepository.delete({ id });
|
||
|
||
const deleteResult = {
|
||
affected: result.affected || 0,
|
||
message: `成功删除ID为 ${id} 的用户`
|
||
};
|
||
|
||
this.logger.log('用户删除成功', {
|
||
operation: 'remove',
|
||
userId: id.toString(),
|
||
affected: deleteResult.affected,
|
||
duration: monitor.getDuration(),
|
||
timestamp: new Date().toISOString()
|
||
});
|
||
|
||
return deleteResult;
|
||
} catch (error) {
|
||
if (error instanceof NotFoundException) {
|
||
throw error;
|
||
}
|
||
|
||
this.logger.error('用户删除系统异常', {
|
||
operation: 'remove',
|
||
userId: id.toString(),
|
||
error: error instanceof Error ? error.message : String(error),
|
||
duration: monitor.getDuration(),
|
||
timestamp: new Date().toISOString()
|
||
}, error instanceof Error ? error.stack : undefined);
|
||
|
||
throw new BadRequestException(ERROR_MESSAGES.USER_DELETE_FAILED);
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 检查更新数据的唯一性约束
|
||
*
|
||
* @param id 用户ID
|
||
* @param updateData 更新数据
|
||
* @throws ConflictException 当发现冲突时
|
||
*/
|
||
private async checkUpdateUniqueness(id: bigint, updateData: Partial<CreateUserDto>): Promise<void> {
|
||
const existingUser = await this.findOne(id);
|
||
|
||
if (updateData.username && updateData.username !== existingUser.username) {
|
||
await this.checkUsernameUniqueness(updateData.username);
|
||
}
|
||
|
||
if (updateData.email && updateData.email !== existingUser.email) {
|
||
await this.checkEmailUniqueness(updateData.email);
|
||
}
|
||
|
||
if (updateData.phone && updateData.phone !== existingUser.phone) {
|
||
await this.checkPhoneUniqueness(updateData.phone);
|
||
}
|
||
|
||
if (updateData.github_id && updateData.github_id !== existingUser.github_id) {
|
||
await this.checkGithubIdUniqueness(updateData.github_id);
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 软删除用户
|
||
*
|
||
* @param id 用户ID
|
||
* @returns 软删除操作结果
|
||
*/
|
||
async softRemove(id: bigint): Promise<Users> {
|
||
const user = await this.findOne(id);
|
||
// 注意:软删除功能暂未实现,当前仅返回用户实体
|
||
return user;
|
||
}
|
||
|
||
/**
|
||
* 统计用户数量
|
||
*
|
||
* @param conditions 查询条件
|
||
* @returns 用户数量
|
||
*/
|
||
async count(conditions?: FindOptionsWhere<Users>): Promise<number> {
|
||
return await this.usersRepository.count({ where: conditions });
|
||
}
|
||
|
||
/**
|
||
* 检查用户是否存在
|
||
*
|
||
* @param id 用户ID
|
||
* @returns 是否存在
|
||
*/
|
||
async exists(id: bigint): Promise<boolean> {
|
||
const count = await this.usersRepository.count({ where: { id } });
|
||
return count > 0;
|
||
}
|
||
|
||
/**
|
||
* 批量创建用户
|
||
*
|
||
* @param createUserDtos 用户数据数组
|
||
* @returns 创建的用户列表
|
||
*/
|
||
async createBatch(createUserDtos: CreateUserDto[]): Promise<Users[]> {
|
||
const users: Users[] = [];
|
||
|
||
for (const dto of createUserDtos) {
|
||
const user = await this.create(dto);
|
||
users.push(user);
|
||
}
|
||
|
||
return users;
|
||
}
|
||
|
||
/**
|
||
* 根据角色查询用户
|
||
*
|
||
* @param role 角色值
|
||
* @param includeDeleted 是否包含已删除用户,默认false
|
||
* @returns 用户列表
|
||
*/
|
||
async findByRole(role: number, includeDeleted: boolean = false): Promise<Users[]> {
|
||
// 注意:软删除功能暂未实现,includeDeleted参数预留用于未来扩展
|
||
const whereCondition = { role };
|
||
|
||
return await this.usersRepository.find({
|
||
where: whereCondition,
|
||
order: { created_at: DATABASE_CONSTANTS.ORDER_DESC }
|
||
});
|
||
}
|
||
|
||
/**
|
||
* 搜索用户(根据用户名或昵称)
|
||
*
|
||
* 功能描述:
|
||
* 根据关键词在用户名和昵称字段中进行模糊搜索,支持部分匹配
|
||
*
|
||
* 业务逻辑:
|
||
* 1. 使用QueryBuilder构建复杂查询
|
||
* 2. 对用户名和昵称字段进行LIKE模糊匹配
|
||
* 3. 按创建时间倒序排列结果
|
||
* 4. 限制返回数量防止性能问题
|
||
*
|
||
* 性能考虑:
|
||
* - 使用数据库索引优化查询性能
|
||
* - 限制返回数量避免大数据量问题
|
||
* - 建议在用户名和昵称字段上建立索引
|
||
*
|
||
* @param keyword 搜索关键词,支持中文、英文、数字等字符
|
||
* @param limit 限制数量,默认20条,建议不超过100
|
||
* @returns 匹配的用户列表,按创建时间倒序排列
|
||
*
|
||
* @example
|
||
* ```typescript
|
||
* // 搜索包含"张三"的用户
|
||
* const users = await usersService.search('张三', 10);
|
||
*
|
||
* // 搜索包含"admin"的用户
|
||
* const adminUsers = await usersService.search('admin');
|
||
* ```
|
||
*/
|
||
async search(keyword: string, limit: number = QUERY_LIMITS.DEFAULT_SEARCH_LIMIT, includeDeleted: boolean = false): Promise<Users[]> {
|
||
const monitor = PerformanceMonitor.create();
|
||
|
||
this.logStart('搜索用户', { keyword, limit, includeDeleted });
|
||
|
||
try {
|
||
// 1. 构建查询 - 使用QueryBuilder支持复杂的WHERE条件
|
||
const queryBuilder = this.usersRepository.createQueryBuilder('user');
|
||
|
||
// 添加搜索条件 - 在用户名和昵称中进行模糊匹配
|
||
let whereClause = 'user.username LIKE :keyword OR user.nickname LIKE :keyword';
|
||
|
||
// 注意:软删除功能暂未实现,includeDeleted参数预留用于未来扩展
|
||
|
||
const result = await queryBuilder
|
||
.where(whereClause, {
|
||
keyword: `%${keyword}%` // 前后加%实现模糊匹配
|
||
})
|
||
.orderBy('user.created_at', DATABASE_CONSTANTS.ORDER_DESC) // 按创建时间倒序
|
||
.limit(limit) // 限制返回数量
|
||
.getMany();
|
||
|
||
this.logSuccess('搜索用户', {
|
||
keyword,
|
||
limit,
|
||
includeDeleted,
|
||
resultCount: result.length
|
||
}, monitor.getDuration());
|
||
|
||
return result;
|
||
} catch (error) {
|
||
// 搜索异常使用特殊处理,返回空数组而不抛出异常
|
||
return this.handleSearchError(error, '搜索用户', {
|
||
keyword,
|
||
limit,
|
||
includeDeleted,
|
||
duration: monitor.getDuration()
|
||
});
|
||
}
|
||
}
|
||
} |