Files
hfb_sys/backend/internal/modules/dispute/mutation.go
T

153 lines
4.2 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) Create(ctx context.Context, userID uint64, orderID uint64, req CreateRequest) (*DisputeDTO, error) {
var createdID uint64
err := 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 order.RenterID != userID && order.OwnerID != userID {
return ErrPermissionDenied
}
if order.Status == "completed" || order.Status == "cancelled" || order.Status == "closed" {
return ErrInvalidDispute
}
isCheckoutDispute := order.Status == "pending_checkout_confirm" || order.Status == "pending_checkout_accept"
var count int64
if err := tx.Model(&model.Dispute{}).
Where("order_id = ? AND status IN ?", order.ID, []string{"open", "processing"}).
Count(&count).Error; err != nil {
return err
}
if count > 0 {
return ErrDisputeExists
}
targetID := order.OwnerID
if userID == order.OwnerID {
targetID = order.RenterID
}
evidence, err := marshalEvidence(req.EvidenceURLS)
if err != nil {
return err
}
row := model.Dispute{
OrderID: order.ID,
InitiatorID: userID,
TargetUserID: targetID,
Type: disputeType(req.Type, isCheckoutDispute),
Status: "open",
Description: req.Description,
EvidenceURLS: evidence,
PreviousOrderStatus: order.Status,
PreviousHandoffStatus: order.HandoffStatus,
PreviousSettlementStatus: order.SettlementStatus,
}
var checkout model.OrderCheckout
if isCheckoutDispute {
if err := tx.Clauses(clause.Locking{Strength: "UPDATE"}).
Where("order_id = ? AND status IN ?", order.ID, []string{"submitted", "countered"}).
Order("id DESC").
First(&checkout).Error; err != nil {
return err
}
row.CheckoutID = &checkout.ID
row.PreviousCheckoutStatus = checkout.Status
}
if err := tx.Create(&row).Error; err != nil {
return err
}
if isCheckoutDispute {
now := time.Now()
order.Status = "checkout_disputing"
order.HandoffStatus = "checkout_disputed"
order.SettlementStatus = "disputed"
updates := map[string]any{
"status": "disputed",
"updated_at": now,
}
if userID == order.RenterID {
updates["renter_rejected_at"] = now
}
if err := tx.Model(&model.OrderCheckout{}).
Where("id = ?", checkout.ID).
Updates(updates).Error; err != nil {
return err
}
} else {
order.Status = "disputing"
}
recordType := "dispute_opened"
recordContent := "用户发起订单申诉:" + req.Description
if isCheckoutDispute {
recordType = "checkout_dispute_opened"
recordContent = "用户发起结账争议:" + req.Description
}
record := model.HandoffRecord{
OrderID: order.ID,
FromUserID: userID,
ToUserID: targetID,
Type: recordType,
Content: recordContent,
}
if err := tx.Create(&record).Error; err != nil {
return err
}
if err := tx.Save(&order).Error; err != nil {
return err
}
disputeID := row.ID
title := "订单进入申诉"
content := "对方已发起申诉,请等待客服仲裁或补充沟通记录。"
if isCheckoutDispute {
title = "订单进入结账争议"
content = "对方已发起结账争议,请等待客服仲裁或补充结账证据。"
}
if err := notification.Append(tx,
notification.Entry{
UserID: targetID,
Type: "dispute",
Title: title,
Content: content,
BizType: "dispute",
BizID: &disputeID,
},
notification.Entry{
UserID: userID,
Type: "dispute",
Title: "申诉已提交",
Content: "申诉已进入待处理状态,客服仲裁后会通知双方。",
BizType: "dispute",
BizID: &disputeID,
},
); err != nil {
return err
}
createdID = row.ID
return nil
})
if err != nil {
return nil, err
}
return r.FindForUser(ctx, userID, createdID)
}
func disputeType(input string, isCheckoutDispute bool) string {
if isCheckoutDispute {
return "checkout_dispute"
}
return input
}