Browse Source

Merge branch 'standalone_develop' into standalone

pull/279/head
jasder 4 years ago
parent
commit
16bfd3b2e3
100 changed files with 1451 additions and 1423 deletions
  1. +45
    -0
      api_document.md
  2. BIN
      app/assets/images/logo.png
  3. +35
    -0
      app/assets/javascripts/admin.js
  4. +141
    -0
      app/assets/javascripts/admins/projects/index.js
  5. +146
    -0
      app/assets/stylesheets/admin.scss
  6. +84
    -84
      app/controllers/accounts_controller.rb
  7. +13
    -4
      app/controllers/admins/project_categories_controller.rb
  8. +30
    -0
      app/controllers/admins/projects_controller.rb
  9. +4
    -0
      app/controllers/admins/system_notifications_controller.rb
  10. +57
    -0
      app/controllers/admins/topic/activity_forums_controller.rb
  11. +57
    -0
      app/controllers/admins/topic/banners_controller.rb
  12. +11
    -0
      app/controllers/admins/topic/base_controller.rb
  13. +57
    -0
      app/controllers/admins/topic/cards_controller.rb
  14. +57
    -0
      app/controllers/admins/topic/cooperators_controller.rb
  15. +57
    -0
      app/controllers/admins/topic/excellent_projects_controller.rb
  16. +57
    -0
      app/controllers/admins/topic/experience_forums_controller.rb
  17. +57
    -0
      app/controllers/admins/topic/pinned_forums_controller.rb
  18. +14
    -11
      app/controllers/admins/users_controller.rb
  19. +34
    -134
      app/controllers/application_controller.rb
  20. +1
    -1
      app/controllers/attachments_controller.rb
  21. +1
    -1
      app/controllers/concerns/controller_rescue_handler.rb
  22. +2
    -2
      app/controllers/concerns/git_common.rb
  23. +2
    -2
      app/controllers/concerns/git_helper.rb
  24. +4
    -0
      app/controllers/concerns/render_helper.rb
  25. +9
    -1
      app/controllers/issues_controller.rb
  26. +27
    -24
      app/controllers/journals_controller.rb
  27. +7
    -3
      app/controllers/organizations/organizations_controller.rb
  28. +4
    -0
      app/controllers/project_categories_controller.rb
  29. +26
    -0
      app/controllers/project_rank_controller.rb
  30. +6
    -0
      app/controllers/projects/members_controller.rb
  31. +10
    -4
      app/controllers/projects_controller.rb
  32. +5
    -0
      app/controllers/pull_requests_controller.rb
  33. +1
    -1
      app/controllers/repositories_controller.rb
  34. +9
    -0
      app/controllers/topics_controller.rb
  35. +24
    -0
      app/controllers/user_rank_controller.rb
  36. +18
    -16
      app/controllers/users/statistics_controller.rb
  37. +15
    -0
      app/controllers/users/system_notification_histories_controller.rb
  38. +2
    -0
      app/controllers/users_controller.rb
  39. +0
    -5
      app/decorators/course_decorator.rb
  40. +0
    -2
      app/decorators/ec_course_target_decorator.rb
  41. +0
    -16
      app/decorators/experience_decorator.rb
  42. +0
    -39
      app/decorators/grade_decorator.rb
  43. +0
    -5
      app/decorators/library_decorator.rb
  44. +0
    -5
      app/decorators/shixun_decorator.rb
  45. +0
    -5
      app/decorators/subject_decorator.rb
  46. +0
    -5
      app/decorators/video_decorator.rb
  47. +0
    -0
      app/docs/slate/source/images/trustie_logo.png
  48. +30
    -0
      app/docs/slate/source/includes/_users.md
  49. +41
    -0
      app/forms/accounts/reset_password_form.rb
  50. +0
    -10
      app/forms/add_school_apply_form.rb
  51. +0
    -27
      app/forms/apply_shixun_mirror_form.rb
  52. +41
    -2
      app/forms/base_form.rb
  53. +0
    -15
      app/forms/examination_banks/save_exam_form.rb
  54. +0
    -12
      app/forms/examination_intelligent_settings/save_exam_form.rb
  55. +0
    -11
      app/forms/examination_intelligent_settings/save_exam_setting_form.rb
  56. +3
    -2
      app/forms/projects/update_form.rb
  57. +30
    -0
      app/forms/register/base_form.rb
  58. +19
    -0
      app/forms/register/check_columns_form.rb
  59. +31
    -0
      app/forms/register/form.rb
  60. +0
    -20
      app/forms/weapps/create_course_form.rb
  61. +0
    -15
      app/forms/weapps/update_course_form.rb
  62. +1
    -1
      app/helpers/admins/projects_helper.rb
  63. +19
    -1
      app/helpers/application_helper.rb
  64. +26
    -0
      app/helpers/avatar_helper.rb
  65. +0
    -2
      app/helpers/boards_helper.rb
  66. +0
    -10
      app/helpers/challenges_helper.rb
  67. +0
    -2
      app/helpers/course_groups_helper.rb
  68. +0
    -2
      app/helpers/course_modules_helper.rb
  69. +0
    -2
      app/helpers/course_second_categories_helper.rb
  70. +0
    -2
      app/helpers/course_stages_helper.rb
  71. +0
    -298
      app/helpers/courses_helper.rb
  72. +0
    -2
      app/helpers/discusses_helper.rb
  73. +0
    -2
      app/helpers/edu_datas_helper.rb
  74. +0
    -150
      app/helpers/graduation_tasks_helper.rb
  75. +0
    -30
      app/helpers/graduation_topics_helper.rb
  76. +0
    -20
      app/helpers/graduation_works_helper.rb
  77. +0
    -2
      app/helpers/hack_user_lastest_codes_helper.rb
  78. +0
    -2
      app/helpers/hacks_helper.rb
  79. +10
    -0
      app/helpers/repositories_helper.rb
  80. +0
    -2
      app/helpers/trustie_hacks_helper.rb
  81. +0
    -69
      app/helpers/weapps/courses_helper.rb
  82. +0
    -20
      app/imports/admins/import_course_member_excel.rb
  83. +0
    -23
      app/jobs/batch_publish_video_notify_job.rb
  84. +12
    -0
      app/jobs/cache_async_clear_job.rb
  85. +16
    -0
      app/jobs/cache_async_reset_job.rb
  86. +16
    -0
      app/jobs/cache_async_set_job.rb
  87. +0
    -67
      app/jobs/course_add_student_create_works_job.rb
  88. +0
    -19
      app/jobs/course_delete_student_delete_works_job.rb
  89. +0
    -22
      app/jobs/course_delete_student_notify_job.rb
  90. +0
    -12
      app/jobs/create_diff_record_job.rb
  91. +0
    -21
      app/jobs/delete_department_notify_job.rb
  92. +0
    -44
      app/jobs/exercise_publish_notify_job.rb
  93. +0
    -17
      app/jobs/get_aliyun_video_info_job.rb
  94. +0
    -22
      app/jobs/graduation_task_cross_comment_job.rb
  95. +0
    -28
      app/jobs/graduation_task_publish_notify_job.rb
  96. +0
    -33
      app/jobs/resubmit_student_work_notify_job.rb
  97. +4
    -4
      app/jobs/send_template_message_job.rb
  98. +1
    -0
      app/libs/custom_regexp.rb
  99. +20
    -0
      app/libs/forum.rb
  100. +2
    -8
      app/mailers/user_mailer.rb

+ 45
- 0
api_document.md View File

@@ -51,6 +51,51 @@ http://localhost:3000/api/accounts/remote_register | jq
|-- token |string|用户token|


返回值
```json
{
"status": 0,
"message": "success",
"user": {
"id": 36400,
"token": "8c87a80d9cfacc92fcb2451845104f35119eda96"
}
}
```
---

#### 独立注册接口
```
POST accounts/register
```
*示例*
```bash
curl -X POST \
-d "login=2456233122@qq.com" \
-d "password=djs_D_00001" \
-d "namespace=16895620" \
-d "code=forge" \
http://localhost:3000/api/accounts/remote_register | jq
```
*请求参数说明:*

|参数名|必选|类型|说明|
|-|-|-|-|
|login |是|string |邮箱或者手机号 |
|namespace |是|string |登录名 |
|password |是|string |密码 |
|code |是|string |验证码 |


*返回参数说明:*

|参数名|类型|说明|
|-|-|-|
|user|json object |返回数据|
|-- id |int |用户id |
|-- token |string|用户token|


