Files
fquiz/api/app/exceptions/menu_exceptions.py
T
chengkai3 e67475aad6 [fix]:[FL-198][菜单管理 - 创建失败且错误信息不明确]
## 改动摘要
- 新增自定义异常类用于菜单验证错误的精确识别
- 修改 create_menu 服务函数,使用异常替代返回 None
- 修改 API 端点捕获异常并返回详细错误信息

## 详细改动

### 1. 新增异常类 (api/app/exceptions/menu_exceptions.py)
创建了以下异常类以区分不同的验证失败场景:
- `MenuValidationError`: 基础菜单验证异常类
- `EmptyMenuCodeError`: 菜单编码为空
- `EmptyMenuNameError`: 菜单名称为空
- `DuplicateMenuCodeError`: 菜单编码重复
- `RemovedMenuCodeError`: 使用已移除的菜单编码
- `SelfParentError`: 菜单将自己设为父菜单
- `ParentNotFoundError`: 父菜单不存在

### 2. 修改服务层 (api/app/services/legacy_admin_rbac_service.py)
- 在 create_menu 函数中,将所有返回 None 的地方替换为抛出对应的异常
- 为数据库异常添加更具体的错误上下文
- 修改返回类型从 `MenuPublic | None` 为 `MenuPublic`

### 3. 修改 API 端点 (api/app/api/v1/admin.py)
- 在 create_menu_endpoint 中捕获 MenuValidationError 异常
- 返回详细的错误信息,包括具体的字段名(如有)
- 替换原来的模糊错误信息"Invalid menu payload or duplicate menu code"

## 测试验证
- 已通过 Python 语法检查
- 所有修改的文件编译通过
- 异常类可以正常导入

## 解决的问题
修复了 Issue FL-198 中描述的问题:
- 之前:所有创建失败都返回同一个模糊错误"Invalid menu payload or duplicate menu code"
- 现在:返回具体的错误原因,如"菜单编码 'xxx' 已存在 (字段: code)"、"父菜单 'xxx' 不存在 (字段: parent_id)"等

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-authored-by: multica-agent <github@multica.ai>
2026-06-18 12:43:37 +08:00

49 lines
1.6 KiB
Python

"""Menu-related exceptions for detailed error reporting."""
class MenuValidationError(Exception):
"""Base exception for menu validation errors."""
def __init__(self, message: str, field: str | None = None):
self.message = message
self.field = field
super().__init__(message)
class EmptyMenuCodeError(MenuValidationError):
"""Raised when menu code is empty."""
def __init__(self):
super().__init__("菜单编码不能为空", "code")
class EmptyMenuNameError(MenuValidationError):
"""Raised when menu name is empty."""
def __init__(self):
super().__init__("菜单名称不能为空", "name")
class DuplicateMenuCodeError(MenuValidationError):
"""Raised when menu code already exists."""
def __init__(self, code: str):
super().__init__(f"菜单编码 '{code}' 已存在", "code")
self.code = code
class RemovedMenuCodeError(MenuValidationError):
"""Raised when attempting to use a removed menu code."""
def __init__(self, code: str):
super().__init__(f"菜单编码 '{code}' 已被系统移除,不能使用", "code")
self.code = code
class SelfParentError(MenuValidationError):
"""Raised when menu tries to set itself as parent."""
def __init__(self):
super().__init__("菜单不能将自己设为父菜单", "parent_id")
class ParentNotFoundError(MenuValidationError):
"""Raised when specified parent menu does not exist."""
def __init__(self, parent_id: str):
super().__init__(f"父菜单 '{parent_id}' 不存在", "parent_id")
self.parent_id = parent_id