瑞吉外卖(二)

1、完整登录功能

问题分析

​ 前面我们已经完成了简单的用户登录功能,但是有一个问题:如果用户不登陆,还是可以访问页面,我们希望的是当跳转到一个新的页面,判断用户是否登录。我们可以添加拦截器或者过滤器,在过滤器或者拦截器中判断用户是否已经完成登录,如果没有登录则跳转到登录页面

代码实现

使用拦截器实现登录拦截

@Slf4j
public class LoginCheckInterceptor implements HandlerInterceptor {
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        if(request.getSession().getAttribute("employee")==null){
            response.getWriter().write(JSON.toJSONString(Result.error("NOTLOGIN")));
            log.info("LoginCheckInterceptor拦截请求成功:{}",request.getRequestURL());
            return false;
        }
        log.info("LoginCheckInterceptor拦截请求失败:{}",request.getRequestURL());
        return true;
    }
}

并在配置类中注册拦截器

@Configuration
public class InterceptorConfig implements WebMvcConfigurer {
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(new LoginCheckInterceptor())
                .addPathPatterns("/**")
                .excludePathPatterns( "/employee/login","/employee/logout", "/backend/**", "/front/**");
    }
}

注意:其实仅仅做到这一步拦截器是起不了作用的,我们知道在开启静态资源映射的时候,配置类继承了WebMvcConfigurationSupport类,通过查看WebMvcAutoConfiguration类:

@ConditionalOnClass({Servlet.class, DispatcherServlet.class, WebMvcConfigurer.class})
//容器中已经有WebMvcConfigurationSupport类的对象了,那么这个自动配置类将不会生效
@ConditionalOnMissingBean({WebMvcConfigurationSupport.class})
@AutoConfigureOrder(-2147483638)
public class WebMvcAutoConfiguration {

可知WebMvcConfigurationSupport 在整个应用程序中只会生效一个,如果用户已经实现了 WebMvcConfigurationSupport,则 WebMvcConfigurer 将不会生效,换句话来说,WebMvcConfigurer 的所有实现类将不会生效。也就是说登录拦截器不生效了。

我的解决方法是将WebMvcConfigurationSupport 类换成WebMvcConfiguration接口:

@Slf4j
@Configuration
public class WebMvcConfig implements WebMvcConfigurer {
    @Override
    public void addResourceHandlers(ResourceHandlerRegistry registry) {
        log.info("开启静态资源映射");
        registry.addResourceHandler("/backend/**").addResourceLocations("classpath:/backend/");
        registry.addResourceHandler("/front/**").addResourceLocations("classpath:/front/");
    }
}

2、添加员工

数据模型

新增员工,其实就是将我们新增页面录入的员工数据插入到employee表。需要注意,employee表中对username字段加入了唯一约束,因为username是员工的登录账号,必须是唯一

代码开发

在开发代码之前,需要梳理一下整个程序的执行过程:

