mirror of
https://github.com/2930134478/AI-CS.git
synced 2026-06-15 16:55:42 +08:00
187 lines
5.9 KiB
Go
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),
|
|
})
|
|
}
|
|
|