核心框架 Springboot
安全框架 Apache Shiro
持久层框架 Mybatis + mybatis-plus
页面模板 Freemarker
缓存框架 Redis
数据库 mysql
消息队列 RabbitMq
分布式搜索 Elasticsearch
双工通讯协议:websocket
网络通讯框架:t-io
工具集合:hutool
系统:win10 64位专业版
erlang:otp_win64_24.0
rabbitMQ:rabbitmq-server-3.8.19
安装rabbitMQ需要依赖erlang语言环境,所以需要我们下载erlang的环境安装程序。
rabbitMQ安装程序下载路径:https://www.rabbitmq.com/install-windows-manual.html
![在这里插入图片描述](https://img-blog.csdnimg.cn/img_convert/fcfdfb6e7ca013aca108e203e2d2f8b4.png
erlang环境安装程序下载路径: https://www.erlang.org/downloads
对于安装路径没有特殊要求的话,就一路next直至安装成功即可,默认安装路径为:C:\Program Files\erl-23.0。
接下来配置环境变量,常规操作,新建系统变量-键入变量名ERLANG_HOME,键入变量值:erlang安装路径。如下图:
然后添加系统path路径中,添加 : %ERLANG_HOME%\bin
然后打开cmd,输入erl,看到我们的erlang版本号,就说明安装成功了
双击我们刚才下载的rabbitmq-server-3.8.19程序,next,install即可,此处需要注意,如果要自定义安装路径的话,路径中最好不要存在中文,会出现错误。
安装完成之后,需要我们激活rabbitmq_management
打开cmd,进到sbin目录下,运行命令
rabbitmq-plugins enable rabbitmq_management
上面的命令执行成功之后,我们就可以通过http://localhost:15672来访问web端的管理界面
初始可以通过用户名:guest 密码guest来登录
net start RabbitMQ #启动
net stop RabbitMQ #停止
rabbitmqctl status #查看状态
https://www.elastic.co/cn/elasticsearch/ 官网
下载完成后
目录结构
bin :启动文件
config 配置文件
log4j2 :日志文件
jvm.options java 虚拟机相关的配置
elasticsearch.yml elasticsearch 的配置文件
lib 相关jar 包
logs 日志
modules 功能模块
plugins 插件 ik
在bin 里点击启动即可
安装head 插件
在 github 下载 https://github.com/mobz/elasticsearch-head/
简单的前端页面
安装 cnpm install
**此时还不能链接上我们的 es **
连接不上解决一下跨域的问题
在ElasticSearch.yml 加上这两句话
http.cors.enabled: true
http.cors.allow-origin: “*”
这个操作比较简单
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0modelVersion>
<parent>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-parentartifactId>
<version>2.1.2.RELEASEversion>
<relativePath/>
parent>
<groupId>com.examplegroupId>
<artifactId>Springboot-blogartifactId>
<version>0.0.1-SNAPSHOTversion>
<name>Springboot-blogname>
<description>Demo project for Spring Bootdescription>
<properties>
<java.version>1.8java.version>
properties>
<dependencies>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-data-redisartifactId>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-freemarkerartifactId>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-webartifactId>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-devtoolsartifactId>
<scope>runtimescope>
<optional>trueoptional>
dependency>
<dependency>
<groupId>mysqlgroupId>
<artifactId>mysql-connector-javaartifactId>
<scope>runtimescope>
dependency>
<dependency>
<groupId>org.projectlombokgroupId>
<artifactId>lombokartifactId>
<optional>trueoptional>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-testartifactId>
<scope>testscope>
<exclusions>
<exclusion>
<groupId>org.junit.vintagegroupId>
<artifactId>junit-vintage-engineartifactId>
exclusion>
exclusions>
dependency>
<dependency>
<groupId>com.baomidougroupId>
<artifactId>mybatis-plus-boot-starterartifactId>
<version>3.2.0version>
dependency>
<dependency>
<groupId>com.baomidougroupId>
<artifactId>mybatis-plus-generatorartifactId>
<version>3.2.0version>
dependency>
<dependency>
<groupId>p6spygroupId>
<artifactId>p6spyartifactId>
<version>3.8.6version>
dependency>
<dependency>
<groupId>org.apache.commonsgroupId>
<artifactId>commons-lang3artifactId>
<version>3.9version>
dependency>
<dependency>
<groupId>cn.hutoolgroupId>
<artifactId>hutool-allartifactId>
<version>4.1.17version>
dependency>
<dependency>
<groupId>com.github.axetgroupId>
<artifactId>kaptchaartifactId>
<version>0.0.9version>
dependency>
<dependency>
<groupId>org.apache.shirogroupId>
<artifactId>shiro-springartifactId>
<version>1.4.0version>
dependency>
<dependency>
<groupId>net.mingsoftgroupId>
<artifactId>shiro-freemarker-tagsartifactId>
<version>0.1version>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-websocketartifactId>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-data-elasticsearchartifactId>
<version>2.1.1.RELEASEversion>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-amqpartifactId>
dependency>
<dependency>
<groupId>org.modelmappergroupId>
<artifactId>modelmapperartifactId>
<version>1.1.0version>
dependency>
<dependency>
<groupId>org.t-iogroupId>
<artifactId>tio-websocket-serverartifactId>
<version>3.2.5.v20190101-RELEASEversion>
dependency>
dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-maven-pluginartifactId>
plugin>
plugins>
build>
project>
我放在了 资源里面 可以下载到https://download.csdn.net/download/m0_46937429/20398937?spm=1001.2014.3001.5503
大概框架是这个样子.下载在我的资源里面
先把 下载好的 fly 3.0 的首页引入到 templates
controller 层
package com.example.springbootblog.Controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
@Controller
public class IndexController {
@RequestMapping ({"","/","index"})
public String index () {
return "index";
}
}
此时访问 http://localhost:8080/
我们可以把 头信息抽离出来 再用 freemarker 引入就可以了
然后在 index.ftl 里面引入就可以了
同样的道理把 导航的部分跟 尾部抽离出来就可以了
中间部分处理
可以先把右边的部分抽离出来
这个 right.ftl 里面就是 那个 md 4 的 同样的道理 把 左侧的可以 抽离出来
这个时候还不太够完善,有好多的地方可以在所有的地方会用到
**新建一个 layout.ftl **
用这个标签 暂时把 index.ftl 里面的东西 复制到 下面这个标签 里面
但是不能全部复制过来 比如 这几个 就不是公共的。就是首页自己的。可能别的页面就没有的
<#macro layout title>
</macro>
怎么引用我们写的layout.ftl 页面呢?
<#include "./inc/layout.ftl"/>
<#-- 导入 layout 的标签 -->
<@layout "首页" >
<#include "./inc/hrader-panel.ftl"/>
<div class="layui-container">
<div class="layui-row layui-col-space15">
<#include "./inc/left.ftl"/>
<#include "./inc/right.ftl"/>
</div>
</div>
</@layout>
运行访问 ,其实就是拼图 套娃。哈哈哈哈
还可以访问,证明我们的抽离没有问题
添加 页面
把这两个页面复制过来
先写Controller 测试一下能不能用
@Controller
public class PostController {
// 指定值接收 数字类型
@GetMapping("/category/{id:\\d*}")
public String category(@PathVariable(name = "id") Long id) {
return "post/category";
}
@GetMapping("/detail/{id:\\d*}")
public String detail(@PathVariable(name = "id") Long id) {
return "post/detail";
}
}
效果
是没有问题的。但是不能用 Controller 形式 跳转。我们应该用 页面的形式 。接下来修改页面用模板进行跳转
category 页面
<#include "../inc/layout.ftl"/>
<#-- 导入 layout 的标签 -->
<@layout "博客分类" >
<#include "../inc/hrader-panel.ftl"/>
<div class="layui-container">
<div class="layui-row layui-col-space15">
<div class="layui-col-md8">
<div class="fly-panel" style="margin-bottom: 0;">
<div class="fly-panel-title fly-filter">
<a href="" class="layui-this">综合</a>
<span class="fly-mid"></span>
<a href="">未结</a>
<span class="fly-mid"></span>
<a href="">已结</a>
<span class="fly-mid"></span>
<a href="">精华</a>
<span class="fly-filter-right layui-hide-xs">
<a href="" class="layui-this">按最新</a>
<span class="fly-mid"></span>
<a href="">按热议</a>
</span>
</div>
<ul class="fly-list">
<li>
<a href="user/home.html" class="fly-avatar">
<img src="https://tva1.sinaimg.cn/crop.0.0.118.118.180/5db11ff4gw1e77d3nqrv8j203b03cweg.jpg" alt="贤心">
</a>
<h2>
<a class="layui-badge">分享</a>
<a href="detail.html">基于 layui 的极简社区页面模版</a>
</h2>
<div class="fly-list-info">
<a href="user/home.html" link>
<cite>贤心</cite>
<!--
<i class="iconfont icon-renzheng" title="认证信息:XXX"></i>
<i class="layui-badge fly-badge-vip">VIP3</i>
-->
</a>
<span>刚刚</span>
<span class="fly-list-kiss layui-hide-xs" title="悬赏飞吻"><i class="iconfont icon-kiss"></i> 60</span>
<!--<span class="layui-badge fly-badge-accept layui-hide-xs">已结</span>-->
<span class="fly-list-nums">
<i class="iconfont icon-pinglun1" title="回答"></i> 66
</span>
</div>
<div class="fly-list-badge">
<span class="layui-badge layui-bg-black">置顶</span>
<!--<span class="layui-badge layui-bg-red">精帖</span>-->
</div>
</li>
<li>
<a href="user/home.html" class="fly-avatar">
<img src="https://tva1.sinaimg.cn/crop.0.0.118.118.180/5db11ff4gw1e77d3nqrv8j203b03cweg.jpg" alt="贤心">
</a>
<h2>
<a class="layui-badge">动态</a>
<a href="detail.html">基于 layui 的极简社区页面模版</a>
</h2>
<div class="fly-list-info">
<a href="user/home.html" link>
<cite>贤心</cite>
<!--<i class="iconfont icon-renzheng" title="认证信息:XXX"></i>-->
<i class="layui-badge fly-badge-vip">VIP3</i>
</a>
<span>刚刚</span>
<span class="fly-list-kiss layui-hide-xs" title="悬赏飞吻"><i class="iconfont icon-kiss"></i> 60</span>
<span class="layui-badge fly-badge-accept layui-hide-xs">已结</span>
<span class="fly-list-nums">
<i class="iconfont icon-pinglun1" title="回答"></i> 66
</span>
</div>
<div class="fly-list-badge">
<span class="layui-badge layui-bg-red">精帖</span>
</div>
</li>
<li>
<a href="user/home.html" class="fly-avatar">
<img src="https://tva1.sinaimg.cn/crop.0.0.118.118.180/5db11ff4gw1e77d3nqrv8j203b03cweg.jpg" alt="贤心">
</a>
<h2>
<a class="layui-badge">动态</a>
<a href="detail.html">基于 layui 的极简社区页面模版</a>
</h2>
<div class="fly-list-info">
<a href="user/home.html" link>
<cite>贤心</cite>
<!--
<i class="iconfont icon-renzheng" title="认证信息:XXX"></i>
<i class="layui-badge fly-badge-vip">VIP3</i>
-->
</a>
<span>刚刚</span>
<span class="fly-list-kiss layui-hide-xs" title="悬赏飞吻"><i class="iconfont icon-kiss"></i> 60</span>
<!--<span class="layui-badge fly-badge-accept layui-hide-xs">已结</span>-->
<span class="fly-list-nums">
<i class="iconfont icon-pinglun1" title="回答"></i> 66
</span>
</div>
<div class="fly-list-badge">
<!--<span class="layui-badge layui-bg-red">精帖</span>-->
</div>
</li>
<li>
<a href="user/home.html" class="fly-avatar">
<img src="https://tva1.sinaimg.cn/crop.0.0.118.118.180/5db11ff4gw1e77d3nqrv8j203b03cweg.jpg" alt="贤心">
</a>
<h2>
<a class="layui-badge">动态</a>
<a href="detail.html">基于 layui 的极简社区页面模版</a>
</h2>
<div class="fly-list-info">
<a href="user/home.html" link>
<cite>贤心</cite>
<!--
<i class="iconfont icon-renzheng" title="认证信息:XXX"></i>
<i class="layui-badge fly-badge-vip">VIP3</i>
-->
</a>
<span>刚刚</span>
<span class="fly-list-kiss layui-hide-xs" title="悬赏飞吻"><i class="iconfont icon-kiss"></i> 60</span>
<!--<span class="layui-badge fly-badge-accept layui-hide-xs">已结</span>-->
<span class="fly-list-nums">
<i class="iconfont icon-pinglun1" title="回答"></i> 66
</span>
</div>
<div class="fly-list-badge">
<!--<span class="layui-badge layui-bg-red">精帖</span>-->
</div>
</li>
<li>
<a href="user/home.html" class="fly-avatar">
<img src="https://tva1.sinaimg.cn/crop.0.0.118.118.180/5db11ff4gw1e77d3nqrv8j203b03cweg.jpg" alt="贤心">
</a>
<h2>
<a class="layui-badge">动态</a>
<a href="detail.html">基于 layui 的极简社区页面模版</a>
</h2>
<div class="fly-list-info">
<a href="user/home.html" link>
<cite>贤心</cite>
<!--
<i class="iconfont icon-renzheng" title="认证信息:XXX"></i>
<i class="layui-badge fly-badge-vip">VIP3</i>
-->
</a>
<span>刚刚</span>
<span class="fly-list-kiss layui-hide-xs" title="悬赏飞吻"><i class="iconfont icon-kiss"></i> 60</span>
<!--<span class="layui-badge fly-badge-accept layui-hide-xs">已结</span>-->
<span class="fly-list-nums">
<i class="iconfont icon-pinglun1" title="回答"></i> 66
</span>
</div>
<div class="fly-list-badge">
<!--<span class="layui-badge layui-bg-red">精帖</span>-->
</div>
</li>
<li>
<a href="user/home.html" class="fly-avatar">
<img src="https://tva1.sinaimg.cn/crop.0.0.118.118.180/5db11ff4gw1e77d3nqrv8j203b03cweg.jpg" alt="贤心">
</a>
<h2>
<a class="layui-badge">动态</a>
<a href="detail.html">基于 layui 的极简社区页面模版</a>
</h2>
<div class="fly-list-info">
<a href="user/home.html" link>
<cite>贤心</cite>
<!--
<i class="iconfont icon-renzheng" title="认证信息:XXX"></i>
<i class="layui-badge fly-badge-vip">VIP3</i>
-->
</a>
<span>刚刚</span>
<span class="fly-list-kiss layui-hide-xs" title="悬赏飞吻"><i class="iconfont icon-kiss"></i> 60</span>
<!--<span class="layui-badge fly-badge-accept layui-hide-xs">已结</span>-->
<span class="fly-list-nums">
<i class="iconfont icon-pinglun1" title="回答"></i> 66
</span>
</div>
<div class="fly-list-badge">
<!--<span class="layui-badge layui-bg-red">精帖</span>-->
</div>
</li>
<li>
<a href="user/home.html" class="fly-avatar">
<img src="https://tva1.sinaimg.cn/crop.0.0.118.118.180/5db11ff4gw1e77d3nqrv8j203b03cweg.jpg" alt="贤心">
</a>
<h2>
<a class="layui-badge">动态</a>
<a href="detail.html">基于 layui 的极简社区页面模版</a>
</h2>
<div class="fly-list-info">
<a href="user/home.html" link>
<cite>贤心</cite>
<!--
<i class="iconfont icon-renzheng" title="认证信息:XXX"></i>
<i class="layui-badge fly-badge-vip">VIP3</i>
-->
</a>
<span>刚刚</span>
<span class="fly-list-kiss layui-hide-xs" title="悬赏飞吻"><i class="iconfont icon-kiss"></i> 60</span>
<!--<span class="layui-badge fly-badge-accept layui-hide-xs">已结</span>-->
<span class="fly-list-nums">
<i class="iconfont icon-pinglun1" title="回答"></i> 66
</span>
</div>
<div class="fly-list-badge">
<!--<span class="layui-badge layui-bg-red">精帖</span>-->
</div>
</li>
<li>
<a href="user/home.html" class="fly-avatar">
<img src="https://tva1.sinaimg.cn/crop.0.0.118.118.180/5db11ff4gw1e77d3nqrv8j203b03cweg.jpg" alt="贤心">
</a>
<h2>
<a class="layui-badge">动态</a>
<a href="detail.html">基于 layui 的极简社区页面模版</a>
</h2>
<div class="fly-list-info">
<a href="user/home.html" link>
<cite>贤心</cite>
<!--
<i class="iconfont icon-renzheng" title="认证信息:XXX"></i>
<i class="layui-badge fly-badge-vip">VIP3</i>
-->
</a>
<span>刚刚</span>
<span class="fly-list-kiss layui-hide-xs" title="悬赏飞吻"><i class="iconfont icon-kiss"></i> 60</span>
<!--<span class="layui-badge fly-badge-accept layui-hide-xs">已结</span>-->
<span class="fly-list-nums">
<i class="iconfont icon-pinglun1" title="回答"></i> 66
</span>
</div>
<div class="fly-list-badge">
<!--<span class="layui-badge layui-bg-red">精帖</span>-->
</div>
</li>
</ul>
<!-- <div class="fly-none">没有相关数据</div> -->
<div style="text-align: center">
<div class="laypage-main"><span class="laypage-curr">1</span><a href="/jie/page/2/">2</a><a href="/jie/page/3/">3</a><a href="/jie/page/4/">4</a><a href="/jie/page/5/">5</a><span>…</span><a href="/jie/page/148/" class="laypage-last" title="尾页">尾页</a><a href="/jie/page/2/" class="laypage-next">下一页</a></div>
</div>
</div>
</div>
<#include "../inc/right.ftl">
</div>
</div>
</@layout>
同理把detail.ftl 页面修改一下
<#include "../inc/layout.ftl"/>
<#-- 导入 layout 的标签 -->
<@layout "博客分类" >
<#include "../inc/hrader-panel.ftl"/>
<div class="layui-container">
<div class="layui-row layui-col-space15">
<div class="layui-col-md8 content detail">
<div class="fly-panel detail-box">
<h1>Fly Template v3.0,基于 layui 的极简社区页面模版</h1>
<div class="fly-detail-info">
<!-- <span class="layui-badge">审核中</span> -->
<span class="layui-badge layui-bg-green fly-detail-column">动态</span>
<span class="layui-badge" style="background-color: #999;">未结</span>
<!-- <span class="layui-badge" style="background-color: #5FB878;">已结</span> -->
<span class="layui-badge layui-bg-black">置顶</span>
<span class="layui-badge layui-bg-red">精帖</span>
<div class="fly-admin-box" data-id="123">
<span class="layui-btn layui-btn-xs jie-admin" type="del">删除</span>
<span class="layui-btn layui-btn-xs jie-admin" type="set" field="stick" rank="1">置顶</span>
<!-- <span class="layui-btn layui-btn-xs jie-admin" type="set" field="stick" rank="0" style="background-color:#ccc;">取消置顶</span> -->
<span class="layui-btn layui-btn-xs jie-admin" type="set" field="status" rank="1">加精</span>
<!-- <span class="layui-btn layui-btn-xs jie-admin" type="set" field="status" rank="0" style="background-color:#ccc;">取消加精</span> -->
</div>
<span class="fly-list-nums">
<a href="#comment"><i class="iconfont" title="回答"></i> 66</a>
<i class="iconfont" title="人气"></i> 99999
</span>
</div>
<div class="detail-about">
<a class="fly-avatar" href="../user/home.html">
<img src="https://tva1.sinaimg.cn/crop.0.0.118.118.180/5db11ff4gw1e77d3nqrv8j203b03cweg.jpg" alt="贤心">
</a>
<div class="fly-detail-user">
<a href="../user/home.html" class="fly-link">
<cite>贤心</cite>
<i class="iconfont icon-renzheng" title="认证信息:{{ rows.user.approve }}"></i>
<i class="layui-badge fly-badge-vip">VIP3</i>
</a>
<span>2017-11-30</span>
</div>
<div class="detail-hits" id="LAY_jieAdmin" data-id="123">
<span style="padding-right: 10px; color: #FF7200">悬赏:60飞吻</span>
<span class="layui-btn layui-btn-xs jie-admin" type="edit"><a href="add.html">编辑此贴</a></span>
</div>
</div>
<div class="detail-body photos">
<p>
该模版由 layui官方社区(<a href="http://fly.layui.com/" target="_blank">fly.layui.com</a>)倾情提供,只为表明我们对 layui 执着的信念、以及对未来持续加强的承诺。该模版基于 layui 搭建而成,可作为极简通用型社区的页面支撑。
</p>
<p>更新日志:</p>
<pre>
# v3.0 2017-11-30
* 采用 layui 2.2.3 作为 UI 支撑
* 全面同步最新的 Fly 社区风格,各种细节得到大幅优化
* 更友好的响应式适配能力
</pre>
下载<hr>
<p>
官网:<a href="http://www.layui.com/template/fly/" target="_blank">http://www.layui.com/template/fly/</a><br>
码云:<a href="https://gitee.com/sentsin/fly/" target="_blank">https://gitee.com/sentsin/fly/</a><br>
GitHub:<a href="https://github.com/layui/fly" target="_blank">https://github.com/layui/fly</a>
</p>
封面<hr>
<p>
<img src="../../res/images/fly.jpg" alt="Fly社区">
</p>
</div>
</div>
<div class="fly-panel detail-box" id="flyReply">
<fieldset class="layui-elem-field layui-field-title" style="text-align: center;">
<legend>回帖</legend>
</fieldset>
<ul class="jieda" id="jieda">
<li data-id="111" class="jieda-daan">
<a name="item-1111111111"></a>
<div class="detail-about detail-about-reply">
<a class="fly-avatar" href="">
<img src="https://tva1.sinaimg.cn/crop.0.0.118.118.180/5db11ff4gw1e77d3nqrv8j203b03cweg.jpg" alt=" ">
</a>
<div class="fly-detail-user">
<a href="" class="fly-link">
<cite>贤心</cite>
<i class="iconfont icon-renzheng" title="认证信息:XXX"></i>
<i class="layui-badge fly-badge-vip">VIP3</i>
</a>
<span>(楼主)</span>
<!--
<span style="color:#5FB878">(管理员)</span>
<span style="color:#FF9E3F">(社区之光)</span>
<span style="color:#999">(该号已被封)</span>
-->
</div>
<div class="detail-hits">
<span>2017-11-30</span>
</div>
<i class="iconfont icon-caina" title="最佳答案"></i>
</div>
<div class="detail-body jieda-body photos">
<p>香菇那个蓝瘦,这是一条被采纳的回帖</p>
</div>
<div class="jieda-reply">
<span class="jieda-zan zanok" type="zan">
<i class="iconfont icon-zan"></i>
<em>66</em>
</span>
<span type="reply">
<i class="iconfont icon-svgmoban53"></i>
回复
</span>
<div class="jieda-admin">
<span type="edit">编辑</span>
<span type="del">删除</span>
<!-- <span class="jieda-accept" type="accept">采纳</span> -->
</div>
</div>
</li>
<li data-id="111">
<a name="item-1111111111"></a>
<div class="detail-about detail-about-reply">
<a class="fly-avatar" href="">
<img src="https://tva1.sinaimg.cn/crop.0.0.118.118.180/5db11ff4gw1e77d3nqrv8j203b03cweg.jpg" alt=" ">
</a>
<div class="fly-detail-user">
<a href="" class="fly-link">
<cite>贤心</cite>
</a>
</div>
<div class="detail-hits">
<span>2017-11-30</span>
</div>
</div>
<div class="detail-body jieda-body photos">
<p>蓝瘦那个香菇,这是一条没被采纳的回帖</p>
</div>
<div class="jieda-reply">
<span class="jieda-zan" type="zan">
<i class="iconfont icon-zan"></i>
<em>0</em>
</span>
<span type="reply">
<i class="iconfont icon-svgmoban53"></i>
回复
</span>
<div class="jieda-admin">
<span type="edit">编辑</span>
<span type="del">删除</span>
<span class="jieda-accept" type="accept">采纳</span>
</div>
</div>
</li>
<!-- 无数据时 -->
<!-- <li class="fly-none">消灭零回复</li> -->
</ul>
<div class="layui-form layui-form-pane">
<form action="/jie/reply/" method="post">
<div class="layui-form-item layui-form-text">
<a name="comment"></a>
<div class="layui-input-block">
<textarea id="L_content" name="content" required lay-verify="required" placeholder="请输入内容" class="layui-textarea fly-editor" style="height: 150px;"></textarea>
</div>
</div>
<div class="layui-form-item">
<input type="hidden" name="jid" value="123">
<button class="layui-btn" lay-filter="*" lay-submit>提交回复</button>
</div>
</form>
</div>
</div>
</div>
<#include "../inc/right.ftl">
</div>
</div>
</@layout>
以上前端暂时开发完毕
导入mp的依赖包
<dependency>
<groupId>com.baomidougroupId>
<artifactId>mybatis-plus-boot-starterartifactId>
<version>3.2.0version>
dependency>
<dependency>
<groupId>com.baomidougroupId>
<artifactId>mybatis-plus-generatorartifactId>
<version>3.2.0version>
dependency>
从官网把生成代码的代码复制下来改一下
public class 生成代码 {
public static String scanner(String tip) {
Scanner scanner = new Scanner(System.in);
StringBuilder help = new StringBuilder();
help.append("请输入" + tip + ":");
System.out.println(help.toString());
if (scanner.hasNext()) {
String ipt = scanner.next();
if (StringUtils.isNotEmpty(ipt)) {
return ipt;
}
}
throw new MybatisPlusException("请输入正确的" + tip + "!");
}
public static void main(String[] args) {
// 代码生成器
AutoGenerator mpg = new AutoGenerator();
// 全局配置
GlobalConfig gc = new GlobalConfig();
String projectPath = System.getProperty("user.dir");
gc.setOutputDir(projectPath + "/src/main/java");
gc.setAuthor("fjj");
gc.setOpen(false);
// gc.setSwagger2(true); 实体属性 Swagger2 注解
mpg.setGlobalConfig(gc);
// 数据源配置
DataSourceConfig dsc = new DataSourceConfig();
dsc.setUrl("jdbc:mysql://localhost:3306/eblog?useUnicode=true&characterEncoding=utf8&serverTimezone=GMT");
// dsc.setSchemaName("public");
dsc.setDriverName("com.mysql.cj.jdbc.Driver");
dsc.setUsername("root");
dsc.setPassword("123456");
mpg.setDataSource(dsc);
// 包配置
PackageConfig pc = new PackageConfig();
// pc.setModuleName(scanner("模块名"));
pc.setParent("com.example.springbootblog");
mpg.setPackageInfo(pc);
// 自定义配置
InjectionConfig cfg = new InjectionConfig() {
@Override
public void initMap() {
// to do nothing
}
};
// 如果模板引擎是 freemarker
String templatePath = "/templates/mapper.xml.ftl";
// 如果模板引擎是 velocity
// String templatePath = "/templates/mapper.xml.vm";
// 自定义输出配置
List<FileOutConfig> focList = new ArrayList<>();
// 自定义配置会被优先输出
focList.add(new FileOutConfig(templatePath) {
@Override
public String outputFile(TableInfo tableInfo) {
// 自定义输出文件名 , 如果你 Entity 设置了前后缀、此处注意 xml 的名称会跟着发生变化!!
return projectPath + "/src/main/resources/mapper/" + pc.getModuleName()
+ "/" + tableInfo.getEntityName() + "Mapper" + StringPool.DOT_XML;
}
});
cfg.setFileOutConfigList(focList);
mpg.setCfg(cfg);
// 配置模板
TemplateConfig templateConfig = new TemplateConfig();
templateConfig.setXml(null);
mpg.setTemplate(templateConfig);
// 策略配置
StrategyConfig strategy = new StrategyConfig();
strategy.setNaming(NamingStrategy.underline_to_camel);
strategy.setColumnNaming(NamingStrategy.underline_to_camel);
strategy.setEntityLombokModel(true);
strategy.setRestControllerStyle(true);
// 公共父类
// 写于父类中的公共字段
strategy.setInclude(scanner("user").split(","));
strategy.setControllerMappingHyphenStyle(true);
mpg.setStrategy(strategy);
mpg.setTemplateEngine(new FreemarkerTemplateEngine());
mpg.execute();
}
}
点击之后生成我们的代码和实体类注意要修改链接数据库还有包的地方
此外在引入MP 之后一定要编写COnfig 类或者在主启动类上加上 MapperScan
这样启动的时候就不会报错了
说白了就是现在上面的那些导航栏是死的,我们应该从数据库里面去查询我们的数据。变成项目启动的时候动态出来我们的数据就可以了 。
创建ContextStartup
@Component
// 实现 启动类 ,还有 上下文的servlect
public class ContextStartup implements ApplicationRunner, ServletContextAware {
// 注入 categoryService
@Autowired
IMCategoryService categoryService;
ServletContext servletContext;
@Override
public void run(ApplicationArguments args) throws Exception {
// 调用全查的方法
List<MCategory> list = categoryService.list(new QueryWrapper<MCategory>().eq("status", 0));
servletContext.setAttribute("List",list);
}
@Override
public void setServletContext(ServletContext servletContext) {
this.servletContext =servletContext;
}
}
修改
变为动态的了,但是还是有一个小的bug 就是当我们点击的时候首页那个绿色不会消失
创建一个公共的Controller 类,主要放一些公共需要继承的东西
修改我们的PostController 类 设置属性
效果这个时候我们点击分享的时候首页就不会是绿色的,只有分享是绿色的
引入Mp 的分页插件
编写首页的Controller
解释为啥用这个方法
@Controller
public class IndexController extends BaseController {
@RequestMapping({"", "/", "index"})
public String index() {
// 设置默认的开始页面
// 这里为啥不用 request.Parameter 获取是因为他不可以设置默认的值 这个工具类会判断你有没有设置默认页面 如果设置了就先展示
int start = ServletRequestUtils.getIntParameter(request, "start", 1);
// 设置默认的展示多少页面
int end = ServletRequestUtils.getIntParameter(request, "end", 2);
// 调用 Mp 的分页方法
Page page = new Page(start, end);
// 调用服务类 1,分页信息 2,分类 3,用户 ,4 置顶 ,5 精选 6排序 排序是通过时间进行一个排序
IPage results = postService.paging(page,null,null,null,null,"created");
request.setAttribute("CategoryId", 0);
request.setAttribute("created", 0);
return "index";
}
}
Postserviceimpl 实现类
/**
*
* 服务实现类
*
*
* @author fjj
* @since 2021-07-23
*/
@Service
public class MPostServiceImpl extends ServiceImpl<MPostMapper, MPost> implements IMPostService {
@Autowired
MPostMapper postMapper;
@Override
public IPage paging(Page page, Long categoryId, Long userId, Integer level, Boolean recommend, String order) {
// 判断 level 是否等于 空
if (level == null) level = -1;
// 获取wrapper
QueryWrapper wrapper = new QueryWrapper<MPost>()
.eq(categoryId != null,"category_id",categoryId)
.eq(userId !=null ,"user_id",userId)
.eq(level == 0 ,"level",0)
.gt(level >0 ,"level",0)
.orderByDesc(order != null,order);
return postMapper.selectPosts(page, wrapper);
}
}
MPostMapper 的Mapper
/**
*
* Mapper 接口
*
*
* @author fjj
* @since 2021-07-23
*/
@Mapper
public interface MPostMapper extends BaseMapper<MPost> {
// 这里之所以要 @Param(Constants.WRAPPER) 是 MP 官网的要求
IPage<PostVo> selectPosts( Page page ,@Param(Constants.WRAPPER) QueryWrapper wrapper);
}
MPostMapper.xml 的Spl 语句 应该是3个表连起来
DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.example.springbootblog.mapper.MPostMapper">
<select id="selectPosts" resultType="com.example.springbootblog.vo.PostVo">
SELECT
p.*,
u.id AS authorId,
u.username AS authorName,
u.avatar AS authorAvatar,
c.id AS categoryId,
c.name AS categoryName
FROM
m_post p
LEFT JOIN m_user u ON p.user_id = u.id
LEFT JOIN m_category c ON p.category_id = c.id
${ew.customSqlSegment}
select>
mapper>
现在我们的Sql 都还不能看到参考MP 配置一下Sql 分析打印
配置application.yml
mybatis-plus:
mapper-locations: classpath*:/mapper/**Mapper.xml
#用于mybatis在控制台打印sql日志
configuration:
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
重启项目
这个SQL是因为在我们的Config 里面有个
前端页面获取
这里之所以是 pageData.records是因为返回的是我们写好的PostVo 因为是三个表查询,有的是通过id查询的别的表的字段,原来的实体类里面没有有些字段所以先建了一个PostVo.class 用来映射个别字段继承Post 的实体类这样就可以有所有的字段了
package com.example.springbootblog.vo;
import com.example.springbootblog.entity.MPost;
import lombok.Data;
@Data
public class PostVo extends MPost {
private Long authorId;
private String authorName;
private String authorAvatar;
// private Long categoryId;
private String categoryName;
}
打印输出一下pageData.records可以看到就是映射的是我们的Postvo.class
这样的话就页面就可以访问到我们数据库的数据了
效果
虽然渲染出来了,但是不是我们想要的数据。我们应该是存我们的后台返回出来的数据进行分页
修改前端的地方
layui 官网对于分页的这几个对象属性的解释
虽然有了从数据库出来的分页的数据的效果但是当我们点击第二页的时候数据并不会变化,所以还需要在修改一下代码layui 官网上就有
修改index.ftl 页面
不要忘记在layout里面添加标签
至此分页功能结束
两种方法
编写freemarker的接口
DirectiveHandler
package com.example.springbootblog.common.templates;
import freemarker.template.*;
import java.text.DateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import static org.apache.commons.lang3.StringUtils.*;
/**
* Freemarker 模型工具类
*
* Created by langhsu on 2017/11/14.
*/
public class TemplateModelUtils {
public static final DateFormat FULL_DATE_FORMAT = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
public static final int FULL_DATE_LENGTH = 19;
public static final DateFormat SHORT_DATE_FORMAT = new SimpleDateFormat("yyyy-MM-dd");
public static final int SHORT_DATE_LENGTH = 10;
public static String converString(TemplateModel model) throws TemplateModelException {
if (null != model) {
if (model instanceof TemplateScalarModel) {
return ((TemplateScalarModel) model).getAsString();
} else if ((model instanceof TemplateNumberModel)) {
return ((TemplateNumberModel) model).getAsNumber().toString();
}
}
return null;
}
public static TemplateHashModel converMap(TemplateModel model) throws TemplateModelException {
if (null != model) {
if (model instanceof TemplateHashModelEx) {
return (TemplateHashModelEx) model;
} else if (model instanceof TemplateHashModel) {
return (TemplateHashModel) model;
}
}
return null;
}
public static Integer converInteger(TemplateModel model) throws TemplateModelException {
if (null != model) {
if (model instanceof TemplateNumberModel) {
return ((TemplateNumberModel) model).getAsNumber().intValue();
} else if (model instanceof TemplateScalarModel) {
String s = ((TemplateScalarModel) model).getAsString();
if (isNotBlank(s)) {
try {
return Integer.parseInt(s);
} catch (NumberFormatException e) {
}
}
}
}
return null;
}
public static Short converShort(TemplateModel model) throws TemplateModelException {
if (null != model) {
if (model instanceof TemplateNumberModel) {
return ((TemplateNumberModel) model).getAsNumber().shortValue();
} else if (model instanceof TemplateScalarModel) {
String s = ((TemplateScalarModel) model).getAsString();
if (isNotBlank(s)) {
try {
return Short.parseShort(s);
} catch (NumberFormatException e) {
}
}
}
}
return null;
}
public static Long converLong(TemplateModel model) throws TemplateModelException {
if (null != model) {
if (model instanceof TemplateNumberModel) {
return ((TemplateNumberModel) model).getAsNumber().longValue();
} else if (model instanceof TemplateScalarModel) {
String s = ((TemplateScalarModel) model).getAsString();
if (isNotBlank(s)) {
try {
return Long.parseLong(s);
} catch (NumberFormatException e) {
}
}
}
}
return null;
}
public static Double converDouble(TemplateModel model) throws TemplateModelException {
if (null != model) {
if (model instanceof TemplateNumberModel) {
return ((TemplateNumberModel) model).getAsNumber().doubleValue();
} else if (model instanceof TemplateScalarModel) {
String s = ((TemplateScalarModel) model).getAsString();
if (isNotBlank(s)) {
try {
return Double.parseDouble(s);
} catch (NumberFormatException ignored) {
}
}
}
}
return null;
}
public static String[] converStringArray(TemplateModel model) throws TemplateModelException {
if (model instanceof TemplateSequenceModel) {
TemplateSequenceModel smodel = (TemplateSequenceModel) model;
String[] values = new String[smodel.size()];
for (int i = 0; i < smodel.size(); i++) {
values[i] = converString(smodel.get(i));
}
return values;
} else {
String str = converString(model);
if (isNotBlank(str)) {
return split(str,',');
}
}
return null;
}
public static Boolean converBoolean(TemplateModel model) throws TemplateModelException {
if (null != model) {
if (model instanceof TemplateBooleanModel) {
return ((TemplateBooleanModel) model).getAsBoolean();
} else if (model instanceof TemplateNumberModel) {
return !(0 == ((TemplateNumberModel) model).getAsNumber().intValue());
} else if (model instanceof TemplateScalarModel) {
String temp = ((TemplateScalarModel) model).getAsString();
if (isNotBlank(temp)) {
return Boolean.valueOf(temp);
}
}
}
return null;
}
public static Date converDate(TemplateModel model) throws TemplateModelException {
if (null != model) {
if (model instanceof TemplateDateModel) {
return ((TemplateDateModel) model).getAsDate();
} else if (model instanceof TemplateScalarModel) {
String temp = trimToEmpty(((TemplateScalarModel) model).getAsString());
return parseDate(temp);
}
}
return null;
}
public static Date parseDate(String date) {
Date ret = null;
try {
if (FULL_DATE_LENGTH == date.length()) {
ret = FULL_DATE_FORMAT.parse(date);
} else if (SHORT_DATE_LENGTH == date.length()) {
ret = SHORT_DATE_FORMAT.parse(date);
}
} catch (ParseException e) {
}
return ret;
}
}
TemplateDirective
package com.example.springbootblog.common.templates;
import freemarker.core.Environment;
import freemarker.template.TemplateDirectiveBody;
import freemarker.template.TemplateDirectiveModel;
import freemarker.template.TemplateException;
import freemarker.template.TemplateModel;
import java.io.IOException;
import java.util.Map;
/**
* Created by langhsu on 2017/11/14.
*/
public abstract class TemplateDirective implements TemplateDirectiveModel {
protected static String RESULT = "result";
protected static String RESULTS = "results";
@Override
public void execute(Environment env, Map parameters,
TemplateModel[] loopVars, TemplateDirectiveBody body) throws TemplateException, IOException {
try {
execute(new DirectiveHandler(env, parameters, loopVars, body));
} catch (IOException e) {
throw e;
} catch (Exception e) {
throw new TemplateException(e, env);
}
}
abstract public String getName();
abstract public void execute(DirectiveHandler handler) throws Exception;
}
TemplateModelUtils
package com.example.springbootblog.common.templates;
import freemarker.template.*;
import java.text.DateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import static org.apache.commons.lang3.StringUtils.*;
/**
* Freemarker 模型工具类
*
* Created by langhsu on 2017/11/14.
*/
public class TemplateModelUtils {
public static final DateFormat FULL_DATE_FORMAT = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
public static final int FULL_DATE_LENGTH = 19;
public static final DateFormat SHORT_DATE_FORMAT = new SimpleDateFormat("yyyy-MM-dd");
public static final int SHORT_DATE_LENGTH = 10;
public static String converString(TemplateModel model) throws TemplateModelException {
if (null != model) {
if (model instanceof TemplateScalarModel) {
return ((TemplateScalarModel) model).getAsString();
} else if ((model instanceof TemplateNumberModel)) {
return ((TemplateNumberModel) model).getAsNumber().toString();
}
}
return null;
}
public static TemplateHashModel converMap(TemplateModel model) throws TemplateModelException {
if (null != model) {
if (model instanceof TemplateHashModelEx) {
return (TemplateHashModelEx) model;
} else if (model instanceof TemplateHashModel) {
return (TemplateHashModel) model;
}
}
return null;
}
public static Integer converInteger(TemplateModel model) throws TemplateModelException {
if (null != model) {
if (model instanceof TemplateNumberModel) {
return ((TemplateNumberModel) model).getAsNumber().intValue();
} else if (model instanceof TemplateScalarModel) {
String s = ((TemplateScalarModel) model).getAsString();
if (isNotBlank(s)) {
try {
return Integer.parseInt(s);
} catch (NumberFormatException e) {
}
}
}
}
return null;
}
public static Short converShort(TemplateModel model) throws TemplateModelException {
if (null != model) {
if (model instanceof TemplateNumberModel) {
return ((TemplateNumberModel) model).getAsNumber().shortValue();
} else if (model instanceof TemplateScalarModel) {
String s = ((TemplateScalarModel) model).getAsString();
if (isNotBlank(s)) {
try {
return Short.parseShort(s);
} catch (NumberFormatException e) {
}
}
}
}
return null;
}
public static Long converLong(TemplateModel model) throws TemplateModelException {
if (null != model) {
if (model instanceof TemplateNumberModel) {
return ((TemplateNumberModel) model).getAsNumber().longValue();
} else if (model instanceof TemplateScalarModel) {
String s = ((TemplateScalarModel) model).getAsString();
if (isNotBlank(s)) {
try {
return Long.parseLong(s);
} catch (NumberFormatException e) {
}
}
}
}
return null;
}
public static Double converDouble(TemplateModel model) throws TemplateModelException {
if (null != model) {
if (model instanceof TemplateNumberModel) {
return ((TemplateNumberModel) model).getAsNumber().doubleValue();
} else if (model instanceof TemplateScalarModel) {
String s = ((TemplateScalarModel) model).getAsString();
if (isNotBlank(s)) {
try {
return Double.parseDouble(s);
} catch (NumberFormatException ignored) {
}
}
}
}
return null;
}
public static String[] converStringArray(TemplateModel model) throws TemplateModelException {
if (model instanceof TemplateSequenceModel) {
TemplateSequenceModel smodel = (TemplateSequenceModel) model;
String[] values = new String[smodel.size()];
for (int i = 0; i < smodel.size(); i++) {
values[i] = converString(smodel.get(i));
}
return values;
} else {
String str = converString(model);
if (isNotBlank(str)) {
return split(str,',');
}
}
return null;
}
public static Boolean converBoolean(TemplateModel model) throws TemplateModelException {
if (null != model) {
if (model instanceof TemplateBooleanModel) {
return ((TemplateBooleanModel) model).getAsBoolean();
} else if (model instanceof TemplateNumberModel) {
return !(0 == ((TemplateNumberModel) model).getAsNumber().intValue());
} else if (model instanceof TemplateScalarModel) {
String temp = ((TemplateScalarModel) model).getAsString();
if (isNotBlank(temp)) {
return Boolean.valueOf(temp);
}
}
}
return null;
}
public static Date converDate(TemplateModel model) throws TemplateModelException {
if (null != model) {
if (model instanceof TemplateDateModel) {
return ((TemplateDateModel) model).getAsDate();
} else if (model instanceof TemplateScalarModel) {
String temp = trimToEmpty(((TemplateScalarModel) model).getAsString());
return parseDate(temp);
}
}
return null;
}
public static Date parseDate(String date) {
Date ret = null;
try {
if (FULL_DATE_LENGTH == date.length()) {
ret = FULL_DATE_FORMAT.parse(date);
} else if (SHORT_DATE_LENGTH == date.length()) {
ret = SHORT_DATE_FORMAT.parse(date);
}
} catch (ParseException e) {
}
return ret;
}
}
package com.example.springbootblog.template;
import com.example.springbootblog.common.templates.DirectiveHandler;
import freemarker.template.TemplateModelException;
import org.springframework.stereotype.Component;
import java.util.Date;
import java.util.List;
@Component
public class TimeAgoMethod extends DirectiveHandler.BaseMethod {
private static final long ONE_MINUTE = 60000L;
private static final long ONE_HOUR = 3600000L;
private static final long ONE_DAY = 86400000L;
private static final long ONE_WEEK = 604800000L;
private static final String ONE_SECOND_AGO = "秒前";
private static final String ONE_MINUTE_AGO = "分钟前";
private static final String ONE_HOUR_AGO = "小时前";
private static final String ONE_DAY_AGO = "天前";
private static final String ONE_MONTH_AGO = "月前";
private static final String ONE_YEAR_AGO = "年前";
private static final String ONE_UNKNOWN = "未知";
@Override
public Object exec(List arguments) throws TemplateModelException {
Date time = getDate(arguments, 0);
return format(time);
}
public static String format(Date date) {
if (null == date) {
return ONE_UNKNOWN;
}
long delta = new Date().getTime() - date.getTime();
if (delta < 1L * ONE_MINUTE) {
long seconds = toSeconds(delta);
return (seconds <= 0 ? 1 : seconds) + ONE_SECOND_AGO;
}
if (delta < 45L * ONE_MINUTE) {
long minutes = toMinutes(delta);
return (minutes <= 0 ? 1 : minutes) + ONE_MINUTE_AGO;
}
if (delta < 24L * ONE_HOUR) {
long hours = toHours(delta);
return (hours <= 0 ? 1 : hours) + ONE_HOUR_AGO;
}
if (delta < 48L * ONE_HOUR) {
return "昨天";
}
if (delta < 30L * ONE_DAY) {
long days = toDays(delta);
return (days <= 0 ? 1 : days) + ONE_DAY_AGO;
}
if (delta < 12L * 4L * ONE_WEEK) {
long months = toMonths(delta);
return (months <= 0 ? 1 : months) + ONE_MONTH_AGO;
} else {
long years = toYears(delta);
return (years <= 0 ? 1 : years) + ONE_YEAR_AGO;
}
}
private static long toSeconds(long date) {
return date / 1000L;
}
private static long toMinutes(long date) {
return toSeconds(date) / 60L;
}
private static long toHours(long date) {
return toMinutes(date) / 60L;
}
private static long toDays(long date) {
return toHours(date) / 24L;
}
private static long toMonths(long date) {
return toDays(date) / 30L;
}
private static long toYears(long date) {
return toMonths(date) / 365L;
}
}
编写Config
@Configuration
public class FreemarkerConfig {
@Autowired
private freemarker.template.Configuration configuration;
@PostConstruct
public void setUp() {
configuration.setSharedVariable("timeAgo", new TimeAgoMethod());
}
}
刚开始的时候我的日期一直是未知 ,打断点发现前台传给后台的时候一直就是null 所以一直返回的未知
打断点调试
最后在实体类的发现用的日期格式化是
换成Date 之后
发现可以使用了
总结可能是因为我们继承的freemarker 的接口他们规定的 yyyy-MM-dd 的格式
@Component
public class PostsTemplate extends TemplateDirective {
// 注入服务类
@Autowired
IMPostService postService;
@Override
public String getName() {
return "posts";
}
@Override
public void execute(DirectiveHandler handler) throws Exception {
// 设置置顶
Integer level = handler.getInteger("level");
//设置分页
Integer pn = handler.getInteger("pn", 1);
Integer size = handler.getInteger("size", 2);
// 设置creadeID
Long categoryId = handler.getLong("categoryId");
IPage<PostVo> page = postService.paging(new Page(pn, size), categoryId, null, level, null, "created");
handler.put(RESULTS,page).render();
}
}
在FreemarkerConfig里面注入
@Configuration
public class FreemarkerConfig {
@Autowired
private freemarker.template.Configuration configuration;
@Autowired
PostsTemplate postsTemplate;
@PostConstruct
public void setUp() {
configuration.setSharedVariable("timeAgo", new TimeAgoMethod());
configuration.setSharedVariable("posts", postsTemplate);
}
}
修改前端这里的页面
把原来的这部分代码抽离出来添加到common.ftl。方便调用
抽离出来之后
index.ftl 页面
效果
数据库中置顶的也只有一篇
点击标题到详情页面功能
PostController
@Controller
public class PostController extends BaseController{
// 指定值接收 数字类型
@GetMapping("/category/{id:\\d*}")
public String category(@PathVariable(name = "id") Long id) {
// 给每一个都设置一个属性
request.setAttribute("CategoryId",id);
return "post/category";
}
@GetMapping("/detail/{id:\\d*}")
public String detail(@PathVariable(name = "id") Long id) {
// 查询数据 后面传得到底是根据那个id 进行查询。
PostVo postVo = postService.selectOnePost(new QueryWrapper<MPost>().eq("p.id",id));
//断言判断是否被删除、如果时空的话就提示文章已经被删除了
Assert.notNull(postVo,"文章已被删除");
// 这个是为了设置 可以方便到底是那个 是分享还是提问之类
request.setAttribute("CategoryId",postVo.getCategoryId());
// 这个是为了设置到前台方便获取到数据
request.setAttribute("post",postVo);
return "post/detail";
}
}
postServiceimpl
@Override
public PostVo selectOnePost(QueryWrapper<MPost> wrapper) {
return postMapper.selectOnePost(wrapper);
}
PostMapper
// 这里要加上这个注解上面解释过为什么了
PostVo selectOnePost(@Param(Constants.WRAPPER) QueryWrapper<MPost> wrapper);
PostMapper.xml
<select id="selectOnePost" resultType="com.example.springbootblog.vo.PostVo">
SELECT
p.*,
u.id AS authorId,
u.username AS authorName,
u.avatar AS authorAvatar,
c.id AS categoryId,
c.name AS categoryName
FROM
m_post p
LEFT JOIN m_user u ON p.user_id = u.id
LEFT JOIN m_category c ON p.category_id = c.id
${ew.customSqlSegment}
select>
修改前端页面detail.ftl
//
<#include "../inc/layout.ftl"/>
<#-- 导入 layout 的标签 -->
<@layout "博客分类" >
<#include "../inc/hrader-panel.ftl"/>
<div class="layui-container">
<div class="layui-row layui-col-space15">
<div class="layui-col-md8 content detail">
<div class="fly-panel detail-box">
<h1>${post.title}</h1>
<div class="fly-detail-info">
<!-- <span class="layui-badge">审核中</span> -->
<span class="layui-badge layui-bg-green fly-detail-column">${post.categoryName}</span>
<#if post.level gt 0 ><span class="layui-badge layui-bg-black">置顶</span></#if>
<#if post.recommend><span class="layui-badge layui-bg-red">精帖</span> </#if>
<div class="fly-admin-box" data-id="${post.id}">
<span class="layui-btn layui-btn-xs jie-admin" type="del">删除</span>
<span class="layui-btn layui-btn-xs jie-admin" type="set" field="stick" rank="1">置顶</span>
<!-- <span class="layui-btn layui-btn-xs jie-admin" type="set" field="stick" rank="0" style="background-color:#ccc;">取消置顶</span> -->
<span class="layui-btn layui-btn-xs jie-admin" type="set" field="status" rank="1">加精</span>
<!-- <span class="layui-btn layui-btn-xs jie-admin" type="set" field="status" rank="0" style="background-color:#ccc;">取消加精</span> -->
</div>
<span class="fly-list-nums">
<a href="#comment"><i class="iconfont" title="回答"></i> ${post.commentCount}</a>
<i class="iconfont" title="人气"></i> ${post.viewCount}
</span>
</div>
<div class="detail-about">
<a class="fly-avatar" href="/user/${post.authorId}">
<img src="${post.authorAvatar}" alt="${post.authorName}">
</a>
<div class="fly-detail-user">
<a href="/user/${post.authorId}" class="fly-link">
<cite>${post.authorName}</cite>
</a>
<span>${timeAgo(post.created)}</span>
</div>
<div class="detail-hits" id="LAY_jieAdmin" data-id="${post.id}">
<#-- <#if profile.id == post.userId><span class="layui-btn layui-btn-xs jie-admin" type="edit"><a href="/post/edit?id=${post.id}">编辑此贴</a></span></#if>-->
</div>
</div>
<div class="detail-body photos">
${post.content}
</div>
</div>
<div class="fly-panel detail-box" id="flyReply">
<fieldset class="layui-elem-field layui-field-title" style="text-align: center;">
<legend>回帖</legend>
</fieldset>
<ul class="jieda" id="jieda">
<li data-id="111" class="jieda-daan">
<a name="item-1111111111"></a>
<div class="detail-about detail-about-reply">
<a class="fly-avatar" href="">
<img src="https://tva1.sinaimg.cn/crop.0.0.118.118.180/5db11ff4gw1e77d3nqrv8j203b03cweg.jpg" alt=" ">
</a>
<div class="fly-detail-user">
<a href="" class="fly-link">
<cite>贤心</cite>
<i class="iconfont icon-renzheng" title="认证信息:XXX"></i>
<i class="layui-badge fly-badge-vip">VIP3</i>
</a>
<span>(楼主)</span>
</div>
<div class="detail-hits">
<span>2017-11-30</span>
</div>
<i class="iconfont icon-caina" title="最佳答案"></i>
</div>
<div class="detail-body jieda-body photos">
<p>香菇那个蓝瘦,这是一条被采纳的回帖</p>
</div>
<div class="jieda-reply">
<span class="jieda-zan zanok" type="zan">
<i class="iconfont icon-zan"></i>
<em>66</em>
</span>
<span type="reply">
<i class="iconfont icon-svgmoban53"></i>
回复
</span>
<div class="jieda-admin">
<span type="edit">编辑</span>
<span type="del">删除</span>
<!-- <span class="jieda-accept" type="accept">采纳</span> -->
</div>
</div>
</li>
<li data-id="111">
<a name="item-1111111111"></a>
<div class="detail-about detail-about-reply">
<a class="fly-avatar" href="">
<img src="https://tva1.sinaimg.cn/crop.0.0.118.118.180/5db11ff4gw1e77d3nqrv8j203b03cweg.jpg" alt=" ">
</a>
<div class="fly-detail-user">
<a href="" class="fly-link">
<cite>贤心</cite>
</a>
</div>
<div class="detail-hits">
<span>2017-11-30</span>
</div>
</div>
<div class="detail-body jieda-body photos">
<p>蓝瘦那个香菇,这是一条没被采纳的回帖</p>
</div>
<div class="jieda-reply">
<span class="jieda-zan" type="zan">
<i class="iconfont icon-zan"></i>
<em>0</em>
</span>
<span type="reply">
<i class="iconfont icon-svgmoban53"></i>
回复
</span>
<div class="jieda-admin">
<span type="edit">编辑</span>
<span type="del">删除</span>
<span class="jieda-accept" type="accept">采纳</span>
</div>
</div>
</li>
<!-- 无数据时 -->
<!-- <li class="fly-none">消灭零回复</li> -->
</ul>
<div class="layui-form layui-form-pane">
<form action="/jie/reply/" method="post">
<div class="layui-form-item layui-form-text">
<a name="comment"></a>
<div class="layui-input-block">
<textarea id="L_content" name="content" required lay-verify="required" placeholder="请输入内容" class="layui-textarea fly-editor" style="height: 150px;"></textarea>
</div>
</div>
<div class="layui-form-item">
<input type="hidden" name="jid" value="123">
<button class="layui-btn" lay-filter="*" lay-submit>提交回复</button>
</div>
</form>
</div>
</div>
</div>
<#include "../inc/right.ftl">
</div>
</div>
</@layout>
分页的信息要在PostController 里面设置一下
public class PostController extends BaseController{
// 指定值接收 数字类型
@GetMapping("/category/{id:\\d*}")
public String category(@PathVariable(name = "id") Long id) {
int pn = ServletRequestUtils.getIntParameter(request, "start", 1);
request.setAttribute("CategoryId", id);
request.setAttribute("start", pn);
return "post/category";
}
@GetMapping("/detail/{id:\\d*}")
public String detail(@PathVariable(name = "id") Long id) {
// 查询数据
PostVo postVo = postService.selectOnePost(new QueryWrapper<MPost>().eq("p.id",id));
//断言判断是否被删除
Assert.notNull(postVo,"文章已被删除");
// 调用 评论的方法 1, 分页 2,文章的id 3, 用户 id 4,排序
IPage<CommentVo> results = imCommentService.paging(getPage(),postVo.getId(),null,"created");
request.setAttribute("CategoryId",postVo.getCategoryId());
request.setAttribute("post",postVo);
request.setAttribute("pageData",results);
return "post/detail";
}
评论的服务类
/**
*
* 服务实现类
*
*
* @author fjj
* @since 2021-07-23
*/
@Service
public class MCommentServiceImpl extends ServiceImpl<MCommentMapper, MComment> implements IMCommentService {
// 导入 Mapper
@Autowired
MCommentMapper mCommentMapper;
@Override
public IPage<CommentVo> paging(Page page, Long postId, Long userid, String order) {
return mCommentMapper.selectComment(page, new QueryWrapper<MComment>()
.eq(postId != null, "post_id", postId)
.eq(userid != null, "user_id", userid)
.orderByDesc(order != null, order)
);
}
}
评论的mapper
@Mapper
public interface MCommentMapper extends BaseMapper<MComment> {
IPage<CommentVo> selectComment(Page page, @Param(Constants.WRAPPER) QueryWrapper<MComment> orderByDesc);
}
评论的mapper.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.example.springbootblog.mapper.MCommentMapper">
<select id="selectComment" resultType="com.example.springbootblog.vo.CommentVo">
SELECT
c.*,
u.id AS authorId,
u.username AS authorName,
U.avatar AS authorAvatar
FROM
m_comment c
LEFT JOIN m_user u ON c.user_id = u.id
${ew.customSqlSegment}
</select>
</mapper>
CommentVo
@Data
public class CommentVo extends MComment {
// 作者的ID
private Long authorId;
//名字
private String authorName;
private String authorAvatar;
}
前端修改
<div class="fly-panel detail-box" id="flyReply">
<fieldset class="layui-elem-field layui-field-title" style="text-align: center;">
<legend>回帖</legend>
</fieldset>
<ul class="jieda" id="jieda">
<#list pageData.records as comment>
<li data-id="${comment.id}" class="jieda-daan">
<a name="${comment.id}"></a>
<div class="detail-about detail-about-reply">
<a class="fly-avatar" href="/user/${post.authorId}">
<img src="${post.authorAvatar}" alt="${post.authorName}">
</a>
<div class="fly-detail-user">
<a href="/user/${post.authorId}" class="fly-link">
<cite>${post.authorName}</cite>
</a>
<#if comment.userId == post.userId>
<span>(楼主)</span>
</#if>
</div>
<div class="detail-hits">
<span>${timeAgo(comment.created)}</span>
</div>
</div>
<div class="detail-body jieda-body photos">
<p>${comment.content}</p>
</div>
<div class="jieda-reply">
<span class="jieda-zan zanok" type="zan">
<i class="iconfont icon-zan"></i>
<em>${comment.voteUp}</em>
</span>
<span type="reply">
<i class="iconfont icon-svgmoban53"></i>
回复
</span>
<div class="jieda-admin">
<span type="edit">编辑</span>
<span type="del">删除</span>
</div>
</div>
</li>
</#list>
</ul>
<@paging pageData></@paging>
<div class="layui-form layui-form-pane">
<form action="/jie/reply/" method="post">
<div class="layui-form-item layui-form-text">
<a name="comment"></a>
<div class="layui-input-block">
<textarea id="L_content" name="content" required lay-verify="required" placeholder="请输入内容" class="layui-textarea fly-editor" style="height: 150px;"></textarea>
</div>
</div>
<div class="layui-form-item">
<input type="hidden" name="jid" value="${post.id}">
<button class="layui-btn" lay-filter="*" lay-submit>提交回复</button>
</div>
</form>
</div>
</div>
我写在了我的另一个博客里面,可以点击查看
当我们刷新页面浏览量应该加1,但是如果一直刷新一直差数据库的话会对数据库造成压力,所以可以先夹缓存里面然后定时添加到数据库
先写入缓存
@Override
public void putViewCount(PostVo postVo) {
// 获取到 key
String key = "rank:post:" +postVo.getId();
// 先从缓存中拿到我们的viewCount
Integer viewCount = (Integer) redisUtil.hget(key, "post:viewCount");
// 判断存在不存在,如果存在的话就加1 不存在从实体 加1
if (viewCount!=null) {
postVo.setViewCount(viewCount +1);
} else {
postVo.setViewCount(postVo.getViewCount()+1);
}
// 同步到缓存中
redisUtil.hset(key,"post:viewCount",postVo.getViewCount());
}
@Component
public class ViewCountSyncTask {
@Autowired
RedisUtil redisUtil;
@Autowired
IMPostService postService;
@Autowired
RedisTemplate redisTemplate;
// 设置定时器的时间设置
@Scheduled(cron = "* 1-2 0/1 * * ? ")
public void task() {
// 获取到所有的key
Set<String> keys = redisTemplate.keys("rank:post:*");
// 创建用来存放的ids
ArrayList<String> ids = new ArrayList<>();
// 遍历所有的key 找到 存放浏览量的 key
for (String key : keys) {
// 判断是否存在 redis 中
if (redisUtil.hHasKey(key, "post:viewCount")) {
ids.add(key.substring("rank:post:".length()));
}
}
if (ids.isEmpty())
return;
// 需要更新的阅读量
List<MPost> posts = postService.list(new QueryWrapper<MPost>().in("id", ids));
// 遍历 并且从 redis 中获取出来
posts.stream().forEach(post -> {
Integer hget = (Integer) redisUtil.hget("rank:post:" + post.getId(), "post:viewCount");
// 添加到 post 里
post.setViewCount(hget);
});
if (posts.isEmpty())
return;
boolean inSucc = postService.updateBatchById(posts);
if (inSucc) {
ids.stream().forEach(id -> {
redisUtil.hdel("rank:post:" + id, "post:viewCount");
});
}
}
}
注册功能的集成,先把我们的登录注册页面拿过来
login.ftl
<#include "../inc/layout.ftl"/>
<#-- 导入 layout 的标签 -->
<@layout "登录" >
<div class="layui-container fly-marginTop">
<div class="fly-panel fly-panel-user" pad20>
<div class="layui-tab layui-tab-brief" lay-filter="user">
<ul class="layui-tab-title">
<li class="/login">登入</li>
<li><a href="/register">注册</a></li>
</ul>
<div class="layui-form layui-tab-content" id="LAY_ucm" style="padding: 20px 0;">
<div class="layui-tab-item layui-show">
<div class="layui-form layui-form-pane">
<form method="post">
<div class="layui-form-item">
<label for="L_email" class="layui-form-label">邮箱</label>
<div class="layui-input-inline">
<input type="text" id="L_email" name="email" required lay-verify="required" autocomplete="off" class="layui-input">
</div>
</div>
<div class="layui-form-item">
<label for="L_pass" class="layui-form-label">密码</label>
<div class="layui-input-inline">
<input type="password" id="L_pass" name="pass" required lay-verify="required" autocomplete="off" class="layui-input">
</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 class="layui-form-mid">
<span style="color: #c00;">{{d.vercode}}</span>
</div>
</div>
<div class="layui-form-item">
<button class="layui-btn" lay-filter="*" lay-submit>立即登录</button>
<span style="padding-left:20px;">
<a href="forget.html">忘记密码?</a>
</span>
</div>
<div class="layui-form-item fly-form-app">
<span>或者使用社交账号登入</span>
<a href="" onclick="layer.msg('正在通过QQ登入', {icon:16, shade: 0.1, time:0})" class="iconfont icon-qq" title="QQ登入"></a>
<a href="" onclick="layer.msg('正在通过微博登入', {icon:16, shade: 0.1, time:0})" class="iconfont icon-weibo" title="微博登入"></a>
</div>
</form>
</div>
</div>
</div>
</div>
</div>
</div>
<script src="../../res/layui/layui.js"></script>
<script>
layui.cache.page = 'user';
</script>
</@layout>
reg.ftl
<#include "../inc/layout.ftl"/>
<#-- 导入 layout 的标签 -->
<@layout "注册" >
<div class="layui-container fly-marginTop">
<div class="fly-panel fly-panel-user" pad20>
<div class="layui-tab layui-tab-brief" lay-filter="user">
<ul class="layui-tab-title">
<li><a href="/login">登入</a></li>
<li class="/register">注册</li>
</ul>
<div class="layui-form layui-tab-content" id="LAY_ucm" style="padding: 20px 0;">
<div class="layui-tab-item layui-show">
<div class="layui-form layui-form-pane">
<form method="post">
<div class="layui-form-item">
<label for="L_email" class="layui-form-label">邮箱</label>
<div class="layui-input-inline">
<input type="text" id="L_email" name="email" autocomplete="off" class="layui-input">
</div>
<div class="layui-form-mid layui-word-aux">将会成为您唯一的登入名</div>
</div>
<div class="layui-form-item">
<label for="L_username" class="layui-form-label">昵称</label>
<div class="layui-input-inline">
<input type="text" id="L_username" name="username" autocomplete="off" class="layui-input">
</div>
</div>
<div class="layui-form-item">
<label for="L_pass" class="layui-form-label">密码</label>
<div class="layui-input-inline">
<input type="password" id="L_pass" name="password" autocomplete="off" class="layui-input">
</div>
<div class="layui-form-mid layui-word-aux">6到16个字符</div>
</div>
<div class="layui-form-item">
<label for="L_repass" class="layui-form-label">确认密码</label>
<div class="layui-input-inline">
<input type="password" id="L_repass" name="repass" autocomplete="off" class="layui-input">
</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" placeholder="请填写后面的验证码" autocomplete="off" class="layui-input">
</div>
<div class="">
<img src="/captcha.jpg" id="captch"></img>
</div>
</div>
<div class="layui-form-item">
<button class="layui-btn" lay-filter="*" lay-submit alert="true">立即注册</button>
</div>
<div class="layui-form-item fly-form-app">
<span>或者直接使用社交账号快捷注册</span>
<a href="" onclick="layer.msg('正在通过QQ登入', {icon:16, shade: 0.1, time:0})" class="iconfont icon-qq" title="QQ登入"></a>
<a href="" onclick="layer.msg('正在通过微博登入', {icon:16, shade: 0.1, time:0})" class="iconfont icon-weibo" title="微博登入"></a>
</div>
</form>
</div>
</div>
</div>
</div>
</div>
</div>
<script src="../../res/layui/layui.js"></script>
<script>
layui.cache.page = 'user';
$("#captch").click(function () {
this.src = "/captcha.jpg"
})
</script>
</@layout>
编写跳转的Controller可以进行页面的跳转
@GetMapping("/login")
public String login() {
return "/auth/login";
}
@GetMapping("/register")
public String register() {
return "/auth/reg";
}
//生成验证码
@Autowired
Producer producer;
@GetMapping("/captcha.jpg")
public void kaptcha(HttpServletResponse response) throws IOException {
// 生成验证
String text = producer.createText();
// 生成图片
BufferedImage image = producer.createImage(text);
// 设置到session 会话属性
request.getSession().setAttribute(KAPTCHA_SESSION, text);
// 设置基本的信息
response.setHeader("Cache-Control", "no-store,no-cache");
response.setContentType("image/jpeg");
ServletOutputStream outputStream = response.getOutputStream();
ImageIO.write(image, "jpg", outputStream);
}
前端获取这个Get路径就可以
验证码成功后开始编写注册的功能。这里有一个需要注意的是如何保证输入的验证码和生成的验证码一样,上面我们先存到了session,等集成shiro 后在修改暂时放到了session里面
@PostMapping("/register")
@ResponseBody
public Result doreg(MUser user, String repass, String vercode) {
// 判断检验实体类
ValidationUtil.ValidResult validResult = ValidationUtil.validateBean(user);
if (validResult.hasErrors()) {
return Result.fail(validResult.getErrors());
}
//判断两次输入的密码是否正确
if (!user.getPassword().equals(repass)) {
return Result.fail("两次密码不正确");
}
// 获取到 用户的输入的验证码
// 获取到session 会话属性
String captcha = (String) request.getSession().getAttribute(KAPTCHA_SESSION);
// 判断验证码是否
if (vercode == null || !vercode.equalsIgnoreCase(captcha)) {
return Result.fail("验证码不一致");
}
// 注册的方法
Result result =userService.register(user);
return result.success().action("/login");
}
这里使用到了两个工具类,结果集和实体校验的工具类,都是网上找的代码就不复制的
服务类MUserServiceImpl
/**
*
* 服务实现类
*
*
* @author fjj
* @since 2021-07-23
*/
@Service
public class MUserServiceImpl extends ServiceImpl<MUserMapper, MUser> implements IMUserService {
// 注入 Mapper
@Autowired
MUserMapper userMapper;
@Override
public Result register(MUser user) {
// 判断是不是唯一的
int count = this.count(new QueryWrapper<MUser>().eq("email", user.getEmail()).or().eq("username", user.getUsername()));
if (count > 0)
Result.fail("用户名或者邮箱已经存在了,建议换个名字啦");
// 创建实体类 只写我们需要注册的字段
MUser temp = new MUser();
temp.setUsername(user.getUsername());
// 密码需要MD5 加密
temp.setPassword(SecureUtil.md5(user.getPassword()));
temp.setEmail(user.getEmail());
temp.setCreated(new Date());
temp.setPoint(0);
temp.setVipLevel(0);
temp.setCommentCount(0);
temp.setPostCount(0);
temp.setAvatar("/res/images/avatar/default.png");
this.save(temp);
return Result.success();
}
}
注册流程结束
登录的流程
前面有登录的login的ftl就不粘贴过来了
Controller 类
@PostMapping("/login")
@ResponseBody
public Result dologin(String email,String password,String vercode) {
// 判断 用户名和密码 是不是为空
if (StrUtil.isEmpty(email )|| StrUtil.isBlank(password)) {
return Result.fail("不能为空");
}
// 获取到 token
UsernamePasswordToken token = new UsernamePasswordToken(email, SecureUtil.md5(password));
// 获取到 用户的输入的验证码
// 获取到session 会话属性
String captcha = (String) request.getSession().getAttribute(KAPTCHA_SESSION);
// 判断验证码是否
if (vercode == null || !vercode.equalsIgnoreCase(captcha)) {
return Result.fail("验证码不一致");
}
try {
SecurityUtils.getSubject().login(token);
} catch (AuthenticationException e) {
if (e instanceof UnknownAccountException) {
return Result.fail("用户不存在");
} else if (e instanceof LockedAccountException) {
return Result.fail("用户被禁用");
} else if (e instanceof IncorrectCredentialsException) {
return Result.fail("密码错误");
} else {
return Result.fail("用户认证失败");
}
}
return Result.success().action("/");
}
这里用到了Shiro的所以要配置一下Shiro的配置文件securityManager
@Slf4j
@Configuration
public class ShiroConfig {
@Bean
public SecurityManager securityManager(AccountRealm accountRealm){
DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
securityManager.setRealm(accountRealm);
log.info("------------------>securityManager注入成功");
return securityManager;
}
@Bean
public ShiroFilterFactoryBean shiroFilterFactoryBean(SecurityManager securityManager) {
ShiroFilterFactoryBean filterFactoryBean = new ShiroFilterFactoryBean();
filterFactoryBean.setSecurityManager(securityManager);
// 配置登录的url和登录成功的url
filterFactoryBean.setLoginUrl("/login");
filterFactoryBean.setSuccessUrl("/user/center");
// 配置未授权跳转页面
filterFactoryBean.setUnauthorizedUrl("/error/403");
// filterFactoryBean.setFilters(MapUtil.of("auth", authFilter()));
Map<String, String> hashMap = new LinkedHashMap<>();
hashMap.put("/res/**", "anon");
hashMap.put("/user/home", "auth");
hashMap.put("/user/set", "auth");
hashMap.put("/user/upload", "auth");
hashMap.put("/user/index", "auth");
hashMap.put("/user/public", "auth");
hashMap.put("/user/collection", "auth");
hashMap.put("/user/mess", "auth");
hashMap.put("/msg/remove/", "auth");
hashMap.put("/message/nums/", "auth");
hashMap.put("/collection/remove/", "auth");
hashMap.put("/collection/find/", "auth");
hashMap.put("/collection/add/", "auth");
hashMap.put("/post/edit", "auth");
hashMap.put("/post/submit", "auth");
hashMap.put("/post/delete", "auth");
hashMap.put("/post/reply/", "auth");
hashMap.put("/websocket", "anon");
hashMap.put("/login", "anon");
return filterFactoryBean;
}
重写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()));
SimpleAuthenticationInfo info = new SimpleAuthenticationInfo(profile, token.getCredentials(), getName());
return info;
}
}
重写AccountProfile实体类用来反射字段使用的
@Data
public class AccountProfile implements Serializable {
private Long id;
private String username;
private String email;
private String sign;
private String avatar;
private String gender;
private Date created;
public String getSex() {
return "0".equals(gender) ? "女" : "男";
}
}
最后就是服务类了
// 登录
@Override
public AccountProfile login(String email, String password) {
MUser user1 = this.getOne(new QueryWrapper<MUser>().eq("email", email));
if (user1 == null) {
if (user1 == null) {
throw new UnknownAccountException();
}
if (!user1.getPassword().equals(user1.getPassword())) {
throw new IncorrectCredentialsException();
}
user1.setLasted(new Date());
this.updateById(user1);
}
AccountProfile profile = new AccountProfile();
BeanUtil.copyProperties(user1,profile);
return profile;
}
之上所以的登录基本流程 over了
上面是登录前的,理想的状态应该是登录后显示每个人的基本信息
这里要用到一个SpringBoot 跟Shiro的一个标签
主要的标签博客
修改我们的header.ftl的页面
未登录用这个标签
登录之后的
// 退出登录页面
@RequestMapping("/logout")
public String logout() {
// 清楚 session
SecurityUtils.getSubject().logout();
return "redirect:/";
}
先把写Controller 类
@Controller
public class UserController extends BaseController {
@GetMapping("/user/home")
public String home() {
// 获取到当前的 user
MUser user = userService.getById(getProfilrId());
// 获取到 该用户的基本信息
List<MPost> posts = postService.list(new QueryWrapper<MPost>()
.eq("user_id", getProfilrId())
// 获取 30 天内的文章
.gt("created", DateUtil.lastMonth())
.orderByDesc("created")
);
request.setAttribute("user",user);
request.setAttribute("posts",posts);
// 设置到缓存
return "/user/home";
}
}
事先在BaseController 里面写入获取到Shiro id的方法
// 获取到 shiro 里面的 用户 id
public AccountProfile getProfile () {
return (AccountProfile) SecurityUtils.getSubject().getPrincipal();
}
protected Long getProfilrId () {
return getProfile().getId();
}
前端页面
<#include "../inc/layout.ftl" />
<@layout "我的主页">
<div class="fly-home fly-panel" style="background-image: url();">
<img src="${user.avatar}" alt="${user.username}">
<i class="iconfont icon-renzheng" title="Fly社区认证"></i>
<h1>
${user.username}
<i class="iconfont icon-nan"></i>
<!-- <i class="iconfont icon-nv"></i> -->
<i class="layui-badge fly-badge-vip">VIP3</i>
<!--
<span style="color:#c00;">(管理员)</span>
<span style="color:#5FB878;">(社区之光)</span>
<span>(该号已被封)</span>
-->
</h1>
<p class="fly-home-info">
<#-- <i class="iconfont icon-kiss" title="飞吻"></i><span style="color: #FF7200;">66666 飞吻</span>-->
<i class="iconfont icon-shijian"></i><span> ${timeAgo(user.created)}加入</span>
<i class="iconfont icon-chengshi"></i><span>${user.address}</span>
</p>
<p class="fly-home-sign">(${user.sign!'这个人好懒,什么都没留下!'})</p>
<#-- <div class="fly-sns" data-user="">-->
<#-- <a href="javascript:;" class="layui-btn layui-btn-primary fly-imActive" data-type="addFriend">加为好友</a>-->
<#-- <a href="javascript:;" class="layui-btn layui-btn-normal fly-imActive" data-type="chat">发起会话</a>-->
<#-- </div>-->
</div>
<div class="layui-container">
<div class="layui-row layui-col-space15">
<div class="layui-col-md6 fly-home-jie">
<div class="fly-panel">
<h3 class="fly-panel-title">${user.username} 最近的提问</h3>
<ul class="jie-row">
<#list posts as post>
<li>
<#if post.recommend><span class="fly-jing">精</span></#if>
<a href="/post/${post.id}" class="jie-title"> ${post.title}</a>
<i>${timeAgo(post.created)}</i>
<em class="layui-hide-xs">${post.viewCount}阅/${post.commentCount}答</em>
</li>
</#list>
<#if posts??>
<div class="fly-none" style="min-height: 50px; padding:30px 0; height:auto;">
<i style="font-size:14px;">没有发表任何求解</i>
</div>
</#if>
</ul>
</div>
</div>
<div class="layui-col-md6 fly-home-da">
<div class="fly-panel">
<h3 class="fly-panel-title">${user.username} 最近的回答</h3>
<ul class="home-jieda">
<div class="fly-none" style="min-height: 50px; padding:30px 0; height:auto;"><span>没有回答任何问题</span></div>
</ul>
</div>
</div>
</div>
</div>
</@layout>
先修改我们的页面,把Set.ftl 页面复制过来
<#include "../inc/layout.ftl" />
<@layout "基本设置">
<div class="layui-container fly-marginTop fly-user-main">
<ul class="layui-nav layui-nav-tree layui-inline" lay-filter="user">
<li class="layui-nav-item">
<a href="/user/home">
<i class="layui-icon"></i>
我的主页
</a>
</li>
<#-- <li class="layui-nav-item">-->
<#-- <a href="index.html">-->
<#-- <i class="layui-icon"></i>-->
<#-- 用户中心-->
<#-- </a>-->
<#-- </li>-->
<li class="layui-nav-item layui-this">
<a href="/user/set">
<i class="layui-icon"></i>
基本设置
</a>
</li>
<li class="layui-nav-item">
<a href="/user/mess">
<i class="layui-icon"></i>
我的消息
</a>
</li>
</ul>
<div class="site-tree-mobile layui-hide">
<i class="layui-icon"></i>
</div>
<div class="site-mobile-shade"></div>
<div class="site-tree-mobile layui-hide">
<i class="layui-icon"></i>
</div>
<div class="site-mobile-shade"></div>
<div class="fly-panel fly-panel-user" pad20>
<div class="layui-tab layui-tab-brief" lay-filter="user">
<ul class="layui-tab-title" id="LAY_mine">
<li class="layui-this" lay-id="info">我的资料</li>
<li lay-id="avatar">头像</li>
<li lay-id="pass">密码</li>
</ul>
<div class="layui-tab-content" style="padding: 20px 0;">
<div class="layui-form layui-form-pane layui-tab-item layui-show">
<form method="post">
<div class="layui-form-item">
<label for="L_email" class="layui-form-label">邮箱</label>
<div class="layui-input-inline">
<input type="text" id="L_email" name="email" required lay-verify="email" autocomplete="off" value="${user.email}" class="layui-input" readonly>
</div>
<div class="layui-form-mid layui-word-aux">如果您在邮箱已激活的情况下,变更了邮箱,需<a href="activate.html" style="font-size: 12px; color: #4f99cf;">重新验证邮箱</a>。</div>
</div>
<div class="layui-form-item">
<label for="L_username" class="layui-form-label">昵称</label>
<div class="layui-input-inline">
<input type="text" id="L_username" name="username" required lay-verify="required" value="${user.username}" autocomplete="off" value="" class="layui-input">
</div>
<div class="layui-inline">
<div class="layui-input-inline">
<input type="radio" name="sex" value="0" <#if user.gender =='0'>checked</#if> title="男">
<input type="radio" name="sex" value="1" <#if user.gender =='1'>checked</#if> title="女">
</div>
</div>
</div>
<div class="layui-form-item">
<label for="L_city" class="layui-form-label">城市</label>
<div class="layui-input-inline">
<input type="text" id="L_city" name="address" autocomplete="off" value="${user.address}" class="layui-input">
</div>
</div>
<div class="layui-form-item layui-form-text">
<label for="L_sign" class="layui-form-label">签名</label>
<div class="layui-input-block">
<textarea placeholder="随便写些什么刷下存在感" id="L_sign" name="sign" autocomplete="off" class="layui-textarea" style="height: 80px;">${user.sign}</textarea>
</div>
</div>
<div class="layui-form-item">
<button class="layui-btn" key="set-mine" lay-filter="*" lay-submit alert="true" reload="true">确认修改</button>
</div>
</form>
</div>
<div class="layui-form layui-form-pane layui-tab-item">
<div class="layui-form-item">
<div class="avatar-add">
<p>建议尺寸168*168,支持jpg、png、gif,最大不能超过50KB</p>
<button type="button" class="layui-btn upload-img">
<i class="layui-icon"></i>上传头像
</button>
<img src="https://tva1.sinaimg.cn/crop.0.0.118.118.180/5db11ff4gw1e77d3nqrv8j203b03cweg.jpg">
<span class="loading"></span>
</div>
</div>
</div>
<div class="layui-form layui-form-pane layui-tab-item">
<form action="/user/repass" method="post">
<div class="layui-form-item">
<label for="L_nowpass" class="layui-form-label">当前密码</label>
<div class="layui-input-inline">
<input type="password" id="L_nowpass" name="nowpass" required lay-verify="required" autocomplete="off" class="layui-input">
</div>
</div>
<div class="layui-form-item">
<label for="L_pass" class="layui-form-label">新密码</label>
<div class="layui-input-inline">
<input type="password" id="L_pass" name="pass" required lay-verify="required" autocomplete="off" class="layui-input">
</div>
<div class="layui-form-mid layui-word-aux">6到16个字符</div>
</div>
<div class="layui-form-item">
<label for="L_repass" class="layui-form-label">确认密码</label>
<div class="layui-input-inline">
<input type="password" id="L_repass" name="repass" required lay-verify="required" autocomplete="off" class="layui-input">
</div>
</div>
<div class="layui-form-item">
<button class="layui-btn" key="set-mine" lay-filter="*" lay-submit>确认修改</button>
</div>
</form>
</div>
</div>
</div>
</div>
</div>
<script>
layui.cache.page = 'user';
</script>
</@layout>
修改后端的Controller类
// 基本设置
@GetMapping ("/user/set")
public String set () {
// 获取到当前的 user
MUser user1 = userService.getById(getProfilrId());
request.setAttribute("user", user1);
return "/user/set";
}
@PostMapping("/user/set")
@ResponseBody
public Result set(MUser user) {
//判断名字是否为空
if (StrUtil.isBlank(user.getUsername())) {
return Result.fail("昵称为空了");
}
// 判断 是否名字被占用了
int count = userService.count(new QueryWrapper<MUser>()
.eq("username", getProfile().getUsername())
.ne("id", getProfilrId()));
if (count >0) {
return Result.fail("名字已经存在");
}
MUser temp = userService.getById(getProfilrId());
temp.setUsername(user.getUsername());
temp.setGender(user.getGender());
temp.setSign(user.getSign());
temp.setAddress(user.getAddress());
userService.updateById(temp);
AccountProfile profile = getProfile();
profile.setAddress(temp.getAddress());
profile.setUsername(temp.getUsername());
profile.setAvatar(temp.getAvatar());
profile.setGender(temp.getGender());
profile.setSign(temp.getSign());
return Result.success().action("/user/set#info");
}
前端页面
<div class="layui-form layui-form-pane layui-tab-item">
<div class="layui-form-item">
<div class="avatar-add">
<p>建议尺寸168*168,支持jpg、png、gif,最大不能超过50KB</p>
<button type="button" class="layui-btn upload-img">
<i class="layui-icon"></i>上传头像
</button>
<img src="${user.avatar}">
<span class="loading"></span>
</div>
</div>
</div>
需要用到上传工具类
@Slf4j
@Component
public class UploadUtil {
@Autowired
Consts consts;
public final static String type_avatar = "avatar";
public Result upload(String type, MultipartFile file) throws IOException {
if(StrUtil.isBlank(type) || file.isEmpty()) {
return Result.fail("上传失败");
}
// 获取文件名
String fileName = file.getOriginalFilename();
log.info("上传的文件名为:" + fileName);
// 获取文件的后缀名
String suffixName = fileName.substring(fileName.lastIndexOf("."));
log.info("上传的后缀名为:" + suffixName);
// 文件上传后的路径
String filePath = consts.getUploadDir();
if ("avatar".equalsIgnoreCase(type)) {
AccountProfile profile = (AccountProfile) SecurityUtils.getSubject().getPrincipal();
fileName = "/avatar/avatar_" + profile.getId() + suffixName;
} else if ("post".equalsIgnoreCase(type)) {
fileName = "/post/post_" + DateUtil.format(new Date(), DatePattern.PURE_DATETIME_MS_PATTERN) + suffixName;
}
File dest = new File(filePath + fileName);
// 检测是否存在目录
if (!dest.getParentFile().exists()) {
dest.getParentFile().mkdirs();
}
try {
file.transferTo(dest);
log.info("上传成功后的文件路径未:" + filePath + fileName);
String path = filePath + fileName;
String url = "/upload" + fileName;
log.info("url ---> {}", url);
return Result.success(url);
} catch (IllegalStateException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
return Result.success(null);
}
}
控制层
@PostMapping("/user/set")
@ResponseBody
public Result set(MUser user) {
// 这里写上传头像的路径 由于layui 的js 封装的 /user/set
if (StrUtil.isNotBlank(user.getAvatar())) {
// 获取到当前用户
MUser user1 = userService.getById(getProfilrId());
user1.setAvatar(user.getAvatar());
userService.updateById(user1);
AccountProfile profile = getProfile();
profile.setAvatar(user.getAvatar());
return Result.success().action("/user/set#avatar");
}
//判断名字是否为空
if (StrUtil.isBlank(user.getUsername())) {
return Result.fail("昵称为空了");
}
// 判断 是否名字被占用了
int count = userService.count(new QueryWrapper<MUser>()
.eq("username", getProfile().getUsername())
.ne("id", getProfilrId()));
if (count > 0) {
return Result.fail("名字已经存在");
}
MUser temp = userService.getById(getProfilrId());
temp.setUsername(user.getUsername());
temp.setGender(user.getGender());
temp.setSign(user.getSign());
temp.setAddress(user.getAddress());
userService.updateById(temp);
AccountProfile profile = getProfile();
profile.setAddress(temp.getAddress());
profile.setUsername(temp.getUsername());
profile.setAvatar(temp.getAvatar());
profile.setGender(temp.getGender());
profile.setSign(temp.getSign());
return Result.success().action("/user/set#info");
}
// 上传头像
@PostMapping("/user/upload")
@ResponseBody
public Result upload(@RequestParam(value = "file") MultipartFile file) throws Exception {
return uploadUtil.upload(UploadUtil.type_avatar, file);
}
前端页面
<form action="/user/repass" method="post">
<div class="layui-form-item">
<label for="L_nowpass" class="layui-form-label">当前密码</label>
<div class="layui-input-inline">
<input type="password" id="L_nowpass" name="nowpass" required lay-verify="required" autocomplete="off" class="layui-input">
</div>
</div>
<div class="layui-form-item">
<label for="L_pass" class="layui-form-label">新密码</label>
<div class="layui-input-inline">
<input type="password" id="L_pass" name="pass" required lay-verify="required" autocomplete="off" class="layui-input">
</div>
<div class="layui-form-mid layui-word-aux">6到16个字符</div>
</div>
<div class="layui-form-item">
<label for="L_repass" class="layui-form-label">确认密码</label>
<div class="layui-input-inline">
<input type="password" id="L_repass" name="repass" required lay-verify="required" autocomplete="off" class="layui-input">
</div>
</div>
<div class="layui-form-item">
<button class="layui-btn" key="set-mine" lay-filter="*" lay-submit alert="true" reload="true">确认修改</button>
</div>
</form>
控制层
// 修改 密码
@ResponseBody
@PostMapping("/user/repass")
public Result repass(String nowpass, String pass, String repass) {
if(!pass.equals(repass)) {
return Result.fail("两次密码不相同");
}
MUser user = userService.getById(getProfilrId());
String nowPassMd5 = SecureUtil.md5(nowpass);
if(!nowPassMd5.equals(user.getPassword())) {
return Result.fail("密码不正确");
}
user.setPassword(SecureUtil.md5(pass));
userService.updateById(user);
return Result.success().action("/user/set#pass");
}
前端的页面
<#include "../inc/layout.ftl" />
<@layout "用户中心">
<div class="layui-container fly-marginTop fly-user-main">
<@centerLeft level=1></@centerLeft>
<div class="site-tree-mobile layui-hide">
<i class="layui-icon"></i>
</div>
<div class="site-mobile-shade"></div>
<div class="site-tree-mobile layui-hide">
<i class="layui-icon"></i>
</div>
<div class="site-mobile-shade"></div>
<div class="fly-panel fly-panel-user" pad20>
<!--
<div class="fly-msg" style="margin-top: 15px;">
您的邮箱尚未验证,这比较影响您的帐号安全,<a href="activate.html">立即去激活?</a>
</div>
-->
<div class="layui-tab layui-tab-brief" lay-filter="user">
<ul class="layui-tab-title" id="LAY_mine">
<li data-type="mine-jie" lay-id="index" class="layui-this">我发的帖(<span>89</span>)</li>
<li data-type="collection" data-url="/collection/find/" lay-id="collection">我收藏的帖(<span>16</span>)</li>
</ul>
<div class="layui-tab-content" style="padding: 20px 0;">
<div class="layui-tab-item layui-show">
<ul class="mine-view jie-row" id="fabu">
<script id="tpl-fabu" type="text/html">
<li>
<a class="jie-title" href="/detail/{{d.id}}" target="_blank">{{ d.title }}</a>
<i>{{layui.util.toDateString(d.created, 'yyyy-MM-dd HH:mm:ss')}}</i>
<a class="mine-edit" href="/post/edit?id={{d.id}}">编辑</a>
<em>{{d.viewCount}}阅/{{d.commentCount}}答</em>
</li>
</script>
</ul>
<div id="LAY_page"></div>
</div>
<div class="layui-tab-item">
<ul class="mine-view jie-row" id="collection">
<script id="tpl-collection" type="text/html">
<li>
<a class="jie-title" href="/detail/{{d.id}}" target="_blank">{{d.title}}</a>
<i>收藏于{{layui.util.timeAgo(d.created, true)}}</i>
</li>
</script>
</ul>
<div id="LAY_page1"></div>
</div>
</div>
</div>
</div>
</div>
<script>
layui.cache.page = 'user';
layui.use(['laytpl', 'flow', 'util'], function() {
var $ = layui.jquery;
var laytpl = layui.laytpl;
var flow = layui.flow;
var util = layui.util;
flow.load({
elem: '#fabu' //指定列表容器
,isAuto: false
,done: function(page, next){
var lis = [];
$.get('/user/public?start='+page, function(res){
layui.each(res.data.records, function(index, item){
var tpl = $("#tpl-fabu").html();
laytpl(tpl).render(item, function (html) {
$("#fabu .layui-flow-more").before(html);
});
});
next(lis.join(''), page < res.data.pages);
});
}
});
flow.load({
elem: '#collection'
,isAuto: false
,done: function(page, next){
var lis = [];
$.get('/user/collection?pn='+page, function(res){
layui.each(res.data.records, function(index, item){
var tpl = $("#tpl-collection").html();
laytpl(tpl).render(item, function (html) {
$("#collection .layui-flow-more").before(html);
});
});
next(lis.join(''), page < res.data.pages);
});
}
});
});
</script>
</@layout>
后端的Controller
// 发表的文章
@GetMapping("/user/public")
@ResponseBody
public Result PublicFaBu() {
// 获取到分页信息
IPage page = postService.page(getPage(), new QueryWrapper<MPost>()
.eq("user_id", getProfilrId())
.orderByDesc("created"));
return Result.success(page);
}
// 收藏的帖子
@GetMapping("/user/collection")
@ResponseBody
public Result Collection() {
IPage page = postService.page(getPage(), new QueryWrapper<MPost>()
.inSql("id", "select post_id from m_user_collection where user_id = " + getProfilrId())
);
return Result.success(page);
}
我的消息先导入前端的页面
<#include "../inc/layout.ftl" />
<@layout "用户中心">
<div class="layui-container fly-marginTop fly-user-main">
<@centerLeft level=3></@centerLeft>
<div class="site-tree-mobile layui-hide">
<i class="layui-icon"></i>
</div>
<div class="site-mobile-shade"></div>
<div class="site-tree-mobile layui-hide">
<i class="layui-icon"></i>
</div>
<div class="site-mobile-shade"></div>
<div class="fly-panel fly-panel-user" pad20>
<div class="layui-tab layui-tab-brief" lay-filter="user" id="LAY_msg" style="margin-top: 15px;">
<button class="layui-btn layui-btn-danger" id="LAY_delallmsg">清空全部消息</button>
<div id="LAY_minemsg" style="margin-top: 10px;">
<!--<div class="fly-none">您暂时没有最新消息</div>-->
<ul class="mine-msg">
<#list pageData.records as mess>
<li data-id="${mess.id}">
<blockquote class="layui-elem-quote">
<#if mess.type == 0>
系统消息:${mess.content}
</#if>
<#if mess.type == 1>
${mess.fromUserName} 评论了你的文章 <${mess.postTitle}>,内容是 (${mess.content})
</#if>
<#if mess.type == 2>
${mess.fromUserName} 回复了你的评论 (${mess.content}),文章是 <${mess.postTitle}>
</#if>
</blockquote>
<p><span>${timeAgo(mess.creted)}</span><a href="javascript:;" class="layui-btn layui-btn-small layui-btn-danger fly-delete">删除</a></p>
</li>
</#list>
</ul>
<@paging pageData></@paging>
</div>
</div>
</div>
</div>
<script>
layui.cache.page = 'user';
</script>
</@layout>
开始写Controller这里我们用的自己的方法,所以要自己写sql
// 基本设置消息
@GetMapping("/user/mess")
public String Mess1() {
IPage page = userMessageService.pageing(getPage(), new QueryWrapper<MUserMessage>()
.eq("to_user_id", getProfilrId())
.orderByAsc("created")
);
request.setAttribute("pageData", page);
return "/user/mess";
}
pageing方法
/**
*
* 服务实现类
*
*
* @author fjj
* @since 2021-07-23
*/
@Service
public class MUserMessageServiceImpl extends ServiceImpl<MUserMessageMapper, MUserMessage> implements IMUserMessageService {
@Autowired
MUserMessageMapper mUserMessageMapper;
@Override
public IPage pageing(Page page, QueryWrapper<MUserMessage> wrapper) {
return mUserMessageMapper.selectMessages(page,wrapper);
}
}
Mapperxml
DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.example.springbootblog.mapper.MUserMessageMapper">
<select id="selectMessages" resultType="com.example.springbootblog.vo.UserMessageVo">
SELECT
m.*, (
SELECT
username
FROM
`m_user`
WHERE
id = m.from_user_id
) AS fromUserName,
(
SELECT
title
FROM
`m_post`
WHERE
id = m.post_id
) AS postTitle
FROM
`m_user_message` m
${ew.customSqlSegment}
select>
mapper>
一定要记得在Mapper 里面写上一个注解
/**
*
* Mapper 接口
*
*
* @author fjj
* @since 2021-07-23
*/
@Mapper
public interface MUserMessageMapper extends BaseMapper<MUserMessage> {
IPage<UserMessageVo> selectMessages(Page page, @Param(Constants.WRAPPER) QueryWrapper<MUserMessage> wrapper);
}
因为可能原来的实体类没有办法帮我们映射完所以新写了一个VO
@Data
public class UserMessageVo extends MUserMessage {
private String toUserName;
private String fromUserName;
private String postTitle;
private String commentContent;
}
// 删除消息功能
@PostMapping("/msg/remove/")
@ResponseBody
public Result msgRemove(Long id, @RequestParam(defaultValue = "false") boolean all) {
boolean remove = userMessageService.remove(new QueryWrapper<MUserMessage>()
.eq("to_user_id", getProfilrId())
.eq(!all, "id", id));
return remove ? Result.success(null) : Result.fail("删除失败了哦!");
}
续篇