续篇一 ----->基于Springboot搭建个人博客 (学习笔记)

对与首页还没有完成的部分内容填充

修改layout 游客部分

由于当前的是没有判断是不是登录后的状态 我们可以获取当前的登录任务,当没有登陆的时候默认是游客

   <script>
        // layui.cache.page = '';
        layui.cache.user = {
            username: '游客'
            , uid: -1
            , avatar: '/res/images/avatar/00.jpg'
            , experience: 83
            , sex: '男'
        };
        layui.config({
            version: "3.0.0"
            , base: '/res/mods/' //这里实际使用时,建议改成绝对路径
        }).extend({
            fly: 'index'
        }).use('fly');
    </script>

修改AccountRealm

@Component
public class AccountRealm extends AuthorizingRealm {
    @Autowired
    IMUserService userService;

    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
        return null;
    }

    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
        UsernamePasswordToken usernamePasswordToken = (UsernamePasswordToken) token;
        // 调用登录的逻辑
        AccountProfile profile = userService.login(usernamePasswordToken.getUsername(), String.valueOf(usernamePasswordToken.getPassword()));
        SecurityUtils.getSubject().getSession().setAttribute("profile",profile);
        SimpleAuthenticationInfo info = new SimpleAuthenticationInfo(profile, token.getCredentials(), getName());
        return info;
    }
}

其次就是当我们每次修改的时候都需要更新一下在UserController 里面把所有需要的更新的地方都加上

 SecurityUtils.getSubject().getSession().setAttribute("profile",profile);

在这里插入图片描述
前端修改后

<script>
        // layui.cache.page = '';
        // layui.cache.page = '';
        layui.cache.user = {
            username: '${profile.username!'游客'}'
            ,uid: ${profile.id!"-1"}
            ,avatar: '${profile.avatar!'/res/images/avatar/00.jpg'}'
            ,experience: 83
            ,sex: '${profile.sex!''}'
        };
        layui.config({
            version: "3.0.0"
            , base: '/res/mods/' //这里实际使用时,建议改成绝对路径
        }).extend({
            fly: 'index'
        }).use('fly');
    </script>

添加未读消息的功能

编写Controller


    // 设置 登录之后的未读的消息
 // 基本设置消息
    @GetMapping("/user/mess")
    public String Mess1() {
        IPage<UserMessageVo> page = userMessageService.pageing(getPage(), new QueryWrapper<MUserMessage>()
                .eq("to_user_id", getProfilrId())
                .orderByAsc("created")
        );
        // 把消息改成已读状态
        ArrayList<Long> ids = new ArrayList<>();
        for (UserMessageVo messageVo : page.getRecords()) {
            if (messageVo.getStatus() == 0) {
                ids.add(messageVo.getId());
            }
        }
        // 批量修改成已读
        userMessageService.updateToReaded(ids);
        request.setAttribute("pageData", page);

        return "/user/mess";
    }

这个路径是在layui 后台封装好的
在这里插入图片描述
这个时候效果就会提示我们有三条消息没有读
在这里插入图片描述
当我们点击这个我的消息的时候这3条消息就会消失
在这里插入图片描述

收藏功能

在这里插入图片描述
后台的代码


    // 点击详情 判断有没有收藏
    @PostMapping("/collection/find")
    @ResponseBody
    public Result collectionFind(Long pid) {
        int count = userCollectionService.count(new QueryWrapper<MUserCollection>()
                .eq("user_id", getProfilrId())
                .eq("post_id", pid)
        );
        return Result.success(MapUtil.of("collection", count > 0 ));
    }
    // 点击详情 收藏
    @PostMapping("/collection/add")
    @ResponseBody
    public Result collectionAdd(Long pid) {
        MPost post = postService.getById(pid);
        Assert.isTrue(post != null,"已经删除");
        int count = userCollectionService.count(new QueryWrapper<MUserCollection>()
                .eq("user_id", getProfilrId())
                .eq("post_id", pid)
        );
        if (count > 0) {
            return Result.fail("文章已经被收藏!");
        }
        MUserCollection collection = new MUserCollection();
        collection.setUserId(getProfilrId());
        collection.setPostId(pid);
        collection.setCreated(new Date());
        collection.setModified(new Date());
        collection.setPostUserId(post.getUserId());
        userCollectionService.save(collection);
        return Result.success();
    }
    // 取消收藏
    @PostMapping("/collection/remove/")
    @ResponseBody
    public Result collectionRemove (Long pid) {
        MPost post = postService.getById(pid);
        Assert.isTrue(post != null, "改帖子已被删除");

        userCollectionService.remove(new QueryWrapper<MUserCollection>()
                .eq("user_id", getProfilrId())
                .eq("post_id", pid));

        return Result.success();
    }

