首页 > 编程语言 >java微信公众平台----带参数二维码生成和扫描事件

java微信公众平台----带参数二维码生成和扫描事件

时间:2024-06-21 17:32:08浏览次数:14  
标签:return String 微信 org ---- import java ikeep com

功能是在详情页面点击按钮,生成二维码。打开微信扫码,扫码之后手机跳转到公众号并发送一条模板消息。点击模板消息,跳转到H5的详情页面。

参考推荐:https://blog.csdn.net/weixin_42720002/category_8977300.html
官方文档:https://developers.weixin.qq.com/doc/offiaccount/Account_Management/Generating_a_Parametric_QR_Code.html
测试账号:https://mp.weixin.qq.com/debug/cgi-bin/sandboxinfo?action=showinfo&t=sandbox/index
扫码登录获取测试账号的信息。


一,Controller

package com.ikeep.findaccount.web.controller.wechat;


import com.ikeep.api.Response;
import com.ikeep.findaccount.domain.wechat.WechatParam;
import com.ikeep.findaccount.service.wechat.WechatDeveloperModeService;
import com.ikeep.findaccount.service.wechat.impl.WechatDeveloperModeServiceImpl;
import com.ikeep.findaccount.util.WechatCheckoutUtil;
import com.ikeep.findaccount.web.annotation.IgnoreUserToken;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.ui.ModelMap;
import org.springframework.util.StreamUtils;
import org.springframework.web.bind.annotation.*;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.nio.charset.Charset;

 

