277 lines
16 KiB
Python
277 lines
16 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})
|
|
UserId = int(payload["user_id"])
|
|
CanViewProgress = await self._check_permission(UserId, [self._PERMISSIONS["progress_view"]])
|
|
Data = await self.CrossReviewService.GetUserTasks(CurrentUserId=UserId, Body=Body, CanViewProgress=CanViewProgress)
|
|
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
|