前后端分离开发
- 前端
 html, css, js, jq
 主要作用:数据显示
 ajax
- 后端
 controller service mapper
 主要作用:返回数据或操作数据
 接口
讲师管理模块(后端)
准备工作
创建数据库,创建讲师数据库表
CREATE TABLE `edu_teacher` (
  `id` char(19) NOT NULL COMMENT '讲师ID',
  `name` varchar(20) NOT NULL COMMENT '讲师姓名',
  `intro` varchar(500) NOT NULL DEFAULT '' COMMENT '讲师简介',
  `career` varchar(500) DEFAULT NULL COMMENT '讲师资历,一句话说明讲师',
  `level` int(10) unsigned NOT NULL COMMENT '头衔 1高级讲师 2首席讲师',
  `avatar` varchar(255) DEFAULT NULL COMMENT '讲师头像',
  `sort` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '排序',
  `is_deleted` tinyint(1) unsigned NOT NULL DEFAULT '0' COMMENT '逻辑删除 1(true)已删除, 0(false)未删除',
  `gmt_create` datetime NOT NULL COMMENT '创建时间',
  `gmt_modified` datetime NOT NULL COMMENT '更新时间',
  PRIMARY KEY (`id`),
  UNIQUE KEY `uk_name` (`name`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='讲师';
- 创建父工程
 pom类型,管理版本依赖和公共依赖 springboot工程- 子模块1 maven工程 
    - 子子模块1 maven工程
- 子子模块2
 
- 子模块2
 
- 子模块1 maven工程 
    
创建项目结构
创建父工程,在pom.xml里面加上一个标签,将它修改成pom工程

把dependiences删掉,放在子模块中,父工程只管理版本
 添加下面的标签确定依赖版本
<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.3</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>
    <swagger-version>2.0.1</swagger-version>
    <project-version>0.0.1-SNAPSHOT</project-version>
</properties>
配置<dependencyManagement>锁定依赖的版本
<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>
        <!--aliyunOSS-->
        <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>
        <!--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>
        <!--aliyun-->
        <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>
        <dependency>
            <groupId>com.github.xiaoymin</groupId>
            <artifactId>knife4j-spring-boot-starter</artifactId>
            <version>${swagger-version}</version>
        </dependency>
        <dependency>
            <groupId>com.jack</groupId>
            <artifactId>service</artifactId>
            <version>${project-version}</version>
        </dependency>
        <dependency>
            <groupId>com.liuscoding</groupId>
            <artifactId>common_utils</artifactId>
            <version>${project-version}</version>
        </dependency>
    </dependencies>
</dependencyManagement>
创建子模块

同样创建之后添加packing标签,导入依赖
<dependencies>
    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-netflix-ribbon</artifactId>
    </dependency>
    <!--hystrix依赖,主要是用  @HystrixCommand -->
    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
    </dependency>
    <!--服务注册-->
    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
    </dependency>
    <!--服务调用-->
    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-openfeign</artifactId>
    </dependency>
    <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>com.jack</groupId>
        <artifactId>service</artifactId>
    </dependency>
    <dependency>
        <groupId>junit</groupId>
        <artifactId>junit</artifactId>
        <version>4.12</version>
    </dependency>
</dependencies>
创建子子模块

讲师管理模块配置

创建application.properties
# 服务端口
server.port=8001
# 服务名
spring.application.name=service_edu
# 环境配置: dev, test, prod
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=xxxxx
# mybatis日志
mybatis-plus.configuration.log-impl=org.apache.ibatis.logging.stdout.StdOutImpl
编写controller service mapper代码内容
mp提供了代码生成器,生成相关代码
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(projectPath + "/src/main/java");
        gc.setAuthor("testjava");
        gc.setOpen(false); //生成后是否打开资源管理器
        gc.setFileOverride(false); //重新生成时文件是否覆盖
        gc.setServiceName("%sService");	//去掉Service接口的首字母I
        gc.setIdType(IdType.ID_WORKER); //主键策略
        gc.setDateType(DateType.ONLY_DATE);//定义生成的实体类中日期类型
        gc.setSwagger2(true);//开启Swagger2模式
        mpg.setGlobalConfig(gc);
        // 3、数据源配置
        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("020406");
        dsc.setDbType(DbType.MYSQL);
        mpg.setDataSource(dsc);
        // 4、包配置
        PackageConfig pc = new PackageConfig();
        pc.setParent("com.jack");
        pc.setModuleName("eduservice"); //模块名
        pc.setController("controller");
        pc.setEntity("entity");
        pc.setService("service");
        pc.setMapper("mapper");
        mpg.setPackageInfo(pc);
        // 5、策略配置
        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); // lombok 模型 @Accessors(chain = true) setter链式操作
        strategy.setRestControllerStyle(true); //restful api风格控制器
        strategy.setControllerMappingHyphenStyle(true); //url中驼峰转连字符
        mpg.setStrategy(strategy);
        // 6、执行
        mpg.execute();
    }
}
生成出来的结果

