feat: 多班级版 v2.0 - Go后端重写 + 43轮代码审查
- 后端从 Python FastAPI 重写为 Go Gin(端口 56789) - 多班级完全隔离 - 超级管理员独立登录 - 课代表作业管理、排行榜分项排行 - 角色加减分上下限可配置 - 家长改密功能(可开关) - 周度/月度重置功能 - MySQL 5.7 兼容 - 43轮代码审查+全部修复 - Apache 2.0 许可证
This commit is contained in:
184
backend-go/internal/repository/attendance_repo.go
Normal file
184
backend-go/internal/repository/attendance_repo.go
Normal file
@@ -0,0 +1,184 @@
|
||||
// ===========================================
|
||||
// 多班级版班级管理系统 - Go 后端
|
||||
//
|
||||
// 开发者: Canglan
|
||||
// 联系方式: admin@sea-studio.top
|
||||
// 版权归属: Sea Network Technology Studio
|
||||
// 许可证: Apache License 2.0
|
||||
//
|
||||
// 版权所有 © Sea Network Technology Studio
|
||||
// ===========================================
|
||||
|
||||
package repository
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"gorm.io/gorm"
|
||||
|
||||
"hz-gitea.sea-studio.top/canglan/SharedClassManager/internal/model"
|
||||
)
|
||||
|
||||
// AttendanceRepo 考勤数据访问层
|
||||
type AttendanceRepo struct {
|
||||
db *gorm.DB
|
||||
}
|
||||
|
||||
// NewAttendanceRepo 创建考勤 Repository
|
||||
func NewAttendanceRepo(db *gorm.DB) *AttendanceRepo {
|
||||
return &AttendanceRepo{db: db}
|
||||
}
|
||||
|
||||
// GetStudentRecords 获取学生考勤记录
|
||||
func (r *AttendanceRepo) GetStudentRecords(studentID int, month string) ([]model.AttendanceRecord, error) {
|
||||
var records []model.AttendanceRecord
|
||||
query := r.db.Where("student_id = ?", studentID)
|
||||
|
||||
if month != "" {
|
||||
query = query.Where("DATE_FORMAT(date, '%Y-%m') = ?", month)
|
||||
}
|
||||
|
||||
if err := query.Order("date DESC").Find(&records).Error; err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return records, nil
|
||||
}
|
||||
|
||||
// GetClassRecords 获取班级考勤记录(支持多种过滤条件)
|
||||
func (r *AttendanceRepo) GetClassRecords(classID int, date string, studentID int, slot string) ([]model.AttendanceRecord, error) {
|
||||
var records []model.AttendanceRecord
|
||||
query := r.db.Table("attendance_records ar").
|
||||
Select("ar.*, s.name as student_name, s.student_no").
|
||||
Joins("JOIN students s ON ar.student_id = s.student_id").
|
||||
Where("1 = 1")
|
||||
|
||||
if classID > 0 {
|
||||
query = query.Where("s.class_id = ?", classID)
|
||||
}
|
||||
if date != "" {
|
||||
query = query.Where("ar.date = ?", date)
|
||||
}
|
||||
if studentID > 0 {
|
||||
query = query.Where("ar.student_id = ?", studentID)
|
||||
}
|
||||
if slot != "" {
|
||||
query = query.Where("ar.slot = ?", slot)
|
||||
}
|
||||
|
||||
if err := query.Order("ar.date DESC, s.student_no").Find(&records).Error; err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return records, nil
|
||||
}
|
||||
|
||||
// CreateRecordResult 创建或更新考勤记录的结果
|
||||
type CreateRecordResult struct {
|
||||
AttendanceID int
|
||||
IsUpdate bool
|
||||
OldDeductionApplied int8
|
||||
OldDeductionRecordID *int64
|
||||
}
|
||||
|
||||
// CreateRecord 创建或更新考勤记录(存在则更新),使用事务+行锁防止并发竞态
|
||||
func (r *AttendanceRepo) CreateRecord(record *model.AttendanceRecord) (*CreateRecordResult, error) {
|
||||
var result CreateRecordResult
|
||||
err := r.db.Transaction(func(tx *gorm.DB) error {
|
||||
// 使用 SELECT ... FOR UPDATE 锁定记录,防止并发请求同时判定为"不存在"
|
||||
var existing model.AttendanceRecord
|
||||
findErr := tx.Set("gorm:query_option", "FOR UPDATE").
|
||||
Where("student_id = ? AND date = ? AND slot = ?",
|
||||
record.StudentID, record.Date, record.Slot).
|
||||
First(&existing).Error
|
||||
|
||||
if findErr == nil {
|
||||
// 更新已有记录
|
||||
if updateErr := tx.Model(&existing).Updates(map[string]interface{}{
|
||||
"status": record.Status,
|
||||
"reason": record.Reason,
|
||||
"recorder_id": record.RecorderID,
|
||||
}).Error; updateErr != nil {
|
||||
return updateErr
|
||||
}
|
||||
result = CreateRecordResult{
|
||||
AttendanceID: existing.AttendanceID,
|
||||
IsUpdate: true,
|
||||
OldDeductionApplied: existing.DeductionApplied,
|
||||
OldDeductionRecordID: existing.DeductionRecordID,
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
if findErr != gorm.ErrRecordNotFound {
|
||||
return findErr
|
||||
}
|
||||
|
||||
// 插入新记录
|
||||
if createErr := tx.Create(record).Error; createErr != nil {
|
||||
return createErr
|
||||
}
|
||||
result = CreateRecordResult{
|
||||
AttendanceID: record.AttendanceID,
|
||||
IsUpdate: false,
|
||||
}
|
||||
return nil
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &result, nil
|
||||
}
|
||||
|
||||
// GetAttendanceStatsBySemester 批量查询学期内所有学生的考勤统计
|
||||
func (r *AttendanceRepo) GetAttendanceStatsBySemester(semesterID int, startDate, endDate string) ([]struct {
|
||||
StudentID int
|
||||
Status string
|
||||
Count int64
|
||||
}, error) {
|
||||
var stats []struct {
|
||||
StudentID int
|
||||
Status string
|
||||
Count int64
|
||||
}
|
||||
err := r.db.Model(&model.AttendanceRecord{}).
|
||||
Select("student_id, status, COUNT(*) as count").
|
||||
Where("semester_id = ? OR (semester_id IS NULL AND `date` BETWEEN ? AND ?)", semesterID, startDate, endDate).
|
||||
Group("student_id, status").
|
||||
Find(&stats).Error
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return stats, nil
|
||||
}
|
||||
|
||||
// GetAttendanceStatsByDateRange 通过日期范围查询学生考勤统计
|
||||
func (r *AttendanceRepo) GetAttendanceStatsByDateRange(startDate, endDate time.Time, classID int) ([]struct {
|
||||
StudentID int
|
||||
Status string
|
||||
Count int64
|
||||
}, error) {
|
||||
var stats []struct {
|
||||
StudentID int
|
||||
Status string
|
||||
Count int64
|
||||
}
|
||||
query := r.db.Model(&model.AttendanceRecord{}).
|
||||
Select("student_id, status, COUNT(*) as count").
|
||||
Where("date BETWEEN ? AND ?", startDate, endDate)
|
||||
|
||||
if classID > 0 {
|
||||
query = query.Where("student_id IN (SELECT student_id FROM students WHERE class_id = ?)", classID)
|
||||
}
|
||||
|
||||
err := query.Group("student_id, status").Find(&stats).Error
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return stats, nil
|
||||
}
|
||||
|
||||
// AssociateSemester 将考勤记录关联到学期
|
||||
func (r *AttendanceRepo) AssociateSemester(attendanceID int, semesterID int) error {
|
||||
return r.db.Model(&model.AttendanceRecord{}).
|
||||
Where("attendance_id = ? AND semester_id IS NULL", attendanceID).
|
||||
Update("semester_id", semesterID).Error
|
||||
}
|
||||
Reference in New Issue
Block a user