@@ -0,0 +1,114 @@
|
||||
package store
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"daili/internal/domain"
|
||||
)
|
||||
|
||||
// Stats 大屏统计数据。
|
||||
type Stats struct {
|
||||
Customers int
|
||||
ActiveSubs int
|
||||
PausedSubs int
|
||||
DueToday int
|
||||
Due7 int
|
||||
DueTodaySubs []Subscription
|
||||
Heat map[string]int // 结束日 -> 该日到期数量
|
||||
MonthDue int
|
||||
Top []Subscription
|
||||
TodayTasks int
|
||||
TodayDone int
|
||||
}
|
||||
|
||||
// Badges 顶栏常驻徽标。
|
||||
func (db *DB) Badges(today time.Time) (dueToday, due3 int, err error) {
|
||||
t := domain.Date(today)
|
||||
if err = db.sql.QueryRow(`SELECT COUNT(*) FROM subscriptions WHERE status<>'closed' AND end_date=?`, t).Scan(&dueToday); err != nil {
|
||||
return
|
||||
}
|
||||
err = db.sql.QueryRow(`SELECT COUNT(*) FROM subscriptions WHERE status<>'closed' AND end_date BETWEEN ? AND ?`,
|
||||
t, domain.Date(domain.AddDays(today, 2))).Scan(&due3)
|
||||
return
|
||||
}
|
||||
|
||||
// ScreenStats 汇总大屏所需数据。
|
||||
func (db *DB) ScreenStats(today time.Time, warnDays int) (*Stats, error) {
|
||||
st := &Stats{Heat: map[string]int{}}
|
||||
var err error
|
||||
if st.Customers, err = db.CustomerCount(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if st.ActiveSubs, err = db.CountActiveSubs(domain.Date(today)); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err = db.sql.QueryRow(`SELECT COUNT(*) FROM subscriptions WHERE status='paused'`).Scan(&st.PausedSubs); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err = db.sql.QueryRow(`SELECT COUNT(*) FROM subscriptions WHERE status<>'closed' AND end_date=?`,
|
||||
domain.Date(today)).Scan(&st.DueToday); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err = db.sql.QueryRow(`SELECT COUNT(*) FROM subscriptions WHERE status<>'closed' AND end_date BETWEEN ? AND ?`,
|
||||
domain.Date(today), domain.Date(domain.AddDays(today, 6))).Scan(&st.Due7); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if st.DueTodaySubs, err = db.DueOn(domain.Date(today)); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// 本月到期热力
|
||||
ms := domain.MonthStart(today.Year(), today.Month())
|
||||
me := domain.MonthEnd(today.Year(), today.Month())
|
||||
rows, err := db.sql.Query(`SELECT end_date, COUNT(*) FROM subscriptions
|
||||
WHERE status<>'closed' AND end_date BETWEEN ? AND ? GROUP BY end_date`,
|
||||
domain.Date(ms), domain.Date(me))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for rows.Next() {
|
||||
var d string
|
||||
var n int
|
||||
if err := rows.Scan(&d, &n); err != nil {
|
||||
rows.Close()
|
||||
return nil, err
|
||||
}
|
||||
st.Heat[d] = n
|
||||
st.MonthDue += n
|
||||
}
|
||||
rows.Close()
|
||||
|
||||
// TOP:按日按剩余天数、按次按剩余次数,各自排序后交替取前 10
|
||||
t := domain.Date(today)
|
||||
daySubs, err := db.querySubs(subSelect+` WHERE s.status<>'closed' AND s.unit='day' AND s.end_date>=?
|
||||
ORDER BY s.end_date ASC LIMIT 12`, t)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
cntSubs, err := db.querySubs(subSelect+` WHERE s.status<>'closed' AND s.unit='count' AND s.used_count < s.total_count
|
||||
AND (IFNULL(s.end_date,'')='' OR s.end_date>=?)
|
||||
ORDER BY (s.total_count - s.used_count) ASC LIMIT 12`, t)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for i := 0; i < 10 && len(st.Top) < 10; i++ {
|
||||
if i < len(daySubs) {
|
||||
st.Top = append(st.Top, daySubs[i])
|
||||
}
|
||||
if len(st.Top) < 10 && i < len(cntSubs) {
|
||||
st.Top = append(st.Top, cntSubs[i])
|
||||
}
|
||||
if i >= len(daySubs) && i >= len(cntSubs) {
|
||||
break
|
||||
}
|
||||
}
|
||||
_ = warnDays
|
||||
return st, nil
|
||||
}
|
||||
|
||||
// RecentDue 返回未来 n 天内到期的订阅(按到期日排序)。
|
||||
func (db *DB) RecentDue(from string, days int) ([]Subscription, error) {
|
||||
to := domain.Date(domain.AddDays(domain.ParseDateOr(from, domain.Today()), days-1))
|
||||
return db.querySubs(subSelect+` WHERE s.status<>'closed' AND s.end_date BETWEEN ? AND ?
|
||||
ORDER BY s.end_date, s.id`, from, to)
|
||||
}
|
||||
Reference in New Issue
Block a user