Browse Source

feat: 新增南瑞科技红外数据定时上送

master
yinhuaiwei 4 weeks ago
parent
commit
2e9f75803f
12 changed files with 546 additions and 2 deletions
  1. +9
    -1
      pom.xml
  2. +8
    -1
      src/main/java/com/inspect/simulator/mapper/ResultAnalysisMapper.java
  3. +16
    -0
      src/main/java/com/inspect/simulator/naritech/config/AuthProperties.java
  4. +23
    -0
      src/main/java/com/inspect/simulator/naritech/config/NaritechProperties.java
  5. +50
    -0
      src/main/java/com/inspect/simulator/naritech/config/RestTemplateConfig.java
  6. +91
    -0
      src/main/java/com/inspect/simulator/naritech/controller/NaritechController.java
  7. +42
    -0
      src/main/java/com/inspect/simulator/naritech/dto/NaritechPatrolData.java
  8. +15
    -0
      src/main/java/com/inspect/simulator/naritech/dto/NaritechPatrolResponse.java
  9. +32
    -0
      src/main/java/com/inspect/simulator/naritech/dto/NaritechTokenResponse.java
  10. +140
    -0
      src/main/java/com/inspect/simulator/naritech/service/NaritechPatrolDataService.java
  11. +77
    -0
      src/main/java/com/inspect/simulator/naritech/service/NaritechTokenService.java
  12. +43
    -0
      src/main/resources/mapper/ResultAnalysisMapper.xml

+ 9
- 1
pom.xml View File

@ -157,7 +157,15 @@
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.4.3</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
</dependency>
<dependency>
<groupId>com.github.pagehelper</groupId>
<artifactId>pagehelper-spring-boot-starter</artifactId>
<version>1.4.1</version>
</dependency>
<dependency>
<groupId>net.java.jna</groupId>
<artifactId>jna</artifactId>


+ 8
- 1
src/main/java/com/inspect/simulator/mapper/ResultAnalysisMapper.java View File

@ -1,11 +1,12 @@
package com.inspect.simulator.mapper;
import com.inspect.simulator.domain.analysis.vo.AnalysisResult;
import com.inspect.simulator.domain.bigmodelr.ContentJson;
import com.inspect.simulator.domain.visual.VisualJson;
import com.inspect.simulator.naritech.dto.NaritechPatrolData;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
import java.time.LocalDateTime;
import java.util.List;
@Mapper
@ -20,4 +21,10 @@ public interface ResultAnalysisMapper {
int addDmtModelInfo(@Param("list") List<ContentJson> contentJsonList);
int addVisualModelInfo(@Param("list") List<VisualJson> visualJsonList);
List<NaritechPatrolData> selectNaritechNotUploaded();
int batchInsertUploadLog(@Param("logIds") List<Long> logIds,
@Param("type") String type,
@Param("uploadTime") LocalDateTime uploadTime);
}

+ 16
- 0
src/main/java/com/inspect/simulator/naritech/config/AuthProperties.java View File

@ -0,0 +1,16 @@
package com.inspect.simulator.naritech.config;
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
@Data
@Component
@ConfigurationProperties(prefix = "naritech.auth")
public class AuthProperties {
private String grantType;
private String scope;
private String clientId;
private String clientSecret;
}

+ 23
- 0
src/main/java/com/inspect/simulator/naritech/config/NaritechProperties.java View File

@ -0,0 +1,23 @@
package com.inspect.simulator.naritech.config;
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
@Data
@Component
@ConfigurationProperties(prefix = "naritech.api")
public class NaritechProperties {
private String baseUrl;
private String tokenPath;
private String patrolReceivePath;
public String getFullTokenUrl() {
return baseUrl + tokenPath;
}
public String getFullPatrolReceiveUrl() {
return baseUrl + patrolReceivePath;
}
}

+ 50
- 0
src/main/java/com/inspect/simulator/naritech/config/RestTemplateConfig.java View File

