当前位置:网站首页>5.菜品管理业务开发
5.菜品管理业务开发
2022-06-24 09:43:00 【xjhqre】
菜品管理业务开发
1、文件上传功能
1.1、导入页面
从资料里导入upload.html文件

1.2、编写文件上传方法
1、保存地址配置,在application.yml添加以下配置
reggie:
path: D:\reggieImg\
2、实现方法
package com.itheima.reggie.controller;
import com.itheima.reggie.common.R;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;
import java.io.File;
import java.io.IOException;
import java.util.UUID;
/** * @Author: xjhqre * @DateTime: 2022/6/18 16:18 */
@RestController
@RequestMapping("/common")
@Slf4j
public class CommonController {
@Value("${reggie.path}")
private String basePath;
@PostMapping("/upload")
public R<String> upload(MultipartFile file) {
// file是一个临时文件,需要转存到指定位置,否则本次请求完成后临时文件会删除
log.info(file.toString());
// 原始文件名
String originalFilename = file.getOriginalFilename();
String suffix = originalFilename.substring(originalFilename.lastIndexOf("."));
// 使用UUID重新生成文件名,防止文件名重复发生覆盖
String fileName = UUID.randomUUID() + suffix;
// 创建一个目录对象
File dir = new File(basePath);
// 判断当前目录是否存在
if (!dir.exists()) {
dir.mkdirs();
}
try {
// 将临时文件转存到指定位置
file.transferTo(new File(basePath + fileName));
} catch (IOException e) {
e.printStackTrace();
}
return R.success(fileName);
}
}
1.3、开放上传图片请求地址
修改LoginCheckFilter的过滤配置

1.4、测试图片上传功能
访问地址http://localhost:8080/backend/page/demo/upload.html
测试上传功能
1.5、使用阿里云OSS存储(可选)
1.5.1、创建bucket

1.5.2、创建AccessKey
1、使用子用户 AccessKey,创建完成后记得保存accessKeySecret,以后无法查询

2、添加用户权限

1.5.3、引入依赖
<dependency>
<groupId>com.aliyun.oss</groupId>
<artifactId>aliyun-sdk-oss</artifactId>
<version>3.10.2</version>
</dependency>
1.5.4、创建上传方法
/** * 文件上传OSS * @param file * @return */
@PostMapping("/upload")
public R<String> upload(MultipartFile file) {
// file是一个临时文件,需要转存到指定位置,否则本次请求完成后临时文件会删除
log.info(file.toString());
// 原始文件名
String originalFilename = file.getOriginalFilename();
String suffix = originalFilename.substring(originalFilename.lastIndexOf("."));
// Endpoint以华东1(杭州)为例,其它Region请按实际情况填写。
String endpoint = "https://oss-cn-hangzhou.aliyuncs.com";
// 阿里云账号AccessKey拥有所有API的访问权限,风险很高。强烈建议您创建并使用RAM用户进行API访问或日常运维,请登录RAM控制台创建RAM用户。
String accessKeyId = "你的accessKeyId";
String accessKeySecret = "你的accessKeySecret";
// 填写Bucket名称,例如examplebucket。
String bucketName = "你的bucketName";
// 填写Object完整路径,完整路径中不能包含Bucket名称,例如exampledir/exampleobject.txt。
String objectName = UUID.randomUUID() + suffix;
// 创建OSSClient实例。
OSS ossClient = new OSSClientBuilder().build(endpoint, accessKeyId, accessKeySecret);
// getInputStream()返回一个InputStream以从中读取文件的内容。通过此方法就可以获取到流
InputStream multipartFileInputStream = null;
try {
multipartFileInputStream = file.getInputStream();
} catch (IOException e) {
e.printStackTrace();
}
try {
// 创建PutObject请求。
ossClient.putObject(bucketName, objectName, multipartFileInputStream);
} catch (OSSException oe) {
System.out.println("Caught an OSSException, which means your request made it to OSS, "
+ "but was rejected with an error response for some reason.");
System.out.println("Error Message:" + oe.getErrorMessage());
System.out.println("Error Code:" + oe.getErrorCode());
System.out.println("Request ID:" + oe.getRequestId());
System.out.println("Host ID:" + oe.getHostId());
} catch (ClientException ce) {
System.out.println("Caught an ClientException, which means the client encountered "
+ "a serious internal problem while trying to communicate with OSS, "
+ "such as not being able to access the network.");
System.out.println("Error Message:" + ce.getMessage());
} finally {
if (ossClient != null) {
ossClient.shutdown();
}
}
return R.success(objectName);
}
2、文件下载功能
需求:当我们上传一个图片后需要在浏览器进行回显
2.1 编写下载方法
在CommonController下编写download方法
/** * 文件下载 * @param name * @param response */
@GetMapping("/download")
public void download(String name, HttpServletResponse response) {
try {
// 输入流,从本地磁盘读取图片信息
FileInputStream fileInputStream = new FileInputStream(new File(basePath + name));
// 输出流,输出图片信息到浏览器
ServletOutputStream outputStream = response.getOutputStream();
response.setContentType("image/jpeg");
int len = 0;
byte[] bytes = new byte[1024];
while ((len = fileInputStream.read(bytes)) != -1) {
outputStream.write(bytes, 0, len);
outputStream.flush();
}
// 关闭资源
outputStream.close();
fileInputStream.close();
} catch (Exception e) {
e.printStackTrace();
}
}
2.2、测试图片回显功能
2.3、请求OSS图片(可选)
如果使用了OSS上传图片服务,想要获取OSS图片,只需要修改图片的请求路径即可,如下图所示:
修改upload.html文件里的handleAvatarSuccess方法请求路径

