登录功能、后台控制台、评论功能完善、修复邮件异步发送bug

master
barney 2 years ago
parent 16e0687b4e
commit 901a6ee8a5
  1. 4
      .gitignore
  2. 0
      cache/.ehcache-diskstore.lock
  3. BIN
      cache/blog-cache.data
  4. 2
      src/main/java/cc/bnblogs/Application.java
  5. 2
      src/main/java/cc/bnblogs/common/MailHelper.java
  6. 5
      src/main/java/cc/bnblogs/common/WebSite.java
  7. 23
      src/main/java/cc/bnblogs/config/WebConfig.java
  8. 66
      src/main/java/cc/bnblogs/controller/AdminController.java
  9. 60
      src/main/java/cc/bnblogs/controller/CommentController.java
  10. 33
      src/main/java/cc/bnblogs/interceptor/LoginInterceptor.java
  11. 26
      src/main/java/cc/bnblogs/mapper/CommentMapper.java
  12. 33
      src/main/java/cc/bnblogs/service/CommentService.java
  13. 4
      src/main/java/cc/bnblogs/utils/CookieUtil.java
  14. 2
      src/main/resources/application.yml
  15. 7
      src/main/resources/templates/admin/article.html
  16. 12
      src/main/resources/templates/admin/banner.html
  17. 11
      src/main/resources/templates/admin/category.html
  18. 403
      src/main/resources/templates/admin/comment.html
  19. 15
      src/main/resources/templates/admin/common.html
  20. 10
      src/main/resources/templates/admin/friends.html
  21. 51
      src/main/resources/templates/admin/index.html
  22. 23
      src/main/resources/templates/admin/login.html
  23. 11
      src/main/resources/templates/admin/navigation.html
  24. 23
      src/test/java/cc/bnblogs/test/TestMd5Password.java

4
.gitignore vendored

@ -1,9 +1,13 @@
.idea/
# 忽略生成的目标文件所在文件夹
target/
# 忽略上传图片的文件夹
upload/
# 忽略日志文件夹
logs/
# 忽略缓存文件夹
cache/
# Compiled class file
*.class

Binary file not shown.

