修复归还逾期订单重置
This commit is contained in:
@@ -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,
|
||||
|
||||
Reference in New Issue
Block a user