前后端分离项目
一:效果展示
图书借阅系统效果展示
二:代码实现
一、设计数据库(表的设计)
1、首先我们要进入图书系统必须要登录,验证身份之后才可进入,角色有:普通用户、管理员
2、其次是图书表
3、图书属于哪个出版社我们也单独设计了一张表
4、然后是借阅表,用户借阅图书的记录
5、最后当用户没有归还图书时,管理员可以进行催还的操作
一共是五张表:
用户表:
CREATE TABLE `user` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`username` varchar(255) DEFAULT NULL,
`password` varchar(255) DEFAULT NULL,
`type` int(255) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8;
图书表:
CREATE TABLE `book` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`name` varchar(255) DEFAULT NULL,
`author` varchar(255) DEFAULT NULL,
`pid` int(255) DEFAULT NULL,
`number` int(11) DEFAULT NULL,
`status` int(11) DEFAULT NULL,
`is_del` int(255) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=15 DEFAULT CHARSET=utf8;
出版社表:
CREATE TABLE `press` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`name` varchar(255) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8;
借阅表:
CREATE TABLE `borrow` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`bid` int(255) DEFAULT NULL,
`uid` int(255) DEFAULT NULL,
`j_time` datetime DEFAULT NULL,
`g_time` datetime DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=5 DEFAULT CHARSET=utf8;
消息表:
CREATE TABLE `mq` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`msg` varchar(255) DEFAULT NULL,
`time` datetime DEFAULT NULL,
`uid` int(11) DEFAULT NULL,
`status` int(255) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=10 DEFAULT CHARSET=utf8;
以上就是该项目中所需的表
二、登录权限
登录页,用户输入用户名和密码,后端校验用户名和密码是否匹配,若匹配则返回登录成功信息,并显示当前登录成功的用户身份 (普通用户 或 管理员),登录后跳转到图书列表页,否则返回登录失败信息
三、图书管理页面
根据角色的不同,显示不同的内容,管理员登陆,可以显示所有图书的信息,但不能显示借阅按钮,可以对图书进行,增删改查。普通用户登陆,可以查询图书信息,并只显示未借阅的图书。并显示借阅按钮。
四、借阅操作
普通用户,点击借阅按钮之后,确定图书的借阅时间,并将图书状态设置为“已借阅”状态,当借阅成功之后,对数据的借阅次数进行+1操作。
五、借阅列表
普通用户,只能看到,自己借阅的书单,并显示“归还”按钮,如果书没有归还,显示归还按钮。当用户点击“归还”按钮之后,将归还时间设置为点击归还的时间。将书的状态,设置为“未借阅”。当管理员进入到借阅列表页面,可以看到所有的借阅记录。催书功能。管理员可以指定某一个未归还的书本信息,并通过RabbitMQ,模拟发送短信,给用户。当用户登陆之后,可以接收发送过来的短信消息。
另外该项目中额外增加了导入导出功能
代码
登录前端:
<template>
<div class="home">
<el-form v-model="login">
用户名:<el-input type="text" v-model="login.username" style="width: 300px"></el-input><br>
账号:<el-input type="text" style="width: 300px" v-model="login.password"></el-input><br>
<el-button type="primary" @click="toLogin()">登录</el-button>
</el-form>
</div>
</template>
<script>
export default {
data(){
return{
login:{}
}
},
methods:{
toLogin(){
this.axios.post("/user/login",this.login).then(res=>{
if(res.data.code==0){
this.$message.error(res.data.msg);
}else{
localStorage.setItem("user",JSON.stringify(res.data.data));
alert("欢迎回来:"+(res.data.data.type==1?"管理员":"普通用户"));
this.$router.push("/book");
}
})
}
}
}
</script>
登录后端:
package cn.jiyun.controlller;
import cn.jiyun.commons.Results;
import cn.jiyun.mapper.UserMapper;
import cn.jiyun.pojo.User;
import com.alibaba.fastjson2.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.google.gson.JsonObject;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.web.bind.annotation.CrossOrigin;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@CrossOrigin
@RestController
@RequestMapping("user")
public class UserController {
@Autowired
UserMapper userMapper;
@Autowired
RedisTemplate redisTemplate;
@RequestMapping("login")
public Results login(@RequestBody User user){
LambdaQueryWrapper<User> qw = new LambdaQueryWrapper<>();
qw.eq(User::getUsername,user.getUsername());
qw.eq(User::getPassword,user.getPassword());
User user1 = userMapper.selectOne(qw);
if(user1==null){
return Results.error("账号或密码错误,请重试!");
}
redisTemplate.boundValueOps("BookUser").set(JSONObject.toJSONString(user1));
return Results.success(user1);
}
}
图书列表展示前端:
<template>
<div>
<h1>当前登录的用户是:{{this.login.type==1?'管理员':'普通用户'}}</h1>
书名:
<el-input v-model="requestParam.name" style="width: 200px" placeholder="请输入内容"></el-input>
作者:
<el-input v-model="requestParam.author" style="width: 200px" placeholder="请输入内容"></el-input>
出版社:
<el-select v-model="requestParam.pid" placeholder="请选择">
<el-option
v-for="item in pressList"
:key="item.id"
:label="item.name"
:value="item.id">
</el-option>
</el-select>
<el-button type="primary" size="mini" @click="findList">搜索</el-button>
<el-button type="primary" size="mini" @click="borrowList">借阅列表</el-button>
<el-button type="primary" v-if="login.type==1" size="mini" @click="daochu">导出</el-button>
<el-upload
class="avatar-uploader"
action="http://localhost:82/book/daoru"
:show-file-list="false"
:on-success="handleAvatarSuccess">
<!-- :before-upload="beforeAvatarUpload">-->
<el-button type="primary" size="mini">导入</el-button>
</el-upload>
<el-button type="primary" v-if="login.type!=1" size="mini" @click="xiaoxi">消息列表</el-button>
<el-dialog title="消息" :visible.sync="dialogxiaoxiVisible">
<el-table
:data="mqList"
stripe
style="width: 100%">
<el-table-column
prop="msg"
label="消息">
</el-table-column>
<el-table-column
prop="time"
label="时间">
</el-table-column>
<el-table-column
prop="status"
label="状态">
<template slot-scope="scope">
{{scope.row.status==1?'未读':'已读'}}
</template>
</el-table-column>
<el-table-column label="操作">
<template slot-scope="scope">
<el-button v-if="scope.row.status==1"
size="mini"
@click="yidu(scope.row.id)">已读
</el-button>
</template>
</el-table-column>
</el-table>
</el-dialog>
<el-table
:data="page.records"
stripe
style="width: 100%">
<el-table-column
prop="id"
label="编号">
</el-table-column>
<el-table-column
prop="name"
label="姓名">
</el-table-column>
<el-table-column
prop="author"
label="作者">
</el-table-column>
<el-table-column
prop="pname"
label="出版社">
</el-table-column>
<el-table-column
prop="number"
label="借阅次数">
</el-table-column>
<el-table-column
prop="status"
label="当前借阅状态">
<!--template:模板 自定义展示内容-->
<template slot-scope="scope">
{{ scope.row.status == 1 ? '借阅中' : '未借阅' }}
</template>
</el-table-column>
<el-table-column label="操作">
<template slot-scope="scope">
<el-button v-if="login.type==1"
size="mini"
@click="handleEdit(scope.row)">修改
</el-button>
<el-button v-if="login.type==1"
type="danger"
size="mini"
@click="handleDel(scope.row.id)">删除
</el-button>
<el-button v-if="login.type!=1"
type="danger"
size="mini"
@click="handleJieShu(scope.row.id)">借阅
</el-button>
</template>
</el-table-column>
</el-table>
<!--分页-->
<el-pagination
@size-change="handleSizeChange"
@current-change="handleCurrentChange"
:current-page="page.current"
:page-sizes="[4, 6, 8, 10]"
:page-size="page.size"
layout="total, sizes, prev, pager, next, jumper"
:total="page.total">
</el-pagination>
<!-- 修改弹窗-->
<el-dialog title="修改书籍" :visible.sync="dialogFormVisible">
<el-form :model="form">
<el-form-item label="活动名称" :label-width="formLabelWidth">
<el-input v-model="form.name" autocomplete="off"></el-input>
</el-form-item>
<el-form-item label="作者" :label-width="formLabelWidth">
<el-input v-model="form.author" autocomplete="off"></el-input>
</el-form-item>
<el-form-item label="借阅次数" :label-width="formLabelWidth">
<el-input v-model="form.number" type="number" autocomplete="off"></el-input>
</el-form-item>
<el-form-item label="出版社" :label-width="formLabelWidth">
<el-select v-model="form.pid" placeholder="请选择">
<el-option
v-for="item in pressList"
:key="item.id"
:label="item.name"
:value="item.id">
</el-option>
</el-select>
</el-form-item>
</el-form>
<div slot="footer" class="dialog-footer">
<el-button @click="dialogFormVisible = false">取 消</el-button>
<el-button type="primary" @click="updateBook()">确 定</el-button>
</div>
</el-dialog>
</div>
</template>
<script>
import axios from "axios";
export default {
data(){
return{
login:{},
pressList:[],
requestParam:{},
pageParam:{
current:1,
size:2,
},
page:{},
dialogFormVisible:false,
form:{},
formLabelWidth:"200px",
mqList:[],
dialogxiaoxiVisible:false,
mqLen:0,
}
},
methods:{
yidu(id){
this.axios.post("/book/yidu?id="+id).then(res=>{
this.findMQList();
})
},
borrowList(){
this.$router.push("/borrowList");
},
handleJieShu(id){
this.axios.post("/book/jieyue?id="+id).then(res=>{
if(res.data.code!=0){
this.$message.success(res.data.msg);
this.findList();
}
})
},
handleDel(id){
this.$confirm('此操作将永久删除该文件, 是否继续?', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}).then(() => {
this.axios.post("/book/delBookById?id="+id).then(res=>{
if(res.data.code!=0){
this.$message.success(res.data.msg);
this.findList();
}
})
}).catch(() => {
this.$message({
type: 'info',
message: '已取消删除'
});
});
},
updateBook(){
this.axios.post("/book/updateBook",this.form).then(res=>{
if(res.data.code!=0){
this.$message.success(res.data.msg);
this.findList();
this.dialogFormVisible=false;
}
})
},
handleAvatarSuccess(){
this.findList();
},
handleEdit(t){
this.form=t;
this.dialogFormVisible=true;
},
handleSizeChange(val) {
this.pageParam.size=val;
this.findList();
},
handleCurrentChange(val) {
this.pageParam.current=val;
this.findList();
},
findList(){
this.axios.post(`/book/bookList?current=${this.pageParam.current}&size=${this.pageParam.size}`,this.requestParam).then(res=>{
this.page=res.data.data;
})
},
findPressList(){
this.axios.post(`/book/findPressList`).then(res=>{
this.pressList=res.data;
})
},
daochu(){
location.href="http://localhost:82/book/daochu";
},
findMQList(){
this.axios.post(`/book/findMQList`).then(res=>{
this.mqList=res.data;
// this.mqLen=this.mqList.length;
// alert(this.mqList[0].msg)
if(this.mqList.length>0){
this.dialogxiaoxiVisible=true;
}
})
},
xiaoxi(){
this.dialogxiaoxiVisible=true;
}
},
created() {
this.login=JSON.parse(localStorage.getItem("user"));
this.findPressList();
this.findList();
this.findMQList();
}
}
</script>
图书列表展示、借阅记录展示后端:
package cn.jiyun.controlller;
import cn.jiyun.commons.Results;
import cn.jiyun.excel.BookExcel;
import cn.jiyun.mapper.BorrowMapper;
import cn.jiyun.mapper.MQMapper;
import cn.jiyun.mapper.PressMapper;
import cn.jiyun.mapper.UserMapper;
import cn.jiyun.pojo.*;
import cn.jiyun.service.BookService;
import com.alibaba.excel.EasyExcel;
import com.alibaba.excel.context.AnalysisContext;
import com.alibaba.excel.read.listener.ReadListener;
import com.alibaba.excel.support.ExcelTypeEnum;
import com.alibaba.fastjson2.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.api.R;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import jdk.nashorn.internal.parser.TokenStream;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.web.bind.annotation.CrossOrigin;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;
import reactor.core.publisher.Flux;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
@CrossOrigin
@RestController
@RequestMapping("book")
public class BookController {
@Autowired
BookService bookService;
@Autowired
PressMapper pressMapper;
@Autowired
RedisTemplate redisTemplate;
@Autowired
private BorrowMapper borrowMapper;
@Autowired
MQMapper mqMapper;
@Autowired
UserMapper userMapper;
@RequestMapping("bookList")
public Results bookList(int current, int size, @RequestBody Book book){
User bookUser = JSONObject.parseObject(redisTemplate.boundValueOps("BookUser").get().toString(), User.class);
if (bookUser.getType()==2) {
book.setStatus(2);
}
Page<Book> bookPage = bookService.bookList(current, size, book);
return Results.success(bookPage);
}
@RequestMapping("findPressList")
public List<Press> findPressList(){
return pressMapper.selectList(null);
}
@RequestMapping("updateBook")
public Results updateBook(@RequestBody Book book){
return bookService.updateBook(book);
}
@RequestMapping("delBookById")
public Results delBookById(Integer id){
return bookService.delBookById(id);
}
@RequestMapping("jieyue")
public Results jieyue(Integer id){
User bookUser = JSONObject.parseObject(redisTemplate.boundValueOps("BookUser").get().toString(), User.class);
Book book=bookService.findBookById(id);
Borrow borrow = new Borrow();
borrow.setBid(book.getId());
borrow.setUid(bookUser.getId());
// borrow.setGTime();
borrow.setJTime(LocalDateTime.now());
borrowMapper.insert(borrow);
book.setNumber(book.getNumber()+1);
book.setStatus(1);
bookService.updateBook(book);
return Results.success("借阅"+book.getName()+"成功");
}
@RequestMapping("borrowList")
public Results borrowList(int current,int size){
User bookUser = JSONObject.parseObject(redisTemplate.boundValueOps("BookUser").get().toString(), User.class);
Borrow borrow = new Borrow();
if(bookUser.getType()!=1){
borrow.setFlag(bookUser.getId());
}
Page<Borrow> borrowPage1 = new Page<>(current, size);
borrowPage1 = borrowMapper.selectFindAll(borrowPage1,borrow);
return Results.success(borrowPage1);
}
@RequestMapping("guiHuan")
public Results guiHuan(Integer id){
Borrow borrow = borrowMapper.selectById(id);
Book book = bookService.findBookById(borrow.getBid());
book.setStatus(2);
bookService.updateBook(book);
borrow.setGTime(LocalDateTime.now());
borrowMapper.updateById(borrow);
return Results.success("归还成功");
}
@RequestMapping("cuiHuan")
public Results cuiHuan(Integer id){
Borrow borrow = borrowMapper.selectById(id);
Book book = bookService.findBookById(borrow.getBid());
MQ mq = new MQ();
User user = userMapper.selectById(borrow.getUid());
User bookUser = JSONObject.parseObject(redisTemplate.boundValueOps("BookUser").get().toString(), User.class);
mq.setMsg(user.getUsername()+"你好,您借阅的《"+book.getName()+"》图书请尽快归还");
mq.setTime(LocalDateTime.now());
mq.setUid(borrow.getUid());
mq.setStatus(1);//1未读,2已读
mqMapper.insert(mq);
return Results.success("催还成功");
}
@RequestMapping("findMQList")
public List<MQ> mqList(){
User bookUser = JSONObject.parseObject(redisTemplate.boundValueOps("BookUser").get().toString(), User.class);
MQ mq = new MQ();
mq.setUid(bookUser.getId());
return mqMapper.selectLis(mq);
}
@RequestMapping("yidu")
public Results yidu(Integer id){
MQ mq = mqMapper.selectById(id);
mq.setStatus(2);
mqMapper.updateById(mq);
return Results.success("");
}
@RequestMapping("daochu")
public void daochu(HttpServletResponse response) throws IOException {
List<Book> records = bookService.bookList(1, 10000, new Book()).getRecords();
List<BookExcel> bookExcels = new ArrayList<>();
for (Book boo : records) {
BookExcel bookExcel = new BookExcel();
bookExcel.setId(boo.getId());
bookExcel.setName(boo.getName());
bookExcel.setPname(boo.getPname());
bookExcel.setStatus(boo.getStatus()==1?"借阅中":"未借阅");
bookExcel.setNumber(boo.getNumber());
bookExcel.setAuthor(boo.getAuthor());
bookExcels.add(bookExcel);
}
response.setHeader("Content-Disposition", "attachment;filename=" + URLEncoder.encode("玩家列表.xlsx", "UTF-8"));
response.setHeader("Connection", "close");
response.setHeader("Content-Type", "application/octet-stream");
EasyExcel.write(response.getOutputStream())
.excelType(ExcelTypeEnum.XLSX)
.head(BookExcel.class)
.sheet("图书列表")
.doWrite(bookExcels);
}
@RequestMapping("daoru")
public void daoru(MultipartFile file) throws IOException {
// 先得到文件的输入流
// 需要从ecxel中获取数据存入该实体类中\
System.out.println(file.getSize());
InputStream inputStream = file.getInputStream();
EasyExcel.read(inputStream, BookExcel.class, new ReadListener<BookExcel>(){
@Override
public void invoke(BookExcel bookExcel, AnalysisContext analysisContext) {
Book book = new Book();
book.setName(bookExcel.getName());
int status=bookExcel.getStatus().equals("借阅中")?1:2;
LambdaQueryWrapper<Press> qw = new LambdaQueryWrapper<>();
qw.eq(Press::getName, bookExcel.getPname());
Press press = pressMapper.selectOne(qw);
book.setStatus(status);
book.setPid(press.getId());
book.setAuthor(bookExcel.getAuthor());
book.setNumber(bookExcel.getNumber());
book.setIsDel(0);
bookService.add(book);
}
@Override
public void doAfterAllAnalysed(AnalysisContext analysisContext) {
}
}).excelType(ExcelTypeEnum.XLSX).sheet(0).doRead();
}
}
借阅记录展示前端:
<template>
<div class="home">
<el-table
:data="page.records"
stripe
style="width: 100%">
<el-table-column
prop="bname"
label="图书">
</el-table-column>
<el-table-column
prop="username"
label="借阅人">
</el-table-column>
<el-table-column
prop="jTime"
label="借阅时间">
</el-table-column>
<el-table-column
prop="gTime"
label="归还时间">
</el-table-column>
<el-table-column label="操作">
<template slot-scope="scope">
<el-button
type="danger" v-if="loginUser.type==2&&scope.row.gTime==null"
size="mini"
@click="guiHuan(scope.row.id)">归还
</el-button>
<el-button
type="danger" v-if="loginUser.type==1&&scope.row.gTime==null"
size="mini"
@click="cuiHuan(scope.row.id)">催还
</el-button>
</template>
</el-table-column>
</el-table>
<!--分页-->
<el-pagination
@size-change="handleSizeChange"
@current-change="handleCurrentChange"
:current-page="page.current"
:page-sizes="[4, 6, 8, 10]"
:page-size="page.size"
layout="total, sizes, prev, pager, next, jumper"
:total="page.total">
</el-pagination>
</div>
</template>
<script>
export default {
data(){
return{
page:{},
pageParam:{
current:1,
size:3,
},
loginUser:{}
}
},
methods:{
guiHuan(id){
this.axios.post(`/book/guiHuan?id=`+id).then(res=>{
this.$message.success(res.data.msg);
this.borrowList();
})
},
cuiHuan(id){
this.axios.post(`/book/cuiHuan?id=`+id).then(res=>{
this.$message.success(res.data.msg);
this.borrowList();
})
},
handleSizeChange(val) {
this.pageParam.size=val;
this.borrowList();
},
handleCurrentChange(val) {
this.pageParam.current=val;
this.borrowList();
},
borrowList(){
this.axios.post(`/book/borrowList?current=${this.pageParam.current}&size=${this.pageParam.size}`).then(res=>{
this.page=res.data.data;
})
}
},
created() {
this.loginUser = JSON.parse(localStorage.getItem("user"));
this.borrowList();
}
}
</script>
以上就是项目的完整代码,希望对每一个在Java道路上努力的人有帮助