Files

94 lines
2.6 KiB
Go

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)
}