新增:邀请用户链接生成

This commit is contained in:
yystopf 2022-06-15 18:07:23 +08:00
parent 42d2b51f7f
commit c42f3d5928
13 changed files with 245 additions and 9 deletions

View File

@ -0,0 +1,36 @@
class Projects::ProjectInviteLinksController < Projects::BaseController
before_action :require_manager!, except: [:redirect_link]
before_action :require_login
def current_link
@project_invite_link = ProjectInviteLink.find_by(user_id: current_user.id, project_id: @project.id)
@project_invite_link = ProjectInviteLink.build!(@project, current_user) unless @project_invite_link.present?
end
def generate_link
ActiveRecord::Base.transaction do
params_data = link_params.merge({user_id: current_user.id, project_id: @project.id})
puts params_data
Projects::ProjectInviteLinks::CreateForm.new(params_data).validate!
@project_invite_link = ProjectInviteLink.create!(params_data.merge(sign: ProjectInviteLink.random_hex_sign))
render_ok
end
rescue Exception => e
uid_logger_error(e.message)
tip_exception(e.message)
end
def redirect_link
Projects::LinkJoinService.call(current_user, @project, params[:invite_sign])
render_ok
rescue Exception => e
uid_logger_error(e.message)
tip_exception(e.message)
end
private
def link_params
params.require(:project_invite_link).permit(:role, :is_apply)
end
end

View File

@ -0,0 +1,8 @@
class Projects::ProjectInviteLinks::CreateForm < BaseForm
attr_accessor :user_id, :project_id, :role, :is_apply
validates :user_id, :project_id, :role, presence: true
validates :role, inclusion: { in: %w(manager developer reporter), message: "请输入正确的权限." }
validates :is_apply, inclusion: {in: [true, false], message: "请输入是否需要管理员审核."}
end

View File

@ -2,24 +2,27 @@
#
# Table name: forge_applied_projects
#
# id :integer not null, primary key
# project_id :integer
# user_id :integer
# role :integer default("0")
# status :integer default("0")
# created_at :datetime not null
# updated_at :datetime not null
# id :integer not null, primary key
# project_id :integer
# user_id :integer
# role :integer default("0")
# status :integer default("0")
# created_at :datetime not null
# updated_at :datetime not null
# project_invite_link_id :integer
#
# Indexes
#
# index_forge_applied_projects_on_project_id (project_id)
# index_forge_applied_projects_on_user_id (user_id)
# index_forge_applied_projects_on_project_id (project_id)
# index_forge_applied_projects_on_project_invite_link_id (project_invite_link_id)
# index_forge_applied_projects_on_user_id (user_id)
#
class AppliedProject < ApplicationRecord
self.table_name = "forge_applied_projects"
belongs_to :user
belongs_to :project
belongs_to :project_invite_link, optional: true
has_many :applied_messages, as: :applied, dependent: :destroy
# has_many :forge_activities, as: :forge_act, dependent: :destroy

View File

@ -128,6 +128,7 @@ class Project < ApplicationRecord
has_many :pinned_projects, dependent: :destroy
has_many :has_pinned_users, through: :pinned_projects, source: :user
has_many :webhooks, class_name: "Gitea::Webhook", primary_key: :gpid, foreign_key: :repo_id
has_many :project_invite_links, dependent: :destroy
after_create :incre_user_statistic, :incre_platform_statistic
after_save :check_project_members
before_save :set_invite_code, :reset_unmember_followed, :set_recommend_and_is_pinned, :reset_cache_data

View File

