diff --git a/CHANGELOG.md b/CHANGELOG.md index d24953c43..a5250040e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,21 @@ # Changelog +## [v3.1.0](https://forgeplus.trustie.net/projects/jasder/forgeplus/releases) - 2021-06-09 + +* ENHANCEMENTS + * ADD 用户活动统计图表功能 + * ADD 用户精选项目功能 + * ADD 用户贡献度统计图表功能 + * ADD 用户开发能力数据统计工 + * ADD 用户角色定位展示功能 + * ADD 用户专业定位标签展示功能 + * ADD 修改用户基本资料功能 + * ADD 更改密码功能 + * ADD 用户个人主页基本现在展示可配置功能 + +* BUGFIXES + * Fix 解决一些bug + * Fix 优化美化页面 -## [v3.0.4](https://forgeplus.trustie.net/projects/jasder/forgeplus/releases) - 2021-05-24 * BUGFIXES * Fix 在线修改文件,页面文件显不及时的问题(46049) diff --git a/app/controllers/admins/auth_schools_controller.rb b/app/controllers/admins/auth_schools_controller.rb index 825c04cdc..39d5b9683 100644 --- a/app/controllers/admins/auth_schools_controller.rb +++ b/app/controllers/admins/auth_schools_controller.rb @@ -32,7 +32,7 @@ class Admins::AuthSchoolsController < Admins::BaseController def search_manager school = School.find_by(id: params[:school_id]) user_ids = school&.ec_school_users&.pluck(:user_id) - @users = User.where.not(id: user_ids).where("CONCAT_WS(lastname, firstname, nickname) like ?", "%#{params[:name].strip.to_s}%").limit(10) + @users = User.where.not(id: user_ids).where("CONCAT(lastname, firstname) like ? OR nickname like ?", "%#{params[:name].strip.to_s}%", "%#{params[:name].strip.to_s}%").limit(10) end # 添加认证学校管理员 diff --git a/app/controllers/admins/faqs_controller.rb b/app/controllers/admins/faqs_controller.rb index fc00f847c..250d3d60a 100644 --- a/app/controllers/admins/faqs_controller.rb +++ b/app/controllers/admins/faqs_controller.rb @@ -2,8 +2,8 @@ class Admins::FaqsController < Admins::BaseController before_action :find_faq, only: [:edit,:update, :destroy] def index - sort_by = params[:sort_by] ||= 'updated_at' - sort_direction = params[:sort_direction] ||= 'desc' + sort_by = Faq.column_names.include?(params[:sort_by]) ? params[:sort_by] : 'updated_at' + sort_direction = %w(desc asc).include?(params[:sort_direction]) ? params[:sort_direction] : 'desc' keyword = params[:keyword].to_s.strip collection = Faq.search_question(keyword).order("#{sort_by} #{sort_direction}") diff --git a/app/controllers/admins/laboratories_controller.rb b/app/controllers/admins/laboratories_controller.rb index 46463fed0..7044b6218 100644 --- a/app/controllers/admins/laboratories_controller.rb +++ b/app/controllers/admins/laboratories_controller.rb @@ -32,8 +32,8 @@ class Admins::LaboratoriesController < Admins::BaseController keyword = params[:keyword].to_s.strip if keyword.present? - like_sql = 'shixuns.name LIKE :keyword OR CONCAT_WS(users.lastname, users.firstname, users.nickname) LIKE :keyword '\ - 'OR mirror_repositories.name LIKE :keyword' + like_sql = 'shixuns.name LIKE :keyword OR CONCAT(users.lastname, users.firstname) LIKE :keyword '\ + 'users.nickname LIKE :keyword OR mirror_repositories.name LIKE :keyword' shixuns = shixuns.joins(:user, :mirror_repositories).where(like_sql, keyword: "%#{keyword}%") end @@ -48,7 +48,7 @@ class Admins::LaboratoriesController < Admins::BaseController keyword = params[:keyword].to_s.strip if keyword.present? - like_sql = 'subjects.name LIKE :keyword OR CONCAT_WS(users.lastname, users.firstname, users.nickname) LIKE :keyword' + like_sql = 'subjects.name LIKE :keyword OR CONCAT(users.lastname, users.firstname) LIKE :keyword OR users.nickname LIKE :keyword' subjects = subjects.joins(:user).where(like_sql, keyword: "%#{keyword}%") end diff --git a/app/controllers/admins/project_categories_controller.rb b/app/controllers/admins/project_categories_controller.rb index 944a2cf04..ba83e841d 100644 --- a/app/controllers/admins/project_categories_controller.rb +++ b/app/controllers/admins/project_categories_controller.rb @@ -3,8 +3,8 @@ class Admins::ProjectCategoriesController < Admins::BaseController before_action :validate_names, only: [:create, :update] def index - sort_by = params[:sort_by] ||= 'created_at' - sort_direction = params[:sort_direction] ||= 'desc' + sort_by = ProjectCategory.column_names.include?(params[:sort_by]) ? params[:sort_by] : 'created_at' + sort_direction = %w(desc asc).include?(params[:sort_direction]) ? params[:sort_direction] : 'desc' q = ProjectCategory.ransack(name_cont: params[:name]) project_categories = q.result(distinct: true).order("#{sort_by} #{sort_direction}") @project_categories = paginate(project_categories) diff --git a/app/controllers/admins/project_ignores_controller.rb b/app/controllers/admins/project_ignores_controller.rb index 427ee86b8..360f189c0 100644 --- a/app/controllers/admins/project_ignores_controller.rb +++ b/app/controllers/admins/project_ignores_controller.rb @@ -3,8 +3,8 @@ class Admins::ProjectIgnoresController < Admins::BaseController before_action :validate_params, only: [:create, :update] def index - sort_by = params[:sort_by] ||= 'created_at' - sort_direction = params[:sort_direction] ||= 'desc' + sort_by = Ignore.column_names.include?(params[:sort_by]) ? params[:sort_by] : 'created_at' + sort_direction = %w(desc asc).include?(params[:sort_direction]) ? params[:sort_direction] : 'desc' q = Ignore.ransack(name_cont: params[:search]) project_ignores = q.result(distinct: true).order("#{sort_by} #{sort_direction}") @project_ignores = paginate(project_ignores) diff --git a/app/controllers/admins/project_languages_controller.rb b/app/controllers/admins/project_languages_controller.rb index e188b75ef..0f26f25bb 100644 --- a/app/controllers/admins/project_languages_controller.rb +++ b/app/controllers/admins/project_languages_controller.rb @@ -3,8 +3,8 @@ class Admins::ProjectLanguagesController < Admins::BaseController before_action :validate_names, only: [:create, :update] def index - sort_by = params[:sort_by] ||= 'created_at' - sort_direction = params[:sort_direction] ||= 'desc' + sort_by = ProjectLanguage.column_names.include?(params[:sort_by]) ? params[:sort_by] : 'created_at' + sort_direction = %w(desc asc).include?(params[:sort_direction]) ? params[:sort_direction] : 'desc' q = ProjectLanguage.ransack(name_cont: params[:search]) project_languages = q.result(distinct: true).order("#{sort_by} #{sort_direction}") @project_languages = paginate(project_languages) diff --git a/app/controllers/admins/project_licenses_controller.rb b/app/controllers/admins/project_licenses_controller.rb index bc5789026..5c16a884b 100644 --- a/app/controllers/admins/project_licenses_controller.rb +++ b/app/controllers/admins/project_licenses_controller.rb @@ -3,8 +3,8 @@ class Admins::ProjectLicensesController < Admins::BaseController before_action :validate_params, only: [:create, :update] def index - sort_by = params[:sort_by] ||= 'created_at' - sort_direction = params[:sort_direction] ||= 'desc' + sort_by = License.column_names.include?(params[:sort_by]) ? params[:sort_by] : 'created_at' + sort_direction = %w(desc asc).include?(params[:sort_direction]) ? params[:sort_direction] : 'desc' q = License.ransack(name_cont: params[:search]) project_licenses = q.result(distinct: true).order("#{sort_by} #{sort_direction}") @project_licenses = paginate(project_licenses) diff --git a/app/controllers/admins/projects_controller.rb b/app/controllers/admins/projects_controller.rb index 2335db6d1..9e06eb1c9 100644 --- a/app/controllers/admins/projects_controller.rb +++ b/app/controllers/admins/projects_controller.rb @@ -1,9 +1,8 @@ class Admins::ProjectsController < Admins::BaseController def index - sort_by = params[:sort_by] ||= 'created_on' - sort_direction = params[:sort_direction] ||= 'desc' - + sort_by = Project.column_names.include?(params[:sort_by]) ? params[:sort_by] : 'created_on' + sort_direction = %w(desc asc).include?(params[:sort_direction]) ? params[:sort_direction] : 'desc' search = params[:search].to_s.strip projects = Project.where("name like ?", "%#{search}%").order("#{sort_by} #{sort_direction}") @projects = paginate projects.includes(:owner, :members, :issues, :versions, :attachments, :project_score) diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb index 8536147ca..8b98a920b 100644 --- a/app/controllers/application_controller.rb +++ b/app/controllers/application_controller.rb @@ -615,8 +615,8 @@ class ApplicationController < ActionController::Base end # 排序 - rorder = option[:order] || "updated_at" - b_order = option[:b_order] || "desc" + rorder = UserExtension.column_names.include?(option[:order]) ? option[:order] : "updated_at" + b_order = %w(desc asc).include?(option[:b_order]) ? option[:b_order] : "desc" if rorder == "created_at" || rorder == "work_score" work_list = work_list.order("graduation_works.#{rorder} #{b_order}") elsif rorder == "student_id" diff --git a/app/controllers/applied_projects_controller.rb b/app/controllers/applied_projects_controller.rb new file mode 100644 index 000000000..e2f55b27a --- /dev/null +++ b/app/controllers/applied_projects_controller.rb @@ -0,0 +1,13 @@ +class AppliedProjectsController < ApplicationController + before_action :require_login + def create + @applied_project = Projects::ApplyJoinService.call(current_user, applied_params) + rescue Projects::ApplyJoinService::Error => ex + render_error(ex.message) + end + + private + def applied_params + params.require(:applied_project).permit(:code, :role) + end +end \ No newline at end of file diff --git a/app/controllers/composes_controller.rb b/app/controllers/composes_controller.rb index c1a7f524f..006604f29 100644 --- a/app/controllers/composes_controller.rb +++ b/app/controllers/composes_controller.rb @@ -3,13 +3,12 @@ class ComposesController < ApplicationController before_action :find_compose, except: [:index, :new,:create] def index - @order_type = params[:order] || "created_at" @search_name = params[:search] composes = Compose.compose_includes if @search_name.present? composes = composes.where("title like ?", "%#{@search_name}%") end - composes = composes.order("#{@order_type} desc") + composes = composes.order("#{order_type} desc") @page = params[:page] || 1 @limit = params[:limit] || 15 @composes_size = composes.size @@ -96,4 +95,8 @@ class ComposesController < ApplicationController end end + def order_type + Compose.column_names.include?(params[:order_type]) ? params[:order_type] : 'created_at' + end + end \ No newline at end of file diff --git a/app/controllers/issue_tags_controller.rb b/app/controllers/issue_tags_controller.rb index 312de7842..d21d8ed4f 100644 --- a/app/controllers/issue_tags_controller.rb +++ b/app/controllers/issue_tags_controller.rb @@ -7,9 +7,6 @@ class IssueTagsController < ApplicationController def index - order_name = params[:order_name] || "created_at" - order_type = params[:order_type] || "desc" - issue_tags = @project.issue_tags.order("#{order_name} #{order_type}") @user_admin_or_member = current_user.present? && (current_user.admin || @project.member?(current_user)) @page = params[:page] || 1 @@ -138,4 +135,14 @@ class IssueTagsController < ApplicationController end end + private + + def order_name + IssueTag.column_names.include?(params[:order_name]) ? params[:order_name] : 'created_at' + end + + def order_type + %w(desc asc).include?(params[:order_type]) ? params[:order_type] : 'desc' + end + end diff --git a/app/controllers/issues_controller.rb b/app/controllers/issues_controller.rb index 3584d6bf5..4b073415a 100644 --- a/app/controllers/issues_controller.rb +++ b/app/controllers/issues_controller.rb @@ -17,11 +17,15 @@ class IssuesController < ApplicationController issues = @project.issues.issue_issue.issue_index_includes issues = issues.where(is_private: false) unless @user_admin_or_member - @all_issues_size = issues.size - @open_issues_size = issues.where.not(status_id: 5).size - @close_issues_size = issues.where(status_id: 5).size - @assign_to_me_size = issues.where(assigned_to_id: current_user&.id).size - @my_published_size = issues.where(author_id: current_user&.id).size + @all_issues = issues + @filter_issues = @all_issues + @filter_issues = @filter_issues.where.not(status_id: IssueStatus::CLOSED) if params[:status_type].to_i == IssueStatus::ADD + @filter_issues = @filter_issues.where(status_id: IssueStatus::CLOSED) if params[:status_type].to_i == IssueStatus::SOLVING + @filter_issues = @filter_issues.where("subject LIKE ? OR description LIKE ? ", "%#{params[:search]}%", "%#{params[:search]}%") if params[:search].present? + @open_issues = @all_issues.where.not(status_id: IssueStatus::CLOSED) + @close_issues = @all_issues.where(status_id: IssueStatus::CLOSED) + @assign_to_me = @filter_issues.where(assigned_to_id: current_user&.id) + @my_published = @filter_issues.where(author_id: current_user&.id) scopes = Issues::ListQueryService.call(issues,params.delete_if{|k,v| v.blank?}, "Issue") @issues_size = scopes.size @issues = paginate(scopes) diff --git a/app/controllers/members_controller.rb b/app/controllers/members_controller.rb index fea57fa72..67aba25f4 100644 --- a/app/controllers/members_controller.rb +++ b/app/controllers/members_controller.rb @@ -18,7 +18,7 @@ class MembersController < ApplicationController scope = @project.members.includes(:roles, user: :user_extension) search = params[:search].to_s.downcase role = params[:role].to_s - scope = scope.joins(:user).where("LOWER(CONCAT_WS(users.lastname, users.firstname, users.login, users.mail, users.nickname)) LIKE ?", "%#{search.split(" ").join('|')}%") if search.present? + scope = scope.joins(:user).merge(User.like(search)) scope = scope.joins(:roles).where("roles.name LIKE ?", "%#{role}%") if role.present? @total_count = scope.size diff --git a/app/controllers/organizations/organization_users_controller.rb b/app/controllers/organizations/organization_users_controller.rb index d2d6963a4..1cddabc90 100644 --- a/app/controllers/organizations/organization_users_controller.rb +++ b/app/controllers/organizations/organization_users_controller.rb @@ -5,7 +5,7 @@ class Organizations::OrganizationUsersController < Organizations::BaseController def index @organization_users = @organization.organization_users.includes(:user) search = params[:search].to_s.downcase - @organization_users = @organization_users.joins(:user).where("LOWER(CONCAT_WS(users.lastname, users.firstname, users.login, users.mail, users.nickname)) LIKE ?", "%#{search.split(" ").join('|')}%") if search.present? + @organization_users = @organization_users.joins(:user).merge(User.like(search)) @organization_users = kaminari_paginate(@organization_users) end diff --git a/app/controllers/organizations/organizations_controller.rb b/app/controllers/organizations/organizations_controller.rb index 7be9390d7..104db90ed 100644 --- a/app/controllers/organizations/organizations_controller.rb +++ b/app/controllers/organizations/organizations_controller.rb @@ -88,11 +88,11 @@ class Organizations::OrganizationsController < Organizations::BaseController end def sort_by - params.fetch(:sort_by, "created_at") + OrganizationExtension.column_names.include?(params[:sort_by]) ? params[:sort_by] : 'created_at' end def sort_direction - params.fetch(:sort_direction, "desc") + %w(desc asc).include?(params[:sort_direction]) ? params[:sort_direction] : 'desc' end end \ No newline at end of file diff --git a/app/controllers/organizations/projects_controller.rb b/app/controllers/organizations/projects_controller.rb index cc275b090..b36a76125 100644 --- a/app/controllers/organizations/projects_controller.rb +++ b/app/controllers/organizations/projects_controller.rb @@ -36,10 +36,10 @@ class Organizations::ProjectsController < Organizations::BaseController end def sort - params.fetch(:sort_by, "updated_on") + Project.column_names.include?(params[:sort_by]) ? params[:sort_by] : 'updated_on' end def sort_direction - params.fetch(:sort_direction, "desc") + %w(desc asc).include?(params[:sort_direction]) ? params[:sort_direction] : 'desc' end end \ No newline at end of file diff --git a/app/controllers/organizations/team_users_controller.rb b/app/controllers/organizations/team_users_controller.rb index d8694a6c5..752e1f0e5 100644 --- a/app/controllers/organizations/team_users_controller.rb +++ b/app/controllers/organizations/team_users_controller.rb @@ -8,7 +8,7 @@ class Organizations::TeamUsersController < Organizations::BaseController @team_users = @team.team_users.includes(:user) search = params[:search].to_s.downcase - @team_users = @team_users.joins(:user).where("LOWER(CONCAT_WS(users.lastname, users.firstname, users.login, users.mail, users.nickname)) LIKE ?", "%#{search.split(" ").join('|')}%") if search.present? + @team_users = @team_users.joins(:user).merge(User.like(search)) @team_users = kaminari_paginate(@team_users) end diff --git a/app/controllers/pull_requests_controller.rb b/app/controllers/pull_requests_controller.rb index 139ea9526..8521cd4b3 100644 --- a/app/controllers/pull_requests_controller.rb +++ b/app/controllers/pull_requests_controller.rb @@ -12,10 +12,12 @@ class PullRequestsController < ApplicationController # @issues = Gitea::PullRequest::ListService.new(@user,@repository.try(:identifier)).call #通过gitea获取 issues = @project.issues.issue_pull_request.issue_index_includes.includes(pull_request: :user) issues = issues.where(is_private: false) unless current_user.present? && (current_user.admin? || @project.member?(current_user)) - @all_issues_size = issues.size - @open_issues_size = issues.joins(:pull_request).where(pull_requests: {status: 0}).size - @close_issues_size = issues.joins(:pull_request).where(pull_requests: {status: 2}).size - @merged_issues_size = issues.joins(:pull_request).where(pull_requests: {status: 1}).size + @all_issues = issues.distinct + @filter_issues = @all_issues + @filter_issues = @filter_issues.where("subject LIKE ? OR description LIKE ? ", "%#{params[:search]}%", "%#{params[:search]}%") if params[:search].present? + @open_issues = @filter_issues.joins(:pull_request).where(pull_requests: {status: PullRequest::OPEN}) + @close_issues = @filter_issues.joins(:pull_request).where(pull_requests: {status: PullRequest::CLOSED}) + @merged_issues = @filter_issues.joins(:pull_request).where(pull_requests: {status: PullRequest::MERGED}) @user_admin_or_member = current_user.present? && (current_user.admin || @project.member?(current_user)) scopes = Issues::ListQueryService.call(issues,params.delete_if{|k,v| v.blank?}, "PullRequest") diff --git a/app/controllers/repositories_controller.rb b/app/controllers/repositories_controller.rb index 6594d06e9..93ba02a92 100644 --- a/app/controllers/repositories_controller.rb +++ b/app/controllers/repositories_controller.rb @@ -56,9 +56,7 @@ class RepositoriesController < ApplicationController # TODO # 临时处理readme文件问题 - admin = current_user.blank? ? User.where(admin: true).last : current_user - - result = Gitea::Repository::Readme::GetService.call(@owner.login, @project.identifier, @ref, admin&.gitea_token) + result = Gitea::Repository::Readme::GetService.call(@owner.login, @project.identifier, @ref, @owner&.gitea_token) @readme = if result[:status] == :success result[:body] diff --git a/app/controllers/users/applied_messages_controller.rb b/app/controllers/users/applied_messages_controller.rb index e80cabf92..950caa2b3 100644 --- a/app/controllers/users/applied_messages_controller.rb +++ b/app/controllers/users/applied_messages_controller.rb @@ -9,7 +9,7 @@ class Users::AppliedMessagesController < Users::BaseController private def check_auth - return render_forbidden unless observed_logged_user? + return render_forbidden unless current_user.admin? || observed_logged_user? end def view_messages diff --git a/app/controllers/users/applied_projects_controller.rb b/app/controllers/users/applied_projects_controller.rb new file mode 100644 index 000000000..3663a6d34 --- /dev/null +++ b/app/controllers/users/applied_projects_controller.rb @@ -0,0 +1,39 @@ +class Users::AppliedProjectsController < Users::BaseController + before_action :check_auth + before_action :find_applied_project, except: [:index] + before_action :find_project, except: [:index] + + def index + @applied_projects = AppliedProject.where(project_id: observed_user.full_admin_projects) + @applied_projects = paginate @applied_projects.order("created_at desc") + end + + # 接受申请 + def accept + @applied_project = Projects::AcceptJoinService.call(current_user, @applied_project) + rescue Exception => e + uid_logger_error(e.message) + tip_exception(e.message) + end + + # 拒绝申请 + def refuse + @applied_project = Projects::RefuseJoinService.call(current_user, @applied_project) + rescue Exception => e + uid_logger_error(e.message) + tip_exception(e.message) + end + + private + def check_auth + return render_forbidden unless current_user.admin? || observed_logged_user? + end + + def find_applied_project + @applied_project = AppliedProject.find_by_id params[:id] + end + + def find_project + @project = @applied_project.project + end +end \ No newline at end of file diff --git a/app/controllers/users/applied_transfer_projects_controller.rb b/app/controllers/users/applied_transfer_projects_controller.rb index b1777f526..a40833943 100644 --- a/app/controllers/users/applied_transfer_projects_controller.rb +++ b/app/controllers/users/applied_transfer_projects_controller.rb @@ -28,7 +28,7 @@ class Users::AppliedTransferProjectsController < Users::BaseController private def check_auth - return render_forbidden unless observed_logged_user? + return render_forbidden unless current_user.admin? || observed_logged_user? end def find_applied_transfer_project diff --git a/app/controllers/users/banks_controller.rb b/app/controllers/users/banks_controller.rb index 2c792151e..99a55e6c4 100644 --- a/app/controllers/users/banks_controller.rb +++ b/app/controllers/users/banks_controller.rb @@ -1,8 +1,8 @@ class Users::BanksController < Users::BaseController before_action :params_filter def index - order = params[:order] || "updated_at" - sort = params[:sort] || "desc" + order = CourseList.column_names.include?(params[:order]) ? params[:order] : "updated_at" + sort = %w(desc asc).includes?(params[:sort]) ? params[:sort] : "desc" @banks = @object_type.classify.constantize.where(@object_filter) @course_lists = CourseList.where(id: @banks.pluck(:course_list_id)) @banks = @banks.where(course_list_id: params[:tag_id]) unless params[:tag_id].blank? diff --git a/app/controllers/users/organizations_controller.rb b/app/controllers/users/organizations_controller.rb index 721339e84..2d949da7d 100644 --- a/app/controllers/users/organizations_controller.rb +++ b/app/controllers/users/organizations_controller.rb @@ -16,10 +16,10 @@ class Users::OrganizationsController < Users::BaseController private def sort_by - params.fetch(:sort_by, "created_at") + OrganizationExtension.column_names.include?(params[:sort_by]) ? params[:sort_by] : 'created_at' end def sort_direction - params.fetch(:sort_direction, "desc") + %w(desc asc).include?(params[:sort_direction]) ? params[:sort_direction] : 'desc' end end \ No newline at end of file diff --git a/app/controllers/users_controller.rb b/app/controllers/users_controller.rb index 163b5849f..a0184a348 100644 --- a/app/controllers/users_controller.rb +++ b/app/controllers/users_controller.rb @@ -28,13 +28,15 @@ class UsersController < ApplicationController def show #待办事项,现在未做 - if User.current.login == @user.login + if User.current.admin? || User.current.login == @user.login @waiting_applied_messages = @user.applied_messages.waiting @common_applied_transfer_projects = AppliedTransferProject.where(owner_id: @user.id).common + AppliedTransferProject.where(owner_id: Organization.joins(team_users: :team).where(team_users: {user_id: @user.id}, teams: {authorize: %w(admin owner)} )).common - @undo_events = @waiting_applied_messages.size + @common_applied_transfer_projects.size + @common_applied_projects = AppliedProject.where(project_id: @user.full_admin_projects).common + @undo_events = @waiting_applied_messages.size + @common_applied_transfer_projects.size + @common_applied_projects.size else @waiting_applied_messages = AppliedMessage.none @common_applied_transfer_projects = AppliedTransferProject.none + @common_applied_projects = AppliedProject.none @undo_events = 0 end #用户的组织数量 @@ -63,8 +65,7 @@ class UsersController < ApplicationController def fan_users watchers = @user.watchers.includes(:user).order("watchers.created_at desc") - watchers = watchers.joins(:user).where("LOWER(CONCAT_WS(users.lastname, users.firstname, users.login, users.nickname)) LIKE ?", "%#{params[:search].split(" ").join('|')}%") if params[:search].present? - + watchers = watchers.joins(:user).merge(User.like(params[:search])) @watchers_count = watchers.size @watchers = paginate(watchers) end diff --git a/app/controllers/users_for_private_messages_controller.rb b/app/controllers/users_for_private_messages_controller.rb index 3a53a1af8..8f73bb36b 100644 --- a/app/controllers/users_for_private_messages_controller.rb +++ b/app/controllers/users_for_private_messages_controller.rb @@ -11,7 +11,7 @@ class UsersForPrivateMessagesController < ApplicationController return end - users = users.where('LOWER(CONCAT_WS(lastname, firstname, nickname)) LIKE ?', "%#{keyword}%") + users = users.like(keyword) @users = users.limit(10).includes(:user_extension) end diff --git a/app/controllers/versions_controller.rb b/app/controllers/versions_controller.rb index 02700fdf5..f5d09ed3b 100644 --- a/app/controllers/versions_controller.rb +++ b/app/controllers/versions_controller.rb @@ -7,8 +7,6 @@ class VersionsController < ApplicationController def index return render_not_found unless @project.has_menu_permission("versions") @user_admin_or_member = current_user.present? && (current_user.admin || @project.member?(current_user)) - order_name = params[:order_name] || "created_on" - order_type = params[:order_type] || "desc" status = params[:status] versions = @project.versions.version_includes @open_versions_size = versions.where(status: "open")&.size @@ -27,9 +25,6 @@ class VersionsController < ApplicationController end def show - order_name = params[:order_name] || "created_on" - order_type = params[:order_type] || "desc" - version_issues = @version.issues.issue_includes status_type = params[:status_type] || "1" @@ -167,4 +162,12 @@ class VersionsController < ApplicationController end end + def order_name + Version.column_names.include?(params[:order_name]) ? params[:order_name] : 'created_on' + end + + def order_type + %w(desc asc).include?(params[:order_type]) ? params[:order_type] : 'desc' + end + end diff --git a/app/controllers/weapps/courses_controller.rb b/app/controllers/weapps/courses_controller.rb index 72782f5cc..d7ac903ed 100644 --- a/app/controllers/weapps/courses_controller.rb +++ b/app/controllers/weapps/courses_controller.rb @@ -86,7 +86,7 @@ class Weapps::CoursesController < Weapps::BaseController end if search.present? - @teacher_list = @teacher_list.joins(:user).where("LOWER(CONCAT_WS(users.lastname, users.firstname, users.nickname)) like ?", "%#{search}%") + @teacher_list = @teacher_list.joins(:user).where("LOWER(CONCAT(users.lastname, users.firstname)) like ? OR users.nickname like ?", "%#{search}%", "%#{search}%") end @teacher_list_size = @teacher_list.size @@ -127,8 +127,8 @@ class Weapps::CoursesController < Weapps::BaseController @students = CourseMember.students(@course) if search.present? - @students = @students.joins(user: :user_extension).where("LOWER(CONCAT_WS(users.lastname, users.firstname, users.nickname)) like ? or - user_extensions.student_id like ?", "%#{search}%", "%#{search}%") + @students = @students.joins(user: :user_extension).where("LOWER(CONCAT(users.lastname, users.firstname)) like ? or users.nickname like ? or + user_extensions.student_id like ?", "%#{search}%", "%#{search}%", "%#{search}%") end if course_group_id.present? diff --git a/app/controllers/zips_controller.rb b/app/controllers/zips_controller.rb index 0c226211f..287891403 100644 --- a/app/controllers/zips_controller.rb +++ b/app/controllers/zips_controller.rb @@ -86,7 +86,7 @@ class ZipsController < ApplicationController #搜索 if params[:search].present? - @ex_users = @ex_users.joins(user: :user_extension).where("CONCAT_WS(lastname, firstname, nickname) like ? OR student_id like ?", "%#{params[:search]}%", "%#{params[:search]}%") + @ex_users = @ex_users.joins(user: :user_extension).where("CONCAT(lastname, firstname) like ? OR nickname like ? OR student_id like ?", "%#{params[:search]}%", "%#{params[:search]}%", "%#{params[:search]}%") end default_ex_users_size = @ex_users&.size @@ -130,8 +130,8 @@ class ZipsController < ApplicationController end unless params[:search].blank? - @all_student_works = @all_student_works.joins(user: :user_extension).where("CONCAT_WS(lastname, firstname, nickname) like ? - or student_id like ?", "%#{params[:search]}%", "%#{params[:search]}%") + @all_student_works = @all_student_works.joins(user: :user_extension).where("CONCAT(lastname, firstname) like ? or nickname like ? + or student_id like ?", "%#{params[:search]}%", "%#{params[:search]}%", "%#{params[:search]}%") end student_work_sizes = @all_student_works&.size diff --git a/app/docs/slate/source/includes/_projects.md b/app/docs/slate/source/includes/_projects.md index 87a37c45b..2b6dd76d0 100644 --- a/app/docs/slate/source/includes/_projects.md +++ b/app/docs/slate/source/includes/_projects.md @@ -1,5 +1,91 @@ # Projects +## 申请加入项目 +申请加入项目 + +> 示例: + +```shell +curl -X POST http://localhost:3000/api/applied_projects.json +``` + +```javascript +await octokit.request('POST /api/appliedr_projects.json') +``` + +### HTTP 请求 +`POST /api/applied_projects.json` + +### 请求参数 +参数 | 必选 | 默认 | 类型 | 字段说明 +--------- | ------- | ------- | -------- | ---------- +|applied_project.code |是| |string |邀请码 | +|applied_project.role |否| |string |项目权限,reporter: 报告者, developer: 开发者,manager:管理员 | + +> 请求的JSON示例 + +```json +{ + "applied_project": { + "code": "1una34", + "role": "developer" + } +} +``` + +### 返回字段说明 +参数 | 类型 | 字段说明 +--------- | ----------- | ----------- +|id |int |申请id | +|status |string |申请状态,canceled:取消,common:正在申请, accept:已接受,refuse:已拒绝| +|time_ago |string |项目申请创建的时间 | +|project.id |int |申请项目的id | +|project.identifier |string |申请项目的标识 | +|project.name |string |申请项目的名称 | +|project.description |string |申请项目的描述 | +|project.is_public |bool |申请项目是否公开 | +|project.owner.id |bool |申请项目拥有者id | +|project.owner.type |string |申请项目拥有者类型 | +|project.owner.name |string |申请项目拥有者昵称 | +|project.owner.login |string |申请项目拥有者标识 | +|project.owner.image_url |string |申请项目拥有者头像 | +|user.id |int |申请创建者的id | +|user.type |string |申请创建者的类型 | +|user.name |string |申请创建者的名称 | +|user.login |string |申请创建者的标识 | +|user.image_url |string |申请创建者头像 | +> 返回的JSON示例: + +```json +{ + "project": { + "id": 74, + "identifier": "hehuisssjssjjsjs", + "name": "hehuisssjssjjsjs", + "description": "wwww", + "is_public": false, + "owner": { + "id": 10, + "type": "User", + "name": "testforge1", + "login": "testforge1", + "image_url": "system/lets/letter_avatars/2/T/19_237_174/120.png" + } + }, + "user": { + "id": 6, + "type": "User", + "name": "何慧", + "login": "yystopf", + "image_url": "images/avatars/User/6?t=1622513134" + }, + "id": 7, + "status": "common", + "created_at": "2021-06-09 16:41", + "time_ago": "1分钟前" +} +``` + ## 获取项目列表 获取项目列表,也可以更加相关条件过滤搜素 diff --git a/app/docs/slate/source/includes/_users.md b/app/docs/slate/source/includes/_users.md index 72bf724a5..bb4fd2b66 100644 --- a/app/docs/slate/source/includes/_users.md +++ b/app/docs/slate/source/includes/_users.md @@ -1,7 +1,7 @@ # Users @@ -1002,11 +1002,11 @@ await octokit.request('GET /api/users/:login/applied_messages.json') |applied.user.name |string |通知主体的迁移创建者的名称 | |applied.user.login |string |通知主体的迁移创建者的标识 | |applied.user.image_url |string |通知主体的迁移创建者头像 | -|applied.owner.id |int |通知主体的迁移接受者的id | -|applied.owner.type |string |通知主体的迁移接受者的类型 | -|applied.owner.name |string |通知主体的迁移接受者的名称 | -|applied.owner.login |string |通知主体的迁移接受者的标识 | -|applied.owner.image_url |string |通知主体的迁移接受者头像 | +|applied_user.id |int |通知发起者的id | +|applied_user.type |string |通知发起者的类型 | +|applied_user.name |string |通知发起者的名称 | +|applied_user.login |string |通知发起者的标识 | +|applied_user.image_url |string |通知发起者头像 | |applied_type |string |通知类型 | |name |string | 通知内容 | |viewed |string|是否已读,waiting:未读,viewed:已读| @@ -1020,6 +1020,48 @@ await octokit.request('GET /api/users/:login/applied_messages.json') { "total_count": 5, "applied_messages": [ + { + "applied": { + "project": { + "id": 74, + "identifier": "hehuisssjssjjsjs", + "name": "hehuisssjssjjsjs", + "description": "wwww", + "is_public": false, + "owner": { + "id": 10, + "type": "User", + "name": "testforge1", + "login": "testforge1", + "image_url": "system/lets/letter_avatars/2/T/19_237_174/120.png" + } + }, + "user": { + "id": 6, + "type": "User", + "name": "何慧", + "login": "yystopf", + "image_url": "images/avatars/User/6?t=1622513134" + }, + "id": 6, + "status": "accepted", + "created_at": "2021-06-09 16:34", + "time_ago": "1分钟前" + }, + "applied_user": { + "id": 6, + "type": "User", + "name": "何慧", + "login": "yystopf", + "image_url": "images/avatars/User/6?t=1622513134" + }, + "applied_type": "AppliedProject", + "name": "已通过你加入【hehuisssjssjjsjs】仓库的申请。", + "viewed": "waiting", + "status": "successed", + "created_at": "2021-06-09 16:34", + "time_ago": "1分钟前" + }, { "applied": { "project": { @@ -1344,4 +1386,240 @@ await octokit.request('GET /api/users/:login/applied_transfer_projects/:id/refus "created_at": "2021-04-25 18:06", "time_ago": "16小时前" } +``` + + +## 待办事项-项目申请 +待办事项-项目申请 + +> 示例: + +```shell +curl -X GET http://localhost:3000/api/users/yystopf/applied_projects.json +``` + +```javascript +await octokit.request('GET /api/users/:login/applied_projects.json') +``` + +### HTTP 请求 +`GET /api/users/:login/applied_projects.json` + +### 请求字段说明: +参数 | 类型 | 字段说明 +--------- | ----------- | ----------- +|login |string |用户标识 | + +### 返回字段说明: +参数 | 类型 | 字段说明 +--------- | ----------- | ----------- +|id |int |申请id | +|status |string |申请状态,canceled:取消,common:正在申请, accept:已接受,refuse:已拒绝| +|time_ago |string |申请创建的时间 | +|project.id |int |申请项目的id | +|project.identifier |string |申请项目的标识 | +|project.name |string |申请项目的名称 | +|project.description |string |申请项目的描述 | +|project.is_public |bool |申请项目是否公开 | +|project.owner.id |bool |申请项目拥有者id | +|project.owner.type |string |申请项目拥有者类型 | +|project.owner.name |string |申请项目拥有者昵称 | +|project.owner.login |string |申请项目拥有者标识 | +|project.owner.image_url |string |申请项目拥有者头像 | +|user.id |int |申请创建者的id | +|user.type |string |申请创建者的类型 | +|user.name |string |申请创建者的名称 | +|user.login |string |申请创建者的标识 | +|user.image_url |string |申请创建者头像 | + + +> 返回的JSON示例: + +```json +{ + "total_count": 4, + "applied_transfer_projects": [ + { + "project": { + "id": 74, + "identifier": "hehuisssjssjjsjs", + "name": "hehuisssjssjjsjs", + "description": "wwww", + "is_public": false, + "owner": { + "id": 10, + "type": "User", + "name": "testforge1", + "login": "testforge1", + "image_url": "system/lets/letter_avatars/2/T/19_237_174/120.png" + } + }, + "user": { + "id": 6, + "type": "User", + "name": "何慧", + "login": "yystopf", + "image_url": "images/avatars/User/6?t=1622513134" + }, + "id": 7, + "status": "common", + "created_at": "2021-06-09 16:41", + "time_ago": "7分钟前" + }, + ... + ] +} +``` + +## 用户接受申请 +用户接受申请 + +> 示例: + +```shell +curl -X POST http://localhost:3000/api/users/yystopf/applied_projects/2/accept.json +``` + +```javascript +await octokit.request('GET /api/users/:login/applied_projects/:id/accept.json') +``` + +### HTTP 请求 +`GET /api/users/:login/applied_projects/:id/accept.json` + +### 请求字段说明: +参数 | 类型 | 字段说明 +--------- | ----------- | ----------- +|login |string |用户标识 | +|id |int |申请id | + +### 返回字段说明: +参数 | 类型 | 字段说明 +--------- | ----------- | ----------- +|id |int |申请id | +|status |string |申请状态,canceled:取消,common:正在申请, accept:已接受,refuse:已拒绝| +|time_ago |string |申请创建的时间 | +|project.id |int |申请项目的id | +|project.identifier |string |申请项目的标识 | +|project.name |string |申请项目的名称 | +|project.description |string |申请项目的描述 | +|project.is_public |bool |申请项目是否公开 | +|project.owner.id |bool |申请项目拥有者id | +|project.owner.type |string |申请项目拥有者类型 | +|project.owner.name |string |申请项目拥有者昵称 | +|project.owner.login |string |申请项目拥有者标识 | +|project.owner.image_url |string |申请项目拥有者头像 | +|user.id |int |申请创建者的id | +|user.type |string |申请创建者的类型 | +|user.name |string |申请创建者的名称 | +|user.login |string |申请创建者的标识 | +|user.image_url |string |申请创建者头像 | + + +> 返回的JSON示例: + +```json +{ + "project": { + "id": 74, + "identifier": "hehuisssjssjjsjs", + "name": "hehuisssjssjjsjs", + "description": "wwww", + "is_public": false, + "owner": { + "id": 10, + "type": "User", + "name": "testforge1", + "login": "testforge1", + "image_url": "system/lets/letter_avatars/2/T/19_237_174/120.png" + } + }, + "user": { + "id": 6, + "type": "User", + "name": "何慧", + "login": "yystopf", + "image_url": "images/avatars/User/6?t=1622513134" + }, + "id": 7, + "status": "accept", + "created_at": "2021-06-09 16:41", + "time_ago": "7分钟前" +} +``` + +## 用户拒绝申请 +用户拒绝申请 + +> 示例: + +```shell +curl -X POST http://localhost:3000/api/users/yystopf/applied_projects/2/refuse.json +``` + +```javascript +await octokit.request('GET /api/users/:login/applied_projects/:id/refuse.json') +``` + +### HTTP 请求 +`GET /api/users/:login/applied_projects/:id/refuse.json` + +### 请求字段说明: +参数 | 类型 | 字段说明 +--------- | ----------- | ----------- +|login |string |用户标识 | +|id |int |申请id | + +### 返回字段说明: +参数 | 类型 | 字段说明 +--------- | ----------- | ----------- +|id |int |申请id | +|status |string |申请状态,canceled:取消,common:正在申请, accept:已接受,refuse:已拒绝| +|time_ago |string |申请创建的时间 | +|project.id |int |申请项目的id | +|project.identifier |string |申请项目的标识 | +|project.name |string |申请项目的名称 | +|project.description |string |申请项目的描述 | +|project.is_public |bool |申请项目是否公开 | +|project.owner.id |bool |申请项目拥有者id | +|project.owner.type |string |申请项目拥有者类型 | +|project.owner.name |string |申请项目拥有者昵称 | +|project.owner.login |string |申请项目拥有者标识 | +|project.owner.image_url |string |申请项目拥有者头像 | +|user.id |int |申请创建者的id | +|user.type |string |申请创建者的类型 | +|user.name |string |申请创建者的名称 | +|user.login |string |申请创建者的标识 | +|user.image_url |string |申请创建者头像 | + +> 返回的JSON示例: + +```json +{ + "project": { + "id": 74, + "identifier": "hehuisssjssjjsjs", + "name": "hehuisssjssjjsjs", + "description": "wwww", + "is_public": false, + "owner": { + "id": 10, + "type": "User", + "name": "testforge1", + "login": "testforge1", + "image_url": "system/lets/letter_avatars/2/T/19_237_174/120.png" + } + }, + "user": { + "id": 6, + "type": "User", + "name": "何慧", + "login": "yystopf", + "image_url": "images/avatars/User/6?t=1622513134" + }, + "id": 7, + "status": "accept", + "created_at": "2021-06-09 16:41", + "time_ago": "7分钟前" +} ``` \ No newline at end of file diff --git a/app/helpers/application_helper.rb b/app/helpers/application_helper.rb index ccc45df0e..a1aea4aeb 100644 --- a/app/helpers/application_helper.rb +++ b/app/helpers/application_helper.rb @@ -438,4 +438,14 @@ module ApplicationHelper return nil if str.blank? Base64.decode64 str end + + def render_admin_statistics_item + url = Rails.application.config_for(:configuration)["admin_statistics_url"] + + return if url.blank? + content_tag(:li) do + sidebar_item(url, "数据统计", icon: 'bar-chart', controller: 'root') + end + end + end diff --git a/app/interactors/projects/add_member_interactor.rb b/app/interactors/projects/add_member_interactor.rb index a3fe9e33e..0c3785c69 100644 --- a/app/interactors/projects/add_member_interactor.rb +++ b/app/interactors/projects/add_member_interactor.rb @@ -23,7 +23,7 @@ module Projects ActiveRecord::Base.transaction do gitea_result = Gitea::Repository::Members::AddService.new(owner, project.identifier, collaborator.login, permission).call if gitea_result.status == 204 - project.add_member!(collaborator.id) + project.add_member!(collaborator.id, role_name) end fail!(nil) end @@ -38,5 +38,20 @@ module Projects @error = error end + def role_name + case permission + when 'read' + 'Reporter' + when 'write' + 'Developer' + when 'admin' + 'Manager' + when 'owner' + 'Manager' + else + 'Reporter' + end + end + end end diff --git a/app/jobs/send_join_project_applied_message_job.rb b/app/jobs/send_join_project_applied_message_job.rb new file mode 100644 index 000000000..557878ede --- /dev/null +++ b/app/jobs/send_join_project_applied_message_job.rb @@ -0,0 +1,27 @@ +class SendJoinProjectAppliedMessageJob < ApplicationJob + queue_as :default + + def perform(applied_project, applied_user, message_status) + project = applied_project.project + return unless project.present? + return unless applied_user.present? + return unless applied_project.user.present? + AppliedMessage.find_or_create_by!(user_id: applied_project.user_id, + applied: applied_project, + status: message_status, + name: build_name(project.name, message_status), + applied_user_id: applied_user.id, + project_id: project.id) + end + + private + def build_name(repo_name, message_status, applied_name="") + case message_status + when 'successed' + return "已通过你加入【#{repo_name}】仓库的申请。" + when 'failure' + return "已拒绝你加入【#{repo_name}】仓库的申请。" + end + "" + end +end \ No newline at end of file diff --git a/app/models/applied_message.rb b/app/models/applied_message.rb index b3ebad34e..5c942b7b3 100644 --- a/app/models/applied_message.rb +++ b/app/models/applied_message.rb @@ -17,6 +17,7 @@ # class AppliedMessage < ApplicationRecord + self.table_name = 'forge_applied_messages' belongs_to :user belongs_to :applied, polymorphic: true belongs_to :project diff --git a/app/models/applied_project.rb b/app/models/applied_project.rb index 68095d0ea..e0b4b6c48 100644 --- a/app/models/applied_project.rb +++ b/app/models/applied_project.rb @@ -7,14 +7,19 @@ # user_id :integer not null # role :integer default("0") # status :integer default("0") +# created_at :datetime +# updated_at :datetime # class AppliedProject < ApplicationRecord + self.table_name = "forge_applied_projects" belongs_to :user belongs_to :project has_many :applied_messages, as: :applied, dependent: :destroy - has_many :forge_activities, as: :forge_act, dependent: :destroy + # has_many :forge_activities, as: :forge_act, dependent: :destroy + + enum role: {manager: 3, developer: 4, reporter: 5} + enum status: {canceled: -1, common: 0, accepted: 1, refused: 2} # -1 已取消 0 待操作 1 已接收 2 已拒绝 - scope :pending, -> { where(status: 0) } end diff --git a/app/models/attachment.rb b/app/models/attachment.rb index 3bfc9a8ce..c6a50d93e 100644 --- a/app/models/attachment.rb +++ b/app/models/attachment.rb @@ -37,6 +37,7 @@ # index_attachments_on_quotes (quotes) # + class Attachment < ApplicationRecord include BaseModel include Publicable @@ -51,7 +52,7 @@ class Attachment < ApplicationRecord # 二级目录 # belongs_to :course_second_category, optional: true - scope :by_filename_or_user_name, -> (keywords) { joins(:author).where("filename like :search or LOWER(CONCAT_WS(users.lastname, users.firstname, users.nickname)) LIKE :search", + scope :by_filename_or_user_name, -> (keywords) { joins(:author).where("filename like :search or LOWER(CONCAT(users.lastname, users.firstname)) LIKE :search OR users.nickname LIKE :search", :search => "%#{keywords.split(" ").join('|')}%") unless keywords.blank? } scope :by_keywords, -> (keywords) { where("filename LIKE ?", "%#{keywords.split(" ").join('|')}%") unless keywords.blank? } scope :ordered, -> (opts = {}) { order("#{opts[:sort_type]} #{opts[:sort] == 1 ? 'asc': 'desc'}") } diff --git a/app/models/ci/user.rb b/app/models/ci/user.rb index cd6246753..c263a1723 100644 --- a/app/models/ci/user.rb +++ b/app/models/ci/user.rb @@ -46,6 +46,10 @@ # is_sync_pwd :boolean default("1") # watchers_count :integer default("0") # devops_step :integer default("0") +# sponsor_certification :integer default("0") +# sponsor_num :integer default("0") +# sponsored_num :integer default("0") +# award_time :datetime # # Indexes # diff --git a/app/models/concerns/dcodes.rb b/app/models/concerns/dcodes.rb new file mode 100644 index 000000000..6c2ff13b0 --- /dev/null +++ b/app/models/concerns/dcodes.rb @@ -0,0 +1,28 @@ +module Dcodes + DCODES = %W(1 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 A B C D E F G H I J K L M N O P Q R S T U V W X Y Z) + + extend ActiveSupport::Concern + + def generate_dcode(field, num, pre='') + code = DCODES.sample(num).join + while self.class.exists?("#{field}": pre+code) do + code = DCODES.sample(num).join + end + code + end + + def init_project_invite_code + while Project.where(invite_code: nil).size > 0 do + projects = Project.where(invite_code: nil).limit(1000) + set_sql = "" + projects.each do |p| + set_sql += "WHEN #{p.id} THEN '#{DCODES.sample(6).join}' " + end + sql = "UPDATE projects SET invite_code = CASE id "+ set_sql+ "END WHERE id IN(#{projects.ids.join(",")})" + Project.connection.execute(sql) + end + repeat_codes = Project.group(:invite_code).count.select{|k,v| v>1} + Project.where(invite_code: repeat_code.keys).update_all(invite_code: nil) + end + +end \ No newline at end of file diff --git a/app/models/concerns/project_operable.rb b/app/models/concerns/project_operable.rb index e016ca1dc..79d099a2e 100644 --- a/app/models/concerns/project_operable.rb +++ b/app/models/concerns/project_operable.rb @@ -65,7 +65,7 @@ module ProjectOperable if owner.is_a?(User) managers.exists?(user_id: user.id) elsif owner.is_a?(Organization) - managers.exists?(user_id: user.id) || owner.is_admin?(user.id) + managers.exists?(user_id: user.id) || owner.is_only_admin?(user.id) else false end @@ -76,7 +76,7 @@ module ProjectOperable if owner.is_a?(User) developers.exists?(user_id: user.id) elsif owner.is_a?(Organization) - developers.exists?(user_id: user.id) || owner.is_write?(user.id) + developers.exists?(user_id: user.id) || owner.is_only_write?(user.id) else false end diff --git a/app/models/issue.rb b/app/models/issue.rb index b43f5a3d2..826ad3a5b 100644 --- a/app/models/issue.rb +++ b/app/models/issue.rb @@ -6,7 +6,7 @@ # tracker_id :integer not null # project_id :integer not null # subject :string(255) default(""), not null -# description :text(65535) +# description :text(4294967295) # due_date :date # category_id :integer # status_id :integer not null @@ -14,7 +14,6 @@ # priority_id :integer not null # fixed_version_id :integer # author_id :integer not null -# lock_version :integer default("0"), not null # created_on :datetime # updated_on :datetime # start_date :date @@ -28,7 +27,7 @@ # closed_on :datetime # project_issues_index :integer # issue_type :string(255) -# token :string(255) +# token :integer default("0") # issue_tags_value :string(255) # is_lock :boolean default("0") # issue_classify :string(255) @@ -74,7 +73,7 @@ class Issue < ApplicationRecord scope :issue_issue, ->{where(issue_classify: [nil,"issue"])} scope :issue_pull_request, ->{where(issue_classify: "pull_request")} scope :issue_index_includes, ->{includes(:tracker, :priority, :version, :issue_status, :journals,:issue_tags,user: :user_extension)} - + scope :closed, ->{where(status_id: 5)} after_update :change_versions_count after_save :reset_cache_data after_destroy :update_closed_issues_count_in_project!, :reset_cache_data diff --git a/app/models/license.rb b/app/models/license.rb index 0a14fb85e..dcd5528fb 100644 --- a/app/models/license.rb +++ b/app/models/license.rb @@ -7,6 +7,7 @@ # content :text(65535) # created_at :datetime not null # updated_at :datetime not null +# is_secret :boolean default("0") # class License < ApplicationRecord diff --git a/app/models/member.rb b/app/models/member.rb index e72ae7c6b..408710a03 100644 --- a/app/models/member.rb +++ b/app/models/member.rb @@ -11,6 +11,7 @@ # course_group_id :integer default("0") # is_collect :integer default("1") # graduation_group_id :integer default("0") +# is_apply_signature :boolean default("0") # # Indexes # diff --git a/app/models/organization.rb b/app/models/organization.rb index 988ecd7fb..666e13ff2 100644 --- a/app/models/organization.rb +++ b/app/models/organization.rb @@ -46,6 +46,10 @@ # is_sync_pwd :boolean default("1") # watchers_count :integer default("0") # devops_step :integer default("0") +# sponsor_certification :integer default("0") +# sponsor_num :integer default("0") +# sponsored_num :integer default("0") +# award_time :datetime # # Indexes # @@ -106,6 +110,14 @@ class Organization < Owner team_users.joins(:team).where(user_id: user_id, teams: {authorize: %w(read write admin owner)}).present? end + def is_only_admin?(user_id) + team_users.joins(:team).where(user_id: user_id, teams: {authorize: %w(admin)}).present? + end + + def is_only_write?(user_id) + team_users.joins(:team).where(user_id: user_id, teams: {authorize: %w(write)}).present? + end + def is_only_read?(user_id) team_users.joins(:team).where(user_id: user_id, teams: {authorize: %w(read)}).present? end diff --git a/app/models/praise_tread.rb b/app/models/praise_tread.rb index 04008eaf6..5a9c19164 100644 --- a/app/models/praise_tread.rb +++ b/app/models/praise_tread.rb @@ -1,19 +1,20 @@ -# == Schema Information -# -# Table name: praise_treads -# -# id :integer not null, primary key -# user_id :integer not null -# praise_tread_object_id :integer -# praise_tread_object_type :string(255) -# praise_or_tread :integer default("1") -# created_at :datetime not null -# updated_at :datetime not null -# -# Indexes -# -# praise_tread (praise_tread_object_id,praise_tread_object_type) -# +# == Schema Information +# +# Table name: praise_treads +# +# id :integer not null, primary key +# user_id :integer not null +# praise_tread_object_id :integer +# praise_tread_object_type :string(255) +# praise_or_tread :integer default("1") +# created_at :datetime not null +# updated_at :datetime not null +# +# Indexes +# +# praise_tread (praise_tread_object_id,praise_tread_object_type) +# + class PraiseTread < ApplicationRecord belongs_to :user diff --git a/app/models/project.rb b/app/models/project.rb index b61f05689..eb8a0bbf4 100644 --- a/app/models/project.rb +++ b/app/models/project.rb @@ -1,63 +1,78 @@ -# == Schema Information -# -# Table name: projects -# -# id :integer not null, primary key -# name :string(255) default(""), not null -# description :text(65535) -# homepage :string(255) default("") -# is_public :boolean default("1"), not null -# parent_id :integer -# created_on :datetime -# updated_on :datetime -# identifier :string(255) -# status :integer default("1"), not null -# lft :integer -# rgt :integer -# inherit_members :boolean default("0"), not null -# project_type :integer default("0") -# hidden_repo :boolean default("0"), not null -# attachmenttype :integer default("1") -# user_id :integer -# dts_test :integer default("0") -# enterprise_name :string(255) -# organization_id :integer -# project_new_type :integer -# gpid :integer -# forked_from_project_id :integer -# forked_count :integer default("0") -# publish_resource :integer default("0") -# visits :integer default("0") -# hot :integer default("0") -# invite_code :string(255) -# qrcode :string(255) -# qrcode_expiretime :integer default("0") -# script :text(65535) -# training_status :integer default("0") -# rep_identifier :string(255) -# project_category_id :integer -# project_language_id :integer -# license_id :integer -# ignore_id :integer -# praises_count :integer default("0") -# watchers_count :integer default("0") -# issues_count :integer default("0") -# pull_requests_count :integer default("0") -# -# Indexes -# -# index_projects_on_forked_from_project_id (forked_from_project_id) -# index_projects_on_identifier (identifier) -# index_projects_on_is_public (is_public) -# index_projects_on_lft (lft) -# index_projects_on_name (name) -# index_projects_on_platform (platform) -# index_projects_on_project_type (project_type) -# index_projects_on_recommend (recommend) -# index_projects_on_rgt (rgt) -# index_projects_on_status (status) -# index_projects_on_updated_on (updated_on) -# +# == Schema Information +# +# Table name: projects +# +# id :integer not null, primary key +# name :string(255) default(""), not null +# description :text(4294967295) +# homepage :string(255) default("") +# is_public :boolean default("1"), not null +# parent_id :integer +# created_on :datetime +# updated_on :datetime +# identifier :string(255) +# status :integer default("1"), not null +# lft :integer +# rgt :integer +# inherit_members :boolean default("0"), not null +# project_type :integer default("0") +# hidden_repo :boolean default("0"), not null +# attachmenttype :integer default("1") +# user_id :integer +# dts_test :integer default("0") +# enterprise_name :string(255) +# organization_id :integer +# project_new_type :integer +# gpid :integer +# forked_from_project_id :integer +# forked_count :integer default("0") +# publish_resource :integer default("0") +# visits :integer default("0") +# hot :integer default("0") +# invite_code :string(255) +# qrcode :string(255) +# qrcode_expiretime :integer default("0") +# script :text(65535) +# training_status :integer default("0") +# rep_identifier :string(255) +# project_category_id :integer +# project_language_id :integer +# license_id :integer +# ignore_id :integer +# praises_count :integer default("0") +# watchers_count :integer default("0") +# issues_count :integer default("0") +# pull_requests_count :integer default("0") +# language :string(255) +# versions_count :integer default("0") +# issue_tags_count :integer default("0") +# closed_issues_count :integer default("0") +# open_devops :boolean default("0") +# gitea_webhook_id :integer +# open_devops_count :integer default("0") +# recommend :boolean default("0") +# platform :integer default("0") +# default_branch :string(255) default("master") +# website :string(255) +# order_index :integer default("0") +# lesson_url :string(255) +# +# Indexes +# +# index_projects_on_forked_from_project_id (forked_from_project_id) +# index_projects_on_identifier (identifier) +# index_projects_on_invite_code (invite_code) +# index_projects_on_is_public (is_public) +# index_projects_on_lft (lft) +# index_projects_on_name (name) +# index_projects_on_platform (platform) +# index_projects_on_project_type (project_type) +# index_projects_on_recommend (recommend) +# index_projects_on_rgt (rgt) +# index_projects_on_status (status) +# index_projects_on_updated_on (updated_on) +# + @@ -66,6 +81,7 @@ class Project < ApplicationRecord include Publicable include Watchable include ProjectOperable + include Dcodes # common:开源托管项目 # mirror:普通镜像项目,没有定时同步功能 @@ -106,6 +122,7 @@ class Project < ApplicationRecord has_many :has_pinned_users, through: :pinned_projects, source: :user after_save :check_project_members, :reset_cache_data + before_save :set_invite_code after_destroy :reset_cache_data scope :project_statics_select, -> {select(:id,:name, :is_public, :identifier, :status, :project_type, :user_id, :forked_count, :visits, :project_category_id, :project_language_id, :license_id, :ignore_id, :watchers_count, :created_on)} scope :no_anomory_projects, -> {where("projects.user_id is not null and projects.user_id != ?", 2)} @@ -123,6 +140,12 @@ class Project < ApplicationRecord self.reset_user_cache_async_job(self.owner) end + def set_invite_code + if self.invite_code.nil? + self.invite_code= self.generate_dcode('invite_code', 6) + end + end + def self.search_project(search) ransack(name_or_identifier_cont: search) end diff --git a/app/models/pull_request.rb b/app/models/pull_request.rb index 97f4a9164..4226d561b 100644 --- a/app/models/pull_request.rb +++ b/app/models/pull_request.rb @@ -16,6 +16,11 @@ # head :string(255) # base :string(255) # issue_id :integer +# fork_project_id :integer +# is_original :boolean default("0") +# comments_count :integer default("0") +# commits_count :integer default("0") +# files_count :integer default("0") # class PullRequest < ApplicationRecord @@ -33,6 +38,9 @@ class PullRequest < ApplicationRecord has_many :project_trends, as: :trend, dependent: :destroy has_many :attachments, as: :container, dependent: :destroy + scope :merged_and_closed, ->{where.not(status: 0)} + scope :opening, -> {where(status: 0)} + after_save :reset_cache_data after_destroy :reset_cache_data diff --git a/app/models/user.rb b/app/models/user.rb index 9d917f4c8..c943e5efa 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -46,6 +46,10 @@ # is_sync_pwd :boolean default("1") # watchers_count :integer default("0") # devops_step :integer default("0") +# sponsor_certification :integer default("0") +# sponsor_num :integer default("0") +# sponsored_num :integer default("0") +# award_time :datetime # # Indexes # @@ -170,7 +174,7 @@ class User < Owner # Groups and active users scope :active, lambda { where(status: STATUS_ACTIVE) } scope :like, lambda { |keywords| - sql = "CONCAT_WS(lastname, firstname, nickname) LIKE :search OR login LIKE :search OR mail LIKE :search OR nickname LIKE :search" + sql = "CONCAT(lastname, firstname) LIKE :search OR nickname LIKE :search OR login LIKE :search OR mail LIKE :search OR nickname LIKE :search" where(sql, :search => "%#{keywords.split(" ").join('|')}%") unless keywords.blank? } @@ -206,6 +210,13 @@ class User < Owner return Project.from("( #{ normal_projects} UNION #{ org_projects } ) AS projects").distinct end + # 用户管理的所有项目 + def full_admin_projects + normal_projects = Project.joins(members: :roles).where(roles: {name: 'Manager'}, members: {user_id: self.id}).to_sql + org_projects = Project.joins(teams: :team_users).where(teams: {authorize: %w(admin owner)}, team_users: {user_id: self.id}).to_sql + return Project.from("( #{ normal_projects} UNION #{ org_projects } ) AS projects").distinct + end + def name login end diff --git a/app/models/version.rb b/app/models/version.rb index 650d5a656..77cb58a7e 100644 --- a/app/models/version.rb +++ b/app/models/version.rb @@ -29,6 +29,8 @@ class Version < ApplicationRecord belongs_to :user, optional: true scope :version_includes, ->{includes(:issues, :user)} + scope :closed, ->{where(status: 'closed')} + scope :opening, ->{where(status: 'open')} # def open_issues_count # issues.select(:id,:status_id).where(status_id: [1,2,3,4,6]).size diff --git a/app/queries/admins/apply_item_bank_query.rb b/app/queries/admins/apply_item_bank_query.rb index 922d136ef..8608e238d 100644 --- a/app/queries/admins/apply_item_bank_query.rb +++ b/app/queries/admins/apply_item_bank_query.rb @@ -26,7 +26,7 @@ class Admins::ApplyItemBankQuery < ApplicationQuery keyword = params[:keyword].to_s.strip if keyword.present? applies = applies.joins(user: { user_extension: :school }) - .where('CONCAT_WS(lastname,firstname, nickname) LIKE :keyword OR schools.name LIKE :keyword', keyword: "%#{keyword}%") + .where('CONCAT(lastname,firstname) LIKE :keyword OR users.nickname LIKE :keyword OR schools.name LIKE :keyword', keyword: "%#{keyword}%") end custom_sort(applies, params[:sort_by], params[:sort_direction]) diff --git a/app/queries/admins/apply_user_authentication_query.rb b/app/queries/admins/apply_user_authentication_query.rb index 60b781d1c..741f28e5a 100644 --- a/app/queries/admins/apply_user_authentication_query.rb +++ b/app/queries/admins/apply_user_authentication_query.rb @@ -26,7 +26,7 @@ class Admins::ApplyUserAuthenticationQuery < ApplicationQuery keyword = params[:keyword].to_s.strip if keyword.present? applies = applies.joins(user: { user_extension: :school }) - .where('CONCAT_WS(lastname,firstname,nickname) LIKE :keyword OR schools.name LIKE :keyword', keyword: "%#{keyword}%") + .where('CONCAT(lastname,firstname) LIKE :keyword OR users.nickname LIKE :keyword OR schools.name LIKE :keyword', keyword: "%#{keyword}%") end custom_sort(applies, params[:sort_by], params[:sort_direction]) diff --git a/app/queries/admins/course_list_query.rb b/app/queries/admins/course_list_query.rb index b97a9cebe..84868b7d0 100644 --- a/app/queries/admins/course_list_query.rb +++ b/app/queries/admins/course_list_query.rb @@ -19,7 +19,7 @@ class Admins::CourseListQuery < ApplicationQuery case search_type when "0" course_lists = course_lists.joins(:user) - .where('CONCAT_WS(lastname, firstname, nickname) like :keyword', keyword: "%#{keyword}%") + .where('CONCAT(lastname, firstname) like :keyword OR users.nickname like :keyword', keyword: "%#{keyword}%") when "1" course_lists = course_lists.where('name like :keyword', keyword: "%#{keyword}%") end diff --git a/app/queries/admins/course_query.rb b/app/queries/admins/course_query.rb index b1466e22f..6fbbc002e 100644 --- a/app/queries/admins/course_query.rb +++ b/app/queries/admins/course_query.rb @@ -35,7 +35,7 @@ class Admins::CourseQuery < ApplicationQuery # 关键字 keyword = params[:keyword].to_s.strip if keyword - sql = 'CONCAT_WS(lastname, firstname, nickname) LIKE :keyword OR courses.name LIKE :keyword OR course_lists.name LIKE :keyword' + sql = 'CONCAT(lastname, firstname) LIKE :keyword OR users.nickname LIKE :keyword OR courses.name LIKE :keyword OR course_lists.name LIKE :keyword' courses = courses.joins(:teacher, :course_list).where(sql, keyword: "%#{keyword}%") end diff --git a/app/queries/admins/laboratory_shixun_query.rb b/app/queries/admins/laboratory_shixun_query.rb index 9b9bf6a21..da7867194 100644 --- a/app/queries/admins/laboratory_shixun_query.rb +++ b/app/queries/admins/laboratory_shixun_query.rb @@ -11,7 +11,7 @@ class Admins::LaboratoryShixunQuery < ApplicationQuery keyword = params[:keyword].to_s.strip if keyword.present? - like_sql = 'shixuns.name LIKE :keyword OR CONCAT_WS(users.lastname, users.firstname, users.nickname) LIKE :keyword' + like_sql = 'shixuns.name LIKE :keyword OR CONCAT(users.lastname, users.firstname) LIKE :keyword OR users.nickname LIKE :keyword' laboratory_shixuns = laboratory_shixuns.joins(shixun: :user).where(like_sql, keyword: "%#{keyword}%") end diff --git a/app/queries/admins/laboratory_subject_query.rb b/app/queries/admins/laboratory_subject_query.rb index 619aeb2c2..d47463332 100644 --- a/app/queries/admins/laboratory_subject_query.rb +++ b/app/queries/admins/laboratory_subject_query.rb @@ -11,7 +11,7 @@ class Admins::LaboratorySubjectQuery < ApplicationQuery keyword = params[:keyword].to_s.strip if keyword.present? - like_sql = 'subjects.name LIKE :keyword OR CONCAT_WS(users.lastname, users.firstname, users.nickname) LIKE :keyword' + like_sql = 'subjects.name LIKE :keyword OR CONCAT(users.lastname, users.firstname) LIKE :keyword OR users.nickname LIKE :keyword' laboratory_subjects = laboratory_subjects.joins(subject: :user).where(like_sql, keyword: "%#{keyword}%") end diff --git a/app/queries/admins/subject_query.rb b/app/queries/admins/subject_query.rb index df2ac40ef..3596c7715 100644 --- a/app/queries/admins/subject_query.rb +++ b/app/queries/admins/subject_query.rb @@ -40,7 +40,7 @@ class Admins::SubjectQuery < ApplicationQuery # 关键字 keyword = params[:keyword].to_s.strip if keyword - sql = 'CONCAT_WS(lastname, firstname, nickname) LIKE :keyword OR subjects.name LIKE :keyword' + sql = 'CONCAT(lastname, firstname) LIKE :keyword OR users.nickname LIKE :keyword OR subjects.name LIKE :keyword' subjects = subjects.joins(:user).where(sql, keyword: "%#{keyword}%") end diff --git a/app/queries/admins/user_query.rb b/app/queries/admins/user_query.rb index 670eef58d..f9506ddf5 100644 --- a/app/queries/admins/user_query.rb +++ b/app/queries/admins/user_query.rb @@ -30,14 +30,14 @@ class Admins::UserQuery < ApplicationQuery # 关键字检索 keyword = params[:keyword].to_s.strip.presence if keyword - sql = 'CONCAT_WS(lastname, firstname, nickname) LIKE :keyword OR login LIKE :keyword OR mail LIKE :keyword OR phone LIKE :keyword' + sql = 'CONCAT(lastname, firstname) LIKE :keyword OR nickname LIKE :keyword OR login LIKE :keyword OR mail LIKE :keyword OR phone LIKE :keyword' users = users.where(sql, keyword: "%#{keyword}%") end # 姓名 name = params[:name].to_s.strip.presence if name.present? - users = users.where('CONCAT_WS(lastname, firstname, nickname) LIKE :name', name: "%#{name}%") + users = users.where('CONCAT(lastname, firstname) LIKE :name OR nickname LIKE :name', name: "%#{name}%") end # 单位ID diff --git a/app/queries/projects/list_my_query.rb b/app/queries/projects/list_my_query.rb index c9f4544df..f275d63a5 100644 --- a/app/queries/projects/list_my_query.rb +++ b/app/queries/projects/list_my_query.rb @@ -55,8 +55,8 @@ class Projects::ListMyQuery < ApplicationQuery scope = q.result.includes(:project_category, :project_language,:owner, :repository, :has_pinned_users) - sort = params[:sort_by] || "updated_on" - sort_direction = params[:sort_direction] || "desc" + sort = Project.column_names.include?(params[:sort_by]) ? params[:sort_by] : "updated_on" + sort_direction = %w(desc asc).include?(params[:sort_direction]) ? params[:sort_direction] : "desc" if params[:choosed].present? && params[:choosed].is_a?(Array) scope.order("FIELD(id, #{params[:choosed].reverse.join(",")}) desc") diff --git a/app/queries/user_query.rb b/app/queries/user_query.rb index f2964c7dc..6668da5c7 100644 --- a/app/queries/user_query.rb +++ b/app/queries/user_query.rb @@ -10,7 +10,7 @@ class UserQuery < ApplicationQuery # 真实姓名 if name = strip_param(:name) - users = users.where('LOWER(CONCAT_WS(users.lastname, users.firstname, users.nickname)) LIKE ?', "%#{name.downcase}%") + users = users.where('LOWER(CONCAT(users.lastname, users.firstname)) LIKE ? OR users.nickname LIKE ?', "%#{name.downcase}%", "%#{name.downcase}%") end # 单位名称 diff --git a/app/queries/weapps/subject_query.rb b/app/queries/weapps/subject_query.rb index e2f5625e1..73e70160a 100644 --- a/app/queries/weapps/subject_query.rb +++ b/app/queries/weapps/subject_query.rb @@ -28,10 +28,10 @@ class Weapps::SubjectQuery < ApplicationQuery private def order_type - params[:order] || "updated_at" + Subject.column_names.include?(params[:order]) ? params[:order] : 'updated_at' end def sort_type - params[:sort] || "desc" + %w(desc asc).include?(params[:sort]) ? params[:sort] : "desc" end end \ No newline at end of file diff --git a/app/services/admins/import_user_service.rb b/app/services/admins/import_user_service.rb index 298bf1e16..5d4c2e10f 100644 --- a/app/services/admins/import_user_service.rb +++ b/app/services/admins/import_user_service.rb @@ -84,7 +84,7 @@ class Admins::ImportUserService < ApplicationService if data.identity.to_i == 1 users = users.where(user_extensions: { student_id: data.student_id }) else - users = users.where(user_extensions: { technical_title: data.technical_title }).where('CONCAT_WS(users.lastname,users.firstname,users.nickname) = ?', data.name) + users = users.where(user_extensions: { technical_title: data.technical_title }).where('CONCAT(users.lastname,users.firstname) = ? OR users.nickname = ?', data.name, data.name) end users.first diff --git a/app/services/issues/list_query_service.rb b/app/services/issues/list_query_service.rb index 1718db97d..f85e2cb9d 100644 --- a/app/services/issues/list_query_service.rb +++ b/app/services/issues/list_query_service.rb @@ -45,9 +45,17 @@ class Issues::ListQueryService < ApplicationService issues = issues.where(issue_type: params[:issue_type].to_s) if params[:issue_type].present? && params[:issue_type].to_s != "all" issues = issues.joins(:issue_tags).where(issue_tags: {id: params[:issue_tag_id].to_i}) if params[:issue_tag_id].present? && params[:issue_tag_id].to_s != "all" - order_type = params[:order_type] || "desc" #或者"asc" - order_name = params[:order_name] || "updated_on" #或者"updated_on" issues.reorder("issues.#{order_name} #{order_type}") end + private + + def order_name + Issue.column_names.include?(params[:order_name]) ? params[:order_name] : 'updated_on' + end + + def order_type + %w(desc asc).include?(params[:order_type]) ? params[:order_type] : 'desc' + end + end \ No newline at end of file diff --git a/app/services/projects/accept_join_service.rb b/app/services/projects/accept_join_service.rb new file mode 100644 index 000000000..69cb97603 --- /dev/null +++ b/app/services/projects/accept_join_service.rb @@ -0,0 +1,61 @@ +class Projects::AcceptJoinService < ApplicationService + attr_accessor :applied_project, :owner + attr_reader :user, :project + + def initialize(user, applied_project) + @user = user + @project = applied_project.project + @applied_project = applied_project + end + + def call + Rails.logger.info("###### Project accept_join_service begin ######") + ActiveRecord::Base.transaction do + validate! + update_apply + operate_project_member + send_apply_message + end + + Rails.logger.info("##### Project accept_join_service end ######") + + + return @applied_project + end + + private + def permission + case @applied_project.role + when 'manager' + 'admin' + when 'developer' + 'write' + when 'reporter' + 'read' + else + 'read' + end + end + + def validate! + raise Error, '该申请已经被接受' if @applied_project.accepted? + raise Error, '该申请不存在' unless @applied_project.present? + raise Error, '未拥有接受申请权限' unless is_permit_operator + end + + def is_permit_operator + return @user.admin? || @project.manager?(@user) + end + + def update_apply + @applied_project.update!(status: 'accepted') + end + + def operate_project_member + Projects::AddMemberInteractor.call(@project.owner, @project, @applied_project.user, permission) + end + + def send_apply_message + SendJoinProjectAppliedMessageJob.perform_now(@applied_project, @user, 'successed') + end +end \ No newline at end of file diff --git a/app/services/projects/apply_join_service.rb b/app/services/projects/apply_join_service.rb index 0b57712d0..677ee20c1 100644 --- a/app/services/projects/apply_join_service.rb +++ b/app/services/projects/apply_join_service.rb @@ -9,26 +9,25 @@ class Projects::ApplyJoinService < ApplicationService end def call - validate! # 项目报告人员直接加入项目 - if params[:role] == 'reporter' - # Projects::JoinService.call(project, user, role: 'reporter') - return project - end + # if params[:role] == 'reporter' + # # Projects::JoinService.call(project, user, role: 'reporter') + # return project + # end ActiveRecord::Base.transaction do + validate! apply = user.applied_projects.create!(project: project, role: role_value) + apply + # apply.forge_activities.find_or_create_by!(user: user, project: project) - apply.forge_activities.find_or_create_by!(user: user, project: project) - - notify_project_manager!(apply) + # notify_project_manager!(apply) end # notify_project_owner - ApplyJoinProjectNotifyJob.perform_later(user.id, project.id, role_value) + # ApplyJoinProjectNotifyJob.perform_later(user.id, project.id, role_value) - project end private @@ -43,7 +42,8 @@ class Projects::ApplyJoinService < ApplicationService when 'manager' then 3 when 'developer' then 4 when 'reporter' then 5 - else raise Error, '角色无效' + else + 5 end end @@ -74,12 +74,18 @@ class Projects::ApplyJoinService < ApplicationService 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]) + raise Error, '请输入6位项目邀请码' unless valid_invite_code( params[:code]) # logical check raise Error, '邀请码无效' if project.blank? - raise Error, '您已在该项目中' if project.member?(user) - raise Error, '您已经提交过申请' if user.applied_projects.pending.exists?(project: project) + raise Error, '您已是项目成员' if project.member?(user) + raise Error, '您已经提交过申请' if user.applied_projects.common.exists?(project: project) + end + + def valid_invite_code(str) + if (str =~ /^[A-Za-z0-9]{6}+$/) + return true + end + return false end end \ No newline at end of file diff --git a/app/services/projects/refuse_join_service.rb b/app/services/projects/refuse_join_service.rb new file mode 100644 index 000000000..f9452586c --- /dev/null +++ b/app/services/projects/refuse_join_service.rb @@ -0,0 +1,39 @@ +class Projects::RefuseJoinService < ApplicationService + attr_accessor :applied_project, :owner + attr_reader :user, :project + + def initialize(user, applied_project) + @user = user + @project = applied_project.project + @applied_project = applied_project + end + + def call + Rails.logger.info("###### Project refuse_join_service begin ######") + validate! + update_apply + send_apply_message + Rails.logger.info("###### Project refuse_join_service end ######") + + return @applied_project + end + + private + def validate! + raise Error, '该申请已被拒绝' if @applied_project.refused? + raise Error, '该申请不存在' unless @applied_project.present? + raise Error, '未拥有接受申请权限' unless is_permit_operator + end + + def is_permit_operator + return @user.admin? || @project.manager?(@user) + end + + def update_apply + @applied_project.update!(status: 'refused') + end + + def send_apply_message + SendJoinProjectAppliedMessageJob.perform_now(@applied_project, @user, 'failure') + end +end \ No newline at end of file diff --git a/app/views/admins/shared/_sidebar.html.erb b/app/views/admins/shared/_sidebar.html.erb index ad53c661a..82553d9a0 100644 --- a/app/views/admins/shared/_sidebar.html.erb +++ b/app/views/admins/shared/_sidebar.html.erb @@ -42,8 +42,11 @@ <% end %>
{
"total_count": 5,
"applied_messages": [
+ {
+ "applied": {
+ "project": {
+ "id": 74,
+ "identifier": "hehuisssjssjjsjs",
+ "name": "hehuisssjssjjsjs",
+ "description": "wwww",
+ "is_public": false,
+ "owner": {
+ "id": 10,
+ "type": "User",
+ "name": "testforge1",
+ "login": "testforge1",
+ "image_url": "system/lets/letter_avatars/2/T/19_237_174/120.png"
+ }
+ },
+ "user": {
+ "id": 6,
+ "type": "User",
+ "name": "何慧",
+ "login": "yystopf",
+ "image_url": "images/avatars/User/6?t=1622513134"
+ },
+ "id": 6,
+ "status": "accepted",
+ "created_at": "2021-06-09 16:34",
+ "time_ago": "1分钟前"
+ },
+ "applied_user": {
+ "id": 6,
+ "type": "User",
+ "name": "何慧",
+ "login": "yystopf",
+ "image_url": "images/avatars/User/6?t=1622513134"
+ },
+ "applied_type": "AppliedProject",
+ "name": "已通过你加入【hehuisssjssjjsjs】仓库的申请。",
+ "viewed": "waiting",
+ "status": "successed",
+ "created_at": "2021-06-09 16:34",
+ "time_ago": "1分钟前"
+ },
{
"applied": {
"project": {
@@ -2699,7 +2753,660 @@ Success — a happy kitten is an authenticated kitten!
"created_at": "2021-04-25 18:06",
"time_ago": "16小时前"
}
-
待办事项-项目申请
+ +++示例:
+
curl -X GET http://localhost:3000/api/users/yystopf/applied_projects.json
+
await octokit.request('GET /api/users/:login/applied_projects.json')
+
GET /api/users/:login/applied_projects.json
参数 | +类型 | +字段说明 | +
---|---|---|
login | +string | +用户标识 | +
参数 | +类型 | +字段说明 | +
---|---|---|
id | +int | +申请id | +
status | +string | +申请状态,canceled:取消,common:正在申请, accept:已接受,refuse:已拒绝 | +
time_ago | +string | +申请创建的时间 | +
project.id | +int | +申请项目的id | +
project.identifier | +string | +申请项目的标识 | +
project.name | +string | +申请项目的名称 | +
project.description | +string | +申请项目的描述 | +
project.is_public | +bool | +申请项目是否公开 | +
project.owner.id | +bool | +申请项目拥有者id | +
project.owner.type | +string | +申请项目拥有者类型 | +
project.owner.name | +string | +申请项目拥有者昵称 | +
project.owner.login | +string | +申请项目拥有者标识 | +
project.owner.image_url | +string | +申请项目拥有者头像 | +
user.id | +int | +申请创建者的id | +
user.type | +string | +申请创建者的类型 | +
user.name | +string | +申请创建者的名称 | +
user.login | +string | +申请创建者的标识 | +
user.image_url | +string | +申请创建者头像 | +
++返回的JSON示例:
+
{
+ "total_count": 4,
+ "applied_transfer_projects": [
+ {
+ "project": {
+ "id": 74,
+ "identifier": "hehuisssjssjjsjs",
+ "name": "hehuisssjssjjsjs",
+ "description": "wwww",
+ "is_public": false,
+ "owner": {
+ "id": 10,
+ "type": "User",
+ "name": "testforge1",
+ "login": "testforge1",
+ "image_url": "system/lets/letter_avatars/2/T/19_237_174/120.png"
+ }
+ },
+ "user": {
+ "id": 6,
+ "type": "User",
+ "name": "何慧",
+ "login": "yystopf",
+ "image_url": "images/avatars/User/6?t=1622513134"
+ },
+ "id": 7,
+ "status": "common",
+ "created_at": "2021-06-09 16:41",
+ "time_ago": "7分钟前"
+ },
+ ...
+ ]
+}
+
用户接受申请
+ +++示例:
+
curl -X POST http://localhost:3000/api/users/yystopf/applied_projects/2/accept.json
+
await octokit.request('GET /api/users/:login/applied_projects/:id/accept.json')
+
GET /api/users/:login/applied_projects/:id/accept.json
参数 | +类型 | +字段说明 | +
---|---|---|
login | +string | +用户标识 | +
id | +int | +申请id | +
参数 | +类型 | +字段说明 | +
---|---|---|
id | +int | +申请id | +
status | +string | +申请状态,canceled:取消,common:正在申请, accept:已接受,refuse:已拒绝 | +
time_ago | +string | +申请创建的时间 | +
project.id | +int | +申请项目的id | +
project.identifier | +string | +申请项目的标识 | +
project.name | +string | +申请项目的名称 | +
project.description | +string | +申请项目的描述 | +
project.is_public | +bool | +申请项目是否公开 | +
project.owner.id | +bool | +申请项目拥有者id | +
project.owner.type | +string | +申请项目拥有者类型 | +
project.owner.name | +string | +申请项目拥有者昵称 | +
project.owner.login | +string | +申请项目拥有者标识 | +
project.owner.image_url | +string | +申请项目拥有者头像 | +
user.id | +int | +申请创建者的id | +
user.type | +string | +申请创建者的类型 | +
user.name | +string | +申请创建者的名称 | +
user.login | +string | +申请创建者的标识 | +
user.image_url | +string | +申请创建者头像 | +
++返回的JSON示例:
+
{
+ "project": {
+ "id": 74,
+ "identifier": "hehuisssjssjjsjs",
+ "name": "hehuisssjssjjsjs",
+ "description": "wwww",
+ "is_public": false,
+ "owner": {
+ "id": 10,
+ "type": "User",
+ "name": "testforge1",
+ "login": "testforge1",
+ "image_url": "system/lets/letter_avatars/2/T/19_237_174/120.png"
+ }
+ },
+ "user": {
+ "id": 6,
+ "type": "User",
+ "name": "何慧",
+ "login": "yystopf",
+ "image_url": "images/avatars/User/6?t=1622513134"
+ },
+ "id": 7,
+ "status": "accept",
+ "created_at": "2021-06-09 16:41",
+ "time_ago": "7分钟前"
+}
+
用户拒绝申请
+ +++示例:
+
curl -X POST http://localhost:3000/api/users/yystopf/applied_projects/2/refuse.json
+
await octokit.request('GET /api/users/:login/applied_projects/:id/refuse.json')
+
GET /api/users/:login/applied_projects/:id/refuse.json
参数 | +类型 | +字段说明 | +
---|---|---|
login | +string | +用户标识 | +
id | +int | +申请id | +
参数 | +类型 | +字段说明 | +
---|---|---|
id | +int | +申请id | +
status | +string | +申请状态,canceled:取消,common:正在申请, accept:已接受,refuse:已拒绝 | +
time_ago | +string | +申请创建的时间 | +
project.id | +int | +申请项目的id | +
project.identifier | +string | +申请项目的标识 | +
project.name | +string | +申请项目的名称 | +
project.description | +string | +申请项目的描述 | +
project.is_public | +bool | +申请项目是否公开 | +
project.owner.id | +bool | +申请项目拥有者id | +
project.owner.type | +string | +申请项目拥有者类型 | +
project.owner.name | +string | +申请项目拥有者昵称 | +
project.owner.login | +string | +申请项目拥有者标识 | +
project.owner.image_url | +string | +申请项目拥有者头像 | +
user.id | +int | +申请创建者的id | +
user.type | +string | +申请创建者的类型 | +
user.name | +string | +申请创建者的名称 | +
user.login | +string | +申请创建者的标识 | +
user.image_url | +string | +申请创建者头像 | +
++返回的JSON示例:
+
{
+ "project": {
+ "id": 74,
+ "identifier": "hehuisssjssjjsjs",
+ "name": "hehuisssjssjjsjs",
+ "description": "wwww",
+ "is_public": false,
+ "owner": {
+ "id": 10,
+ "type": "User",
+ "name": "testforge1",
+ "login": "testforge1",
+ "image_url": "system/lets/letter_avatars/2/T/19_237_174/120.png"
+ }
+ },
+ "user": {
+ "id": 6,
+ "type": "User",
+ "name": "何慧",
+ "login": "yystopf",
+ "image_url": "images/avatars/User/6?t=1622513134"
+ },
+ "id": 7,
+ "status": "accept",
+ "created_at": "2021-06-09 16:41",
+ "time_ago": "7分钟前"
+}
+
申请加入项目
+ +++示例:
+
curl -X POST http://localhost:3000/api/applied_projects.json
+
await octokit.request('POST /api/appliedr_projects.json')
+
POST /api/applied_projects.json
参数 | +必选 | +默认 | +类型 | +字段说明 | +
---|---|---|---|---|
applied_project.code | +是 | ++ | string | +邀请码 | +
applied_project.role | +否 | ++ | string | +项目权限,reporter: 报告者, developer: 开发者,manager:管理员 | +
++请求的JSON示例
+
{
+ "applied_project": {
+ "code": "1una34",
+ "role": "developer"
+ }
+}
+
参数 | +类型 | +字段说明 | +
---|---|---|
id | +int | +申请id | +
status | +string | +申请状态,canceled:取消,common:正在申请, accept:已接受,refuse:已拒绝 | +
time_ago | +string | +项目申请创建的时间 | +
project.id | +int | +申请项目的id | +
project.identifier | +string | +申请项目的标识 | +
project.name | +string | +申请项目的名称 | +
project.description | +string | +申请项目的描述 | +
project.is_public | +bool | +申请项目是否公开 | +
project.owner.id | +bool | +申请项目拥有者id | +
project.owner.type | +string | +申请项目拥有者类型 | +
project.owner.name | +string | +申请项目拥有者昵称 | +
project.owner.login | +string | +申请项目拥有者标识 | +
project.owner.image_url | +string | +申请项目拥有者头像 | +
user.id | +int | +申请创建者的id | +
user.type | +string | +申请创建者的类型 | +
user.name | +string | +申请创建者的名称 | +
user.login | +string | +申请创建者的标识 | +
user.image_url | +string | +申请创建者头像 | +
++返回的JSON示例:
+
{
+ "project": {
+ "id": 74,
+ "identifier": "hehuisssjssjjsjs",
+ "name": "hehuisssjssjjsjs",
+ "description": "wwww",
+ "is_public": false,
+ "owner": {
+ "id": 10,
+ "type": "User",
+ "name": "testforge1",
+ "login": "testforge1",
+ "image_url": "system/lets/letter_avatars/2/T/19_237_174/120.png"
+ }
+ },
+ "user": {
+ "id": 6,
+ "type": "User",
+ "name": "何慧",
+ "login": "yystopf",
+ "image_url": "images/avatars/User/6?t=1622513134"
+ },
+ "id": 7,
+ "status": "common",
+ "created_at": "2021-06-09 16:41",
+ "time_ago": "1分钟前"
+}
+
获取项目列表,也可以更加相关条件过滤搜素
@@ -2710,9 +3417,9 @@ Success — a happy kitten is an authenticated kitten! -d "limit=5" \ http://localhost:3000/api/projects | jqawait octokit.request('GET /api/projects') -
HTTP 请求
+HTTP 请求
-
GET api/projects
请求参数
+请求参数
-
参数 @@ -2779,7 +3486,7 @@ http://localhost:3000/api/projects | jq项目类型, 取值为:common、mirror; common:开源托管项目, mirror:开源镜像项目 返回字段说明
+返回字段说明
参数 @@ -2931,9 +3638,9 @@ Remember — a happy kitten is an authenticated kitten!curl -X GET \ http://localhost:3000/api/projects/recommend | jq
await octokit.request('GET /api/projects/recommend.json') -
HTTP 请求
+HTTP 请求
-
GET api/projects/recommend
返回字段说明
+返回字段说明
参数 @@ -3067,9 +3774,9 @@ Remember — a happy kitten is an authenticated kitten!curl -X GET \ http://localhost:3000/api/yystopf/ceshi/menu_list | jq
await octokit.request('GET /api/yystopf/ceshi/menu_list') -
HTTP 请求
+HTTP 请求
-
GET api/:owner/:repo/menu_list
请求参数
+请求参数
-
参数 @@ -3094,7 +3801,7 @@ http://localhost:3000/api/yystopf/ceshi/menu_list | jq项目标识identifier 返回字段说明
+返回字段说明
参数 @@ -3135,9 +3842,9 @@ http://localhost:3000/api/yystopf/ceshi/menu_list | jqcurl -X GET \ http://localhost:3000/api/jasder/forgeplus/about | jq
await octokit.request('GET /api/jasder/forgeplus/about') -
HTTP 请求
+HTTP 请求
-
GET api/:owner/:repo/about
请求参数
+请求参数
-
参数 @@ -3162,7 +3869,7 @@ http://localhost:3000/api/jasder/forgeplus/about | jq项目标识identifier 返回字段说明
+返回字段说明
参数 @@ -3208,7 +3915,7 @@ Remember — a happy kitten is an authenticated kitten!curl -X GET \ http://localhost:3000/api/yystopf/ceshi/project_units.json
await octokit.request('GET /api/yystopf/ceshi/project_units') -
HTTP 请求
+HTTP 请求
GET /api/yystopf/ceshi/project_units
返回字段说明:
@@ -3251,9 +3958,9 @@ http://localhost:3000/api/yystopf/ceshi/project_units.json -d "{ \"unit_typs\": [\"code\", \"pulls\"]}" \ http://localhost:3000/api/yystopf/ceshi/project_units.json
await octokit.request('POST /api/yystopf/ceshi/project_units') -
HTTP 请求
+HTTP 请求
-
POST /api/yystopf/ceshi/project_units
请求参数
+请求参数
参数 @@ -3315,9 +4022,9 @@ http://localhost:3000/api/yystopf/ceshi/project_units.json -d "license_id=1" \ http://localhost:3000/api/projects.jsonawait octokit.request('GET /api/projects.json') -
HTTP 请求
+HTTP 请求
-
POST api/projects
请求参数
+请求参数
-
参数 @@ -3391,7 +4098,7 @@ http://localhost:3000/api/projects.json项目是否私有, true:为私有,false: 公开,默认为公开 返回字段说明
+返回字段说明
参数 @@ -3433,9 +4140,9 @@ http://localhost:3000/api/projects.json -d "project_language_id=2" \ http://localhost:3000/api/projects/migrate.jsonawait octokit.request('GET /api/projects/migrate.json') -
HTTP 请求
+HTTP 请求
-
POST api/projects/migrate.json
请求参数
+请求参数
-
参数 @@ -3523,7 +4230,7 @@ http://localhost:3000/api/projects/migrate.json项目是否私有, true:为私有,false: 非私有,默认为公开 返回字段说明
+返回字段说明
参数 @@ -3558,9 +4265,9 @@ http://localhost:3000/api/projects/migrate.jsoncurl -X POST http://localhost:3000/api/repositories/1244/sync_mirror.json
await octokit.request('POST /api/repositories/1244/sync_mirror.json') -
HTTP 请求
+HTTP 请求
-
POST api/repositories/:id/sync_mirror.json
请求参数
+请求参数
-
参数 @@ -3578,7 +4285,7 @@ http://localhost:3000/api/projects/migrate.json仓库id 返回字段说明
+返回字段说明
参数 @@ -3613,9 +4320,9 @@ http://localhost:3000/api/projects/migrate.jsoncurl -X POST http://localhost:3000/api/jasder/forgeplus/forks.json
await octokit.request('POST /api/jaser/jasder_test/forks.json') -
HTTP 请求
+HTTP 请求
-
POST api/:owner/:repo/forks.json
请求参数
+请求参数
-
参数 @@ -3640,7 +4347,7 @@ http://localhost:3000/api/projects/migrate.json项目标识identifier 返回字段说明
+返回字段说明
参数 @@ -3676,9 +4383,9 @@ http://localhost:3000/api/projects/migrate.jsoncurl -X GET \ http://localhost:3000/api/ceshi1/ceshi_repo1/applied_transfer_projects/organizations.json | jq
await octokit.request('GET /api/:owner/:repo/applied_transfer_projects/organizations') -
HTTP 请求
+HTTP 请求
-
GET api/:owner/:repo/applied_transfer_projects/organizations
请求参数
+请求参数
-
参数 @@ -3703,7 +4410,7 @@ http://localhost:3000/api/ceshi1/ceshi_repo1/applied_transfer_projects/organizat项目标识identifier 返回字段说明
+返回字段说明
参数 @@ -3770,9 +4477,9 @@ http://localhost:3000/api/ceshi1/ceshi_repo1/applied_transfer_projects/organizatcurl -X POST http://localhost:3000/api/ceshi1/ceshi_repo1/applied_transfer_projects.json
await octokit.request('POST /api/:owner/:repo/applied_transfer_projects.json') -
HTTP 请求
+HTTP 请求
-
POST /api/:owner/:repo/applied_transfer_projects.json
请求参数
+请求参数
-
参数 @@ -3804,7 +4511,7 @@ http://localhost:3000/api/ceshi1/ceshi_repo1/applied_transfer_projects/organizat迁移对象标识 返回字段说明
+返回字段说明
参数 @@ -3974,9 +4681,9 @@ http://localhost:3000/api/ceshi1/ceshi_repo1/applied_transfer_projects/organizatcurl -X POST http://localhost:3000/api/ceshi1/ceshi_repo1/applied_transfer_projects/cancel.json
await octokit.request('POST /api/:owner/:repo/applied_transfer_projects/cancel.json') -
HTTP 请求
+HTTP 请求
-
POST /api/:owner/:repo/applied_transfer_projects/cancel.json
请求参数
+请求参数
-
参数 @@ -4001,7 +4708,7 @@ http://localhost:3000/api/ceshi1/ceshi_repo1/applied_transfer_projects/organizat项目标识identifier 返回字段说明
+返回字段说明
参数