This commit is contained in:
2026-05-09 19:53:30 +09:00
parent 772c66aa64
commit de86879e79
7 changed files with 339 additions and 67 deletions
+79 -42
View File
@@ -44,7 +44,7 @@ class PostsController < ApplicationController
filtered_posts
.joins("LEFT JOIN (#{ pt_max_sql }) pt_max ON pt_max.post_id = posts.id")
.reselect('posts.*', Arel.sql("#{ updated_at_all_sql } AS updated_at_all"))
.preload(tags: [:materials, { tag_name: :wiki_page }])
.preload(tags: [:deerjikists, :materials, { tag_name: :wiki_page }])
.with_attached_thumbnail
q = q.where('posts.url LIKE ?', "%#{ url }%") if url
@@ -95,7 +95,7 @@ class PostsController < ApplicationController
end
def random
post = filtered_posts.preload(tags: [:materials, { tag_name: :wiki_page }])
post = filtered_posts.preload(tags: [:deerjikists, :materials, { tag_name: :wiki_page }])
.order('RAND()')
.first
return head :not_found unless post
@@ -104,7 +104,7 @@ class PostsController < ApplicationController
end
def show
post = Post.includes(tags: [:materials, { tag_name: :wiki_page }]).find_by(id: params[:id])
post = Post.includes(tags: [:deerjikists, :materials, { tag_name: :wiki_page }]).find_by(id: params[:id])
return head :not_found unless post
render json: PostRepr.base(post, current_user)
@@ -173,8 +173,12 @@ class PostsController < ApplicationController
return head :unauthorized unless current_user
return head :forbidden unless current_user.gte_member?
base_version_no = parse_base_version_no
force = truthy_param?(params[:force])
merge = truthy_param?(params[:merge])
return head :bad_request if force && merge
base_version_no = nil
base_version_no = parse_base_version_no unless force
title = params[:title].presence
tag_names = params[:tags].to_s.split
@@ -186,12 +190,17 @@ class PostsController < ApplicationController
conflict_json = nil
ApplicationRecord.transaction do
post = Post.find(params[:id].to_i)
post = Post.lock.find(params[:id].to_i)
base_version = post.post_versions.find_by!(version_no: base_version_no)
base_version = nil
base_snapshot = nil
current_snapshot = nil
unless force
base_version = post.post_versions.find_by!(version_no: base_version_no)
base_snapshot = post_snapshot_from_version(base_version)
current_snapshot = post_snapshot_from_record(post)
base_snapshot = post_snapshot_from_version(base_version)
current_snapshot = post_snapshot_from_record(post)
end
incoming_snapshot = post_incoming_snapshot(post,
title:,
original_created_from:,
@@ -199,29 +208,28 @@ class PostsController < ApplicationController
tag_names:,
parent_post_ids:)
if !(force) && post.version_no != base_version_no
conflict_json = post_conflict_json(post:,
base_version_no:,
base_snapshot:,
current_snapshot:,
incoming_snapshot:)
raise ActiveRecord::Rollback
end
snapshot_to_apply =
if post.version_no == base_version_no || force
incoming_snapshot
else
changes = post_snapshot_changes(base_snapshot, current_snapshot, incoming_snapshot)
conflicts = changes.select { |change| change[:conflict] }
PostVersionRecorder.ensure_snapshot!(post, created_by_user: current_user)
if merge && conflicts.empty?
merge_post_snapshots(base_snapshot, current_snapshot, incoming_snapshot)
else
conflict_json = post_conflict_json(post:,
base_version_no:,
base_snapshot:,
current_snapshot:,
incoming_snapshot:,
changes:,
conflicts:)
raise ActiveRecord::Rollback
end
end
post.update!(title:, original_created_from:, original_created_before:)
normalised_tags = Tag.normalise_tags!(tag_names, with_tagme: false)
TagVersioning.record_tag_snapshots!(normalised_tags, created_by_user: current_user)
tags = post.tags.nico.to_a + normalised_tags
tags = Tag.expand_parent_tags(tags)
sync_post_tags!(post, tags)
sync_parent_posts!(post, parent_post_ids)
PostVersionRecorder.record!(post:, event_type: :update, created_by_user: current_user)
apply_post_snapshot!(post, snapshot_to_apply)
end
return render json: conflict_json, status: :conflict if conflict_json
@@ -253,7 +261,7 @@ class PostsController < ApplicationController
pts = pts.where(post_id: id) if id.present?
pts = pts.where(tag_id:) if tag_id.present?
pts = pts.includes(:post, :created_user, :deleted_user,
tag: [:materials, { tag_name: :wiki_page }])
tag: [:deerjikists, :materials, { tag_name: :wiki_page }])
events = []
pts.each do |pt|
@@ -446,11 +454,11 @@ class PostsController < ApplicationController
{ title: version.title,
original_created_from: snapshot_time(version.original_created_from),
original_created_before: snapshot_time(version.original_created_before),
tag_names: version.tags.to_s.split.sort,
tag_names: version.tags.to_s.split.filter { !(_1.start_with?('nico:')) }.sort,
parent_post_ids: snapshot_parent_post_ids_from_version(version) }
end
def post_snapshot_form_record post
def post_snapshot_from_record post
{ title: post.title,
original_created_from: snapshot_time(post.original_created_from),
original_created_before: snapshot_time(post.original_created_before),
@@ -460,7 +468,7 @@ class PostsController < ApplicationController
def post_incoming_snapshot post, title:, original_created_from:, original_created_before:,
tag_names:, parent_post_ids:
{ title:
{ title:,
original_created_from: snapshot_time(original_created_from),
original_created_before: snapshot_time(original_created_before),
tag_names: incoming_tag_names_for_snapshot(post, tag_names),
@@ -488,17 +496,16 @@ class PostsController < ApplicationController
def incoming_tag_names_for_snapshot post, raw_tag_names
manual_names = normalised_manual_tag_names_for_snapshot(raw_tag_names)
nico_names = post.tags.nico.joins(:tag_name).pluck('tag_names.name')
existing_tags =
Tag
.joins(:tag_name)
.where(tag_names: { name: manual_names + nico_names })
.where(tag_names: { name: manual_names })
.to_a
expanded_names = Tag.expand_parent_tags(existing_tags).map(&:name)
(manual_names + nico_names + expanded_names).uniq.sort
(manual_names + expanded_names).uniq.sort
end
def normalised_manual_tag_names_for_snapshot raw_tag_names
@@ -528,10 +535,7 @@ class PostsController < ApplicationController
end
def post_conflict_json post:, base_version_no:, base_snapshot:,
current_snapshot:, incoming_snapshot:
changes = post_snapshot_changes(base_snapshot, current_snapshot, incoming_snapshot)
conflicts = changes.select { |change| change[:conflict] }
current_snapshot:, incoming_snapshot:, changes:, conflicts:
{ error: 'conflict',
message: '競合が発生しました.',
post_id: post.id,
@@ -548,9 +552,9 @@ class PostsController < ApplicationController
def post_snapshot_changes base_snapshot, current_snapshot, incoming_snapshot
[scalar_snapshot_change(:title, 'タイトル',
base_snapshot, current_snapshot, incoming_snapshot),
scalar_snapshot_change(:original_created_from, '元コンテンツ作成日時(開始',
scalar_snapshot_change(:original_created_from, 'オリジナルの作成日時(以降',
base_snapshot, current_snapshot, incoming_snapshot),
scalar_snapshot_change(:original_created_before, '元コンテンツ作成日時(終了',
scalar_snapshot_change(:original_created_before, 'オリジナルの作成日時(より前',
base_snapshot, current_snapshot, incoming_snapshot),
set_snapshot_change(:tag_names, 'タグ',
base_snapshot, current_snapshot, incoming_snapshot),
@@ -606,4 +610,37 @@ class PostsController < ApplicationController
added_by_me:, removed_by_me:
(added_by_current & removed_by_me).present? || (removed_by_current & added_by_me).present?
end
def apply_post_snapshot! post, snapshot
PostVersionRecorder.ensure_snapshot!(post, created_by_user: current_user)
post.update!(title: snapshot[:title],
original_created_from: snapshot[:original_created_from],
original_created_before: snapshot[:original_created_before])
tags = Tag.normalise_tags!(snapshot[:tag_names], with_tagme: false)
TagVersioning.record_tag_snapshots!(tags, created_by_user: current_user)
tags = Tag.expand_parent_tags(tags)
sync_post_tags!(post, tags)
sync_parent_posts!(post, snapshot[:parent_post_ids])
PostVersionRecorder.record!(post:, event_type: :update, created_by_user: current_user)
end
def merge_post_snapshots base_snapshot, current_snapshot, incoming_snapshot
[:title, :original_created_from, :original_created_before, :tag_names, :parent_post_ids].map {
[_1, merge_scaler_snapshot_value(base_snapshot[_1],
current_snapshot[_1],
incoming_snapshot[_1])]
}.to_h
end
def merge_scaler_snapshot_value base, current, mine
return mine if current == base
return current if mine == base || current == mine
raise ArgumentError, '競合してゐる項目はマージできません.'
end
end