import { Controller, Get, Logger, Param, Query } from '@nestjs/common'; import { ApiOperation, ApiResponse, ApiTags, ApiParam, ApiQuery, } from '@nestjs/swagger'; import { RecommendationService } from './recommendation.service'; import { GetSimilarVideosResponseDto, VideoRecommendationDto, } from './dto/video-recommendation.dto'; import { AdRecommendationDto, GetSimilarAdsResponseDto, } from './dto/ad-recommendation.dto'; @ApiTags('推荐系统') @Controller('api/v1/recommendation') export class RecommendationController { private readonly logger = new Logger(RecommendationController.name); constructor(private readonly recommendationService: RecommendationService) {} @Get('videos/:videoId/similar') @ApiOperation({ summary: '获取相似视频推荐', description: '基于视频标签和Redis分数推荐相似视频。优先从标签相关视频中推荐,不足时从全局热门视频补充。', }) @ApiParam({ name: 'videoId', description: '当前视频ID', example: '6756abc123def', }) @ApiQuery({ name: 'limit', required: false, description: '返回推荐数量(默认10)', example: 10, }) @ApiResponse({ status: 200, description: '成功返回推荐列表', type: GetSimilarVideosResponseDto, }) async getSimilarVideos( @Param('videoId') videoId: string, @Query('limit') limit?: string, ): Promise { const limitNum = limit ? parseInt(limit, 10) : 10; this.logger.debug( `GET /api/v1/recommendation/videos/${videoId}/similar?limit=${limitNum}`, ); const recommendations = await this.recommendationService.getSimilarVideos( videoId, limitNum, ); return { currentVideoId: videoId, recommendations, count: recommendations.length, }; } @Get('ads/:adId/similar') @ApiOperation({ summary: '获取相似广告推荐(带过滤条件)', description: '基于渠道、广告模块和时间有效性过滤,返回相似广告推荐。仅返回同渠道、同模块、状态启用且在有效期内的广告。', }) @ApiParam({ name: 'adId', description: '当前广告ID', example: '6756def456ghi', }) // @ApiQuery({ // name: 'channelId', // required: true, // description: '渠道ID(必须匹配)', // example: '6756channel123', // }) @ApiQuery({ name: 'adsModuleId', required: true, description: '广告模块ID(场景/槽位)', example: '6756module456', }) @ApiQuery({ name: 'limit', required: false, description: '返回推荐数量(默认5)', example: 5, }) @ApiResponse({ status: 200, description: '成功返回推荐列表', type: GetSimilarAdsResponseDto, }) async getSimilarAds( @Param('adId') adId: string, @Query('adsModuleId') adsModuleId: string, @Query('limit') limit?: string, ): Promise { const limitNum = limit ? parseInt(limit, 10) : 5; this.logger.debug( `GET /api/v1/recommendation/ads/${adId}/similar?adsModuleId=${adsModuleId}&limit=${limitNum}`, ); const recommendations = await this.recommendationService.getSimilarAds( adId, { adsModuleId, limit: limitNum, }, ); return { currentAdId: adId, recommendations, count: recommendations.length, context: { adsModuleId, limit: limitNum, }, }; } @Get('ads/:adId/similar-simple') @ApiOperation({ summary: '获取相似广告推荐(简单版)', description: '基于Redis全局分数推荐相似广告,不进行渠道和模块过滤。仅用于测试或特殊场景。', }) @ApiParam({ name: 'adId', description: '当前广告ID', example: '6756def456ghi', }) @ApiQuery({ name: 'limit', required: false, description: '返回推荐数量(默认10)', example: 5, }) @ApiResponse({ status: 200, description: '成功返回推荐列表', type: [VideoRecommendationDto], }) async getSimilarAdsSimple( @Param('adId') adId: string, @Query('limit') limit?: string, ): Promise { const limitNum = limit ? parseInt(limit, 10) : 10; this.logger.debug( `GET /api/v1/recommendation/ads/${adId}/similar-simple?limit=${limitNum}`, ); return this.recommendationService.getSimilarAdsSimple(adId, limitNum); } }