NotaryDomainImpl.java 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449
  1. package me.zhengjie.domain.notary.impl;
  2. import com.alibaba.fastjson.JSONObject;
  3. import com.arcsoft.face.*;
  4. import com.arcsoft.face.enums.DetectMode;
  5. import com.arcsoft.face.enums.DetectOrient;
  6. import com.arcsoft.face.enums.ErrorInfo;
  7. import com.arcsoft.face.toolkit.ImageInfo;
  8. import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
  9. import lombok.RequiredArgsConstructor;
  10. import lombok.extern.slf4j.Slf4j;
  11. import me.zhengjie.base.ResultCode;
  12. import me.zhengjie.base.file.FileHandle;
  13. import me.zhengjie.base.file.FileHandleVo;
  14. import me.zhengjie.base.file.MinioFileHandle;
  15. import me.zhengjie.base.util.FileUploadUtil;
  16. import me.zhengjie.base.util.JuheServiceUtil;
  17. import me.zhengjie.base.util.tencent.utils.HttpClientUtil;
  18. import me.zhengjie.base.util.tencent.utils.Img2Base64Util;
  19. import me.zhengjie.dao.mybatis.ThirdPartyInterfaceRepository;
  20. import me.zhengjie.dao.mybatis.entity.*;
  21. import me.zhengjie.dao.mybatis.mapper.ContractOrderMapper;
  22. import me.zhengjie.dao.mybatis.mapper.FileInfoMapper;
  23. import me.zhengjie.dao.mybatis.mapper.SysUserMapper;
  24. import me.zhengjie.dao.mybatis.mapper.UserAXQInfoMapper;
  25. import me.zhengjie.domain.img.CompareImageDomain;
  26. import me.zhengjie.domain.notary.NotaryDomain;
  27. import me.zhengjie.utils.RedisUtils;
  28. import org.springframework.stereotype.Component;
  29. import org.springframework.util.ObjectUtils;
  30. import org.springframework.web.multipart.MultipartFile;
  31. import java.io.File;
  32. import java.io.IOException;
  33. import java.util.ArrayList;
  34. import java.util.List;
  35. import java.util.concurrent.TimeUnit;
  36. import static com.arcsoft.face.toolkit.ImageFactory.getRGBData;
  37. @Component
  38. @RequiredArgsConstructor
  39. @Slf4j
  40. public class NotaryDomainImpl implements NotaryDomain {
  41. private final SysUserMapper userMapper;
  42. private final UserAXQInfoMapper userAXQInfoMapper;
  43. private final ContractOrderMapper contractOrderMapper;
  44. private final FileInfoMapper fileInfoMapper;
  45. private final RedisUtils redis;
  46. private final ThirdPartyInterfaceRepository thirdPartyInterfaceRepository;
  47. //以下为测试部署环境
  48. private static final String APPID = "6F5JVsCCeSEbBSLSXWVwPki71yK5Y4Jf7oUjA4Y3mLtu";
  49. private static final String SDKKEY = "5qKM4a9oNSXNhXdwd7Ycrvnxdw2JXjcyeccYmb4zND7S";
  50. private static final String LIBPATH = "/home/tom/nt-test/lib/so";
  51. //以下为本地测试
  52. // private static final String APPID = "H6Er8ksqAcKc7Dk4Yum9Rkz4g9Gm77ZXAEMgvenjkTQQ";
  53. // private static final String SDKKEY = "Abd9D8xZQUqG8vDtU2a1tEkdfThkK1tZppjXzydGwEd6";
  54. // private static final String LIBPATH = "D:\\arcsoft_lib";
  55. /**
  56. * 获取base64取消文件头
  57. *
  58. * @param base64
  59. * @return
  60. */
  61. public String getBase64(String base64) {
  62. String[] base64s = base64.split("base64");
  63. if (base64s != null && base64s.length > 1) {
  64. base64 = base64s[1];
  65. }
  66. return base64;
  67. }
  68. /**
  69. * app端的人脸核身
  70. *
  71. * @param compareImageDomain
  72. * @return
  73. */
  74. public ResultCode appCompareFace(CompareImageDomain compareImageDomain) {
  75. String base64 = compareImageDomain.getBase64();
  76. if (base64 == null) {
  77. return ResultCode.PARAM_IS_BLANK;
  78. }
  79. base64 = getBase64(base64);
  80. String idCard = compareImageDomain.getIdCard();
  81. String nickName = compareImageDomain.getName();
  82. //根据业务编号获取订单 判断当事人名字
  83. String businessNo = compareImageDomain.getBusinessNo();
  84. QueryWrapper<ContractOrderEntity> contractOrderEntityQueryWrapper = new QueryWrapper<>();
  85. contractOrderEntityQueryWrapper.eq("business_no", businessNo);
  86. ContractOrderEntity contractOrderEntity = contractOrderMapper.selectOne(contractOrderEntityQueryWrapper);
  87. Integer id = null;
  88. //看是哪个当事人
  89. if (nickName.equals(contractOrderEntity.getName())) {
  90. id = contractOrderEntity.getCompareImgId();
  91. } else if (nickName.equals(contractOrderEntity.getJointName())) {
  92. id = contractOrderEntity.getJointCompareImgId();
  93. }
  94. //后面需要的三个参数 base64 身份证号 姓名
  95. String redisCount = "idCard:" + idCard + ":COUNT:";
  96. Integer count = null;
  97. if (redis.hasKey(redisCount)) {
  98. count = (Integer) redis.get(redisCount);
  99. System.err.println("redis存入次数" + count);
  100. }
  101. File base = null;
  102. try {
  103. base = File.createTempFile("compare", ".jpeg");
  104. base.deleteOnExit();
  105. } catch (IOException e) {
  106. throw new RuntimeException(e);
  107. }
  108. if (!Img2Base64Util.generateImage(base64, base.getPath())) {
  109. log.info("base64转图片异常");
  110. return ResultCode.FACEAUTH_FAIL;
  111. }
  112. FileUploadUtil.saveFaceImage(base64);
  113. FileInfoEntity fileInfo = uploadFaceFile(base64);
  114. boolean redisFlag = (count != null && count >= 2);
  115. if (ObjectUtils.isEmpty(id) || redisFlag) {
  116. //要做人脸核身
  117. ThirdPartyInterfaceEntity thirdPartyInterfaceEntity = JuheServiceUtil.URLPost(idCard, nickName, base64);
  118. thirdPartyInterfaceEntity.setFileId(fileInfo.getId() + "");
  119. thirdPartyInterfaceEntity.setBusinessNo(compareImageDomain.getBusinessNo());
  120. thirdPartyInterfaceRepository.insert(thirdPartyInterfaceEntity);
  121. if (thirdPartyInterfaceEntity.getResult() == 1) {
  122. ContractOrderEntity orderEntity = new ContractOrderEntity();
  123. orderEntity.setId(contractOrderEntity.getId());
  124. //判断是共同责任人还是当事人
  125. if (nickName.equals(contractOrderEntity.getName())) {
  126. orderEntity.setCompareImgId(fileInfo.getId());
  127. }
  128. if (nickName.equals(contractOrderEntity.getJointName())) {
  129. orderEntity.setJointCompareImgId(fileInfo.getId());
  130. }
  131. contractOrderMapper.updateById(orderEntity);
  132. log.info("当事人人脸核身通过");
  133. redis.del(redisCount);
  134. return ResultCode.SUCCESS;
  135. }
  136. return ResultCode.LIVE_VIDEO_INVALID;
  137. }
  138. //要做照片比对
  139. if (!compareImage(base, id, redisCount, count)) {
  140. return ResultCode.LIVE_VIDEO_INVALID;
  141. }
  142. if (count != null) {
  143. redis.del(redisCount);
  144. }
  145. return ResultCode.SUCCESS;
  146. }
  147. /**
  148. * 照片比对
  149. *
  150. * @param compareImageDomain
  151. * @return
  152. */
  153. public ResultCode compareFace(CompareImageDomain compareImageDomain) {
  154. String base64 = compareImageDomain.getBase64();
  155. if (base64 == null) {
  156. return ResultCode.PARAM_IS_BLANK;
  157. }
  158. base64 = getBase64(base64);
  159. //先判断是银行客户经理还是当事人
  160. String roleId = compareImageDomain.getRoleId();
  161. SysUserEntity sysUserEntity = null;
  162. UserAXQInfoEntity userAXQInfoEntity = null;
  163. String idCard = compareImageDomain.getIdCard();
  164. String nickName = compareImageDomain.getName();
  165. QueryWrapper<ContractOrderEntity> orderEntityQueryWrapper = new QueryWrapper<>();
  166. orderEntityQueryWrapper.eq("business_no", compareImageDomain.getBusinessNo());
  167. ContractOrderEntity contractOrder = contractOrderMapper.selectOne(orderEntityQueryWrapper);
  168. if (ObjectUtils.isEmpty(contractOrder)) {
  169. return ResultCode.PARAM_IS_INVALID;
  170. }
  171. Integer compareImageId = null;
  172. boolean flag = "-1".equals(roleId);
  173. if (flag) {
  174. //说明是银行客户经理
  175. QueryWrapper<ContractOrderEntity> contractOrderEntityQueryWrapper = new QueryWrapper<>();
  176. contractOrderEntityQueryWrapper.eq("business_no", compareImageDomain.getBusinessNo()).eq("customer_name", compareImageDomain.getName());
  177. ContractOrderEntity contractOrderEntity = contractOrderMapper.selectOne(contractOrderEntityQueryWrapper);
  178. if (ObjectUtils.isEmpty(contractOrderEntity)) {
  179. return ResultCode.ACCOUNT_MANAGER_NO_ASSIGNED;
  180. }
  181. QueryWrapper<SysUserEntity> sysUserEntityQueryWrapper = new QueryWrapper<>();
  182. sysUserEntityQueryWrapper.eq("id_card", idCard).eq("nick_name", nickName);
  183. sysUserEntity = userMapper.selectOne(sysUserEntityQueryWrapper);
  184. } else {
  185. //说明是当事人
  186. if (compareImageDomain.getName().equals(contractOrder.getName())) {
  187. compareImageId = contractOrder.getCompareImgId();
  188. } else if (compareImageDomain.getName().equals(contractOrder.getJointName())) {
  189. compareImageId = contractOrder.getJointCompareImgId();
  190. }
  191. }
  192. //后面需要的三个参数 base64 身份证号 姓名
  193. String redisCount = "idCard:" + idCard + ":COUNT:";
  194. Integer count = null;
  195. if (redis.hasKey(redisCount)) {
  196. count = (Integer) redis.get(redisCount);
  197. System.err.println("redis存入次数" + count);
  198. }
  199. //将base64转成图片
  200. File base = null;
  201. try {
  202. base = File.createTempFile("compare", ".jpeg");
  203. base.deleteOnExit();
  204. } catch (IOException e) {
  205. throw new RuntimeException(e);
  206. }
  207. if (!Img2Base64Util.generateImage(base64, base.getPath())) {
  208. log.info("base64转图片异常");
  209. return ResultCode.FACEAUTH_FAIL;
  210. }
  211. boolean redisFlag = (count != null && count >= 2);
  212. if (flag) {
  213. //说明是客户经理
  214. if (sysUserEntity.getCompareImgId() == null || redisFlag) {
  215. //说明客户经理需要做人脸核身
  216. FileInfoEntity fileInfo = uploadFaceFile(base64);
  217. ThirdPartyInterfaceEntity thirdPartyInterfaceEntity = JuheServiceUtil.URLPost(idCard, nickName, base64);
  218. thirdPartyInterfaceEntity.setFileId(fileInfo.getId() + "");
  219. thirdPartyInterfaceEntity.setBusinessNo(compareImageDomain.getBusinessNo());
  220. if (thirdPartyInterfaceEntity.getResult() == 1) {
  221. //上传图片到minio FileInfo
  222. SysUserEntity bank = new SysUserEntity();
  223. bank.setUserId(sysUserEntity.getUserId());
  224. bank.setCompareImgId(fileInfo.getId());
  225. userMapper.updateById(bank);
  226. log.info("银行客户经理人脸核身通过");
  227. redis.del(redisCount);
  228. thirdPartyInterfaceRepository.insert(thirdPartyInterfaceEntity);
  229. return ResultCode.SUCCESS;
  230. }
  231. log.info("客户经理人脸核身失败");
  232. thirdPartyInterfaceRepository.insert(thirdPartyInterfaceEntity);
  233. return ResultCode.LIVE_VIDEO_INVALID;
  234. }
  235. } else if (ObjectUtils.isEmpty(compareImageId) || redisFlag) {
  236. //说明当事人需要做人脸核身
  237. FileInfoEntity fileInfo = uploadFaceFile(base64);
  238. ThirdPartyInterfaceEntity thirdPartyInterfaceEntity = JuheServiceUtil.URLPost(idCard, nickName, base64);
  239. thirdPartyInterfaceEntity.setFileId(fileInfo.getId() + "");
  240. thirdPartyInterfaceEntity.setBusinessNo(compareImageDomain.getBusinessNo());
  241. thirdPartyInterfaceRepository.insert(thirdPartyInterfaceEntity);
  242. if (thirdPartyInterfaceEntity.getResult() == 1) {
  243. //上传图片到minio FileInfo;
  244. ContractOrderEntity orderEntity = new ContractOrderEntity();
  245. orderEntity.setId(contractOrder.getId());
  246. //判断是共同责任人还是当事人
  247. if (nickName.equals(contractOrder.getName())) {
  248. orderEntity.setCompareImgId(fileInfo.getId());
  249. }
  250. if (nickName.equals(contractOrder.getJointName())) {
  251. orderEntity.setJointCompareImgId(fileInfo.getId());
  252. }
  253. redis.del(redisCount);
  254. contractOrderMapper.updateById(orderEntity);
  255. log.info("当事人人脸核身通过");
  256. return ResultCode.SUCCESS;
  257. }
  258. log.info("当事人人脸核身失败");
  259. return ResultCode.LIVE_VIDEO_INVALID;
  260. }
  261. //如果不用做人脸核身 那么就去做照片比对
  262. //获取比较图片的id
  263. Integer id = null;
  264. if (!ObjectUtils.isEmpty(sysUserEntity)) {
  265. id = sysUserEntity.getCompareImgId();
  266. }
  267. if (!ObjectUtils.isEmpty(compareImageId)) {
  268. id = compareImageId;
  269. }
  270. if (!ObjectUtils.isEmpty(id)) {
  271. if (!compareImage(base, id, redisCount, count)) {
  272. return ResultCode.LIVE_VIDEO_INVALID;
  273. }
  274. }
  275. if (count != null) {
  276. redis.del(redisCount);
  277. }
  278. return ResultCode.SUCCESS;
  279. }
  280. /**
  281. * 虹软的人脸照片比对
  282. *
  283. * @param base 照片
  284. * @param id 文件库中照片的id
  285. * @param redisCount 数据库中的键
  286. * @param count redis当中的次数
  287. * @return
  288. */
  289. public boolean compareImage(File base, Integer id, String redisCount, Integer count) {
  290. //从官网获取
  291. FaceEngine faceEngine = new FaceEngine(LIBPATH);
  292. //激活引擎
  293. int errorCode = faceEngine.activeOnline(APPID, SDKKEY);
  294. if (errorCode != ErrorInfo.MOK.getValue() && errorCode != ErrorInfo.MERR_ASF_ALREADY_ACTIVATED.getValue()) {
  295. base.delete();
  296. throw new RuntimeException("引擎激活失败");
  297. }
  298. ActiveFileInfo activeFileInfo = new ActiveFileInfo();
  299. errorCode = faceEngine.getActiveFileInfo(activeFileInfo);
  300. if (errorCode != ErrorInfo.MOK.getValue() && errorCode != ErrorInfo.MERR_ASF_ALREADY_ACTIVATED.getValue()) {
  301. base.delete();
  302. throw new RuntimeException("获取激活文件信息失败");
  303. }
  304. //引擎配置
  305. EngineConfiguration engineConfiguration = new EngineConfiguration();
  306. engineConfiguration.setDetectMode(DetectMode.ASF_DETECT_MODE_IMAGE);
  307. engineConfiguration.setDetectFaceOrientPriority(DetectOrient.ASF_OP_ALL_OUT);
  308. engineConfiguration.setDetectFaceMaxNum(10);
  309. engineConfiguration.setDetectFaceScaleVal(16);
  310. //功能配置
  311. FunctionConfiguration functionConfiguration = new FunctionConfiguration();
  312. functionConfiguration.setSupportAge(true);
  313. functionConfiguration.setSupportFace3dAngle(true);
  314. functionConfiguration.setSupportFaceDetect(true);
  315. functionConfiguration.setSupportFaceRecognition(true);
  316. functionConfiguration.setSupportGender(true);
  317. functionConfiguration.setSupportLiveness(true);
  318. functionConfiguration.setSupportIRLiveness(true);
  319. engineConfiguration.setFunctionConfiguration(functionConfiguration);
  320. //初始化引擎
  321. errorCode = faceEngine.init(engineConfiguration);
  322. if (errorCode != ErrorInfo.MOK.getValue()) {
  323. base.delete();
  324. throw new RuntimeException("初始化引擎失败");
  325. }
  326. //人脸检测
  327. ImageInfo imageInfo = getRGBData(base);
  328. List<FaceInfo> faceInfoList = new ArrayList<FaceInfo>();
  329. errorCode = faceEngine.detectFaces(imageInfo.getImageData(), imageInfo.getWidth(), imageInfo.getHeight(), imageInfo.getImageFormat(), faceInfoList);
  330. System.out.println(faceInfoList);
  331. //人脸属性检测
  332. FunctionConfiguration configuration = new FunctionConfiguration();
  333. configuration.setSupportFace3dAngle(true);
  334. errorCode = faceEngine.process(imageInfo.getImageData(), imageInfo.getWidth(), imageInfo.getHeight(), imageInfo.getImageFormat(), faceInfoList, configuration);
  335. //3D信息检测
  336. List<Face3DAngle> face3DAngleList = new ArrayList<Face3DAngle>();
  337. errorCode = faceEngine.getFace3DAngle(face3DAngleList);
  338. //俯仰角
  339. float pitch = Math.abs(face3DAngleList.get(0).getPitch());
  340. //偏向角
  341. float yaw = Math.abs(face3DAngleList.get(0).getYaw());
  342. log.info("俯仰角:{}",pitch);
  343. log.info("偏向角:{}",yaw);
  344. if (pitch >= 10 || yaw >= 10) {
  345. //照片不符合标准
  346. log.info("图片不符合标准");
  347. base.delete();
  348. return false;
  349. }
  350. //特征提取
  351. FaceFeature faceFeature = new FaceFeature();
  352. errorCode = faceEngine.extractFaceFeature(imageInfo.getImageData(), imageInfo.getWidth(), imageInfo.getHeight(), imageInfo.getImageFormat(), faceInfoList.get(0), faceFeature);
  353. //根据id 查找文件
  354. QueryWrapper<FileInfoEntity> fileInfoQw = new QueryWrapper<>();
  355. fileInfoQw.eq("id", id);
  356. FileInfoEntity fileInfoEntity = fileInfoMapper.selectOne(fileInfoQw);
  357. String filePath = fileInfoEntity.getPath();
  358. File file = null;
  359. try {
  360. file=FileUploadUtil.getFileWithPath(filePath);
  361. } catch (Exception e) {
  362. throw new RuntimeException(e);
  363. }finally {
  364. base.delete();
  365. if (file!=null){
  366. file.delete();
  367. }
  368. }
  369. //人脸检测2
  370. ImageInfo imageInfo2 = getRGBData(file);
  371. List<FaceInfo> faceInfoList2 = new ArrayList<FaceInfo>();
  372. errorCode = faceEngine.detectFaces(imageInfo2.getImageData(), imageInfo2.getWidth(), imageInfo2.getHeight(), imageInfo2.getImageFormat(), faceInfoList2);
  373. //特征提取2
  374. FaceFeature faceFeature2 = new FaceFeature();
  375. errorCode = faceEngine.extractFaceFeature(imageInfo2.getImageData(), imageInfo2.getWidth(), imageInfo2.getHeight(), imageInfo2.getImageFormat(), faceInfoList2.get(0), faceFeature2);
  376. //特征比对
  377. FaceFeature targetFaceFeature = new FaceFeature();
  378. targetFaceFeature.setFeatureData(faceFeature.getFeatureData());
  379. FaceFeature sourceFaceFeature = new FaceFeature();
  380. sourceFaceFeature.setFeatureData(faceFeature2.getFeatureData());
  381. FaceSimilar faceSimilar = new FaceSimilar();
  382. errorCode = faceEngine.compareFaceFeature(targetFaceFeature, sourceFaceFeature, faceSimilar);
  383. //如果通过 上传文件 修改用户绑定id 如果没通过 返回null证明失败了
  384. if (faceSimilar.getScore() > 0.8) {
  385. log.info("人脸照片比对通过");
  386. base.delete();
  387. file.delete();
  388. return true;
  389. }
  390. if (count == null) {
  391. redis.set(redisCount, 1, 30, TimeUnit.MINUTES);
  392. } else {
  393. redis.set(redisCount, count + 1, 30, TimeUnit.MINUTES);
  394. }
  395. log.info("人脸照片比对未通过");
  396. base.delete();
  397. file.delete();
  398. return false;
  399. }
  400. /**
  401. * 上传对比照片到minio
  402. * @param base64
  403. */
  404. public void uploadFile(String base64){
  405. JSONObject params = new JSONObject();
  406. params.put("base64", base64);
  407. HttpClientUtil httpClientUtil = new HttpClientUtil();
  408. String xmlData = httpClientUtil.doPost("https://fqgz.flowbb.cn:6001/sp/upload/uploadFaceImage",params.toString(),"utf-8");
  409. JSONObject result = JSONObject.parseObject(xmlData);
  410. String code = result.getString("code");
  411. if ("100".equals(code)){
  412. log.info("上传成功");
  413. }
  414. }
  415. public FileInfoEntity uploadFaceFile(String base64){
  416. String s = FileUploadUtil.saveFaceImage(base64);
  417. String[] split = s.split("/");
  418. FileInfoEntity fileInfo = new FileInfoEntity();
  419. fileInfo.setLoanNo(split[0]);
  420. fileInfo.setFileName(split[split.length-1]);
  421. fileInfo.setPath(s);
  422. fileInfoMapper.insert(fileInfo);
  423. return fileInfo;
  424. }
  425. }