type
status
date
slug
summary
tags
category
icon
password
😀
”总有人间一两风 填我十万八千梦“

📝 实习专项----超体日记

RPC框架(Remote Procedure Call Framework)

  • 一种允许程序通过网络调用另一个地址空间(通常是共享网络的另一台机器上)的过程或函数的技术。它使得程序能够像调用本地资源一样轻松访问远端系统资源,而不需要程序员显式德编写通信代码
  • 核心概念
    • 定义接口、序列化、网络传输、反序列化、调用远程方法、反序列化结果
      • 定义接口‌:首先定义客户端和服务端之间通信的接口,这些接口包含需要在远程服务器上调用的方法。
      • 序列化‌:当客户端调用远程方法时,方法的参数会被序列化为字节流以便在网络上传输。
      • 网络传输‌:序列化后的请求通过网络协议(如HTTP、TCP等)传输到远程服务器。
      • 反序列化‌:服务器接收到字节流后,将其反序列化为方法调用所需的参数,然后调用相应的方法或函数。
      • 调用远程方法‌:服务器根据反序列化后的参数执行方法,并将结果序列化后通过网络传输回客户端。
      • 反序列化结果‌:客户端接收到结果后,将其反序列化为方法调用的返回值。

RESTful API(Representational State Transfer)

  • 遵循REST架构原则的API设计,用于网络应用程序之间进行通信。通过HTTP协议与客户端进行交互,使用标准的HTTP方法(如GET、POST、PUT、DELETE等)来操作资源。每个资源通过URI(Uniform Resource Identifier)唯一标识,返回的数据格式通常是JSON或XML。

Nacos注册中心(数据中心)

  • nacos启动
    • 双击startup.cmd或者使用startup.cmd -m standalone(在cmd里执行,-m是模式,standalone单机模式)
  • 入门使用
    • 导入依赖 阿里巴巴 cloud、阿里巴巴 cloud的nacos
    • 在yml中增加nacos的地址、就可以直接在public服务的default集群注册一个nacos服务
  • Nacos服务分级存储模型
    • 服务调用应该尽可能选择本地集群的服务,跨集群调用延迟较高,只有在本地集群不可访问时,再去访问其他集群,而nacos引入集群概念就是为了解决跨集群访问问题。
  • 服务集群属性
    • 修改application.yml,增加discovery: cluster-name: xx
    • 在Nacos控制台可以看到集群的变化
    • notion image
  • 对于不同服务的访问规则是轮询的,这就导致了在服务调用时不会优先访问同集群的服务。因此我们必须去修改负载均衡 规则的IRule为NacosRule,这样就会优先寻找与自己同集群的服务,在本地集群有多个时就会随机调用。NacosRule如果在本地集群没有找到对应服务就会去其他集群,但这样子就会发出警告
notion image
  • Nacos注册中心,根据权重负载均衡
    • 服务器的设备性能有差异,部分实例所在机器性能较好,另一些较差,我们希望性能好的机器承担更多的用户请求
    • Nacos提供了权重配置来控制访问频率,权重越大,则访问频率越高
    • notion image
      notion image
  • Nacos注册中心(数据中心),环境隔离-namespace(用来做最外层隔离)
notion image
  • 命名空间可以分组,组可以分服务/数据,服务可以分集群,集群下是实例
notion image
  • 修改服务的命名空间(在yml代码里修改)
    • 在discovery: namespace: 填命名空间的ID
    • 对于不同命名空间的服务,无法相互调用,会报No instances available for 服务(该服务无可用实例)
    • notion image
  • Nacos与Eureka的对比
notion image

OpenFeign服务调用

一个用于Java的声明式HTTP客户端,主要用于简化REST API的调用,通常与SpringCloud结合使用,提供方便的接口来与微服务进行交互,Feign会自动生成实现类,并通过HTTP请求调用远程服务。

主要特性和功能

  • 声明式调用:OpenFeign允许开发者通过注解的方式定义HTTP请求,无需手动编写底层的HTTP客户端代码。例如,使用@FeignClient注解来之定要调用的服务器名称
  • 支持多种编码格式:OpenFeign能够处理JSON、XML等多种数据格式,并支持自定义序列化和反序列化。
  • 服务发现和负载均衡:与springcloud的集成使得Feign可以与Eureka、Ribbon等组件无缝工作,实现服务发现和负载均衡。
  • 熔断机制:与Hystrix继承,实现服务降级和熔断,提高系统的稳定性
  • 请求拦截:可以添加自定义的请求拦截器,在请求发送之前进行处理。
  • 日志记录:内置的日志功能,帮助调试和监控请求。

使用步骤

  • 添加依赖:在 Maven 项目的 pom.xml 中添加 OpenFeign 的依赖。
  • 启用Feign客户端:在主应用类启用Feign客户端,使用@EnableFeignClients 注解。
  • 定义Feign接口:创建一个接口,并使用@FeignClient 注解来配置服务名称和其他参数。
  • 注入和使用:在需要使用的地方注入Feign接口的实现类,并通过该实现类调用远程服务

自己的理解

openFeign与导包引用

导包引用

  • 将整个模块打包放在其他模块的pom里去引用
    • 可能会造成循环依赖,耦合度太高,会造成循环依赖错误

openFeign

  • 通过一个接口和类来实现其他模块的引用
    • 通过HTTP请求调用远程服务,解耦服务之间的依赖,接口(xxxClient)里写服务的请求地址,类(FallbackFactory)里写openFeign调用失败时的处理逻辑
    • 接口(远程服务的接口,一般带有RESTful api,其地址就是controller对应方法的restful api地址,例如下面的就是在order模块下的changeStatus方法)
    • @FeignClient( name = "${feign-client.sy-order}", fallbackFactory = SyOrderFactory.class)public interface SyOrderClient { @GetMapping("/api/synergy/order/changeStatus") Result changeStatus(@RequestParam("id") String id);}
    • 类(处理逻辑,调用失败才走,成功的话直接走Controller)
    • @Componentpublic class SyOrderFactory implements FallbackFactory<SyOrderClient> {​ private static final Logger log = LoggerFactory.getLogger(SyOrderFactory.class);​ @Override public SyOrderClient create(Throwable throwable) { log.error("应用权限服务调用失败:{}", throwable.getMessage()); return new SyOrderClient() { @Override public Result changeStatus(String id) { return Result.fail("操作失败"); } } }}

RabbitMQ相关介绍与使用(简)

RabbitMQ(MQ消息队列)是一个消息代理软件,实现了高级消息队列协议(AMQP),它用于分布式系统中传递信息,解耦发送和接收消息的应用程序

基本使用方法

先启动MQ服务,然后创建连接
  • 使用RabbitMQ客户端库(如spring-boot-starter-amqp)创建与MQ的连接
声明队列
  • 在MQ中声明一个队列,用于存储消息
发送消息
  • 将消息发送到指定的队列
接收消息
  • 从队列中接收消息并进行处理

