This commit is contained in:
henry
2021-11-02 09:43:19 +08:00
parent 570bb3c772
commit 4734344985
78 changed files with 4798 additions and 0 deletions

44
serve/cache/init.go vendored Normal file
View File

@ -0,0 +1,44 @@
package cache
import (
"Edu/config"
"Edu/serve/cache/logic"
"fmt"
)
var (
Cache logic.ICache
engines = map[string]func() logic.ICache{
"memory": memory, "redis": redis,
}
)
func memory() logic.ICache {
return logic.NewMemory()
}
func redis() logic.ICache {
return logic.NewRedis(&logic.RedisOption{
Addr: config.SettingInfo.Cache.Redis.Addr,
Password: config.SettingInfo.Cache.Redis.Password,
DB: config.SettingInfo.Cache.Redis.DB,
MinIdleConns: config.SettingInfo.Cache.Redis.MaxIdle,
IdleTimeout: config.SettingInfo.Cache.Redis.IdleTimeout,
})
}
func Init() {
handle, has := engines[config.SettingInfo.Cache.Type]
if !has {
panic(fmt.Sprintf("Unknown Cache Engine Mode%s", config.SettingInfo.Cache.Type))
}
Cache = handle()
err := Cache.Run()
if err != nil {
panic("Cache Run Error" + err.Error())
}
}

28
serve/cache/logic/cache.go vendored Normal file
View File

@ -0,0 +1,28 @@
package logic
type ICache interface {
Set(key string, value interface{}, expiration int) error
Get(key string) (string, error)
Del(key string) error
ZAdd(key string, members ...*ScoreParams) error
ZRangebyscore(key string, opt *ScoreRangeBy) ([]string, error)
ZRem(key string, members ...interface{}) error
HExists(key, field string) (bool, error)
HSet(key, field string, value interface{}) error
HGet(key, field string) (string, error)
HDel(key string, fields ...string) error
SAdd(key string, members ...interface{}) error
SIsMember(key string, members interface{}) (bool, error)
SRem(key string, members ...interface{}) error
Run() error
}
type ScoreParams struct {
Score float64
Member interface{}
}
type ScoreRangeBy struct {
Min, Max string
Offset, Count int64
}

177
serve/cache/logic/memory.go vendored Normal file
View File

