feat(api): implement exam and grade synchronization modules
Add new functionality for managing and synchronizing academic grades and exam schedules. This includes new models, repositories, services, and HTTP handlers, along with updated gRPC definitions and dependency injection configuration. Additionally, improve the reliability of unread message count tracking by implementing an atomic Lua script for Redis operations in the conversation cache.
This commit is contained in:
78
internal/handler/exam_handler.go
Normal file
78
internal/handler/exam_handler.go
Normal file
@@ -0,0 +1,78 @@
|
||||
package handler
|
||||
|
||||
import (
|
||||
"github.com/gin-gonic/gin"
|
||||
|
||||
"with_you/internal/pkg/response"
|
||||
"with_you/internal/repository"
|
||||
"with_you/internal/service"
|
||||
)
|
||||
|
||||
type ExamHandler struct {
|
||||
examSyncService service.ExamSyncService
|
||||
examRepo repository.ExamRepository
|
||||
}
|
||||
|
||||
func NewExamHandler(examSyncService service.ExamSyncService, examRepo repository.ExamRepository) *ExamHandler {
|
||||
return &ExamHandler{
|
||||
examSyncService: examSyncService,
|
||||
examRepo: examRepo,
|
||||
}
|
||||
}
|
||||
|
||||
type syncExamsRequest struct {
|
||||
Username string `json:"username" binding:"required"`
|
||||
Password string `json:"password" binding:"required"`
|
||||
Semester string `json:"semester"`
|
||||
}
|
||||
|
||||
func (h *ExamHandler) SyncExams(c *gin.Context) {
|
||||
userID := c.GetString("user_id")
|
||||
if userID == "" {
|
||||
response.Unauthorized(c, "")
|
||||
return
|
||||
}
|
||||
|
||||
var req syncExamsRequest
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
response.BadRequest(c, "invalid request: "+err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
result, err := h.examSyncService.SyncUserExams(c.Request.Context(), userID, &service.SyncExamsRequest{
|
||||
Username: req.Username,
|
||||
Password: req.Password,
|
||||
Semester: req.Semester,
|
||||
})
|
||||
if err != nil {
|
||||
response.HandleError(c, err, "failed to sync exams")
|
||||
return
|
||||
}
|
||||
|
||||
if !result.Success {
|
||||
response.Success(c, result)
|
||||
return
|
||||
}
|
||||
response.SuccessWithMessage(c, result.Message, result)
|
||||
}
|
||||
|
||||
func (h *ExamHandler) ListExams(c *gin.Context) {
|
||||
userID := c.GetString("user_id")
|
||||
if userID == "" {
|
||||
response.Unauthorized(c, "")
|
||||
return
|
||||
}
|
||||
semester := c.Query("semester")
|
||||
if semester == "" {
|
||||
response.BadRequest(c, "semester is required")
|
||||
return
|
||||
}
|
||||
|
||||
exams, err := h.examRepo.ListByUserAndSemester(userID, semester)
|
||||
if err != nil {
|
||||
response.HandleError(c, err, "failed to list exams")
|
||||
return
|
||||
}
|
||||
|
||||
response.Success(c, gin.H{"exams": exams})
|
||||
}
|
||||
83
internal/handler/grade_handler.go
Normal file
83
internal/handler/grade_handler.go
Normal file
@@ -0,0 +1,83 @@
|
||||
package handler
|
||||
|
||||
import (
|
||||
"github.com/gin-gonic/gin"
|
||||
|
||||
"with_you/internal/pkg/response"
|
||||
"with_you/internal/repository"
|
||||
"with_you/internal/service"
|
||||
)
|
||||
|
||||
type GradeHandler struct {
|
||||
gradeSyncService service.GradeSyncService
|
||||
gradeRepo repository.GradeRepository
|
||||
}
|
||||
|
||||
func NewGradeHandler(gradeSyncService service.GradeSyncService, gradeRepo repository.GradeRepository) *GradeHandler {
|
||||
return &GradeHandler{
|
||||
gradeSyncService: gradeSyncService,
|
||||
gradeRepo: gradeRepo,
|
||||
}
|
||||
}
|
||||
|
||||
type syncGradesRequest struct {
|
||||
Username string `json:"username" binding:"required"`
|
||||
Password string `json:"password" binding:"required"`
|
||||
Semester string `json:"semester"`
|
||||
}
|
||||
|
||||
func (h *GradeHandler) SyncGrades(c *gin.Context) {
|
||||
userID := c.GetString("user_id")
|
||||
if userID == "" {
|
||||
response.Unauthorized(c, "")
|
||||
return
|
||||
}
|
||||
|
||||
var req syncGradesRequest
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
response.BadRequest(c, "invalid request: "+err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
result, err := h.gradeSyncService.SyncUserGrades(c.Request.Context(), userID, &service.SyncGradesRequest{
|
||||
Username: req.Username,
|
||||
Password: req.Password,
|
||||
Semester: req.Semester,
|
||||
})
|
||||
if err != nil {
|
||||
response.HandleError(c, err, "failed to sync grades")
|
||||
return
|
||||
}
|
||||
|
||||
if !result.Success {
|
||||
response.Success(c, result)
|
||||
return
|
||||
}
|
||||
response.SuccessWithMessage(c, result.Message, result)
|
||||
}
|
||||
|
||||
func (h *GradeHandler) ListGrades(c *gin.Context) {
|
||||
userID := c.GetString("user_id")
|
||||
if userID == "" {
|
||||
response.Unauthorized(c, "")
|
||||
return
|
||||
}
|
||||
semester := c.Query("semester")
|
||||
if semester == "" {
|
||||
response.BadRequest(c, "semester is required")
|
||||
return
|
||||
}
|
||||
|
||||
grades, err := h.gradeRepo.ListByUserAndSemester(userID, semester)
|
||||
if err != nil {
|
||||
response.HandleError(c, err, "failed to list grades")
|
||||
return
|
||||
}
|
||||
|
||||
gpaSummary, _ := h.gradeRepo.GetGpaSummary(userID, semester)
|
||||
|
||||
response.Success(c, gin.H{
|
||||
"grades": grades,
|
||||
"gpa_summary": gpaSummary,
|
||||
})
|
||||
}
|
||||
Reference in New Issue
Block a user