SpringBoot实战项目:校园社区的后端实现

项目介绍

这是一个校园社区的后端实现。主要功能如下:

在这里插入图片描述

主要技术栈:

SpringBoot

MyBatisPlus

MySQL

Swagger

Websocket

前期工作

设计生成数据库表

建立数据库,数据库名scnu,
USE scnu,然后建表:

SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;

-- ----------------------------
-- Table structure for billboard_info
-- ----------------------------
DROP TABLE IF EXISTS `billboard_info`;
CREATE TABLE `billboard_info`  (
  `id` int(11) NOT NULL AUTO_INCREMENT COMMENT '主键',
  `content` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '公告',
  `create_time` datetime(0) NULL DEFAULT NULL COMMENT '公告时间',
  `showed` tinyint(1) NULL DEFAULT NULL COMMENT '1:展示中,0:过期',
  PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 6 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci COMMENT = '全站公告表' ROW_FORMAT = Dynamic;

-- ----------------------------
-- Table structure for comment_info
-- ----------------------------
DROP TABLE IF EXISTS `comment_info`;
CREATE TABLE `comment_info`  (
  `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(0) NOT NULL COMMENT '发布时间',
  `modify_time` datetime(0) 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 post_info
-- ----------------------------
DROP TABLE IF EXISTS `post_info`;
CREATE TABLE `post_info`  (
  `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(11) NOT NULL DEFAULT 0 COMMENT '评论统计',
  `collects` int(11) NOT NULL DEFAULT 0 COMMENT '收藏统计',
  `view` int(11) 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(11) NULL DEFAULT 0 COMMENT '专栏ID',
  `create_time` datetime(0) NOT NULL COMMENT '发布时间',
  `modify_time` datetime(0) 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 post_tag
-- ----------------------------
DROP TABLE IF EXISTS `post_tag`;
CREATE TABLE `post_tag`  (
  `id` int(11) NOT NULL AUTO_INCREMENT COMMENT '主键',
  `tag_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 '话题ID',
  PRIMARY KEY (`id`) USING BTREE,
  INDEX `tag_id`(`tag_id`) USING BTREE,
  INDEX `topic_id`(`topic_id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 52 CHARACTER SET = utf8 COLLATE = utf8_general_ci COMMENT = '话题-标签 中间表' ROW_FORMAT = Dynamic;

-- ----------------------------
-- Table structure for promotion_info
-- ----------------------------
DROP TABLE IF EXISTS `promotion_info`;
CREATE TABLE `promotion_info`  (
  `id` int(11) NOT NULL AUTO_INCREMENT COMMENT '主键',
  `title` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL COMMENT '广告标题',
  `link` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL COMMENT '广告链接',
  `description` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL COMMENT '说明',
  PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 4 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci COMMENT = '广告推广表' ROW_FORMAT = Dynamic;

-- ----------------------------
-- Table structure for tag_info
-- ----------------------------
DROP TABLE IF EXISTS `tag_info`;
CREATE TABLE `tag_info`  (
  `id` varchar(20) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT '标签ID',
  `name` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL DEFAULT '' COMMENT '标签',
  `topic_count` int(11) NOT NULL DEFAULT 0 COMMENT '关联话题',
  PRIMARY KEY (`id`) USING BTREE,
  UNIQUE INDEX `name`(`name`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_general_ci COMMENT = '标签表' ROW_FORMAT = Dynamic;

-- ----------------------------
-- Table structure for tip_info
-- ----------------------------
DROP TABLE IF EXISTS `tip_info`;
CREATE TABLE `tip_info`  (
  `id` int(10) UNSIGNED NOT NULL AUTO_INCREMENT COMMENT '主键',
  `content` varchar(1000) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL DEFAULT '' COMMENT '内容',
  `author` varchar(50) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT '' COMMENT '作者',
  `type` tinyint(4) NOT NULL COMMENT '1:使用,0:过期',
  PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 24864 CHARACTER SET = utf8 COLLATE = utf8_general_ci COMMENT = '每日赠言' ROW_FORMAT = Dynamic;

-- ----------------------------
-- Table structure for user_collect
-- ----------------------------
DROP TABLE IF EXISTS `user_collect`;
CREATE TABLE `user_collect`  (
  `id` int(11) NOT NULL AUTO_INCREMENT COMMENT '主键',
  `user_id` varchar(20) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '用户id',
  `topic_id` varchar(20) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '话题id',
  PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci COMMENT = '用户收藏表' ROW_FORMAT = Dynamic;

-- ----------------------------
-- Table structure for user_follow
-- ----------------------------
DROP TABLE IF EXISTS `user_follow`;
CREATE TABLE `user_follow`  (
  `id` int(11) NOT NULL AUTO_INCREMENT COMMENT '主键',
  `parent_id` varchar(20) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL COMMENT '被关注人ID',
  `follower_id` varchar(20) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL COMMENT '关注人ID',
  PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 130 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci COMMENT = '用户关注表' ROW_FORMAT = Dynamic;

-- ----------------------------
-- Table structure for user_info
-- ----------------------------
DROP TABLE IF EXISTS `user_info`;
CREATE TABLE `user_info`  (
  `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(11) 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(11) NULL DEFAULT NULL COMMENT '用户角色',
  `create_time` datetime(0) NOT NULL COMMENT '加入时间',
  `modify_time` datetime(0) 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;

-- ----------------------------
-- Table structure for user_like
-- ----------------------------
DROP TABLE IF EXISTS `user_like`;
CREATE TABLE `user_like`  (
  `id` int(11) UNSIGNED ZEROFILL NOT NULL AUTO_INCREMENT COMMENT '主键',
  `user_id` varchar(20) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '用户id',
  `topic_id` varchar(20) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '点赞的话题id',
  PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 1 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci COMMENT = '用户点赞表' ROW_FORMAT = Dynamic;

-- ----------------------------
-- Table structure for user_message
-- ----------------------------
DROP TABLE IF EXISTS `user_message`;
CREATE TABLE `user_message`  (
  `id` int(11) NOT NULL AUTO_INCREMENT COMMENT '主键',
  `user_id` varchar(20) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '被留言用户id',
  `luser_id` varchar(20) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '留言用户id',
  `content` varchar(500) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '留言内容',
  PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 1 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci COMMENT = '用户留言表' ROW_FORMAT = Dynamic;

-- ----------------------------
-- Table structure for user_chat
-- ----------------------------
DROP TABLE IF EXISTS `user_chat`;
CREATE TABLE `user_chat` (
  `id` int NOT NULL AUTO_INCREMENT COMMENT '主键',
  `send_id` varchar(20) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '发送用户id',
  `to_id` varchar(20) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '接收用户id',
  `content` varchar(500) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '消息内容',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3

不支持排序规则的话使用以下这版:

SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;

-- ----------------------------
-- Table structure for billboard_info
-- ----------------------------
DROP TABLE IF EXISTS `billboard_info`;
CREATE TABLE `billboard_info`  (
  `id` int(11) NOT NULL AUTO_INCREMENT COMMENT '主键',
  `content` varchar(255) CHARACTER SET utf8mb4 NOT NULL COMMENT '公告',
  `create_time` datetime(0) NULL DEFAULT NULL COMMENT '公告时间',
  `showed` 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;

-- ----------------------------
-- Table structure for comment_info
-- ----------------------------
DROP TABLE IF EXISTS `comment_info`;
CREATE TABLE `comment_info`  (
  `id` varchar(20) CHARACTER SET utf8 NOT NULL COMMENT '主键',
  `content` varchar(1000) CHARACTER SET utf8 NOT NULL DEFAULT '' COMMENT '内容',
  `user_id` varchar(20) CHARACTER SET utf8 NOT NULL COMMENT '作者ID',
  `topic_id` varchar(20) CHARACTER SET utf8 NOT NULL COMMENT 'topic_id',
  `create_time` datetime(0) NOT NULL COMMENT '发布时间',
  `modify_time` datetime(0) NULL DEFAULT NULL COMMENT '修改时间',
  PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8 COMMENT = '评论表' ROW_FORMAT = Dynamic;

-- ----------------------------
-- Table structure for post_info
-- ----------------------------
DROP TABLE IF EXISTS `post_info`;
CREATE TABLE `post_info`  (
  `id` varchar(20) CHARACTER SET utf8 NOT NULL COMMENT '主键',
  `title` varchar(255) CHARACTER SET utf8 NOT NULL DEFAULT '' COMMENT '标题',
  `content` longtext CHARACTER SET utf8 NULL COMMENT 'markdown内容',
  `user_id` varchar(20) CHARACTER SET utf8 NOT NULL COMMENT '作者ID',
  `comments` int(11) NOT NULL DEFAULT 0 COMMENT '评论统计',
  `collects` int(11) NOT NULL DEFAULT 0 COMMENT '收藏统计',
  `view` int(11) 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(11) NULL DEFAULT 0 COMMENT '专栏ID',
  `create_time` datetime(0) NOT NULL COMMENT '发布时间',
  `modify_time` datetime(0) 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 COMMENT = '话题表' ROW_FORMAT = Dynamic;

-- ----------------------------
-- Table structure for post_tag
-- ----------------------------
DROP TABLE IF EXISTS `post_tag`;
CREATE TABLE `post_tag`  (
  `id` int(11) NOT NULL AUTO_INCREMENT COMMENT '主键',
  `tag_id` varchar(20) CHARACTER SET utf8 NOT NULL COMMENT '标签ID',
  `topic_id` varchar(20) CHARACTER SET utf8 NOT NULL COMMENT '话题ID',
  PRIMARY KEY (`id`) USING BTREE,
  INDEX `tag_id`(`tag_id`) USING BTREE,
  INDEX `topic_id`(`topic_id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 52 CHARACTER SET = utf8 COMMENT = '话题-标签 中间表' ROW_FORMAT = Dynamic;

-- ----------------------------
-- Table structure for promotion_info
-- ----------------------------
DROP TABLE IF EXISTS `promotion_info`;
CREATE TABLE `promotion_info`  (
  `id` int(11) NOT NULL AUTO_INCREMENT COMMENT '主键',
  `title` varchar(255) CHARACTER SET utf8mb4 NULL DEFAULT NULL COMMENT '广告标题',
  `link` varchar(255) CHARACTER SET utf8mb4 NULL DEFAULT NULL COMMENT '广告链接',
  `description` varchar(255) CHARACTER SET utf8mb4 NULL DEFAULT NULL COMMENT '说明',
  PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 4 CHARACTER SET = utf8mb4 COMMENT = '广告推广表' ROW_FORMAT = Dynamic;

-- ----------------------------
-- Table structure for tag_info
-- ----------------------------
DROP TABLE IF EXISTS `tag_info`;
CREATE TABLE `tag_info`  (
  `id` varchar(20) CHARACTER SET utf8 NOT NULL COMMENT '标签ID',
  `name` varchar(255) CHARACTER SET utf8 NOT NULL DEFAULT '' COMMENT '标签',
  `topic_count` int(11) NOT NULL DEFAULT 0 COMMENT '关联话题',
  PRIMARY KEY (`id`) USING BTREE,
  UNIQUE INDEX `name`(`name`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8 COMMENT = '标签表' ROW_FORMAT = Dynamic;

-- ----------------------------
-- Table structure for tip_info
-- ----------------------------
DROP TABLE IF EXISTS `tip_info`;
CREATE TABLE `tip_info`  (
  `id` int(10) UNSIGNED NOT NULL AUTO_INCREMENT COMMENT '主键',
  `content` varchar(1000) CHARACTER SET utf8 NOT NULL DEFAULT '' COMMENT '内容',
  `author` varchar(50) CHARACTER SET utf8 NULL DEFAULT '' COMMENT '作者',
  `type` tinyint(4) NOT NULL COMMENT '1:使用,0:过期',
  PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 24864 CHARACTER SET = utf8 COMMENT = '每日赠言' ROW_FORMAT = Dynamic;

-- ----------------------------
-- Table structure for user_collect
-- ----------------------------
DROP TABLE IF EXISTS `user_collect`;
CREATE TABLE `user_collect`  (
  `id` int(11) NOT NULL AUTO_INCREMENT COMMENT '主键',
  `user_id` varchar(20) CHARACTER SET utf8mb4 NOT NULL COMMENT '用户id',
  `topic_id` varchar(20) CHARACTER SET utf8mb4 NOT NULL COMMENT '话题id',
  PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8mb4 COMMENT = '用户收藏表' ROW_FORMAT = Dynamic;

-- ----------------------------
-- Table structure for user_follow
-- ----------------------------
DROP TABLE IF EXISTS `user_follow`;
CREATE TABLE `user_follow`  (
  `id` int(11) 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;

-- ----------------------------
-- Table structure for user_info
-- ----------------------------
DROP TABLE IF EXISTS `user_info`;
CREATE TABLE `user_info`  (
  `id` varchar(20) CHARACTER SET utf8 NOT NULL COMMENT '用户ID',
  `username` varchar(15) CHARACTER SET utf8 NOT NULL DEFAULT '' COMMENT '用户名',
  `alias` varchar(255) CHARACTER SET utf8 NULL DEFAULT NULL COMMENT '用户昵称',
  `password` varchar(100) CHARACTER SET utf8 NULL DEFAULT '' COMMENT '密码',
  `avatar` varchar(1000) CHARACTER SET utf8 NULL DEFAULT NULL COMMENT '头像',
  `email` varchar(255) CHARACTER SET utf8 NULL DEFAULT NULL COMMENT '邮箱',
  `mobile` varchar(255) CHARACTER SET utf8 NULL DEFAULT NULL COMMENT '手机',
  `score` int(11) NOT NULL DEFAULT 0 COMMENT '积分',
  `token` varchar(255) CHARACTER SET utf8 NULL DEFAULT '' COMMENT 'token',
  `bio` varchar(255) CHARACTER SET utf8 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(11) NULL DEFAULT NULL COMMENT '用户角色',
  `create_time` datetime(0) NOT NULL COMMENT '加入时间',
  `modify_time` datetime(0) 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 COMMENT = '用户表' ROW_FORMAT = Dynamic;

-- ----------------------------
-- Table structure for user_like
-- ----------------------------
DROP TABLE IF EXISTS `user_like`;
CREATE TABLE `user_like`  (
  `id` int(11) UNSIGNED ZEROFILL NOT NULL AUTO_INCREMENT COMMENT '主键',
  `user_id` varchar(20) CHARACTER SET utf8mb4 NOT NULL COMMENT '用户id',
  `topic_id` varchar(20) CHARACTER SET utf8mb4 NOT NULL COMMENT '点赞的话题id',
  PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 1 CHARACTER SET = utf8mb4 COMMENT = '用户点赞表' ROW_FORMAT = Dynamic;

-- ----------------------------
-- Table structure for user_message
-- ----------------------------
DROP TABLE IF EXISTS `user_message`;
CREATE TABLE `user_message`  (
  `id` int(11) NOT NULL AUTO_INCREMENT COMMENT '主键',
  `user_id` varchar(20) CHARACTER SET utf8mb4 NOT NULL COMMENT '被留言用户id',
  `luser_id` varchar(20) CHARACTER SET utf8mb4 NOT NULL COMMENT '留言用户id',
  `content` varchar(500) CHARACTER SET utf8mb4 NOT NULL COMMENT '留言内容',
  PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 1 CHARACTER SET = utf8mb4 COMMENT = '用户留言表' ROW_FORMAT = Dynamic;

-- ----------------------------
-- Table structure for user_chat
-- ----------------------------
DROP TABLE IF EXISTS `user_chat`;
CREATE TABLE `user_chat` (
  `id` int NOT NULL AUTO_INCREMENT COMMENT '主键',
  `send_id` varchar(20) CHARACTER SET utf8mb4 NOT NULL COMMENT '发送用户id',
  `to_id` varchar(20) CHARACTER SET utf8mb4 NOT NULL COMMENT '接收用户id',
  `content` varchar(500) CHARACTER SET utf8mb4 NOT NULL COMMENT '消息内容',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3

MyBatisPlus代码生成器

先Spring Initializr建一个工程,在pom中导入要用到的依赖:

<?xml version="1.0" encoding="UTF-8"?>
<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.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.3.8.RELEASE</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>com.scnu</groupId>
    <artifactId>community</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>community</name>
    <description>Demo project for Spring Boot</description>
    <properties>
        <java.version>1.8</java.version>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
        <mybatis-plus.version>3.4.1</mybatis-plus.version>
        <velocity.version>2.0</velocity.version>
        <swagger.version>2.9.2</swagger.version>
        <swagger-bootstrap-ui.version>1.9.2</swagger-bootstrap-ui.version>
        <commons-lang3.version>3.9</commons-lang3.version>
        <commons-fileupload.version>1.3.1</commons-fileupload.version>
        <commons-io.version>2.6</commons-io.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-thymeleaf</artifactId>
        </dependency>

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

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

        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>

        <!--websocket-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-websocket</artifactId>
        </dependency>
        <!--分页-->
        <dependency>
            <groupId>com.github.pagehelper</groupId>
            <artifactId>pagehelper-spring-boot-starter</artifactId>
            <version>1.2.13</version>
        </dependency>

        <!--jjwt-->
        <dependency>
            <groupId>io.jsonwebtoken</groupId>
            <artifactId>jjwt</artifactId>
            <version>${jwt.version}</version>
        </dependency>
        <!--jwt类有用到这个包-->
        <dependency>
            <groupId>javax.xml.bind</groupId>
            <artifactId>jaxb-api</artifactId>
            <version>2.1</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>
        <!--mybatis-plus 代码生成器-->
        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-generator</artifactId>
            <version>${mybatis-plus.version}</version>
        </dependency>
        <!-- Mybatis Plus 代码生成器模板引擎,  -->
        <dependency>
            <groupId>org.apache.velocity</groupId>
            <artifactId>velocity-engine-core</artifactId>
            <version>${velocity.version}</version>
        </dependency>

        <!--swagger,生成文档-->
        <dependency>
            <groupId>io.springfox</groupId>
            <artifactId>springfox-swagger2</artifactId>
            <version>${swagger.version}</version>
        </dependency>
        <!--swagger ui,需要注释掉,不然无法使用下面的swagger-bootstrap-ui扩展-->
<!--        <dependency>-->
<!--            <groupId>io.springfox</groupId>-->
<!--            <artifactId>springfox-swagger-ui</artifactId>-->
<!--            <version>${swagger.version}</version>-->
<!--        </dependency>-->
        <!--swagger-bootstrap-ui扩展-->
        <dependency>
            <groupId>com.github.xiaoymin</groupId>
            <artifactId>swagger-bootstrap-ui</artifactId>
            <version>${swagger-bootstrap-ui.version}</version>
        </dependency>

        <!--commons-lang3-->
        <dependency>
            <groupId>org.apache.commons</groupId>
            <artifactId>commons-lang3</artifactId>
            <version>${commons-lang3.version}</version>
        </dependency>

        <!--文件上传-->
        <dependency>
            <groupId>commons-fileupload</groupId>
            <artifactId>commons-fileupload</artifactId>
            <version>${commons-fileupload.version}</version>
        </dependency>

        <!--commons-io-->
        <dependency>
            <groupId>commons-io</groupId>
            <artifactId>commons-io</artifactId>
            <version>${commons-io.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>

        <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>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
                <configuration>
                    <excludes>
                        <exclude>
                            <groupId>org.projectlombok</groupId>
                            <artifactId>lombok</artifactId>
                        </exclude>
                    </excludes>
                </configuration>
            </plugin>
        </plugins>
    </build>

</project>

Maven下载刷新完毕后,在主目录下建个config.CodeGenerator类,写MyBatisPlus的代码生成器:

package com.scnu.community.config;

import com.baomidou.mybatisplus.generator.config.GlobalConfig;
import com.baomidou.mybatisplus.generator.AutoGenerator;
import com.baomidou.mybatisplus.generator.InjectionConfig;
import com.baomidou.mybatisplus.generator.config.DataSourceConfig;
import com.baomidou.mybatisplus.generator.config.FileOutConfig;
import com.baomidou.mybatisplus.generator.config.PackageConfig;
import com.baomidou.mybatisplus.generator.config.StrategyConfig;
import com.baomidou.mybatisplus.generator.config.rules.NamingStrategy;

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

/**
 * 代码生成器
 * 这里生成的代码没有主键
 */
public class CodeGenerator {

    /**
     * <p>
     * 读取控制台内容
     * </p>
     */
    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("Rosemary");
        gc.setOpen(false);
        // gc.setSwagger2(true); 实体属性 Swagger2 注解
        mpg.setGlobalConfig(gc);

        // 数据源配置
        DataSourceConfig dsc = new DataSourceConfig();
        dsc.setUrl("jdbc:mysql://localhost:3306/scnu?useUnicode=true&useSSL=false&characterEncoding=utf8&serverTimezone=UTC");
        // dsc.setSchemaName("public");
        dsc.setDriverName("com.mysql.cj.jdbc.Driver");
        dsc.setUsername("root");
        dsc.setPassword("RoseMary");
        mpg.setDataSource(dsc);

        // 包配置
        PackageConfig pc = new PackageConfig();
        //设置模块名
        pc.setModuleName("community");
        //设置父路径
        pc.setParent("com.scnu");
        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.setFileCreate(new IFileCreate() {
            @Override
            public boolean isCreate(ConfigBuilder configBuilder, FileType fileType, String filePath) {
                // 判断自定义文件夹是否需要创建
                checkDir("调用默认方法创建的目录,自定义目录用");
                if (fileType == FileType.MAPPER) {
                    // 已经生成 mapper 文件判断存在,不想重新生成返回 false
                    return !new File(filePath).exists();
                }
                // 允许生成模板文件
                return true;
            }
        });
        */
        cfg.setFileOutConfigList(focList);
        mpg.setCfg(cfg);

        // 配置模板
        // TemplateConfig templateConfig = new TemplateConfig();

        // 配置自定义输出模板
        //指定自定义模板路径,注意不要带上.ftl/.vm, 会根据使用的模板引擎自动识别
        // templateConfig.setEntity("templates/entity2.java");
        // templateConfig.setService();
        // templateConfig.setController();

        // templateConfig.setXml(null);
        // mpg.setTemplate(templateConfig);

        // 策略配置
        StrategyConfig strategy = new StrategyConfig();
        strategy.setNaming(NamingStrategy.underline_to_camel);
        strategy.setColumnNaming(NamingStrategy.underline_to_camel);
        // strategy.setSuperEntityClass("你自己的父类实体,没有就不用设置!");
        strategy.setEntityLombokModel(true);
        strategy.setRestControllerStyle(true);
        // 公共父类
        // strategy.setSuperControllerClass("你自己的父类控制器,没有就不用设置!");
        // 写于父类中的公共字段
        strategy.setSuperEntityColumns("id");
        // strategy.setInclude(scanner("表名,多个英文逗号分割").split(","));
        strategy.setControllerMappingHyphenStyle(true);
        strategy.setTablePrefix(pc.getModuleName() + "_");
        mpg.setStrategy(strategy);
        // mpg.setTemplateEngine(new FreemarkerTemplateEngine());
        mpg.execute();
    }

}

注意调整数据库的url、用户名、密码,以及输出的路径等。

生成成功后,项目有了基本的结构,包括实体层、mapper、服务层和控制器:

在这里插入图片描述

后面需要自己手动实现的主要是控制器和服务层的实现。

项目配置

resource下的application的配置,这里使用的是yaml格式:

server:
  port: 9030

web:
  domain: http://localhost

spring:
  datasource:
    driver-class-name: com.mysql.cj.jdbc.Driver
    username: root
    password: RoseMary
    url: jdbc:mysql://localhost:3306/scnu?useUnicode=true&characterEncoding=utf8&autoReconnect=true&serverTimezone=GMT%2B8
    type: com.zaxxer.hikari.HikariDataSource

logging:
  level:
    root: info
    com.douyuehan.doubao: debug

注意调整数据库的配置。

调整Application主程序,注意加入mapper扫描:

package com.scnu.community;

import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.boot.web.servlet.support.SpringBootServletInitializer;

/**
 * @author Rosemary
 * @since 2022-09-18
 */
@MapperScan("com.scnu.community.mapper")
@SpringBootApplication
public class CommunityApplication extends SpringBootServletInitializer {

    @Override
    protected SpringApplicationBuilder configure(SpringApplicationBuilder builder) {
        return builder.sources(CommunityApplication.class);
    }

    public static void main(String[] args) {
        SpringApplication.run(CommunityApplication.class, args);
    }
}

Swagger配置

使用swagger2做API文档,写个配置文件config.Swagger2Config:

package com.scnu.community.config;

import com.google.common.base.Predicates;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import springfox.documentation.builders.ApiInfoBuilder;
import springfox.documentation.builders.PathSelectors;
import springfox.documentation.service.ApiInfo;
import springfox.documentation.service.Contact;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spring.web.plugins.Docket;
import springfox.documentation.swagger2.annotations.EnableSwagger2;

/**
 * @author Rosemary
 * @since 2022-09-18
 * 文档分类管理
 */
@Configuration
@EnableSwagger2
public class Swagger2Config {

    /**
     * 后端接口配置
     * 所有/.*的接口都展示在backApi下
     */
    @Bean
    public Docket adminApiConfig(){
        return new Docket(DocumentationType.SWAGGER_2)
                .groupName("backApi")
                .apiInfo(adminInfo())
                .select()
                .paths(Predicates.and(PathSelectors.regex("/.*")))
                //不显示错误的接口地址
                .paths(Predicates.not(PathSelectors.regex("/error.*")))//错误路径不监控
                .build();
    }


    /**
     * 定义后端adminInfo
     */
    private ApiInfo adminInfo(){

        return new ApiInfoBuilder()
                .title("华师社区系统API文档")
                .description("本文档描述了华师社区系统的各个模块的接口的调用方式")
                .version("1.0")
                .contact(new Contact("Rosemary", "http://scnu.com", "back@scnu.com"))
                .build();
    };


    /**
     * 前端接口配置
     */
    @Bean
    public Docket webApiConfig(){
        return new Docket(DocumentationType.SWAGGER_2)
                .groupName("webApi")
                .apiInfo(webApiInfo())
                .select()
                .paths(Predicates.and(PathSelectors.regex("/.*")))
                .build();
    }

    /**
     * 定义前端webApi
     */
    private ApiInfo webApiInfo(){
        return new ApiInfoBuilder()
                .title("华师社区系统网站API文档")
                .description("本文档描述了华师社区系统各个模块的接口的调用方式")
                .version("1.0")
                .contact(new Contact("Rosemary", "http://scnu.web.com", "web@scnu.com"))
                .build();
    }

}

配置完成后就可以进入http://localhost:9030/doc.html查看文档了。

此时还没写控制器,所以文档里什么都没有。

MyBatisPlus配置

common.mybatisplus下建立一个类:

package com.scnu.community.common.mybatisplus;

import com.baomidou.mybatisplus.annotation.DbType;
import com.baomidou.mybatisplus.autoconfigure.ConfigurationCustomizer;
import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;


@Configuration
@MapperScan("com.scnu.community.mapper")
public class MybatisPlusConfig {

    /**
     * 新的分页插件,一缓和二缓遵循mybatis的规则,
     * 需要设置 MybatisConfiguration#useDeprecatedExecutor = false 避免缓存出现问题(该属性会在旧插件移除后一同移除)
     */
    @Bean
    public MybatisPlusInterceptor mybatisPlusInterceptor() {
        MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
        interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));
        return interceptor;
    }

    @Bean
    public ConfigurationCustomizer configurationCustomizer() {
        return configuration -> configuration.setUseDeprecatedExecutor(false);
    }
}

业务实现

状态码与异常

建立result包和exception包,定义一些异常和返回给前端的错误码信息,包括:

result:

  • ResponseEnum,定义错误码及其信息
  • Res,返回给前端

exception:

  • BusinessException,自定义异常类
  • UnifiedExceptionHandler,统一异常处理
  • Assert,断言类

ResponseEnum:

package com.scnu.community.result;

import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.ToString;

/**
 * @author Rosemary
 * @since 2022-09-18
 */
@Getter
@ToString
@AllArgsConstructor
public enum ResponseEnum {

    //成功信息
    SUCCESS(200,"成功"),
    //失败信息
    ERROR(-1,"服务内部错误"),
    //-1xx 服务器错误
    BAD_SQL_GRAMMAR_ERROR(-101, "sql语法错误"),
    SERVLET_ERROR(-102, "servlet请求异常"), //-2xx 参数校验
    UPLOAD_ERROR(-103, "文件上传错误"),
    EXPORT_DATA_ERROR(-104, "数据导出失败"),

    //-2xx 参数校验
    BORROW_AMOUNT_NULL_ERROR(-201, "借款额度不能为空"),
    MOBILE_NULL_ERROR(-202, "手机号码不能为空"),
    MOBILE_ERROR(-203, "手机号码不正确"),
    PASSWORD_NULL_ERROR(-204, "密码不能为空"),
    CODE_NULL_ERROR(-205, "验证码不能为空"),
    CODE_ERROR(-206, "验证码错误"),
    MOBILE_EXIST_ERROR(-207, "手机号已被注册"),
    LOGIN_MOBILE_ERROR(-208, "用户不存在"),
    LOGIN_PASSWORD_ERROR(-209, "密码错误"),
    LOGIN_LOKED_ERROR(-210, "用户被锁定"),
    LOGIN_AUTH_ERROR(-211, "未登录"),
    EMAIL_NULL_ERROR(-212,"邮箱不能为空"),
    PASSWORD_NOT_EQUALS(-213,"两次输入的密码不相同"),
    
    USER_BIND_IDCARD_EXIST_ERROR(-301, "身份证号码已绑定"),
    USER_NO_BIND_ERROR(-302, "用户未绑定"),
    USER_NO_AMOUNT_ERROR(-303, "用户信息未审核"),
    USER_AMOUNT_LESS_ERROR(-304, "您的借款额度不足"),
    LEND_INVEST_ERROR(-305, "当前状态无法投标"),
    LEND_FULL_SCALE_ERROR(-306, "已满标,无法投标"),
    NOT_SUFFICIENT_FUNDS_ERROR(-307, "余额不足"),

    PAY_UNIFIEDORDER_ERROR(-401, "统一下单错误"),

    ALIYUN_RESPONSE_ERROR(-501, "阿里云短信服务响应失败"),
    ALIYUN_SMS_LIMIT_CONTROL_ERROR(-502, "短信发送过于频繁"),//业务限流
    ALIYUN_SMS_ERROR(-503, "短信发送失败"),//其他失败

    WEIXIN_CALLBACK_PARAM_ERROR(-601, "回调参数不正确"),
    WEIXIN_FETCH_ACCESSTOKEN_ERROR(-602, "获取access_token失败"),
    WEIXIN_FETCH_USERINFO_ERROR(-603, "获取用户信息失败");

    //响应状态码
    private Integer code;
    //响应信息
    private String message;

}

Res:

package com.scnu.community.result;

import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;

import java.util.HashMap;
import java.util.Map;

/**
 * @author Rosemary
 * @since 2022-09-18
 * 统一类,返回给前端
 * code 状态码
 * message 状态码对应消息
 * data 得到的数据
 */
@Data
@ApiModel(description = "返回结果对象")
public class Res {

    @ApiModelProperty(value = "状态码")
    private Integer code;

    @ApiModelProperty(value = "状态码对应信息")
    private String message;

    @ApiModelProperty(value = "返回数据")
    private Map<String,Object> data = new HashMap<>();

    /**
     * 构造函数私有化
     */
    public Res(){};

    /**
     * 考虑到经常要调用,且都是返回R对象,故使用static方法
     * @return result ok
     */
    public static Res ok(){
        Res r = new Res();
        r.setCode(ResponseEnum.SUCCESS.getCode());
        r.setMessage(ResponseEnum.SUCCESS.getMessage());
        return r;
    }

    /**
     * 考虑到经常要调用,故使用static方法
     * @return result error
     */
    public static Res error(){
        Res r = new Res();
        r.setCode(ResponseEnum.ERROR.getCode());
        r.setMessage(ResponseEnum.ERROR.getMessage());
        return r;
    }

    /**
     * 设置特定结果
     * @param responseEnum 响应枚举类
     * @return result
     */
    public static Res setResult(ResponseEnum responseEnum){
        Res r = new Res();
        r.setCode(responseEnum.getCode());
        r.setMessage(responseEnum.getMessage());
        return r;
    }

    /**
     * 设置结果data
     * @param key
     * @param value
     * @return
     */
    public Res data(String key, Object value){
        this.data.put(key, value);
        return this;
    }

    /**
     *参数是集合的情况
     * @param map
     * @return
     */
    public Res data(Map<String,Object> map){
        this.setData(map);
        return this;
    }

    /**
     * 设置特定的消息
     * @param message
     * @return
     */
    public Res message(String message){
        this.setMessage(message);
        return this;
    }

    /**
     * 方便扩展其他状态码
     * @param Code
     * @return
     */
    public Res code(Integer Code){
        this.setCode(code);
        return this;
    }
}

自定义异常类:

package com.scnu.community.exception;

import com.scnu.community.result.ResponseEnum;
import lombok.Data;
import lombok.NoArgsConstructor;

/**
 * @author Rosemary
 * @since 2022-09-18
 * 定义错误码和消息,而错误码和消息相对应(在ResponseEnum中定义)
 * 自定义异常类,用运行时异常,否则在controller中写代码时
 * 要么一直往上抛,要么就必须用try catch
 */
@Data
@NoArgsConstructor
public class BusinessException extends RuntimeException {

    //状态码
    private Integer code;
    //消息
    private String message;

    /**
     *
     * @param message 错误消息
     */
    public BusinessException(String message) {
        this.message = message;
    }

    /**
     * @param message 错误消息
     * @param code 错误码
     */
    public BusinessException(String message, Integer code) {
        this.message = message;
        this.code = code;
    }

    /**
     *
     * @param message 错误消息
     * @param code 错误码
     * @param cause 原始异常对象
     */
    public BusinessException(String message, Integer code, Throwable cause) {
        super(cause);
        this.message = message;
        this.code = code;
    }

    /**
     *
     * @param resultCodeEnum 接收枚举类型
     */
    public BusinessException(ResponseEnum resultCodeEnum) {
        this.message = resultCodeEnum.getMessage();
        this.code = resultCodeEnum.getCode();
    }

    /**
     *
     * @param resultCodeEnum 接收枚举类型
     * @param cause 原始异常对象
     */
    public BusinessException(ResponseEnum resultCodeEnum, Throwable cause) {
        super(cause);
        this.message = resultCodeEnum.getMessage();
        this.code = resultCodeEnum.getCode();
    }
}

统一异常处理:

package com.scnu.community.exception;


import com.scnu.community.result.Res;
import com.scnu.community.result.ResponseEnum;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.ConversionNotSupportedException;
import org.springframework.beans.TypeMismatchException;
import org.springframework.http.converter.HttpMessageNotReadableException;
import org.springframework.http.converter.HttpMessageNotWritableException;
import org.springframework.jdbc.BadSqlGrammarException;
import org.springframework.web.HttpMediaTypeNotAcceptableException;
import org.springframework.web.HttpMediaTypeNotSupportedException;
import org.springframework.web.HttpRequestMethodNotSupportedException;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.MissingPathVariableException;
import org.springframework.web.bind.MissingServletRequestParameterException;
import org.springframework.web.bind.ServletRequestBindingException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import org.springframework.web.context.request.async.AsyncRequestTimeoutException;
import org.springframework.web.multipart.support.MissingServletRequestPartException;
import org.springframework.web.servlet.NoHandlerFoundException;

/**
 * @author Rosemary
 * @since 2022-09-18
 * 统一异常处理
 * RestControllerAdvice : 统一异常处理,带有Rest则全部返回json
 */
@Slf4j
@RestControllerAdvice
public class UnifiedExceptionHandler{


    @ExceptionHandler(value = Exception.class)
    public Res handleException(Exception e){
        log.error(e.getMessage(),e);
        return Res.error();
    }

    @ExceptionHandler(value = BadSqlGrammarException.class)
    public Res handleException(BadSqlGrammarException e){
        log.error(e.getMessage(),e);
        return Res.setResult(ResponseEnum.BAD_SQL_GRAMMAR_ERROR);
    }

    /**
     * 处理自定义异常,当有新的异常时,只需要在controller中
     * 设置状态码和消息,则其会被捕获到
     * @param e
     * @return
     */
    @ExceptionHandler(value = BusinessException.class)
    public Res handleException(BusinessException e){
        log.error(e.getMessage(),e);
        return Res.error().message(e.getMessage());
    }


    /**
     * Controller上一层相关异常,即servlet请求异常
     */
    @ExceptionHandler({
            NoHandlerFoundException.class,
            HttpRequestMethodNotSupportedException.class,
            HttpMediaTypeNotSupportedException.class,
            MissingPathVariableException.class,
            MissingServletRequestParameterException.class,
            TypeMismatchException.class,
            HttpMessageNotReadableException.class,
            HttpMessageNotWritableException.class,
            MethodArgumentNotValidException.class,
            HttpMediaTypeNotAcceptableException.class,
            ServletRequestBindingException.class,
            ConversionNotSupportedException.class,
            MissingServletRequestPartException.class,
            AsyncRequestTimeoutException.class
    })
    public Res handleServletException(Exception e) {
        log.error(e.getMessage(), e);
        //SERVLET_ERROR(-102, "servlet请求异常"),
        return Res.error().message(ResponseEnum.SERVLET_ERROR.getMessage()).code(ResponseEnum.SERVLET_ERROR.getCode());
    }
}

断言类:

package com.scnu.community.exception;

import com.scnu.community.result.ResponseEnum;
import lombok.extern.slf4j.Slf4j;
import org.springframework.util.StringUtils;

/**
 * @author Rosemary
 * @since 2022-09-18
 */
@Slf4j
public class Assert {

    /**
     * 断言对象不为空
     * obj 为空则抛异常
     * @param obj
     * @param responseEnum
     */
    public static void notNull(Object obj, ResponseEnum responseEnum){
        if(obj == null){
            log.info("obj is null.....................");
            throw new BusinessException(responseEnum);
        }
    }


    /**
     * 断言对象为空
     * 如果对象obj不为空,则抛出异常
     * @param object
     * @param responseEnum
     */
    public static void isNull(Object object, ResponseEnum responseEnum) {
        if (object != null) {
            log.info("obj is not null......");
            throw new BusinessException(responseEnum);
        }
    }

    /**
     * 断言表达式为真
     * 如果不为真,则抛出异常
     * @param expression 是否成功
     */
    public static void isTrue(boolean expression, ResponseEnum responseEnum) {
        if (!expression) {
            log.info("fail...............");
            throw new BusinessException(responseEnum);
        }
    }

    /**
     * 断言两个对象不相等
     * 如果相等,则抛出异常
     * @param m1
     * @param m2
     * @param responseEnum
     */
    public static void notEquals(Object m1, Object m2,  ResponseEnum responseEnum) {
        if (m1.equals(m2)) {
            log.info("equals...............");
            throw new BusinessException(responseEnum);
        }
    }

    /**
     * 断言两个对象相等
     * 如果不相等,则抛出异常
     * @param m1
     * @param m2
     * @param responseEnum
     */
    public static void equals(Object m1, Object m2,  ResponseEnum responseEnum) {
        if (!m1.equals(m2)) {
            log.info("not equals...............");
            throw new BusinessException(responseEnum);
        }
    }

    /**
     * 断言参数不为空
     * 如果为空,则抛出异常
     * @param s
     * @param responseEnum
     */
    public static void notEmpty(String s, ResponseEnum responseEnum) {
        if (StringUtils.isEmpty(s)) {
            log.info("is empty...............");
            throw new BusinessException(responseEnum);
        }
    }

}

工具准备

建立一个utils包,准备几个工具,包括:

  1. JwtUtils
  2. MD5密码加密工具
  3. websocket聊天工具

JwtUtils:

package com.scnu.community.utils;



import com.scnu.community.exception.BusinessException;
import com.scnu.community.result.ResponseEnum;
import io.jsonwebtoken.*;
import org.springframework.util.StringUtils;

import javax.crypto.spec.SecretKeySpec;
import javax.xml.bind.DatatypeConverter;
import java.util.Date;

/**
 * @author Rosemary
 * @since 2022-09-18
 */
public class JwtUtils {

    private static final String tokenSignKey = "1234rosemary";

    private static SecretKeySpec getKeyInstance(){
        SignatureAlgorithm signatureAlgorithm = SignatureAlgorithm.HS256;
        byte[] bytes = DatatypeConverter.parseBase64Binary(tokenSignKey);
        return new SecretKeySpec(bytes,signatureAlgorithm.getJcaName());
    }

    public static String createToken(Long userId, String userName) {
        //登录有效期是1天
        long tokenExpiration = 24 * 60 * 60 * 1000;
        String token = Jwts.builder()
                .setSubject("SRB-USER")
                .setExpiration(new Date(System.currentTimeMillis() + tokenExpiration))
                .claim("userId", userId)
                .claim("userName", userName)
                //服务器私钥
                .signWith(SignatureAlgorithm.HS512, getKeyInstance())
                .compressWith(CompressionCodecs.GZIP)
                .compact();
        return token;
    }

    /**
     * 判断token是否有效
     * @param token
     * @return
     */
    public static boolean checkToken(String token) {
        if(StringUtils.isEmpty(token)) {
            return false;
        }
        try {
            Jwts.parser().setSigningKey(getKeyInstance()).parseClaimsJws(token);
            return true;
        } catch (Exception e) {
            return false;
        }
    }


    public static Long getUserId(String token) {
        Claims claims = getClaims(token);
        Long userId = (Long)claims.get("userId");
        return userId;
        // return userId.longValue();
    }

    public static String getUserName(String token) {
        Claims claims = getClaims(token);
        return (String)claims.get("userName");
    }

    public static void removeToken(String token) {
        //jwttoken无需删除,客户端扔掉即可。
    }

    /**
     * 校验token并返回Claims
     * @param token
     * @return
     */
    private static Claims getClaims(String token) {
        if(StringUtils.isEmpty(token)) {
            // LOGIN_AUTH_ERROR(-211, "未登录"),
            throw new BusinessException(ResponseEnum.LOGIN_AUTH_ERROR);
        }
        try {
            Jws<Claims> claimsJws = Jwts.parser().setSigningKey(getKeyInstance()).parseClaimsJws(token);
            Claims claims = claimsJws.getBody();
            return claims;
        } catch (Exception e) {
            throw new BusinessException(ResponseEnum.LOGIN_AUTH_ERROR);
        }
    }
}

MD5:

package com.scnu.community.utils;

import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;

/**
 * @author Rosemary
 * @since 2022-09-18
 */
public final class MD5Utils {

    public static String encrypt(String strSrc) {
        try {
            char[] hexChars = { '0', '1', '2', '3', '4', '5', '6', '7', '8',
                    '9', 'a', 'b', 'c', 'd', 'e', 'f' };
            byte[] bytes = strSrc.getBytes();
            MessageDigest md = MessageDigest.getInstance("MD5");
            md.update(bytes);
            bytes = md.digest();
            int j = bytes.length;
            char[] chars = new char[j * 2];
            int k = 0;
            for (int i = 0; i < bytes.length; i++) {
                byte b = bytes[i];
                chars[k++] = hexChars[b >>> 4 & 0xf];
                chars[k++] = hexChars[b & 0xf];
            }
            return new String(chars);
        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
            throw new RuntimeException("MD5加密出错!!+" + e);
        }
    }


}

websocket:

package com.scnu.community.utils;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.google.common.collect.Maps;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;

import javax.websocket.*;
import javax.websocket.server.PathParam;
import javax.websocket.server.ServerEndpoint;
import java.io.IOException;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;

/**
 * @author Rosemary
 * @since 2022-09-18
 */
@Component
@ServerEndpoint("/websocket/{username}")
public class WebSocket {
    private Logger logger = LoggerFactory.getLogger(this.getClass());
    /**
     * 在线人数
     */
    public static int onlineNumber = 0;
    /**
     * 以用户的姓名为key,WebSocket为对象保存起来
     */
    private static Map<String, WebSocket> clients = new ConcurrentHashMap<String, WebSocket>();
    /**
     * 会话
     */
    private Session session;
    /**
     * 用户名称
     */
    private String username;
    /**
     * 建立连接
     *
     * @param session
     */
    @OnOpen
    public void onOpen(@PathParam("username") String username, Session session)
    {
        onlineNumber++;
        logger.info("现在来连接的客户id:"+session.getId()+"用户名:"+username);
        this.username = username;
        this.session = session;
        logger.info("有新连接加入! 当前在线人数" + onlineNumber);
        try {
            //messageType 1代表上线 2代表下线 3代表在线名单 4代表普通消息
            //先给所有人发送通知,说我上线了
            Map<String,Object> map1 = Maps.newHashMap();
            map1.put("messageType",1);
            map1.put("username",username);
            sendMessageAll(JSON.toJSONString(map1),username);

            //把自己的信息加入到map当中去
            clients.put(username, this);
            //给自己发一条消息:告诉自己现在都有谁在线
            Map<String,Object> map2 = Maps.newHashMap();
            map2.put("messageType",3);
            //移除掉自己
            Set<String> set = clients.keySet();
            map2.put("onlineUsers",set);
            sendMessageTo(JSON.toJSONString(map2),username);
        }
        catch (IOException e){
            logger.info(username+"上线的时候通知所有人发生了错误");
        }



    }

    @OnError
    public void onError(Session session, Throwable error) {
        logger.info("服务端发生了错误"+error.getMessage());
        //error.printStackTrace();
    }
    /**
     * 连接关闭
     */
    @OnClose
    public void onClose()
    {
        onlineNumber--;
        //webSockets.remove(this);
        clients.remove(username);
        try {
            //messageType 1代表上线 2代表下线 3代表在线名单  4代表普通消息
            Map<String,Object> map1 = Maps.newHashMap();
            map1.put("messageType",2);
            map1.put("onlineUsers",clients.keySet());
            map1.put("username",username);
            sendMessageAll(JSON.toJSONString(map1),username);
        }
        catch (IOException e){
            logger.info(username+"下线的时候通知所有人发生了错误");
        }
        logger.info("有连接关闭! 当前在线人数" + onlineNumber);
    }

    /**
     * 收到客户端的消息
     *
     * @param message 消息
     * @param session 会话
     */
    @OnMessage
    public void onMessage(String message, Session session)
    {
        try {
            logger.info("来自客户端消息:" + message+"客户端的id是:"+session.getId());

            System.out.println("------------  :"+message);

            JSONObject jsonObject = JSON.parseObject(message);
            String textMessage = jsonObject.getString("message");
            String fromusername = jsonObject.getString("username");
            String tousername = jsonObject.getString("to");
            //如果不是发给所有,那么就发给某一个人
            //messageType 1代表上线 2代表下线 3代表在线名单  4代表普通消息
            Map<String,Object> map1 = Maps.newHashMap();
            map1.put("messageType",4);
            map1.put("textMessage",textMessage);
            map1.put("fromusername",fromusername);
            if(tousername.equals("All")){
                map1.put("tousername","所有人");
                sendMessageAll(JSON.toJSONString(map1),fromusername);
            }
            else{
                map1.put("tousername",tousername);
                sendMessageTo(JSON.toJSONString(map1),tousername);
            }
        }
        catch (Exception e){

            e.printStackTrace();
            logger.info("发生了错误了");
        }

    }


    public void sendMessageTo(String message, String ToUserName) throws IOException {
        for (WebSocket item : clients.values()) {


        //    System.out.println("在线人员名单  :"+item.username.toString());
            if (item.username.equals(ToUserName) ) {
                item.session.getAsyncRemote().sendText(message);

                break;
            }
        }
    }

    public void sendMessageAll(String message,String FromUserName) throws IOException {
        for (WebSocket item : clients.values()) {
            item.session.getAsyncRemote().sendText(message);
        }
    }

    public static synchronized int getOnlineCount() {
        return onlineNumber;
    }

}

处理实体层

前面遗留了一个问题,生成代码的时候,strategy.setSuperEntityColumns(“id”)没有注掉,导致生成的实体类没有id。可以把id手动敲进去,类型为long。

在entity下建一个pojo包放生成的实体类,再建一个vo包写几个vo:

  1. 登陆vo
  2. 发帖vo
  3. 注册vo
  4. 用户信息vo(用来返回给前端)

登陆vo:

package com.scnu.community.entity.vo;

import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;

/**
 * @author Rosemary
 * @since 2022-09-18
 */
@Data
@ApiModel(description = "登录对象")
public class LoginVO {

    @ApiModelProperty(value = "用户手机")
    private String mobile;

    @ApiModelProperty(value = "用户密码")
    private String password;

}

发帖vo:

package com.scnu.community.entity.vo;

import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;

import java.util.List;

/**
 * @author Rosemary
 * @since 2022-09-18
 */
@Data
@ApiModel(description = "发帖对象")
public class PostVO {

    /**
     * 标题
     */
    @ApiModelProperty(value = "帖子标题")
    private String title;

    /**
     * 内容
     */
    @ApiModelProperty(value = "帖子内容")
    private String content;

    /**
     * 标签
     */
    @ApiModelProperty(value = "帖子标签")
    private List<String> tags;
}

注册vo:

package com.scnu.community.entity.vo;

import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;

/**
 * @author Rosemary
 * @since 2022-09-18
 */
@Data
@ApiModel(description = "注册对象")
public class RegisterVO {

    @ApiModelProperty(value = "手机号")
    private String mobile;

    @ApiModelProperty(value = "密码")
    private String password;

    @ApiModelProperty(value = "确认密码")
    private String rePassword;


    @ApiModelProperty(value = "邮箱")
    private String email;


}

用户信息vo:

package com.scnu.community.entity.vo;

import com.scnu.community.entity.pojo.PostInfo;
import com.scnu.community.entity.pojo.UserMessage;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;

import java.util.List;

/**
 * @author Rosemary
 * @since 2022-09-18
 * 返回给前端
 */
@Data
@ApiModel(description="用户信息对象")
public class UserInfoVO {

    @ApiModelProperty(value = "用户表id")
    private Long id;

    @ApiModelProperty(value = "用户姓名")
    private String name;

    @ApiModelProperty(value = "用户昵称")
    private String alias;

    @ApiModelProperty(value = "头像")
    private String avatar;

    @ApiModelProperty(value = "手机号")
    private String mobile;

    @ApiModelProperty(value = "邮箱")
    private String email;

    @ApiModelProperty(value = "个人简介")
    private String bio;

    @ApiModelProperty(value = "邮箱")
    private Integer roleId;

    @ApiModelProperty(value = "我的帖子")
    private List<PostInfo> myPosts;

    @ApiModelProperty(value = "我的点赞")
    private List<PostInfo> myLikes;

    @ApiModelProperty(value = "我的收藏")
    private List<PostInfo> myCollects;

    @ApiModelProperty(value = "给我的留言")
    private List<UserMessage> messageList;

    /**
     * jwt 访问令牌
     */
    @ApiModelProperty(value = "JWT访问令牌")
    private String token;
}

服务层方法与实现

先以公告板功能为例,完成其服务层实现与控制器,测试能否在Api文档中找到他。

在IBillboardInfoService接口中,添加一个String getContent()方法,

代码会显示1 related problem,因为这个方法还没有在BillboardInfoServiceImpl中进行实现,

下面我们实现他:

    @Resource
    private BillboardInfoServiceImpl billboardInfoService;

    @Transactional(rollbackFor = Exception.class)
    @Override
    public String getContent() {

        //查出所有showed为1的信息
        QueryWrapper<BillboardInfo> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("showed", 1);
        List<BillboardInfo> list = baseMapper.selectList(queryWrapper);

        //random随机生成,范围[0,size-1]
        int random = (new Random()).nextInt(list.size());
        //随机返回一条信息
        return list.get(random).getContent();
    }

以上代码会在未过期的信息中随机返回一条。

控制器:

@Resource
private IBillboardInfoService iBillboardInfoService;

@ApiOperation("获取公告板信息")
@GetMapping("/billboardinfo")
public Res getBillboardInfo(){

    //获取公告板信息
    String content = iBillboardInfoService.getContent();
    return Res.ok().data("billboardinfo",content);
}

打开http://localhost:9030/doc.html,可以看到这个控制器,点击调试,可以看到返回信息:

{
  "code": 200,
  "message": "成功",
  "data": {
    "billboardinfo": "R1.0 开始已实现护眼模式 ,妈妈再也不用担心我的眼睛了。"
  }
}

其他控制器不一边写一边测试了,先将服务层方法与实现写完。

最核心的两个接口是帖子信息接口和用户信息接口,里面的方法比较多,需要先将其要调用的方法实现了,

其中用户信息方法需要调用帖子信息方法,所以最后实现。

在实现帖子信息的方法之前,需要先准备好标签、收藏、点赞的相关方法,

下面先来实现IPostTagService中需要的方法:

List<String> getPostByTag(Long tagId);

Impl:

package com.scnu.community.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.scnu.community.entity.pojo.PostTag;
import com.scnu.community.mapper.PostTagMapper;
import com.scnu.community.service.IPostTagService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

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

/**
 * <p>
 * 话题-标签 中间表 服务实现类
 * </p>
 *
 * @author Rosemary
 * @since 2022-09-18
 */
@Service
public class PostTagServiceImpl extends ServiceImpl<PostTagMapper, PostTag> implements IPostTagService {

    @Transactional(rollbackFor = Exception.class)
    @Override
    public List<String> getPostByTag(Long tagId) {

        QueryWrapper<PostTag> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("tag_id", tagId.toString());

        List<PostTag> tagPosts = baseMapper.selectList(queryWrapper);
        List<String> list = new ArrayList<>();

        //从获取的记录中获取出postId,即topicId
        for (PostTag u:tagPosts
        ) {
            list.add(u.getTopicId());
        }
        return list;
    }
}

实现IUserCollectService中的:

List<String> getCollectPosts(Long userId);

Impl:

package com.scnu.community.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.scnu.community.entity.pojo.UserCollect;
import com.scnu.community.mapper.UserCollectMapper;
import com.scnu.community.service.IUserCollectService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

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

/**
 * <p>
 * 用户收藏表 服务实现类
 * </p>
 *
 * @author Rosemary
 * @since 2022-09-18
 */
@Service
public class UserCollectServiceImpl extends ServiceImpl<UserCollectMapper, UserCollect> implements IUserCollectService {

    /**
     * 获取userId对应的收藏帖子的id
     * @param userId
     * @return
     */
    @Transactional(rollbackFor = Exception.class)
    @Override
    public List<String> getCollectPosts(Long userId) {

        QueryWrapper<UserCollect> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("user_id", userId.toString());

        List<UserCollect> userCollects = baseMapper.selectList(queryWrapper);
        List<String> list = new ArrayList<>();

        //从获取的记录中获取出postId,即topicId
        for (UserCollect u:userCollects
        ) {
            list.add(u.getTopicId());
        }
        return list;
    }

}

实现IUserLikeService中的:

List<String> getLikePosts(Long userId);

Impl:

package com.scnu.community.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.scnu.community.entity.pojo.UserLike;
import com.scnu.community.mapper.UserLikeMapper;
import com.scnu.community.service.IUserLikeService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

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

/**
 * <p>
 * 用户点赞表 服务实现类
 * </p>
 *
 * @author Rosemary
 * @since 2022-09-18
 */
@Service
public class UserLikeServiceImpl extends ServiceImpl<UserLikeMapper, UserLike> implements IUserLikeService {

    /**
     * 获取userId对应的点赞帖子的id
     * @param userId
     * @return list
     */
    @Transactional(rollbackFor = Exception.class)
    @Override
    public List<String> getLikePosts(Long userId) {

        QueryWrapper<UserLike> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("user_id", userId.toString());

        List<UserLike> userCollects = baseMapper.selectList(queryWrapper);
        List<String> list = new ArrayList<>();

        //从获取的记录中获取出postId,即topicId
        for (UserLike u:userCollects
        ) {
            list.add(u.getTopicId());
        }
        return list;
    }
}

下面实现帖子信息相关方法:

List<PostInfo> getHotPost();

List<PostInfo> getLastPost();

List<PostInfo> getUserPost(Long userId);

List<PostInfo> getCollectPost(Long userId);

List<PostInfo> getLikePost(Long userId);

List<PostInfo> searchPosts(String search);

Boolean deletePost(Long id, Long userId);

void doPost(Long userId, PostVO postVO);

void editPost(PostInfo postInfo,Long userId);

List<PostInfo> getPostByTag(Long tagId);

Impl:

package com.scnu.community.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.scnu.community.entity.pojo.PostInfo;
import com.scnu.community.entity.vo.PostVO;
import com.scnu.community.mapper.PostInfoMapper;
import com.scnu.community.service.IPostInfoService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.scnu.community.service.IPostTagService;
import com.scnu.community.service.IUserCollectService;
import com.scnu.community.service.IUserLikeService;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;

/**
 * <p>
 * 话题表 服务实现类
 * </p>
 *
 * @author Rosemary
 * @since 2022-09-18
 */
@Service
public class PostInfoServiceImpl extends ServiceImpl<PostInfoMapper, PostInfo> implements IPostInfoService {

    @Resource
    private IUserCollectService iUserCollectService;

    @Resource
    private IUserLikeService iUserLikeService;

    @Resource
    private IPostTagService iPostTagService;

    /**
     * 根据标签获取帖子
     * @return list
     */
    @Transactional(rollbackFor = Exception.class)
    @Override
    public List<PostInfo> getPostByTag(Long tagId) {

        List<PostInfo> list = new ArrayList<>();

        List<String> tagPosts = iPostTagService.getPostByTag(tagId);
        for (String s:tagPosts
        ) {
            PostInfo postInfo = baseMapper.selectById(s);
            list.add(postInfo);
        }
        return list;
    }
    /**
     * 获取热门讨论贴
     * @return list
     */
    @Transactional(rollbackFor = Exception.class)
    @Override
    public List<PostInfo> getHotPost() {

        //浏览量作为热门贴的依据
        QueryWrapper<PostInfo> queryWrapper = new QueryWrapper<>();
        //按浏览量逆序排序
        queryWrapper.orderByDesc("view");
        List<PostInfo> postInfoList = baseMapper.selectList(queryWrapper);
        return postInfoList;
    }

    /**
     * 获取最新讨论帖
     * @return list
     */
    @Transactional(rollbackFor = Exception.class)
    @Override
    public List<PostInfo> getLastPost() {

        QueryWrapper<PostInfo> queryWrapper = new QueryWrapper<>();
        //按时间逆序排序
        queryWrapper.orderByDesc("create_time");
        List<PostInfo> postInfoList = baseMapper.selectList(queryWrapper);
        return postInfoList;
    }

    /**
     * 通过userId获取对应的帖子
     * @return list
     */
    @Transactional(rollbackFor = Exception.class)
    @Override
    public List<PostInfo> getUserPost(Long userId) {

        QueryWrapper<PostInfo> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("user_id",userId);
        List<PostInfo> postInfoList = baseMapper.selectList(queryWrapper);
        return postInfoList;
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public List<PostInfo> getCollectPost(Long userId) {

        //放置收藏的post
        List<PostInfo> list = new ArrayList<>();

        //获取用户收藏的post的id
        List<String> collectPosts = iUserCollectService.getCollectPosts(userId);
        for (String s:collectPosts
        ) {
            PostInfo postInfo = baseMapper.selectById(s);
            list.add(postInfo);
        }
        return list;
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public List<PostInfo> getLikePost(Long userId) {

        //放置点赞的post
        List<PostInfo> list = new ArrayList<>();

        //获取用户点赞的post的id
        List<String> likePosts = iUserLikeService.getLikePosts(userId);
        for (String s:likePosts
        ) {
            PostInfo postInfo = baseMapper.selectById(s);
            list.add(postInfo);
        }
        return list;
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public List<PostInfo> searchPosts(String search) {
        QueryWrapper<PostInfo> queryWrapper = new QueryWrapper<>();
        queryWrapper.like("title",search);
        List<PostInfo> postInfoList = baseMapper.selectList(queryWrapper);
        return postInfoList;
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public Boolean deletePost(Long id, Long userId) {

        QueryWrapper<PostInfo> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("id", id)
                .eq("userId", userId);
        int delete = baseMapper.delete(queryWrapper);

        Boolean flag = true;

        //为0说明没删除成功
        if (delete==0){
            flag = false;
        }
        return flag;
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public void doPost(Long userId, PostVO postVO) {

        PostInfo postInfo = new PostInfo();
        postInfo.setTitle(postVO.getTitle());
        postInfo.setUserId(userId.toString());
        postInfo.setContent(postVO.getContent());
        postInfo.setCreateTime(LocalDateTime.now());

        baseMapper.insert(postInfo);
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public void editPost(PostInfo postInfo,Long userId) {

        Long id = postInfo.getId();
        postInfo.setUserId(userId.toString());
        //修改一下更新时间
        postInfo.setModifyTime(LocalDateTime.now());
        //修改
        baseMapper.updateById(postInfo);
    }
}

现在实现IUserMessageService中的用户留言方法,他将被用户信息方法调用:

List<UserMessage> getUserMessage(Long userId);

Impl:

package com.scnu.community.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.scnu.community.entity.pojo.UserMessage;
import com.scnu.community.mapper.UserMessageMapper;
import com.scnu.community.service.IUserMessageService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.util.List;

/**
 * <p>
 * 用户留言表 服务实现类
 * </p>
 *
 * @author Rosemary
 * @since 2022-09-18
 */
@Service
public class UserMessageServiceImpl extends ServiceImpl<UserMessageMapper, UserMessage> implements IUserMessageService {

    @Resource
    private IUserMessageService iUserMessageService;

    @Transactional(rollbackFor = Exception.class)
    @Override
    public List<UserMessage> getUserMessage(Long userId) {

        QueryWrapper<UserMessage> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("user_id", userId);

        List<UserMessage> userMessages = baseMapper.selectList(queryWrapper);
        return userMessages;
    }

}

IUserInfoService中的用户信息方法:

void register(RegisterVO registerVO);

UserInfoVO login(LoginVO loginVO, String ip);

UserInfoVO getUserInfo(Long userId);

List<UserInfo> searchUsers(String search);

List<PostInfo> searchPosts(String search);

Impl:

package com.scnu.community.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.scnu.community.entity.pojo.PostInfo;
import com.scnu.community.entity.pojo.UserInfo;
import com.scnu.community.entity.pojo.UserMessage;
import com.scnu.community.entity.vo.LoginVO;
import com.scnu.community.entity.vo.RegisterVO;
import com.scnu.community.entity.vo.UserInfoVO;
import com.scnu.community.exception.Assert;
import com.scnu.community.mapper.UserInfoMapper;
import com.scnu.community.result.Res;
import com.scnu.community.result.ResponseEnum;
import com.scnu.community.service.IPostInfoService;
import com.scnu.community.service.IUserInfoService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.scnu.community.service.IUserMessageService;
import com.scnu.community.utils.JwtUtils;
import com.scnu.community.utils.MD5Utils;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.bind.annotation.*;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.time.LocalDateTime;
import java.util.HashMap;
import java.util.List;

/**
 * <p>
 * 用户表 服务实现类
 * </p>
 *
 * @author Rosemary
 * @since 2022-09-18
 */
@Service
public class UserInfoServiceImpl extends ServiceImpl<UserInfoMapper, UserInfo> implements IUserInfoService {

    @Resource
    private IPostInfoService iPostInfoService;

    @Resource
    private IUserMessageService iUserMessageService;

    @Transactional(rollbackFor = Exception.class)
    @Override
    public void register(RegisterVO registerVO) {
        //判断用户是否被注册
        QueryWrapper<UserInfo> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("mobile", registerVO.getMobile());

        Integer count = baseMapper.selectCount(queryWrapper);
        //手机号已被注册,会抛出异常
        Assert.isTrue(count==0, ResponseEnum.MOBILE_EXIST_ERROR);

        //执行到此,说明手机号未注册,则插入数据
        UserInfo userInfo = new UserInfo();
        userInfo.setMobile(registerVO.getMobile());
        userInfo.setEmail(registerVO.getEmail());
        userInfo.setPassword(MD5Utils.encrypt(registerVO.getPassword()));
        userInfo.setCreateTime(LocalDateTime.now());

        baseMapper.insert(userInfo);
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public UserInfoVO login(LoginVO loginVO, String ip) {

        String mobile = loginVO.getMobile();
        String password = loginVO.getPassword();
        System.out.println(mobile);
        //用户是否存在
        QueryWrapper<UserInfo> queryWrapper = new QueryWrapper<>();
        queryWrapper
                .eq("mobile", mobile);

        UserInfo userInfo = baseMapper.selectOne(queryWrapper);
        Assert.notNull(userInfo, ResponseEnum.LOGIN_MOBILE_ERROR);

        //密码是否正确
        Assert.equals(MD5Utils.encrypt(password), userInfo.getPassword(), ResponseEnum.LOGIN_PASSWORD_ERROR);

        //生成token
        String token = JwtUtils.createToken(userInfo.getId(),userInfo.getUsername());

        //组装UserInfoVO
        UserInfoVO userInfoVO = new UserInfoVO();
        userInfoVO.setToken(token);
        userInfoVO.setName(userInfo.getUsername());
        userInfoVO.setAlias(userInfo.getAlias());
        userInfoVO.setAvatar(userInfo.getAvatar());
        userInfoVO.setMobile(mobile);

        //返回,此时token返回给前端
        return userInfoVO;
    }

    /**
     * 用于获取展示个人中心的信息
     * @param userId 用户id
     * @return UserInfoVO
     */
    @Transactional(rollbackFor = Exception.class)
    @Override
    public UserInfoVO getUserInfo(Long userId) {

        //获取用户信息
        UserInfo userInfo = baseMapper.selectById(userId);
        //为空则抛出用户不存在的异常
        Assert.notNull(userInfo, ResponseEnum.LOGIN_MOBILE_ERROR);

        //获取用户帖子列表
        List<PostInfo> userPosts = iPostInfoService.getUserPost(userId);
        UserInfoVO userInfoVO = new UserInfoVO();

        //获取收藏的帖子
        List<PostInfo> collectPosts = iPostInfoService.getCollectPost(userId);

        //获取点赞的帖子
        List<PostInfo> likePosts = iPostInfoService.getLikePost(userId);

        //给我的留言
        List<UserMessage> userMessages = iUserMessageService.getUserMessage(userId);

        //组装userInfoVO
        userInfoVO.setId(userInfo.getId());
        userInfoVO.setAvatar(userInfo.getAvatar());
        userInfoVO.setAlias(userInfo.getAlias());
        userInfoVO.setMobile(userInfo.getMobile());
        userInfoVO.setName(userInfo.getUsername());
        userInfoVO.setBio(userInfo.getBio());
        userInfoVO.setEmail(userInfo.getEmail());
        userInfoVO.setMyPosts(userPosts);
        userInfoVO.setMyCollects(collectPosts);
        userInfoVO.setMyLikes(likePosts);
        userInfoVO.setMessageList(userMessages);

        return userInfoVO;
    }

    /**
     * 搜索返回用户
     * @param search
     * @return
     */
    @Override
    public List<UserInfo> searchUsers(String search) {
        QueryWrapper<UserInfo> queryWrapper = new QueryWrapper<>();
        queryWrapper.like("username", search);
        List<UserInfo> userInfoList = baseMapper.selectList(queryWrapper);
        return userInfoList;
    }

    /**
     * 搜索返回帖子
     * @param search
     * @return
     */
    @Override
    public List<PostInfo> searchPosts(String search) {
        List<PostInfo> postInfoList = iPostInfoService.searchPosts(search);
        return postInfoList;
    }
}

至此已经实现了项目服务层的主体方法,还剩几个小方法没有实现:

  • 广告推送
  • 每日赠言
  • 用户聊天记录
  • 用户关注

IPromotionInfoService广告推送:

List<PromotionInfo> geListPromotions();

Impl:

package com.scnu.community.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.scnu.community.entity.pojo.PromotionInfo;
import com.scnu.community.mapper.PromotionInfoMapper;
import com.scnu.community.service.IPromotionInfoService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.springframework.stereotype.Service;

import java.util.List;

/**
 * <p>
 * 广告推广表 服务实现类
 * </p>
 *
 * @author Rosemary
 * @since 2022-09-18
 */
@Service
public class PromotionInfoServiceImpl extends ServiceImpl<PromotionInfoMapper, PromotionInfo> implements IPromotionInfoService {

    @Override
    public List<PromotionInfo> geListPromotions() {

        //最大的三个id对应的推广
        QueryWrapper<PromotionInfo> queryWrapper = new QueryWrapper<>();
        queryWrapper.orderByDesc("id")
                //表示在后面的sql语句中拼接上该字符串,即只获取三条
                .last("limit 3");
        List<PromotionInfo> promotionInfos = baseMapper.selectList(queryWrapper);
        return promotionInfos;
    }
}

ITipInfoService每日赠言:

TipInfo getContent();

Impl:

package com.scnu.community.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.scnu.community.entity.pojo.TipInfo;
import com.scnu.community.mapper.TipInfoMapper;
import com.scnu.community.service.ITipInfoService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.List;
import java.util.Random;

/**
 * <p>
 * 每日赠言 服务实现类
 * </p>
 *
 * @author Rosemary
 * @since 2022-09-18
 */
@Service
public class TipInfoServiceImpl extends ServiceImpl<TipInfoMapper, TipInfo> implements ITipInfoService {

    @Transactional(rollbackFor = Exception.class)
    @Override
    public TipInfo getContent() {

        //查出所有公告
        QueryWrapper<TipInfo> queryWrapper = new QueryWrapper<>();
        List<TipInfo> tipInfos = baseMapper.selectList(queryWrapper);
        //random随机生成,范围[0,size-1]
        Integer random = (new Random()).nextInt(tipInfos.size());
        //随机返回一条信息
        return tipInfos.get(random);
    }
}

IUserChatService聊天记录:

List<UserChat> getUserChat(Long userId);

void postUserChat(Long userId, Long toId, String content);

Impl:

package com.scnu.community.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.scnu.community.entity.pojo.UserChat;
import com.scnu.community.mapper.UserChatMapper;
import com.scnu.community.service.IUserChatService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.util.List;

/**
 * <p>
 *  服务实现类
 * </p>
 *
 * @author Rosemary
 * @since 2022-09-18
 */
@Service
public class UserChatServiceImpl extends ServiceImpl<UserChatMapper, UserChat> implements IUserChatService {

    private Long chatId = 2L ;

    @Resource
    private IUserChatService iUserChatService;

    @Transactional(rollbackFor = Exception.class)
    @Override
    public List<UserChat> getUserChat(Long userId) {

        QueryWrapper<UserChat> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("to_id", userId.toString());

        List<UserChat> userchat = baseMapper.selectList(queryWrapper);
        return userchat;
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public void postUserChat(Long userId, Long toId, String content){

        UserChat userChat = new UserChat();
        userChat.setSendId(userId.toString());
        userChat.setToId(toId.toString());
        userChat.setContent(content);
        userChat.setId(chatId);
        chatId++;

        baseMapper.insert(userChat);
    }
}

IUserFollowService用户关注:

Boolean doFollow(Long userId, String id);

List<UserInfoVO> getFollowUsers(String userId);

List<UserInfoVO> getFans(String id);

Impl:

package com.scnu.community.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.scnu.community.entity.pojo.UserFollow;
import com.scnu.community.entity.pojo.UserInfo;
import com.scnu.community.entity.vo.UserInfoVO;
import com.scnu.community.mapper.UserFollowMapper;
import com.scnu.community.service.IUserFollowService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.scnu.community.service.IUserInfoService;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.List;

/**
 * <p>
 * 用户关注表 服务实现类
 * </p>
 *
 * @author Rosemary
 * @since 2022-09-18
 */
@Service
public class UserFollowServiceImpl extends ServiceImpl<UserFollowMapper, UserFollow> implements IUserFollowService {

    @Resource
    private IUserInfoService iUserInfoService;

    @Transactional(rollbackFor = Exception.class)
    @Override
    public Boolean doFollow(Long userId, String id) {
        //表的关联
        QueryWrapper<UserFollow> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("follower_id", userId.toString())
                .eq("parent_id", id);

        UserFollow uf = baseMapper.selectOne(queryWrapper);
        //说明没关注过,此时进行关注的操作
        if (uf==null){
            UserFollow userFollow =new UserFollow();
            userFollow.setFollowerId(userId.toString());
            userFollow.setParentId(id);
            baseMapper.insert(userFollow);
            return true;
        }

        //执行到这说明已经关注了,故删除关注记录
        baseMapper.delete(queryWrapper);
        return false;
    }

    /**
     * 获取userId的关注的人
     * @param userId
     * @return
     */
    @Transactional(rollbackFor = Exception.class)
    @Override
    public List<UserInfoVO> getFollowUsers(String userId) {

        QueryWrapper<UserFollow> queryWrapper = new QueryWrapper<>();
        //只查出parent_id这个字段
        // queryWrapper.select("parent_id").eq("user_id", userId);
        queryWrapper.eq("follower_id", userId).select("distinct parent_id");
        List<UserFollow> userFollows =baseMapper.selectList(queryWrapper);
        List<UserInfoVO> userInfoVOList = new ArrayList<>();
        for (UserFollow uf:userFollows
        ) {
            //组装UserInfoVO
            UserInfo userInfo = iUserInfoService.getById(uf.getParentId());
            UserInfoVO userInfoVO = new UserInfoVO();

            userInfoVO.setId(userInfo.getId());
            userInfoVO.setAvatar(userInfo.getAvatar());
            userInfoVO.setAlias(userInfo.getAlias());
            userInfoVO.setMobile(userInfo.getMobile());
            userInfoVO.setName(userInfo.getUsername());
            userInfoVO.setBio(userInfo.getBio());
            userInfoVO.setEmail(userInfo.getEmail());

            userInfoVOList.add(userInfoVO);
        }
        return userInfoVOList;
    }

    /**
     * 获取关注userId的人
     * @param userId
     * @return
     */
    @Transactional(rollbackFor = Exception.class)
    @Override
    public List<UserInfoVO> getFans(String userId) {

        QueryWrapper<UserFollow> queryWrapper = new QueryWrapper<>();
        //只查出parent_id这个字段
        // queryWrapper.select("parent_id").eq("user_id", userId);
        queryWrapper.eq("parent_id", userId).select("distinct follower_id");
        List<UserFollow> userFollows =baseMapper.selectList(queryWrapper);
        List<UserInfoVO> userInfoVOList = new ArrayList<>();
        for (UserFollow uf:userFollows
        ) {
            //组装UserInfoVO
            UserInfo userInfo = iUserInfoService.getById(uf.getFollowerId());
            UserInfoVO userInfoVO = new UserInfoVO();

            userInfoVO.setId(userInfo.getId());
            userInfoVO.setAvatar(userInfo.getAvatar());
            userInfoVO.setAlias(userInfo.getAlias());
            userInfoVO.setMobile(userInfo.getMobile());
            userInfoVO.setName(userInfo.getUsername());
            userInfoVO.setBio(userInfo.getBio());
            userInfoVO.setEmail(userInfo.getEmail());

            userInfoVOList.add(userInfoVO);
        }
        return userInfoVOList;
    }

}

至此,服务层接口与方法大体实现。

控制器实现与Api文档测试

前面已经实现了公告板控制器,本节要实现以下控制器:

  • 帖子信息
  • 用户信息
  • 广告推送
  • 每日赠言
  • 用户聊天记录
  • 用户关注

PostInfoController:

package com.scnu.community.controller;


import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import com.scnu.community.entity.pojo.PostInfo;
import com.scnu.community.entity.vo.PostVO;
import com.scnu.community.result.Res;
import com.scnu.community.service.IPostInfoService;
import com.scnu.community.utils.JwtUtils;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.springframework.web.bind.annotation.*;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.util.List;

/**
 * <p>
 * 话题表 前端控制器
 * </p>
 *
 * @author Rosemary
 * @since 2022-09-18
 */
@Api(tags = "帖子接口")
@RestController
@RequestMapping("/community/scnu")
public class PostInfoController {

    @Resource
    private IPostInfoService iPostInfoService;

    // @Resource
    // private IPostTagService iPostTagService;

    @ApiOperation("发布帖子")
    @PostMapping("/postinfo/post")
    public Res doPost(HttpServletRequest request, @RequestBody PostVO postVO){

        //获取用户id
        String token = request.getHeader("token");
        Long userId = JwtUtils.getUserId(token);

        //增加帖子
        iPostInfoService.doPost(userId, postVO);

        //传入所有blog,得到分页结果对象
        return Res.ok().message("发布成功");
    }

    /**
     * 编辑post
     * @param request
     * @param postInfo
     * @return
     */
    @ApiOperation("编辑修改帖子")
    @PostMapping("/postinfo/edit")
    public Res editPost(HttpServletRequest request,@RequestBody PostInfo postInfo){

        String token = request.getHeader("token");
        Long userId = JwtUtils.getUserId(token);

        iPostInfoService.editPost(postInfo,userId);
        return Res.ok().message("修改成功");
    }


    @ApiOperation("获取高数课主贴")
    @GetMapping("/postinfo/math")
    public Res getMathPost(@RequestParam(required = false,defaultValue = "1",value = "pageNum")
                                   int pageNum){

        PageHelper.startPage(pageNum,5);
        List<PostInfo> postInfoLists = iPostInfoService.getPostByTag(111L);

        PageInfo pageInfo = new PageInfo(postInfoLists);
        return Res.ok().data("pageInfo",pageInfo);
    }

    @ApiOperation("获取专业课主贴")
    @GetMapping("/postinfo/major")
    public Res getMajorPost(@RequestParam(required = false,defaultValue = "1",value = "pageNum")
                                    int pageNum){

        PageHelper.startPage(pageNum,5);
        List<PostInfo> postInfoLists = iPostInfoService.getPostByTag(112L);

        PageInfo pageInfo = new PageInfo(postInfoLists);
        return Res.ok().data("pageInfo",pageInfo);
    }

    @ApiOperation("获取公共课主贴")
    @GetMapping("/postinfo/public")
    public Res getPublicPost(@RequestParam(required = false,defaultValue = "1",value = "pageNum")
                                     int pageNum){

        PageHelper.startPage(pageNum,5);
        List<PostInfo> postInfoLists = iPostInfoService.getPostByTag(113L);

        PageInfo pageInfo = new PageInfo(postInfoLists);
        return Res.ok().data("pageInfo",pageInfo);
    }

    @ApiOperation("获取女生专区主贴")
    @GetMapping("/postinfo/girls")
    public Res getGirlsPost(@RequestParam(required = false,defaultValue = "1",value = "pageNum")
                                    int pageNum){

        PageHelper.startPage(pageNum,5);
        List<PostInfo> postInfoLists = iPostInfoService.getPostByTag(211L);

        PageInfo pageInfo = new PageInfo(postInfoLists);
        return Res.ok().data("pageInfo",pageInfo);
    }

    @ApiOperation("获取男生专区主贴")
    @GetMapping("/postinfo/boys")
    public Res getBoysPost(@RequestParam(required = false,defaultValue = "1",value = "pageNum")
                                   int pageNum){

        PageHelper.startPage(pageNum,5);
        List<PostInfo> postInfoLists = iPostInfoService.getPostByTag(212L);

        PageInfo pageInfo = new PageInfo(postInfoLists);
        return Res.ok().data("pageInfo",pageInfo);
    }
    @ApiOperation("获取社团专区主贴")
    @GetMapping("/postinfo/party")
    public Res getPartyPost(@RequestParam(required = false,defaultValue = "1",value = "pageNum")
                                    int pageNum){

        PageHelper.startPage(pageNum,5);
        List<PostInfo> postInfoLists = iPostInfoService.getPostByTag(213L);

        PageInfo pageInfo = new PageInfo(postInfoLists);
        return Res.ok().data("pageInfo",pageInfo);
    }
    @ApiOperation("获取生活论坛主贴")
    @GetMapping("/postinfo/daily")
    public Res getDailyPost(@RequestParam(required = false,defaultValue = "1",value = "pageNum")
                                    int pageNum){

        PageHelper.startPage(pageNum,5);
        List<PostInfo> postInfoLists = iPostInfoService.getPostByTag(311L);

        PageInfo pageInfo = new PageInfo(postInfoLists);
        return Res.ok().data("pageInfo",pageInfo);
    }

    @ApiOperation("获取闲置出售主贴")
    @GetMapping("/postinfo/unused")
    public Res getUnusedPost(@RequestParam(required = false,defaultValue = "1",value = "pageNum")
                                     int pageNum){

        PageHelper.startPage(pageNum,5);
        List<PostInfo> postInfoLists = iPostInfoService.getPostByTag(312L);

        PageInfo pageInfo = new PageInfo(postInfoLists);
        return Res.ok().data("pageInfo",pageInfo);
    }

    @ApiOperation("获取热门讨论贴")
    @GetMapping("/postinfo/hot")
    public Res getHotPost(@RequestParam(required = false,defaultValue = "1",value = "pageNum")
                                  int pageNum){
        //开启分页,pageNum相当于页数,pageSize为每页展现的帖子数
        PageHelper.startPage(pageNum,5);
        List<PostInfo> postInfoLists = iPostInfoService.getHotPost();

        //传入所有blog,得到分页结果对象
        PageInfo pageInfo = new PageInfo(postInfoLists);
        return Res.ok().data("pageInfo",pageInfo);
    }

    @ApiOperation("获取最新讨论贴")
    @GetMapping("/postinfo/last")
    public Res getLastPost(@RequestParam(required = false,defaultValue = "1",value = "pageNum")
                                   int pageNum){

        PageHelper.startPage(pageNum,5);
        List<PostInfo> postInfoLists = iPostInfoService.getLastPost();

        PageInfo pageInfo = new PageInfo(postInfoLists);
        return Res.ok().data("pageInfo",pageInfo);
    }

    @ApiOperation("删除帖子")
    @PostMapping("/postinfo/delete")
    public Res getLastPost(HttpServletRequest request, @RequestParam(required = true)Long id){

        String token = request.getHeader("token");

        //先获取用户id
        Long userId = JwtUtils.getUserId(token);

        //删除post
        Boolean flag = iPostInfoService.deletePost(id,userId);

        if(flag){
            return Res.ok().message("删除成功");
        }
        return Res.error().message("删除失败,请刷新重试");
    }
}

UserInfoController:

package com.scnu.community.controller;


import com.fasterxml.jackson.annotation.JsonTypeInfo.As;
import com.scnu.community.entity.pojo.PostInfo;
import com.scnu.community.entity.pojo.UserInfo;
import com.scnu.community.entity.vo.LoginVO;
import com.scnu.community.entity.vo.RegisterVO;
import com.scnu.community.entity.vo.UserInfoVO;
import com.scnu.community.exception.Assert;
import com.scnu.community.result.Res;
import com.scnu.community.result.ResponseEnum;
import com.scnu.community.service.IUserFollowService;
import com.scnu.community.service.IUserInfoService;
import com.scnu.community.utils.JwtUtils;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import io.swagger.annotations.ApiParam;
import org.springframework.web.bind.annotation.*;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.util.HashMap;
import java.util.List;

/**
 * <p>
 * 用户表 前端控制器
 * </p>
 *
 * @author Rosemary
 * @since 2022-09-18
 */
@Api(tags = "用户接口")
@RestController
@RequestMapping("/community/scnu/user")
public class UserInfoController {

    @Resource
    private IUserInfoService userInfoService;

    @ApiOperation("用户注册")
    @PostMapping("/register")
    public Res register(@RequestBody RegisterVO registerVO){

        //不能为空
        Assert.notEmpty(registerVO.getMobile(), ResponseEnum.MOBILE_NULL_ERROR);
        Assert.notEmpty(registerVO.getEmail(), ResponseEnum.EMAIL_NULL_ERROR);
        Assert.notEmpty(registerVO.getPassword(), ResponseEnum.PASSWORD_NULL_ERROR);
        Assert.notEmpty(registerVO.getRePassword(), ResponseEnum.PASSWORD_NULL_ERROR);

        //密码与确认密码要相同,不相同则抛出异常
        Assert.equals(registerVO.getPassword(), registerVO.getRePassword(),
                ResponseEnum.PASSWORD_NOT_EQUALS);

        //注册
        userInfoService.register(registerVO);

        return Res.ok().message("注册成功");
    }

    @ApiOperation("用户登录")
    @PostMapping("/login")
    public Res login(@RequestBody LoginVO loginVO, HttpServletRequest request){

        //账号密码不能为空
        Assert.notEmpty(loginVO.getMobile(), ResponseEnum.MOBILE_NULL_ERROR);
        Assert.notEmpty(loginVO.getPassword(), ResponseEnum.PASSWORD_NULL_ERROR);

        //获取客户端请求ip
        String ip = request.getRemoteAddr();

        //登录
        UserInfoVO userInfoVo = userInfoService.login(loginVO,ip);
        System.out.println("执行不到这说明有误");
        return Res.ok().data("userInfo",userInfoVo);
    }

    @ApiOperation("个人中心")
    @GetMapping("/")
    public Res getUserInfo(HttpServletRequest request){

        //从token中获取UserId
        String token = request.getHeader("token");
        Long userId = JwtUtils.getUserId(token);

        //获取个人中心相应的信息,这些信息要用于展示页面
        UserInfoVO userInfoVo = userInfoService.getUserInfo(userId);

        return Res.ok().data("userInfo",userInfoVo);
    }

    /**
     * @param search 要搜索的内容或者用户
     * @return
     */
    @ApiOperation("搜索内容或用户")
    @PostMapping("/search")
    public Res searchUserOrMessage(@RequestBody String search){

        //先展示用户,再展示帖子标题含有search字符串的帖子
        List<UserInfo> userInfoList = userInfoService.searchUsers(search);
        List<PostInfo> postInfoList = userInfoService.searchPosts(search);
        HashMap<String,List> map = new HashMap<>();

        if (userInfoList.size()==0 && postInfoList.size()==0){
            //前端展示时,先判断map是否为空,如果是则说明搜索不到
            return Res.ok().data("map","无此用户或帖子");
        }
        map.put("users", userInfoList);
        map.put("posts", postInfoList);
        //返回map,k1对应users,k2对应posts
        return Res.ok().data("map",map);
    }

}

打开Api文档,测试注册:

{
	"email": "920688354@qq.com",
	"mobile": "13662464656",
	"password": "123456",
	"rePassword": "123456"
}

结果:

{
  "code": 200,
  "message": "注册成功",
  "data": {}
}

测试登陆:

{
	"mobile": "13662464656",
	"password": "123456"
}

结果:

{
  "code": 200,
  "message": "成功",
  "data": {
    "userInfo": {
      "id": null,
      "name": "",
      "alias": null,
      "avatar": null,
      "mobile": "13662464656",
      "email": null,
      "bio": null,
      "roleId": null,
      "myPosts": null,
      "myLikes": null,
      "myCollects": null,
      "messageList": null,
      "token": "eyJhbGciOiJIUzUxMiIsInppcCI6IkdaSVAifQ.H4sIAAAAAAAAAKtWKi5NUrJSCg5y0g0Ndg1S0lFKrShQsjI0MzM2MzM0NTPSUSotTi3yTAGKmZobmpsbWZqZmZpYGFmYm1gaQCT9EnNTgWYo1QIAR0LDQ04AAAA.YZUofoFIuDdwQCxCgN6sZgma1je1BUP0GHWYe-_rBbRY33aYuBG-KEk7DjiESV_AIm9S2S_KgL_C6km_e1wr_w"
    }
  }
}

对于需要登陆后操作的功能,需要先在全局参数中设置一个参数,

参数名称为token,参数值为登陆获得的token,参数类型为header。

添加全局参数后测试个人中心结果:

{
  "code": 200,
  "message": "成功",
  "data": {
    "userInfo": {
      "id": 1571772966548287500,
      "name": "",
      "alias": null,
      "avatar": null,
      "mobile": "13662464656",
      "email": "920688354@qq.com",
      "bio": null,
      "roleId": null,
      "myPosts": [],
      "myLikes": [],
      "myCollects": [],
      "messageList": [],
      "token": null
    }
  }
}

发帖测试:

{
	"content": "测试",
	"tags": [],
	"title": "111111"
}

结果:

{
  "code": 200,
  "message": "发布成功",
  "data": {}
}

继续完成剩余四个控制器。

PromotionInfoController:

package com.scnu.community.controller;


import com.scnu.community.entity.pojo.PromotionInfo;
import com.scnu.community.result.Res;
import com.scnu.community.service.IPromotionInfoService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;

import org.springframework.web.bind.annotation.RestController;

import javax.annotation.Resource;
import java.util.List;

/**
 * <p>
 * 广告推广表 前端控制器
 * </p>
 *
 * @author Rosemary
 * @since 2022-09-18
 */
@Api(tags = "推广链接")
@RestController
@RequestMapping("/community/scnu")
public class PromotionInfoController {

    @Resource
    private IPromotionInfoService iPromotionInfoService;

    @ApiOperation("获取三条推广信息")
    @GetMapping("/promotioninfo")
    public Res getPromotionInfo(){

        //随机获取公告板信息
        List<PromotionInfo> promotionInfoList = iPromotionInfoService.geListPromotions();
        return Res.ok().data("promotionInfo",promotionInfoList);
    }
}

TipInfoController:

package com.scnu.community.controller;


import com.scnu.community.entity.pojo.TipInfo;
import com.scnu.community.result.Res;
import com.scnu.community.service.ITipInfoService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import javax.annotation.Resource;

/**
 * <p>
 * 每日赠言 前端控制器
 * </p>
 *
 * @author Rosemary
 * @since 2022-09-18
 */
@Api(tags = "每日赠言")
@RestController
@RequestMapping("/community/scnu")
public class TipInfoController {

    @Resource
    private ITipInfoService tipInfoService;

    @ApiOperation("随机获取每日赠言记录")
    @GetMapping("/tipinfo")
    public Res getTipInfo(){

        TipInfo tipInfo = tipInfoService.getContent();
        //返回结果
        return Res.ok().data("tipInfo",tipInfo);
    }
}

UserChatController:

package com.scnu.community.controller;


import com.scnu.community.entity.pojo.PostInfo;
import com.scnu.community.entity.pojo.UserChat;
import com.scnu.community.entity.vo.PostVO;
import com.scnu.community.result.Res;
import com.scnu.community.service.IPostInfoService;
import com.scnu.community.service.IUserChatService;
import com.scnu.community.utils.JwtUtils;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.springframework.web.bind.annotation.*;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.util.List;

/**
 * <p>
 * 用户私聊 前端控制器
 * </p>
 *
 * @author Rosemary
 * @since 2022-09-18
 */

@Api(tags = "私聊接口")
@RestController
@RequestMapping("/community/chat")
public class UserChatController {

    @Resource
    private IUserChatService iUserChatService;

    @ApiOperation("接收消息")
    @GetMapping("/get")
    public Res getUserChat(Long sendId){

        List<UserChat> userChatList = iUserChatService.getUserChat(sendId);

        return Res.ok().data("chatContent",userChatList);
    }

    @ApiOperation("发送消息")
    @PostMapping("/post")
    public Res postUserChat(Long sendId, Long toId, String content){

        iUserChatService.postUserChat(sendId, toId, content);

        return Res.ok().message("发送成功");
    }
}


UserFollowController:

package com.scnu.community.controller;

import com.scnu.community.entity.pojo.UserInfo;
import com.scnu.community.entity.vo.UserInfoVO;
import com.scnu.community.exception.Assert;
import com.scnu.community.result.Res;
import com.scnu.community.result.ResponseEnum;
import com.scnu.community.service.IUserFollowService;
import com.scnu.community.utils.JwtUtils;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.springframework.web.bind.annotation.*;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.util.List;

/**
 * <p>
 * 用户关注表 前端控制器
 * </p>
 *
 * @author Rosemary
 * @since 2022-09-18
 */
@Api(tags = "用户关注表接口")
@RestController
@RequestMapping("/community/scnu/userfollow")
public class UserFollowController {

    @Resource
    private IUserFollowService userFollowService;

    /**
     * 这里设置为点击一下关注,再点击一下就取消关注
     * @param id 需要关注的用户
     * @return
     */
    @ApiOperation("关注用户")
    @PostMapping("/follow/{id}")
    public Res followUser(HttpServletRequest request, @PathVariable @RequestBody String id){

        //从请求中获取userId
        String token = request.getHeader("token");
        Long userId = JwtUtils.getUserId(token);
        //关注者和被关注者不能相同
        if (id.equals(userId)){
            Res.ok().data("data","不能关注自己");
        }
        Assert.notNull(userId, ResponseEnum.ERROR);
        //flag为true表示关注,false表示无关注
        Boolean flag = userFollowService.doFollow(userId,id);
        return Res.ok().data("flag",flag);
    }

    /**
     *
     * @param id 用户的id
     * @return 用户id关注的用户列表
     */
    @ApiOperation("获取关注用户")
    @PostMapping("/getfollow/{id}")
    public Res getfollowUsers(@PathVariable @RequestBody String id){

        //用户id不存在
        Assert.notNull(id, ResponseEnum.LOGIN_MOBILE_ERROR);
        //flag为true表示关注,false表示无关注
        List<UserInfoVO> userInfoVOList = userFollowService.getFollowUsers(id);
        return Res.ok().data("userInfoVOList",userInfoVOList);
    }

    /**
     *
     * @param id 用户的id
     * @return 关注用户id的用户列表
     */
    @ApiOperation("获取粉丝")
    @GetMapping("/getfan/{id}")
    public Res getFans(@PathVariable @RequestBody String id){

        //用户id不存在
        Assert.notNull(id, ResponseEnum.LOGIN_MOBILE_ERROR);
        //flag为true表示关注,false表示无关注
        List<UserInfoVO> userInfoVOList = userFollowService.getFans(id);
        return Res.ok().data("userInfoVOList",userInfoVOList);
    }
}

可以调节数据库中的内容进行测试,不再赘述。

整合websocket

config:

package com.scnu.community.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.socket.server.standard.ServerEndpointExporter;


@Configuration
public class WebSocketStompConfig {
    //这个bean的注册,用于扫描带有@ServerEndpoint的注解成为websocket
    @Bean
    public ServerEndpointExporter serverEndpointExporter()
    {
        return new ServerEndpointExporter();
    }
}

前面已经引入了websocketUtils。

controller:

package com.scnu.community.controller;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;

@Controller
public class WebSocketController {

    private Logger logger = LoggerFactory.getLogger(this.getClass());

    @RequestMapping("/websocket/{name}")
    public String webSocket(@PathVariable String name, Model model) {
        try {
            logger.info("跳转到websocket的页面上");
            System.out.println(name);
            model.addAttribute("username", name);
            return "websocket";
        } catch (Exception e) {
            logger.info("跳转到websocket的页面上发生异常,异常信息是:" + e.getMessage());
            return "error";
        }
    }
}

测试用的前端页面(templates/websocket.html):

<!DOCTYPE html>
<html xmlns:th="http://www.w3.org/1999/xhtml">
<head>
    <title>websocket</title>
</head>

<body>
<div style="margin: auto;text-align:left">
    <h1>Websocket测试</h1>
</div>
<br/>
<div style="margin: auto;text-align:left">
    <select id="onLineUser">
        <option>在线好友列表</option>
    </select>
    <input id="text" type="text" />
    <input type="button" value="发送" onclick="send()" />
</div>
<br>
<div style="margin-right: 10px;text-align: left">

    <input type="button" value="关闭连接" onclick="closeWebSocket()" />
</div>
<hr/>
<div id="message" style="text-align: left;"></div>
<input  type="text" th:value="${username}" id="username" style="display: none" />


<script type="text/javascript">
    var webSocket;
    var commWebSocket;
    //判断当前浏览器是否支持WebSocket
    if ("WebSocket" in window)
    {
        websocket = new WebSocket("ws://localhost:9030/websocket/"+document.getElementById('username').value);

        //连通之后的回调事件    连接成功
        websocket.onopen = function()
        {
          //  websocket.send( document.getElementById('username').value+"已经上线了");
            console.log(document.getElementById('username').value+"已经连通了websocket");
            setMessageInnerHTML(document.getElementById('username').value+"已经连通了websocket");
        };

        //接收后台服务端的消息    接收到消息
        websocket.onmessage = function (evt)
        {
            var received_msg = evt.data;
            console.log("数据已接收:" +received_msg);
            var obj = JSON.parse(received_msg);
            console.log("可以解析成json,消息类型(1代表上线 2代表下线 3代表在线名单 4代表普通消息)是:"+obj.messageType);
            //1代表上线 2代表下线 3代表在线名单 4代表普通消息
            if(obj.messageType==1){
                //把名称放入到selection当中供选择
                var onlineName = obj.username;
                var option = "<option>"+onlineName+"</option>";
                $("#onLineUser").append(option);
                setMessageInnerHTML(onlineName+"上线了");
            }
            else if(obj.messageType==2){
                $("#onLineUser").empty();
                var onlineName = obj.onlineUsers;
                var offlineName = obj.username;
                var option = "<option>"+"--所有--"+"</option>";
                for(var i=0;i<onlineName.length;i++){
                    if(!(onlineName[i]==document.getElementById('username').value)){
                        option+="<option>"+onlineName[i]+"</option>"
                    }
                }
                $("#onLineUser").append(option);

                setMessageInnerHTML(offlineName+"下线了");
            }
            else if(obj.messageType==3){
                var onlineName = obj.onlineUsers;
                var option = null;
                for(var i=0;i<onlineName.length;i++){
                    if(!(onlineName[i]==document.getElementById('username').value)){
                        option+="<option>"+onlineName[i]+"</option>"
                    }
                }
                $("#onLineUser").append(option);
                console.log("获取了在线的名单"+onlineName.toString());
            }
            else{

                setMessageInnerHTML(obj.fromusername+":"+obj.textMessage);
            }
        };

        //连接关闭的回调事件     连接关闭
        websocket.onclose = function()
        {
            console.log("连接已关闭");
            setMessageInnerHTML("连接已经关闭");
        };
    }
    else{
        // 浏览器不支持 WebSocket
        alert("您的浏览器不支持 WebSocket!");
    }
    //将消息显示在网页上
    function setMessageInnerHTML(innerHTML) {
        document.getElementById('message').innerHTML += innerHTML + '<br/>';
    }

    function closeWebSocket() {
        //直接关闭websocket的连接
        websocket.close();
    }

    function send() {
        var selectText = $("#onLineUser").find("option:selected").text();
        if(selectText=="在线好友列表"){
            selectText = "All";
        }
        else{
            setMessageInnerHTML(document.getElementById('username').value+":"+ $("#text").val());
        }

        var message = {
            "message":document.getElementById('text').value,
            "username":document.getElementById('username').value,
            "to":selectText
        };
        websocket.send(JSON.stringify(message));
        $("#text").val("");

    }
</script>
</body>
</html>

http://localhost:9030/websocket/{username},用两个不同的username登陆测试,互发消息即可。

补充

跨域问题

什么是跨域?

跨域:指的是浏览器不能执行其他网站的脚本。它是由浏览器的同源策略造成的,是浏览器对javascript施加的安全限制。

例如:a页面想获取b页面资源,如果a、b页面的协议、域名、端口、子域名不同,所进行的访问行动都是跨域的,而浏览器为了安全问题一般都限制了跨域访问,也就是不允许跨域请求资源。注意:跨域限制访问,其实是浏览器的限制。理解这一点很重要!!!

同源策略:是指协议,域名,端口都要相同,其中有一个不同都会产生跨域:

img

解决跨域问题的几种方法:

  1. 返回新的CorsFilter
  2. 重写 WebMvcConfigurer
  3. 使用注解 @CrossOrigin
  4. 手动设置响应头 (HttpServletResponse)
  5. 自定web filter 实现跨域

这里使用重写WebMvcConfigurer的方法实现:

@Configuration
public class GlobalWebMvcConfigurer implements WebMvcConfigurer {

    @Override
    public void addCorsMappings(CorsRegistry registry) {
        registry.addMapping("/**")
                .allowedOrigins("*")
                .allowedMethods("GET", "HEAD", "POST", "PUT", "DELETE", "OPTIONS")
                .allowCredentials(true)
                .maxAge(3600)
                .allowedHeaders("*");
    }
}

即重写WebMvcConfigurer中的addCorsMappings方法,allowCredentials为true表示允许发送cookie。

并声明要放行的原始域。

MD5加密

MD5加密的特点:

1.长度固定

不管多长的字符串,加密后长度都是一样长
作用:方便平时信息的统计和管理

2.易计算

字符串和文件加密的过程是容易的.
作用: 开发者很容易理解和做出加密工具

3.细微性

一个文件,不管多大,小到几k,大到几G,你只要改变里面某个字符,那么都会导致MD5值改变.
作用:很多软件和应用在网站提供下载资源,其中包含了对文件的MD5码,用户下载后只需要用工具测一下下载好的文件,通过对比就知道该文件是否有过更改变动.

4.不可逆性

你明明知道密文和加密方式,你却无法反向计算出原密码.
作用:基于这个特点,很多安全的加密方式都是用到.大大提高了数据的安全性

为防止撞库,还可以在传入的字符串后面加一段固定字符串,即“加盐”。

项目流程梳理

  1. 确定需求。
  2. 设计生成数据库表。
  3. 使用MyBatisPlus代码生成器,生成entity、mapper、服务层和控制器的基本框架。
  4. application.yml的配置,包括连接数据库。
  5. 引入工具类,包括jwtutil、MD5加密、websocket私聊实现。
  6. 设计并实现服务层的接口。
  7. 引入Swagger生成Api文档,并实现控制器。
  8. 测试。
  • 0
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值