@ -0,0 +1,50 @@
package com.inspect.simulator.naritech.config;
import org.apache.http.config.Registry;
import org.apache.http.config.RegistryBuilder;
import org.apache.http.conn.socket.ConnectionSocketFactory;
import org.apache.http.conn.socket.PlainConnectionSocketFactory;
import org.apache.http.conn.ssl.NoopHostnameVerifier;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
import org.apache.http.ssl.SSLContexts;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.client.HttpComponentsClientHttpRequestFactory;
import org.springframework.web.client.RestTemplate;
import javax.net.ssl.SSLContext;
import java.security.KeyManagementException;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
@Configuration
public class RestTemplateConfig {
@Bean
public RestTemplate restTemplate() throws NoSuchAlgorithmException, KeyStoreException, KeyManagementException {
SSLContext sslContext = SSLContexts.custom()
.loadTrustMaterial(null, (chain, authType) -> true)
.build();
SSLConnectionSocketFactory sslSocketFactory = new SSLConnectionSocketFactory(
sslContext, NoopHostnameVerifier.INSTANCE);
Registry<ConnectionSocketFactory> registry = RegistryBuilder.<ConnectionSocketFactory>create()
.register("http", PlainConnectionSocketFactory.getSocketFactory())
.register("https", sslSocketFactory)
.build();
PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager(registry);
connectionManager.setMaxTotal(50);
connectionManager.setDefaultMaxPerRoute(20);
CloseableHttpClient httpClient = HttpClients.custom()
.setConnectionManager(connectionManager)
.build();
return new RestTemplate(new HttpComponentsClientHttpRequestFactory(httpClient));
}
}

+ 91
- 0
src/main/java/com/inspect/simulator/naritech/controller/NaritechController.java View File

@ -0,0 +1,91 @@
package com.inspect.simulator.naritech.controller;
import com.inspect.simulator.naritech.dto.NaritechPatrolData;
import com.inspect.simulator.naritech.dto.NaritechPatrolResponse;
import com.inspect.simulator.naritech.service.NaritechPatrolDataService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.annotation.Resource;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;
@Slf4j
@RestController
@RequestMapping("/naritech")
public class NaritechController {
private static final int BATCH_SIZE = 500;
@Resource
private NaritechPatrolDataService naritechPatrolDataService;
@GetMapping("/uploadPatrolData")
public void uploadPatrolData() {
log.info("===== 巡视数据上送任务开始, 当前时间: {} =====", LocalDateTime.now());
try {
List<NaritechPatrolData> dataList = naritechPatrolDataService.collectPatrolData();
if (dataList.isEmpty()) {
log.info("无待上传数据,任务结束");
return;
}
int total = dataList.size();
int totalBatches = (total - 1) / BATCH_SIZE + 1;
log.info("本次共需上送 {} 条数据, 共 {} 个批次, 每批 {} 条", total, totalBatches, BATCH_SIZE);
List<Long> uploadedIds = new ArrayList<>();
for (int i = 0; i < total; i+=BATCH_SIZE) {
int end = Math.min(i + BATCH_SIZE, total);
List<NaritechPatrolData> subDataList = dataList.subList(i, end);
int batchNum = (i / BATCH_SIZE) + 1;
try {
NaritechPatrolResponse response = naritechPatrolDataService.uploadPatrolData(subDataList);
if (response != null && Boolean.TRUE.equals(response.getResult())) {
List<Long> ids = dataList.stream().map(NaritechPatrolData::getLogId).collect(Collectors.toList());
naritechPatrolDataService.recordUploadSuccess(ids);
uploadedIds.addAll(ids);
log.info("第 {}/{} 批上送成功, {} 条", batchNum, totalBatches, subDataList.size());
} else {
log.error("第 {}/{} 批上送失败, 远端响应: {}",
batchNum, totalBatches,
response != null ? response.getMessage() : "无");
}
} catch (Exception e) {
log.error("第 {}/{} 批上送异常", batchNum, totalBatches, e);
}
}
log.info("===== 任务结束, 成功: {}/{}, 失败: {} =====", uploadedIds.size(), total, total - uploadedIds.size());
} catch (Exception e) {
log.error("===== 巡视数据上送任务异常 =====", e);
}
}
@GetMapping("/test")
public void test() {
log.info("===== 巡视数据测试上送任务开始, 当前时间: {} =====", LocalDateTime.now());
try {
List<NaritechPatrolData> dataList = new ArrayList<>();
NaritechPatrolData data = NaritechPatrolData.builder()
.logId(1L)
.lineId(12736374)
.areaName("极Ⅱ")
.deviceName("极Ⅱ高Y/Y-C相")
.patroldeviceName("")
.patrolpointName("极Ⅱ高Y/Y-C相电容器与管母接头测温")
.value("36.7℃")
.time("2026-05-26 01:01:03")
.unit("℃")
.build();
dataList.add(data);
NaritechPatrolResponse response = naritechPatrolDataService.uploadPatrolData(dataList);
log.info("===== 巡视数据测试上送任务完成, 结果: {} =====",
response != null ? response.getMessage() : "无数据");
} catch (Exception e) {
log.error("===== 巡视数据测试上送任务异常 =====", e);
}
}
}

