添加ocr识别功能

This commit is contained in:
2024-08-20 20:32:23 +08:00
parent e20eb78d0d
commit 2018071720
13 changed files with 630 additions and 0 deletions

View File

@@ -57,6 +57,13 @@
<version>5.8.31</version>
</dependency>
<dependency>
<groupId>com.aliyun</groupId>
<artifactId>ocr_api20210707</artifactId>
<version>3.1.1</version>
</dependency>
<dependency>
<groupId>me.lemire.integercompression</groupId>
<artifactId>JavaFastPFOR</artifactId>

View File

@@ -0,0 +1,58 @@
package cn.lihongjie.coal.ocr.controller;
import cn.lihongjie.coal.annotation.SysLog;
import cn.lihongjie.coal.base.dto.CommonQuery;
import cn.lihongjie.coal.base.dto.IdRequest;
import cn.lihongjie.coal.ocr.dto.CreateOcrDto;
import cn.lihongjie.coal.ocr.dto.OCRRequest;
import cn.lihongjie.coal.ocr.dto.OcrDto;
import cn.lihongjie.coal.ocr.dto.UpdateOcrDto;
import cn.lihongjie.coal.ocr.service.OcrService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping("/ocr")
@SysLog(module = "")
@Slf4j
public class OcrController {
@Autowired private OcrService service;
@PostMapping("/ocr")
public Object ocr(@RequestBody OCRRequest request) {
return this.service.ocr(request);
}
@PostMapping("/create")
public OcrDto create(@RequestBody CreateOcrDto request) {
return this.service.create(request);
}
@PostMapping("/update")
public OcrDto update(@RequestBody UpdateOcrDto request) {
return this.service.update(request);
}
@PostMapping("/delete")
public Object delete(@RequestBody IdRequest request) {
this.service.delete(request);
return true;
}
@PostMapping("/getById")
public OcrDto getById(@RequestBody IdRequest request) {
return this.service.getById(request.getId());
}
@PostMapping("/list")
public Page<OcrDto> list(@RequestBody CommonQuery request) {
return this.service.list(request);
}
}

View File

@@ -0,0 +1,8 @@
package cn.lihongjie.coal.ocr.dto;
import cn.lihongjie.coal.base.dto.CommonDto;
import lombok.Data;
@Data
public class CreateOcrDto extends CommonDto {}

View File

@@ -0,0 +1,19 @@
package cn.lihongjie.coal.ocr.dto;
import lombok.Data;
import java.util.*;
@Data
public class OCRRequest {
private String file;
private String ocrType;
private Map<String, Object> options;
}

View File

@@ -0,0 +1,8 @@
package cn.lihongjie.coal.ocr.dto;
import cn.lihongjie.coal.base.dto.CommonDto;
import lombok.Data;
@Data
public class OcrDto extends CommonDto {}

View File

@@ -0,0 +1,8 @@
package cn.lihongjie.coal.ocr.dto;
import cn.lihongjie.coal.base.dto.CommonDto;
import lombok.Data;
@Data
public class UpdateOcrDto extends CommonDto {}

View File

@@ -0,0 +1,37 @@
package cn.lihongjie.coal.ocr.entity;
import cn.lihongjie.coal.base.entity.CommonEntity;
import cn.lihongjie.coal.file.entity.FileEntity;
import jakarta.persistence.Entity;
import jakarta.persistence.ManyToOne;
import lombok.Data;
import org.hibernate.annotations.Comment;
@Data
@Entity
public class OcrEntity extends CommonEntity {
@ManyToOne
private FileEntity file;
@Comment("ocr类型")
private String ocrType;
@Comment("ocr结果")
private String ocrResult;
@Comment("耗时")
private Long costTime;
@Comment("ak")
private String ak;
}

View File

@@ -0,0 +1,18 @@
package cn.lihongjie.coal.ocr.mapper;
import cn.lihongjie.coal.base.mapper.BaseMapper;
import cn.lihongjie.coal.base.mapper.CommonEntityMapper;
import cn.lihongjie.coal.base.mapper.CommonMapper;
import cn.lihongjie.coal.ocr.dto.CreateOcrDto;
import cn.lihongjie.coal.ocr.dto.OcrDto;
import cn.lihongjie.coal.ocr.dto.UpdateOcrDto;
import cn.lihongjie.coal.ocr.entity.OcrEntity;
import org.mapstruct.Mapper;
import org.mapstruct.control.DeepClone;
@Mapper(
componentModel = org.mapstruct.MappingConstants.ComponentModel.SPRING,
uses = {CommonMapper.class, CommonEntityMapper.class},
mappingControl = DeepClone.class)
public interface OcrMapper extends BaseMapper<OcrEntity, OcrDto, CreateOcrDto, UpdateOcrDto> {}

View File

