优化页面、修复BUG
This commit is contained in:
9
server/repositories/access_log_filter.go
Normal file
9
server/repositories/access_log_filter.go
Normal file
@@ -0,0 +1,9 @@
|
||||
package repositories
|
||||
|
||||
// AccessLogFilter 访问日志筛选条件
|
||||
type AccessLogFilter struct {
|
||||
Path string
|
||||
Region string
|
||||
StartDate string
|
||||
EndDate string
|
||||
}
|
||||
89
server/repositories/code_type_repository.go
Normal file
89
server/repositories/code_type_repository.go
Normal file
@@ -0,0 +1,89 @@
|
||||
package repositories
|
||||
|
||||
import (
|
||||
"log"
|
||||
"time"
|
||||
|
||||
"github.com/niangaodev/art-code/config"
|
||||
"github.com/niangaodev/art-code/models"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
func GetCodeTypes() ([]models.CodeType, error) {
|
||||
var list []models.CodeType
|
||||
err := config.DB.Model(&models.CodeType{}).
|
||||
Where("deleted_at = ?", 0).
|
||||
Order("category ASC, name ASC").
|
||||
Find(&list).Error
|
||||
return list, err
|
||||
}
|
||||
|
||||
func GetCodeTypeByID(id uint) (*models.CodeType, error) {
|
||||
var ct models.CodeType
|
||||
err := config.DB.Model(&models.CodeType{}).
|
||||
Where("id = ? AND deleted_at = ?", id, 0).
|
||||
First(&ct).Error
|
||||
if err != nil {
|
||||
if err == gorm.ErrRecordNotFound {
|
||||
return nil, nil
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
return &ct, nil
|
||||
}
|
||||
|
||||
func CreateCodeType(ct *models.CodeType) error {
|
||||
return config.DB.Create(ct).Error
|
||||
}
|
||||
|
||||
func UpdateCodeType(ct *models.CodeType) error {
|
||||
return config.DB.Model(&models.CodeType{}).
|
||||
Where("id = ? AND deleted_at = ?", ct.ID, 0).
|
||||
Updates(map[string]interface{}{
|
||||
"name": ct.Name,
|
||||
"category": ct.Category,
|
||||
"updated_at": time.Now().Unix(),
|
||||
}).Error
|
||||
}
|
||||
|
||||
func DeleteCodeType(id uint) error {
|
||||
return config.DB.Model(&models.CodeType{}).
|
||||
Where("id = ?", id).
|
||||
Update("deleted_at", time.Now().Unix()).Error
|
||||
}
|
||||
|
||||
func CountSnippetsByCodeTypeID(id uint) (int64, error) {
|
||||
var count int64
|
||||
err := config.DB.Model(&models.Snippet{}).
|
||||
Where("code_type_id = ? AND deleted_at = ?", id, 0).
|
||||
Count(&count).Error
|
||||
return count, err
|
||||
}
|
||||
|
||||
func BuildCodeTypeResponse(ct *models.CodeType) models.CodeTypeResponse {
|
||||
return models.CodeTypeResponse{
|
||||
ID: ct.ID,
|
||||
Name: ct.Name,
|
||||
Category: ct.Category,
|
||||
}
|
||||
}
|
||||
|
||||
func BuildCodeTypesResponse(list []models.CodeType) []models.CodeTypeResponse {
|
||||
res := make([]models.CodeTypeResponse, 0, len(list))
|
||||
for _, ct := range list {
|
||||
res = append(res, BuildCodeTypeResponse(&ct))
|
||||
}
|
||||
return res
|
||||
}
|
||||
|
||||
func GetCodeTypeForSnippet(snippet *models.Snippet) *models.CodeType {
|
||||
if snippet.CodeTypeID == 0 {
|
||||
return nil
|
||||
}
|
||||
ct, err := GetCodeTypeByID(snippet.CodeTypeID)
|
||||
if err != nil {
|
||||
log.Printf("Error loading code type %d: %v", snippet.CodeTypeID, err)
|
||||
return nil
|
||||
}
|
||||
return ct
|
||||
}
|
||||
@@ -129,7 +129,7 @@ func GetDailyUV(startDate, endDate string) ([]UVTrendData, error) {
|
||||
}
|
||||
|
||||
// GetAccessLogs 获取访问日志列表(支持分页和筛选)
|
||||
func GetAccessLogs(page, pageSize int, postID *int) ([]models.AccessLog, int64, error) {
|
||||
func GetAccessLogs(page, pageSize int, postID *int, filter AccessLogFilter) ([]models.AccessLog, int64, error) {
|
||||
query := config.DB.Model(&models.AccessLog{}).
|
||||
Where("deleted_at = ?", 0)
|
||||
|
||||
@@ -168,6 +168,25 @@ func GetAccessLogs(page, pageSize int, postID *int) ([]models.AccessLog, int64,
|
||||
)
|
||||
}
|
||||
|
||||
if filter.Path != "" {
|
||||
query = query.Where("path LIKE ?", "%"+filter.Path+"%")
|
||||
}
|
||||
if filter.Region != "" {
|
||||
query = query.Where("region LIKE ?", "%"+filter.Region+"%")
|
||||
}
|
||||
if filter.StartDate != "" {
|
||||
startUnix := parseDateToUnix(filter.StartDate, false)
|
||||
if startUnix > 0 {
|
||||
query = query.Where("created_at >= ?", startUnix)
|
||||
}
|
||||
}
|
||||
if filter.EndDate != "" {
|
||||
endUnix := parseDateToUnix(filter.EndDate, true)
|
||||
if endUnix > 0 {
|
||||
query = query.Where("created_at <= ?", endUnix)
|
||||
}
|
||||
}
|
||||
|
||||
var total int64
|
||||
if err := query.Count(&total).Error; err != nil {
|
||||
return nil, 0, err
|
||||
@@ -186,7 +205,7 @@ func GetAccessLogs(page, pageSize int, postID *int) ([]models.AccessLog, int64,
|
||||
// GetPostAccessLogsFromAccessLogs 从 access_logs 表获取指定文章的访问记录(已废弃,应使用 user_access_logs)
|
||||
// 保留此函数以保持向后兼容,但建议使用 user_access_log_repository.GetPostAccessLogs
|
||||
func GetPostAccessLogsFromAccessLogs(postID int, page, pageSize int) ([]models.AccessLog, int64, error) {
|
||||
return GetAccessLogs(page, pageSize, &postID)
|
||||
return GetAccessLogs(page, pageSize, &postID, AccessLogFilter{})
|
||||
}
|
||||
|
||||
// ExtractProvinceFromRegion 从归属地字符串中提取省份信息
|
||||
|
||||
@@ -14,7 +14,7 @@ func MigrateToBigInt() {
|
||||
"about_profiles", "partners", "testimonials", "inquiries",
|
||||
"email_suffixes", "access_logs", "user_access_logs",
|
||||
"operation_logs", "permissions", "roles",
|
||||
"work_tech_stack", "work_gallery", "post_tags", "post_history",
|
||||
"work_tech_stack", "work_gallery", "post_tags", "post_history", "code_types",
|
||||
}
|
||||
|
||||
for _, table := range tables {
|
||||
|
||||
11
server/repositories/operation_log_filter.go
Normal file
11
server/repositories/operation_log_filter.go
Normal file
@@ -0,0 +1,11 @@
|
||||
package repositories
|
||||
|
||||
// OperationLogFilter 操作日志筛选条件
|
||||
type OperationLogFilter struct {
|
||||
Action string
|
||||
Method string
|
||||
Status int // 0 表示不筛选
|
||||
UserID uint
|
||||
StartDate string
|
||||
EndDate string
|
||||
}
|
||||
@@ -20,25 +20,45 @@ func CreateOperationLog(operationLog *models.OperationLog) error {
|
||||
}
|
||||
|
||||
// GetOperationLogs 获取操作日志列表
|
||||
func GetOperationLogs(page, pageSize int) ([]models.OperationLog, int64, error) {
|
||||
func GetOperationLogs(page, pageSize int, filter OperationLogFilter) ([]models.OperationLog, int64, error) {
|
||||
offset := (page - 1) * pageSize
|
||||
|
||||
var logs []models.OperationLog
|
||||
var total int64
|
||||
query := config.DB.Model(&models.OperationLog{}).
|
||||
Where("deleted_at = ?", 0)
|
||||
|
||||
// 获取总记录数
|
||||
err := config.DB.Model(&models.OperationLog{}).
|
||||
Where("deleted_at = ?", 0).
|
||||
Count(&total).Error
|
||||
if err != nil {
|
||||
if filter.Action != "" {
|
||||
query = query.Where("action LIKE ?", "%"+filter.Action+"%")
|
||||
}
|
||||
if filter.Method != "" {
|
||||
query = query.Where("method = ?", filter.Method)
|
||||
}
|
||||
if filter.Status > 0 {
|
||||
query = query.Where("status = ?", filter.Status)
|
||||
}
|
||||
if filter.UserID > 0 {
|
||||
query = query.Where("user_id = ?", filter.UserID)
|
||||
}
|
||||
if filter.StartDate != "" {
|
||||
startUnix := parseDateToUnix(filter.StartDate, false)
|
||||
if startUnix > 0 {
|
||||
query = query.Where("created_at >= ?", startUnix)
|
||||
}
|
||||
}
|
||||
if filter.EndDate != "" {
|
||||
endUnix := parseDateToUnix(filter.EndDate, true)
|
||||
if endUnix > 0 {
|
||||
query = query.Where("created_at <= ?", endUnix)
|
||||
}
|
||||
}
|
||||
|
||||
var total int64
|
||||
if err := query.Count(&total).Error; err != nil {
|
||||
log.Printf("Error counting operation logs: %v", err)
|
||||
return nil, 0, err
|
||||
}
|
||||
|
||||
// 获取分页数据
|
||||
err = config.DB.Model(&models.OperationLog{}).
|
||||
Where("deleted_at = ?", 0).
|
||||
Order("created_at DESC").
|
||||
var logs []models.OperationLog
|
||||
err := query.Order("created_at DESC").
|
||||
Limit(pageSize).
|
||||
Offset(offset).
|
||||
Find(&logs).Error
|
||||
@@ -52,8 +72,10 @@ func GetOperationLogs(page, pageSize int) ([]models.OperationLog, int64, error)
|
||||
|
||||
// BuildOperationLogResponse 构建操作日志响应
|
||||
func BuildOperationLogResponse(log *models.OperationLog) *models.OperationLogResponse {
|
||||
// 生成操作描述
|
||||
action := utils.GetOperationAction(log.Path, log.Method)
|
||||
action := log.Action
|
||||
if action == "" {
|
||||
action = utils.GetOperationAction(log.Path, log.Method)
|
||||
}
|
||||
|
||||
return &models.OperationLogResponse{
|
||||
ID: log.ID,
|
||||
|
||||
@@ -5,6 +5,7 @@ import (
|
||||
"fmt"
|
||||
"log"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/niangaodev/art-code/config"
|
||||
@@ -558,18 +559,22 @@ func GetPostHistoryByVersion(postID uint, version uint) (*models.PostHistory, er
|
||||
// BuildPostHistoryResponse 构建历史记录响应
|
||||
func BuildPostHistoryResponse(h *models.PostHistory, includeFull bool) *models.PostHistoryResponse {
|
||||
resp := &models.PostHistoryResponse{
|
||||
ID: h.ID,
|
||||
PostID: h.PostID,
|
||||
Version: h.Version,
|
||||
Title: h.Title,
|
||||
CategoryID: h.CategoryID,
|
||||
ColumnID: h.ColumnID,
|
||||
TagIDs: h.GetTagIDList(),
|
||||
Date: time.Unix(h.CreatedAt, 0).Format("2006-01-02"),
|
||||
IsPublished: h.IsPublished,
|
||||
ModifiedBy: h.ModifiedBy,
|
||||
ModifiedAt: time.Unix(h.ModifiedAt, 0).Format("2006-01-02 15:04:05"),
|
||||
CreatedAt: time.Unix(h.CreatedAt, 0).Format("2006-01-02 15:04:05"),
|
||||
ID: h.ID,
|
||||
PostID: h.PostID,
|
||||
Version: h.Version,
|
||||
Title: h.Title,
|
||||
CategoryID: h.CategoryID,
|
||||
CategoryName: lookupCategoryName(h.CategoryID),
|
||||
ColumnID: h.ColumnID,
|
||||
ColumnName: lookupColumnName(h.ColumnID),
|
||||
TagIDs: h.GetTagIDList(),
|
||||
TagNames: lookupTagNames(h.GetTagIDList()),
|
||||
Date: time.Unix(h.CreatedAt, 0).Format("2006-01-02"),
|
||||
IsPublished: h.IsPublished,
|
||||
ModifiedBy: h.ModifiedBy,
|
||||
ModifiedByName: lookupUsername(h.ModifiedBy),
|
||||
ModifiedAt: time.Unix(h.ModifiedAt, 0).Format("2006-01-02 15:04:05"),
|
||||
CreatedAt: time.Unix(h.CreatedAt, 0).Format("2006-01-02 15:04:05"),
|
||||
}
|
||||
|
||||
if includeFull {
|
||||
@@ -580,6 +585,53 @@ func BuildPostHistoryResponse(h *models.PostHistory, includeFull bool) *models.P
|
||||
return resp
|
||||
}
|
||||
|
||||
func lookupCategoryName(id uint) string {
|
||||
if id == 0 {
|
||||
return ""
|
||||
}
|
||||
var name string
|
||||
config.DB.Model(&models.Category{}).Where("id = ?", id).Pluck("name", &name)
|
||||
return name
|
||||
}
|
||||
|
||||
func lookupColumnName(id *uint) string {
|
||||
if id == nil || *id == 0 {
|
||||
return ""
|
||||
}
|
||||
var name string
|
||||
config.DB.Model(&models.Column{}).Where("id = ?", *id).Pluck("name", &name)
|
||||
return name
|
||||
}
|
||||
|
||||
func lookupTagNames(ids []uint) []string {
|
||||
if len(ids) == 0 {
|
||||
return nil
|
||||
}
|
||||
var tags []models.Tag
|
||||
config.DB.Model(&models.Tag{}).Where("id IN ?", ids).Find(&tags)
|
||||
names := make([]string, 0, len(tags))
|
||||
for _, t := range tags {
|
||||
names = append(names, t.Name)
|
||||
}
|
||||
return names
|
||||
}
|
||||
|
||||
func lookupUsername(id uint) string {
|
||||
if id == 0 {
|
||||
return ""
|
||||
}
|
||||
var username string
|
||||
config.DB.Model(&models.User{}).Where("id = ?", id).Pluck("username", &username)
|
||||
return username
|
||||
}
|
||||
|
||||
func formatPublishedLabel(v int) string {
|
||||
if v == 1 {
|
||||
return "已发布"
|
||||
}
|
||||
return "草稿"
|
||||
}
|
||||
|
||||
// BuildPostHistoryResponses 构建历史记录列表响应
|
||||
func BuildPostHistoryResponses(history []models.PostHistory) []models.PostHistoryResponse {
|
||||
var responses []models.PostHistoryResponse
|
||||
@@ -621,17 +673,14 @@ func GetPostHistoryDiff(postID uint, fromVersion, toVersion uint) (*models.PostH
|
||||
return nil, fmt.Errorf("to version not found")
|
||||
}
|
||||
|
||||
fromTags, _ := json.Marshal(fromHistory.GetTagIDList())
|
||||
toTags, _ := json.Marshal(toHistory.GetTagIDList())
|
||||
|
||||
fields := map[string]models.PostHistoryFieldDiff{
|
||||
"title": buildFieldDiff(fromHistory.Title, toHistory.Title, false),
|
||||
"excerpt": buildFieldDiff(fromHistory.Excerpt, toHistory.Excerpt, false),
|
||||
"content": buildFieldDiff(fromHistory.Content, toHistory.Content, true),
|
||||
"categoryId": buildFieldDiff(strconv.FormatUint(uint64(fromHistory.CategoryID), 10), strconv.FormatUint(uint64(toHistory.CategoryID), 10), false),
|
||||
"columnId": buildFieldDiff(formatOptionalUint(fromHistory.ColumnID), formatOptionalUint(toHistory.ColumnID), false),
|
||||
"tagIds": buildFieldDiff(string(fromTags), string(toTags), false),
|
||||
"isPublished": buildFieldDiff(strconv.Itoa(fromHistory.IsPublished), strconv.Itoa(toHistory.IsPublished), false),
|
||||
"category": buildFieldDiff(lookupCategoryName(fromHistory.CategoryID), lookupCategoryName(toHistory.CategoryID), false),
|
||||
"column": buildFieldDiff(lookupColumnName(fromHistory.ColumnID), lookupColumnName(toHistory.ColumnID), false),
|
||||
"tags": buildFieldDiff(strings.Join(lookupTagNames(fromHistory.GetTagIDList()), ", "), strings.Join(lookupTagNames(toHistory.GetTagIDList()), ", "), false),
|
||||
"isPublished": buildFieldDiff(formatPublishedLabel(fromHistory.IsPublished), formatPublishedLabel(toHistory.IsPublished), false),
|
||||
}
|
||||
|
||||
return &models.PostHistoryDiffResponse{
|
||||
|
||||
@@ -47,12 +47,23 @@ func GetSnippetByID(id string) (*models.Snippet, error) {
|
||||
|
||||
// BuildSnippetResponse 构建代码片段响应
|
||||
func BuildSnippetResponse(snippet *models.Snippet) *models.SnippetResponse {
|
||||
return &models.SnippetResponse{
|
||||
ID: snippet.ID,
|
||||
Title: snippet.Title,
|
||||
Code: snippet.Code,
|
||||
Type: snippet.Type,
|
||||
resp := &models.SnippetResponse{
|
||||
ID: snippet.ID,
|
||||
Title: snippet.Title,
|
||||
Code: snippet.Code,
|
||||
Type: snippet.Type,
|
||||
CodeTypeID: snippet.CodeTypeID,
|
||||
Description: snippet.Description,
|
||||
ViewCount: snippet.ViewCount,
|
||||
}
|
||||
if ct := GetCodeTypeForSnippet(snippet); ct != nil {
|
||||
r := BuildCodeTypeResponse(ct)
|
||||
resp.CodeType = &r
|
||||
if resp.Type == "" {
|
||||
resp.Type = ct.Name
|
||||
}
|
||||
}
|
||||
return resp
|
||||
}
|
||||
|
||||
// BuildSnippetsResponse 构建代码片段列表响应
|
||||
@@ -79,11 +90,12 @@ func UpdateSnippet(snippet *models.Snippet) error {
|
||||
err := config.DB.Model(&models.Snippet{}).
|
||||
Where("id = ? AND deleted_at = ?", snippet.ID, 0).
|
||||
Updates(map[string]interface{}{
|
||||
"title": snippet.Title,
|
||||
"code": snippet.Code,
|
||||
"type": snippet.Type,
|
||||
"description": snippet.Description,
|
||||
"updated_at": time.Now().Unix(),
|
||||
"title": snippet.Title,
|
||||
"code": snippet.Code,
|
||||
"type": snippet.Type,
|
||||
"code_type_id": snippet.CodeTypeID,
|
||||
"description": snippet.Description,
|
||||
"updated_at": time.Now().Unix(),
|
||||
}).Error
|
||||
if err != nil {
|
||||
log.Printf("Error updating snippet: %v", err)
|
||||
|
||||
@@ -49,27 +49,28 @@ func GetUserByID(id uint) (*models.User, error) {
|
||||
return &user, nil
|
||||
}
|
||||
|
||||
// GetUsers 获取所有用户 (分页)
|
||||
func GetUsers(page, pageSize int) ([]models.User, int, error) {
|
||||
// GetUsers 获取所有用户 (分页,支持 keyword 搜索)
|
||||
func GetUsers(page, pageSize int, keyword string) ([]models.User, int, error) {
|
||||
offset := (page - 1) * pageSize
|
||||
|
||||
var users []models.User
|
||||
var total int64
|
||||
query := config.DB.Model(&models.User{}).
|
||||
Where("users.deleted_at = ?", 0)
|
||||
|
||||
// 获取总数
|
||||
err := config.DB.Model(&models.User{}).
|
||||
Where("deleted_at = ?", 0).
|
||||
Count(&total).Error
|
||||
if err != nil {
|
||||
if keyword != "" {
|
||||
like := "%" + keyword + "%"
|
||||
query = query.Where("users.username LIKE ? OR users.email LIKE ?", like, like)
|
||||
}
|
||||
|
||||
var total int64
|
||||
if err := query.Count(&total).Error; err != nil {
|
||||
log.Printf("Error getting user count: %v", err)
|
||||
return nil, 0, err
|
||||
}
|
||||
|
||||
// 获取用户列表
|
||||
err = config.DB.Model(&models.User{}).
|
||||
var users []models.User
|
||||
err := query.
|
||||
Select("users.*, COALESCE(roles.name, users.role) as role").
|
||||
Joins("LEFT JOIN roles ON users.role_id = roles.id").
|
||||
Where("users.deleted_at = ?", 0).
|
||||
Order("users.created_at DESC").
|
||||
Limit(pageSize).
|
||||
Offset(offset).
|
||||
|
||||
Reference in New Issue
Block a user