最全Go语言Web项目搭建,2024年最新程序员深度学习

img
img

网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。

需要这份系统化的资料的朋友,可以添加戳这里获取

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

"github.com/gin-gonic/gin"
"go.uber.org/zap"
"net/http"

)

// 索引页
func IndexGet(c *gin.Context) {
articleList, err := models.QueryAllArticle()
if err != nil {
logger.Error(“models.QueryCurrUserArticleWithPage failed”, zap.Any(“error”, err))
}
logger.Debug(“models.QueryCurrUserArticleWithPage”, zap.Any(“articleList”, articleList))
c.HTML(http.StatusOK, “index.html”, gin.H{“articleList”: articleList})
}


* login.go



package controllers

import (
“blogweb_gin/logger”
“blogweb_gin/models”
“blogweb_gin/utils”
“fmt”
“github.com/gin-contrib/sessions”
“github.com/gin-gonic/gin”
“go.uber.org/zap”
“net/http”
)

// get登陆页
func LoginGet(c *gin.Context) {
//返回html
c.HTML(http.StatusOK, “login.html”, gin.H{“title”: “登录页”})
}

// 提交登陆
func LoginPost(c *gin.Context) {
// 取出请求数据
// 校验用户名密码是否正确
// 返回响应
username := c.PostForm(“username”)
password := c.PostForm(“password”)
logger.Debug(“login”, zap.String(“username”, username), zap.String(“password”, password))

// 去数据库查,注意查找的时候,密码是MD5之后的密码查找
id := models.QueryUserWithParam(username, utils.MD5(password))

fmt.Println("id:", id)

// 登陆成功
if id > 0 {
	// 给响应种上Cookie
	session := sessions.Default(c)
	session.Set("login\_user", username) // 在session中保存k-v,然后写入cookie
	session.Save()

	c.Redirect(http.StatusFound, "/home") // 浏览器收到这个就会跳转到我指定的页面
	c.JSON(http.StatusOK, gin.H{"code": 200, "message": "登录成功"})
} else {
	c.JSON(http.StatusOK, gin.H{"code": 0, "message": "登录失败"})
}

}

// 登出
func LogoutHandler(c *gin.Context) {
//清除该用户登录状态的数据
session := sessions.Default©
session.Delete(“login_user”)
session.Save()

c.Redirect(http.StatusFound, "/login")

}


* register.go



package controllers

import (
“blogweb_gin/logger”
“blogweb_gin/models”
“blogweb_gin/utils”
“fmt”
“github.com/gin-gonic/gin”
“net/http”
“time”
)

// 获取注册
func RegisterGet(c *gin.Context) {
// 返回html
c.HTML(http.StatusOK, “register.html”, gin.H{“title”: “注册页”})
}

// 注册提交
func RegisterPost(c *gin.Context) {
// 取出请求的数据
// 判断注册是否重复 --> 拿着用户名去数据库查一下有没有
// 写入数据库
// 获取表单信息
username := c.PostForm(“username”)
password := c.PostForm(“password”)
repassword := c.PostForm(“repassword”)
logger.Debug(fmt.Sprintf(“%s %s %s”, username, password, repassword))

// 注册之前先判断该用户名是否已经被注册,如果已经注册,返回错误
id := models.QueryUserWithUsername(username)
fmt.Println("id:", id)
if id > 0 {
	c.JSON(http.StatusOK, gin.H{"code": 0, "message": "用户名已经存在"})
	return
}

// 注册用户名和密码
// 存储的密码是md5后的数据,那么在登录的验证的时候,也是需要将用户的密码md5之后和数据库里面的密码进行判断
password = utils.MD5(password)
logger.Debug(fmt.Sprintf("password after md5:%s", password))

user := models.User{
	Username:   username,
	Password:   password,
	Status:     0,
	CreateTime: time.Now().Unix(),
}
\_, err := models.InsertUser(&user)
if err != nil {
	c.JSON(http.StatusOK, gin.H{"code": 0, "message": "注册失败"})
} else {
	c.JSON(http.StatusOK, gin.H{"code": 1, "message": "注册成功"})
}

}


#### dao


* mysql.go



package dao

