// 文件3: BugImageInfo.java
package com.example.model;
public class BugImageInfo {
private String eqName;
private String collectionTime;
private Long monitorId;
private String exhibitionPath;
private String num;
private String pictureUrl;
private Map<String, Object> pictureAnalysis; // 改为 Map 类型
// 省略 getter/setter
} package com.example.service;
import org.springframework.stereotype.Service;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.client.RestTemplate;
import org.springframework.http.ResponseEntity;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.core.type.TypeReference;
import java.util.*;
import java.time.*;
import java.time.format.DateTimeFormatter;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.io.IOException;
import java.util.concurrent.ConcurrentHashMap;
import javax.annotation.PostConstruct;
import com.example.model.BugApiResponse;
import com.example.model.BugResponseData;
import com.example.model.BugImageInfo;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.net.InetAddress;
import java.util.stream.Collectors;
import org.springframework.web.util.UriComponentsBuilder;
import java.net.URI;
@Service
public class BugImageDownloadService {
private static final ZoneId SERVER_ZONE = ZoneId.of("Asia/Shanghai");
private static final String API_URL = "http://nyzbwlw.com/situation/http/device/bug/getBugData";
private static final DateTimeFormatter API_DATE_FORMAT = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
private static final DateTimeFormatter FILE_DATE_FORMAT = DateTimeFormatter.ofPattern("yyyy-MM-dd");
private static final DateTimeFormatter FILE_TIME_FORMAT = DateTimeFormatter.ofPattern("HH_mm_ss");
private final ConcurrentHashMap<String, LocalDateTime> deviceLastUpdateMap = new ConcurrentHashMap<>();
@Value("${bug.download.deviceCodes:GK2025040001,GK2025040002,GK2025040003,GK2025040004,GK2025040005,GK2025040006,GK2025040007,GK2025040008}")
private List<String> deviceCodes;
private final Map<String, String> cookies = new ConcurrentHashMap<>();
// 存储每个设备的下一个处理日期
private final ConcurrentHashMap<String, LocalDate> deviceNextDateMap = new ConcurrentHashMap<>();
@Value("${bug.image.save.path:F:/GKD/}")
private String savePath;
private final RestTemplate restTemplate;
private final ObjectMapper objectMapper;
public BugImageDownloadService(RestTemplate restTemplate, ObjectMapper objectMapper) {
this.restTemplate = restTemplate;
this.objectMapper = objectMapper;
}
@PostConstruct
public void init() {
// 所有设备从固定起始日期开始
LocalDate startDate = LocalDate.of(2025, 7, 1);
for (String deviceCode : deviceCodes) {
deviceNextDateMap.put(deviceCode, startDate);
}
}
/**
* 持续下载所有设备的新图片
*/
public void downloadNewImagesContinuously() {
System.out.println("[TASK] ====== CONTINUOUS DOWNLOAD STARTED ======");
// System.out.println("[INFO] Devices: " + deviceCodes.size());
while (true) {
try {
boolean allDevicesUpToDate = true;
// 处理每个设备的历史数据
for (String deviceCode : deviceCodes) {
LocalDate currentDate = deviceNextDateMap.get(deviceCode);
LocalDate today = LocalDate.now(SERVER_ZONE);
if (currentDate.isBefore(today)) {
allDevicesUpToDate = false;
// 处理该设备当前日期的数据
LocalDateTime startOfDay = currentDate.atStartOfDay();
LocalDateTime endOfDay = currentDate.atTime(23, 59, 59);
System.out.printf("[PROCESS] Device: %s | Date: %s%n",
deviceCode, currentDate);
// 下载该日期的图片
int[] counts = downloadImagesForDevice(
deviceCode,
startOfDay,
endOfDay,
false // 标记为历史数据请求
);
// 更新设备的下一个处理日期(明天)
deviceNextDateMap.put(deviceCode, currentDate.plusDays(1));
// System.out.printf("[RESULT] Device: %s | Date: %s | Found: %d | Downloaded: %d | Failed: %d%n",
// deviceCode, currentDate, counts[0], counts[1], counts[2]);
// 设备间处理间隔
Thread.sleep(2000);
}
}
// 所有设备都处理到最新日期
if (allDevicesUpToDate) {
System.out.println("[STATUS] All devices up-to-date. Starting real-time updates...");
// ===== 优化后的实时更新逻辑 =====
for (String deviceCode : deviceCodes) {
// 获取上次更新时间(默认当天0点)
LocalDateTime lastUpdate = deviceLastUpdateMap.getOrDefault(
deviceCode,
LocalDate.now(SERVER_ZONE).atStartOfDay() // 使用服务器时区
);
LocalDateTime now = LocalDateTime.now();
// System.out.printf("[REALTIME] Device: %s | Time range: %s to %s%n",
// deviceCode, lastUpdate, now);
// 修复点1:添加第四个参数 true 表示实时请求
int[] counts = downloadImagesForDevice(
deviceCode,
lastUpdate, // 从上次更新时间开始
now,
true // 标记为实时请求
);
// 更新最后记录时间(当前轮询结束时间)
deviceLastUpdateMap.put(deviceCode, now);
System.out.printf("[REALTIME-RESULT] Device: %s | Downloaded: %d | Failed: %d%n",
deviceCode, counts[1], counts[2]);
// 设备间处理间隔
Thread.sleep(2000);
}
// ========================
// 等待4h后再次检查
System.out.println("[STATUS] Real-time round completed. Waiting 4 hours...");
Thread.sleep(14_400_000);
}
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
System.err.println("[ERROR] Task interrupted: " + e.getMessage());
break;
} catch (Exception e) {
System.err.println("[ERROR] Critical error: " + e.getMessage());
try {
Thread.sleep(60_000); // 错误后等待1分钟
} catch (InterruptedException ie) {
Thread.currentThread().interrupt();
break;
}
}
}
}
/**
* 下载指定设备在指定时间范围内的图片
*/
private int[] downloadImagesForDevice(String deviceCode, LocalDateTime startTime, LocalDateTime endTime, boolean isRealtime) {
int maxRetries = 3;
int retryCount = 0;
int waitTime = 2000; // 初始等待时间 2秒
int total = 0;
int success = 0;
int failed = 0;
while (retryCount < maxRetries) {
try {
// ====== 网络诊断 ======
// ... [网络诊断代码保持不变] ...
// ========== 统一时区转换逻辑 ==========
ZonedDateTime serverStart = startTime.atZone(ZoneId.systemDefault())
.withZoneSameInstant(SERVER_ZONE);
ZonedDateTime serverEnd = endTime.atZone(ZoneId.systemDefault())
.withZoneSameInstant(SERVER_ZONE);
String startStr = serverStart.format(API_DATE_FORMAT);
String endStr = serverEnd.format(API_DATE_FORMAT);
// 调试日志增强
// System.out.println("[DEBUG] Time Parameters:");
// System.out.println(" Original Local: " + startTime + " - " + endTime);
// System.out.println(" Converted Server: " +
// serverStart.format(API_DATE_FORMAT) + " - " +
// serverEnd.format(API_DATE_FORMAT));
// System.out.println(" Raw Time: " + startStr + " & " + endStr);
// 使用Spring的URI构建器确保正确编码
UriComponentsBuilder builder = UriComponentsBuilder.fromHttpUrl(API_URL)
.queryParam("deviceCode", deviceCode)
.queryParam("startTime", startStr)
.queryParam("endTime", endStr)
.queryParam("size", 1000)
.queryParam("t", System.currentTimeMillis());
String apiUrl = builder.build().toUriString();
// ============================
// System.out.println("[DEBUG] Final request URL: " + apiUrl);
// ====== 创建带Cookie的请求头 ======
HttpHeaders headers = new HttpHeaders();
// 添加浏览器级 User-Agent
headers.set("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/125.0.0.0 Safari/537.36 Edg/125.0.0.0");
// 必须添加的请求头
headers.set("Accept", "application/json, text/plain, */*");
headers.set("Accept-Language", "zh-CN,zh;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6");
headers.set("Connection", "keep-alive");
headers.set("Referer", "http://nyzbwlw.com/situation/"); // 关键修复:添加 Referer
headers.set("Origin", "http://nyzbwlw.com");
headers.set("Sec-Fetch-Dest", "empty");
headers.set("Sec-Fetch-Mode", "cors");
headers.set("Sec-Fetch-Site", "same-origin");
// 添加 Cookie(关键修复:使用持久化 Cookie)
if (!cookies.isEmpty()) {
String cookieHeader = cookies.entrySet().stream()
.map(entry -> entry.getKey() + "=" + entry.getValue())
.collect(Collectors.joining("; "));
headers.add("Cookie", cookieHeader);
// System.out.println("[DEBUG] Sending cookies: " + cookieHeader);
}
// 发送请求
ResponseEntity<String> response = restTemplate.exchange(
apiUrl, HttpMethod.GET, new HttpEntity<>(headers), String.class);
// 保存新Cookie
List<String> setCookieHeaders = response.getHeaders().get("Set-Cookie");
if (setCookieHeaders != null) {
for (String cookie : setCookieHeaders) {
// 提取关键 Cookie(排除过期/路径信息)
String[] cookieParts = cookie.split(";")[0].split("=");
if (cookieParts.length >= 2) {
String cookieName = cookieParts[0].trim();
String cookieValue = cookieParts[1].trim();
cookies.put(cookieName, cookieValue);
// System.out.println("[COOKIE] Saved session cookie: " + cookieName);
}
}
}
// 响应状态日志
// System.out.println("[DEBUG] Response Status: " +
// response.getStatusCodeValue() + " " +
// response.getStatusCode().getReasonPhrase());
if (response.getStatusCode().is2xxSuccessful() && response.getBody() != null) {
String responseBody = response.getBody();
System.out.println("[RESPONSE-DATA] " + responseBody);
// System.out.println("[DEBUG] Raw API response snippet: "
// + responseBody.substring(0, Math.min(300, responseBody.length()))
// + (responseBody.length() > 300 ? "..." : ""));
// 使用POJO解析响应
BugApiResponse apiResponse = objectMapper.readValue(responseBody, BugApiResponse.class);
// System.out.printf("[DEBUG] Parsed API response code: %d | message: %s%n",
// apiResponse.getCode(), apiResponse.getMsg());
if (apiResponse.getCode() == 100) {
BugResponseData data = apiResponse.getData();
List<BugImageInfo> bugList = (data != null && data.getList() != null) ?
data.getList() : Collections.emptyList();
total = bugList.size();
// System.out.printf("[DEBUG] Found %d valid image entries%n", total);
// 如果有数据,处理并返回
if (total > 0) {
for (BugImageInfo bug : bugList) {
String pictureUrl = bug.getPictureUrl();
String collectionTime = bug.getCollectionTime();
if (pictureUrl != null && !pictureUrl.isEmpty() &&
collectionTime != null && !collectionTime.isEmpty()) {
System.out.printf("[PROCESSUrl] Downloading: %s | %s%n",
collectionTime, pictureUrl);
if (downloadImage(deviceCode, pictureUrl, collectionTime)) {
success++;
} else {
failed++;
}
} else {
// System.err.printf("[WARN] Invalid data: URL=%s | Time=%s%n",
// pictureUrl, collectionTime);
failed++;
}
}
return new int[]{total, success, failed};
}
} else {
System.err.println("[API-ERROR] Device: " + deviceCode
+ " | Code: " + apiResponse.getCode());
}
}
} catch (Exception e) {
System.err.println("[DOWNLOAD-ERROR] Device: " + deviceCode);
// e.printStackTrace();
}
// 如果没有数据或出错,等待后重试
retryCount++;
if (retryCount < maxRetries) {
try {
System.out.printf("[RETRY] Retrying in %d ms (attempt %d/%d)%n",
waitTime, retryCount + 1, maxRetries);
Thread.sleep(waitTime);
waitTime *= 2; // 指数退避
} catch (InterruptedException ie) {
Thread.currentThread().interrupt();
break;
}
}
}
System.out.printf("[STATS] Device: %s | Total: %d | Success: %d | Failed: %d%n",
deviceCode, total, success, failed);
return new int[]{total, success, failed};
}
private boolean downloadImage(String deviceCode, String imageUrl, String collectionTime) {
try {
// 解析采集时间
LocalDateTime dateTime = LocalDateTime.parse(collectionTime, API_DATE_FORMAT);
// 创建保存路径
Path saveDir = Paths.get(savePath,
deviceCode,
dateTime.format(FILE_DATE_FORMAT));
// 确保目录存在
if (!Files.exists(saveDir)) {
Files.createDirectories(saveDir);
System.out.println("[DIR] Created directory: " + saveDir);
}
// 生成文件名
String fileName = dateTime.format(FILE_TIME_FORMAT) + ".png";
Path filePath = saveDir.resolve(fileName);
// 跳过已存在文件
if (Files.exists(filePath)) {
System.out.println("[SKIP] File already exists: " + filePath);
return true;
}
// 下载图片
System.out.println("[DOWNLOAD] Fetching: " + imageUrl);
byte[] imageBytes = restTemplate.getForObject(imageUrl, byte[].class);
// 验证图片数据
if (imageBytes == null || imageBytes.length == 0) {
System.err.println("[ERROR] Empty image data: " + imageUrl);
return false;
}
// 保存图片
Files.write(filePath, imageBytes);
System.out.println("[SUCCESS] Saved: " + filePath + " (" + imageBytes.length + " bytes)");
return true;
} catch (Exception e) {
// 详细错误日志
System.err.println("[IMAGE-ERROR] URL: " + imageUrl);
System.err.println(" Collection Time: " + collectionTime);
System.err.println(" Device: " + deviceCode);
System.err.println(" Error Details: " + e.getMessage());
e.printStackTrace(); // 打印完整堆栈
return false;
}
}
}依据上述代码解决报错:[ERROR] Failed to execute goal org.apache.maven.plugins:maven-compiler-plugin:3.10.1:compile (default-compile) on project example-project: Compilation failure: Compilation failure:
[ERROR] /D:/biancheng/rjxm/zxgxspringboot/src/main/java/com/example/model/BugImageInfo.java:[11,13] 找不到符号
[ERROR] 符号: 类 Map
[ERROR] 位置: 类 com.example.model.BugImageInfo
[ERROR] /D:/biancheng/rjxm/zxgxspringboot/src/main/java/com/example/service/BugImageDownloadService.java:[283,52] 找不到符号
[ERROR] 符号: 方法 getPictureUrl()
[ERROR] 位置: 类型为com.example.model.BugImageInfo的变量 bug
[ERROR] /D:/biancheng/rjxm/zxgxspringboot/src/main/java/com/example/service/BugImageDownloadService.java:[284,56] 找不到符号
[ERROR] 符号: 方法 getCollectionTime()
[ERROR] 位置: 类型为com.example.model.BugImageInfo的变量 bug
[ERROR] -> [Help 1]
[ERROR]
[ERROR] To see the full stack trace of the errors, re-run Maven with the -e switch.
[ERROR] Re-run Maven using the -X switch to enable full debug logging.
[ERROR]
[ERROR] For more information about the errors and possible solutions, please read the following articles:
[ERROR] [Help 1] http://cwiki.apache.org/confluence/display/MAVEN/MojoFailureException
最新发布