基于JSP+Servlet的在线医疗预约挂号平台 - 源码深度解析

JavaJavaScriptMavenHTMLCSSMySQLJSP+Servlet
2026-03-104 浏览

文章摘要

本项目是一款基于JSP+Servlet技术栈构建的在线医疗预约挂号平台,旨在解决传统线下挂号排队耗时长、医疗资源分配不均、患者就诊信息不透明等核心痛点。平台通过数字化手段整合医院科室与医生资源,使患者能够便捷地在线查询号源、选择医生并完成预约,有效缩短了候诊时间,提升了医疗服务的可及性与效率,为医患...

在医疗信息化快速发展的今天,传统线下挂号模式因其耗时耗力、信息不透明等痛点,已难以满足现代患者对高效便捷医疗服务的需求。针对这一现状,一个基于JSP+Servlet技术栈构建的在线医疗预约挂号平台应运而生,我们可称其为“医捷通”预约系统。该系统通过数字化手段重构了医患连接方式,实现了医疗资源的优化配置与就诊流程的线上化闭环。

系统采用经典的三层架构模式,清晰地将应用划分为表示层、业务逻辑层和数据持久层。表示层由JSP(JavaServer Pages)负责,利用其动态页面生成能力,结合HTML、CSS和JavaScript构建用户界面,实现数据的可视化展示与交互。业务逻辑层以Servlet为核心,作为系统的控制中枢,处理所有来自前端的HTTP请求,如用户认证、预约提交、数据查询等,并调用相应的业务组件完成处理。数据持久层则通过JDBC(Java Database Connectivity)与MySQL数据库进行交互,确保患者信息、医生排班、预约记录等关键数据的可靠存储与高效访问。项目采用Maven进行依赖管理,保证了第三方库版本的一致性和项目构建的标准化。

数据库设计是系统稳定运行的基石,其核心表结构的设计直接关系到业务逻辑的复杂度和数据一致性。系统共设计了9张核心数据表,以下是几个关键表的结构分析:

患者表(patient) 的设计不仅涵盖了基本的身份信息,还通过status字段实现了账户的状态管理(如激活、禁用),增强了系统的管理灵活性。verification_codecode_expiry字段的引入,为基于邮箱的注册验证流程提供了数据支撑,体现了对安全性的考虑。

CREATE TABLE patient (
  patient_id int NOT NULL AUTO_INCREMENT,
  username varchar(50) NOT NULL,
  password varchar(255) NOT NULL,
  email varchar(100) NOT NULL,
  full_name varchar(100) NOT NULL,
  date_of_birth date DEFAULT NULL,
  gender enum('Male','Female','Other') DEFAULT NULL,
  phone_number varchar(20) DEFAULT NULL,
  address text,
  verification_code varchar(10) DEFAULT NULL,
  code_expiry datetime DEFAULT NULL,
  status enum('Active','Inactive') DEFAULT 'Inactive',
  created_at timestamp NULL DEFAULT CURRENT_TIMESTAMP,
  PRIMARY KEY (patient_id),
  UNIQUE KEY username (username),
  UNIQUE KEY email (email)
);

预约表(appointment) 的设计是业务逻辑的核心。它通过外键patient_idschedule_id分别关联患者与医生排班,明确了预约关系的实体。status字段使用枚举类型定义了预约的生命周期状态(如待确认、已预约、已完成、已取消),是驱动系统流程状态转换的关键。appointment_datesymptoms等字段则记录了具体的就诊信息。

CREATE TABLE appointment (
  appointment_id int NOT NULL AUTO_INCREMENT,
  patient_id int NOT NULL,
  schedule_id int NOT NULL,
  appointment_date date NOT NULL,
  symptoms text,
  status enum('Pending','Confirmed','Completed','Cancelled') DEFAULT 'Pending',
  created_at timestamp NULL DEFAULT CURRENT_TIMESTAMP,
  PRIMARY KEY (appointment_id),
  KEY patient_id (patient_id),
  KEY schedule_id (schedule_id),
  CONSTRAINT appointment_ibfk_1 FOREIGN KEY (patient_id) REFERENCES patient (patient_id),
  CONSTRAINT appointment_ibfk_2 FOREIGN KEY (schedule_id) REFERENCES doctor_schedule (schedule_id)
);

医生排班表(doctor_schedule) 的设计巧妙地将医生、科室和可预约时段进行绑定。dateavailable_slots字段共同决定了某天某医生的号源总量,而booked_slots字段则实时反映了已被预约的数量,二者之差即为剩余可预约号源,这是实现号源控制的基础。status字段允许管理员对排班进行启用或停用操作。

CREATE TABLE doctor_schedule (
  schedule_id int NOT NULL AUTO_INCREMENT,
  doctor_id int NOT NULL,
  department_id int NOT NULL,
  date date NOT NULL,
  available_slots int NOT NULL,
  booked_slots int DEFAULT '0',
  status enum('Available','Full','Cancelled') DEFAULT 'Available',
  created_at timestamp NULL DEFAULT CURRENT_TIMESTAMP,
  PRIMARY KEY (schedule_id),
  KEY doctor_id (doctor_id),
  KEY department_id (department_id),
  CONSTRAINT doctor_schedule_ibfk_1 FOREIGN KEY (doctor_id) REFERENCES doctor (doctor_id),
  CONSTRAINT doctor_schedule_ibfk_2 FOREIGN KEY (department_id) REFERENCES department (department_id)
);

系统的核心功能围绕不同角色的用户展开,主要包括患者端、医生端和管理员端。

1. 患者注册与认证 患者通过注册功能创建账户。前端表单收集用户名、密码、邮箱等信息,提交至PatientRegisterServlet。该Servlet负责数据验证、密码加密(通常使用MD5或BCrypt),并生成随机验证码发送至用户邮箱。只有邮箱验证成功后,账户状态才会被激活。登录功能则由LoginServlet处理,它核对凭证后,将用户对象存入HttpSession,为后续的权限控制提供依据。 用户注册界面

核心代码:用户注册Servlet片段

// PatientRegisterServlet.java
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
    String username = request.getParameter("username");
    String password = request.getParameter("password");
    String email = request.getParameter("email");
    String fullName = request.getParameter("full_name");

    // 检查用户名和邮箱是否已存在
    PatientDAO patientDAO = new PatientDAO();
    if (patientDAO.isUsernameExists(username)) {
        request.setAttribute("errorMessage", "用户名已存在");
        request.getRequestDispatcher("/register.jsp").forward(request, response);
        return;
    }
    if (patientDAO.isEmailExists(email)) {
        request.setAttribute("errorMessage", "邮箱已被注册");
        request.getRequestDispatcher("/register.jsp").forward(request, response);
        return;
    }

    // 加密密码
    String encryptedPassword = HashUtil.md5(password);

    // 生成验证码
    String verificationCode = generateVerificationCode();
    Date expiryTime = new Date(System.currentTimeMillis() + 30 * 60 * 1000); // 30分钟后过期

    Patient patient = new Patient();
    patient.setUsername(username);
    patient.setPassword(encryptedPassword);
    patient.setEmail(email);
    patient.setFullName(fullName);
    patient.setVerificationCode(verificationCode);
    patient.setCodeExpiry(new java.sql.Timestamp(expiryTime.getTime()));
    patient.setStatus("Inactive");

    if (patientDAO.addPatient(patient)) {
        // 发送验证邮件
        EmailUtil.sendVerificationEmail(email, verificationCode);
        response.sendRedirect("register-success.jsp");
    } else {
        request.setAttribute("errorMessage", "注册失败,请重试");
        request.getRequestDispatcher("/register.jsp").forward(request, response);
    }
}

2. 智能预约与号源管理 这是系统的核心价值所在。患者登录后,可浏览科室列表和医生介绍。 科室列表 选择目标科室后,系统展示该科室下所有医生的近期排班。AppointmentServlet处理预约请求,其关键逻辑是并发控制:在患者提交预约时,需要原子性地检查并更新doctor_schedule表中的booked_slots,确保不会出现超售现象。这通常通过数据库的乐观锁或悲观锁机制实现。

核心代码:预约提交Servlet片段

// AppointmentServlet.java
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
    HttpSession session = request.getSession();
    Patient patient = (Patient) session.getAttribute("patient");
    if (patient == null) {
        response.sendRedirect("login.jsp");
        return;
    }

    int scheduleId = Integer.parseInt(request.getParameter("schedule_id"));
    String symptoms = request.getParameter("symptoms");
    Date appointmentDate = new Date(); // 实际应从请求中获取

    AppointmentDAO appointmentDAO = new AppointmentDAO();
    DoctorScheduleDAO scheduleDAO = new DoctorScheduleDAO();

    Connection conn = null;
    try {
        conn = DatabaseConnection.getConnection();
        conn.setAutoCommit(false); // 开启事务

        // 1. 检查号源是否充足(使用行级锁)
        DoctorSchedule schedule = scheduleDAO.getScheduleByIdForUpdate(scheduleId, conn);
        if (schedule == null || schedule.getBookedSlots() >= schedule.getAvailableSlots()) {
            request.setAttribute("errorMessage", "号源已满,预约失败");
            request.getRequestDispatcher("/appointment.jsp").forward(request, response);
            conn.rollback();
            return;
        }

        // 2. 创建预约记录
        Appointment appointment = new Appointment();
        appointment.setPatientId(patient.getPatientId());
        appointment.setScheduleId(scheduleId);
        appointment.setAppointmentDate(new java.sql.Date(appointmentDate.getTime()));
        appointment.setSymptoms(symptoms);
        appointment.setStatus("Pending");

        boolean appointmentSuccess = appointmentDAO.addAppointment(appointment, conn);

        // 3. 更新已预约数量
        boolean updateSuccess = scheduleDAO.incrementBookedSlots(scheduleId, conn);

        if (appointmentSuccess && updateSuccess) {
            conn.commit(); // 提交事务
            response.sendRedirect("booking-success.jsp");
        } else {
            conn.rollback(); // 回滚事务
            request.setAttribute("errorMessage", "预约失败,请重试");
            request.getRequestDispatcher("/appointment.jsp").forward(request, response);
        }
    } catch (Exception e) {
        if (conn != null) {
            try { conn.rollback(); } catch (SQLException ex) {}
        }
        e.printStackTrace();
        // 处理异常
    } finally {
        // 关闭连接
    }
}

预约成功页面

3. 医生工作台与队列管理 医生登录系统后,可以查看自己的排班信息和当日就诊队列。ViewScheduleServlet负责查询并展示医生的未来排班。PatientQueueServlet则根据当前日期和医生ID,从appointment表中检索状态为“已确认”的预约,并按时间顺序生成候诊队列,极大方便了医生规划诊疗顺序。 医生查看患者队列

核心代码:医生查看患者队列Servlet片段

// PatientQueueServlet.java
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
    HttpSession session = request.getSession();
    Doctor doctor = (Doctor) session.getAttribute("doctor");
    if (doctor == null) {
        response.sendRedirect("doctor-login.jsp");
        return;
    }

    String dateStr = request.getParameter("date");
    Date targetDate;
    if (dateStr != null && !dateStr.isEmpty()) {
        try {
            targetDate = new SimpleDateFormat("yyyy-MM-dd").parse(dateStr);
        } catch (ParseException e) {
            targetDate = new Date(); // 默认今天
        }
    } else {
        targetDate = new Date();
    }

    AppointmentDAO appointmentDAO = new AppointmentDAO();
    List<AppointmentDetail> queueList = appointmentDAO.getAppointmentsByDoctorAndDate(doctor.getDoctorId(), new java.sql.Date(targetDate.getTime()));

    request.setAttribute("queueList", queueList);
    request.setAttribute("selectedDate", new SimpleDateFormat("yyyy-MM-dd").format(targetDate));
    request.getRequestDispatcher("/doctor/patient-queue.jsp").forward(request, response);
}

4. 管理员数据看板与资源调配 管理员拥有最高权限,可以管理医生、科室、患者信息,并查看全局数据报表。AdminDashboardServlet会聚合来自多张表的数据,生成可视化的图表,如按科室统计的预约量趋势图,为医院管理层决策提供数据支持。 管理员查看预约趋势图

核心代码:数据统计Servlet片段

// AdminDashboardServlet.java
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
    AdminDAO adminDAO = new AdminDAO();

    // 获取近期预约趋势(最近7天)
    Map<String, Integer> appointmentTrend = adminDAO.getAppointmentTrend(7);

    // 获取各科室预约占比
    Map<String, Integer> departmentDistribution = adminDAO.getAppointmentDistributionByDepartment();

    // 获取医生工作量排名
    List<DoctorWorkload> doctorWorkload = adminDAO.getDoctorWorkloadTopN(10);

    request.setAttribute("appointmentTrend", appointmentTrend);
    request.setAttribute("departmentDistribution", departmentDistribution);
    request.setAttribute("doctorWorkload", doctorWorkload);
    request.getRequestDispatcher("/admin/dashboard.jsp").forward(request, response);
}

系统的实体模型清晰地定义了业务对象及其关系。核心实体如PatientDoctorDepartmentAppointmentDoctorSchedule等,通过DAO(Data Access Object)模式进行持久化操作。每个DAO类封装了对应实体的CRUD(增删改查)操作,并通过DatabaseUtil类获取数据库连接,确保数据访问层的一致性。

核心代码:数据库连接工具类

// DatabaseConnection.java
public class DatabaseConnection {
    private static final String URL = "jdbc:mysql://localhost:3306/medical_appointment?useSSL=false&serverTimezone=UTC";
    private static final String USERNAME = "root";
    private static final String PASSWORD = "password";

    public static Connection getConnection() {
        Connection conn = null;
        try {
            Class.forName("com.mysql.cj.jdbc.Driver");
            conn = DriverManager.getConnection(URL, USERNAME, PASSWORD);
        } catch (ClassNotFoundException | SQLException e) {
            e.printStackTrace();
        }
        return conn;
    }

    public static void closeConnection(Connection conn) {
        if (conn != null) {
            try {
                conn.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
    }
}

核心代码:患者实体类

// Patient.java
public class Patient {
    private int patientId;
    private String username;
    private String password;
    private String email;
    private String fullName;
    private Date dateOfBirth;
    private String gender;
    private String phoneNumber;
    private String address;
    private String verificationCode;
    private Timestamp codeExpiry;
    private String status;
    private Timestamp createdAt;

    // 无参构造器、全参构造器、getter和setter方法
    public Patient() {}

    public Patient(int patientId, String username, String password, String email, String fullName, Date dateOfBirth, String gender, String phoneNumber, String address, String verificationCode, Timestamp codeExpiry, String status, Timestamp createdAt) {
        this.patientId = patientId;
        this.username = username;
        this.password = password;
        this.email = email;
        this.fullName = fullName;
        this.dateOfBirth = dateOfBirth;
        this.gender = gender;
        this.phoneNumber = phoneNumber;
        this.address = address;
        this.verificationCode = verificationCode;
        this.codeExpiry = codeExpiry;
        this.status = status;
        this.createdAt = createdAt;
    }

    // Getter and Setter methods...
    public int getPatientId() { return patientId; }
    public void setPatientId(int patientId) { this.patientId = patientId; }
    // ... 其他getter/setter
}

尽管“医捷通”系统已经实现了核心的预约功能,但在以下方面仍有优化和扩展空间:

  1. 高并发号源处理:在号源释放(如早上8点开放未来一周的号源)时,可能会面临极高的并发请求。当前基于数据库行锁的方案可能成为瓶颈。未来可引入Redis等内存数据库,利用其原子操作(如DECR)实现号源库存的缓存与高速扣减,再将结果异步持久化到MySQL,从而大幅提升系统吞吐量。

  2. 消息推送与智能提醒:集成短信或微信模板消息服务,在预约成功、就诊前一日、医生停诊等关键节点主动推送提醒给患者,提升用户体验,减少爽约率。可使用消息队列(如RabbitMQ)异步处理发送任务,避免阻塞主业务流程。

  3. 分库分表与读写分离:随着医院规模和用户量的增长,单一的MySQL数据库可能面临性能压力。未来可根据业务特性进行垂直分库(如用户库、预约库),并对数据量大的表(如appointment)进行水平分表(按时间范围)。同时,配置主从复制,实现读写分离,减轻主库压力。

  4. 微服务架构重构:当前单体架构在功能复杂后,会面临模块耦合、部署不灵活等问题。可考虑将系统拆分为用户中心、预约服务、排班服务、消息服务等独立的微服务。每个服务使用Spring Boot开发,通过Spring Cloud套件(Eureka, Feign, Gateway等)进行服务治理,提升系统的弹性、可维护性和可扩展性。

  5. 数据分析与智能推荐:在积累足够多的预约数据后,可以构建数据分析模块。利用大数据技术分析各科室、医生的就诊高峰时段、患者偏好等,进而为患者提供智能化的医生推荐,或为医院管理层的资源调配提供更深入的决策支持。

该系统通过严谨的MVC架构、合理的数据库设计以及清晰的业务逻辑实现,成功地将传统的线下挂号流程迁移至线上,有效提升了医疗服务的效率与透明度。其技术选型成熟稳定,代码结构清晰,为后续的功能增强和性能优化奠定了坚实的基础。随着医疗信息化的深入发展,此类平台将在优化医疗资源配置、改善患者就医体验方面发挥越来越重要的作用。

本文关键词
JSPServlet在线医疗预约预约挂号源码解析

上下篇

上一篇
没有更多文章
下一篇
没有更多文章