搜狗站长平台主动提交,安徽省建设工程信息网企业入口在哪,企业网站管理系统如何上传图片,企业培训内容包括哪些内容新增员工需求分析和设计产品原型#xff1a;接口设计#xff1a;本项目约定#xff1a;管理端发出的请求#xff0c;统一使用 /admin 作为前缀用户端发出的请求#xff0c;统一使用 /user 作为前缀数据库表设计#xff1a;代码开发根据新增员工接口设计对应的 DTO#x…新增员工需求分析和设计产品原型接口设计
本项目约定 管理端发出的请求统一使用 /admin 作为前缀 用户端发出的请求统一使用 /user 作为前缀数据库表设计代码开发根据新增员工接口设计对应的 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;}PostMappingApiOperation(新增员工)public Result save(RequestBody EmployeeDTO employeeDTO){log.info(新增了员工: {}, employeeDTO);employeeService.save(employeeDTO);return Result.success();}Overridepublic void save(EmployeeDTO employeeDTO) {Employee employee new Employee();//对象属性拷贝BeanUtils.copyProperties(employeeDTO,employee);//设置账号状态默认正常状态1表示正常0表示锁定employee.setStatus(StatusConstant.ENABLE);//设置加密后的密码employee.setPassword(DigestUtils.md5DigestAsHex(PasswordConstant.DEFAULT_PASSWORD.getBytes()));//设置当前记录的创建时间employee.setUpdateTime(LocalDateTime.now());employee.setCreateTime(LocalDateTime.now());//设置当前记录的创建人id和修改人id//TODO 后期更改为当前登录用户的 idemployee.setCreateUser(10L);employee.setUpdateUser(10L);log.info(新增了员工: {}, employee);employeeMapper.save(employee);}Insert(insert into sky_take_out.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 save(Employee employee);
}功能测试功能测试方法通过接口文档测试 通过前后端联调测试注意由于开发阶段前端和后端是并行开发的后端完成某个功能后此时前端对应的功能可能还没有开发完成导致无法进行前后端联调测试所以在开发阶段后端测试主要以接口文档测试为主。代码完善程序存在的问题录入的用户名已存在抛出异常后没有处理 新增员工时创建人 id 和 修改人 id 设置了固定值先在全局异常处理类中完成第一个问题ExceptionHandlerpublic Result exceptionHandler(BaseException ex){log.error(异常信息{}, ex.getMessage());return Result.error(ex.getMessage());}ExceptionHandlerpublic Result exceptionHandler(SQLIntegrityConstraintViolationException ex){// Duplicate entry zhangsan for key employee.idx_usernameString 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);}}
}这里用到了方法重载针对第二个问题需要通过某种方式动态获取当前登录员工的 id当登录成功时我们的 id 就已经在 claims 里面了所以如果我们想反向拿出来也是可以办到的
package com.sky.interceptor;import com.sky.constant.JwtClaimsConstant;
import com.sky.properties.JwtProperties;
import com.sky.utils.JwtUtil;
import io.jsonwebtoken.Claims;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.HandlerInterceptor;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;/*** jwt令牌校验的拦截器*/
Component
Slf4j
public class JwtTokenAdminInterceptor implements HandlerInterceptor {Autowiredprivate JwtProperties jwtProperties;/*** 校验jwt** param request* param response* param handler* return* throws Exception*/public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {//判断当前拦截到的是Controller的方法还是其他资源if (!(handler instanceof HandlerMethod)) {//当前拦截到的不是动态方法直接放行return true;}//1、从请求头中获取令牌String token request.getHeader(jwtProperties.getAdminTokenName());//2、校验令牌try {log.info(jwt校验:{}, token);Claims claims JwtUtil.parseJWT(jwtProperties.getAdminSecretKey(), token);Long empId Long.valueOf(claims.get(JwtClaimsConstant.EMP_ID).toString());log.info(当前员工id, empId);//3、通过放行return true;} catch (Exception ex) {//4、不通过响应401状态码response.setStatus(401);return false;}}
}
注意第48行就是我们需要的 empId那么现在的问题就是怎么将这个 id 传给我们的 service的 save 方法。这里需要用到一个重要的知识 ThreadLocalThreadLocal 并不是一个 Thread而是 Thread 的局部变量。ThreadLocal 为每一个线程提供单独一份存储空间具有线程隔离效果只有在线程内才能获取到对应的值线程外则不能访问 我们需要一个工具类用来调用 ThreadLocal 方法
package com.sky.context;public class BaseContext {public static ThreadLocalLong 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();}}
然后在校验令牌时取出 empId,并将他放入 threadlocal最后要用的时候拿出来员工分页查询需求分析和设计产品原型接口设计代码开发和前面新增员工一样我们需要一个与接口设计相对应的 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;}GetMapping(/page)ApiOperation(分页查询)public ResultPageResult page(EmployeePageQueryDTO employeePageQueryDTO){log.info(分页查询{}, employeePageQueryDTO);PageResult pageResult employeeService.page(employeePageQueryDTO);return Result.success(pageResult);}Overridepublic PageResult page(EmployeePageQueryDTO employeePageQueryDTO) {PageHelper.startPage(employeePageQueryDTO.getPage(), employeePageQueryDTO.getPageSize());PageEmployee page employeeMapper.pagequery(employeePageQueryDTO);return new PageResult(page.getTotal(), page.getResult());}拓展PageHelper 的底层时 ThreadLocal 实现的select idpagequery resultTypecom.sky.entity.Employeeselect * from sky_take_out.employeewhereif testname ! null and name ! and name like concat(%, #{name}, %)/if/where/select功能测试这里会发现日期这边看起来不舒服接下来完善代码解决这个问题代码完善
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 中拓展 Spring MVC 的消息转换器统一对日期类型进行 格式化处理建议使用第二种这个方法是固定的是我们这个配置类继承的父类里面就有的启用禁用员工账号需求分析和设计产品原型接口设计代码开发PostMapping(/status/{status})ApiOperation(启用或禁用员工)public Result start_or_stop(PathVariable Integer status, Long id){log.info(启用或禁用员工{},{}, status, id);employeeService.start_or_stop(status, id);return Result.success();}Overridepublic void start_or_stop(Integer status, Long id) {Employee employee Employee.builder().status(status).id(id).build();employeeMapper.update(employee);}update idupdateUPDATE sky_take_out.employeesetif testname ! null and name ! name #{name},/ifif testusername ! null and username ! username #{username},/ifif testpassword ! null and password ! password #{password},/ifif testphone ! null and phone ! phone #{phone},/ifif testsex ! null and sex ! sex #{sex},/ifif testidNumber ! null and idNumber ! id_number #{idNumber},/ifif teststatus ! nullstatus #{status},/ifif testupdateTime ! nullupdate_time #{updateTime},/ifif testupdateUser! nullupdate_user #{updateUser}/if/setWHERE id #{id}/update功能测试编辑员工需求分析和设计产品原型接口设计注意这里需要两个接口1根据 id 查询员工信息也就是查询回显2编辑员工信息代码开发首先是查询回显GetMapping(/{id})ApiOperation(根据 id 查询员工)public ResultEmployee queryById(PathVariable Long id){log.info(查询的员工的 id 为{}, id);Employee employee employeeService.queryById(id);return Result.success(employee);}Overridepublic Employee queryById(Long id) {Employee employee employeeMapper.queryById(id);return employee;}select idqueryById resultTypecom.sky.entity.Employeeselect * from sky_take_out.employee where id #{id}/select然后是更新员工PutMappingApiOperation(修改员工数据)public Result update(RequestBody EmployeeDTO employeeDTO){log.info(修改员工{}, employeeDTO);employeeService.update(employeeDTO);return Result.success();}Overridepublic void update(EmployeeDTO employeeDTO) {Employee employee new Employee();BeanUtils.copyProperties(employeeDTO,employee);employee.setUpdateUser(BaseContext.getCurrentId());employee.setUpdateTime(LocalDateTime.now());employeeMapper.update(employee);}update idupdateUPDATE sky_take_out.employeesetif testname ! null and name ! name #{name},/ifif testusername ! null and username ! username #{username},/ifif testpassword ! null and password ! password #{password},/ifif testphone ! null and phone ! phone #{phone},/ifif testsex ! null and sex ! sex #{sex},/ifif testidNumber ! null and idNumber ! id_number #{idNumber},/ifif teststatus ! nullstatus #{status},/ifif testupdateTime ! nullupdate_time #{updateTime},/ifif testupdateUser! nullupdate_user #{updateUser}/if/setWHERE id #{id}/update功能测试导入分类模块功能代码需求分析和设计产品原型接口设计代码导入因为这里和之前员工管理的部分基本一样所以直接导入即可注意最好从后往前导入也就是从Mapper 层开始导入这样会减少报错导入完之后记得手动编译功能测试