Files
AI-CS/backend/controller/system_log_controller.go
2026-04-02 14:55:06 +08:00

187 lines
5.9 KiB
Go

package controller
import (
"net/http"
"strconv"
"strings"
"github.com/2930134478/AI-CS/backend/models"
"github.com/2930134478/AI-CS/backend/repository"
"github.com/2930134478/AI-CS/backend/service"
"github.com/gin-gonic/gin"
)
type SystemLogController struct {
logs *service.SystemLogService
users *service.UserService
appSettings *repository.AppSettingRepository
}
func NewSystemLogController(logs *service.SystemLogService, users *service.UserService, appSettings *repository.AppSettingRepository) *SystemLogController {
return &SystemLogController{logs: logs, users: users, appSettings: appSettings}
}
// GetLogs 查询日志(客服端)。
func (lc *SystemLogController) GetLogs(c *gin.Context) {
if !requirePermission(c, lc.users, string(service.PermLogs)) {
return
}
var convID *uint
if v := strings.TrimSpace(c.Query("conversation_id")); v != "" {
if id, err := strconv.ParseUint(v, 10, 64); err == nil {
t := uint(id)
convID = &t
}
}
page, _ := strconv.Atoi(c.DefaultQuery("page", "1"))
pageSize, _ := strconv.Atoi(c.DefaultQuery("page_size", "50"))
res, err := lc.logs.Query(service.QuerySystemLogsInput{
From: c.Query("from"),
To: c.Query("to"),
Level: c.Query("level"),
Category: c.Query("category"),
Event: c.Query("event"),
Source: c.Query("source"),
ConversationID: convID,
Keyword: c.Query("keyword"),
Page: page,
PageSize: pageSize,
})
if err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": "查询日志失败"})
return
}
c.JSON(http.StatusOK, res)
}
type reportFrontendLogRequest struct {
Level string `json:"level"`
Category string `json:"category"`
Event string `json:"event"`
TraceID string `json:"trace_id"`
ConversationID *uint `json:"conversation_id"`
VisitorID *uint `json:"visitor_id"`
Message string `json:"message"`
Meta map[string]interface{} `json:"meta"`
}
// ReportFrontendLog 前端上报日志(用于捕获页面异常与关键请求失败)。
func (lc *SystemLogController) ReportFrontendLog(c *gin.Context) {
var req reportFrontendLogRequest
if err := c.ShouldBindJSON(&req); err != nil || strings.TrimSpace(req.Message) == "" {
c.JSON(http.StatusBadRequest, gin.H{"error": "请求参数错误"})
return
}
userID := getUserIDFromHeader(c)
var pUserID *uint
if userID > 0 {
pUserID = &userID
}
// 基础防护:限制 message/meta 体量,避免日志接口被刷爆。
if len(req.Message) > 2000 {
req.Message = req.Message[:2000]
}
traceID := strings.TrimSpace(req.TraceID)
if traceID == "" {
traceID = getTraceID(c)
}
if req.Meta != nil {
req.Meta["truncated"] = false
}
if err := lc.logs.Create(service.CreateSystemLogInput{
Level: req.Level,
Category: req.Category,
Event: req.Event,
Source: "frontend",
TraceID: traceID,
ConversationID: req.ConversationID,
UserID: pUserID,
VisitorID: req.VisitorID,
Message: req.Message,
Meta: req.Meta,
}); err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": "写入日志失败"})
return
}
c.JSON(http.StatusOK, gin.H{"ok": true})
}
// GetLogMinLevel 返回当前生效的落库级别、环境变量默认值、是否由数据库覆盖。
func (lc *SystemLogController) GetLogMinLevel(c *gin.Context) {
if !requirePermission(c, lc.users, string(service.PermLogs)) {
return
}
envRank := service.SystemLogMinPersistLevelFromEnv()
effective := lc.logs.MinPersistLevelRank()
var persisted bool
if lc.appSettings != nil {
if row, err := lc.appSettings.Get(models.AppSettingKeySystemLogMinLevel); err == nil && row != nil && strings.TrimSpace(row.Value) != "" {
persisted = true
}
}
c.JSON(http.StatusOK, gin.H{
"effective_min_level": service.SystemLogMinLevelLabel(effective),
"env_min_level": service.SystemLogMinLevelLabel(envRank),
"persisted_in_database": persisted,
})
}
type putLogMinLevelBody struct {
MinLevel string `json:"min_level"`
}
// PutLogMinLevel 将最低落库级别写入数据库并立即生效(覆盖 .env,直至删除该配置)。
func (lc *SystemLogController) PutLogMinLevel(c *gin.Context) {
if !requirePermission(c, lc.users, string(service.PermLogs)) {
return
}
if lc.appSettings == nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": "配置存储不可用"})
return
}
var body putLogMinLevelBody
if err := c.ShouldBindJSON(&body); err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": "请求体无效"})
return
}
rank, err := service.ParseSystemLogMinPersistLevelStrict(body.MinLevel)
if err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
return
}
label := service.SystemLogMinLevelLabel(rank)
if err := lc.appSettings.SetValue(models.AppSettingKeySystemLogMinLevel, label); err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": "保存配置失败"})
return
}
lc.logs.SetMinPersistLevelRank(rank)
c.JSON(http.StatusOK, gin.H{
"ok": true,
"effective_min_level": label,
})
}
// DeleteLogMinLevel 删除数据库中的覆盖项,恢复为环境变量 SYSTEM_LOG_MIN_LEVEL。
func (lc *SystemLogController) DeleteLogMinLevel(c *gin.Context) {
if !requirePermission(c, lc.users, string(service.PermLogs)) {
return
}
if lc.appSettings == nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": "配置存储不可用"})
return
}
if err := lc.appSettings.Delete(models.AppSettingKeySystemLogMinLevel); err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": "删除配置失败"})
return
}
envRank := service.SystemLogMinPersistLevelFromEnv()
lc.logs.SetMinPersistLevelRank(envRank)
c.JSON(http.StatusOK, gin.H{
"ok": true,
"effective_min_level": service.SystemLogMinLevelLabel(envRank),
})
}