0

0

Spring Boot医患关系管理系统:灵活的数据模型与权限设计

DDD

DDD

发布时间:2025-08-15 16:48:21

|

459人浏览过

|

来源于php中文网

原创

Spring Boot医患关系管理系统:灵活的数据模型与权限设计

本文深入探讨了在Spring Boot中构建医患关系管理系统的核心挑战,包括复杂的用户角色(医生与患者)、多对多关系以及基于角色的安全认证与授权。通过分析常见的两种数据模型方案,文章推荐了一种结合通用用户实体与特定角色实体的混合设计,并详细阐述了其实现细节,包括实体关系映射、代码示例及Spring Security集成策略,旨在提供一套灵活且可扩展的解决方案。

系统需求与挑战概述

构建一个医患关系管理系统,核心需求包括:

  1. 用户管理: 区分医生和患者两种用户类型。
  2. 关系管理: 医生可以关联多个患者,患者也可以关联多个医生(多对多关系)。
  3. 特定功能: 患者能够添加和管理自己的用药信息。
  4. 安全认证与授权: 实现用户登录注册,并根据用户角色(医生/患者)控制访问权限。

在设计数据模型时,如何有效地表示医生和患者,并处理其特有属性及关系,同时兼顾安全体系的集成,是关键的挑战。

常见数据模型方案探讨

在系统设计初期,通常会考虑以下两种主要的数据模型方案:

方案一:独立实体与多对多关联

这种方案将医生(Doctor)和患者(Patient)分别设计为独立的实体类,并通过 @ManyToMany 注解建立它们之间的关联。

优点: 实体职责明确,各司其职。 缺点:

  • 安全管理复杂: 对于登录和注册功能,需要针对 Doctor 和 Patient 分别实现用户认证逻辑,可能导致代码重复或逻辑分散。
  • 公共属性冗余: 医生和患者可能存在姓名、联系方式等公共属性,在两个独立实体中会造成数据冗余。

概念代码示例:

// Doctor.java (简化版)
@Entity
@Getter @Setter
public class Doctor {
    @Id @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    private String name;
    private String surname;

    @ManyToMany
    @JoinTable(name = "doctor_patient",
               joinColumns = @JoinColumn(name = "doctor_id"),
               inverseJoinColumns = @JoinColumn(name = "patient_id"))
    private Set patients = new HashSet<>();
}

// Patient.java (简化版)
@Entity
@Getter @Setter
public class Patient {
    @Id @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    private String name;
    private String surname;

    @OneToMany(mappedBy = "patient", cascade = CascadeType.PERSIST)
    private List medicineList = new ArrayList<>();

    @ManyToMany(mappedBy = "patients")
    private Set doctors = new HashSet<>();
}

方案二:单一用户表与角色区分

此方案将医生和患者抽象为统一的 User 实体,通过一个 roleType 字段来区分其身份。

优点:

  • 统一认证: 简化了用户登录注册流程,所有用户都通过 User 表进行认证。
  • 简化权限管理: 可以基于 roleType 字段直接进行权限控制。

缺点:

  • 数据稀疏: User 实体中会包含所有角色特有的字段(例如,medicineList 仅适用于患者),导致大量 null 值,造成数据稀疏和模型不清晰。
  • 业务逻辑耦合: 在服务层和控制器层,需要通过 roleType 进行大量条件判断,以处理不同角色的特定业务逻辑,增加了代码的复杂性和维护难度。

概念代码示例:

// User.java (简化版)
@Entity
@Table(name = "MYUSERS")
@Getter @Setter
public class User {
    @Id @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    private String name;
    private String surname;

    @Enumerated(EnumType.STRING)
    @Column(nullable = false)
    private RoleType roleType; // DOCTOR, PATIENT

    // 仅针对患者:
    @OneToMany(mappedBy = "patient", cascade = CascadeType.PERSIST)
    private List medicineList = new ArrayList<>();

    // 多对多关系(可能需要自关联或复杂逻辑)
    // @ManyToMany with other Users (Doctors/Patients)
}

推荐的数据模型设计:混合方案

综合以上两种方案的优缺点,推荐采用一种混合模型:将用户共性属性抽取到独立的 User 实体中,而将医生和患者的特有属性及关系分别定义在 Doctor 和 Patient 实体中。Doctor 和 Patient 实体通过 @OneToOne 关系与 User 实体关联。

核心思想

  • User 实体: 负责存储所有用户的通用信息,如ID、姓名、姓氏、登录凭证(密码、用户名等)以及一个标识用户类型的字段(可选,但推荐用于快速区分)。
  • Doctor 实体: 存储医生特有的信息,并与 User 实体建立一对一关系。它负责管理医生与患者之间的多对多关系。
  • Patient 实体: 存储患者特有的信息,同样与 User 实体建立一对一关系。它负责管理患者的用药信息以及与医生的多对多关系。
  • Medicine 实体: 存储药品信息,并与 Patient 实体建立多对多关系。

