This commit is contained in:
2026-06-30 09:31:33 +07:00
parent c736326162
commit bbf8336664
77 changed files with 12601 additions and 556 deletions

View File

@@ -0,0 +1,212 @@
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 != ""
}
}
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" — dành cho phòng thi (sẽ bổ sung sau)
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,
})
}
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"
}
}