JAVA项目实战-微信小程序服务端开发(一)
<p><span style="font-size: 16px">微信小程序越来越普遍,本章将分享给大家,实现微信小程序授权,推送微信通知消息。具体内容可参考微信小程序开发文档服务端(https://developers.weixin.qq.com/miniprogram/dev/api-backend/)。</span></p><p>==========================================================</p>
<p> <span style="font-size: 18px"><strong> 封装工具类</strong></span></p>
<p>==========================================================</p>
<pre>package com.sf.vsolution.hb.sfce.util.wechat.appletService;<br><br>import com.alibaba.fastjson.JSON;<br>import com.alibaba.fastjson.JSONObject;<br>import com.sf.vsolution.hb.sfce.util.wechat.appletService.param.*;<br>import io.swagger.annotations.ApiImplicitParam;<br>import io.swagger.annotations.ApiImplicitParams;<br>import io.swagger.annotations.ApiOperation;<br>import lombok.extern.slf4j.Slf4j;<br>import org.springframework.beans.factory.annotation.Autowired;<br>import org.springframework.beans.factory.annotation.Value;<br>import org.springframework.data.redis.core.StringRedisTemplate;<br>import org.springframework.util.Base64Utils;<br>import org.springframework.util.StringUtils;<br><br>import javax.crypto.Cipher;<br>import javax.crypto.spec.IvParameterSpec;<br>import javax.crypto.spec.SecretKeySpec;<br>import java.security.spec.AlgorithmParameterSpec;<br>import java.util.HashMap;<br>import java.util.Map;<br>import java.util.Objects;<br>import java.util.concurrent.TimeUnit;<br><br>/**<br> * @description: 微信小程序服务端开发<br> * @author: zhucj<br> * @date: 2019-11-14 15:10<br> */<br>@Slf4j<br>public class AppletServiceUtil {<br><br> @Value("${applet.appId}")<br> private String appId;<br> @Value("${applet.secret}")<br> private String secret;<br><br> @Value("${wx.template.id}")<br> private String templateId;<br><br> @Value("${wx.template.page}")<br> private String templatePage;<br><br><br> @Autowired<br> private StringRedisTemplate redis;<br><br><br><br> @ApiOperation(value = "微信小程序-获取用户openId或手机号信息")<br> @ApiImplicitParams({<br> @ApiImplicitParam(name = "code",value = "微信授权code",required = true,dataType = "String"),<br> @ApiImplicitParam(name = "encryptedData",value = "完整用户信息的加密数据(不传只获取openId)",required = false,dataType = "String"),<br> @ApiImplicitParam(name = "iv",value = "加密算法的初始向量(如获取更多用户信息传)",required = false,dataType = "String")<br> })<br> public R getUserInfo(WeChatAuthorizeDto dto){<br> if (StringUtils.isEmpty(dto.getCode())){<br> throw new AppletServiceException(AppletServiceConstants.PARAM_ERROR_CODE,AppletServiceConstants.CODE_NULL_ERROR);<br> }<br> //封装请求参数map<br> Map map = new HashMap();<br> map.put("appid",appId);<br> map.put("secret",secret);<br> map.put("js_code",dto.getCode());<br> map.put("grant_type","authorization_code");<br> String resultString = HttpClientUtil.doGet(AppletServiceConstants.AUTH_CODE_API, map);<br> AuthCodeResponse authCodeResponse = JSON.parseObject(resultString, AuthCodeResponse.class);<br> //判断请求返回结果,errcode = 0 请求成功<br> if (!Objects.equals(authCodeResponse.getErrcode(),AppletServiceConstants.SUCCESS_CODE)){<br> log.error("小程序授权失败返回错误码:{},错误信息:{}",authCodeResponse.getErrcode(),authCodeResponse.getErrmsg());<br> throw new AppletServiceException(AppletServiceConstants.PARAM_ERROR_CODE,AppletServiceConstants.AUTHORIZATION_ERROR);<br> }<br><br> HashMap<String,String> respMap = new HashMap<>();<br> respMap.put("openId",authCodeResponse.getOpenid());<br> //如果未传encryptedData和iv 即仅返回openId<br> if (Objects.isNull(dto.getEncryptedData()) || Objects.isNull(dto.getIv())){<br> return R.ok(respMap);<br> }<br> // 解密<br> byte[] encrypData = Base64Utils.decodeFromString(dto.getEncryptedData());<br> byte[] ivData = Base64Utils.decodeFromString(dto.getIv());<br> byte[] sessionKey = Base64Utils.decodeFromString(authCodeResponse.getSession_key());<br> try{<br> AlgorithmParameterSpec ivSpec = new IvParameterSpec(ivData);<br> Cipher cipher= Cipher.getInstance("AES/CBC/PKCS5Padding");<br> SecretKeySpec keySpec = new SecretKeySpec(sessionKey, "AES");<br> cipher.init(Cipher.DECRYPT_MODE, keySpec, ivSpec);<br> String string = new String(cipher.doFinal(encrypData), "UTF-8");<br> JSONObject object = JSONObject.parseObject(string);<br> //获取手机号码<br> String phone = object.getString("phoneNumber");<br> respMap.put("phone", phone);<br> }catch (Exception e){<br> e.getStackTrace();<br> log.error("微信小程序手机号码解密异常:{}",e.getMessage());<br> throw new AppletServiceException(AppletServiceConstants.EXCEPTION_ERROR_CODE,AppletServiceConstants.ENCTYP_EXECPTION);<br> }<br> return R.ok(resultString);<br> }<br><br><br><br> @ApiOperation(value = "获取-小程序accessToken值")<br> public String getToken() throws AppletServiceException {<br> //先从缓存中判断是否存在<br> String redisString = redis.opsForValue().get(AppletServiceConstants.ACCESS_TOKEN);<br> if (Objects.nonNull(redisString)){<br> return redisString;<br> }<br> String reqUrl = AppletServiceConstants.ACCESS_TOKEN_API;<br> Map map = new HashMap();<br> map.put("grant_type","client_credential");<br> map.put("appid",appId);<br> map.put("secret",secret);<br> String result = HttpClientUtil.doGet(reqUrl, map);<br> AuthAccessTokenResponse auth = JSON.parseObject(result, AuthAccessTokenResponse.class);<br> //判断errcode ==0 即请求成功<br> if (Objects.equals(auth.getErrcode(),AppletServiceConstants.SUCCESS_CODE)){<br> log.info("获取的accessToken值:{}",auth.getAccess_token());<br> //将accessToken存入到redis 设置过期时间<br> redis.opsForValue().set(AppletServiceConstants.ACCESS_TOKEN,auth.getAccess_token(),AppletServiceConstants.EXPIRES_IN, TimeUnit.SECONDS);<br> return auth.getAccess_token();<br> }else {<br> log.error("小程序授权失败返回错误码:{},错误信息:{}",auth.getErrcode(),auth.getErrmsg());<br> throw new AppletServiceException(AppletServiceConstants.PARAM_ERROR_CODE,AppletServiceConstants.ACCESS_TOKRN_ERROR);<br> }<br><br><br> }<br><br><br> @ApiOperation(value = "推送模板微信模板信息")<br> @ApiImplicitParams({<br> @ApiImplicitParam(value = "openId",name = "用户openId",required = true,dataType = "String")<br> })<br> public Boolean sendTemplateMsg(String openId) throws AppletServiceException {<br> WxTemplate wxTemplate = build(openId);<br> String str = JSON.toJSONString(wxTemplate);<br> log.info("微信推送模版消息:{}",str);<br> String result = HttpClientUtil.doPostJson(AppletServiceConstants.TEMPLATE_MESSAGE_URL+getToken(), str);<br> CommonResponse commonResponse = JSON.parseObject(result, CommonResponse.class);<br> if (Objects.equals(commonResponse.getErrcode(),AppletServiceConstants.SUCCESS_CODE)) {<br> return true;<br> } else {<br> return false;<br> }<br> }<br><br><br> /**<br> * 构建消息模板<br> * @return<br> */<br> private WxTemplate build(String openId) {<br> //以下是构建消息推送的model,具体情况需要根据模版做更改<br> WxTemplate template = new WxTemplate();<br> //模板Id<br> template.setTemplate_id(templateId);<br> //openId<br> template.setTouser(openId);<br> //此处小程序跳转链接,需要根据需要跳转拼接<br> template.setPage(templatePage);<br> Map<String, TemplateData> param = new HashMap<>();<br> // 封装模板数据,依据小程序模板来设置(可设置成参数,传递过来)<br> param.put("first", getTemplateData(xxxx));<br> param.put("keyword1", getTemplateData("xxxxxx"));<br> param.put("keyword2", getTemplateData("xxxx"));<br> param.put("keyword3", getTemplateData("xxxx"));<br> param.put("keyword4", getTemplateData("xxxx"));<br> param.put("keyword5", getTemplateData("xxxx"));<br> param.put("remark", getTemplateData("xxxx"));<br> template.setData(param);<br> return template;<br> }<br><br><br> /**<br> * 设置 TemplateDate数据<br> * @param value<br> * @return<br> */<br> private static TemplateData getTemplateData(String value) {<br> TemplateData templateData = new TemplateData();<br> templateData.setValue(value);<br> templateData.setColor("#173177");<br> return templateData;<br> }<br><br>}<br><br>==================================================================<br><strong><span style="font-size: 16px">封装请求参数,响应参数,常量类,枚举类和异常类</span></strong><br>==================================================================</pre>
<pre>package com.sf.vsolution.hb.sfce.util.wechat.appletService;<br><br>/**<br> * @Author: zhucj<br> * @Date: 2019/5/23 13:29<br> * @apiNote: 微信小程序服务端常量<br> */<br>public class AppletServiceConstants {<br><br><br> //TODO:服务端Api<br> /**<br> * 小程序登录API<br> */<br> public static final String AUTH_CODE_API = "https://api.weixin.qq.com/sns/jscode2session";<br> /**<br> * accessToken授权API<br> */<br> public static final String ACCESS_TOKEN_API = "https://api.weixin.qq.com/cgi-bin/token";<br><br> /**<br> * 模板消息URL<br> */<br> public static String TEMPLATE_MESSAGE_URL = "https://api.weixin.qq.com/cgi-bin/message/wxopen/template/send?access_token=";<br><br> /**<br> * accessToken缓存key<br> */<br> public static final String ACCESS_TOKEN = "ACCESS_TOKEN";</pre>
<pre> /**<br> * accessToken缓存过期时间<br> */<br><br> public static final Integer EXPIRES_IN= 7000;</pre>
<pre><em><br><br> //TODO:请求状态码<br> public static final Integer PARAM_ERROR_CODE = 400;<br><br> public static final Integer EXCEPTION_ERROR_CODE = 500;<br><br> public static final Integer SUCCESS_CODE = 0;<br><br><br> //TODO: 异常信息状态<br><br> public static final String CODE_NULL_ERROR = "微信授权码code不为空";<br><br> public static final StringAUTHORIZATION_ERROR = "未获取到openId,授权失败";<br><br> public static final String ENCTYP_EXECPTION = "微信小程序手机号码解密异常";<br><br> public static final String ACCESS_TOKRN_ERROR = "小程序授权获取accessToken失败";<br><br>}<br><br>----------------------------------<br></em></pre>
<pre>package com.sf.vsolution.hb.sfce.util.wechat.appletService;<br><br>/**<br> * @author :zhucj<br> * @date :Created in 2019/6/17 17:46<br> * @description: 小程序服务异常类<br> */<br>public class AppletServiceException extends RuntimeException {<br> private static final long serialVersionUID = -5317007026578376164L;<br><br> /**<br> * 错误码<br> */<br> private Integer errorCode;<br> /**<br> * 错误描述<br> */<br> private String errorMsg;<br><br> /**<br> * @param errorCode<br> * @param errorMsg<br> */<br> public AppletServiceException(Integer errorCode, String errorMsg) {<br> super(errorMsg);<br> this.errorCode = errorCode;<br> this.errorMsg = errorMsg;<br> }<br><br> public Integer getErrorCode() {<br> return errorCode;<br> }<br><br> public String getErrorMsg() {<br> return errorMsg;<br> }<br>}</pre>
<pre><em>----------------------------------<br></em></pre>
<pre>package com.sf.vsolution.hb.sfce.util.wechat.appletService;<br><br>import com.alibaba.fastjson.JSON;<br>import lombok.extern.slf4j.Slf4j;<br>import org.apache.http.client.methods.CloseableHttpResponse;<br>import org.apache.http.client.methods.HttpGet;<br>import org.apache.http.client.methods.HttpPost;<br>import org.apache.http.client.utils.URIBuilder;<br>import org.apache.http.entity.ContentType;<br>import org.apache.http.entity.StringEntity;<br>import org.apache.http.impl.client.CloseableHttpClient;<br>import org.apache.http.impl.client.HttpClients;<br>import org.apache.http.util.EntityUtils;<br><br>import java.io.IOException;<br>import java.net.URI;<br>import java.util.Map;<br><br>/**<br> * @description: get/post请求工具类<br> * @author: zhucj<br> * @date: 2019-04-26 10:10<br> */<br>@Slf4j<br>public class HttpClientUtil {<br><br> /**<br> * 发送get请求<br> * @param url<br> * @param param<br> * @return<br> */<br> public static String doGet(String url, Map<String, String> param) {<br> log.info("GET请求地址:{},请求参数内容:{}",url, JSON.toJSON(param));<br> // 创建Httpclient对象<br> CloseableHttpClient httpclient = HttpClients.createDefault();<br><br> String resultString = "";<br> CloseableHttpResponse response = null;<br> try {<br> // 创建uri<br> URIBuilder builder = new URIBuilder(url);<br> if (param != null) {<br> for (String key : param.keySet()) {<br> builder.addParameter(key, param.get(key));<br> }<br> }<br> URI uri = builder.build();<br><br> // 创建http GET请求<br> HttpGet httpGet = new HttpGet(uri);<br><br> // 执行请求<br> response = httpclient.execute(httpGet);<br> // 判断返回状态是否为200<br> if (response.getStatusLine().getStatusCode() == 200) {<br> resultString = EntityUtils.toString(response.getEntity(), "UTF-8");<br> }<br> } catch (Exception e) {<br> e.printStackTrace();<br> log.error("网络出现异常:{}",e.getMessage());<br> } finally {<br> try {<br> if (response != null) {<br> response.close();<br> }<br> httpclient.close();<br> } catch (IOException e) {<br> e.printStackTrace();<br> log.error("IO出现异常:{}",e.getMessage());<br> }<br> }<br> log.info("GET响应参数:{}",resultString);<br> return resultString;<br> }<br><br> /**<br> * 发post请求 传入xml/json字符串<br> * @param url<br> * @param json<br> * @return<br> */<br> public static String doPostJson(String url, String json) {<br> log.info("POST请求地址:{},请求参数内容:{}",url,json);<br> // 创建Httpclient对象<br> CloseableHttpClient httpClient = HttpClients.createDefault();<br> CloseableHttpResponse response = null;<br> String resultString = "";<br> try {<br> // 创建Http Post请求<br> HttpPost httpPost = new HttpPost(url);<br> // 创建请求内容<br> StringEntity entity = new StringEntity(json, ContentType.APPLICATION_JSON);<br> httpPost.setEntity(entity);<br> // 执行http请求<br> response = httpClient.execute(httpPost);<br> resultString = EntityUtils.toString(response.getEntity(), "utf-8");<br> } catch (Exception e) {<br> e.printStackTrace();<br> log.error("网络出现异常:{}",e.getMessage());<br> } finally {<br> try {<br> response.close();<br> } catch (IOException e) {<br> e.printStackTrace();<br> log.error("IO出现异常:{}",e.getMessage());<br> }<br> }<br> log.info("POST响应参数:{}",resultString);<br> return resultString;<br> }<br><br>}<br><br></pre>
<pre><em>-------------------------------------<br></em></pre>
<pre>package com.sf.vsolution.hb.sfce.util.wechat.appletService;<br><br>import io.swagger.annotations.ApiModel;<br>import io.swagger.annotations.ApiModelProperty;<br>import lombok.ToString;<br><br>import java.io.Serializable;<br><br>/**<br> * 返回类型<br> * @author choleece<br> * @date 2018/9/27<br> */<br>@ApiModel<br>@ToString<br>public class R<T> implements Serializable {<br><br> private static final long serialVersionUID = -6287952131441663819L;<br><br> /**<br> * 编码<br> */<br> @ApiModelProperty(value = "响应码", example = "200")<br> private int code = 200;<br><br> /**<br> * 成功标志<br> */<br> @ApiModelProperty(value = "成功标志", example = "true")<br> private Boolean success;<br><br> /**<br> * 返回消息<br> */<br> @ApiModelProperty(value = "返回消息说明", example = "操作成功")<br> private String msg="操作成功";<br><br> /**<br> * 返回数据<br> */<br> @ApiModelProperty(value = "返回数据")<br> private T data;<br><br> /**<br> * 创建实例<br> * @return<br> */<br> public static R instance() {<br> return new R();<br> }<br><br> public int getCode() {<br> return code;<br> }<br><br> public R setCode(int code) {<br> this.code = code;<br> return this;<br> }<br><br> public Boolean getSuccess() {<br> return success;<br> }<br><br> public R setSuccess(Boolean success) {<br> this.success = success;<br> return this;<br> }<br><br> public String getMsg() {<br> return msg;<br> }<br><br> public R setMsg(String msg) {<br> this.msg = msg;<br> return this;<br> }<br><br> public T getData() {<br> return data;<br> }<br> public R setData(T data) {<br> this.data = data;<br> return this;<br> }<br><br> public static R ok() {<br> return R.instance().setSuccess(true);<br> }<br><br> public static R ok(Object data) {<br> return ok().setData(data);<br> }<br><br> public static R ok(Object data, String msg) {<br> return ok(data).setMsg(msg);<br> }<br><br> public static R error() {<br> return R.instance().setSuccess(false);<br> }<br><br> public static R error(String msg) {<br> return error().setMsg(msg);<br> }<br><br> /**<br> * 无参<br> */<br> public R() {<br> }<br><br> public R(int code, String msg) {<br> this.code = code;<br> this.msg = msg;<br> }<br><br> public R(int code, T data){<br> this.code = code;<br> this.data = data;<br> }<br><br> /**<br> * 有全参<br> * @param code<br> * @param msg<br> * @param data<br> * @param success<br> */<br> public R(int code, String msg, T data, Boolean success) {<br> this.code = code;<br> this.msg = msg;<br> this.data = data;<br> this.success = success;<br> }<br><br> /**<br> * 有参<br> * @param code<br> * @param msg<br> * @param data<br> */<br> public R(int code, String msg, T data) {<br> this.code = code;<br> this.msg = msg;<br> this.data = data;<br> }<br><br> <br>}</pre>
<pre><em>---------------------------------------<br></em></pre>
<pre>package com.sf.vsolution.hb.sfce.util.wechat.appletService.param;<br><br>import lombok.*;<br><br>/**<br> * @description: 小程序端返回共用参数<br> * @author: zhucj<br> * @date: 2019-11-14 16:41<br> */<br>@Data<br>@Builder<br>@NoArgsConstructor<br>@AllArgsConstructor<br>@ToString<br>public class CommonResponse {<br><br> /**<br> * 错误码<br> */<br> private Integer errcode;<br><br> /**<br> * 错误信息<br> */<br> private String errmsg;<br>}</pre>
<pre><em><br><br>-----------------------------<br></em></pre>
<pre>package com.sf.vsolution.hb.sfce.util.wechat.appletService.param;<br><br>import lombok.*;<br><br>/**<br> * @description: 小程序获取AccessToken返回参数<br> * @author: zhucj<br> * @date: 2019-11-14 16:43<br> */<br>@Data<br>@Builder<br>@NoArgsConstructor<br>@AllArgsConstructor<br>@ToString<br>public class AuthAccessTokenResponse extends CommonResponse {<br><br><br> /**<br> * 获取到的凭证<br> */<br> private String access_token;<br><br> /**<br> * 凭证有效时间,单位:秒。目前是7200秒之内的值。<br> */<br> private Integer expires_in;<br>}</pre>
<pre><em><br>---------------------------<br><br></em></pre>
<pre>package com.sf.vsolution.hb.sfce.util.wechat.appletService.param;<br><br>import lombok.*;<br><br>/**<br> * @description: 小程序获取openId返回参数<br> * @author: zhucj<br> * @date: 2019-11-14 16:13<br> */<br>@Data<br>@Builder<br>@NoArgsConstructor<br>@AllArgsConstructor<br>@ToString<br>public class AuthCodeResponse extends CommonResponse {<br><br> /**<br> * 用户唯一标识<br> */<br> private String openid;<br><br> /**<br> * 会话密钥<br> */<br> private String session_key;<br><br> /**<br> * 用户在开放平台的唯一标识符,在满足 UnionID 下发条件的情况下会返回,详见 UnionID 机制说明。<br> */<br> private String unionid;<br><br>}<br><br></pre>
<pre><em>------------------------------<br></em></pre>
<pre>package com.sf.vsolution.hb.sfce.util.wechat.appletService.param;<br><br>import lombok.*;<br><br>/**<br> * Template<br> * @author zhucj<br> */<br>@Data<br>@NoArgsConstructor<br>@AllArgsConstructor<br>@ToString<br>@Builder<br>public class TemplateData {<br><br> /**<br> * 模板显示值<br> */<br> private String value;<br> /**<br> * 模板显示颜色<br> */<br> private String color;<br>}<br><br><br>---------------------------</pre>
<pre>package com.sf.vsolution.hb.sfce.util.wechat.appletService.param;<br><br>import lombok.*;<br><br>/**<br> * @description: 微信授权参数<br> * @author: zhucj<br> * @date: 2019-09-26 15:01<br> */<br>@Data<br>@NoArgsConstructor<br>@AllArgsConstructor<br>@ToString<br>@Builder<br>public class WeChatAuthorizeDto {<br><br> /**<br> * 微信授权code<br> */<br> private String code;<br><br> /**<br> * 包括敏感数据在内的完整用户信息的加密数据<br> */<br> private String encryptedData;<br><br> /**<br> * 加密算法的初始向量,详见 用户数据的签名验证和加解密<br> */<br> private String iv;<br>}</pre>
<pre><em>-----------------------------<br></em></pre>
<pre>package com.sf.vsolution.hb.sfce.util.wechat.appletService.param;<br>import lombok.*;<br><br>import java.util.Map;<br><br>@Data<br>@NoArgsConstructor<br>@AllArgsConstructor<br>@ToString<br>@Builder<br>public class WxTemplate {<br><br> /**<br> * 微信通知模板ID<br> */<br> private String template_id;<br> /**<br> * 微信openId<br> */<br> private String touser;<br><br> /** 小程序首页地址 */<br> private String page;<br><br> /**支付返回的 prepay_id,部分模板必传*/<br> private Stringform_id;<br><br> private Map<String, String> miniprogram;<br><br> private Map<String, TemplateData> data;<br>}</pre>
<pre><em>=================================================================<br><strong><span style="font-size: 16px"> yml配置文件</span></strong><br>=================================================================<br></em></pre>
<pre>#微信支付参数<br>wx:<br>#微信小程序appId<br>appId: ******<br>#小程序secret<br>secret: *******<br>template:<br> #模板Id<br> id: ******<br> #通知跳转小程序页面<br> page: ******<br><br><br>=================================================================<br><strong><span style="font-size: 16px"> 项目maven支持<br>===============================================<br></span></strong></pre>
<pre><!-- swagger API --><br><dependency><br> <groupId>io.springfox</groupId><br> <artifactId>springfox-swagger2</artifactId><br> <version>2.6.0</version><br></dependency><br><dependency><br> <groupId>io.springfox</groupId><br> <artifactId>springfox-swagger-ui</artifactId><br> <version>2.6.0</version><br></dependency><br><br></pre>
<pre><!--htttpclient依赖--><br><dependency><br> <groupId>org.apache.httpcomponents</groupId><br> <artifactId>httpclient</artifactId><br> <version>4.5.5</version><br></dependency><br><br></pre>
<pre><!--fastjson依赖--><br><dependency><br> <groupId>com.alibaba</groupId><br> <artifactId>fastjson</artifactId><br> <version>1.2.47</version><br></dependency><br><br></pre>
<pre><!-- redis --><br><dependency><br> <groupId>org.springframework.boot</groupId><br> <artifactId>spring-boot-starter-data-redis</artifactId><br></dependency><br><br></pre>
<pre><br><!-- lombok --><br><dependency><br> <groupId>org.projectlombok</groupId><br> <artifactId>lombok</artifactId><br> <version>1.18.2</version><br> <scope>provided</scope><br></dependency></pre>
<pre><strong><span style="font-size: 16px"> </span></strong></pre>
<pre><em> </em></pre>
<pre></pre><br><br>
来源:https://www.cnblogs.com/zhucj-java/p/11864317.html
頁:
[1]