增加零号主收入结算确认

This commit is contained in:
yml2213
2026-08-29 01:31:08 +08:00
parent 527e5ed228
commit 2487a2d83f
7 changed files with 101 additions and 5 deletions
+1
View File
@@ -134,6 +134,7 @@ type CounterCheckoutRequest struct {
type AdminActionRequest struct {
Reason string `json:"reason" binding:"required"`
ConfirmZeroOwnerIncome bool `json:"confirm_zero_owner_income"`
}
type PlatformHandoffRequest struct {
@@ -44,6 +44,8 @@ func writeOrderError(c *gin.Context, err error) {
response.Error(c, http.StatusConflict, "checkout_cannot_submit", "当前订单不能发起结账")
case errors.Is(err, ErrCheckoutCannotConfirm):
response.Error(c, http.StatusConflict, "checkout_cannot_confirm", "当前结账不能确认")
case errors.Is(err, ErrZeroOwnerIncomeConfirmation):
response.Error(c, http.StatusConflict, "zero_owner_income_confirmation_required", "号主收入为 0,请确认后再结算")
case errors.Is(err, ErrCheckoutCannotCounter):
response.Error(c, http.StatusConflict, "checkout_cannot_counter", "当前结账不能修改")
case errors.Is(err, ErrCheckoutMaxRounds):
@@ -159,6 +159,9 @@ func (r *Repository) AdminPlatformCheckoutConfirm(ctx context.Context, adminID u
if normalizeCheckoutTurn(checkout) != checkoutTurnOwner {
return ErrCheckoutCannotConfirm
}
if checkout.OwnerIncomeAmountCent == 0 && !req.ConfirmZeroOwnerIncome {
return ErrZeroOwnerIncomeConfirmation
}
if err := ensureCheckoutCompletable(order, checkout); err != nil {
return err
}
@@ -196,6 +199,7 @@ func (r *Repository) AdminPlatformCheckoutConfirm(ctx context.Context, adminID u
"after_settlement_status": order.SettlementStatus,
"offline_settlement_status": order.OfflineSettlementStatus,
"offline_settlement_amount_cent": order.OfflineSettlementAmountCent,
"confirm_zero_owner_income": req.ConfirmZeroOwnerIncome,
})
})
if err != nil {
@@ -2,6 +2,7 @@ package order
import (
"context"
"errors"
"testing"
"time"
@@ -1794,3 +1795,48 @@ func createOpenPlatformCheckout(t *testing.T, db *gorm.DB, order model.RentalOrd
}
return checkout
}
func TestAdminPlatformCheckoutConfirmRequiresExplicitConfirmationForZeroOwnerIncome(t *testing.T) {
db := setupOrderTestDB(t)
repo := NewRepository(db, Dependencies{
RefundStarter: RefundStarterFunc(func(context.Context, uint64, int64, string, string) (string, error) {
return "pending", nil
}),
})
const adminID uint64 = 7001
_, renter, order := createPlatformManagedCheckoutOrder(t, db, adminID)
checkout := createOpenPlatformCheckout(t, db, order, renter.ID)
checkout.OwnerIncomeAmountCent = 0
if err := db.Save(&checkout).Error; err != nil {
t.Fatalf("将号主收入调整为零失败: %v", err)
}
err := repo.AdminPlatformCheckoutConfirm(t.Context(), *order.ManagedAdminID, order.ID, AdminActionRequest{
Reason: "客服核对结算",
}, AuditMeta{})
if !errors.Is(err, ErrZeroOwnerIncomeConfirmation) {
t.Fatalf("未确认零收入时 error = %v, want %v", err, ErrZeroOwnerIncomeConfirmation)
}
var pending model.RentalOrder
if err := db.First(&pending, order.ID).Error; err != nil {
t.Fatalf("读取订单失败: %v", err)
}
if pending.Status != orderStatusPendingCheckoutConfirm {
t.Fatalf("未确认零收入后订单状态 = %s, want %s", pending.Status, orderStatusPendingCheckoutConfirm)
}
if err := repo.AdminPlatformCheckoutConfirm(t.Context(), *order.ManagedAdminID, order.ID, AdminActionRequest{
Reason: "已确认号主收入为零",
ConfirmZeroOwnerIncome: true,
}, AuditMeta{}); err != nil {
t.Fatalf("确认零收入后结算失败: %v", err)
}
var completed model.RentalOrder
if err := db.First(&completed, order.ID).Error; err != nil {
t.Fatalf("读取完成订单失败: %v", err)
}
if completed.Status != orderStatusCompleted {
t.Fatalf("确认零收入后订单状态 = %s, want %s", completed.Status, orderStatusCompleted)
}
}
@@ -22,6 +22,7 @@ var (
ErrOrderCannotComplete = errors.New("order cannot complete")
ErrCheckoutCannotSubmit = errors.New("checkout cannot submit")
ErrCheckoutCannotConfirm = errors.New("checkout cannot confirm")
ErrZeroOwnerIncomeConfirmation = errors.New("zero owner income confirmation required")
ErrCheckoutCannotCounter = errors.New("checkout cannot counter")
ErrCheckoutMaxRounds = errors.New("checkout max rounds reached")
ErrCheckoutDepositShortfall = errors.New("checkout deposit shortfall")
@@ -68,6 +68,7 @@ type OrderActionType =
| ''
const actionType = ref<OrderActionType>('')
const reason = ref('')
const zeroOwnerIncomeConfirmed = ref(false)
const refundStatus = ref<RefundStatus | null>(null)
const platformHandoffVisible = ref(false)
const platformHandoffContent = ref('')
@@ -476,7 +477,11 @@ async function submitAction() {
await adminResetHandoff(order.value.id, reason.value)
ElMessage.success(`${resetActionLabel.value}成功`)
} else if (actionType.value === 'platform_checkout_confirm') {
await adminPlatformCheckoutConfirm(order.value.id, reason.value)
await adminPlatformCheckoutConfirm(
order.value.id,
reason.value,
zeroOwnerIncomeConfirmed.value
)
ElMessage.success('结账已确认')
} else if (actionType.value === 'platform_checkout_dispute') {
await adminPlatformCheckoutDispute(order.value.id, reason.value)
@@ -500,6 +505,28 @@ async function submitAction() {
}
}
async function confirmPlatformCheckout() {
if (!order.value) return
zeroOwnerIncomeConfirmed.value = false
if (Number(order.value.checkout?.owner_income_amount_cent ?? 0) === 0) {
try {
await ElMessageBox.confirm(
'本次结算的号主收入为 ¥0.0。确认后将完成结算,请再次确认是否继续。',
'号主收入为零',
{
confirmButtonText: '仍确认结算',
cancelButtonText: '取消',
type: 'warning',
}
)
} catch {
return
}
zeroOwnerIncomeConfirmed.value = true
}
openAction('platform_checkout_confirm')
}
function openPlatformHandoff() {
platformHandoffContent.value = ''
platformHandoffReason.value = ''
@@ -952,7 +979,7 @@ function paymentPaidAt(record: AdminPayment) {
<el-button
v-if="canPlatformCheckoutConfirm"
type="primary"
@click="openAction('platform_checkout_confirm')"
@click="confirmPlatformCheckout"
>
{{ platformCheckoutConfirmAction?.label || '客服确认结账' }}
</el-button>
@@ -1500,6 +1527,17 @@ function paymentPaidAt(record: AdminPayment) {
<p>
<strong>{{ order.order_no }}</strong> · 商品编号 {{ listingCode }} · {{ order.title }}
</p>
<el-alert
v-if="
actionType === 'platform_checkout_confirm' &&
Number(order.checkout?.owner_income_amount_cent ?? 0) === 0
"
title="号主收入为零"
description="本次结算将按号主收入 ¥0.0 完成,请确认无误后再继续。"
type="warning"
:closable="false"
show-icon
/>
<el-input
v-model="reason"
type="textarea"
+6 -2
View File
@@ -458,10 +458,14 @@ export async function adminForceHandoff(id: number, content: string, reason: str
return data.data
}
export async function adminPlatformCheckoutConfirm(id: number, reason: string) {
export async function adminPlatformCheckoutConfirm(
id: number,
reason: string,
confirmZeroOwnerIncome = false
) {
const { data } = await apiClient.post<ApiResponse<{ confirmed: boolean }>>(
`/admin/orders/${id}/platform-checkout/confirm`,
{ reason }
{ reason, confirm_zero_owner_income: confirmZeroOwnerIncome }
)
return data.data
}