blogx添加接口

1.添加七牛云&AI的是否启用信息

api/site_api/enter.go

Go 复制代码
package site_api

import (
	"blog_server/common/res"
	"blog_server/conf"
	"blog_server/core"
	"blog_server/global"
	"blog_server/middlware"
	"fmt"
	"github.com/PuerkitoBio/goquery"
	"github.com/gin-gonic/gin"
	"github.com/pkg/errors"
	"github.com/sirupsen/logrus"
	"os"
)

type SiteApi struct {
}
type SiteInfoRequest struct {
	Name string `uri:"name" binding:"required"`
}
type QiNiu struct {
	Enable bool `json:"enable"`
}
type AI struct {
	Enable bool `json:"enable"`
}
type SiteInfoResponse struct {
	QiNiu QiNiu `json:"qi_niu"`
	AI    AI    `json:"ai"`
	conf.Site
}

func (SiteApi) SiteInfoView(c *gin.Context) {
	var cr SiteInfoRequest
	if err := c.ShouldBindUri(&cr); err != nil {
		res.FailWithError(err, c)
		return
	}

	if cr.Name == "site" {
		global.Config.Site.About.Version = global.Version
		res.OkWithData(SiteInfoResponse{
			Site: global.Config.Site,
			QiNiu: QiNiu{
				Enable: global.Config.Qiniu.Enable,
			},
			AI: AI{
				Enable: global.Config.AI.Enable,
			},
		}, c)
		return
	}
	//判断是否是管理员
	middlware.AdminMiddleware(c)
	_, ok := c.Get("claims")
	if !ok {
		return
	}

	var data any
	switch cr.Name {
	case "email":
		rep := global.Config.Email
		rep.AuthCode = "******"
		data = rep
	case "qq":
		rep := global.Config.QQ
		rep.AppId = "******"
		data = rep
	case "qiniu":
		rep := global.Config.Qiniu
		rep.SecretKey = "******"
		data = rep
	case "ai":
		rep := global.Config.AI
		rep.SecretKey = "******"
		data = rep
	default:
		res.FailWithMsg("不存在的配置", c)
		return
	}

	res.OkWithData(data, c)
	return
}

func (SiteApi) SiteUpdateView(c *gin.Context) {

	var cr SiteInfoRequest
	err := c.ShouldBindUri(&cr)
	if err != nil {
		res.FailWithError(err, c)
		return
	}
	var rep any
	switch cr.Name {
	case "site":
		var data conf.Site
		err = c.ShouldBindJSON(&data)
		rep = data

	case "email":
		var data conf.Email
		err = c.ShouldBindJSON(&data)
		rep = data

	case "qq":
		var data conf.QQ
		err = c.ShouldBindJSON(&data)
		rep = data
	case "qiniu":
		var data conf.QiNiu
		err = c.ShouldBindJSON(&data)
		rep = data
	case "ai":
		var data conf.AI
		err = c.ShouldBindJSON(&data)
		rep = data
	default:
		res.FailWithMsg("不存在的配置", c)
		return
	}
	if err != nil {
		res.FailWithError(err, c)
		return
	}
	switch s := rep.(type) {
	case conf.Site:
		//判断站点信息更新前端文件部分
		err = UploadSite(s) //暂时没有处理函数,先设置为空,之后补充
		if err != nil {
			res.FailWithError(err, c)
			return
		}
		global.Config.Site = s
	case conf.Email:
		if s.AuthCode == "******" {
			s.AuthCode = global.Config.Email.AuthCode
		}
		global.Config.Email = s
	case conf.QQ:
		if s.AppId == "******" {
			s.AppId = global.Config.QQ.AppId
		}
		global.Config.QQ = s
	case conf.QiNiu:
		if s.SecretKey == "******" {
			s.SecretKey = global.Config.Qiniu.SecretKey
		}
		global.Config.Qiniu = s
	case conf.AI:
		if s.SecretKey == "******" {
			s.SecretKey = global.Config.Qiniu.SecretKey
		}
		global.Config.AI = s

	}

	core.SetConf()
	res.FailWithMsg("更新站成功", c)
	return
}

func (SiteApi) SiteInfoQQView(c *gin.Context) {
	res.OkWithData(global.Config.QQ.Url(), c)
}

func UploadSite(site conf.Site) error {
	if site.Project.Icon == "" && site.Project.Title == "" && site.Project.WebPath == "" && site.Seo.KeyWords == "" && site.Seo.Description == "" {
		return nil
	}
	if site.Project.WebPath == "" {
		return errors.New("请配置前端地址")
	}
	file, err := os.Open(site.Project.WebPath)
	if err != nil {
		return errors.New(fmt.Sprintf("%s 文件不存在", site.Project.WebPath))
	}
	doc, err := goquery.NewDocumentFromReader(file)
	if err != nil {
		logrus.Errorf("goquery 解析失败 %s", err)
		return errors.New("goquery 解析失败")
	}
	if site.Project.Title != "" {
		doc.Find("title").SetText(site.Project.Title)
	}
	if site.Project.Icon != "" {
		selection := doc.Find("link[rel='icon']").Length()
		if selection == 0 {
			//没有-》创建
			doc.Find("head").AppendHtml(fmt.Sprintf("<link rel=\"icon\" href=\"%s\">", site.Project.Icon))
		} else {
			//有修改
			doc.Find("link[rel='icon']").SetAttr("href", site.Project.Icon)
		}
	}
	if site.Seo.KeyWords != "" {
		selection := doc.Find("meta[name='keywords']").Length()
		if selection == 0 {
			//没有-》创建
			doc.Find("head").AppendHtml(fmt.Sprintf(" <meta name=\"keywords\" content=\"%s\">", site.Seo.KeyWords))
		} else {
			//有修改
			doc.Find("meta[name='keywords']").SetAttr("content", site.Seo.KeyWords)
		}
	}
	if site.Seo.Description != "" {
		selection := doc.Find("meta[name='description']").Length()
		if selection == 0 {
			//没有-》创建
			doc.Find("head").AppendHtml(fmt.Sprintf(" <meta name=\"description\" content=\"%s\">", site.Seo.Description))
		} else {
			//有修改
			doc.Find("meta[name='description']").SetAttr("content", site.Seo.Description)
		}
	}
	html, err := doc.Html()
	if err != nil {
		logrus.Errorf("生成html失败:%s", err)
		return errors.New("生成html失败")
	}
	err = os.WriteFile(site.Project.WebPath, []byte(html), 0666)
	if err != nil {
		logrus.Errorf("写入html失败:%s", err)
		return errors.New("写入html失败")
	}

	return nil
}

2.用户列表接口【api】

修改用户模型

Go 复制代码
type UserModel struct {
	Model
	Username       string           `gorm:"size:32" json:"username"`
	Nickname       string           `gorm:"32" json:"nickname"`
	Avatar         string           `gorm:"size:255" json:"avatar"`
	Abstract       string           `gorm:"size:255" json:"abstract"`
	RegisterSource int8             `gorm:"default:1" json:"register_source"` //注册来源
	Password       string           `gorm:"size:64" json:"-"`
	Email          string           `gorm:"size:255" json:"email"`
	OpenID         string           `gorm:"size:64" json:"open_id"`     //第三方登录的唯一ID
	Role           enum.RoleType    `gorm:"default:1" json:"role"`      //1管理员 2普通用户 3游客
	UserConfModel  *UserConfModel   `gorm:"foreignKey:UserID" json:"-"` //反向映射
	IP             string           `json:"ip"`
	Addr           string           `json:"addr"`
	ArticleList    []ArticleModel   `gorm:"foreignKey:UserID" json:"-"`
	LoginList      []UserLoginModel `gorm:"foreignKey:UserID" json:"-"`
}

api/user_api/user_list.go

Go 复制代码
package user_api

import (
	"blog_server/common"
	"blog_server/common/res"
	"blog_server/middlware"
	"blog_server/models"
	"blog_server/models/enum"
	"github.com/gin-gonic/gin"
	"time"
)

type UserListRequest struct {
	common.PageInfo
	UserID uint `form:"user_id"`
}
type UserListResponse struct {
	ID            uint          `json:"id"`
	Nickname      string        `json:"nickname"`
	Username      string        `json:"username"`
	Avatar        string        `json:"avatar"`
	IP            string        `json:"ip"`
	Addr          string        `json:"addr"`
	ArticleCount  int           `json:"article_count"`   //发文数
	IndexCount    int           `json:"index_count"`     //主页访问数
	CreatedAt     time.Time     `json:"created_at"`      //注册时间
	LastLoginDate time.Time     `json:"last_login_date"` //最后登录时间
	Role          enum.RoleType `json:"role"`
}

func (UserApi) UserListView(c *gin.Context) {
	cr := middlware.GetBind[UserListRequest](c)
	_list, count, _ := common.ListQuery(models.UserModel{}, common.Options{
		Likes:    []string{"nickname", "username"},
		PreLoads: []string{"ArticleList", "LoginList"},
		PageInfo: cr.PageInfo,
	})
	var list = make([]UserListResponse, 0)
	for _, model := range _list {
		item := UserListResponse{
			ID:           model.ID,
			Nickname:     model.Nickname,
			Username:     model.Username,
			Avatar:       model.Avatar,
			IP:           model.IP,
			Addr:         model.Addr,
			ArticleCount: len(model.ArticleList),
			CreatedAt:    model.CreatedAt,
			Role:         model.Role,
		}
		if len(model.LoginList) > 0 {
			item.LastLoginDate = model.LoginList[len(model.LoginList)-1].CreatedAt
		}
		list = append(list, item)
	}
	res.OkWithList(list, count, c)
}
复制代码
r.GET("user", middlware.AdminMiddleware, middlware.BindQueryMiddleware[user_api.UserListRequest], app.UserListView)

3.文章置顶接口【api】

api/user_api/user_article_top.go(记得将所有models的主键加上)

Go 复制代码
package user_api

import (
	"blog_server/common/res"
	"blog_server/global"
	"blog_server/middlware"
	"blog_server/models"
	"blog_server/models/enum"
	"blog_server/utils/jwts"
	"github.com/gin-gonic/gin"
)

//用户置顶文章、管理员置顶文章接口 少接口
//用户只能置顶自己已发布的一篇文章
//管理员可以置顶记发布的文章

type UserArticleTopRequest struct {
	ArticleID uint `json:"article_id" binding:"required"`
	Type      int8 `json:"type" binding:"required,oneof=1 2"`
}

func (UserApi) UserArticleTopView(c *gin.Context) {

	cr := middlware.GetBind[UserArticleTopRequest](c)
	var model models.ArticleModel
	err := global.DB.Take(&model, cr.ArticleID).Error
	if err != nil {
		res.FailWithMsg("文章不存在", c)
		return
	}
	claims := jwts.GetClaims(c)
	switch cr.Type {
	case 1:
		//用户置顶文章
		//验证文章是不是自己的,且是自己发布的
		if model.UserID != claims.UserId {
			res.FailWithMsg("用户只能置顶自己的文章", c)
			return
		}
		if model.Status != enum.ArticleStatusPublishes {
			res.FailWithMsg("用户只能置顶已发布的文章", c)
			return
		}
		//判断之前自己有没有制定过
		var userTopArticleList []models.UserTopArticleModel
		global.DB.Find(&userTopArticleList, "user_id =?", claims.UserId)
		//查不到 自己从来没有置顶过文章
		if len(userTopArticleList) == 0 {
			//置顶
			global.DB.Create(&models.UserTopArticleModel{UserID: claims.UserId, ArticleID: cr.ArticleID})
			res.OkWithMsg("置顶文章成功", c)
			return
		}
		//查到=1
		if len(userTopArticleList) == 1 {
			uta := userTopArticleList[0]
			if uta.ArticleID != cr.ArticleID {
				res.FailWithMsg("普通用户只能置顶一篇文章", c)
				return
			}
		}
		// 是自己的这篇文章 取消置顶
		uta := userTopArticleList[0]
		global.DB.Delete(&uta)
		res.OkWithMsg("取消置顶文章成功", c)

	case 2:
		//管理员置顶文章
		if claims.RoleId != enum.AdminRole {
			res.FailWithMsg("权限不足", c)
			return
		}
		if model.Status != enum.ArticleStatusPublishes {
			res.FailWithMsg("管理员只能置顶已发布的文章", c)
			return
		}
		var userTopArticle []models.UserTopArticleModel
		err = global.DB.Take(&userTopArticle, "user_id =? and article_id =?", claims.UserId, cr.ArticleID).Error
		if err != nil {
			global.DB.Create(&models.UserTopArticleModel{UserID: claims.UserId, ArticleID: cr.ArticleID})
			res.OkWithMsg("置顶文章成功", c)
			return
		}
		global.DB.Delete(&userTopArticle)
		res.OkWithMsg("取消置顶文章成功", c)

	}
}
复制代码
r.POST("user/article/top", middlware.AuthMiddleware, middlware.BindMiddleware[user_api.UserArticleTopRequest], app.UserArticleTopView)

