154 lines
4.2 KiB
Go
154 lines
4.2 KiB
Go
package service
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"fmt"
|
|
|
|
"github.com/gogf/gf/v2/frame/g"
|
|
"github.com/gogf/gf/v2/util/gconv"
|
|
|
|
walletDao "shop-user-trade/dao/wallet"
|
|
walletDto "shop-user-trade/model/dto/wallet"
|
|
walletEntity "shop-user-trade/model/entity/wallet"
|
|
)
|
|
|
|
type wallet struct{}
|
|
|
|
// Wallet 钱包服务
|
|
var Wallet = new(wallet)
|
|
|
|
// GetByUserId 根据用户ID获取钱包
|
|
func (s *wallet) GetByUserId(ctx context.Context, req *walletDto.GetWalletByUserIdReq) (*walletDto.GetWalletByUserIdResp, error) {
|
|
w, err := walletDao.Wallet.GetByUserID(ctx, req.UserId)
|
|
if err != nil {
|
|
g.Log().Errorf(ctx, "获取用户钱包失败, userId: %d, error: %v", req.UserId, err)
|
|
return nil, err
|
|
}
|
|
if w == nil {
|
|
return nil, errors.New("钱包不存在")
|
|
}
|
|
return &walletDto.GetWalletByUserIdResp{
|
|
Data: walletDto.WalletInfo{
|
|
ID: w.Id,
|
|
UserID: w.UserID,
|
|
Balance: w.Balance,
|
|
Currency: w.Currency,
|
|
Status: int(w.Status),
|
|
},
|
|
}, nil
|
|
}
|
|
|
|
// Create 创建钱包
|
|
func (s *wallet) Create(ctx context.Context, req *walletDto.CreateWalletReq) (*walletDto.CreateWalletResp, error) {
|
|
// 检查钱包是否已存在
|
|
existing, err := walletDao.Wallet.GetByUserID(ctx, req.UserId)
|
|
if err != nil {
|
|
g.Log().Errorf(ctx, "检查用户钱包失败, userId: %d, error: %v", req.UserId, err)
|
|
return nil, err
|
|
}
|
|
if existing != nil {
|
|
return nil, errors.New("钱包已存在")
|
|
}
|
|
id, err := walletDao.Wallet.Create(ctx, req)
|
|
if err != nil {
|
|
g.Log().Errorf(ctx, "创建钱包失败, userId: %d, error: %v", req.UserId, err)
|
|
return nil, err
|
|
}
|
|
return &walletDto.CreateWalletResp{
|
|
Data: walletDto.CreateWalletData{WalletID: id},
|
|
}, nil
|
|
}
|
|
|
|
// UpdateBalance 更新余额
|
|
func (s *wallet) UpdateBalance(ctx context.Context, req *walletDto.UpdateBalanceReq) (*walletDto.UpdateBalanceResp, error) {
|
|
w, err := walletDao.Wallet.GetByID(ctx, req.WalletID)
|
|
if err != nil {
|
|
g.Log().Errorf(ctx, "获取钱包失败, walletId: %d, error: %v", req.WalletID, err)
|
|
return nil, err
|
|
}
|
|
if w == nil {
|
|
return nil, errors.New("钱包不存在")
|
|
}
|
|
|
|
// 检查余额是否充足
|
|
if req.Type == "expense" && w.Balance < req.Amount {
|
|
return nil, errors.New("余额不足")
|
|
}
|
|
|
|
// 记录操作前余额
|
|
balanceBefore := w.Balance
|
|
var balanceAfter int64
|
|
switch req.Type {
|
|
case "income":
|
|
balanceAfter = balanceBefore + req.Amount
|
|
case "expense":
|
|
balanceAfter = balanceBefore - req.Amount
|
|
default:
|
|
balanceAfter = balanceBefore
|
|
}
|
|
|
|
// 更新余额(乐观锁)
|
|
success, err := walletDao.Wallet.UpdateBalance(ctx, req.WalletID, req.Amount, req.Type, w.Version)
|
|
if err != nil {
|
|
g.Log().Errorf(ctx, "更新余额失败, walletId: %d, error: %v", req.WalletID, err)
|
|
return nil, err
|
|
}
|
|
if !success {
|
|
return nil, errors.New("余额更新失败,请重试")
|
|
}
|
|
|
|
// 记录流水日志
|
|
logReq := &walletDto.CreateWalletLogReq{
|
|
UserID: w.UserID,
|
|
WalletID: w.Id,
|
|
OrderNo: req.OrderNo,
|
|
TransactionNo: req.TransactionNo,
|
|
Type: req.Type,
|
|
Amount: req.Amount,
|
|
BalanceBefore: balanceBefore,
|
|
BalanceAfter: balanceAfter,
|
|
Currency: w.Currency,
|
|
Description: req.Description,
|
|
ExtraData: req.ExtraData,
|
|
}
|
|
if _, err = walletDao.Wallet.CreateLog(ctx, logReq); err != nil {
|
|
g.Log().Warningf(ctx, "记录钱包日志失败, walletId: %d, error: %v", req.WalletID, err)
|
|
}
|
|
|
|
return &walletDto.UpdateBalanceResp{
|
|
Success: true,
|
|
Message: "更新成功",
|
|
}, nil
|
|
}
|
|
|
|
// GetWalletLogs 获取钱包日志
|
|
func (s *wallet) GetWalletLogs(ctx context.Context, req *walletDto.GetWalletLogsReq) (*walletDto.GetWalletLogsResp, error) {
|
|
logs, total, err := walletDao.Wallet.ListLogs(ctx, req.UserId, req.Page, req.PageSize)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("获取钱包日志失败: %w", err)
|
|
}
|
|
var logInfos []walletDto.WalletLogInfo
|
|
for _, log := range logs {
|
|
logInfos = append(logInfos, s.logEntityToInfo(&log))
|
|
}
|
|
return &walletDto.GetWalletLogsResp{
|
|
Data: walletDto.WalletLogData{
|
|
Logs: logInfos,
|
|
Total: total,
|
|
},
|
|
}, nil
|
|
}
|
|
|
|
// logEntityToInfo 日志实体转换为Info
|
|
func (s *wallet) logEntityToInfo(e *walletEntity.WalletLog) walletDto.WalletLogInfo {
|
|
info := walletDto.WalletLogInfo{}
|
|
_ = gconv.Struct(e, &info)
|
|
info.ID = e.Id
|
|
info.Type = string(e.Type)
|
|
if e.CreatedAt != nil {
|
|
info.CreatedAt = e.CreatedAt.String()
|
|
}
|
|
return info
|
|
}
|