ファイル
btrc-hub/backend/app/services/post_bulk_creator.rb
T
2026-07-17 23:14:37 +09:00

147 行
4.5 KiB
Ruby

class PostBulkCreator
def initialize actor:, posts:, thumbnails:
@actor = actor
@posts = posts
@thumbnails = thumbnails
end
def run
results = @posts.each_with_index.map { |attributes, index|
create_row(attributes, index)
}
{ results: }
end
private
def create_row attributes, index
preflight =
PostCreatePreflight.new(
attributes: attributes,
thumbnail: thumbnail_for(index, attributes)).run
if preflight[:existing_post].present?
return {
status: 'skipped',
existing_post: preflight[:existing_post] }
end
post = PostCreator.new(
actor: @actor,
attributes: normalised_attributes(attributes, preflight, index)).create!
result = {
status: 'created',
post: { id: post.id } }
result[:field_warnings] = preflight[:field_warnings] if preflight[:field_warnings].present?
result[:base_warnings] = preflight[:base_warnings] if preflight[:base_warnings].present?
result
rescue PostCreatePreflight::ValidationFailed => e
{
status: 'failed',
recoverable: true,
errors: e.fields,
base_errors: e.base_errors }
rescue ActiveRecord::RecordInvalid => e
existing_post = existing_post_for_race(attributes, e.record)
if existing_post.present?
return {
status: 'skipped',
existing_post: existing_post }
end
{
status: 'failed',
recoverable: true,
errors: e.record.errors.to_hash,
base_errors: e.record.errors[:base] }
rescue ActiveRecord::RecordNotUnique => e
if e.message.include?('index_posts_on_url')
existing_post = existing_post_for_race(attributes)
return {
status: 'skipped',
existing_post: existing_post } if existing_post.present?
end
Rails.logger.error(
"post_bulk_creator_record_not_unique #{ { error: e.class.name,
message: e.message }.to_json }")
{
status: 'failed',
recoverable: false,
errors: { base: ['登録中にエラーが発生しました.'] },
base_errors: [] }
rescue Tag::NicoTagNormalisationError
{
status: 'failed',
recoverable: true,
errors: { tags: ['ニコニコ・タグは直接指定できません.'] },
base_errors: [] }
rescue Tag::DeprecatedTagNormalisationError
{
status: 'failed',
recoverable: true,
errors: { tags: ['廃止済みタグは付与できません.'] },
base_errors: [] }
rescue PostCreator::VideoMsParseError
{
status: 'failed',
recoverable: true,
errors: { video_ms: ['動画時間の記法が不正です.'] },
base_errors: [] }
rescue Post::RemoteThumbnailFetchFailed
{
status: 'failed',
recoverable: true,
errors: { thumbnail_base: ['サムネイル画像の取得に失敗しました.'] },
base_errors: [] }
rescue ArgumentError => e
{
status: 'failed',
recoverable: true,
errors: { base: [e.message] },
base_errors: [] }
rescue StandardError => e
Rails.logger.error(
"post_bulk_creator_failure #{ { error: e.class.name,
message: e.message }.to_json }")
{
status: 'failed',
recoverable: false,
errors: { base: ['登録中にエラーが発生しました.'] },
base_errors: [] }
end
def normalised_attributes attributes, preflight, index
{
url: preflight[:url],
title: preflight[:title],
thumbnail_base: preflight[:thumbnail_base],
thumbnail: thumbnail_for(index, attributes),
tags: preflight[:tags],
parent_post_ids: preflight[:parent_post_ids],
original_created_from: preflight[:original_created_from],
original_created_before: preflight[:original_created_before],
duration: preflight[:duration],
video_ms: preflight[:video_ms] }
end
def thumbnail_for index, attributes
return nil if attributes['thumbnail_base'].present? || attributes[:thumbnail_base].present?
@thumbnails[index]
end
def existing_post_for_race attributes, record = nil
return nil if record.present? && !(record.errors.of_kind?(:url, :taken))
normal_url = PostUrlNormaliser.normalise(attributes['url'] || attributes[:url])
return nil if normal_url.blank?
compact_existing_post(Post.with_attached_thumbnail.find_by(url: normal_url))
end
def compact_existing_post post
PostCompactRepr.base(post)
end
end