69 lines
1.9 KiB
Go
69 lines
1.9 KiB
Go
package repository
|
|
|
|
import (
|
|
"time"
|
|
|
|
"infinite-canvas/backend/internal/model"
|
|
)
|
|
|
|
// BillingReviewStats 记录需要人工关注的长期未闭合订单数量。
|
|
// 巡检只读数据库,不改变订单状态或积分余额。
|
|
type BillingReviewStats struct {
|
|
Reserved int64 `json:"reserved"`
|
|
Running int64 `json:"running"`
|
|
Uncertain int64 `json:"uncertain"`
|
|
Oldest *time.Time `json:"oldest,omitempty"`
|
|
}
|
|
|
|
func (s BillingReviewStats) Total() int64 {
|
|
return s.Reserved + s.Running + s.Uncertain
|
|
}
|
|
|
|
// StaleBillingReviewStats 查询超过 age 未更新、仍处于未闭合状态的订单。
|
|
func (r *Repository) StaleBillingReviewStats(now time.Time, age time.Duration) (BillingReviewStats, error) {
|
|
cutoff := now.Add(-age)
|
|
stats := BillingReviewStats{}
|
|
var rows []struct {
|
|
Status model.BillingStatus
|
|
Count int64
|
|
}
|
|
if err := r.db.Model(&model.BillingOrder{}).
|
|
Select("status, count(*) AS count").
|
|
Where("status IN ? AND updated_at < ?", []model.BillingStatus{
|
|
model.BillingStatusReserved,
|
|
model.BillingStatusRunning,
|
|
model.BillingStatusUncertain,
|
|
}, cutoff).
|
|
Group("status").Scan(&rows).Error; err != nil {
|
|
return stats, err
|
|
}
|
|
for _, row := range rows {
|
|
switch row.Status {
|
|
case model.BillingStatusReserved:
|
|
stats.Reserved = row.Count
|
|
case model.BillingStatusRunning:
|
|
stats.Running = row.Count
|
|
case model.BillingStatusUncertain:
|
|
stats.Uncertain = row.Count
|
|
}
|
|
}
|
|
if stats.Total() == 0 {
|
|
return stats, nil
|
|
}
|
|
var oldest struct {
|
|
CreatedAt time.Time
|
|
}
|
|
if err := r.db.Model(&model.BillingOrder{}).
|
|
Select("created_at").
|
|
Where("status IN ? AND updated_at < ?", []model.BillingStatus{
|
|
model.BillingStatusReserved,
|
|
model.BillingStatusRunning,
|
|
model.BillingStatusUncertain,
|
|
}, cutoff).
|
|
Order("created_at asc").Limit(1).Scan(&oldest).Error; err != nil {
|
|
return stats, err
|
|
}
|
|
stats.Oldest = &oldest.CreatedAt
|
|
return stats, nil
|
|
}
|