最近は記事の更新頻度が低いですが、隔月くらいでしょうか。主に勉強中の内容のデモや仕事で使っている内容などを中心に記事を更新していきます。前回の記事より少し長く、目次に基づいて見るべき段落を見つけることができます。
ここではデモは 2 番目の方法についてのみ書かれており、最初の方法は後で補足される可能性があります。
方法 1:
ユーザーにメッセージをプッシュするにはユーザーの openid が必要です。ユーザーが公式アカウントをフォローすると、一意の openid が生成されます。アプレットはログインおよび登録時に openid を取得できますが、アプレットの openid は番号の openid は独立しており、返された Unionid を通じて 2 つを関連付けます。
方法 2 [この記事で説明] :
WeChat が提供するインターフェースを通じても実現でき、メッセージのプッシュは、アプレットにログインしているユーザーによって生成された openid を通じて実現できます (ユーザーがアプレットにログインしていない場合は、メッセージプッシュは実現できず、公式アカウントもフォローする必要があります。)
公式ドキュメントアドレス: https://developers.weixin.qq.com/miniprogram/dev/OpenApiDoc/mp-message-management/uniform-message/sendUniformMessage .html
注: 現在のミニ プログラムと公式アカウントは同じ主題の下にある必要があります
要求されるパラメータは主に次のとおりです。
- access_token は get リクエストを通じて取得できます
- touser ユーザーの openid、アプレットによって返される openid
- mp_template_msg 公式アカウントのメッセージパラメータをプッシュする
- appid 公式アカウントのID
- template_id テンプレートの ID
- URLテンプレートによってリダイレクトされるコンテンツ
- ミニプログラム テンプレートは、ジャンプ先の小さなプログラムを要求します。
- data アプレットによって送信されるメッセージの内容
パラメータの例
メッセージ送信の例
{
"touser":"OPENID",
"mp_template_msg":{
"appid":"APPID ",
"template_id":"TEMPLATE_ID",
"url":"http://weixin.qq.com/download",
"miniprogram":{
"appid":"xiaochengxuappid12345",
"pagepath":"index?foo=bar"
},
"data":{
"first":{
"value":"恭喜你购买成功!",
"color":"#173177"
},
"keyword1":{
"value":"巧克力",
"color":"#173177"
},
"keyword2":{
"value":"39.8元",
"color":"#173177"
},
"keyword3":{
"value":"2014年9月22日",
"color":"#173177"
},
"remark":{
"value":"欢迎再次购买!",
"color":"#173177"
}
}
}
}
成功データを返すインスタンス
{
"errCode": 0,
"errMsg": "openapi.uniformMessage.send:ok"
}
スプリングブートを統合する
使用される依存関係
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.73</version>
</dependency>
<!-- 缓存 用来保存access_token-->
<dependency>
<groupId>com.github.ben-manes.caffeine</groupId>
<artifactId>caffeine</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<optional>true</optional>
</dependency>
<!-- lombok包中携带Slf4j日志 -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
<!-- 定时任务 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-quartz</artifactId>
</dependency>
</dependencies>
- まず、access_tokenを取得する必要があります
公式ドキュメントのアドレス: http://caibaojian.com/wxwiki/9a186d136f1e9dce26e9593cadbc7130083b48d0.html
インターフェイス呼び出しを行う際には公式アカウントが必要になりますaccess_token
。これは、WeChat サーバーとの対話の資格情報に相当します。access_token は 2 時間ごとに更新されます。 、および新しいトークンが有効になると、前のトークンは無効になります。2 つのトークンを交互に使用すると、5 分以内は古いトークンと新しいトークンの両方が使用できるようになります。毎日インターフェイスを呼び出すことができる回数と、1 日あたりの最大呼び出し数には制限があります2000
。
1.1 WeChat関連の構成情報を保存するエンティティクラスを作成する
@Data
@Component
@PropertySource(value = "classpath:/application.yml")
@ConfigurationProperties(prefix = "wechat")
public class WeChatProperties {
/**
* 微信公众号的appid
*/
@Value("${appId}")
private String appId;
/**
* 微信小程序的secret
*/
@Value("${secret}")
private String secret;
/**
* 获取access_token的url
*/
@Value("${accessUrl}")
private String accessUrl;
/**
* 消息发送的id
*/
@Value("${uniformSend}")
private String uniformSend;
/**
* 微信小程序的appid
*/
@Value("${miniAppId}")
private String miniAppId;
/**
* 微信小程序跳转页面路径
*/
@Value("${pagepath}")
private String pagepath;
/**
* 获取token的定时任务corn
*/
@Value("${corn}")
private String corn;
}
1.2 ここでは、restTemplate インターフェイスのインターフェイス呼び出しを直接使用しています。httpclient も使用できますが、自由に選択できるため、
ここでrestTemplate を設定する必要があります。
/**
* @desc:
* @author: LiuChang
* @since: 2022/7/22
*/
@Configuration
public class RestTemplateConfig {
/**
* rest 模板
*
* @param clientHttpRequestFactory
* @return
*/
@Bean
public RestTemplate restTemplate(ClientHttpRequestFactory clientHttpRequestFactory) {
return new RestTemplate(clientHttpRequestFactory);
}
/**
* 请求连接池的配置信息
*
* @return
*/
@Bean
public ClientHttpRequestFactory simpleClientHttpRequestFactory() {
SimpleClientHttpRequestFactory factory = new SimpleClientHttpRequestFactory();
factory.setConnectTimeout(15000);
factory.setReadTimeout(5000);
return factory;
}
}
- yaml 設定ファイルで設定情報を指定する
server:
port: 7777
wechat:
#公众号的appid
appId:
#小程序的appid
miniAppId:
#小程序的secret
secret:
accessUrl: https://api.weixin.qq.com/cgi-bin/token
uniformSend: https://api.weixin.qq.com/cgi-bin/message/wxopen/template/uniform_send?access_token=
#小程序跳转的url
pagepath: pages/index
#定时任务执行
corn: 0 0 0/2 * * ?
- インターフェースを呼び出してトークンを取得する
@Resource
RestTemplate restTemplate;
@Resource
WeChatProperties weChatProperties;
public String getAccessToken() {
String url = weChatProperties.getAccessUrl();
// 这里的参数要和下面的Map Key值对应
String path = "?grant_type={grant_type}&appid={appid}&secret={secret}";
Map<String, String> params = new HashMap<>(3);
params.put("grant_type", "client_credential");
params.put("appid", weChatProperties.getMiniAppId());
params.put("secret", weChatProperties.getSecret());
ResponseEntity<String> forObject = restTemplate.getForEntity(url + path, String.class, params);
JSONObject jsonObject = JSONObject.parseObject(forObject.getBody());
String accessToken = jsonObject.getString("access_token");
if (null == accessToken) {
log.error("获取access_token失败");
} else {
//将token保存到缓存中
//caffeineCache.put("access_token", accessToken);
}
return accessToken;
}
- キャッシュを導入します(無視できます)
2.1 キャッシュ設定ファイル ここでは読み取りのパフォーマンスが良いため、キャッシュにカフェインを使用しています キャッシュを使用するのはプロジェクトのトークンのみなので、全体としてインポートされます redis/memoryCache も使用できます
/**
* @desc: token的缓存配置
* @author: LiuChang
* @since: 2022/7/22
*/
@Configuration
public class TokenCacheConfig {
@Bean
public Cache caffeineCache() {
return Caffeine.newBuilder()
// 设置最后一次写入或访问后经过固定时间过期
// .expireAfterWrite(90, TimeUnit.MINUTES)
// 初始的缓存空间大小
.initialCapacity(100)
// 缓存的最大条数
.maximumSize(1000)
.build();
}
}
2.2 キャッシュの使用
@Resource
Cache<String, Object> caffeineCache;
//获取
caffeineCache.getIfPresent("access_token")
//放入数据
caffeineCache.put("access_token", accessToken);
- タイミングタスクを導入します(無視できます)
3.1 実行する必要があるタスクのタイミング
@Slf4j
@Component
public class QuartzJob extends QuartzJobBean {
@Resource
WxMessageSendService wxMessageSendService;
@Override
protected void executeInternal(JobExecutionContext context) throws JobExecutionException {
try {
// 定时刷新token
String token = wxMessageSendService.getAccessToken();
log.info(token);
} catch (Exception e) {
log.error("任务失败 error:{}", e.getMessage());
}
}
}
3.2 スケジュールされたタスクの設定ファイル
@Configuration
public class QuartzConfig {
/**
* corn表达式
*/
@Value("${wechat.corn}")
private String restartCron;
@Bean
public JobDetail restartJob() {
return JobBuilder.newJob(QuartzJob.class).withIdentity("QuartzJob").storeDurably().build();
}
@Bean
public Trigger restartTrigger() {
CronScheduleBuilder scheduleBuilder = CronScheduleBuilder.cronSchedule(restartCron);
return TriggerBuilder.newTrigger().forJob(restartJob())
.withIdentity("QuartzJob").withSchedule(scheduleBuilder).build();
}
}
3.3 スタートアップクラスにアノテーションを追加する
@SpringBootApplication
@EnableScheduling
- 公式インターフェースを呼び出し、メッセージをプッシュテスト
メッセージコンテンツのエンティティクラス
/**
* @desc: 微信推送消息类
* @author: LiuChang
* @since: 2022/7/25
*/
@Data
@AllArgsConstructor
@NoArgsConstructor
public class WxMessageBean {
private MessageBean first;
private MessageBean keyword1;
private MessageBean keyword2;
private MessageBean keyword3;
private MessageBean keyword4;
private MessageBean remark;
/**
* 自定义构造,first和remark必传,keyword根据需要自定义个数
*
* @param first
* @param remark
* @param keyword
*/
public WxMessageBean(MessageBean first, MessageBean remark, MessageBean... keyword) {
this.first = first;
int count = 1;
for (MessageBean keyword1 : keyword) {
if (count == 1) {
this.keyword1 = keyword1;
} else if (count == 2) {
this.keyword2 = keyword1;
} else if (count == 3) {
this.keyword3 = keyword1;
} else if (count == 4) {
this.keyword4 = keyword1;
}
count++;
}
this.remark = remark;
}
}
@Data
@AllArgsConstructor
@NoArgsConstructor
public class MessageBean {
/**
* 消息内容
*/
private String value;
/**
* color
*/
private String color;
}
インターフェースの書き込み
@Service
@Slf4j
public class WxMessageSendService {
@Resource
RestTemplate restTemplate;
@Resource
WeChatProperties weChatProperties;
@Resource
Cache<String, Object> caffeineCache;
/**
* 推送消息
*
* @param openId
* @param message
* @return
*/
public String messageSend(String openId, Object message) {
String token = (String) caffeineCache.getIfPresent(ConstantEnum.ACCESS_TOKEN.getCode());
if (token == null) {
token = this.getAccessToken();
}
String url = weChatProperties.getUniformSend() + token;
// 这里的参数要和下面的Map Key值对应
JSONObject obj = new JSONObject();
JSONObject mpTemplateMsg = new JSONObject();
JSONObject mini = new JSONObject();
mini.put("appid", weChatProperties.getMiniAppId());
mini.put("pagepath", weChatProperties.getPagepath());
mpTemplateMsg.put("data", message);
mpTemplateMsg.put("miniprogram", mini);
///微信公众号appid
mpTemplateMsg.put("appid", weChatProperties.getAppId());
///微信公众号模板id
mpTemplateMsg.put("template_id", template_id);
obj.put("touser", openId);
obj.put("mp_template_msg", mpTemplateMsg);
ResponseEntity<String> forObject = restTemplate.postForEntity(url, obj, String.class);
JSONObject object = JSON.parseObject(forObject.getBody());
Integer errcode = (Integer) object.get("errcode");
if (errcode == 0) {
log.info("消息推送成功");
return null;
} else if (errcode == 40003) {
log.error("推送消息的openid错误,openid:{},消息内容:{}", openId, message);
return "推送消息的openid错误";
} else if (errcode == 43004) {
log.error("该用户未关注公众号,openid:{},消息内容:{}", openId, message);
return "该用户未关注公众号";
} else {
return null;
}
}
//下面还有个方法是获取access_token的 这里省略没写,在上面获取token已经将方法写了
}
通話インターフェース
@RestController
@RequestMapping("/message_send")
public class WxMessageSendController {
@Resource
WxMessageSendService wxMessageSendService;
/**
* 通过openid和消息内容向指定用户推送公众号模板消息
*
* @return
*/
@PostMapping()
public String messageSend() {
WxMessageBean messageBean = new WxMessageBean(new MessageBean("你有新的待审批消息,请点击查看", "#173177"),
new MessageBean("footer", "#173177"),
new MessageBean("keyword1", "#173177"));
return wxMessageSendService.messageSend(openid, messageBean);
}
}
インターフェイスを呼び出した後の結果
予防:
- 2 つのアプリケーションとシークレットは正しく入力し、逆にしないでください。
- openid はユーザー アプレットの ID です
- access_token パラメータが正しく入力されている