后台准备
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与password,wx下的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">
<!– 忽略列,不生成bean 字段 –>
<!– <ignoreColumn column="FRED" /> –>
<!– 指定列的java数据类型 –>
<!– <columnOverride column="LONG_VARCHAR_FIELD" jdbcType="VARCHAR" /> –>
</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
WXS(WeiXin 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>
注意事项
WXS 不依赖于运行时的基础库版本,可以在所有版本的小程序中运行。
WXS 与 JavaScript 是不同的语言,有自己的语法,并不和 JavaScript 一致。
WXS 的运行环境和其他 JavaScript 代码是隔离的,WXS 中不能调用其他 JavaScript 文件中定义的函数,也不能调用小程序提供的API。
WXS 函数不能作为组件的事件回调。
由于运行环境的差异,在 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
综合案例
在utils目录下创建创建common.wxs文件
添加首页数据处理转换方法
会议状态切换
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 '星期日'
}
}
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。
之后开发者服务器可以根据用户标识来生成自定义登录态,用于后续业务逻辑中前后端交互时识别用户身份。
注意事项
会话密钥 session_key 是对用户数据进行 加密签名 的密钥。为了应用自身的数据安全,开发者服务器不应该把会话密钥下发到小程序,也不应该对外提供这个密钥。
临时登录凭证 code 只能使用一次
appId 作用说明
appid 是微信账号的唯一标识,这个是固定不变的;如果了解微信公众号开发的就需要注意一下,小程序的appid 和 公众号的appid 是不一致的
session_key 功能说明微信客户端通过wx.getUserInfo()获取用户的信息后台有时候也需要获取微信客户端的用户信息,因此,就需要利用session_key这个秘钥来从微信平台中获取官方文档原文签名校验以及数据加解密涉及用户的会话密钥 session_key。 开发者应该事先通过 wx.login 登录流程获取会话密钥 session_key 并保存在服务器。为了数据不被篡改,开发者不应该把 session_key 传到小程序客户端等服务器外的环境。
登录-小程序
执行wx.login 登录获取小程序端的code
服务端根据code去微信端获取session_key并且缓存;同时生成access_token 保存在小程序端,维持登录状态;
小程序端请求服务端用户数据时,先wx.checkSession,有效就通过access_token 确定用户,找到session_key;无效就执行wx.login重新登录重新生成access_token,服务端重新获取session_key;
小程序端长时间不使用,服务端的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