机场项目推送扬尘数据

This commit is contained in:
guoshengxiong 2025-12-05 09:50:10 +08:00
parent 98c61bbb0a
commit 89e37cb627
5 changed files with 548 additions and 5 deletions

View File

@ -1,7 +1,10 @@
package com.zhgd.xmgl.task;
import cn.hutool.core.convert.Convert;
import com.google.common.collect.Lists;
import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.date.DateTime;
import cn.hutool.core.date.DateUnit;
import cn.hutool.core.date.DateUtil;
import com.alibaba.fastjson.JSONArray;
@ -14,6 +17,8 @@ import com.zhgd.xmgl.modules.environment.entity.DustNoiseData;
import com.zhgd.xmgl.modules.environment.entity.EnvironmentDev;
import com.zhgd.xmgl.modules.environment.mapper.DustNoiseDataMapper;
import com.zhgd.xmgl.modules.environment.mapper.EnvironmentDevMapper;
import com.zhgd.xmgl.modules.environment.service.IDustNoiseDataService;
import com.zhgd.xmgl.modules.environment.service.IEnvironmentDevService;
import com.zhgd.xmgl.modules.project.entity.Project;
import com.zhgd.xmgl.modules.project.mapper.ProjectMapper;
import com.zhgd.xmgl.modules.sprayrt.entity.SprayRtDev;
@ -21,16 +26,15 @@ import com.zhgd.xmgl.modules.sprayrt.mapper.SprayRtDevMapper;
import com.zhgd.xmgl.modules.sprayrt.service.ISprayRtDevService;
import com.zhgd.xmgl.modules.sprayrt.service.impl.SprayRtDevServiceImpl;
import com.zhgd.xmgl.task.dto.EnvironmentDustDataDto;
import com.zhgd.xmgl.util.AsyncTaskUtil;
import com.zhgd.xmgl.util.EnvironmentUtils;
import com.zhgd.xmgl.util.WindDirectionUtils;
import com.zhgd.xmgl.util.XiwonUtil;
import com.zhgd.xmgl.util.*;
import com.zhgd.xmgl.util.jc.JcHttpUtil;
import lombok.extern.slf4j.Slf4j;
import net.javacrumbs.shedlock.core.SchedulerLock;
import org.apache.commons.lang.math.NumberUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Lazy;
import org.springframework.http.HttpStatus;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.web.bind.annotation.RequestMapping;
@ -69,6 +73,15 @@ public class EnvironmentDevTask {
@Resource
private XiwonUtil xiwonUtil;
@Lazy
@Autowired
private IEnvironmentDevService environmentDevService;
@Lazy
@Autowired
private IDustNoiseDataService dustNoiseDataService;
@Lazy
@Autowired
private EnvironmentUtil environmentUtil;
/**
* 定时调用外部接口拉取设备实时数据
@ -142,7 +155,6 @@ public class EnvironmentDevTask {
}
}
/**
* 获取扬尘数据(携稳) 每5分钟触发任务
*/
@ -252,4 +264,55 @@ public class EnvironmentDevTask {
}
}
/**
* 机场项目推送扬尘
*/
@SchedulerLock(name = "jcPushDustData", lockAtMostFor = 1000 * 60 * 60, lockAtLeastFor = 1000 * 60 * 3)
@Scheduled(cron = "0 0 */1 * * ?")
@RequestMapping("jcPushDustData")
public void jcPushDustData() {
if (!environmentUtil.isBaise()) {
return;
}
List<EnvironmentDev> devs = environmentDevService.list(new LambdaQueryWrapper<EnvironmentDev>()
.eq(EnvironmentDev::getProjectSn, "3224634D940B4F4E866190875279D1BC"));
Date now = new Date();
DateTime offsetHour = DateUtil.offsetHour(now, -1);
ArrayList<JcHttpUtil.SmartDustDataForm> dustDataFormLists = Lists.newArrayList();
for (EnvironmentDev dev : devs) {
DustNoiseData data = dustNoiseDataService.getOne(new LambdaQueryWrapper<DustNoiseData>()
.eq(DustNoiseData::getDeviceId, dev.getDeviceId())
.ge(DustNoiseData::getUploadDate,DateUtil.formatDateTime(offsetHour))
.le(DustNoiseData::getUploadDate,DateUtil.formatDateTime(now))
.orderByDesc(DustNoiseData::getUploadDate).last("limit 1"));
if (data != null) {
JcHttpUtil.SmartDustDataForm e = new JcHttpUtil.SmartDustDataForm();
e.setDustName(dev.getDeviceName());
e.setDeviceSn(data.getDeviceId());
e.setAccTime(data.getUploadDate());
e.setPm25(Convert.toStr(data.getPm25()));
e.setPm10(Convert.toStr(data.getPm10()));
e.setNoise(Convert.toStr(data.getNoise()));
e.setEnvTemp(Convert.toStr(data.getTemperature()));
e.setEnvRh(Convert.toStr(data.getHumidity()));
if (StringUtils.isNotEmpty(data.getWinddirection())) {
e.setWindDirection(JcHttpUtil.getWindDirection(WindDirectionUtils.getWindDirectionName(data.getWinddirection())));
}
e.setWindSpeed(Convert.toStr(data.getWindspeed()));
e.setPushTime(data.getUploadDate());
e.setState("1");
dustDataFormLists.add(e);
}
}
if (CollUtil.isNotEmpty(dustDataFormLists)) {
JcHttpUtil.DustData e = new JcHttpUtil.DustData();
e.setProjectId(JcHttpUtil.jcProjectId);
for (JcHttpUtil.SmartDustDataForm dustDataForm : dustDataFormLists) {
dustDataForm.setProjectId(e.getProjectId());
}
e.setSmartDustDataFormList(dustDataFormLists);
JcHttpUtil.jcPushDustData(e);
}
}
}

