牛客网项目---6.3.置顶、加精 、删除帖子

功能实现

---点击“置顶”、“加精”、“删除”,修改帖子的状态

权限管理

---版主可以执行“置顶”、“加精”操作。管理员可以执行“删除”操作。

---在SecurityConfig类下配置,置顶、加精、删除的访问权限

按钮显示

---版主可以看到“置顶”、“加精”按钮。管理员可以看到“删除“按钮。

---导包:thymeleaf-extras-springsecurity5,thymeleaf对security的支持

<html lang="en" xmlns:th="http://thymeleaf.org" xmlns:sec="http://www.thymeleaf.org/extras/spring-security">

1.导入依赖: thymeleaf-springsecurity5

        <!--Thymeleaf SpringSecurity-->
        <dependency>
            <groupId>org.thymeleaf.extras</groupId>
            <artifactId>thymeleaf-extras-springsecurity5</artifactId>
        </dependency>

2.点击 置顶、加精、删除来修改帖子的状态

2.1 DiscussPostMapper

import com.nowcoder.community.entity.DiscussPost;
import org.apache.ibatis.annotations.Param;
import org.springframework.stereotype.Repository;

import java.util.List;

@Repository
public interface DiscussPostMapper {
    List<DiscussPost> selectDiscussPosts(int userId,int offset,int limit);

    //@Param注解用于给参数取别名
    //如果只有一个参数,并且在<if>里使用,则必须加别名
    int selectDiscussPostRows(@Param("userId") int userId);

    int insertDiscussPost(DiscussPost discussPost);

    DiscussPost selectDiscussPostById(int id);

    int updateCommentCount(int id,int commentCount);

    int updateType(int id,int type);

    int updateStatus(int id,int status);
}

