基于Rails Devise实现患者与医生双角色独立注册表单需求
我来帮你搞定这个医生预约平台的角色区分和注册表单需求,一步步来:
1. 扩展User模型,添加角色标识与医生专属字段
首先得给User模型加上角色区分,以及医生需要的额外字段。
- 生成迁移文件:
rails generate migration AddRoleAndDoctorFieldsToUsers role:string specialty:text license_number:string rails db:migrate
- 修改User模型,添加验证和角色判断方法:
# app/models/user.rb class User < ApplicationRecord devise :database_authenticatable, :registerable, :recoverable, :rememberable, :validatable # 定义允许的角色类型,避免非法值 ROLES = %w[patient doctor].freeze validates :role, presence: true, inclusion: { in: ROLES } # 只有医生角色需要验证专属字段 validates :specialty, :license_number, presence: true, if: :doctor? def patient? role == 'patient' end def doctor? role == 'doctor' end end
2. 配置Devise,实现两套独立注册表单
默认的Devise注册流程是单表单,我们需要自定义注册控制器和视图,区分患者和医生注册。
- 生成自定义的Devise注册控制器:
rails generate devise:controllers users -c registrations
- 修改路由,指定使用自定义控制器,并添加独立的注册入口:
# config/routes.rb Rails.application.routes.draw do devise_for :users, controllers: { registrations: 'users/registrations' } # 给患者和医生分别设置专属注册页面路由 get '/register/patient', to: 'users/registrations#new_patient', as: :new_patient_registration get '/register/doctor', to: 'users/registrations#new_doctor', as: :new_doctor_registration end
- 修改自定义注册控制器,添加两个注册页面的逻辑,并允许接收额外参数:
# app/controllers/users/registrations_controller.rb class Users::RegistrationsController < Devise::RegistrationsController before_action :configure_sign_up_params, only: [:create] # 患者注册页面:默认设置role为patient def new_patient build_resource({ role: 'patient' }) respond_with resource end # 医生注册页面:默认设置role为doctor def new_doctor build_resource({ role: 'doctor' }) respond_with resource end protected # 允许Devise接收role和医生专属字段 def configure_sign_up_params devise_parameter_sanitizer.permit(:sign_up, keys: [:role, :specialty, :license_number]) end end
创建两套注册视图:
先把Devise默认的注册视图复制到app/views/users/registrations/(可以通过rails generate devise:views生成默认视图,再复制修改),然后新建两个专属视图:患者注册视图(
new_patient.html.erb):
<h2>患者注册</h2> <%= form_for(resource, as: resource_name, url: registration_path(resource_name)) do |f| %> <%= render "devise/shared/error_messages", resource: resource %> <%# 隐藏字段自动设置角色为patient %> <%= f.hidden_field :role, value: 'patient' %> <div class="field"> <%= f.label :email %><br /> <%= f.email_field :email, autofocus: true, autocomplete: "email" %> </div> <div class="field"> <%= f.label :password %> <% if @minimum_password_length %> <em>(<%= @minimum_password_length %> characters minimum)</em> <% end %><br /> <%= f.password_field :password, autocomplete: "new-password" %> </div> <div class="field"> <%= f.label :password_confirmation %><br /> <%= f.password_field :password_confirmation, autocomplete: "new-password" %> </div> <div class="actions"> <%= f.submit "注册为患者" %> </div> <% end %> <%= render "devise/shared/links" %>
医生注册视图(new_doctor.html.erb),额外添加医生专属字段:
<h2>医生注册</h2> <%= form_for(resource, as: resource_name, url: registration_path(resource_name)) do |f| %> <%= render "devise/shared/error_messages", resource: resource %> <%# 隐藏字段自动设置角色为doctor %> <%= f.hidden_field :role, value: 'doctor' %> <div class="field"> <%= f.label :email %><br /> <%= f.email_field :email, autofocus: true, autocomplete: "email" %> </div> <div class="field"> <%= f.label :password %> <% if @minimum_password_length %> <em>(<%= @minimum_password_length %> characters minimum)</em> <% end %><br /> <%= f.password_field :password, autocomplete: "new-password" %> </div> <div class="field"> <%= f.label :password_confirmation %><br /> <%= f.password_field :password_confirmation, autocomplete: "new-password" %> </div> <%# 医生专属字段 %> <div class="field"> <%= f.label :specialty %><br /> <%= f.text_area :specialty, placeholder: "请填写你的专科领域" %> </div> <div class="field"> <%= f.label :license_number %><br /> <%= f.text_field :license_number, placeholder: "请填写你的执业证号" %> </div> <div class="actions"> <%= f.submit "注册为医生" %> </div> <% end %> <%= render "devise/shared/links" %>
3. 实现医生角色的预约功能
接下来要给医生角色关联预约功能,需要创建Appointment模型并建立关联。
- 生成Appointment模型,关联患者和医生(都是User模型):
rails generate model Appointment patient:references doctor:references appointment_date:datetime status:string rails db:migrate
- 修改Appointment模型:
# app/models/appointment.rb class Appointment < ApplicationRecord belongs_to :patient, class_name: 'User', foreign_key: 'patient_id' belongs_to :doctor, class_name: 'User', foreign_key: 'doctor_id' validates :appointment_date, presence: true # 预约状态默认pending,可选值:pending(待确认)、confirmed(已确认)、canceled(已取消) validates :status, inclusion: { in: %w[pending confirmed canceled] }, default: 'pending' end
- 在User模型中添加预约关联:
# app/models/user.rb class User < ApplicationRecord # ... 之前的代码 ... # 患者拥有的预约(作为预约发起方) has_many :patient_appointments, class_name: 'Appointment', foreign_key: 'patient_id', dependent: :destroy # 医生收到的预约(作为预约接收方) has_many :doctor_appointments, class_name: 'Appointment', foreign_key: 'doctor_id', dependent: :destroy end
- 创建Appointments控制器,处理预约的创建、查看和状态更新:
# app/controllers/appointments_controller.rb class AppointmentsController < ApplicationController before_action :authenticate_user! # 根据当前用户角色显示对应预约列表 def index if current_user.doctor? @appointments = current_user.doctor_appointments.order(appointment_date: :asc) elsif current_user.patient? @appointments = current_user.patient_appointments.order(appointment_date: :asc) else redirect_to root_path, alert: '无权限访问此页面' end end # 患者创建预约页面 def new if current_user.patient? @appointment = current_user.patient_appointments.new @doctors = User.where(role: 'doctor') # 列出所有医生供选择 else redirect_to root_path, alert: '仅患者可发起预约' end end # 提交预约申请 def create if current_user.patient? @appointment = current_user.patient_appointments.new(appointment_params) if @appointment.save redirect_to appointments_path, notice: '预约申请已提交,请等待医生确认' else @doctors = User.where(role: 'doctor') render :new end else redirect_to root_path, alert: '仅患者可发起预约' end end # 医生更新预约状态 def update @appointment = Appointment.find(params[:id]) # 只有预约对应的医生才能修改状态 if current_user.doctor? && @appointment.doctor == current_user if @appointment.update(appointment_status_params) redirect_to appointments_path, notice: '预约状态已更新' else redirect_to appointments_path, alert: '状态更新失败,请重试' end else redirect_to root_path, alert: '无权限修改此预约' end end private def appointment_params params.require(:appointment).permit(:doctor_id, :appointment_date) end def appointment_status_params params.require(:appointment).permit(:status) end end
- 添加预约相关路由:
# config/routes.rb Rails.application.routes.draw do # ... 之前的路由 ... resources :appointments, only: [:index, :new, :create, :update] end
4. 测试验证
现在可以测试整个流程:
- 访问
/register/patient注册患者账号,查看是否能正常创建,且role自动设为patient - 访问
/register/doctor注册医生账号,填写专科和执业证号,验证字段必填规则 - 患者登录后,进入
/appointments/new选择医生和预约时间,提交预约申请 - 医生登录后,进入
/appointments查看收到的预约,更新状态为确认或取消
内容的提问来源于stack exchange,提问作者xbrain88
相关产品推荐
相关产品推荐

