This commit is contained in:
2025-07-28 01:46:39 +08:00
parent b03129667a
commit 357639462a
219 changed files with 21634 additions and 8138 deletions

View File

@@ -0,0 +1,140 @@
package entities
import (
"time"
"github.com/google/uuid"
"github.com/shopspring/decimal"
"gorm.io/gorm"
)
// AlipayOrderStatus 支付宝订单状态枚举
type AlipayOrderStatus string
const (
AlipayOrderStatusPending AlipayOrderStatus = "pending" // 待支付
AlipayOrderStatusSuccess AlipayOrderStatus = "success" // 支付成功
AlipayOrderStatusFailed AlipayOrderStatus = "failed" // 支付失败
AlipayOrderStatusCancelled AlipayOrderStatus = "cancelled" // 已取消
AlipayOrderStatusClosed AlipayOrderStatus = "closed" // 已关闭
)
const (
AlipayOrderPlatformApp = "app" // 支付宝APP支付
AlipayOrderPlatformH5 = "h5" // 支付宝H5支付
AlipayOrderPlatformPC = "pc" // 支付宝PC支付
)
// AlipayOrder 支付宝订单详情实体
type AlipayOrder struct {
// 基础标识
ID string `gorm:"primaryKey;type:varchar(36)" json:"id" comment:"支付宝订单唯一标识"`
RechargeID string `gorm:"type:varchar(36);not null;uniqueIndex" json:"recharge_id" comment:"关联充值记录ID"`
OutTradeNo string `gorm:"type:varchar(64);not null;uniqueIndex" json:"out_trade_no" comment:"商户订单号"`
TradeNo *string `gorm:"type:varchar(64);uniqueIndex" json:"trade_no,omitempty" comment:"支付宝交易号"`
// 订单信息
Subject string `gorm:"type:varchar(200);not null" json:"subject" comment:"订单标题"`
Amount decimal.Decimal `gorm:"type:decimal(20,8);not null" json:"amount" comment:"订单金额"`
Platform string `gorm:"type:varchar(20);not null" json:"platform" comment:"支付平台app/h5/pc"`
Status AlipayOrderStatus `gorm:"type:varchar(20);not null;default:'pending';index" json:"status" comment:"订单状态"`
// 支付宝返回信息
BuyerID string `gorm:"type:varchar(64)" json:"buyer_id,omitempty" comment:"买家支付宝用户ID"`
SellerID string `gorm:"type:varchar(64)" json:"seller_id,omitempty" comment:"卖家支付宝用户ID"`
PayAmount decimal.Decimal `gorm:"type:decimal(20,8)" json:"pay_amount,omitempty" comment:"实际支付金额"`
ReceiptAmount decimal.Decimal `gorm:"type:decimal(20,8)" json:"receipt_amount,omitempty" comment:"实收金额"`
// 回调信息
NotifyTime *time.Time `gorm:"index" json:"notify_time,omitempty" comment:"异步通知时间"`
ReturnTime *time.Time `gorm:"index" json:"return_time,omitempty" comment:"同步返回时间"`
// 错误信息
ErrorCode string `gorm:"type:varchar(64)" json:"error_code,omitempty" comment:"错误码"`
ErrorMessage string `gorm:"type:text" json:"error_message,omitempty" comment:"错误信息"`
// 时间戳字段
CreatedAt time.Time `gorm:"autoCreateTime" json:"created_at" comment:"创建时间"`
UpdatedAt time.Time `gorm:"autoUpdateTime" json:"updated_at" comment:"更新时间"`
DeletedAt gorm.DeletedAt `gorm:"index" json:"-" comment:"软删除时间"`
}
// TableName 指定数据库表名
func (AlipayOrder) TableName() string {
return "alipay_orders"
}
// BeforeCreate GORM钩子创建前自动生成UUID
func (a *AlipayOrder) BeforeCreate(tx *gorm.DB) error {
if a.ID == "" {
a.ID = uuid.New().String()
}
return nil
}
// IsPending 检查是否为待支付状态
func (a *AlipayOrder) IsPending() bool {
return a.Status == AlipayOrderStatusPending
}
// IsSuccess 检查是否为支付成功状态
func (a *AlipayOrder) IsSuccess() bool {
return a.Status == AlipayOrderStatusSuccess
}
// IsFailed 检查是否为支付失败状态
func (a *AlipayOrder) IsFailed() bool {
return a.Status == AlipayOrderStatusFailed
}
// IsCancelled 检查是否为已取消状态
func (a *AlipayOrder) IsCancelled() bool {
return a.Status == AlipayOrderStatusCancelled
}
// IsClosed 检查是否为已关闭状态
func (a *AlipayOrder) IsClosed() bool {
return a.Status == AlipayOrderStatusClosed
}
// MarkSuccess 标记为支付成功
func (a *AlipayOrder) MarkSuccess(tradeNo, buyerID, sellerID string, payAmount, receiptAmount decimal.Decimal) {
a.Status = AlipayOrderStatusSuccess
a.TradeNo = &tradeNo
a.BuyerID = buyerID
a.SellerID = sellerID
a.PayAmount = payAmount
a.ReceiptAmount = receiptAmount
now := time.Now()
a.NotifyTime = &now
}
// MarkFailed 标记为支付失败
func (a *AlipayOrder) MarkFailed(errorCode, errorMessage string) {
a.Status = AlipayOrderStatusFailed
a.ErrorCode = errorCode
a.ErrorMessage = errorMessage
}
// MarkCancelled 标记为已取消
func (a *AlipayOrder) MarkCancelled() {
a.Status = AlipayOrderStatusCancelled
}
// MarkClosed 标记为已关闭
func (a *AlipayOrder) MarkClosed() {
a.Status = AlipayOrderStatusClosed
}
// NewAlipayOrder 工厂方法 - 创建支付宝订单
func NewAlipayOrder(rechargeID, outTradeNo, subject string, amount decimal.Decimal, platform string) *AlipayOrder {
return &AlipayOrder{
ID: uuid.New().String(),
RechargeID: rechargeID,
OutTradeNo: outTradeNo,
Subject: subject,
Amount: amount,
Platform: platform,
Status: AlipayOrderStatusPending,
}
}

View File