  • 页面发送ajax请求,将新增员工页面中输入的数据以json的形式提交到服务端
  • 服务端Controller接收页面提交的数据并调用Service将数据进行保存
  • Service调用Mapper操作数据库,保存数据
@PostMapping
public Result<String> save(@RequestBody Employee employee,HttpSession session){
    log.info("save员工:{}",employee);
    employee.setPassword(DigestUtils.md5DigestAsHex("123456".getBytes()));
    Date date = new Date();
    employee.setCreateTime(date);
    employee.setUpdateTime(date);
    Long empId = (Long) session.getAttribute("employee");
    employee.setCreateUser(empId);
    employee.setUpdateUser(empId);
    employeeService.save(employee);
    return Result.success(null);
}

前面的程序还存在一个问题,由于employee表中对该字段加入了唯一约束,当新增员工的账号已经存在时,此时程序会抛出异常:
java.sql.SQLIntegrityConstraintViolationException: Duplicate entry 'heniang' for key 'idx_username'

此时需要我们的程序进行异常捕获,通常有两种处理方式:

1、在Controller方法中加入try-catch进行异常捕获

2、使用异常处理器进行全局异常捕获此时程序会抛出异常

@RestControllerAdvice
@Slf4j
public class GlobalExceptionHandler {
    @ExceptionHandler(SQLIntegrityConstraintViolationException.class)
    public Result<String> exceptionHandler(SQLIntegrityConstraintViolationException ex){
        log.info("异常信息:{}",ex.getMessage());
        if (ex.getMessage().contains("Duplicate entry")){
            return Result.error("账号已存在");
        }
        return Result.error("操作失败");
    }
}

总结

1、根据产品原型明确业务需求

2、重点分析数据的流转过程和数据格式

3、通过debug断点调试跟踪程序执行过程

3、员工分页查询

需求分析
  • 页面发送ajax请求,将分页查询参数(page.pageSize、name)提交到服务端
  • 服务端Controller接收页面提交的数据并调用Service查询数据
  • Service调用Mapper操作数据库,查询分页数据
  • Controller将查询到的分页数据响应给页面
  • 页面接收到分页数据并通过ElementUI的Table组件展示到页面上
代码开发

配置Mybatis-Plus的分页插件:

@Configuration
public class MyBatisPlusInterception {
    @Bean
    MybatisPlusInterceptor MybatisPlusInterceptor(){
        MybatisPlusInterceptor mybatisPlusInterceptor = new MybatisPlusInterceptor();
        mybatisPlusInterceptor.addInnerInterceptor(new PaginationInnerInterceptor());
        return mybatisPlusInterceptor;
    }
}

员工分页查询:

@GetMapping("/page")
public Result<Page> page(@RequestParam(value = "page", defaultValue = "1") Integer page,
                         @RequestParam(value = "pageSize", defaultValue = "10") Integer pageSize,
                         @RequestParam(value = "name", required = false) String name) {
    log.info("分页数据——page:{},pageSize:{},name:{}", page, pageSize, name);
    //条件过滤器
    LambdaQueryWrapper<Employee> wrapper = new LambdaQueryWrapper<>();
    wrapper.like(StringUtils.hasLength(name), Employee::getName, name);
    wrapper.orderByDesc(Employee::getUpdateTime);
    Page<Employee> employeePage = employeeService.page(new Page<>(page, pageSize), wrapper);
    return Result.success(employeePage);
}

4、启用/禁用员工状态

需求分析

在员工管理列表页面,可以对某个员工账号进行启用或者禁用操作。账号禁用的员工不能登录系统,启用后的员工可以正常登录。

需要注意,只有管理员(admin用户)可以对其他普通用户进行启用、禁用操作,所以普通用户登录系统后启用、禁用按钮不显示。

前端发生送给服务器的请求:

function enableOrDisableEmployee (params) {
  return $axios({
    url: '/employee',
    method: 'put',
    data: { ...params }//{'id': this.id, 'status': !this.status ? 1 : 0}
  })
}
代码开发

更新员工状态:

@PutMapping
public Result<String> updateStatus(@RequestBody Employee employee,HttpSession session){
    log.info("id:{},status:{}",employee.getId(),employee.getStatus());
    Long empId = (Long) session.getAttribute("employee");
    employee.setUpdateTime(new Date());
    employee.setUpdateUser(empId);
    employeeService.updateById(employee);
    return Result.success("success");
}
代码修复

前面的代码在执行的时候没有异常,但是数据库在进行更新的时候却没有成功。通过调试我们知道服务器响应的数据是没有问题的,但请求参数却跟数据库表中的信息不一致。原因是服务器响应的id格式是19位的Long类型,但前端js对Long类型的处理只能精确到16位,这样id就丢失了精度,造成数据不匹配。

我们可以在服务端给页面响应json数据时进行处理将long型数据统一转为String字符串

具体实现步骤:

**1)**提供对象转换器JacksonObjectMapper,基于Jackson进行Java对象到json数据的转换

/**
 * 对象映射器:基于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_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(BigInteger.class, ToStringSerializer.instance)
                .addSerializer(Long.class, ToStringSerializer.instance)
                .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);
    }
}

2) 在WebMvcConfig配置类中扩展Spring mvc的消息转换器,在此消息转换器中使用提供的对象转换器进行Java对象到json数据的转换

@Override
public void extendMessageConverters(List<HttpMessageConverter<?>> converters) {
    MappingJackson2HttpMessageConverter messageConverter = new MappingJackson2HttpMessageConverter();
    messageConverter.setObjectMapper(new JacksonObjectMapper());
    converters.add(0,messageConverter);
}

5、编辑员工信息

需求分析

在员工管理列表页面点击编辑按钮,跳转到编辑页面,在编辑页面回显员工信息并进行修改,最后点击保存按钮完成编辑操作。

前端接口:

function queryEmployeeById (id) {
  return $axios({
    url: `/employee/${id}`,
    method: 'get'
  })
}
代码开发

1、点击编辑按钮时,页面跳转到add.html,并在url中携带参数[员工id]

2、在add.html页面获取url中的参数[员工id]

3、发送ajax请求,请求服务端,同时提交员工id参数

4、服务端接收请求,根据员工id查询员工信息,将员工信息以json形式响应给页面

@GetMapping("/{id}")
public Result<Employee> getEmpById(@PathVariable("id") Long id){
    Employee employee = employeeService.getById(id);
    return Result.success(employee);
}

5、页面接收服务端响应的json数据,通过VUE的数据绑定进行员工信息回显

6、点击保存按钮,发送ajax请求,将页面中的员工信息以json方式提交给服务端

// 修改员工
function editEmployee (params) {
  return $axios({
    url: '/employee',
    method: 'put',
    data: { ...params }
  })
}

7、服务端接收员工信息,并进行处理,完成后给页面响应

8、页面接收到服务端响应信息后进行相应处理

@PutMapping
public Result<String> update(@RequestBody Employee employee, HttpSession session) {
    log.info("update员工:{}", employee);
    Date date = new Date();
    employee.setUpdateTime(date);
    Long empId = (Long) session.getAttribute("employee");
    employee.setUpdateUser(empId);
    employeeService.updateById(employee);
    return Result.success("修改失败");
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值