【剪映小助手源码精讲】第35章:异常体系设计

第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`
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值