前一个项目里有一个关于评论系统的需求。感觉这个评论的实现还是蛮好玩的,所以记录下这个系统的实现相关内容。
功能需求:
1、用户可以再视屏下方留言。
2、用户可以再别的用户留言下方回复。
3、用户可以删除自己的留言,删除之后所有属于这个留言的子留言都会被删除。
分析:
看到上面这个需求的时候,想法是可以把这个留言内容作为一个“留言树”,一个留言就是一个根节点,然后留言下方的回复就是根节点下方的字节点。回复的回复就是子节点的子节点。
所以数据库的设计就要提现这几个要素:①父节点标记 ②本身节点标记 ③标记时间(用于区分两个兄弟节点谁先谁后)
“SourseID”表示这个评论所属哪个课件资源。
接下来是树的生成了,主要的设计思路如下:
1、当需要课件A的评论树的时候,先从数据库中取出所有“SourseId”为课件A的评论数据,把上述数据放入队列Queue中,并定义一个空的树Tree
2、Queue出队一个数据D,数据D在Tree中通过递归逐层查询自己的父节点,如果找到父节点就在Tree相应位置插入数据D,如果没有找到就把D再入队Queue中。
3、重复2的操作,直至Queue空队
代码如下:
/// <summary> /// 获取页面视图所需要的评论树 /// </summary> /// <returns></returns> public List<CommentTree> GetCommentsForView(int SourseId) { List<CommentTree> trees = new List<CommentTree>(); List<CommentInfEntity> comments = dal.SelectCommentInfs($" AND C.SourseID ='{SourseId}'"); Queue<CommentInfEntity> queues = new Queue<CommentInfEntity>(comments); while(queues.Count!=0) { CommentInfEntity c = queues.Dequeue(); if (c.PId == 0) { CommentTree tree = new CommentTree(); tree.Node = c; tree.Branch = new List<CommentTree>(); trees.Add(tree); trees = trees.OrderBy(x => x.Node.CommentTime).ToList(); } else if (c.PId != 0) { c.PName = comments.Where(x => x.Id == c.PId).FirstOrDefault().Name; if (!ParentIsExist(trees,c)) { queues.Enqueue(c); } } } return trees; } /// <summary> /// 迭代修改 /// </summary> /// <param name="trees"></param> /// <param name="queue"></param> /// <returns></returns> private bool ParentIsExist( List<CommentTree> trees, CommentInfEntity queue) { for(int i=0;i<trees.Count;i++) { if (trees[i].Node.Id == queue.PId) { CommentTree t = new CommentTree(); t.Branch = new List<CommentTree>(); t.Node = queue; trees[i].Branch.Add(t); trees[i].Branch = trees[i].Branch.OrderBy(x => x.Node.CommentTime).ToList(); return true; } else { if (ParentIsExist(trees[i].Branch, queue)) { return true; } } } return false; }
这样子最后的Tree就获得了。
然后在视图里面就可以用Html.RenderPartial来显示了
@foreach (CommentTree tree in ViewBag.CommentInf) { <div class="weui-cell"> <div class="weui-cell__hd"><i class="glyphicon glyphicon-user"></i></div> <div class="weui-cell__bd"> <p>@(tree.Node.Name):@(tree.Node.Comments)</p> </div> <div class="weui-cell__ft"><i commentname="@(tree.Node.Name)" commentid="@(tree.Node.Id)" class="glyphicon @(tree.Node.WeiXinPublicId == ViewBag.UserWeiXin ? "glyphicon-remove":"glyphicon-pencil") " id="@(tree.Node.WeiXinPublicId == ViewBag.UserWeiXin ? "del-comment":"rep-comment")"></i></div> </div> foreach (var node in tree.Branch) { Html.RenderPartial("../Course/CommentParyial", node); } }
部分视图:
<div class="weui-cell reply"> <div class="weui-cell__hd"><i class="glyphicon glyphicon-user"></i></div> <div class="weui-cell__bd"> <p>@Model.Node.Name 回复 @Model.Node.PName:@Model.Node.Comments</p> </div> <div class="weui-cell__ft"><i commentname="@(Model.Node.Name)" commentid="@(Model.Node.Id)" class="glyphicon @(Model.Node.WeiXinPublicId == ViewBag.UserWeiXin ? "glyphicon-remove":"glyphicon-pencil") " id="@(Model.Node.WeiXinPublicId == ViewBag.UserWeiXin ? "del-comment":"rep-comment")"></i></div> </div> @foreach (var node in Model.Branch) { Html.RenderPartial("../Course/CommentParyial", node); }
上述部分视图也是用了递归的方法。
首先在页面上面显示根节点Node,然后把子节Child点发送给部分视图页面,部分视图页面获得数据后,把得到的子节Child点作为根节点显示,然后把Child的子节点发送给部分视图。。。。。最后直至数据结束,