• Golang实现小型CMS内容管理功能(一):Gin框架搭配Gorm实现增删查改功能


    我自己开发了一款在线客服系统,最近一直琢磨把客服系统官网做好。因为访客来的人不少,大部分人可能就是看看官网界面就走了,怎样把这些访客留存下来,去测试试用客服系统,是我一直琢磨的问题。

    官网是一个企业的门面,也是一个系统的门面,还是需要把门面的内容整理总结一下,让大家能清楚看到系统的功能。网站的内容少,那么搜索引擎收录的就少,这样会导致网站的权重不高,搜索排名比较低。

    因此要简单的加上一个小型的内容管理功能。

    大家感兴趣也可以去看看唯一客服系统

    设计数据库

    很简单的两张表,分类表和内容表

    1. DROP TABLE IF EXISTS `cms_cate`;
    2. CREATE TABLE `cms_cate` (
    3. `id` int(11) NOT NULL AUTO_INCREMENT,
    4. `cat_name` varchar(50) NOT NULL DEFAULT '' COMMENT '分类名称',
    5. `created_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
    6. PRIMARY KEY (`id`) COMMENT '自增主键索引'
    7. ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT 'CMS分类表';
    8. DROP TABLE IF EXISTS `cms_news`;
    9. CREATE TABLE `cms_news` (
    10. `id` int(11) NOT NULL AUTO_INCREMENT,
    11. `title` varchar(500) COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT '标题',
    12. `content` text COLLATE utf8mb4_general_ci COMMENT '内容',
    13. `cat_id` int(11) NOT NULL DEFAULT '0' COMMENT '分类ID',
    14. `created_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
    15. PRIMARY KEY (`id`) COMMENT '自增主键索引'
    16. ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT 'CMS内容表';

    编写数据库操作gorm Model部分

    设计两个结构体

    1. type CmsCate struct {
    2. Id uint `json:"id"`
    3. CatName string `json:"cat_name"`
    4. CreatedAt types.Time `json:"created_at"`
    5. }
    6. type CmsNews struct {
    7. Id uint `json:"id"`
    8. Title string `json:"title"`
    9. Content string `json:"content"`
    10. CreatedAt types.Time `json:"created_at"`
    11. }

    types.Time类型是我对time.Time类型的包装,用于在序列化为json的时候,可以格式化时间。

    默认的Time类型,JSON序列化出来后是UTC格式,不符合观看习惯

    1. package types
    2. import (
    3. "database/sql/driver"
    4. "fmt"
    5. "time"
    6. )
    7. type Time struct {
    8. time.Time
    9. }
    10. func (t Time) MarshalJSON() ([]byte, error) {
    11. localTime := t.Format("2006-01-02 15:04:05")
    12. return []byte(fmt.Sprintf(`"%s"`, localTime)), nil
    13. }
    14. func (t Time) Value() (driver.Value, error) {
    15. var zeroTime time.Time
    16. if t.Time.UnixNano() == zeroTime.UnixNano() {
    17. return nil, nil
    18. }
    19. return t.Time, nil
    20. }
    21. func (t *Time) Scan(v interface{}) error {
    22. value, ok := v.(time.Time)
    23. if ok {
    24. *t = Time{Time: value}
    25. return nil
    26. }
    27. return fmt.Errorf("can not convert %v to timestamp", v)
    28. }

    分类表和内容表的增删查改

    DB就是*gorm.DB类型,这是在链接数据库的时候,已经赋值好的全局变量

    1. /*内容表*/
    2. //根据条件查询条数
    3. func CountCmsNews(query interface{}, args ...interface{}) uint {
    4. var v uint
    5. DB.Table("cms_news").Where(query, args...).Count(&v)
    6. return v
    7. }
    8. //根据条件更新
    9. func (this *CmsNews) SaveCmsNews(query interface{}, args ...interface{}) error {
    10. db := DB.Table("cms_news").Where(query, args...).Update(this)
    11. return db.Error
    12. }
    13. //增加数据
    14. func (this *CmsNews) AddCmsNews() error {
    15. return DB.Create(this).Error
    16. }
    17. //根据条件查询分页列表
    18. func FindCmsNews(page, pagesize int, query interface{}, args ...interface{}) []CmsNews {
    19. offset := (page - 1) * pagesize
    20. var res []CmsNews
    21. DB.Table("cms_news").Where(query, args...).Order("id desc").Offset(offset).Limit(pagesize).Find(&res)
    22. return res
    23. }
    24. //根据条件删除
    25. func DelCmsNews(query interface{}, args ...interface{}) error {
    26. return DB.Where(query, args...).Delete(&CmsNews{}).Error
    27. }
    28. /*分类表*/
    29. //根据条件分类
    30. func (this *CmsCate) SaveCmsCate(query interface{}, args ...interface{}) error {
    31. db := DB.Table("cms_cate").Where(query, args...).Update(this)
    32. return db.Error
    33. }
    34. //增加分类
    35. func (this *CmsCate) AddCmsCate() error {
    36. return DB.Create(this).Error
    37. }
    38. //根据条件查询分类列表
    39. func FindCmsCate(page, pagesize int, query interface{}, args ...interface{}) []CmsCate {
    40. offset := (page - 1) * pagesize
    41. var res []CmsCate
    42. DB.Table("cms_cate").Where(query, args...).Order("id desc").Offset(offset).Limit(pagesize).Find(&res)
    43. return res
    44. }
    45. //根据条件删除分类
    46. func DelCmsCate(query interface{}, args ...interface{}) error {
    47. return DB.Where(query, args...).Delete(&CmsCate{}).Error
    48. }

    编写gin路由处理部分

    gin路由入口

    1. //系统相关
    2. systemGroup := engine.Group("/system")
    3. systemGroup.Use()
    4. {
    5. //分类列表
    6. systemGroup.GET("/cmsCate", controller.GetCmsCate)
    7. //删除分类
    8. systemGroup.GET("/delCmsCate", controller.DelCmsCate)
    9. //增加或编辑分类
    10. systemGroup.POST("/cmsCate", controller.PostCmsCate)
    11. //CMS内容列表
    12. systemGroup.GET("/cmsNews", controller.GetCmsNews)
    13. //增加或编辑内容
    14. systemGroup.POST("/cmsNews", controller.PostCmsNews)
    15. //删除内容
    16. systemGroup.GET("/delCmsNews", controller.DelCmsNews)
    17. }

    gin框架路由处理逻辑

    返回参数部分,我进行了小的封装,可以参考去掉。

    只看调用model部分的处理逻辑

    1. package controller
    2. import (
    3. "github.com/gin-gonic/gin"
    4. "kefu/models"
    5. "kefu/types"
    6. "strconv"
    7. )
    8. type CmsCateForm struct {
    9. Id uint `form:"id" json:"id" uri:"id" xml:"id"`
    10. CateName string `form:"cate_name" json:"cate_name" uri:"cate_name" xml:"cate_name" binding:"required"`
    11. }
    12. //分类列表(暂不分页)
    13. func GetCmsCate(c *gin.Context) {
    14. list := models.FindCmsCate(1, 1000, "")
    15. c.JSON(200, gin.H{
    16. "code": types.ApiCode.SUCCESS,
    17. "msg": types.ApiCode.GetMessage(types.ApiCode.SUCCESS),
    18. "result": list,
    19. })
    20. }
    21. //编辑CMS分类
    22. func PostCmsCate(c *gin.Context) {
    23. var form CmsCateForm
    24. err := c.Bind(&form)
    25. if err != nil {
    26. c.JSON(200, gin.H{
    27. "code": types.ApiCode.FAILED,
    28. "msg": types.ApiCode.GetMessage(types.ApiCode.INVALID),
    29. "result": err.Error(),
    30. })
    31. return
    32. }
    33. modelCms := &models.CmsCate{
    34. Id: form.Id,
    35. CatName: form.CateName,
    36. }
    37. //添加分类
    38. if form.Id == 0 {
    39. err := modelCms.AddCmsCate()
    40. if err != nil {
    41. c.JSON(200, gin.H{
    42. "code": types.ApiCode.FAILED,
    43. "msg": err.Error(),
    44. })
    45. return
    46. }
    47. } else {
    48. //修改分类
    49. err := modelCms.SaveCmsCate("id = ?", form.Id)
    50. if err != nil {
    51. c.JSON(200, gin.H{
    52. "code": types.ApiCode.FAILED,
    53. "msg": err.Error(),
    54. })
    55. return
    56. }
    57. }
    58. c.JSON(200, gin.H{
    59. "code": types.ApiCode.SUCCESS,
    60. "msg": types.ApiCode.GetMessage(types.ApiCode.SUCCESS),
    61. })
    62. }
    63. type CmsNewsForm struct {
    64. Id uint `form:"id" json:"id" uri:"id" xml:"id"`
    65. CateId string `form:"cate_id" json:"cate_id" uri:"cate_id" xml:"cate_id" binding:"required"`
    66. Content string `form:"content" json:"content" uri:"content" xml:"content" binding:"required"`
    67. Title string `form:"title" json:"title" uri:"title" xml:"title" binding:"required"`
    68. }
    69. //CMS内容列表
    70. func GetCmsNews(c *gin.Context) {
    71. //分页处理
    72. page, _ := strconv.Atoi(c.Query("page"))
    73. if page <= 0 {
    74. page = 1
    75. }
    76. pagesize, _ := strconv.Atoi(c.Query("pagesize"))
    77. if pagesize <= 0 || pagesize > 50 {
    78. pagesize = 10
    79. }
    80. //判断分类ID条件
    81. catId := c.Query("cat_id")
    82. query := "1=1 "
    83. args := make([]interface{}, 0)
    84. if catId != "" {
    85. query += "and cat_id = ? "
    86. args = append(args, catId)
    87. }
    88. //分页查询
    89. count := models.CountCmsNews(query, args...)
    90. list := models.FindCmsNews(page, pagesize, query, args...)
    91. c.JSON(200, gin.H{
    92. "code": types.ApiCode.SUCCESS,
    93. "msg": types.ApiCode.GetMessage(types.ApiCode.SUCCESS),
    94. "result": gin.H{
    95. "list": list,
    96. "count": count,
    97. "pagesize": pagesize,
    98. "page": page,
    99. },
    100. })
    101. }
    102. //编辑CMS内容
    103. func PostCmsNews(c *gin.Context) {
    104. var form CmsNewsForm
    105. err := c.Bind(&form)
    106. if err != nil {
    107. c.JSON(200, gin.H{
    108. "code": types.ApiCode.FAILED,
    109. "msg": types.ApiCode.GetMessage(types.ApiCode.INVALID),
    110. "result": err.Error(),
    111. })
    112. return
    113. }
    114. modelCms := &models.CmsNews{
    115. Id: form.Id,
    116. CatId: form.CateId,
    117. Title: form.Title,
    118. }
    119. //添加
    120. if form.Id == 0 {
    121. err := modelCms.AddCmsNews()
    122. if err != nil {
    123. c.JSON(200, gin.H{
    124. "code": types.ApiCode.FAILED,
    125. "msg": err.Error(),
    126. })
    127. return
    128. }
    129. } else {
    130. //修改
    131. err := modelCms.SaveCmsNews("id = ?", form.Id)
    132. if err != nil {
    133. c.JSON(200, gin.H{
    134. "code": types.ApiCode.FAILED,
    135. "msg": err.Error(),
    136. })
    137. return
    138. }
    139. }
    140. c.JSON(200, gin.H{
    141. "code": types.ApiCode.SUCCESS,
    142. "msg": types.ApiCode.GetMessage(types.ApiCode.SUCCESS),
    143. })
    144. }
    145. //删除分类
    146. func DelCmsCate(c *gin.Context) {
    147. id := c.Query("id")
    148. err := models.DelCmsCate("id = ?", id)
    149. if err != nil {
    150. c.JSON(200, gin.H{
    151. "code": types.ApiCode.FAILED,
    152. "msg": err.Error(),
    153. })
    154. return
    155. }
    156. c.JSON(200, gin.H{
    157. "code": types.ApiCode.SUCCESS,
    158. "msg": types.ApiCode.GetMessage(types.ApiCode.SUCCESS),
    159. })
    160. }
    161. //删除内容
    162. func DelCmsNews(c *gin.Context) {
    163. id := c.Query("id")
    164. err := models.DelCmsNews("id = ?", id)
    165. if err != nil {
    166. c.JSON(200, gin.H{
    167. "code": types.ApiCode.FAILED,
    168. "msg": err.Error(),
    169. })
    170. return
    171. }
    172. c.JSON(200, gin.H{
    173. "code": types.ApiCode.SUCCESS,
    174. "msg": types.ApiCode.GetMessage(types.ApiCode.SUCCESS),
    175. })
    176. }

    可以使用接口测试工具,对接口进行测试

  • 相关阅读:
    使用 Watt Toolkit (原名 Steam++)加速github访问
    第十四届蓝桥杯省赛C/C++大学B组真题-飞机降落
    string类的使用方式的介绍
    http模块中----------req请求对象-req.url req.method 与客户端请求相关
    圆环进度条 两种实现方式
    java 读取pdf文件内容
    营丘福稻品牌山东大米 国稻种芯·中国水稻节:淄博高青招牌
    SpringBoot@Profile详解
    一些常用到的git命令
    Selenium —— 网页frame与多窗口处理!
  • 原文地址:https://blog.csdn.net/taoshihan/article/details/127943854