3、新增菜品
执行流程:
- 页面(backend/page/food/add.html)发送ajax请求,请求服务端获取菜品分类数据并展示到下拉框中
- 页面发送请求进行图片上传,请求服务端将图片保存到服务器
- 页面发送请求进行图片下载,将上传的图片进行回显
- 点击保存按钮,发送ajax请求,将菜品相关数据以json形式提交到服务端
3.1、准备工作
先将需要用到的类和接口准备好:
- 实体类DishFlavor(从资料中导入)
- Mapper接口DishFlavorMapper
- 业务层接口DishFlavorService
- 业务层实现类DishFlavorServiceImpl
- 控制层DishController
Dish和DishFlavor的业务都由DishController处理
3.2、查询菜品分类列表
在CategoryController中编写list方法
/** * 根据条件查询分类数据 * @param category * @return */
@GetMapping("/list")
public R<List<Category>> list(Category category) {
// 条件构造器
LambdaQueryWrapper<Category> queryWrapper = new LambdaQueryWrapper<>();
// 添加条件
queryWrapper.eq(category.getType() != null, Category::getType, category.getType());
// 添加排序条件
queryWrapper.orderByAsc(Category::getSort).orderByDesc(Category::getUpdateTime);
List<Category> list = categoryService.list(queryWrapper);
return R.success(list);
}
3.3、导入DTO
由于前端传来的数据,在后端不能直接用Dish和DishFlavor接收,需要有一个实体类同时具有Dish和DishFlavor的属性。
package com.itheima.reggie.dto;
import com.itheima.reggie.entity.Dish;
import com.itheima.reggie.entity.DishFlavor;
import lombok.Data;
import java.util.ArrayList;
import java.util.List;
@Data
public class DishDto extends Dish {
private List<DishFlavor> flavors = new ArrayList<>();
private String categoryName;
private Integer copies;
}
这里可以看出子类其实是可以继承父类的所有东西的。只是不能直接获取权限为private或default的属性和方法
3.4、创建controller方法
在DishController类中创建save方法
package com.itheima.reggie.controller;
import com.itheima.reggie.common.R;
import com.itheima.reggie.dto.DishDto;
import com.itheima.reggie.service.DishFlavorService;
import com.itheima.reggie.service.DishService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
@Slf4j
@RestController
@RequestMapping("/dish")
public class DishController {
@Autowired
DishService dishService;
@Autowired
DishFlavorService dishFlavorService;
/** * 新增菜品 * @param dishDto * @return */
@PostMapping
public R<String> save(@RequestBody DishDto dishDto) {
log.info(dishDto.toString());
dishService.saveWithFlavor(dishDto);
return R.success("新增菜品成功");
}
}
3.5、在service中定义方法
在DishService中定义saveWithFalvor方法
package com.itheima.reggie.service;
import com.baomidou.mybatisplus.extension.service.IService;
import com.itheima.reggie.dto.DishDto;
import com.itheima.reggie.entity.Dish;
public interface DishService extends IService<Dish> {
// 新增菜品,同时插入菜品对应的口味数据,需要操作两张表:dish和dish_falvor
void saveWithFalvor(DishDto dishDto);
}
3.6、在ServiceImpl中实现方法
package com.itheima.reggie.service.impl;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.itheima.reggie.dto.DishDto;
import com.itheima.reggie.entity.Category;
import com.itheima.reggie.entity.Dish;
import com.itheima.reggie.entity.DishFlavor;
import com.itheima.reggie.mapper.CategoryMapper;
import com.itheima.reggie.mapper.DishMapper;
import com.itheima.reggie.service.CategoryService;
import com.itheima.reggie.service.DishFlavorService;
import com.itheima.reggie.service.DishService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.List;
import java.util.stream.Collectors;
@Service
public class DishServiceImpl extends ServiceImpl<DishMapper, Dish> implements DishService {
@Autowired
private DishFlavorService dishFlavorService;
/** * 新增菜品,同时保存对应的口味数据 * @param dishDto */
@Override
@Transactional
public void saveWithFlavor(DishDto dishDto) {
// 保存菜品的基本信息到dish表
this.save(dishDto);
Long dishId = dishDto.getId();
// 菜品口味
List<DishFlavor> flavors = dishDto.getFlavors();
flavors = flavors.stream().peek(item -> item.setDishId(dishId)).collect(Collectors.toList());
// 保存菜品口味数据到dish_flavor表
dishFlavorService.saveBatch(flavors);
}
}
3.7、在主启动类中开启事务
添加@EnableTransactionManagement注解
@Slf4j
@SpringBootApplication
@ServletComponentScan
@EnableTransactionManagement
public class ReggieApplication {
public static void main(String[] args) {
SpringApplication.run(ReggieApplication.class, args);
log.info("项目启动成功...");
}
}
3.8、测试新增菜品
添加菜品后查看数据库是否添加正确

