背景 今天准备写一个ssm整合的crud简单项目,然后顺便应付一下学校课程,然后被spring的注释气到了(注释代码什么都写得一模一样,就是idea报找不到bean,结果我重写了几遍之后莫名的好了,离谱!),被jsp气到了(这个错误之前遇到好几次了,也是代码一模一样,就是报404,离谱!),实在是不想在这些上面折腾了,今天在实验室有一半的时间都在叹气,于是果断放弃了它,投入了springboot+vue的怀抱。
项目简介 项目商业模式 B2C(当前项目)
Business To Customers
两个角色: 管理员:添加、修改、删除 普通用户:查询
B2B2C
Business To Business To Customers
例子:淘宝
项目实现功能模块 后台(管理员)
讲师管理
课程分类管理
课程管理
统计分析
订单管理
banner(轮播图)管理
权限管理
前台(普通用户)
首页数据显示
讲师列表和详情
课程列表和详情
登录和注册
微信扫码登录
微信支付
项目使用的技术点 后端技术
SpringBoot
SpringCloud
MyBatisPlus
SpringSecurity
redis,maven,easyExcel,jwt,OAuth2
……
前端技术
vue
element-ui
axios
node.js
……
其他技术
阿里云oss
阿里云视频点播服务
阿里云短信服务
微信支付和登录
docker,git,Jenkins
……
MyBatisPlus介绍
详见单独的文档说明
搭建项目环境 数据库
详见资料
项目结构
父工程:pom类型,用于管理以来版本和放公共依赖
子模块n:实现各个模块
创建springboot父项目 配置父项目的pom.xml
配置打包方式、删除dependencies、添加properties和dependencyManagement、删除src文件夹等
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 <?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.5.4</version > <relativePath /> </parent > <groupId > com.atguigu</groupId > <artifactId > guli_parent</artifactId > <packaging > pom</packaging > <version > 0.0.1-SNAPSHOT</version > <name > guli_parent</name > <description > guli_parent</description > <properties > <java.version > 1.8</java.version > <guli.version > 0.0.1-SNAPSHOT</guli.version > <mybatis-plus.version > 3.0.5</mybatis-plus.version > <velocity.version > 2.0</velocity.version > <swagger.version > 2.7.0</swagger.version > <aliyun.oss.version > 2.8.3</aliyun.oss.version > <jodatime.version > 2.10.1</jodatime.version > <poi.version > 3.17</poi.version > <commons-fileupload.version > 1.3.1</commons-fileupload.version > <commons-io.version > 2.6</commons-io.version > <httpclient.version > 4.5.1</httpclient.version > <jwt.version > 0.7.0</jwt.version > <aliyun-java-sdk-core.version > 4.3.3</aliyun-java-sdk-core.version > <aliyun-sdk-oss.version > 3.1.0</aliyun-sdk-oss.version > <aliyun-java-sdk-vod.version > 2.15.2</aliyun-java-sdk-vod.version > <aliyun-java-vod-upload.version > 1.4.11</aliyun-java-vod-upload.version > <aliyun-sdk-vod-upload.version > 1.4.11</aliyun-sdk-vod-upload.version > <fastjson.version > 1.2.28</fastjson.version > <gson.version > 2.8.2</gson.version > <json.version > 20170516</json.version > <commons-dbutils.version > 1.7</commons-dbutils.version > <canal.client.version > 1.1.0</canal.client.version > <docker.image.prefix > zx</docker.image.prefix > <cloud-alibaba.version > 0.2.2.RELEASE</cloud-alibaba.version > </properties > <dependencyManagement > <dependencies > <dependency > <groupId > org.springframework.cloud</groupId > <artifactId > spring-cloud-dependencies</artifactId > <version > Hoxton.RELEASE</version > <type > pom</type > <scope > import</scope > </dependency > <dependency > <groupId > org.springframework.cloud</groupId > <artifactId > spring-cloud-alibaba-dependencies</artifactId > <version > ${cloud-alibaba.version}</version > <type > pom</type > <scope > import</scope > </dependency > <dependency > <groupId > com.baomidou</groupId > <artifactId > mybatis-plus-boot-starter</artifactId > <version > ${mybatis-plus.version}</version > </dependency > <dependency > <groupId > org.apache.velocity</groupId > <artifactId > velocity-engine-core</artifactId > <version > ${velocity.version}</version > </dependency > <dependency > <groupId > io.springfox</groupId > <artifactId > springfox-swagger2</artifactId > <version > ${swagger.version}</version > </dependency > <dependency > <groupId > io.springfox</groupId > <artifactId > springfox-swagger-ui</artifactId > <version > ${swagger.version}</version > </dependency > <dependency > <groupId > com.aliyun.oss</groupId > <artifactId > aliyun-sdk-oss</artifactId > <version > ${aliyun.oss.version}</version > </dependency > <dependency > <groupId > joda-time</groupId > <artifactId > joda-time</artifactId > <version > ${jodatime.version}</version > </dependency > <dependency > <groupId > org.apache.poi</groupId > <artifactId > poi</artifactId > <version > ${poi.version}</version > </dependency > <dependency > <groupId > org.apache.poi</groupId > <artifactId > poi-ooxml</artifactId > <version > ${poi.version}</version > </dependency > <dependency > <groupId > commons-fileupload</groupId > <artifactId > commons-fileupload</artifactId > <version > ${commons-fileupload.version}</version > </dependency > <dependency > <groupId > commons-io</groupId > <artifactId > commons-io</artifactId > <version > ${commons-io.version}</version > </dependency > <dependency > <groupId > org.apache.httpcomponents</groupId > <artifactId > httpclient</artifactId > <version > ${httpclient.version}</version > </dependency > <dependency > <groupId > com.google.code.gson</groupId > <artifactId > gson</artifactId > <version > ${gson.version}</version > </dependency > <dependency > <groupId > io.jsonwebtoken</groupId > <artifactId > jjwt</artifactId > <version > ${jwt.version}</version > </dependency > <dependency > <groupId > com.aliyun</groupId > <artifactId > aliyun-java-sdk-core</artifactId > <version > ${aliyun-java-sdk-core.version}</version > </dependency > <dependency > <groupId > com.aliyun.oss</groupId > <artifactId > aliyun-sdk-oss</artifactId > <version > ${aliyun-sdk-oss.version}</version > </dependency > <dependency > <groupId > com.aliyun</groupId > <artifactId > aliyun-java-sdk-vod</artifactId > <version > ${aliyun-java-sdk-vod.version}</version > </dependency > <dependency > <groupId > com.aliyun</groupId > <artifactId > aliyun-java-vod-upload</artifactId > <version > ${aliyun-java-vod-upload.version}</version > </dependency > <dependency > <groupId > com.aliyun</groupId > <artifactId > aliyun-sdk-vod-upload</artifactId > <version > ${aliyun-sdk-vod-upload.version}</version > </dependency > <dependency > <groupId > com.alibaba</groupId > <artifactId > fastjson</artifactId > <version > ${fastjson.version}</version > </dependency > <dependency > <groupId > org.json</groupId > <artifactId > json</artifactId > <version > ${json.version}</version > </dependency > <dependency > <groupId > commons-dbutils</groupId > <artifactId > commons-dbutils</artifactId > <version > ${commons-dbutils.version}</version > </dependency > <dependency > <groupId > com.alibaba.otter</groupId > <artifactId > canal.client</artifactId > <version > ${canal.client.version}</version > </dependency > </dependencies > </dependencyManagement > <build > <plugins > <plugin > <groupId > org.springframework.boot</groupId > <artifactId > spring-boot-maven-plugin</artifactId > </plugin > </plugins > </build > </project >
创建子模块
右键new model创建即可,子项目service等
配置pom.xml 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 <dependencies > <dependency > <groupId > org.springframework.boot</groupId > <artifactId > spring-boot-starter-web</artifactId > </dependency > <dependency > <groupId > com.baomidou</groupId > <artifactId > mybatis-plus-boot-starter</artifactId > </dependency > <dependency > <groupId > mysql</groupId > <artifactId > mysql-connector-java</artifactId > </dependency > <dependency > <groupId > org.apache.velocity</groupId > <artifactId > velocity-engine-core</artifactId > </dependency > <dependency > <groupId > io.springfox</groupId > <artifactId > springfox-swagger2</artifactId > </dependency > <dependency > <groupId > io.springfox</groupId > <artifactId > springfox-swagger-ui</artifactId > </dependency > <dependency > <groupId > org.projectlombok</groupId > <artifactId > lombok</artifactId > </dependency > <dependency > <groupId > org.apache.poi</groupId > <artifactId > poi</artifactId > </dependency > <dependency > <groupId > org.apache.poi</groupId > <artifactId > poi-ooxml</artifactId > </dependency > <dependency > <groupId > commons-fileupload</groupId > <artifactId > commons-fileupload</artifactId > </dependency > <dependency > <groupId > org.apache.httpcomponents</groupId > <artifactId > httpclient</artifactId > </dependency > <dependency > <groupId > commons-io</groupId > <artifactId > commons-io</artifactId > </dependency > <dependency > <groupId > com.google.code.gson</groupId > <artifactId > gson</artifactId > </dependency > <dependency > <groupId > junit</groupId > <artifactId > junit</artifactId > <version > 4.12</version > </dependency > </dependencies >
创建子子模块
service_edu、service_vod
当前项目结构
common模块 swagger工具 创建子模块
命名为common,该模块不需要src文件夹
配置依赖 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 <?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 http://maven.apache.org/xsd/maven-4.0.0.xsd" > <parent > <artifactId > guli_parent</artifactId > <groupId > com.atguigu</groupId > <version > 0.0.1-SNAPSHOT</version > </parent > <modelVersion > 4.0.0</modelVersion > <artifactId > common</artifactId > <packaging > pom</packaging > <properties > <maven.compiler.source > 8</maven.compiler.source > <maven.compiler.target > 8</maven.compiler.target > </properties > <dependencies > <dependency > <groupId > org.springframework.boot</groupId > <artifactId > spring-boot-starter-web</artifactId > <scope > provided</scope > </dependency > <dependency > <groupId > com.baomidou</groupId > <artifactId > mybatis-plus-boot-starter</artifactId > <scope > provided</scope > </dependency > <dependency > <groupId > org.projectlombok</groupId > <artifactId > lombok</artifactId > <scope > provided</scope > </dependency > <dependency > <groupId > io.springfox</groupId > <artifactId > springfox-swagger2</artifactId > <scope > provided</scope > </dependency > <dependency > <groupId > io.springfox</groupId > <artifactId > springfox-swagger-ui</artifactId > <scope > provided</scope > </dependency > <dependency > <groupId > org.springframework.boot</groupId > <artifactId > spring-boot-starter-data-redis</artifactId > </dependency > </dependencies > </project >
创建子子模块
service_base
创建SwaggerConfig.java配置类 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 package com.atguigu.servicebase;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;@Configuration @EnableSwagger2 public class SwaggerConfig { @Bean public Docket webApiConfig () { return new Docket(DocumentationType.SWAGGER_2) .groupName("webApi" ) .apiInfo(webApiInfo()) .select() .paths(Predicates.not(PathSelectors.regex("/admin/.*" ))) .paths(Predicates.not(PathSelectors.regex("/error.*" ))) .build(); } private ApiInfo webApiInfo () { return new ApiInfoBuilder() .title("网站-课程中心API文档" ) .description("本文档描述了课程中心微服务接口定义" ) .version("1.0" ) .contact(new Contact("java" , "http://atguigu.com" , "1123@qq.com" )) .build(); } }
引入swagger子模块
需要在service模块中引入swagger模块的子模块servicebase模块,即在service模块的pom.xml文件中添加依赖即可(因为想要在service模块的子模块中使用,且都写上了pom打包,所以只要在service模块的pom.xml中添加即可)
1 2 3 4 5 <dependency > <groupId > com.atguigu</groupId > <artifactId > service_base</artifactId > <version > 0.0.1-SNAPSHOT</version > </dependency >
启动类添加组件扫描
EduApplication是service_edu模块下的启动类,由于service_base模块中的swaggerconfig配置类所在包地址也是com.atguigu.xxx,所以可以通过这个扫描到,否则只会扫描启动类所在及子包下的类(所以启动类要放在所有package的同级),如果是别的地址再额外添加即可({“com.atguigu”,”com.xxx”})
1 2 3 4 5 6 7 8 9 @SpringBootApplication @ComponentScan(basePackages = {"com.atguigu"}) public class EduApplication { public static void main (String[] args) { SpringApplication.run(EduApplication.class, args); } }
测试(访问)swagger
启动xxxApplication启动类
http://localhost:8001/swagger-ui.html
API文档提示编写
一下以controller为例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 package com.atguigu.eduservice.controller;import com.atguigu.eduservice.entity.EduTeacher;import com.atguigu.eduservice.service.EduTeacherService;import io.swagger.annotations.Api;import io.swagger.annotations.ApiOperation;import io.swagger.annotations.ApiParam;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.web.bind.annotation.*;import java.util.List;@Api(tags = {"讲师管理"}) @RestController @RequestMapping("/eduservice/edu-teacher") public class EduTeacherController { @Autowired private EduTeacherService teacherService; @ApiOperation(value = "所有讲师列表") @GetMapping("findAll") public List<EduTeacher> findAllTeacher () { List<EduTeacher> list = teacherService.list(null ); return list; } @ApiOperation(value = "根据id逻辑删除讲师") @DeleteMapping("{id}") public boolean removeTeacher (@ApiParam(name = "id", value = "讲师ID", required = true) @PathVariable("id") String id) { boolean flag = teacherService.removeById(id); return flag; } }
当前项目结构
统一结果返回格式 统一结果的格式 1 2 3 4 5 6 { "success" : 布尔, "code" : 数字, "message" : 字符串, "data" : HashMap }
创建子模块
common模块下创建common_utils模块
设定状态码
可以写成接口,或者其他形式
1 2 3 4 5 6 7 package com.atguigu.commonutils;public interface ResultCode { public static Integer SUCCESS = 20000 ; public static Integer ERROR = 20001 ; }
定义返回数据格式
创建一个统一返回结果类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 package com.atguigu.commonutils;import io.swagger.annotations.ApiModelProperty;import lombok.Data;import java.util.HashMap;import java.util.Map;@Data public class R { @ApiModelProperty(value = "是否成功") private Boolean success; @ApiModelProperty(value = "返回码") private Integer code; @ApiModelProperty(value = "返回消息") private String message; @ApiModelProperty(value = "返回数据") private Map<String, Object> data = new HashMap<String, Object>(); private R () { } public static R ok () { R r = new R(); r.setSuccess(true ); r.setCode(ResultCode.SUCCESS); r.setMessage("成功" ); return r; } public static R error () { R r = new R(); r.setSuccess(false ); r.setCode(ResultCode.ERROR); r.setMessage("失败" ); return r; } public R success (Boolean success) { this .setSuccess(success); return this ; } public R message (String message) { this .setMessage(message); return this ; } public R code (Integer code) { this .setCode(code); return this ; } public R data (String key, Object value) { this .data.put(key, value); return this ; } public R data (Map<String, Object> map) { this .setData(map); return this ; } }
使用统一返回结果格式
先在模块的pom.xml中引入已经定义好的统一返回结果类所在
将接口方法(controller中的方法)返回结果改为R即可
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 @Api(tags = {"讲师管理"}) @RestController @RequestMapping("/eduservice/edu-teacher") public class EduTeacherController { @Autowired private EduTeacherService teacherService; @ApiOperation(value = "所有讲师列表") @GetMapping("findAll") public List<EduTeacher> findAllTeacher () { List<EduTeacher> list = teacherService.list(null ); return list; } @ApiOperation(value = "根据id逻辑删除讲师") @DeleteMapping("{id}") public boolean removeTeacher (@ApiParam(name = "id", value = "讲师ID", required = true) @PathVariable("id") String id) { boolean flag = teacherService.removeById(id); return flag; } }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 @Api(tags = {"讲师管理"}) @RestController @RequestMapping("/eduservice/edu-teacher") public class EduTeacherController { @Autowired private EduTeacherService teacherService; @ApiOperation(value = "所有讲师列表") @GetMapping("findAll") public R findAllTeacher () { List<EduTeacher> list = teacherService.list(null ); return R.ok().data("teacherList" , list); } @ApiOperation(value = "根据id逻辑删除讲师") @DeleteMapping("{id}") public R removeTeacher (@ApiParam(name = "id", value = "讲师ID", required = true) @PathVariable("id") String id) { boolean flag = teacherService.removeById(id); return flag ? R.ok() : R.error(); } }
当前项目结构
统一异常处理 统一异常处理类
在common模块中的service_base中添加即可
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 @ControllerAdvice public class GlobalExceptionHandler { @ExceptionHandler(Exception.class) @ResponseBody public R error (Exception e) { e.printStackTrace(); return R.error().message("产生了异常!" ); } }
这里有一个依赖的问题,需要注意
特定异常处理 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 @ControllerAdvice public class GlobalExceptionHandler { @ExceptionHandler(Exception.class) @ResponseBody public R error (Exception e) { e.printStackTrace(); return R.error().message("产生了异常!" ); } @ExceptionHandler(ArithmeticException.class) @ResponseBody public R error (ArithmeticException e) { e.printStackTrace(); return R.error().message("产生了ArithmeticException异常!" ); } }
自定义异常处理
创建自定义异常类
1 2 3 4 5 6 7 8 9 10 11 @Data @NoArgsConstructor @AllArgsConstructor public class GuliException extends RuntimeException { private Integer code; private String msg; }
在统一异常处理类中创建方法
1 2 3 4 5 6 7 @ExceptionHandler(GuliException.class) @ResponseBody public R error (GuliException e) { e.printStackTrace(); return R.error().code(e.getCode()).message(e.getMsg()); }
抛出自定义异常
1 2 3 4 5 try { int i = 1 / 0 ; } catch (Exception e) { throw new GuliException(20001 , "zzzzz" ); }
当前项目结构
统一日志处理 配置日志级别
application.properties文件中配置即可
logback日志工具
log4j,logback等可以将日志输出到文件中
注意:需要先将application.properties中的有关日志的配置注释掉
配置工具的配置文件
在resource文件夹下创建logback-spring.xml配置文件
注意 的路径即可
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 <?xml version="1.0" encoding="UTF-8"?> <configuration scan ="true" scanPeriod ="10 seconds" > <contextName > logback</contextName > <property name ="log.path" value ="D:/MyProject/log/guli_edu" /> <property name ="CONSOLE_LOG_PATTERN" value ="%yellow(%date{yyyy-MM-dd HH:mm:ss}) |%highlight(%-5level) |%blue(%thread) |%blue(%file:%line) |%green(%logger) |%cyan(%msg%n)" /> <appender name ="CONSOLE" class ="ch.qos.logback.core.ConsoleAppender" > <filter class ="ch.qos.logback.classic.filter.ThresholdFilter" > <level > INFO</level > </filter > <encoder > <Pattern > ${CONSOLE_LOG_PATTERN}</Pattern > <charset > UTF-8</charset > </encoder > </appender > <appender name ="INFO_FILE" class ="ch.qos.logback.core.rolling.RollingFileAppender" > <file > ${log.path}/log_info.log</file > <encoder > <pattern > %d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n</pattern > <charset > UTF-8</charset > </encoder > <rollingPolicy class ="ch.qos.logback.core.rolling.TimeBasedRollingPolicy" > <fileNamePattern > ${log.path}/info/log-info-%d{yyyy-MM-dd}.%i.log</fileNamePattern > <timeBasedFileNamingAndTriggeringPolicy class ="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP" > <maxFileSize > 100MB</maxFileSize > </timeBasedFileNamingAndTriggeringPolicy > <maxHistory > 15</maxHistory > </rollingPolicy > <filter class ="ch.qos.logback.classic.filter.LevelFilter" > <level > INFO</level > <onMatch > ACCEPT</onMatch > <onMismatch > DENY</onMismatch > </filter > </appender > <appender name ="WARN_FILE" class ="ch.qos.logback.core.rolling.RollingFileAppender" > <file > ${log.path}/log_warn.log</file > <encoder > <pattern > %d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n</pattern > <charset > UTF-8</charset > </encoder > <rollingPolicy class ="ch.qos.logback.core.rolling.TimeBasedRollingPolicy" > <fileNamePattern > ${log.path}/warn/log-warn-%d{yyyy-MM-dd}.%i.log</fileNamePattern > <timeBasedFileNamingAndTriggeringPolicy class ="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP" > <maxFileSize > 100MB</maxFileSize > </timeBasedFileNamingAndTriggeringPolicy > <maxHistory > 15</maxHistory > </rollingPolicy > <filter class ="ch.qos.logback.classic.filter.LevelFilter" > <level > warn</level > <onMatch > ACCEPT</onMatch > <onMismatch > DENY</onMismatch > </filter > </appender > <appender name ="ERROR_FILE" class ="ch.qos.logback.core.rolling.RollingFileAppender" > <file > ${log.path}/log_error.log</file > <encoder > <pattern > %d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n</pattern > <charset > UTF-8</charset > </encoder > <rollingPolicy class ="ch.qos.logback.core.rolling.TimeBasedRollingPolicy" > <fileNamePattern > ${log.path}/error/log-error-%d{yyyy-MM-dd}.%i.log</fileNamePattern > <timeBasedFileNamingAndTriggeringPolicy class ="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP" > <maxFileSize > 100MB</maxFileSize > </timeBasedFileNamingAndTriggeringPolicy > <maxHistory > 15</maxHistory > </rollingPolicy > <filter class ="ch.qos.logback.classic.filter.LevelFilter" > <level > ERROR</level > <onMatch > ACCEPT</onMatch > <onMismatch > DENY</onMismatch > </filter > </appender > <springProfile name ="dev" > <logger name ="com.guli" level ="INFO" /> <root level ="INFO" > <appender-ref ref ="CONSOLE" /> <appender-ref ref ="INFO_FILE" /> <appender-ref ref ="WARN_FILE" /> <appender-ref ref ="ERROR_FILE" /> </root > </springProfile > <springProfile name ="pro" > <root level ="INFO" > <appender-ref ref ="CONSOLE" /> <appender-ref ref ="DEBUG_FILE" /> <appender-ref ref ="INFO_FILE" /> <appender-ref ref ="ERROR_FILE" /> <appender-ref ref ="WARN_FILE" /> </root > </springProfile > </configuration >
输出错误日志
在统一异常处理类上添加注释,并且在异常处理的时候调用log.error()
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 @ControllerAdvice @Slf4j public class GlobalExceptionHandler { @ExceptionHandler(ArithmeticException.class) @ResponseBody public R error (ArithmeticException e) { e.printStackTrace(); log.error(e.getMessage()); return R.error().message("产生了ArithmeticException异常!" ); } }
在配置的路径中即可看到日志文件
当前目录结构
service_edu讲师管理模块(后端) 快速前置配置 配置文件application.properties 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 server.port =8001 spring.application.name =service-edu spring.profiles.active =dev spring.datasource.driver-class-name =com.mysql.cj.jdbc.Driver spring.datasource.url =jdbc:mysql://localhost:3306/guli?serverTimezone=GMT%2B8 spring.datasource.username =root spring.datasource.password =123456789 spring.jackson.date-format =yyyy-MM-dd HH:mm:ss spring.jackson.time-zone =GMT+8 mybatis-plus.configuration.log-impl =org.apache.ibatis.logging.stdout.StdOutImpl
mybatisplus代码生成器 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 import com.baomidou.mybatisplus.annotation.DbType;import com.baomidou.mybatisplus.annotation.IdType;import com.baomidou.mybatisplus.generator.AutoGenerator;import com.baomidou.mybatisplus.generator.config.DataSourceConfig;import com.baomidou.mybatisplus.generator.config.GlobalConfig;import com.baomidou.mybatisplus.generator.config.PackageConfig;import com.baomidou.mybatisplus.generator.config.StrategyConfig;import com.baomidou.mybatisplus.generator.config.rules.DateType;import com.baomidou.mybatisplus.generator.config.rules.NamingStrategy;import org.junit.Test;public class CodeGenerator { @Test public void run () { AutoGenerator mpg = new AutoGenerator(); GlobalConfig gc = new GlobalConfig(); gc.setOutputDir("D:\\MyProject\\springboot\\guli_parent\\service\\service_edu" + "/src/main/java" ); gc.setAuthor("testjava" ); gc.setOpen(false ); gc.setFileOverride(false ); gc.setServiceName("%sService" ); gc.setIdType(IdType.ID_WORKER_STR); gc.setDateType(DateType.ONLY_DATE); gc.setSwagger2(true ); mpg.setGlobalConfig(gc); DataSourceConfig dsc = new DataSourceConfig(); dsc.setUrl("jdbc:mysql://localhost:3306/guli?serverTimezone=GMT%2B8" ); dsc.setDriverName("com.mysql.cj.jdbc.Driver" ); dsc.setUsername("root" ); dsc.setPassword("123456789" ); dsc.setDbType(DbType.MYSQL); mpg.setDataSource(dsc); PackageConfig pc = new PackageConfig(); pc.setModuleName("eduservice" ); pc.setParent("com.atguigu" ); pc.setController("controller" ); pc.setEntity("entity" ); pc.setService("service" ); pc.setMapper("mapper" ); mpg.setPackageInfo(pc); StrategyConfig strategy = new StrategyConfig(); strategy.setInclude("edu_teacher" ); strategy.setNaming(NamingStrategy.underline_to_camel); strategy.setTablePrefix(pc.getModuleName() + "_" ); strategy.setColumnNaming(NamingStrategy.underline_to_camel); strategy.setEntityLombokModel(true ); strategy.setRestControllerStyle(true ); strategy.setControllerMappingHyphenStyle(true ); mpg.setStrategy(strategy); mpg.execute(); } }
当前项目结构
创建配置类
配置包扫描等
1 2 3 4 5 6 7 8 9 package com.atguigu.eduservice.config;import org.mybatis.spring.annotation.MapperScan;import org.springframework.context.annotation.Configuration;@Configuration @MapperScan("com.atguigu.eduservice.mapper") public class EduConfig {}
创建启动类
启动类位置要放在controller、entity等package的同级,然后run即可启动
1 2 3 4 5 6 7 8 9 10 11 12 13 package com.atguigu.eduservice;import org.springframework.boot.SpringApplication;import org.springframework.boot.autoconfigure.SpringBootApplication;@SpringBootApplication public class EduApplication { public static void main (String[] args) { SpringApplication.run(EduApplication.class, args); } }
简单查询所有讲师 编写controller 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 package com.atguigu.eduservice.controller;import com.atguigu.eduservice.entity.EduTeacher;import com.atguigu.eduservice.service.EduTeacherService;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.web.bind.annotation.GetMapping;import org.springframework.web.bind.annotation.RequestMapping;import org.springframework.web.bind.annotation.RestController;import java.util.List;@RestController @RequestMapping("/eduservice/edu-teacher") public class EduTeacherController { @Autowired private EduTeacherService teacherService; @GetMapping("findAll") public List<EduTeacher> findAllTeacher () { List<EduTeacher> list = teacherService.list(null ); return list; } }
测试 localhost:8001//eduservice/edu-teacher/findAll
逻辑删除讲师 实体类属性添加注解
为EduTeacher实体类添加相应的注释@TableLogic
1 2 3 @ApiModelProperty(value = "逻辑删除 1(true)已删除, 0(false)未删除") @TableLogic private Integer isDeleted;
创建mybatisplus配置类
添加逻辑删除插件(注意mybatis版本,低版本是需要添加的,高版本不用,而且TableId自动识别Type AutoGenerator写法也有区别等)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 package com.atguigu.eduservice.config;import com.baomidou.mybatisplus.core.injector.ISqlInjector;import com.baomidou.mybatisplus.extension.injector.LogicSqlInjector;import org.mybatis.spring.annotation.MapperScan;import org.springframework.context.annotation.Bean;import org.springframework.context.annotation.Configuration;@Configuration @MapperScan("com.atguigu.eduservice.mapper") public class EduConfig { @Bean public ISqlInjector sqlInjector () { return new LogicSqlInjector(); } }
编写controller
注意使用REST风格URI,pathVariable对应路径参数
1 2 3 4 5 6 @DeleteMapping("{id}") public boolean removeTeacher (@PathVariable("id") String id) { boolean flag = teacherService.removeById(id); return flag; }
测试(swagger工具)
整合swagger可以用于接口测试、生成在线接口文档等
分页功能 配置类导入插件 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 @Configuration @MapperScan("com.atguigu.eduservice.mapper") public class EduConfig { @Bean public ISqlInjector sqlInjector () { return new LogicSqlInjector(); } @Bean public PaginationInterceptor paginationInterceptor () { return new PaginationInterceptor(); } }
编写controller 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 @GetMapping("pageTeacher/{current}/{limit}") public R pageListTeacher (@PathVariable long current, @PathVariable long limit) { Page<EduTeacher> pageTeacher = new Page<>(current, limit); teacherService.page(pageTeacher, null ); long total = pageTeacher.getTotal(); List<EduTeacher> records = pageTeacher.getRecords(); HashMap<String, Object> map = new HashMap<>(); map.put("total" , total); map.put("records" , records); return R.ok().data(map); }
多条件组合查询分页 创建VO实体对象
把条件值传递到接口里(将条件值封装到VO(ViewObject)实体对象当中,再把对象vo传递到接口中)浅析VO、DTO、DO、PO的概念、区别和用处_zjrbiancheng的专栏-CSDN博客 实体类(VO,DO,DTO)的划分_xin.wang的博客-CSDN博客
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 package com.atguigu.eduservice.entity.vo;import io.swagger.annotations.ApiModelProperty;import lombok.Data;@Data public class TeacherQuery { @ApiModelProperty(value = "教师名称,模糊查询") private String name; @ApiModelProperty(value = "头衔 1高级讲师 2首席讲师") private Integer level; @ApiModelProperty(value = "查询开始时间", example = "2019-01-01 10:10:10") private String begin; @ApiModelProperty(value = "查询结束时间", example = "2019-12-01 10:10:10") private String end; }
编写controller 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 @ApiOperation("多条件组合分页") @PostMapping("pageTeacherCondition/{current}/{limit}") public R pageListTeacherCondition (@PathVariable long current, @PathVariable long limit, @RequestBody(required = false) TeacherQuery teacherQuery) { Page<EduTeacher> pageTeacher = new Page<>(current, limit); QueryWrapper<EduTeacher> wrapper = new QueryWrapper<>(); String name = teacherQuery.getName(); Integer level = teacherQuery.getLevel(); String begin = teacherQuery.getBegin(); String end = teacherQuery.getEnd(); if (StringUtils.hasLength(name)) { wrapper.like("name" , name); } if (level != null ) { wrapper.eq("level" , level); } if (StringUtils.hasLength(begin)) { wrapper.ge("gmt_create" , begin); } if (StringUtils.hasLength(end)) { wrapper.le("gmt_create" , end); } teacherService.page(pageTeacher, wrapper); long total = pageTeacher.getTotal(); List<EduTeacher> records = pageTeacher.getRecords(); return R.ok().data("total" , total).data("records" , records); }
添加讲师 实体类属性添加注解
@TableField(fill = FieldFill.xxx)自动填充注解
1 2 3 4 5 6 7 @ApiModelProperty(value = "创建时间") @TableField(fill = FieldFill.INSERT) private Date gmtCreate;@ApiModelProperty(value = "更新时间") @TableField(fill = FieldFill.INSERT_UPDATE) private Date gmtModified;
创建自定义handler类
可以放到service_base模块,然后需要使用导入依赖即可
当对应方法被使用的使用会被调用
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 package com.atguigu.servicebase.handler;import com.baomidou.mybatisplus.core.handlers.MetaObjectHandler;import org.apache.ibatis.reflection.MetaObject;import org.springframework.stereotype.Component;import java.util.Date;@Component public class MyMetaObjectHandler implements MetaObjectHandler { @Override public void insertFill (MetaObject metaObject) { this .setFieldValByName("gmtCreate" , new Date(), metaObject); this .setFieldValByName("gmtModified" , new Date(), metaObject); } @Override public void updateFill (MetaObject metaObject) { this .setFieldValByName("gmtModified" , new Date(), metaObject); } }
注意结构
编写controller 1 2 3 4 5 6 7 8 9 10 11 12 @ApiOperation("添加讲师") @PostMapping("addTeacher") public R addTeacher (@RequestBody EduTeacher eduTeacher) { boolean save = teacherService.save(eduTeacher); return save ? R.ok() : R.error(); }
修改讲师 编写controller 1 2 3 4 5 6 7 8 9 10 11 12 13 @ApiOperation("根据id查询讲师") @GetMapping("getTeacher/{id}") public R getTeacher (@PathVariable String id) { EduTeacher teacher = teacherService.getById(id); return R.ok().data("teacher" , teacher); } @ApiOperation("修改讲师") @PostMapping("updateTeacher") public R updateTeacher (@RequestBody EduTeacher teacher) { boolean flag = teacherService.updateById(teacher); return flag ? R.ok() : R.error(); }
使用@PutMapping如下
1 2 3 4 5 6 7 8 @ApiOperation("修改讲师") @PutMapping("{id}}") public R updateTeacher (@PathVariable String id, @RequestBody EduTeacher teacher) { teacher.setId(id); boolean flag = teacherService.updateById(teacher); return flag ? R.ok() : R.error(); }
待续