从零开始之golong项目 :做一个属于自己的博客

(一)项目框架 

  采用gin+gorm框架 用 gin自带的模板来创建前后端页面

Gin 是一个用于构建 HTTP 服务器的 web 框架。 它提供了一个简单易用的 API,用于处理 HTTP 请求和响应、路由、中间件和其他常见的 web 应用程序所需的功能。 它以其高性能和简约为特点,提供了轻量级和灵活的解决方案来构建 web 服务器。 Gorm 是 Go 的一个 ORM(对象关系映射)库。 它提供了一个简单易用的 API,用于与数据库交互、处理数据库迁移和执行常见的数据库操作,如查询、插入、更新和删除记录。 它支持多种数据库后端,包括 MySQL、PostgreSQL、SQLite 等。 总而言之, Gin 是用于处理 HTTP 请求和响应、路由、中间件和其他与网络相关的东西的 web 框架,而 Gorm 则是用于与数据库交互并执行常见数据库操作的 ORM 库。

(二)初始化项目

(1)编程工具我们采用vscode

       

  

新建blog文件夹 用cmd打开 在cmd中 输出

code .

即可进入

 (2)初始化项目

   在控制台输入:

go mod init Blog


go mod init用法是在当前目录项目下初始化mod123,用于管理项目的依赖模块。go mod init后面可以跟项目名

(三)项目架构

 (1)创建静态资源文件夹

创建静态资源文件夹assets,在改文件夹中再创建两个文件夹backend和forntend,分别保存前端静态资源和后端静态资源

  (2)创建项目模板文件夹

创建保存项目末班的文件夹templates,在其下面创建两个文件夹backend和forntend分别保存前端模板和后端模板

  (3)创建公共类文件夹

创建文件夹common,在其下面创建config,global,initialize,middleware和utils

    (4)创建控制器文件夹

创建控制器文件夹controller

      (5)创建服务文件夹servic

创建服务器文件夹service

(四) 准备数据库和模型

(1)创建一个数据库 名称为blog_db

create database blog_db

 (2)创建一个表,该表为博客分类频道

CREATE TABLE `channel` (
  `id` bigint(20) NOT NULL AUTO_INCREMENT,
  `title` varchar(75) NOT NULL,
  `slug` varchar(100)  NOT NULL,
  `content` text ,
  `status` int(5) NOT NULL,
  `weight` int(11) NOT NULL,
  PRIMARY KEY (`id`)
);

(3) ​创建对应模型

在models文件夹下面,创建名称为channel.go的文件

内容如下:

package models

// 数据库,用户数据映射模型
type Channel struct {
	Id      uint64 `form:"id" gorm:"primaryKey"`
	Title   string `form:"title" gorm:"title"`
	Slug    string `form:"slug" gorm:"slug"`
	Content string `form:"content" gorm:"content"`
	Status  int    `form:"status" gorm:"status"`
	Weight  int    `form:"weight" gorm:"weight"`
}

(五)创建项目配置

(1)安装读取配置文件包viper

github.com/spf13/viper

(2)在根目录下创建配置文件config.yaml

# 启动端口号
server:
  post: 8001

# MySQL数据源配置
mysql:
  username: root      # 修改为自己数据库的用户名
  password: 123456    # 修改为自己数据库的密码
  url: tcp(localhost:3306)/blog_db?charset=utf8&parseTime=True&loc=Local

(3)创建配置类

在common下面的config下面创建config.go

package config

// 组合全部配置模型
type Config struct {
	Server Server `mapstructure:"server"`
	Mysql  Mysql  `mapstructure:"mysql"`
}

// 服务启动端口号配置
type Server struct {
	Post string `mapstructure:"post"`
}

// MySQL数据源配置
type Mysql struct {
	Username string `mapstructure:"username"`
	Password string `mapstructure:"password"`
	Url      string `mapstructure:"url"`
}

(六)创建数据库连接

