init project

This commit is contained in:
Jasder
2020-03-09 00:40:16 +08:00
commit 2937b2a94d
6549 changed files with 7215173 additions and 0 deletions

View File

@@ -0,0 +1,277 @@
class AccountsController < ApplicationController
#skip_before_action :check_account, :only => [:logout]
def index
render json: session
end
# 其他平台同步注册的用户
def remote_register
username = params[:username]&.gsub(/\s+/, "")
email = params[:email]&.gsub(/\s+/, "")
password = params[:password]
platform = (params[:platform] || 'forge')&.gsub(/\s+/, "")
@user = User.new(admin: false, login: username, mail: email, type: "User")
@user.password = password
@user.platform = platform
@user.activate
ActiveRecord::Base.transaction do
interactor = Gitea::RegisterInteractor.call({username: username, email: email, password: password})
if interactor.success?
gitea_user = interactor.result
result = Gitea::User::GenerateTokenService.new(username, password).call
@user.gitea_token = result['sha1']
@user.gitea_uid = gitea_user['id']
if @user.save!
render_ok({user: {id: @user.id, token: @user.gitea_token}})
end
else
render_error(interactor.error)
end
end
rescue Exception => e
uid_logger_error(e.message)
tip_exception(-1, e.message)
end
# 用户注册
# 注意:用户注册需要兼顾本地版,本地版是不需要验证码及激活码以及使用授权的,注册完成即可使用
# params[:login] 邮箱或者手机号
# params[:code] 验证码
# code_type 1注册手机验证码 8邮箱注册验证码
def register
begin
# 查询验证码是否正确;type只可能是1或者8
type = phone_mail_type(params[:login].strip)
code = params[:code].strip
if type == 1
uid_logger("start register by phone: type is #{type}")
pre = 'p'
email = nil
phone = params[:login]
verifi_code = VerificationCode.where(phone: phone, code: code, code_type: 1).last
else
uid_logger("start register by email: type is #{type}")
pre = 'm'
email = params[:login]
phone = nil
verifi_code = VerificationCode.where(email: email, code: code, code_type: 8).last
end
uid_logger("start register: verifi_code is #{verifi_code}, code is #{code}, time is #{Time.now.to_i - verifi_code.try(:created_at).to_i}")
# check_code = (verifi_code.try(:code) == code.strip && (Time.now.to_i - verifi_code.created_at.to_i) <= 10*60)
# todo 上线前请删除万能验证码"513231"
unless code == "513231" && request.subdomain == "test-newweb"
return normal_status(-2, "验证码不正确") if verifi_code.try(:code) != code.strip
return normal_status(-2, "验证码已失效") if !verifi_code&.effective?
end
return normal_status(-1, "8~16位密码支持字母数字和符号") unless params[:password] =~ CustomRegexp::PASSWORD
code = generate_identifier User, 8, pre
login = pre + code
@user = User.new(admin: false, login: login, mail: email, phone: phone, type: "User")
@user.password = params[:password]
# 现在因为是验证码,所以在注册的时候就可以激活
@user.activate
# 必须要用save操作密码的保存是在users中
if @user.save!
# todo user_extension
UserExtension.create!(user_id: @user.id)
# 注册完成手机号或邮箱想可以奖励500金币
# RewardGradeService.call(
# @user,
# container_id: @user.id,
# container_type: pre == 'p' ? 'Phone' : 'Mail',
# score: 500
# )
# 注册时,记录是否是引流用户
ip = request.remote_ip
ua = UserAgent.find_by_ip(ip)
ua.update_column(:agent_type, UserAgent::USER_REGISTER) if ua
successful_authentication(@user)
# session[:user_id] = @user.id
normal_status("注册成功")
end
rescue Exception => e
uid_logger_error(e.message)
tip_exception(-1, e.message)
end
end
# 用户登录
def login
@user = User.try_to_login(params[:login], params[:password])
return normal_status(-2, "错误的账号或密码") if @user.blank?
# user is already in local database
return normal_status(-2, "违反平台使用规范,账号已被锁定") if @user.locked?
login_control = LimitForbidControl::UserLogin.new(@user)
return normal_status(-2, "登录密码出错已达上限,账号已被锁定, 请#{login_control.forbid_expires/60}分钟后重新登录或找回密码") if login_control.forbid?
password_ok = @user.check_password?(params[:password].to_s)
unless password_ok
if login_control.remain_times-1 == 0
normal_status(-2, "登录密码出错已达上限,账号已被锁定, 请#{login_control.forbid_expires/60}分钟后重新登录或找回密码")
else
normal_status(-2, "你已经输错密码#{login_control.error_times+1}次,还剩余#{login_control.remain_times-1}次机会")
end
login_control.increment!
return
end
successful_authentication(@user)
login_control.clear # 重置每日密码错误次数
# session[:user_id] = @user.id
end
# 忘记密码
def reset_password
begin
code = params[:code]
login_type = phone_mail_type(params[:login].strip)
# 获取验证码
if login_type == 1
phone = params[:login]
verifi_code = VerificationCode.where(phone: phone, code: code, code_type: 2).last
user = User.find_by_phone(phone)
else
email = params[:login]
verifi_code = VerificationCode.where(email: email, code: code, code_type: 3).last
user = User.find_by_mail(email) #这里有问题应该是为email,而不是mail 6.13-hs
end
return normal_status(-2, "验证码不正确") if verifi_code.try(:code) != code.strip
return normal_status(-2, "验证码已失效") if !verifi_code&.effective?
return normal_status(-1, "8~16位密码支持字母数字和符号") unless params[:new_password] =~ CustomRegexp::PASSWORD
user.password, user.password_confirmation = params[:new_password], params[:new_password_confirmation]
ActiveRecord::Base.transaction do
user.save!
LimitForbidControl::UserLogin.new(user).clear
end
sucess_status
rescue Exception => e
uid_logger_error(e.message)
tip_exception(e.message)
end
end
def successful_authentication(user)
uid_logger("Successful authentication start: '#{user.login}' from #{request.remote_ip} at #{Time.now.utc}")
# Valid user
self.logged_user = user
# generate a key and set cookie if autologin
set_autologin_cookie(user)
UserAction.create(:action_id => user.try(:id), :action_type => "Login", :user_id => user.try(:id), :ip => request.remote_ip)
user.update_column(:last_login_on, Time.now)
session[:"#{default_yun_session}"] = user.id
# 注册完成后有一天的试用申请(先去掉)
# UserDayCertification.create(user_id: user.id, status: 1)
end
# def set_autologin_cookie(user)
# token = Token.get_or_create_permanent_login_token(user, "autologin")
# cookie_options = {
# :value => token.value,
# :expires => 1.month.from_now,
# :path => '/',
# :secure => false,
# :httponly => true
# }
# if edu_setting('cookie_domain').present?
# cookie_options = cookie_options.merge(domain: edu_setting('cookie_domain'))
# end
# cookies[autologin_cookie_name] = cookie_options
# logger.info("cookies is #{cookies}")
# end
def logout
UserAction.create(action_id: User.current.id, action_type: "Logout", user_id: User.current.id, :ip => request.remote_ip)
logout_user
render :json => {status: 1, message: "退出成功!"}
end
# 检验邮箱是否已被注册及邮箱或者手机号是否合法
# 参数type为事件类型 1注册2忘记密码3绑定
def valid_email_and_phone
check_mail_and_phone_valid(params[:login], params[:type])
end
# 发送验证码
# params[:login] 手机号或者邮箱号
# params[:type]为事件通知类型 1用户注册注册 2忘记密码 3: 绑定手机 4: 绑定邮箱, 5: 验收手机号有效 # 如果有新的继续后面加
# 发送验证码send_type 1注册手机验证码 2找回密码手机验证码 3找回密码邮箱验证码 4绑定手机 5绑定邮箱
# 6手机验证码登录 7邮箱验证码登录 8邮箱注册验证码 9: 验收手机号有效
def get_verification_code
code = %W(0 1 2 3 4 5 6 7 8 9)
value = params[:login]
type = params[:type].strip.to_i
login_type = phone_mail_type(value)
send_type = verify_type(login_type, type)
verification_code = code.sample(6).join
sign = Digest::MD5.hexdigest("#{OPENKEY}#{value}")
tip_exception(501, "请求不合理") if sign != params[:smscode]
logger.info("########get_verification_code: login_type #{login_type} send_type#{send_type}, ")
# 记录验证码
check_verification_code(verification_code, send_type, value)
sucess_status
end
# 1 手机类型0 邮箱类型
# 注意新版的login是自动名生成的
def phone_mail_type value
value =~ /^1\d{10}$/ ? 1 : 0
end
private
# type 事件类型 1用户注册 2忘记密码 3: 绑定手机 4: 绑定邮箱, 5: 验证手机号是否有效 # 如果有新的继续后面加
# login_type 1手机类型 2邮箱类型
def verify_type login_type, type
case type
when 1
login_type == 1 ? 1 : 8
when 2
login_type == 1 ? 2 : 3
when 3
login_type == 1 ? 4 : tip_exception('请填写正确的手机号')
when 4
login_type == 1 ? tip_exception('请填写正确的邮箱') : 5
when 5
login_type == 1 ? 9 : tip_exception('请填写正确的手机号')
end
end
def generate_login(login)
type = phone_mail_type(login.strip)
if type == 1
uid_logger("start register by phone: type is #{type}")
pre = 'p'
email = nil
phone = login
else
uid_logger("start register by email: type is #{type}")
pre = 'm'
email = login
phone = nil
end
code = generate_identifier User, 8, pre
{ login: pre + code, email: email, phone: phone }
end
def user_params
params.require(:user).permit(:login, :email, :phone)
end
end

View File

@@ -0,0 +1,18 @@
class Admins::AboutsController < Admins::BaseController
def edit
current_doc
end
def update
current_doc.update!(about_us: params[:about_us])
flash[:success] = '保存成功'
redirect_to edit_admins_about_path
end
private
def current_doc
@doc ||= Help.first || Help.create
end
end

View File

@@ -0,0 +1,18 @@
class Admins::AgreementsController < Admins::BaseController
def edit
current_doc
end
def update
current_doc.update!(agreement: params[:agreement])
flash[:success] = '保存成功'
redirect_to edit_admins_agreement_path
end
private
def current_doc
@doc ||= Help.first || Help.create
end
end

View File

@@ -0,0 +1,58 @@
class Admins::AuthSchoolsController < Admins::BaseController
def index
schools = School.where(ec_auth: 1).includes(:users).order("updated_at desc")
@params_page = params[:page] || 1
@schools = paginate schools
end
def destroy
ActiveRecord::Base.transaction do
school = School.where(id: params[:id]).first
school.destroy
render_success_js
end
end
# 工程认证单位列表搜索学校
def search_school
@schools = School.where("ec_auth != 1 and name like '%#{params[:name]}%'").limit(10)
end
# 添加认证学校
def add_school
all_schools = School.all
all_schools.where(id: params[:school_id]).update_all(ec_auth: 1)
schools = all_schools.where(ec_auth: 1).order("updated_at desc")
@params_page = params[:page] || 1
@schools = paginate schools
end
# 搜索用户
def search_manager
school = School.find_by(id: params[:school_id])
user_ids = school&.ec_school_users&.pluck(:user_id)
@users = User.where.not(id: user_ids).where("concat(lastname, firstname) like ?", "%#{params[:name].strip.to_s}%").limit(10)
end
# 添加认证学校管理员
def add_manager
ActiveRecord::Base.transaction do
user_ids = params[:user_id]
@school_id = params[:school_id]
user_ids.each do |id|
EcSchoolUser.create(user_id: id, school_id: @school_id)
end
@school_users = User.where(id: user_ids)
end
end
# 删除学校管理员
def remove_manager
ActiveRecord::Base.transaction do
manager = EcSchoolUser.where(school_id: params[:school_id], user_id: params[:user_id]).first
manager&.destroy
end
end
end

View File

@@ -0,0 +1,45 @@
class Admins::BaseController < ApplicationController
include Base::PaginateHelper
include Admins::RenderHelper
include Base::ErrorRescueHandler
layout 'admin'
skip_before_action :verify_authenticity_token
before_action :require_login, :require_admin!
after_action :rebind_event_if_ajax_render_partial
skip_before_action :check_sign
private
def require_login
return if User.current.logged?
redirect_to "/login?back_url=#{CGI::escape(request.fullpath)}"
end
def require_admin!
return if current_user.blank? || !current_user.logged?
return if current_user.admin_or_business?
render_forbidden
end
# 触发after ajax render partial hooks执行一些因为局部刷新后失效的绑定事件
def rebind_event_if_ajax_render_partial
return if request.format.symbol != :js
return if response.content_type != 'text/javascript'
path = Rails.root.join('app/views/admins/shared/after_render_js_hook.js.erb')
return unless File.exists?(path)
append_js = ERB.new(File.open(path).read).result
response.body += append_js
end
# 重写此方法,防止影响超级管理员端云上实验室功能,因为那里重写了:current_laboratory方法
def setup_laboratory
Laboratory.current = Laboratory.find_by_subdomain(request.subdomain) || Laboratory.find(1)
end
end

View File

@@ -0,0 +1,86 @@
class Admins::CarouselsController < Admins::BaseController
before_action :convert_file!, only: [:create]
helper_method :current_laboratory
def index
@images = current_laboratory.portal_images.order(position: :asc)
end
def create
position = current_laboratory.portal_images.count + 1
ActiveRecord::Base.transaction do
image = current_laboratory.portal_images.create!(create_params.merge(position: position))
file_path = Util::FileManage.disk_filename('PortalImage', image.id)
File.delete(file_path) if File.exist?(file_path) # 删除之前的文件
Util.write_file(@file, file_path)
end
flash[:success] = '保存成功'
redirect_to admins_laboratory_carousels_path(current_laboratory)
end
def update
current_image.update!(update_params)
render_ok
end
def destroy
ActiveRecord::Base.transaction do
current_image.destroy!
# 前移
current_laboratory.portal_images.where('position > ?', current_image.position)
.update_all('position = position - 1')
file_path = Util::FileManage.disk_filename('PortalImage', current_image.id)
File.delete(file_path) if File.exist?(file_path)
end
render_delete_success
end
def drag
move = current_laboratory.portal_images.find_by(id: params[:move_id])
after = current_laboratory.portal_images.find_by(id: params[:after_id])
Admins::DragPortalImageService.call(current_laboratory, move, after)
render_ok
rescue Admins::DragPortalImageService::Error => e
render_error(e.message)
end
private
def current_laboratory
@_current_laboratory ||= Laboratory.find(params[:laboratory_id])
end
def current_image
@_current_image ||= current_laboratory.portal_images.find(params[:id])
end
def create_params
params.require(:portal_image).permit(:name, :link)
end
def update_params
params.permit(:name, :link, :status)
end
def convert_file!
max_size = 10 * 1024 * 1024 # 10M
file = params.dig('portal_image', 'image')
if file.class == ActionDispatch::Http::UploadedFile
@file = file
render_error('请上传文件') if @file.size.zero?
render_error('文件大小超过限制') if @file.size > max_size
else
file = file.to_s.strip
return render_error('请上传正确的图片') if file.blank?
@file = Util.convert_base64_image(file, max_size: max_size)
end
rescue Base64ImageConverter::Error => ex
render_error(ex.message)
end
end

View File

@@ -0,0 +1,11 @@
class Admins::ChooseMirrorRepositoriesController < Admins::BaseController
def new
@mirror = MirrorRepository.find(params[:mirror_id])
@new_mirror = MirrorOperationRecord.where(mirror_repository_id: @mirror.id, status: 1, user_id: -1).first
end
def create
mirror = MirrorRepository.find(params[:mirror_id])
Admins::ChooseMirrorService.call(mirror, current_user, params[:mirror_number])
end
end

View File

@@ -0,0 +1,28 @@
class Admins::ContactUsController < Admins::BaseController
def edit
@cooperations = Cooperation.all.group(:user_type)
@help = Help.first
end
def update
cooperation = Cooperation.find(params[:id])
cooperation.update!(update_cooperation_params)
flash[:success] = '保存成功'
redirect_to edit_admins_contact_us_path
end
def update_address
help = Help.first || Help.create
help.update!(status: params.dig('help', 'status'))
flash[:success] = '保存成功'
redirect_to edit_admins_contact_us_path
end
private
def update_cooperation_params
params.require(:cooperation).permit(:name, :qq, :mail)
end
end

View File

@@ -0,0 +1,84 @@
class Admins::CooperativesController < Admins::BaseController
before_action :convert_file!, only: [:create]
def index
@data = { 'alliance_coop' => [], 'com_coop' => [], 'edu_coop' => [] }
@data = @data.merge CooImg.all.group_by(&:img_type)
end
def create
position = CooImg.where(img_type: create_params[:img_type]).count + 1
ActiveRecord::Base.transaction do
coo = CooImg.create!(create_params.merge(position: position))
file_path = Util::FileManage.disk_filename('CooImg', coo.id)
File.delete(file_path) if File.exist?(file_path) # 删除之前的文件
Util.write_file(@file, file_path)
coo.update!(url_states: Util::FileManage.disk_file_url('CooImg', coo.id))
end
flash[:success] = '保存成功'
redirect_to admins_cooperatives_path
end
def update
current_coo.update!(src_states: params[:url])
render_ok
end
def destroy
ActiveRecord::Base.transaction do
current_coo.destroy!
# 前移
CooImg.where(img_type: current_coo.img_type).where('position > ?', current_coo.position)
.update_all('position = position - 1')
file_path = Util::FileManage.disk_filename('CooImg', current_coo.id)
File.delete(file_path) if File.exist?(file_path)
end
render_delete_success
end
def drag
move = CooImg.find_by(id: params[:move_id])
after = CooImg.find_by(id: params[:after_id])
Admins::DragCooperativeService.call(move, after)
render_ok
rescue Admins::DragCooperativeService::Error => e
render_error(e.message)
end
def replace_image_url
current_coo.update!(url_states: Util::FileManage.disk_file_url('CooImg', current_coo.id))
render_ok
end
private
def current_coo
@_current_coo ||= CooImg.find(params[:id])
end
def create_params
params.require(:coo_img).permit(:img_type, :src_states)
end
def convert_file!
max_size = 10 * 1024 * 1024 # 10M
file = params.dig('coo_img', 'image')
if file.class == ActionDispatch::Http::UploadedFile
@file = file
render_error('请上传文件') if @file.size.zero?
render_error('文件大小超过限制') if @file.size > max_size
else
file = file.to_s.strip
return render_error('请上传正确的图片') if file.blank?
@file = Util.convert_base64_image(file, max_size: max_size)
end
rescue Base64ImageConverter::Error => ex
render_error(ex.message)
end
end

View File

@@ -0,0 +1,35 @@
class Admins::CourseListsController < Admins::BaseController
def index
course_lists = Admins::CourseListQuery.call(params)
@course_lists = paginate course_lists.preload(:courses, :user)
@params_page = params[:page] || 1
respond_to do |format|
format.js
format.html
end
end
def destroy
CourseList.find(params[:id]).destroy!
render_delete_success
end
def merge
origin_course_list = CourseList.find_by!(id: params[:origin_course_list_id])
o_courselist = CourseList.find_by(name: params[:course_list_name])
if o_courselist
origin_course_list.courses.each do |course|
course.update!(name: course.name.sub(origin_course_list.name, params[:course_list_name]), course_list_id: o_courselist.id)
end
origin_course_list.destroy
else
origin_course_list.courses.each do |course|
course.update!(name: course.name.sub(origin_course_list.name, params[:course_list_name]))
end
origin_course_list.update!(name: params[:course_list_name])
end
render_ok
end
end

View File

@@ -0,0 +1,47 @@
class Admins::CoursesController < Admins::BaseController
before_action :find_course, except: [:index]
def index
default_sort('created_at', 'desc')
courses = Admins::CourseQuery.call(params)
@ended_courses = courses.where(is_end: 1).size
@processed_courses = courses.where(is_end: 0).size
@courses = paginate courses.includes(:school, :students, :attachments, :homework_commons, teacher: :user_extension)
respond_to do |format|
format.js
format.html
format.xlsx do
@courses = courses.includes(:school, :students, :attachments, :homework_commons, :course_acts, teacher: :user_extension)
filename = "课堂列表_#{Time.current.strftime('%Y%m%d%H%M%S')}.xlsx"
render xlsx: 'index', filename: filename
end
end
end
def destroy
if @course.is_delete == 0
@course.delete!
Tiding.create!(user_id: current_user.id, trigger_user_id: current_user.id, container_id: @course.id,
container_type: 'DeleteCourse', tiding_type: 'System', belong_container: @course, extra: @course.name)
end
end
def update
unless @course.update_attributes!(setting_params)
redirect_to admins_courses_path
flash[:danger] = "更新失败"
end
end
private
def find_course
@course = Course.find_by!(id: params[:id])
end
def setting_params
params.permit(:homepage_show, :email_notify)
end
end

View File

@@ -0,0 +1,39 @@
class Admins::CustomersController < Admins::BaseController
# skip_before_action :check_sign
helper_method :current_partner
def index
default_sort('created_at', 'desc')
customers = Admins::CustomerQuery.call(params.merge(partner_id: current_partner.id))
@customers = paginate(customers.preload(:school))
end
def create
params[:school_ids] = Array.wrap(params[:school_ids])
school_ids = School.where(id: params[:school_ids]).pluck(:id)
ActiveRecord::Base.transaction do
school_ids.each do |school_id|
next if current_partner.customers.exists?(school_id)
customer = Customer.create!(school_id: school_id)
current_partner.partner_customers.create!(customer: customer)
end
end
render_ok
end
def destroy
current_partner.customers.find(params[:id]).destroy!
render_delete_success
end
private
def current_partner
@_current_partner ||= Partner.find(params[:partner_id])
end
end