返回值
```json
{


BIN
app/assets/images/logo.png View File

Before After
Width: 111  |  Height: 33  |  Size: 2.8 kB Width: 347  |  Height: 110  |  Size: 10 kB

+ 35
- 0
app/assets/javascripts/admin.js View File

@@ -99,3 +99,38 @@ $(document).on("turbolinks:before-cache", function () {
$(function () {
});
$(document).on('turbolinks:load', function() {
$('.logo-item-left').on("change", 'input[type="file"]', function () {
var $fileInput = $(this);
var file = this.files[0];
var imageType = /image.*/;
if (file && file.type.match(imageType)) {
var reader = new FileReader();
reader.onload = function () {
var $box = $fileInput.parent();
$box.find('img').attr('src', reader.result).css('display', 'block');
$box.addClass('has-img');
};
reader.readAsDataURL(file);
} else {
}
});
$('.attachment-item-left').on("change", 'input[type="file"]', function () {
var $fileInput = $(this);
var file = this.files[0];
var imageType = /image.*/;
if (file && file.type.match(imageType)) {
var reader = new FileReader();
reader.onload = function () {
var $box = $fileInput.parent();
$box.find('img').attr('src', reader.result).css('display', 'block');
$box.addClass('has-img');
};
reader.readAsDataURL(file);
} else {
}
});
})

+ 141
- 0
app/assets/javascripts/admins/projects/index.js View File

@@ -0,0 +1,141 @@
/*
* @Description: Do not edit
* @Date: 2021-08-31 11:16:45
* @LastEditors: viletyy
* @Author: viletyy
* @LastEditTime: 2021-08-31 14:19:46
* @FilePath: /forgeplus/app/assets/javascripts/admins/system_notifications/index.js
*/
$(document).on('turbolinks:load', function(){

var showSuccessNotify = function() {
$.notify({
message: '操作成功'
},{
type: 'success'
});
}

// close user
$('.project-list-container').on('click', '.recommend-action', function(){
var $closeAction = $(this);
var $uncloseAction = $closeAction.siblings('.unrecommend-action');
var $editAction = $closeAction.siblings('.edit-recommend-action');

var keywordID = $closeAction.data('id');
customConfirm({
content: '确认将该项目设置为推荐项目吗?',
ok: function(){
$.ajax({
url: '/admins/projects/' + keywordID,
method: 'PUT',
dataType: 'json',
data: {
project: {
recommend: true,
recommend_index: 1
}
},
success: function() {
showSuccessNotify();
$closeAction.hide();
$uncloseAction.show();
$editAction.show();
$(".project-item-"+keywordID).children('td').eq(5).text("√")
}
});
}
});
});

// unclose user
$('.project-list-container').on('click', '.unrecommend-action', function(){
var $uncloseAction = $(this);
var $closeAction = $uncloseAction.siblings('.recommend-action');
var $editAction = $closeAction.siblings('.edit-recommend-action');

var keywordID = $uncloseAction.data('id');
customConfirm({
content: '确认取消该推荐项目吗?',
ok: function () {
$.ajax({
url: '/admins/projects/' + keywordID,
method: 'PUT',
dataType: 'json',
data: {
project: {
recommend: false,
recommend_index: 0
}
},
success: function() {
showSuccessNotify();
$closeAction.show();
$uncloseAction.hide();
$editAction.hide();
$(".project-item-"+keywordID).children('td').eq(5).text("")
}
});
}
})
});


// close user
$('.project-list-container').on('click', '.pinned-action', function(){
var $closeAction = $(this);
var $uncloseAction = $closeAction.siblings('.unpinned-action');

var keywordID = $closeAction.data('id');
customConfirm({
content: '确认将该项目设置为精选项目吗?',
ok: function(){
$.ajax({
url: '/admins/projects/' + keywordID,
method: 'PUT',
dataType: 'json',
data: {
project: {
is_pinned: true,
}
},
success: function() {
showSuccessNotify();
$closeAction.hide();
$uncloseAction.show();
$(".project-item-"+keywordID).children('td').eq(4).text("√")
}
});
}
});
});

// unclose user
$('.project-list-container').on('click', '.unpinned-action', function(){
var $uncloseAction = $(this);
var $closeAction = $uncloseAction.siblings('.pinned-action');

var keywordID = $uncloseAction.data('id');
customConfirm({
content: '确认取消该精选项目吗?',
ok: function () {
$.ajax({
url: '/admins/projects/' + keywordID,
method: 'PUT',
dataType: 'json',
data: {
project: {
is_pinned: false,
}
},
success: function() {
showSuccessNotify();
$closeAction.show();
$uncloseAction.hide();
$(".project-item-"+keywordID).children('td').eq(4).text("")
}
});
}
})
});
})

+ 146
- 0
app/assets/stylesheets/admin.scss View File

@@ -58,3 +58,149 @@ input.form-control {
position: absolute;
}

.logo-item {
display: flex;

&-img {
display: block;
width: 80px;
height: 80px;
background: #e9ecef;
}

&-upload {
cursor: pointer;
position: absolute;
top: 0;
width: 80px;
height: 80px;
background: #e9ecef;
border: 1px solid #ced4da;

&::before {
content: '';
position: absolute;
top: 27px;
left: 39px;
width: 2px;
height: 26px;
background: #495057;
}

&::after {
content: '';
position: absolute;
top: 39px;
left: 27px;
width: 26px;
height: 2px;
background: #495057;
}
}

&-left {
position: relative;
width: 80px;
height: 80px;

&.has-img {
.logo-item-upload {
display: none;
}

&:hover {
.logo-item-upload {
display: block;
background: rgba(145, 145, 145, 0.8);
}
}
}
}

&-right {
display: flex;
flex-direction: column;
justify-content: space-between;
color: #777777;
font-size: 0.8rem;
}

&-title {
color: #23272B;
font-size: 1rem;
}
}

.attachment-item {
display: flex;

&-img {
display: block;
width: 160px;
height: 160px;
background: #e9ecef;
}

&-upload {
cursor: pointer;
position: absolute;
top: 0;
width: 160px;
height: 160px;
background: #e9ecef;
border: 1px solid #ced4da;

&::before {
content: '';
position: absolute;
top: 54px;
left: 78px;
width: 2px;
height: 52px;
background: #495057;
}

&::after {
content: '';
position: absolute;
top: 78px;
left: 54px;
width: 52px;
height: 2px;
background: #495057;
}
}

&-left {
position: relative;
width: 160px;
height: 160px;

&.has-img {
.attachment-item-upload {
display: none;
}

&:hover {
.attachment-item-upload {
display: block;
background: rgba(145, 145, 145, 0.8);
}
}
}
}

&-right {
padding-top: 100px;
display: flex;
flex-direction: column;
justify-content: space-between;
color: #777777;
font-size: 0.8rem;
}

&-title {
color: #23272B;
font-size: 1rem;
}
}

+ 84
- 84
app/controllers/accounts_controller.rb View File

@@ -1,6 +1,5 @@
class AccountsController < ApplicationController
#skip_before_action :check_account, :only => [:logout]
include ApplicationHelper
def index
render json: session
@@ -9,7 +8,7 @@ class AccountsController < ApplicationController
# 其他平台同步注册的用户
def remote_register
username = params[:username]&.gsub(/\s+/, "")
tip_exception("无法使用以下关键词:#{username},请重新命名") if ReversedKeyword.is_reversed(username).present?
tip_exception("无法使用以下关键词:#{username},请重新命名") if ReversedKeyword.check_exists?(username)
email = params[:email]&.gsub(/\s+/, "")
password = params[:password]
platform = (params[:platform] || 'forge')&.gsub(/\s+/, "")
@@ -109,67 +108,48 @@ class AccountsController < ApplicationController
# 用户注册
# 注意:用户注册需要兼顾本地版,本地版是不需要验证码及激活码以及使用授权的,注册完成即可使用
# params[:login] 邮箱或者手机号
# params[:namespace] 登录名
# params[:code] 验证码
# code_type 1:注册手机验证码 8:邮箱注册验证码
# 本地forge注册入口
# 本地forge注册入口需要重新更改逻辑
def register
# type只可能是1或者8
user = nil
begin
# 查询验证码是否正确;type只可能是1或者8
type = phone_mail_type(params[:login].strip)
# code = params[:code].strip
if type == 1
uid_logger("start register by phone: type is #{type}")
pre = 'p'
email = nil
phone = params[:login]
# verifi_code = VerificationCode.where(phone: phone, code: code, code_type: 1).last
# TODO: 暂时限定邮箱注册
return normal_status(-1, '只支持邮箱注册')
else
uid_logger("start register by email: type is #{type}")
pre = 'm'
email = params[:login]
phone = nil
return normal_status(-1, "该邮箱已注册") if User.exists?(mail: params[:login])
return normal_status(-1, "邮箱格式错误") unless params[:login] =~ CustomRegexp::EMAIL
# verifi_code = VerificationCode.where(email: email, code: code, code_type: 8).last
end
# uid_logger("start register: verifi_code is #{verifi_code}, code is #{code}, time is #{Time.now.to_i - verifi_code.try(:created_at).to_i}")
# check_code = (verifi_code.try(:code) == code.strip && (Time.now.to_i - verifi_code.created_at.to_i) <= 10*60)
# todo 上线前请删除万能验证码"513231"
return normal_status(-1, "8~16位密码,支持字母数字和符号") unless params[:password] =~ CustomRegexp::PASSWORD
code = generate_identifier User, 8, pre
login = pre + code
is_admin = !User.exists?(type: 'User')
@user = User.new(admin: is_admin, login: login, mail: email, phone: phone, type: "User")
@user.password = params[:password]
# 现在因为是验证码,所以在注册的时候就可以激活
@user.activate
# 必须要用save操作,密码的保存是在users中
interactor = Gitea::RegisterInteractor.call({username: login, email: email, password: params[:password]})
Register::Form.new(register_params).validate!
user = Users::RegisterService.call(register_params)
password = register_params[:password].strip
# gitea用户注册, email, username, password
interactor = Gitea::RegisterInteractor.call({username: user.login, email: user.mail, password: password})
if interactor.success?
gitea_user = interactor.result
result = Gitea::User::GenerateTokenService.new(login, params[:password]).call
@user.gitea_token = result['sha1']
@user.gitea_uid = gitea_user[:body]['id']
if @user.save!
# set user for admin role
if @user.admin?
sync_params = { email: @user.mail, admin: true }
Gitea::User::UpdateInteractor.call(@user.login, sync_params)
end
UserExtension.create!(user_id: @user.id)
successful_authentication(@user)
normal_status("注册成功")
result = Gitea::User::GenerateTokenService.call(user.login, password)
user.gitea_token = result['sha1']
user.gitea_uid = gitea_user[:body]['id']
if user.save!
UserExtension.create!(user_id: user.id)
successful_authentication(user)
render_ok
end
else
tip_exception(-1, interactor.error)
end
rescue Register::BaseForm::EmailError => e
render_result(-2, e.message)
rescue Register::BaseForm::LoginError => e
render_result(-3, e.message)
rescue Register::BaseForm::PhoneError => e
render_result(-4, e.message)
rescue Register::BaseForm::PasswordFormatError => e
render_result(-5, e.message)
rescue Register::BaseForm::PasswordConfirmationError => e
render_result(-7, e.message)
rescue Register::BaseForm::VerifiCodeError => e
render_result(-6, e.message)
rescue Exception => e
Gitea::User::DeleteService.call(user.login) unless user.nil?
uid_logger_error(e.message)
tip_exception(-1, e.message)
end
@@ -177,7 +157,7 @@ class AccountsController < ApplicationController
# 用户登录
def login
Users::LoginForm.new(account_params).validate!
Users::LoginForm.new(login_params).validate!
@user = User.try_to_login(params[:login], params[:password])
return normal_status(-2, "错误的账号或密码") if @user.blank?
@@ -226,28 +206,27 @@ class AccountsController < ApplicationController
# 忘记密码
def reset_password
begin
code = params[:code]
login_type = phone_mail_type(params[:login].strip)
# 获取验证码
if login_type == 1
phone = params[:login]
verifi_code = VerificationCode.where(phone: phone, code: code, code_type: 2).last
user = User.find_by_phone(phone)
else
email = params[:login]
verifi_code = VerificationCode.where(email: email, code: code, code_type: 3).last
user = User.find_by_mail(email) #这里有问题,应该是为email,而不是mail 6.13-hs
end
return normal_status(-2, "验证码不正确") if verifi_code.try(:code) != code.strip
return normal_status(-2, "验证码已失效") if !verifi_code&.effective?
return normal_status(-1, "8~16位密码,支持字母数字和符号") unless params[:new_password] =~ CustomRegexp::PASSWORD
user.password, user.password_confirmation = params[:new_password], params[:new_password_confirmation]
ActiveRecord::Base.transaction do
user.save!
LimitForbidControl::UserLogin.new(user).clear
end
sucess_status
Accounts::ResetPasswordForm.new(reset_password_params).validate!
user = find_user
return render_error('未找到相关账号') if user.blank?
user = Accounts::ResetPasswordService.call(user, reset_password_params)
LimitForbidControl::UserLogin.new(user).clear if user.save!
render_ok
rescue Register::BaseForm::EmailError => e
render_result(-2, e.message)
rescue Register::BaseForm::PhoneError => e
render_result(-4, e.message)
rescue Register::BaseForm::PasswordFormatError => e
render_result(-5, e.message)
rescue Register::BaseForm::PasswordConfirmationError => e
render_result(-7, e.message)
rescue Register::BaseForm::VerifiCodeError => e
render_result(-6, e.message)
rescue ActiveRecord::Rollback => e
render_result(-1, "服务器异常")
rescue Exception => e
uid_logger_error(e.message)
tip_exception(e.message)
@@ -304,7 +283,7 @@ class AccountsController < ApplicationController
# 发送验证码
# params[:login] 手机号或者邮箱号
# params[:type]为事件通知类型 1:用户注册注册 2:忘记密码 3: 绑定手机 4: 绑定邮箱, 5: 验收手机号有效 # 如果有新的继续后面加
# params[:type]为事件通知类型 1:用户注册 2:忘记密码 3: 绑定手机 4: 绑定邮箱, 5: 验收手机号有效 # 如果有新的继续后面加
# 发送验证码:send_type 1:注册手机验证码 2:找回密码手机验证码 3:找回密码邮箱验证码 4:绑定手机 5:绑定邮箱
# 6:手机验证码登录 7:邮箱验证码登录 8:邮箱注册验证码 9: 验收手机号有效
def get_verification_code
@@ -318,19 +297,22 @@ class AccountsController < ApplicationController
sign = Digest::MD5.hexdigest("#{OPENKEY}#{value}")
tip_exception(501, "请求不合理") if sign != params[:smscode]
logger.info "########### 验证码:#{verification_code}"
logger.info("########get_verification_code: login_type: #{login_type}, send_type:#{send_type}, ")
# 记录验证码
check_verification_code(verification_code, send_type, value)
sucess_status
render_ok
end
# 1 手机类型;0 邮箱类型
# 注意新版的login是自动名生成的
def phone_mail_type value
value =~ /^1\d{10}$/ ? 1 : 0
# check user's login or email or phone is used
# params[:value] 手机号或者邮箱号或者登录名
# params[:type] 为事件类型 1:登录名(login) 2:email(邮箱) 3:phone(手机号)
def check
Register::CheckColumnsForm.new(check_params).validate!
render_ok
end
private
# type 事件类型 1:用户注册 2:忘记密码 3: 绑定手机 4: 绑定邮箱, 5: 验证手机号是否有效 # 如果有新的继续后面加
@@ -373,7 +355,25 @@ class AccountsController < ApplicationController
params.require(:user).permit(:login, :email, :phone)
end
def account_params
def login_params
params.require(:account).permit(:login, :password)
end
def check_params
params.permit(:type, :value)
end
def register_params
params.permit(:login, :namespace, :password, :password_confirmation, :code)
end
def reset_password_params
params.permit(:login, :password, :password_confirmation, :code)
end
def find_user
phone_or_mail = strip(reset_password_params[:login])
User.where("phone = :search OR mail = :search", search: phone_or_mail).last
end
end

+ 13
- 4
app/controllers/admins/project_categories_controller.rb View File

@@ -22,7 +22,7 @@ class Admins::ProjectCategoriesController < Admins::BaseController
max_position_items = ProjectCategory.select(:id, :position).pluck(:position).reject!(&:blank?)
max_position = max_position_items.present? ? max_position_items.max.to_i : 0

@project_category = ProjectCategory.new(name: @name,position: max_position)
@project_category = ProjectCategory.new(name: @name,position: max_position, pinned_index: params[:project_category][:pinned_index].to_i)
if @project_category.save
redirect_to admins_project_categories_path
flash[:success] = '创建成功'
@@ -33,17 +33,18 @@ class Admins::ProjectCategoriesController < Admins::BaseController
end

def update
if @project_category.update_attribute(:name, @name)
if @project_category.update_attributes({name: @name, pinned_index: params[:project_category][:pinned_index].to_i})
save_image_file(params[:logo], 'logo')
redirect_to admins_project_categories_path
flash[:success] = '更新成功'
else
redirect_to admins_project_categories_path
flash[:success] = '更新失败'
flash[:danger] = '更新失败'
end
end

def destroy
if @project_language.destroy
if @project_category.destroy
redirect_to admins_project_categories_path
flash[:success] = "删除成功"
else
@@ -80,4 +81,12 @@ class Admins::ProjectCategoriesController < Admins::BaseController
flash[:danger] = '分类已存在'
end
end

def save_image_file(file, type)
return unless file.present? && file.is_a?(ActionDispatch::Http::UploadedFile)

file_path = Util::FileManage.source_disk_filename(@project_category, type)
File.delete(file_path) if File.exist?(file_path) # 删除之前的文件
Util.write_file(file, file_path)
end
end

+ 30
- 0
app/controllers/admins/projects_controller.rb View File

@@ -1,4 +1,5 @@
class Admins::ProjectsController < Admins::BaseController
before_action :find_project, only: [:edit, :update]

def index
sort_by = Project.column_names.include?(params[:sort_by]) ? params[:sort_by] : 'created_on'
@@ -8,6 +9,26 @@ class Admins::ProjectsController < Admins::BaseController
@projects = paginate projects.includes(:owner, :members, :issues, :versions, :attachments, :project_score)
end

def edit ;end

def update
respond_to do |format|
if @project.update_attributes(project_update_params)
format.html do
redirect_to admins_projects_path
flash[:sucess] = "更新成功"
end
format.js {render_ok}
else
format.html do
redirect_to admins_projects_path
flash[:danger] = "更新失败"
end
format.js {render_js_error}
end
end
end

def destroy
project = Project.find_by!(id: params[:id])
ActiveRecord::Base.transaction do
@@ -21,4 +42,13 @@ class Admins::ProjectsController < Admins::BaseController
redirect_to admins_projects_path
flash[:danger] = "删除失败"
end

private
def find_project
@project = Project.find_by_id(params[:id])
end

def project_update_params
params.require(:project).permit(:is_pinned, :recommend, :recommend_index)
end
end

+ 4
- 0
app/controllers/admins/system_notifications_controller.rb View File

@@ -10,6 +10,10 @@ class Admins::SystemNotificationsController < Admins::BaseController
@notifications = paginate(notifications)
end

def history
@users = @notification.users
end

def new
@notification = SystemNotification.new
end


+ 57
- 0
app/controllers/admins/topic/activity_forums_controller.rb View File

@@ -0,0 +1,57 @@
class Admins::Topic::ActivityForumsController < Admins::Topic::BaseController
before_action :find_activity_forum, only: [:edit, :update, :destroy]

def index
q = ::Topic::ActivityForum.ransack(title_cont: params[:search])
activity_forums = q.result(distinct: true)
@activity_forums = paginate(activity_forums)
end

def new
@activity_forum = ::Topic::ActivityForum.new
end

def create
@activity_forum = ::Topic::ActivityForum.new(activity_forum_params)
if @activity_forum.save
redirect_to admins_topic_activity_forums_path
flash[:success] = "新增平台动态成功"
else
redirect_to admins_topic_activity_forums_path
flash[:danger] = "新增平台动态失败"
end
end

def edit
end

def update
@activity_forum.attributes = activity_forum_params
if @activity_forum.save
redirect_to admins_topic_activity_forums_path
flash[:success] = "更新平台动态成功"
else
redirect_to admins_topic_activity_forums_path
flash[:danger] = "更新平台动态失败"
end
end

def destroy
if @activity_forum.destroy
redirect_to admins_topic_activity_forums_path
flash[:success] = "删除平台动态成功"
else
redirect_to admins_topic_activity_forums_path
flash[:danger] = "删除平台动态失败"
end
end

private
def find_activity_forum
@activity_forum = ::Topic::ActivityForum.find_by_id(params[:id])
end

def activity_forum_params
params.require(:topic_activity_forum).permit(:title, :uuid, :url, :order_index)
end
end

+ 57
- 0
app/controllers/admins/topic/banners_controller.rb View File

@@ -0,0 +1,57 @@
class Admins::Topic::BannersController < Admins::Topic::BaseController
before_action :find_banner, only: [:edit, :update, :destroy]

def index
@banners = paginate(::Topic::Banner)
end

def new
@banner = ::Topic::Banner.new
end

def create
@banner = ::Topic::Banner.new(banner_params)
if @banner.save
save_image_file(params[:image], @banner)
redirect_to admins_topic_banners_path
flash[:success] = "新增banner成功"
else
redirect_to admins_topic_banners_path
flash[:danger] = "新增banner失败"
end
end

def edit
end

def update
@banner.attributes = banner_params
if @banner.save
save_image_file(params[:image], @banner)
redirect_to admins_topic_banners_path
flash[:success] = "更新banner成功"
else
redirect_to admins_topic_banners_path
flash[:danger] = "更新banner失败"
end
end

def destroy
if @banner.destroy
redirect_to admins_topic_banners_path
flash[:success] = "删除banner成功"
else
redirect_to admins_topic_banners_path
flash[:danger] = "删除banner失败"
end
end

private
def find_banner
@banner = ::Topic::Banner.find_by_id(params[:id])
end

def banner_params
params.require(:topic_banner).permit(:title, :order_index)
end
end

+ 11
- 0
app/controllers/admins/topic/base_controller.rb View File

@@ -0,0 +1,11 @@
class Admins::Topic::BaseController < Admins::BaseController
protected
def save_image_file(file, topic)
return unless file.present? && file.is_a?(ActionDispatch::Http::UploadedFile)

file_path = Util::FileManage.source_disk_filename(topic, 'image')
File.delete(file_path) if File.exist?(file_path) # 删除之前的文件
Util.write_file(file, file_path)
end
end

+ 57
- 0
app/controllers/admins/topic/cards_controller.rb View File

@@ -0,0 +1,57 @@
class Admins::Topic::CardsController < Admins::Topic::BaseController
before_action :find_card, only: [:edit, :update, :destroy]

def index
q = ::Topic::Card.ransack(title_cont: params[:search])
cards = q.result(distinct: true)
@cards = paginate(cards)
end

def new
@card = ::Topic::Card.new
end

def create
@card = ::Topic::Card.new(card_params)
if @card.save
redirect_to admins_topic_cards_path
flash[:success] = "新增合作单位成功"
else
redirect_to admins_topic_cards_path
flash[:danger] = "新增合作单位失败"
end
end

def edit
end

def update
@card.attributes = card_params
if @card.save
redirect_to admins_topic_cards_path
flash[:success] = "更新合作单位成功"
else
redirect_to admins_topic_cards_path
flash[:danger] = "更新合作单位失败"
end
end

def destroy
if @card.destroy
redirect_to admins_topic_cards_path
flash[:success] = "删除合作单位成功"
else
redirect_to admins_topic_cards_path
flash[:danger] = "删除合作单位失败"
end
end

private
def find_card
@card = ::Topic::Card.find_by_id(params[:id])
end

def card_params
params.require(:topic_card).permit(:title, :url, :order_index)
end
end

+ 57
- 0
app/controllers/admins/topic/cooperators_controller.rb View File

@@ -0,0 +1,57 @@
class Admins::Topic::CooperatorsController < Admins::Topic::BaseController
before_action :find_cooperator, only: [:edit, :update, :destroy]

def index
@cooperators = paginate(::Topic::Cooperator)
end

def new
@cooperator = ::Topic::Cooperator.new
end

def create
@cooperator = ::Topic::Cooperator.new(cooperator_params)
if @cooperator.save
save_image_file(params[:image], @cooperator)
redirect_to admins_topic_cooperators_path
flash[:success] = "新增合作单位成功"
else
redirect_to admins_topic_cooperators_path
flash[:danger] = "新增合作单位失败"
end
end

def edit
end

def update
@cooperator.attributes = cooperator_params
if @cooperator.save
save_image_file(params[:image], @cooperator)
redirect_to admins_topic_cooperators_path
flash[:success] = "更新合作单位成功"
else
redirect_to admins_topic_cooperators_path
flash[:danger] = "更新合作单位失败"
end
end

def destroy
if @cooperator.destroy
redirect_to admins_topic_cooperators_path
flash[:success] = "删除合作单位成功"
else
redirect_to admins_topic_cooperators_path
flash[:danger] = "删除合作单位失败"
end
end

private
def find_cooperator
@cooperator = ::Topic::Cooperator.find_by_id(params[:id])
end

def cooperator_params
params.require(:topic_cooperator).permit(:title, :url, :order_index)
end
end

+ 57
- 0
app/controllers/admins/topic/excellent_projects_controller.rb View File

@@ -0,0 +1,57 @@
class Admins::Topic::ExcellentProjectsController < Admins::Topic::BaseController
before_action :find_excellent_project, only: [:edit, :update, :destroy]

def index
q = ::Topic::ExcellentProject.ransack(title_cont: params[:search])
excellent_projects = q.result(distinct: true)
@excellent_projects = paginate(excellent_projects)
end

def new
@excellent_project = ::Topic::ExcellentProject.new
end

def create
@excellent_project = ::Topic::ExcellentProject.new(excellent_project_params)
if @excellent_project.save
redirect_to admins_topic_excellent_projects_path
flash[:success] = "新增优秀仓库成功"
else
redirect_to admins_topic_excellent_projects_path
flash[:danger] = "新增优秀仓库失败"
end
end

def edit
end

def update
@excellent_project.attributes = excellent_project_params
if @excellent_project.save
redirect_to admins_topic_excellent_projects_path
flash[:success] = "更新优秀仓库成功"
else
redirect_to admins_topic_excellent_projects_path
flash[:danger] = "更新优秀仓库失败"
end
end

def destroy
if @excellent_project.destroy
redirect_to admins_topic_excellent_projects_path
flash[:success] = "删除优秀仓库成功"
else
redirect_to admins_topic_excellent_projects_path
flash[:danger] = "删除优秀仓库失败"
end
end

private
def find_excellent_project
@excellent_project = ::Topic::ExcellentProject.find_by_id(params[:id])
end

def excellent_project_params
params.require(:topic_excellent_project).permit(:title, :uuid, :url, :order_index)
end
end

+ 57
- 0
app/controllers/admins/topic/experience_forums_controller.rb View File

@@ -0,0 +1,57 @@
class Admins::Topic::ExperienceForumsController < Admins::Topic::BaseController
before_action :find_experience_forum, only: [:edit, :update, :destroy]

def index
q = ::Topic::ExperienceForum.ransack(title_cont: params[:search])
experience_forums = q.result(distinct: true)
@experience_forums = paginate(experience_forums)
end

def new
@experience_forum = ::Topic::ExperienceForum.new
end

def create
@experience_forum = ::Topic::ExperienceForum.new(experience_forum_params)
if @experience_forum.save
redirect_to admins_topic_experience_forums_path
flash[:success] = "新增经验分享成功"
else
redirect_to admins_topic_experience_forums_path
flash[:danger] = "新增经验分享失败"
end
end

def edit
end

def update
@experience_forum.attributes = experience_forum_params
if @experience_forum.save
redirect_to admins_topic_experience_forums_path
flash[:success] = "更新经验分享成功"
else
redirect_to admins_topic_experience_forums_path
flash[:danger] = "更新经验分享失败"
end
end

def destroy
if @experience_forum.destroy
redirect_to admins_topic_experience_forums_path
flash[:success] = "删除经验分享成功"
else
redirect_to admins_topic_experience_forums_path
flash[:danger] = "删除经验分享失败"
end
end

private
def find_experience_forum
@experience_forum = ::Topic::ExperienceForum.find_by_id(params[:id])
end

def experience_forum_params
params.require(:topic_experience_forum).permit(:title, :uuid, :url, :order_index)
end
end

+ 57
- 0
app/controllers/admins/topic/pinned_forums_controller.rb View File

@@ -0,0 +1,57 @@
class Admins::Topic::PinnedForumsController < Admins::Topic::BaseController
before_action :find_pinned_forum, only: [:edit, :update, :destroy]

def index
q = ::Topic::PinnedForum.ransack(title_cont: params[:search])
pinned_forums = q.result(distinct: true)
@pinned_forums = paginate(pinned_forums)
end

def new
@pinned_forum = ::Topic::PinnedForum.new
end

def create
@pinned_forum = ::Topic::PinnedForum.new(pinned_forum_params)
if @pinned_forum.save
redirect_to admins_topic_pinned_forums_path
flash[:success] = "新增精选文章成功"
else
redirect_to admins_topic_pinned_forums_path
flash[:danger] = "新增精选文章失败"
end
end

def edit
end

def update
@pinned_forum.attributes = pinned_forum_params
if @pinned_forum.save
redirect_to admins_topic_pinned_forums_path
flash[:success] = "更新精选文章成功"
else
redirect_to admins_topic_pinned_forums_path
flash[:danger] = "更新精选文章失败"
end
end

def destroy
if @pinned_forum.destroy
redirect_to admins_topic_pinned_forums_path
flash[:success] = "删除精选文章成功"
else
redirect_to admins_topic_pinned_forums_path
flash[:danger] = "删除精选文章失败"
end
end

private
def find_pinned_forum
@pinned_forum = ::Topic::PinnedForum.find_by_id(params[:id])
end

def pinned_forum_params
params.require(:topic_pinned_forum).permit(:title, :uuid, :url, :order_index)
end
end

+ 14
- 11
app/controllers/admins/users_controller.rb View File

@@ -1,4 +1,6 @@
class Admins::UsersController < Admins::BaseController
before_action :finder_user, except: [:index]

def index
params[:sort_by] = params[:sort_by].presence || 'created_on'
params[:sort_direction] = params[:sort_direction].presence || 'desc'
@@ -8,12 +10,9 @@ class Admins::UsersController < Admins::BaseController
end

def edit
@user = User.find(params[:id])
end

def update
@user = User.find(params[:id])

Admins::UpdateUserService.call(@user, update_params)
flash[:success] = '保存成功'
redirect_to edit_admins_user_path(@user)
@@ -26,43 +25,47 @@ class Admins::UsersController < Admins::BaseController
end

def destroy
User.find(params[:id]).destroy!
@user.destroy!
Gitea::User::DeleteService.call(@user.login)

render_delete_success
end

def lock
User.find(params[:id]).lock!
@user.lock!

render_ok
end

def unlock
User.find(params[:id]).activate!
@user.activate!

render_ok
end

def reward_grade
user = User.find(params[:user_id])
return render_unprocessable_entity('金币数量必须大于0') if params[:grade].to_i <= 0

RewardGradeService.call(user, container_id: user.id, container_type: 'Feedback', score: params[:grade].to_i, not_unique: true)
RewardGradeService.call(@user, container_id: @user.id, container_type: 'Feedback', score: params[:grade].to_i, not_unique: true)

render_ok(grade: user.grade)
render_ok(grade: @user.grade)
end

def reset_login_times
User.find(params[:id]).reset_login_times!
@user.reset_login_times!

render_ok
end

private

def finder_user
@user = User.find(params[:id])
end

def update_params
params.require(:user).permit(%i[lastname nickname gender identity technical_title student_id is_shixun_marker
mail phone location location_city school_id department_id admin business is_test
password professional_certification authentication])
password professional_certification authentication login])
end
end

+ 34
- 134
app/controllers/application_controller.rb View File

@@ -26,7 +26,8 @@ class ApplicationController < ActionController::Base
end

DCODES = %W(2 3 4 5 6 7 8 9 a b c f e f g h i j k l m n o p q r s t u v w x y z)
OPENKEY = "79e33abd4b6588941ab7622aed1e67e8"
OPENKEY = Rails.application.config_for(:configuration)['sign_key'] || "79e33abd4b6588941ab7622aed1e67e8"


helper_method :current_user, :base_url

@@ -70,49 +71,11 @@ class ApplicationController < ActionController::Base
(current_user.professional_certification && (ue.teacher? || ue.professional?))
end

def shixun_marker
unless current_user.is_shixun_marker? || current_user.admin_or_business?
tip_exception(403, "..")
end
end

# 实训的访问权限
def shixun_access_allowed
if !current_user.shixun_permission(@shixun)
tip_exception(403, "..")
end
end

def admin_or_business?
User.current.admin? || User.current.business?
end

# 访问课堂时没权限直接弹加入课堂的弹框 :409
def user_course_identity
@user_course_identity = current_user.course_identity(@course)
if @user_course_identity > Course::STUDENT && @course.is_public == 0
tip_exception(401, "..") unless User.current.logged?
check_account
tip_exception(@course.excellent ? 410 : 409, "您没有权限进入")
end
if @user_course_identity > Course::CREATOR && @user_course_identity <= Course::STUDENT && @course.tea_id != current_user.id
# 实名认证和职业认证的身份判断
tip_exception(411, "你的实名认证和职业认证审核未通过") if @course.authentication &&
@course.professional_certification && (!current_user.authentication && !current_user.professional_certification)
tip_exception(411, "你的实名认证审核未通过") if @course.authentication && !current_user.authentication
tip_exception(411, "你的职业认证审核未通过") if @course.professional_certification && !current_user.professional_certification
end
uid_logger("###############user_course_identity:#{@user_course_identity}")
end

# 题库的访问权限
def bank_visit_auth
tip_exception(-2,"未通过职业认证") if current_user.is_teacher? && !current_user.certification_teacher? && !current_user.admin_or_business? && @bank.user_id != current_user.id && @bank.is_public
tip_exception(403, "无权限") unless @bank.user_id == current_user.id || current_user.admin_or_business? ||
(current_user.certification_teacher? && @bank.is_public)
end


# 判断用户的邮箱或者手机是否可用
# params[:type] 1: 注册;2:忘记密码;3:绑定
def check_mail_and_phone_valid login, type
@@ -120,16 +83,16 @@ class ApplicationController < ActionController::Base
login =~ /^[a-zA-Z0-9]+([._\\]*[a-zA-Z0-9])$/
tip_exception(-2, "请输入正确的手机号或邮箱")
end
# 考虑到安全参数问题,多一次查询,去掉Union
user = User.where(phone: login).first || User.where(mail: login).first
if type.to_i == 1 && !user.nil?
user_exist = Owner.exists?(phone: login) || Owner.exists?(mail: login)
if user_exist && type.to_i == 1
tip_exception(-2, "该手机号码或邮箱已被注册")
elsif type.to_i == 2 && user.nil?
elsif type.to_i == 2 && !user_exist
tip_exception(-2, "该手机号码或邮箱未注册")
elsif type.to_i == 3 && user.present?
elsif type.to_i == 3 && user_exist
tip_exception(-2, "该手机号码或邮箱已绑定")
end
sucess_status
render_ok
end

# 发送及记录激活码
@@ -140,7 +103,7 @@ class ApplicationController < ActionController::Base
when 1, 2, 4, 9
# 手机类型的发送
sigle_para = {phone: value}
status = Educoder::Sms.send(mobile: value, code: code)
status = Gitlink::Sms.send(mobile: value, code: code)
tip_exception(-2, code_msg(status)) if status != 0
when 8, 3, 5
# 邮箱类型的发送
@@ -186,26 +149,6 @@ class ApplicationController < ActionController::Base
end
end

def find_course
return normal_status(2, '缺少course_id参数!') if params[:course_id].blank?
@course = Course.find(params[:course_id])
tip_exception(404, "") if @course.is_delete == 1 && !current_user.admin_or_business?
rescue Exception => e
tip_exception(e.message)
end

def course_manager
return normal_status(403, '只有课堂管理员才有权限') if @user_course_identity > Course::CREATOR
end

def find_board
return normal_status(2, "缺少board_id参数") if params[:board_id].blank?
@board = Board.find(params[:board_id])
rescue Exception => e
uid_logger_error(e.message)
tip_exception(e.message)
end

def validate_type(object_type)
normal_status(2, "参数") if params.has_key?(:sort_type) && !SORT_TYPE.include?(params[:sort_type].strip)
end
@@ -215,21 +158,6 @@ class ApplicationController < ActionController::Base
@page_size = params[:page_size] || 15
end

# 课堂教师权限
def teacher_allowed
logger.info("#####identity: #{current_user.course_identity(@course)}")
unless current_user.course_identity(@course) < Course::STUDENT
normal_status(403, "")
end
end

# 课堂教师、课堂管理员、超级管理员的权限(不包含助教)
def teacher_or_admin_allowed
unless current_user.course_identity(@course) < Course::ASSISTANT_PROFESSOR
normal_status(403, "")
end
end

def require_admin
normal_status(403, "") unless User.current.admin?
end
@@ -256,7 +184,7 @@ class ApplicationController < ActionController::Base

# 异常提醒
def tip_exception(status = -1, message)
raise Educoder::TipException.new(status, message)
raise Gitlink::TipException.new(status, message)
end

def missing_template
@@ -265,7 +193,7 @@ class ApplicationController < ActionController::Base

# 弹框提醒
def tip_show_exception(status = -2, message)
raise Educoder::TipException.new(status, message)
raise Gitlink::TipException.new(status, message)
end

def normal_status(status = 0, message)
@@ -344,18 +272,18 @@ class ApplicationController < ActionController::Base

# 测试版前端需求
logger.info("subdomain:#{request.subdomain}")
if request.subdomain != "www"
if params[:debug] == 'teacher' #todo 为了测试,记得讲debug删除
User.current = User.find 81403
elsif params[:debug] == 'student'
User.current = User.find 8686
elsif params[:debug] == 'admin'
logger.info "@@@@@@@@@@@@@@@@@@@@@@ debug mode....."
user = User.find 36480
User.current = user
cookies.signed[:user_id] = user.id
end
end
# if request.subdomain != "www"
# if params[:debug] == 'teacher' #todo 为了测试,记得讲debug删除
# User.current = User.find 81403
# elsif params[:debug] == 'student'
# User.current = User.find 8686
# elsif params[:debug] == 'admin'
# logger.info "@@@@@@@@@@@@@@@@@@@@@@ debug mode....."
# user = User.find 36480
# User.current = user
# cookies.signed[:user_id] = user.id
# end
# end
# User.current = User.find 81403
end

@@ -408,11 +336,6 @@ class ApplicationController < ActionController::Base
@message = message
end

# 实训等对应的仓库地址
def repo_ip_url(repo_path)
"#{edu_setting('git_address_ip')}/#{repo_path}"
end

def repo_url(repo_path)
"#{edu_setting('git_address_domain')}/#{repo_path}"
end
@@ -445,7 +368,7 @@ class ApplicationController < ActionController::Base
JSON.parse(res)
rescue Exception => e
uid_logger_error("--uri_exec: exception #{e.message}")
raise Educoder::TipException.new("实训平台繁忙(繁忙等级:84)")
raise Gitlink::TipException.new("实训平台繁忙(繁忙等级:84)")
end
end

@@ -464,7 +387,7 @@ class ApplicationController < ActionController::Base
end
rescue Exception => e
uid_logger("--uri_exec: exception #{e.message}")
raise Educoder::TipException.new(message)
raise Gitlink::TipException.new(message)
end
end

@@ -488,7 +411,7 @@ class ApplicationController < ActionController::Base
end
rescue Exception => e
uid_logger("--uri_exec: exception #{e.message}")
raise Educoder::TipException.new("服务器繁忙")
raise Gitlink::TipException.new("服务器繁忙")
end
end

@@ -660,8 +583,8 @@ class ApplicationController < ActionController::Base

# 获取Oauth Client
def get_client(site)
client_id = Rails.configuration.educoder['client_id']
client_secret = Rails.configuration.educoder['client_secret']
client_id = Rails.configuration.Gitlink['client_id']
client_secret = Rails.configuration.Gitlink['client_secret']

OAuth2::Client.new(client_id, client_secret, site: site)
end
@@ -681,7 +604,7 @@ class ApplicationController < ActionController::Base

def kaminari_paginate(relation)
limit = params[:limit] || params[:per_page]
limit = (limit.to_i.zero? || limit.to_i > 15) ? 15 : limit.to_i
limit = (limit.to_i.zero? || limit.to_i > 20) ? 20 : limit.to_i
page = params[:page].to_i.zero? ? 1 : params[:page].to_i

relation.page(page).per(limit)
@@ -689,7 +612,7 @@ class ApplicationController < ActionController::Base

def kaminari_array_paginate(relation)
limit = params[:limit] || params[:per_page]
limit = (limit.to_i.zero? || limit.to_i > 15) ? 15 : limit.to_i
limit = (limit.to_i.zero? || limit.to_i > 20) ? 20 : limit.to_i
page = params[:page].to_i.zero? ? 1 : params[:page].to_i

Kaminari.paginate_array(relation).page(page).per(limit)
@@ -814,37 +737,10 @@ class ApplicationController < ActionController::Base
render json: exception.tip_json
end

def render_parameter_missing
render json: { status: -1, message: '参数缺失' }
end

def set_export_cookies
cookies[:fileDownload] = true
end

# 149课程的评审用户数据创建(包含创建课堂学生)
def open_class_user
user = User.find_by(login: "OpenClassUser")
unless user
ActiveRecord::Base.transaction do
user_params = {status: 1, login: "OpenClassUser", lastname: "开放课程",
nickname: "开放课程", professional_certification: 1, certification: 1, grade: 0,
password: "12345678", phone: "11122223333", profile_completed: 1}
user = User.create!(user_params)

UserExtension.create!(user_id: user.id, gender: 0, school_id: 3396, :identity => 1, :student_id => "openclassuser") # 3396

subject = Subject.find_by(id: 149)
if subject
subject.courses.each do |course|
CourseMember.create!(course_id: course.id, role: 3, user_id: user.id) if !course.course_members.exists?(user_id: user.id)
end
end
end
end
user
end

# 记录热门搜索关键字
def record_search_keyword
keyword = params[:keyword].to_s.strip
@@ -854,4 +750,8 @@ class ApplicationController < ActionController::Base
HotSearchKeyword.add(keyword)
end

def find_atme_receivers
@atme_receivers = User.where(login: params[:receivers_login])
end

end

+ 1
- 1
app/controllers/attachments_controller.rb View File

@@ -196,7 +196,7 @@ class AttachmentsController < ApplicationController
end
def file_save_to_ucloud(path, file, content_type)
ufile = Educoder::Ufile.new(
ufile = Gitlink::Ufile.new(
ucloud_public_key: edu_setting('public_key'),
ucloud_private_key: edu_setting('private_key'),
ucloud_public_read: true,


+ 1
- 1
app/controllers/concerns/controller_rescue_handler.rb View File

@@ -20,7 +20,7 @@ module ControllerRescueHandler
end
# rescue_from ActionView::MissingTemplate, with: :object_not_found
# rescue_from ActiveRecord::RecordNotFound, with: :object_not_found
rescue_from Educoder::TipException, with: :tip_show
rescue_from Gitlink::TipException, with: :tip_show
rescue_from ::ActionView::MissingTemplate, with: :missing_template
rescue_from ActiveRecord::RecordNotFound, with: :object_not_found
rescue_from ActionController::ParameterMissing, with: :render_parameter_missing


+ 2
- 2
app/controllers/concerns/git_common.rb View File

@@ -36,10 +36,10 @@ module GitCommon
begin
@commits = GitService.commits(repo_path: @repo_path)
logger.info("git first commit is #{@commits.try(:first)}")
raise Educoder::TipException.new("请先创建版本库") if @commits.nil?
raise Gitlink::TipException.new("请先创建版本库") if @commits.nil?
rescue Exception => e
uid_logger_error(e.message)
raise Educoder::TipException.new("提交记录异常")
raise Gitlink::TipException.new("提交记录异常")
end
end


+ 2
- 2
app/controllers/concerns/git_helper.rb View File

@@ -34,7 +34,7 @@ module GitHelper

rescue Exception => e
Rails.logger.error(e.message)
raise Educoder::TipException.new("文档内容获取异常")
raise Gitlink::TipException.new("文档内容获取异常")
end
end

@@ -64,7 +64,7 @@ module GitHelper

# 版本库Fork功能
def project_fork(container, original_rep_path, username)
raise Educoder::TipException.new("fork源路径为空,fork失败!") if original_rep_path.blank?
raise Gitlink::TipException.new("fork源路径为空,fork失败!") if original_rep_path.blank?
# 将要生成的仓库名字
new_repo_name = "#{username.try(:strip)}/#{container.try(:identifier)}#{ Time.now.strftime("%Y%m%d%H%M%S")}"
# uid_logger("start fork container: repo_name is #{new_repo_name}")


+ 4
- 0
app/controllers/concerns/render_helper.rb View File

@@ -28,4 +28,8 @@ module RenderHelper
def render_result(status=1, message='success')
render json: { status: status, message: message }
end
def render_parameter_missing
render json: { status: -1, message: '参数缺失' }
end
end

+ 9
- 1
app/controllers/issues_controller.rb View File

@@ -9,7 +9,7 @@ class IssuesController < ApplicationController
before_action :check_project_public, only: [:index ,:show, :copy, :index_chosen, :close_issue]

before_action :set_issue, only: [:edit, :update, :destroy, :show, :copy, :close_issue, :lock_issue]
before_action :check_token_enough, only: [:create, :update]
before_action :check_token_enough, :find_atme_receivers, only: [:create, :update]

include ApplicationHelper
include TagChosenHelper
@@ -142,6 +142,10 @@ class IssuesController < ApplicationController
end

@issue.project_trends.create(user_id: current_user.id, project_id: @project.id, action_type: "create")

Rails.logger.info "[ATME] maybe to at such users: #{@atme_receivers.pluck(:login)}"
AtmeService.call(current_user, @atme_receivers, @issue) if @atme_receivers.size > 0

render json: {status: 0, message: "创建成", id: @issue.id}
else
normal_status(-1, "创建失败")
@@ -244,6 +248,10 @@ class IssuesController < ApplicationController
post_to_chain(change_type, change_token.abs, current_user.try(:login))
end
@issue.create_journal_detail(change_files, issue_files, issue_file_ids, current_user&.id) if @issue.previous_changes.present?

Rails.logger.info "[ATME] maybe to at such users: #{@atme_receivers.pluck(:login)}"
AtmeService.call(current_user, @atme_receivers, @issue) if @atme_receivers.size > 0

normal_status(0, "更新成功")
else
normal_status(-1, "更新失败")


+ 27
- 24
app/controllers/journals_controller.rb View File

@@ -1,6 +1,6 @@
class JournalsController < ApplicationController
before_action :require_login, except: [:index, :get_children_journals]
before_action :require_profile_completed, only: [:create]
before_action :require_profile_completed, :find_atme_receivers, only: [:create]
before_action :set_issue
before_action :check_issue_permission
before_action :set_journal, only: [:destroy, :edit, :update]
@@ -22,32 +22,35 @@ class JournalsController < ApplicationController
if notes.blank?
normal_status(-1, "评论内容不能为空")
else
journal_params = {
journalized_id: @issue.id ,
journalized_type: "Issue",
user_id: current_user.id ,
notes: notes.to_s.strip,
parent_id: params[:parent_id]
}
journal = Journal.new journal_params
if journal.save
if params[:attachment_ids].present?
params[:attachment_ids].each do |id|
attachment = Attachment.select(:id, :container_id, :container_type)&.find_by_id(id)
unless attachment.blank?
attachment.container = journal
attachment.author_id = current_user.id
attachment.description = ""
attachment.save
ActiveRecord::Base.transaction do
journal_params = {
journalized_id: @issue.id ,
journalized_type: "Issue",
user_id: current_user.id ,
notes: notes.to_s.strip,
parent_id: params[:parent_id]
}
journal = Journal.new journal_params
if journal.save
if params[:attachment_ids].present?
params[:attachment_ids].each do |id|
attachment = Attachment.select(:id, :container_id, :container_type)&.find_by_id(id)
unless attachment.blank?
attachment.container = journal
attachment.author_id = current_user.id
attachment.description = ""
attachment.save
end
end
end
Rails.logger.info "[ATME] maybe to at such users: #{@atme_receivers.pluck(:login)}"
AtmeService.call(current_user, @atme_receivers, journal) if @atme_receivers.size > 0
# @issue.project_trends.create(user_id: current_user.id, project_id: @project.id, action_type: "journal")
render :json => { status: 0, message: "评论成功", id: journal.id}
# normal_status(0, "评论成功")
else
normal_status(-1, "评论失败")
end

# @issue.project_trends.create(user_id: current_user.id, project_id: @project.id, action_type: "journal")
render :json => { status: 0, message: "评论成功", id: journal.id}
# normal_status(0, "评论成功")
else
normal_status(-1, "评论失败")
end
end
end


+ 7
- 3
app/controllers/organizations/organizations_controller.rb View File

@@ -22,11 +22,12 @@ class Organizations::OrganizationsController < Organizations::BaseController
@can_create_project = @organization.can_create_project?(current_user.id)
@is_admin = can_edit_org?
@is_member = @organization.is_member?(current_user.id)
Cache::V2::OwnerCommonService.new(@organization.id).read
end

def create
ActiveRecord::Base.transaction do
tip_exception("无法使用以下关键词:#{organization_params[:name]},请重新命名") if ReversedKeyword.is_reversed(organization_params[:name]).present?
tip_exception("无法使用以下关键词:#{organization_params[:name]},请重新命名") if ReversedKeyword.check_exists?(organization_params[:name])
Organizations::CreateForm.new(organization_params).validate!
@organization = Organizations::CreateService.call(current_user, organization_params)
Util.write_file(@image, avatar_path(@organization)) if params[:image].present?
@@ -68,8 +69,7 @@ class Organizations::OrganizationsController < Organizations::BaseController
def recommend
recommend = %W(xuos Huawei_Technology openatom_foundation pkecosystem TensorLayer)
@organizations = Organization.with_visibility(%w(common))
.where(login: recommend).select(:id, :login, :firstname, :lastname, :nickname)
@organizations = Organization.includes(:organization_extension).where(organization_extensions: {recommend: true}).to_a.each_slice(group_size).to_a
end

private
@@ -80,6 +80,10 @@ class Organizations::OrganizationsController < Organizations::BaseController
:max_repo_creation, :nickname)
end

def group_size
params.fetch(:group_size, 4).to_i
end

def password
params.fetch(:password, "")
end


+ 4
- 0
app/controllers/project_categories_controller.rb View File

@@ -5,6 +5,10 @@ class ProjectCategoriesController < ApplicationController
@project_categories = q.result(distinct: true)
end

def pinned_index
@project_categories = ProjectCategory.where.not(pinned_index: 0).order(pinned_index: :desc)
end

def group_list
@project_categories = ProjectCategory.where('projects_count > 0').order(projects_count: :desc)
# projects = Project.no_anomory_projects.visible


+ 26
- 0
app/controllers/project_rank_controller.rb View File

@@ -0,0 +1,26 @@
class ProjectRankController < ApplicationController
# 根据时间获取热门项目
def index
$redis_cache.zunionstore("recent-days-project-rank", get_timeable_key_names)
deleted_data = $redis_cache.smembers("v2-project-rank-deleted")
$redis_cache.zrem("recent-days-project-rank", deleted_data) unless deleted_data.blank?
@project_rank = $redis_cache.zrevrange("recent-days-project-rank", 0, 4, withscores: true)
rescue Exception => e
@project_rank = []
end

private
# 默认显示7天的
def time
params.fetch(:time, 7).to_i
end

def get_timeable_key_names
names_array = []
(0...time).to_a.each do |i|
date_time_string = (Date.today - i.days).to_s
names_array << "v2-project-rank-#{date_time_string}"
end
names_array
end
end

+ 6
- 0
app/controllers/projects/members_controller.rb View File

@@ -0,0 +1,6 @@
class Projects::MembersController < Projects::BaseController
def index
users = @project.all_collaborators.like(params[:search]).includes(:user_extension)
@users = kaminari_paginate(users)
end
end

+ 10
- 4
app/controllers/projects_controller.rb View File

@@ -4,9 +4,9 @@ class ProjectsController < ApplicationController
include ProjectsHelper
include Acceleratorable

before_action :require_login, except: %i[index branches branches_slice group_type_list simple show fork_users praise_users watch_users recommend about menu_list]
before_action :require_login, except: %i[index branches branches_slice group_type_list simple show fork_users praise_users watch_users recommend banner_recommend about menu_list]
before_action :require_profile_completed, only: [:create, :migrate]
before_action :load_repository, except: %i[index group_type_list migrate create recommend]
before_action :load_repository, except: %i[index group_type_list migrate create recommend banner_recommend]
before_action :authorizate_user_can_edit_project!, only: %i[update]
before_action :project_public?, only: %i[fork_users praise_users watch_users]

@@ -30,8 +30,8 @@ class ProjectsController < ApplicationController
def index
scope = current_user.logged? ? Projects::ListQuery.call(params, current_user.id) : Projects::ListQuery.call(params)

# @projects = kaminari_paginate(scope)
@projects = paginate scope.includes(:project_category, :project_language, :repository, :project_educoder, :owner, :project_units)
@projects = kaminari_paginate(scope.includes(:project_category, :project_language, :repository, :project_educoder, :owner, :project_units))
# @projects = paginate scope.includes(:project_category, :project_language, :repository, :project_educoder, :owner, :project_units)

category_id = params[:category_id]
@total_count =
@@ -190,6 +190,8 @@ class ProjectsController < ApplicationController
end

def simple
# 为了缓存活跃项目的基本信息,后续删除
Cache::V2::ProjectCommonService.new(@project.id).read
json_response(@project, current_user)
end

@@ -197,6 +199,10 @@ class ProjectsController < ApplicationController
@projects = Project.recommend.includes(:repository, :project_category, :owner).order(visits: :desc)
end

def banner_recommend
@projects = Project.recommend.where.not(recommend_index: 0).includes(:project_category, :owner, :project_language).order(recommend_index: :desc)
end

def about
@project_detail = @project.project_detail
@attachments = Array(@project_detail&.attachments) if request.get?


+ 5
- 0
app/controllers/pull_requests_controller.rb View File

@@ -5,6 +5,7 @@ class PullRequestsController < ApplicationController
before_action :check_menu_authorize
before_action :find_pull_request, except: [:index, :new, :create, :check_can_merge,:get_branches,:create_merge_infos, :files, :commits]
before_action :load_pull_request, only: [:files, :commits]
before_action :find_atme_receivers, only: [:create, :update]
include TagChosenHelper
include ApplicationHelper

@@ -61,6 +62,8 @@ class PullRequestsController < ApplicationController
@pull_request.bind_gitea_pull_request!(@gitea_pull_request[:body]["number"], @gitea_pull_request[:body]["id"])
SendTemplateMessageJob.perform_later('PullRequestAssigned', current_user.id, @pull_request&.id) if Site.has_notice_menu?
SendTemplateMessageJob.perform_later('ProjectPullRequest', current_user.id, @pull_request&.id) if Site.has_notice_menu?
Rails.logger.info "[ATME] maybe to at such users: #{@atme_receivers.pluck(:login)}"
AtmeService.call(current_user, @atme_receivers, @pull_request) if @atme_receivers.size > 0
else
render_error("create pull request error: #{@gitea_pull_request[:status]}")
raise ActiveRecord::Rollback
@@ -106,6 +109,8 @@ class PullRequestsController < ApplicationController
if params[:status_id].to_i == 5
@issue.issue_times.update_all(end_time: Time.now)
end
Rails.logger.info "[ATME] maybe to at such users: #{@atme_receivers.pluck(:login)}"
AtmeService.call(current_user, @atme_receivers, @pull_request) if @atme_receivers.size > 0
normal_status(0, "PullRequest更新成功")
else
normal_status(-1, "PullRequest更新失败")


+ 1
- 1
app/controllers/repositories_controller.rb View File

@@ -48,7 +48,7 @@ class RepositoriesController < ApplicationController
def entries
@project.increment!(:visits)
CacheAsyncSetJob.perform_later("project_common_service", {visits: 1}, @project.id)
if @project.educoder?
@entries = Educoder::Repository::Entries::ListService.call(@project&.project_educoder.repo_name)
else


+ 9
- 0
app/controllers/topics_controller.rb View File

@@ -0,0 +1,9 @@
class TopicsController < ApplicationController

def index
return render_not_found("请输入正确的数据类型") unless params[:topic_type].present?
scope = Topic.with_single_type(params[:topic_type])
@topics = kaminari_paginate(scope)
end

end

+ 24
- 0
app/controllers/user_rank_controller.rb View File

@@ -0,0 +1,24 @@
class UserRankController < ApplicationController
# 根据时间获取热门开发者
def index
$redis_cache.zunionstore("recent-days-user-rank", get_timeable_key_names)
@user_rank = $redis_cache.zrevrange("recent-days-user-rank", 0, 3, withscores: true)
rescue Exception => e
@user_rank = []
end

private
# 默认显示7天的
def time
params.fetch(:time, 7).to_i
end

def get_timeable_key_names
names_array = []
(0...time).to_a.each do |i|
date_time_string = (Date.today - i.days).to_s
names_array << "v2-user-rank-#{date_time_string}"
end
names_array
end
end

+ 18
- 16
app/controllers/users/statistics_controller.rb View File

@@ -188,30 +188,32 @@ class Users::StatisticsController < Users::BaseController
@project_languages_count = time_filter(Project.where(user_id: observed_user.id), 'created_on').joins(:project_language).group("project_languages.name").count
@platform_project_languages_count = time_filter(Project, 'created_on').joins(:project_language).group("project_languages.name").count
else
@platform_result = Cache::V2::PlatformStatisticService.new.read
@user_result = Cache::V2::UserStatisticService.new(observed_user.id).read
# 用户被follow数量
@follow_count = Cache::UserFollowCountService.call(observed_user)
@platform_follow_count = Cache::PlatformFollowCountService.call
@follow_count = @user_result["follow-count"].to_i
@platform_follow_count = @platform_result["follow-count"].to_i
# 用户pr数量
@pullrequest_count = Cache::UserPullrequestCountService.call(observed_user)
@platform_pullrequest_count = Cache::PlatformPullrequestCountService.call
@pullrequest_count = @user_result["pullrequest-count"].to_i
@platform_pullrequest_count = @platform_result["pullrequest-count"].to_i
# 用户issue数量
@issues_count = Cache::UserIssueCountService.call(observed_user)
@platform_issues_count = Cache::PlatformIssueCountService.call
@issues_count = @user_result["issue-count"].to_i
@platform_issues_count = @platform_result["issue-count"].to_i
# 用户总项目数
@project_count = Cache::UserProjectCountService.call(observed_user)
@platform_project_count = Cache::PlatformProjectCountService.call
@project_count = @user_result["project-count"].to_i
@platform_project_count = @platform_result["project-count"].to_i
# 用户项目被fork数量
@fork_count = Cache::UserProjectForkCountService.call(observed_user)
@platform_fork_count = Cache::PlatformProjectForkCountService.call
@fork_count = @user_result["fork-count"].to_i
@platform_fork_count = @platform_result["fork-count"].to_i
# 用户项目关注数
@project_watchers_count = Cache::UserProjectWatchersCountService.call(observed_user)
@platform_project_watchers_count = Cache::PlatformProjectWatchersCountService.call
@project_watchers_count = @user_result["project-watcher-count"].to_i
@platform_project_watchers_count = @platform_result["project-watcher-count"].to_i
# 用户项目点赞数
@project_praises_count = Cache::UserProjectPraisesCountService.call(observed_user)
@platform_project_praises_count = Cache::PlatformProjectPraisesCountService.call
@project_praises_count = @user_result["project-praise-count"].to_i
@platform_project_praises_count = @platform_result["project-praise-count"].to_i
# 用户不同语言项目数量
@project_languages_count = Cache::UserProjectLanguagesCountService.call(observed_user)
@platform_project_languages_count = Cache::PlatformProjectLanguagesCountService.call
@project_languages_count = JSON.parse(@user_result["project-language"])
@platform_project_languages_count = JSON.parse(@platform_result["project-language"])
end
end
end

+ 15
- 0
app/controllers/users/system_notification_histories_controller.rb View File

@@ -0,0 +1,15 @@
class Users::SystemNotificationHistoriesController < Users::BaseController
before_action :private_user_resources!, only: [:create]
def create
@history = observed_user.system_notification_histories.new(system_notification_id: params[:system_notification_id])
if @history.save
render_ok
else
Rails.logger.info @history.errors.as_json
render_error(@history.errors.full_messages.join(","))
end
rescue Exception => e
uid_logger_error(e.message)
tip_exception(e.message)
end
end

+ 2
- 0
app/controllers/users_controller.rb View File

@@ -51,6 +51,8 @@ class UsersController < ApplicationController
@projects_common_count = user_projects.common.size
@projects_mirrior_count = user_projects.mirror.size
@projects_sync_mirrior_count = user_projects.sync_mirror.size
# 为了缓存活跃用户的基本信息,后续删除
Cache::V2::OwnerCommonService.new(@user.id).read
end
def watch_users


+ 0
- 5
app/decorators/course_decorator.rb View File

@@ -1,5 +0,0 @@
module CourseDecorator
def can_visited?
is_public == 1 || User.current.admin_or_business? || User.current.member_of_course?(self)
end
end

+ 0
- 2
app/decorators/ec_course_target_decorator.rb View File

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

+ 0
- 16
app/decorators/experience_decorator.rb View File

@@ -1,16 +0,0 @@
module ExperienceDecorator
def container_type_text
I18n.t("experience.container_type.#{container_type.to_s.underscore}")
end

def content
case container_type.to_s.underscore
when 'game' then
game = Game.find_by(id: container_id)
game.present? ? "通过实训“#{game.challenge.shixun.name}”的第#{game.challenge.position}关获得的奖励" : ''
when 'shixun_publish' then
shixun = Shixun.find_by(id: container_id)
shixun.present? ? "发布实训“#{shixun.name}”获得的奖励" : ''
end
end
end

+ 0
- 39
app/decorators/grade_decorator.rb View File

@@ -1,39 +0,0 @@
module GradeDecorator
def container_type_text
I18n.t("grade.container_type.#{container_type.to_s.underscore}")
end

def content
case container_type.to_s.underscore
when 'avatar' then '用户首次上传头像获得的奖励'
when 'phone' then '用户首次绑定手机号码获得的奖励'
when 'mail' then '用户首次绑定邮箱获得的奖励'
when 'attendance' then '用户每天签到获得的奖励'
when 'account' then '新用户首次填写基本资料获得的奖励'
when 'memo' then '发布的评论或者帖子获得平台奖励'
when 'discusses' then '发布的评论获得平台奖励'
when 'star' then '用户给实训评分获得的随机奖励'
when 'feedback' then '反馈的问题获得平台奖励'
when 'authentication' then '用户首次完成实名认证获得的奖励'
when 'professional' then '用户首次完成职业认证获得的奖励'
when 'answer' then
game = Game.find_by(id: container_id)
game.present? ? "查看实训“#{game.challenge.shixun.name}”第#{game.challenge.position}关的参考答案消耗的金币" : ''
when 'game' then
game = Game.find_by(id: container_id)
game.present? ? "通过实训“#{game.challenge.shixun.name}”的第#{game.challenge.position}关获得的奖励" : ''
when 'test_set' then
game = Game.find_by(id: container_id)
game.present? ? "查看实训“#{game.challenge.shixun.name}”的第#{game.challenge.position}关的隐藏测试集消耗的金币" : ''
when 'shixun_publish' then
shixun = Shixun.find_by(id: container_id)
shixun.present? ? "发布实训“#{shixun.name}”获得的奖励" : ''
when 'check_ta_answer' then
game = Game.find_by(id: container_id)
game.present? ? "查看实训“#{game.challenge.shixun.name}”第#{game.challenge.position}关的TA人解答消耗的金币" : ''
when 'hack' then
hack = Hack.find_by(id: container_id)
hack.present? ? "完成了题目解答“#{hack.name}”,获得金币奖励:#{hack.score}" : ''
end
end
end

+ 0
- 5
app/decorators/library_decorator.rb View File

@@ -1,5 +0,0 @@
module LibraryDecorator
extend ApplicationDecorator

display_time_method :published_at, :created_at, :updated_at
end

+ 0
- 5
app/decorators/shixun_decorator.rb View File

@@ -1,5 +0,0 @@
module ShixunDecorator
def human_status
I18n.t("shixun.status.#{status}")
end
end

+ 0
- 5
app/decorators/subject_decorator.rb View File

@@ -1,5 +0,0 @@
module SubjectDecorator
def can_visited?
published? || User.current.admin? || member?(User.current)
end
end

+ 0
- 5
app/decorators/video_decorator.rb View File

@@ -1,5 +0,0 @@
module VideoDecorator
extend ApplicationDecorator

display_time_method :published_at, :created_at, :updated_at
end

public/docs/images/logo-b38b63e6.png → app/docs/slate/source/images/trustie_logo.png View File


+ 30
- 0
app/docs/slate/source/includes/_users.md View File

@@ -199,6 +199,36 @@ await octokit.request('GET /api/users/:login/messages.json')
Success Data.
</aside>

## 用户阅读系统通知
用户阅读系统通知

> 示例:

```shell
curl -X POST http://localhost:3000/api/users/yystopf/system_notification_histories.json
```

```javascript
await octokit.request('GET /api/users/:login/system_notification_histories.json')
```

### HTTP 请求
`POST /api/users/:login/system_notification_histories.json`

### 请求字段说明:
参数 | 类型 | 字段说明
--------- | ----------- | -----------
|system_notification_id |integer |阅读的系统通知id |

> 返回的JSON示例:

```json
{
"status": 0,
"message": "success"
}
```

## 发送消息
发送消息, 目前只支持atme



+ 41
- 0
app/forms/accounts/reset_password_form.rb View File

@@ -0,0 +1,41 @@
module Accounts
class ResetPasswordForm < ::BaseForm
# login 邮箱、手机号
# code 验证码
# type: 1:手机号注册;2:邮箱注册
attr_accessor :login, :password, :password_confirmation, :code

validates :login, :code, :password, :password_confirmation, presence: true, allow_blank: false
validate :check!
def check!
Rails.logger.info "ResetPasswordForm params: code: #{code} login: #{login}
password: #{password} password_confirmation: #{password_confirmation}"
type = phone_mail_type(login)
db_verifi_code =
if type == 1
check_phone_format(login)
VerificationCode.where(phone: login, code: code, code_type: 2).last
elsif type == 0
check_email_format(login)
VerificationCode.where(email: login, code: code, code_type: 3).last
end
check_password(password)
check_password_confirmation(password, password_confirmation)
check_verifi_code(db_verifi_code, code)
end

def check_phone_format(phone)
phone = strip(phone)
raise LoginError, "登录名格式有误" unless phone =~ CustomRegexp::LOGIN
end

def check_email_format(mail)
mail = strip(mail)
raise EmailError, "邮件格式有误" unless mail =~ CustomRegexp::EMAIL
end
end
end

+ 0
- 10
app/forms/add_school_apply_form.rb View File

@@ -1,10 +0,0 @@
class AddSchoolApplyForm
include ActiveModel::Model

attr_accessor :name, :province, :city, :address, :remarks

validates :name, presence: true
# validates :province, presence: true
# validates :city, presence: true
# validates :address, presence: true
end

+ 0
- 27
app/forms/apply_shixun_mirror_form.rb View File

@@ -1,27 +0,0 @@
class ApplyShixunMirrorForm
include ActiveModel::Model
attr_accessor :language, :runtime, :run_method, :attachment_id
validates :language, presence: true
validates :runtime, presence: true
validates :run_method, presence: true
validates :attachment_id, presence: true, numericality: { only_integer: true }
validate :ensure_attachment_presence
def ensure_attachment_presence
return unless attachment_id
if attachment.blank?
errors.add(:attachment_id, :attachment_not_exist)
end
end
def attachment
@attachment ||= Attachment.find_by_id(attachment_id)
end
def to_json
{ language: language, runtime: runtime, run_method: run_method, attachment_id: attachment_id }.to_json
end
end

+ 41
- 2
app/forms/base_form.rb View File

@@ -1,6 +1,14 @@
class BaseForm
include ActiveModel::Model

Error = Class.new(StandardError)
EmailError = Class.new(Error)
LoginError = Class.new(Error)
PhoneError = Class.new(Error)
PasswordFormatError = Class.new(Error)
VerifiCodeError = Class.new(Error)
PasswordConfirmationError = Class.new(Error)

def check_project_category(project_category_id)
unless project_category_id == ''
raise "project_category_id参数值无效." if project_category_id && !ProjectCategory.exists?(project_category_id)
@@ -23,7 +31,38 @@ class BaseForm
end

def check_reversed_keyword(repository_name)
raise "项目标识已被占用." if ReversedKeyword.is_reversed(repository_name).exists?
raise "项目标识已被占用." if ReversedKeyword.check_exists?(repository_name)
end

def check_password(password)
password = strip(password)
raise PasswordFormatError, "密码8~16位密码,支持字母数字和符号" unless password =~ CustomRegexp::PASSWORD
end

def check_password_confirmation(password, password_confirmation)
password = strip(password)
password_confirmation = strip(password_confirmation)

raise PasswordFormatError, "确认密码为8~16位密码,支持字母数字和符号" unless password_confirmation =~ CustomRegexp::PASSWORD
raise PasswordConfirmationError, "两次输入的密码不一致" unless password == password_confirmation
end

def check_verifi_code(verifi_code, code)
code = strip(code)
# return if code == "123123" # TODO 万能验证码,用于测试

raise VerifiCodeError, "验证码不正确" if verifi_code&.code != code
raise VerifiCodeError, "验证码已失效" if !verifi_code&.effective?
end

private
def strip(str)
str.to_s.strip.presence
end

# 1 手机类型;0 邮箱类型
# 注意新版的login是自动名生成的
def phone_mail_type value
value =~ /^1\d{10}$/ ? 1 : 0
end
end

+ 0
- 15
app/forms/examination_banks/save_exam_form.rb View File

@@ -1,15 +0,0 @@
class ExaminationBanks::SaveExamForm
include ActiveModel::Model

attr_accessor :discipline_id, :sub_discipline_id, :difficulty, :name, :duration, :tag_discipline_id

validates :discipline_id, presence: true
validates :sub_discipline_id, presence: true
validates :difficulty, presence: true, inclusion: {in: 1..3}, numericality: { only_integer: true }
validates :name, presence: true, length: { maximum: 60, too_long: "不能超过60个字符" }
validate :validate_duration

def validate_duration
raise '时长应为大于0的整数' if duration.present? && duration.to_i < 1
end
end

+ 0
- 12
app/forms/examination_intelligent_settings/save_exam_form.rb View File

@@ -1,12 +0,0 @@
class ExaminationIntelligentSettings::SaveExamForm
include ActiveModel::Model

attr_accessor :name, :duration

validates :name, presence: true, length: { maximum: 60 }
validate :validate_duration

def validate_duration
raise '时长应为大于0的整数' if duration.present? && duration.to_i < 1
end
end

+ 0
- 11
app/forms/examination_intelligent_settings/save_exam_setting_form.rb View File

@@ -1,11 +0,0 @@
class ExaminationIntelligentSettings::SaveExamSettingForm
include ActiveModel::Model

attr_accessor :discipline_id, :sub_discipline_id, :source, :difficulty, :tag_discipline_id, :question_settings

validates :discipline_id, presence: true
validates :sub_discipline_id, presence: true
validates :source, presence: true
validates :difficulty, presence: true, inclusion: {in: 1..3}, numericality: { only_integer: true }
validates :question_settings, presence: true
end

+ 3
- 2
app/forms/projects/update_form.rb View File

@@ -3,11 +3,12 @@ class Projects::UpdateForm < BaseForm
validates :name, presence: true
validates :name, length: { maximum: 50 }
validates :description, length: { maximum: 200 }
validates :identifier, format: { with: CustomRegexp::REPOSITORY_NAME_REGEX, multiline: true, message: "只能含有数字、字母、下划线且不能以下划线开头和结尾" }

validate do
check_project_category(project_category_id)
check_project_language(project_language_id)
Rails.logger.info project_identifier
Rails.logger.info identifier

check_repository_name(user_id, identifier) unless identifier.blank? || identifier == project_identifier
end



+ 30
- 0
app/forms/register/base_form.rb View File

@@ -0,0 +1,30 @@
module Register
class BaseForm < ::BaseForm
include ActiveModel::Model
private
def check_login(login)
login = strip(login)
raise LoginError, "登录名格式有误" unless login =~ CustomRegexp::LOGIN

login_exist = Owner.exists?(login: login) || ReversedKeyword.check_exists?(login)
raise LoginError, '登录名已被使用' if login_exist
end

def check_mail(mail)
mail = strip(mail)
raise EmailError, "邮件格式有误" unless mail =~ CustomRegexp::EMAIL
mail_exist = Owner.exists?(mail: mail)
raise EmailError, '邮箱已被使用' if mail_exist
end
def check_phone(phone)
phone = strip(phone)
raise PhoneError, "手机号格式有误" unless phone =~ CustomRegexp::PHONE

phone_exist = Owner.exists?(phone: phone)
raise PhoneError, '手机号已被使用' if phone_exist
end
end
end

+ 19
- 0
app/forms/register/check_columns_form.rb View File

@@ -0,0 +1,19 @@
module Register
class CheckColumnsForm < Register::BaseForm
attr_accessor :type, :value

validates :type, presence: true, numericality: true
validates :value, presence: true
validate :check!
def check!
# params[:type] 为事件类型 1:登录名(login) 2:email(邮箱) 3:phone(手机号)
case strip(type).to_i
when 1 then check_login(strip(value))
when 2 then check_mail(strip(value))
when 3 then check_phone(strip(value))
else raise("type值无效")
end
end
end
end

+ 31
- 0
app/forms/register/form.rb View File

@@ -0,0 +1,31 @@
module Register
class Form < Register::BaseForm
# login 登陆方式,支持邮箱、登陆、手机号等
# namespace 用户空间地址
# type: 1:手机号注册;2:邮箱注册
attr_accessor :login, :namespace, :password, :password_confirmation, :code, :type

validates :login, :code, :password, :password_confirmation, :namespace, presence: true, allow_blank: false
validate :check!
def check!
Rails.logger.info "Register::Form params: code: #{code}; login: #{login};
namespace: #{namespace}; password: #{password}; password_confirmation: #{password_confirmation}"
type = phone_mail_type(strip(login))
db_verifi_code =
if type == 1
check_phone(login)
VerificationCode.where(phone: login, code: code, code_type: 1).last
elsif type == 0
check_mail(login)
VerificationCode.where(email: login, code: code, code_type: 8).last
end
check_login(namespace)
check_verifi_code(db_verifi_code, code)
check_password(password)
check_password_confirmation(password, password_confirmation)
end
end
end

+ 0
- 20
app/forms/weapps/create_course_form.rb View File

@@ -1,20 +0,0 @@
class Weapps::CreateCourseForm
include ActiveModel::Model

attr_accessor :course
attr_accessor :name, :course_list_name, :credit, :course_module_types, :end_date

validates :name, presence: true
validates :course_list_name, presence: true

validate :course_name_prefix
validate :check_course_modules

def course_name_prefix
raise '课堂名称应以课程名称开头' unless name.index(course_list_name) && name.index(course_list_name) == 0
end

def check_course_modules
raise '请至少添加一个课堂模块' if course_module_types.blank?
end
end

+ 0
- 15
app/forms/weapps/update_course_form.rb View File

@@ -1,15 +0,0 @@
class Weapps::UpdateCourseForm
include ActiveModel::Model

attr_accessor :course
attr_accessor :name, :course_list_name, :credit, :end_date

validates :name, presence: true
validates :course_list_name, presence: true

validate :course_name_prefix

def course_name_prefix
raise '课堂名称应以课程名称开头' unless name.index(course_list_name) && name.index(course_list_name) == 0
end
end

+ 1
- 1
app/helpers/admins/projects_helper.rb View File

@@ -4,7 +4,7 @@ module Admins::ProjectsHelper
owner = project.owner

if owner.is_a?(User)
link_to(project.owner&.real_name, "/users/#{project&.owner&.login}", target: '_blank')
link_to(project.owner&.real_name, "/#{project&.owner&.login}", target: '_blank')
elsif owner.is_a?(Organization)
link_to(project.owner&.real_name, "/organize/#{project&.owner&.login}", target: '_blank')
else


+ 19
- 1
app/helpers/application_helper.rb View File

@@ -1,6 +1,6 @@
# 所有的方法请按首字母的顺序依次列出
module ApplicationHelper
include Educoder::I18n
include Gitlink::I18n
include GitHelper
ONE_MINUTE = 60 * 1000
@@ -442,6 +442,14 @@ module ApplicationHelper
User.find_by(gitea_uid: gitea_uid)
end
def find_user_in_redis_cache(login, email)
$redis_cache.hgetall("v2-owner-common:#{login}-#{email}")
end
def find_user_in_redis_cache_by_id(id)
$redis_cache.hgetall("v2-owner-common:#{id}")
end
def render_base64_decoded(str)
return nil if str.blank?
Base64.decode64 str
@@ -455,5 +463,15 @@ module ApplicationHelper
sidebar_item(url, "数据统计", icon: 'bar-chart', controller: 'root')
end
end
# 1 手机类型;0 邮箱类型
# 注意新版的login是自动名生成的
def phone_mail_type value
value =~ /^1\d{10}$/ ? 1 : 0
end
def strip(str)
str.to_s.strip.presence
end
end

+ 26
- 0
app/helpers/avatar_helper.rb View File

@@ -0,0 +1,26 @@
module AvatarHelper
def relative_path
"avatars"
end

def storage_path
File.join(Rails.root, "public", "images", relative_path)
end

def disk_filename(source_type,source_id,image_file=nil)
File.join(storage_path, "#{source_type}", "#{source_id}")
end

def url_to_avatar(source)
if File.exist?(disk_filename(source&.class, source&.id))
ctime = File.ctime(disk_filename(source.class, source.id)).to_i
if %w(User Organization).include?(source.class.to_s)
File.join("images", relative_path, ["#{source.class}", "#{source.id}"]) + "?t=#{ctime}"
else
File.join("images/avatars", ["#{source.class}", "#{source.id}"]) + "?t=#{ctime}"
end
elsif source.class.to_s == 'User'
source.get_letter_avatar_url
end
end
end

+ 0
- 2
app/helpers/boards_helper.rb View File

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

+ 0
- 10
app/helpers/challenges_helper.rb View File

@@ -1,10 +0,0 @@
module ChallengesHelper
def match_begin_symbol str
str.gsub(/\A\r/, "\r\r")
end
end

+ 0
- 2
app/helpers/course_groups_helper.rb View File

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

+ 0
- 2
app/helpers/course_modules_helper.rb View File

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

+ 0
- 2
app/helpers/course_second_categories_helper.rb View File

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

+ 0
- 2
app/helpers/course_stages_helper.rb View File

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

+ 0
- 298
app/helpers/courses_helper.rb View File

@@ -1,298 +0,0 @@
module CoursesHelper
def member_manager group, teachers
str = ""
members = teachers.select{|teacher| teacher.teacher_course_groups.pluck(:course_group_id).include?(group.id) || teacher.teacher_course_groups.size == 0}
str = members.uniq.size == teachers.size ? "全部教师" : members.map{|member| member.user.real_name}.join("、")
str
# teachers.each do |member|
# if member.teacher_course_groups.exists?(course_group_id: group.id) || member.teacher_course_groups.size == 0
# str << member.user.real_name
# end
# end
end
def edit_auth group, teachers
User.current.admin_or_business? ||
teachers.select{|teacher| teacher.user_id == User.current.id &&
(teacher.teacher_course_groups.pluck(:course_group_id).include?(group.id) || teacher.teacher_course_groups.size == 0)}.size > 0
end
# 是否有切换为学生的入口
def switch_student_role is_teacher, course, user
is_teacher && course.course_members.where(user_id: user.id, role: %i(STUDENT)).exists?
end
# 是否有切换为教师的入口
def switch_teacher_role is_student, course, user
is_student && course.course_members.where(user_id: user.id, role: %i(CREATOR PROFESSOR)).exists?
end
# 是否有切换为助教的入口
def switch_assistant_role is_student, course, user
is_student && course.course_members.where(user_id: user.id, role: %i(ASSISTANT_PROFESSOR)).exists?
end
# 课堂结束天数
def course_end_date end_date
if end_date.present?
curr = Time.new
date = ((Date.parse(end_date.to_s) - Date.parse(curr.to_s)).to_i)
date > 0 ? "#{date}天后" : ""
end
end
# 课堂模块的url
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"
"/courses/#{course.id}/common_homeworks/#{mod.id}"
when "group_homework"
"/courses/#{course.id}/group_homeworks/#{mod.id}"
when "graduation"
"/courses/#{course.id}/graduation_topics/#{mod.id}"
when "exercise"
"/courses/#{course.id}/exercises/#{mod.id}"
when "poll"
"/courses/#{course.id}/polls/#{mod.id}"
when "attachment"
"/courses/#{course.id}/files/#{mod.id}"
when "board"
course_board = course.course_board
"/courses/#{course.id}/boards/#{course_board.id}"
when "course_group"
"/courses/#{course.id}/course_groups"
when "statistics"
"/courses/#{course.id}/statistics"
when "video"
"/courses/#{course.id}/course_videos"
end
end
# 子目录对应的url
def category_url category, course
case category.category_type
when "shixun_homework"
"/courses/#{course.id}/shixun_homework/#{category.id}"
when "graduation"
if category.name == "毕设选题"
"/courses/#{course.id}/graduation_topics/#{category.course_module_id}"
else
"/courses/#{course.id}/graduation_tasks/#{category.course_module_id}"
end
when "attachment"
"/courses/#{course.id}/file/#{category.id}"
end
end
# 子目录下的任务数
def category_task_count course, category, user
case category.category_type
when "shixun_homework"
get_homework_commons_count(course, 4, category.id)
when "graduation"
if category.name == "毕设选题"
course.graduation_topics_count
else
course.graduation_tasks_count
end
when "attachment"
get_attachment_count(course, category.id)
end
end
# 课堂模块的任务数
def course_task_count(course, module_type)
case module_type
when "shixun_homework"
get_homework_commons_count(course, 4, 0)
when "common_homework"
get_homework_commons_count(course, 1, 0)
when "group_homework"
get_homework_commons_count(course, 3, 0)
when "graduation"
0
when "exercise"
course.exercises_count
when "poll"
course.polls_count
when "attachment"
get_attachment_count(course, 0)
when "board"
course_board = course.course_board
course_board.present? ? course_board.messages.size : 0
when "course_group"
course.course_groups_count
when "announcement"
course.informs.count
when "online_learning"
course.shixuns.count
when "video"
course.course_videos.count + course.live_links.count
end
end
# 当前用户可见的课堂作业,type指定作业类型, category_id指定二级目录
def visible_homework course, user, type, category_id=0
if user.teacher_of_course?(course)
homeworks = course.homework_commons.where("homework_type = #{type} and course_second_category_id = #{category_id}")
elsif user.member_of_course?(course)
member = course.course_members.find_by(user_id: user.id, role: 4)
if member.try(:course_group_id).to_i == 0
homeworks = course.homework_commons.where("homework_commons.homework_type = #{type} and publish_time <= '#{Time.now}'
and unified_setting = 1 and course_second_category_id = #{category_id}")
else
not_homework_ids = course.homework_group_settings.where("course_group_id = #{member.try(:course_group_id)} and
(publish_time > '#{Time.now}' or publish_time is null)").pluck(:homework_common_id)
# not_homework_ids = not_homework_ids.blank? ? "(-1)" : "(" + not_homework_ids.map(&:homework_common_id).join(",") + ")"
homeworks = course.homework_commons.where.not(id: not_homework_ids).where("homework_commons.homework_type = #{type} and publish_time <= '#{Time.now}'
and course_second_category_id = #{category_id}")
end
else
homeworks = course.homework_commons.where("homework_type = #{type} and publish_time <= '#{Time.now}' and unified_setting = 1
and course_second_category_id = #{category_id}")
end
homeworks
end
# 当前用户可见的课堂试卷
def visible_exercise course, user
if user.teacher_of_course?(course)
exercises = course.exercises
elsif user.member_of_course?(course)
member = course.course_members.find_by(user_id: user.id, role: 4)
if member.try(:course_group_id).to_i == 0
exercises = course.exercises.where("publish_time <= '#{Time.now}' and unified_setting = 1")
else
not_exercise_ids = course.exercise_group_settings.where("course_group_id = #{member.try(:course_group_id)} and
(publish_time > '#{Time.now}' or publish_time is null)").pluck(:exercise_id)
exercises = course.exercises.where.not(id: not_exercise_ids).where("publish_time <= '#{Time.now}'")
end
else
exercises = course.exercises.where("publish_time <= '#{Time.now}' and unified_setting = 1")
end
exercises
end
# 当前用户可见的课堂问卷
def visible_poll course, user
if user.teacher_of_course?(course)
polls = course.polls
elsif user.member_of_course?(course)
member = course.course_members.find_by(user_id: user.id, role: 4)
if member.try(:course_group_id).to_i == 0
polls = course.polls.where("publish_time <= '#{Time.now}' and unified_setting = 1")
else
not_poll_ids = course.poll_group_settings.where("course_group_id = #{member.try(:course_group_id)} and
(publish_time > '#{Time.now}' or publish_time is null)").pluck(:poll_id)
polls = course.polls.where.not(id: not_poll_ids).where("publish_time <= '#{Time.now}'")
end
else
polls = course.polls.where("publish_time <= '#{Time.now}' and unified_setting = 1")
end
polls
end
# 当前用户可见的课堂资源,category_id指定资源的目录
def visible_attachment course, user, category_id=0
result = []
course.attachments.where(course_second_category_id: category_id).each do |attachment|
if attachment.unified_setting
if attachment.is_public == 1 && attachment.is_publish == 1 || user == attachment.author || user.teacher_of_course?(course) || (user.member_of_course?(course) && attachment.is_publish == 1)
result << attachment
end
else
if attachment.is_public == 1 && attachment.is_publish == 1 && !user.member_of_course?(course) || user == attachment.author || user.teacher_of_course?(course)
result << attachment
elsif user.member_of_course?(course) && attachment.is_publish == 1
member = course.course_members.find_by(user_id: user.id, role: 4)
if member.try(:course_group_id).to_i == 0 && attachment.unified_setting
result << attachment
elsif attachment.attachment_group_settings.where("course_group_id = #{member.try(:course_group_id)} and publish_time > '#{Time.now}'").count == 0
result << attachment
end
end
end
end
result
end
# 获取课堂的资源数
def get_attachment_count(course, category_id)
category_id.to_i == 0 ? course.attachments.size : course.attachments.where(course_second_category_id: category_id).size
end
# 获取课堂的作业数
def get_homework_commons_count(course, type, category_id)
category_id == 0 ? HomeworkCommon.where(course_id: course.id, homework_type: type).size :
HomeworkCommon.where(course_id: course.id, homework_type: type, course_second_category_id: category_id).size
end
# 获取课堂的任务数(作业数+试卷数+问卷数)
def get_tasks_count(course)
course.homework_commons_count + course.exercises_count + course.polls_count
end
# 当前用户可见的毕设任务
def visible_graduation_task course, user
if user.teacher_of_course?(course)
tasks = course.graduation_tasks
else
tasks = course.graduation_tasks.where("publish_time <= '#{Time.now}'")
end
tasks
end
# 分班情况
def course_group_info course, user_id
course_group_ids = course.group_course_power(user_id)
course_groups =
if course_group_ids.present?
course.course_groups.where(id: course_group_ids).includes(:course_members)
else
course.course_groups.includes(:course_members)
end
group_info = []
if !course_groups.blank?
course_groups.each do |group|
group_info << {course_group_id: group.id, group_group_name: group.name, count: group.course_members_count}
end
none_group_count = course.students.where(course_group_id: 0).size
group_info << {course_group_id: 0, group_group_name: "未分班", count: none_group_count} if none_group_count > 0 && !course_group_ids.present?
end
return group_info
end
def left_group_info course
group_info = []
if course.course_groups_count > 0
none_group_count = course.students.where(course_group_id: 0).size
group_info << {category_id: 0, category_name: "未分班", position: course.course_groups.pluck(:position).max.to_i + 1,
category_count: none_group_count, category_type: false,
second_category_url: "/courses/#{@course.id}/course_groups/0"}
course.course_groups.each do |course_group|
group_info << {category_id: course_group.id, category_name: course_group.name, position: course_group.position,
category_count: course_group.course_members_count, category_type: false,
second_category_url: "/courses/#{@course.id}/course_groups/#{course_group.id}"}
end
end
group_info
end
def last_subject_shixun course, myshixuns
myshixun = myshixuns.sort{|x,y| y[:updated_at] <=> x[:updated_at] }.first
return "" unless myshixun
stage_shixun = course.course_stage_shixuns.where(shixun_id: myshixun.shixun_id).take
progress = stage_shixun&.course_stage&.position.to_s + "-" + stage_shixun&.position.to_s + " " + myshixun.shixun&.name
end
end

+ 0
- 2
app/helpers/discusses_helper.rb View File

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

+ 0
- 2
app/helpers/edu_datas_helper.rb View File

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

+ 0
- 150
app/helpers/graduation_tasks_helper.rb View File

@@ -1,150 +0,0 @@
module GraduationTasksHelper
include CoursesHelper
# 教师评阅
def teacher_comment task, user_id
[{ id: 0 ,name: "未评", count: task.uncomment_count(user_id)}, {id: 1, name: "已评", count: task.comment_count(user_id)}]
end
# 作品状态
def task_status task, user_id
[{id: 0, name: "未提交", count: task.unfinished_count(user_id)},
{id: 1, name: "按时提交", count: task.finished_count(user_id)},
{id: 2, name: "延时提交", count: task.delay_finished_count(user_id)}]
end
# 交叉评阅
def cross_comment task, user_id
if task.cross_comment && task.status >= 3
[{id: 1, name: "只看我的交叉评阅", count: task.graduation_work_comment_assignations.myself(user_id).count}]
else
[]
end
end
def task_curr_status task, course
result = {}
status = []
time = ""
if course.try(:is_end)
status << "已结束"
time = course.end_date.present? ? course.end_date.strftime("%Y-%m-%d") : ""
else
if task.status > 1 && task.allow_late && (task.late_time.nil? || task.late_time > Time.now)
status << "补交中"
end
case task.status
when 0
status << "未发布"
time = task.publish_time.present? ? "将于 #{format_time(task.publish_time)} 发布" : "创建于#{time_from_now(task.created_at)}"
when 1
if task.end_time && task.end_time >= Time.now
status << "提交中"
time = how_much_time(task.end_time)
end
when 2
status << "评阅中"
time = task.comment_time.present? ? how_much_time(task.comment_time) : course.end_date.present? ? how_much_time(course.end_date.end_of_day) : ""
when 3
status << "交叉评阅中"
time = course.end_date.present? ? how_much_time(course.end_date.end_of_day) : ""
end
status << "未开启补交" if (!task.allow_late && task.status != 0) #6.11 -hs 新增status不等于0
# 如果还在补交阶段则显示补交结束时间
if task.status > 1 && task.allow_late && task.late_time && task.late_time > Time.now
time = how_much_time(task.late_time)
end
end
result[:status] = status
result[:time] = time
result
end
# 作品数统计:type: 1 已提交 0 未提交
def grduationwork_count task, type
works = task.graduation_works
type == 1 ? works.select{|work| work.work_status != 0}.size : works.select{|work| work.work_status == 0}.size
end
# 普通/分组 作业作品状态数组
def graduation_work_status task, user_id, course
status = []
work = task.graduation_works.find_by(user_id: user_id)
work = work || GraduationWork.create(graduation_task_id: task.id, user_id: user_id)
late_time = task.late_time || course.end_date
if course.is_end && work && work.work_status > 0
status << "查看作品"
elsif !course.is_end
if task.publish_time && task.publish_time < Time.now
# 作业未截止时
if task.end_time > Time.now
if task.task_type == 2 && task.base_on_project
if work.project_id.nil? || work.project_id == 0
status << "创建项目"
status << "关联项目"
elsif work.work_status == 0
status << "取消关联"
status << "提交作品"
else
status << "修改作品"
end
else
if work.work_status == 0
status << "提交作品"
else
status << "修改作品"
end
end
# 补交阶段
elsif task.allow_late && (late_time.nil? || late_time > Time.now)
if task.task_type == 2 && task.base_on_project
if work.project_id.nil? || work.project_id == 0
status << "创建项目"
status << "关联项目"
elsif work.work_status == 0
status << "取消关联"
status << "补交作品"
else
status << "补交附件"
status << "查看作品"
end
else
if work.work_status == 0
status << "补交作品"
else
status << "补交附件"
status << "查看作品"
end
end
# 匿评阶段
elsif work.work_status != 0
status << "查看作品"
end
end
end
end
# 阶段剩余时间
def task_left_time task
if task.publish_time && task.publish_time < Time.now
if task.end_time > Time.now
status = "剩余提交时间"
time = "#{how_much_time(task.end_time)}"
else
if task.allow_late && task.late_time && task.late_time >= Time.now
status = "剩余补交时间"
time = "#{how_much_time(task.late_time)}"
end
end
end
{status: status, time: time}
end
end

+ 0
- 30
app/helpers/graduation_topics_helper.rb View File

@@ -1,30 +0,0 @@
module GraduationTopicsHelper
# 课题类型
def topic_type
[{id: 1, name: "设计"}, {id: 2, name: "论文"}, {id: 3, name: "创作"}]
end
# 课程来源
def topic_source
[{id: 1, name: "生产/社会实际"}, {id: 2, name:"结合科研"}, {id: 3, name: "其它"}]
end
# 课题性质1
def topic_property_first
[{id: 1, name: "真题"}, {id: 2, name:"模拟题"}]
end
# 课题性质2
def topic_property_second
[{id: 1, name: "纵向课题"}, {id: 2, name:"横向课题"}, {id: 3, name: "自选"}]
end
# 课题重复
def topic_repeat
[{id: 1, name: "新题"}, {id: 2, name:"往届题,有新要求"}, {id: 3, name: "往届题,无新要求"}]
end
end

+ 0
- 20
app/helpers/graduation_works_helper.rb View File

@@ -1,20 +0,0 @@
module GraduationWorksHelper
include GraduationTasksHelper
# 作品最终成绩
# 参数: work作品, current_user用户,course_identity用户在课堂的身份
def work_final_score work, current_user, course_identity
work_score =
if work.work_score.nil?
"--"
else
if work.check_score_power? current_user, course_identity
format("%.1f", work.work_score < 0 ? 0 : work.work_score.round(1))
else
"**"
end
end
# work_score 最终成绩; late_penalty 迟交扣分; final_score 最终评分
{username: work.user.full_name, login: work.user.login, work_score: work_score, final_score: work.final_score}
end
end

+ 0
- 2
app/helpers/hack_user_lastest_codes_helper.rb View File

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

+ 0
- 2
app/helpers/hacks_helper.rb View File

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

+ 10
- 0
app/helpers/repositories_helper.rb View File

@@ -35,6 +35,16 @@ module RepositoriesHelper
end
end
def render_cache_commit_author(author_json)
Rails.logger.info author_json['Email']
if author_json["name"].present? && author_json["email"].present?
return find_user_in_redis_cache(author_json['name'], author_json['email'])
end
if author_json["Name"].present? && author_json["Email"].present?
return find_user_in_redis_cache(author_json['Name'], author_json['Email'])
end
end
def readme_render_decode64_content(str, path)
return nil if str.blank?
begin


+ 0
- 2
app/helpers/trustie_hacks_helper.rb View File

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

+ 0
- 69
app/helpers/weapps/courses_helper.rb View File

@@ -1,69 +0,0 @@
module Weapps::CoursesHelper
require 'chinese_pinyin'

def teacher_list teachers, user_course_identity
data = []
teachers.each do |teacher|
if teacher.user.present?
teacher_user = teacher.user
name = teacher_user.real_name
role = teacher.role == "CREATOR" ? "管理员" : teacher.role == "PROFESSOR" ? "教师" : "助教"
member_roles = user_course_identity < Course::ASSISTANT_PROFESSOR ? teacher_user.course_role(teacher.course) : []
item = {name: name, course_member_id: teacher.id, login: teacher_user.login, user_id: teacher.user_id, role: role,
school: teacher_user.school_name, image_url: url_to_avatar(teacher_user), member_roles: member_roles}
pinyin = Pinyin.t(name.strip, splitter: '')
first_char = pinyin[0]
letter = first_letter first_char
if data.pluck(:letter).include?(letter)
data.select{|a|a[:letter]==letter}.first[:items] << item
else
data << {letter: letter, items: [item]}
end
end
end
# data = data.sort do |a, b|
# [a[:letter]] <=> [b[:letter]]
# end
# data.push(data.shift) if data.select{|a|a[:letter]=='#'}.first.present? # '#'排在最后
return data
end


def student_list students, excellent, user_course_identity
data = []
students.each do |student|
if student.user.present?
student_user = student.user
name = student_user.real_name
phone = excellent ? "" : student_user.hidden_phone
member_roles = user_course_identity < Course::ASSISTANT_PROFESSOR ? student_user.course_role(student.course) : []
item = {name: name, course_member_id: student.id, login: student_user.login, user_id: student.user_id,
student_id: student_user.student_id, image_url: url_to_avatar(student_user), phone: phone, member_roles: member_roles}
pinyin = Pinyin.t(name.strip, splitter: '')
first_char = pinyin[0]
letter = first_letter first_char
if data.pluck(:letter).include?(letter)
data.select{|a|a[:letter]==letter}.first[:items] << item
else
data << {letter: letter, items: [item]}
end
end
end
# data = data.sort do |a, b|
# [a[:letter]] <=> [b[:letter]]
# end
# data.push(data.shift) if data.select{|a|a[:letter]=='#'}.first.present? # '#'排在最后
return data
end

def first_letter char
if char.ord >= 97 && char.ord <= 122
letter = (char.ord - 32).chr.to_s
elsif char.ord >= 65 && char.ord <= 90
letter = char
else
letter = '#'
end
letter
end
end

+ 0
- 20
app/imports/admins/import_course_member_excel.rb View File

@@ -1,20 +0,0 @@
class Admins::ImportCourseMemberExcel < BaseImportXlsx
Data = Struct.new(:student_id, :name, :course_id, :role, :course_group_name, :school_id)

def read_each(&block)
sheet.each_row_streaming(pad_cells: true, offset: 1) do |row|
data = row.map(&method(:cell_value))[0..5]
block.call Data.new(*data)
end
end

private

def check_sheet_valid!
raise_import_error('请按照模板格式导入') if sheet.row(1).size != 6
end

def cell_value(obj)
obj&.cell_value&.to_s&.strip
end
end

+ 0
- 23
app/jobs/batch_publish_video_notify_job.rb View File

@@ -1,23 +0,0 @@
# 批量发布视频 消息任务
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

+ 12
- 0
app/jobs/cache_async_clear_job.rb View File

@@ -0,0 +1,12 @@
class CacheAsyncClearJob < ApplicationJob
queue_as :cache

def perform(type, id=nil)
case type
when "project_common_service"
Cache::V2::ProjectCommonService.new(id).clear
when "owner_common_service"
Cache::V2::OwnnerCommonService.new(id).clear
end
end
end

+ 16
- 0
app/jobs/cache_async_reset_job.rb View File

@@ -0,0 +1,16 @@
class CacheAsyncResetJob < ApplicationJob
queue_as :cache

def perform(type, id=nil)
case type
when "platform_statistic_service"
Cache::V2::PlatformStatisticService.new.reset
when "project_common_service"
Cache::V2::ProjectCommonService.new(id).reset
when "owner_common_service"
Cache::V2::OwnnerCommonService.new(id).reset
when "user_statistic_service"
Cache::V2::UserStatisticService.new(id).reset
end
end
end

+ 16
- 0
app/jobs/cache_async_set_job.rb View File

@@ -0,0 +1,16 @@
class CacheAsyncSetJob < ApplicationJob
queue_as :cache

def perform(type, params={}, id=nil)
case type
when "platform_statistic_service"
Cache::V2::PlatformStatisticService.new(params).call
when "project_common_service"
Cache::V2::ProjectCommonService.new(id, params).call
when "owner_common_service"
Cache::V2::OwnnerCommonService.new(id, params).call
when "user_statistic_service"
Cache::V2::UserStatisticService.new(id, params).call
end
end
end

+ 0
- 67
app/jobs/course_add_student_create_works_job.rb View File

@@ -1,67 +0,0 @@
# 学生加入课堂时创建相关任务作品
class CourseAddStudentCreateWorksJob < ApplicationJob
queue_as :default

def perform(course_id, student_ids)
course = Course.find_by(id: course_id)
return if course.blank?

# 如果之前存在相关作品,则更新is_delete字段
student_works = StudentWork.joins(:homework_common).where(user_id: student_ids, homework_commons: {course_id: course.id})
student_works.update_all(is_delete: 0)

exercise_users = ExerciseUser.joins(:exercise).where(user_id: student_ids, exercises: {course_id: course.id})
exercise_users.update_all(is_delete: 0)

poll_users = PollUser.joins(:poll).where(user_id: student_ids, polls: {course_id: course.id})
poll_users.update_all(is_delete: 0)

graduation_works = course.graduation_works.where(user_id: student_ids)
graduation_works.update_all(is_delete: 0)

attrs = %i[homework_common_id user_id created_at updated_at]

StudentWork.bulk_insert(*attrs) do |worker|
student_ids.each do |user_id|
same_attrs = {user_id: user_id}
course.homework_commons.where(homework_type: %i[normal group practice]).each do |homework|
next if StudentWork.where(user_id: user_id, homework_common_id: homework.id).any?
worker.add same_attrs.merge(homework_common_id: homework.id)
end
end
end

attrs = %i[exercise_id user_id created_at updated_at]
ExerciseUser.bulk_insert(*attrs) do |worker|
student_ids.each do |user_id|
same_attrs = {user_id: user_id}
course.exercises.each do |exercise|
next if ExerciseUser.where(user_id: user_id, exercise_id: exercise.id).any?
worker.add same_attrs.merge(exercise_id: exercise.id)
end
end
end

attrs = %i[poll_id user_id created_at updated_at]
PollUser.bulk_insert(*attrs) do |worker|
student_ids.each do |user_id|
same_attrs = {user_id: user_id}
course.polls.each do |poll|
next if PollUser.where(user_id: user_id, poll_id: poll.id).any?
worker.add same_attrs.merge(poll_id: poll.id)
end
end
end

attrs = %i[graduation_task_id user_id course_id created_at updated_at]
GraduationWork.bulk_insert(*attrs) do |worker|
student_ids.each do |user_id|
same_attrs = {user_id: user_id, course_id: course.id}
course.graduation_tasks.each do |task|
next if GraduationWork.where(user_id: user_id, graduation_task_id: task.id).any?
worker.add same_attrs.merge(graduation_task_id: task.id)
end
end
end
end
end

+ 0
- 19
app/jobs/course_delete_student_delete_works_job.rb View File

@@ -1,19 +0,0 @@
class CourseDeleteStudentDeleteWorksJob < ApplicationJob
queue_as :default

def perform(course_id, student_ids)
course = Course.find_by(id: course_id)
return if course.blank?

student_works = StudentWork.joins(:homework_common).where(user_id: student_ids, homework_commons: {course_id: course.id})
student_works.update_all(is_delete: 1)

exercise_users = ExerciseUser.joins(:exercise).where(user_id: student_ids, exercises: {course_id: course.id})
exercise_users.update_all(is_delete: 1)

poll_users = PollUser.joins(:poll).where(user_id: student_ids, polls: {course_id: course.id})
poll_users.update_all(is_delete: 1)

course.graduation_works.where(user_id: student_ids).update_all(is_delete: 1)
end
end

+ 0
- 22
app/jobs/course_delete_student_notify_job.rb View File

@@ -1,22 +0,0 @@
# 删除课堂用户
class CourseDeleteStudentNotifyJob < ApplicationJob
queue_as :notify

def perform(course_id, student_ids, trigger_user_id)
course = Course.find_by(id: course_id)
return if course.blank?

attrs = %i[user_id trigger_user_id container_id container_type belong_container_id
belong_container_type tiding_type created_at updated_at]

same_attrs = {
trigger_user_id: trigger_user_id, container_id: course.id, container_type: 'DeleteCourseMember',
belong_container_id: course.id, belong_container_type: 'Course', tiding_type: 'System'
}
Tiding.bulk_insert(*attrs) do |worker|
student_ids.each do |user_id|
worker.add same_attrs.merge(user_id: user_id)
end
end
end
end

+ 0
- 12
app/jobs/create_diff_record_job.rb View File

@@ -1,12 +0,0 @@
class CreateDiffRecordJob < ApplicationJob
queue_as :default

def perform(user_id, obj_id, obj_klass, column_name, before, after)
user = User.find_by(id: user_id)
obj = obj_klass.constantize.find_by(id: obj_id)

return if user.blank? || obj.blank?

CreateDiffRecordService.call(user, obj, column_name, before, after)
end
end

+ 0
- 21
app/jobs/delete_department_notify_job.rb View File

@@ -1,21 +0,0 @@
# 删除部门 消息通知
class DeleteDepartmentNotifyJob < ApplicationJob
queue_as :notify

def perform(department_id, operator_id, user_ids)
department = Department.unscoped.find_by(id: department_id)
return if department.blank? || user_ids.blank?

attrs = %i[ user_id trigger_user_id container_id container_type tiding_type status created_at updated_at]

same_attrs = {
trigger_user_id: operator_id, container_id: department.id, container_type: 'Department',
status: 4, tiding_type: 'System'
}
Tiding.bulk_insert(*attrs) do |worker|
user_ids.each do |user_id|
worker.add same_attrs.merge(user_id: user_id)
end
end
end
end

+ 0
- 44
app/jobs/exercise_publish_notify_job.rb View File

@@ -1,44 +0,0 @@
# 试卷发布 消息通知
class ExercisePublishNotifyJob < ApplicationJob
queue_as :notify
def perform(exercise_id, group_ids)
exercise = Exercise.find_by(id: exercise_id)
return if exercise.blank?
user = exercise.user
course = exercise.course
if group_ids.present?
students = course.students.where(course_group_id: group_ids)
subquery = course.teacher_course_groups.where(course_group_id: group_ids).select(:course_member_id)
teachers = course.teachers.where(id: subquery)
else
students = course.students
teachers = course.teachers
end
attrs = %i[
user_id trigger_user_id container_id container_type parent_container_id parent_container_type
belong_container_id belong_container_type viewed tiding_type created_at updated_at
]
same_attrs = {
trigger_user_id: user.id, container_id: exercise.id, container_type: 'Exercise',
parent_container_id: exercise.id, parent_container_type: 'ExercisePublish',
belong_container_id: exercise.course_id, belong_container_type: 'Course',
viewed: 0, tiding_type: 'Exercise'
}
Tiding.bulk_insert(*attrs) do |worker|
teacher_ids = teachers.pluck(:user_id)
unless exercise.tidings.exists?(parent_container_type: 'ExercisePublish', user_id: teacher_ids)
teacher_ids.each do |user_id|
worker.add same_attrs.merge(user_id: user_id)
end
end
students.pluck(:user_id).each do |user_id|
worker.add same_attrs.merge(user_id: user_id)
end
end
end
end

+ 0
- 17
app/jobs/get_aliyun_video_info_job.rb View File

@@ -1,17 +0,0 @@
# 获取阿里云视频信息
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
- 22
app/jobs/graduation_task_cross_comment_job.rb View File

@@ -1,22 +0,0 @@
# 毕设任务的交叉评阅分配
class GraduationTaskCrossCommentJob < ApplicationJob
queue_as :default

def perform(graduation_task_id)
task = GraduationTask.find_by(id: graduation_task_id)
return if task.blank?

task.graduation_task_group_assignations.includes(:graduation_group, :graduation_work).each do |assignation|
graduation_group = assignation.graduation_group
work = assignation.graduation_work
if graduation_group.present? && work.present?
member_ids = graduation_group.course_members.pluck(:user_id).uniq
member_ids.each do |user_id|
unless work.graduation_work_comment_assignations.exists?(user_id: user_id)
work.graduation_work_comment_assignations << GraduationWorkCommentAssignation.new(user_id: user_id, graduation_task_id: task.id)
end
end
end
end
end
end

+ 0
- 28
app/jobs/graduation_task_publish_notify_job.rb View File

@@ -1,28 +0,0 @@
# 任务发布 消息通知
class GraduationTaskPublishNotifyJob < ApplicationJob
queue_as :notify

def perform(graduation_task_id)
task = GraduationTask.find_by(id: graduation_task_id)
return if task.blank?
course = task.course
return if course.blank?

attrs = %i[
user_id trigger_user_id container_id container_type parent_container_id parent_container_type
belong_container_id belong_container_type viewed tiding_type created_at updated_at
]

same_attrs = {
trigger_user_id: task.user_id, container_id: task.id, container_type: 'GraduationTask',
parent_container_id: task.id, parent_container_type: 'TaskPublish',
belong_container_id: task.course_id, belong_container_type: 'Course',
viewed: 0, tiding_type: 'GraduationTask'
}
Tiding.bulk_insert(*attrs) do |worker|
course.course_members.pluck(:user_id).uniq.each do |user_id|
worker.add same_attrs.merge(user_id: user_id)
end
end
end
end

+ 0
- 33
app/jobs/resubmit_student_work_notify_job.rb View File

@@ -1,33 +0,0 @@
class ResubmitStudentWorkNotifyJob < ApplicationJob
queue_as :notify

def perform(homework_id, student_ids)
homework = HomeworkCommon.find_by(id: homework_id)
return if homework.blank? || student_ids.blank?
course = homework.course

attrs = %i[user_id trigger_user_id container_id container_type parent_container_id parent_container_type
belong_container_id belong_container_type tiding_type viewed created_at updated_at]

same_attrs = {
container_type: 'ResubmitStudentWork', parent_container_id: homework.id, parent_container_type: 'HomeworkCommon',
belong_container_id: course.id, belong_container_type: 'Course', tiding_type: 'HomeworkCommon', viewed: 0
}
Tiding.bulk_insert(*attrs) do |worker|
student_ids.each do |user_id|
next unless User.exists?(id: user_id)

work = homework.student_works.find_by(user_id: user_id)
next if work.blank?
score_user_ids = work.student_works_scores.where.not(score: nil).where(reviewer_role: [1, 2]).pluck(user_id).uniq
next if score_user_ids.blank?

attrs = same_attrs.merge(trigger_user_id: user_id, container_id: work.id)

score_user_ids.each do |user_id|
worker.add attrs.merge(user_id: user_id)
end
end
end
end
end

+ 4
- 4
app/jobs/send_template_message_job.rb View File

@@ -36,9 +36,9 @@ class SendTemplateMessageJob < ApplicationJob
operator = User.find_by_id(operator_id)
issue = Issue.find_by_id(issue_id)
return unless operator.present? && issue.present?
receivers = receivers.where.not(id: operator&.id)
# receivers = receivers.where.not(id: operator&.id)
receivers_string, content, notification_url = MessageTemplate::IssueAtme.get_message_content(receivers, operator, issue)
Notice::Write::CreateService.call(receivers_string, content, notification_url, source, {operator_id: operator.id, issue_id: issue.id}, 2)
Notice::Write::CreateService.call(receivers_string, content, notification_url, source, {operator_id: operator.id, issue_id: issue.id}, 2, operator_id)
when 'IssueChanged'
operator_id, issue_id, change_params = args[0], args[1], args[2]
operator = User.find_by_id(operator_id)
@@ -234,9 +234,9 @@ class SendTemplateMessageJob < ApplicationJob
operator = User.find_by_id(operator_id)
pull_request = PullRequest.find_by_id(pull_request_id)
return unless operator.present? && pull_request.present?
receivers = receivers.where.not(id: operator&.id)
# receivers = receivers.where.not(id: operator&.id)
receivers_string, content, notification_url = MessageTemplate::PullRequestAtme.get_message_content(receivers, operator, pull_request)
Notice::Write::CreateService.call(receivers_string, content, notification_url, source, {operator_id: operator.id, pull_request_id: pull_request.id}, 2)
Notice::Write::CreateService.call(receivers_string, content, notification_url, source, {operator_id: operator.id, pull_request_id: pull_request.id}, 2, operator_id)
when 'PullRequestChanged'
operator_id, pull_request_id, change_params = args[0], args[1], args[2]
operator = User.find_by_id(operator_id)


+ 1
- 0
app/libs/custom_regexp.rb View File

@@ -1,6 +1,7 @@
module CustomRegexp
PHONE = /1\d{10}/
EMAIL = /\A[a-zA-Z0-9]+([._\\]*[a-zA-Z0-9])*@([a-z0-9]+[-a-z0-9]*[a-z0-9]+.){1,63}[a-z0-9]+\z/
LOGIN = /^(?!_)(?!.*?_$)[a-zA-Z0-9_-]+$/ #只含有数字、字母、下划线不能以下划线开头和结尾
LASTNAME = /\A[a-zA-Z0-9\u4e00-\u9fa5]+\z/
NICKNAME = /\A[\u4e00-\u9fa5_a-zA-Z0-9]+\z/
PASSWORD = /\A[a-z_A-Z0-9\-\.!@#\$%\\\^&\*\)\(\+=\{\}\[\]\/",'_<>~\·`\?:;|]{8,16}\z/


