110 lines
2.1 KiB
Go
110 lines
2.1 KiB
Go
package app
|
|
|
|
import (
|
|
"fmt"
|
|
"sort"
|
|
"sync"
|
|
"time"
|
|
)
|
|
|
|
type GuestStatus struct {
|
|
Name string `json:"name"`
|
|
Present bool `json:"present"`
|
|
LastJoin time.Time `json:"last_join"`
|
|
LastLeave time.Time `json:"last_leave"`
|
|
AbsentFor string `json:"absent_for"`
|
|
}
|
|
|
|
type GuestTracker struct {
|
|
mu sync.RWMutex
|
|
current string
|
|
items map[string]*GuestStatus
|
|
}
|
|
|
|
func NewGuestTracker(names []string) *GuestTracker {
|
|
items := make(map[string]*GuestStatus, len(names))
|
|
for _, name := range names {
|
|
items[name] = &GuestStatus{Name: name}
|
|
}
|
|
return &GuestTracker{items: items}
|
|
}
|
|
|
|
func (t *GuestTracker) SetCurrentInstance(label string) {
|
|
t.mu.Lock()
|
|
defer t.mu.Unlock()
|
|
t.current = label
|
|
}
|
|
|
|
func (t *GuestTracker) CurrentInstance() string {
|
|
t.mu.RLock()
|
|
defer t.mu.RUnlock()
|
|
return t.current
|
|
}
|
|
|
|
func (t *GuestTracker) MarkJoin(name string, at time.Time) {
|
|
t.mu.Lock()
|
|
defer t.mu.Unlock()
|
|
s := t.ensure(name)
|
|
s.Present = true
|
|
s.LastJoin = at
|
|
s.AbsentFor = ""
|
|
}
|
|
|
|
func (t *GuestTracker) MarkLeave(name string, at time.Time) {
|
|
t.mu.Lock()
|
|
defer t.mu.Unlock()
|
|
s := t.ensure(name)
|
|
s.Present = false
|
|
s.LastLeave = at
|
|
if !s.LastJoin.IsZero() {
|
|
s.AbsentFor = humanSince(at)
|
|
}
|
|
}
|
|
|
|
func (t *GuestTracker) Snapshot(now time.Time) []GuestStatus {
|
|
t.mu.RLock()
|
|
defer t.mu.RUnlock()
|
|
out := make([]GuestStatus, 0, len(t.items))
|
|
for _, s := range t.items {
|
|
cp := *s
|
|
if !cp.Present && !cp.LastLeave.IsZero() {
|
|
cp.AbsentFor = humanSince(cp.LastLeave, now)
|
|
}
|
|
out = append(out, cp)
|
|
}
|
|
sort.Slice(out, func(i, j int) bool { return out[i].Name < out[j].Name })
|
|
return out
|
|
}
|
|
|
|
func (t *GuestTracker) ensure(name string) *GuestStatus {
|
|
if s, ok := t.items[name]; ok {
|
|
return s
|
|
}
|
|
s := &GuestStatus{Name: name}
|
|
t.items[name] = s
|
|
return s
|
|
}
|
|
|
|
func humanSince(at time.Time, now ...time.Time) string {
|
|
ref := time.Now()
|
|
if len(now) > 0 {
|
|
ref = now[0]
|
|
}
|
|
if at.IsZero() {
|
|
return ""
|
|
}
|
|
d := ref.Sub(at)
|
|
if d < time.Minute {
|
|
return "1分未満"
|
|
}
|
|
m := int(d.Minutes())
|
|
if m < 60 {
|
|
return fmt.Sprintf("%d分前", m)
|
|
}
|
|
h := m / 60
|
|
if h < 24 {
|
|
return fmt.Sprintf("%d時間前", h)
|
|
}
|
|
return fmt.Sprintf("%d日前", h/24)
|
|
}
|