View File

@@ -0,0 +1,37 @@
class Admins::DailySchoolStatisticsController < Admins::BaseController
def index
params[:sort_by] = params[:sort_by].presence || :teacher_count
params[:sort_direction] = params[:sort_direction].presence || :desc
total_count, statistics = Admins::SchoolDailyStatisticService.call(params)
@statistics = paginate statistics, total_count: total_count
@params_page = params[:page] || 1
respond_to do |format|
format.html { load_statistic_total }
format.js
end
end
def export
params[:per_page] = 10000
_count, @schools = Admins::SchoolDailyStatisticService.call(params)
filename = ['学校统计总表', params[:keyword], Time.zone.now.strftime('%Y%m%d%H%M%S')].join('-') << '.xlsx'
render xlsx: 'export', filename: filename
end
private
def load_statistic_total
@teacher_total = User.joins(:user_extension).where(user_extensions: { identity: :teacher }).count
@student_total = User.joins(:user_extension).where(user_extensions: { identity: :student }).count
@course_total = Course.count
@active_course_total = Course.where(is_end: false).count
@shixun_homework_total = HomeworkCommon.where(homework_type: 4).count
@other_homework_total = HomeworkCommon.where(homework_type: [1, 3]).count
@shixun_total = Shixun.count
@shixun_evaluate_total = SchoolReport.sum(:shixun_evaluate_count)
end
end

View File

@@ -0,0 +1,51 @@
class Admins::DashboardsController < Admins::BaseController
def index
@active_user_count = User.where(last_login_on: today).count
@weekly_active_user_count = User.where(last_login_on: current_week).count
@month_active_user_count = User.where(last_login_on: current_month).count
@new_user_count = User.where(created_on: current_month).count
end
def month_active_user
count = UserExtension.where(created_at: current_month).group(:identity).count
data = [
{ value: count['teacher'].to_i, name: '老师' },
{ value: count['student'].to_i, name: '学生' },
{ value: count['professional'].to_i, name: '专业人士' },
{ value: count[nil].to_i, name: '未选职业' },
]
render_ok(data: data)
end
def evaluate
names = []
data = []
1.upto(7) do |i|
date = i.days.ago
names.unshift(date.strftime('%Y-%m-%d'))
count = Output.where(created_at: date.beginning_of_day..date.end_of_day).count
data.unshift(count)
end
render_ok(names: names, data: data)
end
private
def today
Time.now.beginning_of_day..Time.now.end_of_day
end
def current_week
7.days.ago.beginning_of_day..Time.now.end_of_day
end
def current_month
30.days.ago.beginning_of_day..Time.now.end_of_day
end
end

View File

@@ -0,0 +1,106 @@
class Admins::DepartmentAppliesController < Admins::BaseController
before_action :get_apply,only:[:agree,:destroy]
def index
params[:status] ||= 0
params[:sort_by] = params[:sort_by].presence || 'created_at'
params[:sort_direction] = params[:sort_direction].presence || 'desc'
applies = Admins::DepartmentApplyQuery.call(params)
@depart_applies = paginate applies.preload(:school,user: :user_extension)
end
def agree
ActiveRecord::Base.transaction do
@depart_apply.update_attribute("status",1)
@depart_apply&.applied_messages&.update_all(status:1)
@depart_apply&.department&.update_attribute("is_auth",1)
@depart_apply&.user&.user_extension&.update_attribute("department_id",@depart_apply.department_id)
render_success_js
end
end
def merge
apply_id = params[:origin_department_id]
apply_add =ApplyAddDepartment.find(apply_id)
origin_id = apply_add&.department_id
new_id = params[:department_id]
return render_error('请选择其它部门') if origin_id.to_s == new_id.to_s
origin_department = apply_add&.department
to_department = Department.find(new_id)
return render_error('部门所属单位不相同') if origin_department&.school_id != to_department&.school_id
ActiveRecord::Base.transaction do
applied_message = AppliedMessage.where(applied_id: origin_id, applied_type: "ApplyAddDepartment")
applied_message.update_all(:status => 4)
apply_add.update_attribute(:status, 2)
apply_add.tidings.update_all(:status => 1)
extra = to_department&.name + "(#{apply_add&.department&.school&.try(:name)})"
tiding_params = {
user_id: apply_add.user_id,
trigger_user_id: 0,
container_id: apply_add.id,
container_type: 'ApplyAddDepartment',
belong_container_id: apply_add&.department&.school_id,
belong_container_type: "School",
tiding_type: "System",
status: 3,
extra: extra
}
Tiding.create(tiding_params)
origin_department.apply_add_departments.delete_all
origin_department.user_extensions.update_all(department_id: new_id)
if to_department.identifier.blank? && origin_department.identifier.present?
to_department.update!(identifier: origin_department.identifier)
end
origin_department.destroy!
apply_add.destroy!
end
render_ok
end
def destroy
ActiveRecord::Base.transaction do
@depart_apply.update_attribute("status",3)
@depart_apply&.applied_messages&.update_all(status:3)
if params[:tip] == 'unapplied' #未审批时候删除
user_extens = UserExtension.where(department_id: @depart_apply.department_id)
user_extens.update_all(department_id:nil)
User.where(id: user_extens.pluck(:user_id)).update_all(profile_completed:false)
tiding_params = {
user_id: @depart_apply.user_id,
trigger_user_id: 0,
container_id: @depart_apply.id,
container_type: 'ApplyAddDepartment',
belong_container_id: @depart_apply&.department&.school_id,
belong_container_type: "School",
tiding_type: "System",
status: 2,
extra: params[:reason]
}
Tiding.create(tiding_params)
end
@depart_apply&.department&.destroy
@depart_apply.destroy
render_success_js
end
end
private
def get_apply
@depart_apply = ApplyAddDepartment.find_by(id:params[:id])
end
end

View File

@@ -0,0 +1,20 @@
class Admins::DepartmentMembersController < Admins::BaseController
helper_method :current_department
def create
Admins::AddDepartmentMemberService.call(current_department, params)
current_department.reload
end
def destroy
@member = current_department.department_members.find_by(user_id: params[:user_id])
@member.destroy! if @member.present?
end
private
def current_department
@_current_department ||= Department.find(params[:department_id])
end
end

View File

@@ -0,0 +1,95 @@
class Admins::DepartmentsController < Admins::BaseController
helper_method :current_department
def index
params[:sort_by] ||= 'created_at'
params[:sort_direction] ||= 'desc'
departments = Admins::DepartmentQuery.call(params)
@departments = paginate departments.preload(:school, :member_users)
department_ids = @departments.map(&:id)
@users_count = UserExtension.where(department_id: department_ids).group(:department_id).count
@professional_auth_count = UserExtension.where(department_id: department_ids)
.joins(:user).where(users: { professional_certification: true })
.group(:department_id).count
end
def create
department_name = params[:department_name].to_s.strip
school = School.find(params[:school_id])
return render_error('部门名称重复') if school.departments.exists?(name: department_name)
ActiveRecord::Base.transaction do
department = school.departments.create!(name: department_name, is_auth: 1)
ApplyAddDepartment.create!(school_id: school.id, status: 1, name: department.name,
department_id: department.id, user_id: current_user.id)
end
render_ok
end
def edit
end
def update
identifier = update_params.delete(:identifier).presence
if identifier && Department.where.not(id: current_department.id).exists?(identifier: identifier)
return render_error('统计链接重复', type: :notify)
end
current_department.update!(update_params.merge(identifier: identifier))
end
def destroy
ActiveRecord::Base.transaction do
current_department.apply_add_departments.update_all(status: 2)
user_ids = current_department.user_extensions.pluck(:user_id)
if user_ids.present?
DeleteDepartmentNotifyJob.perform_later(current_department.id, 0, user_ids)
current_department.soft_delete!
else
current_department.destroy!
end
end
render_delete_success
end
def merge
return render_error('请选择其它部门') if params[:origin_department_id].to_s == params[:department_id].to_s
origin_department = Department.find(params[:origin_department_id])
to_department = Department.find(params[:department_id])
return render_error('部门所属单位不相同') if origin_department.school_id != to_department.school_id
ActiveRecord::Base.transaction do
origin_department.apply_add_departments.delete_all
origin_department.user_extensions.update_all(department_id: to_department.id)
if to_department.identifier.blank? && origin_department.identifier.present?
to_department.update!(identifier: origin_department.identifier)
end
origin_department.destroy!
end
render_ok
end
private
def current_department
@_current_department ||= Department.find(params[:id])
end
def update_params
params.require(:department).permit(:name, :identifier, :host_count)
end
end

View File

@@ -0,0 +1,77 @@
class Admins::DisciplinesController < Admins::BaseController
def index
@disciplines = Discipline.all
end
def create
name = params[:name].to_s.strip
return render_error('名称重复') if Discipline.where(name: name).exists?
Discipline.create!(name: name, position: Discipline.all.pluck(:position).max + 1)
render_ok
end
def edit
@discipline = current_discipline
end
def update
begin
if params[:discipline] && params[:discipline][:name]
name = params[:discipline][:name].to_s.strip
current_discipline.update_attributes!(name: name)
else
ActiveRecord::Base.transaction do
current_discipline.update_attributes!(setting_params)
current_discipline.sub_disciplines.each do |sub|
sub.tag_disciplines.each do |tag|
tag.update_attributes!(setting_params)
end
sub.update_attributes!(setting_params)
end
end
end
rescue Exception => e
@message = e.message
end
@disciplines = Discipline.all
end
def destroy
@discipline_id = params[:id]
ActiveRecord::Base.transaction do
Discipline.where("position > #{current_discipline.position}").update_all("position=position-1")
current_discipline.destroy!
end
end
def adjust_position
max_position = Discipline.all.pluck(:position).max
opr = params[:opr] || "down"
if (params[:opr] == "up" && current_discipline.position == 1) || (params[:opr] == "down" && current_discipline.position == max_position)
@message = "超出范围"
else
ActiveRecord::Base.transaction do
if opr == "up"
Discipline.find_by("position = #{current_discipline.position - 1}")&.update!(position: current_discipline.position)
current_discipline.update!(position: current_discipline.position - 1)
else
Discipline.find_by("position = #{current_discipline.position + 1}")&.update!(position: current_discipline.position)
current_discipline.update!(position: current_discipline.position + 1)
end
end
end
@disciplines = Discipline.all
rescue Exception => e
@message = e.message
end
private
def current_discipline
@_current_discipline = Discipline.find params[:id]
end
def setting_params
params.permit(:shixun, :subject, :question)
end
end

View File

@@ -0,0 +1,41 @@
class Admins::EcTemplatesController < Admins::BaseController
def index
@params_page = params[:page] || 1
templates = EcTemplate.where(nil).includes(:attachments).order("updated_at desc")
@templates = paginate templates
end
def create_template
ActiveRecord::Base.transaction do
if params[:template_id] == "-1"
ec_template = EcTemplate.new(name: params[:name])
ec_template.save
else
ec_template = EcTemplate.find_by(id: params[:template_id])
end
if params[:attachment_id] != "-1"
attachment_id = params[:attachment_id]
attachment_tem = Attachment.find_by(id: attachment_id)
unless attachment_tem.container_id.present? && attachment_tem.container_id == ec_template&.id
attachment_tem.update_attributes(container_id: ec_template&.id, container_type: "EcTemplate")
end
end
@params_page = params[:page] || 1
templates = EcTemplate.where(nil).includes(:attachments).order("updated_at desc")
@templates = paginate templates
end
end
def destroy
ActiveRecord::Base.transaction do
template = EcTemplate.find_by(id: params[:id])
template.destroy
render_success_js
end
end
end

View File

@@ -0,0 +1,30 @@
class Admins::ExaminationAuthenticationsController < Admins::BaseController
def index
params[:status] ||= 'pending'
params[:sort_direction] = params[:status] == 'pending' ? 'asc' : 'desc'
applies = Admins::ApplyItemBankQuery.call(params.merge(type: "ExaminationBank"))
@applies = paginate applies.preload(user: { user_extension: [:school, :department] })
end
def agree
ActiveRecord::Base.transaction do
exam = ExaminationBank.find current_apply.container_id
current_apply.update!(status: 1)
exam.update!(public: 1)
end
render_success_js
end
def refuse
current_apply.update!(status: 2)
render_success_js
end
private
def current_apply
@_current_apply ||= ApplyAction.find(params[:id])
end
end

View File

@@ -0,0 +1,51 @@
class Admins::FilesController < Admins::BaseController
before_action :convert_file!, only: [:create]
def create
File.delete(file_path) if File.exist?(file_path) # 删除之前的文件
Util.write_file(@file, file_path)
render_ok(
source_id: params[:source_id],
source_type: params[:source_type].to_s,
suffix: params[:suffix].presence,
url: file_url
)
rescue StandardError => ex
logger_error(ex)
render_error('上传失败')
end
private
def convert_file!
max_size = 10 * 1024 * 1024 # 10M
if params[:file].class == ActionDispatch::Http::UploadedFile
@file = params[:file]
render_error('请上传文件') if @file.size.zero?
render_error('文件大小超过限制') if @file.size > max_size
else
file = params[:file].to_s.strip
return render_error('请上传正确的图片') if file.blank?
@file = Util.convert_base64_image(file, max_size: max_size)
end
rescue Base64ImageConverter::Error => ex
render_error(ex.message)
end
def file_path
@_file_path ||= begin
case params[:source_type].to_s
when 'Shixun' then
Util::FileManage.disk_filename('Shixun', params[:source_id], params[:suffix].presence)
else
Util::FileManage.disk_filename(params[:source_type].to_s, params[:source_id].to_s, params[:suffix].presence)
end
end
end
def file_url
Util::FileManage.disk_file_url(params[:source_type].to_s, params[:source_id].to_s, params[:suffix].presence)
end
end

View File

@@ -0,0 +1,33 @@
class Admins::GraduationStandardsController < Admins::BaseController
def index
standards = EcGraduationStandard.all.order("updated_at desc")
@params_page = params[:page] || 1
@standards = paginate standards
end
def create_standard
ActiveRecord::Base.transaction do
if params[:graduation_id] == "-1"
content = params[:content]
EcGraduationStandard.create(:content => content)
else
graduation = EcGraduationStandard.find_by(id: params[:graduation_id])
graduation.update_attribute(:content, params[:content])
end
standards = EcGraduationStandard.all.order("updated_at desc")
@params_page = params[:page] || 1
@standards = paginate standards
end
end
def destroy
ActiveRecord::Base.transaction do
@graduation = EcGraduationStandard.find_by(id: params[:id])
@graduation.destroy
render_success_js
end
end
end

View File

@@ -0,0 +1,18 @@
class Admins::HelpCentersController < Admins::BaseController
def edit
current_doc
end
def update
current_doc.update!(help_center: params[:help_center])
flash[:success] = '保存成功'
redirect_to edit_admins_help_center_path
end
private
def current_doc
@doc ||= Help.first || Help.create
end
end

View File

@@ -0,0 +1,45 @@
class Admins::IdentityAuthenticationsController < Admins::BaseController
def index
params[:status] ||= 'pending'
params[:sort_direction] = params[:status] == 'pending' ? 'asc' : 'desc'
applies = Admins::ApplyUserAuthenticationQuery.call(params.merge(type: 1))
@applies = paginate applies.preload(user: { user_extension: [:school, :department] })
end
def agree
Admins::IdentityAuths::AgreeApplyService.call(current_apply)
render_success_js
end
def refuse
Admins::IdentityAuths::RefuseApplyService.call(current_apply, params)
render_success_js
end
def batch_agree
ApplyUserAuthentication.real_name_auth.where(id: params[:ids]).each do |apply|
begin
Admins::IdentityAuths::AgreeApplyService.call(apply)
rescue => e
Util.logger_error(e)
end
end
render_ok
end
def revoke
Admins::IdentityAuths::RevokeApplyService.call(current_apply)
render_success_js
end
private
def current_apply
@_current_apply ||= ApplyUserAuthentication.real_name_auth.find(params[:id])
end
end

View File

@@ -0,0 +1,10 @@
class Admins::ImportCourseMembersController < Admins::BaseController
def create
return render_error('请上传正确的文件') if params[:file].blank? || !params[:file].is_a?(ActionDispatch::Http::UploadedFile)
result = Admins::ImportCourseMemberService.call(params[:file].to_io)
render_ok(result)
rescue Admins::ImportCourseMemberService::Error => ex
render_error(ex)
end
end

View File

@@ -0,0 +1,10 @@
class Admins::ImportDisciplinesController < Admins::BaseController
def create
return render_error('请上传正确的文件') if params[:file].blank? || !params[:file].is_a?(ActionDispatch::Http::UploadedFile)
result = Admins::ImportDisciplineService.call(params[:file].to_io)
render_ok(result)
rescue Admins::ImportDisciplineService::Error => ex
render_error(ex)
end
end

View File

@@ -0,0 +1,10 @@
class Admins::ImportUsersController < Admins::BaseController
def create
return render_error('请上传正确的文件') if params[:file].blank? || !params[:file].is_a?(ActionDispatch::Http::UploadedFile)
result = Admins::ImportUserService.call(params[:file].to_io)
render_ok(result)
rescue Admins::ImportUserService::Error => ex
render_error(ex)
end
end

View File

@@ -0,0 +1,34 @@
class Admins::ItemAuthenticationsController < Admins::BaseController
def index
params[:status] ||= 'pending'
params[:sort_direction] = params[:status] == 'pending' ? 'asc' : 'desc'
applies = Admins::ApplyItemBankQuery.call(params.merge(type: "ItemBank"))
@applies = paginate applies.preload(user: { user_extension: [:school, :department] })
end
def show
@item = ItemBank.find current_apply.container_id
end
def agree
ActiveRecord::Base.transaction do
item = ItemBank.find current_apply.container_id
current_apply.update!(status: 1)
item.update!(public: 1)
end
render_success_js
end
def refuse
current_apply.update!(status: 2)
render_success_js
end
private
def current_apply
@_current_apply ||= ApplyAction.find(params[:id])
end
end

View File

@@ -0,0 +1,85 @@
class Admins::LaboratoriesController < Admins::BaseController
def index
default_sort('id', 'desc')
laboratories = Admins::LaboratoryQuery.call(params)
@laboratories = paginate laboratories.preload(:school, :laboratory_users)
end
def create
Admins::CreateLaboratoryService.call(create_params)
render_ok
rescue Admins::CreateLaboratoryService::Error => ex
render_error(ex.message)
end
def destroy
current_laboratory.destroy!
render_delete_success
end
def shixuns_for_select
except_shixun_ids = current_laboratory.laboratory_shixuns.pluck(:shixun_id)
shixuns = Shixun.where.not(id: except_shixun_ids)
keyword = params[:keyword].to_s.strip
if keyword.present?
like_sql = 'shixuns.name LIKE :keyword OR CONCAT(users.lastname, users.firstname) LIKE :keyword '\
'OR mirror_repositories.name LIKE :keyword'
shixuns = shixuns.joins(:user, :mirror_repositories).where(like_sql, keyword: "%#{keyword}%")
end
@count = shixuns.count
@shixuns = paginate(shixuns.includes(:user))
end
def subjects_for_select
except_subject_ids = current_laboratory.laboratory_subjects.pluck(:subject_id)
subjects = Subject.where.not(id: except_subject_ids)
keyword = params[:keyword].to_s.strip
if keyword.present?
like_sql = 'subjects.name LIKE :keyword OR CONCAT(users.lastname, users.firstname) LIKE :keyword'
subjects = subjects.joins(:user).where(like_sql, keyword: "%#{keyword}%")
end
@count = subjects.count
@subjects = paginate(subjects.includes(:user))
end
def synchronize_user
school = current_laboratory.school
users = User.joins(:user_extension).where(user_extensions: {school_id: school.id})
users.update_all(laboratory_id: current_laboratory.id)
end
def update_sync_course
current_laboratory.update!(sync_course: !current_laboratory.sync_course)
@laboratory = current_laboratory
end
def update
@laboratory = current_laboratory
unless @laboratory.update_attributes!(setting_params)
redirect_to admins_laboratories_path
flash[:danger] = "更新失败"
end
end
private
def current_laboratory
@_current_laboratory ||= Laboratory.find(params[:id])
end
def create_params
params.permit(:school_id)
end
def setting_params
params.permit(:sync_course, :sync_subject, :sync_shixun)
end
end

View File

@@ -0,0 +1,23 @@
class Admins::LaboratorySettingsController < Admins::BaseController
def show
@laboratory = current_laboratory
end
def update
Admins::SaveLaboratorySettingService.call(current_laboratory, form_params)
render_ok
end
private
def current_laboratory
@_current_laboratory ||= Laboratory.find(params[:laboratory_id])
end
def form_params
params.permit(:identifier, :name,
:nav_logo, :login_logo, :tab_logo, :oj_banner,
:subject_banner, :course_banner, :competition_banner, :moop_cases_banner,
:footer, navbar: %i[name link hidden])
end
end

View File

