forked from datawhale/whale-town-end
- 统一文件命名为snake_case格式(kebab-case snake_case) - 重构zulip模块为zulip_core,明确Core层职责 - 重构user-mgmt模块为user_mgmt,统一命名规范 - 调整模块依赖关系,优化架构分层 - 删除过时的文件和目录结构 - 更新相关文档和配置文件 本次重构涉及大量文件重命名和模块重组, 旨在建立更清晰的项目架构和统一的命名规范。
581 lines
15 KiB
TypeScript
581 lines
15 KiB
TypeScript
/**
|
||
* Zulip账号关联管理控制器
|
||
*
|
||
* 功能描述:
|
||
* - 提供Zulip账号关联管理的REST API接口
|
||
* - 支持CRUD操作和批量管理
|
||
* - 提供账号验证和统计功能
|
||
*
|
||
* @author angjustinl
|
||
* @version 1.0.0
|
||
* @since 2025-01-07
|
||
*/
|
||
|
||
import {
|
||
Controller,
|
||
Get,
|
||
Post,
|
||
Put,
|
||
Delete,
|
||
Body,
|
||
Param,
|
||
Query,
|
||
UseGuards,
|
||
HttpStatus,
|
||
HttpCode,
|
||
Inject,
|
||
} from '@nestjs/common';
|
||
import {
|
||
ApiTags,
|
||
ApiOperation,
|
||
ApiResponse,
|
||
ApiBearerAuth,
|
||
ApiParam,
|
||
ApiQuery,
|
||
} from '@nestjs/swagger';
|
||
import { JwtAuthGuard } from '../../auth/jwt_auth.guard';
|
||
import { ZulipAccountsService } from '../../../core/db/zulip_accounts/zulip_accounts.service';
|
||
import { ZulipAccountsMemoryService } from '../../../core/db/zulip_accounts/zulip_accounts_memory.service';
|
||
import {
|
||
CreateZulipAccountDto,
|
||
UpdateZulipAccountDto,
|
||
QueryZulipAccountDto,
|
||
ZulipAccountResponseDto,
|
||
ZulipAccountListResponseDto,
|
||
ZulipAccountStatsResponseDto,
|
||
BatchUpdateStatusDto,
|
||
BatchUpdateResponseDto,
|
||
VerifyAccountDto,
|
||
VerifyAccountResponseDto,
|
||
} from '../../../core/db/zulip_accounts/zulip_accounts.dto';
|
||
|
||
@ApiTags('zulip-accounts')
|
||
@Controller('zulip-accounts')
|
||
@UseGuards(JwtAuthGuard)
|
||
@ApiBearerAuth('JWT-auth')
|
||
export class ZulipAccountsController {
|
||
constructor(
|
||
@Inject('ZulipAccountsService')
|
||
private readonly zulipAccountsService: ZulipAccountsService | ZulipAccountsMemoryService,
|
||
) {}
|
||
|
||
/**
|
||
* 创建Zulip账号关联
|
||
*/
|
||
@Post()
|
||
@ApiOperation({
|
||
summary: '创建Zulip账号关联',
|
||
description: '为游戏用户创建与Zulip账号的关联关系'
|
||
})
|
||
@ApiResponse({
|
||
status: 201,
|
||
description: '创建成功',
|
||
type: ZulipAccountResponseDto,
|
||
})
|
||
@ApiResponse({
|
||
status: 400,
|
||
description: '请求参数错误',
|
||
})
|
||
@ApiResponse({
|
||
status: 409,
|
||
description: '关联已存在',
|
||
})
|
||
@HttpCode(HttpStatus.CREATED)
|
||
async create(@Body() createDto: CreateZulipAccountDto): Promise<ZulipAccountResponseDto> {
|
||
return this.zulipAccountsService.create(createDto);
|
||
}
|
||
|
||
/**
|
||
* 获取所有Zulip账号关联
|
||
*/
|
||
@Get()
|
||
@ApiOperation({
|
||
summary: '查询Zulip账号关联列表',
|
||
description: '根据条件查询Zulip账号关联列表'
|
||
})
|
||
@ApiQuery({
|
||
name: 'gameUserId',
|
||
required: false,
|
||
description: '游戏用户ID',
|
||
example: '12345'
|
||
})
|
||
@ApiQuery({
|
||
name: 'zulipUserId',
|
||
required: false,
|
||
description: 'Zulip用户ID',
|
||
example: 67890
|
||
})
|
||
@ApiQuery({
|
||
name: 'zulipEmail',
|
||
required: false,
|
||
description: 'Zulip邮箱地址',
|
||
example: 'user@example.com'
|
||
})
|
||
@ApiQuery({
|
||
name: 'status',
|
||
required: false,
|
||
description: '账号状态',
|
||
enum: ['active', 'inactive', 'suspended', 'error']
|
||
})
|
||
@ApiQuery({
|
||
name: 'includeGameUser',
|
||
required: false,
|
||
description: '是否包含游戏用户信息',
|
||
type: Boolean,
|
||
example: false
|
||
})
|
||
@ApiResponse({
|
||
status: 200,
|
||
description: '查询成功',
|
||
type: ZulipAccountListResponseDto,
|
||
})
|
||
async findMany(@Query() queryDto: QueryZulipAccountDto): Promise<ZulipAccountListResponseDto> {
|
||
return this.zulipAccountsService.findMany(queryDto);
|
||
}
|
||
|
||
/**
|
||
* 根据ID获取Zulip账号关联
|
||
*/
|
||
@Get(':id')
|
||
@ApiOperation({
|
||
summary: '根据ID获取Zulip账号关联',
|
||
description: '根据关联记录ID获取详细信息'
|
||
})
|
||
@ApiParam({
|
||
name: 'id',
|
||
description: '关联记录ID',
|
||
example: '1'
|
||
})
|
||
@ApiQuery({
|
||
name: 'includeGameUser',
|
||
required: false,
|
||
description: '是否包含游戏用户信息',
|
||
type: Boolean,
|
||
example: false
|
||
})
|
||
@ApiResponse({
|
||
status: 200,
|
||
description: '获取成功',
|
||
type: ZulipAccountResponseDto,
|
||
})
|
||
@ApiResponse({
|
||
status: 404,
|
||
description: '记录不存在',
|
||
})
|
||
async findById(
|
||
@Param('id') id: string,
|
||
@Query('includeGameUser') includeGameUser?: boolean,
|
||
): Promise<ZulipAccountResponseDto> {
|
||
return this.zulipAccountsService.findById(id, includeGameUser);
|
||
}
|
||
|
||
/**
|
||
* 根据游戏用户ID获取Zulip账号关联
|
||
*/
|
||
@Get('game-user/:gameUserId')
|
||
@ApiOperation({
|
||
summary: '根据游戏用户ID获取Zulip账号关联',
|
||
description: '根据游戏用户ID获取关联的Zulip账号信息'
|
||
})
|
||
@ApiParam({
|
||
name: 'gameUserId',
|
||
description: '游戏用户ID',
|
||
example: '12345'
|
||
})
|
||
@ApiQuery({
|
||
name: 'includeGameUser',
|
||
required: false,
|
||
description: '是否包含游戏用户信息',
|
||
type: Boolean,
|
||
example: false
|
||
})
|
||
@ApiResponse({
|
||
status: 200,
|
||
description: '获取成功',
|
||
type: ZulipAccountResponseDto,
|
||
})
|
||
@ApiResponse({
|
||
status: 404,
|
||
description: '关联不存在',
|
||
})
|
||
async findByGameUserId(
|
||
@Param('gameUserId') gameUserId: string,
|
||
@Query('includeGameUser') includeGameUser?: boolean,
|
||
): Promise<ZulipAccountResponseDto | null> {
|
||
return this.zulipAccountsService.findByGameUserId(gameUserId, includeGameUser);
|
||
}
|
||
|
||
/**
|
||
* 根据Zulip用户ID获取账号关联
|
||
*/
|
||
@Get('zulip-user/:zulipUserId')
|
||
@ApiOperation({
|
||
summary: '根据Zulip用户ID获取账号关联',
|
||
description: '根据Zulip用户ID获取关联的游戏账号信息'
|
||
})
|
||
@ApiParam({
|
||
name: 'zulipUserId',
|
||
description: 'Zulip用户ID',
|
||
example: '67890'
|
||
})
|
||
@ApiQuery({
|
||
name: 'includeGameUser',
|
||
required: false,
|
||
description: '是否包含游戏用户信息',
|
||
type: Boolean,
|
||
example: false
|
||
})
|
||
@ApiResponse({
|
||
status: 200,
|
||
description: '获取成功',
|
||
type: ZulipAccountResponseDto,
|
||
})
|
||
@ApiResponse({
|
||
status: 404,
|
||
description: '关联不存在',
|
||
})
|
||
async findByZulipUserId(
|
||
@Param('zulipUserId') zulipUserId: string,
|
||
@Query('includeGameUser') includeGameUser?: boolean,
|
||
): Promise<ZulipAccountResponseDto | null> {
|
||
return this.zulipAccountsService.findByZulipUserId(parseInt(zulipUserId), includeGameUser);
|
||
}
|
||
|
||
/**
|
||
* 根据Zulip邮箱获取账号关联
|
||
*/
|
||
@Get('zulip-email/:zulipEmail')
|
||
@ApiOperation({
|
||
summary: '根据Zulip邮箱获取账号关联',
|
||
description: '根据Zulip邮箱地址获取关联的游戏账号信息'
|
||
})
|
||
@ApiParam({
|
||
name: 'zulipEmail',
|
||
description: 'Zulip邮箱地址',
|
||
example: 'user@example.com'
|
||
})
|
||
@ApiQuery({
|
||
name: 'includeGameUser',
|
||
required: false,
|
||
description: '是否包含游戏用户信息',
|
||
type: Boolean,
|
||
example: false
|
||
})
|
||
@ApiResponse({
|
||
status: 200,
|
||
description: '获取成功',
|
||
type: ZulipAccountResponseDto,
|
||
})
|
||
@ApiResponse({
|
||
status: 404,
|
||
description: '关联不存在',
|
||
})
|
||
async findByZulipEmail(
|
||
@Param('zulipEmail') zulipEmail: string,
|
||
@Query('includeGameUser') includeGameUser?: boolean,
|
||
): Promise<ZulipAccountResponseDto | null> {
|
||
return this.zulipAccountsService.findByZulipEmail(zulipEmail, includeGameUser);
|
||
}
|
||
|
||
/**
|
||
* 更新Zulip账号关联
|
||
*/
|
||
@Put(':id')
|
||
@ApiOperation({
|
||
summary: '更新Zulip账号关联',
|
||
description: '根据ID更新Zulip账号关联信息'
|
||
})
|
||
@ApiParam({
|
||
name: 'id',
|
||
description: '关联记录ID',
|
||
example: '1'
|
||
})
|
||
@ApiResponse({
|
||
status: 200,
|
||
description: '更新成功',
|
||
type: ZulipAccountResponseDto,
|
||
})
|
||
@ApiResponse({
|
||
status: 404,
|
||
description: '记录不存在',
|
||
})
|
||
async update(
|
||
@Param('id') id: string,
|
||
@Body() updateDto: UpdateZulipAccountDto,
|
||
): Promise<ZulipAccountResponseDto> {
|
||
return this.zulipAccountsService.update(id, updateDto);
|
||
}
|
||
|
||
/**
|
||
* 根据游戏用户ID更新关联
|
||
*/
|
||
@Put('game-user/:gameUserId')
|
||
@ApiOperation({
|
||
summary: '根据游戏用户ID更新关联',
|
||
description: '根据游戏用户ID更新Zulip账号关联信息'
|
||
})
|
||
@ApiParam({
|
||
name: 'gameUserId',
|
||
description: '游戏用户ID',
|
||
example: '12345'
|
||
})
|
||
@ApiResponse({
|
||
status: 200,
|
||
description: '更新成功',
|
||
type: ZulipAccountResponseDto,
|
||
})
|
||
@ApiResponse({
|
||
status: 404,
|
||
description: '关联不存在',
|
||
})
|
||
async updateByGameUserId(
|
||
@Param('gameUserId') gameUserId: string,
|
||
@Body() updateDto: UpdateZulipAccountDto,
|
||
): Promise<ZulipAccountResponseDto> {
|
||
return this.zulipAccountsService.updateByGameUserId(gameUserId, updateDto);
|
||
}
|
||
|
||
/**
|
||
* 删除Zulip账号关联
|
||
*/
|
||
@Delete(':id')
|
||
@ApiOperation({
|
||
summary: '删除Zulip账号关联',
|
||
description: '根据ID删除Zulip账号关联记录'
|
||
})
|
||
@ApiParam({
|
||
name: 'id',
|
||
description: '关联记录ID',
|
||
example: '1'
|
||
})
|
||
@ApiResponse({
|
||
status: 200,
|
||
description: '删除成功',
|
||
schema: {
|
||
type: 'object',
|
||
properties: {
|
||
success: { type: 'boolean', example: true },
|
||
message: { type: 'string', example: '删除成功' }
|
||
}
|
||
}
|
||
})
|
||
@ApiResponse({
|
||
status: 404,
|
||
description: '记录不存在',
|
||
})
|
||
async delete(@Param('id') id: string): Promise<{ success: boolean; message: string }> {
|
||
await this.zulipAccountsService.delete(id);
|
||
return { success: true, message: '删除成功' };
|
||
}
|
||
|
||
/**
|
||
* 根据游戏用户ID删除关联
|
||
*/
|
||
@Delete('game-user/:gameUserId')
|
||
@ApiOperation({
|
||
summary: '根据游戏用户ID删除关联',
|
||
description: '根据游戏用户ID删除Zulip账号关联记录'
|
||
})
|
||
@ApiParam({
|
||
name: 'gameUserId',
|
||
description: '游戏用户ID',
|
||
example: '12345'
|
||
})
|
||
@ApiResponse({
|
||
status: 200,
|
||
description: '删除成功',
|
||
schema: {
|
||
type: 'object',
|
||
properties: {
|
||
success: { type: 'boolean', example: true },
|
||
message: { type: 'string', example: '删除成功' }
|
||
}
|
||
}
|
||
})
|
||
@ApiResponse({
|
||
status: 404,
|
||
description: '关联不存在',
|
||
})
|
||
async deleteByGameUserId(@Param('gameUserId') gameUserId: string): Promise<{ success: boolean; message: string }> {
|
||
await this.zulipAccountsService.deleteByGameUserId(gameUserId);
|
||
return { success: true, message: '删除成功' };
|
||
}
|
||
|
||
/**
|
||
* 获取需要验证的账号列表
|
||
*/
|
||
@Get('management/verification-needed')
|
||
@ApiOperation({
|
||
summary: '获取需要验证的账号列表',
|
||
description: '获取超过指定时间未验证的账号列表'
|
||
})
|
||
@ApiQuery({
|
||
name: 'maxAge',
|
||
required: false,
|
||
description: '最大验证间隔(毫秒),默认24小时',
|
||
example: 86400000
|
||
})
|
||
@ApiResponse({
|
||
status: 200,
|
||
description: '获取成功',
|
||
type: ZulipAccountListResponseDto,
|
||
})
|
||
async findAccountsNeedingVerification(
|
||
@Query('maxAge') maxAge?: number,
|
||
): Promise<ZulipAccountListResponseDto> {
|
||
return this.zulipAccountsService.findAccountsNeedingVerification(maxAge);
|
||
}
|
||
|
||
/**
|
||
* 获取错误状态的账号列表
|
||
*/
|
||
@Get('management/error-accounts')
|
||
@ApiOperation({
|
||
summary: '获取错误状态的账号列表',
|
||
description: '获取处于错误状态的账号列表'
|
||
})
|
||
@ApiQuery({
|
||
name: 'maxRetryCount',
|
||
required: false,
|
||
description: '最大重试次数,默认3次',
|
||
example: 3
|
||
})
|
||
@ApiResponse({
|
||
status: 200,
|
||
description: '获取成功',
|
||
type: ZulipAccountListResponseDto,
|
||
})
|
||
async findErrorAccounts(
|
||
@Query('maxRetryCount') maxRetryCount?: number,
|
||
): Promise<ZulipAccountListResponseDto> {
|
||
return this.zulipAccountsService.findErrorAccounts(maxRetryCount);
|
||
}
|
||
|
||
/**
|
||
* 批量更新账号状态
|
||
*/
|
||
@Put('management/batch-status')
|
||
@ApiOperation({
|
||
summary: '批量更新账号状态',
|
||
description: '批量更新多个账号的状态'
|
||
})
|
||
@ApiResponse({
|
||
status: 200,
|
||
description: '更新成功',
|
||
type: BatchUpdateResponseDto,
|
||
})
|
||
async batchUpdateStatus(@Body() batchDto: BatchUpdateStatusDto): Promise<BatchUpdateResponseDto> {
|
||
return this.zulipAccountsService.batchUpdateStatus(batchDto.ids, batchDto.status);
|
||
}
|
||
|
||
/**
|
||
* 获取账号状态统计
|
||
*/
|
||
@Get('management/statistics')
|
||
@ApiOperation({
|
||
summary: '获取账号状态统计',
|
||
description: '获取各种状态的账号数量统计'
|
||
})
|
||
@ApiResponse({
|
||
status: 200,
|
||
description: '获取成功',
|
||
type: ZulipAccountStatsResponseDto,
|
||
})
|
||
async getStatusStatistics(): Promise<ZulipAccountStatsResponseDto> {
|
||
return this.zulipAccountsService.getStatusStatistics();
|
||
}
|
||
|
||
/**
|
||
* 验证账号有效性
|
||
*/
|
||
@Post('management/verify')
|
||
@ApiOperation({
|
||
summary: '验证账号有效性',
|
||
description: '验证指定游戏用户的Zulip账号关联是否有效'
|
||
})
|
||
@ApiResponse({
|
||
status: 200,
|
||
description: '验证完成',
|
||
type: VerifyAccountResponseDto,
|
||
})
|
||
async verifyAccount(@Body() verifyDto: VerifyAccountDto): Promise<VerifyAccountResponseDto> {
|
||
return this.zulipAccountsService.verifyAccount(verifyDto.gameUserId);
|
||
}
|
||
|
||
/**
|
||
* 检查邮箱是否已存在
|
||
*/
|
||
@Get('validation/email-exists/:email')
|
||
@ApiOperation({
|
||
summary: '检查邮箱是否已存在',
|
||
description: '检查指定的Zulip邮箱是否已被其他账号使用'
|
||
})
|
||
@ApiParam({
|
||
name: 'email',
|
||
description: 'Zulip邮箱地址',
|
||
example: 'user@example.com'
|
||
})
|
||
@ApiQuery({
|
||
name: 'excludeId',
|
||
required: false,
|
||
description: '排除的记录ID(用于更新时检查)',
|
||
example: '1'
|
||
})
|
||
@ApiResponse({
|
||
status: 200,
|
||
description: '检查完成',
|
||
schema: {
|
||
type: 'object',
|
||
properties: {
|
||
exists: { type: 'boolean', example: false },
|
||
email: { type: 'string', example: 'user@example.com' }
|
||
}
|
||
}
|
||
})
|
||
async checkEmailExists(
|
||
@Param('email') email: string,
|
||
@Query('excludeId') excludeId?: string,
|
||
): Promise<{ exists: boolean; email: string }> {
|
||
const exists = await this.zulipAccountsService.existsByEmail(email, excludeId);
|
||
return { exists, email };
|
||
}
|
||
|
||
/**
|
||
* 检查Zulip用户ID是否已存在
|
||
*/
|
||
@Get('validation/zulip-user-exists/:zulipUserId')
|
||
@ApiOperation({
|
||
summary: '检查Zulip用户ID是否已存在',
|
||
description: '检查指定的Zulip用户ID是否已被其他账号使用'
|
||
})
|
||
@ApiParam({
|
||
name: 'zulipUserId',
|
||
description: 'Zulip用户ID',
|
||
example: '67890'
|
||
})
|
||
@ApiQuery({
|
||
name: 'excludeId',
|
||
required: false,
|
||
description: '排除的记录ID(用于更新时检查)',
|
||
example: '1'
|
||
})
|
||
@ApiResponse({
|
||
status: 200,
|
||
description: '检查完成',
|
||
schema: {
|
||
type: 'object',
|
||
properties: {
|
||
exists: { type: 'boolean', example: false },
|
||
zulipUserId: { type: 'number', example: 67890 }
|
||
}
|
||
}
|
||
})
|
||
async checkZulipUserIdExists(
|
||
@Param('zulipUserId') zulipUserId: string,
|
||
@Query('excludeId') excludeId?: string,
|
||
): Promise<{ exists: boolean; zulipUserId: number }> {
|
||
const zulipUserIdNum = parseInt(zulipUserId);
|
||
const exists = await this.zulipAccountsService.existsByZulipUserId(zulipUserIdNum, excludeId);
|
||
return { exists, zulipUserId: zulipUserIdNum };
|
||
}
|
||
} |