u
This commit is contained in:
1 parent
d04d22723b
commit
4ce951dd56
30 files changed
+981
-73
No files matched your search
@@ -0,0 +1 @@
|
|||||||
|
logs
|
||||||
+29
@@ -0,0 +1,29 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="DataSourceManagerImpl" format="xml" multifile-model="true">
|
||||||
|
<data-source source="LOCAL" name="postgres@117.72.182.135" uuid="4db2dbd4-857e-4234-ba5e-f8cbf0ba6e5d">
|
||||||
|
<driver-ref>postgresql</driver-ref>
|
||||||
|
<synchronize>true</synchronize>
|
||||||
|
<jdbc-driver>org.postgresql.Driver</jdbc-driver>
|
||||||
|
<jdbc-url>jdbc:postgresql://117.72.182.135:5432/postgres</jdbc-url>
|
||||||
|
<jdbc-additional-properties>
|
||||||
|
<property name="com.intellij.clouds.kubernetes.db.host.port" />
|
||||||
|
<property name="com.intellij.clouds.kubernetes.db.enabled" value="false" />
|
||||||
|
<property name="com.intellij.clouds.kubernetes.db.container.port" />
|
||||||
|
</jdbc-additional-properties>
|
||||||
|
<working-dir>$ProjectFileDir$</working-dir>
|
||||||
|
</data-source>
|
||||||
|
<data-source source="LOCAL" name="0@117.72.182.135" uuid="a77cbfeb-5761-4963-b061-a21951db7e82">
|
||||||
|
<driver-ref>redis</driver-ref>
|
||||||
|
<synchronize>true</synchronize>
|
||||||
|
<jdbc-driver>jdbc.RedisDriver</jdbc-driver>
|
||||||
|
<jdbc-url>jdbc:redis://117.72.182.135:6379/0</jdbc-url>
|
||||||
|
<jdbc-additional-properties>
|
||||||
|
<property name="com.intellij.clouds.kubernetes.db.host.port" />
|
||||||
|
<property name="com.intellij.clouds.kubernetes.db.enabled" value="false" />
|
||||||
|
<property name="com.intellij.clouds.kubernetes.db.container.port" />
|
||||||
|
</jdbc-additional-properties>
|
||||||
|
<working-dir>$ProjectFileDir$</working-dir>
|
||||||
|
</data-source>
|
||||||
|
</component>
|
||||||
|
</project>
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="DataSourcePerFileMappings">
|
||||||
|
<file url="file://$APPLICATION_CONFIG_DIR$/consoles/db/4db2dbd4-857e-4234-ba5e-f8cbf0ba6e5d/console.sql" value="4db2dbd4-857e-4234-ba5e-f8cbf0ba6e5d" />
|
||||||
|
<file url="file://$APPLICATION_CONFIG_DIR$/consoles/db/4db2dbd4-857e-4234-ba5e-f8cbf0ba6e5d/console_1.sql" value="4db2dbd4-857e-4234-ba5e-f8cbf0ba6e5d" />
|
||||||
|
</component>
|
||||||
|
</project>
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
<component name="InspectionProjectProfileManager">
|
||||||
|
<profile version="1.0">
|
||||||
|
<option name="myName" value="Project Default" />
|
||||||
|
<inspection_tool class="GoDfaErrorMayBeNotNil" enabled="true" level="WARNING" enabled_by_default="true">
|
||||||
|
<functions>
|
||||||
|
<function importPath="time" name="Parse" />
|
||||||
|
</functions>
|
||||||
|
</inspection_tool>
|
||||||
|
</profile>
|
||||||
|
</component>
|
||||||
+12
@@ -0,0 +1,12 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="ProtobufLanguageSettings">
|
||||||
|
<option name="importPathEntries">
|
||||||
|
<list>
|
||||||
|
<ImportPathEntry>
|
||||||
|
<option name="location" value="file://$PROJECT_DIR$/idl" />
|
||||||
|
</ImportPathEntry>
|
||||||
|
</list>
|
||||||
|
</option>
|
||||||
|
</component>
|
||||||
|
</project>
|
||||||
+6
@@ -0,0 +1,6 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="SqlDialectMappings">
|
||||||
|
<file url="PROJECT" dialect="PostgreSQL" />
|
||||||
|
</component>
|
||||||
|
</project>
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
package handler
|
||||||
|
|
||||||
|
import (
|
||||||
|
"allapp/utils/jwtx"
|
||||||
|
|
||||||
|
"github.com/gofiber/fiber/v3"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Hello handle api status
|
||||||
|
func Hello(c fiber.Ctx) error {
|
||||||
|
return c.JSON(fiber.Map{"status": "success", "message": "Hello i'm ok!", "data": nil})
|
||||||
|
}
|
||||||
@@ -0,0 +1,33 @@
|
|||||||
|
package middleware
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/gofiber/fiber/v3"
|
||||||
|
"github.com/gofiber/fiber/v3/log"
|
||||||
|
)
|
||||||
|
|
||||||
|
func Logger() fiber.Handler {
|
||||||
|
return func(c fiber.Ctx) error {
|
||||||
|
ctx := context.WithValue(c.Context(), "request_id", c.RequestID())
|
||||||
|
c.SetContext(ctx)
|
||||||
|
|
||||||
|
start := time.Now()
|
||||||
|
|
||||||
|
err := c.Next()
|
||||||
|
|
||||||
|
duration := time.Since(start)
|
||||||
|
|
||||||
|
// 记录日志
|
||||||
|
log.WithContext(c.Context()).Infow("API Request",
|
||||||
|
"method", c.Method(),
|
||||||
|
"path", c.Path(),
|
||||||
|
"status", c.Response().StatusCode(),
|
||||||
|
"duration", duration.String(),
|
||||||
|
"ip", c.IP(),
|
||||||
|
)
|
||||||
|
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
package router
|
||||||
|
|
||||||
|
import (
|
||||||
|
"allapp/api/handler"
|
||||||
|
"allapp/config"
|
||||||
|
|
||||||
|
"github.com/gofiber/fiber/v3"
|
||||||
|
)
|
||||||
|
|
||||||
|
func SetupRouters(app *fiber.App) {
|
||||||
|
api := app.Group(config.GetConf().Server.BaseUrl)
|
||||||
|
|
||||||
|
api.Get("/", handler.Hello)
|
||||||
|
}
|
||||||
@@ -1,42 +1,32 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"allapp/api/middleware"
|
||||||
|
"allapp/api/router"
|
||||||
"allapp/config"
|
"allapp/config"
|
||||||
"fmt"
|
|
||||||
|
"github.com/gofiber/fiber/v3"
|
||||||
|
"github.com/gofiber/fiber/v3/log"
|
||||||
|
recoverer "github.com/gofiber/fiber/v3/middleware/recover"
|
||||||
|
"github.com/gofiber/fiber/v3/middleware/requestid"
|
||||||
|
"go.uber.org/zap"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
//func errorHandler(ctx fiber.Ctx, err error) error {
|
||||||
|
//
|
||||||
|
//}
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
config.InitConf()
|
if err := config.InitConfig(); err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
fmt.Print(config.GetConf().Server.BaseUrl)
|
app := fiber.New()
|
||||||
|
app.Use(recoverer.New(), requestid.New(), middleware.Logger())
|
||||||
|
|
||||||
//app := fiber.New()
|
router.SetupRouters(app)
|
||||||
|
|
||||||
//logger := middleware.NewLogger(middleware.LoggerConfig{
|
if err := app.Listen(config.GetServerPort()); err != nil {
|
||||||
// ExtraKeys: []string{"request_id", "abc"},
|
log.Fatal("server start failed", zap.Error(err))
|
||||||
//})
|
}
|
||||||
//
|
|
||||||
//log.SetLogger(logger)
|
|
||||||
//
|
|
||||||
//defer logger.Sync()
|
|
||||||
//
|
|
||||||
//app.Use(func(c fiber.Ctx) error {
|
|
||||||
// ctx := context.WithValue(c.Context(), "request_id", "123")
|
|
||||||
// ctx = context.WithValue(ctx, "user_id", "456")
|
|
||||||
// ctx = context.WithValue(ctx, "trace_id", "789")
|
|
||||||
//
|
|
||||||
// c.SetContext(ctx)
|
|
||||||
// return c.Next()
|
|
||||||
//})
|
|
||||||
//
|
|
||||||
//app.Get("/", func(c fiber.Ctx) error {
|
|
||||||
// log.WithContext(c.Context()).Infow("MSG",
|
|
||||||
// "request_id", "12345",
|
|
||||||
// "user_id", "abc123",
|
|
||||||
// "status", "pending",
|
|
||||||
// )
|
|
||||||
// return c.SendString("Hello, World!")
|
|
||||||
//})
|
|
||||||
//
|
|
||||||
//log.Fatal(app.Listen(":3000"))
|
|
||||||
}
|
}
|
||||||
@@ -4,6 +4,8 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/spf13/viper"
|
"github.com/spf13/viper"
|
||||||
)
|
)
|
||||||
@@ -12,11 +14,62 @@ var (
|
|||||||
conf *Config
|
conf *Config
|
||||||
)
|
)
|
||||||
|
|
||||||
func InitConf() {
|
func InitConfig() error {
|
||||||
|
// 加载配置
|
||||||
|
err := readConfigFile()
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("read config file failed: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
initLogger()
|
||||||
|
initUniqueId()
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func defaultConfig() *Config {
|
||||||
|
return &Config{
|
||||||
|
Server: ServerConfig{
|
||||||
|
Port: "8080",
|
||||||
|
BaseUrl: "/",
|
||||||
|
WsUrl: "/ws",
|
||||||
|
},
|
||||||
|
JWT: JwtConfig{
|
||||||
|
Secret: "qwemwddlalmskadklsdklamlkdsakld",
|
||||||
|
AccessExpiry: 24 * time.Hour,
|
||||||
|
},
|
||||||
|
UniqueID: UniqueIDConfig{
|
||||||
|
DataCenterID: 1,
|
||||||
|
WorkerID: 1,
|
||||||
|
},
|
||||||
|
Postgres: PostgresConfig{
|
||||||
|
Port: 5432,
|
||||||
|
SslMode: false,
|
||||||
|
TimeZone: "Asia/Shanghai",
|
||||||
|
MaxOpenConns: 20,
|
||||||
|
MaxIdleConns: 10,
|
||||||
|
ConnMaxLifetime: 30 * time.Minute,
|
||||||
|
},
|
||||||
|
Log: LogConfig{
|
||||||
|
Level: "info",
|
||||||
|
Dir: "logs",
|
||||||
|
MaxSize: 100,
|
||||||
|
MaxBackups: 10,
|
||||||
|
MaxAge: 30,
|
||||||
|
Compress: true,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func readConfigFile() error {
|
||||||
// 配置文件的目录
|
// 配置文件的目录
|
||||||
prefix := "resources"
|
prefix := "resources"
|
||||||
env := GetEnv() // 获取当前环境
|
env := GetEnv() // 获取当前环境
|
||||||
|
|
||||||
|
// 设置默认值
|
||||||
|
conf = defaultConfig() // 先给默认结构体
|
||||||
|
|
||||||
// 默认配置文件
|
// 默认配置文件
|
||||||
confFileRelPath := filepath.Join(prefix, "application.yaml")
|
confFileRelPath := filepath.Join(prefix, "application.yaml")
|
||||||
|
|
||||||
@@ -42,18 +95,18 @@ func InitConf() {
|
|||||||
|
|
||||||
// 如果需要,合并环境特定的配置文件
|
// 如果需要,合并环境特定的配置文件
|
||||||
if err := v.MergeInConfig(); err != nil {
|
if err := v.MergeInConfig(); err != nil {
|
||||||
panic(err)
|
return fmt.Errorf("merge env config failed: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// 将配置文件反序列化到 Config 结构体
|
|
||||||
conf = new(Config)
|
|
||||||
if err := v.Unmarshal(conf); err != nil {
|
if err := v.Unmarshal(conf); err != nil {
|
||||||
panic(err)
|
return fmt.Errorf("unmarshal config failed: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
fmt.Println("✅️ 加载配置成功")
|
fmt.Println("✅️ 加载配置成功")
|
||||||
// 设置环境变量
|
// 设置环境变量
|
||||||
conf.Env = env
|
conf.Env = env
|
||||||
|
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetEnv 获取 GO_ENV 环境变量,默认返回 "dev"
|
// GetEnv 获取 GO_ENV 环境变量,默认返回 "dev"
|
||||||
@@ -66,6 +119,16 @@ func GetEnv() string {
|
|||||||
return e
|
return e
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetConf 获取配置信息
|
||||||
func GetConf() *Config {
|
func GetConf() *Config {
|
||||||
return conf
|
return conf
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetServerPort 项目启动端口
|
||||||
|
func GetServerPort() string {
|
||||||
|
port := conf.Server.Port
|
||||||
|
if strings.HasPrefix(port, ":") {
|
||||||
|
return port
|
||||||
|
}
|
||||||
|
return ":" + port
|
||||||
|
}
|
||||||
@@ -1 +0,0 @@
|
|||||||
package config
|
|
||||||
@@ -1 +1,153 @@
|
|||||||
package config
|
package config
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"sync"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
fiberZap "github.com/gofiber/contrib/v3/zap"
|
||||||
|
"github.com/gofiber/fiber/v3/log"
|
||||||
|
"go.uber.org/zap"
|
||||||
|
"go.uber.org/zap/zapcore"
|
||||||
|
"gopkg.in/natefinch/lumberjack.v2"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
mu sync.Mutex
|
||||||
|
currentDay = make(map[string]string)
|
||||||
|
currentSync = make(map[string]zapcore.WriteSyncer)
|
||||||
|
)
|
||||||
|
|
||||||
|
func initLogger() {
|
||||||
|
dynamicLevel := zap.NewAtomicLevel()
|
||||||
|
dynamicLevel.SetLevel(LogLevel())
|
||||||
|
|
||||||
|
logger := fiberZap.NewLogger(fiberZap.LoggerConfig{
|
||||||
|
ExtraKeys: []string{"request_id"},
|
||||||
|
ZapOptions: []zap.Option{
|
||||||
|
zap.AddCaller(), // 打开 caller
|
||||||
|
zap.AddCallerSkip(2), // 跳过一层 Fiber 调用
|
||||||
|
},
|
||||||
|
CoreConfigs: []fiberZap.CoreConfig{
|
||||||
|
{
|
||||||
|
Encoder: zapcore.NewConsoleEncoder(consoleEncoderConfig()),
|
||||||
|
WriteSyncer: zapcore.AddSync(os.Stdout),
|
||||||
|
LevelEncoder: dynamicLevel,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Encoder: zapcore.NewJSONEncoder(humanEncoderConfig()),
|
||||||
|
WriteSyncer: getWriteSyncer(fmt.Sprintf("%s/debug", conf.Log.Dir)),
|
||||||
|
LevelEncoder: zap.LevelEnablerFunc(func(lev zapcore.Level) bool {
|
||||||
|
return lev == zap.DebugLevel
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Encoder: zapcore.NewJSONEncoder(humanEncoderConfig()),
|
||||||
|
WriteSyncer: getWriteSyncer(fmt.Sprintf("%s/info", conf.Log.Dir)),
|
||||||
|
LevelEncoder: zap.LevelEnablerFunc(func(lev zapcore.Level) bool {
|
||||||
|
return lev == zap.InfoLevel
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Encoder: zapcore.NewJSONEncoder(humanEncoderConfig()),
|
||||||
|
WriteSyncer: getWriteSyncer(fmt.Sprintf("%s/warn", conf.Log.Dir)),
|
||||||
|
LevelEncoder: zap.LevelEnablerFunc(func(lev zapcore.Level) bool {
|
||||||
|
return lev == zap.WarnLevel
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Encoder: zapcore.NewJSONEncoder(humanEncoderConfig()),
|
||||||
|
WriteSyncer: getWriteSyncer(fmt.Sprintf("%s/error", conf.Log.Dir)),
|
||||||
|
LevelEncoder: zap.LevelEnablerFunc(func(lev zapcore.Level) bool {
|
||||||
|
return lev >= zap.ErrorLevel
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
log.SetLogger(logger)
|
||||||
|
|
||||||
|
// 退出时同步日志
|
||||||
|
defer func() {
|
||||||
|
_ = logger.Sync()
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
|
||||||
|
func getWriteSyncer(basePath string) zapcore.WriteSyncer {
|
||||||
|
mu.Lock()
|
||||||
|
defer mu.Unlock()
|
||||||
|
|
||||||
|
today := time.Now().Format("2006-01-02")
|
||||||
|
if day, ok := currentDay[basePath]; !ok || day != today {
|
||||||
|
currentDay[basePath] = today
|
||||||
|
file := fmt.Sprintf("%s/%s.log", basePath, today)
|
||||||
|
|
||||||
|
currentSync[basePath] = zapcore.AddSync(&lumberjack.Logger{
|
||||||
|
Filename: file,
|
||||||
|
MaxSize: conf.Log.MaxSize,
|
||||||
|
MaxBackups: conf.Log.MaxBackups,
|
||||||
|
MaxAge: conf.Log.MaxAge,
|
||||||
|
Compress: conf.Log.Compress,
|
||||||
|
LocalTime: true,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
return currentSync[basePath]
|
||||||
|
}
|
||||||
|
|
||||||
|
func LogLevel() zapcore.Level {
|
||||||
|
level := conf.Log.Level
|
||||||
|
|
||||||
|
switch level {
|
||||||
|
case "trace":
|
||||||
|
return zapcore.DebugLevel
|
||||||
|
case "debug":
|
||||||
|
return zapcore.DebugLevel
|
||||||
|
case "info":
|
||||||
|
return zapcore.InfoLevel
|
||||||
|
case "warn":
|
||||||
|
return zapcore.WarnLevel
|
||||||
|
case "error":
|
||||||
|
return zapcore.ErrorLevel
|
||||||
|
case "dpanic":
|
||||||
|
return zapcore.DPanicLevel
|
||||||
|
case "panic":
|
||||||
|
return zapcore.PanicLevel
|
||||||
|
case "fatal":
|
||||||
|
return zapcore.FatalLevel
|
||||||
|
default:
|
||||||
|
return zapcore.InfoLevel
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func consoleEncoderConfig() zapcore.EncoderConfig {
|
||||||
|
return zapcore.EncoderConfig{
|
||||||
|
MessageKey: "msg",
|
||||||
|
LevelKey: "level",
|
||||||
|
TimeKey: "ts",
|
||||||
|
EncodeTime: customTimeEncoder,
|
||||||
|
EncodeLevel: zapcore.CapitalColorLevelEncoder,
|
||||||
|
EncodeCaller: zapcore.ShortCallerEncoder,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func humanEncoderConfig() zapcore.EncoderConfig {
|
||||||
|
return zapcore.EncoderConfig{
|
||||||
|
MessageKey: "message",
|
||||||
|
LevelKey: "level",
|
||||||
|
NameKey: "mame",
|
||||||
|
TimeKey: "ts",
|
||||||
|
CallerKey: "caller",
|
||||||
|
FunctionKey: "func",
|
||||||
|
StacktraceKey: "stacktrace",
|
||||||
|
LineEnding: "\n",
|
||||||
|
EncodeTime: customTimeEncoder,
|
||||||
|
EncodeLevel: zapcore.LowercaseLevelEncoder,
|
||||||
|
EncodeDuration: zapcore.SecondsDurationEncoder,
|
||||||
|
EncodeCaller: zapcore.ShortCallerEncoder,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func customTimeEncoder(t time.Time, enc zapcore.PrimitiveArrayEncoder) {
|
||||||
|
enc.AppendString(t.Format("2006-01-02 15:04:05.000"))
|
||||||
|
}
|
||||||
@@ -1 +0,0 @@
|
|||||||
package config
|
|
||||||
@@ -5,53 +5,53 @@ import "time"
|
|||||||
type Config struct {
|
type Config struct {
|
||||||
Env string
|
Env string
|
||||||
|
|
||||||
Server ServerConfig `yaml:"server"`
|
Server ServerConfig `mapstructure:"server"`
|
||||||
JWT JwtConfig `yaml:"jwt"`
|
JWT JwtConfig `mapstructure:"jwt"`
|
||||||
SnowID SnowIDConfig `yaml:"snow_id"`
|
UniqueID UniqueIDConfig `mapstructure:"unique_id"`
|
||||||
Postgres PostgresConfig `yaml:"postgres"`
|
Postgres PostgresConfig `mapstructure:"postgres"`
|
||||||
Log LogConfig `yaml:"log"`
|
Log LogConfig `mapstructure:"log"`
|
||||||
Wechat WechatConfig `yaml:"wechat"`
|
Wechat WechatConfig `mapstructure:"wechat"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type ServerConfig struct {
|
type ServerConfig struct {
|
||||||
Port string `yaml:"port" mapstructure:"port"`
|
Port string `mapstructure:"port"`
|
||||||
BaseUrl string `mapstructure:"base_url"` // 添加mapstructure标签
|
BaseUrl string `mapstructure:"base_url"`
|
||||||
WsUrl string `yaml:"ws_url" mapstructure:"ws_url"`
|
WsUrl string `mapstructure:"ws_url"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type JwtConfig struct {
|
type JwtConfig struct {
|
||||||
Secret string `yaml:"secret"`
|
Secret string `mapstructure:"secret"`
|
||||||
AccessExpiry time.Duration `yaml:"access_expiry"`
|
AccessExpiry time.Duration `mapstructure:"access_expiry"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type SnowIDConfig struct {
|
type UniqueIDConfig struct {
|
||||||
DataCenterID uint16 `yaml:"datacenter_id"`
|
DataCenterID uint16 `mapstructure:"datacenter_id"`
|
||||||
WorkerID uint16 `yaml:"worker_id"`
|
WorkerID uint16 `mapstructure:"worker_id"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type PostgresConfig struct {
|
type PostgresConfig struct {
|
||||||
Host string `yaml:"host"`
|
Host string `mapstructure:"host"`
|
||||||
Port int `yaml:"port"`
|
Port int `mapstructure:"port"`
|
||||||
User string `yaml:"user"`
|
User string `mapstructure:"user"`
|
||||||
Password string `yaml:"password"`
|
Password string `mapstructure:"password"`
|
||||||
Dbname string `yaml:"dbname"`
|
Dbname string `mapstructure:"dbname"`
|
||||||
SllMode bool `yaml:"sll_mode"`
|
SslMode bool `mapstructure:"ssl_mode"`
|
||||||
TimeZone string `yaml:"timezone"`
|
TimeZone string `mapstructure:"timezone"`
|
||||||
MaxOpenConns int32 `yaml:"max_open_conns"`
|
MaxOpenConns int32 `mapstructure:"max_open_conns"`
|
||||||
MaxIdleConns int32 `yaml:"max_idle_conns"`
|
MaxIdleConns int32 `mapstructure:"max_idle_conns"`
|
||||||
ConnMaxLifetime time.Duration `yaml:"conn_max_lifetime"`
|
ConnMaxLifetime time.Duration `mapstructure:"conn_max_lifetime"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type LogConfig struct {
|
type LogConfig struct {
|
||||||
Level string `yaml:"level"`
|
Level string `mapstructure:"level"`
|
||||||
Dir string `yaml:"dir"`
|
Dir string `mapstructure:"dir"`
|
||||||
MaxSize int `yaml:"max_size"`
|
MaxSize int `mapstructure:"max_size"`
|
||||||
MaxBackups int `yaml:"max_backups"`
|
MaxBackups int `mapstructure:"max_backups"`
|
||||||
MaxAge int `yaml:"max_age"`
|
MaxAge int `mapstructure:"max_age"`
|
||||||
Compress bool `yaml:"compress"`
|
Compress bool `mapstructure:"compress"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type WechatConfig struct {
|
type WechatConfig struct {
|
||||||
AppId string `yaml:"app_id"`
|
AppId string `mapstructure:"app_id"`
|
||||||
AppSecret string `yaml:"app_secret"`
|
AppSecret string `mapstructure:"app_secret"`
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
package config
|
||||||
|
|
||||||
|
import "allapp/utils/uniqueid"
|
||||||
|
|
||||||
|
// 初始化雪花id
|
||||||
|
func initUniqueId() {
|
||||||
|
options := uniqueid.NewIdGeneratorOptions(conf.UniqueID.WorkerID)
|
||||||
|
uniqueid.SetIdGenerator(options)
|
||||||
|
}
|
||||||
@@ -5,13 +5,17 @@ go 1.25.0
|
|||||||
require (
|
require (
|
||||||
github.com/gofiber/contrib/v3/zap v1.0.0
|
github.com/gofiber/contrib/v3/zap v1.0.0
|
||||||
github.com/gofiber/fiber/v3 v3.1.0
|
github.com/gofiber/fiber/v3 v3.1.0
|
||||||
|
github.com/golang-jwt/jwt/v5 v5.3.1
|
||||||
|
github.com/spf13/viper v1.21.0
|
||||||
go.uber.org/zap v1.27.1
|
go.uber.org/zap v1.27.1
|
||||||
|
gopkg.in/natefinch/lumberjack.v2 v2.2.1
|
||||||
)
|
)
|
||||||
|
|
||||||
require (
|
require (
|
||||||
|
github.com/MicahParks/keyfunc/v2 v2.1.0 // indirect
|
||||||
github.com/andybalholm/brotli v1.2.0 // indirect
|
github.com/andybalholm/brotli v1.2.0 // indirect
|
||||||
github.com/fsnotify/fsnotify v1.9.0 // indirect
|
github.com/fsnotify/fsnotify v1.9.0 // indirect
|
||||||
github.com/go-viper/mapstructure/v2 v2.4.0 // indirect
|
github.com/go-viper/mapstructure/v2 v2.5.0 // indirect
|
||||||
github.com/gofiber/schema v1.7.0 // indirect
|
github.com/gofiber/schema v1.7.0 // indirect
|
||||||
github.com/gofiber/utils/v2 v2.0.2 // indirect
|
github.com/gofiber/utils/v2 v2.0.2 // indirect
|
||||||
github.com/google/uuid v1.6.0 // indirect
|
github.com/google/uuid v1.6.0 // indirect
|
||||||
@@ -20,12 +24,11 @@ require (
|
|||||||
github.com/mattn/go-isatty v0.0.20 // indirect
|
github.com/mattn/go-isatty v0.0.20 // indirect
|
||||||
github.com/pelletier/go-toml/v2 v2.2.4 // indirect
|
github.com/pelletier/go-toml/v2 v2.2.4 // indirect
|
||||||
github.com/philhofer/fwd v1.2.0 // indirect
|
github.com/philhofer/fwd v1.2.0 // indirect
|
||||||
github.com/sagikazarmark/locafero v0.11.0 // indirect
|
github.com/sagikazarmark/locafero v0.12.0 // indirect
|
||||||
github.com/sourcegraph/conc v0.3.1-0.20240121214520-5f936abd7ae8 // indirect
|
github.com/sourcegraph/conc v0.3.1-0.20240121214520-5f936abd7ae8 // indirect
|
||||||
github.com/spf13/afero v1.15.0 // indirect
|
github.com/spf13/afero v1.15.0 // indirect
|
||||||
github.com/spf13/cast v1.10.0 // indirect
|
github.com/spf13/cast v1.10.0 // indirect
|
||||||
github.com/spf13/pflag v1.0.10 // indirect
|
github.com/spf13/pflag v1.0.10 // indirect
|
||||||
github.com/spf13/viper v1.21.0 // indirect
|
|
||||||
github.com/subosito/gotenv v1.6.0 // indirect
|
github.com/subosito/gotenv v1.6.0 // indirect
|
||||||
github.com/tinylib/msgp v1.6.3 // indirect
|
github.com/tinylib/msgp v1.6.3 // indirect
|
||||||
github.com/valyala/bytebufferpool v1.0.0 // indirect
|
github.com/valyala/bytebufferpool v1.0.0 // indirect
|
||||||
|
|||||||
@@ -1,9 +1,15 @@
|
|||||||
|
github.com/MicahParks/keyfunc/v2 v2.1.0 h1:6ZXKb9Rp6qp1bDbJefnG7cTH8yMN1IC/4nf+GVjO99k=
|
||||||
|
github.com/MicahParks/keyfunc/v2 v2.1.0/go.mod h1:rW42fi+xgLJ2FRRXAfNx9ZA8WpD4OeE/yHVMteCkw9k=
|
||||||
github.com/andybalholm/brotli v1.2.0 h1:ukwgCxwYrmACq68yiUqwIWnGY0cTPox/M94sVwToPjQ=
|
github.com/andybalholm/brotli v1.2.0 h1:ukwgCxwYrmACq68yiUqwIWnGY0cTPox/M94sVwToPjQ=
|
||||||
github.com/andybalholm/brotli v1.2.0/go.mod h1:rzTDkvFWvIrjDXZHkuS16NPggd91W3kUSvPlQ1pLaKY=
|
github.com/andybalholm/brotli v1.2.0/go.mod h1:rzTDkvFWvIrjDXZHkuS16NPggd91W3kUSvPlQ1pLaKY=
|
||||||
github.com/fsnotify/fsnotify v1.9.0 h1:2Ml+OJNzbYCTzsxtv8vKSFD9PbJjmhYF14k/jKC7S9k=
|
github.com/fsnotify/fsnotify v1.9.0 h1:2Ml+OJNzbYCTzsxtv8vKSFD9PbJjmhYF14k/jKC7S9k=
|
||||||
github.com/fsnotify/fsnotify v1.9.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0=
|
github.com/fsnotify/fsnotify v1.9.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0=
|
||||||
github.com/go-viper/mapstructure/v2 v2.4.0 h1:EBsztssimR/CONLSZZ04E8qAkxNYq4Qp9LvH92wZUgs=
|
github.com/go-viper/mapstructure/v2 v2.4.0 h1:EBsztssimR/CONLSZZ04E8qAkxNYq4Qp9LvH92wZUgs=
|
||||||
github.com/go-viper/mapstructure/v2 v2.4.0/go.mod h1:oJDH3BJKyqBA2TXFhDsKDGDTlndYOZ6rGS0BRZIxGhM=
|
github.com/go-viper/mapstructure/v2 v2.4.0/go.mod h1:oJDH3BJKyqBA2TXFhDsKDGDTlndYOZ6rGS0BRZIxGhM=
|
||||||
|
github.com/go-viper/mapstructure/v2 v2.5.0 h1:vM5IJoUAy3d7zRSVtIwQgBj7BiWtMPfmPEgAXnvj1Ro=
|
||||||
|
github.com/go-viper/mapstructure/v2 v2.5.0/go.mod h1:oJDH3BJKyqBA2TXFhDsKDGDTlndYOZ6rGS0BRZIxGhM=
|
||||||
|
github.com/gofiber/contrib/v3/jwt v1.0.0 h1:PvqaoJiFgkcdNxfFCFffPaoDlL208fnWc9uYSGn0e8s=
|
||||||
|
github.com/gofiber/contrib/v3/jwt v1.0.0/go.mod h1:N8X1yggexDyIm5+4fWSy5UffXbdZR1dUf7+AgFNM8L8=
|
||||||
github.com/gofiber/contrib/v3/zap v1.0.0 h1:8plUZ3VqNhEG3yQD3ItKPd/Qoc5KPW2FkMT/R1UaB8k=
|
github.com/gofiber/contrib/v3/zap v1.0.0 h1:8plUZ3VqNhEG3yQD3ItKPd/Qoc5KPW2FkMT/R1UaB8k=
|
||||||
github.com/gofiber/contrib/v3/zap v1.0.0/go.mod h1:BgwWh3xoTUq8Tx8R4Zy/pq2DK+p+4rdVT1hdCKT7mV8=
|
github.com/gofiber/contrib/v3/zap v1.0.0/go.mod h1:BgwWh3xoTUq8Tx8R4Zy/pq2DK+p+4rdVT1hdCKT7mV8=
|
||||||
github.com/gofiber/fiber/v3 v3.1.0 h1:1p4I820pIa+FGxfwWuQZ5rAyX0WlGZbGT6Hnuxt6hKY=
|
github.com/gofiber/fiber/v3 v3.1.0 h1:1p4I820pIa+FGxfwWuQZ5rAyX0WlGZbGT6Hnuxt6hKY=
|
||||||
@@ -12,6 +18,8 @@ github.com/gofiber/schema v1.7.0 h1:yNM+FNRZjyYEli9Ey0AXRBrAY9jTnb+kmGs3lJGPvKg=
|
|||||||
github.com/gofiber/schema v1.7.0/go.mod h1:A/X5Ffyru4p9eBdp99qu+nzviHzQiZ7odLT+TwxWhbk=
|
github.com/gofiber/schema v1.7.0/go.mod h1:A/X5Ffyru4p9eBdp99qu+nzviHzQiZ7odLT+TwxWhbk=
|
||||||
github.com/gofiber/utils/v2 v2.0.2 h1:ShRRssz0F3AhTlAQcuEj54OEDtWF7+HJDwEi/aa6QLI=
|
github.com/gofiber/utils/v2 v2.0.2 h1:ShRRssz0F3AhTlAQcuEj54OEDtWF7+HJDwEi/aa6QLI=
|
||||||
github.com/gofiber/utils/v2 v2.0.2/go.mod h1:+9Ub4NqQ+IaJoTliq5LfdmOJAA/Hzwf4pXOxOa3RrJ0=
|
github.com/gofiber/utils/v2 v2.0.2/go.mod h1:+9Ub4NqQ+IaJoTliq5LfdmOJAA/Hzwf4pXOxOa3RrJ0=
|
||||||
|
github.com/golang-jwt/jwt/v5 v5.3.1 h1:kYf81DTWFe7t+1VvL7eS+jKFVWaUnK9cB1qbwn63YCY=
|
||||||
|
github.com/golang-jwt/jwt/v5 v5.3.1/go.mod h1:fxCRLWMO43lRc8nhHWY6LGqRcf+1gQWArsqaEUEa5bE=
|
||||||
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
|
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
|
||||||
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||||
github.com/klauspost/compress v1.18.4 h1:RPhnKRAQ4Fh8zU2FY/6ZFDwTVTxgJ/EMydqSTzE9a2c=
|
github.com/klauspost/compress v1.18.4 h1:RPhnKRAQ4Fh8zU2FY/6ZFDwTVTxgJ/EMydqSTzE9a2c=
|
||||||
@@ -26,6 +34,8 @@ github.com/philhofer/fwd v1.2.0 h1:e6DnBTl7vGY+Gz322/ASL4Gyp1FspeMvx1RNDoToZuM=
|
|||||||
github.com/philhofer/fwd v1.2.0/go.mod h1:RqIHx9QI14HlwKwm98g9Re5prTQ6LdeRQn+gXJFxsJM=
|
github.com/philhofer/fwd v1.2.0/go.mod h1:RqIHx9QI14HlwKwm98g9Re5prTQ6LdeRQn+gXJFxsJM=
|
||||||
github.com/sagikazarmark/locafero v0.11.0 h1:1iurJgmM9G3PA/I+wWYIOw/5SyBtxapeHDcg+AAIFXc=
|
github.com/sagikazarmark/locafero v0.11.0 h1:1iurJgmM9G3PA/I+wWYIOw/5SyBtxapeHDcg+AAIFXc=
|
||||||
github.com/sagikazarmark/locafero v0.11.0/go.mod h1:nVIGvgyzw595SUSUE6tvCp3YYTeHs15MvlmU87WwIik=
|
github.com/sagikazarmark/locafero v0.11.0/go.mod h1:nVIGvgyzw595SUSUE6tvCp3YYTeHs15MvlmU87WwIik=
|
||||||
|
github.com/sagikazarmark/locafero v0.12.0 h1:/NQhBAkUb4+fH1jivKHWusDYFjMOOKU88eegjfxfHb4=
|
||||||
|
github.com/sagikazarmark/locafero v0.12.0/go.mod h1:sZh36u/YSZ918v0Io+U9ogLYQJ9tLLBmM4eneO6WwsI=
|
||||||
github.com/sourcegraph/conc v0.3.1-0.20240121214520-5f936abd7ae8 h1:+jumHNA0Wrelhe64i8F6HNlS8pkoyMv5sreGx2Ry5Rw=
|
github.com/sourcegraph/conc v0.3.1-0.20240121214520-5f936abd7ae8 h1:+jumHNA0Wrelhe64i8F6HNlS8pkoyMv5sreGx2Ry5Rw=
|
||||||
github.com/sourcegraph/conc v0.3.1-0.20240121214520-5f936abd7ae8/go.mod h1:3n1Cwaq1E1/1lhQhtRK2ts/ZwZEhjcQeJQ1RuC6Q/8U=
|
github.com/sourcegraph/conc v0.3.1-0.20240121214520-5f936abd7ae8/go.mod h1:3n1Cwaq1E1/1lhQhtRK2ts/ZwZEhjcQeJQ1RuC6Q/8U=
|
||||||
github.com/spf13/afero v1.15.0 h1:b/YBCLWAJdFWJTN9cLhiXXcD7mzKn9Dm86dNnfyQw1I=
|
github.com/spf13/afero v1.15.0 h1:b/YBCLWAJdFWJTN9cLhiXXcD7mzKn9Dm86dNnfyQw1I=
|
||||||
@@ -60,3 +70,5 @@ golang.org/x/sys v0.41.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
|
|||||||
golang.org/x/text v0.34.0 h1:oL/Qq0Kdaqxa1KbNeMKwQq0reLCCaFtqu2eNuSeNHbk=
|
golang.org/x/text v0.34.0 h1:oL/Qq0Kdaqxa1KbNeMKwQq0reLCCaFtqu2eNuSeNHbk=
|
||||||
golang.org/x/text v0.34.0/go.mod h1:homfLqTYRFyVYemLBFl5GgL/DWEiH5wcsQ5gSh1yziA=
|
golang.org/x/text v0.34.0/go.mod h1:homfLqTYRFyVYemLBFl5GgL/DWEiH5wcsQ5gSh1yziA=
|
||||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
|
gopkg.in/natefinch/lumberjack.v2 v2.2.1 h1:bBRl1b0OH9s/DuPhuXpNl+VtCaJXFZ5/uEFST95x9zc=
|
||||||
|
gopkg.in/natefinch/lumberjack.v2 v2.2.1/go.mod h1:YD8tP3GAjkrDg1eZH7EGmyESg/lsYskCTPBJVb9jqSc=
|
||||||
@@ -2,3 +2,15 @@ server:
|
|||||||
port: 8086
|
port: 8086
|
||||||
base_url: /app
|
base_url: /app
|
||||||
ws_url: /ws
|
ws_url: /ws
|
||||||
|
|
||||||
|
log:
|
||||||
|
level: "debug"
|
||||||
|
dir: "./logs"
|
||||||
|
max_size: 10
|
||||||
|
max_backups: 30
|
||||||
|
max_age: 7
|
||||||
|
compress: true
|
||||||
|
|
||||||
|
jwt:
|
||||||
|
secret: 3Bde3BGEbYqtqyEUzW3ry8jKFcaPH17fRmTmqE7MDr05Lwj95uruRKrrkb44TJ4s
|
||||||
|
access_expiry: -1
|
||||||
@@ -0,0 +1,86 @@
|
|||||||
|
package jwtx
|
||||||
|
|
||||||
|
import (
|
||||||
|
"allapp/config"
|
||||||
|
"context"
|
||||||
|
"errors"
|
||||||
|
"sync"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/golang-jwt/jwt/v5"
|
||||||
|
)
|
||||||
|
|
||||||
|
type CustomClaims struct {
|
||||||
|
UserID int64 `json:"user_id"`
|
||||||
|
jwt.RegisteredClaims
|
||||||
|
}
|
||||||
|
|
||||||
|
// jwtManager 单例管理
|
||||||
|
type jwtManager struct {
|
||||||
|
secret string
|
||||||
|
tokenExpiry time.Duration
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
manager *jwtManager
|
||||||
|
once sync.Once
|
||||||
|
)
|
||||||
|
|
||||||
|
// InitJwt 初始化 JWT 单例
|
||||||
|
func InitJwt() {
|
||||||
|
once.Do(func() {
|
||||||
|
cfg := config.GetConf().JWT
|
||||||
|
manager = &jwtManager{
|
||||||
|
secret: cfg.Secret,
|
||||||
|
tokenExpiry: cfg.AccessExpiry,
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// CreateToken 生成 JWT
|
||||||
|
func CreateToken(ctx context.Context, userID int64) (string, error) {
|
||||||
|
if manager == nil {
|
||||||
|
return "", errors.New("jwt manager not initialized")
|
||||||
|
}
|
||||||
|
return manager.createToken(userID)
|
||||||
|
}
|
||||||
|
|
||||||
|
// VerifyToken 校验 token 并返回 claims
|
||||||
|
func VerifyToken(ctx context.Context, tokenString string) (*CustomClaims, error) {
|
||||||
|
if tokenString == "" || manager == nil {
|
||||||
|
return nil, errors.New("token empty or manager not initialized")
|
||||||
|
}
|
||||||
|
|
||||||
|
token, err := jwt.ParseWithClaims(tokenString, &CustomClaims{}, func(token *jwt.Token) (interface{}, error) {
|
||||||
|
return []byte(manager.secret), nil
|
||||||
|
})
|
||||||
|
|
||||||
|
if err != nil || !token.Valid {
|
||||||
|
return nil, errors.New("invalid token")
|
||||||
|
}
|
||||||
|
|
||||||
|
claims, ok := token.Claims.(*CustomClaims)
|
||||||
|
if !ok {
|
||||||
|
return nil, errors.New("invalid claims")
|
||||||
|
}
|
||||||
|
|
||||||
|
return claims, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// createToken 私有方法
|
||||||
|
func (j *jwtManager) createToken(userID int64) (string, error) {
|
||||||
|
now := time.Now()
|
||||||
|
claims := &CustomClaims{
|
||||||
|
UserID: userID,
|
||||||
|
RegisteredClaims: jwt.RegisteredClaims{
|
||||||
|
IssuedAt: jwt.NewNumericDate(now),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
if j.tokenExpiry > 0 {
|
||||||
|
claims.ExpiresAt = jwt.NewNumericDate(now.Add(j.tokenExpiry))
|
||||||
|
}
|
||||||
|
|
||||||
|
token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
|
||||||
|
return token.SignedString([]byte(j.secret))
|
||||||
|
}
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
package responsex
|
||||||
@@ -0,0 +1,83 @@
|
|||||||
|
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()
|
||||||
|
}
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
package uniqueid
|
||||||
|
|
||||||
|
type IIdGenerator interface {
|
||||||
|
NewLong() uint64
|
||||||
|
}
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
package uniqueid
|
||||||
|
|
||||||
|
type ISnowWorker interface {
|
||||||
|
NextId() int64
|
||||||
|
}
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
package uniqueid
|
||||||
|
|
||||||
|
import "fmt"
|
||||||
|
|
||||||
|
type IdGeneratorException struct {
|
||||||
|
message string
|
||||||
|
error error
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e IdGeneratorException) IdGeneratorException(message ...interface{}) {
|
||||||
|
fmt.Println(message)
|
||||||
|
}
|
||||||
@@ -0,0 +1,25 @@
|
|||||||
|
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,
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,29 @@
|
|||||||
|
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()
|
||||||
|
}
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
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
|
||||||
|
}
|
||||||
@@ -0,0 +1,243 @@
|
|||||||
|
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()
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,37 @@
|
|||||||
|
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
|
||||||
|
}
|
||||||
Reference in new issue
Block a user