init
This commit is contained in:
commit
866e90b251
29
configs/config.yaml
Normal file
29
configs/config.yaml
Normal file
|
@ -0,0 +1,29 @@
|
||||||
|
Server:
|
||||||
|
RunMode: debug
|
||||||
|
HttpPort: 8080
|
||||||
|
ReadTimeout: 60
|
||||||
|
WriteTimeout: 60
|
||||||
|
App:
|
||||||
|
DefaultPageSize: 10
|
||||||
|
MaxPageSize: 100
|
||||||
|
LogSavePath: storage/logs
|
||||||
|
LogFileName: app
|
||||||
|
LogFileExt: .log
|
||||||
|
UploadSavePath: storage/uploads
|
||||||
|
UploadServerUrl: "http://127.0.0.1:8080/static"
|
||||||
|
UploadImageMaxSize: 5 # MB
|
||||||
|
UploadImageAllowExts:
|
||||||
|
- .jpg
|
||||||
|
- .jpeg
|
||||||
|
- .png
|
||||||
|
Database:
|
||||||
|
DBType: mysql
|
||||||
|
Username: xing # 填写你的数据库账号
|
||||||
|
Password: xing # 填写你的数据库密码
|
||||||
|
Host: 127.0.0.1:3306
|
||||||
|
DBName: tt
|
||||||
|
TablePrefix: blog_
|
||||||
|
Charset: utf8mb4
|
||||||
|
ParseTime: True
|
||||||
|
MaxIdleConns: 10
|
||||||
|
MaxOpenConns: 30
|
422
docs/docs.go
Normal file
422
docs/docs.go
Normal file
|
@ -0,0 +1,422 @@
|
||||||
|
// GENERATED BY THE COMMAND ABOVE; DO NOT EDIT
|
||||||
|
// This file was generated by swaggo/swag
|
||||||
|
|
||||||
|
package docs
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"encoding/json"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/alecthomas/template"
|
||||||
|
"github.com/swaggo/swag"
|
||||||
|
)
|
||||||
|
|
||||||
|
var doc = `{
|
||||||
|
"schemes": {{ marshal .Schemes }},
|
||||||
|
"swagger": "2.0",
|
||||||
|
"info": {
|
||||||
|
"description": "{{.Description}}",
|
||||||
|
"title": "{{.Title}}",
|
||||||
|
"termsOfService": "https://github.com/go-programming-tour-book",
|
||||||
|
"contact": {},
|
||||||
|
"version": "{{.Version}}"
|
||||||
|
},
|
||||||
|
"host": "{{.Host}}",
|
||||||
|
"basePath": "{{.BasePath}}",
|
||||||
|
"paths": {
|
||||||
|
"/api/v1/tags": {
|
||||||
|
"get": {
|
||||||
|
"produces": [
|
||||||
|
"application/json"
|
||||||
|
],
|
||||||
|
"summary": "获取多个标签",
|
||||||
|
"parameters": [
|
||||||
|
{
|
||||||
|
"maxLength": 100,
|
||||||
|
"type": "string",
|
||||||
|
"description": "标签名称",
|
||||||
|
"name": "name",
|
||||||
|
"in": "query"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"enum": [
|
||||||
|
0,
|
||||||
|
1
|
||||||
|
],
|
||||||
|
"type": "integer",
|
||||||
|
"default": 1,
|
||||||
|
"description": "状态",
|
||||||
|
"name": "state",
|
||||||
|
"in": "query"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "integer",
|
||||||
|
"default": 1,
|
||||||
|
"description": "页码",
|
||||||
|
"name": "page",
|
||||||
|
"in": "query"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "integer",
|
||||||
|
"default": 10,
|
||||||
|
"description": "每页数量",
|
||||||
|
"name": "page_size",
|
||||||
|
"in": "query"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"responses": {
|
||||||
|
"200": {
|
||||||
|
"description": "成功",
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/definitions/model.TagSwagger"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"400": {
|
||||||
|
"description": "请求错误",
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/definitions/errorcode.Error"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"500": {
|
||||||
|
"description": "内部错误",
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/definitions/errorcode.Error"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"post": {
|
||||||
|
"produces": [
|
||||||
|
"application/json"
|
||||||
|
],
|
||||||
|
"summary": "新增标签",
|
||||||
|
"parameters": [
|
||||||
|
{
|
||||||
|
"maxLength": 100,
|
||||||
|
"minLength": 3,
|
||||||
|
"type": "string",
|
||||||
|
"description": "标签名称",
|
||||||
|
"name": "name",
|
||||||
|
"in": "formData",
|
||||||
|
"required": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"enum": [
|
||||||
|
0,
|
||||||
|
1
|
||||||
|
],
|
||||||
|
"type": "integer",
|
||||||
|
"default": 1,
|
||||||
|
"description": "状态",
|
||||||
|
"name": "state",
|
||||||
|
"in": "formData"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"maxLength": 100,
|
||||||
|
"minLength": 3,
|
||||||
|
"type": "string",
|
||||||
|
"description": "创建者",
|
||||||
|
"name": "created_by",
|
||||||
|
"in": "formData",
|
||||||
|
"required": true
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"responses": {
|
||||||
|
"200": {
|
||||||
|
"description": "成功",
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/definitions/model.TagSwagger"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"400": {
|
||||||
|
"description": "请求错误",
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/definitions/errorcode.Error"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"500": {
|
||||||
|
"description": "内部错误",
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/definitions/errorcode.Error"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"/api/v1/tags/{id}": {
|
||||||
|
"put": {
|
||||||
|
"produces": [
|
||||||
|
"application/json"
|
||||||
|
],
|
||||||
|
"summary": "更新标签",
|
||||||
|
"parameters": [
|
||||||
|
{
|
||||||
|
"type": "integer",
|
||||||
|
"description": "标签 ID",
|
||||||
|
"name": "id",
|
||||||
|
"in": "path",
|
||||||
|
"required": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"minLength": 3,
|
||||||
|
"type": "string",
|
||||||
|
"description": "标签名称",
|
||||||
|
"name": "name",
|
||||||
|
"in": "formData"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"enum": [
|
||||||
|
0,
|
||||||
|
1
|
||||||
|
],
|
||||||
|
"type": "integer",
|
||||||
|
"default": 1,
|
||||||
|
"description": "状态",
|
||||||
|
"name": "state",
|
||||||
|
"in": "formData"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"maxLength": 100,
|
||||||
|
"minLength": 3,
|
||||||
|
"type": "string",
|
||||||
|
"description": "修改者",
|
||||||
|
"name": "modified_by",
|
||||||
|
"in": "formData",
|
||||||
|
"required": true
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"responses": {
|
||||||
|
"200": {
|
||||||
|
"description": "成功",
|
||||||
|
"schema": {
|
||||||
|
"type": "array",
|
||||||
|
"items": {
|
||||||
|
"$ref": "#/definitions/model.TagSwagger"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"400": {
|
||||||
|
"description": "请求错误",
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/definitions/errorcode.Error"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"500": {
|
||||||
|
"description": "内部错误",
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/definitions/errorcode.Error"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"delete": {
|
||||||
|
"produces": [
|
||||||
|
"application/json"
|
||||||
|
],
|
||||||
|
"summary": "删除标签",
|
||||||
|
"parameters": [
|
||||||
|
{
|
||||||
|
"type": "integer",
|
||||||
|
"description": "标签 ID",
|
||||||
|
"name": "id",
|
||||||
|
"in": "path",
|
||||||
|
"required": true
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"responses": {
|
||||||
|
"200": {
|
||||||
|
"description": "成功",
|
||||||
|
"schema": {
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"400": {
|
||||||
|
"description": "请求错误",
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/definitions/errorcode.Error"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"500": {
|
||||||
|
"description": "内部错误",
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/definitions/errorcode.Error"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"/upload/file": {
|
||||||
|
"post": {
|
||||||
|
"produces": [
|
||||||
|
"application/json"
|
||||||
|
],
|
||||||
|
"summary": "上传图片",
|
||||||
|
"parameters": [
|
||||||
|
{
|
||||||
|
"enum": [
|
||||||
|
1,
|
||||||
|
2,
|
||||||
|
3
|
||||||
|
],
|
||||||
|
"type": "integer",
|
||||||
|
"description": "文件类型1图片",
|
||||||
|
"name": "type",
|
||||||
|
"in": "formData",
|
||||||
|
"required": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "file",
|
||||||
|
"description": "文件",
|
||||||
|
"name": "state",
|
||||||
|
"in": "formData",
|
||||||
|
"required": true
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"responses": {
|
||||||
|
"200": {
|
||||||
|
"description": "成功",
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/definitions/service.FileInfo"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"400": {
|
||||||
|
"description": "请求错误",
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/definitions/errorcode.Error"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"500": {
|
||||||
|
"description": "内部错误",
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/definitions/errorcode.Error"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"definitions": {
|
||||||
|
"app.Pager": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"page": {
|
||||||
|
"type": "integer"
|
||||||
|
},
|
||||||
|
"page_size": {
|
||||||
|
"type": "integer"
|
||||||
|
},
|
||||||
|
"total_rows": {
|
||||||
|
"type": "integer"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"errorcode.Error": {
|
||||||
|
"type": "object"
|
||||||
|
},
|
||||||
|
"model.Tag": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"created_by": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"created_on": {
|
||||||
|
"type": "integer"
|
||||||
|
},
|
||||||
|
"deleted_on": {
|
||||||
|
"type": "integer"
|
||||||
|
},
|
||||||
|
"id": {
|
||||||
|
"type": "integer"
|
||||||
|
},
|
||||||
|
"is_del": {
|
||||||
|
"type": "integer"
|
||||||
|
},
|
||||||
|
"modified_by": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"modified_on": {
|
||||||
|
"type": "integer"
|
||||||
|
},
|
||||||
|
"name": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"state": {
|
||||||
|
"type": "integer"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"model.TagSwagger": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"list": {
|
||||||
|
"type": "array",
|
||||||
|
"items": {
|
||||||
|
"$ref": "#/definitions/model.Tag"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"pager": {
|
||||||
|
"$ref": "#/definitions/app.Pager"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"service.FileInfo": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"accessUrl": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"name": {
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}`
|
||||||
|
|
||||||
|
type swaggerInfo struct {
|
||||||
|
Version string
|
||||||
|
Host string
|
||||||
|
BasePath string
|
||||||
|
Schemes []string
|
||||||
|
Title string
|
||||||
|
Description string
|
||||||
|
}
|
||||||
|
|
||||||
|
// SwaggerInfo holds exported Swagger Info so clients can modify it
|
||||||
|
var SwaggerInfo = swaggerInfo{
|
||||||
|
Version: "1.0",
|
||||||
|
Host: "",
|
||||||
|
BasePath: "",
|
||||||
|
Schemes: []string{},
|
||||||
|
Title: "博客系统",
|
||||||
|
Description: "Go 语言编程之旅:一起用 Go 做项目",
|
||||||
|
}
|
||||||
|
|
||||||
|
type s struct{}
|
||||||
|
|
||||||
|
func (s *s) ReadDoc() string {
|
||||||
|
sInfo := SwaggerInfo
|
||||||
|
sInfo.Description = strings.Replace(sInfo.Description, "\n", "\\n", -1)
|
||||||
|
|
||||||
|
t, err := template.New("swagger_info").Funcs(template.FuncMap{
|
||||||
|
"marshal": func(v interface{}) string {
|
||||||
|
a, _ := json.Marshal(v)
|
||||||
|
return string(a)
|
||||||
|
},
|
||||||
|
}).Parse(doc)
|
||||||
|
if err != nil {
|
||||||
|
return doc
|
||||||
|
}
|
||||||
|
|
||||||
|
var tpl bytes.Buffer
|
||||||
|
if err := t.Execute(&tpl, sInfo); err != nil {
|
||||||
|
return doc
|
||||||
|
}
|
||||||
|
|
||||||
|
return tpl.String()
|
||||||
|
}
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
swag.Register(swag.Name, &s{})
|
||||||
|
}
|
358
docs/swagger.json
Normal file
358
docs/swagger.json
Normal file
|
@ -0,0 +1,358 @@
|
||||||
|
{
|
||||||
|
"swagger": "2.0",
|
||||||
|
"info": {
|
||||||
|
"description": "Go 语言编程之旅:一起用 Go 做项目",
|
||||||
|
"title": "博客系统",
|
||||||
|
"termsOfService": "https://github.com/go-programming-tour-book",
|
||||||
|
"contact": {},
|
||||||
|
"version": "1.0"
|
||||||
|
},
|
||||||
|
"paths": {
|
||||||
|
"/api/v1/tags": {
|
||||||
|
"get": {
|
||||||
|
"produces": [
|
||||||
|
"application/json"
|
||||||
|
],
|
||||||
|
"summary": "获取多个标签",
|
||||||
|
"parameters": [
|
||||||
|
{
|
||||||
|
"maxLength": 100,
|
||||||
|
"type": "string",
|
||||||
|
"description": "标签名称",
|
||||||
|
"name": "name",
|
||||||
|
"in": "query"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"enum": [
|
||||||
|
0,
|
||||||
|
1
|
||||||
|
],
|
||||||
|
"type": "integer",
|
||||||
|
"default": 1,
|
||||||
|
"description": "状态",
|
||||||
|
"name": "state",
|
||||||
|
"in": "query"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "integer",
|
||||||
|
"default": 1,
|
||||||
|
"description": "页码",
|
||||||
|
"name": "page",
|
||||||
|
"in": "query"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "integer",
|
||||||
|
"default": 10,
|
||||||
|
"description": "每页数量",
|
||||||
|
"name": "page_size",
|
||||||
|
"in": "query"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"responses": {
|
||||||
|
"200": {
|
||||||
|
"description": "成功",
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/definitions/model.TagSwagger"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"400": {
|
||||||
|
"description": "请求错误",
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/definitions/errorcode.Error"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"500": {
|
||||||
|
"description": "内部错误",
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/definitions/errorcode.Error"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"post": {
|
||||||
|
"produces": [
|
||||||
|
"application/json"
|
||||||
|
],
|
||||||
|
"summary": "新增标签",
|
||||||
|
"parameters": [
|
||||||
|
{
|
||||||
|
"maxLength": 100,
|
||||||
|
"minLength": 3,
|
||||||
|
"type": "string",
|
||||||
|
"description": "标签名称",
|
||||||
|
"name": "name",
|
||||||
|
"in": "formData",
|
||||||
|
"required": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"enum": [
|
||||||
|
0,
|
||||||
|
1
|
||||||
|
],
|
||||||
|
"type": "integer",
|
||||||
|
"default": 1,
|
||||||
|
"description": "状态",
|
||||||
|
"name": "state",
|
||||||
|
"in": "formData"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"maxLength": 100,
|
||||||
|
"minLength": 3,
|
||||||
|
"type": "string",
|
||||||
|
"description": "创建者",
|
||||||
|
"name": "created_by",
|
||||||
|
"in": "formData",
|
||||||
|
"required": true
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"responses": {
|
||||||
|
"200": {
|
||||||
|
"description": "成功",
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/definitions/model.TagSwagger"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"400": {
|
||||||
|
"description": "请求错误",
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/definitions/errorcode.Error"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"500": {
|
||||||
|
"description": "内部错误",
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/definitions/errorcode.Error"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"/api/v1/tags/{id}": {
|
||||||
|
"put": {
|
||||||
|
"produces": [
|
||||||
|
"application/json"
|
||||||
|
],
|
||||||
|
"summary": "更新标签",
|
||||||
|
"parameters": [
|
||||||
|
{
|
||||||
|
"type": "integer",
|
||||||
|
"description": "标签 ID",
|
||||||
|
"name": "id",
|
||||||
|
"in": "path",
|
||||||
|
"required": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"minLength": 3,
|
||||||
|
"type": "string",
|
||||||
|
"description": "标签名称",
|
||||||
|
"name": "name",
|
||||||
|
"in": "formData"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"enum": [
|
||||||
|
0,
|
||||||
|
1
|
||||||
|
],
|
||||||
|
"type": "integer",
|
||||||
|
"default": 1,
|
||||||
|
"description": "状态",
|
||||||
|
"name": "state",
|
||||||
|
"in": "formData"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"maxLength": 100,
|
||||||
|
"minLength": 3,
|
||||||
|
"type": "string",
|
||||||
|
"description": "修改者",
|
||||||
|
"name": "modified_by",
|
||||||
|
"in": "formData",
|
||||||
|
"required": true
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"responses": {
|
||||||
|
"200": {
|
||||||
|
"description": "成功",
|
||||||
|
"schema": {
|
||||||
|
"type": "array",
|
||||||
|
"items": {
|
||||||
|
"$ref": "#/definitions/model.TagSwagger"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"400": {
|
||||||
|
"description": "请求错误",
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/definitions/errorcode.Error"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"500": {
|
||||||
|
"description": "内部错误",
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/definitions/errorcode.Error"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"delete": {
|
||||||
|
"produces": [
|
||||||
|
"application/json"
|
||||||
|
],
|
||||||
|
"summary": "删除标签",
|
||||||
|
"parameters": [
|
||||||
|
{
|
||||||
|
"type": "integer",
|
||||||
|
"description": "标签 ID",
|
||||||
|
"name": "id",
|
||||||
|
"in": "path",
|
||||||
|
"required": true
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"responses": {
|
||||||
|
"200": {
|
||||||
|
"description": "成功",
|
||||||
|
"schema": {
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"400": {
|
||||||
|
"description": "请求错误",
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/definitions/errorcode.Error"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"500": {
|
||||||
|
"description": "内部错误",
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/definitions/errorcode.Error"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"/upload/file": {
|
||||||
|
"post": {
|
||||||
|
"produces": [
|
||||||
|
"application/json"
|
||||||
|
],
|
||||||
|
"summary": "上传图片",
|
||||||
|
"parameters": [
|
||||||
|
{
|
||||||
|
"enum": [
|
||||||
|
1,
|
||||||
|
2,
|
||||||
|
3
|
||||||
|
],
|
||||||
|
"type": "integer",
|
||||||
|
"description": "文件类型1图片",
|
||||||
|
"name": "type",
|
||||||
|
"in": "formData",
|
||||||
|
"required": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "file",
|
||||||
|
"description": "文件",
|
||||||
|
"name": "state",
|
||||||
|
"in": "formData",
|
||||||
|
"required": true
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"responses": {
|
||||||
|
"200": {
|
||||||
|
"description": "成功",
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/definitions/service.FileInfo"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"400": {
|
||||||
|
"description": "请求错误",
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/definitions/errorcode.Error"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"500": {
|
||||||
|
"description": "内部错误",
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/definitions/errorcode.Error"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"definitions": {
|
||||||
|
"app.Pager": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"page": {
|
||||||
|
"type": "integer"
|
||||||
|
},
|
||||||
|
"page_size": {
|
||||||
|
"type": "integer"
|
||||||
|
},
|
||||||
|
"total_rows": {
|
||||||
|
"type": "integer"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"errorcode.Error": {
|
||||||
|
"type": "object"
|
||||||
|
},
|
||||||
|
"model.Tag": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"created_by": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"created_on": {
|
||||||
|
"type": "integer"
|
||||||
|
},
|
||||||
|
"deleted_on": {
|
||||||
|
"type": "integer"
|
||||||
|
},
|
||||||
|
"id": {
|
||||||
|
"type": "integer"
|
||||||
|
},
|
||||||
|
"is_del": {
|
||||||
|
"type": "integer"
|
||||||
|
},
|
||||||
|
"modified_by": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"modified_on": {
|
||||||
|
"type": "integer"
|
||||||
|
},
|
||||||
|
"name": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"state": {
|
||||||
|
"type": "integer"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"model.TagSwagger": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"list": {
|
||||||
|
"type": "array",
|
||||||
|
"items": {
|
||||||
|
"$ref": "#/definitions/model.Tag"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"pager": {
|
||||||
|
"$ref": "#/definitions/app.Pager"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"service.FileInfo": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"accessUrl": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"name": {
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
241
docs/swagger.yaml
Normal file
241
docs/swagger.yaml
Normal file
|
@ -0,0 +1,241 @@
|
||||||
|
definitions:
|
||||||
|
app.Pager:
|
||||||
|
properties:
|
||||||
|
page:
|
||||||
|
type: integer
|
||||||
|
page_size:
|
||||||
|
type: integer
|
||||||
|
total_rows:
|
||||||
|
type: integer
|
||||||
|
type: object
|
||||||
|
errorcode.Error:
|
||||||
|
type: object
|
||||||
|
model.Tag:
|
||||||
|
properties:
|
||||||
|
created_by:
|
||||||
|
type: string
|
||||||
|
created_on:
|
||||||
|
type: integer
|
||||||
|
deleted_on:
|
||||||
|
type: integer
|
||||||
|
id:
|
||||||
|
type: integer
|
||||||
|
is_del:
|
||||||
|
type: integer
|
||||||
|
modified_by:
|
||||||
|
type: string
|
||||||
|
modified_on:
|
||||||
|
type: integer
|
||||||
|
name:
|
||||||
|
type: string
|
||||||
|
state:
|
||||||
|
type: integer
|
||||||
|
type: object
|
||||||
|
model.TagSwagger:
|
||||||
|
properties:
|
||||||
|
list:
|
||||||
|
items:
|
||||||
|
$ref: '#/definitions/model.Tag'
|
||||||
|
type: array
|
||||||
|
pager:
|
||||||
|
$ref: '#/definitions/app.Pager'
|
||||||
|
type: object
|
||||||
|
service.FileInfo:
|
||||||
|
properties:
|
||||||
|
accessUrl:
|
||||||
|
type: string
|
||||||
|
name:
|
||||||
|
type: string
|
||||||
|
type: object
|
||||||
|
info:
|
||||||
|
contact: { }
|
||||||
|
description: Go 语言编程之旅:一起用 Go 做项目
|
||||||
|
termsOfService: https://github.com/go-programming-tour-book
|
||||||
|
title: 博客系统
|
||||||
|
version: "1.0"
|
||||||
|
paths:
|
||||||
|
/api/v1/tags:
|
||||||
|
get:
|
||||||
|
parameters:
|
||||||
|
- description: 标签名称
|
||||||
|
in: query
|
||||||
|
maxLength: 100
|
||||||
|
name: name
|
||||||
|
type: string
|
||||||
|
- default: 1
|
||||||
|
description: 状态
|
||||||
|
enum:
|
||||||
|
- 0
|
||||||
|
- 1
|
||||||
|
in: query
|
||||||
|
name: state
|
||||||
|
type: integer
|
||||||
|
- default: 1
|
||||||
|
description: 页码
|
||||||
|
in: query
|
||||||
|
name: page
|
||||||
|
type: integer
|
||||||
|
- default: 10
|
||||||
|
description: 每页数量
|
||||||
|
in: query
|
||||||
|
name: page_size
|
||||||
|
type: integer
|
||||||
|
produces:
|
||||||
|
- application/json
|
||||||
|
responses:
|
||||||
|
"200":
|
||||||
|
description: 成功
|
||||||
|
schema:
|
||||||
|
$ref: '#/definitions/model.TagSwagger'
|
||||||
|
"400":
|
||||||
|
description: 请求错误
|
||||||
|
schema:
|
||||||
|
$ref: '#/definitions/errorcode.Error'
|
||||||
|
"500":
|
||||||
|
description: 内部错误
|
||||||
|
schema:
|
||||||
|
$ref: '#/definitions/errorcode.Error'
|
||||||
|
summary: 获取多个标签
|
||||||
|
post:
|
||||||
|
parameters:
|
||||||
|
- description: 标签名称
|
||||||
|
in: formData
|
||||||
|
maxLength: 100
|
||||||
|
minLength: 3
|
||||||
|
name: name
|
||||||
|
required: true
|
||||||
|
type: string
|
||||||
|
- default: 1
|
||||||
|
description: 状态
|
||||||
|
enum:
|
||||||
|
- 0
|
||||||
|
- 1
|
||||||
|
in: formData
|
||||||
|
name: state
|
||||||
|
type: integer
|
||||||
|
- description: 创建者
|
||||||
|
in: formData
|
||||||
|
maxLength: 100
|
||||||
|
minLength: 3
|
||||||
|
name: created_by
|
||||||
|
required: true
|
||||||
|
type: string
|
||||||
|
produces:
|
||||||
|
- application/json
|
||||||
|
responses:
|
||||||
|
"200":
|
||||||
|
description: 成功
|
||||||
|
schema:
|
||||||
|
$ref: '#/definitions/model.TagSwagger'
|
||||||
|
"400":
|
||||||
|
description: 请求错误
|
||||||
|
schema:
|
||||||
|
$ref: '#/definitions/errorcode.Error'
|
||||||
|
"500":
|
||||||
|
description: 内部错误
|
||||||
|
schema:
|
||||||
|
$ref: '#/definitions/errorcode.Error'
|
||||||
|
summary: 新增标签
|
||||||
|
/api/v1/tags/{id}:
|
||||||
|
delete:
|
||||||
|
parameters:
|
||||||
|
- description: 标签 ID
|
||||||
|
in: path
|
||||||
|
name: id
|
||||||
|
required: true
|
||||||
|
type: integer
|
||||||
|
produces:
|
||||||
|
- application/json
|
||||||
|
responses:
|
||||||
|
"200":
|
||||||
|
description: 成功
|
||||||
|
schema:
|
||||||
|
type: string
|
||||||
|
"400":
|
||||||
|
description: 请求错误
|
||||||
|
schema:
|
||||||
|
$ref: '#/definitions/errorcode.Error'
|
||||||
|
"500":
|
||||||
|
description: 内部错误
|
||||||
|
schema:
|
||||||
|
$ref: '#/definitions/errorcode.Error'
|
||||||
|
summary: 删除标签
|
||||||
|
put:
|
||||||
|
parameters:
|
||||||
|
- description: 标签 ID
|
||||||
|
in: path
|
||||||
|
name: id
|
||||||
|
required: true
|
||||||
|
type: integer
|
||||||
|
- description: 标签名称
|
||||||
|
in: formData
|
||||||
|
minLength: 3
|
||||||
|
name: name
|
||||||
|
type: string
|
||||||
|
- default: 1
|
||||||
|
description: 状态
|
||||||
|
enum:
|
||||||
|
- 0
|
||||||
|
- 1
|
||||||
|
in: formData
|
||||||
|
name: state
|
||||||
|
type: integer
|
||||||
|
- description: 修改者
|
||||||
|
in: formData
|
||||||
|
maxLength: 100
|
||||||
|
minLength: 3
|
||||||
|
name: modified_by
|
||||||
|
required: true
|
||||||
|
type: string
|
||||||
|
produces:
|
||||||
|
- application/json
|
||||||
|
responses:
|
||||||
|
"200":
|
||||||
|
description: 成功
|
||||||
|
schema:
|
||||||
|
items:
|
||||||
|
$ref: '#/definitions/model.TagSwagger'
|
||||||
|
type: array
|
||||||
|
"400":
|
||||||
|
description: 请求错误
|
||||||
|
schema:
|
||||||
|
$ref: '#/definitions/errorcode.Error'
|
||||||
|
"500":
|
||||||
|
description: 内部错误
|
||||||
|
schema:
|
||||||
|
$ref: '#/definitions/errorcode.Error'
|
||||||
|
summary: 更新标签
|
||||||
|
/upload/file:
|
||||||
|
post:
|
||||||
|
parameters:
|
||||||
|
- description: 文件类型1图片
|
||||||
|
enum:
|
||||||
|
- 1
|
||||||
|
- 2
|
||||||
|
- 3
|
||||||
|
in: formData
|
||||||
|
name: type
|
||||||
|
required: true
|
||||||
|
type: integer
|
||||||
|
- description: 文件
|
||||||
|
in: formData
|
||||||
|
name: state
|
||||||
|
required: true
|
||||||
|
type: file
|
||||||
|
produces:
|
||||||
|
- application/json
|
||||||
|
responses:
|
||||||
|
"200":
|
||||||
|
description: 成功
|
||||||
|
schema:
|
||||||
|
$ref: '#/definitions/service.FileInfo'
|
||||||
|
"400":
|
||||||
|
description: 请求错误
|
||||||
|
schema:
|
||||||
|
$ref: '#/definitions/errorcode.Error'
|
||||||
|
"500":
|
||||||
|
description: 内部错误
|
||||||
|
schema:
|
||||||
|
$ref: '#/definitions/errorcode.Error'
|
||||||
|
summary: 上传图片
|
||||||
|
swagger: "2.0"
|
7
global/db.go
Normal file
7
global/db.go
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
package global
|
||||||
|
|
||||||
|
import "github.com/jinzhu/gorm"
|
||||||
|
|
||||||
|
var (
|
||||||
|
DBEngine *gorm.DB
|
||||||
|
)
|
13
global/setting.go
Normal file
13
global/setting.go
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
package global
|
||||||
|
|
||||||
|
import (
|
||||||
|
"blog/pkg/logger"
|
||||||
|
"blog/pkg/setting"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
ServerSetting *setting.ServerSettingS
|
||||||
|
AppSetting *setting.AppSettingS
|
||||||
|
DatabaseSetting *setting.DatabaseSettingS
|
||||||
|
Logger *logger.Logger
|
||||||
|
)
|
46
go.mod
Normal file
46
go.mod
Normal file
|
@ -0,0 +1,46 @@
|
||||||
|
module blog
|
||||||
|
|
||||||
|
go 1.16
|
||||||
|
|
||||||
|
require (
|
||||||
|
github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751 // indirect
|
||||||
|
github.com/cpuguy83/go-md2man/v2 v2.0.0 // indirect
|
||||||
|
github.com/fsnotify/fsnotify v1.4.9 // indirect
|
||||||
|
github.com/gin-gonic/gin v1.7.2
|
||||||
|
github.com/go-openapi/spec v0.20.3 // indirect
|
||||||
|
github.com/go-openapi/swag v0.19.15 // indirect
|
||||||
|
github.com/go-playground/locales v0.13.0 // indirect
|
||||||
|
github.com/go-playground/universal-translator v0.17.0 // indirect
|
||||||
|
github.com/go-playground/validator v9.31.0+incompatible // indirect
|
||||||
|
github.com/go-playground/validator/v10 v10.6.1 // indirect
|
||||||
|
github.com/go-sql-driver/mysql v1.6.0 // indirect
|
||||||
|
github.com/golang/protobuf v1.5.2 // indirect
|
||||||
|
github.com/jinzhu/gorm v1.9.16
|
||||||
|
github.com/json-iterator/go v1.1.11 // indirect
|
||||||
|
github.com/leodido/go-urn v1.2.1 // indirect
|
||||||
|
github.com/magiconair/properties v1.8.5 // indirect
|
||||||
|
github.com/mailru/easyjson v0.7.7 // indirect
|
||||||
|
github.com/mattn/go-isatty v0.0.13 // indirect
|
||||||
|
github.com/mitchellh/mapstructure v1.4.1 // indirect
|
||||||
|
github.com/pelletier/go-toml v1.9.1 // indirect
|
||||||
|
github.com/russross/blackfriday/v2 v2.1.0 // indirect
|
||||||
|
github.com/spf13/afero v1.6.0 // indirect
|
||||||
|
github.com/spf13/cast v1.3.1 // indirect
|
||||||
|
github.com/spf13/jwalterweatherman v1.1.0 // indirect
|
||||||
|
github.com/spf13/pflag v1.0.5 // indirect
|
||||||
|
github.com/spf13/viper v1.7.1
|
||||||
|
github.com/swaggo/files v0.0.0-20190704085106-630677cd5c14 // indirect
|
||||||
|
github.com/swaggo/gin-swagger v1.3.0 // indirect
|
||||||
|
github.com/swaggo/swag v1.7.0 // indirect
|
||||||
|
github.com/ugorji/go v1.2.6 // indirect
|
||||||
|
github.com/urfave/cli v1.20.0 // indirect
|
||||||
|
golang.org/x/crypto v0.0.0-20210513164829-c07d793c2f9a // indirect
|
||||||
|
golang.org/x/net v0.0.0-20210525063256-abc453219eb5 // indirect
|
||||||
|
golang.org/x/sys v0.0.0-20210525143221-35b2ab0089ea // indirect
|
||||||
|
golang.org/x/text v0.3.6 // indirect
|
||||||
|
golang.org/x/tools v0.1.2 // indirect
|
||||||
|
gopkg.in/go-playground/validator.v9 v9.31.0 // indirect
|
||||||
|
gopkg.in/ini.v1 v1.62.0 // indirect
|
||||||
|
gopkg.in/natefinch/lumberjack.v2 v2.0.0 // indirect
|
||||||
|
gopkg.in/yaml.v2 v2.4.0 // indirect
|
||||||
|
)
|
11
internal/dao/dao.go
Normal file
11
internal/dao/dao.go
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
package dao
|
||||||
|
|
||||||
|
import "github.com/jinzhu/gorm"
|
||||||
|
|
||||||
|
type Dao struct {
|
||||||
|
engine *gorm.DB
|
||||||
|
}
|
||||||
|
|
||||||
|
func New(engine *gorm.DB) *Dao {
|
||||||
|
return &Dao{engine: engine}
|
||||||
|
}
|
47
internal/dao/tag.go
Normal file
47
internal/dao/tag.go
Normal file
|
@ -0,0 +1,47 @@
|
||||||
|
package dao
|
||||||
|
|
||||||
|
import (
|
||||||
|
"blog/internal/model"
|
||||||
|
"blog/pkg/app"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (d *Dao) CountTag(name string, state uint8) (int, error) {
|
||||||
|
tag := model.Tag{Name: name, State: state}
|
||||||
|
return tag.Count(d.engine)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *Dao) GetTagList(name string, state uint8, page, pageSize int) ([]*model.Tag, error) {
|
||||||
|
tag := model.Tag{Name: name, State: state}
|
||||||
|
pageOffset := app.GetPageOffset(page, pageSize)
|
||||||
|
return tag.List(d.engine, pageOffset, pageSize)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *Dao) CreateTag(name string, state uint8, createdBy string) error {
|
||||||
|
tag := model.Tag{
|
||||||
|
Name: name,
|
||||||
|
State: state,
|
||||||
|
Model: &model.Model{CreatedBy: createdBy},
|
||||||
|
}
|
||||||
|
|
||||||
|
return tag.Create(d.engine)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *Dao) UpdateTag(id uint32, name string, state *uint8, modifiedBy string) error {
|
||||||
|
tag := model.Tag{
|
||||||
|
Model: &model.Model{ID: id},
|
||||||
|
}
|
||||||
|
values := map[string]interface{}{
|
||||||
|
"state": *state,
|
||||||
|
"modified_by": modifiedBy,
|
||||||
|
}
|
||||||
|
if name != "" {
|
||||||
|
values["name"] = name
|
||||||
|
}
|
||||||
|
|
||||||
|
return tag.Update(d.engine, values)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *Dao) DeleteTag(id uint32) error {
|
||||||
|
tag := model.Tag{Model: &model.Model{ID: id}}
|
||||||
|
return tag.Delete(d.engine)
|
||||||
|
}
|
38
internal/middleware/translations.go
Normal file
38
internal/middleware/translations.go
Normal file
|
@ -0,0 +1,38 @@
|
||||||
|
package middleware
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
|
"github.com/gin-gonic/gin/binding"
|
||||||
|
"github.com/go-playground/locales/en"
|
||||||
|
"github.com/go-playground/locales/zh"
|
||||||
|
"github.com/go-playground/locales/zh_Hant_TW"
|
||||||
|
ut "github.com/go-playground/universal-translator"
|
||||||
|
"github.com/go-playground/validator/v10"
|
||||||
|
enTranslations "github.com/go-playground/validator/v10/translations/en"
|
||||||
|
zhTranslations "github.com/go-playground/validator/v10/translations/zh"
|
||||||
|
)
|
||||||
|
|
||||||
|
func Translations() gin.HandlerFunc {
|
||||||
|
return func(c *gin.Context) {
|
||||||
|
uni := ut.New(en.New(), zh.New(), zh_Hant_TW.New())
|
||||||
|
locale := c.GetHeader("locale")
|
||||||
|
trans, _ := uni.GetTranslator(locale)
|
||||||
|
v, ok := binding.Validator.Engine().(*validator.Validate)
|
||||||
|
if ok {
|
||||||
|
switch locale {
|
||||||
|
case "zh":
|
||||||
|
_ = zhTranslations.RegisterDefaultTranslations(v, trans)
|
||||||
|
break
|
||||||
|
case "en":
|
||||||
|
_ = enTranslations.RegisterDefaultTranslations(v, trans)
|
||||||
|
break
|
||||||
|
default:
|
||||||
|
_ = zhTranslations.RegisterDefaultTranslations(v, trans)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
c.Set("trans", trans)
|
||||||
|
}
|
||||||
|
|
||||||
|
c.Next()
|
||||||
|
}
|
||||||
|
}
|
21
internal/model/article.go
Normal file
21
internal/model/article.go
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
package model
|
||||||
|
|
||||||
|
import "blog/pkg/app"
|
||||||
|
|
||||||
|
type Article struct {
|
||||||
|
*Model
|
||||||
|
Title string `json:"title"`
|
||||||
|
Desc string `json:"desc"`
|
||||||
|
Content string `json:"content"`
|
||||||
|
CoverImageUrl string `json:"cover_image_url"`
|
||||||
|
State uint8 `json:"state"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a Article) TableName() string {
|
||||||
|
return "blog_article"
|
||||||
|
}
|
||||||
|
|
||||||
|
type ArticleSwagger struct {
|
||||||
|
List []*Article
|
||||||
|
Pager *app.Pager
|
||||||
|
}
|
11
internal/model/article_tag.go
Normal file
11
internal/model/article_tag.go
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
package model
|
||||||
|
|
||||||
|
type ArticleTag struct {
|
||||||
|
*Model
|
||||||
|
TagID uint32 `json:"tag_id"`
|
||||||
|
ArticleID uint32 `json:"article_id"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a ArticleTag) TableName() string {
|
||||||
|
return "blog_article_tag"
|
||||||
|
}
|
106
internal/model/model.go
Normal file
106
internal/model/model.go
Normal file
|
@ -0,0 +1,106 @@
|
||||||
|
package model
|
||||||
|
|
||||||
|
import (
|
||||||
|
"blog/global"
|
||||||
|
"blog/pkg/setting"
|
||||||
|
"fmt"
|
||||||
|
"github.com/jinzhu/gorm"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Model struct {
|
||||||
|
ID uint32 `gorm:"primary_key" json:"id"`
|
||||||
|
CreatedBy string `json:"created_by"`
|
||||||
|
ModifiedBy string `json:"modified_by"`
|
||||||
|
CreatedOn uint32 `json:"created_on"`
|
||||||
|
ModifiedOn uint32 `json:"modified_on"`
|
||||||
|
DeletedOn uint32 `json:"deleted_on"`
|
||||||
|
IsDel uint8 `json:"is_del"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewDBEngine(databaseSetting *setting.DatabaseSettingS) (*gorm.DB, error) {
|
||||||
|
db, err := gorm.Open(databaseSetting.DBType, fmt.Sprintf("%s:%s@tcp(%s)/%s?charset=%s&parseTime=%t&loc=Local",
|
||||||
|
databaseSetting.UserName,
|
||||||
|
databaseSetting.Password,
|
||||||
|
databaseSetting.Host,
|
||||||
|
databaseSetting.DBName,
|
||||||
|
databaseSetting.Charset,
|
||||||
|
databaseSetting.ParseTime,
|
||||||
|
))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if global.ServerSetting.RunMode == "debug" {
|
||||||
|
db.LogMode(true)
|
||||||
|
}
|
||||||
|
db.SingularTable(true)
|
||||||
|
db.DB().SetMaxIdleConns(databaseSetting.MaxIdleConns)
|
||||||
|
db.DB().SetMaxOpenConns(databaseSetting.MaxOpenConns)
|
||||||
|
db.Callback().Create().Replace("gorm:update_time_stamp", updateTimeStampForCreateCallback)
|
||||||
|
db.Callback().Update().Replace("gorm:update_time_stamp", updateTimeStampForUpdateCallback)
|
||||||
|
db.Callback().Delete().Replace("gorm:delete", deleteCallback)
|
||||||
|
return db, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func updateTimeStampForCreateCallback(scope *gorm.Scope) {
|
||||||
|
if !scope.HasError() {
|
||||||
|
nowTime := time.Now().Unix()
|
||||||
|
if createTimeField, ok := scope.FieldByName("CreatedOn"); ok {
|
||||||
|
if createTimeField.IsBlank {
|
||||||
|
_ = createTimeField.Set(nowTime)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if modifyTimeField, ok := scope.FieldByName("ModifiedOn"); ok {
|
||||||
|
if modifyTimeField.IsBlank {
|
||||||
|
_ = modifyTimeField.Set(nowTime)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func updateTimeStampForUpdateCallback(scope *gorm.Scope) {
|
||||||
|
if _, ok := scope.Get("gorm:update_column"); !ok {
|
||||||
|
_ = scope.SetColumn("ModifiedOn", time.Now().Unix())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func deleteCallback(scope *gorm.Scope) {
|
||||||
|
if !scope.HasError() {
|
||||||
|
var extraOption string
|
||||||
|
if str, ok := scope.Get("gorm:delete_option"); ok {
|
||||||
|
extraOption = fmt.Sprint(str)
|
||||||
|
}
|
||||||
|
|
||||||
|
deletedOnField, hasDeletedOnField := scope.FieldByName("DeletedOn")
|
||||||
|
isDelField, hasIsDelField := scope.FieldByName("IsDel")
|
||||||
|
if !scope.Search.Unscoped && hasDeletedOnField && hasIsDelField {
|
||||||
|
now := time.Now().Unix()
|
||||||
|
scope.Raw(fmt.Sprintf(
|
||||||
|
"UPDATE %v SET %v=%v,%v=%v%v%v",
|
||||||
|
scope.QuotedTableName(),
|
||||||
|
scope.Quote(deletedOnField.DBName),
|
||||||
|
scope.AddToVars(now),
|
||||||
|
scope.Quote(isDelField.DBName),
|
||||||
|
scope.AddToVars(1),
|
||||||
|
addExtraSpaceIfExist(scope.CombinedConditionSql()),
|
||||||
|
addExtraSpaceIfExist(extraOption),
|
||||||
|
)).Exec()
|
||||||
|
} else {
|
||||||
|
scope.Raw(fmt.Sprintf(
|
||||||
|
"DELETE FROM %v%v%v",
|
||||||
|
scope.QuotedTableName(),
|
||||||
|
addExtraSpaceIfExist(scope.CombinedConditionSql()),
|
||||||
|
addExtraSpaceIfExist(extraOption),
|
||||||
|
)).Exec()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func addExtraSpaceIfExist(str string) string {
|
||||||
|
if str != "" {
|
||||||
|
return " " + str
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
67
internal/model/tag.go
Normal file
67
internal/model/tag.go
Normal file
|
@ -0,0 +1,67 @@
|
||||||
|
package model
|
||||||
|
|
||||||
|
import (
|
||||||
|
"blog/pkg/app"
|
||||||
|
"github.com/jinzhu/gorm"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Tag struct {
|
||||||
|
*Model
|
||||||
|
Name string `json:"name"`
|
||||||
|
State uint8 `json:"state"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t Tag) TableName() string {
|
||||||
|
return "blog_tag"
|
||||||
|
}
|
||||||
|
|
||||||
|
type TagSwagger struct {
|
||||||
|
List []*Tag
|
||||||
|
Pager *app.Pager
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t Tag) Count(db *gorm.DB) (int, error) {
|
||||||
|
var count int
|
||||||
|
if t.Name != "" {
|
||||||
|
db = db.Where("name = ?", t.Name)
|
||||||
|
}
|
||||||
|
db = db.Where("state = ?", t.State)
|
||||||
|
if err := db.Model(&t).Where("is_del = ?", 0).Count(&count).Error; err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return count, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t Tag) List(db *gorm.DB, pageOffset, pageSize int) ([]*Tag, error) {
|
||||||
|
var tags []*Tag
|
||||||
|
var err error
|
||||||
|
if pageOffset >= 0 && pageSize > 0 {
|
||||||
|
db = db.Offset(pageOffset).Limit(pageSize)
|
||||||
|
}
|
||||||
|
if t.Name != "" {
|
||||||
|
db = db.Where("name = ?", t.Name)
|
||||||
|
}
|
||||||
|
db = db.Where("state = ?", t.State)
|
||||||
|
if err = db.Where("is_del = ?", 0).Find(&tags).Error; err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return tags, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t Tag) Create(db *gorm.DB) error {
|
||||||
|
return db.Create(&t).Error
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t Tag) Update(db *gorm.DB, values interface{}) error {
|
||||||
|
if err := db.Model(t).Where("id = ? AND is_del = ?", t.ID, 0).Updates(values).Error; err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t Tag) Delete(db *gorm.DB) error {
|
||||||
|
return db.Where("id = ? AND is_del = ?", t.Model.ID, 0).Delete(&t).Error
|
||||||
|
}
|
52
internal/routess/api/upload.go
Normal file
52
internal/routess/api/upload.go
Normal file
|
@ -0,0 +1,52 @@
|
||||||
|
package api
|
||||||
|
|
||||||
|
import (
|
||||||
|
"blog/global"
|
||||||
|
"blog/internal/service"
|
||||||
|
"blog/pkg/app"
|
||||||
|
"blog/pkg/convert"
|
||||||
|
"blog/pkg/errorcode"
|
||||||
|
"blog/pkg/upload"
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Upload struct{}
|
||||||
|
|
||||||
|
func NewUpload() Upload {
|
||||||
|
return Upload{}
|
||||||
|
}
|
||||||
|
|
||||||
|
// @Summary 上传图片
|
||||||
|
// @Produce json
|
||||||
|
// @Param type formData int true "文件类型1图片" required Enums(1,2,3)
|
||||||
|
// @Param state formData file true "文件"
|
||||||
|
// @Success 200 {object} service.FileInfo "成功"
|
||||||
|
// @Failure 400 {object} errorcode.Error "请求错误"
|
||||||
|
// @Failure 500 {object} errorcode.Error "内部错误"
|
||||||
|
// @Router /upload/file [post]
|
||||||
|
func (u Upload) UploadFile(c *gin.Context) {
|
||||||
|
response := app.NewResponse(c)
|
||||||
|
file, fileHeader, err := c.Request.FormFile("file")
|
||||||
|
if err != nil {
|
||||||
|
response.ToErrorResponse(errorcode.InvalidParams.WithDetails(err.Error()))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
fileType := convert.StrTo(c.PostForm("type")).MustInt()
|
||||||
|
if fileHeader == nil || fileType <= 0 {
|
||||||
|
response.ToErrorResponse(errorcode.InvalidParams)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
svc := service.New(c.Request.Context())
|
||||||
|
fileInfo, err := svc.UploadFile(upload.FileType(fileType), file, fileHeader)
|
||||||
|
if err != nil {
|
||||||
|
global.Logger.Errorf("svc.UploadFile err: %v", err)
|
||||||
|
response.ToErrorResponse(errorcode.ErrorUploadFileFail.WithDetails(err.Error()))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
response.ToResponse(gin.H{
|
||||||
|
"file_access_url": fileInfo.AccessUrl,
|
||||||
|
})
|
||||||
|
}
|
21
internal/routess/api/v1/article.go
Normal file
21
internal/routess/api/v1/article.go
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
package v1
|
||||||
|
|
||||||
|
import (
|
||||||
|
"blog/pkg/app"
|
||||||
|
"blog/pkg/errorcode"
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Article struct{}
|
||||||
|
|
||||||
|
func NewArticle() Article {
|
||||||
|
return Article{}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a Article) Get(c *gin.Context) {
|
||||||
|
app.NewResponse(c).ToErrorResponse(errorcode.ServerError)
|
||||||
|
}
|
||||||
|
func (a Article) List(c *gin.Context) {}
|
||||||
|
func (a Article) Create(c *gin.Context) {}
|
||||||
|
func (a Article) Update(c *gin.Context) {}
|
||||||
|
func (a Article) Delete(c *gin.Context) {}
|
146
internal/routess/api/v1/tag.go
Normal file
146
internal/routess/api/v1/tag.go
Normal file
|
@ -0,0 +1,146 @@
|
||||||
|
package v1
|
||||||
|
|
||||||
|
import (
|
||||||
|
"blog/global"
|
||||||
|
"blog/internal/service"
|
||||||
|
"blog/pkg/app"
|
||||||
|
"blog/pkg/convert"
|
||||||
|
"blog/pkg/errorcode"
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Tag struct {
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewTag() Tag {
|
||||||
|
return Tag{}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t Tag) Get(c *gin.Context) {}
|
||||||
|
|
||||||
|
// @Summary 获取多个标签
|
||||||
|
// @Produce json
|
||||||
|
// @Param name query string false "标签名称" maxlength(100)
|
||||||
|
// @Param state query int false "状态" Enums(0, 1) default(1)
|
||||||
|
// @Param page query int false "页码" default(1)
|
||||||
|
// @Param page_size query int false "每页数量" default(10)
|
||||||
|
// @Success 200 {object} model.TagSwagger "成功"
|
||||||
|
// @Failure 400 {object} errorcode.Error "请求错误"
|
||||||
|
// @Failure 500 {object} errorcode.Error "内部错误"
|
||||||
|
// @Router /api/v1/tags [get]
|
||||||
|
func (t Tag) List(c *gin.Context) {
|
||||||
|
param := service.TagListRequest{}
|
||||||
|
response := app.NewResponse(c)
|
||||||
|
valid, errs := app.BindAndValid(c, ¶m)
|
||||||
|
if !valid {
|
||||||
|
global.Logger.Infof("app.BindAndValid errs: %v", errs)
|
||||||
|
response.ToErrorResponse(errorcode.InvalidParams.WithDetails(errs.Errors()...))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
svc := service.New(c.Request.Context())
|
||||||
|
pager := app.Pager{Page: app.GetPage(c), PageSize: app.GetPageSize(c)}
|
||||||
|
totalRows, err := svc.CountTag(&service.CountTagRequest{Name: param.Name, State: param.State})
|
||||||
|
if err != nil {
|
||||||
|
global.Logger.Errorf("svc.CountTag err: %v", err)
|
||||||
|
response.ToErrorResponse(errorcode.ErrorCountTagFail)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
tags, err := svc.GetTagList(¶m, &pager)
|
||||||
|
if err != nil {
|
||||||
|
global.Logger.Errorf("svc.GetTagList err: %v", err)
|
||||||
|
response.ToErrorResponse(errorcode.ErrorGetTagListFail)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
response.ToResponseList(tags, totalRows)
|
||||||
|
}
|
||||||
|
|
||||||
|
// @Summary 新增标签
|
||||||
|
// @Produce json
|
||||||
|
// @Param name formData string true "标签名称" minlength(3) maxlength(100)
|
||||||
|
// @Param state formData int false "状态" Enums(0, 1) default(1)
|
||||||
|
// @Param created_by formData string true "创建者" minlength(3) maxlength(100)
|
||||||
|
// @Success 200 {object} model.TagSwagger "成功"
|
||||||
|
// @Failure 400 {object} errorcode.Error "请求错误"
|
||||||
|
// @Failure 500 {object} errorcode.Error "内部错误"
|
||||||
|
// @Router /api/v1/tags [post]
|
||||||
|
func (t Tag) Create(c *gin.Context) {
|
||||||
|
param := service.CreateTagRequest{}
|
||||||
|
response := app.NewResponse(c)
|
||||||
|
valid, errs := app.BindAndValid(c, ¶m)
|
||||||
|
if !valid {
|
||||||
|
global.Logger.Infof("app.BindAndValid errs: %v", errs)
|
||||||
|
response.ToErrorResponse(errorcode.InvalidParams.WithDetails(errs.Errors()...))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
svc := service.New(c.Request.Context())
|
||||||
|
err := svc.CreateTag(¶m)
|
||||||
|
if err != nil {
|
||||||
|
global.Logger.Errorf("svc.CreateTag err: %v", err)
|
||||||
|
response.ToErrorResponse(errorcode.ErrorCreateTagFail)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
response.ToResponse(gin.H{})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// @Summary 更新标签
|
||||||
|
// @Produce json
|
||||||
|
// @Param id path int true "标签 ID"
|
||||||
|
// @Param name formData string false "标签名称" minlength(3),maxlength(100)
|
||||||
|
// @Param state formData int false "状态" Enums(0, 1) default(1)
|
||||||
|
// @Param modified_by formData string true "修改者" minlength(3) maxlength(100)
|
||||||
|
// @Success 200 {array} model.TagSwagger "成功"
|
||||||
|
// @Failure 400 {object} errorcode.Error "请求错误"
|
||||||
|
// @Failure 500 {object} errorcode.Error "内部错误"
|
||||||
|
// @Router /api/v1/tags/{id} [put]
|
||||||
|
func (t Tag) Update(c *gin.Context) {
|
||||||
|
param := service.UpdateTagRequest{ID: convert.StrTo(c.Param("id")).MustUInt32()}
|
||||||
|
response := app.NewResponse(c)
|
||||||
|
valid, errs := app.BindAndValid(c, ¶m)
|
||||||
|
if !valid {
|
||||||
|
global.Logger.Infof("app.BindAndValid errs: %v", errs)
|
||||||
|
response.ToErrorResponse(errorcode.InvalidParams.WithDetails(errs.Errors()...))
|
||||||
|
}
|
||||||
|
|
||||||
|
svc := service.New(c.Request.Context())
|
||||||
|
err := svc.UpdateTag(¶m)
|
||||||
|
if err != nil {
|
||||||
|
global.Logger.Errorf("svc.UpdateTag err: %v", err)
|
||||||
|
response.ToErrorResponse(errorcode.ErrorUpdateTagFail)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
response.ToResponse(gin.H{})
|
||||||
|
}
|
||||||
|
|
||||||
|
// @Summary 删除标签
|
||||||
|
// @Produce json
|
||||||
|
// @Param id path int true "标签 ID"
|
||||||
|
// @Success 200 {string} string "成功"
|
||||||
|
// @Failure 400 {object} errorcode.Error "请求错误"
|
||||||
|
// @Failure 500 {object} errorcode.Error "内部错误"
|
||||||
|
// @Router /api/v1/tags/{id} [delete]
|
||||||
|
func (t Tag) Delete(c *gin.Context) {
|
||||||
|
param := service.DeleteTagRequest{ID: convert.StrTo(c.Param("id")).MustUInt32()}
|
||||||
|
response := app.NewResponse(c)
|
||||||
|
valid, errs := app.BindAndValid(c, ¶m)
|
||||||
|
if !valid {
|
||||||
|
global.Logger.Infof("app.BindAndValid errs: %v", errs)
|
||||||
|
response.ToErrorResponse(errorcode.InvalidParams.WithDetails(errs.Errors()...))
|
||||||
|
}
|
||||||
|
|
||||||
|
svc := service.New(c.Request.Context())
|
||||||
|
err := svc.DeleteTag(¶m)
|
||||||
|
if err != nil {
|
||||||
|
global.Logger.Errorf("svc.DeleteTag err: %v", err)
|
||||||
|
response.ToErrorResponse(errorcode.ErrorDeleteTagFail)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
response.ToResponse(gin.H{})
|
||||||
|
}
|
39
internal/routess/router.go
Normal file
39
internal/routess/router.go
Normal file
|
@ -0,0 +1,39 @@
|
||||||
|
package routess
|
||||||
|
|
||||||
|
import (
|
||||||
|
_ "blog/docs"
|
||||||
|
"blog/internal/middleware"
|
||||||
|
"blog/internal/routess/api"
|
||||||
|
v1 "blog/internal/routess/api/v1"
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
|
swaggerFiles "github.com/swaggo/files"
|
||||||
|
ginSwagger "github.com/swaggo/gin-swagger"
|
||||||
|
)
|
||||||
|
|
||||||
|
func NewRouter() *gin.Engine {
|
||||||
|
r := gin.New()
|
||||||
|
r.Use(gin.Logger())
|
||||||
|
r.Use(gin.Recovery())
|
||||||
|
r.Use(middleware.Translations())
|
||||||
|
article := v1.NewArticle()
|
||||||
|
tag := v1.NewTag()
|
||||||
|
upload := api.NewUpload()
|
||||||
|
r.POST("/upload/file", upload.UploadFile)
|
||||||
|
r.GET("/doc/*any", ginSwagger.WrapHandler(swaggerFiles.Handler))
|
||||||
|
apiv1 := r.Group("/api/v1")
|
||||||
|
{
|
||||||
|
apiv1.POST("/tags", tag.Create)
|
||||||
|
apiv1.DELETE("/tags/:id", tag.Delete)
|
||||||
|
apiv1.PUT("/tags/:id", tag.Update)
|
||||||
|
apiv1.PATCH("/tags/:id/state", tag.Update)
|
||||||
|
apiv1.GET("/tags", tag.List)
|
||||||
|
|
||||||
|
apiv1.POST("/articles", article.Create)
|
||||||
|
apiv1.DELETE("/articles/:id", article.Delete)
|
||||||
|
apiv1.PUT("/articles/:id", article.Update)
|
||||||
|
apiv1.PATCH("/articles/:id/state", article.Update)
|
||||||
|
apiv1.GET("/articles/:id", article.Get)
|
||||||
|
apiv1.GET("/articles", article.List)
|
||||||
|
}
|
||||||
|
return r
|
||||||
|
}
|
18
internal/service/service.go
Normal file
18
internal/service/service.go
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
package service
|
||||||
|
|
||||||
|
import (
|
||||||
|
"blog/global"
|
||||||
|
"blog/internal/dao"
|
||||||
|
"context"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Service struct {
|
||||||
|
ctx context.Context
|
||||||
|
dao *dao.Dao
|
||||||
|
}
|
||||||
|
|
||||||
|
func New(ctx context.Context) Service {
|
||||||
|
svc := Service{ctx: ctx}
|
||||||
|
svc.dao = dao.New(global.DBEngine)
|
||||||
|
return svc
|
||||||
|
}
|
53
internal/service/tag.go
Normal file
53
internal/service/tag.go
Normal file
|
@ -0,0 +1,53 @@
|
||||||
|
package service
|
||||||
|
|
||||||
|
import (
|
||||||
|
"blog/internal/model"
|
||||||
|
"blog/pkg/app"
|
||||||
|
)
|
||||||
|
|
||||||
|
type CountTagRequest struct {
|
||||||
|
Name string `form:"name" binding:"max=100"`
|
||||||
|
State uint8 `form:"state,default=1" binding:"oneof=0 1"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type TagListRequest struct {
|
||||||
|
Name string `form:"name" binding:"max=100"`
|
||||||
|
State uint8 `form:"state,default=1" binding:"oneof=0 1"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type CreateTagRequest struct {
|
||||||
|
Name string `form:"name" binding:"required,min=3,max=100"`
|
||||||
|
CreatedBy string `form:"created_by" binding:"required,min=3,max=100"`
|
||||||
|
State uint8 `form:"state,default=1" binding:"oneof=0 1"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type UpdateTagRequest struct {
|
||||||
|
ID uint32 `form:"id" binding:"required,gte=1"`
|
||||||
|
Name string `form:"name" binding:"min=3,max=100"`
|
||||||
|
State *uint8 `form:"state" binding:"required,oneof=0 1"`
|
||||||
|
ModifiedBy string `form:"modified_by" binding:"required,min=3,max=100"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type DeleteTagRequest struct {
|
||||||
|
ID uint32 `form:"id" binding:"required,gte=1"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (svc *Service) CountTag(param *CountTagRequest) (int, error) {
|
||||||
|
return svc.dao.CountTag(param.Name, param.State)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (svc *Service) GetTagList(param *TagListRequest, pager *app.Pager) ([]*model.Tag, error) {
|
||||||
|
return svc.dao.GetTagList(param.Name, param.State, pager.Page, pager.PageSize)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (svc *Service) CreateTag(param *CreateTagRequest) error {
|
||||||
|
return svc.dao.CreateTag(param.Name, param.State, param.CreatedBy)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (svc *Service) UpdateTag(param *UpdateTagRequest) error {
|
||||||
|
return svc.dao.UpdateTag(param.ID, param.Name, param.State, param.ModifiedBy)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (svc *Service) DeleteTag(param *DeleteTagRequest) error {
|
||||||
|
return svc.dao.DeleteTag(param.ID)
|
||||||
|
}
|
42
internal/service/upload.go
Normal file
42
internal/service/upload.go
Normal file
|
@ -0,0 +1,42 @@
|
||||||
|
package service
|
||||||
|
|
||||||
|
import (
|
||||||
|
"blog/global"
|
||||||
|
"blog/pkg/upload"
|
||||||
|
"errors"
|
||||||
|
"mime/multipart"
|
||||||
|
"os"
|
||||||
|
)
|
||||||
|
|
||||||
|
type FileInfo struct {
|
||||||
|
Name string
|
||||||
|
AccessUrl string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (svc *Service) UploadFile(fileType upload.FileType, file multipart.File, fileHeader *multipart.FileHeader) (*FileInfo, error) {
|
||||||
|
fileName := upload.GetFileName(fileHeader.Filename)
|
||||||
|
if !upload.CheckContainExt(fileType, fileName) {
|
||||||
|
return nil, errors.New("file suffix is not supported")
|
||||||
|
}
|
||||||
|
if upload.CheckMaxSize(fileType, file) {
|
||||||
|
return nil, errors.New("exceeded maximum file limit")
|
||||||
|
}
|
||||||
|
|
||||||
|
uploadSavePath := upload.GetSavePath()
|
||||||
|
if upload.CheckSavePath(uploadSavePath) {
|
||||||
|
if err := upload.CreateSavePath(uploadSavePath, os.ModePerm); err != nil {
|
||||||
|
return nil, errors.New("failed to create save directory")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if upload.CheckPermission(uploadSavePath) {
|
||||||
|
return nil, errors.New("insufficient file permissions")
|
||||||
|
}
|
||||||
|
|
||||||
|
dst := uploadSavePath + "/" + fileName
|
||||||
|
if err := upload.SaveFile(fileHeader, dst); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
accessUrl := global.AppSetting.UploadServerUrl + "/" + fileName
|
||||||
|
return &FileInfo{Name: fileName, AccessUrl: accessUrl}, nil
|
||||||
|
}
|
90
main.go
Normal file
90
main.go
Normal file
|
@ -0,0 +1,90 @@
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"blog/global"
|
||||||
|
"blog/internal/model"
|
||||||
|
"blog/internal/routess"
|
||||||
|
"blog/pkg/logger"
|
||||||
|
"blog/pkg/setting"
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
|
_ "github.com/go-sql-driver/mysql"
|
||||||
|
"gopkg.in/natefinch/lumberjack.v2"
|
||||||
|
"log"
|
||||||
|
"net/http"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
err := setupSetting()
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalf("init.setupSetting err: %v", err)
|
||||||
|
}
|
||||||
|
err = setupDBEngine()
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalf("init.setupDBEngine err: %v", err)
|
||||||
|
}
|
||||||
|
err = setupLogger()
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalf("init.setupLogger err: %v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
func setupDBEngine() error {
|
||||||
|
var err error
|
||||||
|
global.DBEngine, err = model.NewDBEngine(global.DatabaseSetting)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
func setupLogger() error {
|
||||||
|
global.Logger = logger.NewLogger(&lumberjack.Logger{
|
||||||
|
Filename: global.AppSetting.LogSavePath + "/" + global.AppSetting.LogFileName + global.AppSetting.LogFileExt,
|
||||||
|
MaxSize: 600,
|
||||||
|
MaxAge: 10,
|
||||||
|
LocalTime: true,
|
||||||
|
}, "", log.LstdFlags).WithCaller(2)
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func setupSetting() error {
|
||||||
|
newSetting, err := setting.NewSetting()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
err = newSetting.ReadSection("Server", &global.ServerSetting)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
err = newSetting.ReadSection("App", &global.AppSetting)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
err = newSetting.ReadSection("Database", &global.DatabaseSetting)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
global.ServerSetting.ReadTimeout *= time.Second
|
||||||
|
global.ServerSetting.WriteTimeout *= time.Second
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// @title 博客系统
|
||||||
|
// @version 1.0
|
||||||
|
// @description Go 语言编程之旅:一起用 Go 做项目
|
||||||
|
// @termsOfService https://github.com/go-programming-tour-book
|
||||||
|
func main() {
|
||||||
|
gin.SetMode(global.ServerSetting.RunMode)
|
||||||
|
global.Logger.Infof("%s: go-programming-tour-book/%s", "eddycjy", "blog-service")
|
||||||
|
|
||||||
|
r := routess.NewRouter()
|
||||||
|
s := &http.Server{
|
||||||
|
Addr: ":8080",
|
||||||
|
Handler: r,
|
||||||
|
ReadTimeout: 10 * time.Second,
|
||||||
|
WriteTimeout: 10 * time.Second,
|
||||||
|
MaxHeaderBytes: 1 << 20,
|
||||||
|
}
|
||||||
|
s.ListenAndServe()
|
||||||
|
}
|
50
storage/logs/app.log
Normal file
50
storage/logs/app.log
Normal file
|
@ -0,0 +1,50 @@
|
||||||
|
2021/05/27 11:05:37 {"callers":["/home/xing/IdeaProjects/blog/main.go: 26 main.init.0"],"level":"info","message":"eddycjy: go-programming-tour-book/blog-service","time":1622084737609101710}
|
||||||
|
2021/05/27 11:21:12 {"callers":["/home/xing/IdeaProjects/blog/main.go: 26 main.init.0"],"level":"info","message":"eddycjy: go-programming-tour-book/blog-service","time":1622085672929232372}
|
||||||
|
2021/05/27 11:51:10 {"callers":["/home/xing/IdeaProjects/blog/main.go: 26 main.init.0"],"level":"info","message":"eddycjy: go-programming-tour-book/blog-service","time":1622087470164603650}
|
||||||
|
2021/05/27 11:51:27 {"callers":["/home/xing/IdeaProjects/blog/main.go: 26 main.init.0"],"level":"info","message":"eddycjy: go-programming-tour-book/blog-service","time":1622087487441027324}
|
||||||
|
2021/05/27 11:54:41 {"callers":["/home/xing/IdeaProjects/blog/main.go: 26 main.init.0"],"level":"info","message":"eddycjy: go-programming-tour-book/blog-service","time":1622087681328002588}
|
||||||
|
2021/05/27 11:57:54 {"callers":["/home/xing/IdeaProjects/blog/main.go: 26 main.init.0"],"level":"info","message":"eddycjy: go-programming-tour-book/blog-service","time":1622087874303187563}
|
||||||
|
2021/05/27 14:07:35 {"callers":["/home/xing/IdeaProjects/blog/main.go: 26 main.init.0"],"level":"info","message":"eddycjy: go-programming-tour-book/blog-service","time":1622095655653485221}
|
||||||
|
2021/05/27 14:08:02 {"callers":["/home/xing/IdeaProjects/blog/main.go: 26 main.init.0"],"level":"info","message":"eddycjy: go-programming-tour-book/blog-service","time":1622095682368233943}
|
||||||
|
2021/05/27 14:08:25 {"callers":["/home/xing/IdeaProjects/blog/main.go: 26 main.init.0"],"level":"info","message":"eddycjy: go-programming-tour-book/blog-service","time":1622095705608187304}
|
||||||
|
2021/05/27 14:37:06 {"callers":["/home/xing/IdeaProjects/blog/main.go: 26 main.init.0"],"level":"info","message":"eddycjy: go-programming-tour-book/blog-service","time":1622097426493197748}
|
||||||
|
2021/05/27 14:37:14 {"callers":["/home/xing/IdeaProjects/blog/main.go: 26 main.init.0"],"level":"info","message":"app.BindAndValid errs: State必须是[0 1]中的一个","time":1622097434325394979}
|
||||||
|
2021/05/27 14:38:00 {"callers":["/home/xing/IdeaProjects/blog/main.go: 26 main.init.0"],"level":"info","message":"app.BindAndValid errs: State必须是[0 1]中的一个","time":1622097480138488964}
|
||||||
|
2021/05/27 16:02:37 {"callers":["/home/xing/IdeaProjects/blog/main.go: 26 main.init.0"],"level":"info","message":"eddycjy: go-programming-tour-book/blog-service","time":1622102557922696550}
|
||||||
|
2021/05/27 16:06:13 {"callers":["/home/xing/IdeaProjects/blog/main.go: 26 main.init.0"],"level":"info","message":"app.BindAndValid errs: ","time":1622102773119591365}
|
||||||
|
2021/05/27 16:17:48 {"callers":["/home/xing/IdeaProjects/blog/main.go: 26 main.init.0"],"level":"info","message":"eddycjy: go-programming-tour-book/blog-service","time":1622103468619951851}
|
||||||
|
2021/05/27 16:20:57 {"callers":["/home/xing/IdeaProjects/blog/main.go: 26 main.init.0"],"level":"info","message":"eddycjy: go-programming-tour-book/blog-service","time":1622103657062869940}
|
||||||
|
2021/05/27 16:39:06 {"callers":["/home/xing/IdeaProjects/blog/main.go: 26 main.init.0"],"level":"info","message":"eddycjy: go-programming-tour-book/blog-service","time":1622104746415966590}
|
||||||
|
2021/05/27 16:46:18 {"callers":["/home/xing/IdeaProjects/blog/main.go: 26 main.init.0"],"level":"info","message":"eddycjy: go-programming-tour-book/blog-service","time":1622105178606120868}
|
||||||
|
2021/05/27 16:47:02 {"callers":["/home/xing/IdeaProjects/blog/main.go: 26 main.init.0"],"level":"info","message":"eddycjy: go-programming-tour-book/blog-service","time":1622105222324148943}
|
||||||
|
2021/05/27 19:32:06 {"callers":["/home/xing/IdeaProjects/blog/main.go: 26 main.init.0"],"level":"info","message":"eddycjy: go-programming-tour-book/blog-service","time":1622115126240365402}
|
||||||
|
2021/05/27 19:38:00 {"callers":["/home/xing/IdeaProjects/blog/main.go: 26 main.init.0"],"level":"info","message":"eddycjy: go-programming-tour-book/blog-service","time":1622115480450697332}
|
||||||
|
2021/05/27 19:56:13 {"callers":["/home/xing/IdeaProjects/blog/main.go: 26 main.init.0"],"level":"info","message":"app.BindAndValid errs: State为必填字段","time":1622116573249023863}
|
||||||
|
2021/05/27 19:57:06 {"callers":["/home/xing/IdeaProjects/blog/main.go: 26 main.init.0"],"level":"info","message":"app.BindAndValid errs: State为必填字段","time":1622116626105455018}
|
||||||
|
2021/05/27 19:59:00 {"callers":["/home/xing/IdeaProjects/blog/main.go: 26 main.init.0"],"level":"info","message":"eddycjy: go-programming-tour-book/blog-service","time":1622116740320292554}
|
||||||
|
2021/05/27 19:59:17 {"callers":["/home/xing/IdeaProjects/blog/main.go: 26 main.init.0"],"level":"info","message":"app.BindAndValid errs: State为必填字段","time":1622116757447119782}
|
||||||
|
2021/05/27 19:59:51 {"callers":["/home/xing/IdeaProjects/blog/main.go: 26 main.init.0"],"level":"info","message":"app.BindAndValid errs: State为必填字段","time":1622116791393630451}
|
||||||
|
2021/05/28 10:18:28 {"callers":["/home/xing/IdeaProjects/blog/main.go: 26 main.init.0"],"level":"info","message":"eddycjy: go-programming-tour-book/blog-service","time":1622168308865618331}
|
||||||
|
2021/05/28 11:01:23 {"callers":["/home/xing/IdeaProjects/blog/main.go: 26 main.init.0"],"level":"info","message":"app.BindAndValid errs: State为必填字段","time":1622170883605638925}
|
||||||
|
2021/05/28 11:21:46 {"callers":["/home/xing/IdeaProjects/blog/main.go: 26 main.init.0"],"level":"info","message":"eddycjy: go-programming-tour-book/blog-service","time":1622172106599262267}
|
||||||
|
2021/05/28 11:31:47 {"callers":["/home/xing/IdeaProjects/blog/main.go: 26 main.init.0"],"level":"info","message":"app.BindAndValid errs: State为必填字段","time":1622172707489849654}
|
||||||
|
2021/05/28 11:31:50 {"callers":["/home/xing/IdeaProjects/blog/main.go: 26 main.init.0"],"level":"info","message":"app.BindAndValid errs: State为必填字段","time":1622172710615258635}
|
||||||
|
2021/05/28 11:31:53 {"callers":["/home/xing/IdeaProjects/blog/main.go: 26 main.init.0"],"level":"info","message":"app.BindAndValid errs: State为必填字段","time":1622172713867713030}
|
||||||
|
2021/05/28 11:33:49 {"callers":["/home/xing/IdeaProjects/blog/main.go: 26 main.init.0"],"level":"info","message":"app.BindAndValid errs: State为必填字段","time":1622172829474375529}
|
||||||
|
2021/05/28 11:34:18 {"callers":["/home/xing/IdeaProjects/blog/main.go: 26 main.init.0"],"level":"info","message":"app.BindAndValid errs: State为必填字段","time":1622172858829135730}
|
||||||
|
2021/05/28 11:34:19 {"callers":["/home/xing/IdeaProjects/blog/main.go: 26 main.init.0"],"level":"info","message":"app.BindAndValid errs: State为必填字段","time":1622172859814946252}
|
||||||
|
2021/05/28 11:35:47 {"callers":["/home/xing/IdeaProjects/blog/main.go: 26 main.init.0"],"level":"info","message":"eddycjy: go-programming-tour-book/blog-service","time":1622172947398620704}
|
||||||
|
2021/05/28 11:38:50 {"callers":["/home/xing/IdeaProjects/blog/main.go: 26 main.init.0"],"level":"info","message":"eddycjy: go-programming-tour-book/blog-service","time":1622173130414371110}
|
||||||
|
2021/05/28 11:41:36 {"callers":["/home/xing/IdeaProjects/blog/main.go: 26 main.init.0"],"level":"info","message":"app.BindAndValid errs: State为必填字段","time":1622173296327015991}
|
||||||
|
2021/05/28 16:28:31 {"callers":["/home/xing/IdeaProjects/blog/main.go: 26 main.init.0"],"level":"info","message":"eddycjy: go-programming-tour-book/blog-service","time":1622190511003684831}
|
||||||
|
2021/05/28 16:53:48 {"callers":["/home/xing/IdeaProjects/blog/main.go: 26 main.init.0"],"level":"info","message":"eddycjy: go-programming-tour-book/blog-service","time":1622192028460804461}
|
||||||
|
2021/05/28 17:02:49 {"callers":["/home/xing/IdeaProjects/blog/main.go: 26 main.init.0"],"level":"info","message":"app.BindAndValid errs: State为必填字段","time":1622192569815835955}
|
||||||
|
2021/05/28 17:04:13 {"callers":["/home/xing/IdeaProjects/blog/main.go: 26 main.init.0"],"level":"info","message":"app.BindAndValid errs: State为必填字段","time":1622192653208795175}
|
||||||
|
2021/05/28 17:05:45 {"callers":["/home/xing/IdeaProjects/blog/main.go: 26 main.init.0"],"level":"info","message":"app.BindAndValid errs: State为必填字段","time":1622192745007678561}
|
||||||
|
2021/05/28 17:05:49 {"callers":["/home/xing/IdeaProjects/blog/main.go: 26 main.init.0"],"level":"info","message":"app.BindAndValid errs: State为必填字段","time":1622192749363800854}
|
||||||
|
2021/05/28 17:06:02 {"callers":["/home/xing/IdeaProjects/blog/main.go: 26 main.init.0"],"level":"info","message":"app.BindAndValid errs: State为必填字段","time":1622192762407831575}
|
||||||
|
2021/05/28 17:09:42 {"callers":["/home/xing/IdeaProjects/blog/main.go: 26 main.init.0"],"level":"info","message":"eddycjy: go-programming-tour-book/blog-service","time":1622192982235099983}
|
||||||
|
2021/05/28 17:10:37 {"callers":["/home/xing/IdeaProjects/blog/main.go: 26 main.init.0"],"level":"info","message":"eddycjy: go-programming-tour-book/blog-service","time":1622193037895200224}
|
||||||
|
2021/05/28 17:22:05 {"callers":["/home/xing/IdeaProjects/blog/main.go: 26 main.init.0"],"level":"info","message":"eddycjy: go-programming-tour-book/blog-service","time":1622193725365423500}
|
||||||
|
2021/05/28 18:35:09 {"callers":["/home/xing/IdeaProjects/blog/main.go: 26 main.init.0"],"level":"info","message":"eddycjy: go-programming-tour-book/blog-service","time":1622198109257587951}
|
||||||
|
2021/05/28 18:36:38 {"callers":["/home/xing/IdeaProjects/blog/main.go: 26 main.init.0"],"level":"info","message":"eddycjy: go-programming-tour-book/blog-service","time":1622198198869750999}
|
Loading…
Reference in New Issue
Block a user