yunzer_go/server/services/operation_log.go

506 lines
13 KiB
Go
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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
}
// GetAccessLogs 获取访问日志列表
func GetAccessLogs(tenantId int, userId int, module string, resourceType string, startTime *time.Time, endTime *time.Time, pageNum int, pageSize int) ([]*models.AccessLog, int64, error) {
o := orm.NewOrm()
qs := o.QueryTable("sys_access_log")
// 租户过滤
if tenantId > 0 {
qs = qs.Filter("tenant_id", tenantId)
}
// 用户过滤
if userId > 0 {
qs = qs.Filter("user_id", userId)
}
// 模块过滤
if module != "" {
qs = qs.Filter("module", module)
}
// 资源类型过滤
if resourceType != "" {
qs = qs.Filter("resource_type", resourceType)
}
// 时间范围过滤
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.AccessLog
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
}
// GetAccessLogById 根据ID获取访问日志
func GetAccessLogById(id int64) (*models.AccessLog, error) {
o := orm.NewOrm()
log := &models.AccessLog{Id: id}
err := o.Read(log)
if err != nil {
return nil, fmt.Errorf("日志不存在: %v", err)
}
return log, nil
}
// GetUserAccessStats 获取用户访问统计
func GetUserAccessStats(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_access_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_access_log WHERE tenant_id = ? AND user_id = ? AND create_time >= ? GROUP BY module",
tenantId, userId, startTime,
).QueryRows(&moduleCounts)
if err != nil {
return nil, err
}
// 获取按状态分组的访问数
type StatusCount struct {
Status int
Count int
}
var statusCounts []StatusCount
_, err = o.Raw(
"SELECT status, COUNT(*) as count FROM sys_access_log WHERE tenant_id = ? AND user_id = ? AND create_time >= ? GROUP BY status",
tenantId, userId, startTime,
).QueryRows(&statusCounts)
if err != nil {
return nil, err
}
// 构建结果
stats := map[string]interface{}{
"total_access": totalCount,
"module_stats": moduleCounts,
"status_stats": statusCounts,
"period_days": days,
"from_time": startTime,
"to_time": time.Now(),
}
return stats, nil
}
// ClearOldAccessLogs 清除旧访问日志(保留指定天数)
func ClearOldAccessLogs(keepDays int) (int64, error) {
o := orm.NewOrm()
cutoffTime := time.Now().AddDate(0, 0, -keepDays)
result, err := o.Raw("DELETE FROM sys_access_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
}