我自己开发了一款在线客服系统,最近一直琢磨把客服系统官网做好。因为访客来的人不少,大部分人可能就是看看官网界面就走了,怎样把这些访客留存下来,去测试试用客服系统,是我一直琢磨的问题。
官网是一个企业的门面,也是一个系统的门面,还是需要把门面的内容整理总结一下,让大家能清楚看到系统的功能。网站的内容少,那么搜索引擎收录的就少,这样会导致网站的权重不高,搜索排名比较低。
因此要简单的加上一个小型的内容管理功能。
大家感兴趣也可以去看看唯一客服系统
很简单的两张表,分类表和内容表
- DROP TABLE IF EXISTS `cms_cate`;
- CREATE TABLE `cms_cate` (
- `id` int(11) NOT NULL AUTO_INCREMENT,
- `cat_name` varchar(50) NOT NULL DEFAULT '' COMMENT '分类名称',
- `created_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
- PRIMARY KEY (`id`) COMMENT '自增主键索引'
- ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT 'CMS分类表';
- DROP TABLE IF EXISTS `cms_news`;
- CREATE TABLE `cms_news` (
- `id` int(11) NOT NULL AUTO_INCREMENT,
- `title` varchar(500) COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT '标题',
- `content` text COLLATE utf8mb4_general_ci COMMENT '内容',
- `cat_id` int(11) NOT NULL DEFAULT '0' COMMENT '分类ID',
- `created_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
- PRIMARY KEY (`id`) COMMENT '自增主键索引'
- ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT 'CMS内容表';
设计两个结构体
- type CmsCate struct {
- Id uint `json:"id"`
- CatName string `json:"cat_name"`
- CreatedAt types.Time `json:"created_at"`
- }
- type CmsNews struct {
- Id uint `json:"id"`
- Title string `json:"title"`
- Content string `json:"content"`
- CreatedAt types.Time `json:"created_at"`
- }
types.Time类型是我对time.Time类型的包装,用于在序列化为json的时候,可以格式化时间。
默认的Time类型,JSON序列化出来后是UTC格式,不符合观看习惯
- package types
-
- import (
- "database/sql/driver"
- "fmt"
- "time"
- )
-
- type Time struct {
- time.Time
- }
-
- func (t Time) MarshalJSON() ([]byte, error) {
- localTime := t.Format("2006-01-02 15:04:05")
- return []byte(fmt.Sprintf(`"%s"`, localTime)), nil
- }
- func (t Time) Value() (driver.Value, error) {
- var zeroTime time.Time
- if t.Time.UnixNano() == zeroTime.UnixNano() {
- return nil, nil
- }
- return t.Time, nil
- }
- func (t *Time) Scan(v interface{}) error {
- value, ok := v.(time.Time)
- if ok {
- *t = Time{Time: value}
- return nil
- }
- return fmt.Errorf("can not convert %v to timestamp", v)
- }
DB就是*gorm.DB类型,这是在链接数据库的时候,已经赋值好的全局变量
- /*内容表*/
- //根据条件查询条数
- func CountCmsNews(query interface{}, args ...interface{}) uint {
- var v uint
- DB.Table("cms_news").Where(query, args...).Count(&v)
- return v
- }
-
- //根据条件更新
- func (this *CmsNews) SaveCmsNews(query interface{}, args ...interface{}) error {
- db := DB.Table("cms_news").Where(query, args...).Update(this)
- return db.Error
- }
-
- //增加数据
- func (this *CmsNews) AddCmsNews() error {
- return DB.Create(this).Error
- }
-
- //根据条件查询分页列表
- func FindCmsNews(page, pagesize int, query interface{}, args ...interface{}) []CmsNews {
- offset := (page - 1) * pagesize
- var res []CmsNews
- DB.Table("cms_news").Where(query, args...).Order("id desc").Offset(offset).Limit(pagesize).Find(&res)
- return res
- }
-
- //根据条件删除
- func DelCmsNews(query interface{}, args ...interface{}) error {
- return DB.Where(query, args...).Delete(&CmsNews{}).Error
- }
-
- /*分类表*/
- //根据条件分类
- func (this *CmsCate) SaveCmsCate(query interface{}, args ...interface{}) error {
- db := DB.Table("cms_cate").Where(query, args...).Update(this)
- return db.Error
- }
-
- //增加分类
- func (this *CmsCate) AddCmsCate() error {
- return DB.Create(this).Error
- }
-
- //根据条件查询分类列表
- func FindCmsCate(page, pagesize int, query interface{}, args ...interface{}) []CmsCate {
- offset := (page - 1) * pagesize
- var res []CmsCate
- DB.Table("cms_cate").Where(query, args...).Order("id desc").Offset(offset).Limit(pagesize).Find(&res)
- return res
- }
-
- //根据条件删除分类
- func DelCmsCate(query interface{}, args ...interface{}) error {
- return DB.Where(query, args...).Delete(&CmsCate{}).Error
- }
gin路由入口
- //系统相关
- systemGroup := engine.Group("/system")
- systemGroup.Use()
- {
- //分类列表
- systemGroup.GET("/cmsCate", controller.GetCmsCate)
- //删除分类
- systemGroup.GET("/delCmsCate", controller.DelCmsCate)
- //增加或编辑分类
- systemGroup.POST("/cmsCate", controller.PostCmsCate)
- //CMS内容列表
- systemGroup.GET("/cmsNews", controller.GetCmsNews)
- //增加或编辑内容
- systemGroup.POST("/cmsNews", controller.PostCmsNews)
- //删除内容
- systemGroup.GET("/delCmsNews", controller.DelCmsNews)
-
- }
gin框架路由处理逻辑
返回参数部分,我进行了小的封装,可以参考去掉。
只看调用model部分的处理逻辑
- package controller
-
- import (
- "github.com/gin-gonic/gin"
- "kefu/models"
- "kefu/types"
- "strconv"
- )
-
- type CmsCateForm struct {
- Id uint `form:"id" json:"id" uri:"id" xml:"id"`
- CateName string `form:"cate_name" json:"cate_name" uri:"cate_name" xml:"cate_name" binding:"required"`
- }
-
- //分类列表(暂不分页)
- func GetCmsCate(c *gin.Context) {
- list := models.FindCmsCate(1, 1000, "")
- c.JSON(200, gin.H{
- "code": types.ApiCode.SUCCESS,
- "msg": types.ApiCode.GetMessage(types.ApiCode.SUCCESS),
- "result": list,
- })
- }
-
- //编辑CMS分类
- func PostCmsCate(c *gin.Context) {
- var form CmsCateForm
- err := c.Bind(&form)
-
- if err != nil {
- c.JSON(200, gin.H{
- "code": types.ApiCode.FAILED,
- "msg": types.ApiCode.GetMessage(types.ApiCode.INVALID),
- "result": err.Error(),
- })
- return
- }
- modelCms := &models.CmsCate{
- Id: form.Id,
- CatName: form.CateName,
- }
- //添加分类
- if form.Id == 0 {
- err := modelCms.AddCmsCate()
- if err != nil {
- c.JSON(200, gin.H{
- "code": types.ApiCode.FAILED,
- "msg": err.Error(),
- })
- return
- }
- } else {
- //修改分类
- err := modelCms.SaveCmsCate("id = ?", form.Id)
- if err != nil {
- c.JSON(200, gin.H{
- "code": types.ApiCode.FAILED,
- "msg": err.Error(),
- })
- return
- }
- }
-
- c.JSON(200, gin.H{
- "code": types.ApiCode.SUCCESS,
- "msg": types.ApiCode.GetMessage(types.ApiCode.SUCCESS),
- })
- }
-
- type CmsNewsForm struct {
- Id uint `form:"id" json:"id" uri:"id" xml:"id"`
- CateId string `form:"cate_id" json:"cate_id" uri:"cate_id" xml:"cate_id" binding:"required"`
- Content string `form:"content" json:"content" uri:"content" xml:"content" binding:"required"`
- Title string `form:"title" json:"title" uri:"title" xml:"title" binding:"required"`
- }
-
- //CMS内容列表
- func GetCmsNews(c *gin.Context) {
- //分页处理
- page, _ := strconv.Atoi(c.Query("page"))
- if page <= 0 {
- page = 1
- }
- pagesize, _ := strconv.Atoi(c.Query("pagesize"))
- if pagesize <= 0 || pagesize > 50 {
- pagesize = 10
- }
- //判断分类ID条件
- catId := c.Query("cat_id")
- query := "1=1 "
- args := make([]interface{}, 0)
-
- if catId != "" {
- query += "and cat_id = ? "
- args = append(args, catId)
- }
- //分页查询
- count := models.CountCmsNews(query, args...)
- list := models.FindCmsNews(page, pagesize, query, args...)
-
- c.JSON(200, gin.H{
- "code": types.ApiCode.SUCCESS,
- "msg": types.ApiCode.GetMessage(types.ApiCode.SUCCESS),
- "result": gin.H{
- "list": list,
- "count": count,
- "pagesize": pagesize,
- "page": page,
- },
- })
- }
-
- //编辑CMS内容
- func PostCmsNews(c *gin.Context) {
- var form CmsNewsForm
- err := c.Bind(&form)
-
- if err != nil {
- c.JSON(200, gin.H{
- "code": types.ApiCode.FAILED,
- "msg": types.ApiCode.GetMessage(types.ApiCode.INVALID),
- "result": err.Error(),
- })
- return
- }
- modelCms := &models.CmsNews{
- Id: form.Id,
- CatId: form.CateId,
- Title: form.Title,
- }
- //添加
- if form.Id == 0 {
- err := modelCms.AddCmsNews()
- if err != nil {
- c.JSON(200, gin.H{
- "code": types.ApiCode.FAILED,
- "msg": err.Error(),
- })
- return
- }
- } else {
- //修改
- err := modelCms.SaveCmsNews("id = ?", form.Id)
- if err != nil {
- c.JSON(200, gin.H{
- "code": types.ApiCode.FAILED,
- "msg": err.Error(),
- })
- return
- }
- }
-
- c.JSON(200, gin.H{
- "code": types.ApiCode.SUCCESS,
- "msg": types.ApiCode.GetMessage(types.ApiCode.SUCCESS),
- })
- }
-
- //删除分类
- func DelCmsCate(c *gin.Context) {
- id := c.Query("id")
- err := models.DelCmsCate("id = ?", id)
- if err != nil {
- c.JSON(200, gin.H{
- "code": types.ApiCode.FAILED,
- "msg": err.Error(),
- })
- return
- }
- c.JSON(200, gin.H{
- "code": types.ApiCode.SUCCESS,
- "msg": types.ApiCode.GetMessage(types.ApiCode.SUCCESS),
- })
- }
-
- //删除内容
- func DelCmsNews(c *gin.Context) {
- id := c.Query("id")
- err := models.DelCmsNews("id = ?", id)
- if err != nil {
- c.JSON(200, gin.H{
- "code": types.ApiCode.FAILED,
- "msg": err.Error(),
- })
- return
- }
- c.JSON(200, gin.H{
- "code": types.ApiCode.SUCCESS,
- "msg": types.ApiCode.GetMessage(types.ApiCode.SUCCESS),
- })
- }
可以使用接口测试工具,对接口进行测试
