95 lines
4.3 KiB
Go
95 lines
4.3 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) {
|
||
roomIDRequired := !command.DirectGift
|
||
if command.CommandID == "" || (roomIDRequired && command.RoomID == "") || command.SenderUserID <= 0 || command.TargetUserID <= 0 || command.GiftID == "" {
|
||
// 房间送礼必须带 RoomID,让 Room Cell、账务分录和房间 outbox 能共享同一个业务边界。
|
||
// 私聊送礼没有房间状态 owner;DirectGift 使用独立 biz_type 和 request hash 隔离幂等,因此允许 RoomID 为空。
|
||
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)
|
||
}
|