(1)在common 下的initialize目录下面创建db.go文件

package initialize

import (
	"fmt"
	"time"

	"blog/common/global"

	"gorm.io/driver/mysql"
	"gorm.io/gorm"
	"gorm.io/gorm/schema"
)

// Mysql 配置MySQl数据库
func Mysql() {
	m := global.Config.Mysql
	var dsn = fmt.Sprintf("%s:%s@%s", m.Username, m.Password, m.Url)
	db, err := gorm.Open(mysql.Open(dsn), &gorm.Config{
		NamingStrategy: schema.NamingStrategy{SingularTable: true},
	})
	if err != nil {
		fmt.Printf("mysql error: %s", err)
		return
	}
	sqlDb, err := db.DB()
	if err != nil {
		fmt.Printf("mysql error: %s", err)
	}
	sqlDb.SetMaxIdleConns(10)
	sqlDb.SetMaxOpenConns(100)
	sqlDb.SetConnMaxLifetime(time.Hour)
	global.Db = db
}

(七)创建全局变量

 (1)在common下面的global目录下面创建global.go 

package global

import (
	"blog/common/config"

	"gorm.io/gorm"
)

var (
	Config config.Config
	Db     *gorm.DB
)

(八)创建操作频道service

(1)在service目录下创建channel.go文件

package service

import (
	"blog/common/global"
	"blog/models"
)

type ChannelService struct {
}

// 添加
func (u *ChannelService) AddChannel(channel models.Channel) int64 {
	return global.Db.Table("channel").Create(&channel).RowsAffected
}

// 删除
func (u *ChannelService) DelChannel(id int) int64 {
	return global.Db.Delete(&models.Channel{}, id).RowsAffected
}

// 修改
func (u *ChannelService) UpdateChannel(channel models.Channel) int64 {
	return global.Db.Updates(&channel).RowsAffected
}

// get
func (u *ChannelService) GetChannel(id int) models.Channel {
	var channel models.Channel
	global.Db.First(&channel, id)
	return channel
}

// get channel list
func (u *ChannelService) GetChannelList() []models.Channel {
	channelList := make([]models.Channel, 0)
	global.Db.Find(&channelList)
	return channelList
}

(九)创建操作博客频道controller

(1)安装模板安装包

go get github.com/foolin/gin-template

(2)在controller目录下创建channel.go文件

package controller

import (
	"blog/models"
	"blog/service"
	"fmt"
	"net/http"
	"strconv"

	gintemplate "github.com/foolin/gin-template"
	"github.com/gin-gonic/gin"
)

var channel service.ChannelService

// channel list
func ListChannel(c *gin.Context) {
	c2 := channel.GetChannelList()
	gintemplate.HTML(c, http.StatusOK, "channel/list", gin.H{"clist": c2})
}

// view channel
func ViewChannel(c *gin.Context) {
	sid, r := c.GetQuery("id")
	var chann models.Channel
	if r {
		id, _ := strconv.Atoi(sid)
		chann = channel.GetChannel(id)
	}
	gintemplate.HTML(c, http.StatusOK, "channel/view", gin.H{"channel": chann})
}

// delete channel
func DeleteChannel(c *gin.Context) {
	sid, _ := c.GetQuery("id")
	id, _ := strconv.Atoi(sid)
	channel.DelChannel(id)
	c.Redirect(http.StatusFound, "/admin/channel/list")
}

// 添加或者更新
func SaveChannel(c *gin.Context) {
	var chann models.Channel
	if err := c.ShouldBind(&chann); err != nil {
		fmt.Printf("err: %v\n", err)
		return
	}
	chann.Status, _ = strconv.Atoi(c.PostForm("status"))

	id, _ := c.GetPostForm("id")

	if id != "0" {
		channel.UpdateChannel(chann)
	} else {
		channel.AddChannel(chann)
	}
	c.Redirect(http.StatusFound, "/admin/channel/list")
}

