后台交互—springboot+mybatis整合小程序(源码演示)_小程序springboot交互

阿里云国内75折 回扣 微信号:monov8
阿里云国际,腾讯云国际,低至75折。AWS 93折 免费开户实名账号 代冲值 优惠多多 微信号:monov8 飞机:@monov6

后台准备


pom.xml


<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.6.2</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>com.zking.ssm</groupId>
    <artifactId>ssm-oa</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <description>OAPRO</description>
    <properties>
        <java.version>1.8</java.version>
        <fastjson.version>1.2.70</fastjson.version>
        <jackson.version>2.9.8</jackson.version>
    </properties>
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-jdbc</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
​
        <dependency>
            <groupId>org.mybatis.spring.boot</groupId>
            <artifactId>mybatis-spring-boot-starter</artifactId>
            <version>2.2.1</version>
        </dependency>
​
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>5.1.44</version>
            <scope>runtime</scope>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>
        
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>fastjson</artifactId>
            <version>${fastjson.version}</version>
        </dependency>
​
    </dependencies>
​
    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
                <configuration>
                    <excludes>
                        <exclude>
                            <groupId>org.projectlombok</groupId>
                            <artifactId>lombok</artifactId>
                        </exclude>
                    </excludes>
                </configuration>
            </plugin>
​
            <plugin>
                <groupId>org.mybatis.generator</groupId>
                <artifactId>mybatis-generator-maven-plugin</artifactId>
                <version>1.3.2</version>
                <dependencies>
                    <!--使用Mybatis-generator插件不能使用太高版本的mysql驱动 -->
                    <dependency>
                        <groupId>mysql</groupId>
                        <artifactId>mysql-connector-java</artifactId>
                        <version>${mysql.version}</version>
                    </dependency>
                </dependencies>
                <configuration>
                    <overwrite>true</overwrite>
                </configuration>
            </plugin>
        </plugins>
    </build>
​
</project>

配置数据源


appliation.yml

server:
  port: 8080 #指服器端口号
  servlet:
    context-path: /oapro

spring:
  datasource:
    #type连接池类型 DBCP,C3P0,Hikari,Druid,默认为Hikari
    type: com.zaxxer.hikari.HikariDataSource
    driver-class-name: com.mysql.jdbc.Driver
    url: jdbc:mysql://localhost:3306/oapro?useUnicode=true&characterEncoding=UTF-8&useSSL=false
    username: root
    password: 121416
mybatis:
  mapper-locations: classpath*:mapper/*.xml #指定mapper文件位置
  type-aliases-package: com.zking.ssm.model #指定自动生成别名所在包

logging:
  level:
    root:  info
    org.springframework:  info
    org.mybatis:  ERROR
    com.zking.ssm.mapper: debug

oa:
  wx:
    app-id: wxc30d1795f5012e9f
    app-secret: 0ce0ef77621a3efcfc797eb06f929aca
    msgDataFormat: JSON

注意修改数据库名以及username与passwordwx下的id与secret改成自己微信小程序的https://mp.weixin.qq.com/wxamp/wadevelopcode/sandbox

mybatis-generator


生成mapper接口model实体类mapper映射文件

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE generatorConfiguration PUBLIC "-//mybatis.org//DTD MyBatis Generator Configuration 1.0//EN"
        "http://mybatis.org/dtd/mybatis-generator-config_1_0.dtd" >
<generatorConfiguration>
    <!-- 引入配置文件 -->
    <properties resource="jdbc.properties"/>

    <!--指定数据库jdbc驱动jar包的位置-->
    <classPathEntry location="E:\maven\repository\mysql\mysql-connector-java\5.1.44\mysql-connector-java-5.1.44.jar"/>

    <!-- 一个数据库一个context -->
    <context id="infoGuardian">
        <!-- 注释 -->
        <commentGenerator>
            <property name="suppressAllComments" value="true"/><!-- 是否取消注释 -->
            <property name="suppressDate" value="true"/> <!-- 是否生成注释代时间戳 -->
        </commentGenerator>

        <!-- jdbc连接 -->
        <jdbcConnection driverClass="${jdbc.driver}"
                        connectionURL="${jdbc.url}" userId="${jdbc.username}" password="${jdbc.password}"/>

        <!-- 类型转换 -->
        <javaTypeResolver>
            <!-- 是否使用bigDecimal false可自动转化以下类型Long, Integer, Short, etc. -->
            <property name="forceBigDecimals" value="false"/>
        </javaTypeResolver>

        <!-- 01 指定javaBean生成的位置 -->
        <!-- targetPackage指定生成的model生成所在的包名 -->
        <!-- targetProject指定在该项目下所在的路径  -->
        <javaModelGenerator targetPackage="com.zking.ssm.model"
                            targetProject="src/main/java">
            <!-- 是否允许子包即targetPackage.schemaName.tableName -->
            <property name="enableSubPackages" value="false"/>
            <!-- 是否对model添加构造函数 -->
            <property name="constructorBased" value="true"/>
            <!-- 是否针对string类型的字段在set的时候进行trim调用 -->
            <property name="trimStrings" value="false"/>
            <!-- 建立的Model对象是否 不可改变  即生成的Model对象不会有 setter方法只有构造方法 -->
            <property name="immutable" value="false"/>
        </javaModelGenerator>

        <!-- 02 指定sql映射文件生成的位置 om.zking.ssm.-->
        <sqlMapGenerator targetPackage="mapper"
                         targetProject="src/main/resources">
            <!-- 是否允许子包即targetPackage.schemaName.tableName -->
            <property name="enableSubPackages" value="false"/>
        </sqlMapGenerator>

        <!-- 03 生成XxxMapper接口 -->
        <!-- type="ANNOTATEDMAPPER",生成Java Model 和基于注解的Mapper对象 -->
        <!-- type="MIXEDMAPPER",生成基于注解的Java Model 和相应的Mapper对象 -->
        <!-- type="XMLMAPPER",生成SQLMap XML文件和独立的Mapper接口 -->
        <javaClientGenerator targetPackage="com.zking.ssm.mapper"
                             targetProject="src/main/java" type="XMLMAPPER">
            <!-- 是否在当前路径下新加一层schema,false路径com.oop.eksp.user.model true:com.oop.eksp.user.model.[schemaName] -->
            <property name="enableSubPackages" value="false"/>
        </javaClientGenerator>

        <!-- 配置表信息 -->
        <!-- schema即为数据库名 -->
        <!-- tableName为对应的数据库表 -->
        <!-- domainObjectName是要生成的实体类 -->
        <!-- enable*ByExample是否生成 example类 -->
        <!--<table schema="" tableName="t_book" domainObjectName="Book"
               enableCountByExample="false" enableDeleteByExample="false"
               enableSelectByExample="false" enableUpdateByExample="false">
            &lt;!&ndash; 忽略列不生成bean 字段 &ndash;&gt;
            &lt;!&ndash; <ignoreColumn column="FRED" /> &ndash;&gt;
            &lt;!&ndash; 指定列的java数据类型 &ndash;&gt;
            &lt;!&ndash; <columnOverride column="LONG_VARCHAR_FIELD" jdbcType="VARCHAR" /> &ndash;&gt;
        </table>-->

        <table schema="" tableName="wx_user" domainObjectName="WxUser"
               enableCountByExample="false" enableDeleteByExample="false"
               enableSelectByExample="false" enableUpdateByExample="false">
        </table>
    </context>
</generatorConfiguration>

整合mybatis


application.yml

mybatis:
  mapper-locations: classpath*:mapper/*.xml #指定mapper文件位置
  type-aliases-package: com.zking.ssm.model #指定自动生成别名所在包
在启动类
@MapperScan("com.zking.ssm.mapper") //指mapper接口所在包

准备前端的首页的数据


Promise


Promise 是异步编程的一种解决方案比传统的解决方案——回调函数和事件——更合理和更强大。它由社区最早提出和实现ES6 将其写进了语言标准统一了用法原生提供了Promise对象。

所谓Promise简单说就是一个容器里面保存着某个未来才会结束的事件通常是一个异步操作的结果。从语法上说Promise 是一个对象从它可以获取异步操作的消息。Promise 提供统一的 API各种异步操作都可以用同样的方法进行处理。

  • promise运行中有三个状态

  • pending: 等待 (进行中) promise一创建出来就是pending进行中

  • fulfilled: 成功 (已完成), 调用 resolve, 就会将状态从pending改成fulfilled, 且将来就会执行.then

  • rejected: 失败 (拒绝), 调用 reject, 就会将状态从pending改成rejected, 且将来就会执行.catch

  • 注意点

  • 一旦promise的状态发生变化, 状态就会被凝固

  • 如果再调用reject或resolve,进行状态修改就没有意义了

封装request


在/utils/util.js中

/**
 * 封装微信的request请求
 */
