forked from datawhale/whale-town-end
feat:实现位置广播系统
- 添加位置广播核心控制器和服务 - 实现健康检查和位置同步功能 - 添加WebSocket实时位置更新支持 - 完善位置广播的测试覆盖
This commit is contained in:
727
src/business/location_broadcast/location_broadcast.controller.ts
Normal file
727
src/business/location_broadcast/location_broadcast.controller.ts
Normal file
@@ -0,0 +1,727 @@
|
||||
/**
|
||||
* 位置广播HTTP API控制器
|
||||
*
|
||||
* 功能描述:
|
||||
* - 提供位置广播系统的REST API接口
|
||||
* - 处理HTTP请求和响应格式化
|
||||
* - 集成JWT认证和权限验证
|
||||
* - 提供完整的API文档和错误处理
|
||||
*
|
||||
* 职责分离:
|
||||
* - HTTP处理:专注于HTTP请求和响应的处理
|
||||
* - 数据转换:请求参数和响应数据的格式转换
|
||||
* - 权限验证:API访问权限的验证和控制
|
||||
* - 文档生成:Swagger API文档的自动生成
|
||||
*
|
||||
* 技术实现:
|
||||
* - NestJS控制器:使用装饰器定义API端点
|
||||
* - Swagger集成:自动生成API文档
|
||||
* - 数据验证:使用DTO进行请求数据验证
|
||||
* - 异常处理:统一的HTTP异常处理机制
|
||||
*
|
||||
* 最近修改:
|
||||
* - 2026-01-08: 功能新增 - 创建位置广播HTTP API控制器
|
||||
*
|
||||
* @author moyin
|
||||
* @version 1.0.0
|
||||
* @since 2026-01-08
|
||||
* @lastModified 2026-01-08
|
||||
*/
|
||||
|
||||
import {
|
||||
Controller,
|
||||
Get,
|
||||
Post,
|
||||
Put,
|
||||
Delete,
|
||||
Body,
|
||||
Param,
|
||||
Query,
|
||||
UseGuards,
|
||||
HttpStatus,
|
||||
HttpException,
|
||||
Logger,
|
||||
} from '@nestjs/common';
|
||||
import {
|
||||
ApiTags,
|
||||
ApiOperation,
|
||||
ApiResponse,
|
||||
ApiParam,
|
||||
ApiQuery,
|
||||
ApiBearerAuth,
|
||||
ApiBody,
|
||||
} from '@nestjs/swagger';
|
||||
import { JwtAuthGuard, AuthenticatedRequest } from '../auth/jwt_auth.guard';
|
||||
import { CurrentUser } from '../auth/current_user.decorator';
|
||||
import { JwtPayload } from '../../core/login_core/login_core.service';
|
||||
|
||||
// 导入业务服务
|
||||
import {
|
||||
LocationBroadcastService,
|
||||
LocationSessionService,
|
||||
LocationPositionService,
|
||||
} from './services';
|
||||
|
||||
// 导入DTO
|
||||
import {
|
||||
CreateSessionDto,
|
||||
JoinSessionDto,
|
||||
UpdatePositionDto,
|
||||
SessionQueryDto,
|
||||
PositionQueryDto,
|
||||
UpdateSessionConfigDto,
|
||||
} from './dto/api.dto';
|
||||
|
||||
/**
|
||||
* 位置广播API控制器
|
||||
*
|
||||
* 提供以下API端点:
|
||||
* - 会话管理:创建、查询、配置会话
|
||||
* - 位置管理:查询位置、获取统计信息
|
||||
* - 用户管理:获取用户状态、清理数据
|
||||
*/
|
||||
@ApiTags('位置广播')
|
||||
@Controller('location-broadcast')
|
||||
@ApiBearerAuth()
|
||||
@UseGuards(JwtAuthGuard)
|
||||
export class LocationBroadcastController {
|
||||
private readonly logger = new Logger(LocationBroadcastController.name);
|
||||
|
||||
constructor(
|
||||
private readonly locationBroadcastService: LocationBroadcastService,
|
||||
private readonly locationSessionService: LocationSessionService,
|
||||
private readonly locationPositionService: LocationPositionService,
|
||||
) {}
|
||||
|
||||
/**
|
||||
* 创建新会话
|
||||
*/
|
||||
@Post('sessions')
|
||||
@ApiOperation({
|
||||
summary: '创建新会话',
|
||||
description: '创建一个新的游戏会话,用于多人位置广播',
|
||||
})
|
||||
@ApiBody({ type: CreateSessionDto })
|
||||
@ApiResponse({
|
||||
status: 201,
|
||||
description: '会话创建成功',
|
||||
schema: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
success: { type: 'boolean', example: true },
|
||||
data: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
sessionId: { type: 'string', example: 'session_12345' },
|
||||
createdAt: { type: 'number', example: 1641024000000 },
|
||||
config: { type: 'object' },
|
||||
},
|
||||
},
|
||||
message: { type: 'string', example: '会话创建成功' },
|
||||
},
|
||||
},
|
||||
})
|
||||
@ApiResponse({ status: 400, description: '请求参数错误' })
|
||||
@ApiResponse({ status: 409, description: '会话ID已存在' })
|
||||
async createSession(
|
||||
@Body() createSessionDto: CreateSessionDto,
|
||||
@CurrentUser() user: JwtPayload,
|
||||
) {
|
||||
try {
|
||||
this.logger.log('创建会话API请求', {
|
||||
operation: 'createSession',
|
||||
sessionId: createSessionDto.sessionId,
|
||||
userId: user.sub,
|
||||
timestamp: new Date().toISOString(),
|
||||
});
|
||||
|
||||
const session = await this.locationSessionService.createSession({
|
||||
sessionId: createSessionDto.sessionId,
|
||||
creatorId: user.sub,
|
||||
name: createSessionDto.name,
|
||||
description: createSessionDto.description,
|
||||
maxUsers: createSessionDto.maxUsers,
|
||||
allowObservers: createSessionDto.allowObservers,
|
||||
password: createSessionDto.password,
|
||||
allowedMaps: createSessionDto.allowedMaps,
|
||||
broadcastRange: createSessionDto.broadcastRange,
|
||||
metadata: createSessionDto.metadata,
|
||||
});
|
||||
|
||||
return {
|
||||
success: true,
|
||||
data: {
|
||||
sessionId: session.sessionId,
|
||||
createdAt: session.createdAt,
|
||||
config: session.config,
|
||||
metadata: session.metadata,
|
||||
},
|
||||
message: '会话创建成功',
|
||||
};
|
||||
} catch (error) {
|
||||
this.logger.error('创建会话失败', {
|
||||
operation: 'createSession',
|
||||
sessionId: createSessionDto.sessionId,
|
||||
userId: user.sub,
|
||||
error: error instanceof Error ? error.message : String(error),
|
||||
});
|
||||
|
||||
if (error instanceof HttpException) {
|
||||
throw error;
|
||||
}
|
||||
|
||||
throw new HttpException(
|
||||
{
|
||||
success: false,
|
||||
message: '会话创建失败',
|
||||
error: error instanceof Error ? error.message : String(error),
|
||||
},
|
||||
HttpStatus.INTERNAL_SERVER_ERROR,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询会话列表
|
||||
*/
|
||||
@Get('sessions')
|
||||
@ApiOperation({
|
||||
summary: '查询会话列表',
|
||||
description: '根据条件查询游戏会话列表',
|
||||
})
|
||||
@ApiQuery({ name: 'status', required: false, description: '会话状态' })
|
||||
@ApiQuery({ name: 'minUsers', required: false, description: '最小用户数' })
|
||||
@ApiQuery({ name: 'maxUsers', required: false, description: '最大用户数' })
|
||||
@ApiQuery({ name: 'publicOnly', required: false, description: '只显示公开会话' })
|
||||
@ApiQuery({ name: 'offset', required: false, description: '分页偏移' })
|
||||
@ApiQuery({ name: 'limit', required: false, description: '分页大小' })
|
||||
@ApiResponse({
|
||||
status: 200,
|
||||
description: '查询成功',
|
||||
schema: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
success: { type: 'boolean', example: true },
|
||||
data: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
sessions: { type: 'array', items: { type: 'object' } },
|
||||
total: { type: 'number', example: 10 },
|
||||
page: { type: 'number', example: 1 },
|
||||
pageSize: { type: 'number', example: 10 },
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
})
|
||||
async querySessions(@Query() query: SessionQueryDto) {
|
||||
try {
|
||||
const result = await this.locationSessionService.querySessions({
|
||||
status: query.status as any, // 类型转换,因为DTO中是string类型
|
||||
minUsers: query.minUsers,
|
||||
maxUsers: query.maxUsers,
|
||||
publicOnly: query.publicOnly,
|
||||
offset: query.offset || 0,
|
||||
limit: query.limit || 10,
|
||||
});
|
||||
|
||||
return {
|
||||
success: true,
|
||||
data: result,
|
||||
};
|
||||
} catch (error) {
|
||||
this.logger.error('查询会话列表失败', {
|
||||
operation: 'querySessions',
|
||||
query,
|
||||
error: error instanceof Error ? error.message : String(error),
|
||||
});
|
||||
|
||||
throw new HttpException(
|
||||
{
|
||||
success: false,
|
||||
message: '查询会话列表失败',
|
||||
error: error instanceof Error ? error.message : String(error),
|
||||
},
|
||||
HttpStatus.INTERNAL_SERVER_ERROR,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取会话详情
|
||||
*/
|
||||
@Get('sessions/:sessionId')
|
||||
@ApiOperation({
|
||||
summary: '获取会话详情',
|
||||
description: '获取指定会话的详细信息,包括用户列表和位置信息',
|
||||
})
|
||||
@ApiParam({ name: 'sessionId', description: '会话ID' })
|
||||
@ApiResponse({
|
||||
status: 200,
|
||||
description: '获取成功',
|
||||
schema: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
success: { type: 'boolean', example: true },
|
||||
data: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
session: { type: 'object' },
|
||||
users: { type: 'array', items: { type: 'object' } },
|
||||
onlineCount: { type: 'number', example: 5 },
|
||||
activeMaps: { type: 'array', items: { type: 'string' } },
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
})
|
||||
@ApiResponse({ status: 404, description: '会话不存在' })
|
||||
async getSessionDetail(
|
||||
@Param('sessionId') sessionId: string,
|
||||
@CurrentUser() user: JwtPayload,
|
||||
) {
|
||||
try {
|
||||
const result = await this.locationSessionService.getSessionDetail(
|
||||
sessionId,
|
||||
user.sub,
|
||||
);
|
||||
|
||||
return {
|
||||
success: true,
|
||||
data: result,
|
||||
};
|
||||
} catch (error) {
|
||||
this.logger.error('获取会话详情失败', {
|
||||
operation: 'getSessionDetail',
|
||||
sessionId,
|
||||
userId: user.sub,
|
||||
error: error instanceof Error ? error.message : String(error),
|
||||
});
|
||||
|
||||
if (error instanceof HttpException) {
|
||||
throw error;
|
||||
}
|
||||
|
||||
throw new HttpException(
|
||||
{
|
||||
success: false,
|
||||
message: '获取会话详情失败',
|
||||
error: error instanceof Error ? error.message : String(error),
|
||||
},
|
||||
HttpStatus.INTERNAL_SERVER_ERROR,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新会话配置
|
||||
*/
|
||||
@Put('sessions/:sessionId/config')
|
||||
@ApiOperation({
|
||||
summary: '更新会话配置',
|
||||
description: '更新指定会话的配置参数(需要管理员权限)',
|
||||
})
|
||||
@ApiParam({ name: 'sessionId', description: '会话ID' })
|
||||
@ApiBody({ type: UpdateSessionConfigDto })
|
||||
@ApiResponse({
|
||||
status: 200,
|
||||
description: '更新成功',
|
||||
schema: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
success: { type: 'boolean', example: true },
|
||||
data: { type: 'object' },
|
||||
message: { type: 'string', example: '会话配置更新成功' },
|
||||
},
|
||||
},
|
||||
})
|
||||
@ApiResponse({ status: 403, description: '权限不足' })
|
||||
@ApiResponse({ status: 404, description: '会话不存在' })
|
||||
async updateSessionConfig(
|
||||
@Param('sessionId') sessionId: string,
|
||||
@Body() updateConfigDto: UpdateSessionConfigDto,
|
||||
@CurrentUser() user: JwtPayload,
|
||||
) {
|
||||
try {
|
||||
const session = await this.locationSessionService.updateSessionConfig(
|
||||
sessionId,
|
||||
updateConfigDto,
|
||||
user.sub,
|
||||
);
|
||||
|
||||
return {
|
||||
success: true,
|
||||
data: session,
|
||||
message: '会话配置更新成功',
|
||||
};
|
||||
} catch (error) {
|
||||
this.logger.error('更新会话配置失败', {
|
||||
operation: 'updateSessionConfig',
|
||||
sessionId,
|
||||
userId: user.sub,
|
||||
error: error instanceof Error ? error.message : String(error),
|
||||
});
|
||||
|
||||
if (error instanceof HttpException) {
|
||||
throw error;
|
||||
}
|
||||
|
||||
throw new HttpException(
|
||||
{
|
||||
success: false,
|
||||
message: '更新会话配置失败',
|
||||
error: error instanceof Error ? error.message : String(error),
|
||||
},
|
||||
HttpStatus.INTERNAL_SERVER_ERROR,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 结束会话
|
||||
*/
|
||||
@Delete('sessions/:sessionId')
|
||||
@ApiOperation({
|
||||
summary: '结束会话',
|
||||
description: '结束指定的游戏会话(需要管理员权限)',
|
||||
})
|
||||
@ApiParam({ name: 'sessionId', description: '会话ID' })
|
||||
@ApiResponse({
|
||||
status: 200,
|
||||
description: '会话结束成功',
|
||||
schema: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
success: { type: 'boolean', example: true },
|
||||
message: { type: 'string', example: '会话结束成功' },
|
||||
},
|
||||
},
|
||||
})
|
||||
@ApiResponse({ status: 403, description: '权限不足' })
|
||||
@ApiResponse({ status: 404, description: '会话不存在' })
|
||||
async endSession(
|
||||
@Param('sessionId') sessionId: string,
|
||||
@CurrentUser() user: JwtPayload,
|
||||
) {
|
||||
try {
|
||||
await this.locationSessionService.endSession(sessionId, user.sub);
|
||||
|
||||
return {
|
||||
success: true,
|
||||
message: '会话结束成功',
|
||||
};
|
||||
} catch (error) {
|
||||
this.logger.error('结束会话失败', {
|
||||
operation: 'endSession',
|
||||
sessionId,
|
||||
userId: user.sub,
|
||||
error: error instanceof Error ? error.message : String(error),
|
||||
});
|
||||
|
||||
if (error instanceof HttpException) {
|
||||
throw error;
|
||||
}
|
||||
|
||||
throw new HttpException(
|
||||
{
|
||||
success: false,
|
||||
message: '结束会话失败',
|
||||
error: error instanceof Error ? error.message : String(error),
|
||||
},
|
||||
HttpStatus.INTERNAL_SERVER_ERROR,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询位置信息
|
||||
*/
|
||||
@Get('positions')
|
||||
@ApiOperation({
|
||||
summary: '查询位置信息',
|
||||
description: '根据条件查询用户位置信息',
|
||||
})
|
||||
@ApiQuery({ name: 'userIds', required: false, description: '用户ID列表(逗号分隔)' })
|
||||
@ApiQuery({ name: 'mapId', required: false, description: '地图ID' })
|
||||
@ApiQuery({ name: 'sessionId', required: false, description: '会话ID' })
|
||||
@ApiQuery({ name: 'centerX', required: false, description: '范围查询中心X坐标' })
|
||||
@ApiQuery({ name: 'centerY', required: false, description: '范围查询中心Y坐标' })
|
||||
@ApiQuery({ name: 'radius', required: false, description: '范围查询半径' })
|
||||
@ApiQuery({ name: 'offset', required: false, description: '分页偏移' })
|
||||
@ApiQuery({ name: 'limit', required: false, description: '分页大小' })
|
||||
@ApiResponse({
|
||||
status: 200,
|
||||
description: '查询成功',
|
||||
schema: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
success: { type: 'boolean', example: true },
|
||||
data: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
positions: { type: 'array', items: { type: 'object' } },
|
||||
total: { type: 'number', example: 20 },
|
||||
timestamp: { type: 'number', example: 1641024000000 },
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
})
|
||||
async queryPositions(@Query() query: PositionQueryDto) {
|
||||
try {
|
||||
const userIds = query.userIds ? query.userIds.split(',') : undefined;
|
||||
const range = (query.centerX !== undefined && query.centerY !== undefined && query.radius !== undefined) ? {
|
||||
centerX: query.centerX,
|
||||
centerY: query.centerY,
|
||||
radius: query.radius,
|
||||
} : undefined;
|
||||
|
||||
const result = await this.locationPositionService.queryPositions({
|
||||
userIds,
|
||||
mapId: query.mapId,
|
||||
sessionId: query.sessionId,
|
||||
range,
|
||||
pagination: {
|
||||
offset: query.offset || 0,
|
||||
limit: query.limit || 50,
|
||||
},
|
||||
});
|
||||
|
||||
return {
|
||||
success: true,
|
||||
data: result,
|
||||
};
|
||||
} catch (error) {
|
||||
this.logger.error('查询位置信息失败', {
|
||||
operation: 'queryPositions',
|
||||
query,
|
||||
error: error instanceof Error ? error.message : String(error),
|
||||
});
|
||||
|
||||
throw new HttpException(
|
||||
{
|
||||
success: false,
|
||||
message: '查询位置信息失败',
|
||||
error: error instanceof Error ? error.message : String(error),
|
||||
},
|
||||
HttpStatus.INTERNAL_SERVER_ERROR,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取位置统计信息
|
||||
*/
|
||||
@Get('positions/stats')
|
||||
@ApiOperation({
|
||||
summary: '获取位置统计信息',
|
||||
description: '获取位置数据的统计信息,包括用户分布、活跃地图等',
|
||||
})
|
||||
@ApiQuery({ name: 'mapId', required: false, description: '地图ID' })
|
||||
@ApiQuery({ name: 'sessionId', required: false, description: '会话ID' })
|
||||
@ApiResponse({
|
||||
status: 200,
|
||||
description: '获取成功',
|
||||
schema: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
success: { type: 'boolean', example: true },
|
||||
data: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
totalUsers: { type: 'number', example: 100 },
|
||||
onlineUsers: { type: 'number', example: 85 },
|
||||
activeMaps: { type: 'number', example: 5 },
|
||||
mapDistribution: { type: 'object' },
|
||||
updateFrequency: { type: 'number', example: 2.5 },
|
||||
timestamp: { type: 'number', example: 1641024000000 },
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
})
|
||||
async getPositionStats(
|
||||
@Query('mapId') mapId?: string,
|
||||
@Query('sessionId') sessionId?: string,
|
||||
) {
|
||||
try {
|
||||
const result = await this.locationPositionService.getPositionStats({
|
||||
mapId,
|
||||
sessionId,
|
||||
});
|
||||
|
||||
return {
|
||||
success: true,
|
||||
data: result,
|
||||
};
|
||||
} catch (error) {
|
||||
this.logger.error('获取位置统计失败', {
|
||||
operation: 'getPositionStats',
|
||||
mapId,
|
||||
sessionId,
|
||||
error: error instanceof Error ? error.message : String(error),
|
||||
});
|
||||
|
||||
throw new HttpException(
|
||||
{
|
||||
success: false,
|
||||
message: '获取位置统计失败',
|
||||
error: error instanceof Error ? error.message : String(error),
|
||||
},
|
||||
HttpStatus.INTERNAL_SERVER_ERROR,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取用户位置历史
|
||||
*/
|
||||
@Get('users/:userId/position-history')
|
||||
@ApiOperation({
|
||||
summary: '获取用户位置历史',
|
||||
description: '获取指定用户的位置历史记录',
|
||||
})
|
||||
@ApiParam({ name: 'userId', description: '用户ID' })
|
||||
@ApiQuery({ name: 'mapId', required: false, description: '地图ID过滤' })
|
||||
@ApiQuery({ name: 'limit', required: false, description: '最大记录数' })
|
||||
@ApiResponse({
|
||||
status: 200,
|
||||
description: '获取成功',
|
||||
schema: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
success: { type: 'boolean', example: true },
|
||||
data: {
|
||||
type: 'array',
|
||||
items: { type: 'object' },
|
||||
},
|
||||
},
|
||||
},
|
||||
})
|
||||
async getUserPositionHistory(
|
||||
@Param('userId') userId: string,
|
||||
@CurrentUser() user: JwtPayload,
|
||||
@Query('mapId') mapId?: string,
|
||||
@Query('limit') limit?: number,
|
||||
) {
|
||||
try {
|
||||
// 权限检查:只能查看自己的历史记录,或者管理员可以查看所有
|
||||
if (userId !== user.sub && user.role < 2) {
|
||||
throw new HttpException(
|
||||
{
|
||||
success: false,
|
||||
message: '权限不足,只能查看自己的位置历史',
|
||||
},
|
||||
HttpStatus.FORBIDDEN,
|
||||
);
|
||||
}
|
||||
|
||||
const result = await this.locationPositionService.getPositionHistory({
|
||||
userId,
|
||||
mapId,
|
||||
limit: limit || 100,
|
||||
});
|
||||
|
||||
return {
|
||||
success: true,
|
||||
data: result,
|
||||
};
|
||||
} catch (error) {
|
||||
this.logger.error('获取用户位置历史失败', {
|
||||
operation: 'getUserPositionHistory',
|
||||
userId,
|
||||
requestUserId: user.sub,
|
||||
error: error instanceof Error ? error.message : String(error),
|
||||
});
|
||||
|
||||
if (error instanceof HttpException) {
|
||||
throw error;
|
||||
}
|
||||
|
||||
throw new HttpException(
|
||||
{
|
||||
success: false,
|
||||
message: '获取用户位置历史失败',
|
||||
error: error instanceof Error ? error.message : String(error),
|
||||
},
|
||||
HttpStatus.INTERNAL_SERVER_ERROR,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 清理用户数据
|
||||
*/
|
||||
@Delete('users/:userId/data')
|
||||
@ApiOperation({
|
||||
summary: '清理用户数据',
|
||||
description: '清理指定用户的位置广播相关数据(需要管理员权限)',
|
||||
})
|
||||
@ApiParam({ name: 'userId', description: '用户ID' })
|
||||
@ApiResponse({
|
||||
status: 200,
|
||||
description: '清理成功',
|
||||
schema: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
success: { type: 'boolean', example: true },
|
||||
message: { type: 'string', example: '用户数据清理成功' },
|
||||
},
|
||||
},
|
||||
})
|
||||
@ApiResponse({ status: 403, description: '权限不足' })
|
||||
async cleanupUserData(
|
||||
@Param('userId') userId: string,
|
||||
@CurrentUser() user: JwtPayload,
|
||||
) {
|
||||
try {
|
||||
// 权限检查:只有管理员或用户本人可以清理数据
|
||||
if (userId !== user.sub && user.role < 2) {
|
||||
throw new HttpException(
|
||||
{
|
||||
success: false,
|
||||
message: '权限不足,只能清理自己的数据',
|
||||
},
|
||||
HttpStatus.FORBIDDEN,
|
||||
);
|
||||
}
|
||||
|
||||
const success = await this.locationBroadcastService.cleanupUserData(userId);
|
||||
|
||||
if (!success) {
|
||||
throw new HttpException(
|
||||
{
|
||||
success: false,
|
||||
message: '用户数据清理失败',
|
||||
},
|
||||
HttpStatus.INTERNAL_SERVER_ERROR,
|
||||
);
|
||||
}
|
||||
|
||||
return {
|
||||
success: true,
|
||||
message: '用户数据清理成功',
|
||||
};
|
||||
} catch (error) {
|
||||
this.logger.error('清理用户数据失败', {
|
||||
operation: 'cleanupUserData',
|
||||
userId,
|
||||
operatorId: user.sub,
|
||||
error: error instanceof Error ? error.message : String(error),
|
||||
});
|
||||
|
||||
if (error instanceof HttpException) {
|
||||
throw error;
|
||||
}
|
||||
|
||||
throw new HttpException(
|
||||
{
|
||||
success: false,
|
||||
message: '清理用户数据失败',
|
||||
error: error instanceof Error ? error.message : String(error),
|
||||
},
|
||||
HttpStatus.INTERNAL_SERVER_ERROR,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user