diff --git a/app/controllers/challenges_controller.rb b/app/controllers/challenges_controller.rb index c69f5bbfb..47c8fc68c 100644 --- a/app/controllers/challenges_controller.rb +++ b/app/controllers/challenges_controller.rb @@ -159,11 +159,12 @@ class ChallengesController < ApplicationController challenges.shixun_id, games.identifier, games.status" join_sql = "LEFT JOIN games ON games.challenge_id = challenges.id AND games.user_id = #{current_user.id}" # 下面2个参数是为了解决列表获取通关人数与正在游玩人数的问题 - @pass_games_map = @shixun.challenges.joins(:games).where(games: {status:2}).group(:challenge_id).reorder(nil).count - @play_games_map = @shixun.challenges.joins(:games).where(games: {status:[0,1]}).group(:challenge_id).reorder(nil).count + #@pass_games_map = @shixun.challenges.joins(:games).where(games: {status:2}).group(:challenge_id).reorder(nil).count + #@play_games_map = @shixun.challenges.joins(:games).where(games: {status:[0,1]}).group(:challenge_id).reorder(nil).count @challenges = @shixun.challenges.joins(join_sql).select(base_columns) + #@challenges = @shixun.challenges.fields_for_list @editable = @shixun.status == 0 # before_action:有判断权限,如果没发布,则肯定是管理人员 @user = current_user @shixun.increment!(:visits) diff --git a/app/controllers/courses_controller.rb b/app/controllers/courses_controller.rb index 2e94e435d..c9fcb38ac 100644 --- a/app/controllers/courses_controller.rb +++ b/app/controllers/courses_controller.rb @@ -112,6 +112,8 @@ class CoursesController < ApplicationController videos = custom_sort(videos, params[:sort_by], params[:sort_direction]) @count = videos.count + sql = "left join videos on videos.id=course_videos.video_id AND (videos.transcoded=1 OR videos.user_id = #{current_user.id})" + videos = videos.joins(sql).reload @videos = paginate videos.includes(video: [user: :user_extension], user: :user_extension) end @@ -133,13 +135,17 @@ class CoursesController < ApplicationController # 视频移动到目录 def move_to_category + tip_exception("请选择要移动的目录") if params[:new_category_id].blank? category = @course.course_second_categories.find_by(id: params[:new_category_id]) if params[:new_category_id].to_i == 0 || category.present? - videos = @course.course_videos.where(video_id: params[:video_ids]).or(@course.course_videos.where(id: params[:video_ids])) + video = @course.course_videos.where(video_id: params[:video_ids]).or(@course.course_videos.where(id: params[:video_ids])).first + + user_id = video.user_id || video.video.user_id + tip_exception("您不是课堂管理员或者视频发布者,暂不能移动视频。") unless @user_course_identity < Course::PROFESSOR || user_id == current_user.id - videos.update_all(course_second_category_id: params[:new_category_id]) + video.update!(course_second_category_id: params[:new_category_id]) normal_status(0, "操作成功") else normal_status(-1, "目录不存在") diff --git a/app/helpers/application_helper.rb b/app/helpers/application_helper.rb index e352d6cec..d1d2499f0 100644 --- a/app/helpers/application_helper.rb +++ b/app/helpers/application_helper.rb @@ -70,6 +70,8 @@ module ApplicationHelper # shixun开启挑战对应的行为名及url def task_operation_url current_myshixun, shixun + return ["开启挑战", "/shixuns/#{shixun.identifier}/shixun_exec"] unless current_user.logged? + if current_myshixun.blank? name = shixun.status == 0 ? "模拟实战" : "开启挑战" url = "/shixuns/#{shixun.identifier}/shixun_exec" diff --git a/app/libs/aliyun_vod/service/video_manage.rb b/app/libs/aliyun_vod/service/video_manage.rb index 02a4bac49..b00d86892 100644 --- a/app/libs/aliyun_vod/service/video_manage.rb +++ b/app/libs/aliyun_vod/service/video_manage.rb @@ -26,6 +26,20 @@ module AliyunVod::Service::VideoManage result end + # 读取视频编码格式 + def get_meta_code_info(video_id) + params = { + Action: 'GetMezzanineInfo', + VideoId: video_id, + AdditionType: 'video' + }.merge(base_params) + + result = request(:post, params) + result['Mezzanine']['VideoStreamList'][0]['CodecName'] + rescue => e + Rails.logger.info "读取视频编码信息失败: #{video_id}, #{e.message}" + end + # 删除视频信息 def delete_video(video_ids) params = { diff --git a/app/libs/aliyun_vod/service/video_process.rb b/app/libs/aliyun_vod/service/video_process.rb index eec029c20..61acc4051 100644 --- a/app/libs/aliyun_vod/service/video_process.rb +++ b/app/libs/aliyun_vod/service/video_process.rb @@ -14,4 +14,20 @@ module AliyunVod::Service::VideoProcess result end + + # 提交视频转码任务 + def submit_transcode_job(video_id, group_id, **opts) + params = { + Action: 'SubmitTranscodeJobs', + VideoId: video_id, + TemplateGroupId: group_id + }.merge(base_params) + params = opts.merge(params) + + result = request(:post, params) + + raise AliyunVod::Error, '提交视频转码作业失败' if result['TranscodeJobs'].blank? + + result + end end \ No newline at end of file diff --git a/app/models/challenge.rb b/app/models/challenge.rb index 5646da363..31a683760 100644 --- a/app/models/challenge.rb +++ b/app/models/challenge.rb @@ -79,20 +79,34 @@ class Challenge < ApplicationRecord end end - # # 开启挑战 - # def open_game(user_id, shixun) - # - # - # game = self.games.select([:status, :identifier]).where(user_id: user_id).first - # game = self.games.select{|game| game.user_id == user_id} + # def open_game shixun, user_id + # game = self.games.map{|g| g.user_id == user_id}.first # if game.present? # shixun.task_pass || game.status != 3 ? "/tasks/#{game.identifier}" : "" # else - # "/api/shixuns/#{shixun.identifier}/shixun_exec" + # self.position == 1 ? "/api/shixuns/#{shixun.identifier}/shixun_exec" : "" # end # end ## 用户关卡状态 0: 不能开启实训; 1:直接开启; 2表示已完成 + # def user_tpi_status shixun, user_id + # # todo: 以前没加索引导致相同关卡,同一用户有多个games + # # 允许跳关则直接开启 + # game = games.where(user_id: user_id).take + # if game.blank? + # position == 1 ? 1 : 0 + # else + # if game.status == 3 + # shixun.task_pass ? 1 : 0 + # elsif game.status == 2 + # 2 + # else + # 1 + # end + # end + # end + + # ## 用户关卡状态 0: 不能开启实训; 1:直接开启; 2表示已完成 def user_tpi_status shixun # todo: 以前没加索引导致相同关卡,同一用户有多个games # 允许跳关则直接开启 @@ -128,12 +142,14 @@ class Challenge < ApplicationRecord # 关卡用户通关数 def user_passed_count - games.where(status: 2).count + #games.map{|g| g.status == 2}.count + self.games.where(status: 1).count end # 关卡用户正在挑战的人数 def playing_count - games.where(status: [0, 1]).count + #games.map{|g| g.status == 0 || g.status == 1}.count + self.games.where(status: [0,1]).count end def last_challenge diff --git a/app/models/course_video.rb b/app/models/course_video.rb index eb2a17595..2cfa151ce 100644 --- a/app/models/course_video.rb +++ b/app/models/course_video.rb @@ -3,6 +3,6 @@ class CourseVideo < ApplicationRecord belongs_to :video, optional: true belongs_to :user, optional: true - validates :title, length: { maximum: 60, too_long: "不能超过60个字符" } - validates :link, format: { with: CustomRegexp::URL, message: "必须为网址超链接" } + validates :title, length: { maximum: 60, too_long: "不能超过60个字符" }, allow_blank: true + validates :link, format: { with: CustomRegexp::URL, message: "必须为网址超链接" }, allow_blank: true end diff --git a/app/models/shixun.rb b/app/models/shixun.rb index c3c5cec78..b9bd372b0 100644 --- a/app/models/shixun.rb +++ b/app/models/shixun.rb @@ -210,8 +210,9 @@ class Shixun < ApplicationRecord end # 当前用户开启的实训 - def current_myshixun(user_id) - myshixuns.find_by(user_id: user_id) + def current_myshixun(user) + return nil unless user.logged? + myshixuns.find_by(user_id: user.id) end # 实训技术平台 @@ -264,7 +265,7 @@ class Shixun < ApplicationRecord # 实训关卡的总分(由于大部分是实践题,因此没关联查choose表) # 提前加载问题:由于选择题比较少,所以几乎不会触发选择题的查询,所以没必要提前载入choose_score def all_score - self.challenges.pluck(:score).sum + self.challenges.sum(:score) end ### fork 数量 diff --git a/app/models/user.rb b/app/models/user.rb index 7e60983d1..b0bd191d2 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -339,7 +339,7 @@ class User < ApplicationRecord # 实训管理员:实训合作者、admin def manager_of_shixun?(shixun) logger.info("############id: #{id}") - shixun.shixun_members.exists?(role: [1,2], user_id: id) || admin? || business? + shixun.shixun_members.exists?(user_id: id, role: [1,2]) || admin? || business? end # 实训管理员 diff --git a/app/models/video.rb b/app/models/video.rb index 6f5f79ca6..02cccd8f1 100644 --- a/app/models/video.rb +++ b/app/models/video.rb @@ -1,6 +1,9 @@ class Video < ApplicationRecord include AASM + # 标准视频转码组 + NORMAL_TRANSCODE_GROUP_ID = 'a0277c5c0c7458458e171b0cee6ebf5e' + belongs_to :user has_many :video_applies, dependent: :destroy diff --git a/app/services/videos/batch_publish_service.rb b/app/services/videos/batch_publish_service.rb index 9ddcae146..699a55516 100644 --- a/app/services/videos/batch_publish_service.rb +++ b/app/services/videos/batch_publish_service.rb @@ -28,6 +28,16 @@ class Videos::BatchPublishService < ApplicationService if param[:course_id].present? video.status = "published" end + + # 标清转码为h264 + if AliyunVod::Service.get_meta_code_info(video.uuid).start_with?('h264', 'h265') + video.transcoded = true + result = AliyunVod::Service.get_play_info(video.uuid) + video.play_url = result['PlayInfoList']['PlayInfo'][0]['PlayURL'] + else + AliyunVod::Service.submit_transcode_job(video.uuid, Video::NORMAL_TRANSCODE_GROUP_ID) + end + video.save! if param[:course_id].present? @@ -41,7 +51,7 @@ class Videos::BatchPublishService < ApplicationService # 如果是课堂上传则创建课堂记录 Rails.logger.info("#####param: #{ param[:course_id]}") if param[:course_id].present? - course_second_category_id = params[:category_id] || 0 + course_second_category_id = param[:category_id] || 0 video.course_videos.create!(course_id: param[:course_id], course_second_category_id: course_second_category_id) end end diff --git a/app/services/videos/dispatch_callback_service.rb b/app/services/videos/dispatch_callback_service.rb index 5adebea73..149009c96 100644 --- a/app/services/videos/dispatch_callback_service.rb +++ b/app/services/videos/dispatch_callback_service.rb @@ -20,9 +20,9 @@ class Videos::DispatchCallbackService < ApplicationService return if video.cover_url.present? video.update!(cover_url: params['CoverUrl']) - when 'TranscodeComplete' then # 转码完成 + when 'StreamTranscodeComplete' then # 转码完成 return if video.play_url.present? - video.update!(play_url: params['FileUrl']) + video.update!(play_url: params['FileUrl'], transcoded: true) end rescue => ex diff --git a/app/views/challenges/index.json.jbuilder b/app/views/challenges/index.json.jbuilder index d4f2a7f59..eae72dad2 100644 --- a/app/views/challenges/index.json.jbuilder +++ b/app/views/challenges/index.json.jbuilder @@ -16,12 +16,14 @@ if @challenges.present? json.st challenge.st json.name challenge.subject json.score challenge.score - json.passed_count @pass_games_map.fetch(challenge.id, 0) - #json.passed_count challenge.user_passed_count - json.playing_count @play_games_map.fetch(challenge.id, 0) - #json.playing_count challenge.playing_count + #json.passed_count @pass_games_map.fetch(challenge.id, 0) + user_passed_count = challenge.user_passed_count + json.passed_count user_passed_count + #json.playing_count @play_games_map.fetch(challenge.id, 0) + json.playing_count (challenge.games.count - user_passed_count) json.name_url shixun_challenge_path(challenge, shixun_identifier: @shixun.identifier) json.open_game challenge.open_game(@shixun) + #json.open_game challenge.open_game(@shixun, @user.id) if @editable json.edit_url edit_shixun_challenge_path(challenge, shixun_identifier: @shixun.identifier) json.delete_url shixun_challenge_path(challenge, shixun_identifier: @shixun.identifier) @@ -29,6 +31,7 @@ if @challenges.present? json.down_url index_down_shixun_challenge_path(challenge, :shixun_identifier => @shixun.identifier) if @shixun.challenges_count != challenge.position end #json.passed challenge.has_passed?(@user.id) + #json.status challenge.user_tpi_status(@shixun, @user.id) json.status challenge.user_tpi_status(@shixun) end end diff --git a/app/views/shixuns/_top.json.jbuilder b/app/views/shixuns/_top.json.jbuilder index 362cfd15b..c90de5e5b 100644 --- a/app/views/shixuns/_top.json.jbuilder +++ b/app/views/shixuns/_top.json.jbuilder @@ -14,7 +14,7 @@ json.name shixun.name json.stu_num shixun.myshixuns_count json.experience shixun.all_score json.diffcult level_to_s(shixun.trainee) -json.score_info shixun.shixun_preference_info # todo: 这块可以改成只显示实训的平均分,不用每次都去取每种星的分数了。 +json.score_info shixun.averge_star # todo: 这块可以改成只显示实训的平均分,不用每次都去取每种星的分数了。 json.is_jupyter shixun.is_jupyter # 用于是否显示导航栏中的'背景知识' json.propaedeutics shixun.propaedeutics.present? diff --git a/app/views/users/videos/_video.json.jbuilder b/app/views/users/videos/_video.json.jbuilder index dcf85fb75..5b0bc7a6e 100644 --- a/app/views/users/videos/_video.json.jbuilder +++ b/app/views/users/videos/_video.json.jbuilder @@ -1,5 +1,4 @@ -json.extract! video, :id, :title, :cover_url, :file_url, :play_url, :vv, :user_id - +json.extract! video, :id, :title, :cover_url, :file_url, :play_url, :vv, :user_id, :transcoded json.play_duration video.video_play_duration json.published_at video.display_published_at json.created_at video.display_created_at diff --git a/db/migrate/20200309114326_add_transcoded_to_video.rb b/db/migrate/20200309114326_add_transcoded_to_video.rb new file mode 100644 index 000000000..31e2764db --- /dev/null +++ b/db/migrate/20200309114326_add_transcoded_to_video.rb @@ -0,0 +1,5 @@ +class AddTranscodedToVideo < ActiveRecord::Migration[5.2] + def change + add_column :videos, :transcoded, :boolean, default: false + end +end diff --git a/lib/tasks/video_transcode.rake b/lib/tasks/video_transcode.rake new file mode 100644 index 000000000..7e461224e --- /dev/null +++ b/lib/tasks/video_transcode.rake @@ -0,0 +1,12 @@ +#coding=utf-8 +namespace :video_transcode do + desc "视频转码成h264" + task :submit => :environment do + Video.find_each do |v| + if v.uuid && !v.transcoded && !v.file_url.include?('.mp4') && !AliyunVod::Service.get_meta_code_info(v.uuid).start_with?("h264", "h265") + p "--- Start submit video trans code #{v.uuid}" + AliyunVod::Service.submit_transcode_job(v.uuid, 'a0277c5c0c7458458e171b0cee6ebf5e') + end + end + end +end \ No newline at end of file diff --git a/public/react/src/modules/developer/newOrEditTask/index.js b/public/react/src/modules/developer/newOrEditTask/index.js index 43cad6106..f80cece44 100644 --- a/public/react/src/modules/developer/newOrEditTask/index.js +++ b/public/react/src/modules/developer/newOrEditTask/index.js @@ -112,7 +112,7 @@ const NewOrEditTask = (props) => { content: (

