This commit is contained in:
zhouwentao 2025-12-21 16:44:42 +08:00
parent 523f323cd8
commit 17504936d0
5 changed files with 149 additions and 79 deletions

View File

@ -1,7 +1,6 @@
package common package common
import ( import (
"fmt"
"strconv" "strconv"
"sync" "sync"
"time" "time"
@ -9,7 +8,7 @@ import (
// IDGenerator 简单的 ID 生成器(类似 Snowflake // IDGenerator 简单的 ID 生成器(类似 Snowflake
type IDGenerator struct { type IDGenerator struct {
mu sync.Mutex mu sync.RWMutex
lastTime int64 lastTime int64
sequence int64 sequence int64
machineID int64 machineID int64
@ -24,14 +23,44 @@ var (
func GetIDGenerator() *IDGenerator { func GetIDGenerator() *IDGenerator {
once.Do(func() { once.Do(func() {
defaultGenerator = &IDGenerator{ defaultGenerator = &IDGenerator{
machineID: 1, // 默认机器 ID实际可从配置读取 machineID: 1,
} }
}) })
return defaultGenerator return defaultGenerator
} }
// Lazy initialize on first access
func getInstance() *IDGenerator {
if defaultGenerator == nil {
once.Do(func() {
defaultGenerator = &IDGenerator{
machineID: 1,
}
})
}
return defaultGenerator
}
// GenerateStringID 全局辅助函数:生成 string 类型 ID
func GenerateStringID() string {
gen := getInstance()
if gen == nil {
// Fallback: create new instance if singleton fails
gen = &IDGenerator{machineID: 1}
}
return gen.NextIDStr()
}
// GenerateLongID 全局辅助函数:生成 long 类型 ID
func GenerateLongID() int64 {
gen := getInstance()
if gen == nil {
gen = &IDGenerator{machineID: 1}
}
return gen.NextID()
}
// NextID 生成下一个 64 位整数 ID // NextID 生成下一个 64 位整数 ID
// 格式:时间戳(毫秒) + 机器ID + 序列号
func (g *IDGenerator) NextID() int64 { func (g *IDGenerator) NextID() int64 {
g.mu.Lock() g.mu.Lock()
defer g.mu.Unlock() defer g.mu.Unlock()
@ -39,9 +68,8 @@ func (g *IDGenerator) NextID() int64 {
now := time.Now().UnixMilli() now := time.Now().UnixMilli()
if now == g.lastTime { if now == g.lastTime {
g.sequence = (g.sequence + 1) & 4095 // 12位序列号 g.sequence = (g.sequence + 1) & 4095
if g.sequence == 0 { if g.sequence == 0 {
// 序列号溢出,等待下一毫秒
for now <= g.lastTime { for now <= g.lastTime {
now = time.Now().UnixMilli() now = time.Now().UnixMilli()
} }
@ -51,53 +79,11 @@ func (g *IDGenerator) NextID() int64 {
} }
g.lastTime = now g.lastTime = now
// 简单的位运算组合 ID
// 时间戳(41位) | 机器ID(10位) | 序列号(12位)
// 这里为了简单,直接返回时间戳+序列号的组合
// 如果需要严格的 long 类型且包含时间戳信息:
return now*10000 + g.sequence return now*10000 + g.sequence
} }
// NextIDStr 生成字符串类型的 ID // NextIDStr 生成字符串类型的 ID
func (g *IDGenerator) NextIDStr() string { func (g *IDGenerator) NextIDStr() string {
return strconv.FormatInt(g.NextID(), 10) id := g.NextID()
} return strconv.FormatInt(id, 10)
// GenerateLongID 全局辅助函数:生成 long 类型 ID
func GenerateLongID() int64 {
return GetIDGenerator().NextID()
}
// GenerateStringID 全局辅助函数:生成 string 类型 ID
func GenerateStringID() string {
return GetIDGenerator().NextIDStr()
}
// FormatTimestampToLong 将指定时间转为 long 格式 (yyyyMMddHHmmss)
func FormatTimestampToLong(t time.Time) int64 {
s := t.Format("20060102150405")
id, _ := strconv.ParseInt(s, 10, 64)
return id
}
// GenerateTimestampLongID 生成当前时间的 long 格式 ID (yyyyMMddHHmmss + 3位随机/序列)
func (g *IDGenerator) GenerateTimestampLongID() int64 {
g.mu.Lock()
defer g.mu.Unlock()
now := time.Now()
timestampStr := now.Format("20060102150405")
nowMs := now.UnixMilli()
if nowMs/1000 == g.lastTime/1000 {
g.sequence = (g.sequence + 1) & 999
} else {
g.sequence = 0
}
g.lastTime = nowMs
idStr := fmt.Sprintf("%s%03d", timestampStr, g.sequence)
id, _ := strconv.ParseInt(idStr, 10, 64)
return id
} }

View File

@ -19,11 +19,12 @@ import (
type UserScoreService struct { type UserScoreService struct {
yxUserScoreService *service.YxUserScoreService yxUserScoreService *service.YxUserScoreService
yxCalculationMajorService *service.YxCalculationMajorService
mapper *mapper.YxUserScoreMapper mapper *mapper.YxUserScoreMapper
} }
// GetActiveByID 获取当前激活的成绩 // GetActiveByID 获取当前激活的成绩
func (s *UserScoreService) GetActiveByID(userID string) (*vo.UserScoreVO, error) { func (s *UserScoreService) GetActiveByID(userID string) (vo.UserScoreVO, error) {
var score entity.YxUserScore var score entity.YxUserScore
// 明确指定字段,提高可读性 // 明确指定字段,提高可读性
err := config.DB.Model(&entity.YxUserScore{}). err := config.DB.Model(&entity.YxUserScore{}).
@ -31,32 +32,32 @@ func (s *UserScoreService) GetActiveByID(userID string) (*vo.UserScoreVO, error)
First(&score).Error First(&score).Error
if err != nil { if err != nil {
if errors.Is(err, gorm.ErrRecordNotFound) { if errors.Is(err, gorm.ErrRecordNotFound) {
return nil, fmt.Errorf("未找到激活的成绩记录") return vo.UserScoreVO{}, fmt.Errorf("未找到激活的成绩记录")
} }
return nil, fmt.Errorf("查询成绩记录失败: %w", err) return vo.UserScoreVO{}, fmt.Errorf("查询成绩记录失败: %w", err)
} }
return s.convertEntityToVo(&score), nil return s.convertEntityToVo(&score), nil
} }
// GetByID 根据 ID 获取成绩 // GetByID 根据 ID 获取成绩
func (s *UserScoreService) GetByID(id string) (*vo.UserScoreVO, error) { func (s *UserScoreService) GetByID(id string) (vo.UserScoreVO, error) {
var score entity.YxUserScore var score entity.YxUserScore
err := config.DB.Model(&entity.YxUserScore{}). err := config.DB.Model(&entity.YxUserScore{}).
Where("id = ?", id). Where("id = ?", id).
First(&score).Error First(&score).Error
if err != nil { if err != nil {
if errors.Is(err, gorm.ErrRecordNotFound) { if errors.Is(err, gorm.ErrRecordNotFound) {
return nil, fmt.Errorf("记录不存在") return vo.UserScoreVO{}, fmt.Errorf("记录不存在")
} }
return nil, fmt.Errorf("查询失败: %w", err) return vo.UserScoreVO{}, fmt.Errorf("查询失败: %w", err)
} }
return s.convertEntityToVo(&score), nil return s.convertEntityToVo(&score), nil
} }
// ListByUser 获取用户的成绩分页列表 // ListByUser 获取用户的成绩分页列表
func (s *UserScoreService) ListByUser(userID string, page, size int) ([]*vo.UserScoreVO, int64, error) { func (s *UserScoreService) ListByUser(userID string, page, size int) ([]vo.UserScoreVO, int64, error) {
var scores []entity.YxUserScore var scores []entity.YxUserScore
var total int64 var total int64
@ -70,7 +71,7 @@ func (s *UserScoreService) ListByUser(userID string, page, size int) ([]*vo.User
return nil, 0, fmt.Errorf("查询成绩列表失败: %w", err) return nil, 0, fmt.Errorf("查询成绩列表失败: %w", err)
} }
vos := make([]*vo.UserScoreVO, 0, len(scores)) vos := make([]vo.UserScoreVO, 0, len(scores))
for i := range scores { for i := range scores {
vos = append(vos, s.convertEntityToVo(&scores[i])) vos = append(vos, s.convertEntityToVo(&scores[i]))
} }
@ -86,10 +87,10 @@ func NewUserScoreService() *UserScoreService {
} }
// SaveUserScore 保存用户成绩并返回 VO // SaveUserScore 保存用户成绩并返回 VO
func (s *UserScoreService) SaveUserScore(req *dto.SaveScoreRequest) (*vo.UserScoreVO, error) { func (s *UserScoreService) SaveUserScore(req *dto.SaveScoreRequest) (vo.UserScoreVO, error) {
// 1. 业务验证 // 1. 业务验证
if err := req.Validate(); err != nil { if err := req.Validate(); err != nil {
return nil, err return vo.UserScoreVO{}, err
} }
// 2. DTO 转 Entity // 2. DTO 转 Entity
entityItem := s.convertDtoToEntity(req) entityItem := s.convertDtoToEntity(req)
@ -109,19 +110,39 @@ func (s *UserScoreService) SaveUserScore(req *dto.SaveScoreRequest) (*vo.UserSco
Where("create_by = ? AND state = ?", req.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 vo.UserScoreVO{}, fmt.Errorf("更新旧记录失败: %w", err)
} }
// 保存新的成绩记录 // 保存新的成绩记录
if err := tx.Create(entityItem).Error; err != nil { if err := tx.Create(entityItem).Error; err != nil {
tx.Rollback() tx.Rollback()
return nil, fmt.Errorf("保存记录失败: %w", err) return vo.UserScoreVO{}, fmt.Errorf("保存记录失败: %w", err)
}
// 根据成绩计算用户的专业信息
if err := tx.Commit().Error; err != nil {
return nil, fmt.Errorf("提交事务失败: %w", err)
} }
return s.convertEntityToVo(entityItem), nil userScoreVO := s.convertEntityToVo(entityItem)
// 根据成绩计算用户的专业信息
schoolMajorItems, err := s.yxCalculationMajorService.ListByUserQueryType("美术与设计类", "文科", []string{})
if err != nil {
tx.Rollback()
return vo.UserScoreVO{}, fmt.Errorf("查询专业信息失败: %w", err)
}
// 检查录取概率
s.yxCalculationMajorService.CheckEnrollProbability(&schoolMajorItems, userScoreVO)
// 插入到数据库
err = s.yxCalculationMajorService.BatchCreateBySchoolMajorDTO(schoolMajorItems, userScoreVO.ID)
if err != nil {
tx.Rollback()
return vo.UserScoreVO{}, fmt.Errorf("保存专业信息失败: %w", err)
}
// 提交事务
if err := tx.Commit().Error; err != nil {
tx.Rollback()
return vo.UserScoreVO{}, fmt.Errorf("提交事务失败: %w", err)
}
return userScoreVO, nil
} }
// 私有方法DTO 转 Entity // 私有方法DTO 转 Entity
@ -164,8 +185,8 @@ func (s *UserScoreService) convertDtoToEntity(req *dto.SaveScoreRequest) *entity
} }
// 私有方法Entity 转 VO // 私有方法Entity 转 VO
func (s *UserScoreService) convertEntityToVo(item *entity.YxUserScore) *vo.UserScoreVO { func (s *UserScoreService) convertEntityToVo(item *entity.YxUserScore) vo.UserScoreVO {
voItem := &vo.UserScoreVO{ voItem := vo.UserScoreVO{
ID: item.ID, ID: item.ID,
Type: item.Type, Type: item.Type,
EducationalLevel: item.EducationalLevel, EducationalLevel: item.EducationalLevel,

View File

@ -64,12 +64,8 @@ func ComputeHistoryMajorEnrollScoreLineDifferenceWithRulesEnrollProbability(majo
// 特殊逻辑:高职高专(非体育类)需计算分差率(分差/省控线) // 特殊逻辑:高职高专(非体育类)需计算分差率(分差/省控线)
// Java: boolean isVocationalCollege = "高职高专".equals(enrollData.getBatch()); // Java: boolean isVocationalCollege = "高职高专".equals(enrollData.getBatch());
// boolean isSportsMajor = "体育类".equals(enrollData.getMajorType()); // boolean isSportsMajor = "体育类".equals(enrollData.getMajorType());
isSportsMajor := "体育类" == majorType if "体育类" == majorType {
if isSportsMajor { if "2024" == enrollData.Year && "专过文排" != enrollData.EnrollmentCode && "文过专排" != enrollData.EnrollmentCode {
if "2024" == enrollData.Year && "专过文排" != enrollData.EnrollmentCode && "文过专排" != enrollData.EnrollmentCode { // enrollment_code 对应 rulesEnrollProbability? No.
// Java uses rulesEnrollProbability field in entity.
// Go entity YxHistoryMajorEnroll doesn't have RulesEnrollProbability field?
// Let's check entity definition again.
currentDiff = currentDiff * common.Number7p5 currentDiff = currentDiff * common.Number7p5
} else if "2024" == enrollData.Year && "文过专排" == enrollData.EnrollmentCode { // Placeholder } else if "2024" == enrollData.Year && "文过专排" == enrollData.EnrollmentCode { // Placeholder
currentDiff = currentDiff * common.Number5 currentDiff = currentDiff * common.Number5

View File

@ -2,11 +2,14 @@
package service package service
import ( import (
"fmt"
"server/common" "server/common"
"server/modules/user/vo" "server/modules/user/vo"
"server/modules/yx/dto" "server/modules/yx/dto"
"server/modules/yx/entity" "server/modules/yx/entity"
"server/modules/yx/mapper" "server/modules/yx/mapper"
"strings"
"time"
"github.com/google/uuid" "github.com/google/uuid"
) )
@ -17,6 +20,62 @@ type YxCalculationMajorService struct {
historyScoreControlLineService *YxHistoryScoreControlLineService historyScoreControlLineService *YxHistoryScoreControlLineService
} }
func (s *YxCalculationMajorService) BatchCreateBySchoolMajorDTO(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(entities)
}
func NewYxCalculationMajorService() *YxCalculationMajorService { func NewYxCalculationMajorService() *YxCalculationMajorService {
return &YxCalculationMajorService{ return &YxCalculationMajorService{
historyMajorEnrollService: NewYxHistoryMajorEnrollService(), historyMajorEnrollService: NewYxHistoryMajorEnrollService(),

View File

@ -58,6 +58,14 @@ func TestSchoolMajorQuery(t *testing.T) {
cs.CheckEnrollProbability(&items, userScoreVO) cs.CheckEnrollProbability(&items, userScoreVO)
elapsed := time.Now().UnixMilli() - startTime.UnixMilli() elapsed := time.Now().UnixMilli() - startTime.UnixMilli()
common.LogInfo("CheckEnrollProbability elapsed: %v", elapsed) common.LogInfo("CheckEnrollProbability elapsed: %v", elapsed)
// 插入到数据库
// err = cs.BatchCreateBySchoolMajorDTO(items)
if err != nil {
t.Fatalf("插入数据库失败: %v", err)
}
t.Logf("成功插入 %d 条记录到数据库", len(items))
// 打印前几条结果 // 打印前几条结果
// for i, item := range items { // for i, item := range items {
// if i >= 3 { // if i >= 3 {
@ -76,6 +84,6 @@ func TestIDGenerator(t *testing.T) {
idStr := common.GenerateStringID() idStr := common.GenerateStringID()
t.Logf("生成的 String ID: %s", idStr) t.Logf("生成的 String ID: %s", idStr)
idTimestamp := common.GetIDGenerator().GenerateTimestampLongID() // idTimestamp := common.GetIDGenerator().GenerateTimestampLongID()
t.Logf("生成的 Timestamp Long ID: %d", idTimestamp) // t.Logf("生成的 Timestamp Long ID: %d", idTimestamp)
} }