新增风险项撤回接口和审计记录
This commit is contained in:
parent
e965aad724
commit
6559386871
@ -49,6 +49,16 @@ async def update_risk_item(
|
|||||||
return ApiResponse(data=await RiskItemService(session).update(item_id, payload, current_user))
|
return ApiResponse(data=await RiskItemService(session).update(item_id, payload, current_user))
|
||||||
|
|
||||||
|
|
||||||
|
@router.post("/{item_id}/withdraw", response_model=ApiResponse[None])
|
||||||
|
async def withdraw_risk_item(
|
||||||
|
item_id: int,
|
||||||
|
session: DatabaseSession,
|
||||||
|
current_user: Annotated[UserAccount, Depends(require_permission("risk_item.review", operate=True))],
|
||||||
|
) -> ApiResponse[None]:
|
||||||
|
await RiskItemService(session).withdraw(item_id, current_user)
|
||||||
|
return ApiResponse(data=None, message="风险项已撤回")
|
||||||
|
|
||||||
|
|
||||||
@router.post("/{item_id}/review", response_model=ApiResponse[ReviewRecordData])
|
@router.post("/{item_id}/review", response_model=ApiResponse[ReviewRecordData])
|
||||||
async def review_risk_item(
|
async def review_risk_item(
|
||||||
item_id: int,
|
item_id: int,
|
||||||
|
|||||||
@ -68,6 +68,8 @@ class RiskItemService:
|
|||||||
filters = []
|
filters = []
|
||||||
if status != "all":
|
if status != "all":
|
||||||
filters.append(RiskItem.status == status)
|
filters.append(RiskItem.status == status)
|
||||||
|
else:
|
||||||
|
filters.append(RiskItem.status != "已撤回")
|
||||||
if source_type != "all":
|
if source_type != "all":
|
||||||
filters.append(RiskItem.source_type == source_type)
|
filters.append(RiskItem.source_type == source_type)
|
||||||
statement = select(RiskItem).where(*filters).order_by(RiskItem.created_at.desc()).limit(limit)
|
statement = select(RiskItem).where(*filters).order_by(RiskItem.created_at.desc()).limit(limit)
|
||||||
@ -105,6 +107,26 @@ class RiskItemService:
|
|||||||
await self.session.flush()
|
await self.session.flush()
|
||||||
return self.to_data(item)
|
return self.to_data(item)
|
||||||
|
|
||||||
|
async def withdraw(self, item_id: int, operator: UserAccount) -> None:
|
||||||
|
item = await self.session.get(RiskItem, item_id)
|
||||||
|
if item is None:
|
||||||
|
raise AppException("风险项不存在", code=404, status_code=404)
|
||||||
|
if item.status not in {"待审核", "待预审"}:
|
||||||
|
raise AppException("只有待处理风险项可以撤回", code=409, status_code=409)
|
||||||
|
previous_status = item.status
|
||||||
|
item.status = "已撤回"
|
||||||
|
self.session.add(
|
||||||
|
OperationAudit(
|
||||||
|
actor_user_id=operator.id,
|
||||||
|
actor_emp_no=operator.emp_no,
|
||||||
|
action="risk_item.withdraw",
|
||||||
|
resource_type="risk_item",
|
||||||
|
resource_id=str(item.id),
|
||||||
|
audit_metadata={"previous_status": previous_status},
|
||||||
|
)
|
||||||
|
)
|
||||||
|
await self.session.flush()
|
||||||
|
|
||||||
async def review(self, item_id: int, action: str, note: str, reviewer: UserAccount) -> ReviewRecordData:
|
async def review(self, item_id: int, action: str, note: str, reviewer: UserAccount) -> ReviewRecordData:
|
||||||
item = await self.session.get(RiskItem, item_id)
|
item = await self.session.get(RiskItem, item_id)
|
||||||
if item is None:
|
if item is None:
|
||||||
|
|||||||
38
tests/test_risk_item_withdraw.py
Normal file
38
tests/test_risk_item_withdraw.py
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
from unittest.mock import AsyncMock, MagicMock
|
||||||
|
|
||||||
|
import pytest
|
||||||
|
|
||||||
|
from app.core.exceptions import AppException
|
||||||
|
from app.models.entities import OperationAudit, RiskItem, UserAccount
|
||||||
|
from app.services.risk_items import RiskItemService
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.asyncio
|
||||||
|
async def test_pending_risk_item_can_be_withdrawn_with_audit() -> None:
|
||||||
|
item = RiskItem(id=12, source_type="表单", status="待审核", photos=[], ocr_payload={})
|
||||||
|
operator = UserAccount(id=3, emp_no="admin-1", name="管理员", role="admin", status="启用")
|
||||||
|
session = MagicMock()
|
||||||
|
session.get = AsyncMock(return_value=item)
|
||||||
|
session.flush = AsyncMock()
|
||||||
|
|
||||||
|
await RiskItemService(session).withdraw(item.id, operator)
|
||||||
|
|
||||||
|
assert item.status == "已撤回"
|
||||||
|
audit = next(call.args[0] for call in session.add.call_args_list if isinstance(call.args[0], OperationAudit))
|
||||||
|
assert audit.action == "risk_item.withdraw"
|
||||||
|
assert audit.audit_metadata == {"previous_status": "待审核"}
|
||||||
|
session.flush.assert_awaited_once()
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.asyncio
|
||||||
|
async def test_reviewed_risk_item_cannot_be_withdrawn() -> None:
|
||||||
|
item = RiskItem(id=13, source_type="表单", status="已通过", photos=[], ocr_payload={})
|
||||||
|
operator = UserAccount(id=3, emp_no="admin-1", name="管理员", role="admin", status="启用")
|
||||||
|
session = MagicMock()
|
||||||
|
session.get = AsyncMock(return_value=item)
|
||||||
|
|
||||||
|
with pytest.raises(AppException, match="只有待处理风险项可以撤回"):
|
||||||
|
await RiskItemService(session).withdraw(item.id, operator)
|
||||||
|
|
||||||
|
session.add.assert_not_called()
|
||||||
|
|
||||||
Loading…
x
Reference in New Issue
Block a user