From fa287f36aef347b719af1259b6ad3a17eed5d7d2 Mon Sep 17 00:00:00 2001 From: Jerry Yan <792602257@qq.com> Date: Mon, 17 Nov 2025 10:27:38 +0800 Subject: [PATCH] =?UTF-8?q?Revert=20"feat(facebody):=20=E5=AE=9E=E7=8E=B0?= =?UTF-8?q?=E4=BA=BA=E8=84=B8=E8=AF=86=E5=88=AB=E6=90=9C=E7=B4=A2=E7=9A=84?= =?UTF-8?q?=E9=87=8D=E8=AF=95=E6=9C=BA=E5=88=B6"?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This reverts commit 7c42c5c462951e569c7ab9d855abb22cd92fa4ef. --- .../facebody/adapter/BceFaceBodyAdapter.java | 73 +--- .../exceptions/BceErrorCodeClassifier.java | 405 ------------------ .../exceptions/FaceBodyException.java | 17 - .../NonRetryableFaceBodyException.java | 185 -------- .../RetryableFaceBodyException.java | 139 ------ 5 files changed, 5 insertions(+), 814 deletions(-) delete mode 100644 src/main/java/com/ycwl/basic/facebody/exceptions/BceErrorCodeClassifier.java delete mode 100644 src/main/java/com/ycwl/basic/facebody/exceptions/NonRetryableFaceBodyException.java delete mode 100644 src/main/java/com/ycwl/basic/facebody/exceptions/RetryableFaceBodyException.java diff --git a/src/main/java/com/ycwl/basic/facebody/adapter/BceFaceBodyAdapter.java b/src/main/java/com/ycwl/basic/facebody/adapter/BceFaceBodyAdapter.java index 8901ca78..578fb069 100644 --- a/src/main/java/com/ycwl/basic/facebody/adapter/BceFaceBodyAdapter.java +++ b/src/main/java/com/ycwl/basic/facebody/adapter/BceFaceBodyAdapter.java @@ -6,10 +6,6 @@ import com.ycwl.basic.facebody.entity.AddFaceResp; import com.ycwl.basic.facebody.entity.BceFaceBodyConfig; import com.ycwl.basic.facebody.entity.SearchFaceResp; import com.ycwl.basic.facebody.entity.SearchFaceResultItem; -import com.ycwl.basic.facebody.exceptions.BceErrorCodeClassifier; -import com.ycwl.basic.facebody.exceptions.FaceBodyException; -import com.ycwl.basic.facebody.exceptions.NonRetryableFaceBodyException; -import com.ycwl.basic.facebody.exceptions.RetryableFaceBodyException; import com.ycwl.basic.utils.ratelimiter.FixedRateLimiter; import com.ycwl.basic.utils.ratelimiter.IRateLimiter; import lombok.extern.slf4j.Slf4j; @@ -307,71 +303,18 @@ public class BceFaceBodyAdapter implements IFaceBodyAdapter { @Override public SearchFaceResp searchFace(String dbName, String faceUrl) { - int retryCount = 0; - - while (true) { - try { - return doSearchFace(dbName, faceUrl); - } catch (RetryableFaceBodyException e) { - // 获取建议的最大重试次数 - Integer maxRetries = BceErrorCodeClassifier.getSuggestedMaxRetries(e.getErrorCode()); - if (maxRetries == null) { - maxRetries = 1; // 默认重试1次 - } - - if (retryCount >= maxRetries) { - log.error("搜索人脸重试{}次后仍失败,错误码: {}, 错误信息: {}", - retryCount, e.getErrorCode(), e.getMessage()); - // 返回空结果而不是抛出异常,保持与原有逻辑一致 - return null; - } - - // 计算延迟时间 - Long delay = BceErrorCodeClassifier.getSuggestedRetryDelay(e.getErrorCode(), retryCount); - if (delay == null) { - delay = 500L; // 默认延迟500ms - } - - log.warn("搜索人脸失败[错误码: {}],{}ms后进行第{}次重试,错误信息: {}", - e.getErrorCode(), delay, retryCount + 1, e.getMessage()); - - try { - if (delay > 0) { - Thread.sleep(delay); - } - } catch (InterruptedException ignored) { - Thread.currentThread().interrupt(); - } - - retryCount++; - } catch (NonRetryableFaceBodyException e) { - // 不可重试错误,直接返回 - log.error("搜索人脸失败(不可重试),错误码: {}, 类别: {}, 错误信息: {}", - e.getErrorCode(), e.getCategory(), e.getMessage()); - // 返回空结果而不是抛出异常,保持与原有逻辑一致 - return null; - } - } - } - - private SearchFaceResp doSearchFace(String dbName, String faceUrl) { IRateLimiter searchFaceLimiter = getLimiter(LOCK_TYPE.SEARCH_FACE); SearchFaceResp resp = new SearchFaceResp(); - try { AipFace client = getClient(); HashMap options = new HashMap<>(); options.put("max_user_num", "50"); - try { searchFaceLimiter.acquire(); } catch (InterruptedException ignored) { } - JSONObject response = client.search(faceUrl, "URL", dbName, options); - int errorCode = response.getInt("error_code"); - - if (errorCode == 0) { + if (response.getInt("error_code") == 0) { resp.setOriginalFaceScore(100f); JSONObject resultObj = response.getJSONObject("result"); if (resultObj == null) { @@ -395,18 +338,12 @@ public class BceFaceBodyAdapter implements IFaceBodyAdapter { } return resp; } else { - // 使用错误码分类器创建相应的异常 - String errorMsg = response.optString("error_msg", "未知错误"); - throw BceErrorCodeClassifier.createException(errorCode, - "人脸搜索失败[" + errorCode + "]: " + errorMsg); + resp.setOriginalFaceScore(0f); + return resp; } - } catch (FaceBodyException e) { - // 重新抛出 FaceBodyException - throw e; } catch (Exception e) { - // 其他异常(如网络异常)包装为可重试异常 - log.error("搜索人脸网络异常", e); - throw new RetryableFaceBodyException("搜索人脸网络异常: " + e.getMessage(), e); + log.error("搜索人脸失败!", e); + return null; } } diff --git a/src/main/java/com/ycwl/basic/facebody/exceptions/BceErrorCodeClassifier.java b/src/main/java/com/ycwl/basic/facebody/exceptions/BceErrorCodeClassifier.java deleted file mode 100644 index 6045ffe0..00000000 --- a/src/main/java/com/ycwl/basic/facebody/exceptions/BceErrorCodeClassifier.java +++ /dev/null @@ -1,405 +0,0 @@ -package com.ycwl.basic.facebody.exceptions; - -import java.util.Set; - -/** - * 百度云人脸识别错误码分类器 - * - *

根据百度云人脸识别 API 错误码文档,对错误进行分类: - *

- * - *

参考文档: https://cloud.baidu.com/doc/FACE/s/5k37c1ujz - * - * @see RetryableFaceBodyException - * @see NonRetryableFaceBodyException - */ -public class BceErrorCodeClassifier { - - /** - * 可重试的错误码集合 - */ - private static final Set RETRYABLE_ERROR_CODES = Set.of( - // ========== 接口流控及鉴权错误码 ========== - 2, // Service temporarily unavailable - 服务暂不可用 - 4, // Open api request limit reached - 集群超限额 - 17, // Open api daily request limit reached - 每天流量超限额 - 18, // Open api qps request limit reached - QPS超限额 - 19, // Open api total request limit reached - 请求总量超限额 - 110, // Access token invalid or no longer valid - Access Token失效 - 111, // Access token expired - Access token过期 - - // ========== 网络及服务端临时故障 ========== - 222201, // network not available - 服务端请求失败 - 222204, // image_url_download_fail - 从图片的url下载图片失败 - 222205, // network not available - 服务端请求失败 - 222206, // rtse service return fail - 服务端请求失败 - 222302, // system error - 服务端请求失败 - 222301, // get face fail - 获取人脸图片失败 - 222303, // get face fail - 获取人脸图片失败 - 222361, // network not available - 公安服务连接失败 - - // ========== 系统繁忙 ========== - 222901, 222902, 222903, 222904, 222905, 222906, - 222907, 222908, 222909, 222910, 222911, 222912, - 222913, 222914, 222915, 222916, // system busy - 系统繁忙相关 - - // ========== H5活体检测接口临时错误 ========== - 216430, // rtse/face service error - rtse/face 服务异常 - 216431, // voice service error - 语音识别服务异常 - 216432, // video service call fail - 视频解析服务调用失败 - 216433, // video service error - 视频解析服务发生错误 - 216505, // redis connect error - redis连接失败 - 216506, // redis operation error - redis操作失败 - 216612, // system busy - 系统繁忙 - - // ========== H5方案临时错误 ========== - 283400, // 服务异常,请稍后再试 - 283460, // 视频文件过大,核验请求超时 - 283438, // 视频转码失败,请重试 - 283436, // Token生成失败,请重试 - 283502, // 视频文件上传 bos 失败 - 283468, // BOS文件上传失败 - 283447 // 验证失败,请稍后重新尝试 - ); - - /** - * 明确不可重试的错误码集合(参数错误、认证失败、资源不存在、业务规则违反等) - */ - private static final Set NON_RETRYABLE_ERROR_CODES = Set.of( - // ========== 接口权限错误(认证失败) ========== - 6, // no permission to access data - 没有接口权限 - 100, // Invalid parameter - 无效的access_token参数 - - // ========== 参数格式错误 ========== - 222001, 222002, 222003, 222004, 222005, 222006, 222007, 222008, 222009, 222010, - 222011, 222012, 222013, 222014, 222015, 222016, 222017, 222018, 222019, 222020, - 222021, 222022, 222023, 222024, 222025, 222026, 222027, 222028, 222029, 222030, - 222039, 222046, 222101, 222102, 222041, 222042, 222038, // param format error 系列 - - // ========== 图片相关错误 ========== - 222200, // request body should be json format - 格式错误 - 222202, // pic not has face - 图片中没有人脸 - 222203, // image check fail - 无法解析人脸 - 222208, // the number of image is incorrect - 图片的数量错误 - 222213, // face size is too small - 人脸尺寸过小 - 222214, // face are cartoon images - 卡通图像 - 222215, // face quality is not acceptable - 人脸属性编辑处理失败 - 222304, // image size is too large - 图片尺寸太大 - 222305, // pic storage not support - 当前版本不支持图片存储 - 222307, // image illegal, reason: porn - 图片非法 鉴黄未通过 - 222308, // image illegal, reason: sensitive person - 图片非法 含有政治敏感人物 - 222309, // image size is too small - 图片尺寸过小 - - // ========== 人脸库管理错误(资源不存在/已存在) ========== - 223100, // group is not exist - 操作的用户组不存在 - 223101, // group is already exist - 该用户组已存在 - 223102, // user is already exist - 该用户已存在 - 223103, // user is not exist - 找不到该用户 - 223105, // face is already exist - 该人脸已存在 - 223106, // face is not exist - 该人脸不存在 - 223111, // dst group not exist - 目标用户组不存在 - 223136, // images exist in this group - 该组内存在关联图片 - 223128, // group was deleting - 正在清理该用户组的数据 - - // ========== 业务规则违反 ========== - 222104, // group_list is too large - group_list包含组数量过多 - 222110, // uid_list is too large - uid_list包含数量过多 - 222117, // app_list is too large - app_list包含app数量过多 - 222207, // match user is not found - 未找到匹配的用户 - 222209, // face token not exist - face token不存在 - 222210, // the number of user's faces is beyond the limit - 人脸数目超过限制 - 223107, // scene_type not same - 源组与目标组的scene_type不同 - 223112, // quality_conf format error - quality_conf格式不正确 - 223118, // quality control error - 质量控制项错误 - 223119, // liveness control item error - 活体控制项错误 - 223201, // param[scene_type] format error - scene_type格式错误 - 223202, // scene_type does not match - scene_type不匹配 - - // ========== 质量检测未通过(业务规则违反) ========== - 223113, // face is covered - 人脸有被遮挡 - 223114, // face is fuzzy - 人脸模糊 - 223115, // face light is not good - 人脸光照不好 - 223116, // incomplete face - 人脸不完整 - 223129, // face not forward - 人脸未面向正前方 - 223121, 223122, 223123, 223124, 223125, 223126, 223127, // 各部位遮挡检测未通过 - - // ========== 活体检测未通过 ========== - 223120, // liveness check fail - 活体检测未通过 - 223130, // spoofing_control item error - spoofing_control参数格式错误 - 223131, // spoofing check fail - 合成图检测未通过 - 223133, // video extract image liveness check fail - 视频提取图片活体检测失败 - 223052, // action identify fail - 视频中的动作验证未通过 - - // ========== 人脸融合错误 ========== - 222211, // template image quality reject - 模板图质量不合格 - 222212, // merge face fail - 人脸融合失败 - 222300, // add face fail - 人脸图片添加失败 - 222514, // face editattrpro operation fail - 人脸属性编辑v2调用服务失败 - 222152, // param[target] format error - target参数错误 - - // ========== 人脸实名认证错误 ========== - 222350, // police picture is none or low quality - 公安网图片不存在或质量过低 - 222351, // id number and name not match - 身份证号与姓名不匹配 - 222354, // id number not exist - 公安库里不存在此身份证号 - 222355, // police picture not exist - 公安库里没有对应的照片 - 222356, // person picture is low quality - 人脸图片质量不符合要求 - 222357, // picture file format error - 图片格式解析失败 - 222358, // trigger risk interception - 触发数据源风险拦截 - 282105, // image decrypt error - 图片解密失败 - 216201, // image format error - 图片格式失败 - 216100, // invalid param - 参数格式失败 - 282003, // missing required parameter(s) - 缺少必要参数 - 282000, // internal error - 服务器内部错误 - 216600, // 身份证号码格式错误 - 216601, // 身份证号和名字不匹配 - 222360, // 身份核验未通过 - - // ========== H5活体检测错误 ========== - 216500, // code digit error - 验证码位数错误 - 216501, // not found face - 没有找到人脸 - 216502, // session lapse - 当前会话已失效 - 216508, // not found video info - 没有找到视频信息 - 216509, // voice can not identify - 视频中的声音无法识别 - 216510, // video time is too long - 视频长度超过10s - 216511, // voice file error - 语音文件不符合要求 - 216512, // action verify must post session_id - 必须使用会话id - 216513, // detect_model param error - 检测模型参数错误 - 216908, // 视频中人脸质量较差 - 216909, // video all image detect over two face - 人脸数超过2 - 223050, // voice similarity low error - 语音与验证码相似度过低 - - // ========== H5方案错误 ========== - 200, // unsupported operation - 不支持的操作 - 283456, // 图片为空或格式不正确 - 283458, // 当前链接已失效 - 283459, // 请从手机端扫描二维码访问 - 216434, // 活体检测未通过 - 223051, // 唇语验证未通过 - 283738, // 颜色验证未通过 - 283457, // 当前环境存在安全风险 - 283501, // 安全检验未通过 - 283421, // 应用不存在 - 283437, // Token无效或已过期 - 283439, // STS_Token 已经生成 - 283464, // 非法流程 - 283461, // 人脸和对比源不匹配 - 283462, // 比对源配置错误 - 283463, // 人脸图片质量检测未通过 - 283465, // 人脸图片活体检测未通过 - 283467, // 该PLAN_ID下未查询到图片文件 - 283469, // 用户请求的 body 是空 - 283435, // 方案不存在 - 283440, // 身份证照片不符合要求 - 283442, // 身份证信息不合法 - 283443, // 不可使用语音验证码 - 283444, // 语音验证码生成失败 - 283448, // 语音验证码不符合要求 - 283449, // 活体检测视频不符合要求 - 283450, // 认证尚未开始 - 283451, // 认证处理中 - 283453, // 不可使用照片活体 - 283454, // 不可使用视频活体 - 283455, // 超出查询有效期 - 283503, // 对比源信息未传入 - 283504, // 请上传正确的身份证照片 - 283505, // 请上传正确的身份证人像面 - 283506, // 请上传正确的身份证国徽面 - 283507, // 不可使用身份证识别 - 283601, // 重复推送错误信息 - 300201, // 您已拒绝授权摄像头 - 300001, // 受当前环境限制 - 300002, // 受当前环境限制 - 999999, // 请确保是本人操作且正脸采集 - 800001, // 采集超时 - 800002 // 炫瞳检测失败 - ); - - /** - * 判断错误码是否为可重试错误 - * - * @param errorCode 百度云返回的错误码 - * @return true 如果是可重试错误 - */ - public static boolean isRetryable(Integer errorCode) { - if (errorCode == null) { - return false; - } - return RETRYABLE_ERROR_CODES.contains(errorCode); - } - - /** - * 判断错误码是否为不可重试错误 - * - * @param errorCode 百度云返回的错误码 - * @return true 如果是不可重试错误 - */ - public static boolean isNonRetryable(Integer errorCode) { - if (errorCode == null) { - return false; - } - return NON_RETRYABLE_ERROR_CODES.contains(errorCode); - } - - /** - * 根据错误码和错误消息创建合适的异常 - * - * @param errorCode 百度云返回的错误码 - * @param errorMessage 错误消息 - * @return 对应的异常对象 - */ - public static FaceBodyException createException(Integer errorCode, String errorMessage) { - if (isRetryable(errorCode)) { - return new RetryableFaceBodyException(errorMessage, errorCode); - } else if (isNonRetryable(errorCode)) { - return new NonRetryableFaceBodyException( - errorMessage, - errorCode, - categorizeNonRetryableError(errorCode) - ); - } else { - // 未知错误码,默认为不可重试 - return new NonRetryableFaceBodyException( - errorMessage, - errorCode, - NonRetryableFaceBodyException.ErrorCategory.OTHER - ); - } - } - - /** - * 对不可重试错误进行分类 - * - * @param errorCode 错误码 - * @return 错误类别 - */ - private static NonRetryableFaceBodyException.ErrorCategory categorizeNonRetryableError(Integer errorCode) { - if (errorCode == null) { - return NonRetryableFaceBodyException.ErrorCategory.OTHER; - } - - // 认证/权限错误 - if (errorCode == 6 || errorCode == 100) { - return NonRetryableFaceBodyException.ErrorCategory.AUTHENTICATION_ERROR; - } - - // 参数验证错误 - if ((errorCode >= 222001 && errorCode <= 222046) || - (errorCode >= 222101 && errorCode <= 222102) || - (errorCode >= 222041 && errorCode <= 222042) || - errorCode == 222038 || errorCode == 216100 || errorCode == 282003) { - return NonRetryableFaceBodyException.ErrorCategory.VALIDATION_ERROR; - } - - // 资源不存在 - if (errorCode == 223100 || errorCode == 223103 || errorCode == 223106 || - errorCode == 223111 || errorCode == 222207 || errorCode == 222209 || - errorCode == 222354 || errorCode == 222355 || errorCode == 283435 || - errorCode == 283467 || errorCode == 283421) { - return NonRetryableFaceBodyException.ErrorCategory.RESOURCE_NOT_FOUND; - } - - // 数据冲突 - if (errorCode == 223101 || errorCode == 223102 || errorCode == 223105 || - errorCode == 223136 || errorCode == 283439 || errorCode == 283601) { - return NonRetryableFaceBodyException.ErrorCategory.CONFLICT; - } - - // 不支持的操作 - if (errorCode == 200 || errorCode == 222305 || errorCode == 283443 || - errorCode == 283453 || errorCode == 283454 || errorCode == 283507) { - return NonRetryableFaceBodyException.ErrorCategory.UNSUPPORTED_OPERATION; - } - - // 业务规则违反(质量检测、活体检测、人脸融合等) - if ((errorCode >= 223113 && errorCode <= 223131) || - errorCode == 223133 || errorCode == 223052 || errorCode == 223120 || - (errorCode >= 222202 && errorCode <= 222215) || - errorCode == 222304 || errorCode == 222307 || errorCode == 222308 || - errorCode == 222309 || errorCode == 222210 || errorCode == 222211 || - errorCode == 222212 || errorCode == 222300 || errorCode == 222350 || - errorCode == 222351 || errorCode == 222356 || errorCode == 222358 || - errorCode == 216434 || errorCode == 216500 || errorCode == 216501 || - errorCode == 216508 || errorCode == 216509 || errorCode == 216510 || - errorCode == 216511 || errorCode == 216908 || errorCode == 216909 || - errorCode == 223050 || errorCode == 223051 || errorCode == 283738 || - errorCode == 283456 || errorCode == 283457 || errorCode == 283461 || - errorCode == 283463 || errorCode == 283465 || errorCode == 283440 || - errorCode == 283442 || errorCode == 283449 || errorCode == 800001 || - errorCode == 800002 || errorCode == 999999 || errorCode == 216600 || - errorCode == 216601 || errorCode == 222360) { - return NonRetryableFaceBodyException.ErrorCategory.BUSINESS_RULE_VIOLATION; - } - - return NonRetryableFaceBodyException.ErrorCategory.OTHER; - } - - /** - * 根据错误码获取建议的重试次数 - * - * @param errorCode 错误码 - * @return 建议的重试次数,null 表示使用默认值 - */ - public static Integer getSuggestedMaxRetries(Integer errorCode) { - if (errorCode == null || !isRetryable(errorCode)) { - return 0; - } - - // QPS/流量限制,建议重试次数较多 - if (errorCode == 18 || errorCode == 17 || errorCode == 19 || errorCode == 4) { - return 5; - } - - // Token失效,只需重试1次(重新获取token后) - if (errorCode == 110 || errorCode == 111) { - return 1; - } - - // 临时服务故障,建议重试3次 - if (errorCode == 2 || errorCode == 222201 || errorCode == 222204 || errorCode == 222205 || - errorCode == 222206 || errorCode == 222302) { - return 3; - } - - // 默认重试次数 - return 3; - } - - /** - * 根据错误码获取建议的重试延迟时间(毫秒) - * - * @param errorCode 错误码 - * @param retryCount 当前重试次数(从0开始) - * @return 建议的延迟时间(毫秒),null 表示使用默认指数退避策略 - */ - public static Long getSuggestedRetryDelay(Integer errorCode, int retryCount) { - if (errorCode == null || !isRetryable(errorCode)) { - return null; - } - - // QPS限制,建议较长的延迟(指数退避) - if (errorCode == 18) { - return (long) (Math.pow(2, retryCount) * 1000); // 1s, 2s, 4s, 8s... - } - - // 每天流量超限,建议更长的延迟 - if (errorCode == 17 || errorCode == 19) { - return (long) (Math.pow(2, retryCount) * 5000); // 5s, 10s, 20s... - } - - // Token失效,立即重试(因为需要先刷新token) - if (errorCode == 110 || errorCode == 111) { - return 0L; - } - - // 集群超限,建议短暂延迟 - if (errorCode == 4) { - return (long) (Math.pow(1.5, retryCount) * 500); // 500ms, 750ms, 1125ms... - } - - // 默认使用指数退避策略 - return (long) (Math.pow(2, retryCount) * 500); // 500ms, 1s, 2s, 4s... - } -} diff --git a/src/main/java/com/ycwl/basic/facebody/exceptions/FaceBodyException.java b/src/main/java/com/ycwl/basic/facebody/exceptions/FaceBodyException.java index 30a8573f..459f2d01 100644 --- a/src/main/java/com/ycwl/basic/facebody/exceptions/FaceBodyException.java +++ b/src/main/java/com/ycwl/basic/facebody/exceptions/FaceBodyException.java @@ -1,24 +1,7 @@ package com.ycwl.basic.facebody.exceptions; -/** - * 人脸识别异常基类 - * - *

所有 facebody 包相关的异常都应继承此类。 - * - * @see RetryableFaceBodyException - * @see NonRetryableFaceBodyException - */ public class FaceBodyException extends RuntimeException { - public FaceBodyException(String message) { super(message); } - - public FaceBodyException(String message, Throwable cause) { - super(message, cause); - } - - public FaceBodyException(Throwable cause) { - super(cause); - } } diff --git a/src/main/java/com/ycwl/basic/facebody/exceptions/NonRetryableFaceBodyException.java b/src/main/java/com/ycwl/basic/facebody/exceptions/NonRetryableFaceBodyException.java deleted file mode 100644 index db3fec51..00000000 --- a/src/main/java/com/ycwl/basic/facebody/exceptions/NonRetryableFaceBodyException.java +++ /dev/null @@ -1,185 +0,0 @@ -package com.ycwl.basic.facebody.exceptions; - -/** - * 不可重试的人脸识别异常 - * - *

表示操作失败且重试不会改变结果的异常场景,通常由以下原因引起: - *

- * - *

调用方应捕获此异常并进行业务逻辑处理,而非简单重试。 - * - * @see FaceBodyException - * @see RetryableFaceBodyException - */ -public class NonRetryableFaceBodyException extends FaceBodyException { - - private final Integer errorCode; - private final ErrorCategory category; - - /** - * 错误类别枚举 - */ - public enum ErrorCategory { - /** 参数验证错误 */ - VALIDATION_ERROR, - - /** 认证或授权错误 */ - AUTHENTICATION_ERROR, - - /** 资源不存在 */ - RESOURCE_NOT_FOUND, - - /** 业务规则违反 */ - BUSINESS_RULE_VIOLATION, - - /** 不支持的操作 */ - UNSUPPORTED_OPERATION, - - /** 数据冲突 */ - CONFLICT, - - /** 其他不可重试错误 */ - OTHER - } - - /** - * 构造一个不可重试异常 - * - * @param message 错误消息 - */ - public NonRetryableFaceBodyException(String message) { - super(message); - this.errorCode = null; - this.category = ErrorCategory.OTHER; - } - - /** - * 构造一个不可重试异常,包含原始异常信息 - * - * @param message 错误消息 - * @param cause 原始异常 - */ - public NonRetryableFaceBodyException(String message, Throwable cause) { - super(message, cause); - this.errorCode = null; - this.category = ErrorCategory.OTHER; - } - - /** - * 构造一个不可重试异常,指定错误类别 - * - * @param message 错误消息 - * @param category 错误类别 - */ - public NonRetryableFaceBodyException(String message, ErrorCategory category) { - super(message); - this.errorCode = null; - this.category = category; - } - - /** - * 构造一个不可重试异常,包含错误码和类别 - * - * @param message 错误消息 - * @param errorCode 第三方服务返回的错误码 - * @param category 错误类别 - */ - public NonRetryableFaceBodyException(String message, Integer errorCode, ErrorCategory category) { - super(message); - this.errorCode = errorCode; - this.category = category; - } - - /** - * 构造一个不可重试异常,包含原始异常、错误码和类别 - * - * @param message 错误消息 - * @param cause 原始异常 - * @param errorCode 第三方服务返回的错误码 - * @param category 错误类别 - */ - public NonRetryableFaceBodyException(String message, Throwable cause, Integer errorCode, ErrorCategory category) { - super(message, cause); - this.errorCode = errorCode; - this.category = category; - } - - /** - * 获取第三方服务返回的错误码 - * - * @return 错误码,可能为 null - */ - public Integer getErrorCode() { - return errorCode; - } - - /** - * 获取错误类别 - * - * @return 错误类别,不会为 null - */ - public ErrorCategory getCategory() { - return category; - } - - /** - * 判断是否为参数验证错误 - * - * @return true 如果是参数验证错误 - */ - public boolean isValidationError() { - return category == ErrorCategory.VALIDATION_ERROR; - } - - /** - * 判断是否为认证或授权错误 - * - * @return true 如果是认证或授权错误 - */ - public boolean isAuthenticationError() { - return category == ErrorCategory.AUTHENTICATION_ERROR; - } - - /** - * 判断是否为资源不存在错误 - * - * @return true 如果是资源不存在错误 - */ - public boolean isResourceNotFound() { - return category == ErrorCategory.RESOURCE_NOT_FOUND; - } - - /** - * 判断是否为业务规则违反 - * - * @return true 如果是业务规则违反 - */ - public boolean isBusinessRuleViolation() { - return category == ErrorCategory.BUSINESS_RULE_VIOLATION; - } - - /** - * 判断是否为不支持的操作 - * - * @return true 如果是不支持的操作 - */ - public boolean isUnsupportedOperation() { - return category == ErrorCategory.UNSUPPORTED_OPERATION; - } - - /** - * 判断是否为数据冲突 - * - * @return true 如果是数据冲突 - */ - public boolean isConflict() { - return category == ErrorCategory.CONFLICT; - } -} diff --git a/src/main/java/com/ycwl/basic/facebody/exceptions/RetryableFaceBodyException.java b/src/main/java/com/ycwl/basic/facebody/exceptions/RetryableFaceBodyException.java deleted file mode 100644 index 96124600..00000000 --- a/src/main/java/com/ycwl/basic/facebody/exceptions/RetryableFaceBodyException.java +++ /dev/null @@ -1,139 +0,0 @@ -package com.ycwl.basic.facebody.exceptions; - -/** - * 可重试的人脸识别异常 - * - *

表示操作失败但可以通过重试解决的异常场景,通常由以下原因引起: - *

- * - *

调用方应捕获此异常并实现重试机制,建议采用指数退避策略。 - * - * @see FaceBodyException - * @see NonRetryableFaceBodyException - */ -public class RetryableFaceBodyException extends FaceBodyException { - - private final Integer errorCode; - private final Integer maxRetries; - private final Long retryAfterMillis; - - /** - * 构造一个可重试异常 - * - * @param message 错误消息 - */ - public RetryableFaceBodyException(String message) { - super(message); - this.errorCode = null; - this.maxRetries = null; - this.retryAfterMillis = null; - } - - /** - * 构造一个可重试异常,包含原始异常信息 - * - * @param message 错误消息 - * @param cause 原始异常 - */ - public RetryableFaceBodyException(String message, Throwable cause) { - super(message, cause); - this.errorCode = null; - this.maxRetries = null; - this.retryAfterMillis = null; - } - - /** - * 构造一个可重试异常,包含错误码 - * - * @param message 错误消息 - * @param errorCode 第三方服务返回的错误码 - */ - public RetryableFaceBodyException(String message, Integer errorCode) { - super(message); - this.errorCode = errorCode; - this.maxRetries = null; - this.retryAfterMillis = null; - } - - /** - * 构造一个可重试异常,包含完整的重试信息 - * - * @param message 错误消息 - * @param errorCode 第三方服务返回的错误码 - * @param maxRetries 建议的最大重试次数 - * @param retryAfterMillis 建议的重试延迟时间(毫秒) - */ - public RetryableFaceBodyException(String message, Integer errorCode, Integer maxRetries, Long retryAfterMillis) { - super(message); - this.errorCode = errorCode; - this.maxRetries = maxRetries; - this.retryAfterMillis = retryAfterMillis; - } - - /** - * 构造一个可重试异常,包含原始异常和完整的重试信息 - * - * @param message 错误消息 - * @param cause 原始异常 - * @param errorCode 第三方服务返回的错误码 - * @param maxRetries 建议的最大重试次数 - * @param retryAfterMillis 建议的重试延迟时间(毫秒) - */ - public RetryableFaceBodyException(String message, Throwable cause, Integer errorCode, Integer maxRetries, Long retryAfterMillis) { - super(message, cause); - this.errorCode = errorCode; - this.maxRetries = maxRetries; - this.retryAfterMillis = retryAfterMillis; - } - - /** - * 获取第三方服务返回的错误码 - * - * @return 错误码,可能为 null - */ - public Integer getErrorCode() { - return errorCode; - } - - /** - * 获取建议的最大重试次数 - * - * @return 最大重试次数,可能为 null(表示使用默认值) - */ - public Integer getMaxRetries() { - return maxRetries; - } - - /** - * 获取建议的重试延迟时间 - * - * @return 重试延迟时间(毫秒),可能为 null(表示使用默认退避策略) - */ - public Long getRetryAfterMillis() { - return retryAfterMillis; - } - - /** - * 判断是否有明确的重试延迟时间建议 - * - * @return true 如果有明确的延迟时间建议 - */ - public boolean hasRetryAfter() { - return retryAfterMillis != null && retryAfterMillis > 0; - } - - /** - * 判断是否有建议的最大重试次数 - * - * @return true 如果有明确的重试次数限制 - */ - public boolean hasMaxRetries() { - return maxRetries != null && maxRetries > 0; - } -}