(十)准备静态资源和模板文件

 (1)下载gin自带的前端框架和资源并替换原来的文件

  链接: https://pan.baidu.com/s/1JJx8OEbE5GqEbuJtKFdzqQ?pwd=wasd 提取码: wasd 

(十一)创建index信息传输文件

(1)在controller文件夹下创建index.go

package controller

import (
	"net/http"

	"blog/service"

	gintemplate "github.com/foolin/gin-template"
	"github.com/gin-gonic/gin"
)

var indexChannelService service.ChannelService

// 后台首页
func AdminIndex(c *gin.Context) {
	gintemplate.HTML(c, http.StatusOK, "index", nil)
}

// 前台首页
func Index(c *gin.Context) {
	channels := indexChannelService.GetChannelList()

	gintemplate.HTML(c, http.StatusOK, "index", gin.H{"clist": channels})
}

(十二)创建路由文件

在common/initialize 文件下创建router.go文件

package initialize

import (
	"fmt"

	"blog/common/global"
	"blog/controller"

	gintemplate "github.com/foolin/gin-template"
	"github.com/gin-gonic/gin"
)

func Router() {

	engine := gin.Default()

	// 静态资源请求映射

	engine.Static("/assets", "./assets")

	engine.HTMLRender = gintemplate.New(gintemplate.TemplateConfig{
		Root:         "templates/frontend",
		Extension:    ".html",
		Master:       "layouts/master",
		DisableCache: true,
	})

	// 前台
	engine.GET("/", controller.Index)

	mw := gintemplate.NewMiddleware(gintemplate.TemplateConfig{
		Root:         "templates/backend",
		Extension:    ".html",
		Master:       "layouts/master",
		DisableCache: true,
	})

	// 后台管理员前端接口
	web := engine.Group("/admin", mw)

	{
		// index
		web.GET("/", controller.AdminIndex)
	}

	{
		// 用户登录API
		web.GET("/channel/list", controller.ListChannel)
		web.GET("/channel/view", controller.ViewChannel)
		web.POST("/channel/save", controller.SaveChannel)
		web.GET("/channel/del", controller.DeleteChannel)
	}

	// 启动、监听端口
	post := fmt.Sprintf(":%s", global.Config.Server.Post)
	if err := engine.Run(post); err != nil {
		fmt.Printf("server start error: %s", err)
	}
}

(十三)创建启动类main.go

(1)在根目录下创建main.go

package main

import (
	"blog/common/initialize"
)

func main() {
	print("hello")
	initialize.LoadConfig()
	initialize.Mysql()
	initialize.Router()
}

(十四)检验频道前端

(1)启动main.go 检验前端成果 输入端口号 localhost:8001

(十五)准备发布博客post sql表

(1)代码如下:

CREATE TABLE `post` (
  `id` bigint NOT NULL AUTO_INCREMENT, # 主键
  `created_at` datetime DEFAULT NULL,  # 创建时间
  `updated_at` datetime DEFAULT NULL,  # 更新时间
  `deleted_at` datetime DEFAULT NULL,  # 删除时间
  `title` varchar(64) DEFAULT NULL,    # 标题
  `thumbnail` varchar(128) DEFAULT NULL, #缩略图
  `summary` varchar(140) DEFAULT NULL, # 摘要
  `content` longtext DEFAULT "", # 内容
  `author_id` bigint DEFAULT NULL, #作者id
  `channel_id` int DEFAULT NULL,   # 频道id
  `comments` int DEFAULT 0,        # 评论数
  `favors` int DEFAULT 0,          # 收藏数
  `featured` int DEFAULT 0,        # 特征
  `status` int DEFAULT 0,          # 状态
  `tags` varchar(64) DEFAULT NULL, # 标签
  `views` int DEFAULT 0,            # 浏览数
  `weight` int DEFAULT 0,           # 权重
  `url` varchar(500) DEFAULT "",    # 转载url
  PRIMARY KEY (`id`)
);

