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()