冲刺博客7
推翻重做
昨天的成就(项目启动日)
昨天与团队成员共同完成了设备运维管理系统的需求讨论和技术选型。确定使用 Java Servlet + JSP + MySQL 作为技术栈,分工方面我负责后端核心模块:数据库层、认证系统、工单管理、以及飞书机器人集成。昨天完成了以下准备工作:
项目目录结构搭建( src/main/java/com/yunwei/ 下建立 util 、 servlet 、 model 、 service 四个包)
Tomcat 运行环境调试,本地 MySQL 服务确认可用
数据库表结构初步设计(user 表、engineer 表、repair_order 表等)
遇到的困难
困难一:MySQL 驱动与连接池的选择 。最初考虑使用 Druid 连接池,但团队成员建议保持代码简洁,先从原生 JDBC 入手,后续再引入连接池优化。权衡后决定先写原生 DBUtil,保证功能可用优先。
困难二:密码存储方式的讨论 。最初想直接存储明文密码以方便调试,但意识到安全隐患后,还是决定在用户表中设计 password 字段,后续迭代再引入 BCrypt 加密。目前先以明文方式实现登录逻辑,后续统一改造。
困难三:Servlet 版本与 jakarta.servlet 包名问题 。由于使用的是较新的 Tomcat,Servlet API 包名从 javax.servlet 变为 jakarta.servlet ,需要统一导入路径。
今天的任务
- 实现 DBUtil 数据库工具类 ,封装 JDBC 连接与资源关闭
- 实现 LoginServlet 用户登录接口 ,支持管理员 / 工程师 / 普通用户三种角色
- 设计并初始化核心数据表
代码嵌入
DBUtil.java — 数据库连接工具类
package com.yunwei.util;
import java.sql.*;
public class DBUtil {
private static final String URL = "jdbc:mysql://localhost:3306/yunwei_db?useUnicode=true&characterEncoding=utf8&useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true";
private static final String USERNAME = "root";
private static final String PASSWORD = "xht20060223.";
static {
try {
Class.forName("com.mysql.cj.jdbc.Driver");
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
public static Connection getConnection() throws SQLException {
return DriverManager.getConnection(URL, USERNAME, PASSWORD);
}
public static void close(Connection conn, Statement stmt, ResultSet rs) {
try {
if (rs != null) rs.close();
if (stmt != null) stmt.close();
if (conn != null) conn.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
public static void close(Connection conn, Statement stmt) {
close(conn, stmt, null);
}
}
LoginServlet.java — 登录认证核心逻辑
@WebServlet("/api/login")
public class LoginServlet extends
HttpServlet {
@Override
protected void doPost
(HttpServletRequest req,
HttpServletResponse resp)
throws ServletException,
IOException {
resp.setContentType
("application/json;
charset=UTF-8");
req.setCharacterEncoding
("UTF-8");
String username = req.
getParameter("username");
String password = req.
getParameter("password");
if (username == null ||
password == null ||
username.isEmpty() ||
password.isEmpty()) {
resp.getWriter().write("
{"success":false,
"message":"用户名和密
码不能为空"}");
return;
}
String sql = "SELECT u.id,
u.username, u.name, u.role,
u.phone, e.id as
engineer_id, e.status " +
"FROM user u
LEFT JOIN
engineer e ON
u.id = e.
user_id WHERE
u.username = ?
AND u.password
= ?";
try (Connection conn =
DBUtil.getConnection();
PreparedStatement ps =
conn.prepareStatement
(sql)) {
ps.setString(1,
username);
ps.setString(2,
password);
ResultSet rs = ps.
executeQuery();
if (rs.next()) {
HttpSession session
= req.getSession
(true);
session.setAttribute
("userId", rs.getInt
("id"));
session.setAttribute
("username", rs.
getString
("username"));
session.setAttribute
("name", rs.
getString("name"));
session.setAttribute
("role", rs.
getString("role"));
String role = rs.
getString("role");
String redirectUrl;
if ("admin".equals
(role)) {
redirectUrl =
req.
getContextPath
() + "/";
} else if
("engineer".equals
(role)) {
redirectUrl =
req.
getContextPath
() + "/engineer/
home";
} else {
redirectUrl =
req.
getContextPath
() + "/user/
home";
}
resp.getWriter().
write("
{"success":true,
"message":"登录成
功",
"redirect":"" +
redirectUrl + ""}
");
} else {
resp.getWriter().
write("
{"success":false,
"message":"用户名
或密码错误"}");
}
} catch (SQLException e) {
e.printStackTrace();
resp.getWriter().write("
{"success":false,
"message":"登录失败:
" + e.getMessage() +
""}");
}
}
}
核心设计思路: 通过 LEFT JOIN engineer 表,一次性将用户基础信息与工程师扩展信息取出放入 Session,避免后续请求再次查询数据库。角色字段( role )决定登录后的跳转页面,实现前端页面的权限分流。

浙公网安备 33010602011771号