邮件回复和多级评论

master
barney 2 years ago
parent 7e17d02726
commit a8bb79c3e8
  1. 6
      pom.xml
  2. 21
      src/main/java/cc/bnblogs/common/DefaultImages.java
  3. 54
      src/main/java/cc/bnblogs/common/MailHelper.java
  4. 1
      src/main/java/cc/bnblogs/common/PageHelper.java
  5. 59
      src/main/java/cc/bnblogs/controller/IndexController.java
  6. 5
      src/main/java/cc/bnblogs/enums/ResultEnum.java
  7. 20
      src/main/java/cc/bnblogs/mapper/CommentMapper.java
  8. 4
      src/main/java/cc/bnblogs/pojo/Comment.java
  9. 7
      src/main/java/cc/bnblogs/service/ArticleService.java
  10. 113
      src/main/java/cc/bnblogs/service/CommentService.java
  11. 31
      src/main/resources/application.yml
  12. BIN
      src/main/resources/static/image/avatar/1.jpg
  13. BIN
      src/main/resources/static/image/avatar/10.jpg
  14. BIN
      src/main/resources/static/image/avatar/11.jpg
  15. BIN
      src/main/resources/static/image/avatar/12.jpg
  16. BIN
      src/main/resources/static/image/avatar/13.jpg
  17. BIN
      src/main/resources/static/image/avatar/14.jpg
  18. BIN
      src/main/resources/static/image/avatar/2.jpg
  19. BIN
      src/main/resources/static/image/avatar/3.jpg
  20. BIN
      src/main/resources/static/image/avatar/4.jpg
  21. BIN
      src/main/resources/static/image/avatar/5.jpg
  22. BIN
      src/main/resources/static/image/avatar/6.jpg
  23. BIN
      src/main/resources/static/image/avatar/7.jpg
  24. BIN
      src/main/resources/static/image/avatar/8.jpg
  25. BIN
      src/main/resources/static/image/avatar/9.jpg
  26. 4
      src/main/resources/templates/common.html
  27. 209
      src/main/resources/templates/detail.html
  28. 25
      src/test/java/cc/bnblogs/test/AvatarTest.java
  29. 25
      src/test/java/cc/bnblogs/test/MailTest.java

@ -81,6 +81,12 @@
<artifactId>commonmark-ext-gfm-tables</artifactId> <artifactId>commonmark-ext-gfm-tables</artifactId>
<version>${commonmark.version}</version> <version>${commonmark.version}</version>
</dependency> </dependency>
<!-- springboot 邮件mail -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-mail</artifactId>
</dependency>
</dependencies> </dependencies>
<build> <build>

@ -6,6 +6,8 @@ import org.springframework.stereotype.Component;
import org.thymeleaf.util.StringUtils; import org.thymeleaf.util.StringUtils;
import java.util.List; import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/** /**
* @author zfp@bnblogs.cc * @author zfp@bnblogs.cc
@ -15,13 +17,30 @@ import java.util.List;
@Data @Data
@ConfigurationProperties(prefix = "default-images") @ConfigurationProperties(prefix = "default-images")
public class DefaultImages { public class DefaultImages {
/**
* 默认文章封面
*/
private List<String> images; private List<String> images;
/**
* 默认评论头像
*/
private List<String> avatars;
public String cover(String imgUrl) { public String cover(String imgUrl) {
if (StringUtils.isEmptyOrWhitespace(imgUrl)) { if (StringUtils.isEmptyOrWhitespace(imgUrl)) {
// 返回一张随机图 // 返回一张随机图作为封面
return images.get((int)(Math.random() * images.size())); return images.get((int)(Math.random() * images.size()));
} }
return imgUrl; return imgUrl;
} }
public String avatar(String mail) {
Pattern pattern = Pattern.compile("(\\d{5,10})@qq.com");
Matcher matcher = pattern.matcher(mail);
if (matcher.find()) {
String qq = matcher.group(1);
return String.format("https://q1.qlogo.cn/g?b=qq&nk=%s&s=100", qq);
}
return avatars.get((int) (Math.random() * avatars.size()));
}
} }

