package agent import ( "context" "database/sql" "jnc-server/app/main/api/internal/svc" "jnc-server/app/main/api/internal/types" "jnc-server/app/main/model" "jnc-server/common/ctxdata" "jnc-server/common/xerr" "jnc-server/pkg/lzkit/crypto" "github.com/google/uuid" "github.com/pkg/errors" "github.com/zeromicro/go-zero/core/logx" "github.com/zeromicro/go-zero/core/stores/sqlx" ) type CreateWithdrawLogic struct { logx.Logger ctx context.Context svcCtx *svc.ServiceContext } func NewCreateWithdrawLogic(ctx context.Context, svcCtx *svc.ServiceContext) *CreateWithdrawLogic { return &CreateWithdrawLogic{ Logger: logx.WithContext(ctx), ctx: ctx, svcCtx: svcCtx, } } func (l *CreateWithdrawLogic) CreateWithdraw(req *types.CreateWithdrawReq) (resp *types.CreateWithdrawResp, err error) { // 1. 获取当前用户ID(代理ID) userId, err := ctxdata.GetUidFromCtx(l.ctx) if err != nil { return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "获取用户信息失败: %v", err) } // 2. 查询代理信息 agent, err := l.svcCtx.AgentModel.FindOneByUserId(l.ctx, userId) if err != nil { if errors.Is(err, model.ErrNotFound) { return nil, errors.Wrap(xerr.NewErrCode(xerr.CUSTOM_ERROR), "您还不是代理,无法申请提现") } return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "查询代理信息失败: %v", err) } // 3. 查询钱包信息 wallet, err := l.svcCtx.AgentWalletModel.FindOneByAgentId(l.ctx, agent.Id) if err != nil { return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "查询钱包信息失败: %v", err) } // 4. 验证提现金额 if req.WithdrawAmount <= 0 { return nil, errors.Wrap(xerr.NewErrCode(xerr.REUQEST_PARAM_ERROR), "提现金额必须大于0") } minWithdraw := 100.00 // 最低提现金额100元 if req.WithdrawAmount < minWithdraw { return nil, errors.Wrapf(xerr.NewErrCode(xerr.REUQEST_PARAM_ERROR), "最低提现金额为%.2f元", minWithdraw) } if wallet.Balance < req.WithdrawAmount { return nil, errors.Wrap(xerr.NewErrCode(xerr.CUSTOM_ERROR), "可用余额不足") } // 5. 加密银行卡号 encryptedCardNumber, err := crypto.AesEncrypt([]byte(req.BankCardNumber), []byte(l.svcCtx.Config.Encrypt.SecretKey)) if err != nil { logx.Errorf("加密银行卡号失败: %v", err) return nil, errors.Wrap(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "加密银行卡号失败") } // 6. 生成提现记录ID withdrawId := uuid.NewString() // 7. 解密代理手机号(用于冗余存储) agentMobile, err := crypto.DecryptMobile(agent.Mobile, l.svcCtx.Config.Encrypt.SecretKey) if err != nil { logx.Errorf("解密代理手机号失败: %v", err) agentMobile = "" } // 8. 开启事务处理 err = l.svcCtx.AgentWalletModel.Trans(l.ctx, func(ctx context.Context, session sqlx.Session) error { // 8.1 冻结提现金额 wallet.Balance -= req.WithdrawAmount wallet.FrozenAmount += req.WithdrawAmount // 使用版本号乐观锁更新 err := l.svcCtx.AgentWalletModel.UpdateWithVersion(ctx, session, wallet) if err != nil { return errors.Wrapf(err, "冻结金额失败") } // 8.2 创建提现记录 withdrawRecord := &model.AgentWithdraw{ Id: withdrawId, AgentId: agent.Id, WithdrawAmount: req.WithdrawAmount, TaxAmount: 0.00, ActualAmount: 0.00, FrozenAmount: req.WithdrawAmount, AccountName: req.AccountName, BankCardNumber: req.BankCardNumber, BankCardNumberEncrypted: sql.NullString{String: string(encryptedCardNumber), Valid: true}, BankBranch: sql.NullString{String: req.BankBranch, Valid: req.BankBranch != ""}, Status: 0, } // 冗余存储代理手机号和编码 if agentMobile != "" { withdrawRecord.AgentMobile = sql.NullString{String: agentMobile, Valid: true} } if agent.AgentCode > 0 { withdrawRecord.AgentCode = sql.NullInt64{Int64: agent.AgentCode, Valid: true} } _, err = l.svcCtx.AgentWithdrawModel.Insert(ctx, session, withdrawRecord) if err != nil { return errors.Wrapf(err, "创建提现记录失败") } return nil }) if err != nil { return nil, errors.Wrap(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), err.Error()) } logx.Infof("代理 %s 申请提现成功,提现金额: %.2f", agent.Id, req.WithdrawAmount) return &types.CreateWithdrawResp{ WithdrawId: withdrawId, }, nil }