写点什么

Java Web 学生自习管理系统

  • 2024-12-31
    福建
  • 本文字数:11634 字

    阅读完需:约 38 分钟

一、项目背景与需求分析


随着网络技术的不断发展和学校规模的扩大,学生自习管理系统的需求日益增加。传统的自习管理方式存在效率低下、资源浪费等问题,因此,开发一个智能化的学生自习管理系统显得尤为重要。该系统旨在提高自习室的利用率和管理效率,为学生提供方便快捷的自习预约服务,同时为管理员提供高效的资源管理工具。

系统的主要功能需求包括:


1.用户管理:管理员和学生的注册、登录。

2.自习室管理:自习室类型、座位信息的录入和查询。

3.座位预约:学生预约自习室座位、查看预约状态和历史记录。

4.管理员操作:管理员查看预约情况、管理资源分配。


二、技术选型与架构设计


1.技术选型:

  • 前端:HTML、CSS、JavaScript,用于创建用户界面。

  • 后端:Java(JDK 1.8),Servlet,JSP,JDBC,用于处理业务逻辑和数据库交互。

  • 数据库:MySQL,用于存储用户信息和自习室资源数据。

  • 服务器:Apache Tomcat,用于部署和运行 Web 应用。

  • 开发工具:IntelliJ IDEA 或 Eclipse,用于编写和调试代码。


2.架构设计:

三层架构:

  • 表示层:JSP/HTML 作为前台与用户交互,Servlet 用于控制跳转和调用业务逻辑层。

  • 业务逻辑层:处理业务逻辑,调用数据访问层。

  • 数据访问层:与数据库交互,封装数据库操作。


三、数据库设计


1.用户表(users):存储用户信息。


CREATE TABLE users (    id INT AUTO_INCREMENT PRIMARY KEY,    username VARCHAR(50) NOT NULL UNIQUE,    password VARCHAR(50) NOT NULL,    role VARCHAR(10) NOT NULL CHECK (role IN ('student', 'admin')));
复制代码


2.自习室表(study_rooms):存储自习室信息。


CREATE TABLE study_rooms (    id INT AUTO_INCREMENT PRIMARY KEY,    room_name VARCHAR(50) NOT NULL,    capacity INT NOT NULL);
复制代码


3.座位表(seats):存储座位信息。


CREATE TABLE seats (    id INT AUTO_INCREMENT PRIMARY KEY,    room_id INT NOT NULL,    status VARCHAR(10) NOT NULL CHECK (status IN ('available', 'booked')),    FOREIGN KEY (room_id) REFERENCES study_rooms(id));
复制代码


4.预约表(reservations):存储预约信息。


CREATE TABLE reservations (    id INT AUTO_INCREMENT PRIMARY KEY,    user_id INT NOT NULL,    seat_id INT NOT NULL,    start_time DATETIME NOT NULL,    end_time DATETIME NOT NULL,    FOREIGN KEY (user_id) REFERENCES users(id),    FOREIGN KEY (seat_id) REFERENCES seats(id));
复制代码


四、后端实现


1.数据库连接工具类(DBUtil.java):


