#文章管理
在文章管理模块主要实现了文章的添加,删除,编辑等功能。
##发布文章
效果图如下:
博客内容管理实现_区块链
该页面主要实现了文章的发布,用户在填写相应的信息之后点击提交按钮将文章信息提交到后台,主要的保存思路如下:
1.获取用户输入的文章信息,插入数据库。
2.获得用户输入的标签,因为用户输入的标签很可能有多个,所以我们在通过逗号切割之后,取出每个标签之后,去除两边的空格,然后判断用户输入的标签是否有重复,如果有重复需要去重。
3.在标签表判断是否有对应的标签,没有对应的标签需要创建标签,如果有对应的标签,更新count字段。
4.将文章id和标签id组成对应的记录插入到标签文章表中。
5.用处理后的标签在文章表中做一个覆盖。

func (this *ArticleController) Save() {
	//创建文章结构体
	var post models.Post
	// title  color  istop  tags  posttime  status  content
	//获取用户输入的标题
	post.Title = strings.TrimSpace(this.GetString("title"))
	if post.Title == "" {
		this.showmsg("标题不能为空!")
	}
	//获取用户输入的颜色
	post.Color = strings.TrimSpace(this.GetString("color"))
	//获取是否置顶
	if strings.TrimSpace(this.GetString("istop")) == "1" {
		post.Istop = 1
	}
	//获取用户输入的标签
	tags := strings.TrimSpace(this.GetString("tags"))
	//获取文章发布时间
	timestr := strings.TrimSpace(this.GetString("posttime"))
	//获取文章的状态
	post.Status, _ = this.GetInt("status")
	post.Content = this.GetString("content")
	//设置用户
	post.Userid = this.userid
	//设置作者
	post.Author = this.username
	//设置更新时间
	post.Updated = this.getTime()
	//设置随机数种子
	rand.Seed(time.Now().Unix())
	//长生随机数,[0,11)
	var r = rand.Intn(11)
	// /static/images/3.jpg
	post.Cover = "/static/images/" + fmt.Sprintf("%d", r) + ".jpg"
	//  Mon Jan 2 15:04:05 -0700 MST 2006
	posttime, err := time.Parse("2006-01-02 15:04:05", timestr)
	if err == nil {
		post.Posttime = posttime
	}
	//插入数据库
	if err = post.Insert(); err != nil {
		this.showmsg("文章添加失败!")
	}


	//go,数组,C,C++,go
	//go,数组,C,C++,
	//存储用户数的表标签的最终结果
	addtags := make([]string, 0)
	if tags != "" {
		//通过逗号切割用户输入的标签
		tagarr := strings.Split(tags, ",")
		for _, v := range tagarr {
			//取出用户输入的标签并去除两边的空格
			if tag := strings.TrimSpace(v); tags != "" {
				//标志位
				exists := false
				//遍历最终结果切片
				for _, vv := range addtags {
					//如果最终结果切片中存在当前标签,索命有重复,将exists设置为true,并跳出循环
					if vv == tag {
						exists = true
						break
					}
				}
				//如果标志位为false,说明最终结果切片中不存在当前标签,将当前标签追加到最终结果切片中
				if !exists {
					addtags = append(addtags, tag)
				}
			}

		}
	}


	//判断最终结果切片长度是否大于0,如果大于0,说明用户至少输入了一个合法的标签
	if len(addtags) > 0 {
		//遍历最终结果切片
		for _, v := range addtags {
			//根据用户输入的标签创建标签结构体
			tag := &models.Tag{Name:v}
			//根据标签名称在标签表中读取
			if err := tag.Read("Name"); err == orm.ErrNoRows {
				//如果没有对应记录,初始化count字段为1并插入数据库
				tag.Count = 1
				tag.Insert()
			}else {
				//存在对应的记录,更新count字段
				tag.Count += 1
				tag.Update("Count")
			}
			//根据标签id和文章id创建标签文章结构体
			tp := &models.TagPost{Tagid:tag.Id, Postid:post.Id, Poststatus:post.Status, Posttime:this.getTime()}
			//插入
			tp.Insert()
		}
		//go  数组  C
		post.Tags = "," + strings.Join(addtags, ",") + ","//,go,数组,C,
	}
	post.Updated = this.getTime()
	post.Update("tags", "updated")
	this.Redirect("/admin/article/list", 302)
}

