(四)后台-对文章的增删改查操作

(四)后台-对文章的增删改查操作

文章目录

  • (四)后台-对文章的增删改查操作
  • 一、RESTFUL API
  • 二、路由处理
      • 1、/home 后台首页页面
      • 2、/upload 文件上传功能
    • 三、文章模块
      • 1、/list 获取文章列表 GetList
      • 2、/ 新增|编辑文章 SaveOrUpdate
      • 3、/top 更新文章置顶 UpdateTop
      • 4、/:id 文章详情 GetDetail
      • 5、/soft-delete 软删除文章 UpdateSoftDelete
      • 6、/ 物理删除文章 Delete
      • 7、/export 导出文章 Export
      • 8、/import 导入文章 Import

一、RESTFUL API

	auth.GET("/home", blogInfoAPI.GetHomeInfo) // 后台首页信息auth.POST("/upload", uploadAPI.UploadFile) // 文件上传// 文章模块articles := auth.Group("/article"){articles.GET("/list", articleAPI.GetList)                 // 文章列表articles.POST("", articleAPI.SaveOrUpdate)                // 新增/编辑文章articles.PUT("/top", articleAPI.UpdateTop)                // 更新文章置顶articles.GET("/:id", articleAPI.GetDetail)                // 文章详情articles.PUT("/soft-delete", articleAPI.UpdateSoftDelete) // 软删除文章articles.DELETE("", articleAPI.Delete)                    // 物理删除文章articles.POST("/export", articleAPI.Export)               // 导出文章articles.POST("/import", articleAPI.Import)               // 导入文章}

二、路由处理

1、/home 后台首页页面

func (*BlogInfo) GetHomeInfo(c *gin.Context) {db := GetDB(c)rdb := GetRDB(c)// 获得article数量articleCount, err := model.Count(db, &model.Article{}, "status = ? AND is_delete = ?", 1, 0)if err != nil {ReturnError(c, g2.ErrDbOp, err)return}// 获得user数量userCount, err := model.Count(db, &model.UserInfo{})if err != nil {ReturnError(c, g2.ErrDbOp, err)return}// 获得message数量messageCount, err := model.Count(db, &model.Message{})if err != nil {ReturnError(c, g2.ErrDbOp, err)return}// 获得留言数量viewCount, err := rdb.Get(rctx, g2.VIEW_COUNT).Int()if err != nil && err != redis.Nil {ReturnError(c, g2.ErrRedisOp, err)return}ReturnSuccess(c, BlogHomeVO{ArticleCount: articleCount,UserCount:    userCount,MessageCount: messageCount,ViewCount:    viewCount,})
}

2、/upload 文件上传功能

1.主功能函数

func (*Upload) UploadFile(c *gin.Context) {_, fileHeader, err := c.Request.FormFile("file")if err != nil {ReturnError(c, g.ErrFileReceive, err)return}oss := upload.NewOSS()filePath, _, err := oss.UploadFile(fileHeader)fmt.Println(filePath)if err != nil {ReturnError(c, g.ErrFileUpload, err)return}ReturnSuccess(c, filePath)
}

2.使用七牛云上传