4.文章详情中添加显示分类title

api/article_api/article_detail.go

Go 复制代码
package article_api

import (
	"blog_server/common/res"
	"blog_server/global"
	"blog_server/middlware"
	"blog_server/models"
	"blog_server/models/enum"
	"blog_server/services/redis_service/redis_article"
	"blog_server/utils/jwts"
	"github.com/gin-gonic/gin"
)

type ArticleDetailResponse struct {
	models.ArticleModel
	Username      string  `json:"username"`
	UserAvatar    string  `json:"user_avatar"`
	Nickname      string  `json:"nickname"`
	CategoryTitle *string `json:"category_title"`
}

func (ArticleApi) ArticleDetailView(c *gin.Context) {

	cr := middlware.GetBind[models.IdRequest](c)
	var article models.ArticleModel
	err := global.DB.Preload("UserModel").Preload("CategoryModel").Take(&article, cr.ID).Error
	if err != nil {
		res.FailWithMsg("文章不存在", c)
		return
	}

	claims, err := jwts.ParseTokenByGin(c)
	if err != nil {
		if article.Status != enum.ArticleStatusPublishes {
			res.FailWithMsg("文章不存在", c)
			return
		}

	}
	switch claims.RoleId {
	case enum.UserRole:
		if claims.UserId != article.UserID {
			//登录者看的不是自己的
			if article.Status != enum.ArticleStatusPublishes {
				res.FailWithMsg("文章不存在", c)
				return
			}

		}

	}
	//从缓存中获取浏览量和点赞数
	collectCount := redis_article.GetCacheCollect(article.ID)
	lookCount := redis_article.GetCacheLook(article.ID)
	diggCount := redis_article.GetCacheDigg(article.ID)
	commentCount := redis_article.GetCacheComment(article.ID) //获取文章评论
	article.DiggCount = article.DiggCount + diggCount
	article.LookCount = article.LookCount + lookCount
	article.CollectCount = article.CollectCount + collectCount
	article.CommentCount = article.CommentCount + commentCount //评论数统计
	data := ArticleDetailResponse{
		ArticleModel: article,
		Username:     article.UserModel.Username,
		UserAvatar:   article.UserModel.Avatar,
		Nickname:     article.UserModel.Nickname,
	}
	if article.CategoryModel != nil {
		data.CategoryTitle = &article.CategoryModel.Title
	}
	res.OkWithData(data, c)
}

5.用户注销【api】

api/user_api/user_logout.go

Go 复制代码
package user_api

import (
	"blog_server/common/res"
	redis_jwt "blog_server/services/redis_service/redis-jwt"
	"github.com/gin-gonic/gin"
)

func (UserApi) LogoutView(c *gin.Context) {
	token := c.Request.Header.Get("Authorization")
	redis_jwt.TokenBlack(token, redis_jwt.UserBlackType)
	res.OkWithMsg("注销成功", c)
}
复制代码
r.DELETE("user/logout", middlware.AuthMiddleware, app.LogoutView)

6.用户详情添加展示角色

api/user_api/user_detail.go

Go 复制代码
package user_api

import (
	"blog_server/common/res"
	"blog_server/global"
	"blog_server/models"
	"blog_server/models/enum"
	"blog_server/utils/jwts"
	"github.com/gin-gonic/gin"
	"math"
	"time"
)

type UserDetailResponse struct {
	ID             uint          `json:"id"`
	CreatedAt      time.Time     `json:"created_at"`
	Username       string        `json:"username"`
	Nickname       string        `json:"nickname"`
	Avatar         string        `json:"avatar"`
	Abstract       string        `json:"abstract"`
	RegisterSource int8          `json:"register_source"` //注册来源
	CodeAge        int           `json:"code_age"`        //码龄
	Role           enum.RoleType `json:"role"`
	models.UserConfModel
}

func (UserApi) UserDetailView(c *gin.Context) {
	claims := jwts.GetClaims(c) //使用时在路由时必须加上权限中间件才能正常往下走,否则会报错

	var user models.UserModel
	err := global.DB.Preload("UserConfModel").Take(&user, claims.UserId).Error
	if err != nil {
		res.FailWithMsg("用户不存在", c)
		return
	}
	sub := time.Now().Sub(user.CreatedAt)
	codeAge := int(math.Ceil(sub.Hours() / 24 / 265))
	var data = UserDetailResponse{
		ID:             user.ID,
		CreatedAt:      user.CreatedAt,
		Username:       user.Username,
		Nickname:       user.Nickname,
		Role:           user.Role,
		Avatar:         user.Avatar,
		Abstract:       user.Abstract,
		RegisterSource: user.RegisterSource,
		CodeAge:        codeAge,
	}
	if user.UserConfModel != nil {
		data.UserConfModel = *user.UserConfModel

	}

	res.OkWithData(data, c)

}

7.优化mps(解决taglist存放的是字符串的格式)

utils/mps/enter.go

Go 复制代码
package mps

import (
	"encoding/json"
	"reflect"
)

func StructToMap(data any, t string) (mp map[string]any) {
	mp = make(map[string]any)
	v := reflect.ValueOf(data)
	for i := 0; i < v.NumField(); i++ {
		val := v.Field(i)
		tag := v.Type().Field(i).Tag.Get(t)
		//过滤掉tag为空和"-"的
		if tag == "" || tag == "-" {
			continue
		}
		if val.IsNil() {
			continue
		}
		if val.Kind() == reflect.Ptr {
			v1 := val.Elem().Interface()
			if val.Elem().Kind() == reflect.Slice {
				//如果是切片,使用json解析
				byteData, _ := json.Marshal(v1)
				mp[tag] = string(byteData)
			} else {
				mp[tag] = v1
			}
			continue
		}
		mp[tag] = val.Interface()
	}
	return
}

8.用户详情添加显示是否密码登录和邮箱显示

api/user_api/user_detail.go

Go 复制代码
package user_api

import (
	"blog_server/common/res"
	"blog_server/global"
	"blog_server/models"
	"blog_server/models/enum"
	"blog_server/utils/jwts"
	"github.com/gin-gonic/gin"
	"math"
	"time"
)

type UserDetailResponse struct {
	ID             uint          `json:"id"`
	CreatedAt      time.Time     `json:"created_at"`
	Username       string        `json:"username"`
	Nickname       string        `json:"nickname"`
	Avatar         string        `json:"avatar"`
	Abstract       string        `json:"abstract"`
	RegisterSource int8          `json:"register_source"` //注册来源
	CodeAge        int           `json:"code_age"`        //码龄
	Role           enum.RoleType `json:"role"`
	models.UserConfModel
	Email       string `json:"email"`
	UsePassword bool   `json:"usePassword"`
}

func (UserApi) UserDetailView(c *gin.Context) {
	claims := jwts.GetClaims(c) //使用时在路由时必须加上权限中间件才能正常往下走,否则会报错

	var user models.UserModel
	err := global.DB.Preload("UserConfModel").Take(&user, claims.UserId).Error
	if err != nil {
		res.FailWithMsg("用户不存在", c)
		return
	}
	sub := time.Now().Sub(user.CreatedAt)
	codeAge := int(math.Ceil(sub.Hours() / 24 / 265))
	var data = UserDetailResponse{
		ID:             user.ID,
		CreatedAt:      user.CreatedAt,
		Username:       user.Username,
		Nickname:       user.Nickname,
		Role:           user.Role,
		Avatar:         user.Avatar,
		Abstract:       user.Abstract,
		RegisterSource: user.RegisterSource,
		CodeAge:        codeAge,
		Email:          user.Email,
	}
	if user.Password != "" {
		data.UsePassword = true
	}
	if user.UserConfModel != nil {
		data.UserConfModel = *user.UserConfModel

	}

	res.OkWithData(data, c)

}

9.扫描json的字符串的方法处理空字符串

models/ctype/list.go

Go 复制代码
package ctype

import (
	"database/sql/driver"
	"strings"
)

type List []string

func (j *List) Scan(value interface{}) error {
	val, ok := value.([]uint8)
	if ok {
		if string(val) == "" {
			//如果是空的字符串就将这个值赋值为null,
			*j = []string{}
			return nil
		}
		*j = strings.Split(string(val), ",")
	}
	return nil
}

func (j List) Value() (driver.Value, error) {
	return strings.Join(j, ","), nil
}

10.评论列表添加展示我与好友的关系

api/comment_api/comment_list.go

Go 复制代码
package comment_api

import (
	"blog_server/common"
	"blog_server/common/res"
	"blog_server/global"
	"blog_server/middlware"
	"blog_server/models"
	"blog_server/models/enum"
	"blog_server/models/enum/relationships_enum_type"
	"blog_server/services/focus_service"
	"blog_server/services/redis_service/redis_comment"
	"blog_server/utils/jwts"
	"github.com/gin-gonic/gin"
	"time"
)

type CommentListRequest struct {
	common.PageInfo
	ArticleID uint `form:"article_id"`
	UserID    uint `form:"user_id"`
	Type      int8 `form:"type" binding:"required"` //1-查找我发文章的评论 2-查·我发布的评论 3-管理员查看所有评论
}

type commentListResponse struct {
	ID           uint                             `json:"id"`
	CreatedAt    time.Time                        `json:"created_at"`
	Content      string                           `json:"content"`
	UserID       uint                             `json:"user_id"`
	UserNickname string                           `json:"user_nickname"`
	UserAvatar   string                           `json:"user_avatar"`
	ArticleId    uint                             `json:"article_id"`
	ArticleTitle string                           `json:"article_title"`
	ArticleCover string                           `json:"article_cover"`
	DiggCount    uint                             `json:"digg_count"`
	Relation     relationships_enum_type.Relation `json:"relation,omitempty"`
	IsMe         bool                             `json:"is_me"`
}

func (CommentApi) CommentListView(c *gin.Context) {
	cr := middlware.GetBind[CommentListRequest](c)
	query := global.DB.Where("")
	claims := jwts.GetClaims(c)
	switch cr.Type {
	case 1: //1-查找我发文章的评论
		var articleIDList []uint
		global.DB.Model(models.ArticleModel{}).Where("user_id = ? and status = ?", claims.UserId, enum.ArticleStatusPublishes).Select("id").Scan(&articleIDList)
		query.Where("article_id in ?", articleIDList)
		cr.UserID = 0
	case 2: //2-查·我发布的评论
		cr.UserID = claims.UserId
	case 3: //3-管理员查看所有评论

	}
	_list, count, _ := common.ListQuery(models.CommentModel{
		ArticleID: cr.ArticleID,
		UserID:    cr.UserID,
	}, common.Options{
		PageInfo: cr.PageInfo,
		Likes:    []string{"content"},
		PreLoads: []string{"UserModel", "ArticleModel"},
		Where:    query,
	})

	//查找我和查询ID的关系
	var RelationMap = map[uint]relationships_enum_type.Relation{}
	if cr.Type == 1 {
		var userIDList []uint
		for _, model := range _list {
			userIDList = append(userIDList, model.UserID)
		}
		RelationMap = focus_service.CalculatingPatchFriendRelationships(claims.UserId, userIDList)
	}

	var list = make([]commentListResponse, 0)
	for _, model := range _list {
		list = append(list, commentListResponse{
			ID:           model.ID,
			CreatedAt:    model.CreatedAt,
			Content:      model.Content,
			UserID:       model.UserID,
			UserNickname: model.UserModel.Nickname,
			UserAvatar:   model.UserModel.Avatar,
			ArticleId:    model.ArticleID,
			ArticleTitle: model.ArticleModel.Title,
			ArticleCover: model.ArticleModel.Cover,
			DiggCount:    model.DiggCount + uint(redis_comment.GetCacheDigg(model.ID)),
			Relation:     RelationMap[model.UserID],
			IsMe:         model.UserID == claims.UserId,
		})
	}
	res.OkWithList(list, count, c)

}
复制代码
r.GET("user/base", app.UserBaseInfoView)

11.用户基本信息接口【api】

user_model.go 添加计算码龄

Go 复制代码
func (u *UserModel) CodeAge() int {
	sub := time.Now().Sub(u.CreatedAt)
	return int(math.Ceil(sub.Hours() / 24 / 365))
}

