2020-11-23 15:35:03 +08:00
# == Schema Information
#
# Table name: users
#
2020-12-23 15:48:07 +08:00
# id :integer not null, primary key
# login :string(255) default(""), not null
# hashed_password :string(40) default(""), not null
# firstname :string(30) default(""), not null
# lastname :string(255) default(""), not null
# mail :string(60)
# admin :boolean default("0"), not null
# status :integer default("1"), not null
# last_login_on :datetime
# language :string(5) default("")
# auth_source_id :integer
# created_on :datetime
# updated_on :datetime
# type :string(255)
# identity_url :string(255)
# mail_notification :string(255) default(""), not null
# salt :string(64)
# gid :integer
# visits :integer default("0")
# excellent_teacher :integer default("0")
# excellent_student :integer default("0")
# phone :string(255)
# authentication :boolean default("0")
# grade :integer default("0")
# experience :integer default("0")
# nickname :string(255)
# show_realname :boolean default("1")
# professional_certification :boolean default("0")
# ID_number :string(255)
# certification :integer default("0")
# homepage_teacher :boolean default("0")
# homepage_engineer :boolean default("0")
# is_test :integer default("0")
# ecoder_user_id :integer default("0")
# business :boolean default("0")
# profile_completed :boolean default("0")
# laboratory_id :integer
2021-06-03 11:26:14 +08:00
# is_shixun_marker :boolean default("0")
2021-06-25 17:31:23 +08:00
# admin_visitable :boolean default("0")
# collaborator :boolean default("0")
# gitea_uid :integer
2020-12-23 15:48:07 +08:00
# is_sync_pwd :boolean default("1")
# watchers_count :integer default("0")
# devops_step :integer default("0")
2021-06-25 17:31:23 +08:00
# gitea_token :string(255)
# platform :string(255)
2020-11-23 15:35:03 +08:00
#
# Indexes
#
# index_users_on_ecoder_user_id (ecoder_user_id)
# index_users_on_homepage_engineer (homepage_engineer)
# index_users_on_homepage_teacher (homepage_teacher)
# index_users_on_laboratory_id (laboratory_id)
2021-06-25 17:31:23 +08:00
# index_users_on_login (login) UNIQUE
# index_users_on_mail (mail) UNIQUE
# index_users_on_phone (phone) UNIQUE
2020-11-23 15:35:03 +08:00
# index_users_on_type (type)
#
2021-01-15 18:28:38 +08:00
class User < Owner
2021-01-11 17:44:11 +08:00
default_scope { where ( type : %w( User AnonymousUser ) ) }
2020-03-25 12:05:29 +08:00
extend Enumerize
2020-03-09 00:40:16 +08:00
include Watchable
2020-03-20 12:01:01 +08:00
include Likeable
include BaseModel
2020-08-13 23:38:32 +08:00
include Droneable
2021-04-19 15:06:56 +08:00
include User :: Avatar
2020-03-20 12:01:01 +08:00
# include Searchable::Dependents::User
2020-03-09 00:40:16 +08:00
2020-08-06 14:26:19 +08:00
# devops step
2020-09-03 15:52:54 +08:00
# devops_step column: 0: 未填写服务器信息; 1: 已填写服务器信息(未认证); 2: 已认证
2020-08-06 14:26:19 +08:00
DEVOPS_UNINIT = 0
DEVOPS_UNVERIFIED = 1
2020-09-03 15:52:54 +08:00
DEVOPS_CERTIFICATION = 2
2020-08-06 14:26:19 +08:00
2020-03-09 00:40:16 +08:00
# Account statuses
STATUS_ANONYMOUS = 0
STATUS_ACTIVE = 1
STATUS_REGISTERED = 2
STATUS_LOCKED = 3
# tpi tpm权限控制
EDU_ADMIN = 1 # 超级管理员
EDU_BUSINESS = 2 # 运营人员
EDU_SHIXUN_MANAGER = 3 # 实训管理员
EDU_SHIXUN_MEMBER = 4 # 实训成员
EDU_CERTIFICATION_TEACHER = 5 # 平台认证的老师
EDU_GAME_MANAGER = 6 # TPI的创建者
EDU_TEACHER = 7 # 平台老师,但是未认证
EDU_NORMAL = 8 # 普通用户
VALID_EMAIL_REGEX = / ^[a-zA-Z0-9]+([. \ -_ \\ ]*[a-zA-Z0-9])*@([a-z0-9]+[-a-z0-9]*[a-z0-9]+.){1,63}[a-z0-9]+$ /i
2020-03-20 13:21:17 +08:00
VALID_PHONE_REGEX = / ^1 \ d{10}$ /
2020-03-09 00:40:16 +08:00
# 身份证
VALID_NUMBER_REGEX = / (^[1-9] \ d{5}(18|19|20|(3 \ d)) \ d{2}((0[1-9])|(1[0-2]))(([0-2][1-9])|10|20|30|31) \ d{3}[0-9Xx]$)|(^([A-Z] \ d{6,10}( \ ( \ w{1} \ ))?)$) /
LOGIN_LENGTH_LIMIT = 30
MAIL_LENGTH_LMIT = 60
MIX_PASSWORD_LIMIT = 8
LOGIN_CHARS = %W( 2 3 4 5 6 7 8 9 a b c f e f g h i j k l m n o p q r s t u v w x y z ) . freeze
2020-03-20 12:01:01 +08:00
# FIX Invalid single-table inheritance type
# self.inheritance_column = nil
# educoder: 来自Educoder平台
# trustie: 来自Trustie平台
# forge: 平台本身注册的用户
2020-04-01 11:07:01 +08:00
# military: 军科的用户
2022-12-01 17:22:22 +08:00
enumerize :platform , in : [ :forge , :educoder , :trustie , :military , :github , :gitee , :qq , :wechat ] , default : :forge , scope : :shallow
2020-03-20 12:01:01 +08:00
2020-03-09 00:40:16 +08:00
belongs_to :laboratory , optional : true
2022-04-12 11:01:09 +08:00
has_many :composes , dependent : :destroy
has_many :compose_users , dependent : :destroy
2020-03-09 00:40:16 +08:00
has_one :user_extension , dependent : :destroy
has_many :open_users , dependent : :destroy
has_one :wechat_open_user , class_name : 'OpenUsers::Wechat'
2022-12-06 10:24:48 +08:00
has_one :qq_open_user , class_name : 'OpenUsers::Qq'
2020-03-09 00:40:16 +08:00
accepts_nested_attributes_for :user_extension , update_only : true
2020-05-15 17:05:53 +08:00
has_many :fork_users , dependent : :destroy
2020-03-09 00:40:16 +08:00
2020-03-20 12:01:01 +08:00
has_many :versions
has_many :issue_times , :dependent = > :destroy
2020-03-09 00:40:16 +08:00
has_one :onclick_time , :dependent = > :destroy
# 新版私信
has_many :private_messages , dependent : :destroy
has_many :recent_contacts , through : :private_messages , source : :target
has_many :tidings , :dependent = > :destroy
has_many :journals_for_messages , :as = > :jour , :dependent = > :destroy
has_many :attachments , foreign_key : :author_id , :dependent = > :destroy
2020-08-13 23:38:32 +08:00
has_one :ci_cloud_account , class_name : 'Ci::CloudAccount' , dependent : :destroy
2020-03-09 00:40:16 +08:00
# 认证
has_many :apply_user_authentication
has_one :process_real_name_apply , - > { processing . real_name_auth . order ( created_at : :desc ) } , class_name : 'ApplyUserAuthentication'
has_one :process_professional_apply , - > { processing . professional_auth . order ( created_at : :desc ) } , class_name : 'ApplyUserAuthentication'
has_many :apply_actions , dependent : :destroy
has_many :trail_auth_apply_actions , - > { where ( container_type : 'TrialAuthorization' ) } , class_name : 'ApplyAction'
2020-04-21 10:45:29 +08:00
# has_many :attendances
2021-04-25 18:41:13 +08:00
has_many :applied_messages , dependent : :destroy
has_many :operate_applied_messages , class_name : 'AppliedMessage' , dependent : :destroy
2020-03-09 00:40:16 +08:00
# 项目
has_many :applied_projects , dependent : :destroy
2021-04-25 18:41:13 +08:00
has_many :operate_applied_transfer_projects , class_name : 'AppliedTransferProject' , dependent : :destroy
2021-04-28 15:15:45 +08:00
has_many :members , dependent : :destroy
has_many :team_users , dependent : :destroy
has_many :teams , through : :team_users
2020-03-09 00:40:16 +08:00
# 教学案例
2020-04-23 11:08:29 +08:00
# has_many :libraries, dependent: :destroy
2020-03-20 12:01:01 +08:00
has_many :project_trends , dependent : :destroy
2020-10-13 19:49:52 +08:00
has_many :oauths , dependent : :destroy
2020-03-09 00:40:16 +08:00
2021-01-15 14:55:52 +08:00
has_many :organization_users , dependent : :destroy
has_many :organizations , through : :organization_users
2021-05-27 14:23:59 +08:00
has_many :pinned_projects , dependent : :destroy
has_many :is_pinned_projects , through : :pinned_projects , source : :project
accepts_nested_attributes_for :is_pinned_projects
2021-05-28 15:36:05 +08:00
has_many :issues , dependent : :destroy , foreign_key : :author_id
has_many :pull_requests , dependent : :destroy
2021-07-14 15:38:14 +08:00
has_many :public_keys , class_name : " Gitea::PublicKey " , primary_key : :gitea_uid , foreign_key : :owner_id , dependent : :destroy
2022-07-07 13:41:14 +08:00
has_many :sonar_tasks , dependent : :destroy
2021-10-18 11:54:42 +08:00
has_one :user_template_message_setting , dependent : :destroy
2021-10-29 16:00:46 +08:00
has_many :system_notification_histories
has_many :system_notifications , through : :system_notification_histories
2022-03-31 12:54:39 +08:00
has_one :trace_user , dependent : :destroy
2022-11-09 14:41:17 +08:00
has_many :feedbacks , dependent : :destroy
2020-03-09 00:40:16 +08:00
# Groups and active users
scope :active , lambda { where ( status : STATUS_ACTIVE ) }
2020-03-20 12:01:01 +08:00
scope :like , lambda { | keywords |
2021-06-15 16:14:15 +08:00
sql = " CONCAT(lastname, firstname) LIKE :search OR nickname LIKE :search OR login LIKE :search OR mail LIKE :search OR nickname LIKE :search "
2022-06-08 10:37:31 +08:00
where ( sql , :search = > " % #{ keywords . strip } % " ) unless keywords . blank?
2020-03-20 12:01:01 +08:00
}
2020-03-09 00:40:16 +08:00
2021-01-26 17:44:04 +08:00
scope :simple_select , - > { select ( :id , :login , :lastname , :firstname , :nickname , :gitea_uid , :type ) }
2020-06-02 15:57:06 +08:00
2020-03-09 00:40:16 +08:00
attr_accessor :password , :password_confirmation
2021-06-03 10:19:33 +08:00
delegate :description , :gender , :department_id , :school_id , :location , :location_city ,
2022-04-28 10:42:55 +08:00
:show_email , :show_location , :show_department , :super_description , :show_super_description ,
2021-06-03 10:19:33 +08:00
:technical_title , :province , :city , :custom_department , to : :user_extension , allow_nil : true
2020-03-09 00:40:16 +08:00
2022-11-23 17:58:51 +08:00
# before_save :update_hashed_password, :set_lastname
before_save :update_hashed_password
2021-10-27 11:03:34 +08:00
after_save :reset_cache_data
2020-03-09 00:40:16 +08:00
after_create do
SyncTrustieJob . perform_later ( " user " , 1 ) if allow_sync_to_trustie?
end
#
# validations
#
2020-04-15 11:47:00 +08:00
# validates_presence_of :login, :if => Proc.new { |user| !user.is_a?(AnonymousUser) }, case_sensitive: false
2020-03-09 00:40:16 +08:00
validates_uniqueness_of :login , :if = > Proc . new { | user | user . login_changed? && user . login . present? } , case_sensitive : false
validates_uniqueness_of :mail , :if = > Proc . new { | user | user . mail_changed? && user . mail . present? } , case_sensitive : false
2020-03-20 12:01:01 +08:00
# validates_uniqueness_of :phone, :if => Proc.new { |user| user.phone_changed? && user.phone.present? }, case_sensitive: false
2020-03-09 00:40:16 +08:00
validates_length_of :login , maximum : LOGIN_LENGTH_LIMIT
validates_length_of :mail , maximum : MAIL_LENGTH_LMIT
validate :validate_sensitive_string
validate :validate_password_length
2021-10-26 15:22:42 +08:00
def reset_cache_data
2021-11-01 16:20:52 +08:00
Cache :: V2 :: OwnerCommonService . new ( self . id ) . reset
2021-10-26 15:22:42 +08:00
end
2021-05-27 14:23:59 +08:00
# 用户参与的所有项目
def full_member_projects
normal_projects = Project . members_projects ( self . id ) . to_sql
2021-05-28 15:36:05 +08:00
org_projects = Project . joins ( teams : :team_users ) . where ( team_users : { user_id : self . id } ) . to_sql
2021-05-27 14:23:59 +08:00
return Project . from ( " ( #{ normal_projects } UNION #{ org_projects } ) AS projects " ) . distinct
end
2021-06-09 16:53:09 +08:00
# 用户管理的所有项目
def full_admin_projects
normal_projects = Project . joins ( members : :roles ) . where ( roles : { name : 'Manager' } , members : { user_id : self . id } ) . to_sql
org_projects = Project . joins ( teams : :team_users ) . where ( teams : { authorize : %w( admin owner ) } , team_users : { user_id : self . id } ) . to_sql
return Project . from ( " ( #{ normal_projects } UNION #{ org_projects } ) AS projects " ) . distinct
end
2021-04-19 18:36:02 +08:00
def name
login
end
2020-03-09 00:40:16 +08:00
# 删除自动登录的token, 一旦退出下次会提示需要登录
def delete_autologin_token ( value )
Token . where ( :user_id = > id , :action = > 'autologin' , :value = > value ) . delete_all
end
def delete_session_token ( value )
Token . where ( :user_id = > id , :action = > 'session' , :value = > value ) . delete_all
end
def git_mail
mail . blank? ? " #{ login } @educoder.net " : mail
end
2020-03-20 12:01:01 +08:00
def project_manager? ( project )
2021-04-13 11:53:06 +08:00
project . manager? ( self ) || self . admin?
2020-03-20 12:01:01 +08:00
end
2020-03-09 00:40:16 +08:00
# 学号
def student_id
self . user_extension . try ( :student_id )
end
# 关注数
def follow_count
2020-06-03 18:31:26 +08:00
Watcher . where ( user_id : self . id , watchable_type : %w( User ) ) . count
2020-03-09 00:40:16 +08:00
# User.watched_by(id).count
end
# 粉丝数
def fan_count
2020-06-03 18:31:26 +08:00
Watcher . where ( watchable_type : %w( User ) , watchable_id : self . id ) . count
2020-03-09 00:40:16 +08:00
# watchers.count
end
# 判断当前用户是否为老师
def is_teacher?
self . user_extension . teacher?
end
# 平台认证的老师
def is_certification_teacher
self . user_extension . teacher? && self . professional_certification
end
def certification_teacher?
professional_certification? && user_extension . teacher?
end
# 判断用户的身份
def identity
ue = self . user_extension
unless ue . blank?
if ue . teacher?
ue . technical_title ? ue . technical_title : " 老师 "
elsif ue . student?
" 学生 "
else
ue . technical_title ? ue . technical_title : " 专业人士 "
end
end
end
# 实名认证状态
def auth_status
status = if authentication
2020-03-20 12:01:01 +08:00
" 已认证 "
elsif process_real_name_apply . present?
" 待审核 "
else
" 未认证 "
end
2020-03-09 00:40:16 +08:00
end
# 职业认证状态
def pro_status
status = if professional_certification
" 已认证 "
elsif process_professional_apply . present?
" 待审核 "
else
" 未认证 "
end
end
# 判断当前用户是否通过职业认证
def pro_certification?
professional_certification
end
# 学校所在的地区
def school_province
user_extension & . school & . province || ''
end
# 用户的学校名称
2020-04-21 10:24:27 +08:00
# def school_name
# user_extension&.school&.name || ''
# end
2020-03-09 00:40:16 +08:00
# 用户的学院名称
def department_name
user_extension & . department & . name || ''
end
# 课堂的所有身份
def course_role course
course . course_members . where ( user_id : id ) . pluck ( :role )
end
# 课堂的老师(创建者、老师、助教)
def teacher_of_course? ( course )
course . course_members . exists? ( user_id : id , role : [ 1 , 2 , 3 ] , is_active : 1 ) || admin? || business?
end
# 课堂的老师(创建者、老师、助教),不考虑超管和运营人员
def none_admin_teacher_of_course? ( course )
course . course_members . exists? ( user_id : id , role : [ 1 , 2 , 3 ] , is_active : 1 )
end
# 课堂的老师(创建者、老师、助教),不用考虑当前身份
def teacher_of_course_non_active? ( course )
course . course_members . exists? ( user_id : id , role : [ 1 , 2 , 3 ] )
end
# 是否是教师,课堂管理员或者超级管理员
def teacher_or_admin? ( course )
course . course_members . exists? ( user_id : id , role : [ 1 , 2 ] , is_active : 1 ) || admin? || business?
end
# 课堂的创建者(考虑到多重身份的用户)
def creator_of_course? ( course )
course . course_members . exists? ( user_id : id , role : 1 , is_active : 1 ) || admin? || business?
end
# 课堂的学生
def student_of_course? ( course )
course . course_members . exists? ( user_id : id , role : % i [ STUDENT ] , is_active : 1 )
end
# 课堂成员
def member_of_course? ( course )
course & . course_members . exists? ( user_id : id )
end
# 实训路径管理员
def creator_of_subject? ( subject )
subject . user_id == id || admin?
end
# 实训路径: 合作者、admin
def manager_of_subject? ( subject )
subject . subject_members . exists? ( user_id : id , role : [ 1 , 2 ] ) || admin? || business?
end
# 实训管理员: 实训合作者、admin
def manager_of_shixun? ( shixun )
logger . info ( " # # # # # # # # # # # # id: #{ id } " )
2020-03-20 12:01:01 +08:00
shixun . shixun_members . exists? ( role : [ 1 , 2 ] , user_id : id ) || admin? || business?
2020-03-09 00:40:16 +08:00
end
# 实训管理员
def creator_of_shixun? ( shixun )
id == shixun . user_id
end
# 实训的合作者
def member_of_shixun? ( shixun )
#self.shixun_members.where(:role => 2, :shixun_id => shixun.id).present?
shixun . shixun_members . exists? ( role : 2 , user_id : id )
end
# TPI的创建者
def creator_of_game? ( game )
id == game . user_id
end
# 用户账号状态
def active?
status == STATUS_ACTIVE
end
def registered?
status == STATUS_REGISTERED
end
def locked?
status == STATUS_LOCKED
end
def activate
self . status = STATUS_ACTIVE
end
def register
self . status = STATUS_REGISTERED
end
def lock
self . status = STATUS_LOCKED
end
def activate!
update_attribute ( :status , STATUS_ACTIVE )
end
def register!
update_attribute ( :status , STATUS_REGISTERED )
end
def lock!
update_attribute ( :status , STATUS_LOCKED )
end
# 课程用户身份
def course_identity ( course )
if ! logged?
Course :: Anonymous
elsif admin?
Course :: ADMIN
elsif business?
Course :: BUSINESS
else
role = course & . course_members & . find_by ( user_id : id , is_active : 1 ) & . role
case role
when nil then Course :: NORMAL
when 'CREATOR' then Course :: CREATOR
when 'PROFESSOR' then Course :: PROFESSOR
when 'STUDENT' then Course :: STUDENT
when 'ASSISTANT_PROFESSOR' then Course :: ASSISTANT_PROFESSOR
end
end
end
# 实训用户身份
def shixun_identity ( shixun )
@identity =
2020-03-20 12:01:01 +08:00
if admin?
User :: EDU_ADMIN
elsif business?
User :: EDU_BUSINESS
elsif creator_of_shixun? ( shixun )
User :: EDU_SHIXUN_MANAGER
elsif member_of_shixun? ( shixun )
User :: EDU_SHIXUN_MEMBER
elsif is_certification_teacher
User :: EDU_CERTIFICATION_TEACHER
elsif is_teacher?
User :: EDU_TEACHER
else
User :: EDU_NORMAL
end
2020-03-09 00:40:16 +08:00
return @identity
end
# tpi的用户身份
def game_identity ( game )
shixun = game . myshixun . shixun
@identity =
2020-03-20 12:01:01 +08:00
if admin?
User :: EDU_ADMIN
elsif business?
User :: EDU_BUSINESS
elsif creator_of_shixun? ( shixun )
User :: EDU_SHIXUN_MANAGER
elsif member_of_shixun? ( shixun )
User :: EDU_SHIXUN_MEMBER
elsif is_certification_teacher
User :: EDU_CERTIFICATION_TEACHER
elsif creator_of_game? ( game )
User :: EDU_GAME_MANAGER
elsif is_teacher?
User :: EDU_TEACHER
else
User :: EDU_NORMAL
end
2020-03-09 00:40:16 +08:00
return @identity
end
# 我的实训
def my_shixuns
shixun_ids = shixun_members . pluck ( :shixun_id ) + myshixuns . pluck ( :shixun_id )
Shixun . where ( :id = > shixun_ids ) . visible
end
# 用户是否有权限查看实训
# 1、实训删除只有管理员能看到
# 2、实训隐藏了只有管理员、实训合作者能看到
# 3、如果有限制学校范围, 则学校的用户、管理员、实训合作者能看到
def shixun_permission ( shixun )
case shixun . status
when - 1 # 软删除只有管理员能访问
admin?
when 0 , 1 , 3 # 申请发布或者已关闭的实训,只有实训管理员可以访问
manager_of_shixun? ( shixun )
when 2
if shixun . hidden
manager_of_shixun? ( shixun )
else
shixun . use_scope == 0 || manager_of_shixun? ( shixun ) || shixun . shixun_schools . exists? ( school_id : school_id )
end
end
end
# 用户在平台名称的显示方式
def full_name
return '游客' unless logged?
name = show_realname? ? lastname + firstname : nickname
name . blank? ? ( nickname . blank? ? login : nickname ) : name
end
# 用户的真实姓名(不考虑用户是否隐藏了真实姓名,课堂模块都用真实姓名)
def real_name
return '游客' unless logged?
name = lastname + firstname
name = name . blank? ? ( nickname . blank? ? login : nickname ) : name
2022-06-08 10:46:55 +08:00
# name.gsub(/\s+/, '').strip #6.11 -hs
name . strip
2020-03-09 00:40:16 +08:00
end
def only_real_name
" #{ lastname } #{ firstname } "
end
# 用户是否选题毕设课题
def selected_topic? ( topic )
student_graduation_topics . where ( graduation_topic_id : topic . id ) . last . try ( :status )
end
def click_time
click_time = OnclickTime . find_by ( user_id : id ) || OnclickTime . create ( user_id : id , onclick_time : created_on )
click_time . onclick_time
end
def manager_of_memo? ( memo )
id == memo . author_id || admin? || business?
end
# 是否是项目管理者
def manager_of_project? ( project )
project . project_members . where ( user_id : id ) . count > 0
end
def logged?
true
end
def active?
status == STATUS_ACTIVE
end
def locked?
status == STATUS_LOCKED
end
def phone_binded?
phone . present?
end
def email_binded?
mail . present?
end
# def self.current=(user)
# Thread.current[:current_user] = user
# end
#
# def self.current
# Thread.current[:current_user] ||= User.anonymous
# end
def self . current = ( user )
RequestStore . store [ :current_user ] = user
end
def self . current
RequestStore . store [ :current_user ] || = User . anonymous
end
def self . anonymous
anonymous_user = AnonymousUser . unscoped . take
if anonymous_user . nil?
2020-11-24 17:55:12 +08:00
anonymous_user = AnonymousUser . unscoped . create ( lastname : 'Anonymous' , firstname : '' , login : '' , mail : '358551897@qq.com' , phone : '13333333333' , status : 0 , platform : User . platform . forge )
2020-03-09 00:40:16 +08:00
raise " Unable to create the anonymous user: error_info: #{ anonymous_user . errors . messages } " if anonymous_user . new_record?
end
anonymous_user
end
# Returns the user who matches the given autologin +key+ or nil
def self . try_to_autologin ( key )
user = Token . find_active_user ( 'autologin' , key )
user . update ( last_login_on : Time . now ) if user
user
end
def self . hash_password ( clear_password )
Digest :: SHA1 . hexdigest ( clear_password || " " )
end
def check_password? ( clear_password )
# Preventing Timing Attack
ActiveSupport :: SecurityUtils . secure_compare (
User . hash_password ( " #{ salt } #{ User . hash_password clear_password } " ) ,
hashed_password
)
end
# 工程认证的学校
def ec_school
2022-04-12 11:01:09 +08:00
school_id_hash = ActiveRecord :: Base . connection . exec_query ( " SELECT `ec_school_users`.`school_id` FROM `ec_school_users` WHERE `ec_school_users`.`user_id` = #{ self . id } " ) . first ||
ActiveRecord :: Base . connection . exec_query ( " SELECT `ec_major_schools`.`school_id` FROM `ec_major_schools` INNER JOIN `ec_major_school_users` ON `ec_major_schools`.`id` = `ec_major_school_users`.`ec_major_school_id` WHERE`ec_major_school_users`.`user_id` = #{ self . id } " ) . first ||
ActiveRecord :: Base . connection . exec_query ( " SELECT `ec_major_schools`.`school_id` FROM `ec_major_schools` INNER JOIN `ec_years` ON `ec_years`.`ec_major_school_id` = `ec_major_schools`.`id` INNER JOIN `ec_courses` ON `ec_courses`.`ec_year_id` = `ec_years`.`id` INNER JOIN `ec_course_users` ON `ec_course_users`.`ec_course_id` = `ec_courses`.`id` WHERE `ec_course_users`.`user_id` = #{ self . id } " ) . first
if school_id_hash . nil?
return nil
else
school_id_hash [ " school_id " ]
end
rescue
nil
2020-03-09 00:40:16 +08:00
end
# 登录,返回用户名与密码匹配的用户
def self . try_to_login ( login , password )
login = login . to_s . strip
password = password . to_s
# Make sure no one can sign in with an empty login or password
return nil if login . empty? || password . empty?
if ( login =~ VALID_EMAIL_REGEX )
user = find_by_mail ( login )
elsif ( login =~ VALID_PHONE_REGEX )
user = find_by_phone ( login )
else
user = find_by_login ( login )
end
user
rescue = > text
raise text
end
2022-06-17 17:02:24 +08:00
def self . authenticate! ( login , password )
user = self . where ( " login = ? or mail = ? or phone = ? " , login . to_s . gsub ( " " , '' ) , login . to_s . gsub ( " " , '' ) , login . downcase . to_s . gsub ( " " , '' ) ) . limit ( 1 ) . first
return ( user . check_password? ( password ) ? user : nil ) unless user . nil?
nil
end
# Generate public/private keys
def generate_keys
key_size = ( Rails . env == 'test' ? 512 : 2048 )
serialized_private_key = OpenSSL :: PKey :: RSA :: generate ( key_size ) . to_s
serialized_public_key = OpenSSL :: PKey :: RSA . new ( serialized_private_key )
[ serialized_private_key , serialized_public_key ]
end
2020-03-09 00:40:16 +08:00
def show_real_name
name = lastname + firstname
if name . blank?
nickname . blank? ? login : nickname
else
name
end
end
def update_hashed_password
if password
salt_password ( password )
end
end
def salt_password ( clear_password )
self . salt = User . generate_salt
self . hashed_password = User . hash_password ( " #{ salt } #{ User . hash_password clear_password } " )
end
def self . generate_salt
2022-05-17 15:41:39 +08:00
Gitlink :: Utils . random_hex ( 16 )
2020-03-09 00:40:16 +08:00
end
# 全部已认证
def all_certified?
authentication? && professional_certification?
end
# 是否绑定邮箱
def email_binded?
mail . present?
end
# 手机号: 123***123
def hidden_phone
Util . conceal ( phone , :phone ) . to_s
end
# 邮箱: w***l@qq.com
def hidden_mail
Util . conceal ( mail , :email ) . to_s
end
# 学院的url标识
2020-04-21 10:45:29 +08:00
# def college_identifier
# Department.find_by_id(department_members.pluck(:department_id).first)&.identifier
# end
2020-03-09 00:40:16 +08:00
# 是否能申请试用
def can_apply_trial?
return false if certification == 1
apply = ApplyAction . order ( created_at : :desc ) . find_by ( user_id : id , container_type : 'TrialAuthorization' )
apply . present? && ! apply . status . zero?
end
2020-04-21 14:48:52 +08:00
def projects_count
2020-05-14 18:08:27 +08:00
Project . includes ( :members ) . joins ( :members ) . where ( members : { user_id : self . id } ) . select ( :id ) . size
2020-04-21 14:48:52 +08:00
end
2020-03-09 00:40:16 +08:00
# 是否已经签到
def attendance_signed?
attendance = Attendance . find_by ( user_id : id )
attendance . present? && Util . days_between ( Time . zone . now , attendance . created_at ) . zero?
end
# 明日签到金币
def tomorrow_attendance_gold
Attendance . find_by ( user_id : id ) & . next_gold || 60 # 基础50, 连续签到+10
end
def admin_or_business?
admin? || business?
end
def self . generate_login ( prefix )
login = prefix + LOGIN_CHARS . sample ( 8 ) . join ( '' )
while User . exists? ( login : login )
login = prefix + LOGIN_CHARS . sample ( 8 ) . join ( '' )
end
login
end
2022-12-01 17:22:22 +08:00
# 生成数字账号
CODES = %W( 0 1 2 3 4 5 6 7 8 9 )
def self . generate_user_login type
code = CODES . sample ( 8 ) . join
code = type + code . to_s
return User . generate_user_login ( type ) if User . where ( login : code ) . present?
code
end
2020-03-09 00:40:16 +08:00
def bind_open_user? ( type )
case type
when 'wechat' then wechat_open_user . present?
when 'qq' then qq_open_user . present?
else false
end
end
def reset_login_times!
LimitForbidControl :: UserLogin . new ( self ) . clear
end
def from_sub_site?
laboratory_id . present? && laboratory_id != 1
end
2021-09-18 10:24:16 +08:00
def profile_is_completed?
2022-04-12 11:01:09 +08:00
# self.nickname.present? && self.gender.present? && self.mail.present? && self.custom_department.present?
true
2022-03-31 12:54:39 +08:00
end
2020-03-09 00:40:16 +08:00
protected
def validate_password_length
# 管理员的初始密码是5位
if password . present? && password . size < MIX_PASSWORD_LIMIT && ! User . current . admin?
raise ( " 密码长度不能低于 #{ MIX_PASSWORD_LIMIT } 位 " )
end
if password . present? && password . size > 16
raise ( '密码长度不能超过16位' )
end
end
def validate_sensitive_string
2020-03-20 12:01:01 +08:00
raise ( " 真实姓名包含敏感词汇,请重新输入 " ) if lastname && ! HarmoniousDictionary . clean? ( lastname )
raise ( " 昵称包含敏感词汇,请重新输入 " ) if nickname && ! HarmoniousDictionary . clean? ( nickname )
2020-03-09 00:40:16 +08:00
end
def set_laboratory
return unless new_record?
self . laboratory = Laboratory . current if laboratory_id . blank?
end
2021-06-29 10:36:58 +08:00
def set_lastname
self . lastname = self . nickname if changes [ :nickname ] . present?
end
2020-03-09 00:40:16 +08:00
end
class AnonymousUser < User
validate :validate_anonymous_uniqueness , :on = > :create
def validate_anonymous_uniqueness
# There should be only one AnonymousUser in the database
errors . add :base , 'An anonymous user already exists.' if AnonymousUser . exists?
end
def available_custom_fields
[ ]
end
# Overrides a few properties
def logged? ; false end
def admin ; false end
def name ( * args ) ; I18n . t ( :label_user_anonymous ) end
# def mail=(*args); nil end
# def mail; nil end
def time_zone ; nil end
def rss_key ; nil end
def membership ( * args )
nil
end
def member_of? ( * args )
false
end
# Anonymous user can not be destroyed
def destroy
false
end
protected
def instantiate_email_address
end
end