(十六)创建博客post对应模型

(1)在models文件夹下面创建post.go文件,内容如下:

package models

import "gorm.io/gorm"

// 数据库,博客数据映射模型
type Post struct {
	gorm.Model
	Title     string `gorm:"slug"`
	Thumbnail string `gorm:"thumbnail"`
	Summary   string `gorm:"summary"`
	Content   string `gorm:"content"`
	AuthorId  int    `gorm:"author_id"`
	ChannelId int    `gorm:"channel_id"`
	Comments  int    `gorm:"comments"`
	Favors    int    `gorm:"favors"`
	Featured  int    `gorm:"featured"`
	Status    int    `gorm:"status"`
	Tags      string `gorm:"tags"`
	Views     int    `gorm:"views"`
	Weight    int    `gorm:"weight"`
	Url       string `gorm:"url"`
}

(十七)创建博客post service 

(1)在service 目录下面创建一个post.go文件 来实现对post的curd

package service

import (
	"blog/common/global"
	"blog/models"
)

type PostService struct {
}

// 添加
func (p *PostService) AddPost(post models.Post) int64 {
	return global.Db.Table("post").Create(&post).RowsAffected
}

// 删除
func (p *PostService) DelPost(id int) int64 {
	return global.Db.Delete(&models.Post{}, id).RowsAffected
}

// 修改
func (p *PostService) UpdatePost(post models.Post) int64 {
	return global.Db.Updates(&post).RowsAffected
}

// get
func (p *PostService) GetPost(id int) models.Post {
	var post models.Post
	global.Db.First(&post, id)
	return post
}

// get post list
func (p *PostService) GetPostList() []models.Post {
	postList := make([]models.Post, 0)
	global.Db.Find(&postList)
	return postList
}

(十八)创建博客post对应控制器

(1)在controller 目录下面,创建post.go文件

package controller

import (
	"blog/common/stripmd"
	"blog/models"
	"blog/service"
	"fmt"
	"html/template"
	"net/http"
	"os"
	"path/filepath"
	"strconv"

	"github.com/gomarkdown/markdown"
	"github.com/gomarkdown/markdown/parser"

	gintemplate "github.com/foolin/gin-template"
	"github.com/gin-gonic/gin"
	"github.com/google/uuid"
	"github.com/spf13/cast"
)

var postService service.PostService
var channelService service.ChannelService

// post list
func ListPost(c *gin.Context) {
	plist := postService.GetPostList()
	gintemplate.HTML(c, http.StatusOK, "post/list", gin.H{"plist": plist})
}

// view post
func ViewPost(c *gin.Context) {
	sid, r := c.GetQuery("id")
	var post models.Post
	if r {
		id, _ := strconv.Atoi(sid)
		post = postService.GetPost(id)
	}
	clist := channelService.GetChannelList()
	gintemplate.HTML(c, http.StatusOK, "post/view", gin.H{"post": post, "clist": clist})
}

// delete post
func DeletePost(c *gin.Context) {
	sid, _ := c.GetQuery("id")
	id, _ := strconv.Atoi(sid)
	postService.DelPost(id)
	c.Redirect(http.StatusFound, "/admin/post/list")
}

// post detail 前端
func PostDetail(c *gin.Context) {
	sid := c.Param("id")
	id := cast.ToInt((sid))
	post := postService.GetPost(id)

	extensions := parser.CommonExtensions | parser.AutoHeadingIDs
	parser := parser.NewWithExtensions(extensions)

	md := []byte(post.Content)
	md = markdown.NormalizeNewlines(md)
	html := markdown.ToHTML(md, parser, nil)

	gintemplate.HTML(c, http.StatusOK, "/post/detail", gin.H{"post": post, "content": template.HTML(html)})
}

