This commit is contained in:
xing 2021-05-28 18:41:12 +08:00
parent 866e90b251
commit f1ab91c2a4
11 changed files with 611 additions and 0 deletions

49
pkg/app/app.go Normal file
View File

@ -0,0 +1,49 @@
package app
import (
"blog/pkg/errorcode"
"github.com/gin-gonic/gin"
"net/http"
)
type Response struct {
Ctx *gin.Context
}
type Pager struct {
Page int `json:"page"`
PageSize int `json:"page_size"`
TotalRows int `json:"total_rows"`
}
func NewResponse(ctx *gin.Context) *Response {
return &Response{Ctx: ctx}
}
func (r *Response) ToResponse(data interface{}) {
if data == nil {
data = gin.H{}
}
r.Ctx.JSON(http.StatusOK, data)
}
func (r *Response) ToResponseList(list interface{}, totalRows int) {
r.Ctx.JSON(http.StatusOK, gin.H{
"list": list,
"pager": Pager{
Page: GetPage(r.Ctx),
PageSize: GetPageSize(r.Ctx),
TotalRows: totalRows,
},
})
}
func (r *Response) ToErrorResponse(err *errorcode.Error) {
response := gin.H{"code": err.Code(), "msg": err.Msg()}
details := err.Details()
if len(details) > 0 {
response["details"] = details
}
r.Ctx.JSON(err.StatusCode(), response)
}

56
pkg/app/form.go Normal file
View File

@ -0,0 +1,56 @@
package app
import (
"github.com/gin-gonic/gin"
ut "github.com/go-playground/universal-translator"
val "github.com/go-playground/validator/v10"
"strings"
)
type ValidError struct {
Key string
Message string
}
type ValidErrors []*ValidError
func (v *ValidError) Error() string {
return v.Message
}
func (v ValidErrors) Error() string {
return strings.Join(v.Errors(), ",")
}
func (v ValidErrors) Errors() []string {
var errs []string
for _, err := range v {
errs = append(errs, err.Error())
}
return errs
}
func BindAndValid(c *gin.Context, v interface{}) (bool, ValidErrors) {
var errs ValidErrors
err := c.ShouldBind(v)
if err != nil {
v := c.Value("trans")
trans, _ := v.(ut.Translator)
verrs, ok := err.(val.ValidationErrors)
if !ok {
return false, errs
}
for key, value := range verrs.Translate(trans) {
errs = append(errs, &ValidError{
Key: key,
Message: value,
})
}
return false, errs
}
return true, nil
}

37
pkg/app/pagination.go Normal file
View File

@ -0,0 +1,37 @@
package app
import (
"blog/global"
"blog/pkg/convert"
"github.com/gin-gonic/gin"
)
func GetPage(c *gin.Context) int {
page := convert.StrTo(c.Query("page")).MustInt()
if page <= 0 {
return 1
}
return page
}
func GetPageSize(c *gin.Context) int {
pageSize := convert.StrTo(c.Query("page_size")).MustInt()
if pageSize <= 0 {
return global.AppSetting.DefaultPageSize
}
if pageSize > global.AppSetting.MaxPageSize {
return global.AppSetting.MaxPageSize
}
return pageSize
}
func GetPageOffset(page, pageSize int) int {
result := 0
if page > 0 {
result = (page - 1) * pageSize
}
return result
}

29
pkg/convert/convert.go Normal file
View File

@ -0,0 +1,29 @@
package convert
import "strconv"
type StrTo string
func (s StrTo) String() string {
return string(s)
}
func (s StrTo) Int() (int, error) {
v, err := strconv.Atoi(s.String())
return v, err
}
func (s StrTo) MustInt() int {
v, _ := s.Int()
return v
}
func (s StrTo) UInt32() (uint32, error) {
v, err := strconv.Atoi(s.String())
return uint32(v), err
}
func (s StrTo) MustUInt32() uint32 {
v, _ := s.UInt32()
return v
}

View File

