Files
dpb/backend/app/api/v1/module_bre/dus/controller.py
T
34047007@qq.com b95053c52c init: 初始化 dpb 桃育种系统代码库
前后端 + 后端 FastAPI 全量源码、部署脚本与文档。
2026-08-06 00:17:49 +08:00

349 lines
16 KiB
Python

import urllib.parse
from typing import Annotated
from fastapi import APIRouter, Body, Depends, File, Path, Query, UploadFile
from fastapi.responses import JSONResponse, StreamingResponse
from sqlalchemy.ext.asyncio import AsyncSession
from app.common.response import ResponseSchema, StreamResponse, SuccessResponse
from app.core.base_schema import (
AuthSchema,
ImportResultSchema,
PageResultSchema,
PaginationQueryParam,
)
from app.core.dependencies import AuthPermission, db_getter
from app.core.router_class import OperationLogRoute
from app.utils.common_util import bytes2file_response
from .schema import (
DusDescriptorCreateSchema,
DusDescriptorOutSchema,
DusDescriptorQueryParam,
DusDescriptorUpdateSchema,
DusDistinctnessIn,
DusObservationCreateSchema,
DusObservationOutSchema,
DusObservationQueryParam,
DusObservationUpdateSchema,
DusTestCreateSchema,
DusTestOutSchema,
DusTestQueryParam,
DusTestUpdateSchema,
)
from .service import DusService
DusRouter = APIRouter(route_class=OperationLogRoute, prefix="/dus_test", tags=["DUS 测试"])
# ---------- 描述符模板 ----------
@DusRouter.get("/descriptors/detail/{id}", summary="获取 DUS 描述符详情", response_model=ResponseSchema[DusDescriptorOutSchema])
async def get_dus_descriptor__detail_controller(
auth: Annotated[AuthSchema, Depends(AuthPermission(["module_bre:dus_test:detail"]))],
id: Annotated[int, Path(description="描述符ID")],
db: Annotated[AsyncSession, Depends(db_getter)],
) -> JSONResponse:
service = DusService(auth, db)
result_dict = await service.descriptor_detail(id=id)
return SuccessResponse(data=result_dict, msg="获取描述符详情成功")
@DusRouter.get("/descriptors/list", summary="分页查询 DUS 描述符", response_model=ResponseSchema[PageResultSchema[DusDescriptorOutSchema]])
async def get_dus_descriptor__list_controller(
auth: Annotated[AuthSchema, Depends(AuthPermission(["module_bre:dus_test:query"]))],
page: Annotated[PaginationQueryParam, Depends()],
search: Annotated[DusDescriptorQueryParam, Query()],
db: Annotated[AsyncSession, Depends(db_getter)],
) -> JSONResponse:
service = DusService(auth, db)
result_dict = await service.descriptor_page(
page_no=page.page_no,
page_size=page.page_size,
search=search,
order_by=page.order_by,
)
return SuccessResponse(data=result_dict, msg="查询描述符列表成功")
@DusRouter.get("/descriptors/options", summary="DUS 描述符下拉选项")
async def get_dus_descriptor__options_controller(
auth: Annotated[AuthSchema, Depends(AuthPermission(["module_bre:dus_test:query"]))],
db: Annotated[AsyncSession, Depends(db_getter)],
) -> JSONResponse:
service = DusService(auth, db)
options = await service.descriptor_list_options()
return SuccessResponse(data=options, msg="获取描述符选项成功")
@DusRouter.post("/descriptors/create", summary="创建 DUS 描述符", response_model=ResponseSchema[DusDescriptorOutSchema])
async def create_dus_descriptor__controller(
auth: Annotated[AuthSchema, Depends(AuthPermission(["module_bre:dus_test:create"]))],
data: Annotated[DusDescriptorCreateSchema, Body(description="创建参数")],
db: Annotated[AsyncSession, Depends(db_getter)],
) -> JSONResponse:
service = DusService(auth, db)
result_dict = await service.descriptor_create(data=data)
return SuccessResponse(data=result_dict, msg="创建描述符成功")
@DusRouter.put("/descriptors/update/{id}", summary="修改 DUS 描述符", response_model=ResponseSchema[DusDescriptorOutSchema])
async def update_dus_descriptor__controller(
auth: Annotated[AuthSchema, Depends(AuthPermission(["module_bre:dus_test:update"]))],
id: Annotated[int, Path(description="描述符ID")],
data: Annotated[DusDescriptorUpdateSchema, Body(description="修改参数")],
db: Annotated[AsyncSession, Depends(db_getter)],
) -> JSONResponse:
service = DusService(auth, db)
result_dict = await service.descriptor_update(id=id, data=data)
return SuccessResponse(data=result_dict, msg="修改描述符成功")
@DusRouter.delete("/descriptors/delete", summary="删除 DUS 描述符", response_model=ResponseSchema[None])
async def delete_dus_descriptor__controller(
auth: Annotated[AuthSchema, Depends(AuthPermission(["module_bre:dus_test:delete"]))],
ids: Annotated[list[int], Body(description="ID列表")],
db: Annotated[AsyncSession, Depends(db_getter)],
) -> JSONResponse:
service = DusService(auth, db)
await service.descriptor_delete(ids=ids)
return SuccessResponse(msg="删除描述符成功")
@DusRouter.post("/descriptors/export", summary="导出 DUS 描述符")
async def export_dus_descriptor__controller(
auth: Annotated[AuthSchema, Depends(AuthPermission(["module_bre:dus_test:export"]))],
search: Annotated[DusDescriptorQueryParam, Query()],
db: Annotated[AsyncSession, Depends(db_getter)],
) -> StreamingResponse:
service = DusService(auth, db)
result_dict_list = await service.descriptor_get_list(search=search)
export_result = DusService.descriptor_batch_export(
obj_list=[item.model_dump() for item in result_dict_list]
)
return StreamResponse(
data=bytes2file_response(export_result),
media_type="application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
headers={"Content-Disposition": f"attachment; filename={urllib.parse.quote('DUS描述符.xlsx')}"},
)
@DusRouter.post("/descriptors/import", summary="导入 DUS 描述符", response_model=ResponseSchema[ImportResultSchema])
async def import_dus_descriptor__controller(
file: Annotated[UploadFile, File(description="导入文件")],
auth: Annotated[AuthSchema, Depends(AuthPermission(["module_bre:dus_test:import"]))],
db: Annotated[AsyncSession, Depends(db_getter)],
) -> JSONResponse:
service = DusService(auth, db)
batch_import_result = await service.descriptor_batch_import(file=file, update_support=True)
return SuccessResponse(data=batch_import_result, msg="导入描述符成功")
@DusRouter.post("/descriptors/download/template", summary="获取 DUS 描述符导入模板", dependencies=[Depends(AuthPermission(["module_bre:dus_test:download"]))])
async def download_dus_descriptor__template_controller() -> StreamingResponse:
import_template_result = DusService.descriptor_import_template_download()
return StreamResponse(
data=bytes2file_response(import_template_result),
media_type="application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
headers={
"Content-Disposition": f"attachment; filename={urllib.parse.quote('DUS描述符导入模板.xlsx')}",
"Access-Control-Expose-Headers": "Content-Disposition",
},
)
# ---------- 测试记录 ----------
@DusRouter.get("/tests/detail/{id}", summary="获取 DUS 测试详情", response_model=ResponseSchema[DusTestOutSchema])
async def get_dus_test__detail_controller(
auth: Annotated[AuthSchema, Depends(AuthPermission(["module_bre:dus_test:detail"]))],
id: Annotated[int, Path(description="测试ID")],
db: Annotated[AsyncSession, Depends(db_getter)],
) -> JSONResponse:
service = DusService(auth, db)
result_dict = await service.test_detail(id=id)
return SuccessResponse(data=result_dict, msg="获取测试详情成功")
@DusRouter.get("/tests/list", summary="分页查询 DUS 测试", response_model=ResponseSchema[PageResultSchema[DusTestOutSchema]])
async def get_dus_test__list_controller(
auth: Annotated[AuthSchema, Depends(AuthPermission(["module_bre:dus_test:query"]))],
page: Annotated[PaginationQueryParam, Depends()],
search: Annotated[DusTestQueryParam, Query()],
db: Annotated[AsyncSession, Depends(db_getter)],
) -> JSONResponse:
service = DusService(auth, db)
result_dict = await service.test_page(
page_no=page.page_no,
page_size=page.page_size,
search=search,
order_by=page.order_by,
)
return SuccessResponse(data=result_dict, msg="查询测试列表成功")
@DusRouter.get("/tests/options", summary="DUS 测试下拉选项")
async def get_dus_test__options_controller(
auth: Annotated[AuthSchema, Depends(AuthPermission(["module_bre:dus_test:query"]))],
db: Annotated[AsyncSession, Depends(db_getter)],
) -> JSONResponse:
service = DusService(auth, db)
options = await service.test_list_options()
return SuccessResponse(data=options, msg="获取测试选项成功")
@DusRouter.post("/tests/create", summary="创建 DUS 测试", response_model=ResponseSchema[DusTestOutSchema])
async def create_dus_test__controller(
auth: Annotated[AuthSchema, Depends(AuthPermission(["module_bre:dus_test:create"]))],
data: Annotated[DusTestCreateSchema, Body(description="创建参数")],
db: Annotated[AsyncSession, Depends(db_getter)],
) -> JSONResponse:
service = DusService(auth, db)
result_dict = await service.test_create(data=data)
return SuccessResponse(data=result_dict, msg="创建测试记录成功")
@DusRouter.put("/tests/update/{id}", summary="修改 DUS 测试", response_model=ResponseSchema[DusTestOutSchema])
async def update_dus_test__controller(
auth: Annotated[AuthSchema, Depends(AuthPermission(["module_bre:dus_test:update"]))],
id: Annotated[int, Path(description="测试ID")],
data: Annotated[DusTestUpdateSchema, Body(description="修改参数")],
db: Annotated[AsyncSession, Depends(db_getter)],
) -> JSONResponse:
service = DusService(auth, db)
result_dict = await service.test_update(id=id, data=data)
return SuccessResponse(data=result_dict, msg="修改测试记录成功")
@DusRouter.delete("/tests/delete", summary="删除 DUS 测试", response_model=ResponseSchema[None])
async def delete_dus_test__controller(
auth: Annotated[AuthSchema, Depends(AuthPermission(["module_bre:dus_test:delete"]))],
ids: Annotated[list[int], Body(description="ID列表")],
db: Annotated[AsyncSession, Depends(db_getter)],
) -> JSONResponse:
service = DusService(auth, db)
await service.test_delete(ids=ids)
return SuccessResponse(msg="删除测试记录成功")
@DusRouter.post("/distinctness/{test_id}", summary="特异性统计判定(参照默认同测试点;结果落analysis_json+自动建议结论)")
async def run_dus_distinctness__controller(
auth: Annotated[AuthSchema, Depends(AuthPermission(["module_bre:dus_test:create"]))],
test_id: Annotated[int, Path(description="候选测试ID")],
data: Annotated[DusDistinctnessIn, Body(description="判定参数")],
db: Annotated[AsyncSession, Depends(db_getter)],
) -> JSONResponse:
service = DusService(auth, db)
report = await service.test_distinctness(
test_id=test_id,
reference_test_ids=data.reference_test_ids,
alpha=data.alpha,
)
return SuccessResponse(data=report, msg="特异性判定完成")
@DusRouter.get("/distinctness/{test_id}", summary="读取特异性判定报告(已存analysis_json)")
async def get_dus_distinctness__controller(
auth: Annotated[AuthSchema, Depends(AuthPermission(["module_bre:dus_test:query"]))],
test_id: Annotated[int, Path(description="候选测试ID")],
db: Annotated[AsyncSession, Depends(db_getter)],
) -> JSONResponse:
service = DusService(auth, db)
report = await service.distinctness_report(test_id=test_id)
return SuccessResponse(data=report, msg="获取特异性判定报告成功")
@DusRouter.post("/tests/export", summary="导出 DUS 测试")
async def export_dus_test__controller(
auth: Annotated[AuthSchema, Depends(AuthPermission(["module_bre:dus_test:export"]))],
search: Annotated[DusTestQueryParam, Query()],
db: Annotated[AsyncSession, Depends(db_getter)],
) -> StreamingResponse:
service = DusService(auth, db)
result_dict_list = await service.test_get_list(search=search)
export_result = DusService.test_batch_export(obj_list=[item.model_dump() for item in result_dict_list])
return StreamResponse(
data=bytes2file_response(export_result),
media_type="application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
headers={"Content-Disposition": f"attachment; filename={urllib.parse.quote('DUS测试.xlsx')}"},
)
# ---------- 观测 ----------
@DusRouter.get("/observations/detail/{id}", summary="获取 DUS 观测详情", response_model=ResponseSchema[DusObservationOutSchema])
async def get_dus_observation__detail_controller(
auth: Annotated[AuthSchema, Depends(AuthPermission(["module_bre:dus_test:detail"]))],
id: Annotated[int, Path(description="观测ID")],
db: Annotated[AsyncSession, Depends(db_getter)],
) -> JSONResponse:
service = DusService(auth, db)
result_dict = await service.observation_detail(id=id)
return SuccessResponse(data=result_dict, msg="获取观测详情成功")
@DusRouter.get("/observations/list", summary="分页查询 DUS 观测", response_model=ResponseSchema[PageResultSchema[DusObservationOutSchema]])
async def get_dus_observation__list_controller(
auth: Annotated[AuthSchema, Depends(AuthPermission(["module_bre:dus_test:query"]))],
page: Annotated[PaginationQueryParam, Depends()],
search: Annotated[DusObservationQueryParam, Query()],
db: Annotated[AsyncSession, Depends(db_getter)],
) -> JSONResponse:
service = DusService(auth, db)
result_dict = await service.observation_page(
page_no=page.page_no,
page_size=page.page_size,
search=search,
order_by=page.order_by,
)
return SuccessResponse(data=result_dict, msg="查询观测列表成功")
@DusRouter.post("/observations/create", summary="创建 DUS 观测", response_model=ResponseSchema[DusObservationOutSchema])
async def create_dus_observation__controller(
auth: Annotated[AuthSchema, Depends(AuthPermission(["module_bre:dus_test:create"]))],
data: Annotated[DusObservationCreateSchema, Body(description="创建参数")],
db: Annotated[AsyncSession, Depends(db_getter)],
) -> JSONResponse:
service = DusService(auth, db)
result_dict = await service.observation_create(data=data)
return SuccessResponse(data=result_dict, msg="创建观测成功")
@DusRouter.put("/observations/update/{id}", summary="修改 DUS 观测", response_model=ResponseSchema[DusObservationOutSchema])
async def update_dus_observation__controller(
auth: Annotated[AuthSchema, Depends(AuthPermission(["module_bre:dus_test:update"]))],
id: Annotated[int, Path(description="观测ID")],
data: Annotated[DusObservationUpdateSchema, Body(description="修改参数")],
db: Annotated[AsyncSession, Depends(db_getter)],
) -> JSONResponse:
service = DusService(auth, db)
result_dict = await service.observation_update(id=id, data=data)
return SuccessResponse(data=result_dict, msg="修改观测成功")
@DusRouter.delete("/observations/delete", summary="删除 DUS 观测", response_model=ResponseSchema[None])
async def delete_dus_observation__controller(
auth: Annotated[AuthSchema, Depends(AuthPermission(["module_bre:dus_test:delete"]))],
ids: Annotated[list[int], Body(description="ID列表")],
db: Annotated[AsyncSession, Depends(db_getter)],
) -> JSONResponse:
service = DusService(auth, db)
await service.observation_delete(ids=ids)
return SuccessResponse(msg="删除观测成功")
@DusRouter.post("/observations/export", summary="导出 DUS 观测")
async def export_dus_observation__controller(
auth: Annotated[AuthSchema, Depends(AuthPermission(["module_bre:dus_test:export"]))],
search: Annotated[DusObservationQueryParam, Query()],
db: Annotated[AsyncSession, Depends(db_getter)],
) -> StreamingResponse:
service = DusService(auth, db)
result_dict_list = await service.observation_get_list(search=search)
export_result = DusService.observation_batch_export(
obj_list=[item.model_dump() for item in result_dict_list]
)
return StreamResponse(
data=bytes2file_response(export_result),
media_type="application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
headers={"Content-Disposition": f"attachment; filename={urllib.parse.quote('DUS观测.xlsx')}"},
)