// Package service 业务逻辑层 package service import ( "fmt" "server/common" calc "server/common" "server/modules/user/vo" "server/modules/yx/dto" yxDto "server/modules/yx/dto" "server/modules/yx/entity" "server/modules/yx/mapper" "strings" "time" "github.com/google/uuid" ) type YxCalculationMajorService struct { *common.BaseService[entity.YxCalculationMajor] historyMajorEnrollService *YxHistoryMajorEnrollService mapper *mapper.YxCalculationMajorMapper historyScoreControlLineService *YxHistoryScoreControlLineService } func NewYxCalculationMajorService() *YxCalculationMajorService { mapper := mapper.NewYxCalculationMajorMapper() return &YxCalculationMajorService{ BaseService: common.NewBaseService[entity.YxCalculationMajor](), historyMajorEnrollService: NewYxHistoryMajorEnrollService(), mapper: mapper, historyScoreControlLineService: NewYxHistoryScoreControlLineService(), } } // RecommendMajorList 推荐专业列表 func (s *YxCalculationMajorService) RecommendMajorList(schoolMajorQuery yxDto.SchoolMajorQuery) ([]yxDto.UserMajorDTO, int64, dto.ProbabilityCountDTO, error) { if schoolMajorQuery.UserScoreVO.ProfessionalCategory == "" { return nil, 0, dto.ProbabilityCountDTO{}, fmt.Errorf("专业类型错误") } // 根据批次类型设置批次 985/211/双一流 -> 提前批, 公办本科 -> 本科A段, 民办本科 -> 本科B段, 体育类 -> 本科 if schoolMajorQuery.Batch != "" && "本科提前" == schoolMajorQuery.Batch { return []yxDto.UserMajorDTO{}, 0, dto.ProbabilityCountDTO{}, nil } else if schoolMajorQuery.Batch != "" && "高职高专" != schoolMajorQuery.Batch { if schoolMajorQuery.Batch2 == "双一流" { schoolMajorQuery.Batch = "提前批" } else if schoolMajorQuery.Batch2 == "公办本科" { schoolMajorQuery.Batch = "本科A段" } else if schoolMajorQuery.Batch2 == "民办本科" { schoolMajorQuery.Batch = "本科B段" } else if schoolMajorQuery.MajorType == "体育类" { schoolMajorQuery.Batch = "本科" } else { schoolMajorQuery.Batch = "" } } else { schoolMajorQuery.Batch = "高职高专" } calculationMajors, total, probCount, err := s.mapper.FindRecommendList(schoolMajorQuery) if err != nil { return nil, 0, dto.ProbabilityCountDTO{}, err } // 为专业列表添加历史数据 startTime := time.Now() err = s.UserMajorDTOGetHistory(&calculationMajors) endTime := time.Now() // 执行时长 queryCostTime := endTime.Sub(startTime) fmt.Printf(" 历史数据查询耗时:%v\n", queryCostTime) if err != nil { return nil, 0, dto.ProbabilityCountDTO{}, err } return calculationMajors, total, probCount, nil } // BatchCreateBySchoolMajorDTO 根据专业 DTO 批量创建 func (s *YxCalculationMajorService) BatchCreateBySchoolMajorDTO(tableName string, items []dto.SchoolMajorDTO, scoreID string) error { entities := make([]entity.YxCalculationMajor, 0, len(items)) now := time.Now() for _, item := range items { // 构建 OtherScoreLimitation var otherScoreLimitation strings.Builder if item.ChineseScoreLimitation > 0 { otherScoreLimitation.WriteString(fmt.Sprintf("语文成绩不低于%.1f分,", item.ChineseScoreLimitation)) } if item.EnglishScoreLimitation > 0 { otherScoreLimitation.WriteString(fmt.Sprintf("外语成绩不低于%.1f分,", item.EnglishScoreLimitation)) } if item.CulturalScoreLimitation > 0 { otherScoreLimitation.WriteString(fmt.Sprintf("文化成绩不低于%.1f分,", item.CulturalScoreLimitation)) } if item.ProfessionalScoreLimitation > 0 { otherScoreLimitation.WriteString(fmt.Sprintf("专业成绩不低于%.1f分,", item.ProfessionalScoreLimitation)) } otherScoreLimitationStr := otherScoreLimitation.String() if len(otherScoreLimitationStr) > 0 { otherScoreLimitationStr = otherScoreLimitationStr[:len(otherScoreLimitationStr)-1] } entities = append(entities, entity.YxCalculationMajor{ ScoreID: scoreID, SchoolCode: item.SchoolCode, MajorCode: item.MajorCode, MajorName: item.MajorName, EnrollmentCode: item.EnrollmentCode, Tuition: item.Tuition, Detail: item.Detail, Category: item.Category, RulesEnrollProbability: item.RulesEnrollProbability, Batch: item.Batch, StudentOldConvertedScore: item.StudentScore, StudentConvertedScore: item.StudentConvertedScore, EnrollProbability: item.EnrollProbability, ProbabilityOperator: item.ProbabilityOperator, CreateTime: now, MajorType: item.MajorType, MajorTypeChild: item.MajorTypeChild, PlanNum: item.PlanNum, MainSubjects: item.MainSubjects, Limitation: item.Limitation, OtherScoreLimitation: otherScoreLimitationStr, RulesEnrollProbabilitySx: item.RulesEnrollProbabilitySx, Kslx: item.Kslx, PrivateStudentConvertedScore: item.PrivateStudentScore, PrivateRulesEnrollProbability: item.PrivateRulesEnrollProbability, PrivateProbabilityOperator: item.PrivateProbabilityOperator, State: item.State, }) } return s.BatchCreate(tableName, entities) } // GetByScoreID 根据 scoreID 获取计算专业列表 func (s *YxCalculationMajorService) GetByScoreID(scoreID string) ([]entity.YxCalculationMajor, error) { return s.mapper.FindByScoreID(scoreID) } // FindListByCompositeKeys 根据复合键查找列表 func (s *YxCalculationMajorService) FindListByCompositeKeys(tableName string, keys []string, scoreId string) ([]entity.YxCalculationMajor, error) { return s.mapper.FindListByCompositeKeys(tableName, keys, scoreId) } // FindDtoListByCompositeKeys 根据复合键查找 DTO 列表 func (s *YxCalculationMajorService) FindDtoListByCompositeKeys(tableName string, keys []string, scoreId string) ([]dto.SchoolMajorDTO, error) { return s.mapper.FindDtoListByCompositeKeys(tableName, keys, scoreId) } // BatchCreate 批量创建(支持动态表名,使用 UUID 生成 ID) func (s *YxCalculationMajorService) BatchCreate(tableName string, items []entity.YxCalculationMajor) error { for i := range items { items[i].ID = uuid.New().String() } return s.mapper.BatchCreate(tableName, items, 100) } // DeleteByScoreID 根据 scoreID 删除 func (s *YxCalculationMajorService) DeleteByScoreID(scoreID string) error { return s.mapper.DeleteByScoreID(scoreID) } // DeleteByScoreIDFromTable 从指定表删除 scoreID 对应的数据 func (s *YxCalculationMajorService) DeleteByScoreIDFromTable(tableName, scoreID string) error { return s.mapper.DeleteByScoreIDFromTable(tableName, scoreID) } // ListByUserQueryType 根据用户查询类型获取专业列表 func (s *YxCalculationMajorService) ListByUserQueryType(professionalCategory string, cognitioPolyclinic string, professionalCategoryChildren []string) ([]dto.SchoolMajorDTO, error) { // 构造查询条件 query := dto.SchoolMajorQuery{ MajorType: professionalCategory, Category: cognitioPolyclinic, } // 执行院校查询 majorItems, err := mapper.NewYxSchoolMajorMapper().SelectSchoolMajor(query) if err != nil { return nil, err } // 分别收集专业名称和院校代码集合(去重) majorNameSet := make(map[string]bool) schoolCodeSet := make(map[string]bool) years := common.OldYearList for _, dto := range majorItems { majorNameSet[dto.MajorName] = true schoolCodeSet[dto.SchoolCode] = true } // 转换为切片用于查询 majorNames := make([]string, 0, len(majorNameSet)) schoolCodes := make([]string, 0, len(schoolCodeSet)) for name := range majorNameSet { majorNames = append(majorNames, name) } for code := range schoolCodeSet { schoolCodes = append(schoolCodes, code) } // 执行查询院校专业的历年数据 historyItems, err := s.historyMajorEnrollService.ListBySchoolCodesAndMajorNames( schoolCodes, majorNames, query.Category, query.MajorType, years, ) if err != nil { return nil, err } // 构建历史数据映射:schoolCode_majorName_batch_year -> historyItem allHistoryMajorEnrollMap := make(map[string]entity.YxHistoryMajorEnroll) for _, historyItem := range historyItems { key := historyItem.SchoolCode + "_" + historyItem.MajorName + "_" + historyItem.Batch + "_" + historyItem.Year allHistoryMajorEnrollMap[key] = historyItem } // 将历史数据填充到每个专业数据中 for i, majorItem := range majorItems { // 为每个majorItem创建独立的历史数据映射 historyMap := make(map[string]dto.YxHistoryMajorEnrollDTO) for _, year := range years { key := majorItem.SchoolCode + "_" + majorItem.MajorName + "_" + majorItem.Batch + "_" + year if historyItem, ok := allHistoryMajorEnrollMap[key]; ok { dtoItem := dto.YxHistoryMajorEnrollDTO{ Year: historyItem.Year, EnrollmentCode: historyItem.EnrollmentCode, RulesEnrollProbability: historyItem.RulesEnrollProbability, ProbabilityOperator: historyItem.ProbabilityOperator, AdmissionLine: historyItem.AdmissionLine, ControlLine: historyItem.ControlLine, } historyMap[year] = dtoItem } } majorItems[i].HistoryMajorEnrollMap = historyMap } return majorItems, nil } // UserMajorDTOGetHistory 获取用户专业 DTO 的历史数据 func (s *YxCalculationMajorService) UserMajorDTOGetHistory(userMajorDTOList *[]dto.UserMajorDTO) error { if len(*userMajorDTOList) > 0 { // 分别收集专业名称和院校代码集合(去重) majorNameSet := make(map[string]bool) schoolCodeSet := make(map[string]bool) years := common.OldYearList for _, dto := range *userMajorDTOList { majorNameSet[dto.MajorName] = true schoolCodeSet[dto.SchoolCode] = true } // 转换为切片用于查询 majorNames := make([]string, 0, len(majorNameSet)) schoolCodes := make([]string, 0, len(schoolCodeSet)) for name := range majorNameSet { majorNames = append(majorNames, name) } for code := range schoolCodeSet { schoolCodes = append(schoolCodes, code) } // 执行查询院校专业的历年数据 historyItems, err := s.historyMajorEnrollService.ListBySchoolCodesAndMajorNames( schoolCodes, majorNames, (*userMajorDTOList)[0].Category, (*userMajorDTOList)[0].MajorType, years, ) if err != nil { return nil } // 构建历史数据映射:schoolCode_majorName_batch_year -> historyItem allHistoryMajorEnrollMap := make(map[string]entity.YxHistoryMajorEnroll) for _, historyItem := range historyItems { key := historyItem.SchoolCode + "_" + historyItem.MajorName + "_" + historyItem.Batch + "_" + historyItem.Year allHistoryMajorEnrollMap[key] = historyItem } // 将历史数据填充到每个专业数据中 for i, majorItem := range *userMajorDTOList { // 为每个majorItem创建独立的历史数据映射 historyMap := make(map[string]dto.YxHistoryMajorEnrollDTO) for _, year := range years { key := majorItem.SchoolCode + "_" + majorItem.MajorName + "_" + majorItem.Batch + "_" + year if historyItem, ok := allHistoryMajorEnrollMap[key]; ok { dtoItem := dto.YxHistoryMajorEnrollDTO{ Year: historyItem.Year, EnrollmentCode: historyItem.EnrollmentCode, RulesEnrollProbability: historyItem.RulesEnrollProbability, ProbabilityOperator: historyItem.ProbabilityOperator, AdmissionLine: historyItem.AdmissionLine, ControlLine: historyItem.ControlLine, } historyMap[year] = dtoItem } } (*userMajorDTOList)[i].HistoryMajorEnrollMap = historyMap } } return nil } // CheckEnrollProbability 计算专业列表录取率 func (s *YxCalculationMajorService) CheckEnrollProbability(schoolMajorDTOList *[]dto.SchoolMajorDTO, userScoreVO vo.UserScoreVO) error { professionalCategory := userScoreVO.ProfessionalCategory if "表演类" == professionalCategory { // TODO: biaoyanService } else if "音乐类" == professionalCategory { // TODO: musicService } else { s.betaRecommendMajorListSetEnrollProbability(schoolMajorDTOList, userScoreVO) } return nil } // betaRecommendMajorListSetEnrollProbability 美术与设计类,书法类,体育类 获取录取率 func (s *YxCalculationMajorService) betaRecommendMajorListSetEnrollProbability(recommendMajorList *[]dto.SchoolMajorDTO, userScoreVO vo.UserScoreVO) { if recommendMajorList == nil || len(*recommendMajorList) == 0 { return } professionalCategory := userScoreVO.ProfessionalCategory nowBatch := "本科" // 获取省控线 Map historyScoreControlLineMap, err := s.historyScoreControlLineService.MapsBatchByProfessionalCategoryOfYear(common.NowYear, professionalCategory, userScoreVO.CognitioPolyclinic) if err != nil { return } culturalScore := userScoreVO.CulturalScore professionalScore := userScoreVO.ProfessionalScore for i := range *recommendMajorList { item := &(*recommendMajorList)[i] rulesEnrollProbability := item.PrivateRulesEnrollProbability probabilityOperator := item.PrivateProbabilityOperator // 获取对应批次的省控线 controlLineData, ok := historyScoreControlLineMap[item.Batch] if !ok { if val, okDefault := historyScoreControlLineMap["本科"]; okDefault { controlLineData = val } else { continue } } culturalControlLine := controlLineData.CulturalScore specialControlLine := controlLineData.SpecialScore if rulesEnrollProbability == "" { continue } // 补全 probabilityOperator 逻辑 if rulesEnrollProbability == "文过专排" && probabilityOperator == "" { probabilityOperator = "文*0+专*1" } else if rulesEnrollProbability == "专过文排" && probabilityOperator == "" { probabilityOperator = "文*1+专*0" } if probabilityOperator == "" { item.EnrollProbability = common.Number5 continue } // 判断其他录取要求 if !calc.OtherScoreJudge(professionalScore, userScoreVO, *item) { item.EnrollProbability = common.Number0 continue } // 25年专业录取原则变动 (体育类特殊逻辑) if item.MajorType == "体育类" { specialSchoolCodes := []string{"6530", "6085", "6110", "6065", "6050"} isSpecial := false for _, code := range specialSchoolCodes { if item.SchoolCode == code { isSpecial = true break } } if isSpecial { item.EnrollProbability = common.Number0 continue } } // 判断是否过省控线 if !calc.CrossingControlLine(rulesEnrollProbability, culturalScore, professionalScore, culturalControlLine, specialControlLine) { item.EnrollProbability = common.Number0 continue } // 计算学生折合分 studentScore := calc.ConvertIntoScore(rulesEnrollProbability, culturalScore, professionalScore, probabilityOperator) item.PrivateStudentScore = studentScore item.StudentScore = studentScore // 权限检查 if !calc.HasComputeEnrollProbabilityPermissions(nowBatch, item.Batch) { item.EnrollProbability = common.Number0 continue } // 录取方式计算 if common.CulturalControlLineGuoMain == rulesEnrollProbability { if len(item.HistoryMajorEnrollList) == 0 { item.EnrollProbability = common.Number0 continue } item.EnrollProbability = (studentScore * item.HistoryMajorEnrollList[0].AdmissionLine) * common.Number0p75 if studentScore >= item.HistoryMajorEnrollList[0].AdmissionLine { item.EnrollProbability *= common.Number0p5 } continue } else { nowYearProvincialControlLine := calc.ConvertIntoScore(rulesEnrollProbability, culturalControlLine, specialControlLine, probabilityOperator) if nowYearProvincialControlLine <= 0 { item.EnrollProbability = common.Number0 continue } diffMap := calc.ComputeHistoryMajorEnrollScoreLineDifferenceWithRulesEnrollProbability(item.MajorType, rulesEnrollProbability, probabilityOperator, item.HistoryMajorEnrollMap) historyThreeYearDiff := diffMap["scoreDifference"].(float64) if historyThreeYearDiff == 0 { item.EnrollProbability = common.Number0 continue } nowYearDiff := studentScore - nowYearProvincialControlLine enrollProbability := calc.CommonCheckEnrollProbability(nowYearDiff, historyThreeYearDiff) item.EnrollProbability = calc.CommonCheckEnrollProbabilityBeilv(enrollProbability) } } }