package walletservicelogic import ( "context" "database/sql" "errors" "github.com/zeromicro/go-zero/core/stores/sqlx" "tianyuan-api/apps/sentinel/client/product" "tianyuan-api/apps/sentinel/sentinel" "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 } userProduct, getUserProductErr := l.svcCtx.UserProductRpc.GetUserProduct(l.ctx, &sentinel.UserProuctRequest{ UserId: in.UserId, ProductId: consumeProduct.Id, }) if getUserProductErr != nil { return nil, getUserProductErr } // 检查是否已经扣款 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, -userProduct.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: userProduct.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 }