View File

@ -0,0 +1,197 @@
package com.zhgd.xmgl.util.jc;
import cn.hutool.core.codec.Base64Decoder;
import cn.hutool.core.codec.Base64Encoder;
import org.apache.commons.codec.DecoderException;
import org.apache.commons.codec.binary.Base64;
import org.apache.commons.codec.binary.Hex;
import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;
import java.io.UnsupportedEncodingException;
import java.nio.charset.StandardCharsets;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
/**
* @ClassName AESUtil
* @Description TODO
* @Author 801058
* @Date 2022/10/17 11:11
*/
public class AESUtil {
// 身份证号,手机号等数据加密
public static final String SECRET = "smart-constructs";
// 身份证号机密key新改
public static final String SECRET_ID_CARD = "JADD@#KWEII37421@$";
// 工区加密
public static final String WORK_SECRET = "smart-secret-001";
// 加密
public static String Encrypt(String sSrc, String sKey) throws Exception {
if (sKey == null) {
sKey = SECRET;
}
// 判断Key是否为16位
if (sKey.length() != 16) {
System.out.print("Key长度不是16位");
return null;
}
byte[] raw = sKey.getBytes(StandardCharsets.UTF_8);
SecretKeySpec skeySpec = new SecretKeySpec(raw, "AES");
Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding");//"算法/模式/补码方式"
cipher.init(Cipher.ENCRYPT_MODE, skeySpec);
byte[] encrypted = cipher.doFinal(sSrc.getBytes("utf-8"));
return new Base64().encodeToString(encrypted);//此处使用BASE64做转码功能同时能起到2次加密的作用
}
// 解密
public static String Decrypt(String sSrc, String sKey) throws Exception {
try {
// 判断Key是否正确
if (sKey == null) {
System.out.print("Key为空null");
return sSrc;
}
// 判断Key是否为16位
if (sKey.length() != 16) {
System.out.print("Key长度不是16位");
return sSrc;
}
byte[] raw = sKey.getBytes("utf-8");
SecretKeySpec skeySpec = new SecretKeySpec(raw, "AES");
Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding");
cipher.init(Cipher.DECRYPT_MODE, skeySpec);
byte[] encrypted1 = new Base64().decode(sSrc);//先用base64解密
try {
byte[] original = cipher.doFinal(encrypted1);
String originalString = new String(original,"utf-8");
return originalString;
} catch (Exception e) {
System.out.println(e.toString());
return sSrc;
}
} catch (Exception ex) {
System.out.println(ex.toString());
return sSrc;
}
}
/**
* 加密
*
* @param password 密码值
* @param strKey 加密key
* @return 加密后的值
*/
public static String aes_encrypt(String password, String strKey) {
try {
SecretKey key = generateMySQLAESKey(strKey, "ASCII");
Cipher cipher = Cipher.getInstance("AES");
cipher.init(Cipher.ENCRYPT_MODE, key);
byte[] cleartext = password.getBytes("UTF-8");
byte[] ciphertextBytes = cipher.doFinal(cleartext);
return new String(Hex.encodeHex(ciphertextBytes));
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
} catch (NoSuchPaddingException e) {
e.printStackTrace();
} catch (InvalidKeyException e) {
e.printStackTrace();
} catch (IllegalBlockSizeException e) {
e.printStackTrace();
} catch (BadPaddingException e) {
e.printStackTrace();
}
return null;
}
/**
* 解密
*
* @param content 加密后的内容
* @param aesKey 加密key
* @return 解密后的内容
*/
public static String aes_decrypt(String content, String aesKey) {
try {
SecretKey key = generateMySQLAESKey(aesKey, "ASCII");
Cipher cipher = Cipher.getInstance("AES");
cipher.init(Cipher.DECRYPT_MODE, key);
byte[] cleartext = Hex.decodeHex(content.toCharArray());
byte[] ciphertextBytes = cipher.doFinal(cleartext);
return new String(ciphertextBytes, "UTF-8");
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
} catch (NoSuchPaddingException e) {
e.printStackTrace();
} catch (InvalidKeyException e) {
e.printStackTrace();
} catch (IllegalBlockSizeException e) {
e.printStackTrace();
} catch (BadPaddingException e) {
e.printStackTrace();
} catch (DecoderException e) {
e.printStackTrace();
}
return null;
}
/**
* 生成加密key
*
* @param key key
* @param encoding 编码格式
* @return SecretKeySpec
*/
public static SecretKeySpec generateMySQLAESKey(final String key, final String encoding) {
try {
final byte[] finalKey = new byte[16];
int i = 0;
for (byte b : key.getBytes(encoding))
finalKey[i++ % 16] ^= b;
return new SecretKeySpec(finalKey, "AES");
} catch (UnsupportedEncodingException e) {
throw new RuntimeException(e);
}
}
// public static void main(String[] args) throws Exception {
// /*
// * 此处使用AES-128-ECB加密模式key需要为16位
// */
// // 需要加密的字串
// String cSrc = "test加密123456";
// System.out.println(cSrc);
// // 加密
// String enString = AESUtil.Encrypt(cSrc, SECRET);
// System.out.println("加密后的字串是:" + enString);
//
// // 解密
// String DeString = AESUtil.Decrypt(enString, SECRET);
// System.out.println("解密后的字串是:" + DeString);
// }
public static void main(String[] args) throws Exception {
System.out.println(Decrypt("e975b4a1d243a354cb4784f0c3fb973401f9972ee3f9888b2d1c00d0d03406", SECRET));
System.out.println(new String(Base64Decoder.decode("e975b4a1d243a354cb4784f0c3fb973401f9972ee3f9888b2d1c00d0d03406")));
}
}

