项目--苍穹外卖

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删除分类 修改分类 启用禁用分类 根据类型查询分类

相关推荐
qq_44199605几秒前
Mybatis官方生成器使用示例
java·mybatis
巨大八爪鱼7 分钟前
XP系统下用mod_jk 1.2.40整合apache2.2.16和tomcat 6.0.29,让apache可以同时访问php和jsp页面
java·tomcat·apache·mod_jk
码上一元2 小时前
SpringBoot自动装配原理解析
java·spring boot·后端
计算机-秋大田2 小时前
基于微信小程序的养老院管理系统的设计与实现,LW+源码+讲解
java·spring boot·微信小程序·小程序·vue
魔道不误砍柴功4 小时前
简单叙述 Spring Boot 启动过程
java·数据库·spring boot
失落的香蕉4 小时前
C语言串讲-2之指针和结构体
java·c语言·开发语言
枫叶_v4 小时前
【SpringBoot】22 Txt、Csv文件的读取和写入
java·spring boot·后端
wclass-zhengge4 小时前
SpringCloud篇(配置中心 - Nacos)
java·spring·spring cloud
路在脚下@4 小时前
Springboot 的Servlet Web 应用、响应式 Web 应用(Reactive)以及非 Web 应用(None)的特点和适用场景
java·spring boot·servlet
黑马师兄4 小时前
SpringBoot
java·spring