init: 初始化 dpb 桃育种系统代码库

前后端 + 后端 FastAPI 全量源码、部署脚本与文档。
This commit is contained in:
34047007@qq.com
2026-08-06 00:17:49 +08:00
commit b95053c52c
1469 changed files with 322298 additions and 0 deletions
@@ -0,0 +1 @@
@@ -0,0 +1,48 @@
from pathlib import Path
from typing import Annotated, Literal
from fastapi import APIRouter, BackgroundTasks, Body, Depends, File, Form, Query, Request, Security, UploadFile
from fastapi.responses import FileResponse, JSONResponse
from app.common.response import ResponseSchema, SuccessResponse, UploadFileResponse
from app.core.base_schema import AuthSchema, UploadResponseSchema
from app.core.dependencies import AuthPermission, get_current_user
from app.core.router_class import OperationLogRoute
from app.utils.upload_util import UploadUtil
from .service import FileService
FileRouter = APIRouter(route_class=OperationLogRoute, prefix="/file", tags=["文件管理"])
@FileRouter.post("/upload", summary="上传文件", response_model=ResponseSchema[UploadResponseSchema], dependencies=[Security(AuthPermission(["module_common:file:upload"]))])
async def upload_controller(
request: Request,
auth: Annotated[AuthSchema, Depends(get_current_user)],
file: Annotated[UploadFile, File(description="上传文件")],
upload_type: Annotated[
Literal["file", "avatar", "param", "resource"] | None,
Query(description="上传类型: file=通用文件, avatar=头像, param=参数配置, resource=监控资源"),
] = "file",
target_path: Annotated[str | None, Form(description="目标目录路径(仅 resource 类型支持)")] = None,
) -> JSONResponse:
result = await FileService.upload_service(
base_url="/", # 返回 /static 开头的相对路径,去掉域名端口,便于环境迁移(dev 由 vite proxy /static 转发到后端)
file=file,
upload_type=upload_type or "file",
target_path=target_path,
)
return SuccessResponse(data=result, msg="上传文件成功")
@FileRouter.post("/download", summary="下载文件", dependencies=[Security(AuthPermission(["module_common:file:download"]))])
async def download_controller(
auth: Annotated[AuthSchema, Depends(get_current_user)],
background_tasks: BackgroundTasks,
file_path: Annotated[str, Body(description="文件路径")],
delete: Annotated[bool, Body(description="是否删除文件")] = False,
) -> FileResponse:
result = await FileService.download_service(file_path=file_path)
if delete:
background_tasks.add_task(UploadUtil.delete_file, Path(result.file_path))
return UploadFileResponse(file_path=result.file_path, filename=result.file_name)
@@ -0,0 +1,67 @@
import os
from fastapi import UploadFile
from app.config.setting import settings
from app.core.base_schema import DownloadFileSchema, UploadResponseSchema
from app.core.exceptions import CustomException
from app.core.logger import logger
from app.utils.upload_util import UploadUtil
class FileService:
"""文件管理服务层
"""
@classmethod
async def upload_service(
cls,
base_url: str,
file: UploadFile,
upload_type: str = "file",
target_path: str | None = None,
) -> UploadResponseSchema:
"""上传文件"""
filename, filepath, file_url = await UploadUtil.upload_file(
file=file,
base_url=base_url,
upload_type=upload_type,
target_path=target_path,
)
return UploadResponseSchema(
file_path=f"{filepath}",
file_name=filename,
origin_name=file.filename,
file_url=f"{file_url}",
)
@classmethod
async def download_service(cls, file_path: str) -> DownloadFileSchema:
"""下载文件"""
if not file_path:
raise CustomException(msg="请选择要下载的文件")
dangerous_patterns = ["../", "..\\", "\0"]
for pattern in dangerous_patterns:
if pattern in file_path:
logger.error(f"检测到路径穿越攻击: {file_path}")
raise CustomException(msg="非法的文件路径")
upload_root = settings.UPLOAD_FILE_PATH.resolve()
abs_path = os.path.normpath(os.path.abspath(file_path))
if not abs_path.startswith(str(upload_root)):
logger.error(f"路径不在上传目录内: {file_path}")
raise CustomException(msg="非法的文件路径")
if not UploadUtil.check_file_exists(abs_path):
raise CustomException(msg="文件不存在")
file_name = UploadUtil.download_file(abs_path)
return DownloadFileSchema(
file_path=abs_path,
file_name=str(file_name),
)