生活就是问题叠着问题,一个一个的去解决!这句话应该我们这个行业再也合适不过了!
写业务逻辑代码之前,先捋一下思路:用户登录注册之后生成一个有个人标识的二维码,用户可以把这个二维码保存到自己的手机里面,然后分享到微信朋友圈,其他人可以扫面进入小程序,进入指定的小程序页面,进入授权注册,不然我发进入小程序,用户完成授权注册之后,分享出去的那个人下面就会多一个粉丝。
是不是感觉很复杂,才开始我没有仔细阅读小程序官方文档,我还以为前端就可以完成这个工作,做了很久之后,一直报错40001,后来我一看文档,我就不厚道的笑了,去找后台的小哥哥,小哥哥一看没问题,撸代码就是了。经过后台小哥哥不懈努力,终于写了出来!前端这一个只要调用api保存到手机就可以了!
先把后台的代码贴出来吧,虽然我看不懂,我还是后者脸皮要了过来!
package com.weixin.utils.manager;public class WxConfigDTO { //小程序唯一标识 (在微信小程序管理后台获取) public static final String wxspAppid = "你的小程序appid"; //小程序的 APP secret (在微信小程序管理后台获取) public static final String wxspSecret = "你的小程序spSecret"; //小程序获取openId URL public static final String getOpenIdUrl = "你的openIdURL"; //小程序获取openId 授权 public static final String grantType = "你的小程序openId授权"; //小程序获取access_token URL public static final String getAccessToken = "你的小程序access_token URL"; //获取小程序码 URL public static final String getImgCode = "你的小程序码 URL"; //阿里云地址 public static final String endpoint = "阿里云地址"; // 云账号AccessKeyID有所有API访问权限 public static final String accessKeyId = "云账号AccessKeyID有所有API访问权限"; //云账号accessKeySecret public static final String accessKeySecret = "x59xf21ltmr6xrb0rwRfEufthCoOrP"; //阿里云访问图片路径 public static final String ossImg = "云账号accessKeySecret"; //阿里云访问bucketName public static final String bucketName = "阿里云访问bucketName"; //小程序扫码跳转页 public static final String scanToPage = "小程序扫码跳转页"; //是否支持小程序扫码跳转页 public static enum isToPage{ toPage("page","支持扫码跳转,但小程序要上线,否则生成小程序码失败"), notToPage("path","不支持扫码跳转,即时小程序未上线,也可以成功生成小程序码"); isToPage(String scanToPage,String message) { this.scanToPage = scanToPage; this.message = message; } private String scanToPage; private String message; public String getScanToPage() { return scanToPage; } public void setScanToPage(String scanToPage) { this.scanToPage = scanToPage; } public String getMessage() { return message; } public void setMessage(String message) { this.message = message; } }}package com.weixin.utils.manager;import java.io.BufferedInputStream;import java.io.BufferedReader;import java.io.IOException;import java.io.InputStreamReader;import java.io.PrintWriter;import java.io.UnsupportedEncodingException;import java.net.HttpURLConnection;import java.net.URL;import java.net.URLConnection;import java.security.AlgorithmParameters;import java.security.InvalidAlgorithmParameterException;import java.security.InvalidKeyException;import java.security.NoSuchAlgorithmException;import java.security.Security;import java.security.spec.InvalidParameterSpecException;import java.text.SimpleDateFormat;import java.util.Date;import java.util.HashMap;import java.util.List;import java.util.Map;import javax.crypto.BadPaddingException;import javax.crypto.Cipher;import javax.crypto.IllegalBlockSizeException;import javax.crypto.NoSuchPaddingException;import javax.crypto.spec.IvParameterSpec;import javax.crypto.spec.SecretKeySpec;import org.apache.commons.codec.binary.Base64;import org.bouncycastle.jce.provider.BouncyCastleProvider;import org.json.JSONObject;import com.aliyun.oss.OSSClient;import com.aliyun.oss.common.auth.DefaultCredentialProvider;public class WxConfigManager { public static MapgetOpenId(String iv,String codeId,String encryptedData ) throws Exception { Map map=new HashMap (); 1、向微信服务器 使用登录凭证 code 获取 session_key 和 openid //请求参数 //授权(必填)grant_type=authorization_code String params = "appid=" + WxConfigDTO.wxspAppid + "&secret=" + WxConfigDTO.wxspSecret + "&js_code=" + codeId + "&grant_type=" + WxConfigDTO.grantType; //发送请求 String sr = sendGet(WxConfigDTO.getOpenIdUrl, params); System.err.println(sr); //解析相应内容(转换成JSON对象) JSONObject jsonObject = new JSONObject(sr); String sessionKey = jsonObject.getString("session_key"); 2、对encryptedData加密数据进行AES解密 String result = decrypt(encryptedData, sessionKey, iv, "UTF-8"); if (null != result && result.length() > 0) { JSONObject userInfoJSON = new JSONObject(result); map.put("openId",userInfoJSON.get("openId").toString()); map.put("nickName", userInfoJSON.get("nickName").toString()); map.put("gender", userInfoJSON.get("gender").toString()); map.put("city", userInfoJSON.get("city").toString()); map.put("province", userInfoJSON.get("province").toString()); map.put("country", userInfoJSON.get("country").toString()); map.put("avatarUrl", userInfoJSON.get("avatarUrl").toString()); map.put("unionId", userInfoJSON.get("unionId").toString()); } return map; } /** * 生成小程序邀请码并上传阿里云(三步) * 1,获取access_token * 2,获取微信小程序码图片流 * 3,上传阿里云 * @param inviteCode */ public static void main(String[] args) throws Exception { createInviteCode("123456"); } /** * 1,获取access_token * @param inviteCode * @return */ public static String createInviteCode(String inviteCode) throws Exception { //1,获取access_token String object = WxConfigManager.sendGet(WxConfigDTO.getAccessToken, "grant_type=client_credential&appid="+WxConfigDTO.wxspAppid+"&secret="+WxConfigDTO.wxspSecret); //2,获取微信小程序码图片流 return getImageCode(inviteCode,new JSONObject(object).get("access_token").toString()); } /** * 2,获取微信小程序码图片流 * @param inviteCode 邀请码,accessToken * @return 小程序码二进制流 */ public static String getImageCode(String inviteCode,String accessToken) throws Exception { //2,获取微信小程序码图片流 //拼接参数,请求微信接口,获取小程序码 URL url = new URL(WxConfigDTO.getImgCode+accessToken); HttpURLConnection httpURLConnection = (HttpURLConnection) url.openConnection(); // 提交模式 // conn.setConnectTimeout(10000);//连接超时 单位毫秒 // conn.setReadTimeout(2000);//读取超时 单位毫秒 // 发送POST请求必须设置如下两行 httpURLConnection.setDoOutput(true); httpURLConnection.setDoInput(true); // 获取URLConnection对象对应的输出流 PrintWriter printWriter = new PrintWriter(httpURLConnection.getOutputStream()); // 发送请求参数 JSONObject paramJson = new JSONObject(); paramJson.put("scene", inviteCode); paramJson.put(WxConfigDTO.isToPage.notToPage.getScanToPage(), WxConfigDTO.scanToPage); printWriter.write(paramJson.toString()); // flush输出流的缓冲 printWriter.flush(); //开始获取数据 BufferedInputStream inputStream = new BufferedInputStream(httpURLConnection.getInputStream()); //上传阿里云 return uploadOss(inviteCode,inputStream); } /** * 3,上传阿里云 * @param inviteCode * @return 图片地址 */ public static String uploadOss(String inviteCode,BufferedInputStream inputStream) { //3,上传阿里云 // 创建OSSClient实例 OSSClient ossClient = new OSSClient(WxConfigDTO.endpoint, new DefaultCredentialProvider(WxConfigDTO.accessKeyId, WxConfigDTO.accessKeySecret),null); // 上传文件到阿里云。 Date at = new Date(); String endPath = "MpInviteCode" + "/" + new SimpleDateFormat("yyyyMM").format(at) + "/" + new SimpleDateFormat("dd").format(at) + "/" + inviteCode +".jpg"; String cipther = Env.isProduct != null && Env.isProduct ? endPath : "test/" + endPath; ossClient.putObject(WxConfigDTO.bucketName, cipther, inputStream); // 关闭OSSClient。 ossClient.shutdown(); //返回阿里云路径 System.err.println( WxConfigDTO.ossImg+cipther); return WxConfigDTO.ossImg+cipther; } public static String sendGet(String url, String param) { String result = ""; BufferedReader in = null; try { String urlNameString = url + "?" + param; URL realUrl = new URL(urlNameString); // 打开和URL之间的连接 URLConnection connection = realUrl.openConnection(); // 设置通用的请求属性 connection.setRequestProperty("accept", "*/*"); connection.setRequestProperty("connection", "Keep-Alive"); connection.setRequestProperty("user-agent", "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1;SV1)"); // 建立实际的连接 connection.connect(); // 获取所有响应头字段 Map > map = connection.getHeaderFields(); // 遍历所有的响应头字段 for (String key : map.keySet()) { System.out.println(key + "--->" + map.get(key)); } // 定义 BufferedReader输入流来读取URL的响应 in = new BufferedReader(new InputStreamReader( connection.getInputStream())); String line; while ((line = in.readLine()) != null) { result += line; } } catch (Exception e) { System.out.println("发送GET请求出现异常!" + e); e.printStackTrace(); } // 使用finally块来关闭输入流 finally { try { if (in != null) { in.close(); } } catch (Exception e2) { e2.printStackTrace(); } } return result; } /** * 向指定 URL 发送POST方法的请求 * * @param url * 发送请求的 URL * @param param * 请求参数,请求参数应该是 name1=value1&name2=value2 的形式。 * @return 所代表远程资源的响应结果 */ public static String sendPost(String url, String param) { PrintWriter out = null; BufferedReader in = null; String result = ""; try { URL realUrl = new URL(url); // 打开和URL之间的连接 URLConnection conn = realUrl.openConnection(); // 设置通用的请求属性 conn.setRequestProperty("accept", "*/*"); conn.setRequestProperty("connection", "Keep-Alive"); conn.setRequestProperty("user-agent", "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1;SV1)"); // 发送POST请求必须设置如下两行 conn.setDoOutput(true); conn.setDoInput(true); // 获取URLConnection对象对应的输出流 out = new PrintWriter(conn.getOutputStream()); // 发送请求参数 out.print(param); // flush输出流的缓冲 out.flush(); // 定义BufferedReader输入流来读取URL的响应 in = new BufferedReader( new InputStreamReader(conn.getInputStream())); String line; while ((line = in.readLine()) != null) { result += line; } } catch (Exception e) { System.out.println("发送 POST 请求出现异常!"+e); e.printStackTrace(); } //使用finally块来关闭输出流、输入流 finally{ try{ if(out!=null){ out.close(); } if(in!=null){ in.close(); } } catch(IOException ex){ ex.printStackTrace(); } } return result; } static { //BouncyCastle是一个开源的加解密解决方案,主页在http://www.bouncycastle.org/ Security.addProvider(new BouncyCastleProvider()); } /** * AES解密 * * @param data //密文,被加密的数据 * @param key //秘钥 * @param iv //偏移量 * @param encodingFormat //解密后的结果需要进行的编码 * @return * @throws Exception */ public static String decrypt(String data, String key, String iv, String encodingFormat) throws Exception {// initialize(); //被加密的数据 byte[] dataByte = Base64.decodeBase64(data.getBytes()); //加密秘钥 byte[] keyByte = Base64.decodeBase64(key.getBytes()); //偏移量 byte[] ivByte = Base64.decodeBase64(iv.getBytes()); try { Cipher cipher = Cipher.getInstance("AES/CBC/PKCS7Padding"); SecretKeySpec spec = new SecretKeySpec(keyByte, "AES"); AlgorithmParameters parameters = AlgorithmParameters.getInstance("AES"); parameters.init(new IvParameterSpec(ivByte)); cipher.init(Cipher.DECRYPT_MODE, spec, parameters);// 初始化 byte[] resultByte = cipher.doFinal(dataByte); if (null != resultByte && resultByte.length > 0) { String result = new String(resultByte, encodingFormat); return result; } return null; } catch (NoSuchAlgorithmException e) { e.printStackTrace(); } catch (NoSuchPaddingException e) { e.printStackTrace(); } catch (InvalidParameterSpecException e) { e.printStackTrace(); } catch (InvalidKeyException e) { e.printStackTrace(); } catch (InvalidAlgorithmParameterException e) { e.printStackTrace(); } catch (IllegalBlockSizeException e) { e.printStackTrace(); } catch (BadPaddingException e) { e.printStackTrace(); } catch (UnsupportedEncodingException e) { e.printStackTrace(); } return null; }}复制代码
实现逻辑:扫码进来的会进入指定的授权注册页面,完成注册任务,后台会返给我一张带有用户个人标识的小程序二维码,然后我把它放在缓存里面,需要用的时候展示给用户!用户可以把二维码图片保存到手机里,通过朋友圈分享出去!
前端代码逻辑
方法一:
keepPhone: function () { var that = this; var mpInvitePic = that.data.mpInvitePic; var switchs = that.data.switchs; // 检查用户是否授权 wx.getSetting({ success: function (res) { if (!res.authSetting['scope.writePhotosAlbum']) { // 用户授权保存到手机相册 wx.authorize({ scope: 'scope.writePhotosAlbum', success: function (res) { // api不支持网络图片,所以你需要下载一下,把你的网络图片下载下来,微信那边给你生成一个临时路径! wx.downloadFile({ url: mpInvitePic, success: function (res) { //保存到手机相册api wx.saveImageToPhotosAlbum({ filePath: res.tempFilePath, success: function (res) { wx.showToast({ title: '保存成功', }) } }) } }) }, fail(res) { wx.showToast({ title: '影响您使用小程序的某些功能', icon: 'none' }) } }) } }, fail(res) { } }) },复制代码
方法二:
keepPhone:function(){ var that = this; var mpInvitePic = that.data.mpInvitePic; var switchs = that.data.switchs; wx.downloadFile({ url:mpInvitePic, success: function (res) { if (res.statusCode == 200) { wx.saveImageToPhotosAlbum({ filePath: res.tempFilePath, success: function (res) { wx.showToast({ title: '保存成功'}); } }) } } }) },复制代码