2026-06-08 19:39:22 +08:00

242 lines
10 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 grpc
import (
"context"
userv1 "hyapp.local/api/proto/user/v1"
"hyapp/pkg/xerr"
cpdomain "hyapp/services/user-service/internal/domain/cp"
)
// ListCPApplications 返回当前用户收到或发出的 CP/兄弟/姐妹申请。
func (s *Server) ListCPApplications(ctx context.Context, req *userv1.ListCPApplicationsRequest) (*userv1.ListCPApplicationsResponse, error) {
ctx = contextWithApp(ctx, req.GetMeta())
if s.cpSvc == nil {
return nil, xerr.ToGRPCError(xerr.New(xerr.Unavailable, "cp service is not configured"))
}
// gRPC 层只透传分页、方向和状态pending 过期固化由 service/repository 统一处理。
applications, total, serverTimeMS, err := s.cpSvc.ListApplications(ctx, req.GetUserId(), req.GetDirection(), req.GetStatus(), req.GetPage(), req.GetPageSize())
if err != nil {
return nil, xerr.ToGRPCError(err)
}
resp := &userv1.ListCPApplicationsResponse{
Applications: make([]*userv1.CPApplication, 0, len(applications)),
Total: total,
ServerTimeMs: serverTimeMS,
}
for _, application := range applications {
resp.Applications = append(resp.Applications, toProtoCPApplication(application))
}
return resp, nil
}
// AcceptCPApplication 同意申请并返回建立后的关系。
func (s *Server) AcceptCPApplication(ctx context.Context, req *userv1.AcceptCPApplicationRequest) (*userv1.AcceptCPApplicationResponse, error) {
ctx = contextWithApp(ctx, req.GetMeta())
if s.cpSvc == nil {
return nil, xerr.ToGRPCError(xerr.New(xerr.Unavailable, "cp service is not configured"))
}
application, relationship, err := s.cpSvc.AcceptApplication(ctx, req.GetUserId(), req.GetApplicationId())
if err != nil {
return nil, xerr.ToGRPCError(err)
}
return &userv1.AcceptCPApplicationResponse{
Application: toProtoCPApplication(application),
Relationship: toProtoCPRelationship(relationship),
}, nil
}
// RejectCPApplication 拒绝申请;拒绝原因只进入 outbox payload不改变关系主事实。
func (s *Server) RejectCPApplication(ctx context.Context, req *userv1.RejectCPApplicationRequest) (*userv1.RejectCPApplicationResponse, error) {
ctx = contextWithApp(ctx, req.GetMeta())
if s.cpSvc == nil {
return nil, xerr.ToGRPCError(xerr.New(xerr.Unavailable, "cp service is not configured"))
}
application, err := s.cpSvc.RejectApplication(ctx, req.GetUserId(), req.GetApplicationId(), req.GetReason())
if err != nil {
return nil, xerr.ToGRPCError(err)
}
return &userv1.RejectCPApplicationResponse{Application: toProtoCPApplication(application)}, nil
}
// ListCPRelationships 返回用户 active 关系;当前每个用户最多同时拥有一条关系。
func (s *Server) ListCPRelationships(ctx context.Context, req *userv1.ListCPRelationshipsRequest) (*userv1.ListCPRelationshipsResponse, error) {
ctx = contextWithApp(ctx, req.GetMeta())
if s.cpSvc == nil {
return nil, xerr.ToGRPCError(xerr.New(xerr.Unavailable, "cp service is not configured"))
}
relationships, total, err := s.cpSvc.ListRelationships(ctx, req.GetUserId(), req.GetRelationType(), req.GetPage(), req.GetPageSize())
if err != nil {
return nil, xerr.ToGRPCError(err)
}
resp := &userv1.ListCPRelationshipsResponse{
Relationships: make([]*userv1.CPRelationship, 0, len(relationships)),
Total: total,
}
for _, relationship := range relationships {
resp.Relationships = append(resp.Relationships, toProtoCPRelationship(relationship))
}
return resp, nil
}
// PrepareBreakCPRelationship 创建解除关系占位gateway 随后按返回费用调用 wallet-service 扣金币。
func (s *Server) PrepareBreakCPRelationship(ctx context.Context, req *userv1.PrepareBreakCPRelationshipRequest) (*userv1.PrepareBreakCPRelationshipResponse, error) {
ctx = contextWithApp(ctx, req.GetMeta())
if s.cpSvc == nil {
return nil, xerr.ToGRPCError(xerr.New(xerr.Unavailable, "cp service is not configured"))
}
breakup, relationship, err := s.cpSvc.PrepareBreakRelationship(ctx, req.GetUserId(), req.GetRelationshipId(), req.GetCommandId())
if err != nil {
return nil, xerr.ToGRPCError(err)
}
return &userv1.PrepareBreakCPRelationshipResponse{
Relationship: toProtoCPRelationship(relationship),
CommandId: breakup.CommandID,
Status: breakup.Status,
BreakupCostCoins: breakup.BreakupCostCoins,
WalletTransactionId: breakup.WalletTransactionID,
CoinBalanceAfter: breakup.CoinBalanceAfter,
}, nil
}
// ConfirmBreakCPRelationship 在钱包扣费成功后确认解除,关系状态和 outbox 在同一事务提交。
func (s *Server) ConfirmBreakCPRelationship(ctx context.Context, req *userv1.ConfirmBreakCPRelationshipRequest) (*userv1.ConfirmBreakCPRelationshipResponse, error) {
ctx = contextWithApp(ctx, req.GetMeta())
if s.cpSvc == nil {
return nil, xerr.ToGRPCError(xerr.New(xerr.Unavailable, "cp service is not configured"))
}
breakup, relationship, err := s.cpSvc.ConfirmBreakRelationship(ctx, req.GetUserId(), req.GetRelationshipId(), req.GetCommandId(), req.GetWalletTransactionId(), req.GetPaidCoinAmount(), req.GetCoinBalanceAfter())
if err != nil {
return nil, xerr.ToGRPCError(err)
}
return &userv1.ConfirmBreakCPRelationshipResponse{
Relationship: toProtoCPRelationship(relationship),
BreakupCostCoins: breakup.BreakupCostCoins,
WalletTransactionId: breakup.WalletTransactionID,
CoinBalanceAfter: breakup.CoinBalanceAfter,
}, nil
}
// CancelBreakCPRelationship 取消还未扣费成功的解除占位,避免钱包失败后关系被其他请求长期阻塞。
func (s *Server) CancelBreakCPRelationship(ctx context.Context, req *userv1.CancelBreakCPRelationshipRequest) (*userv1.CancelBreakCPRelationshipResponse, error) {
ctx = contextWithApp(ctx, req.GetMeta())
if s.cpSvc == nil {
return nil, xerr.ToGRPCError(xerr.New(xerr.Unavailable, "cp service is not configured"))
}
breakup, err := s.cpSvc.CancelBreakRelationship(ctx, req.GetUserId(), req.GetRelationshipId(), req.GetCommandId(), req.GetReason())
if err != nil {
return nil, xerr.ToGRPCError(err)
}
return &userv1.CancelBreakCPRelationshipResponse{
CommandId: breakup.CommandID,
Status: breakup.Status,
}, nil
}
// ConsumeRoomGiftCPEvent 由 user-service 内部 MQ worker 调用,把 RoomGiftSent 转为 CP 申请或亲密值增长。
func (s *Server) ConsumeRoomGiftCPEvent(ctx context.Context, req *userv1.ConsumeRoomGiftCPEventRequest) (*userv1.ConsumeRoomGiftCPEventResponse, error) {
ctx = contextWithApp(ctx, req.GetMeta())
if s.cpSvc == nil {
return nil, xerr.ToGRPCError(xerr.New(xerr.Unavailable, "cp service is not configured"))
}
event := req.GetEvent()
result, err := s.cpSvc.ConsumeGiftEvent(ctx, cpdomain.GiftEvent{
EventID: event.GetEventId(),
RoomID: event.GetRoomId(),
RoomVersion: event.GetRoomVersion(),
OccurredAtMS: event.GetOccurredAtMs(),
SenderUserID: event.GetSenderUserId(),
TargetUserID: event.GetTargetUserId(),
GiftID: event.GetGiftId(),
GiftCount: event.GetGiftCount(),
GiftValue: event.GetGiftValue(),
BillingReceiptID: event.GetBillingReceiptId(),
VisibleRegionID: event.GetVisibleRegionId(),
CommandID: event.GetCommandId(),
GiftTypeCode: event.GetGiftTypeCode(),
CPRelationType: event.GetCpRelationType(),
GiftName: event.GetGiftName(),
GiftIconURL: event.GetGiftIconUrl(),
GiftAnimationURL: event.GetGiftAnimationUrl(),
})
if err != nil {
return nil, xerr.ToGRPCError(err)
}
return &userv1.ConsumeRoomGiftCPEventResponse{
Consumed: result.Consumed,
Application: toProtoCPApplication(result.Application),
Relationship: toProtoCPRelationship(result.Relationship),
}, nil
}
func toProtoCPApplication(application cpdomain.Application) *userv1.CPApplication {
if application.ApplicationID == "" {
return nil
}
return &userv1.CPApplication{
ApplicationId: application.ApplicationID,
RelationType: application.RelationType,
Status: application.Status,
Requester: toProtoCPUserProfile(application.Requester),
Target: toProtoCPUserProfile(application.Target),
RoomId: application.RoomID,
RoomRegionId: application.RoomRegionID,
Gift: toProtoCPGiftSnapshot(application.Gift),
CreatedAtMs: application.CreatedAtMS,
UpdatedAtMs: application.UpdatedAtMS,
ExpiresAtMs: application.ExpiresAtMS,
DecidedAtMs: application.DecidedAtMS,
}
}
func toProtoCPRelationship(relationship cpdomain.Relationship) *userv1.CPRelationship {
if relationship.RelationshipID == "" {
return nil
}
return &userv1.CPRelationship{
RelationshipId: relationship.RelationshipID,
RelationType: relationship.RelationType,
Status: relationship.Status,
Me: toProtoCPUserProfile(relationship.Me),
Partner: toProtoCPUserProfile(relationship.Partner),
IntimacyValue: relationship.IntimacyValue,
Level: relationship.Level,
FormedAtMs: relationship.FormedAtMS,
UpdatedAtMs: relationship.UpdatedAtMS,
CurrentLevelThreshold: relationship.CurrentLevelThreshold,
NextLevelThreshold: relationship.NextLevelThreshold,
NeededForNextLevel: relationship.NeededForNextLevel,
LevelProgressPercent: relationship.LevelProgressPercent,
MaxLevel: relationship.MaxLevel,
BreakupCostCoins: relationship.BreakupCostCoins,
EndedAtMs: relationship.EndedAtMS,
}
}
func toProtoCPUserProfile(profile cpdomain.UserProfile) *userv1.CPUserProfile {
if profile.UserID <= 0 {
return nil
}
return &userv1.CPUserProfile{
UserId: profile.UserID,
DisplayUserId: profile.DisplayUserID,
Username: profile.Username,
Avatar: profile.Avatar,
}
}
func toProtoCPGiftSnapshot(gift cpdomain.GiftSnapshot) *userv1.CPGiftSnapshot {
if gift.GiftID == "" && gift.GiftName == "" {
return nil
}
return &userv1.CPGiftSnapshot{
GiftId: gift.GiftID,
GiftName: gift.GiftName,
GiftIconUrl: gift.GiftIconURL,
GiftAnimationUrl: gift.GiftAnimationURL,
GiftCount: gift.GiftCount,
GiftValue: gift.GiftValue,
BillingReceiptId: gift.BillingReceiptID,
}
}