优化申诉仲裁流程和证据展示

This commit is contained in:
yml2213
2026-06-13 21:26:38 +08:00
parent 529ad1e433
commit 3ef7705776
22 changed files with 1455 additions and 138 deletions
@@ -3,10 +3,187 @@ package dispute
import (
"strings"
"testing"
"time"
"hfb_sys/backend/internal/model"
"gorm.io/driver/sqlite"
"gorm.io/gorm"
"gorm.io/gorm/logger"
)
func setupDisputeTestDB(t *testing.T) *gorm.DB {
t.Helper()
db, err := gorm.Open(sqlite.Open(":memory:"), &gorm.Config{
Logger: logger.Default.LogMode(logger.Silent),
})
if err != nil {
t.Fatalf("无法创建测试数据库: %v", err)
}
if err := db.AutoMigrate(
&model.User{},
&model.GameAccount{},
&model.RentalListing{},
&model.RentalOrder{},
&model.OrderCheckout{},
&model.HandoffRecord{},
&model.Dispute{},
&model.Notification{},
); err != nil {
t.Fatalf("数据库迁移失败: %v", err)
}
return db
}
func createDisputeOrderFixture(t *testing.T, db *gorm.DB, order model.RentalOrder) (model.User, model.User, model.RentalOrder) {
t.Helper()
owner := model.User{Phone: "13800000001"}
if err := db.Create(&owner).Error; err != nil {
t.Fatalf("创建号主失败: %v", err)
}
renter := model.User{Phone: "13800000002"}
if err := db.Create(&renter).Error; err != nil {
t.Fatalf("创建租客失败: %v", err)
}
account := model.GameAccount{
OwnerID: owner.ID,
Title: "测试账号",
ServerRegion: "国服",
LoginPlatform: "安卓",
}
if err := db.Create(&account).Error; err != nil {
t.Fatalf("创建账号失败: %v", err)
}
listing := model.RentalListing{
AccountID: account.ID,
OwnerID: owner.ID,
PriceCent: 1000,
Status: "rented",
ReviewStatus: "approved",
InTransaction: true,
}
if err := db.Create(&listing).Error; err != nil {
t.Fatalf("创建商品失败: %v", err)
}
order.OrderNo = "RO202606130001"
order.ListingID = listing.ID
order.AccountID = account.ID
order.OwnerID = owner.ID
order.RenterID = renter.ID
if err := db.Create(&order).Error; err != nil {
t.Fatalf("创建订单失败: %v", err)
}
return owner, renter, order
}
func TestRepositoryCancelRestoresNormalDisputeOrderStatus(t *testing.T) {
db := setupDisputeTestDB(t)
repo := NewRepository(db)
_, renter, order := createDisputeOrderFixture(t, db, model.RentalOrder{
Status: "renting",
HandoffStatus: "received",
SettlementStatus: "unsettled",
})
created, err := repo.Create(t.Context(), renter.ID, order.ID, CreateRequest{
Type: "cannot_login",
Description: "无法登录账号",
})
if err != nil {
t.Fatalf("创建申诉失败: %v", err)
}
if created.Status != "open" {
t.Fatalf("申诉状态 = %s, want open", created.Status)
}
cancelled, err := repo.CancelByOrder(t.Context(), renter.ID, order.ID)
if err != nil {
t.Fatalf("取消申诉失败: %v", err)
}
if cancelled.Status != "cancelled" {
t.Fatalf("取消后申诉状态 = %s, want cancelled", cancelled.Status)
}
var restored model.RentalOrder
if err := db.First(&restored, order.ID).Error; err != nil {
t.Fatalf("读取订单失败: %v", err)
}
if restored.Status != "renting" || restored.HandoffStatus != "received" || restored.SettlementStatus != "unsettled" {
t.Fatalf("订单状态未恢复: status=%s handoff=%s settlement=%s", restored.Status, restored.HandoffStatus, restored.SettlementStatus)
}
var records []model.HandoffRecord
if err := db.Where("order_id = ?", order.ID).Order("id ASC").Find(&records).Error; err != nil {
t.Fatalf("读取交接记录失败: %v", err)
}
if len(records) != 2 {
t.Fatalf("交接记录数量 = %d, want 2", len(records))
}
if records[0].Type != "dispute_opened" || records[1].Type != "dispute_cancelled" {
t.Fatalf("交接记录类型 = %s/%s, want dispute_opened/dispute_cancelled", records[0].Type, records[1].Type)
}
}
func TestRepositoryCancelRestoresCheckoutDisputeStatus(t *testing.T) {
db := setupDisputeTestDB(t)
repo := NewRepository(db)
now := time.Now()
_, renter, order := createDisputeOrderFixture(t, db, model.RentalOrder{
Status: "pending_checkout_accept",
HandoffStatus: "pending_renter_checkout",
SettlementStatus: "pending",
})
checkout := model.OrderCheckout{
OrderID: order.ID,
InitiatedBy: renter.ID,
Status: "countered",
OwnerAdjustmentReason: "补扣押金",
OwnerAdjustedAt: &now,
}
if err := db.Create(&checkout).Error; err != nil {
t.Fatalf("创建结账记录失败: %v", err)
}
if _, err := repo.Create(t.Context(), renter.ID, order.ID, CreateRequest{
Type: "checkout_amount",
Description: "不同意结账修正",
}); err != nil {
t.Fatalf("创建结账争议失败: %v", err)
}
if _, err := repo.CancelByOrder(t.Context(), renter.ID, order.ID); err != nil {
t.Fatalf("取消结账争议失败: %v", err)
}
var restored model.RentalOrder
if err := db.First(&restored, order.ID).Error; err != nil {
t.Fatalf("读取订单失败: %v", err)
}
if restored.Status != "pending_checkout_accept" || restored.HandoffStatus != "pending_renter_checkout" || restored.SettlementStatus != "pending" {
t.Fatalf("订单状态未恢复: status=%s handoff=%s settlement=%s", restored.Status, restored.HandoffStatus, restored.SettlementStatus)
}
var restoredCheckout model.OrderCheckout
if err := db.First(&restoredCheckout, checkout.ID).Error; err != nil {
t.Fatalf("读取结账记录失败: %v", err)
}
if restoredCheckout.Status != "countered" {
t.Fatalf("结账状态 = %s, want countered", restoredCheckout.Status)
}
if restoredCheckout.RenterRejectedAt != nil {
t.Fatalf("取消争议后 renter_rejected_at 应被清空")
}
var records []model.HandoffRecord
if err := db.Where("order_id = ?", order.ID).Order("id ASC").Find(&records).Error; err != nil {
t.Fatalf("读取交接记录失败: %v", err)
}
if len(records) != 2 {
t.Fatalf("交接记录数量 = %d, want 2", len(records))
}
if records[0].Type != "checkout_dispute_opened" || records[1].Type != "checkout_dispute_cancelled" {
t.Fatalf("交接记录类型 = %s/%s, want checkout_dispute_opened/checkout_dispute_cancelled", records[0].Type, records[1].Type)
}
}
func TestBuildArbitrationSettlementSkipsFrozenReleaseWhenNoFrozenBalance(t *testing.T) {
order := model.RentalOrder{
ID: 11,