赞
踩
这是一个校园社区的后端实现。主要功能如下:
主要技术栈:
SpringBoot
MyBatisPlus
MySQL
建立数据库,数据库名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
先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); } }
使用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查看文档了。
此时还没写控制器,所以文档里什么都没有。
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:
exception:
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包,准备几个工具,包括:
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:
登陆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; } }
至此,服务层接口与方法大体实现。
前面已经实现了公告板控制器,本节要实现以下控制器:
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); } }
可以调节数据库中的内容进行测试,不再赘述。
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页面的协议、域名、端口、子域名不同,所进行的访问行动都是跨域的,而浏览器为了安全问题一般都限制了跨域访问,也就是不允许跨域请求资源。注意:跨域限制访问,其实是浏览器的限制。理解这一点很重要!!!
同源策略:是指协议,域名,端口都要相同,其中有一个不同都会产生跨域:
解决跨域问题的几种方法:
这里使用重写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加密的特点:
1.长度固定
不管多长的字符串,加密后长度都是一样长
作用:方便平时信息的统计和管理
2.易计算
字符串和文件加密的过程是容易的.
作用: 开发者很容易理解和做出加密工具
3.细微性
一个文件,不管多大,小到几k,大到几G,你只要改变里面某个字符,那么都会导致MD5值改变.
作用:很多软件和应用在网站提供下载资源,其中包含了对文件的MD5码,用户下载后只需要用工具测一下下载好的文件,通过对比就知道该文件是否有过更改变动.
4.不可逆性
你明明知道密文和加密方式,你却无法反向计算出原密码.
作用:基于这个特点,很多安全的加密方式都是用到.大大提高了数据的安全性
为防止撞库,还可以在传入的字符串后面加一段固定字符串,即“加盐”。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。