package user import ( "context" "github.com/perfect-panel/server/internal/model/user" "github.com/perfect-panel/server/internal/svc" "github.com/perfect-panel/server/internal/types" "github.com/perfect-panel/server/pkg/constant" "github.com/perfect-panel/server/pkg/logger" "github.com/perfect-panel/server/pkg/xerr" "github.com/pkg/errors" "gorm.io/gorm" ) type CommissionWithdrawLogic struct { logger.Logger ctx context.Context svcCtx *svc.ServiceContext } // Commission Withdraw func NewCommissionWithdrawLogic(ctx context.Context, svcCtx *svc.ServiceContext) *CommissionWithdrawLogic { return &CommissionWithdrawLogic{ Logger: logger.WithContext(ctx), ctx: ctx, svcCtx: svcCtx, } } func (l *CommissionWithdrawLogic) CommissionWithdraw(req *types.CommissionWithdrawRequest) (resp *types.WithdrawalLog, err error) { u, ok := l.ctx.Value(constant.CtxKeyUser).(*user.User) if !ok { logger.Error("current user is not found in context") return nil, errors.Wrapf(xerr.NewErrCode(xerr.InvalidAccess), "Invalid Access") } // Sum all pending (status=0) withdrawals to compute available balance. // Available = commission - pendingTotal; commission is only deducted on approval. var pendingTotal int64 if err = l.svcCtx.DB.WithContext(l.ctx). Model(&user.Withdrawal{}). Where("user_id = ? AND status = 0", u.Id). Select("COALESCE(SUM(amount), 0)"). Scan(&pendingTotal).Error; err != nil { l.Errorf("Failed to query pending withdrawals for user %d: %v", u.Id, err) return nil, errors.Wrapf(xerr.NewErrCode(xerr.DatabaseQueryError), "Failed to query pending withdrawals for user %d", u.Id) } if u.Commission < req.Amount+pendingTotal { logger.Errorf("User %d insufficient available commission: total=%d pending=%d requested=%d", u.Id, u.Commission, pendingTotal, req.Amount) return nil, errors.Wrapf(xerr.NewErrCode(xerr.UserCommissionNotEnough), "User %d has insufficient commission balance", u.Id) } var w user.Withdrawal err = l.svcCtx.DB.WithContext(l.ctx).Transaction(func(tx *gorm.DB) error { w = user.Withdrawal{ UserId: u.Id, Amount: req.Amount, Content: req.Content, Status: 0, Reason: "", } return tx.Create(&w).Error }) if err != nil { l.Errorf("Failed to create withdrawal for user %d: %v", u.Id, err) return nil, errors.Wrapf(xerr.NewErrCode(xerr.DatabaseInsertError), "Failed to create withdrawal for user %d: %v", u.Id, err) } return &types.WithdrawalLog{ UserId: u.Id, Amount: req.Amount, Content: req.Content, Status: 0, Reason: "", CreatedAt: w.CreatedAt.UnixMilli(), UpdatedAt: w.UpdatedAt.UnixMilli(), }, nil }