@RestController
@RequestMapping("/wechat")
@IgnoreUserToken
public class WechatBindController {
private final Logger LOGGER = LogManager.getLogger(WechatDeveloperModeServiceImpl.class);

@Autowired
private WechatDeveloperModeService wechatDeveloperModeService;


/**
* 微信验证 微信公众号修改‘接口配置信息’会请求这个接口进行验证,验证成功则配置修改成功
* @param
* @return
*/
@GetMapping("/vatToken")
public String toVatifyToken(HttpServletRequest request, HttpServletResponse response, ModelMap map) {
String signature = request.getParameter("signature");
// 时间戳
String timestamp = request.getParameter("timestamp");
// 随机数
String nonce = request.getParameter("nonce");
// 随机字符串
String echostr = request.getParameter("echostr");
if (signature != null && WechatCheckoutUtil.checkSignature(signature, timestamp, nonce)) {
return echostr;
}
return "";
}

/**
* 消息回复 扫码成功后会触发扫码事件,微信会请求这个接口。对请求事件处理
* @param
* @return
*/
@PostMapping("/vatToken")
public String handleMessage(HttpServletRequest request, HttpServletResponse response, ModelMap map) {
String xmlData;
try {
xmlData = StreamUtils.copyToString(request.getInputStream(), Charset.forName("utf-8"));
} catch (IOException e) {
LOGGER.error("Io系统异常:{}",e.getMessage());
return "";
}
wechatDeveloperModeService.handleMessage(xmlData);
return "";

}

/**
* 获取二维码 前端请求的接口,调微信接口返回二维码链接
* @param
* @return
*/
@PostMapping("/getQrCode")
public Response getYJQrCode(@RequestBody WechatParam wechatParam) {
return wechatDeveloperModeService.getYJQrCode(wechatParam);
}
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
二,service

(1)微信验证工具类

package com.ikeep.findaccount.util;

import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;

/**
* @author zhubin
* @version 0.1
* @Pacakage com.ikeep.findaccount.util
* @Description 微信验证
* @create 2019/11/12 10:28
**/
public class WechatCheckoutUtil {

/**
* 这里写的token要与测试公众号Token 一致
*/
private static String TOKEN = "CY90_TOKEN";

/**
* 验证签名
* @param signature
* @param timestamp
* @param nonce
* @return
*/
public static boolean checkSignature(String signature, String timestamp, String nonce) {
String[] arr = new String[]{TOKEN, timestamp, nonce};
// 将token、timestamp、nonce三个参数进行字典序排序
// Arrays.sort(arr);
sort(arr);
StringBuilder content = new StringBuilder();
for (int i = 0; i < arr.length; i++) {
content.append(arr[i]);
}
MessageDigest md = null;
String tmpStr = null;

try {
md = MessageDigest.getInstance("SHA-1");
// 将三个参数字符串拼接成一个字符串进行sha1加密
byte[] digest = md.digest(content.toString().getBytes());
tmpStr = byteToStr(digest);
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
}
content = null;
// 将sha1加密后的字符串可与signature对比,标识该请求来源于微信
return tmpStr != null ? tmpStr.equals(signature.toUpperCase()) : false;

}

/**
* 将字节数组转换为十六进制字符串
* @param byteArray
* @return
*/
private static String byteToStr(byte[] byteArray) {
String strDigest = "";
for (int i = 0; i < byteArray.length; i++) {
strDigest += byteToHexStr(byteArray[i]);
}
return strDigest;

}

/**
* 将字节转换为十六进制字符串
* @param mByte
* @return
*/
private static String byteToHexStr(byte mByte) {
char[] Digit = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'};
char[] tempArr = new char[2];
tempArr[0] = Digit[(mByte >>> 4) & 0X0F];
tempArr[1] = Digit[mByte & 0X0F];
String s = new String(tempArr);
return s;
}

public static void sort(String a[]) {
for (int i = 0; i < a.length - 1; i++) {
for (int j = i + 1; j < a.length; j++) {
if (a[j].compareTo(a[i]) < 0) {
String temp = a[i];
a[i] = a[j];
a[j] = temp;
}
}
}
}

}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
(2)请求t工具类

package com.ikeep.findaccount.util;


import java.io.IOException;
import java.net.URI;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import org.apache.http.NameValuePair;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.utils.URIBuilder;
import org.apache.http.entity.ContentType;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.util.EntityUtils;

/**
* @author zhubin
* @version 0.1
* @Pacakage com.ikeep.findaccount.util
* @Description
* @create 2019/11/12 12:43
**/
public class HttpClientUtil {

public static String doGet(String url, Map<String, String> param) {

// 创建Httpclient对象
CloseableHttpClient httpclient = HttpClients.createDefault();

String resultString = "";
CloseableHttpResponse response = null;
try {
// 创建uri
URIBuilder builder = new URIBuilder(url);
if (param != null) {
for (String key : param.keySet()) {
builder.addParameter(key, param.get(key));
}
}
URI uri = builder.build();

// 创建http GET请求
HttpGet httpGet = new HttpGet(uri);

// 执行请求
response = httpclient.execute(httpGet);
// 判断返回状态是否为200
if (response.getStatusLine().getStatusCode() == 200) {
resultString = EntityUtils.toString(response.getEntity(), "UTF-8");
}
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
if (response != null) {
response.close();
}
httpclient.close();
} catch (IOException e) {
e.printStackTrace();
}
}
return resultString;
}

public static String doGet(String url) {
return doGet(url, null);
}

public static String doPost(String url, Map<String, String> param) {
// 创建Httpclient对象
CloseableHttpClient httpClient = HttpClients.createDefault();
CloseableHttpResponse response = null;
String resultString = "";
try {
// 创建Http Post请求
HttpPost httpPost = new HttpPost(url);
// 创建参数列表
if (param != null) {
List<NameValuePair> paramList = new ArrayList<>();
for (String key : param.keySet()) {
paramList.add(new BasicNameValuePair(key, param.get(key)));
}
// 模拟表单
UrlEncodedFormEntity entity = new UrlEncodedFormEntity(paramList,"utf-8");
httpPost.setEntity(entity);
}
// 执行http请求
response = httpClient.execute(httpPost);
resultString = EntityUtils.toString(response.getEntity(), "utf-8");
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
response.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}

return resultString;
}

public static String doPost(String url) {
return doPost(url, null);
}

public static String doPostJson(String url, String json) {
// 创建Httpclient对象
CloseableHttpClient httpClient = HttpClients.createDefault();
CloseableHttpResponse response = null;
String resultString = "";
try {
// 创建Http Post请求
HttpPost httpPost = new HttpPost(url);
// 创建请求内容
StringEntity entity = new StringEntity(json, ContentType.APPLICATION_JSON);
httpPost.setEntity(entity);
// 执行http请求
response = httpClient.execute(httpPost);
resultString = EntityUtils.toString(response.getEntity(), "utf-8");
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
response.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}

return resultString;
}

}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
(3)配置类

package com.ikeep.findaccount.service.wechat.utill;