##编辑文章
效果图如下:
博客内容管理实现_区块链_02
用户在点击编辑按钮,提交对应的博文id,在后台获取id,根据id在数据库中查找对应的文章信息并将该系你先展示在前台页面(数据回显), 用户在修改之后点击提交按钮,在后台的主要修改思路如下:
分为两种情况:
1.用户没有修改文章的标签,直接获取用户的输入,更新数据库
2.用户修改了标签,则需要更新标签表和文章标签表,
如何更新标签表?
如果用户修改之后的标签在标签表中不存在,则需要在标签表中插入对应新的记录,
如果在标签表中已经存在,则需要更新修改后的标签的count字段,以上两步结束之后,
需要更新原标签中的count字段
3.在标签文章表中将文章id和原标签id所对应的记录删除,然后在标签文章表中插入新的记录,其成员由文章id和新的标签id所组成。

func (this *ArticleController) Update() {
	post := models.Post{}
	id, err := this.GetInt("id")
	//判断是否出现了错误
	if err == nil {
		post.Id = id
		//数据库没有对应的记录
		if post.Read() != nil {
			this.Redirect("/admin/article/list", 302)
		}
	}
	// title  color  istop  tags  posttime   status   content
	post.Title = strings.TrimSpace(this.GetString("title"))
	if post.Title == "" {
		this.showmsg("标题不能为空!")
	}
	post.Color = strings.TrimSpace(this.GetString("color"))
	if strings.TrimSpace(this.GetString("istop")) == "1" {
		post.Istop = 1
	}
	tags := strings.TrimSpace(this.GetString("tags"))
	timrstr := strings.TrimSpace(this.GetString("posttime"))
	post.Status, _= this.GetInt("status")

	post.Content = strings.TrimSpace(this.GetString("content"))
	if posttime, err := time.Parse("2006-01-02 15:04:05", timrstr); err == nil {
		post.Posttime = posttime
	}
	if strings.Trim(post.Tags, ",") == tags {
		post.Update("title", "color", "istop", "posttime", "status", "content")
		this.Redirect("/admin/article/list", 302)
	}
	//判断文章原始的标签是否为空,如果不为空,需要更新count字段并且在
	//标签文章表中删除对应的记录
	if post.Tags != "" {
		var tagpost models.TagPost
		//通过当前文章id在标签文章表中过滤出指定的记录
		query := orm.NewOrm().QueryTable(&tagpost).Filter("postid", post.Id)
		var tagpostarr []*models.TagPost
		//将相关记录存入tagpostarr中
		if n, err := query.All(&tagpostarr); n > 0 && err == nil {
			for i := 0; i < len(tagpostarr); i++ {
				//根据标签id创建标签
				var tag = &models.Tag{Id:tagpostarr[i].Tagid}
				//更新count字段
				if err = tag.Read(); err == nil && tag.Count > 0 {
					tag.Count--
					tag.Update("count")
				}
			}
		}
		query.Delete()
	}
	//创建切片,用于储存过滤之后的最终结果
	addtags := make([]string, 0)
	if tags != "" {
		//  go,C,C++
		//通过逗号切割用户输入的标签
		tagarr := strings.Split(tags, ",")
		//遍历用户输入的标签
		for _ , v := range tagarr {
			//去除每一个标签两边的空格
			if tag := strings.TrimSpace(v); tag != "" {
				//标志位,用于标志是否将标签追加到最终的结果切片中
				exists := false
				//遍历最终结果切片,判断当前标签是否已经在最终结果表中已经存在,
				//如果存在,将exists赋值位true,并退出循环
				for _, vv := range addtags {
					if vv == tag {
						exists = true
						break
					}
				}
				//根据exists判断是否需要将当前标签加入最终结果切片中
				if !exists {
					addtags = append(addtags, tag)
				}
			}
		}
	}
	//更具最终结果切片中的标签id在标签表中更新count字段,或者插入标签
	//在标签文章表中插入对应记录
	if len(addtags) > 0 {
		//遍历最终结果切片
		for _, v := range addtags {
			//根据标签名称创建标签
			tag := models.Tag{Name:v}
			//根据标签名称在标签表中查询,如果不存在则插入
			if err := tag.Read("Name"); err == orm.ErrNoRows {
				tag.Count = 1
				tag.Insert()
			}else {//如果存在更新count字段
				tag.Count++
				tag.Update("Count")
			}
			//创建标签文章结构体
			tp := &models.TagPost{Tagid:tag.Id, Postid:post.Id, Poststatus:post.Status, Posttime:this.getTime()}
			//插入
			tp.Insert()
		}
		post.Tags = "," + strings.Join(addtags, ",") + ","
	}
	post.Updated = this.getTime()
	// title  color  istop  tags  posttime   status   content
	post.Update("title", "color", "istop", "tags", "posttime", "status", "content", "updated")
	this.Redirect("/admin/article/list", 302)
}