@@ -0,0 +1,177 @@
package entities
import (
"errors"
"time"
"github.com/google/uuid"
"github.com/shopspring/decimal"
"gorm.io/gorm"
)
// RechargeType 充值类型枚举
type RechargeType string
const (
RechargeTypeAlipay RechargeType = "alipay" // 支付宝充值
RechargeTypeTransfer RechargeType = "transfer" // 对公转账
RechargeTypeGift RechargeType = "gift" // 赠送
)
// RechargeStatus 充值状态枚举
type RechargeStatus string
const (
RechargeStatusPending RechargeStatus = "pending" // 待处理
RechargeStatusSuccess RechargeStatus = "success" // 成功
RechargeStatusFailed RechargeStatus = "failed" // 失败
RechargeStatusCancelled RechargeStatus = "cancelled" // 已取消
)
// RechargeRecord 充值记录实体
// 记录用户的各种充值操作,包括支付宝充值、对公转账、赠送等
type RechargeRecord struct {
// 基础标识
ID string `gorm:"primaryKey;type:varchar(36)" json:"id" comment:"充值记录唯一标识"`
UserID string `gorm:"type:varchar(36);not null;index" json:"user_id" comment:"充值用户ID"`
// 充值信息
Amount decimal.Decimal `gorm:"type:decimal(20,8);not null" json:"amount" comment:"充值金额"`
RechargeType RechargeType `gorm:"type:varchar(20);not null;index" json:"recharge_type" comment:"充值类型"`
Status RechargeStatus `gorm:"type:varchar(20);not null;default:'pending';index" json:"status" comment:"充值状态"`
// 订单号字段(根据充值类型使用不同字段)
AlipayOrderID *string `gorm:"type:varchar(64);uniqueIndex" json:"alipay_order_id,omitempty" comment:"支付宝订单号"`
TransferOrderID *string `gorm:"type:varchar(64);uniqueIndex" json:"transfer_order_id,omitempty" comment:"转账订单号"`
// 通用字段
Notes string `gorm:"type:varchar(500)" json:"notes,omitempty" comment:"备注信息"`
// 时间戳字段
CreatedAt time.Time `gorm:"autoCreateTime" json:"created_at" comment:"创建时间"`
UpdatedAt time.Time `gorm:"autoUpdateTime" json:"updated_at" comment:"更新时间"`
DeletedAt gorm.DeletedAt `gorm:"index" json:"-" comment:"软删除时间"`
}
// TableName 指定数据库表名
func (RechargeRecord) TableName() string {
return "recharge_records"
}
// BeforeCreate GORM钩子创建前自动生成UUID
func (r *RechargeRecord) BeforeCreate(tx *gorm.DB) error {
if r.ID == "" {
r.ID = uuid.New().String()
}
return nil
}
// IsPending 检查是否为待处理状态
func (r *RechargeRecord) IsPending() bool {
return r.Status == RechargeStatusPending
}
// IsSuccess 检查是否为成功状态
func (r *RechargeRecord) IsSuccess() bool {
return r.Status == RechargeStatusSuccess
}
// IsFailed 检查是否为失败状态
func (r *RechargeRecord) IsFailed() bool {
return r.Status == RechargeStatusFailed
}
// IsCancelled 检查是否为已取消状态
func (r *RechargeRecord) IsCancelled() bool {
return r.Status == RechargeStatusCancelled
}
// MarkSuccess 标记为成功
func (r *RechargeRecord) MarkSuccess() {
r.Status = RechargeStatusSuccess
}
// MarkFailed 标记为失败
func (r *RechargeRecord) MarkFailed() {
r.Status = RechargeStatusFailed
}
// MarkCancelled 标记为已取消
func (r *RechargeRecord) MarkCancelled() {
r.Status = RechargeStatusCancelled
}
// ValidatePaymentMethod 验证支付方式:支付宝订单号和转账订单号只能有一个存在
func (r *RechargeRecord) ValidatePaymentMethod() error {
hasAlipay := r.AlipayOrderID != nil && *r.AlipayOrderID != ""
hasTransfer := r.TransferOrderID != nil && *r.TransferOrderID != ""
if hasAlipay && hasTransfer {
return errors.New("支付宝订单号和转账订单号不能同时存在")
}
if !hasAlipay && !hasTransfer {
return errors.New("必须提供支付宝订单号或转账订单号")
}
return nil
}
// GetOrderID 获取订单号(根据充值类型返回对应的订单号)
func (r *RechargeRecord) GetOrderID() string {
switch r.RechargeType {
case RechargeTypeAlipay:
if r.AlipayOrderID != nil {
return *r.AlipayOrderID
}
case RechargeTypeTransfer:
if r.TransferOrderID != nil {
return *r.TransferOrderID
}
}
return ""
}
// SetAlipayOrderID 设置支付宝订单号
func (r *RechargeRecord) SetAlipayOrderID(orderID string) {
r.AlipayOrderID = &orderID
}
// SetTransferOrderID 设置转账订单号
func (r *RechargeRecord) SetTransferOrderID(orderID string) {
r.TransferOrderID = &orderID
}
// NewAlipayRechargeRecord 工厂方法 - 创建支付宝充值记录
func NewAlipayRechargeRecord(userID string, amount decimal.Decimal, alipayOrderID string) *RechargeRecord {
return &RechargeRecord{
UserID: userID,
Amount: amount,
RechargeType: RechargeTypeAlipay,
Status: RechargeStatusPending,
AlipayOrderID: &alipayOrderID,
}
}
// NewTransferRechargeRecord 工厂方法 - 创建对公转账充值记录
func NewTransferRechargeRecord(userID string, amount decimal.Decimal, transferOrderID, notes string) *RechargeRecord {
return &RechargeRecord{
UserID: userID,
Amount: amount,
RechargeType: RechargeTypeTransfer,
Status: RechargeStatusPending,
TransferOrderID: &transferOrderID,
Notes: notes,
}
}
// NewGiftRechargeRecord 工厂方法 - 创建赠送充值记录
func NewGiftRechargeRecord(userID string, amount decimal.Decimal, notes string) *RechargeRecord {
return &RechargeRecord{
UserID: userID,
Amount: amount,
RechargeType: RechargeTypeGift,
Status: RechargeStatusSuccess, // 赠送直接标记为成功
Notes: notes,
}
}

View File

