87 lines
3.4 KiB
Go
87 lines
3.4 KiB
Go
package wallet
|
|
|
|
import (
|
|
"context"
|
|
"strings"
|
|
|
|
"hyapp/pkg/xerr"
|
|
"hyapp/services/wallet-service/internal/domain/ledger"
|
|
)
|
|
|
|
// Repository 隔离账务存储,扣费必须由 repository 保证余额和幂等。
|
|
type Repository interface {
|
|
DebitGift(ctx context.Context, command ledger.DebitGiftCommand) (ledger.Receipt, error)
|
|
GetBalances(ctx context.Context, userID int64, assetTypes []string) ([]ledger.AssetBalance, error)
|
|
AdminCreditAsset(ctx context.Context, command ledger.AdminCreditAssetCommand) (ledger.AssetBalance, string, error)
|
|
}
|
|
|
|
// Service 承载钱包账务用例。
|
|
type Service struct {
|
|
repository Repository
|
|
}
|
|
|
|
// New 创建钱包服务。
|
|
func New(repository Repository) *Service {
|
|
return &Service{repository: repository}
|
|
}
|
|
|
|
// DebitGift 校验送礼扣费命令,并交给 repository 做原子落账和幂等。
|
|
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.SenderUserID == command.TargetUserID {
|
|
return ledger.Receipt{}, xerr.New(xerr.InvalidArgument, "sender and target must be different")
|
|
}
|
|
if command.GiftCount <= 0 {
|
|
return ledger.Receipt{}, xerr.New(xerr.InvalidArgument, "gift_count must be positive")
|
|
}
|
|
if s.repository == nil {
|
|
return ledger.Receipt{}, xerr.New(xerr.Unavailable, "wallet repository is not configured")
|
|
}
|
|
|
|
return s.repository.DebitGift(ctx, command)
|
|
}
|
|
|
|
// GetBalances 返回用户多资产余额;鉴权范围由 gateway 或后台入口控制。
|
|
func (s *Service) GetBalances(ctx context.Context, userID int64, assetTypes []string) ([]ledger.AssetBalance, error) {
|
|
if userID <= 0 {
|
|
return nil, xerr.New(xerr.InvalidArgument, "user_id is required")
|
|
}
|
|
if s.repository == nil {
|
|
return nil, xerr.New(xerr.Unavailable, "wallet repository is not configured")
|
|
}
|
|
normalized := make([]string, 0, len(assetTypes))
|
|
for _, assetType := range assetTypes {
|
|
assetType = strings.ToUpper(strings.TrimSpace(assetType))
|
|
if assetType == "" {
|
|
continue
|
|
}
|
|
if !ledger.ValidAssetType(assetType) {
|
|
return nil, xerr.New(xerr.InvalidArgument, "asset_type is invalid")
|
|
}
|
|
normalized = append(normalized, assetType)
|
|
}
|
|
|
|
return s.repository.GetBalances(ctx, userID, normalized)
|
|
}
|
|
|
|
// AdminCreditAsset 执行后台手动入账;所有审计字段必须随交易一起落库。
|
|
func (s *Service) AdminCreditAsset(ctx context.Context, command ledger.AdminCreditAssetCommand) (ledger.AssetBalance, string, error) {
|
|
if command.CommandID == "" || command.TargetUserID <= 0 || command.OperatorUserID <= 0 || command.Amount <= 0 {
|
|
return ledger.AssetBalance{}, "", xerr.New(xerr.InvalidArgument, "admin credit command is incomplete")
|
|
}
|
|
command.AssetType = strings.ToUpper(strings.TrimSpace(command.AssetType))
|
|
if !ledger.ValidAssetType(command.AssetType) {
|
|
return ledger.AssetBalance{}, "", xerr.New(xerr.InvalidArgument, "asset_type is invalid")
|
|
}
|
|
if command.Reason == "" || command.EvidenceRef == "" {
|
|
return ledger.AssetBalance{}, "", xerr.New(xerr.InvalidArgument, "reason and evidence_ref are required")
|
|
}
|
|
if s.repository == nil {
|
|
return ledger.AssetBalance{}, "", xerr.New(xerr.Unavailable, "wallet repository is not configured")
|
|
}
|
|
|
|
return s.repository.AdminCreditAsset(ctx, command)
|
|
}
|