项目-论坛系统

基于Spring前后端分离版本的论坛系统。

1、构建项目结构

  • common公共类:统一返回结果、全局变量、异常枚举信息
  • config配置类:Swagger,用于自动生成CRUD和基本对象
  • controller控制器类:用于接受前端信息和控制路由
  • dao数据库访问类:用于控制数据库操作
  • exception自定义异常类:用以定义统一异常变量
  • interceptor自定义拦截器类:用于定义拦截器
  • model数据库实体对应模型类:用于定义对象
  • services业务服务层接口:用于定义服务接口
  • impl业务服务层接口实现类:复写接口的方法,用于实现业务
  • utils辅助工具类:存放一些辅助工具,如加密、解密等
  • mapper:用于存放自动生成的xml文件,以及自定义的xml文件
  • mybatis:用于配置mybatis自动生成CRUD的文件
  • static:前端组件

Mybatis - generator

配置数据源

<!-- 管理依赖版块号-->
<mysql-connector.version>5.1.49</mysql-connector.version>
<mybatis-starter.version>2.3.0</mybatis-starter.version>
<druid-starter.version>1.2.16</druid-starter.version>
# 配置数据源
datasource:
 url: jdbc:mysql://127.0.0.1:3306/forum_db?characterEncoding=utf8&useSSL=false
 # 数据库连接串
 username: root # 数据库⽤⼾名
 password: 123456 # 数据库密码
 driver-class-name: com.mysql.jdbc.Driver # 数据库连接驱动

编写类与映射文件

编写映射文件xxxMapper.xml

编写Dao类,xxxMapper.java

在properties标签中加入版本号,在build的plugins中加入配置

<mybatis-generator-plugin-version>1.4.1</mybatis-generator-plugin-version>
<!-- mybatis ⽣成器插件 -->
<plugin>
 <groupId>org.mybatis.generator</groupId>
 <artifactId>mybatis-generator-maven-plugin</artifactId>
 <version>${mybatis-generator-plugin-version}</version>
 <executions>
 <execution>
 <id>Generate MyBatis Artifacts</id>
<phase>deploy</phase>
 <goals>
 <goal>generate</goal>
 </goals>
 </execution>
 </executions>
 <!-- 相关配置 -->
 <configuration>
 <!-- 打开⽇志 -->
 <verbose>true</verbose>
 <!-- 允许覆盖 -->
 <overwrite>true</overwrite>
 <!-- 配置⽂件路径 -->
 <configurationFile>
 src/main/resources/mybatis/generatorConfig.xml
 </configurationFile>
 </configuration>
</plugin>

创建generatorConfig.xml

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE generatorConfiguration
 PUBLIC "-//mybatis.org//DTD MyBatis Generator Configuration 1.0//EN"
 "http://mybatis.org/dtd/mybatis-generator-config_1_0.dtd">
<generatorConfiguration>
 <!-- 驱动包路径,location中路径替换成⾃⼰本地路径 -->
 <classPathEntry location="D:\database\jar\mysql-connector-java-
5.1.49.jar"/>
 <context id="DB2Tables" targetRuntime="MyBatis3">
 <!-- 禁⽤⾃动⽣成的注释 -->
 <commentGenerator>
 <property name="suppressAllComments" value="true"/>
 <property name="suppressDate" value="true"/>
 </commentGenerator>
 <!-- 连接配置 -->
 <jdbcConnection driverClass="com.mysql.jdbc.Driver"
 connectionURL="jdbc:mysql://127.0.0.1:3306/forum_db?
characterEncoding=utf8&amp;useSSL=false"
 userId="root"
 password="123456">
 </jdbcConnection>
 <javaTypeResolver>
 <!-- ⼩数统⼀转为BigDecimal -->
 <property name="forceBigDecimals" value="false"/>
 </javaTypeResolver>
 <!-- 实体类⽣成位置 -->
 <javaModelGenerator targetPackage="com.bitejiuyeke.forum.model"
targetProject="src/main/java">
 <property name="enableSubPackages" value="true"/>
 <property name="trimStrings" value="true"/>
 </javaModelGenerator>
 <!-- mapper.xml⽣成位置 -->
 <sqlMapGenerator targetPackage="mapper"
