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

@@ -0,0 +1,186 @@
# UserMgmt 用户管理业务模块
UserMgmt 是应用的用户状态管理业务模块,提供完整的用户状态变更、批量操作、状态统计和审计功能,支持管理员对用户生命周期的全面管理,具备完善的权限控制、频率限制和操作审计能力。
## 用户状态管理
### updateUserStatus()
修改单个用户的账户状态,支持激活、锁定、禁用等操作,记录状态变更原因和审计日志。
### getUserStatusStats()
获取各种用户状态的数量统计信息,提供用户状态分布分析和业务指标计算。
### getUserStatusHistory()
查询指定用户的状态变更历史记录,提供完整的状态变更审计追踪。
## 批量操作管理
### batchUpdateUserStatus()
批量修改多个用户的账户状态,支持数量限制控制和操作结果统计反馈。
## 使用的项目内部依赖
### AdminService (来自 business/admin/admin.service)
底层管理员服务,提供用户状态修改的技术实现和数据持久化能力。
### AdminGuard (来自 business/admin/guards/admin.guard)
管理员权限守卫,确保只有具备管理员权限的用户才能执行状态管理操作。
### UserStatus (本模块)
用户状态枚举,定义用户的激活、锁定、禁用、删除、待审核等状态值。
### UserStatusDto (本模块)
用户状态修改请求数据传输对象,提供状态值和修改原因的数据验证规则。
### BatchUserStatusDto (本模块)
批量用户状态修改请求数据传输对象支持用户ID列表和批量操作数量限制验证。
### UserStatusResponseDto (本模块)
用户状态修改响应数据传输对象提供统一的API响应格式和错误信息封装。
### BatchUserStatusResponseDto (本模块)
批量用户状态修改响应数据传输对象,包含操作结果统计和成功失败详情。
### UserStatusStatsResponseDto (本模块)
用户状态统计响应数据传输对象,提供各状态用户数量和统计时间信息。
### ThrottlePresets (来自 core/security_core/throttle.decorator)
频率限制预设配置,控制管理员操作的频率以防止滥用。
### TimeoutPresets (来自 core/security_core/timeout.decorator)
超时控制预设配置,为不同类型的操作设置合理的超时时间。
### BATCH_OPERATION (本模块)
批量操作相关常量,定义批量操作的最大最小用户数量限制。
### VALIDATION (本模块)
验证规则常量,定义状态修改原因的最大长度等验证参数。
### ERROR_CODES (本模块)
错误代码常量,提供标准化的错误代码定义和错误处理支持。
### MESSAGES (本模块)
业务消息常量,定义用户友好的错误消息和提示信息。
### UTILS (本模块)
工具函数集合,提供时间戳生成等通用功能。
## 核心特性
### RESTful API设计
- 标准化的HTTP方法和状态码使用
- 统一的请求响应数据格式
- 完整的Swagger API文档自动生成
- 符合REST设计原则的资源路径规划
### 权限和安全控制
- AdminGuard管理员权限验证
- JWT Bearer Token身份认证
- 操作频率限制防止API滥用
- 请求超时控制避免资源占用
### 批量操作支持
- 支持1-100个用户的批量状态修改
- 批量操作结果详细统计和反馈
- 部分成功场景的优雅处理
- 批量操作数量限制和业务规则验证
### 数据验证和类型安全
- class-validator装饰器数据验证
- TypeScript类型系统完整支持
- 枚举值验证和错误提示
- 请求参数自动转换和验证
### 审计和日志记录
- 完整的操作审计日志记录
- 状态变更原因和时间戳记录
- 操作者身份和操作类型追踪
- 业务指标统计和分析支持
### 错误处理和用户体验
- 标准化的错误代码和消息
- 用户友好的错误提示信息
- 详细的操作结果反馈
- 优雅的异常处理和降级机制
## 潜在风险
### 批量操作性能风险
- 批量修改100个用户可能造成数据库性能压力
- 大量并发批量操作可能导致系统响应缓慢
- 建议监控批量操作的执行时间和数据库负载
### 权限控制风险
- AdminGuard依赖外部权限验证逻辑
- 权限验证失败可能导致未授权访问
- 建议定期审计管理员权限分配和使用情况
### 数据一致性风险
- 批量操作中部分成功可能导致数据不一致
- 并发状态修改可能产生竞态条件
- 建议在关键业务场景中使用事务控制
### 审计日志存储风险
- 大量的状态变更操作会产生海量审计日志
- 日志存储空间可能快速增长
- 建议制定日志轮转和归档策略
### API滥用风险
- 频率限制可能无法完全防止恶意调用
- 批量操作接口可能被用于攻击
- 建议结合IP限制和行为分析进行防护
### 业务逻辑风险
- 状态变更历史功能当前返回空数据
- 某些边界情况的业务规则可能不完善
- 建议完善状态变更历史功能和业务规则验证
## 使用示例
### 修改单个用户状态
```typescript
// 锁定违规用户
const result = await userManagementService.updateUserStatus(BigInt(123), {
status: UserStatus.LOCKED,
reason: '用户发布违规内容'
});
```
### 批量修改用户状态
```typescript
// 批量激活新用户
const result = await userManagementService.batchUpdateUserStatus({
userIds: ['456', '789', '101'],
status: UserStatus.ACTIVE,
reason: '批量激活通过审核的新用户'
});
```
### 获取用户状态统计
```typescript
// 获取用户状态分布统计
const stats = await userManagementService.getUserStatusStats();
console.log(`活跃用户: ${stats.data.stats.active}`);
```
## 模块配置
### 依赖模块
- AdminModule: 提供底层管理员服务支持
- AdminCoreModule: 提供核心管理功能和权限控制
### 导出服务
- UserManagementService: 用户管理业务逻辑服务
### API路由
- PUT /admin/users/:id/status - 修改用户状态
- POST /admin/users/batch-status - 批量修改用户状态
- GET /admin/users/status-stats - 获取用户状态统计
## 版本信息
- **版本**: 1.0.1
- **作者**: moyin
- **创建时间**: 2025-12-24
- **最后修改**: 2026-01-07
- **修改内容**: 代码规范优化,完善测试覆盖,增强功能文档

View File

@@ -0,0 +1,38 @@
/**
* 用户管理业务模块导出
*
* 功能描述:
* - 用户状态管理(激活、锁定、禁用等)
* - 批量用户操作
* - 用户状态统计和分析
* - 状态变更审计和历史记录
*
* 职责分离:
* - 统一导出用户管理模块的所有公共组件
* - 提供模块化的访问接口
* - 简化外部模块的依赖管理
*
* 最近修改:
* - 2026-01-07: 代码规范优化 - 修正文件命名规范,完善注释规范,更新作者信息 (修改者: moyin)
*
* @author moyin
* @version 1.0.1
* @since 2025-12-24
* @lastModified 2026-01-07
*/
// 模块
export * from './user_mgmt.module';
// 控制器
export * from './user_status.controller';
// 服务
export * from './user_management.service';
// DTO
export * from './user_status.dto';
export * from './user_status_response.dto';
// 常量
export * from './user_mgmt.constants';

View File

