黑马程序员苍穹外卖笔记(DAY2)

1、新增员工

1)需求分析和设计

  • 产品原型
  • 接口设计
  • 数据库设计

产品原型

​

接口设计

​

​

数据库设计
​

2)代码开发

  • 需求设计与分析
  • 代码开发、测试
  • 代码完善
根据员工新增的数据设计 DTO

​ 当前端提交的数据和我们所需要的数据差距过大的时候,我们需要去新建一个 DTO 来封装数据,比如上述的条件下,前端的发送来的员工信息和我们实际的员工实体类有很大的差距,我们此时可以封装一个新的 DTO 来接收传来的数据。

@Data
public class EmployeeDTO implements Serializable {

    private Long id;

    private String username;

    private String name;

    private String phone;

    private String sex;

    private String idNumber;

}
开发新增员工的功能
  1. 在 Controller 类中添加一个新增员工的方法,并且调用 Serive 中的新增员工的方法

    @PostMapping
    //swapper 提供的注释,用于在网页上显示该方法
    @ApiOperation("新增员工")
    public Result<String> save(@RequestBody EmployeeDTO employeeDTO) {
        log.info("新增员工的操作{}", employeeDTO);
        employeeService.save(employeeDTO);
        return Result.success();
    }
    
  2. 添加 Service 中新增员工的方法,再调用 Mapper 中的添加到数据库的方法。

    @Override
    public void save(EmployeeDTO employeeDTO) {
        Employee employee = new Employee();
    
        //对象属性拷贝
        BeanUtils.copyProperties(employeeDTO, employee);
    
        //设置用户状态
        employee.setStatus(StatusConstant.ENABLE);
    
        //设置默认密码
        employee.setPassword(DigestUtils.md5DigestAsHex(PasswordConstant.DEFAULT_PASSWORD.getBytes()));
    
        //设置创建时间和修改时间
        employee.setCreateTime(LocalDateTime.now());
        employee.setUpdateTime(LocalDateTime.now());
    
        //设置当前记录创建人和修改人的 id
        //  @TODO 设置当前记录创建人和修改人的 id
        employee.setCreateUser(BaseContext.getCurrentId());
        employee.setUpdateUser(BaseContext.getCurrentId());
    
        employeeMapper.insert(employee);
    }
    
  3. 添加新增的方法在 Mapper 类中

      @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);
    
功能测试

​ 在我们日常开发的时候,由于很多时候前端的界面还未开发完成,无法进行前后端联调,这时我们需要通过接口文档来进行测试,也就是我们前面配置的 Swapper,正常开发的时候以接口文档测试为主。
​

​ 在测试的时候,我们会发现这时的请求是无法直接发送到后端服务器的,因为当我们执行这种类型的命令的时候,后端的过滤器会对我们前端发送过来的 jwt 令牌进行校验,而我们通过这种方式发送的令牌为空,会返回一个异常。
​ 因此我们需要通过登录方法来获取一个令牌,但需要注意的是这个令牌的存在时间为两个小时,后续还需要再次获取。

3)代码完善

解决同名异常问题

​ 通过上面的测试,我们可以发现一个问题,就是我们的用户名在数据库设定为不能重复,当我们输入重复的数据的时候,会抛出一个异常,这个异常如果我们不去处理而去交给其自动处理的话,就会影响程序的正常运行,因此我们可以使用 MVC 中的异常处理器来处理。

    /**
     * 捕获 sql 异常
     * @param ex 抛出的异常
     * @return 处理的结果,是已经约定好的
     */
	@ExceptionHandler
    public Result exceptionHandler(SQLIntegrityConstraintViolationException ex) {
        String message = ex.getMessage();
        if (message.contains("Duplicate entry")) {
            // 利用字符串分割来得到传来的姓名
            String[] s = message.split(" ");
            String username = s[2];
            // 将姓名和存在的字符串拼接起来
            String msg = username + MessageConstant.ALREADY_EXISTS;
            return Result.error(msg);
        } else {
            return Result.error(MessageConstant.UNKNOWN_ERROR);
        }
    }
解决修改人员命名的问题

​ 还有一点就是上面在记录修改员工的管理的 id 和 name 的时候设置了一个默认的值,这里来进行完善

  • 先来回顾一个 jwt 的功能

  • 员工登录后后端将 jwt 令牌返回给前端,这时前端的每一次请求都会去携带这个令牌,我们可以从令牌中解析到员工的 id 数据

    	// 1、从请求头中获取令牌
    	String token = request.getHeader(jwtProperties.getAdminTokenName());
    	// 2、校验令牌
    	try{
    		Claims claims = JwtUtil.parseJWT(jwtProperties.getAdminSecretKey(), token);    
    		Long empId = Long.valueOf(claims.get(JwtClaimsConstant.EMP_ID).toString());    
    	// 3、通过,放行    
    		return true;
    		}catch (Exception ex){   
        // 4、不通过,响应401状态码    
                response.setStatus(401);    
                return false;
    }
    

    这时候我们得到了员工的 id 那应该如何传递给我们的 save 方法呢?

    补充一个 ThreadLocal 的知识