import (
“blogweb_gin/config”
“blogweb_gin/logger”
“fmt”
_ “github.com/go-sql-driver/mysql”
sql “github.com/jmoiron/sqlx”
)

var db *sql.DB

func InitMySQL(cfg *config.MySQLConfig) (err error) {
logger.Info(“InitMySQL…”)
if db == nil {
dsn := fmt.Sprintf(“%s:%s@tcp(%s:%d)/%s”, cfg.Username, cfg.Password, cfg.Host, cfg.Port, cfg.DB)
db, err = sql.Connect(“mysql”, dsn)
if err != nil {
return
}
}

err = CreateTableWithUser() // 创建用户表
if err != nil {
	return
}

err = CreateTableWithArticle() // 创建文章表
if err != nil {
	return
}

err = CreateTableWithAlbum() // 创建图片表
if err != nil {
	return
}

return

}

//创建用户表
func CreateTableWithUser() (err error) {
sqlStr := CREATE TABLE IF NOT EXISTS users( id INT(4) PRIMARY KEY AUTO\_INCREMENT NOT NULL, username VARCHAR(64), password VARCHAR(64), status INT(4), create\_time INT(10) );

\_, err = ModifyDB(sqlStr)
return

}

// 创建文章表
func CreateTableWithArticle() (err error) {
sqlStr := create table if not exists article( id int(4) primary key auto\_increment not null, title varchar(30), author varchar(20), tags varchar(30), short varchar(255), content longtext, create\_time int(10), status int(4) );
_, err = ModifyDB(sqlStr)
return
}

// 创建图片表
func CreateTableWithAlbum() (err error) {
sqlStr := create table if not exists album( id int(4) primary key auto\_increment not null, filepath varchar(255), filename varchar(64), status int(4), create\_time int(10) );
_, err = ModifyDB(sqlStr)
return
}

// 操作数据库
func ModifyDB(sql string, args …interface{}) (int64, error) {
result, err := db.Exec(sql, args…)
if err != nil {
fmt.Println(err)
return 0, err
}
count, err := result.RowsAffected()
if err != nil {
fmt.Println(err)
return 0, err
}
return count, nil
}

// 查询
func QueryRowDB(dest interface{}, sql string, args …interface{}) error {
return db.Get(dest, sql, args…)
}

// 查询多条
func QueryRows(dest interface{}, sql string, args …interface{}) error {
return db.Select(dest, sql, args…)
}


* redis.go



package dao

import (
“blogweb_gin/config”
“fmt”
“github.com/go-redis/redis”
)

var (
Client *redis.Client
)

// 初始化连接
func InitRedis(cfg *config.RedisConfig) (err error) {
Client = redis.NewClient(&redis.Options{
Addr: fmt.Sprintf(“%s:%d”, cfg.Host, cfg.Port),
Password: cfg.Password, // no password set
DB: cfg.DB, // use default DB
})

\_, err = Client.Ping().Result()
if err != nil {
	return err
}
return nil

}


* redis\_key.go



package dao

const (
KeyArticleCount = “blog:article:read:count:%s” // 24小时文章阅读数key eq:blog:article:count:20200315
)


#### logger


* logger.go



package logger

import (
“blogweb_gin/config”
“github.com/gin-gonic/gin”
“github.com/natefinch/lumberjack”
“go.uber.org/zap”
“go.uber.org/zap/zapcore”
“net”
“net/http”
“net/http/httputil”
“os”
“runtime/debug”
“strings”
“time”
)

var Logger *zap.Logger // 我们在项目用都使用这个日志对象

// 初始化Logger
func InitLogger(cfg *config.LogConfig) (err error) {
ws := getLogWriter(cfg.Filename, cfg.MaxSize, cfg.MaxBackups, cfg.MaxAge) // 做日志切割第三方包
encoder := getEncoder() // 日志输出的格式
var level = new(zapcore.Level)
err = level.UnmarshalText([]byte(cfg.Level))
if err != nil {
return
}
core := zapcore.NewCore(encoder, ws, level)
Logger = zap.New(core, zap.AddCaller(), zap.AddCallerSkip(1))
return
}

