feat: reconcile posted articles against live posts table, revert orphaned articles to discovered
This commit is contained in:
parent
52e18daef0
commit
82e9246fdd
|
|
@ -514,7 +514,29 @@ func (s *OfficialAccountsService) PostNextArticle(ctx context.Context, configID
|
||||||
return &art, postID, nil
|
return &art, postID, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ReconcilePostedArticles checks for articles marked 'posted' whose post no longer
|
||||||
|
// exists in the posts table, and reverts them to 'discovered' so they can be re-posted.
|
||||||
|
func (s *OfficialAccountsService) ReconcilePostedArticles(ctx context.Context, configID string) (int, error) {
|
||||||
|
tag, err := s.pool.Exec(ctx, `
|
||||||
|
UPDATE official_account_articles a
|
||||||
|
SET status = 'discovered', post_id = NULL, posted_at = NULL
|
||||||
|
WHERE a.config_id = $1
|
||||||
|
AND a.status = 'posted'
|
||||||
|
AND a.post_id IS NOT NULL
|
||||||
|
AND NOT EXISTS (SELECT 1 FROM public.posts p WHERE p.id::text = a.post_id)
|
||||||
|
`, configID)
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
reverted := int(tag.RowsAffected())
|
||||||
|
if reverted > 0 {
|
||||||
|
log.Info().Int("reverted", reverted).Str("config", configID).Msg("[OfficialAccounts] Reconciled orphaned articles back to discovered")
|
||||||
|
}
|
||||||
|
return reverted, nil
|
||||||
|
}
|
||||||
|
|
||||||
// GetArticleQueue returns articles for a config filtered by status.
|
// GetArticleQueue returns articles for a config filtered by status.
|
||||||
|
// For 'posted' status, only returns articles whose post still exists in the posts table.
|
||||||
func (s *OfficialAccountsService) GetArticleQueue(ctx context.Context, configID string, status string, limit int) ([]CachedArticle, error) {
|
func (s *OfficialAccountsService) GetArticleQueue(ctx context.Context, configID string, status string, limit int) ([]CachedArticle, error) {
|
||||||
if limit <= 0 {
|
if limit <= 0 {
|
||||||
limit = 50
|
limit = 50
|
||||||
|
|
@ -524,12 +546,17 @@ func (s *OfficialAccountsService) GetArticleQueue(ctx context.Context, configID
|
||||||
orderDir = "ASC" // oldest first (next to be posted)
|
orderDir = "ASC" // oldest first (next to be posted)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// For 'posted', reconcile first to catch deleted posts, then query
|
||||||
|
if status == "posted" {
|
||||||
|
_, _ = s.ReconcilePostedArticles(ctx, configID)
|
||||||
|
}
|
||||||
|
|
||||||
query := fmt.Sprintf(`
|
query := fmt.Sprintf(`
|
||||||
SELECT id, config_id, guid, title, link, source_name, source_url, description, pub_date,
|
SELECT a.id, a.config_id, a.guid, a.title, a.link, a.source_name, a.source_url, a.description, a.pub_date,
|
||||||
status, post_id, error_message, discovered_at, posted_at
|
a.status, a.post_id, a.error_message, a.discovered_at, a.posted_at
|
||||||
FROM official_account_articles
|
FROM official_account_articles a
|
||||||
WHERE config_id = $1 AND status = $2
|
WHERE a.config_id = $1 AND a.status = $2
|
||||||
ORDER BY discovered_at %s
|
ORDER BY a.discovered_at %s
|
||||||
LIMIT $3
|
LIMIT $3
|
||||||
`, orderDir)
|
`, orderDir)
|
||||||
|
|
||||||
|
|
@ -564,7 +591,11 @@ type ArticleStats struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetArticleStats returns article counts by status for a config.
|
// GetArticleStats returns article counts by status for a config.
|
||||||
|
// Reconciles orphaned articles first so counts reflect reality.
|
||||||
func (s *OfficialAccountsService) GetArticleStats(ctx context.Context, configID string) (*ArticleStats, error) {
|
func (s *OfficialAccountsService) GetArticleStats(ctx context.Context, configID string) (*ArticleStats, error) {
|
||||||
|
// Reconcile first — revert articles whose posts were deleted
|
||||||
|
_, _ = s.ReconcilePostedArticles(ctx, configID)
|
||||||
|
|
||||||
rows, err := s.pool.Query(ctx, `
|
rows, err := s.pool.Query(ctx, `
|
||||||
SELECT status, COUNT(*) FROM official_account_articles
|
SELECT status, COUNT(*) FROM official_account_articles
|
||||||
WHERE config_id = $1
|
WHERE config_id = $1
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue