菜品管理(day03)

公共字段自动填充
问题分析
业务表中的公共字段:
而针对于这些字段,我们的赋值方式为:
在新增数据时, 将createTime、updateTime 设置为当前时间, createUser、updateUser设置为当前登录用户ID。
在更新数据时, 将updateTime 设置为当前时间, updateUser设置为当前登录用户ID。
问题:代码冗余、不便于后期维护
实现思路
有四个公共字段,需要在新增/更新中进行赋值操作, 具体情况如下:
实现步骤:
- 自定义注解 AutoFill,用于标识需要进行公共字段自动填充的方法
- 自定义切面类 AutoFillAspect,统一拦截加入了 AutoFill 注解的方法,通过反射为公共字段赋值
- 在 Mapper 的方法上加入 AutoFill 注解
技术点:枚举、注解、AOP、反射
java--反射(reflection)_反射 reflection java-CSDN博客
SpringBootWeb 事务&AOP(day13)-CSDN博客
java -- 枚举和注解_java 枚举-CSDN博客
代码开发
•自定义注解 AutoFill
package com.sky.annotation;import com.sky.enumeration.OperationType;import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;/*** 自定义注解,用于标识某个方法需要进行功能字段自行填充
* @author 石头
* @version 1.0
*/
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface AutoFill {//数据库操作类型:UPDATE INSERTOperationType value();
}
Java中自定义注解的使用详解_java自定义注解使用-CSDN博客
自定义切面 AutoFillAspect
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.Signature;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.stereotype.Component;import java.lang.reflect.Method;
import java.time.LocalDateTime;/*** 自定义切面类,实现公告字段自动填充处理逻辑*/
@Aspect
@Component
@Slf4j
public class AutoFillAspect {/*** 切入点*/@Pointcut("execution(* com.sky.mapper.*.*(..)) && @annotation(com.sky.annotation.AutoFill)")public void autoFillPointCut() {}/*** 通知 自动填充公共字段* @param joinPoint*/@Before("autoFillPointCut()")public void autoFill(JoinPoint joinPoint) {log.info("公共字段自动填充...");//获取到当前被拦截方法上的数据库操作类型MethodSignature signature = (MethodSignature)joinPoint.getSignature();//获得方法签名对象//获得方法上的注解AutoFill autoFill = signature.getMethod().getAnnotation(AutoFill.class);//获得注解中的操作类型OperationType operationType = autoFill.value();//获取到当前被拦截的方法的参数---实体对象Object[] args = joinPoint.getArgs();//获取当前目标方法的参数if (args == null || args.length == 0) { //防止空指针return;}//实体对象Object entity = args[0];//准备赋值的数据LocalDateTime time = LocalDateTime.now();Long empId = BaseContext.getCurrentId();//根据当前不同的操作类型,为对应的属性通过反射赋值if (operationType == OperationType.INSERT) {//当前执行的是insert操作,为4个字段赋值try {//获得set方法对象----MethodMethod setCreateTime = entity.getClass().getDeclaredMethod(AutoFillConstant.SET_CREATE_TIME, LocalDateTime.class);Method setUpdateTime = entity.getClass().getDeclaredMethod(AutoFillConstant.SET_UPDATE_TIME, LocalDateTime.class);Method setCreateUser = entity.getClass().getDeclaredMethod(AutoFillConstant.SET_CREATE_USER, Long.class);Method setUpdateUser = entity.getClass().getDeclaredMethod(AutoFillConstant.SET_UPDATE_USER, Long.class);//通过反射调用目标对象的方法setCreateTime.invoke(entity, time);setUpdateTime.invoke(entity, time);setCreateUser.invoke(entity, empId);setUpdateUser.invoke(entity, empId);} catch (Exception ex) {log.error("公共字段自动填充失败:{}", ex.getMessage());}}else {//当前执行的是update操作,为2个字段赋值try {//获得set方法对象----MethodMethod setUpdateTime = entity.getClass().getDeclaredMethod(AutoFillConstant.SET_UPDATE_TIME, LocalDateTime.class);Method setUpdateUser = entity.getClass().getDeclaredMethod(AutoFillConstant.SET_UPDATE_USER, Long.class);//通过反射调用目标对象的方法setUpdateTime.invoke(entity, time);setUpdateUser.invoke(entity, empId);} catch (Exception ex) {log.error("公共字段自动填充失败:{}", ex.getMessage());}}}}
•在Mapper接口的方法上加入 AutoFill 注解
/*** 插入数据* @param category*/
@AutoFill(OperationType.INSERT)
@Insert("insert into category(type, name, sort, status, create_time, update_time, create_user, update_user)" +" VALUES" +" (#{type}, #{name}, #{sort}, #{status}, #{createTime}, #{updateTime}, #{createUser}, #{updateUser})")
void insert(Category category);/*** 根据id修改分类* @param category*/
@AutoFill(OperationType.UPDATE)
void update(Category category);
•将业务层为公共字段赋值的代码注释掉
功能测试
通过观察控制台输出的SQL来确定公共字段填充是否完成
新增菜品
需求分析和设计
产品原型
业务规则:
- 菜品名称必须是唯一的
- 菜品必须属于某个分类下,不能单独存在
- 新增菜品时可以根据情况选择菜品的口味
- 每个菜品必须对应一张图片
接口设计:
• 根据类型查询分类(已完成)• 文件上传• 新增菜品
•根据类型查询分类
•文件上传
•新增菜品
数据库设计(dish菜品表和dish_flavor口味表):


代码开发
开发文件上传接口:
开发文件上传接口:



开发新增菜品接口:

@Data
public class DishDTO implements Serializable {private Long id;//菜品名称private String name;//菜品分类idprivate Long categoryId;//菜品价格private BigDecimal price;//图片private String image;//描述信息private String description;//0 停售 1 起售private Integer status;//口味private List<DishFlavor> flavors = new ArrayList<>();}
开发新增菜品接口:




<!--useGeneratedKeys:true 表示获取主键值keyProperty="id" 表示将主键值赋给id属性
-->
<insert id="insert" useGeneratedKeys="true" keyProperty="id">insert into dish (status, name, category_id, price, image, description, create_time, update_time, create_user,update_user)values (#{status}, #{name}, #{categoryId}, #{price}, #{image}, #{description}, #{createTime}, #{updateTime},#{createUser}, #{updateUser})
</insert>


<insert id="insertBatch">insert into dish_flavor(dish_id, name, value) values<foreach collection="flavors" item="dishFlavor" separator=",">(#{dishFlavor.dishId},#{dishFlavor.name},#{dishFlavor.value})</foreach>
</insert>
功能测试
可以通过接口文档进行测试,也可以进行前后端联调测试
菜品分页查询
需求分析和设计
产品原型:
业务规则:
- 根据页码展示菜品信息
- 每页展示10条数据
- 分页查询时可以根据需要输入菜品名称、菜品分类、菜品状态进行查询
接口设计:

代码开发
根据菜品分页查询接口定义设计对应的DTO:
@Data
public class DishPageQueryDTO implements Serializable {private int page;private int pageSize;//菜品名称private String name;//分类idprivate Integer categoryId;//状态 0表示禁用 1表示启用private Integer status;}
根据菜品分页查询接口定义设计对应的VO:
public class DishVO implements Serializable {private Long id;//菜品名称private String name;//菜品分类idprivate Long categoryId;//菜品价格private BigDecimal price;//图片private String image;//描述信息private String description;//0 停售 1 起售private Integer status;//更新时间private LocalDateTime updateTime;//分类名称private String categoryName;//菜品关联的口味private List<DishFlavor> flavors = new ArrayList<>();}
根据接口定义创建DishController的page分页查询方法:
如果前端是通过 URL 的查询字符串(query string)或路径变量(path variables)来传递数据的(比如 GET 请求中的参数),那么在后端就不需要使用 @RequestBody
在 DishService 中扩展分页查询方法:
在 DishServiceImpl 中实现分页查询方法:
在 DishMapper 接口中声明 pageQuery 方法:
在 DishMapper.xml 中编写SQL:
功能测试
可以通过接口文档进行测试,最后完成前后端联调测试即可
删除菜品
需求分析和设计
产品原型:
业务规则:
- 可以一次删除一个菜品,也可以批量删除菜品
- 起售中的菜品不能删除
- 被套餐关联的菜品不能删除
- 删除菜品后,关联的口味数据也需要删除掉
接口设计:
数据库设计:
代码开发




创建SetmealDishMapper,声明getSetmealIdsByDishIds方法,并在xml文件中编写SQL:
在DishMapper.xml中声明deleteById方法并配置SQL:
在DishFlavorMapper中声明deleteByDishId方法并配置SQL:
功能测试
通过Swagger接口文档进行测试,通过后再前后端联调测试即可
修改菜品
需求分析和设计
产品原型:
接口设计:
• 根据 id 查询菜品• 根据类型查询分类(已实现)• 文件上传(已实现)• 修改菜品
根据id查询菜品
修改菜品
代码开发
根据id查询菜品 接口开发:
根据id查询菜品 接口开发:


修改菜品 接口开发:




功能测试
通过Swagger接口文档进行测试,通过后再前后端联调测试即可
上一节:
员工管理、分类管理(day02)-CSDN博客
下一节:
相关文章:
菜品管理(day03)
公共字段自动填充 问题分析 业务表中的公共字段: 而针对于这些字段,我们的赋值方式为: 在新增数据时, 将createTime、updateTime 设置为当前时间, createUser、updateUser设置为当前登录用户ID。 在更新数据时, 将updateTime 设置为当前时间…...
深入理解 Android 混淆规则
在 Android 开发中,混淆(Obfuscation)是一种保护代码安全的重要手段,通常通过 ProGuard 或 R8 工具来实现。本文将详细介绍 Android 混淆规则的基本原理、配置方法以及最佳实践,帮助开发者更好地保护应用代码。 博主博…...
《Keras 3 在 TPU 上的肺炎分类》
Keras 3 在 TPU 上的肺炎分类 作者:Amy MiHyun Jang创建日期:2020/07/28最后修改时间:2024/02/12描述:TPU 上的医学图像分类。 (i) 此示例使用 Keras 3 在 Colab 中查看 GitHub 源 简介 设置 本教程将介…...
从 Android 进行永久删除照片恢复的 5 种方法
从 Android 设备中丢失珍贵的照片可能是一种毁灭性的经历。无论是由于意外删除、软件故障还是系统更新,如何从 Android 永久恢复已删除的照片是一个普遍的问题。 幸运的是,有一些解决方案可以帮助找回丢失的记忆。本指南将涵盖您需要了解的有关如何检索…...
SDL2:Android APP编译使用
SDL2:Android APP编译使用 3. SDL2:Android APP编译使用3.1 Android Studio环境准备:3.2 构建Android APP(1)方式一:快速构建APK工程(2)方式二:自定义APK工程(…...
linux systemd 服务连续启动失败,不会再重启分析
1. 问题现象 在Linux 系统中,将自已写的可执行文件放到 systemd 服务中做成service 服务,以支持开机自启和失败重启。但是发现服务在重启多次失败后再也起不来,服务状态是 failed,并且报 start request repeated too quickly. 2.…...
【云岚到家】-day03-门户缓存方案选择
【云岚到家】-day03-门户缓存方案选择 1.门户常用的技术方案 什么是门户 说到门户马上会想到门户网站,中国比较早的门户网站有新浪、网易、搜狐、腾讯等,门户网站为用户提供一个集中的、易于访问的平台,使他们能够方便地获取各种信息和服务…...
在IDEA中使用通义灵码插件:全面提升开发效率的智能助手
在IDEA中使用通义灵码插件:全面提升开发效率的智能助手 随着软件开发行业对效率和质量要求的不断提高,开发者们一直在寻找能够简化工作流程、提升代码质量的工具。阿里云推出的通义灵码插件正是这样一个旨在帮助开发者更高效地编写高质量代码的强大工具…...
【正则表达式】从0开始学习正则表达式
正则表达式(英语:Regular Expression,在代码中常简写为regex、regexp或RE) 一、推荐学习网站 正则表达式 – 语法 | 菜鸟教程 正则表达式30分钟入门教程 | 菜鸟教程 编程胶囊-打造学习编程的最好系统 二、必知必记 2.1 元字符…...
PHP智慧小区物业管理小程序
🌟智慧小区物业管理小程序:重塑社区生活,开启便捷高效新篇章 🌟 智慧小区物业管理小程序是一款基于PHPUniApp精心雕琢的智慧小区物业管理小程序,它犹如一股清新的科技之风,吹进了现代智慧小区的每一个角落…...
Linux安装Docker教程(详解)
如果想要系统学习docker,建议进入官方文档中学习:docker官方文档 一. 基本概念 Docker Desktop 和 Docker Engine 有什么区别? Docker Desktop for Linux 提供用户友好的图形界面,可简化容器和服务的管理。它包括 Docker Engine,…...
开源AI微调指南:入门级简单训练,初探AI之路
112,如何让 113? 简单的微调你的 AI, 微调前的效果,怎么调教它都是 112. 要对其进行微调(比如训练113),可以按以下步骤进行。 确保你已经安装了以下工具和库: ollamallama3.2Pyt…...
Leetcode 91. 解码方法 动态规划
原题链接:Leetcode 91. 解码方法 自己写的代码: class Solution { public:int numDecodings(string s) {int ns.size();vector<int> dp(n,1);if(s[n-1]0) dp[n-1]0;for(int in-2;i>0;i--){if(s[i]!0){string ts.substr(i,2);int tmpatoi(t.c…...
ASP .NET Core 学习(.NET9)配置接口访问路由
新创建的 ASP .NET Core Web API项目中Controller进行请求时,是在地址:端口/Controller名称进行访问的,这个时候Controller的默认路由配置如下 访问接口时,是通过请求方法(GET、Post、Put、Delete)进行接口区分的&…...
将 AzureBlob 的日志通过 Azure Event Hubs 发给 Elasticsearch(2 换掉付费的Event Hubs)
前情回顾: 将 AzureBlob 的日志通过 Azure Event Hubs 发给 Elasticsearch(1)-CSDN博客 前边的方案是挺好的,但 Azure Event Hubs 是付费服务,我这里只是一个获取日志进行必要的分析,并且不要求实时性&am…...
idea 如何安装 github copilot
idea 如何安装 github copilot 要在 IntelliJ IDEA 中安装 GitHub Copilot,可以按照以下步骤操作: 打开 IntelliJ IDEA: 启动 IntelliJ IDEA。 打开插件管理器: 点击菜单栏中的 File。 选择 Settings(Windows/Linux)或 Prefere…...
1.17学习
crypto nssctf-[SWPUCTF 2021 新生赛]crypto8 不太认识这是什么编码,搜索一下发现是一个UUENCODE编码,用在线工具UUENCODE解码计算器—LZL在线工具解码就好 misc buuctf-文件中的秘密 下载附件打开后发现是一个图片,应该是一个图片隐写&…...
Redis系列之底层数据结构整数集IntSet
Redis系列之底层数据结构整数集IntSet 什么是IntSet IntSet,整数集合,是Redis集合类型的一种底层数据结构,当一个集合只包含整数值元素,并且这个集合的元素数量不多时,redis就会选用intset作为底层实现。 IntSet的数…...
外包公司名单一览表(成都)
大家好,我是苍何。 之前写了一篇武汉的外包公司名单,评论区做了个简单统计,很多人说,在外包的日子很煎熬,不再想去了。 有小伙伴留言说有些外包会强制离职,不行就转岗,让人极度没有安全感。 这…...
个人vue3-学习笔记
声明:这只是我个人的学习笔记(黑马),供以后复习用 。一天学一点,随时学随时更新。明天会更好的! 这里只给代码,不给运行结果,看不出来代码的作用我也该进厂了。。。。。 Day1 使用create-vue创建项目。 1.检查版本。 node -v 2.创建项目 npm init vue@latest 可…...
设计师不用写代码了?实测TRAE SOLO Builder如何将Figma稿秒变可交互网页
设计师如何用TRAE SOLO Builder实现零代码网页开发 在数字产品设计领域,设计师与开发者之间的协作断层长期存在。设计精美的Figma稿转化为实际网页时,往往面临还原度不足、交互细节丢失等问题。TRAE SOLO Builder的出现,正在重新定义设计到开…...
DeepSeek技术解析:如何利用128K上下文窗口提升代码生成效率
1. 128K上下文窗口的技术革命 第一次看到DeepSeek支持128K上下文窗口时,我的反应和大多数开发者一样:"这数字是不是多打了个0?"毕竟在主流大模型还停留在32K上下文的时候,这个参数直接翻了四倍。但实测下来才发现&#…...
Emu3.5:vision、text 的vocab id 体系
Emu3.5 中视觉与语言 ID 体系的完整分析报告 https://huggingface.co/BAAI/Emu3.5 1. 报告目的 本文专门回答一个问题: Emu3.5 中,图片在进入大模型之前,视觉 tokenizer 的离散索引、视觉 special token 字符串、以及 LLM 最终接收的统一词表整数 id,三者之间到底是什么…...
如何为 Play With Docker 贡献代码:社区参与与开发流程详解
如何为 Play With Docker 贡献代码:社区参与与开发流程详解 【免费下载链接】play-with-docker You know it, you use it, now its time to improve it. PWD!. 项目地址: https://gitcode.com/gh_mirrors/pl/play-with-docker Play With Docker(P…...
pythondjango心理咨询vue
目录Python Django 心理咨询系统功能分析后端(Django)功能模块前端(Vue.js)功能模块技术实现关键点扩展功能建议项目技术支持源码获取详细视频演示 :文章底部获取博主联系方式!同行可合作Python Django 心理…...
大模型核心技术概述:Token、Prompt、Tool与Agent的关系详解
你是不是经常听人聊AI时蹦出这些词:LLM、Token、Context、Prompt、Tool、MCP、Agent?听着好像都认识,但真要问“这到底是啥”,又有点懵。今天把这些词一个个拆开揉碎,讲清楚它们到底是啥、有啥用、又是怎么串起来的。 …...
LeetCode知识点总结 - 524
LeetCode 524. Longest Word in Dictionary through Deleting考点难度ArrayMedium题目 Given a string s and a string array dictionary, return the longest string in the dictionary that can be formed by deleting some of the given string characters. If there is mor…...
看完就会:2026年最强AI论文写作软件榜单,AI工具一键写高质论文
2026 年实测 10 款主流 AI 论文工具,千笔AI以全流程覆盖 语义级降重 免费查重领跑综合榜;ThouPen 稳坐留学生毕业全流程工具头把交椅;免费工具中DeepSeek Scholar、豆包学术版表现亮眼,30 分钟即可生成万字高质量初稿࿰…...
Windows下用C语言实现控制台鼠标交互:从获取坐标到点击响应全流程
Windows控制台鼠标交互开发实战:C语言实现精准坐标捕获与事件响应 引言:当命令行遇上图形交互 在大多数开发者印象中,控制台程序总是与键盘输入绑定在一起——那个闪烁的光标等待着用户键入命令,然后返回几行单调的文字输出。但Wi…...
LLVM指令调度实战:如何用llvm-mca优化AArch64代码性能(附TSV110配置示例)
LLVM指令调度实战:如何用llvm-mca优化AArch64代码性能(附TSV110配置示例) 在ARM架构的性能优化领域,指令调度质量直接影响着关键计算任务的吞吐量。本文将带您深入llvm-mca工具链的实际应用,通过TSV110处理器的具体案例…...















