tianyuan-api-server/apps/user/internal/logic/walletservice/updatewalletlogic.go
2024-10-12 20:41:55 +08:00

115 lines
3.0 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

package walletservicelogic
import (
"context"
"database/sql"
"errors"
"github.com/zeromicro/go-zero/core/stores/sqlx"
"tianyuan-api/apps/sentinel/client/product"
"tianyuan-api/apps/user/internal/model"
"time"
"tianyuan-api/apps/user/internal/svc"
"tianyuan-api/apps/user/user"
"github.com/zeromicro/go-zero/core/logx"
)
type UpdateWalletLogic struct {
ctx context.Context
svcCtx *svc.ServiceContext
logx.Logger
}
func NewUpdateWalletLogic(ctx context.Context, svcCtx *svc.ServiceContext) *UpdateWalletLogic {
return &UpdateWalletLogic{
ctx: ctx,
svcCtx: svcCtx,
Logger: logx.WithContext(ctx),
}
}
const maxRetries = 5 // 最大重试次数
// 修改钱包余额
func (l *UpdateWalletLogic) UpdateWallet(in *user.UpdateWalletRequest) (*user.UpdateWalletResponse, error) {
if !in.Charge {
return nil, errors.New("不需要扣款")
}
// 获取产品信息
consumeProduct, getProductByCodeErr := l.svcCtx.ProductRpc.GetProductByCode(l.ctx, &product.GetRecordByCodeRequest{Code: in.ProductCode})
if getProductByCodeErr != nil {
return nil, getProductByCodeErr
}
// 检查是否已经扣款
deduction, FindOneByTransactionIdErr := l.svcCtx.DeductionsModel.FindOneByTransactionId(l.ctx, in.TransactionId)
if FindOneByTransactionIdErr != nil {
if FindOneByTransactionIdErr == sql.ErrNoRows {
} else {
// 其他错误,直接返回
return nil, FindOneByTransactionIdErr
}
} else if deduction != nil {
// 找到扣款记录,不能重复扣款
return nil, errors.New("该订单已扣款")
}
// 启动事务
err := l.svcCtx.WalletsModel.TransCtx(l.ctx, func(ctx context.Context, session sqlx.Session) error {
var err error
// 尝试多次更新余额(用于乐观锁)
for i := 0; i < maxRetries; i++ {
err = l.svcCtx.WalletsModel.UpdateBalance(session, l.ctx, in.UserId, -consumeProduct.ProductPrice)
if err == nil {
// 成功,退出循环
break
}
if errors.Is(err, model.ErrVersionMismatch) {
// 版本号不匹配,重试
time.Sleep(100 * time.Millisecond) // 重试前的延迟
continue
} else {
// 其他错误,直接返回
return err
}
}
if err != nil {
return err
}
// 更新余额成功后,插入扣款记录
_, err = l.svcCtx.DeductionsModel.InsertDeductionsTrans(ctx, &model.Deductions{
TransactionId: in.TransactionId,
UserId: in.UserId,
Amount: consumeProduct.ProductPrice,
}, session)
if err != nil {
return err
}
return nil
})
if err != nil {
return nil, err
}
// 获取最新的用户余额
wallet, err := l.svcCtx.WalletsModel.FindOneByUserId(l.ctx, in.UserId)
if err != nil {
return nil, err
}
// 如果余额小于 0更新 QuotaExceeded 字段为 1
if wallet.Balance < 0 {
users, findOneErr := l.svcCtx.UserModel.FindOne(l.ctx, in.UserId)
if findOneErr != nil {
return nil, findOneErr
}
users.QuotaExceeded = 1
updateErr := l.svcCtx.UserModel.Update(l.ctx, users)
if updateErr != nil {
return nil, updateErr
}
}
return &user.UpdateWalletResponse{}, nil
}