@ -0,0 +1,57 @@
# == Schema Information
#
# Table name: project_invite_links
#
# id :integer not null, primary key
# project_id :integer
# user_id :integer
# role :integer default("4")
# is_apply :boolean default("1")
# sign :string(255)
# expired_at :datetime
# created_at :datetime not null
# updated_at :datetime not null
#
# Indexes
#
# index_project_invite_links_on_project_id (project_id)
# index_project_invite_links_on_sign (sign)
# index_project_invite_links_on_user_id (user_id)
#
class ProjectInviteLink < ApplicationRecord
default_scope { where("expired_at > ?", Time.now).or(where(expired_at: nil)) }
belongs_to :project
belongs_to :user
has_many :applied_projects
scope :with_project_id, -> (project_id) {where(project_id: project_id)}
scope :with_user_id, -> (user_id) {where(user_id: user_id)}
enum role: {manager: 3, developer: 4, reporter: 5}
before_create :set_old_data_expired_at
def self.random_hex_sign
SecureRandom.hex
end
def self.build!(project, user, role="developer", is_apply=true)
self.create!(
project_id: project&.id,
user_id: user&.id,
role: role,
is_apply: is_apply,
sign: random_hex_sign
)
end
private
def set_old_data_expired_at
ProjectInviteLink.where(user_id: self.user_id, project_id: self.project).update_all(expired_at: Time.now)
end
end

View File

@ -0,0 +1,76 @@
class Projects::LinkJoinService < ApplicationService
Error = Class.new(StandardError)
attr_reader :user, :project, :invite_sign, :params
def initialize(user, project, invite_sign, params={})
@user = user
@project = project
@invite_sign = invite_sign
@params = params
end
def call
ActiveRecord::Base.transaction do
validate!
if invite_link.is_apply
# 如果需要申请才能加入,创建一条申请记录
create_applied_project!
else
# 如果不需要申请,直接为项目添加该成员
create_member!
end
end
end
private
def validate!
raise Error, 'invite_sign必须存在!' if invite_sign.blank?
raise Error, '邀请链接不存在!' unless invite_link.present?
raise Error, '邀请链接已失效!' unless invite_user_in_project
raise Error, '用户已加入该项目!' if project.member?(user.id)
end
def create_applied_project!
user.applied_projects.create!(project: project, role: role_value)
end
def create_member!
Projects::AddMemberInteractor.call(project.owner, project, user, permission)
end
def invite_link
ProjectInviteLink.find_by(project_id: project.id, sign: invite_sign)
end
def invite_user_in_project
in_project = project.member?(invite_link.user)
invite_link.update_column(:expired_at, Time.now) unless in_project
in_project
end
def role_value
@_role ||=
case invite_link&.role
when 'manager' then 3
when 'developer' then 4
when 'reporter' then 5
else
5
end
end
def permission
case invite_link&.role
when 'manager'
'admin'
when 'developer'
'write'
when 'reporter'
'read'
else
'read'
end
end
end

View File

@ -0,0 +1,8 @@
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

View File

@ -0,0 +1,12 @@
json.(project_invite_link, :id, :role, :is_apply, :sign)
json.user do
json.partial! "/users/user_simple", locals: {user: project_invite_link.user}
end
if project_invite_link&.project.present?
json.project do
json.partial! "/projects/detail", locals: {project: project_invite_link.project}
end
else
json.project nil
end

View File

@ -0,0 +1 @@
json.partial! 'detail', locals: { project_invite_link: @project_invite_link }

View File

@ -603,6 +603,13 @@ Rails.application.routes.draw do
post :cancel
end
end
resources :project_invite_links, only: [:index] do
collection do
get :current_link
post :generate_link
post :redirect_link
end
end
resources :webhooks, except: [:show, :new] do
member do
get :tasks

View File

@ -0,0 +1,16 @@
class CreateProjectInviteLinks < ActiveRecord::Migration[5.2]
def change
create_table :project_invite_links do |t|
t.references :project
t.references :user
t.integer :role, default: 4
t.boolean :is_apply, default: true
t.string :sign
t.datetime :expired_at
t.timestamps
end
add_index :project_invite_links, :sign
end
end

View File

@ -0,0 +1,6 @@
class AddProjectInviteLinkToAppliedProjects < ActiveRecord::Migration[5.2]
def change
add_column :forge_applied_projects, :project_invite_link_id, :integer
add_index :forge_applied_projects, :project_invite_link_id
end
end

View File

@ -0,0 +1,5 @@
require 'rails_helper'
RSpec.describe ProjectInviteLink, type: :model do
pending "add some examples to (or delete) #{__FILE__}"
end