#文章管理
在文章管理模块主要实现了文章的添加,删除,编辑等功能。
##发布文章
效果图如下:
该页面主要实现了文章的发布,用户在填写相应的信息之后点击提交按钮将文章信息提交到后台,主要的保存思路如下:
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)
}
##编辑文章
效果图如下:
用户在点击编辑按钮,提交对应的博文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)
}
##删除文章
效果图如下:
用户在点击删除按钮的时候会给与相应的删除提示,该操作需要谨慎操作,因为数据一旦删除无法恢复。在用户确定删除之后,将对应的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)
}
#标签管理
效果图如下:
标签管理模块主要实现了标签的合并和删除。
##标签合并
主要思路:
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在文章表中找到对应的记录,将标签名替换为逗号,最后删除标签文章表中相关的记录,相关代码同上。