// 上传封面
func UploadThumbnails(c *gin.Context) {

	file, err := c.FormFile("file")
	if err != nil {
		c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{
			"message": "没有文件",
		})
		return
	}

	extension := filepath.Ext(file.Filename)
	newFileName := uuid.New().String() + extension
	// The file is received, so let's save it
	pwd, _ := os.Getwd() // 项目根路径
	filePath := fmt.Sprintf("%s/static/thumbnails/%s", pwd, newFileName)
	relativeFilePath := fmt.Sprintf("/static/thumbnails/%s", newFileName)

	if err := c.SaveUploadedFile(file, filePath); err != nil {
		c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{
			"message": "不能保存文件",
		})
		fmt.Printf("err: %v\n", err)
		return
	}
	c.String(http.StatusOK, relativeFilePath)

}

// 添加或者更新
func SavePost(c *gin.Context) {
	var post models.Post

	// id := c.PostForm("id")
	title := c.PostForm("title")
	thumbnail := c.PostForm("thumbnail")
	tags := c.PostForm("tags")
	content := c.PostForm("content")
	channelId := c.PostForm("channelId")

	// 摘要,去掉markdown格式
	summary := stripmd.Strip(content)
	l := len(summary)
	if l >= 200 {
		summary = summary[0:200]
	} else {
		summary = summary[0:l]
	}

	post.Title = title
	post.Thumbnail = thumbnail
	post.Tags = tags
	post.Content = content
	post.Summary = summary
	post.AuthorId = 1 // 实现注册登录后,动态获得
	post.ChannelId = cast.ToInt(channelId)

	if err := c.ShouldBind(&post); err != nil {
		fmt.Printf("err: %v\n", err)
		return
	}

	id := cast.ToInt(c.PostForm("id"))

	if id != 0 {
		postService.UpdatePost(post)
	} else {
		postService.AddPost(post)
	}
	c.Redirect(http.StatusFound, "/admin/post/list")
}

(十九)重置路由

(1)在router.go文件中加入post相关的路由

package initialize

import (
	"fmt"
	"net/http"

	"blog/common/global"
	"blog/controller"

	gintemplate "github.com/foolin/gin-template"
	"github.com/gin-gonic/gin"
)

func Router() {

	engine := gin.Default()

	// 静态资源请求映射

	engine.Static("/assets", "./assets")
	engine.StaticFS("/static", http.Dir("./static"))

	engine.HTMLRender = gintemplate.New(gintemplate.TemplateConfig{
		Root:         "templates/frontend",
		Extension:    ".html",
		Master:       "layouts/master",
		DisableCache: true,
	})

	// 前台
	engine.GET("/", controller.Index)
	engine.GET("/post/:id", controller.PostDetail)

	mw := gintemplate.NewMiddleware(gintemplate.TemplateConfig{
		Root:         "templates/backend",
		Extension:    ".html",
		Master:       "layouts/master",
		DisableCache: true,
	})

	// 后台管理员前端接口
	web := engine.Group("/admin", mw)

	{
		// index
		web.GET("/", controller.AdminIndex)

	}

	{
		// channel
		web.GET("/channel/list", controller.ListChannel)
		web.GET("/channel/view", controller.ViewChannel)
		web.POST("/channel/save", controller.SaveChannel)
		web.GET("/channel/del", controller.DeleteChannel)
	}

	{
		// post
		web.GET("/post/list", controller.ListPost)
		web.GET("/post/view", controller.ViewPost)
		web.POST("/post/save", controller.SavePost)
		web.GET("/post/del", controller.DeletePost)

		web.POST("/post/upload", controller.UploadThumbnails)

	}

	// 启动、监听端口
	post := fmt.Sprintf(":%s", global.Config.Server.Post)
	if err := engine.Run(post); err != nil {
		fmt.Printf("server start error: %s", err)
	}
}

(二十) 检验

http://localhost:8001/ 为首页

后台管理界面

频道操作

添加频道

博客列表

添加博客

实现页面

  • 13
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值