+ 20
- 0
app/libs/forum.rb View File

@@ -0,0 +1,20 @@
module Forum
class << self
def forum_config
forum_config = {}

begin
config = Rails.application.config_for(:configuration).symbolize_keys!
forum_config = config[:forum].symbolize_keys!
raise 'forum config missing' if forum_config.blank?
rescue => ex
raise ex if Rails.env.production?

puts %Q{\033[33m [warning] forum config or configuration.yml missing,
please add it or execute 'cp config/configuration.yml.example config/configuration.yml' \033[0m}
forum_config = {}
end
forum_config
end
end
end

+ 2
- 8
app/mailers/user_mailer.rb View File

@@ -1,17 +1,11 @@
class UserMailer < ApplicationMailer
# 注意:这个地方一定要和你的邮箱服务域名一致
default from: 'educoder@trustie.org'
default from: 'notification@trustie.org'
# 用户注册验证码
def register_email(mail, code)
@code = code
mail(to: mail, subject: '验证你的电子邮件')
mail(to: mail, subject: 'Gitink | 注册验证码')
end
# 课堂讨论区的邮件通知
def course_message_email(mail, message_id)
@message = Message.find_by(id: message_id)
@course = @message&.board&.course
mail(to: mail, subject: '课堂发布了新的帖子') if @message.present? && @course.present?
end
end

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

Loading…
Cancel
Save