195 lines
11 KiB
Go
195 lines
11 KiB
Go
package wallet
|
||
|
||
import (
|
||
"context"
|
||
"hyapp/pkg/appcode"
|
||
"hyapp/pkg/xerr"
|
||
"hyapp/services/wallet-service/internal/domain/ledger"
|
||
"strings"
|
||
)
|
||
|
||
// CreditTaskReward 发放任务奖励金币;任务完成判断属于 activity-service,这里只负责账本幂等入账。
|
||
func (s *Service) CreditTaskReward(ctx context.Context, command ledger.TaskRewardCommand) (ledger.TaskRewardReceipt, error) {
|
||
if command.CommandID == "" || command.TargetUserID <= 0 || command.Amount <= 0 || command.TaskID == "" || command.CycleKey == "" {
|
||
return ledger.TaskRewardReceipt{}, xerr.New(xerr.InvalidArgument, "task reward command is incomplete")
|
||
}
|
||
command.TaskType = strings.ToLower(strings.TrimSpace(command.TaskType))
|
||
if command.TaskType != "daily" && command.TaskType != "exclusive" {
|
||
return ledger.TaskRewardReceipt{}, xerr.New(xerr.InvalidArgument, "task_type is invalid")
|
||
}
|
||
command.Reason = strings.TrimSpace(command.Reason)
|
||
if command.Reason == "" {
|
||
return ledger.TaskRewardReceipt{}, xerr.New(xerr.InvalidArgument, "reason is required")
|
||
}
|
||
if s.repository == nil {
|
||
return ledger.TaskRewardReceipt{}, xerr.New(xerr.Unavailable, "wallet repository is not configured")
|
||
}
|
||
command.AppCode = appcode.Normalize(command.AppCode)
|
||
ctx = appcode.WithContext(ctx, command.AppCode)
|
||
|
||
return s.repository.CreditTaskReward(ctx, command)
|
||
}
|
||
|
||
// CreditLuckyGiftReward 发放幸运礼物中奖金币;抽奖资格和金额已由 activity-service 决策,钱包只做幂等入账。
|
||
func (s *Service) CreditLuckyGiftReward(ctx context.Context, command ledger.LuckyGiftRewardCommand) (ledger.LuckyGiftRewardReceipt, error) {
|
||
if command.CommandID == "" || command.TargetUserID <= 0 || command.Amount <= 0 || command.DrawID == "" || command.RoomID == "" || command.GiftID == "" || command.PoolID == "" {
|
||
return ledger.LuckyGiftRewardReceipt{}, xerr.New(xerr.InvalidArgument, "lucky gift reward command is incomplete")
|
||
}
|
||
command.Reason = strings.TrimSpace(command.Reason)
|
||
if command.Reason == "" {
|
||
return ledger.LuckyGiftRewardReceipt{}, xerr.New(xerr.InvalidArgument, "reason is required")
|
||
}
|
||
if s.repository == nil {
|
||
return ledger.LuckyGiftRewardReceipt{}, xerr.New(xerr.Unavailable, "wallet repository is not configured")
|
||
}
|
||
command.AppCode = appcode.Normalize(command.AppCode)
|
||
command.DrawID = strings.TrimSpace(command.DrawID)
|
||
command.RoomID = strings.TrimSpace(command.RoomID)
|
||
command.GiftID = strings.TrimSpace(command.GiftID)
|
||
command.PoolID = strings.TrimSpace(command.PoolID)
|
||
ctx = appcode.WithContext(ctx, command.AppCode)
|
||
|
||
return s.repository.CreditLuckyGiftReward(ctx, command)
|
||
}
|
||
|
||
// CreditWheelReward 发放转盘金币奖品;抽奖、RTP 和奖品命中归 activity-service,钱包只负责独立 reason 的幂等入账。
|
||
func (s *Service) CreditWheelReward(ctx context.Context, command ledger.WheelRewardCommand) (ledger.WheelRewardReceipt, error) {
|
||
if command.CommandID == "" || command.TargetUserID <= 0 || command.Amount <= 0 || command.DrawID == "" || command.WheelID == "" || command.SelectedTierID == "" {
|
||
return ledger.WheelRewardReceipt{}, xerr.New(xerr.InvalidArgument, "wheel reward command is incomplete")
|
||
}
|
||
command.Reason = strings.TrimSpace(command.Reason)
|
||
if command.Reason == "" {
|
||
return ledger.WheelRewardReceipt{}, xerr.New(xerr.InvalidArgument, "reason is required")
|
||
}
|
||
if s.repository == nil {
|
||
return ledger.WheelRewardReceipt{}, xerr.New(xerr.Unavailable, "wallet repository is not configured")
|
||
}
|
||
command.AppCode = appcode.Normalize(command.AppCode)
|
||
command.DrawID = strings.TrimSpace(command.DrawID)
|
||
command.WheelID = strings.TrimSpace(command.WheelID)
|
||
command.SelectedTierID = strings.TrimSpace(command.SelectedTierID)
|
||
ctx = appcode.WithContext(ctx, command.AppCode)
|
||
|
||
return s.repository.CreditWheelReward(ctx, command)
|
||
}
|
||
|
||
// CreditRoomTurnoverReward 发放每周房间流水奖励金币;结算归 activity-service,钱包只负责幂等入账。
|
||
func (s *Service) CreditRoomTurnoverReward(ctx context.Context, command ledger.RoomTurnoverRewardCommand) (ledger.RoomTurnoverRewardReceipt, error) {
|
||
// 钱包只接受 activity-service 已经生成好的结算命令;缺 settlement、room 或正向金额时直接拒绝,避免钱包自己推断活动规则。
|
||
if command.CommandID == "" || command.TargetUserID <= 0 || command.Amount <= 0 || command.SettlementID == "" || command.RoomID == "" {
|
||
return ledger.RoomTurnoverRewardReceipt{}, xerr.New(xerr.InvalidArgument, "room turnover reward command is incomplete")
|
||
}
|
||
// 周期、流水和档位是账务元数据的一部分,必须随交易落库,后续对账才能从钱包交易反查是哪一周、哪个房间、哪个档位。
|
||
if command.PeriodStartMS <= 0 || command.PeriodEndMS <= command.PeriodStartMS || command.CoinSpent <= 0 || command.TierID <= 0 {
|
||
return ledger.RoomTurnoverRewardReceipt{}, xerr.New(xerr.InvalidArgument, "room turnover reward period or tier is invalid")
|
||
}
|
||
command.Reason = strings.TrimSpace(command.Reason)
|
||
if command.Reason == "" {
|
||
return ledger.RoomTurnoverRewardReceipt{}, xerr.New(xerr.InvalidArgument, "reason is required")
|
||
}
|
||
if s.repository == nil {
|
||
return ledger.RoomTurnoverRewardReceipt{}, xerr.New(xerr.Unavailable, "wallet repository is not configured")
|
||
}
|
||
command.AppCode = appcode.Normalize(command.AppCode)
|
||
command.SettlementID = strings.TrimSpace(command.SettlementID)
|
||
command.RoomID = strings.TrimSpace(command.RoomID)
|
||
command.TierCode = strings.TrimSpace(command.TierCode)
|
||
// AppCode 归一化后写入 context,让 repository 的交易、账户和 outbox 都落到同一个 App 分区。
|
||
ctx = appcode.WithContext(ctx, command.AppCode)
|
||
|
||
return s.repository.CreditRoomTurnoverReward(ctx, command)
|
||
}
|
||
|
||
// CreditInviteActivityReward 发放邀请活动金币奖励;达标和重复领取判断属于 activity-service,钱包只负责幂等入账。
|
||
func (s *Service) CreditInviteActivityReward(ctx context.Context, command ledger.InviteActivityRewardCommand) (ledger.InviteActivityRewardReceipt, error) {
|
||
// 钱包入口再次校验最小账务语义,避免上游 bug 生成空 claim 或 0 金额交易。
|
||
if command.CommandID == "" || command.TargetUserID <= 0 || command.Amount <= 0 || command.ClaimID == "" || command.TierID <= 0 || command.CycleKey == "" {
|
||
return ledger.InviteActivityRewardReceipt{}, xerr.New(xerr.InvalidArgument, "invite activity reward command is incomplete")
|
||
}
|
||
command.RewardType = strings.ToLower(strings.TrimSpace(command.RewardType))
|
||
if command.RewardType != "recharge" && command.RewardType != "valid_invite" && command.RewardType != "invite_inviter" && command.RewardType != "invite_invitee" {
|
||
return ledger.InviteActivityRewardReceipt{}, xerr.New(xerr.InvalidArgument, "invite activity reward_type is invalid")
|
||
}
|
||
command.Reason = strings.TrimSpace(command.Reason)
|
||
if command.Reason == "" {
|
||
return ledger.InviteActivityRewardReceipt{}, xerr.New(xerr.InvalidArgument, "reason is required")
|
||
}
|
||
if s.repository == nil {
|
||
return ledger.InviteActivityRewardReceipt{}, xerr.New(xerr.Unavailable, "wallet repository is not configured")
|
||
}
|
||
command.AppCode = appcode.Normalize(command.AppCode)
|
||
command.ClaimID = strings.TrimSpace(command.ClaimID)
|
||
command.TierCode = strings.TrimSpace(command.TierCode)
|
||
command.CycleKey = strings.TrimSpace(command.CycleKey)
|
||
ctx = appcode.WithContext(ctx, command.AppCode)
|
||
|
||
return s.repository.CreditInviteActivityReward(ctx, command)
|
||
}
|
||
|
||
// CreditAgencyOpeningReward 发放代理开业流水档位金币奖励;命中的档位序号和金额由 activity-service 固化,钱包只做幂等入账。
|
||
func (s *Service) CreditAgencyOpeningReward(ctx context.Context, command ledger.AgencyOpeningRewardCommand) (ledger.AgencyOpeningRewardReceipt, error) {
|
||
if command.CommandID == "" || command.TargetUserID <= 0 || command.Amount <= 0 || command.ApplicationID == "" || command.CycleID == "" || command.AgencyID <= 0 || command.RankNo <= 0 {
|
||
return ledger.AgencyOpeningRewardReceipt{}, xerr.New(xerr.InvalidArgument, "agency opening reward command is incomplete")
|
||
}
|
||
if command.ScoreCoins <= 0 {
|
||
return ledger.AgencyOpeningRewardReceipt{}, xerr.New(xerr.InvalidArgument, "agency opening score is required")
|
||
}
|
||
command.Reason = strings.TrimSpace(command.Reason)
|
||
if command.Reason == "" {
|
||
return ledger.AgencyOpeningRewardReceipt{}, xerr.New(xerr.InvalidArgument, "reason is required")
|
||
}
|
||
if s.repository == nil {
|
||
return ledger.AgencyOpeningRewardReceipt{}, xerr.New(xerr.Unavailable, "wallet repository is not configured")
|
||
}
|
||
command.AppCode = appcode.Normalize(command.AppCode)
|
||
command.ApplicationID = strings.TrimSpace(command.ApplicationID)
|
||
command.CycleID = strings.TrimSpace(command.CycleID)
|
||
ctx = appcode.WithContext(ctx, command.AppCode)
|
||
|
||
return s.repository.CreditAgencyOpeningReward(ctx, command)
|
||
}
|
||
|
||
// DebitCPBreakupFee 扣除解除关系费用;关系是否可解除仍由 user-service 的 pending/confirm 流程决定。
|
||
func (s *Service) DebitCPBreakupFee(ctx context.Context, command ledger.CPBreakupFeeCommand) (ledger.CPBreakupFeeReceipt, error) {
|
||
if command.CommandID == "" || command.UserID <= 0 || strings.TrimSpace(command.RelationshipID) == "" || strings.TrimSpace(command.RelationType) == "" {
|
||
return ledger.CPBreakupFeeReceipt{}, xerr.New(xerr.InvalidArgument, "cp breakup fee command is incomplete")
|
||
}
|
||
if len(command.CommandID) > 128 {
|
||
return ledger.CPBreakupFeeReceipt{}, xerr.New(xerr.InvalidArgument, "command_id is too long")
|
||
}
|
||
if command.Amount <= 0 {
|
||
return ledger.CPBreakupFeeReceipt{}, xerr.New(xerr.InvalidArgument, "cp breakup fee amount must be positive")
|
||
}
|
||
if s.repository == nil {
|
||
return ledger.CPBreakupFeeReceipt{}, xerr.New(xerr.Unavailable, "wallet repository is not configured")
|
||
}
|
||
command.AppCode = appcode.Normalize(command.AppCode)
|
||
command.RelationshipID = strings.TrimSpace(command.RelationshipID)
|
||
command.RelationType = strings.ToLower(strings.TrimSpace(command.RelationType))
|
||
ctx = appcode.WithContext(ctx, command.AppCode)
|
||
return s.repository.DebitCPBreakupFee(ctx, command)
|
||
}
|
||
|
||
// DebitWheelDraw 扣除转盘抽奖消耗金币;抽奖命中必须在 activity-service 完成,wallet 只提供可幂等复用的扣费事实。
|
||
func (s *Service) DebitWheelDraw(ctx context.Context, command ledger.WheelDrawDebitCommand) (ledger.WheelDrawDebitReceipt, error) {
|
||
if command.CommandID == "" || command.UserID <= 0 || strings.TrimSpace(command.WheelID) == "" {
|
||
return ledger.WheelDrawDebitReceipt{}, xerr.New(xerr.InvalidArgument, "wheel draw debit command is incomplete")
|
||
}
|
||
if len(command.CommandID) > 128 {
|
||
return ledger.WheelDrawDebitReceipt{}, xerr.New(xerr.InvalidArgument, "command_id is too long")
|
||
}
|
||
if command.DrawCount <= 0 {
|
||
return ledger.WheelDrawDebitReceipt{}, xerr.New(xerr.InvalidArgument, "draw_count must be positive")
|
||
}
|
||
if command.Amount <= 0 {
|
||
return ledger.WheelDrawDebitReceipt{}, xerr.New(xerr.InvalidArgument, "wheel draw debit amount must be positive")
|
||
}
|
||
if s.repository == nil {
|
||
return ledger.WheelDrawDebitReceipt{}, xerr.New(xerr.Unavailable, "wallet repository is not configured")
|
||
}
|
||
command.AppCode = appcode.Normalize(command.AppCode)
|
||
command.WheelID = strings.TrimSpace(command.WheelID)
|
||
ctx = appcode.WithContext(ctx, command.AppCode)
|
||
return s.repository.DebitWheelDraw(ctx, command)
|
||
}
|