操作日志和访问日志
This commit is contained in:
@@ -4,6 +4,7 @@ import (
|
||||
"encoding/json"
|
||||
"server/models"
|
||||
"server/services"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
@@ -188,6 +189,24 @@ func (c *AuthController) Login() {
|
||||
_, _ = o.Raw("UPDATE yz_tenant_employees SET last_login_time = ?, last_login_ip = ? WHERE id = ?", loginTime, clientIP, employee.Id).Exec()
|
||||
}
|
||||
|
||||
// 记录登录操作
|
||||
loginLog := &models.OperationLog{
|
||||
TenantId: tenantId,
|
||||
UserId: userId,
|
||||
Username: usernameForToken,
|
||||
Module: "auth",
|
||||
ResourceType: "user",
|
||||
Operation: "LOGIN",
|
||||
IpAddress: clientIP,
|
||||
UserAgent: c.Ctx.Input.Header("User-Agent"),
|
||||
RequestMethod: "POST",
|
||||
RequestUrl: c.Ctx.Input.URL(),
|
||||
Status: 1,
|
||||
Duration: 0,
|
||||
CreateTime: loginTime,
|
||||
}
|
||||
_ = services.AddOperationLog(loginLog)
|
||||
|
||||
c.Data["json"] = map[string]interface{}{
|
||||
"code": 0,
|
||||
"message": "登录成功",
|
||||
@@ -205,6 +224,43 @@ func (c *AuthController) Login() {
|
||||
|
||||
// Logout 处理登出请求
|
||||
func (c *AuthController) Logout() {
|
||||
// 获取登出的用户信息
|
||||
userIdStr := c.GetString("user_id")
|
||||
username := c.GetString("username")
|
||||
tenantIdStr := c.GetString("tenant_id")
|
||||
|
||||
var userId, tenantId int
|
||||
if userIdStr != "" {
|
||||
id, err := strconv.Atoi(userIdStr)
|
||||
if err == nil {
|
||||
userId = id
|
||||
}
|
||||
}
|
||||
if tenantIdStr != "" {
|
||||
id, err := strconv.Atoi(tenantIdStr)
|
||||
if err == nil {
|
||||
tenantId = id
|
||||
}
|
||||
}
|
||||
|
||||
// 记录登出操作
|
||||
logoutLog := &models.OperationLog{
|
||||
TenantId: tenantId,
|
||||
UserId: userId,
|
||||
Username: username,
|
||||
Module: "auth",
|
||||
ResourceType: "user",
|
||||
Operation: "LOGOUT",
|
||||
IpAddress: c.Ctx.Input.IP(),
|
||||
UserAgent: c.Ctx.Input.Header("User-Agent"),
|
||||
RequestMethod: "POST",
|
||||
RequestUrl: c.Ctx.Input.URL(),
|
||||
Status: 1,
|
||||
Duration: 0,
|
||||
CreateTime: time.Now(),
|
||||
}
|
||||
_ = services.AddOperationLog(logoutLog)
|
||||
|
||||
// 在实际应用中,这里需要处理JWT或Session的清除
|
||||
c.Data["json"] = map[string]interface{}{
|
||||
"success": true,
|
||||
|
||||
+219
-1
@@ -5,6 +5,7 @@ import (
|
||||
"server/models"
|
||||
"server/services"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"github.com/beego/beego/v2/server/web"
|
||||
)
|
||||
@@ -160,6 +161,32 @@ func (c *DictController) AddDictType() {
|
||||
"data": map[string]interface{}{"id": id},
|
||||
}
|
||||
c.ServeJSON()
|
||||
|
||||
// 记录操作日志 - 创建字典类型
|
||||
go func() {
|
||||
// 异步写日志,避免阻塞请求响应
|
||||
rid := int(id)
|
||||
newVal, _ := json.Marshal(dictType)
|
||||
log := &models.OperationLog{
|
||||
TenantId: dictType.TenantId,
|
||||
UserId: userId,
|
||||
Username: username,
|
||||
Module: "dict",
|
||||
ResourceType: "dict_type",
|
||||
ResourceId: &rid,
|
||||
Operation: "CREATE",
|
||||
Description: "创建字典类型",
|
||||
NewValue: string(newVal),
|
||||
IpAddress: c.Ctx.Input.IP(),
|
||||
UserAgent: c.Ctx.Input.Header("User-Agent"),
|
||||
RequestMethod: c.Ctx.Input.Method(),
|
||||
RequestUrl: c.Ctx.Input.URL(),
|
||||
Status: 1,
|
||||
Duration: 0,
|
||||
CreateTime: time.Now(),
|
||||
}
|
||||
_ = services.AddOperationLog(log)
|
||||
}()
|
||||
}
|
||||
|
||||
// UpdateDictType 更新字典类型
|
||||
@@ -194,6 +221,15 @@ func (c *DictController) UpdateDictType() {
|
||||
}
|
||||
}
|
||||
|
||||
// 获取用户ID
|
||||
userIdData := c.Ctx.Input.GetData("userId")
|
||||
userId := 0
|
||||
if userIdData != nil {
|
||||
if uid, ok := userIdData.(int); ok {
|
||||
userId = uid
|
||||
}
|
||||
}
|
||||
|
||||
dictType.Id = id
|
||||
dictType.UpdateBy = username
|
||||
|
||||
@@ -212,6 +248,31 @@ func (c *DictController) UpdateDictType() {
|
||||
"message": "更新成功",
|
||||
}
|
||||
c.ServeJSON()
|
||||
|
||||
// 记录操作日志 - 更新字典类型
|
||||
go func() {
|
||||
newVal, _ := json.Marshal(dictType)
|
||||
// 旧值可以从服务层获取,如果需要更详细的旧值,可以在 UpdateDictType 前查询并传入
|
||||
log := &models.OperationLog{
|
||||
TenantId: dictType.TenantId,
|
||||
UserId: userId,
|
||||
Username: username,
|
||||
Module: "dict",
|
||||
ResourceType: "dict_type",
|
||||
ResourceId: &dictType.Id,
|
||||
Operation: "UPDATE",
|
||||
Description: "更新字典类型",
|
||||
NewValue: string(newVal),
|
||||
IpAddress: c.Ctx.Input.IP(),
|
||||
UserAgent: c.Ctx.Input.Header("User-Agent"),
|
||||
RequestMethod: c.Ctx.Input.Method(),
|
||||
RequestUrl: c.Ctx.Input.URL(),
|
||||
Status: 1,
|
||||
Duration: 0,
|
||||
CreateTime: time.Now(),
|
||||
}
|
||||
_ = services.AddOperationLog(log)
|
||||
}()
|
||||
}
|
||||
|
||||
// DeleteDictType 删除字典类型
|
||||
@@ -227,7 +288,7 @@ func (c *DictController) DeleteDictType() {
|
||||
return
|
||||
}
|
||||
|
||||
// 获取租户ID
|
||||
// 获取租户ID、用户名和用户ID 用于记录日志
|
||||
tenantIdData := c.Ctx.Input.GetData("tenantId")
|
||||
tenantId := 0
|
||||
if tenantIdData != nil {
|
||||
@@ -235,6 +296,20 @@ func (c *DictController) DeleteDictType() {
|
||||
tenantId = tid
|
||||
}
|
||||
}
|
||||
usernameData := c.Ctx.Input.GetData("username")
|
||||
username := ""
|
||||
if usernameData != nil {
|
||||
if u, ok := usernameData.(string); ok {
|
||||
username = u
|
||||
}
|
||||
}
|
||||
userIdData := c.Ctx.Input.GetData("userId")
|
||||
userId := 0
|
||||
if userIdData != nil {
|
||||
if uid, ok := userIdData.(int); ok {
|
||||
userId = uid
|
||||
}
|
||||
}
|
||||
|
||||
err = services.DeleteDictType(id, tenantId)
|
||||
if err != nil {
|
||||
@@ -251,6 +326,29 @@ func (c *DictController) DeleteDictType() {
|
||||
"message": "删除成功",
|
||||
}
|
||||
c.ServeJSON()
|
||||
|
||||
// 记录操作日志 - 删除字典类型
|
||||
go func() {
|
||||
rid := id
|
||||
log := &models.OperationLog{
|
||||
TenantId: tenantId,
|
||||
UserId: userId,
|
||||
Username: username,
|
||||
Module: "dict",
|
||||
ResourceType: "dict_type",
|
||||
ResourceId: &rid,
|
||||
Operation: "DELETE",
|
||||
Description: "删除字典类型",
|
||||
IpAddress: c.Ctx.Input.IP(),
|
||||
UserAgent: c.Ctx.Input.Header("User-Agent"),
|
||||
RequestMethod: c.Ctx.Input.Method(),
|
||||
RequestUrl: c.Ctx.Input.URL(),
|
||||
Status: 1,
|
||||
Duration: 0,
|
||||
CreateTime: time.Now(),
|
||||
}
|
||||
_ = services.AddOperationLog(log)
|
||||
}()
|
||||
}
|
||||
|
||||
// GetDictItems 获取字典项列表
|
||||
@@ -380,6 +478,46 @@ func (c *DictController) AddDictItem() {
|
||||
"data": map[string]interface{}{"id": id},
|
||||
}
|
||||
c.ServeJSON()
|
||||
|
||||
// 记录操作日志 - 创建字典项
|
||||
go func() {
|
||||
// 获取用户ID和租户ID
|
||||
userIdData := c.Ctx.Input.GetData("userId")
|
||||
userId := 0
|
||||
if userIdData != nil {
|
||||
if uid, ok := userIdData.(int); ok {
|
||||
userId = uid
|
||||
}
|
||||
}
|
||||
tenantIdData := c.Ctx.Input.GetData("tenantId")
|
||||
tenantId := 0
|
||||
if tenantIdData != nil {
|
||||
if tid, ok := tenantIdData.(int); ok {
|
||||
tenantId = tid
|
||||
}
|
||||
}
|
||||
rid := int(id)
|
||||
newVal, _ := json.Marshal(dictItem)
|
||||
log := &models.OperationLog{
|
||||
TenantId: tenantId,
|
||||
UserId: userId,
|
||||
Username: username,
|
||||
Module: "dict",
|
||||
ResourceType: "dict_item",
|
||||
ResourceId: &rid,
|
||||
Operation: "CREATE",
|
||||
Description: "创建字典项",
|
||||
NewValue: string(newVal),
|
||||
IpAddress: c.Ctx.Input.IP(),
|
||||
UserAgent: c.Ctx.Input.Header("User-Agent"),
|
||||
RequestMethod: c.Ctx.Input.Method(),
|
||||
RequestUrl: c.Ctx.Input.URL(),
|
||||
Status: 1,
|
||||
Duration: 0,
|
||||
CreateTime: time.Now(),
|
||||
}
|
||||
_ = services.AddOperationLog(log)
|
||||
}()
|
||||
}
|
||||
|
||||
// UpdateDictItem 更新字典项
|
||||
@@ -432,6 +570,41 @@ func (c *DictController) UpdateDictItem() {
|
||||
"message": "更新成功",
|
||||
}
|
||||
c.ServeJSON()
|
||||
|
||||
// 记录操作日志 - 更新字典项
|
||||
go func() {
|
||||
// 获取用户ID和租户ID
|
||||
userIdData := c.Ctx.Input.GetData("userId")
|
||||
userId := 0
|
||||
if userIdData != nil {
|
||||
if uid, ok := userIdData.(int); ok {
|
||||
userId = uid
|
||||
}
|
||||
}
|
||||
tenantId := 0
|
||||
// 通过字典项id尝试获取所属字典类型并推断租户(可选)
|
||||
// 这里先不查询,留 0 或者可从前端传入
|
||||
newVal, _ := json.Marshal(dictItem)
|
||||
log := &models.OperationLog{
|
||||
TenantId: tenantId,
|
||||
UserId: userId,
|
||||
Username: username,
|
||||
Module: "dict",
|
||||
ResourceType: "dict_item",
|
||||
ResourceId: &dictItem.Id,
|
||||
Operation: "UPDATE",
|
||||
Description: "更新字典项",
|
||||
NewValue: string(newVal),
|
||||
IpAddress: c.Ctx.Input.IP(),
|
||||
UserAgent: c.Ctx.Input.Header("User-Agent"),
|
||||
RequestMethod: c.Ctx.Input.Method(),
|
||||
RequestUrl: c.Ctx.Input.URL(),
|
||||
Status: 1,
|
||||
Duration: 0,
|
||||
CreateTime: time.Now(),
|
||||
}
|
||||
_ = services.AddOperationLog(log)
|
||||
}()
|
||||
}
|
||||
|
||||
// DeleteDictItem 删除字典项
|
||||
@@ -462,6 +635,51 @@ func (c *DictController) DeleteDictItem() {
|
||||
"message": "删除成功",
|
||||
}
|
||||
c.ServeJSON()
|
||||
|
||||
// 记录操作日志 - 删除字典项
|
||||
go func() {
|
||||
// 获取用户ID和租户ID
|
||||
userIdData := c.Ctx.Input.GetData("userId")
|
||||
userId := 0
|
||||
if userIdData != nil {
|
||||
if uid, ok := userIdData.(int); ok {
|
||||
userId = uid
|
||||
}
|
||||
}
|
||||
tenantIdData := c.Ctx.Input.GetData("tenantId")
|
||||
tenantId := 0
|
||||
if tenantIdData != nil {
|
||||
if tid, ok := tenantIdData.(int); ok {
|
||||
tenantId = tid
|
||||
}
|
||||
}
|
||||
usernameData := c.Ctx.Input.GetData("username")
|
||||
username := ""
|
||||
if usernameData != nil {
|
||||
if u, ok := usernameData.(string); ok {
|
||||
username = u
|
||||
}
|
||||
}
|
||||
rid := id
|
||||
log := &models.OperationLog{
|
||||
TenantId: tenantId,
|
||||
UserId: userId,
|
||||
Username: username,
|
||||
Module: "dict",
|
||||
ResourceType: "dict_item",
|
||||
ResourceId: &rid,
|
||||
Operation: "DELETE",
|
||||
Description: "删除字典项",
|
||||
IpAddress: c.Ctx.Input.IP(),
|
||||
UserAgent: c.Ctx.Input.Header("User-Agent"),
|
||||
RequestMethod: c.Ctx.Input.Method(),
|
||||
RequestUrl: c.Ctx.Input.URL(),
|
||||
Status: 1,
|
||||
Duration: 0,
|
||||
CreateTime: time.Now(),
|
||||
}
|
||||
_ = services.AddOperationLog(log)
|
||||
}()
|
||||
}
|
||||
|
||||
// GetDictItemsByCode 根据字典编码获取字典项(用于业务查询)
|
||||
|
||||
@@ -0,0 +1,247 @@
|
||||
package controllers
|
||||
|
||||
import (
|
||||
"server/models"
|
||||
"server/services"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"github.com/beego/beego/v2/server/web"
|
||||
)
|
||||
|
||||
// OperationLogController 操作日志控制器
|
||||
type OperationLogController struct {
|
||||
web.Controller
|
||||
}
|
||||
|
||||
// GetOperationLogs 获取操作日志列表
|
||||
func (c *OperationLogController) GetOperationLogs() {
|
||||
// 获取租户ID和用户ID
|
||||
tenantIdData := c.Ctx.Input.GetData("tenantId")
|
||||
tenantId := 0
|
||||
if tenantIdData != nil {
|
||||
if tid, ok := tenantIdData.(int); ok {
|
||||
tenantId = tid
|
||||
}
|
||||
}
|
||||
|
||||
userIdData := c.Ctx.Input.GetData("userId")
|
||||
userId := 0
|
||||
if userIdData != nil {
|
||||
if uid, ok := userIdData.(int); ok {
|
||||
userId = uid
|
||||
}
|
||||
}
|
||||
|
||||
// 获取查询参数
|
||||
pageNum, _ := c.GetInt("page_num", 1)
|
||||
pageSize, _ := c.GetInt("page_size", 20)
|
||||
module := c.GetString("module")
|
||||
operation := c.GetString("operation")
|
||||
filterUserId, _ := c.GetInt("user_id", 0)
|
||||
startTimeStr := c.GetString("start_time")
|
||||
endTimeStr := c.GetString("end_time")
|
||||
|
||||
var startTime, endTime *time.Time
|
||||
|
||||
if startTimeStr != "" {
|
||||
if t, err := time.Parse("2006-01-02 15:04:05", startTimeStr); err == nil {
|
||||
startTime = &t
|
||||
}
|
||||
}
|
||||
|
||||
if endTimeStr != "" {
|
||||
if t, err := time.Parse("2006-01-02 15:04:05", endTimeStr); err == nil {
|
||||
endTime = &t
|
||||
}
|
||||
}
|
||||
|
||||
// 如果指定了用户ID筛选,使用该ID,否则使用当前用户ID
|
||||
queryUserId := filterUserId
|
||||
if queryUserId <= 0 {
|
||||
queryUserId = userId
|
||||
}
|
||||
|
||||
logs, total, err := services.GetOperationLogs(tenantId, queryUserId, module, operation, startTime, endTime, pageNum, pageSize)
|
||||
if err != nil {
|
||||
c.Data["json"] = map[string]interface{}{
|
||||
"success": false,
|
||||
"message": "查询日志失败: " + err.Error(),
|
||||
}
|
||||
c.ServeJSON()
|
||||
return
|
||||
}
|
||||
|
||||
// 为每条日志批量解析模块名称(减少对菜单表的重复查询)
|
||||
type LogWithModuleName struct {
|
||||
*models.OperationLog
|
||||
ModuleName string `json:"module_name"`
|
||||
}
|
||||
|
||||
// 收集唯一 module 列表
|
||||
moduleSet := make(map[string]struct{})
|
||||
modules := make([]string, 0)
|
||||
for _, l := range logs {
|
||||
m := l.Module
|
||||
if m == "" {
|
||||
continue
|
||||
}
|
||||
if _, ok := moduleSet[m]; !ok {
|
||||
moduleSet[m] = struct{}{}
|
||||
modules = append(modules, m)
|
||||
}
|
||||
}
|
||||
|
||||
moduleNamesMap := map[string]string{}
|
||||
if len(modules) > 0 {
|
||||
if mm, err := services.GetModuleNames(modules); err == nil {
|
||||
moduleNamesMap = mm
|
||||
}
|
||||
}
|
||||
|
||||
logsWithName := make([]*LogWithModuleName, len(logs))
|
||||
for i, log := range logs {
|
||||
name := ""
|
||||
if v, ok := moduleNamesMap[log.Module]; ok {
|
||||
name = v
|
||||
} else {
|
||||
// fallback 单个解析(保留老逻辑)
|
||||
name = services.GetModuleName(log.Module)
|
||||
}
|
||||
|
||||
logsWithName[i] = &LogWithModuleName{
|
||||
OperationLog: log,
|
||||
ModuleName: name,
|
||||
}
|
||||
}
|
||||
|
||||
c.Data["json"] = map[string]interface{}{
|
||||
"success": true,
|
||||
"data": logsWithName,
|
||||
"total": total,
|
||||
"page": pageNum,
|
||||
"page_size": pageSize,
|
||||
}
|
||||
c.ServeJSON()
|
||||
}
|
||||
|
||||
// GetOperationLogById 根据ID获取操作日志
|
||||
func (c *OperationLogController) GetOperationLogById() {
|
||||
idStr := c.Ctx.Input.Param(":id")
|
||||
id, err := strconv.ParseInt(idStr, 10, 64)
|
||||
if err != nil || id <= 0 {
|
||||
c.Data["json"] = map[string]interface{}{
|
||||
"success": false,
|
||||
"message": "参数错误",
|
||||
}
|
||||
c.ServeJSON()
|
||||
return
|
||||
}
|
||||
|
||||
log, err := services.GetOperationLogById(id)
|
||||
if err != nil {
|
||||
c.Data["json"] = map[string]interface{}{
|
||||
"success": false,
|
||||
"message": "日志不存在",
|
||||
}
|
||||
c.ServeJSON()
|
||||
return
|
||||
}
|
||||
|
||||
c.Data["json"] = map[string]interface{}{
|
||||
"success": true,
|
||||
"data": log,
|
||||
}
|
||||
c.ServeJSON()
|
||||
}
|
||||
|
||||
// GetUserStats 获取用户操作统计
|
||||
func (c *OperationLogController) GetUserStats() {
|
||||
userIdData := c.Ctx.Input.GetData("userId")
|
||||
userId := 0
|
||||
if userIdData != nil {
|
||||
if uid, ok := userIdData.(int); ok {
|
||||
userId = uid
|
||||
}
|
||||
}
|
||||
|
||||
tenantIdData := c.Ctx.Input.GetData("tenantId")
|
||||
tenantId := 0
|
||||
if tenantIdData != nil {
|
||||
if tid, ok := tenantIdData.(int); ok {
|
||||
tenantId = tid
|
||||
}
|
||||
}
|
||||
|
||||
days, _ := c.GetInt("days", 7)
|
||||
|
||||
stats, err := services.GetUserOperationStats(tenantId, userId, days)
|
||||
if err != nil {
|
||||
c.Data["json"] = map[string]interface{}{
|
||||
"success": false,
|
||||
"message": "查询统计失败: " + err.Error(),
|
||||
}
|
||||
c.ServeJSON()
|
||||
return
|
||||
}
|
||||
|
||||
c.Data["json"] = map[string]interface{}{
|
||||
"success": true,
|
||||
"data": stats,
|
||||
}
|
||||
c.ServeJSON()
|
||||
}
|
||||
|
||||
// GetTenantStats 获取租户操作统计
|
||||
func (c *OperationLogController) GetTenantStats() {
|
||||
tenantIdData := c.Ctx.Input.GetData("tenantId")
|
||||
tenantId := 0
|
||||
if tenantIdData != nil {
|
||||
if tid, ok := tenantIdData.(int); ok {
|
||||
tenantId = tid
|
||||
}
|
||||
}
|
||||
|
||||
days, _ := c.GetInt("days", 7)
|
||||
|
||||
stats, err := services.GetTenantOperationStats(tenantId, days)
|
||||
if err != nil {
|
||||
c.Data["json"] = map[string]interface{}{
|
||||
"success": false,
|
||||
"message": "查询统计失败: " + err.Error(),
|
||||
}
|
||||
c.ServeJSON()
|
||||
return
|
||||
}
|
||||
|
||||
c.Data["json"] = map[string]interface{}{
|
||||
"success": true,
|
||||
"data": stats,
|
||||
}
|
||||
c.ServeJSON()
|
||||
}
|
||||
|
||||
// ClearOldLogs 清空旧日志
|
||||
func (c *OperationLogController) ClearOldLogs() {
|
||||
keepDays, _ := c.GetInt("keep_days", 90)
|
||||
|
||||
rowsAffected, err := services.DeleteOldLogs(keepDays)
|
||||
if err != nil {
|
||||
c.Data["json"] = map[string]interface{}{
|
||||
"success": false,
|
||||
"message": "删除日志失败: " + err.Error(),
|
||||
}
|
||||
c.ServeJSON()
|
||||
return
|
||||
}
|
||||
|
||||
c.Data["json"] = map[string]interface{}{
|
||||
"success": true,
|
||||
"message": "删除成功",
|
||||
"data": map[string]interface{}{
|
||||
"deleted_count": rowsAffected,
|
||||
"keep_days": keepDays,
|
||||
},
|
||||
}
|
||||
c.ServeJSON()
|
||||
}
|
||||
Reference in New Issue
Block a user