feat: 多班级版班级管理系统 v2.0

技术栈:Go (Gin + GORM) + PHP + MySQL 5.7 + Redis

主要功能:
- 多班级完全隔离(class_id 贯穿全系统)
- 后端 Go Gin(端口 56789),Nginx 反代
- 超级管理员独立登录(env 配置,默认账密 admin/Admin123)
- bcrypt 密码加密(无 PASSWORD_SALT)
- 科任老师/课代表新角色
- 课代表作业管理页面
- 排行榜分项排行(操行分/考勤/作业)
- 角色加减分上下限由班主任配置
- 家长改密功能(可开关)
- 班级角色按需开关
- 宿舍号格式:南0-000
- 周度/月度重置功能
- MySQL 5.7 兼容
- 43 轮代码审查 + 全部修复

开发者: Canglan
版权归属: Sea Network Technology Studio
许可证: Apache License 2.0
This commit is contained in:
2026-06-22 10:21:52 +08:00
commit c6db68a9f4
135 changed files with 19933 additions and 0 deletions

View File

@@ -0,0 +1,143 @@
// ===========================================
// 多班级版班级管理系统 - Go 后端
//
// 开发者: Canglan
// 联系方式: admin@sea-studio.top
// 版权归属: Sea Network Technology Studio
// 许可证: Apache License 2.0
//
// 版权所有 © Sea Network Technology Studio
// ===========================================
package handler
import (
"time"
"github.com/gin-gonic/gin"
"hz-gitea.sea-studio.top/canglan/SharedClassManager/internal/middleware"
"hz-gitea.sea-studio.top/canglan/SharedClassManager/internal/model"
"hz-gitea.sea-studio.top/canglan/SharedClassManager/internal/repository"
"hz-gitea.sea-studio.top/canglan/SharedClassManager/internal/schema"
"hz-gitea.sea-studio.top/canglan/SharedClassManager/internal/service"
"hz-gitea.sea-studio.top/canglan/SharedClassManager/pkg/response"
)
// CadreHandler 课代表处理器
type CadreHandler struct {
assignmentRepo *repository.AssignmentRepo
conductService *service.ConductService
adminRoleRepo *repository.AdminRoleRepo
}
// NewCadreHandler 创建课代表处理器
func NewCadreHandler(assignmentRepo *repository.AssignmentRepo, conductService *service.ConductService, adminRoleRepo *repository.AdminRoleRepo) *CadreHandler {
return &CadreHandler{assignmentRepo: assignmentRepo, conductService: conductService, adminRoleRepo: adminRoleRepo}
}
// HomeworkList 课代表查看作业列表
func (h *CadreHandler) HomeworkList(c *gin.Context) {
classID := middleware.GetClassID(c)
var query schema.CadreHomeworkQuery
if err := c.ShouldBindQuery(&query); err != nil {
response.BadRequest(c, "参数错误")
return
}
subjectID := 0
if query.SubjectID != nil {
subjectID = *query.SubjectID
}
assignments, total, err := h.assignmentRepo.GetAssignmentsByClass(classID, subjectID, query.Page, query.PageSize)
if err != nil {
response.InternalError(c, "获取作业列表失败")
return
}
response.Paginated(c, assignments, total, query.Page, query.PageSize)
}
// HomeworkSubmit 课代表发布作业
func (h *CadreHandler) HomeworkSubmit(c *gin.Context) {
var req schema.CadreHomeworkSubmitRequest
if err := c.ShouldBindJSON(&req); err != nil {
response.BadRequest(c, "参数错误")
return
}
classID := middleware.GetClassID(c)
userID := middleware.GetUserID(c)
// 从管理员角色中获取课代表关联的科目 ID
adminRole, err := h.adminRoleRepo.GetByUserID(userID)
if err != nil || adminRole == nil || adminRole.SubjectID == nil {
response.BadRequest(c, "无法获取课代表关联的科目信息")
return
}
deadline, err := time.Parse("2006-01-02", req.Deadline)
if err != nil {
response.BadRequest(c, "日期格式错误")
return
}
assignment := &model.Assignment{
ClassID: classID,
SubjectID: *adminRole.SubjectID,
Title: req.Title,
Description: &req.Description,
Deadline: deadline,
CreatedBy: userID,
}
assignmentID, err := h.assignmentRepo.CreateAssignment(assignment)
if err != nil {
response.InternalError(c, "发布作业失败")
return
}
response.Success(c, gin.H{
"assignment_id": assignmentID,
}, "发布成功")
}
// AddConductPoints 课代表登记缺交(仅允许作业相关的扣分操作)
func (h *CadreHandler) AddConductPoints(c *gin.Context) {
var req schema.ConductAddRequest
if err := c.ShouldBindJSON(&req); err != nil {
response.BadRequest(c, "参数错误")
return
}
// 课代表只允许扣分操作
if req.PointsChange >= 0 {
response.BadRequest(c, "课代表只能进行扣分操作")
return
}
classID := middleware.GetClassID(c)
userID := middleware.GetUserID(c)
realName := middleware.GetRealName(c)
result, err := h.conductService.CadreAddPoints(
req.StudentIDs, req.PointsChange, req.Reason,
userID, realName, classID, "homework",
)
if err != nil {
response.InternalError(c, err.Error())
return
}
if success, _ := result["success"].(bool); !success {
msg, _ := result["message"].(string)
if msg == "" {
msg = "操作失败"
}
response.BadRequest(c, msg)
return
}
response.Success(c, result, "操作成功")
}