员工管理、API文档
API文档Knife4j
相信无论是前端还是后端开发,都或多或少地被接口文档折磨过。前端经常抱怨后端给的接口文档与实际情况不一致。后端又觉得编写及维护接口文档会耗费不少精力,经常来不及更新。其实无论是前端调用后端,还是后端调用后端,都期望有一个好的接口文档。但是这个接口文档对于程序员来说,就跟注释一样,经常会抱怨别人写的代码没有写注释,然而自己写起代码起来,最讨厌的,也是写注释。所以仅仅只通过强制来规范大家是不够的,随着时间推移,版本迭代,接口文档往往很容易就跟不上代码了。
现在的项目很多是敏捷开发,需求变化也快,导致接口文档不断变化。使用静态接口文档不利于更新,推荐是使用动态API文档(html网页格式)。
动态API文档可以由前端人员编写(在线工具),也可以后端“编写”(生成)。
API文档的技术,流行:swagger、spring doc(OpenAPI3)、Knife4j。
Knife4j是一个集Swagger2 和 OpenAPI3为一体的增强解决方案,Knife4j是一个集Swagger2 和 OpenAPI3,为一体的增强解决方案
官网:https://doc.xiaominfo.com/
下面要在后端项目中添加Knife4j:
https://doc.xiaominfo.com/docs/quick-start
前提条件:
员工管理、API文档
API文档Knife4j
相信无论是前端还是后端开发,都或多或少地被接口文档折磨过。前端经常抱怨后端给的接口文档与实际情况不一致。后端又觉得编写及维护接口文档会耗费不少精力,经常来不及更新。其实无论是前端调用后端,还是后端调用后端,都期望有一个好的接口文档。但是这个接口文档对于程序员来说,就跟注释一样,经常会抱怨别人写的代码没有写注释,然而自己写起代码起来,最讨厌的,也是写注释。所以仅仅只通过强制来规范大家是不够的,随着时间推移,版本迭代,接口文档往往很容易就跟不上代码了。
现在的项目很多是敏捷开发,需求变化也快,导致接口文档不断变化。使用静态接口文档不利于更新,推荐是使用动态API文档(html网页格式)。
动态API文档可以由前端人员编写(在线工具),也可以后端“编写”(生成)。
API文档的技术,流行:swagger、spring doc(OpenAPI3)、Knife4j。
Knife4j是一个集Swagger2 和 OpenAPI3为一体的增强解决方案,Knife4j是一个集Swagger2 和 OpenAPI3,为一体的增强解决方案
官网:https://doc.xiaominfo.com/
下面要在后端项目中添加Knife4j:
https://doc.xiaominfo.com/docs/quick-start
前提条件:
首先,引用Knife4j的starter,Maven坐标如下:
<dependency>
<groupId>com.github.xiaoymin</groupId>
<artifactId>knife4j-openapi3-jakarta-spring-boot-starter</artifactId>
<version>4.1.0</version>
</dependency>
引入之后,其余的配置,开发者即可完全参考springdoc-openapi的项目说明,Knife4j只提供了增强部分,如果要启用Knife4j的增强功能,可以在配置文件中进行开启
部分配置如下:
部分配置如下:
# springdoc-openapi项目配置
springdoc:
swagger-ui:
path: /swagger-ui.html
tags-sorter: alpha
operations-sorter: alpha
api-docs:
path: /v3/api-docs
group-configs:
- group: 'default'
paths-to-match: '/**'
# 最重要:包扫描Controller
packages-to-scan: cn.bobohost.market.web.controller
# knife4j的增强配置,不需要增强可以不配
knife4j:
enable: true
setting:
language: zh_cn
注意:扫描的Controller的包!
访问文档:
通过http://localhost:8989/doc.html 访问
最后,使用OpenAPI3的规范注解,注释各个Spring的REST接口,示例代码如下:
package cn.bobohost.market.web.controller;
import cn.bobohost.market.common.pojo.ResultDto;
import cn.bobohost.market.common.pojo.ResultPageDto;
import cn.bobohost.market.pojo.Employee;
import cn.bobohost.market.service.EmployeeService;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.Parameters;
import io.swagger.v3.oas.annotations.enums.ParameterIn;
import io.swagger.v3.oas.annotations.tags.Tag;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.CrossOrigin;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
/**
* 员工操作的Controller
*/
//允许跨域
@CrossOrigin
@RestController
@RequestMapping("/emp")
@Tag(name = "员工管理")
public class EmployeeController {
//注入service
@Autowired
private EmployeeService employeeService;
/**
* 查询员工列表
* 注意:请求地址和方法必须符合接口文档;参数得能装进去
* @param searchData
* @return
*/
@GetMapping("/getEmp")
@Operation(summary = "分页条件查询员工列表")
@Parameters({
@Parameter(name = "currentPage",description = "当前页码",required = true,in = ParameterIn.QUERY),
@Parameter(name = "pageSize",description = "每页最大记录数",required = true,in = ParameterIn.QUERY),
@Parameter(name = "searchData",description = "查询的条件的数据",required = true,in= ParameterIn.QUERY)
})
public ResultDto getEmp( Employee searchData, int currentPage, int pageSize){
//调用业务层查询分页数据
Page page = employeeService.findEmployeeListPage(searchData, currentPage, pageSize);
//分页的结果重新封装
ResultPageDto resultPageDto = ResultPageDto.of(page.getTotal(), page.getRecords());
return ResultDto.success("查询分页列表成功!",resultPageDto);
}
}
重启服务,重新访问api文档
另外:API文档不但可以生成接口文档、查看接口api,还能测试用(暂时替代postman):
![在这里插入图片描述](https://img-blog.csdnimg.cn/a2b1632853cc4dbbad35adba7819e5af.png
员工管理前端代码分析
分析前端相关代码,目标:让你可以随便改。
回顾:局部组件(先引入,然后用组件标签);全局组件(先引入,使用Vue.use使用)
内容:
自定义菜单
自定路由
自定义页面
页面调用
员工的添加
查看数据库:
前端测试:
后端代码
表现层:
@PostMapping("/addEmp")
@Operation(summary = "添加一个员工")
public ResultDto addEmp(@RequestBody Employee employee){
employeeService.saveEmployee(employee);
return ResultDto.success("保存员工成功!",null);
}
业务层:
/**
* 保存一个员工
* @param employee
*/
void saveEmployee(Employee employee);
@Override
public void saveEmployee(Employee employee) {
employeeMapper.insert(employee);
}
路由的跳转
id的雪花算法
mysql内置的自增长主键不太适合实际业务:
只能保证同一张表的id不重复,不同表的id是会重复的—影响不是很大。
将来对数据库做分库集群的时候,如果还让mysql自己生成id,那么每一个mysql只管自己的id不重复,但整个集群的同一张表的id会重复。
解决方案:不要让mysql自己生成。用程序生成。
我们现在流行用分布式id生成策略:推特的一个算法:雪花算法。。原理自己看看
MyBatis plus 新版本默认使用雪花算法了
雪花算法(Snowflake Algorithm)是一种用于生成全局唯一的分布式ID的算法。它最初由Twitter开发,用于解决分布式系统中的ID生成问题。
雪花算法的核心思想是将一个64位的ID划分成多个部分,每个部分表示不同的信息。具体来说,一个雪花ID由以下几个部分组成:
- 时间戳(41位):使用毫秒级的时间戳,可以支持约69年的时间跨度。
- 工作节点ID(10位):用于标识不同的工作节点或机器,可以支持最多1024个节点。
- 序列号(12位):表示同一毫秒内生成的ID序列号,可以支持每个节点每毫秒产生最多4096个ID。
在分布式系统中,每个工作节点都需要一个唯一的节点ID。通过将时间戳、工作节点ID和序列号按照一定的规则组合起来,就可以生成全局唯一的ID。
使用雪花算法生成的ID具有一定的优势,包括全局唯一性、趋势递增、精确到毫秒、高性能等特点。它可以在分布式环境下快速生成唯一ID,并且不依赖于中心化的ID生成器。
然而,需要注意的是,雪花算法并不能保证绝对的有序性,因为它无法解决网络延迟等问题带来的时钟回拨或者时钟漂移。在实际使用中,需要根据具体场景和需求来评估雪花算法是否适合。
员工的修改
修改的业务都是俩动作:数据回显和数据更新
数据回显
数据回显,有两种方式:
- 一种直接从页面获取。优点:效率高。缺点:如果数据库的数据发生了变化,则获取不到最新数据。
- 一种是从数据库重新查询。优点:可以查看到最新的数据。缺点:效率低。—自己实现。
数据更新
更新数据的api可以和保存数据用一个。
后端如何区分是保存?还是更新?
根据id是否有值来区分:没值的是保存,有值的是更新。
后端代码:
修改conroller
@PostMapping("/addEmp")
@Operation(summary = "添加一个员工")
public ResultDto addEmp(@RequestBody Employee employee){
if(StringUtils.hasText(employee.getId())){
//更新操作
employeeService.updateEmployee(employee);
return ResultDto.success("修改员工成功!",null);
}else{
//保存操作
employeeService.saveEmployee(employee);
return ResultDto.success("保存员工成功!",null);
}
}
业务层接口代码:
/**
* 更新
* @param employee
*/
void updateEmployee(Employee employee);
业务层实现:
@Override
public void updateEmployee(Employee employee) {
employeeMapper.updateById(employee);
}
使用api文档来测试:
对接前端测试:
修改成功,刷新列表,隐藏修改框:
员工的删除
通常删除分为物理删除和逻辑删除。
- 物理删除:将数据真正从数据库删除。—先实现。
- 逻辑删除:更改了在数据库中的一个删除标识(deleted),但数据仍然存在数据库中。
物理删除员工:
前端:
后端代码:
表现层:
@PostMapping("/deleteEmpById")
@Operation(summary = "删除员工")
public ResultDto deleteEmpById(@RequestBody String id){
employeeService.deleteEmployeeById(id);
return ResultDto.success("删除员工成功!",null);
}
业务层接口:
/**
* 删除员工
* @param id
*/
void deleteEmployeeById(String id);
业务层实现:
@Override
public void deleteEmployeeById(String id) {
//物理删除
// employeeMapper.deleteById(id);
//逻辑删除--更新操作--将用户禁用
Employee employee=new Employee();
employee.setId(id);
employee.setStatus("inactive");
employeeMapper.updateById(employee);
}
api测试
前端测试:
…