阅读提醒:最重要的内容都是我手打的字,还有截图上的红字备注部分。
nginx是一个服务器,主要部署一些静态的资源,包括后面做tomcat的集群, 可以接收前端的请求,然后分发给各个tomcat
第一步搭建数据库:
通过url访问页面,可以访问到静态资源页面,也可以访问到controller页面,但是静态资源页面是不能接受后端的数据的,所以会用到jsp页面,或者用Vue来返回数据。
开发小技巧
写业务需求的时,先考虑前端传来的数据是什么,数据格式是什么,我要返回的值是什么,请求的url是什么?等等想清楚这些问题之后先不着急写业务代码,而是接受前端数据并进行log打印,打断点调试一下,看看能不能接收到前端数据,再开始辨析业务需求。
业务开发:(与面试官交流内容)
登录、退出功能
分三步走:
在登录界面上,输入用户名和密码,点击登录按钮就会发送请求,最终这个请求就会请求到服务端的一些组件,比如说先请求到我们的controller,然后通过controller调我们的service,然后通过service调mapper,最终和数据库交互,根据用户名和密码进行查询。
controller对请求进行处理会返回数据,这些数据需要用一个类来封装,相当一个通用结果类,服务端响应的所有结果最终都会包装为此类型返回给前端页面,我们将此类定义为R,并将其放在common包下。
//2、根据页面提交的用户名username查询数据库
LambdaQueryWrapper<Employee> queryWrapper = new LambdaQueryWrapper<>(); //查询包装类
queryWrapper.eq(Employee::getUsername,employee.getUsername()); //比较是否一致
Employee emp = employeeService.getOne(queryWrapper); //username是唯一约束,是不能重复的,所以可以调用getOne方法
//将此用户名的对象赋值给emp
//4、密码比对,如果不一致则返回登录失败结果
if(!emp.getPassword().equals(password)){ //判断数据库中的密码和前端传来的密码password 是否一致
return R.error("登录失败");
}
调试要打断点再调试,然后一步一步往下走,看数据的变化。
如果改了后端的配置文件信息,前端没有生效,原因是浏览器中有缓存,所以需要清理一下历史记录(cookie),再刷新就ok
账号退出的需求:
/**
* 员工退出
* @param request
* @return
*/
@PostMapping("/logout")
public R<String> logout(HttpServletRequest request){
//清理Session中保存的当前登录员工的id
request.getSession().removeAttribute("employee");
return R.success("退出成功");
}
员工管理业务开发
先完成整个架子,再去填充过滤器的细节。
使用参数servletRequest,将其强转为HttpServletRequest,然后调用request.getRequestURL()方法
接下来放行,如何放行?用参数 filterChain调用doFilter( , )方法,将request和response放进去,如:
再进行访问页面,发现控制台和日志有输出,证明我们的过滤器架子搭好了:
用Session判断登录状态,用AntPathMatcher类的match方法进行地址匹配,
这一章是针对员工实体进行一系列的操作,是带分页的,所以会有分页查询的功能,而且点击编辑可以对员工的信息进行修改,还有账号的启用和禁用,点击添加员工可以跳转页面,并进行信息填写
新增员工
业务开发中,需求分析和数据模型是最关键的一块,往后的代码开发和功能测试就是对需求分析一一翻译的过程。
点击新增员工,页面跳转到一个表单,输入信息,点击保存,前端页面发送ajax请求,将数据以json的方式传给controller,它再调用Service将数据进行保存,Service调用Mapper进行查询数据库,看username是否重复,因为username字段设置的唯一,不重复则将数据插入到数据库中,再跳转到员工管理。
/**
* 新增员工
* @param employee
* @return
*/
@PostMapping()
public R<String> save(HttpServletRequest request,@RequestBody Employee employee){ //这个返回值是返回给前端
log.info("员工信息:{}",employee.toString());
employee.setPassword(DigestUtils.md5DigestAsHex("123456".getBytes()));
//获取当前时间
employee.setCreateTime(LocalDateTime.now());
employee.setUpdateTime(LocalDateTime.now());
//获得当前用户的id(用session获得
long empId = (long) request.getSession().getAttribute("employee");
employee.setCreateUser(empId);
employee.setUpdateUser(empId);
employeeService.save(employee);
return R.success("新增员工成功");
}
因为数据库当中的username设置的是唯一,所以当我们输入重复的username时,会抛异常,两种处理方法
手动捕获和使用异常处理器进行全局异常捕获。
推荐使用异常处理器(Handler),如果做?
创建一个异常处理器类,在上面打上@ControllerAdvice注解,advice是通知的意思,然后在自定义的方法上打上@ExceptionHandler(SQLIntegrityConstraintViolationException.class),括号内填入捕捉异常的类型,代码如下:
@ControllerAdvice(annotations = {RestController.class, Controller.class})
@ResponseBody
@Slf4j
public class GlobalExceptionHandler {
/**
* 异常处理方法
* @return
*/
@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("未知错误");
}
}
下次再输入重复的username就可以捕获异常,并在前端输出“***已存在”
员工信息分页查询
展现员工信息的时候,全部展示出来会很乱,所以一般采用分页的方式来展示数据
第一步配置MP的分页插件
/**
* 配置MP的分页插件
*/
@Configuration
public class MybatisPlusConfig {
@Bean
public MybatisPlusInterceptor mybatisPlusInterceptor(){
MybatisPlusInterceptor mybatisPlusInterceptor = new MybatisPlusInterceptor();
mybatisPlusInterceptor.addInnerInterceptor(new PaginationInnerInterceptor());
return mybatisPlusInterceptor;
}
}
controller方法返回值的核心的,前端页面需要什么数据我们就给他什么数据。
然后在页面上的按姓名查询会在url中拼接name,所以参数一共三个。
构造分页条件,我们只需要告诉MP我们的page和pageSize是多少就可以了,他会帮我们做分页查询
这里我们推荐使用like,对于姓名推荐使用like
2023.2.3
最终的代码:
@GetMapping("/page")
public R<Page> page(int page,int pageSize,String name){
log.info("第{}页,每页{}数据,姓名{}",page,pageSize,name);
//构造分页构造器
Page pageInfo = new Page();
//构造条件构造器
LambdaQueryWrapper<Employee> queryWrapper = new LambdaQueryWrapper<>();
//过滤条件
queryWrapper.like(StringUtils.isNotEmpty(name),Employee::getName,name);
//添加排序条件
queryWrapper.orderByDesc(Employee::getUpdateTime);
//执行查询
employeeService.page(pageInfo,queryWrapper);
return R.success(pageInfo) ;
}
启用禁用员工账号
如何实现管理员可以看到禁用按钮,其实是前端做了处理:
前面返回的数据是json,所以加上RequsetBody
代码:
前端的数据是正常的,经过js一处理就不正常了,问题出在js,因为精度问题。所以将long类型给为String类型。
需要一个Java对象转json或者json对象转Java的类,这个类不需要我们写,到时候直接CV,知道作用就可以了。
编辑员工信息:
/**
* 根据员工id查询员工信息
* @param id
* @return
*/
@GetMapping("/{id}")
public R<Employee> getById(@PathVariable Long id ){
Employee employee = employeeService.getById(id);
return R.success(employee);
}
@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("修改员工信息成功");
}
第二阶段目录(菜品分类管理)
公共字段填充
@TableField(fill = FieldFill.INSERT)
private LocalDateTime createTime;
@TableField(fill = FieldFill.INSERT)
private LocalDateTime updateTime;
@TableField(fill = FieldFill.INSERT)
private Long createUser;
@TableField(fill = FieldFill.INSERT_UPDATE)
private Long updateUser;
这里因为获取不到id,所以暂时写死了,后面再来解决这个问题
解决方案:
新增分类
添加分类,直接使用save方法,将接受到的json数据放入。
@PostMapping
public R<String> save(@RequestBody Category category){
categoryService.save(category);
return R.success("新增分类成功");
}
分类信息分页查询
编写一个controller的前提是,明确它要返回什么类型,接受哪几个参数,什么类型的请求映射,路径要写什么合适,具体的业务实现是什么?
@GetMapping("/page")
public R<Page> page(int page ,int pageSize){
Page<Category> pageInfo = new Page(page,pageSize);
LambdaQueryWrapper<Category> queryWrapper = new LambdaQueryWrapper();
queryWrapper.orderByAsc(Category::getSort);
categoryService.page(pageInfo,queryWrapper);
return R.succes(pageInfo);
}
训练中的不足
关于MP的分页查询不熟很熟悉,以及对于LambdaQueryWrapper的使用比较生疏,以及对这条语句categoryService.page(pageInfo,queryWrapper);
的使用
Redis
redis远程连接,