Fix some APIs

This commit is contained in:
2025-09-02 23:14:14 +09:00
parent 9f27357a3b
commit 8ffedc177f
11 changed files with 974 additions and 287 deletions

View File

@ -0,0 +1,160 @@
"""
通過履歴承認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)