ops_request_details.go 3.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151
  1. package service
  2. import (
  3. "context"
  4. "time"
  5. )
  6. type OpsRequestKind string
  7. const (
  8. OpsRequestKindSuccess OpsRequestKind = "success"
  9. OpsRequestKindError OpsRequestKind = "error"
  10. )
  11. // OpsRequestDetail is a request-level view across success (usage_logs) and error (ops_error_logs).
  12. // It powers "request drilldown" UIs without exposing full request bodies for successful requests.
  13. type OpsRequestDetail struct {
  14. Kind OpsRequestKind `json:"kind"`
  15. CreatedAt time.Time `json:"created_at"`
  16. RequestID string `json:"request_id"`
  17. Platform string `json:"platform,omitempty"`
  18. Model string `json:"model,omitempty"`
  19. DurationMs *int `json:"duration_ms,omitempty"`
  20. StatusCode *int `json:"status_code,omitempty"`
  21. // When Kind == "error", ErrorID links to /admin/ops/errors/:id.
  22. ErrorID *int64 `json:"error_id,omitempty"`
  23. Phase string `json:"phase,omitempty"`
  24. Severity string `json:"severity,omitempty"`
  25. Message string `json:"message,omitempty"`
  26. UserID *int64 `json:"user_id,omitempty"`
  27. APIKeyID *int64 `json:"api_key_id,omitempty"`
  28. AccountID *int64 `json:"account_id,omitempty"`
  29. GroupID *int64 `json:"group_id,omitempty"`
  30. Stream bool `json:"stream"`
  31. }
  32. type OpsRequestDetailFilter struct {
  33. StartTime *time.Time
  34. EndTime *time.Time
  35. // kind: success|error|all
  36. Kind string
  37. Platform string
  38. GroupID *int64
  39. UserID *int64
  40. APIKeyID *int64
  41. AccountID *int64
  42. Model string
  43. RequestID string
  44. Query string
  45. MinDurationMs *int
  46. MaxDurationMs *int
  47. // Sort: created_at_desc (default) or duration_desc.
  48. Sort string
  49. Page int
  50. PageSize int
  51. }
  52. func (f *OpsRequestDetailFilter) Normalize() (page, pageSize int, startTime, endTime time.Time) {
  53. page = 1
  54. pageSize = 50
  55. endTime = time.Now()
  56. startTime = endTime.Add(-1 * time.Hour)
  57. if f == nil {
  58. return page, pageSize, startTime, endTime
  59. }
  60. if f.Page > 0 {
  61. page = f.Page
  62. }
  63. if f.PageSize > 0 {
  64. pageSize = f.PageSize
  65. }
  66. if pageSize > 100 {
  67. pageSize = 100
  68. }
  69. if f.EndTime != nil {
  70. endTime = *f.EndTime
  71. }
  72. if f.StartTime != nil {
  73. startTime = *f.StartTime
  74. } else if f.EndTime != nil {
  75. startTime = endTime.Add(-1 * time.Hour)
  76. }
  77. if startTime.After(endTime) {
  78. startTime, endTime = endTime, startTime
  79. }
  80. return page, pageSize, startTime, endTime
  81. }
  82. type OpsRequestDetailList struct {
  83. Items []*OpsRequestDetail `json:"items"`
  84. Total int64 `json:"total"`
  85. Page int `json:"page"`
  86. PageSize int `json:"page_size"`
  87. }
  88. func (s *OpsService) ListRequestDetails(ctx context.Context, filter *OpsRequestDetailFilter) (*OpsRequestDetailList, error) {
  89. if err := s.RequireMonitoringEnabled(ctx); err != nil {
  90. return nil, err
  91. }
  92. if s.opsRepo == nil {
  93. return &OpsRequestDetailList{
  94. Items: []*OpsRequestDetail{},
  95. Total: 0,
  96. Page: 1,
  97. PageSize: 50,
  98. }, nil
  99. }
  100. page, pageSize, startTime, endTime := filter.Normalize()
  101. filterCopy := &OpsRequestDetailFilter{}
  102. if filter != nil {
  103. *filterCopy = *filter
  104. }
  105. filterCopy.Page = page
  106. filterCopy.PageSize = pageSize
  107. filterCopy.StartTime = &startTime
  108. filterCopy.EndTime = &endTime
  109. items, total, err := s.opsRepo.ListRequestDetails(ctx, filterCopy)
  110. if err != nil {
  111. return nil, err
  112. }
  113. if items == nil {
  114. items = []*OpsRequestDetail{}
  115. }
  116. return &OpsRequestDetailList{
  117. Items: items,
  118. Total: total,
  119. Page: page,
  120. PageSize: pageSize,
  121. }, nil
  122. }