2025-07-11 21:05:58 +08:00
|
|
|
|
package entities
|
|
|
|
|
|
|
|
|
|
|
|
import (
|
|
|
|
|
|
"fmt"
|
|
|
|
|
|
"time"
|
|
|
|
|
|
|
2025-07-13 16:36:20 +08:00
|
|
|
|
"github.com/google/uuid"
|
2025-07-11 21:05:58 +08:00
|
|
|
|
"github.com/shopspring/decimal"
|
|
|
|
|
|
"gorm.io/gorm"
|
|
|
|
|
|
)
|
|
|
|
|
|
|
2025-07-28 01:46:39 +08:00
|
|
|
|
// Wallet 钱包聚合根
|
2025-07-11 21:05:58 +08:00
|
|
|
|
// 用户数字钱包的核心信息,支持多种钱包类型和精确的余额管理
|
|
|
|
|
|
// 使用decimal类型确保金额计算的精确性,避免浮点数精度问题
|
2025-07-28 01:46:39 +08:00
|
|
|
|
// 支持欠费(余额<0),但只允许扣到小于0一次,之后不能再扣
|
|
|
|
|
|
// 新建钱包时可配置默认额度
|
|
|
|
|
|
|
2025-07-11 21:05:58 +08:00
|
|
|
|
type Wallet struct {
|
|
|
|
|
|
// 基础标识
|
|
|
|
|
|
ID string `gorm:"primaryKey;type:varchar(36)" json:"id" comment:"钱包唯一标识"`
|
|
|
|
|
|
UserID string `gorm:"type:varchar(36);not null;uniqueIndex" json:"user_id" comment:"关联用户ID"`
|
|
|
|
|
|
|
|
|
|
|
|
// 钱包状态 - 钱包的基本状态信息
|
|
|
|
|
|
IsActive bool `gorm:"default:true" json:"is_active" comment:"钱包是否激活"`
|
|
|
|
|
|
Balance decimal.Decimal `gorm:"type:decimal(20,8);default:0" json:"balance" comment:"钱包余额(精确到8位小数)"`
|
2025-09-12 01:15:09 +08:00
|
|
|
|
Version int64 `gorm:"default:0" json:"version" comment:"乐观锁版本号"`
|
2025-07-11 21:05:58 +08:00
|
|
|
|
|
|
|
|
|
|
// 时间戳字段
|
|
|
|
|
|
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 (Wallet) TableName() string {
|
|
|
|
|
|
return "wallets"
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// IsZeroBalance 检查余额是否为零
|
|
|
|
|
|
func (w *Wallet) IsZeroBalance() bool {
|
|
|
|
|
|
return w.Balance.IsZero()
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-07-28 01:46:39 +08:00
|
|
|
|
// HasSufficientBalance 检查是否有足够余额(允许透支额度)
|
2025-07-11 21:05:58 +08:00
|
|
|
|
func (w *Wallet) HasSufficientBalance(amount decimal.Decimal) bool {
|
2025-07-28 01:46:39 +08:00
|
|
|
|
// 允许扣到额度下限
|
|
|
|
|
|
return w.Balance.Sub(amount).GreaterThanOrEqual(decimal.Zero)
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 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))
|
2025-07-11 21:05:58 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2025-07-28 01:46:39 +08:00
|
|
|
|
// GetBalanceStatus 获取余额状态
|
|
|
|
|
|
func (w *Wallet) GetBalanceStatus() string {
|
|
|
|
|
|
if w.IsArrears() {
|
|
|
|
|
|
return "arrears" // 欠费
|
|
|
|
|
|
} else if w.IsLowBalance() {
|
|
|
|
|
|
return "low" // 余额较低
|
|
|
|
|
|
} else {
|
|
|
|
|
|
return "normal" // 正常
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// AddBalance 增加余额(只做加法,业务规则由服务层控制是否允许充值)
|
2025-07-11 21:05:58 +08:00
|
|
|
|
func (w *Wallet) AddBalance(amount decimal.Decimal) {
|
|
|
|
|
|
w.Balance = w.Balance.Add(amount)
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-07-28 01:46:39 +08:00
|
|
|
|
// SubtractBalance 扣减余额,含欠费业务规则
|
2025-07-11 21:05:58 +08:00
|
|
|
|
func (w *Wallet) SubtractBalance(amount decimal.Decimal) error {
|
2025-07-28 01:46:39 +08:00
|
|
|
|
if w.Balance.LessThan(decimal.Zero) {
|
|
|
|
|
|
return fmt.Errorf("已欠费,不能再扣款")
|
2025-07-11 21:05:58 +08:00
|
|
|
|
}
|
2025-07-28 01:46:39 +08:00
|
|
|
|
newBalance := w.Balance.Sub(amount)
|
|
|
|
|
|
w.Balance = newBalance
|
2025-07-11 21:05:58 +08:00
|
|
|
|
return nil
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// GetFormattedBalance 获取格式化的余额字符串
|
|
|
|
|
|
func (w *Wallet) GetFormattedBalance() string {
|
|
|
|
|
|
return w.Balance.String()
|
|
|
|
|
|
}
|
2025-07-13 16:36:20 +08:00
|
|
|
|
|
|
|
|
|
|
// BeforeCreate GORM钩子:创建前自动生成UUID
|
|
|
|
|
|
func (w *Wallet) BeforeCreate(tx *gorm.DB) error {
|
|
|
|
|
|
if w.ID == "" {
|
|
|
|
|
|
w.ID = uuid.New().String()
|
|
|
|
|
|
}
|
|
|
|
|
|
return nil
|
|
|
|
|
|
}
|
2025-07-28 01:46:39 +08:00
|
|
|
|
|
|
|
|
|
|
// NewWallet 工厂方法
|
|
|
|
|
|
func NewWallet(userID string, defaultCreditLimit decimal.Decimal) *Wallet {
|
|
|
|
|
|
return &Wallet{
|
|
|
|
|
|
UserID: userID,
|
|
|
|
|
|
IsActive: true,
|
|
|
|
|
|
Balance: defaultCreditLimit,
|
|
|
|
|
|
Version: 0,
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|