topfans/backend/services/statisticService/repository/event_repo.go
zerosaturation bed8f8e578 feat(statistic): T4-T8 event collection framework (Event + Sink + Repo + Service + Workers)
- Event model + ToJSON
- EventSink interface + ChannelEventSink (non-blocking Submit)
- event_repo: batch INSERT ON CONFLICT DO NOTHING dedup
- event_service: 7-type whitelist + 1KB props limit + ReceivedAt auto-fill
- event_flusher: 100/1s batch + sync metric_recent_level_ups on level_up
- metric_weekly + metric_upcoming workers (5min/15min with pg_try_advisory_lock)
- partitioner: 7-day pre-create + 30-day cleanup (00:05 create / 00:30 cleanup)
- 22 unit + integration tests (model/repo/service/sink/worker)

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-08 17:20:53 +08:00

61 lines
1.7 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

package repository
import (
"context"
"database/sql"
"encoding/json"
"fmt"
"strings"
"github.com/topfans/backend/services/statisticService/model"
)
// EventRepository events 表操作
type EventRepository struct {
db *sql.DB
schema string
}
// NewEventRepository 构造 EventRepository
func NewEventRepository(db *sql.DB, schema string) *EventRepository {
return &EventRepository{db: db, schema: schema}
}
// InsertBatch 批量插入事件event_id 重复时 ON CONFLICT DO NOTHING
// 返回实际插入的行数
func (r *EventRepository) InsertBatch(ctx context.Context, events []*model.Event) (int, error) {
if len(events) == 0 {
return 0, nil
}
placeholders := make([]string, 0, len(events))
args := make([]interface{}, 0, len(events)*7)
for _, e := range events {
props := e.Properties
if props == nil {
props = map[string]string{}
}
propsJSON, _ := json.Marshal(props)
placeholders = append(placeholders, fmt.Sprintf("($%d, $%d, $%d, $%d, $%d, $%d, $%d)",
len(args)+1, len(args)+2, len(args)+3, len(args)+4, len(args)+5, len(args)+6, len(args)+7))
args = append(args, e.EventID, e.UserID, e.StarID, e.EventType, e.OccurredAt, e.ReceivedAt, string(propsJSON))
}
query := fmt.Sprintf(`
INSERT INTO %s.events (event_id, user_id, star_id, event_type, occurred_at, received_at, properties)
VALUES %s
ON CONFLICT (event_id, received_at) DO NOTHING
`, r.schema, strings.Join(placeholders, ","))
res, err := r.db.ExecContext(ctx, query, args...)
if err != nil {
return 0, fmt.Errorf("insert events: %w", err)
}
n, err := res.RowsAffected()
if err != nil {
return 0, fmt.Errorf("rows affected: %w", err)
}
return int(n), nil
}