项目介绍
该博客是基于ssm实现的个人博客系统,适合初学ssm和制作个人博客的通过学习。项目分为用户模块、文章模块、分类模块、评论模块、链接模块、菜单模块、公告模块、页面模块、标签模块、网站信息模块。
其权限分为普通用户和管理员用户,普通用户其主要功能包括用户注册、用户登录、修改密码、忘记密码、修改个人信息、游览文章、查找文章、删除个人文章、编辑个人文章、发表文章、查看评论,管理员等。管理员用户拥有普通用户的所有功能,额外还有管理用户、管理分类、管理链接、管理菜单、管理公告、管理页面、管理标签、管理网站信息等功能。
项目非完全分离式项目,前端大体使用JSP+LayUi+javascript架构,数据交互使用jsp模板渲染和json。后端使用MVC架构设计模式(SSM框架),将整个系统划分为View层,Controller层,Service层,DAO层四层,使用Spring MVC负责请求的转发和视图管理,Spring实现业务对象管理,Mybatis作为数据对象的持久化引擎。
其涉及技术包括:
Spring +springMVC +Mybatis +MySQL8.x +JWT +Redis +Mybatis-Plus +pagehelper +JSP +rapid +JavaScript +hutool +LayUi +json +cookie +Nginx +Tomcat
项目数据库
er图
概念模型图
物理模型图
项目环境
搭建ssm环境
如何搭建ssm环境我就不介绍了
集成redis
-
导入依赖 <!-- redis-->
<dependency>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-redis</artifactId>
<version>2.1.8.RELEASE</version>
</dependency>
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
<version>2.9.0</version>
</dependency>
-
redis.properties #redis的ip
redis.host=120.79.188.192
#端口号
redis.port=6379
#密码
redis.pass=********
#连接池空闲最大连接数(使用负值表示没有限制)
redis.maxIdle=20
#连接池空闲最小连接数
redis.minIdle=0
#连接池最大连接数(使用负值表示没有限制)
redis.maxActive=100
#连接池最大阻塞时间(使用负值表示没有限制)
redis.maxWait=-1
#使用连接时,检测连接是否成功
redis.testOnBorrow=true
#设置库号,数据库索引
redis.database=1
#设置连接超时时间
redis.timeout=3000
-
redis-context.xml <?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:p="http://www.springframework.org/schema/p"
xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.3.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-4.3.xsd">
<!-- scanner redis properties -->
<context:property-placeholder location="classpath:redis.properties" ignore-unresolvable="true"/>
<!--(1)如果你有多个数据源需要通过<context:property-placeholder管理,且不愿意放在一个配置文件里,那么一定要加上ignore-unresolvable=“true"-->
<!--(2)注意新版的(具体从哪个版本开始不清楚,有兴趣可以查一下)JedisPoolConfig的property name,不是maxActive而是maxTotal,而且没有maxWait属性,建议看一下Jedis源码。-->
<!-- redis连接池 -->
<bean id="jedisConfig" class="redis.clients.jedis.JedisPoolConfig">
<property name="maxTotal" value="${redis.maxActive}"></property>
<property name="maxIdle" value="${redis.maxIdle}"></property>
<property name="minIdle" value="${redis.minIdle}"></property>
<property name="maxWaitMillis" value="${redis.maxWait}"></property>
<property name="testOnBorrow" value="${redis.testOnBorrow}"></property>
</bean>
<!-- redis连接工厂 -->
<bean id="connectionFactory" class="org.springframework.data.redis.connection.jedis.JedisConnectionFactory">
<property name="hostName" value="${redis.host}"></property>
<property name="port" value="${redis.port}"></property>
<property name="password" value="${redis.pass}"></property>
<property name="database" value="${redis.database}"/>
<property name="poolConfig" ref="jedisConfig"></property>
<property name="timeout" value="${redis.timeout}"/>
</bean>
<!-- redis操作模板,这里采用尽量面向对象的模板 -->
<bean id="redisTemplate" class="org.springframework.data.redis.core.StringRedisTemplate">
<property name="connectionFactory" ref="connectionFactory"/>
<!-- 如果不配置Serializer,那么存储的时候只能使用String,如果用对象类型存储,那么会提示错误 can't cast to String!!!-->
<property name="keySerializer">
<bean class="org.springframework.data.redis.serializer.StringRedisSerializer"/>
</property>
<property name="hashKeySerializer">
<bean class="org.springframework.data.redis.serializer.StringRedisSerializer"/>
</property>
<!-- <property name="valueSerializer">-->
<!-- <bean class="org.springframework.data.redis.serializer.JdkSerializationRedisSerializer"/>-->
<!-- </property>-->
<!-- <property name="hashValueSerializer">-->
<!-- <bean class="org.springframework.data.redis.serializer.JdkSerializationRedisSerializer"/>-->
<!-- </property>-->
<!--开启事务-->
<property name="enableTransactionSupport" value="true"/>
</bean>
</beans>
-
导入redis工具类(内容太多,需要的可以在我博客搜索)
集成JWT
-
导入依赖
<dependency>
<groupId>com.auth0</groupId>
<artifactId>java-jwt</artifactId>
<version>3.4.0</version>
</dependency>
-
使用工具类 import com.auth0.jwt.JWT;
import com.auth0.jwt.JWTCreator;
import com.auth0.jwt.algorithms.Algorithm;
import com.auth0.jwt.interfaces.DecodedJWT;
import java.util.Calendar;
import java.util.Map;
public class JWTUtils {
private static final String SIGN="!qwe@[wef";
public static String getToken(Map<String,String> map){
Calendar instance = Calendar.getInstance();
instance.add(Calendar.DATE,7);
JWTCreator.Builder builder = JWT.create();
map.forEach((k,v)->{builder.withClaim(k,v);});
String token = builder.withExpiresAt(instance.getTime())
.sign(Algorithm.HMAC256(SIGN));
return token;
}
public static DecodedJWT verify(String token){
return JWT.require(Algorithm.HMAC256(SIGN)).build().verify(token);
}
public static DecodedJWT getTokenInfo(String token){
DecodedJWT verify = JWT.require(Algorithm.HMAC256(SIGN)).build().verify(token);
return verify;
}
}
-
配置拦截器类 import com.auth0.jwt.exceptions.AlgorithmMismatchException;
import com.auth0.jwt.exceptions.SignatureVerificationException;
import com.auth0.jwt.exceptions.TokenExpiredException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.lzj.utils.CookieUtil;
import com.lzj.utils.JWTUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.http.HttpMethod;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.HashMap;
@Component
public class JWTInterceptor implements HandlerInterceptor {
@Autowired
private RedisTemplate redisTemplate;
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
System.out.println("进入了JWT拦截器");
if (HttpMethod.OPTIONS.toString().equals(request.getMethod())){
System.out.println("OPTIONS请求,放行");
return true;
}
Cookie uid = CookieUtil.getCookieByName(request, "uid");
if(uid==null){
response.sendRedirect("/login");
return false;
}
HashMap<String, Object> map = new HashMap<>();
String cookieUid = uid.getValue();
String token = (String) redisTemplate.opsForValue().get(cookieUid);
try {
JWTUtils.verify(token);
return true;
}catch (SignatureVerificationException e){
e.printStackTrace();
map.put("msg","无效签名");
}catch (TokenExpiredException e){
e.printStackTrace();
map.put("msg","token过期");
}catch (AlgorithmMismatchException e){
e.printStackTrace();
map.put("msg","token算法不一致");
}catch (Exception e){
e.printStackTrace();
map.put("msg","token无效");
}
map.put("state",false);
String json = new ObjectMapper().writeValueAsString(map);
response.setContentType("application/json;charset=UTF-8");
response.getWriter().print(json);
CookieUtil.removeCookie(response,"uid");
response.sendRedirect("/login");
return false;
}
}
-
将拦截器配置到spring容器中 <mvc:interceptors>
<mvc:interceptor>
<mvc:mapping path="/**"/>
<mvc:exclude-mapping path="/"/>
<mvc:exclude-mapping path="/login"/>
<mvc:exclude-mapping path="/register"/>
<mvc:exclude-mapping path="/registerSubmit"/>
<mvc:exclude-mapping path="/loginVerify"/>
<mvc:exclude-mapping path="/css/**"/>
<mvc:exclude-mapping path="/img/**"/>
<mvc:exclude-mapping path="/js/**"/>
<mvc:exclude-mapping path="/plugin/**"/>
<mvc:exclude-mapping path="/Error/**"/>
<mvc:exclude-mapping path="/article/**"/>
<mvc:exclude-mapping path="/category/**"/>
<mvc:exclude-mapping path="/tag/**"/>
<mvc:exclude-mapping path="/user/**"/>
<mvc:exclude-mapping path="/search"/>
<bean class="com.lzj.interceptors.JWTInterceptor"/>
</mvc:interceptor>
</mvc:interceptors>
集成OSS对象存储
-
导入依赖
<dependency>
<groupId>com.aliyun.oss</groupId>
<artifactId>aliyun-sdk-oss</artifactId>
<version>3.5.0</version>
</dependency>
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>1.3.2</version>
</dependency>
<dependency>
<groupId>commons-fileupload</groupId>
<artifactId>commons-fileupload</artifactId>
<version>1.2.1</version>
</dependency>
-
编写工具类 import com.aliyun.oss.OSS;
import com.aliyun.oss.OSSClientBuilder;
import com.aliyun.oss.model.ObjectMetadata;
import java.io.InputStream;
import java.net.URL;
import java.util.Date;
public class OssManager {
private static final String accessKeyId = "LTAI";
private static final String accessKeySecret = "I75";
private static final String endpoint = "http://oss-cn-hangzhou.aliyuncs.com";
public static final String bucket = "********";
public static final String bucketDomain="https://********.oss-cn-hangzhou.aliyuncs.com";
private static OSS client;
static {
client = new OSSClientBuilder().build(endpoint, accessKeyId, accessKeySecret);
}
public static void uploadImage(String fileName, long length, InputStream content) {
uploadBucketImage(bucket, fileName, length, content);
}
public static void uploadBucketImage(String bucket, String fileName, long length, InputStream content) {
ObjectMetadata meta = new ObjectMetadata();
meta.setContentLength(length);
client.putObject(bucket, fileName, content, meta);
}
public static boolean delShopImage(String fileName) {
boolean exist = client.doesObjectExist(bucket, fileName);
if ( ! exist){
return false;
}
client.deleteObject(bucket, fileName);
return true;
}
public static String getUrl(String fileName) {
Date expiration = new Date(System.currentTimeMillis() + 3600L * 1000 * 24 * 365 * 10);
URL url = client.generatePresignedUrl(bucket, fileName, expiration);
if (url != null) {
return url.toString();
}
return null;
}
public static void crateBucket(String bucketName) {
client.createBucket(bucketName);
}
}
集成邮件发送服务
-
导入依赖
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context-support</artifactId>
<version>5.3.15</version>
</dependency>
<dependency>
<groupId>javax.mail</groupId>
<artifactId>mail</artifactId>
<version>1.4.7</version>
</dependency>
-
编写配置类 @Configuration
public class EmailConfig {
@Bean
public MailSender mailSender(){
Properties properties = new Properties();
JavaMailSenderImpl mailSender = new JavaMailSenderImpl();
mailSender.setHost("smtp.qq.com");
mailSender.setUsername("1481315703@qq.com");
mailSender.setPassword("dzrku");
properties.setProperty("mail.smtp.auth","true");
properties.setProperty("mail.smtp.port", "587");
properties.setProperty("mail.smtp.socketFactory.port", "587");
mailSender.setJavaMailProperties(properties);
return mailSender;
}
}
-
编写工具类
@Component
public class EmailUtil {
@Resource
private JavaMailSender mailSender;
public void sendSimpleEmail(String userEmail,String code){
SimpleMailMessage message = new SimpleMailMessage();
message.setFrom("1481315703@qq.com");
message.setTo(userEmail);
message.setSubject("爱学习的大雄博客登录验证码");
message.setText("您的验证码是:"+code);
mailSender.send(message);
}
}
集成Mybatis-plus
-
导入依赖 <dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus</artifactId>
<version>3.5.1</version>
</dependency>
-
更换连接池配置 <bean class="com.baomidou.mybatisplus.extension.spring.MybatisSqlSessionFactoryBean" id="sqlSessionFactory">
集成pagehelper
-
导入依赖 <!-- pagehelper-->
<dependency>
<groupId>com.github.pagehelper</groupId>
<artifactId>pagehelper</artifactId>
<version>5.3.0</version>
</dependency>
-
在mybatis的配置文件中配置 <!--pagehelper-->
<plugins>
<plugin interceptor="com.github.pagehelper.PageInterceptor">
<!-- 分页插件会自动检测当前的数据库链接,自动选择合适的分页方式-->
<property name="helperDialect" value="mysql"/>
<!-- 分页合理化参数,默认值为false。当该参数设置为 true 时,
pageNum<=0 时会查询第一页.pageNum>pages(超过总数时),
会查询最后一页。-->
<property name="reasonable" value="true"/>
<!-- 默认值为 false。设置为 true 时,允许在运行时根据多数据源自动识别对应方言的分页-->
<property name="autoRuntimeDialect" value="false"/>
<!-- 默认值为 false,当该参数设置为 true 时,如果 pageSize=0 或者 RowBounds.limit = 0 就会查询出全部的结果-->
<property name="pageSizeZero" value="true"/>
<!-- 为了支持startPage(Object params)方法-->
<property name="params" value="pageNum=pageNum;pageSize=pageSize;count=countSql"/>
</plugin>
</plugins>
全部依赖
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.13.2</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.28</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.2.8</version>
</dependency>
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus</artifactId>
<version>3.5.1</version>
</dependency>
<dependency>
<groupId>org.apache.velocity</groupId>
<artifactId>velocity-engine-core</artifactId>
<version>2.0</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.3.15</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.3.15</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>5.3.15</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aop</artifactId>
<version>5.3.15</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-beans</artifactId>
<version>5.3.15</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>5.3.15</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>5.3.15</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.13.1</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-annotations</artifactId>
<version>2.13.1</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-core</artifactId>
<version>2.13.1</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.9.7</version>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>servlet-api</artifactId>
<version>2.5</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.22</version>
</dependency>
<dependency>
<groupId>jstl</groupId>
<artifactId>jstl</artifactId>
<version>1.2</version>
</dependency>
<dependency>
<groupId>taglibs</groupId>
<artifactId>standard</artifactId>
<version>1.1.2</version>
</dependency>
<dependency>
<groupId>javax.servlet.jsp</groupId>
<artifactId>jsp-api</artifactId>
<version>2.1</version>
</dependency>
<dependency>
<groupId>com.googlecode.rapid-framework</groupId>
<artifactId>rapid-core</artifactId>
<version>4.0.5</version>
</dependency>
<dependency>
<groupId>com.github.pagehelper</groupId>
<artifactId>pagehelper</artifactId>
<version>5.3.0</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
<version>1.7.25</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.auth0</groupId>
<artifactId>java-jwt</artifactId>
<version>3.4.0</version>
</dependency>
<dependency>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-redis</artifactId>
<version>2.1.8.RELEASE</version>
</dependency>
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
<version>2.9.0</version>
</dependency>
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-all</artifactId>
<version>4.1.13</version>
</dependency>
<dependency>
<groupId>com.aliyun.oss</groupId>
<artifactId>aliyun-sdk-oss</artifactId>
<version>3.5.0</version>
</dependency>
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>1.3.2</version>
</dependency>
<dependency>
<groupId>commons-fileupload</groupId>
<artifactId>commons-fileupload</artifactId>
<version>1.2.1</version>
</dependency>
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger2</artifactId>
<version>2.9.2</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context-support</artifactId>
<version>5.3.15</version>
</dependency>
<dependency>
<groupId>javax.mail</groupId>
<artifactId>mail</artifactId>
<version>1.4.7</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>2.12.4</version>
<configuration>
<skipTests>true</skipTests>
</configuration>
</plugin>
</plugins>
</build>
项目模块功能
游客用户
主页展示功能
功能介绍:展示网站基本信息,文章信息、网站概况、分类信息、标签信息、公告信息
用户模块
注册功能
功能介绍:
- 用户通过输入用户名、昵称、密码、邮箱来进行账号的注册
- 验证输入信息是否符合要求
- 验证邮箱是否为本人邮箱,判断验证码是否正确
- 判断用户名是否存在
- 新增用户信息,密码加密存储,给前端返回信息
可能产生的异常:
- 邮件发送失败的异常
- 用户名被占用的异常
- 插入数据时产生的异常
Service层
@Override
public void emailSend(String email) {
String code = CurrencyUtil.VerificationCode();
try {
emailUtil.sendSimpleEmail(email,code);
}catch (Exception e){
throw new EmailException("邮件发送出现异常");
}
redisClient.set(email,code,60);
}
@Override
public void registerUser(String username, String nickname, String email, String password) {
if(userMapper.queryUserByUserName(username)!=null){
throw new UsernameDuplicatedException("该用户名已存在");
}
User user = new User().setUserName(username).setUserNickname(nickname)
.setUserEmail(email).setUserPass(CurrencyUtil.getMD5Password(password, username))
.setUserRegisterTime(new Date()).setUserRole("user").setUserStatus(1);
user.setUserAvatar(Conts.USERDEFAULTAVATOR);
Integer result = userMapper.insertUser(user);
if (result<=0){
throw new InsertException("注册用户时产生异常");
}
}
controller层
@PostMapping("/registerSubmit")
public JsonResult<Void> registerSubmit(HttpServletRequest request){
String username = request.getParameter("username");
String nickname = request.getParameter("nickname");
String email = request.getParameter("email");
String password = request.getParameter("password");
String code = request.getParameter("code");
if (!code.toUpperCase().equals(redisClient.get(email))){
throw new CodeException("验证码错误");
}
userService.registerUser(username,nickname,email,password);
return new JsonResult<>(OK);
}
@PostMapping("/user/emailSend")
public JsonResult<Void> emailSend(String email){
userService.emailSend(email);
return new JsonResult<>(OK);
}
前端页面
表单:
<form name="registerForm" id="registerForm" method="post">
<p>
<label for="username">用户名<br/>
<input type="text" name="username" id="username" class="input"
size="20" required/></label>
</p>
<p>
<label for="nickname">昵称<br/>
<input type="text" name="nickname" id="nickname" class="input"
size="20" required/></label>
</p>
<p>
<label for="password">登录密码<br/>
<input type="password" name="password" id="password" class="input"
size="20" required/>
</label>
</p>
<p>
<label for="confirmPassword">确认密码<br/>
<input type="password" name="confirmPassword" id="confirmPassword" class="input"
size="20" required/>
</label>
</p>
<p>
<label for="email">电子邮箱<br/>
<input type="text" name="email" class="input" id="email"
size="20" required/>
</label>
</p>
<p>
<label for="code">验证码
<input type="text" name="code" id="code" class="input" placeholder="60秒有效" required>
<input type="button" id="code_btn" value="获取邮箱验证码">
</label>
</p>
<p class="submit">
<input type="button" name="wp-submit" id="submit-btn" class="button button-primary button-large"
value="注册"/>
</p>
</form>
javascript:
<script src="/js/jquery.min.js"></script>
<script type="text/javascript">
<%-- 发送验证码--%>
$("#code_btn").click(function (){
var email = $("#email").val();
var user = $("#username").val();
var nickname = $("#nickname").val();
var password = $("#password").val();
var confirmPass = $("#confirmPassword").val();
let reg= /^([a-zA-Z]|[0-9])(\w|\-)+@[a-zA-Z0-9]+\.([a-zA-Z]{2,4})$/;
if (user == "" || nickname == "" || email == "" || password == "" || confirmPass == "") {
alert("请输入完整信息!")
} else if (password != confirmPass) {
alert("两次密码不一致!");
} else if(user.length < 4 || user.length > 20) {
alert('用户名长度不合法');
} else if(!reg.test(email)){
alert('邮箱格式不合法');
}else{
var time=60;
$(this).attr("disabled",true);
var timer = setInterval(function () {
if(time == 0){
$("#code_btn").removeAttr("disabled");
$("#code_btn").html("重新发送");
$("#code_btn").val("重新发送");
clearInterval(timer);
}else {
$("#code_btn").html(time);
$("#code_btn").val("剩余"+time+"秒");
time--;
}
},1000);
$.ajax({
type: "POST",
url: '/user/emailSend',
data: {
"email": email
},
dataType: "json",
success: function (data) {
if (data.state == 200) {
alert('发送成功');
} else {
alert(data.message);
}
},
error: function (xhr) {
alert("数据获取异常");
}
})
}
})
<%--注册验证--%>
$("#submit-btn").click(function () {
var user = $("#username").val();
var email = $("#email").val();
var nickname = $("#nickname").val();
var password = $("#password").val();
var confirmPass = $("#confirmPassword").val();
var code=$("#code").val();
let reg= /^([a-zA-Z]|[0-9])(\w|\-)+@[a-zA-Z0-9]+\.([a-zA-Z]{2,4})$/;
if (user == "" || nickname == "" || email == "" || password == "" || confirmPass == "") {
alert("请输入完整信息!")
} else if (password != confirmPass) {
alert("两次密码不一致!");
} else if(user.length < 4 || user.length > 20) {
alert('用户名长度不合法');
} else if(!reg.test(email)){
alert('邮箱格式不合法');
} else if (code==""){
alert("请验证邮箱");
}
else {
$.ajax({
async: false,
type: "POST",
url: '/registerSubmit',
contentType: "application/x-www-form-urlencoded;charset=utf-8",
data: $("#registerForm").serialize(),
dataType: "json",
success: function (data) {
if (data.state == 200) {
alert('注册成功');
window.location.href = "/login";
} else {
alert(data.message);
}
},
error: function (xhr) {
alert("数据获取失败")
}
})
}
})
</script>
文章模块
文章游览功能
功能介绍:
-
用户游览文章时,文章游览量会增加 -
用户可以查看到文章的分类和标签 -
可以游览到该文章下所有评论 -
若用户处于登陆状态,则可以进行评论,否则不能进行评论
Controller层
@RequestMapping(value = "/article/view/{id}", method = {RequestMethod.POST})
@ResponseBody
public JsonResult<Integer> increaseViewCount(@PathVariable("id") Integer id) {
Article article = articleService.getArticleByStatusAndId(ArticleStatus.PUBLISH.getValue(), id);
Integer articleCount = article.getArticleViewCount() + 1;
article.setArticleViewCount(articleCount);
articleService.updateArticleViewCount(article);
return new JsonResult<>(OK,articleCount);
}
@RequestMapping("/article/{articleId}")
public String getArticleDetailPage(@PathVariable("articleId") Integer articleId, Model model){
Article article = articleService.getArticleByStatusAndId(ArticleStatus.PUBLISH.getValue(), articleId);
if (article == null) {
return "redirect:/Error/404";
}
model.addAttribute("article", article);
List<Comment> comments = commentService.listCommentByArticleId(articleId);
model.addAttribute("commentList", comments);
List<Category> categoryList = article.getCategoryList();
ArrayList<Integer> categoryIds = new ArrayList<>();
for (Category category : categoryList) {
categoryIds.add(category.getCategoryId());
}
List<Article> similarArticleList = articleService.listArticleByCategoryIds(categoryIds, 5);
model.addAttribute("similarArticleList", similarArticleList);
List<Article> mostViewArticleList = articleService.listArticleByViewCount(5);
model.addAttribute("mostViewArticleList", mostViewArticleList);
List<Article> mostCommentArticleList = articleService.listArticleByCommentCount(8);
model.addAttribute("mostCommentArticleList", mostCommentArticleList);
List<Tag> tags = tagService.listTag();
model.addAttribute("allTagList", tags);
List<Article> randomArticleList = articleService.listRandomArticle(8);
model.addAttribute("randomArticleList", randomArticleList);
Article preArticle = articleService.getPreArticle(articleId);
model.addAttribute("preArticle", preArticle);
Article afterArticle = articleService.getAfterArticle(articleId);
model.addAttribute("afterArticle", afterArticle);
return "Home/Page/articleDetail";
}
普通用户
用户模块
登录功能
功能介绍:
- 用户输入用户名、密码、验证码、是否记住密码进行登录
- 验证输入信息是否符合要求
- 判断用户是否存在,密码加密匹配,用户是否被禁用
- 使用JWT生成token,荷载用户信息
- 使用cookie存储用户id,redis存储生成token
- 判断是否记住密码,若记住密码则将信息存储至cookie
- 记录用户的最后登录时间和最后登录ip
可能产生的异常:
- 用户名不存在的异常
- 密码匹配失败的异常
- 用户名被禁用的异常
- 更新用户数据时产生的异常
Service层
@Override
public void userLogin(String username, String password, String rememberme, HttpServletRequest request, HttpServletResponse response) {
User user = userMapper.queryUserByUserName(username);
if (user==null){
throw new UserNotFoundException("该用户不存在");
}
if (!user.getUserPass().equals(CurrencyUtil.getMD5Password(password,username))){
throw new PasswordNotMatchException("账户与密码不匹配");
}
if(user.getUserStatus()==0){
throw new StatusException("用户状态异常");
}
HashMap<String, String> map = new HashMap<String, String>();
map.put("userName",username);
map.put("userId", String.valueOf(user.getUserId()));
map.put("userRole",user.getUserRole());
map.put("userAvatar",user.getUserAvatar());
map.put("userNickname",user.getUserNickname());
map.put("userUrl",user.getUserUrl());
map.put("userEmail",user.getUserEmail());
String token = JWTUtils.getToken(map);
redisTemplate.opsForValue().set(String.valueOf(user.getUserId()),token,7, TimeUnit.DAYS);
CookieUtil.addCookie(response,"uid", String.valueOf(user.getUserId()),60*60*24*7);
if (rememberme!=null){
Cookie nameCookie = new Cookie("username", username);
nameCookie.setMaxAge(60 * 60 * 24 * 7);
Cookie pwdCookie = new Cookie("password", password);
pwdCookie.setMaxAge(60 * 60 * 24 * 7);
response.addCookie(nameCookie);
response.addCookie(pwdCookie);
}else{
Cookie usernameCookie = CookieUtil.getCookieByName(request, "username");
Cookie passwordCookie = CookieUtil.getCookieByName(request, "password");
if (usernameCookie!=null){
CookieUtil.removeCookie(response,"username");
}
if (passwordCookie!=null){
CookieUtil.removeCookie(response,"password");
}
}
user.setUserLastLoginTime(new Date()).setUserLastLoginIp(CurrencyUtil.getIpAddr(request));
Integer result = userMapper.updateUser(user);
if (result<=0){
throw new UpdateException("更新用户信息时产生异常");
}
}
controller层
@PostMapping("/loginVerify")
public JsonResult<Void> loginVerify(HttpServletRequest request, HttpServletResponse response){
String username = request.getParameter("username");
String password = request.getParameter("password");
String rememberme = request.getParameter("rememberme");
userService.userLogin(username,password,rememberme,request,response);
return new JsonResult<>(OK);
}
前端页面
表单
<%
String username = "";
String password = "";
//获取当前站点的所有Cookie
Cookie[] cookies = request.getCookies();
for (int i = 0; i < cookies.length; i++) {//对cookies中的数据进行遍历,找到用户名、密码的数据
if ("username".equals(cookies[i].getName())) {
username = cookies[i].getValue();
} else if ("password".equals(cookies[i].getName())) {
password = cookies[i].getValue();
}
}
%>
<form name="loginForm" id="loginForm" method="post">
<p>
<label for="user_login">用户名<br />
<input type="text" name="username" id="user_login" class="input" value="<%=username%>" size="20" required/></label>
</p>
<p>
<label for="user_pass">密码<br />
<input type="password" name="password" id="user_pass" class="input" value="<%=password%>" size="20" required/>
</label>
</p>
<p>
<label for="vcode">验证码<br />
<input type="text" name="vcode" id="vcode" placeholder="验证码" value="验证码" onfocus="this.value=''" onblur="if(this.value=='')this.value='验证码'"/>
<span id="code" title="看不清,换一张"></span>
</label>
<div id="search_pass_link"></div>
</p>
<p class="forgetmenot"><label for="rememberme"><input name="rememberme" type="checkbox" id="rememberme" value="1" checked /> 记住密码</label></p>
<p class="submit">
<input type="button" name="wp-submit" id="submit-btn" class="button button-primary button-large" value="登录" />
</p>
</form>
javascript
<script type="text/javascript">
function wp_attempt_focus(){
setTimeout( function(){ try{
d = document.getElementById('user_login');
d.focus();
d.select();
} catch(e){}
}, 200);
}
wp_attempt_focus();
if(typeof wpOnload=='function')wpOnload();
</script>
<p id="backtoblog"><a href="/">← 返回到博客首页</a> | <a href="/register">注册</a> | <a href="/user/forgetPasswordView">忘记密码</a> </p>
</div>
<div class="clear"></div>
<script src="/js/jquery.min.js"></script>
<script type="text/javascript">
let code;
document.getElementById("code").onclick = changeImg;
function changeImg() {
var arrays = new Array(
'1', '2', '3', '4', '5', '6', '7', '8', '9', '0',
'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j',
'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't',
'u', 'v', 'w', 'x', 'y', 'z',
'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J',
'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T',
'U', 'V', 'W', 'X', 'Y', 'Z'
);
code = '';
for(var i = 0; i < 4; i++) {
var r = parseInt(Math.random() * arrays.length);
code += arrays[r];
}
document.getElementById('code').innerHTML = code;
}
<%--登录验证--%>
$("#submit-btn").click(function () {
var user = $("#user_login").val();
var password = $("#r_pass").val();
var input_code= $("#vcode").val();
if(user=="") {
alert("用户名不可为空!");
} else if(password==""){
alert("密码不可为空!");
} else if (input_code.toLowerCase() != code.toLowerCase()){
alert("输入的验证码错误!");
}
else {
$.ajax({
async: false,
type: "POST",
url: '/loginVerify',
contentType: "application/x-www-form-urlencoded; charset=utf-8",
data: $("#loginForm").serialize(),
dataType: "json",
success: function (data) {
if(data.state==200) {
window.location.href="/admin";
} else {
alert(data.message);
}
},
error: function () {
alert("数据获取失败")
}
})
}
})
</script>
忘记密码
功能介绍:
- 用户忘记密码可通过输入用户名和邮箱来进行密码重置
- 邮箱发送验证码,用户将其填写提交,后端判断是否正确
- 信息正确,设置一个cookie存储用户输入的用户名,跳转至修改密码界面
- 用户输入新密码,确定新密码,点击提交
- 密码加密存储,用户密码修改成功,跳转至登陆界面
可能出现的异常:
- 邮箱验证码不匹配的异常
- 邮箱和用户名不匹配的异常
- cookie数据过期的异常
Service层
@Override
public void forgetPasswordVerify(String email, String username) {
QueryWrapper<User> userQueryWrapper = new QueryWrapper<>();
userQueryWrapper.eq("user_name",username).select("user_email");
User user = userMapper.selectOne(userQueryWrapper);
if(!user.getUserEmail().equals(email)){
throw new RoleErrorException("用户名与邮箱不匹配");
}
}
@Override
public void userUpdatePassword(String newPassword, String oldPassword, HttpServletRequest request, HttpServletResponse response) {
User user=null;
if (oldPassword!=null){
Cookie uid = CookieUtil.getCookieByName(request, "uid");
String uidValue = uid.getValue();
user = userMapper.selectById(Integer.valueOf(uidValue));
if (!user.getUserPass().equals(CurrencyUtil.getMD5Password(oldPassword,user.getUserName()))){
throw new PasswordNotMatchException("密码匹配失败!");
}
user.setUserPass(CurrencyUtil.getMD5Password(newPassword,user.getUserName()));
userMapper.updateById(user);
userLoginOut(request,response);
CookieUtil.removeCookie(response,"password");
}else {
Cookie restname = CookieUtil.getCookieByName(request, "restname");
if (restname!=null){
String value = restname.getValue();
UpdateWrapper<User> userUpdateWrapper = new UpdateWrapper<>();
userUpdateWrapper.eq("user_name",value);
user.setUserPass(CurrencyUtil.getMD5Password(newPassword,value));
userMapper.update(user,userUpdateWrapper);
}else {
throw new NotFountException("未找到cookie");
}
}
}
Controller层
@PostMapping("/user/emailSend")
public JsonResult<Void> emailSend(String email){
userService.emailSend(email);
return new JsonResult<>(OK);
}
@PostMapping("/user/forgetPasswordVerify")
public JsonResult<Void> forgetPasswordVerify(String email,String username,String code,HttpServletResponse response){
if(!code.toUpperCase().equals(redisClient.get(email))){
throw new CodeException("验证码错误");
}
userService.forgetPasswordVerify(email,username);
CookieUtil.addCookie(response,"restname",username,60*5);
return new JsonResult<>(OK);
}
@PostMapping("/user/updatePassword")
public JsonResult<Void> updatePassword(HttpServletRequest request,HttpServletResponse response){
String oldPassword = request.getParameter("oldPassword");
String newPassword = request.getParameter("newPassword");
userService.userUpdatePassword(newPassword,oldPassword,request,response);
return new JsonResult<>(OK);
}
修改密码
功能介绍:
- 用户处于登陆状态,输入原来密码,输入修改的密码,输入确定密码,点击提交
- 旧密码匹配判断
- 修改用户密码
- 退出登录,移除密码的cookie
可能出现的异常:
- 密码匹配失败的异常
Service层
@Override
public void userUpdatePassword(String newPassword, String oldPassword, HttpServletRequest request, HttpServletResponse response) {
User user=null;
if (oldPassword!=null){
Cookie uid = CookieUtil.getCookieByName(request, "uid");
String uidValue = uid.getValue();
user = userMapper.selectById(Integer.valueOf(uidValue));
if (!user.getUserPass().equals(CurrencyUtil.getMD5Password(oldPassword,user.getUserName()))){
throw new PasswordNotMatchException("密码匹配失败!");
}
user.setUserPass(CurrencyUtil.getMD5Password(newPassword,user.getUserName()));
userMapper.updateById(user);
userLoginOut(request,response);
CookieUtil.removeCookie(response,"password");
}else {
Cookie restname = CookieUtil.getCookieByName(request, "restname");
if (restname!=null){
String value = restname.getValue();
UpdateWrapper<User> userUpdateWrapper = new UpdateWrapper<>();
userUpdateWrapper.eq("user_name",value);
user.setUserPass(CurrencyUtil.getMD5Password(newPassword,value));
userMapper.update(user,userUpdateWrapper);
}else {
throw new NotFountException("未找到cookie");
}
}
}
Controller层
@PostMapping("/user/updatePassword")
public JsonResult<Void> updatePassword(HttpServletRequest request,HttpServletResponse response){
String oldPassword = request.getParameter("oldPassword");
String newPassword = request.getParameter("newPassword");
userService.userUpdatePassword(newPassword,oldPassword,request,response);
return new JsonResult<>(OK);
}
修改基本信息
功能介绍:
- 用户处于登陆状态,需要修改头像、url、昵称
- 点击进入用户信息修改页面,页面将其数据展示出来
- 点击上传图片,图片信息保存至本地,图片存储路径返回至前端页面
- 修改url或昵称,点击提交
- 通过cookid中的uid获取存储在reidis中的token,获取其中的荷载信息
- 重新将修改后的用户信息荷载生成token存入redis中
- 更新用户信息
可能出现的异常:
- 用户的token不存在的异常
Service层
@Override
public void updateUser(User user) {
HashMap<String, String> map = new HashMap<String, String>();
map.put("userName",user.getUserName());
map.put("userId", String.valueOf(user.getUserId()));
map.put("userRole",user.getUserRole());
map.put("userAvatar",user.getUserAvatar());
map.put("userNickname",user.getUserNickname());
map.put("userUrl",user.getUserUrl());
map.put("userEmail",user.getUserEmail());
String token = JWTUtils.getToken(map);
redisTemplate.opsForValue().set(String.valueOf(user.getUserId()),token,7, TimeUnit.DAYS);
userMapper.updateUser(user);
}
Controller层
@RequestMapping("/admin/profile/edit")
public ModelAndView editUserView(HttpServletRequest request){
ModelAndView modelAndView = new ModelAndView();
Cookie uid = CookieUtil.getCookieByName(request, "uid");
String uidValue = uid.getValue();
User userById = userService.getUserById(Integer.valueOf(uidValue));
modelAndView.addObject("user", userById);
modelAndView.setViewName("Admin/User/editProfile");
return modelAndView;
}
@PostMapping("/admin/profile/save")
public ModelAndView editUserSubmit(User user,HttpServletRequest request){
ModelAndView modelAndView = new ModelAndView();
Cookie uid = CookieUtil.getCookieByName(request, "uid");
String uidValue = uid.getValue();
String toke = (String) redisClient.get(uidValue);
DecodedJWT tokenInfo = JWTUtils.getTokenInfo(toke);
user.setUserRole(tokenInfo.getClaim("userRole").asString());
user.setUserEmail(tokenInfo.getClaim("userEmail").asString());
user.setUserUrl(tokenInfo.getClaim("userUrl").asString());
userService.updateUser(user);
modelAndView.setViewName("redirect:/admin/profile");
return modelAndView;
}
文章模块
文章编写功能
功能介绍:
- 用户写入文章标题、内容,选择分类、标签、上传文章封面图片,选择文章状态(发布或草稿)
- 前端自带markdown编辑器会将输入的内容转换为html的数据格式传入后台
- 后台通过cookie获取用户id,将文章分类信息、文章标签信息、文章信息存入article对象中
- 文章信息写入数据库中时,同时将标签信息写入写入标签-文章关联表、分类信息写入分类-文章关联表
Controller层
@RequestMapping(value = "/insertSubmit", method = RequestMethod.POST)
public String insertArticleSubmit(HttpServletRequest request, ArticleParam articleParam) {
Article article = new Article();
Cookie uid = CookieUtil.getCookieByName(request, "uid");
if (uid != null) {
article.setArticleUserId(Integer.valueOf(uid.getValue()));
}
article.setArticleTitle(articleParam.getArticleTitle());
int summaryLength = 130;
String summaryText = HtmlUtil.cleanHtmlTag(articleParam.getArticleContent());
if (summaryText.length() > summaryLength) {
String summary = summaryText.substring(0, summaryLength);
article.setArticleSummary(summary);
} else {
article.setArticleSummary(summaryText);
}
article.setArticleThumbnail(articleParam.getArticleThumbnail());
article.setArticleContent(articleParam.getArticleContent());
article.setArticleStatus(articleParam.getArticleStatus());
List<Category> categoryList = new ArrayList<>();
if (articleParam.getArticleChildCategoryId() != null) {
categoryList.add(new Category(articleParam.getArticleParentCategoryId()));
}
if (articleParam.getArticleChildCategoryId() != null) {
categoryList.add(new Category(articleParam.getArticleChildCategoryId()));
}
article.setCategoryList(categoryList);
List<Tag> tagList = new ArrayList<>();
if (articleParam.getArticleTagIds() != null) {
for (int i = 0; i < articleParam.getArticleTagIds().size(); i++) {
Tag tag = new Tag(articleParam.getArticleTagIds().get(i));
tagList.add(tag);
}
}
article.setTagList(tagList);
articleService.insertArticle(article);
return "redirect:/admin/article";
}
Service层
@Override
public void insertArticle(Article article) {
article.setArticleCreateTime(new Date());
article.setArticleUpdateTime(new Date());
article.setArticleIsComment(ArticleCommentStatus.ALLOW.getValue());
article.setArticleViewCount(0);
article.setArticleLikeCount(0);
article.setArticleCommentCount(0);
article.setArticleOrder(1);
articleMapper.insert(article);
for (int i = 0; i < article.getCategoryList().size(); i++) {
ArticleCategoryRef articleCategoryRef = new ArticleCategoryRef(article.getArticleId(), article.getCategoryList().get(i).getCategoryId());
articleCategoryRefMapper.insert(articleCategoryRef);
}
for (int i = 0; i < article.getTagList().size(); i++) {
ArticleTagRef articleTagRef = new ArticleTagRef(article.getArticleId(), article.getTagList().get(i).getTagId());
articleTagRefMapper.insert(articleTagRef);
}
}
文章编辑功能
功能介绍:
? 用户可以对自己发布或编写的文章进行重新编辑发布
功能完成:
- 原文章数据的展示
- 修改数据点击提交
- 后台删除文章-分类关联表、标签-文章关联表中本文章的数据,插入新数据
- 更新文章表数据
controller层
@RequestMapping(value = "/edit/{id}")
public String editArticleView(@PathVariable("id") Integer id, Model model, HttpServletRequest request) {
Article article = articleService.getArticleByStatusAndId(null, id);
if (article == null) {
return "redirect:/Error/404";
}
Cookie uid = CookieUtil.getCookieByName(request, "uid");
String uidValue = uid.getValue();
String token = (String) redisClient.get(uidValue);
DecodedJWT tokenInfo = JWTUtils.getTokenInfo(token);
String userRole = tokenInfo.getClaim("userRole").asString();
if (!article.getArticleUserId().equals(Integer.valueOf(uidValue)) && !userRole.equals(UserRole.ADMIN.getValue())) {
return "redirect:/Error/403";
}
model.addAttribute("article", article);
List<Category> categoryList = categoryService.listCategory();
model.addAttribute("categoryList", categoryList);
List<Tag> tagList = tagService.listTag();
model.addAttribute("tagList", tagList);
return "Admin/Article/edit";
}
@RequestMapping(value = "/editSubmit", method = RequestMethod.POST)
public String editArticleSubmit(ArticleParam articleParam, HttpServletRequest request) {
Article dbArticle = articleService.getArticleByStatusAndId(null, articleParam.getArticleId());
if (dbArticle == null) {
return "redirect:/Error/404";
}
Cookie uid = CookieUtil.getCookieByName(request, "uid");
String uidValue = uid.getValue();
String token = (String) redisClient.get(uidValue);
DecodedJWT tokenInfo = JWTUtils.getTokenInfo(token);
String userRole = tokenInfo.getClaim("userRole").asString();
if (!dbArticle.getArticleUserId().equals(Integer.valueOf(uidValue)) && !userRole.equals(UserRole.ADMIN.getValue())) {
return "redirect:/Error/403";
}
Article article = new Article();
article.setArticleThumbnail(articleParam.getArticleThumbnail());
article.setArticleId(articleParam.getArticleId());
article.setArticleTitle(articleParam.getArticleTitle());
article.setArticleContent(articleParam.getArticleContent());
article.setArticleStatus(articleParam.getArticleStatus());
int summaryLength = 150;
String summaryText = HtmlUtil.cleanHtmlTag(article.getArticleContent());
if (summaryText.length() > summaryLength) {
String summary = summaryText.substring(0, summaryLength);
article.setArticleSummary(summary);
} else {
article.setArticleSummary(summaryText);
}
List<Category> categoryList = new ArrayList<>();
if (articleParam.getArticleChildCategoryId() != null) {
categoryList.add(new Category(articleParam.getArticleParentCategoryId()));
}
if (articleParam.getArticleChildCategoryId() != null) {
categoryList.add(new Category(articleParam.getArticleChildCategoryId()));
}
article.setCategoryList(categoryList);
List<Tag> tagList = new ArrayList<>();
if (articleParam.getArticleTagIds() != null) {
for (int i = 0; i < articleParam.getArticleTagIds().size(); i++) {
Tag tag = new Tag(articleParam.getArticleTagIds().get(i));
tagList.add(tag);
}
}
article.setTagList(tagList);
article.setArticleUpdateTime(new Date());
articleService.updateArticleDetail(article);
return "redirect:/admin/article";
}
文章删除功能
功能介绍:
? 用户通过点击删除按钮可以删除自己发布的文章数据
功能完成:
- 后端接收前端传来的文章id
- 查询该文章id是否存在
- 判断用户权限,是否为博主或管理员
- 删除文章表中的数据,删除分类关联,删除标签关联、删除评论关联
Controller层
@RequestMapping(value = "/delete/{id}", method = RequestMethod.POST)
public JsonResult<String> deleteArticle(@PathVariable("id") Integer id, HttpServletRequest request) {
Article dbArticle = articleService.getArticleByStatusAndId(null, id);
if (dbArticle == null) {
throw new NotFountException("文章信息未找到");
}
Cookie uid = CookieUtil.getCookieByName(request, "uid");
String uidValue = uid.getValue();
String token = (String) redisClient.get(uidValue);
DecodedJWT tokenInfo = JWTUtils.getTokenInfo(token);
String userRole = tokenInfo.getClaim("userRole").asString();
if (!dbArticle.getArticleUserId().equals(Integer.valueOf(uidValue)) && !userRole.equals(UserRole.ADMIN.getValue())) {
throw new RoleErrorException("用户权限异常");
}
articleService.deleteArticle(id);
return new JsonResult<>(OK);
}
Service层
@Override
public void deleteArticle(Integer articleId) {
QueryWrapper<Article> articleQueryWrapper = new QueryWrapper<>();
articleQueryWrapper.eq("article_id",articleId);
HashMap<String, Object> map = new HashMap<>();
map.put("article_id",articleId);
articleMapper.delete(articleQueryWrapper);
articleCategoryRefMapper.deleteByMap(map);
articleTagRefMapper.deleteByMap(map);
QueryWrapper<Comment> commentQueryWrapper = new QueryWrapper<>();
commentQueryWrapper.eq("comment_article_id",articleId);
commentMapper.delete(commentQueryWrapper);
}
评论模块
评论他人功能
功能介绍:
? 用户游览文章时,如果处于登陆状态,则可对文章进行评论
功能完成:
- 判断验证用户是否处于登陆状态
- 判断文章是否存在,判断评论人id和博主id是否同一个
- 添加评论,更新文章的评论数量
Controller层
@RequestMapping(value = "/comment", method = {RequestMethod.POST})
@ResponseBody
public JsonResult<String> insertComment(HttpServletRequest request, Comment comment) {
System.out.println("进入了方法");
System.out.println(comment);
Cookie uid = CookieUtil.getCookieByName(request, "uid");
if (uid == null) {
String data="请先登录";
return new JsonResult<>(OK,data);
}
String uidValue = uid.getValue();
User user = userService.getUserById(Integer.valueOf(uidValue));
Article article = articleService.getArticleByStatusAndId(ArticleStatus.PUBLISH.getValue(), comment.getCommentArticleId());
if (article == null) {
String data="文章不存在";
return new JsonResult<>(OK,data);
}
comment.setCommentUserId(user.getUserId());
comment.setCommentCreateTime(new Date());
comment.setCommentIp(CurrencyUtil.getIpAddr(request));
if (Objects.equals(user.getUserId(), article.getArticleUserId())) {
comment.setCommentRole(Role.OWNER.getValue());
} else {
comment.setCommentRole(Role.VISITOR.getValue());
}
comment.setCommentAuthorAvatar(user.getUserAvatar());
comment.setCommentContent(HtmlUtil.escape(comment.getCommentContent()));
comment.setCommentAuthorName(user.getUserNickname());
comment.setCommentAuthorEmail(user.getUserEmail());
comment.setCommentAuthorUrl(user.getUserUrl());
commentService.insertComment(comment);
articleService.updateCommentCount(article.getArticleId());
return new JsonResult<>(OK);
}
查看自己文章的全部评论
功能介绍:
? 用户通过进入后台页面,可以查看到自己发布文章的全部评论信息
Controller层
@RequestMapping("/receive")
public String receive(@RequestParam(required = false, defaultValue = "1") Integer pageIndex,HttpServletRequest request,Model model){
Cookie uid = CookieUtil.getCookieByName(request, "uid");
String cookieUid = uid.getValue();
PageInfo<Comment> commentPageInfo = commentService.listReceiveCommentByPage(pageIndex, Conts.PAGESIZE, Integer.valueOf(cookieUid));
model.addAttribute("pageInfo", commentPageInfo);
model.addAttribute("pageUrlPrefix", "/admin/comment?pageIndex");
return "Admin/Comment/index";
}
Service层
@Override
public PageInfo<Comment> listReceiveCommentByPage(Integer pageIndex, Integer pagesize, Integer userId) {
QueryWrapper<Article> articleQueryWrapper = new QueryWrapper<>();
articleQueryWrapper.select("article_id").eq("article_user_id",userId);
List<Article> articles = articleMapper.selectList(articleQueryWrapper);
PageHelper.startPage(pageIndex,pagesize);
List<Comment> comments=new ArrayList<>();
if (articles!=null&&articles.size()>0){
comments = commentMapper.getReceiveComment(articles);
}
PageInfo<Comment> commentPageInfo = new PageInfo<>(comments);
return commentPageInfo;
}
查看自己发布的全部评论
功能介绍:
? 用户进入后台界面,可以查看到自己发布的全部评论信息
Controller层
@RequestMapping("")
public String index(Model model, @RequestParam(required = false, defaultValue = "1") Integer pageIndex, HttpServletRequest request){
HashMap<String, Object> criteria = new HashMap<>();
Cookie uid = CookieUtil.getCookieByName(request, "uid");
String cookieUid = uid.getValue();
String token = (String) redisClient.get(cookieUid);
DecodedJWT tokenInfo = JWTUtils.getTokenInfo(token);
String userRole = tokenInfo.getClaim("userRole").asString();
if (!UserRole.ADMIN.getValue().equals(userRole)) {
criteria.put("userId", Integer.valueOf(cookieUid));
}
PageInfo<Comment> commentPageInfo = commentService.listCommentByPage(pageIndex, Conts.PAGESIZE, criteria);
model.addAttribute("pageInfo", commentPageInfo);
model.addAttribute("pageUrlPrefix", "/admin/comment?pageIndex");
return "Admin/Comment/index";
}
管理员用户
用户模块
管理用户信息
功能介绍:
- 管理员可查询全部用户信息
- 管理员用户可修改用户的状态
- 管理员用户可重置用户的密码
- 管理员用户可删除用户的信息
文章模块
管理文章信息
功能介绍:
- 管理员可查询全部文章信息
- 管理员可修改文章状态
- 管理员可编辑所有文章
- 管理员可删除所有文章
分类模块
管理分类信息
功能介绍:
- 展示全部分类信息
- 编辑分类信息
- 新增分类信息
- 删除分类信息
评论模块
管理评论信息
功能介绍:
- 展示全部评论
- 编辑评论
- 回复评论
- 删除评论
链接模块
管理链接信息
功能介绍:
- 展示全部链接
- 编辑链接
- 新增链接
- 删除链接
菜单模块
菜单信息管理
功能介绍:
- 展示全部菜单
- 新增菜单信息
- 编辑菜单信息
- 删除菜单信息
公告模块
公告信息管理
功能介绍:
- 展示全部公告信息
- 新增公告信息
- 编辑公告信息
- 删除公告信息
页面模块
页面信息管理
功能介绍:
- 展示全部页面信息
- 新增页面信息
- 编辑页面信息
- 删除页面信息
标签模块
管理标签信息
功能介绍:
- 展示全部标签信息
- 新增标签信息
- 编辑标签信息
- 删除标签信息
网站信息模块
管理网站基本信息
功能介绍:
- 展示网站基本信息
- 修改网站基本信息
项目部署
前提:Linux中已经配置好JDK、tomcat、nginx等环境
本项目是ssm项目,通过打war包的方式部署至LInux云服务器中,通过nginx+tomcat的方式去部署
-
在idea中进行打war包的操作 进入Project Structure ->Artifacts 查看是否有blog-ssm:war exploded 点击主界面上方的Build 点击之后,项目中会生成一个out包,这个时候需要点开out包检查一下是不是所有的类都打包进去了,如果没有,则需要进行重新打包,否则在部署项目时会出现访问不了的问题 打好的war包就是如下这个 -
Linux服务器中复制tomcat为tomcat1、tomcat2
cp -r 原目录名 需要复制到的路径
-
上传打好的war包至tomcat1、tomcat2下的webapps目录下,将原来的ROOT目录删除,将上传的war包修改名为ROOT -
修改tomcat的配置(conf目录下server.xml) 修改其中的三个端口号,每个tomcat的这3个端口号必须不一样 -
启动tomcat(bin目录下的starup.sh) -
修改nginx的配置 -
启动nginx即可
|