2026-06-25 21:46:56 +08:00

95 lines
4.3 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 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 能共享同一个业务边界。
// 私聊送礼没有房间状态 ownerDirectGift 使用独立 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)
}