issue links

This commit is contained in:
呱呱呱 2023-11-07 17:16:25 +08:00
parent 00448f0f01
commit c752671453
15 changed files with 150 additions and 31 deletions

View File

@ -0,0 +1,2 @@
// Place all the behaviors and hooks related to the matching controller here.
// All this logic will automatically be available in application.js.

View File

@ -0,0 +1,3 @@
// Place all the styles related to the api/pm/issue_links controller here.
// They will automatically be included in application.css.
// You can use Sass (SCSS) here: http://sass-lang.com/

View File

@ -31,7 +31,7 @@ class Api::Pm::BaseController < ApplicationController
def load_issue
return render_parameter_missing if params[:pm_project_id].blank?
@issue = @project.issues.issue_issue.where(pm_project_id: params[:pm_project_id]).find_by_id(params[:id])
@issue = @project.issues.issue_issue.where(pm_project_id: params[:pm_project_id]).find_by_id(params[:issue_id])
render_not_found('疑修不存在!') if @issue.blank?
end
# 具有对仓库的管理权限

View File

@ -0,0 +1,25 @@
class Api::Pm::IssueLinksController < Api::Pm::BaseController
before_action :load_project
before_action :load_issue
def index
@links = @issue.pm_links.where(be_linkable_type: 'Issue')
end
def create
@link = @issue.pm_links.find_or_create_by(be_linkable_type: 'Issue', be_linkable_id: params[:link_id])
data = {
data: {
id: @link.id,
issue_id: @link.linkable_id,
linked_issue_id: @link.be_linkable_id
}
}
render_ok(data)
end
def destroy
@link = @issue.pm_links.find params[:id]
@link.destroy
render_ok
end
end

View File

@ -1,7 +1,7 @@
class Api::Pm::IssuesController < Api::Pm::BaseController
before_action :require_login, except: [:index]
before_action :load_project
before_action :load_issue, only: %i[show update destroy]
before_action :load_issue, only: %i[show update destroy link_index]
before_action :load_issues, only: %i[batch_update batch_destroy]
before_action :check_issue_operate_permission, only: %i[update destroy]
@ -19,6 +19,12 @@ class Api::Pm::IssuesController < Api::Pm::BaseController
render 'api/v1/issues/index'
end
def link_index
end
def show
@issue.associate_attachment_container
render 'api/v1/issues/show'
@ -91,6 +97,13 @@ class Api::Pm::IssuesController < Api::Pm::BaseController
return if params[:project_id].to_i.zero?
render_forbidden('您没有操作权限!') unless @project.member?(current_user) || current_user.admin? || @issue.user == current_user
end
def load_issue
return render_parameter_missing if params[:pm_project_id].blank?
@issue = @project.issues.issue_issue.where(pm_project_id: params[:pm_project_id]).find_by_id(params[:id])
render_not_found('疑修不存在!') if @issue.blank?
end
def load_issues
return render_error('请输入正确的ID数组') unless params[:ids].is_a?(Array)
params[:ids].each do |id|

View File

@ -0,0 +1,2 @@
module Api::Pm::IssueLinksHelper
end

View File