@ -0,0 +1,177 @@
package logic
import (
"Edu/utils"
"strings"
"sync"
)
type Memory struct {
cache map[string]interface{}
cacheList []interface{}
cacheSorted map[string][]*ScoreParams
locker *sync.RWMutex
}
func (this *Memory) Set(key string, value interface{}, expiration int) error {
this.locker.Lock()
defer this.locker.Unlock()
this.cache[key] = value
return nil
}
// Get
func (this *Memory) Get(key string) (string, error) {
this.locker.RLock()
defer this.locker.RUnlock()
data, has := this.cache[key]
if !has {
return "", nil
}
return utils.AnyToJSON(data), nil
}
// Del
func (this *Memory) Del(key string) error {
this.locker.Lock()
defer this.locker.Unlock()
delete(this.cache, key)
return nil
}
func (this *Memory) ZAdd(key string, members ...*ScoreParams) error {
this.locker.Lock()
defer this.locker.Unlock()
if _, has := this.cacheSorted[key]; !has {
this.cacheSorted[key] = members
} else {
this.cacheSorted[key] = append(this.cacheSorted[key], members...)
}
return nil
}
func (this *Memory) ZRangebyscore(key string, opt *ScoreRangeBy) ([]string, error) {
this.locker.Lock()
defer this.locker.Unlock()
if _, has := this.cacheSorted[key]; !has {
return []string{}, nil
}
out := make([]string, 0)
function := func(src string) (float64, bool) {
contain := false
if strings.Contains(opt.Min, "(") {
src = strings.Replace(src, "(", "", 1)
contain = true
}
f, _ := utils.StringToFloat(src)
return f, contain
}
min, minContain := function(opt.Min)
max, maxContain := function(opt.Max)
for _, v := range this.cacheSorted[key] {
if ((v.Score >= min && minContain) || v.Score > min) &&
((v.Score <= max && maxContain) || v.Score < max) {
out = append(out, utils.AnyToJSON(v.Member))
if opt.Count > 0 && int64(len(out)) > opt.Count {
return out, nil
}
}
}
return out, nil
}
func (this *Memory) ZRem(key string, members ...interface{}) error {
this.locker.Lock()
defer this.locker.Unlock()
if _, has := this.cacheSorted[key]; !has {
return nil
}
for k, v := range this.cacheSorted[key] {
for _, member := range members {
if v.Member == member {
this.cacheSorted[key] = append(this.cacheSorted[key][:k], this.cacheSorted[key][k+1:]...)
break
}
}
}
return nil
}
// HExists HASH Exist
func (this *Memory) HExists(key, field string) (bool, error) {
return this.cache[key] != nil, nil
}
// HSet HASH Set
func (this *Memory) HSet(key, field string, value interface{}) error {
this.locker.Lock()
defer this.locker.Unlock()
this.cache[key] = value
return nil
}
// HGet Hash Get
func (this *Memory) HGet(key, field string) (string, error) {
this.locker.RLock()
defer this.locker.RUnlock()
data, has := this.cache[key]
if !has {
return "", nil
}
return utils.AnyToJSON(data), nil
}
// HDel HASH Del
func (this *Memory) HDel(key string, fields ...string) error {
this.locker.Lock()
defer this.locker.Unlock()
delete(this.cache, key)
return nil
}
// SAdd
func (this *Memory) SAdd(key string, members ...interface{}) error {
this.locker.Lock()
defer this.locker.Unlock()
this.cacheList = append(this.cacheList, members...)
return nil
}
// SIsMember
func (this *Memory) SIsMember(key string, members interface{}) (bool, error) {
this.locker.RLock()
defer this.locker.RUnlock()
return utils.InArray(members, this.cacheList), nil
}
// SRem
func (this *Memory) SRem(key string, members ...interface{}) error {
this.locker.Lock()
defer this.locker.Unlock()
for k, v := range this.cacheList {
if utils.InArray(v, members) {
this.cacheList = append(this.cacheList[:k], this.cacheList[k+1:]...)
}
}
return nil
}
func (this *Memory) Run() error {
return nil
}
func NewMemory() *Memory {
return &Memory{
cache: make(map[string]interface{}, 0),
cacheList: make([]interface{}, 0),
cacheSorted: make(map[string][]*ScoreParams, 0),
locker: new(sync.RWMutex),
}
}

114
serve/cache/logic/redis.go vendored Normal file
View File

@ -0,0 +1,114 @@
package logic
import (
"time"
"github.com/pkg/errors"
"github.com/go-redis/redis"
)
type Redis struct {
*RedisOption
}
type RedisOption struct {
Addr string
Password string
DB int
MinIdleConns int
IdleTimeout int
}
var RedisClient *redis.Client
// Set
func (this *Redis) Set(key string, value interface{}, expiration int) error {
return RedisClient.Set(key, value, time.Duration(expiration)*time.Second).Err()
}
// Get
func (this *Redis) Get(key string) (string, error) {
return RedisClient.Get(key).Result()
}
// Del
func (this *Redis) Del(key string) error {
return RedisClient.Del(key).Err()
}
func (this *Redis) ZAdd(key string, members ...*ScoreParams) error {
redisZ := make([]redis.Z, 0)
for _, v := range members {
redisZ = append(redisZ, redis.Z{Score: v.Score, Member: v.Member})
}
return RedisClient.ZAdd(key, redisZ...).Err()
}
func (this *Redis) ZRangebyscore(key string, opt *ScoreRangeBy) ([]string, error) {
return RedisClient.ZRangeByScore(key, redis.ZRangeBy{Min: opt.Min, Max: opt.Max, Offset: opt.Offset, Count: opt.Count}).Result()
}
func (this *Redis) ZRem(key string, members ...interface{}) error {
return RedisClient.ZRem(key, members...).Err()
}
// HExists HASH Exist
func (this *Redis) HExists(key, field string) (bool, error) {
return RedisClient.HExists(key, field).Result()
}
// HSet HASH Set
func (this *Redis) HSet(key, field string, value interface{}) error {
return RedisClient.HSet(key, field, value).Err()
}
// HGet Hash Get
func (this *Redis) HGet(key, field string) (string, error) {
return RedisClient.HGet(key, field).Result()
}
// HDel HASH Del
func (this *Redis) HDel(key string, fields ...string) error {
return RedisClient.HDel(key, fields...).Err()
}
// SAdd
func (this *Redis) SAdd(key string, members ...interface{}) error {
return RedisClient.SAdd(key, members...).Err()
}
// SIsMember
func (this *Redis) SIsMember(key string, members interface{}) (bool, error) {
return RedisClient.SIsMember(key, members).Result()
}
// SRem
func (this *Redis) SRem(key string, members ...interface{}) error {
return RedisClient.SRem(key, members...).Err()
}
// Run 开启
func (this *Redis) Run() error {
option := &redis.Options{
Network: "",
Addr: this.Addr,
Password: this.Password,
DB: this.DB,
MinIdleConns: this.MinIdleConns,
IdleTimeout: time.Duration(this.IdleTimeout),
}
RedisClient = redis.NewClient(option)
ping, err := RedisClient.Ping().Result()
if err != nil {
return errors.New(ping + err.Error())
}
return nil
}
func NewRedis(option *RedisOption) *Redis {
return &Redis{option}
}

