| @@ -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 | |||
| { | |||
| @@ -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 { | |||
| } | |||
| }); | |||
| }) | |||
| @@ -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("") | |||
| } | |||
| }); | |||
| } | |||
| }) | |||
| }); | |||
| }) | |||
| @@ -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; | |||
| } | |||
| } | |||
| @@ -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 | |||
| @@ -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 | |||
| @@ -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 | |||
| @@ -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 | |||
| @@ -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 | |||
| @@ -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 | |||
| @@ -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 | |||
| @@ -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 | |||
| @@ -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 | |||
| @@ -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 | |||
| @@ -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 | |||
| @@ -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 | |||
| @@ -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 | |||
| @@ -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 | |||
| @@ -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, | |||
| @@ -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 | |||
| @@ -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 | |||
| @@ -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}") | |||
| @@ -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,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, "更新失败") | |||
| @@ -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 | |||
| @@ -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 | |||
| @@ -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 | |||
| @@ -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 | |||
| @@ -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 | |||
| @@ -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,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更新失败") | |||
| @@ -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 | |||
| @@ -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 | |||
| @@ -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 | |||
| @@ -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 | |||
| @@ -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 | |||
| @@ -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 | |||
| @@ -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 | |||
| @@ -1,2 +0,0 @@ | |||
| module EcCourseTargetDecorator | |||
| end | |||
| @@ -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 | |||
| @@ -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 | |||
| @@ -1,5 +0,0 @@ | |||
| module LibraryDecorator | |||
| extend ApplicationDecorator | |||
| display_time_method :published_at, :created_at, :updated_at | |||
| end | |||
| @@ -1,5 +0,0 @@ | |||
| module ShixunDecorator | |||
| def human_status | |||
| I18n.t("shixun.status.#{status}") | |||
| end | |||
| end | |||
| @@ -1,5 +0,0 @@ | |||
| module SubjectDecorator | |||
| def can_visited? | |||
| published? || User.current.admin? || member?(User.current) | |||
| end | |||
| end | |||
| @@ -1,5 +0,0 @@ | |||
| module VideoDecorator | |||
| extend ApplicationDecorator | |||
| display_time_method :published_at, :created_at, :updated_at | |||
| end | |||
| @@ -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 | |||
| @@ -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 | |||
| @@ -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 | |||
| @@ -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 | |||
| @@ -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 | |||
| @@ -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 | |||
| @@ -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 | |||
| @@ -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,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 | |||
| @@ -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 | |||
| @@ -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 | |||
| @@ -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 | |||
| @@ -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 | |||
| @@ -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 | |||
| @@ -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 | |||
| @@ -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 | |||
| @@ -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 | |||
| @@ -1,2 +0,0 @@ | |||
| module BoardsHelper | |||
| end | |||
| @@ -1,10 +0,0 @@ | |||
| module ChallengesHelper | |||
| def match_begin_symbol str | |||
| str.gsub(/\A\r/, "\r\r") | |||
| end | |||
| end | |||
| @@ -1,2 +0,0 @@ | |||
| module CourseGroupsHelper | |||
| end | |||
| @@ -1,2 +0,0 @@ | |||
| module CourseModulesHelper | |||
| end | |||
| @@ -1,2 +0,0 @@ | |||
| module CourseSecondCategoriesHelper | |||
| end | |||
| @@ -1,2 +0,0 @@ | |||
| module CourseStagesHelper | |||
| end | |||
| @@ -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 | |||
| @@ -1,2 +0,0 @@ | |||
| module DiscussesHelper | |||
| end | |||
| @@ -1,2 +0,0 @@ | |||
| module EduDatasHelper | |||
| end | |||
| @@ -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 | |||
| @@ -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 | |||
| @@ -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 | |||
| @@ -1,2 +0,0 @@ | |||
| module HackUserLastestCodesHelper | |||
| end | |||
| @@ -1,2 +0,0 @@ | |||
| module HacksHelper | |||
| end | |||
| @@ -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 | |||
| @@ -1,2 +0,0 @@ | |||
| module TrustieHacksHelper | |||
| end | |||
| @@ -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 | |||
| @@ -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 | |||
| @@ -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 | |||
| @@ -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 | |||
| @@ -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 | |||
| @@ -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 | |||
| @@ -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 | |||
| @@ -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 | |||
| @@ -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 | |||
| @@ -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 | |||
| @@ -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 | |||
| @@ -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 | |||
| @@ -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 | |||
| @@ -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 | |||
| @@ -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 | |||
| @@ -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 | |||
| @@ -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,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/ | |||
| @@ -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 | |||
| @@ -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 | |||