一、资料链接
1、github地址
https://github.com/qq1534774766/wx-push
2、教程地址
https://blog.csdn.net/qq15347747/article/details/126521774
3、易客云API(自动发送天气)
https://yikeapi.com/account/index
4、apispace-各种接口(名人名言)
https://www.apispace.com/console/api?orgId=6356
5、微信公众平台
https://mp.weixin.qq.com/debug/cgi-bin/sandboxinfo?action=showinfo&t=sandbox/index
二、准备工作
(一)进入接口测试页面
1、找到微信公众平台
2、进入开发者工具

3、进入公众平台测试帐号

4、拉到最下面
微信扫码关注
5、记录下述信息

(二)获取天气预报接口地址
1、进入易客云API(自动发送天气)
https://yikeapi.com/account/index
2、注册登录
3、复制参数appid和appsecret的值

4、记录下述信息

(三)获取(名言警句)地址
1、进入apispace-各种接口
https://www.apispace.com/console/api?orgId=6356
2、注册登录
3、购买名言警句
4、进入我的api,点击测试

5、复制token地址

6、记录下述信息

三、开发部署
1、下载项目
git下载:https://github.com/qq1534774766/wx-push.git
2、填写上述参数(application.yml)
3、配置定时
填写cron表达式@Scheduled(cron = "0 30 7 ? * *")

实现方式:import org.springframework.scheduling.annotation.Scheduled;

4、启动项目

5、测试
修改天气预报城市:localhost:8081
测试发送消息:localhost:8081/send
6、打包上传服务器

nohup java -jar wx.jar >wx.txt &
四、优化
(一)cron表达式配置及含义
1、启动类加注解
@EnableScheduling
2、几分钟执行一次

/**
* 固定频率执行。fixedDelay的单位是ms
*/
@Scheduled(fixedDelay = 1000)
public void remindTask2() throws InterruptedException {
log.info("每隔1s执行一次 当前线程名称{} 当前执行次数{}", Thread.currentThread().getName(), task1Number.incrementAndGet());
}
3、cron表达式配置
5秒执行一次
@Scheduled(cron = "*/5 * * * * ? ")
public void remindTask() throws InterruptedException {
log.info("每隔5秒执行一次, 当前线程名称{} 当前执行次数{}", Thread.currentThread().getName(), taskNumber.incrementAndGet());
}
ps:?的含义是
“?”字符仅被用于天(月)和天(星期)两个子表达式,表示不指定值 当2个子表达式其中之一被指定了值以后,为了避免冲突,需要将另一个子表达式的值设为“?”
项目使用
@Scheduled(cron = "0 30 7 ? * *")
@RequestMapping("/send")
public String send() {
try {
return sendService.sendWeChatMsg();
} catch (Exception e) {
e.printStackTrace();
}
JSONObject json = new JSONObject();
json.put("msg", "信息推送失败");
return json.toJSONString();
}
corn从左到右(用空格隔开):秒 分 小时 月份中的日期 月份 星期中的日期 年份
DayofMonth和DayofWeek会相互影响

校验:https://www.bejson.com/othertools/cronvalidate/

日期和星期相互影响,必须配置一个?【?表示不指定周几或每月几号,因为二者相互影响】