function request(url, data = {}, method = "GET") {
  return new Promise(function (resolve, reject) {
    wx.request({
      url: url,
      data: data,
      method: method,
      header: {
        'Content-Type': 'application/json',
      },
      success: function (res) {
        if (res.statusCode == 200) {
            resolve(res.data);//会把进行中改变成已成功
        } else {
          reject(res.errMsg);//会把进行中改变成已失败
        }
      },
      fail: function (err) {
        reject(err)
      }
    })
  });
}

会议展示


  • index/index.js

loadMeetingInfos(){
    let that=this;
    // wx.request({
    //     url: api.MettingInfos,
    //     dataType: 'json',
    //     success(res) {
    //       console.log(res)
    //       that.setData({
    //           lists:res.data.lists
    //       })
    //     }
    //   })
    util.request(api.IndexUrl).then(res=>{
      this.setData({
        lists:res.data.infoList
      })
    }).catch(res=>{
        console.log('服器没有开启使用模拟数据!')
    })
  }
  • 修改meeting/list/list.wxml

WXS


WXSWeiXin Script是小程序的一套脚本语言结合 WXML可以构建出页面的结构。

入门示例


  • 新建一个wxs文件

var toDecimal2 = function (x) {
    var f = parseFloat(x);
    if (isNaN(f)) {
      return '0.00'
    }
    var f = Math.round(x * 100) / 100;
    var s = f.toString();
    var rs = s.indexOf('.');
    if (rs < 0) {
      rs = s.length;
      s += '.';
    }
    while (s.length <= rs + 2) {
      s += '0';
    }
    return s;
  }
  //module.exports = toDecimal2
module.exports = {
    toDecimal2:toDecimal2
}

  • 在wxml中使用

<!--pages/c/c.wxml-->
<wxs src="../../wxs/PageUtils.wxs" module="PageUtils"></wxs>
<wxs module="m1">
var msg = "hello world";
​
module.exports.message = msg;
</wxs>
<view>
    <text>pages/c/c.wxml,</text>
    <text>{{m1.message}}</text>
    <view>
        <text>{{PageUtils.toDecimal2(123.453)}}</text>
    </view>
    <view>
        <button type="primary" bindtap="jump">跳转到D页面</button>
    </view>
</view>

