Merge branch 'dev_Ysm' of https://bdgit.educoder.net/Hjqreturn/educoder into dev_aliyun

dev_cs_new
杨树明 6 years ago
commit d4c40327ad

1
.gitignore vendored

@ -46,6 +46,7 @@
/config/secrets.yml
/config/redis.yml
/config/elasticsearch.yml
/config/aliyun_vod.yml
public/upload.html
/config/configuration.yml

@ -0,0 +1,2 @@
// Place all the behaviors and hooks related to the matching controller here.
// All this logic will automatically be available in application.js.

@ -0,0 +1,2 @@
// Place all the behaviors and hooks related to the matching controller here.
// All this logic will automatically be available in application.js.

@ -0,0 +1,3 @@
// Place all the styles related to the blob controller here.
// They will automatically be included in application.css.
// You can use Sass (SCSS) here: http://sass-lang.com/

@ -0,0 +1,3 @@
// Place all the styles related to the projects controller here.
// They will automatically be included in application.css.
// You can use Sass (SCSS) here: http://sass-lang.com/

@ -239,7 +239,7 @@ class ApplicationController < ActionController::Base
uid_logger("user_setup: " + (User.current.logged? ? "#{User.current.try(:login)} (id=#{User.current.try(:id)})" : "anonymous"))
if !User.current.logged? && Rails.env.development?
User.current = User.find 57703
User.current = User.find 1
end
@ -354,7 +354,7 @@ class ApplicationController < ActionController::Base
logger.info("--uri_exec: .....res is #{res}")
JSON.parse(res)
rescue Exception => e
uid_logger("--uri_exec: exception #{e.message}")
uid_logger_error("--uri_exec: exception #{e.message}")
raise Educoder::TipException.new("实训平台繁忙繁忙等级84")
end
end
@ -567,6 +567,10 @@ class ApplicationController < ActionController::Base
time.blank? ? '' : time.strftime("%Y-%m-%d %H:%M:%S")
end
def strf_date(date)
date.blank? ? '' : date.strftime("%Y-%m-%d")
end
def logger_error(error)
Rails.logger.error(error.message)
error.backtrace.each { |msg| Rails.logger.error(msg) }

@ -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

@ -0,0 +1,24 @@
class Callbacks::AliyunVodsController < Callbacks::BaseController
before_action :check_signature_valid!
def create
Videos::DispatchCallbackService.call(params)
render_ok
end
private
def check_signature_valid!
return if AliyunVod::Sign.verify?(header_signature, header_timestamp)
render_not_acceptable
end
def header_timestamp
request.headers['X-VOD-TIMESTAMP']
end
def header_signature
request.headers['X-VOD-SIGNATURE']
end
end

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

@ -151,7 +151,7 @@ class ChallengesController < ApplicationController
def index
uid_logger("identifier: #{params}")
@challenges = Challenge.fields_for_list.where(shixun_id: @shixun.id)
@challenges = @shixun.challenges.fields_for_list
@editable = @shixun.status == 0 # before_action有判断权限如果没发布则肯定是管理人员
@user = current_user

@ -14,7 +14,7 @@ module RenderExpand
kit.stylesheets << Rails.root.join('app/templates', path)
end
send_data kit.to_pdf, filename: options[:filename], type: 'application/pdf'
send_data kit.to_pdf, filename: options[:filename], disposition: options[:disposition] || 'attachment', type: 'application/pdf'
end
end
end

@ -7,6 +7,10 @@ module RenderHelper
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 }

@ -13,7 +13,7 @@ class CoursesController < ApplicationController
before_action :require_login, except: [:index, :show, :students, :teachers, :board_list, :mine, :all_course_groups,
:left_banner, :top_banner]
before_action :check_account, only: [:new, :create, :apply_to_join_course]
before_action :check_account, only: [:new, :create, :apply_to_join_course, :join_excellent_course]
before_action :check_auth, except: [:index, :show, :students, :teachers, :board_list, :mine, :all_course_groups,
:left_banner, :top_banner, :apply_to_join_course, :exit_course]
before_action :set_course, :user_course_identity, only: [:show, :update, :destroy, :settings, :set_invite_code_halt,
@ -25,12 +25,13 @@ class CoursesController < ApplicationController
:transfer_to_course_group, :delete_from_course, :search_users, :add_students_by_search,
:base_info, :get_historical_courses, :create_group_by_importing_file,
:attahcment_category_list,:export_member_scores_excel, :duplicate_course,
:switch_to_teacher, :switch_to_assistant, :switch_to_student, :exit_course]
:switch_to_teacher, :switch_to_assistant, :switch_to_student, :exit_course,
:informs, :update_informs, :join_excellent_course, :online_learning]
before_action :teacher_allowed, only: [:update, :destroy, :settings, :search_teacher_candidate,
:transfer_to_course_group, :delete_from_course,
:search_users, :add_students_by_search, :get_historical_courses, :add_teacher_popup, :add_teacher]
before_action :admin_allowed, only: [:set_invite_code_halt, :set_public_or_private, :change_course_admin,
:set_course_group, :create_group_by_importing_file]
:set_course_group, :create_group_by_importing_file, :update_informs]
before_action :teacher_or_admin_allowed, only: [:graduation_group_list, :create_graduation_group, :join_graduation_group,
:change_course_teacher, :export_member_scores_excel, :course_group_list,
:teacher_application_review, :apply_teachers, :delete_course_teacher]
@ -117,13 +118,19 @@ class CoursesController < ApplicationController
authentication: params[:authentication], professional_certification: params[:professional_certification])
@course.tea_id = current_user.id
@course_list_name = params[:course_list_name].strip
@course_list = CourseList.find_by(name: @course_list_name)
if @course_list
@course.course_list_id = @course_list.id
if params[:subject_id].blank?
@course_list_name = params[:course_list_name].strip
@course_list = CourseList.find_by(name: @course_list_name)
if @course_list
@course.course_list_id = @course_list.id
else
new_course_list = CourseList.create!(name: @course_list_name, user_id: current_user.id, is_admin: 0)
@course.course_list_id = new_course_list.id
end
else
new_course_list = CourseList.create!(name: @course_list_name, user_id: current_user.id, is_admin: 0)
@course.course_list_id = new_course_list.id
@course.start_date = params[:start_date]
@course.subject_id = params[:subject_id]
@course.excellent = true
end
@course.is_end = @course.end_date.present? && @course.end_date < Date.today
@ -133,6 +140,13 @@ class CoursesController < ApplicationController
CourseInfo.create!(user_id: current_user.id, course_id: @course.id)
CourseMember.create!(course_id: @course.id, user_id: current_user.id, role: 1)
# 将实践课程的教学团队成员以教师身份加入课堂
if @course.subject
@course.subject.subject_members.where.not(user_id: current_user.id).each do |s_member|
CourseMember.create!(course_id: @course.id, user_id: s_member.user_id, role: 2)
end
end
course_module_types = params[:course_module_types]
@course.create_course_modules(course_module_types)
end
@ -151,7 +165,6 @@ class CoursesController < ApplicationController
begin
extra_params = Hash.new
extra_params[:school_id] = @school.id
extra_params[:is_public] = params[:is_public].present? ? params[:is_public] : 0
if @course.is_end && (course_params[:end_date].blank? || course_params[:end_date].to_date > Date.today)
extra_params[:is_end] = 0
@ -162,13 +175,18 @@ class CoursesController < ApplicationController
extra_params[:authentication] = params[:authentication]
extra_params[:professional_certification] = params[:professional_certification]
@course_list_name = params[:course_list_name].strip
@course_list = CourseList.find_by(name: @course_list_name)
if @course_list
extra_params[:course_list_id] = @course_list.id
if @course.subject
@course.start_date = params[:start_date]
else
new_course_list = CourseList.create(name: @course_list_name, user_id: current_user.id, is_admin: 0)
extra_params[:course_list_id] = new_course_list.id
extra_params[:is_public] = params[:is_public].present? ? params[:is_public] : 0
@course_list_name = params[:course_list_name].strip
@course_list = CourseList.find_by(name: @course_list_name)
if @course_list
extra_params[:course_list_id] = @course_list.id
else
new_course_list = CourseList.create(name: @course_list_name, user_id: current_user.id, is_admin: 0)
extra_params[:course_list_id] = new_course_list.id
end
end
@course.update_attributes!(course_params.merge(extra_params))
@ -183,6 +201,42 @@ class CoursesController < ApplicationController
end
end
def join_excellent_course
tip_exception("您已是课堂成员") if current_user.member_of_course?(@course)
tip_exception("请通过邀请码加入课堂") unless @course.excellent
tip_exception("该课堂已结束") if @course.is_end
begin
new_student = CourseMember.new(user_id: current_user.id, course_id: @course.id, role: 4)
new_student.save!
CourseAddStudentCreateWorksJob.perform_later(@course.id, [current_user.id])
StudentJoinCourseNotifyJob.perform_later(current_user.id, @course.id)
normal_status(0, "加入成功")
rescue => e
uid_logger_error(e.message)
tip_exception(e.message)
end
end
def informs
end
def update_informs
tip_exception("公告内容不能为空") if params[:description].blank?
inform = @course.inform || Inform.new(container: @course)
inform.description = params[:description]
inform.save!
normal_status("更新成功")
end
def online_learning
@subject = @course.subject
@stages = @subject&.stages
@user = current_user
@start_learning = @user_course_identity == Course::STUDENT && @subject&.learning?(current_user.id)
end
def search_course_list
search = params[:search] ? "%#{params[:search].strip}%" : "%%"
@course_lists = CourseList.where("name like ?", "#{search}")
@ -1075,14 +1129,31 @@ class CoursesController < ApplicationController
def validate_course_name
tip_exception("课堂名称不能为空!") if params[:course][:name].blank?
tip_exception("课程名称不能为空!") if params[:course_list_name].blank?
tip_exception("课堂名称应以课程名称开头命名") unless params[:course][:name].index(params[:course_list_name]) &&
if params[:subject_id].blank? || (@course && @course.subject.blank?)
tip_exception("课程名称不能为空!") if params[:course_list_name].blank?
tip_exception("课堂名称应以课程名称开头命名") unless params[:course][:name].index(params[:course_list_name]) &&
params[:course][:name].index(params[:course_list_name]) == 0
else
@subject = @course.present? ? @course.subject : Subject.find_by!(id: params[:subject_id])
tip_exception("开始时间不能为空") if params[:start_date].blank?
tip_exception("结束时间不能为空") if params[:end_date].blank?
tip_exception("结束时间必须晚于开始时间") if params[:end_date] <= params[:start_date]
tip_exception("开始时间和结束时间不能与往期开课时间重叠") if @course.nil? && @subject.max_course_end_date && params[:start_date] <= strf_date(@subject.max_course_end_date)
validate_start_end_date if @course.present?
tip_exception("开放课堂必须包含公告栏和在线学习模块") unless params[:course_module_types].include?("announcement") && params[:course_module_types].include?("online_learning")
end
tip_exception("课堂所属单位不能为空!") if params[:school].blank?
tip_exception("请至少添加一个课堂模块") if params[:course_module_types].blank?
@school = School.find_by!(name: params[:school].strip)
end
def validate_start_end_date
prev_course = @subject.courses.where("id < #{@course.id}").last
next_course = @subject.courses.where("id > #{@course.id}").first
tip_exception("开始时间和结束时间不能与其他期开课时间重叠") if prev_course && params[:start_date] <= strf_date(prev_course.end_date)
tip_exception("开始时间和结束时间不能与其他期开课时间重叠") if next_course && params[:end_date] >= strf_date(next_course.start_date)
end
# 超级管理员和课堂管理员的权限判断
def admin_allowed
unless @user_course_identity < Course::PROFESSOR
@ -1154,6 +1225,12 @@ class CoursesController < ApplicationController
#课堂的作业信息
shixun_homeworks = homeworks.search_homework_type(4) #全部实训作业
shixun_titles = shixun_homeworks.pluck(:name) + ["总得分"]
# 更新实训作业成绩
shixun_homeworks.includes(:homework_challenge_settings, :published_settings, :homework_commons_shixun).each do |homework|
homework.update_homework_work_score
end
shixun_homeworks = shixun_homeworks&.includes(score_student_works: :user)
common_homeworks = homeworks.search_homework_type(1) #全部普通作业

