mixedAdapter.php 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509
  1. <?php
  2. namespace Heanup\Library\WeatherAdapt;
  3. use Heanup\App\Api\Config\CityToCoordinate;
  4. use Heanup\App\Api\Config\GlobalConfig;
  5. use Heanup\Library\Curl;
  6. use Heanup\Library\Redis;
  7. use Heanup\Library\Warming;
  8. /**
  9. * Class mixedAdapter 混合接口(彩云+天气网)
  10. * @package Heanup\Library\WeatherAdapt
  11. */
  12. class mixedAdapter implements Weather
  13. {
  14. private $city_id;
  15. private $location;
  16. private $weather_code = [
  17. 0 => '晴',
  18. 1 => '多云',
  19. 2 => '阴',
  20. 3 => '阵雨',
  21. 4 => '雷阵雨',
  22. 5 => '雷阵雨伴有冰雹',
  23. 6 => '雨夹雪',
  24. 7 => '小雨',
  25. 8 => '中雨',
  26. 9 => '大雨',
  27. 10 => '暴雨',
  28. 11 => '大暴雨',
  29. 12 => '特大暴雨',
  30. 13 => '阵雪',
  31. 14 => '小雪',
  32. 15 => '中雪',
  33. 16 => '大雪',
  34. 17 => '暴雪',
  35. 18 => '雾',
  36. 19 => '冻雨',
  37. 20 => '沙尘暴',
  38. 21 => '小到中雨',
  39. 22 => '中到大雨',
  40. 23 => '大到暴雨',
  41. 24 => '暴雨到大暴雨',
  42. 25 => '大暴雨到特大暴雨',
  43. 26 => '小到中雪',
  44. 27 => '中到大雪',
  45. 28 => '大到暴雪',
  46. 29 => '浮尘',
  47. 30 => '扬沙',
  48. 31 => '强沙尘暴',
  49. 53 => '霾',
  50. 99 => '无',
  51. 32 => '浓雾',
  52. 49 => '强浓雾',
  53. 54 => '中度霾',
  54. 55 => '重度霾',
  55. 56 => '严重霾',
  56. 57 => '大雾',
  57. 58 => '特强浓雾',
  58. 301 => '雨',
  59. 302 => '雪',
  60. ];
  61. /**
  62. * @param $city_id
  63. * @return mixed
  64. */
  65. public function getWeatherData($city_id)
  66. {
  67. $this->city_id = $city_id;
  68. // $city_id = is_numeric($city_id) ? ("CN" . $city_id) : $city_id;
  69. $key = $this->getKey();
  70. $caiyun_version = GlobalConfig::getData("caiyun_version");
  71. $url = sprintf("https://api.caiyunapp.com/v%s/%s/%s/weather_2.json?alert=true&dailysteps=15", $caiyun_version, $key, $this->getCoodinate($city_id));
  72. $data1 = Curl::get($url);
  73. $data1 = json_decode($data1, true);
  74. $data2=Redis::instance()->get("weather:$city_id");
  75. if (!$data2){
  76. $url = sprintf("https://apip.weatherdt.com/plugin/data?key=FKh9Y6PdLP&location=%s", $city_id);
  77. $data2 = Curl::get($url);
  78. $data2 = json_decode($data2, true);
  79. Redis::instance()->set("weather:$city_id", $data2,7200);
  80. }
  81. $data = $this->parseData($data1, $data2);
  82. $i = 0;
  83. while ($data['Weather']['now']['cond_txt'] == "" && $i < 2) {//数据有误,重试3次
  84. $log_file = APP_DIR . "/logs/" . date("Ymd") . "_null.log";
  85. file_put_contents($log_file, sprintf("%s\t\tcity_id:%s", date("Y-m-d H:i:s"), $city_id) . "\t\tdata1:" . json_encode($data1) . "\t\tdata2:".json_encode($data2)."\r\n", FILE_APPEND);
  86. Warming::semdMail("no_data", "混合接口", ALERT_TOTAL, $log_file);
  87. $url = sprintf("https://api.caiyunapp.com/v%s/%s/%s/weather_2.json?alert=true&dailysteps=15", $caiyun_version, $key, $this->getCoodinate($city_id));
  88. $data1 = Curl::get($url);
  89. $url = sprintf("https://apip.weatherdt.com/plugin/data?key=FKh9Y6PdLP&location=%s", $city_id);
  90. $data2 = Curl::get($url);
  91. $data1 = json_decode($data1, true);
  92. $data2 = json_decode($data2, true);
  93. Redis::instance()->set("weather:$city_id", $data2,7200);
  94. $data = $this->parseData($data1, $data2);
  95. $i++;
  96. }
  97. return $data;
  98. }
  99. private function getKey()
  100. {
  101. $keys = GlobalConfig::getData("caiyun_key");
  102. $key = date("Ymd_") . $keys;
  103. Redis::instance()->incr($key);
  104. return $keys;
  105. }
  106. /**
  107. *
  108. * @param $data
  109. * @param null $data2
  110. * @return array
  111. */
  112. public function parseData($data, $data2 = null)
  113. {
  114. $originData = $data;
  115. $return = [];
  116. $weather = [];
  117. $weather['basic'] = [
  118. 'location' => $this->location,
  119. 'city_code' => $this->city_id,
  120. 'update' => date("Y-m-d H:i", $data['server_time'])
  121. ];
  122. $weather['status'] = $originData['status'] ?: "ok";
  123. $result = $originData['result'];
  124. // $weather['key'] = $k;
  125. $weather['air_now_city'] = [
  126. "aqi" => (string)$result['realtime']['air_quality']['aqi']['chn'] ?? "", // ->a3
  127. "qlty" => (string)$result['realtime']['air_quality']['description']['chn'] ?? "", // ->a4
  128. "pm25" => (string)$result['realtime']['air_quality']['pm25'] ?? "", // ->a5
  129. "pm10" => (string)$result['realtime']['air_quality']['pm10'] ?? "", // ->a6
  130. "no2" => (string)$result['realtime']['air_quality']['no2'] ?? "", // ->a7
  131. "so2" => (string)$result['realtime']['air_quality']['so2'] ?? ""
  132. ];
  133. $weather['alarm'] = [
  134. "level" => "", // ->a9
  135. "link" => $this->getAlert($result['alert']['content'][0]['code']), // ->b1
  136. "type" => "", // ->b2
  137. 'alertId' => (string)$result['alert']['content'][0]['alertId'] ?? "",
  138. "title" => (string)$result['alert']['content'][0]['title'] ?? "",// ->z2
  139. "description" => (string)($result['alert']['content'][0]['description'] ? $result['alert']['content'][0]['description'] . sprintf("(来源:%s)", $result['alert']['content'][0]['source']) : ""), // ->z3
  140. "pubtimestamp" => (string)($result['alert']['content'] ? date("Y-m-d H:i", $result['alert']['content'][0]['pubtimestamp']) : ""), // ->z4
  141. "location" => (string)$result['alert']['content'][0]['location'] ?? "", // ->z5
  142. "status" => (string)$result['alert']['content'][0]['status'] ?? "" // ->z6
  143. ];
  144. $weather['now'] = [
  145. "cond_txt" => $this->getSkycon($result['realtime']['skycon']), // ->b3
  146. "fl" => (string)(intval($result['realtime']['apparent_temperature']) ?: ""),// ->b4
  147. "hum" => (string)intval($result['realtime']['humidity'] * 100), // ->b5
  148. "pres" => (string)intval($result['realtime']['pressure'] / 100), // ->b6
  149. "tmp" => (string)(intval($result['realtime']['temperature']) ?: ""), // ->b7
  150. "vis" => (string)$result['realtime']['visibility'] ?: "", // ->b8
  151. "wind_dir" => (string)$this->getWindDirect($result['realtime']['wind']['direction']) ?: "", // ->b9
  152. "wind_sc" => (string)$this->getWindSpeed($result['realtime']['wind']['speed'])['level'] ?: "", // ->c1
  153. "sr" => (string)$result['daily']['astro'][0]['sunrise']['time'], // ->c2
  154. "ss" => (string)$result['daily']['astro'][0]['sunset']['time'], // ->c3
  155. 'forecast_keypoint' => (string)$result['forecast_keypoint'] ?? "",
  156. 'description' => (string)$result['hourly']['description'] ?? "",
  157. ];
  158. if (!empty($result['daily'])) {
  159. if (!empty($data2['forecast'])) {
  160. $i = 0;
  161. $date = date("Y-m-d", strtotime($data2['forecast']['date']));
  162. foreach ($data2['forecast']['forecast'] as $key => $value) {
  163. $temp = [
  164. "cond_txt_d" => $this->weather_code[intval($value['tqw_code_d'])] ?: ($value['tqw_code_d'] ?: ""),//$value['tqw_code_d'] ?: "",
  165. "cond_txt_n" => $this->weather_code[intval($value['tqw_code_n'])] ?: ($value['tqw_code_n'] ?: ""),//$value['tqw_code_n'] ?: "",
  166. "date" => date("Y-m-d", strtotime("+$i days", strtotime($date))),
  167. "tmp_max" => $value['tmp_max'] ?: "",
  168. "tmp_min" => $value['tmp_min'] ?: "",
  169. "wind_dir" => $value['wind_dir_d_txt'] ?: "",
  170. "wind_sc" => $value['wind_sc_d_txt'] ?: "",
  171. ];
  172. $i++;
  173. $weather['daily_forecast'][] = $temp;
  174. }
  175. }
  176. $j = 0;
  177. foreach ($result['daily']['skycon_08h_20h'] as $key => $value) {
  178. if ($j < $i) {
  179. $j++;
  180. continue;
  181. }
  182. $temp = [
  183. "cond_txt_d" => $this->getSkycon($value['value']),
  184. "cond_txt_n" => $this->getSkycon($result['daily']['skycon_20h_32h'][$key]['value']),
  185. "date" => date("Y-m-d", strtotime($value['date'])),
  186. "tmp_max" => (string)intval($result['daily']['temperature'][$key]['max']),
  187. "tmp_min" => (string)intval($result['daily']['temperature'][$key]['min']),
  188. "wind_dir" => (string)$this->getWindDirect($result['daily']['wind'][$key]['avg']['direction']),
  189. "wind_sc" => (string)$this->getWindSpeed($result['daily']['wind'][$key]['avg']['speed'])['level'],
  190. ];
  191. $weather['daily_forecast'][] = $temp;
  192. }
  193. }
  194. if (!empty($result['hourly'])) {
  195. $i = 0;
  196. foreach ($result['hourly']['temperature'] as $key => $value) {
  197. if ($i >= 24) {
  198. break;
  199. }
  200. $i++;
  201. $temp = [
  202. 'cond_txt' => $this->getSkycon($result['hourly']['skycon'][$key]['value']),
  203. 'time' => date("Y-m-d H:i", strtotime($value['datetime'])),
  204. 'tmp' => (string)intval($value['value']),
  205. ];
  206. $weather['hourly'][] = $temp;
  207. }
  208. }
  209. $weather['lifestyle'] = [
  210. [
  211. 'type' => 'flu',
  212. 'brf' => $result['daily']['life_index']['coldRisk'][0]['desc'],
  213. 'txt' => ''
  214. ], [
  215. 'type' => 'cw',
  216. 'brf' => $result['daily']['life_index']['carWashing'][0]['desc'],
  217. 'txt' => ''
  218. ], [
  219. 'type' => 'uv',
  220. 'brf' => $result['daily']['life_index']['ultraviolet'][0]['desc'],
  221. 'txt' => ''
  222. ], [
  223. 'type' => 'comf',
  224. 'brf' => $result['daily']['life_index']['comfort'][0]['desc'],
  225. 'txt' => ''
  226. ], [
  227. 'type' => 'drsg',
  228. 'brf' => $result['daily']['life_index']['dressing'][0]['desc'],
  229. 'txt' => ''
  230. ],
  231. ];
  232. $return['Weather'] = $weather;
  233. $return['source'] = "caiyun";
  234. return $return;
  235. }
  236. /**
  237. * @param $code
  238. * @return array|mixed|string
  239. */
  240. private function getCoodinate($code)
  241. {
  242. $key = "coodinateData";
  243. $coodinate = Redis::instance()->hGet($key, $code);
  244. // unset($coodinate);
  245. if (!$coodinate['lo'] || !$coodinate['la']) {
  246. $coodinate = CityToCoordinate::getData($code);
  247. if (!empty($coodinate)) {
  248. // $coodinate = $coodinate['lo'] . "," . $coodinate['la'];
  249. Redis::instance()->hSet($key, $code, $coodinate);
  250. }
  251. }
  252. $this->location = $coodinate['location'];
  253. return $coodinate['lo'] . "," . $coodinate['la'];
  254. }
  255. /**
  256. * 获取风向
  257. * @param $degree
  258. * @return mixed
  259. */
  260. private function getWindDirect($degree)
  261. {
  262. $directArr = ["北风", "东北风", "东北风", "东北风", "东风", "东南风", "东南风", "东南风", "南风", "西南风", "西南风", "西南风", "西风", "西北风", "西北风", "西北风"];
  263. $index = 0;
  264. if (348.75 <= $degree && $degree <= 360) {
  265. $index = 0;
  266. } else {
  267. if (0 <= $degree && $degree <= 11.25) {
  268. $index = 0;
  269. } else if (11.25 < $degree && $degree <= 33.75) {
  270. $index = 1;
  271. } else if (33.75 < $degree && $degree <= 56.25) {
  272. $index = 2;
  273. } else if (56.25 < $degree && $degree <= 78.75) {
  274. $index = 3;
  275. } else if (78.75 < $degree && $degree <= 101.25) {
  276. $index = 4;
  277. } else if (101.25 < $degree && $degree <= 123.75) {
  278. $index = 5;
  279. } else if (123.75 < $degree && $degree <= 146.25) {
  280. $index = 6;
  281. } else if (146.25 < $degree && $degree <= 168.75) {
  282. $index = 7;
  283. } else if (168.75 < $degree && $degree <= 191.25) {
  284. $index = 8;
  285. } else if (191.25 < $degree && $degree <= 213.75) {
  286. $index = 9;
  287. } else if (213.75 < $degree && $degree <= 236.25) {
  288. $index = 10;
  289. } else if (236.25 < $degree && $degree <= 258.75) {
  290. $index = 11;
  291. } else if (258.75 < $degree && $degree <= 281.25) {
  292. $index = 12;
  293. } else if (281.25 < $degree && $degree <= 303.75) {
  294. $index = 13;
  295. } else if (303.75 < $degree && $degree <= 326.25) {
  296. $index = 14;
  297. } else if (326.25 < $degree && $degree < 348.75) {
  298. $index = 15;
  299. }
  300. }
  301. return $directArr[$index];
  302. }
  303. private function getWindSpeed($speed)
  304. {
  305. $wind = [
  306. 'name' => '无风',
  307. 'level' => 0
  308. ];
  309. if ($speed > 1 && $speed <= 5) {
  310. $wind = [
  311. 'name' => '软风',
  312. 'level' => 1
  313. ];
  314. }
  315. if ($speed > 5 && $speed <= 11) {
  316. $wind = [
  317. 'name' => '轻风',
  318. 'level' => 2
  319. ];
  320. }
  321. if ($speed > 11 && $speed <= 19) {
  322. $wind = [
  323. 'name' => '微风',
  324. 'level' => 3
  325. ];
  326. }
  327. if ($speed > 19 && $speed <= 28) {
  328. $wind = [
  329. 'name' => '和风',
  330. 'level' => 4
  331. ];
  332. }
  333. if ($speed > 28 && $speed <= 38) {
  334. $wind = [
  335. 'name' => '清风',
  336. 'level' => 5
  337. ];
  338. }
  339. if ($speed > 38 && $speed <= 49) {
  340. $wind = [
  341. 'name' => '强风',
  342. 'level' => 6
  343. ];
  344. }
  345. if ($speed > 49 && $speed <= 61) {
  346. $wind = [
  347. 'name' => '劲风',
  348. 'level' => 7
  349. ];
  350. }
  351. if ($speed > 61 && $speed <= 74) {
  352. $wind = [
  353. 'name' => '大风',
  354. 'level' => 8
  355. ];
  356. }
  357. if ($speed > 74 && $speed <= 88) {
  358. $wind = [
  359. 'name' => '烈风',
  360. 'level' => 9
  361. ];
  362. }
  363. if ($speed > 88 && $speed <= 102) {
  364. $wind = [
  365. 'name' => '狂风',
  366. 'level' => 10
  367. ];
  368. }
  369. if ($speed > 102 && $speed <= 117) {
  370. $wind = [
  371. 'name' => '暴风',
  372. 'level' => 11
  373. ];
  374. }
  375. if ($speed > 117 && $speed <= 134) {
  376. $wind = [
  377. 'name' => '台风',
  378. 'level' => 12
  379. ];
  380. }
  381. if ($speed > 134 && $speed <= 149) {
  382. $wind = [
  383. 'name' => '台风',
  384. 'level' => 13
  385. ];
  386. }
  387. if ($speed > 149 && $speed <= 166) {
  388. $wind = [
  389. 'name' => '台风',
  390. 'level' => 14
  391. ];
  392. }
  393. if ($speed > 166 && $speed <= 183) {
  394. $wind = [
  395. 'name' => '台风',
  396. 'level' => 15
  397. ];
  398. }
  399. if ($speed > 183 && $speed <= 201) {
  400. $wind = [
  401. 'name' => '台风',
  402. 'level' => 16
  403. ];
  404. }
  405. if ($speed > 201 && $speed <= 220) {
  406. $wind = [
  407. 'name' => '台风',
  408. 'level' => 17
  409. ];
  410. }
  411. if ($speed > 220) {
  412. $wind = [
  413. 'name' => '台风',
  414. 'level' => 18
  415. ];
  416. }
  417. return $wind;
  418. }
  419. private function getSkycon($skycon)
  420. {
  421. $arr = [
  422. 'CLEAR_DAY' => '晴',
  423. 'CLEAR_NIGHT' => '晴',
  424. 'PARTLY_CLOUDY_DAY' => '多云',
  425. 'PARTLY_CLOUDY_NIGHT' => '多云',
  426. 'CLOUDY' => '阴',
  427. 'LIGHT_HAZE' => '轻度雾霾',
  428. 'MODERATE_HAZE' => '中度雾霾',
  429. 'HEAVY_HAZE' => '重度雾霾',
  430. 'LIGHT_RAIN' => '小雨',
  431. 'MODERATE_RAIN' => '中雨',
  432. 'HEAVY_RAIN' => '大雨',
  433. 'STORM_RAIN' => '暴雨',
  434. 'FOG' => '雾',
  435. 'LIGHT_SNOW' => '小雪',
  436. 'MODERATE_SNOW' => '中雪',
  437. 'HEAVY_SNOW' => '大雪',
  438. 'STORM_SNOW' => '暴雪',
  439. 'DUST' => '浮尘',
  440. 'SAND' => '沙尘',
  441. 'WIND' => '大风',
  442. 'SLEET' => '雨夹雪',
  443. 'THUNDER_SHOWER' => '雷阵雨',
  444. 'HAIL' => '冰雹',
  445. ];
  446. return $arr[$skycon] ?? "";
  447. }
  448. private function getAlert($code, $type = 1)
  449. {
  450. $alert_type = [
  451. '01' => '台风',
  452. '02' => '暴雨',
  453. '03' => '暴雪',
  454. '04' => '寒潮',
  455. '05' => '⼤风',
  456. '06' => '沙尘暴',
  457. '07' => '高温',
  458. '08' => '干旱',
  459. '09' => '雷电',
  460. '10' => '冰雹',
  461. '11' => '霜冻',
  462. '12' => '大雾',
  463. '13' => '霾',
  464. '14' => '道路结冰',
  465. '15' => '森林火灾',
  466. '16' => '雷雨大风',
  467. ];
  468. $alert_level = [
  469. '01' => '蓝色',
  470. '02' => '黄色',
  471. '03' => '橙色',
  472. '04' => '红色',
  473. ];
  474. $code_1 = substr($code, 0, 2);
  475. $code_2 = substr($code, 2, 2);
  476. if ($type == 1) {
  477. return $code ? $alert_type[$code_1] . $alert_level[$code_2] . '预警' : "";
  478. } else {
  479. return $alert_level[$code_2] ?? "";
  480. }
  481. }
  482. }