103 lines
4.2 KiB
Go
103 lines
4.2 KiB
Go
package creator
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"time"
|
|
)
|
|
|
|
// CommentAggregationFilter uses comment publication time, not collection time.
|
|
type CommentAggregationFilter struct {
|
|
SourceType string
|
|
SourceID string
|
|
Hours int
|
|
}
|
|
|
|
func (filter CommentAggregationFilter) Validate() error {
|
|
if filter.SourceType != SourceOwned && filter.SourceType != SourceCompetitor {
|
|
return ErrInvalid
|
|
}
|
|
switch filter.Hours {
|
|
case 1, 6, 12, 24, 72, 120, 168:
|
|
return nil
|
|
default:
|
|
return ErrInvalid
|
|
}
|
|
}
|
|
|
|
// AggregatedComment adds the owning account and work to an already collected comment.
|
|
type AggregatedComment struct {
|
|
ID string `json:"id"`
|
|
AuthorUID string `json:"author_uid"`
|
|
AuthorName string `json:"author_name"`
|
|
Content string `json:"content"`
|
|
PublishedAt time.Time `json:"published_at"`
|
|
SourceType string `json:"source_type"`
|
|
SourceID string `json:"source_id"`
|
|
SourceName string `json:"source_name"`
|
|
SourceAccountKey string `json:"source_account_key"`
|
|
WorkID string `json:"work_id"`
|
|
WorkKey string `json:"work_key"`
|
|
WorkTitle string `json:"work_title"`
|
|
WorkURL string `json:"work_url"`
|
|
}
|
|
|
|
func buildCommentAggregationQuery(filter CommentAggregationFilter, now time.Time) (string, []any, error) {
|
|
if err := filter.Validate(); err != nil {
|
|
return "", nil, err
|
|
}
|
|
query := ` FROM creator_comment c
|
|
JOIN creator_work w ON w.id = c.work_id
|
|
LEFT JOIN social_account a ON w.source_type = 'owned' AND a.account_id = w.source_id
|
|
LEFT JOIN creator_competitor rival ON w.source_type = 'competitor' AND rival.competitor_id = w.source_id
|
|
WHERE w.source_type = $1 AND c.published_at >= $2 AND c.published_at <= $3`
|
|
args := []any{filter.SourceType, now.UTC().Add(-time.Duration(filter.Hours) * time.Hour), now.UTC()}
|
|
if filter.SourceID != "" {
|
|
args = append(args, filter.SourceID)
|
|
query += " AND w.source_id = $4"
|
|
}
|
|
return query, args, nil
|
|
}
|
|
|
|
func (s *Store) ListCommentAggregationPage(ctx context.Context, filter CommentAggregationFilter, page, pageSize int, now time.Time) (Page[AggregatedComment], error) {
|
|
offset, limit, err := pageBounds(page, pageSize)
|
|
if err != nil {
|
|
return Page[AggregatedComment]{}, err
|
|
}
|
|
from, args, err := buildCommentAggregationQuery(filter, now)
|
|
if err != nil {
|
|
return Page[AggregatedComment]{}, err
|
|
}
|
|
result := Page[AggregatedComment]{Data: []AggregatedComment{}, Page: page, PageSize: pageSize}
|
|
if err := s.db.QueryRowContext(ctx, "SELECT COUNT(*)"+from, args...).Scan(&result.Total); err != nil {
|
|
return Page[AggregatedComment]{}, fmt.Errorf("count comment aggregation source=%s account=%s hours=%d: %w", filter.SourceType, filter.SourceID, filter.Hours, err)
|
|
}
|
|
query := `SELECT c.comment_id, c.author_uid, c.author_name, c.content, c.published_at,
|
|
w.source_type, w.source_id,
|
|
CASE WHEN w.source_type = 'owned' THEN COALESCE(a.name, '') ELSE COALESCE(rival.nickname, '') END,
|
|
CASE WHEN w.source_type = 'owned' THEN COALESCE(a.platform_account_key, '') ELSE COALESCE(rival.platform_account_key, '') END,
|
|
w.work_id, w.work_key, w.title, w.original_url` + from
|
|
query += fmt.Sprintf(" ORDER BY c.published_at DESC, c.id DESC LIMIT $%d OFFSET $%d", len(args)+1, len(args)+2)
|
|
args = append(args, limit, offset)
|
|
rows, err := s.db.QueryContext(ctx, query, args...)
|
|
if err != nil {
|
|
return Page[AggregatedComment]{}, fmt.Errorf("list comment aggregation source=%s account=%s hours=%d page=%d: %w", filter.SourceType, filter.SourceID, filter.Hours, page, err)
|
|
}
|
|
defer rows.Close()
|
|
for rows.Next() {
|
|
var item AggregatedComment
|
|
if err := rows.Scan(&item.ID, &item.AuthorUID, &item.AuthorName, &item.Content, &item.PublishedAt,
|
|
&item.SourceType, &item.SourceID, &item.SourceName, &item.SourceAccountKey,
|
|
&item.WorkID, &item.WorkKey, &item.WorkTitle, &item.WorkURL); err != nil {
|
|
return Page[AggregatedComment]{}, fmt.Errorf("scan comment aggregation: %w", err)
|
|
}
|
|
item.PublishedAt = item.PublishedAt.UTC()
|
|
result.Data = append(result.Data, item)
|
|
}
|
|
if err := rows.Err(); err != nil {
|
|
return Page[AggregatedComment]{}, fmt.Errorf("read comment aggregation: %w", err)
|
|
}
|
|
result.HasNext = offset+len(result.Data) < result.Total
|
|
return result, nil
|
|
}
|