import java.sql.Connection;import java.sql.DriverManager;import java.sql.SQLException; public class DBUtil {    private static final String DB_URL = "jdbc:mysql://localhost:3306/study_management";    private static final String DB_USER = "root";    private static final String DB_PASSWORD = "your_password";    private static Connection connection = null;     static {        try {            Class.forName("com.mysql.cj.jdbc.Driver");            connection = DriverManager.getConnection(DB_URL, DB_USER, DB_PASSWORD);        } catch (ClassNotFoundException | SQLException e) {            e.printStackTrace();        }    }     public static Connection getConnection() {        return connection;    }     // 其他数据库操作方法(增删改查)}
复制代码


2.数据访问层(DAO):


  • UserDao.java

import java.sql.*;import java.util.ArrayList;import java.util.List; public class UserDao {    public boolean registerUser(String username, String password, String role) {        String sql = "INSERT INTO users (username, password, role) VALUES (?, ?, ?)";        try (PreparedStatement pst = DBUtil.getConnection().prepareStatement(sql)) {            pst.setString(1, username);            pst.setString(2, password);            pst.setString(3, role);            return pst.executeUpdate() > 0;        } catch (SQLException e) {            e.printStackTrace();            return false;        }    }     // 其他方法(登录、查询用户等)}
复制代码


  • StudyRoomDao.java 和 SeatDao.java 以及 ReservationDao.java 的实现类似,包含各自的增删改查方法。


3.业务逻辑层(Service):


  • UserService.java

public class UserService {    public boolean register(String username, String password, String role) {        return UserDao.registerUser(username, password, role);    }     // 其他方法(登录验证、查询用户信息等)}
复制代码


  • StudyRoomService.javaSeatService.java 和 ReservationService.java 类似,包含各自的业务逻辑处理。


4. Servlet:


  • RegisterServlet.java:


import javax.servlet.*;import javax.servlet.http.*;import java.io.IOException; public class RegisterServlet extends HttpServlet {    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {        String username = request.getParameter("username");        String password = request.getParameter("password");        String role = request.getParameter("role");         boolean isRegistered = UserService.register(username, password, role);         if (isRegistered) {            response.sendRedirect("login.jsp");        } else {            request.setAttribute("error", "Registration failed!");            request.getRequestDispatcher("register.jsp").forward(request, response);        }    }     // 其他方法(登录Servlet、预约Servlet等)}
复制代码


5. JDBC 工具类


import java.sql.Connection;import java.sql.DriverManager;import java.sql.SQLException; public class JDBCUtils {    private static final String URL = "jdbc:mysql://localhost:3306/StudyManagementSystem?useSSL=false&serverTimezone=UTC";    private static final String USER = "root";    private static final String PASSWORD = "your_password"; // 请替换为您的数据库密码     public static Connection getConnection() throws SQLException {        return DriverManager.getConnection(URL, USER, PASSWORD);    }}
复制代码


6. Servlet 示例:添加学生


import javax.servlet.ServletException;import javax.servlet.annotation.WebServlet;import javax.servlet.http.HttpServlet;import javax.servlet.http.HttpServletRequest;import javax.servlet.http.HttpServletResponse;import java.io.IOException;import java.sql.Connection;import java.sql.PreparedStatement;import java.sql.SQLException; @WebServlet("/addStudent")public class AddStudentServlet extends HttpServlet {    @Override    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {        String name = request.getParameter("name");        String major = request.getParameter("major");        int grade = Integer.parseInt(request.getParameter("grade"));        String phone = request.getParameter("phone");         String sql = "INSERT INTO Student (name, major, grade, phone) VALUES (?, ?, ?, ?)";         try (Connection conn = JDBCUtils.getConnection();             PreparedStatement pstmt = conn.prepareStatement(sql)) {            pstmt.setString(1, name);            pstmt.setString(2, major);            pstmt.setInt(3, grade);            pstmt.setString(4, phone);            pstmt.executeUpdate();             response.sendRedirect("students.jsp"); // 重定向到学生列表页面        } catch (SQLException e) {            e.printStackTrace();            request.setAttribute("error", "添加学生失败,请稍后再试!");            request.getRequestDispatcher("addStudent.jsp").forward(request, response);        }    }}
复制代码


7. JSP 页面示例:添加学生页面


<!-- addStudent.jsp --><!DOCTYPE html><html><head>    <title>添加学生</title></head><body>    <h2>添加学生</h2>    <form action="addStudent" method="post">        姓名: <input type="text" name="name" required><br>        专业: <input type="text" name="major" required><br>        年级: <input type="number" name="grade" required><br>        电话: <input type="text" name="phone" required><br>        <input type="submit" value="提交">    </form>    <c:if test="${not empty error}">        <p style="color:red;">${error}</p>    </c:if></body></html>
复制代码


注意:为了使用 JSP 标签库(如<c:if>),您需要在 JSP 页面顶部添加以下指令:


jsp复制代码
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
复制代码


并且,您需要在项目的WEB-INF/lib目录下添加 JSTL 库(如jstl-1.2.jar)。


五、前端实现


1.注册页面(register.jsp):


<%@ page contentType="text/html;charset=UTF-8" language="java" %><html><head>    <title>Register</title></head><body>    <h1>Register</h1>    <form action="RegisterServlet" method="post">        Username: <input type="text" name="username" required><br>        Password: <input type="password" name="password" required><br>        Role: <select name="role">                <option value="student">Student</option>                <option value="admin">Admin</option>            </select><br>        <button type="submit">Register</button>    </form>    <c:if test="${not empty error}">        <p style="color:red">${error}</p>    </c:if></body></html>
复制代码


2.登录页面(login.jsp)


登录页面(login.jsp)** 和 其他页面(如自习室管理页面、座位预约页面等)类似,通过表单提交数据到相应的 Servlet 进行处理。


(1)项目结构


假设项目结构如下:


MyWebApp/├── src/│   ├── main/│   │   ├── java/│   │   │   └── com/│   │   │       └── example/│   │   │           ├── controller/│   │   │           │   ├── LoginServlet.java│   │   │           │   ├── StudyRoomServlet.java│   │   │           │   └── SeatReservationServlet.java│   │   │           └── model/│   │   │               └── User.java│   │   └── webapp/│   │       ├── WEB-INF/│   │       │   ├── web.xml│   │       └── │   │           ├── login.jsp│   │           ├── studyRoom.jsp│   │           └── seatReservation.jsp
复制代码


(2) User 模型类


首先,定义一个简单的User类来表示用户信息。


// src/main/java/com/example/model/User.javapackage com.example.model; public class User {    private String username;    private String password;     // Getters and Setters    public String getUsername() {        return username;    }     public void setUsername(String username) {        this.username = username;    }     public String getPassword() {        return password;    }     public void setPassword(String password) {        this.password = password;    }}
复制代码


(3)登录页面 (login.jsp)


创建一个简单的登录页面。


<!-- src/main/webapp/login.jsp --><!DOCTYPE html><html><head>    <title>Login</title></head><body>    <h2>Login</h2>    <form action="login" method="post">        <label for="username">Username:</label>        <input type="text" id="username" name="username" required><br><br>        <label for="password">Password:</label>        <input type="password" id="password" name="password" required><br><br>        <input type="submit" value="Login">    </form>    <p>Or go to <a href="studyRoom.jsp">Study Room Management</a> or <a href="seatReservation.jsp">Seat Reservation</a> (without login).</p></body></html>
复制代码


(4)登录处理 Servlet (LoginServlet.java)


处理登录表单提交的 Servlet。


// src/main/java/com/example/controller/LoginServlet.javapackage com.example.controller; import com.example.model.User; import javax.servlet.ServletException;import javax.servlet.annotation.WebServlet;import javax.servlet.http.HttpServlet;import javax.servlet.http.HttpServletRequest;import javax.servlet.http.HttpServletResponse;import java.io.IOException; @WebServlet("/login")public class LoginServlet extends HttpServlet {    @Override    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {        String username = request.getParameter("username");        String password = request.getParameter("password");         // Simple hard-coded authentication for demo purposes        if ("admin".equals(username) && "password123".equals(password)) {            User user = new User();            user.setUsername(username);            user.setPassword(password);            request.getSession().setAttribute("user", user);            response.sendRedirect("studyRoom.jsp");        } else {            response.sendRedirect("login.jsp?error=true");        }    }}
复制代码


(5)自习室管理页面 (studyRoom.jsp)


显示自习室管理页面(假设用户已登录)。


<!-- src/main/webapp/studyRoom.jsp --><!DOCTYPE html><html><head>    <title>Study Room Management</title></head><body>    <h2>Study Room Management</h2>    <%        if (session.getAttribute("user") == null) {            response.sendRedirect("login.jsp");            return;        }    %>    <p>Welcome, <%= session.getAttribute("user").getUsername() %>!</p>    <p>This is where you can manage study rooms.</p>    <a href="logout">Logout</a></body></html>
复制代码


(6)座位预约页面 (seatReservation.jsp)


显示座位预约页面(假设用户未登录也可访问)。


<!-- src/main/webapp/seatReservation.jsp --><!DOCTYPE html><html><head>    <title>Seat Reservation</title></head><body>    <h2>Seat Reservation</h2>    <p>You can reserve a seat here.</p>    <%        if (session.getAttribute("user") != null) {            out.println("<p>Logged in as: " + session.getAttribute("user").getUsername() + "</p>");        } else {            out.println("<p>You are not logged in. <a href='login.jsp'>Login</a> to see more options.</p>");        }    %>    <a href="login.jsp">Login</a></body></html>
复制代码


(7)注销处理 Servlet (LogoutServlet.java)


处理用户注销的 Servlet(未包含在代码中,但可以通过添加一个新的 Servlet 实现)。


(8)web.xml 配置文件


配置 Servlet 映射(虽然使用了注解,但也可以在这里配置)。


<!-- src/main/webapp/WEB-INF/web.xml --><web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"         xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee         http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd"         version="3.1">     <servlet>        <servlet-name>LoginServlet</servlet-name>        <servlet-class>com.example.controller.LoginServlet</servlet-class>    </servlet>    <servlet-mapping>        <servlet-name>LoginServlet</servlet-name>        <url-pattern>/login</url-pattern>    </servlet-mapping>     <!-- Add other servlets and mappings if needed --> </web-app>
复制代码


以上代码示例展示了如何创建一个简单的 Java Web 应用程序,包括登录页面、自习室管理页面和座位预约页面,并通过 Servlet 处理表单提交。


前端使用 JSP 页面进行展示。以下是一个简单的学生列表页面示例。


(9)学生列表页面


<!-- students.jsp --><%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %><!DOCTYPE html><html><head>    <title>学生列表</title></head><body>    <h2>学生列表</h2>    <table border="1">        <tr>            <th>姓名</th>            <th>专业</th>            <th>年级</th>            <th>电话</th>        </tr>        <c:forEach var="student" items="${students}">            <tr>                <td>${student.name}</td>                <td>${student.major}</td>                <td>${student.grade}</td>                <td>${student.phone}</td>            </tr>        </c:forEach>    </table>    <a href="addStudent.jsp">添加学生</a></body></html>
复制代码


注意:为了获取学生列表,您需要一个 Servlet 来处理这个请求,并从数据库中检索学生数据。这可以通过创建一个 Servlet,使用 JDBC 查询数据库,然后将结果集存储到请求属性中,并转发到 JSP 页面来完成。


六、系统测试


1. 单元测试:使用 JUnit 对各个 DAO 和 Service 类进行单元测试


DAO 层单元测试


假设我们有一个UserDao类,它包含一些基本的数据访问方法。


// UserDao.javaimport org.springframework.data.jpa.repository.JpaRepository;import org.springframework.stereotype.Repository; @Repositorypublic interface UserDao extends JpaRepository<User, Long> {    User findByUsername(String username);}
复制代码


单元测试类UserDaoTest


// UserDaoTest.javaimport static org.junit.jupiter.api.Assertions.*;import org.junit.jupiter.api.BeforeEach;import org.junit.jupiter.api.Test;import org.junit.jupiter.api.extension.ExtendWith;import org.mockito.InjectMocks;import org.mockito.Mock;import org.mockito.junit.jupiter.MockitoExtension; import java.util.Optional; @ExtendWith(MockitoExtension.class)public class UserDaoTest {     @Mock    private UserDao userDao;     private User user;     @BeforeEach    public void setUp() {        user = new User();        user.setId(1L);        user.setUsername("testuser");    }     @Test    public void testFindByUsername() {        when(userDao.findByUsername("testuser")).thenReturn(user);         User foundUser = userDao.findByUsername("testuser");         assertNotNull(foundUser);        assertEquals("testuser", foundUser.getUsername());    }}
复制代码


Service 层单元测试


假设我们有一个UserService类,它依赖于UserDao


// UserService.javaimport org.springframework.beans.factory.annotation.Autowired;import org.springframework.stereotype.Service; @Servicepublic class UserService {     @Autowired    private UserDao userDao;     public User getUserByUsername(String username) {        return userDao.findByUsername(username);    }}
复制代码


单元测试类UserServiceTest


// UserServiceTest.javaimport static org.junit.jupiter.api.Assertions.*;import org.junit.jupiter.api.BeforeEach;import org.junit.jupiter.api.Test;import org.junit.jupiter.api.extension.ExtendWith;import org.mockito.InjectMocks;import org.mockito.Mock;import org.mockito.junit.jupiter.MockitoExtension; @ExtendWith(MockitoExtension.class)public class UserServiceTest {     @Mock    private UserDao userDao;     @InjectMocks    private UserService userService;     private User user;     @BeforeEach    public void setUp() {        user = new User();        user.setId(1L);        user.setUsername("testuser");    }     @Test    public void testGetUserByUsername() {        when(userDao.findByUsername("testuser")).thenReturn(user);         User foundUser = userService.getUserByUsername("testuser");         assertNotNull(foundUser);        assertEquals("testuser", foundUser.getUsername());    }}
复制代码


2. 集成测试:通过模拟用户操作,测试系统的整体功能


集成测试通常使用 Spring Boot 的测试框架来模拟 HTTP 请求。


// UserControllerTest.javaimport static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; import org.junit.jupiter.api.Test;import org.junit.jupiter.api.extension.ExtendWith;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;import org.springframework.boot.test.mock.mockito.MockBean;import org.springframework.test.context.junit.jupiter.SpringExtension;import org.springframework.test.web.servlet.MockMvc; @ExtendWith(SpringExtension.class)@WebMvcTest(UserController.class)public class UserControllerTest {     @Autowired    private MockMvc mockMvc;     @MockBean    private UserService userService;     @Test    public void testGetUserByUsername() throws Exception {        User user = new User();        user.setId(1L);        user.setUsername("testuser");         when(userService.getUserByUsername("testuser")).thenReturn(user);         mockMvc.perform(get("/users/testuser"))                .andExpect(status().isOk())                .andExpect(content().json("{\"id\":1,\"username\":\"testuser\"}"));    }}
复制代码


3. 性能测试:使用工具(如 JMeter)对系统进行性能测试


性能测试通常不是通过代码来完成的,而是使用性能测试工具如 JMeter、Gatling 等。下面是一个简单的 JMeter 测试计划配置示例:


配置 JMeter 测试计划


(1)创建测试计划:

  • 打开 JMeter,创建一个新的测试计划。


(2)添加线程组:

  • 右键点击测试计划,选择“添加” -> “Threads (Users)” -> “Thread Group”。

  • 设置线程数、启动时间、循环次数等参数。


(3)添加 HTTP 请求:

  • 右键点击线程组,选择“添加” -> “Sampler” -> “HTTP Request”。

  • 设置服务器名称或 IP、端口号、协议、路径等信息。


(4)添加监听器:

  • 右键点击线程组,选择“添加” -> “Listener” -> “View Results Tree”。

  • 右键点击线程组,选择“添加” -> “Listener” -> “Summary Report”。


(5)运行测试:

  • 点击工具栏上的绿色三角形按钮,开始运行测试。


(6)分析结果:

  • 在监听器中查看结果树和摘要报告,评估系统的响应时间和吞吐量。


通过以上步骤,你可以对系统进行详细的单元测试、集成测试和性能测试,确保每个方法的功能正常、各模块之间的协作正常以及系统的性能符合预期。


七、性能优化


1. 数据库优化


数据库优化是提高系统性能的关键环节之一,涉及多个方面:


  • 建立合适的索引:根据查询频率和查询条件,在表的适当字段上创建索引(如主键索引、唯一索引、普通索引、全文索引等)。但过多的索引也会增加写操作的负担,因此需要权衡。

  • 优化 SQL 查询语句:避免使用 SELECT *,只选择需要的字段;利用 EXPLAIN 语句分析查询计划,确保查询使用了索引;避免在 WHERE 子句中使用函数或进行类型转换,因为这会使索引失效;对于复杂的查询,考虑使用子查询、联合查询或存储过程来优化。

  • 数据库配置调整:根据服务器的硬件配置和业务需求,调整数据库的内存分配(如缓存大小)、并发连接数、事务处理策略等配置参数,以达到最佳性能。

  • 分区和分表:对于数据量巨大的表,可以通过水平或垂直分区来减小单表的大小,提高查询效率;对于高并发的写操作,可以考虑使用分表策略来分散压力。


2. 代码优化


代码层面的优化同样重要,直接影响应用的执行效率和资源占用:


  • 算法和数据结构:选择高效的算法和合适的数据结构,比如使用哈希表替代链表进行快速查找,使用堆排序处理大数据集等。

  • 减少不必要的计算:避免重复计算,可以通过缓存中间结果或提前计算并存储的方式来减少计算量。

  • 资源管理:及时关闭不再使用的数据库连接、文件句柄等资源,避免资源泄露。

  • 异步处理:对于耗时操作,如文件上传、图像处理等,采用异步方式处理,避免阻塞主线程。


3. 缓存优化


缓存技术可以显著提升系统响应速度:


  • 选择合适的缓存:根据数据特性和访问模式,选择合适的缓存解决方案,如 Redis(适用于高并发的键值存储)、Memcached(适用于简单的缓存需求)等。

  • 缓存策略:设计合理的缓存失效策略(如 LRU、LFU)、缓存更新策略(如写穿、写回、写更新)和缓存预热机制,确保缓存的有效性和命中率。

  • 分布式缓存:在大型系统中,使用分布式缓存来扩展缓存容量,提高系统的横向扩展能力。


八、部署与运维


1. 部署


系统部署是将开发完成的软件部署到生产环境的过程:


  • 打包:将 Java Web 应用打包成 WAR(Web Application Archive)文件,这通常包括编译后的 Java 类文件、资源文件(如 HTML、CSS、JavaScript)、配置文件等。

  • 部署到 Tomcat:将 WAR 文件上传到 Tomcat 服务器的 webapps 目录下,Tomcat 会自动解压并部署该应用。配置 Tomcat 的 server.xml 文件,根据需要调整应用的端口号、上下文路径等。

  • 环境配置:确保生产环境与开发环境的一致性,包括 JDK 版本、数据库连接信息、第三方服务地址等。

  • 安全加固:配置防火墙规则,限制不必要的端口访问;使用 HTTPS 协议保护数据传输安全;定期更新服务器和应用的安全补丁。


2. 监控


监控系统健康状态是运维工作的基础:


  • 实时监控:利用 Prometheus 等监控工具收集服务器的 CPU、内存、磁盘、网络等性能指标,以及应用的请求量、响应时间、错误率等业务指标。

  • 告警机制:配置告警规则,当指标异常时,通过邮件、短信、Slack 等方式通知相关人员,确保问题能够及时处理。

  • 可视化分析:使用 Grafana 等工具将监控数据可视化展示,帮助运维人员快速定位问题根源。


3. 备份


数据备份是保障数据安全的重要措施:


  • 定期备份:根据数据的重要性和变化频率,制定备份策略,如全量备份、增量备份、差异备份等。

  • 异地备份:将备份数据存储在物理上远离生产环境的地点,以防本地灾难性事件导致数据丢失。

  • 备份验证:定期恢复备份数据进行验证,确保备份数据的有效性和可恢复性。


4. 更新


系统更新是保持系统活力和满足用户需求的关键:


  • 需求收集:通过用户反馈、市场调研等方式收集新功能需求和性能改进建议。

  • 版本规划:根据需求的重要性和紧急程度,制定版本迭代计划,包括功能开发、测试、发布等阶段。

  • 灰度发布:在正式全量发布前,先对部分用户进行灰度发布,收集反馈,评估效果,减少风险。

  • 文档更新:随着系统的更新,及时更新用户手册、API 文档等技术文档,确保用户能够正确理解和使用新功能。


九、项目总结


综上所述,本次 JavaWeb 学生自习管理系统的开发是一次宝贵的实践经验,不仅提高了我们的技术水平,也让我们对软件开发有了更深入的认识和理解。以上内容仅供各位开发读者参考,未来,我们将继续优化和完善系统,为学生提供更加便捷、高效的自习管理服务。


文章转载自:TechSynapse

原文链接:https://www.cnblogs.com/TS86/p/18642543

体验地址:http://www.jnpfsoft.com/?from=infoq

用户头像

还未添加个人签名 2023-06-19 加入

还未添加个人简介

评论

发布
暂无评论
Java Web学生自习管理系统_Java_不在线第一只蜗牛_InfoQ写作社区