掲示板実装
This commit is contained in:
66
app/controllers/board_memberships_controller.rb
Normal file
66
app/controllers/board_memberships_controller.rb
Normal file
@@ -0,0 +1,66 @@
|
||||
class BoardMembershipsController < ApplicationController
|
||||
before_action :require_login
|
||||
before_action :set_game
|
||||
before_action :set_board
|
||||
before_action :set_current_participant
|
||||
|
||||
def create
|
||||
# メンバー追加処理
|
||||
unless @board.negotiation? && @board.member?(@current_participant)
|
||||
return redirect_to game_board_path(@game, @board), alert: "権限がありません"
|
||||
end
|
||||
|
||||
target_participant_id = params[:participant_id]
|
||||
|
||||
# 重複チェック(既に参加しているか)
|
||||
if @board.board_memberships.where(participant_id: target_participant_id, left_at: nil).exists?
|
||||
return redirect_to game_board_path(@game, @board), alert: "そのプレイヤーは既に参加しています"
|
||||
end
|
||||
|
||||
# 退出済みの場合は再参加(left_atをクリア)
|
||||
membership = @board.board_memberships.find_by(participant_id: target_participant_id)
|
||||
if membership
|
||||
membership.update!(left_at: nil, joined_at: Time.current)
|
||||
else
|
||||
@board.board_memberships.create!(participant_id: target_participant_id, joined_at: Time.current)
|
||||
end
|
||||
|
||||
# メンバー構成が変わったので、他の掲示板と重複していないかチェックが必要だが、
|
||||
# モデルのバリデーションは「作成時」を想定しているため、ここでは簡易チェックに留めるか、
|
||||
# あるいはバリデーションエラーをハンドリングする。
|
||||
# ここではsave成功/失敗で判断
|
||||
|
||||
redirect_to game_board_path(@game, @board), notice: "メンバーを追加しました"
|
||||
rescue ActiveRecord::RecordInvalid => e
|
||||
redirect_to game_board_path(@game, @board), alert: "メンバー追加に失敗しました: #{e.message}"
|
||||
end
|
||||
|
||||
def leave
|
||||
# 退出処理
|
||||
unless @board.negotiation? && @board.member?(@current_participant)
|
||||
return redirect_to game_board_path(@game, @board), alert: "退出できません"
|
||||
end
|
||||
|
||||
membership = @board.board_memberships.find_by(participant: @current_participant)
|
||||
if membership
|
||||
membership.leave!
|
||||
redirect_to game_boards_path(@game), notice: "掲示板から退出しました"
|
||||
else
|
||||
redirect_to game_board_path(@game, @board), alert: "退出に失敗しました"
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def set_game
|
||||
@game = Game.find(params[:game_id])
|
||||
end
|
||||
|
||||
def set_board
|
||||
@board = @game.boards.find(params[:board_id])
|
||||
end
|
||||
|
||||
def set_current_participant
|
||||
@current_participant = @game.participants.find_by(user: current_user)
|
||||
end
|
||||
end
|
||||
47
app/controllers/board_posts_controller.rb
Normal file
47
app/controllers/board_posts_controller.rb
Normal file
@@ -0,0 +1,47 @@
|
||||
class BoardPostsController < ApplicationController
|
||||
before_action :require_login
|
||||
before_action :set_game
|
||||
before_action :set_board
|
||||
before_action :set_current_participant
|
||||
|
||||
def create
|
||||
# 参加チェック
|
||||
unless @board.member?(@current_participant) && @game.status == "in_progress"
|
||||
return redirect_to game_board_path(@game, @board), alert: "投稿権限がありません"
|
||||
end
|
||||
|
||||
@post = @board.board_posts.new(post_params)
|
||||
@post.participant = @current_participant
|
||||
|
||||
# フェーズ情報の付与
|
||||
# 最新のターン情報を取得してセットする
|
||||
latest_turn = @game.turns.order(number: :desc).first
|
||||
if latest_turn
|
||||
@post.phase = latest_turn.phase
|
||||
end
|
||||
|
||||
if @post.save
|
||||
redirect_to game_board_path(@game, @board) # noticeはチャット的にうるさいので省略
|
||||
else
|
||||
redirect_to game_board_path(@game, @board), alert: "投稿に失敗しました"
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def set_game
|
||||
@game = Game.find(params[:game_id])
|
||||
end
|
||||
|
||||
def set_board
|
||||
@board = @game.boards.find(params[:board_id])
|
||||
end
|
||||
|
||||
def set_current_participant
|
||||
@current_participant = @game.participants.find_by(user: current_user)
|
||||
end
|
||||
|
||||
def post_params
|
||||
params.require(:board_post).permit(:body)
|
||||
end
|
||||
end
|
||||
71
app/controllers/board_proposals_controller.rb
Normal file
71
app/controllers/board_proposals_controller.rb
Normal file
@@ -0,0 +1,71 @@
|
||||
class BoardProposalsController < ApplicationController
|
||||
before_action :require_login
|
||||
before_action :set_game
|
||||
before_action :set_board
|
||||
before_action :set_current_participant
|
||||
|
||||
def create
|
||||
unless @board.member?(@current_participant)
|
||||
return redirect_to game_board_path(@game, @board), alert: "権限がありません"
|
||||
end
|
||||
|
||||
@proposal = @board.board_proposals.new(proposal_params)
|
||||
@proposal.proposer = @current_participant
|
||||
@proposal.status = "pending"
|
||||
|
||||
# フェーズ情報の保存
|
||||
latest_turn = @game.turns.max_by(&:number)
|
||||
if latest_turn
|
||||
@proposal.phase = latest_turn.phase
|
||||
else
|
||||
@proposal.phase = "S1901M" # 初期値
|
||||
end
|
||||
|
||||
if @proposal.save
|
||||
redirect_to game_board_path(@game, @board), notice: "提案を作成しました"
|
||||
else
|
||||
redirect_to game_board_path(@game, @board), alert: "提案の作成に失敗しました"
|
||||
end
|
||||
end
|
||||
|
||||
def update
|
||||
@proposal = @board.board_proposals.find(params[:id])
|
||||
|
||||
# 承認・拒否権限:
|
||||
# 提案者本人以外が承認/拒否できるべきか、あるいは全員できるべきか?
|
||||
# 通常は「相手」が承認するものだが、多国間の場合は?
|
||||
# ここでは「メンバーであれば誰でもステータス変更可能」とする(簡易実装)
|
||||
# ただし、提案者本人が自分で承認するのは変なので、他者のみとするのがベター
|
||||
|
||||
unless @board.member?(@current_participant)
|
||||
return redirect_to game_board_path(@game, @board), alert: "権限がありません"
|
||||
end
|
||||
|
||||
new_status = params[:board_proposal][:status]
|
||||
if %w[accepted rejected].include?(new_status)
|
||||
@proposal.update!(status: new_status)
|
||||
# ログにも残す(投稿として自動投稿しても良いが、今回はステータス変更のみ)
|
||||
redirect_to game_board_path(@game, @board), notice: "提案を#{new_status == 'accepted' ? '承認' : '拒否'}しました"
|
||||
else
|
||||
redirect_to game_board_path(@game, @board), alert: "不正なステータスです"
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def set_game
|
||||
@game = Game.find(params[:game_id])
|
||||
end
|
||||
|
||||
def set_board
|
||||
@board = @game.boards.find(params[:board_id])
|
||||
end
|
||||
|
||||
def set_current_participant
|
||||
@current_participant = @game.participants.find_by(user: current_user)
|
||||
end
|
||||
|
||||
def proposal_params
|
||||
params.require(:board_proposal).permit(:body)
|
||||
end
|
||||
end
|
||||
186
app/controllers/boards_controller.rb
Normal file
186
app/controllers/boards_controller.rb
Normal file
@@ -0,0 +1,186 @@
|
||||
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
|
||||
Reference in New Issue
Block a user