@ -0,0 +1,54 @@
package cc.bnblogs.common;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.mail.javamail.JavaMailSender;
import org.springframework.mail.javamail.MimeMessageHelper;
import org.springframework.stereotype.Component;
import javax.mail.MessagingException;
import javax.mail.internet.MimeMessage;
import java.util.Date;
/**
* @author zfp@bnblogs.cc
* @createTime: 2022/10/22
*/
@Slf4j
@Component
public class MailHelper {
@Value("${spring.mail.username}")
private String from;
private final JavaMailSender javaMailSender;
public MailHelper(JavaMailSender javaMailSender) {
this.javaMailSender = javaMailSender;
}
/**
* 发送邮件
* @param toSend 收件人(目标邮箱)
* @param subject 主题
* @param text 内容
*/
public void sendMail(String toSend, String subject, String text) {
MimeMessage message = javaMailSender.createMimeMessage();
MimeMessageHelper helper = new MimeMessageHelper(message);
try {
//设置发件时间
helper.setSentDate(new Date());
// 发件人(配置文件中的邮箱)
helper.setFrom(from);
//设置收件人
helper.setTo(toSend);
//设置标签
helper.setSubject(subject);
//设置内容
helper.setText(text, true);
//发邮件
javaMailSender.send(message);
} catch (MessagingException e) {
log.error(e.getMessage(), e);
}
}
}

@ -15,6 +15,7 @@ import java.util.List;
@Builder @Builder
@AllArgsConstructor @AllArgsConstructor
@NoArgsConstructor @NoArgsConstructor
// T: 返回的数据类型
public class PageHelper<T> { public class PageHelper<T> {
// 数据列表 // 数据列表
private List<T> rows; private List<T> rows;

@ -1,21 +1,18 @@
package cc.bnblogs.controller; package cc.bnblogs.controller;
import cc.bnblogs.common.ArticleSearch; import cc.bnblogs.common.*;
import cc.bnblogs.common.LRUCache;
import cc.bnblogs.common.PageHelper;
import cc.bnblogs.config.WebConfig; import cc.bnblogs.config.WebConfig;
import cc.bnblogs.enums.ResultEnum;
import cc.bnblogs.pojo.Article; import cc.bnblogs.pojo.Article;
import cc.bnblogs.pojo.Category; import cc.bnblogs.pojo.Category;
import cc.bnblogs.pojo.Comment;
import cc.bnblogs.pojo.Tag; import cc.bnblogs.pojo.Tag;
import cc.bnblogs.service.*; import cc.bnblogs.service.*;
import cc.bnblogs.utils.CookieUtil; import cc.bnblogs.utils.CookieUtil;
import org.springframework.http.HttpRequest;
import org.springframework.stereotype.Controller; import org.springframework.stereotype.Controller;
import org.springframework.ui.Model; import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.*;
import org.springframework.web.bind.annotation.ModelAttribute; import org.thymeleaf.util.StringUtils;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestParam;
import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpServletResponse;
@ -40,11 +37,15 @@ public class IndexController {
private final LRUCache lruCache; private final LRUCache lruCache;
private final CommentService commentService;
private final WebSite webSite;
public IndexController(FriendsService friendsService, NavigationService navigationService, public IndexController(FriendsService friendsService, NavigationService navigationService,
TagService tagService, ArticleService articleService, BannerService bannerService, TagService tagService, ArticleService articleService, BannerService bannerService,
CategoryService categoryService, LRUCache lruCache) { CategoryService categoryService, LRUCache lruCache, CommentService commentService, WebSite webSite) {
this.friendsService = friendsService; this.friendsService = friendsService;
this.navigationService = navigationService; this.navigationService = navigationService;
this.tagService = tagService; this.tagService = tagService;
@ -52,6 +53,8 @@ public class IndexController {
this.bannerService = bannerService; this.bannerService = bannerService;
this.categoryService = categoryService; this.categoryService = categoryService;
this.lruCache = lruCache; this.lruCache = lruCache;
this.commentService = commentService;
this.webSite = webSite;
} }
@ -192,6 +195,44 @@ public class IndexController {
} }
// todo 文章内容不存在时应该抛出异常 // todo 文章内容不存在时应该抛出异常
model.addAttribute("article",article); model.addAttribute("article",article);
PageHelper<Comment> commentPage = commentService.show(id,1);
// 返回第一页评论
model.addAttribute("commentPage",commentPage);
return "detail"; return "detail";
} }
/**
* 提交评论
* @param comment 文章评论
* @return 操作结果
*/
@PostMapping("/comment")
@ResponseBody
public Result<String> comment(Comment comment) {
// 邮箱是否和管理员相同
if (StringUtils.equals(comment.getEmail(),webSite.getMail())) {
return Result.error(ResultEnum.RESULT_MAIL_FAILED);
}
// 昵称不能和管理员重名
if (StringUtils.equals(comment.getNickname(),webSite.getNickname())) {
return Result.error(ResultEnum.RESULT_COMMENT_NAME_FAILED);
}
commentService.save(comment);
return Result.success();
}
/**
* 评论局部刷新
* @param id 评论id
* @param pageNumber 页号
* @param model MVCmodel
* @return
*/
@GetMapping("/comment/{id}")
public String comments(@PathVariable Integer id, @RequestParam(required = false, defaultValue = "1") Integer pageNumber, Model model) {
pageNumber = Math.max(1, pageNumber);
PageHelper<Comment> commentPage = commentService.show(id, pageNumber);
model.addAttribute("commentPage", commentPage);
return "detail::comments";
}
} }

