fix(ops): 修复告警状态验证和错误处理逻辑
- 增强告警事件状态验证,添加合法状态值检查 - 移除重试逻辑中的遗留字段赋值 - 修正仓库不可用时的错误类型 - 格式化测试文件代码
This commit is contained in:
@@ -544,8 +544,14 @@ func (h *OpsHandler) ListAlertEvents(c *gin.Context) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Cursor pagination
|
// Cursor pagination: both params must be provided together.
|
||||||
if rawTS := strings.TrimSpace(c.Query("before_fired_at")); rawTS != "" {
|
rawTS := strings.TrimSpace(c.Query("before_fired_at"))
|
||||||
|
rawID := strings.TrimSpace(c.Query("before_id"))
|
||||||
|
if (rawTS == "") != (rawID == "") {
|
||||||
|
response.BadRequest(c, "before_fired_at and before_id must be provided together")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if rawTS != "" {
|
||||||
ts, err := time.Parse(time.RFC3339Nano, rawTS)
|
ts, err := time.Parse(time.RFC3339Nano, rawTS)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if t2, err2 := time.Parse(time.RFC3339, rawTS); err2 == nil {
|
if t2, err2 := time.Parse(time.RFC3339, rawTS); err2 == nil {
|
||||||
@@ -557,7 +563,7 @@ func (h *OpsHandler) ListAlertEvents(c *gin.Context) {
|
|||||||
}
|
}
|
||||||
filter.BeforeFiredAt = &ts
|
filter.BeforeFiredAt = &ts
|
||||||
}
|
}
|
||||||
if rawID := strings.TrimSpace(c.Query("before_id")); rawID != "" {
|
if rawID != "" {
|
||||||
id, err := strconv.ParseInt(rawID, 10, 64)
|
id, err := strconv.ParseInt(rawID, 10, 64)
|
||||||
if err != nil || id <= 0 {
|
if err != nil || id <= 0 {
|
||||||
response.BadRequest(c, "Invalid before_id")
|
response.BadRequest(c, "Invalid before_id")
|
||||||
|
|||||||
@@ -925,9 +925,13 @@ func buildOpsErrorLogsWhere(filter *service.OpsErrorLogFilter) (string, []any) {
|
|||||||
// ops_error_logs stores client-visible error requests (status>=400),
|
// ops_error_logs stores client-visible error requests (status>=400),
|
||||||
// but we also persist "recovered" upstream errors (status<400) for upstream health visibility.
|
// but we also persist "recovered" upstream errors (status<400) for upstream health visibility.
|
||||||
// By default, keep list endpoints scoped to unresolved records if the caller didn't specify.
|
// By default, keep list endpoints scoped to unresolved records if the caller didn't specify.
|
||||||
if filter != nil && filter.Resolved == nil {
|
resolvedFilter := (*bool)(nil)
|
||||||
|
if filter != nil {
|
||||||
|
resolvedFilter = filter.Resolved
|
||||||
|
}
|
||||||
|
if resolvedFilter == nil {
|
||||||
f := false
|
f := false
|
||||||
filter.Resolved = &f
|
resolvedFilter = &f
|
||||||
}
|
}
|
||||||
// Keep list endpoints scoped to client errors unless explicitly filtering upstream phase.
|
// Keep list endpoints scoped to client errors unless explicitly filtering upstream phase.
|
||||||
if phaseFilter != "upstream" {
|
if phaseFilter != "upstream" {
|
||||||
@@ -967,8 +971,8 @@ func buildOpsErrorLogsWhere(filter *service.OpsErrorLogFilter) (string, []any) {
|
|||||||
args = append(args, source)
|
args = append(args, source)
|
||||||
clauses = append(clauses, "LOWER(COALESCE(error_source,'')) = $"+itoa(len(args)))
|
clauses = append(clauses, "LOWER(COALESCE(error_source,'')) = $"+itoa(len(args)))
|
||||||
}
|
}
|
||||||
if filter.Resolved != nil {
|
if resolvedFilter != nil {
|
||||||
args = append(args, *filter.Resolved)
|
args = append(args, *resolvedFilter)
|
||||||
clauses = append(clauses, "COALESCE(resolved,false) = $"+itoa(len(args)))
|
clauses = append(clauses, "COALESCE(resolved,false) = $"+itoa(len(args)))
|
||||||
}
|
}
|
||||||
if len(filter.StatusCodes) > 0 {
|
if len(filter.StatusCodes) > 0 {
|
||||||
|
|||||||
@@ -206,7 +206,7 @@ func (s *OpsAlertEvaluatorService) evaluateOnce(interval time.Duration) {
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
scopePlatform, scopeGroupID := parseOpsAlertRuleScope(rule.Filters)
|
scopePlatform, scopeGroupID, scopeRegion := parseOpsAlertRuleScope(rule.Filters)
|
||||||
|
|
||||||
windowMinutes := rule.WindowMinutes
|
windowMinutes := rule.WindowMinutes
|
||||||
if windowMinutes <= 0 {
|
if windowMinutes <= 0 {
|
||||||
@@ -239,7 +239,7 @@ func (s *OpsAlertEvaluatorService) evaluateOnce(interval time.Duration) {
|
|||||||
// Scoped silencing: if a matching silence exists, skip creating a firing event.
|
// Scoped silencing: if a matching silence exists, skip creating a firing event.
|
||||||
if s.opsService != nil {
|
if s.opsService != nil {
|
||||||
platform := strings.TrimSpace(scopePlatform)
|
platform := strings.TrimSpace(scopePlatform)
|
||||||
region := (*string)(nil)
|
region := scopeRegion
|
||||||
if platform != "" {
|
if platform != "" {
|
||||||
if ok, err := s.opsService.IsAlertSilenced(ctx, rule.ID, platform, scopeGroupID, region, now); err == nil && ok {
|
if ok, err := s.opsService.IsAlertSilenced(ctx, rule.ID, platform, scopeGroupID, region, now); err == nil && ok {
|
||||||
continue
|
continue
|
||||||
@@ -370,9 +370,9 @@ func requiredSustainedBreaches(sustainedMinutes int, interval time.Duration) int
|
|||||||
return required
|
return required
|
||||||
}
|
}
|
||||||
|
|
||||||
func parseOpsAlertRuleScope(filters map[string]any) (platform string, groupID *int64) {
|
func parseOpsAlertRuleScope(filters map[string]any) (platform string, groupID *int64, region *string) {
|
||||||
if filters == nil {
|
if filters == nil {
|
||||||
return "", nil
|
return "", nil, nil
|
||||||
}
|
}
|
||||||
if v, ok := filters["platform"]; ok {
|
if v, ok := filters["platform"]; ok {
|
||||||
if s, ok := v.(string); ok {
|
if s, ok := v.(string); ok {
|
||||||
@@ -403,7 +403,15 @@ func parseOpsAlertRuleScope(filters map[string]any) (platform string, groupID *i
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return platform, groupID
|
if v, ok := filters["region"]; ok {
|
||||||
|
if s, ok := v.(string); ok {
|
||||||
|
vv := strings.TrimSpace(s)
|
||||||
|
if vv != "" {
|
||||||
|
region = &vv
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return platform, groupID, region
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *OpsAlertEvaluatorService) computeRuleMetric(
|
func (s *OpsAlertEvaluatorService) computeRuleMetric(
|
||||||
|
|||||||
@@ -208,7 +208,11 @@ func (s *OpsService) UpdateAlertEventStatus(ctx context.Context, eventID int64,
|
|||||||
if eventID <= 0 {
|
if eventID <= 0 {
|
||||||
return infraerrors.BadRequest("INVALID_EVENT_ID", "invalid event id")
|
return infraerrors.BadRequest("INVALID_EVENT_ID", "invalid event id")
|
||||||
}
|
}
|
||||||
if strings.TrimSpace(status) == "" {
|
status = strings.TrimSpace(status)
|
||||||
|
if status == "" {
|
||||||
|
return infraerrors.BadRequest("INVALID_STATUS", "invalid status")
|
||||||
|
}
|
||||||
|
if status != OpsAlertStatusResolved && status != OpsAlertStatusManualResolved {
|
||||||
return infraerrors.BadRequest("INVALID_STATUS", "invalid status")
|
return infraerrors.BadRequest("INVALID_STATUS", "invalid status")
|
||||||
}
|
}
|
||||||
return s.opsRepo.UpdateAlertEventStatus(ctx, eventID, status, resolvedAt)
|
return s.opsRepo.UpdateAlertEventStatus(ctx, eventID, status, resolvedAt)
|
||||||
|
|||||||
@@ -220,11 +220,8 @@ func (s *OpsService) RetryError(ctx context.Context, requestedByUserID int64, er
|
|||||||
msg := result.ErrorMessage
|
msg := result.ErrorMessage
|
||||||
updateErrMsg = &msg
|
updateErrMsg = &msg
|
||||||
}
|
}
|
||||||
|
// Keep legacy result_request_id empty; use upstream_request_id instead.
|
||||||
var resultRequestID *string
|
var resultRequestID *string
|
||||||
if strings.TrimSpace(result.UpstreamRequestID) != "" {
|
|
||||||
v := result.UpstreamRequestID
|
|
||||||
resultRequestID = &v
|
|
||||||
}
|
|
||||||
|
|
||||||
finalStatus := result.Status
|
finalStatus := result.Status
|
||||||
if strings.TrimSpace(finalStatus) == "" {
|
if strings.TrimSpace(finalStatus) == "" {
|
||||||
|
|||||||
@@ -261,7 +261,7 @@ func (s *OpsService) ListRetryAttemptsByErrorID(ctx context.Context, errorID int
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
if s.opsRepo == nil {
|
if s.opsRepo == nil {
|
||||||
return nil, infraerrors.NotFound("OPS_ERROR_NOT_FOUND", "ops error log not found")
|
return nil, infraerrors.ServiceUnavailable("OPS_REPO_UNAVAILABLE", "Ops repository not available")
|
||||||
}
|
}
|
||||||
if errorID <= 0 {
|
if errorID <= 0 {
|
||||||
return nil, infraerrors.BadRequest("OPS_ERROR_INVALID_ID", "invalid error id")
|
return nil, infraerrors.BadRequest("OPS_ERROR_INVALID_ID", "invalid error id")
|
||||||
|
|||||||
@@ -155,7 +155,8 @@ export default {
|
|||||||
noGroupsAvailable: 'No groups available',
|
noGroupsAvailable: 'No groups available',
|
||||||
unknownError: 'Unknown error occurred',
|
unknownError: 'Unknown error occurred',
|
||||||
saving: 'Saving...',
|
saving: 'Saving...',
|
||||||
selectedCount: '({count} selected)', refresh: 'Refresh',
|
selectedCount: '({count} selected)',
|
||||||
|
refresh: 'Refresh',
|
||||||
settings: 'Settings',
|
settings: 'Settings',
|
||||||
notAvailable: 'N/A',
|
notAvailable: 'N/A',
|
||||||
now: 'Now',
|
now: 'Now',
|
||||||
|
|||||||
Reference in New Issue
Block a user