本项目实战教程配有免费视频教程,配套代码完全开源。手把手从零开始搭建一个目前应用最广泛的 Springboot+Vue 前后端分离多用户社区项目。本项目难度适中,为便于大家学习,每一集视频教程对应在 Github 上的每一次提交。
Vue Vuex Vue Router Axios Bulma Buefy Element Vditor DarkReader
Spring Boot Mysql Mybatis MyBatis-Plus Spring Security JWT Lombok
在数据库导入
/* Navicat Premium Data Transfer Source Server : localhost Source Server Type : MySQL Source Server Version : 80022 Source Host : localhost:3306 Source Schema : doubao Target Server Type : MySQL Target Server Version : 80022 File Encoding : 65001 */ SET NAMES utf8mb4; SET FOREIGN_KEY_CHECKS = 0; -- ---------------------------- -- Table structure for bms_billboard -- ---------------------------- DROP TABLE IF EXISTS `bms_billboard`; CREATE TABLE `bms_billboard` ( `id` int NOT NULL AUTO_INCREMENT COMMENT '主键', `content` varchar(255) CHARACTER SET utf8mb4 NOT NULL COMMENT '公告', `create_time` datetime NULL DEFAULT NULL COMMENT '公告时间', `show` tinyint(1) NULL DEFAULT NULL COMMENT '1:展示中,0:过期', PRIMARY KEY (`id`) USING BTREE ) ENGINE = InnoDB AUTO_INCREMENT = 6 CHARACTER SET = utf8mb4 COMMENT = '全站公告' ROW_FORMAT = DYNAMIC; -- ---------------------------- -- Records of bms_billboard -- ---------------------------- INSERT INTO `bms_billboard` VALUES (2, 'R1.0 开始已实现护眼模式 ,妈妈再也不用担心我的眼睛了。', '2020-11-19 17:16:19', 0); INSERT INTO `bms_billboard` VALUES (4, '系统已更新至最新版 1.0.1', NULL, 1); -- ---------------------------- -- Table structure for bms_follow -- ---------------------------- DROP TABLE IF EXISTS `bms_follow`; CREATE TABLE `bms_follow` ( `id` int NOT NULL AUTO_INCREMENT COMMENT '主键', `parent_id` varchar(20) CHARACTER SET utf8mb4 NULL DEFAULT NULL COMMENT '被关注人 ID', `follower_id` varchar(20) CHARACTER SET utf8mb4 NULL DEFAULT NULL COMMENT '关注人 ID', PRIMARY KEY (`id`) USING BTREE ) ENGINE = InnoDB AUTO_INCREMENT = 130 CHARACTER SET = utf8mb4 COMMENT = '用户关注' ROW_FORMAT = DYNAMIC; -- ---------------------------- -- Records of bms_follow -- ---------------------------- INSERT INTO `bms_follow` VALUES (65, '1329723594994229250', '1317498859501797378'); INSERT INTO `bms_follow` VALUES (85, '1332912847614083073', '1332636310897664002'); INSERT INTO `bms_follow` VALUES (129, '1349290158897311745', '1349618748226658305'); -- ---------------------------- -- Table structure for bms_post -- ---------------------------- DROP TABLE IF EXISTS `bms_post`; CREATE TABLE `bms_post` ( `id` varchar(20) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT '主键', `title` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL DEFAULT '' COMMENT '标题', `content` longtext CHARACTER SET utf8 COLLATE utf8_general_ci NULL COMMENT 'markdown 内容', `user_id` varchar(20) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT '作者 ID', `comments` int NOT NULL DEFAULT 0 COMMENT '评论统计', `collects` int NOT NULL DEFAULT 0 COMMENT '收藏统计', `view` int NOT NULL DEFAULT 0 COMMENT '浏览统计', `top` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否置顶,1-是,0-否', `essence` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否加精,1-是,0-否', `section_id` int NULL DEFAULT 0 COMMENT '专栏 ID', `create_time` datetime NOT NULL COMMENT '发布时间', `modify_time` datetime NULL DEFAULT NULL COMMENT '修改时间', UNIQUE INDEX `title`(`title`) USING BTREE, INDEX `user_id`(`user_id`) USING BTREE, INDEX `create_time`(`create_time`) USING BTREE ) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_general_ci COMMENT = '话题表' ROW_FORMAT = DYNAMIC; -- ---------------------------- -- Table structure for bms_comment -- ---------------------------- DROP TABLE IF EXISTS `bms_comment`; CREATE TABLE `bms_comment` ( `id` varchar(20) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT '主键', `content` varchar(1000) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL DEFAULT '' COMMENT '内容', `user_id` varchar(20) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT '作者 ID', `topic_id` varchar(20) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT 'topic_id', `create_time` datetime NOT NULL COMMENT '发布时间', `modify_time` datetime NULL DEFAULT NULL COMMENT '修改时间', PRIMARY KEY (`id`) USING BTREE ) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_general_ci COMMENT = '评论表' ROW_FORMAT = DYNAMIC; -- ---------------------------- -- Table structure for ums_user -- ---------------------------- DROP TABLE IF EXISTS `ums_user`; CREATE TABLE `ums_user` ( `id` varchar(20) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT '用户 ID', `username` varchar(15) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL DEFAULT '' COMMENT '用户名', `alias` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '用户昵称', `password` varchar(100) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT '' COMMENT '密码', `avatar` varchar(1000) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '头像', `email` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '邮箱', `mobile` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '手机', `score` int NOT NULL DEFAULT 0 COMMENT '积分', `token` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT '' COMMENT 'token', `bio` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '个人简介', `active` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否激活,1:是,0:否', `status` bit(1) NULL DEFAULT b'1' COMMENT '状态,1:使用,0:停用', `role_id` int NULL DEFAULT NULL COMMENT '用户角色', `create_time` datetime NOT NULL COMMENT '加入时间', `modify_time` datetime NULL DEFAULT NULL COMMENT '修改时间', PRIMARY KEY (`id`) USING BTREE, UNIQUE INDEX `user_name`(`username`) USING BTREE, INDEX `user_email`(`email`) USING BTREE, INDEX `user_create_time`(`create_time`) USING BTREE ) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_general_ci COMMENT = '用户表' ROW_FORMAT = DYNAMIC; -- ---------------------------- -- Records of ums_user -- ---------------------------- INSERT INTO `ums_user` VALUES ('1349290158897311745', 'admin', 'admin', '$2a$10$8qx711TBg/2hxfL7N.sxf.0ROMhR/iuPhQx33IFqGd7PLgt5nGJTO', 'https://s3.ax1x.com/2020/12/01/DfHNo4.jpg', '[email protected]', NULL, 2, '', '自由职业者', b'1', b'1', NULL, '2021-01-13 17:40:17', NULL); INSERT INTO `ums_user` VALUES ('1349618748226658305', 'zhangsan', 'zhangsan', '$2a$10$7K3yYv8sMV5Xsc2facXTcuyDo8JQ4FJHvjZ7qtWYcJdei3Q6Fvqdm', 'https://s3.ax1x.com/2020/12/01/DfHNo4.jpg', '[email protected]', NULL, 0, '', '自由职业者', b'1', b'1', NULL, '2021-01-14 15:25:59', NULL); SET FOREIGN_KEY_CHECKS = 1;
创建 maven 工程
<parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.3.8.RELEASE</version> <relativePath/> <!-- lookup parent from repository --> </parent>
<properties> <maven.compiler.source>8</maven.compiler.source> <maven.compiler.target>8</maven.compiler.target> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding> <mybatis-plus.version>3.4.2</mybatis-plus.version> <fastjson.version>1.2.75</fastjson.version> <hutool.version>5.5.7</hutool.version> <jwt.version>0.9.1</jwt.version> <emoji-java.version>5.1.1</emoji-java.version> </properties> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter</artifactId> </dependency> <!--web--> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <!--lombok--> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <optional>true</optional> </dependency> <!--jjwt--> <dependency> <groupId>io.jsonwebtoken</groupId> <artifactId>jjwt</artifactId> <version>${jwt.version}</version> </dependency> <!--emoji-java 表情包--> <dependency> <groupId>com.vdurmont</groupId> <artifactId>emoji-java</artifactId> <version>${emoji-java.version}</version> </dependency> <!-- lettuce pool 缓存连接池--> <dependency> <groupId>org.apache.commons</groupId> <artifactId>commons-pool2</artifactId> </dependency> <!--HuTool--> <dependency> <groupId>cn.hutool</groupId> <artifactId>hutool-all</artifactId> <version>${hutool.version}</version> </dependency> <!--mysql--> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <scope>runtime</scope> </dependency> <!--mybatis-plus--> <dependency> <groupId>com.baomidou</groupId> <artifactId>mybatis-plus-boot-starter</artifactId> <version>${mybatis-plus.version}</version> </dependency> <!--fastjson--> <dependency> <groupId>com.alibaba</groupId> <artifactId>fastjson</artifactId> <version>${fastjson.version}</version> </dependency> <!--yaml--> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-configuration-processor</artifactId> <optional>true</optional> </dependency> <!--bean validator--> <dependency> <groupId>org.hibernate.validator</groupId> <artifactId>hibernate-validator</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> <exclusions> <exclusion> <groupId>org.junit.vintage</groupId> <artifactId>junit-vintage-engine</artifactId> </exclusion> </exclusions> </dependency> </dependencies>
# 端口号 server: port: 8081 # 数据库 spring: datasource: driver-class-name: com.mysql.cj.jdbc.Driver username: root password: root url: jdbc:mysql://127.0.0.1:3306/nodepad_mblog?useUnicode=true&characterEncoding=utf8&autoRecOnnect=true&serverTimezOne=GMT%2B8 type: com.zaxxer.hikari.HikariDataSource # sql 日志打印 mybatis-plus: configuration: log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
// 我们这里还没有配置数据库,exclude = {DataSourceAutoConfiguration.class 就是启动时不加载数据库 // @SpringBootApplication(exclude = {DataSourceAutoConfiguration.class}) @Slf4j @SpringBootApplication public class BlogApplication { public static void main(String[] args) { ConfigurableApplicationContext cOntext= SpringApplication.run(BlogApplication.class, args); String port = context.getEnvironment().getProperty("server.port"); log.info("http://localhost:"+port); } }
启动项目测试有没有报错
public interface IErrorCode { /** * 错误编码: -1 失败;200 成功 * * @return 错误编码 */ Integer getCode(); /** * 错误描述 * * @return 错误描述 */ String getMessage(); }
public enum ApiErrorCode implements IErrorCode { /** * 成功 */ SUCCESS(200, "操作成功"), /** * 失败 */ FAILED(-1, "操作失败"), /** * 未登录,Token 过期 */ UNAUTHORIZED(401, "暂未登录或 token 已经过期"), /** * 权限不足 */ FORBIDDEN(403, "权限不足"), /** * 参数校验错误 */ VALIDATE_FAILED(404, "参数检验失败"); private final Integer code; private final String message; ApiErrorCode(int code, String message) { this.code = code; this.message = message; } @Override public Integer getCode() { return code; } @Override public String getMessage() { return message; } @Override public String toString() { return "ApiErrorCode{" + "code=" + code + ", message='" +message + '\'' + '}'; } }
import lombok.Data; import lombok.NoArgsConstructor; import java.io.Serializable; import java.util.Optional; @Data @NoArgsConstructor public class ApiResult<T> implements Serializable { private static final long serialVersiOnUID= -4153430394359594346L; /** * 业务状态码 */ private long code; /** * 结果集 */ private T data; /** * 接口描述 */ private String message; /** * 全参 * * @param code 业务状态码 * @param message 描述 * @param data 结果集 */ public ApiResult(long code, String message, T data) { this.code = code; this.message = message; this.data = data; } public ApiResult(IErrorCode errorCode) { errorCode = Optional.ofNullable(errorCode).orElse(ApiErrorCode.FAILED); this.code = errorCode.getCode(); this.message = errorCode.getMessage(); } /** * 成功 * * @param data 结果集 * @return {code:200,message:操作成功,data:自定义} */ public static <T> ApiResult<T> success() { return new ApiResult<T>(ApiErrorCode.SUCCESS.getCode(), ApiErrorCode.SUCCESS.getMessage(), null); } /** * 成功 * * @param data 结果集 * @return {code:200,message:操作成功,data:自定义} */ public static <T> ApiResult<T> success(T data) { return new ApiResult<T>(ApiErrorCode.SUCCESS.getCode(), ApiErrorCode.SUCCESS.getMessage(), data); } /** * 成功 * * @param data 结果集 * @param message 自定义提示信息 * @return {code:200,message:自定义,data:自定义} */ public static <T> ApiResult<T> success(T data, String message) { return new ApiResult<T>(ApiErrorCode.SUCCESS.getCode(), message, data); } /** * 失败返回结果 */ public static <T> ApiResult<T> failed() { return failed(ApiErrorCode.FAILED); } /** * 失败返回结果 * * @param message 提示信息 * @return {code:枚举 ApiErrorCode 取,message:自定义,data:null} */ public static <T> ApiResult<T> failed(String message) { return new ApiResult<T>(ApiErrorCode.FAILED.getCode(), message, null); } /** * 失败 * * @param errorCode 错误码 * @return {code:封装接口取,message:封装接口取,data:null} */ public static <T> ApiResult<T> failed(IErrorCode errorCode) { return new ApiResult<T>(errorCode.getCode(), errorCode.getMessage(), null); } /** * 失败返回结果 * * @param errorCode 错误码 * @param message 错误信息 * @return {code:枚举 ApiErrorCode 取,message:自定义,data:null} */ public static <T> ApiResult<T> failed(IErrorCode errorCode, String message) { return new ApiResult<T>(errorCode.getCode(), message, null); } /** * 参数验证失败返回结果 */ public static <T> ApiResult<T> validateFailed() { return failed(ApiErrorCode.VALIDATE_FAILED); } /** * 参数验证失败返回结果 * * @param message 提示信息 */ public static <T> ApiResult<T> validateFailed(String message) { return new ApiResult<T>(ApiErrorCode.VALIDATE_FAILED.getCode(), message, null); } /** * 未登录返回结果 */ public static <T> ApiResult<T> unauthorized(T data) { return new ApiResult<T>(ApiErrorCode.UNAUTHORIZED.getCode(), ApiErrorCode.UNAUTHORIZED.getMessage(), data); } /** * 未授权返回结果 */ public static <T> ApiResult<T> forbidden(T data) { return new ApiResult<T>(ApiErrorCode.FORBIDDEN.getCode(), ApiErrorCode.FORBIDDEN.getMessage(), data); } }
public class ApiException extends RuntimeException { private IErrorCode errorCode; public ApiException(IErrorCode errorCode) { super(errorCode.getMessage()); this.errorCode = errorCode; } public ApiException(String message) { super(message); } public IErrorCode getErrorCode() { return errorCode; } }
public class ApiAsserts { /** * 抛失败异常 * * @param message 说明 */ public static void fail(String message) { throw new ApiException(message); } /** * 抛失败异常 * * @param errorCode 状态码 */ public static void fail(IErrorCode errorCode) { throw new ApiException(errorCode); } }
import org.springframework.web.bind.annotation.ControllerAdvice; import org.springframework.web.bind.annotation.ExceptionHandler; import org.springframework.web.bind.annotation.ResponseBody; import java.util.Map; @ControllerAdvice public class GlobalExceptionHandler { /** * 捕获自定义异常 */ @ResponseBody @ExceptionHandler(value = ApiException.class) public ApiResult<Map<String, Object>> handle(ApiException e) { if (e.getErrorCode() != null) { return ApiResult.failed(e.getErrorCode()); } return ApiResult.failed(e.getMessage()); } }
![]() | 1 seven123 2021-02-22 10:06:54 +08:00 不错,感谢 |