@@ -0,0 +1,48 @@
class Admins::LaboratoryShixunsController < Admins::BaseController
helper_method :current_laboratory, :current_laboratory_shixun
def index
laboratory_shixuns = Admins::LaboratoryShixunQuery.call(current_laboratory, params)
@laboratory_shixuns = paginate laboratory_shixuns.includes(shixun: %i[tag_repertoires user])
end
def create
shixun_ids = Array.wrap(params[:shixun_ids])
shixun_ids = Shixun.where(id: shixun_ids).pluck(:id)
exist_shixun_id = current_laboratory.laboratory_shixuns.where(shixun_id: shixun_ids).pluck(:shixun_id)
LaboratoryShixun.bulk_insert(*%i[shixun_id laboratory_id created_at updated_at]) do |worker|
(shixun_ids - exist_shixun_id).each do |shixun_id|
worker.add(shixun_id: shixun_id, laboratory_id: current_laboratory.id)
end
end
render_ok
end
def destroy
return render_js_error('不能删除自建实训', type: :notify) if current_laboratory_shixun.ownership?
current_laboratory_shixun.destroy!
render_delete_success
end
def homepage
current_laboratory_shixun.update!(homepage: true)
render_ok
end
def cancel_homepage
current_laboratory_shixun.update!(homepage: false)
render_ok
end
private
def current_laboratory
@_current_laboratory ||= Laboratory.find(params[:laboratory_id])
end
def current_laboratory_shixun
@_current_laboratory_shixun ||= current_laboratory.laboratory_shixuns.find(params[:id])
end
end

View File

@@ -0,0 +1,51 @@
class Admins::LaboratorySubjectsController < Admins::BaseController
helper_method :current_laboratory, :current_laboratory_subject
def index
laboratory_subjects = Admins::LaboratorySubjectQuery.call(current_laboratory, params)
includes_tables = { subject: [:repertoire, :subject_level_system, user: {user_extension: :school}] }
@laboratory_subjects = paginate(laboratory_subjects.includes(includes_tables))
end
def create
subject = Subject.find(params[:subject_id])
Subjects::CopySubjectService.call(subject, current_user, current_laboratory)
render_ok
end
def destroy
return render_js_error('不能删除自建课程', type: :notify) if current_laboratory_subject.ownership?
ActiveRecord::Base.transaction do
current_subject = current_laboratory_subject.subject
# 实训软删除,并解除与子站的关联
current_laboratory.laboratory_shixuns.where(shixun_id: current_subject.shixuns).destroy_all
current_subject.shixuns.update_all(status: -1)
current_subject.destroy!
render_delete_success
end
end
def homepage
current_laboratory_subject.update!(homepage: true)
render_ok
end
def cancel_homepage
current_laboratory_subject.update!(homepage: false)
render_ok
end
private
def current_laboratory
@_current_laboratory ||= Laboratory.find(params[:laboratory_id])
end
def current_laboratory_subject
@_current_laboratory_subject ||= current_laboratory.laboratory_subjects.find(params[:id])
end
end

View File

@@ -0,0 +1,19 @@
class Admins::LaboratoryUsersController < Admins::BaseController
helper_method :current_laboratory
def create
Admins::AddLaboratoryUserService.call(current_laboratory, params.permit(user_ids: []))
current_laboratory.reload
end
def destroy
@laboratory_user = current_laboratory.laboratory_users.find_by(user_id: params[:user_id])
@laboratory_user.destroy! if @laboratory_user.present?
end
private
def current_laboratory
@_current_laboratory ||= Laboratory.find(params[:laboratory_id])
end
end

View File

@@ -0,0 +1,25 @@
class Admins::LibraryAppliesController < Admins::BaseController
def index
params[:status] ||= 'pending'
applies = Admins::LibraryApplyQuery.call(params)
@library_applies = paginate applies.preload(library: :user)
end
def agree
Libraries::AgreeApplyService.new(current_library_apply, current_user).call
render_success_js
end
def refuse
Libraries::RefuseApplyService.new(current_library_apply, current_user, reason: params[:reason]).call
render_success_js
end
private
def current_library_apply
@_current_library_apply ||= LibraryApply.find(params[:id])
end
end

View File

@@ -0,0 +1,8 @@
class Admins::MajorInformationsController < Admins::BaseController
def index
disciplines = EcDiscipline.includes(ec_discipline_firsts: {ec_majors: :schools}).order("ec_disciplines.code asc")
@disciplines = paginate disciplines
end
end

View File

@@ -0,0 +1,96 @@
class Admins::MirrorRepositoriesController < Admins::BaseController
before_action :check_shixun_mirrors!, only: [:index]
def index
mirrors = MirrorRepository.all
mirrors = mirrors.reorder(status: :desc, main_type: :desc, type_name: :asc)
@mirrors = paginate mirrors.includes(:mirror_scripts)
@error_mirror_names = MirrorRepository.where(status: 5).pluck(:name)
end
def new
@mirror = MirrorRepository.new
end
def create
@mirror = MirrorRepository.new
Admins::SaveMirrorRepositoryService.call(@mirror, current_user, form_params)
flash[:success] = '保存成功'
redirect_to edit_admins_mirror_repository_path(@mirror)
rescue ActiveRecord::RecordInvalid
flash.now[:danger] = '保存失败'
render 'new'
rescue Admins::SaveMirrorRepositoryService::Error => ex
flash.now[:danger] = ex.message
render 'new'
end
def edit
@mirror = current_mirror
end
def update
@mirror = current_mirror
Admins::SaveMirrorRepositoryService.call(current_mirror, current_user, form_params)
flash[:success] = '保存成功'
redirect_to edit_admins_mirror_repository_path(current_mirror)
rescue ActiveRecord::RecordInvalid
flash.now[:danger] = '保存失败'
render 'edit'
rescue Admins::SaveMirrorRepositoryService::Error => ex
flash.now[:danger] = ex.message
render 'edit'
end
def destroy
return render_js_error('该状态下不允许删除') unless current_mirror.deletable?
current_mirror.destroy!
render_delete_success
end
def for_select
mirrors = MirrorRepository.all
keyword = params[:keyword].to_s.strip
mirrors = mirrors.where('name LIKE ?', "%#{keyword}%") if keyword.present?
@mirrors = paginate mirrors
render_ok(count: @mirrors.total_count, mirrors: @mirrors.as_json(only: %i[id name]))
end
def merge
origin_mirror = MirrorRepository.find(params[:mirror_id])
mirror = MirrorRepository.find(params[:new_mirror_id])
ActiveRecord::Base.transaction do
origin_mirror.update!(name: mirror.name, mirrorID: mirror.mirrorID)
mirror.destroy!
end
end
private
def current_mirror
@_current_mirror ||= MirrorRepository.find(params[:id])
end
def form_params
columns = %i[type_name main_type time_limit resource_limit cpu_limit memory_limit description status]
params.require(:mirror_repository).permit(*columns)
end
def check_shixun_mirrors!
return unless request.format.html?
Admins::CheckShixunMirrorsService.call
rescue Admins::CheckShixunMirrorsService::Error => e
internal_server_error(e.message)
end
end

View File

@@ -0,0 +1,59 @@
class Admins::MirrorScriptsController < Admins::BaseController
helper_method :current_mirror
def index
scripts = current_mirror.mirror_scripts.order(updated_at: :desc)
@scripts = paginate scripts
end
def new
@script = current_mirror.mirror_scripts.new
end
def create
@script = current_mirror.mirror_scripts.new(form_params)
if @script.save
flash[:success] = '保存成功'
redirect_to edit_admins_mirror_repository_mirror_script_path(current_mirror, @script)
else
flash[:danger] = '保存失败'
render 'new'
end
end
def edit
@script = current_script
end
def update
@script = current_script
if @script.update(form_params)
flash[:success] = '保存成功'
redirect_to edit_admins_mirror_repository_mirror_script_path(current_mirror, @script)
else
flash[:danger] = '保存失败'
render 'edit'
end
end
def destroy
current_script.destroy!
render_delete_success
end
private
def current_script
@_current_script ||= current_mirror.mirror_scripts.find(params[:id])
end
def current_mirror
@_current_mirror ||= MirrorRepository.find(params[:mirror_repository_id])
end
def form_params
params.require(:mirror_script).permit(:script_type, :description, :script)
end
end

View File

@@ -0,0 +1,29 @@
class Admins::PartnersController < Admins::BaseController
def index
default_sort('created_at', 'desc')
partners = Admins::PartnerQuery.call(params)
@partners = paginate(partners.preload(:school))
end
def create
params[:school_ids] = Array.wrap(params[:school_ids])
school_ids = School.where(id: params[:school_ids]).pluck(:id)
exist_school_ids = Partner.where(school_id: school_ids).pluck(:school_id)
Partner.bulk_insert(*%i[school_id created_at updated_at]) do |worker|
(school_ids - exist_school_ids).each do |school_id|
worker.add(school_id: school_id)
end
end
render_ok
end
def destroy
Partner.find(params[:id]).destroy!
render_delete_success
end
end

View File

@@ -0,0 +1,44 @@
class Admins::ProfessionalAuthenticationsController < Admins::BaseController
def index
params[:status] ||= 'pending'
params[:sort_direction] = params[:status] == 'pending' ? 'asc' : 'desc'
applies = Admins::ApplyUserAuthenticationQuery.call(params.merge(type: 2))
@applies = paginate applies.preload(user: { user_extension: [:school, :department] })
end
def agree
Admins::ProfessionalAuths::AgreeApplyService.call(current_apply)
render_success_js
end
def refuse
Admins::ProfessionalAuths::RefuseApplyService.call(current_apply, params)
render_success_js
end
def batch_agree
ApplyUserAuthentication.professional_auth.where(id: params[:ids]).each do |apply|
begin
Admins::ProfessionalAuths::AgreeApplyService.call(apply)
rescue => e
Util.logger_error(e)
end
end
render_ok
end
def revoke
Admins::ProfessionalAuths::RevokeApplyService.call(current_apply)
render_success_js
end
private
def current_apply
@_current_apply ||= ApplyUserAuthentication.professional_auth.find(params[:id])
end
end

View File

@@ -0,0 +1,37 @@
class Admins::ProjectPackageAppliesController < Admins::BaseController
before_action :current_apply,only: [:agree,:refuse]
def index
params[:status] ||= 'pending'
status = params[:status]
if status == 'all'
status = %w(agreed refused)
end
package_applies = ProjectPackageApply.where(status: status)
keyword = params[:keyword].to_s.strip || ""
if keyword.present?
package_applies = package_applies.joins(:project_package).where("project_packages.title like ?","%#{keyword}%")
end
@package_applies = paginate package_applies.includes(project_package: { creator: :user_extension })
end
def agree
ProjectPackages::AgreeApplyService.new(current_apply).call
render_success_js
rescue ProjectPackages::AgreeApplyService::Error => e
render json: { status: -1, message: e.message }
end
def refuse
ProjectPackages::RefuseApplyService.new(current_apply, reason: params[:reason]).call
render_success_js
rescue ProjectPackages::RefuseApplyService::Error => e
render json: { status: -1, message: e.message }
end
private
def current_apply
@_current_apply ||= ProjectPackageApply.find(params[:id])
end
end

View File

@@ -0,0 +1,70 @@
class Admins::ProjectStatisticsController < Admins::BaseController
def index
projects = Project.project_statics_select.all
by_time = params[:time]
project_type = params[:project_type]
is_private = params[:is_private]
project_category_id = params[:project_category_id]
project_language_id = params[:project_language_id]
license_id = params[:license_id]
projects = projects.where(project_type: project_type) if project_type.present?
projects = projects.where(is_private: is_private) if is_private.present?
projects = projects.where(project_category_id: project_category_id) if project_category_id.present?
projects = projects.where(project_language_id: project_language_id) if project_language_id.present?
projects = projects.where(license_id: license_id) if license_id.present?
if by_time.present?
case by_time.to_s
when "week"
projects = projects.group_by_week(:created_on).size
when "month"
projects = projects.group_by_month(:created_on).size
when "quarter"
projects = projects.group_by_month(:created_on).size
when "year"
projects = projects.group_by_year(:created_on).size
else
projects = projects.group_by_day(:created_on).size
end
end
@projects = projects
end
def visits_static
projects = Project.project_statics_select.all
by_time = params[:time]
project_type = params[:project_type]
is_private = params[:is_private]
project_category_id = params[:project_category_id]
project_language_id = params[:project_language_id]
license_id = params[:license_id]
projects = projects.where(project_type: project_type) if project_type.present?
projects = projects.where(is_private: is_private) if is_private.present?
projects = projects.where(project_category_id: project_category_id) if project_category_id.present?
projects = projects.where(project_language_id: project_language_id) if project_language_id.present?
projects = projects.where(license_id: license_id) if license_id.present?
if by_time.present?
case by_time.to_s
when "week"
projects = projects.group_by_week(:created_on).size
when "month"
projects = projects.group_by_month(:created_on).size
when "quarter"
projects = projects.group_by_month(:created_on).size
when "year"
projects = projects.group_by_year(:created_on).size
else
projects = projects.group_by_day(:created_on).size
end
end
@projects = projects
end
end

View File

@@ -0,0 +1,25 @@
class Admins::ProjectsController < Admins::BaseController
def index
default_sort('created_at', 'desc')
search = params[:search].to_s.strip
projects = Project.where("name like ?", "%#{search}%")
@projects = paginate projects.includes(:owner, :members, :issues, :versions, :attachments, :project_score)
end
def destroy
project = Project.find_by!(id: params[:id])
ActiveRecord::Base.transaction do
g = Gitlab.client
g.delete_project(project.gpid)
# 删除Trustie版本库记录
repoisitory = Repository.where(project_id: project.id, type: "Repository::Gitlab").first
repoisitory.destroy!
Tiding.where(container_id: project.id, container_type: ["JoinProject", "DealProject", "ReporterJoinProject", "ManagerJoinProject"]).destroy_all
project.destroy!
render_delete_success
end
end
end

View File

@@ -0,0 +1,36 @@
class Admins::RepertoiresController < Admins::BaseController
def index
@repertoires = Repertoire.all
end
def edit
@repertoire = current_repertoire
end
def update
Rails.logger.info("#################--------")
if params[:repertoire] && params[:repertoire][:name].present?
name = params[:repertoire][:name].to_s.strip
current_repertoire.update_attributes!(name: name)
end
@repertoires = Repertoire.all
end
def create
name = params[:name].to_s.strip
return render_error('名称重复') if Repertoire.where(name: name).exists?
Repertoire.create!(name: name)
render_ok
end
def destroy
@repertoire_id = params[:id]
current_repertoire.destroy!
end
private
def current_repertoire
@_current_repertoire = Repertoire.find params[:id]
end
end

View File

@@ -0,0 +1,35 @@
class Admins::SalesmanChannelsController < Admins::BaseController
before_action :set_salesman
def index
@channels = @salesman.salesman_channels
if params[:keyword].present?
@channels = @channels.joins(:school).where("schools.name like ?", "%#{params[:keyword]}%")
end
@start_time = params[:start_date]
@end_time = params[:end_date].blank? ? Time.now : params[:end_date]
end
def batch_add
channel_ids = @salesman.salesman_channels.pluck(:school_id)
school_ids = params[:school_ids] - channel_ids
school_ids.each do |school_id|
school = School.find_by(id: school_id)
next if school.blank? || @salesman.salesman_channels.where(school_id: school.id).exists?
@salesman.salesman_channels.create!(school_id: school.id)
end
render_ok
rescue Exception => ex
render_error(ex.message)
end
def destroy
@salesman.salesman_channels.find_by!(id: params[:id]).destroy
end
private
def set_salesman
@salesman = Salesman.find params[:salesman_id]
end
end

View File

@@ -0,0 +1,28 @@
class Admins::SalesmanCustomersController < Admins::BaseController
before_action :set_salesman
def index
@customers = @salesman.salesman_customers.includes(:user, :school)
end
def batch_add
customer_ids = @salesman.salesman_customers.pluck(:user_id)
user_ids = params[:user_ids] - customer_ids
user_ids.each do |user_id|
user = UserExtension.find_by(user_id: user_id)
next if user.blank? || @salesman.salesman_customers.where(user_id: user.user_id).exists?
@salesman.salesman_customers.create!(user_id: user.user_id, school_id: user.school_id)
end
render_ok
end
def destroy
@salesman.salesman_customers.find_by!(id: params[:id]).destroy
end
private
def set_salesman
@salesman = Salesman.find params[:salesman_id]
end
end

View File

@@ -0,0 +1,29 @@
class Admins::SalesmansController < Admins::BaseController
before_action :set_salesman, except: [:index, :batch_add]
def index
@salesmans = Salesman.all
end
def destroy
@salesman.destroy!
end
# 批量增加销售人员
def batch_add
salesman_user_ids = Salesman.where(id: params[:user_ids]).pluck(:user_id)
user_ids = params[:user_ids] - salesman_user_ids
user_ids.each do |user_id|
user = User.find_by(id: user_id)
next if user.blank?
Salesman.create!(user_id: user.id, name: user.real_name)
end
render_ok
end
private
def set_salesman
@salesman = Salesman.find params[:id]
end
end

View File

@@ -0,0 +1,50 @@
class Admins::SchoolStatisticsController < Admins::BaseController
before_action :contrast_column_select_options, only: [:contrast]
def index
params[:data_type] ||= 'grow'
params[:sort_by] = params[:sort_by].presence || :teacher_increase_count
params[:sort_direction] = params[:sort_direction].presence || :desc
service = Admins::StatisticSchoolDataGrowService.new(params)
@grow_summary = service.grow_summary
total_count, statistics = service.call
@params_page = params[:page] || 1
@statistics = paginate statistics, total_count: total_count
end
def contrast
params[:contrast_column] = params[:contrast_column].presence || :teacher_increase_count
params[:sort_direction] ||= :desc
params[:sort_by] = :percentage
# 无对比日期时直接返回无数据页面
if useless_contrast_date_parameter?
@total_count = 0
@statistics = paginate([])
return
end
total_count, statistics = Admins::StatisticSchoolContrastDataService.call(params)
@statistics = paginate statistics, total_count: total_count
rescue Admins::StatisticSchoolContrastDataService::ParameterError
render_unprocessable_entity('参数错误')
end
private
def useless_contrast_date_parameter?
params[:begin_date].blank? && params[:end_date].blank? &&
params[:other_begin_date].blank? &&params[:other_end_date].blank?
end
def contrast_column_select_options
@select_options =
Admins::StatisticSchoolContrastDataService::CONTRAST_COLUMN_LIST.map do |column|
[I18n.t("school_daily_report.#{column}"), column]
end
end
end

View File

@@ -0,0 +1,30 @@
class Admins::SchoolsController < Admins::BaseController
def index
params[:sort_by] ||= 'created_at'
params[:sort_direction] ||= 'desc'
schools = Admins::SchoolQuery.call(params)
@total_count = schools.map(&:id).count
@schools = paginate schools
school_ids = @schools.map(&:id)
@department_count = Department.where(school_id: school_ids).group(:school_id).count
end
def destroy
users = User.joins(:user_extension).where(user_extensions: { school_id: current_school.id })
ActiveRecord::Base.transaction do
users.update_all(profile_completed: false)
current_school.destroy!
end
render_delete_success
end
private
def current_school
@_current_school ||= School.find(params[:id])
end
end

View File

@@ -0,0 +1,84 @@
class Admins::SubDisciplinesController < Admins::BaseController
def index
@discipline = current_discipline
@sub_disciplines = current_discipline.sub_disciplines
end
def create
name = params[:name].to_s.strip
return render_error('名称不能为空') if name.blank?
return render_error('名称重复') if current_discipline.sub_disciplines.where(name: name).exists?
SubDiscipline.create!(name: name, discipline_id: current_discipline.id, position: current_discipline.sub_disciplines.pluck(:position).max + 1)
render_ok
end
def edit
@sub_discipline = current_sub_discipline
end
def update
begin
if params[:sub_discipline] && params[:sub_discipline][:name]
name = params[:sub_discipline][:name].to_s.strip
current_sub_discipline.update_attributes!(name: name)
else
ActiveRecord::Base.transaction do
current_sub_discipline.update_attributes!(setting_params)
current_sub_discipline.tag_disciplines.each do |tag|
tag.update_attributes!(setting_params)
end
end
end
rescue Exception => e
@message = e.message
end
@sub_disciplines = current_sub_discipline.discipline&.sub_disciplines
end
def destroy
@sub_discipline_id = params[:id]
ActiveRecord::Base.transaction do
discipline = current_sub_discipline.discipline
discipline.sub_disciplines.where("position > #{current_sub_discipline.position}").update_all("position=position-1")
current_sub_discipline.destroy!
end
end
def adjust_position
discipline = current_sub_discipline.discipline
max_position = discipline.sub_disciplines.pluck(:position).max
opr = params[:opr] || "down"
if (params[:opr] == "up" && current_sub_discipline.position == 1) || (params[:opr] == "down" && current_sub_discipline.position == max_position)
@message = "超出范围"
else
ActiveRecord::Base.transaction do
if opr == "up"
discipline.sub_disciplines.find_by("position = #{current_sub_discipline.position - 1}")&.update!(position: current_sub_discipline.position)
current_sub_discipline.update!(position: current_sub_discipline.position - 1)
else
discipline.sub_disciplines.find_by("position = #{current_sub_discipline.position + 1}")&.update!(position: current_sub_discipline.position)
current_sub_discipline.update!(position: current_sub_discipline.position + 1)
end
end
end
@sub_disciplines = discipline&.sub_disciplines
rescue Exception => e
@message = e.message
end
private
def current_sub_discipline
@_current_sub_discipline = SubDiscipline.find params[:id]
end
def current_discipline
@_current_discipline = Discipline.find params[:discipline_id]
end
def setting_params
params.permit(:shixun, :subject, :question)
end
end

View File

@@ -0,0 +1,45 @@
class Admins::SubRepertoiresController < Admins::BaseController
def index
@repertoire = current_repertoire
@sub_repertoires = current_repertoire.sub_repertoires
end
def create
name = params[:name].to_s.strip
return render_error('名称重复') if current_repertoire.sub_repertoires.where(name: name).exists?
SubRepertoire.create!(name: name, repertoire_id: current_repertoire.id)
render_ok
end
def edit
@sub_repertoire = current_sub_repertoire
end
def update
if params[:sub_repertoire] && params[:sub_repertoire][:name].present?
name = params[:sub_repertoire][:name].to_s.strip
current_sub_repertoire.update_attributes!(name: name)
end
@sub_repertoires = current_sub_repertoire.repertoire&.sub_repertoires
end
def destroy
@sub_repertoire_id = params[:id]
current_sub_repertoire.destroy!
end
private
def current_sub_repertoire
@_current_sub_repertoire = SubRepertoire.find params[:id]
end
def current_repertoire
@_current_repertoire = Repertoire.find params[:repertoire_id]
end
def setting_params
params.permit(:shixun, :subject, :question)
end
end

