修复归还逾期订单重置

This commit is contained in:
yml2213
2026-06-21 09:28:50 +08:00
parent fcf0230e04
commit b2e52e1d75
2 changed files with 116 additions and 0 deletions
@@ -173,6 +173,7 @@ type adminResetTarget struct {
RenterContent string
AuditAction string
RefreshStageTime bool
RecalculateEnd bool
}
// AdminResetHandoff 将超时卡住的订单恢复到对应待办阶段,并刷新阶段计时,
@@ -195,6 +196,21 @@ func (r *Repository) AdminResetHandoff(ctx context.Context, adminID uint64, orde
if target.RefreshStageTime {
order.HandoffStartedAt = &now
}
if target.RecalculateEnd {
durationHours := estimateOrderDurationHours(order.AccountSnapshot)
if durationHours > 0 {
order.EstimatedDurationHours = durationHours
}
if order.RentedAt == nil {
order.RentedAt = &now
}
if order.RentedAt != nil {
estimatedEndAt := order.RentedAt.Add(time.Duration(order.EstimatedDurationHours) * time.Hour)
if !estimatedEndAt.After(now) {
order.RentedAt = &now
}
}
}
if err := notification.Append(tx,
notification.Entry{
UserID: order.OwnerID,
@@ -272,6 +288,19 @@ func resetTargetForOrder(order model.RentalOrder) (adminResetTarget, bool) {
RefreshStageTime: true,
}, true
}
if order.Status == orderStatusOverdue && order.HandoffStatus == handoffStatusReturnOverdue {
return adminResetTarget{
OrderStatus: orderStatusRenting,
HandoffStatus: handoffStatusReceived,
Label: "重置归还逾期",
OwnerTitle: "订单归还逾期已重置",
OwnerContent: "客服已重置归还逾期状态,订单恢复为使用中。",
RenterTitle: "请及时发起结账",
RenterContent: "客服已重置归还逾期状态,请尽快发起结账。",
AuditAction: "order.reset_return_overdue",
RecalculateEnd: true,
}, true
}
return adminResetTarget{}, false
}
@@ -395,6 +395,20 @@ func TestEstimateOrderDurationHoursFromSnapshot(t *testing.T) {
}
}
func TestEstimateOrderDurationHoursFromServerSnapshot(t *testing.T) {
snapshot := datatypes.JSON([]byte(`{
"haf_coin_amount": 105000000,
"asset_summary": {
"daily_loss_m": 10
}
}`))
hours := estimateOrderDurationHours(snapshot)
if hours != 252 {
t.Fatalf("hours = %d, want 252", hours)
}
}
func TestEstimateOrderDurationHoursUsesDefaultWithoutDailyLoss(t *testing.T) {
snapshot := datatypes.JSON([]byte(`{
"haf_coin_amount": 165000000,
@@ -604,6 +618,62 @@ func TestAdminResetCheckoutConfirmTimeout(t *testing.T) {
}
}
func TestAdminResetReturnOverdueRecalculatesDuration(t *testing.T) {
db := setupOrderTestDB(t)
repo := NewRepository(db)
owner := model.User{Phone: "13800003005"}
renter := model.User{Phone: "13900003005"}
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)
}
rentedAt := time.Now().Add(-72 * time.Hour)
order := model.RentalOrder{
OrderNo: "ORD-RESET-RETURN-001",
ListingID: 1,
AccountID: 1,
OwnerID: owner.ID,
RenterID: renter.ID,
RentedAt: &rentedAt,
EstimatedDurationHours: 24,
AccountSnapshot: datatypes.JSON([]byte(`{
"haf_coin_amount": 105000000,
"asset_summary": {"daily_loss_m": 10}
}`)),
Status: orderStatusOverdue,
HandoffStatus: handoffStatusReturnOverdue,
}
if err := db.Create(&order).Error; err != nil {
t.Fatalf("create order failed: %v", err)
}
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 != orderStatusRenting {
t.Fatalf("status = %q, want %q", saved.Status, orderStatusRenting)
}
if saved.HandoffStatus != handoffStatusReceived {
t.Fatalf("handoff status = %q, want %q", saved.HandoffStatus, handoffStatusReceived)
}
if saved.EstimatedDurationHours != 252 {
t.Fatalf("EstimatedDurationHours = %d, want 252", saved.EstimatedDurationHours)
}
if saved.RentedAt == nil || !saved.RentedAt.Equal(rentedAt) {
t.Fatalf("RentedAt = %#v, want original %v", saved.RentedAt, rentedAt)
}
}
func TestAdminOrderDTOIncludesResetAction(t *testing.T) {
order := model.RentalOrder{
Status: orderStatusAbnormal,
@@ -621,6 +691,23 @@ func TestAdminOrderDTOIncludesResetAction(t *testing.T) {
}
}
func TestAdminOrderDTOIncludesReturnOverdueResetAction(t *testing.T) {
order := model.RentalOrder{
Status: orderStatusOverdue,
HandoffStatus: handoffStatusReturnOverdue,
}
dto := orderRow{RentalOrder: order}.toAdminDTO()
if dto.AdminActions == nil || dto.AdminActions.ResetHandoff == nil {
t.Fatal("return overdue reset action should be present")
}
if !dto.AdminActions.ResetHandoff.Enabled {
t.Fatal("return overdue reset action should be enabled")
}
if dto.AdminActions.ResetHandoff.Label != "重置归还逾期" {
t.Fatalf("label = %q, want 重置归还逾期", dto.AdminActions.ResetHandoff.Label)
}
}
func TestUserOrderDTODoesNotIncludeAdminActions(t *testing.T) {
order := model.RentalOrder{
OwnerID: 1,