+ 42
- 0
src/main/java/com/inspect/simulator/naritech/dto/NaritechPatrolData.java View File

@ -0,0 +1,42 @@
package com.inspect.simulator.naritech.dto;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.Builder;
import lombok.Data;
import lombok.ToString;
@Data
@Builder
@ToString
@JsonInclude(JsonInclude.Include.NON_NULL)
public class NaritechPatrolData {
/** result_analysis 主键(用于记录,不上送) */
@JsonProperty(access = JsonProperty.Access.WRITE_ONLY)
private Long logId;
/** 唯一标识ID */
private Integer lineId;
/** 区域 */
private String areaName;
/** 设备 */
private String deviceName;
/** 相别 */
private String patroldeviceName;
/** 点位 */
private String patrolpointName;
/** 值 */
private String value;
/** 生成时间(格式:yyyy-MM-dd HH:mm:ss) */
private String time;
/** 单位(用于SQL映射,不上送) */
@JsonProperty(access = JsonProperty.Access.WRITE_ONLY)
private String unit;
}

+ 15
- 0
src/main/java/com/inspect/simulator/naritech/dto/NaritechPatrolResponse.java View File

@ -0,0 +1,15 @@
package com.inspect.simulator.naritech.dto;
import lombok.Data;
@Data
public class NaritechPatrolResponse {
private Boolean result;
private Integer code;
private String message;
private String body;
}

+ 32
- 0
src/main/java/com/inspect/simulator/naritech/dto/NaritechTokenResponse.java View File

@ -0,0 +1,32 @@
package com.inspect.simulator.naritech.dto;
import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.Data;
@Data
public class NaritechTokenResponse {
@JsonProperty("access_token")
private String accessToken;
private String ver;
private String code;
private String scope;
private String iss;
@JsonProperty("token_type")
private String tokenType;
private String message;
@JsonProperty("expires_in")
private Integer expiresIn;
@JsonProperty("client_id")
private String clientId;
private String jti;
}

+ 140
- 0
src/main/java/com/inspect/simulator/naritech/service/NaritechPatrolDataService.java View File

