Files
rikkei_simple_care/server/internal/handlers/handlers_student.go
PhuocNTB 96391b0706
All checks were successful
Deploy on Master Change / deploy (push) Successful in 53s
fix
2026-07-03 17:59:02 +07:00

259 lines
7.5 KiB
Go
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

package handlers
import (
"fmt"
"strconv"
"strings"
"time"
internalDb "server/internal/db"
"server/internal/models"
"github.com/gofiber/fiber/v2"
"gorm.io/gorm"
)
type studentShiftItem struct {
Period int `json:"period"`
CourseName string `json:"courseName"`
StartTime string `json:"startTime"`
EndTime string `json:"endTime"`
IsActiveNow bool `json:"isActiveNow"`
OnlineSeconds int `json:"onlineSeconds"`
OfflineSeconds int `json:"offlineSeconds"`
AttendanceStatus int `json:"attendanceStatus"`
AttendanceLabel string `json:"attendanceLabel"`
}
// GET /api/student/status — trạng thái giám sát, lớp/ca hiện tại, tích lũy & điểm danh hôm nay
func GetStudentStatusHandler(db *gorm.DB) fiber.Handler {
return func(c *fiber.Ctx) error {
studentID, err := strconv.ParseInt(c.Query("studentRkId", "0"), 10, 64)
if err != nil || studentID <= 0 {
return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "studentRkId is required"})
}
now := time.Now()
today := now.Format("2006-01-02")
dayIdx := weekdayIndex(now)
currMin := now.Hour()*60 + now.Minute()
classID, activePeriod := internalDb.FindActiveClassAndPeriodForStudent(db, studentID)
if classID <= 0 {
var link models.ClassStudent
if err := db.Where("student_rk_id = ?", studentID).First(&link).Error; err == nil {
classID = link.ClassRkID
}
}
className, classCode := "", ""
if classID > 0 {
var cl models.Class
if err := db.Where("rk_id = ?", classID).First(&cl).Error; err == nil {
className = cl.Name
classCode = cl.ClassCode
}
}
var todaySchedules []models.ClassSchedule
if classID > 0 {
_ = db.Where("class_rk_id = ? AND day_of_week = ? AND is_active = ?", classID, dayIdx, true).
Order("period asc, start_time asc").Find(&todaySchedules).Error
}
inScheduleNow := false
currentPeriod := 0
currentCourse, currentStart, currentEnd := "", "", ""
for _, s := range todaySchedules {
startMin := parseTimeMinutes(s.StartTime)
endMin := parseTimeMinutes(s.EndTime)
if currMin >= startMin && currMin <= endMin {
inScheduleNow = true
currentPeriod = s.Period
if currentPeriod < 1 {
currentPeriod = 1
}
currentCourse = s.CourseName
currentStart = s.StartTime
currentEnd = s.EndTime
break
}
}
if !inScheduleNow && activePeriod > 0 {
currentPeriod = activePeriod
}
hasApps := false
keywords := ""
if classID > 0 {
var appConfig models.ClassAllowedApp
if err := db.Where("class_rk_id = ?", classID).First(&appConfig).Error; err == nil {
keywords = strings.TrimSpace(appConfig.Keywords)
hasApps = keywords != ""
if hasApps {
keywords = keywords + "," + systemAllowedApps
}
}
}
monitorMode := "outside_schedule"
monitorLabel := "Ngoài giờ học"
if classID <= 0 {
monitorMode = "not_configured"
monitorLabel = "Chưa xác định được lớp học"
} else if !hasApps {
monitorMode = "not_configured"
monitorLabel = "Lớp chưa cấu hình ứng dụng được phép"
} else if inScheduleNow {
monitorMode = "learning"
if currentCourse != "" {
monitorLabel = fmt.Sprintf("Ca %d — %s (%s%s)", currentPeriod, currentCourse, currentStart, currentEnd)
} else {
monitorLabel = fmt.Sprintf("Đang giám sát — Ca %d", currentPeriod)
}
}
// monitorMode = "exam" — xử lý ở nhánh FindActiveExamForStudent phía trên
sessionMap := map[int]models.StudentSession{}
attMap := map[int]models.AttendanceResult{}
if classID > 0 {
var sessions []models.StudentSession
_ = db.Where("student_rk_id = ? AND class_rk_id = ? AND session_date = ?", studentID, classID, today).Find(&sessions).Error
for _, s := range sessions {
p := s.Period
if p < 1 {
p = 1
}
sessionMap[p] = s
}
var attendances []models.AttendanceResult
_ = db.Where("student_rk_id = ? AND class_rk_id = ? AND session_date = ?", studentID, classID, today).Find(&attendances).Error
for _, a := range attendances {
p := a.Period
if p < 1 {
p = 1
}
attMap[p] = a
}
}
shifts := make([]studentShiftItem, 0, len(todaySchedules))
for _, sch := range todaySchedules {
period := sch.Period
if period < 1 {
period = 1
}
sess := sessionMap[period]
att := attMap[period]
startMin := parseTimeMinutes(sch.StartTime)
endMin := parseTimeMinutes(sch.EndTime)
isActiveNow := currMin >= startMin && currMin <= endMin
attStatus := -1
attLabel := "Chưa tính"
if att.ID > 0 {
attStatus = att.Status
attLabel = att.StatusLabel
if attLabel == "" {
attLabel = attendanceLabelFallback(att.Status)
}
}
shifts = append(shifts, studentShiftItem{
Period: period,
CourseName: sch.CourseName,
StartTime: sch.StartTime,
EndTime: sch.EndTime,
IsActiveNow: isActiveNow,
OnlineSeconds: sess.OnlineSeconds,
OfflineSeconds: sess.OfflineSeconds,
AttendanceStatus: attStatus,
AttendanceLabel: attLabel,
})
}
// Ưu tiên phòng thi nếu sinh viên đang trong khung giờ thi
if examInfo := internalDb.FindActiveExamForStudent(db, studentID); examInfo != nil {
examKeywords := strings.TrimSpace(examInfo.Room.AllowedApps)
if examKeywords == "" {
examKeywords = systemAllowedApps
} else {
examKeywords = examKeywords + "," + systemAllowedApps
}
paperSent := examInfo.Enrollment.PaperSentAt != nil
var subCount int64
_ = db.Model(&models.ExamSubmission{}).Where("exam_room_id = ? AND student_rk_id = ?", examInfo.Room.ID, studentID).Count(&subCount).Error
examPayload := fiber.Map{
"examRoomId": examInfo.Room.ID,
"examName": examInfo.Room.Name,
"startTime": examInfo.Room.StartTime,
"endTime": examInfo.Room.EndTime,
"quizUrl": examInfo.Room.QuizURL,
"paperSent": paperSent,
"submitted": subCount > 0,
}
if paperSent && examInfo.Paper != nil {
examPayload["paperId"] = examInfo.Paper.ID
examPayload["paperTitle"] = examInfo.Paper.Title
}
return c.JSON(fiber.Map{
"sessionDate": today,
"monitorMode": "exam",
"monitorLabel": fmt.Sprintf("Phòng thi: %s", examInfo.Room.Name),
"classRkId": classID,
"className": className,
"classCode": classCode,
"currentPeriod": 0,
"currentCourseName": "",
"currentShiftStart": "",
"currentShiftEnd": "",
"inScheduleNow": false,
"blockerActive": examKeywords != "",
"allowedKeywords": examKeywords,
"shifts": shifts,
"exam": examPayload,
})
}
return c.JSON(fiber.Map{
"sessionDate": today,
"monitorMode": monitorMode,
"monitorLabel": monitorLabel,
"classRkId": classID,
"className": className,
"classCode": classCode,
"currentPeriod": currentPeriod,
"currentCourseName": currentCourse,
"currentShiftStart": currentStart,
"currentShiftEnd": currentEnd,
"inScheduleNow": inScheduleNow,
"blockerActive": hasApps && inScheduleNow,
"allowedKeywords": keywords,
"shifts": shifts,
})
}
}
func weekdayIndex(t time.Time) int {
wd := t.Weekday()
if wd == time.Sunday {
return 6
}
return int(wd) - 1
}
func attendanceLabelFallback(status int) string {
switch status {
case 1:
return "Nghỉ có phép"
case 2:
return "Nghỉ nửa buổi"
case 3:
return "Đi học muộn"
case 4:
return "Đi học đầy đủ"
default:
return "Nghỉ không phép"
}
}