View File

@@ -0,0 +1,49 @@
class Admins::SubjectAuthorizationsController < Admins::BaseController
def index
params[:status] ||= 'pending'
applies = ApplyAction.where(container_type: 'ApplySubject')
status =
case params[:status]
when 'pending' then 0
when 'processed' then [1, 2]
when 'agreed' then 1
when 'refused' then 2
else 0
end
applies = applies.where(status: status) if status.present?
# 关键字模糊查询
keyword = params[:keyword].to_s.strip
if keyword.present?
applies = applies.joins('JOIN subjects ON subjects.id = apply_actions.container_id')
.where('subjects.name LIKE :keyword', keyword: "%#{keyword}%")
end
applies = applies.order(updated_at: :desc)
@applies = paginate applies.includes(user: :user_extension)
subject_ids = @applies.map(&:container_id)
@subject_map = Subject.where(id: subject_ids).each_with_object({}) { |s, h| h[s.id] = s }
@challenge_count_map = Challenge.joins(shixun: :stage_shixuns).where(st: 0, stage_shixuns: { subject_id: subject_ids}).group('subject_id').count
end
def agree
Admins::SubjectAuths::AgreeApplyService.call(current_apply, current_user)
render_success_js
end
def refuse
Admins::SubjectAuths::RefuseApplyService.call(current_apply, current_user, params)
render_success_js
end
private
def current_apply
@_current_apply ||= ApplyAction.where(container_type: 'ApplySubject').find(params[:id])
end
end

View File

@@ -0,0 +1,34 @@
class Admins::SubjectSettingsController < Admins::BaseController
def index
default_sort('created_at', 'desc')
subjects = Admins::SubjectQuery.call(params)
@sub_disciplines = SubDiscipline.where(subject: 1).pluck(:name,:id)
@subjects = paginate subjects.includes(:repertoire, :subject_level_system, :sub_disciplines)
end
def update
sub_discipline_ids = params[:sub_disciplines] || []
sub_ids = sub_discipline_ids.reject(&:blank?).map(&:to_i)
old_sub_ids = current_subject.sub_discipline_containers.pluck(:sub_discipline_id)
new_ids = sub_ids - old_sub_ids
delete_ids = old_sub_ids - sub_ids
sub_params = new_ids.map{|sub| {sub_discipline_id: sub}}
ActiveRecord::Base.transaction do
current_subject.sub_discipline_containers.where(sub_discipline_id: delete_ids).destroy_all
current_subject.sub_discipline_containers.create!(sub_params)
end
end
def update_mobile_show
subject = Subject.find(params[:subject_id])
subject.update_attributes(:show_mobile => params[:show_mobile])
end
private
def current_subject
@_current_subject ||= Subject.find(params[:id])
end
end

View File

@@ -0,0 +1,71 @@
class Admins::SubjectsController < Admins::BaseController
def index
default_sort('created_at', 'desc')
subjects = Admins::SubjectQuery.call(params)
@subjects = paginate subjects.includes(user: { user_extension: :school })
end
def edit
@subject = current_subject
end
def update
current_subject.update!(update_params)
flash[:success] = '保存成功'
redirect_to admins_subject_settings_path
end
def destroy
current_subject.destroy!
render_delete_success
end
# 隐藏
def hide
current_subject.update!(hidden: true)
render_ok
end
# 展示
def cancel_hide
current_subject.update!(hidden: false)
render_ok
end
# 设为主页展示
def homepage_show
current_subject.update!(homepage_show: true)
render_ok
end
# 取消主页展示
def cancel_homepage_show
current_subject.update!(homepage_show: false)
render_ok
end
# 设为金课
def excellent
current_subject.update!(excellent: true, public: 2)
render_ok
end
# 取消金课
def cancel_excellent
current_subject.update!(excellent: false)
render_ok
end
private
def current_subject
@_current_subject ||= Subject.find(params[:id])
end
def update_params
params.require(:subject).permit(:repertoire_id, :subject_level_system_id, :student_count)
end
end

View File

@@ -0,0 +1,79 @@
class Admins::TagDisciplinesController < Admins::BaseController
def index
@sub_discipline = current_sub_discipline
@tag_disciplines = current_sub_discipline.tag_disciplines
end
def create
name = params[:name].to_s.strip
return render_error('名称重复') if current_sub_discipline.tag_disciplines.where(name: name).exists?
TagDiscipline.create!(name: name, sub_discipline_id: current_sub_discipline.id, user_id: current_user.id,
position: current_sub_discipline.tag_disciplines.pluck(:position).max + 1)
render_ok
end
def edit
@tag_discipline = current_tag_discipline
end
def update
begin
if params[:tag_discipline] && params[:tag_discipline][:name]
name = params[:tag_discipline][:name].to_s.strip
current_tag_discipline.update_attributes!(name: name)
else
current_tag_discipline.update_attributes!(setting_params)
end
rescue Exception => e
@message = e.message
end
@tag_disciplines = current_tag_discipline.sub_discipline&.tag_disciplines
end
def destroy
@tag_discipline_id = params[:id]
ActiveRecord::Base.transaction do
sub_discipline = current_tag_discipline.sub_discipline
sub_discipline.tag_disciplines.where("position > #{current_tag_discipline.position}").update_all("position=position-1")
current_tag_discipline.destroy!
end
end
def adjust_position
sub_discipline = current_tag_discipline.sub_discipline
max_position = sub_discipline.tag_disciplines.pluck(:position).max
opr = params[:opr] || "down"
if (params[:opr] == "up" && current_tag_discipline.position == 1) || (params[:opr] == "down" && current_tag_discipline.position == max_position)
@message = "超出范围"
else
ActiveRecord::Base.transaction do
if opr == "up"
sub_discipline.tag_disciplines.find_by("position = #{current_tag_discipline.position - 1}")&.update!(position: current_tag_discipline.position)
current_tag_discipline.update!(position: current_tag_discipline.position - 1)
else
sub_discipline.tag_disciplines.find_by("position = #{current_tag_discipline.position + 1}")&.update!(position: current_tag_discipline.position)
current_tag_discipline.update!(position: current_tag_discipline.position + 1)
end
end
end
@tag_disciplines = sub_discipline&.tag_disciplines
rescue Exception => e
@message = e.message
end
private
def current_sub_discipline
@_current_sub_discipline = SubDiscipline.find params[:sub_discipline_id]
end
def current_tag_discipline
@_current_tag_discipline = TagDiscipline.find params[:id]
end
def setting_params
params.permit(:shixun, :subject, :question)
end
end

View File

@@ -0,0 +1,43 @@
class Admins::TagRepertoiresController < Admins::BaseController
def index
@sub_repertoire = current_sub_repertoire
@tag_repertoires = current_sub_repertoire.tag_repertoires
end
def create
name = params[:name].to_s.strip
return render_error('名称重复') if current_sub_repertoire.tag_repertoires.where(name: name).exists?
TagRepertoire.create!(name: name, sub_repertoire_id: current_sub_repertoire.id)
render_ok
end
def edit
@tag_repertoire = current_tag_repertoire
end
def update
if params[:tag_repertoire] && params[:tag_repertoire][:name].present?
name = params[:tag_repertoire][:name].to_s.strip
current_tag_repertoire.update_attributes!(name: name)
end
@tag_repertoires = current_tag_repertoire.sub_repertoire&.tag_repertoires
end
def destroy
@tag_repertoire_id = params[:id]
current_tag_repertoire.destroy!
end
private
def current_sub_repertoire
@_current_sub_repertoire = SubRepertoire.find params[:sub_repertoire_id]
end
def current_tag_repertoire
@_current_tag_repertoire = TagRepertoire.find params[:id]
end
end

View File

@@ -0,0 +1,122 @@
class Admins::UnitAppliesController < Admins::BaseController
before_action :get_apply,only: [:agree,:destroy,:edit,:update]
def index
params[:sort_by] ||= 'created_at'
params[:sort_direction] ||= 'desc'
unit_applies = Admins::UnitApplyQuery.call(params)
@unit_applies = paginate unit_applies.preload(:school, :user)
end
def agree
ActiveRecord::Base.transaction do
begin
@unit_apply.update_attribute("status",1)
@unit_apply&.applied_messages&.update_all(status:1)
@unit_apply&.school&.update_attribute("province",@unit_apply.province)
# #申请信息的创建
apply_message_params = {
user_id: @unit_apply&.user_id,
status: 1,
viewed: 0,
applied_id: @unit_apply.school_id,
applied_type: "ApplyAddSchools",
name: @unit_apply.name,
}
AppliedMessage.new(apply_message_params).save(validate: false)
Tiding.where(user_id: 1, trigger_user_id: @unit_apply.user_id, container_id: @unit_apply.id,
container_type: 'ApplyAddSchools', status: 0, tiding_type: "Apply").update_all(status: 1)
#消息的创建
tiding_params = {
user_id: @unit_apply.user_id,
trigger_user_id: 0,
container_id: @unit_apply.id,
container_type: 'ApplyAddSchools',
belong_container_id: @unit_apply.school_id,
belong_container_type: "School",
tiding_type: "System",
status: 1
}
Tiding.create(tiding_params)
render_success_js
rescue Exception => e
Rails.logger.info("############_________________#########{e}")
end
end
end
def destroy
Admins::DeleteUnitApplyService.call(@unit_apply, params)
render_success_js
end
def edit
@all_schools = School.where.not(id: @unit_apply.school_id).pluck("name","id")
end
def update
school = School.find_by(id: params[:school_id])
ActiveRecord::Base.transaction do
@unit_apply&.applied_messages&.update_all(status:4)
Tiding.where(user_id: 1, trigger_user_id: @unit_apply.user_id, container_id: @unit_apply.id,
container_type: 'ApplyAddSchools', status: 0, tiding_type: "Apply").update_all(status: 1)
#消息的创建
tiding_params = {
user_id: @unit_apply.user_id,
trigger_user_id: 0,
container_id: @unit_apply.id,
container_type: 'ApplyAddSchools',
belong_container_id: params[:school_id],
belong_container_type: "School",
tiding_type: "System",
status: 3,
extra: school.try(:name).to_s
}
Tiding.create(tiding_params)
UserExtension.where(school_id: @unit_apply.school_id).update_all(school_id: params[:school_id].to_i)
ApplyAddDepartment.where(:school_id => @unit_apply.school_id).update_all(school_id: params[:school_id].to_i)
# 判断重复
before_apply_departments = Department.where(school_id: @unit_apply.school_id)
before_apply_departments.each do |department|
after_dep = Department.where(school_id: params[:school_id].to_i, name: department.name)&.first
if after_dep.present?
UserExtension.where(school_id: @unit_apply.school_id, department_id: department.id).update_all(department_id: after_dep.id)
department.destroy
department.apply_add_departments.destroy_all
else
department.apply_add_departments.update_all(school_id: school.id)
department.update_attribute(:school_id, school.id)
end
end
@unit_apply&.school&.destroy
apply_params = {
status: 2,
name: school&.name.to_s,
school_id: params[:school_id],
province: params[:province],
city: params[:city],
address: params[:address]
}
@unit_apply.update_attributes(apply_params)
# render_success_js
end
end
private
def get_apply
@unit_apply = ApplyAddSchool.find_by(id:params[:id])
end
def disk_auth_filename(source_type, source_id, type)
File.join(storage_path, "#{source_type}", "#{source_id}#{type}")
end
end

View File

@@ -0,0 +1,19 @@
class Admins::UserStatisticsController < Admins::BaseController
def index
default_sort('finish_shixun_count', 'desc')
total_count, users = Admins::UserStatisticQuery.call(params)
@users = paginate users, total_count: total_count
end
def export
default_sort('finish_shixun_count', 'desc')
params[:per_page] = 10000
_count, @users = Admins::UserStatisticQuery.call(params)
filename = ['用户实训情况', Time.zone.now.strftime('%Y%m%d%H%M%S')].join('-') << '.xlsx'
render xlsx: 'export', filename: filename
end
end

View File

@@ -0,0 +1,68 @@
class Admins::UsersController < Admins::BaseController
def index
params[:sort_by] = params[:sort_by].presence || 'created_on'
params[:sort_direction] = params[:sort_direction].presence || 'desc'
users = Admins::UserQuery.call(params)
@users = paginate users.includes(user_extension: :school)
end
def edit
@user = User.find(params[:id])
end
def update
@user = User.find(params[:id])
Admins::UpdateUserService.call(@user, update_params)
flash[:success] = '保存成功'
redirect_to edit_admins_user_path(@user)
rescue ActiveRecord::RecordInvalid
flash.now[:danger] = '保存失败'
render 'edit'
rescue Admins::UpdateUserService::Error => ex
flash.now[:danger] = ex.message
render 'edit'
end
def destroy
User.find(params[:id]).destroy!
render_delete_success
end
def lock
User.find(params[:id]).lock!
render_ok
end
def unlock
User.find(params[:id]).activate!
render_ok
end
def reward_grade
user = User.find(params[:user_id])
return render_unprocessable_entity('金币数量必须大于0') if params[:grade].to_i <= 0
RewardGradeService.call(user, container_id: user.id, container_type: 'Feedback', score: params[:grade].to_i, not_unique: true)
render_ok(grade: user.grade)
end
def reset_login_times
User.find(params[:id]).reset_login_times!
render_ok
end
private
def update_params
params.require(:user).permit(%i[lastname nickname gender identity technical_title student_id is_shixun_marker
mail phone location location_city school_id department_id admin business is_test
password professional_certification authentication])
end
end

View File

@@ -0,0 +1,41 @@
class Admins::VideoAppliesController < Admins::BaseController
def index
params[:status] ||= 'pending'
status = params[:status]
if status == 'all'
status = %w(agreed refused)
end
applies = VideoApply.where(status: status).order('video_applies.updated_at desc')
search = params[:keyword].to_s.strip
if search.present?
applies = applies.joins(:video)
.where('videos.title like :search', search: "%#{search}%")
end
@video_applies = paginate applies.includes(video: { user: :user_extension })
end
def agree
Videos::AgreeApplyService.new(current_video_apply, current_user).call
render_success_js
rescue Videos::AgreeApplyService::Error => e
render json: { status: -1, message: e.message }
end
def refuse
Videos::RefuseApplyService.new(current_video_apply, current_user, reason: params[:reason]).call
render_success_js
rescue Videos::RefuseApplyService::Error => e
render json: { status: -1, message: e.message }
end
private
def current_video_apply
@_current_video_apply ||= VideoApply.find(params[:id])
end
end

View File

@@ -0,0 +1,79 @@
class Admins::WeappAdvertsController < Admins::BaseController
before_action :convert_file!, only: [:create]
def index
@adverts = WeappSettings::Advert.all
end
def create
position = WeappSettings::Advert.count + 1
ActiveRecord::Base.transaction do
advert = WeappSettings::Advert.create!(create_params.merge(position: position))
file_path = Util::FileManage.source_disk_filename(advert)
File.delete(file_path) if File.exist?(file_path) # 删除之前的文件
Util.write_file(@file, file_path)
end
flash[:success] = '保存成功'
redirect_to admins_weapp_adverts_path
end
def update
current_advert.update!(update_params)
render_ok
end
def destroy
ActiveRecord::Base.transaction do
current_advert.destroy!
# 前移
WeappSettings::Advert.where('position > ?', current_advert.position)
.update_all('position = position - 1')
file_path = Util::FileManage.source_disk_filename(current_advert)
File.delete(file_path) if File.exist?(file_path)
end
render_delete_success
end
def drag
move = WeappSettings::Advert.find_by(id: params[:move_id])
after = WeappSettings::Advert.find_by(id: params[:after_id])
Admins::DragWeappAdvertService.call(move, after)
render_ok
rescue ApplicationService::Error => e
render_error(e.message)
end
private
def current_advert
@_current_advert ||= WeappSettings::Advert.find(params[:id])
end
def create_params
params.require(:weapp_settings_advert).permit(:link)
end
def update_params
params.permit(:link, :online)
end
def convert_file!
max_size = 10 * 1024 * 1024 # 10M
file = params.dig('weapp_settings_advert', 'image')
if file.class == ActionDispatch::Http::UploadedFile
@file = file
render_error('请上传文件') if @file.size.zero?
render_error('文件大小超过限制') if @file.size > max_size
else
file = file.to_s.strip
return render_error('请上传正确的图片') if file.blank?
@file = Util.convert_base64_image(file, max_size: max_size)
end
rescue Base64ImageConverter::Error => ex
render_error(ex.message)
end
end

View File

@@ -0,0 +1,80 @@
class Admins::WeappCarouselsController < Admins::BaseController
before_action :convert_file!, only: [:create]
def index
@carousels = WeappSettings::Carousel.all
end
def create
position = WeappSettings::Carousel.count + 1
ActiveRecord::Base.transaction do
carousel = WeappSettings::Carousel.create!(create_params.merge(position: position))
file_path = Util::FileManage.source_disk_filename(carousel)
File.delete(file_path) if File.exist?(file_path) # 删除之前的文件
Util.write_file(@file, file_path)
end
flash[:success] = '保存成功'
redirect_to admins_weapp_carousels_path
end
def update
current_carousel.update!(update_params)
render_ok
end
def destroy
ActiveRecord::Base.transaction do
current_carousel.destroy!
# 前移
WeappSettings::Carousel.where('position > ?', current_carousel.position)
.update_all('position = position - 1')
file_path = Util::FileManage.source_disk_filename(current_carousel)
File.delete(file_path) if File.exist?(file_path)
end
render_delete_success
end
def drag
move = WeappSettings::Carousel.find_by(id: params[:move_id])
after = WeappSettings::Carousel.find_by(id: params[:after_id])
Admins::DragWeappCarouselService.call(move, after)
render_ok
rescue ApplicationService::Error => e
render_error(e.message)
end
private
def current_carousel
@_current_carousel ||= WeappSettings::Carousel.find(params[:id])
end
def create_params
params.require(:weapp_settings_carousel).permit(:link)
end
def update_params
params.permit(:link, :online)
end
def convert_file!
max_size = 10 * 1024 * 1024 # 10M
file = params.dig('weapp_settings_carousel', 'image')
if file.class == ActionDispatch::Http::UploadedFile
@file = file
render_error('请上传文件') if @file.size.zero?
render_error('文件大小超过限制') if @file.size > max_size
else
file = file.to_s.strip
return render_error('请上传正确的图片') if file.blank?
@file = Util.convert_base64_image(file, max_size: max_size)
end
rescue Base64ImageConverter::Error => ex
render_error(ex.message)
end
end

View File

