系统运行环境
开发工具
eclipse(idea),mysql5.7(大于5.5),navicat,小程序开发工具
硬件要求
windows操作系统
cpu:2.4GHz
内存:4G
硬盘:100G
开发及运行环境
windows10操作系统
jdk1.8
mysql5.7
谷歌浏览器
详细设计
系统架构
后台开发语言选用java,采用maven构建项目,maven有很多优点,最大得优点就是模块化,依赖自动下载,主要用到springboot框架,springboot框架的有点很多,封装了servlet,提高了开发效率,集成了servlet容器,简单的配置,灵活的应用。还用到了mybatis,mybatis主要封装了jdbc,提供了灵活的sql配置文件。后台管理界面则采用了bootstrap框架,bootstrap扁平化设计,使得界面整体美观大方。js用到vue.js,数据dom绑定,操作更加简单方便。小程序则只用了自身的标签去实现,样式采用了微信官方提供weui样式库。数据库采用mysql,mysql体积小,安装方便灵活,适合中小型项目开发。
数据库设计
会员表(base_member)
字段名称 | 数据类型 | 必填 | 注释 |
id | int(11) | 是 | |
openid | varchar(255) | 否 | 微信openid |
nickname | varchar(255) | 否 | 昵称 |
avatar_url | varchar(255) | 否 | 头像 |
gender | char(1) | 否 | 性别 |
real_name | varchar(50) | 否 | 姓名 |
mobile | varchar(50) | 否 | 手机号码 |
login_name | varchar(50) | 否 | 登录账号 |
password | varchar(100) | 否 | 密码 |
integral | int(11) | 否 | 积分 |
create_time | datetime | 否 | 创建时间 |
图书表(book_book)
字段名称 | 数据类型 | 必填 | 注释 |
id | int(11) | 是 | |
book_name | varchar(255) | 否 | 图书名称 |
category_id | int(11) | 否 | 分类ID |
press | varchar(50) | 否 | 出版社 |
author | varchar(20) | 否 | 作者 |
price | decimal(10,2) | 否 | 价格 |
pic_url | varchar(255) | 否 | 图片 |
status | tinyint(2) | 否 | 上下架 |
stock | int(11) | 否 | 库存 |
describe | text | 否 | 描述 |
create_time | datetime | 否 | 创建时间 |
分类表(book_category)
字段名称 | 数据类型 | 必填 | 注释 |
id | int(11) | 是 | |
category_name | varchar(255) | 否 | 分类名称 |
pic_url | varchar(255) | 否 | 图片 |
sort | int(11) | 否 | 排序 |
type | tinyint(255) | 否 | 类型 |
create_time | datetime | 否 | 创建时间 |
章节表(book_chapter)
字段名称 | 数据类型 | 必填 | 注释 |
id | int(11) | 是 | |
title | varchar(50) | 否 | 标题 |
content | longtext | 否 | 内容 |
book_id | int(11) | 否 | 图书id |
sort | int(11) | 否 | 排序 |
create_time | datetime | 否 | 创建时间 |
评价表(book_evaluation)
字段名称 | 数据类型 | 必填 | 注释 |
id | int(11) | 是 | 主键 |
order_id | int(11) | 否 | 订单id |
member_id | int(11) | 否 | 用户id |
content | varchar(255) | 否 | 评价内容 |
star | int(11) | 否 | |
book_id | int(11) | 否 | 图书id |
create_time | datetime | 否 | 评价时间 |
阅读历史表(book_history)
字段名称 | 数据类型 | 必填 | 注释 |
id | int(11) | 是 | |
book_id | int(11) | 否 | 图书id |
member_id | int(11) | 否 | 会员id |
create_time | datetime | 否 | 创建时间 |
管理员表(sys_user)
字段名称 | 数据类型 | 必填 | 注释 |
id | bigint(20) | 是 | |
username | varchar(50) | 是 | 用户名 |
password | varchar(100) | 否 | 密码 |
salt | varchar(20) | 否 | 盐 |
| varchar(100) | 否 | 邮箱 |
mobile | varchar(100) | 否 | 手机号 |
status | tinyint(4) | 否 | 状态 0:禁用 1:正常 |
create_time | datetime | 否 | 创建时间 |
难点解答
小程序和后台通信通过wx.request方式
演示视频:
基于springboot小说阅读微信小程序源码
后台管理功能截图:
微信小程序:
后端关键代码:
package com.wfuhui.modules.wechat.controller;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import cn.binarywang.wx.miniapp.api.WxMaService;
import cn.binarywang.wx.miniapp.bean.WxMaJscode2SessionResult;
import cn.binarywang.wx.miniapp.bean.WxMaUserInfo;
import me.chanjar.weixin.common.error.WxErrorException;
import com.wfuhui.common.annotation.AuthIgnore;
import com.wfuhui.web.utils.JwtUtils;
import com.wfuhui.common.utils.R;
import com.wfuhui.modules.member.entity.MemberEntity;
import com.wfuhui.modules.member.service.MemberService;
/**
* 微信小程序用户接口
*/
@RestController
@RequestMapping("/api/wechat")
public class WxMaUserController {
private final Logger logger = LoggerFactory.getLogger(this.getClass());
@Autowired
private WxMaService wxService;
@Autowired
private MemberService memberService;
@Autowired
private JwtUtils jwtUtils;
/**
* 登陆接口
*/
@AuthIgnore
@GetMapping("login")
public R login(String code) {
if (StringUtils.isBlank(code)) {
return R.error("empty jscode");
}
try {
WxMaJscode2SessionResult session = this.wxService.getUserService().getSessionInfo(code);
this.logger.info(session.getSessionKey());
this.logger.info(session.getOpenid());
//查询用户信息
MemberEntity user = memberService.queryByOpenid(session.getOpenid());
if(user == null) {
String sessionKey = session.getSessionKey();
return R.error(1, "未注册").put("sessionKey", sessionKey);
}
//生成token
String token = jwtUtils.generateToken(user.getId());
Map<String, Object> map = new HashMap<String, Object>();
map.put("token", token);
map.put("userInfo", user);
return R.ok(map);
} catch (Exception e) {
this.logger.error(e.getMessage(), e);
return R.error();
}
}
/**
* 用户注册
*/
@AuthIgnore
@GetMapping("register")
public R register(String avatarUrl, String nickname, String gender, String code) {
try {
String openid = this.wxService.getUserService().getSessionInfo(code).getOpenid();
//查询用户信息
MemberEntity user = memberService.queryByOpenid(openid);
if(user != null) {
return R.ok();
}
//注册
MemberEntity member = new MemberEntity();
member.setAvatarUrl(avatarUrl);
member.setOpenid(openid);
member.setNickname(filterUtf8mb4(nickname));
member.setGender(gender);
member.setCreateTime(new Date());
memberService.save(member);
return R.ok();
} catch (WxErrorException e) {
e.printStackTrace();
return R.error();
}
}
public static String filterUtf8mb4(String str) {
final int LAST_BMP = 0xFFFF;
StringBuilder sb = new StringBuilder(str.length());
for (int i = 0; i < str.length(); i++) {
int codePoint = str.codePointAt(i);
if (codePoint < LAST_BMP) {
sb.appendCodePoint(codePoint);
} else {
i++;
}
}
return sb.toString();
}
}
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.3.4.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.wfuhui</groupId>
<artifactId>novel-server</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>novel-server</name>
<description>novel project for Spring Boot</description>
<properties>
<java.version>1.8</java.version>
<maven-jar-plugin.version>3.1.1</maven-jar-plugin.version>
<druid.version>1.0.28</druid.version>
<mysql.version>8.0.16</mysql.version>
<mybatis.spring.boot.version>1.3.0</mybatis.spring.boot.version>
<commons.lang.version>2.6</commons.lang.version>
<commons.fileupload.version>1.3.1</commons.fileupload.version>
<commons.io.version>2.5</commons.io.version>
<commons.codec.version>1.10</commons.codec.version>
<shiro.version>1.3.2</shiro.version>
<jwt.version>0.7.0</jwt.version>
<kaptcha.version>0.0.9</kaptcha.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</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>${mybatis.spring.boot.version}</version>
</dependency>
<!--devtools热部署-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<optional>true</optional>
<scope>true</scope>
</dependency>
<dependency>
<groupId>commons-lang</groupId>
<artifactId>commons-lang</artifactId>
<version>${commons.lang.version}</version>
</dependency>
<dependency>
<groupId>commons-fileupload</groupId>
<artifactId>commons-fileupload</artifactId>
<version>${commons.fileupload.version}</version>
</dependency>
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>${commons.io.version}</version>
</dependency>
<dependency>
<groupId>commons-codec</groupId>
<artifactId>commons-codec</artifactId>
</dependency>
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-core</artifactId>
<version>${shiro.version}</version>
</dependency>
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-spring</artifactId>
<version>${shiro.version}</version>
</dependency>
<dependency>
<groupId>com.github.axet</groupId>
<artifactId>kaptcha</artifactId>
<version>${kaptcha.version}</version>
</dependency>
<dependency>
<groupId>com.auth0</groupId>
<artifactId>java-jwt</artifactId>
<version>3.3.0</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
<exclusions>
<exclusion>
<groupId>org.junit.vintage</groupId>
<artifactId>junit-vintage-engine</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>${druid.version}</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<dependency>
<groupId>com.github.binarywang</groupId>
<artifactId>weixin-java-miniapp</artifactId>
<version>3.4.0</version>
</dependency>
<dependency>
<groupId>com.github.binarywang</groupId>
<artifactId>weixin-java-pay</artifactId>
<version>3.4.0</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
<repositories>
<repository>
<id>public</id>
<name>aliyun nexus</name>
<url>http://maven.aliyun.com/nexus/content/groups/public/</url>
<releases>
<enabled>true</enabled>
</releases>
</repository>
</repositories>
<pluginRepositories>
<pluginRepository>
<id>public</id>
<name>aliyun nexus</name>
<url>http://maven.aliyun.com/nexus/content/groups/public/</url>
<releases>
<enabled>true</enabled>
</releases>
<snapshots>
<enabled>false</enabled>
</snapshots>
</pluginRepository>
</pluginRepositories>
</project>
//index.js
//获取应用实例
const app = getApp()
var sliderWidth = 57.6; // 需要设置slider的宽度,用于计算中间位置
Page({
data: {
autoplay: true,
interval: 3000,
duration: 1000,
bannerList: [],
categoryList: [{
id: 1,
categoryName: '男生'
},{
id: 2,
categoryName: '女生'
}],
bookList: [],
recommendList: [],
bookEvaluateList: [],
bookHistoryList: [],
isLoad: false,
page: 1,
pageSize: 9,
tabs: ["最新上架", '最多阅读', '最多评价'],
activeIndex: 0
},
onLoad: function() {
this.getAdvert();
//this.getCategory();
//this.getBook();
this.getRecommend();
var that = this;
wx.getSystemInfo({
success: function (res) {
that.setData({
sliderLeft: (res.windowWidth / that.data.tabs.length - sliderWidth) / 2,
sliderOffset: res.windowWidth / that.data.tabs.length * that.data.activeIndex
});
}
});
},
onShow: function(){
},
tabClick: function (e) {
this.setData({
activeIndex: e.currentTarget.id,
sliderOffset: e.currentTarget.offsetLeft,
page: 1
});
if(e.currentTarget.id == 0){
this.getBook()
}else if(e.currentTarget.id == 1){
this.getBookHistory();
}else if(e.currentTarget.id == 2){
this.getBookEvaluate();
}
},
showInput: function () {
wx.navigateTo({
url: '/pages/book/book-list/index',
})
},
getAdvert: function() {
var that = this;
wx.request({
url: app.globalData.domain + '/api/advert/list',
data: {
position: 'shop'
},
success: function(res) {
that.setData({
bannerList: res.data.advertList
});
}
})
},
getCategory(){
var that = this;
wx.request({
url: app.globalData.domain + '/api/category/list',
data: {
},
success: function (res) {
var categoryList = res.data.categoryList;
var categories = ['全部'];
for(var i = 0; i < categoryList.length; i++){
categories.push(categoryList[i].categoryName)
}
that.setData({
categoryList: res.data.categoryList,
tabs: categories
});
wx.getSystemInfo({
success: function (res) {
that.setData({
sliderLeft: (res.windowWidth / that.data.tabs.length - sliderWidth) / 2,
sliderOffset: res.windowWidth / that.data.tabs.length * that.data.activeIndex
});
}
});
}
})
},
getRecommend(){
var that = this;
var categoryId = '';
if (this.data.activeIndex != 0){
categoryId = this.data.categoryList[this.data.activeIndex - 1].id
}
wx.request({
url: app.globalData.domain + '/api/book/list',
data: {
type: categoryId,
page: that.data.page,
limit: that.data.pageSize,
recommend: 1
},
success: function(res) {
that.setData({
recommendList: res.data.bookList
})
}
})
},
getBookEvaluate(){
var that = this;
wx.request({
url: app.globalData.domain + '/api/book/listEvaluate',
data: {
page: that.data.page,
limit: that.data.pageSize
},
success: function(res) {
that.setData({
bookList: res.data.bookList
})
}
})
},
getBookHistory(){
var that = this;
wx.request({
url: app.globalData.domain + '/api/book/listHistory',
data: {
page: that.data.page,
limit: that.data.pageSize
},
success: function(res) {
that.setData({
bookList: res.data.bookList
})
}
})
},
getBook: function() {
var that = this;
var categoryId = '';
if (this.data.activeIndex != 0){
categoryId = this.data.categoryList[this.data.activeIndex - 1].id
}
wx.request({
url: app.globalData.domain + '/api/book/list',
data: {
type: categoryId,
page: that.data.page,
limit: that.data.pageSize
},
success: function(res) {
that.setData({
bookList: res.data.bookList
})
return;
if (that.data.page == 1) {
that.setData({
bookList: []
});
}
if (res.data.code != 0) {
that.setData({
isLoad: false
});
return;
}
if (res.data.bookList.length == 0) {
that.setData({
isLoad: true
});
return;
}
var book = that.data.bookList;
for (var i = 0; i < res.data.bookList.length; i++) {
book.push(res.data.bookList[i]);
}
that.setData({
bookList: book,
isLoad: false
});
}
})
},
loadMore: function () {
return;
console.log("load more")
var that = this;
var isLoad = this.data.isLoad;
console.log(isLoad)
if (!isLoad) {
this.setData({
page: that.data.page + 1
});
this.getBook();
}
},
onPullDownRefresh: function() {
this.setData({
page: 1
});
wx.showNavigationBarLoading()
this.getAdvert();
//this.getCategory();
this.getBook();
setTimeout(function() {
wx.hideNavigationBarLoading() //完成停止加载
wx.stopPullDownRefresh() //停止下拉刷新
}, 1000);
},
onShareAppMessage: function() {
var path = '/pages/index/index';
if (app.globalData.distributor) {
path = path + "?distributor=" + app.globalData.distributor;
}
return {
title: wx.getStorageSync('storeName'),
path: path,
success: function(res) {
// 转发成功
},
fail: function(res) {
// 转发失败
}
}
},
/**
* 页面上拉触底事件的处理函数
*/
onReachBottom: function () {
this.loadMore();
}
})
基于java springboot的小说阅读微信小程序含后台管理系统源码