工程初始化

main
wangyang 2023-03-03 15:24:58 +08:00
parent dccad1b385
commit 006d5443c6
18 changed files with 574 additions and 0 deletions

12
biz/question/question.go Normal file
View File

@ -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
}

6
conf/conf_dev.yaml Normal file
View File

@ -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"

6
conf/conf_prod.yaml Normal file
View File

@ -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""

88
config/config.go Normal file
View File

@ -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)
}

31
config/model.go Normal file
View File

@ -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"`
}

9
constant/constant.go Normal file
View File

@ -0,0 +1,9 @@
package constant
type ContextKey string
const TraceID ContextKey = "trace_id"
const (
EnvNameTencentProd string = "tencent-prod"
)

30
db/common.go Normal file
View File

@ -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
}

6
db/table_name.go Normal file
View File

@ -0,0 +1,6 @@
package db
const (
// TableNameAnnotatingRecord 送标任务记录
TableNameAnnotatingRecord = "annotating_record"
)

13
handler/question.go Normal file
View File

@ -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)
}

56
main.go Normal file
View File

@ -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()))
}
}

4
model/model.go Normal file
View File

@ -0,0 +1,4 @@
package model
type Question struct {
}

26
router.go Normal file
View File

@ -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",
})
}

30
util/gin_util/gin.go Normal file
View File

@ -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()
}

60
util/ierrors/error.go Normal file
View File

@ -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: "入参不符合要求",
}
)

110
util/log/log.go Normal file
View File

@ -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)
}

37
util/log/log_test.go Normal file
View File

@ -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")
}

14
util/string.go Normal file
View File

@ -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]
}

36
util/util.go Normal file
View File

@ -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])
}