账号上下架增加统计
This commit is contained in:
@@ -4,11 +4,15 @@ import (
|
||||
"context"
|
||||
"time"
|
||||
|
||||
"hfb_sys/backend/internal/listingstatus"
|
||||
"hfb_sys/backend/internal/model"
|
||||
"hfb_sys/backend/internal/timeutil"
|
||||
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
const listingDailyTrendDays = 7
|
||||
|
||||
type Repository struct {
|
||||
db *gorm.DB
|
||||
}
|
||||
@@ -19,8 +23,9 @@ func NewRepository(db *gorm.DB) *Repository {
|
||||
|
||||
func (r *Repository) Summary(ctx context.Context) (*DashboardDTO, error) {
|
||||
db := r.db.WithContext(ctx)
|
||||
now := time.Now()
|
||||
today := time.Date(now.Year(), now.Month(), now.Day(), 0, 0, 0, 0, now.Location())
|
||||
loc := timeutil.ShanghaiLocation()
|
||||
now := timeutil.ShanghaiNow()
|
||||
today := time.Date(now.Year(), now.Month(), now.Day(), 0, 0, 0, 0, loc)
|
||||
metrics := MetricsDTO{}
|
||||
pending := PendingDTO{}
|
||||
|
||||
@@ -63,6 +68,11 @@ func (r *Repository) Summary(ctx context.Context) (*DashboardDTO, error) {
|
||||
if err := db.Model(&model.RentalOrder{}).Where("status IN ?", []string{"pending_checkout_confirm", "pending_checkout_accept"}).Count(&pending.PendingReturnConfirms).Error; err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
listingDaily, err := r.listingDailyOverview(ctx, now, listingDailyTrendDays)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
recentOrders, err := r.recentOrders(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -74,12 +84,103 @@ func (r *Repository) Summary(ctx context.Context) (*DashboardDTO, error) {
|
||||
return &DashboardDTO{
|
||||
Metrics: metrics,
|
||||
Pending: pending,
|
||||
ListingDaily: listingDaily,
|
||||
RecentOrders: recentOrders,
|
||||
RecentDisputes: recentDisputes,
|
||||
GeneratedAt: now,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (r *Repository) listingDailyOverview(ctx context.Context, now time.Time, days int) (ListingDailyOverviewDTO, error) {
|
||||
if days <= 0 {
|
||||
days = listingDailyTrendDays
|
||||
}
|
||||
loc := timeutil.ShanghaiLocation()
|
||||
now = now.In(loc)
|
||||
// 右开区间 [start, end)
|
||||
end := time.Date(now.Year(), now.Month(), now.Day(), 0, 0, 0, 0, loc).AddDate(0, 0, 1)
|
||||
start := end.AddDate(0, 0, -days)
|
||||
|
||||
type eventRow struct {
|
||||
EventType string
|
||||
Source string
|
||||
CreatedAt time.Time
|
||||
}
|
||||
events := make([]eventRow, 0)
|
||||
if err := r.db.WithContext(ctx).Model(&model.ListingStatusEvent{}).
|
||||
Select("event_type, source, created_at").
|
||||
Where("created_at >= ? AND created_at < ?", start, end).
|
||||
Scan(&events).Error; err != nil {
|
||||
return ListingDailyOverviewDTO{}, err
|
||||
}
|
||||
|
||||
type bucket struct {
|
||||
published int64
|
||||
activeOffline int64
|
||||
tradeLeave int64
|
||||
}
|
||||
byDay := make(map[string]*bucket, days)
|
||||
for _, ev := range events {
|
||||
key := ev.CreatedAt.In(loc).Format("2006-01-02")
|
||||
b := byDay[key]
|
||||
if b == nil {
|
||||
b = &bucket{}
|
||||
byDay[key] = b
|
||||
}
|
||||
switch {
|
||||
case ev.EventType == listingstatus.EventPublished:
|
||||
b.published++
|
||||
case isActiveOfflineEvent(ev.EventType, ev.Source):
|
||||
b.activeOffline++
|
||||
case isTradeLeaveEvent(ev.EventType, ev.Source):
|
||||
b.tradeLeave++
|
||||
}
|
||||
}
|
||||
|
||||
trend := make([]ListingDayStatsDTO, 0, days)
|
||||
for i := days - 1; i >= 0; i-- {
|
||||
day := time.Date(now.Year(), now.Month(), now.Day(), 0, 0, 0, 0, loc).AddDate(0, 0, -i)
|
||||
key := day.Format("2006-01-02")
|
||||
item := ListingDayStatsDTO{Date: key}
|
||||
if b := byDay[key]; b != nil {
|
||||
item.PublishedCount = b.published
|
||||
item.ActiveOfflineCount = b.activeOffline
|
||||
item.TradeLeaveCount = b.tradeLeave
|
||||
}
|
||||
trend = append(trend, item)
|
||||
}
|
||||
|
||||
todayStats := ListingDayStatsDTO{Date: now.Format("2006-01-02")}
|
||||
if len(trend) > 0 {
|
||||
todayStats = trend[len(trend)-1]
|
||||
}
|
||||
return ListingDailyOverviewDTO{
|
||||
Today: todayStats,
|
||||
Trend: trend,
|
||||
Days: days,
|
||||
Timezone: "Asia/Shanghai",
|
||||
}, nil
|
||||
}
|
||||
|
||||
func isActiveOfflineEvent(eventType, source string) bool {
|
||||
return eventType == listingstatus.EventOffline &&
|
||||
(source == listingstatus.SourceSeller || source == listingstatus.SourceAdmin)
|
||||
}
|
||||
|
||||
func isTradeLeaveEvent(eventType, source string) bool {
|
||||
switch eventType {
|
||||
case listingstatus.EventRented, listingstatus.EventCompleted, listingstatus.EventSealed:
|
||||
return true
|
||||
case listingstatus.EventOffline:
|
||||
return source == listingstatus.SourceOrder ||
|
||||
source == listingstatus.SourceDispute ||
|
||||
source == listingstatus.SourcePickup ||
|
||||
source == listingstatus.SourceSystem
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
func (r *Repository) recentOrders(ctx context.Context) ([]RecentOrderDTO, error) {
|
||||
rows := make([]RecentOrderDTO, 0)
|
||||
err := r.db.WithContext(ctx).Table("rental_orders AS o").
|
||||
|
||||
Reference in New Issue
Block a user