@@ -0,0 +1,15 @@
package cn.lihongjie.coal.ocr.repository;
import cn.lihongjie.coal.base.dao.BaseRepository;
import cn.lihongjie.coal.ocr.entity.OcrEntity;
import org.springframework.data.jpa.repository.Query;
import org.springframework.stereotype.Repository;
import java.util.List;
@Repository
public interface OcrRepository extends BaseRepository<OcrEntity> {
@Query("select false")
boolean isLinked(List<String> ids);
}

View File

@@ -0,0 +1,395 @@
package cn.lihongjie.coal.ocr.service;
import cn.lihongjie.coal.base.dto.CommonQuery;
import cn.lihongjie.coal.base.dto.IdRequest;
import cn.lihongjie.coal.base.service.BaseService;
import cn.lihongjie.coal.common.ReflectUtils;
import cn.lihongjie.coal.dbFunctions.DbFunctionService;
import cn.lihongjie.coal.exception.BizException;
import cn.lihongjie.coal.file.entity.FileEntity;
import cn.lihongjie.coal.file.service.FileService;
import cn.lihongjie.coal.ocr.dto.CreateOcrDto;
import cn.lihongjie.coal.ocr.dto.OCRRequest;
import cn.lihongjie.coal.ocr.dto.OcrDto;
import cn.lihongjie.coal.ocr.dto.UpdateOcrDto;
import cn.lihongjie.coal.ocr.entity.OcrEntity;
import cn.lihongjie.coal.ocr.mapper.OcrMapper;
import cn.lihongjie.coal.ocr.repository.OcrRepository;
import com.fasterxml.jackson.databind.ObjectMapper;
import jakarta.persistence.EntityManager;
import jakarta.persistence.PersistenceContext;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.convert.ConversionService;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Sort;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.Map;
import java.util.Optional;
@Service
@Slf4j
@Transactional
public class OcrService extends BaseService<OcrEntity, OcrRepository> {
@Autowired FileService fileService;
@PersistenceContext EntityManager em;
@Autowired ObjectMapper objectMapper;
@Autowired private OcrRepository repository;
@Autowired private OcrMapper mapper;
@Autowired private ConversionService conversionService;
@Autowired private DbFunctionService dbFunctionService;
@Autowired private com.aliyun.ocr_api20210707.Client ocrClient;
@SneakyThrows
public Object ocr(OCRRequest request) {
/**
* <br>
* RecognizeIdcard - 身份证识别 <br>
* RecognizePassport - 护照识别 <br>
* RecognizeHousehold - 户口本识别 <br>
* RecognizeEstateCertification - 不动产权证识别 <br>
* RecognizeBankCard - 银行卡识别 <br>
* RecognizeBirthCertification - 出生证明识别 <br>
* RecognizeChinesePassport - 中国护照识别 <br>
* RecognizeExitEntryPermitToMainland - 来往大陆(内地)通行证识别 <br>
* RecognizeExitEntryPermitToHK - 往来港澳台通行证识别 <br>
* RecognizeHKIdcard - 中国香港身份证识别 <br>
* RecognizeSocialSecurityCardVersionII - 社保卡识别 <br>
* RecognizeInternationalIdcard - 国际身份证识别
*/
FileEntity file = fileService.get(request.getFile());
// 使用缓存信息
Optional<OcrEntity> cached =
em
.createQuery(
"select e from OcrEntity e where e.file.id = :fileId and e.ocrType = :ocrType and e.status = 0",
OcrEntity.class)
.setParameter("fileId", file.getId())
.setParameter("ocrType", request.getOcrType())
.getResultList()
.stream()
.findFirst();
if (cached.isPresent()) {
log.info("ocr cache hit: file {}, ocrType {} ", file.getId(), request.getOcrType());
return objectMapper.readTree(cached.get().getOcrResult());
}
String publicUrl = file.getPublicUrl();
Object req = null;
Object resp = null;
long start = System.currentTimeMillis();
Exception exception = null;
try {
switch (request.getOcrType()) {
case "RecognizeIdcard":
{
com.aliyun.ocr_api20210707.models.RecognizeIdcardRequest
recognizeIdcardRequest =
new com.aliyun.ocr_api20210707.models
.RecognizeIdcardRequest();
recognizeIdcardRequest.setUrl(publicUrl);
setOptions((req = recognizeIdcardRequest), request.getOptions());
resp = ocrClient.recognizeIdcard(recognizeIdcardRequest);
break;
}
case "RecognizePassport":
{
com.aliyun.ocr_api20210707.models.RecognizePassportRequest
recognizePassportRequest =
new com.aliyun.ocr_api20210707.models
.RecognizePassportRequest();
recognizePassportRequest.setUrl(publicUrl);
setOptions((req = recognizePassportRequest), request.getOptions());
resp = ocrClient.recognizePassport(recognizePassportRequest);
break;
}
case "RecognizeHousehold":
{
com.aliyun.ocr_api20210707.models.RecognizeHouseholdRequest
recognizeHouseholdRequest =
new com.aliyun.ocr_api20210707.models
.RecognizeHouseholdRequest();
recognizeHouseholdRequest.setUrl(publicUrl);
setOptions((req = recognizeHouseholdRequest), request.getOptions());
resp = ocrClient.recognizeHousehold(recognizeHouseholdRequest);
break;
}
case "RecognizeEstateCertification":
{
com.aliyun.ocr_api20210707.models.RecognizeEstateCertificationRequest
recognizeEstateCertificationRequest =
new com.aliyun.ocr_api20210707.models
.RecognizeEstateCertificationRequest();
recognizeEstateCertificationRequest.setUrl(publicUrl);
setOptions(
(req = recognizeEstateCertificationRequest), request.getOptions());
resp =
ocrClient.recognizeEstateCertification(
recognizeEstateCertificationRequest);
break;
}
case "RecognizeBankCard":
{
com.aliyun.ocr_api20210707.models.RecognizeBankCardRequest
recognizeBankCardRequest =
new com.aliyun.ocr_api20210707.models
.RecognizeBankCardRequest();
recognizeBankCardRequest.setUrl(publicUrl);
setOptions((req = recognizeBankCardRequest), request.getOptions());
resp = ocrClient.recognizeBankCard(recognizeBankCardRequest);
break;
}
case "RecognizeBirthCertification":
{
com.aliyun.ocr_api20210707.models.RecognizeBirthCertificationRequest
recognizeBirthCertificationRequest =
new com.aliyun.ocr_api20210707.models
.RecognizeBirthCertificationRequest();
recognizeBirthCertificationRequest.setUrl(publicUrl);
setOptions(
(req = recognizeBirthCertificationRequest), request.getOptions());
resp =
ocrClient.recognizeBirthCertification(
recognizeBirthCertificationRequest);
break;
}
case "RecognizeChinesePassport":
{
com.aliyun.ocr_api20210707.models.RecognizeChinesePassportRequest
recognizeChinesePassportRequest =
new com.aliyun.ocr_api20210707.models
.RecognizeChinesePassportRequest();
recognizeChinesePassportRequest.setUrl(publicUrl);
setOptions((req = recognizeChinesePassportRequest), request.getOptions());
resp = ocrClient.recognizeChinesePassport(recognizeChinesePassportRequest);
break;
}
case "RecognizeExitEntryPermitToMainland":
{
com.aliyun.ocr_api20210707.models.RecognizeExitEntryPermitToMainlandRequest
recognizeExitEntryPermitToMainlandRequest =
new com.aliyun.ocr_api20210707.models
.RecognizeExitEntryPermitToMainlandRequest();
recognizeExitEntryPermitToMainlandRequest.setUrl(publicUrl);
setOptions(
(req = recognizeExitEntryPermitToMainlandRequest),
request.getOptions());
resp =
ocrClient.recognizeExitEntryPermitToMainland(
recognizeExitEntryPermitToMainlandRequest);
break;
}
case "RecognizeExitEntryPermitToHK":
{
com.aliyun.ocr_api20210707.models.RecognizeExitEntryPermitToHKRequest
recognizeExitEntryPermitToHKRequest =
new com.aliyun.ocr_api20210707.models
.RecognizeExitEntryPermitToHKRequest();
recognizeExitEntryPermitToHKRequest.setUrl(publicUrl);
setOptions(
(req = recognizeExitEntryPermitToHKRequest), request.getOptions());
resp =
ocrClient.recognizeExitEntryPermitToHK(
recognizeExitEntryPermitToHKRequest);
break;
}
case "RecognizeHKIdcard":
{
com.aliyun.ocr_api20210707.models.RecognizeHKIdcardRequest
recognizeHKIdcardRequest =
new com.aliyun.ocr_api20210707.models
.RecognizeHKIdcardRequest();
recognizeHKIdcardRequest.setUrl(publicUrl);
setOptions((req = recognizeHKIdcardRequest), request.getOptions());
resp = ocrClient.recognizeHKIdcard(recognizeHKIdcardRequest);
break;
}
case "RecognizeSocialSecurityCardVersionII":
{
com.aliyun.ocr_api20210707.models
.RecognizeSocialSecurityCardVersionIIRequest
recognizeSocialSecurityCardVersionIIRequest =
new com.aliyun.ocr_api20210707.models
.RecognizeSocialSecurityCardVersionIIRequest();
recognizeSocialSecurityCardVersionIIRequest.setUrl(publicUrl);
setOptions(
(req = recognizeSocialSecurityCardVersionIIRequest),
request.getOptions());
resp =
ocrClient.recognizeSocialSecurityCardVersionII(
recognizeSocialSecurityCardVersionIIRequest);
break;
}
case "RecognizeInternationalIdcard":
{
com.aliyun.ocr_api20210707.models.RecognizeInternationalIdcardRequest
recognizeInternationalIdcardRequest =
new com.aliyun.ocr_api20210707.models
.RecognizeInternationalIdcardRequest();
recognizeInternationalIdcardRequest.setUrl(publicUrl);
setOptions(
(req = recognizeInternationalIdcardRequest), request.getOptions());
resp =
ocrClient.recognizeInternationalIdcard(
recognizeInternationalIdcardRequest);
break;
}
default:
{
throw new BizException("不支持的识别类型 " + request.getOcrType());
}
}
} catch (Exception e) {
exception = e;
} finally {
OcrEntity ocr = new OcrEntity();
ocr.setFile(file);
ocr.setOcrType(request.getOcrType());
ocr.setCostTime(System.currentTimeMillis() - start);
ocr.setAk(ocrClient.getAccessKeyId());
if (exception != null) {
ocr.setStatus(1);
ocr.setRemarks(exception.getMessage());
} else {
ocr.setStatus(0);
ocr.setOcrResult(objectMapper.writeValueAsString(resp));
}
repository.save(ocr);
}
if (exception != null) {
log.info(
"ocr failed: \nclientInfo:{}\nreq:{}\nresp:{}",
ocrClient.getAccessKeyId(),
objectMapper.writeValueAsString(req),
objectMapper.writeValueAsString(resp),
exception);
throw new BizException("ocr服务调用失败: " + exception.getMessage());
}
return resp;
}
private void setOptions(Object obj, Map<String, Object> options) {
ReflectUtils.getAllFieldsList(obj.getClass())
.forEach(
f -> {
if (options.containsKey(f.getName())) {
ReflectUtils.writeField(
obj,
f.getName(),
conversionService.convert(
options.get(f.getName()), f.getType()));
}
});
}
public OcrDto create(CreateOcrDto request) {
OcrEntity entity = mapper.toEntity(request);
this.repository.save(entity);
return getById(entity.getId());
}
public OcrDto update(UpdateOcrDto request) {
OcrEntity entity = this.repository.get(request.getId());
this.mapper.updateEntity(entity, request);
this.repository.save(entity);
return getById(entity.getId());
}
public void delete(IdRequest request) {
boolean linked = this.repository.isLinked(request.getIds());
if (linked) {
throw new BizException("数据已被关联,无法删除");
}
this.repository.deleteAllById(request.getIds());
}
public OcrDto getById(String id) {
OcrEntity entity = repository.get(id);
return mapper.toDto(entity);
}
public Page<OcrDto> list(CommonQuery query) {
Page<OcrEntity> page =
repository.findAll(
query.specification(conversionService),
PageRequest.of(
query.getPageNo(),
query.getPageSize(),
Sort.by(query.getOrders())));
return page.map(this.mapper::toDto);
}
}

