feat: 多班级版 v2.0 - Go后端重写 + 43轮代码审查

- 后端从 Python FastAPI 重写为 Go Gin(端口 56789)
- 多班级完全隔离
- 超级管理员独立登录
- 课代表作业管理、排行榜分项排行
- 角色加减分上下限可配置
- 家长改密功能(可开关)
- 周度/月度重置功能
- MySQL 5.7 兼容
- 43轮代码审查+全部修复
- Apache 2.0 许可证
This commit is contained in:
2026-06-22 10:06:10 +08:00
parent 4084afc53c
commit d6dec878bd
214 changed files with 12622 additions and 9725 deletions

View File

@@ -0,0 +1,230 @@
// ===========================================
// 多班级版班级管理系统 - 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
}