#!/usr/bin/env python # -*- coding: utf-8 -*- """ 表单数据操作 — 细粒度异常体系 异常层次: FormDataException (所有表单数据异常的基类) ├── FormNotFoundException (表单不存在) ├── RecordNotFoundException (记录不存在) ├── FormDataValidationError (数据校验失败的基类) │ ├── UniqueConstraintError (唯一约束冲突) │ ├── NotNullConstraintError (非空约束违反) │ ├── CheckConstraintError (CHECK 约束违反) │ ├── ForeignKeyConstraintError (外键约束违反) │ ├── DataTypeMismatchError (数据类型不匹配) │ └── DataTooLongError (数据超长) ├── FormDataSchemaError (表结构/配置错误的基类) │ ├── TableNotFoundError (表不存在) │ └── ColumnNotFoundError (列不存在) ├── FormDataConnectionError (连接相关错误的基类) │ ├── DatabaseConnectionError (连接失败/断开) │ ├── ConnectionPoolExhaustedError (连接池耗尽) │ └── ConnectionTimeoutError (连接超时) ├── FormDataConcurrencyError (并发相关错误的基类) │ ├── DeadlockError (死锁) │ └── LockTimeoutError (锁等待超时) ├── QueryError (SQL 查询构建/执行错误) └── InternalDatabaseError (其他数据库内部错误) 每个异常类携带 error_code 和结构化的上下文信息 (context), 方便 API 层映射到 HTTP 响应 + 前端根据 error_code 做差异化处理。 """ from __future__ import annotations from typing import Any, Dict, Optional # ===================================================================== # 基类 # ===================================================================== class FormDataException(Exception): """表单数据操作异常基类""" error_code: str = "FORM_DATA_ERROR" http_status: int = 400 def __init__( self, message: str, *, error_code: Optional[str] = None, http_status: Optional[int] = None, context: Optional[Dict[str, Any]] = None, ): super().__init__(message) if error_code is not None: self.error_code = error_code if http_status is not None: self.http_status = http_status self.context: Dict[str, Any] = context or {} # ===================================================================== # 资源不存在 # ===================================================================== class FormNotFoundException(FormDataException): """表单定义不存在""" error_code = "FORM_NOT_FOUND" http_status = 404 def __init__(self, form_code: str): super().__init__( f"表单不存在: {form_code}", context={"form_code": form_code}, ) class RecordNotFoundException(FormDataException): """记录不存在""" error_code = "RECORD_NOT_FOUND" http_status = 404 def __init__(self, pk: Any, *, table: str = ""): super().__init__( f"数据不存在: {pk}", context={"pk": str(pk), "table": table}, ) # ===================================================================== # 数据校验 / 约束违反 # ===================================================================== class FormDataValidationError(FormDataException): """数据校验失败的基类""" error_code = "VALIDATION_ERROR" http_status = 400 class UniqueConstraintError(FormDataValidationError): """唯一约束冲突(含重复数据信息)""" error_code = "UNIQUE_CONSTRAINT_VIOLATION" def __init__( self, message: str = "数据重复,违反唯一约束", *, fields: Optional[list[str]] = None, constraint_name: str = "", ): super().__init__( message, context={"fields": fields or [], "constraint_name": constraint_name}, ) class NotNullConstraintError(FormDataValidationError): """非空约束违反""" error_code = "NOT_NULL_VIOLATION" def __init__(self, column: str = ""): msg = f"字段 '{column}' 不能为空" if column else "存在必填字段为空" super().__init__(msg, context={"column": column}) class CheckConstraintError(FormDataValidationError): """CHECK 约束违反""" error_code = "CHECK_CONSTRAINT_VIOLATION" def __init__(self, constraint_name: str = "", detail: str = ""): msg = "数据不满足校验规则" if constraint_name: msg += f" ({constraint_name})" super().__init__(msg, context={"constraint_name": constraint_name, "detail": detail}) class ForeignKeyConstraintError(FormDataValidationError): """外键约束违反(删除/更新时关联数据存在)""" error_code = "FOREIGN_KEY_VIOLATION" def __init__( self, message: str = "", *, detail: str = "", referenced_table: str = "", constraint_name: str = "", ): if not message: message = "操作失败,该数据被其他数据引用" if referenced_table: message = ( f"无法删除,该数据已被「{referenced_table}」引用," "请先删除或解除关联数据" ) elif detail: message += f"({detail})" super().__init__( message, context={ "detail": detail, "referenced_table": referenced_table, "constraint_name": constraint_name, }, ) class DataTypeMismatchError(FormDataValidationError): """数据类型不匹配""" error_code = "DATA_TYPE_MISMATCH" def __init__(self, detail: str = ""): msg = "数据类型不匹配,请检查输入数据格式" if detail: msg += f"({detail})" super().__init__(msg, context={"detail": detail}) class DataTooLongError(FormDataValidationError): """数据长度超出字段限制""" error_code = "DATA_TOO_LONG" def __init__(self, column: str = "", max_length: int = 0): msg = f"字段 '{column}' 的数据过长" if column else "数据长度超出限制" if max_length: msg += f"(最大 {max_length} 个字符)" super().__init__( msg, context={"column": column, "max_length": max_length}, ) # ===================================================================== # 表结构 / 配置错误 # ===================================================================== class FormDataSchemaError(FormDataException): """表结构或配置相关错误基类""" error_code = "SCHEMA_ERROR" http_status = 400 class TableNotFoundError(FormDataSchemaError): """数据库表不存在""" error_code = "TABLE_NOT_FOUND" def __init__(self, table: str = ""): msg = f"数据库表 {table} 不存在,请检查数据源配置" if table else "数据库表不存在" super().__init__(msg, context={"table": table}) class ColumnNotFoundError(FormDataSchemaError): """数据库列不存在""" error_code = "COLUMN_NOT_FOUND" def __init__(self, column: str = "", table: str = ""): msg = f"数据库字段 '{column}' 不存在" if column else "数据库字段不存在" msg += ",请检查表单配置与数据库表结构是否一致" super().__init__(msg, context={"column": column, "table": table}) # ===================================================================== # 连接相关 # ===================================================================== class FormDataConnectionError(FormDataException): """数据库连接相关错误基类""" error_code = "CONNECTION_ERROR" http_status = 503 class DatabaseConnectionError(FormDataConnectionError): """数据库连接失败或断开""" error_code = "DB_CONNECTION_FAILED" def __init__(self, detail: str = ""): msg = "数据库连接异常,请稍后重试" super().__init__(msg, context={"detail": detail}) class ConnectionPoolExhaustedError(FormDataConnectionError): """连接池耗尽""" error_code = "CONNECTION_POOL_EXHAUSTED" def __init__(self): super().__init__( "服务器繁忙(数据库连接池已满),请稍后重试", ) class ConnectionTimeoutError(FormDataConnectionError): """连接超时""" error_code = "CONNECTION_TIMEOUT" def __init__(self): super().__init__("数据库连接超时,请稍后重试") # ===================================================================== # 并发相关 # ===================================================================== class FormDataConcurrencyError(FormDataException): """并发相关错误基类""" error_code = "CONCURRENCY_ERROR" http_status = 409 class DeadlockError(FormDataConcurrencyError): """死锁""" error_code = "DEADLOCK" def __init__(self): super().__init__("操作冲突(死锁),请重试") class LockTimeoutError(FormDataConcurrencyError): """锁等待超时""" error_code = "LOCK_TIMEOUT" def __init__(self): super().__init__("操作等待超时,请稍后重试") # ===================================================================== # 查询 / 其他 # ===================================================================== class QueryError(FormDataException): """SQL 查询构建或执行错误""" error_code = "QUERY_ERROR" http_status = 400 def __init__(self, detail: str = ""): msg = "数据库查询异常,请检查表单配置是否正确" super().__init__(msg, context={"detail": detail}) class InternalDatabaseError(FormDataException): """其他未分类的数据库内部错误""" error_code = "INTERNAL_DB_ERROR" http_status = 500 def __init__(self, detail: str = ""): msg = "数据库操作失败,请联系管理员" super().__init__(msg, context={"detail": detail})