This commit is contained in:
C菌
2022-04-20 03:50:21 +08:00
commit c113e38d82
11 changed files with 465 additions and 0 deletions

40
pkg/base62/base62.go Normal file
View File

@@ -0,0 +1,40 @@
package base62
import (
"math"
"strings"
)
const (
BASE62 = "0123456789abcdefghijklmnoporstuvwxyzABCDEFGHIJKLMNOPORSTUVWXYZ"
LENGTH = 62
)
var (
ma = map[string]int{
"0": 0, "1": 1, "2": 2, "3": 3, "4": 4, "5": 5, "6": 6, "7": 7, "8": 8, "9": 9, "a": 10, "b": 11, "c": 12, "d": 13, "e": 14, "f": 15, "g": 16, "h": 17, "i": 18, "j": 19, "k": 20, "l": 21, "m": 22, "n": 23, "o": 24, "p": 25, "q": 26, "r": 27, "s": 28, "t": 29, "u": 30, "v": 31, "w": 32, "x": 33, "y": 34, "z": 35, "A": 36, "B": 37, "C": 38, "D": 39, "E": 40, "F": 41, "G": 42, "H": 43, "I": 44, "J": 45, "K": 46, "L": 47, "M": 48, "N": 49, "O": 50, "P": 51, "Q": 52, "R": 53, "S": 54, "T": 55, "U": 56, "V": 57, "W": 58, "X": 59, "Y": 60, "Z": 61,
}
)
func EncodeBase62(v int) string {
if v == 0 {
return "0"
}
var result []byte
for v > 0 {
quotient := v / LENGTH
residue := v % LENGTH
result = append(result, BASE62[residue])
v = quotient
}
return string(result)
}
func DecodeBase62(v string) int {
strings.TrimSpace(v)
num := 0
for k, vi := range v {
num += ma[string(vi)] * int(math.Pow(LENGTH, float64(k)))
}
return num
}

10
pkg/base62/tinyurl.go Normal file
View File

@@ -0,0 +1,10 @@
package base62
import (
mh "github.com/spaolacci/murmur3"
)
func TinyUrl(in string) string {
return EncodeBase62(int(mh.Sum32([]byte(in))))
}

72
pkg/config/config.go Normal file
View File

@@ -0,0 +1,72 @@
package config
import (
"io/ioutil"
"time"
"gopkg.in/yaml.v3"
)
type Config struct {
Server Server `yaml:"server"`
Data Data `yaml:"data"`
Log Log `yaml:"log"`
}
type Server struct {
Addr string `yaml:"addr"`
}
type Log struct {
Level string `yaml:"level"`
Output string `yaml:"output"`
Development bool `yaml:"development"`
Rotate Rotate `yaml:"rotate"`
}
type Rotate struct {
Filename string `yaml:"filename"`
MaxSize int `yaml:"maxsize"`
MaxAge int `yaml:"maxage"`
MaxBackups int `yaml:"maxbackups"`
LocalTime bool `yaml:"localtime"`
Compress bool `yaml:"compress"`
}
type Data struct {
DB DB `yaml:"database"`
Redis `yaml:"redis"`
}
type DB struct {
Host string `yaml:"host"`
Port int `yaml:"port"`
User string `yaml:"user"`
Password string `yaml:"password"`
Database string `yaml:"database"`
ConnTimeout time.Duration `yaml:"conn_timeout"`
ReadTimeout time.Duration `yaml:"read_timeout"`
WriteTimeout time.Duration `yaml:"write_timeout"`
ConnMaxIdleTime time.Duration `yaml:"conn_max_dile_time"`
ConnMaxLifeTime time.Duration `yaml:"conn_max_life_time"`
MaxIdleConns int `yaml:"max_idle_conns"`
MaxOpenConns int `yaml:"max_open_conns"`
}
type Redis struct {
Addr string `yaml:"addr"`
ReadTimeout string `yaml:"read_timeout"`
WriteTimeout string `yaml:"write_timeout"`
}
func Parse(file string) (*Config, error) {
data, err := ioutil.ReadFile(file)
if err != nil {
return nil, err
}
cfg := &Config{}
if err = yaml.Unmarshal(data, cfg); err != nil {
return nil, err
}
return cfg, nil
}

37
pkg/db/db.go Normal file
View File

@@ -0,0 +1,37 @@
package db
import (
"time"
"gorm.io/driver/mysql"
"gorm.io/gorm"
)
var (
DB *gorm.DB
)
type TinyUrl struct {
ID int64
OriginUrl string
TinyUrl string
CreatTime time.Time
ExpireTime time.Time
Counter float64
}
type URLDetail struct {
URL string `json:"url"`
CreatedAt string `json:"created_at"`
ExpirationInMinutes time.Duration `json:"expiration_in_minutes"`
}
func initDB() {
// 参考 https://github.com/go-sql-driver/mysql#dsn-data-source-name 获取详情
dsn := "tinyurl:tinyurl@tcp(42.192.36.14:3306)/tinyurl?charset=utf8mb4&parseTime=True&loc=Local"
var err error
DB, err = gorm.Open(mysql.Open(dsn), &gorm.Config{})
if err != nil {
panic(err)
}
}

152
pkg/log/log.go Normal file
View File

