瑞吉外卖开发笔记 二

news2024/11/27 2:41:58

1、完善登录功能

问题分析

前面我们已经完成了后台系统的员工登录功能开发,但是还存在一个问题:用户如果不登录,直接访问系统首页面,照样可以正常访问。

这种设计并不合理,我们希望看到的效果应该是,只有登录成功后才可以访问系统中的页面,如果没有登录则跳转到登录页面。

那么,具体应该怎么实现呢?

答案就是使用过滤器或者拦截器,在过滤器或者拦截器中判断用户是否已经完成登录,如果没有登录则跳转到登录页面

代码实现

实现步骤:

  • 创建自定义过滤器LoginCheckFilter
@WebFilter(filterName = "LoginCheckFilter",urlPatterns = "/*")
@Slf4j
public class LoginCheckFilter implements Filter {
    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        HttpServletRequest request=(HttpServletRequest) servletRequest;
        HttpServletResponse response=(HttpServletResponse) servletResponse;
        log.info("拦截到请求:{}",request.getRequestURI());
        filterChain.doFilter(request,response);
    }
}
  • 在启动类上加入注解@ServletComponentScan
@Slf4j
@SpringBootApplication
@ServletComponentScan
public class ReggieApplication {
    public static void main(String[] args) {
        SpringApplication.run(ReggieApplication.class,args);
        log.info("项目启动成功!!!");
    }
}
  • 完善过滤器的处理逻辑

过滤器具体的处理逻辑如下:

1、获取本次请求的URI

2、判断本次请求是否需要处理

3、如果不需要处理,则直接放行

4、判断登录状态,如果已登录,则直接放行

5、如果未登录则返回未登录结果

image

/**
 * @ClassName: LoginCheckFilter
 * @Description: 检查用户是否已经完成登录
 * @author: 名字
 * @date: 2022/5/10  15:57
 */
@WebFilter(filterName = "LoginCheckFilter",urlPatterns = "/*")
@Slf4j
public class LoginCheckFilter implements Filter {
    //路径匹配器,支持通配符
    public static final AntPathMatcher PATH_MATCHER=new AntPathMatcher();

    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        HttpServletRequest request=(HttpServletRequest) servletRequest;
        HttpServletResponse response=(HttpServletResponse) servletResponse;

//        1、获取本次请求的URI
        String requestURI = request.getRequestURI();

        log.info("拦截到请求:{}",requestURI);

//        定义不需要处理的请求路径
        String[] urls=new String[]{
                "/employee/login",
                "/employee/logout",
                "/backend/**",
                "/front/**"
        };
//        2、判断本次请求是否需要处理
        boolean check = check(urls, requestURI);
//        3、如果不需要处理,则直接放行
        if(check){
            log.info("本次请求{}不需要处理",requestURI);
            filterChain.doFilter(request,response);
            return;
        }
//        4、判断登录状态,如果已登录,则直接放行
        if(request.getSession().getAttribute("employee")!=null){
            log.info("用户已登录,用户id为:{}",request.getSession().getAttribute("employee"));
            filterChain.doFilter(request,response);
            return;
        }
        log.info("用户未登录");
//        5、如果未登录则返回未登录结果,通过输出流向客户端页面响应数据
        response.getWriter().write(JSON.toJSONString(R.error("NOTLOGIN")));
        return;
    }
    //路径匹配,检查本次请求是否需要放行
    public boolean check(String[] urls,String requestURI){
        for (String url : urls) {
            boolean match = PATH_MATCHER.match(url, requestURI);
            if(match==true){
                return true;
            }
        }
        return  false;
    }
}

2、新增员工

需求分析

后台系统中可以管理员工信息,通过新增员工来添加后台系统用户。点击 添加员工 按钮跳转到新增页面,如下:

image

数据模型

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

代码开发

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

  • 页面发送ajax请求,将新增员工页面中输入的数据以json的形式提交到服务端
  • 服务端Controller接收页面提交的数据并调用Service将数据进行保存
  • Service调用Mapper操作数据库,保存数据

    image

    //新增员工
    @PostMapping
    public R<String> save(HttpServletRequest request, @RequestBody Employee employee){
        log.info("新增员工,员工信息:{}",employee.toString());
        //设置初始密码,需要进行md5加密处理
        employee.setPassword(DigestUtils.md5DigestAsHex("123456".getBytes()));

        employee.setCreateTime(LocalDateTime.now());
        employee.setUpdateTime(LocalDateTime.now());

        Long empId = (Long) request.getSession().getAttribute("employee");

        employee.setCreateUser(empId);
        employee.setUpdateUser(empId);

        employeeService.save(employee);

        return R.success("新增员工成功");
    }

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

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

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