targetProject="src/main/resources">
 <property name="enableSubPackages" value="true"/>
 </sqlMapGenerator>
 <!-- DAO类⽣成位置 -->
 <javaClientGenerator type="XMLMAPPER"
targetPackage="com.bitejiuyeke.forum.dao" targetProject="src/main/java">
 <property name="enableSubPackages" value="true"/>
 </javaClientGenerator>
 <!-- 配置⽣成表与实例, 只需要修改表名tableName, 与对应类名domainObjectName 即
可-->
 <table tableName="t_article" domainObjectName="Article"
enableSelectByExample="false"
 enableDeleteByExample="false" enableDeleteByPrimaryKey="false"
enableCountByExample="false"
 enableUpdateByExample="false">
 <!-- 类的属性⽤数据库中的真实字段名做为属性名, 不指定这个属性会⾃动转换 _ 为
驼峰命名规则-->
 <property name="useActualColumnNames" value="true"/>
 </table>
 <table tableName="t_article_reply" domainObjectName="ArticleReply"
enableSelectByExample="false"
 enableDeleteByExample="false" enableDeleteByPrimaryKey="false"
enableCountByExample="false"
 enableUpdateByExample="false">
 <property name="useActualColumnNames" value="true"/>
 </table>
 <table tableName="t_board" domainObjectName="Board"
enableSelectByExample="false" enableDeleteByExample="false"
 enableDeleteByPrimaryKey="false" enableCountByExample="false"
enableUpdateByExample="false">
 <property name="useActualColumnNames" value="true"/>
 </table>
 <table tableName="t_message" domainObjectName="Message"
enableSelectByExample="false"
 enableDeleteByExample="false" enableDeleteByPrimaryKey="false"
enableCountByExample="false"
 enableUpdateByExample="false">
 <property name="useActualColumnNames" value="true"/>
 </table>
 <table tableName="t_user" domainObjectName="User"
enableSelectByExample="false" enableDeleteByExample="false"
 enableDeleteByPrimaryKey="false" enableCountByExample="false"
enableUpdateByExample="false">
 <property name="useActualColumnNames" value="true"/>
 </table>
 </context>
</generatorConfiguration>

创建mapper目录,在maven选项中,plugins节点下出现mybatis-generator,双击运行。

<!-- useGeneratedKeys = true -->
<!-- keyProperty = 主键字段-->
<!-- 当插⼊⼀条数据后,可以通过user.getId()获取到⾃动⽣成的Id值,如果⽅法中需要⽴即获取
Id值,加⼊这个配置 -->
<insert id="insert" parameterType="com.bitejiuyeke.forum.model.User"
useGeneratedKeys="true" keyProperty="id" >

加@Mapper注解,在config包下新建MybatisConfig类,指定mybatis的扫描路径

// 配置类
@Configuration
// 指定Mybatis的扫描路径
@MapperScan("com.bitejiuyeke.forum.dao")
public class MybatisConfig {
}
mybatis:
 mapper-locations: classpath:mapper/**/*.xml # 指定 xxxMapper.xml的扫描路径

2、编写公共代码

编写状态码,用枚举类型

package com.example.forum.common;

/**
 * 枚举结果状态码
 */
public enum ResultCode {
    SUCCESS                              (0, "操作成功"),
    FAILED                               (1000, "操作失败"),
    FAILED_UNAUTHORIZED                  (1001, "未授权"),
    FAILED_PARAMS_VALID                  (1002, "参数校验失败"),
    FAILED_FORBIDDEN                     (1003, "禁⽌访问"),
    FAILED_CREATE                        (1004, "新增失败"),
    FAILED_NOT_EXISTS                    (1005, "资源不存在"),