2..2 DiscussPostMapper.xml

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.nowcoder.community.mapper.DiscussPostMapper">

    <select id="selectDiscussPosts" resultType="DiscussPost">
        select * from discuss_post
        where status!=2
        <if test="userId!=0">
            and user_id=#{userId}
        </if>
        order by type desc,create_time desc
        limit #{offset},#{limit};
    </select>

    <select id="selectDiscussPostRows" resultType="int">
        select count(id) from discuss_post
        where status!=2
        <if test="userId!=0">
            and user_id=#{userId}
        </if>
    </select>

    <insert id="insertDiscussPost" parameterType="DiscussPost" keyProperty="id">
        insert into discuss_post(user_id, title, content, type, status, create_time, comment_count, score) VALUES (#{userId},#{title},#{content},#{type},#{status},#{createTime},#{commentCount},#{score});
    </insert>

    <select id="selectDiscussPostById" resultType="DiscussPost">
        select * from discuss_post where id=#{id};
    </select>

    <update id="updateCommentCount">
        update discuss_post set comment_count=#{commentCount} where id=#{id};
    </update>

    <update id="updateType">
        update discuss_post set type=#{type} where id=#{id}
    </update>

    <update id="updateStatus">
        update discuss_post set status=#{status} where id=#{id}
    </update>
</mapper>

2.3 将修改帖子状态的方法添加到DiscussPostService

import com.nowcoder.community.entity.DiscussPost;
import com.nowcoder.community.mapper.DiscussPostMapper;
import com.nowcoder.community.util.SensitiveFilter;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.web.util.HtmlUtils;

import java.util.List;

@Service
public class DiscussPostServiceImpl implements DiscussPostService{
    @Autowired
    private DiscussPostMapper discussPostMapper;

    @Autowired
    private SensitiveFilter sensitiveFilter;

    @Override
    public List<DiscussPost> selectDiscussPosts(int userId, int offset, int limit) {
        return discussPostMapper.selectDiscussPosts(userId, offset, limit);
    }

    @Override
    public int selectDiscussPostRows(int userId) {
        return discussPostMapper.selectDiscussPostRows(userId);
    }

    @Override
    public int insertDiscussPost(DiscussPost discussPost) {
        return discussPostMapper.insertDiscussPost(discussPost);
    }

    @Override
    public DiscussPost selectDiscussPostById(int id) {
        return discussPostMapper.selectDiscussPostById(id);
    }

    @Override
    public int updateCommentCount(int id, int commentCount) {
        return discussPostMapper.updateCommentCount(id, commentCount);
    }

    @Override
    public int updateType(int id, int type) {
        return discussPostMapper.updateType(id, type);
    }

    @Override
    public int updateStatus(int id, int status) {
        return discussPostMapper.updateStatus(id, status);
    }

    public int addDiscussPost(DiscussPost discussPost){
        if (discussPost==null){
            throw new IllegalArgumentException("参数不能为空!");
        }

        //转移HTML标记
        discussPost.setTitle(HtmlUtils.htmlEscape(discussPost.getTitle()));
        discussPost.setContent(HtmlUtils.htmlEscape(discussPost.getContent()));

        //过滤敏感词
        discussPost.setTitle(sensitiveFilter.filter(discussPost.getTitle()));
        discussPost.setContent(sensitiveFilter.filter(discussPost.getContent()));

        return discussPostMapper.insertDiscussPost(discussPost);
    }
}

2.4 DiscussPostController

import com.nowcoder.community.entity.*;
import com.nowcoder.community.event.EventProducer;
import com.nowcoder.community.service.CommentServiceImpl;
import com.nowcoder.community.service.DiscussPostServiceImpl;
import com.nowcoder.community.service.LikeService;
import com.nowcoder.community.service.UserServiceImpl;
import com.nowcoder.community.util.CommunityConstant;
import com.nowcoder.community.util.CommunityUtil;
import com.nowcoder.community.util.HostHolder;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.*;

import java.util.*;

@Controller
@RequestMapping("/discuss")
public class DiscussPostController implements CommunityConstant {
    @Autowired
    private DiscussPostServiceImpl discussPostService;

    @Autowired
    private HostHolder hostHolder;

    @Autowired
    private UserServiceImpl userService;

    @Autowired
    private CommentServiceImpl commentService;

    @Autowired
    private LikeService likeService;

    @Autowired
    private EventProducer eventProducer;

    @PostMapping("/add")
    @ResponseBody
    public String addDiscussPost(String title,String content){
        User user = hostHolder.getUser();
        if (user==null){
            return CommunityUtil.getJSONString(403,"你还没有登陆");
        }

        DiscussPost discussPost = new DiscussPost();
        discussPost.setUserId(user.getId());
        discussPost.setTitle(title);
        discussPost.setContent(content);
        discussPost.setCreateTime(new Date());

        discussPostService.addDiscussPost(discussPost);

        //触发发帖事件
        Event event = new Event()
                .setTopic(TOPIC_PUBLISH)
                .setEntityType(ENTITY_TYPE_POST)
                .setEntityId(discussPost.getId())
                .setUserId(user.getId());
        eventProducer.fireEvent(event);

        return CommunityUtil.getJSONString(0,"发布成功");
    }

    @GetMapping("/detail/{discussPostId}")
    public String getDiscussPost(@PathVariable("discussPostId") int discussPostId, Model model, Page page){
        //帖子
        DiscussPost post = discussPostService.selectDiscussPostById(discussPostId);
        model.addAttribute("post",post);

        //作者
        User user = userService.selectById(post.getUserId());
        model.addAttribute("user",user);

        //点赞数量
        long likeCount = likeService.selectEntityLikeCount(CommunityConstant.ENTITY_TYPE_POST,discussPostId);
        model.addAttribute("likeCount",likeCount);
        //点赞状态
        int likeStatus=hostHolder.getUser()==null ? 0 : likeService.selectEntityLikeStatus(hostHolder.getUser().getId(),CommunityConstant.ENTITY_TYPE_POST,discussPostId);
        model.addAttribute("likeStatus",likeStatus);

        //评论分页信息
        page.setPath("/discuss/detail/"+discussPostId);
        page.setLimit(5);
        page.setRows(post.getCommentCount());

        //评论:给帖子的评论
        //回复:给评论的评论

        //评论列表
        List<Comment> commentList = commentService.selectCommentByEntity(ENTITY_TYPE_POST, post.getId(), page.getOffset(), page.getLimit());
        //评论VO列表
        List<Map<String,Object>> commentVoList=new ArrayList<>();
        if (commentList!=null){
            for (Comment comment : commentList) {
                //评论VO
                Map<String,Object> commentVo=new HashMap<>();
                //评论
                commentVo.put("comment",comment);
                //作者
                commentVo.put("user",userService.selectById(comment.getUserId()));

                //点赞数量
                likeCount = likeService.selectEntityLikeCount(CommunityConstant.ENTITY_TYPE_COMMENT,comment.getId());
                //点赞状态
                likeStatus=hostHolder.getUser()==null ? 0 : likeService.selectEntityLikeStatus(hostHolder.getUser().getId(),CommunityConstant.ENTITY_TYPE_COMMENT,comment.getId());

                commentVo.put("likeCount",likeCount);
                commentVo.put("likeStatus",likeStatus);

                //回复列表
                List<Comment> replayList = commentService.selectCommentByEntity(ENTITY_TYPE_COMMENT, comment.getId(), 0, Integer.MAX_VALUE);
                //回复VO列表
                List<Map<String,Object>> replayVoList=new ArrayList<>();
                if (replayList!=null){
                    for (Comment replay : replayList) {
                        Map<String,Object> replayVo=new HashMap<>();
                        //回复
                        replayVo.put("replay",replay);
                        //作者
                        replayVo.put("user",userService.selectById(replay.getUserId()));
                        //回复目标
                        User target=replay.getTargetId()==0?null:userService.selectById(replay.getTargetId());
                        replayVo.put("target",target);

                        //点赞数量
                        likeCount = likeService.selectEntityLikeCount(CommunityConstant.ENTITY_TYPE_COMMENT,replay.getId());
                        //点赞状态
                        likeStatus=hostHolder.getUser()==null ? 0 : likeService.selectEntityLikeStatus(hostHolder.getUser().getId(),CommunityConstant.ENTITY_TYPE_COMMENT,replay.getId());

                        replayVo.put("likeCount",likeCount);
                        replayVo.put("likeStatus",likeStatus);

                        replayVoList.add(replayVo);
                    }
                }
                commentVo.put("replays",replayVoList);

                //回复数量
                int replayCount = commentService.selectCountByEntity(ENTITY_TYPE_COMMENT, comment.getId());
                commentVo.put("replayCount",replayCount);

                commentVoList.add(commentVo);
            }
        }
        model.addAttribute("comments",commentVoList);

        return "/site/discuss-detail";
    }

    //置顶
    @PostMapping("/top")
    @ResponseBody
    public String setTop(int id){
        discussPostService.updateType(id,1);

        //触发发帖事件
        Event event = new Event()
                .setTopic(TOPIC_PUBLISH)
                .setEntityType(ENTITY_TYPE_POST)
                .setEntityId(id)
                .setUserId(hostHolder.getUser().getId());
        eventProducer.fireEvent(event);

        return CommunityUtil.getJSONString(0);
    }

    //加精
    @PostMapping("/wonderful")
    @ResponseBody
    public String setWonderful(int id){
        discussPostService.updateStatus(id,1);

        //触发发帖事件
        Event event = new Event()
                .setTopic(TOPIC_PUBLISH)
                .setEntityType(ENTITY_TYPE_POST)
                .setEntityId(id)
                .setUserId(hostHolder.getUser().getId());
        eventProducer.fireEvent(event);

        return CommunityUtil.getJSONString(0);
    }

    //删除
    @PostMapping("/delete")
    @ResponseBody
    public String setDelete(int id){
        discussPostService.updateStatus(id,2);

        //触发发帖事件
        Event event = new Event()
                .setTopic(TOPIC_DELETE)
                .setEntityType(ENTITY_TYPE_POST)
                .setEntityId(id)
                .setUserId(hostHolder.getUser().getId());
        eventProducer.fireEvent(event);

        return CommunityUtil.getJSONString(0);
    }
}

2.5 在EventConsumer中增加消费删帖事件

import com.alibaba.fastjson.JSONObject;
import com.nowcoder.community.entity.DiscussPost;
import com.nowcoder.community.entity.Event;
import com.nowcoder.community.entity.Message;
import com.nowcoder.community.service.DiscussPostServiceImpl;
import com.nowcoder.community.service.ElasticsearchService;
import com.nowcoder.community.service.MessageServiceImpl;
import com.nowcoder.community.util.CommunityConstant;
import org.apache.kafka.clients.consumer.ConsumerRecord;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.kafka.annotation.KafkaListener;
import org.springframework.stereotype.Component;

import java.util.Date;
import java.util.HashMap;
import java.util.Map;

@Component
public class EventConsumer implements CommunityConstant {

    private static final Logger logger= LoggerFactory.getLogger(EventConsumer.class);

    @Autowired
    private MessageServiceImpl messageService;

    @Autowired
    private DiscussPostServiceImpl discussPostService;

    @Autowired
    private ElasticsearchService elasticsearchService;

    @KafkaListener(topics = {TOPIC_COMMENT,TOPIC_FOLLOW,TOPIC_LIKE})
    public void handleCommentMessage(ConsumerRecord record){
        if (record==null||record.value()==null){
            logger.error("消息的内容为空!");
            return;
        }

        Event event= JSONObject.parseObject(record.value().toString(),Event.class);
        if (event==null){
            logger.error("消息格式错误!");
            return;
        }

        //发送站内通知
        Message message=new Message();
        message.setFromId(SYSTEM_USER_ID);
        message.setToId(event.getEntityUserId());
        message.setConversationId(event.getTopic());
        message.setCreateTime(new Date());

        Map<String,Object> content=new HashMap<>();
        content.put("userId",event.getUserId());
        content.put("entityType",event.getEntityType());
        content.put("entityId",event.getEntityId());

        if (!event.getData().isEmpty()){
            for (Map.Entry<String, Object> entry : event.getData().entrySet()) {
                content.put(entry.getKey(), entry.getValue());
            }
        }

        message.setContent(JSONObject.toJSONString(content));
        messageService.addMessage(message);
    }

    //消费发帖事件
    @KafkaListener(topics = {TOPIC_PUBLISH})
    public void handlePublishMessage(ConsumerRecord record){
        if (record==null||record.value()==null){
            logger.error("消息的内容为空!");
            return;
        }

        Event event=JSONObject.parseObject(record.value().toString(),Event.class);
        if (event==null){
            logger.error("消息格式错误!");
            return;
        }

        DiscussPost post = discussPostService.selectDiscussPostById(event.getEntityId());
        elasticsearchService.saveDiscussPost(post);
    }

    //消费删帖事件
    @KafkaListener(topics = {TOPIC_DELETE})
    public void handleDeleteMessage(ConsumerRecord record){
        if (record==null||record.value()==null){
            logger.error("消息的内容为空!");
            return;
        }

        Event event = JSONObject.parseObject(record.value().toString(), Event.class);
        if (event==null){
            logger.error("消息格式错误!");
            return;
        }

        elasticsearchService.deleteDiscussPost(event.getEntityId());
    }
}

3.1 discuss-detail.html

					<div class="float-right">
						<input type="hidden" id="postId" th:value="${post.id}">
						<button type="button" class="btn btn-danger btn-sm" id="topBtn"
								th:disabled="${post.type==1}" sec:authorize="hasAnyAuthority('moderator')">置顶</button>
						<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>
					</div>

3.2 discuss.js

$(function () {
    $("#topBtn").click(setTop);
    $("#wonderfulBtn").click(setWonderful);
    $("#deleteBtn").click(setDelete);
});

function like(btn, entityType, entityId,entityUserId,postId) {
    $.post(
        CONTEXT_PATH + "/like",   // 路径
        {"entityType":entityType,"entityId":entityId,"entityUserId":entityUserId,"postId":postId}, // 参数
        function(data) { // 处理返回数据
            data = $.parseJSON(data);
            if(data.code == 0) {
                $(btn).children("i").text(data.likeCount);
                $(btn).children("b").text(data.likeStatus==1?'已赞':"赞");
            } else {
                alert(data.msg);
            }
        }
    );
}

//置顶
function setTop() {
    $.post(
        CONTEXT_PATH+"/discuss/top",
        {"id":$("#postId").val()},
        function (data) {
            data=$.parseJSON(data);
            if (data.code==0){
                $("#topBtn").attr("disabled","disabled");
            }else {
                alert(data.msg);
            }
        }
    );
}

//加精
function setWonderful() {
    $.post(
        CONTEXT_PATH+"/discuss/wonderful",
        {"id":$("#postId").val()},
        function (data) {
            data=$.parseJSON(data);
            if (data.code==0){
                $("#wonderfulBtn").attr("disabled","disabled");
            }else {
                alert(data.msg);
            }
        }
    );
}

//删除
function setDelete() {
    $.post(
        CONTEXT_PATH+"/discuss/delete",
        {"id":$("#postId").val()},
        function (data) {
            data=$.parseJSON(data);
            if (data.code==0){
                location.href=CONTEXT_PATH+"/index";
            }else {
                alert(data.msg);
            }
        }
    );
}

4.SecurityConfig

import com.nowcoder.community.util.CommunityConstant;
import com.nowcoder.community.util.CommunityUtil;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.builders.WebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.AuthenticationEntryPoint;
import org.springframework.security.web.access.AccessDeniedHandler;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;

@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter implements CommunityConstant {
    @Override
    public void configure(WebSecurity web) throws Exception {
        web.ignoring().antMatchers("/resources/**");
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        //授权
        http.authorizeRequests()
                .antMatchers(
                        "/comment/add/**",
                        "/discuss/add",
                        "/follow",
                        "/unfollow",
                        "/like",
                        "/letter/**",
                        "/notice/**",
                        "/setting",
                        "/upload",
                        "/updatePassword"
                )
                .hasAnyAuthority(
                        AUTHORITY_USER,AUTHORITY_ADMIN,AUTHORITY_MODERATOR
                )
                .antMatchers(
                        "/discuss/top","/discuss/wonderful"
                )
                .hasAnyAuthority(AUTHORITY_MODERATOR)
                .antMatchers("/discuss/delete")
                .hasAnyAuthority(AUTHORITY_ADMIN)
                .anyRequest().permitAll()
                .and().csrf().disable();//禁用csrf
        //权限不够时的处理
        http.exceptionHandling()
                .authenticationEntryPoint(new AuthenticationEntryPoint() {
                    //没有登录
                    @Override
                    public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException e) throws IOException, ServletException {
                        String xRequestedWith = request.getHeader("x-requested-with");
                        if ("XMLHttpRequest".equals(xRequestedWith)){
                            response.setContentType("application/plain;charset=utf-8");
                            PrintWriter writer = response.getWriter();
                            writer.write(CommunityUtil.getJSONString(403,"您还没有登录!"));
                        }else {
                            response.sendRedirect(request.getContextPath()+"/login");
                        }
                    }
                })
                .accessDeniedHandler(new AccessDeniedHandler() {
                    //权限不足
                    @Override
                    public void handle(HttpServletRequest request, HttpServletResponse response, AccessDeniedException e) throws IOException, ServletException {
                        String xRequestedWith = request.getHeader("x-requested-with");
                        if ("XMLHttpRequest".equals(xRequestedWith)){
                            response.setContentType("application/plain;charset=utf-8");
                            PrintWriter writer = response.getWriter();
                            writer.write(CommunityUtil.getJSONString(403,"您没有访问此功能的权限!"));
                        }else {
                            response.sendRedirect(request.getContextPath()+"/denied");
                        }
                    }
                });

        //Security底层默认会拦截/logout请求,进行退出处理
        //覆盖它默认的逻辑,才能执行我们自己的退出代码
        http.logout().logoutUrl("/securitylogout");//此时,Security会拦截securitylogout请求
    }
}

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值