前端 js
在这里插入图片描述

发表新帖

当我们在首页会看到有发表新帖的按钮
在这里插入图片描述

先找到我们的这个按钮,设置跳转的路径
在这里插入图片描述
我们在来编写对应页面跳转的Controller类

    // 发布帖子 跳转的页面的Controller。
    @GetMapping("/post/add")
    public String add() {
        // 获取到 Category 用来 写 专栏的位置
        request.setAttribute("categories", imCategoryService.list());
        return "/post/add";
    }

我们对应的前端页面

       <form action="/post/submit" method="post">
                            <div class="layui-row layui-col-space15 layui-form-item">
                                <div class="layui-col-md3">
                                    <label class="layui-form-label">所在专栏</label>
                                    <div class="layui-input-block">
                                        <select lay-verify="required" name="categoryId" lay-filter="column">
                                            <option></option>
                                            <#list categories as c>
                                                <option value="${c.id}">${c.name}</option>
                                            </#list>
                                        </select>
                                    </div>
                                </div>
                                <div class="layui-col-md9">
                                    <label for="L_title" class="layui-form-label">标题</label>
                                    <div class="layui-input-block">
                                        <input type="text" id="L_title" name="title" required lay-verify="required"
                                               autocomplete="off" class="layui-input">
                                    </div>
                                </div>
                            </div>
                            <div class="layui-form-item layui-form-text">
                                <div class="layui-input-block">
                                    <textarea id="L_content" name="content" required lay-verify="required"
                                              placeholder="详细描述" class="layui-textarea fly-editor"
                                              style="height: 260px;"></textarea>
                                </div>
                            </div>
                            <#--                            验证码部分-->
                            <div class="layui-form-item">
                                <label for="L_vercode" class="layui-form-label">人类验证</label>
                                <div class="layui-input-inline">
                                    <input type="text" id="L_vercode" name="vercode" required lay-verify="required" 			placeholder="请回答后面的问题" autocomplete="off" class="layui-input">
                                </div>
                                <div >
                                    <img src="/captcha.jpg" id="captch"></img>
                                </div>
                            </div>
                            <div class="layui-form-item">
                                <button class="layui-btn" lay-filter="*" lay-submit>立即发布</button>
                            </div>
                        </form>

此时我们点击发表新帖的按钮就会跳转到这个对应的页面
编写对应的发表新帖的方法,我写在了和更新一样的方法里面。

  // 提交 发布的文章
    @ResponseBody
    @PostMapping("/post/submit")
    public Result submit(MPost post, String vercode) {
        // 判断检验实体类
        ValidationUtil.ValidResult validResult = ValidationUtil.validateBean(post);
        if (validResult.hasErrors()) {
            return Result.fail(validResult.getErrors());
        }
        // 判断当前的 id 是不是空 是空的话就是 发布新贴
        if (post.getId() == null) {
            post.setUserId(getProfilrId());
            post.setModified(new Date());
            post.setCreated(new Date());
            post.setCommentCount(0);
            post.setEditMode(null);
            post.setLevel(0);
            post.setRecommend(false);
            post.setViewCount(0);
            post.setVoteDown(0);
            post.setVoteUp(0);
            // 获取到session 会话属性
            String captcha = (String) request.getSession().getAttribute(Consts.KAPTCHA_SESSION);
            // 判断验证码是否
            if (vercode == null || !vercode.equalsIgnoreCase(captcha)) {
                return Result.fail("验证码不一致");
            }
            postService.save(post);
        } else {
            // 通过 id 获取到实体类
            MPost mPost = postService.getById(post.getId());
            // 断言
            Assert.isTrue(mPost.getUserId().longValue() == getProfilrId().longValue(), "无权限编辑此文章!");
            // 更新
            mPost.setTitle(post.getTitle());
            mPost.setContent(post.getContent());
            mPost.setCategoryId(post.getCategoryId());
            // 更新的方法
            postService.updateById(mPost);
        }
        return Result.success().action("/detail/" + post.getId());
    }

