diff --git a/.gitignore b/.gitignore index 26a62dc..df985dc 100644 --- a/.gitignore +++ b/.gitignore @@ -1,20 +1,29 @@ -# ---> Actionscript -# Build and Release Folders -bin-debug/ -bin-release/ -[Oo]bj/ -[Bb]in/ +# ---> Project -# Other files and folders +# logs +**/logs/ + +# ai +.claude + +# ---> Maven +target/ +pom.xml.tag +pom.xml.releaseBackup +pom.xml.versionsBackup +pom.xml.next +release.properties +dependency-reduced-pom.xml +buildNumber.properties +.mvn/timing.properties +# https://github.com/takari/maven-wrapper#usage-without-binary-jar +.mvn/wrapper/maven-wrapper.jar + +# ---> Eclipse .settings/ +.project +.classpath -# Executables -*.swf -*.air -*.ipa -*.apk - -# Project files, i.e. `.project`, `.actionScriptProperties` and `.flexProperties` -# should NOT be excluded as they contain compiler settings and other important -# information for Eclipse / Flash Builder. - +# ---> Idea +.idea/ +*.iml \ No newline at end of file diff --git a/README.md b/README.md index b9cd0ed..8d0401a 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,1112 @@ -# xtools-boot +# xtools-boot 项目设计文档 -低调大师工具箱,SpringBoot工具模块,适配JDK25 \ No newline at end of file +## 一、功能和用途 + +### 1.1 项目概述 + +- **项目名称**:xtools-boot +- **项目版本**:5.0.0 +- **父POM**:org.xujun:xtools-parent-boot:5.0.0 +- **项目定位**:低调大师工具箱,SpringBoot工具模块,适配JDK25。提供企业级应用开发所需的通用功能模块,涵盖缓存、数据库、搜索、消息队列、任务调度、日志、存储、脱敏等核心能力,以Spring Boot Starter形式封装,支持按需引入。 + +### 1.2 技术特点 + +- 采用最新的 JDK 25 版本,充分利用新特性(虚拟线程、结构化并发等) +- 基于 Spring Boot 4.0.5 构建,支持自动配置和快速开发 +- 集成 Spring Framework 7.0.6 核心框架 +- 使用 MyBatis 4.0.1 + MyBatis-Plus 3.5.16 简化数据访问层开发 +- 集成 Spring Data Elasticsearch 6.0.4 + Elasticsearch Client 9.2.6 实现日志存储和检索 +- 集成 Spring AMQP 4.0.2 + RabbitMQ AMQP Client 5.27.1 实现异步消息处理 +- 使用 Spring Data Redis 4.0.4 + Lettuce 6.8.2.RELEASE 实现分布式缓存 +- 集成 XXL-JOB 3.4.0 实现分布式任务调度 +- 集成 AWS S3 SDK 2.42.34 实现对象存储 +- 集成 Knife4j 4.5.0 自动生成 API 文档 +- 使用 BouncyCastle 1.84 提供加密支持 +- 使用 Druid 1.2.28 数据库连接池 +- 使用 FastJSON2 2.0.60 进行 JSON 序列化 +- 使用 MapStruct 1.6.3 进行对象映射 +- 使用 Lombok 1.18.44 简化代码 +- 使用 Velocity 2.4.1 模板引擎 +- 使用 Caffeine 3.2.3 本地缓存 +- 使用 ip2region 3.3.7 实现IP地址定位 +- 使用 OSHI 6.11.1 实现系统监控 +- 使用 Fesod Sheet 2.0.1-incubating 处理Excel + +### 1.3 核心功能 + +```mermaid +graph TB + subgraph 基础模块 + A1[xtools-boot-api
API定义层] + A2[xtools-boot-core
核心工具层] + end + + subgraph 数据模块 + B1[xtools-boot-db
数据库模块] + B2[xtools-boot-cache
缓存模块] + B3[xtools-boot-elasticsearch
搜索引擎模块] + end + + subgraph 中间件模块 + C1[xtools-boot-mq
消息队列模块] + C2[xtools-boot-task
任务管理模块] + C3[xtools-boot-job
任务调度模块] + C4[xtools-boot-thread
线程管理模块] + end + + subgraph 功能模块 + D1[xtools-boot-log
日志模块] + D2[xtools-boot-web
Web模块] + D3[xtools-boot-knife4j
API文档模块] + D4[xtools-boot-ip
IP定位模块] + D5[xtools-boot-mask
数据脱敏模块] + D6[xtools-boot-storage
存储模块] + end + + B1 --> B1a[xtools-boot-db-mybatis
MyBatis集成] + B1 --> B1b[xtools-boot-db-mybatis-plus
MyBatis-Plus集成] + B2 --> B2a[xtools-boot-cache-redis
Redis缓存] + C1 --> C1a[xtools-boot-mq-base
消息总线] + C1 --> C1b[xtools-boot-mq-rabbit
RabbitMQ实现] + C3 --> C3a[xtools-boot-job-xxl
XXL-JOB集成] + D6 --> D6a[xtools-boot-storage-base
存储基础] + D6 --> D6b[xtools-boot-storage-file
文件存储] + D6 --> D6c[xtools-boot-storage-s3
S3对象存储] + D2 --> D2a[xtools-boot-web-base
Web基础] + D2 --> D2b[xtools-boot-web-filter
Web过滤器] +``` + +```mermaid +mindmap + root((xtools-boot
SpringBoot工具箱)) + 基础模块 + xtools-boot-api + 基础实体BaseEntity + 统一枚举BaseEnum + 异常体系 + 日志链路追踪 + xtools-boot-core + Spring上下文工具 + 树形结构工具 + 枚举工具 + 任务执行接口 + 模块加载追踪 + 数据模块 + 数据库 + MyBatis集成 + MyBatis-Plus集成 + Druid连接池 + 数据库监控 + 缓存 + Redis缓存 + 缓存监控 + 搜索引擎 + Elasticsearch集成 + ES查询工具 + ES集群监控 + 中间件模块 + 消息队列 + 消息总线MqBus + RabbitMQ集成 + 虚拟线程消费 + 错误处理 + 任务管理 + TaskBus任务总线 + 任务状态追踪 + 异步任务执行 + 任务调度 + XXL-JOB集成 + 自动注册执行器 + 线程管理 + 虚拟线程支持 + 线程回调机制 + 功能模块 + 日志 + LogBus日志总线 + 链路追踪LogTrack + 多级别日志 + 主子线程区分 + Web + MVC配置 + 请求过滤器 + 日志链路集成 + API文档 + Knife4j增强UI + OpenAPI 3集成 + IP定位 + ip2region离线查询 + 数据脱敏 + 注解式脱敏 + 多类型敏感数据 + 自定义脱敏策略 + 存储 + 本地文件存储 + AWS S3对象存储 + 统一存储接口 +``` + +### 1.4 功能关系图 + +```mermaid +graph LR + Client[客户端请求] --> Web[xtools-boot-web] + Web --> Filter[请求过滤器] + Filter --> LogTrack[日志链路追踪] + + LogTrack --> Log[xtools-boot-log] + Log --> LogBus[LogBus日志总线] + + Web --> Controller[业务Controller] + Controller --> Cache[xtools-boot-cache] + Controller --> DB[xtools-boot-db] + + Cache --> Redis[Redis] + DB --> MySQL[MySQL] + + Controller --> MQ[xtools-boot-mq] + MQ --> RabbitMQ[RabbitMQ] + + MQ --> Thread[xtools-boot-thread] + Thread --> VThread[虚拟线程] + + Controller --> Task[xtools-boot-task] + Task --> TaskBus[TaskBus任务总线] + + Job[xtools-boot-job] --> XXLJob[XXL-JOB] + + Controller --> ES[xtools-boot-elasticsearch] + ES --> Elasticsearch[Elasticsearch] + + Controller --> Storage[xtools-boot-storage] + Storage --> File[本地文件/S3] + + Controller --> Mask[xtools-boot-mask] + + Log --> Doc[xtools-boot-knife4j] +``` + +## 二、项目结构设计 + +### 2.1 整体架构 + +```mermaid +graph TD + App[xtools-boot
父模块] --> API[xtools-boot-api
API定义模块] + App --> Core[xtools-boot-core
核心工具模块] + + App --> DB[xtools-boot-db
数据库模块] + App --> Cache[xtools-boot-cache
缓存模块] + App --> ES[xtools-boot-elasticsearch
搜索引擎模块] + + App --> MQ[xtools-boot-mq
消息队列模块] + App --> Task[xtools-boot-task
任务管理模块] + App --> Job[xtools-boot-job
任务调度模块] + App --> Thread[xtools-boot-thread
线程管理模块] + + App --> Log[xtools-boot-log
日志模块] + App --> Web[xtools-boot-web
Web模块] + App --> Knife4j[xtools-boot-knife4j
API文档模块] + App --> IP[xtools-boot-ip
IP定位模块] + App --> Mask[xtools-boot-mask
数据脱敏模块] + App --> Storage[xtools-boot-storage
存储模块] + + DB --> DBMybatis[xtools-boot-db-mybatis] + DB --> DBMP[xtools-boot-db-mybatis-plus] + + Cache --> CacheRedis[xtools-boot-cache-redis] + + MQ --> MQBase[xtools-boot-mq-base] + MQ --> MQRabbit[xtools-boot-mq-rabbit] + + Job --> JobXXL[xtools-boot-job-xxl] + + Web --> WebBase[xtools-boot-web-base] + Web --> WebFilter[xtools-boot-web-filter] + + Storage --> StorageBase[xtools-boot-storage-base] + Storage --> StorageFile[xtools-boot-storage-file] + Storage --> StorageS3[xtools-boot-storage-s3] +``` + +### 2.2 分层架构 + +```mermaid +flowchart TB + subgraph 自动配置层 + A1[AutoConfiguration
自动配置注册] + A2[ImportSelector
导入选择器] + A3[Configuration
配置类] + end + + subgraph 接口定义层 + B1[BaseEnum
统一枚举接口] + B2[BaseEntity
基础实体] + B3[LogBusInterface
日志处理接口] + B4[StorageService
存储服务接口] + B5[JobInterface
任务执行接口] + B6[BaseTaskType
任务类型接口] + B7[MaskCustom
自定义脱敏接口] + end + + subgraph 基础功能层 + C1[SpringContextUtils
Spring上下文工具] + C2[TreeUtils
树形结构工具] + C3[EnumUtils
枚举工具] + C4[RedisUtils
Redis工具] + C5[EsUtils/EsQueryUtils
ES工具] + C6[MqBus
消息总线] + C7[LogBus
日志总线] + C8[TaskBus
任务总线] + end + + subgraph 扩展功能层 + D1[MySqlMonitor
MySQL监控] + D2[RedisMonitor
Redis监控] + D3[ElasticsearchMonitor
ES监控] + D4[DefaultMaskHandle
默认脱敏处理] + D5[BaseMqHandle
消息处理基类] + D6[BaseErrorHandle
错误处理基类] + end + + A1 --> A2 + A2 --> A3 + A3 --> B1 + B1 --> C1 + C1 --> D1 +``` + +### 2.3 模块职责 + +| 模块 | 职责 | 核心类 | +|------|------|--------| +| xtools-boot-api | API定义层,提供基础实体、枚举、异常、日志链路等公共定义 | BaseEntity、BaseEnum、BizError、LogTrack | +| xtools-boot-core | 核心工具层,提供Spring上下文、树形结构、枚举等通用工具 | SpringContextUtils、TreeUtils、EnumUtils | +| xtools-boot-db-mybatis | MyBatis集成,提供数据库访问、监控、慢查询检测 | MyBatisConfig、MySqlMonitor | +| xtools-boot-db-mybatis-plus | MyBatis-Plus集成,提供分页插件、查询工具 | MybatisPlusConfig、QueryUtils | +| xtools-boot-cache-redis | Redis缓存,提供缓存操作、监控 | RedisUtils、RedisMonitor | +| xtools-boot-elasticsearch | ES集成,提供搜索、查询、集群监控 | EsUtils、EsQueryUtils、ElasticsearchMonitor | +| xtools-boot-mq-base | 消息总线,提供消息发布、处理、错误处理 | MqBus、BaseMqHandle、BaseErrorHandle | +| xtools-boot-mq-rabbit | RabbitMQ实现,提供消息监听、消费 | BootRabbitMqConfiguration | +| xtools-boot-task | 任务管理,提供任务总线、状态追踪 | TaskBus、TaskInfo、TaskStatus | +| xtools-boot-job-xxl | XXL-JOB集成,提供分布式任务调度 | BootXxlJobConfiguration、InitXxlJob | +| xtools-boot-thread | 线程管理,提供虚拟线程、回调机制 | BootThreadConfiguration | +| xtools-boot-log | 日志模块,提供日志总线、链路追踪 | LogBus、LogTrackHolder、LogBody | +| xtools-boot-web-base | Web基础,提供MVC配置、过滤器、转换器 | BootWebBaseConfiguration、CommonFilter | +| xtools-boot-web-filter | Web过滤器,提供过滤器扫描注册 | BootWebFilterConfiguration | +| xtools-boot-knife4j | API文档,提供Knife4j/OpenAPI3集成 | BootKnife4jConfiguration | +| xtools-boot-ip | IP定位,提供离线IP地址查询 | BootIpConfiguration | +| xtools-boot-mask | 数据脱敏,提供注解式敏感数据处理 | DefaultMaskHandle、MaskType | +| xtools-boot-storage-base | 存储基础,提供统一存储接口 | StorageService | +| xtools-boot-storage-file | 文件存储,提供本地文件系统存储 | StorageServiceFileImpl | +| xtools-boot-storage-s3 | S3存储,提供AWS S3对象存储 | BootStorageS3Configuration | + +### 2.4 包结构设计 + +``` +xtools.boot.{module} +├── Boot{Module}Configuration.java # 配置类(自动配置入口) +├── selector/ # 导入选择器 +│ └── Boot{Module}ImportSelector.java +├── config/ # 配置 +├── interfaces/ # 接口定义 +│ ├── *Interface.java # 功能接口 +│ └── *Type.java # 类型接口 +├── model/ # 数据模型 +│ ├── dto/ # 数据传输对象 +│ └── entity/ # 实体类 +├── enums/ # 枚举类 +├── utils/ # 工具类 +├── handle/ # 处理器 +├── monitor/ # 监控 +├── holder/ # 线程持有者 +├── callback/ # 回调 +├── init/ # 初始化 +├── service/ # 服务 +│ └── impl/ # 服务实现 +└── filter/ # 过滤器 +``` + +### 2.5 模块依赖关系 + +```mermaid +graph LR + API[xtools-boot-api] --> Extend[xtools-extend] + API --> WebCore[xtools-web] + Core[xtools-boot-core] --> API + Core --> Extend + + DBMybatis[xtools-boot-db-mybatis] --> Core + DBMybatis --> Log[xtools-boot-log] + DBMybatis --> Thread[xtools-boot-thread] + DBMP[xtools-boot-db-mybatis-plus] --> Core + + CacheRedis[xtools-boot-cache-redis] --> Core + + ES[xtools-boot-elasticsearch] --> Extend + ES --> Core + ES --> Log + + MQBase[xtools-boot-mq-base] --> Core + MQBase --> Log + MQBase --> Thread + MQRabbit[xtools-boot-mq-rabbit] --> Core + MQRabbit --> MQBase + + Task[xtools-boot-task] --> Core + JobXXL[xtools-boot-job-xxl] --> Core + + Log --> Core + Thread --> Core + IP[xtools-boot-ip] --> Extend + IP --> Core + + Knife4j[xtools-boot-knife4j] --> Core + Mask[xtools-boot-mask] --> Core + + StorageBase[xtools-boot-storage-base] --> Core + StorageFile[xtools-boot-storage-file] --> Core + StorageFile --> StorageBase + StorageS3[xtools-boot-storage-s3] --> Extend + StorageS3 --> Core + StorageS3 --> StorageBase + + WebBase[xtools-boot-web-base] --> WebCore + WebBase --> Core + WebBase --> Log + WebBase --> WebFilter[xtools-boot-web-filter] + WebFilter --> Core +``` + +## 三、项目功能设计 + +### 3.1 自动配置机制 + +```mermaid +flowchart TD + A[应用启动] --> B[Spring Boot扫描
META-INF/spring/*.imports] + B --> C[加载自动配置类
BootModuleConfiguration] + C --> D[执行构造函数
ModuleLoadUtils.loadSuccess] + D --> E["@Import导入
XXXImportSelector"] + E --> F[ImportBeanDefinitionRegistrar
注册Bean定义] + F --> G["@ConditionalOnProperty
条件化创建Bean"] + G --> H[模块就绪] +``` + +**自动配置设计**: +- 每个模块通过 `META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports` 注册自动配置类 +- 配置类通过 `@Import(XXXImportSelector.class)` 导入自定义选择器 +- 选择器实现 `ImportBeanDefinitionRegistrar` 接口,按包名扫描注册Bean +- 使用 `@ConditionalOnProperty` 实现条件化Bean创建 +- 构造函数中调用 `ModuleLoadUtils.loadSuccess()` 记录模块加载状态 + +### 3.2 统一枚举设计 + +```mermaid +flowchart TD + A[BaseEnum接口] --> B[int code
枚举编码] + A --> C[String desc
枚举描述] + A --> D["BaseEnum[] all()
获取所有枚举"] + + B --> E[StatusEnum
状态枚举] + B --> F[DeleteEnum
删除枚举] + B --> G[ThreadType
线程类型] + B --> H[FileDataType
文件类型] + B --> I[TaskStatus
任务状态] + B --> J[MaskType
脱敏类型] + B --> K[MySqlMonitorEnums
MySQL监控枚举] + B --> L[ElasticsearchMonitorEnums
ES监控枚举] +``` + +**枚举规范**: +- 所有业务枚举实现 `BaseEnum` 接口 +- 提供 `code()` 和 `desc()` 方法 +- 提供静态 `valueOf(int code)` 和 `valueOfDesc(String desc)` 方法 +- 使用 `EnumUtils` 工具类进行枚举转换 + +### 3.3 异常处理设计 + +```mermaid +flowchart TD + A[CommonException
基础异常] --> B[BizError
业务异常] + A --> C[BizWarning
业务警告] + A --> D[UnauthorizedError
未授权异常] + A --> E[BizPublicKeyError
公钥异常] + + F[BootError
错误码枚举] --> F1[code
错误编码] + F --> F2[type
错误类型] + F --> F3[module
所属模块] + F --> F4[messageTemplate
消息模板] + + G[BootErrorModule
错误模块枚举] --> G1[API
API模块] + G --> G2[CORE
核心模块] + G --> G3[LOG
日志模块] + G --> G4[MQ
消息队列模块] +``` + +**异常体系**: +- `CommonException`:基础异常(来自xtools-core) +- `BizError`:业务逻辑异常,使用错误码和消息模板 +- `BizWarning`:业务警告异常 +- `UnauthorizedError`:认证授权异常 +- `BizPublicKeyError`:公钥相关异常 +- `BootError`:错误码枚举,包含code、type、module、messageTemplate +- `BootErrorModule`:错误模块枚举,用于错误分类 + +### 3.4 日志管理设计 + +```mermaid +flowchart TD + A[业务代码] --> B[LogBus.createLog
创建日志] + B --> C[LogBody
日志体] + C --> C1[title
日志主题] + C --> C2[level
日志级别] + C --> C3[type
日志类型] + C --> C4[logTrack
链路追踪] + C --> C5[runInfo
运行环境] + C --> C6[stackTrace
堆栈信息] + C --> C7[logData
日志数据] + C --> C8[err
异常文本] + + B --> D[LogBusInterface实现类
日志处理器] + D --> E[写入Elasticsearch] + D --> F[写入数据库] + D --> G[发送到消息队列] +``` + +```mermaid +flowchart LR + A[HTTP请求] --> B[LogTrackConfig
生成LogTrack] + B --> C[LogTrackHolder
线程绑定] + C --> D[业务处理] + D --> E{是否异步线程?} + E -->|是| F[传递LogTrack到子线程] + E -->|否| G[直接使用] + F --> H[子线程处理] + G --> I[响应返回] + H --> I +``` + +**日志功能**: +- **LogBus**:日志总线,统一的日志创建和管理入口 +- **LogTrack**:链路追踪信息,包含traceId、spanId、线程类型等 +- **HolderLogTrack**:线程持有者的链路追踪,支持主子线程传递 +- **LogBusInterface**:日志处理接口,支持自定义日志处理器 +- **LogBusType**:日志类型接口,定义日志基础编码 +- **LogBody**:日志体,包含主题、级别、类型、链路、环境、堆栈、数据、异常 +- **LogBusConfig**:日志总线配置,控制日志打印和堆栈包含 +- **RunInfoConfig**:运行环境信息配置 +- **LogLevel**:日志级别(来自xtools-core) + +### 3.5 消息队列设计 + +```mermaid +flowchart TD + A[业务代码] --> B[MqBus.publish
发布消息] + B --> C[MessageDto
消息体] + C --> C1[log
LogTrack链路追踪] + C --> C2[data
消息数据] + B --> D[RabbitMQ Exchange] + D --> E[Queue] + E --> F["@RabbitListener
消息监听器"] + F --> G[BaseMqHandle
消息处理基类] + G --> H{处理是否成功?} + H -->|成功| I[记录处理结果] + H -->|失败| J[BaseErrorHandle
错误处理] + J --> K[重试/记录错误日志] +``` + +**消息队列功能**: +- **MqBus**:消息总线,统一的消息发布入口 +- **BaseMqHandle**:消息处理基类,定义消息处理模板 +- **BaseErrorHandle**:错误处理基类,定义错误处理策略 +- **InitMq**:自动初始化,启动时扫描并注册消息监听器 +- **MessageDto**:消息体,包含LogTrack链路追踪和消息数据 +- **MqEnums**:消息队列相关枚举 +- **RabbitMqParams**:RabbitMQ参数配置 +- 支持虚拟线程消费消息 +- 消息序列化使用FastJSON2 + +### 3.6 任务管理设计 + +```mermaid +flowchart TD + A[TaskBus] --> B["new TaskBus()
创建任务总线"] + B --> C["taskType(BaseTaskType)
设置任务类型"] + C --> D["taskData(Object)
设置任务数据"] + D --> E["execute()
执行任务"] + E --> F{任务执行} + F --> G[TaskStatus.ING
执行中] + G --> H{执行结果} + H -->|成功| I[TaskStatus.SUCCESS] + H -->|失败| J[TaskStatus.ERROR] +``` + +**任务管理功能**: +- **TaskBus**:任务总线,提供Builder模式创建和管理任务 +- **TaskInfo**:任务信息,包含任务类型、任务数据、状态等 +- **TaskStatus**:任务状态枚举(ING、SUCCESS、ERROR) +- **BaseTaskType**:任务类型接口,定义任务类型 +- **TaskBusInterface**:任务总线接口 + +### 3.7 任务调度设计 + +```mermaid +flowchart TD + A[应用启动] --> B[BootXxlJobConfiguration
XXL-JOB配置] + B --> C["@ConditionalOnProperty
条件化加载"] + C --> D[InitXxlJob
初始化XXL-JOB] + D --> E[扫描JobHandler] + E --> F[注册到XXL-JOB调度中心] + F --> G[调度中心触发任务] + G --> H[JobInterface实现类
执行任务] +``` + +**任务调度功能**: +- **BootXxlJobConfiguration**:XXL-JOB自动配置 +- **InitXxlJob**:XXL-JOB初始化,自动扫描和注册任务处理器 +- **JobInterface**:任务执行接口,定义任务执行契约 +- 通过 `@ConditionalOnProperty` 条件化加载 +- 支持分布式任务调度 + +### 3.8 线程管理设计 + +```mermaid +flowchart TD + A[BootThreadConfiguration
线程配置] --> B[虚拟线程支持] + A --> C[VirtualThreadTaskCallback
虚拟线程任务回调] + + B --> D[MQ消息消费使用虚拟线程] + B --> E[异步任务使用虚拟线程] + + C --> F[任务执行前回调] + C --> G[任务执行后回调] + C --> H[异常处理回调] +``` + +**线程管理功能**: +- **BootThreadConfiguration**:线程配置,注册虚拟线程相关Bean +- **VirtualThreadTaskCallback**:虚拟线程任务回调接口,提供执行前、执行后、异常处理回调 +- 支持在消息消费和异步任务中使用虚拟线程 + +### 3.9 缓存设计 + +```mermaid +flowchart LR + A[业务代码] --> B[RedisUtils
Redis工具类] + B --> C{Redis操作} + C --> D[RedisMonitor
缓存监控] + C --> E[读写缓存] + C --> F[设置过期时间] + E --> G[Lettuce客户端] + F --> G + G --> H[Redis Server] +``` + +**缓存功能**: +- **BootCacheRedisConfiguration**:Redis自动配置 +- **RedisUtils**:Redis工具类,封装常用Redis操作 +- **RedisMonitor**:Redis监控,提供缓存统计和健康检查 +- 使用Lettuce作为Redis客户端 + +### 3.10 数据库设计 + +```mermaid +flowchart TD + subgraph xtools-boot-db-mybatis + A1[MyBatisConfig
MyBatis配置] + A2[MySqlMonitor
MySQL监控] + A3[MonitorDatabasesMapper
监控Mapper] + A4[Druid连接池] + end + + subgraph xtools-boot-db-mybatis-plus + B1[MybatisPlusConfig
MP配置] + B2[QueryUtils
查询工具] + end + + A1 --> C[MyBatis 4.0.1] + B1 --> D[MyBatis-Plus 3.5.16] + A4 --> E[Druid 1.2.28] + B2 --> F[分页插件
时间范围过滤] +``` + +**数据库功能**: +- **MyBatisConfig**:MyBatis核心配置 +- **MybatisPlusConfig**:MyBatis-Plus配置,注册分页插件 +- **QueryUtils**:查询工具类,提供时间范围过滤等通用查询方法 +- **MySqlMonitor**:MySQL监控,检测数据库连接、慢查询等 +- **MonitorDatabasesMapper**:数据库监控Mapper +- **MySqlMonitorEnums**:监控指标枚举 +- 使用Druid连接池管理数据库连接 + +### 3.11 搜索引擎设计 + +```mermaid +flowchart TD + A[业务代码] --> B[EsUtils
ES HTTP工具] + A --> C[EsQueryUtils
ES查询工具] + + B --> D[索引操作] + B --> E[文档操作] + B --> F[集群操作] + + C --> G[构建查询] + C --> H[聚合查询] + C --> I[分页查询] + + J[ElasticsearchMonitor
ES监控] --> K[集群健康] + J --> L[索引状态] + J --> M[节点信息] + J --> N[ElasticsearchMonitorEnums
监控枚举] +``` + +**搜索引擎功能**: +- **BootElasticsearchConfiguration**:ES自动配置 +- **EsUtils**:Elasticsearch HTTP客户端工具,提供索引、文档、集群操作 +- **EsQueryUtils**:ES查询工具,提供通用查询构建 +- **ElasticsearchMonitor**:ES集群监控 +- **ElasticsearchMonitorEnums**:监控指标枚举 + +### 3.12 数据脱敏设计 + +```mermaid +flowchart TD + A["实体类字段
@Mask注解"] --> B[MaskType
脱敏类型] + B --> B1[CHINESE_NAME
中文姓名] + B --> B2[ID_CARD
身份证号] + B --> B3[MOBILE_PHONE
手机号] + B --> B4[EMAIL
邮箱] + B --> B5[BANK_CARD
银行卡] + B --> B6[PASSWORD
密码] + B --> B7[ADDRESS
地址] + B --> B8[CUSTOM
自定义] + + A --> C["配置参数
prefixNoMaskLen
suffixNoMaskLen
maskChar"] + C --> D[DefaultMaskHandle
默认脱敏处理] + D --> E[MaskCustom
自定义脱敏接口] + E --> F[Jackson序列化时
自动脱敏] +``` + +**数据脱敏功能**: +- **BootMaskConfiguration**:脱敏模块自动配置 +- **MaskType**:脱敏类型枚举,支持8种内置类型 +- **DefaultMaskHandle**:默认脱敏处理器,根据类型自动脱敏 +- **MaskCustom**:自定义脱敏接口,支持自定义脱敏逻辑 +- 基于Jackson序列化,在JSON输出时自动脱敏 +- 支持配置前缀保留长度、后缀保留长度、脱敏字符 + +### 3.13 存储设计 + +```mermaid +flowchart TD + A[StorageService
统一存储接口] --> A1[exists
文件是否存在] + A --> A2[save
保存文件] + A --> A3[get
获取文件] + A --> A4[del
删除文件] + + B[StorageServiceFileImpl
文件存储实现] --> C[本地文件系统] + D[BootStorageS3Configuration
S3存储配置] --> E[AWS S3] + + F["@ConditionalOnProperty
storage.type=file"] --> B + G["@ConditionalOnProperty
storage.type=s3"] --> D + + A2 --> A2a[bucket
桶名称] + A2 --> A2b[fileName
文件名] + A2 --> A2c[inputStream
输入流] + A2 --> A2d[contentLength
内容长度] +``` + +**存储功能**: +- **StorageService**:统一存储接口,定义exists、save、get、del操作 +- **BootStorageBaseConfiguration**:存储基础配置 +- **StorageServiceFileImpl**:本地文件系统存储实现 +- **BootStorageS3Configuration**:AWS S3对象存储配置 +- **FileStorageConfig**:文件存储配置,支持配置存储路径 +- 通过 `@ConditionalOnProperty` 选择存储实现 + +### 3.14 Web模块设计 + +```mermaid +flowchart TD + A[xtools-boot-web] --> B[xtools-boot-web-base] + A --> C[xtools-boot-web-filter] + + B --> B1[BootWebBaseConfiguration
Web基础配置] + B --> B2[CommonFilter
通用过滤器] + B --> B3[MvcConverterConfig
MVC转换器配置] + B --> B4[LogTrackConfig
日志链路配置] + + C --> C1[BootWebFilterConfiguration
过滤器配置] + C --> C2[BootWebFilterImportSelector
过滤器导入选择器] + + B4 --> D[生成LogTrack] + D --> E[绑定到ThreadLocal] + E --> F[请求处理] + F --> G[清除ThreadLocal] +``` + +**Web模块功能**: +- **BootWebBaseConfiguration**:Web基础配置,集成xtools-web +- **CommonFilter**:通用请求过滤器,处理请求上下文 +- **MvcConverterConfig**:MVC转换器配置 +- **LogTrackConfig**:日志链路配置,为HTTP请求生成和管理LogTrack +- **BootWebFilterConfiguration**:Web过滤器配置 + +### 3.15 API文档设计 + +- **BootKnife4jConfiguration**:Knife4j自动配置 +- 集成 springdoc-openapi-starter-webmvc-ui 3.0.3 +- 集成 knife4j-openapi3-jakarta-spring-boot-starter 4.5.0 +- 使用 Swagger Annotations 2.2.48 定义API文档 +- 使用 `@Schema` 注解标注实体字段描述 + +### 3.16 IP定位设计 + +- **BootIpConfiguration**:IP定位自动配置 +- 使用 ip2region 3.3.7 离线IP地址库 +- 内置 `ip/ip2region_v4.xdb` 数据文件 +- 支持IPv4地址的地理定位查询 + +## 四、编码规范设计 + +### 4.1 命名规范 + +**类命名**: + +| 类型 | 命名规则 | 示例 | +|------|----------|------| +| 配置类 | Boot{Module}Configuration | BootCoreConfiguration | +| 导入选择器 | Boot{Module}ImportSelector | BootCoreImportSelector | +| 工具类 | {功能}Utils | SpringContextUtils | +| 监控类 | {组件}Monitor | MySqlMonitor | +| 枚举类 | {功能}Enum / {功能}Enums | StatusEnum、MySqlMonitorEnums | +| 异常类 | Biz{类型}Error | BizError、BizWarning | +| 接口 | Base{类型} / {功能}Interface / {功能}Type | BaseEnum、LogBusInterface、BaseTaskType | +| DTO类 | {功能}Dto / {功能}Info / {功能}Body | MessageDto、TaskInfo、LogBody | +| 回调类 | {功能}Callback | VirtualThreadTaskCallback | +| 初始化类 | Init{功能} | InitMq、InitXxlJob | +| 处理器 | {功能}Handle / Default{功能}Handle | BaseMqHandle、DefaultMaskHandle | + +**方法命名**: + +| 操作 | 命名规则 | 示例 | +|------|----------|------| +| 查询 | get / find / query | getBean、findById、queryList | +| 创建 | create / save / publish | createLog、save、publish | +| 删除 | delete / del / remove | deleteById、del | +| 判断 | is / has / exists | exists、isValid | +| 转换 | to / convert / of | valueOf、convert | +| 初始化 | init | init | +| 加载 | load | loadSuccess | +| 执行 | execute / run | execute | + +**变量命名**: + +| 类型 | 命名规则 | 示例 | +|------|----------|------| +| 普通变量 | camelCase | logTrack、taskInfo | +| 常量 | UPPER_SNAKE_CASE | CP_NUM0、CP_NUM50 | +| Boolean | is/has前缀 | isValid、hasChildren | +| 集合 | 复数形式 | items、list | +| 配置属性 | camelCase | prefixNoMaskLen、maskChar | + +### 4.2 注释规范 + +**类注释格式**: + +```java +/** + *

Title : 类名称

+ *

Description : 类描述

+ *

DevelopTools : Idea_x64_v2026.1

+ *

DevelopSystem : macOS Sequoia 15.7.5

+ *

Company : org.xujun

+ * + * @author : XuJun + * @version : 5.0.0 + * @date : 2026/1/17 18:50 + */ +``` + +**方法注释格式**: + +```java +/** + * 方法描述 + * + * @param param 参数说明 + * @return 返回值说明 + */ +``` + +**字段注释格式**: + +```java +/** + * 字段描述 + */ +private String fieldName; +``` + +### 4.3 代码风格 + +- 使用 **Lombok** 简化代码(@Data、@Getter、@RequiredArgsConstructor) +- 使用 **MapStruct** 进行对象转换 +- 使用 **FastJSON2** 进行JSON序列化 +- 使用 **构造器注入**(通过Lombok的@RequiredArgsConstructor) +- 使用 **@Schema** 注解标注API文档 +- 使用 **@ConfigurationProperties** 进行配置绑定 +- 使用 **@ConditionalOnProperty** 进行条件化配置 +- 使用 **@Import + ImportSelector** 实现自动配置 +- 模块化设计,每个功能独立成模块 + +### 4.4 设计规范 + +- **分层原则**:接口定义层 → 基础功能层 → 扩展功能层 +- **单一职责**:每个模块专注单一功能领域 +- **开闭原则**:通过接口(LogBusInterface、StorageService、MaskCustom)支持扩展 +- **依赖倒置**:面向接口编程,通过配置选择实现 +- **模块化**:功能独立封装,按需引入 + +### 4.5 安全规范 + +- 数据脱敏:支持身份证、手机号、邮箱、银行卡等敏感数据自动脱敏 +- 加密支持:集成BouncyCastle加密库 +- SQL安全:使用MyBatis参数化查询,防止SQL注入 +- 数据验证:集成Jakarta Validation(@NotNull、@Valid) +- JWT支持:集成java-jwt 4.5.1 + +## 五、项目依赖设计 + +### 5.1 核心框架依赖 + +| 依赖 | 版本 | 用途 | +|------|------|------| +| Spring Boot | 4.0.5 | 应用框架 | +| Spring Framework | 7.0.6 | 核心框架 | +| Spring Security | 7.0.4 | 安全框架 | +| Spring AMQP | 4.0.2 | RabbitMQ集成 | +| Spring Data BOM | 2025.1.4 | Spring Data版本管理 | +| Spring Data Redis | 4.0.4 | Redis集成 | +| Spring Data Elasticsearch | 6.0.4 | Elasticsearch集成 | +| Jakarta Servlet | 6.1.0 | Servlet API | +| Jakarta Validation | 3.1.1 | 参数校验 | + +### 5.2 数据库相关依赖 + +| 依赖 | 版本 | 用途 | +|------|------|------| +| MyBatis Spring Boot Starter | 4.0.1 | MyBatis集成 | +| MyBatis-Plus | 3.5.16 | ORM增强工具 | +| Druid | 1.2.28 | 数据库连接池 | + +### 5.3 搜索引擎依赖 + +| 依赖 | 版本 | 用途 | +|------|------|------| +| Elasticsearch Client | 9.2.6 | ES Java客户端 | + +### 5.4 消息队列依赖 + +| 依赖 | 版本 | 用途 | +|------|------|------| +| RabbitMQ AMQP Client | 5.27.1 | RabbitMQ Java客户端 | + +### 5.5 缓存依赖 + +| 依赖 | 版本 | 用途 | +|------|------|------| +| Lettuce | 6.8.2.RELEASE | Redis客户端 | +| Caffeine | 3.2.3 | 本地缓存 | + +### 5.6 工具库依赖 + +| 依赖 | 版本 | 用途 | +|------|------|------| +| Lombok | 1.18.44 | 代码简化 | +| MapStruct | 1.6.3 | 对象映射 | +| FastJSON2 | 2.0.60 | JSON处理 | +| Velocity | 2.4.1 | 模板引擎 | +| Jackson BOM | 3.1.0 | JSON处理 | +| Commons Lang3 | 3.20.0 | 通用工具 | +| Commons IO | 2.21.0 | IO工具 | +| Commons Text | 1.15.0 | 文本处理 | +| AspectJ Weaver | 1.9.25.1 | AOP支持 | + +### 5.7 安全相关依赖 + +| 依赖 | 版本 | 用途 | +|------|------|------| +| BouncyCastle | 1.84 | 加密库 | +| Easy Captcha | 1.6.2 | 验证码 | +| java-jwt | 4.5.1 | JWT令牌 | + +### 5.8 文档相关依赖 + +| 依赖 | 版本 | 用途 | +|------|------|------| +| Knife4j OpenAPI3 | 4.5.0 | API文档增强 | +| Springdoc OpenAPI | 3.0.3 | OpenAPI 3集成 | +| Swagger Annotations | 2.2.48 | API文档注解 | + +### 5.9 任务调度依赖 + +| 依赖 | 版本 | 用途 | +|------|------|------| +| XXL-JOB Core | 3.4.0 | 分布式任务调度 | + +### 5.10 系统监控依赖 + +| 依赖 | 版本 | 用途 | +|------|------|------| +| OSHI Core | 6.11.1 | 系统监控 | +| ip2region | 3.3.7 | IP地址定位 | + +### 5.11 存储依赖 + +| 依赖 | 版本 | 用途 | +|------|------|------| +| AWS S3 SDK | 2.42.34 | S3对象存储 | + +### 5.12 办公工具依赖 + +| 依赖 | 版本 | 用途 | +|------|------|------| +| Fesod Sheet | 2.0.1-incubating | Excel处理 | +| PDFBox | 3.0.7 | PDF处理 | + +### 5.13 其他工具依赖 + +| 依赖 | 版本 | 用途 | +|------|------|------| +| UserAgentUtils | 1.21 | 浏览器标识解析 | +| Pinyin4j | 2.5.1 | 拼音转换 | +| Thumbnailator | 0.4.21 | 图片压缩 | +| mmseg4j-core | 1.10.0 | 中文分词 | +| ZXing | 3.5.4 | 二维码/条形码 | +| Jsoup | 1.22.1 | HTML解析 | +| Hibernate Validator | 9.0.1.Final | 参数校验 | +| Netty | 4.2.12.Final | 网络通信 | + +### 5.14 测试依赖 + +| 依赖 | 版本 | 用途 | +|------|------|------| +| JUnit Jupiter | 6.0.3 | 单元测试 | + +### 5.15 xtools内部框架依赖 + +| 模块 | 版本 | 用途 | +|------|------|------| +| xtools-core | 5.0.0 | 核心工具库 | +| xtools-web | 5.0.0 | Web工具库 | +| xtools-extend | 5.0.0 | 扩展工具库 | +| xtools-api | 5.0.0 | API定义库 | +| xtools-parent-boot | 5.0.0 | Boot父POM | +| xtools-parent | 5.0.0 | 基础父POM | +| xtools-dependencies | 5.0.0 | 依赖管理POM | + +## 六、技术选型说明 + +### 6.1 JDK 25 + +- **选择原因**:采用最新JDK版本,充分利用Java新特性 +- **关键特性**: + - **虚拟线程(Virtual Threads)**:轻量级线程,大幅提升高并发场景下的吞吐量,项目在消息队列消费和异步任务中使用虚拟线程 + - **结构化并发(Structured Concurrency)**:简化多线程编程模型 + - **作用域值(Scoped Values)**:替代ThreadLocal,更安全地在线程间共享数据 + - **ZGC/Shenandoah GC**:低延迟垃圾回收器,适合微服务场景 + - **Record Classes**:简化不可变数据类的定义 + - **Pattern Matching**:增强的模式匹配,简化类型判断 + - **Sealed Classes**:密封类,增强类型安全 + +### 6.2 Spring Boot 4.0.5 + +- **Spring Framework版本**:7.0.6 +- **Spring Security版本**:7.0.4 +- **主要特性**: + - 全面支持JDK 17+,推荐使用JDK 25 + - 原生支持虚拟线程,通过配置即可启用 + - 支持Jakarta EE 11规范 + - 改进的可观测性(Observability),集成Micrometer + - 优化启动时间和内存占用 + - 支持GraalVM Native Image + - 增强的自动配置机制 + +### 6.3 MyBatis 4.0.1 + MyBatis-Plus 3.5.16 + +- **选择原因**:MyBatis提供灵活的SQL控制,MyBatis-Plus简化CRUD操作 +- **主要特性**: + - MyBatis 4.0.1 提供基础的SQL映射和参数化查询 + - MyBatis-Plus 3.5.16 提供通用Mapper、分页插件、代码生成器 + - 内置分页插件,简化分页查询 + - 条件构造器,简化复杂查询 + - 支持Lambda表达式查询 + +### 6.4 Elasticsearch 9.2.6 + +- **Spring Data Elasticsearch版本**:6.0.4 +- **主要特性**: + - 全文搜索,支持中文分词(mmseg4j-core 1.10.0) + - 高性能分布式搜索和分析引擎 + - 支持复杂的聚合查询 + - 近实时搜索 + - 集群健康监控 + +### 6.5 Redis + +- **Spring Data Redis版本**:4.0.4 +- **Lettuce客户端版本**:6.8.2.RELEASE +- **主要特性**: + - 高性能内存缓存 + - 丰富的数据结构(String、Hash、List、Set、ZSet) + - 支持分布式锁 + - Lettuce基于Netty的异步非阻塞客户端 + - 支持集群模式和哨兵模式 + +### 6.6 RabbitMQ + +- **Spring AMQP版本**:4.0.2 +- **RabbitMQ AMQP Client版本**:5.27.1 +- **主要特性**: + - 可靠的消息传递机制 + - 灵活的路由规则 + - 支持消息确认和重试 + - 高可用集群支持 + - 与Spring Boot深度集成 + +### 6.7 XXL-JOB 3.4.0 + +- **选择原因**:轻量级分布式任务调度平台 +- **主要特性**: + - 可视化任务管理界面 + - 支持多种任务类型(Bean、GLUE等) + - 弹性扩容 + - 失败重试和告警 + - 任务分片广播 + +### 6.8 其他重要依赖版本 + +| 依赖 | 版本 | 用途 | +|------|------|------| +| FastJSON2 | 2.0.60 | 高性能JSON序列化 | +| Lombok | 1.18.44 | 代码简化 | +| MapStruct | 1.6.3 | 编译期对象映射 | +| Velocity | 2.4.1 | 模板引擎(代码生成) | +| Knife4j | 4.5.0 | API文档增强 | +| BouncyCastle | 1.84 | 国密算法支持 | +| OSHI | 6.11.1 | 系统信息采集 | +| ip2region | 3.3.7 | 离线IP定位 | +| Druid | 1.2.28 | 数据库连接池监控 | +| AWS S3 SDK | 2.42.34 | S3对象存储 | +| Caffeine | 3.2.3 | 高性能本地缓存 | +| Jackson | 3.1.0 | JSON处理 | + +--- + +**文档版本**:v1.0 +**编写日期**:2026-04-16 +**项目版本**:5.0.0 +**父POM版本**:xtools-parent-boot:5.0.0 +**JDK版本**:25 +**维护团队**:xujun.org diff --git a/pom.xml b/pom.xml new file mode 100644 index 0000000..fcb7e3d --- /dev/null +++ b/pom.xml @@ -0,0 +1,44 @@ + + 4.0.0 + xtools-boot + pom + xtools-boot + 低调大师工具箱,SpringBoot工具模块,适配JDK25 + + + + org.xujun + xtools-parent-boot + 5.0.0 + + + + + + xtools-boot-api + xtools-boot-core + + xtools-boot-cache + xtools-boot-db + xtools-boot-elasticsearch + xtools-boot-ip + xtools-boot-job + xtools-boot-knife4j + xtools-boot-log + xtools-boot-mask + xtools-boot-mq + xtools-boot-storage + xtools-boot-task + xtools-boot-thread + xtools-boot-web + + + + + + 25 + + + \ No newline at end of file diff --git a/xtools-boot-api/pom.xml b/xtools-boot-api/pom.xml new file mode 100644 index 0000000..11f2907 --- /dev/null +++ b/xtools-boot-api/pom.xml @@ -0,0 +1,27 @@ + + + 4.0.0 + + org.xujun + xtools-boot + 5.0.0 + + xtools-boot-api + + + + + + jakarta.validation + jakarta.validation-api + + + + io.swagger.core.v3 + swagger-annotations + + + + \ No newline at end of file diff --git a/xtools-boot-api/src/main/java/xtools/boot/api/anntation/IgnoreXss.java b/xtools-boot-api/src/main/java/xtools/boot/api/anntation/IgnoreXss.java new file mode 100644 index 0000000..7b51f8a --- /dev/null +++ b/xtools-boot-api/src/main/java/xtools/boot/api/anntation/IgnoreXss.java @@ -0,0 +1,22 @@ +package xtools.boot.api.anntation; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + *

Title : IgnoreXss

+ *

Description : IgnoreXss

+ *

DevelopTools : Idea_x64_v2026.1

+ *

DevelopSystem : macOS Sequoia 15.7.5

+ *

Company : org.xujun

+ * + * @author : XuJun + * @version : 5.0.0 + * @date : 2026/1/9 10:07 + */ +@Target({ElementType.FIELD}) +@Retention(RetentionPolicy.RUNTIME) +public @interface IgnoreXss { +} diff --git a/xtools-boot-api/src/main/java/xtools/boot/api/constant/BootCommonConstant.java b/xtools-boot-api/src/main/java/xtools/boot/api/constant/BootCommonConstant.java new file mode 100644 index 0000000..1e435ca --- /dev/null +++ b/xtools-boot-api/src/main/java/xtools/boot/api/constant/BootCommonConstant.java @@ -0,0 +1,46 @@ +package xtools.boot.api.constant; + +/** + *

Title : BootCommonConstant

+ *

Description : BootCommonConstant

+ *

DevelopTools : Idea_x64_v2026.1

+ *

DevelopSystem : macOS Sequoia 15.7.5

+ *

Company : org.xujun

+ * + * @author : XuJun + * @version : 5.0.0 + * @date : 2026/2/27 15:53 + */ +public interface BootCommonConstant { + + /** + * 认证 + */ + String AUTHORIZATION = "Authorization"; + + /** + * 日志追踪 + */ + String LOG_TRACK = "x-log-track"; + + /** + * 微服务 + */ + String CLOUD = "x-cloud"; + + /** + * 微服务Token + */ + String CLOUD_TOKEN = "x-cloud-token"; + + /** + * 用户ID + */ + String UID = "x-uid"; + + /** + * 认证信息 + */ + String AUTH = "x-auth"; + +} diff --git a/xtools-boot-api/src/main/java/xtools/boot/api/enums/BaseEnum.java b/xtools-boot-api/src/main/java/xtools/boot/api/enums/BaseEnum.java new file mode 100644 index 0000000..d3e6c1b --- /dev/null +++ b/xtools-boot-api/src/main/java/xtools/boot/api/enums/BaseEnum.java @@ -0,0 +1,39 @@ +package xtools.boot.api.enums; + +/** + *

Title : BaseEnum

+ *

Description : BaseEnum

+ *

DevelopTools : Idea_x64_v2026.1

+ *

DevelopSystem : macOS Sequoia 15.7.5

+ *

Company : org.xujun

+ * + * @author : XuJun + * @version : 5.0.0 + * @date : 2026/2/3 10:31 + */ +public interface BaseEnum { + + /** + * 获取所有枚举 + * + * @return 所有枚举 + */ + default BaseEnum[] all() { + return null; + } + + /** + * 获取枚举编码 + * + * @return 枚举编码 + */ + int code(); + + /** + * 获取枚举说明 + * + * @return 枚举说明 + */ + String desc(); + +} diff --git a/xtools-boot-api/src/main/java/xtools/boot/api/enums/BootError.java b/xtools-boot-api/src/main/java/xtools/boot/api/enums/BootError.java new file mode 100644 index 0000000..74a18c5 --- /dev/null +++ b/xtools-boot-api/src/main/java/xtools/boot/api/enums/BootError.java @@ -0,0 +1,122 @@ +package xtools.boot.api.enums; + +import xtools.base.exception.BaseError; +import xtools.base.exception.BaseErrorModule; + +/** + *

Title : BootError

+ *

Description : BootError

+ *

DevelopTools : Idea_x64_v2026.1

+ *

DevelopSystem : macOS Sequoia 15.7.5

+ *

Company : org.xujun

+ * + * @author : XuJun + * @version : 5.0.0 + * @date : 2025/12/23 16:53 + */ +public enum BootError implements BaseError { + + /* api begin */ + // 认证异常 + UNAUTHORIZED_ERROR(401, "UNAUTHORIZED_ERROR", BootErrorModule.BOOT_API, "{}"), + // 公钥错误 + BIZ_PUBLIC_KEY_ERROR(9990, "BIZ_PUBLIC_KEY_ERROR", BootErrorModule.BOOT_API, "{}"), + // 业务警告 + BIZ_WARNING(9998, "BIZ_WARNING", BootErrorModule.BOOT_API, "{}"), + // 业务异常 + BIZ_ERROR(9999, "BIZ_ERROR", BootErrorModule.BOOT_API, "{}"), + // 结果码异常 + RESULT_CODE(10000, "RESULT_CODE", BootErrorModule.BOOT_API, "结果码异常,业务错误码必须大于1000,code:{}"), + /* api end */ + + /* core begin */ + // Spring 核心工具异常 + SPRING_BASE(10001, "SPRING_BASE", BootErrorModule.BOOT_CORE, "Spring基本工具异常,操作类型:[{}]"), + // 时间转换异常 + TIME_CONVERT(10002, "TIME", BootErrorModule.BOOT_CORE, "时间转换异常,时间:{}"), + /* core end */ + + /* log begin */ + // 日志Holder异常 + LOG_HOLDER(10100, "LOG", BootErrorModule.BOOT_LOG, "日志 Holder 异常"), + /* log end */ + + /* mq begin */ + MQ(10200, "MQ", BootErrorModule.BOOT_MQ, "MQ异常,错误:{}"), + /* mq end */; + + /** + * 错误码 + **/ + private final int code; + + /** + * 错误类型 + **/ + private final String type; + + /** + * 错误模块 + **/ + private final BaseErrorModule module; + + /** + * 错误消息模板 + **/ + private final String msgTmp; + + /** + * 构造方法 + * + * @param code 错误码 + * @param type 错误类型 + * @param msgTmp 错误消息模板 + */ + BootError(int code, String type, BaseErrorModule module, String msgTmp) { + this.code = code; + this.type = type; + this.module = module; + this.msgTmp = msgTmp; + } + + /** + * 错误码 + * + * @return 错误码 + */ + @Override + public int code() { + return this.code; + } + + /** + * 错误类型 + * + * @return 错误类型 + */ + @Override + public String type() { + return this.type; + } + + /** + * 错误模块 + * + * @return 错误模块 + */ + @Override + public BaseErrorModule module() { + return this.module; + } + + /** + * 错误消息模板 + * + * @return 错误消息模板 + */ + @Override + public String msgTmp() { + return this.msgTmp; + } + +} diff --git a/xtools-boot-api/src/main/java/xtools/boot/api/enums/BootErrorModule.java b/xtools-boot-api/src/main/java/xtools/boot/api/enums/BootErrorModule.java new file mode 100644 index 0000000..a1d6d8d --- /dev/null +++ b/xtools-boot-api/src/main/java/xtools/boot/api/enums/BootErrorModule.java @@ -0,0 +1,51 @@ +package xtools.boot.api.enums; + +import xtools.base.exception.BaseErrorModule; + +/** + *

Title : BootErrorModule

+ *

Description : BootErrorModule

+ *

DevelopTools : Idea_x64_v2026.1

+ *

DevelopSystem : macOS Sequoia 15.7.5

+ *

Company : org.xujun

+ * + * @author : XuJun + * @version : 5.0.0 + * @date : 2025/12/23 16:53 + */ +public enum BootErrorModule implements BaseErrorModule { + // API模块 + BOOT_API("xtools-boot-api"), + // 核心模块 + BOOT_CORE("xtools-boot-core"), + // 日志模块 + BOOT_LOG("xtools-boot-log"), + // MQ模块 + BOOT_MQ("xtools-boot-mq"), + ; + + /** + * 错误模块 + **/ + private final String module; + + /** + * 构造方法 + * + * @param module 错误模块 + */ + BootErrorModule(String module) { + this.module = module; + } + + /** + * 错误模块 + * + * @return 错误模块 + */ + @Override + public String module() { + return this.module; + } + +} diff --git a/xtools-boot-api/src/main/java/xtools/boot/api/enums/DeleteEnum.java b/xtools-boot-api/src/main/java/xtools/boot/api/enums/DeleteEnum.java new file mode 100644 index 0000000..4474b99 --- /dev/null +++ b/xtools-boot-api/src/main/java/xtools/boot/api/enums/DeleteEnum.java @@ -0,0 +1,105 @@ +package xtools.boot.api.enums; + +import java.util.Objects; + +/** + *

Title : DeleteEnum

+ *

Description : DeleteEnum

+ *

DevelopTools : Idea_x64_v2026.1

+ *

DevelopSystem : macOS Sequoia 15.7.5

+ *

Company : org.xujun

+ * + * @author : XuJun + * @version : 5.0.0 + * @date : 2026/2/20 11:08 + */ +public enum DeleteEnum implements BaseEnum { + + // 正常 + NORMAL(0, "正常"), + // 删除 + DELETE(1, "删除"), + ; + + /** + * 编码 + */ + private final int code; + + /** + * 说明 + */ + private final String desc; + + /** + * 构造函数 + * + * @param code 编码 + * @param desc 说明 + */ + DeleteEnum(int code, String desc) { + this.code = code; + this.desc = desc; + } + + /** + * 判断枚举值类型 + * + * @param code 枚举值 + * @return 枚举值类型 + */ + public static DeleteEnum valueOf(int code) { + for (DeleteEnum type : values()) { + if (type.code == code) { + return type; + } + } + throw new IllegalArgumentException("unknown code, code=" + code); + } + + /** + * 获取枚举值类型 + * + * @param desc 枚举说明 + * @return 枚举值类型 + */ + public static DeleteEnum valueOfDesc(String desc) { + for (DeleteEnum type : values()) { + if (Objects.equals(type.desc, desc)) { + return type; + } + } + throw new IllegalArgumentException("unknown desc, desc=" + desc); + } + + /** + * 获取所有枚举 + * + * @return 所有枚举 + */ + @Override + public BaseEnum[] all() { + return values(); + } + + /** + * 获取枚举编码 + * + * @return 枚举编码 + */ + @Override + public int code() { + return code; + } + + /** + * 获取枚举说明 + * + * @return 枚举说明 + */ + @Override + public String desc() { + return desc; + } + +} diff --git a/xtools-boot-api/src/main/java/xtools/boot/api/enums/FileDataType.java b/xtools-boot-api/src/main/java/xtools/boot/api/enums/FileDataType.java new file mode 100644 index 0000000..52a5f68 --- /dev/null +++ b/xtools-boot-api/src/main/java/xtools/boot/api/enums/FileDataType.java @@ -0,0 +1,73 @@ +package xtools.boot.api.enums; + +/** + *

Title : FileDataType

+ *

Description : FileDataType

+ *

DevelopTools : Idea_x64_v2026.1

+ *

DevelopSystem : macOS Sequoia 15.7.5

+ *

Company : org.xujun

+ * + * @author : XuJun + * @version : 5.0.0 + * @date : 2026/1/6 16:44 + */ +public enum FileDataType implements BaseEnum { + + // 正常 + OK(0, "正常"), + // 临时 + TEMP(1, "临时"), + // 待删除 + DELETE(2, "待删除"); + + /** + * 编码 + */ + private final int code; + + /** + * 说明 + */ + private final String desc; + + /** + * 构造函数 + * + * @param code 编码 + * @param desc 说明 + */ + FileDataType(int code, String desc) { + this.code = code; + this.desc = desc; + } + + /** + * 获取所有枚举 + * + * @return 所有枚举 + */ + @Override + public BaseEnum[] all() { + return values(); + } + + /** + * 获取枚举编码 + * + * @return 枚举编码 + */ + @Override + public int code() { + return code; + } + + /** + * 获取枚举说明 + * + * @return 枚举说明 + */ + @Override + public String desc() { + return desc; + } +} \ No newline at end of file diff --git a/xtools-boot-api/src/main/java/xtools/boot/api/enums/ResultType.java b/xtools-boot-api/src/main/java/xtools/boot/api/enums/ResultType.java new file mode 100644 index 0000000..5418478 --- /dev/null +++ b/xtools-boot-api/src/main/java/xtools/boot/api/enums/ResultType.java @@ -0,0 +1,78 @@ +package xtools.boot.api.enums; + +/** + *

Title : ResultType

+ *

Description : ResultType

+ *

DevelopTools : Idea_x64_v2026.1

+ *

DevelopSystem : macOS Sequoia 15.7.5

+ *

Company : org.xujun

+ * + * @author : XuJun + * @version : 5.0.0 + * @date : 2026/1/5 14:10 + */ +public enum ResultType { + + + // 请求成功 + OK(true, 200, "ok", "请求成功"), + // 服务器内部错误 + INTERNAL_SERVER_ERROR(false, 500, "internal_server_error", "服务器内部错误"), + + // 请求参数缺失 | 参数格式错误(如:期望数字却传了字符串) | 参数类型不匹配 | JSON/XML格式错误 | 必填参数为空 + BAD_REQUEST(false, 400, "bad_request", "请求参数缺失 | 参数格式错误(如:期望数字却传了字符串) | 参数类型不匹配 | JSON/XML格式错误 | 必填参数为空"), + // 认证失败 + UNAUTHORIZED(false, 401, "unauthorized", "认证失败"), + // 权限不足 + FORBIDDEN(false, 403, "forbidden", "权限不足"), + // 资源不存在 + NOT_FOUND(false, 404, "not_found", "资源不存在"), + // 请求方法不被允许 + METHOD_NOT_ALLOWED(false, 405, "method_not_allowed", "请求方法不被允许"), + // 请求过于频繁 + TOO_MANY_REQUESTS(false, 429, "too_many_requests", "请求过于频繁"), + ; + + /** + * 是否成功 + */ + private final boolean success; + + /** + * 响应码 + */ + private final int code; + + /** + * 响应信息 + */ + private final String msg; + + /** + * 响应描述 + */ + private final String desc; + + ResultType(boolean success, int code, String msg, String desc) { + this.success = success; + this.code = code; + this.msg = msg; + this.desc = desc; + } + + public boolean success() { + return success; + } + + public int code() { + return code; + } + + public String msg() { + return msg; + } + + public String desc() { + return desc; + } +} diff --git a/xtools-boot-api/src/main/java/xtools/boot/api/enums/StatusEnum.java b/xtools-boot-api/src/main/java/xtools/boot/api/enums/StatusEnum.java new file mode 100644 index 0000000..d179d8d --- /dev/null +++ b/xtools-boot-api/src/main/java/xtools/boot/api/enums/StatusEnum.java @@ -0,0 +1,105 @@ +package xtools.boot.api.enums; + +import java.util.Objects; + +/** + *

Title : StatusEnum

+ *

Description : StatusEnum

+ *

DevelopTools : Idea_x64_v2026.1

+ *

DevelopSystem : macOS Sequoia 15.7.5

+ *

Company : org.xujun

+ * + * @author : XuJun + * @version : 5.0.0 + * @date : 2026/2/20 11:08 + */ +public enum StatusEnum implements BaseEnum { + + // 正常 + NORMAL(1, "正常"), + // 停用 + DISABLED(0, "停用"), + ; + + /** + * 编码 + */ + private final int code; + + /** + * 说明 + */ + private final String desc; + + /** + * 构造函数 + * + * @param code 编码 + * @param desc 说明 + */ + StatusEnum(int code, String desc) { + this.code = code; + this.desc = desc; + } + + /** + * 判断枚举值类型 + * + * @param code 枚举值 + * @return 枚举值类型 + */ + public static StatusEnum valueOf(int code) { + for (StatusEnum type : values()) { + if (type.code == code) { + return type; + } + } + throw new IllegalArgumentException("unknown code, code=" + code); + } + + /** + * 获取枚举值类型 + * + * @param desc 枚举说明 + * @return 枚举值类型 + */ + public static StatusEnum valueOfDesc(String desc) { + for (StatusEnum type : values()) { + if (Objects.equals(type.desc, desc)) { + return type; + } + } + throw new IllegalArgumentException("unknown desc, desc=" + desc); + } + + /** + * 获取所有枚举 + * + * @return 所有枚举 + */ + @Override + public BaseEnum[] all() { + return values(); + } + + /** + * 获取枚举编码 + * + * @return 枚举编码 + */ + @Override + public int code() { + return code; + } + + /** + * 获取枚举说明 + * + * @return 枚举说明 + */ + @Override + public String desc() { + return desc; + } + +} diff --git a/xtools-boot-api/src/main/java/xtools/boot/api/enums/ThreadType.java b/xtools-boot-api/src/main/java/xtools/boot/api/enums/ThreadType.java new file mode 100644 index 0000000..e8904c4 --- /dev/null +++ b/xtools-boot-api/src/main/java/xtools/boot/api/enums/ThreadType.java @@ -0,0 +1,71 @@ +package xtools.boot.api.enums; + +/** + *

Title : ThreadType

+ *

Description : ThreadType

+ *

DevelopTools : Idea_x64_v2026.1

+ *

DevelopSystem : macOS Sequoia 15.7.5

+ *

Company : org.xujun

+ * + * @author : XuJun + * @version : 5.0.0 + * @date : 2026/1/6 16:44 + */ +public enum ThreadType implements BaseEnum { + + // 主线程 + MAIN(0, "主线程"), + // 子线程 + THREAD(1, "子线程"); + + /** + * 编码 + */ + private final int code; + + /** + * 说明 + */ + private final String desc; + + /** + * 构造函数 + * + * @param code 编码 + * @param desc 说明 + */ + ThreadType(int code, String desc) { + this.code = code; + this.desc = desc; + } + + /** + * 获取所有枚举 + * + * @return 所有枚举 + */ + @Override + public BaseEnum[] all() { + return values(); + } + + /** + * 获取枚举编码 + * + * @return 枚举编码 + */ + @Override + public int code() { + return code; + } + + /** + * 获取枚举说明 + * + * @return 枚举说明 + */ + @Override + public String desc() { + return desc; + } +} \ No newline at end of file diff --git a/xtools-boot-api/src/main/java/xtools/boot/api/exection/BizError.java b/xtools-boot-api/src/main/java/xtools/boot/api/exection/BizError.java new file mode 100644 index 0000000..a52c7a4 --- /dev/null +++ b/xtools-boot-api/src/main/java/xtools/boot/api/exection/BizError.java @@ -0,0 +1,27 @@ +package xtools.boot.api.exection; + +import xtools.base.exception.CommonException; +import xtools.boot.api.enums.BootError; + +/** + *

Title : BizError

+ *

Description : BizError

+ *

DevelopTools : Idea_x64_v2026.1

+ *

DevelopSystem : macOS Sequoia 15.7.5

+ *

Company : org.xujun

+ * + * @author : XuJun + * @version : 5.0.0 + * @date : 2026/1/29 11:08 + */ +public class BizError extends CommonException { + + /** + * 构造方法 + * + * @param message 错误消息 + */ + public BizError(String message) { + super(BootError.BIZ_ERROR, message); + } +} diff --git a/xtools-boot-api/src/main/java/xtools/boot/api/exection/BizPublicKeyError.java b/xtools-boot-api/src/main/java/xtools/boot/api/exection/BizPublicKeyError.java new file mode 100644 index 0000000..43f8c38 --- /dev/null +++ b/xtools-boot-api/src/main/java/xtools/boot/api/exection/BizPublicKeyError.java @@ -0,0 +1,27 @@ +package xtools.boot.api.exection; + +import xtools.base.exception.CommonException; +import xtools.boot.api.enums.BootError; + +/** + *

Title : BizPublicKeyError

+ *

Description : BizPublicKeyError

+ *

DevelopTools : Idea_x64_v2026.1

+ *

DevelopSystem : macOS Sequoia 15.7.5

+ *

Company : org.xujun

+ * + * @author : XuJun + * @version : 5.0.0 + * @date : 2026/1/29 11:08 + */ +public class BizPublicKeyError extends CommonException { + + /** + * 构造方法 + * + * @param message 错误消息 + */ + public BizPublicKeyError(String message) { + super(BootError.BIZ_PUBLIC_KEY_ERROR, message); + } +} diff --git a/xtools-boot-api/src/main/java/xtools/boot/api/exection/BizWarning.java b/xtools-boot-api/src/main/java/xtools/boot/api/exection/BizWarning.java new file mode 100644 index 0000000..2706a7b --- /dev/null +++ b/xtools-boot-api/src/main/java/xtools/boot/api/exection/BizWarning.java @@ -0,0 +1,27 @@ +package xtools.boot.api.exection; + +import xtools.base.exception.CommonException; +import xtools.boot.api.enums.BootError; + +/** + *

Title : BizWarning

+ *

Description : BizWarning

+ *

DevelopTools : Idea_x64_v2026.1

+ *

DevelopSystem : macOS Sequoia 15.7.5

+ *

Company : org.xujun

+ * + * @author : XuJun + * @version : 5.0.0 + * @date : 2026/1/29 11:08 + */ +public class BizWarning extends CommonException { + + /** + * 构造方法 + * + * @param message 错误消息 + */ + public BizWarning(String message) { + super(BootError.BIZ_WARNING, message); + } +} diff --git a/xtools-boot-api/src/main/java/xtools/boot/api/exection/UnauthorizedError.java b/xtools-boot-api/src/main/java/xtools/boot/api/exection/UnauthorizedError.java new file mode 100644 index 0000000..ee002b7 --- /dev/null +++ b/xtools-boot-api/src/main/java/xtools/boot/api/exection/UnauthorizedError.java @@ -0,0 +1,25 @@ +package xtools.boot.api.exection; + +import xtools.base.exception.CommonException; +import xtools.boot.api.enums.BootError; + +/** + *

Title : UnauthorizedError

+ *

Description : UnauthorizedError

+ *

DevelopTools : Idea_x64_v2026.1

+ *

DevelopSystem : macOS Sequoia 15.7.5

+ *

Company : org.xujun

+ * + * @author : XuJun + * @version : 5.0.0 + * @date : 2026/1/29 11:08 + */ +public class UnauthorizedError extends CommonException { + + /** + * 构造方法 + */ + public UnauthorizedError() { + super(BootError.UNAUTHORIZED_ERROR, "认证异常"); + } +} diff --git a/xtools-boot-api/src/main/java/xtools/boot/api/model/EnumInfoDto.java b/xtools-boot-api/src/main/java/xtools/boot/api/model/EnumInfoDto.java new file mode 100644 index 0000000..8281f81 --- /dev/null +++ b/xtools-boot-api/src/main/java/xtools/boot/api/model/EnumInfoDto.java @@ -0,0 +1,34 @@ +package xtools.boot.api.model; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.io.Serializable; + +/** + *

Title : EnumInfoDto

+ *

Description : EnumInfoDto

+ *

DevelopTools : Idea_x64_v2026.1

+ *

DevelopSystem : macOS Sequoia 15.7.5

+ *

Company : org.xujun

+ * + * @author : XuJun + * @version : 5.0.0 + * @date : 2026/2/14 16:30 + */ +@Data +@NoArgsConstructor +@AllArgsConstructor +public class EnumInfoDto implements Serializable { + + /** + * 枚举值 + */ + private int code; + + /** + * 枚举说明 + */ + private String desc; +} diff --git a/xtools-boot-api/src/main/java/xtools/boot/api/model/dto/Result.java b/xtools-boot-api/src/main/java/xtools/boot/api/model/dto/Result.java new file mode 100644 index 0000000..a4cf55d --- /dev/null +++ b/xtools-boot-api/src/main/java/xtools/boot/api/model/dto/Result.java @@ -0,0 +1,139 @@ +package xtools.boot.api.model.dto; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; +import xtools.base.config.BaseParams; +import xtools.base.exception.CommonException; +import xtools.boot.api.enums.BootError; +import xtools.boot.api.enums.ResultType; +import xtools.boot.api.exection.BizError; + +import java.util.Objects; + +/** + *

Title : Result

+ *

Description : Result

+ *

DevelopTools : Idea_x64_v2026.1

+ *

DevelopSystem : macOS Sequoia 15.7.5

+ *

Company : org.xujun

+ * + * @author : XuJun + * @version : 5.0.0 + * @date : 2025/12/22 14:07 + */ +@Data +public final class Result implements BaseParams { + + @Schema(description = "是否成功") + private boolean success; + + @Schema(description = "响应代码") + private int code; + + @Schema(description = "提示信息") + private Object msg; + + @Schema(description = "响应数据") + private T data; + + /** + * 构造函数 + */ + public Result() { + } + + /** + * 构造函数 + * + * @param success 是否成功 + * @param code 响应代码 + * @param msg 提示信息 + * @param data 响应数据 + */ + public Result(boolean success, int code, Object msg, T data) { + this.success = success; + this.code = code; + this.msg = msg; + this.data = data; + } + + /** + * 构造函数 + * + * @param type 响应类型 + * @param data 数据 + */ + public Result(ResultType type, T data) { + this.success = type.success(); + this.code = type.code(); + this.msg = type.msg() + CP_COMMA + type.desc(); + this.data = data; + } + + /** + * 请求成功 + * + * @return 结果 + */ + public static Result ok() { + return new Result<>(ResultType.OK, null); + } + + /** + * 请求成功 + * + * @param data 数据 + * @param 泛型 + * @return 结果 + */ + public static Result ok(T data) { + return new Result<>(ResultType.OK, data); + } + + /** + * 请求成功,业务失败 + * + * @param code 业务错误码 + * @param msg 业务错误信息 + * @return 响应结果 + */ + public static Result fail(int code, Object msg) { + if (code < CP_NUM1000) { + throw CommonException.create(BootError.RESULT_CODE, code); + } + return new Result<>(true, code, msg, null); + } + + /** + * 请求参数缺失 | 参数格式错误(如:期望数字却传了字符串) | 参数类型不匹配 | JSON/XML格式错误 | 必填参数为空 + * + * @return 响应结果 + */ + public static Result badRequest() { + return new Result<>(ResultType.BAD_REQUEST, null); + } + + /** + * 请求参数缺失 | 参数格式错误(如:期望数字却传了字符串) | 参数类型不匹配 | JSON/XML格式错误 | 必填参数为空 + * + * @param msg 错误信息 + * @return 响应结果 + */ + public static Result badRequest(Object msg) { + ResultType type = ResultType.BAD_REQUEST; + return new Result<>(type.success(), type.code(), msg, null); + } + + /** + * 获取数据 + * + * @return 数据 + */ + public T data() { + if (Objects.equals(success, false) || !Objects.equals(code, CP_NUM200)) { + throw new BizError(Objects.isNull(msg) ? "业务处理失败" : String.valueOf(msg)); + } + return data; + } + +} \ No newline at end of file diff --git a/xtools-boot-api/src/main/java/xtools/boot/api/model/dto/log/HolderLogTrack.java b/xtools-boot-api/src/main/java/xtools/boot/api/model/dto/log/HolderLogTrack.java new file mode 100644 index 0000000..304a611 --- /dev/null +++ b/xtools-boot-api/src/main/java/xtools/boot/api/model/dto/log/HolderLogTrack.java @@ -0,0 +1,74 @@ +package xtools.boot.api.model.dto.log; + +import lombok.Data; +import xtools.base.config.BaseParams; +import xtools.boot.api.enums.ThreadType; + +import java.util.concurrent.atomic.AtomicInteger; + +/** + *

Title : HolderLogTrack

+ *

Description : HolderLogTrack

+ *

DevelopTools : Idea_x64_v2026.1

+ *

DevelopSystem : macOS Sequoia 15.7.5

+ *

Company : org.xujun

+ * + * @author : XuJun + * @version : 5.0.0 + * @date : 2026/1/6 16:44 + */ +@Data +public class HolderLogTrack implements BaseParams { + + /** + * 日志追踪 ID + */ + private final String traceId; + /** + * 父线日志 ID + */ + private final String parentId; + /** + * 线程类型 + */ + private final ThreadType type; + /** + * 日志获取次数 + */ + private final AtomicInteger count; + /** + * 是否保存日志 + */ + private boolean save; + + /** + * 构造方法 + * + * @param traceId 日志追踪 ID + * @param parentId 父日志 ID + * @param type 线程类型 + */ + public HolderLogTrack(String traceId, String parentId, ThreadType type) { + this.save = true; + this.traceId = traceId; + this.parentId = parentId; + this.type = type; + this.count = new AtomicInteger(CP_NUM0); + } + + /** + * 构造方法 + * + * @param traceId 日志追踪 ID + * @param parentId 父日志 ID + * @param type 线程类型 + * @param count 日志获取次数 + */ + public HolderLogTrack(String traceId, String parentId, ThreadType type, int count) { + this.save = true; + this.traceId = traceId; + this.parentId = parentId; + this.type = type; + this.count = new AtomicInteger(count); + } +} diff --git a/xtools-boot-api/src/main/java/xtools/boot/api/model/dto/log/LogTrack.java b/xtools-boot-api/src/main/java/xtools/boot/api/model/dto/log/LogTrack.java new file mode 100644 index 0000000..82e4a46 --- /dev/null +++ b/xtools-boot-api/src/main/java/xtools/boot/api/model/dto/log/LogTrack.java @@ -0,0 +1,25 @@ +package xtools.boot.api.model.dto.log; + +import xtools.boot.api.enums.ThreadType; + +/** + *

Title : LogTrack

+ *

Description : LogTrack

+ *

DevelopTools : Idea_x64_v2026.1

+ *

DevelopSystem : macOS Sequoia 15.7.5

+ *

Company : org.xujun

+ * + * @param id 日志 ID + * @param time 日志时间 + * @param traceId 日志追踪 ID + * @param parentId 父线日志 ID + * @param type 线程类型 + * @param index 日志获取次数 + * @param save 是否保存日志 + * @author : XuJun + * @version : 5.0.0 + * @date : 2026/1/6 16:44 + */ +public record LogTrack(String id, Long time, String traceId, String parentId, ThreadType type, Integer index, + boolean save) { +} diff --git a/xtools-boot-api/src/main/java/xtools/boot/api/model/dto/page/PageReq.java b/xtools-boot-api/src/main/java/xtools/boot/api/model/dto/page/PageReq.java new file mode 100644 index 0000000..40799ea --- /dev/null +++ b/xtools-boot-api/src/main/java/xtools/boot/api/model/dto/page/PageReq.java @@ -0,0 +1,43 @@ +package xtools.boot.api.model.dto.page; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.io.Serializable; + +/** + *

Title : PageReq

+ *

Description : PageReq

+ *

DevelopTools : Idea_x64_v2026.1

+ *

DevelopSystem : macOS Sequoia 15.7.5

+ *

Company : org.xujun

+ * + * @author : XuJun + * @version : 5.0.0 + * @date : 2025/12/22 14:58 + */ +@Data +@NoArgsConstructor +@AllArgsConstructor +public class PageReq implements Serializable { + + /** + * 当前页面 + */ + @Schema(description = "当前页面", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") + private Integer currentPage; + + /** + * 页面数据条数 + */ + @Schema(description = "页面数据条数", requiredMode = Schema.RequiredMode.REQUIRED, example = "10") + private Integer pageSize; + + /** + * 查询条件 + */ + @Schema(description = "查询条件") + private T query; +} diff --git a/xtools-boot-api/src/main/java/xtools/boot/api/model/dto/page/PageResp.java b/xtools-boot-api/src/main/java/xtools/boot/api/model/dto/page/PageResp.java new file mode 100644 index 0000000..08b0c11 --- /dev/null +++ b/xtools-boot-api/src/main/java/xtools/boot/api/model/dto/page/PageResp.java @@ -0,0 +1,86 @@ +package xtools.boot.api.model.dto.page; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; +import lombok.NoArgsConstructor; +import xtools.core.extend.PagingUtils; + +import java.io.Serializable; +import java.util.List; + +/** + *

Title : PageResp

+ *

Description : PageResp

+ *

DevelopTools : Idea_x64_v2026.1

+ *

DevelopSystem : macOS Sequoia 15.7.5

+ *

Company : org.xujun

+ * + * @author : XuJun + * @version : 5.0.0 + * @date : 2025/12/22 14:58 + */ +@Data +@NoArgsConstructor +public final class PageResp implements Serializable { + + /** + * 当前页面 + */ + @Schema(description = "当前页面") + private Integer currentPage; + + /** + * 页面数据条数 + */ + @Schema(description = "页面数据条数") + private Integer pageSize; + + /** + * 数据条数 + */ + @Schema(description = "数据条数") + private Long total; + + /** + * 总页数 + */ + @Schema(description = "总页数") + private Long pageCount; + + /** + * 结果数据集 + */ + @Schema(description = "结果数据集") + private List data; + + /** + * 构造方法 + * + * @param req 分页请求 + */ + public PageResp(PageReq req) { + this.currentPage = req.getCurrentPage(); + this.pageSize = req.getPageSize(); + } + + /** + * 构造方法 + * + * @param req 分页请求 + */ + public PageResp(PageReq req, Long total, List data) { + this.currentPage = req.getCurrentPage(); + this.pageSize = req.getPageSize(); + this.total = total; + this.data = data; + } + + /** + * 获取总页数 + * + * @return 总页数 + */ + public Long getPageCount() { + return PagingUtils.getLastPage(this.total, this.pageSize); + } +} diff --git a/xtools-boot-api/src/main/java/xtools/boot/api/model/dto/req/IdListReq.java b/xtools-boot-api/src/main/java/xtools/boot/api/model/dto/req/IdListReq.java new file mode 100644 index 0000000..9c2fe98 --- /dev/null +++ b/xtools-boot-api/src/main/java/xtools/boot/api/model/dto/req/IdListReq.java @@ -0,0 +1,34 @@ +package xtools.boot.api.model.dto.req; + +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.NotNull; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.io.Serializable; +import java.util.List; + +/** + *

Title : IdListReq

+ *

Description : IdListReq

+ *

DevelopTools : Idea_x64_v2026.1

+ *

DevelopSystem : macOS Sequoia 15.7.5

+ *

Company : org.xujun

+ * + * @author : XuJun + * @version : 5.0.0 + * @date : 2026/2/4 11:03 + */ +@Data +@NoArgsConstructor +@AllArgsConstructor +public class IdListReq implements Serializable { + + /** + * idList + */ + @NotNull(message = "不能为空") + @Schema(description = "Id列表") + private List idList; +} diff --git a/xtools-boot-api/src/main/java/xtools/boot/api/model/dto/resp/TreeResp.java b/xtools-boot-api/src/main/java/xtools/boot/api/model/dto/resp/TreeResp.java new file mode 100644 index 0000000..0695b86 --- /dev/null +++ b/xtools-boot-api/src/main/java/xtools/boot/api/model/dto/resp/TreeResp.java @@ -0,0 +1,47 @@ +package xtools.boot.api.model.dto.resp; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.io.Serializable; +import java.util.List; + +/** + *

Title : TreeResp

+ *

Description : TreeResp

+ *

DevelopTools : Idea_x64_v2026.1

+ *

DevelopSystem : macOS Sequoia 15.7.5

+ *

Company : org.xujun

+ * + * @author : XuJun + * @version : 5.0.0 + * @date : 2026/1/30 09:39 + */ +@Data +public class TreeResp implements Serializable { + + /** + * 父id + */ + @Schema(description = "父id", example = "1") + private Long parentId; + + /** + * 值 + */ + @Schema(description = "值", example = "1") + private Long value; + + /** + * 标签 + */ + @Schema(description = "标签", example = "1") + private String label; + + /** + * 子项 + */ + @Schema(description = "子项") + private List children; + +} diff --git a/xtools-boot-api/src/main/java/xtools/boot/api/model/entity/BaseEntity.java b/xtools-boot-api/src/main/java/xtools/boot/api/model/entity/BaseEntity.java new file mode 100644 index 0000000..f12ce3e --- /dev/null +++ b/xtools-boot-api/src/main/java/xtools/boot/api/model/entity/BaseEntity.java @@ -0,0 +1,28 @@ +package xtools.boot.api.model.entity; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.io.Serializable; +import java.time.Instant; + +/** + *

Title : BaseEntity

+ *

Description : BaseEntity

+ *

DevelopTools : Idea_x64_v2026.1

+ *

DevelopSystem : macOS Sequoia 15.7.5

+ *

Company : org.xujun

+ * + * @author : XuJun + * @version : 5.0.0 + * @date : 2026/1/17 18:50 + */ +@Data +public class BaseEntity implements Serializable { + + @Schema(description = "创建时间", example = "2026-01-05 10:32:00") + private Instant gmtCreate; + + @Schema(description = "更新时间", example = "2026-01-05 10:32:00") + private Instant gmtModified; +} \ No newline at end of file diff --git a/xtools-boot-cache/pom.xml b/xtools-boot-cache/pom.xml new file mode 100644 index 0000000..c1e0b57 --- /dev/null +++ b/xtools-boot-cache/pom.xml @@ -0,0 +1,19 @@ + + + 4.0.0 + + org.xujun + xtools-boot + 5.0.0 + + pom + xtools-boot-cache + + + + xtools-boot-cache-redis + + + \ No newline at end of file diff --git a/xtools-boot-cache/xtools-boot-cache-redis/pom.xml b/xtools-boot-cache/xtools-boot-cache-redis/pom.xml new file mode 100644 index 0000000..c4e794f --- /dev/null +++ b/xtools-boot-cache/xtools-boot-cache-redis/pom.xml @@ -0,0 +1,38 @@ + + + 4.0.0 + + org.xujun + xtools-boot-cache + 5.0.0 + + xtools-boot-cache-redis + + + + + + + org.xujun + xtools-boot-core + + + + + + + org.springframework.boot + spring-boot-starter-data-redis + + + + + + com.alibaba.fastjson2 + fastjson2 + + + + \ No newline at end of file diff --git a/xtools-boot-cache/xtools-boot-cache-redis/src/main/java/xtools/boot/cache/redis/BootCacheRedisConfiguration.java b/xtools-boot-cache/xtools-boot-cache-redis/src/main/java/xtools/boot/cache/redis/BootCacheRedisConfiguration.java new file mode 100644 index 0000000..ef11189 --- /dev/null +++ b/xtools-boot-cache/xtools-boot-cache-redis/src/main/java/xtools/boot/cache/redis/BootCacheRedisConfiguration.java @@ -0,0 +1,28 @@ +package xtools.boot.cache.redis; + +import org.springframework.context.annotation.Import; +import xtools.boot.cache.redis.selector.BootCacheRedisImportSelector; +import xtools.boot.core.utils.ModuleLoadUtils; + +/** + *

Title : BootCacheRedisConfiguration

+ *

Description : BootCacheRedisConfiguration

+ *

DevelopTools : Idea_x64_v2026.1

+ *

DevelopSystem : macOS Sequoia 15.7.5

+ *

Company : org.xujun

+ * + * @author : XuJun + * @version : 5.0.0 + * @date : 2026/01/01 09:30 + */ +@Import(BootCacheRedisImportSelector.class) +public class BootCacheRedisConfiguration { + + /** + * 构造方法 + */ + public BootCacheRedisConfiguration() { + ModuleLoadUtils.loadSuccess(BootCacheRedisConfiguration.class); + } + +} diff --git a/xtools-boot-cache/xtools-boot-cache-redis/src/main/java/xtools/boot/cache/redis/base/RedisService.java b/xtools-boot-cache/xtools-boot-cache-redis/src/main/java/xtools/boot/cache/redis/base/RedisService.java new file mode 100644 index 0000000..0373732 --- /dev/null +++ b/xtools-boot-cache/xtools-boot-cache-redis/src/main/java/xtools/boot/cache/redis/base/RedisService.java @@ -0,0 +1,458 @@ +package xtools.boot.cache.redis.base; + +import com.alibaba.fastjson2.JSON; +import jakarta.annotation.Resource; +import org.springframework.data.redis.core.RedisTemplate; +import org.springframework.data.redis.core.StringRedisTemplate; +import org.springframework.data.redis.core.script.DefaultRedisScript; +import org.springframework.stereotype.Component; +import xtools.base.config.BaseParams; +import xtools.core.ArrUtils; +import xtools.core.CollectionUtils; +import xtools.core.StringUtils; + +import java.util.Collections; +import java.util.Map; +import java.util.Objects; +import java.util.Set; +import java.util.StringJoiner; +import java.util.concurrent.TimeUnit; + +/** + *

Title : RedisService

+ *

Description : RedisService

+ *

DevelopTools : Idea_x64_v2026.1

+ *

DevelopSystem : macOS Sequoia 15.7.5

+ *

Company : org.xujun

+ * + * @author : XuJun + * @version : 5.0.0 + * @date : 2026/01/01 09:30 + */ +@Component +public class RedisService implements BaseParams { + + /** + * RedisTemplate + **/ + @Resource + private RedisTemplate redisTemplate; + + /** + * StringRedisTemplate + **/ + @Resource + private StringRedisTemplate stringRedisTemplate; + + /** + * 获取组合 Key + * + * @param keys 多个 Key + * @return 组合 Key + */ + public String getKey(String... keys) { + if (ArrUtils.isEmpty(keys)) { + return null; + } + StringJoiner sj = new StringJoiner(CP_COLON); + for (String key : keys) { + sj.add(key); + } + return sj.toString(); + } + + /** + * 整数 值递增一 + * + * @param key Key + * @return 递增后的值 + */ + public Long incr(String key) { + return stringRedisTemplate.opsForValue().increment(key); + } + + /** + * 保存数据 + * + * @param key Key + * @param data 数据 + */ + public void set(String key, Object data) { + this.set(key, data, null, null); + } + + /** + * 保存数据 + * + * @param key Key + * @param data 数据 + * @param timeout 过期时间 + */ + public void set(String key, Object data, Long timeout) { + this.set(key, data, timeout, TimeUnit.SECONDS); + } + + /** + * 保存数据 + * + * @param key Key + * @param data 数据 + * @param timeout 过期时间 + * @param unit 时间单位 + */ + public void set(String key, Object data, Long timeout, TimeUnit unit) { + // 参数校验 + if (StringUtils.isEmpty(key) || data == null) { + return; + } + // 序列化数据 + String serializeData; + if (data instanceof String) { + serializeData = data.toString(); + } else { + serializeData = JSON.toJSONString(data); + } + if (StringUtils.isEmpty(serializeData)) { + return; + } + if (unit == null) { + unit = TimeUnit.SECONDS; + } + // 判断是否有过期时间 + if (timeout == null || timeout <= 0) { + stringRedisTemplate.opsForValue().set(key, serializeData); + } else { + stringRedisTemplate.opsForValue().set(key, serializeData, timeout, unit); + } + } + + /** + * 保存数据(如果key存在则保存失败) + * + * @param key Key + * @param data 数据 + * @return 保存结果 + */ + public Boolean setNx(String key, Object data) { + return this.setNx(key, data, null, null); + } + + /** + * 保存数据(如果key存在则保存失败) + * + * @param key Key + * @param data 数据 + * @param timeout 有效时间 + * @return 保存结果 + */ + public Boolean setNx(String key, Object data, Long timeout) { + return this.setNx(key, data, timeout, TimeUnit.SECONDS); + } + + /** + * 保存数据(如果key存在则保存失败) + * + * @param key Key + * @param data 数据 + * @param timeout 有效时间 + * @param unit 时间单位 + * @return 保存结果 + */ + public Boolean setNx(String key, Object data, Long timeout, TimeUnit unit) { + // 参数校验 + if (StringUtils.isEmpty(key) || data == null) { + return false; + } + // 序列化数据 + String serializeData; + if (data instanceof String) { + serializeData = data.toString(); + } else { + serializeData = JSON.toJSONString(data); + } + if (StringUtils.isEmpty(serializeData)) { + return false; + } + if (unit == null) { + unit = TimeUnit.SECONDS; + } + // 判断是否有过期时间 + if (timeout == null || timeout <= 0) { + return stringRedisTemplate.opsForValue().setIfAbsent(key, serializeData); + } else { + return stringRedisTemplate.opsForValue().setIfAbsent(key, serializeData, timeout, unit); + } + } + + /** + * 获取数据 + * + * @param Class 类型 + * @param key Key + * @param clazz Class + * @return 数据 + */ + public T get(String key, Class clazz) { + // 参数校验 + if (StringUtils.isEmpty(key)) { + return null; + } + // 从缓存获取数据 + String data = stringRedisTemplate.opsForValue().get(key); + if (StringUtils.isEmpty(data)) { + return null; + } + return JSON.to(clazz, data); + } + + /** + * 删除 key 对应的数据 + * + * @param key Key + * @return 删除结果 + */ + public Boolean del(String key) { + // 参数校验 + if (StringUtils.isEmpty(key)) { + return false; + } + return stringRedisTemplate.delete(key); + } + + /** + * 更改 Key + * + * @param oldKey 旧Key + * @param newKey 新Key + */ + public void rename(String oldKey, String newKey) { + // 参数校验 + if (StringUtils.isEmpty(oldKey) || StringUtils.isEmpty(newKey)) { + return; + } + stringRedisTemplate.rename(oldKey, newKey); + } + + /** + * 设置key的过期时间(秒) + * + * @param key Key + * @param timeout 时间(秒) + * @return 设置结果 + */ + public Boolean expire(String key, long timeout) { + // 参数校验 + if (StringUtils.isEmpty(key) || timeout <= 0) { + return false; + } + return stringRedisTemplate.expire(key, timeout, TimeUnit.SECONDS); + } + + /** + * 取消对 Key 过期时间的设置 + * + * @param key Key + * @return true or false + */ + public Boolean persist(String key) { + // 参数校验 + if (StringUtils.isEmpty(key)) { + return false; + } + return stringRedisTemplate.persist(key); + } + + /** + * 查找所有匹配给定的模式的键 + * + * @param pattern key的表达式,*表示多个,?表示一个 + * @return 全部符合表达式记录 + */ + public Set getByPattern(String pattern) { + // 参数校验 + if (StringUtils.isEmpty(pattern)) { + return null; + } + return stringRedisTemplate.keys(pattern); + } + + /** + * 尝试获取锁 + * + * @param key 锁的key + * @param value 请求标识(可用UUID) + * @param expireTime 过期时间(秒) + * @return 是否获取成功 + */ + public boolean tryLock(String key, String value, long expireTime) { + Boolean result = redisTemplate.opsForValue().setIfAbsent(key, value, expireTime, TimeUnit.SECONDS); + return Boolean.TRUE.equals(result); + } + + /** + * 释放锁(使用Lua脚本保证原子性) + * + * @param key 锁的key + * @param value 请求标识 + * @return 是否释放成功 + */ + public boolean releaseLock(String key, String value) { + String script = "if redis.call('get', KEYS[1]) == ARGV[1] then return redis.call('del', KEYS[1]) else return 0 end"; + + DefaultRedisScript redisScript = new DefaultRedisScript<>(); + redisScript.setScriptText(script); + redisScript.setResultType(Long.class); + + Long result = redisTemplate.execute(redisScript, Collections.singletonList(key), value); + return Long.valueOf(CP_NUM1).equals(result); + } + + /** + * 保存 Hash 数据 + * + * @param key Key + * @param map Hash 数据 + * @return 保存结果 + */ + public boolean hashPutAll(String key, Map map) { + // 参数校验 + if (StringUtils.isEmpty(key)) { + return false; + } + if (CollectionUtils.isEmpty(map)) { + return false; + } + stringRedisTemplate.opsForHash().putAll(key, map); + return true; + } + + /** + * 保存 Hash 数据 + * + * @param key key + * @param hashKey hashKey + * @param data 数据 + * @return 保存结果 + */ + public boolean hashPut(String key, Object hashKey, Object data) { + // 参数校验 + if (StringUtils.isEmpty(key) || Objects.isNull(hashKey)) { + return false; + } + // 序列化数据 + String serializeData; + if (data instanceof String) { + serializeData = data.toString(); + } else { + serializeData = JSON.toJSONString(data); + } + if (StringUtils.isEmpty(serializeData)) { + return false; + } + stringRedisTemplate.opsForHash().putIfAbsent(key, hashKey, serializeData); + return true; + } + + /** + * 保存 Hash 数据 + * + * @param key key + * @param hashKey hashKey + * @param data 数据 + * @param expireTime 过期时间(秒) + * @return 保存结果 + */ + public boolean hashPut(String key, Object hashKey, Object data, long expireTime) { + // 参数校验 + if (StringUtils.isEmpty(key) || Objects.isNull(hashKey)) { + return false; + } + // 序列化数据 + String serializeData; + if (data instanceof String) { + serializeData = data.toString(); + } else { + serializeData = JSON.toJSONString(data); + } + if (StringUtils.isEmpty(serializeData)) { + return false; + } + String script = + "redis.call('HSET', KEYS[1], ARGV[1], ARGV[2]) " + + "redis.call('HEXPIRE', KEYS[1], ARGV[3], 'FIELDS', 1, ARGV[1]) " + + "return 1"; + Long result = stringRedisTemplate.execute( + new DefaultRedisScript<>(script, Long.class), + Collections.singletonList(key), + hashKey, serializeData, String.valueOf(expireTime) + ); + return Long.valueOf(CP_NUM1).equals(result); + } + + /** + * 获取 Hash 中所有数据 + * + * @param 结果类型 + * @param key key + * @param clazz 结果类型 + * @return 数据集 + */ + public T hashEntries(String key, Class clazz) { + // 参数校验 + if (StringUtils.isEmpty(key)) { + return null; + } + Map data = stringRedisTemplate.opsForHash().entries(key); + if (CollectionUtils.isEmpty(data)) { + return null; + } + return JSON.to(clazz, data); + } + + /** + * 获取 Hash 中指定 Key 的数据 + * + * @param 结果类型 + * @param key key + * @param hashKey hashKey + * @param clazz 结果类型 + * @return 数据 + */ + public T hashGet(String key, Object hashKey, Class clazz) { + // 参数校验 + if (StringUtils.isEmpty(key) || Objects.isNull(hashKey)) { + return null; + } + Object data = stringRedisTemplate.opsForHash().get(key, hashKey); + return JSON.to(clazz, data); + } + + /** + * 删除 Hash 中指定 Key 的数据 + * + * @param key key + * @param hashKeys hashKey数组 + * @return 删除结果 + */ + public Long hashDelete(String key, Object... hashKeys) { + if (StringUtils.isBlank(key) || ArrUtils.isEmpty(hashKeys)) { + return 0L; + } + return stringRedisTemplate.opsForHash().delete(key, hashKeys); + } + + /** + * 判断 Hash 中是否存在指定 Key 的数据 + * + * @param key key + * @param hashKey hashKey + * @return 存在结果 + */ + public boolean hashExists(String key, Object hashKey) { + if (StringUtils.isBlank(key) || Objects.isNull(hashKey)) { + return false; + } + return stringRedisTemplate.opsForHash().hasKey(key, hashKey); + } + +} diff --git a/xtools-boot-cache/xtools-boot-cache-redis/src/main/java/xtools/boot/cache/redis/enums/BaseCacheEnum.java b/xtools-boot-cache/xtools-boot-cache-redis/src/main/java/xtools/boot/cache/redis/enums/BaseCacheEnum.java new file mode 100644 index 0000000..49c2c1b --- /dev/null +++ b/xtools-boot-cache/xtools-boot-cache-redis/src/main/java/xtools/boot/cache/redis/enums/BaseCacheEnum.java @@ -0,0 +1,30 @@ +package xtools.boot.cache.redis.enums; + +/** + *

Title : BaseCacheEnum

+ *

Description : BaseCacheEnum

+ *

DevelopTools : Idea_x64_v2026.1

+ *

DevelopSystem : macOS Sequoia 15.7.5

+ *

Company : org.xujun

+ * + * @author : XuJun + * @version : 5.0.0 + * @date : 2026/3/25 14:57 + */ +public interface BaseCacheEnum { + + /** + * 获取key + * + * @return key + */ + String key(); + + /** + * 获取超时时间 + * + * @return 超时时间 + */ + Long expireTime(); + +} diff --git a/xtools-boot-cache/xtools-boot-cache-redis/src/main/java/xtools/boot/cache/redis/monitor/RedisMonitor.java b/xtools-boot-cache/xtools-boot-cache-redis/src/main/java/xtools/boot/cache/redis/monitor/RedisMonitor.java new file mode 100644 index 0000000..359f8fe --- /dev/null +++ b/xtools-boot-cache/xtools-boot-cache-redis/src/main/java/xtools/boot/cache/redis/monitor/RedisMonitor.java @@ -0,0 +1,63 @@ +package xtools.boot.cache.redis.monitor; + +import com.alibaba.fastjson2.JSONArray; +import com.alibaba.fastjson2.JSONObject; +import jakarta.annotation.Resource; +import org.springframework.data.redis.connection.RedisCommands; +import org.springframework.data.redis.connection.RedisConnectionFactory; +import org.springframework.data.redis.core.RedisTemplate; +import org.springframework.stereotype.Component; +import xtools.core.StringUtils; + +import java.util.Objects; +import java.util.Properties; + +/** + *

Title : RedisMonitor

+ *

Description : RedisMonitor

+ *

DevelopTools : Idea_x64_v2026.1

+ *

DevelopSystem : macOS Sequoia 15.7.5

+ *

Company : org.xujun

+ * + * @author : XuJun + * @version : 5.0.0 + * @date : 2026/01/01 09:30 + */ +@Component +public class RedisMonitor { + + @Resource + private RedisTemplate redisTemplate; + + /** + * 状态 + * + * @return 状态数据 + */ + public JSONObject stats() { + RedisConnectionFactory factory = redisTemplate.getConnectionFactory(); + if (Objects.isNull(factory)) { + return null; + } + // 获取redis命令 + RedisCommands commands = factory.getConnection().commands(); + // 获取相对应信息 + Properties info = commands.info(); + Object dbSize = commands.dbSize(); + Properties commandStats = commands.info("commandstats"); + JSONArray cmd = new JSONArray(); + if (Objects.nonNull(commandStats)) { + commandStats.stringPropertyNames().forEach(key -> { + String property = commandStats.getProperty(key); + JSONObject data = JSONObject.of( + "name", StringUtils.removeStart(key, "cmdstat_"), + "value", StringUtils.substringBetween(property, "calls=", ",usec") + ); + cmd.add(data); + }); + } + // 封装数据 + return JSONObject.of("info", info, "dbSize", dbSize, "cmd", cmd); + } + +} diff --git a/xtools-boot-cache/xtools-boot-cache-redis/src/main/java/xtools/boot/cache/redis/selector/BootCacheRedisImportSelector.java b/xtools-boot-cache/xtools-boot-cache-redis/src/main/java/xtools/boot/cache/redis/selector/BootCacheRedisImportSelector.java new file mode 100644 index 0000000..9ae309e --- /dev/null +++ b/xtools-boot-cache/xtools-boot-cache-redis/src/main/java/xtools/boot/cache/redis/selector/BootCacheRedisImportSelector.java @@ -0,0 +1,36 @@ +package xtools.boot.cache.redis.selector; + +import org.jspecify.annotations.NonNull; +import org.springframework.beans.factory.support.BeanDefinitionRegistry; +import org.springframework.context.annotation.ClassPathBeanDefinitionScanner; +import org.springframework.context.annotation.ImportBeanDefinitionRegistrar; +import org.springframework.core.type.AnnotationMetadata; + +/** + *

Title : BootCacheRedisImportSelector

+ *

Description : BootCacheRedisImportSelector

+ *

DevelopTools : Idea_x64_v2026.1

+ *

DevelopSystem : macOS Sequoia 15.7.5

+ *

Company : org.xujun

+ * + * @author : XuJun + * @version : 5.0.0 + * @date : 2026/01/01 09:30 + */ +public class BootCacheRedisImportSelector implements ImportBeanDefinitionRegistrar { + + /** + * 根据给定的注释元数据,根据需要注册bean + * + * @param importingClassMetadata AnnotationMetadata + * @param registry BeanDefinitionRegistry + */ + @Override + public void registerBeanDefinitions(@NonNull AnnotationMetadata importingClassMetadata, @NonNull BeanDefinitionRegistry registry) { + // 构建扫描对象 + ClassPathBeanDefinitionScanner scanner = new ClassPathBeanDefinitionScanner(registry, true); + // 扫描包下路径 + scanner.scan("xtools.boot.cache.redis"); + } + +} \ No newline at end of file diff --git a/xtools-boot-cache/xtools-boot-cache-redis/src/main/java/xtools/boot/cache/redis/utils/RedisUtils.java b/xtools-boot-cache/xtools-boot-cache-redis/src/main/java/xtools/boot/cache/redis/utils/RedisUtils.java new file mode 100644 index 0000000..ebc9f97 --- /dev/null +++ b/xtools-boot-cache/xtools-boot-cache-redis/src/main/java/xtools/boot/cache/redis/utils/RedisUtils.java @@ -0,0 +1,36 @@ +package xtools.boot.cache.redis.utils; + +import xtools.boot.cache.redis.base.RedisService; +import xtools.boot.core.utils.SpringContextUtils; + +/** + *

Title : RedisUtils

+ *

Description : RedisUtils

+ *

DevelopTools : Idea_x64_v2026.1

+ *

DevelopSystem : macOS Sequoia 15.7.5

+ *

Company : org.xujun

+ * + * @author : XuJun + * @version : 5.0.0 + * @date : 2026/2/10 09:02 + */ +public class RedisUtils { + + /** + * Redis 服务 + **/ + private static RedisService redisService; + + /** + * 获取 Redis 服务 + * + * @return Redis 服务 + */ + public static RedisService get() { + if (redisService != null) { + return redisService; + } + return SpringContextUtils.getBean(RedisService.class); + } + +} diff --git a/xtools-boot-cache/xtools-boot-cache-redis/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports b/xtools-boot-cache/xtools-boot-cache-redis/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports new file mode 100644 index 0000000..83e51b8 --- /dev/null +++ b/xtools-boot-cache/xtools-boot-cache-redis/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports @@ -0,0 +1 @@ +xtools.boot.cache.redis.BootCacheRedisConfiguration \ No newline at end of file diff --git a/xtools-boot-core/pom.xml b/xtools-boot-core/pom.xml new file mode 100644 index 0000000..1900d0a --- /dev/null +++ b/xtools-boot-core/pom.xml @@ -0,0 +1,31 @@ + + + 4.0.0 + + org.xujun + xtools-boot + 5.0.0 + + xtools-boot-core + + + + + + + org.xujun + xtools-boot-api + + + + + + org.springframework + spring-context + + + + + \ No newline at end of file diff --git a/xtools-boot-core/src/main/java/xtools/boot/core/BootCoreConfiguration.java b/xtools-boot-core/src/main/java/xtools/boot/core/BootCoreConfiguration.java new file mode 100644 index 0000000..1f87468 --- /dev/null +++ b/xtools-boot-core/src/main/java/xtools/boot/core/BootCoreConfiguration.java @@ -0,0 +1,28 @@ +package xtools.boot.core; + +import org.springframework.context.annotation.Import; +import xtools.boot.core.selector.BootCoreImportSelector; +import xtools.boot.core.utils.ModuleLoadUtils; + +/** + *

Title : BootCoreConfiguration

+ *

Description : BootCoreConfiguration

+ *

DevelopTools : Idea_x64_v2026.1

+ *

DevelopSystem : macOS Sequoia 15.7.5

+ *

Company : org.xujun

+ * + * @author : XuJun + * @version : 5.0.0 + * @date : 2026/01/01 09:30 + */ +@Import(BootCoreImportSelector.class) +public class BootCoreConfiguration { + + /** + * 构造方法 + */ + public BootCoreConfiguration() { + ModuleLoadUtils.loadSuccess(BootCoreConfiguration.class); + } + +} diff --git a/xtools-boot-core/src/main/java/xtools/boot/core/holder/CommonHolder.java b/xtools-boot-core/src/main/java/xtools/boot/core/holder/CommonHolder.java new file mode 100644 index 0000000..267943a --- /dev/null +++ b/xtools-boot-core/src/main/java/xtools/boot/core/holder/CommonHolder.java @@ -0,0 +1,98 @@ +package xtools.boot.core.holder; + +import xtools.base.config.BaseParams; + +import java.util.HashMap; +import java.util.Map; +import java.util.Objects; + +/** + *

Title : CommonHolder

+ *

Description : CommonHolder

+ *

DevelopTools : Idea_x64_v2026.1

+ *

DevelopSystem : macOS Sequoia 15.7.5

+ *

Company : org.xujun

+ * + * @author : XuJun + * @version : 5.0.0 + * @date : 2026/1/31 21:20 + */ +public class CommonHolder implements BaseParams { + + /** + * Mvc通用信息 + */ + private static final ScopedValue> COMMON_INFO = ScopedValue.newInstance(); + + /** + * 获取通用信息 Holder + * + * @return 通用信息 + */ + public static ScopedValue> getScoped() { + return COMMON_INFO; + } + + /** + * 创建通用信息 + * + * @return 通用信息 + */ + public static Map create() { + return new HashMap<>(16); + } + + /** + * 获取通用信息 + * + * @return 通用信息 + */ + public static Map get() { + if (!COMMON_INFO.isBound()) { + return null; + } + return COMMON_INFO.get(); + } + + /** + * 设置通用信息 + * + * @param key Key + * @param value Value + */ + public static void set(String key, Object value) { + Map map = get(); + if (Objects.nonNull(map)) { + map.put(key, value); + } + } + + /** + * 获取通用信息 + * + * @param key Key + * @return Value + */ + public static Object get(String key) { + Map map = get(); + if (Objects.nonNull(map)) { + return map.get(key); + } + return null; + } + + /** + * 获取通用信息 + * + * @param key Key + * @return Value + */ + public static boolean getBoolean(String key) { + Map map = get(); + if (Objects.nonNull(map) && map.get(key) instanceof Boolean value) { + return value; + } + return false; + } + +} diff --git a/xtools-boot-core/src/main/java/xtools/boot/core/interfaces/FilterWhitelist.java b/xtools-boot-core/src/main/java/xtools/boot/core/interfaces/FilterWhitelist.java new file mode 100644 index 0000000..deb6250 --- /dev/null +++ b/xtools-boot-core/src/main/java/xtools/boot/core/interfaces/FilterWhitelist.java @@ -0,0 +1,25 @@ +package xtools.boot.core.interfaces; + +import java.util.Set; + +/** + *

Title : FilterWhitelist

+ *

Description : FilterWhitelist

+ *

DevelopTools : Idea_x64_v2026.1

+ *

DevelopSystem : macOS Sequoia 15.7.5

+ *

Company : org.xujun

+ * + * @author : XuJun + * @version : 5.0.0 + * @date : 2026/3/8 12:23 + */ +public interface FilterWhitelist { + + /** + * 添加过滤器白名单 + * + * @return 过滤器白名单 + */ + Set add(); + +} diff --git a/xtools-boot-core/src/main/java/xtools/boot/core/interfaces/JobInterface.java b/xtools-boot-core/src/main/java/xtools/boot/core/interfaces/JobInterface.java new file mode 100644 index 0000000..f13f174 --- /dev/null +++ b/xtools-boot-core/src/main/java/xtools/boot/core/interfaces/JobInterface.java @@ -0,0 +1,34 @@ +package xtools.boot.core.interfaces; + +/** + *

Title : JobInterface

+ *

Description : JobInterface

+ *

DevelopTools : Idea_x64_v2026.1

+ *

DevelopSystem : macOS Sequoia 15.7.5

+ *

Company : org.xujun

+ * + * @author : XuJun + * @version : 5.0.0 + * @date : 2026/3/12 20:52 + */ +public interface JobInterface { + + /** + * 执行任务 + * + * @throws Exception 任务执行异常 + */ + void execute() throws Exception; + + /** + * 初始化任务 + */ + default void init() { + } + + /** + * 销毁任务 + */ + default void destroy() { + } +} diff --git a/xtools-boot-core/src/main/java/xtools/boot/core/selector/BootCoreImportSelector.java b/xtools-boot-core/src/main/java/xtools/boot/core/selector/BootCoreImportSelector.java new file mode 100644 index 0000000..995ef8b --- /dev/null +++ b/xtools-boot-core/src/main/java/xtools/boot/core/selector/BootCoreImportSelector.java @@ -0,0 +1,36 @@ +package xtools.boot.core.selector; + +import org.jspecify.annotations.NonNull; +import org.springframework.beans.factory.support.BeanDefinitionRegistry; +import org.springframework.context.annotation.ClassPathBeanDefinitionScanner; +import org.springframework.context.annotation.ImportBeanDefinitionRegistrar; +import org.springframework.core.type.AnnotationMetadata; + +/** + *

Title : BootCoreImportSelector

+ *

Description : BootCoreImportSelector

+ *

DevelopTools : Idea_x64_v2026.1

+ *

DevelopSystem : macOS Sequoia 15.7.5

+ *

Company : org.xujun

+ * + * @author : XuJun + * @version : 5.0.0 + * @date : 2026/01/01 09:30 + */ +public class BootCoreImportSelector implements ImportBeanDefinitionRegistrar { + + /** + * 根据给定的注释元数据,根据需要注册bean + * + * @param importingClassMetadata AnnotationMetadata + * @param registry BeanDefinitionRegistry + */ + @Override + public void registerBeanDefinitions(@NonNull AnnotationMetadata importingClassMetadata, @NonNull BeanDefinitionRegistry registry) { + // 构建扫描对象 + ClassPathBeanDefinitionScanner scanner = new ClassPathBeanDefinitionScanner(registry, true); + // 扫描包下路径 + scanner.scan("xtools.boot.core"); + } + +} \ No newline at end of file diff --git a/xtools-boot-core/src/main/java/xtools/boot/core/utils/AddrUtils.java b/xtools-boot-core/src/main/java/xtools/boot/core/utils/AddrUtils.java new file mode 100644 index 0000000..4e1d5ee --- /dev/null +++ b/xtools-boot-core/src/main/java/xtools/boot/core/utils/AddrUtils.java @@ -0,0 +1,67 @@ +package xtools.boot.core.utils; + +import xtools.base.config.BaseParams; +import xtools.core.StringUtils; +import xtools.core.encrypt.Md5Utils; + +import java.util.List; + +/** + *

Title : AddrUtils

+ *

Description : AddrUtils

+ *

DevelopTools : Idea_x64_v2026.1

+ *

DevelopSystem : macOS Sequoia 15.7.5

+ *

Company : org.xujun

+ * + * @author : XuJun + * @version : 5.0.0 + * @date : 2026/3/13 15:09 + */ +public class AddrUtils implements BaseParams { + + /** + * 本地IP列表 + */ + private static final List LOCAL_IP_LIST; + + // 初始化 + static { + LOCAL_IP_LIST = List.of( + "127.0.0.1", + "localhost" + ); + } + + /** + * 是否为本地IP + * + * @param ip IP + * @return true or false + */ + public static boolean isLocalIp(String ip) { + if (StringUtils.isEmpty(ip)) { + return true; + } + return LOCAL_IP_LIST.contains(ip); + } + + /** + * 根据IP地址获取通用地址code + * + * @param country 国家 + * @param province 省份 + * @param city 城市 + * @return 通用地址code + */ + public static String getCode(String country, String province, String city) { + String code = country; + if (StringUtils.isNotBlank(province)) { + code += CP_LINE + province; + } + if (StringUtils.isNotBlank(city)) { + code += CP_LINE + city; + } + return Md5Utils.encryptToString(code); + } + +} diff --git a/xtools-boot-core/src/main/java/xtools/boot/core/utils/AppUtils.java b/xtools-boot-core/src/main/java/xtools/boot/core/utils/AppUtils.java new file mode 100644 index 0000000..a4d5738 --- /dev/null +++ b/xtools-boot-core/src/main/java/xtools/boot/core/utils/AppUtils.java @@ -0,0 +1,33 @@ +package xtools.boot.core.utils; + +import xtools.core.extend.TemplateUtils; + +/** + *

Title : AppUtils

+ *

Description : AppUtils

+ *

DevelopTools : Idea_x64_v2026.1

+ *

DevelopSystem : macOS Sequoia 15.7.5

+ *

Company : org.xujun

+ * + * @author : XuJun + * @version : 5.0.0 + * @date : 2025/12/23 09:44 + */ +public class AppUtils { + + /** + * 应用启动信息 + */ + private static final String APP_START_INFO = "应用启动成功,总耗时{}毫秒"; + + /** + * 获取启动信息 + * + * @param startTime 启动时间 + * @return 启动信息 + */ + public static String info(long startTime) { + return TemplateUtils.format(APP_START_INFO, startTime); + } + +} diff --git a/xtools-boot-core/src/main/java/xtools/boot/core/utils/EnumUtils.java b/xtools-boot-core/src/main/java/xtools/boot/core/utils/EnumUtils.java new file mode 100644 index 0000000..a31baf7 --- /dev/null +++ b/xtools-boot-core/src/main/java/xtools/boot/core/utils/EnumUtils.java @@ -0,0 +1,42 @@ +package xtools.boot.core.utils; + +import xtools.boot.api.enums.BaseEnum; +import xtools.boot.api.model.EnumInfoDto; +import xtools.core.ArrUtils; + +import java.io.Serializable; +import java.util.ArrayList; +import java.util.List; + +/** + *

Title : EnumUtils

+ *

Description : EnumUtils

+ *

DevelopTools : Idea_x64_v2026.1

+ *

DevelopSystem : macOS Sequoia 15.7.5

+ *

Company : org.xujun

+ * + * @author : XuJun + * @version : 5.0.0 + * @date : 2026/2/14 16:29 + */ +public class EnumUtils implements Serializable { + + /** + * 获取枚举信息 + * + * @param list 枚举值列表 + * @return 枚举的详细信息 + */ + public static List getEnumInfos(BaseEnum... list) { + if (ArrUtils.isEmpty(list)) { + return null; + } + List dataList = new ArrayList<>(); + for (BaseEnum item : list) { + dataList.add(new EnumInfoDto(item.code(), item.desc())); + } + return dataList; + } + + +} diff --git a/xtools-boot-core/src/main/java/xtools/boot/core/utils/JarUtils.java b/xtools-boot-core/src/main/java/xtools/boot/core/utils/JarUtils.java new file mode 100644 index 0000000..5574b95 --- /dev/null +++ b/xtools-boot-core/src/main/java/xtools/boot/core/utils/JarUtils.java @@ -0,0 +1,53 @@ +package xtools.boot.core.utils; + +import lombok.extern.slf4j.Slf4j; +import xtools.core.CollectionUtils; +import xtools.core.sys.SysBaseInfoUtils; + +import java.io.Serializable; +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; + +/** + *

Title : JarUtils

+ *

Description : JarUtils

+ *

DevelopTools : Idea_x64_v2026.1

+ *

DevelopSystem : macOS Sequoia 15.7.5

+ *

Company : org.xujun

+ * + * @author : XuJun + * @version : 5.0.0 + * @date : 2026/2/18 20:57 + */ +@Slf4j +public class JarUtils implements Serializable { + + /** + * 获取所有jar名称 + * + * @return jar名称 + */ + public static List getJarName() { + String end = "jar"; + List mfPath = null; + try { + mfPath = SysBaseInfoUtils.getMfPath(); + } catch (Exception e) { + log.error("获取[MANIFEST.MF]文件路径失败", e); + } + if (CollectionUtils.isEmpty(mfPath)) { + return null; + } + List jarList = new ArrayList<>(); + mfPath.stream().filter(Objects::nonNull).forEach(item -> { + item = item.replaceAll("!/META-INF/MANIFEST.MF", ""); + item = item.substring(item.lastIndexOf("/") + 1); + if (item.endsWith(end)) { + jarList.add(item); + } + }); + return jarList.stream().sorted().toList(); + } + +} diff --git a/xtools-boot-core/src/main/java/xtools/boot/core/utils/ModuleLoadUtils.java b/xtools-boot-core/src/main/java/xtools/boot/core/utils/ModuleLoadUtils.java new file mode 100644 index 0000000..5bbf11a --- /dev/null +++ b/xtools-boot-core/src/main/java/xtools/boot/core/utils/ModuleLoadUtils.java @@ -0,0 +1,32 @@ +package xtools.boot.core.utils; + +import lombok.extern.slf4j.Slf4j; + +/** + *

Title : ModuleLoadUtils

+ *

Description : ModuleLoadUtils

+ *

DevelopTools : Idea_x64_v2026.1

+ *

DevelopSystem : macOS Sequoia 15.7.5

+ *

Company : org.xujun

+ * + * @author : XuJun + * @version : 5.0.0 + * @date : 2026/1/5 09:20 + */ +@Slf4j +public class ModuleLoadUtils { + + /** + * 加载成功模版 + */ + private static final String LOAD_TEMPLATE = "模块[{}]加载成功"; + + /** + * 加载成功 + */ + public static void loadSuccess(Class clazz) { + String packageName = clazz.getPackageName(); + log.info(LOAD_TEMPLATE, packageName); + } + +} diff --git a/xtools-boot-core/src/main/java/xtools/boot/core/utils/PathPatternUtils.java b/xtools-boot-core/src/main/java/xtools/boot/core/utils/PathPatternUtils.java new file mode 100644 index 0000000..b7e51ee --- /dev/null +++ b/xtools-boot-core/src/main/java/xtools/boot/core/utils/PathPatternUtils.java @@ -0,0 +1,84 @@ +package xtools.boot.core.utils; + +import org.springframework.util.AntPathMatcher; +import org.springframework.util.PathMatcher; +import xtools.core.ArrUtils; +import xtools.core.CollectionUtils; + +import java.util.List; +import java.util.Set; + +/** + *

Title : PathPatternUtils

+ *

Description : PathPatternUtils

+ *

DevelopTools : Idea_x64_v2026.1

+ *

DevelopSystem : macOS Sequoia 15.7.5

+ *

Company : org.xujun

+ * + * @author : XuJun + * @version : 5.0.0 + * @date : 2026/2/8 09:05 + */ +public class PathPatternUtils { + + /** + * 路径匹配 + * + * @param pathPatterns 路径规则 + * @param path 路径 + * @return 结果 + */ + public static boolean match(String[] pathPatterns, String path) { + if (ArrUtils.isEmpty(pathPatterns)) { + return false; + } + PathMatcher pm = new AntPathMatcher(); + for (String pathPattern : pathPatterns) { + if (pm.match(pathPattern, path)) { + return true; + } + } + return false; + } + + /** + * 路径匹配 + * + * @param pathPatterns 路径规则 + * @param path 路径 + * @return 结果 + */ + public static boolean match(List pathPatterns, String path) { + if (CollectionUtils.isEmpty(pathPatterns)) { + return false; + } + PathMatcher pm = new AntPathMatcher(); + for (String pathPattern : pathPatterns) { + if (pm.match(pathPattern, path)) { + return true; + } + } + return false; + } + + /** + * 路径匹配 + * + * @param pathPatterns 路径规则 + * @param path 路径 + * @return 结果 + */ + public static boolean match(Set pathPatterns, String path) { + if (CollectionUtils.isEmpty(pathPatterns)) { + return false; + } + PathMatcher pm = new AntPathMatcher(); + for (String pathPattern : pathPatterns) { + if (pm.match(pathPattern, path)) { + return true; + } + } + return false; + } + +} diff --git a/xtools-boot-core/src/main/java/xtools/boot/core/utils/SpringContextUtils.java b/xtools-boot-core/src/main/java/xtools/boot/core/utils/SpringContextUtils.java new file mode 100644 index 0000000..ff65290 --- /dev/null +++ b/xtools-boot-core/src/main/java/xtools/boot/core/utils/SpringContextUtils.java @@ -0,0 +1,211 @@ +package xtools.boot.core.utils; + +import lombok.Getter; +import org.jspecify.annotations.NonNull; +import org.springframework.beans.BeansException; +import org.springframework.beans.factory.ListableBeanFactory; +import org.springframework.beans.factory.config.BeanFactoryPostProcessor; +import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; +import org.springframework.beans.factory.support.DefaultSingletonBeanRegistry; +import org.springframework.context.ApplicationContext; +import org.springframework.context.ApplicationContextAware; +import org.springframework.context.ApplicationEvent; +import org.springframework.context.ConfigurableApplicationContext; +import org.springframework.core.annotation.Order; +import org.springframework.stereotype.Component; +import xtools.base.exception.CommonException; +import xtools.boot.api.enums.BootError; + +import java.util.Collection; +import java.util.LinkedList; + +/** + *

Title : SpringContextUtils

+ *

Description : SpringContextUtils

+ *

DevelopTools : Idea_x64_v2026.1

+ *

DevelopSystem : macOS Sequoia 15.7.5

+ *

Company : org.xujun

+ * + * @author : XuJun + * @version : 5.0.0 + * @date : 2026/01/01 09:30 + */ +@Order(-1) +@Component +public class SpringContextUtils implements BeanFactoryPostProcessor, ApplicationContextAware { + + /** + * "@PostConstruct"注解标记的类中,由于ApplicationContext还未加载,导致空指针 + * 因此实现BeanFactoryPostProcessor注入ConfigurableListableBeanFactory实现bean的操作 + */ + private static ConfigurableListableBeanFactory beanFactory; + + /** + * Spring 应用上下文环境 + */ + @Getter + private static ApplicationContext applicationContext; + + /** + * 获取 ListableBeanFactory + * + * @return ListableBeanFactory + */ + public static ListableBeanFactory getBeanFactory() { + return null == beanFactory ? applicationContext : beanFactory; + } + + /** + * 获取 ConfigurableListableBeanFactory + * + * @return ConfigurableListableBeanFactory + */ + public static ConfigurableListableBeanFactory getConfigurableBeanFactory() { + ConfigurableListableBeanFactory factory = null; + if (null != beanFactory) { + factory = beanFactory; + } else if (applicationContext instanceof ConfigurableApplicationContext) { + factory = ((ConfigurableApplicationContext) applicationContext).getBeanFactory(); + } + return factory; + } + + /** + * 根据类的类型获取对应的 Bean + * + * @param 类的类型 + * @param requiredType 类型 + * @return Bean + */ + public static T getBean(Class requiredType) { + try { + return getBeanFactory().getBean(requiredType); + } catch (Exception e) { + throw CommonException.create(BootError.SPRING_BASE, e, "根据类的类型获取对应的 Bean"); + } + } + + /** + * 根据类的类型获取对应的 Bean + * + * @param 类的类型 + * @param requiredType 类型 + * @return Bean + */ + public static T getBeanDefNull(Class requiredType) { + try { + return getBeanFactory().getBean(requiredType); + } catch (Exception e) { + return null; + } + } + + /** + * 根据类的类型获取对应的 Bean 列表 + * + * @param 类的类型 + * @param requiredType 类型 + * @return Bean 列表 + */ + public static Collection getBeanList(Class requiredType) { + try { + return new LinkedList<>(getBeanFactory().getBeansOfType(requiredType).values()); + } catch (Exception e) { + throw CommonException.create(BootError.SPRING_BASE, e, "根据类的类型获取对应的 Bean 列表"); + } + } + + /** + * 根据 Bean 名称获取对象 + * + * @param name Bean 名称 + * @return Bean + */ + public static Object getBean(String name) { + try { + return getBeanFactory().getBean(name); + } catch (Exception e) { + throw CommonException.create(BootError.SPRING_BASE, e, "根据 Bean 名称获取对象"); + } + } + + /** + * 根据 Bean 名称获取对象 + * + * @param name Bean 名称 + * @return Bean + */ + public static Object getBeanDefNull(String name) { + try { + return getBeanFactory().getBean(name); + } catch (Exception e) { + return null; + } + } + + /** + * 动态向 Spring 注册 Bean + * + * @param Bean 类型 + * @param beanName 名称 + * @param bean Bean + */ + public static void registerBean(String beanName, T bean) { + final ConfigurableListableBeanFactory factory = getConfigurableBeanFactory(); + factory.autowireBean(bean); + factory.registerSingleton(beanName, bean); + } + + /** + * 注销bean(将Spring中的bean注销,请谨慎使用) + * + * @param beanName bean 名称 + */ + public static void unregisterBean(String beanName) { + final ConfigurableListableBeanFactory factory = getConfigurableBeanFactory(); + if (factory instanceof DefaultSingletonBeanRegistry registry) { + registry.destroySingleton(beanName); + } + } + + /** + * 发布事件 + * + * @param event 待发布的事件 + */ + public static void publishAppEvent(ApplicationEvent event) { + publishEvent(event); + } + + /** + * 发布事件 + * + * @param event 待发布的事件 + */ + public static void publishEvent(Object event) { + if (null != applicationContext) { + applicationContext.publishEvent(event); + } + } + + /** + * 设置 Spring 应用上下文环境 + * + * @param applicationContext ApplicationContext + */ + @Override + public void setApplicationContext(@NonNull ApplicationContext applicationContext) throws BeansException { + SpringContextUtils.applicationContext = applicationContext; + } + + /** + * 设置 beanFactory + * + * @param beanFactory ConfigurableListableBeanFactory + */ + @Override + public void postProcessBeanFactory(@NonNull ConfigurableListableBeanFactory beanFactory) throws BeansException { + SpringContextUtils.beanFactory = beanFactory; + } + +} diff --git a/xtools-boot-core/src/main/java/xtools/boot/core/utils/TimeUtils.java b/xtools-boot-core/src/main/java/xtools/boot/core/utils/TimeUtils.java new file mode 100644 index 0000000..090b5cb --- /dev/null +++ b/xtools-boot-core/src/main/java/xtools/boot/core/utils/TimeUtils.java @@ -0,0 +1,53 @@ +package xtools.boot.core.utils; + +import xtools.base.exception.CommonException; +import xtools.boot.api.enums.BootError; +import xtools.core.StringUtils; +import xtools.core.enums.TimePattern; +import xtools.core.extend.CheckUtils; +import xtools.core.time.InstantUtils; + +import java.io.Serializable; +import java.time.Instant; + +/** + *

Title : TimeUtils

+ *

Description : TimeUtils

+ *

DevelopTools : Idea_x64_v2026.1

+ *

DevelopSystem : macOS Sequoia 15.7.5

+ *

Company : org.xujun

+ * + * @author : XuJun + * @version : 5.0.0 + * @date : 2026/1/6 14:20 + */ +public class TimeUtils implements Serializable { + + /** + * 时间分隔符 + */ + private static final String T = "T"; + + /** + * 时间转换 + * + * @param time 时间 + * @return 时间 + */ + public static Instant toInstant(String time) { + if (StringUtils.isBlank(time)) { + return null; + } + if (time.contains(T)) { + return Instant.parse(time); + } + if (CheckUtils.pattern(TimePattern.YMDHMS.regex(), time)) { + return InstantUtils.parse(time, TimePattern.YMDHMS); + } else if (CheckUtils.pattern(TimePattern.YMD.regex(), time)) { + return InstantUtils.parse(time, TimePattern.YMD); + } else { + throw CommonException.create(BootError.TIME_CONVERT, time); + } + } + +} diff --git a/xtools-boot-core/src/main/java/xtools/boot/core/utils/TreeUtils.java b/xtools-boot-core/src/main/java/xtools/boot/core/utils/TreeUtils.java new file mode 100644 index 0000000..8217e03 --- /dev/null +++ b/xtools-boot-core/src/main/java/xtools/boot/core/utils/TreeUtils.java @@ -0,0 +1,38 @@ +package xtools.boot.core.utils; + +import xtools.boot.api.model.dto.resp.TreeResp; + +import java.util.ArrayList; +import java.util.List; + +/** + *

Title : TreeUtils

+ *

Description : TreeUtils

+ *

DevelopTools : Idea_x64_v2026.1

+ *

DevelopSystem : macOS Sequoia 15.7.5

+ *

Company : org.xujun

+ * + * @author : XuJun + * @version : 5.0.0 + * @date : 2026/1/30 09:41 + */ +public class TreeUtils { + + /** + * 获取子节点 + * + * @param id 父级 ID + * @param list 数据级 + * @return 子级 + */ + public static List getChild(Long id, List list) { + List childList = new ArrayList<>(); + list.stream().filter(item -> item.getParentId().equals(id)).forEach(item -> { + List child = getChild(item.getValue(), list); + item.setChildren(child); + childList.add(item); + }); + return childList; + } + +} diff --git a/xtools-boot-core/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports b/xtools-boot-core/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports new file mode 100644 index 0000000..49fd071 --- /dev/null +++ b/xtools-boot-core/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports @@ -0,0 +1 @@ +xtools.boot.core.BootCoreConfiguration \ No newline at end of file diff --git a/xtools-boot-db/pom.xml b/xtools-boot-db/pom.xml new file mode 100644 index 0000000..dc50d93 --- /dev/null +++ b/xtools-boot-db/pom.xml @@ -0,0 +1,20 @@ + + + 4.0.0 + + org.xujun + xtools-boot + 5.0.0 + + pom + xtools-boot-db + + + + xtools-boot-db-mybatis + xtools-boot-db-mybatis-plus + + + \ No newline at end of file diff --git a/xtools-boot-db/xtools-boot-db-mybatis-plus/pom.xml b/xtools-boot-db/xtools-boot-db-mybatis-plus/pom.xml new file mode 100644 index 0000000..7131cd5 --- /dev/null +++ b/xtools-boot-db/xtools-boot-db-mybatis-plus/pom.xml @@ -0,0 +1,37 @@ + + + 4.0.0 + + org.xujun + xtools-boot-db + 5.0.0 + + xtools-boot-db-mybatis-plus + + + + + + + org.xujun + xtools-boot-core + + + + + + + com.baomidou + mybatis-plus-jsqlparser + + + com.baomidou + mybatis-plus-spring-boot4-starter + + + + + + \ No newline at end of file diff --git a/xtools-boot-db/xtools-boot-db-mybatis-plus/src/main/java/xtools/boot/db/mybatisplus/BootDbMybatisPlusConfiguration.java b/xtools-boot-db/xtools-boot-db-mybatis-plus/src/main/java/xtools/boot/db/mybatisplus/BootDbMybatisPlusConfiguration.java new file mode 100644 index 0000000..020bdb8 --- /dev/null +++ b/xtools-boot-db/xtools-boot-db-mybatis-plus/src/main/java/xtools/boot/db/mybatisplus/BootDbMybatisPlusConfiguration.java @@ -0,0 +1,28 @@ +package xtools.boot.db.mybatisplus; + +import org.springframework.context.annotation.Import; +import xtools.boot.core.utils.ModuleLoadUtils; +import xtools.boot.db.mybatisplus.selector.BootDbMybatisPlusImportSelector; + +/** + *

Title : BootCacheRedisConfiguration

+ *

Description : BootCacheRedisConfiguration

+ *

DevelopTools : Idea_x64_v2026.1

+ *

DevelopSystem : macOS Sequoia 15.7.5

+ *

Company : org.xujun

+ * + * @author : XuJun + * @version : 5.0.0 + * @date : 2026/01/01 09:30 + */ +@Import(BootDbMybatisPlusImportSelector.class) +public class BootDbMybatisPlusConfiguration { + + /** + * 构造方法 + */ + public BootDbMybatisPlusConfiguration() { + ModuleLoadUtils.loadSuccess(BootDbMybatisPlusConfiguration.class); + } + +} diff --git a/xtools-boot-db/xtools-boot-db-mybatis-plus/src/main/java/xtools/boot/db/mybatisplus/config/MybatisPlusConfig.java b/xtools-boot-db/xtools-boot-db-mybatis-plus/src/main/java/xtools/boot/db/mybatisplus/config/MybatisPlusConfig.java new file mode 100644 index 0000000..c7d62b8 --- /dev/null +++ b/xtools-boot-db/xtools-boot-db-mybatis-plus/src/main/java/xtools/boot/db/mybatisplus/config/MybatisPlusConfig.java @@ -0,0 +1,35 @@ +package xtools.boot.db.mybatisplus.config; + +import com.baomidou.mybatisplus.annotation.DbType; +import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor; +import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +/** + *

Title : MybatisPlusConfig

+ *

Description : MybatisPlusConfig

+ *

DevelopTools : Idea_x64_v2026.1

+ *

DevelopSystem : macOS Sequoia 15.7.5

+ *

Company : org.xujun

+ * + * @author : XuJun + * @version : 5.0.0 + * @date : 2026/1/5 10:15 + */ +@Configuration +public class MybatisPlusConfig { + + /** + * 配置 MybatisPlus 拦截器 + * + * @return MybatisPlus 拦截器 + */ + @Bean + public MybatisPlusInterceptor mybatisPlusInterceptor() { + MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor(); + // 添加分页插件 + interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL)); + return interceptor; + } +} diff --git a/xtools-boot-db/xtools-boot-db-mybatis-plus/src/main/java/xtools/boot/db/mybatisplus/selector/BootDbMybatisPlusImportSelector.java b/xtools-boot-db/xtools-boot-db-mybatis-plus/src/main/java/xtools/boot/db/mybatisplus/selector/BootDbMybatisPlusImportSelector.java new file mode 100644 index 0000000..69d2405 --- /dev/null +++ b/xtools-boot-db/xtools-boot-db-mybatis-plus/src/main/java/xtools/boot/db/mybatisplus/selector/BootDbMybatisPlusImportSelector.java @@ -0,0 +1,36 @@ +package xtools.boot.db.mybatisplus.selector; + +import org.jspecify.annotations.NonNull; +import org.springframework.beans.factory.support.BeanDefinitionRegistry; +import org.springframework.context.annotation.ClassPathBeanDefinitionScanner; +import org.springframework.context.annotation.ImportBeanDefinitionRegistrar; +import org.springframework.core.type.AnnotationMetadata; + +/** + *

Title : BootDbMybatisPlusImportSelector

+ *

Description : BootDbMybatisPlusImportSelector

+ *

DevelopTools : Idea_x64_v2026.1

+ *

DevelopSystem : macOS Sequoia 15.7.5

+ *

Company : org.xujun

+ * + * @author : XuJun + * @version : 5.0.0 + * @date : 2026/01/01 09:30 + */ +public class BootDbMybatisPlusImportSelector implements ImportBeanDefinitionRegistrar { + + /** + * 根据给定的注释元数据,根据需要注册bean + * + * @param importingClassMetadata AnnotationMetadata + * @param registry BeanDefinitionRegistry + */ + @Override + public void registerBeanDefinitions(@NonNull AnnotationMetadata importingClassMetadata, @NonNull BeanDefinitionRegistry registry) { + // 构建扫描对象 + ClassPathBeanDefinitionScanner scanner = new ClassPathBeanDefinitionScanner(registry, true); + // 扫描包下路径 + scanner.scan("xtools.boot.db.mybatisplus"); + } + +} \ No newline at end of file diff --git a/xtools-boot-db/xtools-boot-db-mybatis-plus/src/main/java/xtools/boot/db/mybatisplus/utils/QueryUtils.java b/xtools-boot-db/xtools-boot-db-mybatis-plus/src/main/java/xtools/boot/db/mybatisplus/utils/QueryUtils.java new file mode 100644 index 0000000..3e32da1 --- /dev/null +++ b/xtools-boot-db/xtools-boot-db-mybatis-plus/src/main/java/xtools/boot/db/mybatisplus/utils/QueryUtils.java @@ -0,0 +1,59 @@ +package xtools.boot.db.mybatisplus.utils; + +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.toolkit.support.SFunction; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import xtools.base.config.BaseParams; +import xtools.core.ArrUtils; + +import java.time.Instant; +import java.util.Objects; + +/** + *

Title : QueryUtils

+ *

Description : QueryUtils

+ *

DevelopTools : Idea_x64_v2026.1

+ *

DevelopSystem : macOS Sequoia 15.7.5

+ *

Company : org.xujun

+ * + * @author : XuJun + * @version : 5.0.0 + * @date : 2026/1/17 19:16 + */ +public class QueryUtils implements BaseParams { + + /** + * 添加时间范围查询 + * + * @param query 查询条件 + * @param timeRange 时间范围 + * @param timeField 时间字段 + * @param 泛型 + */ + public static void addTimeRange(LambdaQueryWrapper query, Instant[] timeRange, SFunction timeField) { + if (ArrUtils.isEmpty(timeRange) || timeRange.length != CP_NUM2) { + return; + } + query.ge(Objects.nonNull(timeRange[CP_NUM0]), timeField, timeRange[CP_NUM0]); + query.le(Objects.nonNull(timeRange[CP_NUM1]), timeField, timeRange[CP_NUM1]); + } + + /** + * 获取分页条件 + * + * @param currentPage 当前页 + * @param pageSize 页面大小 + * @param 泛型 + * @return 分页条件 + */ + public static Page getPage(Integer currentPage, Integer pageSize) { + if (Objects.isNull(currentPage) || currentPage < CP_NUM1) { + currentPage = CP_NUM1; + } + if (Objects.isNull(pageSize) || pageSize < CP_NUM1) { + pageSize = CP_NUM10; + } + return new Page<>(currentPage, pageSize); + } + +} diff --git a/xtools-boot-db/xtools-boot-db-mybatis-plus/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports b/xtools-boot-db/xtools-boot-db-mybatis-plus/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports new file mode 100644 index 0000000..f08626c --- /dev/null +++ b/xtools-boot-db/xtools-boot-db-mybatis-plus/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports @@ -0,0 +1 @@ +xtools.boot.db.mybatisplus.BootDbMybatisPlusConfiguration \ No newline at end of file diff --git a/xtools-boot-db/xtools-boot-db-mybatis/pom.xml b/xtools-boot-db/xtools-boot-db-mybatis/pom.xml new file mode 100644 index 0000000..9ef7930 --- /dev/null +++ b/xtools-boot-db/xtools-boot-db-mybatis/pom.xml @@ -0,0 +1,67 @@ + + + 4.0.0 + + org.xujun + xtools-boot-db + 5.0.0 + + xtools-boot-db-mybatis + + + + + + + org.xujun + xtools-boot-core + + + + org.xujun + xtools-boot-log + + + + org.xujun + xtools-boot-thread + + + + + + + org.mybatis.spring.boot + mybatis-spring-boot-starter + + + + + + com.mysql + mysql-connector-j + + + + + com.alibaba + druid-spring-boot-4-starter + + + + + com.alibaba.fastjson2 + fastjson2 + + + + + jakarta.servlet + jakarta.servlet-api + provided + + + + \ No newline at end of file diff --git a/xtools-boot-db/xtools-boot-db-mybatis/src/main/java/xtools/boot/db/mybatis/BootDbMybatisConfiguration.java b/xtools-boot-db/xtools-boot-db-mybatis/src/main/java/xtools/boot/db/mybatis/BootDbMybatisConfiguration.java new file mode 100644 index 0000000..769c329 --- /dev/null +++ b/xtools-boot-db/xtools-boot-db-mybatis/src/main/java/xtools/boot/db/mybatis/BootDbMybatisConfiguration.java @@ -0,0 +1,30 @@ +package xtools.boot.db.mybatis; + +import org.mybatis.spring.annotation.MapperScan; +import org.springframework.context.annotation.Import; +import xtools.boot.core.utils.ModuleLoadUtils; +import xtools.boot.db.mybatis.selector.BootDbMybatisImportSelector; + +/** + *

Title : BootDbMybatisConfiguration

+ *

Description : BootDbMybatisConfiguration

+ *

DevelopTools : Idea_x64_v2026.1

+ *

DevelopSystem : macOS Sequoia 15.7.5

+ *

Company : org.xujun

+ * + * @author : XuJun + * @version : 5.0.0 + * @date : 2026/01/01 09:30 + */ +@MapperScan({"xtools.boot.db.mybatis.mapper"}) +@Import(BootDbMybatisImportSelector.class) +public class BootDbMybatisConfiguration { + + /** + * 构造方法 + */ + public BootDbMybatisConfiguration() { + ModuleLoadUtils.loadSuccess(BootDbMybatisConfiguration.class); + } + +} diff --git a/xtools-boot-db/xtools-boot-db-mybatis/src/main/java/xtools/boot/db/mybatis/config/MyBatisConfig.java b/xtools-boot-db/xtools-boot-db-mybatis/src/main/java/xtools/boot/db/mybatis/config/MyBatisConfig.java new file mode 100644 index 0000000..b3a0fcd --- /dev/null +++ b/xtools-boot-db/xtools-boot-db-mybatis/src/main/java/xtools/boot/db/mybatis/config/MyBatisConfig.java @@ -0,0 +1,32 @@ +package xtools.boot.db.mybatis.config; + +import lombok.Data; +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.context.annotation.Configuration; + +/** + *

Title : MyBatisConfig

+ *

Description : MyBatisConfig

+ *

DevelopTools : Idea_x64_v2026.1

+ *

DevelopSystem : macOS Sequoia 15.7.5

+ *

Company : org.xujun

+ * + * @author : XuJun + * @version : 5.0.0 + * @date : 2026/2/11 14:46 + */ +@Data +@Configuration +@ConfigurationProperties(prefix = "mybatis") +public class MyBatisConfig { + + /** + * 是否开启MyBatis监控 + */ + private Boolean monitor = true; + + /** + * 慢查询时间阈值 + */ + private Integer slowTime = 3000; +} diff --git a/xtools-boot-db/xtools-boot-db-mybatis/src/main/java/xtools/boot/db/mybatis/druid/DruidConf.java b/xtools-boot-db/xtools-boot-db-mybatis/src/main/java/xtools/boot/db/mybatis/druid/DruidConf.java new file mode 100644 index 0000000..cfe740b --- /dev/null +++ b/xtools-boot-db/xtools-boot-db-mybatis/src/main/java/xtools/boot/db/mybatis/druid/DruidConf.java @@ -0,0 +1,96 @@ +package xtools.boot.db.mybatis.druid; + +import com.alibaba.druid.spring.boot4.autoconfigure.properties.DruidStatProperties; +import com.alibaba.druid.util.Utils; +import jakarta.servlet.Filter; +import jakarta.servlet.FilterChain; +import jakarta.servlet.FilterConfig; +import jakarta.servlet.ServletException; +import jakarta.servlet.ServletRequest; +import jakarta.servlet.ServletResponse; +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; +import org.springframework.boot.web.servlet.FilterRegistrationBean; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.stereotype.Component; +import xtools.core.StringUtils; + +import java.io.IOException; + +/** + *

Title : DruidConf

+ *

Description : DruidConf

+ *

DevelopTools : Idea_x64_v2026.1

+ *

DevelopSystem : macOS Sequoia 15.7.5

+ *

Company : org.xujun

+ * + * @author : XuJun + * @version : 5.0.0 + * @date : 2025年8月13日 上午8:06:06 + */ +@Component +@Configuration +public class DruidConf { + + /** + * 广告过滤 + * + * @param properties Druid配置信息 + * @return 广告过滤 + */ + @Bean + @ConditionalOnProperty(name = "spring.datasource.druid.web-stat-filter.enabled", havingValue = "true") + public FilterRegistrationBean removeDruidFilterRegistrationBean(DruidStatProperties properties) { + DruidStatProperties.StatViewServlet config = properties.getStatViewServlet(); + String urlPattern = config.getUrlPattern(); + // 提取common.js的配置路径 + String pattern = StringUtils.isEmpty(urlPattern) ? "/druid/**" : urlPattern; + String commonJsPattern = pattern.replaceAll("\\*", "js/common.js"); + final String filePath = "support/http/resources/js/common.js"; + // 创建filter进行过滤 + Filter filter = new Filter() { + + /** + * 初始化 + * + * @param filterConfig FilterConfig + */ + @Override + public void init(FilterConfig filterConfig) { + } + + /** + * 过滤器 + * + * @param request ServletRequest + * @param response ServletResponse + * @param chain FilterChain + */ + @Override + public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) + throws IOException, ServletException { + chain.doFilter(request, response); + // 重置缓冲区,响应头不会被重置 + response.resetBuffer(); + // 获取common.js + String text = Utils.readFromResource(filePath); + // 正则替换banner, 除去底部的广告信息 + text = text.replaceAll("
", ""); + text = text.replaceAll("powered.*?shrek.wang", ""); + response.getWriter().write(text); + } + + /** + * 销毁 + */ + @Override + public void destroy() { + } + }; + FilterRegistrationBean registrationBean = new FilterRegistrationBean<>(); + registrationBean.setFilter(filter); + registrationBean.addUrlPatterns(commonJsPattern); + return registrationBean; + } + +} diff --git a/xtools-boot-db/xtools-boot-db-mybatis/src/main/java/xtools/boot/db/mybatis/druid/DruidFilterWhitelist.java b/xtools-boot-db/xtools-boot-db-mybatis/src/main/java/xtools/boot/db/mybatis/druid/DruidFilterWhitelist.java new file mode 100644 index 0000000..5d6fd3b --- /dev/null +++ b/xtools-boot-db/xtools-boot-db-mybatis/src/main/java/xtools/boot/db/mybatis/druid/DruidFilterWhitelist.java @@ -0,0 +1,33 @@ +package xtools.boot.db.mybatis.druid; + +import org.springframework.stereotype.Component; +import xtools.boot.core.interfaces.FilterWhitelist; + +import java.util.Set; + +/** + *

Title : DruidFilterWhitelist

+ *

Description : DruidFilterWhitelist

+ *

DevelopTools : Idea_x64_v2026.1

+ *

DevelopSystem : macOS Sequoia 15.7.5

+ *

Company : org.xujun

+ * + * @author : XuJun + * @version : 5.0.0 + * @date : 2026/3/8 12:28 + */ +@Component +public class DruidFilterWhitelist implements FilterWhitelist { + + /** + * 添加过滤器白名单 + * + * @return 过滤器白名单 + */ + @Override + public Set add() { + return Set.of( + "/druid/**" + ); + } +} diff --git a/xtools-boot-db/xtools-boot-db-mybatis/src/main/java/xtools/boot/db/mybatis/enums/MySqlMonitorEnums.java b/xtools-boot-db/xtools-boot-db-mybatis/src/main/java/xtools/boot/db/mybatis/enums/MySqlMonitorEnums.java new file mode 100644 index 0000000..19b46b7 --- /dev/null +++ b/xtools-boot-db/xtools-boot-db-mybatis/src/main/java/xtools/boot/db/mybatis/enums/MySqlMonitorEnums.java @@ -0,0 +1,121 @@ +package xtools.boot.db.mybatis.enums; + +import xtools.boot.api.enums.BaseEnum; + +/** + *

Title : MySqlMonitorEnums

+ *

Description : MySqlMonitorEnums

+ *

DevelopTools : Idea_x64_v2026.1

+ *

DevelopSystem : macOS Sequoia 15.7.5

+ *

Company : org.xujun

+ * + * @author : XuJun + * @version : 5.0.0 + * @date : 2026/2/3 10:31 + */ +public enum MySqlMonitorEnums implements BaseEnum { + + // 进程列表 + PROCESSLIST(1, "进程列表", "SHOW FULL PROCESSLIST"), + // 变量 + VARIABLES(2, "变量", "SHOW GLOBAL VARIABLES"), + // 状态 + STATUS(3, "状态", "SHOW STATUS"), + + // 需要权限 +// // 查看正在进行中的事务 +// INNODB_TRX(100, "查看正在进行中的事务", "SELECT * FROM information_schema.INNODB_TRX"), +// // 查看正在锁的事务 +// INNODB_LOCKS(101, "查看正在锁的事务", "SELECT * FROM INFORMATION_SCHEMA.INNODB_LOCKS"), +// // 查看等待锁的事务 +// INNODB_LOCK_WAITS(102, "查看等待锁的事务", "SELECT * FROM INFORMATION_SCHEMA.INNODB_LOCK_WAITS"), +// // 查询是否锁表 +// LOCK_TABLES(103, "查询是否锁表", "SHOW OPEN TABLES where In_use > 0"), +// // 查看最近死锁的日志 +// INNODB_STATUS(104, "查看最近死锁的日志", "show engine innodb status"), + + ; + + /** + * 编码 + */ + private final int code; + + /** + * 说明 + */ + private final String desc; + + /** + * SQL语句 + */ + private final String sql; + + /** + * 初始化方法 + * + * @param code Code + * @param desc 说明 + * @param sql SQL + */ + MySqlMonitorEnums(int code, String desc, String sql) { + this.code = code; + this.desc = desc; + this.sql = sql; + } + + /** + * 判断枚举值类型 + * + * @param code 枚举值 + * @return 枚举值类型 + */ + public static MySqlMonitorEnums valueOf(int code) { + for (MySqlMonitorEnums type : values()) { + if (type.code == code) { + return type; + } + } + throw new IllegalArgumentException("unknown code, code=" + code); + } + + /** + * 获取所有枚举 + * + * @return 所有枚举 + */ + @Override + public BaseEnum[] all() { + return values(); + } + + /** + * 获取枚举编码 + * + * @return 枚举编码 + */ + @Override + public int code() { + return code; + } + + /** + * 获取枚举说明 + * + * @return 枚举说明 + */ + @Override + public String desc() { + return desc; + } + + /** + * 获取SQL + * + * @return SQL + */ + public String getSql() { + return sql; + } + +} \ No newline at end of file diff --git a/xtools-boot-db/xtools-boot-db-mybatis/src/main/java/xtools/boot/db/mybatis/interceptor/MyBatisInterceptor.java b/xtools-boot-db/xtools-boot-db-mybatis/src/main/java/xtools/boot/db/mybatis/interceptor/MyBatisInterceptor.java new file mode 100644 index 0000000..7fd6e06 --- /dev/null +++ b/xtools-boot-db/xtools-boot-db-mybatis/src/main/java/xtools/boot/db/mybatis/interceptor/MyBatisInterceptor.java @@ -0,0 +1,235 @@ +package xtools.boot.db.mybatis.interceptor; + +import com.alibaba.druid.pool.DruidPooledPreparedStatement; +import com.alibaba.druid.proxy.jdbc.JdbcParameter; +import com.alibaba.druid.proxy.jdbc.PreparedStatementProxyImpl; +import com.alibaba.fastjson2.JSONArray; +import com.alibaba.fastjson2.JSONObject; +import jakarta.annotation.Resource; +import org.apache.ibatis.executor.statement.StatementHandler; +import org.apache.ibatis.plugin.Interceptor; +import org.apache.ibatis.plugin.Intercepts; +import org.apache.ibatis.plugin.Invocation; +import org.apache.ibatis.plugin.Signature; +import org.apache.ibatis.session.ResultHandler; +import org.jspecify.annotations.NonNull; +import org.springframework.stereotype.Component; +import xtools.boot.api.model.dto.log.LogTrack; +import xtools.boot.db.mybatis.config.MyBatisConfig; +import xtools.boot.log.LogBus; +import xtools.boot.log.enums.LogBusBaseType; +import xtools.boot.log.holder.LogTrackHolder; +import xtools.boot.thread.utils.VirtualThreadTaskUtils; +import xtools.core.CollectionUtils; +import xtools.core.enums.LogLevel; +import xtools.core.enums.TimePattern; +import xtools.core.time.InstantUtils; + +import java.sql.PreparedStatement; +import java.sql.Statement; +import java.util.Map; +import java.util.Objects; + +/** + *

Title : MyBatisInterceptor

+ *

Description : MyBatisInterceptor

+ *

DevelopTools : Idea_x64_v2026.1

+ *

DevelopSystem : macOS Sequoia 15.7.5

+ *

Company : org.xujun

+ * + * @author : XuJun + * @version : 5.0.0 + * @date : 2026/2/11 14:35 + */ +@Component +@Intercepts({ + @Signature(type = StatementHandler.class, method = MyBatisInterceptor.QUERY, args = {Statement.class, ResultHandler.class}), + @Signature(type = StatementHandler.class, method = MyBatisInterceptor.UPDATE, args = {Statement.class}) +}) +public class MyBatisInterceptor implements Interceptor { + + /** + * 查询方法名 + **/ + public final static String QUERY = "query"; + /** + * 更新方法名 + **/ + public final static String UPDATE = "update"; + + @Resource + private MyBatisConfig conf; + + /** + * 拦截操作 + * + * @param invocation 调用参数 + * @return 结果 + */ + @Override + public Object intercept(@NonNull Invocation invocation) throws Throwable { + LogTrack log = LogTrackHolder.getDefNull(); + // 是否启动监控 + if (!conf.getMonitor() || Objects.isNull(log) || !log.save()) { + return invocation.proceed(); + } + // 执行结果 + Object result = null; + // 异常信息 + Exception err = null; + // 开始时间 + long startTime = System.currentTimeMillis(); + try { + result = invocation.proceed(); + return result; + } catch (Exception e) { + err = e; + throw e; + } finally { + // 获取当前线程日志信息 + LogTrack logTrack = LogTrackHolder.get(); + // 结束时间 + long endTime = System.currentTimeMillis(); + // 执行时间 + long execTime = endTime - startTime; + // 执行结果信息 + final Object execResult = result; + // 转换参数 + Throwable ft = err; + // 处理SQL日志 + VirtualThreadTaskUtils.simple(() -> { + // 获取运行参数 + Object runArg = invocation.getArgs()[0]; + // 读写标识 + String rw = QUERY.equals(invocation.getMethod().getName()) ? "读" : "写"; + // 日志信息 + JSONObject logData = JSONObject.of( + "startTime", InstantUtils.format(InstantUtils.from(startTime), TimePattern.ALL), + "execTime", execTime, + "result", execResult, + "rw", rw + ); + // 是否为慢SQL + logData.put("slowSql", execTime >= conf.getSlowTime()); + // 日志分析 + this.analysisLog(logTrack, logData, runArg, ft); + }); + } + } + + /** + * 日志分析 + * + * @param logTrack 日志信息 + * @param log 日志信息 + * @param runArg 运行参数 + * @param t 异常 + */ + private void analysisLog(LogTrack logTrack, JSONObject log, Object runArg, Throwable t) { + try { + // 获取sql信息 + JSONObject sqlInfo = this.getSqlInfo(runArg, logTrack, log); + if (CollectionUtils.isNotEmpty(sqlInfo)) { + log.putAll(sqlInfo); + } + } catch (Exception e) { + t = e; + } finally { + this.execLog(logTrack, log, t); + } + + } + + /** + * 获取SQL信息 + * + * @param runArg 运行参数 + * @param log 日志信息 + * @return SQL信息 + */ + private JSONObject getSqlInfo(Object runArg, LogTrack logTrack, JSONObject log) { + // 执行SQL + String sql; + // 参数 + JSONArray params = new JSONArray(); + + // 个性化参数获取方式 + if (DruidPooledPreparedStatement.class.equals(runArg.getClass())) { + // 开启druid监控获取参数方式 + DruidPooledPreparedStatement statement = (DruidPooledPreparedStatement) runArg; + sql = statement.getSql(); + try { + JSONArray dp = this.getParams(statement); + if (dp != null) { + params = dp; + } + } catch (Exception e) { + this.execLog(logTrack, log, e); + } + } else { + sql = runArg.toString(); + // 获取完整SQL + sql = sql.substring(sql.indexOf(":") + 1); + } + // 格式化Sql + sql = sql.replaceAll(",", ", ").replaceAll("\t+", " ").replaceAll("\n+", " ").replaceAll(" +", " ").trim(); + // 结果封装 + return JSONObject.of("sql", sql, "params", params); + } + + /** + * 获取参数 + * + * @param statement DruidPooledPreparedStatement + * @return 参数集合 + */ + private JSONArray getParams(DruidPooledPreparedStatement statement) { + if (statement == null) { + return null; + } + PreparedStatement ps = statement.getRawStatement(); + if (ps == null) { + return null; + } + if (!PreparedStatementProxyImpl.class.equals(ps.getClass())) { + return null; + } + PreparedStatementProxyImpl psp = (PreparedStatementProxyImpl) ps; + int parametersSize = psp.getParametersSize(); + if (parametersSize <= 0) { + return null; + } + Map paramMap = psp.getParameters(); + if (CollectionUtils.isEmpty(paramMap)) { + return null; + } + // 参数 + JSONArray params = new JSONArray(); + paramMap.forEach((key, value) -> params.add(value.getValue())); + return params; + } + + /** + * 处理SQL日志 + * + * @param logTrack 日志信息 + * @param log 日志信息 + * @param t 异常 + */ + private void execLog(LogTrack logTrack, JSONObject log, Throwable t) { + // 是否为慢SQL + boolean slowSql = log.getBooleanValue("slowSql", false); + // 日志级别 + LogLevel level = LogLevel.INFO; + if (t != null) { + level = LogLevel.ERROR; + } else if (slowSql) { + level = LogLevel.WARN; + } + // 获取读写 + String rw = log.getString("rw"); + log.remove("rw"); + LogBus.init(level, LogBusBaseType.MYBATIS, logTrack).title("SQL/" + rw).data(log).error(t).save(); + } + +} diff --git a/xtools-boot-db/xtools-boot-db-mybatis/src/main/java/xtools/boot/db/mybatis/mapper/MonitorDatabasesMapper.java b/xtools-boot-db/xtools-boot-db-mybatis/src/main/java/xtools/boot/db/mybatis/mapper/MonitorDatabasesMapper.java new file mode 100644 index 0000000..14f33e8 --- /dev/null +++ b/xtools-boot-db/xtools-boot-db-mybatis/src/main/java/xtools/boot/db/mybatis/mapper/MonitorDatabasesMapper.java @@ -0,0 +1,37 @@ +package xtools.boot.db.mybatis.mapper; + +import com.alibaba.fastjson2.JSONObject; +import org.apache.ibatis.annotations.Mapper; +import org.apache.ibatis.annotations.Param; +import org.apache.ibatis.annotations.Select; + +import java.util.List; + +/** + *

Title : MonitorDatabasesMapper

+ *

Description : MonitorDatabasesMapper

+ *

DevelopTools : Idea_x64_v2026.1

+ *

DevelopSystem : macOS Sequoia 15.7.5

+ *

Company : org.xujun

+ * + * @author : XuJun + * @version : 5.0.0 + * @date : 2026/2/14 11:49 + */ +@Mapper +public interface MonitorDatabasesMapper { + + /** + * 监控MySQL + * + * @param sql SQL语句 + * @return 监控数据 + */ + @Select({ + "" + }) + List mysql(@Param("sql") String sql); + +} diff --git a/xtools-boot-db/xtools-boot-db-mybatis/src/main/java/xtools/boot/db/mybatis/monitor/MySqlMonitor.java b/xtools-boot-db/xtools-boot-db-mybatis/src/main/java/xtools/boot/db/mybatis/monitor/MySqlMonitor.java new file mode 100644 index 0000000..a8e6850 --- /dev/null +++ b/xtools-boot-db/xtools-boot-db-mybatis/src/main/java/xtools/boot/db/mybatis/monitor/MySqlMonitor.java @@ -0,0 +1,43 @@ +package xtools.boot.db.mybatis.monitor; + +import com.alibaba.fastjson2.JSONObject; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Component; +import xtools.boot.api.exection.BizError; +import xtools.boot.db.mybatis.enums.MySqlMonitorEnums; +import xtools.boot.db.mybatis.mapper.MonitorDatabasesMapper; + +import java.util.List; +import java.util.Objects; + +/** + *

Title : MySqlMonitor

+ *

Description : MySqlMonitor

+ *

DevelopTools : Idea_x64_v2026.1

+ *

DevelopSystem : macOS Sequoia 15.7.5

+ *

Company : org.xujun

+ * + * @author : XuJun + * @version : 5.0.0 + * @date : 2026/2/14 16:18 + */ +@Component +@RequiredArgsConstructor +public class MySqlMonitor { + + private final MonitorDatabasesMapper monitorDatabasesMapper; + + /** + * 获取MySQL监控信息 + * + * @param type 枚举 + * @return MySQL监控信息 + */ + public List info(MySqlMonitorEnums type) { + if (Objects.isNull(type)) { + throw new BizError("类型参数不能为空"); + } + return monitorDatabasesMapper.mysql(type.getSql()); + } + +} diff --git a/xtools-boot-db/xtools-boot-db-mybatis/src/main/java/xtools/boot/db/mybatis/selector/BootDbMybatisImportSelector.java b/xtools-boot-db/xtools-boot-db-mybatis/src/main/java/xtools/boot/db/mybatis/selector/BootDbMybatisImportSelector.java new file mode 100644 index 0000000..e21428a --- /dev/null +++ b/xtools-boot-db/xtools-boot-db-mybatis/src/main/java/xtools/boot/db/mybatis/selector/BootDbMybatisImportSelector.java @@ -0,0 +1,36 @@ +package xtools.boot.db.mybatis.selector; + +import org.jspecify.annotations.NonNull; +import org.springframework.beans.factory.support.BeanDefinitionRegistry; +import org.springframework.context.annotation.ClassPathBeanDefinitionScanner; +import org.springframework.context.annotation.ImportBeanDefinitionRegistrar; +import org.springframework.core.type.AnnotationMetadata; + +/** + *

Title : BootDbMybatisImportSelector

+ *

Description : BootDbMybatisImportSelector

+ *

DevelopTools : Idea_x64_v2026.1

+ *

DevelopSystem : macOS Sequoia 15.7.5

+ *

Company : org.xujun

+ * + * @author : XuJun + * @version : 5.0.0 + * @date : 2026/01/01 09:30 + */ +public class BootDbMybatisImportSelector implements ImportBeanDefinitionRegistrar { + + /** + * 根据给定的注释元数据,根据需要注册bean + * + * @param importingClassMetadata AnnotationMetadata + * @param registry BeanDefinitionRegistry + */ + @Override + public void registerBeanDefinitions(@NonNull AnnotationMetadata importingClassMetadata, @NonNull BeanDefinitionRegistry registry) { + // 构建扫描对象 + ClassPathBeanDefinitionScanner scanner = new ClassPathBeanDefinitionScanner(registry, true); + // 扫描包下路径 + scanner.scan("xtools.boot.db.mybatis"); + } + +} \ No newline at end of file diff --git a/xtools-boot-db/xtools-boot-db-mybatis/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports b/xtools-boot-db/xtools-boot-db-mybatis/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports new file mode 100644 index 0000000..e24976e --- /dev/null +++ b/xtools-boot-db/xtools-boot-db-mybatis/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports @@ -0,0 +1 @@ +xtools.boot.db.mybatis.BootDbMybatisConfiguration \ No newline at end of file diff --git a/xtools-boot-elasticsearch/pom.xml b/xtools-boot-elasticsearch/pom.xml new file mode 100644 index 0000000..d7f88f1 --- /dev/null +++ b/xtools-boot-elasticsearch/pom.xml @@ -0,0 +1,43 @@ + + + 4.0.0 + + org.xujun + xtools-boot + 5.0.0 + + xtools-boot-elasticsearch + + + + + + + org.xujun + xtools-extend + + + + + + + org.xujun + xtools-boot-core + + + + org.xujun + xtools-boot-log + + + + + + com.alibaba.fastjson2 + fastjson2 + + + + \ No newline at end of file diff --git a/xtools-boot-elasticsearch/src/main/java/xtools/boot/elasticsearch/BootElasticsearchConfiguration.java b/xtools-boot-elasticsearch/src/main/java/xtools/boot/elasticsearch/BootElasticsearchConfiguration.java new file mode 100644 index 0000000..a054d9f --- /dev/null +++ b/xtools-boot-elasticsearch/src/main/java/xtools/boot/elasticsearch/BootElasticsearchConfiguration.java @@ -0,0 +1,28 @@ +package xtools.boot.elasticsearch; + +import org.springframework.context.annotation.Import; +import xtools.boot.core.utils.ModuleLoadUtils; +import xtools.boot.elasticsearch.selector.BootElasticsearchImportSelector; + +/** + *

Title : BootElasticsearchConfiguration

+ *

Description : BootElasticsearchConfiguration

+ *

DevelopTools : Idea_x64_v2026.1

+ *

DevelopSystem : macOS Sequoia 15.7.5

+ *

Company : org.xujun

+ * + * @author : XuJun + * @version : 5.0.0 + * @date : 2026/01/01 09:30 + */ +@Import(BootElasticsearchImportSelector.class) +public class BootElasticsearchConfiguration { + + /** + * 构造方法 + */ + public BootElasticsearchConfiguration() { + ModuleLoadUtils.loadSuccess(BootElasticsearchConfiguration.class); + } + +} diff --git a/xtools-boot-elasticsearch/src/main/java/xtools/boot/elasticsearch/enums/ElasticsearchMonitorEnums.java b/xtools-boot-elasticsearch/src/main/java/xtools/boot/elasticsearch/enums/ElasticsearchMonitorEnums.java new file mode 100644 index 0000000..36d6095 --- /dev/null +++ b/xtools-boot-elasticsearch/src/main/java/xtools/boot/elasticsearch/enums/ElasticsearchMonitorEnums.java @@ -0,0 +1,126 @@ +package xtools.boot.elasticsearch.enums; + +import xtools.boot.api.enums.BaseEnum; + +/** + *

Title : ElasticsearchMonitorEnums

+ *

Description : ElasticsearchMonitorEnums

+ *

DevelopTools : Idea_x64_v2026.1

+ *

DevelopSystem : macOS Sequoia 15.7.5

+ *

Company : org.xujun

+ * + * @author : XuJun + * @version : 5.0.0 + * @date : 2026/2/3 10:31 + */ +public enum ElasticsearchMonitorEnums implements BaseEnum { + + // 集群索引信息 + INDICES(1, "集群索引信息", "/_cat/indices?v"), + // 集群健康信息 + HEALTH(2, "集群健康信息", "/_cat/health?v"), + // 集群分片信息 + SHARDS(3, "集群分片信息", "/_cat/shards?v"), + // 集群节点信息 + NODES(4, "集群节点信息", "/_cat/nodes?v"), + // 集群任务 + TASKS(5, "集群任务", "/_cat/tasks?v"), + // 集群段信息 + SEGMENTS(6, "集群段信息", "/_cat/segments?v"), + // 集群文档总数 + COUNT(7, "集群文档总数", "/_cat/count?v"), + // 集群shard的recovery过程 + RECOVERY(8, "集群shard的recovery过程", "/_cat/recovery?v"), + // 集群别名组 + ALIASES(9, "集群别名组", "/_cat/aliases?v"), + // 集群线程池任务 + THREAD_POOL(10, "集群线程池任务", "/_cat/thread_pool?v"), + // 集群插件信息 + PLUGINS(11, "集群插件信息", "/_cat/plugins?v"), + // 单节点的自定义属性 + NODEATTRS(12, "单节点的自定义属性", "/_cat/nodeattrs?v"), + ; + + /** + * 编码 + */ + private final int code; + + /** + * 说明 + */ + private final String desc; + + /** + * 命令 + */ + private final String cmd; + + /** + * 初始化方法 + * + * @param code Code + * @param desc 说明 + * @param cmd 命令 + */ + ElasticsearchMonitorEnums(int code, String desc, String cmd) { + this.code = code; + this.desc = desc; + this.cmd = cmd; + } + + /** + * 判断枚举值类型 + * + * @param code 枚举值 + * @return 枚举值类型 + */ + public static ElasticsearchMonitorEnums valueOf(int code) { + for (ElasticsearchMonitorEnums type : values()) { + if (type.code == code) { + return type; + } + } + throw new IllegalArgumentException("unknown code, code=" + code); + } + + /** + * 获取所有枚举 + * + * @return 所有枚举 + */ + @Override + public BaseEnum[] all() { + return values(); + } + + /** + * 获取枚举编码 + * + * @return 枚举编码 + */ + @Override + public int code() { + return code; + } + + /** + * 获取枚举说明 + * + * @return 枚举说明 + */ + @Override + public String desc() { + return desc; + } + + /** + * 获取命令 + * + * @return 命令 + */ + public String cmd() { + return cmd; + } + +} \ No newline at end of file diff --git a/xtools-boot-elasticsearch/src/main/java/xtools/boot/elasticsearch/monitor/ElasticsearchMonitor.java b/xtools-boot-elasticsearch/src/main/java/xtools/boot/elasticsearch/monitor/ElasticsearchMonitor.java new file mode 100644 index 0000000..2154b97 --- /dev/null +++ b/xtools-boot-elasticsearch/src/main/java/xtools/boot/elasticsearch/monitor/ElasticsearchMonitor.java @@ -0,0 +1,69 @@ +package xtools.boot.elasticsearch.monitor; + +import com.alibaba.fastjson2.JSONObject; +import org.springframework.stereotype.Component; +import xtools.base.config.BaseParams; +import xtools.boot.api.exection.BizError; +import xtools.boot.elasticsearch.enums.ElasticsearchMonitorEnums; +import xtools.boot.elasticsearch.utils.EsUtils; +import xtools.core.ArrUtils; +import xtools.core.CollectionUtils; + +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; + +/** + *

Title : ElasticsearchMonitor

+ *

Description : ElasticsearchMonitor

+ *

DevelopTools : Idea_x64_v2026.1

+ *

DevelopSystem : macOS Sequoia 15.7.5

+ *

Company : org.xujun

+ * + * @author : XuJun + * @version : 5.0.0 + * @date : 2026/2/14 17:58 + */ +@Component +public class ElasticsearchMonitor implements BaseParams { + + /** + * 获取Elasticsearch监控信息 + * + * @param type 枚举 + * @return Elasticsearch监控信息 + */ + public JSONObject info(ElasticsearchMonitorEnums type) { + if (Objects.isNull(type)) { + throw new BizError("类型参数不能为空"); + } + // 查询 + String result; + try { + result = EsUtils.exec(type.cmd(), null, String.class, true); + } catch (Exception e) { + throw new BizError("查询异常"); + } + // 处理数据 + result = result.replaceAll("\n", "###").replaceAll("\\s+", " "); + List arr = ArrUtils.toStringList(result, "###"); + if (CollectionUtils.isEmpty(arr)) { + throw new BizError("类型参数不能为空"); + } + // 封装表头 + List titleList = ArrUtils.toStringList(arr.getFirst(), " "); + // 处理主体数据 + List dataList = new ArrayList<>(); + for (int i = CP_NUM1; i < arr.size(); i++) { + String line = arr.get(i); + List data = ArrUtils.toStringList(line, " "); + JSONObject lineData = new JSONObject(); + for (int j = CP_NUM0; j < data.size(); j++) { + lineData.put(titleList.get(j), data.get(j)); + } + dataList.add(lineData); + } + return JSONObject.of("title", titleList, "dataList", dataList); + } + +} diff --git a/xtools-boot-elasticsearch/src/main/java/xtools/boot/elasticsearch/selector/BootElasticsearchImportSelector.java b/xtools-boot-elasticsearch/src/main/java/xtools/boot/elasticsearch/selector/BootElasticsearchImportSelector.java new file mode 100644 index 0000000..b69a500 --- /dev/null +++ b/xtools-boot-elasticsearch/src/main/java/xtools/boot/elasticsearch/selector/BootElasticsearchImportSelector.java @@ -0,0 +1,36 @@ +package xtools.boot.elasticsearch.selector; + +import org.jspecify.annotations.NonNull; +import org.springframework.beans.factory.support.BeanDefinitionRegistry; +import org.springframework.context.annotation.ClassPathBeanDefinitionScanner; +import org.springframework.context.annotation.ImportBeanDefinitionRegistrar; +import org.springframework.core.type.AnnotationMetadata; + +/** + *

Title : BootElasticsearchImportSelector

+ *

Description : BootElasticsearchImportSelector

+ *

DevelopTools : Idea_x64_v2026.1

+ *

DevelopSystem : macOS Sequoia 15.7.5

+ *

Company : org.xujun

+ * + * @author : XuJun + * @version : 5.0.0 + * @date : 2026/01/01 09:30 + */ +public class BootElasticsearchImportSelector implements ImportBeanDefinitionRegistrar { + + /** + * 根据给定的注释元数据,根据需要注册bean + * + * @param importingClassMetadata AnnotationMetadata + * @param registry BeanDefinitionRegistry + */ + @Override + public void registerBeanDefinitions(@NonNull AnnotationMetadata importingClassMetadata, @NonNull BeanDefinitionRegistry registry) { + // 构建扫描对象 + ClassPathBeanDefinitionScanner scanner = new ClassPathBeanDefinitionScanner(registry, true); + // 扫描包下路径 + scanner.scan("xtools.boot.elasticsearch"); + } + +} \ No newline at end of file diff --git a/xtools-boot-elasticsearch/src/main/java/xtools/boot/elasticsearch/utils/EsQueryUtils.java b/xtools-boot-elasticsearch/src/main/java/xtools/boot/elasticsearch/utils/EsQueryUtils.java new file mode 100644 index 0000000..0e17a47 --- /dev/null +++ b/xtools-boot-elasticsearch/src/main/java/xtools/boot/elasticsearch/utils/EsQueryUtils.java @@ -0,0 +1,78 @@ +package xtools.boot.elasticsearch.utils; + +import com.alibaba.fastjson2.JSONArray; +import com.alibaba.fastjson2.JSONObject; +import xtools.base.config.BaseParams; +import xtools.core.ArrUtils; +import xtools.core.StringUtils; + +import java.time.Instant; +import java.util.Objects; + +/** + *

Title : EsQueryUtils

+ *

Description : EsQueryUtils

+ *

DevelopTools : Idea_x64_v2026.1

+ *

DevelopSystem : macOS Sequoia 15.7.5

+ *

Company : org.xujun

+ * + * @author : XuJun + * @version : 5.0.0 + * @date : 2026/2/14 08:58 + */ +public class EsQueryUtils implements BaseParams { + + /** + * 设置精确查询 + * + * @param query 查询条件 + * @param filedName 字段名称 + * @param params 参数 + */ + public static void setMatchPhrase(JSONArray query, String filedName, Object params) { + if (params instanceof String txt && StringUtils.isNotBlank(txt)) { + query.add(JSONObject.of("match_phrase", JSONObject.of(filedName, txt))); + } else if (Objects.nonNull(params)) { + query.add(JSONObject.of("match_phrase", JSONObject.of(filedName, params))); + } + } + + /** + * 模糊查询 + * + * @param query 模糊查询 + * @param filedName 字段名称 + * @param params 参数 + */ + public static void setWildcard(JSONArray query, String filedName, String params) { + if (StringUtils.isBlank(params)) { + return; + } + query.add(JSONObject.of("wildcard", JSONObject.of(filedName, "*" + params + "*"))); + } + + /** + * 时间范围查询 + * + * @param query 时间范围查询 + * @param filedName 字段名称 + * @param params 参数 + */ + public static void setTimeRange(JSONArray query, String filedName, Instant[] params) { + if (ArrUtils.isEmpty(params)) { + return; + } + long start = Long.MIN_VALUE; + long end = Long.MAX_VALUE; + Instant startTime = params[CP_NUM0]; + if (Objects.nonNull(startTime)) { + start = startTime.toEpochMilli(); + } + Instant endTime = params[CP_NUM1]; + if (Objects.nonNull(endTime)) { + end = endTime.toEpochMilli(); + } + query.add(JSONObject.of("range", JSONObject.of(filedName, JSONObject.of("gte", start, "lte", end)))); + } + +} diff --git a/xtools-boot-elasticsearch/src/main/java/xtools/boot/elasticsearch/utils/EsUtils.java b/xtools-boot-elasticsearch/src/main/java/xtools/boot/elasticsearch/utils/EsUtils.java new file mode 100644 index 0000000..44a82f7 --- /dev/null +++ b/xtools-boot-elasticsearch/src/main/java/xtools/boot/elasticsearch/utils/EsUtils.java @@ -0,0 +1,161 @@ +package xtools.boot.elasticsearch.utils; + +import com.alibaba.fastjson2.JSON; +import com.alibaba.fastjson2.JSONObject; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Component; +import xtools.boot.api.exection.BizError; +import xtools.boot.api.exection.BizWarning; +import xtools.boot.core.utils.SpringContextUtils; +import xtools.boot.log.LogBus; +import xtools.boot.log.enums.LogBusBaseType; +import xtools.core.BytesUtils; +import xtools.core.CollectionUtils; +import xtools.core.StringUtils; +import xtools.core.encrypt.Base64Utils; +import xtools.core.enums.LogLevel; +import xtools.core.extend.HttpUtils; +import xtools.extend.JsonUtils; + +import java.nio.charset.StandardCharsets; +import java.util.Objects; + +/** + *

Title : EsUtils

+ *

Description : EsUtils

+ *

DevelopTools : Idea_x64_v2026.1

+ *

DevelopSystem : macOS Sequoia 15.7.5

+ *

Company : org.xujun

+ * + * @author : XuJun + * @version : 5.0.0 + * @date : 2025年8月17日 下午6:24:22 + */ +@Component +public class EsUtils { + + /** + * 超时时间 + */ + private static final long TIMEOUT = 60 * 1000 * 5; + + /** + * 查询服务器地址 + */ + private static String host; + + /** + * 授权信息 + */ + private static String auth; + + + @Value("${spring.elasticsearch.uris:#{null}}") + private String uris; + @Value("${spring.elasticsearch.username:#{null}}") + private String username; + @Value("${spring.elasticsearch.password:#{null}}") + private String password; + + /** + * 执行 + * + * @param uri 执行的uri + * @return 执行结果 + */ + public static String exec(String uri) { + return exec(uri, null, String.class, false); + } + + /** + * 执行 + * + * @param uri 执行的uri + * @param req 请求参数 + * @return 执行结果 + */ + public static JSONObject exec(String uri, JSONObject req) { + return exec(uri, req, JSONObject.class, false); + } + + /** + * 执行 + * + * @param 结果类型 + * @param uri 执行的uri + * @param req 请求参数 + * @param clazz 结果类型 + * @param isGet 是否为get请求 + * @return 结果 + */ + public static T exec(String uri, JSONObject req, Class clazz, boolean isGet) { + // 参数校验 + if (StringUtils.isBlank(uri)) { + throw new BizWarning("uri不能为空"); + } + if (StringUtils.isBlank(host)) { + // 初始化 + EsUtils bean = SpringContextUtils.getBean(EsUtils.class); + bean.init(); + } + HttpUtils http = HttpUtils.init(host + uri).addHeader("Content-Type", "application/json").timeOut(TIMEOUT); + if (StringUtils.isNotBlank(auth)) { + http.addHeader("Authorization", "Basic " + auth); + } + http.setLogCallBack(log -> { + byte[] resp = log.resp(); + String respData = BytesUtils.isEmpty(resp) ? null : new String(resp); + JSONObject logData = JSONObject.of( + "req", log.reqParam(), + "resp", JsonUtils.isJson(respData) ? JSONObject.parseObject(respData) : respData, + "execTime", log.execTime() + ); + Throwable error = log.error(); + // 保存日志 + LogBus.init(Objects.isNull(error) ? LogLevel.INFO : LogLevel.ERROR, LogBusBaseType.ELASTICSEARCH) + .data(logData) + .error(error) + .save(); + }); + // 查询结果 + String result; + if (isGet) { + result = http.get(); + } else { + result = http.post(CollectionUtils.isEmpty(req) ? null : req.toString()); + } + if (StringUtils.isBlank(result)) { + throw new BizWarning("ES执行结果为空"); + } + return JSON.to(clazz, result); + } + + /** + * 获取授权信息 + * + * @param username 用户名 + * @param password 密码 + * @return 授权信息 + */ + private static String getAuth(String username, String password) { + if (StringUtils.isBlank(username) || StringUtils.isBlank(password)) { + return null; + } + return Base64Utils.encodeToStr((username + ":" + password).getBytes(StandardCharsets.UTF_8)); + } + + /** + * 初始化 + */ + public void init() { + if (StringUtils.isNotBlank(host)) { + return; + } + if (StringUtils.isBlank(uris)) { + throw new BizError("Es工具类初始化失败, spring.elasticsearch.uris 不能为空"); + } + EsUtils.host = uris; + EsUtils.auth = getAuth(username, password); + } + +} diff --git a/xtools-boot-elasticsearch/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports b/xtools-boot-elasticsearch/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports new file mode 100644 index 0000000..7e4b6dd --- /dev/null +++ b/xtools-boot-elasticsearch/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports @@ -0,0 +1 @@ +xtools.boot.elasticsearch.BootElasticsearchConfiguration \ No newline at end of file diff --git a/xtools-boot-ip/pom.xml b/xtools-boot-ip/pom.xml new file mode 100644 index 0000000..5f3db7b --- /dev/null +++ b/xtools-boot-ip/pom.xml @@ -0,0 +1,43 @@ + + + 4.0.0 + + org.xujun + xtools-boot + 5.0.0 + + xtools-boot-ip + + + + + + + + org.lionsoul + ip2region + + + org.xujun + xtools-extend + + + + + + + org.xujun + xtools-boot-core + + + + + + org.springframework.boot + spring-boot + + + + \ No newline at end of file diff --git a/xtools-boot-ip/src/main/java/xtools/boot/ip/BootIpConfiguration.java b/xtools-boot-ip/src/main/java/xtools/boot/ip/BootIpConfiguration.java new file mode 100644 index 0000000..980f40b --- /dev/null +++ b/xtools-boot-ip/src/main/java/xtools/boot/ip/BootIpConfiguration.java @@ -0,0 +1,28 @@ +package xtools.boot.ip; + +import org.springframework.context.annotation.Import; +import xtools.boot.core.utils.ModuleLoadUtils; +import xtools.boot.ip.selector.BootIpImportSelector; + +/** + *

Title : BootIpConfiguration

+ *

Description : BootIpConfiguration

+ *

DevelopTools : Idea_x64_v2026.1

+ *

DevelopSystem : macOS Sequoia 15.7.5

+ *

Company : org.xujun

+ * + * @author : XuJun + * @version : 5.0.0 + * @date : 2026/01/01 09:30 + */ +@Import(BootIpImportSelector.class) +public class BootIpConfiguration { + + /** + * 构造方法 + */ + public BootIpConfiguration() { + ModuleLoadUtils.loadSuccess(BootIpConfiguration.class); + } + +} diff --git a/xtools-boot-ip/src/main/java/xtools/boot/ip/init/InitIp.java b/xtools-boot-ip/src/main/java/xtools/boot/ip/init/InitIp.java new file mode 100644 index 0000000..21a5468 --- /dev/null +++ b/xtools-boot-ip/src/main/java/xtools/boot/ip/init/InitIp.java @@ -0,0 +1,49 @@ +package xtools.boot.ip.init; + +import org.jspecify.annotations.NonNull; +import org.lionsoul.ip2region.xdb.Version; +import org.springframework.boot.ApplicationArguments; +import org.springframework.boot.ApplicationRunner; +import org.springframework.core.annotation.Order; +import org.springframework.core.io.ClassPathResource; +import org.springframework.stereotype.Component; +import xtools.base.config.BaseParams; +import xtools.boot.ip.utils.IpUtils; +import xtools.extend.IpLocalUtils; + +import java.io.IOException; +import java.io.InputStream; + +/** + *

Title : InitIp

+ *

Description : InitIp

+ *

DevelopTools : Idea_x64_v2026.1

+ *

DevelopSystem : macOS Sequoia 15.7.5

+ *

Company : org.xujun

+ * + * @author : XuJun + * @version : 5.0.0 + * @date : 2026/2/16 10:21 + */ +@Component +@Order(BaseParams.CP_NUM50) +public class InitIp implements ApplicationRunner { + + private static final String IP_FILE = "ip/ip2region_v4.xdb"; + + @Override + public void run(@NonNull ApplicationArguments args) throws Exception { + init(); + } + + /** + * 初始化IP库 + */ + private void init() throws IOException { + ClassPathResource classPathResource = new ClassPathResource(IP_FILE); + try (InputStream is = classPathResource.getInputStream()) { + IpLocalUtils.init(is, Version.IPv4); + IpUtils.init(); + } + } +} diff --git a/xtools-boot-ip/src/main/java/xtools/boot/ip/selector/BootIpImportSelector.java b/xtools-boot-ip/src/main/java/xtools/boot/ip/selector/BootIpImportSelector.java new file mode 100644 index 0000000..4fabf66 --- /dev/null +++ b/xtools-boot-ip/src/main/java/xtools/boot/ip/selector/BootIpImportSelector.java @@ -0,0 +1,36 @@ +package xtools.boot.ip.selector; + +import org.jspecify.annotations.NonNull; +import org.springframework.beans.factory.support.BeanDefinitionRegistry; +import org.springframework.context.annotation.ClassPathBeanDefinitionScanner; +import org.springframework.context.annotation.ImportBeanDefinitionRegistrar; +import org.springframework.core.type.AnnotationMetadata; + +/** + *

Title : BootIpImportSelector

+ *

Description : BootIpImportSelector

+ *

DevelopTools : Idea_x64_v2026.1

+ *

DevelopSystem : macOS Sequoia 15.7.5

+ *

Company : org.xujun

+ * + * @author : XuJun + * @version : 5.0.0 + * @date : 2026/01/01 09:30 + */ +public class BootIpImportSelector implements ImportBeanDefinitionRegistrar { + + /** + * 根据给定的注释元数据,根据需要注册bean + * + * @param importingClassMetadata AnnotationMetadata + * @param registry BeanDefinitionRegistry + */ + @Override + public void registerBeanDefinitions(@NonNull AnnotationMetadata importingClassMetadata, @NonNull BeanDefinitionRegistry registry) { + // 构建扫描对象 + ClassPathBeanDefinitionScanner scanner = new ClassPathBeanDefinitionScanner(registry, true); + // 扫描包下路径 + scanner.scan("xtools.boot.ip"); + } + +} \ No newline at end of file diff --git a/xtools-boot-ip/src/main/java/xtools/boot/ip/utils/IpUtils.java b/xtools-boot-ip/src/main/java/xtools/boot/ip/utils/IpUtils.java new file mode 100644 index 0000000..778ec0b --- /dev/null +++ b/xtools-boot-ip/src/main/java/xtools/boot/ip/utils/IpUtils.java @@ -0,0 +1,83 @@ +package xtools.boot.ip.utils; + +import xtools.base.config.BaseParams; +import xtools.boot.api.exection.BizError; +import xtools.core.StringUtils; +import xtools.extend.IpLocalUtils; +import xtools.extend.dto.IpAddrDto; + +import java.util.Objects; +import java.util.StringJoiner; + +/** + *

Title : IpUtils

+ *

Description : IpUtils

+ *

DevelopTools : Idea_x64_v2026.1

+ *

DevelopSystem : macOS Sequoia 15.7.5

+ *

Company : org.xujun

+ * + * @author : XuJun + * @version : 5.0.0 + * @date : 2026/2/16 10:41 + */ +public class IpUtils implements BaseParams { + + /** + * 是否初始化 + */ + private static boolean init = false; + + /** + * 初始化 + */ + public static void init() { + init = true; + } + + /** + * 查询IP对应地址 + * + * @param ip IP + * @return IP对应地址 + */ + public static IpAddrDto search(String ip) { + if (!init) { + throw new BizError("请先初始化IpUtils"); + } + return IpLocalUtils.search(ip, false); + } + + /** + * 获取IP对应地址 + * + * @param ip IP + * @return IP对应地址 + */ + public static String searchAddr(String ip) { + return searchAddr(search(ip)); + } + + /** + * 获取IP对应地址 + * + * @param addr 地址信息 + * @return IP对应地址 + */ + public static String searchAddr(IpAddrDto addr) { + if (Objects.isNull(addr)) { + return "未知"; + } + StringJoiner joiner = new StringJoiner(CP_COMMA); + if (StringUtils.isNotBlank(addr.getCountry())) { + joiner.add(addr.getCountry()); + } + if (StringUtils.isNotBlank(addr.getProvince())) { + joiner.add(addr.getProvince()); + } + if (StringUtils.isNotBlank(addr.getCity())) { + joiner.add(addr.getCity()); + } + return joiner.toString(); + } + +} diff --git a/xtools-boot-ip/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports b/xtools-boot-ip/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports new file mode 100644 index 0000000..e7697a4 --- /dev/null +++ b/xtools-boot-ip/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports @@ -0,0 +1 @@ +xtools.boot.ip.BootIpConfiguration \ No newline at end of file diff --git a/xtools-boot-ip/src/main/resources/ip/ip2region_v4.xdb b/xtools-boot-ip/src/main/resources/ip/ip2region_v4.xdb new file mode 100644 index 0000000..6f86c7d Binary files /dev/null and b/xtools-boot-ip/src/main/resources/ip/ip2region_v4.xdb differ diff --git a/xtools-boot-job/pom.xml b/xtools-boot-job/pom.xml new file mode 100644 index 0000000..bd63f38 --- /dev/null +++ b/xtools-boot-job/pom.xml @@ -0,0 +1,19 @@ + + + 4.0.0 + + org.xujun + xtools-boot + 5.0.0 + + pom + xtools-boot-job + + + + xtools-boot-job-xxl + + + \ No newline at end of file diff --git a/xtools-boot-job/xtools-boot-job-xxl/README.md b/xtools-boot-job/xtools-boot-job-xxl/README.md new file mode 100644 index 0000000..36fbf74 --- /dev/null +++ b/xtools-boot-job/xtools-boot-job-xxl/README.md @@ -0,0 +1,41 @@ +# xxl-job使用 + +## 官网地址 + +[xxl-job官网](https://www.xuxueli.com/xxl-job) + +## 操作说明 + +> 1.先安装xxl-job-admin +> 2.配置application-boot-xxl-job.yaml文件,可以参考application-boot-xxl-job-demo.yaml +> 3.运行项目,即可在admin配置定时任务了 + +## docker命令 + +```sh +# docker命令启动 +docker run -d \ +-e PARAMS=" \ +--spring.datasource.url=jdbc:mysql://devmysql.xujun.org:11010/xxl_job?useUnicode=true&characterEncoding=UTF-8&autoReconnect=true&serverTimezone=Asia/Shanghai \ +--spring.datasource.username=demo \ +--spring.datasource.password=demo321 \ +--xxl.job.accessToken=xtools-app-xxl-job \ +" \ +-p 26000:8080 \ +-v /data1/xxl-job-admin/applogs:/data/applogs \ +--name xxl-job-admin \ +registry.cn-hangzhou.aliyuncs.com/xujun-public/xxl-job-admin:3.3.2 +``` + +## docker-compose + +> 参考docker-compose目录 + +## 访问 + +```sh +# 访问地址 +http://[ip]:[port]/xxl-job-admin +# 账号/密码 +admin/123456 +``` \ No newline at end of file diff --git a/xtools-boot-job/xtools-boot-job-xxl/docker-compose/docker-compose.yml b/xtools-boot-job/xtools-boot-job-xxl/docker-compose/docker-compose.yml new file mode 100644 index 0000000..0d91c13 --- /dev/null +++ b/xtools-boot-job/xtools-boot-job-xxl/docker-compose/docker-compose.yml @@ -0,0 +1,23 @@ +services: + xxl-job-admin: + container_name: xxl-job-admin + image: registry.cn-hangzhou.aliyuncs.com/xujun-public/xxl-job-admin:3.3.2 + pull_policy: always + ports: + - "26000:8080" + volumes: + - "/etc/localtime:/etc/localtime:ro" + - "/data1/xxl-job-admin/applogs:/data/applogs" + env_file: + - env/container.env + restart: always + networks: + - xxl_job_net + +networks: + xxl_job_net: + name: xxl_job_net + driver: bridge + ipam: + config: + - subnet: 172.201.0.0/16 \ No newline at end of file diff --git a/xtools-boot-job/xtools-boot-job-xxl/docker-compose/evn/container.env b/xtools-boot-job/xtools-boot-job-xxl/docker-compose/evn/container.env new file mode 100644 index 0000000..1f5735a --- /dev/null +++ b/xtools-boot-job/xtools-boot-job-xxl/docker-compose/evn/container.env @@ -0,0 +1,2 @@ +# xxl-job-admin配置 +PARAMS=--spring.datasource.url=jdbc:mysql://devmysql.xujun.org:11010/xxl_job?useUnicode=true&characterEncoding=UTF-8&autoReconnect=true&serverTimezone=Asia/Shanghai --spring.datasource.username=demo --spring.datasource.password=demo321 --xxl.job.accessToken=xtools-app-xxl-job \ No newline at end of file diff --git a/xtools-boot-job/xtools-boot-job-xxl/pom.xml b/xtools-boot-job/xtools-boot-job-xxl/pom.xml new file mode 100644 index 0000000..a7787ab --- /dev/null +++ b/xtools-boot-job/xtools-boot-job-xxl/pom.xml @@ -0,0 +1,36 @@ + + + 4.0.0 + + org.xujun + xtools-boot-job + 5.0.0 + + xtools-boot-job-xxl + + + + + + + org.xujun + xtools-boot-core + + + + + + com.xuxueli + xxl-job-core + + + + + org.springframework.boot + spring-boot + + + + \ No newline at end of file diff --git a/xtools-boot-job/xtools-boot-job-xxl/src/main/java/xtools/boot/job/xxl/BootXxlJobConfiguration.java b/xtools-boot-job/xtools-boot-job-xxl/src/main/java/xtools/boot/job/xxl/BootXxlJobConfiguration.java new file mode 100644 index 0000000..97c80d9 --- /dev/null +++ b/xtools-boot-job/xtools-boot-job-xxl/src/main/java/xtools/boot/job/xxl/BootXxlJobConfiguration.java @@ -0,0 +1,28 @@ +package xtools.boot.job.xxl; + +import org.springframework.context.annotation.Import; +import xtools.boot.core.utils.ModuleLoadUtils; +import xtools.boot.job.xxl.selector.BootXxlJobImportSelector; + +/** + *

Title : BootXxlJobConfiguration

+ *

Description : BootXxlJobConfiguration

+ *

DevelopTools : Idea_x64_v2026.1

+ *

DevelopSystem : macOS Sequoia 15.7.5

+ *

Company : org.xujun

+ * + * @author : XuJun + * @version : 5.0.0 + * @date : 2026/01/01 09:30 + */ +@Import(BootXxlJobImportSelector.class) +public class BootXxlJobConfiguration { + + /** + * 构造方法 + */ + public BootXxlJobConfiguration() { + ModuleLoadUtils.loadSuccess(BootXxlJobConfiguration.class); + } + +} diff --git a/xtools-boot-job/xtools-boot-job-xxl/src/main/java/xtools/boot/job/xxl/config/XxlJobConfig.java b/xtools-boot-job/xtools-boot-job-xxl/src/main/java/xtools/boot/job/xxl/config/XxlJobConfig.java new file mode 100644 index 0000000..28b2c7a --- /dev/null +++ b/xtools-boot-job/xtools-boot-job-xxl/src/main/java/xtools/boot/job/xxl/config/XxlJobConfig.java @@ -0,0 +1,125 @@ +package xtools.boot.job.xxl.config; + +import com.xxl.job.core.executor.impl.XxlJobSpringExecutor; +import lombok.Getter; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import xtools.core.StringUtils; + +/** + *

Title : XxlJobConfig

+ *

Description : XxlJobConfig

+ *

DevelopTools : Idea_x64_v2026.1

+ *

DevelopSystem : macOS Sequoia 15.7.5

+ *

Company : org.xujun

+ * + * @author : XuJun + * @version : 5.0.0 + * @date : 2026/3/10 14:06 + */ +@Slf4j +@Configuration +public class XxlJobConfig { + + /** + * 调度中心部署根地址 [选填]:如调度中心集群部署存在多个地址则用逗号分隔.执行器将会使用该地址进行"执行器心跳注册"和"任务结果回调";为空则关闭自动注册; + */ + @Value("${xxl.job.admin.addresses}") + private String adminAddresses; + + /** + * 调度中心通讯TOKEN [选填]:非空时启用; + */ + @Value("${xxl.job.admin.accessToken}") + private String accessToken; + + /** + * 调度中心通讯超时时间[选填],单位秒;默认3s; + */ + @Value("${xxl.job.admin.timeout}") + private int timeout; + + /** + * 执行器启用开关 [选填]:默认开启,关闭时不进行执行器初始化; + */ + @Getter + @Value("${xxl.job.executor.enabled:false}") + private Boolean enabled; + + /** + * 执行器AppName [选填]:执行器心跳注册分组依据;为空则关闭自动注册 + */ + @Value("${xxl.job.executor.appName}") + private String appName; + + /** + * 执行器注册 [选填]:优先使用该配置作为注册地址,为空时使用内嵌服务 ”IP:PORT“ 作为注册地址.从而更灵活支持容器类型执行器动态IP和动态映射端口问题. + */ + @Value("${xxl.job.executor.address}") + private String address; + + /** + * 执行器IP [选填]:默认为空表示自动获取IP,多网卡时可手动设置指定IP,该IP不会绑定Host仅作为通讯使用;地址信息用于 "执行器注册" 和 "调度中心请求并触发任务"; + */ + @Value("${xxl.job.executor.ip}") + private String ip; + + /** + * 执行器端口号 [选填]:小于等于0则自动获取;默认端口为9999,单机部署多个执行器时,注意要配置不同执行器端口; + */ + @Value("${xxl.job.executor.port}") + private int port; + + /** + * 执行器运行日志文件存储磁盘路径 [选填] :需要对该路径拥有读写权限;为空则使用默认路径; + */ + @Value("${xxl.job.executor.logPath}") + private String logPath; + + /** + * 执行器日志文件保存天数 [选填] : 过期日志自动清理, 限制值大于等于3时生效; 否则, 如-1, 关闭自动清理功能; + */ + @Value("${xxl.job.executor.logRetentionDays}") + private int logRetentionDays; + + /** + * 任务扫描排除路径 [选填] :任务扫描时忽略指定包路径下的Bean;支持配置包路径前缀,多个逗号分隔; + */ + @Value("${xxl.job.executor.excludedPackage}") + private String excludedPackage; + + /** + * xxl-job 执行器 + * + * @return xxl-job 执行器 + */ + @Bean + public XxlJobSpringExecutor xxlJobExecutor() { + XxlJobSpringExecutor xxlJobSpringExecutor = new XxlJobSpringExecutor(); + xxlJobSpringExecutor.setAdminAddresses(adminAddresses); + xxlJobSpringExecutor.setAccessToken(accessToken); + xxlJobSpringExecutor.setTimeout(timeout); + xxlJobSpringExecutor.setEnabled(enabled); + xxlJobSpringExecutor.setAppname(appName); + if (StringUtils.isNotBlank(address)) { + xxlJobSpringExecutor.setAddress(address); + } + if (StringUtils.isNotBlank(ip)) { + xxlJobSpringExecutor.setIp(ip); + } + if (port > 0) { + xxlJobSpringExecutor.setPort(port); + } + if (StringUtils.isNotBlank(logPath)) { + xxlJobSpringExecutor.setLogPath(logPath); + } + xxlJobSpringExecutor.setLogRetentionDays(logRetentionDays); + if (StringUtils.isNotBlank(excludedPackage)) { + xxlJobSpringExecutor.setExcludedPackage(excludedPackage); + } + return xxlJobSpringExecutor; + } + +} diff --git a/xtools-boot-job/xtools-boot-job-xxl/src/main/java/xtools/boot/job/xxl/init/InitXxlJob.java b/xtools-boot-job/xtools-boot-job-xxl/src/main/java/xtools/boot/job/xxl/init/InitXxlJob.java new file mode 100644 index 0000000..81763e8 --- /dev/null +++ b/xtools-boot-job/xtools-boot-job-xxl/src/main/java/xtools/boot/job/xxl/init/InitXxlJob.java @@ -0,0 +1,97 @@ +package xtools.boot.job.xxl.init; + +import com.xxl.job.core.executor.XxlJobExecutor; +import com.xxl.job.core.handler.IJobHandler; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.jspecify.annotations.NonNull; +import org.springframework.boot.ApplicationArguments; +import org.springframework.boot.ApplicationRunner; +import org.springframework.core.annotation.Order; +import org.springframework.stereotype.Component; +import xtools.base.config.BaseParams; +import xtools.boot.core.interfaces.JobInterface; +import xtools.boot.core.utils.SpringContextUtils; +import xtools.boot.job.xxl.config.XxlJobConfig; +import xtools.core.CollectionUtils; + +import java.util.Collection; +import java.util.Objects; + +/** + *

Title : InitIp

+ *

Description : InitIp

+ *

DevelopTools : Idea_x64_v2026.1

+ *

DevelopSystem : macOS Sequoia 15.7.5

+ *

Company : org.xujun

+ * + * @author : XuJun + * @version : 5.0.0 + * @date : 2026/2/16 10:21 + */ +@Slf4j +@Component +@RequiredArgsConstructor +@Order(BaseParams.CP_NUM90) +public class InitXxlJob implements ApplicationRunner, BaseParams { + + private final XxlJobConfig xxlJobConfig; + + @Override + public void run(@NonNull ApplicationArguments args) { + init(); + } + + /** + * 初始化 + */ + private void init() { + Boolean enabled = xxlJobConfig.getEnabled(); + if (Objects.isNull(enabled) || !enabled) { + log.info("xxl-job未启用"); + return; + } + Collection beanList = SpringContextUtils.getBeanList(JobInterface.class); + if (CollectionUtils.isEmpty(beanList)) { + log.info("没有需要执行xxl-job的任务"); + return; + } + beanList.forEach(item -> { + try { + String name = item.getClass().getSimpleName(); + name = Character.toLowerCase(name.charAt(CP_NUM0)) + name.substring(CP_NUM1); + registerXxlJob(name, item); + log.info("注册xxl-job任务成功:{}", name); + } catch (Exception e) { + log.error("注册xxl-job任务异常", e); + } + }); + } + + /** + * 注册任务 + * + * @param name 任务名称 + * @param job 任务 + */ + private void registerXxlJob(String name, JobInterface job) { + IJobHandler handler = new IJobHandler() { + @Override + public void execute() throws Exception { + job.execute(); + } + + @Override + public void init() { + job.init(); + } + + @Override + public void destroy() { + job.destroy(); + } + }; + // 注册到XXL-JOB + XxlJobExecutor.registryJobHandler(name, handler); + } +} diff --git a/xtools-boot-job/xtools-boot-job-xxl/src/main/java/xtools/boot/job/xxl/selector/BootXxlJobImportSelector.java b/xtools-boot-job/xtools-boot-job-xxl/src/main/java/xtools/boot/job/xxl/selector/BootXxlJobImportSelector.java new file mode 100644 index 0000000..d8812eb --- /dev/null +++ b/xtools-boot-job/xtools-boot-job-xxl/src/main/java/xtools/boot/job/xxl/selector/BootXxlJobImportSelector.java @@ -0,0 +1,36 @@ +package xtools.boot.job.xxl.selector; + +import org.jspecify.annotations.NonNull; +import org.springframework.beans.factory.support.BeanDefinitionRegistry; +import org.springframework.context.annotation.ClassPathBeanDefinitionScanner; +import org.springframework.context.annotation.ImportBeanDefinitionRegistrar; +import org.springframework.core.type.AnnotationMetadata; + +/** + *

Title : BootXxlJobImportSelector

+ *

Description : BootXxlJobImportSelector

+ *

DevelopTools : Idea_x64_v2026.1

+ *

DevelopSystem : macOS Sequoia 15.7.5

+ *

Company : org.xujun

+ * + * @author : XuJun + * @version : 5.0.0 + * @date : 2026/01/01 09:30 + */ +public class BootXxlJobImportSelector implements ImportBeanDefinitionRegistrar { + + /** + * 根据给定的注释元数据,根据需要注册bean + * + * @param importingClassMetadata AnnotationMetadata + * @param registry BeanDefinitionRegistry + */ + @Override + public void registerBeanDefinitions(@NonNull AnnotationMetadata importingClassMetadata, @NonNull BeanDefinitionRegistry registry) { + // 构建扫描对象 + ClassPathBeanDefinitionScanner scanner = new ClassPathBeanDefinitionScanner(registry, true); + // 扫描包下路径 + scanner.scan("xtools.boot.job.xxl"); + } + +} \ No newline at end of file diff --git a/xtools-boot-job/xtools-boot-job-xxl/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports b/xtools-boot-job/xtools-boot-job-xxl/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports new file mode 100644 index 0000000..e88d062 --- /dev/null +++ b/xtools-boot-job/xtools-boot-job-xxl/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports @@ -0,0 +1 @@ +xtools.boot.job.xxl.BootXxlJobConfiguration \ No newline at end of file diff --git a/xtools-boot-job/xtools-boot-job-xxl/src/main/resources/application-boot-xxl-job-demo.yaml b/xtools-boot-job/xtools-boot-job-xxl/src/main/resources/application-boot-xxl-job-demo.yaml new file mode 100644 index 0000000..e704268 --- /dev/null +++ b/xtools-boot-job/xtools-boot-job-xxl/src/main/resources/application-boot-xxl-job-demo.yaml @@ -0,0 +1,26 @@ +xxl: + job: + admin: + # 调度中心部署根地址 [选填]:如调度中心集群部署存在多个地址则用逗号分隔.执行器将会使用该地址进行"执行器心跳注册"和"任务结果回调";为空则关闭自动注册; + addresses: http://10.1.0.12:8080/xxl-job-admin + # 调度中心通讯TOKEN [选填]:非空时启用; + accessToken: xtools-app-xxl-job + # 调度中心通讯超时时间[选填],单位秒;默认3s; + timeout: 3 + executor: + # 执行器启用开关 [选填]:默认开启,关闭时不进行执行器初始化; + enabled: true + # 执行器AppName [选填]:执行器心跳注册分组依据;为空则关闭自动注册 + appName: xtools-cloud + # 执行器注册 [选填]:优先使用该配置作为注册地址,为空时使用内嵌服务 ”IP:PORT“ 作为注册地址.从而更灵活支持容器类型执行器动态IP和动态映射端口问题. + address: "" + # 执行器IP [选填]:默认为空表示自动获取IP,多网卡时可手动设置指定IP,该IP不会绑定Host仅作为通讯使用;地址信息用于 "执行器注册" 和 "调度中心请求并触发任务"; + ip: "" + # 执行器端口号 [选填]:小于等于0则自动获取;默认端口为9999,单机部署多个执行器时,注意要配置不同执行器端口; + port: 9999 + # 执行器运行日志文件存储磁盘路径 [选填] :需要对该路径拥有读写权限;为空则使用默认路径; + logPath: /Users/xujun/Downloads/xtools + # 执行器日志文件保存天数 [选填] : 过期日志自动清理, 限制值大于等于3时生效; 否则, 如-1, 关闭自动清理功能; + logRetentionDays: 30 + # 任务扫描排除路径 [选填] :任务扫描时忽略指定包路径下的Bean;支持配置包路径前缀,多个逗号分隔; + excludedPackage: "" \ No newline at end of file diff --git a/xtools-boot-knife4j/pom.xml b/xtools-boot-knife4j/pom.xml new file mode 100644 index 0000000..a424494 --- /dev/null +++ b/xtools-boot-knife4j/pom.xml @@ -0,0 +1,37 @@ + + + 4.0.0 + + org.xujun + xtools-boot + 5.0.0 + + xtools-boot-knife4j + + + + + + + org.xujun + xtools-boot-core + + + + + + + org.springdoc + springdoc-openapi-starter-webmvc-ui + + + + com.github.xiaoymin + knife4j-openapi3-jakarta-spring-boot-starter + + + + + \ No newline at end of file diff --git a/xtools-boot-knife4j/src/main/java/xtools/boot/knife4j/BootKnife4jConfiguration.java b/xtools-boot-knife4j/src/main/java/xtools/boot/knife4j/BootKnife4jConfiguration.java new file mode 100644 index 0000000..c58cdeb --- /dev/null +++ b/xtools-boot-knife4j/src/main/java/xtools/boot/knife4j/BootKnife4jConfiguration.java @@ -0,0 +1,28 @@ +package xtools.boot.knife4j; + +import org.springframework.context.annotation.Import; +import xtools.boot.core.utils.ModuleLoadUtils; +import xtools.boot.knife4j.selector.BootKnife4jImportSelector; + +/** + *

Title : BootKnife4jConfiguration

+ *

Description : BootKnife4jConfiguration

+ *

DevelopTools : Idea_x64_v2026.1

+ *

DevelopSystem : macOS Sequoia 15.7.5

+ *

Company : org.xujun

+ * + * @author : XuJun + * @version : 5.0.0 + * @date : 2026/01/01 09:30 + */ +@Import(BootKnife4jImportSelector.class) +public class BootKnife4jConfiguration { + + /** + * 构造方法 + */ + public BootKnife4jConfiguration() { + ModuleLoadUtils.loadSuccess(BootKnife4jConfiguration.class); + } + +} diff --git a/xtools-boot-knife4j/src/main/java/xtools/boot/knife4j/config/Knife4jFilterWhitelist.java b/xtools-boot-knife4j/src/main/java/xtools/boot/knife4j/config/Knife4jFilterWhitelist.java new file mode 100644 index 0000000..2a1945e --- /dev/null +++ b/xtools-boot-knife4j/src/main/java/xtools/boot/knife4j/config/Knife4jFilterWhitelist.java @@ -0,0 +1,35 @@ +package xtools.boot.knife4j.config; + +import org.springframework.stereotype.Component; +import xtools.boot.core.interfaces.FilterWhitelist; + +import java.util.Set; + +/** + *

Title : Knife4jFilterWhitelist

+ *

Description : Knife4jFilterWhitelist

+ *

DevelopTools : Idea_x64_v2026.1

+ *

DevelopSystem : macOS Sequoia 15.7.5

+ *

Company : org.xujun

+ * + * @author : XuJun + * @version : 5.0.0 + * @date : 2026/3/8 12:28 + */ +@Component +public class Knife4jFilterWhitelist implements FilterWhitelist { + + /** + * 添加过滤器白名单 + * + * @return 过滤器白名单 + */ + @Override + public Set add() { + return Set.of( + "/doc.html", + "/webjars/**", + "/v3/api-docs/**" + ); + } +} diff --git a/xtools-boot-knife4j/src/main/java/xtools/boot/knife4j/selector/BootKnife4jImportSelector.java b/xtools-boot-knife4j/src/main/java/xtools/boot/knife4j/selector/BootKnife4jImportSelector.java new file mode 100644 index 0000000..eb125f5 --- /dev/null +++ b/xtools-boot-knife4j/src/main/java/xtools/boot/knife4j/selector/BootKnife4jImportSelector.java @@ -0,0 +1,36 @@ +package xtools.boot.knife4j.selector; + +import org.jspecify.annotations.NonNull; +import org.springframework.beans.factory.support.BeanDefinitionRegistry; +import org.springframework.context.annotation.ClassPathBeanDefinitionScanner; +import org.springframework.context.annotation.ImportBeanDefinitionRegistrar; +import org.springframework.core.type.AnnotationMetadata; + +/** + *

Title : BootKnife4jImportSelector

+ *

Description : BootKnife4jImportSelector

+ *

DevelopTools : Idea_x64_v2026.1

+ *

DevelopSystem : macOS Sequoia 15.7.5

+ *

Company : org.xujun

+ * + * @author : XuJun + * @version : 5.0.0 + * @date : 2026/01/01 09:30 + */ +public class BootKnife4jImportSelector implements ImportBeanDefinitionRegistrar { + + /** + * 根据给定的注释元数据,根据需要注册bean + * + * @param importingClassMetadata AnnotationMetadata + * @param registry BeanDefinitionRegistry + */ + @Override + public void registerBeanDefinitions(@NonNull AnnotationMetadata importingClassMetadata, @NonNull BeanDefinitionRegistry registry) { + // 构建扫描对象 + ClassPathBeanDefinitionScanner scanner = new ClassPathBeanDefinitionScanner(registry, true); + // 扫描包下路径 + scanner.scan("xtools.boot.knife4j"); + } + +} \ No newline at end of file diff --git a/xtools-boot-knife4j/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports b/xtools-boot-knife4j/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports new file mode 100644 index 0000000..8eb9826 --- /dev/null +++ b/xtools-boot-knife4j/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports @@ -0,0 +1 @@ +xtools.boot.knife4j.BootKnife4jConfiguration \ No newline at end of file diff --git a/xtools-boot-log/pom.xml b/xtools-boot-log/pom.xml new file mode 100644 index 0000000..4edef1f --- /dev/null +++ b/xtools-boot-log/pom.xml @@ -0,0 +1,36 @@ + + + 4.0.0 + + org.xujun + xtools-boot + 5.0.0 + + xtools-boot-log + + + + + + + org.xujun + xtools-boot-core + + + + + + com.alibaba.fastjson2 + fastjson2 + + + + + org.springframework.boot + spring-boot + + + + \ No newline at end of file diff --git a/xtools-boot-log/src/main/java/xtools/boot/log/BootLogConfiguration.java b/xtools-boot-log/src/main/java/xtools/boot/log/BootLogConfiguration.java new file mode 100644 index 0000000..547ddf8 --- /dev/null +++ b/xtools-boot-log/src/main/java/xtools/boot/log/BootLogConfiguration.java @@ -0,0 +1,28 @@ +package xtools.boot.log; + +import org.springframework.context.annotation.Import; +import xtools.boot.core.utils.ModuleLoadUtils; +import xtools.boot.log.selector.BootLogImportSelector; + +/** + *

Title : BootLogConfiguration

+ *

Description : BootLogConfiguration

+ *

DevelopTools : Idea_x64_v2026.1

+ *

DevelopSystem : macOS Sequoia 15.7.5

+ *

Company : org.xujun

+ * + * @author : XuJun + * @version : 5.0.0 + * @date : 2026/01/01 09:30 + */ +@Import(BootLogImportSelector.class) +public class BootLogConfiguration { + + /** + * 构造方法 + */ + public BootLogConfiguration() { + ModuleLoadUtils.loadSuccess(BootLogConfiguration.class); + } + +} diff --git a/xtools-boot-log/src/main/java/xtools/boot/log/LogBus.java b/xtools-boot-log/src/main/java/xtools/boot/log/LogBus.java new file mode 100644 index 0000000..c8d3496 --- /dev/null +++ b/xtools-boot-log/src/main/java/xtools/boot/log/LogBus.java @@ -0,0 +1,380 @@ +package xtools.boot.log; + +import com.alibaba.fastjson2.JSONArray; +import com.alibaba.fastjson2.JSONObject; +import com.alibaba.fastjson2.JSONWriter; +import lombok.extern.slf4j.Slf4j; +import xtools.base.config.BaseParams; +import xtools.base.exception.CommonException; +import xtools.base.exception.ExceptionUtils; +import xtools.boot.api.enums.ThreadType; +import xtools.boot.api.model.dto.log.LogTrack; +import xtools.boot.core.utils.SpringContextUtils; +import xtools.boot.log.config.LogBusConfig; +import xtools.boot.log.config.RunInfoConfig; +import xtools.boot.log.holder.LogTrackHolder; +import xtools.boot.log.interfaces.LogBusInterface; +import xtools.boot.log.interfaces.LogBusType; +import xtools.boot.log.model.dto.LogBody; +import xtools.boot.log.model.dto.RunInfo; +import xtools.core.ArrUtils; +import xtools.core.CollectionUtils; +import xtools.core.StringUtils; +import xtools.core.enums.LogLevel; +import xtools.core.enums.TimePattern; +import xtools.core.sys.SysBaseInfoUtils; +import xtools.core.time.InstantUtils; + +import java.util.Arrays; +import java.util.Collection; +import java.util.List; +import java.util.Objects; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; + +/** + *

Title : LogBus

+ *

Description : LogBus

+ *

DevelopTools : Idea_x64_v2026.1

+ *

DevelopSystem : macOS Sequoia 15.7.5

+ *

Company : org.xujun

+ * + * @author : XuJun + * @version : 5.0.0 + * @date : 2026/2/3 09:33 + */ +@Slf4j +public class LogBus implements BaseParams { + + /** + * 运行信息 + */ + private static RunInfo runInfo; + + /** + * 日志体 + */ + private final LogBody logBody; + + /** + * 异常信息 + */ + private Throwable throwable; + + /** + * 是否在控制台打印 + */ + private Boolean print; + + /** + * 构造方法 + * + * @param level 日志级别 + * @param type 日志类型 + * @param logTrack 日志信息 + */ + private LogBus(LogLevel level, LogBusType type, LogTrack logTrack) { + // 参数处理 + if (Objects.isNull(level)) { + level = LogLevel.INFO; + } + if (Objects.isNull(logTrack)) { + logTrack = LogTrackHolder.get(); + } + // 构建日志信息 + logBody = new LogBody(); + logBody.setLevel(level); + logBody.setType(type.desc()); + logBody.setLogTrack(logTrack); + logBody.setRunInfo(getRunInfo()); + } + + /** + * 初始化 + * + * @param type 日志类型 + * @return 当前对象 + */ + public static LogBus init(LogBusType type) { + return new LogBus(null, type, null); + } + + /** + * 初始化 + * + * @param level 日志级别 + * @param type 日志类型 + * @return 当前对象 + */ + public static LogBus init(LogLevel level, LogBusType type) { + return new LogBus(level, type, null); + } + + /** + * 初始化 + * + * @param level 日志级别 + * @param type 日志类型 + * @param logTrack 日志信息 + * @return 当前对象 + */ + public static LogBus init(LogLevel level, LogBusType type, LogTrack logTrack) { + return new LogBus(level, type, logTrack); + } + + /** + * 格式化日志 + * + * @param logBody 日志信息 + * @return 日志信息 + */ + private static StringBuilder fmtLog(LogBody logBody) { + // 日志标题 + String title = logBody.getTitle(); + if (StringUtils.isBlank(title)) { + title = "日志信息"; + } + LogTrack logTrack = logBody.getLogTrack(); + // 日志时间 + String time = InstantUtils.format(InstantUtils.from(logTrack.time()), TimePattern.ALL); + // 线程类型 + ThreadType threadType = logTrack.type(); + // 堆栈信息 + JSONArray stackTraceInfo = logBody.getStackTrace(); + // 日志数据 + JSONObject data = logBody.getLogData(); + // 构建打印日志 + StringBuilder sb = new StringBuilder(); + sb.append(CP_NEWLINE); + sb.append("┏━━━ ").append(title).append(" Start ━━━").append(CP_NEWLINE); + sb.append("┠ ").append("日志ID: ").append(logTrack.id()).append(CP_NEWLINE); + sb.append("┠ ").append("日志时间: ").append(time).append(CP_NEWLINE); + sb.append("┠ ").append("追踪ID: ").append(logTrack.traceId()).append(CP_NEWLINE); + sb.append("┠ ").append("线程类型: ").append(threadType.desc()).append(CP_NEWLINE); + if (!ThreadType.MAIN.equals(threadType)) { + sb.append("┠ ").append("父线程ID: ").append(logTrack.parentId()).append(CP_NEWLINE); + } + sb.append("┠ ").append("日志级别: ").append(logBody.getLevel().level()).append(CP_NEWLINE); + sb.append("┠ ").append("日志类型: ").append(logBody.getType()).append(CP_NEWLINE); + sb.append("┠ ").append("运行环境: ").append(CP_NEWLINE).append(JSONObject.toJSONString(logBody.getRunInfo(), JSONWriter.Feature.PrettyFormat)).append(CP_NEWLINE); + if (CollectionUtils.isNotEmpty(stackTraceInfo)) { + sb.append("┠ ").append("堆栈信息: ").append(CP_NEWLINE).append(stackTraceInfo.toJSONString(JSONWriter.Feature.PrettyFormat)).append(CP_NEWLINE); + } + if (CollectionUtils.isNotEmpty(data)) { + sb.append("┠ ").append("日志数据: ").append(CP_NEWLINE).append(data.toJSONString(JSONWriter.Feature.PrettyFormat)).append(CP_NEWLINE); + } + sb.append("┗━━━ ").append(title).append(" End ━━━").append(CP_NEWLINE); + return sb; + } + + /** + * 格式化堆栈信息 + * + * @param stackTraceInfo 堆栈信息 + * @param includeStackTrace 包含的堆栈信息(开始的包名) + * @return 堆栈信息 + */ + private static JSONArray fmtStackTrace(StackTraceElement[] stackTraceInfo, List includeStackTrace) { + // 获取当前线程的调用栈 + if (ArrUtils.isEmpty(stackTraceInfo)) { + return null; + } + JSONArray result = new JSONArray(); + if (CollectionUtils.isEmpty(includeStackTrace)) { + // 获取所有堆栈数据 + Arrays.stream(stackTraceInfo).toList().forEach((item) -> result.add(item.toString())); + return result; + } + + // 获取关于本工程堆栈信息 + for (StackTraceElement item : stackTraceInfo) { + String className = item.getClassName(); + + boolean flag = false; + for (String include : includeStackTrace) { + if (className.startsWith(include)) { + flag = true; + break; + } + } + if (flag) { + result.add(item.toString()); + } + } + return result; + } + + /** + * 日志标题 + * + * @param title 标题 + * @return 当前对象 + */ + public LogBus title(String title) { + if (StringUtils.isNotBlank(title)) { + logBody.setTitle(title); + } + return this; + } + + /** + * 是否打印 + * + * @param print 是否打印 + * @return 当前对象 + */ + public LogBus print(boolean print) { + this.print = print; + return this; + } + + /** + * 设置日志信息 + * + * @param log 日志信息 + * @return 当前对象 + */ + public LogBus data(final Object log) { + if (Objects.isNull(log)) { + return this; + } + if (log instanceof JSONObject jsonLog) { + logBody.setLogData(jsonLog); + } else { + logBody.setLogData(JSONObject.from(log)); + } + return this; + } + + /** + * 设置异常信息 + * + * @param throwable 异常信息 + * @return 当前对象 + */ + public LogBus error(Throwable throwable) { + this.throwable = throwable; + return this; + } + + /** + * 保存日志 + */ + public void save() { + // 获取堆栈信息 + StackTraceElement[] stackTrace = Thread.currentThread().getStackTrace(); + ExecutorService service = Executors.newVirtualThreadPerTaskExecutor(); + try { + // 创建子线程,并异步提交 + CompletableFuture.runAsync(() -> { + try { + doSave(logBody, throwable, stackTrace); + } catch (Exception e) { + log.error("保存日志异常", e); + } + }, service); + } finally { + // 关闭线程池 + service.shutdown(); + } + } + + /** + * 获取运行信息 + * + * @return 运行信息 + */ + private RunInfo getRunInfo() { + if (runInfo != null) { + return runInfo; + } + RunInfoConfig sysRunInfo = SpringContextUtils.getBean(RunInfoConfig.class); + RunInfo info = new RunInfo(); + info.setLocalIp(SysBaseInfoUtils.getLocalIp()); + info.setAppName(sysRunInfo.getName()); + info.setPort(sysRunInfo.getPort()); + runInfo = info; + return runInfo; + } + + /** + * 执行日志发送 + * + * @param logBody 日志内容 + * @param throwable 异常 + * @param stackTrace 堆栈信息 + */ + private void doSave(LogBody logBody, Throwable throwable, StackTraceElement[] stackTrace) { + // 获取配置文件 + LogBusConfig config = SpringContextUtils.getBean(LogBusConfig.class); + + // 计算日志级 + boolean geWarn = logBody.getLevel().code() > LogLevel.INFO.code(); + // 处理堆栈信息 + logBody.setStackTrace(fmtStackTrace(stackTrace, geWarn ? null : config.getIncludeStackTrace())); + + // 是否打印日志 + if (geWarn || (Objects.nonNull(print) ? print : config.getPrint().booleanValue())) { + printLog(logBody); + } + // 异常日志不做忽略 + if (!geWarn) { + // 不保存日志 + if (!logBody.getLogTrack().save()) { + return; + } + } + // 处理日志内容超长 + JSONObject data = logBody.getLogData(); + if (CollectionUtils.isNotEmpty(data)) { + Integer logDataItemMax = config.getLogDataMaxLength(); + if (!Objects.equals(logDataItemMax, CP_NEGATIVE1)) { + String dataStr = data.toString(); + if (dataStr.length() > logDataItemMax) { + JSONObject newLogData = JSONObject.of( + "msg", "日志内容超长", + "log", dataStr.substring(CP_NUM0, logDataItemMax) + "..." + ); + logBody.setLogData(newLogData); + } + } + } + // 处理异常信息 + if (Objects.nonNull(throwable)) { + if (throwable instanceof CommonException commonException) { + String message = commonException.getMessage(); + Throwable cause = commonException.getCause(); + String errMsg = ExceptionUtils.getErrMsg(Objects.nonNull(cause) ? cause : throwable); + logBody.setErr(message + CP_NEWLINE + errMsg); + } else { + logBody.setErr(ExceptionUtils.getErrMsg(throwable)); + } + } + + // 保存日志信息 + Collection beanList = SpringContextUtils.getBeanList(LogBusInterface.class); + if (CollectionUtils.isNotEmpty(beanList)) { + beanList.forEach(item -> item.save(logBody)); + } + } + + /** + * 打印日志信息 + * + * @param logBody 日志信息 + */ + private void printLog(LogBody logBody) { + // 格式化日志 + StringBuilder logContext = fmtLog(logBody); + // 异常数据 + if (Objects.isNull(throwable)) { + logContext.append(CP_NEWLINE); + log.info(logContext.toString()); + } else { + logContext.append("━━━━ ↓↓↓ exception ↓↓↓ ━━━━").append(CP_NEWLINE); + log.error(logContext.toString(), throwable); + } + } + +} diff --git a/xtools-boot-log/src/main/java/xtools/boot/log/config/LogBusConfig.java b/xtools-boot-log/src/main/java/xtools/boot/log/config/LogBusConfig.java new file mode 100644 index 0000000..77ffb5a --- /dev/null +++ b/xtools-boot-log/src/main/java/xtools/boot/log/config/LogBusConfig.java @@ -0,0 +1,40 @@ +package xtools.boot.log.config; + +import lombok.Data; +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.context.annotation.Configuration; + +import java.util.ArrayList; +import java.util.List; + +/** + *

Title : LogBusConfig

+ *

Description : LogBusConfig

+ *

DevelopTools : Idea_x64_v2026.1

+ *

DevelopSystem : macOS Sequoia 15.7.5

+ *

Company : org.xujun

+ * + * @author : XuJun + * @version : 5.0.0 + * @date : 2026/2/3 15:07 + */ +@Data +@Configuration +@ConfigurationProperties(prefix = "sys.log.bus") +public class LogBusConfig { + + /** + * 是否打印日志 + */ + private Boolean print = false; + + /** + * 包含的堆栈信息(开始的包名) + */ + private List includeStackTrace = new ArrayList<>(); + + /** + * 日志项最大长度 + */ + private Integer logDataMaxLength = -1; +} diff --git a/xtools-boot-log/src/main/java/xtools/boot/log/config/RunInfoConfig.java b/xtools-boot-log/src/main/java/xtools/boot/log/config/RunInfoConfig.java new file mode 100644 index 0000000..9b0263d --- /dev/null +++ b/xtools-boot-log/src/main/java/xtools/boot/log/config/RunInfoConfig.java @@ -0,0 +1,33 @@ +package xtools.boot.log.config; + +import lombok.Getter; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Component; + +/** + *

Title : RunInfoConfig

+ *

Description : RunInfoConfig

+ *

DevelopTools : Idea_x64_v2026.1

+ *

DevelopSystem : macOS Sequoia 15.7.5

+ *

Company : org.xujun

+ * + * @author : XuJun + * @version : 5.0.0 + * @date : 2026/2/3 14:17 + */ +@Getter +@Component +public class RunInfoConfig { + + /** + * 应用名称 + */ + @Value("${spring.application.name:xtools-boot}") + private String name; + + /** + * 应用端口 + */ + @Value("${server.port:8080}") + private int port; +} diff --git a/xtools-boot-log/src/main/java/xtools/boot/log/enums/LogBusBaseType.java b/xtools-boot-log/src/main/java/xtools/boot/log/enums/LogBusBaseType.java new file mode 100644 index 0000000..301bc49 --- /dev/null +++ b/xtools-boot-log/src/main/java/xtools/boot/log/enums/LogBusBaseType.java @@ -0,0 +1,100 @@ +package xtools.boot.log.enums; + +import xtools.boot.api.enums.BaseEnum; +import xtools.boot.log.interfaces.LogBusType; + +/** + *

Title : LogBusBaseType

+ *

Description : LogBusBaseType

+ *

DevelopTools : Idea_x64_v2026.1

+ *

DevelopSystem : macOS Sequoia 15.7.5

+ *

Company : org.xujun

+ * + * @author : XuJun + * @version : 5.0.0 + * @date : 2026/2/3 11:26 + */ +public enum LogBusBaseType implements LogBusType { + + // 其他 + OTHER(0, "其他"), + // THREAD + VIRTUAL_THREAD(1, "虚拟线程"), + // Redis + REDIS(10, "Redis"), + // MyBatis + MYBATIS(20, "MyBatis"), + // MQ + MQ(30, "MQ"), + // Elasticsearch + ELASTICSEARCH(40, "Elasticsearch"), + // Sentinel + SENTINEL(50, "Sentinel"), + // Http + HTTP(80, "Http工具"), + HTTP_REQUEST(81, "Http请求"), + HTTP_RESPONSE(82, "Http响应"), + // Cloud + CLOUD_REQUEST(85, "Cloud请求"), + CLOUD_RESPONSE(86, "Cloud响应"), + // Controller + CONTROLLER(90, "Controller"), + // Task + TASK(91, "Task"), + // Job + JOB(92, "Job"), + // Risk + RISK(93, "Risk"), + ; + + /** + * 编码 + **/ + private final int code; + + /** + * 说明 + **/ + private final String desc; + + /** + * 初始化方法 + * + * @param code Code + * @param desc 说明 + */ + LogBusBaseType(int code, String desc) { + this.code = code; + this.desc = desc; + } + + /** + * 获取所有枚举 + * + * @return 所有枚举 + */ + @Override + public BaseEnum[] all() { + return values(); + } + + /** + * 获取枚举编码 + * + * @return 枚举编码 + */ + @Override + public int code() { + return code; + } + + /** + * 获取枚举说明 + * + * @return 枚举说明 + */ + @Override + public String desc() { + return desc; + } +} diff --git a/xtools-boot-log/src/main/java/xtools/boot/log/holder/LogTrackHolder.java b/xtools-boot-log/src/main/java/xtools/boot/log/holder/LogTrackHolder.java new file mode 100644 index 0000000..7519407 --- /dev/null +++ b/xtools-boot-log/src/main/java/xtools/boot/log/holder/LogTrackHolder.java @@ -0,0 +1,191 @@ +package xtools.boot.log.holder; + +import com.alibaba.fastjson2.JSONObject; +import com.alibaba.fastjson2.JSONWriter; +import xtools.base.exception.CommonException; +import xtools.boot.api.enums.BootError; +import xtools.boot.api.enums.ThreadType; +import xtools.boot.api.model.dto.log.HolderLogTrack; +import xtools.boot.api.model.dto.log.LogTrack; +import xtools.core.UuidUtils; +import xtools.core.encrypt.Base64Utils; + +import java.nio.charset.StandardCharsets; +import java.time.Instant; +import java.util.Objects; + +/** + *

Title : LogTrackHolder

+ *

Description : LogTrackHolder

+ *

DevelopTools : Idea_x64_v2026.1

+ *

DevelopSystem : macOS Sequoia 15.7.5

+ *

Company : org.xujun

+ * + * @author : XuJun + * @version : 5.0.0 + * @date : 2026/1/6 16:36 + */ +public class LogTrackHolder { + + /** + * 日志追踪信息 + */ + private static final ScopedValue LOG_TRACK_INFO = ScopedValue.newInstance(); + + /** + * 获取日志追踪保持信息 + * + * @return 日志追踪保持信息 + */ + public static ScopedValue getScoped() { + return LOG_TRACK_INFO; + } + + + /** + * 新建主线程日志追踪信息 + * + * @return 日志追踪信息 + */ + public static HolderLogTrack newMain() { + ThreadType main = ThreadType.MAIN; + return new HolderLogTrack(UuidUtils.get(), main.name(), main); + } + + /** + * 新建线程日志追踪信息 + * + * @param mainLogTrack 主线程日志追踪信息 + * @return 日志追踪信息 + */ + public static HolderLogTrack newThread(LogTrack mainLogTrack) { + HolderLogTrack holderLogTrack = new HolderLogTrack(mainLogTrack.traceId(), mainLogTrack.id(), ThreadType.THREAD); + holderLogTrack.setSave(mainLogTrack.save()); + return holderLogTrack; + } + + /** + * 新建 Holder 日志追踪信息 + * + * @param logTrack 日志追踪信息 + * @return 日志追踪信息 + */ + public static HolderLogTrack newHolderLogTrack(LogTrack logTrack) { + HolderLogTrack holderLogTrack = new HolderLogTrack(logTrack.traceId(), logTrack.parentId(), logTrack.type(), logTrack.index()); + holderLogTrack.setSave(logTrack.save()); + return holderLogTrack; + } + + /** + * 新建 Holder 日志追踪信息 + * + * @param base64 日志追踪信息 + * @return 日志追踪信息 + */ + public static HolderLogTrack newByBase64(String base64) { + String log = Base64Utils.decodeToStr(base64.getBytes(StandardCharsets.UTF_8)); + LogTrack logTrack = JSONObject.parseObject(log, LogTrack.class); + return newThread(logTrack); + } + + /** + * 获取日志追踪信息 + * + * @return 日志追踪信息 + */ + public static LogTrack get() { + Instant now = Instant.now(); + HolderLogTrack holder = getHolder(); + holder.getCount().getAndIncrement(); + return new LogTrack( + UuidUtils.get(), + now.toEpochMilli(), + holder.getTraceId(), + holder.getParentId(), + holder.getType(), + holder.getCount().get(), + holder.isSave() + ); + } + + /** + * 获取日志追踪信息 + * + * @return 日志追踪信息 + */ + public static LogTrack getDefNull() { + Instant now = Instant.now(); + HolderLogTrack holder = getHolderDefNull(); + if (Objects.isNull(holder)) { + return null; + } + holder.getCount().getAndIncrement(); + return new LogTrack( + UuidUtils.get(), + now.toEpochMilli(), + holder.getTraceId(), + holder.getParentId(), + holder.getType(), + holder.getCount().get(), + holder.isSave() + ); + } + + /** + * 获取日志追踪信息 + * + * @return 日志追踪信息 + */ + public static String getBase64() { + LogTrack logTrack = getDefNull(); + return getBase64(logTrack); + } + + /** + * 获取日志追踪信息 + * + * @param logTrack 日志追踪信息 + * @return 日志追踪信息 + */ + public static String getBase64(LogTrack logTrack) { + if (Objects.isNull(logTrack)) { + return null; + } + String logTxt = JSONObject.toJSONString(logTrack, JSONWriter.Feature.IgnoreEmpty); + return Base64Utils.encodeToStr(logTxt.getBytes(StandardCharsets.UTF_8)); + } + + /** + * 是否保存日志 + * + * @param save true 保存日志, false 不保存日志 + */ + public static void saveLog(boolean save) { + getHolder().setSave(save); + } + + /** + * 获取 Holder 日志追踪信息 + * + * @return Holder 日志追踪信息 + */ + private static HolderLogTrack getHolder() { + if (!LOG_TRACK_INFO.isBound()) { + throw CommonException.create(BootError.LOG_HOLDER); + } + return LOG_TRACK_INFO.get(); + } + + /** + * 获取 Holder 日志追踪信息 + * + * @return Holder 日志追踪信息 + */ + private static HolderLogTrack getHolderDefNull() { + if (!LOG_TRACK_INFO.isBound()) { + return null; + } + return LOG_TRACK_INFO.get(); + } + +} diff --git a/xtools-boot-log/src/main/java/xtools/boot/log/interfaces/LogBusInterface.java b/xtools-boot-log/src/main/java/xtools/boot/log/interfaces/LogBusInterface.java new file mode 100644 index 0000000..8d4adb7 --- /dev/null +++ b/xtools-boot-log/src/main/java/xtools/boot/log/interfaces/LogBusInterface.java @@ -0,0 +1,25 @@ +package xtools.boot.log.interfaces; + +import xtools.boot.log.model.dto.LogBody; + +/** + *

Title : LogBusInterface

+ *

Description : LogBusInterface

+ *

DevelopTools : Idea_x64_v2026.1

+ *

DevelopSystem : macOS Sequoia 15.7.5

+ *

Company : org.xujun

+ * + * @author : XuJun + * @version : 5.0.0 + * @date : 2026/2/3 16:56 + */ +public interface LogBusInterface { + + /** + * 保存日志 + * + * @param logBody 日志 + */ + void save(LogBody logBody); + +} diff --git a/xtools-boot-log/src/main/java/xtools/boot/log/interfaces/LogBusType.java b/xtools-boot-log/src/main/java/xtools/boot/log/interfaces/LogBusType.java new file mode 100644 index 0000000..dfe6c05 --- /dev/null +++ b/xtools-boot-log/src/main/java/xtools/boot/log/interfaces/LogBusType.java @@ -0,0 +1,23 @@ +package xtools.boot.log.interfaces; + +import xtools.boot.api.enums.BaseEnum; + +/** + *

Title : LogBusType

+ *

Description : LogBusType

+ *

DevelopTools : Idea_x64_v2026.1

+ *

DevelopSystem : macOS Sequoia 15.7.5

+ *

Company : org.xujun

+ * + * @author : XuJun + * @version : 5.0.0 + * @date : 2026/2/3 10:31 + */ +public interface LogBusType extends BaseEnum { + + /** + * 基础编码 + */ + int BASE_CODE = 100; + +} diff --git a/xtools-boot-log/src/main/java/xtools/boot/log/model/dto/LogBody.java b/xtools-boot-log/src/main/java/xtools/boot/log/model/dto/LogBody.java new file mode 100644 index 0000000..46ff08c --- /dev/null +++ b/xtools-boot-log/src/main/java/xtools/boot/log/model/dto/LogBody.java @@ -0,0 +1,64 @@ +package xtools.boot.log.model.dto; + +import com.alibaba.fastjson2.JSONArray; +import com.alibaba.fastjson2.JSONObject; +import lombok.Data; +import xtools.boot.api.model.dto.log.LogTrack; +import xtools.core.enums.LogLevel; + +import java.io.Serializable; + +/** + *

Title : LogBody

+ *

Description : LogBody

+ *

DevelopTools : Idea_x64_v2026.1

+ *

DevelopSystem : macOS Sequoia 15.7.5

+ *

Company : org.xujun

+ * + * @author : XuJun + * @version : 5.0.0 + * @date : 2026/2/3 09:37 + */ +@Data +public class LogBody implements Serializable { + + /** + * 日志主题 + */ + private String title; + + /** + * 日志级别 + */ + private LogLevel level; + + /** + * 日志类型 + */ + private String type; + + /** + * 日志链信息 + */ + private LogTrack logTrack; + + /** + * 运行环境信息 + */ + private RunInfo runInfo; + + /** + * 堆栈信息 + */ + private JSONArray stackTrace; + + /** + * 日志数据 + */ + private JSONObject logData; + + /** + * 异常信息(文本) + */ + private String err; +} diff --git a/xtools-boot-log/src/main/java/xtools/boot/log/model/dto/RunInfo.java b/xtools-boot-log/src/main/java/xtools/boot/log/model/dto/RunInfo.java new file mode 100644 index 0000000..588bc13 --- /dev/null +++ b/xtools-boot-log/src/main/java/xtools/boot/log/model/dto/RunInfo.java @@ -0,0 +1,35 @@ +package xtools.boot.log.model.dto; + +import lombok.Data; + +import java.io.Serializable; + +/** + *

Title : RunEvInfo

+ *

Description : RunEvInfo

+ *

DevelopTools : Idea_x64_v2026.1

+ *

DevelopSystem : macOS Sequoia 15.7.5

+ *

Company : org.xujun

+ * + * @author : XuJun + * @version : 5.0.0 + * @date : 2026/2/3 09:38 + */ +@Data +public class RunInfo implements Serializable { + + /** + * 服务名称 + */ + private String appName; + + /** + * 本机IP + */ + private String localIp; + + /** + * 运行端口 + */ + private int port; +} diff --git a/xtools-boot-log/src/main/java/xtools/boot/log/selector/BootLogImportSelector.java b/xtools-boot-log/src/main/java/xtools/boot/log/selector/BootLogImportSelector.java new file mode 100644 index 0000000..8bdcad7 --- /dev/null +++ b/xtools-boot-log/src/main/java/xtools/boot/log/selector/BootLogImportSelector.java @@ -0,0 +1,36 @@ +package xtools.boot.log.selector; + +import org.jspecify.annotations.NonNull; +import org.springframework.beans.factory.support.BeanDefinitionRegistry; +import org.springframework.context.annotation.ClassPathBeanDefinitionScanner; +import org.springframework.context.annotation.ImportBeanDefinitionRegistrar; +import org.springframework.core.type.AnnotationMetadata; + +/** + *

Title : BootLogImportSelector

+ *

Description : BootLogImportSelector

+ *

DevelopTools : Idea_x64_v2026.1

+ *

DevelopSystem : macOS Sequoia 15.7.5

+ *

Company : org.xujun

+ * + * @author : XuJun + * @version : 5.0.0 + * @date : 2026/01/01 09:30 + */ +public class BootLogImportSelector implements ImportBeanDefinitionRegistrar { + + /** + * 根据给定的注释元数据,根据需要注册bean + * + * @param importingClassMetadata AnnotationMetadata + * @param registry BeanDefinitionRegistry + */ + @Override + public void registerBeanDefinitions(@NonNull AnnotationMetadata importingClassMetadata, @NonNull BeanDefinitionRegistry registry) { + // 构建扫描对象 + ClassPathBeanDefinitionScanner scanner = new ClassPathBeanDefinitionScanner(registry, true); + // 扫描包下路径 + scanner.scan("xtools.boot.log"); + } + +} \ No newline at end of file diff --git a/xtools-boot-log/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports b/xtools-boot-log/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports new file mode 100644 index 0000000..04ed11f --- /dev/null +++ b/xtools-boot-log/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports @@ -0,0 +1 @@ +xtools.boot.log.BootLogConfiguration \ No newline at end of file diff --git a/xtools-boot-mask/pom.xml b/xtools-boot-mask/pom.xml new file mode 100644 index 0000000..2cfca62 --- /dev/null +++ b/xtools-boot-mask/pom.xml @@ -0,0 +1,30 @@ + + + 4.0.0 + + org.xujun + xtools-boot + 5.0.0 + + xtools-boot-mask + + + + + + + org.xujun + xtools-boot-core + + + + + + tools.jackson.core + jackson-databind + + + + \ No newline at end of file diff --git a/xtools-boot-mask/src/main/java/xtools/boot/mask/BootMaskConfiguration.java b/xtools-boot-mask/src/main/java/xtools/boot/mask/BootMaskConfiguration.java new file mode 100644 index 0000000..0c4f888 --- /dev/null +++ b/xtools-boot-mask/src/main/java/xtools/boot/mask/BootMaskConfiguration.java @@ -0,0 +1,28 @@ +package xtools.boot.mask; + +import org.springframework.context.annotation.Import; +import xtools.boot.core.utils.ModuleLoadUtils; +import xtools.boot.mask.selector.BootMaskImportSelector; + +/** + *

Title : BootMaskConfiguration

+ *

Description : BootMaskConfiguration

+ *

DevelopTools : Idea_x64_v2026.1

+ *

DevelopSystem : macOS Sequoia 15.7.5

+ *

Company : org.xujun

+ * + * @author : XuJun + * @version : 5.0.0 + * @date : 2026/01/01 09:30 + */ +@Import(BootMaskImportSelector.class) +public class BootMaskConfiguration { + + /** + * 构造方法 + */ + public BootMaskConfiguration() { + ModuleLoadUtils.loadSuccess(BootMaskConfiguration.class); + } + +} diff --git a/xtools-boot-mask/src/main/java/xtools/boot/mask/MaskCustom.java b/xtools-boot-mask/src/main/java/xtools/boot/mask/MaskCustom.java new file mode 100644 index 0000000..7e172b5 --- /dev/null +++ b/xtools-boot-mask/src/main/java/xtools/boot/mask/MaskCustom.java @@ -0,0 +1,27 @@ +package xtools.boot.mask; + +import xtools.boot.mask.anntation.Mask; + +/** + *

Title : MaskCustom

+ *

Description : MaskCustom

+ *

DevelopTools : Idea_x64_v2026.1

+ *

DevelopSystem : macOS Sequoia 15.7.5

+ *

Company : org.xujun

+ * + * @author : XuJun + * @version : 5.0.0 + * @date : 2026/1/9 10:08 + */ +public interface MaskCustom { + + /** + * 脱敏处理 + * + * @param value 待处理的字符串 + * @param mask 脱敏注解 + * @return 脱敏后的字符串 + */ + String mask(String value, Mask mask); + +} diff --git a/xtools-boot-mask/src/main/java/xtools/boot/mask/anntation/Mask.java b/xtools-boot-mask/src/main/java/xtools/boot/mask/anntation/Mask.java new file mode 100644 index 0000000..f32646f --- /dev/null +++ b/xtools-boot-mask/src/main/java/xtools/boot/mask/anntation/Mask.java @@ -0,0 +1,74 @@ +package xtools.boot.mask.anntation; + +import com.fasterxml.jackson.annotation.JacksonAnnotationsInside; +import tools.jackson.databind.annotation.JsonSerialize; +import xtools.boot.mask.MaskCustom; +import xtools.boot.mask.enums.MaskType; +import xtools.boot.mask.handle.DefaultMaskHandle; +import xtools.boot.mask.serializer.MaskSerializer; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + *

Title : Mask

+ *

Description : Mask

+ *

DevelopTools : Idea_x64_v2026.1

+ *

DevelopSystem : macOS Sequoia 15.7.5

+ *

Company : org.xujun

+ * + * @author : XuJun + * @version : 5.0.0 + * @date : 2026/1/9 10:07 + */ +@Target({ElementType.FIELD}) +@Retention(RetentionPolicy.RUNTIME) +@JacksonAnnotationsInside +@JsonSerialize(using = MaskSerializer.class) +public @interface Mask { + + /** + * 脱敏类型 + * + * @return 脱敏类型 + */ + MaskType value(); + + /** + * 是否总是脱敏(无法忽略脱敏) + * + * @return 是否总是脱敏 + */ + boolean always() default false; + + /** + * 前缀不脱敏长度 + * + * @return 前缀不脱敏长度 + */ + int prefixNoMaskLen() default 0; + + /** + * 后缀不脱敏长度 + * + * @return 后缀不脱敏长度 + */ + int suffixNoMaskLen() default 0; + + /** + * 脱敏替代字符 + * + * @return 脱敏替代字符 + */ + String maskChar() default "*"; + + /** + * 自定义脱敏接口 + * + * @return 自定义脱敏接口 + */ + Class custom() default DefaultMaskHandle.class; + +} diff --git a/xtools-boot-mask/src/main/java/xtools/boot/mask/enums/MaskType.java b/xtools-boot-mask/src/main/java/xtools/boot/mask/enums/MaskType.java new file mode 100644 index 0000000..8d4bda4 --- /dev/null +++ b/xtools-boot-mask/src/main/java/xtools/boot/mask/enums/MaskType.java @@ -0,0 +1,33 @@ +package xtools.boot.mask.enums; + +/** + *

Title : MaskType

+ *

Description : MaskType

+ *

DevelopTools : Idea_x64_v2026.1

+ *

DevelopSystem : macOS Sequoia 15.7.5

+ *

Company : org.xujun

+ * + * @author : XuJun + * @version : 5.0.0 + * @date : 2026/1/9 10:07 + */ +public enum MaskType { + + // 中文名 + CHINESE_NAME, + // 身份证号 + ID_CARD, + // 手机号 + MOBILE_PHONE, + // 邮箱 + EMAIL, + // 银行卡 + BANK_CARD, + // 密码 + PASSWORD, + // 地址 + ADDRESS, + // 自定义 + CUSTOM + +} diff --git a/xtools-boot-mask/src/main/java/xtools/boot/mask/handle/DefaultMaskHandle.java b/xtools-boot-mask/src/main/java/xtools/boot/mask/handle/DefaultMaskHandle.java new file mode 100644 index 0000000..945f9a9 --- /dev/null +++ b/xtools-boot-mask/src/main/java/xtools/boot/mask/handle/DefaultMaskHandle.java @@ -0,0 +1,43 @@ +package xtools.boot.mask.handle; + +import xtools.boot.mask.MaskCustom; +import xtools.boot.mask.anntation.Mask; + +/** + *

Title : DefaultMaskHandle

+ *

Description : DefaultMaskHandle

+ *

DevelopTools : Idea_x64_v2026.1

+ *

DevelopSystem : macOS Sequoia 15.7.5

+ *

Company : org.xujun

+ * + * @author : XuJun + * @version : 5.0.0 + * @date : 2026/2/4 15:19 + */ +public class DefaultMaskHandle implements MaskCustom { + + /** + * 脱敏处理 + * + * @param value 待处理的字符串 + * @param mask 脱敏注解 + * @return 脱敏后的字符串 + */ + @Override + public String mask(String value, Mask mask) { + final int length = value.length(); + int prefixNoMaskLen = mask.prefixNoMaskLen(); + int suffixNoMaskLen = mask.suffixNoMaskLen(); + if (prefixNoMaskLen > 0 && length <= prefixNoMaskLen) { + return value; + } + if (suffixNoMaskLen > 0 && length <= suffixNoMaskLen) { + return value; + } + int maskLength = length - prefixNoMaskLen - suffixNoMaskLen; + if (maskLength <= 0) { + return value; + } + return value.substring(0, prefixNoMaskLen) + mask.maskChar().repeat(maskLength) + value.substring(length - suffixNoMaskLen); + } +} diff --git a/xtools-boot-mask/src/main/java/xtools/boot/mask/selector/BootMaskImportSelector.java b/xtools-boot-mask/src/main/java/xtools/boot/mask/selector/BootMaskImportSelector.java new file mode 100644 index 0000000..c699546 --- /dev/null +++ b/xtools-boot-mask/src/main/java/xtools/boot/mask/selector/BootMaskImportSelector.java @@ -0,0 +1,36 @@ +package xtools.boot.mask.selector; + +import org.jspecify.annotations.NonNull; +import org.springframework.beans.factory.support.BeanDefinitionRegistry; +import org.springframework.context.annotation.ClassPathBeanDefinitionScanner; +import org.springframework.context.annotation.ImportBeanDefinitionRegistrar; +import org.springframework.core.type.AnnotationMetadata; + +/** + *

Title : BootMaskImportSelector

+ *

Description : BootMaskImportSelector

+ *

DevelopTools : Idea_x64_v2026.1

+ *

DevelopSystem : macOS Sequoia 15.7.5

+ *

Company : org.xujun

+ * + * @author : XuJun + * @version : 5.0.0 + * @date : 2026/01/01 09:30 + */ +public class BootMaskImportSelector implements ImportBeanDefinitionRegistrar { + + /** + * 根据给定的注释元数据,根据需要注册bean + * + * @param importingClassMetadata AnnotationMetadata + * @param registry BeanDefinitionRegistry + */ + @Override + public void registerBeanDefinitions(@NonNull AnnotationMetadata importingClassMetadata, @NonNull BeanDefinitionRegistry registry) { + // 构建扫描对象 + ClassPathBeanDefinitionScanner scanner = new ClassPathBeanDefinitionScanner(registry, true); + // 扫描包下路径 + scanner.scan("xtools.boot.mask"); + } + +} \ No newline at end of file diff --git a/xtools-boot-mask/src/main/java/xtools/boot/mask/serializer/MaskSerializer.java b/xtools-boot-mask/src/main/java/xtools/boot/mask/serializer/MaskSerializer.java new file mode 100644 index 0000000..e00d256 --- /dev/null +++ b/xtools-boot-mask/src/main/java/xtools/boot/mask/serializer/MaskSerializer.java @@ -0,0 +1,110 @@ +package xtools.boot.mask.serializer; + +import lombok.extern.slf4j.Slf4j; +import tools.jackson.core.JacksonException; +import tools.jackson.core.JsonGenerator; +import tools.jackson.core.TokenStreamContext; +import tools.jackson.databind.SerializationContext; +import tools.jackson.databind.ValueSerializer; +import xtools.boot.mask.MaskCustom; +import xtools.boot.mask.anntation.Mask; +import xtools.boot.mask.utils.MaskIgnoreUtils; +import xtools.core.StringUtils; +import xtools.core.extend.MaskUtils; + +import java.lang.reflect.Field; +import java.util.Objects; + +/** + *

Title : MaskSerializer

+ *

Description : MaskSerializer

+ *

DevelopTools : Idea_x64_v2026.1

+ *

DevelopSystem : macOS Sequoia 15.7.5

+ *

Company : org.xujun

+ * + * @author : XuJun + * @version : 5.0.0 + * @date : 2026/1/9 10:08 + */ +@Slf4j +public class MaskSerializer extends ValueSerializer { + + /** + * 序列化字符串 + * + * @param value 待处理的字符串 + * @param gen JsonGenerator + * @param context SerializationContext + * @throws JacksonException JacksonException + */ + @Override + public void serialize(String value, JsonGenerator gen, SerializationContext context) throws JacksonException { + if (StringUtils.isBlank(value)) { + gen.writeString(value); + return; + } + value = value.trim(); + TokenStreamContext streamContext = gen.streamWriteContext(); + if (Objects.isNull(streamContext)) { + gen.writeString(value); + return; + } + try { + Field field = streamContext.currentValue().getClass().getDeclaredField(streamContext.currentName()); + // 获取注解 + Mask mask = field.getAnnotation(Mask.class); + if (mask == null) { + gen.writeString(value); + return; + } + // 判断是否总是脱敏 + if (!mask.always()) { + // 判断是否忽略脱敏 + if (MaskIgnoreUtils.isIgnore()) { + gen.writeString(value); + return; + } + } + gen.writeString(mask(value, mask)); + } catch (Exception e) { + log.error("脱敏处理异常", e); + gen.writeString(value); + } + } + + /** + * 脱敏处理 + * + * @param value 待处理的字符串 + * @param mask 脱敏注解 + * @return 脱敏后的字符串 + * @throws Exception 异常 + */ + private String mask(String value, Mask mask) throws Exception { + String maskString = mask.maskChar(); + return switch (mask.value()) { + case PASSWORD -> MaskUtils.password(value, maskString); + case MOBILE_PHONE -> MaskUtils.mobilePhone(value, maskString); + case EMAIL -> MaskUtils.email(value, maskString); + case CHINESE_NAME -> MaskUtils.chineseName(value, maskString); + case ADDRESS -> MaskUtils.address(value, maskString); + case ID_CARD -> MaskUtils.idCard(value, maskString); + case BANK_CARD -> MaskUtils.bankCard(value, maskString); + case CUSTOM -> customMask(value, mask); + }; + } + + /** + * 自定义脱敏处理 + * + * @param value 待处理的字符串 + * @param mask 脱敏注解 + * @return 脱敏后的字符串 + * @throws Exception 异常 + */ + private String customMask(String value, Mask mask) throws Exception { + Class custom = mask.custom(); + return custom.getDeclaredConstructor().newInstance().mask(value, mask); + } + +} diff --git a/xtools-boot-mask/src/main/java/xtools/boot/mask/utils/MaskIgnoreUtils.java b/xtools-boot-mask/src/main/java/xtools/boot/mask/utils/MaskIgnoreUtils.java new file mode 100644 index 0000000..4a1fa3e --- /dev/null +++ b/xtools-boot-mask/src/main/java/xtools/boot/mask/utils/MaskIgnoreUtils.java @@ -0,0 +1,39 @@ +package xtools.boot.mask.utils; + +import xtools.boot.core.holder.CommonHolder; + +/** + *

Title : MaskUtils

+ *

Description : MaskUtils

+ *

DevelopTools : Idea_x64_v2026.1

+ *

DevelopSystem : macOS Sequoia 15.7.5

+ *

Company : org.xujun

+ * + * @author : XuJun + * @version : 5.0.0 + * @date : 2026/2/9 16:38 + */ +public class MaskIgnoreUtils { + + /** + * 忽略脱敏 + */ + private static final String IGNORE = "mask-ignore"; + + /** + * 忽略脱敏 + */ + public static void ignore() { + CommonHolder.set(IGNORE, true); + } + + /** + * 是否忽略脱敏 + * + * @return true 忽略, false 不忽略 + */ + public static boolean isIgnore() { + return CommonHolder.getBoolean(IGNORE); + } + +} diff --git a/xtools-boot-mask/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports b/xtools-boot-mask/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports new file mode 100644 index 0000000..63891a7 --- /dev/null +++ b/xtools-boot-mask/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports @@ -0,0 +1 @@ +xtools.boot.mask.BootMaskConfiguration \ No newline at end of file diff --git a/xtools-boot-mq/pom.xml b/xtools-boot-mq/pom.xml new file mode 100644 index 0000000..de4673e --- /dev/null +++ b/xtools-boot-mq/pom.xml @@ -0,0 +1,20 @@ + + + 4.0.0 + + org.xujun + xtools-boot + 5.0.0 + + pom + xtools-boot-mq + + + + xtools-boot-mq-base + xtools-boot-mq-rabbit + + + \ No newline at end of file diff --git a/xtools-boot-mq/xtools-boot-mq-base/pom.xml b/xtools-boot-mq/xtools-boot-mq-base/pom.xml new file mode 100644 index 0000000..11c6fc7 --- /dev/null +++ b/xtools-boot-mq/xtools-boot-mq-base/pom.xml @@ -0,0 +1,40 @@ + + + 4.0.0 + + org.xujun + xtools-boot-mq + 5.0.0 + + xtools-boot-mq-base + + + + + + + org.xujun + xtools-boot-core + + + + org.xujun + xtools-boot-log + + + + org.xujun + xtools-boot-thread + + + + + + com.alibaba.fastjson2 + fastjson2 + + + + \ No newline at end of file diff --git a/xtools-boot-mq/xtools-boot-mq-base/src/main/java/xtools/boot/mq/base/BootMqBaseConfiguration.java b/xtools-boot-mq/xtools-boot-mq-base/src/main/java/xtools/boot/mq/base/BootMqBaseConfiguration.java new file mode 100644 index 0000000..c1d13e0 --- /dev/null +++ b/xtools-boot-mq/xtools-boot-mq-base/src/main/java/xtools/boot/mq/base/BootMqBaseConfiguration.java @@ -0,0 +1,28 @@ +package xtools.boot.mq.base; + +import org.springframework.context.annotation.Import; +import xtools.boot.core.utils.ModuleLoadUtils; +import xtools.boot.mq.base.selector.BootMqBaseImportSelector; + +/** + *

Title : BootMqBaseConfiguration

+ *

Description : BootMqBaseConfiguration

+ *

DevelopTools : Idea_x64_v2026.1

+ *

DevelopSystem : macOS Sequoia 15.7.5

+ *

Company : org.xujun

+ * + * @author : XuJun + * @version : 5.0.0 + * @date : 2026/01/01 09:30 + */ +@Import(BootMqBaseImportSelector.class) +public class BootMqBaseConfiguration { + + /** + * 构造方法 + */ + public BootMqBaseConfiguration() { + ModuleLoadUtils.loadSuccess(BootMqBaseConfiguration.class); + } + +} diff --git a/xtools-boot-mq/xtools-boot-mq-base/src/main/java/xtools/boot/mq/base/enums/MqEnums.java b/xtools-boot-mq/xtools-boot-mq-base/src/main/java/xtools/boot/mq/base/enums/MqEnums.java new file mode 100644 index 0000000..58b4bb0 --- /dev/null +++ b/xtools-boot-mq/xtools-boot-mq-base/src/main/java/xtools/boot/mq/base/enums/MqEnums.java @@ -0,0 +1,30 @@ +package xtools.boot.mq.base.enums; + +/** + *

Title : MqEnums

+ *

Description : MqEnums

+ *

DevelopTools : Idea_x64_v2026.1

+ *

DevelopSystem : macOS Sequoia 15.7.5

+ *

Company : org.xujun

+ * + * @author : XuJun + * @version : 5.0.0 + * @date : 2026/2/5 16:46 + */ +public interface MqEnums { + + /** + * 获取队列名称 + * + * @return 队列名称 + */ + String queue(); + + /** + * 是否保存日志 + * + * @return 是否保存日志 + */ + boolean saveLog(); + +} diff --git a/xtools-boot-mq/xtools-boot-mq-base/src/main/java/xtools/boot/mq/base/handle/BaseErrorHandle.java b/xtools-boot-mq/xtools-boot-mq-base/src/main/java/xtools/boot/mq/base/handle/BaseErrorHandle.java new file mode 100644 index 0000000..713a7c5 --- /dev/null +++ b/xtools-boot-mq/xtools-boot-mq-base/src/main/java/xtools/boot/mq/base/handle/BaseErrorHandle.java @@ -0,0 +1,24 @@ +package xtools.boot.mq.base.handle; + +/** + *

Title : BaseErrorHandle

+ *

Description : BaseErrorHandle

+ *

DevelopTools : Idea_x64_v2026.1

+ *

DevelopSystem : macOS Sequoia 15.7.5

+ *

Company : org.xujun

+ * + * @author : XuJun + * @version : 5.0.0 + * @date : 2026/2/12 10:46 + */ +public interface BaseErrorHandle { + + /** + * 消息处理错误异常(需要消息队列重推消息,抛出异常即可) + * + * @param message 消息内容 + * @param exception 异常信息 + */ + void messageHandle(String message, Exception exception); + +} diff --git a/xtools-boot-mq/xtools-boot-mq-base/src/main/java/xtools/boot/mq/base/handle/BaseMessageHandle.java b/xtools-boot-mq/xtools-boot-mq-base/src/main/java/xtools/boot/mq/base/handle/BaseMessageHandle.java new file mode 100644 index 0000000..e534504 --- /dev/null +++ b/xtools-boot-mq/xtools-boot-mq-base/src/main/java/xtools/boot/mq/base/handle/BaseMessageHandle.java @@ -0,0 +1,133 @@ +package xtools.boot.mq.base.handle; + +import com.alibaba.fastjson2.JSONObject; +import lombok.extern.slf4j.Slf4j; +import xtools.boot.api.model.dto.log.LogTrack; +import xtools.boot.core.utils.SpringContextUtils; +import xtools.boot.log.LogBus; +import xtools.boot.log.enums.LogBusBaseType; +import xtools.boot.log.holder.LogTrackHolder; +import xtools.boot.mq.base.enums.MqEnums; +import xtools.boot.mq.base.model.dto.MessageDto; +import xtools.boot.mq.base.utils.MqMessageUtils; +import xtools.core.enums.LogLevel; + +import java.util.Objects; +import java.util.concurrent.atomic.AtomicReference; + +/** + *

Title : BaseMessageHandle

+ *

Description : BaseMessageHandle

+ *

DevelopTools : Idea_x64_v2026.1

+ *

DevelopSystem : macOS Sequoia 15.7.5

+ *

Company : org.xujun

+ * + * @author : XuJun + * @version : 5.0.0 + * @date : 2026/2/11 17:03 + */ +@Slf4j +public abstract class BaseMessageHandle { + + /** + * 消息泛型类 + */ + private final Class clazz; + + /** + * 构造方法 + * + * @param clazz 泛型类 + */ + public BaseMessageHandle(Class clazz) { + this.clazz = clazz; + } + + /** + * 获取队列 + * + * @return 队列 + */ + public abstract MqEnums queue(); + + /** + * 获取监听方法 + * + * @return 监听方法 + */ + public String listenerMethod() { + return null; + } + + /** + * 获取参数 + * + * @return 参数 + */ + public Object params() { + return null; + } + + /** + * 自定义处理消息 + * + * @param message 消息内容 + * @throws Exception 异常信息 + */ + public abstract void handleMessage(T message) throws Exception; + + /** + * 基础消息处理 + * + * @param message 消息内容 + */ + public void baseHandleMessage(String message) { + // 解包消息 + MessageDto msg = MqMessageUtils.from(message, clazz); + final T data = msg.getData(); + if (Objects.isNull(data)) { + log.warn("消息内容为空"); + return; + } + AtomicReference err = new AtomicReference<>(); + LogTrack logTrack = msg.getLog(); + if (Objects.isNull(logTrack)) { + try { + handleMessage(data); + } catch (Exception e) { + err.set(e); + } + } else { + ScopedValue.where(LogTrackHolder.getScoped(), LogTrackHolder.newHolderLogTrack(logTrack)).run(() -> { + long startTime = System.currentTimeMillis(); + try { + if (logTrack.save()) { + LogBus.init(LogBusBaseType.MQ).title("MQ接收").data(msg).save(); + } + handleMessage(data); + } catch (Exception e) { + err.set(e); + } finally { + if (logTrack.save()) { + long endTime = System.currentTimeMillis(); + LogLevel level = Objects.isNull(err.get()) ? LogLevel.INFO : LogLevel.ERROR; + LogBus.init(level, LogBusBaseType.MQ) + .title("MQ处理完成") + .error(err.get()) + .data(JSONObject.of("execTime", endTime - startTime)) + .save(); + } + } + }); + } + Exception exception = err.get(); + if (Objects.isNull(exception)) { + return; + } + log.error("消息处理异常", exception); + BaseErrorHandle errorHandle = SpringContextUtils.getBeanDefNull(BaseErrorHandle.class); + if (Objects.nonNull(errorHandle)) { + errorHandle.messageHandle(message, exception); + } + } +} diff --git a/xtools-boot-mq/xtools-boot-mq-base/src/main/java/xtools/boot/mq/base/handle/BaseMqHandle.java b/xtools-boot-mq/xtools-boot-mq-base/src/main/java/xtools/boot/mq/base/handle/BaseMqHandle.java new file mode 100644 index 0000000..06ff94d --- /dev/null +++ b/xtools-boot-mq/xtools-boot-mq-base/src/main/java/xtools/boot/mq/base/handle/BaseMqHandle.java @@ -0,0 +1,41 @@ +package xtools.boot.mq.base.handle; + +/** + *

Title : BaseMqHandle

+ *

Description : BaseMqHandle

+ *

DevelopTools : Idea_x64_v2026.1

+ *

DevelopSystem : macOS Sequoia 15.7.5

+ *

Company : org.xujun

+ * + * @author : XuJun + * @version : 5.0.0 + * @date : 2026/2/12 09:23 + */ +public interface BaseMqHandle { + + /** + * 初始化消息队列 + * + * @param queueName 消息队列名称 + */ + void initQueue(String queueName); + + /** + * 添加消息队列监听 + * + * @param queueName 消息队列 + * @param delegate 监听类 + * @param listenerMethod 监听方法 + * @param params 参数 + */ + void addListen(String queueName, Object delegate, String listenerMethod, Object params); + + /** + * 推送消息 + * + * @param routingKey 路由 key + * @param data 消息数据 + */ + void push(String routingKey, Object data); + +} diff --git a/xtools-boot-mq/xtools-boot-mq-base/src/main/java/xtools/boot/mq/base/init/InitMq.java b/xtools-boot-mq/xtools-boot-mq-base/src/main/java/xtools/boot/mq/base/init/InitMq.java new file mode 100644 index 0000000..af9fcb4 --- /dev/null +++ b/xtools-boot-mq/xtools-boot-mq-base/src/main/java/xtools/boot/mq/base/init/InitMq.java @@ -0,0 +1,80 @@ +package xtools.boot.mq.base.init; + +import lombok.extern.slf4j.Slf4j; +import org.jspecify.annotations.NonNull; +import org.springframework.boot.ApplicationArguments; +import org.springframework.boot.ApplicationRunner; +import org.springframework.core.annotation.Order; +import org.springframework.stereotype.Component; +import xtools.base.config.BaseParams; +import xtools.base.exception.CommonException; +import xtools.boot.api.enums.BootError; +import xtools.boot.core.utils.SpringContextUtils; +import xtools.boot.log.LogBus; +import xtools.boot.log.enums.LogBusBaseType; +import xtools.boot.log.holder.LogTrackHolder; +import xtools.boot.mq.base.handle.BaseMessageHandle; +import xtools.boot.mq.base.handle.BaseMqHandle; +import xtools.core.CollectionUtils; +import xtools.core.enums.LogLevel; + +import java.util.Collection; +import java.util.Objects; + + +/** + *

Title : InitMq

+ *

Description : InitMq

+ *

DevelopTools : Idea_x64_v2026.1

+ *

DevelopSystem : macOS Sequoia 15.7.5

+ *

Company : org.xujun

+ * + * @author : XuJun + * @version : 5.0.0 + * @date : 2026/2/2 14:13 + */ +@Slf4j +@Component +@Order(BaseParams.CP_NUM50) +public class InitMq implements ApplicationRunner { + + @Override + public void run(@NonNull ApplicationArguments args) { + ScopedValue.where(LogTrackHolder.getScoped(), LogTrackHolder.newMain()).run(() -> { + try { + init(); + } catch (Exception e) { + LogBus.init(LogLevel.ERROR, LogBusBaseType.MQ).title("初始化异常").error(e).save(); + } + }); + + } + + /** + * 初始化 + */ + @SuppressWarnings("rawtypes") + private void init() { + BaseMqHandle mqHandle = SpringContextUtils.getBeanDefNull(BaseMqHandle.class); + if (Objects.isNull(mqHandle)) { + throw CommonException.create(BootError.MQ, "没有找到MQ处理适配器,请添加对应Jar包"); + } + + Collection beanList = SpringContextUtils.getBeanList(BaseMessageHandle.class); + if (CollectionUtils.isEmpty(beanList)) { + log.info("没有找到MQ的消息处理适配器"); + return; + } + beanList.forEach(item -> { + // 获取队列名称 + String queueName = item.queue().queue(); + try { + mqHandle.initQueue(queueName); + mqHandle.addListen(queueName, item, item.listenerMethod(), item.params()); + log.info("消息队列监听[{}]初始化成功", queueName); + } catch (Exception e) { + LogBus.init(LogLevel.ERROR, LogBusBaseType.MQ).title("初始化" + queueName + "异常").error(e).save(); + } + }); + } +} diff --git a/xtools-boot-mq/xtools-boot-mq-base/src/main/java/xtools/boot/mq/base/model/dto/MessageDto.java b/xtools-boot-mq/xtools-boot-mq-base/src/main/java/xtools/boot/mq/base/model/dto/MessageDto.java new file mode 100644 index 0000000..7ca688f --- /dev/null +++ b/xtools-boot-mq/xtools-boot-mq-base/src/main/java/xtools/boot/mq/base/model/dto/MessageDto.java @@ -0,0 +1,31 @@ +package xtools.boot.mq.base.model.dto; + +import lombok.AllArgsConstructor; +import lombok.Data; +import xtools.boot.api.model.dto.log.LogTrack; + +/** + *

Title : MessageDto

+ *

Description : MessageDto

+ *

DevelopTools : Idea_x64_v2026.1

+ *

DevelopSystem : macOS Sequoia 15.7.5

+ *

Company : org.xujun

+ * + * @author : XuJun + * @version : 5.0.0 + * @date : 2026/2/11 20:06 + */ +@Data +@AllArgsConstructor +public class MessageDto { + + /** + * 日志追踪信息 + */ + private LogTrack log; + + /** + * 消息内容 + */ + private T data; +} diff --git a/xtools-boot-mq/xtools-boot-mq-base/src/main/java/xtools/boot/mq/base/selector/BootMqBaseImportSelector.java b/xtools-boot-mq/xtools-boot-mq-base/src/main/java/xtools/boot/mq/base/selector/BootMqBaseImportSelector.java new file mode 100644 index 0000000..60b3b73 --- /dev/null +++ b/xtools-boot-mq/xtools-boot-mq-base/src/main/java/xtools/boot/mq/base/selector/BootMqBaseImportSelector.java @@ -0,0 +1,36 @@ +package xtools.boot.mq.base.selector; + +import org.jspecify.annotations.NonNull; +import org.springframework.beans.factory.support.BeanDefinitionRegistry; +import org.springframework.context.annotation.ClassPathBeanDefinitionScanner; +import org.springframework.context.annotation.ImportBeanDefinitionRegistrar; +import org.springframework.core.type.AnnotationMetadata; + +/** + *

Title : BootMqBaseImportSelector

+ *

Description : BootMqBaseImportSelector

+ *

DevelopTools : Idea_x64_v2026.1

+ *

DevelopSystem : macOS Sequoia 15.7.5

+ *

Company : org.xujun

+ * + * @author : XuJun + * @version : 5.0.0 + * @date : 2026/01/01 09:30 + */ +public class BootMqBaseImportSelector implements ImportBeanDefinitionRegistrar { + + /** + * 根据给定的注释元数据,根据需要注册bean + * + * @param importingClassMetadata AnnotationMetadata + * @param registry BeanDefinitionRegistry + */ + @Override + public void registerBeanDefinitions(@NonNull AnnotationMetadata importingClassMetadata, @NonNull BeanDefinitionRegistry registry) { + // 构建扫描对象 + ClassPathBeanDefinitionScanner scanner = new ClassPathBeanDefinitionScanner(registry, true); + // 扫描包下路径 + scanner.scan("xtools.boot.mq.base"); + } + +} \ No newline at end of file diff --git a/xtools-boot-mq/xtools-boot-mq-base/src/main/java/xtools/boot/mq/base/utils/MqBus.java b/xtools-boot-mq/xtools-boot-mq-base/src/main/java/xtools/boot/mq/base/utils/MqBus.java new file mode 100644 index 0000000..0723fa2 --- /dev/null +++ b/xtools-boot-mq/xtools-boot-mq-base/src/main/java/xtools/boot/mq/base/utils/MqBus.java @@ -0,0 +1,64 @@ +package xtools.boot.mq.base.utils; + +import com.alibaba.fastjson2.JSONObject; +import xtools.boot.api.exection.BizError; +import xtools.boot.api.model.dto.log.LogTrack; +import xtools.boot.core.utils.SpringContextUtils; +import xtools.boot.log.LogBus; +import xtools.boot.log.enums.LogBusBaseType; +import xtools.boot.log.holder.LogTrackHolder; +import xtools.boot.mq.base.enums.MqEnums; +import xtools.boot.mq.base.handle.BaseMqHandle; +import xtools.boot.thread.utils.VirtualThreadTaskUtils; +import xtools.core.enums.LogLevel; + +import java.util.Objects; + +/** + *

Title : MqBus

+ *

Description : MqBus

+ *

DevelopTools : Idea_x64_v2026.1

+ *

DevelopSystem : macOS Sequoia 15.7.5

+ *

Company : org.xujun

+ * + * @author : XuJun + * @version : 5.0.0 + * @date : 2026/2/12 10:00 + */ +public class MqBus { + + /** + * 推送到消息队列 + * + * @param mq 队列 + * @param data 数据 + */ + public static void push(MqEnums mq, Object data) { + BaseMqHandle mqHandle = SpringContextUtils.getBeanDefNull(BaseMqHandle.class); + if (Objects.isNull(mqHandle)) { + throw new BizError("没有找到MQ处理适配器,请添加对应Jar包"); + } + // 是否保存日志 + boolean saveLog = mq.saveLog(); + LogTrack logTrack; + if (saveLog) { + logTrack = LogTrackHolder.getDefNull(); + if (Objects.isNull(logTrack)) { + saveLog = false; + } else { + saveLog = logTrack.save(); + } + } + if (saveLog) { + VirtualThreadTaskUtils.execute(() -> { + LogTrack track = LogTrackHolder.get(); + JSONObject message = MqMessageUtils.to(data, track); + LogBus.init(LogLevel.INFO, LogBusBaseType.MQ, track).title("MQ推送").data(message).save(); + mqHandle.push(mq.queue(), message.toString()); + }); + } else { + VirtualThreadTaskUtils.simple(() -> mqHandle.push(mq.queue(), MqMessageUtils.to(data, null).toString())); + } + } + +} diff --git a/xtools-boot-mq/xtools-boot-mq-base/src/main/java/xtools/boot/mq/base/utils/MqMessageUtils.java b/xtools-boot-mq/xtools-boot-mq-base/src/main/java/xtools/boot/mq/base/utils/MqMessageUtils.java new file mode 100644 index 0000000..3ae3e40 --- /dev/null +++ b/xtools-boot-mq/xtools-boot-mq-base/src/main/java/xtools/boot/mq/base/utils/MqMessageUtils.java @@ -0,0 +1,53 @@ +package xtools.boot.mq.base.utils; + +import com.alibaba.fastjson2.JSONObject; +import xtools.boot.api.model.dto.log.LogTrack; +import xtools.boot.mq.base.model.dto.MessageDto; + +import java.io.Serializable; + +/** + *

Title : MqMessageUtils

+ *

Description : MqMessageUtils

+ *

DevelopTools : Idea_x64_v2026.1

+ *

DevelopSystem : macOS Sequoia 15.7.5

+ *

Company : org.xujun

+ * + * @author : XuJun + * @version : 5.0.0 + * @date : 2026/2/11 19:56 + */ +public class MqMessageUtils implements Serializable { + + + /** + * 消息包数据 Key + */ + private static final String LOG = "log"; + private static final String DATA = "data"; + + /** + * 构建消息包 + * + * @param data 消息内容 + * @param logTrack 日志追踪信息 + * @return java.lang.String + **/ + public static JSONObject to(Object data, LogTrack logTrack) { + return JSONObject.of(LOG, logTrack, DATA, data); + } + + /** + * 解析消息包 + * + * @param message 消息包 + * @param clazz 消息内容类型 + * @return MessageDto + **/ + public static MessageDto from(String message, Class clazz) { + JSONObject msg = JSONObject.parseObject(message); + LogTrack log = msg.getObject(LOG, LogTrack.class); + T data = msg.getObject(DATA, clazz); + return new MessageDto<>(log, data); + } +} diff --git a/xtools-boot-mq/xtools-boot-mq-base/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports b/xtools-boot-mq/xtools-boot-mq-base/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports new file mode 100644 index 0000000..73b1ae0 --- /dev/null +++ b/xtools-boot-mq/xtools-boot-mq-base/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports @@ -0,0 +1 @@ +xtools.boot.mq.base.BootMqBaseConfiguration \ No newline at end of file diff --git a/xtools-boot-mq/xtools-boot-mq-rabbit/pom.xml b/xtools-boot-mq/xtools-boot-mq-rabbit/pom.xml new file mode 100644 index 0000000..b00fcf4 --- /dev/null +++ b/xtools-boot-mq/xtools-boot-mq-rabbit/pom.xml @@ -0,0 +1,35 @@ + + + 4.0.0 + + org.xujun + xtools-boot-mq + 5.0.0 + + xtools-boot-mq-rabbit + + + + + + + org.xujun + xtools-boot-core + + + + org.xujun + xtools-boot-mq-base + + + + + + org.springframework.boot + spring-boot-starter-amqp + + + + \ No newline at end of file diff --git a/xtools-boot-mq/xtools-boot-mq-rabbit/src/main/java/xtools/boot/mq/rabbit/BootRabbitMqConfiguration.java b/xtools-boot-mq/xtools-boot-mq-rabbit/src/main/java/xtools/boot/mq/rabbit/BootRabbitMqConfiguration.java new file mode 100644 index 0000000..df45a8b --- /dev/null +++ b/xtools-boot-mq/xtools-boot-mq-rabbit/src/main/java/xtools/boot/mq/rabbit/BootRabbitMqConfiguration.java @@ -0,0 +1,28 @@ +package xtools.boot.mq.rabbit; + +import org.springframework.context.annotation.Import; +import xtools.boot.core.utils.ModuleLoadUtils; +import xtools.boot.mq.rabbit.selector.BootRabbitMqImportSelector; + +/** + *

Title : BootRabbitMqConfiguration

+ *

Description : BootRabbitMqConfiguration

+ *

DevelopTools : Idea_x64_v2026.1

+ *

DevelopSystem : macOS Sequoia 15.7.5

+ *

Company : org.xujun

+ * + * @author : XuJun + * @version : 5.0.0 + * @date : 2026/01/01 09:30 + */ +@Import(BootRabbitMqImportSelector.class) +public class BootRabbitMqConfiguration { + + /** + * 构造方法 + */ + public BootRabbitMqConfiguration() { + ModuleLoadUtils.loadSuccess(BootRabbitMqConfiguration.class); + } + +} diff --git a/xtools-boot-mq/xtools-boot-mq-rabbit/src/main/java/xtools/boot/mq/rabbit/handle/RabbitMqHandle.java b/xtools-boot-mq/xtools-boot-mq-rabbit/src/main/java/xtools/boot/mq/rabbit/handle/RabbitMqHandle.java new file mode 100644 index 0000000..d48a8dd --- /dev/null +++ b/xtools-boot-mq/xtools-boot-mq-rabbit/src/main/java/xtools/boot/mq/rabbit/handle/RabbitMqHandle.java @@ -0,0 +1,90 @@ +package xtools.boot.mq.rabbit.handle; + +import jakarta.annotation.Resource; +import org.springframework.amqp.core.Queue; +import org.springframework.amqp.rabbit.connection.ConnectionFactory; +import org.springframework.amqp.rabbit.core.RabbitAdmin; +import org.springframework.amqp.rabbit.core.RabbitTemplate; +import org.springframework.amqp.rabbit.listener.SimpleMessageListenerContainer; +import org.springframework.amqp.rabbit.listener.adapter.MessageListenerAdapter; +import org.springframework.stereotype.Component; +import xtools.boot.mq.base.handle.BaseMqHandle; +import xtools.boot.mq.rabbit.params.RabbitMqParams; +import xtools.core.StringUtils; + +/** + *

Title : RabbitMqHandle

+ *

Description : RabbitMqHandle

+ *

DevelopTools : Idea_x64_v2026.1

+ *

DevelopSystem : macOS Sequoia 15.7.5

+ *

Company : org.xujun

+ * + * @author : XuJun + * @version : 5.0.0 + * @date : 2026/2/4 14:14 + */ +@Component +public class RabbitMqHandle implements BaseMqHandle { + + /** + * 默认监听方法 + */ + private final static String DEFAULT_LISTENER_METHOD = "baseHandleMessage"; + + @Resource + private ConnectionFactory connectionFactory; + + @Resource + private RabbitTemplate rabbitTemplate; + + /** + * 初始化消息队列 + * + * @param queueName 消息队列名称 + */ + @Override + public void initQueue(String queueName) { + Queue queue = new Queue(queueName, true); + new RabbitAdmin(connectionFactory).declareQueue(queue); + } + + /** + * 添加消息队列监听 + * + * @param queueName 消息队列 + * @param delegate 监听类 + * @param listenerMethod 监听方法 + * @param params 参数 + */ + @Override + public void addListen(String queueName, Object delegate, String listenerMethod, Object params) { + if (StringUtils.isBlank(listenerMethod)) { + listenerMethod = DEFAULT_LISTENER_METHOD; + } + SimpleMessageListenerContainer container = new SimpleMessageListenerContainer(); + container.setConnectionFactory(connectionFactory); + container.setQueueNames(queueName); + // 设置参数 + if (params instanceof RabbitMqParams rabbitMqParams) { + // 并发参数 + String concurrency = rabbitMqParams.getConcurrency(); + if (StringUtils.isNotBlank(concurrency)) { + container.setConcurrency(concurrency); + } + } + container.setMessageListener(new MessageListenerAdapter(delegate, listenerMethod)); + container.start(); + } + + /** + * 推送消息 + * + * @param routingKey 路由 key + * @param data 消息数据 + */ + @Override + public void push(String routingKey, Object data) { + rabbitTemplate.convertAndSend(routingKey, data); + } + +} diff --git a/xtools-boot-mq/xtools-boot-mq-rabbit/src/main/java/xtools/boot/mq/rabbit/params/RabbitMqParams.java b/xtools-boot-mq/xtools-boot-mq-rabbit/src/main/java/xtools/boot/mq/rabbit/params/RabbitMqParams.java new file mode 100644 index 0000000..61bcd9d --- /dev/null +++ b/xtools-boot-mq/xtools-boot-mq-rabbit/src/main/java/xtools/boot/mq/rabbit/params/RabbitMqParams.java @@ -0,0 +1,30 @@ +package xtools.boot.mq.rabbit.params; + +import java.io.Serializable; + +/** + *

Title : RabbitMqParam

+ *

Description : RabbitMqParam

+ *

DevelopTools : Idea_x64_v2026.1

+ *

DevelopSystem : macOS Sequoia 15.7.5

+ *

Company : org.xujun

+ * + * @author : XuJun + * @version : 5.0.0 + * @date : 2026/2/12 08:05 + */ +public class RabbitMqParams implements Serializable { + + /** + * 并发 + */ + private String concurrency; + + public String getConcurrency() { + return concurrency; + } + + public void setConcurrency(String concurrency) { + this.concurrency = concurrency; + } +} diff --git a/xtools-boot-mq/xtools-boot-mq-rabbit/src/main/java/xtools/boot/mq/rabbit/selector/BootRabbitMqImportSelector.java b/xtools-boot-mq/xtools-boot-mq-rabbit/src/main/java/xtools/boot/mq/rabbit/selector/BootRabbitMqImportSelector.java new file mode 100644 index 0000000..97f5b3c --- /dev/null +++ b/xtools-boot-mq/xtools-boot-mq-rabbit/src/main/java/xtools/boot/mq/rabbit/selector/BootRabbitMqImportSelector.java @@ -0,0 +1,36 @@ +package xtools.boot.mq.rabbit.selector; + +import org.jspecify.annotations.NonNull; +import org.springframework.beans.factory.support.BeanDefinitionRegistry; +import org.springframework.context.annotation.ClassPathBeanDefinitionScanner; +import org.springframework.context.annotation.ImportBeanDefinitionRegistrar; +import org.springframework.core.type.AnnotationMetadata; + +/** + *

Title : BootRabbitMqImportSelector

+ *

Description : BootRabbitMqImportSelector

+ *

DevelopTools : Idea_x64_v2026.1

+ *

DevelopSystem : macOS Sequoia 15.7.5

+ *

Company : org.xujun

+ * + * @author : XuJun + * @version : 5.0.0 + * @date : 2026/01/01 09:30 + */ +public class BootRabbitMqImportSelector implements ImportBeanDefinitionRegistrar { + + /** + * 根据给定的注释元数据,根据需要注册bean + * + * @param importingClassMetadata AnnotationMetadata + * @param registry BeanDefinitionRegistry + */ + @Override + public void registerBeanDefinitions(@NonNull AnnotationMetadata importingClassMetadata, @NonNull BeanDefinitionRegistry registry) { + // 构建扫描对象 + ClassPathBeanDefinitionScanner scanner = new ClassPathBeanDefinitionScanner(registry, true); + // 扫描包下路径 + scanner.scan("xtools.boot.mq.rabbit"); + } + +} \ No newline at end of file diff --git a/xtools-boot-mq/xtools-boot-mq-rabbit/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports b/xtools-boot-mq/xtools-boot-mq-rabbit/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports new file mode 100644 index 0000000..b5073d4 --- /dev/null +++ b/xtools-boot-mq/xtools-boot-mq-rabbit/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports @@ -0,0 +1 @@ +xtools.boot.mq.rabbit.BootRabbitMqConfiguration \ No newline at end of file diff --git a/xtools-boot-storage/pom.xml b/xtools-boot-storage/pom.xml new file mode 100644 index 0000000..fac3750 --- /dev/null +++ b/xtools-boot-storage/pom.xml @@ -0,0 +1,21 @@ + + + 4.0.0 + + org.xujun + xtools-boot + 5.0.0 + + pom + xtools-boot-storage + + + + xtools-boot-storage-file + xtools-boot-storage-s3 + xtools-boot-storage-base + + + \ No newline at end of file diff --git a/xtools-boot-storage/xtools-boot-storage-base/pom.xml b/xtools-boot-storage/xtools-boot-storage-base/pom.xml new file mode 100644 index 0000000..5d725be --- /dev/null +++ b/xtools-boot-storage/xtools-boot-storage-base/pom.xml @@ -0,0 +1,30 @@ + + + 4.0.0 + + org.xujun + xtools-boot-storage + 5.0.0 + + xtools-boot-storage-base + + + + + + + org.xujun + xtools-boot-core + + + + + + org.springframework.boot + spring-boot + + + + \ No newline at end of file diff --git a/xtools-boot-storage/xtools-boot-storage-base/src/main/java/xtools/boot/storage/base/BootStorageBaseConfiguration.java b/xtools-boot-storage/xtools-boot-storage-base/src/main/java/xtools/boot/storage/base/BootStorageBaseConfiguration.java new file mode 100644 index 0000000..46eb230 --- /dev/null +++ b/xtools-boot-storage/xtools-boot-storage-base/src/main/java/xtools/boot/storage/base/BootStorageBaseConfiguration.java @@ -0,0 +1,28 @@ +package xtools.boot.storage.base; + +import org.springframework.context.annotation.Import; +import xtools.boot.core.utils.ModuleLoadUtils; +import xtools.boot.storage.base.selector.BootStorageBaseImportSelector; + +/** + *

Title : BootStorageBaseConfiguration

+ *

Description : BootStorageBaseConfiguration

+ *

DevelopTools : Idea_x64_v2026.1

+ *

DevelopSystem : macOS Sequoia 15.7.5

+ *

Company : org.xujun

+ * + * @author : XuJun + * @version : 5.0.0 + * @date : 2026/01/01 09:30 + */ +@Import(BootStorageBaseImportSelector.class) +public class BootStorageBaseConfiguration { + + /** + * 构造方法 + */ + public BootStorageBaseConfiguration() { + ModuleLoadUtils.loadSuccess(BootStorageBaseConfiguration.class); + } + +} diff --git a/xtools-boot-storage/xtools-boot-storage-base/src/main/java/xtools/boot/storage/base/config/StorageConfig.java b/xtools-boot-storage/xtools-boot-storage-base/src/main/java/xtools/boot/storage/base/config/StorageConfig.java new file mode 100644 index 0000000..2d4fd90 --- /dev/null +++ b/xtools-boot-storage/xtools-boot-storage-base/src/main/java/xtools/boot/storage/base/config/StorageConfig.java @@ -0,0 +1,77 @@ +package xtools.boot.storage.base.config; + +import lombok.Data; +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.context.annotation.Configuration; + +/** + *

Title : StorageConfig

+ *

Description : StorageConfig

+ *

DevelopTools : Idea_x64_v2026.1

+ *

DevelopSystem : macOS Sequoia 15.7.5

+ *

Company : org.xujun

+ * + * @author : XuJun + * @version : 5.0.0 + * @date : 2026/3/14 07:40 + */ +@Data +@Configuration +@ConfigurationProperties(prefix = "storage") +public class StorageConfig { + + /** + * 存储类型[file|s3] + */ + private String type = "file"; + + /** + * 文件类型 + */ + private FileType file; + + /** + * S3类型 + */ + private S3Type s3; + + /** + * 文件类型 + */ + @Data + public static class FileType { + + /** + * 文件存储路径 + */ + private String path; + } + + /** + * S3类型 + */ + @Data + public static class S3Type { + + /** + * 服务类型[rustfs] + */ + private String server = "default"; + + /** + * 地址 + */ + private String host; + + /** + * 密钥ID + */ + private String accessKeyId; + + /** + * 密钥 + */ + private String secretAccessKey; + } + +} diff --git a/xtools-boot-storage/xtools-boot-storage-base/src/main/java/xtools/boot/storage/base/selector/BootStorageBaseImportSelector.java b/xtools-boot-storage/xtools-boot-storage-base/src/main/java/xtools/boot/storage/base/selector/BootStorageBaseImportSelector.java new file mode 100644 index 0000000..b6b3dca --- /dev/null +++ b/xtools-boot-storage/xtools-boot-storage-base/src/main/java/xtools/boot/storage/base/selector/BootStorageBaseImportSelector.java @@ -0,0 +1,36 @@ +package xtools.boot.storage.base.selector; + +import org.jspecify.annotations.NonNull; +import org.springframework.beans.factory.support.BeanDefinitionRegistry; +import org.springframework.context.annotation.ClassPathBeanDefinitionScanner; +import org.springframework.context.annotation.ImportBeanDefinitionRegistrar; +import org.springframework.core.type.AnnotationMetadata; + +/** + *

Title : BootStorageBaseImportSelector

+ *

Description : BootStorageBaseImportSelector

+ *

DevelopTools : Idea_x64_v2026.1

+ *

DevelopSystem : macOS Sequoia 15.7.5

+ *

Company : org.xujun

+ * + * @author : XuJun + * @version : 5.0.0 + * @date : 2026/01/01 09:30 + */ +public class BootStorageBaseImportSelector implements ImportBeanDefinitionRegistrar { + + /** + * 根据给定的注释元数据,根据需要注册bean + * + * @param importingClassMetadata AnnotationMetadata + * @param registry BeanDefinitionRegistry + */ + @Override + public void registerBeanDefinitions(@NonNull AnnotationMetadata importingClassMetadata, @NonNull BeanDefinitionRegistry registry) { + // 构建扫描对象 + ClassPathBeanDefinitionScanner scanner = new ClassPathBeanDefinitionScanner(registry, true); + // 扫描包下路径 + scanner.scan("xtools.boot.storage.base"); + } + +} \ No newline at end of file diff --git a/xtools-boot-storage/xtools-boot-storage-base/src/main/java/xtools/boot/storage/base/service/StorageService.java b/xtools-boot-storage/xtools-boot-storage-base/src/main/java/xtools/boot/storage/base/service/StorageService.java new file mode 100644 index 0000000..d376420 --- /dev/null +++ b/xtools-boot-storage/xtools-boot-storage-base/src/main/java/xtools/boot/storage/base/service/StorageService.java @@ -0,0 +1,72 @@ +package xtools.boot.storage.base.service; + +import jakarta.validation.constraints.NotNull; + +import java.io.InputStream; +import java.io.OutputStream; + +/** + *

Title : StorageService

+ *

Description : StorageService

+ *

DevelopTools : Idea_x64_v2026.1

+ *

DevelopSystem : macOS Sequoia 15.7.5

+ *

Company : org.xujun

+ * + * @author : XuJun + * @version : 5.0.0 + * @date : 2026/3/14 07:20 + */ +public interface StorageService { + + /** + * 是否存在文件 + * + * @param bucket 桶 + * @param fileName 文件名 + * @return true or false + */ + boolean exists( + @NotNull String bucket, + @NotNull String fileName + ); + + /** + * 保存文件 + * + * @param bucket 桶 + * @param fileName 文件名 + * @param inputStream 输入流 + * @param contentLength 文件长度 + */ + void save( + @NotNull String bucket, + @NotNull String fileName, + @NotNull InputStream inputStream, + long contentLength + ); + + /** + * 获取文件 + * + * @param bucket 桶 + * @param fileName 文件名 + * @param outputStream 输出流 + */ + void get( + @NotNull String bucket, + @NotNull String fileName, + @NotNull OutputStream outputStream + ); + + /** + * 删除文件 + * + * @param bucket 桶 + * @param fileName 文件名 + */ + void del( + @NotNull String bucket, + @NotNull String fileName + ); + +} diff --git a/xtools-boot-storage/xtools-boot-storage-base/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports b/xtools-boot-storage/xtools-boot-storage-base/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports new file mode 100644 index 0000000..4a4342f --- /dev/null +++ b/xtools-boot-storage/xtools-boot-storage-base/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports @@ -0,0 +1 @@ +xtools.boot.storage.base.BootStorageBaseConfiguration \ No newline at end of file diff --git a/xtools-boot-storage/xtools-boot-storage-base/src/main/resources/application-boot-storage-demo.yaml b/xtools-boot-storage/xtools-boot-storage-base/src/main/resources/application-boot-storage-demo.yaml new file mode 100644 index 0000000..10ee6db --- /dev/null +++ b/xtools-boot-storage/xtools-boot-storage-base/src/main/resources/application-boot-storage-demo.yaml @@ -0,0 +1,18 @@ +# 存储配置 +storage: + # 存储类型[file|s3](并且需要引入对应的jar包) + type: ${STORAGE_TYPE:file} + # 文件类型 + file: + # 文件存储路径 + path: ${STORAGE_FILE_PATH:/data/xtools/file} + # S3类型 + s3: + # 服务类型(可为空)[rustfs] + server: ${STORAGE_S3_SERVER:rustfs} + # 地址 + host: ${STORAGE_S3_HOST:http://127.0.0.1:9000} + # 密钥ID + accessKeyId: ${STORAGE_S3_ACCESS_KEY_ID:admin} + # 密钥 + secretAccessKey: ${STORAGE_S3_SECRET_ACCESS_KEY:123456} \ No newline at end of file diff --git a/xtools-boot-storage/xtools-boot-storage-file/pom.xml b/xtools-boot-storage/xtools-boot-storage-file/pom.xml new file mode 100644 index 0000000..3c68eb5 --- /dev/null +++ b/xtools-boot-storage/xtools-boot-storage-file/pom.xml @@ -0,0 +1,41 @@ + + + 4.0.0 + + org.xujun + xtools-boot-storage + 5.0.0 + + xtools-boot-storage-file + + + + + + + org.xujun + xtools-boot-core + + + + org.xujun + xtools-boot-storage-base + + + + + + org.springframework.boot + spring-boot-autoconfigure + + + + + jakarta.annotation + jakarta.annotation-api + + + + \ No newline at end of file diff --git a/xtools-boot-storage/xtools-boot-storage-file/src/main/java/xtools/boot/storage/file/BootStorageFileConfiguration.java b/xtools-boot-storage/xtools-boot-storage-file/src/main/java/xtools/boot/storage/file/BootStorageFileConfiguration.java new file mode 100644 index 0000000..6fd3df6 --- /dev/null +++ b/xtools-boot-storage/xtools-boot-storage-file/src/main/java/xtools/boot/storage/file/BootStorageFileConfiguration.java @@ -0,0 +1,28 @@ +package xtools.boot.storage.file; + +import org.springframework.context.annotation.Import; +import xtools.boot.core.utils.ModuleLoadUtils; +import xtools.boot.storage.file.selector.BootStorageFileImportSelector; + +/** + *

Title : BootStorageFileConfiguration

+ *

Description : BootStorageFileConfiguration

+ *

DevelopTools : Idea_x64_v2026.1

+ *

DevelopSystem : macOS Sequoia 15.7.5

+ *

Company : org.xujun

+ * + * @author : XuJun + * @version : 5.0.0 + * @date : 2026/01/01 09:30 + */ +@Import(BootStorageFileImportSelector.class) +public class BootStorageFileConfiguration { + + /** + * 构造方法 + */ + public BootStorageFileConfiguration() { + ModuleLoadUtils.loadSuccess(BootStorageFileConfiguration.class); + } + +} diff --git a/xtools-boot-storage/xtools-boot-storage-file/src/main/java/xtools/boot/storage/file/config/FileStorageConfig.java b/xtools-boot-storage/xtools-boot-storage-file/src/main/java/xtools/boot/storage/file/config/FileStorageConfig.java new file mode 100644 index 0000000..2b71a05 --- /dev/null +++ b/xtools-boot-storage/xtools-boot-storage-file/src/main/java/xtools/boot/storage/file/config/FileStorageConfig.java @@ -0,0 +1,33 @@ +package xtools.boot.storage.file.config; + +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import xtools.boot.storage.base.service.StorageService; +import xtools.boot.storage.file.service.impl.StorageServiceFileImpl; + +/** + *

Title : FileStorageConfig

+ *

Description : FileStorageConfig

+ *

DevelopTools : Idea_x64_v2026.1

+ *

DevelopSystem : macOS Sequoia 15.7.5

+ *

Company : org.xujun

+ * + * @author : XuJun + * @version : 5.0.0 + * @date : 2026/3/14 07:53 + */ +@Configuration +public class FileStorageConfig { + + /** + * 创建文件存储服务 + * + * @return StorageService + */ + @Bean + @ConditionalOnProperty(prefix = "storage", name = "type", havingValue = "file") + public StorageService fileStorage() { + return new StorageServiceFileImpl(); + } +} diff --git a/xtools-boot-storage/xtools-boot-storage-file/src/main/java/xtools/boot/storage/file/selector/BootStorageFileImportSelector.java b/xtools-boot-storage/xtools-boot-storage-file/src/main/java/xtools/boot/storage/file/selector/BootStorageFileImportSelector.java new file mode 100644 index 0000000..f84bde9 --- /dev/null +++ b/xtools-boot-storage/xtools-boot-storage-file/src/main/java/xtools/boot/storage/file/selector/BootStorageFileImportSelector.java @@ -0,0 +1,36 @@ +package xtools.boot.storage.file.selector; + +import org.jspecify.annotations.NonNull; +import org.springframework.beans.factory.support.BeanDefinitionRegistry; +import org.springframework.context.annotation.ClassPathBeanDefinitionScanner; +import org.springframework.context.annotation.ImportBeanDefinitionRegistrar; +import org.springframework.core.type.AnnotationMetadata; + +/** + *

Title : BootStorageFileImportSelector

+ *

Description : BootStorageFileImportSelector

+ *

DevelopTools : Idea_x64_v2026.1

+ *

DevelopSystem : macOS Sequoia 15.7.5

+ *

Company : org.xujun

+ * + * @author : XuJun + * @version : 5.0.0 + * @date : 2026/01/01 09:30 + */ +public class BootStorageFileImportSelector implements ImportBeanDefinitionRegistrar { + + /** + * 根据给定的注释元数据,根据需要注册bean + * + * @param importingClassMetadata AnnotationMetadata + * @param registry BeanDefinitionRegistry + */ + @Override + public void registerBeanDefinitions(@NonNull AnnotationMetadata importingClassMetadata, @NonNull BeanDefinitionRegistry registry) { + // 构建扫描对象 + ClassPathBeanDefinitionScanner scanner = new ClassPathBeanDefinitionScanner(registry, true); + // 扫描包下路径 + scanner.scan("xtools.boot.storage.file"); + } + +} \ No newline at end of file diff --git a/xtools-boot-storage/xtools-boot-storage-file/src/main/java/xtools/boot/storage/file/service/impl/StorageServiceFileImpl.java b/xtools-boot-storage/xtools-boot-storage-file/src/main/java/xtools/boot/storage/file/service/impl/StorageServiceFileImpl.java new file mode 100644 index 0000000..57f86a8 --- /dev/null +++ b/xtools-boot-storage/xtools-boot-storage-file/src/main/java/xtools/boot/storage/file/service/impl/StorageServiceFileImpl.java @@ -0,0 +1,159 @@ +package xtools.boot.storage.file.service.impl; + +import jakarta.annotation.Resource; +import jakarta.validation.constraints.NotNull; +import lombok.extern.slf4j.Slf4j; +import xtools.base.config.BaseParams; +import xtools.boot.api.exection.BizError; +import xtools.boot.storage.base.config.StorageConfig; +import xtools.boot.storage.base.service.StorageService; +import xtools.core.FileUtils; + +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.InputStream; +import java.io.OutputStream; + +/** + *

Title : StorageServiceS3Impl

+ *

Description : StorageServiceS3Impl

+ *

DevelopTools : Idea_x64_v2026.1

+ *

DevelopSystem : macOS Sequoia 15.7.5

+ *

Company : org.xujun

+ * + * @author : XuJun + * @version : 5.0.0 + * @date : 2026/3/14 07:35 + */ +@Slf4j +public class StorageServiceFileImpl implements StorageService, BaseParams { + + @Resource + private StorageConfig storageConfig; + + /** + * 是否存在文件 + * + * @param bucket 桶 + * @param fileName 文件名 + * @return true or false + */ + @Override + public boolean exists( + @NotNull String bucket, + @NotNull String fileName + ) { + initBucket(bucket); + File file = new File(getFilePath(bucket, fileName)); + return file.exists(); + } + + /** + * 保存文件 + * + * @param bucket 桶 + * @param fileName 文件名 + * @param inputStream 输入流 + * @param contentLength 文件长度 + */ + @Override + public void save( + @NotNull String bucket, + @NotNull String fileName, + @NotNull InputStream inputStream, + long contentLength + ) { + initBucket(bucket); + File file = new File(getFilePath(bucket, fileName)); + try (OutputStream outputStream = new FileOutputStream(file)) { + byte[] bytes = new byte[CP_NUM1024]; + int len; + while ((len = inputStream.read(bytes)) != CP_NEGATIVE1) { + outputStream.write(bytes, CP_NUM0, len); + } + } catch (Exception e) { + log.error("保存文件失败", e); + throw new BizError("保存文件失败"); + } + } + + /** + * 获取文件 + * + * @param bucket 桶 + * @param fileName 文件名 + * @param outputStream 输出流 + */ + @Override + public void get( + @NotNull String bucket, + @NotNull String fileName, + @NotNull OutputStream outputStream + ) { + initBucket(bucket); + try (InputStream inputStream = new FileInputStream(getFilePath(bucket, fileName))) { + byte[] bytes = new byte[CP_NUM1024]; + int len; + while ((len = inputStream.read(bytes)) != CP_NEGATIVE1) { + outputStream.write(bytes, CP_NUM0, len); + } + } catch (Exception e) { + log.error("获取文件失败", e); + throw new BizError("获取文件失败"); + } + } + + /** + * 删除文件 + * + * @param bucket 桶 + * @param fileName 文件名 + */ + @Override + public void del( + @NotNull String bucket, + @NotNull String fileName + ) { + initBucket(bucket); + FileUtils.del(getFilePath(bucket, fileName)); + } + + /** + * 初始化桶 + * + * @param bucket 桶 + */ + private void initBucket(String bucket) { + String path = getPath(bucket); + File file = new File(path); + if (file.exists()) { + return; + } + if (file.mkdirs()) { + return; + } + throw new BizError("创建目录失败" + path); + } + + /** + * 获取路径 + * + * @param bucket 桶 + * @return 路径 + */ + private String getPath(String bucket) { + return storageConfig.getFile().getPath() + CP_SLASH + bucket; + } + + /** + * 获取文件路径 + * + * @param bucket 桶 + * @param fileName 文件名 + * @return 文件路径 + */ + private String getFilePath(String bucket, String fileName) { + return getPath(bucket) + CP_SLASH + fileName; + } +} diff --git a/xtools-boot-storage/xtools-boot-storage-file/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports b/xtools-boot-storage/xtools-boot-storage-file/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports new file mode 100644 index 0000000..8a14b28 --- /dev/null +++ b/xtools-boot-storage/xtools-boot-storage-file/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports @@ -0,0 +1 @@ +xtools.boot.storage.file.BootStorageFileConfiguration \ No newline at end of file diff --git a/xtools-boot-storage/xtools-boot-storage-s3/pom.xml b/xtools-boot-storage/xtools-boot-storage-s3/pom.xml new file mode 100644 index 0000000..5c6fee5 --- /dev/null +++ b/xtools-boot-storage/xtools-boot-storage-s3/pom.xml @@ -0,0 +1,55 @@ + + + 4.0.0 + + org.xujun + xtools-boot-storage + 5.0.0 + + xtools-boot-storage-s3 + + + + + + + + software.amazon.awssdk + s3 + + + org.xujun + xtools-extend + + + + + + + + org.xujun + xtools-boot-core + + + + org.xujun + xtools-boot-storage-base + + + + + + org.springframework.boot + spring-boot-autoconfigure + + + + + jakarta.annotation + jakarta.annotation-api + + + + \ No newline at end of file diff --git a/xtools-boot-storage/xtools-boot-storage-s3/src/main/java/xtools/boot/storage/s3/BootStorageS3Configuration.java b/xtools-boot-storage/xtools-boot-storage-s3/src/main/java/xtools/boot/storage/s3/BootStorageS3Configuration.java new file mode 100644 index 0000000..0928f76 --- /dev/null +++ b/xtools-boot-storage/xtools-boot-storage-s3/src/main/java/xtools/boot/storage/s3/BootStorageS3Configuration.java @@ -0,0 +1,28 @@ +package xtools.boot.storage.s3; + +import org.springframework.context.annotation.Import; +import xtools.boot.core.utils.ModuleLoadUtils; +import xtools.boot.storage.s3.selector.BootStorageS3ImportSelector; + +/** + *

Title : BootStorageS3Configuration

+ *

Description : BootStorageS3Configuration

+ *

DevelopTools : Idea_x64_v2026.1

+ *

DevelopSystem : macOS Sequoia 15.7.5

+ *

Company : org.xujun

+ * + * @author : XuJun + * @version : 5.0.0 + * @date : 2026/01/01 09:30 + */ +@Import(BootStorageS3ImportSelector.class) +public class BootStorageS3Configuration { + + /** + * 构造方法 + */ + public BootStorageS3Configuration() { + ModuleLoadUtils.loadSuccess(BootStorageS3Configuration.class); + } + +} diff --git a/xtools-boot-storage/xtools-boot-storage-s3/src/main/java/xtools/boot/storage/s3/config/S3StorageConfig.java b/xtools-boot-storage/xtools-boot-storage-s3/src/main/java/xtools/boot/storage/s3/config/S3StorageConfig.java new file mode 100644 index 0000000..a94fd0d --- /dev/null +++ b/xtools-boot-storage/xtools-boot-storage-s3/src/main/java/xtools/boot/storage/s3/config/S3StorageConfig.java @@ -0,0 +1,75 @@ +package xtools.boot.storage.s3.config; + +import jakarta.annotation.Resource; +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import software.amazon.awssdk.regions.Region; +import software.amazon.awssdk.services.s3.S3Client; +import xtools.boot.storage.base.config.StorageConfig; +import xtools.boot.storage.base.service.StorageService; +import xtools.boot.storage.s3.service.impl.StorageServiceS3Impl; +import xtools.extend.S3Utils; + +/** + *

Title : S3StorageConfig

+ *

Description : S3StorageConfig

+ *

DevelopTools : Idea_x64_v2026.1

+ *

DevelopSystem : macOS Sequoia 15.7.5

+ *

Company : org.xujun

+ * + * @author : XuJun + * @version : 5.0.0 + * @date : 2026/3/14 07:53 + */ +@Configuration +public class S3StorageConfig { + + @Resource + private StorageConfig storageConfig; + + /** + * 创建S3默认客户端 + * + * @return S3Client + */ + @Bean + @ConditionalOnProperty(prefix = "storage", name = "type", havingValue = "s3") + @ConditionalOnProperty(prefix = "storage.s3", name = "server", havingValue = "default") + public S3Client defaultClient() { + return S3Utils.client( + storageConfig.getS3().getHost(), + storageConfig.getS3().getAccessKeyId(), + storageConfig.getS3().getSecretAccessKey(), + Region.CN_NORTH_1, + true + ); + } + + /** + * 创建S3-rustfs客户端 + * + * @return S3Client + */ + @Bean + @ConditionalOnProperty(prefix = "storage", name = "type", havingValue = "s3") + @ConditionalOnProperty(prefix = "storage.s3", name = "server", havingValue = "rustfs") + public S3Client rustfsClient() { + return S3Utils.rustfsClient( + storageConfig.getS3().getHost(), + storageConfig.getS3().getAccessKeyId(), + storageConfig.getS3().getSecretAccessKey() + ); + } + + /** + * 创建S3存储服务 + * + * @return StorageService + */ + @Bean + @ConditionalOnProperty(prefix = "storage", name = "type", havingValue = "s3") + public StorageService s3Storage() { + return new StorageServiceS3Impl(); + } +} diff --git a/xtools-boot-storage/xtools-boot-storage-s3/src/main/java/xtools/boot/storage/s3/selector/BootStorageS3ImportSelector.java b/xtools-boot-storage/xtools-boot-storage-s3/src/main/java/xtools/boot/storage/s3/selector/BootStorageS3ImportSelector.java new file mode 100644 index 0000000..5917b52 --- /dev/null +++ b/xtools-boot-storage/xtools-boot-storage-s3/src/main/java/xtools/boot/storage/s3/selector/BootStorageS3ImportSelector.java @@ -0,0 +1,36 @@ +package xtools.boot.storage.s3.selector; + +import org.jspecify.annotations.NonNull; +import org.springframework.beans.factory.support.BeanDefinitionRegistry; +import org.springframework.context.annotation.ClassPathBeanDefinitionScanner; +import org.springframework.context.annotation.ImportBeanDefinitionRegistrar; +import org.springframework.core.type.AnnotationMetadata; + +/** + *

Title : BootStorageS3ImportSelector

+ *

Description : BootStorageS3ImportSelector

+ *

DevelopTools : Idea_x64_v2026.1

+ *

DevelopSystem : macOS Sequoia 15.7.5

+ *

Company : org.xujun

+ * + * @author : XuJun + * @version : 5.0.0 + * @date : 2026/01/01 09:30 + */ +public class BootStorageS3ImportSelector implements ImportBeanDefinitionRegistrar { + + /** + * 根据给定的注释元数据,根据需要注册bean + * + * @param importingClassMetadata AnnotationMetadata + * @param registry BeanDefinitionRegistry + */ + @Override + public void registerBeanDefinitions(@NonNull AnnotationMetadata importingClassMetadata, @NonNull BeanDefinitionRegistry registry) { + // 构建扫描对象 + ClassPathBeanDefinitionScanner scanner = new ClassPathBeanDefinitionScanner(registry, true); + // 扫描包下路径 + scanner.scan("xtools.boot.storage.s3"); + } + +} \ No newline at end of file diff --git a/xtools-boot-storage/xtools-boot-storage-s3/src/main/java/xtools/boot/storage/s3/service/impl/StorageServiceS3Impl.java b/xtools-boot-storage/xtools-boot-storage-s3/src/main/java/xtools/boot/storage/s3/service/impl/StorageServiceS3Impl.java new file mode 100644 index 0000000..9ed88c0 --- /dev/null +++ b/xtools-boot-storage/xtools-boot-storage-s3/src/main/java/xtools/boot/storage/s3/service/impl/StorageServiceS3Impl.java @@ -0,0 +1,121 @@ +package xtools.boot.storage.s3.service.impl; + +import jakarta.annotation.Resource; +import jakarta.validation.constraints.NotNull; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import software.amazon.awssdk.services.s3.S3Client; +import xtools.base.config.BaseParams; +import xtools.boot.storage.base.service.StorageService; +import xtools.extend.S3Utils; + +import java.io.InputStream; +import java.io.OutputStream; +import java.util.HashSet; +import java.util.Set; + +/** + *

Title : StorageServiceS3Impl

+ *

Description : StorageServiceS3Impl

+ *

DevelopTools : Idea_x64_v2026.1

+ *

DevelopSystem : macOS Sequoia 15.7.5

+ *

Company : org.xujun

+ * + * @author : XuJun + * @version : 5.0.0 + * @date : 2026/3/14 07:35 + */ +@Slf4j +@RequiredArgsConstructor +public class StorageServiceS3Impl implements StorageService, BaseParams { + + /** + * 桶集合 + */ + private final static Set BUCKET_SET = new HashSet<>(); + + @Resource + private S3Client s3Client; + + /** + * 是否存在文件 + * + * @param bucket 桶 + * @param fileName 文件名 + * @return true or false + */ + @Override + public boolean exists( + @NotNull String bucket, + @NotNull String fileName + ) { + initBucket(bucket); + return S3Utils.exists(s3Client, bucket, fileName); + } + + /** + * 保存文件 + * + * @param bucket 桶 + * @param fileName 文件名 + * @param inputStream 输入流 + * @param contentLength 文件长度 + */ + @Override + public void save( + @NotNull String bucket, + @NotNull String fileName, + @NotNull InputStream inputStream, + long contentLength + ) { + initBucket(bucket); + S3Utils.upload(s3Client, bucket, fileName, inputStream, contentLength); + } + + /** + * 获取文件 + * + * @param bucket 桶 + * @param fileName 文件名 + * @param outputStream 输出流 + */ + @Override + public void get( + @NotNull String bucket, + @NotNull String fileName, + @NotNull OutputStream outputStream + ) { + S3Utils.download(s3Client, bucket, fileName, outputStream); + } + + /** + * 删除文件 + * + * @param bucket 桶 + * @param fileName 文件名 + */ + @Override + public void del( + @NotNull String bucket, + @NotNull String fileName + ) { + S3Utils.delete(s3Client, bucket, fileName); + } + + /** + * 初始化桶 + * + * @param bucket 桶 + */ + private void initBucket(String bucket) { + if (BUCKET_SET.contains(bucket)) { + return; + } + try { + S3Utils.createBucket(s3Client, bucket); + } catch (Exception e) { + log.info("文件桶已存在", e); + } + BUCKET_SET.add(bucket); + } +} diff --git a/xtools-boot-storage/xtools-boot-storage-s3/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports b/xtools-boot-storage/xtools-boot-storage-s3/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports new file mode 100644 index 0000000..0c2343c --- /dev/null +++ b/xtools-boot-storage/xtools-boot-storage-s3/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports @@ -0,0 +1 @@ +xtools.boot.storage.s3.BootStorageS3Configuration \ No newline at end of file diff --git a/xtools-boot-task/pom.xml b/xtools-boot-task/pom.xml new file mode 100644 index 0000000..48bc220 --- /dev/null +++ b/xtools-boot-task/pom.xml @@ -0,0 +1,24 @@ + + + 4.0.0 + + org.xujun + xtools-boot + 5.0.0 + + xtools-boot-task + + + + + + + org.xujun + xtools-boot-core + + + + + \ No newline at end of file diff --git a/xtools-boot-task/src/main/java/xtools/boot/task/BootTaskConfiguration.java b/xtools-boot-task/src/main/java/xtools/boot/task/BootTaskConfiguration.java new file mode 100644 index 0000000..5a53720 --- /dev/null +++ b/xtools-boot-task/src/main/java/xtools/boot/task/BootTaskConfiguration.java @@ -0,0 +1,28 @@ +package xtools.boot.task; + +import org.springframework.context.annotation.Import; +import xtools.boot.core.utils.ModuleLoadUtils; +import xtools.boot.task.selector.BootTaskImportSelector; + +/** + *

Title : BootTaskConfiguration

+ *

Description : BootTaskConfiguration

+ *

DevelopTools : Idea_x64_v2026.1

+ *

DevelopSystem : macOS Sequoia 15.7.5

+ *

Company : org.xujun

+ * + * @author : XuJun + * @version : 5.0.0 + * @date : 2026/01/01 09:30 + */ +@Import(BootTaskImportSelector.class) +public class BootTaskConfiguration { + + /** + * 构造方法 + */ + public BootTaskConfiguration() { + ModuleLoadUtils.loadSuccess(BootTaskConfiguration.class); + } + +} diff --git a/xtools-boot-task/src/main/java/xtools/boot/task/TaskBus.java b/xtools-boot-task/src/main/java/xtools/boot/task/TaskBus.java new file mode 100644 index 0000000..bf03c7b --- /dev/null +++ b/xtools-boot-task/src/main/java/xtools/boot/task/TaskBus.java @@ -0,0 +1,111 @@ +package xtools.boot.task; + +import xtools.boot.api.exection.BizError; +import xtools.boot.core.utils.SpringContextUtils; +import xtools.boot.task.enums.TaskStatus; +import xtools.boot.task.interfaces.BaseTaskType; +import xtools.boot.task.interfaces.TaskBusInterface; +import xtools.boot.task.model.dto.TaskInfo; +import xtools.core.CollectionUtils; +import xtools.core.StringUtils; + +import java.time.Instant; +import java.util.Collection; +import java.util.Objects; + +/** + *

Title : TaskBus

+ *

Description : TaskBus

+ *

DevelopTools : Idea_x64_v2026.1

+ *

DevelopSystem : macOS Sequoia 15.7.5

+ *

Company : org.xujun

+ * + * @author : XuJun + * @version : 5.0.0 + * @date : 2026/2/15 10:07 + */ +public class TaskBus { + + /** + * 任务数据 + **/ + private final TaskInfo taskInfo; + + /** + * 构造方法 + * + * @param code 任务编码 + * @param type 任务类型 + * @param status 任务状态 + */ + private TaskBus(String code, BaseTaskType type, TaskStatus status) { + // 参数处理 + if (StringUtils.isBlank(code)) { + throw new BizError("任务Code不能为空"); + } + if (type == null) { + throw new BizError("任务类型不能为空"); + } + if (status == null) { + throw new BizError("任务状态不能为空"); + } + taskInfo = new TaskInfo(); + // 参数赋值 + taskInfo.setCode(code); + taskInfo.setType(type.desc()); + taskInfo.setStatus(status); + taskInfo.setInfo(type.desc() + "-" + status.desc()); + taskInfo.setTime(Instant.now()); + } + + /** + * 初始化任务 + * + * @param code 任务编码 + * @param type 任务类型 + * @param status 任务状态 + * @return 当前对象 + */ + public static TaskBus init(String code, BaseTaskType type, TaskStatus status) { + return new TaskBus(code, type, status); + } + + /** + * 设置任务信息 + * + * @param info 任务信息 + * @return 当前对象 + */ + public TaskBus info(String info) { + if (StringUtils.isBlank(info)) { + throw new BizError("任务信息不能为空"); + } + taskInfo.setInfo(info); + return this; + } + + /** + * 设置任务时间 + * + * @param time 任务时间 + * @return 当前对象 + */ + public TaskBus time(Instant time) { + if (Objects.isNull(time)) { + throw new BizError("任务时间不能为空"); + } + taskInfo.setTime(time); + return this; + } + + /** + * 保存任务 + */ + public void save() { + Collection beanList = SpringContextUtils.getBeanList(TaskBusInterface.class); + if (CollectionUtils.isNotEmpty(beanList)) { + beanList.forEach(item -> item.save(taskInfo)); + } + } + +} diff --git a/xtools-boot-task/src/main/java/xtools/boot/task/enums/TaskStatus.java b/xtools-boot-task/src/main/java/xtools/boot/task/enums/TaskStatus.java new file mode 100644 index 0000000..66af697 --- /dev/null +++ b/xtools-boot-task/src/main/java/xtools/boot/task/enums/TaskStatus.java @@ -0,0 +1,91 @@ +package xtools.boot.task.enums; + +import xtools.boot.api.enums.BaseEnum; + +/** + *

Title : TaskStatus

+ *

Description : TaskStatus

+ *

DevelopTools : Idea_x64_v2026.1

+ *

DevelopSystem : macOS Sequoia 15.7.5

+ *

Company : org.xujun

+ * + * @author : XuJun + * @version : 5.0.0 + * @date : 2026/2/3 10:31 + */ +public enum TaskStatus implements BaseEnum { + + // 任务进行中 + ING(0, "进行中"), + // 任务成功 + SUCCESS(1, "任务成功"), + // 任务失败 + ERROR(2, "任务失败"); + + /** + * 编码 + */ + private final int code; + + /** + * 说明 + */ + private final String desc; + + /** + * 初始化方法 + * + * @param code Code + * @param desc 说明 + */ + TaskStatus(int code, String desc) { + this.code = code; + this.desc = desc; + } + + /** + * 判断枚举值类型 + * + * @param code 枚举值 + * @return 枚举值类型 + */ + public static TaskStatus valueOf(int code) { + for (TaskStatus type : values()) { + if (type.code == code) { + return type; + } + } + throw new IllegalArgumentException("unknown code, code=" + code); + } + + /** + * 获取所有枚举 + * + * @return 所有枚举 + */ + @Override + public BaseEnum[] all() { + return values(); + } + + /** + * 获取枚举编码 + * + * @return 枚举编码 + */ + @Override + public int code() { + return code; + } + + /** + * 获取枚举说明 + * + * @return 枚举说明 + */ + @Override + public String desc() { + return desc; + } + +} \ No newline at end of file diff --git a/xtools-boot-task/src/main/java/xtools/boot/task/interfaces/BaseTaskType.java b/xtools-boot-task/src/main/java/xtools/boot/task/interfaces/BaseTaskType.java new file mode 100644 index 0000000..462ef4c --- /dev/null +++ b/xtools-boot-task/src/main/java/xtools/boot/task/interfaces/BaseTaskType.java @@ -0,0 +1,17 @@ +package xtools.boot.task.interfaces; + +import xtools.boot.api.enums.BaseEnum; + +/** + *

Title : BaseTaskType

+ *

Description : BaseTaskType

+ *

DevelopTools : Idea_x64_v2026.1

+ *

DevelopSystem : macOS Sequoia 15.7.5

+ *

Company : org.xujun

+ * + * @author : XuJun + * @version : 5.0.0 + * @date : 2026/2/15 09:52 + */ +public interface BaseTaskType extends BaseEnum { +} diff --git a/xtools-boot-task/src/main/java/xtools/boot/task/interfaces/TaskBusInterface.java b/xtools-boot-task/src/main/java/xtools/boot/task/interfaces/TaskBusInterface.java new file mode 100644 index 0000000..e100c22 --- /dev/null +++ b/xtools-boot-task/src/main/java/xtools/boot/task/interfaces/TaskBusInterface.java @@ -0,0 +1,25 @@ +package xtools.boot.task.interfaces; + +import xtools.boot.task.model.dto.TaskInfo; + +/** + *

Title : TaskBusInterface

+ *

Description : TaskBusInterface

+ *

DevelopTools : Idea_x64_v2026.1

+ *

DevelopSystem : macOS Sequoia 15.7.5

+ *

Company : org.xujun

+ * + * @author : XuJun + * @version : 5.0.0 + * @date : 2026/2/15 10:16 + */ +public interface TaskBusInterface { + + /** + * 保存任务 + * + * @param taskInfo 任务 + */ + void save(TaskInfo taskInfo); + +} diff --git a/xtools-boot-task/src/main/java/xtools/boot/task/model/dto/TaskInfo.java b/xtools-boot-task/src/main/java/xtools/boot/task/model/dto/TaskInfo.java new file mode 100644 index 0000000..8bce1e8 --- /dev/null +++ b/xtools-boot-task/src/main/java/xtools/boot/task/model/dto/TaskInfo.java @@ -0,0 +1,47 @@ +package xtools.boot.task.model.dto; + +import lombok.Data; +import xtools.boot.task.enums.TaskStatus; + +import java.io.Serializable; +import java.time.Instant; + +/** + *

Title : TaskInfo

+ *

Description : TaskInfo

+ *

DevelopTools : Idea_x64_v2026.1

+ *

DevelopSystem : macOS Sequoia 15.7.5

+ *

Company : org.xujun

+ * + * @author : XuJun + * @version : 5.0.0 + * @date : 2026/2/15 10:07 + */ +@Data +public class TaskInfo implements Serializable { + + /** + * 任务编码 + */ + private String code; + + /** + * 任务类型 + */ + private String type; + + /** + * 任务状态 + */ + private TaskStatus status; + + /** + * 任务信息 + */ + private String info; + + /** + * 任务时间 + */ + private Instant time; +} \ No newline at end of file diff --git a/xtools-boot-task/src/main/java/xtools/boot/task/selector/BootTaskImportSelector.java b/xtools-boot-task/src/main/java/xtools/boot/task/selector/BootTaskImportSelector.java new file mode 100644 index 0000000..ce719ec --- /dev/null +++ b/xtools-boot-task/src/main/java/xtools/boot/task/selector/BootTaskImportSelector.java @@ -0,0 +1,36 @@ +package xtools.boot.task.selector; + +import org.jspecify.annotations.NonNull; +import org.springframework.beans.factory.support.BeanDefinitionRegistry; +import org.springframework.context.annotation.ClassPathBeanDefinitionScanner; +import org.springframework.context.annotation.ImportBeanDefinitionRegistrar; +import org.springframework.core.type.AnnotationMetadata; + +/** + *

Title : BootTaskImportSelector

+ *

Description : BootTaskImportSelector

+ *

DevelopTools : Idea_x64_v2026.1

+ *

DevelopSystem : macOS Sequoia 15.7.5

+ *

Company : org.xujun

+ * + * @author : XuJun + * @version : 5.0.0 + * @date : 2026/01/01 09:30 + */ +public class BootTaskImportSelector implements ImportBeanDefinitionRegistrar { + + /** + * 根据给定的注释元数据,根据需要注册bean + * + * @param importingClassMetadata AnnotationMetadata + * @param registry BeanDefinitionRegistry + */ + @Override + public void registerBeanDefinitions(@NonNull AnnotationMetadata importingClassMetadata, @NonNull BeanDefinitionRegistry registry) { + // 构建扫描对象 + ClassPathBeanDefinitionScanner scanner = new ClassPathBeanDefinitionScanner(registry, true); + // 扫描包下路径 + scanner.scan("xtools.boot.task"); + } + +} \ No newline at end of file diff --git a/xtools-boot-task/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports b/xtools-boot-task/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports new file mode 100644 index 0000000..77d1bc0 --- /dev/null +++ b/xtools-boot-task/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports @@ -0,0 +1 @@ +xtools.boot.task.BootTaskConfiguration \ No newline at end of file diff --git a/xtools-boot-thread/pom.xml b/xtools-boot-thread/pom.xml new file mode 100644 index 0000000..0ff297d --- /dev/null +++ b/xtools-boot-thread/pom.xml @@ -0,0 +1,29 @@ + + + 4.0.0 + + org.xujun + xtools-boot + 5.0.0 + + xtools-boot-thread + + + + + + + org.xujun + xtools-boot-core + + + + org.xujun + xtools-boot-log + + + + + \ No newline at end of file diff --git a/xtools-boot-thread/src/main/java/xtools/boot/thread/BootThreadConfiguration.java b/xtools-boot-thread/src/main/java/xtools/boot/thread/BootThreadConfiguration.java new file mode 100644 index 0000000..6549238 --- /dev/null +++ b/xtools-boot-thread/src/main/java/xtools/boot/thread/BootThreadConfiguration.java @@ -0,0 +1,28 @@ +package xtools.boot.thread; + +import org.springframework.context.annotation.Import; +import xtools.boot.core.utils.ModuleLoadUtils; +import xtools.boot.thread.selector.BootThreadImportSelector; + +/** + *

Title : BootThreadConfiguration

+ *

Description : BootThreadConfiguration

+ *

DevelopTools : Idea_x64_v2026.1

+ *

DevelopSystem : macOS Sequoia 15.7.5

+ *

Company : org.xujun

+ * + * @author : XuJun + * @version : 5.0.0 + * @date : 2026/01/01 09:30 + */ +@Import(BootThreadImportSelector.class) +public class BootThreadConfiguration { + + /** + * 构造方法 + */ + public BootThreadConfiguration() { + ModuleLoadUtils.loadSuccess(BootThreadConfiguration.class); + } + +} diff --git a/xtools-boot-thread/src/main/java/xtools/boot/thread/callback/VirtualThreadTaskCallback.java b/xtools-boot-thread/src/main/java/xtools/boot/thread/callback/VirtualThreadTaskCallback.java new file mode 100644 index 0000000..c8eb1c0 --- /dev/null +++ b/xtools-boot-thread/src/main/java/xtools/boot/thread/callback/VirtualThreadTaskCallback.java @@ -0,0 +1,21 @@ +package xtools.boot.thread.callback; + +/** + *

Title : VirtualThreadTaskCallback

+ *

Description : VirtualThreadTaskCallback

+ *

DevelopTools : Idea_x64_v2026.1

+ *

DevelopSystem : macOS Sequoia 15.7.5

+ *

Company : org.xujun

+ * + * @author : XuJun + * @version : 5.0.0 + * @date : 2026/2/8 20:48 + */ +public interface VirtualThreadTaskCallback { + + /** + * 回调方法 + */ + void callback(); + +} diff --git a/xtools-boot-thread/src/main/java/xtools/boot/thread/selector/BootThreadImportSelector.java b/xtools-boot-thread/src/main/java/xtools/boot/thread/selector/BootThreadImportSelector.java new file mode 100644 index 0000000..ffec8ef --- /dev/null +++ b/xtools-boot-thread/src/main/java/xtools/boot/thread/selector/BootThreadImportSelector.java @@ -0,0 +1,36 @@ +package xtools.boot.thread.selector; + +import org.jspecify.annotations.NonNull; +import org.springframework.beans.factory.support.BeanDefinitionRegistry; +import org.springframework.context.annotation.ClassPathBeanDefinitionScanner; +import org.springframework.context.annotation.ImportBeanDefinitionRegistrar; +import org.springframework.core.type.AnnotationMetadata; + +/** + *

Title : BootThreadImportSelector

+ *

Description : BootThreadImportSelector

+ *

DevelopTools : Idea_x64_v2026.1

+ *

DevelopSystem : macOS Sequoia 15.7.5

+ *

Company : org.xujun

+ * + * @author : XuJun + * @version : 5.0.0 + * @date : 2026/01/01 09:30 + */ +public class BootThreadImportSelector implements ImportBeanDefinitionRegistrar { + + /** + * 根据给定的注释元数据,根据需要注册bean + * + * @param importingClassMetadata AnnotationMetadata + * @param registry BeanDefinitionRegistry + */ + @Override + public void registerBeanDefinitions(@NonNull AnnotationMetadata importingClassMetadata, @NonNull BeanDefinitionRegistry registry) { + // 构建扫描对象 + ClassPathBeanDefinitionScanner scanner = new ClassPathBeanDefinitionScanner(registry, true); + // 扫描包下路径 + scanner.scan("xtools.boot.thread"); + } + +} \ No newline at end of file diff --git a/xtools-boot-thread/src/main/java/xtools/boot/thread/utils/VirtualThreadTaskUtils.java b/xtools-boot-thread/src/main/java/xtools/boot/thread/utils/VirtualThreadTaskUtils.java new file mode 100644 index 0000000..8ba120e --- /dev/null +++ b/xtools-boot-thread/src/main/java/xtools/boot/thread/utils/VirtualThreadTaskUtils.java @@ -0,0 +1,82 @@ +package xtools.boot.thread.utils; + +import lombok.extern.slf4j.Slf4j; +import xtools.boot.api.model.dto.log.HolderLogTrack; +import xtools.boot.api.model.dto.log.LogTrack; +import xtools.boot.log.LogBus; +import xtools.boot.log.enums.LogBusBaseType; +import xtools.boot.log.holder.LogTrackHolder; +import xtools.boot.thread.callback.VirtualThreadTaskCallback; +import xtools.core.enums.LogLevel; + +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; + +/** + *

Title : VirtualThreadTaskUtils

+ *

Description : VirtualThreadTaskUtils

+ *

DevelopTools : Idea_x64_v2026.1

+ *

DevelopSystem : macOS Sequoia 15.7.5

+ *

Company : org.xujun

+ * + * @author : XuJun + * @version : 5.0.0 + * @date : 2026/2/8 20:46 + */ +@Slf4j +public class VirtualThreadTaskUtils { + + /** + * 创建子线程并执行任务(不带日志记录,不推荐) + * + * @param callback 回调 + */ + public static void simple(VirtualThreadTaskCallback callback) { + ExecutorService service = Executors.newVirtualThreadPerTaskExecutor(); + try { + // 创建子线程,并异步提交 + CompletableFuture.runAsync(() -> { + try { + callback.callback(); + } catch (Exception e) { + log.error("子线程执行异常", e); + } + }, service); + } finally { + // 关闭线程池 + service.shutdown(); + } + } + + /** + * 创建子线程并执行任务(带日志记录,推荐) + * + * @param callback 回调 + */ + public static void execute(VirtualThreadTaskCallback callback) { + // 获取父线程的追踪日志信息 + LogTrack logTrack = LogTrackHolder.get(); + LogBus.init(LogLevel.INFO, LogBusBaseType.VIRTUAL_THREAD, logTrack).title("创建虚拟线程").save(); + ExecutorService service = Executors.newVirtualThreadPerTaskExecutor(); + try { + // 创建子线程,并异步提交 + CompletableFuture.runAsync(() -> { + // 生成子线程的追踪日志信息 + HolderLogTrack holderLogTrack = LogTrackHolder.newThread(logTrack); + ScopedValue.where(LogTrackHolder.getScoped(), holderLogTrack).run(() -> { + try { + callback.callback(); + } catch (Exception e) { + LogBus.init(LogLevel.ERROR, LogBusBaseType.VIRTUAL_THREAD).error(e).title("子线程执行异常").save(); + } + }); + }, service); + } finally { + // 关闭线程池 + service.shutdown(); + } + } + + +} diff --git a/xtools-boot-thread/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports b/xtools-boot-thread/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports new file mode 100644 index 0000000..d2ca89f --- /dev/null +++ b/xtools-boot-thread/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports @@ -0,0 +1 @@ +xtools.boot.thread.BootThreadConfiguration \ No newline at end of file diff --git a/xtools-boot-web/pom.xml b/xtools-boot-web/pom.xml new file mode 100644 index 0000000..3452bdc --- /dev/null +++ b/xtools-boot-web/pom.xml @@ -0,0 +1,20 @@ + + + 4.0.0 + + org.xujun + xtools-boot + 5.0.0 + + pom + xtools-boot-web + + + + xtools-boot-web-base + xtools-boot-web-filter + + + \ No newline at end of file diff --git a/xtools-boot-web/xtools-boot-web-base/pom.xml b/xtools-boot-web/xtools-boot-web-base/pom.xml new file mode 100644 index 0000000..af4f6fe --- /dev/null +++ b/xtools-boot-web/xtools-boot-web-base/pom.xml @@ -0,0 +1,61 @@ + + + 4.0.0 + + org.xujun + xtools-boot-web + 5.0.0 + + xtools-boot-web-base + + + + + + + org.xujun + xtools-web + + + + + + + org.xujun + xtools-boot-core + + + + org.xujun + xtools-boot-log + + + + org.xujun + xtools-boot-web-filter + + + + + + + org.springframework.boot + spring-boot-starter-web + + + + org.springframework.boot + spring-boot-starter-validation + + + + + + com.alibaba.fastjson2 + fastjson2 + + + + \ No newline at end of file diff --git a/xtools-boot-web/xtools-boot-web-base/src/main/java/xtools/boot/web/base/BootWebBaseConfiguration.java b/xtools-boot-web/xtools-boot-web-base/src/main/java/xtools/boot/web/base/BootWebBaseConfiguration.java new file mode 100644 index 0000000..a82fa73 --- /dev/null +++ b/xtools-boot-web/xtools-boot-web-base/src/main/java/xtools/boot/web/base/BootWebBaseConfiguration.java @@ -0,0 +1,28 @@ +package xtools.boot.web.base; + +import org.springframework.context.annotation.Import; +import xtools.boot.core.utils.ModuleLoadUtils; +import xtools.boot.web.base.selector.BootWebBaseImportSelector; + +/** + *

Title : BootWebBaseConfiguration

+ *

Description : BootWebBaseConfiguration

+ *

DevelopTools : Idea_x64_v2026.1

+ *

DevelopSystem : macOS Sequoia 15.7.5

+ *

Company : org.xujun

+ * + * @author : XuJun + * @version : 5.0.0 + * @date : 2026/01/01 09:30 + */ +@Import(BootWebBaseImportSelector.class) +public class BootWebBaseConfiguration { + + /** + * 构造方法 + */ + public BootWebBaseConfiguration() { + ModuleLoadUtils.loadSuccess(BootWebBaseConfiguration.class); + } + +} diff --git a/xtools-boot-web/xtools-boot-web-base/src/main/java/xtools/boot/web/base/config/JacksonConfig.java b/xtools-boot-web/xtools-boot-web-base/src/main/java/xtools/boot/web/base/config/JacksonConfig.java new file mode 100644 index 0000000..78c9fce --- /dev/null +++ b/xtools-boot-web/xtools-boot-web-base/src/main/java/xtools/boot/web/base/config/JacksonConfig.java @@ -0,0 +1,135 @@ +package xtools.boot.web.base.config; + +import com.fasterxml.jackson.annotation.JsonInclude; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import tools.jackson.core.JacksonException; +import tools.jackson.core.JsonGenerator; +import tools.jackson.core.JsonParser; +import tools.jackson.databind.BeanProperty; +import tools.jackson.databind.DeserializationContext; +import tools.jackson.databind.SerializationContext; +import tools.jackson.databind.ValueDeserializer; +import tools.jackson.databind.ValueSerializer; +import tools.jackson.databind.json.JsonMapper; +import tools.jackson.databind.module.SimpleModule; +import xtools.boot.api.anntation.IgnoreXss; +import xtools.boot.core.utils.TimeUtils; +import xtools.core.time.InstantUtils; +import xtools.web.xss.XssUtils; + +import java.time.Instant; +import java.util.Objects; + +/** + *

Title : JacksonConfig

+ *

Description : JacksonConfig

+ *

DevelopTools : Idea_x64_v2026.1

+ *

DevelopSystem : macOS Sequoia 15.7.5

+ *

Company : org.xujun

+ * + * @author : XuJun + * @version : 5.0.0 + * @date : 2026/1/6 12:15 + */ +@Configuration +public class JacksonConfig { + + /** + * 忽略 null 值 + */ + public static final JsonMapper NON_NULL; + + // 初始化 + static { + NON_NULL = JsonMapper.builder().changeDefaultPropertyInclusion(handler -> handler.withValueInclusion(JsonInclude.Include.NON_NULL)).build(); + } + + /** + * 创建 SimpleModule + * + * @return SimpleModule + */ + @Bean + public SimpleModule simpleModule() { + SimpleModule module = new SimpleModule(); + + // 序列化 + module.addSerializer(Long.class, new ValueSerializer<>() { + @Override + public void serialize(Long value, JsonGenerator gen, SerializationContext context) throws JacksonException { + if (value == null) { + gen.writeNull(); + } else { + gen.writeString(value.toString()); + } + } + }); + module.addSerializer(Instant.class, new ValueSerializer<>() { + @Override + public void serialize(Instant value, JsonGenerator gen, SerializationContext context) throws JacksonException { + if (value == null) { + gen.writeNull(); + } else { + gen.writeString(InstantUtils.format(value)); + } + } + }); + + // 反序列化 + module.addDeserializer(Instant.class, new ValueDeserializer<>() { + @Override + public Instant deserialize(JsonParser p, DeserializationContext context) throws JacksonException { + return TimeUtils.toInstant(p.getString()); + } + }); + // xss处理 + module.addDeserializer(String.class, new XssStringDeserializer()); + + return module; + } + + /** + * XSS 字符串反序列化器 + */ + static class XssStringDeserializer extends ValueDeserializer { + + /** + * 是否忽略 XSS 过滤 + */ + private final boolean ignore; + + /** + * 创建 XSS 忽略字符串反序列化器 + */ + XssStringDeserializer() { + this(false); + } + + /** + * 创建 XSS 忽略字符串反序列化器 + * + * @param ignore 是否忽略 XSS 过滤 + */ + XssStringDeserializer(boolean ignore) { + this.ignore = ignore; + } + + @Override + public String deserialize(JsonParser parser, DeserializationContext context) throws JacksonException { + String value = parser.getString(); + if (ignore) { + return value; + } + return XssUtils.filterParam(value); + } + + @Override + public ValueDeserializer createContextual(DeserializationContext context, BeanProperty property) throws JacksonException { + if (Objects.nonNull(property) && Objects.nonNull(property.getAnnotation(IgnoreXss.class))) { + return new XssStringDeserializer(true); + } + return new XssStringDeserializer(false); + } + } +} diff --git a/xtools-boot-web/xtools-boot-web-base/src/main/java/xtools/boot/web/base/config/LogTrackConfig.java b/xtools-boot-web/xtools-boot-web-base/src/main/java/xtools/boot/web/base/config/LogTrackConfig.java new file mode 100644 index 0000000..d94c24f --- /dev/null +++ b/xtools-boot-web/xtools-boot-web-base/src/main/java/xtools/boot/web/base/config/LogTrackConfig.java @@ -0,0 +1,30 @@ +package xtools.boot.web.base.config; + +import lombok.Data; +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.stereotype.Component; + +import java.util.ArrayList; +import java.util.List; + +/** + *

Title : LogTrackConfig

+ *

Description : LogTrackConfig

+ *

DevelopTools : Idea_x64_v2026.1

+ *

DevelopSystem : macOS Sequoia 15.7.5

+ *

Company : org.xujun

+ * + * @author : XuJun + * @version : 5.0.0 + * @date : 2026/2/3 15:07 + */ +@Data +@Component +@ConfigurationProperties(prefix = "sys.log.track") +public class LogTrackConfig { + + /** + * 日志追踪忽略路径 + */ + private List ignorePath = new ArrayList<>(); +} diff --git a/xtools-boot-web/xtools-boot-web-base/src/main/java/xtools/boot/web/base/config/MvcConverterConfig.java b/xtools-boot-web/xtools-boot-web-base/src/main/java/xtools/boot/web/base/config/MvcConverterConfig.java new file mode 100644 index 0000000..a397487 --- /dev/null +++ b/xtools-boot-web/xtools-boot-web-base/src/main/java/xtools/boot/web/base/config/MvcConverterConfig.java @@ -0,0 +1,34 @@ +package xtools.boot.web.base.config; + +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.core.convert.converter.Converter; +import xtools.boot.core.utils.TimeUtils; + +import java.time.Instant; + +/** + *

Title : MvcConverterConfig

+ *

Description : MvcConverterConfig

+ *

DevelopTools : Idea_x64_v2026.1

+ *

DevelopSystem : macOS Sequoia 15.7.5

+ *

Company : org.xujun

+ * + * @author : XuJun + * @version : 5.0.0 + * @date : 2026/1/6 14:44 + */ +@Configuration +public class MvcConverterConfig { + + /** + * 字符串转时间戳 + * + * @return 转换器 + */ + @Bean + public Converter stringToInstantConverter() { + return TimeUtils::toInstant; + } + +} diff --git a/xtools-boot-web/xtools-boot-web-base/src/main/java/xtools/boot/web/base/exception/GlobalControllerExceptionHandler.java b/xtools-boot-web/xtools-boot-web-base/src/main/java/xtools/boot/web/base/exception/GlobalControllerExceptionHandler.java new file mode 100644 index 0000000..3342df8 --- /dev/null +++ b/xtools-boot-web/xtools-boot-web-base/src/main/java/xtools/boot/web/base/exception/GlobalControllerExceptionHandler.java @@ -0,0 +1,238 @@ +package xtools.boot.web.base.exception; + +import jakarta.servlet.http.HttpServletRequest; +import lombok.extern.slf4j.Slf4j; +import org.springframework.http.converter.HttpMessageNotReadableException; +import org.springframework.web.HttpRequestMethodNotSupportedException; +import org.springframework.web.bind.MethodArgumentNotValidException; +import org.springframework.web.bind.annotation.ExceptionHandler; +import org.springframework.web.bind.annotation.RestControllerAdvice; +import org.springframework.web.method.annotation.HandlerMethodValidationException; +import org.springframework.web.method.annotation.MethodArgumentTypeMismatchException; +import org.springframework.web.servlet.resource.NoResourceFoundException; +import xtools.base.config.BaseParams; +import xtools.boot.api.enums.ResultType; +import xtools.boot.api.exection.BizError; +import xtools.boot.api.exection.BizPublicKeyError; +import xtools.boot.api.exection.BizWarning; +import xtools.boot.api.exection.UnauthorizedError; +import xtools.boot.api.model.dto.Result; +import xtools.boot.api.model.dto.log.LogTrack; +import xtools.boot.log.LogBus; +import xtools.boot.log.enums.LogBusBaseType; +import xtools.boot.log.holder.LogTrackHolder; +import xtools.core.StringUtils; +import xtools.core.enums.LogLevel; +import xtools.core.extend.TemplateUtils; + +import java.util.Objects; +import java.util.StringJoiner; + +/** + *

Title : GlobalExceptionHandler

+ *

Description : GlobalExceptionHandler

+ *

DevelopTools : Idea_x64_v2026.1

+ *

DevelopSystem : macOS Sequoia 15.7.5

+ *

Company : org.xujun

+ * + * @author : XuJun + * @version : 5.0.0 + * @date : 2026/1/5 15:21 + */ +@Slf4j +@RestControllerAdvice +public class GlobalControllerExceptionHandler implements BaseParams { + + /** + * 参数类型不匹配异常模板 + */ + private static final String ARGUMENT_TYPE_MISMATCH_TEMPLATE = "参数[{}]类型不匹配"; + + /** + * 参数类型不匹配 + * + * @param e 参数类型不匹配异常 + * @param request 请求 + * @return 错误结果 + */ + @ExceptionHandler(MethodArgumentTypeMismatchException.class) + public Result handleMethodArgumentTypeMismatchException(MethodArgumentTypeMismatchException e, HttpServletRequest request) { + String errMsg = TemplateUtils.format(ARGUMENT_TYPE_MISMATCH_TEMPLATE, e.getPropertyName()); + logException("参数类型不匹配", errMsg, request, e); + return Result.badRequest(errMsg); + } + + /** + * 参数校验异常 + * + * @param e 参数校验异常 + * @param request 请求 + * @return 错误结果 + */ + @ExceptionHandler(HandlerMethodValidationException.class) + public Result handleHandlerMethodValidationException(HandlerMethodValidationException e, HttpServletRequest request) { + // 错误结果 + StringJoiner msg = new StringJoiner(CP_COMMA); + // 处理参数验证结果 + e.getParameterValidationResults().forEach(validation -> { + // 获取参数名称 + String paramName = validation.getMethodParameter().getParameterName(); + // 获取字段约束违规 + validation.getResolvableErrors().forEach(item -> msg.add(paramName + CP_COLON + item.getDefaultMessage())); + }); + String errMsg = msg.toString(); + logException("参数校验异常", errMsg, request, e); + return Result.badRequest(errMsg); + } + + /** + * 参数校验异常 + * + * @param e 参数校验异常 + * @param request 请求 + * @return 错误结果 + */ + @ExceptionHandler(MethodArgumentNotValidException.class) + public Result handleMethodArgumentNotValidException(MethodArgumentNotValidException e, HttpServletRequest request) { + // 错误结果 + StringJoiner msg = new StringJoiner(CP_COMMA); + // 处理参数验证结果 + e.getBindingResult().getFieldErrors().forEach(fieldError -> { + String fieldName = fieldError.getField(); + String errorMessage = fieldError.getDefaultMessage(); + msg.add(fieldName + CP_COLON + errorMessage); + }); + String errMsg = msg.toString(); + logException("参数校验异常", errMsg, request, e); + return Result.badRequest(errMsg); + } + + /** + * 消息不可读异常 + * + * @param e 消息不可读异常 + * @param request 请求 + * @return 错误结果 + */ + @ExceptionHandler(HttpMessageNotReadableException.class) + public Result handleHttpMessageNotReadableException(HttpMessageNotReadableException e, HttpServletRequest request) { + String errMsg = "请求参数格式错误"; + logException(errMsg, e.getMessage(), request, e); + return Result.badRequest(errMsg); + } + + /** + * 请求方法不支持 + * + * @param e 请求方法不支持异常 + * @param request 请求 + * @return 错误结果 + */ + @ExceptionHandler(HttpRequestMethodNotSupportedException.class) + public Result handleHttpRequestMethodNotSupportedException(HttpRequestMethodNotSupportedException e, HttpServletRequest request) { + logException("请求方法不支持", null, request, e); + return new Result<>(ResultType.METHOD_NOT_ALLOWED, null); + } + + /** + * 资源未找到 + * + * @param e 资源未找到异常 + * @param request 请求 + * @return 错误结果 + */ + @ExceptionHandler(NoResourceFoundException.class) + public Result handleNoResourceFoundException(NoResourceFoundException e, HttpServletRequest request) { + logException("资源未找到", null, request, e); + return new Result<>(ResultType.NOT_FOUND, null); + } + + /** + * 认证异常 + * + * @param e 认证异常 + * @param request 请求 + * @return 错误结果 + */ + @ExceptionHandler(UnauthorizedError.class) + public Result handleUnauthorizedErrorException(UnauthorizedError e, HttpServletRequest request) { + logException("认证异常", e.getMessage(), request, e); + return new Result<>(ResultType.UNAUTHORIZED, null); + } + + + /** + * 公钥错误 + * + * @param e 公钥错误 + * @param request 请求 + * @return 错误结果 + */ + @ExceptionHandler(BizPublicKeyError.class) + public Result handleBizPublicKeyErrorException(BizPublicKeyError e, HttpServletRequest request) { + logException("公钥错误", e.getMessage(), request, e); + return new Result<>(true, e.getCode(), e.getMessage(), null); + } + + /** + * 业务异常 + * + * @param e 业务异常 + * @param request 请求 + * @return 错误结果 + */ + @ExceptionHandler(BizError.class) + public Result handleBizErrorException(BizError e, HttpServletRequest request) { + logException("业务异常", e.getMessage(), request, e); + return new Result<>(true, e.getCode(), e.getMessage(), null); + } + + /** + * 业务警告 + * + * @param e 业务异常 + * @param request 请求 + * @return 警告结果 + */ + @ExceptionHandler(BizWarning.class) + public Result handleBizWarningException(BizWarning e, HttpServletRequest request) { + logException("业务警告", e.getMessage(), request, e); + return new Result<>(true, e.getCode(), e.getMessage(), null); + } + + /** + * 默认全局异常处理 + * + * @param e 异常 + * @param request 请求 + * @return 错误结果 + */ + @ExceptionHandler(Exception.class) + public Result handleException(Exception e, HttpServletRequest request) { + logException("默认全局异常处理", null, request, e); + LogTrack logTrack = LogTrackHolder.getDefNull(); + if (Objects.nonNull(logTrack)) { + LogBus.init(LogLevel.ERROR, LogBusBaseType.CONTROLLER, logTrack).title("默认全局异常处理").error(e).save(); + } else { + log.error("默认全局异常处理", e); + } + return new Result<>(ResultType.INTERNAL_SERVER_ERROR, null); + } + + /** + * 日志异常 + * + * @param type 异常类型 + * @param msg 错误消息 + * @param request 请求 + * @param e 异常 + */ + private void logException(String type, String msg, HttpServletRequest request, Exception e) { + if (StringUtils.isBlank(msg)) { + log.debug("请求[{}]异常,类型[{}]", request.getRequestURI(), type, e); + } else { + log.debug("请求[{}]异常,类型[{}],错误消息[{}]", request.getRequestURI(), type, msg); + } + } + +} diff --git a/xtools-boot-web/xtools-boot-web-base/src/main/java/xtools/boot/web/base/filter/CommonFilter.java b/xtools-boot-web/xtools-boot-web-base/src/main/java/xtools/boot/web/base/filter/CommonFilter.java new file mode 100644 index 0000000..e727d7b --- /dev/null +++ b/xtools-boot-web/xtools-boot-web-base/src/main/java/xtools/boot/web/base/filter/CommonFilter.java @@ -0,0 +1,57 @@ +package xtools.boot.web.base.filter; + +import com.alibaba.fastjson2.JSONObject; +import jakarta.servlet.FilterChain; +import jakarta.servlet.ServletException; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import lombok.extern.slf4j.Slf4j; +import org.jspecify.annotations.NonNull; +import org.springframework.core.Ordered; +import org.springframework.stereotype.Component; +import xtools.base.config.BaseParams; +import xtools.boot.api.enums.ResultType; +import xtools.boot.api.model.dto.Result; +import xtools.boot.core.holder.CommonHolder; +import xtools.boot.web.filter.base.BaseFilter; +import xtools.web.HttpServletUtils; + +import java.io.IOException; + +/** + *

Title : CommonFilter

+ *

Description : CommonFilter

+ *

DevelopTools : Idea_x64_v2026.1

+ *

DevelopSystem : macOS Sequoia 15.7.5

+ *

Company : org.xujun

+ * + * @author : XuJun + * @version : 5.0.0 + * @date : 2026/1/6 16:37 + */ +@Slf4j +@Component +public class CommonFilter extends BaseFilter implements Ordered, BaseParams { + + @Override + public int getOrder() { + return CP_NUM100 + CP_NUM1; + } + + @Override + protected void doFilterInternal( + @NonNull HttpServletRequest request, + @NonNull HttpServletResponse response, + @NonNull FilterChain filterChain + ) { + ScopedValue.where(CommonHolder.getScoped(), CommonHolder.create()).run(() -> { + try { + filterChain.doFilter(request, response); + } catch (IOException | ServletException e) { + log.error("添加通用ScopedValue异常", e); + HttpServletUtils.respWriter(response, JSONObject.from(new Result<>(ResultType.INTERNAL_SERVER_ERROR, null))); + } + }); + } + +} diff --git a/xtools-boot-web/xtools-boot-web-base/src/main/java/xtools/boot/web/base/filter/LogTrackFilter.java b/xtools-boot-web/xtools-boot-web-base/src/main/java/xtools/boot/web/base/filter/LogTrackFilter.java new file mode 100644 index 0000000..a973d96 --- /dev/null +++ b/xtools-boot-web/xtools-boot-web-base/src/main/java/xtools/boot/web/base/filter/LogTrackFilter.java @@ -0,0 +1,79 @@ +package xtools.boot.web.base.filter; + +import com.alibaba.fastjson2.JSONObject; +import jakarta.annotation.Resource; +import jakarta.servlet.FilterChain; +import jakarta.servlet.ServletException; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import lombok.extern.slf4j.Slf4j; +import org.jspecify.annotations.NonNull; +import org.springframework.core.Ordered; +import org.springframework.stereotype.Component; +import xtools.base.config.BaseParams; +import xtools.boot.api.constant.BootCommonConstant; +import xtools.boot.api.enums.ResultType; +import xtools.boot.api.model.dto.Result; +import xtools.boot.api.model.dto.log.HolderLogTrack; +import xtools.boot.core.utils.PathPatternUtils; +import xtools.boot.log.holder.LogTrackHolder; +import xtools.boot.web.base.config.LogTrackConfig; +import xtools.boot.web.filter.base.BaseFilter; +import xtools.core.StringUtils; +import xtools.web.HeaderUtils; +import xtools.web.HttpServletUtils; + +import java.io.IOException; + +/** + *

Title : LogTrackFilter

+ *

Description : LogTrackFilter

+ *

DevelopTools : Idea_x64_v2026.1

+ *

DevelopSystem : macOS Sequoia 15.7.5

+ *

Company : org.xujun

+ * + * @author : XuJun + * @version : 5.0.0 + * @date : 2026/1/6 16:37 + */ +@Slf4j +@Component +public class LogTrackFilter extends BaseFilter implements Ordered, BaseParams { + + @Resource + private LogTrackConfig logTrackConfig; + + @Override + public int getOrder() { + return CP_NUM100; + } + + @Override + protected void doFilterInternal( + @NonNull HttpServletRequest request, + @NonNull HttpServletResponse response, + @NonNull FilterChain filterChain + ) { + String uri = HeaderUtils.getUri(request); + String headerLogTrack = HeaderUtils.getHeader(request, BootCommonConstant.LOG_TRACK); + HolderLogTrack logTrack; + if (StringUtils.isBlank(headerLogTrack)) { + logTrack = LogTrackHolder.newMain(); + } else { + logTrack = LogTrackHolder.newByBase64(headerLogTrack); + } + ScopedValue.where(LogTrackHolder.getScoped(), logTrack).run(() -> { + // 判断该uri是否忽略记录日志 + if (PathPatternUtils.match(logTrackConfig.getIgnorePath(), uri)) { + LogTrackHolder.saveLog(false); + } + try { + filterChain.doFilter(request, response); + } catch (IOException | ServletException e) { + log.error("添加日志追踪ScopedValue异常", e); + HttpServletUtils.respWriter(response, JSONObject.from(new Result<>(ResultType.INTERNAL_SERVER_ERROR, null))); + } + }); + } + +} diff --git a/xtools-boot-web/xtools-boot-web-base/src/main/java/xtools/boot/web/base/selector/BootWebBaseImportSelector.java b/xtools-boot-web/xtools-boot-web-base/src/main/java/xtools/boot/web/base/selector/BootWebBaseImportSelector.java new file mode 100644 index 0000000..a4f5d6a --- /dev/null +++ b/xtools-boot-web/xtools-boot-web-base/src/main/java/xtools/boot/web/base/selector/BootWebBaseImportSelector.java @@ -0,0 +1,36 @@ +package xtools.boot.web.base.selector; + +import org.jspecify.annotations.NonNull; +import org.springframework.beans.factory.support.BeanDefinitionRegistry; +import org.springframework.context.annotation.ClassPathBeanDefinitionScanner; +import org.springframework.context.annotation.ImportBeanDefinitionRegistrar; +import org.springframework.core.type.AnnotationMetadata; + +/** + *

Title : BootWebBaseImportSelector

+ *

Description : BootWebBaseImportSelector

+ *

DevelopTools : Idea_x64_v2026.1

+ *

DevelopSystem : macOS Sequoia 15.7.5

+ *

Company : org.xujun

+ * + * @author : XuJun + * @version : 5.0.0 + * @date : 2026/01/01 09:30 + */ +public class BootWebBaseImportSelector implements ImportBeanDefinitionRegistrar { + + /** + * 根据给定的注释元数据,根据需要注册bean + * + * @param importingClassMetadata AnnotationMetadata + * @param registry BeanDefinitionRegistry + */ + @Override + public void registerBeanDefinitions(@NonNull AnnotationMetadata importingClassMetadata, @NonNull BeanDefinitionRegistry registry) { + // 构建扫描对象 + ClassPathBeanDefinitionScanner scanner = new ClassPathBeanDefinitionScanner(registry, true); + // 扫描包下路径 + scanner.scan("xtools.boot.web.base"); + } + +} \ No newline at end of file diff --git a/xtools-boot-web/xtools-boot-web-base/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports b/xtools-boot-web/xtools-boot-web-base/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports new file mode 100644 index 0000000..99aa88e --- /dev/null +++ b/xtools-boot-web/xtools-boot-web-base/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports @@ -0,0 +1 @@ +xtools.boot.web.base.BootWebBaseConfiguration \ No newline at end of file diff --git a/xtools-boot-web/xtools-boot-web-filter/pom.xml b/xtools-boot-web/xtools-boot-web-filter/pom.xml new file mode 100644 index 0000000..3a6d836 --- /dev/null +++ b/xtools-boot-web/xtools-boot-web-filter/pom.xml @@ -0,0 +1,33 @@ + + + 4.0.0 + + org.xujun + xtools-boot-web + 5.0.0 + + xtools-boot-web-filter + + + + + + + + org.xujun + xtools-boot-core + + + + + + + org.springframework.boot + spring-boot-starter-web + + + + + \ No newline at end of file diff --git a/xtools-boot-web/xtools-boot-web-filter/src/main/java/xtools/boot/web/filter/BootWebFilterConfiguration.java b/xtools-boot-web/xtools-boot-web-filter/src/main/java/xtools/boot/web/filter/BootWebFilterConfiguration.java new file mode 100644 index 0000000..d05ebfa --- /dev/null +++ b/xtools-boot-web/xtools-boot-web-filter/src/main/java/xtools/boot/web/filter/BootWebFilterConfiguration.java @@ -0,0 +1,28 @@ +package xtools.boot.web.filter; + +import org.springframework.context.annotation.Import; +import xtools.boot.core.utils.ModuleLoadUtils; +import xtools.boot.web.filter.selector.BootWebFilterImportSelector; + +/** + *

Title : BootWebFilterConfiguration

+ *

Description : BootWebFilterConfiguration

+ *

DevelopTools : Idea_x64_v2026.1

+ *

DevelopSystem : macOS Sequoia 15.7.5

+ *

Company : org.xujun

+ * + * @author : XuJun + * @version : 5.0.0 + * @date : 2026/01/01 09:30 + */ +@Import(BootWebFilterImportSelector.class) +public class BootWebFilterConfiguration { + + /** + * 构造方法 + */ + public BootWebFilterConfiguration() { + ModuleLoadUtils.loadSuccess(BootWebFilterConfiguration.class); + } + +} diff --git a/xtools-boot-web/xtools-boot-web-filter/src/main/java/xtools/boot/web/filter/base/BaseFilter.java b/xtools-boot-web/xtools-boot-web-filter/src/main/java/xtools/boot/web/filter/base/BaseFilter.java new file mode 100644 index 0000000..ef08b70 --- /dev/null +++ b/xtools-boot-web/xtools-boot-web-filter/src/main/java/xtools/boot/web/filter/base/BaseFilter.java @@ -0,0 +1,49 @@ +package xtools.boot.web.filter.base; + +import jakarta.servlet.http.HttpServletRequest; +import org.jspecify.annotations.NonNull; +import org.springframework.web.filter.OncePerRequestFilter; + +/** + *

Title : BaseFilter

+ *

Description : BaseFilter

+ *

DevelopTools : Idea_x64_v2026.1

+ *

DevelopSystem : macOS Sequoia 15.7.5

+ *

Company : org.xujun

+ * + * @author : XuJun + * @version : 5.0.0 + * @date : 2026/3/8 11:06 + */ +public abstract class BaseFilter extends OncePerRequestFilter { + + /** + * 跳过过滤器标识 + */ + private static final String SKIP_FILTER = "skip.filters"; + + /** + * 添加跳过过滤器标识 + * + * @param request 请求 + */ + public void addSkipFilter(HttpServletRequest request) { + request.setAttribute(SKIP_FILTER, true); + } + + /** + * 跳过过滤器 + * + * @param request 请求 + * @return 是否跳过过滤器 + */ + @Override + protected boolean shouldNotFilter(@NonNull HttpServletRequest request) { + Object attribute = request.getAttribute(SKIP_FILTER); + if (attribute instanceof Boolean skip) { + return skip; + } + return false; + } + +} diff --git a/xtools-boot-web/xtools-boot-web-filter/src/main/java/xtools/boot/web/filter/common/SkipFilter.java b/xtools-boot-web/xtools-boot-web-filter/src/main/java/xtools/boot/web/filter/common/SkipFilter.java new file mode 100644 index 0000000..7d88fda --- /dev/null +++ b/xtools-boot-web/xtools-boot-web-filter/src/main/java/xtools/boot/web/filter/common/SkipFilter.java @@ -0,0 +1,69 @@ +package xtools.boot.web.filter.common; + +import jakarta.servlet.FilterChain; +import jakarta.servlet.ServletException; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import org.jspecify.annotations.NonNull; +import org.springframework.core.Ordered; +import org.springframework.stereotype.Component; +import xtools.base.config.BaseParams; +import xtools.boot.core.interfaces.FilterWhitelist; +import xtools.boot.core.utils.PathPatternUtils; +import xtools.boot.core.utils.SpringContextUtils; +import xtools.boot.web.filter.base.BaseFilter; +import xtools.core.CollectionUtils; + +import java.io.IOException; +import java.util.Collection; +import java.util.HashSet; +import java.util.Set; + +/** + *

Title : SkipFilter

+ *

Description : SkipFilter

+ *

DevelopTools : Idea_x64_v2026.1

+ *

DevelopSystem : macOS Sequoia 15.7.5

+ *

Company : org.xujun

+ * + * @author : XuJun + * @version : 5.0.0 + * @date : 2026/3/8 11:00 + */ +@Component +public class SkipFilter extends BaseFilter implements Ordered, BaseParams { + + /** + * 白名单 + */ + private static final Set WHITE_LIST = new HashSet<>(); + + @Override + public int getOrder() { + return CP_NUM0; + } + + @Override + protected void initFilterBean() throws ServletException { + super.initFilterBean(); + Collection beanList = SpringContextUtils.getBeanList(FilterWhitelist.class); + if (CollectionUtils.isEmpty(beanList)) { + return; + } + beanList.forEach(item -> WHITE_LIST.addAll(item.add())); + } + + @Override + protected void doFilterInternal( + @NonNull HttpServletRequest request, + @NonNull HttpServletResponse response, + @NonNull FilterChain filterChain + ) throws ServletException, IOException { + // 校验白名单 + if (PathPatternUtils.match(WHITE_LIST, request.getRequestURI())) { + addSkipFilter(request); + } + filterChain.doFilter(request, response); + } + +} diff --git a/xtools-boot-web/xtools-boot-web-filter/src/main/java/xtools/boot/web/filter/selector/BootWebFilterImportSelector.java b/xtools-boot-web/xtools-boot-web-filter/src/main/java/xtools/boot/web/filter/selector/BootWebFilterImportSelector.java new file mode 100644 index 0000000..f5c6949 --- /dev/null +++ b/xtools-boot-web/xtools-boot-web-filter/src/main/java/xtools/boot/web/filter/selector/BootWebFilterImportSelector.java @@ -0,0 +1,36 @@ +package xtools.boot.web.filter.selector; + +import org.jspecify.annotations.NonNull; +import org.springframework.beans.factory.support.BeanDefinitionRegistry; +import org.springframework.context.annotation.ClassPathBeanDefinitionScanner; +import org.springframework.context.annotation.ImportBeanDefinitionRegistrar; +import org.springframework.core.type.AnnotationMetadata; + +/** + *

Title : BootWebFilterImportSelector

+ *

Description : BootWebFilterImportSelector

+ *

DevelopTools : Idea_x64_v2026.1

+ *

DevelopSystem : macOS Sequoia 15.7.5

+ *

Company : org.xujun

+ * + * @author : XuJun + * @version : 5.0.0 + * @date : 2026/01/01 09:30 + */ +public class BootWebFilterImportSelector implements ImportBeanDefinitionRegistrar { + + /** + * 根据给定的注释元数据,根据需要注册bean + * + * @param importingClassMetadata AnnotationMetadata + * @param registry BeanDefinitionRegistry + */ + @Override + public void registerBeanDefinitions(@NonNull AnnotationMetadata importingClassMetadata, @NonNull BeanDefinitionRegistry registry) { + // 构建扫描对象 + ClassPathBeanDefinitionScanner scanner = new ClassPathBeanDefinitionScanner(registry, true); + // 扫描包下路径 + scanner.scan("xtools.boot.web.filter"); + } + +} \ No newline at end of file diff --git a/xtools-boot-web/xtools-boot-web-filter/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports b/xtools-boot-web/xtools-boot-web-filter/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports new file mode 100644 index 0000000..12aa76d --- /dev/null +++ b/xtools-boot-web/xtools-boot-web-filter/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports @@ -0,0 +1 @@ +xtools.boot.web.filter.BootWebFilterConfiguration \ No newline at end of file