
在开发医疗管理系统时,如何高效地建模医生与患者之间的复杂关系,并在此基础上集成灵活的用户认证与授权机制,是常见的挑战。传统的解决方案往往面临两个主要困境:一是为医生和患者创建完全独立的实体,这导致在用户登录和权限管理时逻辑复杂;二是将所有属性集中到一个用户实体中,通过角色区分,但这可能导致大量空字段和不必要的业务逻辑分支。本教程将介绍一种更优的数据模型设计,并演示如何与spring security集成,以构建一个既清晰又功能强大的医生-患者关系管理系统。
为了解决上述问题,我们采用一种混合模型:一个通用的User实体来存储所有用户的通用信息(如登录凭据、姓名),以及独立的Doctor和Patient实体来存储各自特有的属性和关系。这种设计通过@OneToOne和@MapsId注解将特定角色实体与User实体关联起来,确保了数据的一致性和模型的灵活性。
User实体作为所有用户的基类,包含通用的认证信息和基本身份属性。
import javax.persistence.*;
import lombok.Getter;
import lombok.Setter;
@Entity
@Table(name = "app_users") // 避免与数据库保留字冲突
@Getter
@Setter
public class User {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Column(unique = true, nullable = false)
private String username; // 用户名,用于登录
@Column(nullable = false)
private String password; // 密码,应加密存储
private String name;
private String surname;
@Enumerated(EnumType.STRING)
@Column(nullable = false)
private UserType userType; // 用户类型,如医生、患者
}// UserType 枚举定义
public enum UserType {
DOCTOR,
PATIENT,
ADMIN // 可根据需求扩展
}Doctor实体存储医生特有的信息,并通过@MapsId与User实体建立一对一关联。医生与患者之间存在多对多关系。
import javax.persistence.*;
import java.util.HashSet;
import java.util.Set;
import lombok.Getter;
import lombok.Setter;
@Entity
@Getter
@Setter
public class Doctor {
@Id
private Long id; // 与User的ID保持一致
@OneToOne
@MapsId // 表示Doctor的主键也是其关联User的外键
@JoinColumn(name = "id") // 外键列名
private User user;
@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);
}
}Patient实体存储患者特有的信息,同样通过@MapsId与User实体关联。患者可以拥有多位医生,并记录所服用的药物信息。
import javax.persistence.*;
import java.util.HashSet;
import java.util.Set;
import lombok.Getter;
import lombok.Setter;
@Entity
@Getter
@Setter
public class Patient {
@Id
private Long id; // 与User的ID保持一致
@OneToOne
@MapsId
@JoinColumn(name = "id")
private User user;
@ManyToMany(mappedBy = "patients", fetch = FetchType.LAZY) // 由Doctor实体中的patients字段映射
private Set<Doctor> doctors = new HashSet<>();
@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<>();
// 辅助方法,用于维护双向关系
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);
}
}Medicine实体存储药物的基本信息,与Patient实体存在多对多关系。
import javax.persistence.*;
import java.util.HashSet;
import java.util.Set;
import lombok.Getter;
import lombok.Setter;
@Entity
@Getter
@Setter
public class Medicine {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
@ManyToMany(mappedBy = "medicines", fetch = FetchType.LAZY) // 由Patient实体中的medicines字段映射
private Set<Patient> patients = new HashSet<>();
}这种数据模型为Spring Security的集成提供了清晰的路径。User实体将作为Spring Security进行认证的基石,而UserType则用于授权和区分不同角色的业务逻辑。
UserDetailsService是Spring Security的核心接口,用于加载用户详情。我们将通过username从User实体中加载用户信息,并根据UserType赋予相应的角色权限。
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.stereotype.Service;
import java.util.Collections;
import java.util.List;
import java.util.stream.Collectors;
@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("用户未找到: " + username));
// 根据UserType构建Spring Security的权限列表
List<SimpleGrantedAuthority> authorities = Collections.singletonList(
new SimpleGrantedAuthority("ROLE_" + user.getUserType().name())
);
// 返回Spring Security的User对象
return new org.springframework.security.core.userdetails.User(
user.getUsername(),
user.getPassword(), // 密码应为加密后的形式
authorities
);
}
}注意: UserRepository需要您自行定义,例如:public interface UserRepository extends JpaRepository<User, Long> { Optional<User> findByUsername(String username); }。同时,在实际应用中,User实体中的password字段应存储加密后的密码,并在注册用户时使用PasswordEncoder进行编码。
一旦用户通过认证,其UserType(通过ROLE_UserType形式的权限)就可以用于授权。您可以在控制器或服务层根据当前用户的角色来执行不同的业务逻辑,或者使用Spring Security的注解进行方法级别的权限控制。
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Service;
import java.util.Optional;
@Service
public class HealthCareService {
private final UserRepository userRepository;
private final DoctorRepository doctorRepository; // 假设有DoctorRepository
private final PatientRepository patientRepository; // 假设有PatientRepository
public HealthCareService(UserRepository userRepository, DoctorRepository doctorRepository, PatientRepository patientRepository) {
this.userRepository = userRepository;
this.doctorRepository = doctorRepository;
this.patientRepository = patientRepository;
}
// 医生特有操作:查看其所有患者
@PreAuthorize以上就是Spring Boot中医生-患者关系的高效数据模型与安全实践的详细内容,更多请关注php中文网其它相关文章!
每个人都需要一台速度更快、更稳定的 PC。随着时间的推移,垃圾文件、旧注册表数据和不必要的后台进程会占用资源并降低性能。幸运的是,许多工具可以让 Windows 保持平稳运行。
Copyright 2014-2025 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号