清理与ztcloud中重复的代码,改为 jar 包方式引用 ztcloud

This commit is contained in:
ranke
2026-02-03 15:23:43 +08:00
parent 47e2529b2b
commit 4d997d9b86
3397 changed files with 158 additions and 260291 deletions

29
zt-framework-dsc/pom.xml Normal file
View File

@@ -0,0 +1,29 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<artifactId>zt-dsc</artifactId>
<groupId>com.zt.plat</groupId>
<version>${revision}</version>
</parent>
<packaging>pom</packaging>
<modules>
<module>zt-common-dsc</module>
</modules>
<artifactId>zt-framework-dsc</artifactId>
<description>
该包是技术组件,每个子包,代表一个组件。每个组件包括两部分:
1. core 包:是该组件的核心封装
2. config 包:是该组件基于 Spring 的配置
技术组件,也分成两类:
1. 框架组件:和我们熟悉的 MyBatis、Redis 等等的拓展
2. 业务组件:和业务相关的组件的封装,例如说数据字典、操作日志等等。
如果是业务组件Maven 名字会包含 biz
</description>
<url>https://github.com/YunaiV/ruoyi-vue-pro</url>
</project>

View File

@@ -0,0 +1,199 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<groupId>com.zt.plat</groupId>
<artifactId>zt-framework-dsc</artifactId>
<version>${revision}</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>zt-common-dsc</artifactId>
<packaging>jar</packaging>
<name>${project.artifactId}</name>
<description>定义基础 pojo 类、枚举、工具类等等</description>
<url>https://github.com/YunaiV/ruoyi-vue-pro</url>
<dependencies>
<dependency>
<groupId>com.zt.plat</groupId>
<artifactId>zt-common</artifactId>
</dependency>
<!-- Spring 核心 -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<scope>provided</scope> <!-- 设置为 provided只有工具类需要使用到 -->
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-expression</artifactId>
<scope>provided</scope> <!-- 设置为 provided只有工具类需要使用到 -->
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aop</artifactId>
<scope>provided</scope> <!-- 设置为 provided只有工具类需要使用到 -->
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<scope>provided</scope> <!-- 设置为 provided只有工具类需要使用到 -->
</dependency>
<dependency>
<!-- 用于生成自定义的 Spring @ConfigurationProperties 配置类的说明文件 -->
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<optional>true</optional>
</dependency>
<!-- Web 相关 -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
<scope>provided</scope> <!-- 设置为 provided只有工具类需要使用到 -->
</dependency>
<dependency>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-redis</artifactId>
<scope>provided</scope> <!-- 设置为 provided只有工具类需要使用到 -->
</dependency>
<dependency>
<groupId>jakarta.servlet</groupId>
<artifactId>jakarta.servlet-api</artifactId>
<scope>provided</scope> <!-- 设置为 provided只有工具类需要使用到 -->
</dependency>
<dependency>
<groupId>org.springdoc</groupId>
<artifactId>springdoc-openapi-starter-webmvc-api</artifactId>
<scope>provided</scope> <!-- 设置为 provided主要是 PageParam 使用到 -->
</dependency>
<!-- RPC 远程调用相关 -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-openfeign-core</artifactId>
<scope>provided</scope> <!-- 设置为 provided主要是 api 包使用到 -->
</dependency>
<!-- 监控相关 -->
<dependency>
<groupId>org.apache.skywalking</groupId>
<artifactId>apm-toolkit-trace</artifactId>
</dependency>
<!-- 工具类相关 -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
<dependency>
<groupId>org.mapstruct</groupId>
<artifactId>mapstruct</artifactId>
</dependency>
<dependency>
<groupId>org.mapstruct</groupId>
<artifactId>mapstruct-jdk8</artifactId> <!-- use mapstruct-jdk8 for Java 8 or higher -->
</dependency>
<dependency>
<groupId>org.mapstruct</groupId>
<artifactId>mapstruct-processor</artifactId>
</dependency>
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<scope>provided</scope> <!-- 设置为 provided只有工具类需要使用到 -->
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<scope>provided</scope> <!-- 设置为 provided只有工具类需要使用到 -->
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-core</artifactId>
<scope>provided</scope> <!-- 设置为 provided只有工具类需要使用到 -->
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.datatype</groupId>
<artifactId>jackson-datatype-jsr310</artifactId>
<scope>provided</scope> <!-- 设置为 provided只有工具类需要使用到 -->
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<scope>provided</scope> <!-- 设置为 provided只有工具类需要使用到 -->
</dependency>
<dependency>
<groupId>jakarta.validation</groupId>
<artifactId>jakarta.validation-api</artifactId>
<scope>provided</scope> <!-- 设置为 provided主要是 PageParam 使用到 -->
</dependency>
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-all</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>transmittable-thread-local</artifactId>
</dependency>
<dependency>
<groupId>com.fhs-opensource</groupId> <!-- VO 数据翻译 -->
<artifactId>easy-trans-anno</artifactId> <!-- 默认引入的原因,方便 xxx-module-api 包使用 -->
</dependency>
<!-- Test 测试相关 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-core</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.bouncycastle</groupId>
<artifactId>bcprov-jdk18on</artifactId>
<version>1.78.1</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.apache.httpcomponents.client5</groupId>
<artifactId>httpclient5</artifactId>
</dependency>
<dependency>
<groupId>org.apache.httpcomponents.client5</groupId>
<artifactId>httpclient5</artifactId>
</dependency>
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpclient</artifactId>
<version>4.5.14</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpmime</artifactId>
<version>4.5.14</version>
<scope>compile</scope>
</dependency>
</dependencies>
</project>

