移除轮询

This commit is contained in:
2026-07-17 11:32:34 +08:00
parent 2232ef6f3c
commit 9b788189fd
7 changed files with 753 additions and 718 deletions
+2 -14
View File
@@ -242,14 +242,7 @@ const handleMarkAllRead = async () => {
} }
}; };
const handleMessagesChanged = () => { // 根据菜单列表和当前路径计算出的面包屑导航
fetchUnreadCount();
fetchMessages();
};
let timer: any = null;
// 根据菜单列表和当前路径计算出的面包屑导航
const breadcrumbs = computed(() => { const breadcrumbs = computed(() => {
let chain: Breadcrumb[] = []; let chain: Breadcrumb[] = [];
let currentPath = route.path || '/'; let currentPath = route.path || '/';
@@ -433,9 +426,8 @@ onMounted(async () => {
mediaQuery.addEventListener('change', handleChange); mediaQuery.addEventListener('change', handleChange);
if (authStore.token) { if (authStore.token) {
// 仅在后台页面刷新/首次挂载时更新未读数量,不再自动轮询。
fetchUnreadCount(); fetchUnreadCount();
timer = setInterval(fetchUnreadCount, 60000);
window.addEventListener('site-messages-changed', handleMessagesChanged);
} }
}); });
@@ -444,10 +436,6 @@ onUnmounted(() => {
if (mediaQuery && handleChange) { if (mediaQuery && handleChange) {
mediaQuery.removeEventListener('change', handleChange); mediaQuery.removeEventListener('change', handleChange);
} }
if (timer) {
clearInterval(timer);
}
window.removeEventListener('site-messages-changed', handleMessagesChanged);
}); });
</script> </script>
+339 -332
View File
@@ -1,332 +1,339 @@
package controllers package controllers
import ( import (
"encoding/json" "encoding/json"
"fmt" "fmt"
"io" "io"
"strconv" "strconv"
"strings" "strings"
"time" "time"
"server/models" "server/models"
"server/pkg/jwtutil" "server/pkg/jwtutil"
"server/services"
"github.com/beego/beego/v2/client/orm"
beego "github.com/beego/beego/v2/server/web" "github.com/beego/beego/v2/client/orm"
) beego "github.com/beego/beego/v2/server/web"
)
// BackendOperationLogController 操作日志(yz_system_operation_log
type BackendOperationLogController struct { // BackendOperationLogController 操作日志(yz_system_operation_log
beego.Controller type BackendOperationLogController struct {
} beego.Controller
}
func (c *BackendOperationLogController) backendClaims() (*jwtutil.Claims, error) {
auth := c.Ctx.Request.Header.Get("Authorization") func (c *BackendOperationLogController) backendClaims() (*jwtutil.Claims, error) {
if auth == "" { auth := c.Ctx.Request.Header.Get("Authorization")
return nil, fmt.Errorf("未登录") if auth == "" {
} return nil, fmt.Errorf("未登录")
parts := strings.SplitN(auth, " ", 2) }
if len(parts) != 2 || parts[0] != "Bearer" { parts := strings.SplitN(auth, " ", 2)
return nil, fmt.Errorf("认证信息格式错误") if len(parts) != 2 || parts[0] != "Bearer" {
} return nil, fmt.Errorf("认证信息格式错误")
claims, err := jwtutil.ParseToken(parts[1]) }
if err != nil { claims, err := jwtutil.ParseToken(parts[1])
return nil, fmt.Errorf("无效的token") if err != nil {
} return nil, fmt.Errorf("无效的token")
if claims.UserType != "backend" { }
return nil, fmt.Errorf("无权访问") if claims.UserType != "backend" {
} return nil, fmt.Errorf("无权访问")
return claims, nil }
} return claims, nil
}
func (c *BackendOperationLogController) jsonErr(httpStatus, bizCode int, msg string) {
c.Ctx.Output.SetStatus(httpStatus) func (c *BackendOperationLogController) jsonErr(httpStatus, bizCode int, msg string) {
c.Data["json"] = map[string]interface{}{"code": bizCode, "msg": msg} c.Ctx.Output.SetStatus(httpStatus)
_ = c.ServeJSON() c.Data["json"] = map[string]interface{}{"code": bizCode, "msg": msg}
} _ = c.ServeJSON()
}
// List GET /backend/operationLogs?page=1&pageSize=20&keyword=&module=&action=&status=&startTime=&endTime=
func (c *BackendOperationLogController) List() { // List GET /backend/operationLogs?page=1&pageSize=20&keyword=&module=&action=&status=&startTime=&endTime=
if _, err := c.backendClaims(); err != nil { func (c *BackendOperationLogController) List() {
c.jsonErr(401, 401, err.Error()) if _, err := c.backendClaims(); err != nil {
return c.jsonErr(401, 401, err.Error())
} return
}
page, _ := c.GetInt("page", 1)
pageSize, _ := c.GetInt("pageSize", 20) page, _ := c.GetInt("page", 1)
if page < 1 { pageSize, _ := c.GetInt("pageSize", 20)
page = 1 if page < 1 {
} page = 1
if pageSize < 1 { }
pageSize = 20 if pageSize < 1 {
} pageSize = 20
if pageSize > 200 { }
pageSize = 200 if pageSize > 200 {
} pageSize = 200
}
keyword := strings.TrimSpace(c.GetString("keyword"))
module := strings.TrimSpace(c.GetString("module")) keyword := strings.TrimSpace(c.GetString("keyword"))
action := strings.TrimSpace(c.GetString("action")) module := strings.TrimSpace(c.GetString("module"))
statusStr := strings.TrimSpace(c.GetString("status")) action := strings.TrimSpace(c.GetString("action"))
startTimeStr := strings.TrimSpace(c.GetString("startTime")) statusStr := strings.TrimSpace(c.GetString("status"))
endTimeStr := strings.TrimSpace(c.GetString("endTime")) startTimeStr := strings.TrimSpace(c.GetString("startTime"))
endTimeStr := strings.TrimSpace(c.GetString("endTime"))
qs := models.Orm.QueryTable(new(models.SystemOperationLog)).Filter("delete_time__isnull", true)
qs := models.Orm.QueryTable(new(models.SystemOperationLog)).Filter("delete_time__isnull", true)
// 条件拼装
cond := orm.NewCondition() // 条件拼装
needCond := false cond := orm.NewCondition()
needCond := false
if module != "" {
cond = cond.And("module", module) if module != "" {
needCond = true cond = cond.And("module", module)
} needCond = true
if action != "" { }
cond = cond.And("action", action) if action != "" {
needCond = true cond = cond.And("action", action)
} needCond = true
if statusStr != "" { }
if st, err := strconv.Atoi(statusStr); err == nil { if statusStr != "" {
cond = cond.And("status", st) if st, err := strconv.Atoi(statusStr); err == nil {
needCond = true cond = cond.And("status", st)
} needCond = true
} }
if keyword != "" { }
kw := orm.NewCondition(). if keyword != "" {
Or("module__icontains", keyword). kw := orm.NewCondition().
Or("action__icontains", keyword). Or("module__icontains", keyword).
Or("method__icontains", keyword). Or("action__icontains", keyword).
Or("url__icontains", keyword). Or("method__icontains", keyword).
Or("ip__icontains", keyword). Or("url__icontains", keyword).
Or("user_agent__icontains", keyword) Or("ip__icontains", keyword).
if uid, err := strconv.ParseUint(keyword, 10, 64); err == nil && uid > 0 { Or("user_agent__icontains", keyword)
kw = kw.Or("user_id", uid) if uid, err := strconv.ParseUint(keyword, 10, 64); err == nil && uid > 0 {
} kw = kw.Or("user_id", uid)
cond = cond.AndCond(kw) }
needCond = true cond = cond.AndCond(kw)
} needCond = true
if t, err := parseTimeFlexible(startTimeStr); err == nil && !t.IsZero() { }
cond = cond.And("create_time__gte", t) if t, err := parseTimeFlexible(startTimeStr); err == nil && !t.IsZero() {
needCond = true cond = cond.And("create_time__gte", t)
} needCond = true
if t, err := parseTimeFlexible(endTimeStr); err == nil && !t.IsZero() { }
cond = cond.And("create_time__lte", t) if t, err := parseTimeFlexible(endTimeStr); err == nil && !t.IsZero() {
needCond = true cond = cond.And("create_time__lte", t)
} needCond = true
}
if needCond {
qs = qs.SetCond(cond) if needCond {
} qs = qs.SetCond(cond)
}
total, err := qs.Count()
if err != nil { total, err := qs.Count()
c.jsonErr(500, 500, "获取操作日志失败: "+err.Error()) if err != nil {
return c.jsonErr(500, 500, "获取操作日志失败: "+err.Error())
} return
}
var rows []models.SystemOperationLog
_, err = qs.OrderBy("-id").Limit(pageSize, (page-1)*pageSize).All(&rows) var rows []models.SystemOperationLog
if err != nil { _, err = qs.OrderBy("-id").Limit(pageSize, (page-1)*pageSize).All(&rows)
c.jsonErr(500, 500, "获取操作日志失败: "+err.Error()) if err != nil {
return c.jsonErr(500, 500, "获取操作日志失败: "+err.Error())
} return
}
list := make([]map[string]interface{}, 0, len(rows))
for i := range rows { list := make([]map[string]interface{}, 0, len(rows))
item := map[string]interface{}{ for i := range rows {
"id": rows[i].ID, userAccount, userName := services.OperationLogUser(rows[i].Tid, rows[i].UserID)
"tid": rows[i].Tid, item := map[string]interface{}{
"user_id": rows[i].UserID, "id": rows[i].ID,
"module": rows[i].Module, "tid": rows[i].Tid,
"action": rows[i].Action, "user_id": rows[i].UserID,
"method": rows[i].Method, "user_account": userAccount,
"url": rows[i].URL, "user_name": userName,
"ip": rows[i].IP, "module": rows[i].Module,
"user_agent": rows[i].UserAgent, "action": rows[i].Action,
"request_data": rows[i].RequestData, "method": rows[i].Method,
"response_data": rows[i].ResponseData, "url": rows[i].URL,
"status": rows[i].Status, "ip": rows[i].IP,
"error_message": rows[i].ErrorMessage, "user_agent": rows[i].UserAgent,
"execution_time": rows[i].ExecutionTime, "request_data": rows[i].RequestData,
"create_time": rows[i].CreateTime.Format("2006-01-02 15:04:05"), "response_data": rows[i].ResponseData,
"update_time": "", "status": rows[i].Status,
} "error_message": rows[i].ErrorMessage,
if rows[i].UpdateTime != nil { "execution_time": rows[i].ExecutionTime,
item["update_time"] = rows[i].UpdateTime.Format("2006-01-02 15:04:05") "create_time": rows[i].CreateTime.Format("2006-01-02 15:04:05"),
} "update_time": "",
list = append(list, item) }
} if rows[i].UpdateTime != nil {
item["update_time"] = rows[i].UpdateTime.Format("2006-01-02 15:04:05")
c.Data["json"] = map[string]interface{}{ }
"code": 200, list = append(list, item)
"msg": "success", }
"data": map[string]interface{}{
"list": list, c.Data["json"] = map[string]interface{}{
"total": total, "code": 200,
}, "msg": "success",
} "data": map[string]interface{}{
_ = c.ServeJSON() "list": list,
} "total": total,
},
// Detail GET /backend/operationLogs/:id }
func (c *BackendOperationLogController) Detail() { _ = c.ServeJSON()
if _, err := c.backendClaims(); err != nil { }
c.jsonErr(401, 401, err.Error())
return // Detail GET /backend/operationLogs/:id
} func (c *BackendOperationLogController) Detail() {
idStr := c.Ctx.Input.Param(":id") if _, err := c.backendClaims(); err != nil {
id, err := strconv.ParseUint(idStr, 10, 64) c.jsonErr(401, 401, err.Error())
if err != nil || id == 0 { return
c.jsonErr(400, 400, "无效ID") }
return idStr := c.Ctx.Input.Param(":id")
} id, err := strconv.ParseUint(idStr, 10, 64)
var row models.SystemOperationLog if err != nil || id == 0 {
err = models.Orm.QueryTable(new(models.SystemOperationLog)). c.jsonErr(400, 400, "无效ID")
Filter("id", id). return
Filter("delete_time__isnull", true). }
One(&row) var row models.SystemOperationLog
if err != nil { err = models.Orm.QueryTable(new(models.SystemOperationLog)).
c.jsonErr(404, 404, "记录不存在") Filter("id", id).
return Filter("delete_time__isnull", true).
} One(&row)
out := map[string]interface{}{ if err != nil {
"id": row.ID, c.jsonErr(404, 404, "记录不存在")
"tid": row.Tid, return
"user_id": row.UserID, }
"module": row.Module, userAccount, userName := services.OperationLogUser(row.Tid, row.UserID)
"action": row.Action, out := map[string]interface{}{
"method": row.Method, "id": row.ID,
"url": row.URL, "tid": row.Tid,
"ip": row.IP, "user_id": row.UserID,
"user_agent": row.UserAgent, "user_account": userAccount,
"request_data": row.RequestData, "user_name": userName,
"response_data": row.ResponseData, "module": row.Module,
"status": row.Status, "action": row.Action,
"error_message": row.ErrorMessage, "method": row.Method,
"execution_time": row.ExecutionTime, "url": row.URL,
"create_time": row.CreateTime.Format("2006-01-02 15:04:05"), "ip": row.IP,
} "user_agent": row.UserAgent,
c.Data["json"] = map[string]interface{}{"code": 200, "msg": "success", "data": out} "request_data": row.RequestData,
_ = c.ServeJSON() "response_data": row.ResponseData,
} "status": row.Status,
"error_message": row.ErrorMessage,
// Delete DELETE /backend/operationLogs/:id "execution_time": row.ExecutionTime,
func (c *BackendOperationLogController) Delete() { "create_time": row.CreateTime.Format("2006-01-02 15:04:05"),
if _, err := c.backendClaims(); err != nil { }
c.jsonErr(401, 401, err.Error()) c.Data["json"] = map[string]interface{}{"code": 200, "msg": "success", "data": out}
return _ = c.ServeJSON()
} }
idStr := c.Ctx.Input.Param(":id")
id, err := strconv.ParseUint(idStr, 10, 64) // Delete DELETE /backend/operationLogs/:id
if err != nil || id == 0 { func (c *BackendOperationLogController) Delete() {
c.jsonErr(400, 400, "无效ID") if _, err := c.backendClaims(); err != nil {
return c.jsonErr(401, 401, err.Error())
} return
now := time.Now() }
n, err := models.Orm.QueryTable(new(models.SystemOperationLog)). idStr := c.Ctx.Input.Param(":id")
Filter("id", id). id, err := strconv.ParseUint(idStr, 10, 64)
Filter("delete_time__isnull", true). if err != nil || id == 0 {
Update(map[string]interface{}{"delete_time": now}) c.jsonErr(400, 400, "无效ID")
if err != nil { return
c.jsonErr(500, 500, "删除失败: "+err.Error()) }
return now := time.Now()
} n, err := models.Orm.QueryTable(new(models.SystemOperationLog)).
if n == 0 { Filter("id", id).
c.jsonErr(404, 404, "记录不存在") Filter("delete_time__isnull", true).
return Update(map[string]interface{}{"delete_time": now})
} if err != nil {
c.Data["json"] = map[string]interface{}{"code": 200, "msg": "删除成功"} c.jsonErr(500, 500, "删除失败: "+err.Error())
_ = c.ServeJSON() return
} }
if n == 0 {
type backendBatchDeletePayload struct { c.jsonErr(404, 404, "记录不存在")
IDs []uint64 `json:"ids"` return
} }
c.Data["json"] = map[string]interface{}{"code": 200, "msg": "删除成功"}
// BatchDelete POST /backend/operationLogs/batchDelete _ = c.ServeJSON()
func (c *BackendOperationLogController) BatchDelete() { }
if _, err := c.backendClaims(); err != nil {
c.jsonErr(401, 401, err.Error()) type backendBatchDeletePayload struct {
return IDs []uint64 `json:"ids"`
} }
raw, err := io.ReadAll(c.Ctx.Request.Body)
if err != nil { // BatchDelete POST /backend/operationLogs/batchDelete
c.jsonErr(400, 400, "参数错误") func (c *BackendOperationLogController) BatchDelete() {
return if _, err := c.backendClaims(); err != nil {
} c.jsonErr(401, 401, err.Error())
var p backendBatchDeletePayload return
if err := json.Unmarshal(raw, &p); err != nil { }
c.jsonErr(400, 400, "参数错误") raw, err := io.ReadAll(c.Ctx.Request.Body)
return if err != nil {
} c.jsonErr(400, 400, "参数错误")
if len(p.IDs) == 0 { return
c.jsonErr(400, 400, "请选择要删除的日志") }
return var p backendBatchDeletePayload
} if err := json.Unmarshal(raw, &p); err != nil {
now := time.Now() c.jsonErr(400, 400, "参数错误")
_, err = models.Orm.QueryTable(new(models.SystemOperationLog)). return
Filter("id__in", p.IDs). }
Filter("delete_time__isnull", true). if len(p.IDs) == 0 {
Update(map[string]interface{}{"delete_time": now}) c.jsonErr(400, 400, "请选择要删除的日志")
if err != nil { return
c.jsonErr(500, 500, "批量删除失败: "+err.Error()) }
return now := time.Now()
} _, err = models.Orm.QueryTable(new(models.SystemOperationLog)).
c.Data["json"] = map[string]interface{}{"code": 200, "msg": "批量删除成功"} Filter("id__in", p.IDs).
_ = c.ServeJSON() Filter("delete_time__isnull", true).
} Update(map[string]interface{}{"delete_time": now})
if err != nil {
// Statistics GET /backend/operationLogs/statistics c.jsonErr(500, 500, "批量删除失败: "+err.Error())
// 供前端筛选项:modules/actions return
func (c *BackendOperationLogController) Statistics() { }
if _, err := c.backendClaims(); err != nil { c.Data["json"] = map[string]interface{}{"code": 200, "msg": "批量删除成功"}
c.jsonErr(401, 401, err.Error()) _ = c.ServeJSON()
return }
}
// Statistics GET /backend/operationLogs/statistics
var moduleRows []models.SystemOperationLog // 供前端筛选项:modules/actions
_, _ = models.Orm.QueryTable(new(models.SystemOperationLog)). func (c *BackendOperationLogController) Statistics() {
Filter("delete_time__isnull", true). if _, err := c.backendClaims(); err != nil {
Filter("module__isnull", false). c.jsonErr(401, 401, err.Error())
Limit(1000). return
All(&moduleRows, "Module") }
modSet := map[string]struct{}{}
for i := range moduleRows { var moduleRows []models.SystemOperationLog
m := strings.TrimSpace(moduleRows[i].Module) _, _ = models.Orm.QueryTable(new(models.SystemOperationLog)).
if m != "" { Filter("delete_time__isnull", true).
modSet[m] = struct{}{} Filter("module__isnull", false).
} Limit(1000).
} All(&moduleRows, "Module")
modules := make([]string, 0, len(modSet)) modSet := map[string]struct{}{}
for k := range modSet { for i := range moduleRows {
modules = append(modules, k) m := strings.TrimSpace(moduleRows[i].Module)
} if m != "" {
modSet[m] = struct{}{}
var actionRows []models.SystemOperationLog }
_, _ = models.Orm.QueryTable(new(models.SystemOperationLog)). }
Filter("delete_time__isnull", true). modules := make([]string, 0, len(modSet))
Filter("action__isnull", false). for k := range modSet {
Limit(1000). modules = append(modules, k)
All(&actionRows, "Action") }
actSet := map[string]struct{}{}
for i := range actionRows { var actionRows []models.SystemOperationLog
a := strings.TrimSpace(actionRows[i].Action) _, _ = models.Orm.QueryTable(new(models.SystemOperationLog)).
if a != "" { Filter("delete_time__isnull", true).
actSet[a] = struct{}{} Filter("action__isnull", false).
} Limit(1000).
} All(&actionRows, "Action")
actions := make([]string, 0, len(actSet)) actSet := map[string]struct{}{}
for k := range actSet { for i := range actionRows {
actions = append(actions, k) a := strings.TrimSpace(actionRows[i].Action)
} if a != "" {
actSet[a] = struct{}{}
c.Data["json"] = map[string]interface{}{ }
"code": 200, }
"msg": "success", actions := make([]string, 0, len(actSet))
"data": map[string]interface{}{ for k := range actSet {
"modules": modules, actions = append(actions, k)
"actions": actions, }
},
} c.Data["json"] = map[string]interface{}{
_ = c.ServeJSON() "code": 200,
} "msg": "success",
"data": map[string]interface{}{
"modules": modules,
"actions": actions,
},
}
_ = c.ServeJSON()
}
+358 -351
View File
@@ -1,351 +1,358 @@
package controllers package controllers
import ( import (
"encoding/json" "encoding/json"
"fmt" "fmt"
"io" "io"
"strconv" "strconv"
"strings" "strings"
"time" "time"
"server/models" "server/models"
"server/pkg/jwtutil" "server/pkg/jwtutil"
"server/services"
"github.com/beego/beego/v2/client/orm"
beego "github.com/beego/beego/v2/server/web" "github.com/beego/beego/v2/client/orm"
) beego "github.com/beego/beego/v2/server/web"
)
// PlatformOperationLogController 操作日志(yz_system_operation_log
type PlatformOperationLogController struct { // PlatformOperationLogController 操作日志(yz_system_operation_log
beego.Controller type PlatformOperationLogController struct {
} beego.Controller
}
func (c *PlatformOperationLogController) platformClaims() (*jwtutil.Claims, error) {
auth := c.Ctx.Request.Header.Get("Authorization") func (c *PlatformOperationLogController) platformClaims() (*jwtutil.Claims, error) {
if auth == "" { auth := c.Ctx.Request.Header.Get("Authorization")
return nil, fmt.Errorf("未登录") if auth == "" {
} return nil, fmt.Errorf("未登录")
parts := strings.SplitN(auth, " ", 2) }
if len(parts) != 2 || parts[0] != "Bearer" { parts := strings.SplitN(auth, " ", 2)
return nil, fmt.Errorf("认证信息格式错误") if len(parts) != 2 || parts[0] != "Bearer" {
} return nil, fmt.Errorf("认证信息格式错误")
claims, err := jwtutil.ParseToken(parts[1]) }
if err != nil { claims, err := jwtutil.ParseToken(parts[1])
return nil, fmt.Errorf("无效的token") if err != nil {
} return nil, fmt.Errorf("无效的token")
if claims.UserType != "platform" { }
return nil, fmt.Errorf("无权访问") if claims.UserType != "platform" {
} return nil, fmt.Errorf("无权访问")
return claims, nil }
} return claims, nil
}
func (c *PlatformOperationLogController) jsonErr(httpStatus, bizCode int, msg string) {
c.Ctx.Output.SetStatus(httpStatus) func (c *PlatformOperationLogController) jsonErr(httpStatus, bizCode int, msg string) {
c.Data["json"] = map[string]interface{}{"code": bizCode, "msg": msg} c.Ctx.Output.SetStatus(httpStatus)
_ = c.ServeJSON() c.Data["json"] = map[string]interface{}{"code": bizCode, "msg": msg}
} _ = c.ServeJSON()
}
// List GET /platform/operationLogs?page=1&pageSize=20&keyword=&module=&action=&status=&startTime=&endTime=
func (c *PlatformOperationLogController) List() { // List GET /platform/operationLogs?page=1&pageSize=20&keyword=&module=&action=&status=&startTime=&endTime=
if _, err := c.platformClaims(); err != nil { func (c *PlatformOperationLogController) List() {
c.jsonErr(401, 401, err.Error()) if _, err := c.platformClaims(); err != nil {
return c.jsonErr(401, 401, err.Error())
} return
}
page, _ := c.GetInt("page", 1)
pageSize, _ := c.GetInt("pageSize", 20) page, _ := c.GetInt("page", 1)
if page < 1 { pageSize, _ := c.GetInt("pageSize", 20)
page = 1 if page < 1 {
} page = 1
if pageSize < 1 { }
pageSize = 20 if pageSize < 1 {
} pageSize = 20
if pageSize > 200 { }
pageSize = 200 if pageSize > 200 {
} pageSize = 200
}
keyword := strings.TrimSpace(c.GetString("keyword"))
module := strings.TrimSpace(c.GetString("module")) keyword := strings.TrimSpace(c.GetString("keyword"))
action := strings.TrimSpace(c.GetString("action")) module := strings.TrimSpace(c.GetString("module"))
statusStr := strings.TrimSpace(c.GetString("status")) action := strings.TrimSpace(c.GetString("action"))
startTimeStr := strings.TrimSpace(c.GetString("startTime")) statusStr := strings.TrimSpace(c.GetString("status"))
endTimeStr := strings.TrimSpace(c.GetString("endTime")) startTimeStr := strings.TrimSpace(c.GetString("startTime"))
endTimeStr := strings.TrimSpace(c.GetString("endTime"))
qs := models.Orm.QueryTable(new(models.SystemOperationLog)).Filter("delete_time__isnull", true)
qs := models.Orm.QueryTable(new(models.SystemOperationLog)).Filter("delete_time__isnull", true)
// 条件拼装
cond := orm.NewCondition() // 条件拼装
needCond := false cond := orm.NewCondition()
needCond := false
if module != "" {
cond = cond.And("module", module) if module != "" {
needCond = true cond = cond.And("module", module)
} needCond = true
if action != "" { }
cond = cond.And("action", action) if action != "" {
needCond = true cond = cond.And("action", action)
} needCond = true
if statusStr != "" { }
if st, err := strconv.Atoi(statusStr); err == nil { if statusStr != "" {
cond = cond.And("status", st) if st, err := strconv.Atoi(statusStr); err == nil {
needCond = true cond = cond.And("status", st)
} needCond = true
} }
if keyword != "" { }
kw := orm.NewCondition(). if keyword != "" {
Or("module__icontains", keyword). kw := orm.NewCondition().
Or("action__icontains", keyword). Or("module__icontains", keyword).
Or("method__icontains", keyword). Or("action__icontains", keyword).
Or("url__icontains", keyword). Or("method__icontains", keyword).
Or("ip__icontains", keyword). Or("url__icontains", keyword).
Or("user_agent__icontains", keyword) Or("ip__icontains", keyword).
if uid, err := strconv.ParseUint(keyword, 10, 64); err == nil && uid > 0 { Or("user_agent__icontains", keyword)
kw = kw.Or("user_id", uid) if uid, err := strconv.ParseUint(keyword, 10, 64); err == nil && uid > 0 {
} kw = kw.Or("user_id", uid)
cond = cond.AndCond(kw) }
needCond = true cond = cond.AndCond(kw)
} needCond = true
if t, err := parseTimeFlexible(startTimeStr); err == nil && !t.IsZero() { }
cond = cond.And("create_time__gte", t) if t, err := parseTimeFlexible(startTimeStr); err == nil && !t.IsZero() {
needCond = true cond = cond.And("create_time__gte", t)
} needCond = true
if t, err := parseTimeFlexible(endTimeStr); err == nil && !t.IsZero() { }
cond = cond.And("create_time__lte", t) if t, err := parseTimeFlexible(endTimeStr); err == nil && !t.IsZero() {
needCond = true cond = cond.And("create_time__lte", t)
} needCond = true
}
if needCond {
qs = qs.SetCond(cond) if needCond {
} qs = qs.SetCond(cond)
}
total, err := qs.Count()
if err != nil { total, err := qs.Count()
c.jsonErr(500, 500, "获取操作日志失败: "+err.Error()) if err != nil {
return c.jsonErr(500, 500, "获取操作日志失败: "+err.Error())
} return
}
var rows []models.SystemOperationLog
_, err = qs.OrderBy("-id").Limit(pageSize, (page-1)*pageSize).All(&rows) var rows []models.SystemOperationLog
if err != nil { _, err = qs.OrderBy("-id").Limit(pageSize, (page-1)*pageSize).All(&rows)
c.jsonErr(500, 500, "获取操作日志失败: "+err.Error()) if err != nil {
return c.jsonErr(500, 500, "获取操作日志失败: "+err.Error())
} return
}
list := make([]map[string]interface{}, 0, len(rows))
for i := range rows { list := make([]map[string]interface{}, 0, len(rows))
item := map[string]interface{}{ for i := range rows {
"id": rows[i].ID, userAccount, userName := services.OperationLogUser(rows[i].Tid, rows[i].UserID)
"tid": rows[i].Tid, item := map[string]interface{}{
"user_id": rows[i].UserID, "id": rows[i].ID,
"module": rows[i].Module, "tid": rows[i].Tid,
"action": rows[i].Action, "user_id": rows[i].UserID,
"method": rows[i].Method, "user_account": userAccount,
"url": rows[i].URL, "user_name": userName,
"ip": rows[i].IP, "module": rows[i].Module,
"user_agent": rows[i].UserAgent, "action": rows[i].Action,
"request_data": rows[i].RequestData, "method": rows[i].Method,
"response_data": rows[i].ResponseData, "url": rows[i].URL,
"status": rows[i].Status, "ip": rows[i].IP,
"error_message": rows[i].ErrorMessage, "user_agent": rows[i].UserAgent,
"execution_time": rows[i].ExecutionTime, "request_data": rows[i].RequestData,
"create_time": rows[i].CreateTime.Format("2006-01-02 15:04:05"), "response_data": rows[i].ResponseData,
"update_time": "", "status": rows[i].Status,
} "error_message": rows[i].ErrorMessage,
if rows[i].UpdateTime != nil { "execution_time": rows[i].ExecutionTime,
item["update_time"] = rows[i].UpdateTime.Format("2006-01-02 15:04:05") "create_time": rows[i].CreateTime.Format("2006-01-02 15:04:05"),
} "update_time": "",
list = append(list, item) }
} if rows[i].UpdateTime != nil {
item["update_time"] = rows[i].UpdateTime.Format("2006-01-02 15:04:05")
c.Data["json"] = map[string]interface{}{ }
"code": 200, list = append(list, item)
"msg": "success", }
"data": map[string]interface{}{
"list": list, c.Data["json"] = map[string]interface{}{
"total": total, "code": 200,
}, "msg": "success",
} "data": map[string]interface{}{
_ = c.ServeJSON() "list": list,
} "total": total,
},
// Detail GET /platform/operationLogs/:id }
func (c *PlatformOperationLogController) Detail() { _ = c.ServeJSON()
if _, err := c.platformClaims(); err != nil { }
c.jsonErr(401, 401, err.Error())
return // Detail GET /platform/operationLogs/:id
} func (c *PlatformOperationLogController) Detail() {
idStr := c.Ctx.Input.Param(":id") if _, err := c.platformClaims(); err != nil {
id, err := strconv.ParseUint(idStr, 10, 64) c.jsonErr(401, 401, err.Error())
if err != nil || id == 0 { return
c.jsonErr(400, 400, "无效ID") }
return idStr := c.Ctx.Input.Param(":id")
} id, err := strconv.ParseUint(idStr, 10, 64)
var row models.SystemOperationLog if err != nil || id == 0 {
err = models.Orm.QueryTable(new(models.SystemOperationLog)). c.jsonErr(400, 400, "无效ID")
Filter("id", id). return
Filter("delete_time__isnull", true). }
One(&row) var row models.SystemOperationLog
if err != nil { err = models.Orm.QueryTable(new(models.SystemOperationLog)).
c.jsonErr(404, 404, "记录不存在") Filter("id", id).
return Filter("delete_time__isnull", true).
} One(&row)
out := map[string]interface{}{ if err != nil {
"id": row.ID, c.jsonErr(404, 404, "记录不存在")
"tid": row.Tid, return
"user_id": row.UserID, }
"module": row.Module, userAccount, userName := services.OperationLogUser(row.Tid, row.UserID)
"action": row.Action, out := map[string]interface{}{
"method": row.Method, "id": row.ID,
"url": row.URL, "tid": row.Tid,
"ip": row.IP, "user_id": row.UserID,
"user_agent": row.UserAgent, "user_account": userAccount,
"request_data": row.RequestData, "user_name": userName,
"response_data": row.ResponseData, "module": row.Module,
"status": row.Status, "action": row.Action,
"error_message": row.ErrorMessage, "method": row.Method,
"execution_time": row.ExecutionTime, "url": row.URL,
"create_time": row.CreateTime.Format("2006-01-02 15:04:05"), "ip": row.IP,
} "user_agent": row.UserAgent,
c.Data["json"] = map[string]interface{}{"code": 200, "msg": "success", "data": out} "request_data": row.RequestData,
_ = c.ServeJSON() "response_data": row.ResponseData,
} "status": row.Status,
"error_message": row.ErrorMessage,
// Delete DELETE /platform/operationLogs/:id "execution_time": row.ExecutionTime,
func (c *PlatformOperationLogController) Delete() { "create_time": row.CreateTime.Format("2006-01-02 15:04:05"),
if _, err := c.platformClaims(); err != nil { }
c.jsonErr(401, 401, err.Error()) c.Data["json"] = map[string]interface{}{"code": 200, "msg": "success", "data": out}
return _ = c.ServeJSON()
} }
idStr := c.Ctx.Input.Param(":id")
id, err := strconv.ParseUint(idStr, 10, 64) // Delete DELETE /platform/operationLogs/:id
if err != nil || id == 0 { func (c *PlatformOperationLogController) Delete() {
c.jsonErr(400, 400, "无效ID") if _, err := c.platformClaims(); err != nil {
return c.jsonErr(401, 401, err.Error())
} return
now := time.Now() }
n, err := models.Orm.QueryTable(new(models.SystemOperationLog)). idStr := c.Ctx.Input.Param(":id")
Filter("id", id). id, err := strconv.ParseUint(idStr, 10, 64)
Filter("delete_time__isnull", true). if err != nil || id == 0 {
Update(map[string]interface{}{"delete_time": now}) c.jsonErr(400, 400, "无效ID")
if err != nil { return
c.jsonErr(500, 500, "删除失败: "+err.Error()) }
return now := time.Now()
} n, err := models.Orm.QueryTable(new(models.SystemOperationLog)).
if n == 0 { Filter("id", id).
c.jsonErr(404, 404, "记录不存在") Filter("delete_time__isnull", true).
return Update(map[string]interface{}{"delete_time": now})
} if err != nil {
c.Data["json"] = map[string]interface{}{"code": 200, "msg": "删除成功"} c.jsonErr(500, 500, "删除失败: "+err.Error())
_ = c.ServeJSON() return
} }
if n == 0 {
type batchDeletePayload struct { c.jsonErr(404, 404, "记录不存在")
IDs []uint64 `json:"ids"` return
} }
c.Data["json"] = map[string]interface{}{"code": 200, "msg": "删除成功"}
// BatchDelete POST /platform/operationLogs/batchDelete _ = c.ServeJSON()
func (c *PlatformOperationLogController) BatchDelete() { }
if _, err := c.platformClaims(); err != nil {
c.jsonErr(401, 401, err.Error()) type batchDeletePayload struct {
return IDs []uint64 `json:"ids"`
} }
raw, err := io.ReadAll(c.Ctx.Request.Body)
if err != nil { // BatchDelete POST /platform/operationLogs/batchDelete
c.jsonErr(400, 400, "参数错误") func (c *PlatformOperationLogController) BatchDelete() {
return if _, err := c.platformClaims(); err != nil {
} c.jsonErr(401, 401, err.Error())
var p batchDeletePayload return
if err := json.Unmarshal(raw, &p); err != nil { }
c.jsonErr(400, 400, "参数错误") raw, err := io.ReadAll(c.Ctx.Request.Body)
return if err != nil {
} c.jsonErr(400, 400, "参数错误")
if len(p.IDs) == 0 { return
c.jsonErr(400, 400, "请选择要删除的日志") }
return var p batchDeletePayload
} if err := json.Unmarshal(raw, &p); err != nil {
now := time.Now() c.jsonErr(400, 400, "参数错误")
_, err = models.Orm.QueryTable(new(models.SystemOperationLog)). return
Filter("id__in", p.IDs). }
Filter("delete_time__isnull", true). if len(p.IDs) == 0 {
Update(map[string]interface{}{"delete_time": now}) c.jsonErr(400, 400, "请选择要删除的日志")
if err != nil { return
c.jsonErr(500, 500, "批量删除失败: "+err.Error()) }
return now := time.Now()
} _, err = models.Orm.QueryTable(new(models.SystemOperationLog)).
c.Data["json"] = map[string]interface{}{"code": 200, "msg": "批量删除成功"} Filter("id__in", p.IDs).
_ = c.ServeJSON() Filter("delete_time__isnull", true).
} Update(map[string]interface{}{"delete_time": now})
if err != nil {
// Statistics GET /platform/operationLogs/statistics c.jsonErr(500, 500, "批量删除失败: "+err.Error())
// 供前端筛选项:modules/actions return
func (c *PlatformOperationLogController) Statistics() { }
if _, err := c.platformClaims(); err != nil { c.Data["json"] = map[string]interface{}{"code": 200, "msg": "批量删除成功"}
c.jsonErr(401, 401, err.Error()) _ = c.ServeJSON()
return }
}
// Statistics GET /platform/operationLogs/statistics
var moduleRows []models.SystemOperationLog // 供前端筛选项:modules/actions
_, _ = models.Orm.QueryTable(new(models.SystemOperationLog)). func (c *PlatformOperationLogController) Statistics() {
Filter("delete_time__isnull", true). if _, err := c.platformClaims(); err != nil {
Filter("module__isnull", false). c.jsonErr(401, 401, err.Error())
Limit(1000). return
All(&moduleRows, "Module") }
modSet := map[string]struct{}{}
for i := range moduleRows { var moduleRows []models.SystemOperationLog
m := strings.TrimSpace(moduleRows[i].Module) _, _ = models.Orm.QueryTable(new(models.SystemOperationLog)).
if m != "" { Filter("delete_time__isnull", true).
modSet[m] = struct{}{} Filter("module__isnull", false).
} Limit(1000).
} All(&moduleRows, "Module")
modules := make([]string, 0, len(modSet)) modSet := map[string]struct{}{}
for k := range modSet { for i := range moduleRows {
modules = append(modules, k) m := strings.TrimSpace(moduleRows[i].Module)
} if m != "" {
modSet[m] = struct{}{}
var actionRows []models.SystemOperationLog }
_, _ = models.Orm.QueryTable(new(models.SystemOperationLog)). }
Filter("delete_time__isnull", true). modules := make([]string, 0, len(modSet))
Filter("action__isnull", false). for k := range modSet {
Limit(1000). modules = append(modules, k)
All(&actionRows, "Action") }
actSet := map[string]struct{}{}
for i := range actionRows { var actionRows []models.SystemOperationLog
a := strings.TrimSpace(actionRows[i].Action) _, _ = models.Orm.QueryTable(new(models.SystemOperationLog)).
if a != "" { Filter("delete_time__isnull", true).
actSet[a] = struct{}{} Filter("action__isnull", false).
} Limit(1000).
} All(&actionRows, "Action")
actions := make([]string, 0, len(actSet)) actSet := map[string]struct{}{}
for k := range actSet { for i := range actionRows {
actions = append(actions, k) a := strings.TrimSpace(actionRows[i].Action)
} if a != "" {
actSet[a] = struct{}{}
c.Data["json"] = map[string]interface{}{ }
"code": 200, }
"msg": "success", actions := make([]string, 0, len(actSet))
"data": map[string]interface{}{ for k := range actSet {
"modules": modules, actions = append(actions, k)
"actions": actions, }
},
} c.Data["json"] = map[string]interface{}{
_ = c.ServeJSON() "code": 200,
} "msg": "success",
"data": map[string]interface{}{
func parseTimeFlexible(s string) (time.Time, error) { "modules": modules,
s = strings.TrimSpace(s) "actions": actions,
if s == "" { },
return time.Time{}, fmt.Errorf("empty") }
} _ = c.ServeJSON()
layouts := []string{ }
"2006-01-02 15:04:05",
"2006-01-02 15:04", func parseTimeFlexible(s string) (time.Time, error) {
"2006-01-02", s = strings.TrimSpace(s)
time.RFC3339, if s == "" {
} return time.Time{}, fmt.Errorf("empty")
for _, ly := range layouts { }
if t, err := time.ParseInLocation(ly, s, time.Local); err == nil { layouts := []string{
return t, nil "2006-01-02 15:04:05",
} "2006-01-02 15:04",
} "2006-01-02",
return time.Time{}, fmt.Errorf("invalid time") time.RFC3339,
} }
for _, ly := range layouts {
if t, err := time.ParseInLocation(ly, s, time.Local); err == nil {
return t, nil
}
}
return time.Time{}, fmt.Errorf("invalid time")
}
+38
View File
@@ -0,0 +1,38 @@
package services
import "server/models"
// OperationLogUser 根据操作日志的租户范围解析操作人信息。
// tid 为空或为 0 时,日志来自平台管理员;否则来自租户用户。
func OperationLogUser(tid *uint64, userID uint64) (account, name string) {
if userID == 0 {
return "", ""
}
if tid != nil && *tid != 0 {
var user models.SystemTenantUser
if err := models.Orm.QueryTable(new(models.SystemTenantUser)).
Filter("tid", *tid).
Filter("uid", userID).
One(&user); err == nil {
if user.Account != nil {
account = *user.Account
}
if user.Name != nil {
name = *user.Name
}
}
return account, name
}
var user models.AdminUser
if err := models.Orm.QueryTable(new(models.AdminUser)).
Filter("id", userID).
One(&user); err == nil {
account = user.Account
if user.Name != nil {
name = *user.Name
}
}
return account, name
}
+16 -21
View File
@@ -8,34 +8,29 @@ import (
"server/models" "server/models"
) )
/* findTenantByLoginName 根据租户简称、短名或编码查找租户。
*
* 当前表结构中 tenant_name 的业务含义就是“租户简称”,同时兼容
* tenant_short_name 和 tenant_code 两个历史/备用登录标识。
*/
func findTenantByLoginName(loginName string) (*models.SystemTenant, error) { func findTenantByLoginName(loginName string) (*models.SystemTenant, error) {
loginName = strings.TrimSpace(loginName) loginName = strings.TrimSpace(loginName)
if loginName == "" { if loginName == "" {
return nil, orm.ErrNoRows return nil, orm.ErrNoRows
} }
qs := models.Orm.QueryTable(new(models.SystemTenant)).
Filter("status", 1).
Filter("delete_time__isnull", true)
// tenant_name 的业务含义是租户简称。使用 TRIM 兼容历史数据中字段值
// 前后存在空格的情况;参数通过 SetArgs 绑定,避免 SQL 注入。
tenant := &models.SystemTenant{} tenant := &models.SystemTenant{}
query := ` err := qs.Filter("tenant_name", loginName).One(tenant)
SELECT id, tenant_code, tenant_name, tenant_short_name, if err == nil {
contact_person, contact_phone, contact_email, address, return tenant, nil
worktime, status, remark, create_time, update_time, delete_time }
FROM yz_system_tenant
WHERE (TRIM(tenant_name) = TRIM(?) OR tenant = &models.SystemTenant{}
TRIM(tenant_short_name) = TRIM(?) OR err = models.Orm.QueryTable(new(models.SystemTenant)).
TRIM(tenant_code) = TRIM(?)) Filter("status", 1).
AND status <> 0 Filter("delete_time__isnull", true).
ORDER BY id ASC Filter("tenant_code", loginName).
LIMIT 1` One(tenant)
err := models.Orm.Raw(query).
SetArgs(loginName, loginName, loginName).
QueryRow(tenant)
if err != nil { if err != nil {
return nil, err return nil, err
} }
Binary file not shown.
Binary file not shown.