diff --git a/app/controllers/api/v1/project_topics_controller.rb b/app/controllers/api/v1/project_topics_controller.rb new file mode 100644 index 000000000..5d353fbf4 --- /dev/null +++ b/app/controllers/api/v1/project_topics_controller.rb @@ -0,0 +1,46 @@ +class Api::V1::ProjectTopicsController < Api::V1::BaseController + + def index + @project_topics = ProjectTopic + @project_topics = @project_topics.ransack(name_cont: params[:keyword]) if params[:keyword].present? + @project_topics = @project_topics.includes(:projects) + @project_topics = kaminary_select_paginate(@project_topics) + end + + def create + ActiveRecord::Base.transaction do + @project = Project.find_by_id(create_params[:project_id]) + return render_not_found unless @project.present? + return render_error("请输入项目搜索标签名称.") unless create_params[:name].present? + + @project_topic = ProjectTopic.find_or_create_by!(name: create_params[:name].downcase) + @project_topic_ralate = @project_topic.project_topic_ralates.find_or_create_by!(project_id: create_params[:project_id]) + + if @project_topic.present? && @project_topic_ralate.present? + render_ok + else + render_error("项目关联搜索标签失败.") + end + end + end + + def destroy + ActiveRecord::Base.transaction do + @project = Project.find_by_id(create_params[:project_id]) + return render_not_found unless @project.present? + + @project_topic = ProjectTopic.find_by_id(params[:id]) + @project_topic_ralate = @project_topic.project_topic_ralates.find_by(project_id: @project.id) + if @project_topic_ralate.destroy! + render_ok + else + render_error("项目取消关联搜索标签失败.") + end + end + end + + private + def create_params + params.permit(:project_id, :name) + end +end \ No newline at end of file diff --git a/app/controllers/projects_controller.rb b/app/controllers/projects_controller.rb index a3ab7c879..a2d6ad618 100644 --- a/app/controllers/projects_controller.rb +++ b/app/controllers/projects_controller.rb @@ -34,7 +34,7 @@ class ProjectsController < ApplicationController def index scope = current_user.logged? ? Projects::ListQuery.call(params, current_user.id) : Projects::ListQuery.call(params) - @projects = kaminari_paginate(scope.includes(:project_category, :project_language, :repository, :project_educoder, :owner, :project_units)) + @projects = kaminari_paginate(scope.includes(:project_category, :project_language, :repository, :project_educoder, :owner, :project_units, :project_topics)) # @projects = paginate scope.includes(:project_category, :project_language, :repository, :project_educoder, :owner, :project_units) category_id = params[:category_id] @@ -130,6 +130,13 @@ class ProjectsController < ApplicationController # TODO: # 临时特殊处理修改website、lesson_url操作方法 if project_params.has_key?("website") + if params[:project_topic_names].present? && params[:project_topic_names].is_a?(Array) + ProjectTopicRalate.where(project: @project).destroy_all + params[:project_topic_names].each do |name| + project_topic = ProjectTopic.find_or_create_by!(name: name.downcase) + project_topic.project_topic_ralates.find_or_create_by!(project: @project) + end + end @project.update(project_params) elsif project_params.has_key?("default_branch") @project.update(project_params) diff --git a/app/models/concerns/matchable.rb b/app/models/concerns/matchable.rb index 5c013f951..0640e7c74 100644 --- a/app/models/concerns/matchable.rb +++ b/app/models/concerns/matchable.rb @@ -6,6 +6,7 @@ module Matchable scope :with_project_language, ->(language_id) { where(project_language_id: language_id) unless language_id.blank? } scope :with_project_type, ->(project_type) { where(project_type: project_type) if Project.project_types.include?(project_type) } scope :by_name_or_identifier, ->(search) { where("name like :search or identifier LIKE :search", :search => "%#{search.split(" ").join('|')}%") unless search.blank? } + scope :with_project_topic, ->(topic_id) {joins(:project_topics).where(project_topics: {id: topic_id}) unless topic_id.blank?} end end diff --git a/app/models/project.rb b/app/models/project.rb index 4a356cbc7..f08daae9e 100644 --- a/app/models/project.rb +++ b/app/models/project.rb @@ -126,6 +126,8 @@ class Project < ApplicationRecord has_many :webhooks, class_name: "Gitea::Webhook", primary_key: :gpid, foreign_key: :repo_id has_many :user_trace_tasks, dependent: :destroy has_many :project_invite_links, dependent: :destroy + has_many :project_topic_ralates, dependent: :destroy + has_many :project_topics, through: :project_topic_ralates 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 diff --git a/app/models/project_topic.rb b/app/models/project_topic.rb new file mode 100644 index 000000000..94c9df9c7 --- /dev/null +++ b/app/models/project_topic.rb @@ -0,0 +1,25 @@ +# == Schema Information +# +# Table name: project_topics +# +# id :integer not null, primary key +# user_id :integer +# name :string(255) +# position :integer default("0") +# projects_count :integer default("0") +# created_at :datetime not null +# updated_at :datetime not null +# +# Indexes +# +# index_project_topics_on_user_id (user_id) +# + +class ProjectTopic < ApplicationRecord + + belongs_to :user, optional: true + has_many :project_topic_ralates, dependent: :destroy + has_many :projects, through: :project_topic_ralates + + validates :name, uniqueness: { case_sensitive: false } +end diff --git a/app/models/project_topic_ralate.rb b/app/models/project_topic_ralate.rb new file mode 100644 index 000000000..d8638699f --- /dev/null +++ b/app/models/project_topic_ralate.rb @@ -0,0 +1,22 @@ +# == Schema Information +# +# Table name: project_topic_ralates +# +# id :integer not null, primary key +# project_topic_id :integer +# project_id :integer +# created_at :datetime not null +# updated_at :datetime not null +# +# Indexes +# +# index_project_topic_ralates_on_project_id (project_id) +# index_project_topic_ralates_on_project_topic_id (project_topic_id) +# + +class ProjectTopicRalate < ApplicationRecord + + belongs_to :project_topic, counter_cache: :projects_count + belongs_to :project + +end diff --git a/app/models/user.rb b/app/models/user.rb index 8801cd58d..5b6a3df44 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -182,6 +182,7 @@ class User < Owner has_many :assigned_issues, through: :issue_assigners, source: :issue has_many :issue_participants, foreign_key: :participant_id has_many :participant_issues, through: :issue_participants, source: :issue + has_many :project_topics # Groups and active users scope :active, lambda { where(status: [STATUS_ACTIVE, STATUS_EDIT_INFO]) } scope :like, lambda { |keywords| diff --git a/app/queries/projects/list_my_query.rb b/app/queries/projects/list_my_query.rb index 10f528881..27415543d 100644 --- a/app/queries/projects/list_my_query.rb +++ b/app/queries/projects/list_my_query.rb @@ -67,7 +67,7 @@ class Projects::ListMyQuery < ApplicationQuery keywords = params[:search].to_s.each_char.select { |c| c.bytes.first < 240 }.join('') q = projects.ransack(name_or_identifier_cont: keywords) - scope = q.result.includes(:project_category, :project_language,:owner, :repository, :has_pinned_users) + scope = q.result.includes(:project_category, :project_language,:owner, :repository, :has_pinned_users, :project_topics) 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" diff --git a/app/queries/projects/list_query.rb b/app/queries/projects/list_query.rb index 2b048bd87..72776f7a2 100644 --- a/app/queries/projects/list_query.rb +++ b/app/queries/projects/list_query.rb @@ -3,7 +3,7 @@ class Projects::ListQuery < ApplicationQuery attr_reader :params, :current_user_id - sort_columns :updated_on, :created_on, :forked_count, :praises_count, default_by: :updated_on, default_direction: :desc + sort_columns :updated_on, :created_on, :forked_count, :praises_count, default_by: :updated_on, default_direction: :desc, default_table: 'projects' def initialize(params, current_user_id=nil) @params = params @@ -32,6 +32,7 @@ class Projects::ListQuery < ApplicationQuery collection = by_project_type(collection) collection = by_project_category(collection) collection = by_project_language(collection) + collection = by_project_topic(collection) collection end @@ -74,6 +75,10 @@ class Projects::ListQuery < ApplicationQuery (params[:pinned].present? && params[:category_id].present?) ? items.pinned : items end + def by_project_topic(items) + items.with_project_topic(params[:topic_id]) + end + # 优化排序 def optimize_sorting(relations, sort_by) if sort_by == "updated_on" diff --git a/app/views/api/v1/project_topics/index.json.jbuilder b/app/views/api/v1/project_topics/index.json.jbuilder new file mode 100644 index 000000000..5a39aaa56 --- /dev/null +++ b/app/views/api/v1/project_topics/index.json.jbuilder @@ -0,0 +1,4 @@ +json.total_count @project_topics.total_count +json.project_topics @project_topics.each do |topic| + json.(topic, :id, :name, :projects_count) +end \ No newline at end of file diff --git a/app/views/projects/_project_detail.json.jbuilder b/app/views/projects/_project_detail.json.jbuilder index c9e03fa22..c5b071abf 100644 --- a/app/views/projects/_project_detail.json.jbuilder +++ b/app/views/projects/_project_detail.json.jbuilder @@ -50,3 +50,6 @@ json.language do json.name project.project_language.name end end +json.topics project.project_topics.each do |topic| + json.(topic, :id, :name) +end \ No newline at end of file diff --git a/app/views/projects/index.json.jbuilder b/app/views/projects/index.json.jbuilder index 31d0db9c5..d3b96ab18 100644 --- a/app/views/projects/index.json.jbuilder +++ b/app/views/projects/index.json.jbuilder @@ -48,4 +48,7 @@ json.projects @projects do |project| json.name project.project_language.name end end + json.topics project.project_topics.each do |topic| + json.(topic, :id, :name) + end end diff --git a/app/views/projects/update.json.jbuilder b/app/views/projects/update.json.jbuilder index 01e70377e..3dd8c3cda 100644 --- a/app/views/projects/update.json.jbuilder +++ b/app/views/projects/update.json.jbuilder @@ -6,4 +6,7 @@ json.project_category_id @project.project_category_id json.project_language_id @project.project_language_id json.is_public @project.is_public json.website @project.website -json.lesson_url @project.lesson_url \ No newline at end of file +json.lesson_url @project.lesson_url +json.topics @project.project_topics.each do |topic| + json.(topic, :id, :name) +end \ No newline at end of file diff --git a/app/views/repositories/detail.json.jbuilder b/app/views/repositories/detail.json.jbuilder index 508d4c658..1164f4030 100644 --- a/app/views/repositories/detail.json.jbuilder +++ b/app/views/repositories/detail.json.jbuilder @@ -21,6 +21,9 @@ json.mirror_url @project&.repository.remote_mirror_url json.mirror @project&.repository.mirror_url.present? json.type @project.numerical_for_project_type json.open_devops @project.open_devops? +json.topics @project.project_topics.each do |topic| + json.(topic, :id, :name) +end unless @project.common? json.mirror_status @repository.mirror_status diff --git a/config/routes/api.rb b/config/routes/api.rb index f39fa76c5..0a02065e2 100644 --- a/config/routes/api.rb +++ b/config/routes/api.rb @@ -2,7 +2,7 @@ defaults format: :json do namespace :api do namespace :v1 do scope ':owner' do - resource :users, path: '/', only: [:show, :update, :edit, :destroy] do + resource :users, path: '/', only: [:update, :edit, :destroy] do collection do get :send_email_vefify_code post :check_password @@ -20,7 +20,7 @@ defaults format: :json do scope ':repo', constraints: { repo: /[^\/]+/ } do # projects - resource :projects, path: '/', only: [:show, :update, :edit, :destroy] do + resource :projects, path: '/', only: [:show, :update, :edit] do collection do get :compare get :blame @@ -97,6 +97,7 @@ defaults format: :json do end resources :projects, only: [:index] + resources :project_topics, only: [:index, :create, :destroy] end diff --git a/db/migrate/20230321022108_create_project_topics.rb b/db/migrate/20230321022108_create_project_topics.rb new file mode 100644 index 000000000..07a03e20d --- /dev/null +++ b/db/migrate/20230321022108_create_project_topics.rb @@ -0,0 +1,19 @@ +class CreateProjectTopics < ActiveRecord::Migration[5.2] + def change + create_table :project_topics do |t| + t.references :user + t.string :name + t.integer :position, default: 0 + t.integer :projects_count, default: 0 + + t.timestamps + end + + create_table :project_topic_ralates do |t| + t.belongs_to :project_topic, index: true + t.belongs_to :project, index: true + + t.timestamps + end + end +end