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')}"}, )