本資料はPCでの閲覧を推奨しています。
PDF生成中...
talkSessionInvitations 新設(top-level)+ teachers に リンク発行用デフォルト設定フィールド群追加| # | 分岐 | 決定内容 |
|---|---|---|
| 1 | 目的 | 受験招待リンク(先生がテストを作成 → 生徒がURL経由で受験) |
| 2 | 粒度 | 1リンク = クラス全員共有(Google Forms型) |
| 3 | 生徒認証 | 匿名 + 名前入力 + URLトークン |
| 4 | 受験画面 | tocme_assistant 内の未認証ルート(既存資産を再利用) |
| 5 | DBスキーマ | talkSessionInvitations 新設(top-level) + schools/{id}/talkSessions.invitationId 追加 |
| 6 | バックエンド認証 | Firebase 匿名認証 + invitationId検証(共用アカウント案は棄却) |
| 7 | URL形式 | /take/:code + nanoid 8桁英数字 |
| 8 | 先生UI配置 | サイドナビに「リンク発行」ブランチ新設 + 専用設定画面 + 設定変更時 teachers 自動保存 |
| 9 | 評価表示 | 既存と同じ評価画面を生徒にも表示 |
| 10 | 重複受験 | 無制限・全履歴保存 |
| 11 | 期限 | 固定1時間 |
| 12 | 期限切れ挙動 | 受験開始済みは完走可、新規アクセスのみ拒否 |
| 13 | 入力フォーム | 名前のみ自由入力(1〜30文字) |
| 14 | 発行後UI | シンプルなURL+コピーボタンのみ(QR/コード/カウントダウンなし) |
| 15 | abuse対策 | invitation ごとの累計受験者数上限(デフォルト100件 / 要相談) |
| 16 | 先生デフォルト設定 | teachersに永続化。数値系(会話回数/速度/厳しさ/学年)は既存speakingTest設定と共用、選択系(テーマ/文法/単語/AIキャラ)はリンク発行画面専用フィールド invitationDefault* を新設 |
| 17 | 生徒側の設定変更 | リンク発行時のsnapshot値で初期化、生徒は受験前に自由に変更可。その受験回のみ反映(teachers無影響) |
/take/8h3kn2qxschools/{schoolId}/talkSessions/{talkSessionId}/talkLogs/{...}。speakingTestとsmallTalkは同一talkSessionsを共有(kindフィールドで識別: 1=speakingTest)。studentNamesはList(ペアモード対応)。
| フィールド | 説明 |
|---|---|
invitationId | Firestore auto-ID |
code | nanoid 8桁(URL用、unique) |
schoolId / teacherId | 発行者の学校・先生 |
kind | 1=speakingTest(将来smallTalk拡張余地) |
aiCharacterId 他 | テスト設定(既存talkSessionと同形式) |
expiresAt | createdAt + 1時間(固定) |
maxUsageCount / usedCount | 上限100 / 受験開始時atomic increment |
createdAt | 作成日時 |
schoolIdを知らずにcodeだけで照合できるよう必須| フィールド | 説明 |
|---|---|
talkSessionId | 既存 |
schoolId / teacherId | 既存(invitationから両方コピー) |
studentNames (List) | 既存。リンク経由は[名前]1件で保存 |
kind / aiCharacterId / grammarIds 他 | 既存(invitation値をコピー) |
score / goodPoint / advice | 既存(採点結果) |
invitationId NEW | 招待リンク経由のみ値あり(直接受験はnull) |
talkLogs/ サブコレクション | 既存(会話ログ) |
where invitationId == X を1行足すだけ。| フィールド | 説明 |
|---|---|
speakingTestAiResponseCount 他(既存4項目) | 既存speakingTest設定と共用(数値系: 会話回数 / 発音厳しさ / AIスピード / 学年モード) |
invitationDefaultThemeIds NEW | リンク発行画面で選択したテーマIDの永続化 |
invitationDefaultGrammarIds NEW | リンク発行画面で選択した文法IDの永続化 |
invitationDefaultWordIds NEW | リンク発行画面で選択した単語IDの永続化 |
invitationDefaultCustomWordTexts NEW | リンク発行画面で自由入力した単語テキスト |
invitationDefaultCharacterIndex NEW | リンク発行画面で選択したAIキャラのインデックス |
talkSessionInvitations の snapshot を初期値として受験画面に表示。生徒が変更してもその受験回限り(teachers には影響なし)。talkSessions(名前/スコア/会話履歴)を吸い出せる。
request.auth.uid == teacherId判定が無意味化。ルールを緩めるしかない → セキュリティルール総崩れ。
token.firebase.sign_in_provider != 'anonymous' 判定 → 匿名ユーザーは学校データ・他生徒のリスト/履歴/設定が一切返らない。クライアント改造しても無効。
proxyOpenAi は request.auth を見るだけ → 匿名Authでもパス。クライアントは signInAnonymously() 1行追加で完了。
| 対策 | 実装方法 | 効果 |
|---|---|---|
| 累計受験者数の上限 | Welcome画面「テストを始める」押下時に usedCount atomic increment、超過時は新規拒否 | 1リンクの被害が有限(デフォルト100件) |
| 1時間自動失効 | サーバー側で expiresAt をチェック | 放置時の被害ウィンドウを最小化 |
| 会話往復上限(既存) | conversationLimit (デフォルト12往復) で自動終了 | 1セッションのコストを6分以内に固定 |
| 無音検知タイムアウト NEW | クライアント側で90秒無音 → セッション自動終了。結果は保存せず破棄(採点呼出も発生させない) | 放置接続の自動切断、無駄課金停止 |
| セッション最大時間 NEW | 受験開始から10分でforce close(無音検知の最後の砦) | 異常状態でも被害が10分で必ず止まる |
| タブ閉じ時の切断(既存) | 明示close処理は無し。ブラウザがRTCPeerConnectionを暗黙close → OpenAI server がICE timeout検知 → 数秒〜数十秒で session終了・課金停止 | 新規実装不要、既存挙動で十分 |
| invitationId検証 | 全関数で「invitation有効性」を必須チェック | 関数直叩きも受験設定なしには動かない |
| OpenAIホワイトリスト(既存) | 既存 proxyOpenAi のエンドポイント絞り込みを継続活用 | 許可されたAPI以外は中継しない |
/take/... 以外のパスを打ち込んでも、speakingTest 以外触らせない/take/* 以外アクセス不可。/teacher-dashboard・/settings 等を打ち込んでも「リンクが見つかりません」エラー画面に強制リダイレクト。
talkSessions への 直接書込不可。書込みは必ず Cloud Functions 経由のみ。先生・学校データもread/write一切不可。
proxyOpenAi・採点系関数は「invitationIdがvalid」必須チェック。signInAnonymously() 呼出anon_xY9k...)+ JWT発行localStorage に保存| 条件 | 結果 |
|---|---|
| 同じデバイス・同じブラウザ・localStorage残存 | 同じUIDで継続 |
| localStorageクリア / シークレットモード | 新規UID発行 |
| 別デバイス・別ブラウザ | 新規UID発行 |
| アプリ再インストール (mobile) | 新規UID発行 |
| ユースケース | 例 |
|---|---|
| ショッピングカート | 登録前に商品を入れておく → 後で signup したらカート維持 |
| ゲストモード | 登録なしで試用、後で本登録に昇格 |
| ゲーム進捗保存 | signup なしでも進捗保存、リスクは喪失 |
| レビュー/コメント投稿 | 気軽に書ける、後で身元証明したくなったら昇格 |
| tocme での用途 | 招待リンク経由生徒の使い捨て認証 |
linkWithCredential() を呼ぶと、匿名アカウントを Email/Google/Apple 等のID Providerに **紐付けて永続化**できる。SideNavigation に新規ブランチ追加(StatefulShellRoute 4→5 ブランチ拡張)。専用画面では既存 SpeakingTestSubNavigation を流用し、onStart の代わりに onCreateInvitation を呼ぶ。設定UIは100%再利用。
talkSessions 一覧画面に「リンク経由」バッジを追加するのみ。並び替え・フィルタは V2 で拡張可能。speaking_test_screen 流用before_speaking_test_screen 再利用
speaking_test_evaluation_screen 再利用
speaking_test_with_ai_screen 再利用
getInvitation が not-found エラー
getInvitation / incrementInvitationUsage が failed-precondition エラー
getInvitation / incrementInvitationUsage が resource-exhausted エラー
InvitationErrorScreen(errorType: ...) 単一 Widget で実装。3種の表示は enum 切替(アイコン・色・文言を出し分け)。
| シーン | 挙動 | 意図 |
|---|---|---|
| リンク発行 | 即座にURL生成(1時間有効) | 授業中の即時利用に最適化 |
| 生徒がURLクリック | 匿名サインイン → トークン照合 → Welcome画面 | シームレスな入口 |
| 受験開始時に期限切れ | 新規受験を拒否、エラー画面 | 不正利用防止 |
| 受験中に期限切れ(60分経過) | 最後まで完走可、結果保存OK | 「あと少しで終わるのに消えた」事故防止 |
| 同一生徒の再受験 | 無制限に許可、全履歴保存 | 練習用途・学習効果向上 |
| 累計100件超過 | 新規受験拒否、エラー画面 | コスト爆発防止 |
| 受験完了 | 既存と同じ評価画面、即時フィードバック | 学習モチベーション維持 |
| 同名複数生徒 | 全件保存(区別は createdAt + talkLogs) | V1はシンプル運用、V2で出席番号併用検討 |
| ネットワーク切断 | 既存テストと同じ挙動(再接続トライ) | 既存実装に依存 |
| リンク再発行 | 新規 invitation 作成(旧リンクは1時間後失効) | シンプル運用、無効化APIは V2 |
| 先生が設定変更 | 即時自動保存。数値系=既存speakingTest設定フィールド更新(speakingTest画面にも反映)、選択系=invitationDefault系フィールド更新 | 毎回選び直し不要、UX向上 |
| 生徒が設定変更 | 受験回のみ反映。teachers無影響、invitation snapshot も更新しない | 先生の意図と異なる設定で受験させたい場合の柔軟性 |
| リンク発行ボタン押下 | その時点の設定値で talkSessionInvitations にsnapshot保存 | 後から先生がデフォルト変更しても既発行リンクには影響なし |
| カテゴリ | 内容 | 規模 |
|---|---|---|
| Firestore | talkSessionInvitations新設(top-level), schools/{id}/talkSessions.invitationId追加, rules更新 + teachers に invitationDefault* フィールド群追加(選択系の永続化用) | 小 |
| Cloud Functions (新規) | createInvitation (先生Auth要), getInvitation (匿名可), incrementInvitationUsage (上限+atomic) | 中 |
| Cloud Functions (改修) | proxyOpenAi & 採点系関数に invitationId 検証ガード追加 | 小 |
| Routing | /take/:code 未認証ルート追加 + 先生用 StatefulShellRoute に「リンク発行」ブランチ追加(4→5ブランチ) + redirect 例外設定 | 小 |
| UI 生徒側 | Welcome画面 + 設定画面(既存 SpeakingTestSubNavigation 流用、編集可)+ 完了画面(再受験/終わる)+ エラー画面3種(リンク不正・期限切れ・上限超過) | 中 |
| UI 先生側 | サイドナビ「リンク発行」ブランチ追加 + 専用設定画面(既存 SpeakingTestSubNavigation 流用)+ リンク発行モーダル ※「リンク経由」バッジ表示は tocme_admin 側で別タスク | 中 |
| Auth | /take/ ルート初期化時に signInAnonymously() | 小 |
| Firebase Extensions | 「Delete Anonymous Users」導入(7日経過分自動削除) | 小 |