医院信息系统(HIS)的开发架构解析,代码示例
医院信息系统(HIS)是现代医院运营的核心,其架构设计直接关系到系统的稳定性、安全性、扩展性、维护性和用户体验。一个优秀的架构能够支撑医院未来10年甚至更长时间的业务发展和技术演进。
HIS架构演进历程
HIS的架构并非一成不变,它随着技术的发展而不断演进,主要经历了以下几个阶段:
1、单机架构:早期阶段,功能简单,数据独立,无法共享,已基本淘汰。
2、客户机/服务器架构(C/S):这是传统HIS最主流的架构。客户端需要安装专门的软件,负责UI和业务逻辑,服务器端主要负责数据存储。其优点是响应速度快,能充分利用客户端性能;缺点是部署维护成本高,升级困难。
3、浏览器/服务器架构(B/S):随着Web技术发展,B/S架构成为新趋势。客户端只需浏览器,业务逻辑集中在服务器端。优点是部署维护方便,跨平台性强;缺点是对复杂客户端操作(如大量数据录入、实时性要求高的场景)体验稍逊于C/S。
4、混合架构(C/S + B/S):这是目前大型HIS系统最常见的模式。对内(医生护士工作站、收费发药等高频操作端)采用C/S架构以保证性能和体验;对外(患者门户、网上预约、查询等)采用B/S架构以方便访问和集成。
5、微服务与云原生架构:这是当前及未来的发展方向。将庞大的单体应用拆分为一组小的、相互独立的微服务,每个服务运行在自己的进程中,通过轻量级机制(如HTTP/REST)通信。便于独立开发、部署、伸缩和更新。
本文将对HIS开发架构进行详细解析,并提供一些代码示例,帮助大家理解这一系统的工作原理。
现代HIS系统详细架构解析
我们以目前主流的基于微服务的云原生架构 为蓝本进行分解。这种架构解耦了各个业务单元,使系统更具弹性、可扩展性和可维护性。
整个架构可以分为六个核心层次,如下图所示:
核心微服务代码示例(概念性)
以下是一些关键服务的概念性代码示例,使用 Java Spring Cloud 技术栈。
1、API 网关 - 路由与认证
application.yml
spring:cloud:gateway:routes:- id: patient-serviceuri: lb://patient-servicepredicates:- Path=/api/patients/**filters:- name: RequestRateLimiterargs: # 限流配置redis-rate-limiter.replenishRate: 10redis-rate-limiter.burstCapacity: 20- name: JwtAuthentication # 自定义过滤器,验证JWT- id: order-serviceuri: lb://order-servicepredicates:- Path=/api/orders/**
2、患者微服务 - 领域模型与API
实体类 (Patient.java)
@Entity
@Table(name = "patients")
@Data
public class Patient {@Id@GeneratedValue(strategy = GenerationType.IDENTITY)private Long id;@NotBlankprivate String patientId; // 院内患者唯一标识,如“P202405200001”@NotBlankprivate String name;private String gender;private LocalDate dateOfBirth;@Pattern(regexp = "^1[3-9]\\d{9}$") // 简单的手机号校验private String phoneNumber;private String idCardNumber; // 身份证号private String address;// ... 其他字段如过敏史、主要诊断等
}
控制器 (PatientController.java)
@RestController
@RequestMapping("/api/patients")
@RequiredArgsConstructor
public class PatientController {private final PatientService patientService;@PostMappingpublic ResponseEntity<Patient> createPatient(@RequestBody @Valid Patient patient) {Patient savedPatient = patientService.createPatient(patient);return ResponseEntity.status(HttpStatus.CREATED).body(savedPatient);}@GetMapping("/{id}")public ResponseEntity<Patient> getPatient(@PathVariable Long id) {return patientService.getPatientById(id).map(ResponseEntity::ok).orElse(ResponseEntity.notFound().build());}@GetMapping("/search")public Page<Patient> searchPatients(@RequestParam String keyword,@PageableDefault Pageable pageable) {return patientService.searchPatients(keyword, pageable);}
}
服务层 (PatientService.java)
public interface PatientService {Patient createPatient(Patient patient);Optional<Patient> getPatientById(Long id);Page<Patient> searchPatients(String keyword, Pageable pageable);
}
3、服务间调用 - Feign Client
挂号服务需要查询患者信息,通过 OpenFeign 声明式调用。
PatientServiceClient.java
// 在 registration-service 中
@FeignClient(name = "patient-service", url = "${feign.client.patient-service.url}")
public interface PatientServiceClient {@GetMapping("/api/patients/{id}")ResponseEntity<Patient> getPatientById(@PathVariable("id") Long id);@GetMapping("/api/patients/by-patient-id?patientId={patientId}")ResponseEntity<Patient> getPatientByHospitalId(@RequestParam("patientId") String patientId);
}
在挂号服务中调用
@Service
@RequiredArgsConstructor
public class RegistrationService {private final PatientServiceClient patientServiceClient;public RegistrationDTO createRegistration(RegistrationCreateRequest request) {// 1. 通过Feign验证患者信息是否存在ResponseEntity<Patient> patientResponse = patientServiceClient.getPatientByHospitalId(request.getPatientId());if (!patientResponse.getStatusCode().is2xxSuccessful() || patientResponse.getBody() == null) {throw new IllegalArgumentException("患者不存在");}Patient patient = patientResponse.getBody();// 2. 创建挂号记录// ... 业务逻辑// return registrationDTO;}
}
4、数据库操作 - Spring Data JPA & 多数据源
Repository接口 (PatientRepository.java)
public interface PatientRepository extends JpaRepository<Patient, Long> {Optional<Patient> findByPatientId(String patientId);@Query("SELECT p FROM Patient p WHERE p.name LIKE %:keyword% OR p.phoneNumber LIKE %:keyword% OR p.patientId LIKE %:keyword%")Page<Patient> findByKeyword(@Param("keyword") String keyword, Pageable pageable);
}
5、全局异常处理
GlobalExceptionHandler.java
@RestControllerAdvice
public class GlobalExceptionHandler {@ExceptionHandler(IllegalArgumentException.class)public ResponseEntity<ErrorResponse> handleIllegalArgumentException(IllegalArgumentException ex) {ErrorResponse error = new ErrorResponse("BAD_REQUEST", ex.getMessage());return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(error);}@ExceptionHandler(Exception.class)public ResponseEntity<ErrorResponse> handleGenericException(Exception ex) {// 记录日志ErrorResponse error = new ErrorResponse("INTERNAL_SERVER_ERROR", "系统繁忙,请稍后再试");return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(error);}
}@Data
class ErrorResponse {private String code;private String message;private long timestamp = System.currentTimeMillis();public ErrorResponse(String code, String message) {this.code = code;this.message = message;}
}
关键设计考虑与最佳实践
1、数据库设计:
范式与反范式结合:核心基础数据(如患者、药品)采用第三范式以减少冗余。报表和分析类功能采用数据仓库或反范式设计以优化查询性能。
分库分表:对于海量数据表(如诊疗记录、医嘱执行记录),考虑按时间或业务维度进行分库分表。
读写分离:主数据库处理写操作,多个从数据库处理读操作。
2、事务一致性:
分布式事务:对于跨多个微服务的操作(如“挂号->生成医嘱->计费”),使用最终一致性方案而非强一致性。常用模式:
Saga模式:通过一系列本地事务和补偿事务来管理。
基于消息队列:将操作发布为事件,由其他服务异步消费和处理。例如,挂号成功后发布一个“挂号成功”事件,结算服务和药房服务监听该事件进行后续处理。
3、高并发与性能:
缓存:大量使用 Redis 缓存静态数据(如药品目录、诊断字典)、热点数据和会话信息。
队列削峰:将非实时任务(如发送短信、生成报表、记录日志)放入消息队列(如 RabbitMQ, Kafka),异步处理,避免流量冲垮系统。
接口限流与降级:对核心接口(如号源查询、下单)进行限流,并在系统压力大时提供有损服务(降级),保证核心流程可用。
4、安全性:
HTTPS:全站使用 HTTPS 加密传输。
细粒度权限控制:基于 RBAC 模型,控制到按钮/接口级别。例如,一个医生是否有权限开具某类药品。
数据脱敏:在日志、查询结果中,对患者敏感信息(身份证号、手机号)进行脱敏显示。
操作日志:记录所有关键数据的增删改操作,做到事后审计追踪。
总结
一个现代化的 HIS 系统架构是一个复杂的系统工程,远不止写代码那么简单。它需要:
清晰的微服务划分(按领域驱动设计)
强大的基础设施(容器化、 DevOps、监控)
稳健的数据设计(数据库、缓存、队列)
周密的安全设计(认证、授权、审计、脱敏)
灵活的部署策略(蓝绿部署、滚动发布以保障7x24小时服务)
上面的代码示例提供了一个起点和概念模型。实际开发中,每个服务都需要同样细致的设计、测试和部署。建议从一个小型核心服务(如患者管理)开始实践这套架构,逐步迭代和完善。
成品系统源码:SaaS模式Java版云HIS系统
SaaS模式Java版云HIS系统,在公立二甲医院应用多年,经过多年持续优化和打磨,系统运行稳定、功能齐全,界面布局合理、操作简便。融合B/S版电子病历系统,支持电子病历四级,HIS与电子病历系统均拥有自主知识产权。全套源码开放合作,助力软件企业项目快速交付上线。