32
serve/cache/logic/redis_test.go vendored Normal file
View File

@ -0,0 +1,32 @@
package logic
import (
"github.com/go-redis/redis"
"testing"
)
var redisClient *redis.Client
func InitRedis() {
option := &redis.Options{
Network: "",
//Addr: this.Addr,
//Password: this.Password,
//DB: this.DB,
//MinIdleConns: this.MinIdleConns,
//IdleTimeout: time.Duration(this.IdleTimeout),
}
redisClient = redis.NewClient(option)
ping, err := redisClient.Ping().Result()
if err != nil {
panic(ping + err.Error())
}
}
func TestNewRedis(t *testing.T) {
InitRedis()
//redisClient.
}

32
serve/es/es.go Normal file
View File

@ -0,0 +1,32 @@
package es
import "github.com/elastic/go-elasticsearch/v7"
type Es struct{ *EsConfig }
type EsConfig struct {
Address []string
}
type EsServer func(*EsConfig) *Es
var esClient = new(elasticsearch.Client)
func (this *Es) Run() {
obj := elasticsearch.Config{
Addresses: this.Address,
Username: "",
Password: "",
}
var err error
if esClient, err = elasticsearch.NewClient(obj); err != nil {
panic("Elasticsearch Error " + err.Error())
}
}
func NewEs() EsServer {
return func(config *EsConfig) *Es {
return &Es{config}
}
}

5
serve/es/serve.go Normal file
View File

@ -0,0 +1,5 @@
package es
func Set() {
esClient.Search()
}

89
serve/logger/hook.go Normal file
View File

@ -0,0 +1,89 @@
package logger
import (
"Edu/utils"
"fmt"
"os"
"path/filepath"
"runtime"
"time"
nested "github.com/antonfisher/nested-logrus-formatter"
rotatelogs "github.com/lestrrat-go/file-rotatelogs"
"github.com/rifflock/lfshook"
log "github.com/sirupsen/logrus"
)
// Hook
type Hook struct {
Path string
File string
}
// Levels 只定义 error, warn, panic 等级的日志,其他日志等级不会触发 hook
func (this *Hook) Levels() []log.Level {
return []log.Level{
log.WarnLevel,
log.ErrorLevel,
log.PanicLevel,
}
}
// Fire 将异常日志写入到指定日志文件中
func (this *Hook) Fire(entry *log.Entry) error {
if isExist, _ := utils.PathExists(this.Path); !isExist {
utils.MkdirAll(this.Path)
}
f, err := os.OpenFile(this.Path+this.File, os.O_RDWR|os.O_CREATE|os.O_APPEND, 0666)
if err != nil {
return err
}
_, err = f.Write(utils.AnyToByte(entry.Data))
return err
}
func formatter(isConsole bool) *nested.Formatter {
fmtter := &nested.Formatter{
HideKeys: true,
TimestampFormat: "2006-01-02 15:04:05",
CallerFirst: true,
CustomCallerFormatter: func(frame *runtime.Frame) string {
funcInfo := runtime.FuncForPC(frame.PC)
if funcInfo == nil {
return "error during runtime.FuncForPC"
}
fullPath, line := funcInfo.FileLine(frame.PC)
return fmt.Sprintf(" [%v:%v]", filepath.Base(fullPath), line)
},
}
if isConsole {
fmtter.NoColors = false
} else {
fmtter.NoColors = true
}
return fmtter
}
// NewHook
func NewHook(logName string, rotationTime time.Duration, leastDay uint) log.Hook {
writer, err := rotatelogs.New(
// 日志文件
logName+".%Y%m%d",
rotatelogs.WithRotationCount(leastDay), // 只保留最近的N个日志文件
)
if err != nil {
panic(err)
}
lfsHook := lfshook.NewHook(lfshook.WriterMap{
log.DebugLevel: writer,
log.InfoLevel: writer,
log.WarnLevel: writer,
log.ErrorLevel: writer,
log.FatalLevel: writer,
log.PanicLevel: writer,
}, formatter(false))
return lfsHook
}

