微信公众平台测试账号:https://mp.weixin.qq.com/debug/cgi-bin/sandboxinfo?action=showinfo&t=sandbox/index
微信推送文档:https://mp.weixin.qq.com/debug/cgi-bin/readtmpl?t=tmplmsg/faq_tmpl
百度地图开放平台:https://lbsyun.baidu.com/apiconsole/center#/home
天气服务接口文档:https://lbs.baidu.com/index.php?title=webapi/weather
{{date.DATA}} {{week.DATA}}
{{city.DATA}}的天气:{{temp.DATA}}
最低气温:{{low.DATA}}度
最高气温:{{high.DATA}}度
wechat:
# 微信公众号账号
mappid: wx6269*************
# 微信公众号密码
msecret: d931f8205f1***************
# 微信公众账号原始ID
original_id: gh_6*******
weather:
data_type: all
ak: o5ewfdqWFAkk********
# 区号,例如广州市天河区
district_id: 440106
/**
* @author lanys
* @author Think
* @title: WechatProperties
* @projectName material_cloud
* @description: YML 微信配置信息映射
* @date 2022/12/20 11:09
*/
@Getter
@Setter
@Component
@ConfigurationProperties("wechat")
public class WechatProperties {
private String mappid;
private String msecret;
private String originalId;
}
/**
* @author lanys
* @author Think
* @title: Weather
* @projectName maku-cloud
* @description: 获取百度天气数据
* @date 2022/12/29 12:20
*/
@Getter
@Setter
@Component
@ConfigurationProperties("weather")
public class Weather {
private String districtId;
private String dataType;
private String ak;
}
/**
* 微信通用接口凭证
* @author lanys
*/
public class AccessToken implements java.io.Serializable{
private static final long serialVersionUID = -4240357901925120079L;
/** 获取到的凭证 */
private String token;
/** 凭证有效时间,单位:秒 */
private int expiresIn;
public AccessToken() {
}
public AccessToken(String token, int expiresIn) {
this.token = token;
this.expiresIn = expiresIn;
}
public String getToken() {
return token;
}
public void setToken(String token) {
this.token = token;
}
public int getExpiresIn() {
return expiresIn;
}
public void setExpiresIn(int expiresIn) {
this.expiresIn = expiresIn;
}
}
/**
* @author lanys
* @author Think
* @title: Weather
* @projectName maku-cloud
* @description: 天气
* @date 2022/12/30 12:06
*/
@Setter@Getter
@AllArgsConstructor
@NoArgsConstructor
public class WeatherModel {
/** 省 */
private String province;
/** 市 */
private String city;
/** 区 */
private String name;
/** 天气 */
private String text;
/** 级 */
private String wind_class;
/** 风向 */
private String wind_dir;
private String textNight;
/** 最高温度 */
private String high;
/** 最低温度 */
private String low;
/** 日期 */
private String date;
/** 星期几 */
private String week;
/** 当前温度 */
private String temp;
}
@Component
public class RedisCache {
@Resource
private RedisTemplate<String, Object> redisTemplate;
/**
* 默认过期时长为24小时,单位:秒
*/
public final static long DEFAULT_EXPIRE = 60 * 60 * 24L;
/**
* 过期时长为1小时,单位:秒
*/
public final static long HOUR_ONE_EXPIRE = 60 * 60 * 1L;
/**
* 过期时长为6小时,单位:秒
*/
public final static long HOUR_SIX_EXPIRE = 60 * 60 * 6L;
/**
* 不设置过期时长
*/
public final static long NOT_EXPIRE = -1L;
public void set(String key, Object value, long expire) {
redisTemplate.opsForValue().set(key, value);
if (expire != NOT_EXPIRE) {
expire(key, expire);
}
}
public void set(String key, Object value) {
set(key, value, DEFAULT_EXPIRE);
}
public Object get(String key, long expire) {
Object value = redisTemplate.opsForValue().get(key);
if (expire != NOT_EXPIRE) {
expire(key, expire);
}
return value;
}
public Object get(String key) {
return get(key, NOT_EXPIRE);
}
}
/**
* @Author: Lanys
* @Description: https 调用工具
* @Date: Create in 21:51 2022/12/21
*/
@Slf4j
@Component
@AllArgsConstructor
public class HttpUtils {
private final RedisCache redisCache;
private final WechatProperties wechatProperties;
private final Weather weather;
/** 获取access_token的接口地址(GET) 限2000(次/天) */
public final static String ACCESS_TOKEN_URL = "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid={0}&secret={1}";
/** 获取关注用户id */
public final static String USER_GET_URL = "https://api.weixin.qq.com/cgi-bin/user/get?access_token={0}";
/** 获取天气情况 */
public final static String WEATHER = "https://api.map.baidu.com/weather/v1/?data_type={0}&ak={1}&district_id={2}";
public static JSONObject httpsRequest(String requestUrl, String requestMethod, String outputStr) {
log.debug("[发起https请求]requestUrl=" + requestUrl);
log.debug("[发起https请求]requestMethod=" + requestMethod);
log.debug("[发起https请求]outputStr=" + outputStr);
JSONObject jsonObject = null;
StringBuffer buffer = new StringBuffer();
try {
// 创建SSLContext对象,并使用我们指定的信任管理器初始化
TrustManager[] tm = {new X509TrustManager() {
@Override
public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException {
}
@Override
public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException {
}
@Override
public X509Certificate[] getAcceptedIssuers() {
return new X509Certificate[0];
}
}};
SSLContext sslContext = SSLContext.getInstance("SSL", "SunJSSE");
sslContext.init(null, tm, new java.security.SecureRandom());
// 从上述SSLContext对象中得到SSLSocketFactory对象
SSLSocketFactory ssf = sslContext.getSocketFactory();
URL url = new URL(requestUrl);
HttpsURLConnection httpUrlConn = (HttpsURLConnection) url
.openConnection();
httpUrlConn.setSSLSocketFactory(ssf);
httpUrlConn.setDoOutput(true);
httpUrlConn.setDoInput(true);
httpUrlConn.setUseCaches(false);
// 设置请求方式(GET/POST)
httpUrlConn.setRequestMethod(requestMethod);
//if ("GET".equalsIgnoreCase(requestMethod))
httpUrlConn.connect();
// 当有数据需要提交时
if (null != outputStr) {
OutputStream outputStream = httpUrlConn.getOutputStream();
// 注意编码格式,防止中文乱码
outputStream.write(outputStr.getBytes("UTF-8"));
outputStream.close();
}
// 将返回的输入流转换成字符串
InputStream inputStream = httpUrlConn.getInputStream();
InputStreamReader inputStreamReader = new InputStreamReader(
inputStream, "utf-8");
BufferedReader bufferedReader = new BufferedReader(
inputStreamReader);
String str = null;
while ((str = bufferedReader.readLine()) != null) {
buffer.append(str);
}
bufferedReader.close();
inputStreamReader.close();
// 释放资源
inputStream.close();
httpUrlConn.disconnect();
jsonObject = JSONObject.parseObject(buffer.toString());
} catch (ConnectException ce) {
log.error("Weixin server connection timed out.");
} catch (Exception e) {
log.error("https request error:{}", e);
}
return jsonObject;
}
/**
* 获取token
* @return
* @throws Exception
*/
public AccessToken getAccessToken() throws Exception{
String appid = wechatProperties.getMappid();
String secret = wechatProperties.getMsecret();
AccessToken accessToken = null;
String requestUrl = ACCESS_TOKEN_URL.replace("{0}",appid).replace("{1}",secret);
JSONObject jsonObject=null;
if(null== redisCache.get(appid)){
//access_token的有效期目前为2个小时,需定时刷新,重复获取将导致上次获取的access_token失效。
//获取access_token 每日限额 2000次,注:如各个业务逻辑点各自去刷新access_token,那么就可能会产生冲突,导致服务不稳定
jsonObject = httpsRequest(requestUrl, "GET", null);
log.info("[(刷新)获取access_token]jsonObject="+jsonObject);
//CacheUtil.put(appid, jsonObject, jsonObject.getInt("expires_in")-7000<200?jsonObject.getInt("expires_in"):200 );//简易定时器 ,200秒内值缓存
redisCache.set(appid, jsonObject.toString(), jsonObject.getInteger("expires_in")-7000<200?jsonObject.getInteger("expires_in"):200);
}
jsonObject = JSONObject.parseObject((String) redisCache.get(appid));
if (null != jsonObject) {
try {
accessToken = new AccessToken(jsonObject.getString("access_token"), jsonObject.getInteger("expires_in"));
} catch (JSONException e) {
log.error("获取token失败 errcode:{} errmsg:{}"+jsonObject.getInteger("errcode")+"errmsg:"+jsonObject.getString("errmsg"),e);
}
}
log.info("[获取access_token]jsonObject="+jsonObject);
log.info("[获取access_token]accessToken="+accessToken.getToken());
return accessToken;
}
/**
* 获取关注用户
* @return
*/
public List<String> getUsers() {
try {
log.info("------------ [获取用户信息] ---------------");
String appid = wechatProperties.getMappid();
String token = null;
if(null== redisCache.get(appid)){
AccessToken accessToken = this.getAccessToken();
if (accessToken != null) {
token = accessToken.getToken();
}
} else {
JSONObject jsonObject = JSONObject.parseObject((String) redisCache.get(appid));
token = jsonObject.getString("access_token");
}
if (token != null) {
String requestUrl = USER_GET_URL.replace("{0}",token);
JSONObject jsonObject = HttpUtils.httpsRequest(requestUrl, "GET", null);
String dataJson = jsonObject.getString("data");
JSONObject data = JSONObject.parseObject(dataJson);
JSONArray openid = data.getJSONArray("openid");
List<String> list = new ArrayList<>();
if (openid != null && openid.size() > 0) {
for (int i = 0; i < openid.size(); i++) {
Object obj = openid.get(i);
if (obj != null) {
list.add(obj.toString());
}
}
}
return list;
}
} catch (Exception e) {
log.error("[获取关注用户信息]数据异常:",e);
}
return null;
}
/**
* 获取天气
* @return
*/
public WeatherModel getWeather(){
try{
log.info("------------ [获取天气] ------------");
String requestUrl = WEATHER.replace("{0}", weather.getDataType()).replace("{1}", weather.getAk())
.replace("{2}", weather.getDistrictId());
JSONObject jsonObject = HttpUtils.httpsRequest(requestUrl, "GET", null);
if (jsonObject != null) {
WeatherModel weather = new WeatherModel();
JSONObject jsonObj = jsonObject.getJSONObject("result");
JSONObject object = jsonObj.getJSONObject("location");
if (object != null) {
weather.setProvince(object.getString("province"));
weather.setCity(object.getString("city"));
weather.setName(object.getString("name"));
}
object = jsonObj.getJSONObject("now");
if (object != null) {
weather.setText(object.getString("text"));
weather.setWind_class(object.getString("wind_class"));
weather.setWind_dir(object.getString("wind_dir"));
weather.setTemp(object.getString("temp"));
}
JSONArray forecasts = jsonObj.getJSONArray("forecasts");
if (!CollectionUtils.isEmpty(forecasts)) {
Map<String, Object> obj = (Map<String,Object>)forecasts.get(0);
if (obj != null) {
weather.setTextNight(obj.get("text_night").toString());
weather.setHigh(obj.get("high") != null ? obj.get("high").toString():null);
weather.setLow(obj.get("low") != null ? obj.get("low").toString():null);
weather.setDate(obj.get("date").toString());
weather.setWeek(obj.get("week").toString());
}
}
return weather;
}
} catch (Exception e) {
log.error("[获取天气信息]数据异常:",e);
}
return null;
}
}
/**
* @Author: Lanys
* @Description: 定时推送任务
* @Date: Create in 22:05 2022/12/21
*/
@Slf4j
@Service
public class WechatPush {
@Resource
public HttpUtils httpUtils;
public void run(String params) throws Exception {
//默认都是用正式模板
String templateId = "BaKzfhFvinVpNPV2xVo1zkIWMWrBlazt9cMzxkry-5U";
String messageTemplate = "{\"touser\":\"{0}\",\"template_id\":\""+templateId+"\","
+ "\"data\":{\"date\": {\"value\":\"{1}\",\"color\":\"#00BFFF\"},\"week\":{\"value\":\"{2}\",\"color\":\"#00FFFF\"},"
+ "\"city\": {\"value\":\"{3}\",\"color\":\"#173177\"},\"temp\": {\"value\":\"{4}\",\"color\":\"#EE212D\"},"
+ "\"low\":{\"value\":\"{5}\",\"color\":\"#FF6347\"},"
+ "\"high\":{\"value\":\"{6}\",\"color\":\"#173177\"}}}";
// 获取token
AccessToken accessToken = httpUtils.getAccessToken();
String URL_TEMPLATE_MSG_SEND = "https://api.weixin.qq.com/cgi-bin/message/template/send?access_token={0}";
String sendUrl = URL_TEMPLATE_MSG_SEND.replace("{0}", accessToken.getToken());
// 获取关注用户
List<String> users = httpUtils.getUsers();
if (users != null && users.size() > 0) {
// 获取天气
WeatherModel weather = httpUtils.getWeather();
// 并行遍历推送
users.stream().parallel().forEach(x->{
if (x != null) {
String postData = messageTemplate.replace("{0}", x)
.replace("{1}", weather.getDate())
.replace("{2}", weather.getWeek())
.replace("{3}", weather.getCity())
.replace("{4}", weather.getText())
.replace("{5}", weather.getLow())
.replace("{6}", weather.getHigh());
log.info("[发送模板信息]sendTemplateMessage:"+postData);
JSONObject jsonObject = HttpUtils.httpsRequest(sendUrl, "POST", postData);
log.info("[发送模板信息] sendTemplateMessage result:"+jsonObject);
}
});
}
}
}
地址:百度地图开放平台