@@ -0,0 +1,152 @@
package log
import (
"context"
"errors"
"os"
"tinyurl/pkg/config"
"tinyurl/pkg/trace"
"github.com/fatih/color"
"go.uber.org/zap"
"go.uber.org/zap/zapcore"
"gopkg.in/natefinch/lumberjack.v2"
)
type Logger struct {
logger *zap.Logger
}
const (
StdOutput = "std"
FileOutput = "file"
)
var levelMap = map[string]zapcore.Level{
"debug": zapcore.DebugLevel,
"info": zapcore.InfoLevel,
"warn": zapcore.WarnLevel,
"error": zapcore.ErrorLevel,
"dpanic": zapcore.DPanicLevel,
"panic": zapcore.PanicLevel,
"fatal": zapcore.FatalLevel,
}
var colors = map[color.Attribute]*color.Color{
color.FgGreen: color.New(color.FgGreen),
color.FgHiWhite: color.New(color.FgHiWhite),
color.FgYellow: color.New(color.FgYellow),
color.FgRed: color.New(color.FgRed, color.Underline),
color.FgHiRed: color.New(color.FgHiRed, color.Underline, color.Bold),
color.FgBlue: color.New(color.FgBlue),
}
func New(c config.Log) (*Logger, error) {
level := getLevel(c.Level)
var writer zapcore.WriteSyncer
if c.Output == StdOutput {
writer = getStdWriter()
} else if c.Output == FileOutput {
writer = getFileWriter(c.Rotate)
} else {
return nil, errors.New("output must in [std, file]")
}
encoder := getJSONEncoder()
var opts []zap.Option
if c.Development {
encoder = getConsoleEncoder()
opts = append(opts, zap.AddCaller(), zap.AddCallerSkip(1), zap.Development())
}
core := zapcore.NewCore(encoder, writer, level)
return &Logger{zap.New(core, opts...)}, nil
}
func getLevel(level string) zapcore.Level {
if l, ok := levelMap[level]; ok {
return l
}
return zapcore.InfoLevel
}
func getJSONEncoder() zapcore.Encoder {
conf := zap.NewProductionEncoderConfig()
conf.EncodeTime = zapcore.TimeEncoderOfLayout("2006-01-02 15:04:05")
conf.EncodeCaller = zapcore.FullCallerEncoder
return zapcore.NewJSONEncoder(conf)
}
func getConsoleEncoder() zapcore.Encoder {
conf := zap.NewProductionEncoderConfig()
conf.EncodeTime = zapcore.TimeEncoderOfLayout("2006-01-02 15:04:05")
conf.EncodeCaller = zapcore.FullCallerEncoder
return zapcore.NewConsoleEncoder(conf)
}
func getFileWriter(c config.Rotate) zapcore.WriteSyncer {
logger := &lumberjack.Logger{
Filename: c.Filename,
MaxSize: c.MaxSize,
MaxAge: c.MaxAge,
MaxBackups: c.MaxBackups,
LocalTime: c.LocalTime,
Compress: c.Compress,
}
return zapcore.AddSync(logger)
}
func getStdWriter() zapcore.WriteSyncer {
return os.Stdout
}
func (l *Logger) Sync() error {
return l.logger.Sync()
}
func (l *Logger) Debug(ctx context.Context, msg string, fields ...zap.Field) {
fields = append(fields, getTrace(ctx))
msg = colors[color.FgBlue].Sprintf("%s", msg)
l.logger.Debug(msg, fields...)
}
func (l *Logger) Info(ctx context.Context, msg string, fields ...zap.Field) {
fields = append(fields, getTrace(ctx))
msg = colors[color.FgGreen].Sprintf("%s", msg)
l.logger.Info(msg, fields...)
}
func (l *Logger) Warn(ctx context.Context, msg string, fields ...zap.Field) {
fields = append(fields, getTrace(ctx))
msg = colors[color.FgYellow].Sprintf("%s", msg)
l.logger.Warn(msg, fields...)
}
func (l *Logger) Error(ctx context.Context, msg string, fields ...zap.Field) {
fields = append(fields, getTrace(ctx))
msg = colors[color.FgRed].Sprintf("%s", msg)
l.logger.Error(msg, fields...)
}
func (l *Logger) DPanic(ctx context.Context, msg string, fields ...zap.Field) {
fields = append(fields, getTrace(ctx))
msg = colors[color.FgHiRed].Sprintf("%s", msg)
l.logger.DPanic(msg, fields...)
}
func (l *Logger) Panic(ctx context.Context, msg string, fields ...zap.Field) {
fields = append(fields, getTrace(ctx))
msg = colors[color.FgHiRed].Sprintf("%s", msg)
l.logger.Panic(msg, fields...)
}
func (l *Logger) Fatal(ctx context.Context, msg string, fields ...zap.Field) {
fields = append(fields, getTrace(ctx))
msg = colors[color.FgHiRed].Sprintf("%s", msg)
l.logger.Fatal(msg, fields...)
}
func getTrace(ctx context.Context) zapcore.Field {
if ctx == nil {
return zap.Skip()
}
return zap.String("trace", trace.Trace(ctx))
}

32
pkg/trace/trace.go Normal file
View File

@@ -0,0 +1,32 @@
package trace
import (
"context"
"github.com/flamego/flamego"
"github.com/google/uuid"
)
type contextKey struct{}
var activeSpanKey = contextKey{}
func Context(ctx context.Context) context.Context {
x := uuid.New().String()
return context.WithValue(ctx, activeSpanKey, x)
}
func Trace(ctx context.Context) string {
traceValue := ctx.Value(activeSpanKey)
if trace, ok := traceValue.(string); ok {
return trace
}
return ""
}
func Tracer() flamego.ContextInvoker {
return func(ctx flamego.Context) {
r := ctx.Request().Clone(Context(context.Background()))
ctx.Map(r)
}
}