学習支援システム

DEV Last updated: 2026-04-01
15
ソースファイル
4
ロール階層
33
APIアクション

プロジェクト概要

Google Apps Script (GAS) + Vue.js 3 で構築するマルチテナント型学習支援Webアプリ。
Firestoreをデータベースとして使用し、教育委員会 → 学校 → 教員/生徒 の階層構造で管理する。

個人情報保護: サーバー(Firestore)に生徒の氏名・メール等は一切保存しない。生徒は「管理番号」のみで識別。教員は手元のCSVでブラウザ内照合。

技術スタック

レイヤー技術備考
バックエンドGoogle Apps ScriptdoGet + google.script.run
フロントエンドVue.js 3 (CDN)SFC不使用、Options/Composition API混在
DBFirestore (REST API)ScriptApp.getOAuthToken()で認証
AIClaude APISonnet 4 / Vision対応
ホスティングGAS WebアプリdoGet でHTML配信

画面プレビュー

各画面のUIを確認できます(バックエンド未接続のためデータ取得は動作しません)

開発ステータス

  • Firestore REST APIクライアント (10_Firestore.gs)
  • 4段階ロール認証 (01_Auth.gs)
  • ロールベースRouter (02_Router.gs)
  • 生徒向け処理 — Firestore移行 (03_StudentHandler.gs)
  • 教員向け処理 — Firestore移行 (04_TeacherHandler.gs)
  • AI匿名化 — Firestore移行 (05_AnonymizeAI.gs)
  • AI採点 — Firestore移行 (09_Grading.gs)
  • 生徒画面 — マルチテナント対応 (index.html)
  • 教員画面 — マルチテナント対応 (dashboard.html)
  • AIチャット — GAS連携 (student_chat.html)
  • AI教材生成 — GAS連携 (teacher_assist.html)
  • AI採点画面 — GAS連携 (grading.html)
  • - 教育委員会管理者向けUI (board_admin.html)
  • - Firestoreセキュリティルール
  • - 初期データ投入スクリプト
  • - E2Eテスト

システムアーキテクチャ

FRONTEND (Vue.js 3 CDN)
index.html 生徒向け画面
dashboard.html 教員ダッシュボード
student_chat.html AIチャット
teacher_assist.html AI教材生成
grading.html AI答案採点
BACKEND (Google Apps Script)
02_Router.gs doGet + アクションルーター
01_Auth.gs 4段階ロール認証
03_StudentHandler.gs 生徒向け処理
04_TeacherHandler.gs 教員向け処理
05-09 AI連携・匿名化・採点
DATABASE (Firestore)
10_Firestore.gs REST APIクライアント
boards > schools > [collections]
ScriptApp.getOAuthToken()認証
マルチテナント(教育委員会/学校単位)
AI (Claude API)
06_ClaudeAI.gs API基盤
07_StudentChat.gs ヒント段階制御
08_TeacherAssist.gs 教材/テスト/指導案生成
09_Grading.gs Vision採点

リクエストフロー

生徒のログイン → 教材取得

index.html
google.script.run
loginStudent(boardId, schoolId, studentId)
fsGet() → Firestore
{ ok, role, subjects }
index.html
handleStudentAction({ action:'getMaterials', ... })
authenticateStudent()
getStudentMaterials()
fsQuery()

教員のAIツール利用

dashboard.html
window.open(?action=teacherAssist&boardId=...&tp=...)
teacher_assist.html
teacher_assist.html
handleTeacherAction({ action:'generateMaterial', ... })
authenticateTeacher()
handleGenerateMaterial()
callClaude()

doGet ルーティング

action パラメータ返すHTML用途
index (デフォルト)index.html生徒向け画面
dashboarddashboard.html教員ダッシュボード
studentChatstudent_chat.htmlAIチャット
teacherAssistteacher_assist.htmlAI教材生成
gradinggrading.htmlAI答案採点

公開API(認証不要)

