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