diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb index 8797c4074..508d005a4 100644 --- a/app/controllers/application_controller.rb +++ b/app/controllers/application_controller.rb @@ -250,8 +250,9 @@ class ApplicationController < ActionController::Base # 开放课程通过链接访问的用户 if !User.current.logged? && !params[:chinaoocTimestamp].blank? && !params[:websiteName].blank? && !params[:chinaoocKey].blank? content = "#{OPENKEY}#{params[:websiteName]}#{params[:chinaoocTimestamp]}" + if Digest::MD5.hexdigest(content) == params[:chinaoocKey] - user = User.open_class_user + user = open_class_user start_user_session(user) if user User.current = user end @@ -615,4 +616,27 @@ class ApplicationController < ActionController::Base def set_export_cookies cookies[:fileDownload] = true end + + # 149课程的评审用户数据创建(包含创建课堂学生) + def open_class_user + user = User.find_by(login: "OpenClassUser") + unless user + ActiveRecord::Base.transaction do + user_params = {status: 1, login: "OpenClassUser", lastname: "开放课程", + nickname: "开放课程", professional_certification: 1, certification: 1, grade: 0, + password: "12345678", phone: "11122223333", profile_completed: 1} + user = User.create!(user_params) + + UserExtension.create!(user_id: user.id, gender: 0, school_id: 3396, :identity => 1, :student_id => "openclassuser") # 3396 + + subject = Subject.find_by(id: 149) + if subject + subject.courses.each do |course| + CourseMember.create!(course_id: course.id, role: 3, user_id: user.id) if !course.course_members.exists?(user_id: user.id) + end + end + end + end + user + end end diff --git a/app/controllers/subjects_controller.rb b/app/controllers/subjects_controller.rb index 36f14a5d5..ba9b13f85 100644 --- a/app/controllers/subjects_controller.rb +++ b/app/controllers/subjects_controller.rb @@ -368,7 +368,7 @@ class SubjectsController < ApplicationController @schools.map do |s| school_courses = Course.where(id: course_ids, school_id: s.id) course_count = school_courses.count - student_count = StudentsForCourse.where(course_id: school_courses.pluck(:id)).count + student_count = CourseMember.where(course_id: school_courses.pluck(:id), role: 4).count homework_count = HomeworkCommon.find_by_sql("select count(*) cnt from homework_commons hc join courses c on hc.course_id = c.id where c.school_id = #{s.id} and hc.id in(#{homework_common_id})").first.try(:cnt) s.attributes.dup.merge({name: s.name, course_count: course_count, student_count: student_count,homework_count: homework_count}) diff --git a/app/models/user.rb b/app/models/user.rb index ebf68cce6..1dc511513 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -1,686 +1,663 @@ -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 :recent_contacts, through: :private_messages, source: :target - has_many :tidings, :dependent => :destroy - - has_many :games, :dependent => :destroy - has_many :created_subjects, foreign_key: :user_id, class_name: 'Subject' - has_many :subject_members, :dependent => :destroy - has_many :subjects, :through => :subject_members - 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 - - # 众包 - has_many :project_packages, foreign_key: :creator_id, dependent: :destroy - has_many :bidding_users, dependent: :destroy - has_many :bidden_project_packages, through: :bidding_users, source: :project_package - - # 项目 - has_many :applied_projects, dependent: :destroy - - # 教学案例 - has_many :libraries, dependent: :destroy - - # 视频 - has_many :videos, dependent: :destroy - - # 客户管理 - belongs_to :partner, optional: true - - # Groups and active users - scope :active, lambda { where(status: STATUS_ACTIVE) } - - attr_accessor :password, :password_confirmation - - delegate :gender, :department_id, :school_id, :location, :location_city, :technical_title, to: :user_extension, allow_nil: true - - 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.blank? ? "#{login}@educoder.net" : mail - 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 department_name - user_extension&.department&.name || '' - end - - # 课堂的老师(创建者、老师、助教) - def teacher_of_course?(course) - course.course_members.exists?(user_id: id, role: [1,2,3], is_active: 1) || admin? || business? - 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]) - 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? - 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 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 - 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 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 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 ec_school - school_id = self.ec_school_users.pluck(:school_id).first || - self.ec_major_schools.pluck(:school_id).first || - (self.ec_course_users.first && self.ec_course_users.first.try(:ec_course).try(:ec_year).try(:ec_major_school).try(:school_id)) - 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 - - 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 - - def admin_or_business? - admin? || business? - end - - # 149课程的评审用户数据创建(包含创建课堂学生) - def open_class_user - user = User.find_by(login: "OpenClassUser") - unless user - ActiveRecord::Base.transaction do - user_params = {status: 1, login: "OpenClassUser", lastname: "开放课程", - nickname: "开放课程", professional_certification: 1, certification: 1, grade: 0, - password: "12345678", phone: "11122223333", profile_completed: 1} - user = User.create!(user_params) - - UserExtension.create!(user_id: user.id, gender: 0, school_id: 117, :identity => 1, :student_id => "openclassuser") - - subject = Subject.find_by(id: 149) - if subject - subject.courses.each do |course| - CourseMember.create!(course_id: course.id, role: 4, user_id: user.id) if !course.course_students.exists?(user_id: user.id) - end - end - end - end - user - 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 +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 :recent_contacts, through: :private_messages, source: :target + has_many :tidings, :dependent => :destroy + + has_many :games, :dependent => :destroy + has_many :created_subjects, foreign_key: :user_id, class_name: 'Subject' + has_many :subject_members, :dependent => :destroy + has_many :subjects, :through => :subject_members + 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 + + # 众包 + has_many :project_packages, foreign_key: :creator_id, dependent: :destroy + has_many :bidding_users, dependent: :destroy + has_many :bidden_project_packages, through: :bidding_users, source: :project_package + + # 项目 + has_many :applied_projects, dependent: :destroy + + # 教学案例 + has_many :libraries, dependent: :destroy + + # 视频 + has_many :videos, dependent: :destroy + + # 客户管理 + belongs_to :partner, optional: true + + # Groups and active users + scope :active, lambda { where(status: STATUS_ACTIVE) } + + attr_accessor :password, :password_confirmation + + delegate :gender, :department_id, :school_id, :location, :location_city, :technical_title, to: :user_extension, allow_nil: true + + 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.blank? ? "#{login}@educoder.net" : mail + 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 department_name + user_extension&.department&.name || '' + end + + # 课堂的老师(创建者、老师、助教) + def teacher_of_course?(course) + course.course_members.exists?(user_id: id, role: [1,2,3], is_active: 1) || admin? || business? + 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]) + 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? + 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 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 + 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 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 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 ec_school + school_id = self.ec_school_users.pluck(:school_id).first || + self.ec_major_schools.pluck(:school_id).first || + (self.ec_course_users.first && self.ec_course_users.first.try(:ec_course).try(:ec_year).try(:ec_major_school).try(:school_id)) + 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 + + 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 + + def admin_or_business? + admin? || business? + 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 diff --git a/app/services/users/apply_professional_auth_service.rb b/app/services/users/apply_professional_auth_service.rb index 525a01762..74ee08c85 100644 --- a/app/services/users/apply_professional_auth_service.rb +++ b/app/services/users/apply_professional_auth_service.rb @@ -40,7 +40,7 @@ class Users::ApplyProfessionalAuthService < ApplicationService # sms_cache = Rails.cache.read("apply_pro_certification") # if sms_cache.nil? - sms_notify_admin + sms_notify_admin # Rails.cache.write("apply_pro_certification", 1) # end end diff --git a/public/react/src/modules/courses/gradinforms/myysleduinforms.css b/public/react/src/modules/courses/gradinforms/myysleduinforms.css index b1efc783f..8376c63c8 100644 --- a/public/react/src/modules/courses/gradinforms/myysleduinforms.css +++ b/public/react/src/modules/courses/gradinforms/myysleduinforms.css @@ -58,7 +58,7 @@ float: left; padding-top: 31px; padding-left: 25px; - font-size: 16px; + font-size: 21px; color: #05101A; text-align: left; font-weight:bold;