4、菜品信息分页查询
4.1、创建分页查询方法
4.1.1、方法一:老师的方法
/** * 菜品信息分页查询 * @param page * @param pageSize * @param name * @return */
@GetMapping("/page")
public R<Page<DishDto>> page(int page, int pageSize, String name) {
// 构造分页构造器对象
Page<Dish> pageInfo = new Page<>(page, pageSize);
Page<DishDto> dishDtoPage = new Page<>();
// 条件构造器
LambdaQueryWrapper<Dish> queryWrapper = new LambdaQueryWrapper<>();
// 添加过滤条件
queryWrapper.like(name != null, Dish::getName, name);
// 添加排序条件
queryWrapper.orderByDesc(Dish::getUpdateTime);
// 执行分页查询
dishService.page(pageInfo, queryWrapper);
// 对象拷贝
BeanUtils.copyProperties(pageInfo, dishDtoPage, "records");
List<Dish> records = pageInfo.getRecords();
List<DishDto> list = records.stream().map(item -> {
DishDto dishDto = new DishDto();
BeanUtils.copyProperties(item, dishDto);
Long categoryId = item.getCategoryId();
// 根据分类id查询分类对象
Category category = categoryService.getById(categoryId);
if (category != null) {
String categoryName = category.getName();
dishDto.setCategoryName(categoryName);
}
return dishDto;
}).collect(Collectors.toList());
dishDtoPage.setRecords(list);
return R.success(dishDtoPage);
}
4.1.2、方法二:联表查询方法(可选)
1、DishController层
/** * 菜品信息分页查询 * @param page * @param pageSize * @param name * @return */
@GetMapping("/page")
public R<Page<DishDto>> page(int page, int pageSize, String name) {
// 构造分页构造器对象
Page<DishDto> pageInfo = new Page<>(page, pageSize);
dishService.selectDishWithCategoryNameList(pageInfo, name);
return R.success(pageInfo);
}
2、DishService层,添加以下方法声明
// 查询所有带菜品分类名的菜品信息
void selectDishWithCategoryNameList(Page<DishDto> page, String name);
3、DishServiceImpl层,实现selectDishWithCategoryNameList方法
/** * 查询所有的Dish并带有菜品分类名 * @param page */
@Override
public void selectDishWithCategoryNameList(Page<DishDto> page, String name) {
List<DishDto> records = baseMapper.selectDishWithCategoryNameList(page, name == null ? "" : name);
page.setRecords(records);
}
4、DishMapper层,添加以下方法声明
@Select("SELECT a.*, b.`name` AS category_name FROM dish a JOIN category b ON a.`category_id` = b.`id` WHERE a.`name` LIKE CONCAT('%',#{name},'%') ORDER BY sort")
List<DishDto> selectDishWithCategoryNameList(Page<DishDto> page, @Param("name") String name);
4.2、菜品列表显示阿里云OSS图片(可选)
如果使用的是阿里云存储的图片,需要修改backend/page/food/list.html文件以下两处地方
修改:preview-src-list的值

修改return的值

4.3、测试菜品查询效果

