topfans/backend/gateway/controller/social_controller.go
2026-04-07 22:29:48 +08:00

848 lines
24 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

package controller
import (
"context"
"net/http"
"strconv"
"time"
"dubbo.apache.org/dubbo-go/v3/client"
"dubbo.apache.org/dubbo-go/v3/common/constant"
"github.com/gin-gonic/gin"
"github.com/topfans/backend/gateway/dto"
"github.com/topfans/backend/gateway/pkg/response"
pbCommon "github.com/topfans/backend/pkg/proto/common"
pbSocial "github.com/topfans/backend/pkg/proto/social"
"go.uber.org/zap"
"github.com/topfans/backend/pkg/logger"
)
// SocialController 社交相关控制器
type SocialController struct {
socialService pbSocial.SocialService
}
// NewSocialController 创建社交控制器
func NewSocialController(dubboClient *client.Client) (*SocialController, error) {
// 创建 SocialService 客户端
socialService, err := pbSocial.NewSocialService(dubboClient)
if err != nil {
return nil, err
}
return &SocialController{
socialService: socialService,
}, nil
}
// SendFriendRequest 发送好友请求
// @Summary 发送好友请求
// @Description 向指定用户发送好友请求
// @Tags social
// @Accept json
// @Produce json
// @Security BearerAuth
// @Param request body object{friend_user_id=integer,message=string} true "好友请求参数"
// @Success 200 {object} response.Response
// @Router /api/v1/social/friend-requests [post]
func (ctrl *SocialController) SendFriendRequest(c *gin.Context) {
// 从上下文获取用户信息
userID, exists := c.Get("user_id")
if !exists {
response.Error(c, http.StatusUnauthorized, "未授权")
return
}
starID, exists := c.Get("star_id")
if !exists {
response.Error(c, http.StatusUnauthorized, "未授权")
return
}
// 解析请求参数
var req struct {
FriendUserID int64 `json:"friend_user_id" binding:"required"`
Message string `json:"message"`
}
if err := c.ShouldBindJSON(&req); err != nil {
response.Error(c, http.StatusBadRequest, "参数错误: "+err.Error())
return
}
// 设置上下文和 Dubbo attachments
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()
ctx = context.WithValue(ctx, constant.AttachmentKey, map[string]interface{}{
"user_id": strconv.FormatInt(userID.(int64), 10),
"star_id": strconv.FormatInt(starID.(int64), 10),
})
// 调用 RPC
resp, err := ctrl.socialService.SendFriendRequest(ctx, &pbSocial.SendFriendRequestRequest{
FriendUserId: req.FriendUserID,
Message: req.Message,
})
if err != nil {
logger.Logger.Error("SendFriendRequest RPC failed",
zap.Int64("user_id", userID.(int64)),
zap.Int64("friend_user_id", req.FriendUserID),
zap.Error(err),
)
response.Error(c, http.StatusInternalServerError, "服务调用失败")
return
}
// 处理响应
if resp.Base.Code != pbCommon.StatusCode_STATUS_OK {
response.ErrorWithCode(c, int(resp.Base.Code), resp.Base.Message)
return
}
response.Success(c, gin.H{
"request_id": resp.RequestId,
"status": resp.Status,
"created_at": resp.CreatedAt,
"expires_at": resp.ExpiresAt,
})
}
// GetFriendRequests 获取好友请求列表
// @Summary 获取好友请求列表
// @Description 获取当前用户的好友请求列表
// @Tags social
// @Accept json
// @Produce json
// @Security BearerAuth
// @Param type query string false "请求类型: received/sent"
// @Param status query string false "请求状态"
// @Param page query int false "页码默认1"
// @Param page_size query int false "每页数量默认20"
// @Success 200 {object} response.Response
// @Router /api/v1/social/friend-requests [get]
func (ctrl *SocialController) GetFriendRequests(c *gin.Context) {
userID, _ := c.Get("user_id")
starID, _ := c.Get("star_id")
// 解析查询参数
requestType := c.DefaultQuery("type", "received") // received/sent
status := c.Query("status")
page, _ := strconv.Atoi(c.DefaultQuery("page", "1"))
pageSize, _ := strconv.Atoi(c.DefaultQuery("page_size", "20"))
// 设置上下文
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()
ctx = context.WithValue(ctx, constant.AttachmentKey, map[string]interface{}{
"user_id": strconv.FormatInt(userID.(int64), 10),
"star_id": strconv.FormatInt(starID.(int64), 10),
})
// 调用 RPC
resp, err := ctrl.socialService.GetFriendRequests(ctx, &pbSocial.GetFriendRequestsRequest{
Type: requestType,
Status: status,
Page: int32(page),
PageSize: int32(pageSize),
})
if err != nil {
logger.Logger.Error("GetFriendRequests RPC failed", zap.Error(err))
response.Error(c, http.StatusInternalServerError, "服务调用失败")
return
}
if resp.Base.Code != pbCommon.StatusCode_STATUS_OK {
response.ErrorWithCode(c, int(resp.Base.Code), resp.Base.Message)
return
}
// 转换响应
items := make([]map[string]interface{}, 0, len(resp.Items))
for _, item := range resp.Items {
items = append(items, dto.ConvertFriendRequest(item))
}
// 计算 has_more
hasMore := int64(resp.Page)*int64(resp.PageSize) < resp.Total
response.Success(c, gin.H{
"items": items,
"total": resp.Total,
"page": resp.Page,
"page_size": resp.PageSize,
"has_more": hasMore,
})
}
// HandleFriendRequest 处理好友请求(接受/拒绝)
// @Summary 处理好友请求
// @Description 接受或拒绝好友请求
// @Tags social
// @Accept json
// @Produce json
// @Security BearerAuth
// @Param request body object{request_id=integer,action=string} true "处理请求参数: action=accept/reject"
// @Success 200 {object} response.Response
// @Router /api/v1/social/friend-requests/handle [post]
func (ctrl *SocialController) HandleFriendRequest(c *gin.Context) {
userID, _ := c.Get("user_id")
starID, _ := c.Get("star_id")
// 解析请求参数
var req struct {
RequestID int64 `json:"request_id" binding:"required"`
Action string `json:"action" binding:"required,oneof=accept reject"` // accept/reject
}
if err := c.ShouldBindJSON(&req); err != nil {
response.Error(c, http.StatusBadRequest, "参数错误: "+err.Error())
return
}
// 设置上下文
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()
ctx = context.WithValue(ctx, constant.AttachmentKey, map[string]interface{}{
"user_id": strconv.FormatInt(userID.(int64), 10),
"star_id": strconv.FormatInt(starID.(int64), 10),
})
// 调用 RPC
resp, err := ctrl.socialService.HandleFriendRequest(ctx, &pbSocial.HandleFriendRequestRequest{
RequestId: req.RequestID,
Action: req.Action,
})
if err != nil {
logger.Logger.Error("HandleFriendRequest RPC failed", zap.Error(err))
response.Error(c, http.StatusInternalServerError, "服务调用失败")
return
}
if resp.Base.Code != pbCommon.StatusCode_STATUS_OK {
response.ErrorWithCode(c, int(resp.Base.Code), resp.Base.Message)
return
}
response.Success(c, gin.H{
"action": resp.Action,
"friendship_created": resp.FriendshipCreated,
"processed_at": resp.ProcessedAt,
})
}
// GetFriendList 获取好友列表
// @Summary 获取好友列表
// @Description 获取当前用户的好友列表
// @Tags social
// @Accept json
// @Produce json
// @Security BearerAuth
// @Param keyword query string false "搜索关键词"
// @Param page query int false "页码默认1"
// @Param page_size query int false "每页数量默认20"
// @Success 200 {object} response.Response
// @Router /api/v1/social/friends [get]
func (ctrl *SocialController) GetFriendList(c *gin.Context) {
userID, _ := c.Get("user_id")
starID, _ := c.Get("star_id")
// 解析查询参数
keyword := c.Query("keyword")
page, _ := strconv.Atoi(c.DefaultQuery("page", "1"))
pageSize, _ := strconv.Atoi(c.DefaultQuery("page_size", "20"))
// 设置上下文
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()
ctx = context.WithValue(ctx, constant.AttachmentKey, map[string]interface{}{
"user_id": strconv.FormatInt(userID.(int64), 10),
"star_id": strconv.FormatInt(starID.(int64), 10),
})
// 调用 RPC
resp, err := ctrl.socialService.GetFriendList(ctx, &pbSocial.GetFriendListRequest{
Keyword: keyword,
Page: int32(page),
PageSize: int32(pageSize),
})
if err != nil {
logger.Logger.Error("GetFriendList RPC failed", zap.Error(err))
response.Error(c, http.StatusInternalServerError, "服务调用失败")
return
}
if resp.Base.Code != pbCommon.StatusCode_STATUS_OK {
response.ErrorWithCode(c, int(resp.Base.Code), resp.Base.Message)
return
}
// 转换响应
items := make([]map[string]interface{}, 0, len(resp.Items))
for _, item := range resp.Items {
items = append(items, dto.ConvertFriendship(item))
}
// 计算 has_more
hasMore := int64(resp.Page)*int64(resp.PageSize) < resp.Total
response.Success(c, gin.H{
"items": items,
"total": resp.Total,
"page": resp.Page,
"page_size": resp.PageSize,
"has_more": hasMore,
})
}
// DeleteFriend 删除好友
// @Summary 删除好友
// @Description 删除指定好友
// @Tags social
// @Accept json
// @Produce json
// @Security BearerAuth
// @Param request body object{friend_user_id=integer} true "好友用户ID"
// @Success 200 {object} response.Response
// @Router /api/v1/social/friends [delete]
func (ctrl *SocialController) DeleteFriend(c *gin.Context) {
userID, _ := c.Get("user_id")
starID, _ := c.Get("star_id")
// 解析请求参数
var req struct {
FriendUserID int64 `json:"friend_user_id" binding:"required"`
}
if err := c.ShouldBindJSON(&req); err != nil {
response.Error(c, http.StatusBadRequest, "参数错误: "+err.Error())
return
}
// 设置上下文
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()
ctx = context.WithValue(ctx, constant.AttachmentKey, map[string]interface{}{
"user_id": strconv.FormatInt(userID.(int64), 10),
"star_id": strconv.FormatInt(starID.(int64), 10),
})
// 调用 RPC
resp, err := ctrl.socialService.DeleteFriend(ctx, &pbSocial.DeleteFriendRequest{
FriendUserId: req.FriendUserID,
})
if err != nil {
logger.Logger.Error("DeleteFriend RPC failed", zap.Error(err))
response.Error(c, http.StatusInternalServerError, "服务调用失败")
return
}
if resp.Base.Code != pbCommon.StatusCode_STATUS_OK {
response.ErrorWithCode(c, int(resp.Base.Code), resp.Base.Message)
return
}
response.Success(c, gin.H{
"success": resp.Success,
})
}
// SetFriendRemark 设置好友备注
// @Summary 设置好友备注
// @Description 设置指定好友的备注名称
// @Tags social
// @Accept json
// @Produce json
// @Security BearerAuth
// @Param request body object{friend_user_id=integer,remark=string} true "好友ID和备注"
// @Success 200 {object} response.Response
// @Router /api/v1/social/friends/remark [put]
func (ctrl *SocialController) SetFriendRemark(c *gin.Context) {
userID, _ := c.Get("user_id")
starID, _ := c.Get("star_id")
// 解析请求参数
var req struct {
FriendUserID int64 `json:"friend_user_id" binding:"required"`
Remark string `json:"remark" binding:"max=50"`
}
if err := c.ShouldBindJSON(&req); err != nil {
response.Error(c, http.StatusBadRequest, "参数错误: "+err.Error())
return
}
// 设置上下文
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()
ctx = context.WithValue(ctx, constant.AttachmentKey, map[string]interface{}{
"user_id": strconv.FormatInt(userID.(int64), 10),
"star_id": strconv.FormatInt(starID.(int64), 10),
})
// 调用 RPC
resp, err := ctrl.socialService.SetFriendRemark(ctx, &pbSocial.SetFriendRemarkRequest{
FriendUserId: req.FriendUserID,
Remark: req.Remark,
})
if err != nil {
logger.Logger.Error("SetFriendRemark RPC failed", zap.Error(err))
response.Error(c, http.StatusInternalServerError, "服务调用失败")
return
}
if resp.Base.Code != pbCommon.StatusCode_STATUS_OK {
response.ErrorWithCode(c, int(resp.Base.Code), resp.Base.Message)
return
}
response.Success(c, gin.H{
"remark": resp.Remark,
})
}
// CheckFriendship 检查好友关系
// @Summary 检查好友关系
// @Description 检查当前用户与指定用户是否为好友关系
// @Tags social
// @Accept json
// @Produce json
// @Security BearerAuth
// @Param friend_user_id query int true "好友用户ID"
// @Success 200 {object} response.Response
// @Router /api/v1/social/friendship/check [get]
func (ctrl *SocialController) CheckFriendship(c *gin.Context) {
userID, _ := c.Get("user_id")
starID, _ := c.Get("star_id")
// 解析查询参数
friendUserID, err := strconv.ParseInt(c.Query("friend_user_id"), 10, 64)
if err != nil {
response.Error(c, http.StatusBadRequest, "参数错误: friend_user_id 必须为数字")
return
}
// 设置上下文
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()
ctx = context.WithValue(ctx, constant.AttachmentKey, map[string]interface{}{
"user_id": strconv.FormatInt(userID.(int64), 10),
"star_id": strconv.FormatInt(starID.(int64), 10),
})
// 调用 RPC
resp, err := ctrl.socialService.CheckFriendship(ctx, &pbSocial.CheckFriendshipRequest{
UserId: userID.(int64),
FriendUserId: friendUserID,
})
if err != nil {
logger.Logger.Error("CheckFriendship RPC failed", zap.Error(err))
response.Error(c, http.StatusInternalServerError, "服务调用失败")
return
}
if resp.Base.Code != pbCommon.StatusCode_STATUS_OK {
response.ErrorWithCode(c, int(resp.Base.Code), resp.Base.Message)
return
}
response.Success(c, gin.H{
"is_friend": resp.IsFriend,
})
}
// GetFriendCount 获取好友数量
// @Summary 获取好友数量
// @Description 获取当前用户的好友数量
// @Tags social
// @Accept json
// @Produce json
// @Security BearerAuth
// @Success 200 {object} response.Response
// @Router /api/v1/social/friends/count [get]
func (ctrl *SocialController) GetFriendCount(c *gin.Context) {
userID, _ := c.Get("user_id")
starID, _ := c.Get("star_id")
// 设置上下文
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()
ctx = context.WithValue(ctx, constant.AttachmentKey, map[string]interface{}{
"user_id": strconv.FormatInt(userID.(int64), 10),
"star_id": strconv.FormatInt(starID.(int64), 10),
})
// 调用 RPC
resp, err := ctrl.socialService.GetFriendCount(ctx, &pbSocial.GetFriendCountRequest{})
if err != nil {
logger.Logger.Error("GetFriendCount RPC failed", zap.Error(err))
response.Error(c, http.StatusInternalServerError, "服务调用失败")
return
}
if resp.Base.Code != pbCommon.StatusCode_STATUS_OK {
response.ErrorWithCode(c, int(resp.Base.Code), resp.Base.Message)
return
}
response.Success(c, gin.H{
"count": resp.Count,
})
}
// SearchUserForFriend 查找用户信息(用于加好友)
// @Summary 查找用户
// @Description 根据用户ID查找用户信息用于添加好友
// @Tags social
// @Accept json
// @Produce json
// @Security BearerAuth
// @Param friend_fan_profile_id query int true "粉丝档案ID"
// @Success 200 {object} response.Response
// @Router /api/v1/social/search-user [get]
func (ctrl *SocialController) SearchUserForFriend(c *gin.Context) {
userID, _ := c.Get("user_id")
starID, _ := c.Get("star_id")
// 获取查询参数(前端传 friend_fan_profile_id
friendFanProfileIDStr := c.Query("friend_fan_profile_id")
if friendFanProfileIDStr == "" {
// 兼容旧参数名 friend_user_id
friendFanProfileIDStr = c.Query("friend_user_id")
}
if friendFanProfileIDStr == "" {
response.Error(c, http.StatusBadRequest, "参数 friend_fan_profile_id 不能为空")
return
}
friendFanProfileID, err := strconv.ParseInt(friendFanProfileIDStr, 10, 64)
if err != nil {
response.Error(c, http.StatusBadRequest, "参数 friend_fan_profile_id 格式错误")
return
}
// 设置上下文
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()
ctx = context.WithValue(ctx, constant.AttachmentKey, map[string]interface{}{
"user_id": strconv.FormatInt(userID.(int64), 10),
"star_id": strconv.FormatInt(starID.(int64), 10),
})
// 调用 RPC
resp, err := ctrl.socialService.SearchUserForFriend(ctx, &pbSocial.SearchUserForFriendRequest{
FriendFanProfileId: friendFanProfileID,
})
if err != nil {
logger.Logger.Error("SearchUserForFriend RPC failed",
zap.Int64("user_id", userID.(int64)),
zap.Int64("friend_fan_profile_id", friendFanProfileID),
zap.Error(err),
)
response.Error(c, http.StatusInternalServerError, "服务调用失败")
return
}
if resp.Base.Code != pbCommon.StatusCode_STATUS_OK {
response.ErrorWithCode(c, int(resp.Base.Code), resp.Base.Message)
return
}
// 构建响应
response.Success(c, dto.ToUserSearchResultDTO(resp.User))
}
// GetUsersPaged 获取分页用户列表
// @Summary 获取分页用户列表
// @Description 获取同一明星身份下的分页用户列表包含用户ID、展馆所有者ID、昵称、等级
// @Tags social
// @Accept json
// @Produce json
// @Security BearerAuth
// @Param page query int false "页码默认1"
// @Param page_size query int false "每页数量默认20最大100"
// @Success 200 {object} response.Response
// @Router /api/v1/social/users [get]
func (ctrl *SocialController) GetUsersPaged(c *gin.Context) {
userID, _ := c.Get("user_id")
starID, _ := c.Get("star_id")
page, _ := strconv.Atoi(c.DefaultQuery("page", "1"))
pageSize, _ := strconv.Atoi(c.DefaultQuery("page_size", "20"))
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()
ctx = context.WithValue(ctx, constant.AttachmentKey, map[string]interface{}{
"user_id": strconv.FormatInt(userID.(int64), 10),
"star_id": strconv.FormatInt(starID.(int64), 10),
})
resp, err := ctrl.socialService.GetUsersPaged(ctx, &pbSocial.GetUsersPagedRequest{
Page: int32(page),
PageSize: int32(pageSize),
})
if err != nil {
logger.Logger.Error("GetUsersPaged RPC failed", zap.Error(err))
response.Error(c, http.StatusInternalServerError, "服务调用失败")
return
}
if resp.Base.Code != pbCommon.StatusCode_STATUS_OK {
response.ErrorWithCode(c, int(resp.Base.Code), resp.Base.Message)
return
}
users := dto.ToPagedUsersDTO(resp.Users)
hasMore := int64(resp.Page)*int64(resp.PageSize) < resp.Total
response.Success(c, gin.H{
"users": users,
"total": resp.Total,
"page": resp.Page,
"page_size": resp.PageSize,
"has_more": hasMore,
})
}
// GetRandomUsers 获取随机用户(同一明星下)
// @Summary 获取随机用户
// @Description 获取同一明星身份下的随机推荐用户
// @Tags social
// @Accept json
// @Produce json
// @Security BearerAuth
// @Param count query int false "获取数量默认1最大100"
// @Success 200 {object} response.Response
// @Router /api/v1/social/random-users [get]
func (ctrl *SocialController) GetRandomUsers(c *gin.Context) {
// 1. 获取用户信息(从 Token 中)
userID, exists := c.Get("user_id")
if !exists {
response.Error(c, http.StatusUnauthorized, "未授权")
return
}
starID, exists := c.Get("star_id")
if !exists {
response.Error(c, http.StatusUnauthorized, "未授权")
return
}
// 2. 解析查询参数
countStr := c.DefaultQuery("count", "1")
count, err := strconv.ParseInt(countStr, 10, 32)
if err != nil || count <= 0 {
count = 1 // 默认返回1个
}
if count > 100 {
count = 100 // 最大限制100个
}
// 3. 设置上下文
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()
ctx = context.WithValue(ctx, constant.AttachmentKey, map[string]interface{}{
"user_id": strconv.FormatInt(userID.(int64), 10),
"star_id": strconv.FormatInt(starID.(int64), 10),
})
// 4. 调用 RPC
resp, err := ctrl.socialService.GetRandomUsers(ctx, &pbSocial.GetRandomUsersRequest{
Count: int32(count),
})
if err != nil {
logger.Logger.Error("GetRandomUsers RPC failed",
zap.Int64("user_id", userID.(int64)),
zap.Int64("star_id", starID.(int64)),
zap.Error(err),
)
response.Error(c, http.StatusInternalServerError, "服务调用失败")
return
}
if resp.Base.Code != pbCommon.StatusCode_STATUS_OK {
response.ErrorWithCode(c, int(resp.Base.Code), resp.Base.Message)
return
}
// 5. 构建响应
response.Success(c, dto.ToRandomUsersDTO(resp.Users))
}
// LikeAsset 点赞资产
// @Summary 点赞资产
// @Description 对指定资产进行点赞
// @Tags social
// @Accept json
// @Produce json
// @Security BearerAuth
// @Param asset_id path int true "资产ID"
// @Success 200 {object} response.Response
// @Router /api/v1/social/assets/{asset_id}/like [post]
func (ctrl *SocialController) LikeAsset(c *gin.Context) {
// 1. 从上下文获取用户信息
userID, exists := c.Get("user_id")
if !exists {
response.Error(c, http.StatusUnauthorized, "未授权")
return
}
starID, exists := c.Get("star_id")
if !exists {
response.Error(c, http.StatusUnauthorized, "未授权")
return
}
// 2. 解析资产ID
assetIDStr := c.Param("asset_id")
assetID, err := strconv.ParseInt(assetIDStr, 10, 64)
if err != nil {
response.Error(c, http.StatusBadRequest, "参数错误: asset_id 必须为数字")
return
}
// 3. 设置上下文
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()
ctx = context.WithValue(ctx, constant.AttachmentKey, map[string]interface{}{
"user_id": strconv.FormatInt(userID.(int64), 10),
"star_id": strconv.FormatInt(starID.(int64), 10),
})
// 4. 调用 RPC
resp, err := ctrl.socialService.LikeAsset(ctx, &pbSocial.LikeAssetRequest{
AssetId: assetID,
})
if err != nil {
logger.Logger.Error("LikeAsset RPC failed",
zap.Int64("user_id", userID.(int64)),
zap.Int64("star_id", starID.(int64)),
zap.Int64("asset_id", assetID),
zap.Error(err),
)
// 优先使用响应中的错误信息
if resp != nil && resp.Base != nil && resp.Base.Message != "" {
response.ErrorWithCode(c, int(resp.Base.Code), resp.Base.Message)
return
}
// 解析 RPC 错误字符串
code, msg := parseRPCError(err)
response.ErrorWithCode(c, code, msg)
return
}
if resp.Base.Code != pbCommon.StatusCode_STATUS_OK {
response.ErrorWithCode(c, int(resp.Base.Code), resp.Base.Message)
return
}
// 5. 构建响应
response.Success(c, gin.H{
"asset_id": resp.AssetId,
"new_like_count": resp.NewLikeCount,
})
}
// UnlikeAsset 取消点赞资产
// @Summary 取消点赞
// @Description 取消对指定资产的点赞
// @Tags social
// @Accept json
// @Produce json
// @Security BearerAuth
// @Param asset_id path int true "资产ID"
// @Success 200 {object} response.Response
// @Router /api/v1/social/assets/{asset_id}/like [delete]
func (ctrl *SocialController) UnlikeAsset(c *gin.Context) {
// 1. 从上下文获取用户信息
userID, exists := c.Get("user_id")
if !exists {
response.Error(c, http.StatusUnauthorized, "未授权")
return
}
starID, exists := c.Get("star_id")
if !exists {
response.Error(c, http.StatusUnauthorized, "未授权")
return
}
// 2. 解析资产ID
assetIDStr := c.Param("asset_id")
assetID, err := strconv.ParseInt(assetIDStr, 10, 64)
if err != nil {
response.Error(c, http.StatusBadRequest, "参数错误: asset_id 必须为数字")
return
}
// 3. 设置上下文
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()
ctx = context.WithValue(ctx, constant.AttachmentKey, map[string]interface{}{
"user_id": strconv.FormatInt(userID.(int64), 10),
"star_id": strconv.FormatInt(starID.(int64), 10),
})
// 4. 调用 RPC
resp, err := ctrl.socialService.UnlikeAsset(ctx, &pbSocial.UnlikeAssetRequest{
AssetId: assetID,
})
if err != nil {
logger.Logger.Error("UnlikeAsset RPC failed",
zap.Int64("user_id", userID.(int64)),
zap.Int64("star_id", starID.(int64)),
zap.Int64("asset_id", assetID),
zap.Error(err),
)
// 优先使用响应中的错误信息
if resp != nil && resp.Base != nil && resp.Base.Message != "" {
response.ErrorWithCode(c, int(resp.Base.Code), resp.Base.Message)
return
}
// 解析 RPC 错误字符串
code, msg := parseRPCError(err)
response.ErrorWithCode(c, code, msg)
return
}
if resp.Base.Code != pbCommon.StatusCode_STATUS_OK {
response.ErrorWithCode(c, int(resp.Base.Code), resp.Base.Message)
return
}
// 5. 构建响应
response.Success(c, gin.H{
"asset_id": resp.AssetId,
"new_like_count": resp.NewLikeCount,
})
}
// 注意CheckAssetLike 已被删除
// 检查点赞状态请通过 GET /api/v1/assets/:asset_id 获取,响应中的 is_liked 字段表示当前用户是否已点赞