Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
G
gf_back
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Analytics
Analytics
CI / CD
Repository
Value Stream
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
tianchao
gf_back
Commits
b1d7acda
Commit
b1d7acda
authored
Aug 24, 2023
by
tianchao
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
1增加天气预期
parent
3e727e71
Changes
15
Hide whitespace changes
Inline
Side-by-side
Showing
15 changed files
with
893 additions
and
4 deletions
+893
-4
C09-base/pps-cloud-base/src/main/java/pps/cloud/base/service/BaseWatherCloudService.java
...n/java/pps/cloud/base/service/BaseWatherCloudService.java
+16
-0
C09-base/pps-cloud-base/src/main/java/pps/cloud/base/service/data/CrawlBaseWatherInput.java
...ava/pps/cloud/base/service/data/CrawlBaseWatherInput.java
+43
-0
C09-base/pps-core-base/pom.xml
C09-base/pps-core-base/pom.xml
+7
-0
C09-base/pps-core-base/src/main/java/pps/core/base/entity/BaseWatherEnt.java
...ase/src/main/java/pps/core/base/entity/BaseWatherEnt.java
+195
-0
C09-base/pps-core-base/src/main/java/pps/core/base/mapper/WeatherDataMapper.java
...src/main/java/pps/core/base/mapper/WeatherDataMapper.java
+9
-0
C09-base/pps-core-base/src/main/java/pps/core/base/service/BaseWatherCloudServiceImpl.java
...ava/pps/core/base/service/BaseWatherCloudServiceImpl.java
+281
-0
C09-base/pps-core-base/src/main/java/pps/core/base/service/BaseWatherService.java
...rc/main/java/pps/core/base/service/BaseWatherService.java
+279
-0
C09-base/pps-core-base/src/main/java/pps/core/base/service/data/base_wather/GetBaseWatherInput.java
...ore/base/service/data/base_wather/GetBaseWatherInput.java
+13
-0
C10-task/pps-core-task/pom.xml
C10-task/pps-core-task/pom.xml
+6
-0
C10-task/pps-core-task/src/main/java/pps/core/task/job/TestJob.java
...ps-core-task/src/main/java/pps/core/task/job/TestJob.java
+1
-1
C10-task/pps-core-task/src/main/java/pps/core/task/job/WeatherJob.java
...core-task/src/main/java/pps/core/task/job/WeatherJob.java
+36
-0
D03-deploy/deploy-pps-all/src/main/java/app/DeployPpsAllApplication.java
...oy-pps-all/src/main/java/app/DeployPpsAllApplication.java
+2
-0
D03-deploy/deploy-pps-task/src/main/java/app/DeployPpsTaskApplication.java
...-pps-task/src/main/java/app/DeployPpsTaskApplication.java
+1
-0
D03-deploy/deploy-pps-task/src/main/resources/deploy-pps-task.app.properties
...ps-task/src/main/resources/deploy-pps-task.app.properties
+2
-1
D03-deploy/filters/filter-dev.properties
D03-deploy/filters/filter-dev.properties
+2
-2
No files found.
C09-base/pps-cloud-base/src/main/java/pps/cloud/base/service/BaseWatherCloudService.java
0 → 100644
View file @
b1d7acda
package
pps.cloud.base.service
;
import
pps.cloud.base.service.data.*
;
import
xstartup.annotation.XService
;
import
xstartup.annotation.XText
;
import
xstartup.base.XContext
;
import
xstartup.data.XServiceResult
;
@XService
public
interface
BaseWatherCloudService
{
@XText
(
"获取天气信息"
)
XServiceResult
crawlBaseWatherData
(
XContext
context
);
@XText
(
"获取天气信息"
)
XServiceResult
crawlBaseWatherDataByCityCode
(
XContext
context
,
CrawlBaseWatherInput
input
);
}
C09-base/pps-cloud-base/src/main/java/pps/cloud/base/service/data/CrawlBaseWatherInput.java
0 → 100644
View file @
b1d7acda
package
pps.cloud.base.service.data
;
public
class
CrawlBaseWatherInput
{
private
String
provinceCode
;
private
String
provinceName
;
private
String
cityCode
;
private
String
cityName
;
public
String
getProvinceCode
()
{
return
provinceCode
;
}
public
void
setProvinceCode
(
String
provinceCode
)
{
this
.
provinceCode
=
provinceCode
;
}
public
String
getProvinceName
()
{
return
provinceName
;
}
public
void
setProvinceName
(
String
provinceName
)
{
this
.
provinceName
=
provinceName
;
}
public
String
getCityCode
()
{
return
cityCode
;
}
public
void
setCityCode
(
String
cityCode
)
{
this
.
cityCode
=
cityCode
;
}
public
String
getCityName
()
{
return
cityName
;
}
public
void
setCityName
(
String
cityName
)
{
this
.
cityName
=
cityName
;
}
}
C09-base/pps-core-base/pom.xml
View file @
b1d7acda
...
...
@@ -45,6 +45,13 @@
<version>
1.0.0-pps
</version>
<scope>
compile
</scope>
</dependency>
<dependency>
<groupId>
org.jsoup
</groupId>
<artifactId>
jsoup
</artifactId>
<version>
1.10.2
</version>
</dependency>
</dependencies>
<build>
<plugins>
...
...
C09-base/pps-core-base/src/main/java/pps/core/base/entity/BaseWatherEnt.java
0 → 100644
View file @
b1d7acda
package
pps.core.base.entity
;
import
com.baomidou.mybatisplus.annotation.IdType
;
import
com.baomidou.mybatisplus.annotation.TableField
;
import
com.baomidou.mybatisplus.annotation.TableId
;
import
com.baomidou.mybatisplus.annotation.TableName
;
import
xstartup.annotation.XText
;
import
java.io.Serializable
;
@TableName
(
"base_wather"
)
public
class
BaseWatherEnt
implements
Serializable
{
@XText
(
"主键"
)
@TableId
(
type
=
IdType
.
AUTO
)
private
Integer
id
;
@XText
(
"省份描述"
)
@TableField
private
String
provinceName
;
@XText
(
"省份id"
)
@TableField
private
String
provinceCode
;
@XText
(
"城市描述(包含区县)"
)
@TableField
private
String
cityName
;
@XText
(
"城市id(包含区县)"
)
@TableField
private
String
cityCode
;
@XText
(
"日期-到小时"
)
@TableField
private
String
dataDate
;
@XText
(
"日期天"
)
@TableField
(
exist
=
false
)
private
String
dayTime
;
@XText
(
"星期"
)
@TableField
(
exist
=
false
)
private
String
weekTime
;
@XText
(
"时间:小时"
)
@TableField
(
exist
=
false
)
private
String
hourTime
;
@XText
(
"天气"
)
@TableField
private
String
weather
;
@XText
(
"气温"
)
@TableField
private
String
temperature
;
@XText
(
"降水"
)
@TableField
private
String
precipitation
;
@XText
(
"风速"
)
@TableField
private
String
windSpeed
;
@XText
(
"风向"
)
@TableField
private
String
windDirection
;
@XText
(
"气压"
)
@TableField
private
String
pressure
;
@XText
(
"湿度"
)
@TableField
private
String
humidity
;
@XText
(
"云量"
)
@TableField
private
String
cloudCover
;
public
Integer
getId
()
{
return
id
;
}
public
void
setId
(
Integer
id
)
{
this
.
id
=
id
;
}
public
String
getProvinceName
()
{
return
provinceName
;
}
public
void
setProvinceName
(
String
provinceName
)
{
this
.
provinceName
=
provinceName
;
}
public
String
getProvinceCode
()
{
return
provinceCode
;
}
public
void
setProvinceCode
(
String
provinceCode
)
{
this
.
provinceCode
=
provinceCode
;
}
public
String
getCityName
()
{
return
cityName
;
}
public
void
setCityName
(
String
cityName
)
{
this
.
cityName
=
cityName
;
}
public
String
getCityCode
()
{
return
cityCode
;
}
public
void
setCityCode
(
String
cityCode
)
{
this
.
cityCode
=
cityCode
;
}
public
String
getDayTime
()
{
return
dayTime
;
}
public
void
setDayTime
(
String
dayTime
)
{
this
.
dayTime
=
dayTime
;
}
public
String
getHourTime
()
{
return
hourTime
;
}
public
String
getWeekTime
()
{
return
weekTime
;
}
public
void
setWeekTime
(
String
weekTime
)
{
this
.
weekTime
=
weekTime
;
}
public
void
setHourTime
(
String
hourTime
)
{
this
.
hourTime
=
hourTime
;
}
public
String
getWeather
()
{
return
weather
;
}
public
void
setWeather
(
String
weather
)
{
this
.
weather
=
weather
;
}
public
String
getTemperature
()
{
return
temperature
.
replace
(
"℃"
,
""
);
}
public
void
setTemperature
(
String
temperature
)
{
this
.
temperature
=
temperature
;
}
public
String
getPrecipitation
()
{
return
precipitation
;
}
public
void
setPrecipitation
(
String
precipitation
)
{
this
.
precipitation
=
precipitation
;
}
public
String
getWindSpeed
()
{
return
windSpeed
.
replace
(
"m/s"
,
""
);
}
public
void
setWindSpeed
(
String
windSpeed
)
{
this
.
windSpeed
=
windSpeed
;
}
public
String
getWindDirection
()
{
return
windDirection
;
}
public
void
setWindDirection
(
String
windDirection
)
{
this
.
windDirection
=
windDirection
;
}
public
String
getPressure
()
{
return
pressure
.
replace
(
"hPa"
,
""
);
}
public
void
setPressure
(
String
pressure
)
{
this
.
pressure
=
pressure
;
}
public
String
getHumidity
()
{
return
humidity
.
replace
(
"%"
,
""
);
}
public
void
setHumidity
(
String
humidity
)
{
this
.
humidity
=
humidity
;
}
public
String
getCloudCover
()
{
return
cloudCover
.
replace
(
"%"
,
""
);
}
public
void
setCloudCover
(
String
cloudCover
)
{
this
.
cloudCover
=
cloudCover
;
}
public
String
getDataDate
()
{
return
dataDate
;
}
public
void
setDataDate
(
String
dataDate
)
{
this
.
dataDate
=
dataDate
;
}
}
C09-base/pps-core-base/src/main/java/pps/core/base/mapper/WeatherDataMapper.java
0 → 100644
View file @
b1d7acda
package
pps.core.base.mapper
;
import
com.baomidou.mybatisplus.core.mapper.BaseMapper
;
import
org.springframework.stereotype.Repository
;
import
pps.core.base.entity.BaseWatherEnt
;
@Repository
(
value
=
"pps.core.base.mapper.WeatherDataMapper"
)
public
interface
WeatherDataMapper
extends
BaseMapper
<
BaseWatherEnt
>
{
}
C09-base/pps-core-base/src/main/java/pps/core/base/service/BaseWatherCloudServiceImpl.java
0 → 100644
View file @
b1d7acda
package
pps.core.base.service
;
import
com.alibaba.fastjson.JSONObject
;
import
com.baomidou.mybatisplus.core.conditions.query.QueryWrapper
;
import
org.jsoup.Jsoup
;
import
org.jsoup.nodes.Document
;
import
org.jsoup.nodes.Element
;
import
org.jsoup.select.Elements
;
import
pps.cloud.base.service.BaseWatherCloudService
;
import
pps.cloud.base.service.FBaseDownloadFlowmeterService
;
import
pps.cloud.base.service.data.CrawlBaseWatherInput
;
import
pps.cloud.base.service.data.GetBaseClientStationRelViewInput
;
import
pps.cloud.base.service.data.GetBaseClientStationRelViewOutput
;
import
pps.core.base.entity.BaseWatherEnt
;
import
pps.core.base.mapper.WeatherDataMapper
;
import
pps.core.base.service.data.base_wather.GetBaseWatherInput
;
import
pps.core.base.utils.HttpUtils
;
import
xstartup.annotation.XService
;
import
xstartup.base.XContext
;
import
xstartup.core.base.helper.XThreadHelper
;
import
xstartup.data.XListResult
;
import
xstartup.data.XServiceResult
;
import
xstartup.feature.api.annotation.XApiAnonymous
;
import
xstartup.feature.api.annotation.XApiGet
;
import
java.net.URL
;
import
java.text.SimpleDateFormat
;
import
java.time.LocalTime
;
import
java.util.*
;
@XService
public
class
BaseWatherCloudServiceImpl
implements
BaseWatherCloudService
{
@Override
public
XServiceResult
crawlBaseWatherData
(
XContext
context
)
{
try
{
String
provinceUrl
=
"http://weather.cma.cn/api/dict/province"
;
String
cityUrl
=
"http://weather.cma.cn/api/dict/province/"
;
//请求省份接口。获取省份列表
String
provinceStr
=
HttpUtils
.
send2
(
provinceUrl
,
""
);
//解析省份列表字符串,把字符串解析成json对象
JSONObject
jsonObject
=
JSONObject
.
parseObject
(
provinceStr
);
//处理json对象,变为数组
String
[]
provinceList
=
jsonObject
.
getString
(
"data"
).
split
(
"\\|"
);
for
(
String
proStr:
provinceList
)
{
if
(
proStr
!=
null
&&
proStr
!=
""
)
{
String
[]
pro
=
proStr
.
split
(
","
);
String
provinceCode
=
pro
[
0
];
//省编码
String
provinceName
=
pro
[
1
];
//省名称
//甘肃 .陕西 (全国周期抓取耗费时间超长,共有2400个城市)
if
(
provinceCode
.
equals
(
"AGS"
)
||
provinceCode
.
equals
(
"ASN"
))
{
//请求省份下的区信息
String
cityString
=
HttpUtils
.
send2
(
cityUrl
+
provinceCode
,
""
);
//解析区json,把字符串解析成json对象
JSONObject
cityObj
=
JSONObject
.
parseObject
(
cityString
);
//处理json对象,变为数组
String
[]
cityList
=
cityObj
.
getString
(
"data"
).
split
(
"\\|"
);
for
(
String
cityStr:
cityList
){
if
(
cityStr
!=
null
&&
cityStr
!=
""
){
String
[]
city
=
cityStr
.
split
(
","
);
String
cityCode
=
city
[
0
];
//区编码
String
cityName
=
city
[
1
];
//区名称
XThreadHelper
.
async
(
new
Runnable
()
{
@Override
public
void
run
()
{
insertData
(
context
,
provinceCode
,
provinceName
,
cityCode
,
cityName
,
1
);
}
});
}
}
}
}
}
}
catch
(
Exception
e
){
}
return
XServiceResult
.
OK
;
}
@Override
public
XServiceResult
crawlBaseWatherDataByCityCode
(
XContext
context
,
CrawlBaseWatherInput
input
)
{
insertData
(
context
,
input
.
getProvinceCode
()
,
input
.
getProvinceName
()
,
input
.
getCityCode
()
,
input
.
getCityName
()
,
1
);
return
XServiceResult
.
OK
;
}
public
static
void
insertData
(
XContext
context
,
String
provinceCode
,
String
provinceName
,
String
cityCode
,
String
cityName
,
int
runCount
){
String
url
=
"http://weather.cma.cn/web/weather/"
+
cityCode
+
".html"
;
try
{
WeatherDataMapper
mapper
=
context
.
getBean
(
WeatherDataMapper
.
class
);
Document
document
=
Jsoup
.
parse
(
new
URL
(
url
),
30000
);
Elements
elements
=
document
.
getElementsByClass
(
"mt15"
);
// 获取class name 是‘mt15’的都有元素对象
Element
element
=
null
;
if
(
elements
==
null
||
elements
.
size
()<=
0
){
return
;
}
else
{
element
=
elements
.
get
(
1
);
//根据页面结构获取到需要的元素对象
}
Elements
tables
=
element
.
getElementsByTag
(
"table"
);
SimpleDateFormat
sdf
=
new
SimpleDateFormat
(
"yyyy-MM-dd"
);
List
<
String
>
hourTimeArray
;
// 时间
List
<
String
>
weatherArray
;
// 天气
List
<
String
>
temperatureArray
;
// 气温
List
<
String
>
precipitationArray
;
// 降水
List
<
String
>
windSpeedArray
;
// 风速
List
<
String
>
windDirectionArray
;
// 风向
List
<
String
>
pressureArray
;
// 气压
List
<
String
>
humidityArray
;
// 湿度
List
<
String
>
cloudCoverArray
;
// 云量
Date
date
=
null
;
Calendar
calendar
=
null
;
String
dayTime
=
null
;
BaseWatherEnt
dao
=
null
;
List
<
BaseWatherEnt
>
objList
=
new
ArrayList
<>();
if
(
tables
!=
null
&&
tables
.
size
()>
0
){
for
(
int
i
=
0
;
i
<
tables
.
size
();
i
++){
Map
<
String
,
List
>
map1
=
new
LinkedHashMap
<>();
date
=
new
Date
();
calendar
=
Calendar
.
getInstance
();
calendar
.
add
(
Calendar
.
DATE
,
i
);
date
=
calendar
.
getTime
();
dayTime
=
sdf
.
format
(
date
);
int
week
=
calendar
.
get
(
Calendar
.
DAY_OF_WEEK
);
Elements
trs
=
tables
.
get
(
i
).
select
(
"tr"
);
hourTimeArray
=
new
ArrayList
<>();
// 时间
weatherArray
=
new
ArrayList
<>();
// 天气
temperatureArray
=
new
ArrayList
<>();
// 气温
precipitationArray
=
new
ArrayList
<>();
// 降水
windSpeedArray
=
new
ArrayList
<>();
// 风速
windDirectionArray
=
new
ArrayList
<>();
// 风向
pressureArray
=
new
ArrayList
<>();
// 气压
humidityArray
=
new
ArrayList
<>();
// 湿度
cloudCoverArray
=
new
ArrayList
<>();
// 云量
if
(
trs
!=
null
&&
trs
.
size
()>
0
){
for
(
int
j
=
0
;
j
<
trs
.
size
();
j
++){
// 循环行
Elements
tds
=
trs
.
get
(
j
).
select
(
"td"
);
for
(
int
k
=
1
;
k
<
tds
.
size
();
k
++){
// 循环列
Element
td
=
tds
.
get
(
k
);
String
td_text
;
if
(
"wicon"
.
equals
(
td
.
className
()))
td_text
=
td
.
getElementsByTag
(
"img"
).
attr
(
"src"
);
else
td_text
=
td
.
text
();
if
(
j
==
0
)
hourTimeArray
.
add
(
td_text
);
if
(
j
==
1
)
weatherArray
.
add
(
td_text
);
if
(
j
==
2
)
temperatureArray
.
add
(
td_text
);
if
(
j
==
3
)
precipitationArray
.
add
(
td_text
);
if
(
j
==
4
)
windSpeedArray
.
add
(
td_text
);
if
(
j
==
5
)
windDirectionArray
.
add
(
td_text
);
if
(
j
==
6
)
pressureArray
.
add
(
td_text
);
if
(
j
==
7
)
humidityArray
.
add
(
td_text
);
if
(
j
==
8
)
cloudCoverArray
.
add
(
td_text
);
}
}
}
int
Hous
=
0
;
for
(
int
y
=
0
;
y
<
8
;
y
++){
dao
=
new
BaseWatherEnt
();
dao
.
setCityCode
(
cityCode
);
dao
.
setCityName
(
cityName
);
dao
.
setProvinceCode
(
provinceCode
);
dao
.
setProvinceName
(
provinceName
);
dao
.
setHourTime
(
hourTimeArray
.
get
(
y
));
dao
.
setWeather
(
weatherArray
.
get
(
y
));
dao
.
setTemperature
(
temperatureArray
.
get
(
y
));
dao
.
setPrecipitation
(
precipitationArray
.
get
(
y
));
dao
.
setWindSpeed
(
windSpeedArray
.
get
(
y
));
dao
.
setWindDirection
(
windDirectionArray
.
get
(
y
));
dao
.
setPressure
(
pressureArray
.
get
(
y
));
dao
.
setHumidity
(
humidityArray
.
get
(
y
));
dao
.
setCloudCover
(
cloudCoverArray
.
get
(
y
));
//由于界面时间为当日8点到第二日5点数据,所以写了下面的判断分支
if
(
Hous
==
0
||
Hous
<=
LocalTime
.
parse
(
hourTimeArray
.
get
(
y
)).
getHour
()
){
dao
.
setDayTime
(
dayTime
);
Hous
=
LocalTime
.
parse
(
hourTimeArray
.
get
(
y
)).
getHour
()
;
}
else
if
(
2
==
LocalTime
.
parse
(
hourTimeArray
.
get
(
y
)).
getHour
()
||
5
==
LocalTime
.
parse
(
hourTimeArray
.
get
(
y
)).
getHour
()){
calendar
=
Calendar
.
getInstance
();
calendar
.
add
(
Calendar
.
DATE
,(
i
+
1
));
date
=
calendar
.
getTime
();
dayTime
=
sdf
.
format
(
date
);
dao
.
setDayTime
(
dayTime
);
}
else
continue
;
// 5.执行SQL
QueryWrapper
<
BaseWatherEnt
>
queryWrapper
=
new
QueryWrapper
<>();
queryWrapper
.
lambda
().
eq
(
BaseWatherEnt:
:
getProvinceCode
,
dao
.
getProvinceCode
())
.
eq
(
BaseWatherEnt:
:
getCityCode
,
dao
.
getCityCode
())
.
eq
(
BaseWatherEnt:
:
getDataDate
,
dao
.
getDayTime
()+
' '
+
dao
.
getHourTime
());
mapper
.
delete
(
queryWrapper
);
dao
.
setDataDate
(
dao
.
getDayTime
()+
' '
+
dao
.
getHourTime
());
mapper
.
insert
(
dao
);
objList
.
add
(
dao
);
}
}
// 原站点时间结构为:08:00 11:00 14:00 17:00 20:00 23:00 02:00 05:00
// 两条数据中间差了两个小时,使用上下两条数据推算出中间差掉的两个小时
for
(
int
i
=
0
;
i
<
objList
.
size
()-
1
;
i
++){
for
(
int
y
=
1
;
y
<
3
;
y
++)
{
dao
=
new
BaseWatherEnt
();
dao
.
setCityCode
(
cityCode
);
dao
.
setCityName
(
cityName
);
dao
.
setProvinceCode
(
provinceCode
);
dao
.
setProvinceName
(
provinceName
);
dao
.
setWeather
(
objList
.
get
(
i
).
getWeather
());
int
I_i
=
i
+
1
;
Double
temperature_1
=
Double
.
parseDouble
(
objList
.
get
(
i
).
getTemperature
());
Double
temperature_2
=
Double
.
parseDouble
(
objList
.
get
(
I_i
).
getTemperature
());
dao
.
setTemperature
((
temperature_1
+(
temperature_2
-
temperature_1
)*(
0.3
*
y
))+
"℃"
);
dao
.
setPrecipitation
(
objList
.
get
(
i
).
getPrecipitation
());
Double
windspeed_1
=
Double
.
parseDouble
(
objList
.
get
(
i
).
getWindSpeed
());
Double
windspeed_2
=
Double
.
parseDouble
(
objList
.
get
(
I_i
).
getWindSpeed
());
dao
.
setWindSpeed
((
windspeed_1
+(
windspeed_2
-
windspeed_1
)*(
0.3
*
y
))+
"m/s"
);
dao
.
setWindDirection
(
objList
.
get
(
i
).
getWindDirection
());
Double
pressure_1
=
Double
.
parseDouble
(
objList
.
get
(
i
).
getPressure
());
Double
pressure_2
=
Double
.
parseDouble
(
objList
.
get
(
I_i
).
getPressure
());
dao
.
setPressure
((
pressure_1
+(
pressure_2
-
pressure_1
)*(
0.3
*
y
))+
"hPa"
);
Double
humidity_1
=
Double
.
parseDouble
(
objList
.
get
(
i
).
getHumidity
());
Double
humidity_2
=
Double
.
parseDouble
(
objList
.
get
(
I_i
).
getHumidity
());
dao
.
setHumidity
((
humidity_1
+(
humidity_2
-
humidity_1
)*(
0.3
*
y
))+
"%"
);
Double
cloudcover_1
=
Double
.
parseDouble
(
objList
.
get
(
i
).
getCloudCover
());
Double
cloudcover_2
=
Double
.
parseDouble
(
objList
.
get
(
I_i
).
getCloudCover
());
dao
.
setCloudCover
((
cloudcover_1
+(
cloudcover_2
-
cloudcover_1
)*(
0.3
*
y
))+
"%"
);
if
(
23
==
LocalTime
.
parse
(
objList
.
get
(
i
).
getHourTime
()).
getHour
()){
date
=
sdf
.
parse
(
objList
.
get
(
i
).
getDayTime
());
calendar
=
Calendar
.
getInstance
();
calendar
.
setTime
(
date
);
calendar
.
add
(
Calendar
.
DAY_OF_MONTH
,
1
);
Date
tomorrow
=
calendar
.
getTime
();
dao
.
setDayTime
(
sdf
.
format
(
tomorrow
));
dao
.
setHourTime
(
"0"
+(
y
-
1
)+
":00:00"
);
}
else
{
dao
.
setDayTime
(
objList
.
get
(
i
).
getDayTime
());
String
Hour
=
""
;
if
((
LocalTime
.
parse
(
objList
.
get
(
i
).
getHourTime
()).
getHour
()+
y
)<
10
){
Hour
=
"0"
+(
LocalTime
.
parse
(
objList
.
get
(
i
).
getHourTime
()).
getHour
()+
y
)+
":00:00"
;
}
else
{
Hour
=
(
LocalTime
.
parse
(
objList
.
get
(
i
).
getHourTime
()).
getHour
()+
y
)+
":00:00"
;
}
dao
.
setHourTime
(
Hour
);
}
// 5.执行SQL
QueryWrapper
<
BaseWatherEnt
>
queryWrapper
=
new
QueryWrapper
<>();
queryWrapper
.
lambda
().
eq
(
BaseWatherEnt:
:
getProvinceCode
,
dao
.
getProvinceCode
())
.
eq
(
BaseWatherEnt:
:
getCityCode
,
dao
.
getCityCode
())
.
eq
(
BaseWatherEnt:
:
getDataDate
,
dao
.
getDayTime
()+
' '
+
dao
.
getHourTime
());
mapper
.
delete
(
queryWrapper
);
dao
.
setDataDate
(
dao
.
getDayTime
()+
' '
+
dao
.
getHourTime
());
mapper
.
insert
(
dao
);
}
}
}
}
catch
(
Exception
e
)
{
context
.
getLogger
().
error
(
e
.
getMessage
());
}
}
}
C09-base/pps-core-base/src/main/java/pps/core/base/service/BaseWatherService.java
0 → 100644
View file @
b1d7acda
package
pps.core.base.service
;
import
com.alibaba.fastjson.JSONObject
;
import
com.baomidou.mybatisplus.core.conditions.query.QueryWrapper
;
import
org.jsoup.Jsoup
;
import
org.jsoup.nodes.Document
;
import
org.jsoup.nodes.Element
;
import
org.jsoup.select.Elements
;
import
pps.cloud.base.service.BaseWatherCloudService
;
import
pps.cloud.base.service.data.CrawlBaseWatherInput
;
import
pps.core.base.entity.BaseWatherEnt
;
import
pps.core.base.mapper.WeatherDataMapper
;
import
pps.core.base.service.data.base_wather.GetBaseWatherInput
;
import
pps.core.base.utils.HttpUtils
;
import
xstartup.annotation.XService
;
import
xstartup.base.XContext
;
import
xstartup.core.base.helper.XThreadHelper
;
import
xstartup.data.XServiceResult
;
import
xstartup.feature.api.annotation.XApiAnonymous
;
import
xstartup.feature.api.annotation.XApiGet
;
import
java.net.URL
;
import
java.text.SimpleDateFormat
;
import
java.time.LocalTime
;
import
java.util.*
;
@XService
public
class
BaseWatherService
{
@XApiAnonymous
@XApiGet
public
XServiceResult
crawlBaseWeaterData
(
XContext
context
,
GetBaseWatherInput
input
){
try
{
String
provinceUrl
=
"http://weather.cma.cn/api/dict/province"
;
String
cityUrl
=
"http://weather.cma.cn/api/dict/province/"
;
//请求省份接口。获取省份列表
String
provinceStr
=
HttpUtils
.
send2
(
provinceUrl
,
""
);
//解析省份列表字符串,把字符串解析成json对象
JSONObject
jsonObject
=
JSONObject
.
parseObject
(
provinceStr
);
//处理json对象,变为数组
String
[]
provinceList
=
jsonObject
.
getString
(
"data"
).
split
(
"\\|"
);
for
(
String
proStr:
provinceList
)
{
if
(
proStr
!=
null
&&
proStr
!=
""
)
{
String
[]
pro
=
proStr
.
split
(
","
);
String
provinceCode
=
pro
[
0
];
//省编码
String
provinceName
=
pro
[
1
];
//省名称
//甘肃 .陕西 (全国周期抓取耗费时间超长,共有2400个城市)
if
(
provinceCode
.
equals
(
"AGS"
)
||
provinceCode
.
equals
(
"ASN"
))
{
//请求省份下的区信息
String
cityString
=
HttpUtils
.
send2
(
cityUrl
+
provinceCode
,
""
);
//解析区json,把字符串解析成json对象
JSONObject
cityObj
=
JSONObject
.
parseObject
(
cityString
);
//处理json对象,变为数组
String
[]
cityList
=
cityObj
.
getString
(
"data"
).
split
(
"\\|"
);
for
(
String
cityStr:
cityList
){
if
(
cityStr
!=
null
&&
cityStr
!=
""
){
String
[]
city
=
cityStr
.
split
(
","
);
String
cityCode
=
city
[
0
];
//区编码
String
cityName
=
city
[
1
];
//区名称
XThreadHelper
.
async
(
new
Runnable
()
{
@Override
public
void
run
()
{
insertData
(
context
,
provinceCode
,
provinceName
,
cityCode
,
cityName
,
1
);
}
});
}
}
}
}
}
}
catch
(
Exception
e
){
}
return
XServiceResult
.
OK
;
}
@XApiAnonymous
@XApiGet
public
XServiceResult
crawlBaseWeaterDataByCityCode
(
XContext
context
,
GetBaseWatherInput
input
){
insertData
(
context
,
"ASN"
,
"陕西"
,
"57022"
,
"麟游"
,
1
);
return
XServiceResult
.
OK
;
}
public
static
void
insertData
(
XContext
context
,
String
provinceCode
,
String
provinceName
,
String
cityCode
,
String
cityName
,
int
runCount
){
String
url
=
"http://weather.cma.cn/web/weather/"
+
cityCode
+
".html"
;
try
{
WeatherDataMapper
mapper
=
context
.
getBean
(
WeatherDataMapper
.
class
);
Document
document
=
Jsoup
.
parse
(
new
URL
(
url
),
30000
);
Elements
elements
=
document
.
getElementsByClass
(
"mt15"
);
// 获取class name 是‘mt15’的都有元素对象
Element
element
=
null
;
if
(
elements
==
null
||
elements
.
size
()<=
0
){
return
;
}
else
{
element
=
elements
.
get
(
1
);
//根据页面结构获取到需要的元素对象
}
Elements
tables
=
element
.
getElementsByTag
(
"table"
);
SimpleDateFormat
sdf
=
new
SimpleDateFormat
(
"yyyy-MM-dd"
);
List
<
String
>
hourTimeArray
;
// 时间
List
<
String
>
weatherArray
;
// 天气
List
<
String
>
temperatureArray
;
// 气温
List
<
String
>
precipitationArray
;
// 降水
List
<
String
>
windSpeedArray
;
// 风速
List
<
String
>
windDirectionArray
;
// 风向
List
<
String
>
pressureArray
;
// 气压
List
<
String
>
humidityArray
;
// 湿度
List
<
String
>
cloudCoverArray
;
// 云量
Date
date
=
null
;
Calendar
calendar
=
null
;
String
dayTime
=
null
;
BaseWatherEnt
dao
=
null
;
List
<
BaseWatherEnt
>
objList
=
new
ArrayList
<>();
if
(
tables
!=
null
&&
tables
.
size
()>
0
){
for
(
int
i
=
0
;
i
<
tables
.
size
();
i
++){
Map
<
String
,
List
>
map1
=
new
LinkedHashMap
<>();
date
=
new
Date
();
calendar
=
Calendar
.
getInstance
();
calendar
.
add
(
Calendar
.
DATE
,
i
);
date
=
calendar
.
getTime
();
dayTime
=
sdf
.
format
(
date
);
int
week
=
calendar
.
get
(
Calendar
.
DAY_OF_WEEK
);
Elements
trs
=
tables
.
get
(
i
).
select
(
"tr"
);
hourTimeArray
=
new
ArrayList
<>();
// 时间
weatherArray
=
new
ArrayList
<>();
// 天气
temperatureArray
=
new
ArrayList
<>();
// 气温
precipitationArray
=
new
ArrayList
<>();
// 降水
windSpeedArray
=
new
ArrayList
<>();
// 风速
windDirectionArray
=
new
ArrayList
<>();
// 风向
pressureArray
=
new
ArrayList
<>();
// 气压
humidityArray
=
new
ArrayList
<>();
// 湿度
cloudCoverArray
=
new
ArrayList
<>();
// 云量
if
(
trs
!=
null
&&
trs
.
size
()>
0
){
for
(
int
j
=
0
;
j
<
trs
.
size
();
j
++){
// 循环行
Elements
tds
=
trs
.
get
(
j
).
select
(
"td"
);
for
(
int
k
=
1
;
k
<
tds
.
size
();
k
++){
// 循环列
Element
td
=
tds
.
get
(
k
);
String
td_text
;
if
(
"wicon"
.
equals
(
td
.
className
()))
td_text
=
td
.
getElementsByTag
(
"img"
).
attr
(
"src"
);
else
td_text
=
td
.
text
();
if
(
j
==
0
)
hourTimeArray
.
add
(
td_text
);
if
(
j
==
1
)
weatherArray
.
add
(
td_text
);
if
(
j
==
2
)
temperatureArray
.
add
(
td_text
);
if
(
j
==
3
)
precipitationArray
.
add
(
td_text
);
if
(
j
==
4
)
windSpeedArray
.
add
(
td_text
);
if
(
j
==
5
)
windDirectionArray
.
add
(
td_text
);
if
(
j
==
6
)
pressureArray
.
add
(
td_text
);
if
(
j
==
7
)
humidityArray
.
add
(
td_text
);
if
(
j
==
8
)
cloudCoverArray
.
add
(
td_text
);
}
}
}
int
Hous
=
0
;
for
(
int
y
=
0
;
y
<
8
;
y
++){
dao
=
new
BaseWatherEnt
();
dao
.
setCityCode
(
cityCode
);
dao
.
setCityName
(
cityName
);
dao
.
setProvinceCode
(
provinceCode
);
dao
.
setProvinceName
(
provinceName
);
dao
.
setHourTime
(
hourTimeArray
.
get
(
y
));
dao
.
setWeather
(
weatherArray
.
get
(
y
));
dao
.
setTemperature
(
temperatureArray
.
get
(
y
));
dao
.
setPrecipitation
(
precipitationArray
.
get
(
y
));
dao
.
setWindSpeed
(
windSpeedArray
.
get
(
y
));
dao
.
setWindDirection
(
windDirectionArray
.
get
(
y
));
dao
.
setPressure
(
pressureArray
.
get
(
y
));
dao
.
setHumidity
(
humidityArray
.
get
(
y
));
dao
.
setCloudCover
(
cloudCoverArray
.
get
(
y
));
//由于界面时间为当日8点到第二日5点数据,所以写了下面的判断分支
if
(
Hous
==
0
||
Hous
<=
LocalTime
.
parse
(
hourTimeArray
.
get
(
y
)).
getHour
()
){
dao
.
setDayTime
(
dayTime
);
Hous
=
LocalTime
.
parse
(
hourTimeArray
.
get
(
y
)).
getHour
()
;
}
else
if
(
2
==
LocalTime
.
parse
(
hourTimeArray
.
get
(
y
)).
getHour
()
||
5
==
LocalTime
.
parse
(
hourTimeArray
.
get
(
y
)).
getHour
()){
calendar
=
Calendar
.
getInstance
();
calendar
.
add
(
Calendar
.
DATE
,(
i
+
1
));
date
=
calendar
.
getTime
();
dayTime
=
sdf
.
format
(
date
);
dao
.
setDayTime
(
dayTime
);
}
else
continue
;
// 5.执行SQL
QueryWrapper
<
BaseWatherEnt
>
queryWrapper
=
new
QueryWrapper
<>();
queryWrapper
.
lambda
().
eq
(
BaseWatherEnt:
:
getProvinceCode
,
dao
.
getProvinceCode
())
.
eq
(
BaseWatherEnt:
:
getCityCode
,
dao
.
getCityCode
())
.
eq
(
BaseWatherEnt:
:
getDataDate
,
dao
.
getDayTime
()+
' '
+
dao
.
getHourTime
());
mapper
.
delete
(
queryWrapper
);
dao
.
setDataDate
(
dao
.
getDayTime
()+
' '
+
dao
.
getHourTime
());
mapper
.
insert
(
dao
);
objList
.
add
(
dao
);
}
}
// 原站点时间结构为:08:00 11:00 14:00 17:00 20:00 23:00 02:00 05:00
// 两条数据中间差了两个小时,使用上下两条数据推算出中间差掉的两个小时
for
(
int
i
=
0
;
i
<
objList
.
size
()-
1
;
i
++){
for
(
int
y
=
1
;
y
<
3
;
y
++)
{
dao
=
new
BaseWatherEnt
();
dao
.
setCityCode
(
cityCode
);
dao
.
setCityName
(
cityName
);
dao
.
setProvinceCode
(
provinceCode
);
dao
.
setProvinceName
(
provinceName
);
dao
.
setWeather
(
objList
.
get
(
i
).
getWeather
());
int
I_i
=
i
+
1
;
Double
temperature_1
=
Double
.
parseDouble
(
objList
.
get
(
i
).
getTemperature
());
Double
temperature_2
=
Double
.
parseDouble
(
objList
.
get
(
I_i
).
getTemperature
());
dao
.
setTemperature
((
temperature_1
+(
temperature_2
-
temperature_1
)*(
0.3
*
y
))+
"℃"
);
dao
.
setPrecipitation
(
objList
.
get
(
i
).
getPrecipitation
());
Double
windspeed_1
=
Double
.
parseDouble
(
objList
.
get
(
i
).
getWindSpeed
());
Double
windspeed_2
=
Double
.
parseDouble
(
objList
.
get
(
I_i
).
getWindSpeed
());
dao
.
setWindSpeed
((
windspeed_1
+(
windspeed_2
-
windspeed_1
)*(
0.3
*
y
))+
"m/s"
);
dao
.
setWindDirection
(
objList
.
get
(
i
).
getWindDirection
());
Double
pressure_1
=
Double
.
parseDouble
(
objList
.
get
(
i
).
getPressure
());
Double
pressure_2
=
Double
.
parseDouble
(
objList
.
get
(
I_i
).
getPressure
());
dao
.
setPressure
((
pressure_1
+(
pressure_2
-
pressure_1
)*(
0.3
*
y
))+
"hPa"
);
Double
humidity_1
=
Double
.
parseDouble
(
objList
.
get
(
i
).
getHumidity
());
Double
humidity_2
=
Double
.
parseDouble
(
objList
.
get
(
I_i
).
getHumidity
());
dao
.
setHumidity
((
humidity_1
+(
humidity_2
-
humidity_1
)*(
0.3
*
y
))+
"%"
);
Double
cloudcover_1
=
Double
.
parseDouble
(
objList
.
get
(
i
).
getCloudCover
());
Double
cloudcover_2
=
Double
.
parseDouble
(
objList
.
get
(
I_i
).
getCloudCover
());
dao
.
setCloudCover
((
cloudcover_1
+(
cloudcover_2
-
cloudcover_1
)*(
0.3
*
y
))+
"%"
);
if
(
23
==
LocalTime
.
parse
(
objList
.
get
(
i
).
getHourTime
()).
getHour
()){
date
=
sdf
.
parse
(
objList
.
get
(
i
).
getDayTime
());
calendar
=
Calendar
.
getInstance
();
calendar
.
setTime
(
date
);
calendar
.
add
(
Calendar
.
DAY_OF_MONTH
,
1
);
Date
tomorrow
=
calendar
.
getTime
();
dao
.
setDayTime
(
sdf
.
format
(
tomorrow
));
dao
.
setHourTime
(
"0"
+(
y
-
1
)+
":00:00"
);
}
else
{
dao
.
setDayTime
(
objList
.
get
(
i
).
getDayTime
());
String
Hour
=
""
;
if
((
LocalTime
.
parse
(
objList
.
get
(
i
).
getHourTime
()).
getHour
()+
y
)<
10
){
Hour
=
"0"
+(
LocalTime
.
parse
(
objList
.
get
(
i
).
getHourTime
()).
getHour
()+
y
)+
":00:00"
;
}
else
{
Hour
=
(
LocalTime
.
parse
(
objList
.
get
(
i
).
getHourTime
()).
getHour
()+
y
)+
":00:00"
;
}
dao
.
setHourTime
(
Hour
);
}
// 5.执行SQL
QueryWrapper
<
BaseWatherEnt
>
queryWrapper
=
new
QueryWrapper
<>();
queryWrapper
.
lambda
().
eq
(
BaseWatherEnt:
:
getProvinceCode
,
dao
.
getProvinceCode
())
.
eq
(
BaseWatherEnt:
:
getCityCode
,
dao
.
getCityCode
())
.
eq
(
BaseWatherEnt:
:
getDataDate
,
dao
.
getDayTime
()+
' '
+
dao
.
getHourTime
());
mapper
.
delete
(
queryWrapper
);
dao
.
setDataDate
(
dao
.
getDayTime
()+
' '
+
dao
.
getHourTime
());
mapper
.
insert
(
dao
);
}
}
}
}
catch
(
Exception
e
)
{
context
.
getLogger
().
error
(
e
.
getMessage
());
}
}
}
C09-base/pps-core-base/src/main/java/pps/core/base/service/data/base_wather/GetBaseWatherInput.java
0 → 100644
View file @
b1d7acda
package
pps.core.base.service.data.base_wather
;
public
class
GetBaseWatherInput
{
private
Integer
id
;
public
Integer
getId
()
{
return
id
;
}
public
void
setId
(
Integer
id
)
{
this
.
id
=
id
;
}
}
C10-task/pps-core-task/pom.xml
View file @
b1d7acda
...
...
@@ -33,6 +33,12 @@
<version>
1.0.0-pps
</version>
<scope>
compile
</scope>
</dependency>
<dependency>
<groupId>
gf
</groupId>
<artifactId>
pps-cloud-base
</artifactId>
<version>
1.0.0-pps
</version>
<scope>
compile
</scope>
</dependency>
<dependency>
<groupId>
gf
</groupId>
<artifactId>
pps-cloud-system
</artifactId>
...
...
C10-task/pps-core-task/src/main/java/pps/core/task/job/TestJob.java
View file @
b1d7acda
...
...
@@ -13,7 +13,7 @@ import xstartup.service.job.annotation.XCronTrigger;
@XService
public
class
TestJob
implements
XJob
{
@XCronTrigger
(
value
=
XCronTrigger
.
PRE_5S
)
//
@XCronTrigger(value = XCronTrigger.PRE_5S)
@Override
public
XServiceResult
execute
(
XContext
context
)
{
context
.
getLogger
().
info
(
"push_shipper_gas_split_fail_data_start"
);
...
...
C10-task/pps-core-task/src/main/java/pps/core/task/job/WeatherJob.java
0 → 100644
View file @
b1d7acda
package
pps.core.task.job
;
import
pps.cloud.base.service.BaseWatherCloudService
;
import
pps.cloud.base.service.data.CrawlBaseWatherInput
;
import
xstartup.annotation.XService
;
import
xstartup.annotation.XText
;
import
xstartup.base.XContext
;
import
xstartup.data.XServiceResult
;
import
xstartup.error.XError
;
import
xstartup.service.job.XJob
;
import
xstartup.service.job.annotation.XCronTrigger
;
@XText
(
"天气数据,每8小时执行一次"
)
@XService
public
class
WeatherJob
implements
XJob
{
@XCronTrigger
(
value
=
"0 0 0/8 * * ?"
)
@Override
public
XServiceResult
execute
(
XContext
context
)
{
BaseWatherCloudService
cloudService
=
context
.
getBean
(
BaseWatherCloudService
.
class
);
cloudService
.
crawlBaseWatherData
(
context
);
return
XServiceResult
.
error
(
context
,
XError
.
NotFound
);
}
// @XCronTrigger(value = XCronTrigger.PRE_30S)
// @Override
// public XServiceResult execute(XContext context) {
// BaseWatherCloudService cloudService = context.getBean(BaseWatherCloudService.class);
// CrawlBaseWatherInput crawlBaseWatherInput = new CrawlBaseWatherInput();
// crawlBaseWatherInput.setProvinceCode("ASN");
// crawlBaseWatherInput.setProvinceName("陕西");
// crawlBaseWatherInput.setCityCode("57022");
// crawlBaseWatherInput.setCityName("麟游");
// cloudService.crawlBaseWatherDataByCityCode(context , crawlBaseWatherInput);
// return XServiceResult.error(context, XError.NotFound);
// }
}
D03-deploy/deploy-pps-all/src/main/java/app/DeployPpsAllApplication.java
View file @
b1d7acda
package
app
;
import
pps.core.task.job.TestJob
;
import
pps.core.task.job.WeatherJob
;
import
xstartup.base.XStartup
;
import
xstartup.base.conf.XServerConf
;
import
xstartup.base.conf.XServiceConf
;
...
...
@@ -30,6 +31,7 @@ public class DeployPpsAllApplication {
startup
.
enable
(
XRpcFeature
.
class
);
startup
.
enable
(
XCorsFeature
.
class
);
startup
.
enable
(
XJobFeature
.
class
).
config
(
new
XJobServiceConf
(
TestJob
.
class
));
startup
.
enable
(
XJobFeature
.
class
).
config
(
new
XJobServiceConf
(
WeatherJob
.
class
));
//startup.enable(XCloudHuaweiCseFeature.class);
startup
.
run
(
args
);
}
...
...
D03-deploy/deploy-pps-task/src/main/java/app/DeployPpsTaskApplication.java
View file @
b1d7acda
...
...
@@ -30,6 +30,7 @@ public class DeployPpsTaskApplication {
startup
.
enable
(
XCorsFeature
.
class
);
startup
.
enable
(
XMybatisFeature
.
class
);
startup
.
enable
(
XJobFeature
.
class
).
config
(
new
XJobServiceConf
(
TestJob
.
class
));
startup
.
enable
(
XJobFeature
.
class
).
config
(
new
XJobServiceConf
(
WeatherJob
.
class
));
startup
.
enable
(
XRpcFeature
.
class
);
startup
.
enable
(
XCloudHuaweiCseFeature
.
class
)
...
...
D03-deploy/deploy-pps-task/src/main/resources/deploy-pps-task.app.properties
View file @
b1d7acda
...
...
@@ -7,7 +7,8 @@ pps.core.common.mq-config.consumeInterval=@pps.core.common.mq-config.consumeInte
pps.core.common.obs-config.urlExpires
=
@pps.core.common.obs-config.urlExpires@
pps.core.common.mq-config.ackImsMq
=
@pps.core.common.mq-config.ackImsMq@
x.job.service
=
pps.core.task.job.TestJob
x.job.service
=
pps.core.task.job.TestJob,
\
pps.code.task.job.WeatherJob
#\uFFFD\uFFFD\u05BE\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD
...
...
D03-deploy/filters/filter-dev.properties
View file @
b1d7acda
...
...
@@ -121,9 +121,9 @@ x.mq.ssl-truststore-location=classpath:kafka/truststore-uat.jks
# datasource
x.db.sharding
=
1
x.db.driver
=
com.mysql.jdbc.Driver
x.db.url
=
jdbc:mysql://12
1.36.43.217
:3306/gf_demo?characterEncoding=utf8&allowEncodingChanges=true&serverTimezone=Asia/Shanghai&useSSL=false
x.db.url
=
jdbc:mysql://12
0.46.208.168
:3306/gf_demo?characterEncoding=utf8&allowEncodingChanges=true&serverTimezone=Asia/Shanghai&useSSL=false
x.db.user
=
root
x.db.password
=
_X2csclhj1QIh7OU1ta6iw=
=
x.db.password
=
BJ8HiKUS_Rg
=
x.db.naming
=
snake-case
#huawei-cse config \u4E50\u5F3A\uFF1A10.12.1.98 \u5B66\u71D5\uFF1A10.12.4.102 10.12.0.205 \u5218\u5F3A:10.12.6.213\u4E13\u7EBFcse\u5730\u5740:11.0.25.197
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment