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,602 @@
// ===========================================
// 多班级版班级管理系统 - Go 后端
//
// 开发者: Canglan
// 联系方式: admin@sea-studio.top
// 版权归属: Sea Network Technology Studio
// 许可证: Apache License 2.0
//
// 版权所有 © Sea Network Technology Studio
// ===========================================
package handler
import (
"encoding/json"
"io"
"strconv"
"github.com/gin-gonic/gin"
"hz-gitea.sea-studio.top/canglan/SharedClassManager/internal/middleware"
"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"
)
// AdminHandler 管理端处理器
type AdminHandler struct {
adminService *service.AdminService
conductService *service.ConductService
attendanceSvc *service.AttendanceService
rankingService *service.RankingService
logService *service.LogService
}
// NewAdminHandler 创建管理端处理器
func NewAdminHandler(
adminService *service.AdminService,
conductService *service.ConductService,
attendanceSvc *service.AttendanceService,
rankingService *service.RankingService,
logService *service.LogService,
) *AdminHandler {
return &AdminHandler{
adminService: adminService,
conductService: conductService,
attendanceSvc: attendanceSvc,
rankingService: rankingService,
logService: logService,
}
}
// ========== 学生管理 ==========
// GetDormitories 获取宿舍号列表
func (h *AdminHandler) GetDormitories(c *gin.Context) {
classID := middleware.GetClassID(c)
if classID == 0 {
response.BadRequest(c, "请先选择班级")
return
}
dormitories, err := h.adminService.GetDormitories(classID)
if err != nil {
response.InternalError(c, "获取宿舍号列表失败")
return
}
response.Success(c, gin.H{"dormitories": dormitories}, "操作成功")
}
// StudentList 获取学生列表
func (h *AdminHandler) StudentList(c *gin.Context) {
classID := middleware.GetClassID(c)
if classID == 0 {
response.BadRequest(c, "请先选择班级")
return
}
var query schema.StudentListQuery
if err := c.ShouldBindQuery(&query); err != nil {
response.BadRequest(c, "参数错误")
return
}
result, err := h.adminService.GetStudents(classID, query.Page, query.PageSize, query.Search, query.DormitoryNumber)
if err != nil {
response.InternalError(c, "获取学生列表失败")
return
}
response.Success(c, result, "操作成功")
}
// StudentImport 批量导入学生
func (h *AdminHandler) StudentImport(c *gin.Context) {
classID := middleware.GetClassID(c)
if classID == 0 {
response.BadRequest(c, "请先选择班级")
return
}
file, _, err := c.Request.FormFile("file")
if err != nil {
response.BadRequest(c, "请上传文件")
return
}
defer file.Close()
limitedReader := io.LimitReader(file, 5*1024*1024)
content, err := io.ReadAll(limitedReader)
if err != nil {
response.BadRequest(c, "读取文件失败")
return
}
var data struct {
Students []map[string]interface{} `json:"students"`
}
if err := json.Unmarshal(content, &data); err != nil {
response.BadRequest(c, "JSON格式错误")
return
}
if len(data.Students) == 0 {
response.BadRequest(c, "文件中没有学生数据")
return
}
result, err := h.adminService.ImportStudents(data.Students, classID)
if err != nil {
response.InternalError(c, "导入失败")
return
}
response.Success(c, result, "操作成功")
}
// StudentCreate 新增学生
func (h *AdminHandler) StudentCreate(c *gin.Context) {
classID := middleware.GetClassID(c)
if classID == 0 {
response.BadRequest(c, "请先选择班级")
return
}
var req schema.StudentCreateRequest
if err := c.ShouldBindJSON(&req); err != nil {
response.BadRequest(c, "参数错误")
return
}
result, err := h.adminService.AddStudent(req.StudentNo, req.Name, req.ParentAccount, classID, req.DormitoryNumber)
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, "学生添加成功")
}
// StudentUpdate 编辑学生
func (h *AdminHandler) StudentUpdate(c *gin.Context) {
studentID, ok := parseID(c, "student_id")
if !ok {
return
}
classID := middleware.GetClassID(c)
var req schema.StudentUpdateRequest
if err := c.ShouldBindJSON(&req); err != nil {
response.BadRequest(c, "参数错误")
return
}
if err := h.adminService.UpdateStudent(studentID, req.Name, req.ParentAccount, req.DormitoryNumber, classID); err != nil {
response.InternalError(c, err.Error())
return
}
response.SuccessWithMessage(c, "更新成功")
}
// StudentDelete 删除学生
func (h *AdminHandler) StudentDelete(c *gin.Context) {
studentID, ok := parseID(c, "student_id")
if !ok {
return
}
classID := middleware.GetClassID(c)
if err := h.adminService.DeleteStudent(studentID, classID); err != nil {
response.InternalError(c, err.Error())
return
}
response.SuccessWithMessage(c, "删除成功")
}
// ResetStudentPassword 重置学生密码
func (h *AdminHandler) ResetStudentPassword(c *gin.Context) {
studentID, ok := parseID(c, "student_id")
if !ok {
return
}
var req schema.ResetPasswordRequest
if err := c.ShouldBindJSON(&req); err != nil {
response.BadRequest(c, "参数错误")
return
}
if err := h.adminService.ResetStudentPassword(studentID, req.NewPassword); err != nil {
response.InternalError(c, err.Error())
return
}
response.SuccessWithMessage(c, "密码重置成功")
}
// ========== 操行分管理 ==========
// AddConductPoints 批量加减分
func (h *AdminHandler) AddConductPoints(c *gin.Context) {
var req schema.ConductAddRequest
if err := c.ShouldBindJSON(&req); err != nil {
response.BadRequest(c, "参数错误")
return
}
classID := middleware.GetClassID(c)
userID := middleware.GetUserID(c)
realName := middleware.GetRealName(c)
result, err := h.conductService.AddPoints(
req.StudentIDs, req.PointsChange, req.Reason,
userID, realName, classID, req.RelatedType,
)
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, "操作成功")
}
// RevokeConductRecord 撤销记录
func (h *AdminHandler) RevokeConductRecord(c *gin.Context) {
var req schema.RevokeRequest
if err := c.ShouldBindJSON(&req); err != nil {
response.BadRequest(c, "参数错误")
return
}
userID := middleware.GetUserID(c)
classID := middleware.GetClassID(c)
result, err := h.conductService.RevokeRecord(req.RecordID, userID, classID)
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.SuccessWithMessage(c, "撤销成功")
}
// RestoreConductRecord 反撤销记录
func (h *AdminHandler) RestoreConductRecord(c *gin.Context) {
var req schema.RevokeRequest
if err := c.ShouldBindJSON(&req); err != nil {
response.BadRequest(c, "参数错误")
return
}
userID := middleware.GetUserID(c)
classID := middleware.GetClassID(c)
result, err := h.conductService.RestoreRecord(req.RecordID, userID, classID)
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.SuccessWithMessage(c, "反撤销成功")
}
// GetConductHistory 操行分历史
func (h *AdminHandler) GetConductHistory(c *gin.Context) {
classID := middleware.GetClassID(c)
var query schema.ConductHistoryQuery
if err := c.ShouldBindQuery(&query); err != nil {
response.BadRequest(c, "参数错误")
return
}
result, err := h.conductService.GetHistory(
classID, query.StudentID, query.Page, query.PageSize,
query.StartDate, query.EndDate, query.RelatedType,
query.ReasonPrefix, query.IsRevoked, query.ReasonSearch,
)
if err != nil {
response.InternalError(c, err.Error())
return
}
response.Success(c, result, "操作成功")
}
// BatchRevokeConductRecords 批量撤销
func (h *AdminHandler) BatchRevokeConductRecords(c *gin.Context) {
var req schema.BatchRevokeRequest
if err := c.ShouldBindJSON(&req); err != nil {
response.BadRequest(c, "参数错误")
return
}
userID := middleware.GetUserID(c)
classID := middleware.GetClassID(c)
successCount := 0
failCount := 0
var errors []map[string]interface{}
for _, recordID := range req.RecordIDs {
result, _ := h.conductService.RevokeRecord(recordID, userID, classID)
if result != nil {
if success, _ := result["success"].(bool); success {
successCount++
} else {
failCount++
msg, _ := result["message"].(string)
errors = append(errors, map[string]interface{}{"record_id": recordID, "error": msg})
}
} else {
failCount++
errors = append(errors, map[string]interface{}{"record_id": recordID, "error": "撤销失败"})
}
}
response.Success(c, gin.H{
"success_count": successCount,
"fail_count": failCount,
"errors": errors,
}, "批量撤销完成")
}
// BatchRestoreConductRecords 批量反撤销
func (h *AdminHandler) BatchRestoreConductRecords(c *gin.Context) {
var req schema.BatchRevokeRequest
if err := c.ShouldBindJSON(&req); err != nil {
response.BadRequest(c, "参数错误")
return
}
userID := middleware.GetUserID(c)
classID := middleware.GetClassID(c)
successCount := 0
failCount := 0
var errors []map[string]interface{}
for _, recordID := range req.RecordIDs {
result, _ := h.conductService.RestoreRecord(recordID, userID, classID)
if result != nil {
if success, _ := result["success"].(bool); success {
successCount++
} else {
failCount++
msg, _ := result["message"].(string)
errors = append(errors, map[string]interface{}{"record_id": recordID, "error": msg})
}
} else {
failCount++
errors = append(errors, map[string]interface{}{"record_id": recordID, "error": "反撤销失败"})
}
}
response.Success(c, gin.H{
"success_count": successCount,
"fail_count": failCount,
"errors": errors,
}, "批量反撤销完成")
}
// ========== 考勤管理 ==========
// CreateAttendanceRecord 添加考勤
func (h *AdminHandler) CreateAttendanceRecord(c *gin.Context) {
var req schema.AttendanceCreateRequest
if err := c.ShouldBindJSON(&req); err != nil {
response.BadRequest(c, "参数错误")
return
}
userID := middleware.GetUserID(c)
classID := middleware.GetClassID(c)
result, err := h.attendanceSvc.CreateRecord(
req.StudentID, req.Date, req.Slot, req.Status,
&req.Reason, req.ApplyDeduction, req.CustomDeduction, userID, classID,
)
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
}
msg, _ := result["message"].(string)
if msg == "" {
msg = "操作成功"
}
response.SuccessWithMessage(c, msg)
}
// GetAttendanceRecords 获取考勤记录
func (h *AdminHandler) GetAttendanceRecords(c *gin.Context) {
classID := middleware.GetClassID(c)
var query schema.AttendanceQuery
if err := c.ShouldBindQuery(&query); err != nil {
response.BadRequest(c, "参数错误")
return
}
result, err := h.attendanceSvc.GetRecords(classID, query.Date, query.StudentID, query.Slot)
if err != nil {
response.InternalError(c, err.Error())
return
}
response.Success(c, result, "操作成功")
}
// ========== 管理员管理 ==========
// AdminList 管理员列表
func (h *AdminHandler) AdminList(c *gin.Context) {
classID := middleware.GetClassID(c)
result, err := h.adminService.GetAdmins(classID)
if err != nil {
response.InternalError(c, "获取管理员列表失败")
return
}
response.Success(c, result, "操作成功")
}
// AdminCreate 添加管理员
func (h *AdminHandler) AdminCreate(c *gin.Context) {
classID := middleware.GetClassID(c)
if classID == 0 {
response.BadRequest(c, "请先选择班级")
return
}
var req schema.AdminCreateRequest
if err := c.ShouldBindJSON(&req); err != nil {
response.BadRequest(c, "参数错误")
return
}
result, err := h.adminService.AddAdmin(req.Username, req.RealName, req.Password, req.RoleType, classID, req.SubjectID)
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, "管理员添加成功")
}
// AdminUpdate 更新管理员
func (h *AdminHandler) AdminUpdate(c *gin.Context) {
userID, ok := parseID(c, "user_id")
if !ok {
return
}
classID := middleware.GetClassID(c)
var req schema.AdminUpdateRequest
if err := c.ShouldBindJSON(&req); err != nil {
response.BadRequest(c, "参数错误")
return
}
if err := h.adminService.UpdateAdmin(userID, req.RealName, req.RoleType, classID, req.SubjectID); err != nil {
response.InternalError(c, err.Error())
return
}
response.SuccessWithMessage(c, "更新成功")
}
// AdminDelete 删除管理员
func (h *AdminHandler) AdminDelete(c *gin.Context) {
userID, ok := parseID(c, "user_id")
if !ok {
return
}
classID := middleware.GetClassID(c)
if err := h.adminService.DeleteAdmin(userID, classID); err != nil {
response.InternalError(c, err.Error())
return
}
response.SuccessWithMessage(c, "删除成功")
}
// AdminResetPassword 重置管理员密码
func (h *AdminHandler) AdminResetPassword(c *gin.Context) {
userID, ok := parseID(c, "user_id")
if !ok {
return
}
var req schema.ResetPasswordRequest
if err := c.ShouldBindJSON(&req); err != nil {
response.BadRequest(c, "参数错误")
return
}
if err := h.adminService.ResetAdminPassword(userID, req.NewPassword); err != nil {
response.InternalError(c, err.Error())
return
}
response.SuccessWithMessage(c, "密码重置成功")
}
// UnlockAccount 解除登录锁定
func (h *AdminHandler) UnlockAccount(c *gin.Context) {
var req schema.UnlockUserRequest
if err := c.ShouldBindJSON(&req); err != nil {
response.BadRequest(c, "参数错误")
return
}
if err := h.adminService.UnlockAccount(req.Username, c.ClientIP()); err != nil {
response.InternalError(c, "解锁失败")
return
}
response.SuccessWithMessage(c, "解锁成功")
}
// GetRankings 分项排行榜
func (h *AdminHandler) GetRankings(c *gin.Context) {
classID := middleware.GetClassID(c)
if classID == 0 {
response.BadRequest(c, "请先选择班级")
return
}
rankType := c.DefaultQuery("type", "all")
limit, _ := strconv.Atoi(c.DefaultQuery("limit", "50"))
if limit <= 0 {
limit = 50
}
if limit > 500 {
limit = 500
}
result, err := h.rankingService.GetRankings(classID, rankType, limit)
if err != nil {
response.InternalError(c, err.Error())
return
}
response.Success(c, result, "操作成功")
}

