|
|
|
|
class ShixunsController < ApplicationController
|
|
|
|
|
include ShixunsHelper
|
|
|
|
|
include ApplicationHelper
|
|
|
|
|
|
|
|
|
|
before_action :require_login, :check_auth, except: [:download_file, :index, :menus]
|
|
|
|
|
before_action :check_auth, except: [:download_file, :index, :menus]
|
|
|
|
|
|
|
|
|
|
before_action :find_shixun, :shixun_access_allowed, except: [:index, :new, :create, :menus, :get_recommend_shixuns,
|
|
|
|
|
:propaedeutics, :departments, :apply_shixun_mirror,
|
|
|
|
|
:get_mirror_script, :download_file]
|
|
|
|
|
before_action :find_repo_name, only: [:repository, :commits, :file_content, :update_file, :shixun_exec, :copy]
|
|
|
|
|
|
|
|
|
|
before_action :allowed, only: [:update, :close, :update_propaedeutics, :settings, :publish,
|
|
|
|
|
:shixun_members_added, :change_manager, :collaborators_delete,
|
|
|
|
|
:cancel_publish, :add_collaborators]
|
|
|
|
|
before_action :portion_allowed, only: [:copy]
|
|
|
|
|
|
|
|
|
|
before_action :special_allowed, only: [:send_to_course, :search_user_courses]
|
|
|
|
|
|
|
|
|
|
## 获取课程列表
|
|
|
|
|
def index
|
|
|
|
|
## 我的实训
|
|
|
|
|
@shixuns =
|
|
|
|
|
if params[:order_by] == 'mine'
|
|
|
|
|
tip_exception(401, "..") unless current_user.logged?
|
|
|
|
|
current_user.my_shixuns
|
|
|
|
|
else
|
|
|
|
|
Shixun.unhidden
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
## 方向
|
|
|
|
|
if params[:tag_level].present? && params[:tag_id].present?
|
|
|
|
|
@shixuns = @shixuns.filter_tag(params[:tag_level].to_i, params[:tag_id].to_i)
|
|
|
|
|
case params[:tag_level].to_i
|
|
|
|
|
when 1 #大类
|
|
|
|
|
@search_tags = Repertoire.find(params[:tag_id].to_i).name
|
|
|
|
|
when 2 #子类
|
|
|
|
|
@search_tags = SubRepertoire.find(params[:tag_id].to_i).name
|
|
|
|
|
when 3 #tag
|
|
|
|
|
tag = TagRepertoire.find(params[:tag_id].to_i)
|
|
|
|
|
@search_tags = "#{tag.sub_repertoire.name} / #{tag.name}"
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
## 搜索关键字 匹配关卡名, 用户名, 实训名 和 空格多搜索
|
|
|
|
|
if params[:keyword].present?
|
|
|
|
|
keyword = params[:keyword].strip
|
|
|
|
|
@shixuns = @shixuns.joins(:user, challenges: :challenge_tags).
|
|
|
|
|
where("challenge_tags.name like :keyword
|
|
|
|
|
or challenges.subject like :keyword
|
|
|
|
|
or concat(lastname, firstname) like :keyword
|
|
|
|
|
or shixuns.name like :name",
|
|
|
|
|
keyword: "%#{keyword}%", name: "%#{keyword.split(" ").join("%")}%").distinct
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
## 筛选 状态
|
|
|
|
|
if params[:status].present? && params[:status].to_i != 0
|
|
|
|
|
params[:status] = [0, 1] if params[:status].to_i == 1
|
|
|
|
|
@shixuns = @shixuns.where(status: params[:status])
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
## 筛选 难度
|
|
|
|
|
if params[:diff].present? && params[:diff].to_i != 0
|
|
|
|
|
@shixuns = @shixuns.where(trainee: params[:diff])
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
## 排序参数
|
|
|
|
|
bsort = params[:sort] || 'desc'
|
|
|
|
|
case params[:order_by] || 'publish_time'
|
|
|
|
|
when 'new'
|
|
|
|
|
@shixuns = @shixuns.order("shixuns.status = 2 desc, shixuns.created_at #{bsort}")
|
|
|
|
|
when 'hot'
|
|
|
|
|
@shixuns = @shixuns.order("shixuns.status = 2 desc, shixuns.myshixuns_count #{bsort}")
|
|
|
|
|
when 'mine'
|
|
|
|
|
@shixuns = @shixuns.order("shixuns.created_at #{bsort}")
|
|
|
|
|
else
|
|
|
|
|
@shixuns = @shixuns.order("shixuns.status = 2 desc, shixuns.publish_time #{bsort}")
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
# 用id计数会快10+MS左右,对于搜索的内容随着数据的增加,性能会提升一些。
|
|
|
|
|
@total_count = @shixuns.count("shixuns.id")
|
|
|
|
|
|
|
|
|
|
## 分页参数
|
|
|
|
|
page = params[:page] || 1
|
|
|
|
|
limit = params[:limit] || 16
|
|
|
|
|
|
|
|
|
|
@shixuns = @shixuns.includes(:tag_repertoires, :challenges).page(page).per(limit)
|
|
|
|
|
|
|
|
|
|
@tag_name_map = TagRepertoire.joins(:shixun_tag_repertoires)
|
|
|
|
|
.where(shixun_tag_repertoires: { shixun_id: @shixuns.map(&:id) })
|
|
|
|
|
.group('shixun_tag_repertoires.shixun_id')
|
|
|
|
|
.select('shixun_id, tag_repertoires.name')
|
|
|
|
|
.each_with_object({}) { |r, obj| obj[r.shixun_id] = r.name }
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
## 获取顶部菜单
|
|
|
|
|
def menus
|
|
|
|
|
@repertoires = Repertoire.includes(sub_repertoires: [:tag_repertoires]).order("updated_at asc")
|
|
|
|
|
# respond_with @repertoires
|
|
|
|
|
|
|
|
|
|
render_json
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
## 实训详情
|
|
|
|
|
def show
|
|
|
|
|
# 当前用户开启的实训
|
|
|
|
|
can_fork = current_user.is_certification_teacher || current_user.admin?
|
|
|
|
|
unless can_fork
|
|
|
|
|
@can_fork = {can_fork: "已经职业认证的教师才能fork实训",
|
|
|
|
|
certi_url: edu_setting('old_edu_host') + "/account/professional_certification"}
|
|
|
|
|
end
|
|
|
|
|
@current_myshixun = @shixun.current_myshixun(current_user)
|
|
|
|
|
if @shixun.fork_from
|
|
|
|
|
fork_shixun = Shixun.select(:id, :user_id, :name, :identifier).where(id: @shixun.fork_from).first
|
|
|
|
|
@fork_from = {name: fork_shixun.name, username: fork_shixun.owner.try(:full_name),
|
|
|
|
|
fork_identifier: fork_shixun.identifier} if fork_shixun
|
|
|
|
|
end
|
|
|
|
|
@power = current_user.manager_of_shixun?(@shixun)
|
|
|
|
|
# 更新是为了首页的排序,myshixun的动静需要在实训中展现出来
|
|
|
|
|
if @current_myshixun && params[:exit] && !current_user.admin?
|
|
|
|
|
@current_myshixun.update_column(:updated_at, Time.now)
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
def show_right
|
|
|
|
|
owner = @shixun.owner
|
|
|
|
|
#@fans_count = owner.fan_count
|
|
|
|
|
#@followed_count = owner.follow_count
|
|
|
|
|
@user_own_shixuns = owner.shixuns.published.count
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
# 排行榜
|
|
|
|
|
def ranking_list
|
|
|
|
|
if @shixun.status == 2
|
|
|
|
|
sql = "
|
|
|
|
|
select m.user_id, u.login, u.lastname, m.updated_at,
|
|
|
|
|
(select sum(cost_time) from games g where g.myshixun_id = m.id) as time,
|
|
|
|
|
(select sum(final_score) from games g where g.myshixun_id = m.id) as score
|
|
|
|
|
from (users u left join myshixuns m on m.user_id = u.id) where m.shixun_id = #{@shixun.id} and m.status = 1
|
|
|
|
|
order by score desc, time asc limit 10
|
|
|
|
|
"
|
|
|
|
|
@myshixuns = Myshixun.find_by_sql(sql)
|
|
|
|
|
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
#评论
|
|
|
|
|
def discusses
|
|
|
|
|
new_params = params.merge(container_id: @shixun.id, container_type: 'Shixun')
|
|
|
|
|
discusses = ShixunsService.new.shixun_discuss new_params, current_user
|
|
|
|
|
|
|
|
|
|
if discusses.present?
|
|
|
|
|
@children_list = discusses[:children_list]
|
|
|
|
|
else
|
|
|
|
|
@children_list = []
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
def copy
|
|
|
|
|
ActiveRecord::Base.transaction do
|
|
|
|
|
begin
|
|
|
|
|
@new_shixun = Shixun.new
|
|
|
|
|
@new_shixun.attributes = @shixun.attributes.dup.except("id","user_id","visits","gpid","status", "identifier", "averge_star",
|
|
|
|
|
"homepage_show","repo_name", "myshixuns_count", "challenges_count",
|
|
|
|
|
"can_copy")
|
|
|
|
|
@new_shixun.user_id = User.current.id
|
|
|
|
|
@new_shixun.averge_star = 5
|
|
|
|
|
@new_shixun.identifier = generate_identifier Shixun, 8
|
|
|
|
|
@new_shixun.fork_from = @shixun.id
|
|
|
|
|
@new_shixun.save!
|
|
|
|
|
|
|
|
|
|
# 同步shixun_info的信息
|
|
|
|
|
if @shixun.shixun_info.present?
|
|
|
|
|
ShixunInfo.create!(shixun_id: @new_shixun.id,
|
|
|
|
|
description: @shixun.description,
|
|
|
|
|
evaluate_script: @shixun.evaluate_script)
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
# 同步镜像
|
|
|
|
|
if @shixun.mirror_repositories.present?
|
|
|
|
|
@shixun.mirror_repositories.each do |mirror|
|
|
|
|
|
ShixunMirrorRepository.create!(:shixun_id => @new_shixun.id, :mirror_repository_id => mirror.id)
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
# 同步技术标签
|
|
|
|
|
@shixun.shixun_tag_repertoires.each do |str|
|
|
|
|
|
ShixunTagRepertoire.create!(:tag_repertoire_id => str.tag_repertoire_id, :shixun_id => @new_shixun.id)
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
# 同步配置
|
|
|
|
|
logger.info("########-shixun_service_configs_count: #{@shixun.shixun_service_configs.pluck(:id, :shixun_id)}")
|
|
|
|
|
@shixun.shixun_service_configs.each do |config|
|
|
|
|
|
ShixunServiceConfig.create!(:shixun_id => @new_shixun.id,
|
|
|
|
|
:cpu_limit => config.cpu_limit,
|
|
|
|
|
:lower_cpu_limit => config.lower_cpu_limit,
|
|
|
|
|
:memory_limit => config.memory_limit,
|
|
|
|
|
:request_limit => config.request_limit,
|
|
|
|
|
:mirror_repository_id => config.mirror_repository_id)
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
# fork版本库
|
|
|
|
|
logger.info("###########fork_repo_path: ######{@repo_path}")
|
|
|
|
|
project_fork(@new_shixun, @repo_path, current_user.login)
|
|
|
|
|
|
|
|
|
|
ShixunMember.create!(:user_id => User.current.id, :shixun_id => @new_shixun.try(:id), :role => 1)
|
|
|
|
|
|
|
|
|
|
# 同步复制关卡
|
|
|
|
|
if @shixun.challenges.present?
|
|
|
|
|
@shixun.challenges.each do |challenge|
|
|
|
|
|
new_challenge = Challenge.new
|
|
|
|
|
new_challenge.attributes = challenge.attributes.dup.except("id","shixun_id","user_id", "challenge_tags_count")
|
|
|
|
|
new_challenge.user_id = User.current.id
|
|
|
|
|
new_challenge.shixun_id = @new_shixun.id
|
|
|
|
|
new_challenge.save!
|
|
|
|
|
# 同步参考答案
|
|
|
|
|
challenge.challenge_answers.each do |answer|
|
|
|
|
|
new_answer = ChallengeAnswer.new
|
|
|
|
|
new_answer.attributes = answer.attributes.dup.except("id","challenge_id")
|
|
|
|
|
new_answer.challenge_id = new_challenge.id
|
|
|
|
|
new_answer.save!
|
|
|
|
|
end
|
|
|
|
|
if challenge.st == 0 # 评测题
|
|
|
|
|
# 同步测试集
|
|
|
|
|
if challenge.test_sets.present?
|
|
|
|
|
challenge.test_sets.each do |test_set|
|
|
|
|
|
new_test_set = TestSet.new
|
|
|
|
|
new_test_set.attributes = test_set.attributes.dup.except("id","challenge_id")
|
|
|
|
|
new_test_set.challenge_id = new_challenge.id
|
|
|
|
|
new_test_set.save!
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
# 同步关卡标签
|
|
|
|
|
challenge_tags = ChallengeTag.where("challenge_id =? and challenge_choose_id is null", challenge.id)
|
|
|
|
|
if challenge_tags.present?
|
|
|
|
|
challenge_tags.each do |challenge_tag|
|
|
|
|
|
ChallengeTag.create!(:challenge_id => new_challenge.id, :name => challenge_tag.try(:name))
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
elsif challenge.st == 1 # 选择题
|
|
|
|
|
if challenge.challenge_chooses.present?
|
|
|
|
|
challenge.challenge_chooses.each do |challenge_choose|
|
|
|
|
|
new_challenge_choose = ChallengeChoose.new
|
|
|
|
|
new_challenge_choose.attributes = challenge_choose.attributes.dup.except("id","challenge_id")
|
|
|
|
|
new_challenge_choose.challenge_id = new_challenge.id
|
|
|
|
|
new_challenge_choose.save!
|
|
|
|
|
# 每一题的选项
|
|
|
|
|
if challenge_choose.challenge_questions.present?
|
|
|
|
|
challenge_choose.challenge_questions.each do |challenge_question|
|
|
|
|
|
new_challenge_question = ChallengeQuestion.new
|
|
|
|
|
new_challenge_question.attributes = challenge_question.attributes.dup.except("id","challenge_choose_id")
|
|
|
|
|
new_challenge_question.challenge_choose_id = new_challenge_choose.id
|
|
|
|
|
new_challenge_question.save!
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
# 每一题的知识标签
|
|
|
|
|
st_challenge_tags = ChallengeTag.where(:challenge_id => challenge.id, :challenge_choose_id => challenge_choose.id)
|
|
|
|
|
if st_challenge_tags.present?
|
|
|
|
|
st_challenge_tags.each do |st_challenge_tag|
|
|
|
|
|
ChallengeTag.create!(:challenge_id => new_challenge.id, :name => st_challenge_tag.try(:name), :challenge_choose_id => new_challenge_choose.id)
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
rescue Exception => e
|
|
|
|
|
uid_logger_error("copy shixun failed ##{e.message}")
|
|
|
|
|
g.delete_project(gshixungshixun.id) if gshixun.try(:id).present? # 异常后,如果已经创建了版本库需要删除该版本库
|
|
|
|
|
tip_exception("实训Fork失败")
|
|
|
|
|
raise ActiveRecord::Rollback
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
#合作者
|
|
|
|
|
def collaborators
|
|
|
|
|
@user = current_user
|
|
|
|
|
@members = @shixun.shixun_members.includes(:user)
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
def fork_list
|
|
|
|
|
@shixuns = Shixun.where(:fork_from => @shixun.id)
|
|
|
|
|
@shixuns_count = @shixuns.count
|
|
|
|
|
|
|
|
|
|
## 分页参数
|
|
|
|
|
page = params[:page] || 1
|
|
|
|
|
limit = params[:limit] || 20
|
|
|
|
|
|
|
|
|
|
@shixuns = @shixuns.page(page).per(limit)
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
def new
|
|
|
|
|
@introduction_sample = PlatformSample.where(samples_type: ['introduction', 'knowledge']).pluck([:samples_type, :contents])
|
|
|
|
|
@main_type = shixun_main_type
|
|
|
|
|
@small_type = shixun_small_type
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
def create
|
|
|
|
|
# 评测脚本的一些操作
|
|
|
|
|
main_type, sub_type = params[:main_type], params[:small_type]
|
|
|
|
|
mirror = MirrorScript.where(:mirror_repository_id => main_type)
|
|
|
|
|
|
|
|
|
|
identifier = generate_identifier Shixun, 8
|
|
|
|
|
@shixun = Shixun.new(shixun_params)
|
|
|
|
|
@shixun.identifier = identifier
|
|
|
|
|
@shixun.user_id = current_user.id
|
|
|
|
|
@shixun.reset_time, @shixun.modify_time = Time.now, Time.now
|
|
|
|
|
|
|
|
|
|
if sub_type.blank?
|
|
|
|
|
shixun_script = mirror.first.try(:script)
|
|
|
|
|
else
|
|
|
|
|
main_mirror = MirrorRepository.find(main_type).type_name
|
|
|
|
|
sub_mirror = MirrorRepository.where(id: sub_type).pluck(:type_name)
|
|
|
|
|
if main_mirror == "Java" && sub_mirror.include?("Mysql")
|
|
|
|
|
shixun_script = mirror.last.try(:script)
|
|
|
|
|
else
|
|
|
|
|
shixun_script = mirror.first.try(:script)
|
|
|
|
|
shixun_script = modify_shixun_script @shixun, shixun_script
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
ActiveRecord::Base.transaction do
|
|
|
|
|
begin
|
|
|
|
|
@shixun.save!
|
|
|
|
|
# shixun_info关联ß
|
|
|
|
|
ShixunInfo.create!(shixun_id: @shixun.id, evaluate_script: shixun_script, description: params[:description])
|
|
|
|
|
# 实训的公开范围
|
|
|
|
|
if params[:scope_partment].present?
|
|
|
|
|
arr = []
|
|
|
|
|
ids = School.where(:name => params[:scope_partment]).pluck(:id).uniq
|
|
|
|
|
ids.each do |id|
|
|
|
|
|
arr << { :school_id => id, :shixun_id => @shixun.id }
|
|
|
|
|
end
|
|
|
|
|
ShixunSchool.create!(arr)
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
# 实训合作者
|
|
|
|
|
@shixun.shixun_members.create!(user_id: current_user.id, role: 1)
|
|
|
|
|
|
|
|
|
|
# 镜像-实训关联表
|
|
|
|
|
ShixunMirrorRepository.create!(:shixun_id => @shixun.id, :mirror_repository_id => main_type.to_i) if main_type.present?
|
|
|
|
|
# 实训主镜像服务配置
|
|
|
|
|
ShixunServiceConfig.create!(:shixun_id => @shixun.id, :mirror_repository_id => main_type.to_i)
|
|
|
|
|
if sub_type.present?
|
|
|
|
|
sub_type.each do |mirror|
|
|
|
|
|
ShixunMirrorRepository.create!(:shixun_id => @shixun.id, :mirror_repository_id => mirror)
|
|
|
|
|
# 实训子镜像服务配置
|
|
|
|
|
ShixunServiceConfig.create!(:shixun_id => @shixun.id, :mirror_repository_id => mirror)
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
# 创建版本库
|
|
|
|
|
repo_path = repo_namespace(User.current.login, @shixun.identifier)
|
|
|
|
|
GitService.add_repository(repo_path: repo_path)
|
|
|
|
|
# todo: 为什么保存的时候要去除后面的.git呢??
|
|
|
|
|
@shixun.update_column(:repo_name, repo_path.split(".")[0])
|
|
|
|
|
rescue Exception => e
|
|
|
|
|
uid_logger_error(e.message)
|
|
|
|
|
tip_exception("实训创建失败")
|
|
|
|
|
raise ActiveRecord::Rollback
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
def apply_shixun_mirror
|
|
|
|
|
form_params = params.permit(*%i[language runtime run_method attachment_id])
|
|
|
|
|
form = ApplyShixunMirrorForm.new(form_params)
|
|
|
|
|
form.validate!
|
|
|
|
|
|
|
|
|
|
tiding = Tiding.new(
|
|
|
|
|
user_id: 1,
|
|
|
|
|
trigger_user_id: current_user.id,
|
|
|
|
|
container_type: 'SendMessage',
|
|
|
|
|
viewed: 0,
|
|
|
|
|
tiding_type: 'Apply',
|
|
|
|
|
extra: form.to_json
|
|
|
|
|
)
|
|
|
|
|
ActiveRecord::Base.transaction do
|
|
|
|
|
# TODO: 由于tiding是多态,而SendMessage却没有对应的model,因此,如果不跳过验证会报 container must exist的错.
|
|
|
|
|
tiding.save(validate: false)
|
|
|
|
|
|
|
|
|
|
form.attachment.update!(container: tiding)
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
sucess_status
|
|
|
|
|
rescue ActiveModel::ValidationError => ex
|
|
|
|
|
tip_exception(ex.message)
|
|
|
|
|
rescue Exception => e
|
|
|
|
|
uid_logger_error(e.message)
|
|
|
|
|
tip_exception("申请失败")
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
def update
|
|
|
|
|
ActiveRecord::Base.transaction do
|
|
|
|
|
begin
|
|
|
|
|
@shixun.shixun_mirror_repositories.destroy_all
|
|
|
|
|
if params[:main_type].present?
|
|
|
|
|
ShixunMirrorRepository.create(:shixun_id => @shixun.id, :mirror_repository_id => params[:main_type].to_i)
|
|
|
|
|
end
|
|
|
|
|
if params[:small_type].present?
|
|
|
|
|
params[:small_type].each do |mirror|
|
|
|
|
|
ShixunMirrorRepository.create(:shixun_id => @shixun.id, :mirror_repository_id => mirror)
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
@shixun.update_attributes(shixun_params)
|
|
|
|
|
@shixun.shixun_info.update_attributes(shixun_info_params)
|
|
|
|
|
@shixun.shixun_schools.delete_all
|
|
|
|
|
if params[:scope_partment].present? && params[:user_scope].to_i == 1
|
|
|
|
|
arr = []
|
|
|
|
|
ids = School.where(:name => params[:scope_partment]).pluck(:id).uniq
|
|
|
|
|
ids.each do |id|
|
|
|
|
|
arr << { :school_id => id, :shixun_id => @shixun.id }
|
|
|
|
|
end
|
|
|
|
|
ShixunSchool.create!(arr)
|
|
|
|
|
use_scope = 1
|
|
|
|
|
else
|
|
|
|
|
use_scope = 0
|
|
|
|
|
end
|
|
|
|
|
@shixun.update_attributes!(:use_scope => use_scope)
|
|
|
|
|
# 超级管理员和运营人员才能保存 中间层服务器pod信息的配置
|
|
|
|
|
if current_user.admin? || current_user.business?
|
|
|
|
|
@shixun.shixun_service_configs.destroy_all
|
|
|
|
|
@shixun.shixun_service_configs.create!(service_config_params[:shixun_service_configs])
|
|
|
|
|
end
|
|
|
|
|
rescue Exception => e
|
|
|
|
|
uid_logger_error(e.message)
|
|
|
|
|
tip_exception("实训保存失败")
|
|
|
|
|
raise ActiveRecord::Rollback
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
# 永久关闭实训
|
|
|
|
|
def close
|
|
|
|
|
@shixun.update_attributes(status: 3, closer_id: current_user.id, end_time: Time.now)
|
|
|
|
|
sucess_status
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
def propaedeutics
|
|
|
|
|
@content = Shixun.find_by_identifier!(params[:identifier]).propaedeutics
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
# 更新背景知识
|
|
|
|
|
def update_propaedeutics
|
|
|
|
|
@shixun.shixun_info.update_column(:propaedeutics, params[:content])
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
# 获取推荐实训接口 2个热门实训 + 2个最新实训
|
|
|
|
|
def get_recommend_shixuns
|
|
|
|
|
hot_shixuns = Shixun.field_for_recommend.published.order("myshixuns_count desc").limit(2)
|
|
|
|
|
newest_shixuns = Shixun.field_for_recommend.published.order("created_at desc").limit(2)
|
|
|
|
|
@recommend_shixuns = hot_shixuns + newest_shixuns
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
def settings
|
|
|
|
|
@choice_main_type = @shixun.main_mirror_id
|
|
|
|
|
@choice_small_type = @shixun.small_mirror_id
|
|
|
|
|
@main_type = shixun_main_type
|
|
|
|
|
@small_type = shixun_small_type
|
|
|
|
|
@configs = @shixun.shixun_service_configs
|
|
|
|
|
#@mirror_script = MirrorScript.select([:id, :script_type]).find(@shixun.mirror_script_id).attributes if @shixun.mirror_script_id && @shixun.mirror_script_id != 0
|
|
|
|
|
# @shixun_main_mirror = @shixun.show_shixun_mirror
|
|
|
|
|
# @script_type = @shixun.script_tag.try(:script_type) || "无"
|
|
|
|
|
# @evaluate_scirpt = @shixun.evaluate_script || "无"
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
# 获取脚本内容
|
|
|
|
|
def get_script_contents
|
|
|
|
|
mirrir_script = MirrorScript.find(params[:script_id])
|
|
|
|
|
script = mirrir_script.try(:script)
|
|
|
|
|
@description = mirrir_script.try(:description)
|
|
|
|
|
@script = modify_shixun_script @shixun, script
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
def get_custom_script
|
|
|
|
|
shixun_script = PlatformSample.where(:samples_type => "script").first.try(:contents)
|
|
|
|
|
compile = params[:compile]
|
|
|
|
|
execute = params[:executive]
|
|
|
|
|
if shixun_script
|
|
|
|
|
shixun_script = compile.blank? ? shixun_script.gsub("COMPILEFUNCTION", "").gsub("CHALLENGEFIELPATH", "") :
|
|
|
|
|
shixun_script.gsub("COMPILEFUNCTION", "#{compile_command}").gsub("COMPILECOMMAND", "#{compile}")
|
|
|
|
|
shixun_script = execute.blank? ? shixun_script.gsub("EXECUTEFUNCTION", "") : shixun_script.gsub("EXECUTECOMMAND", "#{execute}")
|
|
|
|
|
shixun_script = modify_shixun_script @shixun, shixun_script
|
|
|
|
|
end
|
|
|
|
|
@shixun_script = shixun_script
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
def departments
|
|
|
|
|
@scope = []
|
|
|
|
|
q = params[:q]
|
|
|
|
|
if q && q.strip.present?
|
|
|
|
|
@scope = School.where("name like ?", "%#{q.strip}%").pluck(:name)
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
def get_mirror_script
|
|
|
|
|
mirror = MirrorRepository.find(params[:mirror_id])
|
|
|
|
|
@script = mirror.mirror_scripts
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
# TODO: 目前实训只做软删除.
|
|
|
|
|
def destroy
|
|
|
|
|
apply_records = ApplyAction.where(container_id: @shixun.id, container_type: "ApplyShixun")
|
|
|
|
|
apply_records.delete_all if apply_records
|
|
|
|
|
# HomeworkCommonShixuns.where(shixun_id: @shixun).delete_all
|
|
|
|
|
# @shixun.destroy
|
|
|
|
|
@shixun.update_column(:status, -1)
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
# 开启挑战
|
|
|
|
|
# 以前在开启挑战的时候检测实训是否更新,更新则重置,觉得应该放在TPI更好
|
|
|
|
|
# 中间需要一个过渡动画
|
|
|
|
|
# TODO: 第一次开启实训都会去判断是否是纯选择题类型,感觉做成在创建关卡的时候就判断该实训是否是纯选择题更加合适
|
|
|
|
|
def shixun_exec
|
|
|
|
|
if is_shixun_opening?
|
|
|
|
|
tip_show_exception(-3, "#{@shixun.opening_time.strftime('%Y-%m-%d %H:%M:%S')}")
|
|
|
|
|
end
|
|
|
|
|
current_myshixun = @shixun.current_myshixun(current_user.id)
|
|
|
|
|
|
|
|
|
|
min_challenges = @shixun.challenges.pluck(:id , :st)
|
|
|
|
|
|
|
|
|
|
Rails.logger.info("11111111112#{current_myshixun.try(:id)}")
|
|
|
|
|
Rails.logger.info("111111111102#{params[:reset] != 1}")
|
|
|
|
|
|
|
|
|
|
# 因为读写分离有延迟,所以如果是重置来的请求可以先跳过,重置过来的params[:reset]为1
|
|
|
|
|
if current_myshixun && params[:reset] != "1"
|
|
|
|
|
games = current_myshixun.games
|
|
|
|
|
# 如果TPM和TPI的管卡数不相等或者关卡顺序错了,说明实训被极大的改动,需要重置,实训发布前打过的实训都需要重置
|
|
|
|
|
if is_shixun_reset?(games, min_challenges, current_myshixun)
|
|
|
|
|
# 这里页面弹框要收到 当前用户myshixun的identifier.
|
|
|
|
|
tip_show_exception("/myshixuns/#{current_myshixun.try(:identifier)}/reset_my_game")
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if current_myshixun.repo_name.nil?
|
|
|
|
|
g = Gitlab.client
|
|
|
|
|
repo_name = g.project(current_myshixun.gpid).try(:path_with_namespace)
|
|
|
|
|
current_myshixun.update_column(:repo_name, repo_name)
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# 如果存在实训,则直接进入实训
|
|
|
|
|
# 如果实训允许跳关,传参params[:challenge_id]跳入具体的关卡
|
|
|
|
|
@current_task =
|
|
|
|
|
if params[:challenge_id]
|
|
|
|
|
game = games.where(challenge_id: params[:challenge_id]).take
|
|
|
|
|
if @shixun.task_pass || game.status != 3
|
|
|
|
|
game
|
|
|
|
|
else
|
|
|
|
|
current_myshixun.current_task(games)
|
|
|
|
|
end
|
|
|
|
|
else
|
|
|
|
|
current_myshixun.current_task(games)
|
|
|
|
|
end
|
|
|
|
|
else
|
|
|
|
|
# 如果未创建关卡一定不能开启实训,否则TPI没法找到当前的关卡
|
|
|
|
|
if @shixun.challenges_count == 0
|
|
|
|
|
tip_exception("开启实战前请先创建实训关卡")
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
# 判断实训是否全为选择题
|
|
|
|
|
is_choice_type = (min_challenges.size == min_challenges.select{|challenge| challenge.last == 1}.count)
|
|
|
|
|
if !is_choice_type
|
|
|
|
|
commit = GitService.commits(repo_path: @repo_path).try(:first)
|
|
|
|
|
uid_logger("First comit########{commit}")
|
|
|
|
|
tip_exception("开启实战前请先在版本库中提交代码") if commit.blank?
|
|
|
|
|
commit_id = commit["id"]
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
ActiveRecord::Base.transaction do
|
|
|
|
|
begin
|
|
|
|
|
cloud_bridge = edu_setting('cloud_bridge')
|
|
|
|
|
myshixun_identifier = generate_identifier Myshixun, 10
|
|
|
|
|
myshixun = @shixun.myshixuns.create!(user_id: current_user.id, identifier: myshixun_identifier,
|
|
|
|
|
modify_time: @shixun.modify_time, reset_time: @shixun.reset_time,
|
|
|
|
|
onclick_time: Time.now, commit_id: commit_id)
|
|
|
|
|
uid_logger("myshixun_id is #{myshixun.id}")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# 其它创建关卡等操作
|
|
|
|
|
challenges = @shixun.challenges
|
|
|
|
|
# 之所以增加user_id是为了方便统计查询性能
|
|
|
|
|
game_attrs = %i[challenge_id myshixun_id status user_id open_time identifier modify_time created_at updated_at]
|
|
|
|
|
Game.bulk_insert(*game_attrs) do |worker|
|
|
|
|
|
base_attr = { myshixun_id: myshixun.id, user_id: myshixun.user_id }
|
|
|
|
|
challenges.each_with_index do |challenge, index|
|
|
|
|
|
status = (index == 0 ? 0 : 3)
|
|
|
|
|
game_identifier = generate_identifier(Game, 12)
|
|
|
|
|
worker.add(base_attr.merge(challenge_id: challenge.id, status: status, open_time: Time.now,
|
|
|
|
|
identifier: game_identifier, modify_time: challenge.modify_time))
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
# 如果实训是纯选择题,则不需要去fork仓库以及中间层的相关操作了
|
|
|
|
|
unless is_choice_type
|
|
|
|
|
# fork仓库
|
|
|
|
|
project_fork(myshixun, @repo_path, current_user.login)
|
|
|
|
|
|
|
|
|
|
rep_url = Base64.urlsafe_encode64(repo_ip_url @repo_path )
|
|
|
|
|
uid_logger("start openGameInstance")
|
|
|
|
|
uri = "#{cloud_bridge}/bridge/game/openGameInstance"
|
|
|
|
|
logger.info("end openGameInstance")
|
|
|
|
|
params = {tpiID: "#{myshixun.id}", tpmGitURL:rep_url, tpiRepoName: myshixun.repo_name.split("/").last}
|
|
|
|
|
uid_logger("openGameInstance params is #{params}")
|
|
|
|
|
interface_post uri, params, 83, "实训云平台繁忙(繁忙等级:83)"
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
@current_task = myshixun.current_task(myshixun.games)
|
|
|
|
|
uid_logger("## shixun exec: myshixun id is #{myshixun.id}")
|
|
|
|
|
rescue Exception => e
|
|
|
|
|
uid_logger_error(e.message)
|
|
|
|
|
tip_exception("实训云平台繁忙(繁忙等级:81)")
|
|
|
|
|
raise ActiveRecord::Rollback
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
# gameID 及实训ID
|
|
|
|
|
# status: 0 , 1 申请过, 2,实训关卡路径未填, 3 实训标签未填, 4 实训未创建关卡
|
|
|
|
|
def publish
|
|
|
|
|
@status = 0
|
|
|
|
|
@position = []
|
|
|
|
|
begin
|
|
|
|
|
if @shixun.challenges.count == 0
|
|
|
|
|
@status = 4
|
|
|
|
|
else
|
|
|
|
|
@shixun.challenges.each do |challenge|
|
|
|
|
|
if challenge.challenge_tags.count == 0
|
|
|
|
|
@status = 3
|
|
|
|
|
@position << challenge.position
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
unfinish_challenge = @shixun.challenges.where(:st => 0, :path => nil)
|
|
|
|
|
if unfinish_challenge.count > 0 && !@shixun.is_choice_type?
|
|
|
|
|
@status = 2
|
|
|
|
|
@pos = []
|
|
|
|
|
unfinish_challenge.each do |challenge|
|
|
|
|
|
@pos << challenge.position
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
if @status == 0
|
|
|
|
|
@shixun.update_attributes!(:status => 1)
|
|
|
|
|
apply = ApplyAction.where(:container_type => "ApplyShixun", :container_id => @shixun.id).order("created_at desc").first
|
|
|
|
|
if apply && apply.status == 0
|
|
|
|
|
@status = 0
|
|
|
|
|
else
|
|
|
|
|
ApplyAction.create(:container_type => "ApplyShixun", :container_id => @shixun.id, :user_id => current_user.id, :status => 0)
|
|
|
|
|
#begin
|
|
|
|
|
# status = Trustie::Sms.send(mobile: '18711011226', send_type:'publish_shixun' , name: '管理员')
|
|
|
|
|
#rescue => e
|
|
|
|
|
# Rails.logger.error "发送验证码出错: #{e}"
|
|
|
|
|
#end
|
|
|
|
|
@status = 1
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
rescue Exception => e
|
|
|
|
|
logger.error("pushlish game #{e}")
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
include GitCommon
|
|
|
|
|
|
|
|
|
|
def update_file
|
|
|
|
|
content = params[:content]
|
|
|
|
|
author_name = current_user.full_name
|
|
|
|
|
author_email = current_user.git_mail
|
|
|
|
|
@content = update_file_content content, @repo_path, @path, author_email, author_name, "Edit by browser"
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
def add_collaborators
|
|
|
|
|
member_ids = "(" + @shixun.shixun_members.map(&:user_id).join(',') + ")"
|
|
|
|
|
user_name = "%#{params[:user_name].to_s.strip}%"
|
|
|
|
|
school_name = "%#{params[:school_name].to_s.strip}%"
|
|
|
|
|
if user_name.present? || school_name.present?
|
|
|
|
|
@users = User.joins(user_extension: :school).where("users.id not in #{member_ids} AND users.status = 1 AND
|
|
|
|
|
(LOWER(users.lastname) LIKE ? or users.phone like ?) AND LOWER(schools.name) LIKE
|
|
|
|
|
?", user_name, user_name, school_name)
|
|
|
|
|
else
|
|
|
|
|
@users = User.none
|
|
|
|
|
end
|
|
|
|
|
page = params[:page] || 1
|
|
|
|
|
limit = params[:limit] || 20
|
|
|
|
|
@user_count = @users.count
|
|
|
|
|
@users = @users.page(page).per(limit)
|
|
|
|
|
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
def shixun_members_added
|
|
|
|
|
raise("user_ids 不能为空!") if params[:user_ids].blank?
|
|
|
|
|
memberships = params[:user_ids]
|
|
|
|
|
memberships.each do |member|
|
|
|
|
|
ShixunMember.create!(:user_id => member, :shixun_id => @shixun.id, :role => 2)
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
def change_manager
|
|
|
|
|
# 搜索成员
|
|
|
|
|
if request.get?
|
|
|
|
|
@collaborators = @shixun.shixun_members.where("user_id != #{@shixun.user_id}")
|
|
|
|
|
else
|
|
|
|
|
if params[:user_id]
|
|
|
|
|
man_member = ShixunMember.where(:shixun_id => @shixun.id, :user_id => @shixun.user_id).first
|
|
|
|
|
cha_member = ShixunMember.where(:user_id => params[:user_id], :shixun_id => @shixun.id).first
|
|
|
|
|
if man_member && cha_member
|
|
|
|
|
man_member.update_attribute(:role, 2)
|
|
|
|
|
cha_member.update_attribute(:role, 1)
|
|
|
|
|
@shixun.update_attribute(:user_id, cha_member.user_id)
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
# 刪除合作者
|
|
|
|
|
def collaborators_delete
|
|
|
|
|
raise("user_id不能为空") if params[:user_id].blank?
|
|
|
|
|
shixun_member = ShixunMember.where(:user_id => params[:user_id], :shixun_id => @shixun.id, :role => 2).first
|
|
|
|
|
shixun_member.delete
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
# 实训的发送至课堂:搜索课堂
|
|
|
|
|
def search_user_courses
|
|
|
|
|
## 分页参数
|
|
|
|
|
page = params[:page] || 1
|
|
|
|
|
limit = params[:limit] || 20
|
|
|
|
|
if params[:search]
|
|
|
|
|
search = "%#{params[:search].to_s.strip.downcase}%"
|
|
|
|
|
course_ids = Course.find_by_sql("SELECT c.id FROM courses c, course_members m
|
|
|
|
|
WHERE m.course_id = c.id AND m.role in (1,2,3)
|
|
|
|
|
AND m.user_id=#{current_user.id} AND c.is_delete = 0 AND c.is_end = 0
|
|
|
|
|
AND c.name like '#{search}' ").map(&:id)
|
|
|
|
|
|
|
|
|
|
else
|
|
|
|
|
course_ids = Course.find_by_sql("SELECT c.id, c.name FROM courses c, course_members m
|
|
|
|
|
WHERE m.course_id = c.id AND m.role in (1,2,3)
|
|
|
|
|
AND m.user_id=#{current_user.id} AND c.is_delete = 0 AND c.is_end = 0").map(&:id)
|
|
|
|
|
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
@course_count = course_ids.length
|
|
|
|
|
@courses = Course.where(:id => course_ids).page(page).per(limit)
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
# 将实训发送到课程
|
|
|
|
|
def send_to_course
|
|
|
|
|
@course = Course.find(params[:course_id])
|
|
|
|
|
homework = HomeworksService.new.create_homework @shixun, @course, nil, current_user
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
# 二维码扫描下载
|
|
|
|
|
def download_file
|
|
|
|
|
file_path = params[:file_name]
|
|
|
|
|
send_file "#{Rails.root}/#{file_path}", :filename => "#{file_path}",
|
|
|
|
|
:type => 'shixun',
|
|
|
|
|
:disposition => 'attachment' #inline can open in browser
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
# 撤销发布
|
|
|
|
|
def cancel_publish
|
|
|
|
|
tip_exception("实训已经发布,无法撤销") if @shixun.status == 2
|
|
|
|
|
apply = ApplyAction.where(:container_type => "ApplyShixun", :container_id => @shixun.id).order("created_at desc").first
|
|
|
|
|
if apply && apply.status == 0
|
|
|
|
|
apply.update_attribute(:status, 3)
|
|
|
|
|
apply.tidings.destroy_all
|
|
|
|
|
end
|
|
|
|
|
@shixun.update_column(:status, 0)
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
private
|
|
|
|
|
def shixun_params
|
|
|
|
|
raise("实训名称不能为空") if params[:shixun][:name].blank?
|
|
|
|
|
params.require(:shixun).permit(:name, :trainee, :webssh, :can_copy, :use_scope, :vnc, :test_set_permission,
|
|
|
|
|
:task_pass, :multi_webssh, :opening_time, :mirror_script_id, :code_hidden,
|
|
|
|
|
:hide_code, :forbid_copy)
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
def shixun_info_params
|
|
|
|
|
raise("实训描述不能为空") if params[:shixun_info][:description].blank?
|
|
|
|
|
raise("评测脚本不能为空") if params[:shixun_info][:evaluate_script].blank?
|
|
|
|
|
params.require(:shixun_info).permit(:description, :evaluate_script)
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def service_config_params
|
|
|
|
|
params.permit(shixun_service_configs: [:cpu_limit, :lower_cpu_limit, :memory_limit, :request_limit, :mirror_repository_id])
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
def find_shixun
|
|
|
|
|
@shixun = Shixun.find_by_identifier(params[:identifier])
|
|
|
|
|
if @shixun.blank?
|
|
|
|
|
normal_status(404, "...")
|
|
|
|
|
return
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
def find_repo_name
|
|
|
|
|
@repo_path = @shixun.try(:repo_path)
|
|
|
|
|
@path = params[:path]
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
def allowed
|
|
|
|
|
unless current_user.manager_of_shixun?(@shixun)
|
|
|
|
|
tip_exception(403, "..")
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
def portion_allowed
|
|
|
|
|
if current_user.shixun_identity(@shixun) > User::EDU_CERTIFICATION_TEACHER
|
|
|
|
|
raise Educoder::TipException.new(403, "..")
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
def special_allowed
|
|
|
|
|
if @shixun.status != 2
|
|
|
|
|
tip_exception(403, "..")
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
# 实训是否需要开启
|
|
|
|
|
def is_shixun_opening?
|
|
|
|
|
@shixun.opening_time.present? &&
|
|
|
|
|
@shixun.opening_time > Time.now &&
|
|
|
|
|
current_user.shixun_identity(@shixun) > User::EDU_SHIXUN_MEMBER
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
# 实训是否需要重置
|
|
|
|
|
def is_shixun_reset?(games, min_challenges, current_myshixun)
|
|
|
|
|
# 用户在申请发布之前,是否玩过实训 TODO: 重置的字段应该迁移到myshixuns表比较合适
|
|
|
|
|
modify_shixun = ShixunModify.exists?(:myshixun_id => current_myshixun.id, :shixun_id => @shixun.id, :status => 1)
|
|
|
|
|
games.size != min_challenges.size || modify_shixun
|
|
|
|
|
end
|
|
|
|
|
end
|