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
import (
"fmt"
"strconv"
"sync"
"time"
@ -9,7 +8,7 @@ import (
// IDGenerator 简单的 ID 生成器(类似 Snowflake
type IDGenerator struct {
mu sync.Mutex
mu sync.RWMutex
lastTime int64
sequence int64
machineID int64
@ -24,14 +23,44 @@ var (
func GetIDGenerator() *IDGenerator {
once.Do(func() {
defaultGenerator = &IDGenerator{
machineID: 1, // 默认机器 ID实际可从配置读取
machineID: 1,
}
})
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
// 格式:时间戳(毫秒) + 机器ID + 序列号
func (g *IDGenerator) NextID() int64 {
g.mu.Lock()
defer g.mu.Unlock()
@ -39,9 +68,8 @@ func (g *IDGenerator) NextID() int64 {
now := time.Now().UnixMilli()
if now == g.lastTime {
g.sequence = (g.sequence + 1) & 4095 // 12位序列号
g.sequence = (g.sequence + 1) & 4095
if g.sequence == 0 {
// 序列号溢出,等待下一毫秒
for now <= g.lastTime {
now = time.Now().UnixMilli()
}
@ -51,53 +79,11 @@ func (g *IDGenerator) NextID() int64 {
}
g.lastTime = now
// 简单的位运算组合 ID
// 时间戳(41位) | 机器ID(10位) | 序列号(12位)
// 这里为了简单,直接返回时间戳+序列号的组合
// 如果需要严格的 long 类型且包含时间戳信息:
return now*10000 + g.sequence
}
// NextIDStr 生成字符串类型的 ID
func (g *IDGenerator) NextIDStr() string {
return strconv.FormatInt(g.NextID(), 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
id := g.NextID()
return strconv.FormatInt(id, 10)
}

View File

@ -19,11 +19,12 @@ import (
type UserScoreService struct {
yxUserScoreService *service.YxUserScoreService
yxCalculationMajorService *service.YxCalculationMajorService
mapper *mapper.YxUserScoreMapper
}
// GetActiveByID 获取当前激活的成绩
func (s *UserScoreService) GetActiveByID(userID string) (*vo.UserScoreVO, error) {
func (s *UserScoreService) GetActiveByID(userID string) (vo.UserScoreVO, error) {
var score entity.YxUserScore
// 明确指定字段,提高可读性
err := config.DB.Model(&entity.YxUserScore{}).
@ -31,32 +32,32 @@ func (s *UserScoreService) GetActiveByID(userID string) (*vo.UserScoreVO, error)
First(&score).Error
if err != nil {
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
}
// GetByID 根据 ID 获取成绩
func (s *UserScoreService) GetByID(id string) (*vo.UserScoreVO, error) {
func (s *UserScoreService) GetByID(id string) (vo.UserScoreVO, error) {
var score entity.YxUserScore
err := config.DB.Model(&entity.YxUserScore{}).
Where("id = ?", id).
First(&score).Error
if err != nil {
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
}
// 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 total int64
@ -70,7 +71,7 @@ func (s *UserScoreService) ListByUser(userID string, page, size int) ([]*vo.User
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 {
vos = append(vos, s.convertEntityToVo(&scores[i]))
}
@ -86,10 +87,10 @@ func NewUserScoreService() *UserScoreService {
}
// SaveUserScore 保存用户成绩并返回 VO
func (s *UserScoreService) SaveUserScore(req *dto.SaveScoreRequest) (*vo.UserScoreVO, error) {
func (s *UserScoreService) SaveUserScore(req *dto.SaveScoreRequest) (vo.UserScoreVO, error) {
// 1. 业务验证
if err := req.Validate(); err != nil {
return nil, err
return vo.UserScoreVO{}, err
}
// 2. DTO 转 Entity
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").
Updates(map[string]interface{}{"state": "2"}).Error; err != nil {
tx.Rollback()
return nil, fmt.Errorf("更新旧记录失败: %w", err)
return vo.UserScoreVO{}, fmt.Errorf("更新旧记录失败: %w", err)
}
// 保存新的成绩记录
if err := tx.Create(entityItem).Error; err != nil {
tx.Rollback()
return nil, fmt.Errorf("保存记录失败: %w", err)
}
// 根据成绩计算用户的专业信息
if err := tx.Commit().Error; err != nil {
return nil, fmt.Errorf("提交事务失败: %w", err)
return vo.UserScoreVO{}, 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
@ -164,8 +185,8 @@ func (s *UserScoreService) convertDtoToEntity(req *dto.SaveScoreRequest) *entity
}
// 私有方法Entity 转 VO
func (s *UserScoreService) convertEntityToVo(item *entity.YxUserScore) *vo.UserScoreVO {
voItem := &vo.UserScoreVO{
func (s *UserScoreService) convertEntityToVo(item *entity.YxUserScore) vo.UserScoreVO {
voItem := vo.UserScoreVO{
ID: item.ID,
Type: item.Type,
EducationalLevel: item.EducationalLevel,

View File

@ -64,12 +64,8 @@ func ComputeHistoryMajorEnrollScoreLineDifferenceWithRulesEnrollProbability(majo
// 特殊逻辑:高职高专(非体育类)需计算分差率(分差/省控线)
// Java: boolean isVocationalCollege = "高职高专".equals(enrollData.getBatch());
// boolean isSportsMajor = "体育类".equals(enrollData.getMajorType());
isSportsMajor := "体育类" == majorType
if isSportsMajor {
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.
if "体育类" == majorType {
if "2024" == enrollData.Year && "专过文排" != enrollData.EnrollmentCode && "文过专排" != enrollData.EnrollmentCode {
currentDiff = currentDiff * common.Number7p5
} else if "2024" == enrollData.Year && "文过专排" == enrollData.EnrollmentCode { // Placeholder
currentDiff = currentDiff * common.Number5

View File

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

View File

@ -58,6 +58,14 @@ func TestSchoolMajorQuery(t *testing.T) {
cs.CheckEnrollProbability(&items, userScoreVO)
elapsed := time.Now().UnixMilli() - startTime.UnixMilli()
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 {
// if i >= 3 {
@ -76,6 +84,6 @@ func TestIDGenerator(t *testing.T) {
idStr := common.GenerateStringID()
t.Logf("生成的 String ID: %s", idStr)
idTimestamp := common.GetIDGenerator().GenerateTimestampLongID()
t.Logf("生成的 Timestamp Long ID: %d", idTimestamp)
// idTimestamp := common.GetIDGenerator().GenerateTimestampLongID()
// t.Logf("生成的 Timestamp Long ID: %d", idTimestamp)
}