fix: stabilize rule config and cross-review backend
This commit is contained in:
@@ -44,6 +44,102 @@ from fastapi_modules.fastapi_leaudit.services.impl.documentServiceImpl import Do
|
||||
class CrossReviewServiceImpl(ICrossReviewService):
|
||||
"""交叉评查服务实现。"""
|
||||
|
||||
_SCHEMA_BOOTSTRAP_STATEMENTS: tuple[str, ...] = (
|
||||
"""
|
||||
CREATE TABLE IF NOT EXISTS leaudit_cross_review_tasks (
|
||||
id BIGINT GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
|
||||
task_name VARCHAR(255) NOT NULL,
|
||||
task_type VARCHAR(32) NOT NULL,
|
||||
doc_type_id BIGINT,
|
||||
doc_type_code VARCHAR(64),
|
||||
assigner_id BIGINT NOT NULL,
|
||||
status VARCHAR(32) NOT NULL DEFAULT 'in_progress',
|
||||
create_time TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
update_time TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
delete_time TIMESTAMPTZ
|
||||
)
|
||||
""",
|
||||
"CREATE INDEX IF NOT EXISTS idx_lcr_tasks_assigner_id ON leaudit_cross_review_tasks (assigner_id)",
|
||||
"CREATE INDEX IF NOT EXISTS idx_lcr_tasks_status ON leaudit_cross_review_tasks (status)",
|
||||
"CREATE INDEX IF NOT EXISTS idx_lcr_tasks_doc_type_id ON leaudit_cross_review_tasks (doc_type_id)",
|
||||
"""
|
||||
CREATE TABLE IF NOT EXISTS leaudit_cross_review_task_members (
|
||||
id BIGINT GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
|
||||
task_id BIGINT NOT NULL,
|
||||
user_id BIGINT NOT NULL,
|
||||
member_role VARCHAR(32) NOT NULL DEFAULT 'participant',
|
||||
create_time TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
update_time TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
delete_time TIMESTAMPTZ
|
||||
)
|
||||
""",
|
||||
"CREATE INDEX IF NOT EXISTS idx_lcr_task_members_task_id ON leaudit_cross_review_task_members (task_id)",
|
||||
"CREATE INDEX IF NOT EXISTS idx_lcr_task_members_user_id ON leaudit_cross_review_task_members (user_id)",
|
||||
"CREATE INDEX IF NOT EXISTS idx_lcr_task_members_role ON leaudit_cross_review_task_members (member_role)",
|
||||
"""
|
||||
CREATE UNIQUE INDEX IF NOT EXISTS uq_lcr_task_members_task_user_active
|
||||
ON leaudit_cross_review_task_members (task_id, user_id)
|
||||
WHERE delete_time IS NULL
|
||||
""",
|
||||
"""
|
||||
CREATE TABLE IF NOT EXISTS leaudit_cross_review_task_documents (
|
||||
id BIGINT GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
|
||||
task_id BIGINT NOT NULL,
|
||||
document_id BIGINT NOT NULL,
|
||||
audit_status INTEGER NOT NULL DEFAULT 0,
|
||||
create_time TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
update_time TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
delete_time TIMESTAMPTZ
|
||||
)
|
||||
""",
|
||||
"CREATE INDEX IF NOT EXISTS idx_lcr_task_documents_task_id ON leaudit_cross_review_task_documents (task_id)",
|
||||
"CREATE INDEX IF NOT EXISTS idx_lcr_task_documents_document_id ON leaudit_cross_review_task_documents (document_id)",
|
||||
"CREATE INDEX IF NOT EXISTS idx_lcr_task_documents_task_status ON leaudit_cross_review_task_documents (task_id, audit_status)",
|
||||
"""
|
||||
CREATE UNIQUE INDEX IF NOT EXISTS uq_lcr_task_documents_task_document_active
|
||||
ON leaudit_cross_review_task_documents (task_id, document_id)
|
||||
WHERE delete_time IS NULL
|
||||
""",
|
||||
"""
|
||||
CREATE TABLE IF NOT EXISTS leaudit_cross_review_proposals (
|
||||
id BIGINT GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
|
||||
task_id BIGINT NOT NULL,
|
||||
document_id BIGINT NOT NULL,
|
||||
rule_result_id BIGINT NOT NULL,
|
||||
proposer_id BIGINT NOT NULL,
|
||||
proposed_score_delta NUMERIC(10, 2) NOT NULL,
|
||||
reason TEXT NOT NULL,
|
||||
status VARCHAR(32) NOT NULL DEFAULT 'pending',
|
||||
create_time TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
update_time TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
delete_time TIMESTAMPTZ
|
||||
)
|
||||
""",
|
||||
"CREATE INDEX IF NOT EXISTS idx_lcr_proposals_task_id ON leaudit_cross_review_proposals (task_id)",
|
||||
"CREATE INDEX IF NOT EXISTS idx_lcr_proposals_document_id ON leaudit_cross_review_proposals (document_id)",
|
||||
"CREATE INDEX IF NOT EXISTS idx_lcr_proposals_rule_result_id ON leaudit_cross_review_proposals (rule_result_id)",
|
||||
"CREATE INDEX IF NOT EXISTS idx_lcr_proposals_proposer_id ON leaudit_cross_review_proposals (proposer_id)",
|
||||
"CREATE INDEX IF NOT EXISTS idx_lcr_proposals_status ON leaudit_cross_review_proposals (status)",
|
||||
"""
|
||||
CREATE TABLE IF NOT EXISTS leaudit_cross_review_votes (
|
||||
id BIGINT GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
|
||||
proposal_id BIGINT NOT NULL,
|
||||
voter_id BIGINT NOT NULL,
|
||||
vote_type VARCHAR(16) NOT NULL,
|
||||
create_time TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
update_time TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
delete_time TIMESTAMPTZ
|
||||
)
|
||||
""",
|
||||
"CREATE INDEX IF NOT EXISTS idx_lcr_votes_proposal_id ON leaudit_cross_review_votes (proposal_id)",
|
||||
"CREATE INDEX IF NOT EXISTS idx_lcr_votes_voter_id ON leaudit_cross_review_votes (voter_id)",
|
||||
"""
|
||||
CREATE UNIQUE INDEX IF NOT EXISTS uq_lcr_votes_proposal_voter_active
|
||||
ON leaudit_cross_review_votes (proposal_id, voter_id)
|
||||
WHERE delete_time IS NULL
|
||||
""",
|
||||
)
|
||||
|
||||
def __init__(self):
|
||||
self.DocumentService: IDocumentService = DocumentServiceImpl()
|
||||
|
||||
@@ -56,6 +152,7 @@ class CrossReviewServiceImpl(ICrossReviewService):
|
||||
principalUserIds = self._unique_int_list(Body.principalUserIds)
|
||||
documentIds = self._unique_int_list(Body.documentIds)
|
||||
|
||||
await self._reset_transaction_for_write(session)
|
||||
async with session.begin():
|
||||
taskRow = (
|
||||
await session.execute(
|
||||
@@ -400,6 +497,7 @@ class CrossReviewServiceImpl(ICrossReviewService):
|
||||
if not permission.canConfirm:
|
||||
raise LeauditException(StatusCodeEnum.HTTP_403_FORBIDDEN, permission.reason)
|
||||
|
||||
await self._reset_transaction_for_write(session)
|
||||
async with session.begin():
|
||||
mapping = (
|
||||
await session.execute(
|
||||
@@ -517,6 +615,7 @@ class CrossReviewServiceImpl(ICrossReviewService):
|
||||
if Body.deductionScore > 0 and currentScore >= fullScore:
|
||||
raise LeauditException(StatusCodeEnum.HTTP_400_BAD_REQUEST, "当前分值已满分,不能继续加分")
|
||||
|
||||
await self._reset_transaction_for_write(session)
|
||||
async with session.begin():
|
||||
proposalRow = (
|
||||
await session.execute(
|
||||
@@ -576,6 +675,7 @@ class CrossReviewServiceImpl(ICrossReviewService):
|
||||
if str(proposal["status"]) in {"approved", "rejected", "cancelled"}:
|
||||
raise LeauditException(StatusCodeEnum.HTTP_400_BAD_REQUEST, "当前提案状态不允许继续投票")
|
||||
|
||||
await self._reset_transaction_for_write(session)
|
||||
async with session.begin():
|
||||
if voteType == "cancel":
|
||||
deleted = await session.execute(
|
||||
@@ -638,6 +738,7 @@ class CrossReviewServiceImpl(ICrossReviewService):
|
||||
if str(proposal["status"]) not in {"pending"}:
|
||||
raise LeauditException(StatusCodeEnum.HTTP_400_BAD_REQUEST, "当前提案状态不允许撤销")
|
||||
|
||||
await self._reset_transaction_for_write(session)
|
||||
async with session.begin():
|
||||
await session.execute(
|
||||
text(
|
||||
@@ -745,6 +846,7 @@ class CrossReviewServiceImpl(ICrossReviewService):
|
||||
|
||||
async with GetAsyncSession() as session:
|
||||
await self._ensure_tables_ready(session)
|
||||
await self._reset_transaction_for_write(session)
|
||||
async with session.begin():
|
||||
exists = bool(
|
||||
await session.scalar(
|
||||
@@ -1153,6 +1255,31 @@ class CrossReviewServiceImpl(ICrossReviewService):
|
||||
"leaudit_cross_review_proposals",
|
||||
"leaudit_cross_review_votes",
|
||||
]
|
||||
missing_tables: list[str] = []
|
||||
for tableName in required:
|
||||
exists = bool(
|
||||
await session.scalar(
|
||||
text(
|
||||
"""
|
||||
SELECT EXISTS (
|
||||
SELECT 1
|
||||
FROM information_schema.tables
|
||||
WHERE table_schema = current_schema()
|
||||
AND table_name = :table_name
|
||||
)
|
||||
"""
|
||||
),
|
||||
{"table_name": tableName},
|
||||
)
|
||||
)
|
||||
if not exists:
|
||||
missing_tables.append(tableName)
|
||||
|
||||
if missing_tables:
|
||||
for statement in self._SCHEMA_BOOTSTRAP_STATEMENTS:
|
||||
await session.execute(text(statement))
|
||||
await session.commit()
|
||||
|
||||
for tableName in required:
|
||||
exists = bool(
|
||||
await session.scalar(
|
||||
@@ -1195,6 +1322,11 @@ class CrossReviewServiceImpl(ICrossReviewService):
|
||||
if not exists:
|
||||
raise LeauditException(StatusCodeEnum.HTTP_403_FORBIDDEN, "当前用户不是交叉评查任务成员")
|
||||
|
||||
async def _reset_transaction_for_write(self, session) -> None:
|
||||
"""显式写事务前清理查询阶段开启的隐式事务。"""
|
||||
if session.in_transaction():
|
||||
await session.rollback()
|
||||
|
||||
def _unique_int_list(self, values: list[int]) -> list[int]:
|
||||
"""去重并保留原顺序。"""
|
||||
seen: set[int] = set()
|
||||
|
||||
Reference in New Issue
Block a user