Java项目——苍穹外卖(一)

Entity、DTO、VO

Entity(实体)

  • Entity 是表示数据库表的对象,通常对应数据库中的一行数据。它通常包含与数据库表对应的字段,并可能包含一些业务逻辑。

DTO(数据传输对象)

  • 作用:DTO 是用于在不同层之间传输数据的对象,通常用于网络传输或服务间调用。DTO 主要用于减少网络请求的次数,携带数据而不包含业务逻辑。
  • 特点:DTO类通常只包含数据字段和相应的getter和setter方法,不包含任何业务逻辑。它们可能包含与Entity类相似的字段,但也可能根据需要进行裁剪或扩展

 VO(视图对象)

  • 位置:虽然VO这个术语不如DTO和Entity那样普遍,但在一些项目中,你可能会看到vo包用于存放视图对象。
  • 作用:VO主要用于封装返回给前端的数据。它们可以被视为DTO的一种特殊形式,专门用于视图层的数据展示。VO可以根据前端的需求进行定制,包含前端需要展示的所有数据,以及可能的前端逻辑(如格式化数据)
  • 特点:VO类与DTO类似,主要关注数据的封装和传输,但更侧重于前端展示的需求。它们可能包含与Entity或DTO不同的字段,或者对字段进行不同的封装和格式化。

举例

对于数据库中的employee表:

@Data
@Builder//构建器注解,此类的对象可以使用builder方法进行构建
@NoArgsConstructor
@AllArgsConstructor
public class Employee implements Serializable {

    private static final long serialVersionUID = 1L;

    private Long id;

    private String username;

    private String name;

    private String password;

    private String phone;

    private String sex;

    private String idNumber;

    private Integer status;

    //@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
    private LocalDateTime createTime;

    //@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
    private LocalDateTime updateTime;

    private Long createUser;

    private Long updateUser;

}
@Data
public class EmployeeDTO implements Serializable {

    private Long id;

    private String username;

    private String name;

    private String phone;

    private String sex;

    private String idNumber;

}

 总结

Entity:

  • 与数据库表对应,包含数据的持久化逻辑。
  • 主要用于数据库操作,在数据访问层与数据库交互时使用。
  • 封装数据库操作的细节
  • 属性通常与数据库表字段一一对应

DTO:

  • 用于层与层之间的数据传输,不包含业务逻辑。
  • 用于系统内部数据传输,在服务层调用中传递数据。
  • 封装业务逻辑的细节
  • 可以包含额外的或部分的属性

VO:

  • 用于封装返回给前端的数据,可能包含前端展示所需的特定逻辑。
  • 在展示层向用户展示数据时使用
  • 封装展示逻辑的细节
  • 可以包含额外的或部分的属性

公共字段填充(AOP)

问题分析

在增加或查询操作中,设置下列字段时代码冗余,不便于后期维护

实现思路

技术点:枚举、注解、AOP、反射

一、创建枚举类

/**
 * 数据库操作类型
 * 枚举类
 */
public enum OperationType {

    /**
     * 更新操作
     */
    UPDATE,

    /**
     * 插入操作
     */
    INSERT

}

二、创建自定义注解类

/**
 * 自定义注解,用于标识某个方法需要进行功能字段自动填充处理
 * 这个类可以作为一个注解进行使用
 */
@Target(ElementType.METHOD)//指定当前的自定义注解只能加在方法上
@Retention(RetentionPolicy.RUNTIME)//固定写法
public @interface AutoFill {
    //数据库操作类型:UPDATE INSERT(只有增加和修改操作涉及公共字段填充)
    OperationType value();
}

三、在切入点方法上加入自定义注解,并指定其属性(更新操作/插入操作)

四、创建切面类,在切面类中实现通知逻辑

/**
 * 自定义切面类,实现公共字段自动填充处理逻辑
 */