View File

@@ -0,0 +1,131 @@
// ===========================================
// 多班级版班级管理系统 - Go 后端
//
// 开发者: Canglan
// 联系方式: admin@sea-studio.top
// 版权归属: Sea Network Technology Studio
// 许可证: Apache License 2.0
//
// 版权所有 © Sea Network Technology Studio
// ===========================================
package handler
import (
"strconv"
"github.com/gin-gonic/gin"
"hz-gitea.sea-studio.top/canglan/SharedClassManager/internal/middleware"
"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"
)
// AuthHandler 认证处理器
type AuthHandler struct {
authService *service.AuthService
superAdminService *service.SuperAdminService
}
// NewAuthHandler 创建认证处理器
func NewAuthHandler(authService *service.AuthService, superAdminService *service.SuperAdminService) *AuthHandler {
return &AuthHandler{authService: authService, superAdminService: superAdminService}
}
// Login 用户登录
func (h *AuthHandler) Login(c *gin.Context) {
var req schema.LoginRequest
if err := c.ShouldBindJSON(&req); err != nil {
response.BadRequest(c, "参数错误")
return
}
ip := c.ClientIP()
userAgent := c.GetHeader("User-Agent")
result := h.authService.Login(req.Username, req.Password, ip, userAgent)
if !result.Success {
response.Unauthorized(c, result.Message)
return
}
response.Success(c, result, "登录成功")
}
// Logout 用户登出
func (h *AuthHandler) Logout(c *gin.Context) {
userID := middleware.GetUserID(c)
if err := h.authService.Logout(userID); err != nil {
response.InternalError(c, "登出失败")
return
}
response.SuccessWithMessage(c, "登出成功")
}
// ChangePassword 修改密码(超级管理员操作 super_admins 表,普通用户操作 users 表)
func (h *AuthHandler) ChangePassword(c *gin.Context) {
var req schema.ChangePasswordRequest
if err := c.ShouldBindJSON(&req); err != nil {
response.BadRequest(c, "参数错误")
return
}
userID := middleware.GetUserID(c)
userType := middleware.GetUserType(c)
// force 参数仅在用户确实需要强制改密时才允许使用
if req.Force {
if userType == "super_admin" {
// 超级管理员的 need_change_password 由 super_admin_service 处理
// force 改密时直接允许(登录时已验证 need_change_password 标记)
} else {
userInfo, err := h.authService.GetUserInfo(userID)
if err != nil {
response.InternalError(c, err.Error())
return
}
needChange, _ := userInfo["need_change_password"].(bool)
if !needChange {
response.BadRequest(c, "当前状态不允许强制修改密码")
return
}
}
}
if userType == "super_admin" {
if err := h.superAdminService.ChangePassword(userID, req.OldPassword, req.NewPassword, req.Force); err != nil {
response.BadRequest(c, err.Error())
return
}
} else {
if err := h.authService.ChangePassword(userID, req.OldPassword, req.NewPassword, req.Force); err != nil {
response.BadRequest(c, err.Error())
return
}
}
response.SuccessWithMessage(c, "密码修改成功,请重新登录")
}
// GetUserInfo 获取当前用户信息
func (h *AuthHandler) GetUserInfo(c *gin.Context) {
userID := middleware.GetUserID(c)
userInfo, err := h.authService.GetUserInfo(userID)
if err != nil {
response.InternalError(c, err.Error())
return
}
response.Success(c, userInfo, "操作成功")
}
// parseID 解析路径参数中的 ID
func parseID(c *gin.Context, key string) (int, bool) {
idStr := c.Param(key)
id, err := strconv.Atoi(idStr)
if err != nil {
response.BadRequest(c, "无效的ID参数")
return 0, false
}
return id, true
}

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, "操作成功")
}

View File

@@ -0,0 +1,271 @@
// ===========================================
// 多班级版班级管理系统 - Go 后端
//
// 开发者: Canglan
// 联系方式: admin@sea-studio.top
// 版权归属: Sea Network Technology Studio
// 许可证: Apache License 2.0
//
// 版权所有 © Sea Network Technology Studio
// ===========================================
package handler
import (
"github.com/gin-gonic/gin"
"hz-gitea.sea-studio.top/canglan/SharedClassManager/internal/middleware"
"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"
)
// ClassHandler 班级管理处理器
type ClassHandler struct {
classService *service.ClassService
}
// NewClassHandler 创建班级管理处理器
func NewClassHandler(classService *service.ClassService) *ClassHandler {
return &ClassHandler{classService: classService}
}
// ClassList 班级列表
func (h *ClassHandler) ClassList(c *gin.Context) {
includeDisabled := c.Query("include_disabled") == "true"
result, err := h.classService.ListClasses(includeDisabled)
if err != nil {
response.InternalError(c, "获取班级列表失败")
return
}
response.Success(c, result, "操作成功")
}
// ClassDetail 班级详情
func (h *ClassHandler) ClassDetail(c *gin.Context) {
classID, ok := parseID(c, "class_id")
if !ok {
return
}
result, err := h.classService.GetClassDetail(classID)
if err != nil {
response.NotFound(c, "班级不存在")
return
}
response.Success(c, result, "操作成功")
}
// ClassCreate 创建班级
func (h *ClassHandler) ClassCreate(c *gin.Context) {
var req schema.ClassCreateRequest
if err := c.ShouldBindJSON(&req); err != nil {
response.BadRequest(c, "参数错误")
return
}
result, err := h.classService.CreateClass(req.ClassName, req.Grade, req.Description)
if err != nil {
response.InternalError(c, err.Error())
return
}
if success, _ := result["success"].(bool); !success {
response.BadRequest(c, result["message"].(string))
return
}
response.Success(c, result, "班级创建成功")
}
// ClassUpdate 更新班级
func (h *ClassHandler) ClassUpdate(c *gin.Context) {
classID, ok := parseID(c, "class_id")
if !ok {
return
}
var req schema.ClassUpdateRequest
if err := c.ShouldBindJSON(&req); err != nil {
response.BadRequest(c, "参数错误")
return
}
if err := h.classService.UpdateClass(classID, req.ClassName, req.Grade, req.Description, req.Status); err != nil {
response.InternalError(c, err.Error())
return
}
response.SuccessWithMessage(c, "更新成功")
}
// ClassDelete 删除班级
func (h *ClassHandler) ClassDelete(c *gin.Context) {
classID, ok := parseID(c, "class_id")
if !ok {
return
}
if err := h.classService.DeleteClass(classID); err != nil {
response.InternalError(c, err.Error())
return
}
response.SuccessWithMessage(c, "删除成功")
}
// SwitchClass 切换班级上下文
func (h *ClassHandler) SwitchClass(c *gin.Context) {
var req schema.SwitchClassRequest
if err := c.ShouldBindJSON(&req); err != nil {
response.BadRequest(c, "参数错误")
return
}
userID := middleware.GetUserID(c)
result, err := h.classService.SwitchClass(userID, req.ClassID)
if err != nil {
response.InternalError(c, err.Error())
return
}
response.Success(c, result, "切换成功")
}
// GetSettings 获取班级设置
func (h *ClassHandler) GetSettings(c *gin.Context) {
classID := middleware.GetClassID(c)
result, err := h.classService.GetSettings(classID)
if err != nil {
response.InternalError(c, "获取设置失败")
return
}
response.Success(c, result, "操作成功")
}
// allowedSettingKeys 允许通过 SaveSetting 端点写入的配置键白名单
var allowedSettingKeys = map[string]bool{
"initial_password": true,
"initial_points": true,
"deduction_attendance_absent": true,
"deduction_attendance_late": true,
"deduction_attendance_leave": true,
"deduction_homework_not_submit": true,
"deduction_homework_late": true,
"reset_frequency": true,
"reset_day_of_week": true,
"reset_day_of_month": true,
}
// SaveSetting 保存班级设置
func (h *ClassHandler) SaveSetting(c *gin.Context) {
classID := middleware.GetClassID(c)
var req schema.SettingRequest
if err := c.ShouldBindJSON(&req); err != nil {
response.BadRequest(c, "参数错误")
return
}
if !allowedSettingKeys[req.SettingKey] {
response.BadRequest(c, "不允许的配置项: "+req.SettingKey)
return
}
if err := h.classService.SaveSetting(classID, req.SettingKey, req.SettingValue); err != nil {
response.InternalError(c, "保存设置失败")
return
}
response.SuccessWithMessage(c, "保存成功")
}
// GetPointLimits 获取角色加减分配置
func (h *ClassHandler) GetPointLimits(c *gin.Context) {
classID := middleware.GetClassID(c)
result, err := h.classService.GetSettings(classID)
if err != nil {
response.InternalError(c, "获取配置失败")
return
}
response.Success(c, result, "操作成功")
}
// allowedPointLimitKeys 允许的操行分限制配置键白名单(与 conduct_service 读取 key 一致)
var allowedPointLimitKeys = map[string]bool{
"point_limit_班长_max": true,
"point_limit_班长_min": true,
"point_limit_学习委员_max": true,
"point_limit_学习委员_min": true,
"point_limit_考勤委员_max": true,
"point_limit_考勤委员_min": true,
"point_limit_劳动委员_max": true,
"point_limit_劳动委员_min": true,
"point_limit_志愿委员_max": true,
"point_limit_志愿委员_min": true,
"point_limit_科任老师_max": true,
"point_limit_科任老师_min": true,
}
// SavePointLimits 保存角色加减分配置
func (h *ClassHandler) SavePointLimits(c *gin.Context) {
classID := middleware.GetClassID(c)
var req map[string]string
if err := c.ShouldBindJSON(&req); err != nil {
response.BadRequest(c, "参数错误")
return
}
for key, value := range req {
if !allowedPointLimitKeys[key] {
response.BadRequest(c, "不允许的配置项: "+key)
return
}
if err := h.classService.SaveSetting(classID, key, value); err != nil {
response.InternalError(c, "保存配置失败")
return
}
}
response.SuccessWithMessage(c, "保存成功")
}
// GetFeatures 获取功能开关
func (h *ClassHandler) GetFeatures(c *gin.Context) {
classID := middleware.GetClassID(c)
result, err := h.classService.GetFeatures(classID)
if err != nil {
response.InternalError(c, "获取功能开关失败")
return
}
response.Success(c, result, "操作成功")
}
// allowedFeatureKeys 允许的功能开关键白名单
var allowedFeatureKeys = map[string]bool{
"parent_account_enabled": true,
"parent_password_change_enabled": true,
"parent_view_attendance": true,
"parent_view_ranking": true,
"student_view_ranking": true,
"homework_management": true,
"attendance_management": true,
"cadre_homework": true,
}
// SaveFeature 保存功能开关
func (h *ClassHandler) SaveFeature(c *gin.Context) {
classID := middleware.GetClassID(c)
var req schema.FeatureToggleRequest
if err := c.ShouldBindJSON(&req); err != nil {
response.BadRequest(c, "参数错误")
return
}
if !allowedFeatureKeys[req.FeatureKey] {
response.BadRequest(c, "不允许的功能开关: "+req.FeatureKey)
return
}
if err := h.classService.SaveFeature(classID, req.FeatureKey, req.Enabled); err != nil {
response.InternalError(c, "保存功能开关失败")
return
}
response.SuccessWithMessage(c, "保存成功")
}

View File

@@ -0,0 +1,44 @@
// ===========================================
// 多班级版班级管理系统 - Go 后端
//
// 开发者: Canglan
// 联系方式: admin@sea-studio.top
// 版权归属: Sea Network Technology Studio
// 许可证: Apache License 2.0
//
// 版权所有 © Sea Network Technology Studio
// ===========================================
package handler
import (
"strconv"
"github.com/gin-gonic/gin"
"hz-gitea.sea-studio.top/canglan/SharedClassManager/internal/service"
"hz-gitea.sea-studio.top/canglan/SharedClassManager/pkg/response"
)
// ConfigHandler 配置处理器
type ConfigHandler struct {
configService *service.ConfigService
}
// NewConfigHandler 创建配置处理器
func NewConfigHandler(configService *service.ConfigService) *ConfigHandler {
return &ConfigHandler{configService: configService}
}
// GetDeductionRules 获取扣分规则(优先从 class_settings 读取班级级配置)
func (h *ConfigHandler) GetDeductionRules(c *gin.Context) {
classID := 0
if classIDStr := c.Query("class_id"); classIDStr != "" {
if id, err := strconv.Atoi(classIDStr); err == nil {
classID = id
}
}
rules := h.configService.GetDeductionRules(classID)
response.Success(c, rules, "操作成功")
}