@ -1291,7 +1291,7 @@ class ExercisesController < ApplicationController
normal_status(0,"正在下载中")
else
set_export_cookies
render pdf: 'exercise_export/blank_exercise', filename: filename_, stylesheets: stylesheets
render pdf: 'exercise_export/blank_exercise', filename: filename_, stylesheets: stylesheets, disposition: 'inline', type:"pdf_attachment.content_type",stream:false
end
end

@ -260,7 +260,12 @@ class FilesController < ApplicationController
return normal_status(-2, "该课程下没有id为 #{params[:id]}的资源") if @file.nil?
return normal_status(403, "您没有权限进行该操作") if @user != @file.author && !@user.teacher_of_course?(@course) && !@file.public?
@is_pdf = false
file_content_type = @file.content_type
file_ext_type = File.extname(@file.filename).strip.downcase[1..-1]
if (file_content_type.present? && file_content_type.downcase.include?("pdf")) || (file_ext_type.present? && file_ext_type.include?("pdf"))
@is_pdf = true
end
@attachment_histories = @file.attachment_histories
end

@ -171,6 +171,14 @@ class HomeworkCommonsController < ApplicationController
@student_works = @student_works.where(user_id: group_user_ids)
end
if @homework.homework_type == "group" && !params[:member_work].blank?
if params[:member_work].to_i == 1
@student_works = @student_works.where("user_id = commit_user_id")
elsif params[:member_work].to_i == 0
@student_works = @student_works.where("user_id != commit_user_id")
end
end
# 输入姓名和学号搜索
# TODO user_extension 如果修改 请调整
unless params[:search].blank?
@ -255,29 +263,7 @@ class HomeworkCommonsController < ApplicationController
def update_score
tip_exception("作业还未发布,暂不能计算成绩") if @homework.publish_time.nil? || @homework.publish_time > Time.now
begin
if @homework.unified_setting
student_works = @homework.student_works
user_ids = @course.students.pluck(:user_id)
else
user_ids = @course.students.where(course_group_id: @homework.published_settings.pluck(:course_group_id)).pluck(:user_id)
student_works = @homework.student_works.where(user_id: user_ids)
end
student_works = student_works.includes(:challenge_work_scores)
challenge_settings = @homework.homework_challenge_settings
challenge_setting_ids = challenge_settings.pluck(:challenge_id)
myshixuns = Myshixun.where(shixun_id: @homework.homework_commons_shixun&.shixun_id, user_id: user_ids).includes(:games)
myshixuns.find_each(batch_size: 100) do |myshixun|
work = student_works.select{|work| work.user_id == myshixun.user_id}.first
if work && myshixun
games = myshixun.games.select{|game| challenge_setting_ids.include?(game.challenge_id)}
HomeworksService.new.update_myshixun_work_score work, myshixun, games, @homework, challenge_settings
end
end
HomeworksService.new.update_student_eff_score @homework if (@homework.allow_late && @homework.late_time < Time.now) ||
(!@homework.allow_late && @homework.end_time < Time.now)
@homework.update_attribute('calculation_time', Time.now)
@homework.update_homework_work_score
normal_status("更新成功")
rescue Exception => e
uid_logger(e.message)
@ -1358,7 +1344,7 @@ class HomeworkCommonsController < ApplicationController
shixun = @homework.shixuns.take
# 通过代码文件来判断语言
language = shixun.challenges.practice_type.pluck(:path).first
language = language.split("")[0].split(".")[1].downcase if language.present?
language = language.split("")[0].split(".").last.downcase if language.present?
user_lists = []
if language.present? && (language == "java" || language == "py")
user_ids = @course.course_members.where(course_group_id: params[:group_ids]).distinct(:user_id).pluck(:user_id)
@ -1481,7 +1467,8 @@ class HomeworkCommonsController < ApplicationController
if game_codes.count > 0
code_rate += game_codes.map(&:rate).sum / challenge.path.split("").length
end
target = game_codes.count > 0 ? game_codes[0].target_user_id : nil
logger.info("#####game_codes: #{game_codes}")
#target = game_codes.count > 0 ? game_codes[0].target_user_id : nil
# 作品完成时间
game = challenge.games.find_by(user_id: @user.id)
end_time = game.end_time
@ -1489,7 +1476,7 @@ class HomeworkCommonsController < ApplicationController
all_score = homework_challenge_settings.find_by(challenge_id: challenge.id).try(:score).to_f
final_score = @student_work.work_challenge_score game, all_score
# 抄袭用户
copy_user = User.find_by_id(game_codes[0].target_user_id)
copy_user = User.find_by_id(game_codes[0].try(:target_user_id))
copy_end_time = copy_user.games.find_by(challenge_id: challenge.id).try(:end_time) if copy_user.present?
# 代码部分
code_list = []
@ -1499,7 +1486,7 @@ class HomeworkCommonsController < ApplicationController
code_list << {path: path, origin_content: info.origin_content, target_content: info.target_content}
end
end
# TODO: 这里本来应该前端做的,但是现在页面已经刷不开了。
{code_rate: code_rate, copy_user_id: copy_user.try(:id), end_time: end_time, final_score: final_score,
all_score: all_score, copy_end_time: copy_end_time, copy_username: copy_user.try(:full_name),
username: game.user.full_name, code_list: code_list, subject: challenge.subject, position: challenge.position,

@ -23,24 +23,19 @@ class MyshixunsController < ApplicationController
begin
ActiveRecord::Base.transaction do
begin
@shixun = Shixun.select(:id, :identifier).find(@myshixun.shixun_id)
@shixun = Shixun.select(:id, :identifier, :challenges_count).find(@myshixun.shixun_id)
@myshixun.destroy!
StudentWork.where(:myshixun_id => @myshixun.id).update_all(:myshixun_id => 0, :work_status => 0)
# 实训在申请发布前,是否玩过实训,如果玩过需要更改记录,防止二次重置
shixun_mod = ShixunModify.where(:shixun_id => @shixun.id, :myshixun_id => @myshixun.id, :status => 1).take
shixun_mod.update_column(:status, 0) if shixun_mod
rescue Exception => e
logger.error("######reset_my_game_failed:#{e.message}")
raise("ActiveRecord::RecordInvalid")
end
end
# 删除版本库
GitService.delete_repository(repo_path: @repo_path)
GitService.delete_repository(repo_path: @repo_path) unless @shixun.is_choice_type?
rescue Exception => e
if e.message != "ActiveRecord::RecordInvalid"
logger.error("######delete_repository_error:#{e.message}")
logger.error("######delete_repository_error-:#{e.message}")
end
raise "delete_repository_error:#{e.message}"
end

@ -18,7 +18,7 @@ class StagesController < ApplicationController
@stage.position = @subject.stages.count + 1
@stage.save!
unless params[:shixun_id].blank?
shixuns = Shixun.where(id: params[:shixun_id])
shixuns = Shixun.where(id: params[:shixun_id]).order("field(id, #{params[:shixun_id].join(",")})")
shixuns.each do |shixun|
StageShixun.create!(stage_id: @stage.id, subject_id: @subject.id, shixun_id: shixun.id, position: @stage.stage_shixuns.count + 1)
end

@ -4,7 +4,7 @@ class StudentWorksController < ApplicationController
before_action :require_login, :check_auth
before_action :find_homework, only: [:new, :create, :search_member_list, :check_project, :relate_project,
:cancel_relate_project]
:cancel_relate_project, :delete_work]
before_action :find_work, only: [:shixun_work_report, :adjust_review_score, :shixun_work, :commit_des, :update_des,
:adjust_score, :show, :adjust_score, :supply_attachments, :revise_attachment,
:comment_list, :add_score, :add_score_reply, :destroy_score, :appeal_anonymous_score,
@ -15,12 +15,12 @@ class StudentWorksController < ApplicationController
before_action :teacher_allowed, only: [:adjust_score, :adjust_review_score, :deal_appeal_score]
before_action :course_student, only: [:new, :commit_des, :update_des, :create, :edit, :update, :search_member_list, :relate_project,
:cancel_relate_project, :relate_project]
:cancel_relate_project, :relate_project, :delete_work]
before_action :my_work, only: [:commit_des, :update_des, :edit, :update, :revise_attachment, :appeal_anonymous_score,
:cancel_appeal]
before_action :edit_duration, only: [:edit, :update]
before_action :edit_duration, only: [:edit, :update, :delete_work]
before_action :end_or_late, only: [:new, :create, :search_member_list, :commit_des, :update_des]
before_action :require_score_id, only: [:destroy_score, :add_score_reply, :appeal_anonymous_score, :deal_appeal_score, :cancel_appeal]
@ -60,6 +60,25 @@ class StudentWorksController < ApplicationController
@members = @members.page(page).per(limit).includes(:course_group, user: :user_extension)
end
def delete_work
ActiveRecord::Base.transaction do
begin
work = @homework.student_works.find_by!(user_id: params[:user_id])
tip_exception("只有组长才能删除组员") if work.commit_user_id != current_user.id
work.update_attributes(description: nil, project_id: 0,
late_penalty: 0, work_status: 0,
commit_time: nil, update_time: nil, group_id: 0,
commit_user_id: nil, final_score: nil, work_score: nil, teacher_score: nil, teaching_asistant_score: nil)
work.attachments.destroy_all
work.tidings.destroy_all
normal_status("删除成功")
rescue Exception => e
uid_logger(e.message)
tip_exception(e.message)
end
end
end
def create
student_work = @homework.student_works.find_or_create_by(user_id: current_user.id)
@ -123,8 +142,9 @@ class StudentWorksController < ApplicationController
@current_user = current_user
if @homework.homework_type == "group"
# todo user_extension
@commit_user_id = @work.commit_user_id
@work_members = @course.students.where(user_id: @homework.student_works.where(group_id: @work.group_id).pluck(:user_id)).
order("course_members.id=#{@work.user_id} desc").includes(:course_group, user: :user_extension)
order("course_members.user_id=#{@work.commit_user_id} desc").includes(:course_group, user: :user_extension)
end
end
@ -136,7 +156,7 @@ class StudentWorksController < ApplicationController
begin
@work.description = params[:description]
@work.update_time = Time.now
@work.commit_user_id = current_user.id
# @work.commit_user_id = current_user.id
if @work.save!
Attachment.associate_container(params[:attachment_ids], @work.id, @work.class)
@ -151,7 +171,8 @@ class StudentWorksController < ApplicationController
# 原成员更新描述、更新时间以及附件
@homework.student_works.where(group_id: @work.group_id, user_id: (work_user_ids & params_user_ids)).each do |work|
work.update_attributes(update_time: Time.now, description: @work.description, commit_user_id: current_user.id)
# work.update_attributes(update_time: Time.now, description: @work.description, commit_user_id: current_user.id)
work.update_attributes(update_time: Time.now, description: @work.description)
work.attachments.destroy_all
@work.attachments.each do |attachment|
att = attachment.copy
@ -179,7 +200,7 @@ class StudentWorksController < ApplicationController
stu_work.update_attributes(user_id: user_id, description: @work.description, homework_common_id: @homework.id,
project_id: @work.project_id, late_penalty: @work.late_penalty,
work_status: @work.work_status, commit_time: Time.now, update_time: Time.now,
group_id: @work.group_id, commit_user_id: current_user.id)
group_id: @work.group_id, commit_user_id: @work.commit_user_id)
@work.attachments.each do |attachment|
att = attachment.copy
att.author_id = attachment.author_id
@ -460,7 +481,7 @@ class StudentWorksController < ApplicationController
filename_ = "#{@use&.student_id}_#{@use&.real_name}_#{@shixun&.name}_#{Time.now.strftime('%Y%m%d_%H%M%S')}"
filename = Base64.urlsafe_encode64(filename_.strip)
stylesheets = %w(shixun_work/shixun_work.css shared/codemirror.css)
render pdf: 'shixun_work/shixun_work', filename: filename, stylesheets: stylesheets
render pdf: 'shixun_work/shixun_work', filename: filename, stylesheets: stylesheets, disposition: 'inline', type:"pdf_attachment.content_type",stream:false
end
# 作品调分