関数名引数戻り値
getBoards()-[{ id, name }]
getSchools(boardId)boardId[{ id, name }]
loginStudent(boardId, schoolId, studentId)3引数{ ok, role, boardId, schoolId, studentId, subjects }
loginTeacher(boardId, schoolId, teacherId, pw)4引数{ ok, role, boardId, schoolId, teacherId, subjects }
loginBoardAdmin(boardId, pw)2引数{ ok, role, boardId, schools }

handleStudentAction アクション一覧

共通パラメータ: { action, boardId, schoolId, studentId }
action追加パラメータ処理関数説明
getMaterialssubject?, grade?, unit?getStudentMaterials教材一覧(公開+履修教科のみ)
getQuizquizIdgetStudentQuiz小テスト取得(正解なし)
submitQuizAnswerquizId, answers[]submitQuizAnswerテスト回答提出(正解付きで返す)
submitReflectionmaterialId, subject, answers{}submitReflection振り返り提出
getMyKartes-getStudentKartes自分のAIカルテ一覧
studentChatsubject, unit, messages[], hintLevelhandleStudentChatAIチャット

handleTeacherAction アクション一覧

共通パラメータ: { action, boardId, schoolId, teacherId, teacherPassword }
action権限処理関数説明
getMaterialsteacher+getTeacherMaterials教材一覧(非公開含む)
getReflectionsteacher+getTeacherReflections振り返り一覧
getStudentDetailteacher+getTeacherStudentDetail生徒詳細
getStudentIdsteacher+getTeacherStudentIds管理番号一覧
exportForAIteacher+exportAnonymizedAI匿名化エクスポート
saveMaterialteacher+saveMaterial教材保存
saveQuizteacher+saveQuizテスト保存
togglePublishteacher+togglePublish公開切替
importAIKarteteacher+importAIKarteカルテインポート
saveSettingsteacher+saveSettings教科マスター保存
registerStudentsschoolAdmin+registerStudents生徒一括登録
registerTeacherschoolAdmin+registerTeacher教員登録
getTeachersschoolAdmin+getTeacherList教員一覧
generateMaterialteacher+handleGenerateMaterialAI教材生成
generateQuizteacher+handleGenerateQuizAIテスト生成
generateLessonteacher+handleGenerateLessonAI指導案生成
gradeAnswerteacher+handleGradeAnswerAI答案採点

handleBoardAction アクション一覧

共通パラメータ: { action, boardId, boardPassword }
action処理関数説明
getSchoolsgetSchools学校一覧
addSchooladdSchool学校追加
getReflectionsgetBoardReflections全学校の振り返り
getSchoolSummarygetSchoolSummary学校サマリー

Firestoreコレクション構造

boards/{boardId} { name, adminPassword } ├─ schools/{schoolId} │ { name } │ ├─ students/{studentId} { subjects[] } │ ├─ teachers/{teacherId} { password, role, subjects[] } │ ├─ materials/{id} { title, subject, grade, unit, body, published, createdAt } │ ├─ quizzes/{id} { title, materialId, subject, questions[], createdAt } │ ├─ quizAnswers/{id} { studentId, quizId, subject, correct, total, answers[], createdAt } │ ├─ reflections/{id} { studentId, materialId, subject, answers{}, createdAt } │ ├─ kartes/{id} { studentId, opinion, support, updatedAt } │ ├─ dummyMap/{id} { studentId, dummyId, createdAt } │ ├─ settings/main { settings[], updatedAt } │ ├─ accessLogs/{id} { who, status, createdAt } │ └─ gradingResults/{id} { studentId, quizId, totalScore, maxScore, detail{}, createdAt } └─ accessLogs/{id} (教育委員会レベルのログ)

CONFIG.COL 定数マッピング