ThreadLocal 并不是一个Thread,而是Thread的局部变量。

ThreadLocal为每个线程提供单独一份存储空间,具有线程隔离的效果,只有在线程内才能获取到对应的值,线程外则不能访问。

ThreadLocal常用方法:

  • public void set(T value) 设置当前线程的线程局部变量的值
  • public T get() 返回当前线程所对应的线程局部变量的值
  • public void remove() 移除当前线程的线程局部变量

​ 当服务器每次发送请求的时候,后端服务器 Tomcat 都对应一个新的线程。

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、通过,放行
            BaseContext.setCurrentId(empId);
            return true;
        } catch (Exception ex) {
            //4、不通过,响应401状态码
            response.setStatus(401);
            return false;
        }
    }

​ 上面的是一个验证令牌的方法,可以看到当从令牌中取出了员工的 id 后,封装到了 ThreadLocal 中,BaseContext 是为了使用方便,将 ThreadLocal 中的方法进行了进一步的封装,这时候我们就可以从线程的空间中取出这个 id,来为上述的两个未处理的值赋值。

2、员工分页查询

  • 需求设计和分析
  • 代码开发

1)需求设计与分析

  • 需要根据页码来展示员工的信息
  • 每页显示十条数据
  • 分页查询的时候要根据需要输入员工的姓名进行查询
接口设计

在这里插入图片描述

![-<img src="../../AppData/Roaming/Typora/typora-user-images/image-20231020115149731.png" alt="image-20231020115149731" style="zoom:50%;" />](https://img-blog.csdnimg.cn/52dd9443bb1b42d9a79e80d581f2d2f9.png)

2)代码开发

根据分页查询的接口设定相应的 DTO

​ 和上述的问题相同,我们虽然有可以接收前端传输的内容的实体类,但是传来的数据没有包含太多实体类的数据。

@Data
public class EmployeePageQueryDTO implements Serializable {

    //员工姓名
    private String name;

    //页码
    private int page;

    //每页显示记录数
    private int pageSize;

}
设计 PageResult 对象

​ 后续为了分页查询的方便,我们做了统一的规定,将返回的数据统一设定为 PageResult 对象,通过上述的接口分析,我们需要设计其包含两个属性,存储返回的数据的属性和总记录数。

@Data
@AllArgsConstructor
@NoArgsConstructor
public class PageResult implements Serializable {

    private long total; //总记录数

    private List records; //当前页数据集合

}
根据接口创建分页查询的方法
@GetMapping("/page")
@ApiOperation("员工的分页查询")
public Result<PageResult> page(EmployeePageQueryDTO employeePageQueryDTO) {
    log.info("员工分页查询");
    PageResult pageResult = employeeService.pageQuery(employeePageQueryDTO);
    return Result.success(pageResult);
}
使用分页插件来简化开发
    <dependency>
        <groupId>com.github.pagehelper</groupId>
        <artifactId>pagehelper-spring-boot-starter</artifactId>
    </dependency>
@Override
    public PageResult pageQuery(EmployeePageQueryDTO employeePageQueryDTO) {
        //开始分页查询
        PageHelper.startPage(employeePageQueryDTO.getPage(), employeePageQueryDTO.getPageSize());

        Page<Employee> page = employeeMapper.pageQuery(employeePageQueryDTO);

        return new PageResult(page.getTotal(), page.getResult());
    }

​ 这里可以通过 PageHelper 来协助分页查询的功能,里面传入两个参数一个是当前的页码,另一个是每一页的需要的元素个数。
​ 这时候我们再调用分页查询的方法,并且将他的返回对象设置为 Page,那当我们进行分页查询的时候,就会自动的为我们添加 limit 语句。

	return new PageResult(page.getTotal(), page.getResult());

​ 这时候我们再使用这个对象的 getTotal 和 getResult 方法就可以得到查询到的所有元素以及查询到的总页数。

​ 后续就继续正常的使用一般的步骤,但写 sql 语句的时候不用再写 limit 语句,且注意不能加分号,该插件默认会在这个 sql 语句的后面加上 limit 修饰。

    <select id="pageQuery" resultType="com.sky.entity.Employee">
        select * from employee
                 <where>
                     <if test="name != '' and name != null">
                         and name like concat('%', #{name}, '%') and name != ""
                     </if>
                 </where>
        order by create_time
    </select>

3) 功能测试

​ 可以通过前后端联调和接口文档测试的方式来进行测试.

4) 代码完善

日期显示的问题