注意事项


  1. WXS 不依赖于运行时的基础库版本可以在所有版本的小程序中运行。

  1. WXS 与 JavaScript 是不同的语言有自己的语法并不和 JavaScript 一致。

  1. WXS 的运行环境和其他 JavaScript 代码是隔离的WXS 中不能调用其他 JavaScript 文件中定义的函数也不能调用小程序提供的API。

  1. WXS 函数不能作为组件的事件回调。

  1. 由于运行环境的差异在 iOS 设备上小程序内的 WXS 会比 JavaScript 代码快 2 ~ 20 倍。在 android 设备上二者运行效率无差异。

以下是一些使用 WXS 的简单示例要完整了解 WXS 语法请参考WXS 语法参考

页面渲染


<!--wxml-->
<wxsmodule="m1">
var msg = "hello world";
​
module.exports.message = msg;
</wxs>
​
<view> {{m1.message}} </view>

页面输出

hello world

数据处理


// page.js
Page({
  data: {
    array: [1, 2, 3, 4, 5, 1, 2, 3, 4]
  }
})
<!--wxml-->
<!--下面的getMax函数接受一个数组且返回数组中最大的元素的值-->
<wxsmodule="m1">
vargetMax=function(array) {
  varmax=undefined;
  for (vari=0; i<array.length; ++i) {
    max=max===undefined?
      array[i] :
      (max>=array[i] ?max : array[i]);
  }
  returnmax;
}
​
module.exports.getMax=getMax;
</wxs>
​
<!--调用wxs里面的getMax函数参数为page.js里面的array-->
<view> {{m1.getMax(array)}} </view>

页面输出

5

综合案例


  1. 在utils目录下创建创建common.wxs文件

  1. 添加首页数据处理转换方法

  • 会议状态切换

var changeStatus=function(state){
  //状态0取消会议1待审核2驳回3待开4进行中5开启投票6结束会议默认值为1
  if(state==0)
    return "取消会议";
  else if(state==1)
    return "待审核";
  else if(state==2)
    return "驳回";
  else if(state==3)
    return "待开";
  else if(state==4)
    return "进行中";
  else if(state==5)
    return "开启投票";
  else
    return "会议结束";
};
  • 参会人数统计

var getCount=function(str){
  //参与者+列席者+主持人==全部参会人员
  //数组分割
  var arr=str.split(',');
  //用于接收数组去重后的结果
  var arr2=[];
  //循环并进行数组去重处理
  for(var i=0;i<arr.length;i++){
    if(arr2.indexOf(arr[i])==-1){
      arr2.push(arr[i]);
    }
  }
  return arr2.length;
}
  • 参会日期转换

function formatDate(ts, option) {
  var date = getDate(ts)
  var year = date.getFullYear()
  var month = date.getMonth() + 1
  var day = date.getDate()
  var week = date.getDay()
  var hour = date.getHours()
  var minute = date.getMinutes()
  var second = date.getSeconds()
  
  //获取 年月日
  if (option == 'YY-MM-DD') return [year, month, day].map(formatNumber).join('-')
  //获取 年月
  if (option == 'YY-MM') return [year, month].map(formatNumber).join('-')
  //获取 年
  if (option == 'YY') return [year].map(formatNumber).toString()
  //获取 月
  if (option == 'MM') return  [mont].map(formatNumber).toString()
  //获取 日
  if (option == 'DD') return [day].map(formatNumber).toString()
  //获取 年月日 周一 至 周日
  if (option == 'YY-MM-DD Week')  return [year, month, day].map(formatNumber).join('-') + ' ' + getWeek(week)
  //获取 月日 周一 至 周日
  if (option == 'MM-DD Week')  return [month, day].map(formatNumber).join('-') + ' ' + getWeek(week)
  //获取 周一 至 周日
  if (option == 'Week')  return getWeek(week)
  //获取 时分秒
  if (option == 'hh-mm-ss') return [hour, minute, second].map(formatNumber).join(':')
  //获取 时分
  if (option == 'hh-mm') return [hour, minute].map(formatNumber).join(':')
  //获取 分秒
  if (option == 'mm-dd') return [minute, second].map(formatNumber).join(':')
  //获取 时
  if (option == 'hh')  return [hour].map(formatNumber).toString()
  //获取 分
  if (option == 'mm')  return [minute].map(formatNumber).toString()
  //获取 秒
  if (option == 'ss') return [second].map(formatNumber).toString()
  //默认 时分秒 年月日
  return [year, month, day].map(formatNumber).join('-') + ' ' + [hour, minute, second].map(formatNumber).join(':')
}
​
function formatNumber(n) {
  n = n.toString()
  return n[1] ? n : '0' + n
}
​
function getWeek(n) {
  switch(n) {
      case 1:
      return '星期一'
      case 2:
      return '星期二'
      case 3:
      return '星期三'
      case 4:
      return '星期四'
      case 5:
      return '星期五'
      case 6:
      return '星期六'
      case 7:
      return '星期日'
  }
}
  1. wxs页面渲染处理

  • 模块输出

module.exports={
  changeStatus:changeStatus,
  getCount:getCount,
  formatDate:formatDate
}
  • 页面渲染

<wxs src="/utils/common.wxs" module="common"></wxs>
...
<block wx:for="{{lists}}" wx:key="id">
  <view class="list">
      <view class="list-img">
        <image src="{{item.image?item.image:'/static/persons/1.jpg'}}"></image>
      </view>
      <view class="list-detail">
        <view class="list-title"><text>{{item.title}}</text></view>
        <view class="list-tag">
          <view>{{common.changeStatus(item.state)}}</view>
          <view><text>{{common.getCount(item.canyuze+','+item.liexize+','+item.zhuchiren)}}</text>人报名</view>
        </view>
        <view class="list-info">
          <text>{{item.location}} | {{common.formatDate(item.starttime)}}</text>
        </view>
      </view>
  </view>