定数パスヘルパー
CONFIG.COL.BOARDS'boards'-
CONFIG.COL.SCHOOLS'schools'schoolPath(boardId, schoolId)
CONFIG.COL.STUDENTS'students'schoolCol(boardId, schoolId, 'students')
CONFIG.COL.TEACHERS'teachers'同上
CONFIG.COL.MATERIALS'materials'同上
CONFIG.COL.QUIZZES'quizzes'同上
CONFIG.COL.QUIZ_ANS'quizAnswers'同上
CONFIG.COL.REFLECTIONS'reflections'同上
CONFIG.COL.KARTES'kartes'同上
CONFIG.COL.DUMMY_MAP'dummyMap'同上
CONFIG.COL.GRADING'gradingResults'同上

Firestore CRUDヘルパー (10_Firestore.gs)

関数用途
fsGet(path)1ドキュメント取得fsGet('boards/b1/schools/s1/students/S001')
fsSet(colPath, docId, data)ID指定で作成/上書きfsSet('boards/b1/schools/s1/students', 'S001', {...})
fsAdd(colPath, data)ID自動生成で作成fsAdd('boards/b1/schools/s1/reflections', {...})
fsUpdate(path, data)部分更新fsUpdate('boards/b1/.../materials/m1', { published: true })
fsDelete(path)削除fsDelete('boards/b1/.../students/S001')
fsList(colPath)全ドキュメント取得fsList('boards/b1/schools/s1/students')
fsQuery(colPath, where, orderBy, dir, limit)条件付き検索fsQuery(col, [{field:'subject', op:'EQUAL', value:'数学'}])

ロール階層と権限

ロール定数認証方法権限範囲
生徒 student 管理番号の存在チェック 自分の履修教科の教材・テスト・振り返り
教科担当 teacher 教員ID + パスワード 担当教科のみ(教材CRUD、振り返り閲覧、AI連携)
学校管理者 schoolAdmin 教員ID + パスワード (role: schoolAdmin) 全教科 + 教員管理 + 生徒管理
教育委員会 boardAdmin 教育委員会ID + パスワード 学校横断閲覧 + 学校追加

権限チェック関数

関数チェック内容
canAccessSubject(auth, subject)教科担当は自教科のみ、schoolAdmin/boardAdminは全教科OK
isTeacherOrAbove(auth)teacher, schoolAdmin, boardAdmin のいずれか
isSchoolAdminOrAbove(auth)schoolAdmin, boardAdmin のいずれか

認証レスポンス形式

生徒 (authenticateStudent)

// 成功 { "ok": true, "role": "student", "boardId": "b1", "schoolId": "s1", "studentId": "S001", "subjects": ["数学", "英語"] } // 失敗 { "ok": false, "error": "この管理番号は登録されていません。" }

教員 (authenticateTeacher)

{ "ok": true, "role": "teacher"|"schoolAdmin", "boardId": "b1", "schoolId": "s1", "teacherId": "T001", "subjects": [...] }

教育委員会 (authenticateBoardAdmin)

{ "ok": true, "role": "boardAdmin", "boardId": "b1", "schools": ["s1", "s2"] }

ファイル一覧と責務

ファイル種別責務依存先
00_Config.gsGSFirebase設定・コレクションパス・ロール定数-
01_Auth.gsGS4段階ロール認証 + アクセスログ10_Firestore
02_Router.gsGSdoGet + handleStudentAction / handleTeacherAction / handleBoardAction01_Auth, 03-09
03_StudentHandler.gsGS教材取得・テスト受験・振り返り提出・カルテ閲覧10_Firestore
04_TeacherHandler.gsGS教材CRUD・振り返り閲覧・生徒詳細・教員管理・設定10_Firestore, 01_Auth
05_AnonymizeAI.gsGS振り返りのダミーID匿名化エクスポート10_Firestore, 01_Auth
06_ClaudeAI.gsGSClaude API呼び出し基盤(テキスト/Vision)-
07_StudentChat.gsGSヒント段階制御AIチャット06_ClaudeAI
08_TeacherAssist.gsGSAI教材/テスト/指導案生成06_ClaudeAI
09_Grading.gsGSAI答案採点 + 採点結果保存06_ClaudeAI, 10_Firestore
10_Firestore.gsGSFirestore REST APIクライアント(CRUD + Query)00_Config
index.htmlHTML生徒向け画面(ログイン・教材・テスト・振り返り)02_Router
dashboard.htmlHTML教員ダッシュボード(管理・AI連携)02_Router
student_chat.htmlHTML生徒AIチャット画面02_Router
teacher_assist.htmlHTML教員AI生成画面(教材/テスト/指導案)02_Router
grading.htmlHTMLAI答案採点画面02_Router

