- 新增 ops 主服务(ops_service.go)和端口定义(ops_port.go) - 实现账号可用性检查服务(ops_account_availability.go) - 实现数据聚合服务(ops_aggregation_service.go) - 实现告警评估服务(ops_alert_evaluator_service.go) - 实现告警管理服务(ops_alerts.go) - 实现数据清理服务(ops_cleanup_service.go) - 实现并发控制服务(ops_concurrency.go) - 实现仪表板服务(ops_dashboard.go) - 实现错误处理服务(ops_errors.go) - 实现直方图服务(ops_histograms.go) - 实现指标采集服务(ops_metrics_collector.go) - 实现查询模式服务(ops_query_mode.go) - 实现实时监控服务(ops_realtime.go) - 实现请求详情服务(ops_request_details.go) - 实现重试机制服务(ops_retry.go) - 实现配置管理服务(ops_settings.go) - 实现趋势分析服务(ops_trends.go) - 实现窗口统计服务(ops_window_stats.go) - 添加 ops 相关领域常量 - 注册 service 依赖注入
78 lines
2.5 KiB
Go
78 lines
2.5 KiB
Go
package service
|
|
|
|
import (
|
|
"context"
|
|
"database/sql"
|
|
"errors"
|
|
"log"
|
|
|
|
infraerrors "github.com/Wei-Shaw/sub2api/internal/pkg/errors"
|
|
)
|
|
|
|
func (s *OpsService) GetDashboardOverview(ctx context.Context, filter *OpsDashboardFilter) (*OpsDashboardOverview, error) {
|
|
if err := s.RequireMonitoringEnabled(ctx); err != nil {
|
|
return nil, err
|
|
}
|
|
if s.opsRepo == nil {
|
|
return nil, infraerrors.ServiceUnavailable("OPS_REPO_UNAVAILABLE", "Ops repository not available")
|
|
}
|
|
if filter == nil {
|
|
return nil, infraerrors.BadRequest("OPS_FILTER_REQUIRED", "filter is required")
|
|
}
|
|
if filter.StartTime.IsZero() || filter.EndTime.IsZero() {
|
|
return nil, infraerrors.BadRequest("OPS_TIME_RANGE_REQUIRED", "start_time/end_time are required")
|
|
}
|
|
if filter.StartTime.After(filter.EndTime) {
|
|
return nil, infraerrors.BadRequest("OPS_TIME_RANGE_INVALID", "start_time must be <= end_time")
|
|
}
|
|
|
|
// Resolve query mode (requested via query param, or DB default).
|
|
filter.QueryMode = s.resolveOpsQueryMode(ctx, filter.QueryMode)
|
|
|
|
overview, err := s.opsRepo.GetDashboardOverview(ctx, filter)
|
|
if err != nil {
|
|
if errors.Is(err, ErrOpsPreaggregatedNotPopulated) {
|
|
return nil, infraerrors.Conflict("OPS_PREAGG_NOT_READY", "Pre-aggregated ops metrics are not populated yet")
|
|
}
|
|
return nil, err
|
|
}
|
|
|
|
// Best-effort system health + jobs; dashboard metrics should still render if these are missing.
|
|
if metrics, err := s.opsRepo.GetLatestSystemMetrics(ctx, 1); err == nil {
|
|
overview.SystemMetrics = metrics
|
|
} else if err != nil && !errors.Is(err, sql.ErrNoRows) {
|
|
log.Printf("[Ops] GetLatestSystemMetrics failed: %v", err)
|
|
}
|
|
|
|
if heartbeats, err := s.opsRepo.ListJobHeartbeats(ctx); err == nil {
|
|
overview.JobHeartbeats = heartbeats
|
|
} else {
|
|
log.Printf("[Ops] ListJobHeartbeats failed: %v", err)
|
|
}
|
|
|
|
return overview, nil
|
|
}
|
|
|
|
func (s *OpsService) resolveOpsQueryMode(ctx context.Context, requested OpsQueryMode) OpsQueryMode {
|
|
if requested.IsValid() {
|
|
// Allow "auto" to be disabled via config until preagg is proven stable in production.
|
|
// Forced `preagg` via query param still works.
|
|
if requested == OpsQueryModeAuto && s != nil && s.cfg != nil && !s.cfg.Ops.UsePreaggregatedTables {
|
|
return OpsQueryModeRaw
|
|
}
|
|
return requested
|
|
}
|
|
|
|
mode := OpsQueryModeAuto
|
|
if s != nil && s.settingRepo != nil {
|
|
if raw, err := s.settingRepo.GetValue(ctx, SettingKeyOpsQueryModeDefault); err == nil {
|
|
mode = ParseOpsQueryMode(raw)
|
|
}
|
|
}
|
|
|
|
if mode == OpsQueryModeAuto && s != nil && s.cfg != nil && !s.cfg.Ops.UsePreaggregatedTables {
|
|
return OpsQueryModeRaw
|
|
}
|
|
return mode
|
|
}
|