"""交叉评查控制器(第一阶段骨架)。""" from typing import Any from fastapi import Depends, File, Form, Query, UploadFile from fastapi.responses import JSONResponse, Response from fastapi_common.fastapi_common_security.security import verify_access_token from fastapi_common.fastapi_common_web.controller import BaseController from fastapi_common.fastapi_common_web.domain.responses import Result from fastapi_modules.fastapi_leaudit.domian.Dto.crossReviewDto import ( CrossReviewProposalCreateDTO, CrossReviewProposalVoteDTO, CrossReviewTaskCreateDTO, CrossReviewTaskDocumentQueryDTO, CrossReviewTaskQueryDTO, ) from fastapi_modules.fastapi_leaudit.domian.vo.crossReviewVo import ( CrossReviewPendingVotesVO, CrossReviewPermissionVO, CrossReviewProposalCancelVO, CrossReviewProposalCreateVO, CrossReviewProposalPageVO, CrossReviewProposalVoteVO, CrossReviewTaskCompleteVO, CrossReviewTaskCreateVO, CrossReviewTaskDocumentPageVO, CrossReviewTaskDocumentUploadVO, CrossReviewTaskPageVO, CrossReviewTaskProgressVO, ) from fastapi_modules.fastapi_leaudit.services.crossReviewService import ICrossReviewService from fastapi_modules.fastapi_leaudit.services.impl.crossReviewServiceImpl import CrossReviewServiceImpl from fastapi_modules.fastapi_leaudit.services.impl.permissionServiceImpl import PermissionServiceImpl from fastapi_modules.fastapi_leaudit.services.permissionService import IPermissionService class CrossReviewController(BaseController): """交叉评查控制器。""" _PERMISSIONS = { "task_create": "cross_review:task:create", "task_read": "cross_review:task:read", "progress_view": "cross_review:progress:view", "document_read": "cross_review:document:read", "document_complete": "cross_review:document:complete", "proposal_create": "cross_review:proposal:create", "proposal_read": "cross_review:proposal:read", "proposal_delete": "cross_review:proposal:delete", "proposal_vote": "cross_review:proposal:vote", } def __init__(self): super().__init__(prefix="/v3/cross-review", tags=["交叉评查"]) self.CrossReviewService: ICrossReviewService = CrossReviewServiceImpl() self.PermissionService: IPermissionService = PermissionServiceImpl() @self.router.post("/tasks", response_model=Result[CrossReviewTaskCreateVO]) async def CreateTask( Body: CrossReviewTaskCreateDTO, payload: dict[str, Any] = Depends(verify_access_token), ): """创建交叉评查任务。""" if not await self._check_permission(int(payload["user_id"]), [self._PERMISSIONS["task_create"]]): return JSONResponse(status_code=403, content={"code": 403, "msg": "当前用户没有创建交叉评查任务权限", "data": None}) Data = await self.CrossReviewService.CreateTask(CurrentUserId=int(payload["user_id"]), Body=Body) return Result.success(data=Data, message="交叉评查任务创建成功") @self.router.post("/tasks/query", response_model=Result[CrossReviewTaskPageVO]) async def GetUserTasks( Body: CrossReviewTaskQueryDTO, payload: dict[str, Any] = Depends(verify_access_token), ): """查询当前用户参与的交叉评查任务。""" if not await self._check_permission(int(payload["user_id"]), [self._PERMISSIONS["task_read"]]): return JSONResponse(status_code=403, content={"code": 403, "msg": "当前用户没有查看交叉评查任务权限", "data": None}) Data = await self.CrossReviewService.GetUserTasks(CurrentUserId=int(payload["user_id"]), Body=Body) return Result.success(data=Data) @self.router.get("/tasks/{TaskId}/progress", response_model=Result[CrossReviewTaskProgressVO]) async def GetTaskProgress( TaskId: int, payload: dict[str, Any] = Depends(verify_access_token), ): """查询任务进度。""" if not await self._check_permission(int(payload["user_id"]), [self._PERMISSIONS["progress_view"]]): return JSONResponse(status_code=403, content={"code": 403, "msg": "当前用户没有查看交叉评查任务进度权限", "data": None}) Data = await self.CrossReviewService.GetTaskProgress(CurrentUserId=int(payload["user_id"]), TaskId=TaskId) return Result.success(data=Data) @self.router.get("/tasks/{TaskId}/documents", response_model=Result[CrossReviewTaskDocumentPageVO]) async def GetTaskDocuments( TaskId: int, page: int = Query(1, ge=1, description="页码"), pageSize: int = Query(20, ge=1, le=100, description="每页大小"), keyword: str | None = Query(None, description="关键字"), payload: dict[str, Any] = Depends(verify_access_token), ): """查询任务文档列表。""" if not await self._check_permission(int(payload["user_id"]), [self._PERMISSIONS["task_read"], self._PERMISSIONS["document_read"]]): return JSONResponse(status_code=403, content={"code": 403, "msg": "当前用户没有查看交叉评查任务文档权限", "data": None}) Body = CrossReviewTaskDocumentQueryDTO(page=page, pageSize=pageSize, keyword=keyword) Data = await self.CrossReviewService.GetTaskDocuments( CurrentUserId=int(payload["user_id"]), TaskId=TaskId, Body=Body, ) return Result.success(data=Data) @self.router.get("/tasks/{TaskId}/can-confirm", response_model=Result[CrossReviewPermissionVO]) async def CanConfirmTaskDocument( TaskId: int, payload: dict[str, Any] = Depends(verify_access_token), ): """查询当前用户是否可确认完成。""" if not await self._check_permission(int(payload["user_id"]), [self._PERMISSIONS["document_complete"]]): return JSONResponse(status_code=403, content={"code": 403, "msg": "当前用户没有确认交叉评查文档完成权限", "data": None}) Data = await self.CrossReviewService.CanConfirmTaskDocument(CurrentUserId=int(payload["user_id"]), TaskId=TaskId) return Result.success(data=Data) @self.router.post("/tasks/{TaskId}/documents/{DocumentId}/complete", response_model=Result[CrossReviewTaskCompleteVO]) async def CompleteTaskDocument( TaskId: int, DocumentId: int, payload: dict[str, Any] = Depends(verify_access_token), ): """确认任务文档完成。""" if not await self._check_permission(int(payload["user_id"]), [self._PERMISSIONS["document_complete"]]): return JSONResponse(status_code=403, content={"code": 403, "msg": "当前用户没有确认交叉评查文档完成权限", "data": None}) Data = await self.CrossReviewService.CompleteTaskDocument( CurrentUserId=int(payload["user_id"]), TaskId=TaskId, DocumentId=DocumentId, ) return Result.success(data=Data, message="交叉评查文档已确认完成") @self.router.post("/tasks/{TaskId}/documents/upload", response_model=Result[CrossReviewTaskDocumentUploadVO]) async def UploadTaskDocument( TaskId: int, file: UploadFile = File(..., description="上传文档"), typeId: int | None = Form(None, description="文档类型ID"), groupId: int | None = Form(None, description="文档子类型ID"), payload: dict[str, Any] = Depends(verify_access_token), ): """向交叉评查任务补传文档。""" if not await self._check_permission(int(payload["user_id"]), [self._PERMISSIONS["document_complete"]]): return JSONResponse(status_code=403, content={"code": 403, "msg": "当前用户没有交叉评查任务补传文档权限", "data": None}) content = await file.read() Data = await self.CrossReviewService.UploadTaskDocument( CurrentUserId=int(payload["user_id"]), TaskId=TaskId, FileName=file.filename or "upload.bin", FileContent=content, ContentType=file.content_type, TypeId=typeId, GroupId=groupId, ) return Result.success(data=Data, message="交叉评查任务文档上传成功") @self.router.post("/proposals", response_model=Result[CrossReviewProposalCreateVO]) async def CreateProposal( Body: CrossReviewProposalCreateDTO, payload: dict[str, Any] = Depends(verify_access_token), ): """创建交叉评查提案。""" if not await self._check_permission(int(payload["user_id"]), [self._PERMISSIONS["proposal_create"]]): return JSONResponse(status_code=403, content={"code": 403, "msg": "当前用户没有创建交叉评查提案权限", "data": None}) Data = await self.CrossReviewService.CreateProposal(CurrentUserId=int(payload["user_id"]), Body=Body) return Result.success(data=Data, message="交叉评查提案创建成功") @self.router.post("/proposals/{ProposalId}/votes", response_model=Result[CrossReviewProposalVoteVO]) async def VoteProposal( ProposalId: int, Body: CrossReviewProposalVoteDTO, payload: dict[str, Any] = Depends(verify_access_token), ): """对交叉评查提案投票。""" if not await self._check_permission(int(payload["user_id"]), [self._PERMISSIONS["proposal_vote"]]): return JSONResponse(status_code=403, content={"code": 403, "msg": "当前用户没有交叉评查提案投票权限", "data": None}) Data = await self.CrossReviewService.VoteProposal(CurrentUserId=int(payload["user_id"]), ProposalId=ProposalId, Body=Body) return Result.success(data=Data, message="交叉评查提案投票成功") @self.router.delete("/proposals/{ProposalId}", response_model=Result[CrossReviewProposalCancelVO]) async def CancelProposal( ProposalId: int, payload: dict[str, Any] = Depends(verify_access_token), ): """撤销交叉评查提案。""" if not await self._check_permission(int(payload["user_id"]), [self._PERMISSIONS["proposal_delete"]]): return JSONResponse(status_code=403, content={"code": 403, "msg": "当前用户没有撤销交叉评查提案权限", "data": None}) Data = await self.CrossReviewService.CancelProposal(CurrentUserId=int(payload["user_id"]), ProposalId=ProposalId) return Result.success(data=Data, message="交叉评查提案已撤销") @self.router.get("/documents/{DocumentId}/proposals", response_model=Result[CrossReviewProposalPageVO]) async def GetDocumentProposals( DocumentId: int, page: int = Query(1, ge=1, description="页码"), pageSize: int = Query(20, ge=1, le=100, description="每页大小"), payload: dict[str, Any] = Depends(verify_access_token), ): """获取文档提案列表。""" if not await self._check_permission(int(payload["user_id"]), [self._PERMISSIONS["proposal_read"]]): return JSONResponse(status_code=403, content={"code": 403, "msg": "当前用户没有查看交叉评查提案权限", "data": None}) Data = await self.CrossReviewService.GetDocumentProposals( CurrentUserId=int(payload["user_id"]), DocumentId=DocumentId, Page=page, PageSize=pageSize, ) return Result.success(data=Data) @self.router.get("/documents/{DocumentId}/pending-votes", response_model=Result[CrossReviewPendingVotesVO]) async def GetDocumentPendingVotes( DocumentId: int, payload: dict[str, Any] = Depends(verify_access_token), ): """获取文档待投票摘要。""" if not await self._check_permission(int(payload["user_id"]), [self._PERMISSIONS["proposal_read"], self._PERMISSIONS["document_complete"]]): return JSONResponse(status_code=403, content={"code": 403, "msg": "当前用户没有查看待投票信息权限", "data": None}) Data = await self.CrossReviewService.GetDocumentPendingVotes(CurrentUserId=int(payload["user_id"]), DocumentId=DocumentId) return Result.success(data=Data) @self.router.get("/documents/{DocumentId}/proposals/export") async def ExportDocumentProposals( DocumentId: int, payload: dict[str, Any] = Depends(verify_access_token), ): """导出文档提案列表 Excel。""" if not await self._check_permission(int(payload["user_id"]), [self._PERMISSIONS["proposal_read"]]): return JSONResponse(status_code=403, content={"code": 403, "msg": "当前用户没有导出交叉评查提案权限", "data": None}) fileContent, fileName = await self.CrossReviewService.ExportDocumentProposals( CurrentUserId=int(payload["user_id"]), DocumentId=DocumentId, ) return Response( content=fileContent, media_type="application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", headers={ "Content-Disposition": f"attachment; filename*=UTF-8''{fileName}", }, ) async def _check_permission(self, user_id: int, permission_keys: list[str]) -> bool: """OR 逻辑权限校验。""" for permission_key in permission_keys: if await self.PermissionService.CheckPermission(user_id, permission_key): return True return False