5、修改菜品页面数据回显
执行流程:
- 页面发送ajax请求,请求服务端获取分类数据,用于菜品分类下拉框中数据展示
- 页面发送ajax请求,请求服务端,根据id查询当前菜品信息,用于菜品信息回显
- 页面发送请求,请求服务端进行图片下载,用于页图片回显
- 点击保存按钮,页面发送ajax请求,将修改后的菜品相关数据以json形式提交到服务端
5.1、DishController
创建get 方法
/** * 根据id查询菜品信息和对应的口味信息 * @param id * @return */
@GetMapping("/{id}")
public R<DishDto> get(@PathVariable Long id) {
DishDto dishDto = dishService.getByIdWithFlavor(id);
return R.success(dishDto);
}
5.2、DishService
声明getByIdWithFlavor方法
// 根据id查询菜品信息和对应的口味信息
DishDto getByIdWithFlavor(Long id);
5.3、DishServiceImpl
实现getByIdWithFlavor方法
/** * 根据id查询菜品信息和对应的口味信息 * @param id * @return */
@Override
public DishDto getByIdWithFlavor(Long id) {
// 查询菜品的基本信息,从dish表查询
Dish dish = this.getById(id);
DishDto dishDto = new DishDto();
BeanUtils.copyProperties(dish, dishDto);
// 查询当前菜品对应的口味信息,从dish_flavor表查询
LambdaQueryWrapper<DishFlavor> queryWrapper = new LambdaQueryWrapper<>();
queryWrapper.eq(DishFlavor::getDishId, dish.getId());
List<DishFlavor> flavors = dishFlavorService.list(queryWrapper);
dishDto.setFlavors(flavors);
return dishDto;
}
5.4、阿里云OSS图片回显(可选)
修改backend/page/food/add.html文件

5.5、测试修改回显

6、修改菜品
先删除dish对应的所有dish_flavor,在进行添加
6.1、DishController
/** * 修改菜品 * @param dishDto * @return */
@PutMapping
public R<String> update(@RequestBody DishDto dishDto) {
log.info(dishDto.toString());
dishService.updateWithFlavor(dishDto);
return R.success("修改菜品成功");
}
6.2、DishService
// 更新菜品信息,同时更新对应的口味信息
void updateWithFlavor(DishDto dishDto);
6.3、DishServiceImpl
@Override
@Transactional
public void updateWithFlavor(DishDto dishDto) {
// 更新dish表基本信息
this.updateById(dishDto);
// 清理当前菜品对应口味数据
LambdaQueryWrapper<DishFlavor> queryWrapper = new LambdaQueryWrapper<>();
queryWrapper.eq(DishFlavor::getDishId, dishDto.getId());
dishFlavorService.remove(queryWrapper);
// 添加当前提交过来的口味数据
List<DishFlavor> flavors = dishDto.getFlavors();
flavors = flavors.stream().peek(item -> item.setDishId(dishDto.getId())).collect(Collectors.toList());
dishFlavorService.saveBatch(flavors);
}
6.4、测试修改菜品
7、删除和批量删除菜品(可选)
使用逻辑删除
先删除菜品对应的口味,在删除菜品,is_delete为1表示删除,0表示未删除
7.1、修改Dish和DishFlavor实体类
给isDeleted属性添加注解@TableLogic(value = "0", delval = "1")。添加此注解后,调用mybatis-plus的删除方法实际上用的是update方法

