菜品管理(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 可…...
基于算法竞赛的c++编程(28)结构体的进阶应用
结构体的嵌套与复杂数据组织 在C中,结构体可以嵌套使用,形成更复杂的数据结构。例如,可以通过嵌套结构体描述多层级数据关系: struct Address {string city;string street;int zipCode; };struct Employee {string name;int id;…...

C++_核心编程_多态案例二-制作饮品
#include <iostream> #include <string> using namespace std;/*制作饮品的大致流程为:煮水 - 冲泡 - 倒入杯中 - 加入辅料 利用多态技术实现本案例,提供抽象制作饮品基类,提供子类制作咖啡和茶叶*//*基类*/ class AbstractDr…...

DBAPI如何优雅的获取单条数据
API如何优雅的获取单条数据 案例一 对于查询类API,查询的是单条数据,比如根据主键ID查询用户信息,sql如下: select id, name, age from user where id #{id}API默认返回的数据格式是多条的,如下: {&qu…...

BCS 2025|百度副总裁陈洋:智能体在安全领域的应用实践
6月5日,2025全球数字经济大会数字安全主论坛暨北京网络安全大会在国家会议中心隆重开幕。百度副总裁陈洋受邀出席,并作《智能体在安全领域的应用实践》主题演讲,分享了在智能体在安全领域的突破性实践。他指出,百度通过将安全能力…...

代码规范和架构【立芯理论一】(2025.06.08)
1、代码规范的目标 代码简洁精炼、美观,可持续性好高效率高复用,可移植性好高内聚,低耦合没有冗余规范性,代码有规可循,可以看出自己当时的思考过程特殊排版,特殊语法,特殊指令,必须…...
从面试角度回答Android中ContentProvider启动原理
Android中ContentProvider原理的面试角度解析,分为已启动和未启动两种场景: 一、ContentProvider已启动的情况 1. 核心流程 触发条件:当其他组件(如Activity、Service)通过ContentR…...

Linux中《基础IO》详细介绍
目录 理解"文件"狭义理解广义理解文件操作的归类认知系统角度文件类别 回顾C文件接口打开文件写文件读文件稍作修改,实现简单cat命令 输出信息到显示器,你有哪些方法stdin & stdout & stderr打开文件的方式 系统⽂件I/O⼀种传递标志位…...
前端高频面试题2:浏览器/计算机网络
本专栏相关链接 前端高频面试题1:HTML/CSS 前端高频面试题2:浏览器/计算机网络 前端高频面试题3:JavaScript 1.什么是强缓存、协商缓存? 强缓存: 当浏览器请求资源时,首先检查本地缓存是否命中。如果命…...

Linux系统:进程间通信-匿名与命名管道
本节重点 匿名管道的概念与原理匿名管道的创建命名管道的概念与原理命名管道的创建两者的差异与联系命名管道实现EchoServer 一、管道 管道(Pipe)是一种进程间通信(IPC, Inter-Process Communication)机制,用于在不…...

多模态学习路线(2)——DL基础系列
目录 前言 一、归一化 1. Layer Normalization (LN) 2. Batch Normalization (BN) 3. Instance Normalization (IN) 4. Group Normalization (GN) 5. Root Mean Square Normalization(RMSNorm) 二、激活函数 1. Sigmoid激活函数(二分类&…...