updates
This commit is contained in:
parent
94486dd149
commit
93bfb590d6
|
|
@ -1,6 +1,7 @@
|
||||||
# 代码库函数概览
|
# 代码库函数概览
|
||||||
|
|
||||||
## server/common
|
## server/common
|
||||||
|
- `constants`: 存放全局常量,如 `RedisTokenPrefix`, `TokenHeader`, `StateActive` 等。
|
||||||
- `Response`: 统一的HTTP响应结构体 `{Code, Message, Data}`。
|
- `Response`: 统一的HTTP响应结构体 `{Code, Message, Data}`。
|
||||||
- `Success(c *gin.Context, data interface{})`: 发送成功响应。
|
- `Success(c *gin.Context, data interface{})`: 发送成功响应。
|
||||||
- `Error(c *gin.Context, code int, msg string)`: 发送错误响应。
|
- `Error(c *gin.Context, code int, msg string)`: 发送错误响应。
|
||||||
|
|
@ -25,7 +26,8 @@
|
||||||
- `YxVolunteerRecordController`: 志愿明细控制器。
|
- `YxVolunteerRecordController`: 志愿明细控制器。
|
||||||
|
|
||||||
## server/modules/user
|
## server/modules/user
|
||||||
- `UserScoreService`: 用户成绩服务。
|
- `UserScoreService`: 用户成绩服务。
|
||||||
- `GetActiveByID(userID string)`: 获取用户当前激活的成绩,返回 `UserScoreVO`。
|
- `GetActiveByID(userID string)`: 获取用户当前激活的成绩,返回 `UserScoreVO`。
|
||||||
|
- `ListByUser(userID string, page, size int)`: 获取用户的所有成绩分页列表。
|
||||||
- `SaveUserScore(req *dto.SaveScoreRequest)`: 保存用户成绩,处理旧记录状态更新及 DTO 转换。
|
- `SaveUserScore(req *dto.SaveScoreRequest)`: 保存用户成绩,处理旧记录状态更新及 DTO 转换。
|
||||||
- `UserScoreVO`: 用户成绩视图对象,包含基础信息、选课列表及子专业成绩映射。
|
- `UserScoreVO`: 用户成绩视图对象,包含基础信息、选课列表及子专业成绩映射。
|
||||||
|
|
|
||||||
|
|
@ -56,3 +56,21 @@
|
||||||
- 定义了 `UserScoreVO` 结构体,其字段设计参考了 `SaveScoreRequest`。
|
- 定义了 `UserScoreVO` 结构体,其字段设计参考了 `SaveScoreRequest`。
|
||||||
- 在 `UserScoreService` 中实现了 `convertEntityToVo` 私有方法,处理了逗号分隔字符串到切片的转换,以及具体分数字段到 Map 的映射。
|
- 在 `UserScoreService` 中实现了 `convertEntityToVo` 私有方法,处理了逗号分隔字符串到切片的转换,以及具体分数字段到 Map 的映射。
|
||||||
- 更新 `GetActiveByID` 返回 `UserScoreVO` 对象。
|
- 更新 `GetActiveByID` 返回 `UserScoreVO` 对象。
|
||||||
|
|
||||||
|
### [任务执行] 增加用户成绩分页列表接口
|
||||||
|
- **操作目标**: 在 `UserScoreController` 和 `UserScoreService` 中增加获取当前用户所有成绩的分页列表接口。
|
||||||
|
- **影响范围**: `server/modules/user/service/user_score_service.go`, `server/modules/user/controller/user_score_controller.go`
|
||||||
|
- **修改前记录**: 仅支持获取当前激活的单条成绩。
|
||||||
|
- **修改结果**:
|
||||||
|
- 在 `UserScoreService` 中实现了 `ListByUser` 方法,支持分页查询并返回 VO 列表。
|
||||||
|
- 在 `UserScoreController` 中增加了 `List` 方法,并注册了 `GET /user/score/list` 路由。
|
||||||
|
|
||||||
|
### [任务执行] 统一管理 Redis Key 及全局常量
|
||||||
|
- **操作目标**: 创建专门存放 Redis Key 和业务常量的地方,并重构相关代码以引用这些常量。
|
||||||
|
- **影响范围**: `server/common/constants.go`, `server/modules/system/service/sys_user_service.go`, `server/middleware/auth.go`, `server/common/context.go`
|
||||||
|
- **修改前记录**: 常量散落在各个业务文件和中间件中,存在硬编码和重复定义。
|
||||||
|
- **修改结果**:
|
||||||
|
- 创建了 `server/common/constants.go`,集中管理 Redis 前缀、Token 请求头、业务状态码等。
|
||||||
|
- 重构了 `SysUserService`,使用 `common.RedisTokenPrefix` 和 `common.RedisTokenExpire`。
|
||||||
|
- 重构了 `AuthMiddleware`,使用 `common.TokenHeader` 和 `common.ContextUserKey`。
|
||||||
|
- 清理了 `common/context.go` 中的重复定义。
|
||||||
|
|
|
||||||
|
|
@ -7,6 +7,7 @@
|
||||||
- `database.go`: 数据库连接配置。
|
- `database.go`: 数据库连接配置。
|
||||||
- `redis.go`: Redis连接配置。
|
- `redis.go`: Redis连接配置。
|
||||||
- `common/`: 通用工具包。
|
- `common/`: 通用工具包。
|
||||||
|
- `constants.go`: 全局常量定义(Redis Key、业务状态等)。
|
||||||
- `response.go`: 统一HTTP响应结构。
|
- `response.go`: 统一HTTP响应结构。
|
||||||
- `context.go`: 上下文辅助函数。
|
- `context.go`: 上下文辅助函数。
|
||||||
- `logger.go`: 日志工具。
|
- `logger.go`: 日志工具。
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,40 @@
|
||||||
|
package common
|
||||||
|
|
||||||
|
import "time"
|
||||||
|
|
||||||
|
// Redis 相关常量
|
||||||
|
const (
|
||||||
|
// RedisTokenPrefix Redis中Token前缀
|
||||||
|
RedisTokenPrefix = "login:token:"
|
||||||
|
// RedisTokenExpire Token过期时间
|
||||||
|
RedisTokenExpire = 24 * time.Hour
|
||||||
|
|
||||||
|
// RedisUserScorePrefix Redis中用户成绩前缀
|
||||||
|
RedisUserScorePrefix = "user:score:"
|
||||||
|
// RedisUserScoreExpire 用户成绩过期时间
|
||||||
|
RedisUserScoreExpire = 8 * time.Hour
|
||||||
|
)
|
||||||
|
|
||||||
|
// HTTP/Context 相关常量
|
||||||
|
const (
|
||||||
|
// ContextUserKey 上下文中存储用户信息的key
|
||||||
|
ContextUserKey = "loginUser"
|
||||||
|
// TokenHeader 请求头中Token的key
|
||||||
|
// "X-Access-Token"
|
||||||
|
TokenHeader = "Authorization"
|
||||||
|
// HeaderTokenPrefix Token前缀 (如有需要)
|
||||||
|
HeaderTokenPrefix = ""
|
||||||
|
)
|
||||||
|
|
||||||
|
// 业务状态常量
|
||||||
|
const (
|
||||||
|
StateActive = "1" // 使用中
|
||||||
|
StateInactive = "0" // 未使用/已删除
|
||||||
|
StateHistory = "2" // 历史记录
|
||||||
|
)
|
||||||
|
|
||||||
|
// 数据类型常量
|
||||||
|
const (
|
||||||
|
TypeNormal = "1" // 普通类
|
||||||
|
TypeArt = "2" // 艺术类
|
||||||
|
)
|
||||||
|
|
@ -7,8 +7,6 @@ import (
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
)
|
)
|
||||||
|
|
||||||
const ContextUserKey = "loginUser"
|
|
||||||
|
|
||||||
// GetLoginUser 从上下文获取当前登录用户
|
// GetLoginUser 从上下文获取当前登录用户
|
||||||
// 在Controller中使用: user := common.GetLoginUser(c)
|
// 在Controller中使用: user := common.GetLoginUser(c)
|
||||||
func GetLoginUser(c *gin.Context) *entity.LoginUser {
|
func GetLoginUser(c *gin.Context) *entity.LoginUser {
|
||||||
|
|
|
||||||
|
|
@ -10,15 +10,6 @@ import (
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
|
||||||
// ContextUserKey 上下文中存储用户信息的key
|
|
||||||
ContextUserKey = "loginUser"
|
|
||||||
// TokenHeader 请求头中Token的key
|
|
||||||
TokenHeader = "Authorization"
|
|
||||||
// TokenPrefix Token前缀
|
|
||||||
TokenPrefix = "Bearer "
|
|
||||||
)
|
|
||||||
|
|
||||||
// 白名单路径 (不需要登录即可访问)
|
// 白名单路径 (不需要登录即可访问)
|
||||||
var whiteList = []string{
|
var whiteList = []string{
|
||||||
"/api/sys/auth/login",
|
"/api/sys/auth/login",
|
||||||
|
|
@ -46,29 +37,28 @@ func AuthMiddleware() gin.HandlerFunc {
|
||||||
}
|
}
|
||||||
|
|
||||||
// 获取Token
|
// 获取Token
|
||||||
token := c.GetHeader(TokenHeader)
|
token := c.GetHeader(common.TokenHeader)
|
||||||
if token == "" {
|
if token == "" {
|
||||||
common.Error(c, 401, "未登录")
|
common.Error(c, 401, "未登录")
|
||||||
c.Abort()
|
c.Abort()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// 去除Bearer前缀
|
// 如果有前缀则处理前缀
|
||||||
token = strings.TrimPrefix(token, TokenPrefix)
|
if common.HeaderTokenPrefix != "" && strings.HasPrefix(token, common.HeaderTokenPrefix) {
|
||||||
// if strings.HasPrefix(token, TokenPrefix) {
|
token = token[len(common.HeaderTokenPrefix):]
|
||||||
// token = token[len(TokenPrefix):]
|
}
|
||||||
// }
|
|
||||||
|
|
||||||
// 验证Token并获取用户信息
|
// 验证Token并获取用户信息
|
||||||
loginUser, err := userService.GetLoginUser(token)
|
loginUser, err := userService.GetLoginUser(token)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
common.Error(c, 401, err.Error())
|
common.Error(c, 401, "登录已失效,请重新登录")
|
||||||
c.Abort()
|
c.Abort()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// 将用户信息存入上下文
|
// 存入上下文
|
||||||
c.Set(ContextUserKey, loginUser)
|
c.Set(common.ContextUserKey, loginUser)
|
||||||
|
|
||||||
c.Next()
|
c.Next()
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -17,11 +17,6 @@ import (
|
||||||
"github.com/google/uuid"
|
"github.com/google/uuid"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
|
||||||
TokenPrefix = "login:token:" // Redis中Token前缀
|
|
||||||
TokenExpire = 24 * time.Hour // Token过期时间
|
|
||||||
)
|
|
||||||
|
|
||||||
type SysUserService struct {
|
type SysUserService struct {
|
||||||
mapper *mapper.SysUserMapper
|
mapper *mapper.SysUserMapper
|
||||||
}
|
}
|
||||||
|
|
@ -117,13 +112,13 @@ func (s *SysUserService) SysLogin(username, password string) (*entity.LoginUser,
|
||||||
// Logout 用户登出
|
// Logout 用户登出
|
||||||
func (s *SysUserService) Logout(token string) error {
|
func (s *SysUserService) Logout(token string) error {
|
||||||
ctx := context.Background()
|
ctx := context.Background()
|
||||||
return config.RDB.Del(ctx, TokenPrefix+token).Err()
|
return config.RDB.Del(ctx, common.RedisTokenPrefix+token).Err()
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetLoginUser 根据Token获取登录用户信息
|
// GetLoginUser 根据Token获取登录用户信息
|
||||||
func (s *SysUserService) GetLoginUser(token string) (*entity.LoginUser, error) {
|
func (s *SysUserService) GetLoginUser(token string) (*entity.LoginUser, error) {
|
||||||
ctx := context.Background()
|
ctx := context.Background()
|
||||||
data, err := config.RDB.Get(ctx, TokenPrefix+token).Result()
|
data, err := config.RDB.Get(ctx, common.RedisTokenPrefix+token).Result()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, errors.New("未登录或登录已过期")
|
return nil, errors.New("未登录或登录已过期")
|
||||||
}
|
}
|
||||||
|
|
@ -134,7 +129,7 @@ func (s *SysUserService) GetLoginUser(token string) (*entity.LoginUser, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// 刷新过期时间
|
// 刷新过期时间
|
||||||
config.RDB.Expire(ctx, TokenPrefix+token, TokenExpire)
|
config.RDB.Expire(ctx, common.RedisTokenPrefix+token, common.RedisTokenExpire)
|
||||||
|
|
||||||
return &loginUser, nil
|
return &loginUser, nil
|
||||||
}
|
}
|
||||||
|
|
@ -142,7 +137,7 @@ func (s *SysUserService) GetLoginUser(token string) (*entity.LoginUser, error) {
|
||||||
// RefreshToken 刷新Token过期时间
|
// RefreshToken 刷新Token过期时间
|
||||||
func (s *SysUserService) RefreshToken(token string) error {
|
func (s *SysUserService) RefreshToken(token string) error {
|
||||||
ctx := context.Background()
|
ctx := context.Background()
|
||||||
return config.RDB.Expire(ctx, TokenPrefix+token, TokenExpire).Err()
|
return config.RDB.Expire(ctx, common.RedisTokenPrefix+token, common.RedisTokenExpire).Err()
|
||||||
}
|
}
|
||||||
|
|
||||||
// 保存登录用户到Redis
|
// 保存登录用户到Redis
|
||||||
|
|
@ -152,7 +147,7 @@ func (s *SysUserService) saveLoginUser(token string, user *entity.LoginUser) err
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
return config.RDB.Set(ctx, TokenPrefix+token, data, TokenExpire).Err()
|
return config.RDB.Set(ctx, common.RedisTokenPrefix+token, data, common.RedisTokenExpire).Err()
|
||||||
}
|
}
|
||||||
|
|
||||||
// 生成Token
|
// 生成Token
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,7 @@ import (
|
||||||
user_service "server/modules/user/service"
|
user_service "server/modules/user/service"
|
||||||
"server/modules/yx/dto"
|
"server/modules/yx/dto"
|
||||||
yx_service "server/modules/yx/service"
|
yx_service "server/modules/yx/service"
|
||||||
|
"strconv"
|
||||||
|
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
)
|
)
|
||||||
|
|
@ -27,6 +28,7 @@ func (ctrl *UserScoreController) RegisterRoutes(rg *gin.RouterGroup) {
|
||||||
{
|
{
|
||||||
// group.GET("/:id", ctrl.Get)
|
// group.GET("/:id", ctrl.Get)
|
||||||
group.GET("/", ctrl.Get)
|
group.GET("/", ctrl.Get)
|
||||||
|
group.GET("/list", ctrl.List) // 新增分页列表接口
|
||||||
group.POST("/save-score", ctrl.SaveUserScore) // 新增接口
|
group.POST("/save-score", ctrl.SaveUserScore) // 新增接口
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -71,3 +73,25 @@ func (ctrl *UserScoreController) Get(c *gin.Context) {
|
||||||
}
|
}
|
||||||
common.Success(c, item)
|
common.Success(c, item)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// List 获取当前用户的成绩列表
|
||||||
|
// @Summary 获取当前用户的成绩列表
|
||||||
|
// @Tags 用户分数
|
||||||
|
// @Param page query int false "页码" default(1)
|
||||||
|
// @Param size query int false "每页数量" default(10)
|
||||||
|
// @Success 200 {object} common.Response
|
||||||
|
// @Router /user/score/list [get]
|
||||||
|
func (ctrl *UserScoreController) List(c *gin.Context) {
|
||||||
|
page, _ := strconv.Atoi(c.DefaultQuery("page", "1"))
|
||||||
|
size, _ := strconv.Atoi(c.DefaultQuery("size", "10"))
|
||||||
|
loginUserId := common.GetLoginUser(c).ID
|
||||||
|
items, total, err := ctrl.userScoreService.ListByUser(loginUserId, page, size)
|
||||||
|
if err != nil {
|
||||||
|
common.Error(c, 500, err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
common.Success(c, gin.H{
|
||||||
|
"items": items,
|
||||||
|
"total": total,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -11,6 +11,7 @@ import (
|
||||||
"server/modules/yx/mapper"
|
"server/modules/yx/mapper"
|
||||||
"server/modules/yx/service"
|
"server/modules/yx/service"
|
||||||
"strings"
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
"gorm.io/gorm"
|
"gorm.io/gorm"
|
||||||
)
|
)
|
||||||
|
|
@ -41,6 +42,29 @@ func (s *UserScoreService) GetActiveByID(userID string) (*vo.UserScoreVO, error)
|
||||||
return scoreVO, nil
|
return scoreVO, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ListByUser 获取用户的成绩分页列表
|
||||||
|
func (s *UserScoreService) ListByUser(userID string, page, size int) ([]*vo.UserScoreVO, int64, error) {
|
||||||
|
var scores []entity.YxUserScore
|
||||||
|
var total int64
|
||||||
|
|
||||||
|
query := config.DB.Model(&entity.YxUserScore{}).Where("create_by = ?", userID)
|
||||||
|
|
||||||
|
if err := query.Count(&total).Error; err != nil {
|
||||||
|
return nil, 0, fmt.Errorf("查询总数失败: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := query.Offset((page - 1) * size).Limit(size).Order("create_time desc").Find(&scores).Error; err != nil {
|
||||||
|
return nil, 0, fmt.Errorf("查询成绩列表失败: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
vos := make([]*vo.UserScoreVO, 0, len(scores))
|
||||||
|
for i := range scores {
|
||||||
|
vos = append(vos, s.convertEntityToVo(&scores[i]))
|
||||||
|
}
|
||||||
|
|
||||||
|
return vos, total, nil
|
||||||
|
}
|
||||||
|
|
||||||
func NewUserScoreService() *UserScoreService {
|
func NewUserScoreService() *UserScoreService {
|
||||||
return &UserScoreService{
|
return &UserScoreService{
|
||||||
yxUserScoreService: service.NewYxUserScoreService(),
|
yxUserScoreService: service.NewYxUserScoreService(),
|
||||||
|
|
@ -56,7 +80,8 @@ func (s *UserScoreService) SaveUserScore(req *dto.SaveScoreRequest) (*entity.YxU
|
||||||
}
|
}
|
||||||
// 2. DTO 转 Entity
|
// 2. DTO 转 Entity
|
||||||
entityItem := s.convertDtoToEntity(req)
|
entityItem := s.convertDtoToEntity(req)
|
||||||
|
entityItem.CreateTime = time.Now()
|
||||||
|
entityItem.UpdateTime = time.Now()
|
||||||
// 3. 执行保存操作(可以包含事务)
|
// 3. 执行保存操作(可以包含事务)
|
||||||
tx := config.DB.Begin()
|
tx := config.DB.Begin()
|
||||||
defer func() {
|
defer func() {
|
||||||
|
|
@ -67,7 +92,7 @@ func (s *UserScoreService) SaveUserScore(req *dto.SaveScoreRequest) (*entity.YxU
|
||||||
|
|
||||||
// 标记该用户的所有旧成绩为历史状态
|
// 标记该用户的所有旧成绩为历史状态
|
||||||
if err := config.DB.Model(&entity.YxUserScore{}).
|
if err := config.DB.Model(&entity.YxUserScore{}).
|
||||||
Where("user_id = ? AND state = ?", entityItem.CreateBy, "1").
|
Where("create_by = ? AND state = ?", req.CreateBy, "1").
|
||||||
Updates(map[string]interface{}{"state": "2"}).Error; err != nil {
|
Updates(map[string]interface{}{"state": "2"}).Error; err != nil {
|
||||||
tx.Rollback()
|
tx.Rollback()
|
||||||
return nil, fmt.Errorf("更新旧记录失败: %w", err)
|
return nil, fmt.Errorf("更新旧记录失败: %w", err)
|
||||||
|
|
|
||||||
|
|
@ -23,9 +23,19 @@ func (req *SaveScoreRequest) Validate() error {
|
||||||
if req.CognitioPolyclinic != "文科" && req.CognitioPolyclinic != "理科" {
|
if req.CognitioPolyclinic != "文科" && req.CognitioPolyclinic != "理科" {
|
||||||
return errors.New("考试类型必须是'物理组'或'历史组'")
|
return errors.New("考试类型必须是'物理组'或'历史组'")
|
||||||
}
|
}
|
||||||
if len(req.SubjectList) != 2 {
|
|
||||||
return errors.New("选考科目有且只能传两个值")
|
if req.ProfessionalCategory == "表演类" || req.ProfessionalCategory == "音乐类" {
|
||||||
|
if len(req.ProfessionalCategoryChildren) == 0 {
|
||||||
|
return errors.New("表演类或音乐类必须至少选一个专业子级")
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
req.ProfessionalCategoryChildren = []string{}
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(req.SubjectList) > 3 {
|
||||||
|
return errors.New("选考科目有且最多只能传三个值")
|
||||||
|
}
|
||||||
|
|
||||||
validSubjects := map[string]bool{"地理": true, "政治": true, "历史": true, "化学": true, "生物": true}
|
validSubjects := map[string]bool{"地理": true, "政治": true, "历史": true, "化学": true, "生物": true}
|
||||||
for _, s := range req.SubjectList {
|
for _, s := range req.SubjectList {
|
||||||
if !validSubjects[s] {
|
if !validSubjects[s] {
|
||||||
|
|
@ -33,16 +43,16 @@ func (req *SaveScoreRequest) Validate() error {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if !isValidScore(*req.ProfessionalScore, 300) {
|
if !isValidScore(*req.ProfessionalScore, 300) {
|
||||||
return errors.New("ProfessionalScore必须在0-300之间")
|
return errors.New("统考成绩必须在0-300之间")
|
||||||
}
|
}
|
||||||
if !isValidScore(*req.CulturalScore, 300) {
|
if !isValidScore(*req.CulturalScore, 750) {
|
||||||
return errors.New("CulturalScore必须在0-300之间")
|
return errors.New("文化成绩必须在0-750之间")
|
||||||
}
|
}
|
||||||
if !isValidScore(*req.EnglishScore, 150) {
|
if !isValidScore(*req.EnglishScore, 150) {
|
||||||
return errors.New("EnglishScore必须在0-150之间")
|
return errors.New("英文成绩必须在0-150之间")
|
||||||
}
|
}
|
||||||
if !isValidScore(*req.ChineseScore, 150) {
|
if !isValidScore(*req.ChineseScore, 150) {
|
||||||
return errors.New("ChineseScore必须在0-150之间")
|
return errors.New("中文成绩必须在0-150之间")
|
||||||
}
|
}
|
||||||
// TODO 在这里判断一下 专业子级,如 表演类只有:"服装表演", "戏剧影视表演", "戏剧影视导演"。音乐类只有:音乐表演声乐、音乐表演器乐、音乐教育。
|
// TODO 在这里判断一下 专业子级,如 表演类只有:"服装表演", "戏剧影视表演", "戏剧影视导演"。音乐类只有:音乐表演声乐、音乐表演器乐、音乐教育。
|
||||||
validProfessionalChildren := map[string]string{ // 子级 -> 父级分类
|
validProfessionalChildren := map[string]string{ // 子级 -> 父级分类
|
||||||
|
|
@ -79,6 +89,51 @@ func (req *SaveScoreRequest) Validate() error {
|
||||||
if count > 3 { // 假设每类最多3个
|
if count > 3 { // 假设每类最多3个
|
||||||
return errors.New(category + "最多只能有3个子级")
|
return errors.New(category + "最多只能有3个子级")
|
||||||
}
|
}
|
||||||
|
for _, key := range req.ProfessionalCategoryChildren {
|
||||||
|
if req.ProfessionalCategoryChildrenScore[key] == 0 {
|
||||||
|
return errors.New(key + "成绩不能为空")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 专业子级和成绩数据一致性校验
|
||||||
|
if err := req.validateProfessionalConsistency(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (req *SaveScoreRequest) validateProfessionalConsistency() error {
|
||||||
|
// 特殊情况:如果不需要专业子级,则两个都应该为空
|
||||||
|
if len(req.ProfessionalCategoryChildren) == 0 {
|
||||||
|
if len(req.ProfessionalCategoryChildrenScore) != 0 {
|
||||||
|
return errors.New("未选择专业子级时,成绩数据应为空")
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
// 创建映射用于双向验证
|
||||||
|
childrenMap := make(map[string]bool)
|
||||||
|
for _, child := range req.ProfessionalCategoryChildren {
|
||||||
|
if childrenMap[child] {
|
||||||
|
return errors.New("专业子级 '" + child + "' 重复")
|
||||||
|
}
|
||||||
|
childrenMap[child] = true
|
||||||
|
}
|
||||||
|
// 验证长度一致
|
||||||
|
if len(childrenMap) != len(req.ProfessionalCategoryChildrenScore) {
|
||||||
|
return errors.New("专业子级列表与成绩数据数量不匹配")
|
||||||
|
}
|
||||||
|
// 双向验证一致性
|
||||||
|
for childName := range req.ProfessionalCategoryChildrenScore {
|
||||||
|
if !childrenMap[childName] {
|
||||||
|
return errors.New("成绩数据中的专业子级 '" + childName + "' 未在选中列表中")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for _, child := range req.ProfessionalCategoryChildren {
|
||||||
|
if _, exists := req.ProfessionalCategoryChildrenScore[child]; !exists {
|
||||||
|
return errors.New("选中的专业子级 '" + child + "' 缺少成绩数据")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue