feat: 多班级版 v2.0 - Go后端重写 + 43轮代码审查
- 后端从 Python FastAPI 重写为 Go Gin(端口 56789) - 多班级完全隔离 - 超级管理员独立登录 - 课代表作业管理、排行榜分项排行 - 角色加减分上下限可配置 - 家长改密功能(可开关) - 周度/月度重置功能 - MySQL 5.7 兼容 - 43轮代码审查+全部修复 - Apache 2.0 许可证
This commit is contained in:
294
backend-go/internal/repository/conduct_repo.go
Normal file
294
backend-go/internal/repository/conduct_repo.go
Normal file
@@ -0,0 +1,294 @@
|
||||
// ===========================================
|
||||
// 多班级版班级管理系统 - Go 后端
|
||||
//
|
||||
// 开发者: Canglan
|
||||
// 联系方式: admin@sea-studio.top
|
||||
// 版权归属: Sea Network Technology Studio
|
||||
// 许可证: Apache License 2.0
|
||||
//
|
||||
// 版权所有 © Sea Network Technology Studio
|
||||
// ===========================================
|
||||
|
||||
package repository
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"gorm.io/gorm"
|
||||
|
||||
"hz-gitea.sea-studio.top/canglan/SharedClassManager/internal/model"
|
||||
)
|
||||
|
||||
// ConductRepo 操行分记录数据访问层
|
||||
type ConductRepo struct {
|
||||
db *gorm.DB
|
||||
}
|
||||
|
||||
// NewConductRepo 创建操行分 Repository
|
||||
func NewConductRepo(db *gorm.DB) *ConductRepo {
|
||||
return &ConductRepo{db: db}
|
||||
}
|
||||
|
||||
// CreateRecord 创建操行分记录
|
||||
func (r *ConductRepo) CreateRecord(record *model.ConductRecord) (int64, error) {
|
||||
if err := r.db.Create(record).Error; err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return record.RecordID, nil
|
||||
}
|
||||
|
||||
// GetRecordByID 根据ID获取记录(含学生信息)
|
||||
func (r *ConductRepo) GetRecordByID(recordID int64) (*model.ConductRecord, error) {
|
||||
var record model.ConductRecord
|
||||
if err := r.db.Table("conduct_records cr").
|
||||
Select("cr.*, s.name as student_name, s.total_points").
|
||||
Joins("JOIN students s ON cr.student_id = s.student_id").
|
||||
Where("cr.record_id = ?", recordID).
|
||||
First(&record).Error; err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &record, nil
|
||||
}
|
||||
|
||||
// CountStudentRecords 统计学生操行分记录总数
|
||||
func (r *ConductRepo) CountStudentRecords(studentID int, includeRevoked bool, startDate, endDate string, recorderID int) (int64, error) {
|
||||
var count int64
|
||||
query := r.db.Model(&model.ConductRecord{}).Where("student_id = ?", studentID)
|
||||
|
||||
if !includeRevoked {
|
||||
query = query.Where("is_revoked = 0")
|
||||
}
|
||||
if startDate != "" {
|
||||
query = query.Where("DATE(created_at) >= ?", startDate)
|
||||
}
|
||||
if endDate != "" {
|
||||
query = query.Where("DATE(created_at) <= ?", endDate)
|
||||
}
|
||||
if recorderID > 0 {
|
||||
query = query.Where("recorder_id = ?", recorderID)
|
||||
}
|
||||
|
||||
if err := query.Count(&count).Error; err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return count, nil
|
||||
}
|
||||
|
||||
// GetStudentRecords 获取学生操行分记录
|
||||
func (r *ConductRepo) GetStudentRecords(studentID int, limit, offset int, includeRevoked bool, startDate, endDate string, recorderID int) ([]model.ConductRecord, error) {
|
||||
var records []model.ConductRecord
|
||||
query := r.db.Table("conduct_records cr").
|
||||
Select("cr.*, u.real_name as recorder_real").
|
||||
Joins("LEFT JOIN users u ON cr.recorder_id = u.user_id").
|
||||
Where("cr.student_id = ?", studentID)
|
||||
|
||||
if !includeRevoked {
|
||||
query = query.Where("cr.is_revoked = 0")
|
||||
}
|
||||
if startDate != "" {
|
||||
query = query.Where("DATE(cr.created_at) >= ?", startDate)
|
||||
}
|
||||
if endDate != "" {
|
||||
query = query.Where("DATE(cr.created_at) <= ?", endDate)
|
||||
}
|
||||
if recorderID > 0 {
|
||||
query = query.Where("cr.recorder_id = ?", recorderID)
|
||||
}
|
||||
|
||||
if err := query.Order("cr.created_at DESC").
|
||||
Limit(limit).
|
||||
Offset(offset).
|
||||
Find(&records).Error; err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return records, nil
|
||||
}
|
||||
|
||||
// GetAllRecords 获取所有记录(管理员用,支持多种过滤条件)
|
||||
func (r *ConductRepo) GetAllRecords(classID int, limit, offset int, startDate, endDate string,
|
||||
studentID int, includeRevoked bool, relatedType, reasonPrefix string,
|
||||
isRevoked *int, reasonSearch string) ([]model.ConductRecord, error) {
|
||||
|
||||
var records []model.ConductRecord
|
||||
query := r.db.Table("conduct_records cr").
|
||||
Select("cr.*, s.name as student_name, s.student_no, s.class_id, u.real_name as recorder_real, ru.real_name as revoker_name").
|
||||
Joins("JOIN students s ON cr.student_id = s.student_id").
|
||||
Joins("JOIN users u ON cr.recorder_id = u.user_id").
|
||||
Joins("LEFT JOIN users ru ON cr.revoked_by = ru.user_id").
|
||||
Where("1 = 1")
|
||||
|
||||
if !includeRevoked {
|
||||
query = query.Where("cr.is_revoked = 0")
|
||||
}
|
||||
if classID > 0 {
|
||||
query = query.Where("s.class_id = ?", classID)
|
||||
}
|
||||
if studentID > 0 {
|
||||
query = query.Where("cr.student_id = ?", studentID)
|
||||
}
|
||||
if startDate != "" {
|
||||
query = query.Where("DATE(cr.created_at) >= ?", startDate)
|
||||
}
|
||||
if endDate != "" {
|
||||
query = query.Where("DATE(cr.created_at) <= ?", endDate)
|
||||
}
|
||||
if relatedType != "" {
|
||||
query = query.Where("cr.related_type = ?", relatedType)
|
||||
}
|
||||
if reasonPrefix != "" {
|
||||
query = query.Where("cr.reason LIKE ?", fmt.Sprintf("%s%%", reasonPrefix))
|
||||
}
|
||||
if reasonSearch != "" {
|
||||
escaped := strings.NewReplacer("\\", "\\\\", "%", "\\%", "_", "\\_").Replace(reasonSearch)
|
||||
query = query.Where("cr.reason LIKE ?", fmt.Sprintf("%%%s%%", escaped))
|
||||
}
|
||||
if isRevoked != nil {
|
||||
query = query.Where("cr.is_revoked = ?", *isRevoked)
|
||||
}
|
||||
|
||||
if err := query.Order("cr.created_at DESC").
|
||||
Limit(limit).
|
||||
Offset(offset).
|
||||
Find(&records).Error; err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return records, nil
|
||||
}
|
||||
|
||||
// CountAllRecords 统计记录总数(与 GetAllRecords 使用相同过滤条件)
|
||||
func (r *ConductRepo) CountAllRecords(classID int, startDate, endDate string,
|
||||
studentID int, includeRevoked bool, relatedType, reasonPrefix string,
|
||||
isRevoked *int, reasonSearch string) (int64, error) {
|
||||
|
||||
var count int64
|
||||
query := r.db.Table("conduct_records cr").
|
||||
Joins("JOIN students s ON cr.student_id = s.student_id").
|
||||
Where("1 = 1")
|
||||
|
||||
if !includeRevoked {
|
||||
query = query.Where("cr.is_revoked = 0")
|
||||
}
|
||||
if classID > 0 {
|
||||
query = query.Where("s.class_id = ?", classID)
|
||||
}
|
||||
if studentID > 0 {
|
||||
query = query.Where("cr.student_id = ?", studentID)
|
||||
}
|
||||
if startDate != "" {
|
||||
query = query.Where("DATE(cr.created_at) >= ?", startDate)
|
||||
}
|
||||
if endDate != "" {
|
||||
query = query.Where("DATE(cr.created_at) <= ?", endDate)
|
||||
}
|
||||
if relatedType != "" {
|
||||
query = query.Where("cr.related_type = ?", relatedType)
|
||||
}
|
||||
if reasonPrefix != "" {
|
||||
query = query.Where("cr.reason LIKE ?", fmt.Sprintf("%s%%", reasonPrefix))
|
||||
}
|
||||
if reasonSearch != "" {
|
||||
escaped := strings.NewReplacer("\\", "\\\\", "%", "\\%", "_", "\\_").Replace(reasonSearch)
|
||||
query = query.Where("cr.reason LIKE ?", fmt.Sprintf("%%%s%%", escaped))
|
||||
}
|
||||
if isRevoked != nil {
|
||||
query = query.Where("cr.is_revoked = ?", *isRevoked)
|
||||
}
|
||||
|
||||
if err := query.Count(&count).Error; err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return count, nil
|
||||
}
|
||||
|
||||
// RevokeRecord 撤销单条操行分记录
|
||||
func (r *ConductRepo) RevokeRecord(recordID int64, revokerID int) error {
|
||||
return r.db.Model(&model.ConductRecord{}).
|
||||
Where("record_id = ? AND is_revoked = 0", recordID).
|
||||
Updates(map[string]interface{}{
|
||||
"is_revoked": 1,
|
||||
"revoked_by": revokerID,
|
||||
}).Error
|
||||
}
|
||||
|
||||
// BatchRevokeRecords 批量撤销记录
|
||||
func (r *ConductRepo) BatchRevokeRecords(recordIDs []int64, revokerID int) (int64, error) {
|
||||
result := r.db.Model(&model.ConductRecord{}).
|
||||
Where("record_id IN ? AND is_revoked = 0", recordIDs).
|
||||
Updates(map[string]interface{}{
|
||||
"is_revoked": 1,
|
||||
"revoked_by": revokerID,
|
||||
"revoked_at": time.Now(),
|
||||
})
|
||||
if result.Error != nil {
|
||||
return 0, result.Error
|
||||
}
|
||||
return result.RowsAffected, nil
|
||||
}
|
||||
|
||||
// BatchRestoreRecords 批量反撤销记录
|
||||
func (r *ConductRepo) BatchRestoreRecords(recordIDs []int64) (int64, error) {
|
||||
result := r.db.Model(&model.ConductRecord{}).
|
||||
Where("record_id IN ? AND is_revoked = 1", recordIDs).
|
||||
Updates(map[string]interface{}{
|
||||
"is_revoked": 0,
|
||||
"revoked_by": nil,
|
||||
"revoked_at": nil,
|
||||
})
|
||||
if result.Error != nil {
|
||||
return 0, result.Error
|
||||
}
|
||||
return result.RowsAffected, nil
|
||||
}
|
||||
|
||||
// AssociateSemester 将记录关联到学期
|
||||
func (r *ConductRepo) AssociateSemester(recordID int64, semesterID int) error {
|
||||
return r.db.Model(&model.ConductRecord{}).
|
||||
Where("record_id = ? AND semester_id IS NULL", recordID).
|
||||
Update("semester_id", semesterID).Error
|
||||
}
|
||||
|
||||
// GetHomeworkRecords 获取学生作业相关的操行分记录
|
||||
func (r *ConductRepo) GetHomeworkRecords(studentID int) ([]model.ConductRecord, error) {
|
||||
var records []model.ConductRecord
|
||||
if err := r.db.Where("student_id = ? AND related_type = 'homework' AND is_revoked = 0", studentID).
|
||||
Order("created_at DESC").
|
||||
Find(&records).Error; err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return records, nil
|
||||
}
|
||||
|
||||
// GetStudentPointsByType 按 related_type 在 SQL 层聚合学生分数(避免全量加载),支持 limit 限制返回数量
|
||||
func (r *ConductRepo) GetStudentPointsByType(classID int, relatedType string, limit int) ([]struct {
|
||||
StudentID int
|
||||
StudentNo string
|
||||
Name string
|
||||
TotalPoints int
|
||||
}, error) {
|
||||
var results []struct {
|
||||
StudentID int
|
||||
StudentNo string
|
||||
Name string
|
||||
TotalPoints int
|
||||
}
|
||||
err := r.db.Table("conduct_records cr").
|
||||
Select("cr.student_id, s.student_no, s.name, SUM(cr.points_change) as total_points").
|
||||
Joins("JOIN students s ON cr.student_id = s.student_id").
|
||||
Where("s.class_id = ? AND s.status = 1 AND cr.related_type = ? AND cr.is_revoked = 0", classID, relatedType).
|
||||
Group("cr.student_id, s.student_no, s.name").
|
||||
Order("total_points DESC").
|
||||
Limit(limit).
|
||||
Find(&results).Error
|
||||
return results, err
|
||||
}
|
||||
|
||||
// GetStudentTotalPoints 获取学生当前总分
|
||||
func (r *ConductRepo) GetStudentTotalPoints(studentID int) (int, error) {
|
||||
var student model.Student
|
||||
if err := r.db.Where("student_id = ?", studentID).First(&student).Error; err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return student.TotalPoints, nil
|
||||
}
|
||||
Reference in New Issue
Block a user