配置哪个含义相同
(二)配置名言外的其他api(apispace)
找到名言警句的配置地址
配置其他api
1、示例文档地址
https://www.apispace.com/console/api?orgId=6356
2、示例代码
OkHttpClient client = new OkHttpClient().newBuilder().build();
MediaType mediaType = MediaType.parse("application/x-www-form-urlencoded");
RequestBody body = RequestBody.create(mediaType, "titleID=1");
Request request = new Request.Builder()
.url("https://eolink.o.apispace.com/myjj/common/aphorism/getAphorismList")
.method("POST",body)
.addHeader("X-APISpace-Token","s2qvfzkmuzb9vq7pvw8tnll882rhrarl")
.addHeader("Authorization-Type","apikey")
.addHeader("Content-Type","application/x-www-form-urlencoded")
.build();
Response response = client.newCall(request).execute();
3、实际代码
ProverbServiceImpl
@Override
public String getOneNormalProverb() {
String proverb = null;
try {
OkHttpClient client = new OkHttpClient().newBuilder().build();
MediaType mediaType = MediaType.parse("application/x-www-form-urlencoded");
RequestBody body = RequestBody.create(mediaType, "titleID="+new Random().nextInt(9));
Request request = new Request.Builder()
.url("https://eolink.o.apispace.com/myjj/common/aphorism/getAphorismList")
.method("POST", body)
.addHeader("X-APISpace-Token", configConstant.getToken())
.addHeader("Authorization-Type", "apikey")
.addHeader("Content-Type", "")
.build();Response response = client.newCall(request).execute();
JSONObject jsonObject = JSONObject.parseObject(response.body().string());
//取出全部句子
JSONArray allProverb = JSONObject.parseArray((String) jsonObject.getJSONArray("result").getJSONObject(0).get("words"));
//随机取出一条句子
String s = (String) allProverb.get(new Random().nextInt(allProverb.size()));
//去除无关元素
proverb = s.replaceAll("^.*、", "");
} catch (IOException e) {
throw new RuntimeException(e);
}
return proverb;
}
通过有道翻译为英文
@Override
public String translateToEnglish(String sentence) {
String result = null;
try {
OkHttpClient client = new OkHttpClient().newBuilder().build();
Request request = new Request.Builder()
.url("https://fanyi.youdao.com/translate?&doctype=json&type=AUTO&i="+sentence)
.get()
.addHeader("Content-Type","")
.build();
Response response = client.newCall(request).execute();
result = response.body().string();
//解析
JSONObject jsonObject = JSONObject.parseObject(result);
result = jsonObject.getJSONArray("translateResult").getJSONArray(0).getJSONObject(0).getString("tgt");
}catch (Exception e) {
e.printStackTrace();
}
return result;
}
(三)配置天气预报的原理(易客云)
1、得到当日天气
public JSONObject getWeatherByCity() {
String result = null;
try {
OkHttpClient client = new OkHttpClient.Builder().build();
HttpUrl url = new HttpUrl.Builder()
.scheme("https")
.host("www.yiketianqi.com")
.addPathSegments("free/day")
.addQueryParameter("appid", configConstant.getWeatherAppId())
.addQueryParameter("appsecret", configConstant.getWeatherAppSecret())
.addQueryParameter("city", configConstant.getCity())
.addQueryParameter("unescape", "1")
.build();
Request request = new Request.Builder()
.url(url)
.get()
.build();
Response re = client.newCall(request).execute();
result = re.body().string();
} catch (IOException e) {
e.printStackTrace();
}
return JSONObject.parseObject(result);
}
2、得到三日天气
public Map<String, String> getTheNextThreeDaysWeather() {
Map<String, String> map = null;
try {
OkHttpClient client = new OkHttpClient.Builder().build();
HttpUrl url = new HttpUrl.Builder()
.scheme("https")
.host("yiketianqi.com")
.addPathSegments("free/week")
.addQueryParameter("appid", configConstant.getWeatherAppId())
.addQueryParameter("appsecret", configConstant.getWeatherAppSecret())
.addQueryParameter("city", configConstant.getCity())
.build();
Request request = new Request.Builder()
.url(url)
.get()
.build();
Response response = client.newCall(request).execute();
String responseResult = response.body().string();
if (!StringUtils.hasText(responseResult)) {
logger.error("获取三天天气失败,检查配置文件");
throw new RuntimeException("获取三天天气失败,检查配置文件");
}
logger.info(responseResult);
ZoneId zoneId = ZoneId.of("Asia/Shanghai");
LocalDate now = LocalDate.now(zoneId);
//封装今天,明天,后天的时间
/**
* 原因分析:从天气api获取到未来的天气的日期是 01 02 03 的两位数。
* 而Java中的LocalDate的日期,是一位数的 1 2 3 。
* 因为一开始用是String类型比较,所以01≠1,最后导致异常。
*/
Map<Integer, String> daySet = new HashMap<>();
daySet.put(now.getDayOfMonth(), "今"); // 用now,就不会报错了,1 2 3
daySet.put(now.plusDays(1L).getDayOfMonth(), "明");
daySet.put(now.plusDays(2L).getDayOfMonth(), "后");
//过滤,提取结果
JSONObject jsonObject = JSONObject.parseObject(responseResult);
if (jsonObject.containsKey("errmsg")) {
logger.error(jsonObject.getString("errmsg"));
throw new IllegalArgumentException(jsonObject.getString("errmsg"));
}
map = jsonObject.getJSONArray("data").stream()
.peek(o -> {
String date = getStringFromJson(o, "date").substring(8);
((JSONObject) o).put("date", date);
})
.filter(o -> daySet.containsKey(getIntegerFromJson(o, "date")))
.collect(Collectors.toMap(
key -> daySet.get(getIntegerFromJson(key, "date")),
value -> getStringFromJson(value, "wea")));
} catch (IOException e) {
e.printStackTrace();
throw new RuntimeException("获取失败");
}
return map;
}
(四)发送消息
1、封装各项请求的内容
if (configConstant.isEnableDaily() && StringUtils.hasText(configConstant.getToken())) {
//名言警句,中文
String noteZh = null;
try {
noteZh = proverbService.getOneNormalProverb();
JSONObject note_Zh = JsonObjectUtil.packJsonObject(noteZh, "#879191");
resultMap.put("note_Zh", note_Zh);
logger.info("note_Zh:{}", note_Zh);
} catch (Exception e) {
logger.info("名言警句获取失败,检查ApiSpace的token是否正确?套餐是否过期?");
}
//名言警句,英文
try {
JSONObject note_En = JsonObjectUtil.packJsonObject(proverbService.translateToEnglish(noteZh), "#879191");
resultMap.put("note_En", note_En);
logger.info("note_En:{}", note_En);
} catch (Exception e) {
logger.info("名言警句翻译失败,网易云翻译接口无法使用");
}
}
//封装数据并发送
sendMessage(accessToken, errorList, resultMap, opedId);
2、发送到微信token
private void sendMessage(String accessToken, List<JSONObject> errorList, HashMap<String, Object> resultMap, String opedId) {
JSONObject templateMsg = new JSONObject(new LinkedHashMap<>());
templateMsg.put("touser", opedId);
templateMsg.put("template_id", configConstant.getTemplateId());
templateMsg.put("data", new JSONObject(resultMap));
String url = "https://api.weixin.qq.com/cgi-bin/message/template/send?access_token=" + accessToken;
String sendPost = HttpUtil.sendPost(url, templateMsg.toJSONString());
JSONObject WeChatMsgResult = JSONObject.parseObject(sendPost);
if (!"0".equals(WeChatMsgResult.getString("errcode"))) {
JSONObject error = new JSONObject();
error.put("openid", opedId);
error.put("errorMessage", WeChatMsgResult.getString("errmsg"));
errorList.add(error);
}
}
五、技术总结
1、Scheduleh注解定时调任务
2、微信token发送指定消息
3、调用接口得到数据(天气预报和各种api)
4、姓名如何在方法头上标注
5、代码结构