7.2、DishController
/** * 逻辑删除 * @param ids * @return */
@DeleteMapping
@Transactional
public R<String> delete(Long[] ids) {
log.info("批量删除的id:{}", Arrays.toString(ids));
// 先逻辑删除菜品对应的口味信息
dishFlavorService.removeByDishIds(Arrays.asList(ids));
// 在逻辑删除菜品
dishService.removeByIds(Arrays.asList(ids));
return R.success("删除菜品成功");
}
7.3、DishFlavorService
/** * 逻辑删除对应菜品的口味记录 * @param dishIds */
void removeByDishIds(List<Long> dishIds);
7.4、DishFlavorServiceImpl
@Service
public class DishFlavorServiceImpl extends ServiceImpl<DishFlavorMapper, DishFlavor> implements DishFlavorService {
@Autowired
DishFlavorMapper dishFlavorMapper;
/** * 逻辑删除对应菜品的口味记录 * @param dishIds */
@Override
public void removeByDishIds(List<Long> dishIds) {
dishFlavorMapper.removeByDishIds(dishIds);
}
}
7.5、DishFlavorMapper
@Mapper
public interface DishFlavorMapper extends BaseMapper<DishFlavor> {
void removeByDishIds(@Param("dishIds") List<Long> dishIds);
}
7.6、DishFlavorDao.xml
在resources目录下创建mapper目录,在mapper目录下创建DishFlavorDao.xml。
<?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.itheima.reggie.mapper.DishFlavorMapper">
<update id="removeByDishIds">
UPDATE dish_flavor SET is_deleted=1 WHERE dish_id IN
<foreach collection="dishIds" item="id" index="index" open="(" close=")" separator=",">
#{id}
</foreach>
AND is_deleted=0
</update> <!-- 修改 -->
</mapper>
7.7、修改菜品分页查询功能
由于我们使用了逻辑删除,删除的菜品还是能在菜品列表上看到,我们需要修改分页查询功能
以下是对我自己写的分页查询方法进行修改,如果用的是老师的分页查询方法请自行修改
修改DishMapper里的selectDishWithCategoryNameList方法,在注解中加上is_deleted字段的判断即可
@Select("SELECT a.*, b.`name` AS category_name FROM dish a JOIN category b ON a.`category_id` = b.`id` WHERE a.`name` LIKE CONCAT('%',#{name},'%') AND a.`is_deleted` = 0 ORDER BY sort")
List<DishDto> selectDishWithCategoryNameList(Page<DishDto> page, @Param("name") String name);
7.8、测试批量删除
8、批量起售停售(可选)
8.1、DishController
/** * 批量停售起售 * @param status * @param ids * @return */
@PostMapping("status/{status}")
@Transactional
public R<String> status(@PathVariable("status") int status, @RequestParam Long[] ids) {
log.info("批量起售停售:{},{}", status, Arrays.toString(ids));
dishService.updateStatusByIds(status, ids);
return R.success("批量起售停售成功");
}
8.2、DishService
// 批量起售停售
void updateStatusByIds(int status, Long[] ids);
8.3、DishServiceImpl
/** * 批量起售停售 * @param status * @param ids */
@Override
public void updateStatusByIds(int status, Long[] ids) {
dishMapper.updateStatusByIds(status, ids);
}
8.4、DishMapper
void updateStatusByIds(@Param("status") int status, @Param("ids") Long[] ids);
8.5、DishDao.xml
在resources目录下的mapper目录下创建DishDao.xml文件
<?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.itheima.reggie.mapper.DishMapper">
<update id="updateStatusByIds">
UPDATE dish
SET status = #{status}
WHERE id IN
<foreach collection="ids" item="id" index="index" open="(" close=")" separator=",">
#{id}
</foreach>
AND status != #{status}
</update> <!-- 修改 -->
</mapper>
8.6、测试批量起售停售
边栏推荐
猜你喜欢

队列Queue

How to solve multi-channel customer communication problems in independent stations? This cross-border e-commerce plug-in must be known!

Groovy obtains Jenkins credentials through withcredentials

How to standardize data center infrastructure management process

SQL Sever关于like操作符(包括字段数据自动填充空格问题)

oracle池式连接请求超时问题排查步骤

2021-08-17

Yolov6: the fast and accurate target detection framework is open source

Analysis of 43 cases of MATLAB neural network: Chapter 32 time series prediction of wavelet neural network - short-term traffic flow prediction

Go language development environment setup +goland configuration under the latest Windows
随机推荐
Geogebra instance clock
How large and medium-sized enterprises build their own monitoring system
oracle池式连接请求超时问题排查步骤
PHP file lock
2022-06-23:给定一个非负数组,任意选择数字,使累加和最大且为7的倍数,返回最大累加和。 n比较大,10的5次方。 来自美团。3.26笔试。
有关二叉树 的基本操作
SQL Server AVG函数取整问题
一群骷髅在飞canvas动画js特效
tf.contrib.layers.batch_norm
Graffiti smart brings a variety of heavy smart lighting solutions to the 2022 American International Lighting Exhibition
Why is JSX syntax so popular?
Get the QR code of wechat applet with parameters - and share the source code of modifying the QR code logo
静态链接库和动态链接库的区别
Which of the top ten securities companies has the lowest Commission and is the safest and most reliable? Do you know anything
Mise en œuvre du rendu de liste et du rendu conditionnel pour l'apprentissage des applets Wechat.
Observer mode
How does home office manage the data center network infrastructure?
SQL Sever关于like操作符(包括字段数据自动填充空格问题)
SVG+js拖拽滑块圆形进度条
如何在一个页面上使用多个KindEditor编辑器并将值传递到服务器端