선생님 호출 시스템 추가 (Firebase 버전을 앱 계정 체계로 이전)

별도로 돌아가던 Firebase RTDB 기반 호출 페이지(index.html/teacher.html)에서
발견한 XSS, 익명 접근(아무나 아무 선생님 위치 변경 가능), 이름 위조 문제를
우리 앱 로그인 체계로 재구현하면서 구조적으로 없앴다. 학생은 본인 계정으로만
호출하고 본인 호출 기록만 보이며, 교사는 본인 위치만 수정하고 본인이 받은
호출만 본다. TTS 대신 FCM 푸시로 대체. 시간표 데이터의 "김지"→"조신현" 오타도
"조신"으로 수정해서 옮겼다.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
2026-09-17 23:34:08 +00:00
co-authored by Claude Sonnet 5
parent 89487a7dfc
commit 4cf759de62
6 changed files with 942 additions and 0 deletions
+132
View File
@@ -0,0 +1,132 @@
// 📅 선생님 호출 화면에서 "지금 수업 중" 배지를 보여주기 위한 시간표 데이터.
// 원래 별도 Firebase 페이지(index.html)에 있던 데이터를 그대로 옮기면서,
// "김지" 약어가 실제로는 조신현 선생님을 가리키던 오타를 "조신"으로 바로잡았다.
// (다른 모든 약어는 이름 앞 두 글자 규칙을 따르는데 이것만 깨져 있었음)
class PeriodTime {
final int period;
final String start;
final String end;
const PeriodTime(this.period, this.start, this.end);
}
const List<PeriodTime> kPeriodSettings = [
PeriodTime(1, "08:40", "09:30"),
PeriodTime(2, "09:40", "10:30"),
PeriodTime(3, "10:40", "11:30"),
PeriodTime(4, "11:40", "12:30"),
PeriodTime(5, "13:30", "14:20"),
PeriodTime(6, "14:30", "15:20"),
PeriodTime(7, "15:30", "16:20"),
];
// 이름 앞 두 글자 약어 → 전체 이름. ("조신":"조신현" 으로 수정됨 - 원래 "김지"였던 오타)
const Map<String, String> kTeacherAbbrevMap = {
"손계": "손계강",
"조신": "조신현",
"진경": "진경아",
"김만": "김만중",
"이길": "이길석",
"김효": "김효정",
"이부": "이부원",
"고경": "고경철",
"김경": "김경원",
"양성": "양성욱",
"김명": "김명선",
"김현": "김현진",
"박은": "박은호",
"심윤": "심윤주",
"조영": "조영희",
"이정": "이정상",
"조용": "조용우",
"이민": "이민혁",
};
// day: 1=월 ~ 5=금, period: 1~7. 값은 그 시간에 수업 중인 선생님 약어 목록.
const Map<int, Map<int, List<String>>> kFullTimetable = {
1: {
1: ["손계", "조신", "진경", "김만", "이길", "김효", "이부"],
2: ["김경", "손계", "이길", "조영", "김효", "양성", "김명", "김현"],
3: ["박은", "심윤", "조영", "고경", "이정", "이부", "김경"],
4: ["심윤", "진경", "김효", "이길", "이정", "김만", "김명", "김현", "양성"],
5: ["박은", "손계", "이정", "고경", "조영", "김현", "양성", "김명"],
6: ["이민", "심윤", "고경", "김만", "이길", "이부", "김경"],
7: ["이민", "김경", "박은", "김만", "이길", "조영", "양성", "조신", "손계"],
},
2: {
1: ["손계", "이정", "박은", "김만", "심윤", "이길", "조영", "김현", "조신", "양성"],
2: ["김효", "김경", "이정", "고경", "조영", "김만", "양성", "김현", "김명"],
3: ["진경", "심윤", "손계", "이민", "이부", "김명", "조용", "조신"],
4: ["이정", "진경", "김효", "이민", "이부", "조용", "양성", "김현"],
5: ["박은", "김효", "심윤", "이민", "이부", "김명", "김현", "손계"],
6: ["김경", "박은", "진경", "이민", "이부", "양성", "김명", "조용"],
7: ["진경", "손계", "이부", "김만", "이길", "김효", "박은", "김경", "김현"],
},
3: {
1: ["김효", "김경", "이민", "이부", "김명", "김현", "이정"],
2: ["조용", "진경", "김효", "조영", "김만", "이길", "이부", "고경"],
3: ["심윤", "손계", "조용", "이민", "박은", "조신", "양성", "이정", "김명"],
4: ["진경", "김효", "심윤", "이민", "박은", "조신", "이정", "양성", "김현"],
},
4: {
1: ["심윤", "김경", "조신", "조영", "이길", "김만", "이정", "양성", "김명"],
2: ["박은", "진경", "김만", "심윤", "이길", "조영", "이부", "고경"],
3: ["진경", "김효", "이민", "이부", "김현", "김명", "이정"],
4: ["손계", "김효", "김경", "이민", "박은", "조신", "양성", "이정", "김현"],
5: ["조신", "진경", "심윤", "김만", "고경", "조영", "이부", "이민"],
6: ["김경", "심윤", "손계", "이길", "조영", "김효", "박은", "진경", "조신"],
7: ["김효", "손계", "박은", "고경", "김만", "이길", "김현", "김명", "양성"],
},
5: {
1: ["손계", "박은", "이정", "김만", "조영", "이길", "김현", "김명", "양성"],
2: ["이정", "손계", "김경", "이길", "고경", "조영", "김명", "조신", "김현"],
3: ["진경", "심윤", "조용", "고경", "김만", "김효", "이부", "김경"],
4: ["조용", "이정", "진경", "김만", "심윤", "이길", "조영", "양성", "김현", "김명"],
5: ["진경", "손계", "이부", "조용", "조신", "고경", "박은", "김경", "김현"],
7: ["김효", "조용", "손계", "조영", "이길", "김만", "이부", "고경"],
},
};
/// 지금 이 순간(day: 1~5, HH:mm) 수업 중인 선생님 전체 이름 목록. 주말/쉬는시간이면 빈 목록.
List<String> teachersInClassNow(int weekday, String hhmm) {
if (weekday < 1 || weekday > 5) return [];
PeriodTime? current;
for (final p in kPeriodSettings) {
if (hhmm.compareTo(p.start) >= 0 && hhmm.compareTo(p.end) <= 0) {
current = p;
break;
}
}
if (current == null) return [];
final abbrevs = kFullTimetable[weekday]?[current.period] ?? [];
return abbrevs.map((a) => kTeacherAbbrevMap[a]).whereType<String>().toList();
}
const Map<String, String> kLocationIcons = {
"교무실": "🏢",
"급식실": "🍴",
"과학실": "🧪",
"도서관": "📖",
"출장": "🚗",
"조퇴/퇴근": "🏠",
"상담실": "💬",
"기숙사": "🏨",
};
const List<String> kLocationOptions = [
"교무실",
"급식실",
"과학실",
"도서관",
"출장",
"조퇴/퇴근",
"상담실",
"기숙사",
];
const List<String> kCallPurposes = [
"간단한 용무",
"질문 있어요",
"상담 신청",
"과제 제출",
"물건 전달",
];