View File

@@ -0,0 +1,20 @@
package handler
import (
"strconv"
"github.com/gin-gonic/gin"
)
// parseQueryParamInt 解析查询参数为 int
func parseQueryParamInt(c *gin.Context, key string, defaultVal int) int {
val := c.Query(key)
if val == "" {
return defaultVal
}
i, err := strconv.Atoi(val)
if err != nil {
return defaultVal
}
return i
}

View File

@@ -0,0 +1,115 @@
// ===========================================
// 多班级版班级管理系统 - Go 后端
//
// 开发者: Canglan
// 联系方式: admin@sea-studio.top
// 版权归属: Sea Network Technology Studio
// 许可证: Apache License 2.0
//
// 版权所有 © Sea Network Technology Studio
// ===========================================
package handler
import (
"github.com/gin-gonic/gin"
"hz-gitea.sea-studio.top/canglan/SharedClassManager/internal/middleware"
"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"
)
// ParentHandler 家长端处理器
type ParentHandler struct {
parentService *service.ParentService
authService *service.AuthService
classService *service.ClassService
}
// NewParentHandler 创建家长端处理器
func NewParentHandler(
parentService *service.ParentService,
authService *service.AuthService,
classService *service.ClassService,
) *ParentHandler {
return &ParentHandler{
parentService: parentService,
authService: authService,
classService: classService,
}
}
// Dashboard 子女操行分(家长仪表盘)
func (h *ParentHandler) Dashboard(c *gin.Context) {
userID := middleware.GetUserID(c)
result, err := h.parentService.GetChildConduct(userID)
if err != nil {
response.InternalError(c, err.Error())
return
}
response.Success(c, result, "操作成功")
}
// History 子女历史记录
func (h *ParentHandler) History(c *gin.Context) {
var query schema.ParentHistoryQuery
if err := c.ShouldBindQuery(&query); err != nil {
response.BadRequest(c, "参数错误")
return
}
userID := middleware.GetUserID(c)
result, err := h.parentService.GetChildHistory(userID, query.Page, query.PageSize)
if err != nil {
response.InternalError(c, err.Error())
return
}
response.Success(c, result, "操作成功")
}
// Attendance 子女考勤
func (h *ParentHandler) Attendance(c *gin.Context) {
userID := middleware.GetUserID(c)
result, err := h.parentService.GetChildAttendance(userID)
if err != nil {
response.InternalError(c, err.Error())
return
}
response.Success(c, result, "操作成功")
}
// Ranking 子女排名
func (h *ParentHandler) Ranking(c *gin.Context) {
userID := middleware.GetUserID(c)
result, err := h.parentService.GetChildRanking(userID)
if err != nil {
response.InternalError(c, err.Error())
return
}
response.Success(c, result, "操作成功")
}
// ChangePassword 家长修改密码(受功能开关控制)
func (h *ParentHandler) ChangePassword(c *gin.Context) {
classID := middleware.GetClassID(c)
// 检查功能开关
if !h.classService.IsFeatureEnabled(classID, "parent_password_change_enabled") {
response.Forbidden(c, "该功能暂未开放")
return
}
var req schema.ChangePasswordRequest
if err := c.ShouldBindJSON(&req); err != nil {
response.BadRequest(c, "参数错误")
return
}
userID := middleware.GetUserID(c)
if err := h.authService.ChangePassword(userID, req.OldPassword, req.NewPassword, false); err != nil {
response.BadRequest(c, err.Error())
return
}
response.SuccessWithMessage(c, "密码修改成功")
}

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 handler
import (
"github.com/gin-gonic/gin"
"hz-gitea.sea-studio.top/canglan/SharedClassManager/internal/middleware"
"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"
)
// SemesterHandler 学期管理处理器
type SemesterHandler struct {
semesterService *service.SemesterService
}
// NewSemesterHandler 创建学期管理处理器
func NewSemesterHandler(semesterService *service.SemesterService) *SemesterHandler {
return &SemesterHandler{semesterService: semesterService}
}
// SemesterList 学期列表
func (h *SemesterHandler) SemesterList(c *gin.Context) {
result, err := h.semesterService.ListSemesters()
if err != nil {
response.InternalError(c, "获取学期列表失败")
return
}
response.Success(c, result, "操作成功")
}
// ActiveSemester 当前学期
func (h *SemesterHandler) ActiveSemester(c *gin.Context) {
semester, err := h.semesterService.GetActiveSemester()
if err != nil {
response.Success(c, nil, "无活跃学期")
return
}
response.Success(c, semester, "操作成功")
}
// SemesterCreate 创建学期
func (h *SemesterHandler) SemesterCreate(c *gin.Context) {
var req schema.SemesterCreateRequest
if err := c.ShouldBindJSON(&req); err != nil {
response.BadRequest(c, "参数错误")
return
}
result, err := h.semesterService.CreateSemester(req.SemesterName, req.StartDate, req.EndDate)
if err != nil {
response.InternalError(c, err.Error())
return
}
if success, _ := result["success"].(bool); !success {
response.BadRequest(c, result["message"].(string))
return
}
response.Success(c, result, "操作成功")
}
// ActivateSemester 激活学期
func (h *SemesterHandler) ActivateSemester(c *gin.Context) {
semesterID, ok := parseID(c, "semester_id")
if !ok {
return
}
if err := h.semesterService.ActivateSemester(semesterID); err != nil {
response.BadRequest(c, err.Error())
return
}
response.SuccessWithMessage(c, "已设为当前学期")
}
// SemesterUpdate 编辑学期
func (h *SemesterHandler) SemesterUpdate(c *gin.Context) {
semesterID, ok := parseID(c, "semester_id")
if !ok {
return
}
var req schema.SemesterUpdateRequest
if err := c.ShouldBindJSON(&req); err != nil {
response.BadRequest(c, "参数错误")
return
}
if err := h.semesterService.UpdateSemester(semesterID, req.SemesterName, req.StartDate, req.EndDate); err != nil {
response.BadRequest(c, err.Error())
return
}
response.SuccessWithMessage(c, "更新成功")
}
// SemesterDelete 删除学期
func (h *SemesterHandler) SemesterDelete(c *gin.Context) {
semesterID, ok := parseID(c, "semester_id")
if !ok {
return
}
if err := h.semesterService.DeleteSemester(semesterID); err != nil {
response.BadRequest(c, err.Error())
return
}
response.SuccessWithMessage(c, "删除成功")
}
// AssociateRecords 关联记录
func (h *SemesterHandler) AssociateRecords(c *gin.Context) {
semesterID, ok := parseID(c, "semester_id")
if !ok {
return
}
result, err := h.semesterService.AssociateRecords(semesterID)
if err != nil {
response.InternalError(c, err.Error())
return
}
if success, _ := result["success"].(bool); !success {
response.BadRequest(c, result["message"].(string))
return
}
response.Success(c, result, "操作成功")
}
// ArchiveSemester 归档学期
func (h *SemesterHandler) ArchiveSemester(c *gin.Context) {
semesterID, ok := parseID(c, "semester_id")
if !ok {
return
}
classID := parseQueryParamInt(c, "class_id", 0)
resetScores := c.Query("reset_scores") == "true"
result, err := h.semesterService.ArchiveSemester(semesterID, classID, resetScores)
if err != nil {
response.InternalError(c, err.Error())
return
}
if success, _ := result["success"].(bool); !success {
response.BadRequest(c, result["message"].(string))
return
}
response.Success(c, result, "操作成功")
}
// GetArchiveData 归档数据
func (h *SemesterHandler) GetArchiveData(c *gin.Context) {
semesterID, ok := parseID(c, "semester_id")
if !ok {
return
}
classID := parseQueryParamInt(c, "class_id", 0)
page := parseQueryParamInt(c, "page", 1)
pageSize := parseQueryParamInt(c, "page_size", 20)
result, err := h.semesterService.GetArchiveRecords(semesterID, classID, page, pageSize)
if err != nil {
response.InternalError(c, err.Error())
return
}
response.Success(c, result, "操作成功")
}
// PeriodReset 手动触发周/月重置
func (h *SemesterHandler) PeriodReset(c *gin.Context) {
var req schema.PeriodResetRequest
if err := c.ShouldBindJSON(&req); err != nil {
response.BadRequest(c, "参数错误")
return
}
classID := middleware.GetClassID(c)
if classID == 0 {
response.BadRequest(c, "未指定班级")
return
}
userID := middleware.GetUserID(c)
realName := middleware.GetRealName(c)
ip := c.ClientIP()
if err := h.semesterService.PeriodReset(classID, req.Period, userID, realName, ip); err != nil {
response.InternalError(c, err.Error())
return
}
response.SuccessWithMessage(c, service.PeriodLabelCN(req.Period)+"重置成功")
}
// GetPeriodArchives 查看周期归档数据
func (h *SemesterHandler) GetPeriodArchives(c *gin.Context) {
var req schema.PeriodArchiveQuery
if err := c.ShouldBindQuery(&req); err != nil {
response.BadRequest(c, "参数错误")
return
}
classID := middleware.GetClassID(c)
if classID == 0 {
response.BadRequest(c, "未指定班级")
return
}
result, err := h.semesterService.GetPeriodArchives(classID, req.Period, req.Page, req.PageSize)
if err != nil {
response.InternalError(c, err.Error())
return
}
response.Success(c, result, "操作成功")
}

