|
|
|
|
class User < ApplicationRecord
|
|
|
|
|
include Watchable
|
|
|
|
|
include Searchable::Dependents::User
|
|
|
|
|
|
|
|
|
|
# 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
|
|
|
|
|
VALID_PHONE_REGEX = /^1\d{10}$/
|
|
|
|
|
|
|
|
|
|
LOGIN_LENGTH_LIMIT = 30
|
|
|
|
|
MAIL_LENGTH_LMIT = 60
|
|
|
|
|
|
|
|
|
|
MIX_PASSWORD_LIMIT = 8
|
|
|
|
|
|
|
|
|
|
has_one :user_extension, dependent: :destroy
|
|
|
|
|
accepts_nested_attributes_for :user_extension, update_only: true
|
|
|
|
|
|
|
|
|
|
has_many :memos, foreign_key: 'author_id'
|
|
|
|
|
has_many :created_shixuns, class_name: 'Shixun'
|
|
|
|
|
has_many :shixun_members, :dependent => :destroy
|
|
|
|
|
has_many :shixuns, :through => :shixun_members
|
|
|
|
|
has_many :myshixuns, :dependent => :destroy
|
|
|
|
|
has_many :study_shixuns, through: :myshixuns, source: :shixun # 已学习的实训
|
|
|
|
|
has_many :course_messages
|
|
|
|
|
has_many :courses, dependent: :destroy
|
|
|
|
|
|
|
|
|
|
#试卷
|
|
|
|
|
has_many :exercise_banks, :dependent => :destroy
|
|
|
|
|
has_many :exercise_users, :dependent => :destroy
|
|
|
|
|
has_many :exercise_answers, :dependent => :destroy #针对每个题目学生的答案
|
|
|
|
|
has_many :exercise_shixun_answers, :dependent => :destroy #针对每个实训题目学生的答案
|
|
|
|
|
has_many :exercise_answer_comments, :dependent => :destroy
|
|
|
|
|
has_many :exercises, :dependent => :destroy #创建的试卷
|
|
|
|
|
|
|
|
|
|
has_many :homework_banks, dependent: :destroy
|
|
|
|
|
|
|
|
|
|
has_many :graduation_works, dependent: :destroy
|
|
|
|
|
|
|
|
|
|
has_many :students_for_courses, foreign_key: :student_id, dependent: :destroy
|
|
|
|
|
has_one :onclick_time, :dependent => :destroy
|
|
|
|
|
|
|
|
|
|
# 新版私信
|
|
|
|
|
has_many :private_messages, :dependent => :destroy
|
|
|
|
|
has_many :tidings, :dependent => :destroy
|
|
|
|
|
|
|
|
|
|
has_many :games, :dependent => :destroy
|
|
|
|
|
has_many :created_subjects, foreign_key: :user_id, class_name: 'Subject'
|
|
|
|
|
has_many :subjects, :through => :subject_members
|
|
|
|
|
has_many :subject_members, :dependent => :destroy
|
|
|
|
|
has_many :grades, :dependent => :destroy
|
|
|
|
|
has_many :experiences, :dependent => :destroy
|
|
|
|
|
has_many :student_works, :dependent => :destroy
|
|
|
|
|
has_many :student_works_scores
|
|
|
|
|
has_many :student_works_evaluation_distributions
|
|
|
|
|
|
|
|
|
|
# 毕业设计
|
|
|
|
|
has_many :graduation_topics, :dependent => :destroy
|
|
|
|
|
has_many :student_graduation_topics, :dependent => :destroy
|
|
|
|
|
|
|
|
|
|
# 题库
|
|
|
|
|
has_many :question_banks, :dependent => :destroy
|
|
|
|
|
# 毕设任务题库
|
|
|
|
|
has_many :gtask_banks, dependent: :destroy
|
|
|
|
|
has_many :gtopic_banks, dependent: :destroy
|
|
|
|
|
|
|
|
|
|
#问卷
|
|
|
|
|
has_many :course_members, :dependent => :destroy
|
|
|
|
|
has_many :poll_votes, :dependent => :destroy
|
|
|
|
|
has_many :poll_users, :dependent => :destroy
|
|
|
|
|
|
|
|
|
|
has_many :messages,foreign_key: 'author_id',:dependent => :destroy
|
|
|
|
|
|
|
|
|
|
has_many :journals_for_messages, :as => :jour, :dependent => :destroy
|
|
|
|
|
has_many :teacher_course_groups, :dependent => :destroy
|
|
|
|
|
|
|
|
|
|
has_many :attachments,foreign_key: :author_id, :dependent => :destroy
|
|
|
|
|
|
|
|
|
|
# 工程认证
|
|
|
|
|
has_many :ec_school_users,:dependent => :destroy
|
|
|
|
|
has_many :schools, :through => :ec_school_users
|
|
|
|
|
|
|
|
|
|
has_many :ec_major_school_users, :dependent => :destroy
|
|
|
|
|
has_many :ec_major_schools, :through => :ec_major_school_users
|
|
|
|
|
|
|
|
|
|
has_many :ec_course_users
|
|
|
|
|
|
|
|
|
|
has_many :department_members, dependent: :destroy #部门管理员
|
|
|
|
|
|
|
|
|
|
# 课堂
|
|
|
|
|
has_many :student_course_members, -> { course_students }, class_name: 'CourseMember'
|
|
|
|
|
has_many :as_student_courses, through: :student_course_members, source: :course
|
|
|
|
|
has_many :manage_course_members, -> { teachers_and_admin }, class_name: 'CourseMember'
|
|
|
|
|
has_many :manage_courses, through: :manage_course_members, source: :course
|
|
|
|
|
|
|
|
|
|
# 关注
|
|
|
|
|
has_many :be_watchers, foreign_key: :user_id, dependent: :destroy # 我的关注
|
|
|
|
|
has_many :be_watcher_users, through: :be_watchers, dependent: :destroy # 我关注的用户
|
|
|
|
|
|
|
|
|
|
# 认证
|
|
|
|
|
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'
|
|
|
|
|
|
|
|
|
|
has_many :attendances
|
|
|
|
|
|
|
|
|
|
# 兴趣
|
|
|
|
|
has_many :user_interests, dependent: :delete_all
|
|
|
|
|
has_many :interests, through: :user_interests, source: :repertoire
|
|
|
|
|
|
|
|
|
|
# Groups and active users
|
|
|
|
|
scope :active, lambda { where(status: STATUS_ACTIVE) }
|
|
|
|
|
|
|
|
|
|
attr_accessor :password, :password_confirmation
|
|
|
|
|
|
|
|
|
|
before_save :update_hashed_password
|
|
|
|
|
|
|
|
|
|
#
|
|
|
|
|
# validations
|
|
|
|
|
#
|
|
|
|
|
validates_presence_of :login, :if => Proc.new { |user| !user.is_a?(AnonymousUser) }, case_sensitive: false
|
|
|
|
|
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
|
|
|
|
|
validates_uniqueness_of :phone, :if => Proc.new { |user| user.phone_changed? && user.phone.present? }, case_sensitive: false
|
|
|
|
|
validates_length_of :login, maximum: LOGIN_LENGTH_LIMIT
|
|
|
|
|
validates_length_of :mail, maximum: MAIL_LENGTH_LMIT
|
|
|
|
|
# validates_format_of :mail, with: VALID_EMAIL_REGEX, multiline: true
|
|
|
|
|
# validates_format_of :phone, with: VALID_PHONE_REGEX, multiline: true
|
|
|
|
|
validate :validate_password_length
|
|
|
|
|
|
|
|
|
|
# validates :nickname, presence: true, length: { maximum: 10 }
|
|
|
|
|
# validates :lastname, presence: true
|
|
|
|
|
|
|
|
|
|
# 删除自动登录的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 || "#{login}@educoder.net"
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
# 学号
|
|
|
|
|
def student_id
|
|
|
|
|
self.user_extension.try(:student_id)
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
# 关注数
|
|
|
|
|
def follow_count
|
|
|
|
|
Watcher.where(user_id: id, watchable_type: %w(Principal User)).count
|
|
|
|
|
# User.watched_by(id).count
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
# 粉丝数
|
|
|
|
|
def fan_count
|
|
|
|
|
Watcher.where(watchable_type: %w(Principal User), watchable_id: id).count
|
|
|
|
|
# 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 pro_certification?
|
|
|
|
|
professional_certification
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
# 用户的学校名称
|
|
|
|
|
def school_name
|
|
|
|
|
user_extension&.school&.name || ''
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
def school_id
|
|
|
|
|
user_extension&.school_id
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
# 课堂的老师(创建者、老师、助教)
|
|
|
|
|
def teacher_of_course?(course)
|
|
|
|
|
course.course_members.exists?(user_id: id, role: [1,2,3], is_active: 1) || admin?
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
# 课堂的老师(创建者、老师、助教),不用考虑当前身份
|
|
|
|
|
def teacher_of_course_non_active?(course)
|
|
|
|
|
course.course_members.exists?(user_id: id, role: [1,2,3]) || admin?
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
# 是否是教师,课堂管理员或者超级管理员
|
|
|
|
|
def teacher_or_admin?(course)
|
|
|
|
|
course.course_members.exists?(user_id: id, role: [1,2], is_active: 1) || admin?
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
# 课堂的创建者(考虑到多重身份的用户)
|
|
|
|
|
def creator_of_course?(course)
|
|
|
|
|
course.course_members.exists?(user_id: id, role: 1, is_active: 1) || admin?
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
# 课堂的学生
|
|
|
|
|
def student_of_course?(course)
|
|
|
|
|
course.course_members.exists?(user_id: id, role: %i[STUDENT])
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
# 课堂成员
|
|
|
|
|
def member_of_course?(course)
|
|
|
|
|
course.course_members.exists?(user_id: id)
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
# 实训路径管理员:创建者或admin
|
|
|
|
|
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?
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
# 实训管理员:实训合作者、admin
|
|
|
|
|
def manager_of_shixun?(shixun)
|
|
|
|
|
shixun.shixun_members.exists?(role: [1,2], user_id: id) || admin? || business?
|
|
|
|
|
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 =
|
|
|
|
|
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
|
|
|
|
|
return @identity
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
# tpi的用户身份
|
|
|
|
|
def game_identity(game)
|
|
|
|
|
shixun = game.myshixun.shixun
|
|
|
|
|
@identity =
|
|
|
|
|
if admin?
|
|
|
|
|
User::EDU_ADMIN
|
|
|
|
|
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
|
|
|
|
|
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
|
|
|
|
|
name.gsub(/\s+/, '').strip #6.11 -hs
|
|
|
|
|
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?
|
|
|
|
|
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 self.current=(user)
|
|
|
|
|
Thread.current[:current_user] = user
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
def self.current
|
|
|
|
|
Thread.current[:current_user] ||= User.anonymous
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
def self.anonymous
|
|
|
|
|
anonymous_user = AnonymousUser.unscoped.take
|
|
|
|
|
if anonymous_user.nil?
|
|
|
|
|
anonymous_user = AnonymousUser.unscoped.create(lastname: 'Anonymous', firstname: '', login: '',
|
|
|
|
|
mail: '358551897@qq.com', phone: '13333333333', status: 0)
|
|
|
|
|
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 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
|
|
|
|
|
|
|
|
|
|
if user
|
|
|
|
|
# user is already in local database
|
|
|
|
|
raise("账号已被注销,请联系管理员") if user.locked?
|
|
|
|
|
raise("密码错误") unless user.check_password?(password)
|
|
|
|
|
else
|
|
|
|
|
raise("账号未注册")
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
user
|
|
|
|
|
rescue => text
|
|
|
|
|
raise text
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
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
|
|
|
|
|
Educoder::Utils.random_hex(16)
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
# 全部已认证
|
|
|
|
|
def all_certified?
|
|
|
|
|
authentication? && professional_certification?
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
# 是否绑定邮箱
|
|
|
|
|
def email_binded?
|
|
|
|
|
mail.present?
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
# 学院的url标识
|
|
|
|
|
def college_identifier
|
|
|
|
|
Department.find_by_id(department_members.pluck(:department_id).first)&.identifier
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
# 是否能申请试用
|
|
|
|
|
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
|
|
|
|
|
|
|
|
|
|
# 是否已经签到
|
|
|
|
|
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
|
|
|
|
|
|
|
|
|
|
protected
|
|
|
|
|
def validate_password_length
|
|
|
|
|
# 管理员的初始密码是5位
|
|
|
|
|
if password.present? && password.size < MIX_PASSWORD_LIMIT && !User.current.admin?
|
|
|
|
|
raise("密码长度不能低于#{MIX_PASSWORD_LIMIT}位")
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
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
|