@ -0,0 +1,140 @@
package com.inspect.simulator.naritech.service;
import com.inspect.simulator.mapper.ResultAnalysisMapper;
import com.inspect.simulator.naritech.config.NaritechProperties;
import com.inspect.simulator.naritech.dto.NaritechPatrolData;
import com.inspect.simulator.naritech.dto.NaritechPatrolResponse;
import com.inspect.simulator.utils.StringUtils;
import org.apache.commons.lang3.math.NumberUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestTemplate;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;
@Service
public class NaritechPatrolDataService {
private static final Logger log = LoggerFactory.getLogger(NaritechPatrolDataService.class);
private final RestTemplate restTemplate;
private final NaritechProperties apiProperties;
private final NaritechTokenService tokenService;
private final ResultAnalysisMapper resultAnalysisMapper;
public NaritechPatrolDataService(RestTemplate restTemplate,
NaritechProperties apiProperties,
NaritechTokenService tokenService,
ResultAnalysisMapper resultAnalysisMapper) {
this.restTemplate = restTemplate;
this.apiProperties = apiProperties;
this.tokenService = tokenService;
this.resultAnalysisMapper = resultAnalysisMapper;
}
/**
* 收集巡视数据
* 此处需要根据实际业务替换为真实的数据来源数据库查询文件读取等
*/
public List<NaritechPatrolData> collectPatrolData() {
// 查询当天未上报的红外算法巡视记录
// infrared: filter=1 其他算法: filter=0
List<NaritechPatrolData> dataList = resultAnalysisMapper.selectNaritechNotUploaded();
// 去掉读数单位
if (dataList != null && !dataList.isEmpty()) {
dataList.forEach(NaritechPatrolDataService::removeUnitFromValue);
}
return dataList;
}
/**
* 去掉读数单位
*/
private static void removeUnitFromValue(NaritechPatrolData data) {
String value = data.getValue();
if (StringUtils.isEmpty(value)) {
return;
}
String unit = data.getUnit();
String[] vals = value.split(",", -1);
List<String> cleanedValues = new ArrayList<>();
for (String val : vals) {
if (val.endsWith(unit)) {
val = val.substring(0, val.length() - unit.length());
}
// 非数字值直接返回空值
if (!NumberUtils.isCreatable(val)) {
data.setValue("");
return;
}
cleanedValues.add(val);
}
data.setValue(String.join(",", cleanedValues));
}
/**
* 上送巡视数据到远端接口
*/
public NaritechPatrolResponse uploadPatrolData(List<NaritechPatrolData> dataList) {
if (dataList == null || dataList.isEmpty()) {
log.warn("无巡视数据需要上送");
return null;
}
String token = tokenService.getAccessToken();
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_JSON);
headers.setBearerAuth(token);
HttpEntity<List<NaritechPatrolData>> requestEntity = new HttpEntity<>(dataList, headers);
String url = apiProperties.getFullPatrolReceiveUrl();
log.info("上送巡视数据到: {}, 数据条数: {}", url, dataList.size());
try {
NaritechPatrolResponse response = restTemplate.postForObject(url, requestEntity, NaritechPatrolResponse.class);
if (response != null && Boolean.TRUE.equals(response.getResult())) {
log.info("巡视数据上送成功: {}", response.getMessage());
} else {
log.error("巡视数据上送失败: {}", response != null ? response.getMessage() : "无响应");
}
return response;
} catch (Exception e) {
log.error("巡视数据上送异常", e);
throw new RuntimeException("巡视数据上送失败: " + e.getMessage(), e);
}
}
public void recordUploadSuccess(List<Long> logIds) {
if (logIds == null || logIds.isEmpty()) {
log.warn("无巡视数据需要上送");
return;
}
resultAnalysisMapper.batchInsertUploadLog(logIds, "naritech#infrared", LocalDateTime.now());
log.info("已记录 {} 条上传成功数据", logIds.size());
}
public static void main(String[] args) {
NaritechPatrolData data = NaritechPatrolData.builder()
.lineId(12736374)
.areaName("极Ⅱ")
.deviceName("极Ⅱ高Y/Y-C相")
.patroldeviceName("")
.patrolpointName("极Ⅱ高Y/Y-C相电容器与管母接头测温")
.value("27,2℃")
.time("2026-05-26 01:01:03")
.unit("℃")
.build();
removeUnitFromValue(data);
System.out.println(data);
}
}

+ 77
- 0
src/main/java/com/inspect/simulator/naritech/service/NaritechTokenService.java View File

