""" QRコードサービスポイント処理API このモジュールは、QRコードを使用したサービスポイントの登録・処理を行います。 """ from rest_framework.decorators import api_view from rest_framework.response import Response from rest_framework import status from rog.models import NewEvent2, Entry, Location2025, GpsCheckin from django.db import transaction from django.utils import timezone from datetime import datetime import logging import json import uuid logger = logging.getLogger(__name__) @api_view(['POST']) def submit_qr_points(request): """ QRコードサービスポイント登録API パラメータ: - event_code: イベントコード - team_name: チーム名 - qr_code_data: QRコードデータ - latitude: 緯度(オプション) - longitude: 経度(オプション) - image: 画像データ(オプション) - cp_number: チェックポイント番号(オプション) """ # リクエストIDを生成してログで追跡できるようにする request_id = str(uuid.uuid4())[:8] logger.info(f"[QR_SUBMIT] 🚀 Starting QR points submission - ID: {request_id}") # クライアント情報を取得 user_agent = request.META.get('HTTP_USER_AGENT', 'Unknown') client_ip = request.META.get('HTTP_X_FORWARDED_FOR') or request.META.get('REMOTE_ADDR', 'Unknown') user_info = str(request.user) if request.user.is_authenticated else 'Anonymous' # リクエストからパラメータを取得 event_code = request.data.get('event_code') team_name = request.data.get('team_name') qr_code_data = request.data.get('qr_code_data') latitude = request.data.get('latitude') longitude = request.data.get('longitude') image_data = request.data.get('image') cp_number = request.data.get('cp_number') # 📋 パラメータをログ出力(デバッグ用) logger.info(f"[QR_SUBMIT] ID: {request_id} - 📋 Request Parameters:") logger.info(f"[QR_SUBMIT] ID: {request_id} - 🏷️ Event Code: '{event_code}'") logger.info(f"[QR_SUBMIT] ID: {request_id} - 👥 Team Name: '{team_name}'") logger.info(f"[QR_SUBMIT] ID: {request_id} - 📱 QR Code Data: '{qr_code_data}'") logger.info(f"[QR_SUBMIT] ID: {request_id} - 📍 Latitude: {latitude}") logger.info(f"[QR_SUBMIT] ID: {request_id} - 🌍 Longitude: {longitude}") logger.info(f"[QR_SUBMIT] ID: {request_id} - 🏁 CP Number: {cp_number}") logger.info(f"[QR_SUBMIT] ID: {request_id} - 📸 Has Image: {image_data is not None}") logger.info(f"[QR_SUBMIT] ID: {request_id} - 🌐 Client IP: {client_ip}") logger.info(f"[QR_SUBMIT] ID: {request_id} - 👤 User: {user_info}") logger.info(f"[QR_SUBMIT] ID: {request_id} - 🔧 User Agent: {user_agent[:100]}...") # 全リクエストデータをログ出力(セキュリティ上重要でないデータのみ) safe_data = {k: v for k, v in request.data.items() if k not in ['image', 'password']} logger.info(f"[QR_SUBMIT] ID: {request_id} - 📊 Full Request Data: {json.dumps(safe_data, ensure_ascii=False, indent=2)}") cp_number = request.data.get('cp_number') # 📊 詳細なパラメータログ logger.info(f"[QR_SUBMIT] 📊 DETAILED PARAMS - ID: {request_id}") logger.info(f"[QR_SUBMIT] 🏷️ Basic params: event_code='{event_code}', team_name='{team_name}'") logger.info(f"[QR_SUBMIT] 📱 QR params: qr_code_data='{qr_code_data}', cp_number={cp_number}") logger.info(f"[QR_SUBMIT] 🌍 Location params: lat={latitude}, lng={longitude}") logger.info(f"[QR_SUBMIT] 🖼️ Image params: has_image={bool(image_data)}, image_size={len(str(image_data)) if image_data else 0}") logger.info(f"[QR_SUBMIT] 📱 Client params: user_agent='{user_agent[:100]}...', client_ip='{client_ip}'") logger.info(f"[QR_SUBMIT] 🔐 Auth params: user_authenticated={request.user.is_authenticated}, user='{user_info}'") # 全リクエストデータをダンプ(デバッグ用) try: request_data_safe = {} for key, value in request.data.items(): if key == 'image' and value: request_data_safe[key] = f"[IMAGE_DATA:{len(str(value))}chars]" else: request_data_safe[key] = value logger.info(f"[QR_SUBMIT] 📥 FULL REQUEST DATA: {json.dumps(request_data_safe, ensure_ascii=False, indent=2)}") except Exception as e: logger.warning(f"[QR_SUBMIT] Failed to log request data: {e}") # パラメータ検証 if not all([event_code, team_name, qr_code_data]): logger.warning(f"[QR_SUBMIT] ❌ Missing required parameters - ID: {request_id}") return Response({ "status": "ERROR", "message": "イベントコード、チーム名、QRコードデータが必要です", "request_id": request_id }, status=status.HTTP_400_BAD_REQUEST) try: with transaction.atomic(): # イベントの存在確認 event = NewEvent2.objects.filter(event_name=event_code).first() if not event: logger.warning(f"[QR_SUBMIT] ❌ Event not found: {event_code} - ID: {request_id}") return Response({ "status": "ERROR", "message": f"指定されたイベント '{event_code}' が見つかりません", "request_id": request_id }, status=status.HTTP_404_NOT_FOUND) # チームの存在確認 entry = Entry.objects.filter( event=event, team__team_name=team_name ).first() if not entry: logger.warning(f"[QR_SUBMIT] ❌ Team not found: {team_name} in event: {event_code} - ID: {request_id}") return Response({ "status": "ERROR", "message": f"指定されたチーム '{team_name}' がイベント '{event_code}' に見つかりません", "request_id": request_id }, status=status.HTTP_404_NOT_FOUND) # QRコードデータの解析 try: if isinstance(qr_code_data, str): # JSON文字列の場合はパース if qr_code_data.startswith('{'): qr_data = json.loads(qr_code_data) else: # 単純な文字列の場合 qr_data = {"code": qr_code_data} else: qr_data = qr_code_data logger.info(f"[QR_SUBMIT] 📱 Parsed QR data: {qr_data} - ID: {request_id}") except json.JSONDecodeError as e: logger.warning(f"[QR_SUBMIT] ❌ Invalid QR code data format: {e} - ID: {request_id}") return Response({ "status": "ERROR", "message": "QRコードデータの形式が正しくありません", "request_id": request_id }, status=status.HTTP_400_BAD_REQUEST) # チェックポイント情報の取得(cp_numberが指定されている場合) location = None if cp_number: location = Location2025.objects.filter( event_id=event.id, cp_number=cp_number ).first() if location: logger.info(f"[QR_SUBMIT] 📍 Found location: CP{cp_number} - {location.cp_name} - ID: {request_id}") else: logger.warning(f"[QR_SUBMIT] ⚠️ Location not found for CP{cp_number} - ID: {request_id}") # QRポイント登録処理 current_time = timezone.now() # GpsCheckinレコードを作成(QRコード情報を含む) checkin_data = { 'event': event, 'entry': entry, 'zekken_number': entry.zekken_number, 'cp_number': cp_number or 0, # cp_numberが指定されていない場合は0 'checkin_time': current_time, 'is_service_checked': True, # QRコードはサービスポイントとして扱う } # 位置情報が提供されている場合は追加 if latitude and longitude: checkin_data['latitude'] = float(latitude) checkin_data['longitude'] = float(longitude) logger.info(f"[QR_SUBMIT] 🌍 GPS coordinates recorded: {latitude}, {longitude} - ID: {request_id}") # QRコードデータを格納(JSONフィールドがある場合) if hasattr(GpsCheckin, 'qr_code_data'): checkin_data['qr_code_data'] = qr_data # 画像データを格納(URLまたはパスの場合) if image_data: if hasattr(GpsCheckin, 'image_url'): checkin_data['image_url'] = image_data logger.info(f"[QR_SUBMIT] 🖼️ Image data recorded - ID: {request_id}") # レコードを作成 gps_checkin = GpsCheckin.objects.create(**checkin_data) # ポイント計算 point_value = 0 if location: point_value = location.cp_point or 0 # 成功レスポンス response_data = { "status": "OK", "message": "QRコードサービスポイントが正常に登録されました", "request_id": request_id, "data": { "event_code": event_code, "team_name": team_name, "zekken_number": entry.zekken_number, "cp_number": cp_number, "point_value": point_value, "checkin_time": current_time.isoformat(), "qr_code_processed": True, "has_location": bool(latitude and longitude), "has_image": bool(image_data), "checkin_id": gps_checkin.id if gps_checkin else None } } logger.info(f"[QR_SUBMIT] ✅ SUCCESS - Team: {team_name}, Zekken: {entry.zekken_number}, CP: {cp_number}, Points: {point_value}, QR: {bool(qr_code_data)}, Client IP: {client_ip}, User: {user_info} - ID: {request_id}") return Response(response_data, status=status.HTTP_200_OK) except Exception as e: logger.error(f"[QR_SUBMIT] ❌ EXCEPTION - ID: {request_id}, Error: {str(e)}", exc_info=True) return Response({ "status": "ERROR", "message": "QRコードサービスポイント処理中にエラーが発生しました", "request_id": request_id, "error_detail": str(e) if logger.getEffectiveLevel() <= logging.DEBUG else None }, status=status.HTTP_500_INTERNAL_SERVER_ERROR) @api_view(['GET']) def qr_points_status(request): """ QRポイント処理状況確認API パラメータ: - event_code: イベントコード - team_name: チーム名(オプション) """ event_code = request.query_params.get('event_code') team_name = request.query_params.get('team_name') logger.info(f"[QR_STATUS] QR points status check - event: {event_code}, team: {team_name}") if not event_code: return Response({ "status": "ERROR", "message": "イベントコードが必要です" }, status=status.HTTP_400_BAD_REQUEST) try: # イベントの存在確認 event = NewEvent2.objects.filter(event_name=event_code).first() if not event: return Response({ "status": "ERROR", "message": f"指定されたイベント '{event_code}' が見つかりません" }, status=status.HTTP_404_NOT_FOUND) # QRコード関連のチェックイン状況を取得 query = GpsCheckin.objects.filter(event=event, is_service_checked=True) if team_name: query = query.filter(entry__team__team_name=team_name) qr_checkins = query.order_by('-checkin_time')[:50] # 最新50件 checkin_list = [] for checkin in qr_checkins: checkin_list.append({ "checkin_id": checkin.id, "team_name": checkin.entry.team.team_name, "zekken_number": checkin.zekken_number, "cp_number": checkin.cp_number, "checkin_time": checkin.checkin_time.isoformat(), "has_qr_data": hasattr(checkin, 'qr_code_data') and bool(checkin.qr_code_data), "has_location": bool(checkin.latitude and checkin.longitude), "is_service_checked": checkin.is_service_checked }) return Response({ "status": "OK", "message": "QRポイント状況を取得しました", "data": { "event_code": event_code, "total_count": len(checkin_list), "checkins": checkin_list } }, status=status.HTTP_200_OK) except Exception as e: logger.error(f"[QR_STATUS] Error getting QR points status: {str(e)}", exc_info=True) return Response({ "status": "ERROR", "message": "QRポイント状況取得中にエラーが発生しました", "error_detail": str(e) if logger.getEffectiveLevel() <= logging.DEBUG else None }, status=status.HTTP_500_INTERNAL_SERVER_ERROR)