services/cron_service/sync_user.go

Go 复制代码
package cron_service

import (
	"blog_server/global"
	"blog_server/models"
	"blog_server/services/redis_service/redis_user"
	"github.com/sirupsen/logrus"
	"gorm.io/gorm"
)

func SyncUser() {
	lookMap := redis_user.GetAllCacheLook()

	var list []models.UserConfModel
	for _, model := range list {
		look := lookMap[model.UserID]
		if look == 0 {
			continue
		}
		err := global.DB.Model(&model).Updates(map[string]any{
			"look_count": gorm.Expr("look_count + ?", look),
		}).Error
		if err != nil {
			logrus.Errorf("更新失败:%s", err)
			continue
		}
		logrus.Infof("%s redis-mysql数据更新成功", model.UserID)
	}

	//走完之后清空
	redis_user.Clear()

}

services/cron_service/enter.go

Go 复制代码
package cron_service

import (
	"github.com/robfig/cron/v3"
	"time"
)

func Cron() {
	//crontab := cron.New()  默认从分开始进行时间调度
	timezone, _ := time.LoadLocation("Asia/Shanghai")
	crontab := cron.New(cron.WithSeconds(), cron.WithLocation(timezone))

	crontab.AddFunc("0 0 2 * * *", SyncArticle) //每天两点同步文章数据
	crontab.AddFunc("0 30 2 * * *", SyncUser)   //每天两点30同步文章数据
	crontab.AddFunc("0 0 3 * * *", SyncComment) //每天三点同步文章数据

	crontab.Start()

}

services/redis_service/redis_user/enter.go

Go 复制代码
package redis_user

import (
	"blog_server/global"
	"github.com/sirupsen/logrus"
	"strconv"
)

type userCacheType string

const (
	userCacheLook userCacheType = "user_look_key"
)

// ============缓存设置=====================
// 传入设置的key,如果increase为true就设置为数量加1,反之则为减1
func set(t userCacheType, userID uint, n int) {
	num, _ := global.Redis.HGet(string(t), strconv.Itoa(int(userID))).Int()
	num += n
	global.Redis.HSet(string(t), strconv.Itoa(int(userID)), num)

}

func SetCacheLook(userID uint, increase bool) {
	var n = 1
	if !increase {
		n = -1
	}
	set(userCacheLook, userID, n)
}

// ============缓存获取====================
func get(t userCacheType, userID uint) int {
	num, _ := global.Redis.HGet(string(t), strconv.Itoa(int(userID))).Int()
	return num
}

func GetCacheLook(userID uint) int {
	return get(userCacheLook, userID)
}

func GetAll(t userCacheType) (mps map[uint]int) {
	res, err := global.Redis.HGetAll(string(t)).Result()
	if err != nil {
		return
	}
	mps = make(map[uint]int)
	for key, nums := range res {
		intkey, err := strconv.Atoi(key)
		if err != nil {
			continue
		}
		intnum, err := strconv.Atoi(nums)
		if err != nil {
			continue
		}
		mps[uint(intkey)] = intnum
	}
	return mps

}
func GetAllCacheLook() (mps map[uint]int) {
	return GetAll(userCacheLook)
}

// 清空数据
func Clear() {
	err := global.Redis.Del("user_look_key").Err()
	if err != nil {
		logrus.Error(err)
	}
}

api/user_api/user_base_info.go

Go 复制代码
package user_api

import (
	"blog_server/common/res"
	"blog_server/global"
	"blog_server/models"
	"blog_server/services/redis_service/redis_user"
	"github.com/gin-gonic/gin"
)

type UserBaseInfoResponse struct {
	UserID       uint   `json:"user_id"`
	CodeAge      int    `json:"code_age"`
	Avatar       string `json:"avatar"`
	Nickname     string `json:"nickname"`
	LookCount    int    `json:"look_count"`
	ArticleCount int    `json:"article_count"`
	FansCount    int    `json:"fans_count"`
	FocusCount   int    `json:"focus_count"`
	Place        string `json:"place"`         //ip归属地
	OpenCollect  bool   `json:"open_collect"`  //公开我的收藏
	OpenFollow   bool   `json:"open_follow"`   //公开我的关注
	OpenFans     bool   `json:"open_fans"`     //公开我的粉丝
	HomeStyleID  uint64 `json:"home_style_id"` //主页样式的id

}

func (UserApi) UserBaseInfoView(c *gin.Context) {
	var cr models.IdRequest
	err := c.ShouldBindQuery(&cr)
	if err != nil {
		res.FailWithError(err, c)
		return
	}

	var user models.UserModel
	err = global.DB.Preload("UserConfModel").Preload("ArticleList").Take(&user, cr.ID).Error
	if err != nil {
		res.FailWithMsg("不存在的用户", c)
		return
	}

	data := UserBaseInfoResponse{
		UserID:       user.ID,
		CodeAge:      user.CodeAge(),
		Avatar:       user.Avatar,
		Nickname:     user.Nickname,
		LookCount:    user.UserConfModel.IndexCount + redis_user.GetCacheLook(cr.ID),
		ArticleCount: len(user.ArticleList),
		FansCount:    1,
		FocusCount:   1,
		Place:        user.Addr,
		OpenCollect:  user.UserConfModel.OpenCollect,
		OpenFollow:   user.UserConfModel.OpenFollow,
		OpenFans:     user.UserConfModel.OpenFans,
		HomeStyleID:  user.UserConfModel.HomeStyleID,
	}

	var focusList []models.UserFocusModel
	global.DB.Find(&focusList, "user_id =? or focus_user_id = ?", cr.ID, cr.ID)
	for _, model := range focusList {
		if model.UserID == cr.ID {
			data.FansCount++
		}
		if model.FocusUserID == cr.ID {
			data.FocusCount++
		}
	}
	redis_user.SetCacheLook(cr.ID, true)

	res.OkWithData(data, c)

}

用户配置表添加主页访问次数

Go 复制代码
// 用户配置表
type UserConfModel struct {
	UserID             uint       `gorm:"primaryKey;unique" json:"user_id"`
	UserModel          UserModel  `gorm:"foreignKey:UserID" json:"-"`
	LikeTags           []string   `gorm:"type:longtext;serializer:json" json:"like_tags"` //兴趣标签
	UpdateUsernameDate *time.Time `json:"update_username_date"`                           //上次用户名修改时间
	OpenCollect        bool       `json:"open_collect"`                                   //公开我的收藏
	OpenFollow         bool       `json:"open_follow"`                                    //公开我的关注
	OpenFans           bool       `json:"open_fans"`                                      //公开我的粉丝
	HomeStyleID        uint64     `json:"home_style_id"`                                  //主页样式的id
	IndexCount         int        `json:"index_count"`                                    //主页的访问次数
}

12.根据收藏夹ID查文章

api/article_api/article_list.go

Go 复制代码
package article_api

import (
	"blog_server/common"
	"blog_server/common/res"
	"blog_server/global"
	"blog_server/middlware"
	"blog_server/models"
	"blog_server/models/enum"
	"blog_server/services/redis_service/redis_article"
	"blog_server/utils/jwts"
	"blog_server/utils/sql"
	"fmt"
	"github.com/gin-gonic/gin"
)

//可以查某个用户发布的文章,只能查已发布的,不需要登录,支持分类查询
//	可以查某个人用户收藏的文章,前提是这个用户开了对应隐私设置
//用户侧 查自己发布的文章,只能查已发布的,,	需要登录, 支持分类查询
//	也能查自己收藏的文章,不会受到自己的隐私设置
//	支持按照状态查询,已发布,草稿箱,待审核
//管理员侧 查全部,支持按照用户搜索,状态过滤,文章标题模糊匹配,分类过滤

type ArticleListRequest struct {
	common.PageInfo
	Type       int8                   `form:"type" binding:"required,oneof=1 2 3"` //1-用户查别人的, 2-查自己的 3-管理员查询
	CategoryID *uint                  `form:"category_id"`
	UserID     uint                   `form:"user_id"`
	Status     enum.ArticleStatusType `form:"status"` //状态 草稿,审核中,已发布
	CollectID  uint                   `form:"collect_id"`
}

type ArticleListResponse struct {
	models.ArticleModel
	UserTop       bool    `json:"user_top"`       //是否是用户置顶
	AdminTop      bool    `json:"admin_top"`      //是否是管理员置顶
	CategoryTitle *string `json:"category_title"` //使用指针,可以使在json序列化中进行判断,""为空值。nill为没有传递
	UserNickname  string  `json:"user_nickname"`
	UserAvatar    string  `json:"user_avatar"`
}

func (ArticleApi) ArticleListView(c *gin.Context) {

	var topArticleIdList []uint //用户置顶列表
	var orderColumnMap = map[string]bool{
		"look_count desc":    true,
		"digg_count desc":    true,
		"comment_count desc": true,
		"collect_count desc": true,
		"look_count":         true,
		"digg_count":         true,
		"comment_count":      true,
		"collect_count":      true,
	}
	cr := middlware.GetBind[ArticleListRequest](c)
	switch cr.Type {
	case 1:
		//查别人的用户id就是必填的
		if cr.UserID == 0 {
			res.FailWithMsg("用户id必填", c)
			return
		}
		if cr.Page > 2 || cr.Limit > 10 {
			res.FailWithMsg("查询更多请登录", c)
			return
		}
		cr.Status = 0
		cr.Order = ""
		if cr.CollectID != 0 {
			if cr.UserID == 0 {
				res.FailWithMsg("请传入用户ID", c)
				return
			}
			var userConf models.UserConfModel
			err := global.DB.Take(&userConf, "user_id = ?", cr.UserID).Error
			if err != nil {
				res.FailWithMsg("用户不存在", c)
				return
			}

			if !userConf.OpenCollect {
				//不公开我的收藏
				//在页面上,我的收藏页面不可见
				//收藏列表接口不能访问,不能用这个收藏夹 id 查文章
				res.FailWithMsg("用户未开启我的收藏", c)
				return
			}
		}
	case 2:
		//查自己
		claims, err := jwts.ParseTokenByGin(c)
		if err != nil {
			res.FailWithMsg("请登录", c)
			return
		}
		cr.UserID = claims.UserId

	case 3:
		//管理员
		claims, err := jwts.ParseTokenByGin(c)
		if !(err == nil && claims.RoleId == enum.AdminRole) {
			res.FailWithMsg("角色错误", c)
			return
		}
	}
	//根据收藏夹ID查文章
	query := global.DB.Where("")
	if cr.CollectID != 0 {
		var articleIDList []uint
		global.DB.Model(models.UserArticleCollectMode{}).Where("collect_id =? ", cr.CollectID).Select("article_id").Scan(&articleIDList)
		query.Where("id in ?", articleIDList)
	}
	if cr.Order != "" {
		_, ok := orderColumnMap[cr.Order]
		if !ok {
			res.FailWithMsg("不支持的排序方式", c)
			return
		}
	}

	//处理用户置顶
	var userTopMap = map[uint]bool{}
	var adminTopMap = map[uint]bool{}
	if cr.UserID != 0 {
		var userTopArticleList []models.UserTopArticleModel
		global.DB.Preload("UserModel").Order("created_at desc").Find(&userTopArticleList, "user_id = ?", cr.UserID)

		for _, i2 := range userTopArticleList {
			topArticleIdList = append(topArticleIdList, i2.ArticleID)
			if i2.UserModel.Role == enum.AdminRole {
				adminTopMap[i2.ArticleID] = true
			}
			userTopMap[i2.ArticleID] = true
		}
	}

	//判断是否有置顶,有的话话再按照置顶列表进行排序,否则就按照时间倒叙排序
	var options = common.Options{
		Likes:        []string{"title"},
		PageInfo:     cr.PageInfo,
		DefaultOrder: "created_at desc",
		Where:        query,
		PreLoads:     []string{"CategoryModel", "UserModel"},
	}
	if len(topArticleIdList) > 0 {
		options.DefaultOrder = fmt.Sprintf("%s,created_at desc", sql.ConvertSliceOrderSql(topArticleIdList)) // [1 2 3] = >(1,2,3)或者 id= 1 desc,id = 2 desc 使用辅助函数实现ConvertSliceOrderSql
	}

	_list, count, _ := common.ListQuery(models.ArticleModel{
		UserID:     cr.UserID,
		CategoryID: cr.CategoryID,
		Status:     cr.Status,
	}, options)

	var list = make([]ArticleListResponse, 0)
	//从缓存中获取浏览量和点赞数
	collectMap := redis_article.GetAllCacheCollect()
	lookMap := redis_article.GetAllCacheLook()
	diggMap := redis_article.GetAllCacheDigg()
	commentMap := redis_article.GetAllCacheComment() //获取评论
	for _, model := range _list {
		model.Content = ""
		model.DiggCount = model.DiggCount + diggMap[model.ID]
		model.LookCount = model.LookCount + lookMap[model.ID]
		model.CollectCount = model.CollectCount + collectMap[model.ID]
		model.CommentCount = model.CommentCount + commentMap[model.ID] //评论数统计
		data := ArticleListResponse{
			ArticleModel: model,
			UserTop:      userTopMap[model.ID],
			AdminTop:     adminTopMap[model.ID],
			UserNickname: model.UserModel.Nickname,
			UserAvatar:   model.UserModel.Avatar,
		}
		//如果有关联的分类表,就将分类表中的title传递过来(使用指针,如果是)
		if model.CategoryModel != nil {
			data.CategoryTitle = &model.CategoryModel.Title
		}
		list = append(list, data)
	}

	res.OkWithList(list, count, c)

}