@Aspect//设置当前类为切面类,告诉spring这个类是用来做AOP的
@Component//通知类(切面类)必须配置成Spring管理的bean
@Slf4j
public class AutoFillAspect {
    //此切入点表达式前半部分锁定mapper包下所有类所有方法,后半部分锁定方法中加入了AutoFill注解的方法
    @Pointcut("execution(* com.sky.mapper.*.*(..)) && @annotation(com.sky.annotation.AutoFill)")
    public void autoFillPointCut(){}

    /**
     * 前置通知,在通知中进行公共字段的赋值
     */
    @Before("autoFillPointCut()")
    public void autoFill(JoinPoint joinPoint){
        log.info("开始进行公共字段自动填充");

        //1.获取当前被拦截的方法上的数据库操作类型
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();//获得方法签名对象,并向下转型为MethodSignature
        AutoFill antoFill = signature.getMethod().getAnnotation(AutoFill.class);//获得方法上的注解对象
        OperationType operationType = antoFill.value();//获得数据库操作类型
        //这三行的代码最终作用是获得注解中的数据库操作类型,也就是 @AutoFill(value = OperationType.UPDATE)中的value

        //2.获取当前被拦截的方法的参数--实体对象
        Object[] args = joinPoint.getArgs();//获取通知点方法的参数
        if(args == null || args.length == 0){//如果没有参数则直接返回
            return;
        }

        Object entity = args[0];

        //3.准备赋值的数据
        LocalDateTime now = LocalDateTime.now();//update_time的值
        Long currentId = BaseContext.getCurrentId();//update_user的值

        //4.根据当前不同的操作类型,为对应的属性通过反射来赋值
        if(operationType == operationType.INSERT){
            //新增操作为4个公共字段赋值
            try {
                //4.1通过反射获取这四个方法
                //使用常量类AutoFillConstant中的常量代替方法名,防止自己写写错
                Method setCreateTime = entity.getClass().getDeclaredMethod(AutoFillConstant.SET_CREATE_TIME, LocalDateTime.class);
                Method setCreateUser = 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);

                //4.2通过反射调用获取到的四个方法为对象属性赋值

                setCreateTime.invoke(entity,now);//entity中存的是Employee
                //这行代码:使用entity对象调用setCreateTime方法,给方法传递参数now

                setCreateUser.invoke(entity,currentId);
                setUpdateTime.invoke(entity,now);
                setUpdateUser.invoke(entity,currentId);
            } catch (Exception 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) {
                e.printStackTrace();
            }
        }
    }
}

总结

1、创建枚举类,其中有插入、修改操作变量

2、创建自定义注解类,其中属性引用枚举类的变量

  • @Target(ElementType.METHOD)//指定当前的自定义注解只能加在方法
  • @Retention(RetentionPolicy.RUNTIME)//固定写法3、

3、在mapper层中的插入、修改方法上加入自定义注解,并通过属性指定其操作类型(插入或修改)

4、创建切面类,进行切入点表达式和通知的实现

  • @Aspect//设置当前类为切面类,告诉spring这个类是用来做AOP的
  • @Component//通知类(切面类)必须配置成Spring管理的bean

文件上传

利用阿里云实现文件上传功能,将文件上传并存储到阿里云存储空间,并返回文件的访问地址,以便在前端可以回显文件

一、在sky-common中的properties包下创建AliOssProperties类

@Component
@ConfigurationProperties(prefix = "sky.alioss")//将配置文件中sky.alioss的相关属性值赋给此类中的成员
@Data
public class AliOssProperties {

    private String endpoint;
    private String accessKeyId;
    private String accessKeySecret;
    private String bucketName;//Bucket名称
}

 二、在sky-common中的utils包下创建AliOssUtil类

 此类实现upload方法,实现文件上传功能,此方法返回文件的访问路径

@Data
@AllArgsConstructor
@Slf4j
public class AliOssUtil {

    private String endpoint;
    private String accessKeyId;
    private String accessKeySecret;
    private String bucketName;

    /**
     * 文件上传
     *
     * @param bytes
     * @param objectName
     * @return
     */
    //将文件上传到阿里云并返回文件的请求路径
    //第一个参数:文件的字节数组,第二个参数:拼接uuid后的新文件名
    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/ObjectName
        StringBuilder stringBuilder = new StringBuilder("https://");
        stringBuilder
                .append(bucketName)
                .append(".")
                .append(endpoint)
                .append("/")
                .append(objectName);