    //用户
    FAILED_USER_EXISTS                   (1101, "⽤⼾已存在"),
    FAILED_USER_NOT_EXISTS               (1102, "⽤⼾不存在"),
    FAILED_LOGIN                         (1103, "⽤⼾名或密码错误"),
    FAILED_USER_BANNED                   (1104, "您已被禁⾔, 请联系管理员, 并重新登录."),
    FAILED_TWO_PWD_NOT_SAME              (1105, "两次输⼊的密码不⼀致"),
    FAILED_USER_ARTICLE_COUNT            (1106,"更新帖子数量失败"),




    //板块
    FAILED_BOARD_ARTICLE_COUNT           (1201,"更新帖子数量失败"),
    FAILED_BOARD_BANNED                  (1202,"板块内部错误"),
    FAILED_BOARD_NOT_EXISTS              (1203, "板块不存在"),

    FAILED_ARTICLE_NOT_EXISTS              (1301, "帖子不存在"),
    FAILED_ARTICLE_BANNED                (1302, "帖子状态异常"),

    //站内信
    FAILED_MESSAGE_NOT_EXISTS           (1401,"站内信不存在"),

    //服务器
    ERROR_SERVICES                       (2000, "服务器内部错误"),
    ERROR_IS_NULL                        (2001, "IS NULL.");

    int code;   //状态码
    String message; //描述信息

    ResultCode(int code, String message) {
        this.code = code;
        this.message = message;
    }

    public int getCode() {
        return code;
    }

    public String getMessage() {
        return message;
    }

    @Override
    public String toString() {
        return "code = " + code + "," + "message = " + message;
    }
}

编写统一返回结果,用AppResult对象的格式返回

package com.example.forum.common;

import com.fasterxml.jackson.annotation.JsonInclude;

/**
 * 统一对外返回格式
 * @param <T>
 */
public class AppResult<T> {

    @JsonInclude(JsonInclude.Include.ALWAYS)  //无论任何情况,这个字段都要参数json序列化
    private int code;   //状态码
    @JsonInclude(JsonInclude.Include.ALWAYS)
    private String message; //描述消息
    @JsonInclude(JsonInclude.Include.ALWAYS)
    private T data;    //具体的数据,通配类型,会被编译成Object

    //构造方法
    public AppResult(int code, String message, T data) {
        this.code = code;
        this.message = message;
        this.data = data;
    }

    public AppResult(int code, String message) {
        this.code = code;
        this.message = message;
        this.data = null;
    }

    //提供给外部使用的静态方法
    public static AppResult success(){
        return new AppResult(ResultCode.SUCCESS.getCode(), ResultCode.SUCCESS.getMessage());
    }

    public static AppResult success(String message){
        return new AppResult(ResultCode.SUCCESS.getCode(), message);
    }

    public static <T> AppResult<T> success (T data){
        return new AppResult<>(ResultCode.SUCCESS.getCode(), ResultCode.SUCCESS.getMessage(),data);
    }

    public static <T> AppResult<T> success(String message,T data){
        return new AppResult<>(AppResult.success().getCode(),message,data);
    }

    public static AppResult failed(){
        return new AppResult(ResultCode.FAILED.getCode(), ResultCode.FAILED.getMessage());
    }

    public static AppResult failed(String message){
        return new AppResult(ResultCode.FAILED.getCode(), message);
    }

    public static AppResult failed(ResultCode resultCode){
        return new AppResult(resultCode.getCode(), resultCode.getMessage());
    }

    //普通get set方法
    public int getCode() {
        return code;
    }

    public void setCode(int code) {
        this.code = code;
    }

    public String getMessage() {
        return message;
    }

    public void setMessage(String message) {
        this.message = message;
    }

    public T getData() {
        return data;
    }

    public void setData(T data) {
        this.data = data;
    }
}

编写自定义全局异常类ApplicationException

package com.example.forum.exception;

import com.example.forum.common.AppResult;

/**
 * 统一异常处理,使用AppResult来包装返回值
 */
public class ApplicationException extends RuntimeException{

    //自定义错误,在异常中持有一个错误信息对象
    protected AppResult errorResult;

    //自定义构造方法
    public ApplicationException(AppResult errorResult){
        super(errorResult.getMessage());
        this.errorResult = errorResult;
    }


    //重写父类方法
    public ApplicationException(String message) {
        super(message);
    }

