微信公众号脚手架

微信公众号脚手架_第1张图片

一、前言

方便要开发微信公众号的朋友们,可以快速将服务搭建起来,不要把时间浪费在服务的搭建上,专心写我们的业务代码。

你需要了解的知识:

1.微信公众号大概的开发流程
2.注册公众号(本脚手架是用公共的测试账号)
3.服务器的配置
4.内网穿透(我用的是花生壳)
5.公众号开发文档先大概看一遍

废话不多说了,你懂得,直接讲重点…

微信公众号脚手架_第2张图片
脚手架代码下载地址:下载


二、几个关键的地方

本项目依赖SDK开发工具包(WxJava)https://gitee.com/binary/weixin-java-tools

2.1 验证服务器地址有效性

开发者ID(AppID)
开发者密码(AppSecret)
服务器地址(URL)
令牌(Token)

2.2 AccessToken的处理

access_token是公众号的全局唯一接口调用凭据,公众号调用各接口时都需使用access_token。开发者需要进行妥善保存。access_token的存储至少要保留512个字符空间。access_token的有效期目前为2个小时(7200秒),需定时刷新,重复获取将导致上次获取的access_token失效。

2.3 公众号消息的处理
2.4 网页授权,拆分链接
2.5 Js-Sdk配置,测试

将以上问题先注意一下。


三、怎么快速把项目跑起来

3.1 项目结构

微信公众号脚手架_第3张图片


3.2 修改配置

1.application.xml (公众号对应配置修改,支持多个公众号)
2.JedisConfiguration.java (修改Redis配置,如果不配置,就内存缓存)


3.3 启动服务

WxMpDemoApplication.java
微信公众号脚手架_第4张图片


3.4 常用配置

我的内网穿透域名:http://chenxingxing.51vip.biz

1.接口配置信息URL:${内网穿透域名}/wx/portal/${appId}
例如:http://chenxingxing.51vip.biz/wx/portal/wx62458041039e62ee

2.JS接口安全域名
例如:chenxingxing.51vip.biz

3.消息模板
例如:{{first.DATA}}

4.创建菜单    
例如:http://chenxingxing.51vip.biz/wx/menu/wx62458041039e62ee/create

5.发送模板
例如:http://chenxingxing.51vip.biz/wx/other/wx62458041039e62ee/sendTemplate

6.授权回调地址
例如:http://chenxingxing.51vip.biz/wx/redirect/wx62458041039e62ee/oauth/callback

7.拆分链接
例如:http://chenxingxing.51vip.biz/wx/redirect/wx62458041039e62ee/split/jump?url=http://chenxingxing.51vip.biz

基本上到这,你的公众号就可以用了。然后你配置一下菜单,发消息,发模板,测试测试。


四、怎么玩

公众号开发文档:https://developers.weixin.qq.com/doc/offiaccount/Basic_Information/Access_Overview.html

4.1 创建菜单

访问url:http://chenxingxing.51vip.biz/wx/menu/wx62458041039e62ee/create
微信公众号脚手架_第5张图片


4.2 发送模板消息

访问url:http://chenxingxing.51vip.biz/wx/other/wx62458041039e62ee/sendTemplate

微信公众号脚手架_第6张图片


4.3 Js-Sdk测试

访问url:http://chenxingxing.51vip.biz/jssdk
微信公众号脚手架_第7张图片


<html xmlns:th="http://www.thymeleaf.org">
<head>
    <title>Jssdk测试title>
    <meta content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=0" name="viewport">
    <script type="text/javascript" src="http://res.wx.qq.com/open/js/jweixin-1.6.0.js">script>
    <script type="text/javascript" src="https://www.w3school.com.cn/jquery/jquery-1.11.1.min.js">script>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
    
    <link rel="stylesheet" href="https://cdn.bootcss.com/weui/1.1.3/style/weui.min.css">
    <link rel="stylesheet" href="https://cdn.bootcss.com/jquery-weui/1.2.1/css/jquery-weui.min.css">
