feat: 完善管理员系统和用户管理模块

- 更新管理员控制器和数据库管理功能
- 完善管理员操作日志系统
- 添加全面的属性测试覆盖
- 优化用户管理和用户档案服务
- 更新代码检查规范文档

功能改进:
- 增强管理员权限验证
- 完善操作日志记录
- 优化数据库管理接口
- 提升系统安全性和可维护性
This commit is contained in:
moyin
2026-01-09 17:05:08 +08:00
parent 8816b29b0a
commit 5f662ef091
30 changed files with 3881 additions and 599 deletions

View File

@@ -14,6 +14,8 @@
* - 日志管理:自动清理和归档功能
*
* 最近修改:
* - 2026-01-09: 代码质量优化 - 使用常量替代硬编码字符串,提高代码一致性 (修改者: moyin)
* - 2026-01-09: 代码质量优化 - 拆分getStatistics长方法为多个私有方法提高可读性 (修改者: moyin)
* - 2026-01-08: 注释规范优化 - 修正@author字段更新版本号和修改记录 (修改者: moyin)
* - 2026-01-08: 注释规范优化 - 为接口添加注释,完善文档说明 (修改者: moyin)
* - 2026-01-08: 注释规范优化 - 添加类注释,完善服务文档说明 (修改者: moyin)
@@ -21,16 +23,16 @@
* - 2026-01-08: 功能新增 - 创建管理员操作日志服务 (修改者: assistant)
*
* @author moyin
* @version 1.2.0
* @version 1.4.0
* @since 2026-01-08
* @lastModified 2026-01-08
* @lastModified 2026-01-09
*/
import { Injectable, Logger } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { Repository } from 'typeorm';
import { AdminOperationLog } from './admin_operation_log.entity';
import { LOG_QUERY_LIMITS, USER_QUERY_LIMITS, LOG_RETENTION } from './admin_constants';
import { LOG_QUERY_LIMITS, USER_QUERY_LIMITS, LOG_RETENTION, OPERATION_TYPES, OPERATION_RESULTS } from './admin_constants';
/**
* 创建日志参数接口
@@ -45,7 +47,7 @@ import { LOG_QUERY_LIMITS, USER_QUERY_LIMITS, LOG_RETENTION } from './admin_cons
export interface CreateLogParams {
adminUserId: string;
adminUsername: string;
operationType: 'CREATE' | 'UPDATE' | 'DELETE' | 'QUERY' | 'BATCH';
operationType: keyof typeof OPERATION_TYPES;
targetType: string;
targetId?: string;
operationDescription: string;
@@ -53,7 +55,7 @@ export interface CreateLogParams {
requestParams?: Record<string, any>;
beforeData?: Record<string, any>;
afterData?: Record<string, any>;
operationResult: 'SUCCESS' | 'FAILED';
operationResult: keyof typeof OPERATION_RESULTS;
errorMessage?: string;
errorCode?: string;
durationMs: number;
@@ -104,6 +106,7 @@ export interface LogStatistics {
failedOperations: number;
operationsByType: Record<string, number>;
operationsByTarget: Record<string, number>;
operationsByAdmin: Record<string, number>;
averageDuration: number;
sensitiveOperations: number;
uniqueAdmins: number;
@@ -301,6 +304,133 @@ export class AdminOperationLogService {
}
}
/**
* 获取基础统计数据
*
* @param queryBuilder 查询构建器
* @returns 基础统计数据
*/
private async getBasicStatistics(queryBuilder: any): Promise<{
totalOperations: number;
successfulOperations: number;
failedOperations: number;
sensitiveOperations: number;
}> {
const totalOperations = await queryBuilder.getCount();
const successfulOperations = await queryBuilder
.clone()
.andWhere('log.operation_result = :result', { result: OPERATION_RESULTS.SUCCESS })
.getCount();
const failedOperations = totalOperations - successfulOperations;
const sensitiveOperations = await queryBuilder
.clone()
.andWhere('log.is_sensitive = :sensitive', { sensitive: true })
.getCount();
return {
totalOperations,
successfulOperations,
failedOperations,
sensitiveOperations
};
}
/**
* 获取操作类型统计
*
* @param queryBuilder 查询构建器
* @returns 操作类型统计
*/
private async getOperationTypeStatistics(queryBuilder: any): Promise<Record<string, number>> {
const operationTypeStats = await queryBuilder
.clone()
.select('log.operation_type', 'type')
.addSelect('COUNT(*)', 'count')
.groupBy('log.operation_type')
.getRawMany();
return operationTypeStats.reduce((acc, stat) => {
acc[stat.type] = parseInt(stat.count);
return acc;
}, {} as Record<string, number>);
}
/**
* 获取目标类型统计
*
* @param queryBuilder 查询构建器
* @returns 目标类型统计
*/
private async getTargetTypeStatistics(queryBuilder: any): Promise<Record<string, number>> {
const targetTypeStats = await queryBuilder
.clone()
.select('log.target_type', 'type')
.addSelect('COUNT(*)', 'count')
.groupBy('log.target_type')
.getRawMany();
return targetTypeStats.reduce((acc, stat) => {
acc[stat.type] = parseInt(stat.count);
return acc;
}, {} as Record<string, number>);
}
/**
* 获取管理员统计
*
* @param queryBuilder 查询构建器
* @returns 管理员统计
*/
private async getAdminStatistics(queryBuilder: any): Promise<Record<string, number>> {
const adminStats = await queryBuilder
.clone()
.select('log.admin_user_id', 'admin')
.addSelect('COUNT(*)', 'count')
.groupBy('log.admin_user_id')
.getRawMany();
if (!adminStats || !Array.isArray(adminStats)) {
return {};
}
return adminStats.reduce((acc, stat) => {
acc[stat.admin] = parseInt(stat.count);
return acc;
}, {} as Record<string, number>);
}
/**
* 获取性能统计
*
* @param queryBuilder 查询构建器
* @returns 性能统计
*/
private async getPerformanceStatistics(queryBuilder: any): Promise<{
averageDuration: number;
uniqueAdmins: number;
}> {
// 平均耗时
const avgDurationResult = await queryBuilder
.clone()
.select('AVG(log.duration_ms)', 'avgDuration')
.getRawOne();
const averageDuration = parseFloat(avgDurationResult?.avgDuration || '0');
// 唯一管理员数量
const uniqueAdminsResult = await queryBuilder
.clone()
.select('COUNT(DISTINCT log.admin_user_id)', 'uniqueAdmins')
.getRawOne();
const uniqueAdmins = parseInt(uniqueAdminsResult?.uniqueAdmins || '0');
return { averageDuration, uniqueAdmins };
}
/**
* 获取操作统计信息
*
@@ -319,72 +449,19 @@ export class AdminOperationLogService {
});
}
// 基础统计
const totalOperations = await queryBuilder.getCount();
const successfulOperations = await queryBuilder
.clone()
.andWhere('log.operation_result = :result', { result: 'SUCCESS' })
.getCount();
const failedOperations = totalOperations - successfulOperations;
const sensitiveOperations = await queryBuilder
.clone()
.andWhere('log.is_sensitive = :sensitive', { sensitive: true })
.getCount();
// 按操作类型统计
const operationTypeStats = await queryBuilder
.clone()
.select('log.operation_type', 'type')
.addSelect('COUNT(*)', 'count')
.groupBy('log.operation_type')
.getRawMany();
const operationsByType = operationTypeStats.reduce((acc, stat) => {
acc[stat.type] = parseInt(stat.count);
return acc;
}, {} as Record<string, number>);
// 按目标类型统计
const targetTypeStats = await queryBuilder
.clone()
.select('log.target_type', 'type')
.addSelect('COUNT(*)', 'count')
.groupBy('log.target_type')
.getRawMany();
const operationsByTarget = targetTypeStats.reduce((acc, stat) => {
acc[stat.type] = parseInt(stat.count);
return acc;
}, {} as Record<string, number>);
// 平均耗时
const avgDurationResult = await queryBuilder
.clone()
.select('AVG(log.duration_ms)', 'avgDuration')
.getRawOne();
const averageDuration = parseFloat(avgDurationResult?.avgDuration || '0');
// 唯一管理员数量
const uniqueAdminsResult = await queryBuilder
.clone()
.select('COUNT(DISTINCT log.admin_user_id)', 'uniqueAdmins')
.getRawOne();
const uniqueAdmins = parseInt(uniqueAdminsResult?.uniqueAdmins || '0');
// 获取各类统计数据
const basicStats = await this.getBasicStatistics(queryBuilder);
const operationsByType = await this.getOperationTypeStatistics(queryBuilder);
const operationsByTarget = await this.getTargetTypeStatistics(queryBuilder);
const operationsByAdmin = await this.getAdminStatistics(queryBuilder);
const performanceStats = await this.getPerformanceStatistics(queryBuilder);
const statistics: LogStatistics = {
totalOperations,
successfulOperations,
failedOperations,
...basicStats,
operationsByType,
operationsByTarget,
averageDuration,
sensitiveOperations,
uniqueAdmins
operationsByAdmin,
...performanceStats
};
this.logger.log('操作统计获取成功', statistics);