        log.info("文件上传到:{}", stringBuilder.toString());

        return stringBuilder.toString();
    }
}

三、在sky-server模块下的config包中创建OssConfiguration类

       此配置类用于创建AliossUtil对象:实现aliOssUtil方法,此方法将AliOssProperties对象作为参数传入,方法内读取AliOssProperties中的四个属性,将其包装为AliossUtil对象返回,并将这个AliossUtil交给ioc容器管理,需要时可以直接依赖注入(注入的AliossUtil对象的四个属性已经赋过值)

       此类相当于一个中间媒介,关联AliOssProperties类和AliOssUtil,接收一个AliOssProperties并将其转换为一个AliOssUtil后返回,本质就是将AliOssProperties中从.yml中读取到的四个属性值赋给AliOssUtil中相应的四个属性

这四个属性的的去向总结为:

.yml(人为预先定义好四个属性的值)--->AliOssProperties类(利用注解从.yml中读取四个属性)-->OssConfiguration类(从类中方法接收的参数AliOssProperties对象中读取四个属性,并封装为AliOssUtil对象)-->AliOssUtil对象(OssConfiguration类中方法的返回值)

@Configuration
@Slf4j
public class OssConfiguration {
    @Bean
    @ConditionalOnMissingBean//保证整个容器中只有一个AliOssUtil对象
    public AliOssUtil aliOssUtil(AliOssProperties aliOssProperties){
        log.info("开始创建阿里云文件上传工具类对象:{}",aliOssProperties);

        return new AliOssUtil(aliOssProperties.getEndpoint(),
                aliOssProperties.getAccessKeyId(),
                aliOssProperties.getAccessKeySecret(),
                aliOssProperties.getBucketName());
    }
}

四、在controller层中创建CommonController类

此类用来对前端文件上传的请求进行响应,前端传来一个文件参数

  1. 在此类中构造新的文件名称(uuid拼接原始文件名后缀)
  2. 调用AliOssUtil中的upload方法进行文件上传(方法参数为文件的字节数组和新文件名字符串)
  3. 上传方法返回值为文件的访问路径,将其封装为Result类型响应给前端
@RestController
@RequestMapping("/admin/common")
@Api(tags = "通用接口")
@Slf4j
public class CommonController {
    @Autowired
    private AliOssUtil aliOssUtil;

    /**
     * 文件上传
     * @param file
     * @return
     */
    @PostMapping("/upload")
    @ApiOperation("文件上传")
    public Result<String> upload(MultipartFile file){
        log.info("文件上传:{}",file);

        try {
            //获取原始文件名
            String originalFilename = file.getOriginalFilename();
            //将原始文件名的后缀截取出来
            String extension = originalFilename.substring(originalFilename.lastIndexOf("."));
            //构造新文件名称(uuid拼接原始文件名后缀)
            String objectName = UUID.randomUUID().toString() + extension;
            //获取文件的请求路径
            String filePath = aliOssUtil.upload(file.getBytes(), objectName);
            return Result.success(filePath);
        } catch (IOException e) {
            log.error("文件上传失败:{}",e);
        }
        return null;
    }
}

总结

文件上传功能的核心为CommonController类和AliOssUtil类

  • CommonController类用来对前端请求进行响应,调用依赖注入的AliOssUtil对象的upload方法进行文件上传,并返回文件的请求路径
  • AliOssUtil类用来实现upload方法进行文件上传功能,实现的前提是已获取到四个属性的值
  • 剩下的AliOssProperties类和OssConfiguration类是用来创建AliOssUtil对象(将其四个属性赋值)并将其交给ioc容器(以便在CommonController中可以直接依赖注入)

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mfbz.cn/a/873004.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Leetcode Hot 100刷题记录 -Day10(合并区间)