head>
<body style="text-align: center;">
<h1>Jssdk测试h1>
<br>
<a href="javascript:;" class="weui-btn weui-btn_primary" onclick="wxConfig()">wxConfiga>
<a href="javascript:;" class="weui-btn weui-btn_warn" onclick="share()">分享a>
<a href="javascript:;" class="weui-btn weui-btn_default" onclick="chooseImage()">拍照a>
<a href="javascript:;" class="weui-btn weui-btn_warn" onclick="pay()">微信支付a>
<a href="javascript:;" class="weui-btn weui-btn_plain-default" onclick="getLocation()">获取地理位置a>
<a href="javascript:;" class="weui-btn weui-btn_plain-primary" onclick="scanQRCode()">扫一扫a>
body>
<script src="https://cdn.bootcss.com/jquery/1.11.0/jquery.min.js">script>
<script src="https://cdn.bootcss.com/jquery-weui/1.2.1/js/jquery-weui.min.js">script>
<script>
    function wxConfig() {
        $.post("wx/redirect/wx62458041039e62ee/create/jsapi_sign",{
            url:location.href  //传递当前页面的url
        },function(data){
            wx.config({
                debug: true,// 开启调试模式,如何设计为true的话调用的所有api的返回值会在客户端alert出来,若要查看传入的参数,可以在pc端打开,参数信息会通过log打出,仅在pc端时才会打印,如果为false则不打印
                appId: data.appId,// 必填,公众号的唯一标识
                timestamp: data.timestamp,// 必填,生成签名的时间戳
                nonceStr: data.nonceStr, // 必填,生成签名的随机串
                signature: data.signature,// 必填,签名
                jsApiList: ['chooseImage', 'scanQRCode','updateAppMessageShareData','getLocation','closeWindow','checkJsApi']  // 必填,需要使用的JS接口列表
            });

            wx.ready(function(){
                $.alert("验证成功");
            });
            wx.error(function(res){
                $.alert("微信认证失败,请重试");
            });
        });
    }

    function share() {
        wx.updateAppMessageShareData({
            title: '分享标题', // 分享标题
            desc: '分享描述', // 分享描述
            link: 'http://chenxingxing.51vip.biz/jssdk', // 该链接域名或路径必须与当前页面对应的公众号JS安全域名一致
            imgUrl: 'http://mmbiz.qpic.cn/mmbiz_jpg/kqu5eakDVMTcnKRKc54W9NZaLkWLSlibgXOAvicicAGV6XwTSYf6WhMQ1ov0RLE9ahKw54BvOcmexNmy9pdNGklqw/0', // 分享图标
            success: function (res) {
                // 设置成功
                $.alert(res.toString());
            }
        })
    }

    function chooseImage() {
        wx.chooseImage({
            count: 1, // 默认9
            sizeType: ['original', 'compressed'], // 可以指定是原图还是压缩图,默认二者都有
            sourceType: ['album', 'camera'], // 可以指定来源是相册还是相机,默认二者都有
            success: function (res) {
                var localIds = res.localIds; // 返回选定照片的本地ID列表,localId可以作为img标签的src属性显示图片
                $.alert(localIds);
            }
        });
    }

    function getLocation() {
        wx.getLocation({
            type: 'wgs84', // 默认为wgs84的gps坐标,如果要返回直接给openLocation用的火星坐标,可传入'gcj02'
            success: function (res) {
                var latitude = res.latitude; // 纬度,浮点数,范围为90 ~ -90
                var longitude = res.longitude; // 经度,浮点数,范围为180 ~ -180。
                var speed = res.speed; // 速度,以米/每秒计
                var accuracy = res.accuracy; // 位置精度
                $.alert(res.toString());
            }
        });
    }

    function scanQRCode() {
        wx.scanQRCode({
            needResult: 0, // 默认为0,扫描结果由微信处理,1则直接返回扫描结果,
            scanType: ["qrCode","barCode"], // 可以指定扫二维码还是一维码,默认二者都有
            success: function (res) {
                var result = res.resultStr; // 当needResult 为 1 时,扫码返回的结果
                $.alert(res.toString());
            }
        });
    }

    function pay() {
        wx.chooseWXPay({
            timestamp: 0, // 支付签名时间戳,注意微信jssdk中的所有使用timestamp字段均为小写。但最新版的支付后台生成签名使用的timeStamp字段名需大写其中的S字符
            nonceStr: '', // 支付签名随机串,不长于 32 位
            package: '', // 统一支付接口返回的prepay_id参数值,提交格式如:prepay_id=\*\*\*)
            signType: '', // 签名方式,默认为'SHA1',使用新版支付需传入'MD5'
            paySign: '', // 支付签名
            success: function (res) {
                // 支付成功后的回调函数
            }
        });
    }
script>
html>

4.4 拆分链接

访问url:http://chenxingxing.51vip.biz/wx/redirect/wx62458041039e62ee/split/jump?url=http://chenxingxing.51vip.biz

比如我们现在微信公众号里面访问http://chenxingxing.51vip.biz这个页面,但是这个页面要先让用户授权,所以这里我们同样通过拆分链接的方式,在后头自动实现授权,然后将用户授权完的信息token,返回给前端。【拆分链接的好处,不需要配置个性化参数,很多地方都可以嵌入该链接】

微信公众号脚手架_第8张图片


4.5 各种事件的处理

在对于Hadler里面处理就可以。
微信公众号脚手架_第9张图片

package com.lxh.mp.config;

import com.lxh.mp.handler.*;
import lombok.Data;
import me.chanjar.weixin.common.api.WxConsts;
import me.chanjar.weixin.mp.api.WxMpMessageRouter;
import me.chanjar.weixin.mp.api.WxMpService;
import me.chanjar.weixin.mp.api.impl.WxMpServiceImpl;
import me.chanjar.weixin.mp.config.impl.WxMpDefaultConfigImpl;
import me.chanjar.weixin.mp.config.impl.WxMpRedisConfigImpl;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.annotation.Order;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;

import javax.annotation.Resource;
import java.util.List;
import java.util.stream.Collectors;