##删除文章
效果图如下:
博客内容管理实现_区块链_03
用户在点击删除按钮的时候会给与相应的删除提示,该操作需要谨慎操作,因为数据一旦删除无法恢复。在用户确定删除之后,将对应的id传递到后台,然后在数据库中根据该id将对应的记录从数据库中删除,删除成功重定向到文章列表页面,否则给与对应的错误提示。

func (this *ArticleController) Delete() {
	id, _ := this.GetInt("id")
	post := &models.Post{Id:id}
	if post.Read() == nil {
		post.Delete()
	}
	this.Redirect("/admin/article/list", 302)
}

#标签管理
效果图如下:
博客内容管理实现_区块链_04
标签管理模块主要实现了标签的合并和删除。
##标签合并
主要思路:
1.获取用户输入的目标标签,去除两边的空格,根据该标签名称去标签表中查找对应的记录,如果没有对应的记录需要插入新的记录
2.根据标签id在标签文章表中查找对应的记录,将对应的记录的标签id更新位目标标签的id
3.在文章表中将原标签替换为目标标签的名称
4.更新目标标签的count字段

func (this *TagController) batch() {
	//获取用户选择的id
	ids := this.GetStrings("ids[]")
	//获取用户的操作(删除还是合并)
	op := this.GetString("op")
	//创建切片,用于存储用户选择的切片
	idarr := make([]int, 0)
	//遍历ids
	for _, v := range ids {
		//将标签id转换为整数,并且判断id是否大于0,应为不存在小于1的id
		if id, _ := strconv.Atoi(v); id > 0 {
			//追加id
			idarr = append(idarr, id)
		}
	}
	//判断操作
	switch op {
	//合并
	case "merge":
		//获取目标签的名称,并去除两边的空格
		toname := strings.TrimSpace(this.GetString("toname"))
		//判断目标标签是否为空且用户选择的标签数量是否大于0
		if toname != "" && len(idarr) > 0 {
			//创建标签结构体
			tag := new(models.Tag)
			//设置标签的名称
			tag.Name = toname
			//根据标签的名称查询标签
			if tag.Read("name") != nil {
				//如果标签表中不存在该标签,设置count字段为0
				tag.Count = 0
				//插入标签
				tag.Insert()
			}
			//遍历用户选择的标签
			for _, id := range idarr {
				//创建标签结构体,并初始化id
				obj := models.Tag{Id:id}
				//通过id查询标签
				if obj.Read() == nil {
					//合并标签
					obj.MergeTo(tag)
					//删除原始标签
					obj.Delete()
				}
			}
			//更新标签表
			tag.UpCount()
		}
	//删除
	case "delete":
		//遍历用户选择的标签
		for _, id := range idarr {
			//创建标签结构体,并初始化id
			obj := models.Tag{Id:id}
			//根据id查询标签
			if obj.Read() == nil {
				//删除标签
				obj.Delete()
			}
		}
	}
	//重定向
	this.Redirect("/admin/tag", 302)
}

##标签删除
主要思路:
需要根据删除的标签的id在标签文章表中查找相关记录,从这些记录中获取对应的文章的id,然后根据文章id在文章表中找到对应的记录,将标签名替换为逗号,最后删除标签文章表中相关的记录,相关代码同上。