feat(错误日志): 添加排序和删除功能

为错误日志页面增加了按 ID 排序以及单条和批量删除日志的功能。

主要变更:

后端 (Python/FastAPI):
- `services.py`:
    - `get_error_logs`: 添加 `sort_by` 和 `sort_order` 参数以支持排序。
    - 新增 `delete_error_logs`: 实现基于 ID 列表的批量删除。
    - 新增 `delete_error_log_by_id`: 实现基于单个 ID 的删除。
- `error_log_routes.py`:
    - `GET /api/logs/errors`: 添加 `sortBy` 和 `sortOrder` 查询参数以支持前端排序请求。
    - 新增 `DELETE /api/logs/errors`: 处理批量删除请求。
    - 新增 `DELETE /api/logs/errors/{log_id}`: 处理单条删除请求。
- `connection.py`: 移除了不再使用的同步 SQLAlchemy Session 相关代码。

前端 (HTML/JavaScript):
- `error_logs.html`:
    - 调整了搜索/操作区域布局,添加了批量删除按钮。
    - ID 表头增加排序图标和点击事件。
    - 表格行操作列添加了删除按钮。
    - 新增了删除确认模态框。
- `error_logs.js`:
    - 添加了处理 ID 排序点击的逻辑,更新排序状态并重新加载数据。
    - 添加了处理单条和批量删除按钮点击的逻辑。
    - 实现了删除确认模态框的显示/隐藏及确认逻辑。
    - 修改 `loadErrorLogs` 以包含排序参数。
    - 修改 `renderErrorLogs` 以添加行删除按钮和必要的 `data-log-id` 属性。
    - 更新了全选/取消全选逻辑以同步批量删除按钮状态。
This commit is contained in:
snaily
2025-04-26 02:39:55 +08:00
parent cd54650431
commit cd257a9406
5 changed files with 442 additions and 55 deletions
+78 -5
View File
@@ -1,15 +1,22 @@
"""
日志路由模块
"""
from typing import List, Optional
from typing import List, Optional, Dict
from datetime import datetime
from pydantic import BaseModel
from fastapi import APIRouter, HTTPException, Request, Query, Path
from fastapi import APIRouter, HTTPException, Request, Query, Path, Body, Response, status
from app.core.security import verify_auth_token
from app.log.logger import get_log_routes_logger
# 假设这些服务函数已更新或添加
from app.database.services import get_error_logs, get_error_logs_count, get_error_log_details
from app.database.services import (
get_error_logs,
get_error_logs_count,
get_error_log_details,
delete_error_logs_by_ids, # 新增导入
delete_error_log_by_id # 新增导入
)
# Removed get_db import comment as it's fully removed now
# 创建路由
router = APIRouter(prefix="/api/logs", tags=["logs"])
@@ -40,10 +47,12 @@ async def get_error_logs_api(
error_search: Optional[str] = Query(None, description="Search term for error type or log message"), # 数据库查询需处理
error_code_search: Optional[str] = Query(None, description="Search term for error code"), # Added error code search parameter
start_date: Optional[datetime] = Query(None, description="Start datetime for filtering"),
end_date: Optional[datetime] = Query(None, description="End datetime for filtering")
end_date: Optional[datetime] = Query(None, description="End datetime for filtering"),
sort_by: str = Query('id', description="Field to sort by (e.g., 'id', 'request_time')"), # 新增排序参数
sort_order: str = Query('desc', description="Sort order ('asc' or 'desc')") # 新增排序参数
):
"""
获取错误日志列表 (返回错误码)
获取错误日志列表 (返回错误码),支持过滤和排序
Args:
request: 请求对象
@@ -54,6 +63,8 @@ async def get_error_logs_api(
error_code_search: 错误码搜索
start_date: 开始日期
end_date: 结束日期
sort_by: 排序字段
sort_order: 排序顺序
Returns:
ErrorLogListResponse: An object containing the list of logs (with error_code) and the total count.
@@ -75,6 +86,8 @@ async def get_error_logs_api(
error_code_search=error_code_search, # Pass error code search to DB function
start_date=start_date,
end_date=end_date,
sort_by=sort_by, # 传递排序参数
sort_order=sort_order # 传递排序参数
)
# Fetch total count with the same search parameters
total_count = await get_error_logs_count(
@@ -126,3 +139,63 @@ async def get_error_log_detail_api(request: Request, log_id: int = Path(..., ge=
except Exception as e:
logger.exception(f"Failed to get error log details for ID {log_id}: {str(e)}")
raise HTTPException(status_code=500, detail=f"Failed to get error log details: {str(e)}")
# 新增:批量删除错误日志
@router.delete("/errors", status_code=status.HTTP_204_NO_CONTENT)
async def delete_error_logs_bulk_api(
request: Request,
payload: Dict[str, List[int]] = Body(...) # Expects {"ids": [1, 2, 3]}
# Ensure db dependency is fully removed
):
"""
批量删除错误日志 (异步)
"""
auth_token = request.cookies.get("auth_token")
if not auth_token or not verify_auth_token(auth_token):
logger.warning("Unauthorized access attempt to bulk delete error logs")
raise HTTPException(status_code=401, detail="Not authenticated")
log_ids = payload.get("ids")
if not log_ids:
raise HTTPException(status_code=400, detail="No log IDs provided for deletion.")
try:
# 调用异步服务函数
deleted_count = await delete_error_logs_by_ids(log_ids)
# 注意:异步函数返回的是尝试删除的数量,可能不是精确值
logger.info(f"Attempted bulk deletion for {deleted_count} error logs with IDs: {log_ids}")
return Response(status_code=status.HTTP_204_NO_CONTENT)
except Exception as e:
logger.exception(f"Error bulk deleting error logs with IDs {log_ids}: {str(e)}")
raise HTTPException(status_code=500, detail="Internal server error during bulk deletion")
# 新增:删除单个错误日志
@router.delete("/errors/{log_id}", status_code=status.HTTP_204_NO_CONTENT)
async def delete_error_log_api(
request: Request,
log_id: int = Path(..., ge=1)
# Ensure db dependency is fully removed
):
"""
删除单个错误日志 (异步)
"""
auth_token = request.cookies.get("auth_token")
if not auth_token or not verify_auth_token(auth_token):
logger.warning(f"Unauthorized access attempt to delete error log ID: {log_id}")
raise HTTPException(status_code=401, detail="Not authenticated")
try:
# 调用异步服务函数
success = await delete_error_log_by_id(log_id)
if not success:
# 服务层现在在未找到时返回 False,我们在这里转换为 404
raise HTTPException(status_code=404, detail=f"Error log with ID {log_id} not found")
logger.info(f"Successfully deleted error log with ID: {log_id}")
return Response(status_code=status.HTTP_204_NO_CONTENT)
except HTTPException as http_exc:
raise http_exc # Re-raise 404 or other HTTP exceptions
except Exception as e:
logger.exception(f"Error deleting error log with ID {log_id}: {str(e)}")
raise HTTPException(status_code=500, detail="Internal server error during deletion")