@@ -0,0 +1,453 @@
/**
* 用户管理业务服务测试
*
* 功能描述:
* - 测试用户状态管理业务逻辑
* - 测试批量用户操作功能
* - 测试用户状态统计功能
* - 测试状态变更审计功能
*
* 职责分离:
* - 单元测试覆盖所有公共方法
* - 异常情况和边界情况测试
* - Mock依赖服务的行为验证
*
* 最近修改:
* - 2026-01-07: 代码规范优化 - 创建完整的测试覆盖 (修改者: moyin)
*
* @author moyin
* @version 1.0.1
* @since 2026-01-07
* @lastModified 2026-01-07
*/
import { Test, TestingModule } from '@nestjs/testing';
import { Logger } from '@nestjs/common';
import { UserManagementService } from './user_management.service';
import { AdminService } from '../admin/admin.service';
import { UserStatusDto, BatchUserStatusDto } from './user_status.dto';
import { UserStatus } from './user_status.enum';
import { BATCH_OPERATION, ERROR_CODES, MESSAGES } from './user_mgmt.constants';
describe('UserManagementService', () => {
let service: UserManagementService;
let mockAdminService: jest.Mocked<AdminService>;
beforeEach(async () => {
const mockAdminServiceProvider = {
updateUserStatus: jest.fn(),
batchUpdateUserStatus: jest.fn(),
getUserStatusStats: jest.fn(),
};
const module: TestingModule = await Test.createTestingModule({
providers: [
UserManagementService,
{
provide: AdminService,
useValue: mockAdminServiceProvider,
},
],
}).compile();
service = module.get<UserManagementService>(UserManagementService);
mockAdminService = module.get(AdminService);
// Mock Logger to avoid console output during tests
jest.spyOn(Logger.prototype, 'log').mockImplementation();
jest.spyOn(Logger.prototype, 'warn').mockImplementation();
});
afterEach(() => {
jest.clearAllMocks();
});
describe('updateUserStatus', () => {
it('should update user status successfully', async () => {
// Arrange
const userId = BigInt(123);
const userStatusDto: UserStatusDto = {
status: UserStatus.ACTIVE,
reason: '用户申诉通过'
};
const expectedResult = {
success: true,
data: {
user: {
id: '123',
username: 'testuser',
nickname: '测试用户',
status: UserStatus.ACTIVE,
status_description: '正常',
updated_at: new Date()
},
reason: '用户申诉通过'
},
message: '用户状态修改成功'
};
mockAdminService.updateUserStatus.mockResolvedValue(expectedResult);
// Act
const result = await service.updateUserStatus(userId, userStatusDto);
// Assert
expect(result).toEqual(expectedResult);
expect(mockAdminService.updateUserStatus).toHaveBeenCalledWith(userId, userStatusDto);
expect(mockAdminService.updateUserStatus).toHaveBeenCalledTimes(1);
});
it('should handle update failure', async () => {
// Arrange
const userId = BigInt(999);
const userStatusDto: UserStatusDto = {
status: UserStatus.LOCKED,
reason: '违规操作'
};
const expectedResult = {
success: false,
message: '用户不存在',
error_code: 'USER_NOT_FOUND'
};
mockAdminService.updateUserStatus.mockResolvedValue(expectedResult);
// Act
const result = await service.updateUserStatus(userId, userStatusDto);
// Assert
expect(result).toEqual(expectedResult);
expect(mockAdminService.updateUserStatus).toHaveBeenCalledWith(userId, userStatusDto);
});
it('should log success when update succeeds', async () => {
// Arrange
const userId = BigInt(123);
const userStatusDto: UserStatusDto = {
status: UserStatus.ACTIVE,
reason: '测试'
};
const successResult = {
success: true,
data: {
user: {
id: '123',
username: 'testuser',
nickname: '测试用户',
status: UserStatus.ACTIVE,
status_description: '正常',
updated_at: new Date()
},
reason: '测试'
},
message: '成功'
};
mockAdminService.updateUserStatus.mockResolvedValue(successResult);
const logSpy = jest.spyOn(Logger.prototype, 'log');
// Act
await service.updateUserStatus(userId, userStatusDto);
// Assert
expect(logSpy).toHaveBeenCalledWith(
'用户管理:用户状态修改成功',
expect.objectContaining({
operation: 'user_mgmt_update_status_success',
userId: '123',
newStatus: UserStatus.ACTIVE
})
);
});
});
describe('batchUpdateUserStatus', () => {
it('should batch update user status successfully', async () => {
// Arrange
const batchUserStatusDto: BatchUserStatusDto = {
userIds: ['1', '2', '3'],
status: UserStatus.LOCKED,
reason: '批量锁定违规用户'
};
const expectedResult = {
success: true,
data: {
result: {
success_users: [],
failed_users: [],
success_count: 3,
failed_count: 0,
total_count: 3
},
reason: '批量锁定违规用户'
},
message: '批量用户状态修改完成'
};
mockAdminService.batchUpdateUserStatus.mockResolvedValue(expectedResult);
// Act
const result = await service.batchUpdateUserStatus(batchUserStatusDto);
// Assert
expect(result).toEqual(expectedResult);
expect(mockAdminService.batchUpdateUserStatus).toHaveBeenCalledWith(batchUserStatusDto);
});
it('should reject batch operation when user count exceeds limit', async () => {
// Arrange
const userIds = Array.from({ length: BATCH_OPERATION.MAX_USER_COUNT + 1 }, (_, i) => i.toString());
const batchUserStatusDto: BatchUserStatusDto = {
userIds,
status: UserStatus.LOCKED,
reason: '超限测试'
};
// Act
const result = await service.batchUpdateUserStatus(batchUserStatusDto);
// Assert
expect(result).toEqual({
success: false,
message: MESSAGES.BATCH_OPERATION_LIMIT_ERROR,
error_code: ERROR_CODES.BATCH_OPERATION_LIMIT_EXCEEDED
});
expect(mockAdminService.batchUpdateUserStatus).not.toHaveBeenCalled();
});
it('should handle empty user list', async () => {
// Arrange
const batchUserStatusDto: BatchUserStatusDto = {
userIds: [],
status: UserStatus.ACTIVE,
reason: '空列表测试'
};
const expectedResult = {
success: true,
data: {
result: {
success_users: [],
failed_users: [],
success_count: 0,
failed_count: 0,
total_count: 0
}
},
message: '批量操作完成'
};
mockAdminService.batchUpdateUserStatus.mockResolvedValue(expectedResult);
// Act
const result = await service.batchUpdateUserStatus(batchUserStatusDto);
// Assert
expect(result).toEqual(expectedResult);
});
it('should log warning when batch operation exceeds limit', async () => {
// Arrange
const userIds = Array.from({ length: BATCH_OPERATION.MAX_USER_COUNT + 1 }, (_, i) => i.toString());
const batchUserStatusDto: BatchUserStatusDto = {
userIds,
status: UserStatus.LOCKED,
reason: '超限测试'
};
const warnSpy = jest.spyOn(Logger.prototype, 'warn');
// Act
await service.batchUpdateUserStatus(batchUserStatusDto);
// Assert
expect(warnSpy).toHaveBeenCalledWith(
'用户管理:批量操作数量超限',
expect.objectContaining({
operation: 'user_mgmt_batch_update_limit_exceeded',
requestCount: BATCH_OPERATION.MAX_USER_COUNT + 1,
maxAllowed: BATCH_OPERATION.MAX_USER_COUNT
})
);
});
});
describe('getUserStatusStats', () => {
it('should get user status statistics successfully', async () => {
// Arrange
const expectedResult = {
success: true,
data: {
stats: {
active: 1250,
inactive: 45,
locked: 12,
banned: 8,
deleted: 3,
pending: 15,
total: 1333
},
timestamp: '2026-01-07T10:00:00.000Z'
},
message: '用户状态统计获取成功'
};
mockAdminService.getUserStatusStats.mockResolvedValue(expectedResult);
// Act
const result = await service.getUserStatusStats();
// Assert
expect(result).toEqual(expectedResult);
expect(mockAdminService.getUserStatusStats).toHaveBeenCalledTimes(1);
});
it('should handle statistics retrieval failure', async () => {
// Arrange
const expectedResult = {
success: false,
message: '统计数据获取失败',
error_code: 'STATS_RETRIEVAL_FAILED'
};
mockAdminService.getUserStatusStats.mockResolvedValue(expectedResult);
// Act
const result = await service.getUserStatusStats();
// Assert
expect(result).toEqual(expectedResult);
});
it('should calculate business metrics when stats are available', async () => {
// Arrange
const statsResult = {
success: true,
data: {
stats: {
active: 80,
inactive: 10,
locked: 5,
banned: 3,
deleted: 2,
pending: 0,
total: 100
},
timestamp: '2026-01-07T10:00:00.000Z'
},
message: '成功'
};
mockAdminService.getUserStatusStats.mockResolvedValue(statsResult);
const logSpy = jest.spyOn(Logger.prototype, 'log');
// Act
await service.getUserStatusStats();
// Assert
expect(logSpy).toHaveBeenCalledWith(
'用户管理:用户状态统计分析',
expect.objectContaining({
operation: 'user_mgmt_status_analysis',
totalUsers: 100,
activeUsers: 80,
activeRate: '80.00%',
problemUsers: 10 // locked + banned + deleted
})
);
});
it('should handle zero total users in statistics', async () => {
// Arrange
const statsResult = {
success: true,
data: {
stats: {
active: 0,
inactive: 0,
locked: 0,
banned: 0,
deleted: 0,
pending: 0,
total: 0
},
timestamp: '2026-01-07T10:00:00.000Z'
},
message: '成功'
};
mockAdminService.getUserStatusStats.mockResolvedValue(statsResult);
const logSpy = jest.spyOn(Logger.prototype, 'log');
// Act
await service.getUserStatusStats();
// Assert
expect(logSpy).toHaveBeenCalledWith(
'用户管理:用户状态统计分析',
expect.objectContaining({
activeRate: '0%'
})
);
});
});
describe('getUserStatusHistory', () => {
it('should return mock history data with default limit', async () => {
// Arrange
const userId = BigInt(123);
// Act
const result = await service.getUserStatusHistory(userId);
// Assert
expect(result).toEqual({
success: true,
data: {
user_id: '123',
history: [],
total_count: 0
},
message: '状态变更历史获取成功(当前返回空数据,待实现完整功能)'
});
});
it('should return mock history data with custom limit', async () => {
// Arrange
const userId = BigInt(456);
const customLimit = 20;
// Act
const result = await service.getUserStatusHistory(userId, customLimit);
// Assert
expect(result).toEqual({
success: true,
data: {
user_id: '456',
history: [],
total_count: 0
},
message: '状态变更历史获取成功(当前返回空数据,待实现完整功能)'
});
});
it('should log history query operation', async () => {
// Arrange
const userId = BigInt(789);
const limit = 15;
const logSpy = jest.spyOn(Logger.prototype, 'log');
// Act
await service.getUserStatusHistory(userId, limit);
// Assert
expect(logSpy).toHaveBeenCalledWith(
'用户管理:获取用户状态变更历史',
expect.objectContaining({
operation: 'user_mgmt_get_status_history',
userId: '789',
limit: 15
})
);
});
});
});

View File

