保持志愿明细接口

This commit is contained in:
zhouwentao 2026-01-24 13:56:16 +08:00
parent c1a0b60218
commit 26ce858f7a
9 changed files with 213 additions and 14 deletions

View File

@ -0,0 +1,14 @@
package common
import "regexp"
// 验证表名格式的辅助函数
func IsValidTableName(tableName string) bool {
if tableName == "" {
return false
}
// 表名只能包含字母、数字、下划线和点号,且长度合理
matched, err := regexp.MatchString(`^[a-zA-Z_][a-zA-Z0-9_.]{0,100}$`, tableName)
return err == nil && matched
}

View File

@ -4,8 +4,10 @@ import (
"server/common" "server/common"
user_service "server/modules/user/service" user_service "server/modules/user/service"
yxDto "server/modules/yx/dto" yxDto "server/modules/yx/dto"
"server/modules/yx/entity"
yx_service "server/modules/yx/service" yx_service "server/modules/yx/service"
"strconv" "strconv"
"time"
"github.com/gin-gonic/gin" "github.com/gin-gonic/gin"
) )
@ -14,6 +16,8 @@ type UserMajorController struct {
userScoreService *user_service.UserScoreService userScoreService *user_service.UserScoreService
yxUserScoreService *yx_service.YxUserScoreService yxUserScoreService *yx_service.YxUserScoreService
yxCalculationMajorService *yx_service.YxCalculationMajorService yxCalculationMajorService *yx_service.YxCalculationMajorService
yxVolunteerService *yx_service.YxVolunteerService
yxVolunteerRecordService *yx_service.YxVolunteerRecordService
} }
func NewUserMajorController() *UserMajorController { func NewUserMajorController() *UserMajorController {
@ -21,6 +25,8 @@ func NewUserMajorController() *UserMajorController {
yxUserScoreService: yx_service.NewYxUserScoreService(), yxUserScoreService: yx_service.NewYxUserScoreService(),
userScoreService: user_service.NewUserScoreService(), userScoreService: user_service.NewUserScoreService(),
yxCalculationMajorService: yx_service.NewYxCalculationMajorService(), yxCalculationMajorService: yx_service.NewYxCalculationMajorService(),
yxVolunteerService: yx_service.NewYxVolunteerService(),
yxVolunteerRecordService: yx_service.NewYxVolunteerRecordService(),
} }
} }
@ -32,6 +38,7 @@ func (ctrl *UserMajorController) RegisterRoutes(rg *gin.RouterGroup) {
// group.GET("/:id", ctrl.GetByID) // group.GET("/:id", ctrl.GetByID)
group.GET("/list", ctrl.List) group.GET("/list", ctrl.List)
group.GET("/list_by_school", ctrl.ListBySchool) group.GET("/list_by_school", ctrl.ListBySchool)
group.POST("/save_volunteer", ctrl.SaveVolunteer)
} }
} }
@ -112,3 +119,101 @@ func (ctrl *UserMajorController) List(c *gin.Context) {
} }
common.SuccessPage(c, newMap, total, page, size) common.SuccessPage(c, newMap, total, page, size)
} }
// SaveVolunteer 保存志愿明细
// @Summary 保存志愿明细
// @Tags 用户专业
// @Param keys body []string true "Keys: schoolCode_majorCode_enrollmentCode"
// @Success 200 {object} common.Response
// @Router /user/major/save_volunteer [post]
func (ctrl *UserMajorController) SaveVolunteer(c *gin.Context) {
var keys []string
if err := c.ShouldBindJSON(&keys); err != nil {
common.Error(c, 500, err.Error())
return
}
// data deduplication
seen := make(map[string]bool)
var uniqueKeys []string
for _, key := range keys {
if !seen[key] {
seen[key] = true
uniqueKeys = append(uniqueKeys, key)
}
}
keys = uniqueKeys
loginUserId := common.GetLoginUser(c).ID
userScoreVO, err := ctrl.userScoreService.GetActiveByID(loginUserId)
if err != nil {
common.Error(c, 500, err.Error())
return
}
if userScoreVO.CalculationTableName == "" {
common.Error(c, 500, "未找到计算表名")
return
}
// 查找当前激活的志愿表
volunteer, err := ctrl.yxVolunteerService.FindActiveByScoreId(userScoreVO.ID)
if err != nil {
common.Error(c, 500, "查找志愿表失败: "+err.Error())
return
}
if volunteer == nil || volunteer.ID == "" {
common.Error(c, 500, "请先创建志愿表")
return
}
// 查找专业信息
majors, err := ctrl.yxCalculationMajorService.FindListByCompositeKeys(userScoreVO.CalculationTableName, keys, userScoreVO.ID)
if err != nil {
common.Error(c, 500, "查找专业信息失败: "+err.Error())
return
}
// 构建 Map 用于保持顺序
majorMap := make(map[string]entity.YxCalculationMajor)
for _, major := range majors {
k := major.SchoolCode + "_" + major.MajorCode + "_" + major.EnrollmentCode
majorMap[k] = major
}
var records []entity.YxVolunteerRecord
for i, key := range keys {
if major, ok := majorMap[key]; ok {
record := entity.YxVolunteerRecord{
VolunteerID: volunteer.ID,
SchoolCode: major.SchoolCode,
MajorCode: major.MajorCode,
EnrollmentCode: major.EnrollmentCode,
Indexs: i + 1,
CreateBy: loginUserId,
CreateTime: time.Now(),
Batch: major.Batch,
EnrollProbability: major.EnrollProbability,
StudentConvertedScore: major.StudentConvertedScore,
CalculationMajorID: major.ID,
}
records = append(records, record)
}
}
// 先删除旧数据
if err := ctrl.yxVolunteerRecordService.DeleteByVolunteerID(volunteer.ID); err != nil {
common.Error(c, 500, "删除旧数据失败: "+err.Error())
return
}
// 批量插入新数据
if len(records) > 0 {
if err := ctrl.yxVolunteerRecordService.BatchCreate(records); err != nil {
common.Error(c, 500, "保存失败: "+err.Error())
return
}
}
common.Success(c, "保存成功")
}

