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