import { Controller, Get, Post, Query, Param, Body, UseGuards, HttpCode, HttpStatus, Inject, Logger, } from '@nestjs/common'; import { ApiTags, ApiOperation, ApiResponse, ApiBearerAuth, ApiParam, } from '@nestjs/swagger'; import { JwtAuthGuard } from '../guards'; import { CurrentUser } from '../decorators'; import { ReferralService } from '../../application/services'; import { ValidateReferralCodeDto, GetDirectReferralsDto, ReferralInfoResponseDto, DirectReferralsResponseDto, ValidateCodeResponseDto, CreateReferralDto, } from '../dto'; import { CreateReferralRelationshipCommand } from '../../application/commands'; import { GetUserReferralInfoQuery, GetDirectReferralsQuery } from '../../application/queries'; import { REFERRAL_RELATIONSHIP_REPOSITORY, IReferralRelationshipRepository, TEAM_STATISTICS_REPOSITORY, ITeamStatisticsRepository, } from '../../domain'; import { AuthorizationServiceClient } from '../../infrastructure/external'; @ApiTags('Referral') @Controller('referral') export class ReferralController { constructor( private readonly referralService: ReferralService, @Inject(REFERRAL_RELATIONSHIP_REPOSITORY) private readonly referralRepo: IReferralRelationshipRepository, @Inject(TEAM_STATISTICS_REPOSITORY) private readonly teamStatsRepo: ITeamStatisticsRepository, ) {} @Get('me') @UseGuards(JwtAuthGuard) @ApiBearerAuth() @ApiOperation({ summary: '获取当前用户推荐信息' }) @ApiResponse({ status: 200, type: ReferralInfoResponseDto }) async getMyReferralInfo(@CurrentUser('accountSequence') accountSequence: string): Promise { this.logger.log(`[getMyReferralInfo] accountSequence from JWT: ${accountSequence}`); // 直接使用JWT中的accountSequence查询 const query = new GetUserReferralInfoQuery(accountSequence); return this.referralService.getUserReferralInfo(query); } @Get('me/direct-referrals') @UseGuards(JwtAuthGuard) @ApiBearerAuth() @ApiOperation({ summary: '获取当前用户直推列表' }) @ApiResponse({ status: 200, type: DirectReferralsResponseDto }) async getMyDirectReferrals( @CurrentUser('accountSequence') accountSequence: string, @Query() dto: GetDirectReferralsDto, ): Promise { const query = new GetDirectReferralsQuery(accountSequence, dto.limit, dto.offset); return this.referralService.getDirectReferrals(query); } @Get('validate/:code') @ApiOperation({ summary: '验证推荐码是否有效' }) @ApiParam({ name: 'code', description: '推荐码' }) @ApiResponse({ status: 200, type: ValidateCodeResponseDto }) async validateCode(@Param('code') code: string): Promise { const referrer = await this.referralService.getReferrerByCode(code.toUpperCase()); return { valid: referrer !== null, referrerId: referrer?.userId, }; } @Post('validate') @HttpCode(HttpStatus.OK) @ApiOperation({ summary: '验证推荐码 (POST方式)' }) @ApiResponse({ status: 200, type: ValidateCodeResponseDto }) async validateCodePost(@Body() dto: ValidateReferralCodeDto): Promise { const referrer = await this.referralService.getReferrerByCode(dto.code.toUpperCase()); return { valid: referrer !== null, referrerId: referrer?.userId, }; } private readonly logger = new Logger(ReferralController.name); /** * 获取用户的推荐链(内部API,供 reward-service 调用) * 返回直接推荐人及其认种状态 * * @param accountSequence 支持两种格式: * - accountSequence 格式: D25121300006 或 25121300006 (去掉D前缀) * - 纯数字 userId 格式: 2 (旧格式,不推荐) */ @Get('chain/:accountSequence') @ApiOperation({ summary: '获取用户推荐链(内部API)' }) @ApiParam({ name: 'accountSequence', description: '用户标识 (accountSequence 或 userId)' }) @ApiResponse({ status: 200, description: '推荐链数据', schema: { type: 'object', properties: { ancestors: { type: 'array', items: { type: 'object', properties: { userId: { type: 'string' }, accountSequence: { type: 'string' }, hasPlanted: { type: 'boolean' }, }, }, }, }, }, }) async getReferralChainForReward( @Param('accountSequence') accountSequence: string, ): Promise<{ ancestors: Array<{ userId: string; accountSequence: string; hasPlanted: boolean }> }> { this.logger.log(`[INTERNAL] getReferralChain: accountSequence=${accountSequence}`); let relationship; // 判断传入的是 accountSequence 还是 userId // accountSequence 格式: D25121300006 或 25121300006 (11位数字或D+11位数字) if (accountSequence.startsWith('D') || accountSequence.length >= 11) { // 使用 accountSequence 查询 const normalizedSequence = accountSequence.startsWith('D') ? accountSequence : `D${accountSequence}`; this.logger.log(`Using accountSequence query: ${normalizedSequence}`); relationship = await this.referralRepo.findByAccountSequence(normalizedSequence); } else { // 尝试作为 userId 查询 (向后兼容) this.logger.log(`Using userId query: ${accountSequence}`); try { const userIdBigInt = BigInt(accountSequence); relationship = await this.referralRepo.findByUserId(userIdBigInt); } catch { this.logger.warn(`Invalid userId format: ${accountSequence}`); return { ancestors: [] }; } } if (!relationship || !relationship.referrerId) { this.logger.log(`No referral found for accountSequence: ${accountSequence}`); return { ancestors: [] }; } // 获取直接推荐人的认种状态 const referrerId = relationship.referrerId; const referrerStats = await this.teamStatsRepo.findByUserId(referrerId); const hasPlanted = referrerStats ? referrerStats.personalPlantingCount > 0 : false; // 获取推荐人的 accountSequence const referrerRelationship = await this.referralRepo.findByUserId(referrerId); const referrerAccountSequence = referrerRelationship?.accountSequence || referrerId.toString(); this.logger.log(`Found referrer: userId=${referrerId}, accountSequence=${referrerAccountSequence}, hasPlanted=${hasPlanted}`); return { ancestors: [ { userId: referrerId.toString(), accountSequence: referrerAccountSequence, hasPlanted, }, ], }; } @Post() @ApiOperation({ summary: '创建推荐关系 (内部接口)' }) @ApiResponse({ status: 201, description: '创建成功' }) async createReferralRelationship( @Body() dto: CreateReferralDto, ): Promise<{ referralCode: string }> { const command = new CreateReferralRelationshipCommand( BigInt(dto.userId), dto.accountSequence, dto.referralCode, dto.referrerCode ?? null, dto.inviterAccountSequence ?? null, ); return this.referralService.createReferralRelationship(command); } @Get('user/:userId') @UseGuards(JwtAuthGuard) @ApiBearerAuth() @ApiOperation({ summary: '获取指定用户推荐信息' }) @ApiParam({ name: 'userId', description: '用户ID' }) @ApiResponse({ status: 200, type: ReferralInfoResponseDto }) async getUserReferralInfo(@Param('userId') userId: string): Promise { const query = new GetUserReferralInfoQuery(userId); // userId 已经是字符串 return this.referralService.getUserReferralInfo(query); } @Get('user/:accountSequence/direct-referrals') @UseGuards(JwtAuthGuard) @ApiBearerAuth() @ApiOperation({ summary: '获取指定用户的直推列表(用于伞下树懒加载)' }) @ApiParam({ name: 'accountSequence', description: '账户序列号 (格式: D + YYMMDD + 5位序号)' }) @ApiResponse({ status: 200, type: DirectReferralsResponseDto }) async getUserDirectReferrals( @Param('accountSequence') accountSequence: string, @Query() dto: GetDirectReferralsDto, ): Promise { this.logger.log(`[getUserDirectReferrals] accountSequence=${accountSequence}, limit=${dto.limit}, offset=${dto.offset}`); const query = new GetDirectReferralsQuery(accountSequence, dto.limit, dto.offset); return this.referralService.getDirectReferrals(query); } } /** * 内部API控制器 - 供其他微服务调用 * 不需要JWT认证 */ @ApiTags('Internal Referral API') @Controller('referrals') export class InternalReferralController { private readonly logger = new Logger(InternalReferralController.name); constructor( private readonly referralService: ReferralService, private readonly authorizationClient: AuthorizationServiceClient, ) {} @Get(':accountSequence/context') @ApiOperation({ summary: '获取用户推荐上下文信息(内部API)' }) @ApiParam({ name: 'accountSequence', description: '账户序列号' }) @ApiResponse({ status: 200, description: '推荐上下文' }) async getReferralContext( @Param('accountSequence') accountSequence: string, @Query('provinceCode') provinceCode: string, @Query('cityCode') cityCode: string, ) { // 1. 获取用户的推荐链 const query = new GetUserReferralInfoQuery(accountSequence); // accountSequence 现在是字符串 const referralInfo = await this.referralService.getUserReferralInfo(query); // 2. 并行查询授权信息(省/市/社区) // 使用 fallback 机制:如果 authorization-service 不可用,返回 null const authorizations = await this.authorizationClient.findAllNearestAuthorizations( accountSequence, // accountSequence 现在是字符串 provinceCode, cityCode, ); this.logger.debug( `[getReferralContext] accountSequence=${accountSequence}, ` + `provinceCode=${provinceCode}, cityCode=${cityCode}, ` + `referrerId=${referralInfo.referrerId}, ` + `nearestCommunity=${authorizations.nearestCommunity}, ` + `nearestProvinceAuth=${authorizations.nearestProvinceAuth}, ` + `nearestCityAuth=${authorizations.nearestCityAuth}`, ); // 3. 返回完整的推荐上下文信息 return { accountSequence, referralChain: referralInfo.referrerId ? [referralInfo.referrerId] : [], referrerId: referralInfo.referrerId, nearestProvinceAuth: authorizations.nearestProvinceAuth ?? null, // 已经是字符串,不需要 toString() nearestCityAuth: authorizations.nearestCityAuth ?? null, // 已经是字符串,不需要 toString() nearestCommunity: authorizations.nearestCommunity ?? null, // 已经是字符串,不需要 toString() }; } }