Compare commits

..

3 Commits

Author SHA1 Message Date
zhouwentao 1b95f4a6c6 updates 2026-01-24 10:09:29 +08:00
zhouwentao ee8c304284 updates 2026-01-24 10:04:53 +08:00
zhouwentao 85819798ca id生成类调整 2026-01-23 20:16:18 +08:00
5 changed files with 98 additions and 38 deletions

3
.gitignore vendored
View File

@ -10,4 +10,5 @@ rebel.xml
## front ## front
**/*.lock **/*.lock
**/*.exe **/*.exe
**/*.exe***

Binary file not shown.

View File

@ -1,17 +1,48 @@
package common package common
import ( import (
"errors"
"strconv" "strconv"
"sync" "sync"
"time" "time"
) )
// IDGenerator 简单的 ID 生成器(类似 Snowflake /**
分布式/多实例运行重要
如果你有多个 Pod 或服务器必须在程序启动时给它们分配不同的 ID否则还是会冲突
func main() {
// 获取当前机器的编号,比如从配置文件或环境变量读取
// 假设这是第 2 号机器
myMachineID := int64(2)
// 初始化
err := common.InitGenerator(myMachineID)
if err != nil {
panic(err)
}
// 之后随处调用
println(common.GenerateStringID())
}
*/
// 定义常量 (标准雪花算法配置)
const (
epoch = int64(1704067200000) // 起始时间戳 2024-01-01
workerBits = uint(10) // 机器ID位数
sequenceBits = uint(12) // 序列号位数
maxWorker = -1 ^ (-1 << workerBits)
maxSequence = -1 ^ (-1 << sequenceBits)
workerShift = sequenceBits
timestampShift = sequenceBits + workerBits
)
type IDGenerator struct { type IDGenerator struct {
mu sync.RWMutex mu sync.Mutex
lastTime int64 lastTime int64
sequence int64 workerID int64
machineID int64 sequence int64
} }
var ( var (
@ -19,56 +50,84 @@ var (
once sync.Once once sync.Once
) )
// GetIDGenerator 获取默认生成器单例 // InitGenerator 初始化单例生成器
func GetIDGenerator() *IDGenerator { // 修复:校验逻辑移到 once.Do 外部,防止校验失败消耗掉 once 的执行机会
func InitGenerator(workerID int64) error {
// 1. 先校验,如果失败直接返回,不要触碰 once
if workerID < 0 || workerID > int64(maxWorker) {
return errors.New("worker ID excess of limit (0-1023)")
}
// 2. 执行初始化
once.Do(func() { once.Do(func() {
defaultGenerator = &IDGenerator{ defaultGenerator = &IDGenerator{
machineID: 1, workerID: workerID,
lastTime: 0,
sequence: 0,
} }
}) })
return defaultGenerator return nil
} }
// Lazy initialize on first access // getInstance 获取单例
func getInstance() *IDGenerator { func getInstance() *IDGenerator {
// 双重检查,虽然 once.Do 是线程安全的,但如果 InitGenerator 没被调用过,
// 我们需要确保这里能兜底初始化
if defaultGenerator == nil { if defaultGenerator == nil {
once.Do(func() { once.Do(func() {
defaultGenerator = &IDGenerator{ defaultGenerator = &IDGenerator{
machineID: 1, workerID: 1, // 默认机器ID防止未初始化导致 panic
lastTime: 0,
sequence: 0,
} }
}) })
} }
// 【关键修复】如果经过上面的逻辑 defaultGenerator 还是 nil
// (这种情况极少见,除非 InitGenerator 曾经被错误调用且没有赋值)
// 强制创建一个临时的或抛出 panic避免空指针崩溃
if defaultGenerator == nil {
// 最后的兜底,防止崩溃
return &IDGenerator{workerID: 1}
}
return defaultGenerator return defaultGenerator
} }
// GenerateStringID 全局辅助函数:生成 string 类型 ID // GenerateLongID 全局辅助函数
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 { func GenerateLongID() int64 {
gen := getInstance() return getInstance().NextID()
if gen == nil {
gen = &IDGenerator{machineID: 1}
}
return gen.NextID()
} }
// NextID 生成下一个 64 位整数 ID // GenerateStringID 全局辅助函数
func GenerateStringID() string {
return strconv.FormatInt(getInstance().NextID(), 10)
}
// NextID 生成下一个 ID
func (g *IDGenerator) NextID() int64 { func (g *IDGenerator) NextID() int64 {
// 防御性编程:防止 g 为 nil
if g == nil {
// 如果实例是 nil尝试获取默认实例
if defaultGenerator != nil {
g = defaultGenerator
} else {
// 极端情况,创建一个临时对象(虽然锁不住全局,但能防崩)
g = &IDGenerator{workerID: 1}
}
}
g.mu.Lock() g.mu.Lock()
defer g.mu.Unlock() defer g.mu.Unlock()
now := time.Now().UnixMilli() now := time.Now().UnixMilli()
if now < g.lastTime {
now = g.lastTime
}
if now == g.lastTime { if now == g.lastTime {
g.sequence = (g.sequence + 1) & 4095 g.sequence = (g.sequence + 1) & int64(maxSequence)
if g.sequence == 0 { if g.sequence == 0 {
for now <= g.lastTime { for now <= g.lastTime {
now = time.Now().UnixMilli() now = time.Now().UnixMilli()
@ -79,11 +138,6 @@ func (g *IDGenerator) NextID() int64 {
} }
g.lastTime = now g.lastTime = now
return now*10000 + g.sequence
}
// NextIDStr 生成字符串类型的 ID return ((now - epoch) << timestampShift) | (g.workerID << workerShift) | g.sequence
func (g *IDGenerator) NextIDStr() string {
id := g.NextID()
return strconv.FormatInt(id, 10)
} }

View File

@ -38,6 +38,10 @@ type UserMajorDTO struct {
// PrivateStudentScore float64 `json:"privateStudentScore"` // 学生折合分(私有) // PrivateStudentScore float64 `json:"privateStudentScore"` // 学生折合分(私有)
// StudentConvertedScore float64 `json:"studentConvertedScore"` // 学生折合分(转换后) // StudentConvertedScore float64 `json:"studentConvertedScore"` // 学生折合分(转换后)
// FirstLevelDiscipline string `json:"firstLevelDiscipline"` // 一级学科 (需确认来源) // FirstLevelDiscipline string `json:"firstLevelDiscipline"` // 一级学科 (需确认来源)
Province string `json:"province"` // 省份
SchoolNature string `json:"schoolNature"` // 院校性质
InstitutionType string `json:"institutionType"` // 院校类型
} }
// SchoolMajorDTO 院校专业查询结果 DTO // SchoolMajorDTO 院校专业查询结果 DTO

View File

@ -125,8 +125,8 @@ func (m *YxCalculationMajorMapper) FindRecommendList(query dto.SchoolMajorQuery)
cm.limitation, cm.limitation,
cm.other_score_limitation, cm.other_score_limitation,
s.province as province, s.province as province,
s.school_nature as schoolNature, s.school_nature,
s.institution_type as institutionType s.institution_type
FROM %s cm FROM %s cm
LEFT JOIN yx_school_child sc ON sc.school_code = cm.school_code LEFT JOIN yx_school_child sc ON sc.school_code = cm.school_code
LEFT JOIN yx_school_research_teaching srt ON srt.school_id = sc.school_id LEFT JOIN yx_school_research_teaching srt ON srt.school_id = sc.school_id
@ -146,6 +146,7 @@ func (m *YxCalculationMajorMapper) FindRecommendList(query dto.SchoolMajorQuery)
mainSQL += " AND (cm.enroll_probability >= 93)" mainSQL += " AND (cm.enroll_probability >= 93)"
} }
mainSQL += " ORDER BY cm.enroll_probability DESC"
// 6. 分页参数合法性校验 // 6. 分页参数合法性校验
page := query.Page page := query.Page
size := query.Size size := query.Size