安全问题修复: - 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错误码集中管理
126 lines
3.0 KiB
Go
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
|
|
}
|