增加删除文章恢复功能,增加部分常见问题解答

This commit is contained in:
Novocaine 2021-09-02 13:26:07 +08:00
parent 2a29da6e82
commit cbbc633697
10 changed files with 210 additions and 5 deletions

14
docs/Questions.md Normal file
View File

@ -0,0 +1,14 @@
# 常见问题
## Elasticsearch相关
#### 1. none of the configured nodes are available
通常为es的配置问题请检查配置文件中es相关的配置设置是否和本地搭建的环境一致。
```yaml
# es
# 可以通过es的config目录下的配置文件 elasticsearch.yml 获取自己集群配置的cluster-name
spring.data.elasticsearch.cluster-name = docker-cluster
# 服务器的ip、端口
spring.data.elasticsearch.cluster-nodes = 127.0.0.1:9300
```

View File

@ -12,7 +12,11 @@ import com.greate.community.util.HostHolder;
import com.greate.community.util.RedisKeyUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.domain.PageImpl;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.security.core.annotation.AuthenticationPrincipal;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
@ -293,5 +297,31 @@ public class DiscussPostController implements CommunityConstant {
return CommunityUtil.getJSONString(0);
}
@PostMapping("/recover")
@ResponseBody
@PreAuthorize(AUTHORITY_ADMIN)
public String recover(int id){
discussPostService.updateStatus(id,0);
Event event = new Event()
.setTopic(TOPIC_PUBLISH)
.setUserId(hostHolder.getUser().getId())
.setEntityType(ENTITY_TYPE_POST)
.setEntityId(id);
eventProducer.fireEvent(event);
return CommunityUtil.getJSONString(200,"恢复成功");
}
@RequestMapping("/onDelete")
@PreAuthorize(AUTHORITY_ADMIN)
public String onDelete(Model model,
@RequestParam(value = "pageIndex", defaultValue = "0") int pageIndex,
@RequestParam(value = "pageSize", defaultValue = "10")int size){
Pageable page = PageRequest.of(pageIndex,size);
PageImpl<DiscussPost> posts = discussPostService.onDelete(page);
model.addAttribute("posts",posts);
return "site/admin/onDelete";
}
}

View File

@ -22,10 +22,11 @@ public class ExceptionAdvice {
@ExceptionHandler({Exception.class})
public void handleException(Exception e, HttpServletRequest request, HttpServletResponse response) throws IOException {
logger.error("服务器发生异常:" + e.getMessage());
for (StackTraceElement element : e.getStackTrace()) {
logger.error(element.toString());
}
// logger.error("服务器发生异常:" + e.getMessage());
logger.error(e.getMessage(), e);
// for (StackTraceElement element : e.getStackTrace()) {
// logger.error(element.toString());
// }
// 区分异步请求和普通请求
String xRequestedWith = request.getHeader("x-requested-with");
if ("XMLHttpRequest".equals(xRequestedWith)) {

View File

@ -74,4 +74,14 @@ public interface DiscussPostMapper {
* @return
*/
int updateScore(int id, double score);
/**
* 查询已经删除的帖子
* @param offset
* @param limit
* @return
*/
List<DiscussPost> selectDiscussPostByStatus(long offset, int limit);
int countDiscussPostByStatus();
}

View File

@ -5,6 +5,7 @@ import com.github.benmanes.caffeine.cache.Caffeine;
import com.github.benmanes.caffeine.cache.LoadingCache;
import com.greate.community.dao.DiscussPostMapper;
import com.greate.community.entity.DiscussPost;
import com.greate.community.entity.Page;
import com.greate.community.util.SensitiveFilter;
import org.checkerframework.checker.nullness.qual.NonNull;
import org.checkerframework.checker.nullness.qual.Nullable;
@ -12,6 +13,8 @@ import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.domain.PageImpl;
import org.springframework.data.domain.Pageable;
import org.springframework.stereotype.Service;
import org.springframework.web.util.HtmlUtils;
@ -201,4 +204,15 @@ public class DiscussPostService {
return discussPostMapper.updateScore(id, score);
}
/**
* 查询已经删除的帖子
* @param page
* @return
*/
public PageImpl<DiscussPost> onDelete(Pageable page) {
List<DiscussPost> discussPosts = discussPostMapper.selectDiscussPostByStatus(page.getOffset(),page.getPageSize());
long total = discussPostMapper.countDiscussPostByStatus();
return new PageImpl(discussPosts, page, total);
}
}

View File

@ -53,6 +53,19 @@
where id = #{id}
</select>
<!-- 根据 状态 查询帖子 -->
<select id="selectDiscussPostByStatus" resultType="com.greate.community.entity.DiscussPost">
select * from discuss_post where status = 2
limit #{offset},#{limit}
</select>
<!-- 根据 状态 查询帖子总数 -->
<select id="countDiscussPostByStatus" resultType="java.lang.Integer">
select count(id)
from discuss_post
where status = 2
</select>
<update id="updateCommentCount">
update discuss_post
set comment_count = #{commentCount}

View File

@ -2,6 +2,7 @@ $(function(){
$("#topBtn").click(updateTop);
$("#wonderfulBtn").click(setWonderful);
$("#deleteBtn").click(setDelete);
$("#recoverBtn").click(recover);
});
// 点赞
@ -79,4 +80,21 @@ function setDelete() {
}
}
)
}
function recover() {
$.post(
CONTEXT_PATH + "/discuss/recover",
{"id":$("#postId").val()},
function (data) {
data = $.parseJSON(data);
if (data.code == 200) {
// 删除成功后,跳转到首页
location.href = CONTEXT_PATH + "/index";
}
else {
alert(data.msg);
}
}
)
}