13.粉丝关注列表添加好友关系字段

api/focus_api/enter.go

Go 复制代码
package focus_api

import (
	"blog_server/common"
	"blog_server/common/res"
	"blog_server/global"
	"blog_server/middlware"
	"blog_server/models"
	"blog_server/models/enum/relationships_enum_type"
	"blog_server/services/focus_service"
	"blog_server/utils/jwts"
	"fmt"
	"github.com/gin-gonic/gin"
	"time"
)

type FocusApi struct {
}
type FocusUserRequest struct {
	FocusUserID uint `json:"focus_user_id" binding:"required"`
}

// ============================= FocusUserView 登录人关注用户==========================================
func (FocusApi) FocusUserView(c *gin.Context) {
	cr := middlware.GetBind[FocusUserRequest](c)
	//查关注的用户是否存在
	var user models.UserModel
	err := global.DB.Take(&user, cr.FocusUserID).Error
	if err != nil {
		res.FailWithMsg("关注的用户不存在", c)
		return
	}

	//查找之前是否已经关注过他了
	claims := jwts.GetClaims(c)
	if cr.FocusUserID == claims.UserId {
		res.FailWithMsg("你时刻都在关注自己!", c)
		return
	}

	var focus models.UserFocusModel
	err = global.DB.Take(&focus, "user_id = ? and focus_user_id = ?", claims.UserId, user.ID).Error
	if err == nil {
		res.FailWithMsg("你已经关注了该用户", c)
		return
	}
	//每天关注是不是应该有限度
	//每天取关是否有限度

	//关注
	global.DB.Create(&models.UserFocusModel{
		UserID:      claims.UserId,
		FocusUserID: cr.FocusUserID,
	})

	res.OkWithMsg("关注成功", c)

}

type FocusUserListRequest struct {
	common.PageInfo
	FocusUserID uint `form:"focus_user_id"`
	UserID      uint `form:"user_id"`
}
type UserListResponse struct {
	UserID       uint                             `form:"user_id"`
	UserNickname string                           `json:"user_nickname"`
	UserAvatar   string                           `json:"user_avatar"`
	UserAbstract string                           `json:"user_abstract"`
	Relationship relationships_enum_type.Relation `json:"relationship"`
	CreatedAt    time.Time                        `json:"created_at"`
}

// ============================= FocusUserListView 查看我的关注列表&查看某用户的关注列表根据 有无user_id判断===============================
func (FocusApi) FocusUserListView(c *gin.Context) {
	cr := middlware.GetBind[FocusUserListRequest](c)
	claims, err := jwts.ParseTokenByGin(c)
	if cr.UserID != 0 {
		//有用户id则查该用户的用户列表
		var userConf models.UserConfModel
		er := global.DB.Take(&userConf, "user_id = ?", cr.UserID).Error
		if er != nil {
			res.FailWithMsg("用户配置信息不存在", c)
			return
		}
		if !userConf.OpenFollow {
			res.FailWithMsg("该用户未公开我的关注", c)
			return
		}
		//如果你没有登录,就不允许你查询第二页
		if err != nil || claims == nil {
			if cr.Limit > 10 || cr.Page > 1 {
				res.FailWithMsg("未登录用户只能查询第一页", c)
				return
			}
		}

	} else {
		if err != nil {
			res.FailWithMsg("请登录", c)
			return
		}
		cr.UserID = claims.UserId
	}
	//模糊匹配
	query := global.DB.Where("")
	if cr.Key != "" {
		var userIDList []uint
		global.DB.Model(&models.UserModel{}).Where("nickname like ?", fmt.Sprintf("%%%s%%", cr.Key)).Select("id").Scan(&userIDList)
		if len(userIDList) > 0 {
			query.Where("focus_user_id in ?", userIDList)
		}
	}

	_list, count, _ := common.ListQuery(models.UserFocusModel{
		FocusUserID: cr.FocusUserID,
		UserID:      cr.UserID,
	}, common.Options{
		PageInfo: cr.PageInfo,
		PreLoads: []string{"FocusUserModel"},
		Where:    query,
	})

	var m = map[uint]relationships_enum_type.Relation{}
	if err == nil && claims != nil {
		var userIDList []uint
		for _, i2 := range _list {
			userIDList = append(userIDList, i2.FocusUserID)
		}
		m = focus_service.CalculatingPatchFriendRelationships(claims.UserId, userIDList)
	}
	var list = make([]UserListResponse, 0)
	for _, model := range _list {
		list = append(list, UserListResponse{
			UserID:       model.FocusUserID,
			UserNickname: model.FocusUserModel.Nickname,
			UserAvatar:   model.FocusUserModel.Avatar,
			Relationship: m[model.FocusUserID],
			UserAbstract: model.FocusUserModel.Abstract,
			CreatedAt:    model.CreatedAt,
		})

	}
	res.OkWithList(list, count, c)
}

type FansUserListRequest struct {
	common.PageInfo
	FocusUserID uint `form:"focus_user_id"`
	UserID      uint `form:"user_id"`
}

//type FansUserListResponse struct {
//	FansUserID       uint      `form:"fans_user_id"`
//	FansUserNickname string    `json:"fans_user_nickname"`
//	FansUserAvatar   string    `json:"fans_user_avatar"`
//	FansUserAbstract string    `json:"fans_user_abstract"`
//	CreatedAt        time.Time `json:"created_at"`
//}

// ============================= FansUserListView 我的粉丝列表和用户的粉丝列表===============================
func (FocusApi) FansUserListView(c *gin.Context) {
	cr := middlware.GetBind[FansUserListRequest](c)
	claims, err := jwts.ParseTokenByGin(c)
	if cr.UserID != 0 {
		//有用户id则查该用户的用户列表
		var userConf models.UserConfModel
		er := global.DB.Take(&userConf, "user_id = ?", cr.UserID).Error
		if er != nil {
			res.FailWithMsg("用户配置信息不存在", c)
			return
		}
		if !userConf.OpenFans {
			res.FailWithMsg("该用户未公开我的粉丝", c)
			return
		}
		//如果你没有登录,就不允许你查询第二页
		if err != nil || claims == nil {
			if cr.Limit > 10 || cr.Page > 1 {
				res.FailWithMsg("未登录用户只能查询第一页", c)
				return
			}
		}

	} else {

		if err != nil {
			res.FailWithMsg("请登录", c)
			return
		}
		cr.UserID = claims.UserId
	}
	//模糊匹配
	query := global.DB.Where("")
	if cr.Key != "" {
		var userIDList []uint
		global.DB.Model(&models.UserModel{}).Where("nickname like ?", fmt.Sprintf("%%%s%%", cr.Key)).Select("id").Scan(&userIDList)
		if len(userIDList) > 0 {
			query.Where("user_id in ?", userIDList)
		}
	}
	_list, count, _ := common.ListQuery(models.UserFocusModel{
		FocusUserID: cr.UserID,
		UserID:      cr.FocusUserID,
	}, common.Options{
		PageInfo: cr.PageInfo,
		PreLoads: []string{"UserModel"},
		Where:    query,
	})
	var m = map[uint]relationships_enum_type.Relation{}
	if err == nil && claims != nil {
		var userIDList []uint
		for _, i2 := range _list {
			userIDList = append(userIDList, i2.UserID)
		}
		m = focus_service.CalculatingPatchFriendRelationships(claims.UserId, userIDList)
	}
	var list = make([]UserListResponse, 0)
	for _, model := range _list {
		list = append(list, UserListResponse{
			UserID:       model.UserID,
			UserNickname: model.UserModel.Nickname,
			UserAvatar:   model.UserModel.Avatar,
			UserAbstract: model.UserModel.Abstract,
			CreatedAt:    model.CreatedAt,
			Relationship: m[model.UserID],
		})

	}
	res.OkWithList(list, count, c)
}

// ============================= // UnFocusUserView 登录人取关=========================================
func (FocusApi) UnFocusUserView(c *gin.Context) {
	cr := middlware.GetBind[FocusUserRequest](c)
	//查关注的用户是否存在
	var user models.UserModel
	err := global.DB.Take(&user, cr.FocusUserID).Error
	if err != nil {
		res.FailWithMsg("取关用户不存在", c)
		return
	}

	//查找之前是否已经关注过他了
	claims := jwts.GetClaims(c)
	if cr.FocusUserID == claims.UserId {
		res.FailWithMsg("你时刻都在关注自己!", c)
		return
	}

	var focus models.UserFocusModel
	err = global.DB.Take(&focus, "user_id = ? and focus_user_id = ?", claims.UserId, user.ID).Error
	if err != nil {
		res.FailWithMsg("未关注该用户", c)
		return
	}
	//取关
	global.DB.Delete(&focus)
	res.OkWithMsg("取消关注成功", c)

}

14.用户基本信息添加好友关系字段响应

api/user_api/user_base_info.go

Go 复制代码
package user_api

import (
	"blog_server/common/res"
	"blog_server/global"
	"blog_server/models"
	"blog_server/models/enum/relationships_enum_type"
	"blog_server/services/focus_service"
	"blog_server/services/redis_service/redis_user"
	"blog_server/utils/jwts"
	"github.com/gin-gonic/gin"
)

type UserBaseInfoResponse struct {
	UserID       uint                             `json:"user_id"`
	CodeAge      int                              `json:"code_age"`
	Avatar       string                           `json:"avatar"`
	Nickname     string                           `json:"nickname"`
	LookCount    int                              `json:"look_count"`
	ArticleCount int                              `json:"article_count"`
	FansCount    int                              `json:"fans_count"`
	FocusCount   int                              `json:"focus_count"`
	Place        string                           `json:"place"`         //ip归属地
	OpenCollect  bool                             `json:"open_collect"`  //公开我的收藏
	OpenFollow   bool                             `json:"open_follow"`   //公开我的关注
	OpenFans     bool                             `json:"open_fans"`     //公开我的粉丝
	HomeStyleID  uint64                           `json:"home_style_id"` //主页样式的id
	Relationship relationships_enum_type.Relation `json:"relationship"`
}

func (UserApi) UserBaseInfoView(c *gin.Context) {
	var cr models.IdRequest
	err := c.ShouldBindQuery(&cr)
	if err != nil {
		res.FailWithError(err, c)
		return
	}

	var user models.UserModel
	err = global.DB.Preload("UserConfModel").Preload("ArticleList").Take(&user, cr.ID).Error
	if err != nil {
		res.FailWithMsg("不存在的用户", c)
		return
	}

	data := UserBaseInfoResponse{
		UserID:       user.ID,
		CodeAge:      user.CodeAge(),
		Avatar:       user.Avatar,
		Nickname:     user.Nickname,
		LookCount:    user.UserConfModel.IndexCount + redis_user.GetCacheLook(cr.ID),
		ArticleCount: len(user.ArticleList),
		FansCount:    1,
		FocusCount:   1,
		Place:        user.Addr,
		OpenCollect:  user.UserConfModel.OpenCollect,
		OpenFollow:   user.UserConfModel.OpenFollow,
		OpenFans:     user.UserConfModel.OpenFans,
		HomeStyleID:  user.UserConfModel.HomeStyleID,
	}
	claims, err := jwts.ParseTokenByGin(c)
	if err == nil && claims != nil {
		data.Relationship = focus_service.CalculatingFriendRelationships(claims.UserId, cr.ID)
	}

	var focusList []models.UserFocusModel
	global.DB.Find(&focusList, "user_id =? or focus_user_id = ?", cr.ID, cr.ID)
	for _, model := range focusList {
		if model.UserID == cr.ID {
			data.FansCount++
		}
		if model.FocusUserID == cr.ID {
			data.FocusCount++
		}
	}
	redis_user.SetCacheLook(cr.ID, true)

	res.OkWithData(data, c)

}

