新增订单待支付与支付确认后端流程

This commit is contained in:
yml
2026-05-24 16:29:47 +08:00
parent 5e422bfa40
commit c4cfe6dd79
15 changed files with 368 additions and 66 deletions
+65
View File
@@ -22,6 +22,7 @@ type Job struct {
}
type thresholds struct {
PendingPaymentTimeoutMinutes int
OwnerSubmitTimeoutMinutes int
RenterConfirmTimeoutMinutes int
ReturnOverdueGraceMinutes int
@@ -66,6 +67,7 @@ func (j *Job) run(ctx context.Context) {
}
now := time.Now()
handlers := []func(context.Context, time.Time, thresholds) (int, error){
j.handlePendingPaymentTimeout,
j.handleOwnerSubmitTimeout,
j.handleRenterConfirmTimeout,
j.handleReturnOverdue,
@@ -87,6 +89,7 @@ func (j *Job) run(ctx context.Context) {
func (j *Job) loadThresholds(ctx context.Context) (thresholds, error) {
cfg := thresholds{
PendingPaymentTimeoutMinutes: 15,
OwnerSubmitTimeoutMinutes: 30,
RenterConfirmTimeoutMinutes: 30,
ReturnOverdueGraceMinutes: 10,
@@ -97,6 +100,7 @@ func (j *Job) loadThresholds(ctx context.Context) (thresholds, error) {
Where("`key` IN ?", []string{
"handoff.owner_submit_timeout_minutes",
"handoff.renter_confirm_timeout_minutes",
"order.pending_payment_timeout_minutes",
"order.return_overdue_grace_minutes",
"handoff.owner_return_confirm_timeout_minutes",
}).
@@ -114,6 +118,8 @@ func (j *Job) loadThresholds(ctx context.Context) (thresholds, error) {
cfg.OwnerSubmitTimeoutMinutes = value
case "handoff.renter_confirm_timeout_minutes":
cfg.RenterConfirmTimeoutMinutes = value
case "order.pending_payment_timeout_minutes":
cfg.PendingPaymentTimeoutMinutes = value
case "order.return_overdue_grace_minutes":
cfg.ReturnOverdueGraceMinutes = value
case "handoff.owner_return_confirm_timeout_minutes":
@@ -123,6 +129,65 @@ func (j *Job) loadThresholds(ctx context.Context) (thresholds, error) {
return cfg, nil
}
func (j *Job) handlePendingPaymentTimeout(ctx context.Context, now time.Time, cfg thresholds) (int, error) {
var rows []model.RentalOrder
err := j.db.WithContext(ctx).
Where("status = ? AND created_at <= ?", "pending_payment", now.Add(-time.Duration(cfg.PendingPaymentTimeoutMinutes)*time.Minute)).
Order("id ASC").
Limit(100).
Find(&rows).Error
if err != nil {
return 0, err
}
count := 0
for _, row := range rows {
if err := j.db.WithContext(ctx).Transaction(func(tx *gorm.DB) error {
var order model.RentalOrder
if err := tx.Clauses(clause.Locking{Strength: "UPDATE"}).First(&order, row.ID).Error; err != nil {
return err
}
if order.Status != "pending_payment" {
return nil
}
before := snapshot(&order)
var listing model.RentalListing
if err := tx.Clauses(clause.Locking{Strength: "UPDATE"}).First(&listing, order.ListingID).Error; err != nil {
return err
}
order.Status = "cancelled"
order.HandoffStatus = "cancelled"
listing.InTransaction = false
orderID := order.ID
if err := notification.Append(tx, notification.Entry{
UserID: order.RenterID,
Type: "timeout",
Title: "订单支付超时",
Content: "订单未在规定时间内完成支付,已自动取消。",
BizType: "order",
BizID: &orderID,
}); err != nil {
return err
}
if err := tx.Save(&order).Error; err != nil {
return err
}
if err := tx.Save(&listing).Error; err != nil {
return err
}
return appendAuditLog(tx, "order.timeout.pending_payment", order.ID, map[string]any{
"order_id": order.ID,
"order_no": order.OrderNo,
"before": before,
"after": snapshot(&order),
})
}); err != nil {
return count, err
}
count++
}
return count, nil
}
func (j *Job) handleOwnerSubmitTimeout(ctx context.Context, now time.Time, cfg thresholds) (int, error) {
var rows []model.RentalOrder
err := j.db.WithContext(ctx).