@@ -0,0 +1,260 @@
/**
* 用户管理业务服务
*
* 功能描述:
* - 用户状态管理业务逻辑
* - 批量用户操作
* - 用户状态统计
* - 状态变更审计
*
* 职责分离:
* - 专注于用户管理相关的业务逻辑实现
* - 调用底层AdminService提供的技术能力
* - 提供用户管理特定的业务规则和流程控制
*
* 最近修改:
* - 2026-01-07: 代码规范优化 - 修正文件命名规范,完善注释规范,更新作者信息 (修改者: moyin)
*
* @author moyin
* @version 1.0.1
* @since 2025-12-24
* @lastModified 2026-01-07
*/
import { Injectable, Logger } from '@nestjs/common';
import { AdminService } from '../admin/admin.service';
import { UserStatusDto, BatchUserStatusDto } from './user_status.dto';
import {
UserStatusResponseDto,
BatchUserStatusResponseDto,
UserStatusStatsResponseDto
} from './user_status_response.dto';
import { BATCH_OPERATION, DEFAULTS, ERROR_CODES, MESSAGES, UTILS } from './user_mgmt.constants';
/**
* 用户管理业务服务
*
* 职责:
* - 实现用户状态管理的完整业务逻辑
* - 提供批量操作和状态统计的业务能力
* - 执行业务规则验证和审计日志记录
*
* 主要方法:
* - updateUserStatus() - 单个用户状态修改业务逻辑
* - batchUpdateUserStatus() - 批量用户状态修改业务逻辑
* - getUserStatusStats() - 用户状态统计业务逻辑
* - getUserStatusHistory() - 用户状态变更历史查询
*
* 使用场景:
* - 管理员执行用户状态管理操作
* - 系统自动化用户生命周期管理
* - 用户状态监控和数据分析
*/
@Injectable()
export class UserManagementService {
private readonly logger = new Logger(UserManagementService.name);
constructor(private readonly adminService: AdminService) {}
/**
* 修改用户状态
*
* 业务逻辑:
* 1. 验证状态变更的业务规则
* 2. 记录状态变更原因
* 3. 调用底层服务执行变更
* 4. 记录业务审计日志
*
* @param userId 用户ID
* @param userStatusDto 状态修改数据
* @returns 修改结果
* @throws NotFoundException 用户不存在时
* @throws BadRequestException 状态变更不符合业务规则时
*
* @example
* ```typescript
* const result = await service.updateUserStatus(BigInt(123), {
* status: UserStatus.ACTIVE,
* reason: '用户申诉通过,恢复正常状态'
* });
* ```
*/
async updateUserStatus(userId: bigint, userStatusDto: UserStatusDto): Promise<UserStatusResponseDto> {
this.logger.log('用户管理:开始修改用户状态', {
operation: 'user_mgmt_update_status',
userId: userId.toString(),
newStatus: userStatusDto.status,
reason: userStatusDto.reason,
timestamp: UTILS.getCurrentTimestamp()
});
// 调用底层管理员服务
const result = await this.adminService.updateUserStatus(userId, userStatusDto);
// 记录业务层日志
if (result.success) {
this.logger.log('用户管理:用户状态修改成功', {
operation: 'user_mgmt_update_status_success',
userId: userId.toString(),
newStatus: userStatusDto.status,
timestamp: UTILS.getCurrentTimestamp()
});
}
return result;
}
/**
* 批量修改用户状态
*
* 业务逻辑:
* 1. 验证批量操作的业务规则
* 2. 分批处理大量用户
* 3. 提供批量操作的进度反馈
* 4. 记录批量操作审计
*
* @param batchUserStatusDto 批量状态修改数据
* @returns 批量修改结果
* @throws BadRequestException 批量操作数量超限或参数无效时
* @throws InternalServerErrorException 批量操作执行失败时
*
* @example
* ```typescript
* const result = await service.batchUpdateUserStatus({
* userIds: ['123', '456'],
* status: UserStatus.LOCKED,
* reason: '批量锁定违规用户'
* });
* ```
*/
async batchUpdateUserStatus(batchUserStatusDto: BatchUserStatusDto): Promise<BatchUserStatusResponseDto> {
this.logger.log('用户管理:开始批量修改用户状态', {
operation: 'user_mgmt_batch_update_status',
userCount: batchUserStatusDto.userIds.length,
newStatus: batchUserStatusDto.status,
reason: batchUserStatusDto.reason,
timestamp: UTILS.getCurrentTimestamp()
});
// 业务规则:限制批量操作的数量
if (batchUserStatusDto.userIds.length > BATCH_OPERATION.MAX_USER_COUNT) {
this.logger.warn('用户管理:批量操作数量超限', {
operation: 'user_mgmt_batch_update_limit_exceeded',
requestCount: batchUserStatusDto.userIds.length,
maxAllowed: BATCH_OPERATION.MAX_USER_COUNT
});
return {
success: false,
message: MESSAGES.BATCH_OPERATION_LIMIT_ERROR,
error_code: ERROR_CODES.BATCH_OPERATION_LIMIT_EXCEEDED
};
}
// 调用底层管理员服务
const result = await this.adminService.batchUpdateUserStatus(batchUserStatusDto);
// 记录业务层日志
if (result.success) {
this.logger.log('用户管理:批量用户状态修改完成', {
operation: 'user_mgmt_batch_update_status_success',
successCount: result.data?.result.success_count || 0,
failedCount: result.data?.result.failed_count || 0,
timestamp: UTILS.getCurrentTimestamp()
});
}
return result;
}
/**
* 获取用户状态统计
*
* 业务逻辑:
* 1. 获取基础统计数据
* 2. 计算业务相关的指标
* 3. 提供状态分布分析
* 4. 缓存统计结果
*
* @returns 状态统计信息
* @throws InternalServerErrorException 统计数据获取失败时
*
* @example
* ```typescript
* const stats = await service.getUserStatusStats();
* // 返回包含各状态用户数量和分析指标的统计数据
* ```
*/
async getUserStatusStats(): Promise<UserStatusStatsResponseDto> {
this.logger.log('用户管理:获取用户状态统计', {
operation: 'user_mgmt_get_status_stats',
timestamp: UTILS.getCurrentTimestamp()
});
// 调用底层管理员服务
const result = await this.adminService.getUserStatusStats();
// 业务层可以在这里添加额外的统计分析
if (result.success && result.data) {
const stats = result.data.stats;
// 计算业务指标
const activeRate = stats.total > 0 ? (stats.active / stats.total * 100).toFixed(2) : '0';
const problemUserCount = stats.locked + stats.banned + stats.deleted;
this.logger.log('用户管理:用户状态统计分析', {
operation: 'user_mgmt_status_analysis',
totalUsers: stats.total,
activeUsers: stats.active,
activeRate: `${activeRate}%`,
problemUsers: problemUserCount,
timestamp: UTILS.getCurrentTimestamp()
});
}
return result;
}
/**
* 获取用户状态变更历史
*
* 业务逻辑:
* 1. 查询指定用户的状态变更记录
* 2. 提供状态变更的审计追踪
* 3. 支持时间范围和数量限制查询
* 4. 格式化历史记录数据
*
* @param userId 用户ID
* @param limit 返回数量限制
* @returns 状态变更历史
* @throws NotFoundException 用户不存在时
* @throws BadRequestException 查询参数无效时
*
* @example
* ```typescript
* const history = await service.getUserStatusHistory(BigInt(123), 20);
* // 返回用户最近20条状态变更记录
* ```
*/
async getUserStatusHistory(userId: bigint, limit: number = DEFAULTS.STATUS_HISTORY_LIMIT) {
this.logger.log('用户管理:获取用户状态变更历史', {
operation: 'user_mgmt_get_status_history',
userId: userId.toString(),
limit,
timestamp: UTILS.getCurrentTimestamp()
});
// 注意:此功能当前返回模拟数据,实际实现需要集成审计日志服务
// 建议在后续版本中实现完整的状态变更历史查询功能
return {
success: true,
data: {
user_id: userId.toString(),
history: [] as any[],
total_count: 0
},
message: '状态变更历史获取成功(当前返回空数据,待实现完整功能)'
};
}
}

View File

@@ -0,0 +1,71 @@
/**
* 用户管理业务常量
*
* 功能描述:
* - 定义用户管理模块的业务常量
* - 统一管理魔法数字和配置参数
* - 提供类型安全的常量访问
*
* 职责分离:
* - 业务规则常量定义和管理
* - 验证规则参数统一配置
* - 系统限制和默认值设置
*
* 最近修改:
* - 2026-01-07: 代码规范优化 - 创建常量定义文件,消除魔法数字 (修改者: moyin)
*
* @author moyin
* @version 1.0.1
* @since 2026-01-07
* @lastModified 2026-01-07
*/
/**
* 批量操作相关常量
*/
export const BATCH_OPERATION = {
/** 批量操作最大用户数量限制 */
MAX_USER_COUNT: 100,
/** 批量操作最小用户数量限制 */
MIN_USER_COUNT: 1,
} as const;
/**
* 验证规则相关常量
*/
export const VALIDATION = {
/** 状态修改原因最大长度 */
REASON_MAX_LENGTH: 200,
} as const;
/**
* 默认参数常量
*/
export const DEFAULTS = {
/** 状态变更历史查询默认数量限制 */
STATUS_HISTORY_LIMIT: 10,
} as const;
/**
* 错误代码常量
*/
export const ERROR_CODES = {
/** 批量操作数量超限错误代码 */
BATCH_OPERATION_LIMIT_EXCEEDED: 'BATCH_OPERATION_LIMIT_EXCEEDED',
} as const;
/**
* 业务消息常量
*/
export const MESSAGES = {
/** 批量操作数量超限错误消息 */
BATCH_OPERATION_LIMIT_ERROR: `批量操作数量不能超过${BATCH_OPERATION.MAX_USER_COUNT}个用户`,
} as const;
/**
* 工具函数
*/
export const UTILS = {
/** 获取当前时间戳 */
getCurrentTimestamp: (): string => new Date().toISOString(),
} as const;

View File

