代码初始化
This commit is contained in:
262
service/knapsack/knapsack_service.go
Normal file
262
service/knapsack/knapsack_service.go
Normal file
@@ -0,0 +1,262 @@
|
||||
package service
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/md5"
|
||||
"errors"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/gogf/gf/v2/util/gconv"
|
||||
|
||||
knapsackConsts "shop-user-trade/consts/knapsack"
|
||||
knapsackDao "shop-user-trade/dao/knapsack"
|
||||
knapsackDto "shop-user-trade/model/dto/knapsack"
|
||||
knapsackEntity "shop-user-trade/model/entity/knapsack"
|
||||
)
|
||||
|
||||
type knapsack struct{}
|
||||
|
||||
// Knapsack 背包服务
|
||||
var Knapsack = new(knapsack)
|
||||
|
||||
// Create 创建背包项
|
||||
func (s *knapsack) Create(ctx context.Context, req *knapsackDto.CreateKnapsackReq) (int64, error) {
|
||||
id, err := knapsackDao.Knapsack.Insert(ctx, req)
|
||||
if err != nil {
|
||||
return 0, fmt.Errorf("创建背包项失败: %w", err)
|
||||
}
|
||||
return id, nil
|
||||
}
|
||||
|
||||
// GetOne 获取单个背包项
|
||||
func (s *knapsack) GetOne(ctx context.Context, req *knapsackDto.GetKnapsackReq) (*knapsackDto.GetKnapsackRes, error) {
|
||||
item, err := knapsackDao.Knapsack.GetOne(ctx, req)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if item == nil {
|
||||
return nil, errors.New("背包项不存在")
|
||||
}
|
||||
return &knapsackDto.GetKnapsackRes{
|
||||
KnapsackItem: s.entityToItem(item),
|
||||
}, nil
|
||||
}
|
||||
|
||||
// List 获取背包列表(支持分页和搜索)
|
||||
func (s *knapsack) List(ctx context.Context, req *knapsackDto.ListKnapsackReq) (*knapsackDto.ListKnapsackRes, error) {
|
||||
list, total, err := knapsackDao.Knapsack.List(ctx, req)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
res := &knapsackDto.ListKnapsackRes{Total: total}
|
||||
for _, item := range list {
|
||||
itemCopy := item
|
||||
res.List = append(res.List, s.entityToItem(&itemCopy))
|
||||
}
|
||||
return res, nil
|
||||
}
|
||||
|
||||
// Use 使用背包项
|
||||
func (s *knapsack) Use(ctx context.Context, req *knapsackDto.UseKnapsackReq) error {
|
||||
item, err := knapsackDao.Knapsack.GetByID(ctx, req.ID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if item == nil {
|
||||
return errors.New("背包项不存在")
|
||||
}
|
||||
if err = s.canBeUsed(item); err != nil {
|
||||
return err
|
||||
}
|
||||
now := time.Now().Unix()
|
||||
usedStatus := knapsackConsts.KnapsackStatusUsed
|
||||
updateReq := &knapsackDto.UpdateKnapsackReq{
|
||||
Id: item.Id,
|
||||
Status: &usedStatus,
|
||||
UsedAt: &now,
|
||||
Updater: req.OperatorName,
|
||||
}
|
||||
if _, err = knapsackDao.Knapsack.Update(ctx, updateReq); err != nil {
|
||||
return fmt.Errorf("更新背包项失败: %w", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// ListToMarket 上架背包项到市场
|
||||
func (s *knapsack) ListToMarket(ctx context.Context, req *knapsackDto.ListToMarketReq) error {
|
||||
item, err := knapsackDao.Knapsack.GetByID(ctx, req.ID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if item == nil {
|
||||
return errors.New("背包项不存在")
|
||||
}
|
||||
if err = s.canBeListed(item); err != nil {
|
||||
return err
|
||||
}
|
||||
listedStatus := knapsackConsts.KnapsackStatusListed
|
||||
updateReq := &knapsackDto.UpdateKnapsackReq{
|
||||
Id: item.Id,
|
||||
Status: &listedStatus,
|
||||
Updater: req.OperatorName,
|
||||
}
|
||||
if _, err = knapsackDao.Knapsack.Update(ctx, updateReq); err != nil {
|
||||
return fmt.Errorf("更新背包项失败: %w", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// UnlistKnapsack 从市场下架背包项
|
||||
func (s *knapsack) UnlistKnapsack(ctx context.Context, req *knapsackDto.UnlistKnapsackReq) error {
|
||||
item, err := knapsackDao.Knapsack.GetByID(ctx, req.ID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if item == nil {
|
||||
return errors.New("背包项不存在")
|
||||
}
|
||||
if item.Status != knapsackConsts.KnapsackStatusListed {
|
||||
return errors.New("只有已上架的物品才能下架")
|
||||
}
|
||||
activeStatus := knapsackConsts.KnapsackStatusActive
|
||||
updateReq := &knapsackDto.UpdateKnapsackReq{
|
||||
Id: item.Id,
|
||||
Status: &activeStatus,
|
||||
Updater: req.OperatorName,
|
||||
}
|
||||
if _, err = knapsackDao.Knapsack.Update(ctx, updateReq); err != nil {
|
||||
return fmt.Errorf("更新背包项失败: %w", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Verify 核销背包项
|
||||
func (s *knapsack) Verify(ctx context.Context, req *knapsackDto.VerifyKnapsackReq) error {
|
||||
item, err := knapsackDao.Knapsack.GetByID(ctx, req.ID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if item == nil {
|
||||
return errors.New("背包项不存在")
|
||||
}
|
||||
if err = s.canBeVerified(item); err != nil {
|
||||
return err
|
||||
}
|
||||
now := time.Now().Unix()
|
||||
usedStatus := knapsackConsts.KnapsackStatusUsed
|
||||
updateReq := &knapsackDto.UpdateKnapsackReq{
|
||||
Id: item.Id,
|
||||
Status: &usedStatus,
|
||||
UsedAt: &now,
|
||||
Updater: req.OperatorName,
|
||||
}
|
||||
if _, err = knapsackDao.Knapsack.Update(ctx, updateReq); err != nil {
|
||||
return fmt.Errorf("更新背包项失败: %w", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// GenerateQRCode 生成核销二维码
|
||||
func (s *knapsack) GenerateQRCode(ctx context.Context, req *knapsackDto.GenerateQRCodeReq) (*knapsackDto.GenerateQRCodeRes, error) {
|
||||
item, err := knapsackDao.Knapsack.GetByID(ctx, req.ID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if item == nil {
|
||||
return nil, errors.New("背包项不存在")
|
||||
}
|
||||
if err = s.canBeUsed(item); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
now := time.Now().Unix()
|
||||
expireTime := now + req.ExpireDuration
|
||||
verifyCode := s.generateVerifyCode(gconv.String(item.Id), now)
|
||||
qrContent := fmt.Sprintf("%d:%d:%d:%s", item.Id, now, expireTime, verifyCode)
|
||||
qrCode := fmt.Sprintf("QR_%x", md5.Sum([]byte(qrContent)))
|
||||
return &knapsackDto.GenerateQRCodeRes{
|
||||
QRCode: qrCode,
|
||||
VerifyCode: verifyCode,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// ExpireExpiredItems 将过期的背包项标记为过期状态(定时任务调用)
|
||||
func (s *knapsack) ExpireExpiredItems(ctx context.Context) (int64, error) {
|
||||
expiredList, err := knapsackDao.Knapsack.ListExpired(ctx)
|
||||
if err != nil {
|
||||
return 0, fmt.Errorf("查询过期物品失败: %w", err)
|
||||
}
|
||||
if len(expiredList) == 0 {
|
||||
return 0, nil
|
||||
}
|
||||
count := int64(len(expiredList))
|
||||
expiredStatus := knapsackConsts.KnapsackStatusExpired
|
||||
for _, item := range expiredList {
|
||||
updateReq := &knapsackDto.UpdateKnapsackReq{
|
||||
Id: item.Id,
|
||||
Status: &expiredStatus,
|
||||
}
|
||||
if _, err = knapsackDao.Knapsack.Update(ctx, updateReq); err != nil {
|
||||
return count, fmt.Errorf("更新过期物品状态失败: %w", err)
|
||||
}
|
||||
}
|
||||
return count, nil
|
||||
}
|
||||
|
||||
// canBeUsed 检查背包项是否可以使用
|
||||
func (s *knapsack) canBeUsed(item *knapsackEntity.Knapsack) error {
|
||||
if item.Status != knapsackConsts.KnapsackStatusActive {
|
||||
return errors.New("物品状态不可用")
|
||||
}
|
||||
if item.ExpireAt != nil && *item.ExpireAt < time.Now().Unix() {
|
||||
return errors.New("物品已过期")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// canBeListed 检查背包项是否可以上架
|
||||
func (s *knapsack) canBeListed(item *knapsackEntity.Knapsack) error {
|
||||
if item.Status != knapsackConsts.KnapsackStatusActive {
|
||||
return errors.New("只有启用状态的物品才能上架")
|
||||
}
|
||||
if item.ExpireAt != nil && *item.ExpireAt < time.Now().Unix() {
|
||||
return errors.New("已过期的物品不能上架")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// canBeVerified 检查背包项是否可以核销
|
||||
func (s *knapsack) canBeVerified(item *knapsackEntity.Knapsack) error {
|
||||
if item.Status != knapsackConsts.KnapsackStatusActive {
|
||||
return errors.New("物品状态不可核销")
|
||||
}
|
||||
if item.ExpireAt != nil && *item.ExpireAt < time.Now().Unix() {
|
||||
return errors.New("物品已过期")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// generateVerifyCode 生成验证码
|
||||
func (s *knapsack) generateVerifyCode(knapsackID string, timestamp int64) string {
|
||||
data := fmt.Sprintf("%s_%d_verify", knapsackID, timestamp)
|
||||
hash := md5.Sum([]byte(data))
|
||||
return fmt.Sprintf("V%x", hash)[:8]
|
||||
}
|
||||
|
||||
// entityToItem 实体转换为Item
|
||||
func (s *knapsack) entityToItem(e *knapsackEntity.Knapsack) *knapsackDto.KnapsackItem {
|
||||
item := &knapsackDto.KnapsackItem{}
|
||||
if err := gconv.Struct(e, item); err != nil {
|
||||
return item
|
||||
}
|
||||
item.ID = e.Id
|
||||
item.Status = e.Status
|
||||
item.Type = e.Type
|
||||
if e.CreatedAt != nil {
|
||||
item.CreatedAt = e.CreatedAt.String()
|
||||
}
|
||||
if e.UpdatedAt != nil {
|
||||
item.UpdatedAt = e.UpdatedAt.String()
|
||||
}
|
||||
return item
|
||||
}
|
||||
249
service/market/market_service.go
Normal file
249
service/market/market_service.go
Normal file
@@ -0,0 +1,249 @@
|
||||
package service
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"gitea.com/red-future/common/consul"
|
||||
"gitea.com/red-future/common/http"
|
||||
"github.com/gogf/gf/v2/util/gconv"
|
||||
|
||||
marketConsts "shop-user-trade/consts/market"
|
||||
marketDao "shop-user-trade/dao/market"
|
||||
marketDto "shop-user-trade/model/dto/market"
|
||||
marketEntity "shop-user-trade/model/entity/market"
|
||||
)
|
||||
|
||||
type market struct{}
|
||||
|
||||
// Market 市场服务
|
||||
var Market = new(market)
|
||||
|
||||
// Create 创建市场物品
|
||||
func (s *market) Create(ctx context.Context, req *marketDto.CreateMarketReq) (int64, error) {
|
||||
// 检查该背包项是否已经在市场中
|
||||
existing, _ := marketDao.Market.GetByKnapsackID(ctx, req.KnapsackID)
|
||||
if existing != nil && existing.Id > 0 {
|
||||
return 0, errors.New("该物品已在市场中")
|
||||
}
|
||||
|
||||
// 设置上架过期时间(默认7天)
|
||||
if req.ListExpireAt == nil {
|
||||
defaultExpire := time.Now().Add(7 * 24 * time.Hour).Unix()
|
||||
req.ListExpireAt = &defaultExpire
|
||||
}
|
||||
|
||||
id, err := marketDao.Market.Insert(ctx, req)
|
||||
if err != nil {
|
||||
return 0, fmt.Errorf("创建市场物品失败: %w", err)
|
||||
}
|
||||
return id, nil
|
||||
}
|
||||
|
||||
// GetOne 获取单个市场物品
|
||||
func (s *market) GetOne(ctx context.Context, req *marketDto.GetMarketReq) (*marketDto.GetMarketRes, error) {
|
||||
item, err := marketDao.Market.GetOne(ctx, req)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if item == nil {
|
||||
return nil, errors.New("市场物品不存在")
|
||||
}
|
||||
return &marketDto.GetMarketRes{
|
||||
MarketItem: s.entityToItem(item),
|
||||
}, nil
|
||||
}
|
||||
|
||||
// List 获取市场列表(支持分页和搜索)
|
||||
func (s *market) List(ctx context.Context, req *marketDto.ListMarketReq) (*marketDto.ListMarketRes, error) {
|
||||
list, total, err := marketDao.Market.List(ctx, req)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
res := &marketDto.ListMarketRes{Total: total}
|
||||
for _, item := range list {
|
||||
itemCopy := item
|
||||
res.List = append(res.List, s.entityToItem(&itemCopy))
|
||||
}
|
||||
return res, nil
|
||||
}
|
||||
|
||||
// Unlist 下架市场物品
|
||||
func (s *market) Unlist(ctx context.Context, req *marketDto.UnlistMarketReq) error {
|
||||
item, err := marketDao.Market.GetByID(ctx, req.ID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if item == nil {
|
||||
return errors.New("市场物品不存在")
|
||||
}
|
||||
if item.Status != marketConsts.MarketStatusActive {
|
||||
return errors.New("只有活跃状态的物品才能下架")
|
||||
}
|
||||
inactiveStatus := marketConsts.MarketStatusInactive
|
||||
updateReq := &marketDto.UpdateMarketReq{
|
||||
Id: item.Id,
|
||||
Status: &inactiveStatus,
|
||||
Updater: req.OperatorName,
|
||||
}
|
||||
if _, err = marketDao.Market.Update(ctx, updateReq); err != nil {
|
||||
return fmt.Errorf("更新市场物品失败: %w", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// UpdatePrice 更新价格
|
||||
func (s *market) UpdatePrice(ctx context.Context, req *marketDto.UpdatePriceReq) error {
|
||||
item, err := marketDao.Market.GetByID(ctx, req.ID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if item == nil {
|
||||
return errors.New("市场物品不存在")
|
||||
}
|
||||
if item.Status != marketConsts.MarketStatusActive {
|
||||
return errors.New("只有活跃状态的物品才能更新价格")
|
||||
}
|
||||
updateReq := &marketDto.UpdateMarketReq{
|
||||
Id: item.Id,
|
||||
Price: &req.Price,
|
||||
Updater: req.OperatorName,
|
||||
}
|
||||
if _, err = marketDao.Market.Update(ctx, updateReq); err != nil {
|
||||
return fmt.Errorf("更新价格失败: %w", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Buy 购买市场物品 - 调用order模块创建订单
|
||||
func (s *market) Buy(ctx context.Context, req *marketDto.BuyMarketReq) (string, error) {
|
||||
item, err := marketDao.Market.GetByID(ctx, req.MarketID)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
if item == nil {
|
||||
return "", errors.New("市场物品不存在")
|
||||
}
|
||||
if err = s.canBeBought(item); err != nil {
|
||||
return "", err
|
||||
}
|
||||
// 检查买家不能是卖家
|
||||
if item.UserID == req.BuyerID {
|
||||
return "", errors.New("不能购买自己上架的物品")
|
||||
}
|
||||
|
||||
// 调用order模块创建订单
|
||||
orderAddr, err := consul.GetInstanceAddr(ctx, "order")
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("获取order服务地址失败: %w", err)
|
||||
}
|
||||
|
||||
orderReq := map[string]interface{}{
|
||||
"user_id": req.BuyerID,
|
||||
"order_type": "market",
|
||||
"subject": item.AssetName,
|
||||
"description": item.Description,
|
||||
"order_items": []map[string]interface{}{
|
||||
{
|
||||
"asset_id": item.AssetID,
|
||||
"asset_name": item.AssetName,
|
||||
"asset_type": item.Type,
|
||||
"image_url": item.ImageURL,
|
||||
"stocks": []map[string]interface{}{
|
||||
{
|
||||
"stock_id": item.StockDetailID,
|
||||
"batch_id": item.BatchID,
|
||||
"batch_no": item.BatchNo,
|
||||
"quantity": 1,
|
||||
"price": item.Price,
|
||||
"stock_mode": item.StockMode,
|
||||
"stock_attrs": map[string]interface{}{
|
||||
"market_id": item.Id,
|
||||
"seller_id": item.UserID,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
"shipping_info": map[string]interface{}{},
|
||||
}
|
||||
|
||||
orderRes := &struct {
|
||||
Code int `json:"code"`
|
||||
Message string `json:"message"`
|
||||
Data struct {
|
||||
OrderNo string `json:"order_no"`
|
||||
TotalAmount int64 `json:"total_amount"`
|
||||
PayAmount int64 `json:"pay_amount"`
|
||||
ExpiredAt string `json:"expired_at"`
|
||||
} `json:"data"`
|
||||
}{}
|
||||
err = http.Post(ctx, fmt.Sprintf("http://%s/order/create", orderAddr), nil, orderRes, orderReq) // #nosec G107
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("创建订单失败: %w", err)
|
||||
}
|
||||
|
||||
// 更新市场物品状态为已售出
|
||||
soldStatus := marketConsts.MarketStatusSold
|
||||
updateReq := &marketDto.UpdateMarketReq{
|
||||
Id: item.Id,
|
||||
Status: &soldStatus,
|
||||
}
|
||||
if _, err = marketDao.Market.Update(ctx, updateReq); err != nil {
|
||||
return "", fmt.Errorf("更新市场物品失败: %w", err)
|
||||
}
|
||||
return orderRes.Data.OrderNo, nil
|
||||
}
|
||||
|
||||
// ExpireExpiredItems 将过期的市场物品标记为过期状态(定时任务调用)
|
||||
func (s *market) ExpireExpiredItems(ctx context.Context) (int64, error) {
|
||||
expiredList, err := marketDao.Market.ListExpired(ctx)
|
||||
if err != nil {
|
||||
return 0, fmt.Errorf("查询过期物品失败: %w", err)
|
||||
}
|
||||
if len(expiredList) == 0 {
|
||||
return 0, nil
|
||||
}
|
||||
count := int64(len(expiredList))
|
||||
expiredStatus := marketConsts.MarketStatusExpired
|
||||
for _, item := range expiredList {
|
||||
updateReq := &marketDto.UpdateMarketReq{
|
||||
Id: item.Id,
|
||||
Status: &expiredStatus,
|
||||
}
|
||||
if _, err = marketDao.Market.Update(ctx, updateReq); err != nil {
|
||||
return count, fmt.Errorf("更新过期物品状态失败: %w", err)
|
||||
}
|
||||
}
|
||||
return count, nil
|
||||
}
|
||||
|
||||
// canBeBought 检查市场物品是否可以购买
|
||||
func (s *market) canBeBought(item *marketEntity.Market) error {
|
||||
if item.Status != marketConsts.MarketStatusActive {
|
||||
return errors.New("物品状态不可购买")
|
||||
}
|
||||
if item.ListExpireAt != nil && *item.ListExpireAt < time.Now().Unix() {
|
||||
return errors.New("物品已过期")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// entityToItem 实体转换为Item
|
||||
func (s *market) entityToItem(e *marketEntity.Market) *marketDto.MarketItem {
|
||||
item := &marketDto.MarketItem{}
|
||||
if err := gconv.Struct(e, item); err != nil {
|
||||
return item
|
||||
}
|
||||
item.ID = e.Id
|
||||
item.Status = e.Status
|
||||
if e.CreatedAt != nil {
|
||||
item.CreatedAt = e.CreatedAt.String()
|
||||
}
|
||||
if e.UpdatedAt != nil {
|
||||
item.UpdatedAt = e.UpdatedAt.String()
|
||||
}
|
||||
return item
|
||||
}
|
||||
153
service/wallet/wallet_service.go
Normal file
153
service/wallet/wallet_service.go
Normal file
@@ -0,0 +1,153 @@
|
||||
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
|
||||
}
|
||||
Reference in New Issue
Block a user