15.文章详情添加是否点赞,是否收藏字段

api/article_api/article_detail.go

Go 复制代码
package article_api

import (
	"blog_server/common/res"
	"blog_server/global"
	"blog_server/middlware"
	"blog_server/models"
	"blog_server/models/enum"
	"blog_server/services/redis_service/redis_article"
	"blog_server/utils/jwts"
	"github.com/gin-gonic/gin"
)

type ArticleDetailResponse struct {
	models.ArticleModel
	Username      string  `json:"username"`
	UserAvatar    string  `json:"user_avatar"`
	Nickname      string  `json:"nickname"`
	CategoryTitle *string `json:"category_title"`
	IsDigg        bool    `json:"is_digg"`    //是否点赞
	IsCollect     bool    `json:"is_collect"` //是否收藏
}

func (ArticleApi) ArticleDetailView(c *gin.Context) {

	cr := middlware.GetBind[models.IdRequest](c)
	var article models.ArticleModel
	err := global.DB.Preload("UserModel").Preload("CategoryModel").Take(&article, cr.ID).Error
	if err != nil {
		res.FailWithMsg("文章不存在", c)
		return
	}

	claims, err := jwts.ParseTokenByGin(c)
	if err != nil {
		if article.Status != enum.ArticleStatusPublishes {
			res.FailWithMsg("文章不存在", c)
			return
		}

	}
	data := ArticleDetailResponse{
		ArticleModel: article,
		Username:     article.UserModel.Username,
		UserAvatar:   article.UserModel.Avatar,
		Nickname:     article.UserModel.Nickname,
	}
	if err == nil && claims != nil {
		switch claims.RoleId {
		case enum.UserRole:
			if claims.UserId != article.UserID {
				//登录者看的不是自己的
				if article.Status != enum.ArticleStatusPublishes {
					res.FailWithMsg("文章不存在", c)
					return
				}

			}

		}
		//查用户是否收藏了,文章,是否点赞了文章

		var userDiggModel models.ArticleDiggModel
		err = global.DB.Take(&userDiggModel, "user_id =? and article_id =?", claims.UserId, article.ID).Error
		if err == nil {
			data.IsDigg = true
		}
		var userCollectModel models.UserArticleCollectMode
		err = global.DB.Take(&userCollectModel, "user_id =? and article_id =?", claims.UserId, article.ID).Error
		if err == nil {
			data.IsCollect = true
		}
	}

	//从缓存中获取浏览量和点赞数
	collectCount := redis_article.GetCacheCollect(article.ID)

	lookCount := redis_article.GetCacheLook(article.ID)
	diggCount := redis_article.GetCacheDigg(article.ID)
	commentCount := redis_article.GetCacheComment(article.ID) //获取文章评论

	data.DiggCount = article.DiggCount + diggCount
	data.LookCount = article.LookCount + lookCount
	data.CollectCount = article.CollectCount + collectCount
	data.CommentCount = article.CommentCount + commentCount //评论数统计

	if article.CategoryModel != nil {
		data.CategoryTitle = &article.CategoryModel.Title
	}
	res.OkWithData(data, c)
}

16.文章收藏,文章点赞取消部分优化(因为表加了主键便方便优化了)

api/article_api/article_digg.go

Go 复制代码
package article_api

import (
	"blog_server/common/res"
	"blog_server/global"
	"blog_server/middlware"
	"blog_server/models"
	"blog_server/models/enum"
	"blog_server/services/message_service"
	"blog_server/services/redis_service/redis_article"
	"blog_server/utils/jwts"
	"github.com/gin-gonic/gin"
)

func (ArticleApi) ArticleDiggView(c *gin.Context) {
	cr := middlware.GetBind[models.IdRequest](c)

	var article models.ArticleModel
	err := global.DB.Take(&article, "status = ? and id = ?", enum.ArticleStatusPublishes, cr.ID).Error //查询已发布的
	if err != nil {
		res.FailWithMsg("文章不存在", c)
		return
	}

	claims := jwts.GetClaims(c)
	//查一下之前有没有点赞
	var UserDiggArticle models.ArticleDiggModel
	err = global.DB.Take(&UserDiggArticle, "user_id = ? and article_id = ?", claims.UserId, article.ID).Error
	if err != nil {
		//不存在-》点赞
		model := models.ArticleDiggModel{
			UserID:    claims.UserId,
			ArticleID: article.ID,
		}
		err = global.DB.Create(&model).Error
		if err != nil {
			res.FailWithMsg("点赞失败", c)
			return
		}
		//将点赞添加到缓存
		redis_article.SetCacheDigg(cr.ID, true)
		//给文章拥有者发消息
		message_service.InsertDiggArticleMessage(model)
		res.OkWithMsg("点赞成功", c)
		return
	}

	//global.DB.Debug().Where("user_id = ? and article_id = ?", claims.UserId, article.ID).
	//	Delete(&models.ArticleDiggModel{})
	global.DB.Delete(&UserDiggArticle)
	redis_article.SetCacheDigg(cr.ID, false)
	res.OkWithMsg("取消点赞成功", c)
	return
}

api/article_api/article_collect.go

Go 复制代码
package article_api

import (
	"blog_server/common/res"
	"blog_server/global"
	"blog_server/middlware"
	"blog_server/models"
	"blog_server/models/enum"
	"blog_server/services/message_service"
	"blog_server/services/redis_service/redis_article"
	"blog_server/utils/jwts"
	"fmt"
	"github.com/gin-gonic/gin"
)

type ArticleCollectRequest struct {
	ArticleId uint `json:"article_id" binding:"required"`
	CollectID uint `json:"collect_id"`
}

func (ArticleApi) ArticleCollectView(c *gin.Context) {
	cr := middlware.GetBind[ArticleCollectRequest](c)
	var article models.ArticleModel
	err := global.DB.Take(&article, "status = ? and id = ?", enum.ArticleStatusPublishes, cr.ArticleId).Error //查询已发布的
	if err != nil {
		res.FailWithMsg("文章不存在", c)
		return
	}
	var collectModel models.CollectModel
	claims, err := jwts.ParseTokenByGin(c)
	if cr.CollectID == 0 {
		//默认收藏夹

		err = global.DB.Take(&collectModel, "user_id =? and is_default = ?", claims.UserId, 1).Error
		if err != nil {
			//创建一个默认收藏夹
			collectModel.Title = "默认收藏夹"
			collectModel.UserID = claims.UserId
			collectModel.IsDefault = true
			global.DB.Create(&collectModel)

		}
		cr.CollectID = collectModel.ID
	} else {
		//判断收藏夹是否存在并且是否是自己创建的

		err = global.DB.Take(&collectModel, "user_id =?", claims.UserId).Error
		if err != nil {
			res.FailWithMsg("收藏夹不存在", c)
			return
		}
	}
	//判断是否收藏
	var articleCollect models.UserArticleCollectMode
	err = global.DB.Where(models.UserArticleCollectMode{
		UserID:    claims.UserId,
		ArticleID: cr.ArticleId,
		CollectID: cr.CollectID,
	}).Take(&articleCollect).Error
	if err != nil {
		//没有就收藏
		model := models.UserArticleCollectMode{
			UserID:    claims.UserId,
			ArticleID: cr.ArticleId,
			CollectID: cr.CollectID,
		}
		err = global.DB.Create(&model).Error
		if err != nil {
			res.FailWithMsg("收藏失败", c)
			return
		}
		//对收藏夹进行加1

		redis_article.SetCacheCollect(cr.CollectID, true)
		//给文章拥有者发消息
		message_service.InsertCollectArticleMessage(model)
		res.FailWithMsg("收藏成功", c)

		//global.DB.Model(&collectModel).Update("article_count", gorm.Expr("article_count + 1"))
		return
	}
	//取消收藏
	//err = global.DB.Where(models.UserArticleCollectMode{
	//	UserID:    claims.UserId,
	//	ArticleID: cr.ArticleId,
	//	CollectID: cr.CollectID,
	//}).Delete(&models.UserArticleCollectMode{}).Error

	err = global.DB.Delete(&articleCollect).Error
	if err != nil {
		res.FailWithMsg("取消收藏失败", c)
		return
	}
	res.FailWithMsg("取消收藏成功", c)
	//对收藏夹进行-1
	//global.DB.Model(&collectModel).Update("article_count", gorm.Expr("article_count - 1"))
	//收藏数同步缓存
	redis_article.SetCacheCollect(cr.CollectID, false)
	return

}
func (ArticleApi) ArticleCollectPatchRemove(c *gin.Context) {
	var cr = middlware.GetBind[models.IdRemoveRequest](c)
	claims := jwts.GetClaims(c)
	var userCollectList []models.UserArticleCollectMode
	global.DB.Find(&userCollectList, "id in ? and user_id = ?", cr.IDList, claims.UserId)

	if len(userCollectList) > 0 {
		global.DB.Debug().Delete(&userCollectList)
	}
	res.FailWithMsg(fmt.Sprintf("批量删除文章收藏%d个", len(userCollectList)), c)

}

问题:但是文章收藏数的计算有问题待处理(只能减不能加)

17.收藏夹的列表添加查询这个文件夹是否被这文章使用

api/article_api/collect.go

Go 复制代码
package article_api

import (
	"blog_server/common"
	"blog_server/common/res"
	"blog_server/global"
	"blog_server/middlware"
	"blog_server/models"
	"blog_server/models/enum"
	"blog_server/utils/jwts"
	"fmt"
	"github.com/gin-gonic/gin"
)

//=====收藏夹的创建和更新,如果如果有ID就是更新,没有ID就是创建=====

type CollectCreateRequest struct {
	ID       uint   `json:"id"`
	Title    string `son:"title" binding:"required"`
	Abstract string `json:"abstract"`
	Cover    string `json:"cover"`
}

func (ArticleApi) CollectCreateView(c *gin.Context) {
	cr := middlware.GetBind[CollectCreateRequest](c)
	claims := jwts.GetClaims(c)

	var model models.CollectModel

	if cr.ID == 0 {
		//创建
		err := global.DB.Take(&model, "user_id = ? and title = ?", claims.UserId, cr.Title).Error
		if err == nil {
			res.FailWithMsg("收藏夹名称重复", c)
			return
		}
		err = global.DB.Create(&models.CollectModel{
			Title:    cr.Title,
			UserID:   claims.UserId,
			Abstract: cr.Abstract,
			Cover:    cr.Cover,
		}).Error
		if err != nil {
			res.FailWithMsg("创建收藏夹失败", c)
			return
		}
		res.OkWithMsg("创建收藏夹成功", c)
		return

	}
	err := global.DB.Take(&model, "user_id = ? and id = ?", claims.UserId, cr.ID).Error
	if err != nil {
		res.FailWithMsg("收藏夹不存在", c)
		return
	}

	err = global.DB.Model(&model).Updates(map[string]any{
		"title":    cr.Title,
		"abstract": cr.Abstract,
		"cover":    cr.Cover,
	}).Error
	if err != nil {
		res.FailWithMsg("更新收藏夹失败", c)
		return
	}
	res.OkWithMsg("更新收藏夹成功", c)
}

// =====收藏夹列表=====
type CollectListRequest struct {
	common.PageInfo
	UserID    uint `form:"user_id"`
	Type      int8 `form:"type" binding:"required,oneof=1 2 3"` //1-查自己 2-查别人 3-后台
	ArticleID uint `form:"article_id"`
}
type CollectListResponse struct {
	models.CollectModel
	ArticleCount int    `json:"article_count"`
	Nickname     string `json:"nickname,omitempty"`
	Avatar       string `json:"avatar,omitempty"`
	ArticleUse   bool   `json:"article_use,omitempty"` //这个收藏夹是否被使用,即里面是否有文章
}

