This commit is contained in:
@@ -291,43 +291,54 @@ func (a *App) handleGuardViolation(kind, reason string) {
|
||||
a.showQuitDialog("Vi phạm giám sát", reason)
|
||||
}
|
||||
|
||||
// HandleBeforeClose — SV bấm X / Alt+F4 khi đang giám sát = vi phạm.
|
||||
// Trả về false để cho phép đóng sau khi đã báo cáo.
|
||||
// HandleBeforeClose — SV bấm X / Alt+F4: luôn hỏi xác nhận.
|
||||
// Có → thoát + lưu vết vi phạm (nếu đang giám sát exam/learning). Không → hủy đóng.
|
||||
func (a *App) HandleBeforeClose() (prevent bool) {
|
||||
if !a.CheckLoginStatus() || !a.isMonitoringActive() {
|
||||
a.clearRunLock()
|
||||
return false
|
||||
}
|
||||
a.mu.Lock()
|
||||
mode := a.dashboard.MonitorMode
|
||||
loggedIn := a.student != nil
|
||||
a.mu.Unlock()
|
||||
if !shouldRecordViolation(mode) {
|
||||
a.clearRunLock()
|
||||
return false
|
||||
|
||||
message := "Bạn có chắc muốn thoát Simple Care không?"
|
||||
if loggedIn && a.isMonitoringActive() && shouldRecordViolation(mode) {
|
||||
message = "Bạn có chắc chắn muốn thoát ứng dụng không?\n\nLưu ý: Thoát khi đang trong giờ thi/học sẽ được ghi nhận là VI PHẠM."
|
||||
}
|
||||
|
||||
selection, err := runtime.MessageDialog(a.ctx, runtime.MessageDialogOptions{
|
||||
Type: runtime.QuestionDialog,
|
||||
Title: "Xác nhận thoát",
|
||||
Message: "Bạn có chắc chắn muốn thoát ứng dụng không?\n\nLưu ý: Thoát ứng dụng khi đang trong giờ thi/học sẽ được ghi nhận là VI PHẠM.",
|
||||
Message: message,
|
||||
Buttons: []string{"Có, thoát ứng dụng", "Không, tiếp tục"},
|
||||
DefaultButton: "Không, tiếp tục",
|
||||
CancelButton: "Không, tiếp tục",
|
||||
})
|
||||
if err != nil {
|
||||
a.reportViolation("app_closed", "Sinh viên tự đóng ứng dụng khi đang giám sát ("+mode+")")
|
||||
a.tearDownBeforeQuit()
|
||||
a.clearRunLock()
|
||||
return false
|
||||
log.Printf("[CLOSE] MessageDialog error: %v — hủy thoát để an toàn", err)
|
||||
return true
|
||||
}
|
||||
|
||||
if selection == "Có, thoát ứng dụng" {
|
||||
a.reportViolation("app_closed", "Sinh viên tự đóng ứng dụng khi đang giám sát ("+mode+")")
|
||||
a.tearDownBeforeQuit()
|
||||
a.clearRunLock()
|
||||
return false
|
||||
if !isConfirmQuitSelection(selection) {
|
||||
return true // Không / Cancel → ở lại
|
||||
}
|
||||
|
||||
return true // Prevent close!
|
||||
// Có → gửi vi phạm lên server rồi mới thoát
|
||||
if loggedIn && a.isMonitoringActive() && shouldRecordViolation(mode) {
|
||||
a.reportViolation("app_closed", "Sinh viên xác nhận tắt ứng dụng khi đang giám sát ("+mode+")")
|
||||
a.tearDownBeforeQuit()
|
||||
}
|
||||
a.clearRunLock()
|
||||
log.Printf("[CLOSE] User confirmed quit (mode=%s, loggedIn=%v)", mode, loggedIn)
|
||||
return false
|
||||
}
|
||||
|
||||
func isConfirmQuitSelection(selection string) bool {
|
||||
s := strings.TrimSpace(strings.ToLower(selection))
|
||||
switch s {
|
||||
case "có, thoát ứng dụng", "co, thoat ung dung", "yes", "ok", "có", "co":
|
||||
return true
|
||||
}
|
||||
// Windows đôi khi trả về đúng nhãn nút đã truyền
|
||||
return strings.Contains(s, "thoát") || strings.Contains(s, "thoat") || s == "yes"
|
||||
}
|
||||
|
||||
// tearDownBeforeQuit ngắt mọi kênh giám sát ngay — trước khi hiện dialog (tránh treo OK để duy trì kết nối).
|
||||
@@ -380,6 +391,8 @@ type pendingViolation struct {
|
||||
MonitorMode string `json:"monitorMode"`
|
||||
ClientAt string `json:"clientAt"`
|
||||
StudentRkID int64 `json:"studentRkId"`
|
||||
ClassRkID int64 `json:"classRkId"`
|
||||
ExamRoomID uint `json:"examRoomId"`
|
||||
}
|
||||
|
||||
// shouldRecordViolation — chỉ lưu vi phạm khi đang thi hoặc đang học (trong giờ).
|
||||
@@ -396,6 +409,11 @@ func (a *App) markRunLock() {
|
||||
a.mu.Lock()
|
||||
studentID := int64(0)
|
||||
mode := a.dashboard.MonitorMode
|
||||
classID := a.dashboard.ClassRkID
|
||||
examRoomID := uint(0)
|
||||
if a.dashboard.Exam != nil {
|
||||
examRoomID = a.dashboard.Exam.ExamRoomID
|
||||
}
|
||||
if a.student != nil {
|
||||
studentID = a.student.StudentID
|
||||
}
|
||||
@@ -406,6 +424,8 @@ func (a *App) markRunLock() {
|
||||
payload, _ := json.Marshal(map[string]any{
|
||||
"studentRkId": studentID,
|
||||
"monitorMode": mode,
|
||||
"classRkId": classID,
|
||||
"examRoomId": examRoomID,
|
||||
"startedAt": time.Now().Format(time.RFC3339),
|
||||
})
|
||||
_ = os.WriteFile(a.runLockPath, payload, 0644)
|
||||
@@ -425,6 +445,8 @@ func (a *App) detectUncleanShutdown() {
|
||||
var meta struct {
|
||||
StudentRkID int64 `json:"studentRkId"`
|
||||
MonitorMode string `json:"monitorMode"`
|
||||
ClassRkID int64 `json:"classRkId"`
|
||||
ExamRoomID uint `json:"examRoomId"`
|
||||
StartedAt string `json:"startedAt"`
|
||||
}
|
||||
_ = json.Unmarshal(data, &meta)
|
||||
@@ -448,6 +470,8 @@ func (a *App) detectUncleanShutdown() {
|
||||
MonitorMode: meta.MonitorMode,
|
||||
ClientAt: time.Now().Format(time.RFC3339),
|
||||
StudentRkID: meta.StudentRkID,
|
||||
ClassRkID: meta.ClassRkID,
|
||||
ExamRoomID: meta.ExamRoomID,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -499,9 +523,11 @@ func (a *App) postViolation(v pendingViolation) bool {
|
||||
"reason": v.Reason,
|
||||
"monitorMode": v.MonitorMode,
|
||||
"clientAt": v.ClientAt,
|
||||
"classRkId": v.ClassRkID,
|
||||
"examRoomId": v.ExamRoomID,
|
||||
}
|
||||
bodyBytes, _ := json.Marshal(payload)
|
||||
client := http.Client{Timeout: 5 * time.Second}
|
||||
client := http.Client{Timeout: 8 * time.Second}
|
||||
resp, err := client.Post(API_BASE+"/api/student/report-violation", "application/json", bytes.NewBuffer(bodyBytes))
|
||||
if err != nil {
|
||||
log.Printf("[VIOLATION] report failed: %v", err)
|
||||
@@ -513,7 +539,7 @@ func (a *App) postViolation(v pendingViolation) bool {
|
||||
log.Printf("[VIOLATION] report status %d: %s", resp.StatusCode, string(body))
|
||||
return false
|
||||
}
|
||||
log.Printf("[VIOLATION] reported kind=%s student=%d", v.Kind, v.StudentRkID)
|
||||
log.Printf("[VIOLATION] reported kind=%s student=%d class=%d exam=%d", v.Kind, v.StudentRkID, v.ClassRkID, v.ExamRoomID)
|
||||
return true
|
||||
}
|
||||
|
||||
@@ -521,6 +547,11 @@ func (a *App) reportViolation(kind, reason string) {
|
||||
a.mu.Lock()
|
||||
studentID := int64(0)
|
||||
mode := a.dashboard.MonitorMode
|
||||
classID := a.dashboard.ClassRkID
|
||||
examRoomID := uint(0)
|
||||
if a.dashboard.Exam != nil {
|
||||
examRoomID = a.dashboard.Exam.ExamRoomID
|
||||
}
|
||||
if a.student != nil {
|
||||
studentID = a.student.StudentID
|
||||
}
|
||||
@@ -534,6 +565,8 @@ func (a *App) reportViolation(kind, reason string) {
|
||||
MonitorMode: mode,
|
||||
ClientAt: time.Now().Format(time.RFC3339),
|
||||
StudentRkID: studentID,
|
||||
ClassRkID: classID,
|
||||
ExamRoomID: examRoomID,
|
||||
}
|
||||
if !a.postViolation(v) {
|
||||
a.enqueuePendingViolation(v)
|
||||
|
||||
Reference in New Issue
Block a user