@@ -0,0 +1,436 @@
/**
* 用户管理模块集成测试
*
* 功能描述:
* - 测试用户管理模块的完整业务流程
* - 测试控制器与服务的集成
* - 测试真实的HTTP请求处理
* - 测试端到端的业务场景
*
* 职责分离:
* - 集成测试覆盖完整的业务流程
* - 测试模块间的协作和数据流
* - 验证真实环境下的功能表现
*
* 最近修改:
* - 2026-01-07: 代码规范优化 - 创建完整的集成测试覆盖 (修改者: moyin)
*
* @author moyin
* @version 1.0.1
* @since 2026-01-07
* @lastModified 2026-01-07
*/
import { Test, TestingModule } from '@nestjs/testing';
import { INestApplication } from '@nestjs/common';
import { UserStatusController } from './user_status.controller';
import { UserManagementService } from './user_management.service';
import { AdminService } from '../admin/admin.service';
import { AdminGuard } from '../admin/guards/admin.guard';
import { UserStatusDto, BatchUserStatusDto } from './user_status.dto';
import { UserStatus } from './user_status.enum';
import { BATCH_OPERATION, ERROR_CODES, MESSAGES } from './user_mgmt.constants';
describe('UserManagement Integration', () => {
let app: INestApplication;
let controller: UserStatusController;
let userManagementService: UserManagementService;
let mockAdminService: jest.Mocked<AdminService>;
beforeAll(async () => {
// Create mock AdminService
const mockAdminServiceProvider = {
updateUserStatus: jest.fn(),
batchUpdateUserStatus: jest.fn(),
getUserStatusStats: jest.fn(),
};
const moduleFixture: TestingModule = await Test.createTestingModule({
controllers: [UserStatusController],
providers: [
UserManagementService,
{
provide: AdminService,
useValue: mockAdminServiceProvider,
},
],
})
.overrideGuard(AdminGuard)
.useValue({ canActivate: jest.fn(() => true) })
.compile();
app = moduleFixture.createNestApplication();
await app.init();
controller = moduleFixture.get<UserStatusController>(UserStatusController);
userManagementService = moduleFixture.get<UserManagementService>(UserManagementService);
mockAdminService = moduleFixture.get(AdminService);
});
afterAll(async () => {
await app.close();
});
beforeEach(() => {
jest.clearAllMocks();
});
describe('Complete User Status Management Flow', () => {
it('should handle complete user status update workflow', async () => {
// Arrange
const userId = '123';
const userStatusDto: UserStatusDto = {
status: UserStatus.LOCKED,
reason: '用户违反社区规定'
};
const mockUpdateResult = {
success: true,
data: {
user: {
id: '123',
username: 'testuser',
nickname: '测试用户',
status: UserStatus.LOCKED,
status_description: '已锁定',
updated_at: new Date('2026-01-07T10:00:00.000Z')
},
reason: '用户违反社区规定'
},
message: '用户状态修改成功'
};
mockAdminService.updateUserStatus.mockResolvedValue(mockUpdateResult);
// Act - Controller calls Service, Service calls AdminService
const result = await controller.updateUserStatus(userId, userStatusDto);
// Assert - Verify complete integration
expect(result).toEqual(mockUpdateResult);
expect(mockAdminService.updateUserStatus).toHaveBeenCalledWith(BigInt(123), userStatusDto);
expect(result.data.user.status).toBe(UserStatus.LOCKED);
expect(result.data.reason).toBe('用户违反社区规定');
});
it('should handle complete batch update workflow', async () => {
// Arrange
const batchUserStatusDto: BatchUserStatusDto = {
userIds: ['1', '2', '3'],
status: UserStatus.BANNED,
reason: '批量处理违规用户'
};
const mockBatchResult = {
success: true,
data: {
result: {
success_users: [
{ id: '1', username: 'user1', nickname: '用户1', status: UserStatus.BANNED, status_description: '已封禁', updated_at: new Date() },
{ id: '2', username: 'user2', nickname: '用户2', status: UserStatus.BANNED, status_description: '已封禁', updated_at: new Date() }
],
failed_users: [
{ user_id: '3', error: '用户不存在' }
],
success_count: 2,
failed_count: 1,
total_count: 3
},
reason: '批量处理违规用户'
},
message: '批量用户状态修改完成'
};
mockAdminService.batchUpdateUserStatus.mockResolvedValue(mockBatchResult);
// Act - Complete batch workflow
const result = await controller.batchUpdateUserStatus(batchUserStatusDto);
// Assert - Verify batch integration
expect(result).toEqual(mockBatchResult);
expect(result.data.result.success_count).toBe(2);
expect(result.data.result.failed_count).toBe(1);
expect(mockAdminService.batchUpdateUserStatus).toHaveBeenCalledWith(batchUserStatusDto);
});
it('should handle complete statistics workflow', async () => {
// Arrange
const mockStatsResult = {
success: true,
data: {
stats: {
active: 1000,
inactive: 200,
locked: 50,
banned: 25,
deleted: 10,
pending: 30,
total: 1315
},
timestamp: '2026-01-07T10:00:00.000Z'
},
message: '用户状态统计获取成功'
};
mockAdminService.getUserStatusStats.mockResolvedValue(mockStatsResult);
// Act - Complete statistics workflow
const result = await controller.getUserStatusStats();
// Assert - Verify statistics integration
expect(result).toEqual(mockStatsResult);
expect(result.data.stats.total).toBe(1315);
expect(mockAdminService.getUserStatusStats).toHaveBeenCalledTimes(1);
});
});
describe('Business Logic Integration', () => {
it('should enforce batch operation limits through service layer', async () => {
// Arrange - Create request exceeding limits
const userIds = Array.from({ length: BATCH_OPERATION.MAX_USER_COUNT + 1 }, (_, i) => i.toString());
const batchUserStatusDto: BatchUserStatusDto = {
userIds,
status: UserStatus.LOCKED,
reason: '超限测试'
};
// Act - Service should reject before calling AdminService
const result = await userManagementService.batchUpdateUserStatus(batchUserStatusDto);
// Assert - Verify business rule enforcement
expect(result.success).toBe(false);
expect(result.message).toBe(MESSAGES.BATCH_OPERATION_LIMIT_ERROR);
expect(result.error_code).toBe(ERROR_CODES.BATCH_OPERATION_LIMIT_EXCEEDED);
expect(mockAdminService.batchUpdateUserStatus).not.toHaveBeenCalled();
});
it('should handle user status history integration', async () => {
// Arrange
const userId = BigInt(456);
const limit = 10;
// Act - Test history functionality (currently mock implementation)
const result = await userManagementService.getUserStatusHistory(userId, limit);
// Assert - Verify history integration
expect(result.success).toBe(true);
expect(result.data.user_id).toBe('456');
expect(result.data.history).toEqual([]);
expect(result.data.total_count).toBe(0);
expect(result.message).toContain('状态变更历史获取成功');
});
});
describe('Error Handling Integration', () => {
it('should handle service errors through complete stack', async () => {
// Arrange
const userId = '999';
const userStatusDto: UserStatusDto = {
status: UserStatus.ACTIVE,
reason: '测试错误处理'
};
const mockErrorResult = {
success: false,
message: '用户不存在',
error_code: 'USER_NOT_FOUND'
};
mockAdminService.updateUserStatus.mockResolvedValue(mockErrorResult);
// Act - Error propagation through layers
const result = await controller.updateUserStatus(userId, userStatusDto);
// Assert - Verify error handling integration
expect(result.success).toBe(false);
expect(result.message).toBe('用户不存在');
expect(result.error_code).toBe('USER_NOT_FOUND');
});
it('should handle batch operation partial failures', async () => {
// Arrange
const batchUserStatusDto: BatchUserStatusDto = {
userIds: ['1', '2', '999', '888'],
status: UserStatus.ACTIVE,
reason: '批量激活测试'
};
const mockPartialFailureResult = {
success: true,
data: {
result: {
success_users: [
{ id: '1', username: 'user1', nickname: '用户1', status: UserStatus.ACTIVE, status_description: '正常', updated_at: new Date() },
{ id: '2', username: 'user2', nickname: '用户2', status: UserStatus.ACTIVE, status_description: '正常', updated_at: new Date() }
],
failed_users: [
{ user_id: '999', error: '用户不存在' },
{ user_id: '888', error: '用户状态无法修改' }
],
success_count: 2,
failed_count: 2,
total_count: 4
},
reason: '批量激活测试'
},
message: '批量用户状态修改完成'
};
mockAdminService.batchUpdateUserStatus.mockResolvedValue(mockPartialFailureResult);
// Act - Handle partial failures
const result = await controller.batchUpdateUserStatus(batchUserStatusDto);
// Assert - Verify partial failure handling
expect(result.success).toBe(true);
expect(result.data.result.success_count).toBe(2);
expect(result.data.result.failed_count).toBe(2);
expect(result.data.result.failed_users).toHaveLength(2);
});
it('should handle statistics service failures', async () => {
// Arrange
const mockStatsError = {
success: false,
message: '数据库连接失败',
error_code: 'DATABASE_CONNECTION_ERROR'
};
mockAdminService.getUserStatusStats.mockResolvedValue(mockStatsError);
// Act - Handle statistics errors
const result = await controller.getUserStatusStats();
// Assert - Verify error propagation
expect(result.success).toBe(false);
expect(result.message).toBe('数据库连接失败');
expect(result.error_code).toBe('DATABASE_CONNECTION_ERROR');
});
});
describe('Data Flow Integration', () => {
it('should maintain data consistency through all layers', async () => {
// Arrange
const userId = '789';
const userStatusDto: UserStatusDto = {
status: UserStatus.INACTIVE,
reason: '长期未活跃'
};
const mockResult = {
success: true,
data: {
user: {
id: '789',
username: 'inactive_user',
nickname: '非活跃用户',
status: UserStatus.INACTIVE,
status_description: '非活跃',
updated_at: new Date('2026-01-07T10:00:00.000Z')
},
reason: '长期未活跃'
},
message: '用户状态修改成功'
};
mockAdminService.updateUserStatus.mockResolvedValue(mockResult);
// Act - Data flows through Controller -> Service -> AdminService
const result = await controller.updateUserStatus(userId, userStatusDto);
// Assert - Verify data consistency
expect(result.data.user.id).toBe(userId);
expect(result.data.user.status).toBe(userStatusDto.status);
expect(result.data.reason).toBe(userStatusDto.reason);
expect(mockAdminService.updateUserStatus).toHaveBeenCalledWith(
BigInt(789),
expect.objectContaining({
status: UserStatus.INACTIVE,
reason: '长期未活跃'
})
);
});
it('should handle BigInt conversion correctly in data flow', async () => {
// Arrange - Test large number handling
const largeUserId = '9007199254740991';
const userStatusDto: UserStatusDto = {
status: UserStatus.PENDING,
reason: '大数字ID测试'
};
const mockResult = {
success: true,
data: {
user: {
id: largeUserId,
username: 'large_id_user',
nickname: '大ID用户',
status: UserStatus.PENDING,
status_description: '待处理',
updated_at: new Date()
},
reason: '大数字ID测试'
},
message: '用户状态修改成功'
};
mockAdminService.updateUserStatus.mockResolvedValue(mockResult);
// Act - Test BigInt conversion in data flow
const result = await controller.updateUserStatus(largeUserId, userStatusDto);
// Assert - Verify BigInt handling
expect(result.data.user.id).toBe(largeUserId);
expect(mockAdminService.updateUserStatus).toHaveBeenCalledWith(
BigInt('9007199254740991'),
userStatusDto
);
});
});
describe('Performance Integration', () => {
it('should handle maximum allowed batch size efficiently', async () => {
// Arrange - Test with maximum allowed batch size
const userIds = Array.from({ length: BATCH_OPERATION.MAX_USER_COUNT }, (_, i) => `user_${i}`);
const batchUserStatusDto: BatchUserStatusDto = {
userIds,
status: UserStatus.ACTIVE,
reason: '性能测试'
};
const mockResult = {
success: true,
data: {
result: {
success_users: userIds.map(id => ({
id,
username: `user_${id}`,
nickname: `用户_${id}`,
status: UserStatus.ACTIVE,
status_description: '正常',
updated_at: new Date()
})),
failed_users: [],
success_count: userIds.length,
failed_count: 0,
total_count: userIds.length
},
reason: '性能测试'
},
message: '批量用户状态修改完成'
};
mockAdminService.batchUpdateUserStatus.mockResolvedValue(mockResult);
// Act - Process maximum batch size
const startTime = Date.now();
const result = await controller.batchUpdateUserStatus(batchUserStatusDto);
const endTime = Date.now();
// Assert - Verify performance and correctness
expect(result.success).toBe(true);
expect(result.data.result.total_count).toBe(BATCH_OPERATION.MAX_USER_COUNT);
expect(endTime - startTime).toBeLessThan(1000); // Should complete within 1 second
});
});
});