合并区间 问题描述&#xff1a; 以数组 intervals 表示若干个区间的集合&#xff0c;其中单个区间为 intervals[i] [starti,endi] 。请你合并所有重叠的区间&#xff0c;并返回 一个不重叠的区间数组&#xff0c;该数组需恰好覆盖输入中的所有区间 。 示例 1&#xff1a; 输入&…

Netty系列-3 ChannelFuture和ChannelPromise介绍

背景 Netty源码中大量使用了Future和Promise&#xff0c;学习ChannelFuture和ChannelFuture有助于理解Netty的设计思路。 本文的重点内容在于梳理清楚这些类的关系以及结合源码实现介绍这些类的作用&#xff0c;其中核心逻辑在于DefaultPromise和DefaultChannelPromise&#x…

UnLua环境搭建

一、环境搭建 1、下载UnLua工程&#xff1a;https://github.com/Tencent/UnLua 2、复制Plugins/UnLua目录下的插件到自己的项目中 3、重新生成自己的VS工程 4、打开VS工程的项目名.Build.cs文件&#xff0c;引用UnLua插件,重新编译工程 PublicDependencyModuleNames.AddRan…

Post-Training有多重要?一文带你了解全部细节

1. 简介 随着LLM学界和工业界日新月异的发展&#xff0c;不仅预训练所用的算力和数据正在疯狂内卷&#xff0c;后训练&#xff08;post-training&#xff09;的对齐和微调方法也在不断更新。InstructGPT、WebGPT等较早发布的模型使用标准RLHF方法&#xff0c;其中的数据管理风…

node.js实现阿里云短信发送

效果图 实现 一、准备工作 1、官网直达网址&#xff1a; 阿里云 - 短信服务 2、按照首页提示依次完成相应资质认证和短信模板审核&#xff1b; 3、获取你的accessKeySecret和accessKeyId&#xff1b; 方法如下&#xff1a; 获取AccessKey-阿里云帮助中心 4、获取SignNa…

Stirling-PDF:基于Web的开源PDF处理工具

PDF文件进行各种各样的操作&#xff0c;比如合并、拆分、加密、解密、旋转、压缩等等&#xff0c;功能超全&#xff0c;而且开源免费&#xff0c;简直是神器&#xff01; GitHub - Stirling-Tools/Stirling-PDF: #1 Locally hosted web application that allows you to perfor…

图形验证码从设计到实现

验证码的使用场景 图形验证码在我们的日常使用中是非常常见的&#xff0c;比如一些App、小程序、PC网站等。涉及到的业务也比较广泛、例如用户登录流程、注册流程、找回密码。下面我们来大致梳理下上述流程&#xff1a; 登录流程 用户首先在登录界面输入手机号然后通过图形验…

心脑血管科张景龙医生:心功能不全,全面解析心脏健康的隐忧

在探讨人体健康的众多话题中&#xff0c;心功能不全是一个不容忽视的重要议题。它不仅关乎心脏这一生命之泵的核心功能&#xff0c;还直接影响到我们的生活质量乃至生命安全。本文旨在以权威、清晰、通俗且实用的方式&#xff0c;全面解析心功能不全的含义、成因、症状、诊断及…

单元测试 Mock不Mock?

文章目录 前言单元测试没必要?Mock不Mock?什么是Mock?Mock的意义何在? 如何Mock&#xff1f;应该Mock什么&#xff1f;Mock 编写示例 总结 前言 前段时间&#xff0c;我们团队就单元测试是否采用 Mock 进行了一番交流&#xff0c;各有各的说法。本文就单元测试 Mock不Mock…

EmguCV学习笔记 VB.Net 9.2 VideoWriter类

版权声明&#xff1a;本文为博主原创文章&#xff0c;转载请在显著位置标明本文出处以及作者网名&#xff0c;未经作者允许不得用于商业目的。 EmguCV是一个基于OpenCV的开源免费的跨平台计算机视觉库,它向C#和VB.NET开发者提供了OpenCV库的大部分功能。 教程VB.net版本请访问…

【数据结构与算法 | 灵神题单 | 删除链表篇】力扣3217, 82, 237