View File

@@ -2,6 +2,7 @@ package cn.lihongjie.coal.spring.config;
import com.aliyun.oss.OSSClient;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
@@ -24,4 +25,26 @@ public class AliyunConfig {
return ossClient;
}
@SneakyThrows
@Bean()
@Lazy
public com.aliyun.ocr_api20210707.Client ocrClient() {
// 工程代码泄露可能会导致 AccessKey 泄露,并威胁账号下所有资源的安全性。以下代码示例仅供参考。
// 建议使用更安全的 STS 方式更多鉴权访问方式请参见https://help.aliyun.com/document_detail/378657.html。
com.aliyun.teaopenapi.models.Config config =
new com.aliyun.teaopenapi.models.Config()
// 必填,请确保代码运行环境设置了环境变量 ALIBABA_CLOUD_ACCESS_KEY_ID。
.setAccessKeyId(aliyunProperty.getOCR().getAk())
// 必填,请确保代码运行环境设置了环境变量 ALIBABA_CLOUD_ACCESS_KEY_SECRET。
.setAccessKeySecret(aliyunProperty.getOCR().getSk());
config.endpoint = aliyunProperty.getOCR().getEndpoint();
return new com.aliyun.ocr_api20210707.Client(config);
}
}

View File

@@ -14,6 +14,23 @@ public class AliyunProperty {
private OSS OSS;
private OCR OCR;
@Data
public static class OCR {
private String endpoint;
private String ak;
private String sk;
}
@Data
public static class OSS {

View File

@@ -0,0 +1,17 @@
package scripts.dict
import cn.lihongjie.coal.base.dto.CommonQuery
import cn.lihongjie.coal.ocr.controller.OcrController
import org.springframework.context.ApplicationContext
ApplicationContext ioc = ioc
def controller = ioc.getBean(OcrController.class)
return controller.list(new CommonQuery())