From 3674e0509cb75265a80b11ca65a7e0b67261a498 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9D=A8=E6=A0=91=E6=9E=97?= <904079904@qq.com> Date: Mon, 29 Jul 2019 16:50:01 +0800 Subject: [PATCH 1/4] =?UTF-8?q?=E8=B0=83=E6=95=B4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- public/react/src/modules/login/LoginDialog.js | 121 +++++++++++++++--- .../modules/user/LoginRegisterComponent.js | 2 +- 2 files changed, 102 insertions(+), 21 deletions(-) diff --git a/public/react/src/modules/login/LoginDialog.js b/public/react/src/modules/login/LoginDialog.js index 2db1929b9..58cd9b7fb 100644 --- a/public/react/src/modules/login/LoginDialog.js +++ b/public/react/src/modules/login/LoginDialog.js @@ -102,6 +102,8 @@ class LoginDialog extends Component { authCodeclass:'log-botton mt5', isRender: false, MyEduCoderModals:false, + Phonenumberisnotco:undefined, + Phonenumberisnotcobool:false, }; } @@ -112,26 +114,103 @@ class LoginDialog extends Component { register=(num) =>{ this.setState({login:1,speedy:num,dialogBox:'dialogBox2'}); } - - loginChange = () =>{ - let reg = /^(\w-*\.*)+@(\w-?)+(\.\w{2,})+$/; - let reg1 = /^1\d{10}$/; - let reg2=/^[a-zA-z]\w{3,14}$/; - // let reg3=/^[a-zA-Z0-9]+([.\-_\\]*[a-zA-Z0-9])*@([a-z0-9]+[-a-z0-9]*[a-z0-9]+.){1,63}[a-z0-9]+$/; - let value=this.refs.loginPassText.value; - let valuenum= value.length; - if(valuenum>0){ - if(!reg.test(value)&&!reg1.test(value)&&!reg2.test(value)){ - this.setState({regular:1}) - return - }else{ - this.setState({loginValue:value}) - this.setState({regular:0}) + inputOnBlur = (e, id) => { + this.Emailphonenumberverification(e.target.value, 1); + }; + // 输入页面 + loginChange = (e) =>{ + var stirngt=""; + if(e.target.value.length>0){ + var str= e.target.value.replace(/\s*/g,"") + stirngt=str; + }else{ + stirngt= e.target.value; + } + + if (e.target.value.length === 0) { + this.setState({ + loginValue: stirngt, + Phonenumberisnotco:undefined, + }) + }else{ + this.setState({ + loginValue: stirngt, + Phonenumberisnotco:undefined, + }) + } + // let reg = /^(\w-*\.*)+@(\w-?)+(\.\w{2,})+$/; + // let reg1 = /^1\d{10}$/; + // let reg2=/^[a-zA-z]\w{3,14}$/; + // // let reg3=/^[a-zA-Z0-9]+([.\-_\\]*[a-zA-Z0-9])*@([a-z0-9]+[-a-z0-9]*[a-z0-9]+.){1,63}[a-z0-9]+$/; + // let value=this.refs.loginPassText.value; + // let valuenum= value.length; + // if(valuenum>0){ + // if(!reg.test(value)&&!reg1.test(value)&&!reg2.test(value)){ + // this.setState({regular:1}) + // return + // }else{ + // // this.setState({loginValue:value}); + // this.setState({regular:0}); + // var stirngt; + // if(value.length>0){ + // var str= value.replace(/\s*/g,"") + // stirngt=str; + // }else{ + // stirngt= value; + // } + // this.setState({ + // loginValue:stirngt, + // }); + // } + // }else{ + // this.setState({loginValue:value}); + // var stirngt; + // if(value.length>0){ + // var str= value.replace(/\s*/g,"") + // stirngt=str; + // }else{ + // stirngt= value; + // } + // this.setState({ + // loginValue:stirngt, + // }); + // } + }; + //邮箱手机号验证 + Emailphonenumberverification = (value, id) => { + var url = `/accounts/valid_email_and_phone.json`; + axios.get((url), { + params: { + login: value, + type: 1, + } + }).then((result) => { + if(result){ + if(result.data.status===-2){ + if(result.data.message==="该手机号码或邮箱已被注册"){ + this.setState({ + Phonenumberisnotco: undefined, + Phonenumberisnotcobool: false, + }) + }else { + this.setState({ + Phonenumberisnotco: result.data.message, + Phonenumberisnotcobool: true, + }) + } + return; + }else { + this.setState({ + Phonenumberisnotco: undefined, + Phonenumberisnotcobool: false, + }) + return; } - }else{ - this.setState({loginValue:value}) } - } + }).catch((error) => { + + }) + }; passwordChange = () =>{ let value =this.refs.passwordText.value; @@ -411,7 +490,7 @@ class LoginDialog extends Component { window.location.href = url; }; render() { - let{open,login,speedy,loginValue,regular,isGoing,isGoingValue,disabled,bottonclass, + let{open,login,speedy,loginValue,regular,isGoing,isGoingValue,disabled,bottonclass,Phonenumberisnotco, dialogBox,shortcutnum,disabledType,gaincode,authCodeType,authCodeclass, isRender}=this.state; if (isRender === undefined) { @@ -457,11 +536,13 @@ class LoginDialog extends Component { id="name_loggin_input" ref="loginPassText" onInput={this.loginChange} + onBlur={(e) => this.inputOnBlur(e, 1)} + value={this.state.loginValue} name="username" placeholder="请输入有效的手机号/邮箱号" >

请输入有效的手机号/邮箱号

+ style={{display: Phonenumberisnotco===undefined?'none':'block'}}>{Phonenumberisnotco}

{ }) - } + }; //短信验证 SMSverification = () => { var url = `/accounts/get_verification_code.json`; From e83ba51d931dfcf6a46939ce5ee52edacaf56082 Mon Sep 17 00:00:00 2001 From: daiao <358551898@qq.com> Date: Mon, 29 Jul 2019 16:54:19 +0800 Subject: [PATCH 2/4] =?UTF-8?q?=E8=BF=81=E7=A7=BB=E6=8A=A5=E9=94=99?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/models/searchable/dependents/user.rb | 2 +- db/migrate/20190729080935_modify_login_for_users.rb | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/app/models/searchable/dependents/user.rb b/app/models/searchable/dependents/user.rb index f6dcaa430..bb55d0530 100644 --- a/app/models/searchable/dependents/user.rb +++ b/app/models/searchable/dependents/user.rb @@ -8,7 +8,7 @@ module Searchable::Dependents::User private def check_searchable_dependents - if firstname_previously_changed? || lastname_previously_changed? || user_extension.school_id_previously_changed? + if firstname_previously_changed? || lastname_previously_changed? || user_extension&.school_id_previously_changed? # reindex shixun created_shixuns.each(&:reindex) diff --git a/db/migrate/20190729080935_modify_login_for_users.rb b/db/migrate/20190729080935_modify_login_for_users.rb index 274e163de..335079a06 100644 --- a/db/migrate/20190729080935_modify_login_for_users.rb +++ b/db/migrate/20190729080935_modify_login_for_users.rb @@ -2,8 +2,8 @@ class ModifyLoginForUsers < ActiveRecord::Migration[5.2] def change users = User.where("created_on > '2019-07-26 19:00:00'") users.find_each do |use| - use.update_attributes(login: use.login.strip, phone: use.phone.try(:strip)) - use.user_extension.update_column(:student_id, use.user_extension.student_id.try(:strip)) + use.update_attributes(login: use.login&.strip, phone: use.phone&.strip) + use.user_extension.update_column(:student_id, use.user_extension&.student_id&.strip) end end end From b8f010217604c42148d589eae56804e0a59816f2 Mon Sep 17 00:00:00 2001 From: daiao <358551898@qq.com> Date: Mon, 29 Jul 2019 16:56:04 +0800 Subject: [PATCH 3/4] =?UTF-8?q?=E8=BF=81=E7=A7=BB=E6=8A=A5=E9=94=99?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- db/migrate/20190729080935_modify_login_for_users.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/db/migrate/20190729080935_modify_login_for_users.rb b/db/migrate/20190729080935_modify_login_for_users.rb index 335079a06..60e5e0964 100644 --- a/db/migrate/20190729080935_modify_login_for_users.rb +++ b/db/migrate/20190729080935_modify_login_for_users.rb @@ -3,7 +3,7 @@ class ModifyLoginForUsers < ActiveRecord::Migration[5.2] users = User.where("created_on > '2019-07-26 19:00:00'") users.find_each do |use| use.update_attributes(login: use.login&.strip, phone: use.phone&.strip) - use.user_extension.update_column(:student_id, use.user_extension&.student_id&.strip) + use.user_extension.update_column(:student_id, use.user_extension&.student_id&.strip) if use.user_extension end end end From 35a25aaf8b0df84eca7d7e7abc0d384b23550ff0 Mon Sep 17 00:00:00 2001 From: p31729568 Date: Mon, 29 Jul 2019 16:10:54 +0800 Subject: [PATCH 4/4] apply join project api --- app/controllers/projects/base_controller.rb | 5 ++ .../projects/project_applies_controller.rb | 14 ++++ app/jobs/apply_join_project_notify_job.rb | 31 +++++++ app/models/applied_project.rb | 9 ++ app/models/forge_activity.rb | 5 ++ app/models/member.rb | 6 +- app/models/member_role.rb | 1 + app/models/project.rb | 3 + app/models/role.rb | 3 + app/models/user.rb | 3 + app/models/user_grade.rb | 4 + app/services/projects/apply_join_service.rb | 82 +++++++++++++++++++ app/services/projects/join_service.rb | 35 ++++++++ config/routes.rb | 4 + 14 files changed, 203 insertions(+), 2 deletions(-) create mode 100644 app/controllers/projects/base_controller.rb create mode 100644 app/controllers/projects/project_applies_controller.rb create mode 100644 app/jobs/apply_join_project_notify_job.rb create mode 100644 app/models/applied_project.rb create mode 100644 app/models/forge_activity.rb create mode 100644 app/models/role.rb create mode 100644 app/models/user_grade.rb create mode 100644 app/services/projects/apply_join_service.rb create mode 100644 app/services/projects/join_service.rb diff --git a/app/controllers/projects/base_controller.rb b/app/controllers/projects/base_controller.rb new file mode 100644 index 000000000..d874b4759 --- /dev/null +++ b/app/controllers/projects/base_controller.rb @@ -0,0 +1,5 @@ +class Projects::BaseController < ApplicationController + include PaginateHelper + + before_action :require_login, :check_auth +end diff --git a/app/controllers/projects/project_applies_controller.rb b/app/controllers/projects/project_applies_controller.rb new file mode 100644 index 000000000..37d9d615e --- /dev/null +++ b/app/controllers/projects/project_applies_controller.rb @@ -0,0 +1,14 @@ +class Projects::ProjectAppliesController < Projects::BaseController + def create + project = Projects::ApplyJoinService.call(current_user, create_params) + render_ok(project_id: project.id) + rescue Projects::ApplyJoinService::Error => ex + render_error(ex.message) + end + + private + + def create_params + params.permit(:code, :role) + end +end \ No newline at end of file diff --git a/app/jobs/apply_join_project_notify_job.rb b/app/jobs/apply_join_project_notify_job.rb new file mode 100644 index 000000000..fe46bf0e0 --- /dev/null +++ b/app/jobs/apply_join_project_notify_job.rb @@ -0,0 +1,31 @@ +# 申请成为 管理员、开发者 加入项目 消息通知 +class ApplyJoinProjectNotifyJob < ApplicationJob + queue_as :notify + + def perform(user_id, project_id, role) + user = User.find_by(id: user_id) + project = Project.find_by(id: project_id) + return if user.blank? || project.blank? + + attrs = %i[user_id trigger_user_id container_id container_type status + belong_container_id belong_container_type tiding_type extra created_at updated_at] + + same_attrs = { + trigger_user_id: user.id, status: 0, tiding_type: 'Apply', extra: role, + container_id: project.id, container_type: 'JoinProject', + belong_container_id: project.id, belong_container_type: 'Project' + } + + # 报告人员加入时消息为系统通知消息 + if role == 5 + same_attrs[:container_type] = 'ReporterJoinProject' + same_attrs[:tiding_type] = 'System' + end + + Tiding.bulk_insert(*attrs) do |worker| + project.manager_members.each do |manager| + worker.add(same_attrs.merge(user_id: manager.user_id)) + end + end + end +end diff --git a/app/models/applied_project.rb b/app/models/applied_project.rb new file mode 100644 index 000000000..901443e81 --- /dev/null +++ b/app/models/applied_project.rb @@ -0,0 +1,9 @@ +class AppliedProject < ApplicationRecord + belongs_to :user + belongs_to :project + + has_many :applied_messages, as: :applied, dependent: :destroy + has_many :forge_activities, as: :forge_act, dependent: :destroy + + scope :pending, -> { where(status: 0) } +end diff --git a/app/models/forge_activity.rb b/app/models/forge_activity.rb new file mode 100644 index 000000000..77103d0ff --- /dev/null +++ b/app/models/forge_activity.rb @@ -0,0 +1,5 @@ +class ForgeActivity < ApplicationRecord + belongs_to :user + belongs_to :project + belongs_to :forge_act, polymorphic: true +end \ No newline at end of file diff --git a/app/models/member.rb b/app/models/member.rb index d1feb8a37..70b7fe305 100644 --- a/app/models/member.rb +++ b/app/models/member.rb @@ -1,6 +1,8 @@ class Member < ApplicationRecord - has_many :member_roles, dependent: :destroy + belongs_to :user belongs_to :course, optional: true belongs_to :project, optional: true - belongs_to :user + + has_many :member_roles, dependent: :destroy + has_many :roles, through: :member_roles end diff --git a/app/models/member_role.rb b/app/models/member_role.rb index 900efc732..2461c52f1 100644 --- a/app/models/member_role.rb +++ b/app/models/member_role.rb @@ -1,3 +1,4 @@ class MemberRole < ApplicationRecord + belongs_to :role belongs_to :member end diff --git a/app/models/project.rb b/app/models/project.rb index ddc6f6e5f..c3c626cb0 100644 --- a/app/models/project.rb +++ b/app/models/project.rb @@ -1,9 +1,12 @@ class Project < ApplicationRecord belongs_to :owner, class_name: 'User', foreign_key: :user_id + has_many :members + has_many :manager_members, -> { joins(:roles).where(roles: { name: 'Manager' }) }, class_name: 'Member' has_one :project_score, dependent: :destroy has_many :issues + has_many :user_grades, dependent: :destroy # 创建者 def creator diff --git a/app/models/role.rb b/app/models/role.rb new file mode 100644 index 000000000..e60606ffa --- /dev/null +++ b/app/models/role.rb @@ -0,0 +1,3 @@ +class Role < ApplicationRecord + has_many :member_roles, dependent: :destroy +end \ No newline at end of file diff --git a/app/models/user.rb b/app/models/user.rb index a95776be9..14d7b2697 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -128,6 +128,9 @@ class User < ApplicationRecord has_many :bidding_users, dependent: :destroy has_many :bidden_project_packages, through: :bidding_users, source: :project_package + # 项目 + has_many :applied_projects, dependent: :destroy + # Groups and active users scope :active, lambda { where(status: STATUS_ACTIVE) } diff --git a/app/models/user_grade.rb b/app/models/user_grade.rb new file mode 100644 index 000000000..dffbb4743 --- /dev/null +++ b/app/models/user_grade.rb @@ -0,0 +1,4 @@ +class UserGrade < ApplicationRecord + belongs_to :project + belongs_to :user +end diff --git a/app/services/projects/apply_join_service.rb b/app/services/projects/apply_join_service.rb new file mode 100644 index 000000000..a177de930 --- /dev/null +++ b/app/services/projects/apply_join_service.rb @@ -0,0 +1,82 @@ +class Projects::ApplyJoinService < ApplicationService + Error = Class.new(StandardError) + + attr_reader :user, :params + + def initialize(user, params) + @user = user + @params = params + end + + def call + validate! + + # 项目报告人员直接加入项目 + if params[:role] == 'reporter' + Projects::JoinService.call(project, user, role: 'reporter') + return project + end + + ActiveRecord::Base.transaction do + apply = user.applied_projects.create!(project: project, role: role_value) + + apply.forge_activities.find_or_create_by!(user: user, project: project) + + notify_project_manager! + end + + # notify_project_owner + ApplyJoinProjectNotifyJob.perform_later(user.id, project.id, role_value) + + project + end + + private + + def project + @_project ||= Project.find_by(invite_code: params[:code].to_s.strip) + end + + def role_value + @_role ||= + case params[:role] + when 'manager' then 3 + when 'developer' then 4 + when 'reporter' then 5 + else raise Error, '角色无效' + end + end + + def notify_project_manager! + columns = %i[user_id applied_id applied_type status viewed applied_user_id role project_id created_at updated_at] + AppliedMessage.bulk_insert(*columns) do |worker| + base_attr = { status: false, viewed: false, applied_user_id: user.id, role: role_value, project_id: project.id } + + project.manager_members.each do |manager| + worker.add(base_attr.merge(user_id: manager.user_id)) + end + end + end + + def notify_project_owner + owner = project.user + return if owner.phone.blank? + + Educoder::Sms.send(mobile: owner.phone, send_type:'applied_project_info', + user_name: owner.show_name, name: project.name) + rescue Exception => ex + Rails.logger.error("发送短信失败 => #{ex.message}") + end + + def validate! + # params check + raise Error, '邀请码不能为空' if params[:code].blank? + raise Error, '角色不能为空' if params[:role].blank? + raise Error, '角色无效' unless %w(manager developer reporter).include?(params[:role]) + + # logical check + raise Error, '邀请码无效' if project.blank? + raise Error, '您已在该项目中' if project.member?(user) + raise Error, '您已经提交过申请' if user.applied_projects.pending.exists?(project: project) + end +end \ No newline at end of file diff --git a/app/services/projects/join_service.rb b/app/services/projects/join_service.rb new file mode 100644 index 000000000..b434e48cd --- /dev/null +++ b/app/services/projects/join_service.rb @@ -0,0 +1,35 @@ +class Projects::JoinService < ApplicationService + attr_reader :project, :user, :opts + + def initialize(project, user, **opts) + @project = project + @user = user + @opts = opts + end + + def call + ActiveRecord::Base.transaction do + member = project.members.create!(user: user) + + member.member_roles.create!(role_id: role_value) + + project.user_grades.find_or_create_by!(user: user) + end + + ApplyJoinProjectNotifyJob.perform_later(user, project, role_value) + + project + end + + private + + def role_value + @_role ||= + case opts[:role] + when 'manager' then 3 + when 'developer' then 4 + when 'reporter' then 5 + else raise ArgumentError + end + end +end \ No newline at end of file diff --git a/config/routes.rb b/config/routes.rb index 6024201a4..6623edd99 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -695,6 +695,10 @@ Rails.application.routes.draw do end resources :libraries, only: [:index, :show, :create, :update, :destroy] + + scope module: :projects do + resources :applied_projects, only: [:create] + end end #git 认证回调