加入使用代理功能,修改json格式!

This commit is contained in:
Kane Wang 2022-11-04 16:11:18 +08:00
parent 628b08e1ef
commit c988bf4c91
8 changed files with 335 additions and 221 deletions

View File

@ -3,14 +3,14 @@
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>
<groupId>com.cpic.xim</groupId> <groupId>com.cpic.xim</groupId>
<artifactId>disaster_warning</artifactId> <artifactId>disaster_warning</artifactId>
<version>1.0-SNAPSHOT</version> <version>1.1</version>
<dependencies> <dependencies>
<dependency> <dependency>
<groupId>com.oracle</groupId> <groupId>com.oracle</groupId>
<artifactId>ojdbc8</artifactId> <artifactId>ojdbc8</artifactId>
<version>19.3.0.0.0</version> <version>19.3.0.0.0</version>
<scope>system</scope> <!-- <scope>system</scope>
<systemPath>${project.basedir}/lib/ojdbc8.jar</systemPath> <systemPath>${project.basedir}/lib/ojdbc8.jar</systemPath> -->
</dependency> </dependency>
<dependency> <dependency>
<groupId>org.apache.httpcomponents</groupId> <groupId>org.apache.httpcomponents</groupId>

View File

@ -22,30 +22,38 @@ import java.sql.SQLException;
import java.util.Vector; import java.util.Vector;
import java.util.logging.*; import java.util.logging.*;
public class AppMain { public class AppMain
{
// private final static String LOG_FILE_PATH = "./logs/app%u.log"; // private final static String LOG_FILE_PATH = "./logs/app%u.log";
public static void main(String[] args) { public static void main( String[] args )
{
String json; String json;
WeatherDisasterNotifyConfig config = null; WeatherDisasterNotifyConfig config = null;
QWeatherDisasterWarning warning = null; QWeatherDisasterWarning warning = null;
Logger logger = null; Logger logger = null;
// 配置logger // 配置logger
try { try
{
setRootLogger(); setRootLogger();
logger = Logger.getLogger( "com.cpicxim" ); logger = Logger.getLogger( "com.cpicxim" );
} catch (IOException error) { }
catch ( IOException error )
{
System.out.println( "配置logger失败原因" + error.getMessage() ); System.out.println( "配置logger失败原因" + error.getMessage() );
return; return;
} }
// 读取配置 // 读取配置
try { try
{
config = AppConfigManager.getConfig(); config = AppConfigManager.getConfig();
} catch (IOException error) { }
catch ( IOException error )
{
System.out.println( "读取配置文件失败!" ); System.out.println( "读取配置文件失败!" );
System.out.println( error.getMessage() ); System.out.println( error.getMessage() );
@ -59,68 +67,90 @@ public class AppMain {
String userKey = config.getKey(); String userKey = config.getKey();
// 遍历所有城市查询是否有警报有则推送 // 遍历所有城市查询是否有警报有则推送
while (true) { while (true)
for (City city : cities) { {
try { for ( City city : cities )
{
try
{
json = WeatherDisasterWarningGrabber.getWeatherDisasterWarningJSON( queryURL, json = WeatherDisasterWarningGrabber.getWeatherDisasterWarningJSON( queryURL,
userKey, city.getCityCode(), false); userKey, city.getCityCode(), config.getProxySetting() );
warning = WeatherDisasterWarningGrabber.convertWeatherDisasterWarning( json ); warning = WeatherDisasterWarningGrabber.convertWeatherDisasterWarning( json );
logger.log(Level.INFO, "查询{0}天气预警,结果:{1}。", new Object[] { city.getCityName(), json }); logger.log( Level.INFO, "查询{0}天气预警,结果:{1}。", new Object[]
{ city.getCityName(), json} );
Vector<QWeatherDisasterWarningItem> warningItems = warning.getWarning(); Vector<QWeatherDisasterWarningItem> warningItems = warning.getWarning();
// 判断是否有警报没有警报就结束当前城市的处理 // 判断是否有警报没有警报就结束当前城市的处理
if (warningItems.isEmpty() == true) { if ( warningItems.isEmpty() == true)
logger.log(Level.INFO, "查询{0}天气预警,无警报!。", new Object[] { city.getCityName() }); {
logger.log( Level.INFO, "查询{0}天气预警,无警报!。", new Object[]
{ city.getCityName()} );
continue; continue;
} }
// 有警报就遍历警报数组 // 有警报就遍历警报数组
for (QWeatherDisasterWarningItem item : warningItems) { for ( QWeatherDisasterWarningItem item : warningItems )
{
// 先检查是否已经发送过 // 先检查是否已经发送过
if (sendMessage.checkWarningHasSended(item.getId()) == false) { if ( sendMessage.checkWarningHasSended( item.getId() ) == false)
logger.log(Level.INFO, "查询{0}天气预警ID:{1},已有发送记录,跳过。", {
new Object[] { city.getCityName(), item.getId() }); logger.log( Level.INFO, "查询{0}天气预警ID:{1},已有发送记录,跳过。", new Object[]
{ city.getCityName(), item.getId()} );
continue; continue;
} }
// 没有发送过 // 没有发送过
logger.log(Level.INFO, "查询{0}天气预警,发送日志。", new Object[] { city.getCityName() }); logger.log( Level.INFO, "查询{0}天气预警,发送日志。", new Object[]
{ city.getCityName()} );
sendMessage.sendWeatherDisasterWarning( config.getWechatOfficalAccountURL(), sendMessage.sendWeatherDisasterWarning( config.getWechatOfficalAccountURL(),
item ); item );
logger.log(Level.INFO, "{0}天气预警,日志发送成功。", new Object[] { city.getCityName() }); logger.log( Level.INFO, "{0}天气预警,日志发送成功。", new Object[]
{ city.getCityName()} );
// 将发送的警报保存起来 // 将发送的警报保存起来
sendMessage.saveWeatherDisasterWarning( city.getCityName(), item ); sendMessage.saveWeatherDisasterWarning( city.getCityName(), item );
} }
} catch (IOException error) { }
catch ( IOException error )
{
System.out.println( "查询" + city.getCityName() + "出现异常!" ); System.out.println( "查询" + city.getCityName() + "出现异常!" );
System.out.println( error.getMessage() ); System.out.println( error.getMessage() );
logger.log(Level.SEVERE, "查询 {0} 出现异常:{1}。", logger.log( Level.SEVERE, "查询 {0} 出现异常:{1}。", new Object[]
new Object[] { city.getCityName(), error.getMessage() }); { city.getCityName(), error.getMessage()} );
} catch (SQLException error) { }
logger.log(Level.SEVERE, "查询 {0} 写入数据库失败:{1}。", catch ( SQLException error )
new Object[] { city.getCityName(), error.getMessage() }); {
} catch (ClassNotFoundException error) { logger.log( Level.SEVERE, "查询 {0} 写入数据库失败:{1}。", new Object[]
logger.log(Level.SEVERE, "查询 {0} 加载oracle驱动失败:{1}。", { city.getCityName(), error.getMessage()} );
new Object[] { city.getCityName(), error.getMessage() }); }
catch ( ClassNotFoundException error )
{
logger.log( Level.SEVERE, "查询 {0} 加载oracle驱动失败:{1}。", new Object[]
{ city.getCityName(), error.getMessage()} );
} catch (Exception error) { }
logger.log(Level.SEVERE, "查询 {0} 出现未知错误:{1}。", catch ( Exception error )
new Object[] { city.getCityName(), error.getMessage() }); {
logger.log( Level.SEVERE, "查询 {0} 出现未知错误:{1}。", new Object[]
{ city.getCityName(), error.getMessage()} );
} }
} }
try { try
{
logger.log( Level.INFO, "查询结束,休眠{0}分钟。", config.getQueryInterval() ); logger.log( Level.INFO, "查询结束,休眠{0}分钟。", config.getQueryInterval() );
Thread.sleep( config.getQueryInterval() * 1000 * 60 ); Thread.sleep( config.getQueryInterval() * 1000 * 60 );
} catch (InterruptedException error) { }
logger.log(Level.SEVERE, "线程休眠异常,错误信息:{0}", new Object[] { error.getMessage() }); catch ( InterruptedException error )
{
logger.log( Level.SEVERE, "线程休眠异常,错误信息:{0}", new Object[]
{ error.getMessage()} );
} }
} }
} }
@ -128,7 +158,8 @@ public class AppMain {
/** /**
* 设置JUL的logger * 设置JUL的logger
*/ */
private static void setRootLogger() throws IOException { private static void setRootLogger() throws IOException
{
LogManager logManager = LogManager.getLogManager(); LogManager logManager = LogManager.getLogManager();
// 使用外部的配置文件 // 使用外部的配置文件
FileInputStream configFile = new FileInputStream( "./logging.properties" ); FileInputStream configFile = new FileInputStream( "./logging.properties" );

View File

@ -8,7 +8,7 @@
* *
* Copyright (c) ${2022} by Kane, All Rights Reserved. * Copyright (c) ${2022} by Kane, All Rights Reserved.
*/ */
package com.cpic.xim.config.db; package com.cpic.xim.config;
import com.fasterxml.jackson.annotation.*;; import com.fasterxml.jackson.annotation.*;;
@ -40,24 +40,24 @@ public class ProxySetting
this.proxyMode = proxyMode; this.proxyMode = proxyMode;
} }
public String getProxy_address() public String getProxyAddress()
{ {
return proxy_address; return proxyAddress;
} }
public void setProxy_address( String proxy_address ) public void setProxyAddress( String proxy_address )
{ {
this.proxy_address = proxy_address; this.proxyAddress = proxy_address;
} }
public int getProxy_port() public int getProxyPort()
{ {
return proxy_port; return proxyPort;
} }
public void setProxy_port( int proxy_port ) public void setProxyPort( int proxy_port )
{ {
this.proxy_port = proxy_port; this.proxyPort = proxy_port;
} }
@Override @Override
@ -67,8 +67,8 @@ public class ProxySetting
int result = 1; int result = 1;
result = prime * result + (enable ? 1231 : 1237); result = prime * result + (enable ? 1231 : 1237);
result = prime * result + ((proxyMode == null) ? 0 : proxyMode.hashCode()); result = prime * result + ((proxyMode == null) ? 0 : proxyMode.hashCode());
result = prime * result + ((proxy_address == null) ? 0 : proxy_address.hashCode()); result = prime * result + ((proxyAddress == null) ? 0 : proxyAddress.hashCode());
result = prime * result + proxy_port; result = prime * result + proxyPort;
return result; return result;
} }
@ -90,13 +90,13 @@ public class ProxySetting
return false; return false;
} else if ( !proxyMode.equals( other.proxyMode )) } else if ( !proxyMode.equals( other.proxyMode ))
return false; return false;
if ( proxy_address == null) if ( proxyAddress == null)
{ {
if ( other.proxy_address != null) if ( other.proxyAddress != null)
return false; return false;
} else if ( !proxy_address.equals( other.proxy_address )) } else if ( !proxyAddress.equals( other.proxyAddress ))
return false; return false;
if ( proxy_port != other.proxy_port) if ( proxyPort != other.proxyPort)
return false; return false;
return true; return true;
} }
@ -108,9 +108,9 @@ public class ProxySetting
private String proxyMode = "http"; private String proxyMode = "http";
@JsonProperty( "proxy_address") @JsonProperty( "proxy_address")
private String proxy_address; private String proxyAddress;
@JsonProperty( "proxy_port") @JsonProperty( "proxy_port")
private int proxy_port; private int proxyPort;
} }