</block>

获取用户昵称头像和昵称


wx.getUserProfile


bindgetuserinfo


登录过程


小程序登录

小程序可以通过微信官方提供的登录能力方便地获取微信提供的用户身份标识快速建立小程序内的用户体系。

  • 说明

  • 调用 wx.login() 获取 临时登录凭证code 并回传到开发者服务器。

  • 调用 auth.code2Session 接口换取 用户唯一标识 OpenID 、 用户在微信开放平台帐号下的唯一标识UnionID若当前小程序已绑定到微信开放平台帐号 和 会话密钥 session_key

  • 之后开发者服务器可以根据用户标识来生成自定义登录态用于后续业务逻辑中前后端交互时识别用户身份。

  • 注意事项

  1. 会话密钥 session_key 是对用户数据进行 加密签名 的密钥。为了应用自身的数据安全开发者服务器不应该把会话密钥下发到小程序也不应该对外提供这个密钥

  1. 临时登录凭证 code 只能使用一次

  • appId 作用说明

  • appid 是微信账号的唯一标识这个是固定不变的如果了解微信公众号开发的就需要注意一下小程序的appid 和 公众号的appid 是不一致的

  • session_key 功能说明微信客户端通过wx.getUserInfo()获取用户的信息后台有时候也需要获取微信客户端的用户信息因此就需要利用session_key这个秘钥来从微信平台中获取官方文档原文签名校验以及数据加解密涉及用户的会话密钥 session_key。 开发者应该事先通过 wx.login 登录流程获取会话密钥 session_key 并保存在服务器。为了数据不被篡改开发者不应该把 session_key 传到小程序客户端等服务器外的环境。

登录-小程序


  1. 执行wx.login 登录获取小程序端的code

  1. 服务端根据code去微信端获取session_key并且缓存同时生成access_token 保存在小程序端维持登录状态

  1. 小程序端请求服务端用户数据时先wx.checkSession有效就通过access_token 确定用户找到session_key无效就执行wx.login重新登录重新生成access_token,服务端重新获取session_key

  1. 小程序端长时间不使用服务端的session_key会失效无法再用session_key去微信端获取数据需要小程序端重新执行登录操作 服务端要获取session_key 只能通过小程序端的登录来操作

wx.checkSession


检查登录态是否过期

wx.login

// pages/auth/login/login.js
var util = require('../../../utils/util.js');
var user = require('../../../utils/user.js');
const app = getApp();
Page({

    /**
     * 页面的初始数据
     */
    data: {
        canIUseGetUserProfile: false, // 用于向前兼容
        lock:false
    },
    onLoad: function(options) {
        // 页面初始化 options为页面跳转所带来的参数
        // 页面渲染完成
        if (wx.getUserProfile) {
          this.setData({
            canIUseGetUserProfile: true
          })
        }
        //console.log('login.onLoad.canIUseGetUserProfile='+this.data.canIUseGetUserProfile)
    },

    /**
     * 生命周期函数--监听页面初次渲染完成
     */
    onReady() {

    },

    /**
     * 生命周期函数--监听页面显示
     */
    onShow() {

    },
    getUserProfile(e) {
        console.log('getUserProfile');
        // 推荐使用wx.getUserProfile获取用户信息开发者每次通过该接口获取用户个人信息均需用户确认
        // 开发者妥善保管用户快速填写的头像昵称避免重复弹窗
        wx.getUserProfile({
            desc: '用于完善会员资料', // 声明获取用户个人信息后的用途后续会展示在弹窗中请谨慎填写
            success: (res) => {
                //console.log(res);
                user.checkLogin().catch(() => {
                    user.loginByWeixin(res.userInfo).then(res => {
                      app.globalData.hasLogin = true;
                      wx.navigateBack({
                        delta: 1
                      })
                    }).catch((err) => {
                      app.globalData.hasLogin = false;
                      if(err.errMsg=="request:fail timeout"){
                        util.showErrorToast('微信登录超时');
                      }else{
                        util.showErrorToast('微信登录失败');
                      }
                      this.setData({
                        lock:false
                      })
                    });
                  });
            },
            fail: (res) => {
                app.globalData.hasLogin = false;
                console.log(res);
                util.showErrorToast('微信登录失败');
            }
        });
    },
    wxLogin: function(e) {
        console.log('wxLogin');
        if (e.detail.userInfo == undefined) {
          app.globalData.hasLogin = false;
          util.showErrorToast('微信登录失败');
          return;
        }
        user.checkLogin().catch(() => {
            user.loginByWeixin(e.detail.userInfo).then(res => {
              app.globalData.hasLogin = true;
              wx.navigateBack({
                delta: 1
              })
            }).catch((err) => {
              app.globalData.hasLogin = false;
              if(err.errMsg=="request:fail timeout"){
                util.showErrorToast('微信登录超时');
              }else{
                util.showErrorToast('微信登录失败');
              }
            });
      
          });
    },
    accountLogin() {
        console.log('开发中....')
    }

})

调用接口获取登录凭证code

wx.request


请求自己小程序服器并且携带了code,userInfo信息

后台


准备数据表