这种设计既实现了统一的用户认证,又保持了特定角色实体的清晰职责,避免了数据稀疏和业务逻辑的过度耦合。

详细实体类实现

1. User 实体: 基础用户信息,用于认证。

盛世企业网站管理系统1.1.2
盛世企业网站管理系统1.1.2

免费 盛世企业网站管理系统(SnSee)系统完全免费使用,无任何功能模块使用限制,在使用过程中如遇到相关问题可以去官方论坛参与讨论。开源 系统Web代码完全开源,在您使用过程中可以根据自已实际情况加以调整或修改,完全可以满足您的需求。强大且灵活 独创的多语言功能,可以直接在后台自由设定语言版本,其语言版本不限数量,可根据自已需要进行任意设置;系统各模块可在后台自由设置及开启;强大且适用的后台管理支

下载
package com.example.model;

import lombok.Getter;
import lombok.Setter;
import lombok.NoArgsConstructor;
import lombok.AllArgsConstructor;

import javax.persistence.*;

@Entity
@Table(name = "users") // 推荐使用更通用的表名
@Getter @Setter
@NoArgsConstructor
@AllArgsConstructor
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; // DOCTOR, PATIENT

    // 可以添加其他通用字段,如电话、地址等
}

2. UserType 枚举: 定义用户类型。

package com.example.model;

public enum UserType {
    DOCTOR,
    PATIENT
}

3. Doctor 实体: 医生特有信息及关系。

package com.example.model;

import lombok.Getter;
import lombok.Setter;
import lombok.NoArgsConstructor;
import lombok.AllArgsConstructor;

import javax.persistence.*;
import java.util.HashSet;
import java.util.Set;

@Entity
@Getter @Setter
@NoArgsConstructor
@AllArgsConstructor
public class Doctor {

    @Id
    private Long id; // 与User的ID共享

    @OneToOne
    @MapsId // 表示此实体的主键也是其关联User实体的主键
    @JoinColumn(name = "id", nullable = false) // 外键列名为id,指向User的id
    private User user;

    // 医生特有属性,例如:专业领域、执业证书编号等
    private String specialty;

    @ManyToMany(cascade = {CascadeType.PERSIST, CascadeType.MERGE}) // 医生和患者的多对多关系
    @JoinTable(
        name = "doctor_patients", // 关联表名
        joinColumns = @JoinColumn(name = "doctor_id"), // 本实体在关联表中的外键
        inverseJoinColumns = @JoinColumn(name = "patient_id") // 对方实体在关联表中的外键
    )
    private Set 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); // 维护双向关系
    }
}

4. Patient 实体: 患者特有信息及关系。

package com.example.model;

import lombok.Getter;
import lombok.Setter;
import lombok.NoArgsConstructor;
import lombok.AllArgsConstructor;

import javax.persistence.*;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

@Entity
@Getter @Setter
@NoArgsConstructor
@AllArgsConstructor
public class Patient {

    @Id
    private Long id; // 与User的ID共享

    @OneToOne
    @MapsId // 表示此实体的主键也是其关联User实体的主键
    @JoinColumn(name = "id", nullable = false) // 外键列名为id,指向User的id
    private User user;

    // 患者特有属性,例如:病史、过敏信息等
    private String medicalHistory;

    @OneToMany(mappedBy = "patient", cascade = CascadeType.ALL, orphanRemoval = true) // 患者与药品清单的一对多关系
    private List medicineList = new ArrayList<>();

    @ManyToMany(mappedBy = "patients", fetch = FetchType.LAZY) // 患者与医生的多对多关系,由Doctor维护
    private Set doctors = new HashSet<>();

    public void addMedicine(Medicine medicine) {
        this.medicineList.add(medicine);
        medicine.setPatient(this); // 维护双向关系
    }

    public void removeMedicine(Medicine medicine) {
        this.medicineList.remove(medicine);
        medicine.setPatient(null); // 维护双向关系
    }
}

5. Medicine 实体: 药品信息。

package com.example.model;

import lombok.Getter;
import lombok.Setter;
import lombok.NoArgsConstructor;
import lombok.AllArgsConstructor;

import javax.persistence.*;

@Entity
@Getter @Setter
@NoArgsConstructor
@AllArgsConstructor
public class Medicine {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    private String name;
    private String dosage; // 剂量
    private String frequency; // 频率

    @ManyToOne(fetch = FetchType.LAZY) // 药品与患者的多对一关系
    @JoinColumn(name = "patient_id", nullable = false)
    private Patient patient;
}

