写在前面 谷粒学苑刚开始是从班助那边了解到的,学完springboot就可以去做了,但是我打开的时候写着的是微服务项目,我就有点迷糊了,我还没学spring cloud呢咋就能做了捏,所以后面我就先去做了瑞吉外卖这个项目,做完之后我才发现我这个顺序应该是对的,因为瑞吉外卖真的是spring boot最好的实战项目了,也很基础很入门,很适合刚刚接触整个项目流程的小白做。而这个谷粒学苑我大致看了一下,不同于瑞吉的最大区别是全栈开发,前端后端都有,真的像一个完完全全的项目,所以可能学起来会比较吃力而且周期也会比较长。但应该来说是收获满满的。我打算按照视频那样,以天数更新blog,做完几天的内容我就跟进这个笔记直到所有代码都完成。不是说我记了几天就是只做了那几天哈哈哈,可能我现实中的好几天成果才是我记录的一天而已,我也有学校的事情,各种别的事情要做,视频里的都是一天都在做这个项目二三十天就完成了。应该来说是很长的一个项目,争取暑假之前把它敲完(虽然我暑假还有数据结构要学,要做一下取舍…)加油吧!o( ̄▽ ̄ )ブ
第一、二天(项目搭建以及讲师模块) 环境搭建 配置项 狠狠的吐槽:启动项目都花了我一天时间,都是版本惹得祸,记录一下,我换了老师给的仓库,以后记得换回来。springboot版本选择:2.2.1.RELEASE 还有数据库常见的问题,新版mysql我已经不知道踩了多少坑了,记录一下.新版必须加上useSSL=false!!! springboot中application.properties的配置项
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 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&useSSL=false spring.datasource.username =root spring.datasource.password =74922423zjn 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
代码生成器 记录一下代码生成器的使用,以后可以直接套用
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 package com.atguigu.demo;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 (); String projectPath = System.getProperty("user.dir" ); gc.setOutputDir("D:\\Codeidea\\guli_parent\\guli_parent\\service\\service_edu" + "/src/main/java" ); gc.setAuthor("zjn" ); 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&useSSL=false" ); dsc.setDriverName("com.mysql.cj.jdbc.Driver" ); dsc.setUsername("root" ); dsc.setPassword("74922423zjn" ); 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(); } }
讲师模块 讲师类(entity包) 没什么好说的,代码生成器生成的,注意的是逻辑删除(即不是真的从库中删除而是根据0或1来判断) @TableField(fill = FieldFill.INSERT)每次自动写入添加时间(mp yyds!)
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 @Data @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) 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; }
mapper&&service包 一般来说很多方法需要写在service包下,然后在controller中调用,但现在代码量少,先全部写在controller中后面再调整 有些sql MybaitPlus实现不了就需要在mapper中自己写sql语句,后面写了再添加
controller包 业务层肯定都是细节 重点:多条件组合查询(动态sql)
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 package com.atguigu.eduservice.controller;import com.atguigu.commonutils.R;import com.atguigu.eduservice.entity.EduTeacher;import com.atguigu.eduservice.entity.vo.TeacherQuery;import com.atguigu.eduservice.service.EduTeacherService;import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;import com.baomidou.mybatisplus.core.metadata.IPage;import com.baomidou.mybatisplus.extension.plugins.pagination.Page;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.util.StringUtils;import org.springframework.web.bind.annotation.*;import java.util.List;@Api("讲师管理") @RestController @RequestMapping("/eduservice/teacher") public class EduTeacherController { @Autowired private EduTeacherService teacherService; @ApiOperation("所有讲师列表") @GetMapping("/findAll") public R findAllTeacher () { List<EduTeacher> list = teacherService.list(null ); return R.ok().data("items" , list); } @ApiOperation("逻辑删除讲师") @DeleteMapping("{id}") public R removeTeacher (@ApiParam(name = "id", value = "讲师ID", required = true) @PathVariable String id) { boolean flag = teacherService.removeById(id); return flag ? R.ok() : R.error(); } @ApiOperation("分页查询讲师") @GetMapping("pageTeacher/{current}/{limit}") public R pageListTeacher (@PathVariable long current, @PathVariable long limit) { Page<EduTeacher> pageTeacher = new Page <>(current, limit); IPage<EduTeacher> page = teacherService.page(pageTeacher, null ); long total = pageTeacher.getTotal(); List<EduTeacher> records = pageTeacher.getRecords(); return R.ok().data("total" , total).data("records" , records); } @ApiOperation("多条件分页查询") @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 <>(); 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); } teacherService.page(pageTeacher, queryWrapper); long total = pageTeacher.getTotal(); List<EduTeacher> records = pageTeacher.getRecords(); return R.ok().data("total" , total).data("records" , records); } @ApiOperation("添加讲师") @PostMapping("addTeacher") public R addTeacher (@RequestBody EduTeacher eduTeacher) { boolean flag = teacherService.save(eduTeacher); return flag ? R.ok() : R.error(); } @ApiOperation("根据id查询讲师") @GetMapping("getTeacher/{id}") public R getTeacher (@PathVariable String id) { EduTeacher eduTeacher = teacherService.getById(id); return R.ok().data("teacher" , eduTeacher); } @ApiOperation("修改讲师信息") @PutMapping("updateTeacher") public R updateTeacher (@RequestBody EduTeacher eduTeacher) { boolean flag = teacherService.updateById(eduTeacher); return flag ? R.ok() : R.error(); } }
配置类(config) 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 package com.atguigu.eduservice.config;import com.baomidou.mybatisplus.core.injector.ISqlInjector;import com.baomidou.mybatisplus.extension.injector.LogicSqlInjector;import com.baomidou.mybatisplus.extension.plugins.PaginationInterceptor;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 (); } @Bean public PaginationInterceptor paginationInterceptor () { return new PaginationInterceptor (); } }
Swagger(Api接口文档) 使用之前必须添加swagger依赖
1 2 3 4 5 6 7 <!--swagger 我这里写的是一般的依赖项,项目中版本被父类管理了--> <dependency> <groupId>io.springfox</groupId> <artifactId>springfox-swagger2</artifactId> <version>2.7.0</version> </dependency>
接着就是配置类,里面的内容根据实际可修改
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 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 ("SunnyHerry" , "http://atguigu.com" , "1585592275@qq.com" )) .build(); } }
其它功能 自动写入时间 mp YYDS!!!
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 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); } }
全局异常处理 第三天会有自定义异常处理以及特定异常处理
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 package com.atguigu.servicebase.exceptionhandler;import com.atguigu.commonutils.R;import org.springframework.web.bind.annotation.ExceptionHandler;import org.springframework.web.bind.annotation.RestControllerAdvice;@RestControllerAdvice public class GlobalExceptionHandler { @ExceptionHandler(Exception.class) public R error (Exception e) { e.printStackTrace(); return R.error().message("执行了全局异常处理.." ); } }
前后端统一返回类(R) 首先定义一个静态状态码接口
1 2 3 4 5 6 package com.atguigu.commonutils;public interface ResultCode { public static Integer SUCCESS = 20000 ; public static Integer ERROR = 20001 ; }
然后就是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 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 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 ; } }
一二天总结 还是有很多点不足的地方,虽然就在刚开始的时候启动类报错找了很久,但是后面还是比较顺利的。总体来说刚开始的项目搭建和瑞吉比较大的区别就是整个项目的模块、包、类分的特别多。我不知道实际开发中是不是分着这么多,刚开始挺乱的,又要分清楚每个模块的含义还要记住哪些包放在哪个位置,还有很多maven要导不同的依赖还要关注依赖的引用关系,防止重复。可能就是对整个项目的架构还不太熟悉吧,讲师类基本上就是和瑞吉大概一致,特点就是用Swagger测试,还挺好用的。整理完笔记之后更清晰了,下几天好像就是学前端了,妈啊前端都要自己手敲了,真就全栈呗哈哈哈。下次见!