""" 施工方案审查API错误码统一定义 集中管理所有接口的错误码和错误响应格式 """ from typing import Dict, Any from fastapi import HTTPException from foundation.observability.logger.loggering import review_logger as logger class ErrorCodes: """错误码常量定义""" # 文件上传接口错误码 (WJSC001-WJSC008) WJSC001 = { "code": "WJSC001", "error_type": "FILE_MISSING", "message": "未上传文件", "status_code": 400 } WJSC002 = { "code": "WJSC002", "error_type": "FILE_MULTIPLE", "message": "仅支持单文件上传", "status_code": 400 } WJSC003 = { "code": "WJSC003", "error_type": "FILE_REJECTED", "message": "格式错误、内容违规、文件为空", "status_code": 400 } WJSC004 = { "code": "WJSC004", "error_type": "FILE_FORMAT_UNSUPPORTED", "message": "文件格式不支持(仅允许pdf/doc/docx)", "status_code": 400 } WJSC005 = { "code": "WJSC005", "error_type": "FILE_SIZE_EXCEEDED", "message": "文件过大(最大不超过50MB)", "status_code": 400 } WJSC007 = { "code": "WJSC007", "error_type": "UNAUTHORIZED", "message": "认证失败(未提供或无效的Authorization)", "status_code": 401 } WJSC008 = { "code": "WJSC008", "error_type": "INVALID_USER", "message": "用户标识未提供或无效", "status_code": 403 } WJSC009 = { "code": "WJSC009", "error_type": "INVALID_PARAMETERS", "message": "请求参数无效或不支持", "status_code": 400 } WJSC011 = { "code": "WJSC011", "error_type": "INTERNAL_ERROR", "message": "服务端内部错误", "status_code": 500 } # 启动审查接口错误码 (QDSC001-QDSC006) QDSC001 = { "code": "QDSC001", "error_type": "MISSING_PARAMETERS", "message": "请求参数缺失", "status_code": 400 } QDSC002 = { "code": "QDSC002", "error_type": "INVALID_PARAM_FORMAT", "message": "请求参数格式错误", "status_code": 400 } QDSC003 = { "code": "QDSC003", "error_type": "UNAUTHORIZED", "message": "认证失败(未提供或无效的Authorization)", "status_code": 401 } QDSC004 = { "code": "QDSC004", "error_type": "INVALID_USER", "message": "用户标识未提供或无效", "status_code": 403 } QDSC005 = { "code": "QDSC005", "error_type": "TASK_NOT_FOUND", "message": "任务ID不存在或已过期", "status_code": 404 } QDSC006 = { "code": "QDSC006", "error_type": "TASK_ALREADY_EXISTS", "message": "任务已存在,请勿重复提交", "status_code": 409 } QDSC007 = { "code": "QDSC007", "error_type": "PROJECT_PLAN_TYPE_INVALID", "message": "无效工程方案类型(未提供或未注册)", "status_code": 400 } QDSC008 = { "code": "QDSC008", "error_type": "ENUM_TYPE_INVALID", "message": "审查枚举类型无效", "status_code": 400 } QDSC009 = { "code": "QDSC009", "error_type": "ENUM_TYPE_CANNOT_BE_NULL", "message": "审查枚举类型不能为空", "status_code": 400 } QDSC010 = { "code": "QDSC010", "error_type": "FILE_INFO_NOT_FOUND", "message": "文件信息获取失败", "status_code": 500 } QDSC011 = { "code": "QDSC011", "error_type": "SERVER_INTERNAL_ERROR", "message": "服务端内部错误", "status_code": 500 } QDSC012 = { "code": "QDSC012", "error_type": "TASK_NOT_FOUND_OR_EXPIRED", "message": "任务ID不存在或已过期,请重新检查callback_task_id是否正确,或重新上传文件", "status_code": 404 } QDSC013 = { "code": "QDSC013", "error_type": "FILE_INFO_NOT_FOUND", "message": "文件信息获取失败,任务ID不存在或已过期", "status_code": 404 } QDSC014 = { "code": "QDSC014", "error_type": "TENDENCY_REVIEW_ROLE_INVALID", "message": "倾向性审查角色无效(未提供或未注册)", "status_code": 400 } QDSC015 = { "code": "QDSC015", "error_type": "INVALID_REVIEW_ITEM_FORMAT", "message": "审查项配置格式错误,必须为「chapter_code_review_dimension」格式", "status_code": 400 } QDSC016 = { "code": "QDSC016", "error_type": "INVALID_CHAPTER_CODE", "message": "章节code不支持", "status_code": 400 } QDSC017 = { "code": "QDSC017", "error_type": "INVALID_REVIEW_DIMENSION", "message": "审查项code不支持", "status_code": 400 } QDSC018 = { "code": "QDSC018", "error_type": "MUTUALLY_EXCLUSIVE_CONFIG", "message": "review_config与review_item_config互斥,只能提供其中一个", "status_code": 400 } QDSC019 = { "code": "QDSC019", "error_type": "REVIEW_CONFIG_REQUIRED", "message": "必须提供review_config或review_item_config中的其中一个", "status_code": 400 } QDSC020 = { "code": "QDSC020", "error_type": "DUPLICATE_REVIEW_ITEMS", "message": "审查项配置中存在重复项", "status_code": 400 } QDSC021 = { "code": "QDSC021", "error_type": "CATALOGUE_COMPLETENESS_ONLY", "message": "目录章节仅支持完整性审查", "status_code": 400 } # 审查结果接口错误码 (SCJG001-SCJG008) SCJG001 = { "code": "SCJG001", "error_type": "INVALID_TYPE", "message": "结果类型无效(非'summary'或'issues')", "status_code": 400 } SCJG002 = { "code": "SCJG002", "error_type": "MISSING_PARAM_ID", "message": "callback_task_id缺失", "status_code": 400 } SCJG003 = { "code": "SCJG003", "error_type": "INVALID_ID_FORMAT", "message": "callback_task_id格式错误", "status_code": 400 } SCJG004 = { "code": "SCJG004", "error_type": "UNAUTHORIZED", "message": "认证失败(未提供或无效的Authorization)", "status_code": 401 } SCJG005 = { "code": "SCJG005", "error_type": "INVALID_USER", "message": "用户标识未提供或无效", "status_code": 403 } SCJG006 = { "code": "SCJG006", "error_type": "TASK_NOT_FOUND", "message": "callback_task_id不存在或已过期", "status_code": 404 } SCJG007 = { "code": "SCJG007", "error_type": "NO_REVIEW_RESULTS", "message": "无审查结果数据", "status_code": 404 } SCJG008 = { "code": "SCJG008", "error_type": "SERVER_ERROR", "message": "服务端内部错误(审查结果生成失败)", "status_code": 500 } def create_http_exception(error_code: Dict[str, Any], custom_message: str = None) -> HTTPException: """ 创建HTTP异常 Args: error_code: 错误码字典 custom_message: 自定义错误消息,可选 Returns: HTTPException: FastAPI异常对象 """ detail = { "code": error_code["code"], "error_type": error_code["error_type"], "message": custom_message or error_code["message"] } return HTTPException( status_code=error_code["status_code"], detail=detail ) def create_server_error(error_code: str, original_error: Exception) -> HTTPException: """ 创建服务器内部错误异常 Args: error_code: 错误码 (如 "WJSC008", "QDSC006", "SCJG008") original_error: 原始异常 Returns: HTTPException: FastAPI异常对象 """ error_map = { "WJSC011": ErrorCodes.WJSC011, "QDSC006": ErrorCodes.QDSC006, "SCJG008": ErrorCodes.SCJG008 } error_config = error_map.get(error_code, ErrorCodes.WJSC008) message = f"{error_config['message']}: {str(original_error)}" return create_http_exception(error_config, message) # 便捷的错误创建函数 class FileUploadErrors: """文件上传接口错误""" @staticmethod def file_missing(): logger.error(ErrorCodes.WJSC001) return create_http_exception(ErrorCodes.WJSC001) @staticmethod def file_multiple(): logger.error(ErrorCodes.WJSC002) return create_http_exception(ErrorCodes.WJSC002) @staticmethod def file_rejected(message: str = None): logger.error(ErrorCodes.WJSC003) return create_http_exception(ErrorCodes.WJSC003, message) @staticmethod def file_format_unsupported(): logger.error(ErrorCodes.WJSC004) return create_http_exception(ErrorCodes.WJSC004) @staticmethod def file_size_exceeded(): logger.error(ErrorCodes.WJSC005) return create_http_exception(ErrorCodes.WJSC005) @staticmethod def project_plan_type_invalid(): logger.error(ErrorCodes.WJSC006) return create_http_exception(ErrorCodes.WJSC006) @staticmethod def unauthorized(): logger.error(ErrorCodes.WJSC007) return create_http_exception(ErrorCodes.WJSC007) @staticmethod def invalid_user(): logger.error(ErrorCodes.WJSC008) return create_http_exception(ErrorCodes.WJSC008) @staticmethod def task_already_exists(): logger.error(ErrorCodes.WJSC010) return create_http_exception(ErrorCodes.WJSC010) @staticmethod def invalid_parameters(): logger.error(ErrorCodes.WJSC009) return create_http_exception(ErrorCodes.WJSC009) @staticmethod def internal_error(original_error: Exception): logger.error(ErrorCodes.WJSC011) return create_server_error("WJSC011", original_error) class LaunchReviewErrors: """启动审查接口错误""" @staticmethod def missing_parameters(): logger.error(ErrorCodes.QDSC001) return create_http_exception(ErrorCodes.QDSC001) @staticmethod def invalid_param_format(): logger.error(ErrorCodes.QDSC002) return create_http_exception(ErrorCodes.QDSC002) @staticmethod def unauthorized(): logger.error(ErrorCodes.QDSC003) return create_http_exception(ErrorCodes.QDSC003) @staticmethod def invalid_user(): logger.error(ErrorCodes.QDSC004) return create_http_exception(ErrorCodes.QDSC004) @staticmethod def task_not_found(): logger.error(ErrorCodes.QDSC005) return create_http_exception(ErrorCodes.QDSC005) @staticmethod def task_already_exists(): logger.error(ErrorCodes.QDSC006) return create_http_exception(ErrorCodes.QDSC006) @staticmethod def project_plan_type_invalid(): logger.error(ErrorCodes.QDSC007) return create_http_exception(ErrorCodes.QDSC007) @staticmethod def enum_type_invalid(): logger.error(ErrorCodes.QDSC008) return create_http_exception(ErrorCodes.QDSC008) @staticmethod def enum_type_cannot_be_null(): logger.error(ErrorCodes.QDSC009) return create_http_exception(ErrorCodes.QDSC009) @staticmethod def file_info_not_found(original_error: Exception): logger.error(ErrorCodes.QDSC010) return create_server_error("QDSC010", original_error) @staticmethod def internal_error(original_error: Exception): logger.error(ErrorCodes.QDSC011) return create_server_error("QDSC011", original_error) @staticmethod def task_not_found_or_expired(): logger.error(ErrorCodes.QDSC012) return create_http_exception(ErrorCodes.QDSC012) @staticmethod def file_info_not_found(): logger.error(ErrorCodes.QDSC013) return create_http_exception(ErrorCodes.QDSC013) @staticmethod def tendency_review_role_invalid(): logger.error(ErrorCodes.QDSC014) return create_http_exception(ErrorCodes.QDSC014) @staticmethod def invalid_review_item_format(invalid_items: list = None): """审查项配置格式错误""" logger.error(f"审查项配置格式错误: {invalid_items}") message = f"审查项配置格式错误,必须为「chapter_code_review_dimension」格式(如 basis_sensitive_word_check)。无效项: {invalid_items}" return create_http_exception(ErrorCodes.QDSC015, message) @staticmethod def invalid_chapter_code(invalid_codes: list = None): """章节code不支持""" logger.error(f"章节code不支持: {invalid_codes}") message = f"章节code不支持。无效的章节code: {invalid_codes}" return create_http_exception(ErrorCodes.QDSC016, message) @staticmethod def invalid_review_dimension(invalid_dimensions: list = None): """审查项code不支持""" logger.error(f"审查项code不支持: {invalid_dimensions}") message = f"审查项code不支持。无效的审查项code: {invalid_dimensions}" return create_http_exception(ErrorCodes.QDSC017, message) @staticmethod def mutually_exclusive_config(): """review_config与review_item_config互斥""" logger.error("review_config与review_item_config同时提供,但两者互斥") message = "参数错误:review_config(审查维度列表)与review_item_config(章节_审查维度列表)互斥,只能提供其中一个,不能同时提供。" return create_http_exception(ErrorCodes.QDSC018, message) @staticmethod def review_config_required(): """必须提供review_config或review_item_config""" logger.error("未提供review_config或review_item_config") message = "参数错误:必须提供review_config(审查维度列表)或review_item_config(章节_审查维度列表)中的其中一个。" return create_http_exception(ErrorCodes.QDSC019, message) @staticmethod def duplicate_review_items(duplicates: list = None): """审查项配置中存在重复项""" logger.error(f"审查项配置中存在重复项: {duplicates}") message = f"参数错误:review_item_config 中存在重复的审查项。重复项: {duplicates}" return create_http_exception(ErrorCodes.QDSC020, message) @staticmethod def catalogue_completeness_only(invalid_items: list = None): """目录章节仅支持完整性审查""" logger.error(f"目录章节使用了非完整性审查: {invalid_items}") message = f"参数错误:目录章节(catalogue)仅支持完整性审查(completeness_check)。无效配置项: {invalid_items}" return create_http_exception(ErrorCodes.QDSC021, message) class ReviewResultsErrors: """审查结果接口错误""" @staticmethod def invalid_type(): logger.error(ErrorCodes.SCJG001) return create_http_exception(ErrorCodes.SCJG001) @staticmethod def missing_param_id(): logger.error(ErrorCodes.SCJG002) return create_http_exception(ErrorCodes.SCJG002) @staticmethod def invalid_id_format(): logger.error(ErrorCodes.SCJG003) return create_http_exception(ErrorCodes.SCJG003) @staticmethod def unauthorized(): logger.error(ErrorCodes.SCJG004) return create_http_exception(ErrorCodes.SCJG004) @staticmethod def invalid_user(): logger.error(ErrorCodes.SCJG005) return create_http_exception(ErrorCodes.SCJG005) @staticmethod def task_not_found(): logger.error(ErrorCodes.SCJG006) return create_http_exception(ErrorCodes.SCJG006) @staticmethod def no_review_results(): logger.error(ErrorCodes.SCJG007) return create_http_exception(ErrorCodes.SCJG007) @staticmethod def server_error(original_error: Exception): logger.error(ErrorCodes.SCJG008) return create_server_error("SCJG008", original_error)