/**
* @author zhubin
* @version 0.1
* @Pacakage com.ikeep.findaccount.service.wechat.utill
* @Description 微信的配置属性
* @create 2019/11/12 22:37
**/
public class WechatConfig {


/**
* 微信获取accessToken接口url
*/
public static final String ACCESS_TOKEN_URL = "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid={appId}&secret={appSecret}";

/**
* 获取ticket
*/
public static final String GET_QRCODE_URL = "https://api.weixin.qq.com/cgi-bin/qrcode/create?access_token={TOKEN}";

/**
* 换取二维码
*/
public static final String QR_CODE_URL = "https://mp.weixin.qq.com/cgi-bin/showqrcode?ticket={TICKET}";

/**
* 推送模板消息的url
*/
public static final String TEMPLATE_URL = "https://api.weixin.qq.com/cgi-bin/message/template/send?access_token={ACCESS_TOKEN}";

/**
* 创业90商家详情
*/
public static final String CY90_MERCHANT = "http://{cy90url}?merchantCode={merchantCode}&productId={productId}&merchantType={merchantType}&opendId={opendId}";

/**
* 创业90企业详情
*/
public static final String CY90_SHOP = "http://{cy90url}?shopCode={shopCode}&productId={productId}&merchantType={merchantType}&opendId={opendId}";

/**
* 模板消息key
*/
public static final String[] KEY = {"first", "keyword1", "keyword2", "keyword3","remark"};

/**
* 模板消息的值
*/
public static final String[] VALUE = {"你好,欢迎联系商家", "戊吉云-获取商家电话", "", "戊吉云电话", "点击拨打电话"};

}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
(4)实现类

package com.ikeep.findaccount.service.wechat.impl;

import com.alibaba.fastjson.JSONObject;
import com.ikeep.api.Response;
import com.ikeep.commons.utils.JsonUtils;
import com.ikeep.findaccount.domain.merchant.enums.MerchantTypeEnum;
import com.ikeep.findaccount.domain.wechat.TemplateParam;
import com.ikeep.findaccount.domain.wechat.WechatParam;
import com.ikeep.findaccount.domain.wechat.WechatTemplate;
import com.ikeep.findaccount.service.wechat.WechatBindService;
import com.ikeep.findaccount.service.wechat.WechatDeveloperModeService;
import com.ikeep.findaccount.service.wechat.utill.WechatConfig;
import com.ikeep.findaccount.util.HttpClientUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.w3c.dom.*;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;
import redis.clients.jedis.JedisCluster;
import javax.annotation.Resource;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import java.io.IOException;
import java.io.StringReader;
import java.time.LocalDateTime;
import java.time.ZoneOffset;
import java.time.format.DateTimeFormatter;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.Map;