func getEncoder() zapcore.Encoder {
encoderConfig := zap.NewProductionEncoderConfig()
encoderConfig.EncodeTime = zapcore.ISO8601TimeEncoder // 时间字符串
encoderConfig.TimeKey = “time”
encoderConfig.EncodeLevel = zapcore.CapitalLevelEncoder
encoderConfig.EncodeDuration = zapcore.SecondsDurationEncoder
encoderConfig.EncodeCaller = zapcore.ShortCallerEncoder // 函数调用
return zapcore.NewJSONEncoder(encoderConfig) // JSON格式
}

func getLogWriter(filename string, maxSize, maxBackup, maxAge int) zapcore.WriteSyncer {
lumberJackLogger := &lumberjack.Logger{
Filename: filename,
MaxSize: maxSize,
MaxBackups: maxBackup,
MaxAge: maxAge,
}
return zapcore.AddSync(lumberJackLogger)
}

// 参考:gin-zap 这个库

// GinLogger 接收gin框架默认的日志
func GinLogger(logger *zap.Logger) gin.HandlerFunc {
return func(c *gin.Context) {
start := time.Now()
path := c.Request.URL.Path
query := c.Request.URL.RawQuery
c.Next()

	cost := time.Since(start)
	logger.Info(path,
		zap.Int("status", c.Writer.Status()),
		zap.String("method", c.Request.Method),
		zap.String("path", path),
		zap.String("query", query),
		zap.String("ip", c.ClientIP()),
		zap.String("user-agent", c.Request.UserAgent()),
		zap.String("errors", c.Errors.ByType(gin.ErrorTypePrivate).String()),
		zap.Duration("cost", cost),
	)
}

}

// GinRecovery recover掉项目可能出现的panic,并使用zap记录相关日志
func GinRecovery(logger *zap.Logger, stack bool) gin.HandlerFunc {
return func(c *gin.Context) {
defer func() {
if err := recover(); err != nil {
// Check for a broken connection, as it is not really a
// condition that warrants a panic stack trace.
var brokenPipe bool
if ne, ok := err.(*net.OpError); ok {
if se, ok := ne.Err.(*os.SyscallError); ok {
if strings.Contains(strings.ToLower(se.Error()), “broken pipe”) || strings.Contains(strings.ToLower(se.Error()), “connection reset by peer”) {
brokenPipe = true
}
}
}

			httpRequest, \_ := httputil.DumpRequest(c.Request, false)
			if brokenPipe {
				logger.Error(c.Request.URL.Path,
					zap.Any("error", err),
					zap.String("request", string(httpRequest)),
				)
				// If the connection is dead, we can't write a status to it.
				c.Error(err.(error)) // nolint: errcheck
				c.Abort()
				return
			}

			if stack {
				logger.Error("[Recovery from panic]",
					zap.Any("error", err),
					zap.String("request", string(httpRequest)),
					zap.String("stack", string(debug.Stack())),
				)
			} else {
				logger.Error("[Recovery from panic]",
					zap.Any("error", err),
					zap.String("request", string(httpRequest)),
				)
			}
			c.AbortWithStatus(http.StatusInternalServerError)
		}
	}()
	c.Next()
}

}

func Debug(msg string, fields …zap.Field) {
Logger.Debug(msg, fields…) // logger.go
}

func Info(msg string, fields …zap.Field) {
Logger.Info(msg, fields…)
}

func Warn(msg string, fields …zap.Field) {
Logger.Warn(msg, fields…)
}

func Error(msg string, fields …zap.Field) {
Logger.Error(msg, fields…)
}


#### logic


* logic.go



package logic

import (
“blogweb_gin/dao”
“blogweb_gin/logger”
“blogweb_gin/models”
“fmt”
“go.uber.org/zap”
“strconv”
“time”
)

// 点击文章,阅读数加 1
// 每次请求/article/show/:idURL的时候 执行redis命令 zincrby code_language 1 golang

// 给指定文章的阅读数+1
func IncArticleReadCount(articleId string) error {
// zincrby code_language 1 golang
todayStr := time.Now().Format(“20060102”)
key := fmt.Sprintf(dao.KeyArticleCount, todayStr)

return dao.Client.ZIncrBy(key, 1, articleId).Err()

}

