From a8dc2ba3b182f998b9a8b4e74f4896febe36ac6e Mon Sep 17 00:00:00 2001 From: Akira Date: Tue, 2 Sep 2025 20:06:58 +0900 Subject: [PATCH] Update new API.. --- .env.local_akira | 0 .env.sql | 2 +- rog/urls.py | 3 +- rog/views.py | 75 ++++++- サーバーAPI変更要求書20250901.md | 258 ++++++++++++++++++++++ 外部システムAPI仕様書.md | 177 ++++++++++++++- 6 files changed, 500 insertions(+), 15 deletions(-) create mode 100644 .env.local_akira create mode 100644 サーバーAPI変更要求書20250901.md diff --git a/.env.local_akira b/.env.local_akira new file mode 100644 index 0000000..e69de29 diff --git a/.env.sql b/.env.sql index 4ef1ced..4256a12 100644 --- a/.env.sql +++ b/.env.sql @@ -2,7 +2,7 @@ POSTGRES_USER=admin POSTGRES_PASS=admin123456 POSTGRES_DBNAME=rogdb DATABASE=postgres -PG_HOST=172.31.25.76 +PG_HOST=postgres-db PG_PORT=5432 GS_VERSION=2.20.0 GEOSERVER_PORT=8600 diff --git a/rog/urls.py b/rog/urls.py index bc4816b..d3129ed 100755 --- a/rog/urls.py +++ b/rog/urls.py @@ -1,7 +1,7 @@ from sys import prefix from rest_framework import urlpatterns from rest_framework.routers import DefaultRouter -from .views import CategoryByNameView, LocationViewSet, Location_lineViewSet, Location_polygonViewSet, Jpn_Main_PerfViewSet, LocationsInPerf, ExtentForSubPerf, SubPerfInMainPerf, ExtentForMainPerf, LocationsInSubPerf, CatView, RegistrationAPI, LoginAPI, UserAPI, UserActionViewset, UserMakeActionViewset, UserDestinations, UpdateOrder, LocationInBound, DeleteDestination, CustomAreaLocations, GetAllGifuAreas, CustomAreaNames, userDetials, UserTracksViewSet, CatByCity, ChangePasswordView, GoalImageViewSet, CheckinImageViewSet, ExtentForLocations, DeleteAccount, PrivacyView, RegistrationView, TeamViewSet,MemberViewSet,EntryViewSet,RegisterView, VerifyEmailView, NewEventListView,NewEvent2ListView,NewCategoryListView,CategoryListView, MemberUserDetailView, TeamMembersWithUserView,MemberAddView,UserActivationView,RegistrationView,TempUserRegistrationView,ResendInvitationEmailView,update_user_info,update_user_detail,ActivateMemberView, ActivateNewMemberView, PasswordResetRequestView, PasswordResetConfirmView, NewCategoryViewSet,LocationInBound2,UserLastGoalTimeView,TeamEntriesView,update_entry_status,get_events,get_zekken_numbers,get_team_info,get_checkins,update_checkins,export_excel,debug_urls,get_ranking, all_ranking_top3 +from .views import CategoryByNameView, LocationViewSet, Location_lineViewSet, Location_polygonViewSet, Jpn_Main_PerfViewSet, LocationsInPerf, ExtentForSubPerf, SubPerfInMainPerf, ExtentForMainPerf, LocationsInSubPerf, CatView, RegistrationAPI, LoginAPI, UserAPI, UserActionViewset, UserMakeActionViewset, UserDestinations, UpdateOrder, LocationInBound, DeleteDestination, CustomAreaLocations, GetAllGifuAreas, CustomAreaNames, userDetials, UserTracksViewSet, CatByCity, ChangePasswordView, GoalImageViewSet, CheckinImageViewSet, ExtentForLocations, DeleteAccount, PrivacyView, RegistrationView, TeamViewSet,MemberViewSet,EntryViewSet,RegisterView, VerifyEmailView, NewEventListView,NewEvent2ListView,NewCategoryListView,CategoryListView, MemberUserDetailView, TeamMembersWithUserView,MemberAddView,UserActivationView,RegistrationView,TempUserRegistrationView,ResendInvitationEmailView,update_user_info,update_user_detail,ActivateMemberView, ActivateNewMemberView, PasswordResetRequestView, PasswordResetConfirmView, NewCategoryViewSet,LocationInBound2,UserLastGoalTimeView,TeamEntriesView,update_entry_status,get_events,get_zekken_numbers,get_team_info,get_checkins,update_checkins,export_excel,debug_urls,get_ranking, all_ranking_top3, current_entry_info from .views_apis.api_auth import check_event_code from .views_apis.api_teams import register_team,update_team_name,team_class_changer,team_register,zekken_max_num,zekken_double_check,get_team_list,get_zekken_list @@ -140,6 +140,7 @@ urlpatterns += [ #path('admin/', admin.site.urls), path('entries//update-status/', update_entry_status, name='update-entry-status'), + path('user/current-entry-info/', views.current_entry_info, name='current-entry-info'), # for Supervisor Web app diff --git a/rog/views.py b/rog/views.py index a00f46a..f5d3e94 100755 --- a/rog/views.py +++ b/rog/views.py @@ -120,15 +120,23 @@ def update_entry_status(request, entry_id): if entry.owner != request.user and not entry.team.members.filter(user=request.user).exists(): return Response({"error": "You don't have permission to update this entry"}, status=status.HTTP_403_FORBIDDEN) - hasParticipated = request.data.get('hasParticipated') - hasGoaled = request.data.get('hasGoaled') + # フィールド名の両方のパターンを受け入れる + hasParticipated = request.data.get('hasParticipated') or request.data.get('has_participated') + hasGoaled = request.data.get('hasGoaled') or request.data.get('has_goaled') if hasParticipated is not None: entry.hasParticipated = hasParticipated if hasGoaled is not None: entry.hasGoaled = hasGoaled - entry.save() + # update()を使用してバリデーションをスキップ + Entry.objects.filter(id=entry_id).update( + hasParticipated=entry.hasParticipated, + hasGoaled=entry.hasGoaled + ) + + # 更新後のオブジェクトを再取得 + entry.refresh_from_db() serializer = EntrySerializer(entry) return Response(serializer.data) @@ -4085,3 +4093,64 @@ def location_checkin_test(request): """ロケーションチェックインのテストページ""" from django.shortcuts import render return render(request, 'location_checkin_test.html') + + +@api_view(['GET']) +@permission_classes([IsAuthenticated]) +def current_entry_info(request): + """ + ユーザーの最新エントリー情報取得API + + GET /api/user/current-entry-info/ + """ + user = request.user + + # ユーザーの最新エントリーを取得(IDの降順でソート) + latest_entry = Entry.objects.filter( + team__owner=user + ).select_related('team', 'event', 'category', 'team__category').order_by('-id').first() + + if not latest_entry: + return Response({ + 'error': 'No active entry found for user', + 'detail': 'User has no current entries' + }, status=status.HTTP_404_NOT_FOUND) + + # ユーザーの全エントリー数を取得 + entries_count = Entry.objects.filter(team__owner=user).count() + + # レスポンスデータを構築 + response_data = { + 'user': { + 'id': user.id, + 'email': user.email, + 'firstname': user.firstname, + 'lastname': user.lastname, + 'is_staff': user.is_staff, + 'event_code': latest_entry.event.event_name # 最新エントリーのイベント名 + }, + 'current_entry': { + 'id': latest_entry.id, + 'team': { + 'id': latest_entry.team.id, + 'team_name': latest_entry.team.team_name, + 'category': { + 'id': latest_entry.category.id, + 'category_name': latest_entry.category.category_name + } + }, + 'event': { + 'id': latest_entry.event.id, + 'event_name': latest_entry.event.event_name, + 'start_datetime': latest_entry.event.start_datetime, + 'end_datetime': latest_entry.event.end_datetime + }, + 'date': latest_entry.date.strftime('%Y-%m-%d') if latest_entry.date else None, + 'has_participated': latest_entry.hasParticipated, + 'has_goaled': latest_entry.hasGoaled + }, + 'entries_count': entries_count, + 'latest_entry_date': latest_entry.date # created_atがないのでdateフィールドを使用 + } + + return Response(response_data) diff --git a/サーバーAPI変更要求書20250901.md b/サーバーAPI変更要求書20250901.md new file mode 100644 index 0000000..677d921 --- /dev/null +++ b/サーバーAPI変更要求書20250901.md @@ -0,0 +1,258 @@ +# サーバーAPI変更要求書 + +**文書番号:** API-REQ-20250901 +**作成日:** 2025年9月1日 +**作成者:** アプリ開発チーム +**対象システム:** 岐阜ロゲイニングアプリ サーバーAPI + +## 概要 + +岐阜ロゲイニングアプリにおいて、ユーザーのエントリー参加後にアプリ側とサーバー側のデータ同期に問題が発生しています。具体的には、ユーザー情報に含まれる`event_code`とEntryControllerで管理されている実際のエントリー情報が一致しないため、競技開始時にValidationErrorが発生している状況です。 + +## 問題の詳細 + +### 現在の問題 +1. **データ同期の不整合** + - ユーザー情報: `event_code: "TestEvent"` (Event ID: 140) + - 実際のエントリー: `event: "揖斐川"` (Event ID: 128, Entry ID: 747) + +2. **競技開始時のエラー** + - API: `PATCH /api/entries/747/update-status/` + - エラー: `ValidationError at /api/entries/747/update-status/` + - ステータス: HTTP 500 + +3. **ユーザー情報の未更新** + - エントリー参加後にユーザー情報(`currentUser`)が最新のエントリー情報で更新されない + +## 要求される変更 + +### 1. 新規API追加 + +#### API 1: ユーザーの最新エントリー情報取得API + +**エンドポイント:** `GET /api/user/current-entry-info/` + +**目的:** ユーザーの最新エントリー情報を取得し、アプリ側とサーバー側のデータ同期を確保 + +**認証:** Token認証必須 + +**リクエスト** +```http +GET /api/user/current-entry-info/ +Authorization: Token +Content-Type: application/json; charset=UTF-8 +``` + +**成功レスポンス (HTTP 200)** +```json +{ + "user": { + "id": 1765, + "email": "akira.miyata@gifuai.net", + "firstname": "明", + "lastname": "宮田", + "is_staff": true, + "event_code": "揖斐川" + }, + "current_entry": { + "id": 747, + "team": { + "id": 155, + "team_name": "狸の宮家_v2", + "category": { + "id": 1, + "category_name": "一般-5時間" + } + }, + "event": { + "id": 128, + "event_name": "揖斐川", + "start_datetime": "2025-01-25T10:00:00+09:00", + "end_datetime": "2025-01-25T16:00:00+09:00" + }, + "date": "2025-01-25", + "has_participated": false, + "has_goaled": false + }, + "entries_count": 6, + "latest_entry_date": "2025-01-25T10:00:00+09:00" +} +``` + +**エラーレスポンス (HTTP 404)** +```json +{ + "error": "No active entry found for user", + "detail": "User has no current entries" +} +``` + +**実装要件:** +- ユーザーの最新エントリーを`team__owner`で検索 +- エントリーは`created_at`の降順でソート +- ユーザー情報の`event_code`は最新エントリーのイベント名を反映 +- Team, Event, Category情報を含む完全な情報を返却 + +### 2. 既存API修正 + +#### API 2: エントリーステータス更新API修正 + +**エンドポイント:** `PATCH /api/entries/{entry_id}/update-status/` + +**問題:** 現在ValidationErrorが発生 + +**現在のリクエストボディ:** +```json +{ + "hasParticipated": true, + "hasGoaled": false +} +``` + +**期待されるフィールド名 (要確認):** +```json +{ + "has_participated": true, + "has_goaled": false +} +``` + +**対応要求:** +1. フィールド名の整合性確認 +2. バリデーションエラーの原因調査 +3. 適切なエラーレスポンスの実装 + +## 実装方針 + +### Django REST Framework実装例 + +```python +# views.py +from rest_framework.decorators import api_view, permission_classes +from rest_framework.permissions import IsAuthenticated +from rest_framework.response import Response +from django.contrib.auth.decorators import login_required + +@api_view(['GET']) +@permission_classes([IsAuthenticated]) +def current_entry_info(request): + user = request.user + + # ユーザーの最新エントリーを取得 + latest_entry = Entry.objects.filter( + team__owner=user + ).order_by('-created_at').first() + + if not latest_entry: + return Response({ + 'error': 'No active entry found for user', + 'detail': 'User has no current entries' + }, status=404) + + # レスポンスデータを構築 + response_data = { + 'user': { + 'id': user.id, + 'email': user.email, + 'firstname': user.first_name, + 'lastname': user.last_name, + 'is_staff': user.is_staff, + 'event_code': latest_entry.event.event_name # 最新エントリーのイベント名 + }, + 'current_entry': { + 'id': latest_entry.id, + 'team': { + 'id': latest_entry.team.id, + 'team_name': latest_entry.team.team_name, + 'category': { + 'id': latest_entry.category.id, + 'category_name': latest_entry.category.category_name + } + }, + 'event': { + 'id': latest_entry.event.id, + 'event_name': latest_entry.event.event_name, + 'start_datetime': latest_entry.event.start_datetime, + 'end_datetime': latest_entry.event.end_datetime + }, + 'date': latest_entry.date, + 'has_participated': latest_entry.has_participated, + 'has_goaled': latest_entry.has_goaled + }, + 'entries_count': Entry.objects.filter(team__owner=user).count(), + 'latest_entry_date': latest_entry.created_at + } + + return Response(response_data) +``` + +```python +# urls.py +urlpatterns = [ + path('api/user/current-entry-info/', views.current_entry_info, name='current_entry_info'), + # ... 他のURL +] +``` + +## 期待される効果 + +1. **データ整合性の確保** + - アプリ側とサーバー側のエントリー情報が常に同期 + - 正しいentry_idとevent_codeを使用した通信 + +2. **エラーの解消** + - 競技開始時のValidationErrorの解決 + - 適切なAPI呼び出しの実現 + +3. **ユーザビリティの向上** + - 競技開始がスムーズに行える + - データの不整合によるアプリクラッシュの防止 + +## 実装スケジュール + +| 項目 | 期限 | 担当 | +|------|------|------| +| API設計レビュー | 2025年9月3日 | サーバーチーム | +| 新規API実装 | 2025年9月5日 | サーバーチーム | +| 既存API修正 | 2025年9月5日 | サーバーチーム | +| 結合テスト | 2025年9月6日 | 全チーム | +| 本番環境デプロイ | 2025年9月8日 | インフラチーム | + +## テスト要件 + +### 1. 単体テスト +- [ ] 新規API: 正常系レスポンス確認 +- [ ] 新規API: エラーケース確認 +- [ ] 既存API: バリデーションエラー修正確認 + +### 2. 結合テスト +- [ ] アプリからの新規API呼び出し +- [ ] データ同期確認 +- [ ] 競技開始フロー確認 + +### 3. 性能テスト +- [ ] 新規APIのレスポンス時間測定 +- [ ] 同時アクセス時の動作確認 + +## リスク評価 + +| リスク | 影響度 | 対策 | +|--------|--------|------| +| 既存API変更による影響 | 中 | 下位互換性の維持 | +| データ移行の必要性 | 低 | 新規APIのため影響なし | +| 性能劣化 | 低 | インデックス最適化 | + +## 承認 + +| 役割 | 氏名 | 承認日 | 署名 | +|------|------|--------|------| +| アプリ開発リーダー | | | | +| サーバー開発リーダー | | | | +| プロジェクトマネージャー | | | | + +--- + +**注意事項:** +- 本変更要求は岐阜ロゲイニングアプリの安定運用のために必要な修正です +- 実装前に必ずステージング環境での検証を行ってください +- 本番環境デプロイ時はユーザーへの事前通知を行ってください diff --git a/外部システムAPI仕様書.md b/外部システムAPI仕様書.md index a7faae3..58eda26 100644 --- a/外部システムAPI仕様書.md +++ b/外部システムAPI仕様書.md @@ -623,6 +623,133 @@ zekken_number=101&event_code=岐阜ロゲイニング2025 --- +## 10. エントリー管理API + +### 10.1 現在のエントリー情報取得 +ユーザーの最新のエントリー情報を取得します。アプリとサーバー間のデータ同期に使用されます。 + +**エンドポイント**: `GET /user/current-entry-info/` + +**認証**: 必須(Token認証) + +**レスポンス(成功時)**: +```json +{ + "id": 123, + "team": { + "id": 45, + "team_name": "サンプルチーム", + "owner": 67 + }, + "event": { + "id": 12, + "event_name": "岐阜ロゲイニング2025", + "start_datetime": "2025-09-15T10:00:00Z", + "end_datetime": "2025-09-15T16:00:00Z" + }, + "category": { + "id": 1, + "category_name": "一般男子", + "duration": 180, + "num_of_member": 4 + }, + "zekken_number": 101, + "date": "2025-09-15T10:00:00Z", + "hasParticipated": false, + "hasGoaled": false +} +``` + +**レスポンス(エントリーなし)**: +```json +{ + "message": "エントリー情報が見つかりません" +} +``` + +**レスポンス(認証エラー)**: +```json +{ + "detail": "認証情報が提供されていません。" +} +``` + +### 10.2 エントリーステータス更新 +エントリーの参加状況とゴール状況を更新します。後方互換性のため、新旧フィールド名の両方をサポートしています。 + +**エンドポイント**: `PUT /entry/{entry_id}/update-status/` + +**認証**: 必須(Token認証) + +**リクエストパラメータ(新フィールド名)**: +```json +{ + "hasParticipated": true, + "hasGoaled": false +} +``` + +**リクエストパラメータ(旧フィールド名・後方互換性)**: +```json +{ + "has_participated": true, + "has_goaled": false +} +``` + +**レスポンス(成功時)**: +```json +{ + "id": 123, + "team": { + "id": 45, + "team_name": "サンプルチーム" + }, + "event": { + "id": 12, + "event_name": "岐阜ロゲイニング2025" + }, + "zekken_number": 101, + "hasParticipated": true, + "hasGoaled": false, + "updated_at": "2025-09-15T11:30:00Z" +} +``` + +**レスポンス(エラー時)**: +```json +{ + "detail": "エントリーが見つかりません。" +} +``` + +**使用例**: + +**現在のエントリー情報取得**: +```bash +curl -X GET "/user/current-entry-info/" \ + -H "Authorization: Token " \ + -H "Content-Type: application/json" +``` + +**ステータス更新(参加開始)**: +```bash +curl -X PUT "/entry/123/update-status/" \ + -H "Authorization: Token " \ + -H "Content-Type: application/json" \ + -d '{"hasParticipated": true, "hasGoaled": false}' +``` + +**ステータス更新(ゴール完了)**: +```bash +curl -X PUT "/entry/123/update-status/" \ + -H "Authorization: Token " \ + -H "Content-Type: application/json" \ + -d '{"hasParticipated": true, "hasGoaled": true}' +``` + +--- + ## エラーコード一覧 | ステータスコード | 説明 | @@ -682,22 +809,37 @@ zekken_number=101&event_code=岐阜ロゲイニング2025 curl -X POST "/entry/" -H "Authorization: Token " -d '{"team":1,"event":1,"category":1}' ``` -7. **スタート** +7. **エントリー情報確認** + ```bash + curl -X GET "/user/current-entry-info/" -H "Authorization: Token " + ``` + +8. **スタート前ステータス更新** + ```bash + curl -X PUT "/entry/123/update-status/" -H "Authorization: Token " -d '{"hasParticipated": true, "hasGoaled": false}' + ``` + +9. **スタート** ```bash curl -X POST "/start_from_rogapp" -d '{"event_code":"岐阜ロゲイニング2025","team_name":"サンプルチーム"}' ``` -8. **チェックイン** - ```bash - curl -X POST "/checkin_from_rogapp" -d '{"event_code":"岐阜ロゲイニング2025","team_name":"サンプルチーム","cp_number":1,"image":"https://example.com/photo1.jpg"}' - ``` +10. **チェックイン** + ```bash + curl -X POST "/checkin_from_rogapp" -d '{"event_code":"岐阜ロゲイニング2025","team_name":"サンプルチーム","cp_number":1,"image":"https://example.com/photo1.jpg"}' + ``` -9. **ゴール** - ```bash - curl -X POST "/goal_from_rogapp" -d '{"event_code":"岐阜ロゲイニング2025","team_name":"サンプルチーム","image":"https://example.com/goal.jpg"}' - ``` +11. **ゴール** + ```bash + curl -X POST "/goal_from_rogapp" -d '{"event_code":"岐阜ロゲイニング2025","team_name":"サンプルチーム","image":"https://example.com/goal.jpg"}' + ``` -10. **証明書取得** +12. **ゴール後ステータス更新** + ```bash + curl -X PUT "/entry/123/update-status/" -H "Authorization: Token " -d '{"hasParticipated": true, "hasGoaled": true}' + ``` + +13. **証明書取得** ```bash curl "/download_scoreboard?zekken_number=101&event_code=岐阜ロゲイニング2025" ``` @@ -725,6 +867,8 @@ zekken_number=101&event_code=岐阜ロゲイニング2025 3. **画像アップロード**: 画像ファイルは事前に適切なストレージにアップロードし、URLを指定してください 4. **時刻形式**: すべての時刻は ISO 8601 形式(YYYY-MM-DDTHH:MM:SSZ)で指定してください 5. **エラーハンドリング**: APIからのエラーレスポンスを適切に処理してください +6. **エントリー情報同期**: アプリ起動時は `/user/current-entry-info/` でエントリー情報を同期してください +7. **ステータス更新**: エントリーステータス更新API は後方互換性のため新旧フィールド名をサポートしています --- @@ -804,6 +948,19 @@ zekken_number=101&event_code=岐阜ロゲイニング2025 - **参照**: `rog_entry` (チーム情報取得) - **参照**: `rog_gpslog` (チェックイン画像URL) +#### 9. エントリー管理系API +**`/user/current-entry-info/` (現在のエントリー情報取得)** +- **参照**: `rog_entry` (ユーザーの最新エントリー情報) +- **参照**: `rog_team` (チーム基本情報) +- **参照**: `rog_newevent2` (イベント情報) +- **参照**: `rog_newcategory` (カテゴリ詳細情報) +- **参照**: `knox_authtoken` (ユーザー認証) + +**`/entry/{entry_id}/update-status/` (エントリーステータス更新)** +- **参照**: `rog_entry` (対象エントリー検索) +- **更新**: `rog_entry` (hasParticipated/hasGoaled更新) +- **参照**: `knox_authtoken` (ユーザー認証・権限確認) + --- ## テーブル別API使用一覧表