You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Spring Boot多角色对象的数据库设计与认证逻辑处理咨询

解决方案:多角色用户的数据表设计与Spring Boot登录实现

一、数据表设计方案(主从表结构)

采用公共认证信息主表+角色专属信息子表的结构,既保证认证逻辑统一,又能灵活存储各角色的专属数据,避免单表冗余。

1. 主表:user(存储所有角色的公共认证信息)

字段名类型说明
idBIGINT主键,自增
emailVARCHAR(100)登录账号,唯一非空
passwordVARCHAR(255)加密后的密码,非空
otpVARCHAR(10)一次性验证码(可选)
roleVARCHAR(20)角色枚举(PATIENT/DOCTOR/LAB_TECH/RECEPTIONIST/CASHIER/ADMIN)
enabledBOOLEAN账号是否启用,默认true
create_timeDATETIME创建时间
update_timeDATETIME更新时间

2. 角色子表(存储各角色专属信息)

每个角色对应一张子表,通过id与user表主键一对一关联(也可单独用user_id作为外键):

  • patient表:id(外键关联user.id)、patient_no(患者编号)、medical_history(病史)、allergy_info(过敏史)等
  • doctor表:id、doctor_no(医生编号)、specialty(专业方向)、license_no(执业证号)等
  • lab_tech/receptionist/cashier表:同理,添加各自业务所需的专属字段

二、Spring Boot实体类与Repository设计

1. 角色枚举类

public enum Role {
    PATIENT, DOCTOR, LAB_TECH, RECEPTIONIST, CASHIER, ADMIN
}

2. 主实体User

@Entity
@Table(name = "user")
public class User {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    @Column(unique = true, nullable = false)
    private String email;

    @Column(nullable = false)
    private String password;

    private String otp;

    @Enumerated(EnumType.STRING)
    private Role role;

    private boolean enabled;

    @Column(name = "create_time")
    private LocalDateTime createTime;

    @Column(name = "update_time")
    private LocalDateTime updateTime;

    // 一对一关联各角色子表(按需添加)
    @OneToOne(mappedBy = "user", cascade = CascadeType.ALL, fetch = FetchType.LAZY)
    private Patient patient;

    @OneToOne(mappedBy = "user", cascade = CascadeType.ALL, fetch = FetchType.LAZY)
    private Doctor doctor;

    // Getter、Setter、构造方法
}

3. 角色子实体(以Patient为例)

@Entity
@Table(name = "patient")
public class Patient {
    @Id
    private Long id; // 与User表id完全一致

    @OneToOne
    @MapsId // 映射User的主键作为当前实体主键
    @JoinColumn(name = "id")
    private User user;

    @Column(name = "patient_no")
    private String patientNo;

    @Column(name = "medical_history")
    private String medicalHistory;

    @Column(name = "allergy_info")
    private String allergyInfo;

    // Getter、Setter
}

4. Repository层

  • 公共UserRepository:负责登录时的用户查询
public interface UserRepository extends JpaRepository<User, Long> {
    Optional<User> findByEmail(String email);
}
  • 角色专属Repository(以PatientRepository为例):
public interface PatientRepository extends JpaRepository<Patient, Long> {
}

其他角色的Repository同理创建即可。

三、登录流程与角色识别实现

1. 核心登录逻辑(AuthService)

@Service
public class AuthService {
    @Autowired
    private UserRepository userRepository;

    @Autowired
    private PatientRepository patientRepository;

    @Autowired
    private DoctorRepository doctorRepository;

    @Autowired
    private PasswordEncoder passwordEncoder;

    @Autowired
    private TokenRepository tokenRepository;

    public AuthResponse login(LoginRequest request) {
        // 1. 根据邮箱查询用户
        User user = userRepository.findByEmail(request.getEmail())
                .orElseThrow(() -> new RuntimeException("账号不存在"));

        // 2. 验证密码
        if (!passwordEncoder.matches(request.getPassword(), user.getPassword())) {
            throw new RuntimeException("密码错误");
        }

        // 3. 验证OTP(如果业务需要)
        if (StringUtils.hasText(user.getOtp()) && !user.getOtp().equals(request.getOtp())) {
            throw new RuntimeException("OTP验证码错误");
        }

        // 4. 根据角色获取专属信息
        Object roleDetail = null;
        switch (user.getRole()) {
            case PATIENT:
                roleDetail = patientRepository.findById(user.getId()).orElse(null);
                break;
            case DOCTOR:
                roleDetail = doctorRepository.findById(user.getId()).orElse(null);
                break;
            case LAB_TECH:
                // 调用LabTechRepository查询
                break;
            case RECEPTIONIST:
                // 调用ReceptionistRepository查询
                break;
            case CASHIER:
                // 调用CashierRepository查询
                break;
            case ADMIN:
                // 管理员无专属子表则无需查询
                break;
        }

        // 5. 生成登录Token(以JWT为例)
        String token = JwtUtils.generateToken(user.getId(), user.getRole().name());

        // 6. 保存Token到token表(会话管理)
        Token tokenEntity = new Token();
        tokenEntity.setToken(token);
        tokenEntity.setUserId(user.getId());
        tokenEntity.setExpireTime(LocalDateTime.now().plusHours(24));
        tokenEntity.setValid(true);
        tokenRepository.save(tokenEntity);

        // 7. 返回登录响应
        return new AuthResponse(user.getId(), user.getEmail(), user.getRole(), roleDetail, token);
    }
}

2. Spring Security整合(可选)

如果用Spring Security做认证,自定义UserDetailsService实现角色加载:

@Service
public class CustomUserDetailsService implements UserDetailsService {
    @Autowired
    private UserRepository userRepository;

    @Override
    public UserDetails loadUserByUsername(String email) throws UsernameNotFoundException {
        User user = userRepository.findByEmail(email)
                .orElseThrow(() -> new UsernameNotFoundException("账号不存在"));

        return User.builder()
                .username(user.getEmail())
                .password(user.getPassword())
                .roles(user.getRole().name())
                .accountEnabled(user.isEnabled())
                .build();
    }
}

四、方案优势

  1. 逻辑统一:所有角色的认证流程复用user表,无需重复开发登录逻辑
  2. 扩展性强:新增角色只需添加对应子表和实体,不影响现有结构
  3. 数据整洁:避免单表冗余字段,各角色专属数据独立存储
  4. 性能友好:登录时仅查询user表,专属数据按需加载,减少IO开销

内容的提问来源于stack exchange,提问作者TunazZz

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.17 05:43:16