package handler import ( "encoding/json" "fmt" "sync" ) // Hub 按 runId 分组的 SSE 订阅中心 type Hub struct { mu sync.Mutex subs map[string]map[chan []byte]struct{} } func NewHub() *Hub { return &Hub{subs: make(map[string]map[chan []byte]struct{})} } func (h *Hub) Subscribe(runId string) chan []byte { ch := make(chan []byte, 64) h.mu.Lock() defer h.mu.Unlock() if h.subs[runId] == nil { h.subs[runId] = make(map[chan []byte]struct{}) } h.subs[runId][ch] = struct{}{} return ch } func (h *Hub) Unsubscribe(runId string, ch chan []byte) { h.mu.Lock() defer h.mu.Unlock() if set := h.subs[runId]; set != nil { delete(set, ch) if len(set) == 0 { delete(h.subs, runId) } } } // Publish 向 runId 的所有订阅者推送一条 SSE 帧(event + data) func (h *Hub) Publish(runId, event string, payload any) { data, err := json.Marshal(payload) if err != nil { return } frame := []byte(fmt.Sprintf("event: %s\ndata: %s\n\n", event, data)) h.mu.Lock() defer h.mu.Unlock() for ch := range h.subs[runId] { select { case ch <- frame: default: // 订阅者消费过慢则丢弃,避免阻塞模拟引擎 } } }