Initial WhaleTown V2 backend

This commit is contained in:
2026-07-20 02:00:52 +08:00
commit c995891c1f
265 changed files with 75689 additions and 0 deletions

View File

@@ -0,0 +1,592 @@
/**
* 管理员业务服务
*
* 功能描述:
* - 管理员登录认证业务逻辑
* - 用户管理业务功能(查询、密码重置、状态管理)
* - 系统日志管理功能
*
* 职责分离:
* - 业务逻辑编排和数据格式化
* - 调用核心服务完成具体操作
* - 异常处理和日志记录
*
* 主要方法:
* - login() - 管理员登录认证
* - listUsers() - 用户列表查询
* - getUser() - 单个用户查询
* - resetPassword() - 重置用户密码
* - updateUserStatus() - 修改用户状态
* - batchUpdateUserStatus() - 批量修改用户状态
* - getUserStatusStats() - 获取用户状态统计
* - getRuntimeLogs() - 获取运行日志
*
* 使用场景:
* - 后台管理系统的业务逻辑处理
* - 管理员权限相关的业务操作
*
* 最近修改:
* - 2026-01-07: 代码规范优化 - 修正文件命名规范,更新作者信息和修改记录
* - 2026-01-08: 注释规范优化 - 补充方法注释,添加@param、@returns、@throws和@example (修改者: moyin)
*
* @author moyin
* @version 1.0.2
* @since 2025-12-19
* @lastModified 2026-01-08
*/
import { Inject, Injectable, Logger, NotFoundException, BadRequestException } from '@nestjs/common';
import { AdminCoreService } from '../../core/admin_core/admin_core.service';
import { Users } from '../../core/db/users/users.entity';
import { UsersService } from '../../core/db/users/users.service';
import { UsersMemoryService } from '../../core/db/users/users_memory.service';
import { LogManagementService } from '../../core/utils/logger/log_management.service';
import { UserStatus, getUserStatusDescription } from '../user_mgmt/user_status.enum';
import { UserStatusDto, BatchUserStatusDto } from '../user_mgmt/user_status.dto';
import { getCurrentTimestamp } from './admin_utils';
import { USER_QUERY_LIMITS } from './admin_constants';
import {
UserStatusResponseDto,
BatchUserStatusResponseDto,
UserStatusStatsResponseDto,
UserStatusInfoDto,
BatchOperationResultDto
} from '../user_mgmt/user_status_response.dto';
export interface AdminApiResponse<T = any> {
success: boolean;
data?: T;
message: string;
error_code?: string;
}
@Injectable()
export class AdminService {
private readonly logger = new Logger(AdminService.name);
constructor(
private readonly adminCoreService: AdminCoreService,
@Inject('UsersService') private readonly usersService: UsersService | UsersMemoryService,
private readonly logManagementService: LogManagementService,
) {}
/**
* 记录操作日志
*
* @param level 日志级别
* @param message 日志消息
* @param context 日志上下文
*/
private logOperation(level: 'log' | 'warn' | 'error', message: string, context: Record<string, any>): void {
this.logger[level](message, {
...context,
timestamp: getCurrentTimestamp()
});
}
/**
* 获取日志目录绝对路径
*
* @returns 日志目录的绝对路径
*/
getLogDirAbsolutePath(): string {
return this.logManagementService.getLogDirAbsolutePath();
}
/**
* 管理员登录
*
* 功能描述:
* 验证管理员身份并生成JWT Token
*
* 业务逻辑:
* 1. 调用核心服务验证登录信息
* 2. 生成JWT Token
* 3. 返回登录结果
*
* @param identifier 登录标识符(用户名/邮箱/手机号)
* @param password 密码
* @returns 登录结果包含Token和管理员信息
*
* @example
* ```typescript
* const result = await adminService.login('admin', 'password123');
* ```
*/
async login(identifier: string, password: string): Promise<AdminApiResponse> {
try {
const result = await this.adminCoreService.login({ identifier, password });
return { success: true, data: result, message: '管理员登录成功' };
} catch (error) {
this.logger.error(`管理员登录失败: ${identifier}`, error instanceof Error ? error.stack : String(error));
return {
success: false,
message: error instanceof Error ? error.message : '管理员登录失败',
error_code: 'ADMIN_LOGIN_FAILED',
};
}
}
/**
* 获取用户列表
*
* 功能描述:
* 分页获取系统中的用户列表
*
* 业务逻辑:
* 1. 调用用户服务获取用户数据
* 2. 格式化用户信息
* 3. 返回分页结果
*
* @param limit 返回数量限制
* @param offset 偏移量
* @returns 用户列表和分页信息
*
* @example
* ```typescript
* const result = await adminService.listUsers(20, 0);
* ```
*/
async listUsers(limit: number, offset: number): Promise<AdminApiResponse<{ users: any[]; limit: number; offset: number }>> {
const users = await this.usersService.findAll(limit, offset);
return {
success: true,
data: {
users: users.map((u: Users) => this.formatUser(u)),
limit,
offset,
},
message: '用户列表获取成功',
};
}
/**
* 获取用户详情
*
* 功能描述:
* 根据用户ID获取指定用户的详细信息
*
* 业务逻辑:
* 1. 查询用户信息
* 2. 格式化用户数据
* 3. 返回用户详情
*
* @param id 用户ID
* @returns 用户详细信息
*
* @throws NotFoundException 当用户不存在时
*
* @example
* ```typescript
* const result = await adminService.getUser(BigInt(123));
* ```
*/
async getUser(id: bigint): Promise<AdminApiResponse<{ user: any }>> {
const user = await this.usersService.findOne(id);
return {
success: true,
data: { user: this.formatUser(user) },
message: '用户信息获取成功',
};
}
/**
* 重置用户密码
*
* 功能描述:
* 管理员直接为指定用户设置新密码
*
* 业务逻辑:
* 1. 验证用户是否存在
* 2. 调用核心服务重置密码
* 3. 记录操作日志
* 4. 返回重置结果
*
* @param id 用户ID
* @param newPassword 新密码
* @returns 重置结果
*
* @throws NotFoundException 当用户不存在时
*
* @example
* ```typescript
* const result = await adminService.resetPassword(BigInt(123), 'NewPass1234');
* ```
*/
async resetPassword(id: bigint, newPassword: string): Promise<AdminApiResponse> {
// 确认用户存在
const user = await this.usersService.findOne(id).catch((): null => null);
if (!user) {
throw new NotFoundException('用户不存在');
}
await this.adminCoreService.resetUserPassword(id, newPassword);
this.logger.log(`管理员重置密码成功: userId=${id.toString()}`);
return { success: true, message: '密码重置成功' };
}
/**
* 获取运行日志
*
* 功能描述:
* 获取系统运行日志的尾部内容
*
* 业务逻辑:
* 1. 调用日志管理服务获取日志
* 2. 返回日志内容和元信息
*
* @param lines 返回的日志行数,可选参数
* @returns 日志内容和元信息
*
* @example
* ```typescript
* const result = await adminService.getRuntimeLogs(200);
* ```
*/
async getRuntimeLogs(lines?: number): Promise<AdminApiResponse<{ file: string; updated_at: string; lines: string[] }>> {
const result = await this.logManagementService.getRuntimeLogTail({ lines });
return {
success: true,
data: result,
message: '运行日志获取成功',
};
}
private formatUser(user: Users) {
return {
id: user.id.toString(),
username: user.username,
nickname: user.nickname,
email: user.email,
email_verified: user.email_verified,
phone: user.phone,
avatar_url: user.avatar_url,
role: user.role,
status: user.status || UserStatus.ACTIVE, // 兼容旧数据
created_at: user.created_at,
updated_at: user.updated_at,
};
}
/**
* 格式化用户状态信息
*
* @param user 用户实体
* @returns 格式化的用户状态信息
*/
private formatUserStatus(user: Users): UserStatusInfoDto {
return {
id: user.id.toString(),
username: user.username,
nickname: user.nickname,
status: user.status || UserStatus.ACTIVE,
status_description: getUserStatusDescription(user.status || UserStatus.ACTIVE),
updated_at: user.updated_at
};
}
/**
* 修改用户状态
*
* 功能描述:
* 管理员修改指定用户的账户状态,支持激活、锁定、禁用等操作
*
* 业务逻辑:
* 1. 验证用户是否存在
* 2. 检查状态变更的合法性
* 3. 更新用户状态
* 4. 记录状态变更日志
*
* @param userId 用户ID
* @param userStatusDto 状态修改数据
* @returns 修改结果
*
* @throws NotFoundException 当用户不存在时
* @throws BadRequestException 当状态变更不合法时
*/
async updateUserStatus(userId: bigint, userStatusDto: UserStatusDto): Promise<UserStatusResponseDto> {
try {
this.logOperation('log', '开始修改用户状态', {
operation: 'update_user_status',
userId: userId.toString(),
newStatus: userStatusDto.status,
reason: userStatusDto.reason
});
// 1. 验证用户是否存在
const user = await this.usersService.findOne(userId);
if (!user) {
this.logOperation('warn', '修改用户状态失败:用户不存在', {
operation: 'update_user_status',
userId: userId.toString()
});
throw new NotFoundException('用户不存在');
}
// 2. 检查状态变更的合法性
if (user.status === userStatusDto.status) {
this.logOperation('warn', '修改用户状态失败:状态未发生变化', {
operation: 'update_user_status',
userId: userId.toString(),
currentStatus: user.status,
newStatus: userStatusDto.status
});
throw new BadRequestException('用户状态未发生变化');
}
// 3. 更新用户状态
const updatedUser = await this.usersService.update(userId, {
status: userStatusDto.status
});
// 4. 记录状态变更日志
this.logOperation('log', '用户状态修改成功', {
operation: 'update_user_status',
userId: userId.toString(),
oldStatus: user.status,
newStatus: userStatusDto.status,
reason: userStatusDto.reason
});
return {
success: true,
data: {
user: this.formatUserStatus(updatedUser),
reason: userStatusDto.reason
},
message: '用户状态修改成功'
};
} catch (error) {
this.logOperation('error', '修改用户状态失败', {
operation: 'update_user_status',
userId: userId.toString(),
error: error instanceof Error ? error.message : String(error)
});
if (error instanceof NotFoundException || error instanceof BadRequestException) {
throw error;
}
return {
success: false,
message: '用户状态修改失败',
error_code: 'USER_STATUS_UPDATE_FAILED'
};
}
}
/**
* 处理单个用户状态修改
*
* @param userIdStr 用户ID字符串
* @param newStatus 新状态
* @returns 处理结果
*/
private async processSingleUserStatus(
userIdStr: string,
newStatus: UserStatus
): Promise<{ success: true; user: UserStatusInfoDto } | { success: false; error: string }> {
try {
const userId = BigInt(userIdStr);
// 验证用户是否存在
const user = await this.usersService.findOne(userId);
if (!user) {
return { success: false, error: '用户不存在' };
}
// 检查状态是否需要变更
if (user.status === newStatus) {
return { success: false, error: '用户状态未发生变化' };
}
// 更新用户状态
const updatedUser = await this.usersService.update(userId, { status: newStatus });
return { success: true, user: this.formatUserStatus(updatedUser) };
} catch (error) {
return {
success: false,
error: error instanceof Error ? error.message : '未知错误'
};
}
}
/**
* 批量修改用户状态
*
* 功能描述:
* 管理员批量修改多个用户的账户状态
*
* 业务逻辑:
* 1. 验证用户ID列表
* 2. 逐个处理用户状态修改
* 3. 收集成功和失败的结果
* 4. 返回批量操作结果
*
* @param batchUserStatusDto 批量状态修改数据
* @returns 批量修改结果
*/
async batchUpdateUserStatus(batchUserStatusDto: BatchUserStatusDto): Promise<BatchUserStatusResponseDto> {
try {
this.logOperation('log', '开始批量修改用户状态', {
operation: 'batch_update_user_status',
userCount: batchUserStatusDto.userIds.length,
newStatus: batchUserStatusDto.status,
reason: batchUserStatusDto.reason
});
const successUsers: UserStatusInfoDto[] = [];
const failedUsers: Array<{ user_id: string; error: string }> = [];
// 逐个处理用户状态修改
for (const userIdStr of batchUserStatusDto.userIds) {
const result = await this.processSingleUserStatus(userIdStr, batchUserStatusDto.status);
if (result.success) {
successUsers.push(result.user);
} else {
failedUsers.push({ user_id: userIdStr, error: (result as { success: false; error: string }).error });
}
}
// 构建批量操作结果
const operationResult: BatchOperationResultDto = {
success_users: successUsers,
failed_users: failedUsers,
success_count: successUsers.length,
failed_count: failedUsers.length,
total_count: batchUserStatusDto.userIds.length
};
this.logOperation('log', '批量修改用户状态完成', {
operation: 'batch_update_user_status',
successCount: operationResult.success_count,
failedCount: operationResult.failed_count,
totalCount: operationResult.total_count
});
return {
success: true,
data: {
result: operationResult,
reason: batchUserStatusDto.reason
},
message: `批量用户状态修改完成,成功:${operationResult.success_count},失败:${operationResult.failed_count}`
};
} catch (error) {
this.logOperation('error', '批量修改用户状态失败', {
operation: 'batch_update_user_status',
error: error instanceof Error ? error.message : String(error)
});
return {
success: false,
message: '批量用户状态修改失败',
error_code: 'BATCH_USER_STATUS_UPDATE_FAILED'
};
}
}
/**
* 计算用户状态统计
*
* @param users 用户列表
* @returns 状态统计结果
*/
private calculateUserStatusStats(users: Users[]) {
const stats = {
active: 0,
inactive: 0,
locked: 0,
banned: 0,
deleted: 0,
pending: 0,
total: users.length
};
users.forEach((user: Users) => {
const status = user.status || UserStatus.ACTIVE;
switch (status) {
case UserStatus.ACTIVE:
stats.active++;
break;
case UserStatus.INACTIVE:
stats.inactive++;
break;
case UserStatus.LOCKED:
stats.locked++;
break;
case UserStatus.BANNED:
stats.banned++;
break;
case UserStatus.DELETED:
stats.deleted++;
break;
case UserStatus.PENDING:
stats.pending++;
break;
}
});
return stats;
}
/**
* 获取用户状态统计
*
* 功能描述:
* 获取各种用户状态的数量统计信息
*
* 业务逻辑:
* 1. 查询所有用户
* 2. 按状态分组统计
* 3. 计算各状态数量
* 4. 返回统计结果
*
* @returns 状态统计信息
*/
async getUserStatusStats(): Promise<UserStatusStatsResponseDto> {
try {
this.logOperation('log', '开始获取用户状态统计', {
operation: 'get_user_status_stats'
});
// 查询所有用户(这里可以优化为直接查询统计信息)
const allUsers = await this.usersService.findAll(USER_QUERY_LIMITS.MAX_USERS_FOR_STATS, 0);
// 计算各状态数量
const stats = this.calculateUserStatusStats(allUsers);
this.logOperation('log', '用户状态统计获取成功', {
operation: 'get_user_status_stats',
stats
});
return {
success: true,
data: {
stats,
timestamp: getCurrentTimestamp()
},
message: '用户状态统计获取成功'
};
} catch (error) {
this.logOperation('error', '获取用户状态统计失败', {
operation: 'get_user_status_stats',
error: error instanceof Error ? error.message : String(error)
});
return {
success: false,
message: '用户状态统计获取失败',
error_code: 'USER_STATUS_STATS_FAILED'
};
}
}
}