第35章:异常体系设计
35.1 概述
异常体系是剪映小助手的错误处理基础框架,通过统一的错误码和异常类设计,为系统提供标准化的错误处理机制。该体系采用Python枚举类定义错误码,支持中英文错误消息转换,覆盖从基础错误到业务错误的完整错误场景,确保系统的稳定性和可维护性。
35.2 错误码体系设计
35.2.1 错误码分类
系统将错误码分为三个主要类别:
class CustomError(Enum):
# 基础错误码 (1000-1999)
INVALID_PARAMETER = (1000, "参数错误", "Invalid parameter")
MISSING_REQUIRED_PARAMETER = (1001, "缺少必填参数", "Missing required parameter")
PARAMETER_TYPE_ERROR = (1002, "参数类型错误", "Parameter type error")
PARAMETER_OUT_OF_RANGE = (1003, "参数超出范围", "Parameter out of range")
# 业务错误码 (2000-2999)
DRAFT_NOT_FOUND = (2000, "草稿不存在", "Draft not found")
VIDEO_NOT_FOUND = (2001, "视频不存在", "Video not found")
AUDIO_NOT_FOUND = (2002, "音频不存在", "Audio not found")
EFFECT_NOT_FOUND = (2003, "特效不存在", "Effect not found")
STICKER_NOT_FOUND = (2004, "贴纸不存在", "Sticker not found")
MASK_NOT_FOUND = (2005, "遮罩不存在", "Mask not found")
FONT_NOT_FOUND = (2006, "字体不存在", "Font not found")
# 系统错误码 (9000-9999)
INTERNAL_SERVER_ERROR = (9000, "服务器内部错误", "Internal server error")
SERVICE_UNAVAILABLE = (9001, "服务不可用", "Service unavailable")
NETWORK_ERROR = (9002, "网络错误", "Network error")
TIMEOUT_ERROR = (9003, "请求超时", "Request timeout")
FILE_OPERATION_ERROR = (9004, "文件操作错误", "File operation error")
CACHE_OPERATION_ERROR = (9005, "缓存操作错误", "Cache operation error")
35.2.2 错误码结构
每个错误码包含三个核心组件:
CUSTOM_ERROR_CODE = (
error_code, # 数字错误码,用于程序识别
chinese_message, # 中文错误信息,面向国内用户
english_message # 英文错误信息,面向国际用户
)
35.3 自定义异常类
35.3.1 基础异常类
CustomException是所有自定义异常的基类:
class CustomException(Exception):
def __init__(self, error: CustomError, details: str = "", lang: str = "zh"):
self.error = error
self.error_code = error.value[0]
self.details = details
self.lang = lang
# 根据语言选择错误消息
if lang == "zh":
message = error.value[1]
else:
message = error.value[2]
# 构建完整的错误消息
if details:
full_message = f"{message}: {details}"
else:
full_message = message
super().__init__(full_message)
35.3.2 异常属性
异常类提供丰富的属性信息:
@property
def code(self) -> int:
"""获取错误码"""
return self.error_code
@property
def message(self) -> str:
"""获取错误消息"""
return str(self)
@property
def to_dict(self) -> dict:
"""转换为字典格式"""
return {
"error_code": self.error_code,
"error_message": str(self),
"details": self.details,
"lang": self.lang
}
35.4 错误处理策略
35.4.1 参数验证错误
def validate_parameters(params: dict, required_params: list) -> None:
"""参数验证"""
# 检查必填参数
for param in required_params:
if param not in params:
raise CustomException(
CustomError.MISSING_REQUIRED_PARAMETER,
details=f"缺少参数: {param}",
lang="zh"
)
# 检查参数类型
for param, value in params.items():
if param == "duration" and not isinstance(value, (int, float)):
raise CustomException(
CustomError.PARAMETER_TYPE_ERROR,
details=f"参数 {param} 类型错误,期望数字类型",
lang="zh"
)
if param == "duration" and value <= 0:
raise CustomException(
CustomError.PARAMETER_OUT_OF_RANGE,
details=f"参数 {param} 超出有效范围",
lang="zh"
)
35.4.2 资源不存在错误
def get_draft(draft_id: str) -> dict:
"""获取草稿信息"""
draft = database.find_draft(draft_id)
if not draft:
raise CustomException(
CustomError.DRAFT_NOT_FOUND,
details=f"草稿ID: {draft_id}",
lang="zh"
)
return draft
35.4.3 系统错误处理
def handle_file_operation(operation: callable) -> any:
"""文件操作错误处理"""
try:
return operation()
except FileNotFoundError as e:
raise CustomException(
CustomError.FILE_OPERATION_ERROR,
details=f"文件未找到: {str(e)}",
lang="zh"
)
except PermissionError as e:
raise CustomException(
CustomError.FILE_OPERATION_ERROR,
details=f"文件权限错误: {str(e)}",
lang="zh"
)
except Exception as e:
raise CustomException(
CustomError.INTERNAL_SERVER_ERROR,
details=f"系统错误: {str(e)}",
lang="zh"
)
35.5 错误码扩展机制
35.5.1 动态错误码注册
class DynamicErrorRegistry:
"""动态错误码注册器"""
def __init__(self):
self._custom_errors = {}
def register_error(self, error_name: str, error_code: int,
chinese_msg: str, english_msg: str) -> None:
"""注册自定义错误码"""
if error_code in [error.value[0] for error in CustomError]:
raise ValueError(f"错误码 {error_code} 已存在")
self._custom_errors[error_name] = (
error_code, chinese_msg, english_msg
)
def get_error(self, error_name: str) -> Optional[tuple]:
"""获取自定义错误码"""
return self._custom_errors.get(error_name)
35.5.2 业务模块错误码
为不同业务模块定义专属错误码范围:
# 视频处理模块 (3000-3099)
VIDEO_PROCESSING_ERROR = (3000, "视频处理错误", "Video processing error")
VIDEO_ENCODING_ERROR = (3001, "视频编码错误", "Video encoding error")
VIDEO_RESOLUTION_UNSUPPORTED = (3002, "不支持的視頻分辨率", "Unsupported video resolution")
# 音频处理模块 (3100-3199)
AUDIO_PROCESSING_ERROR = (3100, "音频处理错误", "Audio processing error")
AUDIO_FORMAT_UNSUPPORTED = (3101, "不支持的音频格式", "Unsupported audio format")
AUDIO_SYNC_ERROR = (3102, "音视频同步错误", "Audio video sync error")
# 特效处理模块 (3200-3299)
EFFECT_PROCESSING_ERROR = (3200, "特效处理错误", "Effect processing error")
EFFECT_RENDER_ERROR = (3201, "特效渲染错误", "Effect render error")
EFFECT_PARAMETER_INVALID = (3202, "特效参数无效", "Effect parameter invalid")
35.6 错误信息国际化
35.6.1 多语言支持
class I18nErrorManager:
"""国际化错误管理器"""
def __init__(self):
self._messages = {
"zh": {
1000: "参数错误",
1001: "缺少必填参数",
2000: "草稿不存在",
9000: "服务器内部错误"
},
"en": {
1000: "Invalid parameter",
1001: "Missing required parameter",
2000: "Draft not found",
9000: "Internal server error"
},
"ja": {
1000: "パラメータエラー",
1001: "必須パラメータが不足しています",
2000: "下書きが見つかりません",
9000: "サーバー内部エラー"
}
}
def get_error_message(self, error_code: int, lang: str = "zh") -> str:
"""获取指定语言的错误消息"""
return self._messages.get(lang, {}).get(error_code, "Unknown error")
35.6.2 语言自动检测
def detect_user_language(request_headers: dict) -> str:
"""从请求头检测用户语言"""
accept_language = request_headers.get("Accept-Language", "zh-CN")
# 简单的语言检测逻辑
if "zh" in accept_language:
return "zh"
elif "en" in accept_language:
return "en"
elif "ja" in accept_language:
return "ja"
else:
return "zh" # 默认中文
35.7 错误日志与监控
35.7.1 结构化错误日志
class ErrorLogger:
"""错误日志记录器"""
def __init__(self, logger: logging.Logger):
self.logger = logger
def log_error(self, exception: CustomException, context: dict = None):
"""记录错误日志"""
error_info = {
"error_code": exception.code,
"error_message": exception.message,
"error_type": exception.error.name,
"details": exception.details,
"lang": exception.lang,
"timestamp": datetime.now().isoformat(),
"context": context or {}
}
# 根据错误级别选择日志级别
if exception.code >= 9000:
self.logger.error(f"系统错误: {json.dumps(error_info)}")
elif exception.code >= 2000:
self.logger.warning(f"业务错误: {json.dumps(error_info)}")
else:
self.logger.info(f"参数错误: {json.dumps(error_info)}")
35.7.2 错误统计与告警
class ErrorMonitor:
"""错误监控器"""
def __init__(self):
self.error_counts = defaultdict(int)
self.error_timestamps = defaultdict(list)
def record_error(self, error_code: int):
"""记录错误发生"""
current_time = datetime.now()
self.error_counts[error_code] += 1
self.error_timestamps[error_code].append(current_time)
# 清理过期记录(保留最近1小时)
cutoff_time = current_time - timedelta(hours=1)
self.error_timestamps[error_code] = [
timestamp for timestamp in self.error_timestamps[error_code]
if timestamp > cutoff_time
]
def should_alert(self, error_code: int, threshold: int = 10) -> bool:
"""判断是否需要告警"""
recent_errors = len(self.error_timestamps[error_code])
return recent_errors >= threshold
35.8 错误处理最佳实践
35.8.1 异常链传递
def process_video(video_path: str) -> dict:
"""处理视频文件"""
try:
# 尝试读取视频文件
video_data = read_video_file(video_path)
# 处理视频数据
return process_video_data(video_data)
except FileNotFoundError as e:
# 包装为自定义异常
raise CustomException(
CustomError.VIDEO_NOT_FOUND,
details=f"视频文件不存在: {video_path}",
lang="zh"
) from e
except Exception as e:
# 包装为通用系统错误
raise CustomException(
CustomError.VIDEO_PROCESSING_ERROR,
details=f"视频处理失败: {str(e)}",
lang="zh"
) from e
35.8.2 批量错误处理
def batch_process_videos(video_paths: List[str]) -> List[dict]:
"""批量处理视频"""
results = []
errors = []
for video_path in video_paths:
try:
result = process_video(video_path)
results.append({
"path": video_path,
"success": True,
"data": result
})
except CustomException as e:
errors.append({
"path": video_path,
"success": False,
"error": e.to_dict
})
results.append({
"path": video_path,
"success": False,
"error": e.to_dict
})
# 记录批量处理结果
logger.info(f"批量处理完成: 成功{len([r for r in results if r['success']])}, "
f"失败{len([r for r in results if not r['success']])}")
return results
35.8.3 错误恢复机制
def safe_process_with_fallback(primary_func: callable,
fallback_func: callable,
error_types: List[CustomError]) -> any:
"""带错误恢复的处理"""
try:
# 尝试主要处理函数
return primary_func()
except CustomException as e:
if e.error in error_types:
logger.warning(f"主要处理失败,使用备用方案: {e.message}")
# 使用备用处理函数
return fallback_func()
else:
# 无法处理的错误,重新抛出
raise
35.9 错误码文档生成
35.9.1 自动生成错误码文档
def generate_error_code_documentation() -> str:
"""生成错误码文档"""
doc = "# 错误码文档\n\n"
# 按错误码排序
sorted_errors = sorted(CustomError, key=lambda x: x.value[0])
current_category = ""
for error in sorted_errors:
error_code = error.value[0]
# 确定错误类别
if 1000 <= error_code < 2000:
category = "基础错误"
elif 2000 <= error_code < 3000:
category = "业务错误"
elif 3000 <= error_code < 4000:
category = "视频处理错误"
elif 9000 <= error_code < 10000:
category = "系统错误"
else:
category = "其他错误"
# 新类别开始
if category != current_category:
doc += f"\n## {category}\n\n"
current_category = category
doc += f"### {error.name}\n"
doc += f"- **错误码**: {error_code}\n"
doc += f"- **中文描述**: {error.value[1]}\n"
doc += f"- **英文描述**: {error.value[2]}\n"
doc += f"- **枚举名称**: {error.name}\n\n"
return doc
35.9.2 错误码使用示例
# 错误码使用示例文档
def generate_error_usage_examples() -> str:
"""生成错误码使用示例"""
examples = """
# 错误码使用示例
## 基本用法
```python
from exceptions import CustomException, CustomError
# 抛出参数错误异常
raise CustomException(CustomError.INVALID_PARAMETER, "用户ID不能为空")
# 抛出业务错误异常
raise CustomException(CustomError.DRAFT_NOT_FOUND, f"草稿ID: {draft_id}")
# 抛出系统错误异常
raise CustomException(CustomError.INTERNAL_SERVER_ERROR, "数据库连接失败")
多语言支持
# 中文错误信息
raise CustomException(CustomError.VIDEO_NOT_FOUND, lang="zh")
# 英文错误信息
raise CustomException(CustomError.VIDEO_NOT_FOUND, lang="en")
异常捕获
try:
result = process_video(video_id)
except CustomException as e:
# 获取错误码
error_code = e.code
# 获取错误消息
error_message = e.message
# 获取详细信息
details = e.details
# 转换为字典格式(用于API返回)
error_dict = e.to_dict
logger.error(f"处理失败: {error_code} - {error_message}")
“”"
return examples
---
## 附录
代码仓库地址:
- GitHub: `https://github.com/Hommy-master/capcut-mate`
- Gitee: `https://gitee.com/taohongmin-gitee/capcut-mate`
接口文档地址:
- API文档地址: `https://docs.jcaigc.cn`

被折叠的 条评论
为什么被折叠?



