完善联调标准化与诊断链路
This commit is contained in:
@@ -3,6 +3,9 @@ package service
|
||||
import (
|
||||
"context"
|
||||
"net/http"
|
||||
"sort"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"cmr-backend/internal/apperr"
|
||||
"cmr-backend/internal/store/postgres"
|
||||
@@ -11,6 +14,39 @@ import (
|
||||
type DevService struct {
|
||||
appEnv string
|
||||
store *postgres.Store
|
||||
mu sync.Mutex
|
||||
logSeq int64
|
||||
logs []ClientDebugLogEntry
|
||||
}
|
||||
|
||||
type ClientDebugLogEntry struct {
|
||||
ID int64 `json:"id"`
|
||||
Source string `json:"source"`
|
||||
Level string `json:"level"`
|
||||
Category string `json:"category,omitempty"`
|
||||
Message string `json:"message"`
|
||||
EventID string `json:"eventId,omitempty"`
|
||||
ReleaseID string `json:"releaseId,omitempty"`
|
||||
SessionID string `json:"sessionId,omitempty"`
|
||||
ManifestURL string `json:"manifestUrl,omitempty"`
|
||||
Route string `json:"route,omitempty"`
|
||||
OccurredAt time.Time `json:"occurredAt"`
|
||||
ReceivedAt time.Time `json:"receivedAt"`
|
||||
Details map[string]any `json:"details,omitempty"`
|
||||
}
|
||||
|
||||
type CreateClientDebugLogInput struct {
|
||||
Source string `json:"source"`
|
||||
Level string `json:"level"`
|
||||
Category string `json:"category"`
|
||||
Message string `json:"message"`
|
||||
EventID string `json:"eventId"`
|
||||
ReleaseID string `json:"releaseId"`
|
||||
SessionID string `json:"sessionId"`
|
||||
ManifestURL string `json:"manifestUrl"`
|
||||
Route string `json:"route"`
|
||||
OccurredAt string `json:"occurredAt"`
|
||||
Details map[string]any `json:"details"`
|
||||
}
|
||||
|
||||
func NewDevService(appEnv string, store *postgres.Store) *DevService {
|
||||
@@ -30,3 +66,83 @@ func (s *DevService) BootstrapDemo(ctx context.Context) (*postgres.DemoBootstrap
|
||||
}
|
||||
return s.store.EnsureDemoData(ctx)
|
||||
}
|
||||
|
||||
func (s *DevService) AddClientDebugLog(_ context.Context, input CreateClientDebugLogInput) (*ClientDebugLogEntry, error) {
|
||||
if !s.Enabled() {
|
||||
return nil, apperr.New(http.StatusNotFound, "not_found", "dev client logs are disabled")
|
||||
}
|
||||
if input.Message == "" {
|
||||
return nil, apperr.New(http.StatusBadRequest, "invalid_request", "message is required")
|
||||
}
|
||||
if input.Source == "" {
|
||||
input.Source = "unknown"
|
||||
}
|
||||
if input.Level == "" {
|
||||
input.Level = "info"
|
||||
}
|
||||
|
||||
occurredAt := time.Now().UTC()
|
||||
if input.OccurredAt != "" {
|
||||
parsed, err := time.Parse(time.RFC3339, input.OccurredAt)
|
||||
if err != nil {
|
||||
return nil, apperr.New(http.StatusBadRequest, "invalid_request", "occurredAt must be RFC3339")
|
||||
}
|
||||
occurredAt = parsed.UTC()
|
||||
}
|
||||
|
||||
entry := ClientDebugLogEntry{
|
||||
Source: input.Source,
|
||||
Level: input.Level,
|
||||
Category: input.Category,
|
||||
Message: input.Message,
|
||||
EventID: input.EventID,
|
||||
ReleaseID: input.ReleaseID,
|
||||
SessionID: input.SessionID,
|
||||
ManifestURL: input.ManifestURL,
|
||||
Route: input.Route,
|
||||
OccurredAt: occurredAt,
|
||||
ReceivedAt: time.Now().UTC(),
|
||||
Details: input.Details,
|
||||
}
|
||||
|
||||
s.mu.Lock()
|
||||
defer s.mu.Unlock()
|
||||
s.logSeq++
|
||||
entry.ID = s.logSeq
|
||||
s.logs = append(s.logs, entry)
|
||||
if len(s.logs) > 200 {
|
||||
s.logs = append([]ClientDebugLogEntry(nil), s.logs[len(s.logs)-200:]...)
|
||||
}
|
||||
copyEntry := entry
|
||||
return ©Entry, nil
|
||||
}
|
||||
|
||||
func (s *DevService) ListClientDebugLogs(_ context.Context, limit int) ([]ClientDebugLogEntry, error) {
|
||||
if !s.Enabled() {
|
||||
return nil, apperr.New(http.StatusNotFound, "not_found", "dev client logs are disabled")
|
||||
}
|
||||
if limit <= 0 || limit > 200 {
|
||||
limit = 50
|
||||
}
|
||||
|
||||
s.mu.Lock()
|
||||
defer s.mu.Unlock()
|
||||
items := append([]ClientDebugLogEntry(nil), s.logs...)
|
||||
sort.Slice(items, func(i, j int) bool {
|
||||
return items[i].ID > items[j].ID
|
||||
})
|
||||
if len(items) > limit {
|
||||
items = items[:limit]
|
||||
}
|
||||
return items, nil
|
||||
}
|
||||
|
||||
func (s *DevService) ClearClientDebugLogs(_ context.Context) error {
|
||||
if !s.Enabled() {
|
||||
return apperr.New(http.StatusNotFound, "not_found", "dev client logs are disabled")
|
||||
}
|
||||
s.mu.Lock()
|
||||
defer s.mu.Unlock()
|
||||
s.logs = nil
|
||||
return nil
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user