操作日志和访问日志

This commit is contained in:
2025-11-11 22:32:46 +08:00
parent efc079c0e5
commit 12a0ff8afc
17 changed files with 2300 additions and 4 deletions
+369
View File
@@ -0,0 +1,369 @@
package services
import (
"fmt"
"server/models"
"strings"
"time"
"github.com/beego/beego/v2/client/orm"
)
// GetModuleName 根据module代码获取模块中文名称
// 首先从菜单表查询,如果找不到则使用默认映射
func GetModuleName(module string) string {
if module == "" {
return ""
}
// 从菜单表中查询:优先匹配 permission,其次尝试匹配 path 的最后一段(如 /system/dict -> dict
o := orm.NewOrm()
var menuName string
// 先尝试通过 permission 精准匹配
err := o.Raw(
"SELECT name FROM yz_menus WHERE permission = ? AND delete_time IS NULL LIMIT 1",
module,
).QueryRow(&menuName)
if err == nil && menuName != "" {
return menuName
}
// 再尝试通过 path 末段匹配(例如 /system/dict -> dict)或 path 包含该段
err = o.Raw(
"SELECT name FROM yz_menus WHERE (path LIKE CONCAT('%/', ?, '/%') OR path LIKE CONCAT('%/', ?)) AND delete_time IS NULL LIMIT 1",
module, module,
).QueryRow(&menuName)
if err == nil && menuName != "" {
return menuName
}
// 如果菜单表中找不到,使用默认映射
defaultMap := map[string]string{
"auth": "认证",
"dict": "字典管理",
"user": "用户管理",
"role": "角色管理",
"permission": "权限管理",
"department": "部门管理",
"employee": "员工管理",
"position": "职位管理",
"tenant": "租户管理",
"system": "系统设置",
"oa": "OA",
"menu": "菜单管理",
"knowledge": "知识库",
}
if name, exists := defaultMap[module]; exists {
return name
}
return module
}
// GetModuleNames 批量获取模块名称,返回 module->name 映射
func GetModuleNames(modules []string) (map[string]string, error) {
result := make(map[string]string)
if len(modules) == 0 {
return result, nil
}
o := orm.NewOrm()
// 1. 尝试通过 permission 精准匹配
placeholders := make([]string, 0)
args := make([]interface{}, 0)
for _, m := range modules {
if m == "" {
continue
}
placeholders = append(placeholders, "?")
args = append(args, m)
}
if len(args) > 0 {
query := "SELECT permission, name FROM yz_menus WHERE permission IN (" + strings.Join(placeholders, ",") + ") AND delete_time IS NULL"
rows := make([]struct {
Permission string
Name string
}, 0)
_, err := o.Raw(query, args...).QueryRows(&rows)
if err == nil {
for _, r := range rows {
if r.Permission != "" {
result[r.Permission] = r.Name
}
}
}
}
// 2. 对于仍未匹配的模块,尝试通过 path 的末段进行模糊匹配
missing := make([]string, 0)
for _, m := range modules {
if m == "" {
continue
}
if _, ok := result[m]; !ok {
missing = append(missing, m)
}
}
if len(missing) > 0 {
// 使用 OR 组合 path LIKE '%/m' OR path LIKE '%/m/%'
conds := make([]string, 0)
args2 := make([]interface{}, 0)
for _, m := range missing {
conds = append(conds, "path LIKE ? OR path LIKE ?")
args2 = append(args2, "%/"+m, "%/"+m+"/%")
}
query2 := "SELECT path, name FROM yz_menus WHERE (" + strings.Join(conds, " OR ") + ") AND delete_time IS NULL"
rows2 := make([]struct {
Path string
Name string
}, 0)
_, err := o.Raw(query2, args2...).QueryRows(&rows2)
if err == nil {
for _, r := range rows2 {
// extract last segment from path
parts := strings.Split(strings.Trim(r.Path, "/"), "/")
if len(parts) > 0 {
key := parts[len(parts)-1]
if key != "" {
if _, exists := result[key]; !exists {
result[key] = r.Name
}
}
}
}
}
}
// 3. 对于仍然没有匹配的,使用默认映射
defaultMap := map[string]string{
"auth": "认证",
"dict": "字典管理",
"user": "用户管理",
"role": "角色管理",
"permission": "权限管理",
"department": "部门管理",
"employee": "员工管理",
"position": "职位管理",
"tenant": "租户管理",
"system": "系统设置",
"oa": "OA",
"menu": "菜单管理",
"knowledge": "知识库",
}
for _, m := range modules {
if m == "" {
continue
}
if _, exists := result[m]; !exists {
if v, ok := defaultMap[m]; ok {
result[m] = v
} else {
result[m] = m
}
}
}
return result, nil
}
// AddOperationLog 添加操作日志
func AddOperationLog(log *models.OperationLog) error {
o := orm.NewOrm()
log.CreateTime = time.Now()
_, err := o.Insert(log)
if err != nil {
return fmt.Errorf("添加操作日志失败: %v", err)
}
return nil
}
// AddAccessLog 添加访问日志(用于记录 GET / READ 行为)
func AddAccessLog(log *models.AccessLog) error {
o := orm.NewOrm()
log.CreateTime = time.Now()
_, err := o.Insert(log)
if err != nil {
return fmt.Errorf("添加访问日志失败: %v", err)
}
return nil
}
// GetOperationLogs 获取操作日志列表
func GetOperationLogs(tenantId int, userId int, module string, operation string, startTime *time.Time, endTime *time.Time, pageNum int, pageSize int) ([]*models.OperationLog, int64, error) {
o := orm.NewOrm()
qs := o.QueryTable("sys_operation_log").Filter("tenant_id", tenantId)
// 用户过滤
if userId > 0 {
qs = qs.Filter("user_id", userId)
}
// 模块过滤
if module != "" {
qs = qs.Filter("module", module)
}
// 操作类型过滤
if operation != "" {
qs = qs.Filter("operation", operation)
}
// 时间范围过滤
if startTime != nil {
qs = qs.Filter("create_time__gte", startTime)
}
if endTime != nil {
qs = qs.Filter("create_time__lte", endTime)
}
// 获取总数
total, err := qs.Count()
if err != nil {
return nil, 0, fmt.Errorf("查询日志总数失败: %v", err)
}
// 分页查询
var logs []*models.OperationLog
offset := (pageNum - 1) * pageSize
_, err = qs.OrderBy("-create_time").Offset(offset).Limit(pageSize).All(&logs)
if err != nil {
return nil, 0, fmt.Errorf("查询操作日志失败: %v", err)
}
return logs, total, nil
}
// GetOperationLogById 根据ID获取操作日志
func GetOperationLogById(id int64) (*models.OperationLog, error) {
o := orm.NewOrm()
log := &models.OperationLog{Id: id}
err := o.Read(log)
if err != nil {
return nil, fmt.Errorf("日志不存在: %v", err)
}
return log, nil
}
// GetUserOperationStats 获取用户操作统计
func GetUserOperationStats(tenantId int, userId int, days int) (map[string]interface{}, error) {
o := orm.NewOrm()
startTime := time.Now().AddDate(0, 0, -days)
// 获取总操作数
var totalCount int64
err := o.Raw(
"SELECT COUNT(*) FROM sys_operation_log WHERE tenant_id = ? AND user_id = ? AND create_time >= ?",
tenantId, userId, startTime,
).QueryRow(&totalCount)
if err != nil {
return nil, err
}
// 获取按模块分组的操作数
type ModuleCount struct {
Module string
Count int
}
var moduleCounts []ModuleCount
_, err = o.Raw(
"SELECT module, COUNT(*) as count FROM sys_operation_log WHERE tenant_id = ? AND user_id = ? AND create_time >= ? GROUP BY module",
tenantId, userId, startTime,
).QueryRows(&moduleCounts)
if err != nil {
return nil, err
}
// 构建结果
stats := map[string]interface{}{
"total_operations": totalCount,
"module_stats": moduleCounts,
"period_days": days,
"from_time": startTime,
"to_time": time.Now(),
}
return stats, nil
}
// GetTenantOperationStats 获取租户操作统计
func GetTenantOperationStats(tenantId int, days int) (map[string]interface{}, error) {
o := orm.NewOrm()
startTime := time.Now().AddDate(0, 0, -days)
// 获取总操作数
var totalCount int64
err := o.Raw(
"SELECT COUNT(*) FROM sys_operation_log WHERE tenant_id = ? AND create_time >= ?",
tenantId, startTime,
).QueryRow(&totalCount)
if err != nil {
return nil, err
}
// 获取按用户分组的操作数(Top 10)
type UserCount struct {
UserId int
Username string
Count int
}
var userCounts []UserCount
_, err = o.Raw(
"SELECT user_id, username, COUNT(*) as count FROM sys_operation_log WHERE tenant_id = ? AND create_time >= ? GROUP BY user_id, username ORDER BY count DESC LIMIT 10",
tenantId, startTime,
).QueryRows(&userCounts)
if err != nil {
return nil, err
}
// 获取按操作类型分组的操作数
type OperationCount struct {
Operation string
Count int
}
var operationCounts []OperationCount
_, err = o.Raw(
"SELECT operation, COUNT(*) as count FROM sys_operation_log WHERE tenant_id = ? AND create_time >= ? GROUP BY operation",
tenantId, startTime,
).QueryRows(&operationCounts)
if err != nil {
return nil, err
}
// 构建结果
stats := map[string]interface{}{
"total_operations": totalCount,
"top_users": userCounts,
"operation_breakdown": operationCounts,
"period_days": days,
"from_time": startTime,
"to_time": time.Now(),
}
return stats, nil
}
// DeleteOldLogs 删除旧日志(保留指定天数)
func DeleteOldLogs(keepDays int) (int64, error) {
o := orm.NewOrm()
cutoffTime := time.Now().AddDate(0, 0, -keepDays)
result, err := o.Raw("DELETE FROM sys_operation_log WHERE create_time < ?", cutoffTime).Exec()
if err != nil {
return 0, fmt.Errorf("删除旧日志失败: %v", err)
}
rowsAffected, err := result.RowsAffected()
if err != nil {
return 0, fmt.Errorf("获取受影响行数失败: %v", err)
}
return rowsAffected, nil
}