package provider import ( "context" "fmt" "strconv" "time" "dubbo.apache.org/dubbo-go/v3/common/constant" "github.com/topfans/backend/pkg/logger" pb "github.com/topfans/backend/pkg/proto/starbook" pbCommon "github.com/topfans/backend/pkg/proto/common" "github.com/topfans/backend/services/starbookService/service" "go.uber.org/zap" ) // StarbookProvider 星册服务Provider实现 // 实现 Triple 协议生成的 StarbookServiceHandler 接口 type StarbookProvider struct { starbookService service.StarbookService } // 确保 StarbookProvider 实现了 StarbookServiceHandler 接口 var _ pb.StarbookServiceHandler = (*StarbookProvider)(nil) // NewStarbookProvider 创建星册服务Provider实例 func NewStarbookProvider(starbookService service.StarbookService) *StarbookProvider { return &StarbookProvider{ starbookService: starbookService, } } // GetStarbookHome 获取星册首页 func (p *StarbookProvider) GetStarbookHome(ctx context.Context, req *pb.GetStarbookHomeRequest) (*pb.GetStarbookHomeResponse, error) { userID, starID, err := extractUserInfoFromDubboAttachments(ctx) if err != nil { return &pb.GetStarbookHomeResponse{ Base: &pbCommon.BaseResponse{ Code: pbCommon.StatusCode_STATUS_UNAUTHORIZED, Message: "user authentication required", Timestamp: 0, }, }, err } resp, err := p.starbookService.GetStarbookHome(userID, starID) if err != nil { logger.Logger.Error("GetStarbookHome failed", zap.Error(err), ) return &pb.GetStarbookHomeResponse{ Base: &pbCommon.BaseResponse{ Code: toStatusCode(err), Message: err.Error(), Timestamp: time.Now().UnixMilli(), }, }, err } return resp, nil } // GetStarbookItems 获取星册藏品列表 func (p *StarbookProvider) GetStarbookItems(ctx context.Context, req *pb.GetStarbookItemsRequest) (*pb.GetStarbookItemsResponse, error) { userID, starID, err := extractUserInfoFromDubboAttachments(ctx) if err != nil { return &pb.GetStarbookItemsResponse{ Base: &pbCommon.BaseResponse{ Code: pbCommon.StatusCode_STATUS_UNAUTHORIZED, Message: "user authentication required", Timestamp: 0, }, }, err } resp, err := p.starbookService.GetStarbookItems(req, userID, starID) if err != nil { logger.Logger.Error("GetStarbookItems failed", zap.Error(err), ) return &pb.GetStarbookItemsResponse{ Base: &pbCommon.BaseResponse{ Code: toStatusCode(err), Message: err.Error(), Timestamp: time.Now().UnixMilli(), }, }, err } return resp, nil } // extractUserInfoFromDubboAttachments 从 Dubbo attachments 中提取用户信息 // 网关调用:网关已验证 Token 并将 user_id 和 star_id 通过 attachments 传递 func extractUserInfoFromDubboAttachments(ctx context.Context) (int64, int64, error) { logger.Logger.Info("=== extractUserInfoFromDubboAttachments called ===") // 使用 constant.AttachmentKey 获取 Dubbo attachments attachments := ctx.Value(constant.AttachmentKey) logger.Logger.Info("ctx.Value(constant.AttachmentKey) result", zap.Any("attachments", attachments), zap.String("type", fmt.Sprintf("%T", attachments)), ) if attachments != nil { logger.Logger.Info("Attachments found in context") if attMap, ok := attachments.(map[string]interface{}); ok { logger.Logger.Info("Attachments is map[string]interface{}", zap.Int("map_size", len(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 } 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 map[string]interface{}", zap.String("actual_type", fmt.Sprintf("%T", attachments)), zap.Any("value", attachments), ) } } else { logger.Logger.Warn("No attachments found in context") // 尝试打印所有 context 的值 logger.Logger.Warn("Context type", zap.String("type", fmt.Sprintf("%T", ctx))) } return 0, 0, fmt.Errorf("user info not found in Dubbo attachments (expected user_id and star_id from gateway)") } // getContextKeys 获取 context 中所有的 key func getContextKeys(ctx context.Context) []string { keys := make([]string, 0) // 直接打印 ctx 类型 logger.Logger.Debug("Context type", zap.String("type", fmt.Sprintf("%T", ctx))) return keys } // getContextValues 遍历 context 中的一些已知值 func getContextValues(ctx context.Context) map[string]interface{} { result := make(map[string]interface{}) // 检查 constant.AttachmentKey if v := ctx.Value(constant.AttachmentKey); v != nil { result["AttachmentKey"] = v } return result } // extractUserInfoFromMap 从 map 中提取 user_id 和 star_id // 支持多种类型:int64, float64, string func extractUserInfoFromMap(attMap map[string]interface{}) (int64, int64) { var userID, starID int64 // 打印所有 key 和 value for k, v := range attMap { logger.Logger.Debug("Attachment map entry", zap.String("key", k), zap.Any("value", v), zap.String("type", fmt.Sprintf("%T", v)), ) } // 提取 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 解析不同类型的整数值 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: if i, err := strconv.ParseInt(val, 10, 64); err == nil { return i } case []string: if len(val) > 0 { if i, err := strconv.ParseInt(val[0], 10, 64); err == nil { return i } } case []interface{}: if len(val) > 0 { if s, ok := val[0].(string); ok { if i, err := strconv.ParseInt(s, 10, 64); err == nil { return i } } } } return 0 } // toStatusCode 将错误转换为Proto状态码 func toStatusCode(err error) pbCommon.StatusCode { if err == nil { return pbCommon.StatusCode_STATUS_OK } // 简化处理,实际应该使用 appErrors.ToStatusCode return pbCommon.StatusCode_STATUS_INTERNAL_ERROR }