186 lines
5.3 KiB
Go
186 lines
5.3 KiB
Go
package dispute
|
|
|
|
import (
|
|
"context"
|
|
"time"
|
|
|
|
"hfb_sys/backend/internal/model"
|
|
"hfb_sys/backend/internal/modules/notification"
|
|
|
|
"gorm.io/gorm"
|
|
"gorm.io/gorm/clause"
|
|
)
|
|
|
|
func (r *Repository) CancelByOrder(ctx context.Context, userID uint64, orderID uint64) (*DisputeDTO, error) {
|
|
var row model.Dispute
|
|
if err := r.db.WithContext(ctx).
|
|
Where("order_id = ? AND initiator_type = ? AND initiator_id = ? AND status IN ?", orderID, disputeInitiatorUser, userID, []string{"open", "processing"}).
|
|
Order("id DESC").
|
|
First(&row).Error; err != nil {
|
|
return nil, err
|
|
}
|
|
return r.Cancel(ctx, userID, row.ID)
|
|
}
|
|
|
|
func (r *Repository) Cancel(ctx context.Context, userID uint64, id uint64) (*DisputeDTO, error) {
|
|
err := r.db.WithContext(ctx).Transaction(func(tx *gorm.DB) error {
|
|
var row model.Dispute
|
|
if err := tx.Clauses(clause.Locking{Strength: "UPDATE"}).First(&row, id).Error; err != nil {
|
|
return err
|
|
}
|
|
if effectiveInitiatorType(row) != disputeInitiatorUser || row.InitiatorID != userID {
|
|
return ErrPermissionDenied
|
|
}
|
|
if row.Status != "open" && row.Status != "processing" {
|
|
return ErrDisputeCannotCancel
|
|
}
|
|
|
|
var order model.RentalOrder
|
|
if err := tx.Clauses(clause.Locking{Strength: "UPDATE"}).First(&order, row.OrderID).Error; err != nil {
|
|
return err
|
|
}
|
|
if err := restoreOrderAfterDisputeCancel(tx, &row, &order); err != nil {
|
|
return err
|
|
}
|
|
|
|
now := time.Now()
|
|
row.Status = "cancelled"
|
|
row.ArbitrationRemark = "用户自行取消申诉"
|
|
row.HandledAt = &now
|
|
if err := tx.Save(&row).Error; err != nil {
|
|
return err
|
|
}
|
|
if err := tx.Save(&order).Error; err != nil {
|
|
return err
|
|
}
|
|
recordType := "dispute_cancelled"
|
|
recordContent := "用户自行取消申诉,订单已恢复到申诉前流程。"
|
|
if row.Type == "checkout_dispute" {
|
|
recordType = "checkout_dispute_cancelled"
|
|
recordContent = "用户自行取消结账争议,订单已恢复到申诉前流程。"
|
|
}
|
|
record := model.HandoffRecord{
|
|
OrderID: order.ID,
|
|
FromUserID: row.InitiatorID,
|
|
ToUserID: row.TargetUserID,
|
|
Type: recordType,
|
|
Content: recordContent,
|
|
}
|
|
if err := tx.Create(&record).Error; err != nil {
|
|
return err
|
|
}
|
|
|
|
disputeID := row.ID
|
|
if err := notification.Append(tx,
|
|
notification.Entry{
|
|
UserID: row.TargetUserID,
|
|
Type: "dispute",
|
|
Title: "申诉已取消",
|
|
Content: "对方已自行取消申诉,订单已恢复到申诉前流程。",
|
|
BizType: "dispute",
|
|
BizID: &disputeID,
|
|
},
|
|
notification.Entry{
|
|
UserID: row.InitiatorID,
|
|
Type: "dispute",
|
|
Title: "申诉已取消",
|
|
Content: "你已取消申诉,订单已恢复到申诉前流程。",
|
|
BizType: "dispute",
|
|
BizID: &disputeID,
|
|
},
|
|
); err != nil {
|
|
return err
|
|
}
|
|
return nil
|
|
})
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
var row disputeRow
|
|
if err := r.baseQuery(ctx).Where("d.id = ?", id).First(&row).Error; err != nil {
|
|
return nil, err
|
|
}
|
|
dto := row.toDTO()
|
|
return &dto, nil
|
|
}
|
|
|
|
func restoreOrderAfterDisputeCancel(tx *gorm.DB, row *model.Dispute, order *model.RentalOrder) error {
|
|
if row.Type == "checkout_dispute" {
|
|
return restoreCheckoutDispute(tx, row, order)
|
|
}
|
|
if order.Status != "disputing" {
|
|
return ErrDisputeCannotCancel
|
|
}
|
|
restoreOrderStatusFromSnapshot(row, order)
|
|
if order.Status == "" || order.Status == "disputing" {
|
|
order.Status = inferOrderStatusForDisputeCancel(*order)
|
|
}
|
|
if order.Status == "" {
|
|
return ErrDisputeCannotCancel
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func restoreCheckoutDispute(tx *gorm.DB, row *model.Dispute, order *model.RentalOrder) error {
|
|
if order.Status != "checkout_disputing" {
|
|
return ErrDisputeCannotCancel
|
|
}
|
|
var checkout model.OrderCheckout
|
|
query := tx.Clauses(clause.Locking{Strength: "UPDATE"}).Where("order_id = ? AND status = ?", order.ID, "disputed")
|
|
if row.CheckoutID != nil && *row.CheckoutID > 0 {
|
|
query = query.Where("id = ?", *row.CheckoutID)
|
|
}
|
|
if err := query.Order("id DESC").First(&checkout).Error; err != nil {
|
|
return err
|
|
}
|
|
restoreOrderStatusFromSnapshot(row, order)
|
|
if order.Status == "" || order.Status == "checkout_disputing" {
|
|
order.Status = "pending_checkout_confirm"
|
|
order.HandoffStatus = "pending_owner_checkout"
|
|
if checkout.OwnerAdjustedAt != nil || checkout.OwnerAdjustmentReason != "" {
|
|
order.Status = "pending_checkout_accept"
|
|
order.HandoffStatus = "pending_renter_checkout"
|
|
}
|
|
order.SettlementStatus = "pending"
|
|
}
|
|
checkout.Status = row.PreviousCheckoutStatus
|
|
if checkout.Status == "" {
|
|
checkout.Status = "submitted"
|
|
if order.Status == "pending_checkout_accept" {
|
|
checkout.Status = "countered"
|
|
}
|
|
}
|
|
checkout.RenterRejectedAt = nil
|
|
if err := tx.Save(&checkout).Error; err != nil {
|
|
return err
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func restoreOrderStatusFromSnapshot(row *model.Dispute, order *model.RentalOrder) {
|
|
if row.PreviousOrderStatus != "" {
|
|
order.Status = row.PreviousOrderStatus
|
|
}
|
|
if row.PreviousHandoffStatus != "" {
|
|
order.HandoffStatus = row.PreviousHandoffStatus
|
|
}
|
|
if row.PreviousSettlementStatus != "" {
|
|
order.SettlementStatus = row.PreviousSettlementStatus
|
|
}
|
|
}
|
|
|
|
func inferOrderStatusForDisputeCancel(order model.RentalOrder) string {
|
|
switch order.HandoffStatus {
|
|
case "none":
|
|
return "pending_payment"
|
|
case "pending_owner", "pending_renter_confirm":
|
|
return "pending_handoff"
|
|
case "received":
|
|
return "renting"
|
|
case "return_overdue":
|
|
return "overdue"
|
|
default:
|
|
return ""
|
|
}
|
|
}
|