115 lines
3.0 KiB
Go
115 lines
3.0 KiB
Go
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
|
||
}
|