@ -3,6 +3,7 @@ package cc.bnblogs;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.scheduling.annotation.EnableAsync;
/**
@ -10,6 +11,7 @@ import org.springframework.cache.annotation.EnableCaching;
* @createTime: 2022/10/16
*/
@EnableCaching
@EnableAsync
@SpringBootApplication
public class Application {
public static void main(String[] args) {

@ -4,6 +4,7 @@ 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.scheduling.annotation.Async;
import org.springframework.stereotype.Component;
import javax.mail.MessagingException;
@ -31,6 +32,7 @@ public class MailHelper {
* @param subject 主题
* @param text 内容
*/
@Async
public void sendMail(String toSend, String subject, String text) {
MimeMessage message = javaMailSender.createMimeMessage();
MimeMessageHelper helper = new MimeMessageHelper(message);

@ -14,6 +14,7 @@ import java.util.List;
@Component
@ConfigurationProperties(prefix = "website")
public class WebSite {
public static final String LOGIN_SIGN = "login_sign";
// 站点名称
private String title;
// SEO的关键字
@ -22,6 +23,10 @@ public class WebSite {
private String description;
// 站长昵称
private String nickname;
// 管理员姓名
private String username;
// 管理员密码
private String password;
// 站长头像
private String avatar;
// 站长坐标

@ -1,9 +1,9 @@
package cc.bnblogs.config;
import cc.bnblogs.common.LRUCache;
import cc.bnblogs.common.Result;
import com.fasterxml.jackson.databind.ObjectMapper;
import cc.bnblogs.interceptor.LoginInterceptor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.web.server.ConfigurableWebServerFactory;
import org.springframework.boot.web.server.ErrorPage;
@ -11,12 +11,10 @@ import org.springframework.boot.web.server.WebServerFactoryCustomizer;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.File;
/**
@ -30,8 +28,23 @@ public class WebConfig implements WebMvcConfigurer {
@Value("${upload.base-dir}")
private String baseDir;
@Override
public void addInterceptors(InterceptorRegistry registry) {
// 注册拦截器,添加拦截路由和放行路由
registry.addInterceptor(loginInterceptor)
.addPathPatterns("/admin/**")
.excludePathPatterns("/admin/login","/admin/logout","/admin/login.html");
}
private final LoginInterceptor loginInterceptor;
public static final Integer MAX_LRU_CACHE_SIZE = 20;
public WebConfig(LoginInterceptor loginInterceptor) {
this.loginInterceptor = loginInterceptor;
}
@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
// 创建static目录到/static/的路由

@ -1,15 +1,24 @@
package cc.bnblogs.controller;
import cc.bnblogs.common.ArticleSearch;
import cc.bnblogs.common.Result;
import cc.bnblogs.common.WebSite;
import cc.bnblogs.pojo.Article;
import cc.bnblogs.pojo.Category;
import cc.bnblogs.service.ArticleService;
import cc.bnblogs.service.CategoryService;
import cc.bnblogs.service.CommentService;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.util.DigestUtils;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.thymeleaf.util.StringUtils;
import javax.servlet.http.HttpSession;
import java.nio.charset.StandardCharsets;
import java.util.List;
import java.util.Objects;
@ -22,14 +31,25 @@ import java.util.Objects;
public class AdminController {
private final CategoryService categoryService;
private final ArticleService articleService;
public AdminController(CategoryService categoryService, ArticleService articleService) {
private final CommentService commentService;
private final WebSite webSite;
public AdminController(CategoryService categoryService, ArticleService articleService, CommentService commentService, WebSite webSite) {
this.categoryService = categoryService;
this.articleService = articleService;
this.commentService = commentService;
this.webSite = webSite;
}
@GetMapping("/")
public String index() {
public String index(Model model) {
// 获取文章总数、分类总数、总评论数、未读评论数、最新的5篇文章、最新的5条评论
model.addAttribute("ArticleCount",articleService.count());
model.addAttribute("CategoryCount",categoryService.count());
model.addAttribute("CommentAll",commentService.count(null));
model.addAttribute("CommentNotReadCount",commentService.count(false));
model.addAttribute("NewArticleList",articleService.search(ArticleSearch.builder()
.type(Article.TYPE_ARTICLE).pageNum(1).pageSize(5).build()).getRows());
model.addAttribute("NewCommentList",commentService.list(null,1,5).getRows());
return "admin/index";
}
@GetMapping("/category.html")
@ -56,7 +76,9 @@ public class AdminController {
}
@GetMapping("/comment.html")
public String comment() {
public String comment(@RequestParam(required = false)Boolean view, Model model) {
// 给前端返回一个isView参数用来选中对应按扭
model.addAttribute("isView",view);
return "admin/comment";
}
@ -77,4 +99,40 @@ public class AdminController {
model.addAttribute("pageTitle", Objects.isNull(article.getId()) ? "创建新文章" : "编辑文章---" + article.getTitle());
return "admin/write";
}
/**
* 前台访问登陆页面
* @return
*/
@GetMapping("/login.html")
public String login() {
return "admin/login";
}
/**
* 登录校验
* @param username 用户名
* @param password 密码
* @return
*/
@PostMapping("/login")
public String login(String username, String password, Model model, HttpSession session) {
// 用户名和密码都一致,登录成功
if (StringUtils.equals(webSite.getPassword(), DigestUtils
.md5DigestAsHex(password.getBytes(StandardCharsets.UTF_8)))
&& StringUtils.equals(username,webSite.getUsername())) {
session.setAttribute(WebSite.LOGIN_SIGN,true);
return "redirect:/admin/";
}
else {
model.addAttribute("errorMsg","用户名或密码错误!");
return "admin/login";
}
}
@GetMapping("/logout")
public String logout(HttpSession session) {
session.removeAttribute(WebSite.LOGIN_SIGN);
return "admin/login";
}
}

@ -1,9 +1,15 @@
package cc.bnblogs.controller;
import cc.bnblogs.common.PageHelper;
import cc.bnblogs.common.Result;
import cc.bnblogs.common.WebSite;
import cc.bnblogs.enums.ResultEnum;
import cc.bnblogs.pojo.Article;
import cc.bnblogs.pojo.Comment;
import cc.bnblogs.service.CommentService;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.web.bind.annotation.*;
import java.util.List;
@ -17,18 +23,29 @@ import java.util.Objects;
@RequestMapping("/admin/comment")
public class CommentController {
private final CommentService commentService;
private final WebSite webSite;
public CommentController(CommentService commentService) {
public CommentController(CommentService commentService, WebSite webSite) {
this.commentService = commentService;
this.webSite = webSite;
}
/**
* 请求所有评论的接口
* @return 返回所有评论
* 根据评论是否已读返回分页后的评论列表
* 按评论创建时间降序返回
* @param view 是否已读
* @param pageNumber 页号
* @param pageSize 页大小
* @return 分页后的评论
*/
@GetMapping("/")
public Result<List<Comment>> list() {
return Result.success(commentService.list());
public Result<PageHelper<Comment>> list(@RequestParam(required = false) Boolean view,
@RequestParam(required = false,defaultValue = "1") Integer pageNumber,
@RequestParam(required = false,defaultValue = "1")Integer pageSize) {
pageNumber = Math.max(1,pageNumber);
pageSize = Math.max(1,pageSize);
return Result.success(commentService.list(view,pageNumber,pageSize));
}
/**
@ -67,4 +84,37 @@ public class CommentController {
commentService.delete(id);
return Result.success();
}
/**
* 一键已读所有评论
*/
@GetMapping("/read")
public Result<String> readAll(){
commentService.readAll();
return Result.success();
}
/**
* 回复评论
* @param pid 父评论id
* @param aid 文章id
* @param message 回复内容
* @return 操作结果
*/
@PostMapping("/{pid}")
public Result<String> reply(@PathVariable Integer pid,Integer aid,String message) {
// 构造一个comment对象
// 填充站长的昵称、邮箱、网站地址、父评论id、文章id、回复内容
Comment comment = Comment.builder()
.article(Article.builder().id(aid).build())
.nickname(webSite.getNickname())
.email(webSite.getMail())
.url(webSite.getDomain())
.pid(pid)
.content(message)
.build();
// 保存评论
commentService.save(comment);
return Result.success();
}
}

@ -0,0 +1,33 @@
package cc.bnblogs.interceptor;
import cc.bnblogs.common.WebSite;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.util.Objects;
/**
* @author zfp@bnblogs.cc
* @createTime: 2022/10/23
* @description: 登录拦截器
*/
@Component
public class LoginInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
HttpSession session = request.getSession();
Boolean sign = (Boolean) session.getAttribute(WebSite.LOGIN_SIGN);
if (Objects.nonNull(sign) && sign) {
return true;
}
else {
request.setAttribute("errorMsg","请登录后再操作!");
// 当Session中没有登录信息时我们直接转发到登录页面并给出请登录后再进行操作的提示
request.getRequestDispatcher("/admin/login.html").forward(request,response);
return false;
}
}
}

@ -5,6 +5,9 @@ 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.Modifying;
import org.springframework.data.jpa.repository.Query;
import org.springframework.transaction.annotation.Transactional;
import java.util.List;
@ -35,4 +38,27 @@ public interface CommentMapper extends JpaRepository<Comment,Integer> {
* @return 该父评论下的所有评论
*/
List<Comment> findAllByPidOrderByCreatedDesc(Integer pid);
/**
* 将所有未读评论改为已读
*/
@Modifying
@Transactional(rollbackFor = {Exception.class})
@Query(value = "update blog_comment set view=true where view=false ", nativeQuery = true)
void readAll();
/**
* 根据view选择不同的评论
* @param view 是否已读
* @param pageable 分页器
* @return 评论列表
*/
Page<Comment> findAllByView(Boolean view, Pageable pageable);
/**
* 根据view的值统计评论数
* @param view 是否已读
* @return 评论个数
*/
Long countCommentByView(Boolean view);
}

@ -10,7 +10,8 @@ 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.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.stereotype.Service;
import java.util.Date;
@ -48,12 +49,13 @@ public class CommentService {
}
/**
* 获取评论总数
*
* @return 评论
* 根据是否view的取值返回评论数(null:表示所有)
* @param view 是否已读
* @return 评论
*/
public long count() {
return commentMapper.count();
public long count(Boolean view) {
return Objects.isNull(view) ?
commentMapper.count(): commentMapper.countCommentByView(view);
}
/**
@ -87,7 +89,9 @@ public class CommentService {
comment.setPid(findCommentPid(comment.getPid()));
}
commentMapper.save(comment);
} else {
}
// 修改评论内容
else {
Comment one = detail(comment.getId());
UpdateUtil.copyNullProperties(comment, one);
commentMapper.save(one);
@ -99,7 +103,6 @@ public class CommentService {
*
* @param id 文章id
*/
@Async
public void sendMailToWebsite(Integer id) {
Article article = articleMapper.findById(id).orElse(null);
if (Objects.isNull(article)) {
@ -119,7 +122,6 @@ public class CommentService {
* @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)) {
@ -170,4 +172,17 @@ public class CommentService {
// 评论内容-当前页数-评论总数-总页数
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();
}
public void readAll() {
commentMapper.readAll();
}
public PageHelper<Comment> list(Boolean view, Integer pageNumber, Integer pageSize) {
Pageable pageable = PageRequest.of(pageNumber-1,pageSize, Sort.by(Sort.Direction.DESC,"created"));
Page<Comment> page = Objects.isNull(view) ? commentMapper.findAll(pageable) : commentMapper.findAllByView(view,pageable);
return PageHelper.<Comment>builder()
.rows(page.getContent())
.total(page.getTotalElements())
.build();
}
}

@ -31,8 +31,8 @@ public class CookieUtil {
}
public static void setCookie(HttpServletResponse response, String cookieName, String value) {
// 有效期为7
setCookie(response, cookieName, value, 7 * 24 * 60 * 60);
// 有效期为1
setCookie(response, cookieName, value, 24 * 60 * 60);
}
public static void deleteCookie(HttpServletResponse response, String cookieName) {

@ -73,6 +73,8 @@ website:
domain: http://localhost:8080 #域名
avatar: https://hugo.bnblogs.cc/images/img/20220215001349.png
nickname: barney
username: admin
password: 0192023a7bbd73250516f069df18b500
address: 广东 广州
tags:
- java

@ -10,7 +10,7 @@
<div class="container lw-main lw-banner">
<div class="btn-group" role="group" style="margin-bottom: 20px;" aria-label="...">
<a th:href="@{/admin/write.html}" class="btn btn-success"><i class="fa fa-plus"></i> 新增</a>
<a th:href="@{/admin/write.html}" class="btn btn-success" style="outline: none;"><i class="fa fa-plus"></i> 新增</a>
</div>
<form id="search-form" class="form-inline" style="float:right;">
@ -41,8 +41,8 @@
<label>关键字</label>
<input id="article-keywords" type="text" class="form-control" placeholder="请输入关键字...">
</div>
<button type="reset" class="btn btn-default" id="reset-btn"><i class="fa fa-refresh"></i> 重置</button>
<button type="submit" class="btn btn-primary"><i class="fa fa-search"></i> 搜索</button>
<button style="outline: none;" type="reset" class="btn btn-default" id="reset-btn"><i class="fa fa-refresh"></i> 重置</button>
<button style="outline: none;" type="submit" class="btn btn-primary"><i class="fa fa-search"></i> 搜索</button>
</form>
<table id="data-table" style="user-select: none"></table>
</div>
@ -60,6 +60,7 @@
pageSize: 5, // 每页5条数据
pageList: [5, 10, 15], // 可选页大小
responseHandler: function (res) {
console.log(res.data);
return res.data
},
//这个是查询参数,就是bootstrap-table请求后端时携带的参数

@ -10,10 +10,10 @@
<div class="container lw-main lw-banner">
<div class="btn-group" role="group" style="margin-bottom: 20px" aria-label="...">
<button type="button" id="create-banner-btn" class="btn btn-default"><i class="fa fa-plus"></i>
<button type="button" style="outline: none;" id="create-banner-btn" class="btn btn-success"><i class="fa fa-plus"></i>
新增
</button>
<button type="button" id="refresh-banner-btn" class="btn btn-default"><i class="fa fa-refresh"></i>
<button type="button" style="outline: none;" id="refresh-banner-btn" class="btn btn-primary"><i class="fa fa-refresh"></i>
保存顺序
</button>
</div>
@ -70,8 +70,8 @@
</div>
<div class="modal-footer">
<button type="button" class="btn btn-default" data-dismiss="modal">关闭</button>
<button type="submit" class="btn btn-primary">保存</button>
<button style="outline: none;" type="button" class="btn btn-default" data-dismiss="modal">关闭</button>
<button style="outline: none;" type="submit" class="btn btn-primary">保存</button>
</div>
</form>
</div>
@ -135,8 +135,8 @@
align: "center",
width: 180,
formatter: function (value) {
return `<button type="button" data-id="${value}" class="btn btn-info btn-sm banner-edit-btn"><i class="fa fa-edit"></i> 编辑</button>
<button type="button" data-id="${value}" class="btn btn-danger btn-sm banner-delete-btn"><i class="fa fa-trash"></i> 删除</button>`
return `<button type="button" style="outline: none;" data-id="${value}" class="btn btn-info btn-sm banner-edit-btn"><i class="fa fa-edit"></i> 编辑</button>
<button type="button" style="outline: none;" data-id="${value}" class="btn btn-danger btn-sm banner-delete-btn"><i class="fa fa-trash"></i> 删除</button>`
}
}
],

@ -8,7 +8,8 @@
<div class="container lw-main lw-banner">
<div class="btn-group" role="group" style="margin-bottom: 20px" aria-label="...">
<button type="button" id="create-category-btn" class="btn btn-default"><i class="fa fa-plus"></i> 新增</button>
<button type="button" id="create-category-btn" style="outline: none;" class="btn btn-success">
<i class="fa fa-plus"></i> 新增</button>
</div>
<table id="data-table"></table>
</div>
@ -36,8 +37,8 @@
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-default" data-dismiss="modal">关闭</button>
<button type="submit" class="btn btn-primary">保存</button>
<button type="button" class="btn btn-default" style="outline: none;" data-dismiss="modal">关闭</button>
<button type="submit" class="btn btn-primary" style="outline: none;">保存</button>
</div>
</form>
</div>
@ -75,8 +76,8 @@
align: "center",
width: 180,
formatter: function (value) {
return `<button type="button" data-id="${value}" class="btn btn-info btn-sm category-edit-btn"><i class="fa fa-edit"></i> 编辑</button>
<button type="button" data-id="${value}" class="btn btn-danger btn-sm category-delete-btn"><i class="fa fa-trash"></i> 删除</button>`
return `<button type="button" style="outline: none;" data-id="${value}" class="btn btn-info btn-sm category-edit-btn"><i class="fa fa-edit"></i> 编辑</button>
<button type="button" style="outline: none;" data-id="${value}" class="btn btn-danger btn-sm category-delete-btn"><i class="fa fa-trash"></i> 删除</button>`
}
}
],

@ -1,232 +1,219 @@
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org" lang="en">
<head th:replace="admin/common::header(~{::title},~{},~{})">
<title>评论管理</title>
<head th:replace="admin/common::header(~{::title},~{},~{::style})">
<title>评论管理</title>
<style>
#comment-content {
width: 400px;
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
}
</style>
</head>
<body>
<th:block th:include="admin/common::nav('comment')"></th:block>
<div class="container lw-main lw-banner">
<div class="btn-group" role="group" style="margin-bottom: 20px" aria-label="...">
<button type="button" id="create-comment-btn" class="btn btn-default"><i class="fa fa-plus"></i>
新增</button>
</div>
<table id="data-table" style="user-select: none"></table>
</div>
<!-- 编辑窗口 -->
<div class="modal fade" id="save-window" tabindex="-1" role="dialog" aria-labelledby="myModalLabel">
<div class="modal-dialog" role="document">
<div class="modal-content">
<form id="data-form">
<input type="hidden" name="id">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-label="Close"><span
aria-hidden="true">&times;</span></button>
<h4 class="modal-title" id="window-title">Modal Title</h4>
</div>
<div class="modal-body">
<div class="form-group">
<label>评论者的昵称</label>
<input type="text" name="nickname" class="form-control"
placeholder="请输入评论者的昵称...">
</div>
<div class="form-group">
<label>评论者的邮箱</label>
<input type="text" name="email" class="form-control"
placeholder="请输入评论者的邮箱...">
</div>
<div class="form-group">
<label>评论者的主页</label>
<input type="text" name="url" class="form-control"
placeholder="请输入评论者的主页...">
</div>
<div class="form-group">
<label>评论内容</label>
<input type="text" name="content" class="form-control"
placeholder="请输入评论内容...">
</div>
<div class="form-group">
<label>是否已读</label>
<input type="text" name="view" class="form-control"
placeholder="请输入是否已读...">
</div>
<div class="form-group">
<label>评论的父评论</label>
<input type="text" name="pid" class="form-control"
placeholder="请输入评论的父评论...">
</div>
<div class="form-group">
<label>评论创建时间</label>
<input type="text" name="created" class="form-control"
placeholder="请输入评论创建时间...">
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-default" data-dismiss="modal">关闭</button>
<button type="submit" class="btn btn-primary">保存</button>
</div>
</form>
<div class="btn-group" role="group" style="margin-bottom: 20px" aria-label="...">
<button style="outline: none;" type="button" id="readAll-comment-btn" class="btn btn-default">
<i class="fa fa-refresh"></i> 一键已读
</button>
</div>
<div class="btn-group" id="btn-selected-group" role="group" style="margin-bottom: 20px; float: right" aria-label="...">
<button type="button" style="outline: none;" th:class="${isView eq null}?'btn btn-primary':'btn btn-default'" data-view="">全部
</button>
<button type="button" style="outline: none;" th:class="${isView eq true}?'btn btn-primary':'btn btn-default'" data-view="true">已读
</button>
<button type="button" style="outline: none;" th:class="${isView eq false}?'btn btn-primary':'btn btn-default'" data-view="false">未读
</button>
</div>
</div>
<table id="data-table" style="user-select: none"></table>
</div>
<th:block th:include="admin/common::footer"></th:block>
<script>
$(function () {
// 显示表单所有评论数据
$('#data-table').bootstrapTable({
url: "/admin/comment/",
columns: [
{
title: "序号",
width: 50,
align: 'center',
// 生成自增的序号
formatter: function (value, row, index) {
return index + 1
}
} ,
{
title: "评论者的昵称",
field: "nickname",
},
{
title: "评论者的邮箱",
field: "email",
},
{
title: "评论者的主页",
field: "url",
},
{
title: "评论内容",
field: "content",
},
{
title: "是否已读",
field: "view",
},
{
title: "评论的父评论",
field: "pid",
},
{
title: "评论创建时间",
field: "created",
},
{
field: "id",
title: '操作',
align: "center",
width: 180,
formatter: function (value) {
return `<button type="button" data-id="${value}" class="btn btn-info btn-sm comment-edit-btn"><i class="fa fa-edit"></i> 编辑</button>
<button type="button" data-id="${value}" class="btn btn-danger btn-sm comment-delete-btn"><i class="fa fa-trash"></i> 删除</button>`
}
}
],
responseHandler: function (res) {
return res.data
}
})
// 编辑对应评论
$("#data-table").on("click", ".comment-edit-btn", function () {
let id = $(this).data("id")
$.ajax({
url: "/admin/comment/" + id,
method: 'GET',
dataType: 'json',
success: res => {
if (res.code === 200) {
$("#window-title").text('编辑评论')
$("#data-form").initForm(res.data)
$('#save-window').modal('show')
}
else {
layer.msg(res.message, { icon: 2 })
}
}
})
})
$(function () {
// 点击已读、未读和全部
$('#btn-selected-group button').on('click',function () {
// 点中哪个就把哪个按钮高亮
$('#btn-selected-group .btn-primary').removeClass('btn-primary').addClass('btn-default')
$(this).removeClass('btn-default').addClass('btn-primary')
$('#data-table').bootstrapTable('refresh', {silent: true})
})
// 新增评论
$("#create-comment-btn").on("click", function () {
$("#window-title").text('新增评论')
// 清空之前表单的数据
$("#data-form").initForm({id: "", nickname: "", email: "", url: "", content: "", view: "", pid: "", created: "",})
$('#save-window').modal('show')
})
// 显示表单所有评论数据
$('#data-table').bootstrapTable({
url: "/admin/comment/",
pagination: true, // 开启分页
sidePagination: 'server', // 使用服务端数据
pageNumber: 1, // 默认页码第一页
pageSize: 10, // 每页5条数据
pageList: [10, 20, 40], // 可选页大小
//这个是查询参数,就是bootstrap-table请求后端时携带的参数
// offset为数据的序号,limit为页大小
queryParams: function (params) {
return {
pageNumber: (params.offset / params.limit) + 1,
pageSize: params.limit,
view: $('#btn-selected-group .btn-primary').data('view'),
}
},
responseHandler: function (res) {
return res.data
},
onDblClickCell: function (field, value, row, $element) {
// 双击评论内容部分时可以编辑评论
let comment = $('#data-table .content');
comment.parent().html(comment.val());
if (field === 'content') {
$element.html(`<textarea class="form-control content">${value}</textarea>`);
$('.content').blur(function () {
// 获取评论id和评论内容
let data = {id: row.id, content: $(this).val()};
$.ajax({
url: '/admin/comment/',
method: 'POST',
data: data,
dataType: 'json',
success: res => {
if (res.code === 200) {
layer.msg("保存成功", {icon: 1, time: 700})
$('#data-table').bootstrapTable('refresh', {silent: true})
} else {
layer.msg(res.message, {icon: 2})
}
}
})
})
}
},
columns: [
{
title: "序号",
align: 'center',
// 生成自增的序号
formatter: function (value, row, index) {
return index + 1
}
},
{
title: "评论者",
field: "nickname",
align: "center",
formatter: (value, row) => {
let html = row.url ? `<a href="${row.url}" target="_blank">${value}</a>` : value;
return html + `<br>${row.email}`;
}
},
{
title: "评论内容",
field: "content",
formatter: value => {
return `<p id="comment-content">${value}</p>`
}
},
{
title: "是否已读",
field: "view",
align: "center",
formatter: value => {
return value ? "已读" : "未读";
}
},
{
title: "评论创建时间",
field: "created",
align: "center",
},
{
field: "id",
title: '操作',
align: "center",
formatter: function (value,row) {
return `<button type="button" data-id="${value}" data-aid="${row.article.id}" class="btn btn-success btn-sm comment-reply-btn"><i class="fa fa-comment"></i> 回复</button>
<button type="button" data-id="${value}" class="btn btn-danger btn-sm comment-delete-btn"><i class="fa fa-trash"></i> 删除</button>`
}
}
]
})
// 删除评论
$("#data-table").on("click", ".comment-delete-btn", function () {
let id = $(this).data('id')
let idx = layer.confirm('是否要删除该数据?', {
btn: ['确认', '取消'] //按钮
}, function () { // 点击确认后删除
$.ajax({
url: '/admin/comment/' + id,
method: 'delete',
dataType: 'json',
success: res => {
if (res.code === 200) {
layer.msg("删除成功", { icon: 1, time: 700 })
$('#data-table').bootstrapTable('refresh', { silent: true })
} else {
layer.msg(res.message, { icon: 2 })
}
}
// 删除评论
$("#data-table").on("click", ".comment-delete-btn", function () {
let id = $(this).data('id')
let idx = layer.confirm('是否要删除该数据?', {
btn: ['确认', '取消'] //按钮
}, function () { // 点击确认后删除
$.ajax({
url: '/admin/comment/' + id,
method: 'delete',
dataType: 'json',
success: res => {
if (res.code === 200) {
layer.msg("删除成功", {icon: 1, time: 700})
$('#data-table').bootstrapTable('refresh', {silent: true})
} else {
layer.msg(res.message, {icon: 2})
}
}
})
layer.close(idx); // 关闭提示框
})
})
layer.close(idx); // 关闭提示框
})
})
// 更新评论数据
$("#data-form").on("submit", function () {
let data = $(this).serialize();
$.ajax({
url: "/admin/comment/",
method: "POST",
data: data,
dataType: "json",
success: res => {
if (res.code === 200) {
// 显示成功信息
layer.msg("保存成功", { icon: 1, time: 600 }, function () {
// 关闭模态框
$('#save-window').modal('hide')
// 重载表格数据
$('#data-table').bootstrapTable('refresh', { silent: true })
// 一键已读评论
$('#readAll-comment-btn').on('click', function () {
$.ajax({
url: "/admin/comment/read",
type: 'GET',
dataType: 'json',
success(res) {
if (res.code === 200) {
layer.msg("操作成功", {icon: 1, time: 700})
$('#data-table').bootstrapTable('refresh', {silent: true})
} else {
layer.msg(res.message, {icon: 2})
}
}
})
}
else {
layer.msg(res.message, { icon: 2 })
}
}
})
return false; // 阻止表单的提交行为
})
})
})
// 回复评论实现
$("#data-table").on('click', '.comment-reply-btn', function () {
// 获取目标资源id
let id = $(this).data('id')
let aid = $(this).data('aid')
layer.prompt({title: '回复', formType: 2}, function (text, index) {
layer.close(index);
$.ajax({
url: '/admin/comment/' + id,
method: 'POST',
data: {
aid: aid,
message: text
}, dataType: 'json',
success: res => {
if (res.code === 200) {
layer.msg("回复成功", {icon: 1})
console.log(res);
$('#data-table').bootstrapTable('refresh', {silent: true})
} else {
layer.msg(res.message, {icon: 2})
}
}
})
});
})
})
</script>
</body>

@ -31,7 +31,7 @@
<span class="icon-bar"></span>
<span class="icon-bar"></span>
</button>
<a class="navbar-brand" href="#">博客后台</a>
<span class="navbar-brand" style="user-select: none">博客后台</span>
</div>
<div class="collapse navbar-collapse" id="bs-example-navbar-collapse-1">
<ul class="nav navbar-nav">
@ -46,8 +46,8 @@
<li th:class="${active eq 'navigation'} ? 'active': ''"><a th:href="@{/admin/navigation.html}">导航管理</a></li>
</ul>
<ul class="nav navbar-nav navbar-right">
<li><a href="#">退出登录</a></li>
<li><a th:href="@{/}">网站前台</a></li>
<li><a href="/admin/logout">退出登录</a></li>
<li><a th:href="@{/}" target="_blank">网站前台</a></li>
</ul>
</div>
</div>
@ -58,13 +58,12 @@
<footer>
<div class="container lw-footer">
<p>
© 2019-2020 <a href="/">冷文学习者</a> <a target="_blank"
href="https://beian.miit.gov.cn">陕ICP备19024566-1号</a>
© 2019-2020 <a th:href="@{/}" th:text="${@webSite.nickname}"></a> <a target="_blank"
href="https://beian.miit.gov.cn">赣ICP备2022002184号-1</a>
<a style="margin-left: 10px" target="_blank"
href="http://www.beian.gov.cn/portal/registerSystemInfo?recordcode=11011402012109">京公网安备
11011402012109号</a>
href="http://www.beian.gov.cn/portal/registerSystemInfo?recordcode=36092202000146">赣公网安备 36092202000146号</a>
</p>
<p>如果您有什么好的意见或建议请发邮件至 <a href="#">kevinlu98@qq.com</a></p>
<p>如果您有什么好的意见或建议请发邮件至 <a href="http://mail.qq.com/cgi-bin/qm_share?t=qm_mailme&email=1337425156@qq.com" th:text="${@webSite.getMail()}"></a></p>
</div>
</footer>
<script src="/static/plugin/jquery/jquery-3.5.1.min.js"></script>

@ -10,7 +10,7 @@
<div class="container lw-main lw-banner">
<div class="btn-group" role="group" style="margin-bottom: 20px" aria-label="...">
<button type="button" id="create-friends-btn" class="btn btn-default"><i class="fa fa-plus"></i>
<button type="button" style="outline: none;" id="create-friends-btn" class="btn btn-success"><i class="fa fa-plus"></i>
新增
</button>
</div>
@ -43,8 +43,8 @@
</div>
<div class="modal-footer">
<button type="button" class="btn btn-default" data-dismiss="modal">关闭</button>
<button type="submit" class="btn btn-primary">保存</button>
<button type="button" style="outline: none;" class="btn btn-default" data-dismiss="modal">关闭</button>
<button type="submit" style="outline: none;" class="btn btn-primary">保存</button>
</div>
</form>
</div>
@ -87,8 +87,8 @@
align: "center",
width: 180,
formatter: function (value) {
return `<button type="button" data-id="${value}" class="btn btn-info btn-sm friends-edit-btn"><i class="fa fa-edit"></i> 编辑</button>
<button type="button" data-id="${value}" class="btn btn-danger btn-sm friends-delete-btn"><i class="fa fa-trash"></i> 删除</button>`
return `<button type="button" style="outline: none;" data-id="${value}" class="btn btn-info btn-sm friends-edit-btn"><i class="fa fa-edit"></i> 编辑</button>
<button type="button" style="outline: none;" data-id="${value}" class="btn btn-danger btn-sm friends-delete-btn"><i class="fa fa-trash"></i> 删除</button>`
}
}
],

@ -10,9 +10,9 @@
<div class="container lw-main">
<div class="jumbotron">
<h3>网站概要</h3>
<p>目前有 <i>83</i> 篇文章, 并有 <i>5010</i> 条关于你的评论在 <i>9</i> 个分类中.</p>
<p>其中有 <i>12</i>条 评论未查看:</p>
<p><a class="btn btn-primary btn-lg" href="#" role="button">点此查看</a></p>
<p>目前有 <i th:text="${ArticleCount}"></i> 篇文章, 并有 <i th:text="${CommentAll}"></i> 条关于你的评论在 <i th:text="${CategoryCount}"></i> 个分类中.</p>
<p>其中有 <i th:text="${CommentNotReadCount}"></i>条 评论未查看:</p>
<p><a class="btn btn-primary btn-lg" th:href="@{/admin/comment.html(view=false)}" role="button">点此查看</a></p>
</div>
<div class="row">
<div class="col-md-6">
@ -20,25 +20,9 @@
<li class="list-group-item disabled">
<b>最新文章</b>
</li>
<li class="list-group-item">
<span class="badge">09-28</span>
<a href="#"> Conda虚拟环境使用</a>
</li>
<li class="list-group-item">
<span class="badge">05-26</span>
<a href="#"> 关于我这些年的经历</a>
</li>
<li class="list-group-item">
<span class="badge">04-28</span>
<a href="#"> 我曾是少年</a>
</li>
<li class="list-group-item">
<span class="badge">03-07</span>
<a href="#"> 迷茫</a>
</li>
<li class="list-group-item">
<span class="badge">02-10</span>
<a href="#"> Spring Boot 3.0 M1 发布</a>
<li class="list-group-item" th:each="article:${NewArticleList}">
<span class="badge" th:text="${#dates.format(article.created,'yyyy-MM-dd HH:mm')}"></span>
<a th:href="@{/{id}.html(id=${article.id})}" target="_blank" th:text="${article.title}"></a>
</li>
</ul>
</div>
@ -47,26 +31,11 @@
<li class="list-group-item disabled">
<b>最新评论</b>
</li>
<li class="list-group-item">
<span class="badge">09-29</span>
再度再度: 发现这个主题很特别!
</li>
<li class="list-group-item">
<span class="badge">09-29</span>
再度再度: 就OK间科技看
</li>
<li class="list-group-item">
<span class="badge">09-28</span>
duelex: 喜欢作者的这个风格,希望作者不断进步
</li>
<li class="list-group-item">
<span class="badge">09-27</span>
蜘蛛: 阿巴阿巴
</li>
<li class="list-group-item">
<span class="badge">09-10</span>
妄想: 需要插件
<li class="list-group-item" th:each="comment:${NewCommentList}">
<span class="badge" th:text="${#dates.format(comment.created,'yyyy-MM-dd HH:mm')}"></span>
<a th:href="@{/{id}.html#comment-number(id=${comment.article.id})}" th:text="${comment.content}"></a>
</li>
</ul>
</div>
</div>

@ -1,5 +1,5 @@
<!doctype html>
<html lang="en">
<html xmlns:th="http://www.thymeleaf.org" lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
@ -7,29 +7,36 @@
<link rel="stylesheet" href="/static/plugin/font-awesome/css/font-awesome.min.css">
<!-- 自定义css文件 -->
<link rel="stylesheet" href="/static/admin/css/login.css">
<style>
#error-msg {
color: red;
text-align: center;
font-size: 20px;
font-weight: bold;
}
</style>
</head>
<body>
<div class="lw-login-page">
<div class="lw-login-box">
<div class="lw-left">
<h1>欢迎登录</h1>
<p>冷文学习者-管理后台</p>
<p><th:block th:text="${@webSite.nickname}"></th:block>-管理后台</p>
</div>
<div class="lw-right">
<form>
<div id="error-msg" th:if="${errorMsg!=null}" th:text="${errorMsg}"></div>
<form th:action="@{/admin/login}" method="post">
<div class="lw-input">
<input type="text" placeholder="用户名">
<input type="text" name="username" placeholder="用户名">
</div>
<div class="lw-input">
<input type="password" placeholder="密码">
<input type="password" name="password" readonly onfocus="this.removeAttribute('readonly')" placeholder="密码">
</div>
<div class="lw-input">
<button type="submit">登录后台</button>
</div>
</form>
<p>Copyright © www.kevinlu98.cn All Rights Reserved.
<br>
冷文学习者版权所有</p>
<p>Copyright © <th:block th:text="${@webSite.title}"></th:block> All Rights Reserved.</p>
</div>
</div>
</div>

@ -10,11 +10,11 @@
<div class="container lw-main lw-banner">
<div class="btn-group" role="group" style="margin-bottom: 20px" aria-label="...">
<button type="button" id="create-navigation-btn" class="btn btn-default"><i class="fa fa-plus"></i>
<button type="button" id="create-navigation-btn" style="outline: none;" class="btn btn-success"><i class="fa fa-plus"></i>
新增
</button>
<button type="button" id="refresh-ordered-btn" class="btn btn-default"><i class="fa fa-refresh"></i>
<button type="button" id="refresh-ordered-btn" style="outline: none;" class="btn btn-primary"><i class="fa fa-refresh"></i>
保存顺序
</button>
</div>
@ -74,8 +74,8 @@
</div>
<div class="modal-footer">
<button type="button" class="btn btn-default" data-dismiss="modal">关闭</button>
<button type="submit" class="btn btn-primary">保存</button>
<button type="button" style="outline: none;" class="btn btn-default" data-dismiss="modal">关闭</button>
<button type="submit" style="outline: none;" class="btn btn-primary">保存</button>
</div>
</form>
</div>
@ -150,7 +150,8 @@
align: "center",
width: 180,
formatter: function (value) {
return `<button type="button" data-id="${value}" class="btn btn-info btn-sm navigation-edit-btn" style="margin-right: 10px"><i class="fa fa-edit"></i> 编辑</button><button type="button" data-id="${value}" class="btn btn-danger btn-sm navigation-delete-btn"><i class="fa fa-trash"></i> 删除</button>`
return `<button style="outline: none;" type="button" data-id="${value}" class="btn btn-info btn-sm navigation-edit-btn" style="margin-right: 10px"><i class="fa fa-edit"></i> 编辑</button>
<button style="outline: none;" type="button" data-id="${value}" class="btn btn-danger btn-sm navigation-delete-btn"><i class="fa fa-trash"></i> 删除</button>`
}
}
]

@ -0,0 +1,23 @@
package cc.bnblogs.test;
import cc.bnblogs.Application;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.util.DigestUtils;
import java.nio.charset.StandardCharsets;
/**
* @author zfp@bnblogs.cc
* @createTime: 2022/10/23
*/
@RunWith(SpringRunner.class)
@SpringBootTest(classes = {Application.class}, webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
public class TestMd5Password {
@Test
public void testMd5() {
System.out.println(DigestUtils.md5DigestAsHex("admin123".getBytes(StandardCharsets.UTF_8)));
}
}
Loading…
Cancel
Save