2026-05-30 07:23:33 +08:00
|
|
|
|
"""
|
|
|
|
|
|
文件管理路由模块
|
|
|
|
|
|
|
|
|
|
|
|
提供文件上传令牌生成和附件保存接口,支持:
|
|
|
|
|
|
- 生成云存储上传令牌(用于客户端直传)
|
|
|
|
|
|
- 保存文件附件元数据到数据库
|
|
|
|
|
|
|
|
|
|
|
|
URL 前缀:/api/files
|
|
|
|
|
|
权限要求:已登录用户(get_current_user)
|
|
|
|
|
|
"""
|
|
|
|
|
|
|
2026-06-01 11:24:20 +08:00
|
|
|
|
from datetime import datetime
|
|
|
|
|
|
from pathlib import Path
|
|
|
|
|
|
|
|
|
|
|
|
from fastapi import APIRouter, Depends, UploadFile
|
2026-05-15 11:10:44 +08:00
|
|
|
|
from sqlalchemy.orm import Session
|
2026-05-14 13:51:06 +08:00
|
|
|
|
|
2026-05-15 14:13:28 +08:00
|
|
|
|
from backend.app.api.deps import get_current_user
|
2026-05-15 11:10:44 +08:00
|
|
|
|
from backend.app.db import get_db_session
|
2026-05-14 13:51:06 +08:00
|
|
|
|
from backend.app.schemas.common import success_payload
|
2026-05-14 16:12:25 +08:00
|
|
|
|
from backend.app.schemas.files import CreateUploadTokenRequest, SaveAttachmentRequest
|
|
|
|
|
|
from backend.app.services.file_service import file_service
|
2026-05-14 13:51:06 +08:00
|
|
|
|
|
|
|
|
|
|
router = APIRouter(prefix="/api/files", tags=["files"])
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@router.post("/upload-token")
|
2026-05-15 11:10:44 +08:00
|
|
|
|
def create_upload_token(
|
2026-05-30 07:23:33 +08:00
|
|
|
|
payload: CreateUploadTokenRequest, # 上传令牌请求体,包含文件名、类型等信息
|
|
|
|
|
|
session: Session = Depends(get_db_session), # 数据库会话
|
|
|
|
|
|
current_user: dict = Depends(get_current_user), # 当前登录用户
|
2026-05-15 11:10:44 +08:00
|
|
|
|
) -> dict:
|
2026-05-30 07:23:33 +08:00
|
|
|
|
"""
|
|
|
|
|
|
生成文件上传令牌
|
|
|
|
|
|
|
|
|
|
|
|
为客户端生成云存储的上传凭证,客户端可使用该令牌直接上传文件到云存储。
|
|
|
|
|
|
|
|
|
|
|
|
请求参数:
|
|
|
|
|
|
payload: CreateUploadTokenRequest 请求体,包含文件名、MIME类型等信息
|
|
|
|
|
|
|
|
|
|
|
|
返回值:
|
|
|
|
|
|
包含上传令牌、上传地址等信息的字典
|
|
|
|
|
|
"""
|
2026-05-15 11:10:44 +08:00
|
|
|
|
return success_payload(file_service.create_upload_token(payload.model_dump(), session))
|
2026-05-14 13:51:06 +08:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@router.post("/attachments")
|
2026-05-15 11:10:44 +08:00
|
|
|
|
def save_attachment(
|
2026-05-30 07:23:33 +08:00
|
|
|
|
payload: SaveAttachmentRequest, # 保存附件请求体,包含文件URL、名称、大小等元数据
|
|
|
|
|
|
session: Session = Depends(get_db_session), # 数据库会话
|
|
|
|
|
|
current_user: dict = Depends(get_current_user), # 当前登录用户,用于记录创建人
|
2026-05-15 11:10:44 +08:00
|
|
|
|
) -> dict:
|
2026-05-30 07:23:33 +08:00
|
|
|
|
"""
|
|
|
|
|
|
保存文件附件元数据
|
|
|
|
|
|
|
|
|
|
|
|
文件上传至云存储后,调用此接口将附件的元数据(URL、文件名、大小等)保存到数据库,
|
|
|
|
|
|
并自动关联到当前登录用户作为创建人。
|
|
|
|
|
|
|
|
|
|
|
|
请求参数:
|
|
|
|
|
|
payload: SaveAttachmentRequest 请求体,包含文件元数据
|
|
|
|
|
|
|
|
|
|
|
|
返回值:
|
|
|
|
|
|
已保存的附件记录信息
|
|
|
|
|
|
|
|
|
|
|
|
权限要求:已登录用户
|
|
|
|
|
|
"""
|
2026-05-15 14:13:28 +08:00
|
|
|
|
return success_payload(file_service.save_attachment({**payload.model_dump(), "created_by": current_user.get("user_id")}, session))
|
2026-06-01 11:24:20 +08:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
LOCAL_UPLOAD_DIR = Path("D:/tmp/order-flow-oss/local-uploads")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@router.post("/local-upload")
|
|
|
|
|
|
async def local_upload(
|
|
|
|
|
|
file: UploadFile,
|
|
|
|
|
|
current_user: dict = Depends(get_current_user),
|
|
|
|
|
|
) -> dict:
|
|
|
|
|
|
"""开发环境本地文件上传,保存到本地目录并返回访问 URL。
|
|
|
|
|
|
|
|
|
|
|
|
绕过 OSS 直传,适用于本地开发调试场景。
|
|
|
|
|
|
"""
|
|
|
|
|
|
LOCAL_UPLOAD_DIR.mkdir(parents=True, exist_ok=True)
|
|
|
|
|
|
timestamp = datetime.now().strftime("%Y%m%d%H%M%S")
|
|
|
|
|
|
safe_name = Path(file.filename or "upload").name
|
|
|
|
|
|
file_path = LOCAL_UPLOAD_DIR / f"{timestamp}_{safe_name}"
|
|
|
|
|
|
content = await file.read()
|
|
|
|
|
|
file_path.write_bytes(content)
|
|
|
|
|
|
url = f"http://127.0.0.1:8000/uploads/{timestamp}_{safe_name}"
|
|
|
|
|
|
return success_payload({"url": url, "file_name": file.filename, "file_size": len(content)})
|