Prechádzať zdrojové kódy

add:OCR-二手委托人接口

lyx 3 týždňov pred
rodič
commit
38e87a7df6

+ 22 - 0
alien-store/src/main/java/shop/alien/store/controller/AliController.java

@@ -27,6 +27,7 @@ import javax.servlet.http.HttpServletResponse;
 import java.text.SimpleDateFormat;
 import java.util.Date;
 import java.util.List;
+import java.util.Map;
 import java.util.Objects;
 import java.util.stream.Collectors;
 
@@ -312,4 +313,25 @@ public class AliController {
                 outTradeNo, refundAmount, refundReason, partialRefundCode);
         return aliApi.processRefund(outTradeNo, refundAmount, refundReason, partialRefundCode);
     }
+
+    /**
+     * 二手委托人识别(底层调用IDcard识别)
+     */
+    @ApiOperation("二手委托人识别(底层调用IDcard识别)")
+    @ApiOperationSupport(order = 16)
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "imageUrl", value = "图片文件", dataType = "File", paramType = "query", required = true),
+            @ApiImplicitParam(name = "ocrType", value = "OCR识别类型", dataType = "String", paramType = "query", required = true),
+            @ApiImplicitParam(name = "userName", value = "委托人姓名", dataType = "String", paramType = "query", required = true),
+            @ApiImplicitParam(name = "idCard", value = "委托人身份证号", dataType = "String", paramType = "query", required = true)
+    })
+    @PostMapping("/secondClient")
+    public R secondClient(@RequestBody Map<String, String> params)
+    {
+        try {
+            return aliApi.secondClient(params.get("imageUrl"), params.get("ocrType"), params.get("userName"), params.get("idCard"));
+        } catch (Exception e) {
+            return R.fail(e.getMessage());
+        }
+    }
 }

+ 75 - 0
alien-store/src/main/java/shop/alien/store/util/ali/AliApi.java

@@ -14,6 +14,8 @@ import lombok.extern.slf4j.Slf4j;
 import org.apache.commons.lang3.StringUtils;
 import org.springframework.beans.factory.annotation.Value;
 import org.springframework.stereotype.Component;
+import org.springframework.web.multipart.MultipartFile;
+import shop.alien.entity.result.R;
 import shop.alien.entity.store.LifeUser;
 import shop.alien.entity.store.StoreAliPayErrorLog;
 import shop.alien.entity.store.StoreAliPayLog;
@@ -22,12 +24,15 @@ import shop.alien.store.service.LifeUserService;
 import shop.alien.store.service.StoreAliPayErrorLogService;
 import shop.alien.store.service.StoreAliPayLogService;
 import shop.alien.store.service.StoreUserService;
+import shop.alien.store.util.ali.ocr.OcrStrategy;
+import shop.alien.store.util.ali.ocr.OcrStrategyFactory;
 import shop.alien.util.common.RandomCreateUtil;
 import shop.alien.util.common.UniqueRandomNumGenerator;
 import shop.alien.util.common.UrlEncode;
 import shop.alien.util.system.OSUtil;
 
 import java.text.SimpleDateFormat;
+import java.util.Base64;
 import java.util.Date;
 
 /**
@@ -51,6 +56,8 @@ public class AliApi {
     private final StoreAliPayErrorLogService storeAliPayErrorLogService;
     
 
+    private final OcrStrategyFactory ocrStrategyFactory;
+
     /**
      * 商家端appId
      */
@@ -690,6 +697,46 @@ public class AliApi {
         return sdf.format(new Date()) + RandomCreateUtil.getRandomNum(4);
     }
 
