diff --git a/util/src/test/java/com/ccsens/util/JacksonUtilTest.java b/util/src/test/java/com/ccsens/util/JacksonUtilTest.java new file mode 100644 index 0000000..148c306 --- /dev/null +++ b/util/src/test/java/com/ccsens/util/JacksonUtilTest.java @@ -0,0 +1,23 @@ +package com.ccsens.util; + +import org.junit.Test; + +import java.io.IOException; + +/** + * @description: + * @author: whj + * @time: 2022/3/9 11:36 + */ +public class JacksonUtilTest { + + @Test + public void test01() throws IOException { + + String xml = "1646796877"; + + + System.out.println(JacksonUtil.xmlToJson(xml)); + System.out.println(JacksonUtil.xmlToBean(xml, NoticeDto.Notice.class)); + } +} diff --git a/util/src/test/java/com/ccsens/util/NoticeDto.java b/util/src/test/java/com/ccsens/util/NoticeDto.java new file mode 100644 index 0000000..3dc68f5 --- /dev/null +++ b/util/src/test/java/com/ccsens/util/NoticeDto.java @@ -0,0 +1,86 @@ +package com.ccsens.util; + +import cn.hutool.core.util.ArrayUtil; +import com.ccsens.util.enterprisewx.SHA1; +import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlProperty; +import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlRootElement; +import lombok.Data; + +import javax.validation.constraints.NotEmpty; +import java.security.NoSuchAlgorithmException; +import java.util.Arrays; + +/** + * @description: + * @author: whj + * @time: 2021/10/21 15:51 + */ +public class NoticeDto { + + /** + * 微信小程序通知校验 + */ + @Data + public static class ValidMsg{ + @NotEmpty + private String signature; + @NotEmpty + private String timestamp; + @NotEmpty + private String nonce; + @NotEmpty + private String echostr; + + + public boolean check(String token) throws NoSuchAlgorithmException { + String[] dict = {token, timestamp, nonce}; + Arrays.sort(dict); + String str = ArrayUtil.join(dict, ""); + String shaStr = SHA1.getSha1(str); + return signature.equals(shaStr); + } + } + + @Data + @JacksonXmlRootElement(localName = "xml") + public static class Notice { + /** + * 小程序的原始ID + */ + @JacksonXmlProperty(localName = "ToUserName") + private String ToUserName; + /** + * 发送者的openid + */ + @JacksonXmlProperty(localName = "FromUserName") + private String FromUserName; + @JacksonXmlProperty(localName = "CreateTime") + private String CreateTime; + /** + * text: 文本 Content + * image:PicUrl MediaId + * miniprogrampage: 小程序卡片 Title AppId PagePath ThumbUrl ThumbMediaId + */ + private String MsgType; + private String Event; + private String SessionFrom; + private String Content; + private String MsgId; + private String PicUrl; + + /**事件KEY值,qrscene_为前缀,后面为二维码的参数值*/ + private String EventKey; + /**二维码的ticket,可用来换取二维码图片*/ + private String Ticket; + + /** + * 图片消息媒体id,可以调用[获取临时素材]((getTempMedia)接口拉取数据。 + */ + private String MediaId; + private String Title; + private String AppId; + private String PagePath; + private String ThumbUrl; + private String ThumbMediaId; + } +} diff --git a/wechatutil/pom.xml b/wechatutil/pom.xml index ee55e01..ffc039f 100644 --- a/wechatutil/pom.xml +++ b/wechatutil/pom.xml @@ -16,11 +16,11 @@ - + util diff --git a/wechatutil/src/main/java/com/ccsens/wechatutil/WechatutilApplication.java b/wechatutil/src/main/java/com/ccsens/wechatutil/WechatutilApplication.java index a2d5472..e44b726 100644 --- a/wechatutil/src/main/java/com/ccsens/wechatutil/WechatutilApplication.java +++ b/wechatutil/src/main/java/com/ccsens/wechatutil/WechatutilApplication.java @@ -1,15 +1,15 @@ package com.ccsens.wechatutil; -import com.ccsens.cloudutil.ribbon.RibbonConfiguration; -import org.mybatis.spring.annotation.MapperScan; -import org.springframework.boot.SpringApplication; -import org.springframework.boot.autoconfigure.SpringBootApplication; -import org.springframework.boot.web.servlet.ServletComponentScan; -import org.springframework.cloud.client.circuitbreaker.EnableCircuitBreaker; -import org.springframework.cloud.openfeign.EnableFeignClients; -import org.springframework.context.annotation.ComponentScan; -import org.springframework.context.annotation.FilterType; -import org.springframework.scheduling.annotation.EnableAsync; +//import com.ccsens.cloudutil.ribbon.RibbonConfiguration; +//import org.mybatis.spring.annotation.MapperScan; +//import org.springframework.boot.SpringApplication; +//import org.springframework.boot.autoconfigure.SpringBootApplication; +//import org.springframework.boot.web.servlet.ServletComponentScan; +//import org.springframework.cloud.client.circuitbreaker.EnableCircuitBreaker; +//import org.springframework.cloud.openfeign.EnableFeignClients; +//import org.springframework.context.annotation.ComponentScan; +//import org.springframework.context.annotation.FilterType; +//import org.springframework.scheduling.annotation.EnableAsync; /** * @author 逗 diff --git a/wechatutil/src/main/java/com/ccsens/wechatutil/bean/dto/wxmini/NoticeDto.java b/wechatutil/src/main/java/com/ccsens/wechatutil/bean/dto/wxmini/NoticeDto.java index 629efc8..dcb275f 100644 --- a/wechatutil/src/main/java/com/ccsens/wechatutil/bean/dto/wxmini/NoticeDto.java +++ b/wechatutil/src/main/java/com/ccsens/wechatutil/bean/dto/wxmini/NoticeDto.java @@ -62,6 +62,12 @@ public class NoticeDto { private String Content; private String MsgId; private String PicUrl; + + /**事件KEY值,qrscene_为前缀,后面为二维码的参数值*/ + private String EventKey; + /**二维码的ticket,可用来换取二维码图片*/ + private String Ticket; + /** * 图片消息媒体id,可以调用[获取临时素材]((getTempMedia)接口拉取数据。 */ diff --git a/wechatutil/src/main/java/com/ccsens/wechatutil/bean/dto/wxofficial/WxQrCodeDto.java b/wechatutil/src/main/java/com/ccsens/wechatutil/bean/dto/wxofficial/WxQrCodeDto.java new file mode 100644 index 0000000..8a35e43 --- /dev/null +++ b/wechatutil/src/main/java/com/ccsens/wechatutil/bean/dto/wxofficial/WxQrCodeDto.java @@ -0,0 +1,54 @@ +package com.ccsens.wechatutil.bean.dto.wxofficial; + +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +/** + * @description: + * @author: whj + * @time: 2022/3/7 9:09 + */ +public class WxQrCodeDto { + + @Data + @ApiModel("生成带参数的二维码-公众号") + public static class Create{ + + @ApiModelProperty("该二维码有效时间,以秒为单位。 最大不超过2592000(即30天),此字段如果不填,则默认有效期为60秒。") + private Long expire_seconds; + @ApiModelProperty("二维码类型,QR_SCENE为临时的整型参数值,QR_STR_SCENE为临时的字符串参数值,QR_LIMIT_SCENE为永久的整型参数值,QR_LIMIT_STR_SCENE为永久的字符串参数值") + private String action_name; + @ApiModelProperty + private ActionInfo action_info; + public Create(){} + + public Create(Long expire_seconds, String action_name, Integer scene_id, String scene_str) { + this.expire_seconds = expire_seconds; + this.action_name = action_name; + this.action_info = new ActionInfo(); + Scene scene = new Scene(); + scene.setScene_id(scene_id); + scene.setScene_str(scene_str); + this.action_info.setScene(scene); + } + } + + + @Data + @ApiModel("生成带参数的二维码场景值1-公众号") + public static class ActionInfo { + private Scene scene; + } + + @Data + @ApiModel("生成带参数的二维码场景值2-公众号") + public static class Scene { + @ApiModelProperty("场景值ID,临时二维码时为32位非0整型,永久二维码时最大值为100000(目前参数只支持1--100000)") + private Integer scene_id; + @ApiModelProperty("场景值ID(字符串形式的ID),字符串类型,长度限制为1到64") + private String scene_str; + + } + +} diff --git a/wechatutil/src/main/java/com/ccsens/wechatutil/bean/vo/wxofficial/WxQrCodeVo.java b/wechatutil/src/main/java/com/ccsens/wechatutil/bean/vo/wxofficial/WxQrCodeVo.java new file mode 100644 index 0000000..9eba5d4 --- /dev/null +++ b/wechatutil/src/main/java/com/ccsens/wechatutil/bean/vo/wxofficial/WxQrCodeVo.java @@ -0,0 +1,25 @@ +package com.ccsens.wechatutil.bean.vo.wxofficial; + +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +/** + * @description: + * @author: whj + * @time: 2022/3/7 9:52 + */ +public class WxQrCodeVo { + + @Data + @ApiModel("生成带参数的二维码-公众号返回") + public static class QrCodeUrl{ + @ApiModelProperty("获取的二维码ticket,凭借此ticket可以在有效时间内换取二维码") + private String ticket; + @ApiModelProperty("该二维码有效时间,以秒为单位。 最大不超过2592000(即30天)") + private Long expire_seconds; + @ApiModelProperty("二维码图片解析后的地址,开发者可根据该地址自行生成需要的二维码图片") + private String url; + } + +} diff --git a/wechatutil/src/main/java/com/ccsens/wechatutil/util/WeiXinPropUtil.java b/wechatutil/src/main/java/com/ccsens/wechatutil/util/WeiXinPropUtil.java new file mode 100644 index 0000000..94c642e --- /dev/null +++ b/wechatutil/src/main/java/com/ccsens/wechatutil/util/WeiXinPropUtil.java @@ -0,0 +1,26 @@ +package com.ccsens.wechatutil.util; + +import com.ccsens.wechatutil.wxofficial.OfficialAccountSigninUtil; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Component; + +/** + * @description: + * @author: whj + * @time: 2022/3/4 17:22 + */ +@Component +public class WeiXinPropUtil { + public static String appIdOfficial; + public static String secretOfficial; + + @Value("${official.appId:}") + public void setAppIdOfficial(String appIdOfficial) { + WeiXinPropUtil.appIdOfficial = appIdOfficial; + } + @Value("${official.secret:}") + public void setApplication(String secretOfficial) { + WeiXinPropUtil.secretOfficial = secretOfficial; + } + +} diff --git a/wechatutil/src/main/java/com/ccsens/wechatutil/util/WxConstant.java b/wechatutil/src/main/java/com/ccsens/wechatutil/util/WxConstant.java index 3c4acb8..45ba859 100644 --- a/wechatutil/src/main/java/com/ccsens/wechatutil/util/WxConstant.java +++ b/wechatutil/src/main/java/com/ccsens/wechatutil/util/WxConstant.java @@ -1,5 +1,11 @@ package com.ccsens.wechatutil.util; +import cn.hutool.core.util.StrUtil; +import com.ccsens.util.JacksonUtil; +import com.ccsens.util.exception.BaseException; +import com.ccsens.wechatutil.bean.po.WxBaseEntity; + +import java.io.IOException; import java.util.HashMap; import java.util.Map; @@ -12,10 +18,16 @@ public class WxConstant { public static final String ANYRING = "anyring"; - /*** 小程序登录路径 */ - public static final String MINI_PROGRAM_LOGIN = "https://api.weixin.qq.com/sns/jscode2session?appid=%1$s&secret=%2$s&js_code=%3$s&grant_type=%4$s"; /*** 公众号获取accessToken */ public static final String URL_GET_OAUTH2_ACCESS_TOKEN = "https://api.weixin.qq.com/sns/oauth2/access_token?appid=%1$s&secret=%2$s&code=%3$s&grant_type=authorization_code"; + /*** 公众号发送订阅消息 */ + public static final String MESSAGE_TEMPLATE_SEND = "https://api.weixin.qq.com/cgi-bin/message/template/send?access_token=%1$s"; + /** 创建二维码 */ + public static final String QR_CODE_CREATE = "https://api.weixin.qq.com/cgi-bin/qrcode/create?access_token={}"; + + + /*** 小程序登录路径 */ + public static final String MINI_PROGRAM_LOGIN = "https://api.weixin.qq.com/sns/jscode2session?appid=%1$s&secret=%2$s&js_code=%3$s&grant_type=%4$s"; /*** 通过网页授权和openId获取用户信息 */ public static final String URL_GET_OAUTH2_USER_INFO = "https://api.weixin.qq.com/sns/userinfo?access_token=%1$s&openid=%2$s"; /*** 获取小程序码A */ @@ -26,16 +38,12 @@ public class WxConstant { public static final String URL_GET_WX_CODE_C = "https://api.weixin.qq.com/cgi-bin/wxaapp/createwxaqrcode?access_token=%1$s"; /*** 小程序获取accessToken */ public static final String URL_GET_ACCESS_TOKEN = "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=%1$s&secret=%2$s"; - /*** 公众号发送订阅消息 */ - public static final String MESSAGE_TEMPLATE_SEND = "https://api.weixin.qq.com/cgi-bin/message/template/send?access_token=%1$s"; /*** 小程序发送订阅消息 */ public static final String URL_SEND_SUBSCRIBE_MESSAGE = "https://api.weixin.qq.com/cgi-bin/message/subscribe/send?access_token=%1$s"; - /*** 小程序消息模板--支付 */ public static String SUBSCRIBE_MSG_ID_BUY = "bqdx4FW51G-JLU6HjL6LyvUbjpo4rFK6CPor2-w3VwM"; /*** 小程序消息模板--订单发送 */ public static String SUBSCRIBE_MSG_ID_ORDERSEND = "5-rhM8h3x1W82tHCt18g9iXyGELyUKQtBDZiFjgItOo"; - /*** 小程序登录路径内的type */ public static final String GRANT_TYPE = "authorization_code"; @@ -106,4 +114,14 @@ public class WxConstant { } + public static void pageResult(String result) { + try { + if (StrUtil.isEmpty(result) || null == JacksonUtil.jsonToBean(result, WxBaseEntity.class)) { + throw new BaseException(WxCodeError.WX_HTTP_ERROR); + } + } catch (IOException e) { + throw new BaseException(e.getMessage()); + } + } + } diff --git a/wechatutil/src/main/java/com/ccsens/wechatutil/wxofficial/OfficialQrCodeUtil.java b/wechatutil/src/main/java/com/ccsens/wechatutil/wxofficial/OfficialQrCodeUtil.java new file mode 100644 index 0000000..818675f --- /dev/null +++ b/wechatutil/src/main/java/com/ccsens/wechatutil/wxofficial/OfficialQrCodeUtil.java @@ -0,0 +1,62 @@ +package com.ccsens.wechatutil.wxofficial; + +import cn.hutool.core.util.StrUtil; +import com.alibaba.fastjson.JSONObject; +import com.ccsens.util.RestTemplateUtil; +import com.ccsens.wechatutil.bean.dto.wxofficial.WxQrCodeDto; +import com.ccsens.wechatutil.bean.vo.wxofficial.WxQrCodeVo; +import com.ccsens.wechatutil.util.WeiXinPropUtil; +import com.ccsens.wechatutil.util.WxConstant; +import com.ccsens.wechatutil.wxcommon.WxCommonUtil; +import lombok.extern.slf4j.Slf4j; + +/** + * @description: + * @author: whj + * @time: 2022/3/4 17:14 + */ +@Slf4j +public class OfficialQrCodeUtil { + /**临时的整型参数值*/ + public static final String QR_SCENE = "QR_SCENE"; + /**临时的字符串参数值*/ + public static final String QR_STR_SCENE = "QR_STR_SCENE"; + /**永久的整型参数值*/ + public static final String QR_LIMIT_SCENE = "QR_LIMIT_SCENE"; + /**永久的字符串参数值*/ + public static final String QR_LIMIT_STR_SCENE = "QR_LIMIT_STR_SCENE"; + + + /** + * 生成带参数的二维码 + * @param expire_seconds 有效时间 + * @param action_name 应用名 + * @param scene_id 场景值ID,临时二维码时为32位非0整型,永久二维码时最大值为100000 + * @param scene_str 场景值ID(字符串形式的ID),字符串类型,长度限制为1到64 + * @return 二维码路径 + */ + public static WxQrCodeVo.QrCodeUrl send (Long expire_seconds, String action_name, Integer scene_id, String scene_str){ + WxQrCodeDto.Create create = new WxQrCodeDto.Create(expire_seconds, action_name, scene_id, scene_str); + String url = String.format(WxConstant.QR_CODE_CREATE, WxCommonUtil.getAccessToken(WeiXinPropUtil.appIdOfficial, WeiXinPropUtil.secretOfficial)); + String response = RestTemplateUtil.postBody(url, create); + log.info("url: {}\nresponse: {}", url, response); + WxConstant.pageResult(response); + return JSONObject.parseObject(response, WxQrCodeVo.QrCodeUrl.class); + } + + + /** + * 生成带参数的二维码 + * @param create 二维码参数 + * @param appIdOfficia appId + * @param secretOfficial secret + * @return 二维码路径 + */ + public static WxQrCodeVo.QrCodeUrl send (WxQrCodeDto.Create create, String appIdOfficia, String secretOfficial){ + String url = StrUtil.format(WxConstant.QR_CODE_CREATE, WxCommonUtil.getAccessToken(appIdOfficia,secretOfficial)); + String response = RestTemplateUtil.postBody(url, create); + log.info("url: {}\nresponse: {}", url, response); + WxConstant.pageResult(response); + return JSONObject.parseObject(response, WxQrCodeVo.QrCodeUrl.class); + } +}