commit f87d425a376d62036cc0bea80c792350eb84e691 Author: xixi.xv <3269265964@qq.com> Date: Wed Jul 10 16:07:44 2024 +0800 init diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..5848a1f --- /dev/null +++ b/.gitignore @@ -0,0 +1,4 @@ +go.sum +*.exe +.vscode +logs/ \ No newline at end of file diff --git a/gin/config.go b/gin/config.go new file mode 100644 index 0000000..0ff824b --- /dev/null +++ b/gin/config.go @@ -0,0 +1,11 @@ +package gin + +// GIN 配置 +type Config struct { + RootPath string + Addr string + Port int + Ssl bool + SslPem string + SslKey string +} diff --git a/gin/gin.go b/gin/gin.go new file mode 100644 index 0000000..d195b27 --- /dev/null +++ b/gin/gin.go @@ -0,0 +1,68 @@ +package gin + +import ( + "fmt" + "log" + "net/http" + "strconv" + "time" + + "github.com/gin-gonic/gin" + "github.com/unrolled/secure" +) + +func Service(conf *Config) { + if conf == nil { + conf = &Config{} + } + if conf.RootPath == "" { + conf.RootPath = "/" + } + if conf.Addr == "" { + conf.Addr = "0.0.0.0" + } + if conf.Port == 0 { + conf.Port = 8080 + } + + go func() { + router := gin.New() + routerSetup(router, &conf.RootPath) + + if conf.Ssl { + router.Use(tlsHandler(conf)) + } + + s := &http.Server{ + Addr: fmt.Sprintf("%s:%d", conf.Addr, conf.Port), + Handler: router, + ReadTimeout: 10 * time.Second, + WriteTimeout: 10 * time.Second, + MaxHeaderBytes: 1 << 20, + } + log.Printf("start service on %s", fmt.Sprintf("%s:%d", conf.Addr, conf.Port)) + + if conf.Ssl { + log.Fatal(s.ListenAndServeTLS(conf.SslPem, conf.SslKey)) + } else { + log.Fatal(s.ListenAndServe()) + } + }() +} + +func tlsHandler(conf *Config) gin.HandlerFunc { + return func(c *gin.Context) { + secureMiddleware := secure.New(secure.Options{ + SSLRedirect: true, + SSLHost: ":" + strconv.Itoa(conf.Port), + }) + err := secureMiddleware.Process(c.Writer, c.Request) + + // If there was an error, do not continue. + if err != nil { + return + } + + c.Next() + } +} diff --git a/gin/router.go b/gin/router.go new file mode 100644 index 0000000..87174fc --- /dev/null +++ b/gin/router.go @@ -0,0 +1,21 @@ +package gin + +import ( + "fmt" + + "github.com/gin-gonic/gin" +) + +// 路由配置 +func routerSetup(router *gin.Engine, rootpath *string) { + router.Use(gin.Recovery()) + router.GET(`/health/check`) + + r := router.Group(fmt.Sprintf("/%s", *rootpath)) + { + r.POST(`/register`) + r.GET(`/accountcheck/:accname`) + r.POST(`/login`) + r.POST(`/forgot`) + } +} diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..68c2d0e --- /dev/null +++ b/go.mod @@ -0,0 +1,45 @@ +module myschools.me/xixi.xv/hello + +go 1.22.2 + +require ( + github.com/gin-gonic/gin v1.10.0 + github.com/gomodule/redigo v1.9.2 + github.com/sirupsen/logrus v1.9.3 + github.com/unrolled/secure v1.15.0 + gorm.io/driver/mysql v1.5.7 + gorm.io/gorm v1.25.10 + gorm.io/plugin/dbresolver v1.5.2 +) + +require ( + github.com/bytedance/sonic v1.11.6 // indirect + github.com/bytedance/sonic/loader v0.1.1 // indirect + github.com/cloudwego/base64x v0.1.4 // indirect + github.com/cloudwego/iasm v0.2.0 // indirect + github.com/gabriel-vasile/mimetype v1.4.3 // indirect + github.com/gin-contrib/sse v0.1.0 // indirect + github.com/go-playground/locales v0.14.1 // indirect + github.com/go-playground/universal-translator v0.18.1 // indirect + github.com/go-playground/validator/v10 v10.20.0 // indirect + github.com/go-sql-driver/mysql v1.7.0 // indirect + github.com/goccy/go-json v0.10.2 // indirect + github.com/jinzhu/inflection v1.0.0 // indirect + github.com/jinzhu/now v1.1.5 // indirect + github.com/json-iterator/go v1.1.12 // indirect + github.com/klauspost/cpuid/v2 v2.2.7 // indirect + github.com/leodido/go-urn v1.4.0 // indirect + github.com/mattn/go-isatty v0.0.20 // indirect + github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect + github.com/modern-go/reflect2 v1.0.2 // indirect + github.com/pelletier/go-toml/v2 v2.2.2 // indirect + github.com/twitchyliquid64/golang-asm v0.15.1 // indirect + github.com/ugorji/go/codec v1.2.12 // indirect + golang.org/x/arch v0.8.0 // indirect + golang.org/x/crypto v0.23.0 // indirect + golang.org/x/net v0.25.0 // indirect + golang.org/x/sys v0.20.0 // indirect + golang.org/x/text v0.15.0 // indirect + google.golang.org/protobuf v1.34.1 // indirect + gopkg.in/yaml.v3 v3.0.1 // indirect +) diff --git a/handler/user-handler.go b/handler/user-handler.go new file mode 100644 index 0000000..522c88a --- /dev/null +++ b/handler/user-handler.go @@ -0,0 +1,25 @@ +package handler + +import ( + "net/http" + + "github.com/gin-gonic/gin" + "myschools.me/xixi.xv/hello/model" + "myschools.me/xixi.xv/hello/service" +) + +func UserHello(c *gin.Context) { + data := &model.User{} + if err := c.ShouldBindJSON(data); err != nil { + c.AbortWithStatus(http.StatusBadRequest) + return + } + + ret, err := service.UserLogin(data) + if err != nil { + c.AbortWithStatus(http.StatusInternalServerError) + return + } + + c.JSON(http.StatusOK, ret) +} diff --git a/main.go b/main.go new file mode 100644 index 0000000..4e2cb79 --- /dev/null +++ b/main.go @@ -0,0 +1,7 @@ +package main + +import "fmt" + +func main() { + fmt.Println("hello world.") +} diff --git a/model/user-model.go b/model/user-model.go new file mode 100644 index 0000000..8dac3a8 --- /dev/null +++ b/model/user-model.go @@ -0,0 +1,13 @@ +package model + +import "gorm.io/gorm" + +type User struct { + gorm.Model + LoginName string + LoginPassword string +} + +type UserAccount struct { + gorm.Model +} diff --git a/mysql/mysql.go b/mysql/mysql.go new file mode 100644 index 0000000..07bc7b3 --- /dev/null +++ b/mysql/mysql.go @@ -0,0 +1,105 @@ +package mysql + +import ( + "os" + "strconv" + "time" + + "gorm.io/driver/mysql" + "gorm.io/gorm" + "gorm.io/gorm/logger" + "gorm.io/gorm/schema" + "gorm.io/plugin/dbresolver" +) + +/* +docker配置 + +MYSQL_DSN=root:root@tcp(mysql:3306)/sample?charset=utf8mb4&parseTime=True&loc=Local \ +MYSQL_MAXLIFETIME=2\ +MYSQL_MAXIDLECONNS=2\ +MYSQL_MAXOPENCONNS=200\ +MYSQL_INIT=true \ +*/ +var ( + _db *gorm.DB +) + +// 创建实例 +func newDB() (*gorm.DB, error) { + if _db != nil { + return _db, nil + } + + dsn := os.Getenv("MYSQL_DSN") + if dsn == "" { + dsn = "root:root@tcp(127.0.0.1:3306)/mysql?charset=utf8&parseTime=True&loc=Local" + } + maxLifetime := func() int { + c := os.Getenv("MYSQL_MAXLIFETIME") + cc, err := strconv.Atoi(c) + if err != nil { + return 1 + } + + if cc <= 0 { + return 1 + } + if cc >= 1000 { + cc = 1000 + } + return cc + }() + maxIdleConns := func() int { + c := os.Getenv("MYSQL_MAXIDLECONNS") + cc, err := strconv.Atoi(c) + if err != nil { + return 1 + } + + if cc < 0 { + return 0 + } + if cc >= 1000 { + cc = 1000 + } + return cc + }() + maxOpenConns := func() int { + c := os.Getenv("MYSQL_MAXOPENCONNS") + cc, err := strconv.Atoi(c) + if err != nil { + return 1 + } + + if cc < 0 { + return 0 + } + if cc >= 1000 { + cc = 1000 + } + return cc + }() + + var err error + _db, err = gorm.Open(mysql.Open(dsn), &gorm.Config{ + SkipDefaultTransaction: true, + Logger: logger.Default.LogMode(logger.Silent), + NamingStrategy: schema.NamingStrategy{ + SingularTable: true, + }, + }) + if err != nil { + return nil, err + } + _db.Use( + dbresolver.Register(dbresolver.Config{ + Sources: []gorm.Dialector{mysql.Open(dsn)}, + Replicas: []gorm.Dialector{mysql.Open(dsn)}, + Policy: dbresolver.RandomPolicy{}, + }).SetConnMaxIdleTime(time.Hour). + SetConnMaxLifetime(time.Duration(maxLifetime) * time.Hour). + SetMaxIdleConns(maxIdleConns). + SetMaxOpenConns(maxOpenConns)) + return _db, nil +} diff --git a/mysql/tables-mysql.go b/mysql/tables-mysql.go new file mode 100644 index 0000000..b6d7294 --- /dev/null +++ b/mysql/tables-mysql.go @@ -0,0 +1,22 @@ +package mysql + +import ( + "os" + + "myschools.me/xixi.xv/hello/model" +) + +func init() { + //不初始化表时返回 + if os.Getenv("MYSQL_INIT") != "true" { + return + } + + db, err := newDB() + if err != nil { + panic(err) + } + if err := db.AutoMigrate(&model.User{}); err != nil { + panic(err) + } +} diff --git a/mysql/user-mysql.go b/mysql/user-mysql.go new file mode 100644 index 0000000..611f61b --- /dev/null +++ b/mysql/user-mysql.go @@ -0,0 +1,7 @@ +package mysql + +import "myschools.me/xixi.xv/hello/model" + +func UserDetailByLogname(loginname *string) (*model.User, error) { + return nil, nil +} diff --git a/readme.md b/readme.md new file mode 100644 index 0000000..b52c12d --- /dev/null +++ b/readme.md @@ -0,0 +1,22 @@ +HELLO + + +--- + + +### summary +asfdkasjdfljasldfj;lsadjflsajdlfjsalf +sadkfjsdlafjlsadjfljsad + + +### commpent +ajsdfkjaslfjasl +jasdfklasdjl;fjasdf +kjasdflkas +asd +fas + + +### QA +* asdfasdfasdfas +* asdfasdfadsfasdfasdf \ No newline at end of file diff --git a/redis/redis.go b/redis/redis.go new file mode 100644 index 0000000..cfda52b --- /dev/null +++ b/redis/redis.go @@ -0,0 +1,169 @@ +package redis + +import ( + "encoding/json" + "os" + "strconv" + "time" + + redigo "github.com/gomodule/redigo/redis" +) + +/* +REDIS_DSN=127.0.0.1:6379 \ +REDIS_PWD=eYX7EwxKPCDmwMtyKVge8oLd2t81 \ +REDIS_DB=0 \ +*/ +var pool *redigo.Pool + +func init() { + dbNumber := func() int { + db := os.Getenv("REDIS_DB") + database, err := strconv.Atoi(db) + if err != nil { + return 0 + } + if database < 0 { + database = 0 + } + if database > 16 { + database = 0 + } + return database + }() + pool = &redigo.Pool{ + MaxActive: 100, + MaxIdle: 1, + IdleTimeout: time.Second * time.Duration(60), + Dial: func() (redigo.Conn, error) { + return redigo.Dial("tcp", os.Getenv("REDIS_DSN"), + redigo.DialDatabase(dbNumber), + redigo.DialPassword(os.Getenv("REDIS_PWD")), + ) + }, + TestOnBorrow: func(conn redigo.Conn, t time.Time) error { + if time.Since(t) < time.Minute { + return nil + } + _, err := conn.Do("PING") + return err + }, + } +} + +// GetBytes 获取一个字节数组值 +func GetBytes(key *string) (*[]byte, error) { + conn := pool.Get() + defer conn.Close() + + data, err := redigo.Bytes(conn.Do("GET", *key)) + return &data, err +} + +// Get 获取一个值 +func Get(key string) interface{} { + conn := pool.Get() + defer conn.Close() + + var data []byte + var err error + if data, err = redigo.Bytes(conn.Do("GET", key)); err != nil { + return nil + } + var reply interface{} + if err = json.Unmarshal(data, &reply); err != nil { + return nil + } + + return reply +} + +// 集合Set增加元素 +func SetAdd(key string, data ...interface{}) error { + conn := pool.Get() + defer conn.Close() + var err error + for _, d := range data { + _, e := conn.Do("SADD", key, d) + if e != nil { + err = e + break + } + } + return err +} + +// 集合Set删除元素 +func SetRem(key string, data ...interface{}) error { + conn := pool.Get() + defer conn.Close() + + var err error + for _, d := range data { + _, e := conn.Do("SREM", key, d) + if e != nil { + err = e + break + } + } + + return err +} + +// 集合Set判断是否存在成员member,结果.(int64)==1表示存在 +func SetIsMember(key string, member interface{}) (interface{}, error) { + conn := pool.Get() + defer conn.Close() + + return conn.Do("SISMEMBER", key, member) +} + +// 设置一个值 +func Set(key string, val interface{}, timeout time.Duration) error { + data, err := json.Marshal(val) + if err != nil { + return err + } + return SetBytes(&key, &data, timeout) +} + +func SetBytes(key *string, data *[]byte, timeout time.Duration) error { + conn := pool.Get() + defer conn.Close() + + _, err := conn.Do("SETEX", *key, int64(timeout/time.Second), *data) + return err +} + +// IsExist 判断key是否存在 +func IsExist(key string) bool { + conn := pool.Get() + defer conn.Close() + + a, _ := conn.Do("EXISTS", key) + i := a.(int64) + return i > 0 +} + +// Delete 删除 +func Delete(key string) error { + conn := pool.Get() + defer conn.Close() + + if _, err := conn.Do("DEL", key); err != nil { + return err + } + + return nil +} + +// Expire 失效时间配置 +func Expire(key string, t int64) error { + conn := pool.Get() + defer conn.Close() + + if _, err := conn.Do("expire", key, t); err != nil { + return err + } + return nil +} diff --git a/redis/user-redis.go b/redis/user-redis.go new file mode 100644 index 0000000..2af6505 --- /dev/null +++ b/redis/user-redis.go @@ -0,0 +1,41 @@ +package redis + +import ( + "encoding/json" + "errors" + "time" + + "github.com/sirupsen/logrus" + "myschools.me/xixi.xv/hello/model" +) + +// 存储用户的Token +func UserTokenSet(key *string, usr *model.User) error { + + err := Set(*key, usr, 7210*time.Second) + if err != nil { + logrus.WithFields(logrus.Fields{ + "func": "UserTokenSet", + }).Warnf("Set: %s", err.Error()) + return err + } + return nil +} + +// 从redis中获取用户信息,最佳实践经验建议把此代码放service层 +func UserTokenGet(token *string) (*model.User, error) { + b, err := GetBytes(token) + if err != nil { + return nil, err + } + + if b == nil { + return nil, errors.New("无效token,请重新登录!") + } + + var user *model.User + if err := json.Unmarshal(*b, user); err != nil { + return nil, err + } + return user, nil +} diff --git a/service/user-service.go b/service/user-service.go new file mode 100644 index 0000000..1d89745 --- /dev/null +++ b/service/user-service.go @@ -0,0 +1,26 @@ +package service + +import ( + "errors" + + "myschools.me/xixi.xv/hello/model" + "myschools.me/xixi.xv/hello/mysql" +) + +func UserLogin(data *model.User) (interface{}, error) { + usr, err := mysql.UserDetailByLogname(&data.LoginName) + if err != nil { + //log + return nil, err + } + + if userPwdSecret(&data.LoginPassword) != usr.LoginPassword { + return nil, errors.New("wrong password") + } + + return nil, nil +} + +func userPwdSecret(s *string) string { + return "" +}