This commit is contained in:
@@ -45,6 +45,7 @@ func FindActiveClassAndPeriodForStudent(db *gorm.DB, studentRkID int64) (int64,
|
||||
|
||||
var schedules []models.ClassSchedule
|
||||
if err := db.Where("class_rk_id IN ? AND is_active = ?", classRkIDs, true).Find(&schedules).Error; err != nil || len(schedules) == 0 {
|
||||
// Không có lớp nào có lịch học → trả về lớp đầu tiên
|
||||
return classRkIDs[0], 1
|
||||
}
|
||||
|
||||
@@ -52,6 +53,7 @@ func FindActiveClassAndPeriodForStudent(db *gorm.DB, studentRkID int64) (int64,
|
||||
day := scheduleDayIndex(now)
|
||||
currMin := now.Hour()*60 + now.Minute()
|
||||
|
||||
// Ưu tiên 1: Lớp có ca đang diễn ra ngay lúc này
|
||||
for _, s := range schedules {
|
||||
if s.DayOfWeek != day {
|
||||
continue
|
||||
@@ -67,6 +69,18 @@ func FindActiveClassAndPeriodForStudent(db *gorm.DB, studentRkID int64) (int64,
|
||||
}
|
||||
}
|
||||
|
||||
// Ưu tiên 2: Fallback về lớp đầu tiên CÓ lịch học (ưu tiên hơn lớp chỉ có allowed_apps)
|
||||
// Lấy tập hợp các class có schedules
|
||||
scheduledClassSet := map[int64]bool{}
|
||||
for _, s := range schedules {
|
||||
scheduledClassSet[s.ClassRkID] = true
|
||||
}
|
||||
for _, id := range classRkIDs {
|
||||
if scheduledClassSet[id] {
|
||||
return id, 1
|
||||
}
|
||||
}
|
||||
|
||||
return classRkIDs[0], 1
|
||||
}
|
||||
|
||||
|
||||
@@ -21,7 +21,10 @@ import (
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
const defaultExamAllowedApps = "chrome,msedge,edge,acrobat,acrord32,foxit,wails,simple_care,client"
|
||||
|
||||
const systemAllowedApps = "explorer.exe,taskmgr.exe,cmd.exe,powershell.exe,conhost.exe,client.exe,simple_care_v1.0.exe,simple_care.exe,wails.exe,msedgewebview2.exe,code.exe,cursor.exe,windsurf.exe,goland.exe,goland64.exe,idea64.exe,clion64.exe,webstorm64.exe,pycharm64.exe,rider64.exe,studio64.exe,eclipse.exe,sublime_text.exe,notepad++.exe,devenv.exe,git-bash.exe,bash.exe,chsime.exe,ctfmon.exe,unikey.exe,unikeyvnt.exe,gotiengviet.exe,openkey.exe,openkey64.exe,applicationframehost.exe,dwm.exe,csrss.exe,consent.exe,finder,dock,windowserver,loginwindow,systemuiserver,controlcenter,notificationcenter,spotlight,vietnamese,pboard,securityagent,keychain,coreaudiod,terminal,iterm,iterm2,zsh,sh,osascript,system events,gnome-shell,mutter,xwayland,xorg,plasmashell,hyprland,sway,waybar,rofi,gdm3,sddm,lightdm,gnome-terminal,ptyxis,konsole,fcitx5,fcitx,ibus-daemon,ibus-x11,ibus-,dbus-daemon,systemd,nm-applet,blueman-applet,kdeconnectd,shellexperiencehost.exe,startmenuexperiencehost.exe,searchhost.exe,searchapp.exe,searchindexer.exe,sihost.exe,taskhostw.exe,taskbar.exe,lockapp.exe,logonui.exe,winlogon.exe,userinit.exe,runtimebroker.exe,backgroundtaskhost.exe,wwahost.exe,microsoftedgecp.exe,textinputhost.exe,chsime.exe,imetip.exe,imecmnt.exe,googlepinyin.exe,baidu.exe,credentialuibroker.exe,smartscreen.exe,securityhealthsystray.exe,securityhealthservice.exe,wscsvc.exe,msseces.exe,msmpeng.exe,nisSrv.exe,notificationplatformcontroller,systemsettings.exe,settingssynchostservice.exe,settingssynchost.exe,regsvc.exe,spoolsv.exe,tabtip.exe,tabtip32.exe,onedrive.exe,onedriveupdater.exe,narrator.exe,magnify.exe,osk.exe,audiodg.exe,sndvol.exe,cmediaaudiocontrolpanel.exe,wuauclt.exe,musnotifyicon.exe,windowsstore.exe,winstore.app.exe,nvdisplay.container.exe,nvcontainer.exe,nvinject.exe,nvtelemetrycontainer.exe,nvvsvc.exe,nvspcaps64.exe,geforce experience.exe,radeonsoftware.exe,amddvr.exe,igfxtray.exe,igfxem.exe,igfxhk.exe,hkcmd.exe,atk hub.exe,atkex.exe,asusoptimization.exe,avastui.exe,avgui.exe,mbam.exe,mbamtray.exe,bdagent.exe,uiseagnt.exe,eguiproxy.exe,resmon.exe,perfmon.exe,mmc.exe,pwsh.exe,windowsterminal.exe,wt.exe,mintty.exe,wsl.exe,wslhost.exe,launchpad,mission control,exposé,universalaccessd,accessibilityuiagent,inputmethodkit,ibus,hiragana kakomi input,kinput2,squirrel,scim,kotoeri,pinyin - simplified,zhuyin - traditional,abc,keychainservicesagent,trustd,opendirectoryd,authorizationhost,securityd,biometricd,touchidd,audioundockhelper,audio midi setup,noiseremoval,networkd,nesessionmanager,scutil,configd,mDNSResponder,mds,mds_stores,mdworker,mdworker_shared,bird,cloudd,com.apple.icloud,cloudphotod,nsurlsessiond,system preferences,system settings,software update,app store,screensaver engine,screensharingd,com.apple.screensaver,colorsyncd,colorsync utility,nightshift,display menu,printingproxy,cupsd,crashreporter,diagnosticsd,spindump,reportmemoryexception,webkit,com.apple.webkit,com.apple.webkit.networking,applescriptkit,applescript runner,rustdesk,anydesk,teamviewer,screen sharing,wezterm,kitty,alacritty,hyper,git,git-credential-manager,git-credential-osxkeychain,github desktop,sourcetree,fork,docker,docker desktop,com.docker,mutter-x11-fram,xorg,Xorg,x11,kwin_wayland,kwin_x11,wayfire,river,labwc,openbox,i3,i3bar,awesome,bspwm,xfwm4,marco,compiz,picom,compton,wl-paste,wl-copy,wlr-randr,kanshi,wlsunset,gammastep,swaylock,swayidle,swaybg,swaync,eww,ags,wofi,fuzzel,tofi,dmenu,bemenu,gdm,lxdm,slim,greetd,gnome-session-bi,lxsession,startx,xinit,gnome-terminal-,foot,xfce4-terminal,tilix,terminator,urxvt,rxvt,sakura,st,fish,dash,ksh,fcitx-,uim,sogou-qimpanel,gcin,kimpanel,gjs,gsd-,goa-identity-ser,evolution-,gnome-keyring-d,gnome-keyring,polkit-gnome-au,polkitd,gnome-settings-d,gnome-initial-se,gnome-control-ce,gvfsd,gvfsd-,tracker-miner-,tracker3,zeitgeist,zeitgeist-,accounts-daemon,colord,power-profiles-,fprintd,fwupd,udisksd,upowerd,packagekitd,nm-dispatcher,at-spi,at-spi-bus-laun,at-spi2-registr,dbus-launch,snapd-,snapd,nm-tray,network-manager-,blueman-tray,blueman-manager,kdeconnect-indi,dunst,mako,notify-osd,xfce4-notifyd,fnott,lxqt-policykit-,xfce-polkit,mate-polkit,pkttyagent,copyq,clipit,xclip,xsel,clipman,greenclip,glycin,zenity,yad,kdialog,webkit2gtk,WebKitWebProcess,WebKitNetworkPro,rustdesk-bin,remmina,clion,webstorm,pycharm,rider,studio,simple_care_v1.0"
|
||||
|
||||
|
||||
|
||||
func examUploadRoot() string {
|
||||
if v := os.Getenv("UPLOAD_DIR"); v != "" {
|
||||
@@ -173,9 +176,6 @@ func CreateExamRoomHandler(db *gorm.DB) fiber.Handler {
|
||||
return c.Status(400).JSON(fiber.Map{"error": "Giờ kết thúc phải sau giờ bắt đầu"})
|
||||
}
|
||||
apps := strings.TrimSpace(req.AllowedApps)
|
||||
if apps == "" {
|
||||
apps = defaultExamAllowedApps
|
||||
}
|
||||
room := models.ExamRoom{
|
||||
Name: req.Name,
|
||||
StartTime: start,
|
||||
|
||||
@@ -205,6 +205,92 @@ func DeleteClassScheduleHandler(db *gorm.DB) fiber.Handler {
|
||||
}
|
||||
}
|
||||
|
||||
// GET /api/classes/:rkId/schedule-conflicts - Kiểm tra sinh viên trùng với lớp đang giám sát có lịch học
|
||||
func GetClassScheduleConflictsHandler(db *gorm.DB) fiber.Handler {
|
||||
return func(c *fiber.Ctx) error {
|
||||
rkID, err := strconv.ParseInt(c.Params("rkId"), 10, 64)
|
||||
if err != nil {
|
||||
return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "Invalid class rkId"})
|
||||
}
|
||||
|
||||
// Lấy danh sách sinh viên của lớp muốn kích hoạt
|
||||
var newClassStudents []int64
|
||||
if err := db.Model(&models.ClassStudent{}).Where("class_rk_id = ?", rkID).Pluck("student_rk_id", &newClassStudents).Error; err != nil {
|
||||
return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{"error": err.Error()})
|
||||
}
|
||||
if len(newClassStudents) == 0 {
|
||||
return c.JSON(fiber.Map{"conflicts": []any{}, "ok": true})
|
||||
}
|
||||
|
||||
// Tìm các lớp đang giám sát CÓ lịch học (không bao gồm lớp đang được kiểm tra)
|
||||
var activeClassIDs []int64
|
||||
if err := db.Model(&models.ClassSchedule{}).Distinct("class_rk_id").Pluck("class_rk_id", &activeClassIDs).Error; err != nil {
|
||||
return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{"error": err.Error()})
|
||||
}
|
||||
|
||||
// Lọc bỏ chính lớp đang kiểm tra
|
||||
var otherActiveIDs []int64
|
||||
for _, id := range activeClassIDs {
|
||||
if id != rkID {
|
||||
otherActiveIDs = append(otherActiveIDs, id)
|
||||
}
|
||||
}
|
||||
if len(otherActiveIDs) == 0 {
|
||||
return c.JSON(fiber.Map{"conflicts": []any{}, "ok": true})
|
||||
}
|
||||
|
||||
// Tìm sinh viên trùng
|
||||
var overlappingStudentIDs []int64
|
||||
if err := db.Model(&models.ClassStudent{}).
|
||||
Where("class_rk_id IN ? AND student_rk_id IN ?", otherActiveIDs, newClassStudents).
|
||||
Pluck("student_rk_id", &overlappingStudentIDs).Error; err != nil {
|
||||
return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{"error": err.Error()})
|
||||
}
|
||||
if len(overlappingStudentIDs) == 0 {
|
||||
return c.JSON(fiber.Map{"conflicts": []any{}, "ok": true})
|
||||
}
|
||||
|
||||
// Lấy thông tin sinh viên và lớp bị trùng
|
||||
type conflictItem struct {
|
||||
StudentRkID int64 `json:"studentRkId"`
|
||||
FullName string `json:"fullName"`
|
||||
StudentCode string `json:"studentCode"`
|
||||
ConflictClassRkID int64 `json:"conflictClassRkId"`
|
||||
ConflictClassName string `json:"conflictClassName"`
|
||||
ConflictClassCode string `json:"conflictClassCode"`
|
||||
}
|
||||
|
||||
var conflicts []conflictItem
|
||||
for _, sid := range overlappingStudentIDs {
|
||||
var st models.Student
|
||||
if err := db.Where("rk_id = ?", sid).First(&st).Error; err != nil {
|
||||
continue
|
||||
}
|
||||
var classIDs []int64
|
||||
_ = db.Model(&models.ClassStudent{}).Where("class_rk_id IN ? AND student_rk_id = ?", otherActiveIDs, sid).Pluck("class_rk_id", &classIDs)
|
||||
for _, cid := range classIDs {
|
||||
var cls models.Class
|
||||
if err := db.Where("rk_id = ?", cid).First(&cls).Error; err != nil {
|
||||
continue
|
||||
}
|
||||
conflicts = append(conflicts, conflictItem{
|
||||
StudentRkID: sid,
|
||||
FullName: st.FullName,
|
||||
StudentCode: st.StudentCode,
|
||||
ConflictClassRkID: cid,
|
||||
ConflictClassName: cls.Name,
|
||||
ConflictClassCode: cls.ClassCode,
|
||||
})
|
||||
}
|
||||
}
|
||||
if conflicts == nil {
|
||||
conflicts = []conflictItem{}
|
||||
}
|
||||
|
||||
return c.JSON(fiber.Map{"conflicts": conflicts, "ok": len(conflicts) == 0})
|
||||
}
|
||||
}
|
||||
|
||||
func parseTimeMinutes(tStr string) int {
|
||||
parts := strings.Split(tStr, ":")
|
||||
if len(parts) != 2 {
|
||||
@@ -231,7 +317,9 @@ func GetAllowedAppsHandler(db *gorm.DB) fiber.Handler {
|
||||
if examInfo := internalDb.FindActiveExamForStudent(db, studentID); examInfo != nil {
|
||||
keywords := strings.TrimSpace(examInfo.Room.AllowedApps)
|
||||
if keywords == "" {
|
||||
keywords = defaultExamAllowedApps
|
||||
keywords = systemAllowedApps
|
||||
} else {
|
||||
keywords = keywords + "," + systemAllowedApps
|
||||
}
|
||||
return c.JSON(fiber.Map{
|
||||
"classRkId": rkID,
|
||||
@@ -307,7 +395,17 @@ func GetAllowedAppsHandler(db *gorm.DB) fiber.Handler {
|
||||
})
|
||||
}
|
||||
|
||||
return c.JSON(appConfig)
|
||||
keywords := strings.TrimSpace(appConfig.Keywords)
|
||||
if keywords != "" {
|
||||
keywords = keywords + "," + systemAllowedApps
|
||||
} else {
|
||||
keywords = systemAllowedApps
|
||||
}
|
||||
return c.JSON(fiber.Map{
|
||||
"classRkId": appConfig.ClassRkID,
|
||||
"keywords": keywords,
|
||||
"exit": false,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -90,6 +90,9 @@ func GetStudentStatusHandler(db *gorm.DB) fiber.Handler {
|
||||
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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -173,7 +176,9 @@ func GetStudentStatusHandler(db *gorm.DB) fiber.Handler {
|
||||
if examInfo := internalDb.FindActiveExamForStudent(db, studentID); examInfo != nil {
|
||||
examKeywords := strings.TrimSpace(examInfo.Room.AllowedApps)
|
||||
if examKeywords == "" {
|
||||
examKeywords = defaultExamAllowedApps
|
||||
examKeywords = systemAllowedApps
|
||||
} else {
|
||||
examKeywords = examKeywords + "," + systemAllowedApps
|
||||
}
|
||||
paperSent := examInfo.Enrollment.PaperSentAt != nil
|
||||
var subCount int64
|
||||
|
||||
@@ -157,6 +157,7 @@ func main() {
|
||||
staff.Delete("/network/accepted-wifis/:id", handlers.DeleteAcceptedWifiHandler(gormDB))
|
||||
staff.Get("/classes/:rkId/session-logs", handlers.ListClassSessionLogsHandler(gormDB))
|
||||
staff.Get("/classes/:rkId/online-students", handlers.GetOnlineStudentsHandler(gormDB))
|
||||
staff.Get("/classes/:rkId/schedule-conflicts", handlers.GetClassScheduleConflictsHandler(gormDB))
|
||||
|
||||
staff.Get("/classes/:rkId/courses", handlers.GetClassCoursesHandler(gormDB, qldtClient))
|
||||
staff.Post("/classes/:rkId/schedule/apply-template", handlers.ApplyScheduleTemplateHandler(gormDB))
|
||||
|
||||
Binary file not shown.
Reference in New Issue
Block a user