- 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>
61 lines
1.7 KiB
Go
61 lines
1.7 KiB
Go
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
|
||
}
|