View File

@ -109,6 +109,7 @@ func NewUserScoreService() *UserScoreService {
return &UserScoreService{ return &UserScoreService{
yxUserScoreService: service.NewYxUserScoreService(), yxUserScoreService: service.NewYxUserScoreService(),
yxCalculationMajorService: service.NewYxCalculationMajorService(), yxCalculationMajorService: service.NewYxCalculationMajorService(),
yxVolunteerService: service.NewYxVolunteerService(),
mapper: mapper.NewYxUserScoreMapper(), mapper: mapper.NewYxUserScoreMapper(),
} }
} }
@ -178,6 +179,13 @@ func (s *UserScoreService) SaveUserScore(req *dto.SaveScoreRequest) (vo.UserScor
return vo.UserScoreVO{}, fmt.Errorf("保存专业信息失败: %w", err) return vo.UserScoreVO{}, fmt.Errorf("保存专业信息失败: %w", err)
} }
// 创建志愿表
err = s.yxVolunteerService.CreateByScoreId(entityItem.ID, req.CreateBy)
if err != nil {
tx.Rollback()
return vo.UserScoreVO{}, fmt.Errorf("创建志愿表失败: %w", err)
}
// 提交事务 // 提交事务
if err := tx.Commit().Error; err != nil { if err := tx.Commit().Error; err != nil {
tx.Rollback() tx.Rollback()
@ -193,9 +201,6 @@ func (s *UserScoreService) SaveUserScore(req *dto.SaveScoreRequest) (vo.UserScor
if err != nil { if err != nil {
return vo.UserScoreVO{}, fmt.Errorf("缓存成绩记录失败: %w", err) return vo.UserScoreVO{}, fmt.Errorf("缓存成绩记录失败: %w", err)
} }
// 创建志愿表
s.yxVolunteerService.CreateByScoreId(entityItem.ID, req.CreateBy)
return userScoreVO, nil return userScoreVO, nil
} }

View File

@ -3,6 +3,7 @@ package mapper
import ( import (
"fmt" "fmt"
"server/common"
"server/config" "server/config"
"server/modules/yx/dto" "server/modules/yx/dto"
"server/modules/yx/entity" "server/modules/yx/entity"
@ -401,6 +402,50 @@ func (m *YxCalculationMajorMapper) FindByScoreID(scoreID string) ([]entity.YxCal
return items, err return items, err
} }
func (m *YxCalculationMajorMapper) FindListByCompositeKeys(tableName string, keys []string, scoreId string) ([]entity.YxCalculationMajor, error) {
if len(keys) == 0 {
return nil, nil
}
// 验证表名格式(防止表名注入)
if !common.IsValidTableName(tableName) {
return nil, fmt.Errorf("无效的表名: %s", tableName)
}
// 验证和转义 score_id
if scoreId == "" {
return nil, fmt.Errorf("score_id 不能为空")
}
var items []entity.YxCalculationMajor
db := config.DB
if tableName != "" {
db = db.Table(tableName)
}
sql := "SELECT * FROM " + tableName + " WHERE score_id = ? AND (school_code, major_code, enrollment_code) IN ("
var params []interface{}
// 将 score_id 作为第一个参数
params = append(params, scoreId)
for i, key := range keys {
parts := strings.Split(key, "_")
if len(parts) != 3 {
continue
}
if i > 0 {
sql += ","
}
sql += "(?, ?, ?)"
params = append(params, parts[0], parts[1], parts[2])
}
sql += ")"
err := db.Raw(sql, params...).Scan(&items).Error
return items, err
}
func (m *YxCalculationMajorMapper) BatchCreate(tableName string, items []entity.YxCalculationMajor, batchSize int) error { func (m *YxCalculationMajorMapper) BatchCreate(tableName string, items []entity.YxCalculationMajor, batchSize int) error {
if tableName != "" { if tableName != "" {
return config.DB.Table(tableName).CreateInBatches(items, batchSize).Error return config.DB.Table(tableName).CreateInBatches(items, batchSize).Error

View File

@ -10,8 +10,15 @@ import (
type YxVolunteerMapper struct{} type YxVolunteerMapper struct{}
func (m *YxVolunteerMapper) CloseOtherVolunteer(userId string) { func (m *YxVolunteerMapper) CloseOtherVolunteer(userId string) error {
config.DB.Model(&entity.YxVolunteer{}).Where("create_by = ? ", userId).Updates(map[string]interface{}{"state": "0"}) result := config.DB.Model(&entity.YxVolunteer{}).
Where("create_by = ?", userId).
Updates(map[string]interface{}{"state": "0"})
if result.Error != nil {
return result.Error
}
return nil
} }
func NewYxVolunteerMapper() *YxVolunteerMapper { func NewYxVolunteerMapper() *YxVolunteerMapper {
@ -32,6 +39,12 @@ func (m *YxVolunteerMapper) FindByID(id string) (*entity.YxVolunteer, error) {
return &item, err return &item, err
} }
func (m *YxVolunteerMapper) FindActiveByScoreId(scoreId string) (*entity.YxVolunteer, error) {
var item entity.YxVolunteer
err := config.DB.Where("score_id = ? AND state = ?", scoreId, "1").First(&item).Error
return &item, err
}
func (m *YxVolunteerMapper) Create(item *entity.YxVolunteer) error { func (m *YxVolunteerMapper) Create(item *entity.YxVolunteer) error {
return config.DB.Create(item).Error return config.DB.Create(item).Error
} }

View File

@ -44,6 +44,10 @@ func (m *YxVolunteerRecordMapper) Delete(id string) error {
return config.DB.Delete(&entity.YxVolunteerRecord{}, "id = ?", id).Error return config.DB.Delete(&entity.YxVolunteerRecord{}, "id = ?", id).Error
} }
func (m *YxVolunteerRecordMapper) DeleteByVolunteerID(volunteerID string) error {
return config.DB.Delete(&entity.YxVolunteerRecord{}, "volunteer_id = ?", volunteerID).Error
}
func (m *YxVolunteerRecordMapper) BatchCreate(items []entity.YxVolunteerRecord, batchSize int) error { func (m *YxVolunteerRecordMapper) BatchCreate(items []entity.YxVolunteerRecord, batchSize int) error {
return config.DB.CreateInBatches(items, batchSize).Error return config.DB.CreateInBatches(items, batchSize).Error
} }

View File

@ -145,6 +145,10 @@ func (s *YxCalculationMajorService) GetByScoreID(scoreID string) ([]entity.YxCal
return s.mapper.FindByScoreID(scoreID) return s.mapper.FindByScoreID(scoreID)
} }
func (s *YxCalculationMajorService) FindListByCompositeKeys(tableName string, keys []string, scoreId string) ([]entity.YxCalculationMajor, error) {
return s.mapper.FindListByCompositeKeys(tableName, keys, scoreId)
}
func (s *YxCalculationMajorService) BatchCreate(tableName string, items []entity.YxCalculationMajor) error { func (s *YxCalculationMajorService) BatchCreate(tableName string, items []entity.YxCalculationMajor) error {
for i := range items { for i := range items {
items[i].ID = uuid.New().String() items[i].ID = uuid.New().String()

View File

@ -41,6 +41,10 @@ func (s *YxVolunteerRecordService) Delete(id string) error {
return s.mapper.Delete(id) return s.mapper.Delete(id)
} }
func (s *YxVolunteerRecordService) DeleteByVolunteerID(volunteerID string) error {
return s.mapper.DeleteByVolunteerID(volunteerID)
}
func (s *YxVolunteerRecordService) BatchCreate(items []entity.YxVolunteerRecord) error { func (s *YxVolunteerRecordService) BatchCreate(items []entity.YxVolunteerRecord) error {
for i := range items { for i := range items {
items[i].ID = uuid.New().String() items[i].ID = uuid.New().String()

View File

@ -2,11 +2,11 @@
package service package service
import ( import (
"fmt"
"server/common"
"server/modules/yx/entity" "server/modules/yx/entity"
"server/modules/yx/mapper" "server/modules/yx/mapper"
"time" "time"
"github.com/google/uuid"
) )
type YxVolunteerService struct { type YxVolunteerService struct {
@ -26,10 +26,14 @@ func (s *YxVolunteerService) GetByID(id string) (*entity.YxVolunteer, error) {
} }
func (s *YxVolunteerService) Create(item *entity.YxVolunteer) error { func (s *YxVolunteerService) Create(item *entity.YxVolunteer) error {
item.ID = uuid.New().String() item.ID = common.GenerateStringID()
return s.mapper.Create(item) return s.mapper.Create(item)
} }
func (s *YxVolunteerService) FindActiveByScoreId(scoreId string) (*entity.YxVolunteer, error) {
return s.mapper.FindActiveByScoreId(scoreId)
}
func (s *YxVolunteerService) Update(item *entity.YxVolunteer) error { func (s *YxVolunteerService) Update(item *entity.YxVolunteer) error {
return s.mapper.Update(item) return s.mapper.Update(item)
} }
@ -44,7 +48,7 @@ func (s *YxVolunteerService) Delete(id string) error {
func (s *YxVolunteerService) BatchCreate(items []entity.YxVolunteer) error { func (s *YxVolunteerService) BatchCreate(items []entity.YxVolunteer) error {
for i := range items { for i := range items {
items[i].ID = uuid.New().String() items[i].ID = common.GenerateStringID()
} }
return s.mapper.BatchCreate(items, 100) return s.mapper.BatchCreate(items, 100)
} }
@ -56,7 +60,7 @@ func (s *YxVolunteerService) BatchUpdate(items []entity.YxVolunteer) error {
func (s *YxVolunteerService) BatchUpsert(items []entity.YxVolunteer, updateColumns []string) error { func (s *YxVolunteerService) BatchUpsert(items []entity.YxVolunteer, updateColumns []string) error {
for i := range items { for i := range items {
if items[i].ID == "" { if items[i].ID == "" {
items[i].ID = uuid.New().String() items[i].ID = common.GenerateStringID()
} }
} }
return s.mapper.BatchUpsert(items, updateColumns) return s.mapper.BatchUpsert(items, updateColumns)
@ -69,8 +73,7 @@ func (s *YxVolunteerService) BatchDelete(ids []string) error {
// 根据ScoreId创建新志愿表 // 根据ScoreId创建新志愿表
func (s *YxVolunteerService) CreateByScoreId(scoreId string, userId string) error { func (s *YxVolunteerService) CreateByScoreId(scoreId string, userId string) error {
volunteer := entity.YxVolunteer{} volunteer := entity.YxVolunteer{}
volunteer.ID = uuid.New().String() volunteer.ID = common.GenerateStringID()
// 志愿表名称格式 时间戳 20260101134501志愿表 // 志愿表名称格式 时间戳 20260101134501志愿表
volunteer.VolunteerName = time.Now().Format("20060102150405") + "志愿表" volunteer.VolunteerName = time.Now().Format("20060102150405") + "志愿表"
@ -81,8 +84,10 @@ func (s *YxVolunteerService) CreateByScoreId(scoreId string, userId string) erro
volunteer.CreateTime = time.Now() volunteer.CreateTime = time.Now()
volunteer.UpdateTime = time.Now() volunteer.UpdateTime = time.Now()
// 先关闭当前用户其他志愿单 // 先关闭当前用户其他志愿单 - ✅ 检查错误
s.mapper.CloseOtherVolunteer(userId) if err := s.mapper.CloseOtherVolunteer(userId); err != nil {
return fmt.Errorf("关闭其他志愿表失败: %w", err)
}
// 创建志愿表 // 创建志愿表
return s.mapper.Create(&volunteer) return s.mapper.Create(&volunteer)
} }