package web import ( "fmt" "net/http" "time" "daili/internal/domain" "daili/internal/store" ) type screenDay struct { Date string Day int InMonth bool IsToday bool IsPast bool Due int Heat int } type screenData struct { Stats *store.Stats MonthTitle string Year, Month int Weeks [][]screenDay Weekdays []string Top []SubView DueToday []SubView TodayList []SubView Recent []store.Adjustment TodayTasks int TodayDone int Refresh int Version string } func (s *Server) handleScreen(w http.ResponseWriter, r *http.Request) { today := domain.Today() warn := s.warnDays() stats, err := s.db.ScreenStats(today, warn) if err != nil { http.Error(w, "读取大屏数据失败", http.StatusInternalServerError) return } data := &screenData{ Stats: stats, MonthTitle: fmt.Sprintf("%d 年 %d 月", today.Year(), int(today.Month())), Year: today.Year(), Month: int(today.Month()), Weekdays: []string{"一", "二", "三", "四", "五", "六", "日"}, Top: subViews(stats.Top, today, warn), DueToday: subViews(stats.DueTodaySubs, today, warn), Refresh: s.screenRefresh(), Version: s.cfg.Version, } if len(data.DueToday) > 6 { data.DueToday = data.DueToday[:6] } // 本月热力 monthStart := domain.MonthStart(today.Year(), today.Month()) monthEnd := domain.MonthEnd(today.Year(), today.Month()) gridStart := domain.AddDays(monthStart, -domain.WeekdayIndex(monthStart)) weeks := (domain.WeekdayIndex(monthStart) + monthEnd.Day() + 6) / 7 for wi := 0; wi < weeks; wi++ { week := make([]screenDay, 0, 7) for di := 0; di < 7; di++ { d := domain.AddDays(gridStart, wi*7+di) ds := domain.Date(d) due := stats.Heat[ds] week = append(week, screenDay{ Date: ds, Day: d.Day(), InMonth: d.Month() == today.Month(), IsToday: ds == domain.Date(today), IsPast: d.Before(today), Due: due, Heat: heatLevel(due), }) } data.Weeks = append(data.Weeks, week) } if adj, err := s.db.RecentAdjustments(6); err == nil { data.Recent = adj } list, done := s.todayTaskList(today) data.TodayTasks = len(list) data.TodayDone = done data.TodayList = subViews(list, today, warn) p := s.newPage(r, "大屏", "screen") p.D = data s.render(w, r, "screen", p) } // todayTaskList 返回今天应服务的订阅与已完成数量。 func (s *Server) todayTaskList(today time.Time) ([]store.Subscription, int) { ts := domain.Date(today) subs, err := s.db.CalendarSubs(ts, ts) if err != nil { return nil, 0 } ids := make([]int64, 0, len(subs)) for _, sub := range subs { ids = append(ids, sub.ID) } pauses, _ := s.db.PausesForSubs(ids) checkins, _ := s.db.CheckinsBetween(ts, ts) warn := s.warnDays() out := []store.Subscription{} done := 0 for _, sub := range subs { if sub.Status == domain.StatusClosed { continue } if sub.StartDate > ts || sub.EndDate == "" || sub.EndDate < ts { continue } cs := calSub{sub: sub, pauses: pauses[sub.ID], hasEnd: true, eval: sub.Eval(today, warn)} if sub.Unit == domain.UnitCount && cs.eval.RemainingCount <= 0 { continue } if cs.pausedOn(ts) { continue } out = append(out, sub) if ci, ok := checkins[sub.ID][ts]; ok && ci.Status == "done" { done++ } } return out, done }