注意:

  • @MapsId 注解使得 Doctor 和 Patient 的主键与其关联的 User 实体的主键相同,实现了共享主键的 @OneToOne 关系。
  • @JoinTable 用于定义 Doctor 和 Patient 之间的多对多关系。
  • cascade 类型需要根据业务需求谨慎选择,CascadeType.ALL 表示所有持久化操作(保存、更新、删除等)都会级联到关联实体。

安全与权限管理

在 Spring Boot 中,结合 Spring Security 可以轻松实现基于角色的认证与授权。

1. 用户认证

  • UserDetailsService 实现: 创建一个自定义的 UserDetailsService,用于从 User 实体中加载用户信息。

    package com.example.security;
    
    import com.example.model.User;
    import com.example.repository.UserRepository;
    import org.springframework.security.core.GrantedAuthority;
    import org.springframework.security.core.authority.SimpleGrantedAuthority;
    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 java.util.Collections;
    import java.util.List;
    
    @Service
    public class CustomUserDetailsService implements UserDetailsService {
    
        private final 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));
    
            List authorities = Collections.singletonList(
                new SimpleGrantedAuthority("ROLE_" + user.getUserType().name()) // 将UserType映射为Spring Security的角色
            );
    
            return new org.springframework.security.core.userdetails.User(
                    user.getUsername(),
                    user.getPassword(),
                    authorities
            );
        }
    }
  • UserRepository 接口:

    package com.example.repository;
    
    import com.example.model.User;
    import org.springframework.data.jpa.repository.JpaRepository;
    import java.util.Optional;
    
    public interface UserRepository extends JpaRepository {
        Optional findByUsername(String username);
    }