@ -0,0 +1,77 @@
package com.inspect.simulator.naritech.service;
import com.inspect.simulator.naritech.config.AuthProperties;
import com.inspect.simulator.naritech.config.NaritechProperties;
import com.inspect.simulator.naritech.dto.NaritechTokenResponse;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
import org.springframework.web.client.RestTemplate;
import java.time.Instant;
@Slf4j
@Service
public class NaritechTokenService {
private final RestTemplate restTemplate;
private final NaritechProperties apiProperties;
private final AuthProperties authProperties;
private volatile String cachedToken;
private volatile long tokenExpireTime = 0;
public NaritechTokenService(RestTemplate restTemplate,
NaritechProperties apiProperties,
AuthProperties authProperties) {
this.restTemplate = restTemplate;
this.apiProperties = apiProperties;
this.authProperties = authProperties;
}
public synchronized String getAccessToken() {
// Token未过期则复用提前5分钟刷新
if (cachedToken != null && System.currentTimeMillis() < tokenExpireTime - 300_000) {
return cachedToken;
}
return fetchToken();
}
private String fetchToken() {
log.info("开始获取访问Token...");
try {
MultiValueMap<String, String> body = new LinkedMultiValueMap<>();
body.add("grant_type", authProperties.getGrantType());
body.add("scope", authProperties.getScope());
body.add("client_id", authProperties.getClientId());
body.add("client_secret", authProperties.getClientSecret());
String url = apiProperties.getFullTokenUrl();
log.info("请求Token接口: {}", url);
NaritechTokenResponse response = restTemplate.postForObject(url, body, NaritechTokenResponse.class);
if (response != null && response.getAccessToken() != null) {
cachedToken = response.getAccessToken();
if (response.getExpiresIn() != null && response.getExpiresIn() > 0) {
tokenExpireTime = System.currentTimeMillis() + response.getExpiresIn() * 1000L;
} else {
// 默认24小时过期
tokenExpireTime = System.currentTimeMillis() + 86400_000L;
}
log.info("Token获取成功, 过期时间: {}", Instant.ofEpochMilli(tokenExpireTime));
return cachedToken;
}
log.error("Token获取失败: 响应为空或缺少access_token");
throw new RuntimeException("Token获取失败");
} catch (Exception e) {
log.error("Token获取异常", e);
throw new RuntimeException("Token获取失败: " + e.getMessage(), e);
}
}
public void clearToken() {
cachedToken = null;
tokenExpireTime = 0;
}
}

+ 43
- 0
src/main/resources/mapper/ResultAnalysisMapper.xml View File

@ -73,4 +73,47 @@
</foreach>
</insert>
<select id="selectNaritechNotUploaded" resultType="com.inspect.simulator.naritech.dto.NaritechPatrolData">
SELECT ra.line_id AS log_id,
ra.objectId AS line_id,
ba.area_name,
bd.device_name,
bdt.device_type_name AS patrolpoint_name,
bp.patrol_point_name,
ra.res_value AS value,
ra.create_time AS time,
bp.purpose AS unit
FROM result_analysis ra
LEFT JOIN basedata_patrolpoint bp ON bp.patrol_point_id = ra.objectId
LEFT JOIN basedata_area ba ON ba.area_id = bp.relation_area_id
LEFT JOIN basedata_device bd ON bd.device_id = bp.relation_device_id
LEFT JOIN basedata_devicetype bdt ON bdt.device_type_id = bp.device_type_id
LEFT JOIN patrol_upload_log pul ON pul.log_id = ra.line_id
LEFT JOIN patrol_task_status pts ON pts.task_patrolled_id = ra.task_patrol_id
WHERE ((ra.alg_type IN (
'infrared2',
'infra_1800',
'infra_yu3',
'infra_camera',
'infra_camera_reverse'
)
AND ra.filter = 0)
OR (
ra.alg_type = 'infrared'
AND ra.filter = 1
))
AND ra.create_time &gt;= CURDATE()
AND ra.create_time &lt; CURDATE() + INTERVAL 1 DAY
AND pts.task_state = '1'
AND pul.id IS NULL
</select>
<insert id="batchInsertUploadLog">
INSERT INTO patrol_upload_log (log_id, type, upload_time)
VALUES
<foreach collection="logIds" item="id" separator=",">
(#{id}, #{type}, #{uploadTime})
</foreach>
ON DUPLICATE KEY UPDATE upload_time = VALUES(upload_time)
</insert>
</mapper>

Loading…
Cancel
Save