Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit 7123ef0

Browse files
Java: MultiDataSource 导出 CVAuto 测试报告接口新增在 Excel 上粘贴原图和渲染图
1 parent ae2a3fd commit 7123ef0

File tree

6 files changed

+247
-34
lines changed

6 files changed

+247
-34
lines changed

‎APIJSON-Java-Server/APIJSONBoot-MultiDataSource/src/main/java/apijson/ExcelUtil.java

Lines changed: 12 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -240,7 +240,7 @@ public static String generateCVAutoReport(List<DetailItem> list, String dir, Str
240240
// 动态计算详情区域的表头行索引
241241
int detailHeaderRowIndex = 1 + 6 + 10; // 标题区(1) + 统计区(标题+6行数据) + 空白行(1) = 17
242242

243-
List<List<Object>> data = prepareData(detailItems, detailHeaderRowIndex + 2);
243+
List<List<Object>> data = prepareData(detailItems, detailHeaderRowIndex + 2, dir);
244244

245245
try {
246246
EasyExcel.write(fileName)
@@ -286,7 +286,7 @@ public static String generateCVAutoReport(List<DetailItem> list, String dir, Str
286286
* @param detailDataStartRow 详情数据在Excel中的起始行号 (1-based)
287287
* @return 包含所有报告数据的列表
288288
*/
289-
private static List<List<Object>> prepareData(List<DetailItem> detailItems, int detailDataStartRow) {
289+
private static List<List<Object>> prepareData(List<DetailItem> detailItems, int detailDataStartRow, Stringdir) {
290290
List<List<Object>> list = new ArrayList<>();
291291

292292
// --- 第 1 部分:主标题 ---
@@ -322,8 +322,11 @@ private static List<List<Object>> prepareData(List<DetailItem> detailItems, int
322322

323323
List<Object> detailRow = new ArrayList<>();
324324
// A-C: 基础信息
325-
detailRow.add(item.getImageName());
326-
detailRow.add(item.getRenderName());
325+
File imgFile = new File(item.getImageName()); // dir + item.getImageName());
326+
File renderFile = new File(item.getRenderName()); // dir + item.getRenderName());
327+
328+
detailRow.add(imgFile.exists() ? imgFile : imgFile.getAbsolutePath());
329+
detailRow.add(renderFile.exists() ? renderFile : renderFile.getAbsolutePath());
327330
// D-F: 手动输入数据
328331
detailRow.add(item.getTargetCount());
329332
detailRow.add(item.getCorrectCount());
@@ -378,11 +381,11 @@ private static WriteCellData<String> createFormulaCell(String formula) {
378381

379382
private static List<DetailItem> getMockDetailData() {
380383
List<DetailItem> list = new ArrayList<>();
381-
list.add(new DetailItem("image_001.jpg", "image_001_res.jpg", 5, 4, 1, "{ \"predictions\": [...] }", "✅", ""));
382-
list.add(new DetailItem("image_002.jpg", "image_002_res.jpg", 3, 3, 0, "{ \"predictions\": [...] }", "✅", ""));
383-
list.add(new DetailItem("image_003.jpg", "image_003_res.jpg", 8, 6, 3, "{ \"predictions\": [...] }", "❌", ""));
384-
list.add(new DetailItem("image_004.jpg", "image_004_res.jpg", 1, 0, 1, "{ \"predictions\": [...] }" ,"❌", ""));
385-
list.add(new DetailItem("image_005.jpg", "image_005_res.jpg", 10, 10, 0, "{ \"predictions\": [...] }", "✅", ""));
384+
list.add(new DetailItem("image_001.jpg", "image_001_render.jpg", 5, 4, 1, "{ \"predictions\": [...] }", "✅", ""));
385+
list.add(new DetailItem("image_002.jpg", "image_002_render.jpg", 3, 3, 0, "{ \"predictions\": [...] }", "✅", ""));
386+
list.add(new DetailItem("image_003.jpg", "image_003_render.jpg", 8, 6, 3, "{ \"predictions\": [...] }", "❌", ""));
387+
list.add(new DetailItem("image_004.jpg", "image_004_render.jpg", 1, 0, 1, "{ \"predictions\": [...] }" ,"❌", ""));
388+
list.add(new DetailItem("image_005.jpg", "image_005_render.jpg", 10, 10, 0, "{ \"predictions\": [...] }", "✅", ""));
386389
return list;
387390
}
388391

‎APIJSON-Java-Server/APIJSONBoot-MultiDataSource/src/main/java/apijson/boot/DemoController.java

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
import org.springframework.beans.factory.annotation.Autowired;
2828
import org.springframework.http.*;
2929
import org.springframework.http.client.HttpComponentsClientHttpRequestFactory;
30+
import org.springframework.http.converter.StringHttpMessageConverter;
3031
import org.springframework.stereotype.Service;
3132
import org.springframework.web.bind.annotation.*;
3233
import org.springframework.web.client.RestClientResponseException;
@@ -1862,7 +1863,20 @@ else if (recordType > 0) {
18621863
static {
18631864
try { // 支持 PATCH 方法,需要 Maven 依赖 org.apache.httpcomponents.client5:httpclient5
18641865
HttpComponentsClientHttpRequestFactory requestFactory = new HttpComponentsClientHttpRequestFactory();
1866+
requestFactory.setConnectTimeout(30000);
1867+
//requestFactory.setReadTimeout(120000);
1868+
requestFactory.setConnectionRequestTimeout(30000);
1869+
1870+
//requestFactory.setBufferRequestBody(true); // spring-framework 6.1 以下启用请求体缓冲
1871+
18651872
CLIENT.setRequestFactory(requestFactory);
1873+
1874+
// 添加大请求支持的消息转换器
1875+
CLIENT.getMessageConverters().forEach(converter -> {
1876+
if (converter instanceof StringHttpMessageConverter) {
1877+
((StringHttpMessageConverter) converter).setWriteAcceptCharset(false);
1878+
}
1879+
});
18661880
}
18671881
catch (Throwable e) {
18681882
e.printStackTrace();

‎APIJSON-Java-Server/APIJSONBoot-MultiDataSource/src/main/java/apijson/boot/FileController.java

Lines changed: 144 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -10,10 +10,15 @@
1010
import apijson.ExcelUtil;
1111
import apijson.RequestMethod;
1212
import apijson.StringUtil;
13+
import apijson.fastjson2.JSON;
14+
import apijson.JSONResponse;
1315
import com.alibaba.fastjson2.JSONArray;
16+
import jakarta.servlet.http.HttpSession;
1417
import org.apache.commons.io.FileUtils;
18+
import org.springframework.beans.factory.annotation.Autowired;
1519
import org.springframework.core.io.InputStreamResource;
1620
import org.springframework.http.HttpHeaders;
21+
import org.springframework.http.HttpMethod;
1722
import org.springframework.http.MediaType;
1823
import org.springframework.http.ResponseEntity;
1924
import org.springframework.stereotype.Controller;
@@ -28,6 +33,8 @@
2833

2934
import apijson.demo.DemoParser;
3035

36+
import javax.imageio.ImageIO;
37+
3138
import static com.google.common.io.Files.getFileExtension;
3239

3340
/**文件相关的控制器,包括上传、下载、浏览等
@@ -77,6 +84,7 @@ public class FileController {
7784
public static final List<String> VIDEO_SUFFIXES = Arrays.asList("mp4");
7885
public static final List<String> IMG_SUFFIXES = Arrays.asList("jpg", "jpeg", "png");
7986
private static List<String> fileNames = null;
87+
8088
@GetMapping("/files")
8189
@ResponseBody
8290
public JSONObject files() {
@@ -126,8 +134,7 @@ public JSONObject upload(@RequestParam("file") MultipartFile file) {
126134
res.put("path", "/download/" + name);
127135
res.put("size", file.getBytes().length);
128136
return new DemoParser().extendSuccessResult(res);
129-
}
130-
catch (Exception e) {
137+
} catch (Exception e) {
131138
e.printStackTrace();
132139
return new DemoParser().newErrorResult(e);
133140
}
@@ -191,10 +198,15 @@ private MediaType determineContentType(String fileName) {
191198
}
192199
}
193200

194-
@GetMapping("/download/cv/report/{reportId}")
201+
@Autowired
202+
DemoController demoController;
203+
204+
@GetMapping("/download/cv/report/{id}")
195205
@ResponseBody
196-
public ResponseEntity<Object> downloadCVReport(@PathVariable(name = "reportId") String reportId) throws FileNotFoundException, IOException {
197-
String name = "CVAuto_report_" + reportId + ".xlsx";
206+
public ResponseEntity<Object> downloadCVReport(@PathVariable(name = "id") String idStr, HttpSession session) throws FileNotFoundException, IOException {
207+
long reportId = Long.parseLong(idStr);
208+
boolean isLast = reportId <= 0;
209+
String name = "CVAuto_report_" + (isLast ? "last" : reportId) + ".xlsx";
198210
String path = fileUploadRootDir + name;
199211
File file = new File(path);
200212
long size = file.exists() ? file.length() : 0;
@@ -207,15 +219,22 @@ public ResponseEntity<Object> downloadCVReport(@PathVariable(name = "reportId")
207219

208220
{ // TestRecord <<<<<<<<<<<<<<<<<<<<<<<<<<<<<
209221
JSONObject testRecord = new JSONObject();
210-
testRecord.put("reportId", reportId);
211-
testRecord.put("@column", "documentId");
222+
if (isLast) {
223+
testRecord.put("@order", "reportId-");
224+
} else {
225+
testRecord.put("reportId", reportId);
226+
}
227+
testRecord.put("@column", "reportId,documentId,randomId,sameIds");
228+
212229
request.put("TestRecord", testRecord);
213230
} // TestRecord >>>>>>>>>>>>>>>>>>>>>>>>>>>>>
214231

215232
{ // [] <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
216233
JSONObject item = new JSONObject();
217234
item.put("count", 3);
235+
//item.put("count", 0);
218236
item.put("join", "&/TestRecord");
237+
//item.put("join", "@/TestRecord");
219238

220239
{ // Random <<<<<<<<<<<<<<<<<<<<<<<<<<<<<
221240
JSONObject random = new JSONObject();
@@ -225,27 +244,83 @@ public ResponseEntity<Object> downloadCVReport(@PathVariable(name = "reportId")
225244
random.put("@combine", "file[>,img[>");
226245
random.put("file[>", 0);
227246
random.put("img[>", 0);
247+
228248
item.put("Random", random);
229249
} // Random >>>>>>>>>>>>>>>>>>>>>>>>>>>>>
230250

231251
{ // TestRecord <<<<<<<<<<<<<<<<<<<<<<<<<<<<<
232252
JSONObject testRecord = new JSONObject();
233-
testRecord.put("randomId@", "/Random/id");
234253
testRecord.put("@column", "id,total,correct,wrong,compare,response");
235-
testRecord.put("reportId", reportId);
254+
testRecord.put("randomId@", "/Random/id");
255+
testRecord.put("documentId@", "TestRecord/documentId");
256+
257+
//testRecord.put("idx{}@", "TestRecord/sameIds");
258+
if (isLast) {
259+
testRecord.put("reportId@", "TestRecord/reportId");
260+
//testRecord.put("@combine", "idx | reportId@");
261+
} else {
262+
testRecord.put("reportId", reportId);
263+
//testRecord.put("@combine", "idx | reportId");
264+
}
265+
//testRecord.put("@key", "idx:(id)");
266+
//testRecord.put("@combine", "idx | reportId");
267+
236268
testRecord.put("total>=", 0);
237269
testRecord.put("correct>=", 0);
238270
testRecord.put("wrong>=", 0);
239271
testRecord.put("@order", "date-");
272+
240273
item.put("TestRecord", testRecord);
241274
} // TestRecord >>>>>>>>>>>>>>>>>>>>>>>>>>>>>
242275

243276
request.put("[]", item);
244277
} // [] >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
245278

279+
{ // TestRecord[] <<<<<<<<<<<<<<<<<<<<<<<<<<<<<
280+
JSONObject item = new JSONObject();
281+
282+
{ // TestRecord <<<<<<<<<<<<<<<<<<<<<<<<<<<<<
283+
JSONObject testRecord = new JSONObject();
284+
testRecord.put("@column", "id,randomId,total,correct,wrong,compare,response");
285+
testRecord.put("id{}@", "TestRecord/sameIds");
286+
testRecord.put("documentId@", "TestRecord/documentId");
287+
if (isLast) {
288+
testRecord.put("reportId@", "TestRecord/reportId");
289+
} else {
290+
testRecord.put("reportId", reportId);
291+
}
292+
testRecord.put("total>=", 0);
293+
testRecord.put("correct>=", 0);
294+
testRecord.put("wrong>=", 0);
295+
testRecord.put("@order", "date-");
296+
297+
item.put("TestRecord", testRecord);
298+
} // TestRecord >>>>>>>>>>>>>>>>>>>>>>>>>>>>>
299+
300+
request.put("TestRecord[]", item);
301+
302+
} // TestRecord[] >>>>>>>>>>>>>>>>>>>>>>>>>>>>>
303+
246304
DemoParser parser = new DemoParser(RequestMethod.GET, false);
247305
JSONObject response = parser.parseResponse(request);
306+
if (! JSONResponse.isSuccess(response)) {
307+
throw new IOException(JSONResponse.getMsg(response));
308+
}
309+
310+
// JSONObject lastTr = response.getJSONObject("TestRecord");
311+
// long documentId = lastTr == null ? 0 : lastTr.getLongValue("documentId");
312+
// long randomId = lastTr == null ? 0 : lastTr.getLongValue("randomId");
313+
if (isLast) {
314+
JSONObject lastTr = response.getJSONObject("TestRecord");
315+
reportId = lastTr == null ? 0 : lastTr.getLongValue("reportId");
316+
if (reportId > 0) {
317+
name = "CVAuto_report_" + reportId + ".xlsx";
318+
path = fileUploadRootDir + name;
319+
}
320+
}
321+
248322
JSONArray array = response.getJSONArray("[]");
323+
JSONArray trArr = response.getJSONArray("TestRecord[]");
249324

250325
List<ExcelUtil.DetailItem> list = new ArrayList<>();
251326
if (array != null) {
@@ -258,19 +333,75 @@ public ResponseEntity<Object> downloadCVReport(@PathVariable(name = "reportId")
258333
}
259334
if (testRecord == null) {
260335
testRecord = new JSONObject();
336+
if (trArr != null) {
337+
int found = -1;
338+
for (int j = 0; j < trArr.size(); j++) {
339+
JSONObject tr = trArr.getJSONObject(j);
340+
long randomId = tr == null ? 0 : tr.getLongValue("randomId");
341+
if (randomId <= 0 || ! Objects.equals(randomId, random.getLongValue("id"))) {
342+
continue;
343+
}
344+
345+
testRecord = tr;
346+
found = j;
347+
break;
348+
}
349+
350+
if (found >= 0) {
351+
trArr.remove(found);
352+
}
353+
}
261354
}
262355

263356
String fn = random.getString("file");
264357
int ind = fn.lastIndexOf(".");
265358
String nfn = fn.substring(0, ind) + "_render" + fn.substring(ind);
359+
360+
String resStr = testRecord.getString("response");
361+
362+
String img = random.getString("img");
363+
JSONObject resObj = StringUtil.isEmpty(img) ? null : JSON.parseObject(resStr);
364+
365+
int l = resStr == null ? 0 : resStr.length();
366+
if (l > 32767) { // EasyExcel 单元格最长字符数限制
367+
resStr = resStr.substring(0, 20000) + " ... " + resStr.substring(l - 12760);
368+
}
369+
370+
boolean hasResult = resObj != null && ! resObj.isEmpty();
371+
if (hasResult) {
372+
try {
373+
JSONObject renderReq = new JSONObject();
374+
renderReq.put("img", img);
375+
renderReq.put("data", resObj);
376+
String body = renderReq.toJSONString();
377+
HttpHeaders headers = new HttpHeaders();
378+
String renderStr = demoController.sendRequest(session, HttpMethod.POST, "http://localhost:3003/cv/render", body, headers);
379+
renderStr = StringUtil.trim(renderStr);
380+
if (renderStr.length() > 100) {
381+
File renderFile = new File(fileUploadRootDir + nfn);
382+
try (FileOutputStream fos = new FileOutputStream(renderFile)) {
383+
int commaInd = renderStr.startsWith("data:image/") ? -1 : renderStr.indexOf("base64,");
384+
String base64 = commaInd < 0 ? renderStr : renderStr.substring(commaInd + "base64,".length());
385+
byte[] bytes = Base64.getDecoder().decode(base64);
386+
fos.write(bytes);
387+
fos.flush();
388+
} catch (Throwable e) {
389+
e.printStackTrace();
390+
}
391+
}
392+
} catch (Throwable e) {
393+
e.printStackTrace();
394+
}
395+
}
396+
266397
list.add(new ExcelUtil.DetailItem(
267-
fn,
268-
nfn, // TODO 调用 JSONResponse.js 来渲染
398+
fileUploadRootDir + fn,
399+
fileUploadRootDir + nfn, // TODO 调用 JSONResponse.js 来渲染
269400
testRecord.getIntValue("total"),
270401
testRecord.getIntValue("correct"),
271402
testRecord.getIntValue("wrong"),
272-
testRecord.getString("response"),
273-
"✅",
403+
resStr,
404+
hasResult ? "✅" : "❌",
274405
testRecord.getString("compare")
275406
));
276407
}

‎APIJSON-Java-Server/APIJSONBoot-MultiDataSource/src/main/resources/application.yml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@ spring:
33
multipart:
44
max-file-size: 10MB
55
max-request-size: 10MB
6+
enabled: true
7+
file-size-threshold: 2KB
68
# If using flyway-core dependency, either comment this out or use @FlywayDatasource in DemoDataSourceConfig
79
# flyway:
810
# url: ${spring.datasource.hikari.jdbc-url}

‎APIJSON-Java-Server/APIJSONBoot-MultiDataSource/src/main/resources/static/cv/apijson/JSONResponse.js

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2582,8 +2582,8 @@ var JSONResponse = {
25822582
return item.degree || item.rotate || item.angle || item.perspective || item.d || item.r || item.p || item.a
25832583
},
25842584

2585-
drawDetections: function(canvas, detection, options, img, ctx) {
2586-
if (!detection||typeofdetection !=='object') {
2585+
drawDetections: function(canvas, detection, options, img, ctx,isClear) {
2586+
if (Object.keys(JSONResponse.isObject(detection) ? detection : {})<=0) {
25872587
console.error('drawDetections: invalid detection input');
25882588
return;
25892589
}
@@ -2595,7 +2595,9 @@ var JSONResponse = {
25952595
const isRoot = ctx == null;
25962596
if (isRoot) {
25972597
ctx = canvas.getContext('2d');
2598-
ctx.clearRect(0, 0, width, height);
2598+
if (isClear != false) {
2599+
ctx.clearRect(0, 0, width, height);
2600+
}
25992601
}
26002602
ctx.lineWidth = Math.max(1, Math.min(8, height * 0.005));
26012603
ctx.font = `bold ${fontSize}px sans-serif`;

0 commit comments

Comments
(0)

AltStyle によって変換されたページ (->オリジナル) /