369 lines
15 KiB
Go
369 lines
15 KiB
Go
package grpc
|
|
|
|
import (
|
|
"context"
|
|
|
|
"hyapp/pkg/xerr"
|
|
hostservice "hyapp/services/user-service/internal/service/host"
|
|
|
|
userv1 "hyapp.local/api/proto/user/v1"
|
|
)
|
|
|
|
// 这个文件保持薄传输层:只做 proto 与业务层的数据转换和错误映射。
|
|
// 角色授权、区域校验、幂等和事务都在业务层/持久化层,避免不同入口行为分叉。
|
|
|
|
// SearchAgencies 查询当前用户同国家可加入 Agency。
|
|
func (s *Server) SearchAgencies(ctx context.Context, req *userv1.SearchAgenciesRequest) (*userv1.SearchAgenciesResponse, error) {
|
|
if s.hostSvc == nil {
|
|
return nil, xerr.ToGRPCError(xerr.New(xerr.Unavailable, "host service is not configured"))
|
|
}
|
|
ctx = contextWithApp(ctx, req.GetMeta())
|
|
agencies, err := s.hostSvc.SearchAgencies(ctx, req.GetUserId(), req.GetKeyword(), req.GetPageSize())
|
|
if err != nil {
|
|
return nil, xerr.ToGRPCError(err)
|
|
}
|
|
|
|
resp := &userv1.SearchAgenciesResponse{Agencies: make([]*userv1.Agency, 0, len(agencies))}
|
|
for _, agency := range agencies {
|
|
resp.Agencies = append(resp.Agencies, toProtoAgency(agency))
|
|
}
|
|
return resp, nil
|
|
}
|
|
|
|
// ApplyToAgency 创建用户加入 Agency 的待处理申请。
|
|
func (s *Server) ApplyToAgency(ctx context.Context, req *userv1.ApplyToAgencyRequest) (*userv1.ApplyToAgencyResponse, error) {
|
|
if s.hostSvc == nil {
|
|
return nil, xerr.ToGRPCError(xerr.New(xerr.Unavailable, "host service is not configured"))
|
|
}
|
|
ctx = contextWithApp(ctx, req.GetMeta())
|
|
// command_id 和 user_id 来自 App 调用约定;当前阶段没有在传输层注入认证上下文。
|
|
application, err := s.hostSvc.ApplyToAgency(ctx, hostservice.ApplyToAgencyInput{
|
|
CommandID: req.GetCommandId(),
|
|
UserID: req.GetUserId(),
|
|
AgencyID: req.GetAgencyId(),
|
|
})
|
|
if err != nil {
|
|
return nil, xerr.ToGRPCError(err)
|
|
}
|
|
|
|
return &userv1.ApplyToAgencyResponse{Application: toProtoAgencyApplication(application)}, nil
|
|
}
|
|
|
|
// ReviewAgencyApplication 审核用户加入 Agency 申请。
|
|
func (s *Server) ReviewAgencyApplication(ctx context.Context, req *userv1.ReviewAgencyApplicationRequest) (*userv1.ReviewAgencyApplicationResponse, error) {
|
|
if s.hostSvc == nil {
|
|
return nil, xerr.ToGRPCError(xerr.New(xerr.Unavailable, "host service is not configured"))
|
|
}
|
|
ctx = contextWithApp(ctx, req.GetMeta())
|
|
// reviewer_user_id 只是传入业务层的操作者;是否为 Agency 拥有者在持久化层锁内确认。
|
|
result, err := s.hostSvc.ReviewAgencyApplication(ctx, hostservice.ReviewAgencyApplicationInput{
|
|
CommandID: req.GetCommandId(),
|
|
ReviewerUserID: req.GetReviewerUserId(),
|
|
ApplicationID: req.GetApplicationId(),
|
|
Decision: req.GetDecision(),
|
|
Reason: req.GetReason(),
|
|
})
|
|
if err != nil {
|
|
return nil, xerr.ToGRPCError(err)
|
|
}
|
|
|
|
return &userv1.ReviewAgencyApplicationResponse{
|
|
Application: toProtoAgencyApplication(result.Application),
|
|
HostProfile: toProtoHostProfile(result.HostProfile),
|
|
Membership: toProtoAgencyMembership(result.Membership),
|
|
}, nil
|
|
}
|
|
|
|
// KickAgencyHost 结束普通 Host 的 Agency 归属。
|
|
func (s *Server) KickAgencyHost(ctx context.Context, req *userv1.KickAgencyHostRequest) (*userv1.KickAgencyHostResponse, error) {
|
|
if s.hostSvc == nil {
|
|
return nil, xerr.ToGRPCError(xerr.New(xerr.Unavailable, "host service is not configured"))
|
|
}
|
|
ctx = contextWithApp(ctx, req.GetMeta())
|
|
// 传输层不判断拥有者和成员关系,防止未来 HTTP/任务入口复制一份不一致规则。
|
|
result, err := s.hostSvc.KickAgencyHost(ctx, hostservice.KickAgencyHostInput{
|
|
CommandID: req.GetCommandId(),
|
|
OperatorUserID: req.GetOperatorUserId(),
|
|
AgencyID: req.GetAgencyId(),
|
|
HostUserID: req.GetHostUserId(),
|
|
Reason: req.GetReason(),
|
|
})
|
|
if err != nil {
|
|
return nil, xerr.ToGRPCError(err)
|
|
}
|
|
|
|
return &userv1.KickAgencyHostResponse{
|
|
Membership: toProtoAgencyMembership(result.Membership),
|
|
HostProfile: toProtoHostProfile(result.HostProfile),
|
|
}, nil
|
|
}
|
|
|
|
// InviteAgency 直接创建用户成为 Agency owner 的关系事实,不再等待目标用户确认。
|
|
func (s *Server) InviteAgency(ctx context.Context, req *userv1.InviteAgencyRequest) (*userv1.InviteAgencyResponse, error) {
|
|
if s.hostSvc == nil {
|
|
return nil, xerr.ToGRPCError(xerr.New(xerr.Unavailable, "host service is not configured"))
|
|
}
|
|
ctx = contextWithApp(ctx, req.GetMeta())
|
|
// 邀请人的 BD 身份和目标用户的区域/身份冲突都由业务事务校验。
|
|
invitation, err := s.hostSvc.InviteAgency(ctx, hostservice.InviteAgencyInput{
|
|
CommandID: req.GetCommandId(),
|
|
InviterUserID: req.GetInviterUserId(),
|
|
TargetUserID: req.GetTargetUserId(),
|
|
AgencyName: req.GetAgencyName(),
|
|
})
|
|
if err != nil {
|
|
return nil, xerr.ToGRPCError(err)
|
|
}
|
|
|
|
return &userv1.InviteAgencyResponse{Invitation: toProtoRoleInvitation(invitation)}, nil
|
|
}
|
|
|
|
// InviteBD 直接创建用户成为 BD 的关系事实,只有 BD 负责人可调用。
|
|
func (s *Server) InviteBD(ctx context.Context, req *userv1.InviteBDRequest) (*userv1.InviteBDResponse, error) {
|
|
if s.hostSvc == nil {
|
|
return nil, xerr.ToGRPCError(xerr.New(xerr.Unavailable, "host service is not configured"))
|
|
}
|
|
ctx = contextWithApp(ctx, req.GetMeta())
|
|
// BD 负责人授权在持久化层内基于 bd_leader_profiles 当前事实判断。
|
|
invitation, err := s.hostSvc.InviteBD(ctx, hostservice.InviteBDInput{
|
|
CommandID: req.GetCommandId(),
|
|
InviterUserID: req.GetInviterUserId(),
|
|
TargetUserID: req.GetTargetUserId(),
|
|
})
|
|
if err != nil {
|
|
return nil, xerr.ToGRPCError(err)
|
|
}
|
|
|
|
return &userv1.InviteBDResponse{Invitation: toProtoRoleInvitation(invitation)}, nil
|
|
}
|
|
|
|
// ListBDLeaderBDs 返回当前 BD Leader 直属 BD 列表。
|
|
func (s *Server) ListBDLeaderBDs(ctx context.Context, req *userv1.ListBDLeaderBDsRequest) (*userv1.ListBDLeaderBDsResponse, error) {
|
|
if s.hostSvc == nil {
|
|
return nil, xerr.ToGRPCError(xerr.New(xerr.Unavailable, "host service is not configured"))
|
|
}
|
|
ctx = contextWithApp(ctx, req.GetMeta())
|
|
profiles, err := s.hostSvc.ListBDLeaderBDs(ctx, hostservice.ListBDLeaderBDsCommand{
|
|
LeaderUserID: req.GetLeaderUserId(),
|
|
Status: req.GetStatus(),
|
|
PageSize: int(req.GetPageSize()),
|
|
})
|
|
if err != nil {
|
|
return nil, xerr.ToGRPCError(err)
|
|
}
|
|
|
|
resp := &userv1.ListBDLeaderBDsResponse{BdProfiles: make([]*userv1.BDProfile, 0, len(profiles))}
|
|
for _, profile := range profiles {
|
|
resp.BdProfiles = append(resp.BdProfiles, toProtoBDProfile(profile))
|
|
}
|
|
return resp, nil
|
|
}
|
|
|
|
// ListBDLeaderAgencies 返回当前 BD Leader 团队下的 Agency 列表。
|
|
func (s *Server) ListBDLeaderAgencies(ctx context.Context, req *userv1.ListBDLeaderAgenciesRequest) (*userv1.ListBDLeaderAgenciesResponse, error) {
|
|
if s.hostSvc == nil {
|
|
return nil, xerr.ToGRPCError(xerr.New(xerr.Unavailable, "host service is not configured"))
|
|
}
|
|
ctx = contextWithApp(ctx, req.GetMeta())
|
|
agencies, err := s.hostSvc.ListBDLeaderAgencies(ctx, hostservice.ListBDLeaderAgenciesCommand{
|
|
LeaderUserID: req.GetLeaderUserId(),
|
|
Status: req.GetStatus(),
|
|
PageSize: int(req.GetPageSize()),
|
|
})
|
|
if err != nil {
|
|
return nil, xerr.ToGRPCError(err)
|
|
}
|
|
|
|
resp := &userv1.ListBDLeaderAgenciesResponse{Agencies: make([]*userv1.Agency, 0, len(agencies))}
|
|
for _, agency := range agencies {
|
|
resp.Agencies = append(resp.Agencies, toProtoAgency(agency))
|
|
}
|
|
return resp, nil
|
|
}
|
|
|
|
// ListBDAgencies 返回当前 BD 自己拓展的 Agency 列表。
|
|
func (s *Server) ListBDAgencies(ctx context.Context, req *userv1.ListBDAgenciesRequest) (*userv1.ListBDAgenciesResponse, error) {
|
|
if s.hostSvc == nil {
|
|
return nil, xerr.ToGRPCError(xerr.New(xerr.Unavailable, "host service is not configured"))
|
|
}
|
|
ctx = contextWithApp(ctx, req.GetMeta())
|
|
agencies, err := s.hostSvc.ListBDAgencies(ctx, hostservice.ListBDAgenciesCommand{
|
|
BDUserID: req.GetBdUserId(),
|
|
Status: req.GetStatus(),
|
|
PageSize: int(req.GetPageSize()),
|
|
})
|
|
if err != nil {
|
|
return nil, xerr.ToGRPCError(err)
|
|
}
|
|
|
|
resp := &userv1.ListBDAgenciesResponse{Agencies: make([]*userv1.Agency, 0, len(agencies))}
|
|
for _, agency := range agencies {
|
|
resp.Agencies = append(resp.Agencies, toProtoAgency(agency))
|
|
}
|
|
return resp, nil
|
|
}
|
|
|
|
// ProcessRoleInvitation 处理 Agency 或 BD 邀请。
|
|
func (s *Server) ProcessRoleInvitation(ctx context.Context, req *userv1.ProcessRoleInvitationRequest) (*userv1.ProcessRoleInvitationResponse, error) {
|
|
if s.hostSvc == nil {
|
|
return nil, xerr.ToGRPCError(xerr.New(xerr.Unavailable, "host service is not configured"))
|
|
}
|
|
ctx = contextWithApp(ctx, req.GetMeta())
|
|
// actor_user_id 的含义由动作决定:取消是邀请人,接受/拒绝是目标用户。
|
|
result, err := s.hostSvc.ProcessRoleInvitation(ctx, hostservice.ProcessRoleInvitationInput{
|
|
CommandID: req.GetCommandId(),
|
|
ActorUserID: req.GetActorUserId(),
|
|
InvitationID: req.GetInvitationId(),
|
|
Action: req.GetAction(),
|
|
Reason: req.GetReason(),
|
|
})
|
|
if err != nil {
|
|
return nil, xerr.ToGRPCError(err)
|
|
}
|
|
|
|
return &userv1.ProcessRoleInvitationResponse{
|
|
Invitation: toProtoRoleInvitation(result.Invitation),
|
|
HostProfile: toProtoHostProfile(result.HostProfile),
|
|
Agency: toProtoAgency(result.Agency),
|
|
Membership: toProtoAgencyMembership(result.Membership),
|
|
BdProfile: toProtoBDProfile(result.BDProfile),
|
|
}, nil
|
|
}
|
|
|
|
// GetHostProfile 返回当前用户主播身份事实。
|
|
func (s *Server) GetHostProfile(ctx context.Context, req *userv1.GetHostProfileRequest) (*userv1.GetHostProfileResponse, error) {
|
|
if s.hostSvc == nil {
|
|
return nil, xerr.ToGRPCError(xerr.New(xerr.Unavailable, "host service is not configured"))
|
|
}
|
|
ctx = contextWithApp(ctx, req.GetMeta())
|
|
profile, err := s.hostSvc.GetHostProfile(ctx, req.GetUserId())
|
|
if err != nil {
|
|
return nil, xerr.ToGRPCError(err)
|
|
}
|
|
|
|
return &userv1.GetHostProfileResponse{HostProfile: toProtoHostProfile(profile)}, nil
|
|
}
|
|
|
|
// GetBDProfile 按调用方声明的角色返回普通 BD 或 BD Leader 身份事实。
|
|
func (s *Server) GetBDProfile(ctx context.Context, req *userv1.GetBDProfileRequest) (*userv1.GetBDProfileResponse, error) {
|
|
if s.hostSvc == nil {
|
|
return nil, xerr.ToGRPCError(xerr.New(xerr.Unavailable, "host service is not configured"))
|
|
}
|
|
ctx = contextWithApp(ctx, req.GetMeta())
|
|
profile, err := s.hostSvc.GetBDProfile(ctx, req.GetUserId(), req.GetRole())
|
|
if err != nil {
|
|
return nil, xerr.ToGRPCError(err)
|
|
}
|
|
|
|
return &userv1.GetBDProfileResponse{BdProfile: toProtoBDProfile(profile)}, nil
|
|
}
|
|
|
|
// GetCoinSellerProfile 返回当前用户币商身份事实。
|
|
func (s *Server) GetCoinSellerProfile(ctx context.Context, req *userv1.GetCoinSellerProfileRequest) (*userv1.GetCoinSellerProfileResponse, error) {
|
|
if s.hostSvc == nil {
|
|
return nil, xerr.ToGRPCError(xerr.New(xerr.Unavailable, "host service is not configured"))
|
|
}
|
|
ctx = contextWithApp(ctx, req.GetMeta())
|
|
profile, err := s.hostSvc.GetCoinSellerProfile(ctx, req.GetUserId())
|
|
if err != nil {
|
|
return nil, xerr.ToGRPCError(err)
|
|
}
|
|
|
|
return &userv1.GetCoinSellerProfileResponse{CoinSellerProfile: toProtoCoinSellerProfile(profile)}, nil
|
|
}
|
|
|
|
// ListActiveCoinSellersInMyRegion 返回当前用户同区域启用币商。
|
|
func (s *Server) ListActiveCoinSellersInMyRegion(ctx context.Context, req *userv1.ListActiveCoinSellersInMyRegionRequest) (*userv1.ListActiveCoinSellersInMyRegionResponse, error) {
|
|
if s.hostSvc == nil {
|
|
return nil, xerr.ToGRPCError(xerr.New(xerr.Unavailable, "host service is not configured"))
|
|
}
|
|
ctx = contextWithApp(ctx, req.GetMeta())
|
|
items, err := s.hostSvc.ListActiveCoinSellersInMyRegion(ctx, req.GetUserId())
|
|
if err != nil {
|
|
return nil, xerr.ToGRPCError(err)
|
|
}
|
|
|
|
resp := &userv1.ListActiveCoinSellersInMyRegionResponse{CoinSellers: make([]*userv1.CoinSellerListItem, 0, len(items))}
|
|
for _, item := range items {
|
|
resp.CoinSellers = append(resp.CoinSellers, toProtoCoinSellerListItem(item))
|
|
}
|
|
return resp, nil
|
|
}
|
|
|
|
// GetUserRoleSummary 返回 App 入口显隐所需的角色摘要,替代 gateway 多角色 fanout。
|
|
func (s *Server) GetUserRoleSummary(ctx context.Context, req *userv1.GetUserRoleSummaryRequest) (*userv1.GetUserRoleSummaryResponse, error) {
|
|
if s.hostSvc == nil {
|
|
return nil, xerr.ToGRPCError(xerr.New(xerr.Unavailable, "host service is not configured"))
|
|
}
|
|
ctx = contextWithApp(ctx, req.GetMeta())
|
|
summary, err := s.hostSvc.GetUserRoleSummary(ctx, req.GetUserId())
|
|
if err != nil {
|
|
return nil, xerr.ToGRPCError(err)
|
|
}
|
|
|
|
return &userv1.GetUserRoleSummaryResponse{Summary: toProtoUserRoleSummary(summary)}, nil
|
|
}
|
|
|
|
// GetAgency 返回 Agency 当前事实。
|
|
func (s *Server) GetAgency(ctx context.Context, req *userv1.GetAgencyRequest) (*userv1.GetAgencyResponse, error) {
|
|
if s.hostSvc == nil {
|
|
return nil, xerr.ToGRPCError(xerr.New(xerr.Unavailable, "host service is not configured"))
|
|
}
|
|
ctx = contextWithApp(ctx, req.GetMeta())
|
|
agency, err := s.hostSvc.GetAgency(ctx, req.GetAgencyId())
|
|
if err != nil {
|
|
return nil, xerr.ToGRPCError(err)
|
|
}
|
|
|
|
return &userv1.GetAgencyResponse{Agency: toProtoAgency(agency)}, nil
|
|
}
|
|
|
|
// CheckBusinessCapability 返回 H5/App 业务入口可用性,具体身份规则由 host service 统一判断。
|
|
func (s *Server) CheckBusinessCapability(ctx context.Context, req *userv1.CheckBusinessCapabilityRequest) (*userv1.CheckBusinessCapabilityResponse, error) {
|
|
if s.hostSvc == nil {
|
|
return nil, xerr.ToGRPCError(xerr.New(xerr.Unavailable, "host service is not configured"))
|
|
}
|
|
ctx = contextWithApp(ctx, req.GetMeta())
|
|
allowed, reason, err := s.hostSvc.CheckBusinessCapability(ctx, req.GetActorUserId(), req.GetCapability())
|
|
if err != nil {
|
|
return nil, xerr.ToGRPCError(err)
|
|
}
|
|
return &userv1.CheckBusinessCapabilityResponse{Allowed: allowed, Reason: reason}, nil
|
|
}
|
|
|
|
// GetAgencyMembers 返回 Agency 成员关系事实列表。
|
|
func (s *Server) GetAgencyMembers(ctx context.Context, req *userv1.GetAgencyMembersRequest) (*userv1.GetAgencyMembersResponse, error) {
|
|
if s.hostSvc == nil {
|
|
return nil, xerr.ToGRPCError(xerr.New(xerr.Unavailable, "host service is not configured"))
|
|
}
|
|
ctx = contextWithApp(ctx, req.GetMeta())
|
|
memberships, err := s.hostSvc.ListAgencyMembers(ctx, req.GetAgencyId(), req.GetStatus())
|
|
if err != nil {
|
|
return nil, xerr.ToGRPCError(err)
|
|
}
|
|
|
|
resp := &userv1.GetAgencyMembersResponse{Memberships: make([]*userv1.AgencyMembership, 0, len(memberships))}
|
|
for _, membership := range memberships {
|
|
resp.Memberships = append(resp.Memberships, toProtoAgencyMembership(membership))
|
|
}
|
|
return resp, nil
|
|
}
|
|
|
|
// GetAgencyApplications 返回 Agency 申请事实列表。
|
|
func (s *Server) GetAgencyApplications(ctx context.Context, req *userv1.GetAgencyApplicationsRequest) (*userv1.GetAgencyApplicationsResponse, error) {
|
|
if s.hostSvc == nil {
|
|
return nil, xerr.ToGRPCError(xerr.New(xerr.Unavailable, "host service is not configured"))
|
|
}
|
|
ctx = contextWithApp(ctx, req.GetMeta())
|
|
applications, err := s.hostSvc.ListAgencyApplications(ctx, req.GetAgencyId(), req.GetStatus())
|
|
if err != nil {
|
|
return nil, xerr.ToGRPCError(err)
|
|
}
|
|
|
|
resp := &userv1.GetAgencyApplicationsResponse{Applications: make([]*userv1.AgencyApplication, 0, len(applications))}
|
|
for _, application := range applications {
|
|
resp.Applications = append(resp.Applications, toProtoAgencyApplication(application))
|
|
}
|
|
return resp, nil
|
|
}
|