支持平台代管客服一键交接

This commit is contained in:
yml2213
2026-08-20 16:43:45 +08:00
parent b0d4d5bfef
commit cc974f954f
3 changed files with 84 additions and 5 deletions
+1 -1
View File
@@ -139,7 +139,7 @@ type PlatformHandoffRequest struct {
Reason string `json:"reason" binding:"required"` Reason string `json:"reason" binding:"required"`
} }
// ForceHandoffRequest 客服确认普通号主订单已完成线下交接。 // ForceHandoffRequest 客服确认订单已完成交接。
type ForceHandoffRequest struct { type ForceHandoffRequest struct {
Content string `json:"content"` Content string `json:"content"`
Reason string `json:"reason" binding:"required"` Reason string `json:"reason" binding:"required"`
@@ -13,7 +13,13 @@ import (
) )
func canAdminForceHandoff(order model.RentalOrder) bool { func canAdminForceHandoff(order model.RentalOrder) bool {
if isPlatformHandoffOrder(order) || isPlatformSettlementOrder(order) { if isPlatformHandoffOrder(order) {
if order.Status == orderStatusPendingHandoff {
return order.HandoffStatus == handoffStatusPendingRenterConfirm
}
return order.Status == orderStatusAbnormal && order.HandoffStatus == handoffStatusRenterConfirmTimeout
}
if isPlatformSettlementOrder(order) {
return false return false
} }
if order.Status == orderStatusPendingHandoff { if order.Status == orderStatusPendingHandoff {
@@ -24,11 +30,18 @@ func canAdminForceHandoff(order model.RentalOrder) bool {
return order.Status == orderStatusAbnormal && order.HandoffStatus == handoffStatusRenterConfirmTimeout return order.Status == orderStatusAbnormal && order.HandoffStatus == handoffStatusRenterConfirmTimeout
} }
func forceHandoffRecordType(order model.RentalOrder) string {
if isPlatformHandoffOrder(order) {
return "platform_handoff"
}
return "owner_handoff"
}
func forceHandoffNeedsContent(order model.RentalOrder) bool { func forceHandoffNeedsContent(order model.RentalOrder) bool {
return order.HandoffStatus == handoffStatusPendingOwner || order.HandoffStatus == handoffStatusOwnerTimeout return order.HandoffStatus == handoffStatusPendingOwner || order.HandoffStatus == handoffStatusOwnerTimeout
} }
// AdminForceHandoff 由客服确认普通号主订单已完成线下交接,并立即开始租期。 // AdminForceHandoff 由客服确认订单已完成交接,并立即开始租期。
func (r *Repository) AdminForceHandoff(ctx context.Context, adminID uint64, orderID uint64, req ForceHandoffRequest, meta AuditMeta) error { func (r *Repository) AdminForceHandoff(ctx context.Context, adminID uint64, orderID uint64, req ForceHandoffRequest, meta AuditMeta) error {
return r.db.WithContext(ctx).Transaction(func(tx *gorm.DB) error { return r.db.WithContext(ctx).Transaction(func(tx *gorm.DB) error {
var order model.RentalOrder var order model.RentalOrder
@@ -60,7 +73,7 @@ func (r *Repository) AdminForceHandoff(ctx context.Context, adminID uint64, orde
if !needsContent { if !needsContent {
var handoffCount int64 var handoffCount int64
if err := tx.Model(&model.HandoffRecord{}). if err := tx.Model(&model.HandoffRecord{}).
Where("order_id = ? AND type = ?", order.ID, "owner_handoff"). Where("order_id = ? AND type = ?", order.ID, forceHandoffRecordType(order)).
Count(&handoffCount).Error; err != nil { Count(&handoffCount).Error; err != nil {
return err return err
} }
@@ -1079,7 +1079,7 @@ func TestAdminForceHandoffStartsNormalOwnerOrder(t *testing.T) {
} }
} }
func TestAdminForceHandoffUsesExistingOwnerRecordAndRejectsPlatformOrder(t *testing.T) { func TestAdminForceHandoffUsesExistingOwnerRecordAndRejectsPlatformOrderBeforeHandoff(t *testing.T) {
db := setupOrderTestDB(t) db := setupOrderTestDB(t)
if err := db.AutoMigrate(&model.Dispute{}); err != nil { if err := db.AutoMigrate(&model.Dispute{}); err != nil {
t.Fatalf("migrate disputes failed: %v", err) t.Fatalf("migrate disputes failed: %v", err)
@@ -1160,6 +1160,72 @@ func TestAdminForceHandoffUsesExistingOwnerRecordAndRejectsPlatformOrder(t *test
} }
} }
func TestAdminForceHandoffStartsPlatformManagedOrderAfterPlatformHandoff(t *testing.T) {
db := setupOrderTestDB(t)
if err := db.AutoMigrate(&model.Dispute{}); err != nil {
t.Fatalf("migrate disputes failed: %v", err)
}
repo := NewRepository(db)
owner := model.User{Phone: "admin:force-handoff"}
renter := model.User{Phone: "13900003008"}
if err := db.Create(&owner).Error; err != nil {
t.Fatalf("create owner failed: %v", err)
}
if err := db.Create(&renter).Error; err != nil {
t.Fatalf("create renter failed: %v", err)
}
order := model.RentalOrder{
OrderNo: "ORD-FORCE-HANDOFF-PLATFORM-001",
ListingID: 4,
AccountID: 4,
OwnerID: owner.ID,
RenterID: renter.ID,
Status: orderStatusPendingHandoff,
HandoffStatus: handoffStatusPendingRenterConfirm,
HandoffMode: handoffModePlatform,
SettlementMode: settlementModePlatformManaged,
AccountSnapshot: datatypes.JSON([]byte(`{
"haf_coin_amount": 100000000,
"daily_loss_m": 50
}`)),
}
if err := db.Create(&order).Error; err != nil {
t.Fatalf("create order failed: %v", err)
}
if err := db.Create(&model.HandoffRecord{
OrderID: order.ID,
FromUserID: 99,
ToUserID: renter.ID,
Type: "platform_handoff",
Content: "客服已提交账号和登录说明",
}).Error; err != nil {
t.Fatalf("create platform handoff record failed: %v", err)
}
if err := repo.AdminForceHandoff(t.Context(), 99, order.ID, ForceHandoffRequest{
Reason: "租客未确认,客服已核实账号可正常使用",
}, AuditMeta{}); err != nil {
t.Fatalf("AdminForceHandoff() error = %v", err)
}
var saved model.RentalOrder
if err := db.First(&saved, order.ID).Error; err != nil {
t.Fatalf("load order failed: %v", err)
}
if saved.Status != orderStatusRenting || saved.HandoffStatus != handoffStatusReceived {
t.Fatalf("status = %s/%s, want renting/received", saved.Status, saved.HandoffStatus)
}
var record model.HandoffRecord
if err := db.Where("order_id = ? AND type = ?", order.ID, "admin_force_handoff").First(&record).Error; err != nil {
t.Fatalf("find force handoff record failed: %v", err)
}
if record.Content != "客服已确认双方完成交接,订单已进入使用中。" {
t.Fatalf("record content = %q", record.Content)
}
}
func TestSubmitCheckoutRefreshesStageTime(t *testing.T) { func TestSubmitCheckoutRefreshesStageTime(t *testing.T) {
db := setupOrderTestDB(t) db := setupOrderTestDB(t)
repo := NewRepository(db) repo := NewRepository(db)