Files
leaudit-platform-backend/fastapi_modules/fastapi_leaudit/controllers/crossReviewController.py
T

275 lines
15 KiB
Python

"""交叉评查控制器(第一阶段骨架)。"""
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 (
CrossReviewTaskDocumentAppendVO,
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("/tasks/{TaskId}/documents/{DocumentId}/attachments", response_model=Result[CrossReviewTaskDocumentAppendVO])
async def AppendTaskDocumentAttachments(
TaskId: int,
DocumentId: int,
files: list[UploadFile] = File(..., description="附件文件列表"),
remark: str | None = Form(None, description="本次追加附件备注"),
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})
filePayloads: list[tuple[str, bytes, str | None]] = []
for file in files:
content = await file.read()
filePayloads.append((file.filename or "attachment.bin", content, file.content_type))
Data = await self.CrossReviewService.AppendTaskDocumentAttachments(
CurrentUserId=int(payload["user_id"]),
TaskId=TaskId,
DocumentId=DocumentId,
Files=filePayloads,
Remark=remark,
)
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