总结&#xff0c;删除链表节点问题使用到列表&#xff0c;哈希表&#xff0c;递归比较容易超时&#xff0c;我觉得使用计数排序比较稳&#xff0c;处理起来也不是很难。 1. 力扣3217&#xff1a;从链表中移除在数组中的节点 1.1 题目&#xff1a; 给你一个整数数组 nums 和一…

Azure AI Search 中的二进制量化:优化存储和加快搜索速度

随着组织继续利用生成式 AI 的强大功能来构建检索增强生成 (RAG) 应用程序和代理&#xff0c;对高效、高性能和可扩展解决方案的需求从未如此强烈。 今天&#xff0c;我们很高兴推出二进制量化&#xff0c;这项新功能可将向量大小减少高达 96%&#xff0c;同时将搜索延迟减少高…

集合及映射

1、集合类图 1&#xff09;ArrayList与LinkedList 区别 LinkedList 实现了双向队列的接口&#xff0c;对于数据的插入速度较快&#xff0c;只需要修改前后的指向即可&#xff1b;ArrayList对于特定位置插入数据&#xff0c;需要移动特定位置后面的数据&#xff0c;有额外开销 …

Spring Boot-自定义banner

在 Spring Boot 应用中&#xff0c;你可以自定义启动时显示的 banner。这些 banner 可以包括图形、文字或者其他形式的标识。如图所示&#xff1a; 1. 使用 banner.txt 文件 默认情况下&#xff0c;Spring Boot 使用项目的 banner.txt 文件中的内容作为启动时的 banner。你可以…

Verilog语法+:和-:有什么用?

Verilog语法:和-:主要用于位选择&#xff0c;可以让代码更简洁。 一、位选择基础 在Verilog中&#xff0c;位选择可以通过直接索引来实现&#xff0c;例如&#xff1a; reg [7:0] data; wire select_a; wire [2:0] select_b; assign select_a data[3]; assign select_b …

【Hot100】LeetCode—739. 每日温度

目录 1- 思路单调栈 2- 实现⭐739. 每日温度——题解思路 3- ACM 实现 原题链接&#xff1a;739. 每日温度 1- 思路 单调栈 寻找当前位置的下一个比他大的元素的位置利用 Stack 栈维护一个单调栈 当前元素 < 栈顶元素&#xff0c;直接压栈否则 利用 while 循环判断 2- 实…

备忘录怎么隐藏起来 适合记录秘密的备忘录推荐

备忘录作为我们日常生活中常用的记录工具&#xff0c;经常被用来记录重要资料或工作事项。然而&#xff0c;当我们需要在备忘录中记录一些涉及个人隐私的内容时&#xff0c;如何确保这些信息不被他人轻易窥视&#xff0c;保护内容的安全就显得尤为重要。 想象一下&#xff0c;…

【数学建模国赛思路预约】2024全国大学生数学建模竞赛助攻思路、代码、论文

2024年全国大学生数学建模大赛马上就要开始了&#xff0c;大家有没有准备好呢&#xff0c;今年将会和之前一样&#xff0c;将会在比赛赛中时期为大家提供比赛各题的相关解题思路、可运行代码参考以及成品论文。 一、分享计划表如下所示 1、 赛中分享内容包括&#xff08;2023国…

【LeetCode】06.Z字形变换

题目要求 解题思路 首先映入我们脑海的就是暴力。这一方法可行&#xff0c;但是时间复杂度空间复杂度很高&#xff0c;因此我们使用找规律的方法。这样的话我们可以模拟插入下标&#xff0c;这样的话很容易发现首行和末行插入的位置刚好是d2*n-2&#xff0c;而中间行的两个位置…

视频编码与传输 学习笔记 1 一些视频压缩算法的介绍

大概是这么个结构&#xff1a; 说白了&#xff0c;就是视频太大&#xff0c;不压缩不行&#xff0c;因此我们会用压缩比非常夸张但对于视频来说效果很好的压缩方法先对视频压缩&#xff08;source coding&#xff09;然后把压缩后的视频发出去&#xff0c;要看的时候再解压。 就…