refactor: 移除审批和回写功能模块

移除了整个审批(approvals)和官方回写(writebacks)功能模块,
包括相关模型、路由、服务和配置项。更新了数据库迁移文件,
删除了相关的审批请求表和官方回写运行表。同时从API路由器中
移除了相应的路由,并调整了安全常量和字段验证器以匹配变更。
```
This commit is contained in:
2026-07-08 17:08:59 +08:00
parent 19e59e83cc
commit 0a153b264a
48 changed files with 135 additions and 2225 deletions

View File

@@ -10,21 +10,14 @@ from sqlalchemy import Select, func, select
from sqlalchemy.sql.schema import Column
from sqlalchemy.orm import Session
from app.core.constants import ActorValue
from app.core.pagination import bounded_limit, bounded_offset
from app.modules.audit.constants import AuditRiskLevel, AuditSource
from app.modules.audit.schemas import AuditLogCreate
from app.modules.audit.service import AuditService
from app.modules.approvals.constants import ApprovalActionValue, approval_action
from app.modules.approvals.service import ApprovalService
from app.modules.business.registry import get_domain_model, get_writable_fields, is_high_risk_domain
from app.modules.business.registry import get_domain_model, get_writable_fields
from app.modules.business.constants import (
INVALID_FIELD_VALUE_TEMPLATE,
READ_ONLY_FIELD_TEMPLATE,
UNKNOWN_FIELD_TEMPLATE,
BusinessErrorDetail,
BusinessField,
BusinessPayloadKey,
)
@@ -90,11 +83,10 @@ def _model_payload(domain: str, model: Any, data: dict[str, Any]) -> dict[str, A
class BusinessService:
"""Manage generic CRUD operations across registered business domains."""
"""Read business records across registered domains."""
def __init__(self, db: Session):
self.db = db
self.audit = AuditService(db)
def list_records(
self,
@@ -124,116 +116,3 @@ class BusinessService:
detail=BusinessErrorDetail.RECORD_NOT_FOUND,
)
return serialize_model(record)
def create_record(
self,
domain: str,
data: dict[str, Any],
actor: str = ActorValue.API,
approval_ticket_id: str | None = None,
) -> dict[str, Any]:
model = get_domain_model(domain)
high_risk = is_high_risk_domain(domain)
payload = _model_payload(domain, model, data)
record = model(**payload)
self.db.add(record)
if high_risk:
self.db.flush()
self._consume_approval(
approval_ticket_id,
domain,
record.id,
approval_action(ApprovalActionValue.CREATE, domain),
data,
actor,
)
self.db.commit()
self.db.refresh(record)
result = serialize_model(record)
self.audit.log(
AuditLogCreate(
actor=actor,
source=AuditSource.API,
action=approval_action(ApprovalActionValue.CREATE, domain),
target_type=domain,
target_id=str(record.id),
risk_level=AuditRiskLevel.HIGH if high_risk else AuditRiskLevel.LOW,
request_payload={
BusinessPayloadKey.DATA: data,
BusinessPayloadKey.APPROVAL_TICKET_ID: approval_ticket_id,
},
response_payload=result,
)
)
return result
def update_record(
self,
domain: str,
record_id: int,
data: dict[str, Any],
actor: str = ActorValue.API,
approval_ticket_id: str | None = None,
) -> dict[str, Any]:
model = get_domain_model(domain)
high_risk = is_high_risk_domain(domain)
record = self.db.get(model, record_id)
if record is None:
raise HTTPException(
status_code=status.HTTP_404_NOT_FOUND,
detail=BusinessErrorDetail.RECORD_NOT_FOUND,
)
payload = _model_payload(domain, model, data)
if high_risk:
self._consume_approval(
approval_ticket_id,
domain,
record_id,
approval_action(ApprovalActionValue.UPDATE, domain),
data,
actor,
)
for key, value in payload.items():
setattr(record, key, value)
self.db.commit()
self.db.refresh(record)
result = serialize_model(record)
self.audit.log(
AuditLogCreate(
actor=actor,
source=AuditSource.API,
action=approval_action(ApprovalActionValue.UPDATE, domain),
target_type=domain,
target_id=str(record.id),
risk_level=AuditRiskLevel.HIGH if high_risk else AuditRiskLevel.LOW,
request_payload={
BusinessPayloadKey.DATA: data,
BusinessPayloadKey.APPROVAL_TICKET_ID: approval_ticket_id,
},
response_payload=result,
)
)
return result
def _consume_approval(
self,
approval_ticket_id: str | None,
domain: str,
record_id: str | int | None,
action: str,
payload: dict[str, Any],
actor: str,
) -> None:
if not approval_ticket_id:
raise HTTPException(
status_code=status.HTTP_409_CONFLICT,
detail=BusinessErrorDetail.HIGH_RISK_APPROVAL_REQUIRED,
)
ApprovalService(self.db).consume_for(
approval_ticket_id,
domain,
record_id,
action,
payload,
actor,
)