集成 OCR

This commit is contained in:
cwchen 2025-10-16 13:39:43 +08:00
parent 75c8ab3350
commit e9925a6419
14 changed files with 363 additions and 44 deletions

View File

@ -1,35 +0,0 @@
package com.bonus.web.controller.common;
import com.bonus.common.core.domain.AjaxResult;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.servlet.http.HttpServletRequest;
/**
* 会话检查控制器
*/
@RestController
@RequestMapping("/api/session")
public class SessionCheckController {
// @Autowired
// private SessionManagementService sessionManagementService;
/**
* 检查会话状态
*/
/*@GetMapping("/check")
public AjaxResult checkSession(HttpServletRequest request) {
String sessionId = request.getSession().getId();
if (sessionManagementService.isSessionForcedLogout(sessionId)) {
String message = sessionManagementService.getForceLogoutMessage(sessionId);
return AjaxResult.error(403, message).put("forceLogout", true);
}
return AjaxResult.success().put("forceLogout", false);
}*/
}

View File

@ -0,0 +1,7 @@
# MinIO 配置
minio:
url: http://192.168.0.14:9090
endpoint: http://192.168.0.14:9090
access-key: minio
secret-key: bonus@admin123
bucket-name: smart-bid

View File

@ -0,0 +1,6 @@
ocr:
service:
url: http://192.168.0.37:8000/ocr/ # ocr 请求地址
healthUrl: http://192.168.0.37:8000/ # ocr 服务健康检查
timeout: 30000 # ocr 请求超时时间
max-connections: 100

View File

@ -0,0 +1,63 @@
spring:
rabbitmq:
# 连接配置
host: localhost
port: 15672
username: guest
password: guest
virtual-host: /
# 连接超时和心跳
connection-timeout: 30000
requested-heartbeat: 60
# 通道缓存配置
cache:
channel:
size: 25
checkout-timeout: 2000
# 发布确认
publisher-confirm-type: correlated
publisher-returns: true
# 模板配置
template:
mandatory: true
receive-timeout: 30000
reply-timeout: 30000
retry:
enabled: true
initial-interval: 2000
max-attempts: 3
multiplier: 1.5
max-interval: 10000
# 监听器配置
listener:
type: simple
simple:
acknowledge-mode: auto
concurrency: 2
max-concurrency: 10
prefetch: 1
# 重要:解决队列检查超时问题
missing-queues-fatal: false
auto-startup: true
default-requeue-rejected: false
retry:
enabled: true
initial-interval: 3000
max-attempts: 3
max-interval: 10000
multiplier: 2.0
# Actuator 配置
management:
endpoints:
web:
exposure:
include: health,info,metrics
endpoint:
health:
show-details: always

View File

@ -1,6 +1,6 @@
spring:
profiles:
active: @profiles.active@,druid
active: @profiles.active@,druid,ocr
# 解决SpringBoot 2.6+与SpringFox兼容性问题
mvc:
pathmatch:

View File

@ -169,6 +169,18 @@
<artifactId>jackson-databind</artifactId>
</dependency>
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpclient</artifactId>
<version>4.5.13</version>
</dependency>
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpmime</artifactId>
<version>4.5.14</version>
</dependency>
</dependencies>
</project>

View File

@ -0,0 +1,35 @@
package com.bonus.common.domain.ocr.dto;
import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.Data;
import java.io.File;
/**
* @className:OcrRequest
* @author:cwchen
* @date:2025-10-16-10:45
* @version:1.0
* @description:OCR请求DTO
*/
@Data
public class OcrRequest {
@JsonProperty("file")
private File file; // 文件对象
@JsonProperty("type")
private String type; // 文件类型
@JsonProperty("analysisContent")
private String analysisContent;
public OcrRequest(File file, String type, String analysisContent) {
this.file = file;
this.type = type;
this.analysisContent = analysisContent;
}
public OcrRequest() {
}
}

View File

@ -0,0 +1,29 @@
package com.bonus.common.domain.ocr.vo;
import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.Data;
import java.util.List;
/**
* @className:OcrData
* @author:cwchen
* @date:2025-10-16-10:43
* @version:1.0
* @description: OCR数据DTO
*/
@Data
public class OcrData {
@JsonProperty("text")
private String text; // 识别出的文本
@JsonProperty("confidence")
private Double confidence; // 置信度
@JsonProperty("words")
private List<WordInfo> words; // 单词列表
@JsonProperty("processing_time")
private Double processingTime; // 处理时间
}

View File

@ -0,0 +1,27 @@
package com.bonus.common.domain.ocr.vo;
import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.Data;
/**
* @className:OcrResponse
* @author:cwchen
* @date:2025-10-16-9:53
* @version:1.0
* @description:Ocr响应结果
*/
@Data
public class OcrResponse {
@JsonProperty("code")
private Integer code; // 状态码
@JsonProperty("message")
private String message; // 消息
@JsonProperty("data")
private OcrData data; // 识别数据
@JsonProperty("request_id")
private String requestId; // 请求ID
}

View File