`

​ 观察返回的对象 json 对象可以发现日期显示的是以上图的形式返回的,体现在数据的显示就会发现日期显示的格式有问题.

解决方式

  • 方式一:在属性上加入注解,对日期进行格式化
  • 方式二:在 WebMvcConfiguration 中扩展Spring MVC的消息转换器,统一对日期类型进行格式化处理
protected void extendMessageConverters(List<HttpMessageConverter<?>> converters) {
	log.info("开始拓展消息转换器");
	// 创建一个消息转换器对象
	MappingJackson2HttpMessageConverter converter = new MappingJackson2HttpMessageConverter();
	//创建对象转换器,可以将 Java 对象转换为 json 字符串
	converter.setObjectMapper(new JacksonObjectMapper());
	
	// 将我们自己的转换器放入 springMVC 框架的容器中
	converters.add(0, converter);
}

3、禁用和启用员工的功能

  • 需求设计和分析
  • 代码开发
  • 功能测试

1) 需求设计分析

业务规则

  • 可以对状态为启用的员工张航进行禁用操作
  • 可以对状态为禁用的员工账号进行启用的操作
  • 状态为禁用的员工的账号不能登录系统

接口设计

-

`

根据接口的路径信息和传输的参数来设计方法

2) 代码开发

    @PostMapping("status/{status}")
    @ApiOperation("启用禁用员工账号")
    public Result EnableAndDisableEmp(@PathVariable Integer status,
                                               @RequestParam(name = "id") long id) {
        log.info("启用禁用员工账号{}{}", status, id);
        employeeService.enableAndDisableEmp(status, id);
        return Result.success();
    }
    @Override
    public void enableAndDisableEmp(Integer status, long id) {
        Employee build = Employee.builder()
                .status(status)
                .id(id)
                .build();
        employeeMapper.update(build);
    }

​ 这里借用了插件lomcat ,可以通过在类上面加入 @Build 注解来在构建类的时候可以使用 builder 方法

​ 因为后面 update 方法会经常被使用,我们可以来在 sql 语句中加入判断数据是否为空来提高代码的复用性.

    <update id="update">
        update employee
    <set>
        <if test="name != null"> name = #{name}, </if>
        <if test="username != null">username = #{username},</if>
        <if test="password != null">password = #{password},</if>
        <if test="phone != null">phone = #{phone},</if>
        <if test="sex != null">sex = #{sex},</if>
        <if test="idNumber != null">id_number = #{idNumber},</if>
        <if test="status != null"> status = #{status}, </if>
    </set>
    where id = #{id}
    </update>

​ 前端通过发送 id 和要修改的 status 来完成对登录状态的修改

3) 功能测试

​ 通过前后端联调或者接口文档的方式均可以进行测试

4、编辑员工

​ 这里需要完成两个操作,首先是完成通过 id 查询对象,来实现修改的复显功能然后通过修改功能来实现员工的编辑.

    @GetMapping("/{id}")
    @ApiOperation("通过 id 查询员工信息")
    public Result<Employee> getById(@PathVariable Long id) {
        Employee empById = employeeService.getEmpById(id);
        empById.setPassword("******");
        return Result.success(empById);
    }
    @PutMapping
    public Result editEmp(@RequestBody EmployeeDTO employeeDTO) {
        employeeService.editEmp(employeeDTO);
        return Result.success();
    }

5、导入分类模块的方法

​ 将文件中的代码导入即可,注意要按照从底层到高层的顺序来导入

​

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
黑马SpringBoot是一个基于Java语言开发的Web应用开发框架。它通过简化配置和提供一系列的自动化工具,使得开发人员能够更加快速和便捷地构建和部署应用程序。黑马SpringBoot框架的核心思想是约定大于配置,即通过一些默认的规则和约定,减少开发人员需要手动配置的工作,从而提高开发效率。 在使用黑马SpringBoot框架时,可以通过使用@Import注解来导入自定义的配置类,如引用中的示例代码中的@Import(MyImportSelector.class)。这样可以将需要自动配置的内容放入这个配置类中,使得在SpringBoot启动时无需对SpringBoot进行相关的手动配置,如引用所述。 另,如果想要使用黑马SpringBoot框架整合ActiveMQ,可以通过在项目的pom.xml文件中添加相应的依赖,如引用所示。这样就可以使用SpringBoot提供的ActiveMQ的starter,简化了对ActiveMQ的配置和集成过程。 总结来说,黑马SpringBoot是一个基于Java语言开发的Web应用开发框架,通过简化配置和提供自动化工具,使得开发人员能够更加快速和便捷地构建和部署应用程序。可以通过导入自定义的配置类和使用相应的starter来实现对SpringBoot的自动配置和集成。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* *3* [黑马SpringBoot --原理篇](https://blog.csdn.net/D_boj/article/details/129494265)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] - *2* [黑马SpringBoot笔记](https://blog.csdn.net/qq_39634715/article/details/127869532)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] [ .reference_list ]
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

*Soo_Young*

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值