@@ -0,0 +1,772 @@
require 'oauth2'
class ApplicationController < ActionController::Base
include CodeExample
include RenderExpand
include RenderHelper
include ControllerRescueHandler
include LaboratoryHelper
include GitHelper
include LoggerHelper
include LoginHelper
protect_from_forgery prepend: true, unless: -> { request.format.json? }
before_action :check_sign
before_action :user_setup
#before_action :check_account
DCODES = %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)
OPENKEY = "79e33abd4b6588941ab7622aed1e67e8"
helper_method :current_user
# 所有请求必须合法签名
def check_sign
if !Rails.env.development?
Rails.logger.info("66666 #{params}")
# suffix = request.url.split(".").last.split("?").first
# suffix_arr = ["xls", "xlsx", "pdf", "zip"] # excel文件先注释
# unless suffix_arr.include?(suffix)
if params[:client_key].present?
randomcode = params[:randomcode]
# tip_exception(501, "请求不合理") unless (Time.now.to_i - randomcode.to_i).between?(0,5)
sign = Digest::MD5.hexdigest("#{OPENKEY}#{randomcode}")
Rails.logger.info("2222 #{sign}")
tip_exception(501, "请求不合理") if sign != params[:client_key]
else
tip_exception(501, "请求不合理")
end
# end
end
end
# 全局配置参数
# 返回name对应的value
def edu_setting(name)
EduSetting.get(name)
end
# 平台身份权限判断(学生用户无权限)
def identity_auth
ue = current_user.user_extension
tip_exception(403, "..") unless current_user.admin_or_business? || ue.teacher? || ue.professional?
end
# 平台已认证身份判断(已认证的老师和专业人士)
def certi_identity_auth
ue = current_user.user_extension
tip_exception(403, "..") unless current_user.admin_or_business? ||
(current_user.professional_certification && (ue.teacher? || ue.professional?))
end
def shixun_marker
unless current_user.is_shixun_marker? || current_user.admin_or_business?
tip_exception(403, "..")
end
end
# 实训的访问权限
def shixun_access_allowed
if !current_user.shixun_permission(@shixun)
tip_exception(403, "..")
end
end
def admin_or_business?
User.current.admin? || User.current.business?
end
# 访问课堂时没权限直接弹加入课堂的弹框 409
def user_course_identity
@user_course_identity = current_user.course_identity(@course)
if @user_course_identity > Course::STUDENT && @course.is_public == 0
tip_exception(401, "..") unless User.current.logged?
check_account
tip_exception(@course.excellent ? 410 : 409, "您没有权限进入")
end
if @user_course_identity > Course::CREATOR && @user_course_identity <= Course::STUDENT && @course.tea_id != current_user.id
# 实名认证和职业认证的身份判断
tip_exception(411, "你的实名认证和职业认证审核未通过") if @course.authentication &&
@course.professional_certification && (!current_user.authentication && !current_user.professional_certification)
tip_exception(411, "你的实名认证审核未通过") if @course.authentication && !current_user.authentication
tip_exception(411, "你的职业认证审核未通过") if @course.professional_certification && !current_user.professional_certification
end
uid_logger("###############user_course_identity:#{@user_course_identity}")
end
# 题库的访问权限
def bank_visit_auth
tip_exception(-2,"未通过职业认证") if current_user.is_teacher? && !current_user.certification_teacher? && !current_user.admin_or_business? && @bank.user_id != current_user.id && @bank.is_public
tip_exception(403, "无权限") unless @bank.user_id == current_user.id || current_user.admin_or_business? ||
(current_user.certification_teacher? && @bank.is_public)
end
# 判断用户的邮箱或者手机是否可用
# params[:type] 1: 注册2忘记密码3绑定
def check_mail_and_phone_valid login, type
unless login =~ /^[a-zA-Z0-9]+([._\\]*[a-zA-Z0-9])*@([a-z0-9]+[-a-z0-9]*[a-z0-9]+.){1,63}[a-z0-9]+$/ || login =~ /^1\d{10}$/ ||
login =~ /^[a-zA-Z0-9]+([._\\]*[a-zA-Z0-9])$/
tip_exception(-2, "请输入正确的手机号或邮箱")
end
# 考虑到安全参数问题多一次查询去掉Union
user = User.where(phone: login).first || User.where(mail: login).first
if type.to_i == 1 && !user.nil?
tip_exception(-2, "该手机号码或邮箱已被注册")
elsif type.to_i == 2 && user.nil?
tip_exception(-2, "该手机号码或邮箱未注册")
elsif type.to_i == 3 && user.present?
tip_exception(-2, "该手机号码或邮箱已绑定")
end
sucess_status
end
# 发送及记录激活码
# 发送验证码type 1注册手机验证码 2找回密码手机验证码 3找回密码邮箱验证码 4绑定手机 5绑定邮箱
# 6手机验证码登录 7邮箱验证码登录 8邮箱注册验证码 9验证手机号有效
def check_verification_code(code, send_type, value)
case send_type
when 1, 2, 4, 9
# 手机类型的发送
sigle_para = {phone: value}
status = Educoder::Sms.send(mobile: value, code: code)
tip_exception(-2, code_msg(status)) if status != 0
when 8, 3, 5
# 邮箱类型的发送
sigle_para = {email: value}
# 60s内不能重复发送
send_email_limit_cache_key = "send_email_60_second_limit:#{value}"
tip_exception(-1, '请勿频繁操作') if Rails.cache.exist?(send_email_limit_cache_key)
# 短时间内不能大量发送
send_email_control = LimitForbidControl::SendEmailCode.new(value)
tip_exception(-1, '邮件发送太频繁,请稍后再试') if send_email_control.forbid?
begin
UserMailer.register_email(value, code).deliver_now
Rails.cache.write(send_email_limit_cache_key, 1, expires_in: 1.minute)
send_email_control.increment!
# Mailer.run.email_register(code, value)
rescue Exception => e
logger_error(e)
tip_exception(-2,"邮件发送失败,请稍后重试")
end
end
ver_params = {code_type: send_type, code: code}.merge(sigle_para)
VerificationCode.create!(ver_params)
end
def code_msg status
case status
when 0
"验证码已经发送到您的手机,请注意查收"
when 8
"同一手机号30秒内重复提交相同的内容"
when 9
"同一手机号5分钟内重复提交相同的内容超过3次"
when 22
"1小时内同一手机号发送次数超过限制"
when 33
"验证码发送次数超过频率"
when 43
"一天内同一手机号发送次数超过限制"
when 53
"手机号接收超过频率限制"
end
end
def find_course
return normal_status(2, '缺少course_id参数') if params[:course_id].blank?
@course = Course.find(params[:course_id])
tip_exception(404, "") if @course.is_delete == 1 && !current_user.admin_or_business?
rescue Exception => e
tip_exception(e.message)
end
def course_manager
return normal_status(403, '只有课堂管理员才有权限') if @user_course_identity > Course::CREATOR
end
def find_board
return normal_status(2, "缺少board_id参数") if params[:board_id].blank?
@board = Board.find(params[:board_id])
rescue Exception => e
uid_logger_error(e.message)
tip_exception(e.message)
end
def validate_type(object_type)
normal_status(2, "参数") if params.has_key?(:sort_type) && !SORT_TYPE.include?(params[:sort_type].strip)
end
def set_pagination
@page = params[:page] || 1
@page_size = params[:page_size] || 15
end
# 课堂教师权限
def teacher_allowed
logger.info("#####identity: #{current_user.course_identity(@course)}")
unless current_user.course_identity(@course) < Course::STUDENT
normal_status(403, "")
end
end
# 课堂教师、课堂管理员、超级管理员的权限(不包含助教)
def teacher_or_admin_allowed
unless current_user.course_identity(@course) < Course::ASSISTANT_PROFESSOR
normal_status(403, "")
end
end
def require_admin
normal_status(403, "") unless User.current.admin?
end
def require_business
normal_status(403, "") unless admin_or_business?
end
# 前端会捕捉401,弹登录弹框
# 未授权的捕捉407弹试用申请弹框
def require_login
#6.13 -hs
tip_exception(401, "请登录后再操作") unless User.current.logged?
end
# 异常提醒
def tip_exception(status = -1, message)
raise Educoder::TipException.new(status, message)
end
def missing_template
tip_exception(404, "...")
end
# 弹框提醒
def tip_show_exception(status = -2, message)
raise Educoder::TipException.new(status, message)
end
def normal_status(status = 0, message)
case status
when 403
message = "您没有权限进行该操作"
when 404
message = "您访问的页面不存在或已被删除"
end
render :json => { status: status, message: message }
end
# 资料是否完善
def check_account
if !current_user.profile_completed?
#info_url = '/account/profile'
tip_exception(402, nil)
end
end
# 系统全局认证(暂时隐藏试用申请的判断)
def check_auth
# day_cer = UserDayCertification.find_by(user_id: current_user.id)
# # 如果注册超过24小时则需要完善资料及授权
# if (Time.now.to_i - day_cer.try(:created_at).to_i) > 86400
# if !current_user.profile_completed?
# info_url = '/account/profile'
# tip_exception(402, info_url)
# elsif current_user.certification != 1
# if current_user.apply_actions.exists?(container_type: 'TrialAuthorization', status: 0)
# tip_exception(408, "您的试用申请正在审核中,请耐心等待")
# end
# tip_exception(407, "系统未授权")
# end
# end
# if current_user.certification != 1 && current_user.apply_actions.exists?(container_type: 'TrialAuthorization', status: 0)
# tip_exception(408, "您的试用申请正在审核中,请耐心等待")
# elsif (Time.now.to_i - day_cer.try(:created_at).to_i) < 86400
# if !current_user.profile_completed?
# info_url = '/account/profile'
# tip_exception(402, info_url)
# elsif current_user.certification != 1
# day_cer = UserDayCertification.find_by(user_id: current_user.id)
# tip_exception(407, "系统未授权") unless (Time.now.to_i - day_cer.try(:created_at).to_i) < 86400
# end
# end
end
def user_setup
# # reacct静态资源加载不需要走这一步
#return if params[:controller] == "main"
# Find the current user
#Rails.logger.info("current_laboratory is #{current_laboratory} domain is #{request.subdomain}")
User.current = find_current_user
uid_logger("user_setup: " + (User.current.logged? ? "#{User.current.try(:login)} (id=#{User.current.try(:id)})" : "anonymous"))
# 开放课程通过链接访问的用户
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 = open_class_user
if user
start_user_session(user)
set_autologin_cookie(user)
end
User.current = user
end
end
# if !User.current.logged? && Rails.env.development?
# User.current = User.find 1
# end
# 测试版前端需求
logger.info("subdomain:#{request.subdomain}")
if request.subdomain != "www"
if params[:debug] == 'teacher' #todo 为了测试,记得讲debug删除
User.current = User.find 81403
elsif params[:debug] == 'student'
User.current = User.find 8686
elsif params[:debug] == 'admin'
User.current = User.find 1
end
end
# User.current = User.find 81403
end
# Returns the current user or nil if no user is logged in
# and starts a session if needed
def find_current_user
uid_logger("user setup start: session[:user_id] is #{session[:user_id]}")
uid_logger("0000000000000user setup start: default_yun_session is #{default_yun_session}, session[:current_user_id] is #{session[:"#{default_yun_session}"]}")
current_domain_session = session[:"#{default_yun_session}"]
if current_domain_session
# existing session
User.current = (User.active.find(current_domain_session) rescue nil)
elsif autologin_user = try_to_autologin
autologin_user
elsif params[:format] == 'atom' && params[:key] && request.get? && accept_rss_auth?
# RSS key authentication does not start a session
User.find_by_rss_key(params[:key])
end
end
def try_to_autologin
if cookies[autologin_cookie_name]
# auto-login feature starts a new session
user = nil
Rails.logger.info("111111111111111111#{default_yun_session}, session is #{session[:"#{default_yun_session}"]} ")
user = User.try_to_autologin(cookies[autologin_cookie_name]) if session[:"#{default_yun_session}"]
start_user_session(user) if user
user
end
end
def api_request?
%w(xml json).include? params[:format]
end
def current_user
User.current
end
## 默认输出json
def render_json
respond_to do |format|
format.json
end
end
## 输出错误信息
def error_status(message = nil)
@status = -1
@message = message
end
# 实训等对应的仓库地址
def repo_ip_url(repo_path)
"#{edu_setting('git_address_ip')}/#{repo_path}"
end
def repo_url(repo_path)
"#{edu_setting('git_address_domain')}/#{repo_path}"
end
# 通关后,把最后一次成功的代码存到数据库
# type 0 创始内容, 1 最新内容
# def game_passed_code(path, myshixun, game_id)
# # 如果代码窗口是隐藏的,则不用保存代码
# return if myshixun.shixun.hide_code || myshixun.shixun.vnc
# file_content = git_fle_content myshixun.repo_path, path
# #unless file_content.present?
# # raise("获取文件代码异常")
# #end
# logger.info("#######game_id:#{game_id}, file_content:#{file_content}")
# game_code = GameCode.where(:game_id => game_id, :path => path).first
# if game_code.nil?
# GameCode.create!(:game_id => game_id, :new_code => file_content, :path => path)
# else
# game_code.update_attributes!(:new_code => file_content)
# end
# end
# Post请求
def uri_post(uri, params)
begin
uid_logger_dubug("--uri_exec: params is #{params}, url is #{uri}")
uri = URI.parse(URI.encode(uri.strip))
res = Net::HTTP.post_form(uri, params).body
uid_logger_dubug("--uri_exec: .....res is #{res}")
JSON.parse(res)
rescue Exception => e
uid_logger_error("--uri_exec: exception #{e.message}")
raise Educoder::TipException.new("实训平台繁忙繁忙等级84")
end
end
# 处理返回非0就报错的请求
def interface_post(uri, params, status, message)
begin
uid_logger_dubug("--uri_exec: params is #{params}, url is #{uri}")
uri = URI.parse(URI.encode(uri.strip))
res = Net::HTTP.post_form(uri, params).body
uid_logger_dubug("--uri_exec: .....res is #{res}")
res = JSON.parse(res)
if (res && res['code'] != 0)
tip_exception(status, message)
else
res
end
rescue Exception => e
uid_logger("--uri_exec: exception #{e.message}")
raise Educoder::TipException.new(message)
end
end
# json格式请求
def interface_json_post(uri, params, status, message)
begin
uid_logger_dubug("--uri_exec: params is #{params}, url is #{uri}")
uri = URI.parse(URI.encode(uri.strip))
res = Net::HTTP.start(uri.host, uri.port) do |http|
req = Net::HTTP::Post.new(uri)
req['Content-Type'] = 'application/json'
req.body = params.to_json
http.request(req)
end
uid_logger_dubug("--uri_exec: .....res is #{res.body}")
res = JSON.parse(res.body)
if (res && res['code'] != 0)
tip_exception(status, message)
else
res
end
rescue Exception => e
uid_logger("--uri_exec: exception #{e.message}")
raise Educoder::TipException.new("服务器繁忙")
end
end
# 适用与已经用url_safe编码后回调字符串形式
def tran_base64_decode64(str)
s_size = str.size % 4
if s_size != 0
str += "=" * (4 - s_size)
end
if str.blank?
str
else
Base64.decode64(str.tr("-_", "+/")).force_encoding("utf-8")
end
end
def sucess_status(message = 'success')
render :json => { status: 1, message: message }
end
# 随机生成字符
def generate_identifier(container, num, pre='')
code = DCODES.sample(num).join
if container == User
while container.exists?(login: pre+code) do
code = DCODES.sample(num).join
end
else
while container.exists?(identifier: code) do
code = DCODES.sample(num).join
end
end
code
end
# 实训主类别列表,自带描述
def shixun_main_type
list = []
mirrors = MirrorRepository.select([:id, :type_name, :description, :name]).published_main_mirror
mirrors.try(:each) do |mirror|
list << {id: mirror.id, type_name: mirror.type_name, description: mirror.try(:description), mirror_name: mirror.name}
end
list
end
# 小类别列表
def shixun_small_type
list = []
mirrors = MirrorRepository.select([:id, :type_name, :description, :name]).published_small_mirror
mirrors.try(:each) do |mirror|
list << {id: mirror.id, type_name: mirror.type_name, description: mirror.description, mirror_name: mirror.name}
end
list
end
def container_limit(mirror_repositories)
container = []
mirror_repositories.each do |mr|
if mr.name.present?
container << {:image => mr.name, :cpuLimit => mr.cpu_limit, :memoryLimit => "#{mr.memory_limit}M", :type => mr.try(:main_type) == "1" ? "main" : "sub"}
end
end
container.to_json
end
# 实训中间层pod配置
def shixun_container_limit shixun
container = []
shixun.shixun_service_configs.each do |config|
mirror = config.mirror_repository
if mirror.name.present?
# 资源限制没有就传默认值。
cpu_limit = config.cpu_limit.presence || 1
cpu_request = config.lower_cpu_limit.presence || 0.1
memory_limit = config.memory_limit.presence || 1024
request_limit = config.request_limit.presence || 10
resource_limit = config.resource_limit.presence || 10000
container << {:image => mirror.name,
:cpuLimit => cpu_limit,
:cpuRequest => cpu_request,
:memoryLimit => "#{memory_limit}M",
:memoryRequest => "#{request_limit}M",
:resourceLimit => "#{resource_limit}K",
:type => mirror.try(:main_type) == "1" ? "main" : "sub"}
end
end
container.to_json
end
# 毕设任务列表的赛选
def course_work(task, **option)
logger.info("#############{option}")
course = task.course
work_list = task.graduation_works.includes(user: [:user_extension])
# 教师评阅搜索 0: 未评, 1 已评
if option[:teacher_comment]
graduation_work_ids = GraduationWorkScore.where(graduation_work_id: work_list.map(&:id)).pluck(:graduation_work_id)
if option[:teacher_comment].zero?
work_list = work_list.where.not(id: graduation_work_ids)
elsif option[:teacher_comment] == 1
work_list = work_list.where(id: graduation_work_ids).where.not(work_status: 0)
end
end
# 作品状态 0 未提交, 1 按时提交, 2 延迟提交
if option[:task_status]
work_list = work_list.where(work_status: option[:task_status])
end
# 分班情况
if option[:course_group]
group_user_ids = course.course_members.where(course_group_id: option[:course_group]).pluck(:user_id)
# 有分组只可能是老师身份查看列表
work_list = work_list.where(user_id: group_user_ids)
end
# 只看我的交叉评阅
if option[:cross_comment]
graduation_work_id = task.graduation_work_comment_assignations.where(:user_id => current_user.id)
.pluck(:graduation_work_id).uniq if task.graduation_work_comment_assignations
work_list = work_list.where(id: graduation_work_id)
end
# 输入姓名和学号搜索
# TODO user_extension 如果修改 请调整
if option[:search]
work_list = work_list.joins(user: :user_extension).where("concat(lastname, firstname) like ?
or student_id like ?", "%#{option[:search]}%", "%#{option[:search]}%")
end
# 排序
rorder = option[:order] || "updated_at"
b_order = option[:b_order] || "desc"
if rorder == "created_at" || rorder == "work_score"
work_list = work_list.order("graduation_works.#{rorder} #{b_order}")
elsif rorder == "student_id"
work_list = work_list.joins(user: :user_extension).order("user_extensions.#{rorder} #{b_order}")
end
work_list
end
def strip_html(text, len=0, endss="...")
ss = ""
if !text.nil? && text.length>0
ss=text.gsub(/<\/?.*?>/, '').strip
ss = ss.gsub(/&nbsp;*/, '')
ss = ss.gsub(/\r\n/,'') #新增
ss = ss.gsub(/\n/,'') #新增
if len > 0 && ss.length > len
ss = ss[0, len] + endss
elsif len > 0 && ss.length <= len
ss = ss
#ss = truncate(ss, :length => len)
end
end
ss
end
# Returns a string that can be used as filename value in Content-Disposition header
def filename_for_content_disposition(name)
request.env['HTTP_USER_AGENT'] =~ %r{MSIE|Trident|Edge} ? ERB::Util.url_encode(name) : name
end
def format_time(time)
time.blank? ? '' : time.strftime("%Y-%m-%d %H:%M")
end
# 获取Oauth Client
def get_client(site)
client_id = Rails.configuration.educoder['client_id']
client_secret = Rails.configuration.educoder['client_secret']
OAuth2::Client.new(client_id, client_secret, site: site)
end
def paginate(relation)
limit = params[:limit] || params[:per_page]
limit = (limit.to_i.zero? || limit.to_i > 20) ? 20 : limit.to_i
page = params[:page].to_i.zero? ? 1 : params[:page].to_i
offset = (page - 1) * limit
if relation.is_a?(Array)
relation[offset, limit]
else
relation.limit(limit).offset(offset)
end
end
def strf_time(time)
time.blank? ? '' : time.strftime("%Y-%m-%d %H:%M:%S")
end
def strf_date(date)
date.blank? ? '' : date.to_date.strftime("%Y-%m-%d")
end
def logger_error(error)
Rails.logger.error(error.message)
error.backtrace.each { |msg| Rails.logger.error(msg) }
end
def find_user
@user = User.find_by_login params[:login]
render_not_found("未找到’#{params[:login]}’相关的用户") unless @user
end
def find_user_with_id
@user = User.find_by_id params[:user_id]
render_not_found("未找到’#{params[:login]}’相关的用户") unless @user
end
def find_repository
@repo = @user.repositories.find_by_identifier params[:repo_identifier]
render_not_found("未找到’#{params[:repo_identifier]}’相关的项目") unless @repo
end
def find_project
project_id = params[:project_id] ? params[:project_id] : params[:id]
project = Project.where(identifier: project_id)
if project.exists?
@project = project.first
else
@project = Project.find project_id
end
render_not_found("未找到’#{project}’相关的项目") unless @project
end
def find_project_with_identifier
@project = Project.find_by_identifier! params[:id]
render_not_found("未找到’#{params[:id]}’相关的项目") unless @project
end
def find_project_with_id
@project = Project.find(params[:project_id] || params[:id])
rescue Exception => e
logger_error(e.message)
tip_exception(e.message)
end
def render_response(interactor)
interactor.success? ? render_ok : render_error(interactor.error)
end
private
def object_not_found
uid_logger("Missing template or cant't find record, responding with 404")
render json: {message: "您访问的页面不存在或已被删除", status: 404}
false
end
def tip_show(exception)
uid_logger("Tip show status is #{exception.status}, message is #{exception.message}")
render json: exception.tip_json
end
def render_parameter_missing
render json: { status: -1, message: '参数缺失' }
end
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
# 记录热门搜索关键字
def record_search_keyword
keyword = params[:keyword].to_s.strip
return if keyword.blank? || keyword.size <= 1
return unless HotSearchKeyword.available?
HotSearchKeyword.add(keyword)
end
end

View File

@@ -0,0 +1,240 @@
#coding=utf-8
#
# 文件上传
class AttachmentsController < ApplicationController
before_action :require_login, :check_auth, except: [:show]
before_action :find_file, only: %i[show destroy]
before_action :attachment_candown, only: [:show]
skip_before_action :check_sign, only: [:show, :create]
include ApplicationHelper
def show
# 1. 优先跳到cdn
# 2. 如果没有cdnsend_file
if @file.cloud_url.present?
update_downloads(@file)
redirect_to @file.cloud_url and return
end
type_attachment = params[:disposition] || "attachment"
if type_attachment == "inline"
send_file absolute_path(local_path(@file)),filename: @file.title, disposition: 'inline',type: 'application/pdf'
elsif type_attachment == "MP4"
send_file_with_range absolute_path(local_path(@file)), disposition: 'inline', type: "video/mp4", range: true
else
send_file(absolute_path(local_path(@file)), filename: @file.title,stream:false, type: @file.content_type.presence || 'application/octet-stream')
end
update_downloads(@file)
end
def create
# 1. 本地存储
# 2. 上传到云
begin
upload_file = params["file"] || params["#{params[:file_param_name]}"] # 这里的file_param_name是为了方便其他插件名称
uid_logger("#########################file_params####{params["#{params[:file_param_name]}"]}")
raise "未上传文件" unless upload_file
folder = edu_setting('attachment_folder')
raise "存储目录未定义" unless folder.present?
month_folder = current_month_folder
save_path = File.join(folder, month_folder)
ext = file_ext(upload_file.original_filename)
local_path, digest = file_save_to_local(save_path, upload_file.tempfile, ext)
content_type = upload_file.content_type.presence || 'application/octet-stream'
# remote_path = file_save_to_ucloud(local_path[folder.size, local_path.size], local_path, content_type)
remote_path = nil # TODO 暂时本地上传,待域名配置后方可上传至云端
logger.info "local_path: #{local_path}"
logger.info "remote_path: #{remote_path}"
disk_filename = local_path[save_path.size + 1, local_path.size]
#存数据库
#
@attachment = Attachment.where(disk_filename: disk_filename,
author_id: current_user.id,
cloud_url: remote_path).first
if @attachment.blank?
@attachment = Attachment.new
@attachment.filename = upload_file.original_filename
@attachment.disk_filename = local_path[save_path.size + 1, local_path.size]
@attachment.filesize = upload_file.tempfile.size
@attachment.content_type = content_type
@attachment.digest = digest
@attachment.author_id = current_user.id
@attachment.disk_directory = month_folder
@attachment.cloud_url = remote_path
@attachment.save!
else
logger.info "文件已存在id = #{@attachment.id}, filename = #{@attachment.filename}"
end
render_json
rescue => e
uid_logger_error(e.message)
tip_exception(e.message)
end
end
def destroy
begin
@file_path = absolute_path(local_path(@file))
#return normal_status(403, "") unless @file.author == current_user
@file.destroy!
delete_file(@file_path)
normal_status("删除成功")
rescue Exception => e
uid_logger_error(e.message)
tip_exception(e.message)
raise ActiveRecord::Rollback
end
end
private
def find_file
@file =
if params[:type] == 'history'
AttachmentHistory.find params[:id]
else
Attachment.find params[:id]
end
end
def delete_file(file_path)
File.delete(file_path) if File.exist?(file_path)
end
def current_month_folder
date = Time.now
"#{date.year}/#{date.month.to_s.rjust(2, '0')}"
end
def file_ext(file_name)
ext = ''
exts = file_name.split(".")
if exts.size > 1
ext = ".#{exts.last}"
end
ext
end
def file_save_to_local(save_path, temp_file, ext)
unless Dir.exists?(save_path)
FileUtils.mkdir_p(save_path) ##不成功这里会抛异常
end
digest = md5_file(temp_file)
digest = "#{digest}_#{(Time.now.to_f * 1000).to_i}"
local_file_path = File.join(save_path, digest) + ext
save_temp_file(temp_file, local_file_path)
[local_file_path, digest]
end
def save_temp_file(temp_file, save_file_path)
File.open(save_file_path, 'wb') do |f|
temp_file.rewind
while (buffer = temp_file.read(8192))
f.write(buffer)
end
end
end
def md5_file(temp_file)
md5 = Digest::MD5.new
temp_file.rewind
while (buffer = temp_file.read(8192))
md5.update(buffer)
end
md5.hexdigest
end
def file_save_to_ucloud(path, file, content_type)
ufile = Educoder::Ufile.new(
ucloud_public_key: edu_setting('public_key'),
ucloud_private_key: edu_setting('private_key'),
ucloud_public_read: true,
ucloud_public_bucket: edu_setting('public_bucket'),
ucloud_public_bucket_host: edu_setting('public_bucket_host'),
ucloud_public_cdn_host: edu_setting('public_cdn_host'),
)
File.open(file) do |f|
ufile.put(path, f, 'Content-Type' => content_type)
end
edu_setting('public_cdn_host') + "/" + path
end
def attachment_candown
unless current_user.admin? || current_user.business?
candown = true
unless params[:type] == 'history'
if @file.container && current_user.logged?
# 课堂资源、作业、毕设相关资源的权限判断
if @file.container.is_a?(Course)
course = @file.container
candown = current_user.member_of_course?(course) || (course.is_public? && @file.publiced?)
elsif @file.container.is_a?(HomeworkCommon) || @file.container.is_a?(GraduationTask) || @file.container.is_a?(GraduationTopic)
course = @file.container&.course
candown = current_user.member_of_course?(course)
elsif @file.container.is_a?(StudentWork)
course = @file.container&.homework_common&.course
candown = current_user.member_of_course?(course)
elsif @file.container.is_a?(StudentWorksScore)
course = @file.container&.student_work&.homework_common&.course
candown = current_user.member_of_course?(course)
elsif @file.container.is_a?(GraduationWork)
course = @file.container&.graduation_task&.course
candown = current_user.member_of_course?(course)
elsif @file.container.is_a?(GraduationWorkScore)
course = @file.container&.graduation_work&.graduation_task&.course
candown = current_user.member_of_course?(course)
elsif @file.container.is_a?(Issue)
course = @file.container.project
candown = course.member?(current_user)
elsif @file.container.is_a?(Journal)
course = @file.container.issue.project
candown = course.member?(current_user)
end
tip_exception(403, "您没有权限进入") if course.present? && !candown
tip_exception(403, "您没有权限进入") if @file.container.is_a?(ApplyUserAuthentication)
end
end
end
end
def send_file_with_range(path, options = {})
logger.info("########request.headers: #{request.headers}")
logger.info("########request.headers: #{File.exist?(path)}")
if File.exist?(path)
size = File.size(path)
logger.info("########request.headers: #{request.headers}")
if !request.headers["Range"]
status_code = 200 # 200 OK
offset = 0
length = File.size(path)
else
status_code = 206 # 206 Partial Content
bytes = Rack::Utils.byte_ranges(request.headers, size)[0]
offset = bytes.begin
length = bytes.end - bytes.begin
end
response.header["Accept-Ranges"] = "bytes"
response.header["Content-Range"] = "bytes #{bytes.begin}-#{bytes.end}/#{size}" if bytes
response.header["status"] = status_code
send_data IO.binread(path, length, offset), options
else
raise ActionController::MissingFile, "Cannot read file #{path}."
end
end
end

View File

@@ -0,0 +1,23 @@
class BiddingUsersController < ApplicationController
before_action :require_login, :check_auth
def create
ProjectPackages::BiddingService.call(current_package, current_user)
render_ok
rescue ProjectPackages::BiddingService::Error => ex
render_error(ex.message)
end
def win
ProjectPackages::WinBiddingService.call(current_package, current_user, params)
render_ok
rescue ProjectPackages::WinBiddingService::Error => ex
render_error(ex.message)
end
private
def current_package
@_current_package ||= ProjectPackage.find(params[:project_package_id])
end
end

View File

@@ -0,0 +1,44 @@
class BindUsersController < ApplicationController
# before_action :require_login
def create
# user = CreateBindUserService.call(create_params)
#
if params[:type] == "qq"
begin
user = CreateBindUserService.call(current_user, create_params)
successful_authentication(user) if user.id != current_user.id
render_ok
rescue ApplicationService::Error => ex
render_error(ex.message)
end
else
begin
tip_exception '系统错误' if session[:unionid].blank?
bind_user = User.try_to_login(params[:username], params[:password])
tip_exception '用户名或者密码错误' if bind_user.blank?
tip_exception '用户名或者密码错误' unless bind_user.check_password?(params[:password].to_s)
tip_exception '该账号已被绑定,请更换其他账号进行绑定' if bind_user.bind_open_user?(params[:type].to_s)
OpenUsers::Wechat.create!(user: bind_user, uid: session[:unionid])
successful_authentication(bind_user)
render_ok
rescue Exception => e
render_error(e.message)
end
end
end
def new_user
current_user
end
private
def create_params
params.permit(:username, :password, :type, :not_bind)
end
end

View File

@@ -0,0 +1,12 @@
class BlobController < ApplicationController
def new
commit unless @repository.empty?
end
def create
create_commit(Files::CreateService, success_path: after_create_path,
failure_view: :new,
failure_path: namespace_project_new_blob_path(@project.namespace, @project, @ref))
end
end

View File

@@ -0,0 +1,5 @@
class Callbacks::BaseController < ActionController::Base
include RenderHelper
skip_before_action :verify_authenticity_token
end

View File

@@ -0,0 +1,342 @@
class ChallengesController < ApplicationController
before_action :require_login, :check_auth, except: [:index]
before_action :find_shixun, only: [:new, :create, :index]
skip_before_action :verify_authenticity_token, only: [:create, :update, :create_choose_question, :crud_answer]
before_action :find_challenge, only: [:edit, :show, :update, :create_choose_question, :index_down, :index_up,
:edit_choose_question, :show_choose_question, :destroy_challenge_choose,
:update_choose_question, :destroy, :crud_answer, :answer]
# 关卡更新和操作的权限控制
before_action :update_allowed, except: [:index]
# 关卡访问的权限控制
before_action :shixun_access_allowed, only: [:index]
include ShixunsHelper
include ChallengesHelper
# 新建实践题
def new
@position = @shixun.challenges.count + 1
@st = params[:st].to_i
@task_pass_default = PlatformSample.find_by(samples_type: "taskPass").try(:contents)
end
# params
# challenge:{"subject": "标题", "task_pass": "过关任务",
# "diffculty": "关卡难度", "score": "关卡分数", "st": "关卡类型"} 关卡相关信息
# challenge_tag: 关卡标签
#
def create
ActiveRecord::Base.transaction do
begin
@challenge = Challenge.new(challenge_params)
@challenge.position = @shixun.challenges.count + 1
@challenge.shixun_id = @shixun.id
@challenge.user_id = current_user.id
@challenge.modify_time = Time.now
@challenge.save!
# 实训是否需要重置, 非实践任务创建第一个阶段调用, 避免不包含实践任务的实训进行模拟实战报错 todo: 新建实训需要重置TPI
# shixun_modify_status_without_publish(@shixun, 1) if @challenge.position != 1
tags = params[:challenge_tag]
if tags.present?
tags.each do |tag|
# TODO 创建tag的时候为什么一直报challenge choose必须存在??
ChallengeTag.create!(name: tag, challenge_id: @challenge.id)
end
end
rescue Exception => e
uid_logger_error("create challenge failed #{e}")
end
end
end
# 创建选择题
def create_choose_question
ActiveRecord::Base.transaction do
begin
@challenge_choose = ChallengeChoose.new(chooce_params)
@challenge_choose.position = @challenge.challenge_chooses.count + 1
@challenge_choose.category = @challenge_choose.standard_answer.length == 1 ? 1 : 2
@challenge_choose.challenge_id = @challenge.id
if @challenge_choose.save!
# 创建选项
params[:question][:cnt].each_with_index do |test, index|
answer = params[:choice][:answer][index]
ChallengeQuestion.create(:option_name => test,
:challenge_choose_id => @challenge_choose.id,
:position => index, :right_key => answer)
end
# 创建单选多选的技能标签
if params[:challenge_tag].present?
params[:challenge_tag].each do |tag|
ChallengeTag.create(:name => tag, :challenge_choose_id => @challenge_choose.id, :challenge_id => @challenge.id)
end
end
@challenge.update_column(:score, @challenge.challenge_chooses.sum(:score))
end
rescue Exception => e
raise ActiveRecord::Rollback
end
end
end
# 选择题详情页面
def show_choose_question
@challenge_choose = ChallengeChoose.find params[:choose_id]
end
# 选择题更新页面
def update_choose_question
@challenge_choose = ChallengeChoose.find(params[:choose_id])
ActiveRecord::Base.transaction do
if params[:standard_answer] != @challenge_choose.standard_answer
@challenge.update_column(:modify_time, Time.now)
end
@challenge_choose.update_attributes(chooce_params)
@challenge.update_column(:score, @challenge.challenge_chooses.sum(:score))
# 单选多选题的更新
category = @challenge_choose.standard_answer.length > 1 ? 2 : 1
@challenge_choose.update_column(:category, category)
begin
@challenge_choose.challenge_questions.delete_all
params[:question][:cnt].each_with_index do |test, index|
answer = params[:choice][:answer][index]
ChallengeQuestion.create(:option_name => test, :challenge_choose_id => @challenge_choose.id, :position => index, :right_key => answer)
end
@challenge_choose.challenge_tags.delete_all unless @challenge_choose.challenge_tags.blank?
if params[:challenge_tag].present?
params[:challenge_tag].each do |tag|
ChallengeTag.create(:name => tag, :challenge_choose_id => @challenge_choose.id, :challenge_id => @challenge.id)
end
end
@challenge_choose = ChallengeChoose.find params[:choose_id]
rescue Exception => e
raise ActiveRecord::Rollback
end
end
end
# 选择题的编辑
def edit_choose_question
@challenge_choose = ChallengeChoose.find params[:choose_id]
end
def destroy_challenge_choose
ActiveRecord::Base.transaction do
@challenge_choose = ChallengeChoose.where(:id => params[:choose_id]).first
pos = @challenge_choose.position
@challenge.challenge_chooses.where("position > ?", pos).update_all("position = position - 1")
@challenge_choose.destroy
@status = 1
# 发起重置请求 TODO: 重置实训需要后续做
# shixun_modify_status_without_publish(@shixun, 1)
end
end
# 编辑模式
# tab 0,nil 过关任务, 1 评测设置, 2 参考答案
def edit
@tab = params[:tab].to_i
@power = @shixun.status == 0
challenge_num = Challenge.where(:shixun_id => @shixun).count
@position = @challenge.position
@chooses = @challenge.challenge_chooses
if @position < challenge_num
@next_challenge = Challenge.where(:shixun_id => @shixun, :position => @position + 1).first
end
@prev_challenge = Challenge.where(:shixun_id => @shixun, :position => @position - 1).first if @position - 1 > 0
end
def index
uid_logger("identifier: #{params}")
@challenges = @shixun.challenges.fields_for_list
@editable = @shixun.status == 0 # before_action有判断权限如果没发布则肯定是管理人员
@user = current_user
@shixun.increment!(:visits)
end
def show
@tab = params[:tab].nil? ? 1 : params[:tab].to_i
challenge_num = @shixun.challenges_count
@power = @shixun.status == 0 # 之前验证走过了是不是管理员,因此这里只用判断是否发布
@position = @challenge.position
if @position < challenge_num
@next_challenge = Challenge.where(:shixun_id => @shixun, :position => @position + 1).first
end
@prev_challenge = Challenge.where(:shixun_id => @shixun, :position => @position - 1).first if @position - 1 > 0
end
# tab 0:过关任务的更新; 1:评测设置的更新; 2:表示参考答案的更新;
def update
begin
ActiveRecord::Base.transaction do
tab = params[:tab].to_i
@challenge.update_attributes!(challenge_params)
if tab == 0 && @challenge.st == 0
@challenge.challenge_tags.delete_all
if params[:challenge_tag].present?
params[:challenge_tag].each do |input|
ChallengeTag.create!(:name => input, :challenge_id => @challenge.id)
end
end
elsif tab == 1
path = @challenge.path
exec_path = @challenge.exec_path
test_set = @challenge.test_sets
sets_output = test_set.map(&:output)
sets_input = test_set.map(&:input)
sets_open = test_set.map(&:is_public)
set_score = test_set.map(&:score)
set_match_rule = test_set.map(&:match_rule)
params_hidden = params[:test_set].map{|set| set[:hidden].to_i == 0}
params_output = params[:test_set].map{|set| set[:output] }
params_input = params[:test_set].map{|set| set[:input] }
params_score = params[:test_set].map{|set| set[:score]}
params_test_set = params[:test_set].map{|set| set[:match_rule]}
# 测试集变化则需要更新(输入、 输出、 是否隐藏)
if sets_output != params_output || sets_open != params_hidden || sets_input != params_input ||
set_score != params_score || params_test_set != set_match_rule
test_set.delete_all unless test_set.blank?
params[:test_set].each_with_index do |set, index|
# last 末尾匹配, full: 全完匹配
logger.info("set: #{set}; match_rule : #{set[:match_rule]}")
match_rule = set[:match_rule] == 'last' ? 'last' : 'full'
TestSet.create!(:challenge_id => @challenge.id,
:input => "#{set[:input]}",
:output => "#{set[:output]}",
:is_public => params_hidden[index],
:score => set[:score],
:match_rule => "#{match_rule}",
:position => (index + 1))
end
@challenge.update_column(:modify_time, Time.now)
# 测试集的
@shixun.myshixuns.update_all(:system_tip => 0)
end
if params[:challenge][:show_type].to_i == -1
@challenge.update_attributes(picture_path: nil, web_route: nil, expect_picture_path: nil, original_picture_path: nil)
end
# 关卡评测执行文件如果被修改,需要修改脚本内容
logger.info("############shixun_publiced:#{@shixun.public == 0}")
if @shixun.public == 0
script = modify_shixun_script @shixun, @shixun.evaluate_script
@shixun.shixun_info.update_column(:evaluate_script, script)
end
# TODO:
# if path != params[:challenge][:path]
# shixun_modify_status_without_publish(@shixun, 1)
# end
#Attachment.attach_files(@challenge, params[:attachments])
end
end
rescue Exception => e
logger.error("##update_challenges: ##{e.message}")
tip_exception("#{e.message}")
end
end
# 参考答案的'增,删,改'
# POST: /shixuns/:id/challenges/:id/crud_answer
# {'challenge_answer': [
# {'name': 'name', contents: 'contents', score: 10},
# {...},
# {...}, ...]
#}
def crud_answer
if @challenge.challenge_answers && params[:challenge_answer].blank?
@challenge.challenge_answers.destroy_all
else
raise '参考答案不能为空' if params[:challenge_answer].empty?
raise '占比之和必须为100%' if params[:challenge_answer].map{|a| a[:score]}.sum != 100
ActiveRecord::Base.transaction do
@challenge.challenge_answers.destroy_all if @challenge.challenge_answers
params[:challenge_answer].each_with_index do |answer, index|
# 内容为空不保存
next if answer[:contents].blank?
ChallengeAnswer.create!(name: answer[:name], contents: answer[:contents],
level: index+1, score: answer[:score], challenge_id: @challenge.id)
end
end
end
end
# 查看参考答案接口
def answer
@answers = @challenge.challenge_answers
end
def index_down
next_challenge = @challenge.next_challenge
position = @challenge.position
@challenge.update_attribute(:position, (position + 1))
next_challenge.update_attribute(:position, next_challenge.position - 1)
# 关卡位置被修改,需要修改脚本
script = modify_shixun_script @shixun, @shixun.evaluate_script
@shixun.shixun_info.update_column(:evaluate_script, script)
end
def index_up
position = @challenge.position
last_challenge = @challenge.last_challenge
@challenge.update_attribute(:position, (position - 1))
last_challenge.update_attribute(:position, last_challenge.position + 1)
# 关卡位置被修改,需要修改脚本
script = modify_shixun_script @shixun, @shixun.evaluate_script
@shixun.shixun_info.update_column(:evaluate_script, script)
end
def destroy
next_challenges = @shixun.challenges.where("position > #{@challenge.position}")
next_challenges.update_all("position = position - 1")
# Todo: 实训修改后,关卡需要重置
# shixun_modify_status_without_publish(@shixun, 1)
@challenge.destroy
# 关卡位置被删除,需要修改脚本
script = modify_shixun_script @shixun, @shixun.evaluate_script
@shixun.shixun_info.update_column(:evaluate_script, script)
end
private
def find_shixun
@shixun = Shixun.find_by_identifier(params[:shixun_identifier])
end
# 通用接口
def find_challenge
@challenge = Challenge.find params[:id]
@shixun = Shixun.find_by!(identifier: params[:shixun_identifier])
end
def challenge_params
tip_exception("评测时间不能超过300秒") if params[:challenge][:exec_time].to_i > 300
params.require(:challenge).permit(:subject, :task_pass, :difficulty, :score, :st, :modify_time, :test_set_average,
:path, :exec_path, :show_type, :original_picture_path, :test_set_score,
:expect_picture_path, :picture_path, :web_route, :answer, :exec_time)
end
def chooce_params
params.require(:challenge_choose).permit(:subject, :answer,
:standard_answer, :score, :difficult)
end
def update_allowed
unless current_user.manager_of_shixun?(@shixun)
raise Educoder::TipException.new(403, "..")
end
end
end

View File

@@ -0,0 +1,75 @@
class CommentsController < ApplicationController
before_action :find_hack
before_action :require_login
# 评论
def create
begin
@discuss = @hack.discusses.new(comment_params) # 管理员回复的能够显示
@discuss.hidden = false
@discuss.user_id = current_user.id
@discuss.save!
rescue Exception => e
uid_logger_error("create discuss failed : #{e.message}")
render_error("评论异常")
end
end
# 回复
def reply
begin
@discuss = @hack.discusses.new(reply_params)
@discuss.hidden = false
@discuss.user_id = current_user.id
@discuss.root_id = params[:comments][:parent_id]
@discuss.save!
rescue Exception => e
uid_logger_error("reply discuss failed : #{e.message}")
render_error("回复评论异常")
end
end
# 列表
def index
discusses =
if current_user.admin_or_business?
@hack.discusses.where(root_id: nil)
else
@hack.discusses.where(root_id: nil, hidden: false)
end
@discusses_count = discusses.count
@discusses= paginate discusses
end
# 删除
def destroy
@hack.discusses.find_by(id: params[:id]).destroy
render_ok
end
# 隐藏、取消隐藏
def hidden
if current_user.admin_or_business?
@discuss = @hack.discusses.where(id: params[:id]).first
@discuss.update_attribute(:hidden, params[:hidden].to_i == 1)
sucess_status
else
Educoder::TipException(403, "..")
end
end
private
def find_hack
@hack = Hack.find_by_identifier(params[:hack_identifier])
end
def comment_params
params.require(:comments).permit(:content)
end
def reply_params
params.require(:comments).permit(:content, :parent_id)
end
end

View File

@@ -0,0 +1,76 @@
class CommonsController < ApplicationController
OBJECT_TYPE = %W[message journals_for_message]
before_action :require_login, :check_auth
before_action :validate_object_type
before_action :find_object
before_action :validate_power
def delete
begin
@object.destroy!
rescue Exception => e
uid_logger_error(e.message)
tip_exception(e.message)
raise ActiveRecord::Rollback
end
end
def hidden
action(true)
end
def unhidden
action(false)
end
private
def find_object
begin
@object = params[:object_type].strip.classify.constantize.find params[:object_id]
rescue Exception => e
uid_logger_error(e.message)
tip_exception(e.message)
return
end
end
def validate_object_type
return normal_status(2, "缺少object_id参数") if params[:object_id].blank?
return normal_status(2, "缺少object_type参数") if params[:object_type].blank?
return normal_status(2, "object_type参数格式错误") unless OBJECT_TYPE.include? params[:object_type].strip
end
def validate_power
code =
case params[:object_type].strip
when 'message'
if current_user.course_identity(@object.board.course) >= Course::STUDENT && @object.author != current_user
403
else
200
end
when 'journals_for_message'
course = @object&.jour_type.to_s == "StudentWorksScore" ? @object.jour&.student_work&.homework_common&.course : @object.jour&.course
if current_user.course_identity(course) >= Course::STUDENT && @object.user != current_user
403
else
200
end
else
current_user.admin_or_business? ? 200 : 403
end
return normal_status(code, "你没有权限操作!") if code == 403
end
def action(flag)
begin
@object.has_attribute?(:is_hidden) ? @object.update_attributes(:is_hidden => flag )
: @object.update_attributes(:hidden => flag )
rescue Exception => e
uid_logger_error(e.message)
tip_exception(e.message)
raise ActiveRecord::Rollback
end
end
end

View File

@@ -0,0 +1,38 @@
class ComposeProjectsController < ApplicationController
#未做完
before_action :require_login
before_action :set_compose
def create
project_ids = params[:project_ids]
ComposeProject.transaction do
project_ids.each do |p|
project = Project.select(:id, :user_id).find(p)
unless project.blank? || ComposeProject.exists?(user_id: project.user_id, project_id: p, compose_id: @compose.id)
ComposeProject.create!(user_id: project.user_id, project_id: p, compose_id: @compose.id, position: p)
end
end
end
normal_status(0, "添加成功")
end
def destroy
project_ids = params[:project_ids]
if ComposeProject.where(project_id: project_ids, compose_id: @compose.id).delete_all
normal_status(0, "项目删除成功")
else
normal_status(-1, "项目删除失败")
end
end
private
def set_compose
@compose = Compose.find(params[:compose_id])
unless @compose.present?
normal_status(-1, "组织不存在")
end
end
end

View File

@@ -0,0 +1,99 @@
class ComposesController < ApplicationController
before_action :require_login, except: [:index]
before_action :find_compose, except: [:index, :new,:create]
def index
@order_type = params[:order] || "created_at"
@search_name = params[:search]
composes = Compose.compose_includes
if @search_name.present?
composes = composes.where("title like ?", "%#{@search_name}%")
end
composes = composes.order("#{@order_type} desc")
@page = params[:page] || 1
@limit = params[:limit] || 15
@composes_size = composes.size
@composes = composes.page(@page).per(@limit)
end
def new
end
def create
ActiveRecord::Base.transaction do
begin
@compose = Compose.new(compose_params.merge(user_id: current_user.id))
if @compose.save
ComposeUser.create!(user_id: current_user.id, compose_id: @compose.id, is_manager: 1)
normal_status(0,"组织创建成功")
else
error_messages = @compose.errors.messages[:title][0]
normal_status(-1,"组织创建失败:#{error_messages}")
end
rescue Exception => e
tip_exception("#{e}")
raise ActiveRecord::Rollback
end
end
end
def edit
end
def update
if @compose.update_attributes(compose_params)
normal_status(0,"组织更新成功")
else
error_messages = @compose.errors.messages[:title][0]
normal_status(-1,"组织更新失败:#{error_messages}")
end
end
def destroy
if @compose.destroy
normal_status(0,"组织删除成功")
else
normal_status(-1,"组织删除失败,请稍后重试")
end
end
def show
compose_projects_ids = @compose&.compose_projects&.pluck(:project_id)
search = params[:search]
if compose_projects_ids.size > 0
compose_projects = Project.where(id: compose_projects_ids)
if search.present?
compose_projects = compose_projects.where("name like ? ", "%#{search.to_s.strip}%")
end
else
compose_projects = []
end
@compose_projects_size = compose_projects.size
if @compose_projects_size > 0
@page = params[:page] || 1
@limit = params[:limit] || 15
@compose_projects = compose_projects.page(@page).per(@limit)
else
@compose_projects = compose_projects
end
end
private
def compose_params
params.require(:compose).permit(:user_id, :title, :description, :show_mode, :compose_mode, :compose_users_count, :compose_projects_count)
end
def find_compose
@compose = Compose.find(params[:compose_id])
unless @compose.present?
normal_status(-1, "组织不存在")
end
end
end

View File

View File

@@ -0,0 +1,18 @@
module Admins::RenderHelper
extend ActiveSupport::Concern
include Base::RenderHelper
def render_delete_success
render_js_template 'admins/shared/delete'
end
alias_method :render_success_js, :render_delete_success
def render_js_error(message, type: :alert)
if type == :notify
render js: "$.notify({ message: '#{message}' },{ type: 'danger', delay: 5000 });"
else
render_js_template 'admins/shared/error', locals: { message: message }
end
end
end

View File

@@ -0,0 +1,25 @@
module Base::ErrorRescueHandler
extend ActiveSupport::Concern
included do
rescue_from Exception do |e|
raise e if Rails.env.development?
Util.logger_error e
internal_server_error
end
rescue_from ActionView::MissingTemplate, ActiveRecord::RecordNotFound, with: :render_not_found
rescue_from ActionController::ParameterMissing do
render_unprocessable_entity('参数缺失')
end
# form validation error
rescue_from ActiveModel::ValidationError do |ex|
render_unprocessable_entity(ex.model.errors.full_messages.join(','))
end
rescue_from ActiveRecord::RecordInvalid do |ex|
ex.backtrace.each { |msg| Rails.logger.error(msg) }
render_unprocessable_entity(ex.record.errors.full_messages.join(','))
end
end
end

View File

@@ -0,0 +1,29 @@
module Base::PaginateHelper
extend ActiveSupport::Concern
def default_sort(sort_by, direction)
params[:sort_by] = params[:sort_by].presence || sort_by
params[:sort_direction] = params[:sort_direction].presence || direction
end
def offset
(page - 1) * per_page
end
def page
params[:page].to_i <= 0 ? 1 : params[:page].to_i
end
def per_page
params[:per_page].to_i <= 0 || params[:per_page].to_i > 100 ? 20 : params[:per_page].to_i
end
alias_method :limit, :per_page
def paginate(relations, total_count: nil)
if relations.is_a?(Array)
Kaminari.paginate_array(relations, limit: limit, offset: offset, total_count: total_count)
else
relations.page(page).per(per_page)
end
end
end

View File

@@ -0,0 +1,38 @@
module Base::RenderHelper
extend ActiveSupport::Concern
def render_by_format(hash)
format = request.format.symbol
hash.key?(format) ? hash[format].call : hash[:html].call
end
def render_forbidden
render_by_format(html: -> { current_user&.business? ? render('shared/403') : redirect_to('/403') },
js: -> { render_js_error(I18n.t('error.forbidden'), type: :notify) },
json: -> { render status: 403, json: { messages: I18n.t('error.forbidden') } } )
end
def render_not_found
render_by_format(html: -> { render 'shared/404' },
js: -> { render_js_error('资源未找到') },
json: -> { render status: 404, json: { message: '资源未找到' } })
end
def render_unprocessable_entity(message, type: :alert)
render_by_format(html: -> { render 'shared/422' },
js: -> { render_js_error(message, type: type) },
json: -> { render status: 422, json: { message: message } })
end
alias_method :render_error, :render_unprocessable_entity
def internal_server_error(message = '系统错误')
@message = message
render_by_format(html: -> { render 'shared/500' },
js: -> { render_js_error(message) },
json: -> { render status: 500, json: { message: message } })
end
def render_js_template(template, **opts)
render({ template: template, formats: :js }.merge(opts))
end
end

View File

@@ -0,0 +1,137 @@
module CodeExample
extend ActiveSupport::Concern
#老师C语言的标准代码
def c_stantard_code_teacher
"// 老师您好这是一个C语言的样例程序
// 程序功能:输入两个整数,输出两者之和
// 测试集合:老师可以给出多组测试集,例如:
// 输入1和2输出3
// 输入3和4输出7
// ... ...
// 系统将根据您给出的测试集对学生代码进行自动评分
// 特别提醒程序采用命令行传参方式输入通过argv传入
// 否则您的作业标准代码将不能通过测试
#include <stdio.h> //引用必须头文件
int main(int argc, char** argv) {
int a = atoi(argv[1]); //将第一个输入转成整型
int b = atoi(argv[2]); //将第二个输入转换为整型
printf(\"%d\",a+b); //输出a+b
return 0;
}".html_safe
end
#老师C++语言的标准代码
def c_stantard_code_teacher_
"// 老师您好这是一个C++语言的样例程序
// 程序功能:输入两个整数,输出两者之和
// 测试集合:老师可以给出多组测试集,例如:
// 输入1和2输出3
// 输入3和4输出7
// ... ...
// 系统将根据您给出的测试集对学生代码进行自动评分
// 特别提醒程序采用命令行传参方式输入通过argv传入
// 否则您的作业标准代码将不能通过测试
#include <iostream> //引用必须头文件
#include <cstdlib>
using namespace std;
int main(int argc, char** argv){
int a = atoi(argv[1]); //将第一个输入转成整型
int b = atoi(argv[2]); //将第二个输入转换为整型
cout<<a+b; //输出a+b
return 0;
}".html_safe
end
#学生C语言的标准代码
def c_stantard_code_student
"// 同学好这是一个C语言的样例程序
// 程序功能:输入两个整数,输出两者之和
// 测试集合:老师可以给出多组测试集,例如:
// 输入1和2输出3
// 输入3和4输出7
// ... ...
// 系统将根据您给出的测试集对学生代码进行自动评分
// 特别提醒程序采用命令行传参方式输入通过argv传入
// 否则您的作业标准代码将不能通过测试
#include <stdio.h> //引用必须头文件
int main(int argc, char** argv) {
int a = atoi(argv[1]); //将第一个输入转成整型
int b = atoi(argv[2]); //将第二个输入转换为整型
printf(\"%d\",a+b); //输出a+b
return 0;
}".html_safe
end
#学生C++语言的标准代码
def c_stantard_code_student_
"// 同学好这是一个C++语言的样例程序
// 程序功能:输入两个整数,输出两者之和
// 测试集合:老师可以给出多组测试集,例如:
// 输入1和2输出3
// 输入3和4输出7
// ... ...
// 系统将根据您给出的测试集对学生代码进行自动评分
// 特别提醒程序采用命令行传参方式输入通过argv传入
// 否则您的作业标准代码将不能通过测试
#include <iostream> //引用必须头文件
#include <cstdlib>
using namespace std;
int main(int argc, char** argv){
int a = atoi(argv[1]); //将第一个输入转成整型
int b = atoi(argv[2]); //将第二个输入转换为整型
cout<<a+b; //输出a+b
return 0;
}".html_safe
end
def compile_command
"compile(){
# 编译命令
compileCommand=\"COMPILECOMMAND\"
# 取当前关卡的编译文件
challengeProgramName=${challengeProgramNames[$1 - 1]}
# 获取编译结果(此处编译无输出则说明编译通过,否则输出编译错误信息,请按实训实际情况调整)
compileResult=$($compileCommand $challengeProgramName 2>&1 | base64)
if [ -z \"$compileResult\" ]; then
compileResult=$(echo -n \"compile successfully\" | base64)
fi
}
compile $1"
end
def execute_command
"execute(){
#执行命令
executeCommand=\"EXECUTECOMMAND\"
#执行文件名
sourceClassName=${sourceClassNames[$1 - 1]}
challengeStage=$1
output=''
i=0
while [[ i -lt ${#ins[*]} ]]; do
#执行,并拼接执行结果
result=$(echo \"${ins[$i]}\" | base64 -d | $executeCommand $sourceClassName 2>&1 | base64)
#拼接输出结果
output=$output\\\"$result\\\",
let i++
done
output=\"[${output%?}]\"
}
execute $1
"
end
end

View File

@@ -0,0 +1,40 @@
module ControllerRescueHandler
extend ActiveSupport::Concern
included do
rescue_from Exception do |e|
Util.logger_error e
render json: {status: -1, message: e.message}
end
rescue_from ActiveRecord::StatementInvalid do |e|
Util.logger_error e
render json: {status: -1, message: "接口数据异常"}
end
rescue_from NoMethodError do |e|
Util.logger_error e
render json: {status: -1, message: "接口方法异常"}
end
rescue_from ActionController::UnknownFormat do |e|
render json: {status: -1, message: "接口调用非JSON格式"}
end
# rescue_from ActionView::MissingTemplate, with: :object_not_found
# rescue_from ActiveRecord::RecordNotFound, with: :object_not_found
rescue_from Educoder::TipException, with: :tip_show
rescue_from ::ActionView::MissingTemplate, with: :missing_template
rescue_from ActiveRecord::RecordNotFound, with: :object_not_found
rescue_from ActionController::ParameterMissing, with: :render_parameter_missing
# form validation error
rescue_from ActiveModel::ValidationError do |ex|
render_error(ex.model.errors.full_messages.join(','))
end
rescue_from ActiveRecord::RecordInvalid do |ex|
render_error(ex.record.errors.full_messages.join(','))
end
# rescue_from RuntimeError do |ex|
# Util.logger_error "#######ex:#{ex}"
# render_error(ex.message)
# end
end
end

View File

@@ -0,0 +1,16 @@
module Cooperative::RenderHelper
include Base::RenderHelper
def render_delete_success
render_js_template 'cooperative/shared/delete'
end
alias_method :render_success_js, :render_delete_success
def render_js_error(message, type: :alert)
if type == :notify
render js: "$.notify({ message: '#{message}' },{ type: 'danger', delay: 5000 });"
else
render_js_template 'cooperative/shared/error', locals: { message: message }
end
end
end

View File

@@ -0,0 +1,59 @@
module GitCommon
extend ActiveSupport::Concern
included do
end
# ------------------------
# 版本库目录结构
def repository
begin
@repo_url = repo_url @repo_path
@trees = GitService.file_tree(repo_path: @repo_path, path: @path)
logger.info("#11@@#@#@#@111#@@@@###{@trees}")
# TPI(学员实训)不需要获取最近的一次提交
if params[:controller] != "myshixuns" && @trees
logger.info("#@@#@#@#@#@@@@###{@trees.try(:count)}")
@latest_commit = [GitService.commits(repo_path: @repo_path).first]
Rails.logger.info("########## #{@latest_commit}")
end
rescue Exception => e
logger.error(e.message)
end
end
def file_content
@content = git_fle_content @repo_path, @path
end
# 版本库提交记录
# Redo: commit接口需要按倒叙排列
def commits
begin
@commits = GitService.commits(repo_path: @repo_path)
logger.info("git first commit is #{@commits.try(:first)}")
raise Educoder::TipException.new("请先创建版本库") if @commits.nil?
rescue Exception => e
uid_logger_error(e.message)
raise Educoder::TipException.new("提交记录异常")
end
end
# 为版本库添加文件
def add_file
@path, message, content = params[:path].strip, params[:message], params[:content]
author_name, author_email = current_user.real_name, current_user.git_mail
Rails.logger.info(" good repo_name is #{@repo_path}")
@content = GitService.update_file(repo_path: @repo_path,
file_path: @path,
message: message.force_encoding('UTF-8'),
content: content.force_encoding('UTF-8'),
author_name: author_name,
author_email: author_email)
end
end

View File

@@ -0,0 +1,80 @@
module GitHelper
extend ActiveSupport::Concern
# 版本库目录空间
def repo_namespace(user_login, shixun_identifier)
"#{user_login}/#{shixun_identifier}.git"
end
# 版本库文件内容,带转码
def git_fle_content(repo_path, path)
begin
Rails.logger.info("git file content: repo_path is #{repo_path}, path is #{path}")
content = GitService.file_content(repo_path: repo_path, path: path)
Rails.logger.info("git file content: content is #{content}")
decode_content = nil
if content.present?
content = content["content"] #6.24 -hs 这个为新增,因为当实训题里含有选择题时,这里会报错,undefined method `[]' for nil:NilClass
content = Base64.decode64(content)
cd = CharDet.detect(content)
Rails.logger.info "encoding: #{cd['encoding']} confidence: #{cd['confidence']}"
# 字符编码问题GB18030编码识别率不行
decode_content =
if cd["encoding"] == 'GB18030' && cd['confidence'] > 0.8
content.encode('UTF-8', 'GBK', {:invalid => :replace, :undef => :replace, :replace => ' '})
else
content.force_encoding('UTF-8')
end
end
decode_content
rescue Exception => e
Rails.logger.error(e.message)
raise Educoder::TipException.new("文档内容获取异常")
end
end
# 更新文件代码
# content 文件内容message提交描述
def update_file_content(content, repo_path, path, mail, username, message)
#content = Base64.encode64(content)
GitService.update_file(repo_path: repo_path, file_path: path, message: message,
content: content, author_name: username, author_email: mail)
end
def update_file_base64_content(content, repo_path, path, mail, username, message)
content = Base64.encode64(content)
GitService.update_file_base64(repo_path: repo_path, file_path: path, message: message,
content: content, author_name: username, author_email: mail)
end
# 添加目录
def git_add_folder(folder_path, author_name, author_email, message)
GitService.add_tree(file_path: folder_path, message: message, author_name: author_name, author_email: author_email)
end
# 删除文件
def git_delete_file(file_path, author_name, author_email, message)
GitService.delete_file(file_path: file_path, message: message, author_name: author_name, author_email: author_email)
end
# 版本库Fork功能
def project_fork(container, original_rep_path, username)
raise Educoder::TipException.new("fork源路径为空,fork失败!") if original_rep_path.blank?
# 将要生成的仓库名字
new_repo_name = "#{username.try(:strip)}/#{container.try(:identifier)}#{ Time.now.strftime("%Y%m%d%H%M%S")}"
# uid_logger("start fork container: repo_name is #{new_repo_name}")
GitService.fork_repository(repo_path: original_rep_path, fork_repository_path: (new_repo_name + ".git"))
container.update_attributes!(:repo_name => new_repo_name)
end
#实训题的关卡url初始化
def challenge_path(path)
cha_path = path.present? ? path.split("") : []
cha_path.reject(&:blank?)[0].try(:strip)
end
end

View File

@@ -0,0 +1,32 @@
module LaboratoryHelper
extend ActiveSupport::Concern
included do
before_action :setup_laboratory
helper_method :current_laboratory
helper_method :default_setting
helper_method :default_yun_session
end
def current_laboratory
@_current_laboratory ||= (Laboratory.find_by_subdomain(request.subdomain) || Laboratory.find(1))
end
def default_laboratory
@_default_laboratory ||= Laboratory.find(1)
end
def default_setting
@_default_setting ||= LaboratorySetting.find_by(laboratory_id: 1)
end
def setup_laboratory
Laboratory.current = current_laboratory
end
def default_yun_session
laboratory ||= (Laboratory.find_by_subdomain(request.subdomain) || Laboratory.find(1))
@_default_yun_session = "#{laboratory.try(:identifier).split('.').first}_user_id"
end
end

View File

@@ -0,0 +1,22 @@
module LoggerHelper
extend ActiveSupport::Concern
extend LoggerHelper
# 以用户id开始的日志定义
def uid_logger(message)
Rails.logger.info("##:#{current_user.try(:id)} --#{message}")
end
# debug日志
def uid_logger_dubug(message)
Rails.logger.info("##dubug-#{current_user.try(:id)} --#{message}")
end
# 以用户id开始的日志定义
def uid_logger_error(message)
Rails.logger.error("##:#{current_user.try(:id)} --#{message}")
end
end

View File

@@ -0,0 +1,85 @@
module LoginHelper
extend ActiveSupport::Concern
def edu_setting(name)
EduSetting.get(name)
end
def autologin_cookie_name
edu_setting('autologin_cookie_name').presence || 'autologin'
end
def set_autologin_cookie(user)
token = Token.get_or_create_permanent_login_token(user, "autologin")
cookie_options = {
:value => token.value,
:expires => 1.month.from_now,
:path => '/',
:secure => false,
:httponly => false
}
if edu_setting('cookie_domain').present?
cookie_options = cookie_options.merge(domain: edu_setting('cookie_domain'))
end
cookies[autologin_cookie_name] = cookie_options
Rails.logger.info("cookies is #{cookies}")
end
def successful_authentication(user)
Rails.logger.info("id: #{user&.id} Successful authentication start: '#{user.login}' from #{request.remote_ip} at #{Time.now.utc}")
# Valid user
self.logged_user = user
session[:"#{default_yun_session}"] = user.id
# generate a key and set cookie if autologin
set_autologin_cookie(user)
UserAction.create(action_id: user&.id, action_type: 'Login', user_id: user&.id, ip: request.remote_ip)
user.update_column(:last_login_on, Time.now)
# 注册完成后有一天的试用申请(先去掉)
# UserDayCertification.create(user_id: user.id, status: 1)
end
def logout_user
if User.current.logged?
if autologin = cookies.delete(autologin_cookie_name)
User.current.delete_autologin_token(autologin)
end
User.current.delete_session_token(session[:tk])
self.logged_user = nil
end
# 云上实验室退出清理当前session
laboratory ||= (Laboratory.find_by_subdomain(request.subdomain) || Laboratory.find(1))
default_yun_session = "#{laboratory.try(:identifier).split('.').first}_user_id"
# end
session[:"#{default_yun_session}"] = nil
end
# Sets the logged in user
def logged_user=(user)
# reset_session
if user && user.is_a?(User)
User.current = user
start_user_session(user)
else
User.current = User.anonymous
end
end
def start_user_session(user)
# re_subdomain = "#{request.subdomain.split('.').first}_user_id"
# session[:"#{request.subdomain}_user_id"] = user.id
# Rails.logger.info("domain_user_id session is: 3333332222111#{session[:"#{request.subdomain}_user_id"]}")
# Rails.logger.info("user_id session is: 3333332222111#{session[:"#{request.subdomain}_user_id"]}")
#
# # if current_laboratory.main_site?
# # session[:user_id] = user.id
# # else
# # session[:"#{request.subdomain}_user_id"] = user.id
# # end
# session[:user_id] = user.id
session[:"#{default_yun_session}"] = user.id
session[:ctime] = Time.now.utc.to_i
session[:atime] = Time.now.utc.to_i
end
end

View File

@@ -0,0 +1,12 @@
module OperateProjectAbilityAble
extend ActiveSupport::Concern
included do
end
def authorizate_user_can_edit_project!
return if current_user.project_manager? @project || current_user.admin?
render_forbidden('你没有权限操作.')
end
end

View File

@@ -0,0 +1,12 @@
module PaginateHelper
def paginate(objs, **opts)
page = params[:page].to_i <= 0 ? 1 : params[:page].to_i
per_page = params[:per_page].to_i > 0 && params[:per_page].to_i < 50 ? params[:per_page].to_i : opts[:per_page] || 20
if objs.is_a?(Array)
Kaminari.paginate_array(objs).page(page).per(per_page)
else
objs.page(page).per(per_page)
end
end
end

View File

@@ -0,0 +1,40 @@
module RenderExpand
extend ActiveSupport::Concern
included do
ActionController.add_renderer :pdf do |template, options|
file = File.open(Rails.root.join('app/templates', template << '.html.erb'))
html = ERB.new(file.read).result(binding)
kit = PDFKit.new(html)
base_css = %w(app/templates/shared/main.css)
base_css.each { |css| kit.stylesheets << Rails.root.join(css) }
Array.wrap(options.delete(:stylesheets)).each do |path|
kit.stylesheets << Rails.root.join('app/templates', path)
end
send_data kit.to_pdf, filename: options[:filename], disposition: options[:disposition] || 'attachment', type: 'application/pdf'
end
ActionController.add_renderer :exam_pdf do |template, options|
file = File.open(Rails.root.join('app/templates', template << '.html.erb'))
html = ERB.new(file.read).result(binding)
kit = PDFKit.new(html)
# PDFKit初始化后再添加样式文件到stylesheets会出现在页面的<!DOCTYPE>定义不起作用文档模式为默认为BackCompat从而导致katex不起作用
# 而测试发现通过配置添加css样式文件不会有影响
PDFKit.configure do |config|
config.default_options = {
:"user-style-sheet" => Rails.root.join('app/templates', options[:stylesheets])
}
end
send_data kit.to_pdf, filename: options[:filename], disposition: options[:disposition] || 'attachment', type: 'application/pdf'
end
end
end

View File

@@ -0,0 +1,31 @@
module RenderHelper
def render_ok(data = {})
render json: { status: 0, message: 'success' }.merge(data)
end
def render_error(message = '')
render json: { status: -1, message: message }
end
def render_not_acceptable(message = '请求已拒绝')
render json: { status: 406, message: message }
end
def render_not_found(message = I18n.t('error.record_not_found'))
render json: { status: 404, message: message }
# render status: 404, json: { errors: errors }
end
def render_forbidden(message = I18n.t('error.forbidden'))
render json: { status: 403, message: message }
# render status: 403, json: { errors: errors }
end
def render_unauthorized(message = I18n.t('error.unauthorized'))
render json: { status: 401, message: message }
end
def render_result(status=1, message='success')
render json: { status: status, message: message }
end
end

View File

@@ -0,0 +1,37 @@
class ContentsController < ApplicationController
before_action :find_user, :find_repository
before_action :require_login, only: %i[create update_file delete_file]
def create
interactor = Gitea::CreateFileInteractor.call(current_user, content_params)
if interactor.success?
@file = interactor.result
else
render_error(interactor.error)
end
end
def update_file
interactor = Gitea::UpdateFileInteractor.call(current_user, params)
if interactor.success?
@file = interactor.result
else
render_error(interactor.error)
end
end
def delete_file
interactor = Gitea::DeleteFileInteractor.call(current_user, params)
if interactor.success?
@file = interactor.result
else
render_error(interactor.error)
end
end
private
def content_params
params.permit(:login, :repo_identifier, :filepath, :branch, :content, :message, :new_branch)
end
end

View File

@@ -0,0 +1,73 @@
class CourseGroupsController < ApplicationController
before_action :require_login, :check_auth
before_action :set_group, except: [:create]
before_action :find_course, only: [:create]
before_action :teacher_allowed, except: [:set_invite_code_halt]
def create
tip_exception("分班名称不能为空") if params[:name].blank?
if @course.course_groups.where(name: params[:name]).count > 0
normal_status(-1, "已存在同名分班")
else
course_group = @course.course_groups.create!(name: params[:name], position: @course.course_groups.count + 1)
render :json => {group_id: course_group.id, status: 0, message: "创建成功"}
end
end
def destroy
ActiveRecord::Base.transaction do
begin
@course.course_groups.where("position > #{@group.position}").update_all("position = position - 1")
# 将该分班的学生转到未分班
@group.course_members.update_all(course_group_id: 0)
@group.destroy
rescue Exception => e
uid_logger_error(e.message)
tip_exception("删除分班失败")
end
end
end
# 分班重命名
def rename_group
tip_exception("名称不能为空") if params[:name].blank?
if @course.course_groups.where(name: params[:name]).count > 0
normal_status(-1, "已存在同名分班")
else
@group.update_attributes(name: params[:name].strip)
normal_status(0, "更新成功")
end
end
# 分班的拖动
def move_category
tip_exception("移动失败") if params[:position].blank?
unless params[:position].to_i == @group.position
if params[:position].to_i < @group.position
@course.course_groups.where("position < #{@group.position} and position >= ?", params[:position]).update_all("position = position + 1")
else
@course.course_groups.where("position > #{@group.position} and position <= ?", params[:position]).update_all("position = position - 1")
end
@group.update_attributes(position: params[:position])
normal_status(0, "移动成功")
else
normal_status(-1, "位置没有变化")
end
end
# 邀请码停用/启用
def set_invite_code_halt
teacher = @course.teachers.find_by(user_id: current_user.id)
tip_exception(403, "无权限") unless current_user.admin_or_business? ||
(teacher.present? && (teacher.teacher_course_groups.pluck(:course_group_id).include?(@group.id) || teacher.teacher_course_groups.size == 0))
@group.update!(invite_code_halt: !@group.invite_code_halt)
normal_status(0, "成功")
end
private
def set_group
@group = CourseGroup.find_by!(id: params[:id])
@course = @group.course
end
end

View File

@@ -0,0 +1,68 @@
class CourseModulesController < ApplicationController
before_action :require_login, :check_auth
before_action :set_module, except: [:unhidden_modules]
before_action :find_course, only: [:unhidden_modules]
before_action :teacher_or_admin_allowed, except: [:add_second_category]
before_action :teacher_allowed, only: [:add_second_category]
# 模块置顶
def sticky_module
# position为1则不做处理否则该模块的position置为1position小于当前模块的position加1
unless @course_module.position == 1
@course.course_modules.where("position < #{@course_module.position}").update_all("position = position + 1")
@course_module.update_attributes(position: 1)
end
normal_status(0, "置顶成功")
end
# 模块隐藏
def hidden_module
tip_exception("请至少保留一个课堂模块") if @course.none_hidden_course_modules.where.not(id: @course_module.id).size == 0
@course_module.update_attributes(hidden: 1)
normal_status(0, "更新成功")
end
# 模块重命名
def rename_module
name = params[:name].strip
tip_exception("名称不能为空") if name.blank?
tip_exception("已存在同名模块") if @course.course_modules.exists?(module_name: name)
@course_module.update_attributes(module_name: name)
case @course_module.module_type
when 'board'
@course.course_board.update_columns(name: name)
end
normal_status(0, "更新成功")
end
# 模块的显示
def unhidden_modules
tip_exception("请选择要显示的模块") if params[:module_ids].blank?
@course.course_modules.where(id: params[:module_ids]).update_all(hidden: 0)
normal_status(0, "更新成功")
end
# 添加二级目录
def add_second_category
tip_exception("子目录名称不能为空") if params[:name].blank?
tip_exception("已存在同名子目录") if @course_module.course_second_categories.exists?(name: params[:name].strip)
ActiveRecord::Base.transaction do
begin
category = @course_module.course_second_categories.create!(name: params[:name].strip, category_type: @course_module.module_type,
course_id: @course.id, position: @course_module.course_second_categories.count + 1)
render :json => {category_id: category.id, status: 0, message: "添加成功"}
rescue Exception => e
uid_logger_error(e.message)
tip_exception("添加子目录失败")
end
end
end
private
def set_module
@course_module = CourseModule.find_by!(id: params[:id])
@course = @course_module.course
end
end

View File

@@ -0,0 +1,60 @@
class CourseSecondCategoriesController < ApplicationController
before_action :require_login, :check_auth
before_action :set_category
before_action :teacher_allowed
# 目录重命名
def rename_category
tip_exception("毕设子目录不能重命名") if @category.category_type == "graduation"
tip_exception("名称不能为空") if params[:name].blank?
tip_exception("已存在同名子目录") if @course_module.course_second_categories.exists?(name: params[:name].strip)
@category.update_attributes!(name: params[:name].strip)
normal_status(0, "更新成功")
end
# 子目录的拖动
def move_category
tip_exception("移动失败") if params[:position].blank?
unless params[:position].to_i == @category.position
if params[:position].to_i < @category.position
@course_module.course_second_categories.where("position < #{@category.position} and position >= ?", params[:position]).update_all("position = position + 1")
else
@course_module.course_second_categories.where("position > #{@category.position} and position <= ?", params[:position]).update_all("position = position - 1")
end
@category.update!(position: params[:position])
normal_status(0, "移动成功")
else
normal_status(-1, "位置没有变化")
end
end
def destroy
tip_exception("毕设子目录不能删除") if @category.category_type == "graduation"
ActiveRecord::Base.transaction do
begin
@course_module.course_second_categories.where("position > #{@category.position}").update_all("position = position - 1")
# 更新相应对象的子目录id
if @course_module.module_type == "shixun_homework"
@category.homework_commons.update_all(course_second_category_id: 0)
@right_url = "/courses/#{@course.id}/shixun_homeworks/#{@course_module.id}"
elsif @course_module.module_type == "attachment"
Attachment.where(course_second_category_id: @category.id).update_all(course_second_category_id: 0)
@right_url = "/courses/#{@course.id}/files/#{@course_module.id}"
end
@category.destroy
rescue Exception => e
uid_logger_error(e.message)
tip_exception("删除子目录失败")
end
end
end
private
def set_category
@category = CourseSecondCategory.find_by!(id: params[:id])
@course_module = @category.course_module
@course = @course_module.try(:course)
end
end

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,13 @@
class DepartmentsController < ApplicationController
skip_before_action :check_sign
def for_option
render_ok(departments: current_school.departments.without_deleted.select(:id, :name).as_json)
end
private
def current_school
@_current_school ||= School.find(params[:id])
end
end

View File

@@ -0,0 +1,6 @@
class DisciplinesController < ApplicationController
def index
end
end

View File

@@ -0,0 +1,29 @@
class EduDatasController < ApplicationController
before_action :find_game
skip_before_action :user_setup
skip_before_action :setup_laboratory
# layout :false
include GitHelper
# params[:game_id]
def game
@shixun = @challenge.shixun
@shixun_env = @shixun.mirror_name
@shixun_tags = @challenge.challenge_tags.map(&:name)
end
def code_lines
path = @challenge.path
myshixun = @game.myshixun
# content = git_fle_content(myshixun.repo_path, path) || ""
@content = {"content":"#coding=utf-8\n\n#请在此处添加代码完成输出“Hello Python”,注意要区分大小写!\n###### Begin ######\n\n\n\n###### End ######\n\n"}
@content[:content].include?("Begin")
end
private
def find_game
game_id = params[:game_id]
@game = Game.find(game_id)
@challenge = @game.challenge
end
end

View File

@@ -0,0 +1,75 @@
class EduSettingsController < ApplicationController
before_action :require_admin
before_action :set_edu_setting, only: [:show, :edit, :update, :destroy]
skip_before_action :check_sign
# GET /edu_settings
# GET /edu_settings.json
def index
@edu_settings = EduSetting.all
end
# GET /edu_settings/1
# GET /edu_settings/1.json
def show
end
# GET /edu_settings/new
def new
@edu_setting = EduSetting.new
end
# GET /edu_settings/1/edit
def edit
end
# POST /edu_settings
# POST /edu_settings.json
def create
@edu_setting = EduSetting.new(edu_setting_params)
respond_to do |format|
if @edu_setting.save
format.html { redirect_to @edu_setting, notice: 'Edu setting was successfully created.' }
format.json { render :show, status: :created, location: @edu_setting }
else
format.html { render :new }
format.json { render json: @edu_setting.errors, status: :unprocessable_entity }
end
end
end
# PATCH/PUT /edu_settings/1
# PATCH/PUT /edu_settings/1.json
def update
respond_to do |format|
if @edu_setting.update(edu_setting_params)
format.html { redirect_to @edu_setting, notice: 'Edu setting was successfully updated.' }
format.json { render :show, status: :ok, location: @edu_setting }
else
format.html { render :edit }
format.json { render json: @edu_setting.errors, status: :unprocessable_entity }
end
end
end
# DELETE /edu_settings/1
# DELETE /edu_settings/1.json
def destroy
@edu_setting.destroy
respond_to do |format|
format.html { redirect_to edu_settings_url, notice: 'Edu setting was successfully destroyed.' }
format.json { head :no_content }
end
end
private
# Use callbacks to share common setup or constraints between actions.
def set_edu_setting
@edu_setting = EduSetting.find(params[:id])
end
# Never trust parameters from the scary internet, only allow the white list through.
def edu_setting_params
params.require(:edu_setting).permit(:name, :value)
end
end

View File

@@ -0,0 +1,92 @@
class ExaminationBanksController < ApplicationController
include PaginateHelper
before_action :require_login
before_action :certi_identity_auth, only: [:create, :edit, :update, :destroy, :set_public, :revoke_item]
before_action :find_exam, except: [:index, :create]
before_action :edit_auth, only: [:update, :destroy, :set_public, :revoke_item]
before_action :identity_auth, only: [:index]
def index
exams = ExaminationBankQuery.call(params)
@exams_count = exams.size
@exams = paginate exams.includes(:user, :examination_items)
end
def show
@items = @exam.examination_items
@single_questions = @items.where(item_type: "SINGLE")
@multiple_questions = @items.where(item_type: "MULTIPLE")
@judgement_questions = @items.where(item_type: "JUDGMENT")
@program_questions = @items.where(item_type: "PROGRAM")
end
def create
ActiveRecord::Base.transaction do
exam = ExaminationBank.new(user: current_user)
# 保存试卷基础信息
exam = ExaminationBanks::SaveExaminationBankService.call(exam, form_params)
# 将试题篮中的试题发送到试卷,试卷的题目与试题独立
current_user.item_baskets.includes(:item_bank).each do |basket|
item = basket.item_bank
if item.present?
new_item = ExaminationItem.new
new_item.new_item(item, exam, basket.score, basket.position)
end
end
current_user.item_baskets.destroy_all
end
render_ok
rescue ApplicationService::Error => ex
render_error(ex.message)
end
def edit; end
def update
ExaminationBanks::SaveExaminationBankService.call(@exam, form_params)
render_ok
rescue ApplicationService::Error => ex
render_error(ex.message)
end
def destroy
ActiveRecord::Base.transaction do
ApplyAction.where(container_type: "ExaminationBank", container_id: @exam.id).destroy_all
@exam.destroy!
render_ok
end
end
def set_public
tip_exception(-1, "该试卷已公开") if @exam.public?
tip_exception(-1, "请勿重复提交申请") if ApplyAction.where(container_id: @exam.id, container_type: "ExaminationBank", status: 0).exists?
ApplyAction.create!(container_id: @exam.id, container_type: "ExaminationBank", user_id: current_user.id)
# @exam.update_attributes!(public: 1)
render_ok
end
def revoke_item
item = @exam.examination_items.find_by!(item_bank_id: params[:item_id])
ActiveRecord::Base.transaction do
@exam.examination_items.where(item_type: item.item_type).where("position > #{item.position}").update_all("position = position -1")
item.destroy!
end
render_ok
end
private
def form_params
params.permit(:discipline_id, :sub_discipline_id, :difficulty, :name, :duration, tag_discipline_id: [])
end
def find_exam
@exam = ExaminationBank.find_by!(id: params[:id])
end
def edit_auth
current_user.admin_or_business? || @exam.user == current_user
end
end

View File

@@ -0,0 +1,108 @@
class ExaminationIntelligentSettingsController < ApplicationController
before_action :require_login
before_action :certi_identity_auth, only: [:create, :optinal_items, :save_exam, :exchange_one_item, :exchange_items]
before_action :find_exam, only: [:exchange_one_item, :exchange_items, :save_exam]
def optinal_items
sub_discipline_id = params[:sub_discipline_id]
tag_discipline_id = params[:tag_discipline_id]
difficulty = params[:difficulty]
source = params[:source]
items = OptionalItemQuery.call(sub_discipline_id, tag_discipline_id, difficulty, source)
@single_question_count = items.select{ |item| item.item_type == "SINGLE" }.size
@multiple_question_count = items.select{ |item| item.item_type == "MULTIPLE" }.size
@judgement_question_count = items.select{ |item| item.item_type == "JUDGMENT" }.size
@program_question_count = items.select{ |item| item.item_type == "PROGRAM" }.size
end
def create
ActiveRecord::Base.transaction do
exam = ExaminationIntelligentSetting.new(user: current_user)
# 保存试卷基础信息
exam = ExaminationIntelligentSettings::SaveSettingService.call(exam, form_params)
render_ok({exam_setting_id: exam.id})
end
rescue ApplicationService::Error => ex
render_error(ex.message)
end
def save_exam
new_exam = ExaminationBank.new(user: current_user)
# 保存试卷基础信息
ExaminationIntelligentSettings::SaveExaminationService.call(new_exam, save_params, @exam)
render_ok
rescue ApplicationService::Error => ex
render_error(ex.message)
end
def exchange_one_item
item = @exam.item_baskets.find_by!(item_bank_id: params[:item_id])
exam_type_setting = @exam.examination_type_settings.find_by!(item_type: item.item_type)
# 获取可选的题
items = OptionalItemQuery.call(@exam.sub_discipline_id, @exam.tag_discipline_containers.pluck(:tag_discipline_id), @exam.difficulty, @exam.public)
# 可选题中去掉已组卷的试题
type_items = items.select{ |t_item| t_item.item_type == item.item_type }
optional_item_ids = (type_items.pluck(:id) - @exam.item_baskets.where(item_type: item.item_type).pluck(:item_bank_id)).uniq
# 如果可选的题数等于0则提示无可换的题
tip_exception("无可换的题") if optional_item_ids.size == 0
new_item = ItemBank.find optional_item_ids.sample(1).first
ActiveRecord::Base.transaction do
@exam.item_baskets << ItemBasket.new(item_bank_id: new_item.id, position: item.position, score: item.score, item_type: new_item.item_type)
item.destroy!
end
render_ok
end
def exchange_items
exam_type_setting = @exam.examination_type_settings.find_by!(item_type: params[:item_type])
choosed_items = @exam.item_baskets.where(item_type: params[:item_type])
# 获取可选的题
items = OptionalItemQuery.call(@exam.sub_discipline_id, @exam.tag_discipline_containers.pluck(:tag_discipline_id), @exam.difficulty, @exam.public)
type_items = items.select{ |t_item| t_item.item_type == params[:item_type] }
# 可选题中去掉已组卷的试题
choosed_item_ids = choosed_items.pluck(:item_bank_id)
optional_item_ids = (type_items.pluck(:id) - choosed_item_ids).uniq
# 如果可选的题数等于0则提示无可换的题
tip_exception("无可换的题") if optional_item_ids.size == 0
# 如果可选题数小于设置的题数n则在原来的选题中随机选n个确保换题时能选到新的题
if optional_item_ids.size < exam_type_setting.count
absence_count = exam_type_setting.count - optional_item_ids.size
optional_item_ids = optional_item_ids + choosed_item_ids.sample(absence_count)
end
ActiveRecord::Base.transaction do
# 取试题分数
score = choosed_items.first&.score || (params[:item_type] == "PROGRAM" ? 10 : 5)
choosed_items.destroy_all
optional_item_ids.sample(exam_type_setting.count).each_with_index do |item_id, index|
new_item = ItemBank.find item_id
@exam.item_baskets << ItemBasket.new(item_bank_id: new_item.id, position: index+1, score: score, item_type: new_item.item_type)
end
end
render_ok
end
private
def find_exam
@exam = ExaminationIntelligentSetting.find_by!(id: params[:id])
tip_exception(403,"无权限编辑") unless current_user.admin_or_business? || @exam.user_id == current_user.id
end
def form_params
params.permit(:discipline_id, :sub_discipline_id, :difficulty, :source, tag_discipline_id: [], question_settings: %i[item_type count])
end
def save_params
params.permit(:name, :duration)
end
end

Some files were not shown because too many files have changed in this diff Show More