class AutoTurnProcessJob < ApplicationJob queue_as :default def perform Game.where(status: "in_progress") .where.not(next_deadline_at: nil) .where("next_deadline_at <= ?", Time.current) .find_each do |game| process_game(game) rescue StandardError => e Rails.logger.error "AutoTurnProcessJob: Game #{game.id} failed: #{e.message}" Rails.logger.error e.backtrace.first(5).join("\n") end end private def process_game(game) latest_turn = game.turns.where.not(phase: "COMPLETED").last return unless latest_turn client = GameApiClient.new current_orders = latest_turn.orders || {} # 人間が担当していない国のAutoOrderを生成 # 人間プレイヤーの未提出分はそのまま(空のまま)処理する all_powers = latest_turn.game_state&.dig("units")&.keys || [] human_powers = game.participants.where.not(power: nil).pluck(:power).map(&:upcase) submitted_powers = current_orders.keys.map(&:upcase) all_powers.each do |power| next if submitted_powers.include?(power.upcase) # 人間プレイヤーが担当している国は命令未提出のまま進行 next if human_powers.include?(power.upcase) # 人間が担当していない国のみAutoOrderを適用 if game.auto_order_mode == "random" auto_orders_response = client.api_calculate_auto_orders(latest_turn.game_state, power) if auto_orders_response && auto_orders_response["orders"] current_orders[power] = auto_orders_response["orders"] end end # auto_order_mode == "hold" の場合はAPIがデフォルトでHOLDを適用するため何もしない end # 自動生成した命令を保存 latest_turn.update_columns(orders: current_orders) if current_orders.present? # ターン処理を実行(force: true で未提出でも強制進行) service = TurnProcessingService.new(latest_turn, client: client) result = service.process(force: "true") if result[:success] game.reload if game.status == "in_progress" game.update!(next_deadline_at: game.calculate_next_deadline) else game.update!(next_deadline_at: nil) end Rails.logger.info "AutoTurnProcess: Game #{game.id} processed successfully: #{result[:message]}" else Rails.logger.error "AutoTurnProcess: Game #{game.id} failed: #{result[:message]}" end end end