@ -15,14 +15,17 @@ public enum ResultEnum {
* 操作结果的枚举常量 * 操作结果的枚举常量
*/ */
RESULT_SUCCESS(200,"请求成功"), RESULT_SUCCESS(200,"请求成功"),
RESULT_NOT_FOUND(404,"数据不存在"), RESULT_NOT_FOUND(404,"数据不存在"),
RESULT_UPLOAD_FAIL(501,"文件上传出错"), RESULT_UPLOAD_FAIL(501,"文件上传出错"),
RESULT_MAIL_FAILED(503,"不能用管理员邮箱进行评论"),
RESULT_COMMENT_NAME_FAILED(504,"不能与管理员重名"),
RESULT_ERROR(500,"请求错误"); RESULT_ERROR(500,"请求错误");
private final Integer code; private final Integer code;
private final String message; private final String message;
} }

@ -2,8 +2,12 @@ package cc.bnblogs.mapper;
import cc.bnblogs.pojo.Article; import cc.bnblogs.pojo.Article;
import cc.bnblogs.pojo.Comment; import cc.bnblogs.pojo.Comment;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.data.jpa.repository.JpaRepository;
import java.util.List;
/** /**
* @author zfp@bnblogs.cc * @author zfp@bnblogs.cc
* @createTime: 2022/10/17 * @createTime: 2022/10/17
@ -15,4 +19,20 @@ public interface CommentMapper extends JpaRepository<Comment,Integer> {
* @return 评论个数 * @return 评论个数
*/ */
long countByArticle(Article article); long countByArticle(Article article);
/**
* 按照文章创建时间降序来分页
* @param article 文章
* @param pid 父评论的id
* @param pageable 分页
* @return 该页的评论
*/
Page<Comment> findAllByArticleAndPidOrderByCreatedDesc(Article article, Integer pid,Pageable pageable);
/**
* 根据父id获取所有子评论
* @param pid 评论的父id
* @return 该父评论下的所有评论
*/
List<Comment> findAllByPidOrderByCreatedDesc(Integer pid);
} }

