import { Controller, Get, Param, Query, NotFoundException } from '@nestjs/common'; import { ApiTags, ApiOperation, ApiResponse, ApiParam, ApiQuery } from '@nestjs/swagger'; import { GetContributionAccountQuery } from '../../application/queries/get-contribution-account.query'; import { GetContributionStatsQuery } from '../../application/queries/get-contribution-stats.query'; import { GetContributionRankingQuery } from '../../application/queries/get-contribution-ranking.query'; import { GetPlantingLedgerQuery, PlantingLedgerDto } from '../../application/queries/get-planting-ledger.query'; import { GetTeamTreeQuery, DirectReferralsResponseDto, MyTeamInfoDto } from '../../application/queries/get-team-tree.query'; import { ContributionAccountResponse, ContributionRecordsResponse, ActiveContributionResponse, } from '../dto/response/contribution-account.response'; import { ContributionStatsResponse } from '../dto/response/contribution-stats.response'; import { ContributionRankingResponse, UserRankResponse } from '../dto/response/contribution-ranking.response'; import { GetContributionRecordsRequest } from '../dto/request/get-records.request'; import { Public } from '../../shared/guards/jwt-auth.guard'; @ApiTags('Contribution') @Controller('contribution') export class ContributionController { constructor( private readonly getAccountQuery: GetContributionAccountQuery, private readonly getStatsQuery: GetContributionStatsQuery, private readonly getRankingQuery: GetContributionRankingQuery, private readonly getPlantingLedgerQuery: GetPlantingLedgerQuery, private readonly getTeamTreeQuery: GetTeamTreeQuery, ) {} @Get('stats') @Public() @ApiOperation({ summary: '获取算力统计数据' }) @ApiResponse({ status: 200, type: ContributionStatsResponse }) async getStats(): Promise { return this.getStatsQuery.execute(); } @Get('ranking') @ApiOperation({ summary: '获取算力排行榜' }) @ApiResponse({ status: 200, type: ContributionRankingResponse }) async getRanking(@Query('limit') limit?: number): Promise { const data = await this.getRankingQuery.execute(limit ?? 100); return { data }; } @Get('accounts/:accountSequence') @ApiOperation({ summary: '获取账户算力信息' }) @ApiParam({ name: 'accountSequence', description: '账户序号' }) @ApiResponse({ status: 200, type: ContributionAccountResponse }) async getAccount(@Param('accountSequence') accountSequence: string): Promise { // 始终返回 200,通过 status 字段区分账户状态: // - ACTIVE: 账户正常,有算力数据 // - INACTIVE: 用户存在但暂无认种记录 // - USER_NOT_FOUND: 账户不存在 return this.getAccountQuery.execute(accountSequence); } @Get('accounts/:accountSequence/records') @ApiOperation({ summary: '获取账户算力明细记录' }) @ApiParam({ name: 'accountSequence', description: '账户序号' }) @ApiResponse({ status: 200, type: ContributionRecordsResponse }) async getRecords( @Param('accountSequence') accountSequence: string, @Query() query: GetContributionRecordsRequest, ): Promise { const result = await this.getAccountQuery.getRecords(accountSequence, { sourceType: query.sourceType, includeExpired: query.includeExpired, page: query.page, pageSize: query.pageSize, }); return { ...result, page: query.page ?? 1, pageSize: query.pageSize ?? 50, }; } @Get('accounts/:accountSequence/active') @ApiOperation({ summary: '获取账户活跃算力统计' }) @ApiParam({ name: 'accountSequence', description: '账户序号' }) @ApiResponse({ status: 200, type: ActiveContributionResponse }) @ApiResponse({ status: 404, description: '账户不存在' }) async getActiveContribution(@Param('accountSequence') accountSequence: string): Promise { const result = await this.getAccountQuery.getActiveContribution(accountSequence); if (!result) { throw new NotFoundException(`Account ${accountSequence} not found`); } return result; } @Get('accounts/:accountSequence/rank') @ApiOperation({ summary: '获取账户排名' }) @ApiParam({ name: 'accountSequence', description: '账户序号' }) @ApiResponse({ status: 200, type: UserRankResponse }) @ApiResponse({ status: 404, description: '账户不存在' }) async getUserRank(@Param('accountSequence') accountSequence: string): Promise { const result = await this.getRankingQuery.getUserRank(accountSequence); if (!result) { throw new NotFoundException(`Account ${accountSequence} not found`); } return result; } @Get('accounts/:accountSequence/planting-ledger') @ApiOperation({ summary: '获取账户认种分类账' }) @ApiParam({ name: 'accountSequence', description: '账户序号' }) @ApiQuery({ name: 'page', required: false, type: Number, description: '页码' }) @ApiQuery({ name: 'pageSize', required: false, type: Number, description: '每页数量' }) @ApiResponse({ status: 200, description: '认种分类账' }) async getPlantingLedger( @Param('accountSequence') accountSequence: string, @Query('page') page?: number, @Query('pageSize') pageSize?: number, ): Promise { return this.getPlantingLedgerQuery.execute( accountSequence, page ?? 1, pageSize ?? 20, ); } // ========== 团队树 API ========== @Get('accounts/:accountSequence/team') @ApiOperation({ summary: '获取账户团队信息' }) @ApiParam({ name: 'accountSequence', description: '账户序号' }) @ApiResponse({ status: 200, description: '团队信息' }) async getMyTeamInfo( @Param('accountSequence') accountSequence: string, ): Promise { return this.getTeamTreeQuery.getMyTeamInfo(accountSequence); } @Get('accounts/:accountSequence/team/direct-referrals') @ApiOperation({ summary: '获取账户直推列表(用于伞下树懒加载)' }) @ApiParam({ name: 'accountSequence', description: '账户序号' }) @ApiQuery({ name: 'limit', required: false, type: Number, description: '每页数量' }) @ApiQuery({ name: 'offset', required: false, type: Number, description: '偏移量' }) @ApiResponse({ status: 200, description: '直推列表' }) async getDirectReferrals( @Param('accountSequence') accountSequence: string, @Query('limit') limit?: number, @Query('offset') offset?: number, ): Promise { return this.getTeamTreeQuery.getDirectReferrals( accountSequence, limit ?? 100, offset ?? 0, ); } }