feat: 初始化项目
This commit is contained in:
244
src/users/users.controller.ts
Normal file
244
src/users/users.controller.ts
Normal file
@@ -0,0 +1,244 @@
|
||||
import {
|
||||
Controller,
|
||||
Get,
|
||||
Post,
|
||||
Body,
|
||||
Param,
|
||||
HttpCode,
|
||||
HttpStatus,
|
||||
Put,
|
||||
Logger,
|
||||
UseGuards,
|
||||
Inject,
|
||||
Req,
|
||||
} from '@nestjs/common';
|
||||
import { Request } from 'express';
|
||||
import { WINSTON_MODULE_PROVIDER } from 'nest-winston';
|
||||
import { Logger as WinstonLogger } from 'winston';
|
||||
import { UsersService } from './users.service';
|
||||
import { CreateUserDto } from './dto/create-user.dto';
|
||||
import { UserResponseDto } from './dto/user-response.dto';
|
||||
import { UserRelationInfoDto, UserRelationInfoResponseDto } from './dto/user-relation-info.dto';
|
||||
import { ApiOperation, ApiBody, ApiResponse, ApiTags } from '@nestjs/swagger';
|
||||
import { UpdateUserDto, UpdateUserResponseDto } from './dto/update-user.dto';
|
||||
import {
|
||||
CreateClientLogDto,
|
||||
CreateBatchClientLogDto,
|
||||
CreateClientLogResponseDto,
|
||||
CreateBatchClientLogResponseDto,
|
||||
} from './dto/client-log.dto';
|
||||
import { AppleLoginDto, AppleLoginResponseDto, RefreshTokenDto, RefreshTokenResponseDto } from './dto/apple-login.dto';
|
||||
import { DeleteAccountDto, DeleteAccountResponseDto } from './dto/delete-account.dto';
|
||||
import { GuestLoginDto, GuestLoginResponseDto, RefreshGuestTokenDto, RefreshGuestTokenResponseDto } from './dto/guest-login.dto';
|
||||
import { AppStoreServerNotificationDto, ProcessNotificationResponseDto } from './dto/app-store-notification.dto';
|
||||
import { RestorePurchaseDto, RestorePurchaseResponseDto } from './dto/restore-purchase.dto';
|
||||
import { Public } from '../common/decorators/public.decorator';
|
||||
import { CurrentUser } from '../common/decorators/current-user.decorator';
|
||||
import { AccessTokenPayload } from './services/apple-auth.service';
|
||||
import { JwtAuthGuard } from 'src/common/guards/jwt-auth.guard';
|
||||
import { ResponseCode } from 'src/base.dto';
|
||||
|
||||
|
||||
@ApiTags('users')
|
||||
@Controller('users')
|
||||
export class UsersController {
|
||||
private readonly logger = new Logger(UsersController.name);
|
||||
constructor(
|
||||
private readonly usersService: UsersService,
|
||||
@Inject(WINSTON_MODULE_PROVIDER) private readonly winstonLogger: WinstonLogger,
|
||||
) { }
|
||||
|
||||
@UseGuards(JwtAuthGuard)
|
||||
@Get()
|
||||
@HttpCode(HttpStatus.OK)
|
||||
@ApiOperation({ summary: '创建用户' })
|
||||
@ApiBody({ type: CreateUserDto })
|
||||
@ApiResponse({ type: UserResponseDto })
|
||||
async getProfile(@CurrentUser() user: AccessTokenPayload): Promise<UserResponseDto> {
|
||||
this.logger.log(`get profile: ${JSON.stringify(user)}`);
|
||||
return this.usersService.getProfile(user);
|
||||
}
|
||||
|
||||
|
||||
// 更新用户昵称、头像
|
||||
@UseGuards(JwtAuthGuard)
|
||||
@Put('update')
|
||||
async updateUser(@Body() updateUserDto: UpdateUserDto): Promise<UpdateUserResponseDto> {
|
||||
return this.usersService.updateUser(updateUserDto);
|
||||
}
|
||||
|
||||
|
||||
// 获取用户关系
|
||||
@UseGuards(JwtAuthGuard)
|
||||
@Put('relations')
|
||||
async updateOrCreateRelationInfo(@Body() relationInfoDto: UserRelationInfoDto) {
|
||||
return this.usersService.updateOrCreateRelationInfo(relationInfoDto);
|
||||
}
|
||||
|
||||
@UseGuards(JwtAuthGuard)
|
||||
@Get('relations/:userId')
|
||||
async getRelationInfo(@Param('userId') userId: string): Promise<UserRelationInfoResponseDto> {
|
||||
return this.usersService.getRelationInfo(userId);
|
||||
}
|
||||
|
||||
// 创建客户端日志
|
||||
@UseGuards(JwtAuthGuard)
|
||||
@Post('logs')
|
||||
@HttpCode(HttpStatus.OK)
|
||||
@ApiOperation({ summary: '创建客户端日志' })
|
||||
@ApiBody({ type: CreateClientLogDto })
|
||||
async createClientLog(@Body() createClientLogDto: CreateClientLogDto): Promise<CreateClientLogResponseDto> {
|
||||
return this.usersService.createClientLog(createClientLogDto);
|
||||
}
|
||||
|
||||
// 批量创建客户端日志
|
||||
@UseGuards(JwtAuthGuard)
|
||||
@Post('logs/batch')
|
||||
@HttpCode(HttpStatus.OK)
|
||||
@ApiOperation({ summary: '批量创建客户端日志' })
|
||||
@ApiBody({ type: CreateBatchClientLogDto })
|
||||
async createBatchClientLog(@Body() createBatchClientLogDto: CreateBatchClientLogDto): Promise<CreateBatchClientLogResponseDto> {
|
||||
return this.usersService.createBatchClientLog(createBatchClientLogDto);
|
||||
}
|
||||
|
||||
// Apple 登录
|
||||
@Public()
|
||||
@Post('auth/apple/login')
|
||||
@HttpCode(HttpStatus.OK)
|
||||
@ApiOperation({ summary: 'Apple 登录验证' })
|
||||
@ApiBody({ type: AppleLoginDto })
|
||||
@ApiResponse({ type: AppleLoginResponseDto })
|
||||
async appleLogin(@Body() appleLoginDto: AppleLoginDto): Promise<AppleLoginResponseDto> {
|
||||
return this.usersService.appleLogin(appleLoginDto);
|
||||
}
|
||||
|
||||
// 刷新访问令牌
|
||||
@Public()
|
||||
@Post('auth/refresh')
|
||||
@HttpCode(HttpStatus.OK)
|
||||
@ApiOperation({ summary: '刷新访问令牌' })
|
||||
@ApiBody({ type: RefreshTokenDto })
|
||||
@ApiResponse({ type: RefreshTokenResponseDto })
|
||||
async refreshToken(@Body() refreshTokenDto: RefreshTokenDto): Promise<RefreshTokenResponseDto> {
|
||||
return this.usersService.refreshToken(refreshTokenDto);
|
||||
}
|
||||
|
||||
// 删除用户账号
|
||||
@UseGuards(JwtAuthGuard)
|
||||
@Post('delete-account')
|
||||
@HttpCode(HttpStatus.OK)
|
||||
@ApiOperation({ summary: '删除用户账号' })
|
||||
@ApiBody({ type: DeleteAccountDto })
|
||||
@ApiResponse({ type: DeleteAccountResponseDto })
|
||||
async deleteAccount(@CurrentUser() user: AccessTokenPayload): Promise<DeleteAccountResponseDto> {
|
||||
const deleteAccountDto = {
|
||||
userId: user.sub,
|
||||
};
|
||||
return this.usersService.deleteAccount(deleteAccountDto);
|
||||
}
|
||||
|
||||
// 游客登录
|
||||
@Public()
|
||||
@Post('auth/guest/login')
|
||||
@HttpCode(HttpStatus.OK)
|
||||
@ApiOperation({ summary: '游客登录' })
|
||||
@ApiBody({ type: GuestLoginDto })
|
||||
@ApiResponse({ type: GuestLoginResponseDto })
|
||||
async guestLogin(@Body() guestLoginDto: GuestLoginDto): Promise<GuestLoginResponseDto> {
|
||||
return this.usersService.guestLogin(guestLoginDto);
|
||||
}
|
||||
|
||||
// 刷新游客令牌
|
||||
@Public()
|
||||
@Post('auth/guest/refresh')
|
||||
@HttpCode(HttpStatus.OK)
|
||||
@ApiOperation({ summary: '刷新游客访问令牌' })
|
||||
@ApiBody({ type: RefreshGuestTokenDto })
|
||||
@ApiResponse({ type: RefreshGuestTokenResponseDto })
|
||||
async refreshGuestToken(@Body() refreshGuestTokenDto: RefreshGuestTokenDto): Promise<RefreshGuestTokenResponseDto> {
|
||||
return this.usersService.refreshGuestToken(refreshGuestTokenDto);
|
||||
}
|
||||
|
||||
// App Store 服务器通知接收接口
|
||||
@Public()
|
||||
@Post('app-store-notifications')
|
||||
@HttpCode(HttpStatus.OK)
|
||||
@ApiOperation({ summary: '接收App Store服务器通知' })
|
||||
@ApiBody({ type: AppStoreServerNotificationDto })
|
||||
@ApiResponse({ type: ProcessNotificationResponseDto })
|
||||
async handleAppStoreNotification(@Body() notificationDto: AppStoreServerNotificationDto): Promise<ProcessNotificationResponseDto> {
|
||||
this.logger.log(`收到App Store服务器通知`);
|
||||
return this.usersService.processAppStoreNotification(notificationDto);
|
||||
}
|
||||
|
||||
// RevenueCat Webhook
|
||||
@Public()
|
||||
@Post('revenuecat-webhook')
|
||||
@HttpCode(HttpStatus.OK)
|
||||
@ApiOperation({ summary: '接收 RevenueCat webhook' })
|
||||
async handleRevenueCatWebhook(@Body() webhook) {
|
||||
// 使用结构化日志记录webhook接收
|
||||
this.winstonLogger.info('RevenueCat webhook received', {
|
||||
context: 'UsersController',
|
||||
eventType: webhook.event?.type,
|
||||
eventId: webhook.event?.id,
|
||||
appUserId: webhook.event?.app_user_id,
|
||||
timestamp: new Date().toISOString(),
|
||||
webhookData: {
|
||||
apiVersion: webhook.api_version,
|
||||
eventTimestamp: webhook.event?.event_timestamp_ms
|
||||
}
|
||||
});
|
||||
|
||||
try {
|
||||
await this.usersService.handleRevenueCatWebhook(webhook);
|
||||
|
||||
this.winstonLogger.info('RevenueCat webhook processed successfully', {
|
||||
context: 'UsersController',
|
||||
eventType: webhook.event?.type,
|
||||
eventId: webhook.event?.id,
|
||||
appUserId: webhook.event?.app_user_id
|
||||
});
|
||||
|
||||
return { code: ResponseCode.SUCCESS, message: 'success' };
|
||||
} catch (error) {
|
||||
this.winstonLogger.error('RevenueCat webhook processing failed', {
|
||||
context: 'UsersController',
|
||||
eventType: webhook.event?.type,
|
||||
eventId: webhook.event?.id,
|
||||
appUserId: webhook.event?.app_user_id,
|
||||
error: error.message,
|
||||
stack: error.stack
|
||||
});
|
||||
|
||||
return { code: ResponseCode.ERROR, message: error.message };
|
||||
}
|
||||
}
|
||||
|
||||
// 恢复购买
|
||||
@UseGuards(JwtAuthGuard)
|
||||
@Post('restore-purchase')
|
||||
async restorePurchase(
|
||||
@Body() restorePurchaseDto: RestorePurchaseDto,
|
||||
@CurrentUser() user: AccessTokenPayload,
|
||||
@Req() request: Request
|
||||
): Promise<RestorePurchaseResponseDto> {
|
||||
const clientIp = request.ip || request.connection.remoteAddress || 'unknown';
|
||||
const userAgent = request.get('User-Agent') || 'unknown';
|
||||
|
||||
this.logger.log(`恢复购买请求 - 用户ID: ${user.sub}, IP: ${clientIp}`);
|
||||
|
||||
// 记录安全相关信息
|
||||
this.winstonLogger.info('Purchase restore request', {
|
||||
context: 'UsersController',
|
||||
userId: user.sub,
|
||||
clientIp,
|
||||
userAgent,
|
||||
originalAppUserId: restorePurchaseDto.customerInfo?.originalAppUserId,
|
||||
entitlementsCount: Object.keys(restorePurchaseDto.customerInfo?.activeEntitlements || {}).length,
|
||||
nonSubTransactionsCount: restorePurchaseDto.customerInfo?.nonSubscriptionTransactions?.length || 0
|
||||
});
|
||||
|
||||
return this.usersService.restorePurchase(restorePurchaseDto, user.sub, clientIp, userAgent);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user