// 获取阅读排行榜排名前N的文章
func GetArticleReadCountTopN(n int64) []*models.Article {
// 1. zrevrange Key 0 n-1 从redis取出前n位的文章id
todayStr := time.Now().Format(“20060102”)
key := fmt.Sprintf(dao.KeyArticleCount, todayStr)
idStrs, err := dao.Client.ZRevRange(key, 0, n-1).Result()
if err != nil {
logger.Error(“ZRevRange”, zap.Any(“error”, err))
}

// 2. 根据上一步获取的文章id查询数据库取文章标题 ["3" "1" "5"]
// select id, title from article where id in (3, 1, 5); // 文章的顺序对吗? 不对
// 1. 让MySQL排序
// select id, title from article where id in (3, 1, 5) order by FIND\_IN\_SET(id, (3, 1, 5));
// 2. 查询出来自己排序
// 先准备好要查询的ID Slice
var ids = make([]int64, len(idStrs))
for \_, idStr := range idStrs {
	id, err := strconv.ParseInt(idStr, 0, 16)
	if err != nil {
		logger.Warn("ArticleTopN:strconv.ParseInt failed", zap.Any("error", err))
		continue
	}

	ids = append(ids, id)
}

articleList, err := models.QueryArticlesByIds(ids, idStrs)
if err != nil {
	logger.Error("queryArticlesByIds", zap.Any("error", err))
}
return articleList

}


#### middlewares


* auth.go



package middlewares

import (
“github.com/gin-contrib/sessions”
“github.com/gin-gonic/gin”
“net/http”
)

// 最基础的认证校验 只要cookie中带了login_user标识就认为是登录用户
func BasicAuth() func(c *gin.Context) {
return func(c *gin.Context) {
// c代表了请求相关的所有内容,获取当前请求对应的session数据
session := sessions.Default©

	loginUser := session.Get("login\_user")
	// 请求对应的session中找不到我想要的数据,说明不是登录的用户
	if loginUser == nil {
		c.Redirect(http.StatusFound, "/login")
		c.Abort() // 终止当前请求的处理函数调用链
		return    // 终止当前处理函数
	}

	// 根据loginUser 去数据库里用户对象取出来 gob是go语言里面二进制的数据格式
	// 如果是一个登录的用户,我就在c上设置两个自定义的键值对!!!
	c.Set("is\_login", true)
	c.Set("login\_user", loginUser)
	c.Next()
}

}


#### models


* album.go



package models

import “blogweb_gin/dao”

type Album struct {
Id int
Filepath string
Filename string
Status int
CreateTime int64 db:"create\_time"
}

// 增加图片
func AddAlbum(album *Album) (int64, error) {
return dao.ModifyDB(“insert into album(filepath,filename,status,create_time)values(?,?,?,?)”,
album.Filepath, album.Filename, album.Status, album.CreateTime)
}

// 获取图片
func QueryAlbum() (dest []*Album, err error) {
sqlStr := “select id,filepath,filename,status,create_time from album”
err = dao.QueryRows(&dest, sqlStr)
return
}


* article.go



package models

import (
“blogweb_gin/dao”
“blogweb_gin/logger”
sql “github.com/jmoiron/sqlx”
“go.uber.org/zap”
“strings”
)

const (
pageSize = 4
)

type Article struct {
Id int json:"id",form:"id"
Title string json:"title",form:"title"
Tags string json:"tags",form:"tags"
Short string json:"short",form:"short"
Content string json:"content",form:"content"
Author string
CreateTime int64 db:"create\_time"
Status int // Status=0为正常,1为删除,2为冻结
}

//-----------数据库操作---------------

// 增加文章
func AddArticle(article *Article) (int64, error) {
return dao.ModifyDB(“insert into article(title,tags,short,content,author,create_time,status) values(?,?,?,?,?,?,?)”,
article.Title, article.Tags, article.Short, article.Content, article.Author, article.CreateTime, article.Status)
}

// 更新文章
func UpdateArticle(article *Article) (int64, error) {
sqlStr := “update article set title=?,tags=?,short=?,content=? where id=?”
return dao.ModifyDB(sqlStr, article.Title, article.Tags, article.Short, article.Content, article.Id)
}

// 删除文章
func DeleteArticle(id string) (int64, error) {
sqlStr := “delete from article where id=?”
return dao.ModifyDB(sqlStr, id)
}

// 查询所有文章

