fix(order): 支持待号主交接及异常状态下客服重置交接倒计时

This commit is contained in:
yml2213
2026-09-09 16:05:48 +08:00
parent 02d091434e
commit 17d4f7be9e
4 changed files with 114 additions and 6 deletions
@@ -254,7 +254,8 @@ func (r *Repository) AdminResetHandoff(ctx context.Context, adminID uint64, orde
}
func resetTargetForOrder(order model.RentalOrder) (adminResetTarget, bool) {
if order.Status == orderStatusPendingHandoff && order.HandoffStatus == handoffStatusOwnerTimeout {
if (order.Status == orderStatusPendingHandoff || order.Status == orderStatusAbnormal) &&
(order.HandoffStatus == handoffStatusOwnerTimeout || order.HandoffStatus == handoffStatusPendingOwner) {
return adminResetTarget{
OrderStatus: orderStatusPendingHandoff,
HandoffStatus: handoffStatusPendingOwner,
@@ -267,7 +268,8 @@ func resetTargetForOrder(order model.RentalOrder) (adminResetTarget, bool) {
RefreshStageTime: true,
}, true
}
if order.Status == orderStatusAbnormal && order.HandoffStatus == handoffStatusRenterConfirmTimeout {
if (order.Status == orderStatusPendingHandoff || order.Status == orderStatusAbnormal) &&
(order.HandoffStatus == handoffStatusRenterConfirmTimeout || order.HandoffStatus == handoffStatusPendingRenterConfirm) {
return adminResetTarget{
OrderStatus: orderStatusPendingHandoff,
HandoffStatus: handoffStatusPendingRenterConfirm,
@@ -280,9 +282,10 @@ func resetTargetForOrder(order model.RentalOrder) (adminResetTarget, bool) {
RefreshStageTime: true,
}, true
}
if order.Status == orderStatusAbnormal &&
if (order.Status == orderStatusPendingCheckoutConfirm || order.Status == orderStatusAbnormal) &&
(order.HandoffStatus == handoffStatusOwnerCheckoutConfirmTimeout ||
order.HandoffStatus == handoffStatusOwnerReturnConfirmTimeout) {
order.HandoffStatus == handoffStatusOwnerReturnConfirmTimeout ||
order.HandoffStatus == handoffStatusPendingOwnerCheckout) {
return adminResetTarget{
OrderStatus: orderStatusPendingCheckoutConfirm,
HandoffStatus: handoffStatusPendingOwnerCheckout,
@@ -295,7 +298,8 @@ func resetTargetForOrder(order model.RentalOrder) (adminResetTarget, bool) {
RefreshStageTime: true,
}, true
}
if order.Status == orderStatusOverdue && order.HandoffStatus == handoffStatusReturnOverdue {
if (order.Status == orderStatusOverdue || order.Status == orderStatusRenting || order.Status == orderStatusAbnormal) &&
order.HandoffStatus == handoffStatusReturnOverdue {
return adminResetTarget{
OrderStatus: orderStatusRenting,
HandoffStatus: handoffStatusReceived,
@@ -35,7 +35,7 @@ func writeOrderError(c *gin.Context, err error) {
case errors.Is(err, ErrOrderCannotForceHandoff):
response.Error(c, http.StatusConflict, "order_cannot_force_handoff", "当前订单不能客服一键交接")
case errors.Is(err, ErrOrderCannotResetHandoff):
response.Error(c, http.StatusConflict, "order_cannot_reset_handoff", "仅超时卡住的订单可重置到对应待办阶段")
response.Error(c, http.StatusConflict, "order_cannot_reset_handoff", "当前订单状态不可重置待办")
case errors.Is(err, ErrOrderCannotReceive):
response.Error(c, http.StatusConflict, "order_cannot_receive", "当前订单不能确认收号")
case errors.Is(err, ErrOrderCannotReturn):
@@ -1026,6 +1026,101 @@ func TestCancelDoesNotReleaseAssetsWhenSiblingOrderActive(t *testing.T) {
}
}
func TestAdminResetOwnerHandoff(t *testing.T) {
db := setupOrderTestDB(t)
repo := NewRepository(db)
owner := model.User{Phone: "13800003009"}
renter := model.User{Phone: "13900003009"}
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)
}
// 1. 测试在待号主交接(pending_owner)状态下重置交接
oldTime := time.Now().Add(-1 * time.Hour)
order := model.RentalOrder{
OrderNo: "ORD-RESET-OWNER-001",
ListingID: 1,
AccountID: 1,
OwnerID: owner.ID,
RenterID: renter.ID,
Status: orderStatusPendingHandoff,
HandoffStatus: handoffStatusPendingOwner,
HandoffStartedAt: &oldTime,
SettlementStatus: settlementStatusPending,
}
if err := db.Create(&order).Error; err != nil {
t.Fatalf("create order failed: %v", err)
}
actions := adminActionsForOrder(order)
if actions == nil || actions.ResetHandoff == nil || !actions.ResetHandoff.Enabled {
t.Fatalf("expected ResetHandoff action to be enabled for pending_owner, got %+v", actions)
}
err := repo.AdminResetHandoff(t.Context(), 1, order.ID, AdminActionRequest{Reason: "客服协调重置交接时限"}, AuditMeta{})
if err != nil {
t.Fatalf("AdminResetHandoff() 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 != orderStatusPendingHandoff {
t.Fatalf("status = %q, want %q", saved.Status, orderStatusPendingHandoff)
}
if saved.HandoffStatus != handoffStatusPendingOwner {
t.Fatalf("handoff status = %q, want %q", saved.HandoffStatus, handoffStatusPendingOwner)
}
if saved.HandoffStartedAt == nil || !saved.HandoffStartedAt.After(oldTime) {
t.Fatal("handoff started at should be refreshed to current time")
}
// 2. 测试在号主交接超时(owner_timeout)状态下重置交接
orderTimeout := model.RentalOrder{
OrderNo: "ORD-RESET-OWNER-002",
ListingID: 1,
AccountID: 1,
OwnerID: owner.ID,
RenterID: renter.ID,
Status: orderStatusPendingHandoff,
HandoffStatus: handoffStatusOwnerTimeout,
HandoffStartedAt: &oldTime,
SettlementStatus: settlementStatusPending,
}
if err := db.Create(&orderTimeout).Error; err != nil {
t.Fatalf("create order failed: %v", err)
}
actionsTimeout := adminActionsForOrder(orderTimeout)
if actionsTimeout == nil || actionsTimeout.ResetHandoff == nil || !actionsTimeout.ResetHandoff.Enabled {
t.Fatalf("expected ResetHandoff action to be enabled for owner_timeout, got %+v", actionsTimeout)
}
err = repo.AdminResetHandoff(t.Context(), 1, orderTimeout.ID, AdminActionRequest{Reason: "超时后人工重置交接"}, AuditMeta{})
if err != nil {
t.Fatalf("AdminResetHandoff() error = %v", err)
}
var savedTimeout model.RentalOrder
if err := db.First(&savedTimeout, orderTimeout.ID).Error; err != nil {
t.Fatalf("load order failed: %v", err)
}
if savedTimeout.Status != orderStatusPendingHandoff {
t.Fatalf("status = %q, want %q", savedTimeout.Status, orderStatusPendingHandoff)
}
if savedTimeout.HandoffStatus != handoffStatusPendingOwner {
t.Fatalf("handoff status = %q, want %q", savedTimeout.HandoffStatus, handoffStatusPendingOwner)
}
if savedTimeout.HandoffStartedAt == nil || !savedTimeout.HandoffStartedAt.After(oldTime) {
t.Fatal("handoff started at should be refreshed to current time")
}
}
func TestAdminResetCheckoutConfirmTimeout(t *testing.T) {
db := setupOrderTestDB(t)
repo := NewRepository(db)