// =========================================== // 多班级版班级管理系统 - Go 后端 // // 开发者: Canglan // 联系方式: admin@sea-studio.top // 版权归属: Sea Network Technology Studio // 许可证: Apache License 2.0 // // 版权所有 © Sea Network Technology Studio // =========================================== package repository import ( "fmt" "strings" "gorm.io/gorm" "hz-gitea.sea-studio.top/canglan/SharedClassManager/internal/model" ) // StudentRepo 学生数据访问层 type StudentRepo struct { db *gorm.DB } // NewStudentRepo 创建学生 Repository func NewStudentRepo(db *gorm.DB) *StudentRepo { return &StudentRepo{db: db} } // GetByID 根据ID获取学生信息(含班级名称) func (r *StudentRepo) GetByID(studentID int) (*model.Student, error) { var student model.Student if err := r.db.Table("students s"). Select("s.*, c.class_name"). Joins("LEFT JOIN classes c ON s.class_id = c.class_id"). Where("s.student_id = ?", studentID). First(&student).Error; err != nil { return nil, err } return &student, nil } // GetByStudentNo 根据学号获取学生(可指定班级) func (r *StudentRepo) GetByStudentNo(studentNo string, classID int) (*model.Student, error) { var student model.Student query := r.db.Where("student_no = ?", studentNo) if classID > 0 { query = query.Where("class_id = ?", classID) } if err := query.First(&student).Error; err != nil { return nil, err } return &student, nil } // GetAll 获取指定班级的学生列表 func (r *StudentRepo) GetAll(classID int, includeDisabled bool) ([]model.Student, error) { var students []model.Student query := r.db.Where("class_id = ?", classID) if !includeDisabled { query = query.Where("status = 1") } if err := query.Order("student_no").Find(&students).Error; err != nil { return nil, err } return students, nil } // GetDormitoryList 获取班级内所有不重复的宿舍号列表 func (r *StudentRepo) GetDormitoryList(classID int) ([]string, error) { var dormitories []string err := r.db.Model(&model.Student{}). Where("class_id = ? AND status = 1 AND dormitory_number IS NOT NULL AND dormitory_number != ''", classID). Distinct("dormitory_number"). Order("dormitory_number"). Pluck("dormitory_number", &dormitories).Error if err != nil { return nil, err } return dormitories, nil } // Create 创建学生记录 func (r *StudentRepo) Create(student *model.Student) (int, error) { if err := r.db.Create(student).Error; err != nil { return 0, err } return student.StudentID, nil } // Update 更新学生信息(仅更新非零值字段) func (r *StudentRepo) Update(studentID int, updates map[string]interface{}) error { if len(updates) == 0 { return nil } return r.db.Model(&model.Student{}). Where("student_id = ?", studentID). Updates(updates).Error } // SoftDelete 软删除学生 func (r *StudentRepo) SoftDelete(studentID int) error { return r.db.Model(&model.Student{}). Where("student_id = ?", studentID). Update("status", 0).Error } // UpdateTotalPoints 更新学生总分(增量更新,下限保护为 0) func (r *StudentRepo) UpdateTotalPoints(studentID int, pointsChange int) error { return r.db.Model(&model.Student{}). Where("student_id = ?", studentID). Update("total_points", gorm.Expr("GREATEST(total_points + ?, 0)", pointsChange)).Error } // GetRanking 获取班级内学生排行 func (r *StudentRepo) GetRanking(classID int, limit int) ([]model.Student, error) { var students []model.Student if err := r.db.Where("status = 1 AND class_id = ?", classID). Order("total_points DESC, student_id ASC"). Limit(limit). Find(&students).Error; err != nil { return nil, err } return students, nil } // GetTotalCount 获取班级内活跃学生总数 func (r *StudentRepo) GetTotalCount(classID int) (int64, error) { var count int64 if err := r.db.Model(&model.Student{}). Where("status = 1 AND class_id = ?", classID). Count(&count).Error; err != nil { return 0, err } return count, nil } // ListByClass 分页获取班级学生列表(支持搜索和宿舍号过滤) func (r *StudentRepo) ListByClass(classID int, page, pageSize int, search, dormitoryNumber string) ([]model.Student, int64, error) { var students []model.Student var total int64 query := r.db.Model(&model.Student{}).Where("status = 1 AND class_id = ?", classID) if search != "" { escaped := strings.NewReplacer("\\", "\\\\", "%", "\\%", "_", "\\_").Replace(search) searchPattern := fmt.Sprintf("%%%s%%", escaped) query = query.Where("student_no LIKE ? OR name LIKE ?", searchPattern, searchPattern) } if dormitoryNumber != "" { query = query.Where("dormitory_number = ?", dormitoryNumber) } // 获取总数 if err := query.Count(&total).Error; err != nil { return nil, 0, err } // 分页查询 offset := (page - 1) * pageSize if err := query.Order("student_no"). Limit(pageSize). Offset(offset). Find(&students).Error; err != nil { return nil, 0, err } return students, total, nil } // BatchCreate 批量创建学生 func (r *StudentRepo) BatchCreate(students []model.Student) error { return r.db.Create(&students).Error } // GetStudentNosByClass 获取指定班级所有学生学号(用于批量导入去重) func (r *StudentRepo) GetStudentNosByClass(classID int) ([]string, error) { var studentNos []string if err := r.db.Model(&model.Student{}). Where("class_id = ?", classID). Pluck("student_no", &studentNos).Error; err != nil { return nil, err } return studentNos, nil } // ResetPoints 重置班级内所有学生的操行分为初始值 func (r *StudentRepo) ResetPoints(classID int, initialPoints int) error { return r.db.Model(&model.Student{}). Where("class_id = ? AND status = 1", classID). Update("total_points", initialPoints).Error } // GetByParentAccount 根据家长账号查找学生 func (r *StudentRepo) GetByParentAccount(parentAccount string) (*model.Student, error) { var student model.Student if err := r.db.Where("parent_account = ? AND status = 1", parentAccount).First(&student).Error; err != nil { return nil, err } return &student, nil } // GetRankByStudentID 使用密集排名(dense rank)计算学生排名:相同分数同名次,后续名次不跳过 func (r *StudentRepo) GetRankByStudentID(classID, studentID int) (int, error) { var student model.Student if err := r.db.Select("total_points").Where("student_id = ?", studentID).First(&student).Error; err != nil { return 0, err } var distinctHigherCount int64 if err := r.db.Raw("SELECT COUNT(DISTINCT total_points) FROM students WHERE status = 1 AND class_id = ? AND total_points > ?", classID, student.TotalPoints).Scan(&distinctHigherCount).Error; err != nil { return 0, err } return int(distinctHigherCount) + 1, nil } // GetStudentsByClassID 获取班级内所有活跃学生(用于归档等批量操作) func (r *StudentRepo) GetStudentsByClassID(classID int) ([]model.Student, error) { var students []model.Student if err := r.db.Where("class_id = ? AND status = 1", classID). Order("total_points DESC, student_id ASC"). Find(&students).Error; err != nil { return nil, err } return students, nil }