基于SpringBoot的个性化推荐的图书借阅管理系统前后台设计

目录
1 需求分析 1
2技术栈 1
3 总体设计 1
3.1 系统功能概述 1
3.1.1 前台系统 1
3.1.2 后台系统 2
3.2 E-R模型 2
3.3 系统流程图 4
4 详细设计 5
4.1 前台系统 5
4.2 后台系统 8
5 总结 10

1 需求分析
图书借阅管理系统是模拟学校图书馆实现的一个具有前后台的web系统.对于读者,能够提供全文检索,个性化推荐,借阅等功能.对于管理员,能够提供可视化数据分析,信息管理等功能.
2技术栈
前端: Layui,jQuery,echarts
后端:Spring Boot,MyBatis,elasticsearch,thymeleaf(一个模板引擎,代替jsp)
开发工具:IDEA,HBuilder,postman,Navicat for MySQL
项目管理工具:Maven
3 总体设计
3.1 系统功能概述
3.1.1 前台系统
(1) 读者登录,注册,修改密码
(2) 基本资料查看,修改
(3) 图书查询(复杂查询),仿京东商城以图片卡片形式分页展示,可点击每本书的评价数进行评价,点击卡片查看详细图书信息以及借阅
(4) 个性化推荐图书:根据读者的兴趣爱好,本文转载自http://www.biyezuopin.vip/onews.asp?id=15210每本书对应读者的浏览量,每本书的借阅量,每本书的评论数进行多重排序,展示在读者面前
(5) 历史记录浏览,查询
(6) 基于elasticsearch搜索引擎的全文检索
3.1.2 后台系统
(1) 管理员登录,修改密码
(2) 读者管理:查看,添加,编辑,删除
(3) 管理员管理:查看,添加,删除
(4) 角色管理
(5) 统计分析:
A.分别以树状图,饼图展示图书分类占比
B.以折线图加饼图展示每年的图书类型借阅情况
C.查询每年的热门图书排名以及借阅次数
(6) 历史记录管理
(7) 图书管理:查看,添加,删除

<!DOCTYPE html>
<!--suppress ALL-->
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1">
    <title>spring-boot-bms</title>
    <link rel="stylesheet" th:href="@{/css/layui.css}" media="all">
    <script th:src="@{/layui.all.js}"></script>
    <script th:src="@{/jquery-3.3.1.js}"></script>
    <script th:src="@{/table_select.js}"></script>
    <script th:src="@{/common.js}"></script>

</head>
<style>
    .layui-form-select .layui-input {
        padding-right: 0px;
        cursor: pointer;
    }
