diff --git a/src/main/java/com/weaver/seconddev/portal/action/entry/BankCardRecognitionAction.java b/src/main/java/com/weaver/seconddev/portal/action/entry/BankCardRecognitionAction.java new file mode 100644 index 0000000..610a3bc --- /dev/null +++ b/src/main/java/com/weaver/seconddev/portal/action/entry/BankCardRecognitionAction.java @@ -0,0 +1,99 @@ +package com.weaver.seconddev.portal.action.entry; + +import cn.hutool.core.convert.Convert; +import cn.hutool.core.io.IoUtil; +import cn.hutool.http.HttpRequest; +import cn.hutool.http.HttpResponse; +import com.alibaba.fastjson.JSON; +import com.alibaba.fastjson.JSONArray; +import com.alibaba.fastjson.JSONObject; +import com.weaver.common.base.entity.result.WeaResult; +import com.weaver.esb.api.rpc.EsbServerlessRpcRemoteInterface; +import com.weaver.eteams.file.client.file.FileData; +import com.weaver.eteams.file.client.file.FileObj; +import com.weaver.file.ud.api.FileDownloadService; +import com.weaver.seconddev.portal.constant.ApplicationConfigConstant; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import java.io.IOException; +import java.io.InputStream; +import java.util.HashMap; +import java.util.Map; + +/** + * @author:dxfeng + * @createTime: 2025/07/29 + * @version: 1.0 + */ +@Slf4j +@Service("bankCardRecognitionAction") +public class BankCardRecognitionAction implements EsbServerlessRpcRemoteInterface { + + @Autowired + FileDownloadService fileDownloadService; + + @Override + public WeaResult> execute(Map params) { + Map returnMap = new HashMap<>(); + Long fileId = Convert.toLong(params.get("fileId"), null); + log.error("fileId==" + fileId); + if (null == fileId) { + return WeaResult.success(returnMap); + } + FileData fileData = fileDownloadService.downloadFile(fileId); + FileObj fileObj = fileData.getFileObj(); + InputStream inputStream = fileData.getInputStream(); + String fileName = fileObj.getName(); + log.error("fileName==" + fileName); + try { + log.error("inputStream==" + inputStream.available()); + } catch (IOException e) { + log.error("inputStream获取异常", e); + throw new RuntimeException(e); + } + + String response = callBankCardOcrApi(inputStream, fileName); + log.error("response==" + response); + // 正面响应数据 + JSONObject jsonObject = JSONObject.parseObject(response); + if (jsonObject.getBoolean("isSuccess")) { + JSONObject data = jsonObject.getJSONObject("data"); + JSONArray resultArray = data.getJSONArray("result"); + if (resultArray.size() > 0) { + JSONObject result = resultArray.getJSONObject(0); + returnMap.put("org", result.get("org")); + returnMap.put("number", result.get("number")); + returnMap.put("valid_thru", result.get("valid_thru")); + returnMap.put("type", result.get("type")); + returnMap.put("valid_from", result.get("valid_from")); + returnMap.put("holder", result.get("holder")); + } + + } + log.error("returnMap==" + JSON.toJSONString(returnMap)); + return WeaResult.success(returnMap); + } + + /** + * 调用千里聆身份证识别接口 + * + * @param inputStream 文件输入流 + * @param fileName 文件名 + * @return + */ + public static String callBankCardOcrApi(InputStream inputStream, String fileName) { + byte[] bytes = IoUtil.readBytes(inputStream); + long currentTime = System.currentTimeMillis(); + HttpResponse response = HttpRequest.post(ApplicationConfigConstant.BANK_CARD_OCR_URL) + .header("Content-Type", "multipart/form-data") + .header("sign", IDCardRecognitionAction.getSign(currentTime)) + .header("appId", ApplicationConfigConstant.OCR_APP_ID) + .header("timestamp", String.valueOf(currentTime)) + .form("image_file", bytes, fileName) + .execute(); + return response.body(); + } + +} diff --git a/src/main/java/com/weaver/seconddev/portal/action/entry/IDCardRecognitionAction.java b/src/main/java/com/weaver/seconddev/portal/action/entry/IDCardRecognitionAction.java new file mode 100644 index 0000000..8af72f1 --- /dev/null +++ b/src/main/java/com/weaver/seconddev/portal/action/entry/IDCardRecognitionAction.java @@ -0,0 +1,208 @@ +package com.weaver.seconddev.portal.action.entry; + +import cn.hutool.core.convert.Convert; +import cn.hutool.core.io.IoUtil; +import cn.hutool.http.HttpRequest; +import cn.hutool.http.HttpResponse; +import com.alibaba.fastjson.JSON; +import com.alibaba.fastjson.JSONArray; +import com.alibaba.fastjson.JSONObject; +import com.weaver.common.base.entity.result.WeaResult; +import com.weaver.esb.api.rpc.EsbServerlessRpcRemoteInterface; +import com.weaver.eteams.file.client.file.FileData; +import com.weaver.eteams.file.client.file.FileObj; +import com.weaver.file.ud.api.FileDownloadService; +import com.weaver.seconddev.portal.constant.ApplicationConfigConstant; +import com.weaver.seconddev.portal.entity.param.BaseParam; +import com.weaver.seconddev.portal.mapper.dictionary.DataConvertMapper; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang.StringUtils; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import java.io.IOException; +import java.io.InputStream; +import java.math.BigInteger; +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; +import java.time.LocalDate; +import java.time.Period; +import java.time.format.DateTimeFormatter; +import java.util.HashMap; +import java.util.Map; + +/** + * @author:dxfeng + * @createTime: 2025/07/29 + * @version: 1.0 + */ +@Slf4j +@Service("idCardRecognitionAction") +public class IDCardRecognitionAction implements EsbServerlessRpcRemoteInterface { + + @Autowired + FileDownloadService fileDownloadService; + @Autowired + DataConvertMapper dataConvertMapper; + + BaseParam baseParam = new BaseParam(); + + @Override + public WeaResult> execute(Map params) { + Map returnMap = new HashMap<>(); + Long fileId = Convert.toLong(params.get("fileId"), null); + log.error("fileId==" + fileId); + String nationalityConvert = Convert.toStr(params.get("nationalityConvert"), ""); + log.error("nationalityConvert==" + nationalityConvert); + if (null == fileId) { + return WeaResult.success(returnMap); + } + FileData fileData = fileDownloadService.downloadFile(fileId); + FileObj fileObj = fileData.getFileObj(); + InputStream inputStream = fileData.getInputStream(); + String fileName = fileObj.getName(); + log.error("fileName==" + fileName); + try { + log.error("inputStream==" + inputStream.available()); + } catch (IOException e) { + log.error("inputStream获取异常", e); + throw new RuntimeException(e); + } + + String response = callIdCardOcrApi(inputStream, fileName); + // 正面响应数据 + //String response = "{\"isSuccess\": true, \"data\": {\"page_num\": \"1\", \"result\": [{\"姓名\": \"王某某\", \"性别\": \"男\", \"民族\": \"汉\", \"出生\": \"1989年3月21日\", \"住址\": \"上海市浦东新区塘桥街道蓝村路xxx号\", \"公民身份号码\": \"370112198903217890\", \"标签\": \"头像面\"}]}, \"status_code\": 5200}"; + log.error("response==" + response); + + JSONObject jsonObject = JSONObject.parseObject(response); + if (jsonObject.getBoolean("isSuccess")) { + JSONObject data = jsonObject.getJSONObject("data"); + JSONArray resultArray = data.getJSONArray("result"); + if (resultArray.size() > 0) { + JSONObject result = resultArray.getJSONObject(0); + returnMap.put("name", result.getString("姓名")); + returnMap.put("sex", result.getString("性别")); + String nation = result.getString("民族"); + if (StringUtils.isNotBlank(nation) && StringUtils.isNotBlank(nationalityConvert)) { + Long nationId = dataConvertMapper.getIdByName(baseParam, "nation", nation + "族"); + log.error("nationId==" + nationId); + nation = Convert.toStr(nationId, ""); + log.error("nation==" + nation); + + } + returnMap.put("nation", nation); + + returnMap.put("birthday", Convert.toStr(result.getString("出生"), "").replace("年", "-").replace("月", "-").replace("日", "-")); + returnMap.put("address", result.getString("住址")); + String idNumber = result.getString("公民身份号码"); + returnMap.put("idCard", idNumber); + // 根据身份证号,计算年龄、 性别 + if (StringUtils.isNotBlank(idNumber)) { + returnMap.put("age", getAge(idNumber)); + returnMap.put("gender", getGender(idNumber)); + } + + returnMap.put("issueAuthority", result.getString("签发机关")); + returnMap.put("validity", result.getString("有效期限")); + } + } + log.error("returnMap==" + JSON.toJSONString(returnMap)); + return WeaResult.success(returnMap); + } + + /** + * 调用千里聆身份证识别接口 + * + * @param inputStream 文件输入流 + * @param fileName 文件名 + * @return + */ + public static String callIdCardOcrApi(InputStream inputStream, String fileName) { + byte[] bytes = IoUtil.readBytes(inputStream); + long currentTime = System.currentTimeMillis(); + HttpResponse response = HttpRequest.post(ApplicationConfigConstant.ID_CARD_OCR_URL) + .header("Content-Type", "multipart/form-data") + .header("sign", getSign(currentTime)) + .header("appId", ApplicationConfigConstant.OCR_APP_ID) + .header("timestamp", String.valueOf(currentTime)) + .form("img", bytes, fileName) + .execute(); + return response.body(); + } + + /** + * 千里聆签名 + * + * @param timestamp 当前时间戳(毫秒数) + * @return + */ + public static String getSign(long timestamp) { + try { + MessageDigest md5 = MessageDigest.getInstance("MD5"); + md5.update(ApplicationConfigConstant.OCR_APP_ID.getBytes()); + md5.update((timestamp + "").getBytes()); + md5.update(ApplicationConfigConstant.OCR_APP_SECRET.getBytes()); + byte[] bytes = md5.digest(); + return (new BigInteger(1, bytes)).toString(16); + } catch (NoSuchAlgorithmException var8) { + throw new RuntimeException("不支持的加密算法", var8); + } + } + + /** + * 根据身份证号获取性别 + * + * @param idCard 身份证号 + * @return 性别:"男"或"女" + * @throws IllegalArgumentException 身份证号不合法时抛出 + */ + public static String getGender(String idCard) { + // 校验身份证号长度 + if (idCard == null || (idCard.length() != 18 && idCard.length() != 15)) { + throw new IllegalArgumentException("身份证号长度不合法"); + } + + // 18位身份证取第17位,15位身份证取第15位 + char genderChar; + if (idCard.length() == 18) { + genderChar = idCard.charAt(16); + } else { + genderChar = idCard.charAt(14); + } + + // 奇数为男,偶数为女 + return (Integer.parseInt(String.valueOf(genderChar)) % 2 == 1) ? "male" : "female"; + } + + /** + * 根据身份证号计算年龄 + * + * @param idCard 身份证号 + * @return 年龄 + * @throws IllegalArgumentException 身份证号不合法时抛出 + */ + public static int getAge(String idCard) { + // 校验身份证号长度 + if (idCard == null || (idCard.length() != 18 && idCard.length() != 15)) { + throw new IllegalArgumentException("身份证号长度不合法"); + } + + // 解析出生日期 + LocalDate birthDate; + if (idCard.length() == 18) { + // 18位身份证:第7-14位为出生日期(yyyyMMdd) + String birthStr = idCard.substring(6, 14); + birthDate = LocalDate.parse(birthStr, DateTimeFormatter.ofPattern("yyyyMMdd")); + } else { + // 15位身份证:第7-12位为出生日期(yyMMdd),默认19xx年 + String birthStr = "19" + idCard.substring(6, 12); + birthDate = LocalDate.parse(birthStr, DateTimeFormatter.ofPattern("yyyyMMdd")); + } + + // 计算与当前日期的差距 + LocalDate now = LocalDate.now(); + Period period = Period.between(birthDate, now); + + return period.getYears(); + } +} diff --git a/src/main/java/com/weaver/seconddev/portal/constant/ApplicationConfigConstant.java b/src/main/java/com/weaver/seconddev/portal/constant/ApplicationConfigConstant.java index 20e2acf..d8dbb39 100644 --- a/src/main/java/com/weaver/seconddev/portal/constant/ApplicationConfigConstant.java +++ b/src/main/java/com/weaver/seconddev/portal/constant/ApplicationConfigConstant.java @@ -23,4 +23,17 @@ public class ApplicationConfigConstant { * 组织中心APP Secret */ public static final String ORGANIZATION_APP_SECRET = "332ed6328a15f6189efa4d2ac5935bc1"; + + + /** + * 千里聆服务APP ID + */ + public static final String OCR_APP_ID = "6ou6wvl8"; + /** + * 千里聆服务APP SECRET + */ + public static final String OCR_APP_SECRET = "53fc247ffe4f3e8d6c96a5d0a9a222a7"; + + public static final String ID_CARD_OCR_URL = "https://open.easst.cn/openapi/rest/common/idcardocr"; + public static final String BANK_CARD_OCR_URL = "https://open.easst.cn/openapi/rest/common/bank_card_ocr"; } diff --git a/src/main/java/com/weaver/seconddev/portal/mapper/dictionary/DataConvertMapper.java b/src/main/java/com/weaver/seconddev/portal/mapper/dictionary/DataConvertMapper.java new file mode 100644 index 0000000..e0beb90 --- /dev/null +++ b/src/main/java/com/weaver/seconddev/portal/mapper/dictionary/DataConvertMapper.java @@ -0,0 +1,23 @@ +package com.weaver.seconddev.portal.mapper.dictionary; + +import com.weaver.seconddev.portal.entity.param.BaseParam; +import org.apache.ibatis.annotations.Mapper; +import org.apache.ibatis.annotations.Param; + +/** + * @author:dxfeng + * @createTime: 2025/07/29 + * @version: 1.0 + */ +@Mapper +public interface DataConvertMapper { + /** + * 根据名称、类型获取ID + * + * @param param + * @param type + * @param name + * @return + */ + Long getIdByName(@Param("param") BaseParam param, @Param("type") String type, @Param("name") String name); +} diff --git a/src/main/resources/mapper/dictionary/DataConvertMapper.xml b/src/main/resources/mapper/dictionary/DataConvertMapper.xml new file mode 100644 index 0000000..5108878 --- /dev/null +++ b/src/main/resources/mapper/dictionary/DataConvertMapper.xml @@ -0,0 +1,12 @@ + + + + + + +