/**
* @author zhubin
* @version 0.1
* @Pacakage com.ikeep.findaccount.service.wechat.impl
* @Description 微信开发者模式
* @create 2019/11/12 10:50
**/
@Service
@Slf4j
public class WechatDeveloperModeServiceImpl implements WechatDeveloperModeService{

//测试环境和正式环境切换需要变得的配置信息写在spring属性文件里
@Value("${wechat.appId}")
private String APPID;
@Value("${wechat.appsecret}")
private String APPSECRET;
@Value("${wechat.templateId}")
private String TEMPLATE_ID;
@Value("${wechat.cy90url}")
private String CY90URL;

DateTimeFormatter DF = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
private final String REDIS_KEY = "CY90_accesstoken";
@Resource
private JedisCluster jedisCluster;
@Autowired
private WechatBindService wechatBindService;

/**
* 获取微信二维码 scene_str 自定义的场景参数,触发扫码事件时,会携带此参数。由于需求需要多个参数,于是通过
* 下划线拼接
* @return
*/
@Override
public Response getYJQrCode(WechatParam wechatParam) {

log.info("开始获取二维码,参数wechatParam:{}", JsonUtils.toJson(wechatParam));
String merchantCode = wechatParam.getMerchantCode();
String productId = wechatParam.getProductId();
String merchantType = wechatParam.getMerchantType();
String freeOffer = wechatParam.getFreeOffer();
String scene_str = new StringBuilder(merchantCode).append("_").append(productId).append("_").append(merchantType).toString();
if (StringUtils.isNotBlank(freeOffer)){
scene_str = new StringBuilder(scene_str).append("_").append(freeOffer).toString();
}

String accessToken = getAccessToken();
if (StringUtils.isBlank(accessToken)){
return Response.failure("获取微信accessToken失败");
}
JSONObject req=new JSONObject();
JSONObject data=new JSONObject();
JSONObject datare=new JSONObject();
req.put("action_name","QR_LIMIT_STR_SCENE");
datare.put("scene_str",scene_str);
data.put("scene",datare);
req.put("action_info",data);
req.put("expire_seconds","604800");
log.info("获取二维码入参:{}", JsonUtils.toJson(req));
String url = WechatConfig.GET_QRCODE_URL.replace("{TOKEN}", accessToken);
String resut = HttpClientUtil.doPostJson(url, JsonUtils.toJson(req));
Map<String, String> map = JsonUtils.fromJson(resut, Map.class);
String ticket = map.get("ticket");
if (StringUtils.isBlank(ticket)){
return Response.failure(map.get("errmsg"));
}
log.info("获取二维码出参:{}",JsonUtils.toJson(resut));
String ticket1 = map.get("ticket");
log.info("获取ticket出参:{}",ticket1);
String returnUrl = WechatConfig.QR_CODE_URL.replace("{TICKET}", ticket1);
log.info("returnUrl:{}",returnUrl);
return Response.success(returnUrl);

}

/**
* 事件处理
* @param xmlData
* @return
*/
@Override
public Response handleMessage(String xmlData) {

log.info("开始事件处理:{}", xmlData);
Map<String, String> mapData = convertToMap(xmlData);
// 发送方帐号(open_id)
String fromUserName = mapData.get("FromUserName");
//消息类型
String msgType = mapData.get("MsgType");
//场景
String eventKey = mapData.get("EventKey");
// 事件消息
if ("event".equals(msgType)) {
// 区分事件推送
String event = mapData.get("Event");
log.info("****区分事件推送****" + event);
// 订阅事件 或 未关注扫描二维码事件
if ("subscribe".equals(event)) {
Response subscribe = toPostTemplateMessage(fromUserName, eventKey, "subscribe");
if (!subscribe.isSuccess()){
return subscribe;
}

// 已关注扫描二维码事件
} else if ("SCAN".equals(event)) {
Response scan = toPostTemplateMessage(fromUserName, eventKey, "SCAN");
if (!scan.isSuccess()){
return scan;
}
//取关事件
}else if ("unsubscribe".equals(event)){
wechatBindService.removeBinding(fromUserName);

} else if ("LOCATION".equals(event)) {
// todo 处理上报地理位置事件
} else if ("CLICK".equals(event)) {
// todo 处理点击菜单拉取消息时的事件推送事件
} else if ("VIEW".equals(event)) {
// todo 处理点击菜单跳转链接时的事件推送
}

}
return Response.success();
}

/**
* 推送模板消息
* @param opendId
* @param eventKey
*/
private Response toPostTemplateMessage(String opendId, String eventKey, String eventType){

log.info("开始执行推送消息,opendId :{}",opendId);

String freeOfferUrl = "&freeOffer={freeOffer}";
String[] split = eventKey.split("_");
String code;
String productId;
String type;
String freeoffece = null;
//关注扫码
if ("subscribe".equals(eventType)){
code = split[1];
productId = split[2];
type = split[3];
if (split.length == 5){
freeoffece = split[4];
}
}else {
code = split[0];
productId = split[1];
type = split[2];
if (split.length == 4){
freeoffece = split[3];
}
}


String accessToken = getAccessToken();
if (StringUtils.isBlank(accessToken)){
return Response.failure("获取微信accessToken失败");
}
String templateUrl = WechatConfig.TEMPLATE_URL.replace("{ACCESS_TOKEN}", accessToken);

WechatTemplate wechatTemplate =new WechatTemplate();
Map<String, TemplateParam> map = new HashMap<>(5);
TemplateParam templateParam;
String[] key = WechatConfig.KEY;
String[] value = WechatConfig.VALUE;
for(int i =0; i < value.length; i++){
templateParam = new TemplateParam();
if (i != 2){
templateParam.setValue(value[i]);
}else {
templateParam.setValue(LocalDateTime.now(ZoneOffset.of("+8")).format(DF));
}
if(i == 4){
templateParam.setColor("#0000FF");
}
map.put(key[i], templateParam);
}
wechatTemplate.setData(map);
wechatTemplate.setTouser(opendId);
wechatTemplate.setTemplate_id(TEMPLATE_ID);
String cy90DetailUrl;
if (MerchantTypeEnum.SHOP.getCode().toString().equals(type)){
cy90DetailUrl = WechatConfig.CY90_SHOP.replace("{cy90url}", CY90URL).replace("{shopCode}", code).replace("{productId}", productId).replace("{merchantType}", type).replace("{opendId}", opendId);

}else {
cy90DetailUrl = WechatConfig.CY90_MERCHANT.replace("{cy90url}", CY90URL).replace("{merchantCode}", code).replace("{productId}", productId).replace("{merchantType}", type).replace("{opendId}", opendId);
}
if (StringUtils.isNotBlank(freeoffece)){
cy90DetailUrl = new StringBuilder(cy90DetailUrl).append(freeOfferUrl).toString().replace("{freeOffer}", freeoffece);
}
wechatTemplate.setUrl(cy90DetailUrl);
String postStr = JsonUtils.toJson(wechatTemplate);
String result = HttpClientUtil.doPostJson(templateUrl, postStr);
log.info("微信推送消息结果:{}", result);

return Response.success();
}
/**
* xml转map
* @param xml
* @return
*/
private static Map<String,String> convertToMap(String xml){
Map<String, String> map = new LinkedHashMap<>();
try {
DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
DocumentBuilder db = dbf.newDocumentBuilder();
StringReader sr = new StringReader(xml);
InputSource is = new InputSource(sr);
Document document = db.parse(is);

Element root = document.getDocumentElement();
if(root != null){
NodeList childNodes = root.getChildNodes();
if(childNodes != null && childNodes.getLength()>0){
for(int i = 0;i < childNodes.getLength();i++){
Node node = childNodes.item(i);
if( node != null && node.getNodeType() == Node.ELEMENT_NODE){
map.put(node.getNodeName(), node.getTextContent());
}
}
}
}
} catch (DOMException e) {
e.printStackTrace();
} catch (ParserConfigurationException e) {
e.printStackTrace();
} catch (SAXException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
return map;
}


/**
* 获取access_Token
*
* @return
*/
private String getAccessToken() {

log.info("开始获取access_Token");
String accessToken;
try {
accessToken = jedisCluster.get(REDIS_KEY + APPID);
if (StringUtils.isNotBlank(accessToken)){
return accessToken;
}
}catch (Exception e){
log.error(e.getMessage());
}
String url = WechatConfig.ACCESS_TOKEN_URL.replace("{appId}", APPID).replace("{appSecret}", APPSECRET);
String accessTokenJsonStr = HttpClientUtil.doGet(url);
log.info("请求微信accessToken返回值:{}", accessTokenJsonStr);
if (StringUtils.isBlank(accessTokenJsonStr)){
log.error("请求微信返回值为空");
return null;
}
Map<String, Object> accessTokenMap = JsonUtils.fromJson(accessTokenJsonStr, Map.class);
log.info("转换map值:{}", JsonUtils.toJson(accessTokenMap));
if (accessTokenMap.isEmpty()){
log.error("accessTokenMap为空");
return null;
}
accessToken = (String) accessTokenMap.get("access_token");
if (StringUtils.isEmpty(accessToken)) {
Integer errCode = null;
String errMsg = null;
try {
errCode = (Integer)accessTokenMap.get("errcode");
errMsg = (String) accessTokenMap.get("errmsg");
} catch (Exception e) {
log.error("获取微信accessToken异常:{}", JsonUtils.toJson(accessTokenMap));
}
log.error("获取accessToken失败;errCode为:{},errMsg{}", errCode , errMsg);
return null;
}
//存入缓存,官方有效期为2小时
try {
jedisCluster.setex(REDIS_KEY + APPID, 2*60*60 - 5 , accessToken);
}catch (Exception e){
log.error(e.getMessage());
}
log.info("获取access_Token成功,accessToken:", accessToken);
return accessToken;
}
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329


三,发布
发布到正式环境,需求修改spring属性文件的配置信息。修改公众号的信息时,需要先部署项目,因为修改公众号配置会调校验的接口

问题
1.关注事件,场景参数EventKey前面会拼接“qrscene”,所以取值的时候注意一下。
2.扫码之后,公众号连续推送三条重复模板消息(三次重复事件),看返回值是否正确。“”或“success”
加粗样式

 


文章知识点与官方知识档案匹配,可进一步学习相关知识
————————————————

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。

原文链接:https://blog.csdn.net/weixin_43229729/article/details/103205052

标签:return,String,微信,org,----,import,java,ikeep,com
From: https://www.cnblogs.com/fswhq/p/18125595

相关文章

  • STM32单片机SPI通信详解
    文章目录1.SPI通信概述2.硬件电路3.移位示意图4.SPI时序基本单元5.SPI时序6.Flash操作注意事项7.SPI外设简介8.SPI框图9.SPI基本结构10. 主模式全双工连续传输11. 非连续传输12. 软件/硬件波形对比13.代码示例1.SPI通信概述SPI(SerialPeriphera......
  • Vue项目中elementUI表单部分验证validateField以及星号显示等问题
    原文地址:https://huaweicloud.csdn.net/63a0040fdacf622b8df90fa6.html一、红色必填星号红色星号不显示,可能是没有正确填写prop和字段名称,检查一遍有验证规则但不想加红色星号,可以在el-form标签上加 :hide-required-asterisk=“true”当没有添加验证规则,但又要加上红色......
  • 游泳耳机入耳式好,还是骨传导好?看这四大游泳耳机准没错!
    在热爱水上运动的朋友们眼中,选择一款适合游泳的耳机无疑是提升运动体验的关键。面对市面上众多的游泳耳机,入耳式和骨传导两种设计方式各有千秋。作为游泳爱好者或专业运动员,我们需要仔细权衡两者优劣,以确保在享受音乐的同时,也能保持对周围环境的警觉。(上图为部分测试过的游......
  • manim边学边做--BulletedList
    BulletedList是Mobjects分类中用来显示列表的class。使用BulletedList可以帮助我们快速生成一个对齐的列表结构。BulletedList在manim各个模块中的位置如上图中所示。1.主要参数BulletedList的主要参数有:参数名称类型说明itemslist列表的内容bufffloat列表内......
  • 游泳耳机品牌排行榜,10大实力超群的游泳耳机分享!
    在当今快节奏的生活中,运动已成为许多人不可或缺的一部分,不仅为了健康,也是释放压力、提升生活品质的有效方式。而随着科技与健身的深度融合,智能穿戴设备尤其是专为运动设计的耳机,正逐渐成为运动爱好者的新宠。对于热爱水上运动,特别是游泳的人群而言,一款性能卓越的游泳耳机,不仅能......
  • 游泳耳机戴久了对人体有危害吗?6大精心总结游泳耳机选购策略!
    随着科技的发展,游泳爱好者们如今能够享受到一边畅游池畔,一边聆听音乐带来的乐趣。然而,长时间佩戴游泳耳机并非无条件的好事,它可能对我们的听力健康产生影响。在享受水下世界的动感旋律的同时,我们有必要了解游泳耳机的潜在危害,并掌握科学的选购策略。(上图为之前测试过的部分......
  • 淘宝扭蛋机小程序:互联网时代下行业的发展动力
    近几年,扭蛋机在潮玩市场风靡,与各类IP合作,推出各种新颖有趣的扭蛋商品,吸引了众多的IP粉丝,他们会通过扭蛋机进行抽奖,获得喜欢的商品。目前,移动应用程序不断升级优化,“互联网+扭蛋机”模式迅速发展,成为了行业高质量发展的动力!在当下互联网时代中,人们逐渐热衷在网上进行购物,这也为......
  • 游泳耳机哪种款式好?四款热销游泳耳机绝对不能错过!
    游泳时享受音乐或者保持通讯联系,选择一款合适的游泳耳机至关重要。然而,在市场上选择合适的游泳耳机并不容易,特别是面对各种款式和功能的选择时。无论是传统的入耳式还是创新的骨传导耳机,每种设计都有其独特的优势和适用场景。(上图为部分测试过的游泳耳机)本文将带你深入了解......
  • EarMaster Pro中文版安装包下载及安装教程
    ​众所周知软件功能和优势:插上麦克风,演唱,拍手,或在电脑屏幕上演奏您的答案(您还能够选择在mid键盘上演奏答案)。很明显来自丹麦皇家歌曲学院的多媒体歌曲教育软件EarMasterPro以问答的交互形式,寓教于乐的视听方法,给专业和非专业歌曲人士以极大的歌曲学习帮助。根据大数据调......
  • PDFFactory&FinePrint中文版安装包下载及安装教程
    ​由此可知嵌入字体:确保文档中使用的原始字体能被正确显示,即使计算机中没有相应的字体。从总体上来看修复了导致快速连续打印多个文档时导致打印作业失败的错误。很明显修复了有时导致PDF文件中缺少文本的错误。值得一提的是拖放:轻松与Evernote,Gmail和其他基于云的功能集成。......