    public ApplicationException(String message, Throwable cause) {
        super(message, cause);
    }

    public ApplicationException(Throwable cause) {
        super(cause);
    }

    //get方法
    public AppResult getErrorResult() {
        return errorResult;
    }
}

编写登录拦截器

package com.example.forum.interceptor;

import com.example.forum.common.AppConfig;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;

/**
 * 拦截器
 */
@Component
public class LoginInterceptor implements HandlerInterceptor {
    @Value("${bit-forum.login.url}")
    private String defaultURL;


    /**
     * 对请求的前置预处理
     * @return
     */
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        HttpSession session = request.getSession(false);
        if (session != null && session.getAttribute(AppConfig.USER_SESSION) != null){
            //校验通过,已登录状态
            return true;
        }
        //校验URL前面有/吗
        if (!defaultURL.startsWith("/")){
            defaultURL = "/" + defaultURL;
        }
        //不通过就跳转到登录界面
        response.sendRedirect(defaultURL);
        return false;
    }
}
package com.example.forum.interceptor;

import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

import javax.annotation.Resource;

@Configuration
public class AppInterceptorConfigurer implements WebMvcConfigurer {
    @Resource
    private LoginInterceptor loginInterceptor;

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        // 添加登录拦截器
        registry.addInterceptor(loginInterceptor)       // 添加用户登录拦截器
                .addPathPatterns("/**")                 // 拦截所有请求
                .excludePathPatterns("/sign-in.html")   // 排除登录HTML
                .excludePathPatterns("/sign-up.html")   // 排除注册HTML
                .excludePathPatterns("/user/login")     // 排除登录api接口
                .excludePathPatterns("/user/register")  // 排除注册api接口
                .excludePathPatterns("/user/logout")    // 排除退出api接口
                .excludePathPatterns("/swagger*/**")    // 排除登录swagger下所有
                .excludePathPatterns("/v3*/**")         // 排除登录v3下所有,与swagger相关
                .excludePathPatterns("/dist/**")        // 排除所有静态文件
                .excludePathPatterns("/image/**")
                .excludePathPatterns("/js/**")
                .excludePathPatterns("/**.ico");
    }
}
# 项⽬⾃定义相关配置
bit-forum:
 login:
 url: sign-in.html # 未登录状况下强制跳转⻚⾯

3、Swagger检测

<springfox-boot-starter.version>3.0.0</springfox-boot-starter.version>
<!-- API⽂档⽣成,基于swagger2 -->
<dependency>
 <groupId>io.springfox</groupId>
 <artifactId>springfox-boot-starter</artifactId>
 <version>${springfox-boot-starter.version}</version>
</dependency>
<!-- SpringBoot健康监控 -->
<dependency>
 <groupId>org.springframework.boot</groupId>
 <artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
package com.example.forum.config;

import org.springframework.boot.actuate.autoconfigure.endpoint.web.CorsEndpointProperties;
import org.springframework.boot.actuate.autoconfigure.endpoint.web.WebEndpointProperties;
import org.springframework.boot.actuate.autoconfigure.web.server.ManagementPortType;
import org.springframework.boot.actuate.endpoint.ExposableEndpoint;
import org.springframework.boot.actuate.endpoint.web.*;
import org.springframework.boot.actuate.endpoint.web.annotation.ControllerEndpointsSupplier;
import org.springframework.boot.actuate.endpoint.web.annotation.ServletEndpointsSupplier;
import org.springframework.boot.actuate.endpoint.web.servlet.WebMvcEndpointHandlerMapping;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.env.Environment;
import org.springframework.util.StringUtils;
import springfox.documentation.builders.ApiInfoBuilder;
import springfox.documentation.builders.PathSelectors;
import springfox.documentation.builders.RequestHandlerSelectors;
import springfox.documentation.oas.annotations.EnableOpenApi;
import springfox.documentation.service.ApiInfo;
import springfox.documentation.service.Contact;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spring.web.plugins.Docket;

import java.util.ArrayList;
import java.util.Collection;
import java.util.List;