View File

@@ -0,0 +1,52 @@
/**
* 用户管理业务模块
*
* 功能描述:
* - 整合用户状态管理相关的所有组件
* - 提供用户生命周期管理功能
* - 支持批量操作和状态统计
*
* 职责分离:
* - 模块配置和依赖管理
* - 组件注册和导出控制
* - 业务模块边界定义
*
* 最近修改:
* - 2026-01-07: 代码规范优化 - 修正文件命名规范,完善注释规范,更新作者信息 (修改者: moyin)
*
* @author moyin
* @version 1.0.1
* @since 2025-12-24
* @lastModified 2026-01-07
*/
import { Module } from '@nestjs/common';
import { UserStatusController } from './user_status.controller';
import { UserManagementService } from './user_management.service';
import { AdminModule } from '../admin/admin.module';
import { AdminCoreModule } from '../../core/admin_core/admin_core.module';
/**
* 用户管理业务模块
*
* 职责:
* - 整合用户状态管理的所有业务组件
* - 管理模块间的依赖关系和配置
* - 提供统一的用户管理业务入口
*
* 主要组件:
* - UserStatusController - 用户状态管理API控制器
* - UserManagementService - 用户管理业务逻辑服务
*
* 使用场景:
* - 管理员进行用户状态管理操作
* - 批量用户操作和状态统计
* - 用户生命周期管理流程
*/
@Module({
imports: [AdminModule, AdminCoreModule],
controllers: [UserStatusController],
providers: [UserManagementService],
exports: [UserManagementService],
})
export class UserMgmtModule {}

View File