View File

@@ -0,0 +1,6 @@
/**
* 基础的通用类,和框架无关
*
* 例如说CommonResult 为通用返回
*/
package com.zt.plat.framework.common;

View File

@@ -0,0 +1,137 @@
package com.zt.plat.framework.common.util.asyncTask;
import java.util.LinkedList;
import java.util.List;
import java.util.concurrent.*;
/**
* 异步任务同步处理工具类
* 多次提交,一次等待
*/
public class AsyncLatchUtils {
private static final ThreadLocal<List<TaskInfo>> THREADLOCAL = ThreadLocal.withInitial(LinkedList::new);
/**
* 提交一个异步任务
* @param executor 指定执行此任务的线程池
* @param runnable 需要异步执行的具体业务逻辑
*/
public static void submitTask(Executor executor, Runnable runnable) {
THREADLOCAL.get().add(new TaskInfo(executor, runnable));
}
/**
* 获取当前线程已提交的任务列表,并自动清理当前线程的已提交任务列表。
* @return
*/
private static List<TaskInfo> popTask() {
List<TaskInfo> taskInfos = THREADLOCAL.get();
THREADLOCAL.remove();
return taskInfos;
}
/**
* 触发所有已提交任务的执行,并同步等待它们全部完成。
* @param timeout 最长等待时间
* @param timeUnit 等待时间单位
* @return true: 如果所有任务在指定时间内成功完成。false: 如果等待超时。
* 该方法在执行后会自动清理当前线程提交的任务列表,因此可以重复使用。
*/
public static boolean waitFor(long timeout, TimeUnit timeUnit) {
List<TaskInfo> taskInfos = popTask();
if (taskInfos.isEmpty()) {
return true;
}
CountDownLatch latch = new CountDownLatch(taskInfos.size());
for (TaskInfo taskInfo : taskInfos) {
Executor executor = taskInfo.executor;
Runnable runnable = taskInfo.runnable;
executor.execute(() -> {
try {
runnable.run();
} finally {
latch.countDown();
}
});
}
boolean await = false;
try {
await = latch.await(timeout, timeUnit);
} catch (Exception ignored) {}
return await;
}
private static final class TaskInfo {
private final Executor executor;
private final Runnable runnable;
public TaskInfo(Executor executor, Runnable runnable) {
this.executor = executor;
this.runnable = runnable;
}
}
/**
* 调用样例
* @param args
*/
public static void main(String[] args) {
// 1. 准备一个线程池
ExecutorService executorService = Executors.newFixedThreadPool(3);
System.out.println("主流程开始,准备分发异步任务...");
System.out.println("主线程id:" + Thread.currentThread().getId());
// 2. 提交多个异步任务
// 任务一:获取用户信息
AsyncLatchUtils.submitTask(executorService, () -> {
try {
System.out.println("任务一子线程id:" + Thread.currentThread().getId());
System.out.println("开始获取用户信息...");
Thread.sleep(1000); // 模拟耗时
System.out.println("获取用户信息成功!");
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
});
// 任务二:获取订单信息
AsyncLatchUtils.submitTask(executorService, () -> {
try {
System.out.println("任务二子线程id:" + Thread.currentThread().getId());
System.out.println("开始获取订单信息...");
Thread.sleep(1500); // 模拟耗时
System.out.println("获取订单信息成功!");
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
});
// 任务三:获取商品信息
AsyncLatchUtils.submitTask(executorService, () -> {
try {
System.out.println("任务三子线程id:" + Thread.currentThread().getId());
System.out.println("开始获取商品信息...");
Thread.sleep(500); // 模拟耗时
System.out.println("获取商品信息成功!");
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
});
System.out.println("所有异步任务已提交,主线程开始等待...");
// 3. 等待所有任务完成最长等待5秒
boolean allTasksCompleted = AsyncLatchUtils.waitFor(5, TimeUnit.SECONDS);
// 4. 根据等待结果继续主流程
if (allTasksCompleted) {
System.out.println("所有异步任务执行成功,主流程继续...");
} else {
System.err.println("有任务执行超时,主流程中断!");
}
// 5. 关闭线程池
executorService.shutdown();
}
}

View File

@@ -0,0 +1,7 @@
/**
* 对于工具类的选择,优先查找 Hutool 中有没对应的方法
* 如果没有,则自己封装对应的工具类,以 Utils 结尾,用于区分
*
* ps如果担心 Hutool 存在坑的问题,可以阅读 Hutool 的实现源码,以确保可靠性。并且,可以补充相关的单元测试。
*/
package com.zt.plat.framework.common.util;

View File

@@ -0,0 +1,216 @@
package com.zt.plat.framework.common.util.tree;
import com.alibaba.fastjson.JSON;
import com.zt.plat.framework.common.util.object.ObjectUtils;
import lombok.Data;
import java.util.*;
import java.util.function.*;
import java.util.stream.Collectors;
/**
* 树操作方法工具类
*/
public class TreeUtil {
/**
* 将list合成树
*@param list 需要合成树的List
*@param rootCheck 判断E中为根节点的条件x->x.getPId()==-1L,x->x.getParentId()==null,x->x.getParentMenuId()==0
*@param parentCheck 判断E中为父节点条件(x,y)->x.getId().equals(y.getPId())
*@param setSubChildren E中设置下级数据方法Menu::setSubMenus
*@param<E> 泛型实体对象
*@return 合成好的树
*/
public static <E> List<E> makeTree(List<E> list, Predicate<E> rootCheck, BiFunction<E,E,Boolean> parentCheck, BiConsumer<E,List<E>> setSubChildren){
return list.stream().filter(rootCheck).peek(x->setSubChildren.accept(x,makeChildren(x,list,parentCheck,setSubChildren))).collect(Collectors.toList());
}
/**
*将树打平成tree
*@paramtree需要打平的树
*@paramgetSubChildren设置下级数据方法Menu::getSubMenus,x->x.setSubMenus(null)
*@paramsetSubChildren将下级数据置空方法x->x.setSubMenus(null)
*@return打平后的数据
*@param<E>泛型实体对象
*/
public static <E> List<E> flat(List<E> tree, Function<E,List<E>> getSubChildren, Consumer<E> setSubChildren){
List<E> res = new ArrayList<>();
forPostOrder(tree,item->{
setSubChildren.accept(item);
res.add(item);
},getSubChildren);
return res;
}
/**
*前序遍历
*
*@paramtree需要遍历的树
*@paramconsumer遍历后对单个元素的处理方法x->System.out.println(x)、System.out::println打印元素
*@paramsetSubChildren设置下级数据方法Menu::getSubMenus,x->x.setSubMenus(null)
*@param<E>泛型实体对象
*/
public static <E> void forPreOrder(List<E> tree,Consumer<E> consumer,Function<E,List<E>> setSubChildren){
for(E l : tree){
consumer.accept(l);
List<E> es = setSubChildren.apply(l);
if(es != null && es.size() > 0){
forPreOrder(es,consumer,setSubChildren);
}
}
}
/**
*层序遍历
*
*@paramtree需要遍历的树
*@paramconsumer遍历后对单个元素的处理方法x->System.out.println(x)、System.out::println打印元素
*@paramsetSubChildren设置下级数据方法Menu::getSubMenus,x->x.setSubMenus(null)
*@param<E>泛型实体对象
*/
public static <E> void forLevelOrder(List<E> tree,Consumer<E> consumer,Function<E,List<E>> setSubChildren){
Queue<E> queue=new LinkedList<>(tree);
while(!queue.isEmpty()){
E item = queue.poll();
consumer.accept(item);
List<E> childList = setSubChildren.apply(item);
if(childList !=null && !childList.isEmpty()){
queue.addAll(childList);
}
}
}
/**
*后序遍历
*
*@paramtree需要遍历的树
*@paramconsumer遍历后对单个元素的处理方法x->System.out.println(x)、System.out::println打印元素
*@paramsetSubChildren设置下级数据方法Menu::getSubMenus,x->x.setSubMenus(null)
*@param<E>泛型实体对象
*/
public static <E> void forPostOrder(List<E> tree,Consumer<E> consumer,Function<E,List<E>> setSubChildren){
for(E item : tree) {
List<E> childList = setSubChildren.apply(item);
if(childList != null && !childList.isEmpty()){
forPostOrder(childList,consumer,setSubChildren);
}
consumer.accept(item);
}
}
/**
*对树所有子节点按comparator排序
*
*@paramtree需要排序的树
*@paramcomparator排序规则ComparatorComparator.comparing(MenuVo::getRank)按Rank正序,(x,y)->y.getRank().compareTo(x.getRank())按Rank倒序
*@paramgetChildren获取下级数据方法MenuVo::getSubMenus
*@return排序好的树
*@param<E>泛型实体对象
*/
public static <E> List<E> sort(List<E> tree, Comparator<? super E> comparator, Function<E,List<E>> getChildren){
for(E item : tree){
List<E> childList = getChildren.apply(item);
if(childList != null &&! childList.isEmpty()){
sort(childList,comparator,getChildren);
}
}
tree.sort(comparator);
return tree;
}
private static <E> List<E> makeChildren(E parent,List<E> allData,BiFunction<E,E,Boolean> parentCheck,BiConsumer<E,List<E>> children){
return allData.stream().filter(x->parentCheck.apply(parent,x)).peek(x->children.accept(x,makeChildren(x,allData,parentCheck,children))).collect(Collectors.toList());
}
/**
* 使用样例
* @param args
*/
public static void main(String[] args) {
MenuVo menu0 = new MenuVo(0L, -1L, "一级菜单", 0);
MenuVo menu1 = new MenuVo(1L, 0L, "二级菜单", 1);
MenuVo menu2 = new MenuVo(2L, 0L, "三级菜单", 2);
MenuVo menu3 = new MenuVo(3L, 1L, "四级菜单", 3);
MenuVo menu4 = new MenuVo(4L, 1L, "五级菜单", 4);
MenuVo menu5 = new MenuVo(5L, 2L, "六级菜单", 5);
MenuVo menu6 = new MenuVo(6L, 2L, "七级菜单", 6);
MenuVo menu7 = new MenuVo(7L, 3L, "八级菜单", 7);
MenuVo menu8 = new MenuVo(8L, 3L, "九级菜单", 8);
MenuVo menu9 = new MenuVo(9L, 4L, "十级菜单", 9);
//基本数据
List<MenuVo> menuList = Arrays.asList(menu0,menu1, menu2,menu3,menu4,menu5,menu6,menu7,menu8,menu9);
//合成树
/**
* 第1个参数List list为我们需要合成树的List如上面Demo中的menuList
* 第2个参数Predicate rootCheck判断为根节点的条件如上面Demo中pId==-1就是根节点
* 第3个参数parentCheck 判断为父节点条件如上面Demo中 id==pId
* 第4个参数setSubChildren设置下级数据方法如上面Demo中Menu::setSubMenus
*/
List<MenuVo> tree= TreeUtil.makeTree(menuList, x->x.getPId()==-1L,(x, y)->x.getId().equals(y.getPId()), MenuVo::setSubMenus);
System.out.println(JSON.toJSONString(tree));
//先序
/**
* 遍历数参数解释:
* tree 需要遍历的树就是makeTree()合
* 成的对象Consumer consumer 遍历后对单个元素的处理方法x-> System.out.println(x)、 postOrder.append(x.getId().toString())
* Function<E, List> getSubChildren,获取下级数据方法如Menu::getSubMenus
*/
StringBuffer preStr = new StringBuffer();
TreeUtil.forPreOrder(tree,x-> preStr.append(x.getId().toString()),MenuVo::getSubMenus);
ObjectUtils.equalsAny("0123456789",preStr.toString());
//层序
StringBuffer levelStr=new StringBuffer();
TreeUtil.forLevelOrder(tree,x-> levelStr.append(x.getId().toString()),MenuVo::getSubMenus);
ObjectUtils.equalsAny("0123456789",levelStr.toString());
//后序
StringBuffer postOrder=new StringBuffer();
TreeUtil.forPostOrder(tree,x-> postOrder.append(x.getId().toString()),MenuVo::getSubMenus);
ObjectUtils.equalsAny("7839415620",postOrder.toString());
// 树平铺
/**
* flat()参数解释:
* tree 需要打平的树,就是makeTree()合成的对象Function<E, List> getSubChildren,
* 获取下级数据方法如Menu::getSubMenusConsumer setSubChildren,
* 设置下级数据方法x->x.setSubMenus(null)
*/
List<MenuVo> flat = TreeUtil.flat(tree, MenuVo::getSubMenus,x->x.setSubMenus(null));
ObjectUtils.equalsAny(flat.size(),menuList.size());
flat.forEach(x -> {
if (x.getSubMenus() != null) {
throw new RuntimeException("树平铺失败");
}
});
// 按rank正序
/**
* sort参数解释
* tree 需要排序的树,就是makeTree()合成的对象Comparator<? super E> comparator
* 排序规则ComparatorComparator.comparing(MenuVo::getRank) 按Rank正序 ,(x,y)->y.getRank().compareTo(x.getRank())按Rank倒序Function<E, List> getChildren
* 获取下级数据方法MenuVo::getSubMenus
*/
List<MenuVo> sortTree= TreeUtil.sort(tree, Comparator.comparing(MenuVo::getRank), MenuVo::getSubMenus);
// 按rank倒序
List<MenuVo> sortTreeReverse = TreeUtil.sort(tree, (x,y)->y.getRank().compareTo(x.getRank()), MenuVo::getSubMenus);
}
@Data
static class MenuVo {
private Long id; // 主键id
private Long pId; // 父级id
private String name; // 菜单名称
private Integer rank = 0; // 排序
private List<MenuVo> subMenus = new ArrayList<>(); // 子菜单
public MenuVo(Long id, Long pId, String name, Integer rank) {
this.id = id;
this.pId = pId;
this.name = name;
this.rank = rank;
}
}
}