| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151 |
- package service
- import (
- "context"
- "time"
- )
- type OpsRequestKind string
- const (
- OpsRequestKindSuccess OpsRequestKind = "success"
- OpsRequestKindError OpsRequestKind = "error"
- )
- // OpsRequestDetail is a request-level view across success (usage_logs) and error (ops_error_logs).
- // It powers "request drilldown" UIs without exposing full request bodies for successful requests.
- type OpsRequestDetail struct {
- Kind OpsRequestKind `json:"kind"`
- CreatedAt time.Time `json:"created_at"`
- RequestID string `json:"request_id"`
- Platform string `json:"platform,omitempty"`
- Model string `json:"model,omitempty"`
- DurationMs *int `json:"duration_ms,omitempty"`
- StatusCode *int `json:"status_code,omitempty"`
- // When Kind == "error", ErrorID links to /admin/ops/errors/:id.
- ErrorID *int64 `json:"error_id,omitempty"`
- Phase string `json:"phase,omitempty"`
- Severity string `json:"severity,omitempty"`
- Message string `json:"message,omitempty"`
- UserID *int64 `json:"user_id,omitempty"`
- APIKeyID *int64 `json:"api_key_id,omitempty"`
- AccountID *int64 `json:"account_id,omitempty"`
- GroupID *int64 `json:"group_id,omitempty"`
- Stream bool `json:"stream"`
- }
- type OpsRequestDetailFilter struct {
- StartTime *time.Time
- EndTime *time.Time
- // kind: success|error|all
- Kind string
- Platform string
- GroupID *int64
- UserID *int64
- APIKeyID *int64
- AccountID *int64
- Model string
- RequestID string
- Query string
- MinDurationMs *int
- MaxDurationMs *int
- // Sort: created_at_desc (default) or duration_desc.
- Sort string
- Page int
- PageSize int
- }
- func (f *OpsRequestDetailFilter) Normalize() (page, pageSize int, startTime, endTime time.Time) {
- page = 1
- pageSize = 50
- endTime = time.Now()
- startTime = endTime.Add(-1 * time.Hour)
- if f == nil {
- return page, pageSize, startTime, endTime
- }
- if f.Page > 0 {
- page = f.Page
- }
- if f.PageSize > 0 {
- pageSize = f.PageSize
- }
- if pageSize > 100 {
- pageSize = 100
- }
- if f.EndTime != nil {
- endTime = *f.EndTime
- }
- if f.StartTime != nil {
- startTime = *f.StartTime
- } else if f.EndTime != nil {
- startTime = endTime.Add(-1 * time.Hour)
- }
- if startTime.After(endTime) {
- startTime, endTime = endTime, startTime
- }
- return page, pageSize, startTime, endTime
- }
- type OpsRequestDetailList struct {
- Items []*OpsRequestDetail `json:"items"`
- Total int64 `json:"total"`
- Page int `json:"page"`
- PageSize int `json:"page_size"`
- }
- func (s *OpsService) ListRequestDetails(ctx context.Context, filter *OpsRequestDetailFilter) (*OpsRequestDetailList, error) {
- if err := s.RequireMonitoringEnabled(ctx); err != nil {
- return nil, err
- }
- if s.opsRepo == nil {
- return &OpsRequestDetailList{
- Items: []*OpsRequestDetail{},
- Total: 0,
- Page: 1,
- PageSize: 50,
- }, nil
- }
- page, pageSize, startTime, endTime := filter.Normalize()
- filterCopy := &OpsRequestDetailFilter{}
- if filter != nil {
- *filterCopy = *filter
- }
- filterCopy.Page = page
- filterCopy.PageSize = pageSize
- filterCopy.StartTime = &startTime
- filterCopy.EndTime = &endTime
- items, total, err := s.opsRepo.ListRequestDetails(ctx, filterCopy)
- if err != nil {
- return nil, err
- }
- if items == nil {
- items = []*OpsRequestDetail{}
- }
- return &OpsRequestDetailList{
- Items: items,
- Total: total,
- Page: page,
- PageSize: pageSize,
- }, nil
- }
|