Files
kondiplo_front/app/controllers/boards_controller.rb
kontei bb9ec2df1d
Some checks failed
CI / scan_ruby (push) Has been cancelled
CI / scan_js (push) Has been cancelled
CI / lint (push) Has been cancelled
CI / test (push) Has been cancelled
CI / system-test (push) Has been cancelled
掲示板実装
2026-02-19 22:30:59 +09:00

187 lines
7.8 KiB
Ruby
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
class BoardsController < ApplicationController
before_action :require_login
before_action :set_game
before_action :set_board, only: [ :show, :toggle_public ]
def index
# 参加中の掲示板(共通掲示板含む)を取得
# ゲーム終了後は履歴モードとして、過去に参加していた掲示板もすべて表示
@current_participant = @game.participants.find_by(user: current_user)
# マップ表示用:各国がどの掲示板(交渉)を持っているかのデータを作成
# { "FRANCE" => ["GERMANY", "ENGLAND"], ... } のような形式でビューに渡す
@diplomacy_matrix = build_diplomacy_matrix
if @game.status == "finished"
# 履歴モード:かつて参加していた掲示板をすべて表示
# (共通掲示板は全員参加扱いなので含む)
@boards = @game.boards.select { |b| b.member?(@current_participant) || b.global? }
.sort_by { |b| [ b.global? ? 0 : 1, b.created_at ] }
else
return redirect_to game_path(@game), alert: "参加者ではないためアクセスできません" unless @current_participant
# 進行中:参加中の掲示板のみ
@boards = @current_participant.boards.includes(:participants, :board_posts).order(created_at: :desc)
# 共通掲示板がまだ紐付いていない場合のフォールバック(通常は作成時に紐づくはずだが念の為)
global_board = @game.boards.global.first
if global_board && !@boards.include?(global_board)
# 表示上追加するが、DBには保存しない閲覧時に参加処理をする作りも考えられるが、createタイミングで保証する前提
@boards = [ global_board ] + @boards
end
# 先頭に共通掲示板を持ってくる
@boards = @boards.sort_by { |b| b.global? ? 0 : 1 }
end
end
def show
@current_participant = @game.participants.find_by(user: current_user)
# アクセス制御
# アクセス制御
access_allowed = false
if @board.global?
# 共通掲示板はゲーム参加者なら誰でもアクセス可能
# 未読管理のためにメンバーシップがない場合は作成する
access_allowed = true
unless @board.board_memberships.exists?(participant: @current_participant)
@board.board_memberships.create(participant: @current_participant, joined_at: Time.current)
end
elsif @board.member?(@current_participant)
access_allowed = true
elsif @game.status == "finished"
# ゲーム終了後は、元メンバーならアクセス可能(共通掲示板は上記でカバー済み)
is_past_member = @board.board_memberships.exists?(participant: @current_participant)
access_allowed = is_past_member
elsif @board.is_public?
# 公開掲示板は誰でも閲覧可能
access_allowed = true
end
unless access_allowed
return redirect_to game_boards_path(@game), alert: "権限がありません"
end
# 既読更新
if @current_participant && @board.active_memberships.exists?(participant: @current_participant)
last_post = @board.board_posts.last
if last_post
membership = @board.board_memberships.find_by(participant: @current_participant)
membership.mark_read!(last_post.id)
end
end
@posts = @board.board_posts.includes(:participant).order(created_at: :desc)
@new_post = BoardPost.new
@new_proposal = BoardProposal.new
@active_members = @board.active_memberships.includes(:participant).map(&:participant)
# メンバー追加用:招待可能なプレイヤー一覧(自分と既に参加している人を除く)
if @board.negotiation? && !@board.history_mode?
existing_member_ids = @board.board_memberships.where(left_at: nil).pluck(:participant_id)
@candidates = @game.participants.where.not(id: existing_member_ids).where.not(power: nil)
end
end
def new
@board = Board.new(board_type: "negotiation")
@current_participant = @game.participants.find_by(user: current_user)
# 自分以外の参加者一覧
@participants = @game.participants.where.not(id: @current_participant.id).where.not(power: nil)
end
def create
@current_participant = @game.participants.find_by(user: current_user)
# トランザクションで掲示板作成とメンバー追加を一括実行
ActiveRecord::Base.transaction do
@board = @game.boards.new(board_params)
@board.board_type = "negotiation"
@board.created_by_participant_id = @current_participant.id
if @board.save
# 作成者をメンバーに追加
@board.board_memberships.create!(participant: @current_participant, joined_at: Time.current)
# 招待されたメンバーを追加
if params[:invited_participant_ids].present?
params[:invited_participant_ids].each do |pid|
@board.board_memberships.create!(participant_id: pid, joined_at: Time.current)
end
end
redirect_to game_board_path(@game, @board), notice: "交渉用掲示板を作成しました"
else
@participants = @game.participants.where.not(id: @current_participant.id).where.not(power: nil)
render :new, status: :unprocessable_entity
end
end
rescue ActiveRecord::RecordInvalid
@participants = @game.participants.where.not(id: @current_participant.id).where.not(power: nil)
render :new, status: :unprocessable_entity
end
def toggle_public
# 公開宣言機能(追加提案)
@current_participant = @game.participants.find_by(user: current_user)
unless @board.member?(@current_participant)
return redirect_to game_board_path(@game, @board), alert: "権限がありません"
end
@board.update(is_public: !@board.is_public)
status = @board.is_public ? "公開" : "非公開"
redirect_to game_board_path(@game, @board), notice: "掲示板を#{status}に設定しました"
end
private
def set_game
@game = Game.find(params[:game_id])
end
def set_board
@board = @game.boards.find(params[:id])
end
def board_params
# パラメータは特にないが、将来的にタイトルなど追加するかも
params.fetch(:board, {}).permit(:is_public)
end
def build_diplomacy_matrix
# 外交関係マップ構築
# Matrix: { "FRANCE" => { "GERMANY" => true, "ENGLAND" => false }, ... }
# 全組み合わせの初期化
powers = @game.participants.where.not(power: nil).pluck(:power)
matrix = {}
powers.each { |p| matrix[p] = [] }
# 参加中の全掲示板を走査(自分が参加していないものも含めたいが、
# DB設計上 board_memberships を見る必要がある)
# is_publicなもの、または自分が参加しているものについて情報を開示
# 仕様書には「どの国とどの国が掲示板を持っているか」とあるが、
# 秘密交渉なので、本来は「自分が見えている範囲」あるいは「公開宣言されたもの」のみ?
# ここでは「自分が参加している掲示板」および「公開された掲示板」の関係を表示する。
visible_boards = @game.boards.negotiation.includes(:participants)
visible_boards.each do |board|
# アクセス権チェック(簡易)
is_member = board.board_memberships.exists?(participant: @current_participant, left_at: nil)
next unless is_member || board.is_public || @game.status == "finished"
# メンバー間のリンクを作成
members = board.participants.pluck(:power)
members.each do |p1|
members.each do |p2|
next if p1 == p2
matrix[p1] ||= []
matrix[p1] << p2 unless matrix[p1].include?(p2)
end
end
end
matrix
end
end