毕设项目<<基于微信小程序的餐馆外卖系统>>(开发记录(二))

毕设项目<<基于微信小程序的餐馆外卖系统的设计后端>>(开发记录(二)菜品管理)


视频传送带

菜品管理

公共字段自动填充

问题分析

请添加图片描述

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

实现思路

请添加图片描述

  • 自定义注解AutoFile,用于标识需要进行公共字段自动填充的方法
  • 自定义切面类AutoFileAspect,统一加入拦截AutoFile方法,通过反射为公共字段赋值
  • Mapper的方法上加入AutoFile注解
  • 技术点:枚举、注解、AOP、反射
代码开发
package com.sky.aspect;  
  
import com.sky.annotation.AutoFile;  
import com.sky.constant.AutoFillConstant;  
import com.sky.context.BaseContext;  
import com.sky.enumeration.OperationType;  
import lombok.extern.slf4j.Slf4j;  
import org.aopalliance.intercept.Joinpoint;  
import org.aspectj.lang.JoinPoint;  
import org.aspectj.lang.Signature;  
import org.aspectj.lang.annotation.Aspect;  
import org.aspectj.lang.annotation.Before;  
import org.aspectj.lang.annotation.Pointcut;  
import org.aspectj.lang.reflect.MemberSignature;  
import org.aspectj.lang.reflect.MethodSignature;  
import org.springframework.stereotype.Component;  
  
import java.lang.reflect.Method;  
import java.time.LocalDateTime;  
  
/**  
* projectName: sky-take-out  
* package: com.sky.aspect  
* author: Amos  
* description: 自定义切面,实现公共字段自动填充的逻辑  
* date: 2024/1/3 0003 02:18:04  
* version: 1.0  
*/  
@Aspect  
@Component  
@Slf4j  
public class AutoFileAspect {  
/**  
* 切点表达式:切入点  
*/  
@Pointcut("execution(* com.sky.mapper.*.*(..)) && @annotation(com.sky.annotation.AutoFile)")  
public void autoFillPointCut(){}  
  
/**  
* 前置通知,在前置通知中进行公共字段赋值  
*/  
@Before("autoFillPointCut()")  
public void autoFill(JoinPoint joinPoint){  
log.info("开始进行公共字段的自动填充...");  
//获取到当前被拦截的方法的数据库操作类型  
MethodSignature signature = (MethodSignature) joinPoint.getSignature();//方法签名对象  
AutoFile autoFile = signature.getMethod().getAnnotation(AutoFile.class);//获得方法上的注解对象  
OperationType operationType = autoFile.value();//获得数据库操作类型  
  
//获取被拦截的方法的参数--实体对象  
Object[L|400] args = joinPoint.getArgs();  
if(args == null ||args.length==0)  
return;  
Object entity = args[0];  
  
//准备赋值的数据--时间和用户id  
LocalDateTime now = LocalDateTime.now();  
Long currentId = BaseContext.getCurrentId();  
  
//根据当前不同操作类型,为对应的属性通过反射赋值  
if(operationType == OperationType.INSERT){  
//为四个公共字段赋值  
try {  
Method setCreatTime = entity.getClass().getDeclaredMethod(AutoFillConstant.SET_CREATE_TIME, LocalDateTime.class);  
Method setCreatUser = entity.getClass().getDeclaredMethod(AutoFillConstant.SET_CREATE_USER, Long.class);  
Method setUpdateTime = entity.getClass().getDeclaredMethod(AutoFillConstant.SET_UPDATE_TIME, LocalDateTime.class);  
Method setUpdateUser = entity.getClass().getDeclaredMethod(AutoFillConstant.SET_UPDATE_USER, Long.class);  
//通过反射为对象属性赋值  
setCreatTime.invoke(entity,now);  
setCreatUser.invoke(entity,currentId);  
setUpdateTime.invoke(entity,now);  
setUpdateUser.invoke(entity,currentId);  
  
} catch (Exception e) {  
//throw new RuntimeException(e);  
e.printStackTrace();  
}  
} else if (operationType == OperationType.UPDATE) {  
//为2个公共字段赋值  
try {  
  
Method setUpdateTime = entity.getClass().getDeclaredMethod(AutoFillConstant.SET_UPDATE_TIME, LocalDateTime.class);  
Method setUpdateUser = entity.getClass().getDeclaredMethod(AutoFillConstant.SET_UPDATE_USER, Long.class);  
//通过反射为对象属性赋值  
setUpdateTime.invoke(entity,now);  
setUpdateUser.invoke(entity,currentId);  
  
} catch (Exception e) {  
//throw new RuntimeException(e);  
e.printStackTrace();  
}  
  
}  
  
}  
}

新增菜品

需求分析和设计
业务规则
  • 菜品名称必须唯一
  • 菜品必须属于某个分类下面,不能单独存在
  • 新增菜品时可以根据情况选择菜品的口味
  • 每个菜品必须对应应一张图片
接口设计
  • 根据类型查询分类

  • 请添加图片描述

  • 请添加图片描述

  • 文件上传

请添加图片描述
请添加图片描述

  • 新增菜品
    请添加图片描述
    请添加图片描述
数据库设计

dish表
请添加图片描述
请添加图片描述

dish_flavor
请添加图片描述
请添加图片描述

代码开发

请添加图片描述

阿里云对象存储

请添加图片描述
配置

alioss:  
endpoint: oss-cn-beijing.aliyuncs.com
access-key-id: 
access-key-secret:  
bucket-name: 