/**
分页查询数据库
limit分页查询语句,
语法:limit m,n

m代表从多少位开始获取,与id值无关
n代表获取多少条数据

总共有10条数据,每页显示4条。 --> 总共需要(10-1)/4+1 页。
问第2页数据是哪些? --> 5,6,7,8 (2-1)*4,4

*/
// 查询数据库文章
func QueryAllArticle() ([]*Article, error) {
sqlStr := “select id,title,tags,short,content,author,create_time from article”
var articleList []*Article
err := dao.QueryRows(&articleList, sqlStr)
if err != nil {
return nil, err
}
return articleList, nil
}

// 根据Page查询文章
func QueryCurrUserArticleWithPage(username string, pageNum int) (articleList []*Article, err error) {
sqlStr := “select id,title,tags,short,content,author,create_time from article where author=? limit ?,?”

articleList, err = queryArticleWithCon(pageNum, sqlStr, username)
if err != nil {
	logger.Debug("queryArticleWithCon, ", zap.Any("error", err))
	return nil, err
}

logger.Debug("QueryCurrUserArticleWithPage,", zap.Any("articleList", articleList))
return articleList, nil

}

// 根据Id查询文章
func QueryArticleWithId(id string) (article *Article, err error) {
article = new(Article)
sqlStr := “select id,title,tags,short,content,author,create_time from article where id=?”
err = dao.QueryRowDB(article, sqlStr, id)
return
}

// 根据查询条件查询指定页数有的文章
func queryArticleWithCon(pageNum int, sqlStr string, args …interface{}) (articleList []*Article, err error) {
pageNum–
args = append(args, pageNum*pageSize, pageSize)
logger.Debug(“queryArticleWithCon”, zap.Any(“pageNum”, pageNum), zap.Any(“args”, args))
err = dao.QueryRows(&articleList, sqlStr, args…)
logger.Debug(“dao.QueryRows result”, zap.Any(“articleList”, articleList))
return
}

// 查询文章的总条数
func QueryArticleRowNum() (num int, err error) {
err = dao.QueryRowDB(&num, “select count(id) from article”)
return
}

// 根据id查文章 按顺序
func QueryArticlesByIds(ids []int64, idStrs []string) ([]*Article, error) {
// 让MySQL排序
query, args, err := sql.In(“select id, title from article where id in (?) order by FIND_IN_SET(id, ?)”, ids, strings.Join(idStrs, “,”))
if err != nil {
logger.Error(“QueryArticlesByIds”, zap.Any(“error”, err))
return nil, err
}

var dest []\*Article
err = dao.QueryRows(&dest, query, args...)
return dest, err

}


* home.go



package models

import (
“blogweb_gin/logger”
“blogweb_gin/utils”
“fmt”
“go.uber.org/zap”
“strconv”
“strings”
)

type HomeBlockParam struct {
Article *Article

TagLinks      []\*TagLink
CreateTimeStr string
//查看文章的地址
Link string

//修改文章的地址
UpdateLink string
DeleteLink string

//记录是否登录
IsLogin bool

}

type TagLink struct {
TagName string
TagUrl string
}

// HomePagination 分页器
type HomePagination struct {
HasPre bool
HasNext bool
ShowPage string
PreLink string
NextLink string
}

//将tags字符串转化成首页模板所需要的数据结构
func createTagsLinks(tagStr string) []*TagLink {
var tagLinks = make([]*TagLink, 0, strings.Count(tagStr, “&”))
tagList := strings.Split(tagStr, “&”)
for _, tag := range tagList {
tagLinks = append(tagLinks, &TagLink{tag, “/?tag=” + tag})
}
return tagLinks
}

// 生成home页面数据结构
func GenHomeBlocks(articleList []*Article, isLogin bool) (ret []*HomeBlockParam) {
// 内存申请一次到位
ret = make([]*HomeBlockParam, 0, len(articleList))
for _, art := range articleList {
// 将数据库model转换为首页模板所需要的model
homeParam := HomeBlockParam{
Article: art,
IsLogin: isLogin,
}
homeParam.TagLinks = createTagsLinks(art.Tags)
homeParam.CreateTimeStr = utils.SwitchTimeStampToStr(art.CreateTime)

	homeParam.Link = fmt.Sprintf("/article/show/%d", art.Id)
	homeParam.UpdateLink = fmt.Sprintf("/article/update?id=%d", art.Id)
	homeParam.DeleteLink = fmt.Sprintf("/article/delete?id=%d", art.Id)
	ret = append(ret, &homeParam) // 不再需要动态扩容
}
return

}

