From 89e37cb6279609aa79b7bad15d96e74d88d212d8 Mon Sep 17 00:00:00 2001 From: guoshengxiong <1923636941@qq.com> Date: Fri, 5 Dec 2025 09:50:10 +0800 Subject: [PATCH] =?UTF-8?q?=E6=9C=BA=E5=9C=BA=E9=A1=B9=E7=9B=AE=E6=8E=A8?= =?UTF-8?q?=E9=80=81=E6=89=AC=E5=B0=98=E6=95=B0=E6=8D=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../zhgd/xmgl/task/EnvironmentDevTask.java | 73 ++++++- .../java/com/zhgd/xmgl/util/jc/AESUtil.java | 197 ++++++++++++++++++ .../zhgd/xmgl/util/jc/CqHdyHttpSignature.java | 79 +++++++ .../com/zhgd/xmgl/util/jc/JcHttpUtil.java | 128 ++++++++++++ .../xmgl/task/EnvironmentDevTaskTest.java | 76 +++++++ 5 files changed, 548 insertions(+), 5 deletions(-) create mode 100644 src/main/java/com/zhgd/xmgl/util/jc/AESUtil.java create mode 100644 src/main/java/com/zhgd/xmgl/util/jc/CqHdyHttpSignature.java create mode 100644 src/main/java/com/zhgd/xmgl/util/jc/JcHttpUtil.java create mode 100644 src/test/java/com/zhgd/xmgl/task/EnvironmentDevTaskTest.java diff --git a/src/main/java/com/zhgd/xmgl/task/EnvironmentDevTask.java b/src/main/java/com/zhgd/xmgl/task/EnvironmentDevTask.java index e8f7cf65d..8c5da987f 100644 --- a/src/main/java/com/zhgd/xmgl/task/EnvironmentDevTask.java +++ b/src/main/java/com/zhgd/xmgl/task/EnvironmentDevTask.java @@ -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 devs = environmentDevService.list(new LambdaQueryWrapper() + .eq(EnvironmentDev::getProjectSn, "3224634D940B4F4E866190875279D1BC")); + Date now = new Date(); + DateTime offsetHour = DateUtil.offsetHour(now, -1); + ArrayList dustDataFormLists = Lists.newArrayList(); + for (EnvironmentDev dev : devs) { + DustNoiseData data = dustNoiseDataService.getOne(new LambdaQueryWrapper() + .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); + } + } + } diff --git a/src/main/java/com/zhgd/xmgl/util/jc/AESUtil.java b/src/main/java/com/zhgd/xmgl/util/jc/AESUtil.java new file mode 100644 index 000000000..74a623b74 --- /dev/null +++ b/src/main/java/com/zhgd/xmgl/util/jc/AESUtil.java @@ -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"))); + } +} diff --git a/src/main/java/com/zhgd/xmgl/util/jc/CqHdyHttpSignature.java b/src/main/java/com/zhgd/xmgl/util/jc/CqHdyHttpSignature.java new file mode 100644 index 000000000..ab96d4b22 --- /dev/null +++ b/src/main/java/com/zhgd/xmgl/util/jc/CqHdyHttpSignature.java @@ -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; + } + +} + + + + + + diff --git a/src/main/java/com/zhgd/xmgl/util/jc/JcHttpUtil.java b/src/main/java/com/zhgd/xmgl/util/jc/JcHttpUtil.java new file mode 100644 index 000000000..8979aef90 --- /dev/null +++ b/src/main/java/com/zhgd/xmgl/util/jc/JcHttpUtil.java @@ -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 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 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; + + } + + +} diff --git a/src/test/java/com/zhgd/xmgl/task/EnvironmentDevTaskTest.java b/src/test/java/com/zhgd/xmgl/task/EnvironmentDevTaskTest.java new file mode 100644 index 000000000..947ff7ab8 --- /dev/null +++ b/src/test/java/com/zhgd/xmgl/task/EnvironmentDevTaskTest.java @@ -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()); + } +}