View File

@ -0,0 +1,79 @@
package com.zhgd.xmgl.util.jc;
import cn.hutool.core.codec.Base64Encoder;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import javax.crypto.Mac;
import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;
import java.net.URLEncoder;
public class CqHdyHttpSignature {
private static final String MAC_NAME = "HmacSHA1";
private static final String ENCODING = "UTF-8";
private static final String SIGNATURE_TPL = "ts=${ts}&ttl=${ttl}&uid=${uid}";
private static final String uid="fawkes";
private static final int ttl=1800;
// 请联系接口提供方提供秘钥值
private static final String CLIENT_SECRET="fawkes_secret";
public static byte[] HmacSHA1Encrypt(String encryptText, String encryptKey) {
try {
byte[] data = encryptKey.getBytes(ENCODING);
// 根据给定的字节数组构造一个密钥,第二参数指定一个密钥算法的名称
SecretKey secretKey = new SecretKeySpec(data, MAC_NAME);
// 生成一个指定 Mac 算法 Mac 对象
Mac mac = Mac.getInstance(MAC_NAME);
// 用给定密钥初始化 Mac 对象
mac.init(secretKey);
byte[] text = encryptText.getBytes(ENCODING);
// 完成 Mac 操作
return mac.doFinal(text);
} catch (Exception e) {
throw new RuntimeException(e.getMessage());
}
}
/**
* 演示方法
* @param args
*/
public static void main(String[] args) {
System.out.println("请在url后拼接该字符串: ?" + getSignParams());
}
public static String getSignParams() {
try {
String signStr = SIGNATURE_TPL.replace("${ts}", (System.currentTimeMillis()+"").substring(0,10))
.replace("${ttl}", ttl + "")
.replace("${uid}", uid).toString();
String signParams = Base64Encoder.encode(HmacSHA1Encrypt(signStr, CLIENT_SECRET));
signParams = URLEncoder.encode(signParams, "utf-8");
return "sign=" + signParams + "&" + signStr;
} catch (Exception e) {
throw new RuntimeException(e.getMessage());
}
}
@AllArgsConstructor
@NoArgsConstructor
@Data
public static class SignatureData {
private String sign;
private long ts;
private int ttl;
private String uid;
}
}