func (ArticleApi) CollectListView(c *gin.Context) {
	cr := middlware.GetBind[CollectListRequest](c)

	var preload = []string{"ArticleList"}

	switch cr.Type {
	case 1:
		claims, err := jwts.ParseTokenByGin(c)
		if err != nil {
			res.FailWithError(err, c)
			return
		}
		cr.UserID = claims.UserId
	case 2:
		//不公开我的收藏
		//在页面上,我的收藏页面不可见
		//收藏列表接口不能访问,不能用这个收藏夹 id 查文章
		var userConf models.UserConfModel
		err := global.DB.Take(&userConf, "user_id = ?", cr.UserID).Error
		if err != nil {
			res.FailWithMsg("用户不存在", c)
			return
		}

		if !userConf.OpenCollect {
			res.FailWithMsg("用户未开启我的收藏", c)
			return
		}
	case 3:
		claims, err := jwts.ParseTokenByGin(c)
		if err != nil {
			res.FailWithError(err, c)
			return
		}
		if claims.RoleId != enum.AdminRole {
			res.FailWithMsg("权限不足!", c)
			return
		}
		preload = append(preload, "UserModel")

	}

	_list, count, _ := common.ListQuery(models.CollectModel{
		UserID: cr.UserID,
	}, common.Options{
		PageInfo: cr.PageInfo,
		Likes:    []string{"title"},
		PreLoads: preload,
	})

	var list = make([]CollectListResponse, 0)
	for _, i2 := range _list {
		item := CollectListResponse{
			CollectModel: i2,
			ArticleCount: len(i2.ArticleList),
			Nickname:     i2.UserModel.Nickname,
			Avatar:       i2.UserModel.Avatar,
		}
		for _, model := range i2.ArticleList {
			if model.ArticleID == cr.ArticleID {
				item.ArticleUse = true
				break
			}
		}
		list = append(list, item)
	}

	res.OkWithList(list, count, c)
}

func (ArticleApi) CollectRemoveView(c *gin.Context) {
	var cr = middlware.GetBind[models.IdRemoveRequest](c)
	var list []models.CollectModel
	query := global.DB.Where("id in ?", cr.IDList)
	claims := jwts.GetClaims(c)
	if claims.RoleId != enum.AdminRole {
		query.Where("user_id = ?", claims.UserId)
	}
	global.DB.Where(query).Find(&list)
	if len(list) > 0 {
		err := global.DB.Delete(&list).Error
		if err != nil {
			res.FailWithMsg("删除收藏夹失败!", c)
			return
		}
	}
	msg := fmt.Sprintf("删除收藏夹成功,共删除%d条", len(list))
	res.OkWithMsg(msg, c)

}

18.评论数添加是否点赞和好友关系字段

api/comment_api/comment_tree.go

Go 复制代码
package comment_api

import (
	"blog_server/common/res"
	"blog_server/global"
	"blog_server/middlware"
	"blog_server/models"
	"blog_server/models/enum"
	"blog_server/models/enum/relationships_enum_type"
	"blog_server/services/comment_service"
	"blog_server/services/focus_service"
	"blog_server/utils"
	"blog_server/utils/jwts"
	"github.com/gin-gonic/gin"
)

func (CommentApi) CommentTreeView(c *gin.Context) {
	var cr = middlware.GetBind[models.IdRequest](c)
	var article models.ArticleModel
	err := global.DB.Take(&article, "status = ? and id =?", enum.ArticleStatusPublishes, cr.ID).Error
	if err != nil {
		res.FailWithMsg("文章不存在", c)
		return
	}
	var userRelationMap = map[uint]relationships_enum_type.Relation{}
	var userDiggCommentMap = map[uint]bool{}
	claims, err := jwts.ParseTokenByGin(c)
	if err == nil && claims != nil {
		//登陆了
		var commentList []models.CommentModel //文章的评论列表
		global.DB.Find(&commentList, "article_id = ?", cr.ID)
		if len(commentList) > 0 {
			//查我的点赞的评论列表
			var commentIDList []uint
			var userIDList []uint
			for _, model := range commentList {
				commentIDList = append(commentIDList, model.ID)
				userIDList = append(userIDList, model.UserID)
			}
			userIDList = utils.Unique(userIDList) //对用户id去重
			userRelationMap = focus_service.CalculatingPatchFriendRelationships(claims.UserId, userIDList)
			var commentDiggList []models.CommentDiggModel
			global.DB.Find(&commentDiggList, "user_id =? and comment_id in ?", claims.UserId, commentIDList)
			for _, model := range commentDiggList {
				userDiggCommentMap[model.CommentID] = true
			}
		}
	}

	//查找根评论
	var commentList []models.CommentModel
	global.DB.Order("created_at desc").Find(&commentList, "article_id = ? and parent_id is null", cr.ID)
	var list = make([]comment_service.CommentResponse, 0)
	for _, model := range commentList {
		response := comment_service.GetCommentTreeV3(model.ID, userRelationMap,
			userDiggCommentMap)
		list = append(list, *response)
	}

	res.OkWithList(list, len(list), c)
}

services/comment_service/enter.go

Go 复制代码
package comment_service

import (
	"blog_server/global"
	"blog_server/models"
	"blog_server/models/enum/relationships_enum_type"
	"blog_server/services/redis_service/redis_comment"
	"time"
)

// 获取一个评论的根评论
func GetRootComment(commentID uint) (model *models.CommentModel) {
	var comment models.CommentModel
	err := global.DB.Take(&comment, commentID).Error
	if err != nil {
		return nil
	}
	if comment.ParentID == nil {
		//没有父评论了,那么自己就是根评论
		return &comment
	}
	return GetRootComment(*comment.RootParentID)

}

// 获取多有的父评论
func GetParents(commentID uint) (list []models.CommentModel) {
	var comment models.CommentModel
	err := global.DB.Take(&comment, commentID).Error
	if err != nil {
		return
	}

	list = append(list, comment)
	if comment.ParentID != nil {
		list = append(list, GetParents(*comment.ParentID)...)
	}
	return
}

// 获取评论树
func GetCommentTree(model *models.CommentModel) {
	global.DB.Preload("SubCommentList").Take(model)
	for _, commentModel := range model.SubCommentList {
		GetCommentTree(commentModel)
	}
}

// 获取评论树版本2
func GetCommentTreeV2(id uint) (model *models.CommentModel) {
	model = &models.CommentModel{
		Model: models.Model{ID: id},
	}
	global.DB.Preload("SubCommentList").Take(model)
	for i := 0; i < len(model.SubCommentList); i++ {
		commentModel := model.SubCommentList[i]
		item := GetCommentTreeV2(commentModel.ID)
		model.SubCommentList[i] = item

	}
	return
}

// 评论一维化
func GetCommentOneDimensional(id uint) (list []models.CommentModel) {
	model := models.CommentModel{
		Model: models.Model{ID: id}}
	global.DB.Preload("SubCommentList").Take(&model)
	list = append(list, model)
	for _, commentModel := range model.SubCommentList {
		subList := GetCommentOneDimensional(commentModel.ID)
		list = append(list, subList...)
	}
	return
}

type CommentResponse struct {
	ID           uint                             `json:"id"`
	CreatedAt    time.Time                        `json:"created_at"`
	Content      string                           `json:"content"`
	UserID       uint                             `json:"user_id"`
	UserNickname string                           `json:"user_nickname"`
	UserAvatar   string                           `json:"user_avatar"`
	ArticleID    uint                             `json:"article_id"`
	ParentID     *uint                            `json:"parent_id"`
	DiggCount    uint                             `json:"digg_count"`
	ApplyCount   int                              `json:"apply_count"`
	SubComments  []*CommentResponse               `json:"sub_comments"`
	IsDigg       bool                             `json:"is_digg"`
	Relationship relationships_enum_type.Relation `json:"relationship"`
}

// 获取评论树版本3返回用户等信息
func GetCommentTreeV3(id uint, userRelationMap map[uint]relationships_enum_type.Relation, userDiggMap map[uint]bool) (res *CommentResponse) {
	return getCommentTreeV3(id, 1, userRelationMap, userDiggMap)
}
func getCommentTreeV3(id uint, line int, userRelationMap map[uint]relationships_enum_type.Relation, userDiggMap map[uint]bool) (res *CommentResponse) {
	model := &models.CommentModel{
		Model: models.Model{ID: id},
	}
	global.DB.Preload("UserModel").Preload("SubCommentList").Take(model)

	res = &CommentResponse{
		ID:           model.ID,
		CreatedAt:    model.CreatedAt,
		Content:      model.Content,
		UserID:       model.UserID,
		UserNickname: model.UserModel.Nickname,
		UserAvatar:   model.UserModel.Avatar,
		ArticleID:    model.ArticleID,
		ParentID:     model.ParentID,
		DiggCount:    model.DiggCount + uint(redis_comment.GetCacheDigg(model.ID)), // 获取评论点赞数
		ApplyCount:   redis_comment.GetCacheApply(model.ID),                        // 获取评论数
		SubComments:  make([]*CommentResponse, 0),
		IsDigg:       userDiggMap[model.ID],
		Relationship: userRelationMap[model.UserID],
	}
	if line >= global.Config.Site.Article.CommentLine {
		return
	}
	for _, commentModel := range model.SubCommentList {
		res.SubComments = append(res.SubComments, getCommentTreeV3(commentModel.ID, line+1, userRelationMap, userDiggMap))
	}
	return
}

19.评论点赞删除优化

api/comment_api/comment_digg.go

Go 复制代码
package comment_api

import (
	"blog_server/common/res"
	"blog_server/global"
	"blog_server/middlware"
	"blog_server/models"
	"blog_server/services/message_service"
	"blog_server/services/redis_service/redis_comment"
	"blog_server/utils/jwts"
	"github.com/gin-gonic/gin"
)

func (CommentApi) CommentDiggView(c *gin.Context) {
	cr := middlware.GetBind[models.IdRequest](c)

	var comment models.CommentModel
	err := global.DB.Take(&comment, cr.ID).Error //查询已发布的
	if err != nil {
		res.FailWithMsg("评论不存在", c)
		return
	}

	claims := jwts.GetClaims(c)
	//查一下之前有没有点赞
	var UserDiggComment models.CommentDiggModel
	err = global.DB.Take(&UserDiggComment, "user_id = ? and comment_id = ?", claims.UserId, comment.ID).Error
	if err != nil {
		//不存在-》点赞
		model := models.CommentDiggModel{
			UserID:    claims.UserId,
			CommentID: comment.ID,
		}
		err = global.DB.Create(&model).Error
		if err != nil {
			res.FailWithMsg("点赞失败", c)
			return
		}

		redis_comment.SetCacheDigg(cr.ID, 1)
		//给评论拥有者发消息
		message_service.InsertDiggCommentMessage(model)
		res.OkWithMsg("点赞成功", c)
		return
	}

	global.DB.Delete(&UserDiggComment)
	redis_comment.SetCacheDigg(cr.ID, -1)
	res.OkWithMsg("取消点赞成功", c)
	return
}

20.添加标签列表接口

api/search_api/tag_agg.go

Go 复制代码
package search_api

import (
	"blog_server/common"
	"blog_server/common/res"
	"blog_server/global"
	"blog_server/middlware"
	"blog_server/models"
	"encoding/json"
	"github.com/gin-gonic/gin"
	"github.com/olivere/elastic/v7"
	"github.com/sirupsen/logrus"
	"golang.org/x/net/context"
	"sort"
)

type TagAggResponse struct {
	Tag          string `json:"tag"`
	ArticleCount int    `json:"articleCount"`
}
type AggType struct {
	DocCountErrorUpperBound int `json:"doc_count_error_upper_bound"`
	SumOtherDocCount        int `json:"sum_other_doc_count"`
	Buckets                 []struct {
		Key      string `json:"key"`
		DocCount int    `json:"doc_count"`
	} `json:"buckets"`
}

type AggCount struct {
	Value int `json:"value"`
}

// 使用es聚合查询&&mysl查询标签云
func (SearchApi) TagAggView(c *gin.Context) {
	var cr = middlware.GetBind[common.PageInfo](c)
	var list = make([]TagAggResponse, 0)
	if global.ESClient == nil {
		//如果没有配置es,服务降级使用mysql查询。分页查询暂时,没做
		var articleList []models.ArticleModel
		global.DB.Find(&articleList, "tag_list <> '' ")
		var tagMap = map[string]int{}
		for _, model := range articleList {
			for _, tag := range model.TagList {
				count, ok := tagMap[tag]
				if !ok {
					tagMap[tag] = 1
					continue
				}
				tagMap[tag] = count + 1
			}
		}
		for tag, count := range tagMap {
			list = append(list, TagAggResponse{
				Tag:          tag,
				ArticleCount: count,
			})
		}
		//排序
		sort.Slice(list, func(i, j int) bool {
			return list[i].ArticleCount > list[j].ArticleCount
		})
		res.OkWithList(list, len(list), c)

		return
	}

	agg := elastic.NewTermsAggregation().Field("tag_list")
	agg.SubAggregation("page", elastic.NewBucketSortAggregation().From(cr.GetOffset()).Size(cr.Limit))
	agg.SubAggregation("sum", elastic.NewSumAggregation().Field("tag_list"))
	query := elastic.NewBoolQuery()
	query.MustNot(elastic.NewTermQuery("tag_list", ""))
	result, err := global.ESClient.Search(models.ArticleModel{}.Index()).Query(query).Aggregation("tags", agg).Aggregation("tag_count", elastic.NewCardinalityAggregation().Field("tag_list")).Size(0).Do(context.Background())
	if err != nil {
		logrus.Errorf("查询失败 %s", err)
		res.FailWithMsg("查询失败", c)
		return
	}
	var count AggCount
	json.Unmarshal(result.Aggregations["tag_count"], &count)
	var t AggType
	var val = result.Aggregations["tags"]
	err = json.Unmarshal(val, &t)
	if err != nil {
		logrus.Errorf("解析json失败 %s", err)
		res.FailWithMsg("查询失败", c)
		return
	}
	for _, bucket := range t.Buckets {
		list = append(list, TagAggResponse{
			Tag:          bucket.Key,
			ArticleCount: bucket.DocCount,
		})
	}

	res.OkWithList(list, count.Value, c)
}
复制代码
r.GET("search/tags", middlware.BindQueryMiddleware[common.PageInfo], app.TagAggView)

