项目--苍穹外卖

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

相关推荐
木易 士心7 分钟前
Spring AI 核心架构解析:构建企业级 AI 应用的 Java 新范式
java·spring
61900833618 分钟前
linux 安装jdk
java·linux·运维
懂得节能嘛.21 分钟前
【动态配置中心】Java+Redis构建动态配置中心
java·开发语言·redis
专注于大数据技术栈22 分钟前
Java中JDK、JRE、JVM概念
java·开发语言·jvm
YuanlongWang25 分钟前
C# 基础——值类型与引用类型的本质区别
java·jvm·c#
Kay_Liang1 小时前
大语言模型如何精准调用函数—— Function Calling 系统笔记
java·大数据·spring boot·笔记·ai·langchain·tools
自由的疯1 小时前
Java 如何学习Docker
java·后端·架构
自由的疯1 小时前
Java Docker本地部署
java·后端·架构
007php0071 小时前
猿辅导Java面试真实经历与深度总结(二)
java·开发语言·python·计算机网络·面试·职场和发展·golang
摇滚侠1 小时前
Spring Boot 3零基础教程,WEB 开发 内容协商机制 笔记34
java·spring boot·笔记·缓存