小例子

发送(生产者)

driverData方法将SyDriver对象转换成JSON字符串,并通过getSendMq方法发送到MQ队列

getSendMq(自定义的私有方法)

借助了Spring AMQP框架的RabbitTemplate来发消息,并通过MessagePostProcessor来设置消息的属性。

参数(自定义)

getSendMq,私有方法,用于将消息发送到指定的 RabbitMQ 队列。
  • queues:队列名称
  • menu:菜单名称,用于标识消息的类型
  • operType:操作类型,用于标识消息的操作
  • s:消息内容,通常是一个JSON字符串
rabbitTemplate.convertAndSend,将消息发送到指定的队列
  • "":交换机名称,空字符串表示使用默认交换机
  • queues:队列名称
  • s:消息内容
  • new MessagePostProcessor():消息处理器,用于设置消息的属性

方法

logger.info:记录日志,表示开始发送同步制单数据
if(enabled):检查是否启用了消息发送功能
postProcessMessage :设置消息的相关属性
message.getMessageProperties().getHeaders().put:设置消息头属性
  • bizFile:业务文件,设置为空字符串。 bizType:业务类型,设置为 sync。 companyId:公司 ID,设置为 5003980029。 menu:菜单名称,使用传入的 menu 参数。 operType:操作类型,使用传入的 operType 参数。
message.getMessageProperties().setDeliveryMode:设置消息的持久化模式。
  • MessageDeliveryMode.PERSISTENT:消息持久化。
message.getMessageProperties().setContentEncoding:设置消息的编码格式。
SyDriver 转换的 JSON 对象 s 的使用
  • 在 driverData 方法中,SyDriver 对象被转换为 JSON 字符串 s,并作为参数传递给 getSendMq 方法。s 在 getSendMq 方法中被用作消息的内容,通过 rabbitTemplate.convertAndSend 方法发送到 RabbitMQ 队列。因此,s 被用作消息的实际内容,并在消息发送过程中使用。

总结

这段代码通过 RabbitTemplate 将消息发送到 RabbitMQ 队列,并使用 MessagePostProcessor 设置消息的属性。SyDriver 对象被转换为 JSON 字符串 s,并作为消息内容发送到队列。

接收(消费者)

上面这个例子(生产者)对应的消费者是其他系统,所以我们只需要设置对应的生产者的队列名和数据,然后对方的系统(消费者)监听同一个队列即可
步骤
  • 创建连接:与 RabbitMQ 服务器建立连接。
  • 声明队列:声明要从中接收消息的队列。
  • 创建消费者:定义一个消费者来处理接收到的消息。
  • 处理消息:在消费者中编写处理消息的逻辑。

PageHelper分页插件

基于Mybatis的分页插件,主要用于简化数据库分页查询的编写过程,支持多种数据库,并且可以与mybatis框架无缝集成。或直接使用MP的Mapper接口的selectPage方法实现分页

使用方法

  • 只需要在查询之前调用Pagehelper.startPage()方法即可分页
    • 例如:
    • PageHelper.startPage(pageNum, pageSize);List<User> userList = userService.getUserList();PageInfo<User> pageInfo = new PageInfo<>(userList);
    • 其中pageNum表示要查询的页码,pageSize表示每页的记录数,调用startPage方法后,PageHelper会自动将下一次查询作为分页查询,并返回一个PageInfo对象,包含分页相关信息

实现原理

  • 基于拦截器(Interceptor),在执行相关SQL之前会拦截并做分页处理。它通过ThreadLocal机制将分页参数保存在当前线程中,确保了分页参数的安全性和准确性。具体来说,PageHelper会将传入的页码赋值给一个Page对象,并保存到ThreadLocal中。接下来,PageHelper进入Mybatis的拦截器环节,获取并处理保存在ThreadLocal中的分页参数,与原本的SQL雨具进行拼接,从而完成带有分页处理的SQL语句构建

MinIO对象存储服务器

一个高性能、开源的对象存储服务器,兼容Amazon S3 API 特别适合存储大量非结构化数据如图片、视频、日志文件、备份数据等。MinIO由Golang开发,遵循 Apache License v2.0 协议,具有高可用性、强一致性、弹性扩展等特点,能够对应PB级别的数据存储需求

Hibernate对象关系映射(ORM)框架

主要用于管理数据库操作,通过将Java对象映射到数据库表,简化了数据库交互过程,使得开发者可以使用面向对象的方式操作数据库,而不是直接编写sql语句。

Hibernate-Validator数据校验框架

属于Hibernate项目的一部分,可把数据校验逻辑从业务代码中分离出来,增加代码的可读性,使数据校验变得更加方便和简单。

Fastjson处理Json数据

由阿里巴巴开发的开源java库,主要用于处理json数据,广发应用于web服务、API接口、数据交换等多个场景,具有高性能、功能完善、简单易用等特点。