@ -60,16 +60,16 @@ class Issue < ApplicationRecord
has_many :project_trends, as: :trend, dependent: :destroy
has_one :pull_request
# belongs_to :issue_tag,optional: true
belongs_to :priority, :class_name => 'IssuePriority', foreign_key: :priority_id,optional: true
belongs_to :priority, class_name: 'IssuePriority', foreign_key: :priority_id,optional: true
belongs_to :version, foreign_key: :fixed_version_id,optional: true, counter_cache: true
belongs_to :user,optional: true, foreign_key: :author_id
belongs_to :issue_status, foreign_key: :status_id,optional: true
has_many :commit_issues
has_many :attachments, as: :container, dependent: :destroy
# has_many :memos
has_many :journals, :as => :journalized, :dependent => :destroy
has_many :journals, as: :journalized, dependent: :destroy
has_many :journal_details, through: :journals
has_many :claims, :dependent => :destroy
has_many :claims, dependent: :destroy
has_many :claim_users, through: :claims, source: :user
has_many :issue_tags_relates, dependent: :destroy
has_many :issue_tags, through: :issue_tags_relates
@ -79,19 +79,21 @@ class Issue < ApplicationRecord
has_many :assigners, through: :issue_assigners
has_many :issue_participants, dependent: :destroy
has_many :participants, through: :issue_participants
has_many :show_participants, -> {joins(:issue_participants).where.not(issue_participants: {participant_type: "atme"}).distinct}, through: :issue_participants, source: :participant
has_many :show_participants, -> {joins(:issue_participants).where.not(issue_participants: {participant_type: 'atme'}).distinct}, through: :issue_participants, source: :participant
has_many :show_assigners, -> {joins(:issue_assigners).distinct}, through: :issue_assigners, source: :assigner
has_many :show_issue_tags, -> {joins(:issue_tags_relates).distinct}, through: :issue_tags_relates, source: :issue_tag
has_many :comment_journals, -> {where.not(notes: nil)}, class_name: "Journal", :as => :journalized
has_many :operate_journals, -> {where(notes: nil)}, class_name: "Journal", :as => :journalized
has_many :pull_attached_issues, dependent: :destroy
has_many :comment_journals, -> {where.not(notes: nil)}, class_name: 'Journal', as: :journalized
has_many :operate_journals, -> {where(notes: nil)}, class_name: 'Journal', as: :journalized
has_many :pull_attached_issues, dependent: :destroy
has_many :attach_pull_requests, through: :pull_attached_issues, source: :pull_request
# PM 关联工作项目
has_many :pm_links, as: :linkable, dependent: :destroy
scope :issue_includes, ->{includes(:user)}
scope :issue_many_includes, ->{includes(journals: :user)}
scope :issue_issue, ->{where(issue_classify: [nil,"issue"])}
scope :issue_pull_request, ->{where(issue_classify: "pull_request")}
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)}
scope :opened, ->{where.not(status_id: 5)}
@ -100,27 +102,27 @@ class Issue < ApplicationRecord
after_destroy :update_closed_issues_count_in_project!, :decre_project_common, :decre_user_statistic, :decre_platform_statistic
def incre_project_common
CacheAsyncSetJob.perform_later("project_common_service", {issues: 1}, self.project_id)
CacheAsyncSetJob.perform_later('project_common_service', {issues: 1}, self.project_id)
end
def decre_project_common
CacheAsyncSetJob.perform_later("project_common_service", {issues: -1}, self.project_id)
CacheAsyncSetJob.perform_later('project_common_service', {issues: -1}, self.project_id)
end
def incre_user_statistic
CacheAsyncSetJob.perform_later("user_statistic_service", {issue_count: 1}, self.author_id)
CacheAsyncSetJob.perform_later('user_statistic_service', {issue_count: 1}, self.author_id)
end
def decre_user_statistic
CacheAsyncSetJob.perform_later("user_statistic_service", {issue_count: -1}, self.author_id)
CacheAsyncSetJob.perform_later('user_statistic_service', {issue_count: -1}, self.author_id)
end
def incre_platform_statistic
CacheAsyncSetJob.perform_later("platform_statistic_service", {issue_count: 1})
CacheAsyncSetJob.perform_later('platform_statistic_service', {issue_count: 1})
end
def decre_platform_statistic
CacheAsyncSetJob.perform_later("platform_statistic_service", {issue_count: -1})
CacheAsyncSetJob.perform_later('platform_statistic_service', {issue_count: -1})
end
def get_assign_user
@ -129,20 +131,20 @@ class Issue < ApplicationRecord
def create_journal_detail(change_files, issue_files, issue_file_ids, user_id)
journal_params = {
journalized_id: self.id, journalized_type: "Issue", user_id: user_id
journalized_id: self.id, journalized_type: 'Issue', user_id: user_id
}
journal = Journal.new journal_params
if journal.save
if change_files
old_attachment_names = self.attachments.select(:filename,:id).where(id: issue_file_ids).pluck(:filename).join(",")
new_attachment_name = self.attachments.select(:filename,:id).where(id: issue_files).pluck(:filename).join(",")
journal.journal_details.create(property: "attachment", prop_key: "#{issue_files.size}", old_value: old_attachment_names, value: new_attachment_name)
old_attachment_names = self.attachments.select(:filename,:id).where(id: issue_file_ids).pluck(:filename).join(',')
new_attachment_name = self.attachments.select(:filename,:id).where(id: issue_files).pluck(:filename).join(',')
journal.journal_details.create(property: 'attachment', prop_key: "#{issue_files.size}", old_value: old_attachment_names, value: new_attachment_name)
end
change_values = %w(subject description is_private assigned_to_id tracker_id status_id priority_id fixed_version_id start_date due_date estimated_hours done_ratio issue_tags_value issue_type token branch_name)
change_values.each do |at|
if self.send("saved_change_to_#{at}?")
journal.journal_details.create(property: "attr", prop_key: "#{at}", old_value: self.send("#{at}_before_last_save"), value: self.send(at))
journal.journal_details.create(property: 'attr', prop_key: "#{at}", old_value: self.send("#{at}_before_last_save"), value: self.send(at))
end
end
end
@ -150,11 +152,11 @@ class Issue < ApplicationRecord
def custom_journal_detail(prop_key, old_value, value, user_id)
journal_params = {
journalized_id: self.id, journalized_type: "Issue", user_id: user_id
journalized_id: self.id, journalized_type: 'Issue', user_id: user_id
}
journal = Journal.new journal_params
if journal.save
journal.journal_details.create(property: "attr", prop_key: prop_key, old_value: old_value, value: value)
journal.journal_details.create(property: 'attr', prop_key: prop_key, old_value: old_value, value: value)
end
end
@ -180,14 +182,14 @@ class Issue < ApplicationRecord
def get_issue_tags_name
if issue_tags.present?
issue_tags.select(:name).uniq.pluck(:name).join(",")
issue_tags.select(:name).uniq.pluck(:name).join(',')
else
nil
end
end
def only_reply_journals
journals.where.not(notes: [nil, ""]).journal_includes.limit(2)
journals.where.not(notes: [nil, '']).journal_includes.limit(2)
end
def change_versions_count
@ -232,15 +234,15 @@ class Issue < ApplicationRecord
att_ids += self.description.to_s.scan(/\/api\/attachments\/.+\"/).map{|s|s.match(/\d+/)[0]}
att_ids += self.description.to_s.scan(/\/api\/attachments\/\d+/).map{|s|s.match(/\d+/)[0]}
if att_ids.present?
Attachment.where(id: att_ids).where("container_type IS NULL OR container_type = 'Issue'").update_all(container_id: self.project_id, container_type: "Project")
Attachment.where(id: att_ids).where("container_type IS NULL OR container_type = 'Issue'").update_all(container_id: self.project_id, container_type: 'Project')
end
end
def to_builder
Jbuilder.new do |issue|
issue.(self, :id, :project_issues_index, :subject, :description, :branch_name, :start_date, :due_date)
issue.created_at self.created_on.strftime("%Y-%m-%d %H:%M")
issue.updated_at self.updated_on.strftime("%Y-%m-%d %H:%M")
issue.created_at self.created_on.strftime('%Y-%m-%d %H:%M')
issue.updated_at self.updated_on.strftime('%Y-%m-%d %H:%M')
issue.tags self.show_issue_tags.map{|t| JSON.parse(t.to_builder.target!)}
issue.status self.issue_status.to_builder
if self.priority.present?

25
app/models/pm_link.rb Normal file
View File

@ -0,0 +1,25 @@
# == Schema Information
#
# Table name: pm_links
#
# id :integer not null, primary key
# be_linkable_type :string(255) not null
# be_linkable_id :integer not null
# linkable_type :string(255) not null
# linkable_id :integer not null
# created_at :datetime not null
# updated_at :datetime not null
#
# Indexes
#
# index_pm_links_on_linkable_id (linkable_id)
# index_pm_links_on_linkable_type (linkable_type)
#
class PmLink < ApplicationRecord
belongs_to :linkable, polymorphic: true
def be_linkable
be_linkable_type.constantize.find be_linkable_id
end
end

View File

@ -70,8 +70,10 @@ class Api::V1::Issues::ListService < ApplicationService
issues = issues.where(fixed_version_id: milestone_id) if milestone_id.present?
#pm相关
# root_id,
issues = issues.where(root_id: root_id) if root_id.present?
# root_id
if pm_project_id.present?
issues = issues.where(root_id: root_id.present? ? nil : root_id)
end
# pm_issue_type
issues = issues.where(pm_issue_type: pm_issue_type) if pm_issue_type.present?

View File

@ -0,0 +1,7 @@
json.links @links.each do |link|
json.id link.id
json.issue do
json.partial! "api/v1/issues/simple_detail", locals: {issue: link.be_linkable}
end
end

View File

@ -9,6 +9,7 @@ defaults format: :json do
get :tags
get :statues
end
resources :issue_links
resources :journals do
member do

View File

@ -0,0 +1,12 @@
class CreatePmLinks < ActiveRecord::Migration[5.2]
def change
create_table :pm_links do |t|
t.string :be_linkable_type, null: false
t.integer :be_linkable_id, null: false
t.string :linkable_type, null: false, index: true
t.integer :linkable_id, null: false, index: true
t.timestamps
end
end
end

View File

@ -0,0 +1,5 @@
require 'rails_helper'
RSpec.describe Api::Pm::IssueLinksController, type: :controller do
end

View File

@ -0,0 +1,15 @@
require 'rails_helper'
# Specs in this file have access to a helper object that includes
# the Api::Pm::IssueLinksHelper. For example:
#
# describe Api::Pm::IssueLinksHelper do
# describe "string concat" do
# it "concats two strings with spaces" do
# expect(helper.concat_strings("this","that")).to eq("this that")
# end
# end
# end
RSpec.describe Api::Pm::IssueLinksHelper, type: :helper do
pending "add some examples to (or delete) #{__FILE__}"
end

View File

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