一.项目各种统一要求
1.项目创建
(1)项目结构:
(2)父工程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 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<!--该工程的信息-->
<groupId>org.example</groupId>
<artifactId>copy_used</artifactId>
<packaging>pom</packaging>
<version>1.0-SNAPSHOT</version>
<modules>
<module>service</module>
</modules>
<!--引入springboot的父工程-->
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.2.1.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<properties>
<maven.compiler.source>14</maven.compiler.source>
<maven.compiler.target>14</maven.compiler.target>
<java.version>1.8</java.version><!--java版本号-->
<mybatis-plus.version>3.0.5</mybatis-plus.version><!--mybatis-plus-->
<velocity.version>2.0</velocity.version><!--velocity 模板引擎,用于生成代码-->
<swagger.version>2.7.0</swagger.version><!--swagger-->
<jodatime.version>2.10.1</jodatime.version><!--日期时间工具-->
<poi.version>3.17</poi.version><!--操作excel文件-->
<commons-fileupload.version>1.3.1</commons-fileupload.version><!--文件上传-->
<commons-io.version>2.6</commons-io.version><!--文件上传-->
<httpclient.version>4.5.1</httpclient.version><!--支持基本的http协议-->
<jwt.version>0.7.0</jwt.version><!--Json web token 是一种为了传递声明指定的Json标准-->
<fastjson.version>1.2.28</fastjson.version><!--可以解析json的字符串-->
<gson.version>2.8.2</gson.version><!--是谷歌提供的用来在Json数据和Java对象进行映射的Java类库-->
<json.version>20170516</json.version><!--Json版本-->
<commons-dbutils.version>1.7</commons-dbutils.version><!--JDBC工具类库,对JDBC简单封装-->
<canal.client.version>1.1.0</canal.client.version><!--用于mysql数据库增量日志解析-->
<docker.image.prefix>zx</docker.image.prefix><!--docker镜像-->
<cloud-alibaba.version>0.2.2.RELEASE</cloud-alibaba.version>
</properties>
<dependencyManagement>
<dependencies>
<!--Spring Cloud-->
<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>
<!--mybatis-plus 持久层-->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>${mybatis-plus.version}</version>
</dependency>
<!-- velocity 模板引擎, 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-->
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger-ui</artifactId>
<version>${swagger.version}</version>
</dependency>
<!--日期时间工具-->
<dependency>
<groupId>joda-time</groupId>
<artifactId>joda-time</artifactId>
<version>${jodatime.version}</version>
</dependency>
<!--xls-->
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi</artifactId>
<version>${poi.version}</version>
</dependency>
<!--xlsx-->
<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>
<!--commons-io-->
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>${commons-io.version}</version>
</dependency>
<!--httpclient-->
<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>
<!-- JWT -->
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt</artifactId>
<version>${jwt.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>
(2)子工程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 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<!--标志他的父亲是谁-->
<parent>
<artifactId>copy_used</artifactId>
<groupId>org.example</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<!--这个是pom文件的版本不用管-->
<modelVersion>4.0.0</modelVersion>
<!--version和groppId和父亲一样所以省略了-->
<artifactId>service</artifactId>
<packaging>pom</packaging>
<modules>
<module>service_trip</module>
</modules>
<properties>
<maven.compiler.source>14</maven.compiler.source>
<maven.compiler.target>14</maven.compiler.target>
</properties>
<dependencies>
<!--引入web环境-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!--mybatis-plus-->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
</dependency>
<!--mysql-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<!-- velocity 模板引擎, Mybatis Plus 代码生成器需要 -->
<dependency>
<groupId>org.apache.velocity</groupId>
<artifactId>velocity-engine-core</artifactId>
</dependency>
<!--swagger-->
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger2</artifactId>
</dependency>
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger-ui</artifactId>
</dependency>
<!--lombok用来简化实体类:需要安装lombok插件-->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
<!--xls-->
<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>
<!--httpclient-->
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpclient</artifactId>
</dependency>
<!--commons-io-->
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
</dependency>
<!--gson-->
<dependency>
<groupId>com.google.code.gson</groupId>
<artifactId>gson</artifactId>
</dependency>
<!--测试-->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
</dependency>
</dependencies>
<build>
<resources>
<resource>
<directory>src/main/java</directory>
<includes>
<include>**/*.xml</include>
</includes>
<filtering>false</filtering>
</resource>
</resources>
</build>
</project>
(4)孙子工程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 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>service</artifactId>
<groupId>org.example</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>service_trip</artifactId>
<properties>
<maven.compiler.source>14</maven.compiler.source>
<maven.compiler.target>14</maven.compiler.target>
</properties>
<dependencies>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>easyexcel</artifactId>
<version>2.1.1</version>
</dependency>
</dependencies>
</project>
2.代码生成器
这个代码生成器还不够完善,需要补充:
1.配置类,在配置类上加上@MapperScan("com.guguo.tripservice.mapper")
2.启动类
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;
/**
* @author
* @since 2018/12/13
*/
public class CodeGenerator{
@Test
public void run() {
// 1、创建代码生成器
AutoGenerator mpg = new AutoGenerator();
// 2、全局配置
GlobalConfig gc = new GlobalConfig();
String projectPath = System.getProperty("user.dir");
gc.setOutputDir("D:\\idea_saved\\copy_used\\service\\service_trip" + "/src/main/java");
gc.setAuthor("testjava");
gc.setOpen(false); //生成后是否打开资源管理器
gc.setFileOverride(false); //重新生成时文件是否覆盖
gc.setServiceName("%sService"); //去掉Service接口的首字母I
gc.setIdType(IdType.ID_WORKER_STR); //主键策略
gc.setDateType(DateType.ONLY_DATE);//定义生成的实体类中日期类型
gc.setSwagger2(true);//开启Swagger2模式
mpg.setGlobalConfig(gc);
// 3、数据源配置
DataSourceConfig dsc = new DataSourceConfig();
dsc.setUrl("jdbc:mysql://localhost:3306/copy_used?serverTimezone=GMT%2B8");
dsc.setDriverName("com.mysql.cj.jdbc.Driver");
dsc.setUsername("root");
dsc.setPassword("root");
dsc.setDbType(DbType.MYSQL);
mpg.setDataSource(dsc);
// 4、包配置
PackageConfig pc = new PackageConfig();
pc.setParent("com.guguo");
pc.setModuleName("tripservice"); //模块名
pc.setController("controller");
pc.setEntity("entity");
pc.setService("service");
pc.setMapper("mapper");
mpg.setPackageInfo(pc);
// 5、策略配置
StrategyConfig strategy = new StrategyConfig();
strategy.setInclude("traveller");
strategy.setNaming(NamingStrategy.underline_to_camel);//数据库表映射到实体的命名策略
strategy.setTablePrefix(pc.getModuleName() + "_"); //生成实体时去掉表前缀
strategy.setColumnNaming(NamingStrategy.underline_to_camel);//数据库表字段映射到实体的命名策略
strategy.setEntityLombokModel(true); // lombok 模型 @Accessors(chain = true) setter链式操作
strategy.setRestControllerStyle(true); //restful api风格控制器
strategy.setControllerMappingHyphenStyle(true); //url中驼峰转连字符
mpg.setStrategy(strategy);
// 6、执行
mpg.execute();
}
}
3.配置swagger2
步骤:
1.引坐标(前面干过了)
2.写一个新的配置类,加上如下配置。如果写到别的模块要扫描进启动类
3.访问local:8080/swagger-ui.html
package com.guguo.tripservice.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;
@Configuration
@EnableSwagger2//swagger注解
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("Helen", "http://atguigu.com", "55317332@qq.com"))
.build();
}
}
5.统一结果返回
1.在接口中定义返回码的值
2.创建结果类,定义成功和失败时候的方法。
3. 定义在别的模块要记得引入坐标
package com.guguo.tripservice.utils;
public interface ResultCode {
Integer SUCCESS=20000;
Integer ERROR=20001;
}
package com.guguo.tripservice.utils;
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.setMessage("成功");
r.setSuccess(true);
r.setCode(ResultCode.SUCCESS);
return r;
}
public static R error(){
R r = new R();
r.setMessage("失败");
r.setSuccess(false);
r.setCode(ResultCode.ERROR);
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;
}
}
4.统一异常处理
普通异常:
使用@ControllerAdvice创建一个统一异常处理类
自定义异常:
1.创建自定义的异常类,继承RuntimeException,类中定义一些异常的属性
2.把自定义的异常类写到上面的统一异常处理类中去
3.手动抛出异常
package com.guguo.tripservice.utils.exception;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
//这是自定义异常类
@Data
@NoArgsConstructor
@AllArgsConstructor
public class MyException extends RuntimeException{
private Integer code=20001;
private String msg="执行了自定义的异常";
}
package com.guguo.tripservice.utils.exception;
import com.guguo.tripservice.utils.result.R;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;
//为了进行统一的异常处理
@Slf4j
@ControllerAdvice
public class GlobalExceptionHandler {
//指定出现什么异常执行这个方法,普通异常
@ExceptionHandler(ArithmeticException.class)
@ResponseBody
public R error(ArithmeticException e){
e.printStackTrace();
return R.error().message("执行了全局异常处理");
}
//自定义异常
@ExceptionHandler(MyException.class)//自定义异常
@ResponseBody
public R error(MyException e){
e.printStackTrace();
log.error(e.getMessage());
return R.error().message(e.getMsg()+" "+e.getCode());//这些数据,可以在自定义异常类里面设置
}
}
5.统一日志管理
1.日志等级:OFF FATAL ERROR WARN INFO DEBUG ALL
2.设置日志级别,在配置文件中: logging.level.root=WARN
可以使用logback日志工具将日志输出到文件中,步骤如下:
1.删除配置文件中的日志配置
2.在reasources中创建logback-spring.xml文件(名字不要改),然后复制下面的内容。
3.如果想把运行时候的一样也输出到文件中,那就在上面的异常处理类上加@SlF4j,然
后在类中的方法中写log.error(e.getMessage)
<?xml version="1.0" encoding="UTF-8"?>
<configuration scan="true" scanPeriod="10 seconds">
<!-- 日志级别从低到高分为TRACE < DEBUG < INFO < WARN < ERROR < FATAL,如果设置为WARN,则低于WARN的信息都不会输出 -->
<!-- scan:当此属性设置为true时,配置文件如果发生改变,将会被重新加载,默认值为true -->
<!-- scanPeriod:设置监测配置文件是否有修改的时间间隔,如果没有给出时间单位,默认单位是毫秒。当scan为true时,此属性生效。默认的时间间隔为1分钟。 -->
<!-- debug:当此属性设置为true时,将打印出logback内部日志信息,实时查看logback运行状态。默认值为false。 -->
<contextName>logback</contextName>
<!-- name的值是变量的名称,value的值时变量定义的值。通过定义的值会被插入到logger上下文中。定义变量后,可以使“${}”来使用变量。 -->
<property name="log.path" value="D:/idea_saved/guli_log/edu" />
<!-- 彩色日志 -->
<!-- 配置格式变量:CONSOLE_LOG_PATTERN 彩色日志格式 -->
<!-- magenta:洋红 -->
<!-- boldMagenta:粗红-->
<!-- cyan:青色 -->
<!-- white:白色 -->
<!-- magenta:洋红 -->
<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">
<!--此日志appender是为开发使用,只配置最底级别,控制台输出的日志级别是大于或等于此级别的日志信息-->
<!-- 例如:如果此处配置了INFO级别,则后面其他位置即使配置了DEBUG级别的日志,也不会被输出 -->
<filter class="ch.qos.logback.classic.filter.ThresholdFilter">
<level>INFO</level>
</filter>
<encoder>
<Pattern>${CONSOLE_LOG_PATTERN}</Pattern>
<!-- 设置字符集 -->
<charset>UTF-8</charset>
</encoder>
</appender>
<!--输出到文件-->
<!-- 时间滚动输出 level为 INFO 日志 -->
<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>
<!-- 此日志文件只记录info级别的 -->
<filter class="ch.qos.logback.classic.filter.LevelFilter">
<level>INFO</level>
<onMatch>ACCEPT</onMatch>
<onMismatch>DENY</onMismatch>
</filter>
</appender>
<!-- 时间滚动输出 level为 WARN 日志 -->
<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>
<!-- 此日志文件只记录warn级别的 -->
<filter class="ch.qos.logback.classic.filter.LevelFilter">
<level>warn</level>
<onMatch>ACCEPT</onMatch>
<onMismatch>DENY</onMismatch>
</filter>
</appender>
<!-- 时间滚动输出 level为 ERROR 日志 -->
<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>
<!-- 此日志文件只记录ERROR级别的 -->
<filter class="ch.qos.logback.classic.filter.LevelFilter">
<level>ERROR</level>
<onMatch>ACCEPT</onMatch>
<onMismatch>DENY</onMismatch>
</filter>
</appender>
<!--
<logger>用来设置某一个包或者具体的某一个类的日志打印级别、以及指定<appender>。
<logger>仅有一个name属性,
一个可选的level和一个可选的addtivity属性。
name:用来指定受此logger约束的某一个包或者具体的某一个类。
level:用来设置打印级别,大小写无关:TRACE, DEBUG, INFO, WARN, ERROR, ALL 和 OFF,
如果未设置此属性,那么当前logger将会继承上级的级别。
-->
<!--
使用mybatis的时候,sql语句是debug下才会打印,而这里我们只配置了info,所以想要查看sql语句的话,有以下两种操作:
第一种把<root level="INFO">改成<root level="DEBUG">这样就会打印sql,不过这样日志那边会出现很多其他消息
第二种就是单独给mapper下目录配置DEBUG模式,代码如下,这样配置sql语句会打印,其他还是正常DEBUG级别:
-->
<!--开发环境:打印控制台-->
<springProfile name="dev">
<!--可以输出项目中的debug日志,包括mybatis的sql日志-->
<logger name="com.guli" level="INFO" />
<!--
root节点是必选节点,用来指定最基础的日志输出级别,只有一个level属性
level:用来设置打印级别,大小写无关:TRACE, DEBUG, INFO, WARN, ERROR, ALL 和 OFF,默认是DEBUG
可以包含零个或多个appender元素。
-->
<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>
二.项目的前置条件:
数据库的表:
edu_teacher及其对应的实体类
edu_subject及其对应的实体类
@Data//自动生成get和set方法
@EqualsAndHashCode(callSuper = false)
@Accessors(chain = true)
@ApiModel(value="EduTeacher对象", description="讲师")
public class EduTeacher implements Serializable {
private static final long serialVersionUID = 1L;
@ApiModelProperty(value = "讲师ID")
@TableId(value = "id", type = IdType.ID_WORKER_STR)//使用雪花算法并且将算出来的id变成String的
private String id;
@ApiModelProperty(value = "讲师姓名")
private String name;
@ApiModelProperty(value = "讲师简介")
private String intro;
@ApiModelProperty(value = "讲师资历,一句话说明讲师")
private String career;
@ApiModelProperty(value = "头衔 1高级讲师 2首席讲师")
private Integer level;
@ApiModelProperty(value = "讲师头像")
private String avatar;
@ApiModelProperty(value = "排序")
private Integer sort;
@ApiModelProperty(value = "逻辑删除 1(true)已删除, 0(false)未删除")
@TableLogic//逻辑删除的注解
private Boolean isDeleted;
@ApiModelProperty(value = "创建时间")
@TableField(fill=FieldFill.INSERT)//自动填充
private Date gmtCreate;
@ApiModelProperty(value = "更新时间")
@TableField(fill=FieldFill.INSERT_UPDATE)//自动填充
private Date gmtModified;
}
@Data
@EqualsAndHashCode(callSuper = false)
@Accessors(chain = true)
@ApiModel(value="EduSubject对象", description="课程科目")
public class EduSubject implements Serializable {
private static final long serialVersionUID = 1L;
@ApiModelProperty(value = "课程类别ID")
@TableId(value = "id", type = IdType.ID_WORKER_STR)
private String id;
@ApiModelProperty(value = "类别名称")
private String title;
@ApiModelProperty(value = "父ID")
private String parentId;
@ApiModelProperty(value = "排序字段")
private Integer sort;
@ApiModelProperty(value = "创建时间")
@TableField(fill= FieldFill.INSERT)
private Date gmtCreate;
@ApiModelProperty(value = "更新时间")
@TableField(fill= FieldFill.INSERT_UPDATE)
private Date gmtModified;
}
配置文件application.properties:
# 端口号
server.port=8001
spring.application.name=service-edu
# 开发环境
spring.profiles.active=dev
# mysql的配置
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=root
#json
spring.jackson.date-format=yyyy-MM-dd HH:mm:ss
spring.jackson.time-zone=GMT+8
#设置日志的等级
#logging.level.root=debug
#mybatis的日志配置
#mybatis-plus.configuration.log-impl=org.apache.ibatis.logging.stdout.StdOutImpl
maven依赖:
三.单表的增删改查功能
1.添加教师(需要前端上传一个Json格式的教师对象,不返回数据)
//Controller层代码
@Autowired
private EduTeacherServiceImpl eduTeacherService;
@PostMapping("/addTeacher")
public R addTeacher(@RequestBody EduTeacher eduTeacher){
boolean b = eduTeacherService.save(eduTeacher);
if(b){
return R.ok();
}else{
return R.error();
}
}
//mapper和Service里面没有代码,save方法在BaseMapper里面封装好了
2.通过条件分页查询教师数据(需要页数和每页的记录数,返回查询的总数和查询的结果)
//Controller层代码
@PostMapping("/pageTeacherCondition/{current}/{limit}")
public R pageTeacherCondition(@PathVariable long current,
@PathVariable long limit,
@RequestBody(required=false) TeacherQuery teacherQuery){
Page<EduTeacher> pageTeacher = new Page<>(current,limit);//用于分页
QueryWrapper<EduTeacher> queryWrapper = new QueryWrapper<>();//用于设置查询的条件
//多条件组合查询,判断条件值是不是空,通过创建动态sql语句来实现
String name = teacherQuery.getName();
Integer level = teacherQuery.getLevel();
String begin = teacherQuery.getBegin();
String end = teacherQuery.getEnd();
if(!StringUtils.isEmpty(name)){
queryWrapper.like("name",name);//前一个是字段的名称,后一个是传过来的参数
}
if(!StringUtils.isEmpty(level)){
queryWrapper.eq("level",level);
}
if(!StringUtils.isEmpty(begin)){
queryWrapper.ge("gmt_create",begin);//≥
}
if(!StringUtils.isEmpty(end)){
queryWrapper.le("gmt_modified",end);//≤
}
queryWrapper.orderByDesc("gmt_create");
//进行查询
eduTeacherService.page(pageTeacher,queryWrapper);
long total = pageTeacher.getTotal();//获得pageTeacher对象中封装的总记录数
List<EduTeacher> records = pageTeacher.getRecords();//获得pageTeacher对象中封装的所有记录
return R.ok().data("total",total).data("items",records);
}
//这个是进行分页查询的条件封装的实体类
@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;//注意,这里使用的是String类型,前端传过来的数据无需进行类型转换
@ApiModelProperty(value = "查询结束时间", example = "2019-12-01 10:10:10")
private String end;
}
3.删除教师信息通过id(需要一个通过路径赋值的id,不返回数据)
//Controller层代码
@Autowired
private EduTeacherServiceImpl eduTeacherService;
@DeleteMapping("/{id}")
public R deleteteacher(@PathVariable String id){
boolean flag = eduTeacherService.removeById(id);
if(flag){
return R.ok();
}else{
return R.error();
}
}
//mapper和Service里面没有代码,removeById方法在BaseMapper里面封装好了
4.修改教师信息(需要前端上传一个Json格式的教师对象,不返回数据)
//Controller层代码
@Autowired
private EduTeacherServiceImpl eduTeacherService;
@PostMapping("/updateTeacher")
public R updateTeacher(@RequestBody EduTeacher eduTeacher){
boolean b = eduTeacherService.updateById(eduTeacher);
if(b){
return R.ok();
}else{
return R.error();
}
}
mapper和Service里面没有代码,removeById方法在updateById里面封装好了
5.查询所有的课程课程分类信息,然后在前端进行树状展示
将二类课程封装到一类课程里面(不需要数据,返回课程对象的List集合,在第一类课程类里添加以第二类课程为泛型的List集合,分别从数据库查出两个List集合,然后用BeanUtils进行封装就好)。
//这个是一级课程分类的实体类,每个一级实体类都含有多个二级课程分类的实体类
@Component
@Data
public class OneSubject {
//比如说,前端开发包含vue,JavaScript,node...
private String id;//这个是一级课程的id
private String title;
private List<TwoSubject> children=new ArrayList<>();
}
//这是二级课程分类的实体类
@Component
@Data
public class TwoSubject {
private String id;
private String title;
}
//Controller层代码
@Autowired
private EduSubjectService eduSubjectService;
@GetMapping("/getAllSubject")
public R getAllSubject(){
List<OneSubject> subjects=eduSubjectService.getAllSubjects();
return R.ok().data("subjects",subjects);
}
//Service层代码
@Override
public List<OneSubject> getAllSubjects() {
//查询所有的一级分类
QueryWrapper<EduSubject> oneSubjectQueryWrapper = new QueryWrapper<>();
oneSubjectQueryWrapper.eq("parent_id","0");
List<EduSubject> list = this.list(oneSubjectQueryWrapper);
//查询所有的二级分类
QueryWrapper<EduSubject> twoSubjectQueryWrapper = new QueryWrapper<>();
twoSubjectQueryWrapper.ne("parent_id","0");
List<EduSubject> list2 = this.list(twoSubjectQueryWrapper);
//创建标准返回结果对象
ArrayList<OneSubject> onesubjects = new ArrayList<>();
//把二级分类插入到一级分类中
for (EduSubject Subject1 : list) {
OneSubject onesubject = new OneSubject();
BeanUtils.copyProperties(Subject1,onesubject);
onesubjects.add(onesubject);
String pid = Subject1.getId();
//拿二级分类去填一级分类的对象
for (EduSubject subject2 : list2) {
if(subject2.getParentId().equals(pid)){
TwoSubject twosubject = new TwoSubject();
BeanUtils.copyProperties(subject2,twosubject);
onesubject.getChildren().add(twosubject);
}
}
}
return onesubjects;
}
6.文件的上传和下载功能
@RequestMapping(value = "/upload")
public String upload(@RequestParam("file") MultipartFile file) { //注意参数
try {
if (file.isEmpty()) {
return "文件为空";
}
// 获取文件名
String fileName = file.getOriginalFilename();
log.info("上传的文件名为:" + fileName);//写日志
// 获取文件的后缀名
String suffixName = fileName.substring(fileName.lastIndexOf("."));
log.info("文件的后缀名为:" + suffixName);//写日志
// 设置文件存储路径 *************************************************
String filePath = "./FILE/KING/";
String path = filePath + fileName;
File dest = new File(new File(path).getAbsolutePath());// dist为文件,有多级目录的文件
// 检测是否存在目录
if (!dest.getParentFile().exists()) {//因此这里使用.getParentFile(),目的就是取文件前面目录的路径
dest.getParentFile().mkdirs();// 新建文件夹
}
file.transferTo(dest);// 文件写入
return "上传成功";
} catch (IllegalStateException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
return "上传失败";
}
//文件下载
@PostMapping("/download")
public void fileDownLoad(HttpServletRequest request, HttpServletResponse response) throws IOException {
String fileName= "3.jpg";
ServletContext servletContext = request.getServletContext();
String mimeType = servletContext.getMimeType("3.jpg");//告诉浏览器文件类型
System.out.println("下载的文件类型是"+mimeType);
response.setContentType(mimeType);
response.setHeader("Content-Disposition","attachment;fileName="+fileName);//告诉浏览器收到的数据要怎么处理,这里是下载
FileInputStream fileInputStream = new FileInputStream("D:\\idea_saved\\copy_used\\service\\service_trip\\src\\main\\resources\\3.jpg");
ServletOutputStream outputStream = response.getOutputStream();//获取输出流资源
IOUtils.copy(fileInputStream,outputStream);
}