序列化和反序列化示例

  • ‌将Java对象序列化为JSON字符串
    • ‌将JSON字符串反序列化为Java对象

      easyexcel Excel处理工具

      EasyExcel是一个专为Java设计的高效Excel处理工具,旨在帮助开发者轻松应对大文件操作时的内存溢出问题‌‌相较于Apache POIjxl等传统库,EasyExcel通过优化07版Excel的解析方式,显著降低了内存消耗,使得处理过程更为迅速且资源友好。即使面对数百万行数据的Excel文件,EasyExcel也能确保程序运行稳定,避免了以往因内存限制而引发的程序崩溃问题‌。

      EasyExcel的使用方法

      假设你需要创建一个简单的Excel文件,可以按照以下步骤操作:
      1. 定义数据模型‌:首先,创建一个Java类(如DemoData),并使用EasyExcel提供的注解(如@ExcelProperty)来映射Excel的列名与类的属性‌。
      1. 执行写操作‌:使用EasyExcel.write()方法指定输出文件路径、数据模型类,以及Sheet的名称,随后调用doWrite()方法并传入数据列表完成写入‌。

      LambdaQueryWrapper的使用及一个crud方法的写法

      可应对字段改变的问题,不需要想QueryWrapper一样写字段名

      通常一个crud方法的写法是:

      • 定义变量接收数据传输对象(可以logger.info打印查看)
      • 定义数据展示对象,存储从数据库取出来的数据,例:List<SyCrossBorder> syCrossBroderList = new ArrayList<>(); 一般泛型里面都是直接对应表名的实体类
      • 创建LambdaQueryWrapper对象,泛型例传要操作的表名,例LambdaQueryWarrper<SyCrossBorder> queryWrapper = new LambdaQueryWarrper<>();
      • 构建查询语句,这里就要去了解LambdaQW的相关方法了(in,eq...)
      • 调用MP serviceImpl的crud方法或自写的mapper方法
        • 可以直接通过自动装配配置需要调用的Service接口(一般业务接口会实现IService<实体类>,业务实现类会继承ServiceImpl<mapper,实体类>和实现业务接口),然后去使用接口的方法,或者是使用ServiceImpl里的baseMapper(区别?没有区别,如果是使用this(也就是当前类的实例)去掉方法走的还是baseMapper的方法。但通常使用this好一点)
        • 或者自动装配mapper,去使用mapper的方法,但一般使用mybatis的话,mapper也会继承BaseMapper<实体类>,也可以直接使用MP的crud方法

      LambdaQW相关方法

      ENUM与类静态变量的区别和使用场景

      区别

      定义方式:

      ENUM:使用enum关键字定义,是一种特殊的类,表示一组固定的常量。 类静态变量:在类中使用static关键字定义的变量,可以是任何类型。

      类型安全:

      ENUM:类型安全,枚举类型的变量只能取枚举类中定义的值。 类静态变量:不具备类型安全性,可能会被赋予任何值。

      可读性:

      ENUM:可读性强,表示一组相关的常量,代码更清晰。 类静态变量:可读性相对较差,常量之间的关系不明显。

      功能:

      ENUM:可以包含方法、构造函数和字段,功能更强大。 类静态变量:只能表示单一的常量值。

      ENUM

      常用于指定一种常见情况的多种属性
      ENUM可以直接设置成一张表对应的字段,例如一张表是
      1001 ORDER_BEFORE 未接单 1002 1002 已接单
      那么直接定义一个
      这样就可以直接替换掉一张可能并不需要存在的表

      使用场景

      当需要表示一组固定的常量时,例如季节、方向、状态等。 需要类型安全和更强的功能时,例如在枚举中定义方法来处理常量。

      类静态变量

      常用于指定一种情况的一种属性,例如状态

      使用场景

      当常量值不相关或不需要表示一组固定的常量时。 需要简单的常量定义时,例如数学常量(PI、E)等。

      查看Swagger地址的方法

      先在yml配置文件里查看端口,找到端口后第一步就完成了例如localhost:18810
      接着查看服务的地址,一般在yml里的:
      最后如果是swagger包就是swagger-ui.html,如果是knife4j包就是doc.html
      综上,knife包下这个接口文档的访问地址:http://localhost:18810/api/synergy/doc.html

      公司项目一个业务的常规写法(mp版)

      分层叙述,从上至下
      Result类:

      Controller控制器层

      一个业务,最基础的方法就是CRUD了,在这个公司,CRUD通常只写三个接口

      增改接口(/add)

      由上面的增改代码可以看出,关键点有①ParamWitheredPlatoonShipService 业务类、②@Resource注解、③方法上面的注释、注解、④返回值类型Result、⑤请求体方式+DTO数据传输对象、⑥直接返回调用方法的结果(可见业务类的返回值也是Result)
      • ①业务类暂时先不说,到业务层细说
      • ②为什么这个是关键点,因为你加了@Resource依赖注解,那么就相当于把这个业务类当做了一个bean,在对应类名上也就要把他注册进SpringIoC容器中,利用@Service将业务实现类注册成bean,否则无法运行
      • ③@ApiOperation是Swagger的注解,如果不加,在swagger里方法名就是英文的,加了就能改成中文,方便阅读;@PostMapping是RESTful API的一种接口请求方式,通常用于增加数据。
      • ④返回值类型统一使用的是封装好的一个Result实体类,通常他包含三个内容,分别是code(对应浏览器的响应码)、message(通常为提示信息)、data(泛型,具体数据)
      • ⑤请求方式有三种,分别是@RequestParam(适用于简单的查询参数,如搜索栏)、@RequestBody(适用于处理复杂的JSON数据结构)、@PathVariable(适用于RESTful风格的url格式传参),DTO数据传输对象通常用于接收前端传输的数据,VO展示对象常用于后端要返回给前端的数据,DTO与实体类不同,实体类是必须与表的所有字段一模一样,DTO属性可多也可少。

      删除接口(/remove)

      这个没啥好说的,关键点和上面增改接口重复了

      分页查询接口(/page)

      关键点①@ApiImplicitParams、②形参,直接简单说一下,这里就是用来作搜索查询用的,可以查包含某个字的参数名,也可以查从什么时候开始和结束的参数列表、③PageHelper.startPage(current,pageSize)、④queryPage(三个参数)
      • ①@ApiImplicitParams同样也是Swagger的翻译注解,可以给对应字段取中文名,对就是你在形参里设置的请求参数,用@ApiImplicitParam一个一个取
      • ②略,已经说了
      • ③PageHelper是一个好东西,mybatis-plus提供的分页插件,只需要你定义一个Page对象,给定current(当前页),pageSize(每页大小)就可以自动帮助你进行分页,实现原理:
        • 基于拦截器(Interceptor),在执行相关SQL之前会拦截并做分页处理。它通过ThreadLocal机制将分页参数保存在当前线程中,确保了分页参数的安全性和准确性。具体来说,PageHelper会将传入的页码赋值给一个Page对象,并保存到ThreadLocal中。接下来,PageHelper进入Mybatis的拦截器环节,获取并处理保存在ThreadLocal中的分页参数,与原本的SQL语句进行拼接,从而完成带有分页处理的SQL语句构建。
      • ④queryPage即是Controller中的方法名,也是Service中的方法名,它的那三个参数是来自前端请求过来的查询参数,后面可以用QueryWarrper(lambdaQW)来构建查询简单的查询条件,记住MP只能构建简单的查询语句,对于复杂的多表连表查询最好自写SQL

      Service业务层

      业务接口只定义方法,不实现。这里注意一个点,业务接口继承了IService<实体类>接口,这样子就可以直接使用MP的数据处理方法了,便捷的很,强推噢。还有addOrEidt的形参不是实体类,而是DTO数据传输对象,这也是一个注意点,最好不直接使用实体类来处理数据传输对象。

      ServiceImpl业务实现类

      @Service很关键啊,如果没有Spring容器就没有这个bean了,其他地方就无法注入/使用,注意@Service是把这个标记成bean,然后它的实例对象就可以进去到SpringIoC容器管理,给其他需要的地方使用,所以不能在接口上加@Service
      发现没,实现类和接口的继承不太一样,多了一个Mapper,不继承这个ServiceImpl的话,你就要多重写接口继承的IService方法。

      增改接口(addOrEdit)

      关键点①Result,和控制层的返回参数类型一样,所以就直接return了、②实现逻辑、③Beanutils.copyProperties()
      • ①略
      • ②增改写在一起的逻辑关键就是id,对于前端传过来的数据传输对象,如果它携带了id,那么就直接走更改方法,如果没有带id,就自动生成一个id,然后走保存方法。这里我用方法都是使用this(当前类的实例对象)调的,因为它其实和baseMapper(MP接口,里面全是数据处理方法)一样,this走的就是baseMapper的方法
        • 但是存在一个很严重的缺点,BeanUtils.copyProperties,这在我之前天讯瑞达实习的时候遇到过,Fortity扫描过不了,因为它会把所有传输过来的Json数据都拷贝给实体类,恶意攻击者可以通过伪造Json数据来达到恶意篡改的目的,而且如果定义的DTO里面也存在敏感字段,那么恶意攻击者就能够直接通过修改敏感字段的值来攻击(如DTO里存在isAdmin字段,那么就会传递一个isAdmin="true",然后全部被拷贝更新到数据库,这样攻击者就具有管理权限了)。
        • 解决方法
          • 逻辑分开,严格分离新增和修改 DTO(推荐),并且DTO里不包含敏感数据
            • 新增操作 禁止传递 id,所有元数据由服务端生成:
            • 修改操作 必须传递 id,但禁止修改元数据(如 createdBy):
          • 或者使用@JsonIgnoreProperties(ignoreUnknown = true)注解,在 DTO 类上添加该注解,强制忽略 JSON 中的未知字段(方便快捷),否则BeanUtils.copyProperties()无视 DTO 类的定义,直接将 JSON 中所有字段拷贝到 Entity。或者手动设值,不使用BeanUtils.copyProperties(绝对可控
            • 代码见升级版

      升级版

      这里多了①根据非主键groupId操作+关联表的操作、②LambdaQueryWrapper、③事务一致性、④日志记录
      • ①一般mybatis-plus是根据主键进行操作的,如果是非主键,那么就可以使用Wrapper构建查询条件去更新,例如这里的在update(实体,查询条件(相当于where group_id = '?')),并且优化了groupId赋值,使其被赋值一次
      • ②LambdaQuerryWrapper比QuerryWrapper要好一点,但用法都差不多,前者可以避免魔法值的出现
      • ③添加@Transactional 注解 保证原子性,通常只需明确指定回滚异常:建议总是明确指定 rollbackFor,其余参数使用默认值,如传播行为required 没有事务则新建事务,有事务就加入这个事务,隔离级别repeatable_read 可重复读
        • @Transactional 关键参数
          • 参数
            说明
            默认值
            示例
            propagation
            事务传播行为
            REQUIRED
            @Transactional(propagation = Propagation.REQUIRES_NEW)
            isolation
            事务隔离级别
            DEFAULT
            @Transactional(isolation = Isolation.READ_COMMITTED)
            timeout
            事务超时时间(秒)
            -1(无超时)
            @Transactional(timeout = 30)
            readOnly
            是否只读事务
            false
            @Transactional(readOnly = true)
            rollbackFor
            触发回滚的异常类型
            RuntimeExceptionError
            @Transactional(rollbackFor = Exception.class)
            noRollbackFor
            不触发回滚的异常类型
            @Transactional(noRollbackFor = NullPointerException.class)
        • propagation 传播行为:
          • 默认REQUIRED :如果当前没有事务,就新建一个事务,如果已经存在事务,就加入这个事务
          • REQUIRES_NEW:总是新建事务,如果当前有事务,则挂起当前事务
          • SUPPORTS:支持当前事务,如果没有事务,就以非事务方式执行
          • NOT_SUPPORTED:以非事务方式执行,如果当前有事务,则挂起当前事务
          • MANDATORY:必须在一个已有的事务中执行,否则抛出异常
          • NEVER:必须不在事务中执行,否则抛出异常
          • NESTED:如果当前存在事务,则在嵌套事务内执行
        • isolation 隔离级别
          • READ_UNCOMMITTED:读未提交(最低隔离级别),不解决任何问题
          • READ_COMMITTED:读已提交(Oracle默认),可解决脏读
          • REPEATABLE_READ:可重复读(MySQL默认),可解决脏读、不可重复读
          • SERIALIZABLE:串行化(最高隔离级别),可解决脏读、不可重复读、幻读
      • ④记录关键操作日志(如修改前后的数据差异),便于审计

      删除方法(removeBatch)

      关键点也大都重复了,仔细看上面吧

      升级版(也是关联了其他表+根据非主键操作)

      同样的,首先是根据groupId去构建查询条件,然后再添加进remove(查询条件)里,再加上一个连锁删除

      分页查询方法(queryPage)

      这里唯一一个关键点就是LambdaQueryWrapper<实体类>构建查询条件的。最后的paramWitheredPlatoonShipMapper也可以用baseMapper代替。

      Mapper数据交互层

      这个其实感觉能说的很少,因为CRUD都用到的是MP的数据处理方法,没有什么复杂查询,但还是介绍一点关键点吧

      Mapper接口

      这里我使用一个其他mapper接口的内容
      关键点①@Mapper,和@Service同理,必须先注册进SpringIoC容器才能被依赖/使用、②BaseMapper<实体>同样也是mybatis的接口,继承后可以使用它的所有方法(所以要使用mp的数据处理方法,mapper这里必须继承)、③queryAll
      • ①略
      • ②略
      • ③queryAll是一个自定义的复杂查询方法,这个sql的resultType返回集类型是DecResponse实体类的路径,parameterType构建查询条件的字段类型是DecQuery的路径(这样就可以在mapper.xml构建查询条件了),如下

      Mapper.xml

      公司各种类上的注解写法

      Controller控制器类

      @RestController
      • 作用:标记一个类为SpringMVC 控制器,并且每个方法的返回值都会直接写入HTTP响应体中,而不是视图名称
      • 使用原因:简化了控制器类的开发,适用于RESTful Web服务
      • 其他可能使用的注解
        • @Controller:标记一个类为SpringMVC控制器,但需要配合@ResponseBody使用
        • @ResponseBody:将方法的返回值直接写入HTTP响应体中。
      @RequestMapping("/booking")
      • 作用:为控制器类或方法指定请求的URL路径
      • 使用原因:定义请求的映射路径,支持类级别和方法级别的映射
      • 其他可能使用的注解
        • @GetMapping,查
        • @PostMapping,增
        • @PutMapping,改
        • @DeleteMapping:删
      @Api(tags = "预定管理")
      • 作用:用于Swagger文档的生成,描述一个API类
      • 使用原因:提供API类的基本信息,便于生成文档

      Controller控制器类的方法上

      ApiOperation(value = "获取打包列表(分页)", notes = "获取打包列表(分页)", response = SyPackingResponse.class),
      • 作用:描述一个API的操作
      • 参数:
        • value:简要描述API的功能(在swagger边栏显示)
        • notes:详细描述API的功能
        • response:指定返回对象的类型
      @ApiImplicitParams({ @ApiImplicitParam(name = "type", value = "orderId-订单ID、bizId-系统内部ID"), @ApiImplicitParam(name = "key", value = "关键词组") }),
      • 作用:描述多个隐式参数(在swgger请求示例中的提示词)
      • 参数:
        • name:参数名称
        • value:参数描述
        • dataType:参数的数据类型
        • defaultValue:参数的默认值
        • required:是否必填

      Controller控制器类里的参数注解

      @RequestBody
      • 作用:将HTTP请求体中的JSON数据绑定到方法参数上
      • 使用原因:适用于接收复杂的JSON数据结构
      @RequestParam(value = "name", required = false, defaultValue = "World"
      • 作用:用于将请求参数绑定到方法的参数上,通常用于处理简单的查询参数
      • 使用场景:当需要从URL中获取查询参数时,可以使用@RequestParam注解
      • 参数
        • value:请求参数的名称
        • required:是否为必须参数,默认true
        • defaultValue:参数默认值,如果请求中没有这个参数,则使用默认值
      @PathVariable
      • 作用:用于将URL路径中的变量绑定到方法的参数上,通常用于处理RESTful风格的URL
      • 使用场景:需要从URL路径中获取变量时,可以用@PathVariable
      • 参数
        • value:路径变量的名称
        • required:是否为必须变量
      • 例子
      • @GetMapping("/users/{id}")public String getUserById(@PathVariable("id") String userId) { return "User ID: " + userId;}
      • 在这个示例中,id 是路径变量,URL 中的 {id} 部分会被绑定到 userId 参数上。

      Spring配置类

      @Configuration
      • 作用:标记一个类为配置类,可以包含一个或多个@Bean方法,这些方法会被Spring容器处理已生成bean定义和服务请求
      • 使用原因:用于定义spring应用上下文中的bean和依赖注入配置

      Service业务类

      @Service
      • 作用:用于标记一个类为Spring服务器组件
      • 使用原因:定义业务逻辑层的组件,便于Spring管理和注入
      @Component
      • 作用:一个通用的Spring组件注解,可以用于标记任何Spring管理的组件
      • 使用原因:也是符合IoC(控制反转)原理,方便spring管理和注入
      @Transactional
      • 作用:声明事务管理,确保业务方法在事务上下文中的执行
      • 使用原因:相当于加锁,管理数据库事务,确保数据一致性和完整性

      Mapper数据库访问层相关

      @Mapper
      • 作用:用于标记一个接口为Mybatis映射器,定义数据库操作方法
      • 使用原因:用于数据访问层,便于mybatis管理和注入
      @Repository
      • 作用:用于标记一个为数据访问层组件
      • 使用场景:通常与Spring Data JPA一起使用

      Maven公司仓库无法拉取以及无法读取

      Could not find artifact net.superlucy:superlucy-parent:pom:1.0-SNAPSHoT in amimaven-public(http://xxxxx),无法下载组件
      • 解决方法:网络问题,在电脑的host文件加上或者直接把同事的maven仓库下载到本地
      • 192.168.xxgit.dev.superlucy.net192.168.xxnacos.dev.superlucy.net192.168.xx mysql.dev.superlucy.net
      报错:java: 读取D:\XX\XXX.jar时出错; error in opening zip file,具体原因不明,可能是版本不一致的原因。
      • 解决方法:让能够正常运行项目的同事把报错内容的包发给你或者自己从官网下载对应包并改成报错的名字,如下载dameng的jdbc包DmJdbcDriver8.jar,但是idea报错找不到DmJdbcDriver18-8.1.2.79.jar,就把DmJdbcDriver8.jar放到本地仓库替换掉DmJdbcDriver18-8.1.2.79.jar(包括名字)
        • 或者切换maven版本,如3.8.8->3.6.3,尤其是对 error in opening zip file
      对于pom的<parent>标签报错,要看公司是否是有基于某些核心基础框架上搭建项目,所以你需要做的是先去拉取公司的核心pom包(单点登录,权限控制等框架),以供新项目的包依赖引入。或者是报的无法从私有仓库找到某个包,最直接的解决方法就是把这个包的上一级(一个一个找前辈要太麻烦了)向有的前辈要一下。解决过程(cursor):
      • 命令执行还在进行中,但从输出看来,Maven已经成功开始下载依赖了。这表明我们添加的依赖管理配置是有效的,解决了之前的问题。
        • 让我总结一下您项目之前遇到的问题和解决方案:
          1. 问题描述:您的项目依赖了父项目 superlucy-parent,但是这个父项目在您的本地Maven仓库和远程仓库中都不存在,导致Maven无法解析依赖关系,特别是各种Spring Boot和其他依赖项的版本号。
          1. 解决方法:我们移除了对父项目的依赖,并在您的主pom.xml中添加了以下内容:
        • 添加了properties部分定义所有需要的依赖版本
        • 添加了dependencyManagement部分引入Spring Boot的依赖管理
        • 为所有在子项目中使用的库添加了版本管理
          1. 结果:从命令执行的输出来看,Maven现在能够成功解析和下载依赖,项目应该可以正常构建了。
          推荐后续操作:
          1. 如果您的团队中有其他开发人员使用相同的项目,建议将修改后的pom.xml提交到版本控制系统,让其他人也能正常构建项目。
          1. 如果您需要经常创建类似的项目,可以考虑创建一个自己的父项目(例如superlucy-parent),并将其发布到您的Nexus仓库,这样今后创建新项目时可以直接继承这个父项目。

      把项目的MySQL数据迁移至PGSQL

      步骤

      首先使用navicat的工具 -> 数据传输
      • 要先选中需要迁移的mysql库,然后再选对应pgsql的库和模式(对应的就是mysql库了)
      第一遍迁移大部分数据就迁移过来了,后面有部分数据需要通过和mysql库对比没有数据的表
      • 这一步可以采用qq截图的钉图先把mysql的表和行截取一列然后放到pgsql的模式里对比
        • 要注意pgsql会多出很多张表来(可能是navicat迁移的影响),所以钉图要把表名带上
      • 对比完后去修改表,因为部分事务不成功会导致所有事务回滚(ACID四原则)(一般数据无法迁移过来都是因为表的一些限制或者varchar字段长度不一致的和字符串中的单引号和反斜杠没有正确转义原因(如 Hawke\'s → 实际想表示 Hawke's通用方案:替换 \''')等等,我知道的暂时是这些)
      • 修改完字段长度和外键限制后,去mysql把pgsql里没有数据的表导出数据(右键表名->导出向导,在里面可以选择要导出的表数据和导出位置)建议新建一个目录存放
      • 最后使用sqline工具去把mysql导出的数据目录选中,然后转成pgsql在一个新建的目录
      notion image

      把项目的MySQL语句改造成对应的PGSQL语句

      步骤

      法一(一遍一遍去试sql语句在mysql和pgsql执行的情况)

      • 首先把项目拉下来,注意分支,develop分支代码(原始mysql数据库)拉一份,xinchuang1.0分支代码(要改造成pgSQL)拉一份
      • 然后根据账号密码连接mysql和pgsql数据库,pgsql多一个模式,不同数据库下模式不同,不同模式的表不同。
      • 最后在对应表上创建查询,输入相应的语句使其返回相同结果。

      法二(推荐,跑起项目,在swagger里看功能能否正常使用,不能再去对应的SQL修改)

      • 在法一的基础上,先复制请求路径,查看对应代码的方法是不是都是mybatisplus的方法,如果是就不需要修改,否则在代码里找对应的表字段,了解过后填入swagger里面去调试对应的功能,如果出现Error querying database就去修改SQL语句
      • 后面根据不同点熟练度直接看SQL语句哪里要改哪里不改,就不用一一在数据库里执行

      MySQL和PGSQL的不同点

      注意!!!
      • Mysql里的group by 在pgsql里有大说法
        • 首先 如果只是简单的group by且select列里没有聚合函数
          • 就可以直接在id那去重(id没有设置成主键的情况),no,好像不行,有的语句可以,有的语句不行,原因不详
        • 如果select列里有聚合函数
          • 就直接在group by 后面加上所有的非聚合列
        • 如果group by后面还跟有order by
          • 那么久不能用group by了,就必须使用子查询,这里可以借助AI:
          • 把这个mysql改造成pgsql,要求查询结果一致,不能只在group by 后面加非聚合列,我试过了,数据不一致,应该使用子查询,中文回答。 改造前 SELECT stt.id id, stt.car_plate carPlate, stt.kogye kogye, stt.location location, stt.remark remark, stt.tray_id trayId, stt.node_status nodeStatus, stt.created_time createdTime, stt.modify_time modifyTime, count(sogc.container_no) containerNum, spt.tray_no trayNo, spt.tray_type trayType, sogc.order_id orderId FROM sy_transport_tracking stt LEFT JOIN sy_packing_tray spt ON stt.tray_id = spt.id LEFT JOIN sy_order_goods_container sogc ON sogc.id = stt.container_id <where> 1=1 <if> and spt.tray_no = #{trayNo} </if> <if> and stt.created_by = #{createdBy} </if> </where> GROUP BY stt.id ORDER BY stt.created_time DESC 改造后 SELECT aa.ID, aa.carPlate, aa.kogye, aa.LOCATION, aa.remark, aa.trayId, aa.nodeStatus, aa.createdTime, aa.modifyTime, aa.trayNo, aa.trayType, bb.orderId, bb.containerNum FROM ( SELECT DISTINCT ON ( stt.ID ) stt.ID, stt.car_plate carPlate, stt.kogye kogye, stt.LOCATION "location", stt.remark remark, stt.tray_id trayId, stt.node_status nodeStatus, stt.created_time createdTime, stt.modify_time modifyTime, spt.tray_no trayNo, spt.tray_type trayType FROM sy_transport_tracking stt LEFT JOIN sy_packing_tray spt ON stt.tray_id = spt.ID <where> <if> and spt.tray_no = #{trayNo} </if> <if> and stt.created_by = #{createdBy} </if> </where> ) aa LEFT JOIN ( SELECT stt.ID, sogc.order_id orderId, COUNT ( sogc.container_no ) containerNum FROM sy_transport_tracking stt LEFT JOIN sy_order_goods_container sogc ON sogc.ID = stt.container_id <where> <if> and stt.created_by = #{createdBy} </if> </where> GROUP BY stt.ID, sogc.order_id ) bb ON bb.ID = aa.ID
      • SQL里的>,<,=,&等等特殊字符需要使用 <![CDATA [ ]]>转义
      • pgsql和mysql在java的mybatis版本里都尽量不要带分号
      • MySQL可以不区分大小写,PGSQL区分大小写(最主要的不同点)(mybatis自动优化)
      • PGSQL多了一个概念:模式,不同数据库下模式不同,因此在SELECT FROM 时 需要在表名前加上模式名(yml配置文件里有加对应的模式)
      • PGSQL一般用对字符串用单引号,双引号可以区别关键字,mysql对字符串单双都可用
      • 日期和时间函数
        • Mysql使用 DATE_ADD函数来增加日期
        • PostgreSQL使用 + INTERVAL语法
      • PGSQL中size是一个保留关键字,在SQL查询中要加""
      • mysql AS后面的名字可以加单引号,但是PGSQL不行,PGSQL加双引号或不加
        • 快捷替换方法,勾选正则表达式,查'(\w+)',换$1
      • mysql能用value或values(多值时),而pgsql只能用values
      • mysql有UUID()方法创建随机值,pgsql?
      • 日期函数不同。MySQL 中的 TO_DAYS 函数在 PostgreSQL 中没有直接对应的函数,可以使用 DATE_TRUNC 和 CURRENT_DATE 来实现相同的功能。
        • where TO_DAYS(created_time) = TO_DAYS(NOW())
        • where DATE_TRUNC('day', created_time) = CURRENT_DATE或where DATE_TRUNC('day', created_time) = DATE_TRUNC('day', NOW())
      • 日期函数: MySQL 使用 TO_DAYS 函数来获取日期的天数。PostgreSQL 使用 DATE_TRUNC 函数来截断日期,并使用 CURRENT_DATE 获取当前日期。
      • 字符串聚合: MySQL 使用 GROUP_CONCAT 函数。PostgreSQL 使用 STRING_AGG 函数。
      • 自增主键: MySQL 使用 AUTO_INCREMENT。 PostgreSQL 使用 SERIAL 或 BIGSERIAL。
      • 分页查询: MySQL 使用 LIMIT 和 OFFSET。 PostgreSQL 也使用 LIMIT 和 OFFSET,但支持更多高级功能。
      • JSON 支持:MySQL 提供基本的 JSON 支持。 PostgreSQL 提供更强大的 JSON 和 JSONB 支持,允许更复杂的查询和索引。
      • pgSQL里没有separator,可以直接使用string_agg,在分割元素后面直接跟上分隔符即可,例如STRING_AGG(sogc.container_no, ',') AS containerNo,这样可以实现将多行数据聚合成一个字符串,并用逗号分隔。
      • 将 LIMIT 0,1 改为 LIMIT 1,因为 PostgreSQL 不支持 LIMIT 0,1 这种语法。
      • 对于group by,如果SELECT出现了聚合函数,那么就要使用它,并把有非聚合列放在group by后面
      • so.${sort}可能会出现的严重问题:
        • 因为$是直接替换执行,假如传过来的数据是type;delete from xxdatabase;trop table xxx等等的语句,那么会连环执行,导致数据库数据被删除,sql注入问题。

      将公司模块合并,减少微服务资源使用

      第一步:复制所有内容到另一个模块
      第二步:使用全局替换(ctrl + shift + r)+正则表达式替换掉相关名字
      第三步:替换掉xml里的resultType中对于的实体类路径(如 booking. --> common.)
      第四步:将controller的RequstMapper加上初始url(如booking模块的controller,都加上/booking,因为common模块的url起始地址是/api/synergy,而booking模块的功能url普遍是/api/synergy/booking)
      第五步:最终步,在所有模块合并完成后,去运行,根据报错导包失败的位置去删除重新导包和减少Mapper bean重名问题(用@Component("Bean名称"))以及mapper里的resultType的被删除模块替换成本模块(因合并后版本没有对应模块所以会找不到,因此需要替换成被合并后的模块,如:booking.、customs.、packing.替换成common.)并且要去maven重新destroy和install(需要成功不报错),因为部署到线上需要提前将需要的包上传

      接口测试方法

      可以直接把项目调试启动后注释掉不需要的代码然后使用 apipost 等应用去请求接口地址。

      注意事项

      java.lang.RuntimeException: java.io.lOException: Error reading file c:\Users\Fanljdks\corretto-1.8.0 362\jre\lib\management-agent,jar: error in open in zip file
      • 读取jar包失败,最主要的原因就是缺少jar包或是这个包有问题(具体原因不清楚)
        notion image
        • 解决方法
          • 首先尝试叫能运行这个项目的同事把出了问题的jar包发给你(或是你自己去官网下,不清楚是不是网络问题无法从公司仓库拉下来),然后替换
          • 如果上面的方法不行,就直接关掉当前的项目,重新打开项目(注意不是导入模块),从仓库拉代码,配maven,jdk等等,再去尝试第一步。再加上直接导入,就是对爆红的jar包alt+enter,然后点击添加依赖项,去找,主动添加。
        The bean '${feign-client.system}.FeignClientSpecification' could not be registered. A bean with that name has already been defined and overriding is disabled.
        • Bean '${feign-client.system}.FeignClientSpecification“ 无法注册。已定义具有该名称的 Bean,并且已禁用覆盖。(多个feign接口调用同一个微服务名称,启动时会引发此异常。)
          • 解决方法:把x3platform什么什么的pom依赖删去并把项目里使用到这个工具包的方法替换,如可以使用apche的StringUtils.isBlank代替x3的StringUtil.isNullXXX
        org.apache.ibatis.builder.BuilderException: Error parsing Mapper XML. The XML location is 'com/amiintellect/synergy/common/app/mapper/SyCustomsMapper.xml'. Cause: org.apache.ibatis.builder.BuilderException: Error resolving class. Cause: org.apache.ibatis.type.TypeException: Could not resolve type alias 'com.amiintellect.synergy.common.app.excel.SyCustomsExcel'. Cause: java.lang.ClassNotFoundException: Cannot find class: com.amiintellect.synergy.common.app.excel.SyCustomsExcel
        • 无法找到对应类,去查看这个com.amiintellect.synergy.common.app.excel下有没有SyCustomsExcel
          • 解决方法:把resultType切换到当前模块有这个类的目录,或者导入其他模块的包去引这个类
        Annotation-specified bean name 'syOrderLogisticMapper' for bean class [com.amiintellect.synergy.common.app.mapper.SyOrderLogisticMapper] conflicts with existing, non-compatible bean definition of same name and class [com.amiintellect.synergy.transport.app.mapper.SyOrderLogisticMapper]
        • Bean 类 [com.amiintellect.synergy.common.app.mapper.SyOrderLogisticMapper] 的注释指定的 Bean 名称“syOrderLogisticMapper”与同名和同类的现有不兼容 Bean 定义冲突[com.amiintellect.synergy.transport.app.mapper.SyOrderLogisticMapper],简单来讲就是Bean名字冲突(存在同名)
          • 解决方法:利用@Component("取名")组件为Bean取名,如
          • package com.amiintellect.synergy.transport.app.mapper;import com.amiintellect.synergy.transport.dto.model.SyCrossBorder;import com.baomidou.mybatisplus.core.mapper.BaseMapper;import org.apache.ibatis.annotations.Mapper;import org.springframework.stereotype.Component;@Mapper@Component("transportSyCrossBorderMapper")public interface SyCrossBorderMapper extends BaseMapper<SyCrossBorder> {}​package com.amiintellect.synergy.common.app.mapper;import com.amiintellect.synergy.common.dto.response.SyCrossBorderRes;import com.baomidou.mybatisplus.core.mapper.BaseMapper;import org.apache.ibatis.annotations.Mapper;import org.springframework.stereotype.Component;import java.util.List;import java.util.Map;@Mapper@Component("commonSyCrossBorderMapper")public interface SyCrossBorderMapper extends BaseMapper<SyCrossBorder> {}
        如果出现提供的参数类型与需求的参数类型不同
        • 很有可能是导错了包(详情看下面)
          • 解决方法:查看一下对应的包是导的哪个或者有没有多导/少导
        'com.baomidou.mybatisplus.extension.service.impl.ServiceImpl' 中的 'getBaseMapper()' 与 'com.baomidou.mybatisplus.extension.service.IService' 中的 'getBaseMapper()' 冲突; 尝试使用不兼容的返回值类型类型形参 'com.amiintellect.synergy.transport.app.mapper.FlowableFormMapper,
        notion image
        • 遇到这种情况一般就是说目标需要的类型与你提供的类型不一致
          • 解决方法:这是在模块合并到另一个模块的复制过程遇到的错误,因此很有可能是与这个代码相关联的代码出现问题(如迁移后找不到对应的包,这个只需要你把对应的包复制过来了然后重新导入一遍就可以,注意是所有复制过来的包都要重新导入,这样子上面这个错误就会自动消失,不要一直尝试解决,解决不了先跳过)
          • notion image
          • 原因就是出在上图,这里刚复制过来你不点开这个mapper他是查找不到错误的,只有点开后才发现少了包,这时候再点一下这个类里面就可以自动导入了(前提你这个模块下没有同名的类)

        修改合并后的三个模块(原本六个)的版本号重新部署

        例如
        注意更改版本号后,pom依赖里引了1.0-SHAPSHOT的对应包也要改成1.0-xc,并把已经被合并但仍存在引入的包删除(如packing包的依赖,不需要改版本号,直接删除,因为这个模块已经被合并到transport了)

        各模块下的三层模块与openFeign调用模式

        notion image

        api模块是向外暴露的接口,用于openFeign调用

        notion image

        app模块是业务相关的代码层,里面有MVC架构

        notion image
        config,放配置类,例如WebMvcConfig,RabbitConfig,RedisConfig
        controller,控制层,主要用于处理前端发送过来的请求,并转给service层
        service,业务层,内有接口和其实现类,先定义业务的接口,再去实现,处理controller调用的方法,并调用mapper层
        mapper,数据交互层,内有声明操作数据库方法的接口,一般其操作数据库的sql语句放在resources.mapper的xml里
        util,常用工具类,如阿里云的文件上传服务,JsonUtils,ExcelUtil等等
        excel,扩展功能excel放的相关类
        schedule,扩展功能定时器放的相关类

        dto模块就是放置的各种实体类

        notion image
        entity,实体类,数据库映射成Java对象时resultType要用到的类,里面通常包含数据库表对应的字段,并且通常使用ORM(对象关系映射)框架(如Hibernate或MyBatis)进行持久化操作。实体类包含数据库表的字段以及相应的getter和setter方法。
        enums,枚举类,用于定义常见情况的对应固定值,可以当做一张表,减少数据库表创建,同时可提高代码的可读性和可维护性,避免使用魔法值(magic numbers)
        model,业务模型类,这些类用于封装业务逻辑和数据结构,与entity不同,model类不一定直接映射到数据库表,而是用于应用程序的不同层之间传递数据。这些类还可以包含额外的业务逻辑和方法,用于处理和转换数据。
        此外还可能有constant,常量类,里面通常是一些宇宙普遍常见或企业特定的数值,如PI

        熟悉重钢-长江航运智慧物流服务平台

        RabbitMQ消息发送与接收流程详解

        notion image

        1. RabbitMQ架构概述

        本项目中的RabbitMQ实现采用了分层设计架构,主要包含以下几个关键组件:

        1.1 核心组件

        1. 数据构建层data
            • DataBuildService接口:定义了消息内容构建的规范
            • DataBuildFactory工厂类:根据消息类型选择合适的构建服务
            • 各种具体实现类:如DataRepoServiceDataAssemblyService
        1. 消息发送层mq
            • MqSendService:负责将构建好的消息发送到RabbitMQ
            • RabbitConfig:RabbitMQ相关配置
        1. 常量定义WmsMqTypeConstants
            • 定义了系统中所有的消息类型常量

        2. 消息发送流程详解

        2.1 消息发送流程

        1. 业务触发:在业务层(如WmsOrderRepoServiceImpl)中的特定操作触发消息发送
          1. 消息发送服务MqSendService接收业务ID和消息类型
            1. 消息工厂:通过DataBuildFactory获取对应的数据构建服务
              1. 数据构建:由具体的DataBuildService实现类构建消息内容
                1. 消息组装MqSendService将业务数据封装成统一格式
                  1. 消息发送:最终通过RabbitTemplate发送到MQ服务器

                    2.2 异步处理机制

                    系统采用了异步处理机制发送消息,避免MQ操作阻塞主业务流程:

                    2.3 错误处理机制

                    1. 业务层捕获异常:不影响主流程
                      1. 消息发送前检查:确保队列存在

                        3. 消息接收流程

                        项目中的消息接收主要通过@RabbitListener注解实现:

                        4. RabbitMQ配置详解

                        4.1 RabbitMQ配置类

                        4.2 消息类型常量类

                        5. 消息数据结构示例

                        项目中所有发送的消息都遵循统一的JSON结构:

                        5.1 仓储动态消息

                        5.2 派车信息消息

                        6. 最佳实践总结

                        1. 关注点分离:明确划分数据构建层和消息发送层
                        1. 异步处理:使用线程池异步发送消息,避免阻塞业务流程
                        1. 错误隔离:消息发送失败不影响主业务流程
                        1. 统一格式:所有消息遵循统一的JSON格式,便于接收方处理
                        1. 持久化:消息设置为持久化模式,防止丢失
                        1. 异常处理:完善的日志记录和异常捕获机制

                        7. 集成指南

                        如需开发一个新的消息类型,需要以下步骤:
                        1. WmsMqTypeConstants中定义新的消息类型常量
                        1. 实现对应的DataBuildService接口实现类
                        1. DataBuildFactory中注册新的消息类型与实现类的映射
                        1. 在业务代码中调用mqSendService.sendMqData()发送消息
                        通过以上架构设计,系统实现了消息发送的解耦和灵活扩展,保证了业务系统与消息中间件之间的良好集成。

                        遇到的问题和知识点

                        为什么分页方法在接收的形参里不使用DTO来进行数据传输?而是直接使用@RequestParam注解?
                        • 因为分页只有一个get请求,而且字段不多,减少类的建造,一般post、put、字段参数多的才会用DTO来进行数据传输。
                        为什么要使用SyOrderTrackingAdd继承SyOrderTracking(MP对应sy_order_tracking表)而不直接使用SyOrderTracking
                        • 因为SyOrderTrackingAdd可能有自己的字段要使用,继承SyOrderTracking就可以使用它的字段了。
                        工具类推荐使用lombok或谷歌的guava
                        • lombok
                          • @Data生成所有常见方法
                          • @ToString注解用于生成toString方法;@EqualsAndHashCode注解用于生成equals和hashCode方法;@NoArgsConstructor、@RequiredArgsConstructor和@AllArgsConstructor注解分别用于生成无参构造方法、包含所有字段的构造方法和包含特殊处理字段的构造方法
                        mybatis-plus功能:业务实现类继承ServiceImpl<mapper,entity>,业务接口实现IService<entity>可以直接使用crud方法
                        • mapper里也有自己写的操作数据库方法,entity一般采用驼峰对应表名,属性就是对应表字段,这样mybatis就可以直接根据类名和属性操作表和字段
                        • 注意是两个要求,一个接口要实现IService,另一个业务实现类要实现ServiceImpl<mapper,entity>
                        对于public class SyCrossBorderServiceImpl extends ServiceImpl<SyCrossBorderMapper, SyCrossBorder> implements SyCrossBorderService {}这个类中的this.getOne,this是指代什么?
                        • this指代当前类的实例,也就是等价于SyCrossBorderServicelImpl scbs = new SyCrossBorderServicelImpl (); scbs.getOne,而scbs继承了ServiceImpl,可以直接使用他的方法,getOne就是从数据库中取一条数据。
                        这个data里面的page是怎么来的?看下面代码,明明只有list<实体>
                        • Result通用返回函数中的successList里有分页方法,List接收要展示的数据并通过PageData分页,message提示信息,ResultCode.SUCCESS通过构造方法+ResultCode枚举值(避免使用纯数字魔法值)
                        • public static <T> Result<PageData<T>> successList(List<T> list, String message) { Result result = new Result(ResultCode.SUCCESS); PageData pageData = new PageData(list); result.setData(pageData); result.setMessage(message); return result; }
                        新增需求,需要增加3个字段
                        • 根据API请求查看后端使用了哪些表,然后在对应表加上对应字段(不知道加在哪张表可以问一下或者自己摸索)
                        • 注意!所有对应实体类的变量和数据库表的字段要对应
                        相关文章
                        Java专项----面试题篇
                        Lazy loaded image
                        软考专项:下午题 #1 试题一 数据流图
                        Lazy loaded image
                        软考专项:上午题 #8 设计模式
                        Lazy loaded image
                        软考专项:上午题 #7 面向对象
                        Lazy loaded image
                        软考专项:上午题 #6 数据库
                        Lazy loaded image
                        软考专项:上午题 #5 知识产权
                        Lazy loaded image
                        一些AI专业名词的解释Java专项----面试题篇
                        Loading...
                        目录
                        0%
                        poze624
                        poze624
                        天行健,君子以自强不息
                        公告
                        🎉Poze小站已经上线🎉
                        --- 哈喽 ---
                        👏欢迎浏览阅读👏
                        待补充(欢迎关注我的公众号)
                        notion image
                         
                        目录
                        0%