app
This commit is contained in:
parent
866e90b251
commit
f1ab91c2a4
49
pkg/app/app.go
Normal file
49
pkg/app/app.go
Normal 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
56
pkg/app/form.go
Normal 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
37
pkg/app/pagination.go
Normal 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
29
pkg/convert/convert.go
Normal 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
|
||||
}
|
87
pkg/errorcode/common_code.go
Normal file
87
pkg/errorcode/common_code.go
Normal 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
|
||||
}
|
10
pkg/errorcode/module_code.go
Normal file
10
pkg/errorcode/module_code.go
Normal 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
165
pkg/logger/logger.go
Normal 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
44
pkg/setting/section.go
Normal 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
20
pkg/setting/setting.go
Normal 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
101
pkg/upload/file.go
Normal 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
13
pkg/util/md5.go
Normal 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))
|
||||
}
|
Loading…
Reference in New Issue
Block a user