21.首页作者推荐

api/article_api/auth_recommend.go

Go 复制代码
package article_api

import (
	"blog_server/common"
	"blog_server/common/res"
	"blog_server/global"
	"blog_server/middlware"
	"blog_server/models"
	"blog_server/models/enum/relationships_enum_type"
	"blog_server/services/focus_service"
	"blog_server/utils/jwts"
	"fmt"
	"github.com/gin-gonic/gin"
)

type AuthRecommendResponse struct {
	UserID       uint   `json:"user_id"`
	UserNickname string `json:"user_nickname"`
	UserAvatar   string `json:"user_avatar"`
	UserAbstract string `json:"user_abstract"`
}

// 作者推荐
// 查询我还没有关注的,且发了文章的用户,
func (ArticleApi) AuthRecommendView(c *gin.Context) {
	cr := middlware.GetBind[common.PageInfo](c)
	var count int
	var userIDList []uint
	global.DB.Model(models.ArticleModel{}).Group("user_id").Select("count(*)").Scan(&count)
	global.DB.Model(models.ArticleModel{}).Group("user_id").
		Offset(cr.GetOffset()).Limit(cr.GetLimit()).
		Select("user_id").Scan(&userIDList)
	claims, err := jwts.ParseTokenByGin(c)

	if err == nil && claims != nil {
		m := focus_service.CalculatingPatchFriendRelationships(claims.UserId, userIDList)

		userIDList = []uint{}
		for u, relation := range m {
			if relation == relationships_enum_type.RelationStranger || relation == relationships_enum_type.RelationFocus {
				fmt.Println("##:", u)
				userIDList = append(userIDList, u)
			}
		}
	}
	fmt.Println("@@@@", userIDList)

	var userList []models.UserModel
	global.DB.Debug().Find(&userList, "id in ?", userIDList)
	var list = make([]AuthRecommendResponse, 0)
	for _, model := range userList {
		list = append(list, AuthRecommendResponse{
			UserID:       model.ID,
			UserNickname: model.Nickname,
			UserAvatar:   model.Avatar,
			UserAbstract: model.Abstract,
		})
	}
	res.OkWithList(list, count, c)

}

22.文章推荐

api/article_api/article_recommend.go

Go 复制代码
package article_api

import (
	"blog_server/common"
	"blog_server/common/res"
	"blog_server/global"
	"blog_server/middlware"
	"blog_server/models"
	"github.com/gin-gonic/gin"
)

type ArticleRecommendResponse struct {
	ID        uint   `json:"id" gorm:"column:id"`
	Title     string `json:"title" gorm:"column:title"`
	LookCount int    `json:"lookCount" gorm:"column:lookCount"`
}

// 推荐每天浏览量最高的文章
func (ArticleApi) ArticleReCommendView(c *gin.Context) {
	cr := middlware.GetBind[common.PageInfo](c)
	var list = make([]ArticleRecommendResponse, 0)
	global.DB.Debug().Model(models.ArticleModel{}).Order("look_count desc").Where("date(created_at) = date(now())").Limit(cr.Limit).Select("id", "title", "look_count").Scan(&list)
	res.OkWithList(list, len(list), c)

}
复制代码
rg.GET("article/auth_recommend", middlware.BindQueryMiddleware[common.PageInfo], app.AuthRecommendView)
rg.GET("article/article_recommend", middlware.BindQueryMiddleware[common.PageInfo], app.ArticleReCommendView)

23.banner添加字段type,

api/banner_api/enter.go

Go 复制代码
package banner_api

import (
	"blog_server/common"
	"blog_server/common/res"
	"blog_server/global"
	"blog_server/models"
	"fmt"
	"github.com/gin-gonic/gin"
)

type BannerCreateRequest struct {
	Cover string `json:"cover" binding:"required"`
	Href  string `json:"href"`
	Show  bool   `json:"show"`
	Type  int8   `json:"type" binding:"required,oneof=1 2"`
}
type BannerApi struct {
}

func (BannerApi) BannerCreateView(c *gin.Context) {
	var cr BannerCreateRequest
	err := c.ShouldBindJSON(&cr)
	if err != nil {
		res.FailWithError(err, c)
		return
	}
	err = global.DB.Create(&models.BannerModel{
		Cover: cr.Cover,
		Href:  cr.Href,
		Show:  cr.Show,
		Type:  cr.Type,
	}).Error
	if err != nil {
		res.FailWithError(err, c)
		return
	}
	res.OkWithMsg("添加banner成功", c)

}

type BannerListRequest struct {
	common.PageInfo
	Show bool `form:"show"`
	Type int8 `form:"type"` //1-banner 2-独家推广
}

func (BannerApi) BannerListView(c *gin.Context) {

	var cr BannerListRequest
	c.ShouldBindQuery(&cr)
	list, count, _ := common.ListQuery(models.BannerModel{
		Show: cr.Show,
		Type: cr.Type,
	}, common.Options{
		PageInfo: cr.PageInfo,
	})

	res.OkWithList(list, count, c)

}

func (BannerApi) BannerRemoveView(c *gin.Context) {
	var cr models.IdRemoveRequest
	err := c.ShouldBindJSON(&cr)
	if err != nil {
		res.FailWithError(err, c)
		return
	}
	var list []models.BannerModel
	global.DB.Find(&list, "id in ?", cr.IDList)
	if len(list) > 0 {
		global.DB.Delete(&list)
	}
	res.OkWithMsg(fmt.Sprintf("删除banner%d个,成功%d个", len(cr.IDList), len(list)), c)
}

func (BannerApi) BannerUpdateView(c *gin.Context) {
	var id models.IdRequest
	err := c.ShouldBindUri(&id)
	if err != nil {
		res.FailWithError(err, c)
		return
	}
	var cr BannerCreateRequest
	err = c.ShouldBindJSON(&cr)
	if err != nil {
		res.FailWithError(err, c)
		return
	}
	var model models.BannerModel
	err = global.DB.Take(&model, id.ID).Error
	if err != nil {
		res.FailWithMsg("不存在的id", c)
	}

	err = global.DB.Model(&model).Updates(map[string]any{
		"cover": cr.Cover,
		"href":  cr.Href,
		"show":  cr.Show,
	}).Error
	if err != nil {
		res.FailWithError(err, c)
		return
	}
	res.OkWithMsg("banner更新成功", c)
}

models/banner_model.go

Go 复制代码
package models

// banner表
type BannerModel struct {
	Model
	Cover string ` json:"cover"` //	图片首页
	Href  string ` json:"href"`  //图片链接
	Show  bool   `json:"show"`   //是否展示
	Type  int8   `json:"type"`   //1-banner 2-独家推广
}

24.站内信-消息列表添加好友关系

api/site_msg_api/msg_list.go

Go 复制代码
package site_msg_api

import (
	"blog_server/common"
	"blog_server/common/res"
	"blog_server/global"
	"blog_server/middlware"
	"blog_server/models"
	"blog_server/models/enum/message_enum_type"
	"blog_server/models/enum/relationships_enum_type"
	"blog_server/services/focus_service"
	"blog_server/utils/jwts"
	"github.com/gin-gonic/gin"
)

type SiteMsgListRequest struct {
	common.PageInfo
	T int8 `form:"t" binding:"required,oneof=1 2 3"` //1评论回复 2赞和收藏 3系统
}

type SiteMsgListResponse struct {
	models.MessageModel
	Relationship relationships_enum_type.Relation `json:"relationship"`
}

func (SiteMsgApi) SiteMsgListView(c *gin.Context) {
	cr := middlware.GetBind[SiteMsgListRequest](c)
	claims := jwts.GetClaims(c)
	var typeList []message_enum_type.Type
	switch cr.T {
	case 1:
		typeList = append(typeList, message_enum_type.CommentType, message_enum_type.ApplyType)

	case 2:
		typeList = append(typeList, message_enum_type.DiggArticleTypeType, message_enum_type.DiggCommentTypeType, message_enum_type.CollectArticleType)

	case 3:
		typeList = append(typeList, message_enum_type.SystemTypeType)

	}

	_list, count, _ := common.ListQuery(models.MessageModel{
		RevUserID: claims.UserId,
	}, common.Options{
		PageInfo: cr.PageInfo,
		Where:    global.DB.Where("type in ?", typeList),
		Debug:    true,
	})

	var userIDList []uint
	for _, model := range _list {
		if model.ActionUserID != 0 {
			userIDList = append(userIDList, model.ActionUserID)
		}
	}
	var m = map[uint]relationships_enum_type.Relation{}
	if len(userIDList) > 0 {
		m = focus_service.CalculatingPatchFriendRelationships(claims.UserId, userIDList)
	}
	var list = make([]SiteMsgListResponse, 0)
	for _, model := range _list {
		list = append(list, SiteMsgListResponse{
			MessageModel: model,
			Relationship: m[model.ActionUserID],
		})
	}
	res.OkWithList(list, count, c)

}

25.批量取消文章收藏优化

api/article_api/article_collect.go

Go 复制代码
package article_api

import (
	"blog_server/common/res"
	"blog_server/global"
	"blog_server/middlware"
	"blog_server/models"
	"blog_server/models/enum"
	"blog_server/services/message_service"
	"blog_server/services/redis_service/redis_article"
	"blog_server/utils/jwts"
	"fmt"
	"github.com/gin-gonic/gin"
)

type ArticleCollectRequest struct {
	ArticleId uint `json:"article_id" binding:"required"`
	CollectID uint `json:"collect_id"`
}

func (ArticleApi) ArticleCollectView(c *gin.Context) {
	cr := middlware.GetBind[ArticleCollectRequest](c)
	var article models.ArticleModel
	err := global.DB.Take(&article, "status = ? and id = ?", enum.ArticleStatusPublishes, cr.ArticleId).Error //查询已发布的
	if err != nil {
		res.FailWithMsg("文章不存在", c)
		return
	}
	var collectModel models.CollectModel
	claims, err := jwts.ParseTokenByGin(c)
	if cr.CollectID == 0 {
		//默认收藏夹

		err = global.DB.Take(&collectModel, "user_id =? and is_default = ?", claims.UserId, 1).Error
		if err != nil {
			//创建一个默认收藏夹
			collectModel.Title = "默认收藏夹"
			collectModel.UserID = claims.UserId
			collectModel.IsDefault = true
			global.DB.Create(&collectModel)

		}
		cr.CollectID = collectModel.ID
	} else {
		//判断收藏夹是否存在并且是否是自己创建的

		err = global.DB.Take(&collectModel, "user_id =?", claims.UserId).Error
		if err != nil {
			res.FailWithMsg("收藏夹不存在", c)
			return
		}
	}
	//判断是否收藏
	var articleCollect models.UserArticleCollectMode
	err = global.DB.Where(models.UserArticleCollectMode{
		UserID:    claims.UserId,
		ArticleID: cr.ArticleId,
		CollectID: cr.CollectID,
	}).Take(&articleCollect).Error
	if err != nil {
		//没有就收藏
		model := models.UserArticleCollectMode{
			UserID:    claims.UserId,
			ArticleID: cr.ArticleId,
			CollectID: cr.CollectID,
		}
		err = global.DB.Create(&model).Error
		if err != nil {
			res.FailWithMsg("收藏失败", c)
			return
		}
		//对收藏夹进行加1

		redis_article.SetCacheCollect(cr.CollectID, true)
		//给文章拥有者发消息
		message_service.InsertCollectArticleMessage(model)
		res.FailWithMsg("收藏成功", c)

		//global.DB.Model(&collectModel).Update("article_count", gorm.Expr("article_count + 1"))
		return
	}
	//取消收藏
	//err = global.DB.Where(models.UserArticleCollectMode{
	//	UserID:    claims.UserId,
	//	ArticleID: cr.ArticleId,
	//	CollectID: cr.CollectID,
	//}).Delete(&models.UserArticleCollectMode{}).Error

	err = global.DB.Delete(&articleCollect).Error
	if err != nil {
		res.FailWithMsg("取消收藏失败", c)
		return
	}
	res.FailWithMsg("取消收藏成功", c)
	//对收藏夹进行-1
	//global.DB.Model(&collectModel).Update("article_count", gorm.Expr("article_count - 1"))
	//收藏数同步缓存
	redis_article.SetCacheCollect(cr.CollectID, false)
	return

}

