Files
lijiaoqiao/supply-api/internal/httpapi/alert_api.go
Your Name d5b5a8ece0 fix: 系统性修复安全问题、性能问题和错误处理
安全问题修复:
- X-Forwarded-For越界检查(auth.go)
- checkTokenStatus Context参数传递(auth.go)
- Type Assertion安全检查(auth.go)

性能问题修复:
- TokenCache过期清理机制
- BruteForceProtection过期清理
- InMemoryIdempotencyStore过期清理

错误处理修复:
- AuditStore.Emit返回error
- domain层emitAudit辅助方法
- List方法返回空slice而非nil
- 金额/价格负数验证

架构一致性:
- 统一使用model.RoleHierarchyLevels

新增功能:
- Alert API完整实现(CRUD+Resolve)
- pkg/error错误码集中管理
2026-04-07 07:41:25 +08:00

126 lines
3.0 KiB
Go

package httpapi
import (
"net/http"
"net/url"
"lijiaoqiao/supply-api/internal/audit/handler"
"lijiaoqiao/supply-api/internal/audit/service"
)
// AlertAPI 告警API处理器
type AlertAPI struct {
alertHandler *handler.AlertHandler
}
// NewAlertAPI 创建告警API处理器
func NewAlertAPI() *AlertAPI {
// 创建内存告警存储
alertStore := service.NewInMemoryAlertStore()
// 创建告警服务
alertSvc := service.NewAlertService(alertStore)
// 创建告警处理器
alertHandler := handler.NewAlertHandler(alertSvc)
return &AlertAPI{
alertHandler: alertHandler,
}
}
// Register 注册告警路由
func (a *AlertAPI) Register(mux *http.ServeMux) {
// Alert CRUD
mux.HandleFunc("/api/v1/audit/alerts", a.handleAlert)
mux.HandleFunc("/api/v1/audit/alerts/", a.handleAlertByID)
}
// handleAlert 处理 /api/v1/audit/alerts 的路由分发
func (a *AlertAPI) handleAlert(w http.ResponseWriter, r *http.Request) {
switch r.Method {
case http.MethodPost:
a.alertHandler.CreateAlert(w, r)
case http.MethodGet:
a.alertHandler.ListAlerts(w, r)
default:
writeError(w, http.StatusMethodNotAllowed, "METHOD_NOT_ALLOWED", "method not allowed")
}
}
// handleAlertByID 处理 /api/v1/audit/alerts/{alert_id} 的路由分发
func (a *AlertAPI) handleAlertByID(w http.ResponseWriter, r *http.Request) {
// 提取路径最后部分判断操作
path := r.URL.Path
if len(path) > 0 && path[len(path)-1] == '/' {
path = path[:len(path)-1]
}
parts := splitPath(path)
if len(parts) < 5 {
writeError(w, http.StatusBadRequest, "INVALID_PATH", "invalid path")
return
}
alertID := parts[4]
// 检查是否是特殊操作
if len(parts) > 5 && parts[5] == "resolve" {
if r.Method == http.MethodPost {
a.alertHandler.ResolveAlert(w, r)
} else {
writeError(w, http.StatusMethodNotAllowed, "METHOD_NOT_ALLOWED", "method not allowed")
}
return
}
// 常规CRUD操作
switch r.Method {
case http.MethodGet:
// 安全设置alert_id到查询参数
query := make(url.Values)
for k, v := range r.URL.Query() {
query[k] = v
}
query.Set("alert_id", alertID)
r.URL.RawQuery = query.Encode()
a.alertHandler.GetAlert(w, r)
case http.MethodPut:
query := make(url.Values)
for k, v := range r.URL.Query() {
query[k] = v
}
query.Set("alert_id", alertID)
r.URL.RawQuery = query.Encode()
a.alertHandler.UpdateAlert(w, r)
case http.MethodDelete:
query := make(url.Values)
for k, v := range r.URL.Query() {
query[k] = v
}
query.Set("alert_id", alertID)
r.URL.RawQuery = query.Encode()
a.alertHandler.DeleteAlert(w, r)
default:
writeError(w, http.StatusMethodNotAllowed, "METHOD_NOT_ALLOWED", "method not allowed")
}
}
// splitPath 分割路径
func splitPath(path string) []string {
var parts []string
var current []byte
for i := 0; i < len(path); i++ {
if path[i] == '/' {
if len(current) > 0 {
parts = append(parts, string(current))
current = nil
}
} else {
current = append(current, path[i])
}
}
if len(current) > 0 {
parts = append(parts, string(current))
}
return parts
}