View File

@ -0,0 +1,128 @@
package com.zhgd.xmgl.util.jc;
import cn.hutool.core.util.URLUtil;
import cn.hutool.http.HttpRequest;
import com.alibaba.fastjson.JSONObject;
import com.gexin.fastjson.JSON;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import java.nio.charset.StandardCharsets;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@Slf4j
@Component
public class JcHttpUtil {
public static String jcUrl;
public static String jcProjectId;
@Value("${jcUrl:http://bim.szairport.com:58001}")
public void setJcUrl(String jcUrl) {
JcHttpUtil.jcUrl = jcUrl;
}
@Value("${jcProjectId:1941334582112555010}")
public void setJcProjectId(String jcProjectId) {
JcHttpUtil.jcProjectId = jcProjectId;
}
public static Integer getWindDirection(String windDirectionName) {
if (windDirectionName == null || windDirectionName.trim().isEmpty()) {
return null;
}
// 去除首尾空格
String direction = windDirectionName.trim();
// 使用HashMap建立映射关系
Map<String, Integer> directionMap = new HashMap<>();
directionMap.put("东北偏北", 0);
directionMap.put("东北", 1);
directionMap.put("东北偏东", 2);
directionMap.put("", 3);
directionMap.put("东南偏东", 4);
directionMap.put("东南", 5);
directionMap.put("东南偏南", 6);
directionMap.put("", 7);
directionMap.put("西南偏南", 8);
directionMap.put("西南", 9);
directionMap.put("西南偏西", 10);
directionMap.put("西", 11);
directionMap.put("西北偏西", 12);
directionMap.put("西北", 13);
directionMap.put("西北偏北", 14);
directionMap.put("", 15);
// 精确匹配
if (directionMap.containsKey(direction)) {
return directionMap.get(direction);
}
return null;
}
/**
* 机场项目推送实时环境数据
*
* @param dustData
*/
public static void jcPushDustData(JcHttpUtil.DustData dustData) {
String url = JcHttpUtil.jcUrl + "/api/smart-site/remote/dust/sync?" + CqHdyHttpSignature.getSignParams();
log.info("机场项目推送实时环境数据,url:{}", url);
String body = JSON.toJSONString(dustData);
log.info("机场项目推送实时环境数据,body:{}", body);
String result = HttpRequest.post(url).body(body).execute().body();
log.info("机场项目推送实时环境数据,rs:{}", result);
}
@Data
public static class DustData {
private String projectId;
private List<SmartDustDataForm> smartDustDataFormList;
}
@Data
public static class SmartDustDataForm {
/*
projectId String 项目id(请联系接口提供方获取)
smartDustDataFormList List 列表名称(字段如下)
dustId String 环境监测设备id
dustName String 环境监测名称
deviceSn String 环境监测设备sn
accTime String 环境监测数据采集时间(yyyy-MM-dd HH:mm:ss
pm25 String
pm10 String
noise String 噪音
envTemp String 环境温度
envRh String 环境湿度
windDirection Integer 风向(具体参考下面所对应的字典)
windSpeed String 风速
projectId Integer 项目id
orgId String 推送方id
pushTime String 推送数据的时间
muddy String 浊度
state String State 0:离线 1在线
*/
private Object dustId;
private String dustName;
private String deviceSn;
private String accTime;
private String pm25;
private String pm10;
private String noise;
private String envTemp;
private String envRh;
private Integer windDirection;
private String windSpeed;
private String projectId;
private Object orgId;
private String pushTime;
private Object muddy;
private String state;
}
}

