当前位置: 首页 > news >正文

#苍穹外卖#(day3-4)

目录

day03菜品管理

1 公共字段自动填充

2 自定义切面类

2 新增菜品

3 菜品分页查询

4 删除菜品

5 修改菜品

day04 套餐管理


day03菜品管理

1 公共字段自动填充

1 自定义注解AutoFill

2 自定义切面类

对公共字段进行处理,下面这些是公共字段

常量方法,代码规范

将不需要对公共的属性进行赋值,这些方法底层都是调用update与insert的方法,我们从切面当中已经处理了这些公共字段那在调用这些方法时

就不需要再在业务代码当中手动重复设置时间(将下面这些类的对这些公共字段设置时间的语句进行删除)

加上注解(加上的就会自动填充公共字段)

切面类

package com.sky.aspect;import com.sky.annotation.AutoFill;
import com.sky.constant.AutoFillConstant;
import com.sky.context.BaseContext;
import com.sky.enumeration.OperationType;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;import java.lang.reflect.Method;
import java.time.LocalDateTime;/*** 自定义切面,用于自动填充公共字段处理逻辑*/@Aspect
@Component
@Slf4j
public class AutoFillAspect {/*** 切入点表达式 com.sky.mapper 包下的所有类中的所有方法并且有 @AutoFill 注解的方法*/@Pointcut("execution(* com.sky.mapper.*.*(..)) && @annotation(com.sky.annotation.AutoFill)")public void autoFillPointCut() {}/*** 前置通知,在目标方法执行前执行*/@Before("autoFillPointCut()")public void autoFill(JoinPoint joinPoint) {log.info("开始进行公共字段自动填充...");//获取到当前杯拦截的方法上的数据库操作类型AutoFill autoFill = joinPoint.getTarget().getClass().getAnnotation(AutoFill.class);//获得注解对象OperationType operationType = autoFill.value();//数据库操作类型//获取当前杯拦截的方法的参数--实体对象Object[] args = joinPoint.getArgs();if (args == null || args.length == 0) {return;}Object entity = args[0];log.info("当前自动填充的实体对象:{}", entity.toString());//准备赋值的数据LocalDateTime createTime = LocalDateTime.now();Long currentId = BaseContext.getCurrentId();//根据当前不同的操作类型,为对应的实体对象通过反射来赋值if (operationType == OperationType.INSERT) {//四个公共字段:createTime、createUser、updateTime、updateUser赋值try {//获取当前实体类中的对应方法(使用本地的常量方法名)Method createTimeMethod = entity.getClass().getDeclaredMethod(AutoFillConstant.SET_CREATE_TIME, LocalDateTime.class);Method createUserMethod = entity.getClass().getDeclaredMethod(AutoFillConstant.SET_CREATE_USER, Long.class);Method updateTimeMethod = entity.getClass().getDeclaredMethod(AutoFillConstant.SET_UPDATE_TIME, LocalDateTime.class);Method updateUserMethod = entity.getClass().getDeclaredMethod(AutoFillConstant.SET_UPDATE_USER, Long.class);//通过反射为实体对象赋值createTimeMethod.invoke(entity, createTime);createUserMethod.invoke(entity, currentId);updateTimeMethod.invoke(entity, createTime);updateUserMethod.invoke(entity, currentId);log.info("为实体类 {} 赋值成功", entity.getClass());} catch (Exception e) {e.printStackTrace();}} else if (operationType == OperationType.UPDATE) {try {//两个公共字段:updateTime、updateUser赋值Method updateTimeMethod = entity.getClass().getDeclaredMethod(AutoFillConstant.SET_UPDATE_TIME, LocalDateTime.class);Method updateUserMethod = entity.getClass().getDeclaredMethod(AutoFillConstant.SET_UPDATE_USER, Long.class);updateTimeMethod.invoke(entity, createTime);updateUserMethod.invoke(entity, currentId);log.info("为实体类 {} 赋值成功", entity.getClass());} catch (Exception e) {e.printStackTrace();}}}
}

2 新增菜品

使用到三个接口:

根据类型查询分类(前面已完成)/文件上传/ 新增菜品

需要结合阿里云服务器进行相关操作

最终实现对图片的上传

AliOssProperties 是配置属性映射类,负责从配置文件当中读取OSS的配置项

OssConfiguration定义SpringBean的创建逻辑,将AliOssProPerties的配置值传递给AliOssUtil

AliOssUtil工具类,封装阿里云OSS的上传功能,是实际执行文件操作的类

AliOssProperties

package com.sky.properties;import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;@Component
@ConfigurationProperties(prefix = "sky.alioss")
@Data
public class AliOssProperties {private String endpoint;private String accessKeyId;private String accessKeySecret;private String bucketName;}

AliOssUtil

package com.sky.utils;import com.aliyun.oss.ClientException;
import com.aliyun.oss.OSS;
import com.aliyun.oss.OSSClientBuilder;
import com.aliyun.oss.OSSException;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import java.io.ByteArrayInputStream;@Data
@AllArgsConstructor
@Slf4j
public class AliOssUtil {private String endpoint;private String accessKeyId;private String accessKeySecret;private String bucketName;/*** 文件上传** @param bytes* @param objectName* @return*/public String upload(byte[] bytes, String objectName) {// 创建OSSClient实例。OSS ossClient = new OSSClientBuilder().build(endpoint, accessKeyId, accessKeySecret);try {// 创建PutObject请求。ossClient.putObject(bucketName, objectName, new ByteArrayInputStream(bytes));} 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();}}//文件访问路径规则 https://BucketName.Endpoint/ObjectNameStringBuilder stringBuilder = new StringBuilder("https://");stringBuilder.append(bucketName).append(".").append(endpoint).append("/").append(objectName);log.info("文件上传到:{}", stringBuilder.toString());return stringBuilder.toString();}
}

OssConfiguration

package com.sky.config;import com.sky.properties.AliOssProperties;
import com.sky.utils.AliOssUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;/*** 配置类:用于 注入阿里云OSS配置信息**/@Slf4j
@Configuration
public class OssConfiguration {//在方法当中创建对象,需要将AiliOssUtil当中的属性赋值,属性的值是从配置文件中获取,最后再封装成对象AilOssProperties返回//如果容器当中没有AilOssProperties这个对象,则创建这个对象,否则不创建@ConditionalOnMissingBean@Bean@ConditionalOnMissingBeanpublic AliOssUtil aliOssUtil(AliOssProperties aliOssProperties){log.info("开始创建阿里云OSS对象:{}",aliOssProperties);return new AliOssUtil(aliOssProperties.getEndpoint(),aliOssProperties.getAccessKeyId(),aliOssProperties.getAccessKeySecret(),aliOssProperties.getBucketName());}
}

controller层

1 一个文件上传的通用接口

package com.sky.controller.admin;import com.sky.constant.MessageConstant;
import com.sky.result.Result;
import com.sky.utils.AliOssUtil;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import org.springframework.web.multipart.MultipartFile;import java.io.IOException;
import java.util.UUID;/***  通用接口*  @author mxsky*/@Slf4j
@RestController
@RequestMapping("/admin/common")
@Api(tags = "通用接口")
public class CommonController {@Autowiredprivate AliOssUtil aliOssUtil;/*** 文件上传* @param file* @return*/@RequestMapping("/upload")@ApiOperation("文件上传")public Result<String> upload(MultipartFile file) {log.info("文件上传{}",file);try {//获取文件原始名称String originalFilename = file.getOriginalFilename();// 获取文件后缀assert originalFilename != null;String suffix = originalFilename.substring(originalFilename.lastIndexOf("."));// 生成新文件名String objectName = UUID.randomUUID().toString() + suffix;// 文件的请求路径String  filPath = aliOssUtil.upload(file.getBytes(), objectName);return Result.success(filPath);} catch (IOException e) {log.error("文件上传失败:{}", e);}return Result.error(MessageConstant.UPLOAD_FAILED);}}

对菜品的新增

package com.sky.controller.admin;import com.sky.dto.DishDTO;
import com.sky.result.Result;
import com.sky.service.DishService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;@Slf4j
@RestController
@RequestMapping("/admin/dish")
@Api(tags="菜品相关接口")
public class DishController {@Autowiredprivate DishService dishService;/*** 新增菜品* @param dishDTO* @return*/@ApiOperation("新增菜品")@PostMappingpublic Result save(@RequestBody DishDTO dishDTO){log.info("新增菜品:{}", dishDTO);dishService.saveWithFlavor(dishDTO);return Result.success();}
}

service层

接口

package com.sky.service;import com.sky.dto.DishDTO;public interface DishService {// 新增菜品和对应的口味数据public void saveWithFlavor(DishDTO dishDTO) ;}

实现类

package com.sky.service.impl;import com.sky.dto.DishDTO;
import com.sky.entity.Dish;
import com.sky.entity.DishFlavor;
import com.sky.mapper.DishFlavorMapper;
import com.sky.mapper.DishMapper;
import com.sky.service.DishService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;import java.util.List;@Slf4j
@Service
public class DishServiceImpl implements DishService {@Autowiredprivate DishMapper dishMapper;@Autowiredprivate DishFlavorMapper dishFlavorMapper;/*** 新增菜品和对应的口味数据** @param dishDTO*/@Transactionalpublic void saveWithFlavor(DishDTO dishDTO) {Dish dish = new Dish();BeanUtils.copyProperties(dishDTO, dish);//向菜品表中插入1条数据dishMapper.insert(dish);//获取菜品idLong dishId = dish.getId();//向口味表插入n条数据List<DishFlavor> flavors = dishDTO.getFlavors();if (flavors != null && !flavors.isEmpty()) {flavors.forEach(dishFlavor -> {dishFlavor.setDishId(dishId);});// 向口味表插入n条数据dishFlavorMapper.insertBatch(flavors);}}
}

mapper接口

插入单个

package com.sky.mapper;import com.sky.annotation.AutoFill;
import com.sky.entity.Dish;
import com.sky.enumeration.OperationType;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Select;@Mapper
public interface DishMapper {/*** 根据分类id查询菜品数量* @param categoryId* @return*/@Select("select count(id) from dish where category_id = #{categoryId}")Integer countByCategoryId(Long categoryId);/***插入数据* @return*/// 公共字段自动填充@AutoFill(OperationType.INSERT)void insert(Dish dish);
}

批量插入口味的数据

package com.sky.mapper;import com.sky.entity.DishFlavor;
import org.apache.ibatis.annotations.Mapper;import java.util.List;@Mapper
public interface DishFlavorMapper {/*** 批量插入口味数据* @param flavors*/void insertBatch(List<DishFlavor> flavors);
}

上面两个Mapper接口的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.sky.mapper.DishMapper"><!--  useGenerateKeys获取主键值  --><insert id="insert" useGeneratedKeys="true" keyProperty="id">insert into dish(name, category_id, price, image, description, status, create_time, update_time, create_user,update_user)values (#{name}, #{categoryId}, #{price}, #{image}, #{description}, #{status}, #{createTime}, #{updateTime},#{createUser}, #{updateUser})</insert>
</mapper>
<?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.DishFlavorMapper"><insert id="insertBatch">insert into dish_flavor (dish_id, name, value) values<foreach collection="flavors" item="flavor" separator=",">(#{flavor.dishId},#{flavor.name},#{flavor.value})</foreach></insert>
</mapper>

错误bug修改

1 AutoFillAspect 当中这里出错

2 在jwtTokenAdminInterceptor当中没有及时对校验过后的id给BaseContext赋值

第一个问题导致Aspect切面类无法生效,第二个导致id的设置无法获取到,从而无法赋值

3 菜品分页查询

首先,在DishController类当中编写相关方法,

类中定义了pageQuery方法,我们需要实现,这样我们就来到了Service层

顺便我们再将他的实现类给完善

完善DishMapper接口当中的方法

最后在Mapper的xml当中编写适当的动态SQL语句

4 删除菜品

首先在controller业务层完善相关代码

再在Service层编写删除的功能代码

编写Service的实现类(删除功能)

package com.sky.service.impl;import com.github.pagehelper.Page;
import com.github.pagehelper.PageHelper;
import com.sky.constant.MessageConstant;
import com.sky.dto.DishDTO;
import com.sky.dto.DishPageQueryDTO;
import com.sky.entity.Dish;
import com.sky.entity.DishFlavor;
import com.sky.exception.DeletionNotAllowedException;
import com.sky.mapper.DishFlavorMapper;
import com.sky.mapper.DishMapper;
import com.sky.mapper.SetMealDishMapper;
import com.sky.result.PageResult;
import com.sky.service.DishService;
import com.sky.vo.DishVO;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;import java.util.List;@Slf4j
@Service
public class DishServiceImpl implements DishService {@Autowiredprivate DishMapper dishMapper;@Autowiredprivate DishFlavorMapper dishFlavorMapper;@Autowiredprivate SetMealDishMapper setMealDishMapper;/*** 新增菜品和对应的口味数据** @param dishDTO*/@Override@Transactionalpublic void saveWithFlavor(DishDTO dishDTO) {Dish dish = new Dish();BeanUtils.copyProperties(dishDTO, dish);//向菜品表中插入1条数据dishMapper.insert(dish);//获取菜品idLong dishId = dish.getId();//向口味表插入n条数据List<DishFlavor> flavors = dishDTO.getFlavors();if (flavors != null && !flavors.isEmpty()) {flavors.forEach(dishFlavor -> {dishFlavor.setDishId(dishId);});// 向口味表插入n条数据dishFlavorMapper.insertBatch(flavors);}}/*** 菜品分页查询** @param dishPageQueryDTO* @return*/@Overridepublic PageResult pageQuery(DishPageQueryDTO dishPageQueryDTO) {PageHelper.startPage(dishPageQueryDTO.getPage(), dishPageQueryDTO.getPageSize());Page<DishVO> page = dishMapper.pageQuery(dishPageQueryDTO);return new PageResult(page.getTotal(), page.getResult());}/*** 批量删除菜品** @param ids*/@Transactional@Overridepublic void deleteBatch(List<Long> ids) {//判断当前菜品是否在售for (Long id : ids) {Dish dish = dishMapper.getById(id);//根据id查询菜品数据(添加方法)if (dish.getStatus() == 1) {//如果菜品处于启售状态,则不能删除throw new DeletionNotAllowedException(MessageConstant.DISH_ON_SALE);}}//判断当前菜品是否被套餐关联List<Long> dishIds = setMealDishMapper.getSetMealIdsByDishIds(ids);if (dishIds != null && !dishIds.isEmpty()) {//如果存在关联的套餐,则不能删除throw new DeletionNotAllowedException(MessageConstant.DISH_BE_RELATED_BY_SETMEAL);}//删除菜品数据for (Long id : ids) {dishMapper.deleteById(id);}//删除菜品对应的口味数据for (Long id : ids) {dishFlavorMapper.deleteByDishId(id);}}
}

删除功能还需补全对应的Mapper接口实现对数据库当中数据的操作

1 根据id查询菜品

2 根据id删除菜品

3 这里需要创建一个额外接口实现对口味的业务数据操作

对应的xml

4 根据菜品id删除对应的口味

这里还可以进行改进

5 修改菜品

根据id查询菜品

首先在controller层当中编写相关方法

Service中实现

接口的实现类

在Mapper中补全对应的SQL查询语句

修改菜品

controller层业务代码实现

service层的修改相关业务代码

实现类的编写

Mapper接口的实现

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.sky.mapper.DishMapper"><!--  useGenerateKeys获取主键值  --><insert id="insert" useGeneratedKeys="true" keyProperty="id">insert into dish(name, category_id, price, image, description, status, create_time, update_time, create_user,update_user)values (#{name}, #{categoryId}, #{price}, #{image}, #{description}, #{status}, #{createTime}, #{updateTime},#{createUser}, #{updateUser})</insert><!--  根据菜品id批量删除  --><delete id="deleteByIds">delete from dish where id in<foreach collection="ids" open="(" close=")" separator="," item="id">#{id}</foreach></delete><!--  菜品分页查询  --><select id="pageQuery" resultType="com.sky.vo.DishVO">select d.*,c.name as categoryNamefrom dish dleft join category c on d.category_id = c.id<where><if test="name != null and name != ''">and d.name like concat('%', #{name}, '%')</if><if test="categoryId != null">and d.category_id = #{categoryId}</if><if test="status != null">and d.status = #{status}</if></where>order by d.create_time desc</select><!--  根据id动态修改菜品数据  --><update id="update">update dish<set><if test="name != null">name = #{name},</if><if test="categoryId != null">category_id = #{categoryId},</if><if test="price != null">price = #{price},</if><if test="image != null">image = #{image},</if><if test="description != null">description = #{description},</if><if test="status != null">status = #{status},</if><if test="updateTime != null">update_time = #{updateTime},</if><if test="updateUser != null">update_user = #{updateUser}</if></set>where id = #{id}</update></mapper>

day04 套餐管理

接口设计(共涉及到4个接口):

  • 根据类型查询分类(已完成)

  • 根据分类id查询菜品

  • 图片上传(已完成)

  • 新增套餐

新增套餐时没有将主键回显导致主键无法获取到

 

相关文章:

  • Verilog的整数除法
  • 【FPGA】——DDS信号发生器设计
  • 单位门户网站被攻击后的安全防护策略
  • P12130 [蓝桥杯 2025 省 B] 移动距离
  • Python Django基于协同过滤算法的招聘信息推荐系统【附源码、文档说明】
  • 深入解析TCP Keep-Alive机制:原理、作用与最佳实践
  • SSM考研助手管理系统
  • 基于MFC 的链接库
  • vue项目打包部署到maven仓库
  • 如何针对游戏、金融行业定制CC攻击防护规则?
  • 如何配置环境变量HADOOP_HOMEM、AVEN_HOME?不配置会怎么样
  • RCEP框架下eBay日本站选品战略重构:五维解析关税红利机遇
  • java使用 ​Stream 流对自定义对象数组去重的
  • 欧拉服务器操作系统安装MySQL
  • Ollama、vLLM、LMDeploy选型指南
  • 什么是ETF跟踪误差?场内基金佣金最低是多少?
  • 批量将不同位置的多个文件复制到一个文件夹
  • Oracle查询大表的全部数据
  • JDBC 初认识、速了解
  • 谷云科技iPaaS集成平台4月发布新版本V7.4
  • 上海咖啡消费有多“嗲”?咖啡馆已逾9000家,咖啡节主市集持续4天
  • 山西省朔州市政府党组成员、副市长李润军接受审查调查
  • 建行原副行长章更生被开除党籍:靠贷吃贷,大搞权钱交易
  • 外交部答澎湃:愿同阿曼在国际和地区事务中加强沟通协调
  • 习近平对双拥工作作出重要指示
  • 全球最大车展在上海启幕,解放日报头版头条:“看懂上海车展,就能预判未来”