package service import ( "context" "fmt" "time" "github.com/topfans/backend/pkg/logger" assetPb "github.com/topfans/backend/pkg/proto/asset" pbCommon "github.com/topfans/backend/pkg/proto/common" pb "github.com/topfans/backend/pkg/proto/social" "github.com/topfans/backend/services/socialService/client" "github.com/topfans/backend/services/socialService/repository" "go.uber.org/zap" ) // AssetLikeService 资产点赞业务逻辑层 type AssetLikeService struct { assetClient *client.AssetClient socialRepo repository.SocialRepository } // NewAssetLikeService 创建资产点赞Service实例 func NewAssetLikeService(assetClient *client.AssetClient, socialRepo repository.SocialRepository) *AssetLikeService { return &AssetLikeService{ assetClient: assetClient, socialRepo: socialRepo, } } // LikeAsset 点赞资产 func (s *AssetLikeService) LikeAsset(ctx context.Context, assetID, userID, starID int64) error { logger.Logger.Debug("AssetLikeService.LikeAsset called", zap.Int64("asset_id", assetID), zap.Int64("user_id", userID), zap.Int64("star_id", starID), ) // 1. 验证资产是否存在(使用内部RPC,不验证所有权) getAssetReq := &assetPb.GetAssetForRPCRequest{ AssetId: assetID, } getAssetResp, err := s.assetClient.GetAssetForRPC(ctx, getAssetReq) if err != nil { logger.Logger.Error("Failed to get asset for RPC", zap.Error(err), zap.Int64("asset_id", assetID), ) return fmt.Errorf("failed to get asset: %w", err) } if getAssetResp.Base.Code != pbCommon.StatusCode_STATUS_OK { logger.Logger.Warn("Asset not found or error", zap.Int64("asset_id", assetID), zap.Int32("code", int32(getAssetResp.Base.Code)), zap.String("message", getAssetResp.Base.Message), ) return fmt.Errorf("asset not found: %s", getAssetResp.Base.Message) } // 2. 检查是否已点赞 checkLikeReq := &assetPb.CheckAssetLikeRequest{ AssetId: assetID, } checkLikeResp, err := s.assetClient.CheckAssetLike(ctx, checkLikeReq) if err != nil { logger.Logger.Error("Failed to check asset like", zap.Error(err), zap.Int64("asset_id", assetID), ) return fmt.Errorf("failed to check asset like: %w", err) } if checkLikeResp.IsLiked { logger.Logger.Warn("Already liked asset", zap.Int64("asset_id", assetID), zap.Int64("user_id", userID), ) return fmt.Errorf("already liked this asset") } // 3. 调用 Asset Service 点赞接口 likeReq := &assetPb.LikeAssetRequest{ AssetId: assetID, } likeResp, err := s.assetClient.LikeAsset(ctx, likeReq) if err != nil { logger.Logger.Error("Failed to like asset", zap.Error(err), zap.Int64("asset_id", assetID), ) return fmt.Errorf("failed to like asset: %w", err) } if likeResp.Base.Code != pbCommon.StatusCode_STATUS_OK { logger.Logger.Warn("Like asset failed", zap.Int64("asset_id", assetID), zap.Int32("code", int32(likeResp.Base.Code)), zap.String("message", likeResp.Base.Message), ) return fmt.Errorf("like asset failed: %s", likeResp.Base.Message) } logger.Logger.Info("Successfully liked asset", zap.Int64("asset_id", assetID), zap.Int64("user_id", userID), zap.Int64("star_id", starID), ) return nil } // UnlikeAsset 取消点赞资产 func (s *AssetLikeService) UnlikeAsset(ctx context.Context, assetID, userID, starID int64) error { logger.Logger.Debug("AssetLikeService.UnlikeAsset called", zap.Int64("asset_id", assetID), zap.Int64("user_id", userID), zap.Int64("star_id", starID), ) // 1. 检查是否已点赞 checkLikeReq := &assetPb.CheckAssetLikeRequest{ AssetId: assetID, } checkLikeResp, err := s.assetClient.CheckAssetLike(ctx, checkLikeReq) if err != nil { logger.Logger.Error("Failed to check asset like", zap.Error(err), zap.Int64("asset_id", assetID), ) return fmt.Errorf("failed to check asset like: %w", err) } if !checkLikeResp.IsLiked { logger.Logger.Warn("Not liked asset yet", zap.Int64("asset_id", assetID), zap.Int64("user_id", userID), ) return fmt.Errorf("not liked this asset yet") } // 2. 调用 Asset Service 取消点赞接口 unlikeReq := &assetPb.UnlikeAssetRequest{ AssetId: assetID, } unlikeResp, err := s.assetClient.UnlikeAsset(ctx, unlikeReq) if err != nil { logger.Logger.Error("Failed to unlike asset", zap.Error(err), zap.Int64("asset_id", assetID), ) return fmt.Errorf("failed to unlike asset: %w", err) } if unlikeResp.Base.Code != pbCommon.StatusCode_STATUS_OK { logger.Logger.Warn("Unlike asset failed", zap.Int64("asset_id", assetID), zap.Int32("code", int32(unlikeResp.Base.Code)), zap.String("message", unlikeResp.Base.Message), ) return fmt.Errorf("unlike asset failed: %s", unlikeResp.Base.Message) } logger.Logger.Info("Successfully unliked asset", zap.Int64("asset_id", assetID), zap.Int64("user_id", userID), zap.Int64("star_id", starID), ) return nil } // CheckAssetLike 检查是否已点赞资产 func (s *AssetLikeService) CheckAssetLike(ctx context.Context, assetID, userID, starID int64) (bool, error) { logger.Logger.Debug("AssetLikeService.CheckAssetLike called", zap.Int64("asset_id", assetID), zap.Int64("user_id", userID), zap.Int64("star_id", starID), ) checkLikeReq := &assetPb.CheckAssetLikeRequest{ AssetId: assetID, } checkLikeResp, err := s.assetClient.CheckAssetLike(ctx, checkLikeReq) if err != nil { logger.Logger.Error("Failed to check asset like", zap.Error(err), zap.Int64("asset_id", assetID), ) return false, fmt.Errorf("failed to check asset like: %w", err) } if checkLikeResp.Base.Code != pbCommon.StatusCode_STATUS_OK { logger.Logger.Warn("Check asset like failed", zap.Int64("asset_id", assetID), zap.Int32("code", int32(checkLikeResp.Base.Code)), zap.String("message", checkLikeResp.Base.Message), ) return false, fmt.Errorf("check asset like failed: %s", checkLikeResp.Base.Message) } return checkLikeResp.IsLiked, nil } // GetAssetLikes 获取资产点赞列表 func (s *AssetLikeService) GetAssetLikes(ctx context.Context, assetID int64, page, pageSize int32) (*assetPb.GetAssetLikesResponse, error) { logger.Logger.Debug("AssetLikeService.GetAssetLikes called", zap.Int64("asset_id", assetID), zap.Int32("page", page), zap.Int32("page_size", pageSize), ) getLikesReq := &assetPb.GetAssetLikesRequest{ AssetId: assetID, Page: page, PageSize: pageSize, } getLikesResp, err := s.assetClient.GetAssetLikes(ctx, getLikesReq) if err != nil { logger.Logger.Error("Failed to get asset likes", zap.Error(err), zap.Int64("asset_id", assetID), ) return nil, fmt.Errorf("failed to get asset likes: %w", err) } if getLikesResp.Base.Code != pbCommon.StatusCode_STATUS_OK { logger.Logger.Warn("Get asset likes failed", zap.Int64("asset_id", assetID), zap.Int32("code", int32(getLikesResp.Base.Code)), zap.String("message", getLikesResp.Base.Message), ) return nil, fmt.Errorf("get asset likes failed: %s", getLikesResp.Base.Message) } return getLikesResp, nil } // GetMyLikedAssets 获取我点赞的作品列表 func (s *AssetLikeService) GetMyLikedAssets(ctx context.Context, req *pb.GetMyLikedAssetsRequest, userID, starID int64) (*pb.GetMyLikedAssetsResponse, error) { page := req.Page if page <= 0 { page = 1 } pageSize := req.PageSize if pageSize <= 0 { pageSize = 20 } if pageSize > 100 { pageSize = 100 } items, total, err := s.socialRepo.GetMyLikedAssets(userID, starID, int(page), int(pageSize)) if err != nil { logger.Logger.Error("Failed to get my liked assets", zap.Error(err), zap.Int64("user_id", userID), zap.Int64("star_id", starID), ) return &pb.GetMyLikedAssetsResponse{ Base: &pbCommon.BaseResponse{ Code: pbCommon.StatusCode_STATUS_INTERNAL_ERROR, Message: "Failed to get my liked assets: " + err.Error(), Timestamp: time.Now().UnixMilli(), }, }, nil } // 转换为 proto 类型 pbItems := make([]*pb.LikedAssetItem, 0, len(items)) for _, item := range items { pbItems = append(pbItems, &pb.LikedAssetItem{ AssetId: item.AssetID, Name: item.Name, CoverUrl: item.CoverURL, LikeCount: item.LikeCount, LikedAt: item.LikedAt, Earnings: item.Earnings, }) } hasMore := int64(page)*int64(pageSize) < total return &pb.GetMyLikedAssetsResponse{ Base: &pbCommon.BaseResponse{ Code: pbCommon.StatusCode_STATUS_OK, Message: "success", Timestamp: time.Now().UnixMilli(), }, Data: &pb.LikedAssetsData{ Items: pbItems, Page: page, PageSize: pageSize, Total: total, HasMore: hasMore, }, }, nil } // GetMyTodayLikedAssets 获取我今日点赞的作品列表 func (s *AssetLikeService) GetMyTodayLikedAssets(ctx context.Context, req *pb.GetMyTodayLikedAssetsRequest, userID, starID int64) (*pb.GetMyTodayLikedAssetsResponse, error) { page := req.Page if page <= 0 { page = 1 } pageSize := req.PageSize if pageSize <= 0 { pageSize = 20 } if pageSize > 100 { pageSize = 100 } items, total, err := s.socialRepo.GetMyTodayLikedAssets(userID, starID, int(page), int(pageSize)) if err != nil { logger.Logger.Error("Failed to get my today liked assets", zap.Error(err), zap.Int64("user_id", userID), zap.Int64("star_id", starID), ) return &pb.GetMyTodayLikedAssetsResponse{ Base: &pbCommon.BaseResponse{ Code: pbCommon.StatusCode_STATUS_INTERNAL_ERROR, Message: "Failed to get my today liked assets: " + err.Error(), Timestamp: time.Now().UnixMilli(), }, }, nil } pbItems := make([]*pb.LikedAssetItem, 0, len(items)) for _, item := range items { pbItems = append(pbItems, &pb.LikedAssetItem{ AssetId: item.AssetID, Name: item.Name, CoverUrl: item.CoverURL, LikeCount: item.LikeCount, LikedAt: item.LikedAt, Earnings: item.Earnings, }) } hasMore := int64(page)*int64(pageSize) < total return &pb.GetMyTodayLikedAssetsResponse{ Base: &pbCommon.BaseResponse{ Code: pbCommon.StatusCode_STATUS_OK, Message: "success", Timestamp: time.Now().UnixMilli(), }, Data: &pb.LikedAssetsData{ Items: pbItems, Page: page, PageSize: pageSize, Total: total, HasMore: hasMore, }, }, nil } // GetMyWeekLikedAssets 获取我本周点赞的作品列表 func (s *AssetLikeService) GetMyWeekLikedAssets(ctx context.Context, req *pb.GetMyWeekLikedAssetsRequest, userID, starID int64) (*pb.GetMyWeekLikedAssetsResponse, error) { page := req.Page if page <= 0 { page = 1 } pageSize := req.PageSize if pageSize <= 0 { pageSize = 20 } if pageSize > 100 { pageSize = 100 } items, total, err := s.socialRepo.GetMyWeekLikedAssets(userID, starID, int(page), int(pageSize)) if err != nil { logger.Logger.Error("Failed to get my week liked assets", zap.Error(err), zap.Int64("user_id", userID), zap.Int64("star_id", starID), ) return &pb.GetMyWeekLikedAssetsResponse{ Base: &pbCommon.BaseResponse{ Code: pbCommon.StatusCode_STATUS_INTERNAL_ERROR, Message: "Failed to get my week liked assets: " + err.Error(), Timestamp: time.Now().UnixMilli(), }, }, nil } pbItems := make([]*pb.LikedAssetItem, 0, len(items)) for _, item := range items { pbItems = append(pbItems, &pb.LikedAssetItem{ AssetId: item.AssetID, Name: item.Name, CoverUrl: item.CoverURL, LikeCount: item.LikeCount, LikedAt: item.LikedAt, Earnings: item.Earnings, }) } hasMore := int64(page)*int64(pageSize) < total return &pb.GetMyWeekLikedAssetsResponse{ Base: &pbCommon.BaseResponse{ Code: pbCommon.StatusCode_STATUS_OK, Message: "success", Timestamp: time.Now().UnixMilli(), }, Data: &pb.LikedAssetsData{ Items: pbItems, Page: page, PageSize: pageSize, Total: total, HasMore: hasMore, }, }, nil } // GetUserLikedAssets 获取他人点赞的作品列表 func (s *AssetLikeService) GetUserLikedAssets(ctx context.Context, req *pb.GetUserLikedAssetsRequest, userID, starID int64) (*pb.GetUserLikedAssetsResponse, error) { page := req.Page if page <= 0 { page = 1 } pageSize := req.PageSize if pageSize <= 0 { pageSize = 20 } if pageSize > 100 { pageSize = 100 } items, total, err := s.socialRepo.GetUserLikedAssets(userID, starID, int(page), int(pageSize)) if err != nil { logger.Logger.Error("Failed to get user liked assets", zap.Error(err), zap.Int64("user_id", userID), zap.Int64("star_id", starID), ) return &pb.GetUserLikedAssetsResponse{ Base: &pbCommon.BaseResponse{ Code: pbCommon.StatusCode_STATUS_INTERNAL_ERROR, Message: "Failed to get user liked assets: " + err.Error(), Timestamp: time.Now().UnixMilli(), }, }, nil } pbItems := make([]*pb.LikedAssetItem, 0, len(items)) for _, item := range items { pbItems = append(pbItems, &pb.LikedAssetItem{ AssetId: item.AssetID, Name: item.Name, CoverUrl: item.CoverURL, LikeCount: item.LikeCount, LikedAt: item.LikedAt, Earnings: item.Earnings, }) } hasMore := int64(page)*int64(pageSize) < total return &pb.GetUserLikedAssetsResponse{ Base: &pbCommon.BaseResponse{ Code: pbCommon.StatusCode_STATUS_OK, Message: "success", Timestamp: time.Now().UnixMilli(), }, Data: &pb.LikedAssetsData{ Items: pbItems, Page: page, PageSize: pageSize, Total: total, HasMore: hasMore, }, }, nil }