This commit is contained in:
oneao committed 2026-04-20 17:20:47 +08:00
1 parent 89aaef1d6b
commit 6d54a9e407
192 files changed
+971 -51708

No files matched your search

-52
View File
@@ -1,52 +0,0 @@
package db
import (
"fmt"
"strconv"
"strings"
)
func validateData(table string, keyField string, data map[string]any) (any, error) {
if table == "" {
return nil, fmt.Errorf("表名不能为空")
}
if len(data) == 0 {
return nil, fmt.Errorf("数据不能为空")
}
if keyField == "" {
return nil, fmt.Errorf("主键字段不能为空")
}
val, ok := data[keyField]
if !ok {
return nil, fmt.Errorf("缺少主键字段: %s", keyField)
}
if val == nil {
return nil, fmt.Errorf("主键字段值不能为空")
}
return val, nil
}
// 高效 placeholder 转换
func convertPlaceholder(sql string) string {
var sb strings.Builder
sb.Grow(len(sql))
argIndex := 1
for i := 0; i < len(sql); i++ {
if sql[i] == '?' {
sb.WriteByte('$')
sb.WriteString(strconv.Itoa(argIndex))
argIndex++
} else {
sb.WriteByte(sql[i])
}
}
return sb.String()
}
-42
View File
@@ -1,42 +0,0 @@
package db
import (
"context"
"github.com/jackc/pgx/v5"
"github.com/jackc/pgx/v5/pgconn"
"github.com/jackc/pgx/v5/pgxpool"
)
var defaultClient *Client
type Executor interface {
Exec(ctx context.Context, sql string, args ...any) (pgconn.CommandTag, error)
Query(ctx context.Context, sql string, args ...any) (pgx.Rows, error)
QueryRow(ctx context.Context, sql string, args ...any) pgx.Row
}
type Client struct {
pool *pgxpool.Pool
tx pgx.Tx
}
// Init 初始化
func Init(pool *pgxpool.Pool) {
defaultClient = &Client{pool: pool}
}
// New 创建会话
func New() *Client {
return &Client{
pool: defaultClient.pool,
}
}
// 内部获取执行器(关键)
func (c *Client) exec() Executor {
if c.tx != nil {
return c.tx
}
return c.pool
}
-83
View File
@@ -1,83 +0,0 @@
package db
import (
"context"
"fmt"
"strings"
)
func (c *Client) Delete(
ctx context.Context,
table string,
keyField string,
data map[string]any,
) error {
if keyField == "" {
return fmt.Errorf("keyField 不能为空")
}
keyVal, err := validateData(table, keyField, data)
if err != nil {
return err
}
sql := fmt.Sprintf(
"DELETE FROM %s WHERE %s = $1",
table,
keyField,
)
_, err = c.exec().Exec(ctx, sql, keyVal)
return err
}
func (c *Client) BatchDelete(
ctx context.Context,
table string,
keyField string,
dataList []map[string]any,
) error {
if table == "" {
return fmt.Errorf("表名不能为空")
}
if keyField == "" {
return fmt.Errorf("主键字段不能为空")
}
if len(dataList) == 0 {
return fmt.Errorf("数据不能为空")
}
var (
placeholders []string
args []any
argIndex = 1
)
for _, data := range dataList {
keyVal, err := validateData(table, keyField, data)
if err != nil {
return err
}
if keyVal == nil {
return fmt.Errorf("主键字段值不能为空")
}
placeholders = append(placeholders, fmt.Sprintf("$%d", argIndex))
args = append(args, keyVal)
argIndex++
}
sql := fmt.Sprintf(
"DELETE FROM %s WHERE %s IN (%s)",
table,
keyField,
strings.Join(placeholders, ", "),
)
_, err := c.exec().Exec(ctx, sql, args...)
return err
}
-120
View File
@@ -1,120 +0,0 @@
package db
import (
"context"
"fmt"
"sort"
"strings"
)
// 构建 INSERT SQL(单条)
func buildInsertSQL(table string, data map[string]any) (string, []any) {
var (
columns []string
placeholders []string
args []any
)
i := 1
for col, val := range data {
columns = append(columns, col)
placeholders = append(placeholders, fmt.Sprintf("$%d", i))
args = append(args, val)
i++
}
sql := fmt.Sprintf(
"INSERT INTO %s (%s) VALUES (%s)",
table,
strings.Join(columns, ", "),
strings.Join(placeholders, ", "),
)
return sql, args
}
func (c *Client) Insert(
ctx context.Context,
table string,
keyField string,
data map[string]any,
) error {
if _, err := validateData(table, keyField, data); err != nil {
return err
}
sql, args := buildInsertSQL(table, data)
_, err := c.exec().Exec(ctx, sql, args...)
return err
}
func (c *Client) BatchInsert(
ctx context.Context,
table string,
keyField string,
dataList []map[string]any,
) error {
if len(dataList) == 0 {
return fmt.Errorf("数据不能为空")
}
// 用第一条确定列顺序
first := dataList[0]
if _, err := validateData(table, keyField, first); err != nil {
return err
}
var columns []string
for col := range first {
columns = append(columns, col)
}
sort.Strings(columns)
var (
valueStrings []string
args []any
argIndex = 1
)
for _, data := range dataList {
// 统一校验
if _, err := validateData(table, keyField, data); err != nil {
return err
}
// 字段数量检查
if len(data) != len(columns) {
return fmt.Errorf("批量插入失败:数据字段不一致")
}
var placeholders []string
for _, col := range columns {
val, ok := data[col]
if !ok {
return fmt.Errorf("缺少字段: %s", col)
}
placeholders = append(placeholders, fmt.Sprintf("$%d", argIndex))
args = append(args, val)
argIndex++
}
valueStrings = append(
valueStrings,
fmt.Sprintf("(%s)", strings.Join(placeholders, ", ")),
)
}
sql := fmt.Sprintf(
"INSERT INTO %s (%s) VALUES %s",
table,
strings.Join(columns, ", "),
strings.Join(valueStrings, ", "),
)
_, err := c.exec().Exec(ctx, sql, args...)
return err
}
-82
View File
@@ -1,82 +0,0 @@
package db
import (
"context"
"fmt"
"strings"
)
func (c *Client) LoadData(
ctx context.Context,
viewName string,
searchCondition string,
orderBy string,
searchColumns []string,
args []any,
) ([][]any, error) {
var where string
// WHERE 构造
if searchCondition != "" && len(searchColumns) > 0 {
var conditions []string
for _, col := range searchColumns {
conditions = append(conditions, fmt.Sprintf("%s LIKE ?", col))
}
where = " WHERE (" + strings.Join(conditions, " OR ") + ")"
// 自动加 %
for i := range args {
if s, ok := args[i].(string); ok {
args[i] = "%" + s + "%"
}
}
}
// ORDER BY
var order string
if orderBy != "" {
order = " ORDER BY " + orderBy
}
sql := fmt.Sprintf("SELECT * FROM %s%s%s", viewName, where, order)
return c.query(ctx, sql, args...)
}
func (c *Client) LoadDataBySQL(
ctx context.Context,
sql string,
args []any,
) ([][]any, error) {
return c.query(ctx, sql, args...)
}
// 统一查询方法(核心优化)
func (c *Client) query(ctx context.Context, sql string, args ...any) ([][]any, error) {
sql = convertPlaceholder(sql)
rows, err := c.exec().Query(ctx, sql, args...)
if err != nil {
return nil, err
}
defer rows.Close()
result := make([][]any, 0, 16)
for rows.Next() {
values, err := rows.Values()
if err != nil {
return nil, err
}
row := make([]any, len(values))
copy(row, values)
result = append(result, row)
}
return result, rows.Err()
}
-34
View File
@@ -1,34 +0,0 @@
package db
import (
"context"
"github.com/jackc/pgx/v5"
)
// WithTx 开启事务(在当前 client 上)
func (c *Client) WithTx(ctx context.Context, fn func(tx *Client) error) error {
tx, err := c.pool.Begin(ctx)
if err != nil {
return err
}
// 创建事务 client
txClient := &Client{
pool: c.pool,
tx: tx,
}
defer func(tx pgx.Tx, ctx context.Context) {
err := tx.Rollback(ctx)
if err != nil {
}
}(tx, ctx)
if err := fn(txClient); err != nil {
return err
}
return tx.Commit(ctx)
}
-140
View File
@@ -1,140 +0,0 @@
package db
import (
"context"
"fmt"
"sort"
"strings"
)
func (c *Client) Update(
ctx context.Context,
table string,
keyField string,
data map[string]any,
) error {
keyVal, err := validateData(table, keyField, data)
if err != nil {
return err
}
var (
setClauses []string
args []any
i = 1
)
for col, val := range data {
if col == keyField {
continue
}
setClauses = append(setClauses, fmt.Sprintf("%s=$%d", col, i))
args = append(args, val)
i++
}
// WHERE 条件
where := fmt.Sprintf("%s=$%d", keyField, i)
args = append(args, keyVal)
sql := fmt.Sprintf(
"UPDATE %s SET %s WHERE %s",
table,
strings.Join(setClauses, ", "),
where,
)
_, err = c.exec().Exec(ctx, sql, args...)
return err
}
func (c *Client) BatchUpdate(
ctx context.Context,
table string,
keyField string,
dataList []map[string]any,
) error {
if len(dataList) == 0 {
return fmt.Errorf("数据不能为空")
}
// 用第一条数据确定字段
first := dataList[0]
if _, err := validateData(table, keyField, first); err != nil {
return err
}
// 提取字段(排除主键)+ 排序(关键)
var columns []string
for col := range first {
if col != keyField {
columns = append(columns, col)
}
}
sort.Strings(columns)
var (
args []any
argIndex = 1
)
// CASE 语句
var setClauses []string
for _, col := range columns {
var caseBuilder strings.Builder
caseBuilder.WriteString(fmt.Sprintf("%s = CASE %s ", col, keyField))
for _, data := range dataList {
// 校验 key
keyVal, err := validateData(table, keyField, data)
if err != nil {
return err
}
val, ok := data[col]
if !ok {
return fmt.Errorf("缺少字段: %s", col)
}
caseBuilder.WriteString(fmt.Sprintf(
"WHEN $%d THEN $%d ",
argIndex,
argIndex+1,
))
args = append(args, keyVal, val)
argIndex += 2
}
caseBuilder.WriteString("END")
setClauses = append(setClauses, caseBuilder.String())
}
// WHERE IN
var wherePlaceholders []string
for _, data := range dataList {
keyVal, ok := data[keyField]
if !ok || keyVal == nil {
return fmt.Errorf("缺少主键字段值: %s", keyField)
}
wherePlaceholders = append(wherePlaceholders, fmt.Sprintf("$%d", argIndex))
args = append(args, keyVal)
argIndex++
}
sql := fmt.Sprintf(
"UPDATE %s SET %s WHERE %s IN (%s)",
table,
strings.Join(setClauses, ", "),
keyField,
strings.Join(wherePlaceholders, ", "),
)
_, err := c.exec().Exec(ctx, sql, args...)
return err
}
-112
View File
@@ -1,112 +0,0 @@
package jwtx
import (
"context"
"sync"
"time"
"github.com/golang-jwt/jwt/v5"
)
// ==================
// 内部管理器
// ==================
type jwtManager struct {
secret string
tokenExpiry time.Duration
}
var (
manager *jwtManager
once sync.Once
)
// ==================
// 初始化
// ==================
// Init 初始化(只执行一次)
// expiry = -1 表示不过期
func Init(secret string, expiry time.Duration) {
once.Do(func() {
manager = &jwtManager{
secret: secret,
tokenExpiry: expiry,
}
})
}
func getManager() *jwtManager {
if manager == nil {
panic("jwt未初始化,请先调用Init")
}
return manager
}
// ==================
// Claims(通用结构)
// ==================
type CustomClaims struct {
Data map[string]any `json:"data"`
jwt.RegisteredClaims
}
// ==================
// 对外方法
// ==================
// CreateToken 生成 JWT
func CreateToken(ctx context.Context, data map[string]any) (string, error) {
m := getManager()
return m.createToken(data)
}
// VerifyToken 校验 token 并返回 claims
func VerifyToken(ctx context.Context, tokenString string) (*CustomClaims, bool) {
if tokenString == "" {
return nil, false
}
m := getManager()
token, err := jwt.ParseWithClaims(tokenString, &CustomClaims{}, func(token *jwt.Token) (interface{}, error) {
return []byte(m.secret), nil
})
if err != nil || !token.Valid {
return nil, false
}
claims, ok := token.Claims.(*CustomClaims)
if !ok {
return nil, false
}
return claims, true
}
// ==================
// 内部实现
// ==================
func (j *jwtManager) createToken(data map[string]any) (string, error) {
now := time.Now()
claims := &CustomClaims{
Data: data,
RegisteredClaims: jwt.RegisteredClaims{
IssuedAt: jwt.NewNumericDate(now),
},
}
// ✅ 只有 >0 才设置过期时间
// -1 / 0 都表示不过期
if j.tokenExpiry > 0 {
claims.ExpiresAt = jwt.NewNumericDate(now.Add(j.tokenExpiry))
}
token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
return token.SignedString([]byte(j.secret))
}
-233
View File
@@ -1,233 +0,0 @@
package logger
import (
"context"
"os"
"path/filepath"
"strings"
"time"
"github.com/gofiber/fiber/v3"
"go.uber.org/zap"
"go.uber.org/zap/zapcore"
"gopkg.in/natefinch/lumberjack.v2"
)
var Log *zap.Logger
const TraceIDKey = "request_id"
// ======================
// 配置结构
// ======================
type Config struct {
Dir string // 日志目录
Level string // debug info warn error
MaxSize int // MB
MaxBackups int
MaxAge int // 天
Compress bool // 是否压缩
}
// ======================
// 初始化
// ======================
func Init(cfg Config) {
if cfg.Dir == "" {
cfg.Dir = "logs"
}
if cfg.Level == "" {
cfg.Level = "info"
}
level := parseLevel(cfg.Level)
// ======================
// encoder 配置
// ======================
encoderConfig := zapcore.EncoderConfig{
TimeKey: "time",
LevelKey: "level",
MessageKey: "msg",
CallerKey: "caller",
EncodeCaller: zapcore.ShortCallerEncoder,
}
// 👉 文件 JSON encoder
jsonEncoder := zapcore.NewJSONEncoder(func() zapcore.EncoderConfig {
cfg := encoderConfig
cfg.EncodeLevel = zapcore.CapitalLevelEncoder
cfg.EncodeTime = zapcore.TimeEncoderOfLayout("2006-01-02 15:04:05")
return cfg
}())
// 👉 控制台 encoder(可读)
consoleEncoder := zapcore.NewConsoleEncoder(func() zapcore.EncoderConfig {
cfg := encoderConfig
cfg.EncodeLevel = zapcore.CapitalColorLevelEncoder
cfg.EncodeTime = zapcore.TimeEncoderOfLayout("2006-01-02 15:04:05")
return cfg
}())
// ======================
// writer
// ======================
consoleWriter := zapcore.AddSync(os.Stdout)
infoWriter := getWriter(cfg, "info")
errorWriter := getWriter(cfg, "error")
// ======================
// core
// ======================
// 控制台(所有级别)
consoleCore := zapcore.NewCore(
consoleEncoder,
consoleWriter,
level,
)
// info 文件
infoCore := zapcore.NewCore(
jsonEncoder,
infoWriter,
zap.LevelEnablerFunc(func(lvl zapcore.Level) bool {
return lvl < zapcore.ErrorLevel && lvl >= level
}),
)
// error 文件
errorCore := zapcore.NewCore(
jsonEncoder,
errorWriter,
zap.LevelEnablerFunc(func(lvl zapcore.Level) bool {
return lvl >= zapcore.ErrorLevel
}),
)
core := zapcore.NewTee(
consoleCore,
infoCore,
errorCore,
)
Log = zap.New(
core,
zap.AddCaller(), // 打印调用位置
zap.AddCallerSkip(1), // 跳过封装层
)
}
// ======================
// Daily Writer(按天切割)
// ======================
type DailyWriter struct {
cfg Config
level string
log *lumberjack.Logger
date string
}
func newDailyWriter(cfg Config, level string) *DailyWriter {
return &DailyWriter{
cfg: cfg,
level: level,
}
}
func (w *DailyWriter) Write(p []byte) (n int, err error) {
today := time.Now().Format("2006-01-02")
if w.log == nil || w.date != today {
dir := filepath.Join(w.cfg.Dir, w.level)
_ = os.MkdirAll(dir, os.ModePerm)
filename := filepath.Join(dir, today+".log")
w.log = &lumberjack.Logger{
Filename: filename,
MaxSize: w.cfg.MaxSize,
MaxBackups: w.cfg.MaxBackups,
MaxAge: w.cfg.MaxAge,
Compress: w.cfg.Compress,
}
w.date = today
}
return w.log.Write(p)
}
func (w *DailyWriter) Sync() error {
if w.log != nil {
return w.log.Close()
}
return nil
}
// ======================
// writer 封装
// ======================
func getWriter(cfg Config, level string) zapcore.WriteSyncer {
return zapcore.AddSync(newDailyWriter(cfg, level))
}
// ======================
// level 解析
// ======================
func parseLevel(lvl string) zapcore.Level {
switch strings.ToLower(lvl) {
case "debug":
return zap.DebugLevel
case "warn":
return zap.WarnLevel
case "error":
return zap.ErrorLevel
default:
return zap.InfoLevel
}
}
// ======================
// trace 支持
// ======================
func SetTraceID(c fiber.Ctx, traceID string) {
if c == nil {
return
}
ctx := context.WithValue(c.Context(), TraceIDKey, traceID)
c.SetContext(ctx)
}
func FromCtx(ctx context.Context) *zap.Logger {
if ctx == nil {
return Log
}
traceID, _ := ctx.Value(TraceIDKey).(string)
if traceID == "" {
return Log
}
return Log.With(zap.String("trace_id", traceID))
}
// ======================
// 优雅退出
// ======================
func Sync() {
if Log != nil {
_ = Log.Sync()
}
}
@@ -1,83 +0,0 @@
package uniqueid
import (
"strconv"
"time"
)
type DefaultIdGenerator struct {
Options *IdGeneratorOptions
SnowWorker ISnowWorker
IdGeneratorException IdGeneratorException
}
func NewDefaultIdGenerator(options *IdGeneratorOptions) *DefaultIdGenerator {
if options == nil {
panic("dig.Options error.")
}
// 1.BaseTime
minTime := int64(631123200000) // time.Now().AddDate(-30, 0, 0).UnixNano() / 1e6
if options.BaseTime < minTime || options.BaseTime > time.Now().UnixNano()/1e6 {
panic("BaseTime error.")
}
// 2.WorkerIdBitLength
if options.WorkerIdBitLength <= 0 {
panic("WorkerIdBitLength error.(range:[1, 21])")
}
if options.WorkerIdBitLength+options.SeqBitLength > 22 {
panic("error:WorkerIdBitLength + SeqBitLength <= 22")
}
// 3.WorkerId
maxWorkerIdNumber := uint16(1<<options.WorkerIdBitLength) - 1
if maxWorkerIdNumber == 0 {
maxWorkerIdNumber = 63
}
if options.WorkerId < 0 || options.WorkerId > maxWorkerIdNumber {
panic("WorkerId error. (range:[0, " + strconv.FormatUint(uint64(maxWorkerIdNumber), 10) + "]")
}
// 4.SeqBitLength
if options.SeqBitLength < 2 || options.SeqBitLength > 21 {
panic("SeqBitLength error. (range:[2, 21])")
}
// 5.MaxSeqNumber
maxSeqNumber := uint32(1<<options.SeqBitLength) - 1
if maxSeqNumber == 0 {
maxSeqNumber = 63
}
if options.MaxSeqNumber < 0 || options.MaxSeqNumber > maxSeqNumber {
panic("MaxSeqNumber error. (range:[1, " + strconv.FormatUint(uint64(maxSeqNumber), 10) + "]")
}
// 6.MinSeqNumber
if options.MinSeqNumber < 5 || options.MinSeqNumber > maxSeqNumber {
panic("MinSeqNumber error. (range:[5, " + strconv.FormatUint(uint64(maxSeqNumber), 10) + "]")
}
var snowWorker ISnowWorker
switch options.Method {
case 1:
snowWorker = NewSnowWorkerM1(options)
case 2:
snowWorker = NewSnowWorkerM2(options)
default:
snowWorker = NewSnowWorkerM1(options)
}
if options.Method == 1 {
time.Sleep(time.Duration(500) * time.Microsecond)
}
return &DefaultIdGenerator{
Options: options,
SnowWorker: snowWorker,
}
}
func (dig DefaultIdGenerator) NewLong() int64 {
return dig.SnowWorker.NextId()
}
@@ -1,5 +0,0 @@
package uniqueid
type IIdGenerator interface {
NewLong() uint64
}
@@ -1,5 +0,0 @@
package uniqueid
type ISnowWorker interface {
NextId() int64
}
@@ -1,12 +0,0 @@
package uniqueid
import "fmt"
type IdGeneratorException struct {
message string
error error
}
func (e IdGeneratorException) IdGeneratorException(message ...interface{}) {
fmt.Println(message)
}
@@ -1,25 +0,0 @@
package uniqueid
type IdGeneratorOptions struct {
Method uint16 // 雪花计算方法,(1-漂移算法|2-传统算法),默认1
BaseTime int64 // 基础时间(ms单位),不能超过当前系统时间
WorkerId uint16 // 机器码,必须由外部设定,最大值 2^WorkerIdBitLength-1
WorkerIdBitLength byte // 机器码位长,默认值6,取值范围 [1, 15](要求:序列数位长+机器码位长不超过22)
SeqBitLength byte // 序列数位长,默认值6,取值范围 [3, 21](要求:序列数位长+机器码位长不超过22)
MaxSeqNumber uint32 // 最大序列数(含),设置范围 [MinSeqNumber, 2^SeqBitLength-1],默认值0,表示最大序列数取最大值(2^SeqBitLength-1])
MinSeqNumber uint32 // 最小序列数(含),默认值5,取值范围 [5, MaxSeqNumber],每毫秒的前5个序列数对应编号0-4是保留位,其中1-4是时间回拨相应预留位,0是手工新值预留位
TopOverCostCount uint32 // 最大漂移次数(含),默认2000,推荐范围500-10000(与计算能力有关)
}
func NewIdGeneratorOptions(workerId uint16) *IdGeneratorOptions {
return &IdGeneratorOptions{
Method: 1,
WorkerId: workerId,
BaseTime: 1582136402000,
WorkerIdBitLength: 6,
SeqBitLength: 6,
MaxSeqNumber: 0,
MinSeqNumber: 5,
TopOverCostCount: 2000,
}
}
-29
View File
@@ -1,29 +0,0 @@
package uniqueid
import (
"sync"
)
var singletonMutex sync.Mutex
var idGenerator *DefaultIdGenerator
// SetIdGenerator .
func SetIdGenerator(options *IdGeneratorOptions) {
singletonMutex.Lock()
idGenerator = NewDefaultIdGenerator(options)
singletonMutex.Unlock()
}
// NextId .
func NextId() int64 {
if idGenerator == nil {
singletonMutex.Lock()
defer singletonMutex.Unlock()
if idGenerator == nil {
options := NewIdGeneratorOptions(1)
idGenerator = NewDefaultIdGenerator(options)
}
}
return idGenerator.NewLong()
}
@@ -1,19 +0,0 @@
package uniqueid
type OverCostActionArg struct {
ActionType int32
TimeTick int64
WorkerId uint16
OverCostCountInOneTerm int32
GenCountInOneTerm int32
TermIndex int32
}
func (ocaa OverCostActionArg) OverCostActionArg(workerId uint16, timeTick int64, actionType int32, overCostCountInOneTerm int32, genCountWhenOverCost int32, index int32) {
ocaa.ActionType = actionType
ocaa.TimeTick = timeTick
ocaa.WorkerId = workerId
ocaa.OverCostCountInOneTerm = overCostCountInOneTerm
ocaa.GenCountInOneTerm = genCountWhenOverCost
ocaa.TermIndex = index
}
@@ -1,243 +0,0 @@
package uniqueid
import (
"sync"
"time"
)
// SnowWorkerM1 .
type SnowWorkerM1 struct {
BaseTime int64 //基础时间
WorkerId uint16 //机器码
WorkerIdBitLength byte //机器码位长
SeqBitLength byte //自增序列数位长
MaxSeqNumber uint32 //最大序列数(含)
MinSeqNumber uint32 //最小序列数(含)
TopOverCostCount uint32 //最大漂移次数
_TimestampShift byte
_CurrentSeqNumber uint32
_LastTimeTick int64
_TurnBackTimeTick int64
_TurnBackIndex byte
_IsOverCost bool
_OverCostCountInOneTerm uint32
_GenCountInOneTerm uint32
_TermIndex uint32
sync.Mutex
}
// NewSnowWorkerM1 .
func NewSnowWorkerM1(options *IdGeneratorOptions) ISnowWorker {
var workerIdBitLength byte
var seqBitLength byte
var maxSeqNumber uint32
// 1.BaseTime
var baseTime int64
if options.BaseTime != 0 {
baseTime = options.BaseTime
} else {
baseTime = 1582136402000
}
// 2.WorkerIdBitLength
if options.WorkerIdBitLength == 0 {
workerIdBitLength = 6
} else {
workerIdBitLength = options.WorkerIdBitLength
}
// 3.WorkerId
var workerId = options.WorkerId
// 4.SeqBitLength
if options.SeqBitLength == 0 {
seqBitLength = 6
} else {
seqBitLength = options.SeqBitLength
}
// 5.MaxSeqNumber
if options.MaxSeqNumber <= 0 {
maxSeqNumber = (1 << seqBitLength) - 1
} else {
maxSeqNumber = options.MaxSeqNumber
}
// 6.MinSeqNumber
var minSeqNumber = options.MinSeqNumber
// 7.Others
var topOverCostCount = options.TopOverCostCount
if topOverCostCount == 0 {
topOverCostCount = 2000
}
timestampShift := (byte)(workerIdBitLength + seqBitLength)
currentSeqNumber := minSeqNumber
return &SnowWorkerM1{
BaseTime: baseTime,
WorkerIdBitLength: workerIdBitLength,
WorkerId: workerId,
SeqBitLength: seqBitLength,
MaxSeqNumber: maxSeqNumber,
MinSeqNumber: minSeqNumber,
TopOverCostCount: topOverCostCount,
_TimestampShift: timestampShift,
_CurrentSeqNumber: currentSeqNumber,
_LastTimeTick: 0,
_TurnBackTimeTick: 0,
_TurnBackIndex: 0,
_IsOverCost: false,
_OverCostCountInOneTerm: 0,
_GenCountInOneTerm: 0,
_TermIndex: 0,
}
}
// DoGenIDAction .
func (m1 *SnowWorkerM1) DoGenIdAction(arg *OverCostActionArg) {
}
func (m1 *SnowWorkerM1) BeginOverCostAction(useTimeTick int64) {
}
func (m1 *SnowWorkerM1) EndOverCostAction(useTimeTick int64) {
if m1._TermIndex > 10000 {
m1._TermIndex = 0
}
}
func (m1 *SnowWorkerM1) BeginTurnBackAction(useTimeTick int64) {
}
func (m1 *SnowWorkerM1) EndTurnBackAction(useTimeTick int64) {
}
func (m1 *SnowWorkerM1) NextOverCostId() int64 {
currentTimeTick := m1.GetCurrentTimeTick()
if currentTimeTick > m1._LastTimeTick {
m1.EndOverCostAction(currentTimeTick)
m1._LastTimeTick = currentTimeTick
m1._CurrentSeqNumber = m1.MinSeqNumber
m1._IsOverCost = false
m1._OverCostCountInOneTerm = 0
m1._GenCountInOneTerm = 0
return m1.CalcId(m1._LastTimeTick)
}
if m1._OverCostCountInOneTerm >= m1.TopOverCostCount {
m1.EndOverCostAction(currentTimeTick)
m1._LastTimeTick = m1.GetNextTimeTick()
m1._CurrentSeqNumber = m1.MinSeqNumber
m1._IsOverCost = false
m1._OverCostCountInOneTerm = 0
m1._GenCountInOneTerm = 0
return m1.CalcId(m1._LastTimeTick)
}
if m1._CurrentSeqNumber > m1.MaxSeqNumber {
m1._LastTimeTick++
m1._CurrentSeqNumber = m1.MinSeqNumber
m1._IsOverCost = true
m1._OverCostCountInOneTerm++
m1._GenCountInOneTerm++
return m1.CalcId(m1._LastTimeTick)
}
m1._GenCountInOneTerm++
return m1.CalcId(m1._LastTimeTick)
}
// NextNormalID .
func (m1 *SnowWorkerM1) NextNormalId() int64 {
currentTimeTick := m1.GetCurrentTimeTick()
if currentTimeTick < m1._LastTimeTick {
if m1._TurnBackTimeTick < 1 {
m1._TurnBackTimeTick = m1._LastTimeTick - 1
m1._TurnBackIndex++
// 每毫秒序列数的前5位是预留位,0用于手工新值,1-4是时间回拨次序
// 最多4次回拨(防止回拨重叠)
if m1._TurnBackIndex > 4 {
m1._TurnBackIndex = 1
}
m1.BeginTurnBackAction(m1._TurnBackTimeTick)
}
// time.Sleep(time.Duration(1) * time.Millisecond)
return m1.CalcTurnBackId(m1._TurnBackTimeTick)
}
// 时间追平时,_TurnBackTimeTick清零
if m1._TurnBackTimeTick > 0 {
m1.EndTurnBackAction(m1._TurnBackTimeTick)
m1._TurnBackTimeTick = 0
}
if currentTimeTick > m1._LastTimeTick {
m1._LastTimeTick = currentTimeTick
m1._CurrentSeqNumber = m1.MinSeqNumber
return m1.CalcId(m1._LastTimeTick)
}
if m1._CurrentSeqNumber > m1.MaxSeqNumber {
m1.BeginOverCostAction(currentTimeTick)
m1._TermIndex++
m1._LastTimeTick++
m1._CurrentSeqNumber = m1.MinSeqNumber
m1._IsOverCost = true
m1._OverCostCountInOneTerm = 1
m1._GenCountInOneTerm = 1
return m1.CalcId(m1._LastTimeTick)
}
return m1.CalcId(m1._LastTimeTick)
}
// CalcID .
func (m1 *SnowWorkerM1) CalcId(useTimeTick int64) int64 {
result := int64(useTimeTick<<m1._TimestampShift) + int64(m1.WorkerId<<m1.SeqBitLength) + int64(m1._CurrentSeqNumber)
m1._CurrentSeqNumber++
return result
}
// CalcTurnBackID .
func (m1 *SnowWorkerM1) CalcTurnBackId(useTimeTick int64) int64 {
result := int64(useTimeTick<<m1._TimestampShift) + int64(m1.WorkerId<<m1.SeqBitLength) + int64(m1._TurnBackIndex)
m1._TurnBackTimeTick--
return result
}
// GetCurrentTimeTick .
func (m1 *SnowWorkerM1) GetCurrentTimeTick() int64 {
var millis = time.Now().UnixNano() / 1e6
return millis - m1.BaseTime
}
// GetNextTimeTick .
func (m1 *SnowWorkerM1) GetNextTimeTick() int64 {
tempTimeTicker := m1.GetCurrentTimeTick()
for tempTimeTicker <= m1._LastTimeTick {
tempTimeTicker = m1.GetCurrentTimeTick()
}
return tempTimeTicker
}
// NextId .
func (m1 *SnowWorkerM1) NextId() int64 {
m1.Lock()
defer m1.Unlock()
if m1._IsOverCost {
return m1.NextOverCostId()
} else {
return m1.NextNormalId()
}
}
@@ -1,37 +0,0 @@
package uniqueid
import (
"fmt"
"strconv"
)
type SnowWorkerM2 struct {
*SnowWorkerM1
}
func NewSnowWorkerM2(options *IdGeneratorOptions) ISnowWorker {
return &SnowWorkerM2{
NewSnowWorkerM1(options).(*SnowWorkerM1),
}
}
func (m2 SnowWorkerM2) NextId() int64 {
m2.Lock()
defer m2.Unlock()
currentTimeTick := m2.GetCurrentTimeTick()
if m2._LastTimeTick == currentTimeTick {
m2._CurrentSeqNumber++
if m2._CurrentSeqNumber > m2.MaxSeqNumber {
m2._CurrentSeqNumber = m2.MinSeqNumber
currentTimeTick = m2.GetNextTimeTick()
}
} else {
m2._CurrentSeqNumber = m2.MinSeqNumber
}
if currentTimeTick < m2._LastTimeTick {
fmt.Println("Time error for {0} milliseconds", strconv.FormatInt(m2._LastTimeTick-currentTimeTick, 10))
}
m2._LastTimeTick = currentTimeTick
result := int64(currentTimeTick<<m2._TimestampShift) + int64(m2.WorkerId<<m2.SeqBitLength) + int64(m2._CurrentSeqNumber)
return result
}