实现一个登录注册,增删改查功能的系统
需求分析
分析用户主要需求 提取项目核心功能,根据核心功能构建页面原型
库表设计:
详细设计(流程图、伪代码):
验证库表准确性
功能实现(编码)
环境搭建,具体功能实现
功能测试,部署,上线,运维,维护
全栈式开发:前端+后端+运维
用户表:user
员工表:employee
表与表关系:user,employee 独立两张表
创建库表
create database ems;
use ems;
create TABLE user(
id int auto_increment ,username VARCHAR(40) COMMENT '用户名' ,
realname VARCHAR(40) COMMENT '真实姓名' ,
`password` VARCHAR(50) COMMENT '密码',gender TINYINT(3) COMMENT '性别',
PRIMARY KEY (`id`)
);
create TABLE employee(
id int auto_increment,
name VARCHAR(40) COMMENT '姓名',
birthday datetime COMMENT '生日',
salary DOUBLE COMMENT '薪资',
gender TINYINT(3) COMMENT '性别',
PRIMARY KEY(id)
)
技术选型:SpringBoot + MyBatis + JSP + MySQL
环境搭建:Spring Boot + JSP + MyBatis
创建名为ems-jsp的项目,并引入web支持依赖,创建完成
pom.xml依赖导入
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<!--jsp解析依赖-->
<dependency>
<groupId>org.apache.tomcat.embed</groupId>
<artifactId>tomcat-embed-jasper</artifactId>
</dependency>
<!--引入和MyBatis整合相关的依赖-->
<!--druid-->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.1.19</version>
</dependency>
<!--mysql-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<!--mybatis-spring-boot-stater-->
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>3.0.0</version>
</dependency>
<!--开启热部署-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
</dependency>
</dependencies>
application.yml:
# 应用服务 WEB 访问端口
server:
port: 8989
servlet:
context-path: /ems-jsp
jsp:
init-parameters:
development: true # 开启jsp模板开发模式
# 配置jsp展示
spring:
mvc:
view:
prefix: /
suffix: .jsp
datasource:
type: com.alibaba.druid.pool.DruidDataSource
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://localhost:3306/ems?characterEncoding=UTF-8
username: root
password: 123456
# 配置mybatis
mybatis:
mapper-locations: classpath:com.baizhi/mapper/*.xml
type-aliases-package: com.baizhi.entity
# 配置日志
logging:
level:
com.baizhi: debug
添加dao包扫描
@SpringBootApplication
@MapperScan("com.baizhi.dao")
public class EmsJspApplication {
public static void main(String[] args) {
SpringApplication.run(EmsJspApplication.class, args);
}
}
业务逻辑
register.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Registration Page</title>
<link rel="stylesheet" type="text/css" href="css/register.css">
</head>
<body>
<h2>用户注册</h2>
<div class="container">
<h3 style="color: red">${param.msg}</h3>
<form action="${pageContext.request.contextPath}/user/register" method="post">
用户名: <input type="text" name="username"><br>
真实姓名: <input type="text" name="realname" ><br>
密码: <input type="password" name="password" ><br>
<%-- 确认密码: <input type="password" name="confirmPassword"><br>--%>
性别:
<select name="gender" >
<option value="1">男</option>
<option value="0">女</option>
</select><br>
<!-- 验证码展示 -->
<label for="verifyCode">验证码:</label><br>
<img id="verifyCode" src="" alt="Captcha Image">
<a href="javascript:" id="refresh">换一张</a>
<input type="text" name="code"><br>
<input type="submit" value="注册">
</form>
</div>
<script src="js/jquery.js"></script>
<script>
// jQuery代码
$(document).ready(function() {
$('#registerForm').submit(function() {
return validateRegisterForm();
});
});
// 表单验证
function validateRegisterForm() {
var username = $('#username').val(),
password = $('#password').val(),
// confirmPassword = $('#confirmPassword').val(),
realname = $('#realname').val(),
code = $('#code').val();
console.log(backCode,code)
if (username === "" || password === "" || realname === "" || code === "") {
alert("请填写所有字段和验证码");
return false;
}
if (backCode.toLowerCase() === code.toLowerCase()) {
alert("验证码填写不正确")
refreshVerifyCode();// 刷新验证码
return false;
}
// if (password !== confirmPassword) {
// alert("密码不匹配");
// return false;
// }
alert("注册成功,请登录")
return true;
}
var backCode = "";
// 验证码刷新
function refreshVerifyCode() {
$.ajax({
url: '${pageContext.request.contextPath}/user/verifyCode',
method: 'GET',
dataType: 'json',
success: function(data) {
console.log(data)
backCode = data.code;
$('#verifyCode').attr('src', data.image);
},
error: function(error) {
console.error('error:', error);
}
});
}
// 初始化页面加载时显示验证码
refreshVerifyCode();
// 点击“换一张”按钮时刷新验证码
$('#refresh').click(function() {
refreshVerifyCode();
});
</script>
</body>
</html>
css
body {
font-family: 'Arial', sans-serif;
margin: auto;
justify-content: center;
align-items: center;
width: 500px;
height: 800px;
/*border: 1px solid red;*/
}
.container {
padding: 30px;
background-color: #ffffff;
border-radius: 8px;
box-shadow: 0 0 15px rgba(0, 0, 0, 0.1);
/*border: 1px solid red;*/
}
h2 {
text-align: center;
}
input[type="text"],
input[type="password"],
input[type="submit"],
select {
width: calc(100% - 20px);
margin-bottom: 15px;
padding: 10px;
border: 1px solid #dddddd;
border-radius: 5px;
transition: border-color 0.3s ease-in-out;
}
input[type="text"]:focus,
input[type="password"]:focus,
select:focus {
outline: none;
border-color: #66afe9;
}
input[type="submit"] {
background-color: #4CAF50;
color: white;
border: none;
cursor: pointer;
transition: background-color 0.3s ease-in-out;
}
input[type="submit"]:hover {
background-color: seagreen;
}
验证码 实现类
package com.baizhi.utils;
import javax.imageio.ImageIO;
import java.awt.Color;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.image.BufferedImage;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.util.Random;
public class VerifyCode {
private int width = 100;// 定义图片的width
private int height = 40;// 定义图片的height
private int codeCount = 4;// 定义图片上显示验证码的个数
private int lineCount = 20;// 定义图片上显示干扰线的条数
private String code = null;// 定义用于保存验证码的字符串
private BufferedImage buffImg = null;// 定义图片Buffer
private char[] codeSequence = { 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'P', 'Q', 'R',
'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', '1', '2', '3', '4', '5', '6', '7', '8', '9' };
public VerifyCode() {
this.createCode();
}
/**
* @param width
* 图片宽
* @param height
* 图片高
*/
public VerifyCode(int width, int height) {
this.width = width;
this.height = height;
this.createCode();
}
/**
* @param width
* 图片宽
* @param height
* 图片高
* @param codeCount
* 字符个数
* @param lineCount
* 干扰线条数
*/
public VerifyCode(int width, int height, int codeCount, int lineCount) {
this.width = width;
this.height = height;
this.codeCount = codeCount;
this.lineCount = lineCount;
this.createCode();
}
public void createCode() {
int x = 0, fontHeight = 0, codeY = 0;
int red = 0, green = 0, blue = 0;
x = width / (codeCount + 2);// 每个字符的宽度
fontHeight = height - 2;// 字体的高度
codeY = height - 4;
// 图像buffer
buffImg = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
Graphics g = buffImg.createGraphics();
// 创建一个随机数生成器类
Random random = new Random();
// 将图像填充为白色
g.setColor(Color.WHITE);
g.fillRect(0, 0, width, height);
// 创建字体,字体的大小应该根据图片的高度来定。
Font font = new Font("Fixedsys", Font.PLAIN, fontHeight);
// 设置字体。
g.setFont(font);
for (int i = 0; i < lineCount; i++) {
// 设置随机开始和结束坐标
int xs = random.nextInt(width);// x坐标开始
int ys = random.nextInt(height);// y坐标开始
int xe = xs + random.nextInt(width / 8);// x坐标结束
int ye = ys + random.nextInt(height / 8);// y坐标结束
// 生成随机颜色
red = random.nextInt(255);
green = random.nextInt(255);
blue = random.nextInt(255);
g.setColor(new Color(red, green, blue));
g.drawLine(xs, ys, xe, ye);
}
// randomCode记录随机产生的验证码
StringBuffer randomCode = new StringBuffer();
// 随机产生codeCount个字符的验证码。
for (int i = 0; i < codeCount; i++) {
// 得到随机产生的验证码数字。
String strRand = String.valueOf(codeSequence[random.nextInt(codeSequence.length)]);
// 用随机产生的颜色将验证码绘制到图像中。
red = random.nextInt(255);
green = random.nextInt(255);
blue = random.nextInt(255);
g.setColor(new Color(red, green, blue));
g.drawString(strRand, (i + 1) * x, codeY);
// 将产生的四个随机数组合在一起。
randomCode.append(strRand);
}
// 将四位数字的验证码保存到Session中。
code = randomCode.toString();
}
public void write(String path) throws IOException {
OutputStream sos = new FileOutputStream(path);
this.write(sos);
}
public void write(OutputStream sos) throws IOException {
ImageIO.write(buffImg, "png", sos);
sos.close();
}
public BufferedImage getBuffImg() {
return buffImg;
}
public String getCode() {
return code;
}
}
验证码生成 请求
@Controller
@RequestMapping("user")
public class UserController {
/**
* 生成验证码
*/
@ResponseBody
@RequestMapping("verifyCode")
public Map<String, String> verifyCode(HttpServletRequest request) throws IOException {
Map<String,String> map = new HashMap<>();
// 1.使用工具类生成验证码
VerifyCode vc = new VerifyCode(120, 40, 4, 100);
String code = vc.getCode();
map.put("code", code);
// 2. 获取验证码的BufferedImage对象
BufferedImage captchaImage = vc.getBuffImg();
//4.将图片转为base64 [放入src,可以直接显示图片]
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
ImageIO.write(captchaImage, "png", outputStream);
byte[] imageBytes = outputStream.toByteArray();
String image = "data:image/png;base64," + Base64Utils.encodeToString(imageBytes);
map.put("image", image);
return map;
}
}
业务逻辑
Service
@Service
@Transactional
public class UserServiceImpl implements UserService {
@Autowired
private UserDao userDao;
@Override
public void register(User user) {
User userDB = userDao.findByUserName(user.getUsername());
if (!ObjectUtils.isEmpty(userDB)) {
throw new RuntimeException("用户名已存在");
}
// 注册之前给密码进行加密
String passwordSecret = DigestUtils.md5DigestAsHex(user.getPassword().getBytes(StandardCharsets.UTF_8));
user.setPassword(passwordSecret);
userDao.save(user);
}
}
mapper语句
<select id="findByUserName" resultType="com.baizhi.entity.User">
select id,username,realname,password,gender from `user`
where username = #{username}
</select>
api
@Autowired
private UserService userService;
@RequestMapping("register")
public String register(User user, String code, HttpSession session) {
log.debug("接受的验证码:{}", code);
log.debug("User:{}", user);
// 比较验证
try {
String sessionCode = session.getAttribute("code").toString();
if (!sessionCode.equalsIgnoreCase(code)) {
throw new RuntimeException("验证码输入错误!!!");
}
userService.register(user);
} catch (RuntimeException e) {
e.printStackTrace();
return "redirect:/register.jsp?msg=" + UriEncoder.encode(e.getMessage());
}
return "redirect:/login.jsp";
}
login.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Login Page</title>
<link rel="stylesheet" href="css/register.css">
</head>
<body>
<h2>用户登录</h2>
<h3 style="color: red">${param.msg}</h3>
<form action="${pageContext.request.contextPath}/employee/list" method="post">
<label for="username">用户名:</label><br>
<input type="text" id="username" name="username"><br>
<label for="password">密码:</label><br>
<input type="password" id="password" name="password"><br>
<input type="submit" value="登录">
<input type="button" onclick="window.location.href='register.jsp'" value="注册">
</form>
</body>
</html>
ServiceImpl
@Override
public User login(String username, String password) {
//1. 根据用户名查询数据库是否存在
User user = userDao.findByUserName(username);
//2.判断对象是否存在
if (ObjectUtils.isEmpty(user)) {
throw new RuntimeException("用户名输入错误!");
}
//3.判断密码正确性
String digestPassword = DigestUtils.md5DigestAsHex(password.getBytes(StandardCharsets.UTF_8));
if (!user.getPassword().equals(digestPassword)) {
throw new RuntimeException("密码错误!");
}
return user;
}
UserController
@RequestMapping("login")
public String login(String username, String password,HttpSession session) throws UnsupportedEncodingException {
log.debug("接受到的用户名:{},接收到的密码:{}", username, password);
try {
// 1.执行登录业务逻辑
User user = userService.login(username, password);
// 2.登录成功,保存用户信息
session.setAttribute("user", user);
} catch (Exception e) {
e.printStackTrace();
return "redirect:/login.jsp?msg=" + UriEncoder.encode(e.getMessage());
}
return "redirect:/emplist.jsp";
}
emplist.jsp
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt" %>
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8" %>
<!DOCTYPE html>
<html>
<head>
<title>用户列表</title>
<link rel="stylesheet" href="${pageContext.request.contextPath}/css/emplist.css">
</head>
<body>
<div id="container">
<h2>用户列表</h2>
<table>
<tr>
<th>ID</th>
<th>姓名</th>
<th>性别</th>
<th>薪水</th>
<th>生日</th>
<th>操作</th>
</tr>
<c:forEach var="employee" items="${requestScope.employees}">
<tr>
<td>${employee.id}</td>
<td>${employee.name}</td>
<td>${employee.gender?'男':'女'}</td>
<td>${employee.salary}</td>
<td><fmt:formatDate value="${employee.birthday}" pattern="yyyy-MM-dd"/></td>
<td>
<a href="javascript:;">删除</a>
<a href="javascript:;">修改</a>
</td>
</tr>
</c:forEach>
</table>
<a href="javascript:;">添加员工信息</a>
</div>
</body>
</html>
emplist.css
body {
font-family: Arial, sans-serif;
margin: 0;
padding: 0;
}
#container {
max-width: 800px;
margin: 0 auto;
padding: 20px;
}
h2 {
text-align: center;
}
table {
width: 100%;
border-collapse: collapse;
margin-bottom: 20px;
}
th, td {
padding: 10px;
text-align: left;
border: 1px solid #ccc;
}
thead {
background-color: #f2f2f2;
}
div > button {
margin: 5px;
padding: 5px 10px;
border: none;
background-color: #007bff;
color: #fff;
cursor: pointer;
}
div > button:hover {
background-color: #0056b3;
}
select, button {
padding: 5px;
}
div > span {
margin: 0 10px;
font-weight: bold;
}
label {
font-weight: bold;
}
Service
@Service
@Transactional
public class EmployeeServiceImpl implements EmployeeService {
private final EmployeeDao employeeDao;
@Autowired
public EmployeeServiceImpl(EmployeeDao employeeDao) {
this.employeeDao = employeeDao;
}
@Override
public List<Employee> list() {
return employeeDao.list();
}
}
EmployeeController
@Controller
@RequestMapping("employee")
public class EmployeeController {
private EmployeeService employeeService;
@Autowired
public EmployeeController(EmployeeService employeeService) {
this.employeeService = employeeService;
}
/**
* 员工列表
*
* @return
*/
@RequestMapping("list")
public String listEmployee(HttpServletRequest request, Model model) {
//1. 获取员工列表
List<Employee> employees = employeeService.list();
// request.setAttribute("employees", employees);
model.addAttribute("employees", employees);
return "emplist";
}
}
addEmp.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8" %>
<!DOCTYPE html>
<html>
<head>
<title>添加员工</title>
<link rel="stylesheet" href="css/addEmp.css">
</head>
<body>
<h2>添加员工</h2>
<div id="container">
<form action="${pageContext.request.contextPath}/employee/add" method="post">
<table>
<tr>
<td>姓名:</td>
<td><input type="text" id="name" name="name"></td>
</tr>
<tr>
<td>薪水:</td>
<td><input type="text" id="salary" name="salary"></td>
</tr>
<tr>
<td>生日:</td>
<td><input type="text" id="birthday" name="birthday"></td>
</tr>
<tr>
<td>性别:</td>
<td>
<select name="gender" >
<option value="1">男</option>
<option value="0">女</option>
</select>
</td>
</tr>
<tr>
<td colspan="2"><input type="submit" value="添加"></td>
</tr>
</table>
</form>
</div>
</body>
</html>
addEmp.css
body {
font-family: Arial, sans-serif;
margin: 0;
padding: 0;
}
#container {
max-width: 800px;
margin: 0 auto;
padding: 20px;
}
h2 {
text-align: center;
}
table {
width: 100%;
border-collapse: collapse;
margin-bottom: 20px;
color: #212529;
}
td, th {
vertical-align: top;
padding: 10px;
text-align: left;
border: 1px solid #ccc;
}
input[type="text"], input[type="date"], select {
width: 60%;
padding: .375rem .75rem;
border: 1px solid #ced4da;
border-radius: .25rem;
}
input[type="submit"] {
color: #fff;
background-color: #007bff;
border-color: #007bff;
padding: .375rem .75rem;
border-radius: .25rem;
}
EmployeeDaomapper.xml
<insert id="add" parameterType="Employee" useGeneratedKeys="true" keyProperty="id">
INSERT INTO `ems`.`employee`(`id`, `name`, `birthday`, `salary`, `gender`) VALUES (#{id},#{name},#{birthday},#{salary},#{gender});
</insert>
EmployeeServiceImpl
public void addEmployee(Employee employee) {
employeeDao.add(employee);
}
Controller
@RequestMapping("add")
public String addEmployee(Employee employee) {
log.debug("员工信息:{}", employee);
//1. 保存员工信息
employeeService.addEmployee(employee);
return "redirect:/employee/list";
}
显示员工信息
UpdateEmp.jsp
<%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt" %>
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8" %>
<!DOCTYPE html>
<html>
<head>
<title>修改员工信息</title>
<link rel="stylesheet" href="${pageContext.request.contextPath}/css/updateEmp.css">
</head>
<body>
<div id="container">
<h2>修改员工信息</h2>
<form action="${pageContext.request.contextPath}/employee/update" method="post">
<input type="text" id="id" name="id" value="${employee.id}" style="display: none">
<label for="name">姓名:</label><br>
<input type="text" id="name" name="name" value="${employee.name}"><br>
<label for="gender">性别:</label><br>
<select id="gender" name="gender">
<option value="1" ${employee.gender?'selected':''}>男</option>
<option value="0" ${!employee.gender?'selected':''}>女</option>
</select><br>
<label for="salary">薪水:</label><br>
<input type="text" id="salary" name="salary" value="${employee.salary}"><br>
<label for="birthday">生日:</label><br>
<input type="text" id="birthday" name="birthday" value="<fmt:formatDate value='${employee.birthday}' pattern='yyyy/MM/dd'/>"/><br>
<input type="submit" value="提交">
</form>
</div>
</body>
</html>
UpdateEmp.css
body {
font-family: Arial, sans-serif;
}
#container {
width: 300px;
margin: 0 auto;
padding: 20px;
border: 1px solid #ccc;
border-radius: 5px;
background-color: #f8f8f8;
}
h2 {
text-align: center;
color: #333;
}
label {
font-weight: bold;
color: #555;
}
input[type="text"], input[type="date"],select {
width: 70%;
padding: 10px;
margin: 5px 0 15px;
border: 1px solid #ccc;
border-radius: 3px;
}
input[type="submit"] {
width: 100%;
padding: 10px;
color: white;
background-color: #007BFF;
border: none;
border-radius: 3px;
cursor: pointer;
}
input[type="submit"]:hover {
background-color: #0056b3;
}
Controller
@RequestMapping("detail")
public String detailEmployee(Integer id, Model model) {
log.debug("接收的id:{}",id);
Employee employee = employeeService.idByEmployee(id);
model.addAttribute("employee", employee);
return "updateEmp";
}
更改员工信息
Controller
@RequestMapping("update")
public String updateEmployee(Employee employee) {
log.debug("修改的员工信息:{}", employee);
employeeService.updateEmployee(employee);
return "redirect:/employee/list";
}
emplist.jsp
<a href="javascript:;" onclick="deleteEmployee()">删除</a>
<script>
function deleteEmployee(){
if (window.confirm('确定删除这条记录吗')) {
location.href= '${pageContext.request.contextPath}/employee/delete?id=${employee.id}'
}
}
</script>
Controller
@RequestMapping("delete")
public String deleteEmployee(Integer id) {
log.debug("接收的id:{}", id);
employeeService.deleteEmployee(id);
return "redirect:/employee/list";
}
mapper
<delete id="deleteEmployee">
delete from `employee`where id =#{id}
</delete>