上面的Controller 类里也有编辑帖子的代码一会我就不复制编辑帖子的Controller了

编辑帖子

当我们点击每一个帖子的话会出现编辑贴得按钮
在这里插入图片描述
设置全局的异常处理类

/**
 * 全局异常处理
 */
@Slf4j
@ControllerAdvice
public class GlobalExcepitonHandler {

    @ExceptionHandler(value = Exception.class)
    public ModelAndView handler(HttpServletRequest req, HttpServletResponse resp, Exception e) throws IOException {

        // ajax 处理
        String header = req.getHeader("X-Requested-With");
        if(header != null  && "XMLHttpRequest".equals(header)) {
            resp.setContentType("application/json;charset=UTF-8");
            resp.getWriter().print(JSONUtil.toJsonStr(Result.fail(e.getMessage())));
            return null;
        }

        if(e instanceof NullPointerException) {
            // ...
        }

        // web处理
        ModelAndView modelAndView = new ModelAndView("tips");
        modelAndView.addObject("message", e.getMessage());
        return modelAndView;
    }

}

添加我们如果出现异常的页面


<#include "inc/layout.ftl" />

<@layout "错误的提示">

	<div class="layui-container fly-marginTop">
		<div class="fly-panel">
			<div class="fly-none">
				<h2><i class="iconfont icon-tishilian"></i></h2>
				<p>${message!}</p>

			</div>
			<a href="/"> 点击这里返回首页!</a>
		</div>
	</div>
	<script>
		layui.cache.page = 'jie';
	</script>
</@layout>


此时如果我们登录的用户不是他自己写的博客的话是不可以进行编辑帖子的
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
解决这个问题之后我们就可以开始写编辑帖子的功能了
先写跳转的路径
在这里插入图片描述
在编写页面跳转的Contoller


    // 编辑帖子 这里其实应该是 把编辑和 添加写到一个页面里面可是 我不太会使用 freemarker 的模块 所以只能分开了。功能 实现了就可以

    @GetMapping("/post/edit")
    public String edit() {
        String id = request.getParameter("id");
        if (!StringUtils.isEmpty(id)) {
            MPost post = postService.getById(id);
            Assert.isTrue(post != null, "改帖子已被删除");
            Assert.isTrue(post.getUserId().longValue() == getProfilrId().longValue(), "没权限操作此文章");
            request.setAttribute("post", post);
        }
        request.setAttribute("categories", imCategoryService.list());
        return "/post/edit";
    }

还有一个Controller 跟上面的那个一样。因为公用了一个提交的方法,在上面的方法里面我做了一个 if 判断。
前端的页面

<#include "../inc/layout.ftl" />

<@layout "添加或编辑博客">
  <div class="layui-container fly-marginTop">
    <div class="fly-panel" pad20 style="padding-top: 5px;">
      <div class="layui-form layui-form-pane">
        <div class="layui-tab layui-tab-brief" lay-filter="user">
          <ul class="layui-tab-title">
            <li class="layui-this">编辑帖子</li>
          </ul>
          <div class="layui-form layui-tab-content" id="LAY_ucm" style="padding: 20px 0;">
            <div class="layui-tab-item layui-show">
              <form action="/post/submit" method="post">
                <div class="layui-row layui-col-space15 layui-form-item">
                  <div class="layui-col-md3">
                    <label class="layui-form-label">所在专栏</label>
                    <div class="layui-input-block">
                      <select lay-verify="required" name="categoryId" lay-filter="column">
                        <option></option>
                        <#list categories as c>
                          <option value="${c.id}" <#if c.id == post.categoryId>selected</#if> >${c.name}</option>
                        </#list>
                      </select>
                    </div>
                  </div>
                  <div class="layui-col-md9">
                    <label for="L_title" class="layui-form-label">标题</label>
                    <div class="layui-input-block">
                      <input type="text" id="L_title" name="title" value="${post.title}" required lay-verify="required" autocomplete="off" class="layui-input">
                      <input type="hidden" name="id" value="${post.id}">
                    </div>
                  </div>
                </div>
                <div class="layui-form-item layui-form-text">
                  <div class="layui-input-block">
                    <textarea id="L_content" name="content" required lay-verify="required" placeholder="详细描述" class="layui-textarea fly-editor" style="height: 260px;">${post.content}</textarea>
                  </div>
                </div>
                <div class="layui-form-item">
                  <button class="layui-btn" lay-filter="*" lay-submit alert="true">立即发布</button>
                </div>
              </form>
            </div>
          </div>
        </div>
      </div>
    </div>
  </div>

  <script>
    layui.cache.page = 'jie';
  </script>
