提交修改

This commit is contained in:
quyixiao 2025-11-12 13:04:53 +08:00
parent 8d5fcc2c70
commit ebca091a83
4 changed files with 127 additions and 48 deletions

View File

@ -37,6 +37,8 @@ public class WeChatUtils {
public static final String wechatQrcodeUrl = "https://api.weixin.qq.com/wxa/getwxacodeunlimit?access_token=";
public static final String genwxashortlinkUrl= "https://api.weixin.qq.com/wxa/genwxashortlink?access_token=";
@Autowired
private RedisUtils redisUtils;
@ -48,6 +50,14 @@ public class WeChatUtils {
@Value("${eb.config.weixin.pay.appSecret}")
private String appSecret;
/***
* 重试次数
*/
public final static int try_count = 3;
public final static String access_token_is_invalid = "access_token is invalid";
public static final String jsapi_acessToken = "jsapi_acessToken";
@ -88,29 +98,23 @@ public class WeChatUtils {
return null;
}
public AccessTokenDTO getAccessTokenDTO() {
public AccessTokenDTO getAccessTokenDTO(boolean useCache) {
AccessTokenDTO accessTokenDTO = null;
String acessTokenJson = redisUtils.get(jsapi_acessToken);
if (StringUtils.isNotEmpty(acessTokenJson)) {
if (StringUtils.isNotEmpty(acessTokenJson) && useCache) {
log.info("getAccessTokenDTO redis acessToken :{}", acessTokenJson);
accessTokenDTO = JSONObject.parseObject(acessTokenJson, AccessTokenDTO.class);
} else {
accessTokenDTO = getAccessToken(appid, appSecret);
String url = String.format(wechatAccessTokenUrl, appid, appSecret);
String responseBody = HttpUtils.doGet(url);
log.info("getAccessToken responseBody:{}", responseBody);
accessTokenDTO = JSONObject.parseObject(responseBody, AccessTokenDTO.class);
redisUtils.set(jsapi_acessToken, JSON.toJSONString(accessTokenDTO), RedisUtils.one_hours);
}
return accessTokenDTO;
}
// 获取access_token
private AccessTokenDTO getAccessToken(String appid, String appSecret) {
String url = String.format(wechatAccessTokenUrl, appid, appSecret);
String responseBody = HttpUtils.doGet(url);
log.info("getAccessToken responseBody:{}", responseBody);
AccessTokenDTO accessTokenDTO = JSONObject.parseObject(responseBody, AccessTokenDTO.class);
return accessTokenDTO;
}
/**
* 生成小程序跳转短链 code
* https://blog.csdn.net/weixin_44548582/article/details/131868367
@ -122,23 +126,29 @@ public class WeChatUtils {
* @return
*/
public String jumpAppletShortUrl(String path, String query, Integer days) {
String accessToken = getAccessTokenDTO().getAccessToken();
for (int i = 0; i < try_count; i++) {
String accessToken = getAccessTokenDTO(true).getAccessToken();
String url = linkUrl + accessToken;
cn.hutool.json.JSONObject body = cn.hutool.json.JSONUtil.createObj();
body.putOpt("path", path); // 通过 URL Link 进入的小程序页面路径必须是已经发布的小程序存在的页面不可携带 query path 为空时会跳转小程序主页
body.putOpt("query", query); // 通过 URL Link 进入小程序时的query最大1024个字符只支持数字大小写英文以及部分特殊字符!#$&'()*+,/:;=?@-._~%
//链接过期类型0时间戳 1间隔天数
body.putOpt("expire_type", 1); // 默认值0.小程序 URL Link 失效类型失效时间0失效间隔天数1
String url = linkUrl + accessToken;
cn.hutool.json.JSONObject body = cn.hutool.json.JSONUtil.createObj();
body.putOpt("path", path); // 通过 URL Link 进入的小程序页面路径必须是已经发布的小程序存在的页面不可携带 query path 为空时会跳转小程序主页
body.putOpt("query", query); // 通过 URL Link 进入小程序时的query最大1024个字符只支持数字大小写英文以及部分特殊字符!#$&'()*+,/:;=?@-._~%
//链接过期类型0时间戳 1间隔天数
body.putOpt("expire_type", 1); // 默认值0.小程序 URL Link 失效类型失效时间0失效间隔天数1
//指定失效天数最多30
days = (days == null || days > 30) ? 30 : days;
body.putOpt("expire_interval", days); // 到期失效的URL Link的失效间隔天数生成的到期失效URL Link在该间隔时间到达前有效最长间隔天数为30天expire_type 1 必填
//指定失效天数最多30
days = (days == null || days > 30) ? 30 : days;
body.putOpt("expire_interval", days); // 到期失效的URL Link的失效间隔天数生成的到期失效URL Link在该间隔时间到达前有效最长间隔天数为30天expire_type 1 必填
body.putOpt("env_version", "trial"); // 默认值"release"要打开的小程序版本正式版为 "release"体验版为"trial"开发版为"develop"仅在微信外打开时生效
String result = HttpUtil.post(url, body.toJSONString(2));
log.info("jumpAppletShortUrl params:{}, result:{}", JSON.toJSONString(body), result);
return result;
body.putOpt("env_version", "trial"); // 默认值"release"要打开的小程序版本正式版为 "release"体验版为"trial"开发版为"develop"仅在微信外打开时生效
String result = HttpUtil.post(url, body.toJSONString(2));
log.info("jumpAppletShortUrl url:{} params:{}, result:{}", url, JSON.toJSONString(body), result);
if (StringUtils.isNotEmpty(result) && result.contains(access_token_is_invalid)) {
getAccessTokenDTO(false);
continue;
}
return result;
}
return null;
}
@ -153,26 +163,70 @@ public class WeChatUtils {
* @return
*/
public String jumpAppletSchemeUrl(String path, String query, Integer days) {
String url = schemeUrl + getAccessTokenDTO().getAccessToken();
cn.hutool.json.JSONObject body = cn.hutool.json.JSONUtil.createObj();
cn.hutool.json.JSONObject jumpWxa = cn.hutool.json.JSONUtil.createObj();
jumpWxa.putOpt("path", path); // 通过 scheme 码进入的小程序页面路径必须是已经发布的小程序存在的页面不可携带 querypath 为空时会跳转小程序主页
jumpWxa.putOpt("query", query); // 通过 scheme 码进入小程序时的 query最大1024个字符只支持数字大小写英文以及部分特殊字符!#$&'()*+,/:;=?@-._~%`
jumpWxa.putOpt("env_version", "release"); // 默认值"release"要打开的小程序版本正式版为"release"体验版为"trial"开发版为"develop"仅在微信外打开时生效
body.putOpt("jump_wxa", jumpWxa); // 跳转到的目标小程序信息
//链接过期类型0时间戳 1间隔天数
body.putOpt("expire_type", 1); // 默认值0到期失效的 scheme 码失效类型失效时间0失效间隔天数1
body.putOpt("is_expire", true);
//指定失效天数最多30
days = (days == null || days > 30) ? 30 : days;
body.putOpt("expire_interval", days); // 到期失效的 scheme 码的失效间隔天数生成的到期失效 scheme 码在该间隔时间到达前有效最长间隔天数为30天is_expire true expire_type 1 时必填
String post = HttpUtil.post(url, body.toJSONString(2));
log.info("jumpAppletSchemeUrl params:{}, result:{}", JSON.toJSONString(body), post);
cn.hutool.json.JSONObject result = cn.hutool.json.JSONUtil.parseObj(post);
return result.toJSONString(2);
for(int i = 0 ; i < try_count ;i ++){
String url = schemeUrl + getAccessTokenDTO(true).getAccessToken();
cn.hutool.json.JSONObject body = cn.hutool.json.JSONUtil.createObj();
cn.hutool.json.JSONObject jumpWxa = cn.hutool.json.JSONUtil.createObj();
jumpWxa.putOpt("path", path); // 通过 scheme 码进入的小程序页面路径必须是已经发布的小程序存在的页面不可携带 querypath 为空时会跳转小程序主页
jumpWxa.putOpt("query", query); // 通过 scheme 码进入小程序时的 query最大1024个字符只支持数字大小写英文以及部分特殊字符!#$&'()*+,/:;=?@-._~%`
jumpWxa.putOpt("env_version", "release"); // 默认值"release"要打开的小程序版本正式版为"release"体验版为"trial"开发版为"develop"仅在微信外打开时生效
body.putOpt("jump_wxa", jumpWxa); // 跳转到的目标小程序信息
//链接过期类型0时间戳 1间隔天数
body.putOpt("expire_type", 1); // 默认值0到期失效的 scheme 码失效类型失效时间0失效间隔天数1
body.putOpt("is_expire", true);
//指定失效天数最多30
days = (days == null || days > 30) ? 30 : days;
body.putOpt("expire_interval", days); // 到期失效的 scheme 码的失效间隔天数生成的到期失效 scheme 码在该间隔时间到达前有效最长间隔天数为30天is_expire true expire_type 1 时必填
String post = HttpUtil.post(url, body.toJSONString(2));
if(StringUtils.isNotEmpty(post) && post.contains(access_token_is_invalid)){
getAccessTokenDTO(false);
continue;
}
log.info("jumpAppletSchemeUrl url:{} params:{}, result:{}",url, JSON.toJSONString(body), post);
cn.hutool.json.JSONObject result = cn.hutool.json.JSONUtil.parseObj(post);
return result.toJSONString(2);
}
return null;
}
/**
* scheme 跳转微信小程序需中转H5
*
*https://developers.weixin.qq.com/miniprogram/dev/OpenApiDoc/qrcode-link/short-link/generateShortLink.html
* @return
*/
public String genShortlink(String path, String query, Integer days) {
String resul = null;
for(int i = 0 ; i < try_count ;i ++){
String url = genwxashortlinkUrl + getAccessTokenDTO(true).getAccessToken();
cn.hutool.json.JSONObject body = cn.hutool.json.JSONUtil.createObj();
body.putOpt("page_url", path); //通过 Short Link 进入的小程序页面路径必须是已经发布的小程序存在的页面可携带 query最大1024个字符
body.putOpt("page_title", query); // 页面标题不能包含违法信息超过20字符会用... 截断代替
body.putOpt("is_permanent", "false"); // 默认值false生成的 Short Link 类型短期有效false永久有效true
String post = HttpUtil.post(url, body.toJSONString(2));
log.info("genShortlink url:{} params:{}, result:{}",url, JSON.toJSONString(body), url);
if(StringUtils.isNotEmpty(post) && post.contains(access_token_is_invalid)){
getAccessTokenDTO(false);
continue;
}
log.info("genShortlink params:{}, result:{}", JSON.toJSONString(body), post);
cn.hutool.json.JSONObject result = cn.hutool.json.JSONUtil.parseObj(post);
return result.toJSONString(2);
}
return null;
}
}

View File

@ -0,0 +1,11 @@
package com.heyu.api.alibaba.request.vv;
import lombok.Data;
@Data
public class AppShortLinkRequest extends AppBaseRequest {
}

View File

@ -3,6 +3,7 @@ package com.heyu.api.controller.vv;
import com.heyu.api.alibaba.request.vv.AppLinkRequest;
import com.heyu.api.alibaba.request.vv.AppSchemeRequest;
import com.heyu.api.alibaba.request.vv.AppShortLinkRequest;
import com.heyu.api.common.annotation.Describe;
import com.heyu.api.data.utils.R;
import com.heyu.api.data.utils.WeChatUtils;
@ -38,6 +39,21 @@ public class AppLinkController {
}
/***
* 短链接
* http://localhost:8888/app/create/shortLink
*/
@Describe("生成")
@RequestMapping("/shortLink")
public R shortlink(@RequestBody AppShortLinkRequest request) {
String url = weChatUtils.genShortlink("abc", "89ew8392", 30);
return R.ok();
}
/***
* 链接
* http://localhost:8888/app/create/scheme

View File

@ -46,18 +46,16 @@ public class AppQrCodeController {
@RequestMapping("/create")
public R create(@RequestBody AppQrCodeRequest request) {
AccessTokenDTO accessTokenDTO = weChatUtils.getAccessTokenDTO();
AccessTokenDTO accessTokenDTO = weChatUtils.getAccessTokenDTO(true);
// 当前登录账号
String accountId = "123456";
String url = WeChatUtils.wechatQrcodeUrl + accessTokenDTO.getAccessToken();
Map<String, Object> body = new HashMap<>();
// 场景码请根据业务场景与前端约定
// 最大32个可见字符只支持数字大小写英文以及部分特殊字符!#$&'()*+,/:;=?@-._~其它字符请自行编码为合法字符因不支持%中文无法使用 urlencode 处理请使用其他编码方式
body.put("scene", String.format("a=%s", accountId));
body.put("env_version", envVersion);
// body.put("page", envVersion); 默认是主页页面 page例如 pages/index/index根路径前不要填加 /不能携带参数参数请放在scene字段里如果不填写这个字段默认跳主页面scancode_time为系统保留参数不允许配置
// 透明