优化页面、修复BUG

This commit is contained in:
李琦
2026-06-24 17:06:22 +08:00
parent 3f653bc336
commit d5ff40e4d3
38 changed files with 1507 additions and 354 deletions

View File

@@ -0,0 +1,9 @@
package repositories
// AccessLogFilter 访问日志筛选条件
type AccessLogFilter struct {
Path string
Region string
StartDate string
EndDate string
}

View 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
}

View File

@@ -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 从归属地字符串中提取省份信息

View File

@@ -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 {

View File

@@ -0,0 +1,11 @@
package repositories
// OperationLogFilter 操作日志筛选条件
type OperationLogFilter struct {
Action string
Method string
Status int // 0 表示不筛选
UserID uint
StartDate string
EndDate string
}

View File

@@ -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,

View File

@@ -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{

View File

@@ -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)

View File

@@ -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).