import static me.chanjar.weixin.common.api.WxConsts.EventType.*;
import static me.chanjar.weixin.common.api.WxConsts.XmlMsgType.EVENT;
import static me.chanjar.weixin.common.api.WxConsts.MenuButtonType.CLICK;
import static me.chanjar.weixin.common.api.WxConsts.MenuButtonType.VIEW;
import static me.chanjar.weixin.mp.constant.WxMpEventConstants.CustomerService.*;
import static me.chanjar.weixin.mp.constant.WxMpEventConstants.POI_CHECK_NOTIFY;

/**
 * created by [email protected] on 2020/2/21
 * 微信公众号配置
 * wxMpService
 * WxMpMessageRouter
 *
 */
@Configuration
@Data
@EnableConfigurationProperties(WxMpProperties.class)
@Order(1)
public class WxMpConfiguration {
    private final WxMpProperties properties;
    // 更新2020.2.22 加入redis
    @Resource
    private JedisPool jedisPool;

    private final LogHandler logHandler;
    private final NullHandler nullHandler;
    private final KfSessionHandler kfSessionHandler;
    private final StoreCheckNotifyHandler storeCheckNotifyHandler;
    private final LocationHandler locationHandler;
    private final MenuHandler menuHandler;
    private final MsgHandler msgHandler;
    private final UnSubscribeHandler unsubscribeHandler;
    private final SubscribeHandler subscribeHandler;
    private final ScanHandler scanHandler;

    @Bean
    public WxMpService wxMpService() {
        final List<WxMpProperties.MpConfig> configs = this.properties.getConfigs();
        if (configs == null) {
            throw new RuntimeException("请添加公众号相关配置,注意别配错了!");
        }
        WxMpService service = new WxMpServiceImpl();
        // 公众号信息缓存起来
        service.setMultiConfigStorages(configs.stream().map(a -> {
            // 默认缓存在内存中,配置redis就放入redis中
            WxMpDefaultConfigImpl configStorage = !redisIsOk() ? new WxMpDefaultConfigImpl() : new WxMpRedisConfigImpl(jedisPool);

            configStorage.setAppId(a.getAppId());
            configStorage.setSecret(a.getSecret());
            configStorage.setToken(a.getToken());
            configStorage.setAesKey(a.getAesKey());
            return configStorage;
        }).collect(Collectors.toMap(WxMpDefaultConfigImpl::getAppId, e -> e, (o, n) -> o)));
        return service;
    }


    /**
     * redis是否可用
     * @return
     */
    private boolean redisIsOk(){
        try {
            Jedis jedis = jedisPool.getResource();
            jedis.ping();
            return true;
        }catch (Exception e){
            e.printStackTrace();
            return false;
        }
    }

    @Bean
    public WxMpMessageRouter wxMpMessageRouter(WxMpService wxMpService) {
        final WxMpMessageRouter newRouter = new WxMpMessageRouter(wxMpService);

        // 记录所有事件的日志 (异步执行)
        newRouter.rule().handler(this.logHandler).next();


        /**---------------------------------------  事件处理 ----------------------------------------**/
        // 接收客服会话管理事件
        newRouter.rule().async(false).msgType(EVENT).event(KF_CREATE_SESSION).handler(this.kfSessionHandler).end();
        newRouter.rule().async(false).msgType(EVENT).event(KF_CLOSE_SESSION).handler(this.kfSessionHandler).end();
        newRouter.rule().async(false).msgType(EVENT).event(KF_SWITCH_SESSION).handler(this.kfSessionHandler).end();
        // 门店审核事件
        newRouter.rule().async(false).msgType(EVENT).event(POI_CHECK_NOTIFY).handler(this.storeCheckNotifyHandler).end();
        // 自定义菜单事件
        newRouter.rule().async(false).msgType(EVENT).event(CLICK).handler(this.menuHandler).end();
        // 点击菜单连接事件
        newRouter.rule().async(false).msgType(EVENT).event(VIEW).handler(this.nullHandler).end();
        // 关注事件
        newRouter.rule().async(false).msgType(EVENT).event(SUBSCRIBE).handler(this.subscribeHandler).end();
        // 取消关注事件
        newRouter.rule().async(false).msgType(EVENT).event(UNSUBSCRIBE).handler(this.unsubscribeHandler).end();
        // 上报地理位置事件
        newRouter.rule().async(false).msgType(EVENT).event(WxConsts.EventType.LOCATION).handler(this.locationHandler).end();
        // 接收地理位置消息
        newRouter.rule().async(false).msgType(WxConsts.XmlMsgType.LOCATION).handler(this.locationHandler).end();
        // 扫码事件
        newRouter.rule().async(false).msgType(EVENT).event(WxConsts.EventType.SCAN).handler(this.scanHandler).end();

        // 默认
        newRouter.rule().async(false).handler(this.msgHandler).end();
        return newRouter;
    }
}


后续还将整理小程序,企业微信的脚手架!!!

你可能感兴趣的:(微信支付宝)