6、配置参数调用-通过value和自动注入

configConstant.getWeatherAppId()

大约一年前,我决定确保每个包含非唯一文本的Flash通知都将从模块中的方法中获取文本。我这样做的最初原因是为了避免一遍又一遍地输入相同的字符串。如果我想更改措辞,我可以在一个地方轻松完成,而且一遍又一遍地重复同一件事而出现拼写错误的可能性也会降低。我最终得到的是这样的:moduleMessagesdefformat_error_messages(errors)errors.map{|attribute,message|"Error:#{attribute.to_s.titleize}#{message}."}enddeferror_message_could_not_find(obje
如何在buildr项目中使用Ruby?我在很多不同的项目中使用过Ruby、JRuby、Java和Clojure。我目前正在使用我的标准Ruby开发一个模拟应用程序,我想尝试使用Clojure后端(我确实喜欢功能代码)以及JRubygui和测试套件。我还可以看到在未来的不同项目中使用Scala作为后端。我想我要为我的项目尝试一下buildr(http://buildr.apache.org/),但我注意到buildr似乎没有设置为在项目中使用JRuby代码本身!这看起来有点傻,因为该工具旨在统一通用的JVM语言并且是在ruby中构建的。除了将输出的jar包含在一个独特的、仅限ruby
我在我的Rails项目中使用Pow和powifygem。现在我尝试升级我的ruby版本(从1.9.3到2.0.0,我使用RVM)当我切换ruby版本、安装所有gem依赖项时,我通过运行railss并访问localhost:3000确保该应用程序正常运行以前,我通过使用pow访问http://my_app.dev来浏览我的应用程序。升级后,由于错误Bundler::RubyVersionMismatch:YourRubyversionis1.9.3,butyourGemfilespecified2.0.0,此url不起作用我尝试过的:重新创建pow应用程序重启pow服务器更新战俘
我已经像这样安装了一个新的Rails项目:$railsnewsite它执行并到达:bundleinstall但是当它似乎尝试安装依赖项时我得到了这个错误Gem::Ext::BuildError:ERROR:Failedtobuildgemnativeextension./System/Library/Frameworks/Ruby.framework/Versions/2.0/usr/bin/rubyextconf.rbcheckingforlibkern/OSAtomic.h...yescreatingMakefilemake"DESTDIR="cleanmake"DESTDIR="
我有用于控制用户任务的Rails5API项目,我有以下错误,但并非总是针对相同的Controller和路由。ActionController::RoutingError:uninitializedconstantApi::V1::ApiController我向您描述了一些我的项目,以更详细地解释错误。应用结构路线scopemodule:'api'donamespace:v1do#=>Loginroutesscopemodule:'login'domatch'login',to:'sessions#login',as:'login',via::postend#=>Teamroutessc
假设我有这个范围:("aaaaa".."zzzzz")如何在不事先/每次生成整个项目的情况下从范围中获取第N个项目? 最佳答案 一种快速简便的方法:("aaaaa".."zzzzz").first(42).last#==>"aaabp"如果出于某种原因你不得不一遍又一遍地这样做,或者如果你需要避免为前N个元素构建中间数组,你可以这样写:moduleEnumerabledefskip(n)returnto_enum:skip,nunlessblock_given?each_with_indexdo|item,index|yieldit
在应用开发中,有时候我们需要获取系统的设备信息,用于数据上报和行为分析。那在鸿蒙系统中,我们应该怎么去获取设备的系统信息呢,比如说获取手机的系统版本号、手机的制造商、手机型号等数据。1、获取方式这里分为两种情况,一种是设备信息的获取,一种是系统信息的获取。1.1、获取设备信息获取设备信息,鸿蒙的SDK包为我们提供了DeviceInfo类,通过该类的一些静态方法,可以获取设备信息,DeviceInfo类的包路径为:ohos.system.DeviceInfo.具体的方法如下:ModifierandTypeMethodDescriptionstatic StringgetAbiList()Obt
我正在使用Mandrill的RubyAPIGem并使用以下简单的测试模板:testastic按照Heroku指南中的示例,我有以下Ruby代码:require'mandrill'm=Mandrill::API.newrendered=m.templates.render'test-template',[{:header=>'someheadertext',:main_section=>'Themaincontentblock',:footer=>'asdf'}]mail(:to=>"JaysonLane",:subject=>"TestEmail")do|format|format.h
我正在尝试使用Ruby2.0.0和Rails4.0.0提供的API从imgur中提取图像。我已尝试按照Ruby2.0.0文档中列出的各种方式构建http请求,但均无济于事。代码如下:require'net/http'require'net/https'defimgurheaders={"Authorization"=>"Client-ID"+my_client_id}path="/3/gallery/image/#{img_id}.json"uri=URI("https://api.imgur.com"+path)request,data=Net::HTTP::Get.new(path
Rails相对较新。我正在尝试调用一个API,它应该向我返回一个唯一的URL。我的应用程序中捆绑了HTTParty。我已经创建了一个UniqueNumberController,并且我已经阅读了几个HTTParty指南,直到我想要什么,但也许我只是有点迷路,真的不知道该怎么做。基本上,我需要做的就是调用API,获取它返回的URL,然后将该URL插入到用户的数据库中。谁能给我指出正确的方向或与我分享一些代码? 最佳答案 假设API为JSON格式并返回如下数据:{"url":"http://example.com/unique-url"