初回セットアップ手順

  1. Firebase プロジェクトを作成し、Firestore を有効化
  2. GAS プロジェクトを作成し、同じ GCP プロジェクトに紐付ける
    GAS エディタ → 設定 → Google Cloud Platform (GCP) プロジェクト → プロジェクト番号を入力
  3. 00_Config.gsFIREBASE_PROJECT_ID を設定
  4. Claude API キーをスクリプトプロパティに設定
    GAS エディタ → プロジェクトの設定 → スクリプトプロパティ キー: CLAUDE_API_KEY 値: sk-ant-api03-...
  5. Firestore に初期データを投入
    // boards コレクションにドキュメントを作成 boards/board1 = { "name": "XX市教育委員会", "adminPassword": "your-secure-password" } // schools サブコレクション boards/board1/schools/school1 = { "name": "XX市立第一中学校" } // 最初の学校管理者を登録 boards/board1/schools/school1/teachers/admin = { "password": "your-secure-password", "role": "schoolAdmin", "subjects": [] }
  6. GAS Webアプリとしてデプロイ(アクセス: 「全員」)

URLパラメータ(サブページ共通)

dashboard.html からサブページを開く際、URLパラメータで認証情報を渡す
パラメータ用途使用ページ
actiondoGetルーティング全ページ
boardId教育委員会IDstudent_chat, teacher_assist, grading
schoolId学校ID同上
studentId管理番号student_chat
teacherId教員IDteacher_assist, grading
tp教員パスワードteacher_assist, grading

開発ルール(厳守)

  1. 個人情報をサーバーに保存しない。管理番号のみで識別する。
  2. CSV照合はブラウザ内のみ。google.script.run でCSVデータを送信しない。
  3. 小テストの正解(answer)は受験中のレスポンスに含めない。提出後のみ返す。
  4. アクセスログは全認証試行を記録。ただし個人情報は含めない。
  5. ダミーID対応表は外部に出さない。GAS内で逆引きしてから保存。
  6. 教科担当の教員は自教科のデータのみアクセス可能。canAccessSubject() で制御。

コーディング規約

項目規約
GAS関数名camelCase: getStudentMaterials, handleTeacherAction
プライベート関数_プレフィックス: _fsHeaders(), _writeLog()
Firestore操作必ず 10_Firestore.gs のヘルパーを使う(直接REST呼び出し禁止)
認証チェックRouter内で authenticate* を呼ぶ。Handler側では呼ばない。
パスヘルパーschoolPath(), schoolCol() を使う(パス文字列を直書きしない)
フロントエンド → GASgas(fnName, ...args) ヘルパー経由。直接 google.script.run を使わない。
エラーハンドリングGAS側は throw new Error()、フロント側は try/catch + showToast

セキュリティ注意点

教員パスワードがURLパラメータに含まれる: teacher_assist.html / grading.html はdashboard.htmlから ?tp=... で開かれる。 GAS WebアプリはHTTPS + Google認証基盤上で動作するため許容しているが、将来的にはセッショントークン方式への移行を検討。
Firestoreセキュリティルール: 現在はGAS(サーバーサイド)からのみアクセスするため、Firestoreルールは厳格に制限すべき(クライアント直接アクセス禁止)。