View File

@@ -0,0 +1,192 @@
// ===========================================
// 多班级版班级管理系统 - Go 后端
//
// 开发者: Canglan
// 联系方式: admin@sea-studio.top
// 版权归属: Sea Network Technology Studio
// 许可证: Apache License 2.0
//
// 版权所有 © Sea Network Technology Studio
// ===========================================
package handler
import (
"strconv"
"github.com/gin-gonic/gin"
"hz-gitea.sea-studio.top/canglan/SharedClassManager/internal/middleware"
"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"
)
// StudentHandler 学生端处理器
type StudentHandler struct {
studentService *service.StudentService
classRepo *repository.ClassRepo
}
// NewStudentHandler 创建学生端处理器
func NewStudentHandler(studentService *service.StudentService, classRepo *repository.ClassRepo) *StudentHandler {
return &StudentHandler{studentService: studentService, classRepo: classRepo}
}
// Dashboard 学生个人信息(仪表盘)
func (h *StudentHandler) Dashboard(c *gin.Context) {
studentID := middleware.GetStudentID(c)
if studentID == 0 {
response.BadRequest(c, "非学生用户")
return
}
result, err := h.studentService.GetStudentInfo(studentID)
if err != nil {
response.InternalError(c, err.Error())
return
}
response.Success(c, result, "操作成功")
}
// resolveStudentID 校验学生归属:学生只能查看自己的数据,家长只能查看关联子女,管理员可查看指定学生
func (h *StudentHandler) resolveStudentID(c *gin.Context) (int, bool) {
userType := middleware.GetUserType(c)
if userType == "student" {
// 学生只能查看自己的数据,忽略 URL 参数中的 student_id
studentID := middleware.GetStudentID(c)
if studentID == 0 {
response.BadRequest(c, "非学生用户")
return 0, false
}
return studentID, true
}
requestedID, ok := parseID(c, "student_id")
if !ok {
return 0, false
}
// 家长只能查看自己关联的子女数据
if userType == "parent" {
parentStudentID := middleware.GetStudentID(c)
if parentStudentID == 0 || parentStudentID != requestedID {
response.Forbidden(c, "无权访问该学生数据")
return 0, false
}
return requestedID, true
}
// 管理员/超级管理员允许查看(角色权限由路由中间件 RequireRole 控制)
return requestedID, true
}
// ConductHistory 学生操行分历史
func (h *StudentHandler) ConductHistory(c *gin.Context) {
studentID, ok := h.resolveStudentID(c)
if !ok {
return
}
var query schema.StudentConductQuery
if err := c.ShouldBindQuery(&query); err != nil {
response.BadRequest(c, "参数错误")
return
}
result, err := h.studentService.GetConductHistory(studentID, query.Limit, query.Offset)
if err != nil {
response.InternalError(c, err.Error())
return
}
response.Success(c, result, "操作成功")
}
// Homework 学生作业情况
func (h *StudentHandler) Homework(c *gin.Context) {
studentID, ok := h.resolveStudentID(c)
if !ok {
return
}
result, err := h.studentService.GetHomeworkStatus(studentID)
if err != nil {
response.InternalError(c, err.Error())
return
}
response.Success(c, result, "操作成功")
}
// Attendance 学生考勤记录
func (h *StudentHandler) Attendance(c *gin.Context) {
studentID, ok := h.resolveStudentID(c)
if !ok {
return
}
month := c.Query("month")
result, err := h.studentService.GetAttendanceRecords(studentID, month)
if err != nil {
response.InternalError(c, err.Error())
return
}
response.Success(c, result, "操作成功")
}
// Ranking 操行分排行
func (h *StudentHandler) Ranking(c *gin.Context) {
classID := middleware.GetClassID(c)
// 检查班级功能开关:学生查看排行榜
feature, err := h.classRepo.GetFeature(classID, "student_view_ranking")
if err == nil && feature != nil && feature.Enabled == 0 {
response.Forbidden(c, "该功能暂未开放")
return
}
limit, _ := strconv.Atoi(c.DefaultQuery("limit", "50"))
if limit <= 0 {
limit = 50
}
if limit > 500 {
limit = 500
}
result, err := h.studentService.GetRanking(classID, limit)
if err != nil {
response.InternalError(c, err.Error())
return
}
response.Success(c, result, "操作成功")
}
// MyInfo 学生个人信息
func (h *StudentHandler) MyInfo(c *gin.Context) {
studentID := middleware.GetStudentID(c)
if studentID == 0 {
response.BadRequest(c, "非学生用户")
return
}
result, err := h.studentService.GetStudentInfo(studentID)
if err != nil {
response.InternalError(c, err.Error())
return
}
response.Success(c, result, "操作成功")
}
// SemesterRecords 学期归档记录
func (h *StudentHandler) SemesterRecords(c *gin.Context) {
studentID := middleware.GetStudentID(c)
if studentID <= 0 {
response.BadRequest(c, "非学生用户")
return
}
result, err := h.studentService.GetSemesterRecords(studentID)
if err != nil {
response.InternalError(c, err.Error())
return
}
response.Success(c, result, "操作成功")
}

