package jobdef import ( "context" "encoding/json" "fmt" "os" "path/filepath" "strings" "sync" "github.com/redis/go-redis/v9" "go.mongodb.org/mongo-driver/mongo" "scheduler-backend/pkg/config" "scheduler-backend/pkg/log" ) const logTailLines = 2000 type LogCollector struct { mu sync.Mutex lines []string } func NewLogCollector() *LogCollector { return &LogCollector{} } func (c *LogCollector) Appendf(format string, args ...any) { if c == nil { return } c.mu.Lock() c.lines = append(c.lines, fmt.Sprintf(format, args...)) c.mu.Unlock() } func (c *LogCollector) String() string { if c == nil { return "" } c.mu.Lock() defer c.mu.Unlock() return strings.Join(c.lines, "\n") } type FlushResult struct { LogText string LogFile string } func (c *LogCollector) Flush(executionID string) FlushResult { if c == nil { return FlushResult{} } c.mu.Lock() lines := c.lines c.mu.Unlock() if len(lines) == 0 { return FlushResult{} } if len(lines) <= logTailLines { return FlushResult{LogText: strings.Join(lines, "\n")} } filename := executionID + ".log" dir := filepath.Join("logs", "joblog") _ = os.MkdirAll(dir, 0o755) fullPath := filepath.Join(dir, filename) _ = os.WriteFile(fullPath, []byte(strings.Join(lines, "\n")+"\n"), 0o644) tail := lines[len(lines)-logTailLines:] header := fmt.Sprintf("[完整日志: %s (%d行)]", filename, len(lines)) return FlushResult{ LogText: header + "\n" + strings.Join(tail, "\n"), LogFile: filename, } } type ExecuteRequest struct { ExecutionID string `json:"executionID"` JobID string `json:"jobID"` TriggerType string `json:"triggerType"` Params json.RawMessage `json:"params"` LogCollector *LogCollector `json:"-"` } type Runtime struct { Config config.Config Logger log.Logger MetaDB *mongo.Database BusinessDB *mongo.Database Redis *redis.Client } type Handler interface { Key() string Name() string Description() string Run(ctx context.Context, runtime Runtime, req ExecuteRequest) error }