feat: add payout account review flow
This commit is contained in:
@@ -1,6 +1,9 @@
|
||||
package dto
|
||||
|
||||
import "quyun/v2/app/requests"
|
||||
import (
|
||||
"quyun/v2/app/requests"
|
||||
"quyun/v2/pkg/consts"
|
||||
)
|
||||
|
||||
// SuperPayoutAccountListFilter 超管结算账户列表过滤条件。
|
||||
type SuperPayoutAccountListFilter struct {
|
||||
@@ -17,6 +20,8 @@ type SuperPayoutAccountListFilter struct {
|
||||
Username *string `query:"username"`
|
||||
// Type 账户类型过滤(bank/alipay)。
|
||||
Type *string `query:"type"`
|
||||
// Status 审核状态过滤(pending/approved/rejected)。
|
||||
Status *consts.PayoutAccountStatus `query:"status"`
|
||||
// CreatedAtFrom 创建时间起始(RFC3339)。
|
||||
CreatedAtFrom *string `query:"created_at_from"`
|
||||
// CreatedAtTo 创建时间结束(RFC3339)。
|
||||
@@ -45,8 +50,25 @@ type SuperPayoutAccountItem struct {
|
||||
Account string `json:"account"`
|
||||
// Realname 收款人姓名。
|
||||
Realname string `json:"realname"`
|
||||
// Status 审核状态。
|
||||
Status consts.PayoutAccountStatus `json:"status"`
|
||||
// StatusDescription 审核状态描述(用于展示)。
|
||||
StatusDescription string `json:"status_description"`
|
||||
// ReviewedBy 审核操作者ID。
|
||||
ReviewedBy int64 `json:"reviewed_by"`
|
||||
// ReviewedAt 审核时间(RFC3339)。
|
||||
ReviewedAt string `json:"reviewed_at"`
|
||||
// ReviewReason 审核说明/驳回原因。
|
||||
ReviewReason string `json:"review_reason"`
|
||||
// CreatedAt 创建时间(RFC3339)。
|
||||
CreatedAt string `json:"created_at"`
|
||||
// UpdatedAt 更新时间(RFC3339)。
|
||||
UpdatedAt string `json:"updated_at"`
|
||||
}
|
||||
|
||||
type SuperPayoutAccountReviewForm struct {
|
||||
// Action 审核动作(approve/reject)。
|
||||
Action string `json:"action" validate:"required,oneof=approve reject"`
|
||||
// Reason 审核说明(驳回时必填)。
|
||||
Reason string `json:"reason"`
|
||||
}
|
||||
|
||||
@@ -43,3 +43,21 @@ func (c *payoutAccounts) List(ctx fiber.Ctx, filter *dto.SuperPayoutAccountListF
|
||||
func (c *payoutAccounts) Remove(ctx fiber.Ctx, user *models.User, id int64) error {
|
||||
return services.Super.RemovePayoutAccount(ctx, user.ID, id)
|
||||
}
|
||||
|
||||
// Review payout account
|
||||
//
|
||||
// @Router /super/v1/payout-accounts/:id<int>/review [post]
|
||||
// @Summary Review payout account
|
||||
// @Description Review payout account across tenants
|
||||
// @Tags Finance
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Param id path int64 true "Payout account ID"
|
||||
// @Param form body dto.SuperPayoutAccountReviewForm true "Review form"
|
||||
// @Success 200 {string} string "Reviewed"
|
||||
// @Bind user local key(__ctx_user)
|
||||
// @Bind id path
|
||||
// @Bind form body
|
||||
func (c *payoutAccounts) Review(ctx fiber.Ctx, user *models.User, id int64, form *dto.SuperPayoutAccountReviewForm) error {
|
||||
return services.Super.ReviewPayoutAccount(ctx, user.ID, id, form)
|
||||
}
|
||||
|
||||
@@ -305,6 +305,13 @@ func (r *Routes) Register(router fiber.Router) {
|
||||
r.payoutAccounts.List,
|
||||
Query[dto.SuperPayoutAccountListFilter]("filter"),
|
||||
))
|
||||
r.log.Debugf("Registering route: Post /super/v1/payout-accounts/:id<int>/review -> payoutAccounts.Review")
|
||||
router.Post("/super/v1/payout-accounts/:id<int>/review"[len(r.Path()):], Func3(
|
||||
r.payoutAccounts.Review,
|
||||
Local[*models.User]("__ctx_user"),
|
||||
PathParam[int64]("id"),
|
||||
Body[dto.SuperPayoutAccountReviewForm]("form"),
|
||||
))
|
||||
// Register routes for controller: reports
|
||||
r.log.Debugf("Registering route: Get /super/v1/reports/overview -> reports.Overview")
|
||||
router.Get("/super/v1/reports/overview"[len(r.Path()):], DataFunc1(
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
package dto
|
||||
|
||||
import "quyun/v2/app/requests"
|
||||
import (
|
||||
"quyun/v2/app/requests"
|
||||
"quyun/v2/pkg/consts"
|
||||
)
|
||||
|
||||
type ApplyForm struct {
|
||||
// Name 频道/创作者名称。
|
||||
@@ -206,6 +209,14 @@ type PayoutAccount struct {
|
||||
Account string `json:"account"`
|
||||
// Realname 收款人姓名。
|
||||
Realname string `json:"realname"`
|
||||
// Status 审核状态(pending/approved/rejected)。
|
||||
Status consts.PayoutAccountStatus `json:"status"`
|
||||
// StatusDescription 审核状态描述(用于展示)。
|
||||
StatusDescription string `json:"status_description"`
|
||||
// ReviewedAt 审核时间(RFC3339)。
|
||||
ReviewedAt string `json:"reviewed_at"`
|
||||
// ReviewReason 审核说明/驳回原因。
|
||||
ReviewReason string `json:"review_reason"`
|
||||
}
|
||||
|
||||
type WithdrawForm struct {
|
||||
|
||||
@@ -5,6 +5,7 @@ import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"quyun/v2/app/errorx"
|
||||
@@ -864,7 +865,8 @@ func (s *creator) ListPayoutAccounts(ctx context.Context, tenantID, userID int64
|
||||
return nil, err
|
||||
}
|
||||
|
||||
list, err := models.PayoutAccountQuery.WithContext(ctx).Where(models.PayoutAccountQuery.TenantID.Eq(tid)).Find()
|
||||
tbl, q := models.PayoutAccountQuery.QueryContext(ctx)
|
||||
list, err := q.Where(tbl.TenantID.Eq(tid)).Find()
|
||||
if err != nil {
|
||||
return nil, errorx.ErrDatabaseError.WithCause(err)
|
||||
}
|
||||
@@ -872,11 +874,15 @@ func (s *creator) ListPayoutAccounts(ctx context.Context, tenantID, userID int64
|
||||
var data []creator_dto.PayoutAccount
|
||||
for _, v := range list {
|
||||
data = append(data, creator_dto.PayoutAccount{
|
||||
ID: v.ID,
|
||||
Type: v.Type,
|
||||
Name: v.Name,
|
||||
Account: v.Account,
|
||||
Realname: v.Realname,
|
||||
ID: v.ID,
|
||||
Type: string(v.Type),
|
||||
Name: v.Name,
|
||||
Account: v.Account,
|
||||
Realname: v.Realname,
|
||||
Status: v.Status,
|
||||
StatusDescription: v.Status.Description(),
|
||||
ReviewedAt: s.formatTime(v.ReviewedAt),
|
||||
ReviewReason: v.ReviewReason,
|
||||
})
|
||||
}
|
||||
return data, nil
|
||||
@@ -892,10 +898,11 @@ func (s *creator) AddPayoutAccount(ctx context.Context, tenantID, userID int64,
|
||||
pa := &models.PayoutAccount{
|
||||
TenantID: tid,
|
||||
UserID: uid,
|
||||
Type: form.Type,
|
||||
Type: consts.PayoutAccountType(form.Type),
|
||||
Name: form.Name,
|
||||
Account: form.Account,
|
||||
Realname: form.Realname,
|
||||
Status: consts.PayoutAccountStatusPending,
|
||||
}
|
||||
if err := models.PayoutAccountQuery.WithContext(ctx).Create(pa); err != nil {
|
||||
return errorx.ErrDatabaseError.WithCause(err)
|
||||
@@ -909,9 +916,8 @@ func (s *creator) RemovePayoutAccount(ctx context.Context, tenantID, userID, id
|
||||
return err
|
||||
}
|
||||
|
||||
_, err = models.PayoutAccountQuery.WithContext(ctx).
|
||||
Where(models.PayoutAccountQuery.ID.Eq(id), models.PayoutAccountQuery.TenantID.Eq(tid)).
|
||||
Delete()
|
||||
tbl, q := models.PayoutAccountQuery.QueryContext(ctx)
|
||||
_, err = q.Where(tbl.ID.Eq(id), tbl.TenantID.Eq(tid)).Delete()
|
||||
if err != nil {
|
||||
return errorx.ErrDatabaseError.WithCause(err)
|
||||
}
|
||||
@@ -925,7 +931,7 @@ func (s *creator) Withdraw(ctx context.Context, tenantID, userID int64, form *cr
|
||||
}
|
||||
uid := userID
|
||||
|
||||
// Validate User Real-name Status
|
||||
// 校验用户实名认证状态,未通过不允许提现。
|
||||
user, err := models.UserQuery.WithContext(ctx).Where(models.UserQuery.ID.Eq(uid)).First()
|
||||
if err != nil {
|
||||
return errorx.ErrDatabaseError.WithCause(err)
|
||||
@@ -939,19 +945,25 @@ func (s *creator) Withdraw(ctx context.Context, tenantID, userID int64, form *cr
|
||||
return errorx.ErrBadRequest.WithMsg("金额无效")
|
||||
}
|
||||
|
||||
// Validate Payout Account
|
||||
account, err := models.PayoutAccountQuery.WithContext(ctx).
|
||||
Where(models.PayoutAccountQuery.ID.Eq(form.AccountID), models.PayoutAccountQuery.TenantID.Eq(tid)).
|
||||
First()
|
||||
// 校验收款账户可用性与审核状态。
|
||||
tbl, q := models.PayoutAccountQuery.QueryContext(ctx)
|
||||
account, err := q.Where(tbl.ID.Eq(form.AccountID), tbl.TenantID.Eq(tid)).First()
|
||||
if err != nil {
|
||||
return errorx.ErrRecordNotFound.WithMsg("收款账户不存在")
|
||||
}
|
||||
if account.Status != consts.PayoutAccountStatusApproved {
|
||||
reason := strings.TrimSpace(account.ReviewReason)
|
||||
if account.Status == consts.PayoutAccountStatusRejected && reason != "" {
|
||||
return errorx.ErrPreconditionFailed.WithMsg("收款账户审核未通过:" + reason)
|
||||
}
|
||||
return errorx.ErrPreconditionFailed.WithMsg("收款账户未审核通过")
|
||||
}
|
||||
|
||||
// 将收款账户快照写入订单,便于超管审核与打款核对。
|
||||
snapshotPayload, err := json.Marshal(fields.OrdersWithdrawalSnapshot{
|
||||
Method: form.Method,
|
||||
AccountID: account.ID,
|
||||
AccountType: account.Type,
|
||||
AccountType: string(account.Type),
|
||||
AccountName: account.Name,
|
||||
Account: account.Account,
|
||||
AccountRealname: account.Realname,
|
||||
@@ -1032,6 +1044,13 @@ func (s *creator) getTenantID(ctx context.Context, tenantID, userID int64) (int6
|
||||
return t.ID, nil
|
||||
}
|
||||
|
||||
func (s *creator) formatTime(t time.Time) string {
|
||||
if t.IsZero() {
|
||||
return ""
|
||||
}
|
||||
return t.Format(time.RFC3339)
|
||||
}
|
||||
|
||||
func (s *creator) validateContentAssets(
|
||||
ctx context.Context,
|
||||
tx *models.Query,
|
||||
|
||||
@@ -247,6 +247,7 @@ func (s *CreatorTestSuite) Test_PayoutAccount() {
|
||||
So(err, ShouldBeNil)
|
||||
So(len(list), ShouldEqual, 1)
|
||||
So(list[0].Account, ShouldEqual, "user@example.com")
|
||||
So(list[0].Status, ShouldEqual, consts.PayoutAccountStatusPending)
|
||||
|
||||
// Remove
|
||||
err = Creator.RemovePayoutAccount(ctx, tenantID, u.ID, list[0].ID)
|
||||
@@ -285,10 +286,11 @@ func (s *CreatorTestSuite) Test_Withdraw() {
|
||||
pa := &models.PayoutAccount{
|
||||
TenantID: t.ID,
|
||||
UserID: u.ID,
|
||||
Type: "bank",
|
||||
Type: consts.PayoutAccountTypeBank,
|
||||
Name: "Bank",
|
||||
Account: "123",
|
||||
Realname: "Creator",
|
||||
Status: consts.PayoutAccountStatusApproved,
|
||||
}
|
||||
models.PayoutAccountQuery.WithContext(ctx).Create(pa)
|
||||
|
||||
@@ -316,7 +318,7 @@ func (s *CreatorTestSuite) Test_Withdraw() {
|
||||
var snap fields.OrdersWithdrawalSnapshot
|
||||
So(json.Unmarshal(o.Snapshot.Data().Data, &snap), ShouldBeNil)
|
||||
So(snap.AccountID, ShouldEqual, pa.ID)
|
||||
So(snap.AccountType, ShouldEqual, pa.Type)
|
||||
So(snap.AccountType, ShouldEqual, string(pa.Type))
|
||||
So(snap.AccountName, ShouldEqual, pa.Name)
|
||||
So(snap.Account, ShouldEqual, pa.Account)
|
||||
So(snap.AccountRealname, ShouldEqual, pa.Realname)
|
||||
|
||||
@@ -14,6 +14,7 @@ import (
|
||||
super_dto "quyun/v2/app/http/super/v1/dto"
|
||||
v1_dto "quyun/v2/app/http/v1/dto"
|
||||
"quyun/v2/app/requests"
|
||||
"quyun/v2/database/fields"
|
||||
"quyun/v2/database/models"
|
||||
"quyun/v2/pkg/consts"
|
||||
jwt_provider "quyun/v2/providers/jwt"
|
||||
@@ -1150,7 +1151,10 @@ func (s *super) ListPayoutAccounts(ctx context.Context, filter *super_dto.SuperP
|
||||
q = q.Where(tbl.UserID.Eq(*filter.UserID))
|
||||
}
|
||||
if filter.Type != nil && strings.TrimSpace(*filter.Type) != "" {
|
||||
q = q.Where(tbl.Type.Eq(strings.TrimSpace(*filter.Type)))
|
||||
q = q.Where(tbl.Type.Eq(consts.PayoutAccountType(strings.TrimSpace(*filter.Type))))
|
||||
}
|
||||
if filter.Status != nil && *filter.Status != "" {
|
||||
q = q.Where(tbl.Status.Eq(*filter.Status))
|
||||
}
|
||||
|
||||
tenantIDs, tenantFilter, err := s.lookupTenantIDs(ctx, filter.TenantCode, filter.TenantName)
|
||||
@@ -1273,18 +1277,23 @@ func (s *super) ListPayoutAccounts(ctx context.Context, filter *super_dto.SuperP
|
||||
}
|
||||
|
||||
items = append(items, super_dto.SuperPayoutAccountItem{
|
||||
ID: pa.ID,
|
||||
TenantID: pa.TenantID,
|
||||
TenantCode: tenantCode,
|
||||
TenantName: tenantName,
|
||||
UserID: pa.UserID,
|
||||
Username: username,
|
||||
Type: pa.Type,
|
||||
Name: pa.Name,
|
||||
Account: pa.Account,
|
||||
Realname: pa.Realname,
|
||||
CreatedAt: s.formatTime(pa.CreatedAt),
|
||||
UpdatedAt: s.formatTime(pa.UpdatedAt),
|
||||
ID: pa.ID,
|
||||
TenantID: pa.TenantID,
|
||||
TenantCode: tenantCode,
|
||||
TenantName: tenantName,
|
||||
UserID: pa.UserID,
|
||||
Username: username,
|
||||
Type: string(pa.Type),
|
||||
Name: pa.Name,
|
||||
Account: pa.Account,
|
||||
Realname: pa.Realname,
|
||||
Status: pa.Status,
|
||||
StatusDescription: pa.Status.Description(),
|
||||
ReviewedBy: pa.ReviewedBy,
|
||||
ReviewedAt: s.formatTime(pa.ReviewedAt),
|
||||
ReviewReason: pa.ReviewReason,
|
||||
CreatedAt: s.formatTime(pa.CreatedAt),
|
||||
UpdatedAt: s.formatTime(pa.UpdatedAt),
|
||||
})
|
||||
}
|
||||
|
||||
@@ -1322,6 +1331,84 @@ func (s *super) RemovePayoutAccount(ctx context.Context, operatorID, id int64) e
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *super) ReviewPayoutAccount(ctx context.Context, operatorID, id int64, form *super_dto.SuperPayoutAccountReviewForm) error {
|
||||
if operatorID == 0 {
|
||||
return errorx.ErrUnauthorized.WithMsg("缺少操作者信息")
|
||||
}
|
||||
if id == 0 {
|
||||
return errorx.ErrBadRequest.WithMsg("结算账户ID不能为空")
|
||||
}
|
||||
if form == nil {
|
||||
return errorx.ErrBadRequest.WithMsg("审核参数不能为空")
|
||||
}
|
||||
|
||||
action := strings.ToLower(strings.TrimSpace(form.Action))
|
||||
if action != "approve" && action != "reject" {
|
||||
return errorx.ErrBadRequest.WithMsg("审核动作非法")
|
||||
}
|
||||
reason := strings.TrimSpace(form.Reason)
|
||||
if action == "reject" && reason == "" {
|
||||
return errorx.ErrBadRequest.WithMsg("驳回原因不能为空")
|
||||
}
|
||||
|
||||
nextStatus := consts.PayoutAccountStatusApproved
|
||||
if action == "reject" {
|
||||
nextStatus = consts.PayoutAccountStatusRejected
|
||||
}
|
||||
|
||||
var account *models.PayoutAccount
|
||||
// 事务内校验状态并写入审核结果,避免并发重复审核。
|
||||
err := models.Q.Transaction(func(tx *models.Query) error {
|
||||
tbl, q := tx.PayoutAccount.QueryContext(ctx)
|
||||
existing, err := q.Where(tbl.ID.Eq(id)).First()
|
||||
if err != nil {
|
||||
if errors.Is(err, gorm.ErrRecordNotFound) {
|
||||
return errorx.ErrRecordNotFound.WithMsg("结算账户不存在")
|
||||
}
|
||||
return errorx.ErrDatabaseError.WithCause(err)
|
||||
}
|
||||
if existing.Status != consts.PayoutAccountStatusPending {
|
||||
return errorx.ErrStatusConflict.WithMsg("结算账户已完成审核")
|
||||
}
|
||||
|
||||
now := time.Now()
|
||||
updates := map[string]interface{}{
|
||||
"status": nextStatus,
|
||||
"reviewed_by": operatorID,
|
||||
"reviewed_at": now,
|
||||
"review_reason": reason,
|
||||
"updated_at": now,
|
||||
}
|
||||
if action == "approve" {
|
||||
updates["review_reason"] = ""
|
||||
}
|
||||
if _, err := q.Where(tbl.ID.Eq(existing.ID)).Updates(updates); err != nil {
|
||||
return errorx.ErrDatabaseError.WithCause(err)
|
||||
}
|
||||
account = existing
|
||||
return nil
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
title := "结算账户审核结果"
|
||||
detail := "结算账户审核通过"
|
||||
if action == "reject" {
|
||||
detail = "结算账户审核驳回"
|
||||
if reason != "" {
|
||||
detail += ",原因:" + reason
|
||||
}
|
||||
}
|
||||
if Notification != nil && account != nil {
|
||||
_ = Notification.Send(ctx, account.TenantID, account.UserID, string(consts.NotificationTypeAudit), title, detail)
|
||||
}
|
||||
if Audit != nil && account != nil {
|
||||
Audit.Log(ctx, account.TenantID, operatorID, "review_payout_account", cast.ToString(account.ID), detail)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *super) ListTenantJoinRequests(ctx context.Context, filter *super_dto.SuperTenantJoinRequestListFilter) (*requests.Pager, error) {
|
||||
if filter == nil {
|
||||
filter = &super_dto.SuperTenantJoinRequestListFilter{}
|
||||
@@ -8008,6 +8095,26 @@ func (s *super) ApproveWithdrawal(ctx context.Context, operatorID, id int64) err
|
||||
return errorx.ErrStatusConflict.WithMsg("订单状态不正确")
|
||||
}
|
||||
|
||||
snapshot := o.Snapshot.Data()
|
||||
if snapshot.Kind == "withdrawal" && len(snapshot.Data) > 0 {
|
||||
var snap fields.OrdersWithdrawalSnapshot
|
||||
if err := json.Unmarshal(snapshot.Data, &snap); err != nil {
|
||||
return errorx.ErrInternalError.WithCause(err).WithMsg("解析提现快照失败")
|
||||
}
|
||||
if snap.AccountID > 0 {
|
||||
account, err := models.PayoutAccountQuery.WithContext(ctx).Where(models.PayoutAccountQuery.ID.Eq(snap.AccountID)).First()
|
||||
if err != nil {
|
||||
if errors.Is(err, gorm.ErrRecordNotFound) {
|
||||
return errorx.ErrRecordNotFound.WithMsg("收款账户不存在")
|
||||
}
|
||||
return errorx.ErrDatabaseError.WithCause(err)
|
||||
}
|
||||
if account.Status != consts.PayoutAccountStatusApproved {
|
||||
return errorx.ErrPreconditionFailed.WithMsg("收款账户未审核通过")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Mark as Paid (Assumes external transfer done)
|
||||
_, err = models.OrderQuery.WithContext(ctx).Where(models.OrderQuery.ID.Eq(id)).Updates(&models.Order{
|
||||
Status: consts.OrderStatusPaid,
|
||||
|
||||
@@ -733,3 +733,67 @@ func (s *SuperTestSuite) Test_OrderGovernance() {
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
func (s *SuperTestSuite) Test_PayoutAccountReview() {
|
||||
Convey("PayoutAccountReview", s.T(), func() {
|
||||
ctx := s.T().Context()
|
||||
database.Truncate(ctx, s.DB, models.TableNamePayoutAccount, models.TableNameUser, models.TableNameTenant)
|
||||
|
||||
admin := &models.User{Username: "payout_admin"}
|
||||
owner := &models.User{Username: "payout_owner"}
|
||||
models.UserQuery.WithContext(ctx).Create(admin, owner)
|
||||
|
||||
tenant := &models.Tenant{
|
||||
UserID: owner.ID,
|
||||
Name: "Payout Tenant",
|
||||
Code: "payout",
|
||||
Status: consts.TenantStatusVerified,
|
||||
}
|
||||
models.TenantQuery.WithContext(ctx).Create(tenant)
|
||||
|
||||
account := &models.PayoutAccount{
|
||||
TenantID: tenant.ID,
|
||||
UserID: owner.ID,
|
||||
Type: consts.PayoutAccountTypeBank,
|
||||
Name: "Bank",
|
||||
Account: "123",
|
||||
Realname: "Owner",
|
||||
Status: consts.PayoutAccountStatusPending,
|
||||
}
|
||||
models.PayoutAccountQuery.WithContext(ctx).Create(account)
|
||||
|
||||
Convey("should approve payout account", func() {
|
||||
err := Super.ReviewPayoutAccount(ctx, admin.ID, account.ID, &super_dto.SuperPayoutAccountReviewForm{
|
||||
Action: "approve",
|
||||
})
|
||||
So(err, ShouldBeNil)
|
||||
|
||||
reloaded, _ := models.PayoutAccountQuery.WithContext(ctx).Where(models.PayoutAccountQuery.ID.Eq(account.ID)).First()
|
||||
So(reloaded.Status, ShouldEqual, consts.PayoutAccountStatusApproved)
|
||||
So(reloaded.ReviewedBy, ShouldEqual, admin.ID)
|
||||
So(reloaded.ReviewedAt.IsZero(), ShouldBeFalse)
|
||||
})
|
||||
|
||||
Convey("should require reason when rejecting", func() {
|
||||
account2 := &models.PayoutAccount{
|
||||
TenantID: tenant.ID,
|
||||
UserID: owner.ID,
|
||||
Type: consts.PayoutAccountTypeAlipay,
|
||||
Name: "Alipay",
|
||||
Account: "user@example.com",
|
||||
Realname: "Owner",
|
||||
Status: consts.PayoutAccountStatusPending,
|
||||
}
|
||||
models.PayoutAccountQuery.WithContext(ctx).Create(account2)
|
||||
|
||||
err := Super.ReviewPayoutAccount(ctx, admin.ID, account2.ID, &super_dto.SuperPayoutAccountReviewForm{
|
||||
Action: "reject",
|
||||
})
|
||||
So(err, ShouldNotBeNil)
|
||||
|
||||
var appErr *errorx.AppError
|
||||
So(errors.As(err, &appErr), ShouldBeTrue)
|
||||
So(appErr.Code, ShouldEqual, errorx.ErrBadRequest.Code)
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user