+    /**
+     * 支付宝OCR识别
+     *
+     * @param imageId 图片id
+     * @param ocrType OCR识别类型
+     * @return OCR识别结果
+     * @throws Exception 识别异常
+     */
+    public R ocrRequest(String imageId, String ocrType) throws Exception {
+        OcrStrategy strategy = ocrStrategyFactory.getStrategy(ocrType);
+        return strategy.recognize(imageId);
+    }
+
+    /**
+     * 支付宝OCR识别
+     *
+     * @param imageUrl 图片url
+     * @param ocrType OCR识别类型
+     * @return OCR识别结果
+     * @throws Exception 识别异常
+     */
+    public R ocrRequestUrl(String imageUrl, String ocrType) throws Exception {
+        OcrStrategy strategy = ocrStrategyFactory.getStrategy(ocrType);
+        return strategy.recognizeUrl(imageUrl);
+    }
+
+    /**
+     * 支付宝OCR识别(Base64方式,使用策略工厂模式)
+     * 目前没用w
+     * @param imageFile 图片文件
+     * @param ocrType OCR识别类型
+     * @return OCR识别结果
+     * @throws Exception 识别异常
+     */
+    public R ocrRequestByBase64(MultipartFile imageFile, String ocrType) throws Exception {
+        OcrStrategy strategy = ocrStrategyFactory.getStrategy(ocrType);
+        byte[] imageBytes = imageFile.getBytes();
+        String imageBase64 = Base64.getEncoder().encodeToString(imageBytes);
+        return strategy.recognizeByBase64(imageBase64);
+    }
 
     private String getMassage(String subCode) {
         switch (subCode) {
@@ -730,4 +777,32 @@ public class AliApi {
         return "支付宝请求错误";
     }
 
+    public R secondClient(String imageUrl, String ocrType, String userName, String idCard) {
+        try {
+            R r = this.ocrRequestUrl(imageUrl, ocrType);
+            r.getCode();
+            if(200 == r.getCode()) {
+                if(!JSONObject.parseObject(r.getData().toString()).containsKey("face")) {
+                    return R.fail("OCR识别异常:请上传身份证正面照片");
+                }
+                JSONObject jsonObject = JSONObject.parseObject(r.getData().toString()).getJSONObject("face").getJSONObject("data");
+                String name = jsonObject.getString("name");
+                String idCardNo = jsonObject.getString("idNumber");
+                if(userName.equals(name) && idCard.equals(idCardNo)) {
+                    return R.success("验证成功");
+                } else {
+                    return R.fail("委托人姓名或身份证号错误");
+                }
+            }
+        } catch (IllegalArgumentException e) {
+            // 7. 细化异常类型(JSON 解析失败,比如数据格式不匹配)
+            log.error("OCR 结果解析失败(数据格式异常):{}", e.getMessage(), e);
+            return R.fail("OCR识别异常:返回数据格式错误");
+        } catch (Exception e) {
+            // 8. 兜底异常(记录详细日志,便于排查)
+            log.error("委托人身份验证未知异常:{}", e.getMessage(), e);
+            return R.fail("委托人身份验证未知异常:"+e.getMessage());
+        }
+        return R.fail("OCR识别异常");
+    }
 }

+ 95 - 0
alien-store/src/main/java/shop/alien/store/util/ali/ocr/AbstractOcrStrategy.java

@@ -0,0 +1,95 @@
+package shop.alien.store.util.ali.ocr;
+
+import com.aliyun.ocr_api20210707.Client;
+import com.aliyun.tea.TeaException;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Value;
+
+/**
+ * OCR策略抽象基类
+ * 提供公共的客户端创建和异常处理逻辑
+ *
+ * @author lyx
+ * @date 2025/11/18
+ */
+@Slf4j
+public abstract class AbstractOcrStrategy implements OcrStrategy {
+    
+    @Value("${ali.ocr.accessKeyId:}")
+    protected String accessKeyId;
+    
+    @Value("${ali.ocr.accessKeySecret:}")
+    protected String accessKeySecret;
+    
+    @Value("${ali.ocr.endpoint:ocr-api.cn-hangzhou.aliyuncs.com}")
+    protected String endpoint;
+    
+    /**
+     * 创建OCR客户端
+     * 
+     * @return OCR客户端
+     * @throws Exception 创建客户端异常
+     */
+    protected Client createOcrClient() throws Exception {
+        // 工程代码建议使用更安全的无 AK 方式,凭据配置方式请参见:https://help.aliyun.com/document_detail/378657.html。
+        com.aliyun.credentials.Client credential = new com.aliyun.credentials.Client();
+        com.aliyun.teaopenapi.models.Config config = new com.aliyun.teaopenapi.models.Config()
+                .setCredential(credential);
+        
+        // 如果配置了AccessKey,则使用配置的值,否则使用默认值(仅用于测试)
+        if (org.apache.commons.lang3.StringUtils.isNotBlank(accessKeyId)) {
+            config.setAccessKeyId(accessKeyId);
+        } else {
+            // 默认值,仅用于测试,生产环境应该从配置文件读取
+            config.setAccessKeyId("LTAI5tLAUTQg7R1xaKvxAYJu");
+        }
+        
+        if (org.apache.commons.lang3.StringUtils.isNotBlank(accessKeySecret)) {
+            config.setAccessKeySecret(accessKeySecret);
+        } else {
+            // 默认值,仅用于测试,生产环境应该从配置文件读取
+            config.setAccessKeySecret("ayVk34nK9vQZ2bs5vDCYftQCEXXN3B");
+        }
+        
+        // Endpoint 请参考 https://api.aliyun.com/product/ocr-api
+        config.endpoint = endpoint;
+        
+        return new Client(config);
+    }
+    
+    /**
+     * 处理OCR识别异常
+     * 
+     * @param error 异常对象
+     * @param errorMessage 错误消息
+     * @throws Exception 重新抛出的异常
+     */
+    protected void handleOcrException(TeaException error, String errorMessage) throws Exception {
+        log.error("OCR识别失败: {}", errorMessage, error);
+        // 错误 message
+        log.error("错误信息: {}", error.getMessage());
+        // 诊断地址
+        if (error.getData() != null && error.getData().get("Recommend") != null) {
+            log.error("诊断地址: {}", error.getData().get("Recommend"));
+        }
+        com.aliyun.teautil.Common.assertAsString(error.message);
+        throw new Exception("OCR识别失败: " + errorMessage, error);
+    }
+    
+    /**
+     * 处理通用异常
+     * 
+     * @param error 异常对象
+     * @param errorMessage 错误消息
+     * @throws Exception 重新抛出的异常
+     */
+    protected void handleOcrException(Exception error, String errorMessage) throws Exception {
+        if (error instanceof TeaException) {
+            handleOcrException((TeaException) error, errorMessage);
+        } else {
+            TeaException teaException = new TeaException(error.getMessage(), error);
+            handleOcrException(teaException, errorMessage);
+        }
+    }
+}
+

+ 49 - 0
alien-store/src/main/java/shop/alien/store/util/ali/ocr/OcrStrategy.java

@@ -0,0 +1,49 @@
+package shop.alien.store.util.ali.ocr;
+
+import shop.alien.entity.result.R;
+
+/**
+ * OCR识别策略接口
+ *
+ * @author lyx
+ * @date 2025/11/18
+ */
+public interface OcrStrategy {
+
+    /**
+     * 执行OCR识别
+     *
+     * @param imageId 图片id
+     * @return OCR识别结果(JSON格式)
+     * @throws Exception 识别异常
+     */
+    R recognize(String imageId) throws Exception;
+
+    /**
+     * 执行OCR识别
+     *
+     * @param imageUrl 图片url
+     * @return OCR识别结果(JSON格式)
+     * @throws Exception 识别异常
+     */
+    R recognizeUrl(String imageUrl) throws Exception;
+
+    /**
+     * 执行OCR识别(Base64方式)
+     *
+     * @param imageBase64 图片Base64编码
+     * @return OCR识别结果(JSON格式)
+     * @throws Exception 识别异常
+     */
+    R recognizeByBase64(String imageBase64) throws Exception;
+
+    /**
+     * 获取策略类型字符串
+     *
+     * @return 策略类型字符串
+     */
+    String getType();
+}
+
+
+

+ 79 - 0
alien-store/src/main/java/shop/alien/store/util/ali/ocr/OcrStrategyFactory.java

@@ -0,0 +1,79 @@
+package shop.alien.store.util.ali.ocr;
+
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Component;
+
+import javax.annotation.PostConstruct;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * OCR策略工厂
+ * 根据OCR类型创建对应的策略实例
+ * 
+ * @author lyx
+ * @date 2025/11/18
+ */
+@Slf4j
+@Component
+public class OcrStrategyFactory {
+    
+    @Autowired
+    private List<OcrStrategy> ocrStrategies;
+    
+    private final Map<String, OcrStrategy> strategyMap = new HashMap<>();
+    
+    /**
+     * 初始化策略映射
+     */
+    @PostConstruct
+    public void init() {
+        if (ocrStrategies != null && !ocrStrategies.isEmpty()) {
+            for (OcrStrategy strategy : ocrStrategies) {
+                strategyMap.put(strategy.getType(), strategy);
+                log.info("注册OCR策略: {} -> {}", strategy.getType(), strategy.getClass().getSimpleName());
+            }
+        }
+    }
+    
+    /**
+     * 根据类型获取OCR策略
+     * 
+     * @param type OCR类型
+     * @return OCR策略实例
+     * @throws IllegalArgumentException 如果类型不存在
+     */
+    public OcrStrategy getStrategy(String type) {
+        OcrStrategy strategy = strategyMap.get(type);
+        if (strategy == null) {
+            throw new IllegalArgumentException("不支持的OCR类型: " + type);
+        }
+        return strategy;
+    }
+    
+    /**
+     * 根据类型代码获取OCR策略
+     * 
+     * @param typeCode 类型代码
+     * @return OCR策略实例
+     * @throws IllegalArgumentException 如果类型不存在
+     */
+    public OcrStrategy getStrategyByCode(String typeCode) {
+        return getStrategy(typeCode);
+    }
+    
+    /**
+     * 检查是否支持指定的OCR类型
+     * 
+     * @param type OCR类型
+     * @return 是否支持
+     */
+    public boolean supports(String type) {
+        return strategyMap.containsKey(type);
+    }
+}
+
+
+

+ 115 - 0
alien-store/src/main/java/shop/alien/store/util/ali/ocr/strategy/BusinessLicenseOcrStrategy.java

@@ -0,0 +1,115 @@
+package shop.alien.store.util.ali.ocr.strategy;
+
+import com.alibaba.fastjson.JSONObject;
+import com.aliyun.ocr_api20210707.Client;
+import com.aliyun.ocr_api20210707.models.RecognizeBusinessLicenseRequest;
+import com.aliyun.ocr_api20210707.models.RecognizeBusinessLicenseResponse;
+import com.aliyun.ocr_api20210707.models.RecognizeBusinessLicenseResponseBody;
+import com.aliyun.tea.TeaException;
+import com.aliyun.teautil.models.RuntimeOptions;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.stereotype.Component;
+import shop.alien.entity.result.R;
+import shop.alien.entity.store.StoreImg;
+import shop.alien.store.service.StoreImgService;
+import shop.alien.store.util.ali.ocr.AbstractOcrStrategy;
+import shop.alien.util.common.constant.OcrTypeEnum;
+
+/**
+ * 营业执照OCR识别策略
+ *
+ * @author lyx
+ * @date 2025/11/18
+ */
+@Slf4j
+@Component
+@RequiredArgsConstructor
+public class BusinessLicenseOcrStrategy extends AbstractOcrStrategy {
+
+    private final StoreImgService storeImgService;
+
+    @Override
+    public R recognize(String imageId) throws Exception {
+
+        // 从数据库中获取图片URL
+        StoreImg storeImage = storeImgService.getById(imageId);
+        String imageUrl = storeImage.getImgUrl();
+        return recognizeUrl(imageUrl);
+    }
+
+    @Override
+    public R recognizeUrl(String imageUrl) throws Exception {
+        Client client = createOcrClient();
+        RecognizeBusinessLicenseRequest request = new RecognizeBusinessLicenseRequest()
+                .setUrl(imageUrl);
+
+        try {
+            RecognizeBusinessLicenseResponse response = client.recognizeBusinessLicenseWithOptions(
+                    request, new RuntimeOptions());
+            RecognizeBusinessLicenseResponseBody body = response.getBody();
+
+            if (body == null || body.getData() == null) {
+                throw new Exception("OCR识别返回结果为空");
+            }
+
+            JSONObject jsonObject = JSONObject.parseObject(body.getData());
+            log.info("营业执照OCR识别成功: {}", jsonObject.getJSONObject("data"));
+            return R.data(jsonObject.getJSONObject("data"));
+
+        } catch (TeaException error) {
+            handleOcrException(error, "营业执照识别失败");
+            return R.fail("营业执照识别失败");
+        } catch (Exception error) {
+            handleOcrException(error, "营业执照识别异常");
+            return R.fail("营业执照识别异常");
+        }
+    }
+
+    @Override
+    public R recognizeByBase64(String imageBase64) throws Exception {
+        Client client = createOcrClient();
+        // 将Base64字符串转换为字节数组,然后创建InputStream
+        byte[] imageBytes = java.util.Base64.getDecoder().decode(imageBase64);
+        java.io.InputStream imageInputStream = new java.io.ByteArrayInputStream(imageBytes);
+
+        RecognizeBusinessLicenseRequest request = new RecognizeBusinessLicenseRequest()
+                .setBody(imageInputStream);
+
+        try {
+            RecognizeBusinessLicenseResponse response = client.recognizeBusinessLicenseWithOptions(
+                    request, new RuntimeOptions());
+            RecognizeBusinessLicenseResponseBody body = response.getBody();
+
+            if (body == null || body.getData() == null) {
+                throw new Exception("OCR识别返回结果为空");
+            }
+
+            JSONObject jsonObject = JSONObject.parseObject(body.getData());
+            log.info("营业执照OCR识别成功: {}", jsonObject.getJSONObject("data"));
+            return R.data(jsonObject.getJSONObject("data"));
+
+        } catch (TeaException error) {
+            handleOcrException(error, "营业执照识别失败");
+            return R.fail("营业执照识别失败");
+        } catch (Exception error) {
+            handleOcrException(error, "营业执照识别异常");
+            return R.fail("营业执照识别异常");
+        } finally {
+            // 关闭流
+            if (imageInputStream != null) {
+                try {
+                    imageInputStream.close();
+                } catch (java.io.IOException e) {
+                    log.warn("关闭输入流失败", e);
+                }
+            }
+        }
+    }
+
+    @Override
+    public String getType() {
+        return OcrTypeEnum.BUSINESS_LICENSE.getCode();
+    }
+}
+

+ 111 - 0
alien-store/src/main/java/shop/alien/store/util/ali/ocr/strategy/FoodManageLicenseOcrStrategy.java

@@ -0,0 +1,111 @@
+package shop.alien.store.util.ali.ocr.strategy;
+
+import com.alibaba.fastjson.JSONObject;
+import com.aliyun.ocr_api20210707.Client;
+import com.aliyun.ocr_api20210707.models.*;
+import com.aliyun.tea.TeaException;
+import com.aliyun.teautil.models.RuntimeOptions;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.stereotype.Component;
+import shop.alien.entity.result.R;
+import shop.alien.entity.store.StoreImg;
+import shop.alien.store.service.StoreImgService;
+import shop.alien.store.util.ali.ocr.AbstractOcrStrategy;
+import shop.alien.util.common.constant.OcrTypeEnum;
+
+/**
+ * 食品经营许可证OCR识别策略
+ *
+ * @author lyx
+ * @date 2025/11/18
+ */
+@Slf4j
+@Component
+@RequiredArgsConstructor
+public class FoodManageLicenseOcrStrategy extends AbstractOcrStrategy {
+
+    private final StoreImgService storeImgService;
+
+    @Override
+    public R recognize(String imageId) throws Exception {
+
+        // 从数据库中获取图片URL
+        StoreImg storeImage = storeImgService.getById(imageId);
+        String imageUrl = storeImage.getImgUrl();
+        return recognizeUrl(imageUrl);
+    }
+
+    @Override
+    public R recognizeUrl(String imageUrl) throws Exception {
+        Client client = createOcrClient();
+        RecognizeFoodManageLicenseRequest request = new RecognizeFoodManageLicenseRequest()
+                .setUrl(imageUrl);
+
+        try {
+            RecognizeFoodManageLicenseResponse response = client.recognizeFoodManageLicenseWithOptions(
+                    request, new RuntimeOptions());
+            RecognizeFoodManageLicenseResponseBody body = response.getBody();
+
+            if (body == null || body.getData() == null) {
+                throw new Exception("OCR识别返回结果为空");
+            }
+
+            JSONObject jsonObject = JSONObject.parseObject(body.getData());
+            log.info("食品经营许可证OCR识别成功: {}", jsonObject.getJSONObject("data"));
+            return R.data(jsonObject.getJSONObject("data"));
+
+        } catch (TeaException error) {
+            handleOcrException(error, "食品经营许可证识别失败");
+            return R.fail("食品经营许可证识别失败");
+        } catch (Exception error) {
+            handleOcrException(error, "食品经营许可证识别异常");
+            return R.fail("食品经营许可证识别异常");
+        }
+    }
+
+    @Override
+    public R recognizeByBase64(String imageBase64) throws Exception {
+        Client client = createOcrClient();
+        // 将Base64字符串转换为字节数组,然后创建InputStream
+        byte[] imageBytes = java.util.Base64.getDecoder().decode(imageBase64);
+        java.io.InputStream imageInputStream = new java.io.ByteArrayInputStream(imageBytes);
+
+        RecognizeFoodManageLicenseRequest request = new RecognizeFoodManageLicenseRequest()
+                .setBody(imageInputStream);
+
+        try {
+            RecognizeFoodManageLicenseResponse response = client.recognizeFoodManageLicenseWithOptions(
+                    request, new RuntimeOptions());
+            RecognizeFoodManageLicenseResponseBody body = response.getBody();
+
+            if (body == null || body.getData() == null) {
+                throw new Exception("OCR识别返回结果为空");
+            }
+
+            JSONObject jsonObject = JSONObject.parseObject(body.getData());
+            log.info("食品经营许可证OCR识别成功: {}", jsonObject.getJSONObject("data"));
+            return R.data(jsonObject.getJSONObject("data"));
+        } catch (TeaException error) {
+            handleOcrException(error, "食品经营许可证识别失败");
+            return R.fail("食品经营许可证识别失败");
+        } catch (Exception error) {
+            handleOcrException(error, "食品经营许可证识别异常");
+            return R.fail("食品经营许可证识别异常");
+        } finally {
+            // 关闭流
+            if (imageInputStream != null) {
+                try {
+                    imageInputStream.close();
+                } catch (java.io.IOException e) {
+                    log.warn("关闭输入流失败", e);
+                }
+            }
+        }
+    }
+
+    @Override
+    public String getType() {
+        return OcrTypeEnum.FOOD_MANAGE_LICENSE.getCode();
+    }
+}

+ 113 - 0
alien-store/src/main/java/shop/alien/store/util/ali/ocr/strategy/IdCardOcrStrategy.java

@@ -0,0 +1,113 @@
+package shop.alien.store.util.ali.ocr.strategy;
+
+import com.alibaba.fastjson.JSONObject;
+import com.aliyun.ocr_api20210707.Client;
+import com.aliyun.ocr_api20210707.models.*;
+import com.aliyun.tea.TeaException;
+import com.aliyun.teautil.models.RuntimeOptions;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.stereotype.Component;
+import shop.alien.entity.result.R;
+import shop.alien.entity.store.StoreImg;
+import shop.alien.store.service.StoreImgService;
+import shop.alien.store.util.ali.ocr.AbstractOcrStrategy;
+import shop.alien.util.common.constant.OcrTypeEnum;
+
+/**
+ * 身份证OCR识别策略
+ *
+ * @author lyx
+ * @date 2025/11/18
+ */
+@Slf4j
+@Component
+@RequiredArgsConstructor
+public class IdCardOcrStrategy extends AbstractOcrStrategy {
+
+    private final StoreImgService storeImgService;
+
+    @Override
+    public R recognize(String imageId) throws Exception {
+
+        // 从数据库中获取图片URL
+        StoreImg storeImage = storeImgService.getById(imageId);
+        String imageUrl = storeImage.getImgUrl();
+        return recognizeUrl(imageUrl);
+    }
+
+    @Override
+    public R recognizeUrl(String imageUrl) throws Exception {
+        Client client = createOcrClient();
+        RecognizeIdcardRequest request = new RecognizeIdcardRequest()
+                .setUrl(imageUrl); // 默认识别正面,可根据需要修改为 "back" 识别反面
+
+        try {
+            RecognizeIdcardResponse response = client.recognizeIdcardWithOptions(
+                    request, new RuntimeOptions());
+            RecognizeIdcardResponseBody body = response.getBody();
+
+            if (body == null || body.getData() == null) {
+                throw new Exception("OCR识别返回结果为空");
+            }
+
+            JSONObject jsonObject = JSONObject.parseObject(body.getData());
+            log.info("身份证OCR识别成功: {}", jsonObject.getJSONObject("data"));
+            return R.data(jsonObject.getJSONObject("data"));
+
+        } catch (TeaException error) {
+            handleOcrException(error, "身份证识别失败");
+            return R.fail("身份证识别失败");
+        } catch (Exception error) {
+            handleOcrException(error, "身份证识别异常");
+            return R.fail("身份证识别异常");
+        }
+    }
+
+    @Override
+    public R recognizeByBase64(String imageBase64) throws Exception {
+        Client client = createOcrClient();
+        // 将Base64字符串转换为字节数组,然后创建InputStream
+        byte[] imageBytes = java.util.Base64.getDecoder().decode(imageBase64);
+        java.io.InputStream imageInputStream = new java.io.ByteArrayInputStream(imageBytes);
+
+        RecognizeIdcardRequest request = new RecognizeIdcardRequest()
+                .setBody(imageInputStream); // 默认识别正面,可根据需要修改为 "back" 识别反面
+
+        try {
+            RecognizeIdcardResponse response = client.recognizeIdcardWithOptions(
+                    request, new RuntimeOptions());
+            RecognizeIdcardResponseBody body = response.getBody();
+
+            if (body == null || body.getData() == null) {
+                throw new Exception("OCR识别返回结果为空");
+            }
+
+            JSONObject jsonObject = JSONObject.parseObject(body.getData());
+            log.info("身份证OCR识别成功: {}", jsonObject.getJSONObject("data"));
+            return R.data(jsonObject.getJSONObject("data"));
+
+        } catch (TeaException error) {
+            handleOcrException(error, "身份证识别失败");
+            return R.fail("身份证识别失败");
+        } catch (Exception error) {
+            handleOcrException(error, "身份证识别异常");
+            return R.fail("身份证识别异常");
+        } finally {
+            // 关闭流
+            if (imageInputStream != null) {
+                try {
+                    imageInputStream.close();
+                } catch (java.io.IOException e) {
+                    log.warn("关闭输入流失败", e);
+                }
+            }
+        }
+    }
+
+    @Override
+    public String getType() {
+        return OcrTypeEnum.ID_CARD.getCode();
+    }
+}
+

+ 7 - 0
alien-util/pom.xml

@@ -130,6 +130,13 @@
             <version>2.20.0</version>
         </dependency>
 
+        <!-- 阿里云OCR文字识别 -->
+        <dependency>
+            <groupId>com.aliyun</groupId>
+            <artifactId>ocr_api20210707</artifactId>
+            <version>3.1.3</version>
+        </dependency>
+
         <!--openoffice-->
         <dependency>
             <groupId>com.artofsolving</groupId>

+ 50 - 0
alien-util/src/main/java/shop/alien/util/common/constant/OcrTypeEnum.java

@@ -0,0 +1,50 @@
+package shop.alien.util.common.constant;
+
+/**
+ * OCR识别类型枚举
+ * 
+ * @author ssk
+ * @date 2024/12/10
+ */
+public enum OcrTypeEnum {
+    /**
+     * 营业执照识别
+     */
+    BUSINESS_LICENSE("BUSINESS_LICENSE", "营业执照识别"),
+    
+    /**
+     * 身份证识别
+     */
+    ID_CARD("ID_CARD", "身份证识别"),
+    /**
+     * 食品管理许可证识别
+     */
+    FOOD_MANAGE_LICENSE("FOOD_MANAGE_LICENSE", "食品经营许可证识别"),
+
+    /**
+     * 银行卡识别
+     */
+    BANK_CARD("BANK_CARD", "银行卡识别"),
+    
+    /**
+     * 通用文字识别
+     */
+    GENERAL("GENERAL", "通用文字识别");
+    
+    private final String code;
+    private final String description;
+    
+    OcrTypeEnum(String code, String description) {
+        this.code = code;
+        this.description = description;
+    }
+    
+    public String getCode() {
+        return code;
+    }
+    
+    public String getDescription() {
+        return description;
+    }
+}
+