// 生成home页面分页数据结构
func GenHomePagination(page int) *HomePagination {
pageObj := new(HomePagination)

// 查询出总的条数
num, \_ := QueryArticleRowNum()

// 从配置文件中读取每页显示的条数
// 计算出总页数
allPageNum := (num-1)/pageSize + 1

pageObj.ShowPage = fmt.Sprintf("%d/%d", page, allPageNum)

//当前页数小于等于1,那么上一页的按钮不能点击
if page <= 1 {
	pageObj.HasPre = false
} else {
	pageObj.HasPre = true
}

//当前页数大于等于总页数,那么下一页的按钮不能点击
if page >= allPageNum {
	pageObj.HasNext = false
} else {
	pageObj.HasNext = true
}

pageObj.PreLink = "/?page=" + strconv.Itoa(page-1)
pageObj.NextLink = "/?page=" + strconv.Itoa(page+1)
logger.Debug("GenHomePagination", zap.Any("pageObj", \*pageObj))
return pageObj

}


* user.go



package models

import (
“blogweb_gin/dao”
)

// 定义 模型 与 数据库中的表相对应

type User struct {
Id int
Username string
Password string
Status int // 0 正常状态, 1删除
CreateTime int64
}

//--------------数据库操作-----------------

// 插入新注册的用户
func InsertUser(user *User) (int64, error) {
return dao.ModifyDB(“insert into users(username,password,status,create_time) values (?,?,?,?)”,
user.Username, user.Password, user.Status, user.CreateTime)
}

// 根据用户名查询id
func QueryUserWithUsername(username string) int {
var user User
err := dao.QueryRowDB(&user, “select id from users where username=?”, username)
if err != nil {
return 0
}
return user.Id
}

//根据用户名和密码,查询id
func QueryUserWithParam(username, password string) int {
var user User
err := dao.QueryRowDB(&user, “select id from users where username=? and password=?”, username, password)
if err != nil {
return 0
}
return user.Id
}


* router.go



package routers

import (
“blogweb_gin/controllers”
“blogweb_gin/logger”
“blogweb_gin/middlewares”
“github.com/gin-contrib/sessions” // session包 定义了一套session操作的接口 类似于 database/sql
“github.com/gin-gonic/gin”
“html/template”
“time”

//"github.com/gin-contrib/sessions/cookie" // session具体存储的介质
"github.com/gin-contrib/sessions/redis" // session具体存储的介质
//"github.com/gin-contrib/sessions/memcached" // session具体存储的介质

// github.com/go-redis/redis --> go连接redis的一个第三方库

)

// 设置路由
func SetupRouter() *gin.Engine {
r := gin.New()

// 接管Gin框架的Logger模块 和 Recovery模块
r.Use(logger.GinLogger(logger.Logger), logger.GinRecovery(logger.Logger, true))

// 设置时间格式
r.SetFuncMap(template.FuncMap{
	"timeStr": func(timestamp int64) string {
		return time.Unix(timestamp, 0).Format("2006-01-02 15:04:05")
	},
})

// 配置静态文件
r.Static("/static", "static")

// 配置模板
r.LoadHTMLGlob("views/\*")

// 设置session 和中间件middleware
store, \_ := redis.NewStore(10, "tcp", "127.0.0.1:6379", "", []byte("secret"))
r.Use(sessions.Sessions("mysession", store))

// 登录注册 无需认证
{
	r.GET("/register", controllers.RegisterGet)
	r.POST("/register", controllers.RegisterPost)

	r.GET("/login", controllers.LoginGet)
	r.POST("/login", controllers.LoginPost)

	// 获取阅读排行榜前几
	r.GET("/article/top/:n", controllers.ArticleTopN)
}

// 需要认证的一些路由
{
	// 路由组注册中间件
	basicAuthGroup := r.Group("/", middlewares.BasicAuth())
	basicAuthGroup.GET("/home", controllers.HomeGet)
	basicAuthGroup.GET("/", controllers.IndexGet)
	basicAuthGroup.GET("/logout", controllers.LogoutHandler)

	//路由组
	article := basicAuthGroup.Group("/article")
	{
		// 写文章
		article.GET("/add", controllers.AddArticleGet)
		article.POST("/add", controllers.AddArticlePost)

		// 文章详情
		article.GET("/show/:id", controllers.ShowArticleGet)

		// 更新文章
		article.GET("/update", controllers.UpdateArticleGet)
		article.POST("/update", controllers.UpdateArticlePost)

		// 删除文章
		article.GET("/delete", controllers.DeleteArticle)

	}

	// 相册
	basicAuthGroup.GET("/album", controllers.AlbumGet)

	// 文件上传
	basicAuthGroup.POST("/upload", controllers.UploadPost)
}

return r

}