</style>
<body class="layui-layout-body" onload="getUrl()">
<div class="layui-layout layui-layout-admin">
    <div th:replace="layout/adminHeader :: header"></div>
    <div th:replace="layout/left :: left"></div>
    <div class="layui-body" id="main_body">
        <!-- 内容主体区域 -->
        <blockquote class="layui-elem-quote layui-text">
            what are you looking for...
        </blockquote>
        <form class="layui-form" action="/bookManage" accept-charset="UTF-8" th:fragment="book_search">
            <div class="layui-form-item">
                <div class="layui-inline">
                <label class="layui-form-label">ID</label>
                <div class="layui-input-block">
                    <input type="text" name="s_bookId" autocomplete="off" placeholder="请输入" class="layui-input">
                </div>
                </div>
                <div class="layui-inline">
                    <label class="layui-form-label">书名</label>
                    <div class="layui-input-block">
                        <input type="text" name="s_bookName" placeholder="请输入"
                               autocomplete="off" class="layui-input" baiduSug=1>
                    </div>
                </div>
            </div>
            <div class="layui-form-item">
                <div class="layui-inline">
                <label class="layui-form-label">作者</label>
                <div class="layui-input-block">
                    <input type="text" name="s_author" placeholder="请输入"
                           autocomplete="off" class="layui-input" id="find_author">
                </div>
                </div>
                <div class="layui-inline">
                    <label class="layui-form-label">出版社</label>
                    <div class="layui-input-block">
                        <input type="text" name="s_press" placeholder="请输入"
                               autocomplete="off" class="layui-input" id="find_press">
                    </div>
                </div>
            </div>
            <div class="layui-form-item">
                <div class="layui-inline">
                <label class="layui-form-label">分类</label>
                <div class="layui-input-block">
                    <input type="text" name="s_bookType" placeholder="请输入"
                           autocomplete="off" class="layui-input" id="find_type">
                </div>
                </div>
                <div class="layui-inline">
                    <label class="layui-form-label">范围</label>
                    <div class="layui-input-inline" style="width: 100px;">
                        <input type="text" id="date1" name="s_date1" placeholder="yyyy-MM" autocomplete="off" class="layui-input">
                    </div>
                    <div class="layui-form-mid">-</div>
                    <div class="layui-input-inline" style="width: 100px;">
                        <input type="text" id="date2" name="s_date2" placeholder="yyyy-MM" autocomplete="off" class="layui-input">
                    </div>
                </div>
                <div class="layui-inline">
                    <div class="layui-input-block">
                        <button class="layui-btn" lay-submit="" lay-filter="demo1">立即提交</button>
                        <button type="reset" class="layui-btn layui-btn-primary">重置</button>
                    </div>
                </div>
            </div>
        </form>
        <table class="layui-hide" id="demo" lay-filter="test"></table>
    </div>
    <div th:replace="layout/footer :: footer"></div>
</div>
<div th:replace="common :: myInfo"></div>
<div th:replace="common :: myPw"></div>
<div th:replace="common :: showBookRow"></div>
<div th:replace="common :: editBookRow"></div>
<div th:replace="common :: newBook"></div>
<script>
    layui.use(['element','form', 'layedit', 'laydate'], function () {
        var form = layui.form;
        var element = layui.element;
        var laydate = layui.laydate;
        element.init();
        //常规用法
        laydate.render({
            elem: '#date1'
        });
        laydate.render({
            elem: '#date2'
        });

    });
</script>
<!--搜索输入框-->
<script>
    var tableSelect = layui.tableSelect;
    tableSelect.render({
        elem: '#find_author',
        checkedKey: 'inputId',
        table: {
            url: '/input/author'
            , response: {
                statusCode: 200 //规定成功的状态码,默认:0
            }
            , parseData: function (res) {
                return {
                    "code": 200
                    , "msg": ""
                    , "count": res.total
                    , "data": res.list
                }
            },
            cols: [
                [{
                    type: 'radio'
                },
                    {
                        field: 'inputId',
                        title: 'ID'
                    },
                    {
                        field: 'inputName',
                        title: '作者'
                    }
                ]
            ]
        },
        done: function(elem, data) {
            var NEWJSON = []
            layui.each(data.data, function(index, item) {
                NEWJSON.push(item.inputName)
            })
            elem.val(NEWJSON.join(","))
        }
    });
    tableSelect.render({
        elem: '#find_press',
        checkedKey: 'inputId',
        table: {
            url: '/input/press'
            , response: {
                statusCode: 200 //规定成功的状态码,默认:0
            }
            , parseData: function (res) {
                return {
                    "code": 200
                    , "msg": ""
                    , "count": res.total
                    , "data": res.list
                }
            },
            cols: [
                [{
                    type: 'radio'
                },
                    {
                        field: 'inputId',
                        title: 'ID'
                    },
                    {
                        field: 'inputName',
                        title: '出版社'
                    }
                ]
            ]
        },
        done: function(elem, data) {
            var NEWJSON = []
            layui.each(data.data, function(index, item) {
                NEWJSON.push(item.inputName)
            })
            elem.val(NEWJSON.join(","))
        }
    });
    tableSelect.render({
        elem: '#find_type',
        checkedKey: 'inputId',
        table: {
            url: '/input/type'
            , response: {
                statusCode: 200 //规定成功的状态码,默认:0
            }
            , parseData: function (res) {
                return {
                    "code": 200
                    , "msg": ""
                    , "count": res.total
                    , "data": res.list
                }
            },
            cols: [
                [{
                    type: 'radio'
                },
                    {
                        field: 'inputId',
                        title: 'ID'
                    },
                    {
                        field: 'inputName',
                        title: '类型'
                    }
                ]
            ]
        },
        done: function(elem, data) {
            var NEWJSON = []
            layui.each(data.data, function(index, item) {
                NEWJSON.push(item.inputName)
            })
            elem.val(NEWJSON.join(","))
        }
    });