@@ -1,76 +0,0 @@
package entities
import (
"time"
"github.com/google/uuid"
"gorm.io/gorm"
)
// UserSecrets 用户密钥实体
// 存储用户的API访问密钥信息用于第三方服务集成和API调用
// 支持密钥的生命周期管理,包括激活状态、过期时间、使用统计等
type UserSecrets struct {
// 基础标识
ID string `gorm:"primaryKey;type:varchar(36)" comment:"密钥记录唯一标识"`
UserID string `gorm:"type:varchar(36);not null;uniqueIndex" comment:"关联用户ID"`
AccessID string `gorm:"type:varchar(100);not null;uniqueIndex" comment:"访问ID(用于API认证)"`
AccessKey string `gorm:"type:varchar(255);not null" comment:"访问密钥(加密存储)"`
// 密钥状态 - 密钥的生命周期管理
IsActive bool `gorm:"default:true" comment:"密钥是否激活"`
LastUsedAt *time.Time `comment:"最后使用时间"`
ExpiresAt *time.Time `comment:"密钥过期时间"`
// 时间戳字段
CreatedAt time.Time `gorm:"autoCreateTime" comment:"创建时间"`
UpdatedAt time.Time `gorm:"autoUpdateTime" comment:"更新时间"`
DeletedAt gorm.DeletedAt `gorm:"index" comment:"软删除时间"`
}
// TableName 指定数据库表名
func (UserSecrets) TableName() string {
return "user_secrets"
}
// IsExpired 检查密钥是否已过期
// 判断密钥是否超过有效期,过期后需要重新生成或续期
func (u *UserSecrets) IsExpired() bool {
if u.ExpiresAt == nil {
return false // 没有过期时间表示永不过期
}
return time.Now().After(*u.ExpiresAt)
}
// IsValid 检查密钥是否有效
// 综合判断密钥是否可用,包括激活状态和过期状态检查
func (u *UserSecrets) IsValid() bool {
return u.IsActive && !u.IsExpired()
}
// UpdateLastUsedAt 更新最后使用时间
// 在密钥被使用时调用,记录最新的使用时间,用于使用统计和监控
func (u *UserSecrets) UpdateLastUsedAt() {
now := time.Now()
u.LastUsedAt = &now
}
// Deactivate 停用密钥
// 将密钥设置为非激活状态禁止使用该密钥进行API调用
func (u *UserSecrets) Deactivate() {
u.IsActive = false
}
// Activate 激活密钥
// 重新启用密钥允许使用该密钥进行API调用
func (u *UserSecrets) Activate() {
u.IsActive = true
}
// BeforeCreate GORM钩子创建前自动生成UUID
func (u *UserSecrets) BeforeCreate(tx *gorm.DB) error {
if u.ID == "" {
u.ID = uuid.New().String()
}
return nil
}

View File

