技术选型
JAVA版本:JDK17
数据库:Mysql5.7+Navicat
后端框架:SpringBoot3.0.6 + SpringMVC + Mybatis-Plus3.5.0
权限控制:SpringSecurity
前端框架:AdminLTE2
模板引擎:Thymeleaf
工具类:发邮件工具类、生成验证码工具类
其他技术:lombok、ajax、logback
- 将数据库脚本导入数据库
- 创建名为 "travel" springboot项目, 引入相关依赖
<?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>3.0.6</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.hgy</groupId>
<artifactId>travel</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>travel</name>
<description>travel</description>
<properties>
<java.version>17</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>com.mysql</groupId>
<artifactId>mysql-connector-j</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<!-- mybatis-Plus-->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.5.0</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>
</plugins>
</build>
</project>
- 编写配置文件 application.yml
#端口
server:
port: 80
#数据源
spring:
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql:///travel?serverTimezone=UTC
username: root
password: root
#配置mybatis-plus
mybatis-plus:
global-config:
db-config:
#主键生成策略为自增
id-type: auto
configuration:
#关闭列名自动驼峰命名映射
map-underscore-to-camel-case: false
#开启sql日志
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
# 日志格式
logging:
pattern:
console: '%d{HH:mm:ss.SSS} %clr(%-5level) --- [%-15thread] %cyan(%-50logger{50}):%msg%n'
- 在src目录中创建包
- 将数据库相关实体类复制到
pojo
包
- 在
controller
包编写PageController类
,用于跳转到Thymeleaf页面, 能跳转到前台页面也可以跳转到后台页面
package com.hgy.travel.controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
/**
* TODO 类描述
*
* @author HGY
* @date 2023/5/10 20:01
*/
@Controller
public class PageController {
//访问后台页面
@RequestMapping("/backstage/{page}")
public String showPageBackstage(@PathVariable String page){
return "/backstage/" + page;
}
//访问前台页面
@RequestMapping("/frontdesk/{page}")
public String showPageFrontdesk(@PathVariable String page){
return "/frontdesk/" + page;
}
//忽略访问项目logo 可以写也可以不写
@RequestMapping("favicon.ico")
@ResponseBody
void returnNoFavicon(){}
}
- 在templates下编写前台和后台文件夹然后再backstage下创建一个demo.html然后启动项目进行测试
项目搭建_AdminLTE
官网:
项目分为管理员端和用户端。管理员端负责管理网站资源,发布旅游产品;用户端可以查询旅游产品,收藏旅游产品等。两端使用的页面风格不同。在项目中,我们使用AdminLTE框架作为管理员端页面,使用自己编写的网页作为用户端页面。
AdminLTE是一款建立在bootstrap和jquery之上的开源的模板主题工具,它提供了一系列响应的、 可重复使用的组件,并内置了多个模板页面;同时自适应多种屏幕分辨率,兼容PC和移动端。通过AdminLTE,我们可以快速的创建一个响应式的Html5网站。AdminLTE框架在网页架构与设计上,有很大的辅助作用,尤其是前端架构设计师,用好AdminLTE 不但美观,而且可以免去写很大CSS与JS的工作量。
使用AdminLTE非常简单,只需要根据需求将需要的组件复制到我们的页面中即可。
- 将dist中的静态资源复制到
/static/backstage
中。
编写后台首页
使用AdminLTE的模板页面编写后台首页index.html
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<!-- 页面meta -->
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<title>首页</title>
<link rel="stylesheet" href="/backstage/plugins/bootstrap/css/bootstrap.min.css">
<link rel="stylesheet" href="/backstage/plugins/font-awesome/css/font-awesome.min.css">
<link rel="stylesheet" href="/backstage/plugins/ionicons/css/ionicons.min.css">
<link rel="stylesheet" href="/backstage/plugins/iCheck/square/blue.css">
<link rel="stylesheet" href="/backstage/plugins/morris/morris.css">
<link rel="stylesheet" href="/backstage/plugins/jvectormap/jquery-jvectormap-1.2.2.css">
<link rel="stylesheet" href="/backstage/plugins/datepicker/datepicker3.css">
<link rel="stylesheet" href="/backstage/plugins/daterangepicker/daterangepicker.css">
<link rel="stylesheet" href="/backstage/plugins/bootstrap-wysihtml5/bootstrap3-wysihtml5.min.css">
<link rel="stylesheet" href="/backstage/plugins/datatables/dataTables.bootstrap.css">
<link rel="stylesheet" href="/backstage/plugins/treeTable/jquery.treetable.css">
<link rel="stylesheet" href="/backstage/plugins/treeTable/jquery.treetable.theme.default.css">
<link rel="stylesheet" href="/backstage/plugins/select2/select2.css">
<link rel="stylesheet" href="/backstage/plugins/colorpicker/bootstrap-colorpicker.min.css">
<link rel="stylesheet" href="/backstage/plugins/bootstrap-markdown/css/bootstrap-markdown.min.css">
<link rel="stylesheet" href="/backstage/plugins/adminLTE/css/AdminLTE.css">
<link rel="stylesheet" href="/backstage/plugins/adminLTE/css/skins/_all-skins.min.css">
<link rel="stylesheet" href="/backstage/css/style.css">
<link rel="stylesheet" href="/backstage/plugins/ionslider/ion.rangeSlider.css">
<link rel="stylesheet" href="/backstage/plugins/ionslider/ion.rangeSlider.skinNice.css">
<link rel="stylesheet" href="/backstage/plugins/bootstrap-slider/slider.css">
<link rel="stylesheet" href="/backstage/plugins/bootstrap-datetimepicker/bootstrap-datetimepicker.css">
</head>
<body class="hold-transition skin-purple sidebar-mini">
<div class="wrapper">
<!-- 页面头部 -->
<header class="main-header">
<!-- Logo -->
<a href="all-admin-index.html" class="logo">
<!-- mini logo for sidebar mini 50x50 pixels -->
<span class="logo-mini"><b>数据</b></span>
<!-- logo for regular state and mobile devices -->
<span class="logo-lg"><b>数据</b>后台管理</span>
</a>
<!-- Header Navbar: style can be found in header.less -->
<nav class="navbar navbar-static-top">
<!-- Sidebar toggle button-->
<a href="#" class="sidebar-toggle" data-toggle="offcanvas" role="button">
<span class="sr-only">Toggle navigation</span>
</a>
<div class="navbar-custom-menu">
<ul class="nav navbar-nav">
<!-- User Account: style can be found in dropdown.less -->
<li class="dropdown user user-menu">
<a href="#" class="dropdown-toggle" data-toggle="dropdown">
<img src="/backstage/img/user2-160x160.jpg" class="user-image" alt="User Image">
<span class="hidden-xs">管理员</span>
</a>
<ul class="dropdown-menu">
<!-- User image -->
<li class="user-header">
<img src="/backstage/img/user2-160x160.jpg" class="img-circle" alt="User Image">
<p>
管理员 - 数据管理员
</p>
</li>
<!-- Menu Footer-->
<li class="user-footer">
<div class="pull-right">
<a href="/admin/logout" class="btn btn-default btn-flat">注销</a>
</div>
</li>
</ul>
</li>
</ul>
</div>
</nav>
</header>
<!-- 页面头部 /-->
<!-- 导航侧栏 -->
<aside class="main-sidebar">
<!-- sidebar: style can be found in sidebar.less -->
<section class="sidebar">
<!-- Sidebar user panel -->
<div class="user-panel">
<div class="pull-left image">
<img src="/backstage/img/user2-160x160.jpg" class="img-circle" alt="User Image">
</div>
<div class="pull-left info">
<p>管理员</p>
<a href="#"><i class="fa fa-circle text-success"></i> 在线</a>
</div>
</div>
<!-- sidebar menu: : style can be found in sidebar.less -->
<ul class="sidebar-menu">
<li class="header">菜单</li>
<li id="admin-index"><a href="/backstage/index"><i class="fa fa-dashboard"></i> <span>首页</span></a>
</li>
<!-- 菜单 -->
<li class="treeview" id="system">
<a href="#">
<i class="fa fa-folder"></i> <span>系统管理</span>
<span class="pull-right-container">
<i class="fa fa-angle-left pull-right"></i>
</span>
</a>
<ul class="treeview-menu">
<li id="admin">
<a href="/backstage/admin/all">
<i class="fa fa-circle-o"></i> 用户管理
</a>
</li>
<li id="role">
<a href="/backstage/role/all">
<i class="fa fa-circle-o"></i> 角色管理
</a>
</li>
<li id="permission">
<a href="/backstage/permission/all">
<i class="fa fa-circle-o"></i> 权限管理
</a>
</li>
</ul>
</li>
<li class="treeview" id="business">
<a href="#">
<i class="fa fa-laptop"></i> <span>业务管理</span>
<span class="pull-right-container">
<i class="fa fa-angle-left pull-right"></i>
</span>
</a>
<ul class="treeview-menu">
<li id="category">
<a href="/backstage/category/all">
<i class="fa fa-circle-o"></i> 类型管理
</a>
</li>
<li id="product">
<a href="/backstage/product/all">
<i class="fa fa-circle-o"></i> 产品管理
</a>
</li>
</ul>
</li>
</ul>
</section>
<!-- /.sidebar -->
</aside>
<!-- 导航侧栏 /-->
<!-- 内容区域 -->
<!-- @@master = admin-layout.html-->
<!-- @@block = content -->
<div class="content-wrapper">
</div>
<!-- @@close -->
<!-- 内容区域 /-->
<!-- 底部导航 -->
<footer class="main-footer">
<div class="pull-right hidden-xs">
<b>Version</b> 1.0.8
</div>
<strong>Copyright © <a href="http://www.itbaizhan.com">JAVA研究院</a>.</strong> All rights reserved.
</footer>
<!-- 底部导航 /-->
</div>
<script src="/backstage/plugins/jQuery/jquery-2.2.3.min.js"></script>
<script src="/backstage/plugins/jQueryUI/jquery-ui.min.js"></script>
<script>
$.widget.bridge('uibutton', $.ui.button);
</script>
<script src="/backstage/plugins/bootstrap/js/bootstrap.min.js"></script>
<script src="/backstage/plugins/raphael/raphael-min.js"></script>
<script src="/backstage/plugins/morris/morris.min.js"></script>
<script src="/backstage/plugins/sparkline/jquery.sparkline.min.js"></script>
<script src="/backstage/plugins/jvectormap/jquery-jvectormap-1.2.2.min.js"></script>
<script src="/backstage/plugins/jvectormap/jquery-jvectormap-world-mill-en.js"></script>
<script src="/backstage/plugins/knob/jquery.knob.js"></script>
<script src="/backstage/plugins/daterangepicker/moment.min.js"></script>
<script src="/backstage/plugins/daterangepicker/daterangepicker.js"></script>
<script src="/backstage/plugins/daterangepicker/daterangepicker.zh-CN.js"></script>
<script src="/backstage/plugins/datepicker/bootstrap-datepicker.js"></script>
<script src="/backstage/plugins/datepicker/locales/bootstrap-datepicker.zh-CN.js"></script>
<script src="/backstage/plugins/bootstrap-wysihtml5/bootstrap3-wysihtml5.all.min.js"></script>
<script src="/backstage/plugins/slimScroll/jquery.slimscroll.min.js"></script>
<script src="/backstage/plugins/fastclick/fastclick.js"></script>
<script src="/backstage/plugins/iCheck/icheck.min.js"></script>
<script src="/backstage/plugins/adminLTE/js/app.min.js"></script>
<script src="/backstage/plugins/treeTable/jquery.treetable.js"></script>
<script src="/backstage/plugins/select2/select2.full.min.js"></script>
<script src="/backstage/plugins/colorpicker/bootstrap-colorpicker.min.js"></script>
<script src="/backstage/plugins/bootstrap-wysihtml5/bootstrap-wysihtml5.zh-CN.js"></script>
<script src="/backstage/plugins/bootstrap-markdown/js/bootstrap-markdown.js"></script>
<script src="/backstage/plugins/bootstrap-markdown/locale/bootstrap-markdown.zh.js"></script>
<script src="/backstage/plugins/bootstrap-markdown/js/markdown.js"></script>
<script src="/backstage/plugins/bootstrap-markdown/js/to-markdown.js"></script>
<script src="/backstage/plugins/ckeditor/ckeditor.js"></script>
<script src="/backstage/plugins/input-mask/jquery.inputmask.js"></script>
<script src="/backstage/plugins/input-mask/jquery.inputmask.date.extensions.js"></script>
<script src="/backstage/plugins/input-mask/jquery.inputmask.extensions.js"></script>
<script src="/backstage/plugins/datatables/jquery.dataTables.min.js"></script>
<script src="/backstage/plugins/datatables/dataTables.bootstrap.min.js"></script>
<script src="/backstage/plugins/chartjs/Chart.min.js"></script>
<script src="/backstage/plugins/flot/jquery.flot.min.js"></script>
<script src="/backstage/plugins/flot/jquery.flot.resize.min.js"></script>
<script src="/backstage/plugins/flot/jquery.flot.pie.min.js"></script>
<script src="/backstage/plugins/flot/jquery.flot.categories.min.js"></script>
<script src="/backstage/plugins/ionslider/ion.rangeSlider.min.js"></script>
<script src="/backstage/plugins/bootstrap-slider/bootstrap-slider.js"></script>
<script src="/backstage/plugins/bootstrap-datetimepicker/bootstrap-datetimepicker.js"></script>
<script src="/backstage/plugins/bootstrap-datetimepicker/locales/bootstrap-datetimepicker.zh-CN.js"></script>
<script>
$(document).ready(function () {
// 选择框
$(".select2").select2();
// WYSIHTML5编辑器
$(".textarea").wysihtml5({
locale: 'zh-CN'
});
});
// 设置激活菜单
function setSidebarActive(tagUri) {
var liObj = $("#" + tagUri);
if (liObj.length > 0) {
liObj.parent().parent().addClass("active");
liObj.addClass("active");
}
}
$(document).ready(function () {
setSidebarActive("admin-blank");
});
</script>
</body>
</html>
提取统一后台模板
把头、菜单、footer、重复引入的资源等提取到一个模板.
提取完的index
后台用户管理_管理员列表
后台用户也称为管理员,每个管理员能在后台进行的操作不同,所以不同的管理员有不同的权限。在项目中,权限表的设计为用户—角色
多对多,角色—权限
多对多,既一个用户有多个角色,一个角色有多个权限。所以后台首先要拥有用户管理、角色管理、权限管理的功能。
我们首先编写用户管理中的查询用户功能。
编写AdminMapper
编写AdminService
在启动类扫描mapper
接口,注册mybatis-plus
分页插件
编写AdminController
管理员列表_前端页面
编写页面admin_all.html
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<title>用户管理</title>
<th:block th:replace="/backstage/common_resources::common_css"></th:block>
<th:block th:replace="/backstage/common_resources::common_js"></th:block>
</head>
<body class="hold-transition skin-purple sidebar-mini">
<!--头部-->
<header th:replace="/backstage/common_header::header"></header>
<!--菜单-->
<aside th:replace="/backstage/common_aside::aside"></aside>
<div class="wrapper">
<div class="content-wrapper">
<!-- 内容头部 -->
<section class="content-header">
<h1>
用户管理
<small>用户列表</small>
</h1>
<ol class="breadcrumb">
<li><a href="@{/backstage/index}"><i class="fa fa-dashboard"></i> 首页</a></li>
<li><a href="@{/backstage/admin/all}">用户管理</a></li>
<li class="active">数据列表</li>
</ol>
</section>
<!-- 内容头部 /-->
<!-- 正文区域 -->
<section class="content">
<!-- .box-body -->
<div class="box box-primary">
<div class="box-header with-border">
<h3 class="box-title">列表</h3>
</div>
<div class="box-body">
<!-- 数据表格 -->
<div class="table-box">
<!--工具栏-->
<div class="pull-left">
<div class="form-group form-inline">
<div class="btn-group">
<button type="button" class="btn btn-default" title="新建"><i
class="fa fa-file-o"></i> 新建
</button>
</div>
</div>
</div>
<!--数据列表-->
<table id="dataList" class="table table-bordered table-striped table-hover dataTable">
<thead>
<tr>
<th class="" style="padding-right:0px;">
<input id="selall" type="checkbox" class="icheckbox_square-blue">
</th>
<th>用户名</th>
<th>邮箱</th>
<th>联系电话</th>
<th>状态</th>
<th class="text-center">操作</th>
</tr>
</thead>
<tbody>
<tr th:each="admin:${adminPage.records}">
<td><input name="ids" type="checkbox"></td>
<td th:text="${admin.username}"></td>
<td th:text="${admin.email}"></td>
<td th:text="${admin.phoneNum}"></td>
<td th:text="${admin.status}"></td>
<td class="text-center">
<a th:href="@{/backstage/admin/desc(aid=${admin.aid})}" class="btn bg-olive btn-xs">详情</a>
<a th:href="@{/backstage/admin/edit(aid=${admin.aid})}" class="btn bg-olive btn-xs">修改</a>
<a th:href="@{/backstage/admin/findRole(aid=${admin.aid})}" class="btn bg-olive btn-xs">分配角色</a>
<a th:href="@{/backstage/admin/updateStatus(aid=${admin.aid})}" class="btn bg-olive btn-xs">启用/禁用</a>
</td>
</tr>
</tbody>
</table>
<!--数据列表/-->
</div>
<!-- 数据表格 /-->
</div>
<!-- /.box-body -->
<!-- .box-footer-->
<div class="box-footer">
<div class="pull-left">
<div class="form-group form-inline">
总共<span th:text="${adminPage.pages}"></span>页,
共<span th:text="${adminPage.total}"></span>条数据。
</div>
</div>
<div class="box-tools pull-right">
<ul class="pagination">
<li>
<a href="#" aria-label="Previous">首页</a>
</li>
<li><a href="#">上一页</a></li>
<li><a href="#">1</a></li>
<li><a href="#">2</a></li>
<li><a href="#">3</a></li>
<li><a href="#">4</a></li>
<li><a href="#">5</a></li>
<li><a href="#">下一页</a></li>
<li>
<a href="#" aria-label="Next">尾页</a>
</li>
</ul>
</div>
</div>
<!-- /.box-footer-->
</div>
</section>
<!-- 正文区域 /-->
</div>
</div>
<!--尾部-->
<footer th:replace="/backstage/common_footer::footer"></footer>
</body>
</html>
在通用页面引入分页插件
在admin_all页面下编写分页插件
新增管理员_前端页面
编写页面admin_add.html
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<title>新增用户</title>
<th:block th:replace="/backstage/common_resources::common_css"></th:block>
<th:block th:replace="/backstage/common_resources::common_js"></th:block>
</head>
<body class="hold-transition skin-purple sidebar-mini">
<!--头部-->
<header th:replace="/backstage/common_header::header"></header>
<!--菜单-->
<aside th:replace="/backstage/common_aside::aside"></aside>
<div class="wrapper">
<div class="content-wrapper">
<!-- 内容头部 -->
<section class="content-header">
<h1>
用户管理
<small>新增用户</small>
</h1>
<ol class="breadcrumb">
<li><a href="@{/backstage/index}"><i class="fa fa-dashboard"></i> 首页</a></li>
<li><a href="@{/backstage/admin/all}">用户管理</a></li>
<li class="active">新增用户</li>
</ol>
</section>
<!-- 内容头部 /-->
<!-- 正文区域 -->
<section class="content">
<form th:action="@{/backstage/admin/add}" method="post">
<div class="row data-type">
<div class="col-md-2 title">用户名</div>
<div class="col-md-4 data">
<input type="text" class="form-control" placeholder="用户名" name="username">
</div>
<div class="col-md-2 title">密码</div>
<div class="col-md-4 data">
<input type="password" class="form-control" placeholder="密码" name="password" value="">
</div>
<div class="col-md-2 title">邮箱</div>
<div class="col-md-4 data">
<input type="text" class="form-control" placeholder="邮箱" name="email">
</div>
<div class="col-md-2 title">联系电话</div>
<div class="col-md-4 data">
<input type="text" class="form-control" placeholder="联系电话" name="phoneNum" value="">
</div>
<div class="col-md-2 title">用户状态</div>
<div class="col-md-4 data">
<select class="form-control" name="status">
<option value="true">开启</option>
<option value="false">关闭</option>
</select>
</div>
<div class="col-md-2 title"></div>
<div class="col-md-10 data text-center">
<button type="submit" class="btn bg-maroon">保存</button>
<button type="button" class="btn bg-default" onclick="history.back(-1);">返回</button>
</div>
</div>
</form>
</section>
<!-- 正文区域 /-->
</div>
</div>
<!--尾部-->
<footer th:replace="/backstage/common_footer::footer"></footer>
</body>
</html>
AdminService类
AdminController类
修改管理员