项目--苍穹外卖

目录

前端发送的请求,是如何请求到后端服务的?

完善登录功能

前后端 分离开发流程:

导入接口文档 

Swagger

新增员工

员工分页查询

日期格式 错误:

启用禁用员工账号

编辑员工 

导入分类模块功能代码

公共字段自动填充


1.| constant | 存放相关常量类 |
| context | 存放上下文类 |
| enumeration | 项目的枚举类存储 |
| exception | 存放自定义异常类 |
| json | 处理json转换的类 |
| properties | 存放SpringBoot相关的配置属性类 |
| result | 返回结果类的封装 |
| utils | 常用工具类 |
2.| Entity | 实体,通常和数据库中的表对应 |
| DTO | 数据传输对象,通常用于程序中各层之间传递数据 |
| VO | 视图对象,为前端展示数据提供的对象 |

| POJO | 普通Java对象,只有属性和对应的getter和setter |

3.| config | 存放配置类 |
| controller | 存放controller类 |
| interceptor | 存放拦截器类 |
| mapper | 存放mapper接口 |
| service | 存放service类 |
| SkyApplication | 启动类 |

3.先编译一下,然后正常运行

 前后端联调:

nginx反向代理,就是将前端发送的动态请求由nginx转发到后端服务器

好处: 

前端发送的请求,是如何请求到后端服务的?

在使用Nginx作为反向代理的Java项目中,前端发送的请求会被Nginx接收并处理,然后转发给后端服务。以下是一些基本步骤:

客户端(前端)发送请求到Nginx服务器。
Nginx服务器接收请求,并根据配置的规则进行处理。
Nginx根据配置的规则将请求转发给后端服务。
后端服务接收到请求并处理,然后返回响应。
Nginx接收后端服务的响应,并将其返回给客户端。

在这个过程中,Nginx扮演了反向代理的角色,接收客户端的请求并转发给后端服务,同时也将后端服务的响应返回给客户端。通过Nginx,可以实现对请求的负载均衡(把大量的请求按照我们指定的方式均衡的分配给集中的每台服务器)、缓存、安全过滤等处理,提高系统的性能和安全性。 

配置:nginx.conf