@@ -0,0 +1,586 @@
/**
* 用户状态管理控制器测试
*
* 功能描述:
* - 测试用户状态管理API接口
* - 测试HTTP请求处理和参数验证
* - 测试权限控制和频率限制
* - 测试响应格式和错误处理
*
* 职责分离:
* - 单元测试覆盖所有API端点
* - Mock业务服务依赖
* - 验证请求参数和响应格式
*
* 最近修改:
* - 2026-01-07: 代码规范优化 - 创建完整的控制器测试覆盖 (修改者: moyin)
*
* @author moyin
* @version 1.0.1
* @since 2026-01-07
* @lastModified 2026-01-07
*/
import { Test, TestingModule } from '@nestjs/testing';
import { Logger } from '@nestjs/common';
import { UserStatusController } from './user_status.controller';
import { UserManagementService } from './user_management.service';
import { AdminGuard } from '../admin/guards/admin.guard';
import { UserStatusDto, BatchUserStatusDto } from './user_status.dto';
import { UserStatus } from './user_status.enum';
import { BATCH_OPERATION } from './user_mgmt.constants';
describe('UserStatusController', () => {
let controller: UserStatusController;
let mockUserManagementService: jest.Mocked<UserManagementService>;
beforeEach(async () => {
const mockUserManagementServiceProvider = {
updateUserStatus: jest.fn(),
batchUpdateUserStatus: jest.fn(),
getUserStatusStats: jest.fn(),
};
const module: TestingModule = await Test.createTestingModule({
controllers: [UserStatusController],
providers: [
{
provide: UserManagementService,
useValue: mockUserManagementServiceProvider,
},
],
})
.overrideGuard(AdminGuard)
.useValue({ canActivate: jest.fn(() => true) })
.compile();
controller = module.get<UserStatusController>(UserStatusController);
mockUserManagementService = module.get(UserManagementService);
// Mock Logger to avoid console output during tests
jest.spyOn(Logger.prototype, 'log').mockImplementation();
});
afterEach(() => {
jest.clearAllMocks();
});
describe('updateUserStatus', () => {
it('should update user status successfully', async () => {
// Arrange
const userId = '123';
const userStatusDto: UserStatusDto = {
status: UserStatus.ACTIVE,
reason: '用户申诉通过'
};
const expectedResult = {
success: true,
data: {
user: {
id: '123',
username: 'testuser',
nickname: '测试用户',
status: UserStatus.ACTIVE,
status_description: '正常',
updated_at: new Date()
},
reason: '用户申诉通过'
},
message: '用户状态修改成功'
};
mockUserManagementService.updateUserStatus.mockResolvedValue(expectedResult);
// Act
const result = await controller.updateUserStatus(userId, userStatusDto);
// Assert
expect(result).toEqual(expectedResult);
expect(mockUserManagementService.updateUserStatus).toHaveBeenCalledWith(BigInt(123), userStatusDto);
expect(mockUserManagementService.updateUserStatus).toHaveBeenCalledTimes(1);
});
it('should handle user not found error', async () => {
// Arrange
const userId = '999';
const userStatusDto: UserStatusDto = {
status: UserStatus.LOCKED,
reason: '违规操作'
};
const expectedResult = {
success: false,
message: '用户不存在',
error_code: 'USER_NOT_FOUND'
};
mockUserManagementService.updateUserStatus.mockResolvedValue(expectedResult);
// Act
const result = await controller.updateUserStatus(userId, userStatusDto);
// Assert
expect(result).toEqual(expectedResult);
expect(mockUserManagementService.updateUserStatus).toHaveBeenCalledWith(BigInt(999), userStatusDto);
});
it('should log operation details', async () => {
// Arrange
const userId = '456';
const userStatusDto: UserStatusDto = {
status: UserStatus.BANNED,
reason: '严重违规'
};
const mockResult = {
success: true,
data: {
user: {
id: '456',
username: 'testuser',
nickname: '测试用户',
status: UserStatus.BANNED,
status_description: '已封禁',
updated_at: new Date()
},
reason: '严重违规'
},
message: '成功'
};
mockUserManagementService.updateUserStatus.mockResolvedValue(mockResult);
const logSpy = jest.spyOn(Logger.prototype, 'log');
// Act
await controller.updateUserStatus(userId, userStatusDto);
// Assert
expect(logSpy).toHaveBeenCalledWith(
'管理员修改用户状态',
expect.objectContaining({
operation: 'update_user_status',
userId: '456',
newStatus: UserStatus.BANNED,
reason: '严重违规'
})
);
});
it('should convert string id to BigInt correctly', async () => {
// Arrange
const userId = '9007199254740991'; // Large number as string
const userStatusDto: UserStatusDto = {
status: UserStatus.INACTIVE,
reason: '长期未活跃'
};
const mockResult = {
success: true,
data: {
user: {
id: '9007199254740991',
username: 'large_id_user',
nickname: '大ID用户',
status: UserStatus.INACTIVE,
status_description: '非活跃',
updated_at: new Date()
},
reason: '长期未活跃'
},
message: '成功'
};
mockUserManagementService.updateUserStatus.mockResolvedValue(mockResult);
// Act
await controller.updateUserStatus(userId, userStatusDto);
// Assert
expect(mockUserManagementService.updateUserStatus).toHaveBeenCalledWith(
BigInt('9007199254740991'),
userStatusDto
);
});
});
describe('batchUpdateUserStatus', () => {
it('should batch update user status successfully', async () => {
// Arrange
const batchUserStatusDto: BatchUserStatusDto = {
userIds: ['1', '2', '3'],
status: UserStatus.LOCKED,
reason: '批量锁定违规用户'
};
const expectedResult = {
success: true,
data: {
result: {
success_users: [
{ id: '1', username: 'user1', nickname: '用户1', status: UserStatus.LOCKED, status_description: '已锁定', updated_at: new Date() },
{ id: '2', username: 'user2', nickname: '用户2', status: UserStatus.LOCKED, status_description: '已锁定', updated_at: new Date() },
{ id: '3', username: 'user3', nickname: '用户3', status: UserStatus.LOCKED, status_description: '已锁定', updated_at: new Date() }
],
failed_users: [],
success_count: 3,
failed_count: 0,
total_count: 3
},
reason: '批量锁定违规用户'
},
message: '批量用户状态修改完成'
};
mockUserManagementService.batchUpdateUserStatus.mockResolvedValue(expectedResult);
// Act
const result = await controller.batchUpdateUserStatus(batchUserStatusDto);
// Assert
expect(result).toEqual(expectedResult);
expect(mockUserManagementService.batchUpdateUserStatus).toHaveBeenCalledWith(batchUserStatusDto);
expect(mockUserManagementService.batchUpdateUserStatus).toHaveBeenCalledTimes(1);
});
it('should handle partial success in batch operation', async () => {
// Arrange
const batchUserStatusDto: BatchUserStatusDto = {
userIds: ['1', '2', '999'],
status: UserStatus.ACTIVE,
reason: '批量激活用户'
};
const expectedResult = {
success: true,
data: {
result: {
success_users: [
{ id: '1', username: 'user1', nickname: '用户1', status: UserStatus.ACTIVE, status_description: '正常', updated_at: new Date() },
{ id: '2', username: 'user2', nickname: '用户2', status: UserStatus.ACTIVE, status_description: '正常', updated_at: new Date() }
],
failed_users: [
{ user_id: '999', error: '用户不存在' }
],
success_count: 2,
failed_count: 1,
total_count: 3
},
reason: '批量激活用户'
},
message: '批量用户状态修改完成'
};
mockUserManagementService.batchUpdateUserStatus.mockResolvedValue(expectedResult);
// Act
const result = await controller.batchUpdateUserStatus(batchUserStatusDto);
// Assert
expect(result).toEqual(expectedResult);
expect(result.data.result.success_count).toBe(2);
expect(result.data.result.failed_count).toBe(1);
});
it('should handle empty user list', async () => {
// Arrange
const batchUserStatusDto: BatchUserStatusDto = {
userIds: [],
status: UserStatus.ACTIVE,
reason: '空列表测试'
};
const expectedResult = {
success: true,
data: {
result: {
success_users: [],
failed_users: [],
success_count: 0,
failed_count: 0,
total_count: 0
},
reason: '空列表测试'
},
message: '批量操作完成'
};
mockUserManagementService.batchUpdateUserStatus.mockResolvedValue(expectedResult);
// Act
const result = await controller.batchUpdateUserStatus(batchUserStatusDto);
// Assert
expect(result).toEqual(expectedResult);
expect(result.data.result.total_count).toBe(0);
});
it('should log batch operation details', async () => {
// Arrange
const batchUserStatusDto: BatchUserStatusDto = {
userIds: ['1', '2', '3', '4', '5'],
status: UserStatus.BANNED,
reason: '批量封禁违规用户'
};
const mockResult = {
success: true,
data: {
result: {
success_users: [],
failed_users: [],
success_count: 0,
failed_count: 0,
total_count: 0
}
},
message: '成功'
};
mockUserManagementService.batchUpdateUserStatus.mockResolvedValue(mockResult);
const logSpy = jest.spyOn(Logger.prototype, 'log');
// Act
await controller.batchUpdateUserStatus(batchUserStatusDto);
// Assert
expect(logSpy).toHaveBeenCalledWith(
'管理员批量修改用户状态',
expect.objectContaining({
operation: 'batch_update_user_status',
userCount: 5,
newStatus: UserStatus.BANNED,
reason: '批量封禁违规用户'
})
);
});
it('should handle large user list within limits', async () => {
// Arrange
const userIds = Array.from({ length: BATCH_OPERATION.MAX_USER_COUNT }, (_, i) => i.toString());
const batchUserStatusDto: BatchUserStatusDto = {
userIds,
status: UserStatus.INACTIVE,
reason: '批量设置非活跃'
};
const mockResult = {
success: true,
data: {
result: {
success_users: userIds.map(id => ({
id,
username: `user_${id}`,
nickname: `用户_${id}`,
status: UserStatus.INACTIVE,
status_description: '非活跃',
updated_at: new Date()
})),
failed_users: [],
success_count: userIds.length,
failed_count: 0,
total_count: userIds.length
}
},
message: '批量操作完成'
};
mockUserManagementService.batchUpdateUserStatus.mockResolvedValue(mockResult);
// Act
const result = await controller.batchUpdateUserStatus(batchUserStatusDto);
// Assert
expect(result.success).toBe(true);
expect(result.data.result.total_count).toBe(BATCH_OPERATION.MAX_USER_COUNT);
expect(mockUserManagementService.batchUpdateUserStatus).toHaveBeenCalledWith(batchUserStatusDto);
});
});
describe('getUserStatusStats', () => {
it('should get user status statistics successfully', async () => {
// Arrange
const expectedResult = {
success: true,
data: {
stats: {
active: 1250,
inactive: 45,
locked: 12,
banned: 8,
deleted: 3,
pending: 15,
total: 1333
},
timestamp: '2026-01-07T10:00:00.000Z'
},
message: '用户状态统计获取成功'
};
mockUserManagementService.getUserStatusStats.mockResolvedValue(expectedResult);
// Act
const result = await controller.getUserStatusStats();
// Assert
expect(result).toEqual(expectedResult);
expect(mockUserManagementService.getUserStatusStats).toHaveBeenCalledTimes(1);
});
it('should handle statistics retrieval failure', async () => {
// Arrange
const expectedResult = {
success: false,
message: '统计数据获取失败',
error_code: 'STATS_RETRIEVAL_FAILED'
};
mockUserManagementService.getUserStatusStats.mockResolvedValue(expectedResult);
// Act
const result = await controller.getUserStatusStats();
// Assert
expect(result).toEqual(expectedResult);
expect(result.success).toBe(false);
});
it('should log statistics query operation', async () => {
// Arrange
const mockResult = {
success: true,
data: {
stats: {
active: 800,
inactive: 150,
locked: 30,
banned: 15,
deleted: 5,
pending: 20,
total: 1020
},
timestamp: '2026-01-07T15:30:00.000Z'
},
message: '成功'
};
mockUserManagementService.getUserStatusStats.mockResolvedValue(mockResult);
const logSpy = jest.spyOn(Logger.prototype, 'log');
// Act
await controller.getUserStatusStats();
// Assert
expect(logSpy).toHaveBeenCalledWith(
'管理员获取用户状态统计',
expect.objectContaining({
operation: 'get_user_status_stats'
})
);
});
it('should return detailed statistics breakdown', async () => {
// Arrange
const expectedResult = {
success: true,
data: {
stats: {
active: 800,
inactive: 150,
locked: 30,
banned: 15,
deleted: 5,
pending: 20,
total: 1020
},
timestamp: '2026-01-07T15:30:00.000Z',
metadata: {
last_updated: '2026-01-07T15:30:00.000Z',
cache_duration: 300
}
},
message: '用户状态统计获取成功'
};
mockUserManagementService.getUserStatusStats.mockResolvedValue(expectedResult);
// Act
const result = await controller.getUserStatusStats();
// Assert
expect(result.data.stats.total).toBe(1020);
expect(result.data.stats.active).toBe(800);
expect(result.data.stats.locked).toBe(30);
expect(result.data.stats.banned).toBe(15);
});
it('should handle zero statistics gracefully', async () => {
// Arrange
const expectedResult = {
success: true,
data: {
stats: {
active: 0,
inactive: 0,
locked: 0,
banned: 0,
deleted: 0,
pending: 0,
total: 0
},
timestamp: '2026-01-07T10:00:00.000Z'
},
message: '用户状态统计获取成功'
};
mockUserManagementService.getUserStatusStats.mockResolvedValue(expectedResult);
// Act
const result = await controller.getUserStatusStats();
// Assert
expect(result.data.stats.total).toBe(0);
expect(result.success).toBe(true);
});
});
describe('AdminGuard Integration', () => {
it('should be protected by AdminGuard', () => {
// Verify that AdminGuard is applied to the controller methods
const updateUserStatusMethod = Reflect.getMetadata('__guards__', UserStatusController.prototype.updateUserStatus);
const batchUpdateMethod = Reflect.getMetadata('__guards__', UserStatusController.prototype.batchUpdateUserStatus);
const getStatsMethod = Reflect.getMetadata('__guards__', UserStatusController.prototype.getUserStatusStats);
// At least one method should have guards (they are applied via @UseGuards decorator)
expect(updateUserStatusMethod || batchUpdateMethod || getStatsMethod).toBeTruthy();
});
});
describe('Error Handling', () => {
it('should handle service errors gracefully in updateUserStatus', async () => {
// Arrange
const userId = '123';
const userStatusDto: UserStatusDto = {
status: UserStatus.ACTIVE,
reason: '测试错误处理'
};
mockUserManagementService.updateUserStatus.mockRejectedValue(new Error('Service error'));
// Act & Assert
await expect(controller.updateUserStatus(userId, userStatusDto)).rejects.toThrow('Service error');
});
it('should handle service errors gracefully in batchUpdateUserStatus', async () => {
// Arrange
const batchUserStatusDto: BatchUserStatusDto = {
userIds: ['1', '2'],
status: UserStatus.ACTIVE,
reason: '测试错误处理'
};
mockUserManagementService.batchUpdateUserStatus.mockRejectedValue(new Error('Batch service error'));
// Act & Assert
await expect(controller.batchUpdateUserStatus(batchUserStatusDto)).rejects.toThrow('Batch service error');
});
it('should handle service errors gracefully in getUserStatusStats', async () => {
// Arrange
mockUserManagementService.getUserStatusStats.mockRejectedValue(new Error('Stats service error'));
// Act & Assert
await expect(controller.getUserStatusStats()).rejects.toThrow('Stats service error');
});
});
});

