175 lines
7.5 KiB
Go
175 lines
7.5 KiB
Go
package wallet
|
|
|
|
import (
|
|
"context"
|
|
"sort"
|
|
"strings"
|
|
|
|
"hyapp/pkg/appcode"
|
|
"hyapp/pkg/xerr"
|
|
"hyapp/services/wallet-service/internal/domain/ledger"
|
|
)
|
|
|
|
// GetRedPacketConfig 返回红包配置;未配置时 repository 会返回关闭状态。
|
|
func (s *Service) GetRedPacketConfig(ctx context.Context, appCode string) (ledger.RedPacketConfig, error) {
|
|
if s.repository == nil {
|
|
return ledger.RedPacketConfig{}, xerr.New(xerr.Unavailable, "wallet repository is not configured")
|
|
}
|
|
appCode = appcode.Normalize(appCode)
|
|
ctx = appcode.WithContext(ctx, appCode)
|
|
return s.repository.GetRedPacketConfig(ctx, appCode)
|
|
}
|
|
|
|
// UpdateRedPacketConfig 保存后台红包配置;时间和档位必须在 wallet-service 内强校验。
|
|
func (s *Service) UpdateRedPacketConfig(ctx context.Context, config ledger.RedPacketConfig) (ledger.RedPacketConfig, error) {
|
|
if s.repository == nil {
|
|
return ledger.RedPacketConfig{}, xerr.New(xerr.Unavailable, "wallet repository is not configured")
|
|
}
|
|
config.AppCode = appcode.Normalize(config.AppCode)
|
|
config.CountTiers = normalizeCountTiers(config.CountTiers)
|
|
config.AmountTiers = normalizeAmountTiers(config.AmountTiers)
|
|
config.RuleURL = strings.TrimSpace(config.RuleURL)
|
|
config.ExpireSeconds = ledger.RedPacketExpireSeconds
|
|
if config.UpdatedByAdminID <= 0 {
|
|
return ledger.RedPacketConfig{}, xerr.New(xerr.InvalidArgument, "operator_user_id is required")
|
|
}
|
|
if len(config.CountTiers) == 0 || len(config.AmountTiers) == 0 {
|
|
return ledger.RedPacketConfig{}, xerr.New(xerr.InvalidArgument, "red packet tiers are required")
|
|
}
|
|
if config.DelayedOpenSeconds < 0 || config.DailySendLimit < 0 {
|
|
return ledger.RedPacketConfig{}, xerr.New(xerr.InvalidArgument, "red packet config is invalid")
|
|
}
|
|
if config.DelayedOpenSeconds >= ledger.RedPacketExpireSeconds {
|
|
return ledger.RedPacketConfig{}, xerr.New(xerr.InvalidArgument, "red packet expire_seconds must be greater than delayed_open_seconds")
|
|
}
|
|
if len(config.CountTiers) > 0 && len(config.AmountTiers) > 0 && config.AmountTiers[0] < int64(config.CountTiers[len(config.CountTiers)-1]) {
|
|
return ledger.RedPacketConfig{}, xerr.New(xerr.InvalidArgument, "red packet amount tiers must cover count tiers")
|
|
}
|
|
ctx = appcode.WithContext(ctx, config.AppCode)
|
|
return s.repository.UpdateRedPacketConfig(ctx, config)
|
|
}
|
|
|
|
// CreateRedPacket 发普通或延迟红包;扣款、随机拆分和 outbox 都由 repository 事务完成。
|
|
func (s *Service) CreateRedPacket(ctx context.Context, command ledger.RedPacketCreateCommand) (ledger.RedPacketCreateReceipt, error) {
|
|
if s.repository == nil {
|
|
return ledger.RedPacketCreateReceipt{}, xerr.New(xerr.Unavailable, "wallet repository is not configured")
|
|
}
|
|
command.AppCode = appcode.Normalize(command.AppCode)
|
|
command.CommandID = strings.TrimSpace(command.CommandID)
|
|
command.RoomID = strings.TrimSpace(command.RoomID)
|
|
command.PacketType = ledger.NormalizeRedPacketType(command.PacketType)
|
|
if command.CommandID == "" || command.SenderUserID <= 0 || command.RoomID == "" || command.RegionID <= 0 {
|
|
return ledger.RedPacketCreateReceipt{}, xerr.New(xerr.InvalidArgument, "red packet command is incomplete")
|
|
}
|
|
if command.PacketType == "" {
|
|
return ledger.RedPacketCreateReceipt{}, xerr.New(xerr.InvalidArgument, "red packet type is invalid")
|
|
}
|
|
if command.TotalAmount <= 0 || command.PacketCount <= 0 {
|
|
return ledger.RedPacketCreateReceipt{}, xerr.New(xerr.InvalidArgument, "red packet amount and count are required")
|
|
}
|
|
if command.TotalAmount < int64(command.PacketCount) {
|
|
return ledger.RedPacketCreateReceipt{}, xerr.New(xerr.RedPacketAmountTooSmall, "red packet amount is too small")
|
|
}
|
|
ctx = appcode.WithContext(ctx, command.AppCode)
|
|
return s.repository.CreateRedPacket(ctx, command)
|
|
}
|
|
|
|
// ClaimRedPacket 抢一个红包份额;同一个用户同一红包只允许成功一次。
|
|
func (s *Service) ClaimRedPacket(ctx context.Context, command ledger.RedPacketClaimCommand) (ledger.RedPacketClaimReceipt, error) {
|
|
if s.repository == nil {
|
|
return ledger.RedPacketClaimReceipt{}, xerr.New(xerr.Unavailable, "wallet repository is not configured")
|
|
}
|
|
command.AppCode = appcode.Normalize(command.AppCode)
|
|
command.CommandID = strings.TrimSpace(command.CommandID)
|
|
command.PacketID = strings.TrimSpace(command.PacketID)
|
|
if command.CommandID == "" || command.PacketID == "" || command.UserID <= 0 {
|
|
return ledger.RedPacketClaimReceipt{}, xerr.New(xerr.InvalidArgument, "red packet claim command is incomplete")
|
|
}
|
|
ctx = appcode.WithContext(ctx, command.AppCode)
|
|
return s.repository.ClaimRedPacket(ctx, command)
|
|
}
|
|
|
|
func (s *Service) ListRedPackets(ctx context.Context, query ledger.RedPacketQuery) ([]ledger.RedPacket, int64, error) {
|
|
if s.repository == nil {
|
|
return nil, 0, xerr.New(xerr.Unavailable, "wallet repository is not configured")
|
|
}
|
|
query.AppCode = appcode.Normalize(query.AppCode)
|
|
query.RoomID = strings.TrimSpace(query.RoomID)
|
|
query.PacketType = ledger.NormalizeRedPacketType(query.PacketType)
|
|
query.Status = ledger.NormalizeRedPacketStatus(query.Status)
|
|
if query.Page <= 0 {
|
|
query.Page = 1
|
|
}
|
|
if query.PageSize <= 0 {
|
|
query.PageSize = 20
|
|
}
|
|
if query.PageSize > 100 {
|
|
query.PageSize = 100
|
|
}
|
|
ctx = appcode.WithContext(ctx, query.AppCode)
|
|
return s.repository.ListRedPackets(ctx, query)
|
|
}
|
|
|
|
func (s *Service) GetRedPacket(ctx context.Context, appCode string, packetID string, viewerUserID int64, includeClaims bool) (ledger.RedPacket, error) {
|
|
if s.repository == nil {
|
|
return ledger.RedPacket{}, xerr.New(xerr.Unavailable, "wallet repository is not configured")
|
|
}
|
|
packetID = strings.TrimSpace(packetID)
|
|
if packetID == "" {
|
|
return ledger.RedPacket{}, xerr.New(xerr.InvalidArgument, "packet_id is required")
|
|
}
|
|
appCode = appcode.Normalize(appCode)
|
|
ctx = appcode.WithContext(ctx, appCode)
|
|
return s.repository.GetRedPacket(ctx, appCode, packetID, viewerUserID, includeClaims)
|
|
}
|
|
|
|
func (s *Service) ExpireRedPackets(ctx context.Context, appCode string, batchSize int32) (ledger.RedPacketExpireResult, error) {
|
|
if s.repository == nil {
|
|
return ledger.RedPacketExpireResult{}, xerr.New(xerr.Unavailable, "wallet repository is not configured")
|
|
}
|
|
appCode = appcode.Normalize(appCode)
|
|
ctx = appcode.WithContext(ctx, appCode)
|
|
return s.repository.ExpireRedPackets(ctx, appCode, batchSize)
|
|
}
|
|
|
|
func (s *Service) RetryRedPacketRefund(ctx context.Context, appCode string, packetID string, operatorUserID int64) (ledger.RedPacketRefundReceipt, error) {
|
|
if s.repository == nil {
|
|
return ledger.RedPacketRefundReceipt{}, xerr.New(xerr.Unavailable, "wallet repository is not configured")
|
|
}
|
|
packetID = strings.TrimSpace(packetID)
|
|
if packetID == "" || operatorUserID <= 0 {
|
|
return ledger.RedPacketRefundReceipt{}, xerr.New(xerr.InvalidArgument, "red packet refund retry command is incomplete")
|
|
}
|
|
appCode = appcode.Normalize(appCode)
|
|
ctx = appcode.WithContext(ctx, appCode)
|
|
return s.repository.RetryRedPacketRefund(ctx, appCode, packetID, operatorUserID)
|
|
}
|
|
|
|
func normalizeCountTiers(values []int32) []int32 {
|
|
seen := make(map[int32]bool, len(values))
|
|
out := make([]int32, 0, len(values))
|
|
for _, value := range values {
|
|
if value <= 0 || seen[value] {
|
|
continue
|
|
}
|
|
seen[value] = true
|
|
out = append(out, value)
|
|
}
|
|
sort.Slice(out, func(i, j int) bool { return out[i] < out[j] })
|
|
return out
|
|
}
|
|
|
|
func normalizeAmountTiers(values []int64) []int64 {
|
|
seen := make(map[int64]bool, len(values))
|
|
out := make([]int64, 0, len(values))
|
|
for _, value := range values {
|
|
if value <= 0 || seen[value] {
|
|
continue
|
|
}
|
|
seen[value] = true
|
|
out = append(out, value)
|
|
}
|
|
sort.Slice(out, func(i, j int) bool { return out[i] < out[j] })
|
|
return out
|
|
}
|