dingdanquanliucheng/backend/app/services/ai_service.py

137 lines
5.1 KiB
Python

import json
from sqlalchemy.exc import SQLAlchemyError
from sqlalchemy.orm import Session
from backend.app.core.error_codes import ErrorCode
from backend.app.core.exceptions import AppException
from backend.app.repositories.ai_repository import AIRepository
from backend.app.services.audit_service import audit_service
class AIService:
def __init__(self) -> None:
self.repository = AIRepository()
def recognize_image(self, payload: dict, session: Session | None = None) -> dict:
image_url = (payload.get("image_url") or "").strip()
biz_type = (payload.get("biz_type") or "").strip()
biz_id = payload.get("biz_id")
if not image_url:
raise AppException(code=ErrorCode.PARAM_ERROR, message="图片地址不能为空", status_code=400)
if not biz_type:
raise AppException(code=ErrorCode.PARAM_ERROR, message="业务类型不能为空", status_code=400)
if image_url.startswith("mock://fail"):
raise AppException(code=ErrorCode.THIRD_PARTY_FAILED, message="AI 识别失败", status_code=400)
suggested_result = self._build_suggested_result(image_url, biz_type, biz_id)
raw_result = {
"provider": "mock_aliyun_adapter",
"image_url": image_url,
"fields": suggested_result,
}
confidence = 0.92
if session is not None:
try:
log = self.repository.create_log(
session,
{
"biz_type": biz_type,
"biz_id": biz_id,
"image_url": image_url,
"raw_result": json.dumps(raw_result, ensure_ascii=False),
"confidence": confidence,
"corrected_result": None,
"created_by": None,
},
)
audit_service.write_log(
session,
{
"operate_type": "ai_recognize",
"biz_type": biz_type,
"biz_id": biz_id,
"before_value": None,
"after_value": {
"log_id": log.id,
"confidence": confidence,
"suggested_result": suggested_result,
},
"remark": f"AI识别图片 {image_url}",
},
)
session.commit()
return {
"log_id": log.id,
"raw_result": raw_result,
"confidence": confidence,
"suggested_result": suggested_result,
}
except AppException:
session.rollback()
raise
except SQLAlchemyError:
session.rollback()
return {
"log_id": 7001,
"raw_result": raw_result,
"confidence": confidence,
"suggested_result": suggested_result,
}
def correct_result(self, log_id: int, payload: dict, session: Session | None = None) -> dict:
corrected_result = payload.get("corrected_result")
if not isinstance(corrected_result, dict) or not corrected_result:
raise AppException(code=ErrorCode.PARAM_ERROR, message="修正结果不能为空", status_code=400)
if session is not None:
try:
log = self.repository.get_log(session, log_id)
if log is None:
raise AppException(code=ErrorCode.NOT_FOUND, message="识别记录不存在", status_code=404)
before_value = self._safe_load_json(log.corrected_result)
log.corrected_result = json.dumps(corrected_result, ensure_ascii=False)
session.add(log)
audit_service.write_log(
session,
{
"operate_type": "ai_correct",
"biz_type": log.biz_type,
"biz_id": log.biz_id,
"before_value": before_value,
"after_value": corrected_result,
"remark": f"修正AI识别结果 {log_id}",
},
)
session.commit()
return {"log_id": log.id, "corrected": True}
except AppException:
session.rollback()
raise
except SQLAlchemyError:
session.rollback()
return {"log_id": log_id, "corrected": True}
def _build_suggested_result(self, image_url: str, biz_type: str, biz_id: int) -> dict:
tail = image_url.rsplit("/", 1)[-1]
return {
"image_name": tail,
"biz_type": biz_type,
"biz_id": biz_id,
}
def _safe_load_json(self, value: str | None):
if not value:
return None
try:
return json.loads(value)
except json.JSONDecodeError:
return value
ai_service = AIService()