文件上传
/**  
* 文件上传  
* @param file  
* @return  
*/  
@PostMapping("/upload")  
@ApiOperation("文件上传")  
public Result<String> upload(MultipartFile file){  
log.info("文件开始上传:{}",file);  
try {  
//原始文件名  
String fileOriginalFilename = file.getOriginalFilename();  
//截取原始文件名后缀 xxxx.pngString extension = fileOriginalFilename.substring(fileOriginalFilename.lastIndexOf("."));  
//构造新文件名称  
String fileName = UUID.randomUUID() + extension;  
  
String filePath = aliOssUtil.upload(file.getBytes(), fileName);  
return Result.success(filePath);  
} catch (IOException e) {  
log.error("文件上传失败: {}", e.getMessage());  
}  
return Result.error(MessageConstant.UPLOAD_FAILED);  
}  
}
新增菜品
/**  
* 新增菜品及口味  
* @param dishDTO  
*/  
@Transactional  
public void saveWithFlavor(DishDTO dishDTO) {  
Dish dish = new Dish();  
BeanUtils.copyProperties(dishDTO, dish);  
//向菜品表插入1条数据  
dishMapper.insert(dish);  
// 获取insert语句的主键值  
Long dishId = dish.getId();  
// 新增n条口味数据  
List<DishFlavor> flavors = dishDTO.getFlavors();  
if (flavors != null && flavors.size() > 0) {  
//向口味表插入n条数据  
flavors.forEach(dishFlavor -> {  
dishFlavor.setDishId(dishId);  
});  
  
// for (DishFlavor flavor : flavors) {  
// flavor.setDishId(dishId);  
// }  
dishFlavorMapper.insertBatch(flavors);  
}  
  
}

菜品分页查询

需求分析和设计
产品原型

请添加图片描述

业务规则
  • 根据页码展示菜品信息
  • 每页展示10条信息
  • 分页查询可以根据需要输入菜品名称、菜品分类、菜品状态进行查询
接口设计

请添加图片描述

代码开发
  • 根据菜品分页查询接口定义设计对应的DTO,通过DTO封装,
    请添加图片描述

  • 根据菜品分页查询接口定义设计对应的VO,将其转成json数据传给前端,然后前端进行正常展示
    请添加图片描述

数据库查询

SELECT d.*,c.`name` AS categoryName FROM dish d LEFT OUTER JOIN category c on d.category_id = c.id
/**  
*  
* 菜品分页查询  
* @param dishPageQueryDTO  
* @return  
*/  
@Override  
public PageResult pageQuery(DishPageQueryDTO dishPageQueryDTO) {  
PageHelper.startPage(dishPageQueryDTO.getPage(),dishPageQueryDTO.getPageSize());  
Page<DishVO> page = dishMapper.pageQuery(dishPageQueryDTO);  
  
return new PageResult(page.getTotal(),page.getResult());  
}
代码测试
接口文档测试

请添加图片描述
请添加图片描述

前后端联调

请添加图片描述

删除菜品

需求分析和设计

请添加图片描述

业务规则
  • 可以一次删除一个菜品,也可以批量删除菜品
  • 启售中的菜品不能删除
  • 被套餐关联的菜品不能删除
  • 删除菜品后,关联的口味数据也要删除
接口设计

请添加图片描述

数据库设计

请添加图片描述

代码开发
@Transactional  
public void deleteBatch(List<Long> ids) {  
// 1. 起售中不可删  
for (long id:ids  
) {  
//根据主键查询  
Dish dish = dishMapper.getById(id);  
if (dish.getStatus() == StatusConstant.ENABLE) {  
throw new DeletionNotAllowedException(MessageConstant.DISH_ON_SALE);  
}  
}  
  
// 2. 被套餐关联的菜品不可删  
List<Long> setmealIds = setmealDishMapper.getSetmealIdsByDishIds(ids);  
if (setmealIds != null && setmealIds.size() > 0) {  
throw new DeletionNotAllowedException(MessageConstant.DISH_BE_RELATED_BY_SETMEAL);  
}  
  
// 3. 删除菜品后关联的口味数据也要删除掉  
for (Long id : ids) {  
dishMapper.deleteById(id);  
dishFlavorMapper.deleteByDishIds(id);  
}  
  
}
代码测试

修改菜品

需求分析和设计
产品原型

请添加图片描述

接口设计
  • 根据id查询菜品
  • 根据类型查询分类
  • 文件上传
  • 修改菜品
    请添加图片描述

请添加图片描述
请添加图片描述

请添加图片描述

代码开发
根据id查询菜品,用于页面回显
  
/**  
* 根据id查询菜品和对应口味数据  
*  
* @param id  
* @return  
*/  
public DishVO getByIdWithFlavor(Long id) {  
//根据id查询菜品数据  
Dish dish = dishMapper.getById(id);  
  
//根据id查询口味数据  
List<DishFlavor> dishFlavors = dishFlavorMapper.getByDishId(id);  
  
//将查询数据封装到VO  
DishVO dishVO = new DishVO();  
BeanUtils.copyProperties(dish,dishVO);  
dishVO.setFlavors(dishFlavors);  
  
return dishVO;  
}
菜品修改
/**  
* 根据id修改菜品  
* @param dishDTO  
*/  
public void updateWithFlafvor(DishDTO dishDTO) {  
Dish dish = new Dish();  
BeanUtils.copyProperties(dishDTO,dish);  
//修改菜品基本信息  
dishMapper.update(dish);  
  
//删除原有的口味数据  
dishFlavorMapper.deleteByDishId(dishDTO.getId());  
  
//重新插入口味数据  
List<DishFlavor> flavors = dishDTO.getFlavors();  
if (flavors != null && flavors.size() > 0) {  
//向口味表插入n条数据  
for (DishFlavor dishFlavor : flavors) {  
dishFlavor.setDishId(dishDTO.getId());  
}  
  
dishFlavorMapper.insertBatch(flavors);  
}  
  
}
代码测试

请添加图片描述

请添加图片描述

  • 9
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值