2、使用异常处理器进行全局异常捕获

@ControllerAdvice(annotations = {RestController.class, Controller.class})
@ResponseBody
@Slf4j
public class GlobalExceptionHandler {

    //进行异常处理方法
    @ExceptionHandler(SQLIntegrityConstraintViolationException.class)
    public R<String> exceptionHandler(SQLIntegrityConstraintViolationException ex){
        log.error(ex.getMessage());

        if(ex.getMessage().contains("Duplicate entry")){
            String[] split = ex.getMessage().split(" ");
            String msg=split[2]+"已存在";
            return R.error(msg);
        }

        return R.error("未知错误");
    }
}

总结

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

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

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

3、员工信息分页查询

需求分析

系统中的员工很多的时候,如果在一个页面中全部展示出来会显得比较乱,不便于查看,所以一般的系统中都会以分页的方式来展示列表数据。

image

代码开发

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

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

配置MP分页插件

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

员工信息分页查询

@GetMapping("/page")
public R<Page> page(int page, int pageSize, String name) {
    log.info("page={},pageSize={},name={}", page, pageSize, name);

    //构造分页构造器
    Page pageInfo=new Page(page,pageSize);

    //构造条件构造器
    LambdaQueryWrapper<Employee> queryWrapper=new LambdaQueryWrapper();
    //添加过滤条件
    queryWrapper.like(!StringUtils.isEmpty(name),Employee::getName,name);
    //添加排序条件
    queryWrapper.orderByDesc(Employee::getUpdateTime);

    //执行查询
    employeeService.page(pageInfo,queryWrapper);

    return R.success(pageInfo);
}

功能测试

image

4、启用/禁用员工账号

需求分析

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

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

代码开发

页面中是怎么做到只有管理员admin能够看到启用、禁用按钮的?

image

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

1、页面发送ajax请求,将参数(id、 status)提交到服务端

2、服务端Controller接收页面提交的数据并调用Service更新数据

3、Service调用Mapper操作数据库

image

页面中的ajax请求是如何发送的?

image

根据id修改员工信息

@PutMapping
public R<String> update(HttpServletRequest request,@RequestBody Employee employee){
    log.info(employee.toString());

    Long empId = (Long) request.getSession().getAttribute("employee");
    employee.setUpdateTime(LocalDateTime.now());
    employee.setUpdateUser(empId);
    employeeService.updateById(employee);

    return R.success("员工信息修改成功");
}

测试过程中没有报错,但是功能并没有实现,查看数据库中的数据也没有变化。观察控制台输出的SQL:

image

SQL执行的结果是更新的数据行数为0,仔细观察id的值,和数据库中对应记录的id值并不相同

image

代码修复

通过观察控制台输出的SQL发现页面传递过来的员工id的值和数据库中的id值不一致,这是怎么回事呢?

分页查询时服务端响应给页面的数据中id的值为19位数字,类型为long

页面中js处理long型数字只能精确到前16位,所以最终通过ajax请求提交给服务端的时候id就改变了

前面我们已经发现了问题的原因,即js对long型数据进行处理时丢失精度,导致提交的id和数据库中的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
protected void extendMessageConverters(List<HttpMessageConverter<?>> converters) {
    //创建消息转换器
    MappingJackson2HttpMessageConverter messageConverter = new MappingJackson2HttpMessageConverter();
    //设置对象转换器,底层使用Jackson将Java转换为json
    messageConverter.setObjectMapper(new JacksonObjectMapper());
    //将上面的消息转换器对象追加到mvc框架的转换器集合中
    converters.add(0,messageConverter);
    super.extendMessageConverters(converters);
}

5、编辑员工信息

需求分析

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

image

代码开发

在开发代码之前需要梳理一下操作过程和对应的程序的执行流程:

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

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

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

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

