Skip to content
GitLab
Menu
Projects
Groups
Snippets
Loading...
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Sign in / Register
Toggle navigation
Menu
Open sidebar
陈曦
sub2api
Commits
16ddc6a8
Commit
16ddc6a8
authored
Jan 14, 2026
by
IanShaw027
Browse files
feat(repository): 扩展ops数据访问层功能
- 新增告警静默相关数据库操作 - 增强错误日志查询和统计功能 - 优化重试结果和解决状态的存储
parent
340dc9ca
Changes
2
Hide whitespace changes
Inline
Side-by-side
backend/internal/repository/ops_repo.go
View file @
16ddc6a8
...
@@ -134,6 +134,7 @@ func (r *opsRepository) ListErrorLogs(ctx context.Context, filter *service.OpsEr
...
@@ -134,6 +134,7 @@ func (r *opsRepository) ListErrorLogs(ctx context.Context, filter *service.OpsEr
pageSize
=
500
pageSize
=
500
}
}
// buildOpsErrorLogsWhere may mutate filter (default resolved filter).
where
,
args
:=
buildOpsErrorLogsWhere
(
filter
)
where
,
args
:=
buildOpsErrorLogsWhere
(
filter
)
countSQL
:=
"SELECT COUNT(*) FROM ops_error_logs "
+
where
countSQL
:=
"SELECT COUNT(*) FROM ops_error_logs "
+
where
...
@@ -150,11 +151,19 @@ SELECT
...
@@ -150,11 +151,19 @@ SELECT
created_at,
created_at,
error_phase,
error_phase,
error_type,
error_type,
COALESCE(error_owner, ''),
COALESCE(error_source, ''),
severity,
severity,
COALESCE(upstream_status_code, status_code, 0),
COALESCE(upstream_status_code, status_code, 0),
COALESCE(platform, ''),
COALESCE(platform, ''),
COALESCE(model, ''),
COALESCE(model, ''),
duration_ms,
duration_ms,
COALESCE(is_retryable, false),
COALESCE(retry_count, 0),
COALESCE(resolved, false),
resolved_at,
resolved_by_user_id,
resolved_retry_id,
COALESCE(client_request_id, ''),
COALESCE(client_request_id, ''),
COALESCE(request_id, ''),
COALESCE(request_id, ''),
COALESCE(error_message, ''),
COALESCE(error_message, ''),
...
@@ -186,16 +195,27 @@ LIMIT $` + itoa(len(args)+1) + ` OFFSET $` + itoa(len(args)+2)
...
@@ -186,16 +195,27 @@ LIMIT $` + itoa(len(args)+1) + ` OFFSET $` + itoa(len(args)+2)
var
apiKeyID
sql
.
NullInt64
var
apiKeyID
sql
.
NullInt64
var
accountID
sql
.
NullInt64
var
accountID
sql
.
NullInt64
var
groupID
sql
.
NullInt64
var
groupID
sql
.
NullInt64
var
resolvedAt
sql
.
NullTime
var
resolvedBy
sql
.
NullInt64
var
resolvedRetryID
sql
.
NullInt64
if
err
:=
rows
.
Scan
(
if
err
:=
rows
.
Scan
(
&
item
.
ID
,
&
item
.
ID
,
&
item
.
CreatedAt
,
&
item
.
CreatedAt
,
&
item
.
Phase
,
&
item
.
Phase
,
&
item
.
Type
,
&
item
.
Type
,
&
item
.
Owner
,
&
item
.
Source
,
&
item
.
Severity
,
&
item
.
Severity
,
&
statusCode
,
&
statusCode
,
&
item
.
Platform
,
&
item
.
Platform
,
&
item
.
Model
,
&
item
.
Model
,
&
latency
,
&
latency
,
&
item
.
IsRetryable
,
&
item
.
RetryCount
,
&
item
.
Resolved
,
&
resolvedAt
,
&
resolvedBy
,
&
resolvedRetryID
,
&
item
.
ClientRequestID
,
&
item
.
ClientRequestID
,
&
item
.
RequestID
,
&
item
.
RequestID
,
&
item
.
Message
,
&
item
.
Message
,
...
@@ -209,6 +229,18 @@ LIMIT $` + itoa(len(args)+1) + ` OFFSET $` + itoa(len(args)+2)
...
@@ -209,6 +229,18 @@ LIMIT $` + itoa(len(args)+1) + ` OFFSET $` + itoa(len(args)+2)
);
err
!=
nil
{
);
err
!=
nil
{
return
nil
,
err
return
nil
,
err
}
}
if
resolvedAt
.
Valid
{
t
:=
resolvedAt
.
Time
item
.
ResolvedAt
=
&
t
}
if
resolvedBy
.
Valid
{
v
:=
resolvedBy
.
Int64
item
.
ResolvedByUserID
=
&
v
}
if
resolvedRetryID
.
Valid
{
v
:=
resolvedRetryID
.
Int64
item
.
ResolvedRetryID
=
&
v
}
if
latency
.
Valid
{
if
latency
.
Valid
{
v
:=
int
(
latency
.
Int64
)
v
:=
int
(
latency
.
Int64
)
item
.
LatencyMs
=
&
v
item
.
LatencyMs
=
&
v
...
@@ -262,11 +294,19 @@ SELECT
...
@@ -262,11 +294,19 @@ SELECT
created_at,
created_at,
error_phase,
error_phase,
error_type,
error_type,
COALESCE(error_owner, ''),
COALESCE(error_source, ''),
severity,
severity,
COALESCE(upstream_status_code, status_code, 0),
COALESCE(upstream_status_code, status_code, 0),
COALESCE(platform, ''),
COALESCE(platform, ''),
COALESCE(model, ''),
COALESCE(model, ''),
duration_ms,
duration_ms,
COALESCE(is_retryable, false),
COALESCE(retry_count, 0),
COALESCE(resolved, false),
resolved_at,
resolved_by_user_id,
resolved_retry_id,
COALESCE(client_request_id, ''),
COALESCE(client_request_id, ''),
COALESCE(request_id, ''),
COALESCE(request_id, ''),
COALESCE(error_message, ''),
COALESCE(error_message, ''),
...
@@ -301,6 +341,9 @@ LIMIT 1`
...
@@ -301,6 +341,9 @@ LIMIT 1`
var
latency
sql
.
NullInt64
var
latency
sql
.
NullInt64
var
statusCode
sql
.
NullInt64
var
statusCode
sql
.
NullInt64
var
upstreamStatusCode
sql
.
NullInt64
var
upstreamStatusCode
sql
.
NullInt64
var
resolvedAt
sql
.
NullTime
var
resolvedBy
sql
.
NullInt64
var
resolvedRetryID
sql
.
NullInt64
var
clientIP
sql
.
NullString
var
clientIP
sql
.
NullString
var
userID
sql
.
NullInt64
var
userID
sql
.
NullInt64
var
apiKeyID
sql
.
NullInt64
var
apiKeyID
sql
.
NullInt64
...
@@ -318,11 +361,19 @@ LIMIT 1`
...
@@ -318,11 +361,19 @@ LIMIT 1`
&
out
.
CreatedAt
,
&
out
.
CreatedAt
,
&
out
.
Phase
,
&
out
.
Phase
,
&
out
.
Type
,
&
out
.
Type
,
&
out
.
Owner
,
&
out
.
Source
,
&
out
.
Severity
,
&
out
.
Severity
,
&
statusCode
,
&
statusCode
,
&
out
.
Platform
,
&
out
.
Platform
,
&
out
.
Model
,
&
out
.
Model
,
&
latency
,
&
latency
,
&
out
.
IsRetryable
,
&
out
.
RetryCount
,
&
out
.
Resolved
,
&
resolvedAt
,
&
resolvedBy
,
&
resolvedRetryID
,
&
out
.
ClientRequestID
,
&
out
.
ClientRequestID
,
&
out
.
RequestID
,
&
out
.
RequestID
,
&
out
.
Message
,
&
out
.
Message
,
...
@@ -359,6 +410,18 @@ LIMIT 1`
...
@@ -359,6 +410,18 @@ LIMIT 1`
v
:=
int
(
latency
.
Int64
)
v
:=
int
(
latency
.
Int64
)
out
.
LatencyMs
=
&
v
out
.
LatencyMs
=
&
v
}
}
if
resolvedAt
.
Valid
{
t
:=
resolvedAt
.
Time
out
.
ResolvedAt
=
&
t
}
if
resolvedBy
.
Valid
{
v
:=
resolvedBy
.
Int64
out
.
ResolvedByUserID
=
&
v
}
if
resolvedRetryID
.
Valid
{
v
:=
resolvedRetryID
.
Int64
out
.
ResolvedRetryID
=
&
v
}
if
clientIP
.
Valid
{
if
clientIP
.
Valid
{
s
:=
clientIP
.
String
s
:=
clientIP
.
String
out
.
ClientIP
=
&
s
out
.
ClientIP
=
&
s
...
@@ -487,9 +550,15 @@ SET
...
@@ -487,9 +550,15 @@ SET
status = $2,
status = $2,
finished_at = $3,
finished_at = $3,
duration_ms = $4,
duration_ms = $4,
result_request_id = $5,
success = $5,
result_error_id = $6,
http_status_code = $6,
error_message = $7
upstream_request_id = $7,
used_account_id = $8,
response_preview = $9,
response_truncated = $10,
result_request_id = $11,
result_error_id = $12,
error_message = $13
WHERE id = $1`
WHERE id = $1`
_
,
err
:=
r
.
db
.
ExecContext
(
_
,
err
:=
r
.
db
.
ExecContext
(
...
@@ -499,8 +568,14 @@ WHERE id = $1`
...
@@ -499,8 +568,14 @@ WHERE id = $1`
strings
.
TrimSpace
(
input
.
Status
),
strings
.
TrimSpace
(
input
.
Status
),
nullTime
(
input
.
FinishedAt
),
nullTime
(
input
.
FinishedAt
),
input
.
DurationMs
,
input
.
DurationMs
,
nullBool
(
input
.
Success
),
nullInt
(
input
.
HTTPStatusCode
),
opsNullString
(
input
.
UpstreamRequestID
),
nullInt64
(
input
.
UsedAccountID
),
opsNullString
(
input
.
ResponsePreview
),
nullBool
(
input
.
ResponseTruncated
),
opsNullString
(
input
.
ResultRequestID
),
opsNullString
(
input
.
ResultRequestID
),
opsN
ullInt64
(
input
.
ResultErrorID
),
n
ullInt64
(
input
.
ResultErrorID
),
opsNullString
(
input
.
ErrorMessage
),
opsNullString
(
input
.
ErrorMessage
),
)
)
return
err
return
err
...
@@ -526,6 +601,12 @@ SELECT
...
@@ -526,6 +601,12 @@ SELECT
started_at,
started_at,
finished_at,
finished_at,
duration_ms,
duration_ms,
success,
http_status_code,
upstream_request_id,
used_account_id,
response_preview,
response_truncated,
result_request_id,
result_request_id,
result_error_id,
result_error_id,
error_message
error_message
...
@@ -540,6 +621,12 @@ LIMIT 1`
...
@@ -540,6 +621,12 @@ LIMIT 1`
var
startedAt
sql
.
NullTime
var
startedAt
sql
.
NullTime
var
finishedAt
sql
.
NullTime
var
finishedAt
sql
.
NullTime
var
durationMs
sql
.
NullInt64
var
durationMs
sql
.
NullInt64
var
success
sql
.
NullBool
var
httpStatusCode
sql
.
NullInt64
var
upstreamRequestID
sql
.
NullString
var
usedAccountID
sql
.
NullInt64
var
responsePreview
sql
.
NullString
var
responseTruncated
sql
.
NullBool
var
resultRequestID
sql
.
NullString
var
resultRequestID
sql
.
NullString
var
resultErrorID
sql
.
NullInt64
var
resultErrorID
sql
.
NullInt64
var
errorMessage
sql
.
NullString
var
errorMessage
sql
.
NullString
...
@@ -555,6 +642,12 @@ LIMIT 1`
...
@@ -555,6 +642,12 @@ LIMIT 1`
&
startedAt
,
&
startedAt
,
&
finishedAt
,
&
finishedAt
,
&
durationMs
,
&
durationMs
,
&
success
,
&
httpStatusCode
,
&
upstreamRequestID
,
&
usedAccountID
,
&
responsePreview
,
&
responseTruncated
,
&
resultRequestID
,
&
resultRequestID
,
&
resultErrorID
,
&
resultErrorID
,
&
errorMessage
,
&
errorMessage
,
...
@@ -579,6 +672,30 @@ LIMIT 1`
...
@@ -579,6 +672,30 @@ LIMIT 1`
v
:=
durationMs
.
Int64
v
:=
durationMs
.
Int64
out
.
DurationMs
=
&
v
out
.
DurationMs
=
&
v
}
}
if
success
.
Valid
{
v
:=
success
.
Bool
out
.
Success
=
&
v
}
if
httpStatusCode
.
Valid
{
v
:=
int
(
httpStatusCode
.
Int64
)
out
.
HTTPStatusCode
=
&
v
}
if
upstreamRequestID
.
Valid
{
s
:=
upstreamRequestID
.
String
out
.
UpstreamRequestID
=
&
s
}
if
usedAccountID
.
Valid
{
v
:=
usedAccountID
.
Int64
out
.
UsedAccountID
=
&
v
}
if
responsePreview
.
Valid
{
s
:=
responsePreview
.
String
out
.
ResponsePreview
=
&
s
}
if
responseTruncated
.
Valid
{
v
:=
responseTruncated
.
Bool
out
.
ResponseTruncated
=
&
v
}
if
resultRequestID
.
Valid
{
if
resultRequestID
.
Valid
{
s
:=
resultRequestID
.
String
s
:=
resultRequestID
.
String
out
.
ResultRequestID
=
&
s
out
.
ResultRequestID
=
&
s
...
@@ -602,18 +719,217 @@ func nullTime(t time.Time) sql.NullTime {
...
@@ -602,18 +719,217 @@ func nullTime(t time.Time) sql.NullTime {
return
sql
.
NullTime
{
Time
:
t
,
Valid
:
true
}
return
sql
.
NullTime
{
Time
:
t
,
Valid
:
true
}
}
}
func
nullBool
(
v
*
bool
)
sql
.
NullBool
{
if
v
==
nil
{
return
sql
.
NullBool
{}
}
return
sql
.
NullBool
{
Bool
:
*
v
,
Valid
:
true
}
}
func
(
r
*
opsRepository
)
ListRetryAttemptsByErrorID
(
ctx
context
.
Context
,
sourceErrorID
int64
,
limit
int
)
([]
*
service
.
OpsRetryAttempt
,
error
)
{
if
r
==
nil
||
r
.
db
==
nil
{
return
nil
,
fmt
.
Errorf
(
"nil ops repository"
)
}
if
sourceErrorID
<=
0
{
return
nil
,
fmt
.
Errorf
(
"invalid source_error_id"
)
}
if
limit
<=
0
{
limit
=
50
}
if
limit
>
200
{
limit
=
200
}
q
:=
`
SELECT
id,
created_at,
COALESCE(requested_by_user_id, 0),
source_error_id,
COALESCE(mode, ''),
pinned_account_id,
COALESCE(status, ''),
started_at,
finished_at,
duration_ms,
success,
http_status_code,
upstream_request_id,
used_account_id,
response_preview,
response_truncated,
result_request_id,
result_error_id,
error_message
FROM ops_retry_attempts
WHERE source_error_id = $1
ORDER BY created_at DESC
LIMIT $2`
rows
,
err
:=
r
.
db
.
QueryContext
(
ctx
,
q
,
sourceErrorID
,
limit
)
if
err
!=
nil
{
return
nil
,
err
}
defer
func
()
{
_
=
rows
.
Close
()
}()
out
:=
make
([]
*
service
.
OpsRetryAttempt
,
0
,
16
)
for
rows
.
Next
()
{
var
item
service
.
OpsRetryAttempt
var
pinnedAccountID
sql
.
NullInt64
var
requestedBy
sql
.
NullInt64
var
startedAt
sql
.
NullTime
var
finishedAt
sql
.
NullTime
var
durationMs
sql
.
NullInt64
var
success
sql
.
NullBool
var
httpStatusCode
sql
.
NullInt64
var
upstreamRequestID
sql
.
NullString
var
usedAccountID
sql
.
NullInt64
var
responsePreview
sql
.
NullString
var
responseTruncated
sql
.
NullBool
var
resultRequestID
sql
.
NullString
var
resultErrorID
sql
.
NullInt64
var
errorMessage
sql
.
NullString
if
err
:=
rows
.
Scan
(
&
item
.
ID
,
&
item
.
CreatedAt
,
&
requestedBy
,
&
item
.
SourceErrorID
,
&
item
.
Mode
,
&
pinnedAccountID
,
&
item
.
Status
,
&
startedAt
,
&
finishedAt
,
&
durationMs
,
&
success
,
&
httpStatusCode
,
&
upstreamRequestID
,
&
usedAccountID
,
&
responsePreview
,
&
responseTruncated
,
&
resultRequestID
,
&
resultErrorID
,
&
errorMessage
,
);
err
!=
nil
{
return
nil
,
err
}
item
.
RequestedByUserID
=
requestedBy
.
Int64
if
pinnedAccountID
.
Valid
{
v
:=
pinnedAccountID
.
Int64
item
.
PinnedAccountID
=
&
v
}
if
startedAt
.
Valid
{
t
:=
startedAt
.
Time
item
.
StartedAt
=
&
t
}
if
finishedAt
.
Valid
{
t
:=
finishedAt
.
Time
item
.
FinishedAt
=
&
t
}
if
durationMs
.
Valid
{
v
:=
durationMs
.
Int64
item
.
DurationMs
=
&
v
}
if
success
.
Valid
{
v
:=
success
.
Bool
item
.
Success
=
&
v
}
if
httpStatusCode
.
Valid
{
v
:=
int
(
httpStatusCode
.
Int64
)
item
.
HTTPStatusCode
=
&
v
}
if
upstreamRequestID
.
Valid
{
s
:=
upstreamRequestID
.
String
item
.
UpstreamRequestID
=
&
s
}
if
usedAccountID
.
Valid
{
v
:=
usedAccountID
.
Int64
item
.
UsedAccountID
=
&
v
}
if
responsePreview
.
Valid
{
s
:=
responsePreview
.
String
item
.
ResponsePreview
=
&
s
}
if
responseTruncated
.
Valid
{
v
:=
responseTruncated
.
Bool
item
.
ResponseTruncated
=
&
v
}
if
resultRequestID
.
Valid
{
s
:=
resultRequestID
.
String
item
.
ResultRequestID
=
&
s
}
if
resultErrorID
.
Valid
{
v
:=
resultErrorID
.
Int64
item
.
ResultErrorID
=
&
v
}
if
errorMessage
.
Valid
{
s
:=
errorMessage
.
String
item
.
ErrorMessage
=
&
s
}
out
=
append
(
out
,
&
item
)
}
if
err
:=
rows
.
Err
();
err
!=
nil
{
return
nil
,
err
}
return
out
,
nil
}
func
(
r
*
opsRepository
)
UpdateErrorResolution
(
ctx
context
.
Context
,
errorID
int64
,
resolved
bool
,
resolvedByUserID
*
int64
,
resolvedRetryID
*
int64
,
resolvedAt
*
time
.
Time
)
error
{
if
r
==
nil
||
r
.
db
==
nil
{
return
fmt
.
Errorf
(
"nil ops repository"
)
}
if
errorID
<=
0
{
return
fmt
.
Errorf
(
"invalid error id"
)
}
q
:=
`
UPDATE ops_error_logs
SET
resolved = $2,
resolved_at = $3,
resolved_by_user_id = $4,
resolved_retry_id = $5
WHERE id = $1`
at
:=
sql
.
NullTime
{}
if
resolvedAt
!=
nil
&&
!
resolvedAt
.
IsZero
()
{
at
=
sql
.
NullTime
{
Time
:
resolvedAt
.
UTC
(),
Valid
:
true
}
}
else
if
resolved
{
now
:=
time
.
Now
()
.
UTC
()
at
=
sql
.
NullTime
{
Time
:
now
,
Valid
:
true
}
}
_
,
err
:=
r
.
db
.
ExecContext
(
ctx
,
q
,
errorID
,
resolved
,
at
,
nullInt64
(
resolvedByUserID
),
nullInt64
(
resolvedRetryID
),
)
return
err
}
func
buildOpsErrorLogsWhere
(
filter
*
service
.
OpsErrorLogFilter
)
(
string
,
[]
any
)
{
func
buildOpsErrorLogsWhere
(
filter
*
service
.
OpsErrorLogFilter
)
(
string
,
[]
any
)
{
clauses
:=
make
([]
string
,
0
,
8
)
clauses
:=
make
([]
string
,
0
,
12
)
args
:=
make
([]
any
,
0
,
8
)
args
:=
make
([]
any
,
0
,
12
)
clauses
=
append
(
clauses
,
"1=1"
)
clauses
=
append
(
clauses
,
"1=1"
)
phaseFilter
:=
""
phaseFilter
:=
""
if
filter
!=
nil
{
if
filter
!=
nil
{
phaseFilter
=
strings
.
TrimSpace
(
strings
.
ToLower
(
filter
.
Phase
))
phaseFilter
=
strings
.
TrimSpace
(
strings
.
ToLower
(
filter
.
Phase
))
}
}
// ops_error_logs
primarily
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 client errors unless explicitly filtering upstream phase.
// By default, keep list endpoints scoped to unresolved records if the caller didn't specify.
if
filter
!=
nil
&&
filter
.
Resolved
==
nil
{
f
:=
false
filter
.
Resolved
=
&
f
}
// Keep list endpoints scoped to client errors unless explicitly filtering upstream phase.
if
phaseFilter
!=
"upstream"
{
if
phaseFilter
!=
"upstream"
{
clauses
=
append
(
clauses
,
"COALESCE(status_code, 0) >= 400"
)
clauses
=
append
(
clauses
,
"COALESCE(status_code, 0) >= 400"
)
}
}
...
@@ -643,6 +959,18 @@ func buildOpsErrorLogsWhere(filter *service.OpsErrorLogFilter) (string, []any) {
...
@@ -643,6 +959,18 @@ func buildOpsErrorLogsWhere(filter *service.OpsErrorLogFilter) (string, []any) {
args
=
append
(
args
,
phase
)
args
=
append
(
args
,
phase
)
clauses
=
append
(
clauses
,
"error_phase = $"
+
itoa
(
len
(
args
)))
clauses
=
append
(
clauses
,
"error_phase = $"
+
itoa
(
len
(
args
)))
}
}
if
owner
:=
strings
.
TrimSpace
(
strings
.
ToLower
(
filter
.
Owner
));
owner
!=
""
{
args
=
append
(
args
,
owner
)
clauses
=
append
(
clauses
,
"LOWER(COALESCE(error_owner,'')) = $"
+
itoa
(
len
(
args
)))
}
if
source
:=
strings
.
TrimSpace
(
strings
.
ToLower
(
filter
.
Source
));
source
!=
""
{
args
=
append
(
args
,
source
)
clauses
=
append
(
clauses
,
"LOWER(COALESCE(error_source,'')) = $"
+
itoa
(
len
(
args
)))
}
if
filter
.
Resolved
!=
nil
{
args
=
append
(
args
,
*
filter
.
Resolved
)
clauses
=
append
(
clauses
,
"COALESCE(resolved,false) = $"
+
itoa
(
len
(
args
)))
}
if
len
(
filter
.
StatusCodes
)
>
0
{
if
len
(
filter
.
StatusCodes
)
>
0
{
args
=
append
(
args
,
pq
.
Array
(
filter
.
StatusCodes
))
args
=
append
(
args
,
pq
.
Array
(
filter
.
StatusCodes
))
clauses
=
append
(
clauses
,
"COALESCE(upstream_status_code, status_code, 0) = ANY($"
+
itoa
(
len
(
args
))
+
")"
)
clauses
=
append
(
clauses
,
"COALESCE(upstream_status_code, status_code, 0) = ANY($"
+
itoa
(
len
(
args
))
+
")"
)
...
...
backend/internal/repository/ops_repo_alerts.go
View file @
16ddc6a8
...
@@ -354,7 +354,7 @@ SELECT
...
@@ -354,7 +354,7 @@ SELECT
created_at
created_at
FROM ops_alert_events
FROM ops_alert_events
`
+
where
+
`
`
+
where
+
`
ORDER BY fired_at DESC
ORDER BY fired_at DESC
, id DESC
LIMIT `
+
limitArg
LIMIT `
+
limitArg
rows
,
err
:=
r
.
db
.
QueryContext
(
ctx
,
q
,
args
...
)
rows
,
err
:=
r
.
db
.
QueryContext
(
ctx
,
q
,
args
...
)
...
@@ -413,6 +413,43 @@ LIMIT ` + limitArg
...
@@ -413,6 +413,43 @@ LIMIT ` + limitArg
return
out
,
nil
return
out
,
nil
}
}
func
(
r
*
opsRepository
)
GetAlertEventByID
(
ctx
context
.
Context
,
eventID
int64
)
(
*
service
.
OpsAlertEvent
,
error
)
{
if
r
==
nil
||
r
.
db
==
nil
{
return
nil
,
fmt
.
Errorf
(
"nil ops repository"
)
}
if
eventID
<=
0
{
return
nil
,
fmt
.
Errorf
(
"invalid event id"
)
}
q
:=
`
SELECT
id,
COALESCE(rule_id, 0),
COALESCE(severity, ''),
COALESCE(status, ''),
COALESCE(title, ''),
COALESCE(description, ''),
metric_value,
threshold_value,
dimensions,
fired_at,
resolved_at,
email_sent,
created_at
FROM ops_alert_events
WHERE id = $1`
row
:=
r
.
db
.
QueryRowContext
(
ctx
,
q
,
eventID
)
ev
,
err
:=
scanOpsAlertEvent
(
row
)
if
err
!=
nil
{
if
err
==
sql
.
ErrNoRows
{
return
nil
,
nil
}
return
nil
,
err
}
return
ev
,
nil
}
func
(
r
*
opsRepository
)
GetActiveAlertEvent
(
ctx
context
.
Context
,
ruleID
int64
)
(
*
service
.
OpsAlertEvent
,
error
)
{
func
(
r
*
opsRepository
)
GetActiveAlertEvent
(
ctx
context
.
Context
,
ruleID
int64
)
(
*
service
.
OpsAlertEvent
,
error
)
{
if
r
==
nil
||
r
.
db
==
nil
{
if
r
==
nil
||
r
.
db
==
nil
{
return
nil
,
fmt
.
Errorf
(
"nil ops repository"
)
return
nil
,
fmt
.
Errorf
(
"nil ops repository"
)
...
@@ -591,6 +628,121 @@ type opsAlertEventRow interface {
...
@@ -591,6 +628,121 @@ type opsAlertEventRow interface {
Scan
(
dest
...
any
)
error
Scan
(
dest
...
any
)
error
}
}
func
(
r
*
opsRepository
)
CreateAlertSilence
(
ctx
context
.
Context
,
input
*
service
.
OpsAlertSilence
)
(
*
service
.
OpsAlertSilence
,
error
)
{
if
r
==
nil
||
r
.
db
==
nil
{
return
nil
,
fmt
.
Errorf
(
"nil ops repository"
)
}
if
input
==
nil
{
return
nil
,
fmt
.
Errorf
(
"nil input"
)
}
if
input
.
RuleID
<=
0
{
return
nil
,
fmt
.
Errorf
(
"invalid rule_id"
)
}
platform
:=
strings
.
TrimSpace
(
input
.
Platform
)
if
platform
==
""
{
return
nil
,
fmt
.
Errorf
(
"invalid platform"
)
}
if
input
.
Until
.
IsZero
()
{
return
nil
,
fmt
.
Errorf
(
"invalid until"
)
}
q
:=
`
INSERT INTO ops_alert_silences (
rule_id,
platform,
group_id,
region,
until,
reason,
created_by,
created_at
) VALUES (
$1,$2,$3,$4,$5,$6,$7,NOW()
)
RETURNING id, rule_id, platform, group_id, region, until, COALESCE(reason,''), created_by, created_at`
row
:=
r
.
db
.
QueryRowContext
(
ctx
,
q
,
input
.
RuleID
,
platform
,
opsNullInt64
(
input
.
GroupID
),
opsNullString
(
input
.
Region
),
input
.
Until
,
opsNullString
(
input
.
Reason
),
opsNullInt64
(
input
.
CreatedBy
),
)
var
out
service
.
OpsAlertSilence
var
groupID
sql
.
NullInt64
var
region
sql
.
NullString
var
createdBy
sql
.
NullInt64
if
err
:=
row
.
Scan
(
&
out
.
ID
,
&
out
.
RuleID
,
&
out
.
Platform
,
&
groupID
,
&
region
,
&
out
.
Until
,
&
out
.
Reason
,
&
createdBy
,
&
out
.
CreatedAt
,
);
err
!=
nil
{
return
nil
,
err
}
if
groupID
.
Valid
{
v
:=
groupID
.
Int64
out
.
GroupID
=
&
v
}
if
region
.
Valid
{
v
:=
strings
.
TrimSpace
(
region
.
String
)
if
v
!=
""
{
out
.
Region
=
&
v
}
}
if
createdBy
.
Valid
{
v
:=
createdBy
.
Int64
out
.
CreatedBy
=
&
v
}
return
&
out
,
nil
}
func
(
r
*
opsRepository
)
IsAlertSilenced
(
ctx
context
.
Context
,
ruleID
int64
,
platform
string
,
groupID
*
int64
,
region
*
string
,
now
time
.
Time
)
(
bool
,
error
)
{
if
r
==
nil
||
r
.
db
==
nil
{
return
false
,
fmt
.
Errorf
(
"nil ops repository"
)
}
if
ruleID
<=
0
{
return
false
,
fmt
.
Errorf
(
"invalid rule id"
)
}
platform
=
strings
.
TrimSpace
(
platform
)
if
platform
==
""
{
return
false
,
nil
}
if
now
.
IsZero
()
{
now
=
time
.
Now
()
.
UTC
()
}
q
:=
`
SELECT 1
FROM ops_alert_silences
WHERE rule_id = $1
AND platform = $2
AND (group_id IS NOT DISTINCT FROM $3)
AND (region IS NOT DISTINCT FROM $4)
AND until > $5
LIMIT 1`
var
dummy
int
err
:=
r
.
db
.
QueryRowContext
(
ctx
,
q
,
ruleID
,
platform
,
opsNullInt64
(
groupID
),
opsNullString
(
region
),
now
)
.
Scan
(
&
dummy
)
if
err
!=
nil
{
if
err
==
sql
.
ErrNoRows
{
return
false
,
nil
}
return
false
,
err
}
return
true
,
nil
}
func
scanOpsAlertEvent
(
row
opsAlertEventRow
)
(
*
service
.
OpsAlertEvent
,
error
)
{
func
scanOpsAlertEvent
(
row
opsAlertEventRow
)
(
*
service
.
OpsAlertEvent
,
error
)
{
var
ev
service
.
OpsAlertEvent
var
ev
service
.
OpsAlertEvent
var
metricValue
sql
.
NullFloat64
var
metricValue
sql
.
NullFloat64
...
@@ -652,6 +804,10 @@ func buildOpsAlertEventsWhere(filter *service.OpsAlertEventFilter) (string, []an
...
@@ -652,6 +804,10 @@ func buildOpsAlertEventsWhere(filter *service.OpsAlertEventFilter) (string, []an
args
=
append
(
args
,
severity
)
args
=
append
(
args
,
severity
)
clauses
=
append
(
clauses
,
"severity = $"
+
itoa
(
len
(
args
)))
clauses
=
append
(
clauses
,
"severity = $"
+
itoa
(
len
(
args
)))
}
}
if
filter
.
EmailSent
!=
nil
{
args
=
append
(
args
,
*
filter
.
EmailSent
)
clauses
=
append
(
clauses
,
"email_sent = $"
+
itoa
(
len
(
args
)))
}
if
filter
.
StartTime
!=
nil
&&
!
filter
.
StartTime
.
IsZero
()
{
if
filter
.
StartTime
!=
nil
&&
!
filter
.
StartTime
.
IsZero
()
{
args
=
append
(
args
,
*
filter
.
StartTime
)
args
=
append
(
args
,
*
filter
.
StartTime
)
clauses
=
append
(
clauses
,
"fired_at >= $"
+
itoa
(
len
(
args
)))
clauses
=
append
(
clauses
,
"fired_at >= $"
+
itoa
(
len
(
args
)))
...
@@ -661,6 +817,14 @@ func buildOpsAlertEventsWhere(filter *service.OpsAlertEventFilter) (string, []an
...
@@ -661,6 +817,14 @@ func buildOpsAlertEventsWhere(filter *service.OpsAlertEventFilter) (string, []an
clauses
=
append
(
clauses
,
"fired_at < $"
+
itoa
(
len
(
args
)))
clauses
=
append
(
clauses
,
"fired_at < $"
+
itoa
(
len
(
args
)))
}
}
// Cursor pagination (descending by fired_at, then id)
if
filter
.
BeforeFiredAt
!=
nil
&&
!
filter
.
BeforeFiredAt
.
IsZero
()
&&
filter
.
BeforeID
!=
nil
&&
*
filter
.
BeforeID
>
0
{
args
=
append
(
args
,
*
filter
.
BeforeFiredAt
)
tsArg
:=
"$"
+
itoa
(
len
(
args
))
args
=
append
(
args
,
*
filter
.
BeforeID
)
idArg
:=
"$"
+
itoa
(
len
(
args
))
clauses
=
append
(
clauses
,
fmt
.
Sprintf
(
"(fired_at < %s OR (fired_at = %s AND id < %s))"
,
tsArg
,
tsArg
,
idArg
))
}
// Dimensions are stored in JSONB. We filter best-effort without requiring GIN indexes.
// Dimensions are stored in JSONB. We filter best-effort without requiring GIN indexes.
if
platform
:=
strings
.
TrimSpace
(
filter
.
Platform
);
platform
!=
""
{
if
platform
:=
strings
.
TrimSpace
(
filter
.
Platform
);
platform
!=
""
{
args
=
append
(
args
,
platform
)
args
=
append
(
args
,
platform
)
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
.
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment