预约挂号系统
医院预约挂号系统
项目概述
这是一个基于Java Web开发的医院预约挂号系统,支持患者在线预约医生、医生管理预约信息、管理员管理医院资源等功能。系统采用MVC架构,使用Servlet+JSP技术实现,提供了完整的预约挂号流程。
技术栈
- 后端框架:Java Servlet + JSP
- 数据库:MySQL 8.0
- 构建工具:Maven
- 前端技术:HTML, CSS, JavaScript, jQuery, Bootstrap
- ORM:自定义DAO层实现
- 连接池:Druid
系统架构
项目采用经典的三层架构设计:
- 表示层:JSP页面负责用户界面展示
- 业务层:Servlet处理请求和业务逻辑
- 数据层:DAO层负责数据库操作
目录结构
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| src/ ├── main/ │ ├── java/com/qst/ │ │ ├── bean/ # 实体类 │ │ ├── dao/ # 数据访问层 │ │ ├── filter/ # 过滤器 │ │ ├── servlet/ # 控制器 │ │ └── util/ # 工具类 │ ├── resources/ # 配置文件 │ └── webapp/ # Web资源 │ ├── WEB-INF/ # Web配置 │ ├── admin/ # 管理员页面 │ ├── doctor/ # 医生页面 │ ├── css/ # 样式文件 │ ├── js/ # JavaScript文件 │ ├── images/ # 图片资源 │ └── include/ # 公共组件 └── test/ # 测试代码
|
核心功能模块
1. 用户认证模块
- 支持三种角色登录:管理员、医生、患者
- 用户注册功能
- 密码修改功能
2. 预约挂号模块
- 按科室查询医生
- 按医生姓名搜索
- 查看医生出诊信息
- 选择就诊时间和号源
- 确认预约信息
- 查看预约记录
3. 医生管理模块
4. 管理员模块
数据库设计
系统包含以下主要数据表:
admin(管理员表)
account:管理员账号
password:密码
name:管理员姓名
office(科室表)
officename:科室名称
description:科室描述
doctornum:医生数量
doctor(医生表)
did:医生ID
account:医生账号
password:密码
dname:医生姓名
fee:诊查费用
gender:性别
age:年龄
office:所属科室
career:职称
description:医生简介
picpath:头像路径
patient(患者表)
pid:患者ID
account:患者账号
email:邮箱
password:密码
name:患者姓名
integity:信用积分
record(预约记录表)
rid:记录ID
pid:患者ID
wid:工作日ID
did:医生ID
serialnumber:序号
visitdate:就诊日期
visittime:就诊时间
ordertime:预约时间
state:状态(成功、完成、爽约)
workday(工作日表)
wid:工作日ID
did:医生ID
worktime:工作日(0-6表示周日到周六)
ampm:上午/下午
nsnum:剩余号源数量
state:状态(预约、停诊)
系统流程
预约挂号流程
- 患者登录系统
- 浏览科室或搜索医生
- 查看医生出诊信息
- 选择就诊时间和号源
- 确认预约信息
- 完成预约,系统生成预约记录
预约管理流程
- 医生登录系统
- 查看当日预约患者列表
- 管理患者就诊状态
部署说明
环境要求
- JDK 1.8+
- MySQL 8.0+
- Tomcat 8.0+
- Maven 3.0+
部署步骤
- 克隆项目到本地
- 导入Maven依赖
- 执行
hospital.sql脚本创建数据库和表
- 配置数据库连接信息
- 打包成WAR文件
- 部署到Tomcat服务器
- 访问系统首页
默认账号
- 管理员:账号
admin,密码admin
- 医生:可使用数据库中已有的账号,默认密码
123456
- 患者:需要先注册
功能展示
首页
展示医院概况、科室信息、医生推荐等内容
科室浏览
展示医院所有科室信息,可查看科室详情
医生列表
展示各科室医生信息,可查看医生详情
预约确认
展示预约详细信息,包括医生、时间、费用等
预约记录
患者可查看自己的所有预约记录
系统特色
- 多角色权限管理:支持管理员、医生、患者三种角色
- 灵活的预约机制:可按科室或医生进行预约
- 实时号源管理:预约成功后自动更新剩余号源
- 完善的用户体验:简洁明了的界面设计
- 安全性保障:用户认证和会话管理
注意事项
- 系统需要在支持Servlet 4.0的容器中运行
- 数据库连接信息需要根据实际环境进行配置
- 图片资源需要正确部署到指定路径
- 系统目前仅支持基础的预约功能,可根据需求扩展更多功能
开发团队
本系统由医院信息化开发团队开发维护。
许可证
保留所有权利。
医院预约系统关键代码分析
📊 系统架构概览
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
| ┌─────────────────────────────────────────────────────────┐ │ 表示层 (Web层) │ │ ┌─────────────┐ ┌─────────────┐ ┌────────────────┐ │ │ │ JSP页面 │ │ HTML/CSS │ │ JavaScript/jQuery│ │ │ └───────┬─────┘ └───────┬─────┘ └────────┬───────┘ │ │ │ │ │ │ └──────────┼────────────────┼─────────────────┼───────────┘ │ │ │ ┌──────────▼────────────────▼─────────────────▼───────────┐ │ 控制层 (Servlet层) │ │ ┌─────────────┐ ┌─────────────┐ ┌────────────────┐ │ │ │ 登录认证 │ │ 预约管理 │ │ 医生/管理员操作 │ │ │ └───────┬─────┘ └───────┬─────┘ └────────┬───────┘ │ │ │ │ │ │ └──────────┼────────────────┼─────────────────┼───────────┘ │ │ │ ┌──────────▼────────────────▼─────────────────▼───────────┐ │ 数据访问层 (DAO层) │ │ ┌─────────────┐ ┌─────────────┐ ┌────────────────┐ │ │ │ 用户DAO │ │ 预约DAO │ │ 其他业务DAO │ │ │ └───────┬─────┘ └───────┬─────┘ └────────┬───────┘ │ │ │ │ │ │ └──────────┼────────────────┼─────────────────┼───────────┘ │ │ │ ┌──────────▼────────────────▼─────────────────▼───────────┐ │ 数据层 (数据库) │ │ ┌─────────────┐ ┌─────────────┐ ┌────────────────┐ │ │ │ 用户表 │ │ 预约表 │ │ 其他业务表 │ │ │ └─────────────┘ └─────────────┘ └────────────────┘ │ └─────────────────────────────────────────────────────────┘
|
🔑 核心功能代码分析
1. 用户登录认证模块
Login.java - 处理三种角色的登录验证
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45
| @WebServlet(urlPatterns = "/login") public class Login extends HttpServlet { protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { request.setCharacterEncoding("UTF-8"); String account = request.getParameter("account"); String password = request.getParameter("password"); String accounttype = request.getParameter("accounttype"); switch (accounttype) { case "管理员": AdminDao adminDao = new IAdminimpl(); List<Admin> admins = adminDao.getAdmin(account); if (admins.size() > 0 && admins.get(0).getPassword().equals(password)) { request.getSession().setAttribute("admin", admins.get(0)); response.sendRedirect("admin/index.jsp"); } break; case "医生": DoctorDao doctorDao = new DoctorDao(); List<Doctor> doctors = doctorDao.query("where account =?", new Object[]{account}); if (doctors.size() > 0 && doctors.get(0).getPassword().equals(password)) { request.getSession().setAttribute("doctor", doctors.get(0)); response.sendRedirect("doctor/index.jsp"); return; } break; case "患者": PatientDao patientDao = new PatientDao(); List<Patient> patients = patientDao.query("account", account); if (patients.size() > 0 && patients.get(0).getPassword().equals(password)) { request.getSession().setAttribute("patient", patients.get(0)); response.sendRedirect("index.jsp"); return; } break; } request.getSession().setAttribute("message", "用户名或密码错误!!"); response.sendRedirect("login.jsp"); } }
|
代码亮点:
- ✅ 支持三种角色(管理员、医生、患者)的身份验证
- ✅ 使用Session存储用户登录状态
- ✅ 基于角色的页面重定向
- ✅ 错误信息反馈机制
2. 预约挂号流程核心代码
Order.java - 处理患者预约流程
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58
| @WebServlet(urlPatterns = "/order") public class Order extends HttpServlet { private RecodeDao recodeDao = new RecodeDao(); private NumSourceDao numSourceDao = new NumSourceDao(); protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { Patient patient = (Patient) request.getSession().getAttribute("patient"); String wid = request.getParameter("wid"); String did = request.getParameter("did"); String action = request.getParameter("action"); switch (action) { case "order": NumSource numSource = new NumSource(strings[0], strings[1], strings[2], strings[3], wid); DoctorDao doctorDao = new DoctorDao(); List<Doctor> doctors = doctorDao.query(" where did=?", new Object[]{did}); request.getSession().setAttribute("numSource", numSource); request.setAttribute("doctor", doctors.get(0)); request.getRequestDispatcher("confirmOrder.jsp").forward(request, response); break; case "confirm": NumSource numSources = (NumSource) request.getSession().getAttribute("numSource"); Recode recode = new Recode(); recode.setPid(patient.getId()); recode.setDid(did); recode.setWid(wid); recode.setSerialnumber(numSources.getSerialnumber()); recode.setVisitdate(sdf.parse(numSources.getVisitdate())); recode.setVisittime(numSources.getVisittime()); recode.setState("成功"); recode.setOrdertime(new Timestamp(new Date().getTime())); List<Recode> list = recodeDao.query(where1, new Object[]{numSources.getState(), numSources.getVisitdate(), numSources.getVisittime()}); if (list.size() == 0 && recodeDao.order(recode)) { WorkDayDao workDayDao = new WorkDayDao(); List<WorkDay> workDay = workDayDao.query(" where wid=? ", new Object[]{wid}); int num = workDay.get(0).getNsnum() - 1; if (num != 0) { workDayDao.update("set nsnum=? where wid=?", new Object[]{num, wid}); } else { workDayDao.update("set nsnum=? and state=? where wid=?", new Object[]{num, "停诊", wid}); } request.getSession().setAttribute("message", "预约成功!"); request.getRequestDispatcher("orderList").forward(request, response); } else { request.getSession().setAttribute("message", "预约失败或号源已被预约!"); request.getRequestDispatcher("ShowWorkday?did=" + did).forward(request, response); } break; } } }
|
代码亮点:
- ✅ 两步预约流程(准备和确认)提高用户体验
- ✅ 号源冲突检测避免重复预约
- ✅ 自动更新号源数量和状态
- ✅ 完整的事务处理和错误反馈
3. 数据库连接和工具类
DBUtil.java - 数据库操作核心工具类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69
| public class DBUtil { private static String url = "jdbc:mysql://101.133.234.109:3305/hospital?useSSL=false&characterEncoding=UTF-8&allowPublicKeyRetrieval=true"; private static final String user = "root"; private static final String password = "123456"; static { try { Class.forName("com.mysql.cj.jdbc.Driver"); } catch (ClassNotFoundException e) { e.printStackTrace(); } } public static Connection createConn() { try { return DriverManager.getConnection(url, user, password); } catch (SQLException e) { e.printStackTrace(); } return null; } public static ResultSet executeQuery(String sql, Object[] params) { Connection conn = createConn(); PreparedStatement ps = null; ResultSet rs = null; try { ps = conn.prepareStatement(sql); prepare(ps, params); rs = ps.executeQuery(); } catch (SQLException e) { e.printStackTrace(); } return rs; } public static boolean executeUpdate(String sql, Object[] params) { Connection conn = createConn(); PreparedStatement ps = null; int i = 0; try { ps = conn.prepareStatement(sql); prepare(ps, params); i = ps.executeUpdate(); } catch (SQLException e) { e.printStackTrace(); } finally { closeAll(); } return i > 0; } private static void prepare(PreparedStatement ps, Object[] params) throws SQLException { if (params != null) { for (int i = 0; i < params.length; i++) { ps.setObject(i + 1, params[i]); } } } public static void closeAll() { } }
|
Util.java - 通用工具方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
| public class Util { public static int getCode() { return 100000 + (int)(899999 * Math.random()); } public static boolean isEmail(String string) { if (string == null) return false; String regEx1 = "^([a-z0-9A-Z]+[-|\\.]?)+[a-z0-9A-Z]@([a-z0-9A-Z]+(-[a-z0-9A-Z]+)?\\.)+[a-zA-Z]{2,}$"; Pattern p = Pattern.compile(regEx1); Matcher m = p.matcher(string); return m.matches(); } public static String nullToString(String s) { return s == null ? "" : s; } public static String getDate(int day) { Calendar calendar = Calendar.getInstance(); int day0fWeek = calendar.get(Calendar.DAY_OF_WEEK) - 1; if (day0fWeek > day) calendar.add(Calendar.DATE, 7); calendar.set(Calendar.DAY_OF_WEEK, day + 1); return calendar.get(Calendar.YEAR) + "-" + (calendar.get(Calendar.MONTH) + 1) + "-" + calendar.get(Calendar.DAY_OF_MONTH); } }
|
4. 前端关键页面代码
登录页面表单 (login.jsp)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35
| <form role="form" action="login" method="post" class="login-form"> <div class="form-group col-xs-12"> <label class="sr-only"></label>账号(*):<input style="font-weight: bold" type="text" name="account" class="form-control input-control clearfix" required /><span wid="accountTip"></span> </div> <div class="form-group col-xs-12"> <label class="sr-only">password</label>密码(*):<input style="font-weight: bold" type="password" name="password" class="form-password form-control" wid="pwd1" required /> </div> <input hidden value="患者" name="accounttype"> <div class="form-group col-xs-12"> <button type="submit" class="btn" wid="btn">立即登录</button> </div> </form>
<script> function isCheckEmail() { var email = document.getElementById("userEmail").value; var reg = /^\w+((-\w+)|(\.\w+))*\@[A-Za-z0-9]+((\.|-)[A-Za-z0-9]+)*\.[A-Za-z0-9]+$/; isok = reg.test(email); if (isok) { document.getElementById("emailTip").innerHTML = "邮箱格式正确"; document.getElementById("btn").disabled = false; return true; } else { document.getElementById("emailTip").innerHTML = "邮箱格式不正确"; document.getElementById("btn").disabled = true; return false; } } </script>
|
预约确认页面 (confirmOrder.jsp)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| <div class="container1"> <style> .container1 .title{color: #00E8D7;margin:20px auto;display: table;} .container1 .left{float: left;width: 30%;} .container1 .left .confirm{text-align: center;background-color: #009F95;color:#fff;border-radius: 10px;height:30px;cursor:pointer;} </style> <h2 class="title">预约信息核对</h2> <div class="left"> <form autocomplete="off" method="post" action="order"> <input hidden name="wid" value="${numSource.state}"> <input hidden name="did" value="${doctor.did}"> <input hidden name="action" value="confirm"> <table> <tr><td>姓 名:</td><td>${sessionScope.patient.name}</td></tr> <tr><td>预约医生:</td><td>${doctor.dname}</td></tr> <tr><td>预约科室:</td><td>${doctor.office} </td></tr> <tr><td>就诊时间:</td><td>${numSource.visitdate} ${numSource.visittime}第${numSource.serialnumber}号</td></tr> <tr><td>诊 查 费:</td><td>${doctor.fee}.00元</td></tr> <tr><td>你的邮箱:</td><td>${sessionScope.patient.email}</td></tr> <tr><td colspan="2" class="confirm"><button type="submit" class="btn">确定</button></td></tr> </table> </form> </div> </div>
|
📈 业务流程图解
预约挂号流程
1 2 3 4 5 6 7 8 9 10 11 12 13
| ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │ 患者登录 │────>│ 选择科室/医生 │────>│ 查看出诊信息 │ └─────────────┘ └─────────────┘ └──────┬──────┘ │ ▼ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │ 查看预约记录 │<────┤ 完成预约 │<────┤ 确认预约信息 │ └─────────────┘ └──────┬──────┘ └──────┬──────┘ │ │ ▼ │ ┌─────────────┐ │ │ 更新号源状态 │<──────────┘ └─────────────┘
|
🎯 代码优化建议
数据库连接安全
- 当前代码中数据库连接信息(URL、用户名、密码)硬编码在DBUtil类中
- 建议:将连接信息移至配置文件,使用Properties或Spring配置进行管理
密码安全
- 密码以明文形式存储和验证,存在安全风险
- 建议:使用MD5、SHA256等哈希算法加盐存储密码
错误处理优化
- 当前代码使用System.out.println()进行日志输出
- 建议:引入Log4j或SLF4J进行日志管理
连接资源管理
- 部分方法中存在连接资源未正确关闭的情况
- 建议:使用try-with-resources语句确保资源自动关闭
前端表单验证
- 前端验证逻辑不够完善
- 建议:增加更多的表单验证和用户友好的错误提示
📝 总结
医院预约系统采用经典的三层架构(表示层、业务层、数据层),通过Servlet+JSP技术实现了完整的预约挂号流程。系统支持多角色登录和权限管理,具有灵活的预约机制和实时号源管理功能。核心代码体现了良好的模块化设计和业务逻辑封装,但在安全性和错误处理方面还有优化空间。
通过本代码分析,可以清晰了解系统的工作原理和核心流程,为后续的维护和扩展提供了参考基础。