一、需求分析和设计
(1)产品原型
一般在做需求分析时,往往都是对照着产品原型进行分析,因为产品原型比较直观,便于我们理解业务。后台系统中可以管理员工信息,通过新增员工来添加后台系统用户。
新增员工原型:
当填写完表单信息, 点击"保存"按钮后, 会提交该表单的数据到服务端, 在服务端中需要接受数据, 然后将数据保存至数据库中。
(2)接口设计
找到资料-->项目接口文档-->苍穹外卖-管理端接口.html
本项目约定:
管理端发出的请求,统一使用 /admin 作为前缀
用户端发出的请求,统一使用 /user 作为前缀
(3)表设计
新增员工,其实就是将我们新增页面录入的员工数据插入到employee表。
employee表结构:
其中,employee表中的status字段已经设置了默认值1,表示状态正常
二、代码开发
(1)设计DTO类
当前端提交的数据和实体类中对应的属性差别比较大时,建议使用DTO来封装数据。由于上述传入参数和实体类有较大差别,所以自定义DTO类。
进入sky-pojo模块,在com.sky.dto包下,已定义EmployeeDTO
(2)Controller层
EmployeeController中创建新增员工方法
进入到sky-server模块中,在com.sky.controller.admin包下,在EmployeeController中创建新增员工方法,接收前端提交的参数。
/ 新增员工 // @param employeeDTO // @return @PostMapping @ApiOperation("新增员工") public Result save(@RequestBody EmployeeDTO employeeDTO) { log.info("新增员工:{}",employeeDTO); employeeService.save(employeeDTO); return null; }
(3)Service层接口
在EmployeeService接口中声明新增员工方法
进入到sky-server模块中,com.sky.server.EmployeeService
(4)Service层实现
在EmployeeServiceImpl中实现新增员工方法
com.sky.server.impl.EmployeeServiceImpl中创建方法
/** * 新增员工 * * @param employeeDTO */ public void save(EmployeeDTO employeeDTO) { Employee employee = new Employee(); //对象属性拷贝 BeanUtils.copyProperties(employeeDTO, employee); //设置账号的状态,默认正常状态 1表示正常 0表示锁定 employee.setStatus(StatusConstant.ENABLE); //设置密码,默认密码123456 employee.setPassword(DigestUtils.md5DigestAsHex(PasswordConstant.DEFAULT_PASSWORD.getBytes())); //设置当前记录的创建时间和修改时间 employee.setCreateTime(LocalDateTime.now()); employee.setUpdateTime(LocalDateTime.now()); //设置当前记录创建人id和修改人id employee.setCreateUser(10L);//目前写个假数据,后期修改 employee.setUpdateUser(10L); employeeMapper.insert(employee);//后续步骤定义 }
(5)Mapper层
在EmployeeMapper中声明insert方法
com.sky.EmployeeMapper中添加方法
/** * 插入员工数据 * @param employee */ @Insert("insert into employee (name, username, password, phone, sex, id_number, create_time, update_time, create_user, update_user,status) " + "values " + "(#{name},#{username},#{password},#{phone},#{sex},#{idNumber},#{createTime},#{updateTime},#{createUser},#{updateUser},#{status})") void insert(Employee employee);
记得把EmployeeController 中的return null 改为 result.success()
三、功能测试
代码已经发开发完毕,对新增员工功能进行测试。
功能测试实现方式:
-
通过接口文档测试
-
通前后端联调测试
接下来我们使用上述两种方式分别测试。
(1)接口文档测试
启动服务:访问http://localhost:8080/doc.html,进入新增员工接口,记得重新启动项目,才会出现新增员工接口
报错原因:由于JWT令牌校验失败,导致EmployeeController的save方法没有被调用
解决方法:调用员工登录接口获得一个合法的JWT令牌
复制令牌
"eyJhbGciOiJIUzI1NiJ9.eyJlbXBJZCI6MSwiZXhwIjoxNzE4Nzg4NTEyfQ.MZThMA9BMC_ijVflCMCEieCDaInKKN5YEopGQ-z4Obs"
添加令牌:
将合法的JWT令牌添加到全局参数中
文档管理-->全局参数设置-->添加参数
接口测试:
其中,请求头部含有JWT令牌
查看employee表: 测试成功
(2)前后端联调测试
启动nginx,访问 http://localhost
登录-->员工管理-->添加员工
注意:由于开发阶段前端和后端是并行开发的,后端完成某个功能后,此时前端对应的功能可能还没有开发完成, 导致无法进行前后端联调测试。所以在开发阶段,后端测试主要以接口文档测试为主。
四、代码完善
目前,程序存在的问题主要有两个:
-
录入的用户名已存,抛出的异常后没有处理
-
新增员工时,创建人id和修改人id设置为固定值
接下来,我们对上述两个问题依次进行分析和解决。
(1)问题一
描述:录入的用户名已存,抛出的异常后没有处理
分析:新增username=zhangsan的用户,若employee表中之前已存在。
解决:
通过全局异常处理器来处理。
进入到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]; String msg = username + MessageConstant.ALREADY_EXISTS; return Result.error(msg); }else{ return Result.error(MessageConstant.UNKNOWN_ERROR); } }
进入到sky-common模块,在MessageConstant.java添加
public static final String ALREADY_EXISTS = "已存在";
再次,接口测试:
(2)问题二
描述:新增员工时,创建人id和修改人id设置为固定值
针对第二个问题,需要通过某种方式动态获取当前登录员工的id
员工登录成功后会生成JWT令牌并响应给前端
后续请求中,前端会携带JWT令牌,通过JWT令牌可以解析出当前登录员工id
思考:解析出登录员工id后,如何传递给Service的save方法?
通过ThreadLocal进行传递。
(3)ThreadLocal
介绍:
ThreadLocal 并不是一个Thread,而是Thread的局部变量。 ThreadLocal为每个线程提供单独一份存储空间,具有线程隔离的效果,只有在线程内才能获取到对应的值,线程外则不能访问。
常用方法:
-
public void set(T value) 设置当前线程的线程局部变量的值
-
public T get() 返回当前线程所对应的线程局部变量的值
-
public void remove() 移除当前线程的线程局部变量
注意:客户端发送的每次请求,后端的Tomcat服务器都会分配一个单独的线程来处理请求
五、代码提交
六、问题解决
(1)private EmployeeMapper employeeMapper;报红