#### utils


* tools.go



package utils

import (
“crypto/md5”
“fmt”
“time”
)

const (
secret = “你猜不到的东西”
)

//传入的数据不一样,那么MD5后的32位长度的数据肯定会不一样
func MD5(str string) string {
md5str := fmt.Sprintf(“%x”, md5.Sum(append([]byte(str), []byte(secret)…)))
return md5str
}

//将传入的时间戳转为时间
func SwitchTimeStampToStr(timeStamp int64) string {
t := time.Unix(timeStamp, 0)
return t.Format(“2006-01-02 15:04:05”)
}


#### 入口函数


* main.go



package main

import (
“blogweb_gin/config”
“blogweb_gin/dao”
“blogweb_gin/logger”
“blogweb_gin/routers”
“fmt”
)

func main() {
// 用conf/conf.json初始化
//if len(os.Args) < 2 {
// return
//}
//if err := config.Init(os.Args[1]); err != nil {
// fmt.Printf(“config.Init failed, err:%v\n”, err)
// return
//}

// 调试方便,先字符串初始化
s := `{

“server”: {
“port”: 8080
},
“mysql”: {
“host”: “127.0.0.1”,
“port”: 3306,
“db”: “gin_blog”,
“username”: “root”,
“password”: “wxlzs999”
},
“redis”: {
“host”: “127.0.0.1”,
“port”: 6379,
“db”: 0,
“password”: “”
},
“log”:{
“level”: “debug”,
“filename”: “log/gin_blog.log”,
“maxsize”: 500,
“max_age”: 7,
“max_backups”: 10
}
}`

// 初始化Config的全局变量
if err := config.InitFromStr(s); err != nil {
	fmt.Printf("config.Init failed, err:%v\n", err)
	return
}

// 初始化日志模块
if err := logger.InitLogger(config.Conf.LogConfig); err != nil {
	fmt.Printf("init logger failed, err:%v\n", err)
	return
}

// 初始化Mysql数据库
if err := dao.InitMySQL(config.Conf.MySQLConfig); err != nil {
	fmt.Printf("init redis failed, err:%v\n", err)
	return
}

img
img

网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。

需要这份系统化的资料的朋友,可以添加戳这里获取

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

s := { "server": { "port": 8080 }, "mysql": { "host": "127.0.0.1", "port": 3306, "db": "gin\_blog", "username": "root", "password": "wxlzs999" }, "redis": { "host": "127.0.0.1", "port": 6379, "db": 0, "password": "" }, "log":{ "level": "debug", "filename": "log/gin\_blog.log", "maxsize": 500, "max\_age": 7, "max\_backups": 10 } }

// 初始化Config的全局变量
if err := config.InitFromStr(s); err != nil {
	fmt.Printf("config.Init failed, err:%v\n", err)
	return
}

// 初始化日志模块
if err := logger.InitLogger(config.Conf.LogConfig); err != nil {
	fmt.Printf("init logger failed, err:%v\n", err)
	return
}

// 初始化Mysql数据库
if err := dao.InitMySQL(config.Conf.MySQLConfig); err != nil {
	fmt.Printf("init redis failed, err:%v\n", err)
	return
}

[外链图片转存中…(img-GQCVIoiS-1715815408872)]
[外链图片转存中…(img-wDZjrsiv-1715815408872)]

网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。

需要这份系统化的资料的朋友,可以添加戳这里获取

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

  • 27
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值