From c75267145393b596d023322e5f1099f74360f83b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=91=B1=E5=91=B1=E5=91=B1?= Date: Tue, 7 Nov 2023 17:16:25 +0800 Subject: [PATCH] issue links --- app/assets/javascripts/api/pm/issue_links.js | 2 + .../stylesheets/api/pm/issue_links.scss | 3 + app/controllers/api/pm/base_controller.rb | 2 +- .../api/pm/issue_links_controller.rb | 25 +++++++++ app/controllers/api/pm/issues_controller.rb | 15 ++++- app/helpers/api/pm/issue_links_helper.rb | 2 + app/models/issue.rb | 56 ++++++++++--------- app/models/pm_link.rb | 25 +++++++++ app/services/api/v1/issues/list_service.rb | 6 +- .../api/pm/issue_links/index.json.jbuilder | 7 +++ config/routes/api.rb | 1 + db/migrate/20231107072541_create_pm_links.rb | 12 ++++ .../api/pm/issue_links_controller_spec.rb | 5 ++ .../helpers/api/pm/issue_links_helper_spec.rb | 15 +++++ spec/models/pm_link_spec.rb | 5 ++ 15 files changed, 150 insertions(+), 31 deletions(-) create mode 100644 app/assets/javascripts/api/pm/issue_links.js create mode 100644 app/assets/stylesheets/api/pm/issue_links.scss create mode 100644 app/controllers/api/pm/issue_links_controller.rb create mode 100644 app/helpers/api/pm/issue_links_helper.rb create mode 100644 app/models/pm_link.rb create mode 100644 app/views/api/pm/issue_links/index.json.jbuilder create mode 100644 db/migrate/20231107072541_create_pm_links.rb create mode 100644 spec/controllers/api/pm/issue_links_controller_spec.rb create mode 100644 spec/helpers/api/pm/issue_links_helper_spec.rb create mode 100644 spec/models/pm_link_spec.rb diff --git a/app/assets/javascripts/api/pm/issue_links.js b/app/assets/javascripts/api/pm/issue_links.js new file mode 100644 index 000000000..dee720fac --- /dev/null +++ b/app/assets/javascripts/api/pm/issue_links.js @@ -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. diff --git a/app/assets/stylesheets/api/pm/issue_links.scss b/app/assets/stylesheets/api/pm/issue_links.scss new file mode 100644 index 000000000..730f1f3e1 --- /dev/null +++ b/app/assets/stylesheets/api/pm/issue_links.scss @@ -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/ diff --git a/app/controllers/api/pm/base_controller.rb b/app/controllers/api/pm/base_controller.rb index a78d29b38..f2850ec95 100644 --- a/app/controllers/api/pm/base_controller.rb +++ b/app/controllers/api/pm/base_controller.rb @@ -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 # 具有对仓库的管理权限 diff --git a/app/controllers/api/pm/issue_links_controller.rb b/app/controllers/api/pm/issue_links_controller.rb new file mode 100644 index 000000000..01ca9a059 --- /dev/null +++ b/app/controllers/api/pm/issue_links_controller.rb @@ -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 diff --git a/app/controllers/api/pm/issues_controller.rb b/app/controllers/api/pm/issues_controller.rb index 5ec4d7cfe..29a61542c 100644 --- a/app/controllers/api/pm/issues_controller.rb +++ b/app/controllers/api/pm/issues_controller.rb @@ -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| diff --git a/app/helpers/api/pm/issue_links_helper.rb b/app/helpers/api/pm/issue_links_helper.rb new file mode 100644 index 000000000..ff7d1ef33 --- /dev/null +++ b/app/helpers/api/pm/issue_links_helper.rb @@ -0,0 +1,2 @@ +module Api::Pm::IssueLinksHelper +end diff --git a/app/models/issue.rb b/app/models/issue.rb index 76208bdee..c64cfadaa 100644 --- a/app/models/issue.rb +++ b/app/models/issue.rb @@ -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? diff --git a/app/models/pm_link.rb b/app/models/pm_link.rb new file mode 100644 index 000000000..91962bf7b --- /dev/null +++ b/app/models/pm_link.rb @@ -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 diff --git a/app/services/api/v1/issues/list_service.rb b/app/services/api/v1/issues/list_service.rb index 3fd8e3adc..81eff04f7 100644 --- a/app/services/api/v1/issues/list_service.rb +++ b/app/services/api/v1/issues/list_service.rb @@ -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? diff --git a/app/views/api/pm/issue_links/index.json.jbuilder b/app/views/api/pm/issue_links/index.json.jbuilder new file mode 100644 index 000000000..066319e13 --- /dev/null +++ b/app/views/api/pm/issue_links/index.json.jbuilder @@ -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 \ No newline at end of file diff --git a/config/routes/api.rb b/config/routes/api.rb index 3789100d6..7f5fb99cf 100644 --- a/config/routes/api.rb +++ b/config/routes/api.rb @@ -9,6 +9,7 @@ defaults format: :json do get :tags get :statues end + resources :issue_links resources :journals do member do diff --git a/db/migrate/20231107072541_create_pm_links.rb b/db/migrate/20231107072541_create_pm_links.rb new file mode 100644 index 000000000..84cdb00e8 --- /dev/null +++ b/db/migrate/20231107072541_create_pm_links.rb @@ -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 diff --git a/spec/controllers/api/pm/issue_links_controller_spec.rb b/spec/controllers/api/pm/issue_links_controller_spec.rb new file mode 100644 index 000000000..a80df2c77 --- /dev/null +++ b/spec/controllers/api/pm/issue_links_controller_spec.rb @@ -0,0 +1,5 @@ +require 'rails_helper' + +RSpec.describe Api::Pm::IssueLinksController, type: :controller do + +end diff --git a/spec/helpers/api/pm/issue_links_helper_spec.rb b/spec/helpers/api/pm/issue_links_helper_spec.rb new file mode 100644 index 000000000..924962a6c --- /dev/null +++ b/spec/helpers/api/pm/issue_links_helper_spec.rb @@ -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 diff --git a/spec/models/pm_link_spec.rb b/spec/models/pm_link_spec.rb new file mode 100644 index 000000000..d911d5b7e --- /dev/null +++ b/spec/models/pm_link_spec.rb @@ -0,0 +1,5 @@ +require 'rails_helper' + +RSpec.describe PmLink, type: :model do + pending "add some examples to (or delete) #{__FILE__}" +end