一、开发准备
1、公众号的AppID,AppSecret
AppID:
开发者ID是公众号开发识别码,配合开发者密码可调用公众号的接口能力。
AppSecret:
开发者密码是校验公众号开发者身份的密码,具有极高的安全性。切记勿把密码直接交给第三方开发者或直接存储在代码中。如需第三方代开发公众号,请使用授权方式接入
2、在公众号设置中选择功能设置,下载TXT文件放到web服务器的目录下,将域名填写至白名单等
3、获取access_token
public String getSubStableAccessToken() {
//token有效期为7200s,需要保存起来,先从redis中获取accessToken,没有则请求获取
String accessToken = stringRedisTemplate.opsForValue().get(WxConstant.SHARE_FLY_SUB_STABLE_ACCESS_TOKEN);
if (StringUtils.isBlank(accessToken)) {
Map<String, String> params = new HashMap<String, String>();
params.put("grant_type", "client_credential");
params.put("appid", userSystemConstant.getWxSubAppId());
params.put("secret", userSystemConstant.getWxSubSecret());
String res = null;
try {
res = HttpsRequestUtil.httpsRequest("https://api.weixin.qq.com/cgi-bin/stable_token", "POST", null, JSONObject.toJSONString(params));
} catch (Exception e) {
e.printStackTrace();
}
JSONObject tokenResult = JSONObject.parseObject(res);
if (res.indexOf("access_token") == -1) {
log.info("获取公众号access_token有误:{}", tokenResult);
return null;
}
accessToken = tokenResult.getString("access_token");
long expiresIn = tokenResult.getLong("expires_in");
//保存进redis
stringRedisTemplate.opsForValue().set(WxConstant.SHARE_FLY_SUB_STABLE_ACCESS_TOKEN, accessToken, expiresIn, TimeUnit.SECONDS);
return accessToken;
}
return accessToken;
}
4、OpenID 与 UnionID
OpenID(全局不唯一) :
在关注者与公众号产生消息交互后,公众号可获得关注者的OpenID(加密后的微信号,每个用户对每个公众号的OpenID是唯一的。对于不同公众号,同一用户的OpenID不同)
UnionID(全局唯一) :
请注意,如果开发者有在多个公众号,或在公众号、移动应用之间统一用户帐号的需求,需要前往微信开放平台(open.weixin.qq.com)绑定公众号后,才可利用UnionID机制来满足上述需求。
可以根据UnionID找到对应的微信公众号的UnionID
(1)同步微信公众号用户
实体类
@Data
@NoArgsConstructor
@AllArgsConstructor
public class UserSubInfoDO implements Serializable {
private static final long serialVersionUID = 8534660282946889549L;
private Integer id;
private String openId;
private String unionId;
private String nickName;
private Integer regStatus;
private Integer deleted;
private Date createTime;
private Date updateTime;
public UserSubInfoDTO buildUserSbuInfoDTO() {
return UserSubInfoDTO.builder().openId(openId).unionId(unionId).subscribe(this.deleted == 0 ? 1 : 0).build();
}
}
service层
@Override
public ResultBody moveSubUserToTest() {
String accessToken = this.getSubStableAccessToken();
String url = "https://api.weixin.qq.com/cgi-bin/user/get?access_token=" + accessToken;
String jsonStr = HttpsRequestUtil.httpsGet(url, null);
log.info("all users:{}", jsonStr);
JSONObject obj = JSONObject.parseObject(jsonStr);
List<String> lists = JSONArray.parseArray(obj.getJSONObject("data").get("openid").toString(), String.class);
int count = 0;
for (String openid : lists) {
Integer integer = userSubInfoDao.countUserSubInfo(openid);
if (integer == 0) {
count++;
UserSubInfoDO sub = new UserSubInfoDO();
sub.setOpenId(openid);
sub.setRegStatus(1);
sub.setDeleted(0);
sub.setCreateTime(new Date());
userSubInfoDao.insertUserSubInfo(sub);
}
}
log.info("User count: {}", count);
return ResultBody.success();
}
dao层
<select id="countUserSubInfo" resultType="Integer" parameterType="String">
select count(*)
from user_sub_info u
where u.open_id = #{openId} and is_deleted = 0
</select>
<insert id="insertUserSubInfo" useGeneratedKeys="true"
keyProperty="id" parameterType="com.mmc.iuav.user.entity.UserSubInfoDO">
insert into user_sub_info
(
open_id,union_id,nick_name,reg_status,is_deleted,create_time,update_time
)
values
(
#{openId},#{unionId},#{nickName},#{regStatus},#{deleted},#{createTime},#{updateTime}
)
</insert>
(2)同步微信公众号用户unionId
service层
@Override
public ResultBody moveSubUserUnionIdToTest() {
String accessToken = this.getSubStableAccessToken();
String url = "https://api.weixin.qq.com/cgi-bin/user/get?access_token=" + accessToken + "&next_openid=" + "oB6kk6obNektxM62mjj9PJHPtcME";
String jsonStr = HttpsRequestUtil.httpsGet(url, null);
log.info("all users:{}", jsonStr);
JSONObject obj = JSONObject.parseObject(jsonStr);
List<String> lists = JSONArray.parseArray(obj.getJSONObject("data").get("openid").toString(), String.class);
int count = 0;
for (String openid : lists) {
UserSubInfoDO userSubInfo = userSubInfoDao.getUserSubInfo(openid);
if (userSubInfo != null) {
String InfoUrl = "https://api.weixin.qq.com/cgi-bin/user/info?access_token=" + accessToken + "&openid=" + openid
+ "&lang=zh_CN";
String jsonStr1 = HttpsRequestUtil.httpsGet(InfoUrl, null);
JSONObject obj1 = JSONObject.parseObject(jsonStr1);
userSubInfo.setUnionId(obj1.getString("unionid"));
userSubInfo.setRegStatus(1);
userSubInfoDao.updateUserSubInfo(userSubInfo);
}
}
log.info("User count: {}", count);
return ResultBody.success();
}
dao层
<select id="getUserSubInfo" resultMap="userSubInfoResultMap" parameterType="String">
select u.id,u.open_id,u.union_id,u.nick_name,u.reg_status,
u.is_deleted,u.create_time,u.update_time
from user_sub_info u
where u.open_id = #{openId}
</select>
<update id="updateUserSubInfo"
parameterType="com.mmc.iuav.user.entity.UserSubInfoDO">
update user_sub_info
<set>
<if test="unionId != null and unionId != '' ">
union_id = #{unionId},
</if>
<if test="nickName != null">
nick_name = #{nickName},
</if>
<if test="regStatus != null">
reg_status = #{regStatus},
</if>
</set>
where open_id = #{openId}
</update>
以上可以通过unionId获取对应的微信公众号中的用户的openId
二、准备关键数据
1、获取touser
touser就是上述获取到的微信公众号中的用户的openId
2、获取模版id(templateId)
(1)、申请模版消息
进入公众号,找到功能-模板消息-我的末班-从模板库中添加,进行新增模板库(需要审核)
得到模版ID
3、获取data数据
public JSONObject spellMsg(String name, String result){
JSONObject data=new JSONObject();
//申请项目{{thing3.DATA}}
JSONObject thing3=new JSONObject();
thing3.put("value",name);
data.put("thing3",keyword1);
//审核时间{{time6.DATA}}
JSONObject time6=new JSONObject();
time6.put("value",new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()));
data.put("time6",keyword4);
//审核结果{{thing5.DATA}}
JSONObject thing5=new JSONObject();
thing5.put("value",payAccount);
data.put("thing5",result);
return data;
}
三、相关接口准备
1、实体类
@Builder
@Data
@NoArgsConstructor
@AllArgsConstructor
public class WxMsgVO implements Serializable {
private static final long serialVersionUID = 107288179600606045L;
@ApiModelProperty(value = "用户id")
private Integer userAccountId;
@ApiModelProperty(value = "公众号的openId")
private String touser;
@ApiModelProperty(value = "unionId-全局通用")
private String unionId;
@ApiModelProperty(value = "模板id")
@NotNull(message = "模板id不能为空")
private String templateId;
@ApiModelProperty(value = "网页跳转连接")
private String url;
@ApiModelProperty(value = "小程序APPID")
private String wxAppletAppId;
@ApiModelProperty(value = "小程序跳转连接")
private String appletPath;
@ApiModelProperty(value = "标题")
private MsgData title;
@ApiModelProperty(value = "消息备注")
private MsgData remark;
@ApiModelProperty(value = "按照接口文档传数据")
private JSONObject dataObject;
public String buildTemplateMsg() {
JSONObject msg = new JSONObject();
msg.put("touser", this.touser);
msg.put("template_id", this.templateId);
// 跳转页面
if (!StringUtils.isBlank(this.url)) {
msg.put("url", this.url);
}
// 跳转小程序页面设置
if (!StringUtils.isBlank(this.wxAppletAppId) && !StringUtils.isBlank(this.appletPath)) {
JSONObject mini = new JSONObject();
mini.put("appid", this.wxAppletAppId);
mini.put("pagepath", this.appletPath);
msg.put("miniprogram", mini);
}
msg.put("data", this.dataObject);
return msg.toString();
}
}
2、service层
获取accessToken
@Override
public String getSubStableAccessToken() {
//token有效期为7200s,需要保存起来,先从redis中获取accessToken,没有则请求获取
String accessToken = stringRedisTemplate.opsForValue().get(WxConstant.SHARE_FLY_SUB_STABLE_ACCESS_TOKEN);
if (StringUtils.isBlank(accessToken)) {
Map<String, String> params = new HashMap<String, String>();
params.put("grant_type", "client_credential");
params.put("appid", userSystemConstant.getWxSubAppId());
params.put("secret", userSystemConstant.getWxSubSecret());
String res = null;
try {
res = HttpsRequestUtil.httpsRequest("https://api.weixin.qq.com/cgi-bin/stable_token", "POST", null, JSONObject.toJSONString(params));
} catch (Exception e) {
e.printStackTrace();
}
JSONObject tokenResult = JSONObject.parseObject(res);
if (res.indexOf("access_token") == -1) {
log.info("获取公众号access_token有误:{}", tokenResult);
return null;
}
accessToken = tokenResult.getString("access_token");
long expiresIn = tokenResult.getLong("expires_in");
//保存进redis
stringRedisTemplate.opsForValue().set(WxConstant.SHARE_FLY_SUB_STABLE_ACCESS_TOKEN, accessToken, expiresIn, TimeUnit.SECONDS);
return accessToken;
}
return accessToken;
}
@Override
public ResultBody sendSubTemplateMsg(WxMsgVO ws) {
String accessToken = null;
try {
accessToken = this.getSubStableAccessToken();
String url = "https://api.weixin.qq.com/cgi-bin/message/template/send?access_token=" + accessToken;
String param = ws.buildTemplateMsg();
String result = HttpHelper.httpPost(url, param);
log.info("模板消息发送结果:{}", result);
} catch (Exception e) {
log.error("accessToken获取失败:{}", e.getMessage());
return ResultBody.error(ResultEnum.WX_ACCESS_TOKEN_ERROR.getResultCode(),
ResultEnum.WX_ACCESS_TOKEN_ERROR.getResultMsg() + e.getMessage());
}
return ResultBody.success();
}
3、Http请求
public class HttpHelper {
// get 请求
public static String httpGet(String url, Header[] headers) throws Exception {
HttpUriRequest uriRequest = new HttpGet(url);
if (null != headers) {
uriRequest.setHeaders(headers);
}
CloseableHttpClient httpClient = null;
try {
httpClient = declareHttpClientSSL(url);
CloseableHttpResponse httpresponse = httpClient.execute(uriRequest);
HttpEntity httpEntity = httpresponse.getEntity();
String result = EntityUtils.toString(httpEntity, REQ_ENCODEING_UTF8);
return result;
} catch (ClientProtocolException e) {
out.println(String.format("http请求失败,uri{%s},exception{%s}", new Object[] { url, e }));
} catch (IOException e) {
out.println(String.format("IO Exception,uri{%s},exception{%s}", new Object[] { url, e }));
} finally {
if (null != httpClient) {
httpClient.close();
}
}
return null;
}
// post 请求
public static String httpPost(String url, String params) throws Exception {
HttpPost post = new HttpPost(url);
post.addHeader("Content-Type", "application/json;charset=" + REQ_ENCODEING_UTF8);
// 设置传输编码格式
StringEntity stringEntity = new StringEntity(params, REQ_ENCODEING_UTF8);
stringEntity.setContentEncoding(REQ_ENCODEING_UTF8);
post.setEntity(stringEntity);
HttpResponse httpresponse = null;
CloseableHttpClient httpClient = null;
try {
httpClient = declareHttpClientSSL(url);
httpresponse = httpClient.execute(post);
HttpEntity httpEntity = httpresponse.getEntity();
String result = EntityUtils.toString(httpEntity, REQ_ENCODEING_UTF8);
return result;
} catch (ClientProtocolException e) {
out.println(String.format("http请求失败,uri{%s},exception{%s}", new Object[] { url, e }));
} catch (IOException e) {
out.println(String.format("IO Exception,uri{%s},exception{%s}", new Object[] { url, e }));
} finally {
if (null != httpClient) {
httpClient.close();
}
}
return null;
}
private static CloseableHttpClient declareHttpClientSSL(String url) {
if (url.startsWith("https://")) {
return sslClient();
} else {
return HttpClientBuilder.create().setConnectionManager(httpClientConnectionManager).build();
}
}
/**
* 设置SSL请求处理
*
*/
private static CloseableHttpClient sslClient() {
try {
SSLContext ctx = SSLContext.getInstance("TLS");
X509TrustManager tm = new X509TrustManager() {
@Override
public X509Certificate[] getAcceptedIssuers() {
return null;
}
@Override
public void checkClientTrusted(X509Certificate[] xcs, String str) {
}
@Override
public void checkServerTrusted(X509Certificate[] xcs, String str) {
}
};
ctx.init(null, new TrustManager[] { tm }, null);
SSLConnectionSocketFactory sslConnectionSocketFactory = SSLConnectionSocketFactory.getSocketFactory();
return HttpClients.custom().setSSLSocketFactory(sslConnectionSocketFactory).build();
} catch (NoSuchAlgorithmException e) {
throw new RuntimeException(e);
} catch (KeyManagementException e) {
throw new RuntimeException(e);
}
}
private static final String REQ_ENCODEING_UTF8 = "utf-8";
private static PoolingHttpClientConnectionManager httpClientConnectionManager;
public HttpHelper() {
httpClientConnectionManager = new PoolingHttpClientConnectionManager();
httpClientConnectionManager.setMaxTotal(100);
httpClientConnectionManager.setDefaultMaxPerRoute(20);
}
// get 请求
public static String httpGet(String url) throws Exception {
return httpGet(url, null);
}
四、接口应用
WxMsgVO ws = new WxMsgVO();
String unionId = userServiceDao.getUserAccountById(applyInfo.getUserAccountId()).getUnionId();
// 查询对应的openId(touser)
String touser = userSubInfoDao.getUserSubInfoByUnionId(unionId).getOpenId();
JSONObject jsonObject = spellMsg(applyInfo.getName());
ws.setTouser(touser);
ws.setTemplateId("y6PHvZownxqR5hf4dSXaa2Lp1jeq3ccytd6G-kUQla4");
ws.setDataObject(jsonObject);
wxService.sendSubTemplateMsg(ws);
public JSONObject spellMsg(String name, String result){
JSONObject data=new JSONObject();
//申请项目{{thing3.DATA}}
JSONObject thing3=new JSONObject();
thing3.put("value",name);
data.put("thing3",keyword1);
//审核时间{{time6.DATA}}
JSONObject time6=new JSONObject();
time6.put("value",new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()));
data.put("time6",keyword4);
//审核结果{{thing5.DATA}}
JSONObject thing5=new JSONObject();
thing5.put("value",payAccount);
data.put("thing5",result);
return data;
}