2. 权限授权

  • Spring Security 配置: 在 WebSecurityConfig 中配置 URL 访问权限。

    package com.example.security;
    
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
    import org.springframework.security.config.annotation.web.builders.HttpSecurity;
    import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
    import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
    import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
    import org.springframework.security.crypto.password.PasswordEncoder;
    
    @Configuration
    @EnableWebSecurity
    public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
    
        private final CustomUserDetailsService customUserDetailsService;
    
        public WebSecurityConfig(CustomUserDetailsService customUserDetailsService) {
            this.customUserDetailsService = customUserDetailsService;
        }
    
        @Override
        protected void configure(AuthenticationManagerBuilder auth) throws Exception {
            auth.userDetailsService(customUserDetailsService).passwordEncoder(passwordEncoder());
        }
    
        @Override
        protected void configure(HttpSecurity http) throws Exception {
            http
                .csrf().disable() // 实际生产环境应启用CSRF防护
                .authorizeRequests()
                    .antMatchers("/register", "/login").permitAll() // 注册和登录接口允许所有人访问
                    .antMatchers("/api/doctors/**").hasRole("DOCTOR") // 医生相关接口仅限DOCTOR角色访问
                    .antMatchers("/api/patients/**").hasRole("PATIENT") // 患者相关接口仅限PATIENT角色访问
                    .antMatchers("/api/medicines/**").hasRole("PATIENT") // 药品相关接口仅限PATIENT角色访问
                    .anyRequest().authenticated() // 其他所有请求需要认证
                .and()
                .formLogin() // 或.httpBasic()
                .and()
                .logout();
        }
    
        @Bean
        public PasswordEncoder passwordEncoder() {
            return new BCryptPasswordEncoder();
        }
    }
  • 控制器层授权: 在控制器或服务层方法上使用 @PreAuthorize 注解进行更细粒度的控制。

    package com.example.controller;
    
    import com.example.model.User;
    import com.example.model.UserType;
    import com.example.repository.UserRepository;
    import com.example.service.DoctorService;
    import com.example.service.PatientService;
    import org.springframework.security.access.prepost.PreAuthorize;
    import org.springframework.security.core.Authentication;
    import org.springframework.security.core.context.SecurityContextHolder;
    import org.springframework.web.bind.annotation.*;
    
    @RestController
    @RequestMapping("/api")
    public class MyController {
    
        private final UserRepository userRepository;
        private final DoctorService doctorService;
        private final PatientService patientService;
    
        public MyController(UserRepository userRepository, DoctorService doctorService, PatientService patientService) {
            this.userRepository = userRepository;
            this.doctorService = doctorService;
            this.patientService = patientService;
        }
    
        // 获取当前登录用户ID和类型
        private User getCurrentUser() {
            Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
            String username = authentication.getName();

热门AI工具

更多
DeepSeek
DeepSeek

幻方量化公司旗下的开源大模型平台

豆包大模型
豆包大模型

字节跳动自主研发的一系列大型语言模型

通义千问
通义千问

阿里巴巴推出的全能AI助手

腾讯元宝
腾讯元宝

腾讯混元平台推出的AI助手

文心一言
文心一言

文心一言是百度开发的AI聊天机器人,通过对话可以生成各种形式的内容。

讯飞写作
讯飞写作

基于讯飞星火大模型的AI写作工具,可以快速生成新闻稿件、品宣文案、工作总结、心得体会等各种文文稿

即梦AI
即梦AI

一站式AI创作平台,免费AI图片和视频生成。

ChatGPT
ChatGPT

最最强大的AI聊天机器人程序,ChatGPT不单是聊天机器人,还能进行撰写邮件、视频脚本、文案、翻译、代码等任务。

相关专题

更多
spring框架介绍
spring框架介绍

本专题整合了spring框架相关内容,想了解更多详细内容,请阅读专题下面的文章。

114

2025.08.06

Java Spring Security 与认证授权
Java Spring Security 与认证授权

本专题系统讲解 Java Spring Security 框架在认证与授权中的应用,涵盖用户身份验证、权限控制、JWT与OAuth2实现、跨站请求伪造(CSRF)防护、会话管理与安全漏洞防范。通过实际项目案例,帮助学习者掌握如何 使用 Spring Security 实现高安全性认证与授权机制,提升 Web 应用的安全性与用户数据保护。

29

2026.01.26

spring boot框架优点
spring boot框架优点

spring boot框架的优点有简化配置、快速开发、内嵌服务器、微服务支持、自动化测试和生态系统支持。本专题为大家提供spring boot相关的文章、下载、课程内容,供大家免费下载体验。

135

2023.09.05

spring框架有哪些
spring框架有哪些

spring框架有Spring Core、Spring MVC、Spring Data、Spring Security、Spring AOP和Spring Boot。详细介绍:1、Spring Core,通过将对象的创建和依赖关系的管理交给容器来实现,从而降低了组件之间的耦合度;2、Spring MVC,提供基于模型-视图-控制器的架构,用于开发灵活和可扩展的Web应用程序等。

390

2023.10.12

Java Spring Boot开发
Java Spring Boot开发

本专题围绕 Java 主流开发框架 Spring Boot 展开,系统讲解依赖注入、配置管理、数据访问、RESTful API、微服务架构与安全认证等核心知识,并通过电商平台、博客系统与企业管理系统等项目实战,帮助学员掌握使用 Spring Boot 快速开发高效、稳定的企业级应用。

70

2025.08.19

Java Spring Boot 4更新教程_Java Spring Boot 4有哪些新特性
Java Spring Boot 4更新教程_Java Spring Boot 4有哪些新特性

Spring Boot 是一个基于 Spring 框架的 Java 开发框架,它通过 约定优于配置的原则,大幅简化了 Spring 应用的初始搭建、配置和开发过程,让开发者可以快速构建独立的、生产级别的 Spring 应用,无需繁琐的样板配置,通常集成嵌入式服务器(如 Tomcat),提供“开箱即用”的体验,是构建微服务和 Web 应用的流行工具。

34

2025.12.22

Java Spring Boot 微服务实战
Java Spring Boot 微服务实战

本专题深入讲解 Java Spring Boot 在微服务架构中的应用,内容涵盖服务注册与发现、REST API开发、配置中心、负载均衡、熔断与限流、日志与监控。通过实际项目案例(如电商订单系统),帮助开发者掌握 从单体应用迁移到高可用微服务系统的完整流程与实战能力。

135

2025.12.24

c语言中null和NULL的区别
c语言中null和NULL的区别

c语言中null和NULL的区别是:null是C语言中的一个宏定义,通常用来表示一个空指针,可以用于初始化指针变量,或者在条件语句中判断指针是否为空;NULL是C语言中的一个预定义常量,通常用来表示一个空值,用于表示一个空的指针、空的指针数组或者空的结构体指针。

236

2023.09.22

俄罗斯Yandex引擎入口
俄罗斯Yandex引擎入口

2026年俄罗斯Yandex搜索引擎最新入口汇总,涵盖免登录、多语言支持、无广告视频播放及本地化服务等核心功能。阅读专题下面的文章了解更多详细内容。

158

2026.01.28

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
SQL 教程
SQL 教程

共61课时 | 3.6万人学习

10分钟--Midjourney创作自己的漫画
10分钟--Midjourney创作自己的漫画

共1课时 | 0.1万人学习

Midjourney 关键词系列整合
Midjourney 关键词系列整合

共13课时 | 0.9万人学习

关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号 技术交流群
PHP中文网订阅号
每天精选资源文章推送

Copyright 2014-2026 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号