DROP TABLE IF EXISTS `wx_user`;
CREATE TABLE `wx_user`  (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `username` varchar(63) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '用户名称',
  `password` varchar(63) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT '用户密码',
  `gender` tinyint(3) NOT NULL DEFAULT 0 COMMENT '性别0 未知 1男 1 女',
  `birthday` date NULL DEFAULT NULL COMMENT '生日',
  `last_login_time` datetime(0) NULL DEFAULT NULL COMMENT '最近一次登录时间',
  `last_login_ip` varchar(63) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT '最近一次登录IP地址',
  `user_level` tinyint(3) NULL DEFAULT 0 COMMENT '用户层级 0 普通用户1 VIP用户2 区域代理用户',
  `nickname` varchar(63) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT '用户昵称或网络名称',
  `mobile` varchar(20) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT '用户手机号码',
  `avatar` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT '用户头像图片',
  `weixin_openid` varchar(63) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT '微信登录openid',
  `status` tinyint(3) NOT NULL DEFAULT 0 COMMENT '0 可用, 1 禁用, 2 注销',
  `add_time` datetime(0) NULL DEFAULT NULL COMMENT '创建时间',
  `update_time` datetime(0) NULL DEFAULT NULL COMMENT '更新时间',
  `deleted` tinyint(1) NULL DEFAULT 0 COMMENT '逻辑删除',
  `share_user_id` int(11) NULL DEFAULT NULL,
  PRIMARY KEY (`id`) USING BTREE,
  UNIQUE INDEX `user_name`(`username`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 1 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = '用户表' ROW_FORMAT = Compact;

反向生成工具生成


  • WxUser.java

package com.lhm.ssm.model;

import java.util.Date;

public class WxUser {
    private Integer id;

    private String username;

    private String password;

    private Byte gender;

    private Date birthday;

    private Date lastLoginTime;

    private String lastLoginIp;

    private Byte userLevel;

    private String nickname;

    private String mobile;

    private String avatar;

    private String weixinOpenid;

    private Byte status;

    private Date addTime;

    private Date updateTime;

    private Boolean deleted;

    private Integer shareUserId;

    public WxUser(Integer id, String username, String password, Byte gender, Date birthday, Date lastLoginTime, String lastLoginIp, Byte userLevel, String nickname, String mobile, String avatar, String weixinOpenid, Byte status, Date addTime, Date updateTime, Boolean deleted, Integer shareUserId) {
        this.id = id;
        this.username = username;
        this.password = password;
        this.gender = gender;
        this.birthday = birthday;
        this.lastLoginTime = lastLoginTime;
        this.lastLoginIp = lastLoginIp;
        this.userLevel = userLevel;
        this.nickname = nickname;
        this.mobile = mobile;
        this.avatar = avatar;
        this.weixinOpenid = weixinOpenid;
        this.status = status;
        this.addTime = addTime;
        this.updateTime = updateTime;
        this.deleted = deleted;
        this.shareUserId = shareUserId;
    }

    public WxUser() {
        super();
    }

    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    public String getUsername() {
        return username;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    public String getPassword() {
        return password;
    }

    public void setPassword(String password) {
        this.password = password;
    }

    public Byte getGender() {
        return gender;
    }

    public void setGender(Byte gender) {
        this.gender = gender;
    }

    public Date getBirthday() {
        return birthday;
    }

    public void setBirthday(Date birthday) {
        this.birthday = birthday;
    }

    public Date getLastLoginTime() {
        return lastLoginTime;
    }

    public void setLastLoginTime(Date lastLoginTime) {
        this.lastLoginTime = lastLoginTime;
    }

    public String getLastLoginIp() {
        return lastLoginIp;
    }

    public void setLastLoginIp(String lastLoginIp) {
        this.lastLoginIp = lastLoginIp;
    }

    public Byte getUserLevel() {
        return userLevel;
    }

    public void setUserLevel(Byte userLevel) {
        this.userLevel = userLevel;
    }

    public String getNickname() {
        return nickname;
    }

    public void setNickname(String nickname) {
        this.nickname = nickname;
    }

    public String getMobile() {
        return mobile;
    }

    public void setMobile(String mobile) {
        this.mobile = mobile;
    }

    public String getAvatar() {
        return avatar;
    }

    public void setAvatar(String avatar) {
        this.avatar = avatar;
    }

    public String getWeixinOpenid() {
        return weixinOpenid;
    }

    public void setWeixinOpenid(String weixinOpenid) {
        this.weixinOpenid = weixinOpenid;
    }

    public Byte getStatus() {
        return status;
    }

    public void setStatus(Byte status) {
        this.status = status;
    }

    public Date getAddTime() {
        return addTime;
    }

    public void setAddTime(Date addTime) {
        this.addTime = addTime;
    }

    public Date getUpdateTime() {
        return updateTime;
    }

    public void setUpdateTime(Date updateTime) {
        this.updateTime = updateTime;
    }

    public Boolean getDeleted() {
        return deleted;
    }

    public void setDeleted(Boolean deleted) {
        this.deleted = deleted;
    }

    public Integer getShareUserId() {
        return shareUserId;
    }

    public void setShareUserId(Integer shareUserId) {
        this.shareUserId = shareUserId;
    }
}
  • WxUserMapper.java

package com.lhm.ssm.mapper;

import com.zking.ssm.model.WxUser;

public interface WxUserMapper {
    int deleteByPrimaryKey(Integer id);

    int insert(WxUser record);

    int insertSelective(WxUser record);

    WxUser selectByPrimaryKey(Integer id);

    int updateByPrimaryKeySelective(WxUser record);

    int updateByPrimaryKey(WxUser record);

    WxUser queryByOid(String openId);
}
  • WxUserMapper.xml

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" >
<mapper namespace="com.zking.ssm.mapper.WxUserMapper" >
  <resultMap id="BaseResultMap" type="com.zking.ssm.model.WxUser" >
    <constructor >
      <idArg column="id" jdbcType="INTEGER" javaType="java.lang.Integer" />
      <arg column="username" jdbcType="VARCHAR" javaType="java.lang.String" />
      <arg column="password" jdbcType="VARCHAR" javaType="java.lang.String" />
      <arg column="gender" jdbcType="TINYINT" javaType="java.lang.Byte" />
      <arg column="birthday" jdbcType="DATE" javaType="java.util.Date" />
      <arg column="last_login_time" jdbcType="TIMESTAMP" javaType="java.util.Date" />
      <arg column="last_login_ip" jdbcType="VARCHAR" javaType="java.lang.String" />
      <arg column="user_level" jdbcType="TINYINT" javaType="java.lang.Byte" />
      <arg column="nickname" jdbcType="VARCHAR" javaType="java.lang.String" />
      <arg column="mobile" jdbcType="VARCHAR" javaType="java.lang.String" />
      <arg column="avatar" jdbcType="VARCHAR" javaType="java.lang.String" />
      <arg column="weixin_openid" jdbcType="VARCHAR" javaType="java.lang.String" />
      <arg column="status" jdbcType="TINYINT" javaType="java.lang.Byte" />
      <arg column="add_time" jdbcType="TIMESTAMP" javaType="java.util.Date" />
      <arg column="update_time" jdbcType="TIMESTAMP" javaType="java.util.Date" />
      <arg column="deleted" jdbcType="BIT" javaType="java.lang.Boolean" />
      <arg column="share_user_id" jdbcType="INTEGER" javaType="java.lang.Integer" />
    </constructor>
  </resultMap>
  <sql id="Base_Column_List" >
    id, username, password, gender, birthday, last_login_time, last_login_ip, user_level, 
    nickname, mobile, avatar, weixin_openid, status, add_time, update_time, deleted, 
    share_user_id
  </sql>
  <select id="selectByPrimaryKey" resultMap="BaseResultMap" parameterType="java.lang.Integer" >
    select 
    <include refid="Base_Column_List" />
    from wx_user
    where id = #{id,jdbcType=INTEGER}
  </select>
  <delete id="deleteByPrimaryKey" parameterType="java.lang.Integer" >
    delete from wx_user
    where id = #{id,jdbcType=INTEGER}
  </delete>
  <insert id="insert" parameterType="com.zking.ssm.model.WxUser" >
    insert into wx_user (id, username, password, 
      gender, birthday, last_login_time, 
      last_login_ip, user_level, nickname, 
      mobile, avatar, weixin_openid, 
      status, add_time, update_time, 
      deleted, share_user_id)
    values (#{id,jdbcType=INTEGER}, #{username,jdbcType=VARCHAR}, #{password,jdbcType=VARCHAR}, 
      #{gender,jdbcType=TINYINT}, #{birthday,jdbcType=DATE}, #{lastLoginTime,jdbcType=TIMESTAMP}, 
      #{lastLoginIp,jdbcType=VARCHAR}, #{userLevel,jdbcType=TINYINT}, #{nickname,jdbcType=VARCHAR}, 
      #{mobile,jdbcType=VARCHAR}, #{avatar,jdbcType=VARCHAR}, #{weixinOpenid,jdbcType=VARCHAR}, 
      #{status,jdbcType=TINYINT}, #{addTime,jdbcType=TIMESTAMP}, #{updateTime,jdbcType=TIMESTAMP}, 
      #{deleted,jdbcType=BIT}, #{shareUserId,jdbcType=INTEGER})
  </insert>
  <insert id="insertSelective" parameterType="com.zking.ssm.model.WxUser" >
    <selectKey keyProperty="id" order="AFTER" resultType="java.lang.Integer">
      SELECT LAST_INSERT_ID()
    </selectKey>
    insert into wx_user
    <trim prefix="(" suffix=")" suffixOverrides="," >
      <if test="id != null" >
        id,
      </if>
      <if test="username != null" >
        username,
      </if>
      <if test="password != null" >
        password,
      </if>
      <if test="gender != null" >
        gender,
      </if>
      <if test="birthday != null" >
        birthday,
      </if>
      <if test="lastLoginTime != null" >
        last_login_time,
      </if>
      <if test="lastLoginIp != null" >
        last_login_ip,
      </if>
      <if test="userLevel != null" >
        user_level,
      </if>
      <if test="nickname != null" >
        nickname,
      </if>
      <if test="mobile != null" >
        mobile,
      </if>
      <if test="avatar != null" >
        avatar,
      </if>
      <if test="weixinOpenid != null" >
        weixin_openid,
      </if>
      <if test="status != null" >
        status,
      </if>
      <if test="addTime != null" >
        add_time,
      </if>
      <if test="updateTime != null" >
        update_time,
      </if>
      <if test="deleted != null" >
        deleted,
      </if>
      <if test="shareUserId != null" >
        share_user_id,
      </if>
    </trim>
    <trim prefix="values (" suffix=")" suffixOverrides="," >
      <if test="id != null" >
        #{id,jdbcType=INTEGER},
      </if>
      <if test="username != null" >
        #{username,jdbcType=VARCHAR},
      </if>
      <if test="password != null" >
        #{password,jdbcType=VARCHAR},
      </if>
      <if test="gender != null" >
        #{gender,jdbcType=TINYINT},
      </if>
      <if test="birthday != null" >
        #{birthday,jdbcType=DATE},
      </if>
      <if test="lastLoginTime != null" >
        #{lastLoginTime,jdbcType=TIMESTAMP},
      </if>
      <if test="lastLoginIp != null" >
        #{lastLoginIp,jdbcType=VARCHAR},
      </if>
      <if test="userLevel != null" >
        #{userLevel,jdbcType=TINYINT},
      </if>
      <if test="nickname != null" >
        #{nickname,jdbcType=VARCHAR},
      </if>
      <if test="mobile != null" >
        #{mobile,jdbcType=VARCHAR},
      </if>
      <if test="avatar != null" >
        #{avatar,jdbcType=VARCHAR},
      </if>
      <if test="weixinOpenid != null" >
        #{weixinOpenid,jdbcType=VARCHAR},
      </if>
      <if test="status != null" >
        #{status,jdbcType=TINYINT},
      </if>
      <if test="addTime != null" >
        #{addTime,jdbcType=TIMESTAMP},
      </if>
      <if test="updateTime != null" >
        #{updateTime,jdbcType=TIMESTAMP},
      </if>
      <if test="deleted != null" >
        #{deleted,jdbcType=BIT},
      </if>
      <if test="shareUserId != null" >
        #{shareUserId,jdbcType=INTEGER},
      </if>
    </trim>
  </insert>
  <update id="updateByPrimaryKeySelective" parameterType="com.zking.ssm.model.WxUser" >
    update wx_user
    <set >
      <if test="username != null" >
        username = #{username,jdbcType=VARCHAR},
      </if>
      <if test="password != null" >
        password = #{password,jdbcType=VARCHAR},
      </if>
      <if test="gender != null" >
        gender = #{gender,jdbcType=TINYINT},
      </if>
      <if test="birthday != null" >
        birthday = #{birthday,jdbcType=DATE},
      </if>
      <if test="lastLoginTime != null" >
        last_login_time = #{lastLoginTime,jdbcType=TIMESTAMP},
      </if>
      <if test="lastLoginIp != null" >
        last_login_ip = #{lastLoginIp,jdbcType=VARCHAR},
      </if>
      <if test="userLevel != null" >
        user_level = #{userLevel,jdbcType=TINYINT},
      </if>
      <if test="nickname != null" >
        nickname = #{nickname,jdbcType=VARCHAR},
      </if>
      <if test="mobile != null" >
        mobile = #{mobile,jdbcType=VARCHAR},
      </if>
      <if test="avatar != null" >
        avatar = #{avatar,jdbcType=VARCHAR},
      </if>
      <if test="weixinOpenid != null" >
        weixin_openid = #{weixinOpenid,jdbcType=VARCHAR},
      </if>
      <if test="status != null" >
        status = #{status,jdbcType=TINYINT},
      </if>
      <if test="addTime != null" >
        add_time = #{addTime,jdbcType=TIMESTAMP},
      </if>
      <if test="updateTime != null" >
        update_time = #{updateTime,jdbcType=TIMESTAMP},
      </if>
      <if test="deleted != null" >
        deleted = #{deleted,jdbcType=BIT},
      </if>
      <if test="shareUserId != null" >
        share_user_id = #{shareUserId,jdbcType=INTEGER},
      </if>
    </set>
    where id = #{id,jdbcType=INTEGER}
  </update>
  <update id="updateByPrimaryKey" parameterType="com.zking.ssm.model.WxUser" >
    update wx_user
    set username = #{username,jdbcType=VARCHAR},
      password = #{password,jdbcType=VARCHAR},
      gender = #{gender,jdbcType=TINYINT},
      birthday = #{birthday,jdbcType=DATE},
      last_login_time = #{lastLoginTime,jdbcType=TIMESTAMP},
      last_login_ip = #{lastLoginIp,jdbcType=VARCHAR},
      user_level = #{userLevel,jdbcType=TINYINT},
      nickname = #{nickname,jdbcType=VARCHAR},
      mobile = #{mobile,jdbcType=VARCHAR},
      avatar = #{avatar,jdbcType=VARCHAR},
      weixin_openid = #{weixinOpenid,jdbcType=VARCHAR},
      status = #{status,jdbcType=TINYINT},
      add_time = #{addTime,jdbcType=TIMESTAMP},
      update_time = #{updateTime,jdbcType=TIMESTAMP},
      deleted = #{deleted,jdbcType=BIT},
      share_user_id = #{shareUserId,jdbcType=INTEGER}
    where id = #{id,jdbcType=INTEGER}
  </update>

  <select id="queryByOid"  resultMap="BaseResultMap" parameterType="string"  >
    select
    <include refid="Base_Column_List" />
     from wx_user
        where weixin_openid = #{openId}
  </select>
</mapper>

准备封装前端传过来的数据


  • UserInfo

package com.lhm.ssm.model;

import lombok.Data;

import java.io.Serializable;

/**
 * 封装微信UserInfo用户信息
 */
@Data
public class UserInfo implements Serializable {
    private static final long serialVersionUID = -5813029516433359765L;

    private Integer userId;
    private String nickName;
    private String avatarUrl;
    private String country;
    private String province;
    private String city;
    private String language;
    private Byte gender;
    private String phone;
    private Byte userLevel;// 用户层级 0 普通用户1 VIP用户2 区域代理用户
    private String userLevelDesc;// 代理用户描述
    
    private Byte status;//状态
    private String registerDate;//注册日期
    
}
  • WxLoginInfo

package com.lhm.ssm.model;

import lombok.Data;

import java.io.Serializable;

/**
 * 封装请求数据
 */
@Data
public class WxLoginInfo implements Serializable {

    private static final long serialVersionUID = -7722430332896313642L;

    private String code;
    private UserInfo userInfo;

}

小程序服器配置


导入微信小程序SDK

<dependency>
    <groupId>com.github.binarywang</groupId>
    <artifactId>weixin-java-miniapp</artifactId>
    <version>3.3.0</version>
</dependency>

application.yml

oa:
  wx:
    app-id: wxf23b28b5e4ea4d6a
    app-secret: 212621faa31cdf0691367ea45b2b6041
    msgDataFormat: JSON

WxProperties

封装oa.wx的数据

@Data
@Configuration
@ConfigurationProperties(prefix = "oa.wx")
public class WxProperties {
    /**
     * 设置微信小程序的appId
     */
    private String appId;
    /**
     * 设置微信小程序的Secret
     */
    private String appSecret;
    /**
     * 消息数据格式
     */
    private String msgDataFormat;
​
}

WxConfig

注册WxMaService

@Configuration
public class WxConfig {
    @Autowired
    private WxProperties properties;
​
    @Bean
    public WxMaConfig wxMaConfig() {
        WxMaInMemoryConfig config = new WxMaInMemoryConfig();
        config.setAppid(properties.getAppId());
        config.setSecret(properties.getAppSecret());
        config.setMsgDataFormat(properties.getMsgDataFormat());
        return config;
    }
​
    @Bean
    public WxMaService wxMaService(WxMaConfig maConfig) {
        WxMaService service = new WxMaServiceImpl();
        service.setWxMaConfig(maConfig);
        return service;
    }
​
}

WxAuthController


@RequestMapping("/wx/auth")
public class WxAuthController {
    @Autowired
    private WxMaService wxService;
     @PostMapping("login_by_weixin")
    public Object loginByWeixin(@RequestBody WxLoginInfo wxLoginInfo, HttpServletRequest request) {
​
        //客户端需携带code与userInfo信息
        String code = wxLoginInfo.getCode();
        UserInfo userInfo = wxLoginInfo.getUserInfo();
        if (code == null || userInfo == null) {
            return ResponseUtil.badArgument();
        }
        //调用微信sdk获取openId及sessionKey
        String sessionKey = null;
        String openId = null;
        try {
            WxMaJscode2SessionResult result = this.wxService.getUserService().getSessionInfo(code);
            sessionKey = result.getSessionKey();//session id
            openId = result.getOpenid();//用户唯一标识 OpenID
        } catch (Exception e) {
            e.printStackTrace();
        }
​
        if (sessionKey == null || openId == null) {
            log.error("微信登录,调用官方接口失败{}", code);
            return ResponseUtil.fail();
        }else{
            log.info("openId={},sessionKey={}",openId,sessionKey);
        }
        //根据openId查询wx_user表
        //如果不存在初始化wx_user,并保存到数据库中
        //如果存在更新最后登录时间
        //....
        // token
        UserToken userToken = null;
        try {
            userToken = UserTokenManager.generateToken(user.getId());
        } catch (Exception e) {
            log.error("微信登录失败,生成token失败{}", user.getId());
            e.printStackTrace();
            return ResponseUtil.fail();
        }
        userToken.setSessionKey(sessionKey);
        log.info("SessionKey={}",UserTokenManager.getSessionKey(user.getId()));
        Map<Object, Object> result = new HashMap<Object, Object>();
        result.put("token", userToken.getToken());
        result.put("tokenExpire", userToken.getExpireTime().toString());
        result.put("userInfo", userInfo);
        //....
​
​
        log.info("【请求结束】微信登录,响应结果:{}", JSONObject.toJSONString(result));
​
        return ResponseUtil.ok(result);
    }

响应给客户端数据有

token userInfo

登录-小程序


将oa-mini项目导入到微信开发者工具中这里注意一定要修改appid。

点击"修改"将弹出一个对话框请在对话框中输入你的小程序测试号appid

login.js


user.loginByWeixin(res.userInfo).then(res => {
    app.globalData.hasLogin = true;
    wx.navigateBack({
    delta: 1
    })
})

user.js


function loginByWeixin(userInfo) {
  return new Promise(function(resolve, reject) {
    return login().then((res) => {
      //登录远程服务器
      util.request(api.AuthLoginByWeixin, {
        code: res.code,
        userInfo: userInfo
      }, 'POST').then(res => {
        if (res.errno === 0) {
          //存储用户信息
          wx.setStorageSync('userInfo', res.data.userInfo);
          wx.setStorageSync('token', res.data.token);
          resolve(res);
        } else {
          reject(res);
        }
      })
将userInfo,token数据保存到本地

util.js


function request(url, data = {}, method = "GET") {
  return new Promise(function (resolve, reject) {
    wx.request({
      url: url,
      data: data,
      method: method,
      timeout:6000,
      header: {
        'Content-Type': 'application/json',
        'X-OA-Token': wx.getStorageSync('token')
      },
如果使用util.request函数每次请求都会携带'X-OA-Token': wx.getStorageSync('token');而服器已经保存了所有的token,所以服器通过token区分每个客户端

emoji


mysql的utf8编码的一个字符最多3个字节但是一个emoji表情为4个字节所以utf8不支持存储emoji表情。但是utf8的超集utf8mb4一个字符最多能有4字节所以能支持emoji表情的存储。

Linux系统中MySQL的配置文件为my.cnf。

Winows中的配置文件为my.ini。

[mysql]
# 设置mysql客户端默认字符集
default-character-set=utf8mb4

[mysqld]
#设置3306端口
port = 3306
# 设置mysql的安装目录
basedir=D:\\tools\\mysql-5.7.23-winx64
# 设置mysql数据库的数据的存放目录
datadir=D:\\tools\\mysql-5.7.23-winx64\\data
# 允许最大连接数
max_connections=200
# 服务端使用的字符集默认为8比特编码的latin1字符集
character-set-server=utf8mb4
# 创建新表时将使用的默认存储引擎
default-storage-engine=INNODB
阿里云国内75折 回扣 微信号:monov8
阿里云国际,腾讯云国际,低至75折。AWS 93折 免费开户实名账号 代冲值 优惠多多 微信号:monov8 飞机:@monov6
标签: Spring