View File

@ -48,6 +48,7 @@
<div class="dropdown-menu" aria-labelledby="navbarDropdown">
<a class="dropdown-item text-center" th:href="@{|/user/profile/${loginUser.id}|}"><i class="bi bi-person-fill"></i> 个人主页</a>
<a class="dropdown-item text-center" th:href="@{/user/setting}"><i class="bi bi-gear"></i> 账号设置</a>
<a class="dropdown-item text-center" th:href="@{/discuss/onDelete}" sec:authorize="hasAnyAuthority('admin')"><i class="bi bi-bag-x"></i> 文章恢复</a>
<a class="dropdown-item text-center" th:href="@{/data}" sec:authorize="hasAnyAuthority('admin')"><i class="bi bi-clipboard-data"></i> 数据统计</a>
<a class="dropdown-item text-center" th:href="@{/logout}"><i class="bi bi-box-arrow-right"></i> 退出登录</a>
<div class="dropdown-divider"></div>

View File

@ -0,0 +1,102 @@
<!doctype html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<link rel="icon" type="shortcut icon" th:href="@{/img/favicon.ico}" />
<link rel="stylesheet" type="text/css" th:href="@{/css/bootstrap.min.css}" />
<link rel="stylesheet" type="text/css" th:href="@{/css/global.css}" />
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.3.0/font/bootstrap-icons.css">
<title>Echo - 文章管理</title>
</head>
<body>
<div class="nk-container">
<!-- 头部 -->
<header class="bg-dark sticky-top" th:replace="index::header"></header>
<!-- 内容 -->
<div class="main">
<div class="container">
<div class="position-relative">
<!-- 筛选条件 -->
<!-- <ul class="nav nav-tabs mb-3">-->
<!-- <li class="nav-item">-->
<!-- <a th:class="|nav-link ${orderMode==0 ? 'active' : ''}|" th:href="@{/index(orderMode=0)}"><i class="bi bi-lightning"></i> 最新</a>-->
<!-- </li>-->
<!-- <li class="nav-item">-->
<!-- <a th:class="|nav-link ${orderMode==1 ? 'active' : ''}|" th:href="@{/index(orderMode=1)}"><i class="bi bi-hand-thumbs-up"></i> 最热</a>-->
<!-- </li>-->
<!-- </ul>-->
<!-- 提示框 -->
<div class="modal fade" id="hintModal" tabindex="-1" role="dialog" aria-labelledby="hintModalLabel" aria-hidden="true">
<div class="modal-dialog modal-lg" role="document">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="hintModalLabel">提示</h5>
</div>
<div class="modal-body" id="hintBody"></div>
</div>
</div>
</div>
<!-- 帖子列表 -->
<ul class="list-unstyled">
<li class="media pb-3 pt-3 mb-3 border-bottom" th:each="map:${posts.getContent()}">
<a th:href="@{|/user/profile/${map.userId}|}">
<!-- <img th:src="${}" class="mr-4 rounded-circle" alt="用户头像" style="width:50px;height:50px;">-->
</a>
<div class="media-body">
<h6 class="mt-0 mb-3">
<a th:href="@{|/discuss/detail/${map.id}|}" th:utext="${map.title}"></a>
<span class="badge badge-secondary bg-danger" th:if="${map.type==1}"
style="font-weight: 500; color: #f85959; background-color: rgba(248,89,89,0.1) !important;">顶</span>
<span class="badge badge-secondary bg-primary" th:if="${map.status==1}"
style="font-weight: 500; color: #3c8cff; background-color: rgba(60,140,255,0.1) !important;">精</span>
</h6>
<div class="text-muted font-size-12">
<u class="mr-3" th:utext="xxx"></u> 发布于 <b th:text="${#dates.format(map.createTime,'yyyy-MM-dd HH:mm:ss')}"></b>
<ul class="d-inline float-right">
<!-- <li class="d-inline ml-2">赞 <span th:text="${map.likeCount}"></span></li>-->
<!-- <li class="d-inline ml-2">|</li>-->
<!-- <li class="d-inline ml-2">回帖 <span th:text="${map.post.commentCount}"></span></li>-->
</ul>
</div>
</div>
</li>
</ul>
<!--分页 -->
<nav class="mt-5" th:if = "${posts.getTotalPages()>0}" th:fragment="pagination">
<ul class="pagination justify-content-center">
<li class="page-item">
<a class="page-link" th:href="@{/discuss/onDelete}">首页</a>
</li>
<li th:class="|page-item ${posts.hasPrevious() ? '' : 'disabled'}|">
<a class="page-link" th:href="@{/discuss/onDelete(pageIndex=${posts.getNumber() - 1})}">上一页</a>
</li>
<!--numbers.sequence 生成一个 page.from 到 page.to 的连续整数数组-->
<li th:each="i:${#numbers.sequence((posts.getTotalPages() < 5 ? 1 : (posts.getTotalPages() - 5)), posts.getTotalPages())}" th:class="|page-item ${(i - 1) ==posts.number ? 'active' : ''}|" >
<a class="page-link" th:href="@{/discuss/onDelete(pageIndex=${i - 1})}" th:text="${i}"></a>
</li>
<li th:class="|page-item ${posts.hasNext() ? '':'disabled'}|">
<a class="page-link" th:href="@{/discuss/onDelete(pageIndex=${posts.getNumber() + 1})}">下一页</a>
</li>
<li class="page-item">
<a class="page-link" th:href="@{/discuss/onDelete(pageIndex=${posts.getTotalPages() - 1})}">末页</a>
</li>
</ul>
</nav>
</div>
</div>
<!-- 尾部 -->
<footer class="bg-dark" th:replace="index::footer"></footer>
</div>
<script th:src="@{/js/jquery-3.1.0.min.js}"></script>
<script th:src="@{/js/popper.min.js}"></script>
<script th:src="@{/js/bootstrap.min.js}"></script>
<script th:src="@{/js/global.js}"></script>
</body>
</html>

View File

@ -36,7 +36,9 @@
<button type="button" class="btn btn-danger btn-sm" id="wonderfulBtn"
th:disabled="${post.status == 1}" sec:authorize="hasAnyAuthority('moderator')">加精</button>
<button type="button" class="btn btn-danger btn-sm" id="deleteBtn"
th:disabled="${post.status == 2}" sec:authorize="hasAnyAuthority('admin')">删除</button>
th:if="${post.status != 2}" sec:authorize="hasAnyAuthority('admin')">删除</button>
<button type="button" class="btn btn-success btn-sm" id="recoverBtn"
th:if="${post.status == 2}" sec:authorize="hasAnyAuthority('admin')">恢复</button>
</div>
</h5>
<!-- 作者 -->