1.微信公众平台
(1)打开测试号管理:
(2)关注自己对应的公众号:
这里的微信号即为open_id,但正式开发一定要自己获取open_id
(3)获取用户open_id【代码示例已给出】
有问题可自行查询策略:通过java方式获取微信用户openId_java获取openid-CSDN博客
注意:与微信公众号测试交互,必须内网穿透
启动内网穿透:ngork http 7676ngrok | API Gateway, IoT Device Gateway, Secure Tunnels for Containers, Apps & APIs,开放对应的后端端口
填写域名:09f7-2408-8226-8f12-aab0-b1-c083-28f4-f8ab.ngrok-free.app,没有http
(4)添加测试模板【自行设计消防微信报警提醒模板】
例如:
标题:
对接进度提醒
内容:
对接内容:{{AchievementName.DATA}}
成果方:{{SchoolName.DATA}}
需求方:{{EnterpriseName.DATA}}
备注:{{Remark.DATA}}
时间:{{JoinTime.DATA}}
反馈如下:
2.微信开发实现示例【消防请写成接口】
(1)导入依赖
<!-- WxJava公众号 -->
<dependency>
<groupId>com.github.binarywang</groupId>
<artifactId>weixin-java-mp</artifactId>
<version>3.6.0</version>
</dependency>
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi</artifactId>
<version>3.17</version>
</dependency>
(2)添加微信公众号配置
(3)配置类
package com.tyut.co_school.config;
import me.chanjar.weixin.mp.api.WxMpService;
import me.chanjar.weixin.mp.api.impl.WxMpServiceImpl;
import me.chanjar.weixin.mp.config.WxMpConfigStorage;
import me.chanjar.weixin.mp.config.impl.WxMpDefaultConfigImpl;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* @author xh
* @Date 2022/7/14
*/
@Configuration
public class WxConfig {
private final WxMpProperties wxMpProperties;
/**
* 构造注入
*
* @param wxMpProperties
*/
WxConfig(WxMpProperties wxMpProperties) {
this.wxMpProperties = wxMpProperties;
}
/**
* 微信客户端配置存储
*
* @return
*/
@Bean
public WxMpConfigStorage wxMpConfigStorage() {
WxMpDefaultConfigImpl configStorage = new WxMpDefaultConfigImpl();
// 公众号appId
configStorage.setAppId(wxMpProperties.getAppId());
// 公众号appSecret
configStorage.setSecret(wxMpProperties.getSecret());
// 公众号Token
// configStorage.setToken(wxMpProperties.getToken());
// 公众号EncodingAESKey
// configStorage.setAesKey(wxMpProperties.getAesKey());
return configStorage;
}
/**
* 声明实例
*
* @return
*/
@Bean
public WxMpService wxMpService() {
WxMpService wxMpService = new WxMpServiceImpl();
wxMpService.setWxMpConfigStorage(wxMpConfigStorage());
return wxMpService;
}
}
package com.tyut.co_school.config;
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
/**
* @author xh
* @Date 2022/7/14
*/
@Data
@Component
@ConfigurationProperties(prefix = "wx")
public class WxMpProperties {
/**
* 公众号appId
*/
private String appId;
/**
* 公众号appSecret
*/
private String secret;
/**
* 服务器地址
*/
private String url;
/**
* 前端服务器地址
*/
private String frontUrl;
/**
* 公众号token
*/
// private String token;
/**
* 公众号aesKey
*/
// private String aesKey;
}
(4)controller
package com.tyut.co_school.controller;
import com.google.gson.Gson;
import com.tyut.co_school.config.WxMpProperties;
import com.tyut.co_school.utils.R;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
//import org.springframework.boot.configurationprocessor.json.JSONObject;
import org.springframework.http.ResponseEntity;
import org.springframework.http.converter.StringHttpMessageConverter;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.client.RestTemplate;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
import java.util.*;
/**
* @author xh
* @Date 2022/7/14
*/
//@RestController
@Slf4j
@Controller
public class WxMsgController {
/**
* 获取微信配置信息
*/
@Autowired
private WxMpProperties wxMpProperties;
@GetMapping("/index")
public String index(){
return "/index";
}
/**
* 测试接收微信官方通知
*
* @param request
* @return
*/
@GetMapping(value = "/weChatToken", produces = {"application/json;charset=utf-8"})
@ResponseBody
public String notify(HttpServletRequest request) {
String token = "heart";
// 判断是否是Get请求
boolean isGet = request.getMethod().toLowerCase().equals("get");
if (isGet) {
// 微信加密签名
String signature = request.getParameter("signature");
// 时间戳
String timestamp = request.getParameter("timestamp");
// 随机数
String nonce = request.getParameter("nonce");
log.info("signature:{}, timestamp:{}, nonce:{}", signature, timestamp, nonce);
// 随机字符串
String echostr = request.getParameter("echostr");
// 1)将token、timestamp、nonce三个参数进行字典序排序
List<String> list = Arrays.asList(token, timestamp, nonce);
Collections.sort(list);
// 2)将三个参数字符串拼接成一个字符串进行sha1加密
StringBuilder stringBuilder = new StringBuilder();
for (String s : list){
stringBuilder.append(s);
}
try {
// 通过检验signature对请求进行校验,若校验成功则原样返回echostr,表示接入成功,否则接入失败
MessageDigest instance =
MessageDigest.getInstance("sha1");
//使⽤sha1进⾏加密,获得byte数组
byte[] digest =
instance.digest(stringBuilder.toString().getBytes());
StringBuilder sum = new StringBuilder();
for (byte b : digest) {
sum.append(Integer.toHexString((b >> 4) & 15));
sum.append(Integer.toHexString(b & 15));
}
// 3)开发者获得加密后的字符串可与 signature 对⽐,标识该请求来源于微信
if (!com.baomidou.mybatisplus.core.toolkit.StringUtils.isEmpty(signature) &&
signature.equals(sum.toString())) {
return echostr;
}
} catch (Exception e) {
log.info("测试微信公众号的接口配置信息发生异常:", e);
return "系统异常!";
}
} else {
return "非法请求!疑似黑客攻击,列入黑名单";
}
return null;
}
/**
* 获取access_token,access_token是公众号的全局唯一接口调用凭据
*/
@GetMapping("/get_wx_token")
@ResponseBody
public Map<String, Long> getWxToken() {
RestTemplate restTemplate = new RestTemplate();
Map<String, String> map = new HashMap<>();
map.put("AppId", wxMpProperties.getAppId());
map.put("AppSecret", wxMpProperties.getSecret());
// 调用微信公众号接口
ResponseEntity<String> forEntity = restTemplate.getForEntity("https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid={AppId}&secret={AppSecret}", String.class, map);
// 解析JSON
String body = forEntity.getBody();
log.info("发送token获得到的body:{}", body);
Gson gson = new Gson();
HashMap<String, Object> jsonObject = gson.fromJson(body, HashMap.class);
String accessToken = String.valueOf(jsonObject.get("access_token"));
Double expiresIn = Double.parseDouble(String.valueOf(jsonObject.get("expires_in")));//时效--7200s
return new HashMap<String, Long>() {{put(accessToken, expiresIn.longValue());}};
}
/**
* 获取微信用户code,并重定向获取用户openId
*
* @return
*/
@GetMapping("/getOpenIdUrl")
@ResponseBody
public R getOpenIdUrl() {
String url = wxMpProperties.getUrl();
String backUrl = url + "/api/getUserOpenId";
String getOpenIdUrl = "https://open.weixin.qq.com/connect/oauth2/authorize?appid=APPID&redirect_uri=" + backUrl + "&response_type=code&scope=snsapi_userinfo&state=1,0#wechat_redirect";
getOpenIdUrl = getOpenIdUrl.replace("APPID", wxMpProperties.getAppId());
return R.ok(getOpenIdUrl);
}
@GetMapping("/wxlogin")
public String wxlogin(){
String url = "https://open.weixin.qq.com/connect/oauth2/authorize?appid=" + wxMpProperties.getAppId() +
"&redirect_uri=" + wxMpProperties.getUrl()+ "/api/getUserOpenId" +
"&response_type=code" +
"&scope=snsapi_userinfo" +
"&state=STATE#wechat_redirect";
return "redirect:"+url;
}
/**
* 获取用户openId
*
* @return
* @throws IOException
*/
@GetMapping("/getUserOpenId")
public void getUserOpenId(HttpServletRequest request, HttpServletResponse response) throws IOException {
//获取code
String code = request.getParameter("code");
log.info("获取到微信授权后:code:{}", code);
//换取用户openid
String url = "https://api.weixin.qq.com/sns/oauth2/access_token?appid=APPID&secret=SECRET&code=CODE&grant_type=authorization_code";
url = url.replace("APPID", wxMpProperties.getAppId()).replace("SECRET", wxMpProperties.getSecret()).replace("CODE", code);
RestTemplate restTemplate = new RestTemplate();
//解决乱码,以utf-8编码格式获取,不加这个会乱码
restTemplate.getMessageConverters().set(1, new StringHttpMessageConverter(StandardCharsets.UTF_8));
ResponseEntity<String> forEntity = restTemplate.getForEntity(url, String.class);
// 解析JSON
String body = forEntity.getBody();
log.info("body:{}", body);
Gson gson = new Gson();
HashMap<String, Object> jsonObject = gson.fromJson(body, HashMap.class);
String openid = String.valueOf(jsonObject.get("openid"));
response.sendRedirect(wxMpProperties.getFrontUrl() + "?openId=" + openid);
// return "redirect:?openid=" + openid;
}
}
(5)微信公众号提醒模板entity
package com.tyut.co_school.entity.vo.wx;
import lombok.Data;
import java.util.Date;
/**
* Created with IntelliJ IDEA.
*
*/
@Data
public class SendMsgReq {
/**
* 对接成果
*/
private String achievementName;
/**
* 成果方
*/
private String schoolName;
/**
* 需求方
*/
private String enterpriseName;
/**
* 时间
*/
private String joinTime;
/**
* 备注
*/
private String remark;
/**
* 用户openId
*/
private String openId;
/**
* 模板ID
*/
private String templateId = "WT2nYHIvZtxR4oIcwFkjJ2uw5fGDDcQ2KzoW3xnjKWA";
/**
* 跳转网页
*/
private String url = "http://47.100.198.147:7777/#/app/join_view/";
}
(6)service实现类
package com.tyut.co_school.service.impl;
import com.tyut.co_school.entity.vo.wx.SendMsgReq;
import com.tyut.co_school.service.WxMsgService;
import com.tyut.co_school.utils.BusinessException;
import com.tyut.co_school.utils.enums.ErrorCode;
import lombok.extern.slf4j.Slf4j;
import me.chanjar.weixin.common.error.WxErrorException;
import me.chanjar.weixin.mp.api.WxMpService;
import me.chanjar.weixin.mp.bean.template.WxMpTemplateData;
import me.chanjar.weixin.mp.bean.template.WxMpTemplateMessage;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.List;
/**
* @author xh
* @Date 2022/7/14
*/
@Service
@Slf4j
public class WxMsgServiceImpl implements WxMsgService {
/**
* 微信公众号API的Service
*/
@Autowired
private WxMpService wxMpService;
@Override
public Boolean SendWxMsg(SendMsgReq sendMsgReq) {
// 发送模板消息接口
WxMpTemplateMessage templateMessage = WxMpTemplateMessage.builder()
// 接收者openid
.toUser(sendMsgReq.getOpenId())
// 模板id
.templateId(sendMsgReq.getTemplateId())
// 模板跳转链接,预约平台地址
.url(sendMsgReq.getUrl())
.build();
// 获取发送数据
// 添加模板数据
templateMessage
.addData(new WxMpTemplateData("AchievementName", sendMsgReq.getAchievementName(), "#FF00FF"))
.addData(new WxMpTemplateData("SchoolName", sendMsgReq.getSchoolName(), "#A9A9A9"))
.addData(new WxMpTemplateData("EnterpriseName", sendMsgReq.getEnterpriseName(), "#FF00FF"))
.addData(new WxMpTemplateData("JoinTime", sendMsgReq.getJoinTime().toString(), "#000000"))
.addData(new WxMpTemplateData("Remark", sendMsgReq.getRemark(), "#000000"))
;
String msgId = null;
try {
// 发送模板消息
msgId = wxMpService.getTemplateMsgService().sendTemplateMsg(templateMessage);
} catch (WxErrorException e) {
e.printStackTrace();
}
log.warn("·==++--·推送微信模板信息:{}·--++==·", msgId != null ? "成功" : "失败");
return msgId != null;
}
@Override
public Boolean SendWxMsgBatchs(List<SendMsgReq> sendMsgReqs) {
if(sendMsgReqs == null || sendMsgReqs.size() == 0) return false;
// 并行流处理
sendMsgReqs.stream().forEach(sendMsgReq -> {
String openid = sendMsgReq.getOpenId(); // 微信openid
String templateId = sendMsgReq.getTemplateId(); // 模板id
String url = sendMsgReq.getUrl();
// 发送模板消息接口
WxMpTemplateMessage templateMessage = WxMpTemplateMessage.builder()
// 接收者openid
.toUser(openid)
// 模板id
.templateId(templateId)
// 模板跳转链接
.url(url)
.build();
// 添加模板数据
String achievementName = sendMsgReq.getAchievementName();
String schoolName = sendMsgReq.getSchoolName();
String enterpriseName = sendMsgReq.getEnterpriseName();
String joinTime = sendMsgReq.getJoinTime().toString();
templateMessage
.addData(new WxMpTemplateData("AchievementName", achievementName, "#FF00FF"))
.addData(new WxMpTemplateData("SchoolName", schoolName, "#A9A9A9"))
.addData(new WxMpTemplateData("EnterpriseName", enterpriseName, "#FF00FF"))
.addData(new WxMpTemplateData("JoinTime", joinTime, "#000000"))
.addData(new WxMpTemplateData("Remark", sendMsgReq.getRemark(), "#000000"))
;
try {
// 发送模板消息
wxMpService.getTemplateMsgService().sendTemplateMsg(templateMessage);
} catch (WxErrorException e) {
e.printStackTrace();
throw new BusinessException(ErrorCode.SYSTEM_ERROR, "发送模板消息出错");
}
});
return true;
}
}
(7)微信提醒测试
填入自己的open_id和模板数据进行测试