测试一下
- 编写Controller
@RestController
@RequestMapping("/eduservice/teacher")
public class EduTeacherController {
    @Autowired
    EduTeacherService eduTeacherService;
    // 1. 查询讲师表所有数据
    @GetMapping("findAll")
    public List<EduTeacher> findAllTeacher() {
        // 调用service的方法实现查询所有的操作
        List<EduTeacher> list = eduTeacherService.list(null);
        return list;
    }
}
-  创建启动类 @SpringBootApplication public class EduApplication { public static void main(String[] args) { SpringApplication.run(EduApplication.class, args); } }
-  新建配置类,添加mapperscan注解 @Configuration @MapperScan("com.jack.eduservice.mapper") public class EduConfig { }
-  给Mapper加上 @Mapper注解
-  设置日期格式 # 设置日期格式 spring.jackson.date-format=yyyy-MM-dd HH:mm:ss spring.jackson.time-zone=GMT+8
-  测试 
讲师逻辑删除功能
-  在配置类里配置逻辑删除插件 @Bean public ISqlInjector sqlInjector() { return new LogicSqlInjector(); }
-  在实体类属性上加上注解 @ApiModelProperty(value = "逻辑删除 1(true)已删除, 0(false)未删除") @TableLogic private Integer isDeleted;
-  编写Controller方法 @DeleteMapping("{id}") public boolean removeTeacher(@PathVariable Integer id) { return eduTeacherService.removeById(id); }
-  如何测试 
 delete提交没有办法通过浏览器直接提交
 借助一些工具进行测试: 1. swagger测试(重点) 2. postman(了解)
整合swagger

- 生成在线接口文档
- 方便接口测试
创建一个公共模块
整合swagger,为了所有模块都能用
创建子模块common,再创建service_base模块,新建配置类
@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();
    }
}
具体使用
在service_edu中引入service_base依赖
 还需要在启动类配置扫描组件,以便Swagger的配置类生效
@SpringBootApplication
@ComponentScan(basePackages = {"com.jack"})
public class EduApplication {
    public static void main(String[] args) {
        SpringApplication.run(EduApplication.class, args);
    }
}
访问swagger
http://localhost:8001/swagger-ui.html
可以在Controller上添加注解,增强swagger文档的可读性

统一返回数据格式
-  在common模块创建子模块 common_utils 
-  创建interface,定义数据返回状态码 - 成功 20000
- 失败 20001
 public interface ResultCode { public static Integer SUCCESS = 20000; public static Integer ERROR = 20001; }
