XmoComplements.java 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341
  1. package com.example.getweather.service;
  2. import cn.hutool.db.Db;
  3. import cn.hutool.db.Entity;
  4. import cn.hutool.http.HttpUtil;
  5. import cn.hutool.json.JSONObject;
  6. import cn.hutool.json.JSONUtil;
  7. import com.example.getweather.entity.DistrictCodeDO;
  8. import com.example.getweather.entity.HeFengDay;
  9. import com.example.getweather.entity.HeFengHour;
  10. import com.example.getweather.util.WeixinPush;
  11. import com.taosdata.jdbc.TSDBDriver;
  12. import lombok.extern.slf4j.Slf4j;
  13. import org.springframework.stereotype.Service;
  14. import org.springframework.validation.annotation.Validated;
  15. import javax.annotation.Resource;
  16. import java.math.BigDecimal;
  17. import java.math.RoundingMode;
  18. import java.sql.*;
  19. import java.text.ParseException;
  20. import java.text.SimpleDateFormat;
  21. import java.time.Instant;
  22. import java.time.LocalDateTime;
  23. import java.time.ZoneId;
  24. import java.time.format.DateTimeFormatter;
  25. import java.util.*;
  26. import java.util.Date;
  27. import java.util.stream.Collectors;
  28. @Service
  29. @Validated
  30. @Slf4j
  31. public class XmoComplements {
  32. @Resource
  33. private WeixinPush weixinPush;
  34. // 辅助方法:安全地获取列表中的值并转换为字符串,如果列表为空或索引越界则返回空字符串
  35. private static String getValueSafelyAsString(List<?> list, int index) {
  36. if (list == null || index < 0 || index >= list.size()) {
  37. return "";
  38. }
  39. Object value = list.get(index);
  40. return value != null ? String.valueOf(value) : "";
  41. }
  42. // 辅助方法:安全地获取列表中的值,如果列表为空或索引越界则返回null
  43. private static <T> T getValueSafely(List<T> list, int index) {
  44. if (list == null || index < 0 || index >= list.size()) {
  45. return null;
  46. }
  47. return list.get(index);
  48. }
  49. public static Timestamp convertToTimestamp(String dateTimeStr) {
  50. SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
  51. try {
  52. Date parsedDate = dateFormat.parse(dateTimeStr);
  53. return new Timestamp(parsedDate.getTime());
  54. } catch (ParseException e) {
  55. e.printStackTrace();
  56. return null;
  57. }
  58. }
  59. public void download() {
  60. List<String> areaCodes = new ArrayList<>();
  61. List<DistrictCodeDO> districtCodeDOList = new ArrayList<>();
  62. // 获取今天的日期(yyyy-MM-dd)
  63. String startDate = "2025-02-18";
  64. String endDate = "2025-03-05";
  65. boolean isApiQuotaExhausted = false; // 引入标志变量
  66. try {
  67. List<Entity> dictList = Db.use().findAll("system_dict_data");
  68. for (Entity entity : dictList) {
  69. String name = entity.getStr("label");
  70. if (name.contains("area_code")) {
  71. String code = entity.getStr("value");
  72. String[] s = code.split(",");
  73. areaCodes = Arrays.asList(s);
  74. }
  75. }
  76. List<Entity> districtCodes = Db.use().findAll("jy_district_code");
  77. for (Entity e : districtCodes) {
  78. DistrictCodeDO districtCodeDO = new DistrictCodeDO();
  79. districtCodeDO.setCode(Integer.parseInt(e.getStr("code")));
  80. districtCodeDO.setName(e.getStr("name"));
  81. districtCodeDO.setLevel(Integer.parseInt(e.getStr("level")));
  82. districtCodeDO.setType(Integer.parseInt(e.getStr("type")));
  83. districtCodeDO.setAbname(e.getStr("abname"));
  84. districtCodeDO.setPid(Integer.parseInt(e.getStr("pid")));
  85. districtCodeDO.setLat(!Objects.equals(e.getStr("lat"), "") && e.getStr("lat") != null ? new BigDecimal(e.getStr("lat")).setScale(6, RoundingMode.HALF_UP).doubleValue() : 0d);
  86. districtCodeDO.setLng(!Objects.equals(e.getStr("lng"), "") && e.getStr("lng") != null ? new BigDecimal(e.getStr("lng")).setScale(6, RoundingMode.HALF_UP).doubleValue() : 0d);
  87. districtCodeDOList.add(districtCodeDO);
  88. }
  89. String jdbcUrl = "jdbc:TAOS://192.168.12.241:29501/etadm_local?user=root&password=taosdata";
  90. Properties connProps = new Properties();
  91. connProps.setProperty(TSDBDriver.PROPERTY_KEY_CHARSET, "UTF-8");
  92. connProps.setProperty(TSDBDriver.PROPERTY_KEY_LOCALE, "en_US.UTF-8");
  93. connProps.setProperty(TSDBDriver.PROPERTY_KEY_TIME_ZONE, "UTC-8");
  94. Connection conn = DriverManager.getConnection(jdbcUrl, connProps);
  95. //省份
  96. for (String code : areaCodes) {
  97. if (isApiQuotaExhausted) break; // 检查标志变量
  98. List<DistrictCodeDO> shiList = districtCodeDOList.stream().filter(item -> item.getPid().toString().equals(code)).collect(Collectors.toList());
  99. //市
  100. for (DistrictCodeDO d : shiList) {
  101. if (isApiQuotaExhausted) break; // 检查标志变量
  102. List<DistrictCodeDO> quList = districtCodeDOList.stream().filter(item -> item.getPid().toString().equals(d.getCode().toString())).collect(Collectors.toList());
  103. String xmoUrl;
  104. //曦谋综合数据查询
  105. xmoUrl = "https://weather-api.xm-opt.com/v1/forecast15Minutes?latitude=" + d.getLat() + "&longitude=" + d.getLng() + "&minutely_15=weather_code,temperature_2m,surface_pressure,relative_humidity_2m,precipitation,cloud_cover,wind_speed_10m,wind_direction_10m,shortwave_radiation&timezone=Asia%2FShanghai&start_date=" + startDate + "&end_date=" + endDate;
  106. isApiQuotaExhausted = analysis(xmoUrl, d.getCode(), d.getName(), conn); // 传递标志变量
  107. if (isApiQuotaExhausted) break; // 检查标志变量
  108. //区
  109. for (DistrictCodeDO q : quList) {
  110. if (isApiQuotaExhausted) break; // 检查标志变量
  111. xmoUrl = "https://weather-api.xm-opt.com/v1/forecast15Minutes?latitude=" + d.getLat() + "&longitude=" + d.getLng() + "&minutely_15=weather_code,temperature_2m,surface_pressure,relative_humidity_2m,precipitation,cloud_cover,wind_speed_10m,wind_direction_10m,shortwave_radiation&timezone=Asia%2FShanghai&start_date=" + startDate + "&end_date=" + endDate;
  112. isApiQuotaExhausted = analysis(xmoUrl, q.getCode(), q.getName(), conn); // 传递标志变量
  113. if (isApiQuotaExhausted) break; // 检查标志变量
  114. //线程睡5秒 防止请求过快
  115. Thread.sleep(5000);
  116. }
  117. }
  118. }
  119. conn.close();
  120. String alarm = "电力交易系统在获取15分钟级天气数据时报错, 曦谋天气API 额度已用尽";
  121. String description = "接口调用异常";
  122. if (isApiQuotaExhausted) {
  123. //给企业微信发送消息
  124. //weixinPush.sendMessageBot(alarm, description);
  125. } else {
  126. alarm = "电力交易系统曦谋天气数据全部下载完毕";
  127. description = "接口调用成功";
  128. //weixinPush.sendMessageBot(alarm, description);
  129. log.info("曦谋天气15分钟级天气数据全部下载完毕");
  130. }
  131. } catch (Exception e) {
  132. e.printStackTrace();
  133. }
  134. }
  135. public boolean analysis(String xmoUrl, int areaCode, String name, Connection conn) {
  136. log.info("曦谋天气数据,开始下载地区:{} 的天气数据", name);
  137. try {
  138. Statement ps = conn.createStatement();
  139. String body = HttpUtil.createGet(xmoUrl).header("apikey", "c615a8f6-f5dd-4a81-86fe-db2a42862c15").execute().charset("utf-8").body();
  140. JSONObject jsonObject = JSONUtil.parseObj(body);
  141. if (jsonObject.toString().contains("API call limit has been reached")) {
  142. return true;
  143. }
  144. JSONObject result = JSONUtil.parseObj(jsonObject.get("minutely_15"));
  145. List<HeFengHour> xmoList = new ArrayList<>();
  146. // 获取各个数据数组
  147. List<String> timeList = result.get("time", List.class);
  148. List<Integer> weatherCodeList = result.get("weather_code", List.class);
  149. List<Double> temperature2mList = result.get("temperature_2m", List.class);
  150. List<Double> surfacePressureList = result.get("surface_pressure", List.class);
  151. List<Double> relativeHumidity2mList = result.get("relative_humidity_2m", List.class);
  152. List<Double> precipitationList = result.get("precipitation", List.class);
  153. List<Double> cloudCoverList = result.get("cloud_cover", List.class);
  154. List<Double> windSpeed10mList = result.get("wind_speed_10m", List.class);
  155. List<Double> windDirection10mList = result.get("wind_direction_10m", List.class);
  156. List<Double> shortwaveRadiationList = result.get("shortwave_radiation", List.class);
  157. int length = timeList == null ? 0 : timeList.size();
  158. //获取当前时间 yyyy-mm-dd hh:mm
  159. //String currentTime = LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm"));
  160. // 定义输入日期时间格式
  161. DateTimeFormatter inputFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm");
  162. if (length > 0) {
  163. // 遍历数组,填充XMO对象并添加到列表
  164. for (int i = 0; i < length; i++) {
  165. HeFengHour heFengHour = new HeFengHour();
  166. // 解析输入字符串为 LocalDateTime 对象
  167. LocalDateTime localDateTime = LocalDateTime.parse(getValueSafely(timeList, i), inputFormatter);
  168. // 将 LocalDateTime 转换为 Instant 对象
  169. Instant instant = localDateTime.atZone(ZoneId.systemDefault()).toInstant();
  170. // 将 Instant 对象转换为 Date 对象
  171. //Date 转为 timestamp。getTimestamp
  172. Date date = Date.from(instant);
  173. heFengHour.setTs(new Timestamp(date.getTime()));
  174. heFengHour.setWindSpeed(Objects.equals(getValueSafelyAsString(windSpeed10mList, i), "") || getValueSafelyAsString(windSpeed10mList, i) == null ? null : Float.parseFloat(getValueSafelyAsString(windSpeed10mList, i)));
  175. heFengHour.setWindSpeedM(Objects.equals(getValueSafelyAsString(windSpeed10mList, i), "") || getValueSafelyAsString(windSpeed10mList, i) == null ? null : Float.parseFloat(getValueSafelyAsString(windSpeed10mList, i)) * 1000 / 3600);
  176. heFengHour.setWindDir(getValueSafelyAsString(windDirection10mList, i));
  177. heFengHour.setTemp(Objects.equals(getValueSafelyAsString(temperature2mList, i), "") || getValueSafelyAsString(temperature2mList, i) == null ? null : Float.parseFloat(getValueSafelyAsString(temperature2mList, i)));
  178. heFengHour.setPrecip(Float.parseFloat(getValueSafelyAsString(precipitationList, i)));
  179. heFengHour.setText(skycon(getValueSafelyAsString(weatherCodeList, i)));
  180. heFengHour.setDswrf(Float.parseFloat(getValueSafelyAsString(shortwaveRadiationList, i)));
  181. heFengHour.setHumidity(Objects.equals(getValueSafelyAsString(relativeHumidity2mList, i), "") || getValueSafelyAsString(relativeHumidity2mList, i) == null ? null : Float.parseFloat(getValueSafelyAsString(relativeHumidity2mList, i)));
  182. heFengHour.setPressure(Objects.equals(getValueSafelyAsString(surfacePressureList, i), "") || getValueSafelyAsString(surfacePressureList, i) == null ? null : Float.parseFloat(getValueSafelyAsString(surfacePressureList, i)));
  183. heFengHour.setCloud(Objects.equals(getValueSafelyAsString(cloudCoverList, i), "") || getValueSafelyAsString(cloudCoverList, i) == null ? null : Float.parseFloat(getValueSafelyAsString(cloudCoverList, i)));
  184. xmoList.add(heFengHour);
  185. }
  186. }
  187. for (HeFengHour item : xmoList) {
  188. String insertSql = " INSERT INTO hefeng_hour_unit_" + areaCode + " USING hefeng_hour_unit TAGS('" + areaCode + "') values('" + item.getTs() + "', " + item.getTemp() + ", '" + item.getText() + "', '" + item.getWindDir() + "', " + item.getWind360() + ", " + item.getWindSpeed() + ", " + item.getWindScale() + ", " + item.getHumidity() + ", " + item.getCloud() + ", " + item.getPrecip() + ", " + item.getDew() + ", " + item.getPressure() + ", " + item.getDswrf() + ", " + item.getWindSpeedM() + ")";
  189. int affectedRows = ps.executeUpdate(insertSql);
  190. }
  191. if (ps != null)
  192. ps.close();
  193. } catch (Exception e) {
  194. log.error("曦谋天气,地区:{} 15分钟级天气数据下载失败JSON格式化错误" + e, name);
  195. return true;
  196. }
  197. log.info("曦谋天气,地区:{} 15分钟级天气数据下载完成", name);
  198. return false;
  199. }
  200. /**
  201. * 根据代码获取对应天气描述
  202. *
  203. * @param skycon
  204. * @return
  205. */
  206. private String skycon(String skycon) {
  207. switch (skycon) {
  208. case "0":
  209. return "晴";
  210. case "1":
  211. return "多云";
  212. case "2":
  213. return "少云";
  214. case "3":
  215. return "阴";
  216. case "45":
  217. return "雾";
  218. case "48":
  219. return "冻雾";
  220. case "51":
  221. case "53":
  222. case "55":
  223. return "毛毛雨";
  224. case "56":
  225. case "57":
  226. return "冻毛毛雨";
  227. case "61":
  228. return "小雨";
  229. case "63":
  230. return "中雨";
  231. case "65":
  232. return "大雨";
  233. case "66":
  234. case "67":
  235. return "冻雨";
  236. case "71":
  237. return "小雪";
  238. case "73":
  239. return "中雪";
  240. case "75":
  241. return "大雪";
  242. case "77":
  243. return "雪粒";
  244. case "80":
  245. return "小阵雨";
  246. case "81":
  247. return "阵雨";
  248. case "82":
  249. return "强阵雨";
  250. case "85":
  251. return "小阵雪";
  252. case "86":
  253. return "阵雪";
  254. case "95":
  255. return "雷雨";
  256. case "96":
  257. case "99":
  258. return "雷雨伴有冰雹";
  259. default:
  260. return "晴"; // 默认返回晴
  261. }
  262. }
  263. public void cal() {
  264. try {
  265. String jdbcUrl = "jdbc:TAOS://192.168.12.241:29501/etadm_local?user=root&password=taosdata";
  266. Properties connProps = new Properties();
  267. connProps.setProperty(TSDBDriver.PROPERTY_KEY_CHARSET, "UTF-8");
  268. connProps.setProperty(TSDBDriver.PROPERTY_KEY_LOCALE, "en_US.UTF-8");
  269. connProps.setProperty(TSDBDriver.PROPERTY_KEY_TIME_ZONE, "UTC-8");
  270. Connection conn = DriverManager.getConnection(jdbcUrl, connProps);
  271. //--------------------------------------------------
  272. Statement ps;
  273. ps = conn.createStatement();
  274. //所有数据
  275. ResultSet rs = null;
  276. //转换过后的数据
  277. List<Map<String, String>> a;
  278. String start = "";
  279. String end = "";
  280. //---------创建文件----------
  281. //String[] csvNames = table.split(",");
  282. String sql = "select * from etadm_local.hefeng_day_unit";
  283. rs = ps.executeQuery(sql);
  284. //计数
  285. int count = 0;
  286. List<HeFengDay> hefengDayList = new ArrayList<>();
  287. while (rs.next()) {
  288. HeFengDay heFengDay = new HeFengDay();
  289. heFengDay.setAreaCode(rs.getString("area_code"));
  290. heFengDay.setTs(convertToTimestamp(rs.getString("ts")));
  291. heFengDay.setTempMax(Float.parseFloat(rs.getString("temp_max")));
  292. heFengDay.setTempMin(Float.parseFloat(rs.getString("temp_min")));
  293. heFengDay.setTextDay(rs.getString("text_day"));
  294. heFengDay.setTextNight(rs.getString("text_night"));
  295. heFengDay.setWind360Day(Float.parseFloat(rs.getString("wind_360_day")));
  296. heFengDay.setWindDirDay(rs.getString("wind_dir_day"));
  297. heFengDay.setWindScaleDay(new BigDecimal(rs.getString("wind_scale_day")).intValue());
  298. heFengDay.setWindSpeedDay(Float.parseFloat(rs.getString("wind_speed_day")));
  299. heFengDay.setWind360Night(Float.parseFloat(rs.getString("wind_360_night")));
  300. heFengDay.setWindDirNight(rs.getString("wind_dir_night"));
  301. heFengDay.setWindScaleNight(new BigDecimal(rs.getString("wind_scale_night")).intValue());
  302. heFengDay.setWindSpeedNight(Float.parseFloat(rs.getString("wind_speed_night")));
  303. heFengDay.setHumidity(Float.parseFloat(rs.getString("humidity")));
  304. heFengDay.setPrecip(Float.parseFloat(rs.getString("precip")));
  305. heFengDay.setPressure(Float.parseFloat(rs.getString("pressure")));
  306. heFengDay.setDswrf(Float.parseFloat(rs.getString("dswrf")));
  307. heFengDay.setWindSpeedMDay(new BigDecimal(rs.getString("wind_speed_day")).divide(BigDecimal.valueOf(3.6), 4, RoundingMode.HALF_UP).floatValue());
  308. heFengDay.setWindSpeedMNight(new BigDecimal(rs.getString("wind_speed_night")).divide(BigDecimal.valueOf(3.6), 4, RoundingMode.HALF_UP).floatValue());
  309. hefengDayList.add(heFengDay);
  310. }
  311. for (HeFengDay heFengDay : hefengDayList) {
  312. String insertSQL = "INSERT INTO etadm_local.hefeng_day_unit_" + heFengDay.getAreaCode() + " USING hefeng_day_unit TAGS('" + heFengDay.getAreaCode() + "') values('" + heFengDay.getTs() + "', " + heFengDay.getTempMax() + ", " + heFengDay.getTempMin() + ", '" + heFengDay.getTextDay() + "', '" + heFengDay.getTextNight() + "', " + heFengDay.getWind360Day() + ", '"
  313. + heFengDay.getWindDirDay() + "', " + heFengDay.getWindScaleDay() + ", " + heFengDay.getWindSpeedDay() + ", " + heFengDay.getWind360Night() + ", '" + heFengDay.getWindDirNight() + "', " + heFengDay.getWindScaleNight() + ", " + heFengDay.getWindSpeedNight() + ", " + heFengDay.getHumidity() + ", " + heFengDay.getPrecip() + ", "
  314. + heFengDay.getPressure() + ", " + heFengDay.getDswrf() + ", " + heFengDay.getWindSpeedMDay() + ", " + heFengDay.getWindSpeedMNight() + ")";
  315. int affectedRows = ps.executeUpdate(insertSQL);
  316. count = count + affectedRows;
  317. }
  318. log.info("数据转换完成,共转换{}条数据", count);
  319. if (rs != null)
  320. rs.close();
  321. if (ps != null)
  322. ps.close();
  323. conn.close();
  324. } catch (Exception e) {
  325. e.printStackTrace();
  326. }
  327. }
  328. }