- 新增 GET /api/v1/me/liked-assets 接口 - 新增 GET /api/v1/me/exhibited-assets 接口 - 新增 GetMyLikedAssets 和 GetMyExhibitedAssets RPC 方法 - 新增 ExhibitedAssetItemDTO 和 GetMyExhibitedAssetsResponseDTO - 前端新增 getUserLikedAssetsApi 和 getUserExhibitedAssetsApi(暂不实现) Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
950 lines
27 KiB
Go
950 lines
27 KiB
Go
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,nickname=string,search_mode=boolean} 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"`
|
||
Message string `json:"message"`
|
||
Nickname string `json:"nickname"`
|
||
SearchMode bool `json:"search_mode"`
|
||
}
|
||
|
||
if err := c.ShouldBindJSON(&req); err != nil {
|
||
response.Error(c, http.StatusBadRequest, "参数错误: "+err.Error())
|
||
return
|
||
}
|
||
|
||
// 验证参数:search_mode=true 时需要 nickname,否则需要 friend_user_id
|
||
if req.SearchMode && req.Nickname == "" {
|
||
response.Error(c, http.StatusBadRequest, "搜索模式下昵称不能为空")
|
||
return
|
||
}
|
||
if !req.SearchMode && req.FriendUserID == 0 && req.Nickname == "" {
|
||
response.Error(c, http.StatusBadRequest, "friend_user_id 或 nickname 不能同时为空")
|
||
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
|
||
rpcReq := &pbSocial.SendFriendRequestRequest{
|
||
FriendUserId: req.FriendUserID,
|
||
Message: req.Message,
|
||
Nickname: req.Nickname,
|
||
SearchMode: req.SearchMode,
|
||
}
|
||
resp, err := ctrl.socialService.SendFriendRequest(ctx, rpcReq)
|
||
|
||
if err != nil {
|
||
logger.Logger.Error("SendFriendRequest RPC failed",
|
||
zap.Int64("user_id", userID.(int64)),
|
||
zap.Int64("friend_user_id", req.FriendUserID),
|
||
zap.String("nickname", req.Nickname),
|
||
zap.Bool("search_mode", req.SearchMode),
|
||
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
|
||
}
|
||
|
||
// 如果是搜索模式,返回匹配用户列表
|
||
if req.SearchMode {
|
||
users := make([]map[string]interface{}, 0, len(resp.MatchedUsers))
|
||
for _, user := range resp.MatchedUsers {
|
||
users = append(users, map[string]interface{}{
|
||
"fan_profile_id": user.FanProfileId,
|
||
"user_id": user.UserId,
|
||
"nickname": user.Nickname,
|
||
"avatar": user.Avatar,
|
||
"fan_level": user.FanLevel,
|
||
"relationship_status": user.RelationshipStatus,
|
||
"can_send_request": user.CanSendRequest,
|
||
"cooldown_ends_at": user.CooldownEndsAt,
|
||
})
|
||
}
|
||
response.Success(c, gin.H{
|
||
"users": users,
|
||
})
|
||
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 字段表示当前用户是否已点赞
|
||
|
||
// GetMyLikedAssets 获取我点赞的作品列表
|
||
// @Summary 获取我点赞的作品列表
|
||
// @Description 获取当前用户点赞过的作品列表(只返回展出中且未过期的)
|
||
// @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/me/liked-assets [get]
|
||
func (ctrl *SocialController) GetMyLikedAssets(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.GetMyLikedAssets(ctx, &pbSocial.GetMyLikedAssetsRequest{
|
||
Page: int32(page),
|
||
PageSize: int32(pageSize),
|
||
})
|
||
|
||
if err != nil {
|
||
logger.Logger.Error("GetMyLikedAssets 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.Data.Items))
|
||
for _, item := range resp.Data.Items {
|
||
items = append(items, map[string]interface{}{
|
||
"asset_id": item.AssetId,
|
||
"name": item.Name,
|
||
"cover_url": item.CoverUrl,
|
||
"like_count": item.LikeCount,
|
||
"liked_at": item.LikedAt,
|
||
"earnings": item.Earnings,
|
||
})
|
||
}
|
||
|
||
response.Success(c, gin.H{
|
||
"items": items,
|
||
"total": resp.Data.Total,
|
||
"page": resp.Data.Page,
|
||
"page_size": resp.Data.PageSize,
|
||
"has_more": resp.Data.HasMore,
|
||
})
|
||
}
|