开发前环境配置
- idea 2022
- Navicat Premium 15安装
- 下载和安装VS Code
- Node.js安装
- MySQL8.0安装
- JDK1.8安装
数据库设计规则
1、库名与应用名称尽量一致
2、表名、字段名必须使用小写字母或数字,禁止出现数字开头,
3、表名不使用复数名词
4、表的命名最好是加上“业务名称_表的作用”。如,edu_teacher
5、表必备三字段:id, gmt_create, gmt_modified
说明:
其中 id 必为主键,类型为 bigint unsigned、单表时自增、步长为 1。
(如果使用分库分表集群部署,则id类型为verchar,非自增,业务中使用分布式id生成器)
gmt_create, gmt_modified 的类型均为 datetime 类型,前者现在时表示主动创建,后者过去分词表示被 动更新。
6、单表行数超过 500 万行或者单表容量超过 2GB,才推荐进行分库分表。 说明:如果预计三年后的数据量根本达不到这个级别,请不要在创建表时就分库分表。
7、表达是与否概念的字段,必须使用 is_xxx 的方式命名,数据类型是 unsigned tinyint (1 表示是,0 表示否)。
说明:任何字段如果为非负数,必须是 unsigned。
注意:POJO 类中的任何布尔类型的变量,都不要加 is 前缀。数据库表示是与否的值,使用 tinyint 类型,坚持 is_xxx 的 命名方式是为了明确其取值含义与取值范围。
正例:表达逻辑删除的字段名 is_deleted,1 表示删除,0 表示未删除。
8、小数类型为 decimal,禁止使用 float 和 double。 说明:float 和 double 在存储的时候,存在精度损失的问题,很可能在值的比较时,得到不 正确的结果。如果存储的数据范围超过 decimal 的范围,建议将数据拆成整数和小数分开存储。
9、如果存储的字符串长度几乎相等,使用 char 定长字符串类型。
10、varchar 是可变长字符串,不预先分配存储空间,长度不要超过 5000,如果存储长度大于此值,定义字段类型为 text,独立出来一张表,用主键来对应,避免影响其它字段索 引效率。
11、唯一索引名为 uk_字段名;普通索引名则为 idx_字段名。
说明:uk_ 即 unique key;idx_ 即 index 的简称
12、不得使用外键与级联,一切外键概念必须在应用层解决。外键与级联更新适用于单机低并发,不适合分布式、高并发集群;级联更新是强阻塞,存在数据库更新风暴的风险;外键影响数据库的插入速度。
创建项目
项目文件夹
目前项目结构
开发流程
利用代码生成器
把 教师(teacher) 和课程(subject)表生成对应的controller
,mapper
,service
public class CodeGet {
public static void main(String[] args) {
// 1、创建代码生成器
AutoGenerator mpg = new AutoGenerator();
// 2、全局配置
// 全局配置
GlobalConfig gc = new GlobalConfig();
String projectPath = System.getProperty("user.dir");
//gc.setOutputDir(projectPath + "/src/main/java");
gc.setOutputDir("D:\\Users\\admin\\Desktop\\23学习\\zskt_parent\\service\\service_vod"+"/src/main/java");
gc.setServiceName("%sService"); //去掉Service接口的首字母I
gc.setAuthor("atguigu");
gc.setOpen(false);
mpg.setGlobalConfig(gc);
// 3、数据源配置
DataSourceConfig dsc = new DataSourceConfig();
dsc.setUrl("jdbc:mysql://localhost:3306/glkt_vod");
dsc.setDriverName("com.mysql.jdbc.Driver");
dsc.setUsername("root");
dsc.setPassword("123456");
dsc.setDbType(DbType.MYSQL);
mpg.setDataSource(dsc);
// 4、包配置
PackageConfig pc = new PackageConfig();
pc.setParent("com.example.zskt");
pc.setModuleName("vod"); //模块名
pc.setController("controller");
pc.setEntity("entity");
pc.setService("service");
pc.setMapper("mapper");
mpg.setPackageInfo(pc);
// 5、策略配置
StrategyConfig strategy = new StrategyConfig();
strategy.setInclude("subject");
strategy.setNaming(NamingStrategy.underline_to_camel);//数据库表映射到实体的命名策略
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();
}
}
定义统一返回结果对象
- 在service_utils模块创建结果类
- 代码
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
/**
* 全局统一返回结果类
*
*/
@Data
@ApiModel(value = "全局统一返回结果")
public class Result<T> {
@ApiModelProperty(value = "返回码")
private Integer code;
@ApiModelProperty(value = "返回消息")
private String message;
@ApiModelProperty(value = "返回数据")
private T data;
public Result(){}
public static <T> Result<T> build(T body, Integer code, String message) {
Result<T> result = new Result<T>();
if (body != null) {
result.setData(body);
}
result.setCode(code);
result.setMessage(message);
return result;
}
public static<T> Result<T> ok(){
return Result.ok(null);
}
/**
* 操作成功
* @param data baseCategory1List
* @param <T>
* @return
*/
public static<T> Result<T> ok(T data){
return build(data,200,"成功");
}
public static<T> Result<T> fail(){
return Result.fail(null);
}
/**
* 操作失败
* @param data
* @param <T>
* @return
*/
public static<T> Result<T> fail(T data){
return build(data, 201,"失败");
}
public Result<T> message(String msg){
this.setMessage(msg);
return this;
}
public Result<T> code(Integer code){
this.setCode(code);
return this;
}
}
编写TeacherController
package com.example.zskt.vod.controller;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.example.result.Result;
import com.example.zskt.model.vod.Teacher;
import com.example.zskt.vo.vod.TeacherQueryVo;
import com.example.zskt.vod.service.TeacherService;
import com.mysql.jdbc.StringUtils;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import java.util.List;
@Api(tags = "讲师管理接口")
@RestController
@RequestMapping(value="/admin/vod/teacher")
@CrossOrigin //跨域
public class TeacherController {
@Autowired
private TeacherService teacherService;
//http://localhost:8301/admin/vod/teacher/findAll
//1 查询所有讲师
// @ApiOperation("查询所有讲师")
// @GetMapping("findAll")
// public List<Teacher> findAllTeacher(){
// List<Teacher> list = teacherService.list();
// return list;
// }
@ApiOperation("查询所有讲师")
@GetMapping("findAll")
public Result findAllTeacher(){
List<Teacher> list = teacherService.list();
return Result.ok(list);
}
//删除讲师
@ApiOperation("逻辑删除讲师")
@DeleteMapping("remove/{id}")
public Result<Object> removeById(@PathVariable String id){
boolean isSuccerss = teacherService.removeById(id);
if(isSuccerss){
return Result.ok(null);
}
else {
return Result.fail(null);
}
}
//3.条件分页查询
@ApiOperation("条件查询分页")
@PostMapping("findQueryPage/{current}/{limit}")
public Result findPage(@PathVariable long current,
@PathVariable long limit,
@RequestBody TeacherQueryVo teacherQueryVo){
//创建page对象,传递当前页和每页记录数
Page<Teacher> pageParam = new Page<>(current, limit);
if(teacherQueryVo == null) {
IPage<Teacher> pageModel= teacherService.page(pageParam,null);
return Result.ok(pageModel);
}else{
//获取条件值
String name = teacherQueryVo.getName();//讲师名称
Integer level = teacherQueryVo.getLevel();//讲师级别
String joinDateBegin = teacherQueryVo.getJoinDateBegin();//开始时间
String joinDateEnd = teacherQueryVo.getJoinDateEnd();//结束时间
//进行非空判断,条件封装
QueryWrapper<Teacher> wrapper = new QueryWrapper<>();
if(!StringUtils.isEmptyOrWhitespaceOnly(name)){
wrapper.like("name",name);
}
if(level!=null){
wrapper.eq("name",name);
}
if(!StringUtils.isEmptyOrWhitespaceOnly(joinDateBegin)){
wrapper.ge("name",name);
}
if(!StringUtils.isEmptyOrWhitespaceOnly(joinDateEnd)){
wrapper.le("name",name);
}
//调用方法得到分页查询结果
IPage<Teacher> pageModel = teacherService.page(pageParam, wrapper);
return Result.ok(pageModel);
}
}
//4 添加讲师
@ApiOperation(value = "添加讲师")
@PostMapping("save")
public Result save(@RequestBody Teacher teacher) {
boolean isSuccess = teacherService.save(teacher);
if(isSuccess){
return Result.ok(null);
}else{
return Result.fail(null);
}
}
//5 修改讲师
@ApiOperation(value = "修改讲师")
@PutMapping("update")
public Result updateById(@RequestBody Teacher teacher) {
teacherService.updateById(teacher);
return Result.ok(null);
}
//6 修改-根据id查询
@ApiOperation("根据id查询")
@GetMapping("getTeacher/{id}")
public Result getTeacher(@PathVariable Long id) {
Teacher teacher = teacherService.getById(id);
return Result.ok(teacher);
}
//7 根据id列表删除
@ApiOperation(value = "批量删除讲师")
@DeleteMapping("batchRemove")
public Result batchRemove(@RequestBody List<Long> idList) {
teacherService.removeByIds(idList);
return Result.ok(null);
}
}
统一异常处理
package com.example.exception;
import com.example.result.Result;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;
@ControllerAdvice
public class GlobalExceptionHandler {
//全局异常处理
@ExceptionHandler(Exception.class)
@ResponseBody
public Result error(Exception e){
e.printStackTrace();
return Result.fail().message("执行了全局异常处理");
}
//处理特定异常
@ExceptionHandler(ArithmeticException.class)
@ResponseBody
public Result error(ArithmeticException e){
e.printStackTrace();
return Result.fail().message("执行了特定异常处理");
}
//自定义异常处理
@ExceptionHandler(ZSktException.class)
@ResponseBody
public Result error(ZSktException e){
e.printStackTrace();
return Result.fail().message(e.getMsg()).code(e.getCode());
}
}
配置Swagger2生成API接口文档
package com.example.swagger;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import springfox.documentation.builders.ApiInfoBuilder;
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 Swagger2Config {
@Bean
public Docket webApiConfig(){
return new Docket(DocumentationType.SWAGGER_2)
.groupName("zskt")
.apiInfo(webApiInfo())
.select()
//只显示api路径下的页面
//.paths(Predicates.and(PathSelectors.regex("/api/.*")))
.build();
}
private ApiInfo webApiInfo(){
return new ApiInfoBuilder()
.title("网站-API文档")
.description("本文档描述了网站微服务接口定义")
.version("1.0")
.contact(new Contact("atguigu", "http://baidu.com", "baidu.com"))
.build();
}
}
接口测试
整合腾讯云
- 创建工具类
/**
* 常量类,读取配置文件application.properties中的配置
*/
@Component
public class ConstantPropertiesUtil implements InitializingBean {
@Value("${tencent.cos.file.region}")
private String region;
@Value("${tencent.cos.file.secretid}")
private String secretId;
@Value("${tencent.cos.file.secretkey}")
private String secretKey;
@Value("${tencent.cos.file.bucketname}")
private String bucketName;
public static String END_POINT;
public static String ACCESS_KEY_ID;
public static String ACCESS_KEY_SECRET;
public static String BUCKET_NAME;
@Override
public void afterPropertiesSet() throws Exception {
END_POINT = region;
ACCESS_KEY_ID = secretId;
ACCESS_KEY_SECRET = secretKey;
BUCKET_NAME = bucketName;
}
}
- 编写
FileUploadController
@Api(tags = "文件上传接口")
@RestController
@RequestMapping("/admin/vod/file")
public class FileUploadController {
@Autowired
private FileService fileService;
/**
* 文件上传
*/
@ApiOperation(value = "文件上传")
@PostMapping("upload")
public Result upload(
@ApiParam(name = "file", value = "文件", required = true)
@RequestParam("file") MultipartFile file) {
String uploadUrl = fileService.upload(file);
return Result.ok(uploadUrl).message("文件上传成功");
}
}