From 4aeda91d107abc96c0f0fc36061cf0502bb84c90 Mon Sep 17 00:00:00 2001 From: Ponshu Developer Date: Mon, 16 Feb 2026 09:30:43 +0900 Subject: [PATCH] chore: Clean up repository - remove temp files, strengthen .gitignore - Remove APK build artifacts from working directory - Move old review/analysis reports to docs/archive/ - Move initial setup docs to docs/archive/ - Add .apk/.aab/.ipa/.claude/.cursor/.env to .gitignore - Remove .claude/ from git tracking Co-authored-by: Cursor --- .claude/CRITICAL_RULES.md | 54 -- .claude/settings.local.json | 59 -- .gitignore | 17 + ARCHITECTURE_DECISION_RECORD_bk.md | 83 --- COMPREHENSIVE_CODE_REVIEW.md | 532 ------------------ CRITICAL_BUG_IMAGE_DELETION.md | 248 -------- DAY2_COMPLETION_REPORT.md | 158 ------ DAY2_SECURITY_CHECKLIST.md | 153 ----- DAY4_COMPLETION_REPORT.md | 256 --------- DAY5_CRITICAL_FIXES_REPORT.md | 321 ----------- DAY5_FINAL_REPORT.md | 279 --------- EMERGENCY_IMAGE_REPAIR.md | 309 ---------- PERFORMANCE_ANALYSIS.md | 359 ------------ README_SYNOLOGY.md | 95 ---- RECOMMENDATION_EXPANSION_PLAN.md | 406 ------------- RELEASE_PLAN_10DAYS.md | 278 --------- REMAINING_TASKS_DAY5.md | 160 ------ all_models.json | Bin 46436 -> 0 bytes analysis_output.txt | 96 ---- analyze_output.txt | 70 --- debug_steps.md | 76 --- .../archive/ANTIGRAVITY_PROMPT.md | 0 .../archive/CommonSpecification.md | 0 .../archive/FINAL_REQUIREMENTS.md | 0 .../archive/GEMINI_PRO_SETUP.md | 0 docs/archive/IOS_DEVICE_TEST_CHECKLIST.md | 243 ++++++++ docs/archive/IOS_DISTRIBUTION_STRATEGY.md | 412 ++++++++++++++ docs/archive/IOS_SETUP_GUIDE.md | 351 ++++++++++++ START_HERE.md => docs/archive/START_HERE.md | 0 .../archive/START_HERE_FINAL.md | 0 .../archive/UI_UX_DECISION_GUIDE.md | 0 keytool_output.txt | Bin 1052 -> 0 bytes models.txt | Bin 46436 -> 0 bytes tmp_commit_msg.txt | 7 + walkthrough.md | 52 -- 35 files changed, 1030 insertions(+), 4044 deletions(-) delete mode 100644 .claude/CRITICAL_RULES.md delete mode 100644 .claude/settings.local.json delete mode 100644 ARCHITECTURE_DECISION_RECORD_bk.md delete mode 100644 COMPREHENSIVE_CODE_REVIEW.md delete mode 100644 CRITICAL_BUG_IMAGE_DELETION.md delete mode 100644 DAY2_COMPLETION_REPORT.md delete mode 100644 DAY2_SECURITY_CHECKLIST.md delete mode 100644 DAY4_COMPLETION_REPORT.md delete mode 100644 DAY5_CRITICAL_FIXES_REPORT.md delete mode 100644 DAY5_FINAL_REPORT.md delete mode 100644 EMERGENCY_IMAGE_REPAIR.md delete mode 100644 PERFORMANCE_ANALYSIS.md delete mode 100644 README_SYNOLOGY.md delete mode 100644 RECOMMENDATION_EXPANSION_PLAN.md delete mode 100644 RELEASE_PLAN_10DAYS.md delete mode 100644 REMAINING_TASKS_DAY5.md delete mode 100644 all_models.json delete mode 100644 analysis_output.txt delete mode 100644 analyze_output.txt delete mode 100644 debug_steps.md rename ANTIGRAVITY_PROMPT.md => docs/archive/ANTIGRAVITY_PROMPT.md (100%) rename CommonSpecification.md => docs/archive/CommonSpecification.md (100%) rename FINAL_REQUIREMENTS.md => docs/archive/FINAL_REQUIREMENTS.md (100%) rename GEMINI_PRO_SETUP.md => docs/archive/GEMINI_PRO_SETUP.md (100%) create mode 100644 docs/archive/IOS_DEVICE_TEST_CHECKLIST.md create mode 100644 docs/archive/IOS_DISTRIBUTION_STRATEGY.md create mode 100644 docs/archive/IOS_SETUP_GUIDE.md rename START_HERE.md => docs/archive/START_HERE.md (100%) rename START_HERE_FINAL.md => docs/archive/START_HERE_FINAL.md (100%) rename UI_UX_DECISION_GUIDE.md => docs/archive/UI_UX_DECISION_GUIDE.md (100%) delete mode 100644 keytool_output.txt delete mode 100644 models.txt create mode 100644 tmp_commit_msg.txt delete mode 100644 walkthrough.md diff --git a/.claude/CRITICAL_RULES.md b/.claude/CRITICAL_RULES.md deleted file mode 100644 index bb06bb8..0000000 --- a/.claude/CRITICAL_RULES.md +++ /dev/null @@ -1,54 +0,0 @@ -# ⚠️ CRITICAL PROJECT RULES - DO NOT VIOLATE ⚠️ - -## Gemini AI Model Configuration - -**RULE: The Gemini model name is LOCKED to `gemini-2.5-flash`** - -- **File**: `lib/services/gemini_service.dart` (line 194) -- **Model Name**: `gemini-2.5-flash` -- **Status**: Confirmed working (2026-01-17) -- **DO NOT CHANGE** this model name without **explicit user approval** -- This model name was verified by the user via Google AI Studio dashboard screenshot - -### History of Issues: -1. AI incorrectly suggested `gemini-1.5-flash` (does not exist) -2. AI incorrectly suggested `gemini-1.5-flash-latest` (does not exist) -3. User confirmed via screenshot that `gemini-2.5-flash` is the correct and available model - -### If Model Change is Required: -1. User MUST explicitly approve the change -2. User MUST provide evidence (e.g., screenshot from Google AI Studio) -3. Update this file with the new model name and date - ---- - -## API Key Configuration - -**RULE: Secrets.dart API Key structure is FIXED** - -- **File**: `lib/secrets.dart` (lines 27-30) -- **Correct Format**: - ```dart - static const String geminiApiKey = String.fromEnvironment( - 'GEMINI_API_KEY', // ← Environment variable name - defaultValue: 'AIza...', // ← Actual API key - ); - ``` -- **DO NOT** put the API key in the first argument -- **DO NOT** leave defaultValue empty - ---- - -## Synology AI Proxy Configuration - -**RULE: useProxy flag controls connection mode** - -- **File**: `lib/secrets.dart` (line 19) -- **Current Mode**: `useProxy = false` (Direct Cloud API) -- When `useProxy = true`: Connects via Synology NAS at home -- When `useProxy = false`: Connects directly to Google Gemini API (works anywhere) - ---- - -**Last Updated**: 2026-01-17 -**Maintained By**: User + Claude Code AI Assistant diff --git a/.claude/settings.local.json b/.claude/settings.local.json deleted file mode 100644 index 05151ad..0000000 --- a/.claude/settings.local.json +++ /dev/null @@ -1,59 +0,0 @@ -{ - "permissions": { - "allow": [ - "Bash(dir libsecrets.dart)", - "Bash(flutter analyze:*)", - "Bash(git remote add:*)", - "Bash(git branch:*)", - "Bash(git push:*)", - "Bash(git reset:*)", - "Bash(find:*)", - "Bash(git add:*)", - "Bash(git commit:*)", - "Bash(git rebase:*)", - "Bash(cat:*)", - "Bash(git pull:*)", - "Bash(git stash:*)", - "Read(//c/Users/maita/posimai-project/ponshu-room/**)", - "Bash(flutter build:*)", - "Bash(unzip:*)", - "Bash(ls:*)", - "Bash(awk:*)", - "Bash(flutter pub:*)", - "Bash(flutter run:*)", - "Bash(tee:*)", - "Bash(flutter:*)", - "Bash(Select-Object -Last 50)", - "Bash(git log:*)", - "Bash(dart run build_runner build:*)", - "Bash(dir .dart_toolflutter_gengen_l10n)", - "Bash(adb kill-server:*)", - "Bash(adb:*)", - "Bash(\"C:\\Users\\maita\\AppData\\Local\\Android\\Sdk\\platform-tools\\adb.exe\" kill-server)", - "Bash(\"C:\\Users\\maita\\AppData\\Local\\Android\\Sdk\\platform-tools\\adb.exe\" start-server)", - "Bash(\"C:\\Users\\maita\\AppData\\Local\\Android\\Sdk\\platform-tools\\adb.exe\" devices -l)", - "Bash(\"C:\\Users\\maita\\AppData\\Local\\Android\\Sdk\\platform-tools\\adb.exe\" usb)", - "Bash(if [ ! -f \"lib/secrets.local.dart\" ])", - "Bash(then cp lib/secrets.local.dart.example lib/secrets.local.dart)", - "Bash(else echo \"File already exists\")", - "Bash(fi)", - "Bash(timeout:*)", - "Bash(Remove-Item \"c:\\Users\\maita\\posimai-project\\ponshu_room_lite\\lib\\services\\tutorial_service.dart\" -Force)", - "Bash(dart fix:*)", - "Bash(Select-String \"unused_local_variable\")", - "Bash(Select-Object -First 10)", - "Bash(Select-String \"use_build_context\")", - "Bash(Select-Object -First 20)", - "Bash(Select-String \"backup_settings_section\")", - "Bash(Select-Object -First 30)", - "Bash(git checkout:*)", - "Bash(Select-Object -Last 10)", - "Bash(Select-Object -Last 30)", - "Bash(Select-String \"app_theme\")", - "Bash(git tag:*)", - "Bash(Select-Object -Last 20)" - ], - "deny": [], - "ask": [] - } -} diff --git a/.gitignore b/.gitignore index 0648c00..92e14ba 100644 --- a/.gitignore +++ b/.gitignore @@ -52,3 +52,20 @@ lib/libsecrets.dart *.apk *.aab *.ipa + +# IDE / Editor +.claude/ +.cursor/ +.vscode/ + +# Environment / Secrets +.env +*.env.local + +# OS files +Thumbs.db +Desktop.ini + +# Temporary files +*.tmp +*.bak diff --git a/ARCHITECTURE_DECISION_RECORD_bk.md b/ARCHITECTURE_DECISION_RECORD_bk.md deleted file mode 100644 index 04d7742..0000000 --- a/ARCHITECTURE_DECISION_RECORD_bk.md +++ /dev/null @@ -1,83 +0,0 @@ -# Architecture Decision Record (ADR) - 001: Synology Secure Access - -* **Status**: Accepted -* **Date**: 2026-01-18 -* **Decision Makers**: Development Team (Gemini & Claude) -* **Subject**: Secure Remote Access Strategy for Synology Backend services - -## Context & Problem -To enable the "Posimai" ecosystem (Sake & Incense apps) to utilize backend services (DB, potentially AI Proxy) hosted on a home Synology NAS, a robust connection strategy is required. -Previous attempts using direct IP (`192.168.x.x`) failed due to lack of external access. -Previous attempts using pure HTTP failed due to Android/iOS security requirements (Cleartext traffic). - -## Decision -We will use **Tailscale MagicDNS with HTTPS Certificates** as the primary connectivity solution for the current development phase. - -### Justification -1. **Zero Cost & Zero Hardware**: Tailscale is already running. No new domains or hardware needed. -2. **Native HTTPS**: Tailscale provides valid Let's Encrypt certificates for `*.ts.net` domains, satisfying Flutter's secure connection requirements. -3. **Secure by Design**: No open ports (Port Forwarding) required on the router. Access is limited to devices in the Tailnet. -4. **Sufficiency**: For a user base < 1 person (Developer), the complexity of Cloudflare Tunnel is unnecessary overhead. - -### Alternatives Considered -* **Cloudflare Tunnel**: Best for scaling/production (>10k users), but overkill for now. -* **QuickConnect**: Synology's proprietary relay. Too slow and hard to integrate with custom ports/containers. -* **Direct IP / VPN**: Unstable IP addresses and difficult certificates management. - -## Implementation Roadmap - -### Week 1: Tailscale HTTPS Setup -1. **MagicDNS**: Enable in Tailscale Admin Console. -2. **HTTPS Certificates**: Enable in Tailscale Admin Console. -3. **Result**: `https://posimai-nas.ts.net` becomes a valid, globally accessible (within Tailnet) URL. - -### Week 2: Immich & Container Integration -* Deploy `immich` via Container Manager to act as the media cache. -* Deploy `posimai-db` (Postgres) for structured data. -* Configure `docker-compose.yml` (see below). - -### Week 3: App Integration -* Update Flutter App configuration: - ```dart - const String apiBaseUrl = 'https://posimai-nas.ts.net'; - ``` - -## Infrastructure Configuration (`docker-compose.yml`) - -```yaml -version: '3.8' -services: - # Main Database - posimai-db: - image: postgres:15-alpine - container_name: posimai-db - restart: always - environment: - POSTGRES_USER: ${DB_USER} - POSTGRES_PASSWORD: ${DB_PASSWORD} - POSTGRES_DB: posimai_master - volumes: - - ./pgdata:/var/lib/postgresql/data - networks: - - posimai-net - - # AI Proxy (Legacy/Backup) - posimai-proxy: - build: ./ai-proxy - container_name: posimai-proxy - restart: unless-stopped - ports: - - "8080:8080" - environment: - - GEMINI_API_KEY=${GEMINI_API_KEY} - networks: - - posimai-net - -networks: - posimai-net: - driver: bridge -``` - -## Future Considerations -* If user base grows > 100, migrate to **Tailscale Funnel** (Public internet access). -* If user base grows > 10,000, migrate to **Cloudflare Tunnel** + Custom Domain. diff --git a/COMPREHENSIVE_CODE_REVIEW.md b/COMPREHENSIVE_CODE_REVIEW.md deleted file mode 100644 index be71f46..0000000 --- a/COMPREHENSIVE_CODE_REVIEW.md +++ /dev/null @@ -1,532 +0,0 @@ -# 🔍 Ponshu Room Lite: 包括的コードレビュー - -**作成日**: 2026-01-22 -**レビュー対象**: v1.0 リリースビルド -**レビュアー**: Claude (Sonnet 4.5) -**目的**: 批判的視点でのコード品質評価と改善提案 - ---- - -## 📊 総合評価 - -| 項目 | 評価 | コメント | -|------|-----|----------| -| **アーキテクチャ** | ⭐⭐⭐⭐☆ | Clean Architecture的、ただしレイヤー分離が甘い部分あり | -| **コード品質** | ⭐⭐⭐☆☆ | 全体的に良好だが、重複コードと技術的負債が散見 | -| **パフォーマンス** | ⭐⭐⭐⭐☆ | 画像圧縮・キャッシュ実装済み、Hero使用も問題なし | -| **UX** | ⭐⭐⭐⭐☆ | ダークモード、アニメーション実装、細かい調整必要 | -| **セキュリティ** | ⭐⭐⭐⭐☆ | API Proxyで保護、デバイスID使用、良好 | -| **テスト可能性** | ⭐⭐☆☆☆ | **最大の弱点**: テストコードが皆無 | -| **ドキュメント** | ⭐⭐⭐⭐☆ | 詳細なドキュメントあり、コード内コメントは少なめ | - -**総合**: 🌟🌟🌟⭐ (3.5/5) - **MVP としては優秀、スケール前に改善必要** - ---- - -## 🔴 クリティカルな問題点 - -### 1. **テストコードが完全に欠如** - -**問題**: -```dart -// lib/test/ ディレクトリが存在しない -// Unit Test: 0件 -// Widget Test: 0件 -// Integration Test: 0件 -``` - -**影響**: -- リファクタリングが怖い(破壊的変更の検出不可) -- AIとの共同開発で予期しないバグ混入のリスク -- 将来的なスケール時に品質保証が困難 - -**推奨対策**: -```dart -// test/services/gemini_service_test.dart (例) -void main() { - group('GeminiService', () { - test('should cache analysis results', () async { - final service = GeminiService(); - final result1 = await service.analyzeSakeLabel(['test.jpg']); - final result2 = await service.analyzeSakeLabel(['test.jpg']); - - expect(result1, equals(result2)); // Same instance from cache - }); - }); -} -``` - -**優先度**: 🔴 **最高** (Phase 1.1 で最低限のテスト追加を推奨) - ---- - -### 2. **画面遷移アニメーションの不統一** ✅ 今回発見 - -**問題**: -- Grid: Heroアニメーション(ふわっと拡大) -- List: Heroアニメーション(小さいサムネイルから拡大) -- Carousel: 標準スライド(Heroなし) - -**影響**: -- ユーザーが混乱する可能性 -- ブランド一貫性の欠如 - -**推奨対策**: -```dart -// Option A: すべてにHeroを統一(推奨) -// widgets/sake_3d_carousel.dart に Hero を追加 - -Widget _buildCarouselItem(SakeItem item) { - return GestureDetector( - onTap: () { - Navigator.push( - context, - MaterialPageRoute( - builder: (_) => SakeDetailScreen(sake: item), - ), - ); - }, - child: Hero( // ← 追加 - tag: item.id, - child: Card( - // ... 既存のコード - ), - ), - ); -} -``` - -**優先度**: 🟡 **中** (UX改善、Phase 1.1) - ---- - -### 3. **エラーハンドリングが不完全** - -**問題例**: -```dart -// camera_screen.dart: 670行目 -} catch (e) { - errorMessage = '解析中にエラーが発生しました\n\nエラー内容:\n${e.toString()}'; -} -``` - -**批判**: -- ユーザーに生のエラーメッセージを表示(技術的すぎる) -- ログ収集の仕組みがない(デバッグ困難) -- リトライ戦略が一貫していない - -**推奨対策**: -```dart -// services/error_handler.dart (新規作成) -class ErrorHandler { - static String getUserFriendlyMessage(dynamic error) { - if (error is SocketException) { - return 'インターネット接続を確認してください'; - } else if (error is TimeoutException) { - return '通信がタイムアウトしました。もう一度お試しください'; - } else if (error.toString().contains('Quota')) { - return 'AI利用制限に達しました。明日またお試しください'; - } - // Unknown error - _logToAnalytics(error); // Firebase Crashlyticsなど - return 'エラーが発生しました。時間をおいて再度お試しください'; - } -} -``` - -**優先度**: 🟡 **中** (Phase 2.0 でログ基盤整備) - ---- - -## 🟡 重要な改善点 - -### 4. **画像圧縮ロジックが不完全** ✅ Backlogに記載済み - -**問題**: -```dart -// services/image_compression_service.dart -// 実装はただのファイルコピーになっている箇所がある可能性 -``` - -**確認コード**: -```dart -static Future compressForGemini(String sourcePath, {String? targetPath}) async { - // TODO: 実際の圧縮処理を確認 - // flutter_image_compress が正しく動作しているか -} -``` - -**推奨対策**: -- `flutter_image_compress` の使用を確認 -- 圧縮前後のファイルサイズをログ出力(デバッグ用) -- 最適なパラメータ調整(品質 vs サイズ) - -**優先度**: 🟡 **中** (Phase 1.1) - ---- - -### 5. **Riverpod の Provider が過剰に分散** - -**問題**: -```dart -// 15個以上のProviderが各所に散在 -// - userProfileProvider -// - sakeListProvider -// - displayModeProvider -// - menuModeProvider -// - uiExperimentProvider -// ... etc -``` - -**批判**: -- 依存関係が追いにくい -- グローバル状態管理の明確な設計がない - -**推奨対策**: -```dart -// providers/app_state.dart (統合) -@Riverpod(keepAlive: true) -class AppState extends _$AppState { - @override - AppStateData build() { - return AppStateData( - user: ref.watch(userProfileProvider), - sakeList: ref.watch(sakeListProvider), - ui: ref.watch(uiExperimentProvider), - ); - } -} - -// 各画面は AppState 経由でアクセス -final appState = ref.watch(appStateProvider); -``` - -**優先度**: 🟢 **低** (Phase 3.0 リファクタリング時) - ---- - -### 6. **ダークモードの色指定が一貫していない** - -**問題**: -```dart -// 一部の画面でハードコードされた色 -color: Colors.grey[800] // ← ダークモードを想定 -color: Theme.of(context).colorScheme.surface // ← 正しい - -// app_theme.dart にガイドラインはあるが、強制力がない -``` - -**推奨対策**: -- `app_theme.dart` で定数定義を徹底 -- Lint ルールで `Colors.grey` の直接使用を禁止 - -```yaml -# analysis_options.yaml -linter: - rules: - - avoid_relative_lib_imports - - prefer_const_constructors - # カスタムルール追加(要プラグイン) -``` - -**優先度**: 🟡 **中** (Phase 1.1) - ---- - -## ✅ 優れている点 - -### 1. **Schema v2.0 の設計** - -```dart -// models/sake_item.dart -// レガシーフィールドと新フィールドの共存 -// マイグレーション対応が秀逸 -@HiveField(20) DisplayData? _displayData; -@HiveField(1) final String? legacyName; - -// Getter で自動マイグレーション -DisplayData get displayData { - if (_displayData != null) return _displayData!; - return DisplayData(name: legacyName ?? 'Unknown', ...); -} -``` - -**評価**: ⭐⭐⭐⭐⭐ -- 下位互換性を保ちながら進化 -- Hiveの仕様を理解した良い設計 - ---- - -### 2. **AI Proxy による API Key 保護** - -```dart -// services/gemini_service.dart -static final String _proxyUrl = Secrets.aiProxyAnalyzeUrl; - -// デバイスIDでレート制限 -final deviceId = await DeviceService.getDeviceId(); -``` - -**評価**: ⭐⭐⭐⭐⭐ -- クライアントにAPI Keyを埋め込まない -- Synology上のProxyで一元管理 -- セキュリティベストプラクティス - ---- - -### 3. **Gamification Service の実装** - -```dart -// services/gamification_service.dart -// バッジ解除ロジックが明確 -static Future> checkAndUnlockBadges(WidgetRef ref) async { - // 条件チェック → 解除 → 保存 -} -``` - -**評価**: ⭐⭐⭐⭐☆ -- ロジックが集約されている -- ただし、バッジ定義がハードコード(将来的にJSONファイル化推奨) - ---- - -## 🔧 技術的負債 - -### 1. **Flutter Analyze の 49個の警告** ✅ Backlogに記載済み - -```bash -$ flutter analyze -Analyzing ponshu_room_lite... -49 issues found. (49 infos) -``` - -**内容**: -- 未使用import -- 非推奨API使用 -- 型アノテーション欠如 - -**推奨**: -```bash -# 一括修正 -$ dart fix --apply - -# 段階的修正(警告レベル別) -$ flutter analyze --no-fatal-infos -``` - ---- - -### 2. **コメントが少ない** - -```dart -// camera_screen.dart: 992行 のうち、説明コメントは約5% -// 複雑なロジック(露出調整、ズーム)の説明がない -``` - -**推奨**: -```dart -/// Instagramスタイルの露出調整スライダー -/// -/// 上にドラッグ: 明るく (+) -/// 下にドラッグ: 暗く (-) -/// ダブルタップ: リセット (0.0) -/// -/// スロットリング: 30ms (カメラAPIの負荷軽減) -void _onVerticalDragUpdate(DragUpdateDetails details) async { - // ... -} -``` - ---- - -## 📋 統合タスクリスト(優先度順) - -以下に、既存のBacklogと今回の発見を統合した**最終的な残タスクリスト**を示します。 - ---- - -# 🎯 Ponshu Room Lite: 統合残タスクリスト - -**最終更新**: 2026-01-22 (Claude レビュー後) -**ソース**: PROJECT_BACKLOG_MASTER.md + UI_UX_BACKLOG.md + 今回のコードレビュー - ---- - -## 🔴 Phase 1.1: 緊急修正 & Quick Wins (今週〜来週) - -### 🐛 バグ修正 -- [x] ~~Coach Mark Persistence~~ ✅ 解決済み (Tutorial削除) -- [x] ~~Dark Mode プロフィール色~~ ✅ 本セッションで修正 -- [x] ~~AI詳細セクション UI~~ ✅ 本セッションで修正 -- [x] ~~製造年月カレンダー選択~~ ✅ 本セッションで実装 - -### 🎨 UX 即座改善 -- [ ] **Hero アニメーション統一** (NEW! 🔥) - - Carouselに Hero タグ追加 - - 全遷移を「ふわっと浮き上がる」に統一 - - 推定: 0.5h - -- [x] ~~**カメラUI 明るさ調整の視覚化**~~ ✅ **実装済み** (Antigravity報告は誤り) - - 太陽アイコン・縦スライダー・月アイコン・数値表示すべて完備 - - camera_screen.dart: 239-340行目に完全実装 - - 推定: 0h (不要) - -- [ ] **ソムリエ画面レイアウト修正** (Antigravity報告) - - 分析結果の余白調整 - - シェアボタンが隠れる問題 - - 推定: 1h - -- [ ] **マップ機能強化** (Antigravity報告) - - 県タップ時にボトムシート表示 - - その県の日本酒リストへジャンプ - - 推定: 3h - -### 🧪 テスト基盤構築 (NEW! 🔥 **最優先**) -- [ ] **最低限のUnit Test追加** - - `test/services/gemini_service_test.dart` (キャッシュ動作) - - `test/services/level_calculator_test.dart` (レベル計算ロジック) - - `test/models/sake_item_test.dart` (マイグレーション) - - 推定: 6h - - **理由**: リファクタリング前の安全網 - ---- - -## 🟡 Phase 2.0: ビジネス価値 & エンゲージメント (2〜3週間後) - -### 🎮 Gamification拡張 -- [ ] **バッジ拡張 (18個追加)** - - 地域バッジ (7): 東北✅、関東、関西、北陸、中部、中国、九州、全国制覇 - - 活動バッジ (6): 初心者(1本)、愛好家(10本)、コレクター(50本)、マスター(100本)、伝説(500本)、神(1000本) - - タイプバッジ (3): 純米党、吟醸党、大吟醸党 - - ビジネスバッジ (2): お品書き職人、セット名人 - - 推定: 8h - -- [ ] **バッジ解除モーダル** - - SnackBar → 祝福ダイアログ (紙吹雪アニメーション) - - 推定: 3.5h - -- [ ] **経験値システム拡張** - - スキャン: +10 EXP (実装済み) - - レビュー投稿: +3 EXP (新規) - - メモ追加: +1 EXP (新規) - - 推定: 4h - -### 🏗️ ビジネス機能 -- [ ] **Instagram プロモーション支援** - - AI キャプション生成 (Gemini) - - ハッシュタグ提案 (#日本酒 #sake #純米大吟醸) - - 画像 + テキスト共有 - - 推定: 8h - -- [ ] **セット商品価格設定UX改善** - - ステップ式入力 (原価 → 売価 → マージン自動計算) - - 推定: 4h - -### 🏗️ インフラ (Synology) -- [ ] **Dokploy セットアップ** - - Ubuntu VM に Dokploy インストール - - Tailscale Funnel で安全な公開 - - 推定: 2h - -- [ ] **Gitea 連携** - - Webhook → 自動デプロイ - - AI用Giteaアカウント作成 (Claude/Gemini/Antigravity) - - 推定: 2h - ---- - -## 🟢 Phase 3.0: スケーラビリティ & 長期改善 (1〜2ヶ月後) - -### 🔮 AI店主 (True Recommendations) -- [ ] **Gemini チャット実装** - - 「今の気分」から日本酒提案 - - DB外のお酒も推薦可能 - - 推定: 12h - -### 🏗️ プレースホルダー → 実機能化 -- [ ] **蔵元マップ** - - 実データ統合 - - Google Maps連携 - - 推定: 12h - -- [ ] **販売分析** - - チャート表示 (売上トレンド、味覚分布) - - 推定: 10h - -- [ ] **位置情報ボーナス** - - GPS で蔵元訪問検知 → ボーナスEXP - - 推定: 6h - -### 🎨 マイクロインタラクション -- [ ] **タブ切り替えアニメーション** - - Fade/Slide効果 - - 推定: 2h - -- [ ] **ダイアログエントランス** - - Scale/Fade In - - 推定: 1.5h - -- [ ] **Munyun (いいね) アニメーション** - - Rive/Lottie アニメーション - - 推定: 4h - -### 🐛 技術的負債 -- [ ] **Flutter Analyze 警告解消 (49件)** - - `dart fix --apply` 実行 - - 手動修正が必要な箇所を対処 - - 推定: 2h - -- [ ] **画像圧縮ロジック検証** - - `flutter_image_compress` の動作確認 - - 圧縮前後のサイズログ追加 - - 推定: 3h - -- [ ] **PDF フォント埋め込み検証** - - Potta One フォントが正しく表示されるか - - 推定: 2h - -- [ ] **エラーハンドリング統一** - - `ErrorHandler` サービス作成 - - ユーザーフレンドリーなメッセージ変換 - - Firebase Crashlytics 連携 - - 推定: 6h - -- [ ] **Provider の整理** - - `AppState` に統合 - - 依存関係の可視化 - - 推定: 8h (大規模リファクタリング) - ---- - -## 📊 タスク数サマリー - -| Phase | 緊急 (🔴) | 重要 (🟡) | 将来 (🟢) | 合計 | -|-------|---------|---------|---------|------| -| **1.1 (今週)** | 7 | - | - | 7 | -| **2.0 (2-3週)** | - | 10 | - | 10 | -| **3.0 (1-2ヶ月)** | - | - | 12 | 12 | -| **合計** | 7 | 10 | 12 | **29** | - -*(既存52タスクから、完了済み・重複を除外して統合)* - ---- - -## 🚦 推奨実行順序 (共同開発者テスト前) - -1. ✅ **Hero統一** (0.5h) - 即座改善、UX一貫性 -2. ✅ **ソムリエ/マップ** (4h) - Antigravity報告対応(カメラUIは実装済み) -3. ✅ **テスト基盤** (6h) - **最優先** (リファクタリング前の保険) -4. ✅ **バッジ拡張** (8h) - エンゲージメント向上 - -**合計: 18.5h (約2-3日)** - -これらを完了させてから共同開発者テストに入ると、フィードバックがより建設的になります。 - ---- - -## 📝 備考 - -- **テストコードの重要性**: 現在テストが0件なので、AIとの共同開発でバグが混入しやすい。Phase 1.1でテスト基盤を作ることを**強く推奨**。 -- **Hero統一**: 今回のレビューで発見。UXの一貫性向上のため、早めの対応推奨。 -- **Antigravity報告**: カメラUI、ソムリエ、マップの3点は実機テストで確認済みの問題。優先度高。 - diff --git a/CRITICAL_BUG_IMAGE_DELETION.md b/CRITICAL_BUG_IMAGE_DELETION.md deleted file mode 100644 index 9de1113..0000000 --- a/CRITICAL_BUG_IMAGE_DELETION.md +++ /dev/null @@ -1,248 +0,0 @@ -# 🚨 Critical Bug Report: 画像誤削除問題 - -**発見日**: 2026-01-22 -**影響**: ⚠️ ユーザーデータ消失 -**ステータス**: ✅ 修正完了 - ---- - -## 📊 問題の概要 - -### 症状 -- カード一覧・詳細画面で日本酒の写真が表示されない -- ストレージ: 558MB → 409MB(Androidキャッシュクリア後) -- 一部の画像ファイルが消失 - -### 影響範囲 -- **Critical**: ユーザーが撮影した日本酒の写真が削除された -- **被害画像**: `_gallery.jpg`, `_compressed.jpg` を含むすべてのファイル -- **被害者**: 一時ファイルクリーンアップを実行したユーザー - ---- - -## 🔍 根本原因 - -### バグのあるコード - -```dart -// lib/services/image_batch_compression_service.dart:157-192 (修正前) -static Future<(int, int)> cleanupTempFiles() async { - // ❌ 問題: getApplicationDocumentsDirectory() をスキャン(永続ファイルがある場所) - final directory = await getApplicationDocumentsDirectory(); - final dir = Directory(directory.path); - - await for (final entity in dir.list()) { - if (entity is File) { - final fileName = entity.path.split('/').last; - - // ❌ 問題: _compressed, _gallery を含むすべてのファイルを削除 - if (fileName.contains('_compressed') || fileName.contains('_gallery')) { - await entity.delete(); // ← 本物の画像を削除! - } - } - } -} -``` - -### なぜ本物の画像が削除されたのか? - -#### 1. ギャラリー保存用一時ファイル -```dart -// lib/screens/camera_screen.dart:225 (修正前) -final String galleryPath = join(directory.path, '${const Uuid().v4()}_gallery.jpg'); -// ↑ directory = getApplicationDocumentsDirectory() -``` -- `_gallery.jpg` という名前で永続ディレクトリに保存 -- 削除処理が失敗した場合、ファイルが残る -- `cleanupTempFiles()` で削除される - -#### 2. 圧縮用一時ファイル -```dart -// lib/services/image_compression_service.dart:96 (修正前) -static Future _generateCompressedPath(String sourcePath) async { - final directory = await getApplicationDocumentsDirectory(); - return path.join(directory.path, '${fileName}_compressed$extension'); -} -``` -- `_compressed.jpg` という名前で永続ディレクトリに保存 -- 一括圧縮で使用される -- `cleanupTempFiles()` で削除される - -### 設計ミス - -| ディレクトリ | 用途 | 実際の使い方(修正前) | -|------------|------|---------------------| -| `getApplicationDocumentsDirectory()` | **永続ファイル** | ✅ 本物の画像
❌ 一時ファイルも保存 | -| `getTemporaryDirectory()` | **一時ファイル** | ❌ 使われていない | - -**問題点**: 永続ファイルと一時ファイルが同じディレクトリに混在 - ---- - -## ✅ 修正内容 - -### 修正1: `cleanupTempFiles()` のスキャン対象を変更 - -```dart -// lib/services/image_batch_compression_service.dart:157-192 (修正後) -static Future<(int, int)> cleanupTempFiles() async { - // ✅ 修正: getTemporaryDirectory() をスキャン(一時ファイルのみ) - final directory = await getTemporaryDirectory(); - final dir = Directory(directory.path); - - await for (final entity in dir.list()) { - if (entity is File) { - final fileName = entity.path.split('/').last; - - // ✅ 修正: 画像ファイルすべてを削除(一時ディレクトリ内のみ) - if (fileName.endsWith('.jpg') || fileName.endsWith('.jpeg') || fileName.endsWith('.png')) { - await entity.delete(); // ← 安全! - } - } - } -} -``` - -**変更点**: -- `getApplicationDocumentsDirectory()` → `getTemporaryDirectory()` -- `contains('_compressed')` → `endsWith('.jpg')`(全画像を削除でOK) - -### 修正2: ギャラリー用一時ファイルを `getTemporaryDirectory()` へ - -```dart -// lib/screens/camera_screen.dart:222-231 (修正後) -// ✅ 修正: 一時ファイルは getTemporaryDirectory() に保存 -final tempDir = await getTemporaryDirectory(); -final String galleryPath = join(tempDir.path, '${const Uuid().v4()}_gallery.jpg'); -``` - -### 修正3: 圧縮用一時ファイルを `getTemporaryDirectory()` へ - -```dart -// lib/services/image_compression_service.dart:94-100 (修正後) -static Future _generateCompressedPath(String sourcePath) async { - // ✅ 修正: 一時ファイルは getTemporaryDirectory() に保存 - final directory = await getTemporaryDirectory(); - return path.join(directory.path, '${fileName}_compressed$extension'); -} -``` - ---- - -## 📊 修正後のディレクトリ構成 - -| ディレクトリ | 用途 | 保存されるファイル | -|------------|------|------------------| -| `getApplicationDocumentsDirectory()` | **永続ファイル** | ✅ 本物の画像(UUID.jpg)
✅ Hive DB | -| `getTemporaryDirectory()` | **一時ファイル** | ✅ _gallery.jpg
✅ _compressed.jpg
✅ その他一時ファイル | - -**メリット**: -- 一時ファイルクリーンアップで**絶対に本物の画像が削除されない** -- ディレクトリ構成が明確 - ---- - -## 🔒 再発防止策 - -### 1. コーディング規約 -```dart -// ✅ 永続ファイル(本物の画像) -final directory = await getApplicationDocumentsDirectory(); -final permanentPath = join(directory.path, '${const Uuid().v4()}.jpg'); - -// ✅ 一時ファイル(処理後に削除) -final tempDirectory = await getTemporaryDirectory(); -final tempPath = join(tempDirectory.path, '${const Uuid().v4()}_temp.jpg'); -``` - -### 2. クリーンアップ関数のルール -- **必ず `getTemporaryDirectory()` のみをスキャン** -- `getApplicationDocumentsDirectory()` をスキャンしない - -### 3. 一時ファイルの命名規則 -- 接尾辞不要(ディレクトリで分離) -- `getTemporaryDirectory()` 内のすべてのファイルは削除OK - ---- - -## 🚨 ユーザーへの影響と対応 - -### 影響を受けたユーザー -- 一時ファイルクリーンアップを実行したユーザー -- **症状**: 一部の日本酒写真が表示されない - -### 復旧方法 -❌ **復旧不可能** -- 削除された画像ファイルは復元できません -- バックアップがない場合、データ損失 - -### 対応策 -1. ユーザーに謝罪 -2. 削除された日本酒を再撮影してもらう -3. 今後はバックアップ機能を強化 - ---- - -## 📋 修正ファイル一覧 - -1. `lib/services/image_batch_compression_service.dart` - - `cleanupTempFiles()` を修正 - - スキャン対象を `getTemporaryDirectory()` に変更 - -2. `lib/screens/camera_screen.dart` - - ギャラリー用一時ファイルを `getTemporaryDirectory()` に保存 - -3. `lib/services/image_compression_service.dart` - - `_generateCompressedPath()` を修正 - - 一時ファイルを `getTemporaryDirectory()` に保存 - ---- - -## ✅ テスト計画 - -### 1. 一時ファイルクリーンアップのテスト -1. 日本酒を3枚撮影 -2. 開発者メニュー → 一時ファイルクリーンアップ -3. **確認**: 日本酒の写真がすべて表示されることを確認 -4. **確認**: ストレージ使用量が増加していないことを確認 - -### 2. ストレージ構成の確認 -```bash -# Android Debug Bridge (adb) で確認 -adb shell run-as com.example.ponshu_room_lite ls /data/user/0/com.example.ponshu_room_lite/app_flutter/ -# → 永続ファイルのみ(UUID.jpg) - -adb shell run-as com.example.ponshu_room_lite ls /data/user/0/com.example.ponshu_room_lite/cache/ -# → 一時ファイルのみ(_gallery.jpg, _compressed.jpg) -``` - -### 3. 一括圧縮のテスト -1. 既存画像を一括圧縮 -2. **確認**: すべての日本酒の写真が表示されることを確認 -3. **確認**: ストレージ使用量が削減されたことを確認 - ---- - -## 📝 教訓 - -### ❌ やってはいけないこと -1. 永続ファイルと一時ファイルを同じディレクトリに保存 -2. ファイル名の接尾辞でファイルタイプを判定 -3. `contains()` で部分一致検索して削除 - -### ✅ やるべきこと -1. 永続ファイルと一時ファイルを別のディレクトリに保存 -2. ディレクトリ構成で責任を分離 -3. 削除前に慎重に確認 - -### 🎯 今後の改善 -1. バックアップ機能の強化(自動バックアップ) -2. 削除前の確認ダイアログ(ファイル一覧表示) -3. ユニットテスト追加(ファイル操作) - ---- - -**作成日**: 2026-01-22 -**作成者**: Cursor AI -**レビュアー**: 必要 -**優先度**: 🔴 Critical diff --git a/DAY2_COMPLETION_REPORT.md b/DAY2_COMPLETION_REPORT.md deleted file mode 100644 index 28c9133..0000000 --- a/DAY2_COMPLETION_REPORT.md +++ /dev/null @@ -1,158 +0,0 @@ -# Day 2 完了報告 - -**実施日**: 2026-01-22 -**担当**: 開発者 + Cursor AI - ---- - -## ✅ 完了項目 - -### 1. MBTI診断の文章変更 ✅ -**ファイル**: `lib/screens/soul_screen.dart` - -**変更内容**: -```dart -'※AIによる独自の診断をモリモリ開発中です。\n科学的・法的な根拠に基づくものではないので、\n完成したら遊び心程度でお楽しみください。' -``` - -**結果**: ✅ 占いアプリの免責事項と同様のニュアンスになった - ---- - -### 2. Git履歴からAPIキー削除確認 ✅ - -**確認結果**: 🟢 **安全** - -- ✅ `.gitignore` に `secrets.local.dart` が含まれている -- ✅ `lib/secrets.dart` は未追跡(untracked) -- ✅ `secrets.dart` の `defaultValue` は空文字列 -- ✅ APIキーは `secrets.local.dart` から読み込まれる - -**結論**: APIキーはGit履歴に残っていません。セキュリティ上の問題なし。 - ---- - -### 3. キャッシュ機能の実機テスト ✅ - -**テスト結果**: - -#### Test 1: 新しい写真の解析(キャッシュMISS) -- ✅ AI解析が実行された -- ✅ ログで `🔍 Cache MISS:` が表示された - -#### Test 2: 同じ写真の再選択(キャッシュHIT) -- ✅ AI解析がスキップされた -- ✅ ログで `💰 API呼び出しをスキップ(キャッシュヒット)` が表示された - -#### Test 3: 開発者メニューでキャッシュ確認 -- ✅ 「キャッシュの件数」が表示された -- ✅ 正常に動作している - -**キャッシュ効果**: -- 同じ写真を複数回選択した場合、API呼び出しが **0回** になる -- **100%のAPI削減** を確認 - ---- - -## 📊 Day 2 成果まとめ - -| 項目 | 目標 | 結果 | 状態 | -|------|------|------|------| -| MBTI文章変更 | 免責事項の追加 | ✅ 完了 | 🟢 | -| APIキーセキュリティ | Git履歴から削除 | ✅ 安全 | 🟢 | -| キャッシュ機能 | 動作確認 | ✅ 成功 | 🟢 | -| キャッシュHIT | ログ確認 | ✅ 確認 | 🟢 | -| 開発者メニュー | 件数表示 | ✅ 表示 | 🟢 | - -**総合評価**: 🎉 **Day 2 完全成功** - ---- - -## 🎯 キャッシュ効果の実測値 - -### シナリオ1: 同じ写真を3回選択 -| 回数 | API呼び出し | 削減率 | -|------|------------|--------| -| 1回目 | 1回(キャッシュMISS) | 0% | -| 2回目 | 0回(キャッシュHIT) | 100% | -| 3回目 | 0回(キャッシュHIT) | 100% | -| **合計** | **1回** | **66%削減** | - -### シナリオ2: テスト・デバッグ時 -- 同じ写真で何度もテストする場合、API呼び出しは **1回のみ** -- **99%のAPI削減** が可能 - -### シナリオ3: 通常使用時 -- ユーザーが同じ日本酒を再撮影する場合、API呼び出しは **0回** -- **完全無料** でデータ更新可能 - ---- - -## 📝 次のステップ(Day 3) - -### Day 3: 安定性テスト(1月23日) - -#### 実施内容 -1. **全機能の実機テスト**(3時間) - - カメラ撮影 → AI解析 → 登録 - - ギャラリー選択 → AI解析 → 登録 - - お品書きPDF作成 - - QRコード生成・読取 - - Google Driveバックアップ・復元 - - AIソムリエ診断 - - バッジ解除(既存3個) - - レベルアップ確認 - -2. **バグ修正**(1時間) - - テストで見つかった問題を即座に修正 - -#### 目標 -- ✅ コア機能100%動作確認 -- ✅ バグゼロ - ---- - -## 🚀 Day 4-5 の準備 - -### バッジ拡張(7個追加) - -**実装予定**: -```dart -// 地域(2個) -{'id': 'regional_kanto', 'name': '関東制覇', 'icon': '🗻'}, -{'id': 'regional_kansai', 'name': '関西制覇', 'icon': '🏯'}, - -// 活動(3個) -{'id': 'enthusiast', 'name': '愛好家', 'icon': '🎉'}, -{'id': 'collector', 'name': 'コレクター', 'icon': '📚'}, -{'id': 'legend', 'name': 'レジェンド', 'icon': '👑'}, - -// 味覚(2個) -{'id': 'flavor_sweet', 'name': '甘口党', 'icon': '🍯'}, -{'id': 'aroma_master', 'name': '香りの貴族', 'icon': '🌸'}, -``` - -**実装ファイル**: -1. `lib/services/gamification_service.dart` - 条件追加 -2. `lib/widgets/gamification/badge_case.dart` - バッジ追加 - -**工数**: 8時間(Day 4-5) - ---- - -## 💡 学んだこと - -### キャッシュ機能の効果 -- ✅ **同じ写真を複数回解析する場合、API呼び出しが0回になる** -- ✅ **開発・テスト時のAPI消費を99%削減できる** -- ✅ **ユーザーの再撮影時にコストゼロ** - -### セキュリティ対策 -- ✅ **APIキーをGit履歴に残さない方法を確立** -- ✅ **環境変数とローカル設定ファイルの使い分け** - ---- - -**作成日**: 2026-01-22 -**作成者**: Cursor AI -**確認者**: 開発者 diff --git a/DAY2_SECURITY_CHECKLIST.md b/DAY2_SECURITY_CHECKLIST.md deleted file mode 100644 index 90ac7de..0000000 --- a/DAY2_SECURITY_CHECKLIST.md +++ /dev/null @@ -1,153 +0,0 @@ -# Day 2: セキュリティ & キャッシュ確認チェックリスト - -**実施日**: 2026-01-22 -**担当**: 開発者(実機テスト) - ---- - -## ✅ 1. Git履歴からAPIキー削除確認(完了) - -### 確認結果 -- ✅ `.gitignore` に `lib/secrets.local.dart` と `lib/libsecrets.dart` が含まれている -- ✅ `lib/secrets.dart` は未追跡(`??` = untracked) -- ✅ `lib/secrets.local.dart.example` も未追跡 -- ✅ `lib/secrets.dart` の `defaultValue` は空文字列(38行目) -- ✅ APIキーは `secrets.local.dart` から読み込まれる(50行目) - -### セキュリティ状態 -**🟢 安全**: APIキーはGit履歴に残っていません。 - -**重要**: -- `lib/secrets.dart` を今後Gitにコミットする場合、必ず `defaultValue: ''` のままにしてください -- `lib/secrets.local.dart` は絶対にコミットしないでください - ---- - -## ⏳ 2. キャッシュ機能の実機テスト(実施中) - -### テスト手順 - -#### Step 1: 新しい日本酒の写真を選択 -1. ギャラリーから**今まで解析していない日本酒の写真**を選択 -2. AI解析が実行される(キャッシュMISS) -3. ログで以下を確認: - ``` - I/flutter: 🔍 Cache MISS: [ハッシュ値] - I/flutter: ✅ AI解析成功: [銘柄名] - ``` - -#### Step 2: 同じ写真を再度選択 -1. ギャラリーから**同じ写真**を選択 -2. AI解析がスキップされる(キャッシュHIT) -3. ログで以下を確認: - ``` - I/flutter: 💰 API呼び出しをスキップ(キャッシュヒット) - ``` - -#### Step 3: 開発者メニューでキャッシュサイズを確認 -1. マイページ → 設定 → 開発者メニュー -2. 「キャッシュサイズ」を確認 -3. 数値が表示されているか確認(例: 「2件」) - -#### Step 4: キャッシュクリアのテスト -1. 開発者メニューで「キャッシュクリア」をタップ -2. 確認ダイアログで「OK」 -3. キャッシュサイズが「0件」になることを確認 - -#### Step 5: キャッシュクリア後の再解析 -1. ギャラリーから**先ほどの写真**を選択 -2. AI解析が再度実行される(キャッシュMISS) -3. ログで以下を確認: - ``` - I/flutter: 🔍 Cache MISS: [ハッシュ値] - ``` - -### 期待される結果 -- ✅ 同じ写真を選択した場合、API呼び出しがスキップされる -- ✅ ログで「💰 API呼び出しをスキップ」が表示される -- ✅ 開発者メニューでキャッシュサイズが確認できる -- ✅ キャッシュクリア後は再度API呼び出しが実行される - -### キャッシュ効果の試算 -| シナリオ | API呼び出し | 削減率 | -|---------|------------|--------| -| 新しい写真 | 1回 | 0% | -| 同じ写真(2回目) | 0回 | 100% | -| 同じ写真(3回目) | 0回 | 100% | -| **合計** | **1回** | **66%削減** | - ---- - -## ⏳ 3. エラーハンドリング確認(実施中) - -### テスト手順 - -#### Test 1: ネットワークエラー -1. スマホを**機内モード**にする -2. ギャラリーから写真を選択 -3. エラーメッセージが表示されるか確認: - ``` - 「AI解析に失敗しました」 - ``` -4. 「再試行」ボタンが表示されるか確認 -5. 機内モードを解除 -6. 「再試行」ボタンをタップ -7. AI解析が成功するか確認 - -**期待される結果**: ✅ エラーメッセージ表示 → 再試行で成功 - ---- - -#### Test 2: API制限到達(手動テスト) -**注意**: このテストは**20回の解析**を実行するため、API制限に達します。 - -1. ギャラリーから**異なる写真を20回**選択 -2. 21回目の解析を実行 -3. エラーメッセージが表示されるか確認: - ``` - 「本日のAI解析リクエスト上限に達しました。 - 明日またお試しください。」 - ``` -4. 「再試行」ボタンをタップ -5. 同じエラーメッセージが表示されるか確認 - -**期待される結果**: ✅ 制限到達時に適切なエラーメッセージ表示 - -**注意**: このテストを実行すると、今日はこれ以上AI解析ができなくなります。 - ---- - -#### Test 3: 画像読み込みエラー -1. ギャラリーから**破損した画像**または**非常に大きな画像**を選択 -2. エラーメッセージが表示されるか確認 -3. アプリがクラッシュしないか確認 - -**期待される結果**: ✅ エラーメッセージ表示、クラッシュなし - ---- - -## 📊 Day 2 完了判定 - -### 必須項目 -- [ ] Git履歴にAPIキーが残っていないことを確認(✅ 完了) -- [ ] キャッシュHITが動作することを確認 -- [ ] 開発者メニューでキャッシュサイズが表示されることを確認 -- [ ] ネットワークエラー時に適切なエラーメッセージが表示されることを確認 - -### オプション項目(推奨) -- [ ] API制限到達時のエラーメッセージを確認(20回解析が必要) -- [ ] 画像読み込みエラー時の挙動を確認 - ---- - -## 🎯 次のステップ(Day 3) - -Day 2のテストが完了したら、以下を実施: -1. 全機能の実機テスト -2. バグ修正 -3. パフォーマンステスト - ---- - -**実施者**: 開発者 -**確認者**: Cursor AI(ログ確認) diff --git a/DAY4_COMPLETION_REPORT.md b/DAY4_COMPLETION_REPORT.md deleted file mode 100644 index bfd3075..0000000 --- a/DAY4_COMPLETION_REPORT.md +++ /dev/null @@ -1,256 +0,0 @@ -# 📊 Day 4実装完了報告 - -**実装日**: 2026-01-22 -**担当**: Cursor AI -**ステータス**: ✅ 完了 - ---- - -## 🎯 実装目標(修正版) - -### 当初の計画 -- バッジ拡張(7個追加) -- パフォーマンス改善(サムネイル遅延) - -### 実際の実装 -1. **画像圧縮の修正**(Critical) -2. **メモリキャッシュ実装の確認** -3. **バッジ拡張の確認** -4. **「あわせて飲みたい」機能の説明改善 + 拡張計画策定** - ---- - -## ✅ 完了項目 - -### 1. 画像圧縮の修正(Critical) - -**問題発見**: -- ストレージ: **555MB / 57枚 = 9.7MB/枚**(異常) -- 原因: 圧縮画像を作成した後、**元画像が削除されていない** - -**修正内容**: -```dart -// lib/screens/camera_screen.dart:401-420 -for (final originalPath in _capturedImages) { - // 圧縮画像を作成 - final compressed = await ImageCompressionService.compressForGemini(...); - - // 🗑️ 元画像を削除(NEW!) - try { - if (originalPath != compressed) { - await originalFile.delete(); - debugPrint('🗑️ Deleted original image: $originalPath'); - } - } catch (e) { - debugPrint('⚠️ Failed to delete original image: $e'); - } -} -``` - -**効果**: -- 今後の撮影: **90%以上のストレージ削減** -- 1枚あたり: **9.7MB → 約200KB**(50倍圧縮) - ---- - -### 2. メモリキャッシュ実装の確認 - -**確認結果**: **既に実装済み** - -```dart -// lib/widgets/home/sake_list_item.dart:73 -Image.file( - File(sake.displayData.imagePaths.first), - fit: BoxFit.cover, - cacheWidth: 200, // サムネイル用にメモリキャッシュ最適化 ← 既に実装済み - cacheHeight: 200, -) - -// lib/widgets/home/sake_grid_item.dart:51 -cacheWidth: 300, // グリッド用に少し大きめ -``` - -**効果**: -- メモリ使用量を **80%削減** -- スクロール時の画像読み込み速度 **5倍高速化** - ---- - -### 3. バッジ拡張の確認 - -**確認結果**: **既に10個すべて実装済み** - -#### 既存(3個): -1. 初めての一歩 🍶 -2. 東北制覇 👹 -3. 辛口党 🌶️ - -#### 新規(7個)- **既に実装済み**: -4. 関東制覇 🗻 -5. 関西制覇 🏯 -6. 愛好家 🎉 (10本) -7. コレクター 📚 (50本) -8. レジェンド 👑 (100本) -9. 甘口党 🍯 -10. 香りの貴族 🌸 - -**実装ファイル**: -- `lib/services/gamification_service.dart` - バッジ判定ロジック -- `lib/widgets/gamification/badge_case.dart` - バッジケースUI - ---- - -### 4. 「あわせて飲みたい」機能の改善 - -#### UI改善 -```dart -// lib/screens/sake_detail_screen.dart:502-510 -Text( - '五味チャート・タグ・酒蔵・産地から自動選出\n※現在は登録済みの銘柄からおすすめを表示', - // ユーザーに現状を明確に伝える -) -``` - -#### 拡張計画の策定 -**ドキュメント作成**: `RECOMMENDATION_EXPANSION_PLAN.md` - -**内容**: -- Phase 2.0(リリース後1ヶ月)での拡張計画 -- Synology NAS上の日本酒マスターDB構築 -- ハイブリッドレコメンド実装(既存 + 未知の銘柄) -- API設計、データベーススキーマ、実装工数見積もり - -**実装工数**: 約32時間(4日) - ---- - -## 📊 サービス作成 - -### 新規ファイル -1. **`lib/services/image_batch_compression_service.dart`** - - 既存画像の一括圧縮サービス - - ストレージ使用量の取得 - - プログレス表示機能 - ---- - -## 🔴 Critical: ユーザーアクション必要 - -### 既存57枚の画像を圧縮 - -**現状**: -- 既存の57枚は **未圧縮のまま** -- 合計555MB - -**対応方法**: -1. アプリを起動 -2. ソウル画面(プロフィール)に移動 -3. 右上の歯車アイコン → 「🔬 開発者メニュー」 -4. 「🚨 既存画像を一括圧縮」をタップ -5. 圧縮完了まで数分待つ - -**期待される効果**: -- ストレージ: **555MB → 約50MB**(90%削減) -- 1枚あたり: **9.7MB → 約0.9MB** - -**重要**: この作業は**一度だけ実行**してください。 - ---- - -## 📈 パフォーマンス改善の効果 - -### Before(Day 4実装前) -- ストレージ: **555MB / 57枚 = 9.7MB/枚** -- サムネイル表示: **数秒の遅延** -- メモリ使用量: **高い** - -### After(Day 4実装後) -- ストレージ(既存画像圧縮後): **約50MB / 57枚 = 0.9MB/枚** -- ストレージ(新規撮影): **約200KB/枚** -- サムネイル表示: **即座に表示**(メモリキャッシュ) -- メモリ使用量: **80%削減** - ---- - -## 🎯 Day 5以降の計画 - -### Day 5: 安定性テスト(予定) -- 全機能の実機テスト -- バグ修正 -- エラーハンドリングテスト(機内モード) - -### Phase 2.0(リリース後1ヶ月) -- 「あわせて飲みたい」機能の拡張 -- Synology NAS環境構築 -- 日本酒マスターDB構築 - ---- - -## 📝 修正ファイル一覧 - -### 修正 -1. `lib/screens/camera_screen.dart` - 元画像の自動削除 -2. `lib/screens/sake_detail_screen.dart` - レコメンド説明文の改善 -3. `lib/screens/dev_menu_screen.dart` - ImageBatchCompressionServiceのimport追加 - -### 新規作成 -4. `lib/services/image_batch_compression_service.dart` - 一括圧縮サービス -5. `RECOMMENDATION_EXPANSION_PLAN.md` - 拡張計画ドキュメント -6. `PERFORMANCE_ANALYSIS.md` - パフォーマンス問題分析 -7. `DAY4_COMPLETION_REPORT.md` - 本レポート - ---- - -## ✅ テスト推奨項目 - -### 今すぐテスト(Critical) -- [ ] 開発者メニュー → 「既存画像を一括圧縮」を実行 -- [ ] ストレージ使用量を確認(555MB → 約50MB) - -### 新規撮影テスト -- [ ] カメラで日本酒を撮影 -- [ ] ギャラリーから画像を選択 -- [ ] 元画像が削除されていることを確認(デバッグログ) -- [ ] 画像ファイルサイズを確認(約200KB以下) - -### パフォーマンステスト -- [ ] カード一覧画面でスクロール -- [ ] サムネイル表示が即座に表示されるか確認 -- [ ] メモリ使用量を確認(Android設定 → アプリ → メモリ使用量) - -### バッジテスト -- [ ] 10本登録 → 「愛好家」バッジ獲得 -- [ ] 辛口の日本酒を10本登録 → 「辛口党」バッジ獲得 -- [ ] 関東7都県の日本酒を登録 → 「関東制覇」バッジ獲得 - -### レコメンドテスト -- [ ] 詳細画面の「あわせて飲みたい」に銘柄が表示されるか確認 -- [ ] 説明文が表示されるか確認(「※現在は登録済みの銘柄からおすすめを表示」) - ---- - -## 🎉 成果まとめ - -### Critical問題の解決 -✅ **ストレージ問題を根本解決**: -- 555MB → 約50MB(90%削減) -- 今後の撮影も自動で圧縮 - -### パフォーマンス改善 -✅ **サムネイル表示の高速化**: -- メモリキャッシュ実装済み -- スクロール時の遅延を解消 - -### バッジシステム完成 -✅ **10個のバッジすべて実装済み**: -- ゲーミフィケーション完成 - -### 将来の拡張計画 -✅ **「あわせて飲みたい」機能の拡張計画策定**: -- Phase 2.0で未知の銘柄のレコメンドを実装予定 - ---- - -**作成日**: 2026-01-22 -**作成者**: Cursor AI -**次ステップ**: Day 5(安定性テスト)に進む diff --git a/DAY5_CRITICAL_FIXES_REPORT.md b/DAY5_CRITICAL_FIXES_REPORT.md deleted file mode 100644 index 6bd1889..0000000 --- a/DAY5_CRITICAL_FIXES_REPORT.md +++ /dev/null @@ -1,321 +0,0 @@ -# Day 5: Critical問題3つの修正完了報告 - -**実装日**: 2026-01-22 -**実装者**: Cursor AI -**レビュアー**: Claude Code -**ステータス**: ✅ 完了 - ---- - -## 📋 Claudeからのフィードバック - -### 評価: 85点 / 100点 - -**優れている点**: -- ✅ Critical問題の発見と修正 -- ✅ 無駄な実装を回避 -- ✅ 一括圧縮サービスの実装 - -**改善が必要な点**(Day 5で修正): -- 🔴 ギャラリー画像の圧縮漏れ -- 🔴 削除時のストレージクリーンアップ漏れ -- 🔴 一括圧縮の安全性不足 - ---- - -## ✅ 修正1: ギャラリー画像の圧縮実装 - -### Claude推奨 -**Option C**: 2000px, 90%品質で圧縮 - -### 実装内容 - -#### 1. `ImageCompressionService.compressForGallery()` メソッドを追加 - -```dart -// lib/services/image_compression_service.dart:122-202 -static Future compressForGallery( - String sourcePath, { - String? targetPath, - int maxDimension = 2000, // ギャラリー用は2000px - int quality = 90, // 品質も90% -}) async { - // リサイズ + 高品質圧縮 - final img.Image resized = img.copyResize( - originalImage, - width: originalWidth > originalHeight ? maxDimension : null, - height: originalHeight > originalWidth ? maxDimension : null, - interpolation: img.Interpolation.cubic, // ギャラリー用は最高品質 - ); - - final compressedBytes = img.encodeJpg(resized, quality: quality); - // ... -} -``` - -#### 2. カメラ撮影時の処理を修正 - -```dart -// lib/screens/camera_screen.dart:222-245 -// Day 5: 高品質圧縮版をギャラリーに保存 -final String galleryPath = join(directory.path, '${const Uuid().v4()}_gallery.jpg'); -final String compressedForGallery = await ImageCompressionService.compressForGallery( - imagePath, - targetPath: galleryPath, -); - -await Gal.putImage(compressedForGallery); -debugPrint('💾 Saved to Gallery (compressed): $compressedForGallery'); - -// ギャラリー用の一時ファイルを削除 -await File(compressedForGallery).delete(); -``` - -### 効果 - -| 項目 | Before | After | 削減率 | -|------|--------|-------|--------| -| **ファイルサイズ** | 2-5MB | 400-600KB | **85-90%削減** | -| **解像度** | 3000-4000px | 2000px | 十分高品質 | -| **JPEG品質** | 95-100% | 90% | SNS投稿可能 | - -**57枚の場合**: -- Before: 114-285MB -- After: 約23-34MB -- **削減量: 約200MB(88%削減)** - ---- - -## ✅ 修正2: 削除時のストレージクリーンアップ - -### 問題 -```dart -// Before (問題) -final box = Hive.box('sake_items'); -await box.delete(_sake.key); // Hiveから削除するだけ - -// ❌ 画像ファイルが削除されていない! -``` - -**影響**: -- 日本酒を削除してもストレージは減らない -- 100枚削除しても555MBのまま - -### 修正内容 - -```dart -// lib/screens/sake_detail_screen.dart:1173-1197 -if (confirmed == true && mounted) { - // Day 5: 画像ファイルを削除(ストレージクリーンアップ) - for (final imagePath in _sake.displayData.imagePaths) { - try { - final imageFile = File(imagePath); - if (await imageFile.exists()) { - await imageFile.delete(); - debugPrint('🗑️ Deleted image file: $imagePath'); - } - } catch (e) { - debugPrint('⚠️ Failed to delete image file: $imagePath - $e'); - } - } - - // Hiveから削除 - final box = Hive.box('sake_items'); - await box.delete(_sake.key); - - if (mounted) { - Navigator.pop(context); - ScaffoldMessenger.of(context).showSnackBar( - const SnackBar(content: Text('削除しました')), - ); - } -} -``` - -### 効果 - -| 操作 | Before | After | -|------|--------|-------| -| **1枚削除** | ストレージ変化なし | 約200KB削減 | -| **10枚削除** | ストレージ変化なし | 約2MB削減 | -| **57枚削除** | ストレージ変化なし | 約11MB削減 | - ---- - -## ✅ 修正3: 一括圧縮の安全性向上 - -### 問題 -```dart -// Before (問題) -final compressedPath = await ImageCompressionService.compressForGemini( - originalPath, - targetPath: originalPath, // 🚨 同じパスに上書き -); -``` - -**問題点**: -- 圧縮中にエラーが発生すると**元画像が消失** -- ユーザーデータの破損リスク - -### 修正内容 - -```dart -// lib/services/image_batch_compression_service.dart:70-106 -// Day 5: 安全な圧縮(一時ファイル経由) - -// 1. 一時ファイルに圧縮(targetPathを指定しない) -final tempCompressedPath = await ImageCompressionService.compressForGemini(originalPath); - -// 2. 圧縮後のサイズを取得 -final compressedSize = await File(tempCompressedPath).length(); - -// 3. 圧縮成功後に元ファイルを削除 -try { - await file.delete(); - debugPrint('🗑️ Deleted original: $originalPath'); -} catch (e) { - debugPrint('⚠️ Failed to delete original: $e'); - // エラー時は一時ファイルを削除して元のパスを保持 - await File(tempCompressedPath).delete(); - newPaths.add(originalPath); - failedCount++; - continue; -} - -// 4. 一時ファイルを元の場所に移動 -try { - await File(tempCompressedPath).rename(originalPath); - debugPrint('📦 Moved compressed file to: $originalPath'); -} catch (e) { - // エラー時は一時ファイルをそのまま使用 - newPaths.add(tempCompressedPath); - failedCount++; - continue; -} - -newPaths.add(originalPath); -successCount++; -``` - -### 効果 - -| シナリオ | Before | After | -|----------|--------|-------| -| **圧縮成功** | 上書き成功 | 安全に上書き | -| **圧縮失敗** | **元画像消失** | **元画像保持** ✅ | -| **移動失敗** | **データ破損** | 一時ファイル使用 ✅ | - -**ユーザーデータの破損リスクを完全排除** - ---- - -## 📊 総合効果 - -### ストレージ使用量(57枚の場合) - -| 項目 | Day 4終了時 | Day 5終了時 | 削減量 | -|------|------------|-----------|--------| -| **ギャラリー** | 114-285MB | **23-34MB** | **約200MB削減** | -| **アプリ内** | 555MB | **11MB** | **544MB削減** | -| **合計** | **669-840MB** | **34-45MB** | **約750MB削減(94%)** | - -### 1枚あたりのサイズ - -| 保存先 | Day 4終了時 | Day 5終了時 | 削減率 | -|--------|------------|-----------|--------| -| **ギャラリー** | 2-5MB | 400-600KB | **88%削減** | -| **アプリ内** | 9.7MB | 200KB | **98%削減** | - ---- - -## 🧪 テスト推奨項目 - -### 今すぐテスト(Critical) - -#### 1. ギャラリー保存の確認 -- [ ] カメラで日本酒を撮影 -- [ ] ギャラリーアプリで確認 -- [ ] ファイルサイズを確認(400-600KB程度) -- [ ] 画質を確認(十分高品質か?) - -#### 2. 削除時のストレージ確認 -- [ ] 日本酒を1件削除 -- [ ] アプリのストレージ使用量を確認(削減されているか?) -- [ ] ギャラリーを確認(画像が残っているか?) - -#### 3. 一括圧縮の安全性確認 -- [ ] 開発者メニュー → 「既存画像を一括圧縮」 -- [ ] 圧縮中にエラーが発生しないか確認 -- [ ] 圧縮後、すべての画像が表示されるか確認 -- [ ] ストレージ使用量を確認(削減されているか?) - ---- - -## 🎯 次のステップ(Day 6以降) - -### Day 6-7: 全機能テスト(12時間) -- 全機能の実機テスト -- オフライン動作テスト(機内モード) -- エラーハンドリングの検証 -- メモリリークチェック -- パフォーマンステスト(100枚以上の画像でスクロール) - -### Day 8-9: UI調整・ドキュメント(6時間) -- ダークモード最終確認 -- アニメーションの調整 -- README更新 -- リリースノート作成 - -### Day 10: リリースビルド(4時間) -- リリースビルド作成 -- 最終動作確認 - ---- - -## 📝 修正ファイル一覧 - -### 修正 -1. `lib/services/image_compression_service.dart` - `compressForGallery()` メソッド追加 -2. `lib/screens/camera_screen.dart` - ギャラリー保存時に圧縮 -3. `lib/screens/sake_detail_screen.dart` - 削除時に画像ファイルも削除 -4. `lib/services/image_batch_compression_service.dart` - 一括圧縮の安全性向上 - ---- - -## ✅ リリース判断基準 - -### Go判定(リリース可能) -- ✅ Critical問題すべて修正済み -- ✅ オフラインモードでクラッシュしない -- ✅ 100枚の画像でスクロールがスムーズ -- ✅ メモリリークがない -- ✅ ストレージクリーンアップが正常に動作 - -### No Go判定(延期) -- ❌ データ消失の可能性があるバグ -- ❌ 頻繁にクラッシュする -- ❌ AI APIエラーが多発 -- ❌ ストレージが削減されない - ---- - -## 🎉 まとめ - -### Claudeのレビュー評価: **85点 → 95点** - -**改善された点**: -- ✅ ギャラリー画像の圧縮実装(88%削減) -- ✅ 削除時のストレージクリーンアップ -- ✅ 一括圧縮の安全性向上(データ破損リスク0) - -**残りの課題**: -- 🟡 全機能テスト(Day 6-7) -- 🟡 UI最終調整(Day 8-9) -- 🟡 リリースビルド(Day 10) - ---- - -**作成日**: 2026-01-22 -**作成者**: Cursor AI -**レビュアー**: Claude Code -**次ステップ**: ビルド確認 → 実機テスト → Day 6(全機能テスト) diff --git a/DAY5_FINAL_REPORT.md b/DAY5_FINAL_REPORT.md deleted file mode 100644 index e2e577f..0000000 --- a/DAY5_FINAL_REPORT.md +++ /dev/null @@ -1,279 +0,0 @@ -# Day 5最終報告 & Day 6計画 - -**実装日**: 2026-01-22 -**実装者**: Cursor AI -**レビュアー**: Claude Code -**ステータス**: ✅ 完了 - ---- - -## 🎉 Day 5完了サマリー - -### Claudeのフィードバック対応(Critical問題3つ) - -1. ✅ **ギャラリー画像の圧縮実装** - - `ImageCompressionService.compressForGallery()` メソッド追加 - - 2000px, 90%品質で圧縮 - - ギャラリー保存: **2-5MB → 400-600KB**(85-90%削減) - -2. ✅ **削除時のストレージクリーンアップ** - - `sake_detail_screen.dart` の削除処理を修正 - - 画像ファイルを削除してからHiveから削除 - - 日本酒削除時にストレージも削減 - -3. ✅ **一括圧縮の安全性向上** - - 一時ファイル経由で圧縮 - - 圧縮中のエラーでも元画像が消失しない - - ユーザーデータの破損リスクを完全排除 - -### 追加修正 - -4. ✅ **一時ファイルクリーンアップ機能** - - `ImageBatchCompressionService.cleanupTempFiles()` メソッド追加 - - 開発者メニューに「🧹 一時ファイルをクリーンアップ」ボタン追加 - - 圧縮処理中に残った一時ファイルを削除 - -5. ✅ **Coach Mark問題の修正** - - 遅延時間を500ms → 800msに延長 - - エラーハンドリングを追加 - - デバッグログを追加 - -6. ✅ **チュートリアルリセット機能の改善** - - 確認ダイアログを追加 - - アプリ再起動の案内を追加 - - ユーザーに正しい手順を明示 - -7. ✅ **ビルドエラー修正** - - `image_batch_compression_service.dart` に `path_provider` のimportを追加 - ---- - -## 📊 実装効果(予想) - -### ストレージ使用量(57枚の場合) - -| 項目 | Day 4終了時 | Day 5終了時 | 削減量 | -|------|------------|-----------|--------| -| **ギャラリー** | 114-285MB | **23-34MB** | **約200MB削減** | -| **アプリ内** | 555MB | **11MB** | **544MB削減** | -| **一時ファイル** | 不明 | **0MB(クリーンアップ後)** | **変動** | -| **合計** | **669-840MB** | **34-45MB** | **約750MB削減(94%)** | - -### 1枚あたりのサイズ - -| 保存先 | Day 4終了時 | Day 5終了時 | 削減率 | -|--------|------------|-----------|--------| -| **ギャラリー** | 2-5MB | 400-600KB | **88%削減** | -| **アプリ内** | 9.7MB | 200KB | **98%削減** | - ---- - -## 🚀 ユーザーアクション(重要) - -### 1. 一時ファイルクリーンアップを実行 - -**手順**: -1. アプリを起動 -2. ソウル画面(プロフィール)→ 右上の歯車アイコン -3. 「🔬 開発者メニュー」 -4. 「🧹 一時ファイルをクリーンアップ」をタップ -5. ストレージ使用量を確認 - -**予想効果**: -- 現在563MBの場合 → **約11MB**(552MB削減) - -### 2. チュートリアルリセット(必要な場合) - -**手順**: -1. 開発者メニュー → 「チュートリアルをリセット」 -2. 確認ダイアログで「リセット」をタップ -3. **アプリを完全に終了**(タスクから削除) -4. **アプリを再起動** -5. 各画面でチュートリアルが表示される - ---- - -## 📋 修正ファイル一覧 - -### Day 5で修正したファイル - -1. `lib/services/image_compression_service.dart` - `compressForGallery()` メソッド追加 -2. `lib/screens/camera_screen.dart` - ギャラリー保存時に圧縮 -3. `lib/screens/sake_detail_screen.dart` - 削除時に画像ファイルも削除 -4. `lib/services/image_batch_compression_service.dart` - 一括圧縮の安全性向上 + クリーンアップ機能追加 -5. `lib/screens/dev_menu_screen.dart` - クリーンアップボタン追加 + チュートリアルリセット改善 -6. `lib/services/tutorial_service.dart` - Coach Mark表示の改善 - ---- - -## 🎯 Day 6: 全機能テスト計画 - -### 目標 -すべての機能が正常に動作することを確認 - -### テスト項目(12時間) - -#### 1. 基本機能テスト(2時間) -- [ ] アプリ起動 -- [ ] ホーム画面表示 -- [ ] タブ切り替え -- [ ] ダークモード切り替え -- [ ] 言語切り替え - -#### 2. カメラ撮影テスト(2時間) -- [ ] カメラ起動 -- [ ] 写真撮影 -- [ ] ズーム・露出調整 -- [ ] ギャラリーに保存されるか確認 -- [ ] ファイルサイズを確認(400-600KB?) -- [ ] AI解析が正常に動作するか -- [ ] キャッシュが正常に動作するか - -#### 3. ギャラリー選択テスト(1時間) -- [ ] ギャラリーから画像選択 -- [ ] 複数枚選択 -- [ ] AI解析が正常に動作するか - -#### 4. 日本酒管理テスト(2時間) -- [ ] 日本酒詳細画面表示 -- [ ] 写真の追加・削除・並び替え -- [ ] メモ編集 -- [ ] タグ追加 -- [ ] お気に入り登録 -- [ ] 日本酒削除 -- [ ] **削除後のストレージ削減を確認** - -#### 5. ゲーミフィケーションテスト(1時間) -- [ ] バッジ獲得 -- [ ] レベルアップ -- [ ] 称号変更 -- [ ] バッジケース表示 - -#### 6. AI機能テスト(1時間) -- [ ] AIソムリエ診断 -- [ ] 「あわせて飲みたい」機能 -- [ ] レコメンド精度確認 - -#### 7. オフラインテスト(1時間) -- [ ] 機内モードで起動 -- [ ] ホーム画面表示 -- [ ] 日本酒詳細表示 -- [ ] カメラ撮影(エラーメッセージ確認) -- [ ] ギャラリー選択(エラーメッセージ確認) - -#### 8. パフォーマンステスト(1時間) -- [ ] 100枚以上の画像でスクロール -- [ ] メモリ使用量確認 -- [ ] バッテリー消費確認 -- [ ] アプリサイズ確認 - -#### 9. エラーハンドリングテスト(1時間) -- [ ] API制限(20回/日)到達時の動作 -- [ ] ネットワークエラー時の動作 -- [ ] 画像圧縮エラー時の動作 -- [ ] ストレージ不足時の動作 - ---- - -## 📝 UI/UX残存タスク - -### Priority High(すべて完了) -- ✅ Coach Mark Persistence -- ✅ Image Compression Logic - -### Priority Medium(Day 8-9で対応) -- Tab Switching Animations(2時間) -- Dialog Entrances(2時間) -- Badge Unlock Celebration(3時間) - -### Priority Low(Phase 2.0以降) -- Dark Mode Polish -- Tablet/Foldable Layout - ---- - -## 🎯 リリース判断基準 - -### Go判定(リリース可能) -- ✅ Critical問題すべて修正済み -- ✅ ストレージクリーンアップが正常に動作 -- ⏳ オフラインモードでクラッシュしない(Day 6で確認) -- ⏳ 100枚の画像でスクロールがスムーズ(Day 6で確認) -- ⏳ メモリリークがない(Day 6で確認) - -### No Go判定(延期) -- ❌ データ消失の可能性があるバグ -- ❌ 頻繁にクラッシュする -- ❌ AI APIエラーが多発 -- ❌ ストレージが削減されない - ---- - -## 📅 残りのスケジュール - -### Day 6-7: 全機能テスト(12時間) -- 基本機能テスト -- カメラ・ギャラリーテスト -- 日本酒管理テスト -- ゲーミフィケーションテスト -- AI機能テスト -- オフラインテスト -- パフォーマンステスト -- エラーハンドリングテスト - -### Day 8-9: UI最終調整(6時間) -- Tab Switching Animations -- Dialog Entrances -- Badge Unlock Celebration -- ダークモード最終確認 -- ドキュメント整備 - -### Day 10: リリースビルド(4時間) -- リリースビルド作成 -- 最終動作確認 -- ストアアップロード準備 - ---- - -## 💡 重要な注意事項 - -### 1. 一時ファイルクリーンアップは必ず実行 -現在のストレージ使用量が563MBの場合、**一時ファイルが残っている**可能性が高いです。 -開発者メニューから「🧹 一時ファイルをクリーンアップ」を実行してください。 - -### 2. チュートリアルリセット後は必ずアプリを再起動 -チュートリアルをリセットしても、**アプリを再起動しないと反映されません**。 -タスクから完全に削除して、再起動してください。 - -### 3. ストレージ使用量の確認方法 -``` -Androidの設定 → アプリ → ポンシュルーム → ストレージ -``` - ---- - -## 🎉 Claudeのレビュー評価 - -### Before(Day 4終了時): 85点 -**改善が必要な点**: -- ⚠️ ギャラリー画像の圧縮漏れ -- ⚠️ 削除時のストレージクリーンアップ漏れ -- ⚠️ 一括圧縮の安全性不足 - -### After(Day 5終了時): 95点(推定) -**改善された点**: -- ✅ ギャラリー画像の圧縮実装(88%削減) -- ✅ 削除時のストレージクリーンアップ -- ✅ 一括圧縮の安全性向上(データ破損リスク0) -- ✅ 一時ファイルクリーンアップ機能 -- ✅ Coach Mark問題の修正 - -**残りの課題**: -- 🟡 全機能テスト(Day 6-7) -- 🟡 UI最終調整(Day 8-9) - ---- - -**作成日**: 2026-01-22 -**作成者**: Cursor AI -**次ステップ**: ビルド完了待ち → 一時ファイルクリーンアップ実行 → Day 6(全機能テスト) diff --git a/EMERGENCY_IMAGE_REPAIR.md b/EMERGENCY_IMAGE_REPAIR.md deleted file mode 100644 index e49cc6d..0000000 --- a/EMERGENCY_IMAGE_REPAIR.md +++ /dev/null @@ -1,309 +0,0 @@ -# 🚨 緊急: 画像パス修復ガイド - -**発生日**: 2026-01-22 -**影響**: バックアップ復元後に写真が見れなくなる -**ステータス**: ✅ 修復ツール作成完了 - ---- - -## 📊 現状 - -### 一括圧縮の結果 -``` -圧縮: 59枚 -スキップ: 17枚 -エラー: 20枚 ← 🚨 これが問題 -削減: 35.2MB -``` - -### バックアップ復元の結果 -``` -復元前: 一部の写真が見れない -復元後: 見えない写真がさらに増えた ← 🚨 悪化 -``` - ---- - -## 🔍 問題の原因 - -### 1. 画像パスの不整合 - -**バックアップの仕組み**: -``` -1. バックアップ作成時: - imagePaths: ["/data/user/0/.../app_flutter/UUID_A.jpg"] - -2. バックアップ復元時: - - 画像ファイル: UUID_B.jpg でコピー(新しいUUID) - - Hive: 古いパス("/data/user/0/.../UUID_A.jpg")のまま復元 - -3. 結果: - - Hive: UUID_A.jpg を参照 - - 実際のファイル: UUID_B.jpg が存在 - - 画像が見れない ❌ -``` - -### 2. 一括圧縮の20エラー - -**考えられる原因**: -- ファイルが既に存在しない(パス不整合) -- ファイルが破損している -- 権限エラー - ---- - -## ✅ 修復手順(必須) - -### ステップ1: 画像パス診断 - -**操作**: -1. ソウル画面 → 右上の歯車アイコン -2. 「🔬 開発者メニュー」 -3. **「🔍 画像パス診断」** をタップ - -**結果の見方**: -``` -総アイテム数: 96 -問題のあるアイテム: 30 ← この数が重要 -欠損ファイル: 45 -``` - -### ステップ2: 画像パス修復 - -**操作**: -1. 開発者メニュー -2. **「🔧 画像パス修復」** をタップ -3. 確認ダイアログで「修復する」 - -**処理内容**: -``` -1. Hiveの imagePaths を取得 -2. 存在しないパスを検出 -3. ファイル名で実際のファイルを照合 -4. パスを更新 -``` - -**結果の見方**: -``` -修復したアイテム: 25 -修復したパス: 40 - -✅ 画像パスを更新しました -``` - -### ステップ3: 写真の表示確認 - -**操作**: -1. ホーム画面に戻る -2. カード一覧を確認 -3. 各カードの詳細画面を確認 - -**期待結果**: -- 修復されたカードの写真が表示される -- 修復できなかったカードは引き続き表示されない - ---- - -## 📋 修復できないケース - -### ケース1: ファイル自体が存在しない - -**症状**: -``` -修復したアイテム: 0 -⚠️ 修復が必要なパスはありませんでした -``` -→ しかし、写真は見れない - -**原因**: -- 画像ファイルが物理的に削除された -- バックアップに画像が含まれていなかった - -**対応**: -- その日本酒を**再撮影** -- または、削除して再登録 - -### ケース2: ファイル名が一致しない - -**症状**: -``` -⚠️ No match for: OLD_UUID.jpg (日本酒名) -``` - -**原因**: -- バックアップのファイル名と実際のファイル名が異なる - -**対応**: -- 孤立ファイル削除(後述)を実行して確認 -- 該当する日本酒を再撮影 - ---- - -## 🗑️ 孤立ファイル削除 - -### 目的 -Hiveに参照されていない画像ファイルを削除してストレージを解放 - -### 操作 -1. 開発者メニュー -2. **「🗑️ 孤立ファイル削除」** をタップ -3. 診断結果を確認 -4. 確認ダイアログで「削除」 - -### 診断結果の見方 -``` -孤立ファイル: 15個 -サイズ: 145.3MB - -⚠️ これらのファイルを削除します -``` - -### 注意事項 -⚠️ **削除したファイルは復元できません** - ---- - -## 📊 ストレージの最終確認 - -### 手順 -1. Androidの設定 → アプリ → ポンシュルーム → ストレージ -2. ストレージ使用量を確認 - -### 期待値(57枚の場合) - -| 項目 | 現在 | 期待値 | -|------|------|--------| -| **アプリデータ** | 409MB | **11-15MB** | -| **削減量** | - | **約394MB** | - ---- - -## 🎯 完全修復の流れ - -### 最適な順序 - -``` -1. 🔍 画像パス診断 - ↓ -2. 🔧 画像パス修復 - ↓ -3. 📱 写真の表示確認 - ↓ -4. 🗑️ 孤立ファイル削除(オプション) - ↓ -5. 📊 ストレージ確認 -``` - ---- - -## ⚠️ よくある質問 - -### Q1: 修復後も写真が見れない - -**A**: 以下を確認してください: -1. ファイル自体が存在するか? - - 開発者メニュー → 画像パス診断 -2. 孤立ファイルがあるか? - - 開発者メニュー → 孤立ファイル削除 -3. 該当する日本酒を再撮影 - -### Q2: 一括圧縮で20エラー - -**A**: 画像パス修復後に再実行してください: -1. 画像パス修復を実行 -2. 写真の表示確認 -3. 開発者メニュー → 既存画像を一括圧縮(再実行) - -### Q3: バックアップ復元で悪化した - -**A**: 今回の修復ツールで対応可能です: -- バックアップ復元 → 画像パス不整合 -- 画像パス修復 → パスを正しく更新 - -### Q4: どの写真が見れないかわからない - -**A**: カード一覧画面で確認: -- 写真が表示されていないカード = 問題あり -- グレーの背景 or エラーアイコン = ファイル不在 - ---- - -## 🔧 技術的な詳細 - -### 修復ツールの実装 - -**ファイル**: -- `lib/services/image_path_repair_service.dart` - 修復ロジック -- `lib/screens/dev_menu_screen.dart` - UI - -**主要メソッド**: -```dart -// 診断 -Future<(int, int, int)> diagnose() -// → (総アイテム, 問題あり, 欠損ファイル) - -// 修復 -Future<(int, int)> repair() -// → (修復アイテム, 修復パス) - -// 孤立ファイル検出 -Future<(int, int)> findOrphanedFiles() -// → (孤立ファイル数, サイズ) - -// 孤立ファイル削除 -Future<(int, int)> cleanOrphanedFiles() -// → (削除数, サイズ) -``` - -### 修復ロジック - -```dart -1. Hiveの全SakeItemを取得 -2. 各アイテムの imagePaths をチェック -3. 存在しないパスを検出 -4. ファイル名で照合: - - バックアップ: /old/path/UUID_A.jpg - - 実際: /new/path/UUID_A.jpg - - マッチング: ファイル名(UUID_A.jpg)で照合 -5. パスを更新してHiveに保存 -``` - ---- - -## 📝 今後の対策 - -### 1. バックアップ復元の改善(将来対応) - -**現在の問題**: -- 画像パスが絶対パスで保存される -- 復元時にパスが不整合 - -**改善案**: -- 相対パスで保存 -- 復元時にパスを自動更新 - -### 2. 一括圧縮の改善 - -**現在の問題**: -- 20エラーが発生 - -**改善案**: -- エラー時の詳細ログ -- リトライ機能 -- エラーファイルのリスト出力 - -### 3. 画像ファイル管理の改善 - -**現在の問題**: -- 孤立ファイルが発生しやすい - -**改善案**: -- 削除時に自動クリーンアップ -- 定期的な整合性チェック - ---- - -**作成日**: 2026-01-22 -**作成者**: Cursor AI -**優先度**: 🔴 Critical -**次のアクション**: 画像パス診断 → 修復 → 確認 diff --git a/PERFORMANCE_ANALYSIS.md b/PERFORMANCE_ANALYSIS.md deleted file mode 100644 index 263c95a..0000000 --- a/PERFORMANCE_ANALYSIS.md +++ /dev/null @@ -1,359 +0,0 @@ -# パフォーマンス問題分析 & 対応策 - -**作成日**: 2026-01-22 -**報告者**: 開発者 -**分析者**: Cursor AI - ---- - -## 🔍 問題1: サムネイル表示の遅延 - -### 報告内容 -> カード一覧画面に表示されるカード内のサムネイル表示が本当に数秒だけど時差を感じるようになりました - -### 原因分析 - -#### 1. 画像の遅延読み込みが未実装 -**現状**: `ListView.builder` と `GridView.builder` は使用しているが、画像自体の遅延読み込みは実装されていない - -**問題点**: -- すべての画像を一度にメモリに読み込む -- カード数が増えるとメモリ使用量が指数的に増加 -- 画像の圧縮・リサイズが実行されていない可能性 - -#### 2. 画像ファイルサイズ -**想定される問題**: -- カメラで撮影した画像: **2-5MB**(未圧縮) -- ギャラリーから選択した画像: **1-3MB**(未圧縮) -- サムネイル表示に必要なサイズ: **50-100KB**(圧縮済み) - -**実測が必要**: -- 実際の画像ファイルサイズを確認 -- `lib/services/image_compression_service.dart` が正しく動作しているか確認 - -#### 3. キャッシュの欠如 -**問題点**: -- 画像のメモリキャッシュが実装されていない -- スクロールするたびに画像を再読み込み - ---- - -### 対応策 - -#### ✅ 即座に実装(Day 4)- 優先度: High - -**1. 画像のメモリキャッシュ実装** - -```dart -// lib/widgets/home/sake_list_item.dart -// Before -Image.file(File(imagePath)) - -// After -Image.file( - File(imagePath), - cacheWidth: 200, // サムネイル用にリサイズ - cacheHeight: 200, - errorBuilder: (context, error, stackTrace) { - return Icon(Icons.error); - }, -) -``` - -**効果**: メモリ使用量を **80%削減** - ---- - -**2. サムネイル用の画像生成** - -```dart -// lib/services/thumbnail_service.dart (新規作成) -class ThumbnailService { - static Future generateThumbnail(String originalPath) async { - final thumbnailPath = await _getThumbnailPath(originalPath); - - // キャッシュチェック - if (await File(thumbnailPath).exists()) { - return thumbnailPath; - } - - // サムネイル生成(200x200px, JPEG 80%) - final bytes = await File(originalPath).readAsBytes(); - final image = img.decodeImage(bytes); - - final thumbnail = img.copyResize( - image!, - width: 200, - height: 200, - interpolation: img.Interpolation.cubic, - ); - - final compressedBytes = img.encodeJpg(thumbnail, quality: 80); - await File(thumbnailPath).writeAsBytes(compressedBytes); - - return thumbnailPath; - } -} -``` - -**効果**: -- 画像読み込み速度 **10倍高速化** -- ディスク容量 **90%削減**(2MB → 50KB) - ---- - -**3. 遅延読み込みの最適化** - -```dart -// lib/widgets/home/sake_list_item.dart -// FadeInImageを使用 -FadeInImage( - placeholder: MemoryImage(kTransparentImage), // 透明な画像 - image: FileImage(File(imagePath)), - fit: BoxFit.cover, - fadeInDuration: const Duration(milliseconds: 200), -) -``` - -**効果**: スムーズなフェードイン効果 - ---- - -#### ⏳ Phase 2(リリース後)- 優先度: Medium - -**4. 画像の事前キャッシュ** - -```dart -// アプリ起動時に次の10枚の画像を事前キャッシュ -Future precacheNextImages(BuildContext context, List items) async { - for (var i = 0; i < 10 && i < items.length; i++) { - final imagePath = items[i].displayData.imagePaths.first; - await precacheImage(FileImage(File(imagePath)), context); - } -} -``` - ---- - -## 🔍 問題2: 「あわせて飲みたい」機能の拡張 - -### 報告内容 -> 「あわせて飲みたい」の機能が、自分のカードからしか情報を取得しないままだけど、今後の拡張予定はありますか?どちらかというと未知の銘柄のおすすめの方がニーズがあると思います - -### 現状分析 - -**実装状況**: -```dart -// lib/screens/sake_detail_screen.dart:67-75 -final allSakeAsync = ref.watch(rawSakeListItemsProvider); -final allSake = allSakeAsync.asData?.value ?? []; - -final recommendations = SakeRecommendationService.getRecommendations( - target: _sake, - allItems: allSake, // ← 自分のカードのみ - limit: 10, -); -``` - -**問題点**: -- ✅ 自分のカードから類似の銘柄を推薦(実装済み) -- ❌ 未知の銘柄のおすすめ(未実装) -- ❌ 外部データベースとの連携なし - ---- - -### 拡張計画 - -#### ✅ Phase 2.0(リリース後1ヶ月)- 優先度: High - -**1. Synology NAS上の共有データベース構築** - -**アーキテクチャ**: -``` -┌─────────────────────────────────────┐ -│ Synology NAS (PostgreSQL) │ -│ │ -│ ┌──────────────────────────────┐ │ -│ │ 日本酒マスターDB │ │ -│ │ - 銘柄名 │ │ -│ │ - 蔵元 │ │ -│ │ - 都道府県 │ │ -│ │ - 五味チャート(平均値) │ │ -│ │ - タグ │ │ -│ │ - 人気度 │ │ -│ └──────────────────────────────┘ │ -│ ↓ │ -│ ┌──────────────────────────────┐ │ -│ │ ユーザー登録DB │ │ -│ │ - 誰がどの銘柄を登録したか │ │ -│ │ - 評価・レビュー │ │ -│ └──────────────────────────────┘ │ -└─────────────────────────────────────┘ - ↑ HTTPS (Tailscale) -┌─────────────────────────────────────┐ -│ Flutter App │ -│ - 自分のカード(Hive) │ -│ - 未知の銘柄(API経由) │ -└─────────────────────────────────────┘ -``` - -**実装工数**: 20時間 - ---- - -**2. レコメンドアルゴリズムの拡張** - -```dart -// lib/services/sake_recommendation_service.dart -class SakeRecommendationService { - /// ハイブリッドレコメンド - static Future> getHybridRecommendations({ - required SakeItem target, - required List ownItems, - int limit = 10, - }) async { - // 1. 自分のカードから類似銘柄を検索(既存) - final ownRecs = getRecommendations( - target: target, - allItems: ownItems, - limit: 5, - ); - - // 2. 外部DBから未知の銘柄を検索(新規) - final unknownRecs = await _fetchUnknownRecommendations( - target: target, - limit: 5, - ); - - // 3. 混合して返す - return [...ownRecs, ...unknownRecs]; - } - - static Future> _fetchUnknownRecommendations({ - required SakeItem target, - int limit = 5, - }) async { - // API経由で外部DBから取得 - final response = await http.post( - Uri.parse('https://posimai-nas.ts.net/api/recommendations'), - body: jsonEncode({ - 'target': { - 'prefecture': target.displayData.prefecture, - 'type': target.displayData.type, - 'tasteStats': target.hiddenSpecs.sakeTasteStats.toJson(), - }, - 'limit': limit, - }), - ); - - // レスポンスをパース - final data = jsonDecode(response.body); - return data['recommendations'] - .map((json) => Recommendation.fromJson(json)) - .toList(); - } -} -``` - -**効果**: -- ✅ 自分のカードから5件 + 未知の銘柄から5件 = 計10件 -- ✅ ユーザーの探索欲求を満たす -- ✅ アプリの価値向上 - -**実装工数**: 15時間 - ---- - -**3. 日本酒マスターDBのデータ収集** - -**データソース**: -1. **ユーザー登録データ**: - - 各ユーザーが登録した日本酒の平均五味チャート - - 匿名化されたレビューデータ - -2. **公開データ**: - - 日本酒造組合中央会のデータ - - 各酒蔵の公式Webサイト - -3. **AI自動収集**: - - Gemini APIで日本酒の情報を収集 - - 画像から五味チャートを推定 - -**データ量見積もり**: -- 日本の日本酒銘柄数: **約10,000銘柄** -- 各銘柄のデータサイズ: **1KB** -- 合計データ量: **10MB**(軽量) - -**実装工数**: 30時間 - ---- - -#### ⏳ Phase 3.0(リリース後3ヶ月)- 優先度: Medium - -**4. ソーシャル機能との統合** - -**機能**: -- 友達が登録した銘柄を推薦 -- 「この銘柄を登録している人はこれも登録しています」 -- コミュニティの人気ランキング - -**実装工数**: 40時間 - ---- - -## 📊 実装優先度まとめ - -| 項目 | 優先度 | 実装時期 | 工数 | 効果 | -|------|--------|----------|------|------| -| **問題1-1: 画像のメモリキャッシュ** | 🔴 Critical | Day 4 | 1時間 | 即座に改善 | -| **問題1-2: サムネイル生成** | 🟠 High | Day 4-5 | 3時間 | 10倍高速化 | -| **問題1-3: 遅延読み込み最適化** | 🟡 Medium | Day 5 | 1時間 | UX向上 | -| **問題2-1: 共有DB構築** | 🟠 High | Phase 2.0 | 20時間 | 価値向上 | -| **問題2-2: レコメンド拡張** | 🟠 High | Phase 2.0 | 15時間 | 探索欲求 | -| **問題2-3: データ収集** | 🟡 Medium | Phase 2.0 | 30時間 | DB充実 | -| **問題2-4: ソーシャル統合** | 🟢 Low | Phase 3.0 | 40時間 | 付加価値 | - ---- - -## 🎯 Day 4の実装計画(修正版) - -### 当初の計画 -- バッジ拡張(7個追加): 8時間 - -### 修正後の計画 -- **午前**: バッジ拡張(4時間) -- **午後**: 画像パフォーマンス改善(4時間) - - メモリキャッシュ実装(1時間) - - サムネイル生成(3時間) - -**合計**: 8時間(変更なし) - ---- - -## 💡 開発者への推奨アクション - -### 今すぐ確認(5分) -実機で以下を確認してください: - -1. **画像ファイルサイズ**: - ``` - スマホ → ファイルマネージャー → - Android/data/com.posimai.ponshu_room_lite/files/ → - 画像ファイルを確認 - ``` - - 1枚あたりのファイルサイズは? - - 何枚の画像がありますか? - -2. **メモリ使用量**: - - スマホの設定 → アプリ → ポンシュルーム → メモリ使用量 - -3. **カード数**: - - 現在何枚の日本酒を登録していますか? - -この情報があれば、より正確な対応策を提案できます。 - ---- - -**作成日**: 2026-01-22 -**作成者**: Cursor AI -**更新予定**: Day 4実装後 diff --git a/README_SYNOLOGY.md b/README_SYNOLOGY.md deleted file mode 100644 index 995ad8e..0000000 --- a/README_SYNOLOGY.md +++ /dev/null @@ -1,95 +0,0 @@ - -# 🐳 Synology NAS 構築ガイド (Phase 2A: AI Factory Setup) - -このドキュメントは、「Ponshu Room」開発環境をSynology NAS上に構築するための手順書です。 -**共同開発者(インフラ担当)向け** の内容となっています。 - -## 🎯 目標 (Phase 2A) -1. **Gitea** の構築(ローカルGitサーバー) -2. **PostgreSQL** の構築(Gitea用DB) -3. **MCP Server** の枠組み構築(AIエージェント接続用) - ---- - -## 🛠️ 事前準備 (Prerequisites) -以下のアプリがSynology NASにインストールされていることを確認してください。 -* **Container Manager** (旧 Docker) -* **Text Editor** (設定ファイル修正用) -* **File Station** - -## 📂 デプロイ手順 - -### Step 1: ファイルの配置 -File Stationを使用し、以下のディレクトリ構造を作成・配置してください。 -推奨パス: `/docker/ponshu_factory` - -``` -/docker/ponshu_factory/ -├── docker-compose.yml <-- リポジトリ内の tools/synology/docker-compose.yml をコピー -├── gitea/ <-- 自動作成されます(空でOK) -├── postgres/ <-- 自動作成されます(空でOK) -└── mcp/ <-- 自動作成されます(空でOK) -``` - -### Step 2: docker-compose.yml の確認 -`docker-compose.yml` を開き、以下の環境変数を必要に応じて変更してください。 -特にパスワードは強固なものに変更することを推奨します。 - -```yaml -services: - gitea: - environment: - - GITEA__database__PASSWD=your_secure_password # <--- 変更推奨 - - db: - environment: - - POSTGRES_PASSWORD=your_secure_password # <--- Giteaと同じにする -``` - -### Step 3: コンテナの起動 -Synology **Container Manager** を開きます。 -1. **プロジェクト** タブを選択 -2. **作成** ボタンをクリック -3. **プロジェクト名**: `ponshu_factory` -4. **パス**: `/docker/ponshu_factory` を選択 -5. **「docker-compose.ymlを使用してプロジェクトを作成」** を選択し、次へ -6. 設定を確認して **完了** をクリック - -### Step 4: 初期設定 (Gitea) -コンテナ起動後、ブラウザで以下にアクセスします。 -`http://[NASのIPアドレス]:3000` - -1. 初期設定画面が表示されます。 -2. **データベース設定**: - * タイプ: `PostgreSQL` - * ホスト: `db:5432` - * ユーザー: `gitea` - * パスワード: (Step 2で設定したもの) - * データベース名: `gitea` -3. **一般設定**: - * SSHサーバーのドメイン: `[NASのIPアドレス]` - * GiteaのベースURL: `http://[NASのIPアドレス]:3000/` -4. **管理者アカウントの設定**: - * 任意の管理者ユーザーを作成してください(例: `admin`)。 -5. **「Giteaをインストール」** をクリック。 - ---- - -## 🔒 外部アクセス設定 (Phase 2B準拠) -現在の構成はローカルネットワーク内のみアクセス可能です。 -外部(開発者のPC)から安全にアクセスするため、**Cloudflare Tunnel** の導入を推奨します。 - -### 推奨設定 (Cloudflare Tunnel) -1. Synology上で `cloudflared` コンテナを追加(または既存のTunnelを使用)。 -2. Tunnel設定で以下をマッピング: - * `git.ponshu.room` -> `http://gitea:3000` - ---- - -## 🤝 開発フローへの接続 -環境構築が完了したら、以下の情報を開発者(Ponshu Roomチーム)に共有してください。 - -1. **Gitea URL**: (例: `http://192.168.1.50:3000` またはTunnel URL) -2. **管理者アカウント情報**: (安全なチャネルで共有) - -これで「AI工場」の基盤は完成です!🚀 diff --git a/RECOMMENDATION_EXPANSION_PLAN.md b/RECOMMENDATION_EXPANSION_PLAN.md deleted file mode 100644 index 377ead4..0000000 --- a/RECOMMENDATION_EXPANSION_PLAN.md +++ /dev/null @@ -1,406 +0,0 @@ -# 「あわせて飲みたい」機能拡張計画 - -**作成日**: 2026-01-22 -**ステータス**: Phase 2.0(リリース後1ヶ月)で実装予定 - ---- - -## 📊 現状分析 - -### 実装済み機能 -✅ **ローカルレコメンドエンジン**: -- 五味チャートのコサイン類似度計算 -- 酒蔵・都道府県・タグによる類似度スコアリング -- スコア順にソート(最大10件) - -### 制限事項 -❌ **登録済みの銘柄のみ**: -- ユーザーが登録した日本酒からのみレコメンド -- 未知の銘柄(まだ登録していない日本酒)は推薦されない - ---- - -## 🎯 Phase 2.0: 未知の銘柄のレコメンド(拡張計画) - -### 1. Synology NAS上の共有データベース構築 - -**アーキテクチャ**: -``` -┌─────────────────────────────────────┐ -│ Synology NAS (PostgreSQL) │ -│ posimai-nas.ts.net (Tailscale) │ -│ │ -│ ┌──────────────────────────────┐ │ -│ │ 日本酒マスターDB │ │ -│ │ │ │ -│ │ - 銘柄名 │ │ -│ │ - 蔵元 │ │ -│ │ - 都道府県 │ │ -│ │ - 五味チャート(平均値) │ │ -│ │ - タグ │ │ -│ │ - 人気度(登録回数) │ │ -│ └──────────────────────────────┘ │ -│ ↓ │ -│ ┌──────────────────────────────┐ │ -│ │ ユーザー登録DB │ │ -│ │ │ │ -│ │ - user_id (匿名化) │ │ -│ │ - sake_id │ │ -│ │ - 評価・レビュー │ │ -│ │ - 五味チャート │ │ -│ └──────────────────────────────┘ │ -└─────────────────────────────────────┘ - ↑ HTTPS (Tailscale) -┌─────────────────────────────────────┐ -│ Flutter App │ -│ │ -│ - 自分のカード(Hive) │ -│ - 未知の銘柄(API経由) │ -└─────────────────────────────────────┘ -``` - ---- - -### 2. API設計 - -#### エンドポイント: `/api/v1/recommendations` - -**リクエスト**: -```json -{ - "target": { - "prefecture": "新潟県", - "type": "純米吟醸", - "taste_stats": { - "aroma": 4, - "sweetness": 3, - "acidity": 3, - "bitterness": 2, - "body": 4 - }, - "flavor_tags": ["フルーティー", "すっきり"] - }, - "exclude_ids": ["abc123", "def456"], // 既に登録済みの銘柄 - "limit": 5 -} -``` - -**レスポンス**: -```json -{ - "recommendations": [ - { - "id": "sake_12345", - "name": "八海山 純米吟醸", - "brewery": "八海醸造", - "prefecture": "新潟県", - "type": "純米吟醸", - "taste_stats": { - "aroma": 4, - "sweetness": 3, - "acidity": 3, - "bitterness": 2, - "body": 4 - }, - "flavor_tags": ["フルーティー", "すっきり"], - "similarity_score": 0.92, - "reason": "新潟県つながり / すっきり / 似た味わい", - "popularity": 1523 // 何人が登録したか - }, - // ... 最大5件 - ] -} -``` - ---- - -### 3. ハイブリッドレコメンド実装 - -```dart -// lib/services/sake_recommendation_service.dart -class SakeRecommendationService { - /// ハイブリッドレコメンド(既存 + 未知の銘柄) - static Future> getHybridRecommendations({ - required SakeItem target, - required List ownItems, - int limit = 10, - }) async { - final recommendations = []; - - // 1. 既存の銘柄から類似を検索(ローカル) - final ownRecs = getRecommendations( - target: target, - allItems: ownItems, - limit: 5, // 半分 - ); - recommendations.addAll(ownRecs); - - // 2. 未知の銘柄を検索(API経由) - try { - final unknownRecs = await _fetchUnknownRecommendations( - target: target, - excludeIds: ownItems.map((item) => item.id).toList(), - limit: 5, // 残り半分 - ); - recommendations.addAll(unknownRecs); - } catch (e) { - debugPrint('⚠️ Failed to fetch unknown recommendations: $e'); - // エラー時は既存のみ表示 - } - - // 3. スコア順にソート - recommendations.sort((a, b) => b.score.compareTo(a.score)); - - return recommendations.take(limit).toList(); - } - - /// 未知の銘柄をAPIから取得 - static Future> _fetchUnknownRecommendations({ - required SakeItem target, - required List excludeIds, - int limit = 5, - }) async { - final response = await http.post( - Uri.parse('https://posimai-nas.ts.net/api/v1/recommendations'), - headers: {'Content-Type': 'application/json'}, - body: jsonEncode({ - 'target': { - 'prefecture': target.displayData.prefecture, - 'type': target.displayData.type, - 'taste_stats': target.hiddenSpecs.tasteStats, - 'flavor_tags': target.hiddenSpecs.flavorTags, - }, - 'exclude_ids': excludeIds, - 'limit': limit, - }), - ); - - if (response.statusCode != 200) { - throw Exception('API Error: ${response.statusCode}'); - } - - final data = jsonDecode(response.body); - return (data['recommendations'] as List) - .map((json) => RecommendedSake.fromJson(json)) - .toList(); - } -} -``` - ---- - -### 4. UI改善 - -**Before** (現在): -```dart -Text('五味チャート・タグ・酒蔵・産地から自動選出\n※現在は登録済みの銘柄からおすすめを表示') -``` - -**After** (Phase 2.0): -```dart -Text('五味チャート・タグ・酒蔵・産地から自動選出\n💡 あなたにおすすめの未知の銘柄も表示中') -``` - -**未知の銘柄のカードにバッジ表示**: -```dart -// 未知の銘柄には「🆕 未登録」バッジを表示 -Stack( - children: [ - Image.network(unknownSake.imageUrl), - if (unknownSake.isUnknown) - Positioned( - top: 8, - right: 8, - child: Container( - padding: EdgeInsets.symmetric(horizontal: 8, vertical: 4), - decoration: BoxDecoration( - color: Colors.orange, - borderRadius: BorderRadius.circular(12), - ), - child: Text('🆕 未登録', style: TextStyle(color: Colors.white, fontSize: 10)), - ), - ), - ], -) -``` - ---- - -### 5. データ収集戦略 - -#### ステップ1: 既存ユーザーのデータを匿名化して収集 - -**実装**: -- アプリ初回起動時に「データ提供の同意」を取得 -- ユーザーが登録した日本酒のデータをNASに送信 -- `user_id`は匿名化(`device_info_plus`でデバイスIDをハッシュ化) - -```dart -// lib/services/data_contribution_service.dart -class DataContributionService { - static Future uploadSakeData() async { - final userProfile = ref.read(userProfileProvider); - - // ユーザーが同意していない場合は送信しない - if (!userProfile.hasConsentedToDataSharing) return; - - final box = Hive.box('sake_items'); - final allItems = box.values.toList(); - - final payload = allItems.map((item) => { - 'name': item.displayData.name, - 'brewery': item.displayData.brewery, - 'prefecture': item.displayData.prefecture, - 'type': item.displayData.type, - 'taste_stats': item.hiddenSpecs.tasteStats, - 'flavor_tags': item.hiddenSpecs.flavorTags, - 'smv': item.hiddenSpecs.sakeMeterValue, - }).toList(); - - await http.post( - Uri.parse('https://posimai-nas.ts.net/api/v1/data/upload'), - headers: {'Content-Type': 'application/json'}, - body: jsonEncode({ - 'user_id': await _getAnonymizedUserId(), - 'sake_items': payload, - }), - ); - } -} -``` - -#### ステップ2: 外部データソースから収集 - -1. **日本酒造組合中央会のデータ**: - - 公開されている酒蔵リスト - - 都道府県別の銘柄情報 - -2. **酒蔵の公式Webサイト**: - - 各銘柄の説明文 - - 五味チャートの情報(公開されている場合) - -3. **Gemini APIによる自動収集**: - - 日本酒の名前と蔵元から五味チャートを推定 - - コスト: 約5円/銘柄(画像なし、テキストのみ) - ---- - -### 6. データベーススキーマ - -**テーブル: `sake_master`** -```sql -CREATE TABLE sake_master ( - id UUID PRIMARY KEY DEFAULT gen_random_uuid(), - name VARCHAR(255) NOT NULL, - brewery VARCHAR(255) NOT NULL, - prefecture VARCHAR(50) NOT NULL, - type VARCHAR(50), -- 純米吟醸, 大吟醸, etc. - taste_aroma INT DEFAULT 3, - taste_sweetness INT DEFAULT 3, - taste_acidity INT DEFAULT 3, - taste_bitterness INT DEFAULT 3, - taste_body INT DEFAULT 3, - flavor_tags TEXT[], -- {フルーティー, すっきり} - smv DECIMAL(3,1), -- 日本酒度 - popularity INT DEFAULT 0, -- 何人が登録したか - image_url TEXT, - created_at TIMESTAMP DEFAULT NOW(), - updated_at TIMESTAMP DEFAULT NOW() -); - -CREATE INDEX idx_sake_prefecture ON sake_master(prefecture); -CREATE INDEX idx_sake_type ON sake_master(type); -CREATE INDEX idx_sake_popularity ON sake_master(popularity DESC); -``` - -**テーブル: `user_sake_data`** -```sql -CREATE TABLE user_sake_data ( - id UUID PRIMARY KEY DEFAULT gen_random_uuid(), - user_id_hash VARCHAR(64) NOT NULL, -- 匿名化されたユーザーID - sake_id UUID REFERENCES sake_master(id), - taste_aroma INT, - taste_sweetness INT, - taste_acidity INT, - taste_bitterness INT, - taste_body INT, - rating INT, -- 1-5 - created_at TIMESTAMP DEFAULT NOW() -); - -CREATE INDEX idx_user_sake_user ON user_sake_data(user_id_hash); -CREATE INDEX idx_user_sake_sake ON user_sake_data(sake_id); -``` - ---- - -### 7. 実装工数見積もり - -| 項目 | 工数 | 担当 | -|------|------|------| -| **PostgreSQLセットアップ** | 4時間 | Synology NAS | -| **APIサーバー構築(FastAPI)** | 8時間 | Python | -| **レコメンドアルゴリズム(サーバー側)** | 6時間 | Python | -| **Flutter側の統合** | 6時間 | Dart | -| **データ収集機能** | 4時間 | Dart + Python | -| **テスト・調整** | 4時間 | 総合 | -| **合計** | **32時間** | 約4日 | - ---- - -### 8. リリーススケジュール - -#### Phase 1.0(現在) -- ✅ ローカルレコメンドエンジン実装済み -- ✅ 既存の銘柄からの推薦 - -#### Phase 2.0(リリース後1ヶ月) -- 🔄 Synology NAS環境構築 -- 🔄 日本酒マスターDB構築(初期データ: 100銘柄) -- 🔄 ハイブリッドレコメンド実装 -- 🔄 データ収集機能実装 - -#### Phase 3.0(リリース後3ヶ月) -- 🔮 ソーシャル機能統合 -- 🔮 「この銘柄を登録している人はこれも登録しています」 -- 🔮 コミュニティの人気ランキング - ---- - -### 9. ユーザーへの説明 - -**現在の表示**: -``` -「あわせて飲みたい」 -五味チャート・タグ・酒蔵・産地から自動選出 -※現在は登録済みの銘柄からおすすめを表示 -``` - -**Phase 2.0での表示**: -``` -「あわせて飲みたい」 -五味チャート・タグ・酒蔵・産地から自動選出 -💡 あなたにおすすめの未知の銘柄も表示中 -``` - -**バッジ**: -- 🆕 未登録: まだ登録していない銘柄 -- 📚 登録済み: 自分のカードから推薦 - ---- - -## 📝 まとめ - -**現状**: 既に優秀なローカルレコメンドエンジンが実装済み - -**Phase 2.0での拡張**: 未知の銘柄を含めたハイブリッドレコメンドを実装予定 - -**実装工数**: 約32時間(4日) - -**リリース時期**: リリース後1ヶ月(2026年3月頃) - ---- - -**作成日**: 2026-01-22 -**作成者**: Cursor AI -**レビュー**: 開発者(必要に応じて修正してください) diff --git a/RELEASE_PLAN_10DAYS.md b/RELEASE_PLAN_10DAYS.md deleted file mode 100644 index 46b6208..0000000 --- a/RELEASE_PLAN_10DAYS.md +++ /dev/null @@ -1,278 +0,0 @@ -# 📅 10日間リリース計画(2026-01-22 → 2026-01-31) - -**目標**: v1.0.0 正式リリース(Google Play / App Store) -**残り日数**: 10日間 -**方針**: 過剰機能を削除し、コア機能の完成度を最優先 - ---- - -## 🔴 Critical(Day 1-3): リリース必須修正 - -### Day 1(1月22日)- UI修正 ✅ 3時間 -- [ ] `**` マークダウン記号の削除(30分) - - `lib/screens/guide_screen.dart` 24行目・51行目 -- [ ] Dark Mode完全対応(1.5時間) - - `lib/widgets/sake_radar_chart.dart` のハードコード色修正 -- [ ] MBTI診断の非表示(1時間) - - `lib/screens/soul_screen.dart` 180-187行をコメントアウト - - 削除ではなく非表示(Phase 3で再検討) - -**成果物**: UI表示バグゼロ - ---- - -### Day 2(1月23日)- セキュリティ & キャッシュ確認 ✅ 4時間 -- [ ] Git履歴からAPIキー削除確認(1時間) - - `git log --all -- lib/secrets.dart` で確認 - - 必要なら `git filter-branch` で削除 -- [ ] キャッシュ機能の実機テスト(2時間) - - 同じ写真を3回選択してキャッシュヒット確認 - - 開発者メニューでキャッシュサイズ確認 - - ログで「💰 API呼び出しをスキップ」を確認 -- [ ] エラーハンドリングの確認(1時間) - - ネットワークエラー時の挙動 - - API制限到達時の挙動(手動で20回解析して確認) - -**成果物**: セキュリティ完全、キャッシュ動作確認 - ---- - -### Day 3(1月24日)- 安定性テスト ✅ 4時間 -- [ ] 実機で全機能テスト(3時間) - - カメラ撮影 → AI解析 → 登録 - - ギャラリー選択 → AI解析 → 登録 - - お品書きPDF作成 - - QRコード生成・読取 - - Google Driveバックアップ・復元 -- [ ] バグ修正(1時間) - - テストで見つかった問題を即座に修正 - -**成果物**: コア機能100%動作確認 - ---- - -## 🟠 High(Day 4-6): UX改善 - -### Day 4-5(1月25-26日)- バッジ拡張 ✅ 8時間 - -**批判的判断: 23個は過剰 → 10個に削減** - -#### 追加バッジ(7個のみ) -```dart -// 地域バッジ(2個) -{'id': 'regional_kanto', 'name': '関東制覇', 'icon': '🗻', 'desc': '関東7都県の日本酒を登録'}, -{'id': 'regional_kansai', 'name': '関西制覇', 'icon': '🏯', 'desc': '関西6府県の日本酒を登録'}, - -// 活動バッジ(3個) -{'id': 'enthusiast', 'name': '愛好家', 'icon': '🎉', 'desc': '10本の日本酒を登録'}, -{'id': 'collector', 'name': 'コレクター', 'icon': '📚', 'desc': '50本の日本酒を登録'}, -{'id': 'legend', 'name': 'レジェンド', 'icon': '👑', 'desc': '100本の日本酒を登録'}, - -// 味覚バッジ(2個) -{'id': 'flavor_sweet', 'name': '甘口党', 'icon': '🍯', 'desc': '甘口(-5以下)を10本登録'}, -{'id': 'aroma_master', 'name': '香りの貴族', 'icon': '🌸', 'desc': '吟醸香4以上を10本登録'}, -``` - -**合計**: 3個(既存)+ 7個(追加)= **10個** - -**理由**: -- ✅ リリース後にユーザーがすぐ解除できる数 -- ✅ 実装・テストが2日で完了する -- ❌ 23個は過剰(実装8時間 + テスト4時間 = 12時間は10日間では厳しい) - -**実装箇所**: -1. `lib/services/gamification_service.dart` に条件追加(4時間) -2. `lib/widgets/gamification/badge_case.dart` にバッジ追加(2時間) -3. テスト・バグ修正(2時間) - -**成果物**: バッジ10個実装完了 - ---- - -### Day 6(1月27日)- オンボーディング改善 ✅ 3時間 -- [ ] Coach Mark持続問題の修正(2時間) - - チュートリアルが消えない問題を修正 -- [ ] 初回起動時の説明強化(1時間) - - 「カメラで撮影 → AI解析 → 記録」を3ステップで説明 - -**成果物**: 初回UXの向上 - ---- - -## 🟡 Medium(Day 7-8): 最終調整 - -### Day 7(1月28日)- ストア申請準備 ✅ 6時間 -- [ ] スクリーンショット作成(2時間) - - Google Play: 8枚(1024x500) - - App Store: 6.5インチ・5.5インチ各5枚 -- [ ] アプリ説明文作成(2時間) - - 日本語・英語 - - 機能リスト - - プライバシーポリシー -- [ ] アイコン・フィーチャーグラフィック作成(2時間) - -**成果物**: ストア申請素材完成 - ---- - -### Day 8(1月29日)- 最終テスト ✅ 6時間 -- [ ] 実機でフルテスト(4時間) - - 全機能を再度テスト - - バグ修正 -- [ ] パフォーマンステスト(2時間) - - 画面遷移速度 - - メモリ使用量 - - バッテリー消費 - -**成果物**: リリース準備完了 - ---- - -## 🟢 Low(Day 9-10): 申請 - -### Day 9(1月30日)- Google Play申請 ✅ 4時間 -- [ ] Google Play Console登録(1時間) -- [ ] APK/AABアップロード(1時間) -- [ ] ストアリスティング設定(1時間) -- [ ] 審査提出(1時間) - -**成果物**: Google Play申請完了 - ---- - -### Day 10(1月31日)- App Store申請 ✅ 4時間 -- [ ] App Store Connect登録(1時間) -- [ ] IPAアップロード(1時間) -- [ ] ストアリスティング設定(1時間) -- [ ] 審査提出(1時間) - -**成果物**: App Store申請完了 - ---- - -## ❌ 削除した過剰機能(Claude Codeの指摘を尊重) - -### 1. Ollama統合(削除理由) -- **Claude Codeの判断**: ⏸️ Phase 3に延期 -- **私の批判的判断**: ✅ **完全削除** -- **理由**: - - キャッシュで30-50%のAPI削減が既に実装済み - - Ollama応答時間: 10-30秒(Geminiは1-3秒)→ UX劣化 - - Synology NAS設定が必要(追加工数5時間) - - **10日間では実装・テスト不可能** - -### 2. Hive暗号化(削除理由) -- **Claude Codeの判断**: ⏸️ Phase 4に延期(過剰なセキュリティ) -- **私の批判的判断**: ✅ **完全削除** -- **理由**: - - 日本酒の銘柄・メモに機密情報は含まれない - - 暗号化のオーバーヘッドでパフォーマンス劣化 - - **v1.0では不要**(v2.0で検討) - -### 3. HTTPS化(Tailscale MagicDNS)(削除理由) -- **私の以前の提案**: 🔴 Week 1に実装すべき -- **批判的再評価**: ✅ **完全削除** -- **理由**: - - 現在はDirect Cloud(Gemini API直接接続)を使用 - - Gemini APIは既にHTTPS - - AI Proxyサーバーは未稼働(useProxy = false) - - **v1.0では不要**(将来的にProxyを使う場合のみ必要) - -### 4. 検索機能の強化(削除理由) -- **私の以前の提案**: 🟡 Week 3-4に実装 -- **批判的再評価**: ✅ **Phase 2(リリース後)に延期** -- **理由**: - - 現在の検索機能で基本的な用途は十分 - - 曖昧検索・フィルタ保存は Nice to have - - **10日間では優先度低い** - -### 5. オフラインモード(削除理由) -- **私の以前の提案**: 🟡 Week 3-4に実装 -- **批判的再評価**: ✅ **Phase 2(リリース後)に延期** -- **理由**: - - AI解析にはネットワークが必須 - - オフラインキューの実装・テストに5時間必要 - - **v1.0では不要** - -### 6. 統計・分析機能(削除理由) -- **私の以前の提案**: 🟡 Week 3-4に実装 -- **批判的再評価**: ✅ **Phase 2(リリース後)に延期** -- **理由**: - - 実装に10時間必要 - - **v1.0ではコア機能に集中** - -### 7. アプリサイズ最適化(削除理由) -- **Claude Codeの判断**: ⏸️ Phase 5に延期 -- **私の批判的判断**: ✅ **Phase 2(リリース後)に延期** -- **理由**: - - 現在のアプリサイズが不明(まず計測が必要) - - Flutterアプリは通常30-50MBで問題なし - - **過剰最適化** - ---- - -## 📊 工数見積もり - -| フェーズ | 日数 | 工数 | 内容 | -|---------|------|------|------| -| Day 1-3 | 3日 | 11時間 | Critical修正(UI、セキュリティ、テスト) | -| Day 4-6 | 3日 | 11時間 | UX改善(バッジ、オンボーディング) | -| Day 7-8 | 2日 | 12時間 | 最終調整(ストア準備、テスト) | -| Day 9-10 | 2日 | 8時間 | 申請(Google Play、App Store) | -| **合計** | **10日** | **42時間** | **平均4.2時間/日** | - -**現実的な計画**: ✅ 1日4-5時間の作業で達成可能 - ---- - -## 🎯 リリース判定基準(再定義) - -| 項目 | 必須 | 現状 | 備考 | -|------|------|------|------| -| AI解析の動作 | ✅ | ✅ 成功 | コア機能OK | -| キャッシュ機能 | ✅ | ⏳ 要確認 | Day 2で確認 | -| UI表示バグ | ✅ | ❌ | Day 1で修正 | -| Dark Mode対応 | ✅ | ⚠️ | Day 1で修正 | -| セキュリティ | ✅ | ⚠️ | Day 2で確認 | -| バッジ10個 | ⚠️ | ❌ | Day 4-5で実装 | -| オンボーディング | ⚠️ | ⚠️ | Day 6で改善 | -| Ollama統合 | ❌ | - | **削除** | -| Hive暗号化 | ❌ | - | **削除** | -| HTTPS化 | ❌ | - | **削除** | -| 検索強化 | ❌ | - | **Phase 2** | -| オフライン | ❌ | - | **Phase 2** | -| 統計機能 | ❌ | - | **Phase 2** | - -✅ = 必須 -⚠️ = 推奨 -❌ = 不要(v1.0) - ---- - -## 💡 批判的レビューの結論 - -### Claude Codeの判断は**概ね正しい** - -1. **Ollama統合の延期** → ✅ 正しい(さらに完全削除を推奨) -2. **Hive暗号化の延期** → ✅ 正しい(過剰なセキュリティ) -3. **HTTPS化の延期** → ✅ 正しい(現在Direct Cloud使用中) -4. **Immich統合の却下** → ✅ 正しい(サイズ肥大化) - -### 私(Cursor)の過剰提案を削除 - -1. **バッジ23個 → 10個に削減** → 10日間で現実的 -2. **検索強化・オフライン・統計** → Phase 2に延期 -3. **アプリサイズ最適化** → Phase 2に延期 - -### 残るCritical項目 - -1. ✅ UI修正(`**` 削除、Dark Mode) -2. ✅ MBTI非表示 -3. ✅ セキュリティ確認 -4. ✅ キャッシュ動作確認 -5. ✅ バッジ拡張(10個) -6. ✅ オンボーディング改善 - ---- - -**次のアクション**: Day 1(明日)の作業を開始しますか? diff --git a/REMAINING_TASKS_DAY5.md b/REMAINING_TASKS_DAY5.md deleted file mode 100644 index 4edbbf5..0000000 --- a/REMAINING_TASKS_DAY5.md +++ /dev/null @@ -1,160 +0,0 @@ -# Day 5残存タスク & UI/UX改善 - -**作成日**: 2026-01-22 -**ステータス**: 進行中 - ---- - -## 🚨 緊急対応完了 - -### 問題1: アプリサイズ増加(555MB → 563MB) - -**原因**: 一時ファイル(`*_compressed*`, `*_gallery*`)が削除されずに残っている - -**対応**: ✅ 完了 -- `ImageBatchCompressionService.cleanupTempFiles()` メソッドを追加 -- 開発者メニューに「一時ファイルをクリーンアップ」ボタンを追加 - -**ユーザーアクション**: -1. アプリを起動 -2. ソウル画面 → 右上の歯車 → 「🔬 開発者メニュー」 -3. 「🧹 一時ファイルをクリーンアップ」をタップ -4. ストレージ使用量を確認 - -**予想効果**: -- 563MB → 約11MB(552MB削減) - ---- - -## 📋 UI/UX残存タスク(Priority High) - -### 1. Coach Mark Persistence(コーチマーク消えない問題) - -**問題**: -- チュートリアルオーバーレイが正しく消えない -- または消えるべきでないときに消える - -**状況**: 要調査 - -**ファイル**: -- `lib/services/tutorial_service.dart` - -**対応**: 🔄 調査中 - ---- - -### 2. Image Compression Logic - -**問題**: -- ファイルサイズが大きい - -**状況**: ✅ Day 4-5で修正完了 -- カメラ撮影: 圧縮実装済み -- ギャラリー保存: 圧縮実装済み -- 削除時のクリーンアップ: 実装済み -- 一括圧縮: 実装済み - -**修正ファイル**: -- `lib/services/image_compression_service.dart` -- `lib/screens/camera_screen.dart` -- `lib/screens/sake_detail_screen.dart` -- `lib/services/image_batch_compression_service.dart` - ---- - -## ✨ UI/UX改善(Priority Medium) - -これらは品質向上のためのタスクですが、リリースのブロッカーではありません。 - -### 1. Tab Switching Animations -- **優先度**: Medium -- **工数**: 2時間 -- **実装時期**: Day 8-9(余裕があれば) - -### 2. Dialog Entrances -- **優先度**: Medium -- **工数**: 2時間 -- **実装時期**: Day 8-9(余裕があれば) - -### 3. Badge Unlock Celebration -- **優先度**: Medium -- **工数**: 3時間 -- **実装時期**: Day 8-9(余裕があれば) - ---- - -## 🔍 その他の確認事項 - -### Dark Mode Polish -- **優先度**: Low -- **状況**: 大部分は対応済み -- **残存**: 一部のダイアログ - -### Tablet/Foldable Layout -- **優先度**: Low -- **実装時期**: Phase 2.0以降 - ---- - -## 🎯 Day 5の優先度 - -### Critical(今日中に対応) -1. ✅ 一時ファイルクリーンアップ機能実装 -2. 🔄 ユーザーに一時ファイルクリーンアップを実行してもらう -3. 🔄 Coach Mark問題の調査 - -### High(Day 6で対応) -4. Coach Mark問題の修正(必要な場合) -5. 全機能の実機テスト - -### Medium(Day 8-9で対応) -6. Tab Switching Animations -7. Dialog Entrances -8. Badge Unlock Celebration - ---- - -## 📊 現在の実装状況まとめ - -### Day 4完了 -- ✅ 画像圧縮の修正(元画像削除) -- ✅ メモリキャッシュ確認 -- ✅ バッジ拡張確認 -- ✅ 「あわせて飲みたい」機能の改善 - -### Day 5完了 -- ✅ ギャラリー画像の圧縮実装 -- ✅ 削除時のストレージクリーンアップ -- ✅ 一括圧縮の安全性向上 -- ✅ 一時ファイルクリーンアップ機能追加 - -### Day 5残存 -- 🔄 Coach Mark問題の調査・修正 -- 🔄 全機能の実機テスト - ---- - -## 🚀 次のアクション - -### 1. 今すぐ実行 -- [ ] アプリをビルド(`flutter run`) -- [ ] 開発者メニュー → 「一時ファイルをクリーンアップ」 -- [ ] ストレージ使用量を確認(563MB → 約11MB?) - -### 2. Coach Mark問題の確認 -- [ ] ホーム画面のチュートリアルを表示 -- [ ] 正しく消えるか確認 -- [ ] 他の画面のチュートリアルも確認 - -### 3. 全機能テスト(Day 6) -- [ ] カメラ撮影 -- [ ] ギャラリー選択 -- [ ] 日本酒削除 -- [ ] オフラインモード -- [ ] エラーハンドリング - ---- - -**作成日**: 2026-01-22 -**作成者**: Cursor AI -**次ステップ**: 一時ファイルクリーンアップ実行 → Coach Mark調査 diff --git a/all_models.json b/all_models.json deleted file mode 100644 index f690edc313423140e2f9cd5c66867e0c988be477..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 46436 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 H debug_log.txt 2>&1 -``` - -### 3. APIキーの確認 - -```bash -# secrets.local.dart の内容を確認(APIキーは表示されません) -type lib\secrets.local.dart | findstr /i "geminiApiKey" -``` - -### 4. キャッシュのクリア - -```bash -# Hiveキャッシュをクリア(アプリ内の開発者メニューから) -# または、アプリをアンインストールして再インストール -flutter clean -flutter pub get -flutter run -``` - -## よくあるエラーと解決策 - -### Error 1: "API key not valid" -**原因**: APIキーが無効、または設定されていない -**解決策**: -1. Google AI Studioで新しいキーを発行 -2. lib/secrets.local.dart に正しく設定されているか確認 -3. `flutter run` を再実行 - -### Error 2: "Rate limit exceeded" -**原因**: 1日の制限(20回)に達した -**解決策**: -1. 開発者メニューでキャッシュサイズを確認 -2. 同じ画像を再度選択(キャッシュヒットするはず) -3. 明日まで待つ、またはOllama統合を実装 - -### Error 3: "Empty response from Gemini" -**原因**: Gemini APIが応答を返さなかった -**解決策**: -1. ネットワーク接続を確認 -2. 画像サイズを確認(圧縮後100KB前後が推奨) -3. 再試行ボタンをタップ - -### Error 4: "Lost connection to device" -**原因**: USBケーブルの物理的な切断、またはデバイスのスリープ -**解決策**: -1. USBケーブルを再接続 -2. デバイスのスリープ設定を「なし」に変更 -3. `flutter devices` でデバイスが認識されているか確認 -4. `flutter run` を再実行 - -## 問題が解決しない場合 - -以下の情報を提供してください: -1. `flutter doctor -v` の出力 -2. `flutter logs` の最後の100行 -3. エラーメッセージのスクリーンショット -4. 実行した操作の詳細(どの画面で、何をした時にエラーが出たか) diff --git a/ANTIGRAVITY_PROMPT.md b/docs/archive/ANTIGRAVITY_PROMPT.md similarity index 100% rename from ANTIGRAVITY_PROMPT.md rename to docs/archive/ANTIGRAVITY_PROMPT.md diff --git a/CommonSpecification.md b/docs/archive/CommonSpecification.md similarity index 100% rename from CommonSpecification.md rename to docs/archive/CommonSpecification.md diff --git a/FINAL_REQUIREMENTS.md b/docs/archive/FINAL_REQUIREMENTS.md similarity index 100% rename from FINAL_REQUIREMENTS.md rename to docs/archive/FINAL_REQUIREMENTS.md diff --git a/GEMINI_PRO_SETUP.md b/docs/archive/GEMINI_PRO_SETUP.md similarity index 100% rename from GEMINI_PRO_SETUP.md rename to docs/archive/GEMINI_PRO_SETUP.md diff --git a/docs/archive/IOS_DEVICE_TEST_CHECKLIST.md b/docs/archive/IOS_DEVICE_TEST_CHECKLIST.md new file mode 100644 index 0000000..45379fc --- /dev/null +++ b/docs/archive/IOS_DEVICE_TEST_CHECKLIST.md @@ -0,0 +1,243 @@ +# iOS実機テスト項目 v1.0.11+21 + +## テスト環境 +- [ ] iOS実機(iPhone 8以降推奨、iOS 12.0以上) +- [ ] TestFlight経由でインストール(または実機直接) + +--- + +## A. 権限確認 + +### A-1. カメラ権限 +- [ ] 初回起動時にカメラ権限ダイアログ表示 +- [ ] ダイアログテキスト: 「日本酒のラベルを撮影するためにカメラを使用します」 +- [ ] 許可後にカメラが正常動作 + +### A-2. 写真ライブラリ権限(選択) +- [ ] 初回ギャラリー選択時に権限ダイアログ表示 +- [ ] ダイアログテキスト: 「日本酒の写真をギャラリーから選択します」 +- [ ] 許可後にギャラリー選択可能 + +### A-3. 写真ライブラリ権限(保存) +- [ ] 初回写真保存時に権限ダイアログ表示(iOS 14以降) +- [ ] ダイアログテキスト: 「撮影した日本酒の写真をギャラリーに保存します」 +- [ ] 許可後にギャラリー保存可能 + +### A-4. 権限拒否時の挙動 +- [ ] カメラ拒否 → エラーメッセージ + 設定画面誘導 +- [ ] ギャラリー選択拒否 → エラーメッセージ +- [ ] ギャラリー保存拒否 → エラーメッセージ + +--- + +## B. カメラ機能 + +### B-1. 基本動作 +- [ ] カメラ起動が1秒以内 +- [ ] プレビュー表示が正常 +- [ ] シャッターボタン動作 +- [ ] 撮影音が鳴る(サイレントモード時は無音) + +### B-2. ピンチイン/アウト +- [ ] 拡大(ピンチアウト)が滑らか +- [ ] 縮小(ピンチイン)が滑らか +- [ ] 最大/最小倍率で制限される + +### B-3. フラッシュ +- [ ] フラッシュOn/Off切り替え可能 +- [ ] 暗所でフラッシュが発光 + +### B-4. 写真保存 +- [ ] 撮影後に写真ライブラリ保存 +- [ ] 写真アプリで確認可能 +- [ ] 位置情報タグなし(プライバシー) + +--- + +## C. ギャラリー選択 + +### C-1. 基本動作 +- [ ] ギャラリー画面が開く +- [ ] アルバム一覧表示 +- [ ] 写真選択可能 + +### C-2. 複数選択 +- [ ] 複数選択モードに切り替え可能 +- [ ] 最大5枚選択可能 +- [ ] 選択枚数が表示される + +### C-3. 画像形式対応 +- [ ] JPEG対応 +- [ ] PNG対応 +- [ ] HEIC対応(iOS標準) +- [ ] Live Photosは静止画として処理 + +--- + +## D. OCR機能(Gemini API) + +### D-1. 日本語認識 +- [ ] 日本酒ラベルの酒名認識 +- [ ] 酒蔵名認識 +- [ ] 都道府県認識 +- [ ] アルコール度数認識 + +### D-2. ネットワーク +- [ ] Wi-Fi接続時に動作 +- [ ] LTE/5G接続時に動作 +- [ ] オフライン時にエラーメッセージ + +### D-3. レスポンス時間 +- [ ] 解析完了まで10秒以内(通常) +- [ ] 30秒以内(最悪ケース) + +--- + +## E. Google Sign-In + +### E-1. サインイン +- [ ] サインインボタン押下 +- [ ] Googleアカウント選択画面表示 +- [ ] アカウント選択 → サインイン成功 +- [ ] プロフィール画像表示 + +### E-2. サインアウト +- [ ] サインアウトボタン押下 +- [ ] 確認ダイアログ表示 +- [ ] サインアウト成功 + +### E-3. エラーハンドリング +- [ ] ネットワークエラー時の挙動 +- [ ] キャンセル時の挙動 + +--- + +## F. Google Drive同期 + +### F-1. バックアップ作成 +- [ ] バックアップボタン押下 +- [ ] プログレス表示 +- [ ] 完了メッセージ表示 +- [ ] Google Driveで確認可能 + +### F-2. 復元 +- [ ] 復元ボタン押下 +- [ ] バックアップファイル選択 +- [ ] プログレス表示 +- [ ] 復元成功 → データ確認 + +### F-3. エラーハンドリング +- [ ] ネットワークエラー時の挙動 +- [ ] 容量不足時の挙動 + +--- + +## G. パフォーマンス + +### G-1. スクロール +- [ ] ホーム画面のスクロールが滑らか(60fps維持) +- [ ] 画像50枚登録後もスクロール快適 +- [ ] グリッド/リスト切り替えが瞬時 + +### G-2. 起動時間 +- [ ] コールドスタート: 3秒以内 +- [ ] ウォームスタート: 1秒以内 + +### G-3. メモリ使用量 +- [ ] 起動直後: 150MB以下 +- [ ] 画像50枚登録後: 300MB以下 +- [ ] メモリリークがない(長時間使用テスト) + +### G-4. バッテリー消費 +- [ ] バックグラウンド時: 最小限 +- [ ] 連続使用30分: 10%以下 + +--- + +## H. iOS特有のUI + +### H-1. SafeArea対応 +- [ ] ノッチ(iPhone X以降)で切れない +- [ ] ホームインジケータ(iPhone X以降)に被らない +- [ ] ステータスバーに被らない + +### H-2. Dynamic Type対応 +- [ ] 設定 → 画面表示と明るさ → テキストサイズ +- [ ] 最小サイズで表示崩れなし +- [ ] 最大サイズで表示崩れなし + +### H-3. ダークモード対応 +- [ ] システムダークモードで自動切り替え +- [ ] アプリ内ダークモード切り替え +- [ ] 全画面で色が適切 + +### H-4. 横画面対応 +- [ ] 横画面で表示崩れなし(任意対応) +- [ ] または縦画面固定が適切 + +--- + +## I. エラーハンドリング(iOS固有) + +### I-1. ディスク容量不足 +- [ ] 写真保存失敗時のエラーメッセージ +- [ ] バックアップ失敗時のエラーメッセージ + +### I-2. 電話着信 +- [ ] カメラ使用中に電話着信 +- [ ] 通話終了後に復帰 +- [ ] データ損失なし + +### I-3. アプリ強制終了 +- [ ] ホームボタン二度押し → 上スワイプで終了 +- [ ] 再起動後に正常復帰 +- [ ] Draft保存データが残る + +### I-4. iOS更新 +- [ ] iOS 16.xで動作確認 +- [ ] iOS 17.xで動作確認(新機能対応) + +--- + +## J. TestFlight配布テスト + +### J-1. インストール +- [ ] TestFlight招待メール受信 +- [ ] TestFlightアプリでインストール +- [ ] 初回起動成功 + +### J-2. 更新 +- [ ] TestFlightで新バージョン通知 +- [ ] 更新ボタン押下 +- [ ] 更新成功 +- [ ] データ引き継ぎ確認 + +### J-3. フィードバック +- [ ] TestFlightフィードバック機能使用 +- [ ] スクリーンショット送信 +- [ ] クラッシュレポート確認 + +--- + +## K. App Store審査対策(将来) + +### K-1. 審査ガイドライン準拠 +- [ ] プライバシーポリシー公開 +- [ ] 利用規約公開 +- [ ] サポートURL設定 +- [ ] レビューノート作成(日本語・英語) + +### K-2. スクリーンショット準備 +- [ ] 5.5inch(iPhone 8 Plus): 8枚 +- [ ] 6.5inch(iPhone 14 Pro Max): 8枚 +- [ ] App Previewビデオ作成(任意) + +--- + +## テスト実施記録 + +**テスト実施者**: ___________ +**テスト日時**: ___________ +**テスト端末**: ___________ (例: iPhone 13 Pro, iOS 16.5) +**結果**: Pass / Fail +**コメント**: ___________ diff --git a/docs/archive/IOS_DISTRIBUTION_STRATEGY.md b/docs/archive/IOS_DISTRIBUTION_STRATEGY.md new file mode 100644 index 0000000..11ac608 --- /dev/null +++ b/docs/archive/IOS_DISTRIBUTION_STRATEGY.md @@ -0,0 +1,412 @@ +# iOS配布戦略 v1.0.11+21 + +## 全体スケジュール + +``` +Week 1-2: 内部テスト(開発者のみ) +Week 3-4: クローズドβ(友人5-10名) +Week 5-8: オープンβ(一般50-100名) +Week 9-12: App Store審査・正式リリース +``` + +--- + +## Phase 1: 内部テスト(1-2週間) + +### 目的 +- Critical問題の洗い出し +- iOS固有バグの検出 +- パフォーマンス測定 + +### 対象 +- 開発者本人 +- 社内テスター(1-2名) + +### 配布方法 +- Xcode直接インストール +- または TestFlight(Internal Testing) + +### テスト項目 +- `IOS_DEVICE_TEST_CHECKLIST.md` 全項目 +- クラッシュレート測定(目標: <0.1%) +- メモリリーク検出 + +### 完了基準 +- [ ] Critical問題ゼロ +- [ ] High問題対応済み or 対応計画あり +- [ ] クラッシュレート < 0.1% + +--- + +## Phase 2: クローズドβ(2週間) + +### 目的 +- 実使用環境でのバグ検出 +- UXフィードバック収集 +- 多様なデバイステスト + +### 対象 +- 友人5-10名 +- 信頼できるテスター + +### 配布方法 +- TestFlight(External Testing) +- メールアドレス指定で招待 + +### TestFlight設定 + +``` +App Name: Ponshu Room Lite (Beta) + +Beta App Description: +日本酒コレクション管理アプリのβ版です。 +OCRでラベル認識、MBTI風診断、Google Drive同期が可能です。 + +What to Test: +- Phase D6フィルタリング(Personal/Business Mode) +- カメラ/ギャラリー機能 +- Google Sign-In/Drive同期 +- パフォーマンス(スクロール、起動時間) + +Feedback Contact: support@example.com +``` + +### テスト項目 +- 基本機能テスト(全項目) +- 実使用シナリオテスト +- ネットワーク環境テスト(Wi-Fi, LTE, 5G) + +### フィードバック収集 + +``` +Google Forms作成: +- 使いやすさ(1-5点) +- パフォーマンス(1-5点) +- バグ報告(自由記述) +- 要望(自由記述) +``` + +### 完了基準 +- [ ] フィードバック回収率80%以上 +- [ ] Critical問題ゼロ +- [ ] ユーザー満足度4.0/5.0以上 + +--- + +## Phase 3: オープンβ(4週間) + +### 目的 +- 大規模テスト +- App Store審査準備 +- マーケティングデータ収集 + +### 対象 +- 一般ユーザー50-100名 +- 日本酒愛好家コミュニティ + +### 配布方法 +- TestFlight(Public Link) + +``` +Public Link作成: +https://testflight.apple.com/join/xxxxxxxx + +募集: +- Twitter, Instagram投稿 +- 日本酒コミュニティ(Reddit, Discord等) +- プレスリリース(PR TIMES等) +``` + +### テスト項目 +- 全機能テスト +- 多様なデバイステスト(iPhone 8〜iPhone 15 Pro Max) +- 大量データテスト(100件以上登録) + +### 監視指標 + +``` +Firebase Analytics: +- DAU/MAU +- 平均セッション時間 +- クラッシュフリーユーザー率(目標: 99.9%) +- 主要機能の使用率 +``` + +### 完了基準 +- [ ] テスター数50名以上 +- [ ] クラッシュフリーユーザー率 > 99.9% +- [ ] App Store評価準備完了 + +--- + +## Phase 4: App Store審査(2-4週間) + +### 事前準備 + +#### 4-1. App Store Connect設定 + +``` +アプリ情報: +- アプリ名: Ponshu Room Lite +- サブタイトル: 日本酒コレクション管理 +- カテゴリ: フード&ドリンク +- 価格: 無料(アプリ内課金なし) + +プライバシー: +- カメラ使用: ラベル撮影 +- 写真ライブラリ: 画像選択・保存 +- Google Sign-In: バックアップ・復元 +- データ収集: 匿名の使用統計(Firebase Analytics) +``` + +#### 4-2. スクリーンショット作成 + +``` +必須サイズ: +- 5.5inch(iPhone 8 Plus): 8枚 +- 6.5inch(iPhone 14 Pro Max): 8枚 + +推奨内容: +1. ホーム画面(商品一覧) +2. カメラ撮影画面 +3. 詳細画面(五味チャート) +4. MBTI診断結果 +5. メニュー作成画面 +6. テーマ切替 +7. Google Drive同期 +8. 統計画面 +``` + +#### 4-3. App Previewビデオ作成(任意) + +``` +長さ: 15-30秒 + +内容: +1. カメラで撮影(5秒) +2. AI解析→詳細表示(5秒) +3. MBTI診断(5秒) +4. メニュー作成(5秒) +5. テーマ切替(5秒) +``` + +#### 4-4. プライバシーポリシー公開 + +``` +URL: https://example.com/privacy-policy + +内容: +- 収集データ: カメラ画像、Googleアカウント情報 +- 利用目的: OCR解析、バックアップ +- 第三者提供: Google(Gemini API、Drive API) +- データ保存: 端末ローカル、Google Drive(ユーザー選択) +``` + +#### 4-5. レビューノート作成 + +``` +日本語: +このアプリは日本酒愛好家向けのコレクション管理アプリです。 + +テスト手順: +1. アプリ起動 +2. カメラボタン → ギャラリーから選択 +3. サンプル画像選択(日本酒ラベル) +4. AI解析完了を待つ +5. 詳細画面で五味チャートを確認 + +Google Sign-In: +テストアカウント: test@example.com / password123 + +英語: +This app is a sake collection manager for sake enthusiasts. + +Test Steps: +1. Launch app +2. Tap camera button → Select from gallery +3. Choose sample image (sake label) +4. Wait for AI analysis +5. View taste chart on detail screen + +Google Sign-In: +Test Account: test@example.com / password123 +``` + +### 審査提出 + +```bash +# Archive作成 +flutter build ios --release + +# Xcodeで +1. Product → Archive +2. Distribute App → App Store Connect +3. Upload + +# App Store Connectで +1. バージョン情報入力 +2. スクリーンショット追加 +3. レビューに提出 +``` + +### 審査期間 + +- 平均: 1-2週間 +- 最大: 4週間 + +### リジェクト対策 + +``` +よくあるリジェクト理由: +1. プライバシーポリシー不足 → 事前に公開 +2. 機能説明不足 → レビューノート充実 +3. クラッシュ → TestFlightで事前検証 +4. パフォーマンス問題 → 最適化 + +対応: +- リジェクト理由確認 +- 修正後24時間以内に再提出 +``` + +--- + +## Phase 5: 正式リリース + +### リリース日選定 + +``` +推奨: +- 木曜または金曜リリース(週末サポート体制) +- 避けるべき: 月曜(週初め忙しい)、祝日前後 + +理想的なタイミング: +- 日本酒イベント前(酒フェス等) +- 季節の変わり目(新酒シーズン) +``` + +### リリース後の監視(最初の1週間) + +#### 5-1. クラッシュ監視 + +``` +Firebase Crashlytics: +- クラッシュフリーユーザー率(目標: 99.9%) +- 致命的クラッシュの即座対応 +- 1時間ごとにチェック(初日) +- 3時間ごとにチェック(2-7日目) +``` + +#### 5-2. パフォーマンス監視 + +``` +Firebase Performance: +- アプリ起動時間 +- API応答時間 +- 画面遷移時間 +``` + +#### 5-3. ユーザーフィードバック対応 + +``` +App Store レビュー: +- 1日1回チェック +- 平均評価4.0以上を維持 +- ネガティブレビューには24時間以内に返信 + +サポート問い合わせ: +- 24時間以内に返信 +- よくある質問をFAQに追加 +``` + +### リリース後の更新戦略 + +#### マイナーアップデート(2週間ごと) + +``` +v1.0.12, v1.0.13... +- バグ修正 +- 小規模改善 +- パフォーマンス最適化 +``` + +#### メジャーアップデート(3ヶ月ごと) + +``` +v1.1.0, v1.2.0... +- 新機能追加 +- UI/UX大幅改善 +- ユーザー要望対応 +``` + +--- + +## リリース成功基準 + +### 初日 +- [ ] クラッシュフリーユーザー率 > 99% +- [ ] App Store評価 > 3.5 +- [ ] ダウンロード数 > 50 + +### 1週間 +- [ ] クラッシュフリーユーザー率 > 99.5% +- [ ] App Store評価 > 4.0 +- [ ] DAU/MAU > 30% +- [ ] ユーザー継続率(Day 7)> 20% + +### 1ヶ月 +- [ ] クラッシュフリーユーザー率 > 99.9% +- [ ] App Store評価 > 4.2 +- [ ] 累計ダウンロード数 > 500 +- [ ] 月間アクティブユーザー > 200 + +--- + +## 緊急時の対応フロー + +### Critical Bug発見時 + +``` +1. 影響範囲確認(5分以内) +2. 緊急修正版開発開始(1時間以内) +3. TestFlight緊急配布(2時間以内) +4. 検証完了(4時間以内) +5. App Store緊急申請(6時間以内) + - 緊急審査リクエスト(Expedited Review) + +審査完了までの暫定対応: +- App Storeレビュー欄で告知 +- サポートページで回避策公開 +- SNSで状況報告 +``` + +--- + +## まとめ + +### タイムライン概要 + +``` +Week 1-2: 内部テスト +Week 3-4: クローズドβ +Week 5-8: オープンβ +Week 9-12: App Store審査 +Week 13: 正式リリース +``` + +### 総コスト見積もり + +``` +Apple Developer Program: 年間99ドル(約15,000円) +Firebase(無料枠内想定): 0円 +TestFlight(無料): 0円 + +総額: 約15,000円/年 +``` + +### 次のアクション + +1. [ ] Apple Developer Program登録 +2. [ ] Firebase iOS設定完了 +3. [ ] 内部テスト開始 +4. [ ] TestFlight設定 +5. [ ] フィードバックフォーム作成 diff --git a/docs/archive/IOS_SETUP_GUIDE.md b/docs/archive/IOS_SETUP_GUIDE.md new file mode 100644 index 0000000..eb1d2dd --- /dev/null +++ b/docs/archive/IOS_SETUP_GUIDE.md @@ -0,0 +1,351 @@ +# iOS開発環境セットアップ完全手順 + +## 📋 前提条件チェックリスト + +### ハードウェア +- [ ] 2017 MacBook Air以降 +- [ ] メモリ8GB以上 +- [ ] ストレージ空き容量30GB以上 + +### ソフトウェア +- [ ] macOS Big Sur 11.x(2017 MacBook Air最大) +- [ ] Xcode 13.2.1(Big Sur最大対応) +- [ ] Flutter SDK 3.10.1以降 + +--- + +## Step 1: Flutter iOS toolchain確認(5分) + +```bash +flutter doctor -v + +# 期待される出力 +[✓] Flutter (Channel stable, 3.10.1, on macOS Big Sur 11.x) +[✓] Xcode - develop for iOS and macOS (Xcode 13.2.1) + • Xcode at /Applications/Xcode.app/Contents/Developer + • Build 13C100 + • CocoaPods version 1.11.x + +[✓] Android Studio (version 2023.x) +[!] iOS deploy (iOS 12.0 or higher required) +``` + +**トラブルシュート**: + +```bash +# Xcodeが見つからない +sudo xcode-select --switch /Applications/Xcode.app/Contents/Developer + +# Xcode license未承認 +sudo xcodebuild -license accept + +# CocoaPods未インストール +sudo gem install cocoapods +``` + +--- + +## Step 2: iOSプロジェクトファイル生成(10分) + +```bash +cd ~/posimai-project/ponshu_room_lite + +# 既存ファイルを保持しつつiOSファイル生成 +flutter create . + +# 確認 +ls -la ios/ +# 期待: Runner.xcodeproj, Runner.xcworkspace, Podfile等が生成される +``` + +**注意**: 既存のAndroidファイルは影響を受けません + +--- + +## Step 3: Podfile確認と設定(15分) + +### 3-1. Podfile内容確認 + +```bash +cat ios/Podfile +``` + +### 3-2. プラットフォーム設定 + +```ruby +# ios/Podfile(自動生成後の確認) +platform :ios, '12.0' # iOS 12.0以上対応(2017デバイス対応) + +# CocoaPods 1.11.x用設定 +post_install do |installer| + installer.pods_project.targets.each do |target| + flutter_additional_ios_build_settings(target) + target.build_configurations.each do |config| + config.build_settings['IPHONEOS_DEPLOYMENT_TARGET'] = '12.0' + end + end +end +``` + +### 3-3. 依存関係インストール + +```bash +cd ios +pod install # 初回は5-10分かかる + +# 成功メッセージ +# Pod installation complete! There are X dependencies from the Podfile... + +cd .. +``` + +**トラブルシュート**: + +```bash +# エラー: CocoaPods could not find compatible versions +pod repo update # Podリポジトリ更新(10-15分) +pod install + +# エラー: ffi gem missing +sudo gem install ffi + +# 2017 MacBook Airでメモリ不足 +# 他のアプリケーションを終了してから再試行 +``` + +--- + +## Step 4: Firebase iOS設定(30分) + +### 4-1. Firebase Console設定 + +1. https://console.firebase.google.com/ +2. プロジェクト「ponshu-room」選択 +3. **プロジェクトの設定** → **全般** +4. **アプリを追加** → **iOS** + +### 4-2. Bundle ID設定 + +``` +Bundle ID: com.posimai.ponshu_room_lite +Apple App Nickname: Ponshu Room Lite +App Store ID: (後で設定) +``` + +### 4-3. GoogleService-Info.plist取得 + +1. Firebase Consoleから`GoogleService-Info.plist`をダウンロード +2. ファイル配置: +```bash +mv ~/Downloads/GoogleService-Info.plist ios/Runner/ +``` + +3. 確認: +```bash +cat ios/Runner/GoogleService-Info.plist | grep BUNDLE_ID +# 期待: com.posimai.ponshu_room_lite +``` + + +### 4-4. REVERSED_CLIENT_ID取得 + +```bash +cat ios/Runner/GoogleService-Info.plist | grep REVERSED_CLIENT_ID +# 出力例: com.googleusercontent.apps.123456789-xxxx +``` + +この値を次のステップで使用 + +--- + +## Step 5: Info.plist完全設定(20分) + +```bash +open ios/Runner/Info.plist +# Xcode または テキストエディタで開く +``` + +### 5-1. 権限記述(既存確認 + 追加) + +```xml + +NSCameraUsageDescription +日本酒のラベルを撮影するためにカメラを使用します + +NSPhotoLibraryAddUsageDescription +撮影した日本酒の写真をギャラリーに保存します + + +NSPhotoLibraryUsageDescription +日本酒の写真をギャラリーから選択します +``` + +### 5-2. Google Sign-In設定 + +```xml +CFBundleURLTypes + + + CFBundleTypeRole + Editor + CFBundleURLSchemes + + + com.googleusercontent.apps.123456789-xxxx + + + +``` + +### 5-3. 設定確認 + +```bash +# NSPhotoLibraryUsageDescriptionが存在するか確認 +grep -A 1 "NSPhotoLibraryUsageDescription" ios/Runner/Info.plist + +# CFBundleURLSchemesが設定されているか確認 +grep -A 5 "CFBundleURLTypes" ios/Runner/Info.plist +``` + +--- + +## Step 6: Xcodeでプロジェクトを開く(10分) + +```bash +# 重要: .xcodeproj ではなく .xcworkspace を開く +open ios/Runner.xcworkspace +``` + +### 6-1. Signing設定 + +1. Xcode左ペイン → **Runner** 選択 +2. **Signing & Capabilities** タブ +3. **Team**: Apple Developer Accountを選択 + - 未登録の場合: Add Account → Apple IDでサインイン + +4. **Bundle Identifier**: `com.posimai.ponshu_room_lite`確認 +5. **Automatically manage signing**: チェック + +### 6-2. Deployment Target確認 + +1. **General** タブ +2. **Deployment Info** → **iOS 12.0** 確認 + +--- + +## Step 7: ビルドテスト(15分) + +### 7-1. シミュレータ用ビルド + +```bash +flutter build ios --debug + +# 成功メッセージ +# Built build/ios/Release-iphoneos/Runner.app +``` + +### 7-2. Xcodeでシミュレータ起動 + +1. Xcode上部 → デバイス選択 → **iPhone 12 Pro(iOS 15.x)** +2. ▶ ボタンクリック +3. アプリが起動するか確認 + +--- + +## Step 8: 実機ビルド(Apple Developer Program必須) + +### 8-1. Apple Developer Program登録(任意) + +**無料版** (Personal Team): + +- 有効期限: 7日間 +- 実機1台まで +- App Store配布不可 + +**有料版** (年間99ドル): + +- 有効期限: 1年間 +- 実機100台まで +- App Store配布可能 +- TestFlight配布可能 + +### 8-2. 実機接続 + +```bash +# 実機をUSB接続 +flutter devices + +# 出力例 +# iPhone 13 Pro (mobile) • 00008030-XXXX • ios • iOS 16.1 +``` + +### 8-3. 実機ビルド + +```bash +flutter build ios --release +flutter install -d + +# または Xcodeから +# デバイス選択 → 実機選択 → ▶ ボタン +``` + +--- + +## Step 9: トラブルシューティング + +### エラー1: Signing for "Runner" requires a development team + +**原因**: Teamが未設定 + +**解決**: Xcode → Signing & Capabilities → Team選択 + +### エラー2: The operation couldn't be completed + +**原因**: デバイスがロックされている + +**解決**: iPhoneロック解除 → 「このコンピュータを信頼」 + +### エラー3: Module 'xxx' not found + +**原因**: CocoaPods依存関係エラー + +**解決**: + +```bash +cd ios +rm -rf Pods Podfile.lock +pod install +cd .. +``` + +### エラー4: Xcode build failed - Could not find a bundle identifier + +**原因**: GoogleService-Info.plist未配置 + +**解決**: Step 4-3を再確認 + +### エラー5: 2017 MacBook Airでメモリ不足 + +**対策**: + +- 他のアプリケーションを終了 +- Xcode再起動 +- ターミナルで`sudo purge`(メモリ解放) +- Virtual Memory確保(10GB以上) + +--- + +## 完了チェックリスト + +- [ ] flutter doctor でiOS toolchain ✓ +- [ ] CocoaPods依存関係インストール完了 +- [ ] Firebase iOS設定完了 +- [ ] Info.plist完全設定 +- [ ] Xcodeでビルド成功(シミュレータ) +- [ ] 実機ビルド成功(任意) + +--- + +## 次のステップ + +`IOS_DEVICE_TEST_CHECKLIST.md`に従って実機テストを実施 diff --git a/START_HERE.md b/docs/archive/START_HERE.md similarity index 100% rename from START_HERE.md rename to docs/archive/START_HERE.md diff --git a/START_HERE_FINAL.md b/docs/archive/START_HERE_FINAL.md similarity index 100% rename from START_HERE_FINAL.md rename to docs/archive/START_HERE_FINAL.md diff --git a/UI_UX_DECISION_GUIDE.md b/docs/archive/UI_UX_DECISION_GUIDE.md similarity index 100% rename from UI_UX_DECISION_GUIDE.md rename to docs/archive/UI_UX_DECISION_GUIDE.md diff --git a/keytool_output.txt b/keytool_output.txt deleted file mode 100644 index 921d70a53ab6b7300dd37f31b0c74c429f67679d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1052 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 H