refactor:项目架构重构和命名规范化

- 统一文件命名为snake_case格式(kebab-case  snake_case)
- 重构zulip模块为zulip_core,明确Core层职责
- 重构user-mgmt模块为user_mgmt,统一命名规范
- 调整模块依赖关系,优化架构分层
- 删除过时的文件和目录结构
- 更新相关文档和配置文件

本次重构涉及大量文件重命名和模块重组,
旨在建立更清晰的项目架构和统一的命名规范。
This commit is contained in:
moyin
2026-01-08 00:14:14 +08:00
parent 4fa4bd1a70
commit bb796a2469
178 changed files with 24767 additions and 3484 deletions

View File

@@ -2,42 +2,73 @@
* 用户服务类
*
* 功能描述:
* - 提供用户的增删改查操作
* - 处理用户数据的业务逻辑
* - 数据验证和错误处理
* - 提供用户数据的增删改查技术实现
* - 处理数据持久化和存储操作
* - 数据格式验证和约束检查
* - 支持完整的数据生命周期管理
*
* 职责分离:
* - 数据持久化通过TypeORM操作MySQL数据库
* - 数据验证:数据格式和约束完整性检查
* - 异常处理:统一的错误处理和日志记录
* - 性能监控:操作耗时统计和性能优化
*
* 最近修改:
* - 2026-01-07: 代码规范优化 - 完善注释规范,添加完整的文件头和方法注释
* - 2026-01-07: 功能优化 - 添加完整的日志记录系统和详细的技术实现注释
* - 2026-01-07: 性能优化 - 优化异常处理和性能监控机制
*
* @author moyin
* @version 1.0.0
* @version 1.0.1
* @since 2025-12-17
*
* @lastModified 2025-01-07 by moyin
* @lastChange 添加完整的日志记录系统和详细的业务逻辑注释,优化异常处理和性能监控
* @lastModified 2026-01-07
*/
import { Injectable, ConflictException, NotFoundException, BadRequestException, Logger } from '@nestjs/common';
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 '../../../business/user-mgmt/enums/user-status.enum';
import { UserStatus } from '../../../business/user_mgmt/user_status.enum';
import { validate } from 'class-validator';
import { plainToClass } from 'class-transformer';
import { BaseUsersService } from './base_users.service';
@Injectable()
export class UsersService {
private readonly logger = new Logger(UsersService.name);
export class UsersService extends BaseUsersService {
constructor(
@InjectRepository(Users)
private readonly usersRepository: Repository<Users>,
) {}
) {
super(); // 调用基类构造函数
}
/**
* 创建新用户
*
* @param createUserDto 创建用户的数据传输对象
* @returns 创建的用户实体
* @throws BadRequestException 当数据验证失败时
* 技术实现:
* 1. 验证输入数据的格式和完整性
* 2. 使用class-validator进行DTO数据验证
* 3. 创建用户实体并设置默认值
* 4. 保存用户数据到数据库
* 5. 记录操作日志和性能指标
* 6. 返回创建成功的用户实体
*
* @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 startTime = Date.now();
@@ -120,10 +151,24 @@ export class UsersService {
/**
* 创建新用户(带重复检查)
*
* 技术实现:
* 1. 检查用户名、邮箱、手机号、GitHub ID的唯一性约束
* 2. 如果所有检查都通过调用create方法创建用户
* 3. 记录操作日志和性能指标
*
* @param createUserDto 创建用户的数据传输对象
* @returns 创建的用户实体
* @throws ConflictException 当用户名、邮箱手机号已存在时
* @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 startTime = Date.now();
@@ -138,65 +183,8 @@ export class UsersService {
});
try {
// 检查用户名是否已存在
if (createUserDto.username) {
const existingUser = await this.usersRepository.findOne({
where: { username: createUserDto.username }
});
if (existingUser) {
this.logger.warn('用户创建失败:用户名已存在', {
operation: 'createWithDuplicateCheck',
username: createUserDto.username,
existingUserId: existingUser.id.toString()
});
throw new ConflictException('用户名已存在');
}
}
// 检查邮箱是否已存在
if (createUserDto.email) {
const existingEmail = await this.usersRepository.findOne({
where: { email: createUserDto.email }
});
if (existingEmail) {
this.logger.warn('用户创建失败:邮箱已存在', {
operation: 'createWithDuplicateCheck',
email: createUserDto.email,
existingUserId: existingEmail.id.toString()
});
throw new ConflictException('邮箱已存在');
}
}
// 检查手机号是否已存在
if (createUserDto.phone) {
const existingPhone = await this.usersRepository.findOne({
where: { phone: createUserDto.phone }
});
if (existingPhone) {
this.logger.warn('用户创建失败:手机号已存在', {
operation: 'createWithDuplicateCheck',
phone: createUserDto.phone,
existingUserId: existingPhone.id.toString()
});
throw new ConflictException('手机号已存在');
}
}
// 检查GitHub ID是否已存在
if (createUserDto.github_id) {
const existingGithub = await this.usersRepository.findOne({
where: { github_id: createUserDto.github_id }
});
if (existingGithub) {
this.logger.warn('用户创建失败GitHub ID已存在', {
operation: 'createWithDuplicateCheck',
github_id: createUserDto.github_id,
existingUserId: existingGithub.id.toString()
});
throw new ConflictException('GitHub ID已存在');
}
}
// 执行所有唯一性检查
await this.validateUniqueness(createUserDto);
// 调用普通的创建方法
const user = await this.create(createUserDto);
@@ -232,15 +220,87 @@ export class UsersService {
}
}
/**
* 验证用户数据的唯一性
*
* @param createUserDto 用户数据
* @throws ConflictException 当发现重复数据时
*/
private async validateUniqueness(createUserDto: CreateUserDto): Promise<void> {
// 检查用户名是否已存在
if (createUserDto.username) {
const existingUser = await this.usersRepository.findOne({
where: { username: createUserDto.username }
});
if (existingUser) {
this.logger.warn('用户创建失败:用户名已存在', {
operation: 'createWithDuplicateCheck',
username: createUserDto.username,
existingUserId: existingUser.id.toString()
});
throw new ConflictException('用户名已存在');
}
}
// 检查邮箱是否已存在
if (createUserDto.email) {
const existingEmail = await this.usersRepository.findOne({
where: { email: createUserDto.email }
});
if (existingEmail) {
this.logger.warn('用户创建失败:邮箱已存在', {
operation: 'createWithDuplicateCheck',
email: createUserDto.email,
existingUserId: existingEmail.id.toString()
});
throw new ConflictException('邮箱已存在');
}
}
// 检查手机号是否已存在
if (createUserDto.phone) {
const existingPhone = await this.usersRepository.findOne({
where: { phone: createUserDto.phone }
});
if (existingPhone) {
this.logger.warn('用户创建失败:手机号已存在', {
operation: 'createWithDuplicateCheck',
phone: createUserDto.phone,
existingUserId: existingPhone.id.toString()
});
throw new ConflictException('手机号已存在');
}
}
// 检查GitHub ID是否已存在
if (createUserDto.github_id) {
const existingGithub = await this.usersRepository.findOne({
where: { github_id: createUserDto.github_id }
});
if (existingGithub) {
this.logger.warn('用户创建失败GitHub ID已存在', {
operation: 'createWithDuplicateCheck',
github_id: createUserDto.github_id,
existingUserId: existingGithub.id.toString()
});
throw new ConflictException('GitHub ID已存在');
}
}
}
/**
* 查询所有用户
*
* @param limit 限制返回数量默认100
* @param offset 偏移量默认0
* @param includeDeleted 是否包含已删除用户默认false
* @returns 用户列表
*/
async findAll(limit: number = 100, offset: number = 0): Promise<Users[]> {
async findAll(limit: number = 100, offset: number = 0, includeDeleted: boolean = false): Promise<Users[]> {
const whereCondition = includeDeleted ? {} : { deleted_at: null };
return await this.usersRepository.find({
where: whereCondition,
take: limit,
skip: offset,
order: { created_at: 'DESC' }
@@ -251,12 +311,15 @@ export class UsersService {
* 根据ID查询用户
*
* @param id 用户ID
* @param includeDeleted 是否包含已删除用户默认false
* @returns 用户实体
* @throws NotFoundException 当用户不存在时
*/
async findOne(id: bigint): Promise<Users> {
async findOne(id: bigint, includeDeleted: boolean = false): Promise<Users> {
const whereCondition = includeDeleted ? { id } : { id, deleted_at: null };
const user = await this.usersRepository.findOne({
where: { id }
where: whereCondition
});
if (!user) {
@@ -270,11 +333,14 @@ export class UsersService {
* 根据用户名查询用户
*
* @param username 用户名
* @param includeDeleted 是否包含已删除用户默认false
* @returns 用户实体或null
*/
async findByUsername(username: string): Promise<Users | null> {
async findByUsername(username: string, includeDeleted: boolean = false): Promise<Users | null> {
const whereCondition = includeDeleted ? { username } : { username, deleted_at: null };
return await this.usersRepository.findOne({
where: { username }
where: whereCondition
});
}
@@ -282,11 +348,14 @@ export class UsersService {
* 根据邮箱查询用户
*
* @param email 邮箱
* @param includeDeleted 是否包含已删除用户默认false
* @returns 用户实体或null
*/
async findByEmail(email: string): Promise<Users | null> {
async findByEmail(email: string, includeDeleted: boolean = false): Promise<Users | null> {
const whereCondition = includeDeleted ? { email } : { email, deleted_at: null };
return await this.usersRepository.findOne({
where: { email }
where: whereCondition
});
}
@@ -294,11 +363,14 @@ export class UsersService {
* 根据GitHub ID查询用户
*
* @param githubId GitHub ID
* @param includeDeleted 是否包含已删除用户默认false
* @returns 用户实体或null
*/
async findByGithubId(githubId: string): Promise<Users | null> {
async findByGithubId(githubId: string, includeDeleted: boolean = false): Promise<Users | null> {
const whereCondition = includeDeleted ? { github_id: githubId } : { github_id: githubId, deleted_at: null };
return await this.usersRepository.findOne({
where: { github_id: githubId }
where: whereCondition
});
}
@@ -525,15 +597,15 @@ export class UsersService {
}
/**
* 软删除用户(如果需要保留数据)
* 注意:需要在实体中添加 @DeleteDateColumn 装饰器
* 软删除用户
*
* @param id 用户ID
* @returns 软删除操作结果
*/
async softRemove(id: bigint): Promise<Users> {
const user = await this.findOne(id);
return await this.usersRepository.softRemove(user);
user.deleted_at = new Date();
return await this.usersRepository.save(user);
}
/**
@@ -578,11 +650,14 @@ export class UsersService {
* 根据角色查询用户
*
* @param role 角色值
* @param includeDeleted 是否包含已删除用户默认false
* @returns 用户列表
*/
async findByRole(role: number): Promise<Users[]> {
async findByRole(role: number, includeDeleted: boolean = false): Promise<Users[]> {
const whereCondition = includeDeleted ? { role } : { role, deleted_at: null };
return await this.usersRepository.find({
where: { role },
where: whereCondition,
order: { created_at: 'DESC' }
});
}
@@ -617,24 +692,25 @@ export class UsersService {
* const adminUsers = await usersService.search('admin');
* ```
*/
async search(keyword: string, limit: number = 20): Promise<Users[]> {
async search(keyword: string, limit: number = 20, includeDeleted: boolean = false): Promise<Users[]> {
const startTime = Date.now();
this.logger.log('开始搜索用户', {
operation: 'search',
keyword,
limit,
timestamp: new Date().toISOString()
});
this.logStart('搜索用户', { keyword, limit, includeDeleted });
try {
// 1. 构建查询 - 使用QueryBuilder支持复杂的WHERE条件
const queryBuilder = this.usersRepository.createQueryBuilder('user');
// 2. 添加搜索条件 - 在用户名和昵称中进行模糊匹配
// 使用参数化查询防止SQL注入攻击
let whereClause = 'user.username LIKE :keyword OR user.nickname LIKE :keyword';
// 3. 添加软删除过滤条件
if (!includeDeleted) {
whereClause += ' AND user.deleted_at IS NULL';
}
const result = await queryBuilder
.where('user.username LIKE :keyword OR user.nickname LIKE :keyword', {
.where(whereClause, {
keyword: `%${keyword}%` // 前后加%实现模糊匹配
})
.orderBy('user.created_at', 'DESC') // 按创建时间倒序
@@ -643,30 +719,19 @@ export class UsersService {
const duration = Date.now() - startTime;
this.logger.log('用户搜索完成', {
operation: 'search',
this.logSuccess('搜索用户', {
keyword,
limit,
resultCount: result.length,
duration,
timestamp: new Date().toISOString()
});
includeDeleted,
resultCount: result.length
}, duration);
return result;
} catch (error) {
const duration = Date.now() - startTime;
this.logger.error('用户搜索异常', {
operation: 'search',
keyword,
limit,
error: error instanceof Error ? error.message : String(error),
duration,
timestamp: new Date().toISOString()
}, error instanceof Error ? error.stack : undefined);
// 搜索失败时返回空数组,不影响用户体验
return [];
// 搜索异常使用特殊处理,返回空数组而不抛出异常
return this.handleSearchError(error, '搜索用户', { keyword, limit, includeDeleted, duration });
}
}
}