目录
一、员工管理-新增员工
准备工作:
-
先把服务端启动:运行引导类
-
再把nginx启动:双击nginx.exe,去任务管理器里查看一下,如果有两个nginx进程就说明启动成功了
开发功能时的依赖:
-
接口文档
1.需求分析和设计
产品原型
一般在做需求分析时,往往都是对照着产品原型进行分析,因为产品原型比较直观,便于我们理解业务。
后台系统中可以管理员工信息,通过新增员工来添加后台系统用户。
新增员工原型:
根据接口文档写:
2.代码开发
//DTO类
package com.sky.dto;
import lombok.Data;
import java.io.Serializable;
@Data
public class EmployeeDTO implements Serializable {
private Long id;
private String username;
private String name;
private String phone;
private String sex;
private String idNumber;
}
--------------------
//EmployeeController中创建新增员工方法
@PostMapping
@ApiOperation("新增员工")
public Result addEmp(@RequestBody EmployeeDTO dto){
log.info("新增员工:{}", dto);
return employeeService.addEmployee(dto);
}
-------------------
//Result类定义了后端统一返回结果格式
package com.sky.result;
import lombok.Data;
import java.io.Serializable;
/**
* 后端统一返回结果
* @param <T>
*/
@Data
public class Result<T> implements Serializable {
private Integer code; //编码:1成功,0和其它数字为失败
private String msg; //错误信息
private T data; //数据
public static <T> Result<T> success() {
Result<T> result = new Result<T>();
result.code = 1;
return result;
}
public static <T> Result<T> success(T object) {
Result<T> result = new Result<T>();
result.data = object;
result.code = 1;
return result;
}
public static <T> Result<T> error(String msg) {
Result result = new Result();
result.msg = msg;
result.code = 0;
return result;
}
}
------------------
//在EmployeeService接口中声明新增员工方法
/**
* 新增员工
* @param dto 员工信息
* @return
*/
Result addEmployee(EmployeeDTO dto);
--------------------
//在EmployeeServiceImpl中实现新增员工方法
@Override
public Result addEmployee(EmployeeDTO dto) {
//封装实体类对象
Employee employee = new Employee();
//使用工具类BeanUtils把dto里的数据,拷贝到employee对象里
BeanUtils.copyProperties(dto, employee);
//补充数据
// 默认密码
employee.setPassword(DigestUtils.md5Hex(PasswordConstant.DEFAULT_PASSWORD));
// status状态:
employee.setStatus(StatusConstant.ENABLE);
// createTime和updateTime
LocalDateTime now = LocalDateTime.now();
employee.setCreateTime(now);
employee.setUpdateTime(now);
// createUser和updateUser:先写假数据
employee.setCreateUser(10L);
employee.setUpdateUser(10L);
employeeMapper.insert(employee);
return Result.success();
}
---------------------
//在sky-common模块com.sky.constants包下已定义StatusConstant.java
package com.sky.constant;
/**
* 状态常量,启用或者禁用
*/
public class StatusConstant {
//启用
public static final Integer ENABLE = 1;
//禁用
public static final Integer DISABLE = 0;
}
---------------------
//在EmployeeMapper中声明insert方法
@Insert("INSERT INTO employee (name, username, password, phone, sex, id_number, status, create_time, update_time, create_user, update_user)" +
" VALUES (#{name}, #{username}, #{password}, #{phone}, #{sex}, #{idNumber}, #{status}, #{createTime}, #{updateTime}, #{createUser}, #{updateUser})")
void insert(Employee employee);
--------------------
//在application.yml中已开启驼峰命名,故id_number和idNumber可对应
mybatis:
configuration:
#开启驼峰命名
map-underscore-to-camel-case: true
3 功能测试
接口文档测试
请求没有携带token测试报401
启动服务:访问http://localhost:8080/doc.html,进入新增员工接口
前后端联调测试
启动nginx,访问 http://localhost
登录-->员工管理-->添加员工
添加完成之后看数据库
4 代码完善一:增加异常处理器
问题说明
描述:录入的用户名已存,抛出的异常后没有处理
分析:
新增username=zhangsan的用户,若employee表中之前已存在,数据库有设置唯一unique。
后台报错信息:
增加异常处理器
通过全局异常处理器来处理。
进入到sky-server模块,com.sky.hander包下,GlobalExceptionHandler.java添加方法
/**
* 处理SQL异常
* @param ex
* @return
*/
@ExceptionHandler
public Result exceptionHandler(SQLIntegrityConstraintViolationException ex){
//Duplicate entry 'zhangsan' for key 'employee.idx_username'
String message = ex.getMessage();
if(message.contains("Duplicate entry")){
String[] split = message.split(" ");
String username = split[2];
return Result.error(username + MessageConstant.ALREADY_EXISTS);
}else{
return Result.error(MessageConstant.UNKNOWN_ERROR);
}
}
5 代码完善二:绑定当前用户名
问题说明
描述:新增员工时,创建人id和修改人id设置为固定值
通过某种方式动态获取当前登录员工的id
员工登录成功后会生成JWT令牌并响应给前端:
当前用户我们从jwt令牌中获取,可以直接一个传输,在你的类中直接创建,当然不是很方便,要每个类都传,这时候有个更好的办法放线程里
ThreadLocal介绍
源码:
多次访问我们的线程不会冲突,因为每次请求就会有一个新线程,线程里的一个map空间,以this当前的对象作为键key,以set设定的值作为value
public void set(T value) {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null) {
map.set(this, value);
} else {
createMap(t, value);
}
}
介绍:
ThreadLocal 并不是一个Thread,而是Thread的局部变量。
每个线程都有单独一份存储空间,是一个HashMap,存取数据时以ThreadLocal对象为key,以存储的值为value
这个存储空间具有线程隔离的效果,只有在线程内才能获取到对应的值,线程外则不能访问。
常用方法:
-
public void set(T value) 设置当前线程的线程局部变量的值
-
public T get() 返回当前线程所对应的线程局部变量的值
-
public void remove() 移除当前线程的线程局部变量
对ThreadLocal有了一定认识后,接下来继续解决问题二
(1) 准备ThreadLocal工具类
初始工程中已经封装了 ThreadLocal 操作的工具类:
在sky-common模块
package com.sky.context;
public class BaseContext {
public static ThreadLocal<Long> threadLocal = new ThreadLocal<>();
public static void setCurrentId(Long id) {
threadLocal.set(id);
}
public static Long getCurrentId() {
return threadLocal.get();
}
public static void removeCurrentId() {
threadLocal.remove();
}
}
(2) 修改拦截器代码
在拦截器中解析出当前登录员工id,并放入线程局部变量中:
在sky-server模块中,拦截器:
package com.sky.interceptor;
/**
* jwt令牌校验的拦截器
*/
@Component
@Slf4j
public class JwtTokenAdminInterceptor implements HandlerInterceptor {
@Autowired
private JwtProperties jwtProperties;
/**
* 校验jwt
*
* @param request
* @param response
* @param handler
* @return
* @throws Exception
*/
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
//.............................
//2、校验令牌
try {
//.................
Claims claims = JwtUtil.parseJWT(jwtProperties.getAdminSecretKey(), token);
Long empId = Long.valueOf(claims.get(JwtClaimsConstant.EMP_ID).toString());
log.info("当前员工id:", empId);
/将用户id存储到ThreadLocal
BaseContext.setCurrentId(empId);
//3、通过,放行
return true;
} catch (Exception ex) {
//......................
}
}
}
(3) Service里获取当前用户
在Service中获取线程局部变量中的值:
小结
ThreadLocal的使用:
作用:用于在同一线程内共享数据的
用法:
1. 创建ThreadLocal对象: ThreadLocal<管理的数据类型> tl = new ThreadLocal<>();
2. 把数据绑定到当前线程上:tl.set(数据);
3. 从当前线程上获取数据: tl.get();
4. 从当前线程上清理数据: tl.remove();
注意:
一个ThreadLocal对象,只能管理一个数据。
使用哪个ThreadLocal对象存储的数据,就必须用哪个ThreadLocal对象获取到这个数据
当使用完成数据之后,一定要把数据从当前线程清理掉;否则可能会存在内存溢出的风险
使用技巧:
通常是创建一个类,类里提供静态的ThreadLocal对象,并提供存取数据的方法
public class Xxxx{
private static final ThreadLocal tl = new ThreadLocal();
public static void setXxx(Xxx xxx){
tl.set(xxx);
}
public static Xxx getXxx(){
return tl.get();
}
public static void remove(){
tl.remove();
}
}
如果需要绑定数据到当前线程:Xxxx.setXxx(值)
如果需要从当前线程获取数据:Xxxx.getXxx()
如果需要从当前线程清理数据:Xxxx.remote();
二、员工管理-分页查询
1.需求分析和设计
2 代码开发
//DTO类
package com.sky.dto;
import lombok.Data;
import java.io.Serializable;
@Data
public class EmployeePageQueryDTO implements Serializable {
//员工姓名
private String name;
//页码
private int page;
//每页显示记录数
private int pageSize;
}
-----------------
//后面所有的分页查询,统一都封装为PageResult对象。
package com.sky.result;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.io.Serializable;
import java.util.List;
/**
* 封装分页查询结果
*/
@Data
@AllArgsConstructor
@NoArgsConstructor
public class PageResult implements Serializable {
private long total; //总记录数
private List records; //当前页数据集合
}
-----------------
//员工信息分页查询后端返回的对象类型为: Result<PageResult>
package com.sky.result;
import lombok.Data;
import java.io.Serializable;
/**
* 后端统一返回结果
* @param <T>
*/
@Data
public class Result<T> implements Serializable {
private Integer code; //编码:1成功,0和其它数字为失败
private String msg; //错误信息
private T data; //数据
public static <T> Result<T> success() {
Result<T> result = new Result<T>();
result.code = 1;
return result;
}
public static <T> Result<T> success(T object) {
Result<T> result = new Result<T>();
result.data = object;
result.code = 1;
return result;
}
public static <T> Result<T> error(String msg) {
Result result = new Result();
result.msg = msg;
result.code = 0;
return result;
}
}
------------------
//在sky-server模块中,com.sky.controller.admin.EmployeeController中添加分页查询方法。
/**
* 员工分页查询
* @param employeePageQueryDTO
* @return
*/
@GetMapping("/page")
@ApiOperation("员工分页查询")
public Result<PageResult> page(EmployeePageQueryDTO employeePageQueryDTO){
log.info("员工分页查询,参数为:{}", employeePageQueryDTO);
PageResult pageResult = employeeService.pageQuery(employeePageQueryDTO);//后续定义
return Result.success(pageResult);
}
--------------------
//在EmployeeService接口中声明pageQuery方法
/**
* 分页查询
* @param employeePageQueryDTO
* @return
*/
PageResult pageQuery(EmployeePageQueryDTO employeePageQueryDTO);
--------------------
//在EmployeeServiceImpl中实现pageQuery方法:
/**
* 分页查询
*
* @param employeePageQueryDTO
* @return
*/
public PageResult pageQuery(EmployeePageQueryDTO employeePageQueryDTO) {
// select * from employee limit 0,10
//开始分页查询
PageHelper.startPage(employeePageQueryDTO.getPage(), employeePageQueryDTO.getPageSize());
Page<Employee> page = employeeMapper.pageQuery(employeePageQueryDTO);//后续定义
long total = page.getTotal();
List<Employee> records = page.getResult();
return new PageResult(total, records);
}
------------------
**注意:**此处使用 mybatis 的分页插件 PageHelper 来简化分页代码的开发。底层基于 mybatis 的拦截器实现。
故在pom.xml文中添加依赖(初始工程已添加)
<dependency>
<groupId>com.github.pagehelper</groupId>
<artifactId>pagehelper-spring-boot-starter</artifactId>
<version>${pagehelper}</version>
</dependency>
-----------------
//在 EmployeeMapper 中声明 pageQuery 方法:
/**
* 分页查询
* @param employeePageQueryDTO
* @return
*/
Page<Employee> pageQuery(EmployeePageQueryDTO employeePageQueryDTO);
------------------
//在 src/main/resources/mapper/EmployeeMapper.xml 中编写SQL:
<select id="pageQuery" resultType="com.sky.entity.Employee">
select * from employee
<where>
<if test="name != null and name != ''">
and name like concat('%',#{name},'%')
</if>
</where>
order by create_time desc
</select>
3 功能测试
两种方式:
1.接口文档测试
请求没有携带token测试报401
启动服务:访问http://localhost:8080/doc.html,进入新增员工接口
2.前后端联调测试
启动nginx,访问 http://localhost
登录-->员工管理-->添加员工
4.问题描述:操作时间字段显示有问题。
解决方式:
(1). 方式一
在属性上加上注解,对日期进行格式化
但这种方式,需要在每个时间属性上都要加上该注解,使用较麻烦,不能全局处理。
(2). 方式二(推荐 )
在WebMvcConfiguration中扩展SpringMVC的消息转换器,统一对日期类型进行格式处理
package com.sky.json;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.module.SimpleModule;
import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateDeserializer;
import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateTimeDeserializer;
import com.fasterxml.jackson.datatype.jsr310.deser.LocalTimeDeserializer;
import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateSerializer;
import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateTimeSerializer;
import com.fasterxml.jackson.datatype.jsr310.ser.LocalTimeSerializer;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.format.DateTimeFormatter;
import static com.fasterxml.jackson.databind.DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES;
/**
* 对象映射器:基于jackson将Java对象转为json,或者将json转为Java对象
* 将JSON解析为Java对象的过程称为 [从JSON反序列化Java对象]
* 从Java对象生成JSON的过程称为 [序列化Java对象到JSON]
*/
public class JacksonObjectMapper extends ObjectMapper {
public static final String DEFAULT_DATE_FORMAT = "yyyy-MM-dd";
//public static final String DEFAULT_DATE_TIME_FORMAT = "yyyy-MM-dd HH:mm:ss";
public static final String DEFAULT_DATE_TIME_FORMAT = "yyyy-MM-dd HH:mm";
public static final String DEFAULT_TIME_FORMAT = "HH:mm:ss";
public JacksonObjectMapper() {
super();
//收到未知属性时不报异常
this.configure(FAIL_ON_UNKNOWN_PROPERTIES, false);
//反序列化时,属性不存在的兼容处理
this.getDeserializationConfig().withoutFeatures(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES);
SimpleModule simpleModule = new SimpleModule()
.addDeserializer(LocalDateTime.class, new LocalDateTimeDeserializer(DateTimeFormatter.ofPattern(DEFAULT_DATE_TIME_FORMAT)))
.addDeserializer(LocalDate.class, new LocalDateDeserializer(DateTimeFormatter.ofPattern(DEFAULT_DATE_FORMAT)))
.addDeserializer(LocalTime.class, new LocalTimeDeserializer(DateTimeFormatter.ofPattern(DEFAULT_TIME_FORMAT)))
.addSerializer(LocalDateTime.class, new LocalDateTimeSerializer(DateTimeFormatter.ofPattern(DEFAULT_DATE_TIME_FORMAT)))
.addSerializer(LocalDate.class, new LocalDateSerializer(DateTimeFormatter.ofPattern(DEFAULT_DATE_FORMAT)))
.addSerializer(LocalTime.class, new LocalTimeSerializer(DateTimeFormatter.ofPattern(DEFAULT_TIME_FORMAT)));
//注册功能模块 例如,可以添加自定义序列化器和反序列化器
this.registerModule(simpleModule);
}
}
-------------------------
//在WebMvcConfiguration中扩展SpringMVC的消息转换器,统一对日期类型进行格式处理
/**
* 扩展Spring MVC框架的消息转化器
* @param converters
*/
protected void extendMessageConverters(List<HttpMessageConverter<?>> converters) {
log.info("扩展消息转换器...");
//创建一个消息转换器对象
MappingJackson2HttpMessageConverter converter = new MappingJackson2HttpMessageConverter();
//需要为消息转换器设置一个对象转换器,对象转换器可以将Java对象序列化为json数据
converter.setObjectMapper(new JacksonObjectMapper());
//将自己的消息转化器加入容器中
converters.add(0,converter);
}
三、员工管理-启用禁用
1 需求分析与设计
2 代码开发
//在sky-server模块中,根据接口设计中的请求参数形式对应的在 EmployeeController 中创建启用禁用员工账号的方法:
@PostMapping("/status/{status}")
@ApiOperation("启用/禁用员工帐号")
public Result updateStatus(@PathVariable("status") Integer status,
@RequestParam("id") Long empId){
return employeeService.updateStatus(empId, status);
}
----------------
//在 EmployeeService 接口中声明启用禁用员工账号的业务方法:
/**
* 更新员工状态:启动/禁用
* @param empId 员工id
* @param status 状态
* @return
*/
Result updateStatus(Long empId, Integer status);
-------------------
//在 EmployeeServiceImpl 中实现启用禁用员工账号的业务方法:
@Override
public Result updateStatus(Long empId, Integer status) {
//封装Employee对象
Employee emp = Employee.builder().id(empId).status(status).build();
//调用Mapper执行update语句
employeeMapper.update(emp);
return Result.success();
}
-----------------
//在 EmployeeMapper 接口中声明 update 方法:
/**
* 根据主键动态修改属性
* @param employee
*/
void update(Employee emp);
-------------------
//在 EmployeeMapper.xml 中编写SQL:
<update id="update">
update employee
<set>
<if test="username!=null and username.length()>0">username=#{username},</if>
<if test="name!=null and name.length()>0">name=#{name},</if>
<if test="password!=null and password.length()>0">password=#{password},</if>
<if test="phone!=null and phone.length()>0">phone=#{phone},</if>
<if test="sex!=null and sex.length()>0">sex=#{sex},</if>
<if test="status!=null">status=#{status},</if>
<if test="updateTime!=null">update_time=#{updateTime},</if>
<if test="createUser!=null">create_user=#{createUser},</if>
<if test="updateUser!=null">update_user=#{updateUser},</if>
</set>
where id = #{id}
</update>
3 功能测试
1.接口文档测试
请求没有携带token测试报401
启动服务:访问http://localhost:8080/doc.html,进入新增员工接口
2.前后端联调测试
启动nginx,访问 http://localhost
package com.sky.json;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.module.SimpleModule;
import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateDeserializer;
import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateTimeDeserializer;
import com.fasterxml.jackson.datatype.jsr310.deser.LocalTimeDeserializer;
import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateSerializer;
import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateTimeSerializer;
import com.fasterxml.jackson.datatype.jsr310.ser.LocalTimeSerializer;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.format.DateTimeFormatter;
import static com.fasterxml.jackson.databind.DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES;
/**
* 对象映射器:基于jackson将Java对象转为json,或者将json转为Java对象
* 将JSON解析为Java对象的过程称为 [从JSON反序列化Java对象]
* 从Java对象生成JSON的过程称为 [序列化Java对象到JSON]
*/
public class JacksonObjectMapper extends ObjectMapper {
public static final String DEFAULT_DATE_FORMAT = "yyyy-MM-dd";
//public static final String DEFAULT_DATE_TIME_FORMAT = "yyyy-MM-dd HH:mm:ss";
public static final String DEFAULT_DATE_TIME_FORMAT = "yyyy-MM-dd HH:mm";
public static final String DEFAULT_TIME_FORMAT = "HH:mm:ss";
public JacksonObjectMapper() {
super();
//收到未知属性时不报异常
this.configure(FAIL_ON_UNKNOWN_PROPERTIES, false);
//反序列化时,属性不存在的兼容处理
this.getDeserializationConfig().withoutFeatures(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES);
SimpleModule simpleModule = new SimpleModule()
.addDeserializer(LocalDateTime.class, new LocalDateTimeDeserializer(DateTimeFormatter.ofPattern(DEFAULT_DATE_TIME_FORMAT)))
.addDeserializer(LocalDate.class, new LocalDateDeserializer(DateTimeFormatter.ofPattern(DEFAULT_DATE_FORMAT)))
.addDeserializer(LocalTime.class, new LocalTimeDeserializer(DateTimeFormatter.ofPattern(DEFAULT_TIME_FORMAT)))
.addSerializer(LocalDateTime.class, new LocalDateTimeSerializer(DateTimeFormatter.ofPattern(DEFAULT_DATE_TIME_FORMAT)))
.addSerializer(LocalDate.class, new LocalDateSerializer(DateTimeFormatter.ofPattern(DEFAULT_DATE_FORMAT)))
.addSerializer(LocalTime.class, new LocalTimeSerializer(DateTimeFormatter.ofPattern(DEFAULT_TIME_FORMAT)));
//注册功能模块 例如,可以添加自定义序列化器和反序列化器
this.registerModule(simpleModule);
}
}
--------------------------
//在WebMvcConfiguration中扩展SpringMVC的消息转换器,统一对日期类型进行格式处理
/**
* 扩展Spring MVC框架的消息转化器
* @param converters
*/
protected void extendMessageConverters(List<HttpMessageConverter<?>> converters) {
log.info("扩展消息转换器...");
//创建一个消息转换器对象
MappingJackson2HttpMessageConverter converter = new MappingJackson2HttpMessageConverter();
//需要为消息转换器设置一个对象转换器,对象转换器可以将Java对象序列化为json数据
converter.setObjectMapper(new JacksonObjectMapper());
//将自己的消息转化器加入容器中
converters.add(0,converter);
}
四、员工管理-编辑员工
1 需求分析与设计
2 代码开发
//在 EmployeeController 中创建 getById 方法
/**
* 根据id查询员工信息
* @param id
* @return
*/
@GetMapping("/{id}")
@ApiOperation("根据id查询员工信息")
public Result<Employee> getById(@PathVariable Long id){
Employee employee = employeeService.getById(id);
return Result.success(employee);
}
--------------
//在 EmployeeService 接口中声明 getById 方法:
/**
* 根据id查询员工
* @param id
* @return
*/
Employee getById(Long id);
----------------
//在 EmployeeServiceImpl 中实现 getById 方法:
/**
* 根据id查询员工
*
* @param id
* @return
*/
public Employee getById(Long id) {
Employee employee = employeeMapper.getById(id);
employee.setPassword("****");
return employee;
}
----------------
//在 EmployeeMapper 接口中声明 getById 方法:
/**
* 根据id查询员工信息
* @param id
* @return
*/
@Select("select * from employee where id = #{id}")
Employee getById(Long id);
----------------
//在 EmployeeController 中创建 update 方法:
/**
* 编辑员工信息
* @param employeeDTO
* @return
*/
@PutMapping
@ApiOperation("编辑员工信息")
public Result update(@RequestBody EmployeeDTO employeeDTO){
log.info("编辑员工信息:{}", employeeDTO);
employeeService.update(employeeDTO);
return Result.success();
}
-----------------
//在 EmployeeService 接口中声明 update 方法:
/**
* 编辑员工信息
* @param employeeDTO
*/
void update(EmployeeDTO employeeDTO);
-----------------
//在 EmployeeServiceImpl 中实现 update 方法:
/**
* 编辑员工信息
*
* @param employeeDTO
*/
public void update(EmployeeDTO employeeDTO) {
Employee employee = new Employee();
BeanUtils.copyProperties(employeeDTO, employee);
employee.setUpdateTime(LocalDateTime.now());
employee.setUpdateUser(BaseContext.getCurrentId());
employeeMapper.update(employee);
}
3 功能测试
两种方式:
1.接口文档测试
请求没有携带token测试报401
启动服务:访问http://localhost:8080/doc.html,进入新增员工接口
2.前后端联调测试
启动nginx,访问 http://localhost