feat: migrate cross review to v3 leaudit flow
This commit is contained in:
@@ -1,6 +1,7 @@
|
||||
"""LeAudit 服务层导出。"""
|
||||
|
||||
from fastapi_modules.fastapi_leaudit.services.auditService import IAuditService
|
||||
from fastapi_modules.fastapi_leaudit.services.crossReviewService import ICrossReviewService
|
||||
from fastapi_modules.fastapi_leaudit.services.documentService import IDocumentService
|
||||
from fastapi_modules.fastapi_leaudit.services.evaluationPointService import IEvaluationPointService
|
||||
from fastapi_modules.fastapi_leaudit.services.entryModuleAdminService import IEntryModuleAdminService
|
||||
@@ -16,6 +17,7 @@ from fastapi_modules.fastapi_leaudit.services.ruleService import IRuleService
|
||||
|
||||
__all__ = [
|
||||
"IAuditService",
|
||||
"ICrossReviewService",
|
||||
"IDocumentService",
|
||||
"IEvaluationPointService",
|
||||
"IEntryModuleAdminService",
|
||||
|
||||
@@ -0,0 +1,112 @@
|
||||
"""交叉评查服务接口。"""
|
||||
|
||||
from abc import ABC, abstractmethod
|
||||
|
||||
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,
|
||||
)
|
||||
|
||||
|
||||
class ICrossReviewService(ABC):
|
||||
"""交叉评查服务接口。"""
|
||||
|
||||
@abstractmethod
|
||||
async def CreateTask(self, CurrentUserId: int, Body: CrossReviewTaskCreateDTO) -> CrossReviewTaskCreateVO:
|
||||
"""创建交叉评查任务。"""
|
||||
...
|
||||
|
||||
@abstractmethod
|
||||
async def GetUserTasks(self, CurrentUserId: int, Body: CrossReviewTaskQueryDTO) -> CrossReviewTaskPageVO:
|
||||
"""查询当前用户参与的交叉评查任务。"""
|
||||
...
|
||||
|
||||
@abstractmethod
|
||||
async def GetTaskProgress(self, CurrentUserId: int, TaskId: int) -> CrossReviewTaskProgressVO:
|
||||
"""查询任务进度。"""
|
||||
...
|
||||
|
||||
@abstractmethod
|
||||
async def GetTaskDocuments(
|
||||
self,
|
||||
CurrentUserId: int,
|
||||
TaskId: int,
|
||||
Body: CrossReviewTaskDocumentQueryDTO,
|
||||
) -> CrossReviewTaskDocumentPageVO:
|
||||
"""查询任务文档列表。"""
|
||||
...
|
||||
|
||||
@abstractmethod
|
||||
async def CanConfirmTaskDocument(self, CurrentUserId: int, TaskId: int) -> CrossReviewPermissionVO:
|
||||
"""判断当前用户是否有权确认任务文档完成。"""
|
||||
...
|
||||
|
||||
@abstractmethod
|
||||
async def CompleteTaskDocument(self, CurrentUserId: int, TaskId: int, DocumentId: int) -> CrossReviewTaskCompleteVO:
|
||||
"""确认任务文档完成。"""
|
||||
...
|
||||
|
||||
@abstractmethod
|
||||
async def CreateProposal(self, CurrentUserId: int, Body: CrossReviewProposalCreateDTO) -> CrossReviewProposalCreateVO:
|
||||
"""创建交叉评查提案。"""
|
||||
...
|
||||
|
||||
@abstractmethod
|
||||
async def VoteProposal(
|
||||
self,
|
||||
CurrentUserId: int,
|
||||
ProposalId: int,
|
||||
Body: CrossReviewProposalVoteDTO,
|
||||
) -> CrossReviewProposalVoteVO:
|
||||
"""对交叉评查提案投票。"""
|
||||
...
|
||||
|
||||
@abstractmethod
|
||||
async def CancelProposal(self, CurrentUserId: int, ProposalId: int) -> CrossReviewProposalCancelVO:
|
||||
"""撤销交叉评查提案。"""
|
||||
...
|
||||
|
||||
@abstractmethod
|
||||
async def GetDocumentProposals(
|
||||
self,
|
||||
CurrentUserId: int,
|
||||
DocumentId: int,
|
||||
Page: int,
|
||||
PageSize: int,
|
||||
) -> CrossReviewProposalPageVO:
|
||||
"""获取文档提案列表。"""
|
||||
...
|
||||
|
||||
@abstractmethod
|
||||
async def GetDocumentPendingVotes(self, CurrentUserId: int, DocumentId: int) -> CrossReviewPendingVotesVO:
|
||||
"""获取文档待投票信息。"""
|
||||
...
|
||||
|
||||
@abstractmethod
|
||||
async def UploadTaskDocument(
|
||||
self,
|
||||
CurrentUserId: int,
|
||||
TaskId: int,
|
||||
FileName: str,
|
||||
FileContent: bytes,
|
||||
ContentType: str | None,
|
||||
) -> CrossReviewTaskDocumentUploadVO:
|
||||
"""向交叉评查任务补传文档。"""
|
||||
...
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1128,6 +1128,7 @@ class DocumentServiceImpl(IDocumentService):
|
||||
)
|
||||
).scalar_one()
|
||||
await self._syncRuleBindings(Session, int(row), Body.ruleSetIds, "default")
|
||||
await sync_group_bindings_from_doc_type(Session, int(row), Body.ruleSetIds)
|
||||
await Session.commit()
|
||||
|
||||
return await self.GetDocumentType(int(row))
|
||||
@@ -1170,6 +1171,7 @@ class DocumentServiceImpl(IDocumentService):
|
||||
|
||||
if "ruleSetIds" in providedFields and Body.ruleSetIds is not None:
|
||||
await self._syncRuleBindings(Session, Id, Body.ruleSetIds, "default")
|
||||
await sync_group_bindings_from_doc_type(Session, Id, Body.ruleSetIds)
|
||||
|
||||
await Session.commit()
|
||||
return await self.GetDocumentType(Id)
|
||||
@@ -1186,6 +1188,7 @@ class DocumentServiceImpl(IDocumentService):
|
||||
if not current:
|
||||
raise LeauditException(StatusCodeEnum.HTTP_404_NOT_FOUND, "文档类型不存在")
|
||||
await Session.execute(text("UPDATE leaudit_document_types SET deleted_at = NOW() WHERE id = :id"), {"id": Id})
|
||||
await Session.execute(text("UPDATE leaudit_rule_type_bindings SET deleted_at = NOW() WHERE doc_type_id = :id AND deleted_at IS NULL"), {"id": Id})
|
||||
await Session.commit()
|
||||
|
||||
async def ListDocumentTypeRoots(self, EntryModuleId: int | None = None) -> list[DocumentTypeRootItemVO]:
|
||||
@@ -1331,36 +1334,17 @@ class DocumentServiceImpl(IDocumentService):
|
||||
await Session.execute(
|
||||
text(
|
||||
"""
|
||||
SELECT
|
||||
child.document_type_id AS doc_type_id,
|
||||
rgb.rule_set_id,
|
||||
child.sort_order AS child_sort_order,
|
||||
rgb.priority,
|
||||
rgb.id
|
||||
FROM leaudit_evaluation_point_groups child
|
||||
JOIN leaudit_rule_group_bindings rgb
|
||||
ON rgb.group_id = child.id
|
||||
AND rgb.deleted_at IS NULL
|
||||
AND rgb.is_active = TRUE
|
||||
WHERE child.document_type_id = ANY(:ids)
|
||||
AND child.deleted_at IS NULL
|
||||
AND COALESCE(child.pid, 0) <> 0
|
||||
ORDER BY
|
||||
child.document_type_id ASC,
|
||||
COALESCE(child.sort_order, 0) ASC,
|
||||
COALESCE(rgb.priority, 0) DESC,
|
||||
rgb.id ASC
|
||||
SELECT doc_type_id, rule_set_id
|
||||
FROM leaudit_rule_type_bindings
|
||||
WHERE doc_type_id = ANY(:ids) AND deleted_at IS NULL AND is_active = true
|
||||
ORDER BY priority DESC
|
||||
"""
|
||||
),
|
||||
{"ids": allIds},
|
||||
)
|
||||
).fetchall()
|
||||
for docTypeId, ruleSetId, *_ in bindingRows:
|
||||
current = bindingsMap.setdefault(int(docTypeId), [])
|
||||
normalizedRuleSetId = int(ruleSetId)
|
||||
if normalizedRuleSetId not in current:
|
||||
current.append(normalizedRuleSetId)
|
||||
|
||||
for b in bindingRows:
|
||||
bindingsMap.setdefault(int(b[0]), []).append(int(b[1]))
|
||||
return rows, bindingsMap
|
||||
|
||||
async def _queryDocumentTypeRoots(self, Session, Ids: list[int] | None = None, EntryModuleId: int | None = None):
|
||||
@@ -2198,6 +2182,33 @@ class DocumentServiceImpl(IDocumentService):
|
||||
|
||||
async def _loadScoringProposals(self, Session, DocumentId: int) -> list[dict[str, Any]]:
|
||||
"""读取交叉评分提案;缺表时降级为空。"""
|
||||
if await self._tableExists(Session, "leaudit_cross_review_proposals"):
|
||||
rows = (
|
||||
await Session.execute(
|
||||
text(
|
||||
"""
|
||||
SELECT
|
||||
id,
|
||||
rule_result_id AS evaluation_result_id,
|
||||
proposer_id,
|
||||
proposed_score_delta AS proposed_score,
|
||||
reason,
|
||||
status,
|
||||
create_time AS created_at,
|
||||
update_time AS updated_at,
|
||||
document_id
|
||||
FROM leaudit_cross_review_proposals
|
||||
WHERE document_id = :document_id
|
||||
AND delete_time IS NULL
|
||||
ORDER BY id DESC
|
||||
"""
|
||||
),
|
||||
{"document_id": DocumentId},
|
||||
)
|
||||
).mappings().all()
|
||||
if rows:
|
||||
return [dict(row) for row in rows]
|
||||
|
||||
if not await self._tableExists(Session, "cross_scoring_proposals"):
|
||||
return []
|
||||
|
||||
@@ -2580,8 +2591,21 @@ class DocumentServiceImpl(IDocumentService):
|
||||
return str(Row.get("rule_name") or Row.get("rule_id") or "")
|
||||
|
||||
async def _syncRuleBindings(self, Session, DocTypeId: int, RuleSetIds: list[int], Region: str = "default") -> None:
|
||||
"""全量替换规则绑定,仅写入新分组绑定。"""
|
||||
await sync_group_bindings_from_doc_type(Session, DocTypeId, RuleSetIds)
|
||||
"""全量替换规则绑定。"""
|
||||
await Session.execute(
|
||||
text("UPDATE leaudit_rule_type_bindings SET deleted_at = NOW() WHERE doc_type_id = :id AND deleted_at IS NULL"),
|
||||
{"id": DocTypeId},
|
||||
)
|
||||
for idx, ruleSetId in enumerate(RuleSetIds):
|
||||
await Session.execute(
|
||||
text(
|
||||
"""
|
||||
INSERT INTO leaudit_rule_type_bindings (doc_type_id, rule_set_id, binding_mode, priority, region, is_active, created_at, updated_at)
|
||||
VALUES (:doc_type_id, :rule_set_id, 'explicit', :priority, :region, true, NOW(), NOW())
|
||||
"""
|
||||
),
|
||||
{"doc_type_id": DocTypeId, "rule_set_id": ruleSetId, "priority": 100 - idx, "region": Region},
|
||||
)
|
||||
|
||||
|
||||
async def _find_latest_version_candidate(
|
||||
|
||||
@@ -97,6 +97,41 @@ class RbacAdminServiceImpl(IRbacAdminService):
|
||||
"is_cache": True,
|
||||
"meta": {"group": "documents"},
|
||||
},
|
||||
{
|
||||
"route_path": "/cross-checking",
|
||||
"route_name": "cross-checking",
|
||||
"component": "cross-checking",
|
||||
"route_title": "交叉评查",
|
||||
"icon": "ri-color-filter-line",
|
||||
"sort_order": 60,
|
||||
"is_hidden": False,
|
||||
"is_cache": True,
|
||||
"meta": {"group": "cross-review"},
|
||||
},
|
||||
{
|
||||
"route_path": "/cross-checking/upload",
|
||||
"route_name": "cross-checking-upload",
|
||||
"component": "cross-checking.upload",
|
||||
"route_title": "创建任务",
|
||||
"icon": "ri-upload-cloud-line",
|
||||
"sort_order": 1,
|
||||
"parent_path": "/cross-checking",
|
||||
"is_hidden": False,
|
||||
"is_cache": True,
|
||||
"meta": {"group": "cross-review"},
|
||||
},
|
||||
{
|
||||
"route_path": "/cross-checking/result",
|
||||
"route_name": "cross-checking-result",
|
||||
"component": "cross-checking.result",
|
||||
"route_title": "评查结果",
|
||||
"icon": "ri-file-list-3-line",
|
||||
"sort_order": 2,
|
||||
"parent_path": "/cross-checking",
|
||||
"is_hidden": False,
|
||||
"is_cache": True,
|
||||
"meta": {"group": "cross-review"},
|
||||
},
|
||||
{
|
||||
"route_path": "/settings",
|
||||
"route_name": "system-settings",
|
||||
@@ -171,6 +206,15 @@ class RbacAdminServiceImpl(IRbacAdminService):
|
||||
{"permission_key": "evaluation_point:create:write", "display_name": "创建评查点", "module": "evaluation_point", "resource": "create", "action": "write", "api_method": "POST", "api_path": "/api/v3/evaluation-points", "route_path": "/rules"},
|
||||
{"permission_key": "evaluation_point:update:write", "display_name": "更新评查点", "module": "evaluation_point", "resource": "update", "action": "write", "api_method": "PUT", "api_path": "/api/v3/evaluation-points/{id}", "route_path": "/rules"},
|
||||
{"permission_key": "evaluation_point:delete:delete", "display_name": "删除评查点", "module": "evaluation_point", "resource": "delete", "action": "delete", "api_method": "DELETE", "api_path": "/api/v3/evaluation-points/{id}", "route_path": "/rules"},
|
||||
{"permission_key": "cross_review:task:create", "display_name": "创建交叉评查任务", "module": "cross_review", "resource": "task", "action": "create", "api_method": "POST", "api_path": "/api/v3/cross-review/tasks", "route_path": "/cross-checking/upload"},
|
||||
{"permission_key": "cross_review:task:read", "display_name": "查看交叉评查任务", "module": "cross_review", "resource": "task", "action": "read", "api_method": "POST", "api_path": "/api/v3/cross-review/tasks/query", "route_path": "/cross-checking"},
|
||||
{"permission_key": "cross_review:progress:view", "display_name": "查看交叉评查任务进度", "module": "cross_review", "resource": "progress", "action": "view", "api_method": "GET", "api_path": "/api/v3/cross-review/tasks/{task_id}/progress", "route_path": "/cross-checking"},
|
||||
{"permission_key": "cross_review:document:read", "display_name": "查看交叉评查任务文档", "module": "cross_review", "resource": "document", "action": "read", "api_method": "GET", "api_path": "/api/v3/cross-review/tasks/{task_id}/documents", "route_path": "/cross-checking/result"},
|
||||
{"permission_key": "cross_review:document:complete", "display_name": "确认交叉评查文档完成", "module": "cross_review", "resource": "document", "action": "complete", "api_method": "GET", "api_path": "/api/v3/cross-review/tasks/{task_id}/can-confirm", "route_path": "/cross-checking/result"},
|
||||
{"permission_key": "cross_review:proposal:create", "display_name": "创建交叉评查提案", "module": "cross_review", "resource": "proposal", "action": "create", "api_method": "POST", "api_path": "/api/v3/cross-review/proposals", "route_path": "/cross-checking/result"},
|
||||
{"permission_key": "cross_review:proposal:read", "display_name": "查看交叉评查提案", "module": "cross_review", "resource": "proposal", "action": "read", "api_method": "GET", "api_path": "/api/v3/cross-review/documents/{document_id}/proposals", "route_path": "/cross-checking/result"},
|
||||
{"permission_key": "cross_review:proposal:delete", "display_name": "撤销交叉评查提案", "module": "cross_review", "resource": "proposal", "action": "delete", "api_method": "DELETE", "api_path": "/api/v3/cross-review/proposals/{proposal_id}", "route_path": "/cross-checking/result"},
|
||||
{"permission_key": "cross_review:proposal:vote", "display_name": "交叉评查提案投票", "module": "cross_review", "resource": "proposal", "action": "vote", "api_method": "POST", "api_path": "/api/v3/cross-review/proposals/{proposal_id}/votes", "route_path": "/cross-checking/result"},
|
||||
{"permission_key": "rbac:roles:read", "display_name": "角色列表", "module": "rbac", "resource": "roles", "action": "read", "api_method": "GET", "api_path": "/api/v3/rbac/roles", "route_path": "/role-permissions"},
|
||||
{"permission_key": "rbac:roles:create", "display_name": "创建角色", "module": "rbac", "resource": "roles", "action": "create", "api_method": "POST", "api_path": "/api/v3/rbac/roles", "route_path": "/role-permissions"},
|
||||
{"permission_key": "rbac:roles:update", "display_name": "更新角色", "module": "rbac", "resource": "roles", "action": "update", "api_method": "PUT", "api_path": "/api/v3/rbac/roles/{role_id}", "route_path": "/role-permissions"},
|
||||
|
||||
Reference in New Issue
Block a user