</@layout>

删除帖子

删除帖子 我们需要判断是不会死单前用户 获取是不会死管理员 如果不是的话 是没有权利删除的。
这个时候我们就要去修改我们的前端的页面了。
在这里插入图片描述
再来写对应的Controller 类。

    // 删除帖子
    @ResponseBody
    @Transactional
    @PostMapping("/post/delete")
    public Result del (Long id) {
        MPost post = postService.getById(id);
        // 判断有没有被删除
        Assert.notNull(post,"该帖子已经被删除");
        Assert.isTrue(post.getUserId().longValue() == getProfilrId().longValue(),"没有权利");
        postService.removeById(id);
        // 删除相关的
        userMessageService.removeByMap(MapUtil.of("post_id",id));
        userCollectionService.removeByMap(MapUtil.of("post_id",id));
        return Result.success().action("/");
    }

这样就可以做到删除帖子了。比较简单。

处理管理员的问题

像上面的帖子我们管理员也是可以进行删除和操作要不要置顶的
layui 帮我们封装好了JS 了
在这里插入图片描述
修改前端的样式
在这里插入图片描述
再来编写我们的AdminController 类


@RequestMapping("/admin")
@Controller
public class AdminController extends BaseController {
    // 取消收藏 删除 取消加精 的 方法
    @ResponseBody
    @PostMapping("/jie-set")
    public Result jetSet(Long id, Integer rank, String field) {
        // 获取到实体类
        MPost post = postService.getById(id);
        Assert.notNull(post,"帖子已经被删除了");
        // 判断是删除 还是 取消收藏 还是加精
        if ("delete".equals(field)) {
            postService.removeById(id);
            // 删除相关的
            userMessageService.removeByMap(MapUtil.of("post_id",id));
            userCollectionService.removeByMap(MapUtil.of("post_id",id));
            return Result.success();
        } else if ("status".equals(field)) {
            // 这里 0 是取消 1是确定
            post.setRecommend(rank == 1);

        } else if ("stick".equals(field)) {
            post.setLevel(rank);
        }
        // 调用 更新的
        postService.updateById(post);
        return Result.success();
    }
}

添加权限,我们权限表 就只能这样查了,在我们数据库中admin的id 是12
在这里插入图片描述
效果
在这里插入图片描述
普通的人是没有这几个按钮的 只有删除 但是只有自己的文章才有这个删除的按钮
在这里插入图片描述

处理评论区的问题

点击一个贴子的时候下面的评论功能

在这里插入图片描述
后端的代码

 // 评论帖子
    @ResponseBody
    @Transactional
    @PostMapping("/jie/reply/")
    public Result reply(Long jid, String content) {
        Assert.notNull(jid, "找不到对应的文章");
        Assert.notNull(content, "评论内容不能为空");

        MPost post = postService.getById(jid);
        Assert.isTrue(post != null, "该文章已被删除");

        MComment comment = new MComment();
        comment.setPostId(jid);
        comment.setContent(content);
        comment.setUserId(getProfilrId());
        comment.setCreated(new Date());
        comment.setModified(new Date());
        comment.setLevel(0);
        comment.setVoteDown(0);
        comment.setVoteUp(0);
        imCommentService.save(comment);

        // 评论数量加一
        post.setCommentCount(post.getCommentCount() + 1);
        postService.updateById(post);

        // 本周热议数量加一
        postService.incrCommentCountAndUnionForWeekRank(post, true);
        // 通知作者,有人评论了你的文章
        // 作者自己评论自己文章,不需要通知
        if(comment.getUserId() != post.getUserId()) {
            MUserMessage message = new MUserMessage();
            message.setPostId(jid);
            message.setCommentId(comment.getId());
            message.setFromUserId(getProfilrId());
            message.setToUserId(post.getUserId());
            message.setType(1);
            message.setContent(content);
            message.setCreated(new Date());
            message.setStatus(0);
            userMessageService.save(message);
            // 即时通知作者(websocket)
            wsService.sendMessCountToUser(message.getToUserId());
        }

        // 通知被@的人,有人回复了你的文章
        if(content.startsWith("@")) {
            String username = content.substring(1, content.indexOf(" "));
            System.out.println(username);

            MUser user = userService.getOne(new QueryWrapper<MUser>().eq("username", username));
            if (user != null) {
                MUserMessage message = new MUserMessage();

                message.setPostId(jid);
                message.setCommentId(comment.getId());
                message.setFromUserId(getProfilrId());
                message.setToUserId(user.getId());
                message.setType(2);
                message.setContent(content);
                message.setCreated(new Date());
                message.setStatus(0);
                userMessageService.save(message);

                // 即时通知被@的用户
            }
        }
        return Result.success().action("/detail/" + post.getId());
    }