View File

@@ -0,0 +1,243 @@
/**
* 用户状态管理控制器
*
* 功能描述:
* - 管理员管理用户账户状态
* - 支持批量状态操作
* - 提供状态变更审计日志
*
* 职责分离:
* - HTTP请求处理和参数验证
* - API文档生成和接口规范定义
* - 业务服务调用和响应格式化
*
* API端点
* - PUT /admin/users/:id/status - 修改用户状态
* - POST /admin/users/batch-status - 批量修改用户状态
* - GET /admin/users/status-stats - 获取用户状态统计
*
* 最近修改:
* - 2026-01-07: 代码规范优化 - 修正文件命名规范,完善注释规范,更新作者信息 (修改者: moyin)
*
* @author moyin
* @version 1.0.1
* @since 2025-12-24
* @lastModified 2026-01-07
*/
import { Body, Controller, Get, HttpCode, HttpStatus, Param, Put, Post, UseGuards, ValidationPipe, UsePipes, Logger } from '@nestjs/common';
import { ApiBearerAuth, ApiBody, ApiOperation, ApiParam, ApiResponse, ApiTags } from '@nestjs/swagger';
import { AdminGuard } from '../admin/guards/admin.guard';
import { UserManagementService } from './user_management.service';
import { Throttle, ThrottlePresets } from '../../core/security_core/throttle.decorator';
import { Timeout, TimeoutPresets } from '../../core/security_core/timeout.decorator';
import { UserStatusDto, BatchUserStatusDto } from './user_status.dto';
import { UserStatusResponseDto, BatchUserStatusResponseDto, UserStatusStatsResponseDto } from './user_status_response.dto';
import { BATCH_OPERATION, UTILS } from './user_mgmt.constants';
/**
* 用户状态管理控制器
*
* 职责:
* - 处理用户状态管理相关的HTTP请求
* - 提供RESTful API接口和Swagger文档
* - 执行请求参数验证和权限控制
*
* 主要方法:
* - updateUserStatus() - 修改单个用户状态
* - batchUpdateUserStatus() - 批量修改用户状态
* - getUserStatusStats() - 获取用户状态统计
*
* 使用场景:
* - 管理员通过API管理用户状态
* - 系统集成和自动化用户管理
* - 用户状态监控和统计分析
*/
@ApiTags('user_management')
@Controller('admin/users')
export class UserStatusController {
private readonly logger = new Logger(UserStatusController.name);
constructor(private readonly userManagementService: UserManagementService) {}
/**
* 修改用户状态
*
* 业务逻辑:
* 1. 验证管理员权限和操作频率限制
* 2. 验证用户ID格式和状态参数有效性
* 3. 记录状态修改操作的审计日志
* 4. 调用业务服务执行状态变更
* 5. 返回操作结果和用户最新状态
*
* @param id 用户ID
* @param userStatusDto 状态修改数据
* @returns 修改结果
* @throws ForbiddenException 管理员权限不足时
* @throws NotFoundException 用户不存在时
* @throws TooManyRequestsException 操作过于频繁时
*
* @example
* ```typescript
* const result = await controller.updateUserStatus('123', {
* status: UserStatus.LOCKED,
* reason: '用户违反社区规定'
* });
* ```
*/
@ApiBearerAuth('JWT-auth')
@ApiOperation({
summary: '修改用户状态',
description: '管理员修改指定用户的账户状态,支持激活、锁定、禁用等操作'
})
@ApiParam({ name: 'id', description: '用户ID' })
@ApiBody({ type: UserStatusDto })
@ApiResponse({
status: 200,
description: '状态修改成功',
type: UserStatusResponseDto
})
@ApiResponse({
status: 403,
description: '权限不足'
})
@ApiResponse({
status: 404,
description: '用户不存在'
})
@ApiResponse({
status: 429,
description: '操作过于频繁'
})
@UseGuards(AdminGuard)
@Throttle(ThrottlePresets.ADMIN_OPERATION)
@Timeout(TimeoutPresets.NORMAL)
@Put(':id/status')
@HttpCode(HttpStatus.OK)
@UsePipes(new ValidationPipe({ transform: true }))
async updateUserStatus(
@Param('id') id: string,
@Body() userStatusDto: UserStatusDto
): Promise<UserStatusResponseDto> {
this.logger.log('管理员修改用户状态', {
operation: 'update_user_status',
userId: id,
newStatus: userStatusDto.status,
reason: userStatusDto.reason,
timestamp: UTILS.getCurrentTimestamp()
});
return await this.userManagementService.updateUserStatus(BigInt(id), userStatusDto);
}
/**
* 批量修改用户状态
*
* 业务逻辑:
* 1. 验证管理员权限和批量操作频率限制
* 2. 验证用户ID列表和状态参数有效性
* 3. 检查批量操作数量限制(最多${BATCH_OPERATION.MAX_USER_COUNT}个用户)
* 4. 记录批量操作的审计日志
* 5. 调用业务服务执行批量状态变更
* 6. 返回批量操作结果统计
*
* @param batchUserStatusDto 批量状态修改数据
* @returns 批量修改结果
* @throws ForbiddenException 管理员权限不足时
* @throws BadRequestException 批量操作数量超限时
* @throws TooManyRequestsException 操作过于频繁时
*
* @example
* ```typescript
* const result = await controller.batchUpdateUserStatus({
* userIds: ['123', '456', '789'],
* status: UserStatus.LOCKED,
* reason: '批量处理违规用户'
* });
* ```
*/
@ApiBearerAuth('JWT-auth')
@ApiOperation({
summary: '批量修改用户状态',
description: '管理员批量修改多个用户的账户状态'
})
@ApiBody({ type: BatchUserStatusDto })
@ApiResponse({
status: 200,
description: '批量修改成功',
type: BatchUserStatusResponseDto
})
@ApiResponse({
status: 403,
description: '权限不足'
})
@ApiResponse({
status: 429,
description: '操作过于频繁'
})
@UseGuards(AdminGuard)
@Throttle(ThrottlePresets.ADMIN_OPERATION)
@Timeout(TimeoutPresets.SLOW)
@Post('batch-status')
@HttpCode(HttpStatus.OK)
@UsePipes(new ValidationPipe({ transform: true }))
async batchUpdateUserStatus(
@Body() batchUserStatusDto: BatchUserStatusDto
): Promise<BatchUserStatusResponseDto> {
this.logger.log('管理员批量修改用户状态', {
operation: 'batch_update_user_status',
userCount: batchUserStatusDto.userIds.length,
newStatus: batchUserStatusDto.status,
reason: batchUserStatusDto.reason,
timestamp: UTILS.getCurrentTimestamp()
});
return await this.userManagementService.batchUpdateUserStatus(batchUserStatusDto);
}
/**
* 获取用户状态统计
*
* 业务逻辑:
* 1. 验证管理员权限
* 2. 调用业务服务获取状态统计数据
* 3. 记录统计查询的审计日志
* 4. 返回各种状态的用户数量统计
* 5. 提供状态分布分析数据
*
* @returns 状态统计信息
* @throws ForbiddenException 管理员权限不足时
* @throws InternalServerErrorException 统计数据获取失败时
*
* @example
* ```typescript
* const stats = await controller.getUserStatusStats();
* // 返回: { active: 1250, inactive: 45, locked: 12, ... }
* ```
*/
@ApiBearerAuth('JWT-auth')
@ApiOperation({
summary: '获取用户状态统计',
description: '获取各种用户状态的数量统计信息'
})
@ApiResponse({
status: 200,
description: '获取成功',
type: UserStatusStatsResponseDto
})
@ApiResponse({
status: 403,
description: '权限不足'
})
@UseGuards(AdminGuard)
@Timeout(TimeoutPresets.DATABASE_QUERY)
@Get('status-stats')
async getUserStatusStats(): Promise<UserStatusStatsResponseDto> {
this.logger.log('管理员获取用户状态统计', {
operation: 'get_user_status_stats',
timestamp: UTILS.getCurrentTimestamp()
});
return await this.userManagementService.getUserStatusStats();
}
}

View File