@ -25,13 +25,13 @@ class SubjectsController < ApplicationController
if reorder == "myshixun_count"
if select
@subjects = Subject.find_by_sql("SELECT subjects.id, subjects.user_id, subjects.name, subjects.stages_count, subjects.repertoire_id, subjects.status,
subjects.shixuns_count, sum(shixuns.myshixuns_count) AS myshixun_member_count FROM subjects join stage_shixuns
subjects.shixuns_count, subjects.excellent, sum(shixuns.myshixuns_count) AS myshixun_member_count FROM subjects join stage_shixuns
on stage_shixuns.subject_id = subjects.id join shixuns on shixuns.id = stage_shixuns.shixun_id where
subjects.hidden = 0 AND subjects.status = 2 AND subjects.name like '%#{search}%'
AND subjects.repertoire_id = #{select} GROUP BY subjects.id ORDER BY myshixun_member_count DESC")
else
@subjects = Subject.find_by_sql("SELECT subjects.id, subjects.user_id, subjects.name, subjects.stages_count, subjects.repertoire_id, subjects.status,
subjects.shixuns_count, sum(shixuns.myshixuns_count) AS myshixun_member_count FROM subjects join stage_shixuns
subjects.shixuns_count, subjects.excellent, sum(shixuns.myshixuns_count) AS myshixun_member_count FROM subjects join stage_shixuns
on stage_shixuns.subject_id = subjects.id join shixuns on shixuns.id = stage_shixuns.shixun_id where
subjects.hidden = 0 AND subjects.status = 2 AND subjects.name like '%#{search}%'
GROUP BY subjects.id ORDER BY myshixun_member_count DESC")
@ -81,6 +81,7 @@ class SubjectsController < ApplicationController
def show
@user = current_user
@is_creator = current_user.creator_of_subject?(@subject)
@is_manager = @user.manager_of_subject?(@subject)
# 合作团队
@members = @subject.subject_members.includes(:user)
@shixuns = @subject.shixuns.published.pluck(:id)
@ -281,7 +282,7 @@ class SubjectsController < ApplicationController
# 删除实训
# DELETE: /api/subejcts/:id/delete_member
def delete_member
tip_exception(403, "没权限操作") if !current_user.admin?
tip_exception(403, "没权限操作") unless current_user.manager_of_subject?(@subject)
tip_exception('用户id不能为空') if params[:user_id].blank?
user = @subject.subject_members.where(:user_id => params[:user_id], :role => 2).first
tip_exception("管理员用户不允许删除,或用户不存在") if user.blank?

@ -21,7 +21,7 @@ class Users::BaseController < ApplicationController
def private_user_resources!
require_login
return if current_user.admin? || observed_logged_user?
return if current_user.admin_or_business? || observed_logged_user?
render_forbidden
end

@ -0,0 +1,26 @@
class Users::VideoAuthsController < Users::BaseController
before_action :private_user_resources!
def create
result = Videos::CreateAuthService.call(observed_user, create_params)
render_ok(data: result)
rescue Videos::CreateAuthService::Error => ex
render_error(ex.message)
end
def update
video = observed_user.videos.find_by(uuid: params[:video_id])
return render_error('该视频凭证不存在') if video.blank?
result = AliyunVod::Service.refresh_upload_video(video.uuid)
render_ok(data: result)
rescue AliyunVod::Error => _
render_error('刷新上传凭证失败')
end
private
def create_params
params.permit(:title, :file_name, :file_size, :description, :cover_url)
end
end

@ -0,0 +1,60 @@
class Users::VideosController < Users::BaseController
before_action :private_user_resources!
helper_method :current_video
def index
videos = Users::VideoQuery.call(observed_user, search_params)
@count = videos.count
@videos = paginate videos
end
def update
return render_error('该状态下不能编辑视频信息') unless current_video.published?
current_video.update!(title: params[:title])
AliyunVod::Service.update_video_info(current_video.uuid, Title: current_video.title) rescue nil
end
def cancel
video = observed_user.videos.find_by(uuid: params[:video_id])
return render_not_found if video.blank?
return render_error('该状态下不能删除视频') unless video.pending?
video.destroy!
AliyunVod::Service.delete_video([video.uuid]) rescue nil
render_ok
end
def review
params[:status] = 'processing'
videos = Users::VideoQuery.call(observed_user, params)
@count = videos.count
@videos = paginate videos
end
def batch_publish
Videos::BatchPublishService.call(observed_user, batch_publish_params)
render_ok
rescue Videos::BatchPublishService::Error => ex
render_error(ex.message)
end
private
def current_video
@_current_video ||= observed_user.videos.find_by(id: params[:id])
end
def search_params
params.permit(:keyword, :sort_by, :sort_direction)
end
def batch_publish_params
params.permit(videos: %i[video_id title])
end
end

@ -372,4 +372,12 @@ module TidingDecorator
I18n.t(locale_format(tiding_type)) % [container.try(:title) || extra]
end
end
def video_content
if tiding_type == 'System'
I18n.t(locale_format(tiding_type, status), reason: extra) % container.try(:title)
else
I18n.t(locale_format(tiding_type)) % [container.try(:title) || extra]
end
end
end

@ -0,0 +1,5 @@
module VideoDecorator
extend ApplicationDecorator
display_time_method :published_at, :created_at, :updated_at
end

@ -0,0 +1,2 @@
module BlobHelper
end

@ -28,6 +28,10 @@ module CoursesHelper
def module_url mod, course
return nil if mod.blank? or course.blank?
case mod.module_type
when "announcement"
"/courses/#{course.id}/informs"
when "online_learning"
"/courses/#{course.id}/online_learning"
when "shixun_homework"
"/courses/#{course.id}/shixun_homeworks/#{mod.id}"
when "common_homework"
@ -261,4 +265,10 @@ module CoursesHelper
group_info
end
def last_subject_shixun user_id, subject
myshixun = Myshixun.where(user_id: user_id, shixun_id: subject&.shixuns).order("updated_at desc").first
return "" unless myshixun
stage_shixun = subject&.stage_shixuns.where(shixun_id: myshixun.shixun_id).take
progress = stage_shixun&.position.to_s + "-" + stage_shixun&.position.to_s + " " + myshixun.shixun&.name
end
end

@ -146,8 +146,8 @@ module ExportHelper
w_6 = "--"
end
w_7 = w.work_status == 0 ? '--' : myshixun.try(:passed_count).to_s+"/"+shixun.challenges_count.to_s
w_8 = myshixun ? myshixun.try(:passed_time) == "--" ? "--" : format_time(myshixun.try(:passed_time)) : "--" # 通关时间
w_9 = myshixun ? (myshixun.try(:passed_count) > 0 ? myshixun.total_spend_time : '--') : "--" #总耗时
w_8 = myshixun ? myshixun.try(:passed_time).to_s == "--" ? "--" : format_time(myshixun.try(:passed_time)) : "--" # 通关时间
w_9 = myshixun ? (myshixun.try(:passed_count).to_i > 0 ? myshixun.total_spend_time : '--') : "--" #总耗时
w_10 = myshixun ? myshixun.output_times : 0 #评测次数
w_11 = myshixun ? myshixun.total_score : "--" #获得经验值
w_12 = w.final_score.present? ? w.final_score : 0
@ -543,7 +543,7 @@ module ExportHelper
end
def format_sheet_name name
name = name.gsub(":", "-")
name = name.gsub(":", "-").gsub("/", "_")
end
def rename_same_file(name, index)

@ -0,0 +1,2 @@
module ProjectsHelper
end

@ -2,7 +2,7 @@ module StagesHelper
# 章节实训的通关情况
def stage_myshixun_status shixun, user
myshixun = Myshixun.where(user_id: user.id, shixun_id: shixun.id).first
myshixun = Myshixun.where(user_id: user.id, shixun_id: shixun.id).take
myshixun.try(:status) == 1 ? 1 : 0
end

@ -103,6 +103,10 @@ module StudentWorksHelper
# 对结果进行排序
efficiency_list =
results.sort {|x, y| x[:efficiency] <=> y[:efficiency]}.each_with_index.map do |result, index|
if result[:user_id] == work.user.id
myself_eff = [index+1, result[:efficiency]]
myself_object = [result[:consume_time], result[:eff_score], result[:round_size]]
end
[index + 1, result[:efficiency], result[:user_id]]
end

@ -11,7 +11,7 @@ class ApplyTeacherRoleJoinCourseNotifyJob < ApplicationJob
belong_container_type tiding_type extra created_at updated_at]
same_attrs = {
trigger_user_id: user.id, container_id: course.id, container_type: 'JoinCourse',
trigger_user_id: user.id, container_id: course.id, container_type: 'JoinCourse', status: 0,
belong_container_id: course.id, belong_container_type: 'Course', tiding_type: 'Apply', extra: role
}
Tiding.bulk_insert(*attrs) do |worker|

@ -0,0 +1,23 @@
# 批量发布视频 消息任务
class BatchPublishVideoNotifyJob < ApplicationJob
queue_as :notify
def perform(user_id, video_ids)
user = User.find_by(id: user_id)
return if user.blank?
attrs = %i[user_id trigger_user_id container_id container_type tiding_type status created_at updated_at]
same_attrs = {
user_id: 1,
trigger_user_id: user.id,
container_type: 'Video',
tiding_type: 'Apply', status: 0
}
Tiding.bulk_insert(*attrs) do |worker|
user.videos.where(id: video_ids).each do |video|
worker.add same_attrs.merge(container_id: video.id)
end
end
end
end

@ -0,0 +1,17 @@
# 获取阿里云视频信息
class GetAliyunVideoInfoJob < ApplicationJob
queue_as :default
def perform(vod_video_id)
video = Video.find_by(uuid: vod_video_id)
return if video.blank? || video.vod_uploading?
result = AliyunVod::Service.get_play_info(video.uuid)
cover_url = result.dig('VideoBase', 'CoverURL')
file_url = (result.dig('PlayInfoList', 'PlayInfo') || []).first&.[]('PlayURL')
video.cover_url = cover_url if cover_url.present? && video.cover_url.blank?
video.file_url = file_url if file_url.present?
video.save!
end
end

@ -0,0 +1,5 @@
module AliyunVod
class << self
attr_accessor :access_key_id, :access_key_secret, :base_url, :cate_id, :callback_url, :signature_key
end
end

@ -0,0 +1,2 @@
class AliyunVod::Error < StandardError
end

@ -0,0 +1,8 @@
module AliyunVod::Service
extend AliyunVod::Service::Base
extend AliyunVod::Service::VideoUpload
extend AliyunVod::Service::VideoProcess
extend AliyunVod::Service::VideoManage
extend AliyunVod::Service::VideoPlay
end

@ -0,0 +1,46 @@
module AliyunVod::Service::Base
def request(method, params)
params = AliyunVod::Sign.format_params(params.compact) # 多层hash需要预先处理保证值为string
params[:Signature] = AliyunVod::Sign.generate(params, method: method.to_s.upcase)
Rails.logger.info("[AliyunVod] request => method: #{method}, params: #{params}")
response = Faraday.public_send(method, AliyunVod.base_url, params)
result = JSON.parse(response.body)
Rails.logger.info("[AliyunVod] response => status: #{response.status}, result: #{result}")
raise AliyunVod::Error, result['Code'] if response.status != 200
result
rescue => ex
::Util.logger_error(ex)
raise AliyunVod::Error, ex.message
end
def base_params
{
AccessKeyId: AliyunVod.access_key_id,
Format: 'JSON',
Version: '2017-03-21',
SignatureMethod: 'HMAC-SHA1',
SignatureVersion: '1.0',
SignatureNonce: signature_nonce,
Timestamp: timestamp,
UserData: user_data
}
end
def user_data
{ MessageCallback: { CallbackURL: AliyunVod.callback_url } }
end
def timestamp
Time.now.utc.iso8601
end
def signature_nonce
chars = ('a'..'z').to_a + ('A'..'Z').to_a + ('0'..'9').to_a
chars.sample(16).join('')
end
end

@ -0,0 +1,40 @@
# 视频管理
module AliyunVod::Service::VideoManage
# 修改视频信息
def update_video_info(video_id, **opts)
params = {
Action: 'UpdateVideoInfo',
VideoId: video_id
}.merge(base_params)
params = opts.merge(params)
result = request(:post, params)
result
end
# 获取视频信息
def get_video_info(video_id)
params = {
Action: 'GetVideoInfo',
VideoId: video_id
}.merge(base_params)
result = request(:post, params)
result
end
# 删除视频信息
def delete_video(video_ids)
params = {
Action: 'DeleteVideo',
VideoIds: video_ids.join(',')
}.merge(base_params)
result = request(:post, params)
result
end
end

@ -0,0 +1,17 @@
# 视频播放
module AliyunVod::Service::VideoPlay
# 获取视频播放地址
# https://help.aliyun.com/document_detail/56124.html?spm=a2c4g.11186623.6.715.4d7e2d52dU1CTK
def get_play_info(video_id, **opts)
params = {
Action: 'GetPlayInfo',
VideoId: video_id
}.merge(base_params)
params = opts.merge(params)
result = request(:post, params)
result
end
end

@ -0,0 +1,17 @@
# 视频处理
module AliyunVod::Service::VideoProcess
# 提交媒体截图作业
def submit_snapshot_job(video_id, **opts)
params = {
Action: 'SubmitSnapshotJob',
VideoId: video_id
}.merge(base_params)
params = opts.merge(params)
result = request(:post, params)
raise AliyunVod::Error, '提交媒体截图作业失败' if result['SnapshotJob'].blank?
result
end
end

@ -0,0 +1,37 @@
# 视频上传
module AliyunVod::Service::VideoUpload
# 获取视频上传地址和凭证
def create_upload_video(title, filename, **opts)
params = {
Action: 'CreateUploadVideo',
Title: title,
FileName: filename
}.merge(base_params)
# 分类
cate_id = AliyunVod.cate_id
params[:CateId] = cate_id if cate_id.present?
params = opts.merge(params)
result = request(:post, params)
raise AliyunVod::Error, '获取上传凭证失败' if result['UploadAddress'].blank?
result
end
# 刷新视频上传凭证
def refresh_upload_video(video_id)
params = {
Action: 'RefreshUploadVideo',
VideoId: video_id
}.merge(base_params)
result = request(:post, params)
raise AliyunVod::Error, '刷新上传凭证失败' if result['UploadAddress'].blank?
result
end
end

@ -0,0 +1,41 @@
module AliyunVod::Sign
# https://help.aliyun.com/document_detail/44434.html?spm=a2c4g.11186623.2.16.354c7853oqlhMb&/#SignatureNonce
def self.generate(params, **opts)
method = opts[:method] || 'POST'
key = opts[:key] || AliyunVod.access_key_secret + '&'
digest = OpenSSL::Digest.new('sha1')
str = params_to_string(params)
str = percent_encode(str)
str = "#{method}&%2F&#{str}"
Base64.encode64(OpenSSL::HMAC.digest(digest, key, str)).gsub(/\n/, '')
end
def self.verify?(signature, timestamp)
content = "#{AliyunVod.callback_url}|#{timestamp}|#{AliyunVod.signature_key}"
our_signature = Digest::MD5.hexdigest(content)
ActiveSupport::SecurityUtils.secure_compare(signature, our_signature)
end
def self.params_to_string(params)
params.sort.map { |k, v| "#{percent_encode(k)}=#{percent_encode(v)}" }.join('&')
end
def self.percent_encode(str)
return '' if str.blank?
CGI::escape(str.to_s).gsub(/\+/,'%20').gsub(/\*/,'%2A').gsub(/%7E/,'~')
end
def self.format_params(params)
params.each_with_object({}) do |arr, obj|
obj[arr[0]] = arr[1].is_a?(Hash) ? parse_hash_to_str(arr[1]) : arr[1]
end
end
def self.parse_hash_to_str(hash)
hash.each_with_object({}) do |h, obj|
obj[h[0]] = h[1].is_a?(Hash) ? parse_hash_to_str(h[1].clone) : h[1].to_s
end.to_json
end
end

@ -5,7 +5,7 @@ class BiddingUser < ApplicationRecord
belongs_to :project_package, counter_cache: true
aasm(:status) do
state :pending, initiali: true
state :pending, initial: true
state :bidding_won
state :bidding_lost

@ -1,7 +1,6 @@
class Challenge < ApplicationRecord
# difficulty: 关卡难度: 1.简单 2.中等 3.困难
# show_type: 效果展示:-1.无效果 1.图片 2.apk/exe 3.txt 4.html 5.mp3 6.mp4
default_scope { order("challenges.position asc") }
belongs_to :shixun, :touch => true, counter_cache: true
belongs_to :user

@ -5,7 +5,11 @@ class Course < ApplicationRecord
belongs_to :teacher, class_name: 'User', foreign_key: :tea_id # 定义一个方法teacher该方法通过tea_id来调用User表
belongs_to :school, class_name: 'School', foreign_key: :school_id #定义一个方法school该方法通过school_id来调用School表
belongs_to :course_list
belongs_to :course_list, optional: true
# 所属实践课程
belongs_to :subject, optional: true
has_one :inform, as: :container, dependent: :destroy
has_many :course_infos, dependent: :destroy
# 课堂左侧导航栏的模块
@ -87,7 +91,7 @@ class Course < ApplicationRecord
NORMAL = 6 # 普通用户
Anonymous = 7 # 普未登录
validates :name, presence: true, length: { maximum: 30 }
validates :name, presence: true, length: { maximum: 60 }
after_create :create_board_sync, :act_as_course_activity, :send_tiding
@ -176,7 +180,7 @@ class Course < ApplicationRecord
end
def all_course_module_types
%w[activity shixun_homework common_homework group_homework graduation exercise poll attachment board course_group]
%w[activity announcement online_learning shixun_homework common_homework group_homework graduation exercise poll attachment board course_group]
end
def get_course_module_by_type(type)
@ -327,13 +331,15 @@ class Course < ApplicationRecord
#创建课程后,给该用户发送消息
def send_tiding
self.tidings << Tiding.new(user_id: tea_id, trigger_user_id: tea_id, belong_container_id: id,
self.tidings << Tiding.new(user_id: tea_id, trigger_user_id: 1, belong_container_id: id,
belong_container_type: 'Course', tiding_type: 'System')
end
def get_name_by_type(type)
case type
when 'activity' then '动态'
when 'announcement' then '公告栏'
when 'online_learning' then '在线学习'
when 'shixun_homework' then '实训作业'
when 'common_homework' then '普通作业'
when 'group_homework' then '分组作业'
@ -350,15 +356,17 @@ class Course < ApplicationRecord
def get_position_by_type(type)
case type
when 'activity' then 1
when 'shixun_homework' then 2
when 'common_homework' then 3
when 'group_homework' then 4
when 'graduation' then 5
when 'exercise' then 6
when 'poll' then 7
when 'attachment' then 8
when 'board' then 9
when 'course_group' then 10
when 'announcement' then 2
when 'online_learning' then 3
when 'shixun_homework' then 4
when 'common_homework' then 5
when 'group_homework' then 6
when 'graduation' then 7
when 'exercise' then 8
when 'poll' then 9
when 'attachment' then 10
when 'board' then 11
when 'course_group' then 12
else 100
end
end

@ -8,7 +8,7 @@ class CourseGroup < ApplicationRecord
has_many :homework_group_reviews, :dependent => :destroy
scope :by_group_ids, lambda { |ids| where(id: ids)}
validates :name, length: { maximum: 20 }
validates :name, length: { maximum: 60 }
after_create :generate_invite_code

@ -28,7 +28,7 @@ class CourseMessage < ApplicationRecord
def send_deal_tiding deal_status
# 发送申请处理结果消息
Tiding.create!(
user_id: course_message_id, trigger_user: User.current, container_id: course_id, container_type: 'DealCourse',
user_id: course_message_id, trigger_user_id: 1, container_id: course_id, container_type: 'DealCourse',
belong_container: course, extra: content.to_i == 2 ? '9' : '7', tiding_type: 'System', status: deal_status
)
# 将申请消息置为已处理

@ -264,4 +264,30 @@ class HomeworkCommon < ApplicationRecord
def challenge_score challenge_id
homework_challenge_settings.find_by(challenge_id: challenge_id)&.score.to_f
end
def update_homework_work_score
if unified_setting
works = student_works
user_ids = course.students.pluck(:user_id)
else
user_ids = course.students.where(course_group_id: published_settings.pluck(:course_group_id)).pluck(:user_id)
works = student_works.where(user_id: user_ids)
end
works = works.includes(:challenge_work_scores)
challenge_settings = homework_challenge_settings
challenge_setting_ids = challenge_settings.pluck(:challenge_id)
myshixuns = Myshixun.where(shixun_id: homework_commons_shixun&.shixun_id, user_id: user_ids).includes(:games)
myshixuns.find_each(batch_size: 100) do |myshixun|
work = works.select{|work| work.user_id == myshixun.user_id}.first
if work && myshixun
games = myshixun.games.select{|game| challenge_setting_ids.include?(game.challenge_id)}
HomeworksService.new.update_myshixun_work_score work, myshixun, games, self, challenge_settings
end
end
HomeworksService.new.update_student_eff_score(self) if (allow_late && late_time < Time.now) ||
(!allow_late && end_time < Time.now)
update_attribute('calculation_time', Time.now)
end
end

@ -0,0 +1,3 @@
class Inform < ApplicationRecord
belongs_to :container, polymorphic: true, optional: true
end

@ -16,7 +16,7 @@ class Library < ApplicationRecord
validates :uuid, presence: true, uniqueness: true
aasm(:status) do
state :pending, initiali: true
state :pending, initial: true
state :processing
state :refused
state :published

@ -4,7 +4,7 @@ class LibraryApply < ApplicationRecord
belongs_to :library
aasm(:status) do
state :pending, initiali: true
state :pending, initial: true
state :refused
state :agreed

@ -82,7 +82,7 @@ class Myshixun < ApplicationRecord
# 通关时间
def passed_time
self.status == 1 ? self.games.map(&:end_time).max : "--"
self.status == 1 ? self.games.select{|game| game.status == 2}.map(&:end_time).max : "--"
end
# 耗时

@ -0,0 +1,3 @@
class Partner < ApplicationRecord
has_many :users
end

@ -17,7 +17,7 @@ class ProjectPackage < ApplicationRecord
scope :invisible, -> { where(status: %i[pending applying refused]) }
aasm(:status) do
state :pending, initiali: true
state :pending, initial: true
state :applying
state :refused
state :published

@ -4,7 +4,7 @@ class ProjectPackageApply < ApplicationRecord
belongs_to :project_package
aasm(:status) do
state :pending, initiali: true
state :pending, initial: true
state :refused
state :agreed

@ -21,8 +21,8 @@ module Searchable::Course
def to_searchable_json
{
id: id,
author_name: teacher.real_name,
author_school_name: teacher.school_name,
author_name: teacher&.real_name,
author_school_name: teacher&.school_name,
visits_count: visits,
members_count: members_count,
is_public: is_public == 1

@ -5,7 +5,7 @@ class Shixun < ApplicationRecord
# hide_code 隐藏代码窗口
# code_hidden: 隐藏代码目录
# task_pass: 跳关
has_many :challenges, dependent: :destroy
has_many :challenges, -> {order("challenges.position asc")}, dependent: :destroy
has_many :challenge_tags, through: :challenges
has_many :myshixuns, :dependent => :destroy
has_many :shixun_members, dependent: :destroy

@ -123,10 +123,10 @@ class StudentWork < ApplicationRecord
# 更新作品成绩
def set_work_score
if work_status > 0 && homework_common && homework_common.homework_detail_manual && !self.ultimate_score
if work_status > 0 && homework_common && !self.ultimate_score
case homework_common.homework_type
when "normal", "group"
if !homework_common.homework_detail_manual.final_mode
if !homework_common&.homework_detail_manual&.final_mode
tea_ass_proportion = homework_common.homework_detail_manual.ta_proportion
tea_proportion = homework_common.homework_detail_manual.te_proportion
if self.teacher_score

@ -18,6 +18,9 @@ class Subject < ApplicationRecord
has_many :tidings, as: :container, dependent: :destroy
has_many :stages, -> { order("stages.position ASC") }, dependent: :destroy
# 开放课堂
has_many :courses, -> { order("courses.id ASC") }
validates :name, length: { maximum: 40 }
validates :description, length: { maximum: 5000 }
validates :learning_notes, length: { maximum: 500 }
@ -31,6 +34,11 @@ class Subject < ApplicationRecord
self.tidings << Tiding.new(user_id: self.user_id, trigger_user_id: self.user_id, belong_container_id: self.id, belong_container_type: 'Subject', tiding_type: "System", viewed: 0)
end
# 所有开课课堂的最大结束时间
def max_course_end_date
courses.pluck(:end_date).max
end
# 挑战过路径的成员数
def member_count
shixuns.pluck(:myshixuns_count).sum
@ -92,4 +100,8 @@ class Subject < ApplicationRecord
challenges = Challenge.where(shixun_id: shixuns.unhidden)
@tags = ChallengeTag.where(challenge_id: challenges).pluck(:name).uniq
end
def learning? user_id
Myshixun.where(user_id: user_id, shixun_id: shixuns).exists?
end
end

@ -60,8 +60,8 @@ class User < ApplicationRecord
has_many :games, :dependent => :destroy
has_many :created_subjects, foreign_key: :user_id, class_name: 'Subject'
has_many :subjects, :through => :subject_members
has_many :subject_members, :dependent => :destroy
has_many :subjects, :through => :subject_members
has_many :grades, :dependent => :destroy
has_many :experiences, :dependent => :destroy
has_many :student_works, :dependent => :destroy
@ -135,6 +135,11 @@ class User < ApplicationRecord
# 教学案例
has_many :libraries, dependent: :destroy
# 视频
has_many :videos, dependent: :destroy
# 客户管理
belongs_to :partner
# Groups and active users
scope :active, lambda { where(status: STATUS_ACTIVE) }
@ -261,9 +266,9 @@ class User < ApplicationRecord
course&.course_members.exists?(user_id: id)
end
# 实训路径管理员创建者或admin
# 实训路径管理员
def creator_of_subject?(subject)
subject.user_id == id || admin?
subject.user_id == id
end
# 实训路径合作者、admin
@ -376,6 +381,8 @@ class User < ApplicationRecord
@identity =
if admin?
User::EDU_ADMIN
elsif business?
User::EDU_BUSINESS
elsif creator_of_shixun?(shixun)
User::EDU_SHIXUN_MANAGER
elsif member_of_shixun?(shixun)

@ -0,0 +1,36 @@
class Video < ApplicationRecord
include AASM
belongs_to :user
has_many :video_applies, dependent: :destroy
has_one :processing_video_apply, -> { where(status: :pending) }, class_name: 'VideoApply'
aasm(:status) do
state :pending, initial: true
state :processing
state :refused
state :published
event :apply_publish do
transitions from: :pending, to: :processing
end
event :refuse do
transitions from: :processing, to: :refused
end
event :publish do
transitions from: :processing, to: :published, guard: :vod_uploaded?
end
end
aasm(:vod_status, namespace: :vod) do
state :uploading, initial: true
state :uploaded
event :upload_success do
transitions from: :uploading, to: :uploaded
end
end
end

@ -0,0 +1,19 @@
class VideoApply < ApplicationRecord
include AASM
belongs_to :video
aasm(:status) do
state :pending, initial: true
state :refused
state :agreed
event :refuse do
transitions from: :pending, to: :refused
end
event :agree do
transitions from: :pending, to: :agreed
end
end
end

@ -0,0 +1,3 @@
class ApplicationQuery
include Callable
end

@ -0,0 +1,28 @@
class Users::VideoQuery < ApplicationQuery
include CustomSortable
sort_columns :published_at, :title, default_by: :published_at, default_direction: :desc
attr_reader :user, :params
def initialize(user, params)
@user = user
@params = params
end
def call
videos = user.videos
videos =
case params[:status]
when 'published' then videos.published
when 'processing' then videos.processing
else videos.published
end
keyword = params[:keyword].to_s.strip
videos = videos.where('title LIKE ?', "%#{keyword}%") if keyword.present?
custom_sort(videos, params[:sort_by], params[:sort_direction])
end
end

@ -22,14 +22,7 @@ module ElasticsearchAble
fragment_size: EduSetting.get('es_highlight_fragment_size') || 30,
tag: '<span class="highlight">',
fields: {
name: { type: 'plain' },
challenge_names: { type: 'plain' },
challenge_tag_names: { type: 'plain' },
description: { type: 'plain' },
subject_stages: { type: 'plain' },
content: { type: 'plain' },
descendants_contents: { type: 'plain' },
member_user_names: { type: 'plain' }
'*' => { type: 'plain', number_of_fragments: 3 }
}
}
end
@ -48,4 +41,4 @@ module ElasticsearchAble
def page
params[:page].to_i <= 0 ? 1 : params[:page].to_i
end
end
end

@ -130,7 +130,7 @@ class DuplicateCourseService < ApplicationService
def copy_attachments!
origin_course.attachments.each do |origin_attachment|
attachment = origin_attachment.copy
attachment.tag_list.add(origin_attachment.tag_list) # tag关联
# attachment.tag_list.add(origin_attachment.tag_list) # tag关联
attachment.container = course
attachment.created_on = Time.now
attachment.publish_time = nil

@ -0,0 +1,35 @@
class Videos::AgreeApplyService < ApplicationService
Error = Class.new(StandardError)
attr_reader :video_apply, :video, :user
def initialize(video_apply, user)
@video_apply = video_apply
@video = video_apply.video
@user = user
end
def call
raise Error, '该状态下不能进行此操作' unless video_apply.may_agree? && video.may_publish?
ActiveRecord::Base.transaction do
video_apply.agree!
video.published_at = Time.now
video.publish
video.save!
# 将消息改为已处理
Tiding.where(container_id: video.id, container_type: 'Video', tiding_type: 'Apply', status: 0).update_all(status: 1)
notify_video_author!
end
end
private
def notify_video_author!
Tiding.create!(user_id: video.user_id, trigger_user_id: 1,
container_id: video.id, container_type: 'Video',
tiding_type: 'System', status: 1)
end
end

@ -0,0 +1,35 @@
class Videos::BatchPublishService < ApplicationService
Error = Class.new(StandardError)
attr_reader :user, :params
def initialize(user, params)
@user = user
@params = params
end
def call
video_params = Array.wrap(params[:videos]).compact
return if video_params.blank?
video_ids = []
ActiveRecord::Base.transaction do
video_params.each do |param|
video = user.videos.find_by(uuid: param[:video_id])
next if video.blank? || video.processing_video_apply.present?
raise Error, '视频还未上传完成' if video.vod_uploading?
video.title = param[:title].to_s.strip.presence || video.title
video.apply_publish
video.save!
video.video_applies.create!
video_ids << video.id
end
end
BatchPublishVideoNotifyJob.perform_later(user.id, video_ids) if video_ids.present?
end
end

@ -0,0 +1,41 @@
class Videos::CreateAuthService < ApplicationService
Error = Class.new(StandardError)
attr_reader :user, :params
def initialize(user, params)
@user = user
@params = params.clone
end
def call
validate!
result = upload_video_result
Video.create!(user: user, uuid: result['VideoId'], title: title, cover_url: params[:cover_url])
result
end
private
def title
@_title ||= params.delete(:title).to_s.strip
end
def filename
@_filename ||= params.delete(:file_name).to_s.strip
end
def validate!
raise Error, '视频标题不能为空' if title.blank?
raise Error, '源文件名不能为空' if filename.blank?
end
def upload_video_result
AliyunVod::Service.create_upload_video(title, filename, params)
rescue AliyunVod::Error => _
raise Error, '获取视频上传凭证失败'
end
end

@ -0,0 +1,27 @@
class Videos::DispatchCallbackService < ApplicationService
attr_reader :video, :params
def initialize(params)
@video = Video.find_by(uuid: params[:VideoId])
@params = params
end
def call
return if video.blank?
# TODO:: 拆分事件分发
case params['EventType']
when 'FileUploadComplete' then # 视频上传完成
video.file_url = params['FileUrl']
video.upload_success
video.save!
when 'SnapshotComplete' then # 封面截图完成
return if video.cover_url.present?
video.update!(cover_url: params['CoverUrl'])
end
rescue => ex
Util.logger_error(ex)
end
end

@ -0,0 +1,38 @@
class Videos::RefuseApplyService < ApplicationService
Error = Class.new(StandardError)
attr_reader :video_apply, :video, :user, :params
def initialize(video_apply, user, params)
@video_apply = video_apply
@video = video_apply.video
@user = user
@params = params
end
def call
reason = params[:reason].to_s.strip
raise Error, '原因不能为空' if reason.blank?
raise Error, '该状态下不能进行此操作' unless video_apply.may_refuse?
ActiveRecord::Base.transaction do
video_apply.reason = reason
video_apply.refuse
video_apply.save!
video.refuse!
# 将消息改为已处理
Tiding.where(container_id: video.id, container_type: 'Video', tiding_type: 'Apply', status: 0).update_all(status: 1)
notify_video_author!
end
end
private
def notify_video_author!
Tiding.create!(user_id: video.user_id, trigger_user_id: 1,
container_id: video.id, container_type: 'Video',
tiding_type: 'System', status: 2, extra: video_apply.reason)
end
end

@ -12,5 +12,6 @@ json.courses @courses do |course|
json.is_accessible course.is_public == 1 || @user.course_identity(course) < Course::NORMAL
json.is_end course.is_end
json.first_category_url module_url(course.none_hidden_course_modules.first, course)
json.excellent course.excellent
end
json.courses_count @courses_count

@ -0,0 +1 @@
json.description @course.inform&.description

@ -0,0 +1,11 @@
json.stages @stages do |stage|
json.partial! 'stages/stage', locals: {stage: stage, user:@user, subject:@subject}
end
json.description @subject&.description
json.start_learning @start_learning
json.learned @start_learning ? @subject&.my_subject_progress : 0
json.last_shixun @start_learning ? last_subject_shixun(@user.id, @subject) : ""

@ -1,5 +1,5 @@
json.course_list_id @course.course_list.id
json.course_list_name @course.course_list.name
json.course_list_id @course.course_list&.id
json.course_list_name @course.course_list&.name
json.name @course.name
json.course_id @course.id
json.school @course.school&.name
@ -9,4 +9,6 @@ json.end_date @course.end_date
json.is_public @course.is_public
json.course_module_types @course.course_modules.where(hidden: 0).pluck(:module_type)
json.authentication @course.authentication
json.professional_certification @course.professional_certification
json.professional_certification @course.professional_certification
json.subject_id @course.subject_id
json.excellent @course.excellent

@ -21,6 +21,7 @@ json.switch_to_assistant switch_assistant_role(@is_student, @course, @user)
#json.join_course !@user.member_of_course?(@course)
#json.copy_course !@user.member_of_course?(@course) && @user.is_teacher?
json.course_identity @user_course_identity
json.excellent @course.excellent
if @course.is_end == 0
json.days_remaining (@course.end_date.to_date - Time.now.to_date).to_i
end

@ -17,7 +17,8 @@ if question.question_type <= 2 #当为选择题或判断题时,只显示选
end
json.question_choices do
json.array! exercise_choices.each_with_index.to_a do |a,index|
standard_answer_b = standard_answers_array.include?(a.choice_position)
#TODO: 旧版本来一个题只有一个标准答案的新版又做成了一个题有多个标准答案exercise_choice_id存放的是标准答案的位置..
standard_answer_b = standard_answers_array.join("").include?(a.choice_position.to_s)
user_answer_b = user_answer.include?(a.id)
json.c_position (index+1) if ex_choice_random_boolean #当选项随机时,选项位置以此为准,否则不出现
json.choice_id a.id

@ -1,2 +1,3 @@
json.is_pdf @is_pdf
json.partial! 'attachments/attachment_small', attachment: @file
json.partial! "attachment_histories/list", attachment_histories: @attachment_histories

@ -17,6 +17,8 @@ json.homeworks @homework_commons.each do |homework|
json.status_time curr_status[:time]
json.time_status curr_status[:time_status]
json.allow_late homework.allow_late
# 只有在主目录才显示
json.upper_category_name homework.course_second_category&.name unless params[:category]
unless curr_status[:status].include?("未发布")
json.commit_count studentwork_count homework, 1, @member

@ -69,6 +69,7 @@ elsif @user_course_identity == Course::STUDENT
json.project_info project_info @work, @current_user, @user_course_identity
end
json.work_group @work.work_group_name
json.is_leader @work.user_id == @work.commit_user_id
end
end
@ -140,6 +141,7 @@ elsif @homework.homework_type == "group" || @homework.homework_type == "normal"
if @homework.homework_detail_group.base_on_project
json.project_info project_info work, @current_user, @user_course_identity
end
json.is_leader work.user_id == work.commit_user_id
json.work_group work.work_group_name
end

@ -11,7 +11,7 @@ json.array! @members do |member|
json.partial! 'users/user', locals: { user: member.user }
json.user_shixuns_count member.user.shixuns.published.count
#json.fans_count member.user.fan_count
json.brief_introduction member.user.user_extension.brief_introduction
#json.brief_introduction member.user.user_extension.brief_introduction
json.identity member.user.identity
json.school_name member.user.school_name
json.shixun_manager member.role == 1

@ -1,12 +1,14 @@
json.partial! "homework_commons/homework_public_navigation", locals: {homework: @homework, course: @course, user: @current_user}
json.work_id @work.id
json.description @work.description
json.
json.attachments @work.attachments do |atta|
json.partial! "attachments/attachment_simple", locals: {attachment: atta, delete: @work.delete_atta(atta)}
end
if @homework.homework_type == "group"
json.is_leader_work @work.user_id == @commit_user_id
json.min_num @homework.homework_detail_group.try(:min_num)
json.max_num @homework.homework_detail_group.try(:max_num)
@ -15,5 +17,6 @@ if @homework.homework_type == "group"
json.user_name member.user.real_name
json.group_name member.course_group_name
json.student_id member.user.student_id
json.is_leader member.user_id == @commit_user_id
end
end

@ -3,6 +3,7 @@ json.(@work, :description, :commit_time, :update_time)
json.is_evaluation @is_evaluation
json.author_name @is_evaluation ? "匿名" : @work.user.real_name
json.is_leader_work @work.user_id == @work.commit_user_id if @homework.homework_type == "group"
json.is_author @is_author
json.update_user_name @is_evaluation ? "匿名" : @work.commit_user.try(:real_name)
@ -17,8 +18,10 @@ unless @is_evaluation
json.project_info project_info @work, @current_user, @user_course_identity
end
json.work_members @work_members.each do |member|
json.user_name member.user.real_name
json.user_login member.user.login
json.work_members @work_members.each do |work|
json.user_name work.user.real_name
json.user_login work.user.login
json.work_id work.id
json.is_leader work.user_id == work.commit_user_id
end
end

@ -1,5 +1,6 @@
json.array! subjects do |subject|
json.id subject.id
json.excellent subject.excellent
json.image_url url_to_avatar(subject)
json.name subject.name
json.tag_name subject.repertoire.try(:name)

@ -1,3 +1,4 @@
json.members @subject.subject_members do |member|
json.partial! 'subject_member', locals: { user: member.user }
json.role member.role
end

@ -5,15 +5,16 @@ json.challenges_count @subject.subject_challenge_count
json.subject_score @subject.all_score
json.member_count @subject.member_count
json.allow_delete @is_creator && (@subject.status != 2 || @user.admin?)
json.publish_status publish_status(@subject, @is_creator, @user, @shixuns)
json.allow_statistics @user.manager_of_subject?(@subject)
json.allow_delete (@subject.status != 2 && @is_creator) || @user.admin?
json.publish_status publish_status(@subject, @is_manager, @user, @shixuns)
json.allow_statistics @is_manager
json.allow_send @user.logged?
json.allow_visit @user.manager_of_subject?(@subject) || @user.admin? || @subject.status > 1
json.allow_add_member @user.manager_of_subject?(@subject)
json.allow_visit @subject.status > 1 || @is_manager
json.allow_add_member @is_manager
json.members @members do |member|
json.partial! 'subject_member', locals: { user: member.user }
json.role member.role
end
# 技能标签

@ -1,3 +1,4 @@
json.members @subject.subject_members do |member|
json.partial! 'subject_member', locals: { user: member.user }
json.role member.role
end

@ -1,4 +1,4 @@
json.id user.id
json.name user.full_name
json.name user.real_name
json.login user.login
json.image_url url_to_avatar(user)

@ -16,6 +16,9 @@ json.top do
json.moop_cases_url "#{@old_domain}/moop_cases"
json.crowdsourcing_url "/crowdsourcing"
# 客户管理
json.customer_management_url current_user.partner ? "#{@old_domain}/cooperates/#{current_user.partner.try(:id)}/partner_list" : nil
json.career_url do
json.array! @career.to_a do |c|
if c[1].present?

@ -14,6 +14,7 @@ if @course
json.course_identity @course_identity
json.course_name @course.name
json.course_public @course.is_public
json.course_excellent @course.excellent
if params[:group_info]
json.group_info @course.teacher_group(@user.id) if @course_identity < Course::STUDENT
end

@ -7,7 +7,7 @@ json.grade @user.grade
json.follow_count @user.follow_count
json.fan_count @user.fan_count
json.identity @user.identity
json.brief_introduction @user.user_extension&.brief_introduction
#json.brief_introduction @user.user_extension&.brief_introduction
json.authentication @user.authentication
json.professional_certification @user.professional_certification
json.phone_binded @user.phone_binded?

@ -0,0 +1,5 @@
json.extract! video, :id, :title, :cover_url, :file_url
json.published_at video.display_published_at
json.created_at video.display_created_at
json.updated_at video.display_updated_at

@ -0,0 +1,2 @@
json.count @count
json.videos @videos, partial: 'video', as: :video

@ -0,0 +1,7 @@
json.count @count
json.videos do
json.array! @videos.each do |video|
json.partial! 'video', video: video
json.file_url nil
end
end

@ -0,0 +1 @@
json.partial! 'video', video: current_video

@ -0,0 +1,16 @@
defaults: &defaults
access_key_id: 'test'
access_key_secret: 'test'
base_url: 'http://vod.cn-shanghai.aliyuncs.com'
cate_id: '-1'
callback_url: 'http://47.96.87.25:48080/api/callbacks/aliyun_vod.json'
signature_key: 'test12345678'
development:
<<: *defaults
test:
<<: *defaults
production:
<<: *defaults

@ -0,0 +1,16 @@
defaults: &defaults
access_key_id: 'test'
access_key_secret: 'test'
base_url: 'http://vod.cn-shanghai.aliyuncs.com'
cate_id: '-1'
callback_url: 'http://47.96.87.25:48080/api/callbacks/aliyun_vod.json'
signature_key: 'test12345678'
development:
<<: *defaults
test:
<<: *defaults
production:
<<: *defaults

@ -0,0 +1,7 @@
config = Rails.application.config_for(:aliyun_vod)
AliyunVod.access_key_id = config['access_key_id']
AliyunVod.access_key_secret = config['access_key_secret']
AliyunVod.base_url = config['base_url'] || 'http://vod.cn-shanghai.aliyuncs.com'.freeze
AliyunVod.cate_id = config['cate_id']
AliyunVod.callback_url = config['callback_url']
AliyunVod.signature_key = config['signature_key']

@ -4,5 +4,5 @@
# Rails.application.config.session_store :active_record_store
# Be sure to restart your server when you modify this file.
Rails.application.config.session_store :cache_store, :expire_after => 10.hours, key: '_educoder_session', domain: :all
Rails.application.config.session_store :cache_store, :expire_after => 24.hours, key: '_educoder_session', domain: :all

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

Loading…
Cancel
Save