@ -8,6 +8,7 @@ import lombok.NoArgsConstructor;
import javax.persistence.*; import javax.persistence.*;
import java.io.Serializable; import java.io.Serializable;
import java.util.Date; import java.util.Date;
import java.util.List;
/** /**
* @author zfp@bnblogs.cc * @author zfp@bnblogs.cc
@ -43,5 +44,8 @@ public class Comment implements Serializable {
// 评论和文章是多对一关系 // 评论和文章是多对一关系
@ManyToOne @ManyToOne
private Article article; private Article article;
// 父评论的评论列表
@Transient
private List<Comment> children;
} }

@ -71,7 +71,12 @@ public class ArticleService {
* @return id对应的文章对象 * @return id对应的文章对象
*/ */
public Article detail(Integer id) { public Article detail(Integer id) {
return articleMapper.findById(id).orElse(null); Article article = articleMapper.findById(id).orElse(null);
if (Objects.isNull(article)) {
return null;
}
article.setCommentCount(commentMapper.countByArticle(article));
return article;
} }
/** /**

@ -1,10 +1,22 @@
package cc.bnblogs.service; package cc.bnblogs.service;
import cc.bnblogs.common.MailHelper;
import cc.bnblogs.common.PageHelper;
import cc.bnblogs.common.WebSite;
import cc.bnblogs.mapper.ArticleMapper;
import cc.bnblogs.mapper.CommentMapper; import cc.bnblogs.mapper.CommentMapper;
import cc.bnblogs.pojo.Article;
import cc.bnblogs.pojo.Comment; import cc.bnblogs.pojo.Comment;
import cc.bnblogs.utils.UpdateUtil;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import java.util.Date;
import java.util.List; import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;
/** /**
* @author zfp@bnblogs.cc * @author zfp@bnblogs.cc
@ -15,12 +27,20 @@ public class CommentService {
private final CommentMapper commentMapper; private final CommentMapper commentMapper;
public CommentService(CommentMapper commentMapper) { private final MailHelper mailHelper;
private final WebSite webSite;
private final ArticleMapper articleMapper;
public CommentService(CommentMapper commentMapper, MailHelper mailHelper, WebSite webSite, ArticleMapper articleMapper) {
this.commentMapper = commentMapper; this.commentMapper = commentMapper;
this.mailHelper = mailHelper;
this.webSite = webSite;
this.articleMapper = articleMapper;
} }
/** /**
* 获取所有评论 * 获取所有评论
*
* @return 评论列表 * @return 评论列表
*/ */
public List<Comment> list() { public List<Comment> list() {
@ -29,6 +49,7 @@ public class CommentService {
/** /**
* 获取评论总数 * 获取评论总数
*
* @return 评论总数 * @return 评论总数
*/ */
public long count() { public long count() {
@ -37,6 +58,7 @@ public class CommentService {
/** /**
* 根据id查询评论对象 * 根据id查询评论对象
*
* @param id 评论id * @param id 评论id
* @return id对应的评论对象 * @return id对应的评论对象
*/ */
@ -46,17 +68,106 @@ public class CommentService {
/** /**
* 保存评论 * 保存评论
*
* @param comment 待保存的评论对象 * @param comment 待保存的评论对象
*/ */
public void save(Comment comment) { public void save(Comment comment) {
// 如果是新评论
if (Objects.isNull(comment.getId())) {
// 有人在你的网站评论了
sendMailToWebsite(comment.getArticle().getId());
comment.setCreated(new Date());
comment.setView(false);
if (comment.getPid() != 0) {
// 有人回复了你的评论
sendMailToComment(comment.getArticle().getId(), comment.getPid(), comment.getContent());
// 展示它的父评论
Comment parent = detail(comment.getPid());
comment.setContent("@" + parent.getNickname() + ": " + comment.getContent());
comment.setPid(findCommentPid(comment.getPid()));
}
commentMapper.save(comment); commentMapper.save(comment);
} else {
Comment one = detail(comment.getId());
UpdateUtil.copyNullProperties(comment, one);
commentMapper.save(one);
}
}
/**
* 网站有人留言了发送一封邮件给站长
*
* @param id 文章id
*/
@Async
public void sendMailToWebsite(Integer id) {
Article article = articleMapper.findById(id).orElse(null);
if (Objects.isNull(article)) {
return;
}
String content = "<p>\n" + " 你的文章 <a href=\"" + webSite.getDomain() + "/" + id + ".html\">" + article.getTitle()
+ "</a>收到了新评论," +
"<a href=\"" + webSite.getDomain() + "/" + id + ".html\">" + "点此</a>" + "查看\n" + "</p>\n" +
"<p style=\"text-align:right ;\">\n" + "时间:\n" + new Date() + "</p>";
mailHelper.sendMail(webSite.getMail(), webSite.getTitle() + "收到新评论", content);
}
/**
* 异步方法: 有人回复了你的评论需要给你发送一封邮件
*
* @param id 文章id
* @param cid 被评论的评论id
* @param reply 回复内容
*/
@Async
public void sendMailToComment(Integer id, Integer cid, String reply) {
Article article = articleMapper.findById(id).orElse(null);
if (Objects.isNull(article)) {
return;
}
Comment comment = commentMapper.findById(cid).orElse(null);
if (Objects.isNull(comment)) {
return;
}
String content = "<p>\n" + " 你在<a href=\"" + webSite.getDomain() + "\">" + webSite.getTitle() + "</a>对 <a href=\"" + webSite.getDomain() + "/" + article.getId() + ".html\">" + article.getTitle() + "</a>文章的评论收到了新回复,回复内容如下:\n" + "</p>\n" + "<p>\n" + reply + "</p>\n" + "<p style=\"text-align:right ;\">\n" + " 一一发件人:" + webSite.getTitle() + "</p>\n" + "<p style=\"text-align:right ;\">\n" + "时间:" + new Date() + "</p>\n" + "<p style=\"text-align:right ;\">\n" + " 此邮件是由" + webSite.getTitle() + "自动发送,请勿回复 \n" + " </p>";
mailHelper.sendMail(comment.getEmail(), webSite.getTitle() + "收到新回复", content);
}
/**
* 递归查找直到找到最原始评论
*
* @param id 评论id
* @return 祖先评论的id
*/
private Integer findCommentPid(Integer id) {
Comment comment = detail(id);
// 直到找到某个评论的pid为0,因为他就是最原始的评论
if (comment.getPid() == 0) {
return comment.getId();
}
return findCommentPid(comment.getPid());
} }
/** /**
* 根据id删除评论对象 * 根据id删除评论对象
*
* @param id 待删除评论的id * @param id 待删除评论的id
*/ */
public void delete(Integer id) { public void delete(Integer id) {
commentMapper.deleteById(id); commentMapper.deleteById(id);
} }
/**
* 返回文章的评论
*
* @param id 文章id
* @param pageNumber 初始页号
* @return 评论分页
*/
public PageHelper<Comment> show(Integer id, int pageNumber) {
// 每页显示5个评论
Page<Comment> page = commentMapper.findAllByArticleAndPidOrderByCreatedDesc(Article.builder().id(id).build(), 0, PageRequest.of(pageNumber - 1, 5));
// 评论内容-当前页数-评论总数-总页数
return PageHelper.<Comment>builder().rows(page.getContent().stream().peek(x -> x.setChildren(commentMapper.findAllByPidOrderByCreatedDesc(x.getId()))).collect(Collectors.toList())).currentPage(pageNumber).total(page.getTotalElements()).totalPages(page.getTotalPages()).build();
}
} }

@ -34,6 +34,21 @@ spring:
type: ehcache type: ehcache
ehcache: ehcache:
config: classpath:/ehcache-spring.xml config: classpath:/ehcache-spring.xml
mail:
# 默认的邮件编码为UTF-8
default-encoding: UTF-8
# 邮箱服务器
host: smtp.qq.com
# 邮箱
username: 1337425156@qq.com
# 密码
password: yidyoatxwswwjgjj
# 端口
port: 587
# 其它属性,这里只开启debug输出错误信息
properties:
debug: true
logging: logging:
file: file:
@ -81,3 +96,19 @@ default-images:
- /static/image/3.jpg - /static/image/3.jpg
- /static/image/4.jpg - /static/image/4.jpg
- /static/image/5.jpg - /static/image/5.jpg
avatars:
- /static/image/avatar/1.jpg
- /static/image/avatar/2.jpg
- /static/image/avatar/3.jpg
- /static/image/avatar/4.jpg
- /static/image/avatar/5.jpg
- /static/image/avatar/6.jpg
- /static/image/avatar/7.jpg
- /static/image/avatar/8.jpg
- /static/image/avatar/9.jpg
- /static/image/avatar/10.jpg
- /static/image/avatar/11.jpg
- /static/image/avatar/12.jpg
- /static/image/avatar/13.jpg
- /static/image/avatar/14.jpg

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 24 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 24 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 21 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 43 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 26 KiB

@ -28,7 +28,7 @@
> >
</a> </a>
</div> </div>
<span th:text="${@webSite.getDescription()}" style="font-size: 30px"></span> <span th:text="${@webSite.getNavDesc()}" style="font-size: 20px;text-shadow:none;color: rgb(22, 18, 9)"></span>
<div class="lw-fr lw-linkme"> <div class="lw-fr lw-linkme">
<a target="_blank" <a target="_blank"
th:href="${T(java.lang.String).format('tencent://message/?uin=%s&amp;Site=&amp;menu=yes',@webSite.qq)}"><i th:href="${T(java.lang.String).format('tencent://message/?uin=%s&amp;Site=&amp;menu=yes',@webSite.qq)}"><i
@ -57,7 +57,7 @@
<div class="lw-md-hidden lw-phone-header"> <div class="lw-md-hidden lw-phone-header">
<a href="javascript:void(0)" class="lw-posa lw-phone-topbtn lw-search-show lw-search-btn"><i <a href="javascript:void(0)" class="lw-posa lw-phone-topbtn lw-search-show lw-search-btn"><i
class="fa fa-search"></i></a> class="fa fa-search"></i></a>
<a class="lw-index" href="/"><h1>冷文学习者</h1></a> <a class="lw-index" href="/"><h1 th:text="${@webSite.title}"></h1></a>
</div> </div>
<div class="lw-md-hidden" style="height: 100px;"></div> <div class="lw-md-hidden" style="height: 100px;"></div>
</th:block> </th:block>

@ -66,13 +66,15 @@
<div class="lw-article-list"> <div class="lw-article-list">
<ol class="breadcrumb lw-crumb"> <ol class="breadcrumb lw-crumb">
<li><a href="/">首页</a></li> <li><a href="/">首页</a></li>
<li th:if="${article.type eq T(cc.bnblogs.pojo.Article).TYPE_ARTICLE}"><a th:href="@{/category/{id}.html(id=${article.category.id})}" <li th:if="${article.type eq T(cc.bnblogs.pojo.Article).TYPE_ARTICLE}"><a
th:href="@{/category/{id}.html(id=${article.category.id})}"
th:text="${article.category.name}" th:text="${article.category.name}"
></a></li> ></a></li>
<li class="active" th:if="${article.type eq T(cc.bnblogs.pojo.Article).TYPE_ARTICLE}">正文</li> <li class="active" th:if="${article.type eq T(cc.bnblogs.pojo.Article).TYPE_ARTICLE}">正文</li>
<li class="active" th:if="${article.type eq T(cc.bnblogs.pojo.Article).TYPE_PAGE}" th:text="${article.title}"></li> <li class="active" th:if="${article.type eq T(cc.bnblogs.pojo.Article).TYPE_PAGE}"
th:text="${article.title}"></li>
</ol> </ol>
<div class="lw-article"> <div class="lw-article" id="lw-article-box">
<div class="lw-article-title"> <div class="lw-article-title">
<h1 th:text="${article.title}"></h1> <h1 th:text="${article.title}"></h1>
<p> <p>
@ -136,102 +138,96 @@
</li> </li>
</ul> </ul>
</div> </div>
<div style="margin-top: 20px"></div>
<h2 id="comment-number" style="text-align: center;margin-bottom: 10px">评论 (
<th:block th:text="${article.getCommentCount() > 0 ? article.getCommentCount() : 0}"></th:block>
)
</h2>
<div class="lw-comment-box row lw-m0"> <div class="lw-comment-box row lw-m0">
<h2>评论(6)</h2>
<form action=""> <form id="lw-comment-form">
<input type="hidden" id="article-id" name="article.id" th:value="${article.id}">
<input type="hidden" name="pid" value="0">
<div class="col-md-12 lw-p0" style="padding-right: 10px;"> <div class="col-md-12 lw-p0" style="padding-right: 10px;">
<textarea placeholder="请输入你的评论..."></textarea> <textarea name="content" required placeholder="请输入你的评论..."></textarea>
</div> </div>
<div class="col-md-4 col-xs-12 lw-comment-input lw-posr"> <div class="col-md-4 col-xs-12 lw-comment-input lw-posr">
<input type="text" placeholder="请输入昵称..."> <input type="text" name="nickname" required placeholder="请输入昵称...">
<i class="fa fa-user lw-comment-input lw-posa"></i> <i class="fa fa-user lw-comment-input lw-posa"></i>
</div> </div>
<div class="col-md-4 col-xs-12 lw-comment-input lw-posr"> <div class="col-md-4 col-xs-12 lw-comment-input lw-posr">
<input type="email" placeholder="请输入电子邮件..."> <input type="email" name="email" required placeholder="请输入电子邮件...">
<i class="fa fa-envelope lw-posa"></i> <i class="fa fa-envelope lw-posa"></i>
</div> </div>
<div class="col-md-4 col-xs-12 lw-comment-input lw-posr"> <div class="col-md-4 col-xs-12 lw-comment-input lw-posr">
<input type="url" placeholder="请输入主页地址..."> <input type="url" name="url" placeholder="请输入主页地址...">
<i class="fa fa-link fa-link lw-posa"></i> <i class="fa fa-link fa-link lw-posa"></i>
</div> </div>
<div class="col-md-12 col-xs-12 lw-comment-input lw-posr" style="text-align: right;">
<button class="lw-comment-submit">提交评论</button>
</div>
</form> </form>
</div> </div>
</div> </div>
<div id="comment-list">
<th:block th:fragment="comments">
<div class="lw-comment-list"> <div class="lw-comment-list">
<ul> <ul>
<li class="lw-posr"> <li class="lw-posr" th:each="comment:${commentPage.rows}">
<div class="lw-comment-avatar lw-posa"> <div class="lw-comment-avatar lw-posa">
<img src="https://kevinlu98.cn/freecdn-1.4/image/avatar/22.png" alt=""> <img th:src="${@defaultImages.avatar(comment.email)}" alt="">
</div> </div>
<div class="lw-comment-content"> <div class="lw-comment-content">
<p><b>xpboy</b> <span>2022-06-03 18:32</span> <a href="">回复</a></p> <p>
<p class="lw-comment-info"> <b th:text="${comment.nickname}"></b> <span
已经解决,问题是后台的的永久链接--重写功能,一定要开启成功,我是开启没有成功,但是可以使用,部分功能受限,如点赞、登录、注册等。我是windows th:text="${#dates.format(comment.created,'yyyy-MM-dd HH:mm')}"></span>
iis,添加web.config放到网站根目录就可以了,希望踩坑的朋友注意了。具体的web.config伪静态规则代码,可以联系我免费提供哦 <a th:data-id="${comment.id}" href="javascript:void(0);" class="comment-reply">回复</a>
qq24985536,希望帮助到大家</p> <a href="javascript:void(0);" class="comment-cancel" style="display: none">取消回复</a>
</p>
<p class="lw-comment-info" th:text="${comment.content}"></p>
</div> </div>
<ul> <ul th:if="${comment.children.size() > 0}">
<li class="lw-posr"> <li class="lw-posr" th:each="child:${comment.children}">
<div class="lw-comment-avatar lw-posa"> <div class="lw-comment-avatar lw-posa">
<img src="https://q1.qlogo.cn/g?b=qq&nk=1518228633&s=100" alt=""> <img th:src="${@defaultImages.avatar(child.email)}" alt="">
</div> </div>
<div class="lw-comment-content"> <div class="lw-comment-content">
<p><b>VOODOO</b> <span>2022-09-28 19:53</span> <a href="">回复</a></p> <p>
<p class="lw-comment-info"> <b th:text="${child.nickname}"></b>
@xpboy <span th:text="${#dates.format(child.created,'yyyy-MM-dd HH:mm')}"></span>
<br> <a th:data-id="${child.id}" href="javascript:void(0)"
谢谢</p> class="comment-reply">回复</a>
<a href="javascript:void(0)" class="comment-cancel"
style="display: none">取消回复</a>
</p>
<p class="lw-comment-info" th:text="${child.content}"></p>
</div> </div>
</li> </li>
</ul> </ul>
</li> </li>
<li class="lw-posr"> </ul>
<div class="lw-comment-avatar lw-posa">
<img src="https://q1.qlogo.cn/g?b=qq&nk=68159424&s=100" alt="">
</div>
<div class="lw-comment-content">
<p><b>anle</b> <span>2022-06-03 18:32</span> <a href="">回复</a></p>
<p class="lw-comment-info">
您好~我是俺没偷前端的运营,关注了您在分享的技术文章,觉得您的这套模板很棒,我们诚挚邀请您加入俺没偷前端CP主计划。完整福利和详细介绍请见:https://anlenotes.com/cp
我们会给作者提供包括流量、创作分成等, 我们诚挚的邀请您并期待您的加入~</p>
</div> </div>
<ul th:if="${commentPage.totalPages>1}" id="page-list" class="lw-pagenation">
<li th:if="${commentPage.currentPage != 1}"><a href="javascript:void(0)" data-page="1">首页</a></li>
<li th:each="num:${#numbers.sequence((commentPage.currentPage < 3 ? 1: commentPage.currentPage -2) ,(commentPage.currentPage < commentPage.totalPages -2?commentPage.currentPage+2: commentPage.totalPages))}">
<a th:class="${commentPage.currentPage eq num}?'lw-active':''"
href="javascript:void(0)"
th:data-page="${num}"
th:text="${num}"></a>
</li> </li>
<li class="lw-posr"> <li th:if="${commentPage.currentPage != commentPage.totalPages}">
<div class="lw-comment-avatar lw-posa"> <a href="javascript:void(0)" th:data-page="${commentPage.totalPages}">尾页</a>
<img src="https://q1.qlogo.cn/g?b=qq&nk=602314742&s=100" alt="">
</div>
<div class="lw-comment-content">
<p><b>王伟忘记使自己快乐</b> <span>2022-06-03 18:32</span> <a href="">回复</a></p>
<p class="lw-comment-info">
你好,请问一下,搭建的网页,在本地访问一点问题都没有,通过互联网域名访问,打开很慢,而且显示不正常,电脑手机都是一样的,是什么问题呢</p>
</div>
</li>
<li class="lw-posr">
<div class="lw-comment-avatar lw-posa">
<img src="https://q1.qlogo.cn/g?b=qq&nk=545431&s=100" alt="">
</div>
<div class="lw-comment-content">
<p><b>小布丁</b> <span>2022-06-03 18:32</span> <a href="">回复</a></p>
<p class="lw-comment-info">
为什么启用这个主题 后,评论用不了了,报错。</p>
</div>
</li> </li>
<li th:text="${'共 '+ commentPage.totalPages +' 页'}"></li>
</ul> </ul>
</th:block>
</div> </div>
<ul class="lw-pagenation">
<li><a href="">首页</a></li>
<li><a href="">1</a></li>
<li><a href="">2</a></li>
<li><a href="">3</a></li>
<li><a href="">4</a></li>
<li><a href="">5</a></li>
<li><a href="">尾页</a></li>
</ul>
</div> </div>
<div class="lw-right-list lw-md-show lw-posa"> <div class="lw-right-list lw-md-show lw-posa">
<th:block th:fragment="right"> <th:block th:fragment="right">
@ -292,7 +288,8 @@
<th:block th:fragment="footer"> <th:block th:fragment="footer">
<div id="right-bottom-bar"> <div id="right-bottom-bar">
<a id="to-top" href="javascript:void(0)"><i class="fa fa-arrow-up"></i></a> <a id="to-top" href="javascript:void(0)" title="回到顶部"><i class="fa fa-arrow-up"></i></a>
<a id="to-comment" href="javascript:void(0)" title="查看评论"><i class="fa fa-comment"></i></a>
<a target="_blank" <a target="_blank"
th:href="${T(java.lang.String).format('tencent://message/?uin=%s&amp;Site=&amp;menu=yes',@webSite.qq)}"><i th:href="${T(java.lang.String).format('tencent://message/?uin=%s&amp;Site=&amp;menu=yes',@webSite.qq)}"><i
class="fa fa-qq"></i></a> class="fa fa-qq"></i></a>
@ -328,6 +325,8 @@
<script src="/static/js/main.js"></script> <script src="/static/js/main.js"></script>
</th:block> </th:block>
<script src="/static/plugin/prism/prism.js"></script> <script src="/static/plugin/prism/prism.js"></script>
<script src="/static/plugin/layer/layer.js"></script>
<script> <script>
$(function () { $(function () {
// h1-h6缩进 // h1-h6缩进
@ -351,9 +350,84 @@
</script> </script>
<script> <script>
$(function () { $(function () {
// 当前文章id
let article_id = $('#article-id').val();
// 提交评论
$('.lw-main').on('submit', '#lw-comment-form',function () {
// 文章id突然没了????
$('#article-id').val(article_id);
let data = $(this).serialize();
console.log(data);
$.ajax({
url: '/comment',
method: 'POST',
data: data,
dataType: 'json',
success: res => {
if (res.code === 200) {
layer.msg('评论成功', {icon: 1})
commBoxReset();
// 局部刷新评论列表
$('#comment-list').load('/comment/'+article_id);
} else {
console.log(res);
layer.msg(res.message, {icon: 2})
}
}
})
return false; // 阻止表单的默认提交
})
// 重置或取消评论时调用
function commBoxReset() {
let source = $('.lw-comment-box')
let cloneBox = source.clone();
// 先移除当前评论框
source.remove()
// 恢复原来的评论框(放在评论数下面)
$('#comment-number').after(cloneBox)
// pid改为0
$('#lw-comment-form input[name=pid]').val(0)
// 清空输入的内容
$('#lw-comment-form textarea').val('')
$('#lw-comment-form input').val('')
// 显示回复按钮,不显示取消回复
$('#comment-list .comment-cancel').hide()
$('#comment-list .comment-reply').show()
}
// 局部刷新评论页数
$('#comment-list').on('click','#page-list a', function () {
let pageNum = $(this).data('page');
$('#comment-list').load(`/comment/${article_id}?pageNumber=${pageNum}`);
}).on('click','.comment-reply', function () {
let comment_id = $(this).data('id');
// 原本的评论框
let source = $('.lw-comment-box');
// 复制一个评论框
let cloneBox = source.clone();
// 删除原本的评论框
source.remove();
$(this).parent().parent().append(cloneBox);
// 更新父评论id
$('#lw-comment-form input[name=pid]').val(comment_id);
// 清空之前评论的内容
$('#lw-comment-form textarea').val('')
// 显示取消回复按钮
$(this).hide();
$(this).parent().find('.comment-cancel').show();
}).on('click','.comment-cancel',function () {
commBoxReset();
})
$(window).scroll(function () { $(window).scroll(function () {
let toTop = $(window).scrollTop(); // 距离页面顶端的高度 let toTop = $(window).scrollTop(); // 距离页面顶端的高度
let height = $(window).height(); // 窗口高度 let height = $(window).height(); // 窗口高度
// 定位到评论数上方20px
let toComment = $('#comment-number').offset().top;
// 判断滚动条高度大于屏幕高度一半时展示到顶部的按钮 // 判断滚动条高度大于屏幕高度一半时展示到顶部的按钮
if (toTop > height / 2) { if (toTop > height / 2) {
$('#to-top').stop().animate({ $('#to-top').stop().animate({
@ -370,6 +444,13 @@
scrollTop: 0 scrollTop: 0
}) })
}) })
// 跳到评论页面
$("#to-comment").on("click", function () {
$('body,html').stop().animate({
scrollTop: toComment
}, 500)
})
// 目录浮动 // 目录浮动
$(window).scroll(function () { $(window).scroll(function () {
let toTop = $(window).scrollTop() let toTop = $(window).scrollTop()

@ -0,0 +1,25 @@
package cc.bnblogs.test;
import cc.bnblogs.Application;
import cc.bnblogs.common.DefaultImages;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;
/**
* @author zfp@bnblogs.cc
* @createTime: 2022/10/22
*/
@RunWith(SpringRunner.class)
@SpringBootTest(classes = {Application.class}, webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
public class AvatarTest {
@Autowired
private DefaultImages defaultImages;
@Test
public void testAvatar() {
System.out.println(defaultImages.avatar("1337425156@qq.com"));
System.out.println(defaultImages.avatar("1337425156@q1q.com"));
}
}

@ -0,0 +1,25 @@
package cc.bnblogs.test;
import cc.bnblogs.Application;
import cc.bnblogs.common.MailHelper;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;
/**
* @author zfp@bnblogs.cc
* @createTime: 2022/10/22
*/
@RunWith(SpringRunner.class)
@SpringBootTest(classes = {Application.class}, webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
public class MailTest {
@Autowired
private MailHelper helper;
@Test
public void testSend(){
helper.sendMail("1337425156@qq.com","测试邮件","<h1>测试邮件</h1><p>我是一个测试邮件</p>");
}
}
Loading…
Cancel
Save