init: EZOps AIOps 智能运维平台
- 前端: React 18 + TypeScript + Vite + Tailwind CSS + shadcn/ui - 后端: Go + Gin + GORM + JWT (golang-jwt/jwt/v5) - 功能: 服务器管理、K8s集群、监控告警、CI/CD、自动化运维、审批流程、审计日志 - 文档: 设计文档(ROADMAP/PDD/架构/模块)
This commit is contained in:
commit
7d6674bddd
156 files changed
+45704
No files matched your search
@@ -0,0 +1,362 @@
|
||||
package handlers
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"ezops/internal/models"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
// AuditHandler 审计日志处理器
|
||||
type AuditHandler struct {
|
||||
db *gorm.DB
|
||||
}
|
||||
|
||||
// NewAuditHandler 创建审计日志处理器
|
||||
func NewAuditHandler(db *gorm.DB) *AuditHandler {
|
||||
return &AuditHandler{db: db}
|
||||
}
|
||||
|
||||
// ListAuditLogs 获取审计日志列表
|
||||
// @Summary 获取审计日志列表
|
||||
// @Description 获取审计日志列表,支持分页和筛选
|
||||
// @Tags 审计日志
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Param page query int false "页码" default(1)
|
||||
// @Param pageSize query int false "每页数量" default(20)
|
||||
// @Param module query string false "模块"
|
||||
// @Param action query string false "操作类型"
|
||||
// @Param username query string false "用户名"
|
||||
// @Param startTime query string false "开始时间"
|
||||
// @Param endTime query string false "结束时间"
|
||||
// @Success 200 {object} models.AuditLogListResponse
|
||||
// @Router /api/v1/audit/logs [get]
|
||||
func (h *AuditHandler) ListAuditLogs(c *gin.Context) {
|
||||
var req models.AuditLogQuery
|
||||
if err := c.ShouldBindQuery(&req); err != nil {
|
||||
c.JSON(http.StatusBadRequest, models.Response{Code: 400, Message: err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
// 设置默认值
|
||||
if req.Page <= 0 {
|
||||
req.Page = 1
|
||||
}
|
||||
if req.PageSize <= 0 {
|
||||
req.PageSize = 20
|
||||
}
|
||||
|
||||
// 构建查询
|
||||
query := h.db.Model(&models.AuditLog{})
|
||||
|
||||
if req.Module != "" {
|
||||
query = query.Where("module = ?", req.Module)
|
||||
}
|
||||
if req.Action != "" {
|
||||
query = query.Where("action = ?", req.Action)
|
||||
}
|
||||
if req.Username != "" {
|
||||
query = query.Where("username LIKE ?", "%"+req.Username+"%")
|
||||
}
|
||||
if req.StartTime != "" {
|
||||
query = query.Where("created_at >= ?", req.StartTime)
|
||||
}
|
||||
if req.EndTime != "" {
|
||||
query = query.Where("created_at <= ?", req.EndTime)
|
||||
}
|
||||
|
||||
// 获取总数
|
||||
var total int64
|
||||
if err := query.Count(&total).Error; err != nil {
|
||||
c.JSON(http.StatusInternalServerError, models.Response{Code: 500, Message: "查询失败"})
|
||||
return
|
||||
}
|
||||
|
||||
// 获取列表
|
||||
var logs []models.AuditLog
|
||||
if err := query.Order("created_at DESC").
|
||||
Offset((req.Page - 1) * req.PageSize).
|
||||
Limit(req.PageSize).
|
||||
Find(&logs).Error; err != nil {
|
||||
c.JSON(http.StatusInternalServerError, models.Response{Code: 500, Message: "查询失败"})
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, models.AuditLogListResponse{
|
||||
List: logs,
|
||||
Total: total,
|
||||
})
|
||||
}
|
||||
|
||||
// GetAuditLogDetail 获取审计日志详情
|
||||
// @Summary 获取审计日志详情
|
||||
// @Description 获取指定ID的审计日志详情
|
||||
// @Tags 审计日志
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Param id path int true "日志ID"
|
||||
// @Success 200 {object} models.AuditLog
|
||||
// @Router /api/v1/audit/logs/{id} [get]
|
||||
func (h *AuditHandler) GetAuditLogDetail(c *gin.Context) {
|
||||
id, err := strconv.ParseUint(c.Param("id"), 10, 64)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusBadRequest, models.Response{Code: 400, Message: "无效的日志ID"})
|
||||
return
|
||||
}
|
||||
|
||||
var log models.AuditLog
|
||||
if err := h.db.First(&log, id).Error; err != nil {
|
||||
if err == gorm.ErrRecordNotFound {
|
||||
c.JSON(http.StatusNotFound, models.Response{Code: 404, Message: "日志不存在"})
|
||||
return
|
||||
}
|
||||
c.JSON(http.StatusInternalServerError, models.Response{Code: 500, Message: "查询失败"})
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, log)
|
||||
}
|
||||
|
||||
// GetAuditModules 获取审计模块列表
|
||||
// @Summary 获取审计模块列表
|
||||
// @Description 获取所有审计模块类型
|
||||
// @Tags 审计日志
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Success 200 {object} map[string]interface{}
|
||||
// @Router /api/v1/audit/modules [get]
|
||||
func (h *AuditHandler) GetAuditModules(c *gin.Context) {
|
||||
modules := []map[string]string{
|
||||
{"value": "user", "label": "用户管理"},
|
||||
{"value": "server", "label": "服务器管理"},
|
||||
{"value": "k8s", "label": "Kubernetes"},
|
||||
{"value": "alert", "label": "监控告警"},
|
||||
{"value": "pipeline", "label": "应用发布"},
|
||||
{"value": "automation", "label": "自动化运维"},
|
||||
{"value": "approval", "label": "审批流程"},
|
||||
{"value": "navigation", "label": "站内导航"},
|
||||
{"value": "cloud_account", "label": "云账号管理"},
|
||||
{"value": "domain", "label": "域名管理"},
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, gin.H{"data": modules})
|
||||
}
|
||||
|
||||
// GetAuditActions 获取审计操作类型列表
|
||||
// @Summary 获取审计操作类型列表
|
||||
// @Description 获取所有审计操作类型
|
||||
// @Tags 审计日志
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Success 200 {object} map[string]interface{}
|
||||
// @Router /api/v1/audit/actions [get]
|
||||
func (h *AuditHandler) GetAuditActions(c *gin.Context) {
|
||||
actions := []map[string]string{
|
||||
{"value": "create", "label": "创建"},
|
||||
{"value": "update", "label": "更新"},
|
||||
{"value": "delete", "label": "删除"},
|
||||
{"value": "view", "label": "查看"},
|
||||
{"value": "login", "label": "登录"},
|
||||
{"value": "logout", "label": "登出"},
|
||||
{"value": "execute", "label": "执行"},
|
||||
{"value": "approve", "label": "审批"},
|
||||
{"value": "reject", "label": "驳回"},
|
||||
{"value": "import", "label": "导入"},
|
||||
{"value": "export", "label": "导出"},
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, gin.H{"data": actions})
|
||||
}
|
||||
|
||||
// ExportAuditLogs 导出审计日志
|
||||
// @Summary 导出审计日志
|
||||
// @Description 导出审计日志为Excel或CSV
|
||||
// @Tags 审计日志
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Param module query string false "模块"
|
||||
// @Param action query string false "操作类型"
|
||||
// @Param username query string false "用户名"
|
||||
// @Param startTime query string false "开始时间"
|
||||
// @Param endTime query string false "结束时间"
|
||||
// @Param format query string false "导出格式" default("csv") Enums(csv, excel)
|
||||
// @Success 200 {file} binary
|
||||
// @Router /api/v1/audit/logs/export [get]
|
||||
func (h *AuditHandler) ExportAuditLogs(c *gin.Context) {
|
||||
var req models.AuditLogQuery
|
||||
if err := c.ShouldBindQuery(&req); err != nil {
|
||||
c.JSON(http.StatusBadRequest, models.Response{Code: 400, Message: err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
// 构建查询
|
||||
query := h.db.Model(&models.AuditLog{})
|
||||
|
||||
if req.Module != "" {
|
||||
query = query.Where("module = ?", req.Module)
|
||||
}
|
||||
if req.Action != "" {
|
||||
query = query.Where("action = ?", req.Action)
|
||||
}
|
||||
if req.Username != "" {
|
||||
query = query.Where("username LIKE ?", "%"+req.Username+"%")
|
||||
}
|
||||
if req.StartTime != "" {
|
||||
query = query.Where("created_at >= ?", req.StartTime)
|
||||
}
|
||||
if req.EndTime != "" {
|
||||
query = query.Where("created_at <= ?", req.EndTime)
|
||||
}
|
||||
|
||||
// 获取数据
|
||||
var logs []models.AuditLog
|
||||
if err := query.Order("created_at DESC").Find(&logs).Error; err != nil {
|
||||
c.JSON(http.StatusInternalServerError, models.Response{Code: 500, Message: "导出失败"})
|
||||
return
|
||||
}
|
||||
|
||||
// TODO: 实现CSV/Excel导出功能
|
||||
// 这里返回JSON格式的数据,实际项目中应该生成CSV或Excel文件
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"message": "导出成功",
|
||||
"count": len(logs),
|
||||
"data": logs,
|
||||
})
|
||||
}
|
||||
|
||||
// GetAuditStatistics 获取审计统计
|
||||
// @Summary 获取审计统计
|
||||
// @Description 获取审计日志统计信息
|
||||
// @Tags 审计日志
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Param days query int false "统计天数" default(7)
|
||||
// @Success 200 {object} map[string]interface{}
|
||||
// @Router /api/v1/audit/statistics [get]
|
||||
func (h *AuditHandler) GetAuditStatistics(c *gin.Context) {
|
||||
days, _ := strconv.Atoi(c.DefaultQuery("days", "7"))
|
||||
if days <= 0 {
|
||||
days = 7
|
||||
}
|
||||
|
||||
startTime := time.Now().AddDate(0, 0, -days)
|
||||
|
||||
// 统计各模块的操作数量
|
||||
var moduleStats []struct {
|
||||
Module string `json:"module"`
|
||||
Count int64 `json:"count"`
|
||||
}
|
||||
h.db.Model(&models.AuditLog{}).
|
||||
Select("module, COUNT(*) as count").
|
||||
Where("created_at >= ?", startTime).
|
||||
Group("module").
|
||||
Scan(&moduleStats)
|
||||
|
||||
// 统计各操作类型的数量
|
||||
var actionStats []struct {
|
||||
Action string `json:"action"`
|
||||
Count int64 `json:"count"`
|
||||
}
|
||||
h.db.Model(&models.AuditLog{}).
|
||||
Select("action, COUNT(*) as count").
|
||||
Where("created_at >= ?", startTime).
|
||||
Group("action").
|
||||
Scan(&actionStats)
|
||||
|
||||
// 统计每日操作数量
|
||||
var dailyStats []struct {
|
||||
Date string `json:"date"`
|
||||
Count int64 `json:"count"`
|
||||
}
|
||||
h.db.Model(&models.AuditLog{}).
|
||||
Select("DATE(created_at) as date, COUNT(*) as count").
|
||||
Where("created_at >= ?", startTime).
|
||||
Group("DATE(created_at)").
|
||||
Order("date").
|
||||
Scan(&dailyStats)
|
||||
|
||||
// 总记录数
|
||||
var totalCount int64
|
||||
h.db.Model(&models.AuditLog{}).Count(&totalCount)
|
||||
|
||||
// 今日记录数
|
||||
today := time.Now().Format("2006-01-02")
|
||||
var todayCount int64
|
||||
h.db.Model(&models.AuditLog{}).
|
||||
Where("DATE(created_at) = ?", today).
|
||||
Count(&todayCount)
|
||||
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"total_count": totalCount,
|
||||
"today_count": todayCount,
|
||||
"module_stats": moduleStats,
|
||||
"action_stats": actionStats,
|
||||
"daily_stats": dailyStats,
|
||||
})
|
||||
}
|
||||
|
||||
// ClearAuditLogs 清理审计日志
|
||||
// @Summary 清理审计日志
|
||||
// @Description 清理指定日期之前的审计日志
|
||||
// @Tags 审计日志
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Param request body map[string]string true "清理请求"
|
||||
// @Success 200 {object} map[string]interface{}
|
||||
// @Router /api/v1/audit/logs/clear [post]
|
||||
func (h *AuditHandler) ClearAuditLogs(c *gin.Context) {
|
||||
var req struct {
|
||||
BeforeDate string `json:"before_date" binding:"required"`
|
||||
}
|
||||
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
c.JSON(http.StatusBadRequest, models.Response{Code: 400, Message: "无效的请求参数"})
|
||||
return
|
||||
}
|
||||
|
||||
// 解析日期
|
||||
beforeDate, err := time.Parse("2006-01-02", req.BeforeDate)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusBadRequest, models.Response{Code: 400, Message: "无效的日期格式"})
|
||||
return
|
||||
}
|
||||
|
||||
// 删除指定日期之前的日志
|
||||
result := h.db.Where("created_at < ?", beforeDate).Delete(&models.AuditLog{})
|
||||
if result.Error != nil {
|
||||
c.JSON(http.StatusInternalServerError, models.Response{Code: 500, Message: "清理失败"})
|
||||
return
|
||||
}
|
||||
|
||||
// 记录审计日志
|
||||
userID, _ := c.Get("userID")
|
||||
username, _ := c.Get("username")
|
||||
h.RecordAuditLog(userID.(uint), username.(string), "audit", "clear", "清理审计日志", gin.H{
|
||||
"before_date": req.BeforeDate,
|
||||
"deleted": result.RowsAffected,
|
||||
})
|
||||
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"message": "清理成功",
|
||||
"deleted": result.RowsAffected,
|
||||
})
|
||||
}
|
||||
|
||||
// RecordAuditLog 记录审计日志
|
||||
func (h *AuditHandler) RecordAuditLog(userID uint, username, module, action, description string, details interface{}) {
|
||||
uid := userID
|
||||
log := models.AuditLog{
|
||||
UserID: &uid,
|
||||
Username: username,
|
||||
Module: module,
|
||||
Action: action,
|
||||
Resource: description,
|
||||
}
|
||||
|
||||
h.db.Create(&log)
|
||||
}
|
||||
Reference in new issue
Block a user