发布后即可应用到自己管理的课堂
是否确认发布?

), onOk() { changePublishLoadingStatus(true); - handlePublish(props, 'publish'); + props.handlePublish(props, 'publish'); } }); } diff --git a/public/react/src/redux/actions/ojForm.js b/public/react/src/redux/actions/ojForm.js index 62de0fed8..783c6a793 100644 --- a/public/react/src/redux/actions/ojForm.js +++ b/public/react/src/redux/actions/ojForm.js @@ -222,38 +222,63 @@ export const validateOjForm = (props, type, cb) => { } - try { + let boolflad=false; + try { if(ojForm.sub_discipline_id.length===0){ hasSuccess = false; notification['error']({ message: '提示', description: '课程必须选择!' }); - + boolflad=true; }else if(ojForm.timeLimit===null){ hasSuccess = false; notification['error']({ message: '提示', description: '时间限制必须输入!' }); - } else if(ojForm.name.length===0){ + boolflad=true; + + } else if(ojForm.name.length===0){ hasSuccess = false; notification['error']({ message: '提示', description: '任务名称必须输入!' }); - + boolflad=true; }else if(ojForm.description.length===0){ hasSuccess = false; notification['error']({ message: '提示', description: '描述必须输入!' }); - } + boolflad=true; + } }catch (e) { } + try { + if( hasSuccess === false){ + if(boolflad===true){ + props.changeSubmitLoadingStatus(false); + } + } + }catch (e) { + + } + try { + if( hasSuccess === false){ + if(boolflad===true){ + props.changePublishLoadingStatus(false); + } + } + }catch (e) { + + } + + +