</script>
<script type="text/html" id="barDemo2">
    <a class="layui-btn layui-btn-primary layui-btn-xs" lay-event="detail">查看</a>
    <a class="layui-btn layui-btn-xs" lay-event="edit">编辑</a>
    <a class="layui-btn layui-btn-danger layui-btn-xs" lay-event="del">删除</a>
</script>
<script th:inline="none">
    var args = {};
    args["s_bookId"] = 0, args["s_bookName"] = "#", args["s_author"] = "#", args["s_press"] = "#", args["s_bookType"] = "#", args["s_date1"] = "#", args["s_date2"] = "#";
    function getUrl() {
        if(location.search!=""){
            var str = (location.search.length > 0 ? location.search.substring(1) : "");
            var items = str.split("&");
            var len = items.length, name, value;
            for (i = 0; i < len; i++) {
                item = items[i].split("=");
                if (item[1].length > 0) {
                    name = decodeURIComponent(item[0]);
                    value = decodeURIComponent(item[1]);
                    args[name] = value;
                    console.log(value);
                }
            }
        }
        layui.use('table', function () {
            var table = layui.table;
            //展示已知数据
            table.render({
                elem: '#demo'
                , url: '/book/all'
                , method: 'get'
                , response: {
                    statusCode: 200 //规定成功的状态码,默认:0
                }
                , where: {
                    "bookId": args["s_bookId"],
                    "bookName": args["s_bookName"].toString(),
                    "author": args["s_author"].toString(),
                    "press": args["s_press"].toString(),
                    "bookType": args["s_bookType"].toString(),
                    "date1": args["s_date1"].toString(),
                    "date2": args["s_date2"].toString(),
                }
                , even: true
                , page: true //是否显示分页
                , limits: [3, 4, 5]
                , limit: 4 //每页默认显示的数量
                , parseData: function (res) {
                    console.log(res);
                    return {
                        "code": 200
                        , "msg": ""
                        , "count": res.total
                        , "data": res.list
                    }
                }
                , toolbar: 'default' //开启工具栏,此处显示默认图标,可以自定义模板,详见文档
                , cols: [[ //标题栏
                    {type: 'checkbox', fixed: 'left'}
                    , {field: 'bookId', title: 'ID',  sort: true}
                    , {field: 'bookName', title: '书名'}
                    , {field: 'author', title: '作者'}
                    , {field: 'press', title: '出版社'}
                    , {field: 'bookDate', title: '出版日期',sort: true}
                    , {fixed: 'right',align: 'center', toolbar: '#barDemo2'}
                ]]
                //,skin: 'line' //表格风格

            });
            //监听头工具栏事件
            table.on('toolbar(test)', function (obj) {
                var checkStatus = table.checkStatus(obj.config.id)
                    , data = checkStatus.data; //获取选中的数据
                switch (obj.event) {
                    case 'add':
                        layer.open({
                            type: 1,
                            // title: 'iframe父子操作',
                            shadeClose: true, //点击遮罩关闭层
                            area: ['800px', '520px'],
                            content: $("#new_book"),
                            cancel: function (index, layero) {
                                $("#new_book").hide();
                            }
                        });
                        break;
                    case 'update':
                        if (data.length === 0) {
                            layer.msg('请选择一行');
                        } else if (data.length > 1) {
                            layer.msg('只能同时编辑一个');
                        } else {
                            layer.alert('编辑 [id]:' + checkStatus.data[0].id);
                        }
                        break;
                    case 'delete':
                        if (data.length === 0) {
                            layer.msg('请选择一行');
                        } else {
                            var array = [];
                            for (i = 0; i < data.length; i++) array.push(data[i].bookId);
                            alert(array);
                            $.ajax({
                                type: "post",
                                url: "/book/check",
                                data: {
                                    "array": array
                                },
                                success: function (data) {
                                    if (data > 0) {
                                        alert("删除成功");
                                        window.location.reload();
                                    }
                                }
                            });

                        }
                        break;
                }
                ;
            });
            //监听行工具事件
            table.on('tool(test)', function (obj) { //注:tool 是工具条事件名,test 是 table 原始容器的属性 lay-filter="对应的值"
                var data = obj.data //获得当前行数据
                    , layEvent = obj.event; //获得 lay-event 对应的值
                if (layEvent === 'detail') {
                    layer.msg('查看操作');
                    $.ajax({
                        type: "get",
                        url: "/book/find",
                        data: {
                            "bookId": data.bookId
                        },
                        success: function (data) {
                            $("#bookId").val(data.bookId);
                            $("#bookName").val(data.bookName);
                            $("#author").val(data.author);
                            $("#press").val(data.press);
                            $("#bookDate").val(data.bookDate);
                            $("#bookType").val(data.bookType);
                            $("#bookDesc").val(data.bookDesc);
                            $("#bookImage").attr("src", data.photo);
                            layer.open({
                                type: 1,
                                // title: 'iframe父子操作',
                                shadeClose: true, //点击遮罩关闭层
                                area: ['800px', '520px'],
                                content: $("#show_row"),
                                cancel: function (index, layero) {
                                    $("#show_row").hide();
                                }
                            });

                        }
                    });

                } else if (layEvent === 'del') {
                    layer.confirm('真的删除行么', function (index) {
                        //向服务端发送删除指令
                        $.ajax({
                            type: "post",
                            url: "/book/delete",
                            data: {
                                "bookId": data.bookId
                            },
                            success: function (data) {
                                if (data > 0) {
                                    obj.del(); //删除对应行(tr)的DOM结构
                                    layer.close(index);
                                    alert("图书删除成功");
                                    window.location.reload();
                                } else {
                                    alert("图书删除失败");
                                    window.location.reload();
                                }
                            }
                        });
                    });
                } else if (layEvent === 'edit') {
                    layer.msg('编辑操作');
                    $.ajax({
                        type: "get",
                        url: "/book/find",
                        data: {
                            "bookId": data.bookId
                        },
                        success: function (data) {
                            $("#_bookId").val(data.bookId);
                            $("#_bookName").val(data.bookName);
                            $("#_author").val(data.author);
                            $("#_press").val(data.press);
                            $("#_bookDate").val(data.bookDate);
                            $("#_bookType").val(data.bookType);
                            $("#_bookDesc").val(data.bookDesc);
                            $("#_bookImage").val(data.photo);
                            layer.open({
                                type: 1,
                                // title: 'iframe父子操作',
                                shadeClose: true, //点击遮罩关闭层
                                area: ['800px', '520px'],
                                content: $("#edit_row"),
                                cancel: function (index, layero) {
                                    $("#edit_row").hide();
                                }
                            });

                        }
                    });
                }
            });
        });
    }
</script>
<script>
    // 展示修改密码界面
    $('#parentIframe').on('click', function () {
        layer.open({
            type: 1,
            // title: 'iframe父子操作',
            shadeClose: true, //点击遮罩关闭层
            area: ['800px', '520px'],
            content: $("#info"),
            cancel: function (index, layero) {
                $("#info").hide();
            }
        });
    });
    // 展示基本资料界面
    $('#parentIframe2').on('click', function () {
        layer.open({
            type: 1,
            // title: 'iframe父子操作',
            shadeClose: true, //点击遮罩关闭层
            area: ['800px', '520px'],
            content: $("#owninfo"),
            cancel: function (index, layero) {
                $("#owninfo").hide();
            }
        });
    });
</script>
</body>
</html>

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述


版权声明:本文为sheziqiong原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。