// 配置类
@Configuration
// 开启Springfox-Swagger
@EnableOpenApi
public class SwaggerConfig {
    //创建基本扫描路径,与controller路径要一致
    public Docket createApi() {
        Docket docket = new Docket(DocumentationType.OAS_30)
                .apiInfo(apiInfo())
                .select()
                .apis(RequestHandlerSelectors.basePackage("com.example.forum.controller"))
                .paths(PathSelectors.any())
                .build();
        return docket;
    }
    // 配置API基本信息
    private ApiInfo apiInfo() {
        ApiInfo apiInfo = new ApiInfoBuilder()
                .title("论坛系统API")
                .description("论坛系统前后端分离API测试")
                .contact(new Contact(" Tech",
                        "https://", "1161245326@qq.com"))
                .version("1.0")
                .build();
        return apiInfo;
    }
    /**
     * 解决SpringBoot 6.0以上与Swagger 3.0.0 不兼容的问题
     * 复制即可
     **/
    @Bean
    public WebMvcEndpointHandlerMapping webMvcEndpointHandlerMapping (
            WebEndpointsSupplier webEndpointsSupplier,
            ServletEndpointsSupplier servletEndpointsSupplier,
            ControllerEndpointsSupplier controllerEndpointsSupplier,
            EndpointMediaTypes endpointMediaTypes,
            CorsEndpointProperties corsProperties,
            WebEndpointProperties webEndpointProperties,
            Environment environment) {
        List<ExposableEndpoint<?>> allEndpoints = new ArrayList();
        Collection<ExposableWebEndpoint> webEndpoints = webEndpointsSupplier.getEndpoints();
        allEndpoints.addAll(webEndpoints);
        allEndpoints.addAll(servletEndpointsSupplier.getEndpoints());
        allEndpoints.addAll(controllerEndpointsSupplier.getEndpoints());
        String basePath = webEndpointProperties.getBasePath();
        EndpointMapping endpointMapping = new EndpointMapping(basePath);
        boolean shouldRegisterLinksMapping = this.shouldRegisterLinksMapping(webEndpointProperties, environment,
                basePath);
        return new WebMvcEndpointHandlerMapping(endpointMapping, webEndpoints,
                endpointMediaTypes,
                corsProperties.toCorsConfiguration(), new
                EndpointLinksResolver(allEndpoints, basePath),
                shouldRegisterLinksMapping, null);
    }
    private boolean shouldRegisterLinksMapping(WebEndpointProperties
                                                       webEndpointProperties, Environment environment,
                                               String basePath) {
        return webEndpointProperties.getDiscovery().isEnabled() &&
                (StringUtils.hasText(basePath)
                        ||
                        ManagementPortType.get(environment).equals(ManagementPortType.DIFFERENT));
    }
}
mvc:
 pathmatch:
 matching-strategy: ANT_PATH_MATCHER #Springfox-Swagger兼容性配置

4、MD5Util

package com.example.forum.utils;

import org.apache.commons.codec.cli.Digest;
import org.apache.commons.codec.digest.DigestUtils;

/**
 * 用于MD5加密的工具类
 */
public class MD5Util {

    /**
     * 对单个字符串进行加密
     * 传入源字符串后,可以得到一个经过MD5加密后的密文字符串
     * @param str
     * @return
     */
    public static String md5(String str){
        return DigestUtils.md5Hex(str);
    }

    /**
     * 给密码进行加密,盐+密码
     * @param str
     * @param salt
     * @return
     */
    public static String md5Salt(String str,String salt){
        return md5(salt + md5(str));
    }

}
package com.example.forum.utils;


import java.util.UUID;

public class UUIDUtil {

    /**
     * 生成一个标准的36位的UUID
     * @return
     */
    public static  String UUID_36(){
        return UUID.randomUUID().toString();
    }

    /**
     * 生成一个32位的UUID
     * @return
     */
    public static String UUID_32(){
        return UUID.randomUUID().toString().replace("-","");
    }


}
package com.example.forum.utils;

public class StringUtil {

    //判断字符串是否为空
    public static boolean isEmpty(String value){
        return value == null || value.length() == 0;
    }


}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值