博客地址:ONESTARの客棧html
源碼領取方式一:前端
- 掃一掃文末二維碼,關注公衆號【編程日刊】,後臺回覆【博客】,便可領取源碼
源碼領取方式二:java
前端頁面源碼地址:github.com/oneStarLR/m…node
以jpa爲持久層源碼地址:github.com/oneStarLR/m…git
以mybatis爲持久層源碼地址:github.com/oneStarLR/m…github
歡迎給star以鼓勵(^_−)☆web
博客詳情頁面包括文章內容和評論部分,本文將從文章內容顯示和評論功能來說述SpringBoot搭建我的博客的詳情頁面顯示,因爲博客詳情和分類功能都是獨立的,這裏將評論單獨用一個類來編寫接口,查詢博客詳情就直接放在首頁的控制器進行處理spring
分析:數據庫
問:博客詳情頁面是包含文章內容和評論部分,要如何處理apache
答:在跳轉博客詳情頁面的時候,能夠返回連個model,一個是文章詳情內容,一個是評論列表
問:文章詳情內容如何處理,須要哪些接口?評論如何處理,又須要哪些接口?
答:文章詳情內容定義getDetailedBlog博客詳情接口,須要定義一個查詢實體類來封裝一下查詢內容,並把分類信息也包含進來;評論功能則須要定義listCommentByBlogId查詢評論列表接口、saveComment新增保存接口、deleteComment刪除接口
分析:
問:在博客詳情頁面中,文章顯示格式要如何處理,文章訪問數量如何處理,評論數量又該如何處理?
答:這些都在getDetailedBlog接口實現類中實現
- 文章顯示格式:使用開源的Markdown編輯器:Editor,調用工具類方法來增長擴展
- 訪問數量:在持久層接口定義方法updateViews來更新文章訪問數量,點擊文章後數值自增
- 評論數量:在持久層接口定義方法getCommentCountById來根據博客id查詢出評論數量
博客詳情除了顯示博客信息外,還須要顯示分類信息,因此還要建立分類名稱屬性,在queryvo包下建立DetailedBlog博客詳情實體類,代碼以下(省去get、set、toString方法):
package com.star.queryvo; import java.util.Date; /** * @Description: 博客詳情實體類 * @Date: Created in 10:10 2020/6/19 * @Author: ONESTAR * @QQ羣: 530311074 * @URL: https://onestar.newstar.net.cn/ */ public class DetailedBlog { //博客信息 private Long id; private String firstPicture; private String flag; private String title; private String content; private Integer views; private Integer commentCount; private Date updateTime; private boolean commentabled; private boolean shareStatement; private boolean appreciation; private String nickname; private String avatar; //分類名稱 private String typeName; } 複製代碼
在pom.xml中添加
<dependency> <groupId>com.atlassian.commonmark</groupId> <artifactId>commonmark</artifactId> <version>0.10.0</version> </dependency> <dependency> <groupId>com.atlassian.commonmark</groupId> <artifactId>commonmark-ext-heading-anchor</artifactId> <version>0.10.0</version> </dependency> <dependency> <groupId>com.atlassian.commonmark</groupId> <artifactId>commonmark-ext-gfm-tables</artifactId> <version>0.10.0</version> </dependency> 複製代碼
在util工具包下添加MarkdownUtils工具類:
package com.star.util; import org.commonmark.Extension; import org.commonmark.ext.gfm.tables.TableBlock; import org.commonmark.ext.gfm.tables.TablesExtension; import org.commonmark.ext.heading.anchor.HeadingAnchorExtension; import org.commonmark.node.Link; import org.commonmark.node.Node; import org.commonmark.parser.Parser; import org.commonmark.renderer.html.AttributeProvider; import org.commonmark.renderer.html.AttributeProviderContext; import org.commonmark.renderer.html.AttributeProviderFactory; import org.commonmark.renderer.html.HtmlRenderer; import java.util.*; /** * @Description: Markdown編輯器 * @Author: ONESTAR * @Date: Created in 13:24 2020/4/5 * @QQ羣: 530311074 * @URL: https://onestar.newstar.net.cn/ */ public class MarkdownUtils { /** * markdown格式轉換成HTML格式 * @param markdown * @return */ public static String markdownToHtml(String markdown) { Parser parser = Parser.builder().build(); Node document = parser.parse(markdown); HtmlRenderer renderer = HtmlRenderer.builder().build(); return renderer.render(document); } /** * 增長擴展[標題錨點,表格生成] * Markdown轉換成HTML * @param markdown * @return */ public static String markdownToHtmlExtensions(String markdown) { //h標題生成id Set<Extension> headingAnchorExtensions = Collections.singleton(HeadingAnchorExtension.create()); //轉換table的HTML List<Extension> tableExtension = Arrays.asList(TablesExtension.create()); Parser parser = Parser.builder() .extensions(tableExtension) .build(); Node document = parser.parse(markdown); HtmlRenderer renderer = HtmlRenderer.builder() .extensions(headingAnchorExtensions) .extensions(tableExtension) .attributeProviderFactory(new AttributeProviderFactory() { public AttributeProvider create(AttributeProviderContext context) { return new CustomAttributeProvider(); } }) .build(); return renderer.render(document); } /** * 處理標籤的屬性 */ static class CustomAttributeProvider implements AttributeProvider { @Override public void setAttributes(Node node, String tagName, Map<String, String> attributes) { //改變a標籤的target屬性爲_blank if (node instanceof Link) { attributes.put("target", "_blank"); } if (node instanceof TableBlock) { attributes.put("class", "ui celled table"); } } } public static void main(String[] args) { String table = "| hello | hi | 哈哈哈 |\n" + "| ----- | ---- | ----- |\n" + "| 斯維爾多 | 士大夫 | f啊 |\n" + "| 阿什頓發 | 非固定杆 | 撒阿什頓發 |\n" + "\n"; String a = "[ONESTAR](https://onestar.newstar.net.cn/)"; System.out.println(markdownToHtmlExtensions(a)); } } 複製代碼
文章內容顯示是從首頁點擊文章標題或圖片,而後跳轉到博客詳情頁面的,因此這裏就將代碼放在博客業務這一塊
在BlogDao接口中添加查詢博客詳情、文章訪問更新、查詢評論數量接口
//查詢博客詳情 DetailedBlog getDetailedBlog(Long id); //文章訪問更新 int updateViews(Long id); //根據博客id查詢出評論數量 int getCommentCountById(Long id); 複製代碼
根據持久層接口,編寫以下SQL:查詢博客詳情、文章訪問更新、查詢評論數量,這裏須要對博客詳情進行封裝
<resultMap id="detailedBlog" type="com.star.queryvo.DetailedBlog"> <id property="id" column="bid"/> <result property="firstPicture" column="first_picture"/> <result property="flag" column="flag"/> <result property="title" column="title"/> <result property="content" column="content"/> <result property="typeName" column="name"/> <result property="views" column="views"/> <result property="commentCount" column="comment_count"/> <result property="updateTime" column="update_time"/> <result property="commentabled" column="commentabled"/> <result property="shareStatement" column="share_statement"/> <result property="appreciation" column="appreciation"/> <result property="nickname" column="nickname"/> <result property="avatar" column="avatar"/> </resultMap> <!--博客詳情查詢--> <select id="getDetailedBlog" resultMap="detailedBlog"> select b.id bid,b.first_picture,b.flag,b.title,b.content,b.views,b.comment_count,b.update_time,b.commentabled,b.share_statement,b.appreciation, u.nickname,u.avatar,t.name from myblog.t_blog b,myblog.t_user u, myblog.t_type t where b.user_id = u.id and b.type_id = t.id and b.id = #{id} </select> <!--文章訪問自增--> <update id="updateViews" parameterType="com.star.entity.Blog"> update myblog.t_blog b set b.views = b.views+1 where b.id = #{id} </update> <!--查詢出文章評論數量並更新--> <update id="getCommentCountById" parameterType="com.star.entity.Blog"> update myblog.t_blog b set b.comment_count = ( select count(*) from myblog.t_comment c where c.blog_id = #{id} and b.id = #{id} ) WHERE b.id = #{id} </update> 複製代碼
在BlogService接口中添加查詢博客詳情方法:
//查詢博客詳情 DetailedBlog getDetailedBlog(Long id); 複製代碼
次接口實現主要是設置文章顯示格式,文章訪問自增和文章評論的統計,在BlogServiceImpl類中添加實現方法,以下:
@Override public DetailedBlog getDetailedBlog(Long id) { DetailedBlog detailedBlog = blogDao.getDetailedBlog(id); if (detailedBlog == null) { throw new NotFoundException("該博客不存在"); } String content = detailedBlog.getContent(); detailedBlog.setContent(MarkdownUtils.markdownToHtmlExtensions(content)); //文章訪問數量自增 blogDao.updateViews(id); //文章評論數量更新 blogDao.getCommentCountById(id); return detailedBlog; } 複製代碼
在IndexController類中添加方法,調用業務層接口:
//跳轉博客詳情頁面 @GetMapping("/blog/{id}") public String blog(@PathVariable Long id, Model model) { DetailedBlog detailedBlog = blogService.getDetailedBlog(id); model.addAttribute("blog", detailedBlog); return "blog"; } 複製代碼
因爲評論稍微複雜些,這裏將評論單獨放一個業務層
分析:
問:評論業務層須要哪些接口?
答:評論直接在前端頁面上進行操做,沒有後臺管理,只是區分的管理員和普通用戶,管理員能夠對評論進行刪除,所以須要查詢評論列表(listCommentByBlogId)、添加保存評論(saveComment)、刪除評論(deleteComment)接口
問:業務層這些接口夠了,但持久層光這些夠了嗎?須要哪些SQL,須要哪些持久層接口呢?
答:持久層接口確定是不夠的,主要是查詢評論列表的時候,須要將評論和回覆加以區分,根據評論功能來看,有父評論、子評論(回覆),而且父子評論在前端顯示的位置有不一樣,這裏細說一下查詢:
- 根據id爲「-1」和博客id查詢出全部父評論(父級評論id爲‘-1’)
- 根據父評論的id查詢出一級子回覆
- 根據子回覆的id循環迭代查詢出全部子集回覆
- 將查詢出來的子回覆放到一個集合中
因此查詢評論信息須要:查詢父級評論(findByBlogIdParentIdNull)、查詢一級回覆(findByBlogIdParentIdNotNull)、查詢二級回覆(findByBlogIdAndReplayId)
在dao包下建立CommentDao接口,添加以下接口:
package com.star.dao; import com.star.entity.Comment; import org.apache.ibatis.annotations.Mapper; import org.apache.ibatis.annotations.Param; import org.springframework.stereotype.Repository; import java.util.List; /** * @Description: 評論持久層接口 * @Date: Created in 9:21 2020/6/23 * @Author: ONESTAR * @QQ羣: 530311074 * @URL: https://onestar.newstar.net.cn/ */ @Mapper @Repository public interface CommentDao { //查詢父級評論 List<Comment> findByBlogIdParentIdNull(@Param("blogId") Long blogId, @Param("blogParentId") Long blogParentId); //查詢一級回覆 List<Comment> findByBlogIdParentIdNotNull(@Param("blogId") Long blogId, @Param("id") Long id); //查詢二級回覆 List<Comment> findByBlogIdAndReplayId(@Param("blogId") Long blogId,@Param("childId") Long childId); //添加一個評論 int saveComment(Comment comment); //刪除評論 void deleteComment(Long id); } 複製代碼
在mapper目錄下建立CommentDao.xml文件,添加以下:
<?xml version="1.0" encoding="utf-8" ?> <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" > <mapper namespace="com.star.dao.CommentDao"> <!--添加評論--> <insert id="saveComment" parameterType="com.star.entity.Comment"> insert into myblog.t_comment (nickname,email,content,avatar,create_time,blog_id,parent_comment_id,admin_comment) values (#{nickname},#{email},#{content},#{avatar},#{createTime},#{blogId},#{parentCommentId},#{adminComment}); </insert> <!--查詢父級評論--> <select id="findByBlogIdParentIdNull" resultType="com.star.entity.Comment"> select * from myblog.t_comment c where c.blog_id = #{blogId} and c.parent_comment_id = #{blogParentId} order by c.create_time desc </select> <!--查詢一級子評論--> <select id="findByBlogIdParentIdNotNull" resultType="com.star.entity.Comment"> select * from myblog.t_comment c where c.blog_id = #{blogId} and c.parent_comment_id = #{id} order by c.create_time desc </select> <!--查詢二級子評論--> <select id="findByBlogIdAndReplayId" resultType="com.star.entity.Comment"> select * from myblog.t_comment c where c.blog_id = #{blogId} and c.parent_comment_id = #{childId} order by c.create_time desc </select> <!--刪除評論--> <delete id="deleteComment" > delete from myblog.t_comment where id = #{id} </delete> </mapper> 複製代碼
講解:
添加刪除:直接使用insert和delete便可進行添加和刪除
查詢:
- findByBlogIdParentIdNull:根據id爲「-1」和博客id查詢出全部父評論(父級評論id爲‘-1’)
- findByBlogIdParentIdNotNull:根據父評論的id查詢出一級子回覆
- findByBlogIdAndReplayId:根據子回覆的id循環迭代查詢出全部子集回覆
在service包下建立CommentService接口,以下:
package com.star.service; import com.star.entity.Comment; import java.util.List; /** * @Description: 評論業務層接口 * @Date: Created in 10:22 2020/6/23 * @Author: ONESTAR * @QQ羣: 530311074 * @URL: https://onestar.newstar.net.cn/ */ public interface CommentService { //根據博客id查詢評論信息 List<Comment> listCommentByBlogId(Long blogId); //添加保存評論 int saveComment(Comment comment); //刪除評論 void deleteComment(Comment comment,Long id); } 複製代碼
在Impl包下建立接口實現類:CommentServiceImpl,功能都在這個接口中實現
package com.star.service.Impl; import com.star.dao.BlogDao; import com.star.dao.CommentDao; import com.star.entity.Comment; import com.star.service.CommentService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import java.util.ArrayList; import java.util.Date; import java.util.List; /** * @Description: 博客評論業務層接口實現類 * @Date: Created in 10:23 2020/6/23 * @Author: ONESTAR * @QQ羣: 530311074 * @URL: https://onestar.newstar.net.cn/ */ @Service public class CommentServiceImpl implements CommentService { @Autowired private CommentDao commentDao; @Autowired private BlogDao blogDao; //存放迭代找出的全部子代的集合 private List<Comment> tempReplys = new ArrayList<>(); /** * @Description: 查詢評論 * @Auther: ONESTAR * @Date: 10:42 2020/6/23 * @Param: blogId:博客id * @Return: 評論消息 */ @Override public List<Comment> listCommentByBlogId(Long blogId) { //查詢出父節點 List<Comment> comments = commentDao.findByBlogIdParentIdNull(blogId, Long.parseLong("-1")); for(Comment comment : comments){ Long id = comment.getId(); String parentNickname1 = comment.getNickname(); List<Comment> childComments = commentDao.findByBlogIdParentIdNotNull(blogId,id); //查詢出子評論 combineChildren(blogId, childComments, parentNickname1); comment.setReplyComments(tempReplys); tempReplys = new ArrayList<>(); } return comments; } /** * @Description: 查詢出子評論 * @Auther: ONESTAR * @Date: 10:43 2020/6/23 * @Param: childComments:全部子評論 * @Param: parentNickname1:父評論姓名 * @Return: */ private void combineChildren(Long blogId, List<Comment> childComments, String parentNickname1) { //判斷是否有一級子評論 if(childComments.size() > 0){ //循環找出子評論的id for(Comment childComment : childComments){ String parentNickname = childComment.getNickname(); childComment.setParentNickname(parentNickname1); tempReplys.add(childComment); Long childId = childComment.getId(); //查詢出子二級評論 recursively(blogId, childId, parentNickname); } } } /** * @Description: 循環迭代找出子集回覆 * @Auther: ONESTAR * @Date: 10:44 2020/6/23 * @Param: chileId:子評論id * @Param: parentNickname1:子評論姓名 * @Return: */ private void recursively(Long blogId, Long childId, String parentNickname1) { //根據子一級評論的id找到子二級評論 List<Comment> replayComments = commentDao.findByBlogIdAndReplayId(blogId,childId); if(replayComments.size() > 0){ for(Comment replayComment : replayComments){ String parentNickname = replayComment.getNickname(); replayComment.setParentNickname(parentNickname1); Long replayId = replayComment.getId(); tempReplys.add(replayComment); recursively(blogId,replayId,parentNickname); } } } //新增評論 @Override public int saveComment(Comment comment) { comment.setCreateTime(new Date()); int comments = commentDao.saveComment(comment); //文章評論計數 blogDao.getCommentCountById(comment.getBlogId()); return comments; } //刪除評論 @Override public void deleteComment(Comment comment,Long id) { commentDao.deleteComment(id); blogDao.getCommentCountById(comment.getBlogId()); } } 複製代碼
comment.avatar: /images/avatar.png message.avatar: /images/avatar.png 複製代碼
在controller包下建立CommentController類,以下:
package com.star.controller; import com.star.entity.Comment; import com.star.entity.User; import com.star.queryvo.DetailedBlog; import com.star.service.BlogService; import com.star.service.CommentService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Controller; import org.springframework.ui.Model; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.servlet.mvc.support.RedirectAttributes; import javax.servlet.http.HttpSession; import java.util.List; /** * @Description: 評論控制器 * @Date: Created in 10:25 2020/6/23 * @Author: ONESTAR * @QQ羣: 530311074 * @URL: https://onestar.newstar.net.cn/ */ @Controller public class CommentController { @Autowired private CommentService commentService; @Autowired private BlogService blogService; @Value("${comment.avatar}") private String avatar; //查詢評論列表 @GetMapping("/comments/{blogId}") public String comments(@PathVariable Long blogId, Model model) { List<Comment> comments = commentService.listCommentByBlogId(blogId); model.addAttribute("comments", comments); return "blog :: commentList"; } //新增評論 @PostMapping("/comments") public String post(Comment comment, HttpSession session, Model model) { Long blogId = comment.getBlogId(); User user = (User) session.getAttribute("user"); if (user != null) { comment.setAvatar(user.getAvatar()); comment.setAdminComment(true); } else { //設置頭像 comment.setAvatar(avatar); } if (comment.getParentComment().getId() != null) { comment.setParentCommentId(comment.getParentComment().getId()); } commentService.saveComment(comment); List<Comment> comments = commentService.listCommentByBlogId(blogId); model.addAttribute("comments", comments); return "blog :: commentList"; } //刪除評論 @GetMapping("/comment/{blogId}/{id}/delete") public String delete(@PathVariable Long blogId, @PathVariable Long id, Comment comment, RedirectAttributes attributes, Model model){ commentService.deleteComment(comment,id); DetailedBlog detailedBlog = blogService.getDetailedBlog(blogId); List<Comment> comments = commentService.listCommentByBlogId(blogId); model.addAttribute("blog", detailedBlog); model.addAttribute("comments", comments); return "blog"; } } 複製代碼
講解:
查詢評論列表:調用接口查詢評論信息列表,局部刷新評論信息
新增評論:對評論進行判斷,區分遊客和管理員
刪除評論:將博客id和評論id參數傳入,判斷刪除的是哪一條評論,這裏沒有作迭代刪除子評論,若刪除了含有回覆的評論,根據以前的查詢來看,在前端回覆也不會查詢出來,但回覆並無刪除,依然在數據庫裏面,刪除的只是父評論
給出部分前端代碼,僅供參考,瞭解更多能夠查看整個項目源碼:github.com/oneStarLR/m…
<input type="hidden" name="blogId" th:value="${blog.id}"> <input type="hidden" name="parentComment.id" value="-1"> <div class="field"> <textarea name="content" placeholder="請輸入評論信息..."></textarea> </div> <div class="fields"> <div class="field m-mobile-wide m-margin-bottom-small"> <div class="ui left icon input"> <i class="user icon"></i> <input type="text" name="nickname" placeholder="姓名" th:value="${session.user}!=null ? ${session.user.nickname}"> </div> </div> <div class="field m-mobile-wide m-margin-bottom-small"> <div class="ui left icon input"> <i class="mail icon"></i> <input type="text" name="email" placeholder="郵箱" th:value="${session.user}!=null ? ${session.user.email}"> </div> </div> <div class="field m-margin-bottom-small m-mobile-wide"> <button id="commentpost-btn" type="button" class="ui teal button m-mobile-wide"><i class="edit icon"></i>發佈</button> </div> </div> 複製代碼
$('#commentpost-btn').click(function () { var boo = $('.ui.form').form('validate form'); if (boo) { console.log('校驗成功'); postData(); } else { console.log('校驗失敗'); } }); function postData() { $("#comment-container").load(/*[[@{/comments}]]*/"",{ "parentComment.id" : $("[name='parentComment.id']").val(), "blogId" : $("[name='blogId']").val(), "nickname": $("[name='nickname']").val(), "email" : $("[name='email']").val(), "content" : $("[name='content']").val() },function (responseTxt, statusTxt, xhr) { $(window).scrollTo($('#goto'),500); clearContent(); }); } 複製代碼
<div id="comment-container" class="ui teal segment"> <div th:fragment="commentList"> <div class="ui threaded comments" style="max-width: 100%;"> <h3 class="ui dividing header">評論</h3> <div class="comment" th:each="comment : ${comments}"> <a class="avatar"> <img src="https://unsplash.it/100/100?image=1005" th:src="@{${comment.avatar}}"> </a> <div class="content"> <a class="author" > <span th:text="${comment.nickname}">Matt</span> <div class="ui mini basic teal left pointing label m-padded-mini" th:if="${comment.adminComment}">棧主</div> </a> <div class="metadata"> <span class="date" th:text="${#dates.format(comment.createTime,'yyyy-MM-dd HH:mm')}">Today at 5:42PM</span> </div> <div class="text" th:text="${comment.content}"> How artistic! </div> <div class="actions"> <a class="reply" data-commentid="1" data-commentnickname="Matt" th:attr="data-commentid=${comment.id},data-commentnickname=${comment.nickname}" onclick="reply(this)">回覆</a> <a class="delete" href="#" th:href="@{/comment/{param1}/{param2}/delete(param1=${comment.blogId},param2=${comment.id})}" onclick="return confirm('肯定要刪除該評論嗎?三思啊! 刪了可就沒了!')" th:if="${session.user}">刪除</a> <!--<a class="delete" href="#" th:href="@{/comment/{id}/delete(id=${comment.id})}" onclick="return confirm('肯定要刪除該評論嗎?三思啊! 刪了可就沒了!')" th:if="${session.user}">刪除</a>--> </div> </div> <!--子集評論--> <div class="comments" th:if="${#arrays.length(comment.replyComments)}>0"> <div class="comment" th:each="reply : ${comment.replyComments}"> <a class="avatar"> <img src="https://unsplash.it/100/100?image=1005" th:src="@{${reply.avatar}}"> </a> <div class="content"> <a class="author" > <span th:text="${reply.nickname}">小紅</span> <div class="ui mini basic teal left pointing label m-padded-mini" th:if="${reply.adminComment}">棧主</div> <span th:text="|@ ${reply.parentNickname}|" class="m-teal">@ 小白</span> </a> <div class="metadata"> <span class="date" th:text="${#dates.format(reply.createTime,'yyyy-MM-dd HH:mm')}">Today at 5:42PM</span> </div> <div class="text" th:text="${reply.content}"> How artistic! </div> <div class="actions"> <a class="reply" data-commentid="1" data-commentnickname="Matt" th:attr="data-commentid=${reply.id},data-commentnickname=${reply.nickname}" onclick="reply(this)">回覆</a> <a class="delete" href="#" th:href="@{/comment/{param1}/{param2}/delete(param1=${reply.blogId},param2=${reply.id})}" onclick="return confirm('肯定要刪除該評論嗎?三思啊! 刪了可就沒了!')" th:if="${session.user}">刪除</a> <!--<a class="delete" href="#" th:href="@{/comment/{id}/delete(id=${reply.id})}" onclick="return confirm('肯定要刪除該評論嗎?三思啊! 刪了可就沒了!')" th:if="${session.user}">刪除</a>--> </div> </div> </div> </div> </div> </div> </div> </div> 複製代碼
運行項目,訪問 http://localhost:8080/, 點擊一片文章,能夠查看文章信息,並能進行評論,登陸後能夠對評論進行刪除
至此,Springboot搭建我的博客詳情頁面顯示開發完成,因爲留言功能和評論功能基本同樣,以前也寫過一篇有關評論的博客(SpringBoot和Mybatis實現評論樓中樓功能(一張表搞定)),因此這裏就不講留言功能的開發了,還有不懂的夥伴能夠問我,也能夠加羣討論,下一篇就直接講述分類、時間軸、音樂盒、友人賬、照片牆關於我頁面的顯示,比較簡單,直接在一篇文章中講完
【點關注,不迷路,歡迎持續關注本站】