server{     

             listen 80;     

             server_name localhost;          

             location /api/ {                     

                             proxy_pass   http://localhost:8080/admin/;  #反向代理     } }

nginx 负载均衡的配置方式: nginx.conf

upstream webservers{     

                server 192.168.100.128:8080;     

                server 192.168.100.129:8080;

}

server{     listen 80;     

                server_name localhost;          

                location /api/ {                   

                                proxy_pass   http://webservers/admin/;  #负载均衡     } }

完善登录功能

问题:员工表中的密码是明文存储,安全性太低。 

1.将密码加密后存储,提高安全性

2.使用MD5加密方式对明文密码加密

1.修改数据库中明文密码,改为MD5加密后的密文

2.修改Java代码,前端提交的密码进行MD5加密后再跟数据库中密码比对

在EmployeeServiceImpl中

//进行md5加密,然后再进行比对

password = DigestUtils.md5DigestAsHex(password.getBytes());

if (!password.equals(employee.getPassword())) {    

//密码错误  

throw new PasswordErrorException(MessageConstant.PASSWORD_ERROR); }

前后端 分离开发流程:

导入接口文档 

json导入YApi

Yapi 是设计阶段使用的工具,管理和维护接口

Swagger 在开发阶段使用的框架,帮助后端开发人员做后端的接口测试

Swagger

使用Swagger你只需要按照它的规范去定义接口及接口相关的信息,就可以做到生成接口文档,以及在线接口调试页面。

Knife4j 是为Java MVC框架集成Swagger生成Api文档的增强解决方案。

1.导入maven坐标

2.在配置类中加入knnife4j相关配置

3.设置静态资源映射,否则接口文档页面无法访问

<dependency>            

<groupId>com.github.xiaoymin</groupId>            

<artifactId>knife4j-spring-boot-starter</artifactId>            

<version>3.0.2</version>

</dependency>

 WebMvcConfiguration中:

@Bean

public Docket docket(){    

    ApiInfo apiInfo = new ApiInfoBuilder()            

                .title(“苍穹外卖项目接口文档”)            

                .version(“2.0”)            

                .description(“苍穹外卖项目接口文档")            

                .build();    

Docket docket = new Docket(DocumentationType.SWAGGER_2)            

           .apiInfo(apiInfo)            

           .select()            

           //指定生成接口需要扫描的包      .apis(RequestHandlerSelectors.basePackage("com.sky.controller"))                          .paths(PathSelectors.any())            

        .build();

return  docket;

}

/* * 
*设置静态资源映射 ,主要是访问接口文档
* @param registry
*/
protected void addResourceHandlers(ResourceHandlerRegistry registry) {
   log.info(“开始设置静态资源映射...");   
   registry.addResourceHandler("/doc.html").addResourceLocations("classpath:/META-INF/resources/");    
   registry.addResourceHandler("/webjars/**").addResourceLocations("classpath:/META-INF/resources/webjars/");
   }

常用注解:

通过注解可以控制生成的接口文档,使接口文档拥有更好的可读性,常用注解如下:

@Api(tags ="员工相关接口")

@ApiOperation(value = "员工登录")    方法上

@ApiOperation("员工退出")

DTO:专注于做数据传输

当前端提交的数据和实体类中对应的属性差别比较大时,建议使用DTO来封装数据

VO

新增员工

@PostMapping
@ApiOperation("新增员工")
public Result save(@RequestBody EmployeeDTO employeeDTO){
log.info("新增员工:{}",employeeDTO);
employeeService.save(employeeDTO);
return Result.success();
}

void save(EmployeeDTO employeeDTO); 

@Override
public void save(EmployeeDTO employeeDTO) {
Employee employee = new Employee();
//employee.setName(employeeDTO.getName());

//对象属性拷贝
BeanUtils.copyProperties(employeeDTO,employee);
//设置账号的状态,默认正常状态1表示正常,0表示锁定
employee.setStatus(StatusConstant.ENABLE);

//设置密码,默认123456
employee.setPassword(DigestUtils.md5DigestAsHex(PasswordConstant.

DEFAULT_PASSWORD.getBytes()));

//设置当前记录的创建时间和修改时间
employee.setCreateTime(LocalDateTime.now());
employee.setUpdateTime(LocalDateTime.now());

//设置当前记录创建人id和修改人id
//TODO 后期需要改为当前登录用户的id
employee.setCreateUser(10L);
employee.setUpdateUser(10L);

employeeMapper.insert(employee);
}

  

@Insert("insert into employee (name,username,password,phone,sex,id_number,create_time,update_time," +
"create_user,update_user)" +"values "+
"(#{name},#{username},#{password},#{phone},#{sex},#{idNumber},#{createTime},#{updateTime},#{createUser},#{updateUser})")
void insert(Employee employee);

 

存在问题

1.录入的用户名已存在,抛出异常后没有处理

2.新增员工时,创建人id和修改人id设置为了固定值 

@ExceptionHandler
public Result exceptionHandler(SQLIntegrityConstraintViolationException ex) {
log.error("异常信息:{}", ex.getMessage());
Duplicate entry 'zhangsan' for key 'employee.idx username
String message = ex.getMessage(); //获得异常信息
if (message.contains("Duplicate entry")) { //判断异常信息里面有没有关键字Duplicate entry
String[] split = message.split(" "); //动态把字符串提取出来,根据空格分割,得到数组对象
String username = split[2]; //第三个,下标为2
String msg = username + MessageConstant.ALREADY_EXISTS; //用户名已存在
return Result.error(msg);
} else {
return Result.error(MessageConstant.UNKNOWN_ERROR);
}
}

 

解析出登录员工id后,如何传递给Service的save方法?

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

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

ThreadLocal常用方法:

public void set(T value)     设置当前线程的线程局部变量的值

public T get()         返回当前线程所对应的线程局部变量的值

public void remove()        移除当前线程的线程局部变量 

注意:客户端发送的每次请求,后端的Tomcat服务器都会分配一个单独的线程来处理请求

员工分页查询

业务规则:

根据页码展示员工信息

每页展示10条数据

分页查询时可以根据需要,输入员工姓名进行查询

返回数据data:

total:总记录数   records:代表当前这一页要展示的数据集合,数据多个,为数组

根据分页查询接口设计对应的DTO:

@Data

public class EmployeePageQueryDTO implements Serializable {    

//员工姓名    

private String name;    

//页码    

private int page;    

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

}

后面所有的分页查询,统一都封装成PageResult对象:

/**  * 封装分页查询结果  */

@Data

@AllArgsConstructor

@NoArgsConstructor

public class PageResult implements Serializable {    

private long total; //总记录数    

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

员工信息分页查询后端返回的对象类型为:Result<PageResult>

@GetMapping("/page")
@ApiOperation("员工分页查询")
public Result<PageResult> page(EmployeePageQueryDTO employeePageQueryDTO){
log.info("员工分页查询,参数为:{}",employeePageQueryDTO); //输出
PageResult pageResult =employeeService.pageQuery(employeePageQueryDTO);
return Result.success(pageResult);
}

PageResult pageQuery(EmployeePageQueryDTO employeePageQueryDTO); 

@Override
public PageResult pageQuery(EmployeePageQueryDTO employeePageQueryDTO) {
//select * from employee limit 0,10
//开始分页查询
PageHelper.startPage(employeePageQueryDTO.getPage(),employeePageQueryDTO.getPageSize());

Page<Employee> page =employeeMapper.pageQuery(employeePageQueryDTO);
long total = page.getTotal(); //总记录数
List<Employee> records = page.getResult(); //records
return new PageResult(total,records);
}

Page<Employee> pageQuery(EmployeePageQueryDTO employeePageQueryDTO); 

  

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd" >
<mapper namespace="com.sky.mapper.EmployeeMapper">
<select id="pageQuery" resultType="com.sky.entity.Employee">
select * from employee
<where>
<if test="name !=null and name !=''">
and name like concat('%',#{name},'%')
</if>
</where>
order by create_time desc
</select>
</mapper>

日期格式 错误:

解决方式: 方式一:在属性上加入注解,对日期进行格式化

方式二:在 WebMvcConfiguration 中扩展Spring MVC的消息转换器,统一对日期类型进行格式化处理

配置类上

@Override
protected void extendMessageConverters(List<HttpMessageConverter<?>> converters) {
log.info("扩展消息转换器...");
//创建一个消息转换器对象
MappingJackson2HttpMessageConverter converter = new MappingJackson2HttpMessageConverter();
//需要为消息转换器设置一个对象转换器,对象转换器可以将java对象序列化为json数据
converter.setObjectMapper(new JacksonObjectMapper());
//将自己的消息转换器加入容器中
converters.add(0,converter); //优先使用
}

启用禁用员工账号

业务规则: 可以对状态为“启用” 的员工账号进行“禁用”操作 可以对状态为“禁用”的员工账号进行“启用”操作 状态为“禁用”的员工账号不能登录系统

根据接口设计中的请求参数形式对应的在 EmployeeController 中创建启用禁用员工账号的方法:

@PostMapping("/status/{status}")
@ApiOperation("启用禁用员工账号")
public Result startStop(@PathVariable Integer status, Long id) {
log.info("启用禁用员工账号:{},{}", status, id);
employeeService.startOrStop(status, id);
return Result.success();
}

在 EmployeeService 接口中声明启用禁用员工账号的业务方法:

void startOrStop(Integer status, Long id);

 在 EmployeeServiceImpl 中实现启用禁用员工账号的业务方法:

@Override
public void startOrStop(Integer status, Long id) {
// Employee employee = new Employee();
// employee.setStatus(status);
// employee.setId(id);
Employee employee = Employee.builder()
.status(status)
.id(id)
.build();
//Update employee set status =?where id = ?
employeeMapper.update(employee);

}

在 EmployeeMapper 接口中声明 update 方法:

void updae(Employee employee);

在 EmployeeMapper.xml 中编写SQL:

<update id="update" parameterType="Employee">
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="updateTime != null">update_Time = #{updateTime},</if>
<if test="updateUser != null">update_User = #{updateUser},</if>
<if test="status != null">status = #{status},</if>
</set>
where id = #{id}
</update>

编辑员工 

编辑员工功能涉及到两个接口: 根据id查询员工信息 编辑员工信息

在 EmployeeController 中创建 getById 方法:

@GetMapping("/{id}")
@ApiOperation("根据id查询员工信息")
public Result<Employee> getById(@PathVariable Long id) {
Employee employee = employeeService.getById(id);
return Result.success(employee);
}

在 EmployeeService 接口中声明 getById 方法: 

Employee getById(Long id); 

在 EmployeeServiceImpl 中实现 getById 方法: 

@Override
public Employee getById(Long id) {
Employee employee=employeeMapper.getById(id);
employee.setPassword("****");
return employee;
}

在 EmployeeMapper 接口中声明 getById 方法: 

@Select("select *from employee where id = #{id}")
Employee getById(Long id);

 在 EmployeeController 中创建 update 方法:

@PutMapping
@ApiOperation("编辑员工信息")
public Result update(@RequestBody EmployeeDTO employeeDTO) {
log.info("编辑员工信息:{}", employeeDTO);
employeeService.update(employeeDTO);
return Result.success();
}

在 EmployeeService 接口中声明 update 方法: 

void update(EmployeeDTO employeeDTO);

 在 EmployeeServiceImpl 中实现 update 方法:

public void update(EmployeeDTO employeeDTO) {//通过DTO来接收前端提交过来的数据
//对象属性拷贝,给employee赋值
Employee employee = new Employee();
BeanUtils.copyProperties(employeeDTO, employee);
employee.setUpdateTime(LocalDateTime.now());
employee.setUpdateUser(BaseContext.getCurrentId());
employeeMapper.update(employee); //调用service方法
}

导入分类模块功能代码

业务规则:

分类名称必须是唯一

分类按照类型可以分为菜品分类和套餐分类

新添加的分类状态默认为“禁用” 

接口设计: 新增分类 分类分页查询 根据id删除分类 修改分类 启用禁用分类 根据类型查询分类

公共字段自动填充

问题:代码冗余,不便于后期维护

  • 自定义注解 AutoFill,用于标识需要进行公共字段自动填充的方法
  • 自定义切面类 AutoFillAspect,统一拦截加入了 AutoFill 注解的方法,通过反射为公共字段赋值
  • 在 Mapper 的方法上加入 AutoFill 注解
  • 技术点:枚举、注解、AOP、反射

1. 

2. 自定义注解 AutoFill

3.自定义切面 AutoFillAspect

在Mapper接口的方法上加入 AutoFill 注解

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值