(1)、删除 src 目录,指定pom
(2)导入依赖
org.springframework.boot
spring-boot-starter-parent
2.2.9.RELEASE
Hoxton.SR5
2.2.0.RELEASE
3.3.1
1.1.21
2.3.2
1.2.8
2.6
3.2.2
2.6
1.0.4
UTF-8
1.8
org.springframework.cloud
spring-cloud-dependencies
${spring-cloud.version}
pom
import
com.alibaba.cloud
spring-cloud-alibaba-dependencies
${cloud-alibaba.version}
pom
import
com.baomidou
mybatis-plus-boot-starter
${mybatis-plus.version}
com.alibaba
druid-spring-boot-starter
${druid.version}
com.spring4all
swagger-spring-boot-starter
1.9.1.RELEASE
com.github.penggle
kaptcha
${kaptcha.version}
com.aliyun.oss
aliyun-sdk-oss
3.8.0
com.alibaba
fastjson
${fastjson.version}
com.arronlong
httpclientutil
${httpclientutil.version}
commons-lang
commons-lang
${commons-lang.version}
commons-collections
commons-collections
${commons-collections.version}
commons-io
commons-io
${commons-io.version}
org.apache.maven.plugins
maven-compiler-plugin
${java.version}
${project.build.sourceEncoding}
org.springframework.boot
spring-boot-maven-plugin
src/main/java
**/*.xml
src/main/resources
创建子工程的工具包,如果需要注入到Spring容器中,调用,则需要和其他微服务的总包保持一致
确保被扫描到
比如我的utils工程如下
子微服务如下
(1)、导入工具类的依赖
org.springframework.boot
spring-boot-starter-web
com.baomidou
mybatis-plus-boot-starter
com.alibaba
druid-spring-boot-starter
mysql
mysql-connector-java
runtime
org.springframework.boot
spring-boot-configuration-processor
true
org.projectlombok
lombok
com.spring4all
swagger-spring-boot-starter
com.aliyun.oss
aliyun-sdk-oss
com.alibaba
fastjson
com.arronlong
httpclientutil
commons-lang
commons-lang
commons-collections
commons-collections
commons-io
commons-io
(2)、创建枚举类及统一接口进行管理工具类
package com.jhj.blog.utils.enums;
import lombok.AllArgsConstructor;
import lombok.Getter;
@Getter
@AllArgsConstructor //有参构造方法
public enum ResultEnum {
SUCCESS(20000, "成功"),
ERROR(999, "错误"),
UNAUTHENTICATED(401, "请先通过身份认证"),
AUTH_FAIL(1400, "认证失败"),
// token异常
TOKEN_PAST(1401, "身份过期,请求重新登录!"),
TOKEN_ERROR(1402, "令牌错误"),
HEADEA_ERROR(1403, "请求头错误"),
AUTH_USERNAME_NONE(1405, "用户名不能为空"),
AUTH_PASSWORD_NONE(1406, "密码不能为空"),
MENU_NO(306, "没此权限,请联系管理员!");
private Integer code;
private String desc;
}
package com.jhj.blog.utils.base;
import com.alibaba.fastjson.JSON;
import com.jhj.blog.utils.enums.ResultEnum;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.Serializable;
/**
* 用于封装接口统一响应结果
*/
@Data
@NoArgsConstructor // 无参构造方法
@AllArgsConstructor // 有参构造方法
public final class Result implements Serializable {
private static final Logger logger = LoggerFactory.getLogger(Result.class);
private static final long serialVersionUID = 1L;
/**
* 响应业务状态码
*/
private Integer code;
/**
* 响应信息
*/
private String message;
/**
* 响应中的数据
*/
private Object data;
public static Result ok() {
return new Result(ResultEnum.SUCCESS.getCode(), ResultEnum.SUCCESS.getDesc(), null);
}
public static Result ok(Object data) {
return new Result(ResultEnum.SUCCESS.getCode(), ResultEnum.SUCCESS.getDesc(), data);
}
public static Result ok(String message, Object data) {
return new Result(ResultEnum.SUCCESS.getCode(), message, data);
}
public static Result error(String message) {
logger.debug("返回错误:code={}, message={}", ResultEnum.ERROR.getCode(), message);
return new Result(ResultEnum.ERROR.getCode(), message, null);
}
public static Result build(int code, String message) {
logger.debug("返回结果:code={}, message={}", code, message);
return new Result(code, message, null);
}
public static Result build(ResultEnum resultEnum) {
logger.debug("返回结果:code={}, message={}", resultEnum.getCode(), resultEnum.getDesc());
return new Result(resultEnum.getCode(), resultEnum.getDesc(), null);
}
public String toJsonString() {
return JSON.toJSONString(this);
}
}
(3)、创建mybatisPlus的page 分页方法的封装实体类
package com.jhj.blog.utils.base;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import lombok.experimental.Accessors;
import java.io.Serializable;
/**
* 请求参数基础类、带分页参数
* @param
*/
@Accessors(chain = true)
//@Accessors(chain = true)在lomBook中级联操作,setter 方法有返回值 this ,
// 所以可以连续点Page().setCurrent(this.current).setSize(this.size)
@Data
public class BaseRequest implements Serializable {
// @ApiModelProperty(value = "页码", required = true) 注解是 Swagger 提供的,用于生成接口文档用的,
@ApiModelProperty(value = "页码", required = true)
private long current;
@ApiModelProperty(value = "每页显示多少条", required = true)
private long size;
/**
* 封装分页对象
* @return
*/
@ApiModelProperty(hidden = true) // hidden = true 不在swagger接口文档中显示
public IPage getPage() {
// Page tPage = new Page<>(this.current, this.size);相当于这样吧
return new Page().setCurrent(this.current).setSize(this.size);
}
}
(4)在resourse下面创建 日志配置文件logback.xml
${CONSOLE_LOG_PATTERN}
(1)、添加依赖
第一个工具类的依赖,第二个feign的依赖,后期做接口统一调度
jhj-blog
jhj-blog-utils
1.0-SNAPSHOT
org.springframework.cloud
spring-cloud-starter-openfeign
(2)现在针对 mxg_category表做个测试
在jhj-blog-api子项目中添加该表的实体类
package com.jhj.blog.entities;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import lombok.EqualsAndHashCode;
import java.io.Serializable;
import java.util.Date;
/**
* @program: jhj-blog
* @ClassName Category
* @description:
* @author:蒋皓洁
* @create: 2021-12-07 16:55
* @Version 1.0
**/
@ApiModel(value = "Category对象", description = "类别表描述")
@Data // lombok 注解,生成 setter,getter等
@TableName("mxg_category") // mybatis-plus注解,对应表名
public class Category implements Serializable {
private static final long serialVersionUID = 1L;
/**
* 主键,分布式id
*/
@ApiModelProperty(value = "主键")
@TableId(value = "id", type = IdType.ASSIGN_ID)
private String id;
/**
* 分类名称
*/
@ApiModelProperty(value = "分类名称")
private String name;
/**
* 备注
*/
@ApiModelProperty(value = "备注")
private String remark;
/**
* 状态(1:正常,0:禁用)
*/
@ApiModelProperty(value = "状态(1:正常,0:禁用)")
private Integer status;
/**
* 排序
*/
@ApiModelProperty(value = "排序")
private Integer sort;
/**
* 创建时间
*/
@ApiModelProperty(value = "创建时间")
private Date createDate;
/**
* 更新时间
*/
@ApiModelProperty(value = "更新时间")
private Date updateDate;
/**
*
* 标签表
*
*
* @author jiangHaoJie
* @since 2021-12-08
*/
}
(1)、添加pom依赖
添加了jhj-blog-api依赖 因为jhj-blog-api依赖中包含jhj-blog-utils依赖,所以不用引入
jhj-blog
jhj-blog-api
1.0-SNAPSHOT
org.springframework.boot
spring-boot-starter-test
test
org.springframework.boot
spring-boot-devtools
org.springframework.boot
spring-boot-maven-plugin
(2)、添加application.yml
swagger:
title: 博客系统接口文档
description: 博客系统-分类&标签&文章&广告管理接口
exclude-path: /error # 剔除Springboot自定义的error请求
server:
port: 8001
servlet:
context-path: /article # 上下文件路径,请求前缀 ip:port/article
spring:
application:
name: article-server # 应用名
# 数据源配置
datasource:
username: root
password: root
url: jdbc:mysql://127.0.0.1:3306/mxg_blog_article?useUnicode=true&characterEncoding=utf8&useSSL=false&serverTimezone=GMT%2B8&allowMultiQueries=true
#mysql8版本以上驱动包指定新的驱动类
driver-class-name: com.mysql.cj.jdbc.Driver
# 数据源其他配置, 在 DruidConfig配置类中手动绑定
initialSize: 8
minIdle: 5
maxActive: 20
maxWait: 60000
timeBetweenEvictionRunsMillis: 60000
minEvictableIdleTimeMillis: 300000
validationQuery: SELECT 1 FROM DUAL
mybatis-plus:
type-aliases-package: com.jhj.blog.entities
# xxxMapper.xml 路径
mapper-locations: classpath*:com/jhj/blog/article/mapper/**/*.xml
# 日志级别,会打印sql语句
logging:
level:
com.jhj.blog.article.mapper: debug
其中 下面这个表示配置swagger的统一配置项目的标题等,去除springboot中自带的error接口
swagger:
title: 博客系统接口文档
description: 博客系统-分类&标签&文章&广告管理接口
exclude-path: /error # 剔除Springboot自定义的error请求
配置mybatisPlus的实体类路径,和mapper的扫描路径
mybatis-plus:
type-aliases-package: com.jhj.blog.entities
# xxxMapper.xml 路径
mapper-locations: classpath*:com/jhj/blog/article/mapper/**/*.xml
配置日志打印SQL的mapper路径
logging:
level:
com.jhj.blog.article.mapper: debug
(3)配置mybatisPlus分页配置
@MapperScan("com.jhj.blog.article.mapper")//扫描指定的Map接口
@Configuration//标注配置类
@EnableTransactionManagement //开启事务管理
package com.jhj.blog.article.config;
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;
import org.springframework.transaction.annotation.EnableTransactionManagement;
/**
* @program: jhj-blog
* @ClassName MybatisPlusConfig
* @description:
* @author:蒋皓洁
* @create: 2021-12-08 15:00
* @Version 1.0
**/
@MapperScan("com.jhj.blog.article.mapper")//扫描Map接口
@Configuration//标注配置类
@EnableTransactionManagement //开启事务管理
public class MybatisPlusConfig {
/***
* 分页插件
* @return
* 添加Bean注解,添加到容器中
*/
@Bean
public PaginationInterceptor paginationInterceptor() {
return new PaginationInterceptor();
}
}
(4) mxg_category表的mapper层
package com.jhj.blog.article.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.jhj.blog.entities.Category;
/**
*
* 文章分类表 Mapper 接口
*
* @author
*/
public interface CategoryMapper extends BaseMapper {
}
(5)server层
package com.jhj.blog.article.service;
import com.baomidou.mybatisplus.extension.service.IService;
import com.jhj.blog.article.req.CategoryREQ;
import com.jhj.blog.entities.Category;
import com.jhj.blog.utils.base.Result;
/**
* 文章分类
*/
public interface ICategoryService extends IService {
//分页条件查询分类信息
Result queryPage(CategoryREQ req);
/**
* 获取所有正常状态的分类 status==1
* findAllNormal
*/
Result findAllNormal();
}
package com.jhj.blog.article.service.impl;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.jhj.blog.article.mapper.CategoryMapper;
import com.jhj.blog.article.req.CategoryREQ;
import com.jhj.blog.article.service.ICategoryService;
import com.jhj.blog.entities.Category;
import com.jhj.blog.utils.base.Result;
import org.apache.commons.lang.StringUtils;
import org.springframework.stereotype.Service;
import java.util.Date;
/**
* @program: jhj-blog
* @ClassName CategoryServiceImpl
* @description:
* @author:蒋皓洁
* @create: 2021-12-08 14:40
* @Version 1.0
* 继承ServiceImpl传入mapper,和实体entity
**/
@Service
public class CategoryServiceImpl extends ServiceImpl implements ICategoryService {
@Override
public Result queryPage(CategoryREQ req) {
QueryWrapper categoryQueryWrapper = new QueryWrapper<>();
//分类名称
if (StringUtils.isNotEmpty(req.getName())) {
categoryQueryWrapper.like("name", req.getName());
}
//分类状态
if (req.getStatus() != null) {
categoryQueryWrapper.eq("status", req.getStatus());
}
// status 1正常 0 异常,降序排列(由高到底)orderByDesc,再对sort进行升序排列
categoryQueryWrapper.orderByDesc("status").orderByAsc("sort");
IPage categoryIPage = baseMapper.selectPage(req.getPage(), categoryQueryWrapper);
return Result.ok(categoryIPage);
}
@Override
public Result findAllNormal() {
QueryWrapper wrapper = new QueryWrapper<>();
wrapper.eq("status", 1); // 1 正常,0 禁用
return Result.ok(baseMapper.selectList(wrapper));
}
@Override
public boolean updateById(Category entity) {
entity.setUpdateDate(new Date());
// super调用父类的实现方法
return super.updateById(entity);
}
}
重写了父类的 updateById的方法,将修改时候,设置最新时间,再返回父类的super.updateById(entity);方法
@Override
public boolean updateById(Category entity) {
entity.setUpdateDate(new Date());
// super调用父类的实现方法
return super.updateById(entity);
}
(6)controller层
package com.jhj.blog.article.controller;
import com.jhj.blog.article.req.CategoryREQ;
import com.jhj.blog.article.service.ICategoryService;
import com.jhj.blog.entities.Category;
import com.jhj.blog.utils.base.Result;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiImplicitParam;
import io.swagger.annotations.ApiOperation;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.*;
/**
* @program: jhj-blog
* @ClassName CategoryController
* @description:
* @author:蒋皓洁
* @create: 2021-12-08 15:05
* @Version 1.0
**/
@Api(value = "分类管理接口", description = "显示分类管理的增删改查")
@RestController//所有的方法都会返回Json字符串进行相应
@RequestMapping("/category")
public class CategoryController {
@Autowired
private ICategoryService iCategoryService;
@ApiOperation("根据分类名称及状态查询分类列表接口")
@PostMapping("/search")
public Result search(@RequestBody CategoryREQ req) {
return iCategoryService.queryPage(req);
}
@ApiOperation("根据ID查询分类接口")
@ApiImplicitParam(name = "id", value = "类别ID", required = true)
@GetMapping("/{id}")
public Result view(@PathVariable("id") String id) {
Category byId = iCategoryService.getById(id);
return Result.ok(byId);
}
@ApiOperation("更新分类信息")
@PutMapping
public Result update(@RequestBody Category category) {
iCategoryService.updateById(category);
return Result.ok();
}
@ApiOperation("新增分类信息接口")
@PostMapping
public Result save(@RequestBody Category category) {
boolean save = iCategoryService.save(category);
if (save) {
return Result.ok();
} else {
return Result.error("更新失败");
}
}
@ApiOperation("删除分类信息接口")
@ApiImplicitParam(name = "id", value = "类别ID", required = true)
@DeleteMapping("/{id}")
public Result delete(@PathVariable("id") String id) {
boolean b = iCategoryService.removeById(id);
if (b) {
return Result.ok();
} else {
return Result.error("删除失败");
}
}
/**
* 查询所有正常的类别
* @return
*/
@ApiOperation("获取所有正常状态的分类接口")
@GetMapping("/list")
public Result list() {
return iCategoryService.findAllNormal();
}
}
1、依赖
因为前面父Pom文件加入了下面这个依赖,所以在子工程(jhj-blog-article)不用添加其他依赖
com.spring4all
swagger-spring-boot-starter
2、启动类添加 @EnableSwagger2Doc 注解
@SpringBootApplication
@EnableSwagger2Doc
public class ArticleApplication {
public static void main(String[] args) {
SpringApplication.run(ArticleApplication.class, args);
}
}
3、请求实体类注解
@ApiModel:用在请求参数是对象上,描述该对象类的作用
@ApiModelProperty:用在请求参数是对象的属性上,描述对象属性的作用。 value:属性的描述 hidden:是否是查询条件属性, false:(默认值)在api文档显示,作为查询条件;true 隐藏,不是条件属性
@Data
@Accessors(chain = true)
//swagger的注解
@ApiModel(value = "CategoryREQ条件对象", description = "类别查询条件")
public class CategoryREQ extends BaseRequest {
/**
* 分类名称
*/
@ApiModelProperty(value = "分类名称")
private String name;
/**
* 状态(1:正常,0:禁用)
*/
@ApiModelProperty(value = "状态")
private Integer status;
}
4、接口注解
(1)接口类注解
@Api:用在 Controller 类上,描述该类的作用,注解参数: value="简要说明" description="详细描述该类的作用"
@Api(value = "分类管理接口", description = "显示分类管理的增删改查")
@RestController//所有的方法都会返回Json字符串进行相应
@RequestMapping("/category")
public class CategoryController {
(2)方法上注解
@ApiOperation:用在 Controller 请求方法上,描述方法的作用。
@ApiOperation("根据分类名称及状态查询分类列表接口")
@PostMapping("/search")
public Result search(@RequestBody CategoryREQ req) {
return iCategoryService.queryPage(req);
}
@ApiImplicitParams:用在请求方法上,对多个请求参数增加描述。
@ApiImplicitParam:可单独使用,或在 @ApiImplicitParams 中使用,给方法的一个请求参数增加描述。
name:参数名
value:描述参数的作用
dataType:参数类型,参数类型,默认String,其它值 dataType="Integer"
defaultValue:参数默认值
required:参数是否必传(true/false)
paramTpye:指定参数放在哪些地方(header/query/path/body/form)
header :参数在request headers 里边提交@RequestHeader
query :直接跟参数完成自动映射赋值 @RequestParam
path :以路径变量的形式提交数据 @PathVariable
body :以流的形式提交 仅支持POST(不常用) form :以form表单的形式提交 仅支持POST (不常用)
@ApiOperation("根据ID查询分类接口")
@ApiImplicitParam(name = "id", value = "类别ID", required = true)
@GetMapping("/{id}")
public Result view(@PathVariable("id") String id) {
Category byId = iCategoryService.getById(id);
return Result.ok(byId);
}
// 请求方法有多个请求参数 size, current
@ApiImplicitParams({
@ApiImplicitParam(name="current", value="页码", required=true, paramType="path",
dataType="int"),
@ApiImplicitParam(name="size", value="每页记录数", required=true, paramType="path",
dataType="int")
})
@ApiOperation("根据分类名称与状态查询分类列表接口")
@PostMapping("/search/{current}/{size}")
Result search(@RequestBody CategoryREQ req,
@PathVariable int current, @PathVariable int size);
5、启动项目
访问地址:http://127.0.0.1:8001/article/swagger-ui.html
以新增接口为例
@ApiOperation("新增分类信息接口")
@PostMapping
public Result save(@RequestBody Category category) {
boolean save = iCategoryService.save(category);
if (save) {
return Result.ok();
} else {
return Result.error("更新失败");
}
}
查看传入对象字段属性
1、添加依赖,(1)、工具类依赖,(2)、mybatisPlus代码生成器依赖 (3)、模本引擎依赖
jhj-blog
jhj-blog-utils
1.0-SNAPSHOT
com.baomidou
mybatis-plus-generator
3.3.2
org.freemarker
freemarker
2、添加代码生成器
package com.jhj.generator;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.core.exceptions.MybatisPlusException;
import com.baomidou.mybatisplus.core.toolkit.StringUtils;
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 com.baomidou.mybatisplus.generator.engine.FreemarkerTemplateEngine;
import java.util.Scanner;
// 执行 main 方法控制台输入模块表名回车自动生成对应项目目录中
public class CodeGenerator {
// 生成的代码放到哪个工程中
private static String PROJECT_NAME = "jhj-blog-article";
// 数据库名称
private static String DATABASE_NAME = "mxg_blog_article";
// 子包名
private static String MODULE_NAME = "article";
public static void main(String[] args) {
// 代码生成器
AutoGenerator mpg = new AutoGenerator();
// 数据源配置
DataSourceConfig dsc = new DataSourceConfig();
dsc.setUrl("jdbc:mysql://localhost:3306/"+ DATABASE_NAME +"?useUnicode=true&characterEncoding=utf8&useSSL=false&serverTimezone=GMT%2B8");
dsc.setDriverName("com.mysql.cj.jdbc.Driver");
dsc.setUsername("root");
dsc.setPassword("root");
mpg.setDataSource(dsc);
// 全局配置
GlobalConfig gc = new GlobalConfig();
String projectPath = System.getProperty("user.dir") + "/";
gc.setOutputDir(projectPath + PROJECT_NAME +"/src/main/java");
gc.setIdType(IdType.ASSIGN_ID); // 分布式id
gc.setAuthor("jiangHaoJie");
gc.setFileOverride(true); //覆盖现有的,覆盖代码
gc.setOpen(false); //是否生成后打开
gc.setDateType(DateType.ONLY_DATE);
gc.setSwagger2(true); //实体属性 Swagger2 注解
mpg.setGlobalConfig(gc);
// 包配置
PackageConfig pc = new PackageConfig();
pc.setParent("com.jhj.blog"); //父包名
pc.setController(MODULE_NAME+".controller"); // com.mengxuegu.blog.aritcle.controller
pc.setService(MODULE_NAME+".service");
pc.setServiceImpl(MODULE_NAME+".service.impl");
pc.setMapper(MODULE_NAME+".mapper");
pc.setXml(MODULE_NAME+".mapper.xml");
pc.setEntity("entities");//实体类存储包名 com.mengxuegu.blog.entities
mpg.setPackageInfo(pc);
// 策略配置
StrategyConfig strategy = new StrategyConfig();
strategy.setNaming(NamingStrategy.underline_to_camel);
strategy.setColumnNaming(NamingStrategy.underline_to_camel);
strategy.setEntityLombokModel(true); //使用lombok
strategy.setEntitySerialVersionUID(true);// 实体类的实现接口Serializable
strategy.setRestControllerStyle(true); // @RestController
strategy.setInclude(scanner("表名,多个英文逗号分割").split(","));
strategy.setControllerMappingHyphenStyle(true);
strategy.setTablePrefix("mxg_"); // 去掉表前缀
mpg.setStrategy(strategy);
mpg.setTemplateEngine(new FreemarkerTemplateEngine());
mpg.execute();
}
/**
*
* 读取控制台内容
*
*/
public static String scanner(String tip) {
Scanner scanner = new Scanner(System.in);
StringBuilder help = new StringBuilder();
help.append("请输入" + tip + ":");
System.out.println(help.toString());
if (scanner.hasNext()) {
String ipt = scanner.next();
if (StringUtils.isNotBlank(ipt)) {
return ipt;
}
}
throw new MybatisPlusException("请输入正确的" + tip + "!");
}
}
3、点击运行