View File

@@ -0,0 +1,152 @@
// ===========================================
// 多班级版班级管理系统 - Go 后端
//
// 开发者: Canglan
// 联系方式: admin@sea-studio.top
// 版权归属: Sea Network Technology Studio
// 许可证: Apache License 2.0
//
// 版权所有 © Sea Network Technology Studio
// ===========================================
package handler
import (
"github.com/gin-gonic/gin"
"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"
)
// SubjectHandler 科目管理处理器
type SubjectHandler struct {
subjectService *service.SubjectService
}
// NewSubjectHandler 创建科目管理处理器
func NewSubjectHandler(subjectService *service.SubjectService) *SubjectHandler {
return &SubjectHandler{subjectService: subjectService}
}
// SubjectList 科目列表
func (h *SubjectHandler) SubjectList(c *gin.Context) {
var isActive *bool
if v := c.Query("is_active"); v == "true" {
b := true
isActive = &b
} else if v == "false" {
b := false
isActive = &b
}
result, err := h.subjectService.GetSubjects(isActive)
if err != nil {
response.InternalError(c, "获取科目列表失败")
return
}
response.Success(c, result, "操作成功")
}
// SubjectCreate 创建科目
func (h *SubjectHandler) SubjectCreate(c *gin.Context) {
var req schema.SubjectCreateRequest
if err := c.ShouldBindJSON(&req); err != nil {
response.BadRequest(c, "参数错误")
return
}
result, err := h.subjectService.CreateSubject(req.SubjectName, req.SubjectCode, req.SortOrder)
if err != nil {
response.InternalError(c, err.Error())
return
}
if success, _ := result["success"].(bool); !success {
response.BadRequest(c, result["message"].(string))
return
}
response.Success(c, result, "操作成功")
}
// SubjectUpdate 更新科目
func (h *SubjectHandler) SubjectUpdate(c *gin.Context) {
subjectID, ok := parseID(c, "subject_id")
if !ok {
return
}
var req schema.SubjectUpdateRequest
if err := c.ShouldBindJSON(&req); err != nil {
response.BadRequest(c, "参数错误")
return
}
updates := make(map[string]interface{})
if req.SubjectName != nil {
updates["subject_name"] = *req.SubjectName
}
if req.SubjectCode != nil {
updates["subject_code"] = *req.SubjectCode
}
if req.IsActive != nil {
updates["is_active"] = *req.IsActive
}
if req.SortOrder != nil {
updates["sort_order"] = *req.SortOrder
}
if err := h.subjectService.UpdateSubject(subjectID, updates); err != nil {
response.InternalError(c, err.Error())
return
}
response.SuccessWithMessage(c, "更新成功")
}
// SubjectDelete 删除科目
func (h *SubjectHandler) SubjectDelete(c *gin.Context) {
subjectID, ok := parseID(c, "subject_id")
if !ok {
return
}
if err := h.subjectService.DeleteSubject(subjectID); err != nil {
response.InternalError(c, err.Error())
return
}
response.SuccessWithMessage(c, "删除成功")
}
// SubjectToggle 切换科目启用/禁用状态
func (h *SubjectHandler) SubjectToggle(c *gin.Context) {
subjectID, ok := parseID(c, "subject_id")
if !ok {
return
}
var req struct {
IsActive bool `json:"is_active"`
}
if err := c.ShouldBindJSON(&req); err != nil {
response.BadRequest(c, "参数错误")
return
}
var err error
if req.IsActive {
err = h.subjectService.EnableSubject(subjectID)
} else {
err = h.subjectService.DisableSubject(subjectID)
}
if err != nil {
response.InternalError(c, err.Error())
return
}
if req.IsActive {
response.SuccessWithMessage(c, "科目已启用")
} else {
response.SuccessWithMessage(c, "科目已禁用")
}
}

