2026-06-23 11:53:00 +08:00

92 lines
4.0 KiB
Go

// Package gift contains gift debit usecases.
package gift
import (
"context"
"hyapp/pkg/appcode"
"hyapp/pkg/xerr"
"hyapp/services/wallet-service/internal/domain/ledger"
"hyapp/services/wallet-service/internal/service/wallet/ports"
)
// Service 校验礼物扣费命令,并把原子账务提交交给 repository。
type Service struct {
// repository 只需要礼物账本端口,后续单测不用实现完整 wallet Repository。
repository ports.GiftLedgerStore
}
// New 创建礼物扣费用例。
func New(repository ports.GiftLedgerStore) *Service {
return &Service{repository: repository}
}
// DebitGift 校验普通送礼扣费命令,并交给 repository 做原子落账和幂等。
func (s *Service) DebitGift(ctx context.Context, command ledger.DebitGiftCommand) (ledger.Receipt, error) {
command.RobotGift = false
return s.debitGift(ctx, command)
}
// DebitRobotGift 校验机器人房间专用送礼扣费命令,并强制使用 ROBOT_COIN 账务语义。
func (s *Service) DebitRobotGift(ctx context.Context, command ledger.DebitGiftCommand) (ledger.Receipt, error) {
command.RobotGift = true
command.TargetIsHost = false
command.TargetHostRegionID = 0
command.TargetAgencyOwnerUserID = 0
return s.debitGift(ctx, command)
}
func (s *Service) debitGift(ctx context.Context, command ledger.DebitGiftCommand) (ledger.Receipt, error) {
if command.CommandID == "" || command.RoomID == "" || command.SenderUserID <= 0 || command.TargetUserID <= 0 || command.GiftID == "" {
return ledger.Receipt{}, xerr.New(xerr.InvalidArgument, "billing command is incomplete")
}
if command.GiftCount <= 0 {
return ledger.Receipt{}, xerr.New(xerr.InvalidArgument, "gift_count must be positive")
}
if command.TargetIsHost && command.TargetHostRegionID <= 0 {
// 主播周期钻石后续要按区域工资政策结算;只有 active host profile 且带区域时才允许入账。
return ledger.Receipt{}, xerr.New(xerr.InvalidArgument, "target_host_region_id is required")
}
if s == nil || s.repository == nil {
return ledger.Receipt{}, xerr.New(xerr.Unavailable, "wallet repository is not configured")
}
command.AppCode = appcode.Normalize(command.AppCode)
ctx = appcode.WithContext(ctx, command.AppCode)
return s.repository.DebitGift(ctx, command)
}
// BatchDebitGift 校验多目标送礼扣费命令,并要求 repository 在同一事务中完成全部目标结算。
func (s *Service) BatchDebitGift(ctx context.Context, command ledger.BatchDebitGiftCommand) (ledger.BatchGiftReceipt, error) {
if command.CommandID == "" || command.RoomID == "" || command.SenderUserID <= 0 || command.GiftID == "" {
return ledger.BatchGiftReceipt{}, xerr.New(xerr.InvalidArgument, "billing command is incomplete")
}
if command.GiftCount <= 0 {
return ledger.BatchGiftReceipt{}, xerr.New(xerr.InvalidArgument, "gift_count must be positive")
}
if len(command.Targets) == 0 {
return ledger.BatchGiftReceipt{}, xerr.New(xerr.InvalidArgument, "gift targets are required")
}
seen := make(map[int64]struct{}, len(command.Targets))
for _, target := range command.Targets {
if target.CommandID == "" || target.TargetUserID <= 0 {
return ledger.BatchGiftReceipt{}, xerr.New(xerr.InvalidArgument, "gift target is incomplete")
}
if _, exists := seen[target.TargetUserID]; exists {
return ledger.BatchGiftReceipt{}, xerr.New(xerr.InvalidArgument, "gift target is duplicated")
}
seen[target.TargetUserID] = struct{}{}
if target.TargetIsHost && target.TargetHostRegionID <= 0 {
// 主播工资入账必须带该目标自己的主播区域,不能复用房间区域或其他目标快照。
return ledger.BatchGiftReceipt{}, xerr.New(xerr.InvalidArgument, "target_host_region_id is required")
}
}
if s == nil || s.repository == nil {
return ledger.BatchGiftReceipt{}, xerr.New(xerr.Unavailable, "wallet repository is not configured")
}
command.AppCode = appcode.Normalize(command.AppCode)
ctx = appcode.WithContext(ctx, command.AppCode)
return s.repository.BatchDebitGift(ctx, command)
}