feat(backend): 完善日志系统,支持按日期分目录存储
- 实现 logs/YYYY-MM-DD/ 日期文件夹结构 - 添加 success.log 和 failure.log 专用日志 - 使用 TimedRotatingFileHandler 实现按天切割 - 添加 log_success 和 log_failure 便捷函数 - 集成 markitdown 进行文件转换 - 优化文件存储路径,按项目ID分类存储 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -5,7 +5,7 @@ import os
|
||||
import asyncio
|
||||
from pathlib import Path
|
||||
from typing import Optional
|
||||
from uuid import UUID
|
||||
from uuid import UUID, uuid4
|
||||
from fastapi import APIRouter, Depends, UploadFile, File, Query
|
||||
from fastapi.responses import FileResponse
|
||||
from sqlalchemy.ext.asyncio import AsyncSession
|
||||
@@ -15,20 +15,31 @@ from app.core.config import get_settings
|
||||
from app.core.database import get_db
|
||||
from app.core.exceptions import ValidationException, NotFoundException
|
||||
from app.core.crud import CRUDBase
|
||||
from app.core.logging import log_success, log_failure
|
||||
from app.models.models import File as FileModel
|
||||
from app.schemas.file import FileResponse, FileCreateSchema
|
||||
|
||||
settings = get_settings()
|
||||
router = APIRouter()
|
||||
|
||||
# Ensure upload directory exists
|
||||
UPLOAD_DIR = Path(settings.UPLOAD_DIR)
|
||||
UPLOAD_DIR.mkdir(parents=True, exist_ok=True)
|
||||
|
||||
# Initialize CRUD
|
||||
file_crud = CRUDBase(FileModel)
|
||||
|
||||
|
||||
def get_project_raw_dir(project_id: str) -> Path:
|
||||
"""获取项目的 raw 文件目录"""
|
||||
base_dir = Path("/data/code/YG-Datasets/data") / project_id / "raw"
|
||||
base_dir.mkdir(parents=True, exist_ok=True)
|
||||
return base_dir
|
||||
|
||||
|
||||
def get_project_ready_dir(project_id: str) -> Path:
|
||||
"""获取项目的 ready 文件目录(处理后的文件)"""
|
||||
base_dir = Path("/data/code/YG-Datasets/data") / project_id / "ready"
|
||||
base_dir.mkdir(parents=True, exist_ok=True)
|
||||
return base_dir
|
||||
|
||||
|
||||
def get_file_type(filename: str) -> str:
|
||||
"""Get file type from extension"""
|
||||
ext = filename.rsplit('.', 1)[-1].lower() if '.' in filename else ''
|
||||
@@ -82,40 +93,62 @@ async def upload_file(
|
||||
db: AsyncSession = Depends(get_db)
|
||||
):
|
||||
"""Upload a file"""
|
||||
# Read file content for validation
|
||||
content = await file.read()
|
||||
file_size = len(content)
|
||||
try:
|
||||
# Read file content for validation
|
||||
content = await file.read()
|
||||
file_size = len(content)
|
||||
|
||||
# Validate file
|
||||
validate_file(file.filename, file_size)
|
||||
# Validate file
|
||||
validate_file(file.filename, file_size)
|
||||
|
||||
# Save file to disk
|
||||
safe_filename = f"{project_id}_{UUID.uuid4().hex[:8]}_{file.filename}"
|
||||
file_path = UPLOAD_DIR / safe_filename
|
||||
# Save file to disk - 使用项目 raw 目录
|
||||
safe_filename = f"{uuid4().hex[:8]}_{file.filename}"
|
||||
project_dir = get_project_raw_dir(str(project_id))
|
||||
file_path = project_dir / safe_filename
|
||||
|
||||
# Write file asynchronously
|
||||
await asyncio.get_event_loop().run_in_executor(
|
||||
None,
|
||||
lambda: file_path.write_bytes(content)
|
||||
)
|
||||
# Write file asynchronously
|
||||
await asyncio.get_event_loop().run_in_executor(
|
||||
None,
|
||||
lambda: file_path.write_bytes(content)
|
||||
)
|
||||
|
||||
# Create file record
|
||||
db_file = FileModel(
|
||||
project_id=project_id,
|
||||
filename=file.filename,
|
||||
file_type=get_file_type(file.filename),
|
||||
file_path=str(file_path),
|
||||
size=file_size,
|
||||
status="pending"
|
||||
)
|
||||
db.add(db_file)
|
||||
await db.commit()
|
||||
await db.refresh(db_file)
|
||||
# Create file record
|
||||
db_file = FileModel(
|
||||
project_id=project_id,
|
||||
filename=file.filename,
|
||||
file_type=get_file_type(file.filename),
|
||||
file_path=str(file_path),
|
||||
size=file_size,
|
||||
status="pending"
|
||||
)
|
||||
db.add(db_file)
|
||||
await db.commit()
|
||||
await db.refresh(db_file)
|
||||
|
||||
return ApiResponse.ok(
|
||||
data={"id": str(db_file.id), "filename": db_file.filename, "status": db_file.status},
|
||||
message="File uploaded successfully"
|
||||
)
|
||||
# 记录成功日志
|
||||
log_success(
|
||||
"文件上传成功",
|
||||
project_id=str(project_id),
|
||||
file_id=str(db_file.id),
|
||||
filename=file.filename,
|
||||
file_type=db_file.file_type,
|
||||
file_size=file_size,
|
||||
file_path=str(file_path)
|
||||
)
|
||||
|
||||
return ApiResponse.ok(
|
||||
data={"id": str(db_file.id), "filename": db_file.filename, "status": db_file.status},
|
||||
message="File uploaded successfully"
|
||||
)
|
||||
except Exception as e:
|
||||
# 记录失败日志
|
||||
log_failure(
|
||||
"文件上传失败",
|
||||
project_id=str(project_id),
|
||||
filename=file.filename if 'file' in locals() else "unknown",
|
||||
error=str(e)
|
||||
)
|
||||
raise
|
||||
|
||||
|
||||
@router.get("", response_model=ApiResponse)
|
||||
|
||||
Reference in New Issue
Block a user