package state import ( "encoding/json" "fmt" "os" "path/filepath" "time" ) type State struct { RunID string `json:"runId"` SentAt time.Time `json:"sentAt"` } type Store struct{ dir string } func New(dir string) Store { return Store{dir: dir} } func (s Store) Prepare() error { dir := filepath.Join(s.dir, "sent") if err := os.MkdirAll(dir, 0o700); err != nil { return fmt.Errorf("create sent marker directory: %w", err) } file, err := os.CreateTemp(dir, ".write-check-*") if err != nil { return fmt.Errorf("check sent marker directory: %w", err) } name := file.Name() if err := file.Close(); err != nil { _ = os.Remove(name) return fmt.Errorf("close sent marker write check: %w", err) } if err := os.Remove(name); err != nil { return fmt.Errorf("remove sent marker write check: %w", err) } return nil } func (s Store) Has(runID string) (bool, error) { _, err := os.Stat(filepath.Join(s.dir, "sent", runID+".json")) if err == nil { return true, nil } if os.IsNotExist(err) { return false, nil } return false, fmt.Errorf("check sent marker: %w", err) } func (s Store) Mark(runID string) error { if err := s.Prepare(); err != nil { return err } dir := filepath.Join(s.dir, "sent") body, _ := json.Marshal(State{RunID: runID, SentAt: time.Now()}) tmp := filepath.Join(dir, fmt.Sprintf(".%s.tmp", runID)) final := filepath.Join(dir, runID+".json") if err := os.WriteFile(tmp, body, 0o600); err != nil { return err } return os.Rename(tmp, final) } // Dir 返回状态目录,供调用方复用(例如告警节流状态落盘)。 func (s Store) Dir() string { return s.dir }