//根据id查询员工信息
@GetMapping("/{id}")
public R<Employee> getById(@PathVariable String id){
    log.info("根据id查对象");
    Employee emp = employeeService.getById(id);
    if(emp!=null){
        return R.success(emp);
    }
    return R.error("没有查询到该用户信息");
}

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

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

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

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

注意:add.html页面为公共页面,新增员工和编辑员工都是在此页面操作,所以该代码部分与之前添加员工代码对应,不需要重写。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/781959.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

JVM运行时数据区——字符串常量池位置的调整

在JDK6及之前&#xff0c;使用永久代来实现方法区&#xff0c;字符串常量池(StringTable)是在永久代(方法区)中的&#xff0c;但是方法区的回收效率不高&#xff0c;在Full GC时才会回收。 在JDK7中&#xff0c;将字符串常量池转移到了堆中&#xff0c;分配在年轻代和老年代中。…

ROS——roslaunch传参注意

本文就总结自己最近开发项目中出现的一些roslaunch有关传参的注意事项说明一下&#xff0c;以免下次有犯错 roslaunch 文件传入到.cpp 这里我采用传参比较简单的方式&#xff0c;还有其他方式&#xff0c;不过这种最为简便 nh.param<xxx>("roslaunch中的名字,如A…

基于 H5 的画图工具

完整资料进入【数字空间】查看——baidu搜索"writebug" 第一章 绪论 1.1 研究的背景 计算机图形学是随着计算机及其外围设备而产生和发展起来的。它是近代计算机科学 与雷达电视及图象处理技术的发展汇合而产生的硕果。在造船、航空航天、汽车、电子、 机械、土建工…

Spring 容器

Spring 容器 两个核心接口&#xff1a;BeanFactory 和 ApplicationContext&#xff08;是BeanFactory的子接口&#xff09;&#xff0c;生成Bean实例并管理Bean的工厂 Bean 对象 Spring管理的基本单位&#xff0c;在基于Spring应用中&#xff0c;所有的组件都可以理解为是一…

【前端|CSS系列第4篇】CSS布局之网格布局

前言 最近在做的一个项目前台首页有一个展示词条的功能&#xff0c;每一个词条都以一个固定大小的词条卡片进行展示&#xff0c;要将所有的词条卡片展示出来&#xff0c;大概是下面这种布局 每一行的卡片数目会随着屏幕大小自动变化&#xff0c;并且希望整个卡片区域周围不要…

【CSDN竞赛】练习题(练手题)及解析(含代码+注释):小玉家的电费;饿龙咆哮-逃离城堡;收件邮箱;寻找宝藏山

订阅专栏,学习更多干货知识! 为想要参与CSDN竞赛的朋友提供几道练手题目及解决方案。含有题目描述以及不同编程语言的解决方案。 💿目录 🏮一、题目一:小玉家的电费📂1.1 题目描述📂1.2 示例代码🏮二、题目二:饿龙咆哮-逃离城堡📂2.1 题目描述📂2.2 示例代码…

基于PySceneDetect的视频场景变换侦测与处理

剪映中集成了一个智能镜头分割的功能,其实是基于python的三方库PySceneDetect来实现的,主要用于对视频进行分析,寻找场景切换或剪辑。 不过一个一个处理起来比较麻烦,这里介绍一个python的三方库实现自动化批量处理。 文章目录 PySceneDetect主要功能特征PySceneDetect的安…

股票基金入门知识

1.开盘价和收盘价如何产生 时间9:30-11:30 13:00-15:00 集合竞价时间段&#xff1a;9:15-9:25 以此产生开盘价 最后集中竞价时间段&#xff1a;深市14:57-15:00 &#xff0c;以此产生收盘价。 沪市则采用最后一分钟加权得出收盘价影响股价的因素 市场投资情绪&#xff0c;宏观…

Java实现字典树单词插入、查找以及删除

文章目录 前言题目思路代码实现测试用例结果输出 结语 前言 字典树又称为前缀树或Trie树&#xff0c;是处理字符串常见的数据结构。 字典树是一种树形结构&#xff0c;优点是利用字符串的公共前缀来节约存储空间&#xff0c;比如插入"abc"、“abcd”、 “abd”、“…

JTAG 和 SWD 接口定义

