
在构建医疗管理系统时,核心挑战在于如何高效、灵活地管理医生与患者之间的复杂关系,并确保不同角色用户的权限隔离。具体需求包括:
在设计数据模型时,常见的困惑在于:是为每个角色创建独立的实体并处理各自的认证流程,还是采用统一的用户表并结合角色字段来区分?这两种方案各有优缺点,尤其是在安全认证和数据结构灵活性方面。
在实际开发中,通常会考虑两种基础的数据模型设计思路。
这种方案为每个业务角色(如Doctor和Patient)创建独立的JPA实体。
此方案引入一个通用的User实体,其中包含一个roleType字段(如枚举类型DOCTOR, PATIENT)来区分用户类型。
综合考虑上述两种方案的优缺点,推荐一种混合模式:通用用户认证与特定角色数据分离。这种方案既能利用统一用户表简化认证,又能通过角色特有实体保持数据模型的清晰和业务逻辑的解耦。
// 1. User 实体:通用用户认证信息
import javax.persistence.*;
import lombok.Getter;
import lombok.Setter;
import lombok.NoArgsConstructor;
import lombok.AllArgsConstructor;
@Entity
@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor
@Table(name = "app_users") // 避免与数据库保留字冲突
public class User {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String username; // 用于登录的用户名
private String password; // 加密后的密码
private String name;
private String surname;
@Enumerated(EnumType.STRING)
@Column(nullable = false)
private UserType userType; // DOCTOR 或 PATIENT
// 可以添加其他通用字段,例如 email, phone 等
}
// UserType 枚举
public enum UserType {
DOCTOR,
PATIENT
}
// 2. Doctor 实体:医生特有信息和关系
import javax.persistence.*;
import lombok.Getter;
import lombok.Setter;
import java.util.HashSet;
import java.util.Set;
@Entity
@Getter
@Setter
public class Doctor {
@Id
private Long id; // 与 User 实体共享主键
@OneToOne(fetch = FetchType.LAZY)
@MapsId // 表示此实体的主键是其关联实体的主键
@JoinColumn(name = "id", nullable = false)
private User user; // 关联的 User 实体
// 医生特有属性,例如专业领域、执业证书编号等
private String specialization;
// 医生与患者的多对多关系
@ManyToMany(cascade = {CascadeType.PERSIST, CascadeType.MERGE}) // 级联操作
@JoinTable(
name = "doctor_patient", // 关系表名
joinColumns = @JoinColumn(name = "doctor_id"), // 医生表在外键中的列名
inverseJoinColumns = @JoinColumn(name = "patient_id") // 患者表在外键中的列名
)
private Set<Patient> patients = new HashSet<>();
public void addPatient(Patient patient) {
this.patients.add(patient);
patient.getDoctors().add(this);
}
public void removePatient(Patient patient) {
this.patients.remove(patient);
patient.getDoctors().remove(this);
}
}
// 3. Patient 实体:患者特有信息和关系
import javax.persistence.*;
import lombok.Getter;
import lombok.Setter;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
@Entity
@Getter
@Setter
public class Patient {
@Id
private Long id; // 与 User 实体共享主键
@OneToOne(fetch = FetchType.LAZY)
@MapsId // 表示此实体的主键是其关联实体的主键
@JoinColumn(name = "id", nullable = false)
private User user; // 关联的 User 实体
// 患者特有属性,例如病史、过敏信息等
private String medicalHistory;
// 患者与药物的多对多关系
@ManyToMany(cascade = {CascadeType.PERSIST, CascadeType.MERGE})
@JoinTable(
name = "patient_medicine",
joinColumns = @JoinColumn(name = "patient_id"),
inverseJoinColumns = @JoinColumn(name = "medicine_id")
)
private Set<Medicine> medicines = new HashSet<>();
// 患者与医生的多对多关系(通过 Doctor 实体映射)
@ManyToMany(mappedBy = "patients", fetch = FetchType.LAZY)
private Set<Doctor> doctors = new HashSet<>();
public void addMedicine(Medicine medicine) {
this.medicines.add(medicine);
medicine.getPatients().add(this);
}
public void removeMedicine(Medicine medicine) {
this.medicines.remove(medicine);
medicine.getPatients().remove(this);
}
}
// 4. Medicine 实体:药物信息
import javax.persistence.*;
import lombok.Getter;
import lombok.Setter;
import java.util.HashSet;
import java.util.Set;
@Entity
@Getter
@Setter
public class Medicine {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
private String description;
// 药物与患者的多对多关系(通过 Patient 实体映射)
@ManyToMany(mappedBy = "medicines", fetch = FetchType.LAZY)
private Set<Patient> patients = new HashSet<>();
}采用上述混合模型后,Spring Security的实现将变得更加简洁和灵活。
基于 User 实体进行认证:
// 示例:自定义 UserDetailsService
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Service;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import java.util.Collections;
@Service
public class CustomUserDetailsService implements UserDetailsService {
private final UserRepository userRepository; // 假设有一个 UserRepository
public CustomUserDetailsService(UserRepository userRepository) {
this.userRepository = userRepository;
}
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
User user = userRepository.findByUsername(username)
.orElseThrow(() -> new UsernameNotFoundException("User not found with username: " + username));
return new org.springframework.security.core.userdetails.User(
user.getUsername(),
user.getPassword(),
Collections.singletonList(new SimpleGrantedAuthority("ROLE_" + user.getUserType().name())) // 将 UserType 映射为角色
);
}
}根据用户类型进行权限控制:
// 示例:获取当前登录用户并加载其角色实体
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetails;
@Service
public class UserProfileService {
private final UserRepository userRepository;
private final DoctorRepository doctorRepository;
private final PatientRepository patientRepository;
// 构造器注入...
public Object getCurrentUserProfile() {
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
if (authentication == null || !(authentication.getPrincipal() instanceof UserDetails)) {
throw new IllegalStateException("User not authenticated.");
}
UserDetails userDetails = (UserDetails) authentication.getPrincipal();
User user = userRepository.findByUsername(userDetails.getUsername())
.orElseThrow(() -> new UsernameNotFoundException("User not found"));
if (user.getUserType() == UserType.DOCTOR) {
return doctorRepository.findById(user.getId())
.orElseThrow(() -> new RuntimeException("Doctor profile not found"));
} else if (user.getUserType() == UserType.PATIENT) {
return patientRepository.findById(user.getId())
.orElseThrow(() -> new RuntimeException("Patient profile not found"));
} else {
// 处理其他用户类型或抛出异常
return null;
}
}
}处理“医生同时也是患者”的场景:
基于上述实体结构,服务层可以清晰地进行职责划分:
这种划分使得每个服务类职责单一,代码可读性高,易于维护和扩展。
本文推荐的 Spring Boot 医生-患者关系数据模型,通过引入一个通用的 User 实体进行统一认证,并利用 @OneToOne 和 @MapsId 将 Doctor 和 Patient 等特定角色实体与 User 实体关联,有效地解决了传统方案中的痛点。
这种混合模型的主要优势在于:
在实际应用中,还需要考虑事务管理、错误处理、API设计等多个方面,但一个健壮、清晰的数据模型是构建高效、可维护系统的基石。
以上就是Spring Boot中医生-患者关系与权限管理实践的详细内容,更多请关注php中文网其它相关文章!
每个人都需要一台速度更快、更稳定的 PC。随着时间的推移,垃圾文件、旧注册表数据和不必要的后台进程会占用资源并降低性能。幸运的是,许多工具可以让 Windows 保持平稳运行。
Copyright 2014-2025 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号