修复平台代管订单流程
This commit is contained in:
@@ -10,6 +10,7 @@ import (
|
||||
|
||||
"hfb_sys/backend/internal/listingstatus"
|
||||
"hfb_sys/backend/internal/model"
|
||||
"hfb_sys/backend/internal/modules/adminnotification"
|
||||
"hfb_sys/backend/internal/modules/notification"
|
||||
"hfb_sys/backend/internal/modules/wallet"
|
||||
"hfb_sys/backend/pkg/money"
|
||||
@@ -62,7 +63,12 @@ func (r *Repository) Arbitrate(ctx context.Context, adminID uint64, id uint64, r
|
||||
order.Status = arbitrateOrderStatus(req.Result)
|
||||
order.SettlementStatus = "arbitrated"
|
||||
order.SettledAt = &now
|
||||
order.OwnerSettledAt = &now
|
||||
if isPlatformManagedOrder(order) {
|
||||
order.OwnerSettledAt = nil
|
||||
applyPlatformManagedSettlement(&order, &settlement)
|
||||
} else {
|
||||
order.OwnerSettledAt = &now
|
||||
}
|
||||
if order.HandoffStatus != "cancelled" && order.HandoffStatus != "returned" {
|
||||
order.HandoffStatus = "arbitrated"
|
||||
}
|
||||
@@ -147,25 +153,32 @@ func (r *Repository) Arbitrate(ctx context.Context, adminID uint64, id uint64, r
|
||||
}); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := notification.Append(tx,
|
||||
notification.Entry{
|
||||
UserID: order.RenterID,
|
||||
Type: "arbitration",
|
||||
Title: "申诉仲裁已完成",
|
||||
Content: "客服已给出仲裁结果,请在订单和申诉记录中查看处理说明。",
|
||||
BizType: "dispute",
|
||||
BizID: &disputeID,
|
||||
},
|
||||
notification.Entry{
|
||||
UserID: order.OwnerID,
|
||||
Type: "arbitration",
|
||||
Title: "申诉仲裁已完成",
|
||||
Content: "客服已给出仲裁结果,请在订单和申诉记录中查看处理说明。",
|
||||
BizType: "dispute",
|
||||
BizID: &disputeID,
|
||||
},
|
||||
); err != nil {
|
||||
return err
|
||||
renterNotification := notification.Entry{
|
||||
UserID: order.RenterID,
|
||||
Type: "arbitration",
|
||||
Title: "申诉仲裁已完成",
|
||||
Content: "客服已给出仲裁结果,请在订单和申诉记录中查看处理说明。",
|
||||
BizType: "dispute",
|
||||
BizID: &disputeID,
|
||||
}
|
||||
if isPlatformManagedOrder(order) {
|
||||
if err := notification.Append(tx, renterNotification); err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
if err := notification.Append(tx,
|
||||
renterNotification,
|
||||
notification.Entry{
|
||||
UserID: order.OwnerID,
|
||||
Type: "arbitration",
|
||||
Title: "申诉仲裁已完成",
|
||||
Content: "客服已给出仲裁结果,请在订单和申诉记录中查看处理说明。",
|
||||
BizType: "dispute",
|
||||
BizID: &disputeID,
|
||||
},
|
||||
); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
})
|
||||
@@ -181,6 +194,42 @@ func (r *Repository) Arbitrate(ctx context.Context, adminID uint64, id uint64, r
|
||||
return &dto, nil
|
||||
}
|
||||
|
||||
func isPlatformManagedOrder(order model.RentalOrder) bool {
|
||||
return order.SettlementMode == "platform_managed" || order.HandoffMode == "platform"
|
||||
}
|
||||
|
||||
func appendPlatformManagedAdminNotification(tx *gorm.DB, order model.RentalOrder, typ string, title string, content string) error {
|
||||
if order.ManagedAdminID == nil || *order.ManagedAdminID == 0 || title == "" {
|
||||
return nil
|
||||
}
|
||||
return adminnotification.Append(tx, adminnotification.Entry{
|
||||
AdminUserID: *order.ManagedAdminID,
|
||||
Type: typ,
|
||||
Title: title,
|
||||
Content: content,
|
||||
})
|
||||
}
|
||||
|
||||
func applyPlatformManagedSettlement(order *model.RentalOrder, settlement *arbitrationSettlement) {
|
||||
if order == nil || settlement == nil {
|
||||
return
|
||||
}
|
||||
filtered := settlement.Entries[:0]
|
||||
for _, entry := range settlement.Entries {
|
||||
if entry.UserID == order.OwnerID && entry.Direction == "in" && entry.BizType == "arbitration_owner_income" {
|
||||
continue
|
||||
}
|
||||
filtered = append(filtered, entry)
|
||||
}
|
||||
settlement.Entries = filtered
|
||||
order.OfflineSettlementAmountCent = settlement.OwnerIncomeAmountCent
|
||||
if settlement.OwnerIncomeAmountCent > 0 {
|
||||
order.OfflineSettlementStatus = "pending"
|
||||
} else {
|
||||
order.OfflineSettlementStatus = "none"
|
||||
}
|
||||
}
|
||||
|
||||
type arbitrationSettlement struct {
|
||||
Entries []wallet.Entry
|
||||
RenterRefundAmountCent int64
|
||||
|
||||
@@ -115,25 +115,35 @@ func (r *Repository) Create(ctx context.Context, userID uint64, orderID uint64,
|
||||
title = "订单进入结账争议"
|
||||
content = "对方已发起结账争议,请等待客服仲裁或补充结账证据。"
|
||||
}
|
||||
if err := notification.Append(tx,
|
||||
notification.Entry{
|
||||
UserID: targetID,
|
||||
Type: "dispute",
|
||||
Title: title,
|
||||
Content: content,
|
||||
BizType: "dispute",
|
||||
BizID: &disputeID,
|
||||
},
|
||||
notification.Entry{
|
||||
UserID: userID,
|
||||
Type: "dispute",
|
||||
Title: "申诉已提交",
|
||||
Content: "申诉已进入待处理状态,客服仲裁后会通知双方。",
|
||||
BizType: "dispute",
|
||||
BizID: &disputeID,
|
||||
},
|
||||
); err != nil {
|
||||
return err
|
||||
initiatorNotification := notification.Entry{
|
||||
UserID: userID,
|
||||
Type: "dispute",
|
||||
Title: "申诉已提交",
|
||||
Content: "申诉已进入待处理状态,客服仲裁后会通知双方。",
|
||||
BizType: "dispute",
|
||||
BizID: &disputeID,
|
||||
}
|
||||
if isPlatformManagedOrder(order) {
|
||||
if err := appendPlatformManagedAdminNotification(tx, order, "dispute", title, content); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := notification.Append(tx, initiatorNotification); err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
if err := notification.Append(tx,
|
||||
notification.Entry{
|
||||
UserID: targetID,
|
||||
Type: "dispute",
|
||||
Title: title,
|
||||
Content: content,
|
||||
BizType: "dispute",
|
||||
BizID: &disputeID,
|
||||
},
|
||||
initiatorNotification,
|
||||
); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
createdID = row.ID
|
||||
return nil
|
||||
|
||||
@@ -169,6 +169,76 @@ func TestRepositoryCancelRestoresCheckoutDisputeStatus(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestPlatformManagedArbitrationUsesOfflineSettlement(t *testing.T) {
|
||||
db := setupDisputeTestDB(t)
|
||||
repo := NewRepository(db)
|
||||
adminID := uint64(77)
|
||||
|
||||
owner, renter, order := createDisputeOrderFixture(t, db, model.RentalOrder{
|
||||
Status: "renting",
|
||||
HandoffStatus: "received",
|
||||
SettlementStatus: "unsettled",
|
||||
HandoffMode: "platform",
|
||||
SettlementMode: "platform_managed",
|
||||
ManagedAdminID: &adminID,
|
||||
RentAmountCent: 10000,
|
||||
OwnerRentAmountCent: 8000,
|
||||
PlatformFeeCent: 2000,
|
||||
OfflineSettlementStatus: "none",
|
||||
OfflineSettlementAmountCent: 0,
|
||||
})
|
||||
|
||||
created, err := repo.Create(t.Context(), renter.ID, order.ID, CreateRequest{
|
||||
Type: "cannot_login",
|
||||
Description: "外部上传账号需要客服处理",
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("创建申诉失败: %v", err)
|
||||
}
|
||||
var ownerNotificationCount int64
|
||||
if err := db.Model(&model.Notification{}).Where("user_id = ?", owner.ID).Count(&ownerNotificationCount).Error; err != nil {
|
||||
t.Fatalf("统计号主通知失败: %v", err)
|
||||
}
|
||||
if ownerNotificationCount != 0 {
|
||||
t.Fatalf("owner notification count = %d, want 0", ownerNotificationCount)
|
||||
}
|
||||
var adminNotificationCount int64
|
||||
if err := db.Model(&model.AdminNotification{}).Where("admin_user_id = ?", adminID).Count(&adminNotificationCount).Error; err != nil {
|
||||
t.Fatalf("统计客服通知失败: %v", err)
|
||||
}
|
||||
if adminNotificationCount == 0 {
|
||||
t.Fatal("platform managed dispute should notify managed admin")
|
||||
}
|
||||
|
||||
if _, err := repo.Arbitrate(t.Context(), adminID, created.ID, ArbitrateRequest{
|
||||
Result: "release_deposit",
|
||||
Remark: "确认租金给卖家,线下结算",
|
||||
}, AuditMeta{}); err != nil {
|
||||
t.Fatalf("仲裁失败: %v", err)
|
||||
}
|
||||
|
||||
var saved model.RentalOrder
|
||||
if err := db.First(&saved, order.ID).Error; err != nil {
|
||||
t.Fatalf("读取订单失败: %v", err)
|
||||
}
|
||||
if saved.OfflineSettlementStatus != "pending" {
|
||||
t.Fatalf("offline settlement status = %q, want pending", saved.OfflineSettlementStatus)
|
||||
}
|
||||
if saved.OfflineSettlementAmountCent != 8000 {
|
||||
t.Fatalf("offline settlement amount = %d, want 8000", saved.OfflineSettlementAmountCent)
|
||||
}
|
||||
if saved.OwnerSettledAt != nil {
|
||||
t.Fatalf("owner settled at = %#v, want nil before offline settlement", saved.OwnerSettledAt)
|
||||
}
|
||||
var ownerLedgerCount int64
|
||||
if err := db.Model(&model.WalletLedger{}).Where("user_id = ?", owner.ID).Count(&ownerLedgerCount).Error; err != nil {
|
||||
t.Fatalf("统计钱包流水失败: %v", err)
|
||||
}
|
||||
if ownerLedgerCount != 0 {
|
||||
t.Fatalf("owner wallet ledger count = %d, want 0", ownerLedgerCount)
|
||||
}
|
||||
}
|
||||
|
||||
func TestBuildArbitrationSettlementSkipsFrozenReleaseWhenNoFrozenBalance(t *testing.T) {
|
||||
order := model.RentalOrder{
|
||||
ID: 11,
|
||||
|
||||
Reference in New Issue
Block a user