From 5489ea9fbc7e0adcfdcf3b01dc4cb65ac0fb89dc Mon Sep 17 00:00:00 2001 From: "vilet.yy" Date: Wed, 9 Jun 2021 16:53:09 +0800 Subject: [PATCH] add: applied join project --- .../applied_projects_controller.rb | 13 + .../users/applied_messages_controller.rb | 2 +- .../users/applied_projects_controller.rb | 39 + .../applied_transfer_projects_controller.rb | 2 +- app/docs/slate/source/includes/_projects.md | 86 ++ app/docs/slate/source/includes/_users.md | 290 ++++++- .../projects/add_member_interactor.rb | 17 +- .../send_join_project_applied_message_job.rb | 27 + app/models/applied_project.rb | 3 + app/models/attachment.rb | 77 +- app/models/ci/user.rb | 4 + app/models/concerns/project_operable.rb | 4 +- app/models/issue.rb | 5 +- app/models/license.rb | 1 + app/models/member.rb | 1 + app/models/organization.rb | 12 + app/models/praise_tread.rb | 33 +- app/models/project.rb | 15 +- app/models/pull_request.rb | 5 + app/models/user.rb | 11 + app/services/projects/accept_join_service.rb | 61 ++ app/services/projects/apply_join_service.rb | 36 +- app/services/projects/refuse_join_service.rb | 39 + .../applied_projects/_detail.json.jbuilder | 18 + .../applied_projects/create.json.jbuilder | 1 + .../applied_messages/_detail.json.jbuilder | 7 +- .../applied_projects/accept.json.jbuilder | 1 + .../applied_projects/index.json.jbuilder | 4 + .../applied_projects/refuse.json.jbuilder | 1 + config/routes.rb | 8 + ...72904_add_timestamp_to_applied_projects.rb | 5 + public/docs/api.html | 801 +++++++++++++++++- 32 files changed, 1497 insertions(+), 132 deletions(-) create mode 100644 app/controllers/applied_projects_controller.rb create mode 100644 app/controllers/users/applied_projects_controller.rb create mode 100644 app/jobs/send_join_project_applied_message_job.rb create mode 100644 app/services/projects/accept_join_service.rb create mode 100644 app/services/projects/refuse_join_service.rb create mode 100644 app/views/applied_projects/_detail.json.jbuilder create mode 100644 app/views/applied_projects/create.json.jbuilder create mode 100644 app/views/users/applied_projects/accept.json.jbuilder create mode 100644 app/views/users/applied_projects/index.json.jbuilder create mode 100644 app/views/users/applied_projects/refuse.json.jbuilder create mode 100644 db/migrate/20210609072904_add_timestamp_to_applied_projects.rb 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/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/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..d2960b3e5 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/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_project.rb b/app/models/applied_project.rb index 8362cfa70..d1fea2f77 100644 --- a/app/models/applied_project.rb +++ b/app/models/applied_project.rb @@ -7,6 +7,8 @@ # user_id :integer not null # role :integer default("0") # status :integer default("0") +# created_at :datetime +# updated_at :datetime # class AppliedProject < ApplicationRecord @@ -16,6 +18,7 @@ class AppliedProject < ApplicationRecord has_many :applied_messages, as: :applied, 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 已拒绝 end diff --git a/app/models/attachment.rb b/app/models/attachment.rb index 3bfc9a8ce..721cf84ba 100644 --- a/app/models/attachment.rb +++ b/app/models/attachment.rb @@ -1,41 +1,42 @@ -# == Schema Information -# -# Table name: attachments -# -# id :integer not null, primary key -# container_id :integer -# container_type :string(30) -# filename :string(255) default(""), not null -# disk_filename :string(255) default(""), not null -# filesize :integer default("0"), not null -# content_type :string(255) default("") -# digest :string(60) default(""), not null -# downloads :integer default("0"), not null -# author_id :integer default("0"), not null -# created_on :datetime -# description :text(65535) -# disk_directory :string(255) -# attachtype :integer default("1") -# is_public :integer default("1") -# copy_from :integer -# quotes :integer default("0") -# is_publish :integer default("1") -# publish_time :datetime -# resource_bank_id :integer -# unified_setting :boolean default("1") -# cloud_url :string(255) default("") -# course_second_category_id :integer default("0") -# delay_publish :boolean default("0") -# -# Indexes -# -# index_attachments_on_author_id (author_id) -# index_attachments_on_container_id_and_container_type (container_id,container_type) -# index_attachments_on_course_second_category_id (course_second_category_id) -# index_attachments_on_created_on (created_on) -# index_attachments_on_is_public (is_public) -# index_attachments_on_quotes (quotes) -# +# == Schema Information +# +# Table name: attachments +# +# id :integer not null, primary key +# container_id :integer +# container_type :string(30) +# filename :string(255) default(""), not null +# disk_filename :string(255) default(""), not null +# filesize :integer default("0"), not null +# content_type :string(255) default("") +# digest :string(60) default(""), not null +# downloads :integer default("0"), not null +# author_id :integer default("0"), not null +# created_on :datetime +# description :text(65535) +# disk_directory :string(255) +# attachtype :integer default("1") +# is_public :integer default("1") +# copy_from :integer +# quotes :integer default("0") +# is_publish :integer default("1") +# publish_time :datetime +# resource_bank_id :integer +# unified_setting :boolean default("1") +# cloud_url :string(255) default("") +# course_second_category_id :integer default("0") +# delay_publish :boolean default("0") +# +# Indexes +# +# index_attachments_on_author_id (author_id) +# index_attachments_on_container_id_and_container_type (container_id,container_type) +# index_attachments_on_course_second_category_id (course_second_category_id) +# index_attachments_on_created_on (created_on) +# index_attachments_on_is_public (is_public) +# index_attachments_on_quotes (quotes) +# + class Attachment < ApplicationRecord include BaseModel 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/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..ce4b0a5fa 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) 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 f58f9f236..55cce6c72 100644 --- a/app/models/project.rb +++ b/app/models/project.rb @@ -4,7 +4,7 @@ # # id :integer not null, primary key # name :string(255) default(""), not null -# description :text(65535) +# description :text(4294967295) # homepage :string(255) default("") # is_public :boolean default("1"), not null # parent_id :integer @@ -43,6 +43,19 @@ # 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 # diff --git a/app/models/pull_request.rb b/app/models/pull_request.rb index 97f4a9164..d2e61f128 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 diff --git a/app/models/user.rb b/app/models/user.rb index 9d917f4c8..39cf3de46 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 # @@ -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/services/projects/accept_join_service.rb b/app/services/projects/accept_join_service.rb new file mode 100644 index 000000000..4cc539907 --- /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, @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/applied_projects/_detail.json.jbuilder b/app/views/applied_projects/_detail.json.jbuilder new file mode 100644 index 000000000..1dda3568b --- /dev/null +++ b/app/views/applied_projects/_detail.json.jbuilder @@ -0,0 +1,18 @@ +project = object.project +json.project do + json.id project.id + json.identifier project.identifier + json.name project.name + json.description project.description + json.is_public project.is_public + json.owner do + json.partial! "/users/user_simple", locals: {user: project.owner} + end +end +json.user do + json.partial! "/users/user_simple", locals: {user: object.user} +end +json.id object.id +json.status object.status +json.created_at format_time(object.created_at) +json.time_ago time_from_now(object.created_at) diff --git a/app/views/applied_projects/create.json.jbuilder b/app/views/applied_projects/create.json.jbuilder new file mode 100644 index 000000000..e2512880c --- /dev/null +++ b/app/views/applied_projects/create.json.jbuilder @@ -0,0 +1 @@ +json.partial! "detail", locals: {object: @applied_project} diff --git a/app/views/users/applied_messages/_detail.json.jbuilder b/app/views/users/applied_messages/_detail.json.jbuilder index cca202c10..796d0387f 100644 --- a/app/views/users/applied_messages/_detail.json.jbuilder +++ b/app/views/users/applied_messages/_detail.json.jbuilder @@ -13,7 +13,12 @@ # json.partial! "/users/user_simple", locals: {user: object.user} # end json.applied do - json.partial! "/projects/applied_transfer_projects/detail", locals: {object: object.applied} + case object.applied_type + when 'AppliedTransferProject' + json.partial! "/projects/applied_transfer_projects/detail", locals: {object: object.applied} + when 'AppliedProject' + json.partial! "/applied_projects/detail", locals: {object: object.applied} + end end json.applied_user do json.partial! "/users/user_simple", locals: {user: object.applied_user} diff --git a/app/views/users/applied_projects/accept.json.jbuilder b/app/views/users/applied_projects/accept.json.jbuilder new file mode 100644 index 000000000..d13d24bed --- /dev/null +++ b/app/views/users/applied_projects/accept.json.jbuilder @@ -0,0 +1 @@ +json.partial! "/applied_projects/detail", locals: {object: @applied_project} diff --git a/app/views/users/applied_projects/index.json.jbuilder b/app/views/users/applied_projects/index.json.jbuilder new file mode 100644 index 000000000..6e1a1a02b --- /dev/null +++ b/app/views/users/applied_projects/index.json.jbuilder @@ -0,0 +1,4 @@ +json.total_count @applied_projects.total_count +json.applied_projects @applied_projects do |apply| + json.partial! "/applied_projects/detail", locals: {object: apply} +end diff --git a/app/views/users/applied_projects/refuse.json.jbuilder b/app/views/users/applied_projects/refuse.json.jbuilder new file mode 100644 index 000000000..d13d24bed --- /dev/null +++ b/app/views/users/applied_projects/refuse.json.jbuilder @@ -0,0 +1 @@ +json.partial! "/applied_projects/detail", locals: {object: @applied_project} diff --git a/config/routes.rb b/config/routes.rb index 9796f774e..5a883fcb1 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -148,6 +148,8 @@ Rails.application.routes.draw do resources :issue_depends, only: [:create, :destroy] end + resources :applied_projects, only: [:create] + resources :project_categories, only: [:index, :show] do get :group_list, on: :collection end @@ -266,6 +268,12 @@ Rails.application.routes.draw do post :refuse end end + resources :applied_projects, only: [:index] do + member do + post :accept + post :refuse + end + end resources :headmaps, only: [:index] resources :is_pinned_projects, only: [:index, :update] do collection do diff --git a/db/migrate/20210609072904_add_timestamp_to_applied_projects.rb b/db/migrate/20210609072904_add_timestamp_to_applied_projects.rb new file mode 100644 index 000000000..d5d4067a8 --- /dev/null +++ b/db/migrate/20210609072904_add_timestamp_to_applied_projects.rb @@ -0,0 +1,5 @@ +class AddTimestampToAppliedProjects < ActiveRecord::Migration[5.2] + def change + add_timestamps(:applied_projects, null: true) + end +end diff --git a/public/docs/api.html b/public/docs/api.html index 0cf007cf1..9a5a58aa0 100644 --- a/public/docs/api.html +++ b/public/docs/api.html @@ -373,11 +373,23 @@
  • 用户拒绝迁移
  • +
  • + 待办事项-项目申请 +
  • +
  • + 用户接受迁移 +
  • +
  • + 用户拒绝迁移 +
  • Projects