这里涉及到一个即使通讯的问题 就是当我评论了对方 对方可以即使收到消息
导入websocket jar

     <!--websocket-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-websocket</artifactId>
        </dependency>

配置websocket 的Config

@EnableAsync
@Configuration
@EnableWebSocketMessageBroker // 表示开启使用STOMP协议来传输基于代理的消息
public class WsConfig implements WebSocketMessageBrokerConfigurer {

    @Override
    public void registerStompEndpoints(StompEndpointRegistry registry) {
        registry.addEndpoint("/websocket") // 注册一个端点,websocket的访问地址
                .withSockJS(); //
    }

    @Override
    public void configureMessageBroker(MessageBrokerRegistry registry) {
        registry.enableSimpleBroker("/user/", "/topic/"); //推送消息前缀
        registry.setApplicationDestinationPrefixes("/app");
    }
}

websocket 的实现类

package com.example.springbootblog.service;

public interface WsService {
    void sendMessCountToUser(Long toUserId);
}

@Service
public class WsServiceImpl implements WsService {

    @Autowired
    IMUserMessageService messageService;

    @Autowired
    SimpMessagingTemplate messagingTemplate;

    @Async
    @Override
    public void sendMessCountToUser(Long toUserId) {
        int count = messageService.count(new QueryWrapper<MUserMessage>()
                .eq("to_user_id", toUserId)
                .eq("status", "0")
        );

        // websocket通知 (/user/20/messCount)
        messagingTemplate.convertAndSendToUser(toUserId.toString(), "/messCount", count);
    }
}

前端通知后端

    <script>
        function showTips(count) {
            var msg = $('<a class="fly-nav-msg" href="javascript:;">'+ count +'</a>');
            var elemUser = $('.fly-nav-user');
            elemUser.append(msg);
            msg.on('click', function(){
                location.href = "/user/mess";
            });
            layer.tips('你有 '+ count +' 条未读消息', msg, {
                tips: 3
                ,tipsMore: true
                ,fixed: true
            });
            msg.on('mouseenter', function(){
                layer.closeAll('tips');
            })
        }
        $(function () {
            var elemUser = $('.fly-nav-user');
            if(layui.cache.user.uid !== -1 && elemUser[0]){
                var socket = new SockJS("/websocket")
                stompClient = Stomp.over(socket);
                stompClient.connect({}, function (frame) {
                // 监听这个方法 当访问这个路径的时候 就 提示消息
                    stompClient.subscribe("/user/" + ${profile.id} + "/messCount", function (res) {

                        console.log(res);

                        // 弹窗
                        showTips(res.body);
                    })
                });

            }
        });
    </script>

修改的layout.ftl

删除评论

前端代码
在这里插入图片描述
后端代码

    @ResponseBody
    @Transactional
    @PostMapping("/post/jieda-delete")
    public Result reply (Long id) {
        Assert.notNull(id,"评论不能为空");
        MComment comment = imCommentService.getById(id);
        Assert.notNull(comment,"评论不能为空");
        // 判断是不是自己的评论
        if (comment.getUserId().longValue() == getProfilrId().longValue()) {
            return Result.fail("不是你发表的评论");
        }
        // 删除评论
        imCommentService.removeById(id);
        // 评论数量减一
        MPost post = postService.getById(id);
        post.setCommentCount(post.getCommentCount() - 1);
        postService.saveOrUpdate(post);
        // 评论数量减一
        postService.incrCommentCountAndUnionForWeekRank(post,false);
        return Result.success(null);
    }

就可以实现删除评论的功能了

  • 2
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值