139 lines
4.4 KiB
Go
139 lines
4.4 KiB
Go
package order
|
|
|
|
import (
|
|
"context"
|
|
"strings"
|
|
"time"
|
|
|
|
"hfb_sys/backend/internal/model"
|
|
"hfb_sys/backend/internal/modules/notification"
|
|
|
|
"gorm.io/gorm"
|
|
"gorm.io/gorm/clause"
|
|
)
|
|
|
|
func canAdminForceHandoff(order model.RentalOrder) bool {
|
|
if isPlatformHandoffOrder(order) {
|
|
if order.Status == orderStatusPendingHandoff {
|
|
return order.HandoffStatus == handoffStatusPendingRenterConfirm
|
|
}
|
|
return order.Status == orderStatusAbnormal && order.HandoffStatus == handoffStatusRenterConfirmTimeout
|
|
}
|
|
if isPlatformSettlementOrder(order) {
|
|
return false
|
|
}
|
|
if order.Status == orderStatusPendingHandoff {
|
|
return order.HandoffStatus == handoffStatusPendingOwner ||
|
|
order.HandoffStatus == handoffStatusOwnerTimeout ||
|
|
order.HandoffStatus == handoffStatusPendingRenterConfirm
|
|
}
|
|
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 {
|
|
return order.HandoffStatus == handoffStatusPendingOwner || order.HandoffStatus == handoffStatusOwnerTimeout
|
|
}
|
|
|
|
// AdminForceHandoff 由客服确认订单已完成交接,并立即开始租期。
|
|
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 {
|
|
var order model.RentalOrder
|
|
if err := tx.Clauses(clause.Locking{Strength: "UPDATE"}).First(&order, orderID).Error; err != nil {
|
|
return err
|
|
}
|
|
if !canAdminForceHandoff(order) || strings.TrimSpace(req.Reason) == "" {
|
|
return ErrOrderCannotForceHandoff
|
|
}
|
|
if order.RefundStatus == refundStatusPending || order.RefundStatus == refundStatusPendingReview {
|
|
return ErrOrderCannotForceHandoff
|
|
}
|
|
|
|
var activeDisputeCount int64
|
|
if err := tx.Model(&model.Dispute{}).
|
|
Where("order_id = ? AND status IN ?", order.ID, []string{"open", "processing"}).
|
|
Count(&activeDisputeCount).Error; err != nil {
|
|
return err
|
|
}
|
|
if activeDisputeCount > 0 {
|
|
return ErrOrderCannotForceHandoff
|
|
}
|
|
|
|
needsContent := forceHandoffNeedsContent(order)
|
|
content := strings.TrimSpace(req.Content)
|
|
if needsContent && content == "" {
|
|
return ErrOrderCannotForceHandoff
|
|
}
|
|
if !needsContent {
|
|
var handoffCount int64
|
|
if err := tx.Model(&model.HandoffRecord{}).
|
|
Where("order_id = ? AND type = ?", order.ID, forceHandoffRecordType(order)).
|
|
Count(&handoffCount).Error; err != nil {
|
|
return err
|
|
}
|
|
if handoffCount == 0 {
|
|
return ErrOrderCannotForceHandoff
|
|
}
|
|
content = "客服已确认双方完成交接,订单已进入使用中。"
|
|
}
|
|
|
|
beforeOrderStatus := order.Status
|
|
beforeHandoffStatus := order.HandoffStatus
|
|
now := time.Now()
|
|
record := model.HandoffRecord{
|
|
OrderID: order.ID,
|
|
FromUserID: adminID,
|
|
ToUserID: order.RenterID,
|
|
Type: "admin_force_handoff",
|
|
Content: content,
|
|
}
|
|
if err := tx.Create(&record).Error; err != nil {
|
|
return err
|
|
}
|
|
order.Status = orderStatusRenting
|
|
order.HandoffStatus = handoffStatusReceived
|
|
order.EstimatedDurationHours = estimateOrderDurationHours(order.AccountSnapshot)
|
|
order.RentedAt = &now
|
|
orderID := order.ID
|
|
if err := notification.Append(tx,
|
|
notification.Entry{
|
|
UserID: order.RenterID,
|
|
Type: "order_admin",
|
|
Title: "客服已确认交接",
|
|
Content: "客服已确认双方完成交接,订单已进入使用中,租期已开始计算。",
|
|
BizType: "order",
|
|
BizID: &orderID,
|
|
},
|
|
notification.Entry{
|
|
UserID: order.OwnerID,
|
|
Type: "order_admin",
|
|
Title: "客服已确认交接",
|
|
Content: "客服已确认双方完成交接,订单已进入使用中,租期已开始计算。",
|
|
BizType: "order",
|
|
BizID: &orderID,
|
|
},
|
|
); err != nil {
|
|
return err
|
|
}
|
|
if err := appendAuditLog(tx, adminID, "order.force_handoff", "order", order.ID, meta, map[string]any{
|
|
"order_id": order.ID,
|
|
"order_no": order.OrderNo,
|
|
"reason": strings.TrimSpace(req.Reason),
|
|
"content_length": len(content),
|
|
"before_order_status": beforeOrderStatus,
|
|
"after_order_status": order.Status,
|
|
"before_handoff_status": beforeHandoffStatus,
|
|
"after_handoff_status": order.HandoffStatus,
|
|
}); err != nil {
|
|
return err
|
|
}
|
|
return tx.Save(&order).Error
|
|
})
|
|
}
|