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,224 @@
// ===========================================
// 多班级版班级管理系统 - Go 后端
//
// 开发者: Canglan
// 联系方式: admin@sea-studio.top
// 版权归属: Sea Network Technology Studio
// 许可证: Apache License 2.0
//
// 版权所有 © Sea Network Technology Studio
// ===========================================
package service
import (
"context"
"fmt"
"hz-gitea.sea-studio.top/canglan/SharedClassManager/internal/config"
"hz-gitea.sea-studio.top/canglan/SharedClassManager/internal/model"
"hz-gitea.sea-studio.top/canglan/SharedClassManager/internal/repository"
appJwt "hz-gitea.sea-studio.top/canglan/SharedClassManager/pkg/jwt"
"hz-gitea.sea-studio.top/canglan/SharedClassManager/pkg/database"
)
// ClassService 班级服务
type ClassService struct {
classRepo *repository.ClassRepo
userRepo *repository.UserRepo
adminRoleRepo *repository.AdminRoleRepo
}
// NewClassService 创建班级服务
func NewClassService(
classRepo *repository.ClassRepo,
userRepo *repository.UserRepo,
adminRoleRepo *repository.AdminRoleRepo,
) *ClassService {
return &ClassService{
classRepo: classRepo,
userRepo: userRepo,
adminRoleRepo: adminRoleRepo,
}
}
// ListClasses 获取班级列表
func (s *ClassService) ListClasses(includeDisabled bool) (map[string]interface{}, error) {
classes, err := s.classRepo.GetAll(includeDisabled)
if err != nil {
return nil, err
}
for i := range classes {
count, _ := s.classRepo.GetStudentCount(classes[i].ClassID)
classes[i].StudentCount = count
}
return map[string]interface{}{
"classes": classes,
"total": len(classes),
}, nil
}
// GetClassDetail 获取班级详情
func (s *ClassService) GetClassDetail(classID int) (map[string]interface{}, error) {
cls, err := s.classRepo.GetByID(classID)
if err != nil {
return nil, err
}
cls.StudentCount, _ = s.classRepo.GetStudentCount(classID)
return map[string]interface{}{"class": cls}, nil
}
// CreateClass 创建班级
func (s *ClassService) CreateClass(className string, grade, description *string) (map[string]interface{}, error) {
existing, _ := s.classRepo.GetByName(className)
if existing != nil {
return map[string]interface{}{"success": false, "message": "班级名称已存在"}, nil
}
cls := &model.Class{
ClassName: className,
Grade: grade,
Description: description,
Status: 1,
}
classID, err := s.classRepo.Create(cls)
if err != nil {
return nil, err
}
return map[string]interface{}{
"success": true,
"class_id": classID,
"message": "班级创建成功",
}, nil
}
// UpdateClass 更新班级
func (s *ClassService) UpdateClass(classID int, className, grade, description *string, status *int8) error {
existing, err := s.classRepo.GetByID(classID)
if err != nil {
return fmt.Errorf("班级不存在")
}
updates := make(map[string]interface{})
if className != nil && *className != existing.ClassName {
nameExists, _ := s.classRepo.GetByName(*className)
if nameExists != nil {
return fmt.Errorf("班级名称已存在")
}
updates["class_name"] = *className
}
if grade != nil {
updates["grade"] = *grade
}
if description != nil {
updates["description"] = *description
}
if status != nil {
updates["status"] = *status
}
return s.classRepo.Update(classID, updates)
}
// DeleteClass 删除班级
func (s *ClassService) DeleteClass(classID int) error {
hasStudents, _ := s.classRepo.HasActiveStudents(classID)
if hasStudents {
return fmt.Errorf("该班级下还有学生,无法删除")
}
return s.classRepo.Delete(classID)
}
// SwitchClass 切换班级上下文(超级管理员)
func (s *ClassService) SwitchClass(userID int, classID int) (map[string]interface{}, error) {
cfg := config.AppConfig
cls, err := s.classRepo.GetByID(classID)
if err != nil {
return nil, fmt.Errorf("班级不存在")
}
user, err := s.userRepo.GetByUserID(userID)
if err != nil {
return nil, fmt.Errorf("用户不存在")
}
// 查询目标班级中该用户的角色
var role string
if user.UserType == "super_admin" {
role = "系统管理员"
} else {
adminRole, _ := s.adminRoleRepo.GetByUserIDAndClass(userID, classID)
if adminRole != nil {
role = adminRole.RoleType
}
}
// 生成新 Token更新 class_id
token, err := appJwt.CreateToken(
user.UserID, user.Username, user.UserType,
user.StudentID, role, user.RealName, &classID,
user.NeedChangePassword == 1,
)
if err != nil {
return nil, fmt.Errorf("生成令牌失败")
}
ctx := context.Background()
_ = database.SetUserToken(ctx, userID, token, cfg.JWTIdleTimeoutMinutes)
return map[string]interface{}{
"token": token,
"class_id": classID,
"class_name": cls.ClassName,
}, nil
}
// GetSettings 获取班级设置
func (s *ClassService) GetSettings(classID int) (map[string]interface{}, error) {
settings, err := s.classRepo.GetSettings(classID)
if err != nil {
return nil, err
}
result := make(map[string]string)
for _, setting := range settings {
result[setting.SettingKey] = setting.SettingValue
}
return map[string]interface{}{"settings": result}, nil
}
// SaveSetting 保存班级设置
func (s *ClassService) SaveSetting(classID int, key, value string) error {
return s.classRepo.SaveSetting(classID, key, value)
}
// GetFeatures 获取班级功能开关
func (s *ClassService) GetFeatures(classID int) (map[string]interface{}, error) {
features, err := s.classRepo.GetFeatures(classID)
if err != nil {
return nil, err
}
result := make(map[string]int8)
for _, f := range features {
result[f.FeatureKey] = f.Enabled
}
return map[string]interface{}{"features": result}, nil
}
// SaveFeature 保存班级功能开关
func (s *ClassService) SaveFeature(classID int, featureKey string, enabled int8) error {
return s.classRepo.SaveFeature(classID, featureKey, enabled)
}
// IsFeatureEnabled 检查功能开关是否启用
func (s *ClassService) IsFeatureEnabled(classID int, featureKey string) bool {
feature, err := s.classRepo.GetFeature(classID, featureKey)
if err != nil || feature == nil {
return true // 默认启用
}
return feature.Enabled == 1
}