type ArticleCollectPatchRequest struct {
	CollectID     uint   `json:"collect_id"`
	ArticleIDList []uint `json:"article_id_list"`
}

func (ArticleApi) ArticleCollectPatchRemove(c *gin.Context) {
	var cr = middlware.GetBind[ArticleCollectPatchRequest](c)
	claims := jwts.GetClaims(c)
	var userCollectList []models.UserArticleCollectMode
	global.DB.Find(&userCollectList, "collect_id = ? and article_id in ? and user_id = ?", cr.CollectID, cr.ArticleIDList, claims.UserId)

	if len(userCollectList) > 0 {
		global.DB.Debug().Delete(&userCollectList)
		for _, u := range cr.ArticleIDList {
			redis_article.SetCacheCollect(u, false)
		}
	}
	res.FailWithMsg(fmt.Sprintf("批量删除文章收藏%d个", len(userCollectList)), c)

}
复制代码
rg.DELETE("article/collect", middlware.AuthMiddleware, middlware.BindMiddleware[article_api.ArticleCollectPatchRequest], app.ArticleCollectPatchRemove) //批量取消文章收藏

26.添加查用户所有的收藏文章

api/article_api/article_list.go

Go 复制代码
package article_api

import (
	"blog_server/common"
	"blog_server/common/res"
	"blog_server/global"
	"blog_server/middlware"
	"blog_server/models"
	"blog_server/models/enum"
	"blog_server/services/redis_service/redis_article"
	"blog_server/utils/jwts"
	"blog_server/utils/sql"
	"fmt"
	"github.com/gin-gonic/gin"
)

//可以查某个用户发布的文章,只能查已发布的,不需要登录,支持分类查询
//	可以查某个人用户收藏的文章,前提是这个用户开了对应隐私设置
//用户侧 查自己发布的文章,只能查已发布的,,	需要登录, 支持分类查询
//	也能查自己收藏的文章,不会受到自己的隐私设置
//	支持按照状态查询,已发布,草稿箱,待审核
//管理员侧 查全部,支持按照用户搜索,状态过滤,文章标题模糊匹配,分类过滤

type ArticleListRequest struct {
	common.PageInfo
	Type       int8                   `form:"type" binding:"required,oneof=1 2 3"` //1-用户查别人的, 2-查自己的 3-管理员查询
	CategoryID *uint                  `form:"category_id"`
	UserID     uint                   `form:"user_id"`
	Status     enum.ArticleStatusType `form:"status"`     //状态 草稿,审核中,已发布
	CollectID  int                    `form:"collect_id"` //id =-1为查所有的收藏文章
}

type ArticleListResponse struct {
	models.ArticleModel
	UserTop       bool    `json:"user_top"`       //是否是用户置顶
	AdminTop      bool    `json:"admin_top"`      //是否是管理员置顶
	CategoryTitle *string `json:"category_title"` //使用指针,可以使在json序列化中进行判断,""为空值。nill为没有传递
	UserNickname  string  `json:"user_nickname"`
	UserAvatar    string  `json:"user_avatar"`
}

func (ArticleApi) ArticleListView(c *gin.Context) {

	var topArticleIdList []uint //用户置顶列表
	var orderColumnMap = map[string]bool{
		"look_count desc":    true,
		"digg_count desc":    true,
		"comment_count desc": true,
		"collect_count desc": true,
		"look_count":         true,
		"digg_count":         true,
		"comment_count":      true,
		"collect_count":      true,
	}
	cr := middlware.GetBind[ArticleListRequest](c)
	switch cr.Type {
	case 1:
		//查别人的用户id就是必填的
		if cr.UserID == 0 {
			res.FailWithMsg("用户id必填", c)
			return
		}
		if cr.Page > 2 || cr.Limit > 10 {
			res.FailWithMsg("查询更多请登录", c)
			return
		}
		cr.Status = 0
		cr.Order = ""
		if cr.CollectID != 0 {
			if cr.UserID == 0 {
				res.FailWithMsg("请传入用户ID", c)
				return
			}
			var userConf models.UserConfModel
			err := global.DB.Take(&userConf, "user_id = ?", cr.UserID).Error
			if err != nil {
				res.FailWithMsg("用户不存在", c)
				return
			}

			if !userConf.OpenCollect {
				//不公开我的收藏
				//在页面上,我的收藏页面不可见
				//收藏列表接口不能访问,不能用这个收藏夹 id 查文章
				res.FailWithMsg("用户未开启我的收藏", c)
				return
			}
		}
	case 2:
		//查自己
		claims, err := jwts.ParseTokenByGin(c)
		if err != nil {
			res.FailWithMsg("请登录", c)
			return
		}
		cr.UserID = claims.UserId

	case 3:
		//管理员
		claims, err := jwts.ParseTokenByGin(c)
		if !(err == nil && claims.RoleId == enum.AdminRole) {
			res.FailWithMsg("角色错误", c)
			return
		}
	}
	//根据收藏夹ID查文章
	query := global.DB.Where("")
	if cr.CollectID != 0 {
		var articleIDList []uint
		if cr.CollectID != -1 {
			global.DB.Model(models.UserArticleCollectMode{}).Where("collect_id =? ", cr.CollectID).Select("article_id").Scan(&articleIDList)
		} else {
			//查这个人的所有·的收藏ID
			if cr.UserID == 0 {
				res.FailWithMsg("查所有的收藏文章,需要用户id", c)
				return
			}
			global.DB.Model(models.UserArticleCollectMode{}).Where("user_id =? ", cr.UserID).Select("article_id").Scan(&articleIDList)
		}

		query.Where("id in ?", articleIDList)
	}
	if cr.Order != "" {
		_, ok := orderColumnMap[cr.Order]
		if !ok {
			res.FailWithMsg("不支持的排序方式", c)
			return
		}
	}

	//处理用户置顶
	var userTopMap = map[uint]bool{}
	var adminTopMap = map[uint]bool{}
	if cr.UserID != 0 {
		var userTopArticleList []models.UserTopArticleModel
		global.DB.Preload("UserModel").Order("created_at desc").Find(&userTopArticleList, "user_id = ?", cr.UserID)

		for _, i2 := range userTopArticleList {
			topArticleIdList = append(topArticleIdList, i2.ArticleID)
			if i2.UserModel.Role == enum.AdminRole {
				adminTopMap[i2.ArticleID] = true
			}
			userTopMap[i2.ArticleID] = true
		}
	}

	//判断是否有置顶,有的话话再按照置顶列表进行排序,否则就按照时间倒叙排序
	var options = common.Options{
		Likes:        []string{"title"},
		PageInfo:     cr.PageInfo,
		DefaultOrder: "created_at desc",
		Where:        query,
		PreLoads:     []string{"CategoryModel", "UserModel"},
	}
	if len(topArticleIdList) > 0 {
		options.DefaultOrder = fmt.Sprintf("%s,created_at desc", sql.ConvertSliceOrderSql(topArticleIdList)) // [1 2 3] = >(1,2,3)或者 id= 1 desc,id = 2 desc 使用辅助函数实现ConvertSliceOrderSql
	}

	_list, count, _ := common.ListQuery(models.ArticleModel{
		UserID:     cr.UserID,
		CategoryID: cr.CategoryID,
		Status:     cr.Status,
	}, options)

	var list = make([]ArticleListResponse, 0)
	//从缓存中获取浏览量和点赞数
	collectMap := redis_article.GetAllCacheCollect()
	lookMap := redis_article.GetAllCacheLook()
	diggMap := redis_article.GetAllCacheDigg()
	commentMap := redis_article.GetAllCacheComment() //获取评论
	for _, model := range _list {
		model.Content = ""
		model.DiggCount = model.DiggCount + diggMap[model.ID]
		model.LookCount = model.LookCount + lookMap[model.ID]
		model.CollectCount = model.CollectCount + collectMap[model.ID]
		model.CommentCount = model.CommentCount + commentMap[model.ID] //评论数统计
		data := ArticleListResponse{
			ArticleModel: model,
			UserTop:      userTopMap[model.ID],
			AdminTop:     adminTopMap[model.ID],
			UserNickname: model.UserModel.Nickname,
			UserAvatar:   model.UserModel.Avatar,
		}
		//如果有关联的分类表,就将分类表中的title传递过来(使用指针,如果是)
		if model.CategoryModel != nil {
			data.CategoryTitle = &model.CategoryModel.Title
		}
		list = append(list, data)
	}

	res.OkWithList(list, count, c)

}

27.用户删除

api/user_api/user_remove.go

Go 复制代码
package user_api

import (
	"blog_server/common/res"
	"blog_server/global"
	"blog_server/middlware"
	"blog_server/models"
	"fmt"
	"github.com/gin-gonic/gin"
)

func (UserApi) UserRemoveView(c *gin.Context) {
	cr := middlware.GetBind[models.IdRemoveRequest](c)

	var list []models.UserModel
	err := global.DB.Find(&list, "id =? ", cr.IDList).Error

	if len(list) > 0 {
		err = global.DB.Delete(&list).Error
		if err != nil {
			res.FailWithMsg("删除用户失败", c)
			return
		}
	}

	res.OkWithMsg(fmt.Sprintf("成功删除%d条数据", len(list)), c)

}

models/user_model.go

Go 复制代码
func (u *UserModel) BeforeDelete(tx *gorm.DB) (err error) {
	var list = []any{
		ArticleModel{},
		ArticleDiggModel{},
		CategoryModel{},
		CollectModel{},
		CommentModel{},
		CommentDiggModel{},
		LogModel{},
		UserArticleCollectMode{},
		UserArticleLookHistoryModel{},
		UserChatActionModel{},
		UserFocusModel{},
		UserGlobalNotificationModel{},
		UserLoginModel{},
		UserTopArticleModel{},
	}
	for _, model := range list {
		count := tx.Delete(&model, "user_id =?", u.ID).RowsAffected
		logrus.Infof("删除 %s 成功%d 条", reflect.TypeOf(model).Name(), count)
	}
	var chatList []ChatModel
	tx.Find(&chatList, "send_user_id = ? or rev_user_id =?", u.ID, u.ID).Delete(&chatList)
	logrus.Infof("删除关联对话%d 条", len(chatList))

	var messageList []MessageModel
	tx.Find(&messageList, "rev_user_id =? ", u.ID).Delete(&messageList)
	logrus.Infof("删除关联消息%d 条", len(messageList))
	return nil
}
复制代码
r.DELETE("user", middlware.AdminMiddleware, middlware.BindMiddleware[models.IdRemoveRequest], app.UserRemoveView)

=============================

缓存策略

相关推荐
染指11102 天前
28.实现MDL驱动读写-Windows驱动
windows·驱动开发·驱动·mdl
颜子鱼3 天前
Linux字符设备驱动
linux·c语言·驱动开发
染指11103 天前
25.实现过TP保护ACE保护NP保护BE保护EAC保护-内存读写检测(私有句柄表和全局句柄表的句柄提权)-Windows驱动
windows·驱动开发·windows驱动·句柄提权
染指11104 天前
24.IDA逆向句柄表算法-Windows驱动
windows·驱动开发·内核·保护·windows驱动
Guistar~~4 天前
【Linux驱动开发IMX6ULL】使用NXP MfgTool 烧写系统到eMMC
linux·运维·驱动开发
Y unes4 天前
《uboot基础命令记录①》
linux·驱动开发·嵌入式硬件·mcu·ubuntu·uboot
GesLuck5 天前
伺服电机(200 smart & )调试文档
开发语言·驱动开发·硬件工程
chen_mangoo5 天前
Android10低电量无法打开相机
android·linux·驱动开发·嵌入式硬件
傻啦嘿哟5 天前
实战:用GraphQL接口高效采集数据
开发语言·驱动开发·php