@@ -9,9 +9,12 @@ import (
"gorm.io/gorm"
)
// Wallet 钱包实体
// Wallet 钱包聚合根
// 用户数字钱包的核心信息,支持多种钱包类型和精确的余额管理
// 使用decimal类型确保金额计算的精确性避免浮点数精度问题
// 支持欠费(余额<0但只允许扣到小于0一次之后不能再扣
// 新建钱包时可配置默认额度
type Wallet struct {
// 基础标识
ID string `gorm:"primaryKey;type:varchar(36)" json:"id" comment:"钱包唯一标识"`
@@ -20,10 +23,7 @@ type Wallet struct {
// 钱包状态 - 钱包的基本状态信息
IsActive bool `gorm:"default:true" json:"is_active" comment:"钱包是否激活"`
Balance decimal.Decimal `gorm:"type:decimal(20,8);default:0" json:"balance" comment:"钱包余额(精确到8位小数)"`
// 钱包信息 - 钱包的详细配置信息
WalletAddress string `gorm:"type:varchar(255)" json:"wallet_address,omitempty" comment:"钱包地址"`
WalletType string `gorm:"type:varchar(50);default:'MAIN'" json:"wallet_type" comment:"钱包类型(MAIN/DEPOSIT/WITHDRAWAL)"` // MAIN, DEPOSIT, WITHDRAWAL
Version int64 `gorm:"version" json:"version" comment:"乐观锁版本号"`
// 时间戳字段
CreatedAt time.Time `gorm:"autoCreateTime" json:"created_at" comment:"创建时间"`
@@ -37,36 +37,53 @@ func (Wallet) TableName() string {
}
// IsZeroBalance 检查余额是否为零
// 判断钱包余额是否为零,用于业务逻辑判断
func (w *Wallet) IsZeroBalance() bool {
return w.Balance.IsZero()
}
// HasSufficientBalance 检查是否有足够余额
// 判断钱包余额是否足够支付指定金额,用于交易前的余额验证
// HasSufficientBalance 检查是否有足够余额(允许透支额度)
func (w *Wallet) HasSufficientBalance(amount decimal.Decimal) bool {
return w.Balance.GreaterThanOrEqual(amount)
// 允许扣到额度下限
return w.Balance.Sub(amount).GreaterThanOrEqual(decimal.Zero)
}
// AddBalance 增加余额
// 向钱包增加指定金额,用于充值、收入等场景
// IsArrears 是否欠费(余额<0
func (w *Wallet) IsArrears() bool {
return w.Balance.LessThan(decimal.Zero)
}
// IsLowBalance 是否余额较低(余额<300
func (w *Wallet) IsLowBalance() bool {
return w.Balance.LessThan(decimal.NewFromInt(300))
}
// GetBalanceStatus 获取余额状态
func (w *Wallet) GetBalanceStatus() string {
if w.IsArrears() {
return "arrears" // 欠费
} else if w.IsLowBalance() {
return "low" // 余额较低
} else {
return "normal" // 正常
}
}
// AddBalance 增加余额(只做加法,业务规则由服务层控制是否允许充值)
func (w *Wallet) AddBalance(amount decimal.Decimal) {
w.Balance = w.Balance.Add(amount)
}
// SubtractBalance 减少余额
// 从钱包扣除指定金额,用于消费、转账等场景
// 如果余额不足会返回错误,确保资金安全
// SubtractBalance 扣减余额,含欠费业务规则
func (w *Wallet) SubtractBalance(amount decimal.Decimal) error {
if !w.HasSufficientBalance(amount) {
return fmt.Errorf("余额不足")
if w.Balance.LessThan(decimal.Zero) {
return fmt.Errorf("已欠费,不能再扣款")
}
w.Balance = w.Balance.Sub(amount)
newBalance := w.Balance.Sub(amount)
w.Balance = newBalance
return nil
}
// GetFormattedBalance 获取格式化的余额字符串
// 将decimal类型的余额转换为字符串格式便于显示和传输
func (w *Wallet) GetFormattedBalance() string {
return w.Balance.String()
}
@@ -78,3 +95,13 @@ func (w *Wallet) BeforeCreate(tx *gorm.DB) error {
}
return nil
}
// NewWallet 工厂方法
func NewWallet(userID string, defaultCreditLimit decimal.Decimal) *Wallet {
return &Wallet{
UserID: userID,
IsActive: true,
Balance: defaultCreditLimit,
Version: 0,
}
}

View File

@@ -0,0 +1,52 @@
package entities
import (
"time"
"github.com/google/uuid"
"github.com/shopspring/decimal"
"gorm.io/gorm"
)
// WalletTransaction 钱包扣款记录
// 记录API调用产生的扣款操作
type WalletTransaction struct {
// 基础标识
ID string `gorm:"primaryKey;type:varchar(36)" json:"id" comment:"交易记录唯一标识"`
UserID string `gorm:"type:varchar(36);not null;index" json:"user_id" comment:"扣款用户ID"`
ApiCallID string `gorm:"type:varchar(64);not null;uniqueIndex" json:"api_call_id" comment:"关联API调用ID"`
TransactionID string `gorm:"type:varchar(64);not null;uniqueIndex" json:"transaction_id" comment:"交易ID"`
ProductID string `gorm:"type:varchar(64);not null;index" json:"product_id" comment:"产品ID"`
// 扣款信息
Amount decimal.Decimal `gorm:"type:decimal(20,8);not null" json:"amount" comment:"扣款金额"`
// 时间戳字段
CreatedAt time.Time `gorm:"autoCreateTime" json:"created_at" comment:"创建时间"`
UpdatedAt time.Time `gorm:"autoUpdateTime" json:"updated_at" comment:"更新时间"`
DeletedAt gorm.DeletedAt `gorm:"index" json:"-" comment:"软删除时间"`
}
// TableName 指定数据库表名
func (WalletTransaction) TableName() string {
return "wallet_transactions"
}
// BeforeCreate GORM钩子创建前自动生成UUID
func (t *WalletTransaction) BeforeCreate(tx *gorm.DB) error {
if t.ID == "" {
t.ID = uuid.New().String()
}
return nil
}
// NewWalletTransaction 工厂方法 - 创建扣款记录
func NewWalletTransaction(userID, apiCallID, transactionID, productID string, amount decimal.Decimal) *WalletTransaction {
return &WalletTransaction{
UserID: userID,
ApiCallID: apiCallID,
TransactionID: transactionID,
ProductID: productID,
Amount: amount,
}
}

View File

@@ -0,0 +1,20 @@
package repositories
import (
"context"
"tyapi-server/internal/domains/finance/entities"
)
// AlipayOrderRepository 支付宝订单仓储接口
type AlipayOrderRepository interface {
Create(ctx context.Context, order entities.AlipayOrder) (entities.AlipayOrder, error)
GetByID(ctx context.Context, id string) (entities.AlipayOrder, error)
GetByOutTradeNo(ctx context.Context, outTradeNo string) (*entities.AlipayOrder, error)
GetByRechargeID(ctx context.Context, rechargeID string) (*entities.AlipayOrder, error)
GetByUserID(ctx context.Context, userID string) ([]entities.AlipayOrder, error)
Update(ctx context.Context, order entities.AlipayOrder) error
UpdateStatus(ctx context.Context, id string, status entities.AlipayOrderStatus) error
Delete(ctx context.Context, id string) error
Exists(ctx context.Context, id string) (bool, error)
}

View File

@@ -1,57 +0,0 @@
package repositories
import (
"context"
"tyapi-server/internal/domains/finance/entities"
"tyapi-server/internal/domains/finance/repositories/queries"
"tyapi-server/internal/shared/interfaces"
)
// FinanceStats 财务统计信息
type FinanceStats struct {
TotalWallets int64
ActiveWallets int64
TotalBalance string
TodayTransactions int64
}
// WalletRepository 钱包仓储接口
type WalletRepository interface {
interfaces.Repository[entities.Wallet]
// 基础查询 - 直接使用实体
GetByUserID(ctx context.Context, userID string) (*entities.Wallet, error)
GetByWalletAddress(ctx context.Context, walletAddress string) (*entities.Wallet, error)
GetByWalletType(ctx context.Context, userID string, walletType string) (*entities.Wallet, error)
// 复杂查询 - 使用查询参数
ListWallets(ctx context.Context, query *queries.ListWalletsQuery) ([]*entities.Wallet, int64, error)
// 业务操作
UpdateBalance(ctx context.Context, walletID string, balance string) error
AddBalance(ctx context.Context, walletID string, amount string) error
SubtractBalance(ctx context.Context, walletID string, amount string) error
ActivateWallet(ctx context.Context, walletID string) error
DeactivateWallet(ctx context.Context, walletID string) error
// 统计信息
GetStats(ctx context.Context) (*FinanceStats, error)
GetUserWalletStats(ctx context.Context, userID string) (*FinanceStats, error)
}
// UserSecretsRepository 用户密钥仓储接口
type UserSecretsRepository interface {
interfaces.Repository[entities.UserSecrets]
// 基础查询 - 直接使用实体
GetByUserID(ctx context.Context, userID string) (*entities.UserSecrets, error)
GetBySecretType(ctx context.Context, userID string, secretType string) (*entities.UserSecrets, error)
// 复杂查询 - 使用查询参数
ListUserSecrets(ctx context.Context, query *queries.ListUserSecretsQuery) ([]*entities.UserSecrets, int64, error)
// 业务操作
UpdateSecret(ctx context.Context, userID string, secretType string, secretValue string) error
DeleteSecret(ctx context.Context, userID string, secretType string) error
ValidateSecret(ctx context.Context, userID string, secretType string, secretValue string) (bool, error)
}

View File

@@ -0,0 +1,23 @@
package repositories
import (
"context"
"tyapi-server/internal/domains/finance/entities"
"tyapi-server/internal/shared/interfaces"
)
// RechargeRecordRepository 充值记录仓储接口
type RechargeRecordRepository interface {
Create(ctx context.Context, record entities.RechargeRecord) (entities.RechargeRecord, error)
GetByID(ctx context.Context, id string) (entities.RechargeRecord, error)
GetByUserID(ctx context.Context, userID string) ([]entities.RechargeRecord, error)
GetByAlipayOrderID(ctx context.Context, alipayOrderID string) (*entities.RechargeRecord, error)
GetByTransferOrderID(ctx context.Context, transferOrderID string) (*entities.RechargeRecord, error)
Update(ctx context.Context, record entities.RechargeRecord) error
UpdateStatus(ctx context.Context, id string, status entities.RechargeStatus) error
// 管理员查询方法
List(ctx context.Context, options interfaces.ListOptions) ([]entities.RechargeRecord, error)
Count(ctx context.Context, options interfaces.CountOptions) (int64, error)
}

View File

@@ -0,0 +1,37 @@
package repositories
import (
"context"
"tyapi-server/internal/domains/finance/entities"
"tyapi-server/internal/shared/interfaces"
)
// FinanceStats 财务统计信息
type FinanceStats struct {
TotalWallets int64
ActiveWallets int64
TotalBalance string
TodayTransactions int64
}
// WalletRepository 钱包仓储接口
// 只保留核心方法,聚合服务负责业务规则
// 业务操作只保留乐观锁更新和基础更新
type WalletRepository interface {
interfaces.Repository[entities.Wallet]
// 基础查询
GetByUserID(ctx context.Context, userID string) (*entities.Wallet, error)
// 乐观锁更新(自动重试)
UpdateBalanceWithVersion(ctx context.Context, walletID string, newBalance string, oldVersion int64) (bool, error)
// 状态操作
ActivateWallet(ctx context.Context, walletID string) error
DeactivateWallet(ctx context.Context, walletID string) error
// 统计
GetStats(ctx context.Context) (*FinanceStats, error)
GetUserWalletStats(ctx context.Context, userID string) (*FinanceStats, error)
}

View File

@@ -0,0 +1,28 @@
package repositories
import (
"context"
"tyapi-server/internal/domains/finance/entities"
"tyapi-server/internal/shared/interfaces"
)
// WalletTransactionRepository 钱包扣款记录仓储接口
type WalletTransactionRepository interface {
interfaces.Repository[entities.WalletTransaction]
// 基础查询
GetByUserID(ctx context.Context, userID string, limit, offset int) ([]*entities.WalletTransaction, error)
GetByApiCallID(ctx context.Context, apiCallID string) (*entities.WalletTransaction, error)
// 新增:分页查询用户钱包交易记录
ListByUserId(ctx context.Context, userId string, options interfaces.ListOptions) ([]*entities.WalletTransaction, int64, error)
// 新增:根据条件筛选钱包交易记录
ListByUserIdWithFilters(ctx context.Context, userId string, filters map[string]interface{}, options interfaces.ListOptions) ([]*entities.WalletTransaction, int64, error)
// 新增:根据条件筛选钱包交易记录(包含产品名称)
ListByUserIdWithFiltersAndProductName(ctx context.Context, userId string, filters map[string]interface{}, options interfaces.ListOptions) (map[string]string, []*entities.WalletTransaction, int64, error)
// 新增:统计用户钱包交易次数
CountByUserId(ctx context.Context, userId string) (int64, error)
}

View File

@@ -1,160 +0,0 @@
package services
import (
"context"
"fmt"
"github.com/shopspring/decimal"
"go.uber.org/zap"
"tyapi-server/internal/domains/finance/entities"
"tyapi-server/internal/domains/finance/repositories"
)
// FinanceService 财务领域服务
// 负责财务相关的业务逻辑,包括钱包管理、余额操作等
type FinanceService struct {
walletRepo repositories.WalletRepository
logger *zap.Logger
}
// NewFinanceService 创建财务领域服务
func NewFinanceService(
walletRepo repositories.WalletRepository,
logger *zap.Logger,
) *FinanceService {
return &FinanceService{
walletRepo: walletRepo,
logger: logger,
}
}
// CreateWallet 创建钱包
func (s *FinanceService) CreateWallet(ctx context.Context, userID string) (*entities.Wallet, error) {
// 检查用户是否已有钱包
existingWallet, err := s.walletRepo.GetByUserID(ctx, userID)
if err == nil && existingWallet != nil {
return nil, fmt.Errorf("用户已有钱包")
}
// 创建钱包
wallet := &entities.Wallet{
UserID: userID,
Balance: decimal.Zero,
IsActive: true,
WalletType: "MAIN",
}
createdWallet, err := s.walletRepo.Create(ctx, *wallet)
if err != nil {
s.logger.Error("创建钱包失败", zap.Error(err))
return nil, fmt.Errorf("创建钱包失败: %w", err)
}
s.logger.Info("钱包创建成功",
zap.String("wallet_id", createdWallet.ID),
zap.String("user_id", userID),
)
return &createdWallet, nil
}
// GetWallet 获取钱包信息
func (s *FinanceService) GetWallet(ctx context.Context, userID string) (*entities.Wallet, error) {
wallet, err := s.walletRepo.GetByUserID(ctx, userID)
if err != nil {
return nil, fmt.Errorf("钱包不存在: %w", err)
}
return wallet, nil
}
// GetWalletByID 根据ID获取钱包
func (s *FinanceService) GetWalletByID(ctx context.Context, walletID string) (*entities.Wallet, error) {
wallet, err := s.walletRepo.GetByID(ctx, walletID)
if err != nil {
return nil, fmt.Errorf("钱包不存在: %w", err)
}
return &wallet, nil
}
// RechargeWallet 充值钱包
func (s *FinanceService) RechargeWallet(ctx context.Context, userID string, amount float64) error {
if amount <= 0 {
return fmt.Errorf("充值金额必须大于0")
}
wallet, err := s.walletRepo.GetByUserID(ctx, userID)
if err != nil {
return fmt.Errorf("钱包不存在: %w", err)
}
// 更新余额
amountDecimal := decimal.NewFromFloat(amount)
wallet.AddBalance(amountDecimal)
if err := s.walletRepo.Update(ctx, *wallet); err != nil {
s.logger.Error("充值失败", zap.Error(err))
return fmt.Errorf("充值失败: %w", err)
}
s.logger.Info("钱包充值成功",
zap.String("wallet_id", wallet.ID),
zap.String("user_id", userID),
zap.Float64("amount", amount),
zap.String("new_balance", wallet.GetFormattedBalance()),
)
return nil
}
// DeductWallet 扣减钱包余额
func (s *FinanceService) DeductWallet(ctx context.Context, userID string, amount float64) error {
if amount <= 0 {
return fmt.Errorf("扣减金额必须大于0")
}
wallet, err := s.walletRepo.GetByUserID(ctx, userID)
if err != nil {
return fmt.Errorf("钱包不存在: %w", err)
}
amountDecimal := decimal.NewFromFloat(amount)
if err := wallet.SubtractBalance(amountDecimal); err != nil {
return err
}
if err := s.walletRepo.Update(ctx, *wallet); err != nil {
s.logger.Error("扣减失败", zap.Error(err))
return fmt.Errorf("扣减失败: %w", err)
}
s.logger.Info("钱包扣减成功",
zap.String("wallet_id", wallet.ID),
zap.String("user_id", userID),
zap.Float64("amount", amount),
zap.String("new_balance", wallet.GetFormattedBalance()),
)
return nil
}
// GetWalletBalance 获取钱包余额
func (s *FinanceService) GetWalletBalance(ctx context.Context, userID string) (float64, error) {
wallet, err := s.walletRepo.GetByUserID(ctx, userID)
if err != nil {
return 0, fmt.Errorf("钱包不存在: %w", err)
}
balance, _ := wallet.Balance.Float64()
return balance, nil
}
// CheckWalletBalance 检查钱包余额是否足够
func (s *FinanceService) CheckWalletBalance(ctx context.Context, userID string, amount float64) (bool, error) {
wallet, err := s.walletRepo.GetByUserID(ctx, userID)
if err != nil {
return false, fmt.Errorf("钱包不存在: %w", err)
}
amountDecimal := decimal.NewFromFloat(amount)
return wallet.HasSufficientBalance(amountDecimal), nil
}

View File

@@ -0,0 +1,349 @@
package services
import (
"context"
"fmt"
"github.com/shopspring/decimal"
"go.uber.org/zap"
"tyapi-server/internal/domains/finance/entities"
"tyapi-server/internal/domains/finance/repositories"
"tyapi-server/internal/shared/database"
"tyapi-server/internal/shared/interfaces"
)
// RechargeRecordService 充值记录服务接口
type RechargeRecordService interface {
// 对公转账充值
TransferRecharge(ctx context.Context, userID string, amount decimal.Decimal, transferOrderID, notes string) (*entities.RechargeRecord, error)
// 赠送充值
GiftRecharge(ctx context.Context, userID string, amount decimal.Decimal, operatorID, notes string) (*entities.RechargeRecord, error)
// 支付宝充值
CreateAlipayRecharge(ctx context.Context, userID string, amount decimal.Decimal, alipayOrderID string) (*entities.RechargeRecord, error)
GetRechargeRecordByAlipayOrderID(ctx context.Context, alipayOrderID string) (*entities.RechargeRecord, error)
// 支付宝订单管理
CreateAlipayOrder(ctx context.Context, rechargeID, outTradeNo, subject string, amount decimal.Decimal, platform string) error
HandleAlipayPaymentSuccess(ctx context.Context, outTradeNo string, amount decimal.Decimal, tradeNo string) error
// 通用查询
GetByID(ctx context.Context, id string) (*entities.RechargeRecord, error)
GetByUserID(ctx context.Context, userID string) ([]entities.RechargeRecord, error)
GetByTransferOrderID(ctx context.Context, transferOrderID string) (*entities.RechargeRecord, error)
// 管理员查询
GetAll(ctx context.Context, filters map[string]interface{}, options interfaces.ListOptions) ([]entities.RechargeRecord, error)
Count(ctx context.Context, filters map[string]interface{}) (int64, error)
}
// RechargeRecordServiceImpl 充值记录服务实现
type RechargeRecordServiceImpl struct {
rechargeRecordRepo repositories.RechargeRecordRepository
alipayOrderRepo repositories.AlipayOrderRepository
walletRepo repositories.WalletRepository
walletService WalletAggregateService
txManager *database.TransactionManager
logger *zap.Logger
}
func NewRechargeRecordService(
rechargeRecordRepo repositories.RechargeRecordRepository,
alipayOrderRepo repositories.AlipayOrderRepository,
walletRepo repositories.WalletRepository,
walletService WalletAggregateService,
txManager *database.TransactionManager,
logger *zap.Logger,
) RechargeRecordService {
return &RechargeRecordServiceImpl{
rechargeRecordRepo: rechargeRecordRepo,
alipayOrderRepo: alipayOrderRepo,
walletRepo: walletRepo,
walletService: walletService,
txManager: txManager,
logger: logger,
}
}
// TransferRecharge 对公转账充值
func (s *RechargeRecordServiceImpl) TransferRecharge(ctx context.Context, userID string, amount decimal.Decimal, transferOrderID, notes string) (*entities.RechargeRecord, error) {
// 检查钱包是否存在
_, err := s.walletRepo.GetByUserID(ctx, userID)
if err != nil {
return nil, fmt.Errorf("钱包不存在")
}
// 检查转账订单号是否已存在
existingRecord, _ := s.rechargeRecordRepo.GetByTransferOrderID(ctx, transferOrderID)
if existingRecord != nil {
return nil, fmt.Errorf("转账订单号已存在")
}
var createdRecord entities.RechargeRecord
// 在事务中执行所有更新操作
err = s.txManager.ExecuteInTx(ctx, func(txCtx context.Context) error {
// 创建充值记录
rechargeRecord := entities.NewTransferRechargeRecord(userID, amount, transferOrderID, notes)
record, err := s.rechargeRecordRepo.Create(txCtx, *rechargeRecord)
if err != nil {
s.logger.Error("创建转账充值记录失败", zap.Error(err))
return err
}
createdRecord = record
// 使用钱包聚合服务更新钱包余额
err = s.walletService.Recharge(txCtx, userID, amount)
if err != nil {
return err
}
// 标记充值记录为成功
createdRecord.MarkSuccess()
err = s.rechargeRecordRepo.Update(txCtx, createdRecord)
if err != nil {
s.logger.Error("更新充值记录状态失败", zap.Error(err))
return err
}
return nil
})
if err != nil {
return nil, err
}
s.logger.Info("对公转账充值成功",
zap.String("user_id", userID),
zap.String("amount", amount.String()),
zap.String("transfer_order_id", transferOrderID))
return &createdRecord, nil
}
// GiftRecharge 赠送充值
func (s *RechargeRecordServiceImpl) GiftRecharge(ctx context.Context, userID string, amount decimal.Decimal, operatorID, notes string) (*entities.RechargeRecord, error) {
// 检查钱包是否存在
_, err := s.walletRepo.GetByUserID(ctx, userID)
if err != nil {
return nil, fmt.Errorf("钱包不存在")
}
var createdRecord entities.RechargeRecord
// 在事务中执行所有更新操作
err = s.txManager.ExecuteInTx(ctx, func(txCtx context.Context) error {
// 创建赠送充值记录
rechargeRecord := entities.NewGiftRechargeRecord(userID, amount, notes)
record, err := s.rechargeRecordRepo.Create(txCtx, *rechargeRecord)
if err != nil {
s.logger.Error("创建赠送充值记录失败", zap.Error(err))
return err
}
createdRecord = record
// 使用钱包聚合服务更新钱包余额
err = s.walletService.Recharge(txCtx, userID, amount)
if err != nil {
return err
}
return nil
})
if err != nil {
return nil, err
}
s.logger.Info("赠送充值成功",
zap.String("user_id", userID),
zap.String("amount", amount.String()),
zap.String("notes", notes))
return &createdRecord, nil
}
// CreateAlipayRecharge 创建支付宝充值记录
func (s *RechargeRecordServiceImpl) CreateAlipayRecharge(ctx context.Context, userID string, amount decimal.Decimal, alipayOrderID string) (*entities.RechargeRecord, error) {
// 检查钱包是否存在
_, err := s.walletRepo.GetByUserID(ctx, userID)
if err != nil {
return nil, fmt.Errorf("钱包不存在")
}
// 检查支付宝订单号是否已存在
existingRecord, _ := s.rechargeRecordRepo.GetByAlipayOrderID(ctx, alipayOrderID)
if existingRecord != nil {
return nil, fmt.Errorf("支付宝订单号已存在")
}
// 创建充值记录
rechargeRecord := entities.NewAlipayRechargeRecord(userID, amount, alipayOrderID)
createdRecord, err := s.rechargeRecordRepo.Create(ctx, *rechargeRecord)
if err != nil {
s.logger.Error("创建支付宝充值记录失败", zap.Error(err))
return nil, err
}
s.logger.Info("支付宝充值记录创建成功",
zap.String("user_id", userID),
zap.String("amount", amount.String()),
zap.String("alipay_order_id", alipayOrderID),
zap.String("recharge_id", createdRecord.ID))
return &createdRecord, nil
}
// CreateAlipayOrder 创建支付宝订单
func (s *RechargeRecordServiceImpl) CreateAlipayOrder(ctx context.Context, rechargeID, outTradeNo, subject string, amount decimal.Decimal, platform string) error {
// 检查充值记录是否存在
_, err := s.rechargeRecordRepo.GetByID(ctx, rechargeID)
if err != nil {
s.logger.Error("充值记录不存在", zap.String("recharge_id", rechargeID), zap.Error(err))
return fmt.Errorf("充值记录不存在")
}
// 检查支付宝订单号是否已存在
existingOrder, _ := s.alipayOrderRepo.GetByOutTradeNo(ctx, outTradeNo)
if existingOrder != nil {
s.logger.Info("支付宝订单已存在,跳过重复创建", zap.String("out_trade_no", outTradeNo))
return nil
}
// 创建支付宝订单
alipayOrder := entities.NewAlipayOrder(rechargeID, outTradeNo, subject, amount, platform)
_, err = s.alipayOrderRepo.Create(ctx, *alipayOrder)
if err != nil {
s.logger.Error("创建支付宝订单失败", zap.Error(err))
return err
}
s.logger.Info("支付宝订单创建成功",
zap.String("recharge_id", rechargeID),
zap.String("out_trade_no", outTradeNo),
zap.String("subject", subject),
zap.String("amount", amount.String()),
zap.String("platform", platform))
return nil
}
// GetRechargeRecordByAlipayOrderID 根据支付宝订单号获取充值记录
func (s *RechargeRecordServiceImpl) GetRechargeRecordByAlipayOrderID(ctx context.Context, alipayOrderID string) (*entities.RechargeRecord, error) {
return s.rechargeRecordRepo.GetByAlipayOrderID(ctx, alipayOrderID)
}
// HandleAlipayPaymentSuccess 处理支付宝支付成功回调
func (s *RechargeRecordServiceImpl) HandleAlipayPaymentSuccess(ctx context.Context, outTradeNo string, amount decimal.Decimal, tradeNo string) error {
// 查找支付宝订单
alipayOrder, err := s.alipayOrderRepo.GetByOutTradeNo(ctx, outTradeNo)
if err != nil {
s.logger.Error("查找支付宝订单失败", zap.String("out_trade_no", outTradeNo), zap.Error(err))
return fmt.Errorf("查找支付宝订单失败: %w", err)
}
if alipayOrder == nil {
s.logger.Error("支付宝订单不存在", zap.String("out_trade_no", outTradeNo))
return fmt.Errorf("支付宝订单不存在")
}
// 检查订单状态
if alipayOrder.Status == entities.AlipayOrderStatusSuccess {
s.logger.Info("支付宝订单已处理成功,跳过重复处理",
zap.String("out_trade_no", outTradeNo),
zap.String("order_id", alipayOrder.ID),
)
return nil
}
// 查找对应的充值记录
rechargeRecord, err := s.rechargeRecordRepo.GetByID(ctx, alipayOrder.RechargeID)
if err != nil {
s.logger.Error("查找充值记录失败", zap.String("recharge_id", alipayOrder.RechargeID), zap.Error(err))
return fmt.Errorf("查找充值记录失败: %w", err)
}
// 检查充值记录状态
if rechargeRecord.Status == entities.RechargeStatusSuccess {
s.logger.Info("充值记录已处理成功,跳过重复处理",
zap.String("recharge_id", rechargeRecord.ID),
)
return nil
}
// 在事务中执行所有更新操作
err = s.txManager.ExecuteInTx(ctx, func(txCtx context.Context) error {
// 更新支付宝订单状态为成功
alipayOrder.MarkSuccess(tradeNo, "", "", amount, amount)
err := s.alipayOrderRepo.Update(txCtx, *alipayOrder)
if err != nil {
s.logger.Error("更新支付宝订单状态失败", zap.Error(err))
return err
}
// 更新充值记录状态为成功
rechargeRecord.MarkSuccess()
err = s.rechargeRecordRepo.Update(txCtx, rechargeRecord)
if err != nil {
s.logger.Error("更新充值记录状态失败", zap.Error(err))
return err
}
// 使用钱包聚合服务更新钱包余额
err = s.walletService.Recharge(txCtx, rechargeRecord.UserID, amount)
if err != nil {
s.logger.Error("更新钱包余额失败", zap.String("user_id", rechargeRecord.UserID), zap.Error(err))
return err
}
return nil
})
if err != nil {
return err
}
s.logger.Info("支付宝支付成功回调处理成功",
zap.String("user_id", rechargeRecord.UserID),
zap.String("amount", amount.String()),
zap.String("out_trade_no", outTradeNo),
zap.String("trade_no", tradeNo),
zap.String("recharge_id", rechargeRecord.ID),
zap.String("order_id", alipayOrder.ID))
return nil
}
// GetByID 根据ID获取充值记录
func (s *RechargeRecordServiceImpl) GetByID(ctx context.Context, id string) (*entities.RechargeRecord, error) {
record, err := s.rechargeRecordRepo.GetByID(ctx, id)
if err != nil {
return nil, err
}
return &record, nil
}
// GetByUserID 根据用户ID获取充值记录列表
func (s *RechargeRecordServiceImpl) GetByUserID(ctx context.Context, userID string) ([]entities.RechargeRecord, error) {
return s.rechargeRecordRepo.GetByUserID(ctx, userID)
}
// GetByTransferOrderID 根据转账订单号获取充值记录
func (s *RechargeRecordServiceImpl) GetByTransferOrderID(ctx context.Context, transferOrderID string) (*entities.RechargeRecord, error) {
return s.rechargeRecordRepo.GetByTransferOrderID(ctx, transferOrderID)
}
// GetAll 获取所有充值记录(管理员功能)
func (s *RechargeRecordServiceImpl) GetAll(ctx context.Context, filters map[string]interface{}, options interfaces.ListOptions) ([]entities.RechargeRecord, error) {
return s.rechargeRecordRepo.List(ctx, options)
}
// Count 统计充值记录数量(管理员功能)
func (s *RechargeRecordServiceImpl) Count(ctx context.Context, filters map[string]interface{}) (int64, error) {
countOptions := interfaces.CountOptions{
Filters: filters,
}
return s.rechargeRecordRepo.Count(ctx, countOptions)
}

View File

@@ -0,0 +1,142 @@
package services
import (
"context"
"fmt"
"github.com/shopspring/decimal"
"go.uber.org/zap"
"tyapi-server/internal/config"
"tyapi-server/internal/domains/finance/entities"
"tyapi-server/internal/domains/finance/repositories"
)
// WalletAggregateService 钱包聚合服务接口
type WalletAggregateService interface {
CreateWallet(ctx context.Context, userID string) (*entities.Wallet, error)
Recharge(ctx context.Context, userID string, amount decimal.Decimal) error
Deduct(ctx context.Context, userID string, amount decimal.Decimal, apiCallID, transactionID, productID string) error
GetBalance(ctx context.Context, userID string) (decimal.Decimal, error)
LoadWalletByUserId(ctx context.Context, userID string) (*entities.Wallet, error)
}
// WalletAggregateServiceImpl 实现
// WalletAggregateServiceImpl 钱包聚合服务实现
type WalletAggregateServiceImpl struct {
walletRepo repositories.WalletRepository
transactionRepo repositories.WalletTransactionRepository
logger *zap.Logger
cfg *config.Config
}
func NewWalletAggregateService(
walletRepo repositories.WalletRepository,
transactionRepo repositories.WalletTransactionRepository,
logger *zap.Logger,
cfg *config.Config,
) WalletAggregateService {
return &WalletAggregateServiceImpl{
walletRepo: walletRepo,
transactionRepo: transactionRepo,
logger: logger,
cfg: cfg,
}
}
// CreateWallet 创建钱包
func (s *WalletAggregateServiceImpl) CreateWallet(ctx context.Context, userID string) (*entities.Wallet, error) {
// 检查是否已存在
w, _ := s.walletRepo.GetByUserID(ctx, userID)
if w != nil {
return nil, fmt.Errorf("用户已存在钱包")
}
wallet := entities.NewWallet(userID, decimal.NewFromFloat(s.cfg.Wallet.DefaultCreditLimit))
created, err := s.walletRepo.Create(ctx, *wallet)
if err != nil {
s.logger.Error("创建钱包失败", zap.Error(err))
return nil, err
}
s.logger.Info("钱包创建成功", zap.String("user_id", userID), zap.String("wallet_id", created.ID))
return &created, nil
}
// Recharge 充值
func (s *WalletAggregateServiceImpl) Recharge(ctx context.Context, userID string, amount decimal.Decimal) error {
w, err := s.walletRepo.GetByUserID(ctx, userID)
if err != nil {
return fmt.Errorf("钱包不存在")
}
// 更新钱包余额
w.AddBalance(amount)
ok, err := s.walletRepo.UpdateBalanceWithVersion(ctx, w.ID, w.Balance.String(), w.Version)
if err != nil {
return err
}
if !ok {
return fmt.Errorf("高并发下充值失败,请重试")
}
s.logger.Info("钱包充值成功",
zap.String("user_id", userID),
zap.String("wallet_id", w.ID),
zap.String("amount", amount.String()),
zap.String("balance_after", w.Balance.String()))
return nil
}
// Deduct 扣款,含欠费规则
func (s *WalletAggregateServiceImpl) Deduct(ctx context.Context, userID string, amount decimal.Decimal, apiCallID, transactionID, productID string) error {
w, err := s.walletRepo.GetByUserID(ctx, userID)
if err != nil {
return fmt.Errorf("钱包不存在")
}
// 扣减余额
if err := w.SubtractBalance(amount); err != nil {
return err
}
// 更新钱包余额
ok, err := s.walletRepo.UpdateBalanceWithVersion(ctx, w.ID, w.Balance.String(), w.Version)
if err != nil {
return err
}
if !ok {
return fmt.Errorf("高并发下扣款失败,请重试")
}
// 创建扣款记录
transaction := entities.NewWalletTransaction(userID, apiCallID, transactionID, productID, amount)
_, err = s.transactionRepo.Create(ctx, *transaction)
if err != nil {
s.logger.Error("创建扣款记录失败", zap.Error(err))
// 不返回错误,因为钱包余额已经更新成功
}
s.logger.Info("钱包扣款成功",
zap.String("user_id", userID),
zap.String("wallet_id", w.ID),
zap.String("amount", amount.String()),
zap.String("balance_after", w.Balance.String()),
zap.String("api_call_id", apiCallID))
return nil
}
// GetBalance 查询余额
func (s *WalletAggregateServiceImpl) GetBalance(ctx context.Context, userID string) (decimal.Decimal, error) {
w, err := s.walletRepo.GetByUserID(ctx, userID)
if err != nil {
return decimal.Zero, fmt.Errorf("钱包不存在")
}
return w.Balance, nil
}
func (s *WalletAggregateServiceImpl) LoadWalletByUserId(ctx context.Context, userID string) (*entities.Wallet, error) {
return s.walletRepo.GetByUserID(ctx, userID)
}