63
serve/logger/init.go Normal file
View File

@ -0,0 +1,63 @@
package logger
import (
"io"
"os"
log "github.com/sirupsen/logrus"
)
type Logger struct {
*Option
Logger *log.Logger
}
type Option struct {
File string `json:"file"`
LeastDay uint `json:"least_day"`
Level string `json:"level"`
IsStdout bool `json:"is_stdout"`
}
var logger *log.Logger
var loggerLevel = map[string]log.Level{
"debug": log.DebugLevel,
"info": log.InfoLevel,
"warn": log.WarnLevel,
"error": log.ErrorLevel,
}
func (this *Logger) level() log.Level {
if _, has := loggerLevel[this.Level]; !has {
return log.ErrorLevel
}
return loggerLevel[this.Level]
}
func (this *Logger) Load() {
logger = this.Logger
}
func (this *Logger) Init(option *Option) *Logger {
this.Option = option
logger = log.New()
logger.SetFormatter(&log.JSONFormatter{TimestampFormat: "2006-01-02 15:04:05"})
logger.SetReportCaller(true)
logger.AddHook(NewHook(this.File, 0, this.LeastDay))
if this.IsStdout {
logger.SetOutput(io.MultiWriter(os.Stdout))
}
logger.SetFormatter(formatter(true))
logger.SetLevel(this.level())
this.Logger = logger
return this
}
func NewLogger() *Logger {
return &Logger{}
}

37
serve/logger/serve.go Normal file
View File

@ -0,0 +1,37 @@
package logger
import "github.com/sirupsen/logrus"
func Warn(args ...interface{}) {
logger.Log(logrus.WarnLevel, args...)
}
func Error(args ...interface{}) {
logger.Log(logrus.ErrorLevel, args...)
}
func Fatal(args ...interface{}) {
logger.Log(logrus.FatalLevel, args...)
logger.Exit(1)
}
func Panic(args ...interface{}) {
logger.Log(logrus.PanicLevel, args...)
}
func WarnF(format string, args ...interface{}) {
logger.Logf(logrus.WarnLevel, format, args...)
}
func ErrorF(format string, args ...interface{}) {
logger.Logf(logrus.ErrorLevel, format, args...)
}
func FatalF(format string, args ...interface{}) {
logger.Logf(logrus.FatalLevel, format, args...)
logger.Exit(1)
}
func PanicF(format string, args ...interface{}) {
logger.Logf(logrus.PanicLevel, format, args...)
}

View File

@ -0,0 +1,7 @@
package logic
import "gorm.io/gorm"
type IEngine interface {
DSN() gorm.Dialector
}

20
serve/orm/logic/mysql.go Normal file
View File

@ -0,0 +1,20 @@
package logic
import (
"fmt"
"gorm.io/driver/mysql"
"gorm.io/gorm"
)
type Mysql struct {
User, Password, Host string
Port int
DBName, Parameters string
}
func (this *Mysql) DSN() gorm.Dialector {
return mysql.Open(fmt.Sprintf("%s:%s@tcp(%s:%d)/%s?%s",
this.User, this.Password, this.Host, this.Port, this.DBName, this.Parameters,
))
}

19
serve/orm/logic/sqlite.go Normal file
View File

@ -0,0 +1,19 @@
package logic
import (
"github.com/belief428/gorm-engine/tools"
"gorm.io/driver/sqlite"
"gorm.io/gorm"
)
type Sqlite struct {
Path string
Name string
}
func (this *Sqlite) DSN() gorm.Dialector {
if isExist, _ := tools.PathExists(this.Path); !isExist {
_ = tools.MkdirAll(this.Path)
}
return sqlite.Open(this.Path + "/" + this.Name)
}