// 七牛云文件上传
type Qiniu struct{}func (*Qiniu) UploadFile(file *multipart.FileHeader) (filePath, fileName string, err error) {putPolicy := storage.PutPolicy{Scope: g.GetConfig().Qiniu.Bucket,}mac := qbox.NewMac(g.GetConfig().Qiniu.AccessKey, g.GetConfig().Qiniu.SecretKey)upToken := putPolicy.UploadToken(mac)formUploader := storage.NewFormUploader(qiniuConfig())ret := storage.PutRet{}// 获取文件的 Content-TypecontentType := file.Header.Get("Content-Type")// 设置上传额外参数,包括文件名和文件的 Content-TypeputExtra := storage.PutExtra{Params:   map[string]string{"x:name": "github logo"},MimeType: contentType,}f, openError := file.Open()if openError != nil {return "", "", errors.New("function file.Open() Filed, err:" + openError.Error())}defer f.Close()// 文件名格式 建议保证唯一性fileKey := fmt.Sprintf("%d%s%s", time.Now().Unix(), utils.MD5(file.Filename), path.Ext(file.Filename))putErr := formUploader.Put(context.Background(), &ret, upToken, fileKey, f, file.Size, &putExtra)if putErr != nil {return "", "", errors.New("function formUploader.Put() Filed, err:" + putErr.Error())}return g.GetConfig().Qiniu.ImgPath + "/" + ret.Key, ret.Key, nil
}func (*Qiniu) DeleteFile(key string) error {mac := qbox.NewMac(g.GetConfig().Qiniu.AccessKey, g.GetConfig().Qiniu.SecretKey)cfg := qiniuConfig()bucketManager := storage.NewBucketManager(mac, cfg)if err := bucketManager.Delete(g.GetConfig().Qiniu.Bucket, key); err != nil {return errors.New("function bucketManager.Delete() Filed, err:" + err.Error())}return nil
}// 七牛云配置信息
func qiniuConfig() *storage.Config {cfg := storage.Config{UseHTTPS:      false,UseCdnDomains: false,}switch g.GetConfig().Qiniu.Zone { // 根据配置文件进行初始化空间对应的机房case "ZoneHuadong":cfg.Zone = &storage.ZoneHuadongcase "ZoneHuabei":cfg.Zone = &storage.ZoneHuabeicase "ZoneHuanan":cfg.Zone = &storage.ZoneHuanancase "ZoneBeimei":cfg.Zone = &storage.ZoneBeimeicase "ZoneXinjiapo":cfg.Zone = &storage.ZoneXinjiapo}return &cfg
}

三、文章模块

  • 对文章的查询 的结构体

    type ArticleQuery struct {PageQuery/*// pageQuery 结构体type PageQuery struct {Page    int    `form:"page_num"`  // 当前页数(从1开始)Size    int    `form:"page_size"` // 每页条数Keyword string `form:"keyword"`   // 搜索关键字}*/Title      string `form:"title"`CategoryId int    `form:"category_id"`TagId      int    `form:"tag_id"`Type       int    `form:"type"`Status     int    `form:"status"`IsDelete   *bool  `form:"is_delete"`
    }
    

1、/list 获取文章列表 GetList

func (*Article) GetList(c *gin.Context) {var query ArticleQueryif err := c.ShouldBindQuery(&query); err != nil {ReturnError(c, g2.ErrRequest, err)return}// 获取数据库和Redis连接db := GetDB(c)rdb := GetRDB(c)// 查询list, total, err := model.GetArticleList(db, query.Page, query.Size, query.Title, query.IsDelete, query.Status, query.Type, query.CategoryId, query.TagId)if err != nil {ReturnError(c, g2.ErrDbOp, err)return}// 获取点赞数和浏览量likeCountMap := rdb.HGetAll(rctx, g2.ARTICLE_LIKE_COUNT).Val()viewCountZ := rdb.ZRangeWithScores(rctx, g2.ARTICLE_VIEW_COUNT, 0, -1).Val()// 获取浏览量viewCountMap := make(map[int]int)for _, article := range viewCountZ {id, _ := strconv.Atoi(article.Member.(string))viewCountMap[id] = int(article.Score)}// 封装数据data := make([]ArticleVO, 0)for _, article := range list {likeCount, _ := strconv.Atoi(likeCountMap[strconv.Itoa(article.ID)])data = append(data, ArticleVO{Article:   article,LikeCount: likeCount,ViewCount: viewCountMap[article.ID],})}ReturnSuccess(c, PageResult[ArticleVO]{Size:  query.Size,Page:  query.Page,Total: total,List:  data,})
}

2、/ 新增|编辑文章 SaveOrUpdate

  • 路由功能函数

    func (*Article) SaveOrUpdate(c *gin.Context) {var req AddOrEditArticleReqif err := c.ShouldBindJSON(&req); err != nil {ReturnError(c, g2.ErrRequest, err)return}// 获取当前用户db := GetDB(c)auth, _ := CurrentUserAuth(c) // 获取当前登录的用户信息// 获取图片if req.Img == "" {req.Img = model.GetConfig(db, g2.CONFIG_ARTICLE_COVER) // 默认图片}// 获取类型if req.Type == 0 {req.Type = 1 // 默认为原创}// 保存文章article := model.Article{Model:       model.Model{ID: req.ID},Title:       req.Title,Desc:        req.Desc,Content:     req.Content,Img:         req.Img,Type:        req.Type,Status:      req.Status,OriginalUrl: req.OriginalUrl,IsTop:       req.IsTop,UserId:      auth.UserInfoId,}err := model.SaveOrUpdateArticle(db, &article, req.CategoryName, req.TagNames)if err != nil {ReturnError(c, g2.ErrDbOp, err)return}ReturnSuccess(c, article)
    }
    
  • 数据库底层操作信息

    // 新增/编辑文章, 同时根据 分类名称, 标签名称 维护关联表
    func SaveOrUpdateArticle(db *gorm.DB, article *Article, categoryName string, tagNames []string) error {return db.Transaction(func(tx *gorm.DB) error {// 分类不存在则创建category := Category{Name: categoryName}result := db.Model(&Category{}).Where("name", categoryName).FirstOrCreate(&category)if result.Error != nil {return result.Error}article.CategoryId = category.ID// 先 添加/更新 文章, 获取到其 IDif article.ID == 0 {result = db.Create(&article)} else {result = db.Model(&article).Where("id", article.ID).Updates(article)}if result.Error != nil {return result.Error}// 清空文章标签关联result = db.Delete(ArticleTag{}, "article_id", article.ID)if result.Error != nil {return result.Error}var articleTags []ArticleTagfor _, tagName := range tagNames {// 标签不存在则创建tag := Tag{Name: tagName}result := db.Model(&Tag{}).Where("name", tagName).FirstOrCreate(&tag)if result.Error != nil {return result.Error}articleTags = append(articleTags, ArticleTag{ArticleId: article.ID,TagId:     tag.ID,})}result = db.Create(&articleTags)return result.Error})
    }
    

3、/top 更新文章置顶 UpdateTop

// 修改置顶信息
func (*Article) UpdateTop(c *gin.Context) {var req UpdateArticleTopReqif err := c.ShouldBindJSON(&req); err != nil {ReturnError(c, g2.ErrRequest, err)return}err := model.UpdateArticleTop(GetDB(c), req.ID, req.IsTop)if err != nil {ReturnError(c, g2.ErrDbOp, err)return}ReturnSuccess(c, nil)
}// model 修改数据库置顶信息
func UpdateArticleTop(db *gorm.DB, id int, isTop bool) error {result := db.Model(&Article{Model: Model{ID: id}}).Update("is_top", isTop)return result.Error
}

4、/:id 文章详情 GetDetail

// 获取文章详细信息
func (*Article) GetDetail(c *gin.Context) {id, err := strconv.Atoi(c.Param("id"))if err != nil {ReturnError(c, g2.ErrRequest, err)return}article, err := model.GetArticle(GetDB(c), id)if err != nil {ReturnError(c, g2.ErrDbOp, err)return}ReturnSuccess(c, article)
}// 数据库根据 id 搜索这个文章信息
func GetArticle(db *gorm.DB, id int) (data *Article, err error) {result := db.Preload("Category").Preload("Tags").Where(Article{Model: Model{ID: id}}).First(&data)return data, result.Error
}

5、/soft-delete 软删除文章 UpdateSoftDelete

func (*Article) UpdateSoftDelete(c *gin.Context) {var req SoftDeleteReqif err := c.ShouldBindJSON(&req); err != nil {ReturnError(c, g2.ErrRequest, err)return}rows, err := model.UpdateArticleSoftDelete(GetDB(c), req.Ids, req.IsDelete)if err != nil {ReturnError(c, g2.ErrDbOp, err)return}ReturnSuccess(c, rows)
}// 数据库软删除信息,也就是说再回收站能够恢复的文件,所以说只需要修改软删除标志即可
// 软删除文章(修改)
func UpdateArticleSoftDelete(db *gorm.DB, ids []int, isDelete bool) (int64, error) {result := db.Model(Article{}).Where("id IN ?", ids).Update("is_delete", isDelete)if result.Error != nil {return 0, result.Error}return result.RowsAffected, nil
}

6、/ 物理删除文章 Delete

func (*Article) Delete(c *gin.Context) {var ids []intif err := c.ShouldBindJSON(&ids); err != nil {ReturnError(c, g2.ErrRequest, err)return}rows, err := model.DeleteArticle(GetDB(c), ids)if err != nil {ReturnError(c, g2.ErrDbOp, err)return}ReturnSuccess(c, rows)
}// 物理删除文章  把数据库里的信息删掉
func DeleteArticle(db *gorm.DB, ids []int) (int64, error) {// 删除 [文章-标签] 关联result := db.Where("article_id IN ?", ids).Delete(&ArticleTag{})if result.Error != nil {return 0, result.Error}// 删除 [文章]result = db.Where("id IN ?", ids).Delete(&Article{})if result.Error != nil {return 0, result.Error}return result.RowsAffected, nil
}

7、/export 导出文章 Export

8、/import 导入文章 Import

// 导入文章: 题目 + 内容
func (*Article) Import(c *gin.Context) {db := GetDB(c)auth, _ := CurrentUserAuth(c)_, fileHeader, err := c.Request.FormFile("file")if err != nil {ReturnError(c, g2.ErrFileReceive, err)return}fileName := fileHeader.Filenametitle := fileName[:len(fileName)-3]content, err := readFromFileHeader(fileHeader)if err != nil {ReturnError(c, g2.ErrFileReceive, err)return}defaultImg := model.GetConfig(db, g2.CONFIG_ARTICLE_COVER)err = model.ImportArticle(db, auth.ID, title, content, defaultImg)if err != nil {ReturnError(c, g2.ErrDbOp, err)return}ReturnSuccess(c, nil)
}// md 文件读取
func readFromFileHeader(file *multipart.FileHeader) (string, error) {open, err := file.Open()if err != nil {slog.Error("文件读取, 目标地址错误: ", err)return "", err}defer open.Close()all, err := io.ReadAll(open)if err != nil {slog.Error("文件读取失败: ", err)return "", err}return string(all), nil
}// 导入文章 也就是数据库新建文章信息
func ImportArticle(db *gorm.DB, userAuthId int, title, content, img string) error {article := Article{Title:   title,Content: content,Img:     img,Status:  STATUS_DRAFT,Type:    TYPE_ORIGINAL,UserId:  userAuthId,}result := db.Create(&article)return result.Error
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/diannao/4136.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

AI 提示词生成器

https://prompt-generator.ilovecoke.cc/参考 AI 提示词生成器

docker部署前端项目(三)简易迅速版本

前两个docker 部署都出现了 意外,通过dockerfile 文件操作的时候, 不是 npm 无法下载,就是 npm build 无法打包 总是困难重重,原因甚多,不是网络导致,就是版本不对, 原因可能是 node 版本和 npm…

运维 kubernetes(k8s)基础学习

一、容器相关 1、发展历程:主机–虚拟机–容器 主机类似别墅的概念,一个地基上盖的房子只属于一个人家,很多房子会空出来,资源比较空闲浪费。 虚拟机类似楼房,一个地基上盖的楼房住着很多人家,相对主机模式…

微信小程序的开发

1.了解项目的基本组成结构 pages 用来存放所有小程序的页面 utils 用来存放工具性质的模块(例如:格式化时间的自定义模块) app.js 小程序项目的入口文件 app.json 小程序项目的全局配置文件 app.wxss 小程序项目的全局样式文件 project.config.json 项目的配置文件 sitem…

Docker基础学习(5.Docker镜像命令)

⭐ 作者简介:码上言 ⭐ 代表教程:Spring Boot vue-element 开发个人博客项目实战教程 ⭐专栏内容:个人博客系统 ⭐我的文档网站:http://xyhwh-nav.cn/ ⭐微信公众号:码上言 文章目录 Docker run流程镜像是什么&a…

通用视觉多模态大模型 一统理解/生成/分割/编辑

这是一款通用视觉多模态大模型,支持从视觉理解到视觉生成、从低层次到高层次的一系列视觉任务,解决了困扰大语言模型产业已久的图像/视频模型割裂问题,提供了一个全面统一静态图像与动态视频内容的理解、生成、分割、编辑等任务的像素级通用视…

C++必修:类与对象(一)

✨✨ 欢迎大家来到贝蒂大讲堂✨✨ 🎈🎈养成好习惯,先赞后看哦~🎈🎈 所属专栏:C学习 贝蒂的主页:Betty’s blog 1. 面向过程与面向对象 1.1. 面向过程 我们之前学习的C语言就是一种面向过程的语…

【网络通信】初探网络层次结构(OSI七层网络模型)

随着信息技术的飞速发展,网络通信已经成为现代社会不可或缺的一部分。网络通信的实现离不开网络协议栈的支持,而网络协议栈则是由多个层次组成的。这些层次各自承担着不同的任务,共同构成了网络通信的基石。本文将对网络通信中的各类层进行详…

00.Jenkins 基本介绍与安装

Jenkins Jenkins 是一个开源的持续集成(Continuous Integration,CI)和持续交付(Continuous Delivery,CD)工具,用于自动化软件开发过程中的构建、测试和部署。它提供了丰富的插件生态系统,可以支持几乎所有主流的开发、构建和部署工具,使得软件开发团队能够更加高效地…

【信息系统项目管理师知识点速记】整合管理:结束项目或阶段

8.9 结束项目或阶段 1. 定义: 终结项目、阶段或合同的所有活动,存档项目或阶段信息,完成计划的工作,释放组织团队资源以展开新的工作。 2. 重要性: 确保项目目标的实现和项目工作的完结。归档项目信息以便未来参考。…

Swift - 函数

文章目录 Swift - 函数1. 函数的定义2. 隐式返回(Implicit Return)3. 返回元组:实现多返回值4. 函数的文档注释5. 参数标签(Argument Label)6. 默认参数值(Default Parameter Value)7. 可变参数(Variadic P…

LM2576D2TR4-5G 3.0安15伏降压开关稳压器 PDF中文资料_参数_引脚图

LM2576D2TR4-5G 规格信息: 制造商:ON Semiconductor 产品种类:开关稳压器 RoHS:是 装置风格:SMD/SMT 封装 / 箱体:TO-263-5 输出电压:5 V 输出电流:3 A 输出端数量:1 Output 最大输入电压:45 V 拓扑结构:Buck 最小输入电压:7 V 开关频率:52 kHz 最小工作…

seq2seq架构略解

用于序列翻译任务&#xff08;下图来自d2l&#xff09; 训练时输入输出格式&#xff1a; 若数据集为{ <(a1,a2,a3,a4,a5),(b1,b2,b3,b4,b5)> }&#xff08;AB语言对应的句子组&#xff09; 输入 A语言的单词序列结束符&#xff08;a1,a2,a3,a4,a5,<eos>&#xf…

RabbitMQ是如何保证消息不被重复消费,或者说是如何保证消息消费时的幂等性的

目录 面试官:RabbitMQ是如何保证消息不被重复消费?或者说是如何保证消息消费时的幂等性的1. 使用唯一业务标识2. 使用RabbitMQ的消息去重插件3. 使用业务逻辑实现幂等性4. 使用消息属性和死信队列5. 使用Spring Boot的重试机制该文章专注于面试,面试只要回答关键点即可,不需…

Unity类银河恶魔城学习记录14-5 p152 Lost currency save and enemy‘s currency drop

Alex教程每一P的教程原代码加上我自己的理解初步理解写的注释&#xff0c;可供学习Alex教程的人参考 此代码仅为较上一P有所改变的代码 【Unity教程】从0编程制作类银河恶魔城游戏_哔哩哔哩_bilibili LostCurrencyController.cs using System.Collections; using System.Colle…

【STM32+HAL】三轴按键PS2摇杆

一、准备工作&#xff1a; 有关CUBEMX的初始化配置&#xff0c;参见我的另一篇blog&#xff1a;【STM32HAL】CUBEMX初始化配置 有关定时器触发ADC模式配置&#xff0c;详见【STM32HAL】ADC采集波形实现 二、所用工具&#xff1a; 1、芯片&#xff1a; STM32F407VET6 2、CUBE…

通义灵码-IDEA的使用教程

通义灵码-IDEA的使用教程 1、通义灵码是什么&#xff1f; 通义灵码&#xff0c;是阿里云出品的一款基于通义大模型的智能编码辅助工具&#xff0c;提供行级/函数级实时续写、自然语言生成代码、单元测试生成、代码注释生成、代码解释、研发智能问答、异常报错排查等能力&#…

【QT进阶】Qt线程与并发之线程和并发的简单介绍

往期回顾&#xff1a; 【QT进阶】Qt http编程之实现websocket server服务器端-CSDN博客 【QT进阶】Qt http编程之实现websocket client客户端-CSDN博客 【QT进阶】Qt线程与并发之创建线程的三种方法(超详细介绍)-CSDN博客 【QT进阶】Qt线程与并发之线程和并发的简单介绍 一、 …

MATLAB矩阵

MATLAB 矩阵 矩阵是数字的二维数组。 在MATLAB中&#xff0c;您可以通过在每行中以逗号或空格分隔的数字输入元素并使用分号标记每行的结尾来创建矩阵。 例如&#xff0c;让我们创建一个45矩阵一- 示例 a [ 1 2 3 4 5; 2 3 4 5 6; 3 4 5 6 7; 4 5 6 7 8] MATLAB将执行上述语…

C++可调用对象的绑定器和包装器

包装器和绑定器 乃神器也可调用对象、包装器std:function、绑定器std:bind应用场景:可变函数和参数、回调函数、取代虚函数 可调用对象 在C中&#xff0c;可以像函数一样调用的有:普通函数、类的静态成员函数、仿函数、lambda函数、类 的成员函数、可被转换为函数的类的对象…