-  创建结果类 package com.jack.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<>(); // 把构造方法私有化 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; } }
-  使用统一结果 - 先在Service模块引入依赖
 <dependency> <groupId>com.jack</groupId> <artifactId>common_utils</artifactId> </dependency>-  将Controller中的返回值都改为R类型 @Api(description = "讲师管理") @RestController @RequestMapping("/eduservice/teacher") public class EduTeacherController { @Autowired EduTeacherService eduTeacherService; // 1. 查询讲师表所有数据 @ApiOperation(value = "所有讲师列表") @GetMapping("findAll") public R findAllTeacher() { // 调用service的方法实现查询所有的操作 List<EduTeacher> list = eduTeacherService.list(null); return R.ok().data("list", list); } @ApiOperation(value = "根据ID删除讲师") @DeleteMapping("{id}") public R removeTeacher(@PathVariable String id) { boolean flag = eduTeacherService.removeById(id); if(flag) { return R.ok(); } else { return R.error(); } } }
 
讲师分页功能
-  配置分页插件 @Bean public PaginationInterceptor paginationInterceptor() { return new PaginationInterceptor(); }
-  编写讲师分页查询方法 // 分页查询讲师方法 @GetMapping("pageTeacher/{current}/{limit}") public R pageListTeacher(@PathVariable("current")Long current, @PathVariable("limit") Long limit) { // 创建page对象 Page<EduTeacher> page = new Page<>(current, limit); // 调用方法实现分页 // 调用方法的时候,底层封装,把分页的数据封装到page对象里面 eduTeacherService.page(page, null); long total = page.getTotal(); // 总记录数 List<EduTeacher> records = page.getRecords(); /* Map map = new HashMap(); map.put("total", total); map.put("rows", records); return R.ok().data(map);*/ return R.ok().data("total", total).data("rows", records); }
多条件组合分页查询

-  把条件值传递到接口里面 
 把条件值封装到对象里面,把对象传递到接口里面 (VO对象)
 创建VO类@Data @ApiModel("教师查询对象") public class TeacherQuery { @ApiModelProperty("教师名称,模糊查询") private String name; @ApiModelProperty("头衔 1 高级讲师 2 首席讲师") private Integer level; @ApiModelProperty(value = "查询开始时间",example = "2020-01-01 10:10:10") /** * 注意,这里使用的是String类型,前端传过来的数据无需进行类型转换。 */ private String begin; @ApiModelProperty(value = "查询结束时间",example = "2020-04-01 10:10:10") private String end; }
-  根据条件值进行判断,拼接条件 @ApiOperation("多条件组合查询带分页") @GetMapping("pageTeacherCondition/{current}/{limit}") public R pageTeacherCondition(@PathVariable long current, @PathVariable long limit, TeacherQuery teacherQuery) { Page<EduTeacher> page = new Page<>(current, limit); // 构建条件 QueryWrapper<EduTeacher> queryWrapper = new QueryWrapper<>(); 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_create", end); } eduTeacherService.page(page, queryWrapper); long total = page.getTotal(); // 总记录数 List<EduTeacher> records = page.getRecords(); // list return R.ok().data("total", total).data("rows", records); }
讲师添加
-  配置字段自动填充 
-  编写Controller @ApiOperation("添加讲师") @PostMapping("addTeacher") public R addTeacher(@RequestBody EduTeacher eduTeacher) { boolean save = eduTeacherService.save(eduTeacher); if(save) { return R.ok(); } else { return R.error(); } }
讲师修改功能
-  根据讲师id进行查询,用于回显 @ApiOperation("根据讲师id进行查询") @GetMapping("getTeacher/{id}") public R getTeacher(@PathVariable String id) { EduTeacher teacher = eduTeacherService.getById(id); return R.ok().data("teacher", teacher); }
-  讲师修改 @ApiOperation("讲师修改") @PostMapping("updateTeacher") public R updateTeacher(@RequestBody EduTeacher teacher) { boolean b = eduTeacherService.updateById(teacher); return b ? R.ok() : R.error(); }
统一异常处理
统一异常处理类
@ControllerAdvice
public class GlobalExceptionHandler {
    @ExceptionHandler(Exception.class)  // 指定出现什么异常会被处理
    @ResponseBody  // 为了能够返回数据
    public R error(Exception e) {
        e.printStackTrace();
        return R.error().message("执行了全局异常处理");
    }
}



