79
serve/orm/orm.go Normal file
View File

@ -0,0 +1,79 @@
package orm
import (
"Edu/config"
"Edu/serve/orm/logic"
"fmt"
"log"
"os"
"time"
"gorm.io/gorm/logger"
"gorm.io/gorm/schema"
"gorm.io/gorm"
)
var (
orm *gorm.DB
engines = map[string]func() logic.IEngine{
"mysql": mysql, "sqlite": sqlite,
}
)
func mysql() logic.IEngine {
return &logic.Mysql{
User: config.SettingInfo.Engine.Mysql.User, Password: config.SettingInfo.Engine.Mysql.Password,
Host: config.SettingInfo.Engine.Mysql.Host, Port: config.SettingInfo.Engine.Mysql.Port,
DBName: config.SettingInfo.Engine.Mysql.DBName, Parameters: config.SettingInfo.Engine.Mysql.Parameters,
}
}
func sqlite() logic.IEngine {
return &logic.Sqlite{Path: config.SettingInfo.Engine.Sqlite.Path, Name: config.SettingInfo.Engine.Sqlite.Name}
}
func Init() {
handle, has := engines[config.SettingInfo.Engine.DBMode]
if !has {
panic(fmt.Sprintf("Unknown Engine Mode%d", config.SettingInfo.Engine.DBMode))
}
option := &gorm.Config{
DisableForeignKeyConstraintWhenMigrating: true,
NamingStrategy: schema.NamingStrategy{
TablePrefix: config.SettingInfo.Engine.TablePrefix,
SingularTable: !config.SettingInfo.Engine.Complex,
},
}
if config.SettingInfo.Engine.Debug {
option.Logger = logger.New(
log.New(os.Stdout, "\r\n", log.LstdFlags),
logger.Config{
SlowThreshold: time.Second,
LogLevel: logger.Info,
Colorful: false,
IgnoreRecordNotFoundError: true,
},
)
}
db, err := gorm.Open(handle().DSN(), option)
if err != nil {
panic("Orm Open Error" + err.Error())
}
_db, _ := db.DB()
_db.SetMaxIdleConns(config.SettingInfo.Engine.MaxIdleConns)
_db.SetMaxOpenConns(config.SettingInfo.Engine.MaxOpenConns)
_db.SetConnMaxLifetime(time.Duration(config.SettingInfo.Engine.MaxLifetime) * time.Second)
orm = db
}
func GetDB() *gorm.DB {
if _, err := orm.DB(); err != nil {
Init()
}
return orm
}

62
serve/web/web.go Normal file
View File

@ -0,0 +1,62 @@
package web
import (
"fmt"
"net/http"
"os"
"os/signal"
"syscall"
"time"
)
type Web struct {
*WebConfig
httpServer *http.Server
}
type WebConfig struct {
Port, ReadTimeout, WriteTimeout, IdleTimeout int
}
type WebServer func(config *WebConfig) *Web
func (this *Web) stop() {
c := make(chan os.Signal)
signal.Notify(c, os.Interrupt, os.Kill, syscall.SIGHUP, syscall.SIGTERM)
go func() {
defer func() {
if err := recover(); err != nil {
err = fmt.Errorf("internal error: %v", err)
}
}()
s := <-c
defer close(c)
signal.Stop(c)
fmt.Printf("Http Server StopClosed - %d\n", s)
os.Exit(0)
}()
}
func (this *Web) Run(handler http.Handler) {
this.httpServer = &http.Server{
Addr: fmt.Sprintf(":%d", this.Port),
Handler: handler,
ReadTimeout: time.Duration(this.ReadTimeout) * time.Second,
WriteTimeout: time.Duration(this.WriteTimeout) * time.Second,
IdleTimeout: time.Duration(this.IdleTimeout) * time.Second,
MaxHeaderBytes: 1 << 20,
}
this.stop()
fmt.Println("Http Server Start")
fmt.Printf("Http Server Address - %v\n", fmt.Sprintf("http://127.0.0.1:%d", this.Port))
_ = this.httpServer.ListenAndServe()
}
func NewWeb() WebServer {
return func(config *WebConfig) *Web {
return &Web{WebConfig: config}
}
}