写在前面&#xff1a; 本文章旨在总结备份、方便以后查询&#xff0c;由于是个人总结&#xff0c;如有不对&#xff0c;欢迎指正&#xff1b;另外&#xff0c;内容大部分来自网络、书籍、和各类手册&#xff0c;如若侵权请告知&#xff0c;马上删帖致歉。 目录 JTAG引脚分布接…

js的事件循环机制(详解)

答题思路&#xff1a; ● 首先基本定义&#xff0c;宏任务和微任务 ● 事件循环机制执行顺序 ● async / await 执行顺序 一、什么是事件循环 事件循环机制就是一种同步编程模型&#xff0c;用于异步处理操作。当代码中遇到需要等待一部操作结果的语句时&#xff0c;js引擎不会…

如何区分接口测试和功能测试

接口测试和功能测试的区别&#xff1a; 2023最新Jmeter接口测试从入门到精通&#xff08;全套项目实战教程&#xff09; 本文主要分为两个部分&#xff1a; 第一部分&#xff1a;主要从问题出发&#xff0c;引入接口测试的相关内容并与前端测试进行简单对比&#xff0c;总结两者…

5.2.tensorRT基础(2)-使用onnx解析器来读取onnx文件(源码编译)

目录 前言1. ONNX解析器2. libnvonnxparser.so3. 源代码编译4. 补充知识总结 前言 杜老师推出的 tensorRT从零起步高性能部署 课程&#xff0c;之前有看过一遍&#xff0c;但是没有做笔记&#xff0c;很多东西也忘了。这次重新撸一遍&#xff0c;顺便记记笔记。 本次课程学习 t…

5.3.tensorRT基础(2)-从下载onnx-tensorrt到配置好并运行起来

目录 前言1. ONNX解析器更新总结 前言 杜老师推出的 tensorRT从零起步高性能部署 课程&#xff0c;之前有看过一遍&#xff0c;但是没有做笔记&#xff0c;很多东西也忘了。这次重新撸一遍&#xff0c;顺便记记笔记。 本次课程学习 tensorRT 基础-从下载 onnx-tensorrt 到配置好…

js基础算法05--查找重复的字符

开始之前 了解什么是hash table &#xff08;哈希表&#xff09; for 循环中break 和 return的区别 哈希表的方式解 第一种方式&#xff0c;用哈希表。循环俩次&#xff0c;第一次储存当前字符出现的频率&#xff0c;没出现一次&#xff0c;对象key的评率value就加一。然后第…

vue3+Luckysheet实现表格的在线预览编辑(electron可用)

前言&#xff1a; 整理中 官方资料&#xff1a; 1、github 项目地址https://github.com/oy-paddy/luckysheet-vue-importAndExport/tree/master/https://github.com/oy-paddy/luckysheet-vue-importAndExport/tree/master/ 2、xlsx vue3 json数据导出excel_vue3导出excel_羊…

Zookeeper的基本概念以及安装

Zookeeper简介 Zookeeper是一个分布式的(多台机器同时干一件事情),开源的分布式应用程序协调服务,是Google公司Chubby产品,是Hadoop和Base重要的组件,.它是一个分布式应用程序提供一致性的服务的软件,提供的功能包括:配置服务,域名服务,分布式同步,组服务等 Zookeeper目…

Python生成中文词云图

词云&#xff08;word cloud&#xff09;&#xff0c;也称标签云&#xff0c;主要用于展示文本数据。把每个单词按照其重要性进行展示&#xff0c;主要通过字体和颜色进行区分。Python提供了wordcloud库&#xff0c;可以很容易实现词云图。“词云”看起来比较美观易理解&#x…

Python学习(十六)柱状图

zdaPython学习&#xff08;十四&#xff09;折线图开发_yikuaidabin的博客-CSDN博客 案例数据资源 ↑ """演示基础柱状图的开发 """ from pyecharts.charts import Bar from pyecharts.options import LabelOpts # 使用Bar构建基础柱状图 bar …

视频对比工具(基于python+ffmpeg+airtest实现视频抽帧比较工具)

VideoDiff&#xff1a;基于ffmpeg&#xff0c;实现视频抽帧比较工具 使用场景&#xff1a;在视频渲染模块发生迭代&#xff0c;快速回归测试其产出的视频是否存在问题&#xff0c;从而节省人工回归成本 源码地址&#xff1a;https://github.com/jiangliuer32/VideoDiff 原理图…