topfans/backend/services/userService/provider/user_provider.go
2026-04-07 22:29:48 +08:00

854 lines
22 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 provider
import (
"context"
"fmt"
"dubbo.apache.org/dubbo-go/v3/common/constant"
appErrors "github.com/topfans/backend/pkg/errors"
"github.com/topfans/backend/pkg/logger"
pbCommon "github.com/topfans/backend/pkg/proto/common"
pb "github.com/topfans/backend/pkg/proto/user"
"github.com/topfans/backend/services/userService/service"
"go.uber.org/zap"
)
// UserProvider 用户信息Provider实现
type UserProvider struct {
userService service.UserService
identityService service.IdentityService
}
// NewUserProvider 创建用户信息Provider实例
func NewUserProvider(userService service.UserService, identityService service.IdentityService) *UserProvider {
return &UserProvider{
userService: userService,
identityService: identityService,
}
}
// GetUser 获取用户信息
func (p *UserProvider) GetUser(ctx context.Context, req *pb.GetUserRequest) (*pb.GetUserResponse, error) {
// 记录请求日志
logger.Logger.Info("Received GetUser request",
zap.Int64("user_id", req.UserId),
)
// 调用Service层
resp, err := p.userService.GetUser(req)
if err != nil {
logger.Logger.Error("GetUser failed",
zap.Int64("user_id", req.UserId),
zap.Error(err),
)
// 如果响应为空,构建错误响应
if resp == nil {
resp = &pb.GetUserResponse{
Base: &pbCommon.BaseResponse{
Code: appErrors.ToStatusCode(err),
Message: err.Error(),
Timestamp: 0,
},
}
}
return resp, err
}
logger.Logger.Debug("GetUser successful",
zap.Int64("user_id", req.UserId),
)
return resp, nil
}
// GetFanProfile 获取粉丝档案
func (p *UserProvider) GetFanProfile(ctx context.Context, req *pb.GetFanProfileRequest) (*pb.GetFanProfileResponse, error) {
// 记录请求日志
logger.Logger.Info("Received GetFanProfile request",
zap.Int64("user_id", req.UserId),
zap.Int64("star_id", req.StarId),
)
// 调用Service层
resp, err := p.userService.GetFanProfile(req)
if err != nil {
logger.Logger.Error("GetFanProfile failed",
zap.Int64("user_id", req.UserId),
zap.Int64("star_id", req.StarId),
zap.Error(err),
)
// 如果响应为空,构建错误响应
if resp == nil {
resp = &pb.GetFanProfileResponse{
Base: &pbCommon.BaseResponse{
Code: appErrors.ToStatusCode(err),
Message: err.Error(),
Timestamp: 0,
},
}
}
return resp, err
}
logger.Logger.Debug("GetFanProfile successful",
zap.Int64("user_id", req.UserId),
zap.Int64("star_id", req.StarId),
)
return resp, nil
}
// GetCurrentUser 获取当前登录用户信息
// 假设Gin 网关已经验证 Token并通过 Dubbo attachments 传递 user_id 和 star_id
func (p *UserProvider) GetCurrentUser(ctx context.Context, req *pb.GetCurrentUserRequest) (*pb.GetCurrentUserResponse, error) {
// 记录请求日志
logger.Logger.Info("Received GetCurrentUser request")
// 从 Dubbo attachments 获取用户信息(网关已验证并传递)
userID, starID, err := extractUserInfoFromDubboAttachments(ctx)
if err != nil {
logger.Logger.Error("Failed to extract user info from attachments",
zap.Error(err),
)
return &pb.GetCurrentUserResponse{
Base: &pbCommon.BaseResponse{
Code: pbCommon.StatusCode_STATUS_UNAUTHORIZED,
Message: "user authentication required",
Timestamp: 0,
},
}, err
}
logger.Logger.Debug("User info extracted from Dubbo attachments",
zap.Int64("user_id", userID),
zap.Int64("star_id", starID),
)
// 调用Service层的GetMyProfile使用GetMyProfileRequest
myProfileReq := &pb.GetMyProfileRequest{}
resp, err := p.userService.GetMyProfile(myProfileReq, userID, starID)
if err != nil {
logger.Logger.Error("GetCurrentUser failed",
zap.Int64("user_id", userID),
zap.Error(err),
)
return &pb.GetCurrentUserResponse{
Base: &pbCommon.BaseResponse{
Code: appErrors.ToStatusCode(err),
Message: err.Error(),
Timestamp: 0,
},
}, err
}
// 转换为GetCurrentUserResponse格式
currentUserResp := &pb.GetCurrentUserResponse{
Base: resp.Base,
User: resp.User,
FanProfile: resp.FanProfile,
FanProfiles: resp.FanProfiles,
}
logger.Logger.Debug("GetCurrentUser successful",
zap.Int64("user_id", userID),
)
return currentUserResp, nil
}
// GetMyProfile 获取个人信息页
func (p *UserProvider) GetMyProfile(ctx context.Context, req *pb.GetMyProfileRequest) (*pb.GetMyProfileResponse, error) {
// 记录请求日志
logger.Logger.Info("Received GetMyProfile request")
// 从 Dubbo attachments 获取用户信息(网关已验证并传递)
userID, starID, err := extractUserInfoFromDubboAttachments(ctx)
if err != nil {
logger.Logger.Error("Failed to extract user info from attachments",
zap.Error(err),
)
return &pb.GetMyProfileResponse{
Base: &pbCommon.BaseResponse{
Code: pbCommon.StatusCode_STATUS_UNAUTHORIZED,
Message: "user authentication required",
Timestamp: 0,
},
}, err
}
// 调用Service层
resp, err := p.userService.GetMyProfile(req, userID, starID)
if err != nil {
logger.Logger.Error("GetMyProfile failed",
zap.Int64("user_id", userID),
zap.Int64("star_id", starID),
zap.Error(err),
)
// 如果响应为空,构建错误响应
if resp == nil {
resp = &pb.GetMyProfileResponse{
Base: &pbCommon.BaseResponse{
Code: appErrors.ToStatusCode(err),
Message: err.Error(),
Timestamp: 0,
},
}
}
return resp, err
}
logger.Logger.Debug("GetMyProfile successful",
zap.Int64("user_id", userID),
zap.Int64("star_id", starID),
)
return resp, nil
}
// CheckNickname 检查昵称是否已被注册
func (p *UserProvider) CheckNickname(ctx context.Context, req *pb.CheckNicknameRequest) (*pb.CheckNicknameResponse, error) {
// 记录请求日志
logger.Logger.Info("Received CheckNickname request",
zap.String("nickname", req.Nickname),
)
// 调用Service层
resp, err := p.userService.CheckNickname(req)
if err != nil {
logger.Logger.Error("CheckNickname failed",
zap.String("nickname", req.Nickname),
zap.Error(err),
)
// 如果响应为空,构建错误响应
if resp == nil {
resp = &pb.CheckNicknameResponse{
Base: &pbCommon.BaseResponse{
Code: appErrors.ToStatusCode(err),
Message: err.Error(),
Timestamp: 0,
},
}
}
return resp, err
}
logger.Logger.Info("CheckNickname successful",
zap.String("nickname", req.Nickname),
zap.Bool("exists", resp.Exists),
)
return resp, nil
}
// CheckMobile 检查手机号是否已被注册
func (p *UserProvider) CheckMobile(ctx context.Context, req *pb.CheckMobileRequest) (*pb.CheckMobileResponse, error) {
// 记录请求日志
logger.Logger.Info("Received CheckMobile request",
zap.String("mobile", req.Mobile),
)
// 调用Service层
resp, err := p.userService.CheckMobile(req)
if err != nil {
logger.Logger.Error("CheckMobile failed",
zap.String("mobile", req.Mobile),
zap.Error(err),
)
// 如果响应为空,构建错误响应
if resp == nil {
resp = &pb.CheckMobileResponse{
Base: &pbCommon.BaseResponse{
Code: appErrors.ToStatusCode(err),
Message: err.Error(),
Timestamp: 0,
},
}
}
return resp, err
}
logger.Logger.Info("CheckMobile successful",
zap.String("mobile", req.Mobile),
zap.Bool("exists", resp.Exists),
)
return resp, nil
}
// UpdateNickname 修改昵称
func (p *UserProvider) UpdateNickname(ctx context.Context, req *pb.UpdateNicknameRequest) (*pb.UpdateNicknameResponse, error) {
// 记录请求日志
logger.Logger.Info("Received UpdateNickname request")
// 从 Dubbo attachments 获取用户信息(网关已验证并传递)
userID, starID, err := extractUserInfoFromDubboAttachments(ctx)
if err != nil {
logger.Logger.Error("Failed to extract user info from attachments",
zap.Error(err),
)
return &pb.UpdateNicknameResponse{
Base: &pbCommon.BaseResponse{
Code: pbCommon.StatusCode_STATUS_UNAUTHORIZED,
Message: "user authentication required",
Timestamp: 0,
},
}, err
}
// 调用Service层
resp, err := p.userService.UpdateNickname(req, userID, starID)
if err != nil {
logger.Logger.Error("UpdateNickname failed",
zap.Int64("user_id", userID),
zap.Int64("star_id", starID),
zap.Error(err),
)
// 如果响应为空,构建错误响应
if resp == nil {
resp = &pb.UpdateNicknameResponse{
Base: &pbCommon.BaseResponse{
Code: appErrors.ToStatusCode(err),
Message: err.Error(),
Timestamp: 0,
},
}
}
return resp, err
}
logger.Logger.Info("UpdateNickname successful",
zap.Int64("user_id", userID),
zap.Int64("star_id", starID),
zap.String("new_nickname", req.Nickname),
)
return resp, nil
}
// UpdatePassword 修改密码
func (p *UserProvider) UpdatePassword(ctx context.Context, req *pb.UpdatePasswordRequest) (*pb.UpdatePasswordResponse, error) {
// 记录请求日志
logger.Logger.Info("Received UpdatePassword request")
// 从 Dubbo attachments 获取用户信息(网关已验证并传递)
userID, _, err := extractUserInfoFromDubboAttachments(ctx)
if err != nil {
logger.Logger.Error("Failed to extract user info from attachments",
zap.Error(err),
)
return &pb.UpdatePasswordResponse{
Base: &pbCommon.BaseResponse{
Code: pbCommon.StatusCode_STATUS_UNAUTHORIZED,
Message: "user authentication required",
Timestamp: 0,
},
}, err
}
// 调用Service层
resp, err := p.userService.UpdatePassword(req, userID)
if err != nil {
logger.Logger.Error("UpdatePassword failed",
zap.Int64("user_id", userID),
zap.Error(err),
)
// 如果响应为空,构建错误响应
if resp == nil {
resp = &pb.UpdatePasswordResponse{
Base: &pbCommon.BaseResponse{
Code: appErrors.ToStatusCode(err),
Message: err.Error(),
Timestamp: 0,
},
}
}
return resp, err
}
logger.Logger.Info("UpdatePassword successful",
zap.Int64("user_id", userID),
)
return resp, nil
}
// UpdateAvatar 更新用户头像
func (p *UserProvider) UpdateAvatar(ctx context.Context, req *pb.UpdateAvatarRequest) (*pb.UpdateAvatarResponse, error) {
// 记录请求日志
logger.Logger.Info("Received UpdateAvatar request")
// 从 Dubbo attachments 获取用户信息(网关已验证并传递)
userID, starID, err := extractUserInfoFromDubboAttachments(ctx)
if err != nil {
logger.Logger.Error("Failed to extract user info from attachments",
zap.Error(err),
)
return &pb.UpdateAvatarResponse{
Base: &pbCommon.BaseResponse{
Code: pbCommon.StatusCode_STATUS_UNAUTHORIZED,
Message: "user authentication required",
Timestamp: 0,
},
}, err
}
// 调用Service层
resp, err := p.userService.UpdateAvatar(req, userID, starID)
if err != nil {
logger.Logger.Error("UpdateAvatar failed",
zap.Int64("user_id", userID),
zap.Error(err),
)
// 如果响应为空,构建错误响应
if resp == nil {
resp = &pb.UpdateAvatarResponse{
Base: &pbCommon.BaseResponse{
Code: appErrors.ToStatusCode(err),
Message: err.Error(),
Timestamp: 0,
},
}
}
return resp, err
}
logger.Logger.Info("UpdateAvatar successful",
zap.Int64("user_id", userID),
zap.String("avatar_url", req.AvatarUrl),
)
return resp, nil
}
// GetFanIdentities 获取可选粉丝身份列表
func (p *UserProvider) GetFanIdentities(ctx context.Context, req *pb.GetFanIdentitiesRequest) (*pb.GetFanIdentitiesResponse, error) {
// 记录请求日志
logger.Logger.Info("Received GetFanIdentities request",
zap.String("keyword", req.Keyword),
)
// 调用Service层
resp, err := p.identityService.GetFanIdentities(req)
if err != nil {
logger.Logger.Error("GetFanIdentities failed",
zap.String("keyword", req.Keyword),
zap.Error(err),
)
// 如果响应为空,构建错误响应
if resp == nil {
resp = &pb.GetFanIdentitiesResponse{
Base: &pbCommon.BaseResponse{
Code: appErrors.ToStatusCode(err),
Message: err.Error(),
Timestamp: 0,
},
}
}
return resp, err
}
logger.Logger.Debug("GetFanIdentities successful",
zap.String("keyword", req.Keyword),
zap.Int("count", len(resp.Stars)),
)
return resp, nil
}
// GetMyFanIdentities 获取我的粉丝身份列表
func (p *UserProvider) GetMyFanIdentities(ctx context.Context, req *pb.GetMyFanIdentitiesRequest) (*pb.GetMyFanIdentitiesResponse, error) {
// 从 Dubbo attachments 获取用户信息(网关已验证并传递)
userID, starID, err := extractUserInfoFromDubboAttachments(ctx)
if err != nil {
logger.Logger.Error("Failed to extract user info from attachments",
zap.Error(err),
)
return &pb.GetMyFanIdentitiesResponse{
Base: &pbCommon.BaseResponse{
Code: pbCommon.StatusCode_STATUS_UNAUTHORIZED,
Message: "user authentication required",
Timestamp: 0,
},
}, err
}
// 记录请求日志
logger.Logger.Info("Received GetMyFanIdentities request",
zap.Int64("user_id", userID),
zap.Int64("star_id", starID),
)
// 调用Service层
resp, err := p.identityService.GetMyFanIdentities(userID, starID)
if err != nil {
logger.Logger.Error("GetMyFanIdentities failed",
zap.Int64("user_id", userID),
zap.Int64("star_id", starID),
zap.Error(err),
)
// 如果响应为空,构建错误响应
if resp == nil {
resp = &pb.GetMyFanIdentitiesResponse{
Base: &pbCommon.BaseResponse{
Code: appErrors.ToStatusCode(err),
Message: err.Error(),
Timestamp: 0,
},
}
}
return resp, err
}
logger.Logger.Debug("GetMyFanIdentities successful",
zap.Int64("user_id", userID),
zap.Int64("star_id", starID),
zap.Int("count", len(resp.Items)),
)
return resp, nil
}
// AddIdentity 新增粉丝身份
func (p *UserProvider) AddIdentity(ctx context.Context, req *pb.AddIdentityRequest) (*pb.AddIdentityResponse, error) {
// 记录请求日志
logger.Logger.Info("Received AddIdentity request",
zap.Int64("star_id", req.StarId),
zap.String("nickname", req.Nickname),
)
// 从 Dubbo attachments 获取用户信息(网关已验证并传递)
userID, _, err := extractUserInfoFromDubboAttachments(ctx)
if err != nil {
logger.Logger.Error("Failed to extract user info from attachments",
zap.Error(err),
)
return &pb.AddIdentityResponse{
Base: &pbCommon.BaseResponse{
Code: pbCommon.StatusCode_STATUS_UNAUTHORIZED,
Message: "user authentication required",
Timestamp: 0,
},
}, err
}
// 调用Service层
resp, err := p.identityService.AddIdentity(req, userID)
if err != nil {
logger.Logger.Error("AddIdentity failed",
zap.Int64("user_id", userID),
zap.Int64("star_id", req.StarId),
zap.Error(err),
)
// 如果响应为空,构建错误响应
if resp == nil {
resp = &pb.AddIdentityResponse{
Base: &pbCommon.BaseResponse{
Code: appErrors.ToStatusCode(err),
Message: err.Error(),
Timestamp: 0,
},
}
}
return resp, err
}
logger.Logger.Info("AddIdentity successful",
zap.Int64("user_id", userID),
zap.Int64("star_id", req.StarId),
)
return resp, nil
}
// SwitchIdentity 切换粉丝身份
func (p *UserProvider) SwitchIdentity(ctx context.Context, req *pb.SwitchIdentityRequest) (*pb.SwitchIdentityResponse, error) {
// 记录请求日志
logger.Logger.Info("Received SwitchIdentity request",
zap.Int64("new_star_id", req.NewStarId),
)
// 从 Dubbo attachments 获取用户信息(网关已验证并传递)
userID, currentStarID, err := extractUserInfoFromDubboAttachments(ctx)
if err != nil {
logger.Logger.Error("Failed to extract user info from attachments",
zap.Error(err),
)
return &pb.SwitchIdentityResponse{
Base: &pbCommon.BaseResponse{
Code: pbCommon.StatusCode_STATUS_UNAUTHORIZED,
Message: "user authentication required",
Timestamp: 0,
},
}, err
}
// 调用Service层
resp, err := p.identityService.SwitchIdentity(req, userID, currentStarID)
if err != nil {
logger.Logger.Error("SwitchIdentity failed",
zap.Int64("user_id", userID),
zap.Int64("current_star_id", currentStarID),
zap.Int64("new_star_id", req.NewStarId),
zap.Error(err),
)
// 如果响应为空,构建错误响应
if resp == nil {
resp = &pb.SwitchIdentityResponse{
Base: &pbCommon.BaseResponse{
Code: appErrors.ToStatusCode(err),
Message: err.Error(),
Timestamp: 0,
},
}
}
return resp, err
}
logger.Logger.Info("SwitchIdentity successful",
zap.Int64("user_id", userID),
zap.Int64("current_star_id", currentStarID),
zap.Int64("new_star_id", req.NewStarId),
)
return resp, nil
}
// extractUserInfoFromDubboAttachments 从 Dubbo attachments 中提取用户信息
// 网关调用:网关已验证 Token 并将 user_id 和 star_id 通过 attachments 传递
// 直接调用:可以通过 grpcurl -rpc-header 传递 user_id 和 star_id 进行测试
func extractUserInfoFromDubboAttachments(ctx context.Context) (int64, int64, error) {
logger.Logger.Info("Extracting user info from Dubbo attachments")
// 使用正确的 constant.AttachmentKey 获取 Dubbo attachments
if attachments := ctx.Value(constant.AttachmentKey); attachments != nil {
logger.Logger.Info("Found attachments in context",
zap.Any("attachments", attachments),
zap.String("key", fmt.Sprintf("%v", constant.AttachmentKey)),
)
if attMap, ok := attachments.(map[string]interface{}); ok {
logger.Logger.Info("Attachments is a map", zap.Any("map", attMap))
userID, starID := extractUserInfoFromMap(attMap)
if userID > 0 && starID > 0 {
logger.Logger.Info("Successfully extracted user info",
zap.Int64("user_id", userID),
zap.Int64("star_id", starID),
)
return userID, starID, nil
} else {
logger.Logger.Warn("Extracted zero user_id or star_id",
zap.Int64("user_id", userID),
zap.Int64("star_id", starID),
)
}
} else {
logger.Logger.Warn("Attachments is not a map[string]interface{}",
zap.String("type", fmt.Sprintf("%T", attachments)),
)
}
} else {
logger.Logger.Warn("No attachments found in context",
zap.String("key", fmt.Sprintf("%v", constant.AttachmentKey)),
)
}
logger.Logger.Error("Failed to extract user info from Dubbo attachments")
return 0, 0, fmt.Errorf("user info not found in Dubbo attachments (expected user_id and star_id from gateway)")
}
// extractUserInfoFromMap 从 map 中提取 user_id 和 star_id
// 支持多种类型int64, float64, string, []string, []interface{}
func extractUserInfoFromMap(attMap map[string]interface{}) (int64, int64) {
var userID, starID int64
// 提取 user_id
if v, ok := attMap["user_id"]; ok {
userID = parseIntValue(v)
}
// 提取 star_id
if v, ok := attMap["star_id"]; ok {
starID = parseIntValue(v)
}
return userID, starID
}
// parseIntValue 解析各种类型的值为 int64
func parseIntValue(v interface{}) int64 {
switch val := v.(type) {
case int64:
return val
case int:
return int64(val)
case float64:
return int64(val)
case string:
var result int64
fmt.Sscanf(val, "%d", &result)
return result
case []string:
if len(val) > 0 {
var result int64
fmt.Sscanf(val[0], "%d", &result)
return result
}
case []interface{}:
if len(val) > 0 {
return parseIntValue(val[0])
}
}
return 0
}
// UpdateFanProfileSocial 更新粉丝档案的好友数量内部RPC调用
func (p *UserProvider) UpdateFanProfileSocial(ctx context.Context, req *pb.UpdateFanProfileSocialRequest) (*pb.UpdateFanProfileSocialResponse, error) {
logger.Logger.Info("Received UpdateFanProfileSocial request",
zap.Int64("user_id", req.UserId),
zap.Int64("star_id", req.StarId),
zap.Int32("delta", req.Delta),
)
// 调用Service层
resp, err := p.userService.UpdateFanProfileSocial(req)
if err != nil {
logger.Logger.Error("UpdateFanProfileSocial failed",
zap.Int64("user_id", req.UserId),
zap.Int64("star_id", req.StarId),
zap.Int32("delta", req.Delta),
zap.Error(err),
)
// 如果响应为空,构建错误响应
if resp == nil {
resp = &pb.UpdateFanProfileSocialResponse{
Base: &pbCommon.BaseResponse{
Code: appErrors.ToStatusCode(err),
Message: err.Error(),
Timestamp: 0,
},
}
}
return resp, err
}
logger.Logger.Info("UpdateFanProfileSocial successful",
zap.Int64("user_id", req.UserId),
zap.Int64("star_id", req.StarId),
zap.Int32("delta", req.Delta),
zap.Int32("new_social", resp.NewSocial),
)
return resp, nil
}
// UpdateCrystalBalance 更新水晶余额内部RPC调用
func (p *UserProvider) UpdateCrystalBalance(ctx context.Context, req *pb.UpdateCrystalBalanceRequest) (*pb.UpdateCrystalBalanceResponse, error) {
logger.Logger.Info("Received UpdateCrystalBalance request",
zap.Int64("user_id", req.UserId),
zap.Int64("star_id", req.StarId),
zap.Int64("delta", req.Delta),
)
// 调用Service层
resp, err := p.userService.UpdateCrystalBalance(req)
if err != nil {
logger.Logger.Error("UpdateCrystalBalance failed",
zap.Int64("user_id", req.UserId),
zap.Int64("star_id", req.StarId),
zap.Int64("delta", req.Delta),
zap.Error(err),
)
// 如果响应为空,构建错误响应
if resp == nil {
resp = &pb.UpdateCrystalBalanceResponse{
Base: &pbCommon.BaseResponse{
Code: appErrors.ToStatusCode(err),
Message: err.Error(),
Timestamp: 0,
},
}
}
return resp, err
}
logger.Logger.Info("UpdateCrystalBalance successful",
zap.Int64("user_id", req.UserId),
zap.Int64("star_id", req.StarId),
zap.Int64("delta", req.Delta),
zap.Int64("new_balance", resp.NewBalance),
)
return resp, nil
}
// UpdateAssetsCount 更新资产数量内部RPC调用
func (p *UserProvider) UpdateAssetsCount(ctx context.Context, req *pb.UpdateAssetsCountRequest) (*pb.UpdateAssetsCountResponse, error) {
logger.Logger.Info("Received UpdateAssetsCount request",
zap.Int64("user_id", req.UserId),
zap.Int64("star_id", req.StarId),
zap.Int32("delta", req.Delta),
)
// 调用Service层
resp, err := p.userService.UpdateAssetsCount(req)
if err != nil {
logger.Logger.Error("UpdateAssetsCount failed",
zap.Int64("user_id", req.UserId),
zap.Int64("star_id", req.StarId),
zap.Int32("delta", req.Delta),
zap.Error(err),
)
// 如果响应为空,构建错误响应
if resp == nil {
resp = &pb.UpdateAssetsCountResponse{
Base: &pbCommon.BaseResponse{
Code: appErrors.ToStatusCode(err),
Message: err.Error(),
Timestamp: 0,
},
}
}
return resp, err
}
logger.Logger.Info("UpdateAssetsCount successful",
zap.Int64("user_id", req.UserId),
zap.Int64("star_id", req.StarId),
zap.Int32("delta", req.Delta),
zap.Int32("new_count", resp.NewCount),
)
return resp, nil
}