View File

@ -11,7 +11,6 @@
package com.cpic.xim.config; package com.cpic.xim.config;
import java.util.Vector; import java.util.Vector;
import com.cpic.xim.config.db.ProxySetting;
import com.fasterxml.jackson.annotation.JsonProperty; import com.fasterxml.jackson.annotation.JsonProperty;

View File

@ -11,145 +11,6 @@ public class QWeatherDisasterWarningItem
public QWeatherDisasterWarningItem() public QWeatherDisasterWarningItem()
{} {}
public QWeatherDisasterWarningItem( String id, String sender, Date pubTime, String title,
String status, String level, String severity, String severityColor, String type,
String typeName, String text, String related, String urgency, String certainty)
{
this.id = id;
this.sender = sender;
this.pubTime = pubTime;
this.title = title;
this.status = status;
this.level = level;
this.severity = severity;
this.severityColor = severityColor;
this.type = type;
this.typeName = typeName;
this.text = text;
this.related = related;
this.urgency = urgency;
this.certainty = certainty;
}
@Override
public int hashCode()
{
final int prime = 31;
int result = 1;
result = prime * result + ((certainty == null) ? 0 : certainty.hashCode());
result = prime * result + ((id == null) ? 0 : id.hashCode());
result = prime * result + ((level == null) ? 0 : level.hashCode());
result = prime * result + ((pubTime == null) ? 0 : pubTime.hashCode());
result = prime * result + ((related == null) ? 0 : related.hashCode());
result = prime * result + ((sender == null) ? 0 : sender.hashCode());
result = prime * result + ((severity == null) ? 0 : severity.hashCode());
result = prime * result + ((severityColor == null) ? 0 : severityColor.hashCode());
result = prime * result + ((status == null) ? 0 : status.hashCode());
result = prime * result + ((text == null) ? 0 : text.hashCode());
result = prime * result + ((title == null) ? 0 : title.hashCode());
result = prime * result + ((type == null) ? 0 : type.hashCode());
result = prime * result + ((typeName == null) ? 0 : typeName.hashCode());
result = prime * result + ((urgency == null) ? 0 : urgency.hashCode());
return result;
}
@Override
public boolean equals( Object obj )
{
if ( this == obj)
return true;
if ( obj == null)
return false;
if ( getClass() != obj.getClass())
return false;
QWeatherDisasterWarningItem other = (QWeatherDisasterWarningItem) obj;
if ( certainty == null)
{
if ( other.certainty != null)
return false;
} else if ( !certainty.equals( other.certainty ))
return false;
if ( id == null)
{
if ( other.id != null)
return false;
} else if ( !id.equals( other.id ))
return false;
if ( level == null)
{
if ( other.level != null)
return false;
} else if ( !level.equals( other.level ))
return false;
if ( pubTime == null)
{
if ( other.pubTime != null)
return false;
} else if ( !pubTime.equals( other.pubTime ))
return false;
if ( related == null)
{
if ( other.related != null)
return false;
} else if ( !related.equals( other.related ))
return false;
if ( sender == null)
{
if ( other.sender != null)
return false;
} else if ( !sender.equals( other.sender ))
return false;
if ( severity == null)
{
if ( other.severity != null)
return false;
} else if ( !severity.equals( other.severity ))
return false;
if ( severityColor == null)
{
if ( other.severityColor != null)
return false;
} else if ( !severityColor.equals( other.severityColor ))
return false;
if ( status == null)
{
if ( other.status != null)
return false;
} else if ( !status.equals( other.status ))
return false;
if ( text == null)
{
if ( other.text != null)
return false;
} else if ( !text.equals( other.text ))
return false;
if ( title == null)
{
if ( other.title != null)
return false;
} else if ( !title.equals( other.title ))
return false;
if ( type == null)
{
if ( other.type != null)
return false;
} else if ( !type.equals( other.type ))
return false;
if ( typeName == null)
{
if ( other.typeName != null)
return false;
} else if ( !typeName.equals( other.typeName ))
return false;
if ( urgency == null)
{
if ( other.urgency != null)
return false;
} else if ( !urgency.equals( other.urgency ))
return false;
return true;
}
public String getId() public String getId()
{ {
return id; return id;
@ -190,6 +51,26 @@ public class QWeatherDisasterWarningItem
this.title = title; this.title = title;
} }
public Date getStartTime()
{
return startTime;
}
public void setStartTime( Date startTime )
{
this.startTime = startTime;
}
public Date getEndTime()
{
return endTime;
}
public void setEndTime( Date endTime )
{
this.endTime = endTime;
}
public String getStatus() public String getStatus()
{ {
return status; return status;
@ -290,23 +171,187 @@ public class QWeatherDisasterWarningItem
this.certainty = certainty; this.certainty = certainty;
} }
@Override
public int hashCode()
{
final int prime = 31;
int result = 1;
result = prime * result + ((id == null) ? 0 : id.hashCode());
result = prime * result + ((sender == null) ? 0 : sender.hashCode());
result = prime * result + ((pubTime == null) ? 0 : pubTime.hashCode());
result = prime * result + ((title == null) ? 0 : title.hashCode());
result = prime * result + ((startTime == null) ? 0 : startTime.hashCode());
result = prime * result + ((endTime == null) ? 0 : endTime.hashCode());
result = prime * result + ((status == null) ? 0 : status.hashCode());
result = prime * result + ((level == null) ? 0 : level.hashCode());
result = prime * result + ((severity == null) ? 0 : severity.hashCode());
result = prime * result + ((severityColor == null) ? 0 : severityColor.hashCode());
result = prime * result + ((type == null) ? 0 : type.hashCode());
result = prime * result + ((typeName == null) ? 0 : typeName.hashCode());
result = prime * result + ((text == null) ? 0 : text.hashCode());
result = prime * result + ((related == null) ? 0 : related.hashCode());
result = prime * result + ((urgency == null) ? 0 : urgency.hashCode());
result = prime * result + ((certainty == null) ? 0 : certainty.hashCode());
return result;
}
@Override
public boolean equals( Object obj )
{
if ( this == obj)
return true;
if ( obj == null)
return false;
if ( getClass() != obj.getClass())
return false;
QWeatherDisasterWarningItem other = (QWeatherDisasterWarningItem) obj;
if ( id == null)
{
if ( other.id != null)
return false;
} else if ( !id.equals( other.id ))
return false;
if ( sender == null)
{
if ( other.sender != null)
return false;
} else if ( !sender.equals( other.sender ))
return false;
if ( pubTime == null)
{
if ( other.pubTime != null)
return false;
} else if ( !pubTime.equals( other.pubTime ))
return false;
if ( title == null)
{
if ( other.title != null)
return false;
} else if ( !title.equals( other.title ))
return false;
if ( startTime == null)
{
if ( other.startTime != null)
return false;
} else if ( !startTime.equals( other.startTime ))
return false;
if ( endTime == null)
{
if ( other.endTime != null)
return false;
} else if ( !endTime.equals( other.endTime ))
return false;
if ( status == null)
{
if ( other.status != null)
return false;
} else if ( !status.equals( other.status ))
return false;
if ( level == null)
{
if ( other.level != null)
return false;
} else if ( !level.equals( other.level ))
return false;
if ( severity == null)
{
if ( other.severity != null)
return false;
} else if ( !severity.equals( other.severity ))
return false;
if ( severityColor == null)
{
if ( other.severityColor != null)
return false;
} else if ( !severityColor.equals( other.severityColor ))
return false;
if ( type == null)
{
if ( other.type != null)
return false;
} else if ( !type.equals( other.type ))
return false;
if ( typeName == null)
{
if ( other.typeName != null)
return false;
} else if ( !typeName.equals( other.typeName ))
return false;
if ( text == null)
{
if ( other.text != null)
return false;
} else if ( !text.equals( other.text ))
return false;
if ( related == null)
{
if ( other.related != null)
return false;
} else if ( !related.equals( other.related ))
return false;
if ( urgency == null)
{
if ( other.urgency != null)
return false;
} else if ( !urgency.equals( other.urgency ))
return false;
if ( certainty == null)
{
if ( other.certainty != null)
return false;
} else if ( !certainty.equals( other.certainty ))
return false;
return true;
}
@JsonProperty( "id")
private String id; private String id;
@JsonProperty( "sender")
private String sender; private String sender;
@JsonProperty( "pubTime") @JsonProperty( "pubTime")
@JsonFormat( shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd\'T\'HH:mmXXX") @JsonFormat( shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd\'T\'HH:mmXXX")
private Date pubTime; private Date pubTime;
@JsonProperty( "title")
private String title; private String title;
@JsonProperty( "startTime")
@JsonFormat( shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd\'T\'HH:mmXXX")
private Date startTime;
@JsonProperty( "endTime")
@JsonFormat( shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd\'T\'HH:mmXXX")
private Date endTime;
@JsonProperty( "status")
private String status; private String status;
@JsonProperty( "level")
private String level; private String level;
@JsonProperty( "severity")
private String severity; private String severity;
@JsonProperty( "severityColor")
private String severityColor; private String severityColor;
@JsonProperty( "type")
private String type; private String type;
@JsonProperty( "typeName")
private String typeName; private String typeName;
@JsonProperty( "text")
private String text; private String text;
@JsonProperty( "related")
private String related; private String related;
@JsonProperty( "urgency")
private String urgency; private String urgency;
@JsonProperty( "certainty")
private String certainty; private String certainty;
} }

View File

@ -4,17 +4,18 @@
*/ */
package com.cpic.xim.notify.disaster; package com.cpic.xim.notify.disaster;
import com.fasterxml.jackson.databind.ObjectMapper; import java.io.BufferedReader;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.io.InputStreamReader; import java.io.InputStreamReader;
import java.io.BufferedReader;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.HttpURLConnection; import java.net.HttpURLConnection;
import java.net.InetSocketAddress;
import java.net.MalformedURLException;
import java.net.Proxy;
import java.net.URL;
import java.util.zip.GZIPInputStream; import java.util.zip.GZIPInputStream;
import com.cpic.xim.config.ProxySetting;
import com.fasterxml.jackson.databind.ObjectMapper;
/** /**
* *
@ -28,7 +29,7 @@ public class WeatherDisasterWarningGrabber
* @return 返回警报的json字符串 * @return 返回警报的json字符串
*/ */
public static String getWeatherDisasterWarningJSON( String queryURL, String userKey, public static String getWeatherDisasterWarningJSON( String queryURL, String userKey,
String cityCode, boolean useProxy ) String cityCode, ProxySetting proxySetting )
{ {
// 拼接url字符串 // 拼接url字符串
String json = ""; String json = "";
@ -44,7 +45,19 @@ public class WeatherDisasterWarningGrabber
try try
{ {
url = new URL( requestURL ); url = new URL( requestURL );
// 是否要用代理服务器
if ( proxySetting.isEnable())
{
InetSocketAddress proxyAddress = new InetSocketAddress(
proxySetting.getProxyAddress(), proxySetting.getProxyPort() );
Proxy proxy = new Proxy( Proxy.Type.HTTP, proxyAddress );
connection = (HttpURLConnection) url.openConnection( proxy );
} else
{
connection = (HttpURLConnection) url.openConnection(); connection = (HttpURLConnection) url.openConnection();
}
connection.setRequestMethod( "GET" ); connection.setRequestMethod( "GET" );
connection.setConnectTimeout( 15000 ); connection.setConnectTimeout( 15000 );

View File

@ -2,7 +2,7 @@
* @Author: Kane * @Author: Kane
* @Date: 2022-04-22 10:53:49 * @Date: 2022-04-22 10:53:49
* @LastEditors: Kane * @LastEditors: Kane
* @LastEditTime: 2022-05-12 09:54:36 * @LastEditTime: 2022-11-04 15:40:37
* @FilePath: \DisasterWarning\src\test\java\com\cpic\xim\wechat\officalAccount\sendMessageTest.java * @FilePath: \DisasterWarning\src\test\java\com\cpic\xim\wechat\officalAccount\sendMessageTest.java
* @Description: * @Description:
* *

View File

@ -0,0 +1,26 @@
{
"code": "200",
"updateTime": "2022-11-04T15:18+08:00",
"fxLink": "http://hfx.link/3565",
"warning": [
{
"id": "10123020120221104060100024343791",
"sender": "厦门市气象台",
"pubTime": "2022-11-04T06:01+08:00",
"title": "厦门市气象台发布大风黄色预警[Ⅲ级/较重]",
"startTime": "2022-11-04T06:09+08:00",
"endTime": "2022-11-05T06:09+08:00",
"status": "active",
"level": "黄色",
"severity": "Moderate",
"severityColor": "Yellow",
"type": "1006",
"typeName": "大风",
"urgency": "",
"certainty": "",
"text": "厦门市气象台2022年11月04日06时01分发布大风黄色预警信号受冷空气影响未来12小时思明区、湖里区、集美区、海沧区及海上风力逐渐增强预计厦门市区东北风最大可达56级、阵风78级厦门内海及各大桥、高海拔山区最大可达67级、阵风89级崇武到东山沿海最大可达78级、阵风9级台湾海峡南部最大可达78级、阵风910级。请注意防范。同安区、翔安区请关注属地的预警信号发布情况。",
"related": ""
}
],
"refer": { "sources": ["12379"], "license": ["no commercial use"] }
}