package models import ( "database/sql" "errors" "os" "path/filepath" "time" _ "modernc.org/sqlite" ) type DownloadRecord struct { ID int64 `json:"id"` URL string `json:"url"` Source string `json:"source"` OutputPath string `json:"outputPath"` Status string `json:"status"` StartedAt time.Time `json:"startedAt"` CompletedAt time.Time `json:"completedAt,omitempty"` } func InitDB(path string) (*sql.DB, error) { if err := os.MkdirAll(filepath.Dir(path), 0o755); err != nil { return nil, err } db, err := sql.Open("sqlite", path) if err != nil { return nil, err } schema := ` CREATE TABLE IF NOT EXISTS download_history ( id INTEGER PRIMARY KEY AUTOINCREMENT, url TEXT NOT NULL, source TEXT NOT NULL, output_path TEXT NOT NULL, status TEXT NOT NULL, started_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, completed_at DATETIME ); CREATE INDEX IF NOT EXISTS idx_download_history_url ON download_history(url); ` if _, err := db.Exec(schema); err != nil { return nil, err } return db, nil } func InsertDownload(db *sql.DB, url, source, outputPath, status string) (int64, error) { res, err := db.Exec( `INSERT INTO download_history (url, source, output_path, status) VALUES (?, ?, ?, ?)`, url, source, outputPath, status, ) if err != nil { return 0, err } return res.LastInsertId() } func MarkDownloadCompleted(db *sql.DB, id int64, status string) error { _, err := db.Exec( `UPDATE download_history SET status = ?, completed_at = CURRENT_TIMESTAMP WHERE id = ?`, status, id, ) return err } func FindByURL(db *sql.DB, url string) (*DownloadRecord, error) { row := db.QueryRow( `SELECT id, url, source, output_path, status, started_at, COALESCE(completed_at, '') FROM download_history WHERE url = ? ORDER BY id DESC LIMIT 1`, url, ) var rec DownloadRecord var completedRaw string if err := row.Scan(&rec.ID, &rec.URL, &rec.Source, &rec.OutputPath, &rec.Status, &rec.StartedAt, &completedRaw); err != nil { if errors.Is(err, sql.ErrNoRows) { return nil, nil } return nil, err } if completedRaw != "" { parsed, err := time.Parse("2006-01-02 15:04:05", completedRaw) if err == nil { rec.CompletedAt = parsed } } return &rec, nil }