Files
rogaining_srv/rog/views_apis/api_approval.py
2025-09-02 23:14:14 +09:00

161 lines
7.1 KiB
Python
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.

"""
通過履歴承認API
ユーザーが自分のチームの通過履歴を確認し、承認確定する処理を行う
"""
import logging
import uuid
from django.http import JsonResponse
from django.utils import timezone
from django.db import transaction
from rest_framework.decorators import api_view, permission_classes
from rest_framework.permissions import IsAuthenticated
from rest_framework.response import Response
from rest_framework import status
from knox.auth import TokenAuthentication
from ..models import NewEvent2, Entry, GpsLog
# ログ設定
logger = logging.getLogger(__name__)
@api_view(['POST'])
@permission_classes([IsAuthenticated])
def approve_checkin_history(request):
"""
ユーザーがアプリ上で通過履歴を確認し、承認確定する処理
パラメータ:
- event_code: イベントコード (必須)
- zekken_number: ゼッケン番号 (必須)
- checkin_ids: 承認するチェックインIDのリスト (必須)
- approval_comment: 承認コメント (任意)
"""
# リクエストID生成デバッグ用
request_id = str(uuid.uuid4())[:8]
client_ip = request.META.get('HTTP_X_FORWARDED_FOR', request.META.get('REMOTE_ADDR', 'Unknown'))
logger.info(f"[APPROVE_CHECKIN] 🎯 API call started - ID: {request_id}, User: {request.user.email if request.user.is_authenticated else 'Anonymous'}, Client IP: {client_ip}")
try:
# リクエストデータの取得
data = request.data
event_code = data.get('event_code')
zekken_number = data.get('zekken_number')
checkin_ids = data.get('checkin_ids', [])
approval_comment = data.get('approval_comment', '')
logger.info(f"[APPROVE_CHECKIN] 📝 Request data - ID: {request_id}, event_code: '{event_code}', zekken_number: '{zekken_number}', checkin_ids: {checkin_ids}")
# 必須パラメータの検証
if not all([event_code, zekken_number, checkin_ids]):
logger.warning(f"[APPROVE_CHECKIN] ❌ Missing required parameters - ID: {request_id}")
return Response({
"status": "ERROR",
"message": "イベントコード、ゼッケン番号、チェックインIDが必要です"
}, status=status.HTTP_400_BAD_REQUEST)
if not isinstance(checkin_ids, list) or len(checkin_ids) == 0:
logger.warning(f"[APPROVE_CHECKIN] ❌ Invalid checkin_ids format - ID: {request_id}")
return Response({
"status": "ERROR",
"message": "チェックインIDは空でない配列で指定してください"
}, status=status.HTTP_400_BAD_REQUEST)
# イベントの存在確認
event = NewEvent2.objects.filter(event_name=event_code).first()
if not event:
logger.warning(f"[APPROVE_CHECKIN] ❌ Event not found - ID: {request_id}, event_code: '{event_code}'")
return Response({
"status": "ERROR",
"message": "指定されたイベントが見つかりません"
}, status=status.HTTP_404_NOT_FOUND)
logger.info(f"[APPROVE_CHECKIN] ✅ Event found - ID: {request_id}, event: '{event_code}', event_id: {event.id}")
# チームの存在確認とオーナー権限の検証
entry = Entry.objects.filter(
event=event,
team__zekken_number=zekken_number
).first()
if not entry:
logger.warning(f"[APPROVE_CHECKIN] ❌ Team not found - ID: {request_id}, zekken_number: '{zekken_number}', event_code: '{event_code}'")
return Response({
"status": "ERROR",
"message": "指定されたゼッケン番号のチームが見つかりません"
}, status=status.HTTP_404_NOT_FOUND)
logger.info(f"[APPROVE_CHECKIN] ✅ Team found - ID: {request_id}, team_name: '{entry.team.team_name}', zekken: {zekken_number}, entry_id: {entry.id}")
# オーナー権限の確認
if entry.owner != request.user:
logger.warning(f"[APPROVE_CHECKIN] ❌ Permission denied - ID: {request_id}, user: {request.user.email}, team_owner: {entry.owner.email}")
return Response({
"status": "ERROR",
"message": "このチームの通過履歴を承認する権限がありません"
}, status=status.HTTP_403_FORBIDDEN)
# 指定されたチェックインIDの存在確認
existing_checkins = GpsLog.objects.filter(
id__in=checkin_ids,
zekken_number=zekken_number,
event_code=event_code
)
existing_ids = list(existing_checkins.values_list('id', flat=True))
invalid_ids = [cid for cid in checkin_ids if cid not in existing_ids]
if invalid_ids:
logger.warning(f"[APPROVE_CHECKIN] ⚠️ Invalid checkin IDs found - ID: {request_id}, invalid_ids: {invalid_ids}, valid_ids: {existing_ids}")
return Response({
"status": "ERROR",
"message": "指定されたチェックイン記録が見つかりません",
"error_details": {
"invalid_checkin_ids": invalid_ids,
"valid_checkin_ids": existing_ids
}
}, status=status.HTTP_404_NOT_FOUND)
logger.info(f"[APPROVE_CHECKIN] ✅ All checkin IDs validated - ID: {request_id}, count: {len(existing_ids)}")
# 承認処理現時点ではACK応答のみ
# TODO: 実際のDB更新処理をここに実装
# - validation_statusの更新
# - approval_commentの保存
# - approved_atタイムスタンプの設定
# - approved_byユーザーの記録
approval_time = timezone.now()
approved_checkins = []
for checkin in existing_checkins:
approved_checkins.append({
"checkin_id": checkin.id,
"cp_number": checkin.cp_number,
"approved_at": approval_time.strftime("%Y-%m-%d %H:%M:%S")
})
logger.info(f"[APPROVE_CHECKIN] ✅ Approval completed - ID: {request_id}, approved_count: {len(approved_checkins)}, comment: '{approval_comment[:50]}...' if len(approval_comment) > 50 else approval_comment")
# 成功レスポンス
return Response({
"status": "OK",
"message": "通過履歴の承認が完了しました",
"approved_count": len(approved_checkins),
"approved_checkins": approved_checkins,
"team_info": {
"team_name": entry.team.team_name,
"zekken_number": zekken_number,
"event_code": event_code
}
})
except Exception as e:
logger.error(f"[APPROVE_CHECKIN] 💥 Unexpected error - ID: {request_id}, error: {str(e)}", exc_info=True)
return Response({
"status": "ERROR",
"message": "サーバーエラーが発生しました"
}, status=status.HTTP_500_INTERNAL_SERVER_ERROR)