View File

@@ -0,0 +1,56 @@
// ===========================================
// 多班级版班级管理系统 - Go 后端
//
// 开发者: Canglan
// 联系方式: admin@sea-studio.top
// 版权归属: Sea Network Technology Studio
// 许可证: Apache License 2.0
//
// 版权所有 © Sea Network Technology Studio
// ===========================================
package handler
import (
"github.com/gin-gonic/gin"
"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"
)
// SuperAdminHandler 超级管理员处理器
type SuperAdminHandler struct {
superAdminService *service.SuperAdminService
}
// NewSuperAdminHandler 创建超级管理员处理器
func NewSuperAdminHandler(superAdminService *service.SuperAdminService) *SuperAdminHandler {
return &SuperAdminHandler{superAdminService: superAdminService}
}
// Login 超级管理员登录
func (h *SuperAdminHandler) Login(c *gin.Context) {
var req schema.LoginRequest
if err := c.ShouldBindJSON(&req); err != nil {
response.BadRequest(c, "参数错误")
return
}
ip := c.ClientIP()
userAgent := c.GetHeader("User-Agent")
result, err := h.superAdminService.Login(req.Username, req.Password, ip, userAgent)
if err != nil {
response.InternalError(c, err.Error())
return
}
success, ok := result["success"].(bool)
if !ok || !success {
msg, _ := result["message"].(string)
response.Unauthorized(c, msg)
return
}
response.Success(c, result, "登录成功")
}