refactor(doc): 重构文档分类响应数据结构

- 将 DocClassifyResp 中的 items 字段重命名为 wordsResult
- 将 DocClassifyResp 中的 count 字段移除,新增 logId 字段
- 将 DocClassifyItemResp 中的位置相关字段提取为独立的 DocClassifyLocationResp 对象
- 更新所有相关的 getter/setter 调用以匹配新的数据结构
- 修复注释中关于百度 API 字段名拼写错误的说明
- 添加详细的字段说明注释以提高代码可读性
This commit is contained in:
zhengli 2026-06-09 16:02:52 +08:00
parent d5ebd16c21
commit 39738eead9
4 changed files with 92 additions and 57 deletions

View File

@ -13,6 +13,7 @@ import com.heyu.api.data.utils.R;
import com.heyu.api.data.utils.StringUtils;
import com.heyu.api.request.doc.DocClassifyRequest;
import com.heyu.api.resp.doc.DocClassifyItemResp;
import com.heyu.api.resp.doc.DocClassifyLocationResp;
import com.heyu.api.resp.doc.DocClassifyResp;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.PostMapping;
@ -195,7 +196,7 @@ public class DocClassifyController extends AbstractRecognizeController {
"解析状态=结果集为空"
);
}
if (data == null || data.getItems() == null || data.getItems().isEmpty()) {
if (data == null || data.getWordsResult() == null || data.getWordsResult().isEmpty()) {
log.info("文档分类:平台有返回,但分类结果列表为空(可能图片不包含可识别主体)。{}。客户传的:{}。平台回执:{}",
ctx.imageInput.getType().getDesc(),
ctx.inputLog, buildPlatformReceiptSummary(platformResult));
@ -215,7 +216,7 @@ public class DocClassifyController extends AbstractRecognizeController {
"字段映射为空",
"平台回执包含分类条目,但类别、置信度、位置等结构化字段均未命中",
"请确认上传图片清晰且包含可识别的文档、卡证或票据主体",
"分类条目数=" + data.getItems().size() + ",有效字段数=0"
"分类条目数=" + data.getWordsResult().size() + ",有效字段数=0"
);
}
return null;
@ -224,7 +225,7 @@ public class DocClassifyController extends AbstractRecognizeController {
private void logRecognizeResult(RecognizeContext ctx, Map<String, Object> platformResult,
DocClassifyResp data, String hint, long start) {
long cost = System.currentTimeMillis() - start;
int itemCount = data != null && data.getItems() != null ? data.getItems().size() : 0;
int itemCount = data != null && data.getWordsResult() != null ? data.getWordsResult().size() : 0;
int totalFields = countTotalItemFields(data);
if (StringUtils.isNotBlank(hint)) {
log.info("文档分类:处理结束(接口仍返回成功,但带了提示信息)。耗时 {} ms分类出 {} 个主体、共 {} 个字段。"
@ -290,12 +291,15 @@ public class DocClassifyController extends AbstractRecognizeController {
@SuppressWarnings("unchecked")
private DocClassifyResp buildResp(Map<String, Object> platformResult) {
DocClassifyResp resp = new DocClassifyResp();
Object wordsResult = platformResult.get("words_result");
Object wordsResultNum = platformResult.get("words_result_num");
resp.setCount(wordsResultNum != null ? String.valueOf(wordsResultNum) : "0");
// 一级字段log_id
Object logId = platformResult.get("log_id");
resp.setLogId(logId != null ? String.valueOf(logId) : null);
// 一级字段words_result
Object wordsResult = platformResult.get("words_result");
if (!(wordsResult instanceof List)) {
resp.setItems(Collections.emptyList());
resp.setWordsResult(Collections.emptyList());
return resp;
}
List<Map<String, Object>> resultList = (List<Map<String, Object>>) wordsResult;
@ -303,26 +307,32 @@ public class DocClassifyController extends AbstractRecognizeController {
for (Map<String, Object> item : resultList) {
items.add(buildItemResp(item));
}
resp.setItems(items);
resp.setWordsResult(items);
return resp;
}
@SuppressWarnings("unchecked")
private DocClassifyItemResp buildItemResp(Map<String, Object> item) {
DocClassifyItemResp resp = new DocClassifyItemResp();
// 二级字段type
Object type = item.get("type");
resp.setType(type != null ? String.valueOf(type) : null);
// 注意百度 API 原始字段名为 "probablity"拼写错误此处与之保持一致
// 二级字段probability注意百度 API 原始字段名为 "probablity"拼写错误此处与之保持一致
Object prob = item.get("probablity");
resp.setProbability(prob != null ? String.valueOf(prob) : null);
// 二级字段location嵌套对象
Object location = item.get("location");
if (location instanceof Map) {
Map<String, Object> loc = (Map<String, Object>) location;
resp.setTop(stringOrNull(loc.get("top")));
resp.setLeft(stringOrNull(loc.get("left")));
resp.setWidth(stringOrNull(loc.get("width")));
resp.setHeight(stringOrNull(loc.get("height")));
DocClassifyLocationResp locationResp = new DocClassifyLocationResp();
locationResp.setLeft(stringOrNull(loc.get("left")));
locationResp.setTop(stringOrNull(loc.get("top")));
locationResp.setWidth(stringOrNull(loc.get("width")));
locationResp.setHeight(stringOrNull(loc.get("height")));
resp.setLocation(locationResp);
}
return resp;
}
@ -353,11 +363,11 @@ public class DocClassifyController extends AbstractRecognizeController {
* 统计所有分类条目的非空字段总数使用父类反射工具
*/
private int countTotalItemFields(DocClassifyResp data) {
if (data == null || data.getItems() == null) {
if (data == null || data.getWordsResult() == null) {
return 0;
}
int total = 0;
for (DocClassifyItemResp item : data.getItems()) {
for (DocClassifyItemResp item : data.getWordsResult()) {
total += countNonBlankStringFields(item);
}
return total;
@ -370,10 +380,10 @@ public class DocClassifyController extends AbstractRecognizeController {
* 改为遍历每条 ItemResp 做反射判断</p>
*/
private boolean isAllItemsFieldsBlank(DocClassifyResp data) {
if (data == null || data.getItems() == null || data.getItems().isEmpty()) {
if (data == null || data.getWordsResult() == null || data.getWordsResult().isEmpty()) {
return true;
}
for (DocClassifyItemResp item : data.getItems()) {
for (DocClassifyItemResp item : data.getWordsResult()) {
if (!isAllStringFieldsBlank(item)) {
return false;
}

View File

@ -3,10 +3,14 @@ package com.heyu.api.resp.doc;
import lombok.Data;
/**
* 文档分类检测 单个分类结果项
* 文档分类检测 单个分类结果项words_result 数组元素
* <p>
* 所有字段均为 String 类型保证反射统计工具覆盖
* 字段与百度 doc_classify 接口返回的 words_result 数组元素一一对应
* 二级字段
* <ul>
* <li>type 类别信息</li>
* <li>probability 分类置信度</li>
* <li>location 位置数组左上角为坐标0点</li>
* </ul>
* </p>
*
* @author zhengli
@ -23,38 +27,16 @@ public class DocClassifyItemResp {
private String type;
/**
* 分类置信度0~1 之间的小数字符串形式
* 对应百度字段probablityfloat
* 分类置信度0~1 之间的小数
* 对应百度字段probabilityfloat
* 示例"0.9999860525"
*/
private String probability;
/**
* 位置信息 表示定位位置的长方形左上顶点的垂直坐标像素
* 对应百度字段location.topuint32
* 示例"1684"
* 位置信息左上角为坐标0点
* 对应百度字段locationobject
*/
private String top;
/**
* 位置信息 表示定位位置的长方形左上顶点的水平坐标像素
* 对应百度字段location.leftuint32
* 示例"300"
*/
private String left;
/**
* 位置信息 表示定位位置的长方形的宽度像素
* 对应百度字段location.widthuint32
* 示例"2710"
*/
private String width;
/**
* 位置信息 表示定位位置的长方形的高度像素
* 对应百度字段location.heightuint32
* 示例"1712"
*/
private String height;
private DocClassifyLocationResp location;
}

View File

@ -0,0 +1,41 @@
package com.heyu.api.resp.doc;
import lombok.Data;
/**
* 文档分类检测 位置信息location 子对象
* <p>
* 表示定位位置的长方形左上角为坐标0点
* </p>
*
* @author zhengli
* @since 20260609_zl
*/
@Data
public class DocClassifyLocationResp {
/**
* 表示定位位置的长方形左上顶点的水平坐标像素
* 对应百度字段location.leftuint32
*/
private String left;
/**
* 表示定位位置的长方形左上顶点的垂直坐标像素
* 对应百度字段location.topuint32
*/
private String top;
/**
* 表示定位位置的长方形的宽度像素
* 对应百度字段location.widthuint32
*/
private String width;
/**
* 表示定位位置的长方形的高度像素
* 对应百度字段location.heightuint32
*/
private String height;
}

View File

@ -5,11 +5,13 @@ import lombok.Data;
import java.util.List;
/**
* 文档分类检测响应
* 文档分类检测响应对齐百度官方接口返回结构
* <p>
* 对图片中的文档卡证票据等含文字的主体进行检测和分类
* 返回每个主体的类别置信度和位置信息
* 如图片中无文字内容items 为空列表
* 一级字段
* <ul>
* <li>logId 唯一的日志id用于问题定位</li>
* <li>wordsResult 检测和分类结果数组如图片中无文字内容则此数组为空</li>
* </ul>
* </p>
*
* @author zhengli
@ -19,15 +21,15 @@ import java.util.List;
public class DocClassifyResp {
/**
* 分类结果列表图片中无文字内容时为空列表
* 示例[{type="卡证_银行卡", probability="0.9999860525", top="1684", left="300", width="2710", height="1712"}]
* 唯一的日志id用于问题定位
* 对应百度字段log_iduint64
*/
private List<DocClassifyItemResp> items;
private String logId;
/**
* 检测到的主体数量
* 示例"3"
* 检测和分类结果数组如图片中无文字内容则此数组为空
* 对应百度字段words_resultarray
*/
private String count;
private List<DocClassifyItemResp> wordsResult;
}