package repository import ( "context" "database/sql" "fmt" "strings" "time" ) const BusinessEventSucceeded = "成功" type BusinessEvent struct { ID int64 `json:"id"` OccurredAt time.Time `json:"occurred_at"` Event string `json:"event"` Status string `json:"status"` } func (r *SQLiteUsageRepository) RecordBusinessEvent(ctx context.Context, event, status string, occurredAt time.Time) error { return insertBusinessEvent(ctx, r.db, event, status, occurredAt) } func (r *SQLiteUsageRepository) ListBusinessEvents(ctx context.Context, limit int) ([]BusinessEvent, error) { if limit < 1 || limit > 500 { limit = 200 } rows, err := r.readDB.QueryContext(ctx, ` SELECT id, occurred_at, event, status FROM business_events ORDER BY occurred_at DESC, id DESC LIMIT ?`, limit) if err != nil { return nil, fmt.Errorf("查询业务日志: %w", err) } defer rows.Close() events := make([]BusinessEvent, 0, limit) for rows.Next() { var item BusinessEvent var occurredRaw string if err := rows.Scan(&item.ID, &occurredRaw, &item.Event, &item.Status); err != nil { return nil, fmt.Errorf("读取业务日志: %w", err) } item.OccurredAt, _ = time.Parse(time.RFC3339Nano, occurredRaw) events = append(events, item) } if err := rows.Err(); err != nil { return nil, fmt.Errorf("遍历业务日志: %w", err) } return events, nil } type businessEventExecer interface { ExecContext(context.Context, string, ...any) (sql.Result, error) } func insertBusinessEvent(ctx context.Context, executor businessEventExecer, event, status string, occurredAt time.Time) error { event = strings.TrimSpace(event) status = strings.TrimSpace(status) if event == "" || status == "" { return fmt.Errorf("业务日志事件和状态不能为空") } if occurredAt.IsZero() { occurredAt = time.Now().UTC() } if _, err := executor.ExecContext(ctx, ` INSERT INTO business_events (occurred_at, event, status) VALUES (?, ?, ?)`, formatTime(occurredAt), event, status); err != nil { return fmt.Errorf("写入业务日志: %w", err) } return nil } func quotedName(value string) string { value = strings.TrimSpace(value) if value == "" { return "未命名用户" } return "`" + strings.ReplaceAll(value, "`", "") + "`" } func decimalMicros(value int64) string { prefix := "" if value < 0 { prefix = "-" value = -value } whole := value / 1_000_000 fraction := fmt.Sprintf("%06d", value%1_000_000) fraction = strings.TrimRight(fraction, "0") if fraction == "" { return prefix + fmt.Sprintf("%d", whole) } return prefix + fmt.Sprintf("%d.%s", whole, fraction) }