33b4c734aa
后端: - 新增认证(auth)、任务(tasks)、映射(mappings)、对账(reconciliation)、异常(exceptions)、导出(exports) API - 新增核心模块: database, security, permissions, tenant, exceptions, error_handlers - 新增数据模型: user, company, reconciliation_task, field_mapping, uploaded_file 等 - 新增服务层: ai_recognizer, file_parser, file_storage, mapping, reconciliation 等 - 添加数据库迁移脚本 前端: - 新增登录页面和仪表盘页面 - 新增任务列表、任务详情、字段映射页面 - 新增异常处理页面和规则设置页面 - 新增 API 代理路由 /api/[...path] - 新增 UI 组件库 (button, card, dialog, input, table 等) - 新增 auth 组件 (ProtectedRoute, PermissionGate) - 新增 layout 组件 (Header, Sidebar) - 新增 mapping 组件 (FieldMappingTable, AISuggestionPanel) - 新增 API 客户端和 hooks (useAsync, useToast, usePermission 等) - 新增状态管理 (auth-store, company-store, ui-store) - 集成 Tailwind CSS 和 shadcn/ui 组件库 其他: - 添加 Alembic 数据库迁移配置 - 添加初始化示例数据脚本 - 更新项目文档
191 lines
4.5 KiB
Python
191 lines
4.5 KiB
Python
from typing import Any
|
|
|
|
from fastapi import HTTPException, Request, status
|
|
from fastapi.exceptions import RequestValidationError
|
|
from fastapi.responses import JSONResponse
|
|
from sqlalchemy.exc import IntegrityError
|
|
|
|
from app.core.exceptions import (
|
|
BusinessException,
|
|
ExternalServiceException,
|
|
ForbiddenException,
|
|
NotFoundException,
|
|
S2FException,
|
|
UnauthorizedException,
|
|
ValidationException,
|
|
)
|
|
from app.core.logging import logger
|
|
|
|
|
|
def create_error_response(
|
|
code: str,
|
|
message: str,
|
|
details: Any = None,
|
|
status_code: int = status.HTTP_500_INTERNAL_SERVER_ERROR,
|
|
) -> JSONResponse:
|
|
"""
|
|
创建统一错误响应
|
|
|
|
Args:
|
|
code: 错误代码
|
|
message: 错误消息
|
|
details: 错误详情
|
|
status_code: HTTP 状态码
|
|
|
|
Returns:
|
|
JSON 响应
|
|
"""
|
|
error_data = {
|
|
"error": {
|
|
"code": code,
|
|
"message": message,
|
|
}
|
|
}
|
|
|
|
if details:
|
|
error_data["error"]["details"] = details
|
|
|
|
return JSONResponse(
|
|
status_code=status_code,
|
|
content=error_data,
|
|
)
|
|
|
|
|
|
async def s2f_exception_handler(request: Request, exc: S2FException) -> JSONResponse:
|
|
"""
|
|
处理自定义业务异常
|
|
|
|
Args:
|
|
request: 请求对象
|
|
exc: 异常对象
|
|
|
|
Returns:
|
|
JSON 响应
|
|
"""
|
|
logger.warning(
|
|
"business_exception",
|
|
code=exc.code,
|
|
message=exc.message,
|
|
details=exc.details,
|
|
path=request.url.path,
|
|
)
|
|
|
|
# 根据异常类型映射 HTTP 状态码
|
|
status_code_map = {
|
|
NotFoundException: status.HTTP_404_NOT_FOUND,
|
|
UnauthorizedException: status.HTTP_401_UNAUTHORIZED,
|
|
ForbiddenException: status.HTTP_403_FORBIDDEN,
|
|
ValidationException: status.HTTP_422_UNPROCESSABLE_ENTITY,
|
|
BusinessException: status.HTTP_400_BAD_REQUEST,
|
|
ExternalServiceException: status.HTTP_503_SERVICE_UNAVAILABLE,
|
|
}
|
|
|
|
status_code = status_code_map.get(type(exc), status.HTTP_500_INTERNAL_SERVER_ERROR)
|
|
|
|
return create_error_response(
|
|
code=exc.code,
|
|
message=exc.message,
|
|
details=exc.details,
|
|
status_code=status_code,
|
|
)
|
|
|
|
|
|
async def validation_exception_handler(request: Request, exc: RequestValidationError) -> JSONResponse:
|
|
"""
|
|
处理请求验证异常
|
|
|
|
Args:
|
|
request: 请求对象
|
|
exc: 验证异常
|
|
|
|
Returns:
|
|
JSON 响应
|
|
"""
|
|
logger.warning(
|
|
"validation_error",
|
|
errors=exc.errors(),
|
|
body=exc.body,
|
|
path=request.url.path,
|
|
)
|
|
|
|
return create_error_response(
|
|
code="VALIDATION_ERROR",
|
|
message="请求参数验证失败",
|
|
details={"errors": exc.errors()},
|
|
status_code=status.HTTP_422_UNPROCESSABLE_ENTITY,
|
|
)
|
|
|
|
|
|
async def http_exception_handler(request: Request, exc: HTTPException) -> JSONResponse:
|
|
"""
|
|
处理 HTTP 异常
|
|
|
|
Args:
|
|
request: 请求对象
|
|
exc: HTTP 异常
|
|
|
|
Returns:
|
|
JSON 响应
|
|
"""
|
|
logger.warning(
|
|
"http_exception",
|
|
status_code=exc.status_code,
|
|
detail=exc.detail,
|
|
path=request.url.path,
|
|
)
|
|
|
|
return create_error_response(
|
|
code="HTTP_ERROR",
|
|
message=str(exc.detail),
|
|
status_code=exc.status_code,
|
|
)
|
|
|
|
|
|
async def integrity_error_handler(request: Request, exc: IntegrityError) -> JSONResponse:
|
|
"""
|
|
处理数据库完整性错误
|
|
|
|
Args:
|
|
request: 请求对象
|
|
exc: 完整性错误
|
|
|
|
Returns:
|
|
JSON 响应
|
|
"""
|
|
logger.error(
|
|
"database_integrity_error",
|
|
error=str(exc),
|
|
path=request.url.path,
|
|
)
|
|
|
|
return create_error_response(
|
|
code="DATABASE_ERROR",
|
|
message="数据库操作失败,可能存在重复或约束冲突",
|
|
status_code=status.HTTP_409_CONFLICT,
|
|
)
|
|
|
|
|
|
async def generic_exception_handler(request: Request, exc: Exception) -> JSONResponse:
|
|
"""
|
|
处理未捕获的异常
|
|
|
|
Args:
|
|
request: 请求对象
|
|
exc: 异常对象
|
|
|
|
Returns:
|
|
JSON 响应
|
|
"""
|
|
logger.error(
|
|
"unhandled_exception",
|
|
error=str(exc),
|
|
error_type=type(exc).__name__,
|
|
path=request.url.path,
|
|
exc_info=True,
|
|
)
|
|
|
|
return create_error_response(
|
|
code="INTERNAL_ERROR",
|
|
message="服务器内部错误",
|
|
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
|
|
) |