View File

@ -0,0 +1,76 @@
package com.zhgd.xmgl.task;
import com.baomidou.mybatisplus.core.MybatisConfiguration;
import com.baomidou.mybatisplus.core.metadata.TableInfoHelper;
import com.zhgd.xmgl.modules.environment.entity.DustNoiseData;
import com.zhgd.xmgl.modules.environment.entity.EnvironmentDev;
import com.zhgd.xmgl.modules.environment.service.IDustNoiseDataService;
import com.zhgd.xmgl.modules.environment.service.IEnvironmentDevService;
import com.zhgd.xmgl.util.EnvironmentUtil;
import org.apache.ibatis.builder.MapperBuilderAssistant;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
import java.util.Collections;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.*;
@ExtendWith(MockitoExtension.class)
class EnvironmentDevTaskTest {
@Mock
private EnvironmentUtil environmentUtil;
@Mock
private IEnvironmentDevService environmentDevService;
@Mock
private IDustNoiseDataService dustNoiseDataService;
@InjectMocks
private EnvironmentDevTask controller; // 替换为你的控制器类名
// 在类加载时初始化 TableInfo静态方法
@BeforeAll
static void initMybatisPlus() {
TableInfoHelper.initTableInfo(new MapperBuilderAssistant(new MybatisConfiguration(), ""), DustNoiseData.class);
}
@Test
void testJcPushDusatData() {
// 1. 模拟环境检查通过
when(environmentUtil.isBaise()).thenReturn(true);
// 2. 模拟有设备数据
EnvironmentDev mockDev = new EnvironmentDev();
mockDev.setDeviceId("YC30625PSP");
mockDev.setDeviceName("三跑道环境监测设备");
mockDev.setProjectSn("3224634D940B4F4E866190875279D1BC");
when(environmentDevService.list(any())).thenReturn(Collections.singletonList(mockDev));
// 3. 模拟有扬尘数据
DustNoiseData mockData = new DustNoiseData();
mockData.setDeviceId("YC30625PSP");
mockData.setUploadDate("2025-12-04 13:16:12");
mockData.setPm25(13.0);
mockData.setPm10(15.0);
mockData.setNoise(69.0);
mockData.setTemperature(25.0);
mockData.setHumidity(55.0);
mockData.setWinddirection("东北");
mockData.setWindspeed(3.0);
when(dustNoiseDataService.getOne(any())).thenReturn(mockData);
// 4. 执行方法
controller.jcPushDustData();
// 5. 验证基本调用
verify(environmentUtil).isBaise();
verify(environmentDevService).list(any());
verify(dustNoiseDataService).getOne(any());
}
}