Files
aitrade/pkg/db/news.go
T
kaedwen fe16cb55cb
Build and Push Docker Image / build-and-push (push) Successful in 3m43s
fix news page
Signed-off-by: kaedwen <kaedwen@heinrich.blue>
2026-07-09 22:54:40 +02:00

174 lines
4.4 KiB
Go

package db
import (
"context"
"fmt"
"time"
"github.com/pheinrich/aitrade/pkg/model"
)
type NewsRepository struct {
db Database
}
func NewNewsRepository(db Database) *NewsRepository {
return &NewsRepository{db: db}
}
func (r *NewsRepository) Create(ctx context.Context, article *model.NewsArticle) error {
query := `
INSERT INTO news_articles (
source, title, url, content, published_at, fetched_at, symbols,
sentiment_score, sentiment_label,
llm_sentiment_score, llm_model, llm_confidence, sentiment_method
)
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
ON CONFLICT(url) DO NOTHING
`
result, err := r.db.ExecContext(ctx, query,
article.Source,
article.Title,
article.URL,
article.Content,
article.PublishedAt,
article.FetchedAt,
article.Symbols,
article.SentimentScore,
article.SentimentLabel,
article.LLMSentimentScore,
article.LLMModel,
article.LLMConfidence,
article.SentimentMethod,
)
if err != nil {
return fmt.Errorf("failed to insert news article: %w", err)
}
id, err := result.LastInsertId()
if err != nil {
// ON CONFLICT DO NOTHING means no rows affected, but not an error
rowsAffected, _ := result.RowsAffected()
if rowsAffected == 0 {
return nil // Duplicate, silently ignore
}
return fmt.Errorf("failed to get last insert id: %w", err)
}
article.ID = id
return nil
}
func (r *NewsRepository) GetRecent(ctx context.Context, limit int) ([]*model.NewsArticle, error) {
return r.GetRecentWithOffset(ctx, limit, 0)
}
func (r *NewsRepository) GetRecentWithOffset(ctx context.Context, limit, offset int) ([]*model.NewsArticle, error) {
query := `
SELECT id, source, title, url, content, published_at, fetched_at, symbols,
sentiment_score, sentiment_label,
llm_sentiment_score, llm_model, llm_confidence, sentiment_method
FROM news_articles
ORDER BY published_at DESC
LIMIT ? OFFSET ?
`
rows, err := r.db.QueryContext(ctx, query, limit, offset)
if err != nil {
return nil, fmt.Errorf("failed to query recent news: %w", err)
}
defer rows.Close()
var articles []*model.NewsArticle
for rows.Next() {
var article model.NewsArticle
if err := rows.Scan(
&article.ID,
&article.Source,
&article.Title,
&article.URL,
&article.Content,
&article.PublishedAt,
&article.FetchedAt,
&article.Symbols,
&article.SentimentScore,
&article.SentimentLabel,
&article.LLMSentimentScore,
&article.LLMModel,
&article.LLMConfidence,
&article.SentimentMethod,
); err != nil {
return nil, fmt.Errorf("failed to scan news article: %w", err)
}
articles = append(articles, &article)
}
if err := rows.Err(); err != nil {
return nil, fmt.Errorf("rows error: %w", err)
}
return articles, nil
}
func (r *NewsRepository) GetBySymbol(ctx context.Context, symbol string, since time.Time, limit int) ([]*model.NewsArticle, error) {
query := `
SELECT id, source, title, url, content, published_at, fetched_at, symbols,
sentiment_score, sentiment_label,
llm_sentiment_score, llm_model, llm_confidence, sentiment_method
FROM news_articles
WHERE symbols LIKE ? AND published_at >= ?
ORDER BY published_at DESC
LIMIT ?
`
rows, err := r.db.QueryContext(ctx, query, "%"+symbol+"%", since, limit)
if err != nil {
return nil, fmt.Errorf("failed to query news by symbol: %w", err)
}
defer rows.Close()
var articles []*model.NewsArticle
for rows.Next() {
var article model.NewsArticle
if err := rows.Scan(
&article.ID,
&article.Source,
&article.Title,
&article.URL,
&article.Content,
&article.PublishedAt,
&article.FetchedAt,
&article.Symbols,
&article.SentimentScore,
&article.SentimentLabel,
&article.LLMSentimentScore,
&article.LLMModel,
&article.LLMConfidence,
&article.SentimentMethod,
); err != nil {
return nil, fmt.Errorf("failed to scan news article: %w", err)
}
articles = append(articles, &article)
}
if err := rows.Err(); err != nil {
return nil, fmt.Errorf("error iterating news rows: %w", err)
}
return articles, nil
}
// CountRecentByTime returns the count of news articles fetched since the given time
func (r *NewsRepository) CountRecentByTime(ctx context.Context, since time.Time) (int, error) {
query := `SELECT COUNT(*) FROM news_articles WHERE fetched_at >= ?`
var count int
err := r.db.QueryRowContext(ctx, query, since).Scan(&count)
if err != nil {
return 0, fmt.Errorf("failed to count recent news: %w", err)
}
return count, nil
}