@ -0,0 +1,87 @@
package errorcode
import (
"fmt"
"net/http"
)
var (
Success = NewError(0, "成功")
ServerError = NewError(10000000, "服务内部错误")
InvalidParams = NewError(10000001, "入参错误")
NotFound = NewError(10000002, "找不到")
UnauthorizedAuthNotExist = NewError(10000003, "鉴权失败,找不到对应的 AppKey 和 AppSecret")
UnauthorizedTokenError = NewError(10000004, "鉴权失败Token 错误")
UnauthorizedTokenTimeout = NewError(10000005, "鉴权失败Token 超时")
UnauthorizedTokenGenerate = NewError(10000006, "鉴权失败Token 生成失败")
TooManyRequests = NewError(10000007, "请求过多")
)
type Error struct {
code int `json:"code"`
msg string `json:"msg"`
details []string `json:"details"`
}
var codes = map[int]string{}
func NewError(code int, msg string) *Error {
if _, ok := codes[code]; ok {
panic(fmt.Sprintf("错误码 %d 已经存在,请更换一个", code))
}
codes[code] = msg
return &Error{code: code, msg: msg}
}
func (e *Error) Error() string {
return fmt.Sprintf("错误码:%d, 错误信息:%s", e.Code(), e.Msg())
}
func (e *Error) Code() int {
return e.code
}
func (e *Error) Msg() string {
return e.msg
}
func (e *Error) Msgf(args []interface{}) string {
return fmt.Sprintf(e.msg, args...)
}
func (e *Error) Details() []string {
return e.details
}
func (e *Error) WithDetails(details ...string) *Error {
newError := *e
newError.details = []string{}
for _, d := range details {
newError.details = append(newError.details, d)
}
return &newError
}
func (e *Error) StatusCode() int {
switch e.Code() {
case Success.Code():
return http.StatusOK
case ServerError.Code():
return http.StatusInternalServerError
case InvalidParams.Code():
return http.StatusBadRequest
case UnauthorizedAuthNotExist.Code():
fallthrough
case UnauthorizedTokenError.Code():
fallthrough
case UnauthorizedTokenGenerate.Code():
fallthrough
case UnauthorizedTokenTimeout.Code():
return http.StatusUnauthorized
case TooManyRequests.Code():
return http.StatusTooManyRequests
}
return http.StatusInternalServerError
}

View File

@ -0,0 +1,10 @@
package errorcode
var (
ErrorGetTagListFail = NewError(20010001, "获取标签列表失败")
ErrorCreateTagFail = NewError(20010002, "创建标签失败")
ErrorUpdateTagFail = NewError(20010003, "更新标签失败")
ErrorDeleteTagFail = NewError(20010004, "删除标签失败")
ErrorCountTagFail = NewError(20010005, "统计标签失败")
ErrorUploadFileFail = NewError(20030001, "上传文件失败")
)

165
pkg/logger/logger.go Normal file
View File

@ -0,0 +1,165 @@
package logger
import (
"context"
"encoding/json"
"fmt"
"io"
"log"
"runtime"
"time"
)
type Level int8
type Fields map[string]interface{}
const (
LevelDebug Level = iota
LevelInfo
LevelWarn
LevelError
LevelFatal
LevelPanic
)
func (l Level) String() string {
switch l {
case LevelDebug:
return "debug"
case LevelInfo:
return "info"
case LevelWarn:
return "warn"
case LevelError:
return "error"
case LevelFatal:
return "fatal"
case LevelPanic:
return "panic"
}
return ""
}
type Logger struct {
newLogger *log.Logger
ctx context.Context
fields Fields
callers []string
}
func NewLogger(w io.Writer, prefix string, flag int) *Logger {
l := log.New(w, prefix, flag)
return &Logger{newLogger: l}
}
func (l *Logger) clone() *Logger {
nl := *l
return &nl
}
func (l *Logger) WithFields(f Fields) *Logger {
ll := l.clone()
if ll.fields == nil {
ll.fields = make(Fields)
}
for k, v := range f {
ll.fields[k] = v
}
return ll
}
func (l *Logger) WithContext(ctx context.Context) *Logger {
ll := l.clone()
ll.ctx = ctx
return ll
}
func (l *Logger) WithCaller(skip int) *Logger {
ll := l.clone()
pc, file, line, ok := runtime.Caller(skip)
if ok {
f := runtime.FuncForPC(pc)
ll.callers = []string{fmt.Sprintf("%s: %d %s", file, line, f.Name())}
}
return ll
}
func (l *Logger) WithCallersFrames() *Logger {
maxCallerDepth := 25
minCallerDepth := 1
callers := []string{}
pcs := make([]uintptr, maxCallerDepth)
depth := runtime.Callers(minCallerDepth, pcs)
frames := runtime.CallersFrames(pcs[:depth])
for frame, more := frames.Next(); more; frame, more = frames.Next() {
callers = append(callers, fmt.Sprintf("%s: %d %s", frame.File, frame.Line, frame.Function))
if !more {
break
}
}
ll := l.clone()
ll.callers = callers
return ll
}
func (l *Logger) JSONFormat(level Level, message string) map[string]interface{} {
data := make(Fields, len(l.fields)+4)
data["level"] = level.String()
data["time"] = time.Now().Local().UnixNano()
data["message"] = message
data["callers"] = l.callers
if len(l.fields) > 0 {
for k, v := range l.fields {
if _, ok := data[k]; !ok {
data[k] = v
}
}
}
return data
}
func (l *Logger) Output(level Level, message string) {
body, _ := json.Marshal(l.JSONFormat(level, message))
content := string(body)
switch level {
case LevelDebug:
l.newLogger.Print(content)
case LevelInfo:
l.newLogger.Print(content)
case LevelWarn:
l.newLogger.Print(content)
case LevelError:
l.newLogger.Print(content)
case LevelFatal:
l.newLogger.Fatal(content)
case LevelPanic:
l.newLogger.Panic(content)
}
}
func (l *Logger) Info(v ...interface{}) {
l.Output(LevelInfo, fmt.Sprint(v...))
}
func (l *Logger) Infof(format string, v ...interface{}) {
l.Output(LevelInfo, fmt.Sprintf(format, v...))
}
func (l *Logger) Fatal(v ...interface{}) {
l.Output(LevelFatal, fmt.Sprint(v...))
}
func (l *Logger) Fatalf(format string, v ...interface{}) {
l.Output(LevelFatal, fmt.Sprintf(format, v...))
}
func (l *Logger) Error(v ...interface{}) {
l.Output(LevelError, fmt.Sprint(v...))
}
func (l *Logger) Errorf(format string, v ...interface{}) {
l.Output(LevelError, fmt.Sprintf(format, v...))
}

44
pkg/setting/section.go Normal file
View File

@ -0,0 +1,44 @@
package setting
import "time"
type ServerSettingS struct {
RunMode string
HttpPort string
ReadTimeout time.Duration
WriteTimeout time.Duration
}
type AppSettingS struct {
DefaultPageSize int
MaxPageSize int
LogSavePath string
LogFileName string
LogFileExt string
UploadSavePath string
UploadServerUrl string
UploadImageMaxSize int
UploadImageAllowExts []string
}
type DatabaseSettingS struct {
DBType string
UserName string
Password string
Host string
DBName string
TablePrefix string
Charset string
ParseTime bool
MaxIdleConns int
MaxOpenConns int
}
func (s *Setting) ReadSection(k string, v interface{}) error {
err := s.vp.UnmarshalKey(k, v)
if err != nil {
return err
}
return nil
}

20
pkg/setting/setting.go Normal file
View File

@ -0,0 +1,20 @@
package setting
import "github.com/spf13/viper"
type Setting struct {
vp *viper.Viper
}
func NewSetting() (*Setting, error) {
vp := viper.New()
vp.SetConfigName("config")
vp.AddConfigPath("configs/")
vp.SetConfigType("yaml")
err := vp.ReadInConfig()
if err != nil {
return nil, err
}
return &Setting{vp}, nil
}

101
pkg/upload/file.go Normal file
View File

@ -0,0 +1,101 @@
package upload
import (
"blog/global"
"blog/pkg/util"
"io"
"io/ioutil"
"mime/multipart"
"os"
"path"
"strings"
)
type FileType int
const (
TypeImage FileType = iota + 1
TypeExcel
TypeTxt
)
func GetFileName(name string) string {
ext := GetFileExt(name)
fileName := strings.TrimSuffix(name, ext)
fileName = util.EncodeMD5(fileName)
return fileName + ext
}
func GetFileExt(name string) string {
return path.Ext(name)
}
func GetSavePath() string {
return global.AppSetting.UploadSavePath
}
func CheckSavePath(dst string) bool {
_, err := os.Stat(dst)
return os.IsNotExist(err)
}
func CheckContainExt(t FileType, name string) bool {
ext := GetFileExt(name)
ext = strings.ToUpper(ext)
switch t {
case TypeImage:
for _, allowExt := range global.AppSetting.UploadImageAllowExts {
if strings.ToUpper(allowExt) == ext {
return true
}
}
}
return false
}
func CheckMaxSize(t FileType, f multipart.File) bool {
content, _ := ioutil.ReadAll(f)
size := len(content)
switch t {
case TypeImage:
if size >= global.AppSetting.UploadImageMaxSize*1024*1024 {
return true
}
}
return false
}
func CheckPermission(dst string) bool {
_, err := os.Stat(dst)
return os.IsPermission(err)
}
func CreateSavePath(dst string, perm os.FileMode) error {
err := os.MkdirAll(dst, perm)
if err != nil {
return err
}
return nil
}
func SaveFile(file *multipart.FileHeader, dst string) error {
src, err := file.Open()
if err != nil {
return err
}
defer src.Close()
out, err := os.Create(dst)
if err != nil {
return err
}
defer out.Close()
_, err = io.Copy(out, src)
return err
}

13
pkg/util/md5.go Normal file
View File

@ -0,0 +1,13 @@
package util
import (
"crypto/md5"
"encoding/hex"
)
func EncodeMD5(value string) string {
m := md5.New()
m.Write([]byte(value))
return hex.EncodeToString(m.Sum(nil))
}