// Package walletclient 适配 wallet-service 资源 RPC,供 user-service 构建 CP 读模型。 package walletclient import ( "context" "google.golang.org/grpc" walletv1 "hyapp.local/api/proto/wallet/v1" "hyapp/pkg/idgen" cpdomain "hyapp/services/user-service/internal/domain/cp" ) const avatarFrameResourceType = "avatar_frame" // Client 只暴露 user-service 构建 CP 读模型需要的钱包资源读取能力。 type Client struct { client walletv1.WalletServiceClient } // NewGRPC 基于共享 gRPC 连接创建 wallet-service 资源读取客户端。 func NewGRPC(conn grpc.ClientConnInterface) *Client { return &Client{client: walletv1.NewWalletServiceClient(conn)} } // BatchGetAvatarFrames 返回用户当前佩戴头像框快照;没有佩戴的用户不会出现在结果 map 中。 func (c *Client) BatchGetAvatarFrames(ctx context.Context, appCode string, userIDs []int64) (map[int64]cpdomain.AvatarFrameSnapshot, error) { if c == nil || c.client == nil || len(userIDs) == 0 { // 没有用户或依赖未装配时返回空 map,调用方会保持 avatar_frame=nil;这里不制造默认头像框。 return map[int64]cpdomain.AvatarFrameSnapshot{}, nil } // wallet-service 是资源佩戴事实的 owner;user-service 只按用户批量读取 avatar_frame 类型,避免读取整包资源列表。 resp, err := c.client.BatchGetUserEquippedResources(ctx, &walletv1.BatchGetUserEquippedResourcesRequest{ RequestId: idgen.New("cp_frame"), AppCode: appCode, UserIds: userIDs, ResourceTypes: []string{avatarFrameResourceType}, }) if err != nil { return nil, err } frames := make(map[int64]cpdomain.AvatarFrameSnapshot, len(resp.GetUsers())) for _, user := range resp.GetUsers() { for _, entitlement := range user.GetResources() { resource := entitlement.GetResource() if resource == nil || resource.GetResourceType() != avatarFrameResourceType || resource.GetResourceId() <= 0 { // 返回包里可能混入空资源或未来扩展资源类型;榜单只认有效 avatar_frame,其他内容不能进入 CP 快照。 continue } // 一个用户同一类型理论上只应佩戴一个资源;命中第一条有效头像框后 break,避免后续异常重复覆盖。 frames[user.GetUserId()] = cpdomain.AvatarFrameSnapshot{ ResourceID: resource.GetResourceId(), ResourceCode: resource.GetResourceCode(), Name: resource.GetName(), AssetURL: resource.GetAssetUrl(), PreviewURL: resource.GetPreviewUrl(), AnimationURL: resource.GetAnimationUrl(), MetadataJSON: resource.GetMetadataJson(), EntitlementID: entitlement.GetEntitlementId(), } break } } return frames, nil }