@@ -0,0 +1,132 @@
/**
* 用户状态管理 DTO
*
* 功能描述:
* - 定义用户状态管理相关的请求数据结构
* - 提供数据验证规则和错误提示
* - 确保状态管理操作的数据格式一致性
*
* 职责分离:
* - 请求数据结构定义和类型约束
* - 数据验证规则配置和错误消息定义
* - Swagger API文档生成支持
*
* 最近修改:
* - 2026-01-07: 代码规范优化 - 修正文件命名规范,完善注释规范,更新作者信息 (修改者: moyin)
*
* @author moyin
* @version 1.0.1
* @since 2025-12-24
* @lastModified 2026-01-07
*/
import { IsString, IsNotEmpty, IsEnum, IsOptional, IsArray, ArrayMinSize, ArrayMaxSize } from 'class-validator';
import { ApiProperty } from '@nestjs/swagger';
import { UserStatus } from './user_status.enum';
import { BATCH_OPERATION, VALIDATION } from './user_mgmt.constants';
/**
* 用户状态修改请求DTO
*
* 职责:
* - 定义单个用户状态修改的请求数据格式
* - 提供状态值和修改原因的验证规则
* - 支持Swagger文档自动生成
*
* 主要字段:
* - status - 新的用户状态(必填)
* - reason - 状态修改原因(可选)
*
* 使用场景:
* - 管理员修改单个用户状态的API请求
* - 用户状态变更操作的数据传输
*/
export class UserStatusDto {
/**
* 新的用户状态
*/
@ApiProperty({
description: '用户状态',
enum: UserStatus,
example: UserStatus.ACTIVE,
enumName: 'UserStatus'
})
@IsEnum(UserStatus, { message: '用户状态必须是有效的枚举值' })
@IsNotEmpty({ message: '用户状态不能为空' })
status: UserStatus;
/**
* 状态修改原因
*/
@ApiProperty({
description: '状态修改原因(可选)',
example: '用户违反社区规定',
required: false,
maxLength: VALIDATION.REASON_MAX_LENGTH
})
@IsOptional()
@IsString({ message: '修改原因必须是字符串' })
reason?: string;
}
/**
* 批量用户状态修改请求DTO
*
* 职责:
* - 定义批量用户状态修改的请求数据格式
* - 提供用户ID列表和状态值的验证规则
* - 限制批量操作的数量范围(${BATCH_OPERATION.MIN_USER_COUNT}-${BATCH_OPERATION.MAX_USER_COUNT}个用户)
*
* 主要字段:
* - userIds - 用户ID列表必填${BATCH_OPERATION.MIN_USER_COUNT}-${BATCH_OPERATION.MAX_USER_COUNT}个)
* - status - 新的用户状态(必填)
* - reason - 批量修改原因(可选)
*
* 使用场景:
* - 管理员批量修改用户状态的API请求
* - 系统自动化批量用户管理操作
*/
export class BatchUserStatusDto {
/**
* 用户ID列表
*/
@ApiProperty({
description: '用户ID列表',
example: ['1', '2', '3'],
type: [String],
minItems: BATCH_OPERATION.MIN_USER_COUNT,
maxItems: BATCH_OPERATION.MAX_USER_COUNT
})
@IsArray({ message: '用户ID列表必须是数组' })
@ArrayMinSize(BATCH_OPERATION.MIN_USER_COUNT, { message: '至少需要选择一个用户' })
@ArrayMaxSize(BATCH_OPERATION.MAX_USER_COUNT, { message: `一次最多只能操作${BATCH_OPERATION.MAX_USER_COUNT}个用户` })
@IsString({ each: true, message: '用户ID必须是字符串' })
@IsNotEmpty({ each: true, message: '用户ID不能为空' })
userIds: string[];
/**
* 新的用户状态
*/
@ApiProperty({
description: '用户状态',
enum: UserStatus,
example: UserStatus.LOCKED,
enumName: 'UserStatus'
})
@IsEnum(UserStatus, { message: '用户状态必须是有效的枚举值' })
@IsNotEmpty({ message: '用户状态不能为空' })
status: UserStatus;
/**
* 状态修改原因
*/
@ApiProperty({
description: '批量修改原因(可选)',
example: '批量处理违规用户',
required: false,
maxLength: VALIDATION.REASON_MAX_LENGTH
})
@IsOptional()
@IsString({ message: '修改原因必须是字符串' })
reason?: string;
}

View File

@@ -0,0 +1,31 @@
/**
* 用户状态枚举Business层兼容性导出
*
* 功能描述:
* - 重新导出Core层的用户状态枚举
* - 保持向后兼容性
* - 符合架构分层原则
*
* 职责分离:
* - 提供Business层对Core层用户状态的访问接口
* - 维护现有代码的兼容性
* - 遵循依赖倒置原则
*
* 最近修改:
* - 2026-01-07: 架构优化 - 改为重新导出Core层枚举符合架构分层原则 (修改者: moyin)
*
* @author moyin
* @version 1.0.2
* @since 2025-12-24
* @lastModified 2026-01-07
*/
// 重新导出Core层的用户状态枚举和相关函数
export {
UserStatus,
getUserStatusDescription,
canUserLogin,
getUserStatusErrorMessage,
getAllUserStatuses,
isValidUserStatus
} from '../../core/db/users/user_status.enum';

View File

@@ -0,0 +1,303 @@
/**
* 用户状态管理响应 DTO
*
* 功能描述:
* - 定义用户状态管理相关的响应数据结构
* - 提供Swagger文档生成支持
* - 确保状态管理API响应的数据格式一致性
*
* 职责分离:
* - 响应数据结构定义和类型约束
* - API响应格式标准化和文档生成
* - 错误信息和成功结果的统一封装
*
* 最近修改:
* - 2026-01-07: 代码规范优化 - 修正文件命名规范,完善注释规范,更新作者信息 (修改者: moyin)
*
* @author moyin
* @version 1.0.1
* @since 2025-12-24
* @lastModified 2026-01-07
*/
import { ApiProperty } from '@nestjs/swagger';
import { UserStatus } from './user_status.enum';
/**
* 用户状态信息DTO
*/
export class UserStatusInfoDto {
@ApiProperty({
description: '用户ID',
example: '1'
})
id: string;
@ApiProperty({
description: '用户名',
example: 'testuser'
})
username: string;
@ApiProperty({
description: '用户昵称',
example: '测试用户'
})
nickname: string;
@ApiProperty({
description: '用户状态',
enum: UserStatus,
example: UserStatus.ACTIVE
})
status: UserStatus;
@ApiProperty({
description: '状态描述',
example: '正常'
})
status_description: string;
@ApiProperty({
description: '状态修改时间',
example: '2025-12-24T10:00:00.000Z'
})
updated_at: Date;
}
/**
* 用户状态修改响应数据DTO
*/
export class UserStatusDataDto {
@ApiProperty({
description: '用户信息',
type: UserStatusInfoDto
})
user: UserStatusInfoDto;
@ApiProperty({
description: '修改原因',
example: '用户违反社区规定',
required: false
})
reason?: string;
}
/**
* 用户状态修改响应DTO
*/
export class UserStatusResponseDto {
@ApiProperty({
description: '请求是否成功',
example: true
})
success: boolean;
@ApiProperty({
description: '响应数据',
type: UserStatusDataDto,
required: false
})
data?: UserStatusDataDto;
@ApiProperty({
description: '响应消息',
example: '用户状态修改成功'
})
message: string;
@ApiProperty({
description: '错误代码',
example: 'USER_STATUS_UPDATE_FAILED',
required: false
})
error_code?: string;
}
/**
* 批量操作结果DTO
*/
export class BatchOperationResultDto {
@ApiProperty({
description: '成功处理的用户列表',
type: [UserStatusInfoDto]
})
success_users: UserStatusInfoDto[];
@ApiProperty({
description: '处理失败的用户列表',
type: [Object],
example: [
{
user_id: '999',
error: '用户不存在'
}
]
})
failed_users: Array<{
user_id: string;
error: string;
}>;
@ApiProperty({
description: '成功处理数量',
example: 5
})
success_count: number;
@ApiProperty({
description: '失败处理数量',
example: 1
})
failed_count: number;
@ApiProperty({
description: '总处理数量',
example: 6
})
total_count: number;
}
/**
* 批量用户状态修改响应数据DTO
*/
export class BatchUserStatusDataDto {
@ApiProperty({
description: '批量操作结果',
type: BatchOperationResultDto
})
result: BatchOperationResultDto;
@ApiProperty({
description: '修改原因',
example: '批量处理违规用户',
required: false
})
reason?: string;
}
/**
* 批量用户状态修改响应DTO
*/
export class BatchUserStatusResponseDto {
@ApiProperty({
description: '请求是否成功',
example: true
})
success: boolean;
@ApiProperty({
description: '响应数据',
type: BatchUserStatusDataDto,
required: false
})
data?: BatchUserStatusDataDto;
@ApiProperty({
description: '响应消息',
example: '批量用户状态修改完成'
})
message: string;
@ApiProperty({
description: '错误代码',
example: 'BATCH_USER_STATUS_UPDATE_FAILED',
required: false
})
error_code?: string;
}
/**
* 用户状态统计DTO
*/
export class UserStatusStatsDto {
@ApiProperty({
description: '正常用户数量',
example: 1250
})
active: number;
@ApiProperty({
description: '未激活用户数量',
example: 45
})
inactive: number;
@ApiProperty({
description: '锁定用户数量',
example: 12
})
locked: number;
@ApiProperty({
description: '禁用用户数量',
example: 8
})
banned: number;
@ApiProperty({
description: '已删除用户数量',
example: 3
})
deleted: number;
@ApiProperty({
description: '待审核用户数量',
example: 15
})
pending: number;
@ApiProperty({
description: '总用户数量',
example: 1333
})
total: number;
}
/**
* 用户状态统计响应数据DTO
*/
export class UserStatusStatsDataDto {
@ApiProperty({
description: '用户状态统计',
type: UserStatusStatsDto
})
stats: UserStatusStatsDto;
@ApiProperty({
description: '统计时间',
example: '2025-12-24T10:00:00.000Z'
})
timestamp: string;
}
/**
* 用户状态统计响应DTO
*/
export class UserStatusStatsResponseDto {
@ApiProperty({
description: '请求是否成功',
example: true
})
success: boolean;
@ApiProperty({
description: '响应数据',
type: UserStatusStatsDataDto,
required: false
})
data?: UserStatusStatsDataDto;
@ApiProperty({
description: '响应消息',
example: '用户状态统计获取成功'
})
message: string;
@ApiProperty({
description: '错误代码',
example: 'USER_STATUS_STATS_FAILED',
required: false
})
error_code?: string;
}