在医疗信息化快速发展的今天,住院部作为医院运营的核心部门,其管理效率直接关系到医疗服务质量与患者满意度。传统模式下,住院信息管理高度依赖纸质单据的传递与手工记录,导致信息孤岛、数据不一致、部门协同效率低下等问题日益凸显。针对这些痛点,我们设计并实现了一套基于SSM(Spring + Spring MVC + MyBatis)框架的智能住院业务协同平台,旨在通过技术手段实现住院业务流程的全面数字化、标准化与协同化。
本平台采用经典的三层架构,后端以Spring框架为核心,负责业务对象的生命周期管理与复杂的事务控制。Spring的IoC(控制反转)容器将患者管理、医嘱执行、床位分配等模块解耦,使得系统具备高度的可维护性与可扩展性。同时,利用Spring AOP(面向切面编程)实现了统一的日志记录与权限校验逻辑,例如对所有涉及患者隐私数据的访问操作进行自动拦截与审计。Web层选用Spring MVC框架,通过DispatcherServlet作为前端控制器,将HTTP请求精准路由至对应的@Controller注解类。结合方法级别的@RequestMapping注解,实现了RESTful风格的API设计,简化了前后端数据交互。持久层采用MyBatis框架,其强大的动态SQL能力与灵活的映射配置,使得复杂的数据查询与更新操作变得直观高效。通过XML映射文件,将Java接口方法与SQL语句动态关联,有效管理了患者信息、床位状态、费用明细等核心数据实体。前端技术栈选用轻量级的jQuery库与响应式布局框架Bootstrap,配合JSP页面模板进行动态内容渲染。表单数据的实时验证与页面局部的异步更新通过Ajax技术实现,确保了用户操作的流畅性与即时反馈。数据库选用稳定可靠的MySQL,通过精心设计的规范化表结构,确保了数据的一致性、完整性与安全性。
数据库架构设计与核心表分析
数据库设计是系统稳定运行的基石。本平台共设计17张数据表,涵盖了用户权限、患者信息、医疗资源、业务流程等全部核心领域。以下重点分析三个具有代表性的表结构,展示其设计亮点与技术深度。
1. 用户表(sys_user):多角色权限体系的基石
用户表的设计直接支撑了系统的多角色协同工作模式。它不仅存储基本的登录凭证,还通过外键与角色表关联,实现了灵活的权限分配机制。
CREATE TABLE `sys_user` (
`user_id` int(11) NOT NULL AUTO_INCREMENT COMMENT '用户ID',
`username` varchar(50) NOT NULL COMMENT '用户名',
`password` varchar(100) NOT NULL COMMENT '密码',
`real_name` varchar(50) DEFAULT NULL COMMENT '真实姓名',
`role_id` int(11) NOT NULL COMMENT '角色ID',
`dept_id` int(11) DEFAULT NULL COMMENT '部门ID',
`is_active` tinyint(1) DEFAULT '1' COMMENT '是否启用(1-启用 0-禁用)',
`create_time` datetime DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
`update_time` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
PRIMARY KEY (`user_id`),
UNIQUE KEY `uk_username` (`username`),
KEY `fk_user_role` (`role_id`),
KEY `idx_dept` (`dept_id`),
CONSTRAINT `fk_user_role` FOREIGN KEY (`role_id`) REFERENCES `sys_role` (`role_id`) ON DELETE RESTRICT
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='系统用户表';
设计亮点分析:
- 密码安全存储:
password字段采用高强度哈希算法(如BCrypt)加密存储,而非明文,有效保障用户信息安全。 - 灵活的权限模型:通过
role_id外键关联角色表,实现了用户与角色的解耦。一个用户可以属于一个角色(如护士、医生、财务),而角色所拥有的权限可以在sys_role和sys_permission表中动态配置,极大提升了系统的可配置性。 - 软删除与状态控制:
is_active字段实现了软删除功能。当禁用某个用户时,并非物理删除记录,而是改变其状态,这便于审计和历史数据追溯。create_time和update_time自动维护了数据的生命周期。
2. 住院流水表(inpatient_record):业务流程的核心载体
住院流水表是记录患者从入院到出院全过程的中心表,它与患者表、床位表、医生表等多个实体关联,结构相对复杂,体现了业务核心逻辑。
CREATE TABLE `inpatient_record` (
`record_id` int(11) NOT NULL AUTO_INCREMENT COMMENT '住院流水ID',
`patient_id` int(11) NOT NULL COMMENT '患者ID',
`bed_id` int(11) NOT NULL COMMENT '床位ID',
`admitting_doctor_id` int(11) NOT NULL COMMENT '主治医生ID',
`admission_time` datetime NOT NULL COMMENT '入院时间',
`expected_discharge_time` datetime DEFAULT NULL COMMENT '预计出院时间',
`actual_discharge_time` datetime DEFAULT NULL COMMENT '实际出院时间',
`status` tinyint(4) NOT NULL DEFAULT '1' COMMENT '状态(1-在院 2-已出院 3-转科)',
`total_cost` decimal(10,2) DEFAULT '0.00' COMMENT '总费用',
`paid_amount` decimal(10,2) DEFAULT '0.00' COMMENT '已缴金额',
`created_by` int(11) NOT NULL COMMENT '登记人',
`created_time` datetime DEFAULT CURRENT_TIMESTAMP COMMENT '登记时间',
PRIMARY KEY (`record_id`),
KEY `fk_inpatient_patient` (`patient_id`),
KEY `fk_inpatient_bed` (`bed_id`),
KEY `fk_inpatient_doctor` (`admitting_doctor_id`),
KEY `idx_status` (`status`),
KEY `idx_admission_time` (`admission_time`),
CONSTRAINT `fk_inpatient_bed` FOREIGN KEY (`bed_id`) REFERENCES `bed_info` (`bed_id`),
CONSTRAINT `fk_inpatient_doctor` FOREIGN KEY (`admitting_doctor_id`) REFERENCES `sys_user` (`user_id`),
CONSTRAINT `fk_inpatient_patient` FOREIGN KEY (`patient_id`) REFERENCES `patient_info` (`patient_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='住院流水表';
设计亮点分析:
- 状态驱动的工作流:
status字段清晰地定义了患者在院生命周期的不同阶段(在院、已出院、转科)。系统后端可以根据状态的变化触发不同的业务逻辑,例如,当状态变为“已出院”时,自动触发结算流程并释放床位。 - 财务字段的原子性保证:
total_cost(总费用)和paid_amount(已缴金额)字段的设计,结合MySQL的事务特性,确保了费用计算和结算的准确性。任何一笔费用的产生(如药品、检查)和支付都会在一个事务内更新这些字段,防止数据不一致。 - 高效的查询索引:为
status和admission_time等高频查询条件建立了索引,显著提升了在院患者列表、历史病历查询等操作的响应速度。
3. 床位信息表(bed_info):医疗资源管理的关键
床位是住院部最核心的物理资源,其管理效率直接影响患者的收治能力。床位表的设计需要精准反映床位的实时状态和属性。
CREATE TABLE `bed_info` (
`bed_id` int(11) NOT NULL AUTO_INCREMENT COMMENT '床位ID',
`ward_id` int(11) NOT NULL COMMENT '病房ID',
`bed_number` varchar(20) NOT NULL COMMENT '床位编号',
`bed_type` tinyint(4) NOT NULL COMMENT '床位类型(1-普通床 2-监护床)',
`is_occupied` tinyint(1) NOT NULL DEFAULT '0' COMMENT '是否占用(1-占用 0-空置)',
`current_record_id` int(11) DEFAULT NULL COMMENT '当前住院流水ID',
`notes` varchar(200) DEFAULT NULL COMMENT '备注',
PRIMARY KEY (`bed_id`),
UNIQUE KEY `uk_ward_bed_number` (`ward_id`,`bed_number`),
KEY `fk_bed_ward` (`ward_id`),
KEY `idx_occupied` (`is_occupied`),
CONSTRAINT `fk_bed_ward` FOREIGN KEY (`ward_id`) REFERENCES `ward_info` (`ward_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='床位信息表';
设计亮点分析:
- 唯一性约束与业务逻辑:通过
UNIQUE KEY uk_ward_bed_number (ward_id,bed_number)约束,确保了在同一病房内床位编号的唯一性,这是业务上的硬性要求。 - 状态标志与关联:
is_occupied字段提供了对床位占用情况的快速查询能力。而current_record_id外键直接关联到inpatient_record表,可以快速定位到当前占用该床位的患者及其全部住院信息,实现了资源与业务的紧密绑定。 - 类型化管理:
bed_type字段支持对不同类型床位(如普通床、监护床)的分类管理,为后续实现按床位类型进行差异化收费或分配策略奠定了基础。
核心功能模块深度解析
1. 患者入院登记与床位智能分配
患者入院是住院流程的起点,该功能实现了患者信息录入与床位分配的一体化操作。前端通过Bootstrap模态框提供清晰的表单界面,后端则在一个事务中完成多项数据操作,确保数据一致性。

后端Controller层代码(Spring MVC):
@Controller
@RequestMapping("/inpatient")
public class InpatientController {
@Autowired
private InpatientService inpatientService;
@PostMapping("/admit")
@ResponseBody
public ResponseEntity<Map<String, Object>> admitPatient(@RequestBody AdmissionForm form) {
Map<String, Object> result = new HashMap<>();
try {
// 调用服务层方法,在事务中处理入院逻辑
InpatientRecord newRecord = inpatientService.processAdmission(form);
result.put("success", true);
result.put("message", "患者入院登记成功!住院号:" + newRecord.getRecordId());
result.put("data", newRecord);
return ResponseEntity.ok(result);
} catch (NoBedAvailableException e) {
result.put("success", false);
result.put("message", "入院失败:" + e.getMessage());
return ResponseEntity.badRequest().body(result);
} catch (Exception e) {
result.put("success", false);
result.put("message", "系统错误,入院登记失败");
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(result);
}
}
}
服务层核心业务逻辑(Spring Service):
@Service
@Transactional // 声明式事务管理,确保以下操作原子性
public class InpatientServiceImpl implements InpatientService {
@Autowired
private PatientMapper patientMapper;
@Autowired
private BedMapper bedMapper;
@Autowired
private InpatientRecordMapper inpatientRecordMapper;
@Override
public InpatientRecord processAdmission(AdmissionForm form) throws NoBedAvailableException {
// 1. 校验患者信息是否存在或需要新建
PatientInfo patient = patientMapper.selectByIdCard(form.getIdCard());
if (patient == null) {
patient = new PatientInfo();
BeanUtils.copyProperties(form, patient); // 使用Spring工具类拷贝属性
patientMapper.insert(patient);
}
// 2. 查找并锁定一个合适的空床位
BedInfo availableBed = bedMapper.selectOneAvailableBedByType(form.getRequiredBedType());
if (availableBed == null) {
throw new NoBedAvailableException("当前暂无符合条件的空床位");
}
// 3. 创建住院流水记录
InpatientRecord record = new InpatientRecord();
record.setPatientId(patient.getPatientId());
record.setBedId(availableBed.getBedId());
record.setAdmittingDoctorId(form.getDoctorId());
record.setAdmissionTime(new Date());
record.setStatus(InpatientStatus.IN_HOSPITAL.getCode());
record.setCreatedBy(form.getOperatorUserId());
inpatientRecordMapper.insert(record);
// 4. 更新床位状态为“已占用”,并关联当前住院流水ID
availableBed.setIsOccupied(1);
availableBed.setCurrentRecordId(record.getRecordId());
bedMapper.updateById(availableBed);
return record;
}
}
此功能模块的亮点在于其事务性。@Transactional注解保证了“创建住院记录”和“更新床位状态”这两个数据库操作要么全部成功,要么全部失败,彻底避免了患者已登记但床位未占用的数据不一致情况。
2. 医嘱下达与执行跟踪
医嘱是治疗过程的核心记录。系统为医生提供了清晰的界面下达医嘱,并允许护士记录执行情况,形成了完整的闭环管理。

MyBatis Mapper接口与动态SQL:
// Mapper接口
public interface MedicalOrderMapper {
List<MedicalOrder> selectOrdersByCriteria(@Param("recordId") Integer recordId,
@Param("doctorId") Integer doctorId,
@Param("status") String status);
}
// 对应的XML映射文件
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.hospital.mapper.MedicalOrderMapper">
<select id="selectOrdersByCriteria" resultType="MedicalOrder">
SELECT
mo.*,
pi.patient_name,
di.real_name as doctor_name
FROM medical_order mo
LEFT JOIN inpatient_record ir ON mo.record_id = ir.record_id
LEFT JOIN patient_info pi ON ir.patient_id = pi.patient_id
LEFT JOIN sys_user di ON mo.doctor_id = di.user_id
<where>
<if test="recordId != null">
AND mo.record_id = #{recordId}
</if>
<if test="doctorId != null">
AND mo.doctor_id = #{doctorId}
</if>
<if test="status != null and status != ''">
AND mo.order_status = #{status}
</if>
</where>
ORDER BY mo.create_time DESC
</select>
</mapper>
MyBatis的动态SQL能力在此得到充分体现。<where>和<if>标签使得查询条件可以灵活组合,无论是医生查看自己下达的所有医嘱,还是护士查看某个患者的待执行医嘱,都可以通过同一个接口实现,代码简洁而强大。
3. 出院结算与床位释放
出院结算是住院流程的终点,涉及费用核算、医保结算、床位释放等多个步骤。系统将财务计算与业务操作紧密结合。

费用计算服务类:
@Service
public class BillingServiceImpl implements BillingService {
@Autowired
private CostDetailMapper costDetailMapper;
@Override
public DischargeBill generateDischargeBill(Integer recordId) {
DischargeBill bill = new DischargeBill();
// 1. 聚合计算所有费用明细
List<CostDetail> costDetails = costDetailMapper.selectByRecordId(recordId);
BigDecimal totalCost = costDetails.stream()
.map(CostDetail::getAmount)
.reduce(BigDecimal.ZERO, BigDecimal::add);
bill.setTotalCost(totalCost);
// 2. 计算已缴金额(从支付记录中汇总)
BigDecimal paidAmount = ... // 省略支付记录查询汇总逻辑
bill.setPaidAmount(paidAmount);
// 3. 计算待缴金额
BigDecimal dueAmount = totalCost.subtract(paidAmount);
bill.setDueAmount(dueAmount);
// 4. 填充账单详情
bill.setCostDetails(costDetails);
bill.setGeneratedTime(new Date());
return bill;
}
}
出院操作的核心事务方法:
@Service
public class DischargeServiceImpl implements DischargeService {
@Autowired
private InpatientRecordMapper recordMapper;
@Autowired
private BedMapper bedMapper;
@Autowired
private BillingService billingService;
@Transactional
@Override
public void processDischarge(Integer recordId, Integer operatorId) {
// 1. 生成最终账单
DischargeBill finalBill = billingService.generateDischargeBill(recordId);
if (finalBill.getDueAmount().compareTo(BigDecimal.ZERO) > 0) {
throw new BusinessException("患者尚有未结清费用,无法办理出院");
}
// 2. 更新住院记录状态和实际出院时间
InpatientRecord record = recordMapper.selectById(recordId);
record.setStatus(InpatientStatus.DISCHARGED.getCode());
record.setActualDischargeTime(new Date());
recordMapper.updateById(record);
// 3. 释放关联的床位
BedInfo bed = bedMapper.selectById(record.getBedId());
bed.setIsOccupied(0);
bed.setCurrentRecordId(null);
bedMapper.updateById(bed);
// 4. 记录操作日志(通过AOP实现)
// ...
}
}
出院流程严格遵循业务规则,只有在费用完全结清后,系统才允许完成出院操作并释放床位。整个流程同样被@Transactional包裹,确保了财务数据和床位状态变更的原子性。
4. 基于角色的动态权限控制
平台支持医护人员、行政管理人员、财务人员等多角色协同工作,其背后是一套基于URL拦截的精细权限控制系统。

Spring AOP权限检查切面:
@Component
@Aspect
public class PermissionAspect {
@Autowired
private HttpSession session;
@Before("@annotation(requiredPermission)") // 前置通知,在方法执行前进行权限检查
public void checkPermission(RequiredPermission requiredPermission) {
// 从session中获取当前登录用户
User currentUser = (User) session.getAttribute("currentUser");
if (currentUser