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,
}
}