feat: 增加 SafeW 已监控群列表接口

通过 getUpdates 将群写入 Redis,供创建/编辑渠道时选择 chat_id,避免前端重复传递 token。
This commit is contained in:
2026-08-15 00:40:40 +08:00
parent 0d0cd0c510
commit bd8a9ff96d
14 changed files with 2265 additions and 4 deletions
+117
View File
@@ -0,0 +1,117 @@
package safew
import (
"context"
"log/slog"
"sync"
"time"
"aiaa-notification-service/internal/adapter"
)
type Poller func(token string, offset int64, timeout int) ([]adapter.SafewChat, int64, error)
type Watcher struct {
store ChatStore
poll Poller
bgIdle time.Duration
mu sync.Mutex
running map[string]context.CancelFunc
stopped bool
}
func NewWatcher(store ChatStore, poll Poller) *Watcher {
return &Watcher{
store: store,
poll: poll,
bgIdle: time.Second,
running: map[string]context.CancelFunc{},
}
}
func (w *Watcher) Ensure(token string) {
if token == "" || w == nil {
return
}
w.mu.Lock()
defer w.mu.Unlock()
if w.stopped {
return
}
if _, ok := w.running[token]; ok {
return
}
ctx, cancel := context.WithCancel(context.Background())
w.running[token] = cancel
go w.loop(ctx, token)
}
func (w *Watcher) Stop() {
w.mu.Lock()
w.stopped = true
for _, cancel := range w.running {
cancel()
}
w.running = map[string]context.CancelFunc{}
w.mu.Unlock()
}
func (w *Watcher) Refresh(ctx context.Context, token string) error {
return w.pollOnce(ctx, token, 0)
}
func (w *Watcher) List(ctx context.Context, token, q string) ([]adapter.SafewChat, error) {
chats, err := w.store.ListSafewChats(ctx, token)
if err != nil {
return nil, err
}
return adapter.FilterSafewChats(chats, q), nil
}
func (w *Watcher) loop(ctx context.Context, token string) {
for {
if err := w.pollOnce(ctx, token, 30); err != nil {
if ctx.Err() != nil {
return
}
slog.Warn("safew watcher poll", "error", err)
}
idle := w.bgIdle
if idle <= 0 {
idle = time.Second
}
select {
case <-ctx.Done():
return
case <-time.After(idle):
}
}
}
func (w *Watcher) pollOnce(ctx context.Context, token string, timeout int) error {
ok, err := w.store.TrySafewPollLock(ctx, token, 35*time.Second)
if err != nil {
return err
}
if !ok {
return nil
}
defer func() { _ = w.store.UnlockSafewPoll(ctx, token) }()
offset, err := w.store.GetSafewOffset(ctx, token)
if err != nil {
return err
}
chats, next, err := w.poll(token, offset, timeout)
if err != nil {
return err
}
if err := w.store.MergeSafewChats(ctx, token, chats); err != nil {
return err
}
if next != offset {
return w.store.SetSafewOffset(ctx, token, next)
}
return nil
}