@ -0,0 +1,24 @@
package com.bonus.common.domain.ocr.vo;
import com.fasterxml.jackson.annotation.JsonProperty;
import java.util.List;
/**
* @className:WordInfo
* @author:cwchen
* @date:2025-10-16-10:43
* @version:1.0
* @description: 单词信息DTO
*/
public class WordInfo {
@JsonProperty("text")
private String text; // 单词文本
@JsonProperty("confidence")
private Double confidence; // 单词置信度
@JsonProperty("bbox")
private List<Integer> bbox; // 边界框 [x1, y1, x2, y2, x3, y3, x4, y4]
}

View File

@ -1,7 +0,0 @@
package com.bonus;
public class Main {
public static void main(String[] args) {
System.out.println("Hello, World!");
}
}

View File

@ -0,0 +1,129 @@
package com.bonus.ocr.service;
import com.bonus.common.domain.ocr.dto.OcrRequest;
import com.bonus.common.domain.ocr.vo.OcrResponse;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.extern.slf4j.Slf4j;
import org.apache.http.HttpEntity;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.util.EntityUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.apache.http.entity.mime.MultipartEntityBuilder;
import org.apache.http.entity.mime.HttpMultipartMode;
import org.apache.http.entity.mime.content.FileBody;
import org.apache.http.entity.ContentType;
import java.nio.charset.StandardCharsets;
import java.io.IOException;
/**
* @className:OcrService
* @author:cwchen
* @date:2025-10-16-10:35
* @version:1.0
* @description: OCR服务实现类
*/
@Service(value = "OcrService")
@Slf4j
public class OcrService {
@Value("${ocr.service.url}")
private String ocrServiceUrl;
@Value("${ocr.service.healthUrl}")
private String healthUrl;
@Value("${ocr.service.timeout}")
private int timeout;
private final CloseableHttpClient httpClient;
private final ObjectMapper objectMapper;
public OcrService() {
// 配置HTTP客户端
RequestConfig requestConfig = RequestConfig.custom()
.setConnectTimeout(timeout)
.setSocketTimeout(timeout)
.setConnectionRequestTimeout(timeout)
.build();
this.httpClient = HttpClients.custom()
.setDefaultRequestConfig(requestConfig)
.build();
this.objectMapper = new ObjectMapper();
}
/**
* 调用OCR服务
*/
private OcrResponse callOcrService(OcrRequest ocrRequest) throws IOException {
HttpPost httpPost = new HttpPost(ocrServiceUrl);
try {
// 创建 multipart entity builder
MultipartEntityBuilder builder = MultipartEntityBuilder.create();
builder.setCharset(StandardCharsets.UTF_8);
builder.setMode(HttpMultipartMode.BROWSER_COMPATIBLE);
// 添加文件字段
builder.addPart("file",
new FileBody(ocrRequest.getFile(), ContentType.MULTIPART_FORM_DATA, ocrRequest.getFile().getName()));
// 添加类型字段
builder.addTextBody("type", ocrRequest.getType(),
ContentType.TEXT_PLAIN.withCharset("UTF-8"));
// 添加解析内容字段
builder.addTextBody("analysisContent", ocrRequest.getAnalysisContent(),
ContentType.TEXT_PLAIN.withCharset("UTF-8"));
// 设置请求实体 - 注意这里会自动设置正确的 Content-Type boundary
httpPost.setEntity(builder.build());
// 设置 Accept
httpPost.setHeader("Accept", "application/json");
log.debug("发送OCR请求到: {}", ocrServiceUrl);
// 执行请求
try (CloseableHttpResponse response = httpClient.execute(httpPost)) {
HttpEntity entity = response.getEntity();
String responseBody = EntityUtils.toString(entity, "UTF-8");
log.debug("OCR服务响应状态: {}", response.getStatusLine().getStatusCode());
log.debug("OCR服务响应内容: {}", responseBody);
// 解析响应
OcrResponse ocrResponse = objectMapper.readValue(responseBody, OcrResponse.class);
if (ocrResponse.getCode() != null && ocrResponse.getCode() == 200) {
/*log.info("OCR识别成功识别字符数: {}",
ocrResponse.getData() != null && ocrResponse.getData().getText() != null ?
ocrResponse.getData().getText().length() : 0);*/
} else {
log.warn("OCR识别失败: {}", ocrResponse.getMessage());
}
return ocrResponse;
}
} catch (Exception e) {
log.error("调用OCR服务失败", e);
throw new IOException("OCR服务调用失败: " + e.getMessage(), e);
} finally {
httpPost.releaseConnection();
}
}
/**
* 关闭HTTP客户端
*/
public void close() {
try {
httpClient.close();
} catch (IOException e) {
log.error("关闭HTTP客户端失败", e);
}
}
}

View File

@ -0,0 +1,29 @@
package com.bonus.ocr;
import com.alibaba.fastjson2.JSONObject;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.ResponseEntity;
import org.springframework.util.MultiValueMap;
import org.springframework.web.client.RestTemplate;
/**
* @className:test
* @author:cwchen
* @date:2025-10-16-9:42
* @version:1.0
* @description:测试
*/
public class test {
public void testOcr() {
}
public static void main(String[] args) {
}
}

View File

@ -19,7 +19,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
<sql id="selectNoticeVo">
select notice_id, notice_title, notice_type, cast(notice_content as char) as notice_content, status, create_by, create_time, update_by, update_time, remark
from da_ky_sys_menu
from sys_notice
</sql>
<select id="selectNoticeById" parameterType="Long" resultMap="SysNoticeResult">