最近、インターネット上でガールフレンドに対して WeChat 公式アカウントのプッシュを行う傾向があるので、私のガールフレンドにも彼女以外にも彼女がいるはずです。おお、WeChat 公式アカウントのプッシュを行う原理について簡単に勉強しました。簡単に言うと、バックグラウンド サーバーはタイマー タスクを実行し、WeChat パブリック プラットフォームが提供する Web API インターフェイス (HTTP) を定期的に呼び出し、テンプレート メッセージ (JSON データ) を送信します。技術スタックと開発プロセスは次のように要約されます。
- フロントエンド: WeChat パブリック アカウント - サブスクリプション アカウント - テスト アカウント
- バックエンド: SpringBoot + RestTemplate
1. WeChat パブリック プラットフォームのテスト アカウントを構成する
クリックして WeChat パブリック プラットフォームのリンク ( WeChat パブリック プラットフォーム ) を開き、WeChat パブリック テスト アカウントを登録および申請します。テスト アカウントは、WeChat パブリック プラットフォームのすべての機能 API インターフェイスを体験できます。ここでは主にテンプレート メッセージ プッシュ API を使用します。ただし、テストアカウントの制限は、デフォルトの公式アカウント名のみが使用できることと、機能がいつでも廃止される可能性があることです。条件が許せば、エンタープライズ サブスクリプション アカウント/サービス アカウントを申請できます (個人認定サブスクリプション アカウントにはテンプレート メッセージ プッシュ API権限がなく、エンタープライズのみがサービス アカウントを認証できます)。テスト番号の構成では、次の部分がより重要です (開発ドキュメントテンプレート メッセージ | WeChat オープン ドキュメント (qq.com)を参照してください)。
テスト番号情報: appID と appsecret を含む、本人確認のためのトークンと API インターフェイスを取得するために使用されます。
ユーザー リスト:サブスクライブしたユーザーの openId を取得し、それをターゲット ユーザーにプッシュするために使用されます (最初にこのサブスクリプション番号をたどる必要があります)
テンプレート メッセージ インターフェイス:テンプレート ID (インターフェイス呼び出し用)、テンプレート コンテンツなどを含む、プッシュされたメッセージ テンプレートの設定に使用されます。パラメーターは、インターフェースを呼び出すときに使用されるテンプレートのコンテンツに設定できます (テンプレートのタイトルは許可されません)。パラメーターは、 { { params.DATA }}の形式で構成する必要があります。このうち、params はバックエンド サーバーから送信される対応する JSON データ変数の名前、DATA はフロントエンド テンプレート メッセージの固定構文です。
//JSON数据传输格式示例
{
"touser":"OPENID",
"template_id":"ngqIpbwh8bUfcSsECmogfXcV14J0tQlEpBO27izEYtY",
"topcolor":"#FF0000",
"data":{
"date": {
"value":"2022-09-04 星期日",
"color":"#173177"
},
"remark":{
"value":"♥",
"color":"#173177"
},
"city": {
"value":"北京",
"color":"#173177"
},
"weather": {
"value":"多云转晴",
"color":"#173177"
},
...
}
}
2. データインターフェースのカプセル化
メッセージプッシュテンプレートで使用する必要があるデータは、気候(天気、気温、都市など)、デート日、誕生日カウントダウン日、虹のおなら文などで、データの取得方法は以下の通りです。
- 気候 (天気、気温、都市など)、虹のおならステートメント: Tianxing データ インターフェイス Web API を使用 ( Tianxing data TianAPI - 開発者向け API データ プラットフォーム)
- 恋の日数、誕生日までのカウントダウン:ローカルパッケージ計算ツール
2.1 Tianxing データインターフェースのカプセル化
Tianxing インターフェース データの取得も、Web API 経由でリクエストを送信し、その応答内のデータ (RestTemplate) を取得することであり、インターフェース ドキュメントは次のとおりです。
- 天気インターフェース:天気予報 API インターフェース - Tianxing Data TianAPI
- Rainbow Fart: Rainbow Fart API インターフェイス - TianAPI
public class DataUtils {
/**
* 获取 Weather 信息
* @param restTemplate
* @return
*/
public static Weather getWeather(RestTemplate restTemplate){
String responseJson = restTemplate.getForObject(WeChatConfigure.Weather_API, String.class);
JSONObject responseResult = JSONObject.parseObject(responseJson);
JSONObject jsonObject = responseResult.getJSONArray("newslist").getJSONObject(0);
return jsonObject.toJavaObject(Weather.class);
}
/**
* 获取 RainbowPi 信息
* @param restTemplate
* @return
*/
public static String getRainbow(RestTemplate restTemplate){
String responseJson = restTemplate.getForObject(WeChatConfigure.Rainbow_API, String.class);
JSONObject responseResult = JSONObject.parseObject(responseJson);
JSONObject jsonObject = responseResult.getJSONArray("newslist").getJSONObject(0);
return jsonObject.getString("content");
}
}
2.2 日付計算ツールのパッケージ
誕生日のカウントダウンを計算するロジックと、恋の日数を計算するロジックは異なります。誕生日のカウントダウンを計算するには、誕生日が過ぎたかどうかを判断する必要がありますが、恋の日数の計算は比較的簡単で、時間を直接カウントするだけです。
public class DataUtils {
/**
* 计算生日天数 days
* @return
*/
public static int getBirthDays(String birthday) {
SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd");
Calendar cToday = Calendar.getInstance(); // 存今天
Calendar cBirth = Calendar.getInstance(); // 存生日
int days = 0;
try {
cBirth.setTime(dateFormat.parse(birthday)); // 设置生日
cBirth.set(Calendar.YEAR, cToday.get(Calendar.YEAR)); // 修改为本年
if (cBirth.get(Calendar.DAY_OF_YEAR) < cToday.get(Calendar.DAY_OF_YEAR)) {
// 生日已经过了,要算明年的了
days = (cToday.getActualMaximum(Calendar.DAY_OF_YEAR) - cToday.get(Calendar.DAY_OF_YEAR)) + cBirth.get(Calendar.DAY_OF_YEAR);
} else {
// 生日还没过
days = cBirth.get(Calendar.DAY_OF_YEAR) - cToday.get(Calendar.DAY_OF_YEAR);
}
} catch (ParseException e) {
e.printStackTrace();
}
return days;
}
/**
* 计算恋爱天数 days
* @return
*/
public static int getLoveDays(String loveday){
SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd");
int days = 0;
try {
long time = System.currentTimeMillis() - dateFormat.parse(loveday).getTime();
days = (int) (time / (24*60*60*1000));
} catch (ParseException e) {
e.printStackTrace();
}
return days;
}
}
3. プッシュロジックをカプセル化する
3.1 パッケージ構成クラス
@Component
public class WeChatConfigure {
public static String Access_URL = "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid={0}&secret={1}";
public static String Send_URL = "https://api.weixin.qq.com/cgi-bin/message/template/send?access_token={0}";
public static String App_ID;
@Value("${WeChat.AppID}")
public void setAppID(String AppID) {
App_ID = AppID;
}
public static String App_Secret;
@Value("${WeChat.AppSecret}")
public void setAppSecret(String AppSecret) {
App_Secret = AppSecret;
}
public static String Open_ID;
@Value("${WeChat.OpenID}")
public void setOpenID(String OpenID) {
Open_ID = OpenID;
}
public static String Template_ID;
@Value("${WeChat.TemplateID}")
public void setTemplateID(String TemplateID) {
Template_ID = TemplateID;
}
public static String Top_Color;
@Value("${WeChat.TopColor}")
public void setTopColor(String TopColor) {
Top_Color = TopColor;
}
public static String Weather_API;
@Value("${WeChat.WeatherAPI}")
public void setWeatherAPI(String WeatherAPI) {
Weather_API = WeatherAPI;
}
public static String Rainbow_API;
@Value("${WeChat.RainbowAPI}")
public void setRainbowAPI(String RainbowAPI) {
Rainbow_API = RainbowAPI;
}
public static String Boy_Birthday;
@Value("${WeChat.BoyBirthday}")
public void setBoyBirthday(String BoyBirthday) {
Boy_Birthday = BoyBirthday;
}
public static String Girl_Birthday;
@Value("${WeChat.GirlBirthday}")
public void setGirlBirthday(String GirlBirthday) {
Girl_Birthday = GirlBirthday;
}
public static String Love_Day;
@Value("${WeChat.LoveDay}")
public void setLoveDay(String LoveDay) {
Love_Day = LoveDay;
}
}
3.2 エンティティクラスのカプセル化
//单条数据Item封装
public class DataItem {
private String value;
private String color;
public DataItem(String _value, String _color) {
this.value = _value;
this.color = _color;
}
public String getValue() {
return value;
}
public void setValue(String value) {
this.value = value;
}
public String getColor() {
return color;
}
public void setColor(String color) {
this.color = color;
}
}
//发送数据集result封装
public class ResultVo {
private String touser;
private String template_id;
private String topcolor;
private HashMap<String, DataItem> data;
private ResultVo(String _touser, String _template_id, String _topcolor, HashMap<String, DataItem> _data) {
this.touser = _touser;
this.template_id = _template_id;
this.topcolor = _topcolor;
this.data = _data;
}
public String getTouser() {
return touser;
}
public String getTemplate_id() {
return template_id;
}
public String getTopcolor() {
return topcolor;
}
public HashMap<String, DataItem> getData() {
return data;
}
public static ResultVo initializeResultVo(String _touser, String _template_id, String _topcolor){
return new ResultVo(_touser,_template_id,_topcolor,null);
}
public static ResultVo initializeResultVo(String _touser, String _template_id, String _topcolor,HashMap<String, DataItem> _data){
return new ResultVo(_touser,_template_id,_topcolor,_data);
}
public ResultVo setAttribute(String key, DataItem item){
if(this.data==null)this.data = new HashMap<String,DataItem>();
this.data.put(key,item);
return this;
}
}
3.3 プッシュ ロジック コントローラーの実装
@Controller
public class WeChatController {
@Autowired
RestTemplate restTemplate;
/**
* {
{date.DATA}}
* {
{remark.DATA}}
* 所在城市:{
{city.DATA}}
* 今日天气:{
{weather.DATA}}
* 气温变化:{
{min_temperature.DATA}} ~ {
{max_temperature.DATA}}
* 今日建议:{
{tips.DATA}}
* 今天是我们恋爱的第 {
{love_days.DATA}} 天
* 距离xx生日还有 {
{girl_birthday.DATA}} 天
* 距离xx生日还有 {
{boy_birthday.DATA}} 天
* {
{rainbow.DATA}}
*/
public void push(){
ResultVo resultVo = ResultVo.initializeResultVo(WeChatConfigure.Open_ID,WeChatConfigure.Template_ID,WeChatConfigure.Top_Color);
//1.设置城市与天气信息
Weather weather = DataUtils.getWeather(restTemplate);
resultVo.setAttribute("date",new DataItem(weather.getDate() + " " + weather.getWeek(),"#00BFFF"));
resultVo.setAttribute("city",new DataItem(weather.getArea(),null));
resultVo.setAttribute("weather",new DataItem(weather.getWeather(),"#1f95c5"));
resultVo.setAttribute("min_temperature",new DataItem(weather.getLowest(),"#0ace3c"));
resultVo.setAttribute("max_temperature",new DataItem(weather.getHighest(),"#dc1010"));
resultVo.setAttribute("tips",new DataItem(weather.getTips(),null));
//2.设置日期相关
int love_days = DataUtils.getLoveDays(WeChatConfigure.Love_Day);
int girl_birthday = DataUtils.getBirthDays(WeChatConfigure.Girl_Birthday);
int boy_birthday = DataUtils.getBirthDays(WeChatConfigure.Boy_Birthday);
resultVo.setAttribute("love_days",new DataItem(love_days+"","#FFA500"));
resultVo.setAttribute("girl_birthday",new DataItem(girl_birthday+"","#FFA500"));
resultVo.setAttribute("boy_birthday",new DataItem(boy_birthday+"","#FFA500"));
//3.设置彩虹屁
String rainbow = DataUtils.getRainbow(restTemplate);
resultVo.setAttribute("rainbow",new DataItem(rainbow,"#FF69B4"));
//4.其他
String remark = "❤";
if(DataUtils.getBirthDays(WeChatConfigure.Love_Day) == 0){
remark = "今天是恋爱周年纪念日!永远爱你~";
}else if(girl_birthday == 0){
remark = "今天是xx宝贝的生日!生日快乐哟~";
}else if(boy_birthday == 0){
remark = "今天是xx的生日!别忘了好好爱他~";
}
resultVo.setAttribute("remark",new DataItem(remark,"#FF1493"));
//5.发送请求,推送消息
String responseStr = restTemplate.postForObject(WeChatConfigure.Send_URL, resultVo, String.class, DataUtils.getAccessToken(restTemplate));
printPushLog(responseStr);
}
/**
* 打印 response log
* @param responseStr
*/
private void printPushLog(String responseStr){
JSONObject jsonObject = JSONObject.parseObject(responseStr);
String msgCode = jsonObject.getString("errcode");
String msgContent = jsonObject.getString("errmsg");
SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
System.out.println("[ " + dateFormat.format(new Date()) + " ] : messageCode=" + msgCode + ",messageContent=" + msgContent);
}
}
4. タイミングタスクをカプセル化する
@Component
public class PushTask {
@Autowired
WeChatController weChatController;
//每日 早上9点 定时推送
@Scheduled(cron = "0 0 9 * * ?")
public void scheduledPush(){
weChatController.push();
}
}
5.Tencent Cloudをパッケージ化して展開する
プロジェクトは jar パッケージにパッケージ化されて Tencent クラウド サーバーにアップロードされ、直接実行することで毎日のプッシュを実現できます。
#nohup指令 后台启动jar包,日志信息输出到log.file文件
nohup java -jar xxx-0.0.1-SNAPSHOT.jar > log.file 2>&1 &