From 006d5443c665cb5acd976ae16cd36d041ccf2602 Mon Sep 17 00:00:00 2001 From: wangyang Date: Fri, 3 Mar 2023 15:24:58 +0800 Subject: [PATCH] =?UTF-8?q?=E5=B7=A5=E7=A8=8B=E5=88=9D=E5=A7=8B=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- biz/question/question.go | 12 +++++ conf/conf_dev.yaml | 6 +++ conf/conf_prod.yaml | 6 +++ config/config.go | 88 +++++++++++++++++++++++++++++++ config/model.go | 31 +++++++++++ constant/constant.go | 9 ++++ db/common.go | 30 +++++++++++ db/table_name.go | 6 +++ handler/question.go | 13 +++++ main.go | 56 ++++++++++++++++++++ model/model.go | 4 ++ router.go | 26 +++++++++ util/gin_util/gin.go | 30 +++++++++++ util/ierrors/error.go | 60 +++++++++++++++++++++ util/log/log.go | 110 +++++++++++++++++++++++++++++++++++++++ util/log/log_test.go | 37 +++++++++++++ util/string.go | 14 +++++ util/util.go | 36 +++++++++++++ 18 files changed, 574 insertions(+) create mode 100644 biz/question/question.go create mode 100644 conf/conf_dev.yaml create mode 100644 conf/conf_prod.yaml create mode 100644 config/config.go create mode 100644 config/model.go create mode 100644 constant/constant.go create mode 100644 db/common.go create mode 100644 db/table_name.go create mode 100644 handler/question.go create mode 100644 main.go create mode 100644 model/model.go create mode 100644 router.go create mode 100644 util/gin_util/gin.go create mode 100644 util/ierrors/error.go create mode 100644 util/log/log.go create mode 100644 util/log/log_test.go create mode 100644 util/string.go create mode 100644 util/util.go diff --git a/biz/question/question.go b/biz/question/question.go new file mode 100644 index 0000000..330c39c --- /dev/null +++ b/biz/question/question.go @@ -0,0 +1,12 @@ +package question + +import ( + "context" + "interview-one-stop-server/model" + "interview-one-stop-server/util/ierrors" +) + +func GetQuestionList(ctx context.Context) ([]model.Question, ierrors.OneStopErr) { + + return nil, ierrors.OneStopSuccess +} diff --git a/conf/conf_dev.yaml b/conf/conf_dev.yaml new file mode 100644 index 0000000..2ae8aba --- /dev/null +++ b/conf/conf_dev.yaml @@ -0,0 +1,6 @@ +service: + env: dev + domain: "http://aip-dev.nioint.com" + port: 7777 +db: + dsn: "root:EKghQrcnBEcPZpTT@tcp(112.54.161.118:3306)/question_one_stop?charset=utf8mb4&parseTime=True&loc=Local" \ No newline at end of file diff --git a/conf/conf_prod.yaml b/conf/conf_prod.yaml new file mode 100644 index 0000000..01e9af5 --- /dev/null +++ b/conf/conf_prod.yaml @@ -0,0 +1,6 @@ +service: + env: prod + domain: "http://aip.nioint.com" + port: 7777 +db: + dsn: "root:EKghQrcnBEcPZpTT@tcp(112.54.161.118:3306)/question_one_stop?charset=utf8mb4&parseTime=True&loc=Local"" \ No newline at end of file diff --git a/config/config.go b/config/config.go new file mode 100644 index 0000000..509cf3b --- /dev/null +++ b/config/config.go @@ -0,0 +1,88 @@ +package config + +import ( + "fmt" + "interview-one-stop-server/constant" + "io" + "os" + "os/exec" + + "github.com/spf13/viper" +) + +var GlobalConf Config + +func InitConfig() error { + v := viper.New() + v.AddConfigPath("./conf") // 设置读取的文件路径 + v.AddConfigPath("../../conf") // 设置读取的文件路径 + v.SetConfigType("yaml") // 设置文件的类型 + + env := os.Getenv("ENV_NAME") + switch env { + case constant.EnvNameTencentProd: + v.SetConfigName("conf_prod") + default: + v.SetConfigName("conf_dev") + } + + // 尝试进行配置读取 + if err := v.ReadInConfig(); err != nil { + return err + } + + err := v.Unmarshal(&GlobalConf) + if err != nil { + return err + } + go modifyDefaultSwaggerHost() + return nil +} + +func modifyDefaultSwaggerHost() { + defer func() { + if r := recover(); r != nil { + fmt.Printf("linux cmd run failed,err is %v\n", r) + } + }() + + path := "/app/swagger/dist/" + host := GlobalConf.Service.Domain[7:] + ph := "" + + env := os.Getenv("ENV_NAME") + if env == "" { + host = "localhost:4004" + pwd, _ := os.Getwd() + path = pwd + "/swagger/dist/" + ph = "''" + } + + cmd := exec.Command("/bin/bash", "-c", fmt.Sprintf(`cp %s %s && sed -i %s ' s/UNKNOWNHOSTHHH/%s/g' %s `, + path+"swagger-initializer.js.bak", path+"swagger-initializer.js", ph, host, path+"swagger-initializer.js")) + + output, err := cmd.StdoutPipe() + if err != nil { + fmt.Println("无法获取命令的标准输出管道", err.Error()) + return + } + + if err := cmd.Start(); err != nil { + fmt.Println("Linux命令执行失败,请检查命令输入是否有误", err.Error()) + return + } + + // 读取所有输出 + bytes, err := io.ReadAll(output) + if err != nil { + fmt.Println("打印异常,请检查") + return + } + + if err := cmd.Wait(); err != nil { + fmt.Println("Wait", err.Error()) + return + } + + fmt.Printf("打印内存信息:\n\n%s", bytes) +} diff --git a/config/model.go b/config/model.go new file mode 100644 index 0000000..9028252 --- /dev/null +++ b/config/model.go @@ -0,0 +1,31 @@ +package config + +type Config struct { + Service `mapstructure:"service"` + DB `mapstructure:"db"` + Dataset `mapstructure:"dataset"` + EventMesh `mapstructure:"eventmesh"` +} + +// Service 服务配置项 +type Service struct { + Env string `mapstructure:"env"` + Domain string `mapstructure:"domain"` + Port string `mapstructure:"port"` +} + +// DB 数据库配置项 +type DB struct { + DSN string `mapstructure:"dsn"` +} + +// Dataset 数据集相关配置项 +type Dataset struct { + Host string `mapstructure:"host"` + LabelingHost string `mapstructure:"labeling_host"` +} + +// EventMesh 数据库配置项 +type EventMesh struct { + Host string `mapstructure:"host"` +} diff --git a/constant/constant.go b/constant/constant.go new file mode 100644 index 0000000..1af236e --- /dev/null +++ b/constant/constant.go @@ -0,0 +1,9 @@ +package constant + +type ContextKey string + +const TraceID ContextKey = "trace_id" + +const ( + EnvNameTencentProd string = "tencent-prod" +) diff --git a/db/common.go b/db/common.go new file mode 100644 index 0000000..5ce2d3b --- /dev/null +++ b/db/common.go @@ -0,0 +1,30 @@ +package db + +import ( + "gorm.io/driver/mysql" + "gorm.io/gorm" + "interview-one-stop-server/config" + "time" +) + +var gormDB *gorm.DB + +func InitDB() error { + db, err := gorm.Open(mysql.Open(config.GlobalConf.DB.DSN)) + if err != nil { + return err + } + + gormDB = db + sqlDB, err := gormDB.DB() + if err != nil { + return err + } + // SetMaxIdleConns 设置空闲连接池中连接的最大数量 + sqlDB.SetMaxIdleConns(10) + // SetMaxOpenConns 设置打开数据库连接的最大数量。 + sqlDB.SetMaxOpenConns(30) + // SetConnMaxLifetime 设置了连接可复用的最大时间。 + sqlDB.SetConnMaxLifetime(time.Hour) + return nil +} diff --git a/db/table_name.go b/db/table_name.go new file mode 100644 index 0000000..adba102 --- /dev/null +++ b/db/table_name.go @@ -0,0 +1,6 @@ +package db + +const ( + // TableNameAnnotatingRecord 送标任务记录 + TableNameAnnotatingRecord = "annotating_record" +) diff --git a/handler/question.go b/handler/question.go new file mode 100644 index 0000000..2dc0686 --- /dev/null +++ b/handler/question.go @@ -0,0 +1,13 @@ +package handler + +import ( + "github.com/gin-gonic/gin" + "interview-one-stop-server/biz/question" + "interview-one-stop-server/util/gin_util" +) + +// HandleQueryQuestionList 获取题目列表 +func HandleQueryQuestionList(c *gin.Context) { + l, e := question.GetQuestionList(gin_util.GetContextFromGin(c)) + gin_util.GenGinResponse(c, e, l) +} diff --git a/main.go b/main.go new file mode 100644 index 0000000..1c7fd61 --- /dev/null +++ b/main.go @@ -0,0 +1,56 @@ +// Start a web server +package main + +import ( + "context" + "fmt" + "interview-one-stop-server/config" + "interview-one-stop-server/constant" + "interview-one-stop-server/db" + "interview-one-stop-server/util" + "interview-one-stop-server/util/log" + "time" + + ginzap "github.com/gin-contrib/zap" + "github.com/gin-gonic/gin" +) + +func main() { + err := config.InitConfig() + if err != nil { + panic(err) + } + + r := gin.Default() + logger := log.GetLogger() + r.Use(ginzap.Ginzap(logger, time.RFC3339, true)) + r.Use(ginzap.RecoveryWithZap(logger, true)) + r.Use(TraceLog()) + + err = db.InitDB() + if err != nil { + log.Error(context.Background(), "InitDB", fmt.Sprintf("db init failed, err is %s", err.Error())) + panic("db not ready") + } + + post := GetPostRouter() + for k, v := range post { + r.POST(k, v) + } + + get := GetGetRouter() + for k, v := range get { + r.GET(k, v) + } + + r.GET("/ping", Ping) + if err := r.Run(fmt.Sprintf(":%s", config.GlobalConf.Service.Port)); err != nil { + log.Error(context.Background(), "main", err.Error()) + } +} + +func TraceLog() gin.HandlerFunc { + return func(c *gin.Context) { + c.Set("ctx", context.WithValue(context.Background(), constant.TraceID, util.GenUUID())) + } +} diff --git a/model/model.go b/model/model.go new file mode 100644 index 0000000..8f017f2 --- /dev/null +++ b/model/model.go @@ -0,0 +1,4 @@ +package model + +type Question struct { +} diff --git a/router.go b/router.go new file mode 100644 index 0000000..0357cd2 --- /dev/null +++ b/router.go @@ -0,0 +1,26 @@ +package main + +import ( + "github.com/gin-gonic/gin" + "interview-one-stop-server/handler" +) + +var post = map[string]gin.HandlerFunc{} + +var get = map[string]gin.HandlerFunc{ + "/api/v1/question/list": handler.HandleQueryQuestionList, +} + +func GetPostRouter() map[string]gin.HandlerFunc { + return post +} + +func GetGetRouter() map[string]gin.HandlerFunc { + return get +} + +func Ping(c *gin.Context) { + c.JSON(200, gin.H{ + "message": "pong", + }) +} diff --git a/util/gin_util/gin.go b/util/gin_util/gin.go new file mode 100644 index 0000000..2c3ce99 --- /dev/null +++ b/util/gin_util/gin.go @@ -0,0 +1,30 @@ +package gin_util + +import ( + "context" + "github.com/gin-gonic/gin" + "interview-one-stop-server/util/ierrors" + "interview-one-stop-server/util/log" +) + +func GenGinResponse(c *gin.Context, err ierrors.OneStopErr, data interface{}) { + if err.ErrNumber.Code != ierrors.Success.Code { + ctx, _ := c.Get("ctx") + funcName, _ := c.Get("func_name") + log.Error(ctx.(context.Context), funcName.(string), err.Error()) + } + + c.JSON(200, gin.H{ + "code": err.Code, + "message": err.Message, + "data": data, + }) +} + +func GetContextFromGin(c *gin.Context) context.Context { + ctx, ok := c.Get("ctx") + if ok { + return ctx.(context.Context) + } + return context.Background() +} diff --git a/util/ierrors/error.go b/util/ierrors/error.go new file mode 100644 index 0000000..e657c3f --- /dev/null +++ b/util/ierrors/error.go @@ -0,0 +1,60 @@ +package ierrors + +import "fmt" + +type ErrNumber struct { + Code int + Message string +} + +func (err ErrNumber) Error() string { + return err.Message +} + +type OneStopErr struct { + ErrNumber + Errord error // 保存内部错误信息 +} + +func (err *OneStopErr) Error() string { + return fmt.Sprintf("OneStop - code: %d, message: %s, error: %s", err.Code, err.Message, err.Errord) +} + +func NewOneStopErr(errN ErrNumber, err error) OneStopErr { + return OneStopErr{ + ErrNumber: errN, + Errord: err, + } +} + +var OneStopSuccess = NewOneStopErr(Success, nil) + +//错误码一共四位,一二位为功能,三四位为具体错误 + +var ( + Success = ErrNumber{ + Code: 0, + Message: "SUCCEED", + } + /*** 公共错误码,一二位为10 ***/ + + ErrJsonParseFailed = ErrNumber{ + Code: 1000, + Message: "JSON 转换失败", + } + + ErrHttpBodyReadFailed = ErrNumber{ + Code: 1001, + Message: "请求body获取失败", + } + + ErrHttpDbOperateFailed = ErrNumber{ + Code: 1002, + Message: "数据库操作异常", + } + + ErrHttpParamNotValid = ErrNumber{ + Code: 1003, + Message: "入参不符合要求", + } +) diff --git a/util/log/log.go b/util/log/log.go new file mode 100644 index 0000000..8f8a0bd --- /dev/null +++ b/util/log/log.go @@ -0,0 +1,110 @@ +package log + +import ( + "context" + "fmt" + "go.uber.org/zap" + "go.uber.org/zap/zapcore" + lumberjack "gopkg.in/natefinch/lumberjack.v2" + "interview-one-stop-server/util" + "net/url" +) + +var log *zap.Logger + +type lumberjackSink struct { + *lumberjack.Logger +} + +func (l lumberjackSink) Sync() error { + return nil +} + +func GetLogger() *zap.Logger { + return log +} + +func newLogger(logLevel string, logFolder string, dev bool) (*zap.Logger, error) { + var level zapcore.Level + switch logLevel { + case "debug": + level = zap.DebugLevel + case "info": + level = zap.InfoLevel + case "warning": + level = zap.WarnLevel + case "error": + level = zap.ErrorLevel + default: + return nil, fmt.Errorf("unknown log level %s", logLevel) + } + + encoderConfig := zapcore.EncoderConfig{ + TimeKey: "ts", + LevelKey: "level", + NameKey: "logger", + MessageKey: "msg", + StacktraceKey: "stacktrace", + LineEnding: zapcore.DefaultLineEnding, + EncodeLevel: zapcore.CapitalLevelEncoder, + EncodeTime: zapcore.RFC3339TimeEncoder, + EncodeDuration: zapcore.SecondsDurationEncoder, + EncodeCaller: zapcore.ShortCallerEncoder, + ConsoleSeparator: " ", + } + ll := lumberjack.Logger{ + Filename: logFolder, + MaxSize: 100, // MB + MaxBackups: 31, + MaxAge: 31, // days + Compress: false, + } + + _ = zap.RegisterSink("lumberjack", func(*url.URL) (zap.Sink, error) { + return lumberjackSink{ + Logger: &ll, + }, nil + }) + + loggerConfig := zap.Config{ + Level: zap.NewAtomicLevelAt(level), + Development: dev, + Encoding: "console", + EncoderConfig: encoderConfig, + OutputPaths: []string{"stderr", fmt.Sprintf("lumberjack:%s", logFolder)}, + } + + logger, err := loggerConfig.Build() + if err != nil { + panic(fmt.Sprintf("build zap logger from config error: %v", err)) + } + return logger, nil +} + +func init() { + log, _ = newLogger("info", "logs/app.log", false) +} + +func Error(ctx context.Context, label string, msg string) { + log.Sugar().Error(getTraceIDFromContext(ctx) + " label=" + label + ",msg=" + msg) +} + +func Warn(ctx context.Context, label string, msg string) { + log.Sugar().Warnf(getTraceIDFromContext(ctx) + " label=" + label + ",msg=" + msg) +} + +func Info(ctx context.Context, label string, msg string) { + log.Sugar().Info(getTraceIDFromContext(ctx) + " label=" + label + ",msg=" + msg) +} + +func Debug(ctx context.Context, label string, msg string) { + log.Sugar().Debug(getTraceIDFromContext(ctx) + " label=" + label + ",msg=" + msg) +} + +func getTraceIDFromContext(ctx context.Context) string { + traceID := ctx.Value("trace_id") + if traceID == nil { + return util.GenUUID() + } + return traceID.(string) +} diff --git a/util/log/log_test.go b/util/log/log_test.go new file mode 100644 index 0000000..0c27938 --- /dev/null +++ b/util/log/log_test.go @@ -0,0 +1,37 @@ +package log + +import ( + "context" + "errors" + "fmt" + "interview-one-stop-server/util" + "testing" +) + +func TestLog(t *testing.T) { + ctx := context.WithValue(context.Background(), "trace_id", util.GenUUID()) + + err := errors.New("it's an error") + Error(ctx, "TestLog Error", fmt.Sprintf("err is %s,value is %+v", err.Error(), struct { + Name string + Age int + }{ + "issue", + 2, + })) + Warn(ctx, "TestLog Warn", fmt.Sprintf("err is %s,value is %v", errors.New("it's an error").Error(), struct { + Name string + Age int + }{ + "issue", + 2, + })) + Info(ctx, "TestLog Info", fmt.Sprintf("no error,value is %+v", struct { + Name string + Age int + }{ + "issue", + 2, + })) + Debug(ctx, "TestLog Debug", "it's end") +} diff --git a/util/string.go b/util/string.go new file mode 100644 index 0000000..fca09dd --- /dev/null +++ b/util/string.go @@ -0,0 +1,14 @@ +package util + +import "strconv" + +func Int64ArrJoin(nums []int64, sep string) string { + res := "" + if len(nums) == 0 { + return res + } + for _, num := range nums { + res += strconv.Itoa(int(num)) + sep + } + return res[:len(res)-1] +} diff --git a/util/util.go b/util/util.go new file mode 100644 index 0000000..bcb9f4b --- /dev/null +++ b/util/util.go @@ -0,0 +1,36 @@ +package util + +import ( + "context" + "fmt" + "github.com/google/uuid" + "interview-one-stop-server/constant" + "runtime" +) + +func CreateContextWithTraceID() context.Context { + return context.WithValue(context.Background(), constant.TraceID, GenUUID()) +} + +func GenUUID() string { + return uuid.New().String() +} + +// PanicToError Panic转换为error +func PanicToError(f func()) (err error) { + defer func() { + if e := recover(); e != nil { + err = fmt.Errorf(PanicTrace(e)) + } + }() + f() + return +} + +// PanicTrace panic调用链跟踪 +func PanicTrace(err interface{}) string { + stackBuf := make([]byte, 4096) + n := runtime.Stack(stackBuf, false) + + return fmt.Sprintf("panic: %v %s", err, stackBuf[:n]) +}