# treasure **Repository Path**: packagejava/treasure ## Basic Information - **Project Name**: treasure - **Description**: Treasure是一个Java技术生态项目,涵盖了单体、微服务、DDD等架构实践,以兴趣、学习目的、技术积累为理念,逐步完善迭代。主要包含学习成长过程中一些技术点、工作中积累的一些心得,面试中一些业务场景模拟及解决方案一些常见、通用业务的解决方案、合理应用设计模式进行一些业务代码的重构优化、常用小轮子的积累、一些更优雅的编码实现、通用场景封装等内容。 - **Primary Language**: Java - **License**: Apache-2.0 - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 65 - **Created**: 2025-07-25 - **Last Updated**: 2025-07-25 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README

Treasure

2020-05-05~至今

## 初衷 积累,成长 ## 分支规划 > 目前分支规划存在一定的不合理性,将在后期进行完善优化.所有的架构都是基于业务进行演化的. > > 工具类 > 工具包 > 功能模块 > 场景启动器 > 功能服务 > 分布式 + `master` 微服务架构 业务场景模拟,学习案例 + `SpringBoot 2.3.2.RELEASE` + `SpringCloud Hoxton.SR8` + `Nacos` + `OpenFeign 2.2.5.RELEASE` + `Hystrix 2.2.5.RELEASE ` + `gateway` + `study` 单体架构、学习案例 + `SpringBoot 2.3.12.RELEASE` + `scene` 单体架构、最佳实践、插件化、模块化、配置化 + `SpringBoot 2.7.5` + `Spring Security 2.7.5` + `cloud` 规划中 + `SpringBoot 2.4.2` + `SpringCloud 2020.0.6` + `eureka 3.0.6` + `hystrix 2.2.10.RELEASE` + `openfeign 3.0.7` + `ribbon 2.2.10.RELEASE` + `cloud alibaba` 规划中 + `nacos` + `gateway` + `sentinel` ## 简介 > Treasure是一个Java技术生态项目,涵盖了单体、微服务、DDD等架构实践,以兴趣、学习目的、技术积累为理念,逐步完善迭代。主要包含学习成长过程中一些技术点、工作中积累的一些心得,面试中一些业务场景模拟及解决方案一些常见、通用业务的解决方案、合理应用设计模式进行一些业务代码的重构优化、常用小轮子的积累、一些更优雅的编码实现、通用场景封装等内容 > 单体+微服务灵活切换的企业级脚手架 ## 模块 ### `scene`分支 > 为了方便随取随用,没有做过多的聚合,各个模块相对独立 ~~~ top.dingwen.io ├── scene[综合场景启动器使用示例] │ └── pom.xml[ Maven依赖] ├── starter[启动器集] │ └── base-spring-boot-starter[基础场景启动器][已上中央仓库,最新版本:1.2.4] │ └── api-docs-spring-boot-starter[API文档再封装场景启动器] │ └── screw-spring-boot-starter[数据库文档生成场景启动器][已上中央仓库,最新版本:1.0.2] │ └── webplus-spring-boot-starter[Web再封装场景启动器][已上中央仓库,最新版本:1.2.6] │ └── redis-spring-boot-starter[Redis场景启动器][已上中央仓库,最新版本:1.2.5] │ └── caffeine-spring-boot-starter[本地缓存启动器][已上中央仓库,最新版本:1.0.5] │ └── async-spring-boot-starter[异步场景启动器][已上中央仓库,最新版本:1.1.4] │ └── enums-spring-boot-starter[枚举场景启动器][已上中央仓库,最新版本:1.2.6] │ └── mybatisplus-spring-boot-starter[MybatisPlus场景启动器][已上中央仓库,最新版本:1.0.3] │ └── log-spring-boot-starter[日志场景启动器][已上中央仓库,最新版本:1.0.3] │ └── logv-spring-boot-starter[日志查看启动器][已上中央仓库,最新版本:1.0.2] │ └── email-spring-boot-starter[电子邮件场景启动器][已上中央仓库,最新版本:1.0.3] │ └── mongo-spring-boot-starter[MongoDB场景启动器] │ └── xxl-job-spring-boot-starter[xxljob场景启动器] │ └── translate-spring-boot-starter[翻译场景启动器][已上中央仓库,最新版本:1.0.0] │ └── oss-spring-boot-starter[文件存储场景启动器][已上中央仓库,最新版本:1.0.0] │ └── pipeline-spring-boot-starter[责任链Pipeline场景启动器][已上中央仓库,最新版本:1.0.2] │ └── bar-spring-boot-starter[进度条场景启动器][已上中央仓库,最新版本:1.0.5] │ └── dic-spring-boot-starter[字典场景启动器][已上中央仓库,最新版本:1.0.1] │ └── excel-spring-boot-starter[Excel场景启动器] │ └── config-spring-boot-starter[系统配置场景启动器] │ └── quartz-spring-boot-starter[Quartz定时任务场景启动器] │ └── file-spring-boot-starter[文件场景启动器] │ └── change-log-spring-boot-starter[变更记录场景启动器] │ └── db-backup-spring-boot-starter[数据归档场景启动器] │ └── event-spring-boot-starter[事件场景启动器] │ └── sensitive-spring-boot-stareter[数据脱敏场景启动 器][最新版本:1.0.0] │ └── jwt-spring-boot-starter[JWT场景启动器][已上中央仓库,最新版本:1.0.2] │ └── security-plus-spring-boot-starter[Security场景启动器] │ └── auth-spring-boot-starter[认证场景启动器] │ └── dcache-spring-boot-starter[二级缓存启动器][已上中央仓库,最新版本:1.0.5] │ └── websocket-spring-boot-starter[WebSocket启动器][最新版本:1.0.0] │ └── encrypt-api-spring-boot-starter[API加解密场景启动器][最新版本:1.0.0] │ └── kkFile-docker[文件预览场景启动器] │ └── 规划中[监控场景启动器] │ └── 规划中[审核场景启动器] │ └── 规划中[Flowable启动器] │ └── 规划中[Camunda启动器] │ └── 规划中[微信公众号开发启动器] │ └── 规划中[钉钉开发启动器] │ └── 规划中[重要表单变更日志启动器] │ └── 规划中[elasticsearch启动器] │ └── 规划中[Dingger消息告警二次封装] │ └── 规划中[文本预览,dox动态渲染poi-tl,动态模板管理] ├── pom.xml[Maven依赖] ~~~ ### `study`分支 ~~~ com.dingwen ├── treasure-canal-client[canal客户端 [80]] ├── treasure-kettle[kettle集成企业级解决方案 [9999]] ├── treasure-websocket[websocket方案 [8081] [8080]] ├── treasure-sms4j[通用短信解决方案 [8080]] ├── treasure-poi-tl[word模板渲染解决方案 [8080]] ├── treasure-jimu-report[开源报表解决方案 [8080]] ├── treasure-dingtalk-ger[钉钉,企业微信预警机器人解决方案 [8080]] ├── 规划中[认证解决方案 [8080]] ├── 规划中[单点登录解决方案 [8080]] ├── 规划中[动态表单解决方案 [8080]] ├── treasure-gof[大话设计模式] ├── pom.xml[Maven依赖] ~~~ ### `master`分支 ~~~ com.cdn com.dingwen ├── treasure-auth[认证服务 [20902]] ├── treasure-business[业务服务 [20903]] ├── treasure-admin[监控服务 [20901]] ├── treasure-common[通用模块] │ └── common-pom[依赖管理模块] │ └── common-base[基础模块] │ └── common-beansearcher[对象搜索] │ └── common-config[基础配置] │ └── common-core[ 核心模块] │ └── common-jpa[持久层JPA] │ └── common-jwt[JWT令牌] │ └── common-knifej[接口文档] │ └── common-model[通用MODEL] │ └── common-mongodb[MongoDB] │ └── common-mybatisplus[持久层Mybatisplus] │ └── common-rabbitmq[RabbitMQ] │ └── common-redis[Redis] │ └── common-security[安全模块] │ └── common-sensitive[自定义注解实现数据脱敏] │ └── common-web[WEB模块] │ └── common-tkmybatis[tkmybatis模块] │ └── common-minio[minio文件存储] │ └── common-easyexcel[excel 文件导入导出] │ └── common-influxdb[时序数据库案例] │ └── common-open-api[open api 案例] │ └── open-api-baidu-map[百度地图] │ └── open-api-sms[阿里云短信] │ └── open-api-tx[天行数据基础服务] │ └── open-api-wechat-pub[微信公众号] │ └── open-api-baidu-map[百度地图] │ └── open-api-tianxing-rainbow[天行数据彩虹屁] ├── treasure-gateway[网关服务 [20904]] ├── treasure-log[日志服务 [20905]] ├── treasure-manage[后台管理 [20906]] ├── treasure-file-generate[ 文件生成服务 [20907]] ├── treasure-task-quartz[定时任务(Quarzt实现) [20908]] ├── treasure-file[文件服务 [20909]] ├── treasure-code-generate[代码生成服务 [20910]] ├── treasure-slow-sql[慢SQL [20911]] ├── treasure-xxl-job-admin[xxl-job-admin [20933]] ├── logs[日志] ├── sql[sql] ├── img[图片] ├── pom.xml[公共依赖] ~~~ ### `cloud` > SpringCloud 实践 ### `alibaba_cloud` > 阿里系微服务生态实践 ### `ddd` > DDD架构实践 ## 架构图 > 参考若依 > 规划中 ## 概览
### API文档 [在线文档](https://www.apifox.cn/apidoc/shared-2d9191fd-dfd4-45a3-bc29-57500b3a5f1b) ## 技术点 | | | | | | | | | | | | ----------- | --------------------- | --------------- | ------------------------------ | -------------- | ------------------------ | ------- | --- | --- | ---------------- | | `Redis` | `Mysql` | `MongoDB` | `Canal` | `Postgresql` | `ElasticSearch` | | | | Alibaba Druid | | `Spring` | `SpringMVC` | `SpringBoot` | `Spring Security` | `Spring-retry` | `mybatis-plus-join-boot` | | | | SpringBoot Admin | | `Mybatis` | `MybatisPlus` | `TK Mybatis` | | | | | | | JPA | | `Mapstruct` | `MapstructPlus` | `Hutool` | `Screw` | `BeanSearcher` | `EasyExcel` | knife4j | | | p6spy | | `ip2region` | `Guava` | `commons-lang3` | `Lombok` | | | | | | Maven | | `xxl-job` | | | | | | | | | Quartz | | `Nacos` | `SpringCloud Alibaba` | `GateWay` | `Feign` | `Hystrix` | | | | | Ribbon | | `RabbitMQ` | `RocketMQ` | | | | | | | | Kafka | | `Kettle` | | | | | | | | | ELK | | `Thymeleaf` | | | | | | | | | Layui | | `Minio` | `Aliyun OSS` | | | | | | | | JWT | | `Websocket` | | | | | | | | | sms4j | | `Bistoury` | `poi-tl` | `jimu-report` | `dingtalk-spring-boot-starter` | | | | | | Arthas | ## 通用应用场景Starter封装 > 接口文档地址: https://apifox.com/apidoc/shared-8d3d332d-b936-4ec1-8d14-516d89a8f85d ### 基础场景启动器 > 基于`SpringBoot2.1.7`和`JDK1.8`封装的基础场景启动器 #### 核心功能 + 基础异常 + 通用工具类 + `logback`配置优化,异步日志优化`disruptor` + 内置`logback`配置 `log4j2-spring.xml` + 统一内部入参设计`BaseParam` + 统一入参设计`BaseRequest`,`PageRequest` + 统一出参设计`BaseResponse`,`MultiResponse`,`PageResponse`,`RestResponse`,`SingleResponse` + 统一常量管理`IConstant`,统一规范 + 统一锁管理`LockTemplate`,`LockMethodAspect`,`LockMethod` + 统一异常处理`ExceptionCode`,`BaseRuntimeException`,`BusinessException`,`SystemException`,`AbstractExceptionHandler` #### 使用 ##### 引入依赖 ```xml top.dingwen.io base-spring-boot-starter 1.2.4 ``` #### 核心工具类 + `AspectUtils`: 切面工具类 + `BeanCopyUtils`: bean深拷贝工具(基于 cglib 性能优异) + `DateUtils`: 日期工具类 + `LaExUtils`: Lambdas受检异常封装处理 + `AddressUtils`: 地址工具类 + `RegionUtils`: IP离线定位 + `ReflectUtils`: 反射工具 + `SqlUtils`: sql操作工具类 + `StringUtils`: 字符串操作工具 + `StreamUtils`: stream工具类 + `distinct()` List复杂类型去重+简单类型去重 + `group2Map(Collection collection, Integer groupSize)` List任务分组 + ` List map(Collection collection, Function mapper)`: 对象字段收集 + `BigDecimal safeAdds(List data)`: 安全求和 + ... + `IdUtils`: ID工具类 + `Validatetils`: 编程式灵活校验工具 + `SpringUtils`: Spring工具类(基于Hutool拓展,获取代理对象)+发布事件持久化拓展 + ` T safeGetBean(String name)`: 安全获取对象 + ` T safeGetBean(Class clazz)`: 安全获取对象 + ... + `OptimizeUtils` : 优化工具类 + `PinYin4jUtils`: 文字转汉语拼音 + `MapstructUtils`: 对象映射拷贝工具再封装 + `AsyncTaskUtils`: 多线程任务编排 + `EncryptUtils`: 加解密工具类 + `JaDocUtils`: Java文档获取工具类(兼容JDK1.8)(1.9之后版本反射API能够获取) + `JsonSchemaUtils`: Java字节码转JsonSchema (包括类,属性及文档注释) + `GsonUtils`: 谷歌Json工具包 + `SupWarsConstant`: 抑制警告常量类 #### 用户上下文支持 + `ICurrentUserService`: 当前用户接口 + `CurrentUserHelper`: 快捷访问工具 + `ScopeContainer`: 范围控制拓展 #### 使用案例 ##### `OptimizeUtils` > 参考Spring StopWatch 的拓展优化,精确计算执行耗时,执行次数,方便进行优化 > `OptimizeUtilController` ```java /** *  OptimizeUtilController: 优化工具测试 *  @author dingwen *  @since 2022/8/28 */ @Api(tags = "优化工具API") @RestController @Slf4j @RequestMapping("optimize") @RequiredArgsConstructor public class OptimizeUtilController { @ApiOperation(value = "API使用测试") @GetMapping public void test() { optimizeApi(); } @SneakyThrows(Throwable.class) private void optimizeApi() { OptimizeUtil.start("任务1"); TimeUnit.SECONDS.sleep(1); OptimizeUtil.stop("任务1"); for (int i = 0; i < 100; i++) { OptimizeUtil.start("任务2"); for (int j = 0; j < 1000; j++) { OptimizeUtil.start("任务2-1"); OptimizeUtil.stop("任务2-1"); } OptimizeUtil.stop("任务2"); } OptimizeUtil.print("任务2"); OptimizeUtil.print(); } } ``` ![优化工具类](./img/OptimizeUtil.png) ##### `SpringUtils` > `publishEvent(event)`,发布订阅事件拓展,提供基础事件对象`BaseEvent`,同一发布事件入口,进行事件持久化方便进行监控和重发.当然监听实现方需要考虑幂等实现 > 将提供`event-spring-boot-starter`实现`IEvent`接口实现事件对象持久化,重发等功能 ```java /** * 发布事件,将指定的应用事件发布到Spring事件传播机制中。 * @param event 应用事件对象,代表一个具体的事件实例,将被Spring应用上下文中的事件监听器处理。 */ public static void publishEvent(BaseEvent event){ // 将事件对象持久化到数据库,方便实现重发 Map events = SpringUtil.getBeansOfType(IEvent.class); events.forEach((eName,ev) -> ev.saveEvent(event)); // 将事件发布到Spring应用上下文中 SpringUtil.publishEvent(event); } ``` #### 可靠的事件订阅机制 > 参考: 事件场景启动器 #### 获取当前用户服务 `ICurrentUserService` > 提供顶层接口,供外部实现.从而实现与权限模块解耦. **注意: 次接口仅仅支持单实现,不支持策略**
#### 统一加锁逻辑 `LockTemplate` > 基于`ReentrantLock`封装使用,规范加锁使用,支持注解`LockMethod`. > 注意: 当方法过长,逻辑复杂需要较多资源时,应该使用手动加锁的方式或拆分逻辑 ### web场景启动器 > `SpringBoot Web`的二次封装 #### 核心功能 + 序列化反序列化配置 + `Date` + `LocalDateTime` + `Long` 解决大数字前端精度丢失问题 + `Debug`方法级别调试日志 + 静态资源映射配置化实现 + `Xss`防护配置化实现 + 构建可重复读取inputStream的request + 分页工具 `PageUtils` + 支持国际化的统一异常处理 `GlobalExceptionHandler` + 支持国际化的统一接口返回 `ResultVOGenerator` + 增加`tranceId`返回: 实现每次请求可溯源 + 优雅校验实现 + 允许的值集校验 `AllowableValues` + 禁止的值集校验 `BanValues` + 中文字符校验 `Chinese` + 身份证号码校验 `IdCard` + 枚举值校验 `EnumValues` + 多个字段必须有一个不为空 `ChooseRequired` + 手机号码校验 `Mobile` + 数字校验 `Numbers` + 当指定字段满足某值时当前字段不能为空 `WhenRequired` + 分组校验 `ValidGroup` + 编程式灵活校验 `ValidateUtils` + `ServletUtils` 若依拓展`Servlet`工具 + `Controller`基础接口抽象 + `BaseCrudController` + `BaseViewController` + 基础查询对象封装,支持数据权限 + 全局日志请求耗时过滤器`GlobalLogFilter` + 自适应浏览器的国际化方案 + `CheckerUtils`: Lambda形式对象校验包 + `MessageUtils`: 国际化消息 + 默认配置文件 `application-webplus.yml` + 全局可配置灵活控制的字符串空格替换处理方案 `globalTrimStringsEnabled` `@Trim` + `FileDpzUtils`: 文件下载、简单预览、压缩、压缩下载工具类 + 热启动 `HeatStartApplication` #### 使用 ##### 引入依赖 ```xml top.dingwen.io webplus-spring-boot-starter 1.2.6 ``` > 配置webplus ```yml dingwen: treasure: # webplus webplus: # 开启Debug debug: true # 静态资源映射处理 handlers: - handler: "doc.html" locations: "classpath:/META-INF/resources/" - handler: "swagger-ui.html" locations: "classpath:/META-INF/resources/" - handler: "/webjars/**" locations: "classpath:/META-INF/resources/webjars/" # 防止XSS攻击 # 过滤开关 xssEnabled: true # 排除链接(多个用逗号分隔) xssExcludes: /system/notice # 匹配链接 xssUrlPatterns: /system/*,/monitor/*,/tool/* # 全局异常消息发送 globalExMsgHandlerEnabled: true # 是否开启默认API [默认关闭] defaultApiEnabled: true # 是否开启全局接口提交参数去空格,默认关闭 globalTrimStringsEnabled: false # 热启动端口[默认值 6666] heatStartBackupPort: 6666 # 优雅停机尝试次数[默认值20] graceShutdownTryCount: 20 # 优雅停机尝试休眠毫秒数量[默认500] graceShutdownTrySleep: 500 # 是否允许强制停机,默认关闭 shutdownKillFlag: false ``` #### 概览
#### 链路追踪日志 > 基于过滤器+AOP+`InheritableThreadLocal`实现的可配置的链路追踪日志方案,后期可对接ELK加工处理
#### 全局异常优雅处理 > 区分不同的环境,通过全局异常捕获,统一返回国际化的友好的异常消息.
#### 全局可支配过滤器 + 全局日志请求耗时`GlobalLogFilter` + 构建可重复读取的请求对象`RepeatableFilter` + 发布式链路追踪日志`TraceIdFilter` + 防止`XSS`攻击`XssFilter` #### 自适应浏览器的国际化方案 > 支持异常消息和`valid`校验. `I18nConfig`,`ValidateConfig` ```java @ApiOperation("测试国际化校验消息") @ApiImplicitParam(name = "msg", value = "消息", dataTypeClass = String.class) @GetMapping("/valid") public ResultVO testInternationalization(@RequestParam @Size(min = 1,max = 6,message = "{treasure.webplus.valid.test.msg.size}") String msg) { return success(msg); } ```
### 高效的通用枚举处理启动器 > 从前端到服务乃至数据库到枚举解决方案,零代码,开箱即用 > 已加本地缓存,速度杆杆滴 #### 主要功能 + 统一枚举实现 + 统一枚举响应 + 统一枚举MVC转换 + 统一枚举序列化 + 统一枚举反序列化 + 统一枚举接口实现 + 获取所有枚举时支持分页 + 统一异常处理 + 简单枚举`大道至简` #### 内置接口 + 获取服务端所有枚举 `/common/enums` + 分页查询枚举 `/common/enums/page` + 指定枚举类全路径查询执行枚举 `/common/enums/{enumClassName}` #### 使用 > 枚举持久化依赖各自持久层实现。 > MybatisPlus 3.5.2后版本只需要在实体类中存储数据库值的字段标注 > `@EnumValue`即可 #### 引入依赖 ```xml top.dingwen.io enums-spring-boot-starter 1.2.6 ``` #### 创建自己的枚举 ```java /** *  逻辑删除 *  @author dingwen *  @since 2022/6/14 */ @Getter @AllArgsConstructor public enum LogicDelete implements IBaseEnum { /** * 已删除 */ DELETED(0, "删除"), /** *存在 */ EXISTED(1, "存在"); /** * 状态值 */ @EnumValue private final Integer code; /** * 状态描述 */ private final String desc; } ``` #### 配置项 ```yml dingwen: treasure: # enums enums: # 是否开启默认API defaultApiEnabled: true # 枚举类扫描包 packagepath: "top.dingwen" # 枚举类所在类路径 classpath: "/**/*.class" ``` #### 注意点 > IBaseEnum中已提供枚举比较方法以及转换方法可直接使用 #### 概览
#### 更高效的获取枚举 > 以空间换时间的方式实现. > > 枚举场景初始化时会将所有枚举缓存到`map`,使用时再通过`key`获取即可,省去了循环查找的过程.在枚举数量较多的场景下效率较高.
#### 大道至简的枚举 ```java package com.dingwen.treasure.scene.enums; import com.dingwen.treasure.enums.core.IBaseEnum; /** * 配置环境简单枚举 * * @author dingwen * @since 2024/1/18 15:22 */ public enum ProfileSimpleEnum implements IBaseEnum { /** * 生产环境 */ PROD, /** * 开发环境 */ DEV, /** * 测试环境 */ TEST } ``` #### `IBaseEnum`核心方法 + `eq(T code)`: 判断枚举值是否相等 + `from(Class eClass, Object code)`: 通过枚举code枚举转换 + `fastFrom(Class eClass, Object code)`: 更高效的通过枚举code枚举转换 + `fastDescFrom(Class eClass, Object code)`: 更高效的通过枚举code枚举转换获取描述信息 #### 全局异常处理拓展消息告警 `GlobalExceptionMsgSender` > 使用方只需要实现`GlobalExceptionMsgSender`接口即可,当发生异常是会自动处理异常消息发送.业务系统可以自定义消息告警的方式.首推飞书的机器人告警. > 成熟的开源解决方案: https://gitee.com/jaemon/dingtalk-spring-boot-starter.git #### 自定义文件视图`WebPlusView` > 继承自`org.springframework.web.servlet.view.AbstractView`实现自定义文件视图 #### `CheckerUtils` ``` Checker checker = Checkers.lambdaCheck() .notNull(SysUser::getName) .ne(SysUser::getAge, 0) .custom(item -> item.getAge() > queryByDb(item.getId()), "年龄异常"); checker.check(sysUser); ``` #### 默认配置文件 > 需要时在使用时激活,具体配置参考`application-webplus.yml` ```yml spring: profiles: active: test,webplus ``` #### 可配置的提交参数去除空格 > `globalTrimStringsEnabled` [是否开启全局接口提交参数去空格,默认关闭 ] > 同时支持请求参数+路径参数+请求体参数
### 异步通用场景启动器 > 实现SpringBoot中线程池动态化配置 #### 核心功能 + 线程池参数详细动态化配置 + 实时执行日志可配置打印 + 通用异步任务组件 `AsyncTaskComponent` + 注解式+编程式灵活使用 + 延迟定时任务线程池支持 `defaultScheduledExecutorService` #### 使用 ##### 引入依赖 ```xml top.dingwen.io async-spring-boot-starter 1.1.4 ``` > 配置线程池 ```yml dingwen: treasure: # async async: # 是否开启默认API defaultApiEnabled: false # 是否开启定时线程池 delayScheduledPollEnabled: false # 定时线程池核心参数 delayScheduledPollCoreSize: 10 # 是否开启线程池实时日志打印 logPrint: true pool: # 核心线程数 - core: 8 # 最大线程数 max: 16 # 线程空闲时间 keepAliveTime: 60 # 缓冲队列大小 queueCapacity: 2000 # 线程池前缀 poolNamePrefix: treasure-async- # 线程池对象名称 poolBeanName: logvExecutor # 线程池拒绝策略 poolPolicy: CallerRunsPolicy ``` ##### 代码片段 ``` // 注解式 @Resource(name = "fileExecutor") @Lazy private ThreadPoolTaskExecutor fileExecutor; // 编程式 ThreadPoolTaskExecutor logVExecutor = SpringUtil.getBean("logvExecutor", ThreadPoolTaskExecutor.class); ``` ### 电子邮件启动器 > 基于`sun.mail`实现的可配置的电子邮件使用场景 #### 使用 ##### 引入依赖 ```xml top.dingwen.io email-spring-boot-starter 1.0.3 ``` #### 配置项 ```yml dingwen: treasure: # 电子邮件 email: # 设置开启默认API defaultApiEnabled: false enabled: true # SMTP服务器域名 host: smtp.163.com port: 465 # 是否需要用户名密码验证 auth: true # 发送方,遵循RFC-822标准 from: dingwen0314@163.com # 用户名(注意:如果使用foxmail邮箱,此处user为qq号) user: dingwen0314@163.com # 密码(注意,某些邮箱需要为SMTP服务单独设置密码,详情查看相关帮助) pass: TODO # 使用 STARTTLS安全连接,STARTTLS是对纯文本通信协议的扩展。 starttlsEnable: true # 使用SSL安全连接 sslEnable: true # SMTP超时时长,单位毫秒,缺省值不超时 timeout: 0 # Socket连接超时值,单位毫秒,缺省值不超时 connectionTimeout: 0 ``` #### 使用 ```java /** * 发送电子邮件 */ @Test public void sendEmail() { String messageId = MailUtils.sendText("1981723769@qq.com", "test", "测试邮件内容"); System.out.println(messageId); } ``` #### API概览
### 本地缓存启动器 > 基于`caffeine`实现的配置化的本地缓存 #### 核心功能 + 配置化实现,无需反锁的整合配置 + 基于`Spring cache`实现 + `@Cacheable`: 将方法的结果缓存起来,下一次方法执行参数相同时,将不执行方法,返回缓存中的结果 + `@CacheEvict`: 移除指定缓存 + `@CachePut`: 更新缓存 + `@Caching`: 可以指定相同类型的多个缓存注解,例如根据不同的条件 + `@CacheConfig`: 类级别注解,可以设置一些共通的配置,@CacheConfig(cacheNames=""), 代表该类下的方法均使用这个cacheNames + 提供丰富的API,可供前端页面展示 + 获取所有的缓存 + 获取所有的缓存项 + 获取缓存详情 + 清除缓存 + 缓存数据下载 + 缓存工具类`CacheHelper` + 自定义缓存`VisualCaffeineCache` + 本地缓存服务实现类 `CaffeineCacheServiceImpl` #### 使用 ##### 引入依赖 ```xml top.dingwen.io caffeine-spring-boot-starter 1.0.5 ``` ##### 缓存配置项 ```yml dingwen: treasure: # caffeine caffeine: # 是否开启默认API defaultApiEnabled: false caches: # 缓存Spring Bean名称 - name: testCache # 有效时间,单位秒 invalidTime: 60 # 最大缓存数量 maximumSize: 100 - name: fileCache invalidTime: 60 maximumSize: 1000 ``` ##### 使用 ```java package com.dingwen.treasure.scene.controller.caffeine; /** * Caffeine * * @author dingwen * @since 2023/5/22 16:20 */ @Api(tags = "本地缓存API") @RestController @Slf4j @RequestMapping("common/caffeine") @CacheConfig(cacheManager = "caffeineCacheManager") public class CaffeineController implements BaseViewController { @Resource(name = "defaultVisualCaffeineCache") @Lazy private CaffeineCache defaultVisualCaffeineCache; /** * 缓存get|set测试 * * @return {@link ResultVO}<{@link String}> */ @Cacheable(cacheNames = "defaultVisualCaffeineCache") @GetMapping("/test/gs") public ResultVO testCacheGs(@RequestParam String key,@RequestParam String value) { defaultVisualCaffeineCache.putIfAbsent(key, value); return success(defaultVisualCaffeineCache.get(key, String.class)); } ``` #### 通用缓存工具类`CacheHelper` > 封装同一的现查询本地缓存,若存在则返回,不存在则查询数据库返回且同时加入本地缓存 ```java package com.dingwen.treasure.caffeine.util; import cn.hutool.core.collection.CollUtil; import cn.hutool.core.util.StrUtil; import org.springframework.cache.Cache; import org.springframework.cache.caffeine.CaffeineCache; import java.lang.reflect.Field; import java.util.*; import java.util.function.Function; /** * 缓存工具类 * * @author dingwen * @since 2023/5/30 16:01 */ public class CacheHelper { /** * 实体初始化 */ private static final CacheHelper CACHEHELPER = new CacheHelper(); /** * 构造器私有 */ private CacheHelper() { } /** * 获取实例 * * @return {@link CacheHelper} */ public static CacheHelper getInstance() { return CACHEHELPER; } /** * 缓存Map * * @param cache 缓存 * @return {@link Map}<{@link String}, {@link Object}> */ public Map cacheToMap(Cache cache) { Object obj = cache.getNativeCache(); Map map = new HashMap<>(16); Field[] fields = obj.getClass().getDeclaredFields(); try { for (Field field : fields) { field.setAccessible(true); map.put(field.getName(), field.get(obj)); } } catch (Exception e) { return null; } return (Map) map.get("cache"); } /** * 本地缓存通用逻辑处理 * * @param key 缓存key id * @param keyPrefix 缓存key 前缀 * @param keySuffix 缓存key 后缀 * @param clazz 缓存值字节码 * @param func 实际执行函数 * @param caffeineCache 缓存池 * @return {@link V} */ public V cacheAndGet(K key, String keyPrefix, String keySuffix, Class clazz, Function func, CaffeineCache caffeineCache) { if (Objects.isNull(key) || Objects.isNull(keyPrefix) || Objects.isNull(keySuffix) || Objects.isNull(clazz) || Objects.isNull(caffeineCache)) { if (Objects.isNull(func)) { return null; } return func.apply(key); } String cacheKey = StrUtil.format("{}_{}_{}", keyPrefix, key, keySuffix); V vCache = caffeineCache.get(cacheKey, clazz); if (Objects.nonNull(vCache)) { return vCache; } V v = func.apply(key); caffeineCache.put(cacheKey, v); return v; } /** * caches and gets * * @param keys 缓存keys ids * @param keyPrefix 缓存key 前缀 * @param keySuffix 缓存key 后缀 * @param clazz 缓存值字节码 * @param func 实际执行函数 * @param caffeineCache 缓存池 * @return {@link V} */ public List cachesAndGets(List keys, String keyPrefix, String keySuffix, Class clazz, Function, Map> func, CaffeineCache caffeineCache) { if (CollUtil.isEmpty(keys) || Objects.isNull(keyPrefix) || Objects.isNull(keySuffix) || Objects.isNull(clazz) || Objects.isNull(caffeineCache)) { if (Objects.isNull(func)) { return null; } return new ArrayList<>(func.apply(keys).values()); } // 缓存的结果 List cacheValues = new ArrayList<>(keys.size()); // 没有在缓存中的key List noCacheKeys = new LinkedList<>(); for (K key : keys) { String cacheKey = buildCacheKey(key, keyPrefix, keySuffix); V vCache = caffeineCache.get(cacheKey, clazz); if (Objects.nonNull(vCache)) { cacheValues.add(vCache); } else { // 缓存未命中 noCacheKeys.add(key); } } // 查询参数完全命中,直接返回 if (CollUtil.isNotEmpty(noCacheKeys)) { return cacheValues; } // 未命中缓存的数据 Map noCacheValues = func.apply(noCacheKeys); if (CollUtil.isEmpty(noCacheValues)) { return cacheValues; } // 添加新查询到的数据到缓存中 noCacheValues.forEach((K key, V value) -> { String cacheKey = buildCacheKey(key, keyPrefix, keySuffix); caffeineCache.put(cacheKey, value); cacheValues.add(value); }); return cacheValues; } /** * 构建缓存key * * @param key key * @param keyPrefix key prefix * @param keySuffix key suffix * @return {@link String} */ public String buildCacheKey(K key, String keyPrefix, String keySuffix) { return StrUtil.format("{}_{}_{}", keyPrefix, key, keySuffix); } } ``` #### 概览
#### 自定义缓存`VisualCaffeineCache` > 显示更丰富的内容
### Redis启动器 > 基于`RedisTemplate`封装的启动器 #### 核心功能 + 常用API + 对象缓存 + 过期时间 + 缓存是否存在 + 删除缓存 + `List`,`Set`,`Map`缓存操作 + 发布订阅 + 自增自减 + `void removeZset(final String key, Double scoreStart, Double scoreEnd)` + ` Set getZsetCacheZet(final String key,Double scoreStart,Double scoreEnd)` + 常用功能 + 通用逻辑轻松缓存 `@EasyCache` + 限流 `@RateLimiter` + 防重 `@ReSubmit` + 监控信息 + `Redis`信息 + 缓存信息 + 缓存删除 + 缓存预热统一封装 `RedisCache` + 分布式锁组件 `RedisShareLockComponent` + 基于`zset`实现延迟队列 + `CAS`基于`lua`脚本实现 + 延迟双删`DelayRemoves` #### 使用 ##### 引入依赖 ```xml top.dingwen.io redis-spring-boot-starter 1.2.5 ``` ##### 配置项 ```yml dingwen: treasure: # redis redis: # 缓存项配置 caches: - keyPrefix: "re_submit:" remark: "防止重复提交" - keyPrefix: "common_lock:" remark: "通用锁" # 分布式锁超时时间,默认一分钟 shareLockTimeOut: 60000 # 是否开启延迟双删 delayRemoves: false # 是否开启轻松缓存 easyCache: false # rateLimiter rateLimiter: false # 是否开启防重复提交 reSubmit: false # 是否开启默认API defaultApiEnabled: false ``` ##### 使用案例 ```java package com.dingwen.treasure.scene.controller.redis; /** * Redis场景API * * @author dingwen * @since 2023/5/22 11:11 */ @Api(tags = "Redis缓存API") @RestController @Slf4j @RequestMapping("common/redis") public class RedisController implements BaseViewController { /** * redis 服务 */ @Resource private RedisService redisService; @Resource private RedisProperties redisProperties; /** * 得到Redis信息 * * @return {@link ResultVO} */ @ApiOperation("获取Redis信息") @GetMapping("/info") public ResultVO getInfo() { return success(redisService.getInfo()); } /** * 获取缓存项 * * @return {@link ResultVO}<{@link List}<{@link CacheVO}>> */ @ApiOperation(value = "获取缓存项") @GetMapping public ResultVO> getCaches() { List caches = redisProperties.getCaches(); List cacheVOS = BeanCopyUtils.copyList(caches, CacheVO.class); return success(cacheVOS); } /** * 获取指定前缀的所有key * * @param keyPrefix 关键前缀 * @return {@link ResultVO}<{@link Collection}<{@link String}>> */ @ApiOperation(value = "获取指定前缀的所有key") @ApiImplicitParam(name = "keyPrefix", value = "key前缀", dataTypeClass = String.class) @GetMapping("/{keyPrefix}") public ResultVO> getKeys(@PathVariable("keyPrefix") String keyPrefix) { return success(redisService.getKeys(keyPrefix + RedisConstant.KEY_ALL)); } /** * 获取缓存值 * * @param keyPrefix 关键前缀 * @param key 关键 * @return {@link ResultVO}<{@link CacheVO}> */ @ApiOperation(value = "获取指定key的缓存值") @ApiImplicitParams({ @ApiImplicitParam(name = "keyPrefix", value = "key前缀", dataTypeClass = String.class), @ApiImplicitParam(name = "key", value = "key", dataTypeClass = String.class) }) @GetMapping("/{keyPrefix}/{key}") public ResultVO getCache(@PathVariable("keyPrefix") String keyPrefix, @PathVariable("key") String key) { String cacheValue = redisService.getCacheObject(key); CacheVO cacheVO = CacheVO.builder() .keyPrefix(keyPrefix) .cacheKey(key) .cacheValue(cacheValue) .build(); return success(cacheVO); } /** * 清除指定key前缀的所有缓存 * * @param keyPrefix 关键前缀 * @return {@link ResultVO}<{@link String}> */ @ApiOperation(value = "清除指定key前缀的所有缓存") @ApiImplicitParam(name = "keyPrefix", value = "key前缀", dataTypeClass = String.class) @PutMapping("/{keyPrefix}") public ResultVO cleanCaches(@PathVariable("keyPrefix") String keyPrefix) { redisService.removeKeys(keyPrefix + RedisConstant.KEY_ALL); return success(); } /** * 清除指定key的缓存 * * @param key 关键 * @return {@link ResultVO}<{@link String}> */ @ApiOperation(value = "清除指定key的缓存") @ApiImplicitParam(name = "key", value = "key", dataTypeClass = String.class) @DeleteMapping("/{key}") public ResultVO cleanCache(@PathVariable("key") String key) { redisService.deleteObject(key); return success(); } /** * 清洗所有缓存 * * @return {@link ResultVO}<{@link String}> */ @ApiOperation(value = "清除所有缓存") @DeleteMapping public ResultVO cleanAllCache() { redisService.removeKeys(RedisConstant.KEY_ALL); return success(); } /** * 轻松缓存 * * @param easyCacheSubmitVO 轻松缓存提交内容 * @return {@link ResultVO}<{@link String}> */ @PostMapping("/easy-cache") @ApiOperation("轻松缓存测试") @EasyCache(keyParams = { "#easyCacheSubmitVO.getId()", "#easyCacheSubmitVO.getName()"}, time = 100, returnType = ResultVO.class) public ResultVO easyCache(@RequestBody EasyCacheSubmitVO easyCacheSubmitVO) { return success(easyCacheSubmitVO); } /** * 速率限制 * * @return {@link ResultVO}<{@link String}> */ @ApiOperation("redis限流测试") @GetMapping("/rate-limit") @RateLimiter(time = 1, count = 2) public ResultVO rateLimit() { return success(); } /** * 防止重复提交测试 */ @ApiOperation("防止重复提交测试") @GetMapping("/re-submit") @ReSubmit(message = "登录重复提交请求", isDeleteKey = false, time = 90) public void resubmit() { } /** * 锁
* redisson方案;... * * @return {@link ResultVO}<{@link String}> */ @ApiOperation("lock测试") @GetMapping("/lock") public ResultVO lock() { boolean ifAbsent = redisService.setIfAbsent(RedisKeyConstant.LOCK_PREFIX.concat("test"), "lock test", 1L, TimeUnit.MINUTES); // 剩余时间 long expire = redisService.getExpire(RedisKeyConstant.LOCK_PREFIX.concat("test"), TimeUnit.SECONDS); if (ObjectUtil.isNotNull(ifAbsent) && ifAbsent) { return success(); } String message = StrUtil.format("频繁的操作,请{}秒后重试", expire); return failure(message); } } ``` #### 缓存预热 > 继承抽象类`AbstractRedisCache`完成自身预热逻辑,由统一预热组件进行调用
#### 分布式锁组件 `RedisShareLockComponent` ```java /** * redis 分布式锁组件 * * @author dingwen * @since 2023/12/5 14:57 */ @Component @Slf4j public class RedisShareLockComponent { @Resource private RedisProperties redisProperties; @Resource private RedisService redisService; /** * 加锁å * * @param lockKey 锁key * @param requestId 请求id * @param time 时间 * @param timeUnit 时间单位 * @return boolean */ public boolean lock(String lockKey, String requestId, Long time, TimeUnit timeUnit) { // 参数检查 lockParamsCheck(lockKey, requestId, time, timeUnit); // 当前时间 long currentTime = System.currentTimeMillis(); // 超时时间 long outTime = currentTime + redisProperties.getShareLockTimeOut(); boolean lockResult = false; // 加锁 while (currentTime < outTime) { lockResult = redisService.setIfAbsent(lockKey, requestId, time, timeUnit); if (lockResult) { log.info("[Redis模块]\t[分布式锁],加锁成功:lockKey:{},requestId:{},time:{},timeUnit:{}", lockKey, requestId, time, timeUnit); return true; } ThreadUtil.sleep(100); currentTime = System.currentTimeMillis(); } return lockResult; } /** * 加锁参数检查 * * @param lockKey lock key * @param requestId request id * @param time time * @param timeUnit time unit */ private void lockParamsCheck(String lockKey, String requestId, Long time, TimeUnit timeUnit) { if (StrUtil.isBlank(lockKey) || StrUtil.isBlank(requestId) || time <= 0 || Objects.isNull(timeUnit)) { log.error("[Redis模块]\t[分布式锁],加锁参数错误:lockKey:{},requestId:{},time:{},timeUnit:{}", lockKey, requestId, time, timeUnit); throw new ShareLockException("加锁参数异常"); } } /** * 解锁 * * @param lockKey 锁keu * @param requestId 请求id * @return boolean */ public boolean unLock(String lockKey, String requestId) { if (StrUtil.isBlank(lockKey) || StrUtil.isBlank(requestId)) { throw new ShareLockException("解锁参数异常"); } try { String cacheRequestId = redisService.getCacheObject(lockKey); if (requestId.equals(cacheRequestId)) { redisService.deleteObject(cacheRequestId); return true; } } catch (Exception e) { log.error("[Redis模块]\t[分布式锁],解锁失败。lockKey:{},requestId:{}", lockKey, requestId, e); } return false; } /** * 尝试加锁方法 * * @param lockKey 锁key * @param requestId 请求id * @param time 时间 * @param timeUnit 时间单位 * @return boolean */ public boolean tryLock(String lockKey, String requestId, Long time, TimeUnit timeUnit) { // 参数检查 lockParamsCheck(lockKey, requestId, time, timeUnit); return redisService.setIfAbsent(lockKey, requestId, time, timeUnit); } } ``` #### 基于`zset`实现延迟队列 `DelayTaskComponent` > 当我们有期望一个任务再某一个时间点再去执行,此时业务相对比较简单,不想引入Mq组件时可以考虑实用Redis实现延迟队列。 > 基于redis的zset实现,zset天生具有score的特性。可以根据score放入,而且可以通过range进行排序获取,以及删除指定的值。从业务上,我们可以再新增任务的时候放入,再通过定时任务进行拉取,要注意的一点就是拉取的时候要有分布式锁,保证不进行重复拉取就可以了。
#### `CAS`基于`lua`脚本实现
#### 延迟双删 `DelayRemoves` > 使用延时双删,保证缓存数据库一致性问题 #### 概览
### 二级缓存场景启动器 > 基于本地缓存场景启动器和Redis场景启动器,Caffeine + Redis + Spring Cache 实现的二级缓存 > 注意: **Redis缓存的时间会是本地缓存时间的两倍** #### 使用 ##### 引入依赖 ```xml top.dingwen.io dcache-spring-boot-starter 1.0.4 ``` ##### 启动项配置 ```yml dingwen: treasure: dcache: # 是否开启默认API defaultApiEnabled: true # 是否开启日志打印 logPrintEnabled: true ``` #### 核心功能 + 支持分布式跨`JVM`进程刷新 + `com.dingwen.treasure.dcache.core.DoubleCache` : 二级缓存 + `com.dingwen.treasure.dcache.core.DoubleCacheManager`: 缓存管理器 + `com.dingwen.treasure.dcache.core.RefreshCacheListener`: 字典缓存监听通过redis订阅发布实现缓存刷新 #### 使用案例 ```shell /** * 测试二级缓存获取 * * @param businessId 业务id * @return {@link ResultVO }<{@link String }> */ @Cacheable(cacheNames = CaffeineConstant.C_L_P_C_IN_NAME, key = "#businessId", cacheManager = "doubleCacheManager" ) @GetMapping("/test/gets") public ResultVO testDCacheGets(@RequestParam String businessId) { log.info("{},{},业务方法执行,拟查询数据库", DCacheConstant.L_P_S,DCacheConstant.L_P_API); return ResultVOGenerator.genSuccessResult(businessId); } ```
### 进度条启动器 > 基于`CompletableFuture`实现的多线程任务调度的进度条任务场景 #### 核心功能 + 提供模板类,封装通用逻辑 + 提供丰富API接口 + 任务提交 + 查询进度 + 多种缓存实现支持 + `redisServiceImpl` : Redis缓存支持 [兼容分布式环境] + `caffeineCacheServiceImpl`: Caffeine缓存支持 + `localCacheServiceImpl`: 本地缓存支持 + 拓展接口`DefaultBarProcessSender`支持[可用于`websocket`]形式的实时进度推送 #### 使用 ##### 引入依赖 ```xml top.dingwen.io bar-spring-boot-starter 1.0.4 ``` ##### 启动项配置 ```yml dingwen: treasure: bar: # 是否开启默认API defaultApiEnabled: true ``` #### 概览
### 异常消息告警场景启动器 > TODO ### API文档场景启动器 > 基于`Knif4j`封装的API文档场景启动器 #### 使用 ##### 引入依赖 ```xml com.dingwen api-docs-spring-boot-starter 1.0.0-SNAPSHOT ``` ##### 启动项配置 > 在启动类上添加`@EnableApiDocs`注解,以开启接口文档功能 ``` @EnableApiDocs ``` > 配置 ```yml dingwen: treasure: # API接口文档生成 api: docs: # 标题 title: "Ding Wen Service Api" # 描述 description: "This is Interface Desc" # 服务地址 url: "http://127.0.0.1" # 版本号 version: "1.0.0" # 分组名称 group: prod # 内部API请求头值 headervalue: "DINGWEN-API-VALUE" # 内部APi请求名称 headername: "DINGWEN-API-NAME" # 文档联系人名称 contactname: "dingwen" # 文档联系人站点 contacturl: "https://treasure.dingwen.top" # 文档联系人邮箱 contactemail: "dingwen0314@163.com" ``` #### 概览 + [协议://IP:端口/doc.html](协议://IP:端口/doc.html)
### 日志通用场景启动器 > 实现方法级别的日志,请求级别的日志,业务级别的日志配置化,拓展化,可视化以及代码定位 #### 核心功能 ##### 方法日志 > 方法日志细分为整体日志`MeLog`,参数日志`ParamMeLog`,返回结果日志`ResultMeLog`,方法异常日志`ThrowingMeLog`可灵活配置实现 > 可基于日志格式化接口定制实现,已提供默认实现 > 提供回调接口可灵活拓展处理日志 + 方法日志 + 参数日志 + 返回结果日志 + 异常日志 ##### 请求日志 > 可整体配置所有请求的日志记录规则,提供回调接口可进行定制化 ##### 操作日志 > 基于注解实现的操作日志,依附于Spring事件监听机制解耦,监听对应日志实现及可实现定制化 ##### 登录日志 > Spring事件监听机制解耦,监听对应日志实现及可实现定制化
#### 使用 ##### 引入依赖 ```xml top.dingwen.io log-spring-boot-starter 1.0.3 ``` > 日志配置 ```yml dingwen: treasure: # 请求日志 log: # 是否开启默认API[默认关闭] defaultApiEnabled: false # 是否开启默认操作日志[默认关闭] opLogEnabled: false # 是否开启方法日志[默认关闭] meLogEnabled: false # 是否开启默认链路追踪[默认关闭] tranceEnabled: false request: # 是否开启请求日志 reEnable: true # 是否记录请求体内容 reEnableBody: false method: global-log-level: debug # 全局综合日志代码定位 global-log-position: unknown # 全局综合日志格式化 global-log-formatter: com.dingwen.treasure.log.method.format.DefaultMeLogFormatter # 全局综合日志回调 global-log-callback: com.dingwen.treasure.log.method.callback.DefaultMeLogCallback # 全局参数日志级别 global-param-log-level: debug # 全局参数日志代码定位 global-param-log-position: unknown # 全局参数日志格式化 global-param-log-formatter: com.dingwen.treasure.log.method.format.DefaultMeParamLogFormatter # 全局参数日志回调 global-param-log-callback: com.dingwen.treasure.log.method.callback.DefaultMeLogCallback # 全局结果日志级别 global-result-log-level: debug # 全局结果日志代码定位 global-result-log-position: unknown # 全局结果日志格式化 global-result-log-formatter: com.dingwen.treasure.log.method.format.DefaultMeResultLogFormatter # 全局结果日志回调 global-result-log-callback: com.dingwen.treasure.log.method.callback.DefaultMeLogCallback # 全局异常日志回调 global-throwing-log-callback: com.dingwen.treasure.log.method.callback.DefaultMeLogCallback ``` ##### 代码片段 ``` /** * 测试方法日志 * * @param name 名字 * @return {@link ResultVO}<{@link String}> */ @GetMapping("/method-test") @ApiOperation(value = "测试方法日志") @ApiImplicitParam(name = "name", value = "名称", dataTypeClass = String.class) @MeLog(value = "方法日志测试业务") public ResultVO testMeLog(@RequestParam String name) { return success(); } /** * 测试操作日志 * * @param name 名称 * @return {@link ResultVO}<{@link String}> */ @GetMapping("/operate-test") @ApiOperation(value = "测试操作日志") @ApiImplicitParam(name = "name", value = "名称", dataTypeClass = String.class) @OperateLogAnnotation(module = "场景", desc = "test") public ResultVO testOperateLog(@RequestParam String name) { return success(); } ``` #### 概览
### 日志文件查看启动器 > 基于Websocket、SpringBoot2.x、layui实现的可配置的Web版日志查看器 #### 核心功能 + 可选基础目录生成文件树 + 日志文件编码配置化 + 追加新日志自动滚动 + 支持下载日志文件 [需要配合权限框架做好权限控制] #### 优化点 + 日志查看页增加文件下载 + 文件树按照创建时间降序 + 大量的日志输出导致内存飙升 + 大量的客户端链接导致CPU飙高 + UI美化 + 异常提示完善 #### 使用 ##### 引入依赖 ```xml top.dingwen.io logv-spring-boot-starter 1.0.2 ``` > 配置接口文档放行、线程池、监控项 ```yml dingwen: treasure: # webplus webplus: debug: true handlers: - handler: "static/**" locations: "classpath:/static/" - handler: "doc.html" locations: "classpath:/META-INF/resources/" - handler: "swagger-ui.html" locations: "classpath:/META-INF/resources/" - handler: "/webjars/**" locations: "classpath:/META-INF/resources/webjars/" # async async: logPrint: true pool: - core: 8 max: 16 keepAliveTime: 60 queueCapacity: 2000 poolNamePrefix: treasure-async- poolBeanName: logvExecutor poolPolicy: CallerRunsPolicy # 监控项 management: endpoints: web: exposure: include: "*" endpoint: health: show-details: always shutdown: enabled: true ``` #### 访问地址 > 默认启动端口: `2023` + [首页](/common/logv) + [接口文档](/doc.htm) #### 概览
### PipeLine启动器 > 工厂模式 + 策略模式 + 门面模式 + 模板方法 实现的定制化配置化规则引擎 #### 核心功能 + 抽象上下文,实现通用逻辑`PipelineContext` + 异步执行管道支持 + 外部配置化的管道流 + 注解式的管道前后进行过滤操作 + 详尽的日志追踪 + 提供测试案例 #### 使用 ##### 引入依赖 ```xml top.dingwen.io pipeline-spring-boot-starter 1.0.2 ``` #### 配置项 ```yaml dingwen: treasure: # pipeline pipeline: # 是否开启默认API defaultApiEnabled: true routes: # 管道上下文名称 key testContext: # 第一个执行的管道 - onePipeLine # 第二个执行的管道 - twoPipeLine ``` ```java // 配置管道过滤器 @PipeFilters(filters = { @PipeFilter(beanName = "beforeOneFilter",exePoint = PipelineFilterExePoint.ALL), @PipeFilter(beanName = "beforeTwoFilter",exePoint = PipelineFilterExePoint.BEFORE), }) ``` #### 核心类 + `PipelineContext`管道上下文 + `Pipeline`管道接口 + `PipelineFilter`管道过滤器接口 + `@PipeFilter`过滤器注解 + `PipelineFactory`管道执行工厂 + `PipelineExecutor`管道执行器 #### 概览
### 数据库文档生成启动器 > 基于Screw、Freemarker实现的支持word,markdown,html文件生成的数据库文档生器 #### 核心功能 + 数据源支持 + postgresql + mysql + oracle + 包含可配置的详细注释生成 + 表名称,前缀,后缀等细粒度可配置化 + 文档名称,标题可配置化 + 同时支持work,markdown,html版本数据库设计文档 #### 使用 ##### 引入依赖 ```xml top.dingwen.io screw-spring-boot-starter 1.0.0 ``` > 配置文档参数 ```yml dingwen: treasure: # 数据库文档生成 screw: # 数据库名称 dbName: test # 数据源连接地址 jdbcUrl: ${spring.datasource.dynamic.datasource.master.url} # 数据源用户名 username: ${spring.datasource.dynamic.datasource.master.username} # 数据源连接密码 password: ${spring.datasource.dynamic.datasource.master.password} # 驱动类名称 driverClassName: ${spring.datasource.dynamic.datasource.master.driver-class-name} # 是否读取表注释信息 tableRemark: true # 文档版本号 docVersion: 1.0.0 # 文件输出目录 fileOutputDir: @project.name@/src/main/resources/static ``` #### 访问地址 + [首页](http://127.0.0.1:8080/treasure_doc_1.0.0.html) #### 概览
### MongoDB场景启动器 > 基于`MongoTemplate`分装的类似`MybatisPLus`的`lambda`形式的增删改查API #### 核心功能 + 复杂条件灵活构造查询 + 分页查询 + 模糊查询 + 排序 + 集合查询 + 新增 + 修改 + 删除 #### 使用 ##### 引入依赖 ```xml top.dingwen.io mongo-spring-boot-starter 1.0.0 ``` ##### 编写实体类 ```java /** * 请求日志 * * @author dingwen * @since 2023/7/24 13:12 */ @ApiModel(value = "RequestLog", description = "请求日志实体") @Document("tre_c_request_log") @Data @AllArgsConstructor @NoArgsConstructor @Builder @EqualsAndHashCode(callSuper = true) public class RequestLog extends BaseMongoEntity { @ApiModelProperty(value = "请求日志id") @Id private String reLogId; @ApiModelProperty(value = "请求时间") @Field("reTime") private LocalDateTime reTime; @ApiModelProperty(value = "请求IP") @Field("reIp") @Indexed private String reIp; @ApiModelProperty(value = "IP属地") @Field("reAddress") private String reAddress; @ApiModelProperty(value = "耗时") @Field("consumeTime") private Long consumeTime; @ApiModelProperty(value = "请求体信息") @Field("resBody") private String resBody; @ApiModelProperty(value = "响应体信息") @Field("respBody") private String respBody; @ApiModelProperty(value = "请求地址") @Field("reUrl") @Indexed private String reUrl; @ApiModelProperty(value = "请求头信息") @Field("reqHeaders") private String reqHeaders; } ``` ##### 编写接口 ```java /** * 请求日志服务 * * @author dingwen * @since 2023/7/24 13:36 */ public interface IRequestLogService extends MongoService { } ``` ##### 编写实现类 ```java /** * 请求日志服务 * * @author dingwen * @since 2023/7/24 13:38 */ @Service public class RequestLogServiceImpl extends MongoServiceImpl implements IRequestLogService { } ``` ##### 调用 ```java /** * MongoAPI * * @author dingwen * @since 2023/7/24 13:42 */ @Api(tags = "MongoDB API") @RestController @Slf4j @RequestMapping("common/mongo") public class MongoController implements BaseViewController { @Resource private IRequestLogService requestLogService; /** * 请求日志列表 * */ @ApiOperation(value = "请求日志列表") @ApiImplicitParams({ @ApiImplicitParam(name = "reIp", value = "请求IP", dataTypeClass = String.class), @ApiImplicitParam(name = "reAddress", value = "IP属地", dataTypeClass = String.class), @ApiImplicitParam(name = "reUrl", value = "请求地址", dataTypeClass = String.class) }) @GetMapping("request-logs") public ResultVO> getRequestLogPage(@RequestParam(required = false) String reIp, @RequestParam(required = false) String reAddress, @RequestParam(required = false) String reUrl) { LambdaQueryWrapper query = Wrappers.lambdaQuery() .like(StrUtil.isNotBlank(reIp), RequestLog::getReIp, reIp) .like(StrUtil.isNotBlank(reAddress), RequestLog::getReAddress, reAddress) .like(StrUtil.isNotBlank(reUrl), RequestLog::getReUrl, reUrl) .orderByDesc(RequestLog::getCreateTime); Page page = requestLogService.page(query, PageUtils.getPageNum(), PageUtils.getPageSize()); return page(page.getRecords(), Convert.toInt(page.getTotal())); } } ``` #### 支持的条件类型 + `eq`: 等于 + `ne`: 不等于 + `le`: 小于等于 + `lt`: 小于 + `ge`: 大于等于 + `gt`: 大于 + `bw`: 在...之间 + `in`: 包含 + `nin`: 不包含 + `like`: 全模糊查询 + `left_like`: 左模糊查询 + `right_like`: 右模糊查询 #### 概览
### xxl-job定时人场景启动器 > 基于`xxl-job`v2.4.0版本的执行器封装,实现配置化、插件化使用 #### 核心功能 参考官网 [xxl-job](https://github.com/xuxueli/xxl-job) #### 使用 ##### 引入依赖 ```xml com.dingwen xxl-job-spring-boot-starter 1.0.0-SNAPSHOT ``` ##### 启动项配置 > 在启动类上添加`@EnableXxlJob`注解以开启`xxl-job`定时任务场景功能 ``` @EnableXxlJob ``` > 配置xxl-job参数 ```yml dingwen: treasure: #xxl-job定时任务场景 xxljob: # 执行器开关 enabled: true adminAddresses: http://127.0.0.1:8003 #调度中心应用名 adminAppName: treasure-xxl-job-admin # 执行器通讯TOKEN accessToken: xxl-job-access-token # 执行器配置 executor: appName: scene #执行器日志文件保存天数:大于3生效 logRetentionDays: 10 # 执行器运行日志文件存储磁盘路径 【注意不要和项目本身log路径冲突】 logPath: logs/xxljob # 执行器端口号 port: 9999 ``` #### 概览
### OSS启动器 > 基于亚马逊`S3`封装的支持阿里云,`Minio`等多种存储方式的对象存储通用业务场景启动器 #### 使用 ##### 引入依赖 ```xml top.dingwen.io oss-spring-boot-starter 1.0.0-SNAPSHOT ``` #### 配置项 ```yml dingwen: treasure: # oss oss: # 对象存储服务的URL endpoint: http://127.0.0.1:9000 # key access-key: admin # 密钥 secret-key: 1234567890 # 路径风格 path-style-access: true # 最大线程数 max-connections: 100 # 区域 region: ``` #### `OssTemplate`操作API
### mybatis-plus启动器 > 基于`MybatisPlus`的二次封装 #### 核心功能 + 统一实体 + 统一查询对象 + 场景异常封装处理 + 逻辑删除 + 枚举处理 + 通用字段填充 + 创建人 + 创建时间 + 修改人 + 修改时间 + 多租户 + 数据权限 + 通用选项查询组件 + 完整`SQL`日志打印 + `SQL`执行耗时 + 通用字符串字段长度校验组件 + 同一数据唯一性校验组件 + 获取表信息工具 + 字段比较工具 + 通用查询组件 `QueryUtils` [支持数据权限] + `FULL`: 全模糊查询 + `EQ`: 全等于查询 + `LEFT`: 全以什么结尾模糊查询查询 + `RIGHT`: 全以什么开头模糊查询查询 + `IN`: 在集合查询 + `NOT_IN`: 不在集合查询 + `RANGE`: 范围内查询 + `DESC`: 降序 + `ASC`: 升序 + 数据冗余解决方案`IRedundancyMaintainService` #### 使用 ##### 引入依赖 ```xml top.dingwen.io mybatisplus-spring-boot-starter 1.0.3 ``` #### 选项查询通用组件 > 当你需要查询某个数据作为下拉选项时可使用此组件,支持四级选项、数据权限、中间表关联。已实现本地缓存优化。 ##### 三级关联查询 ```java @Test void testOpForLevel3() { List opParams = new ArrayList<>(); OpParam unitOpParam = OpParam .builder() .labelField("unit_name") .valueField("unit_id") .tableName("hom_u_unit") .build(); OpParam deptOpParam = OpParam .builder() .labelField("t1.dept_name") .valueField("t1.dept_id") .tableName("sys_dept t1") .leftJoinField(", t2.dept_id") .leftJoinSql("left join hom_u_unit_lnk_dept t2 on t1.dept_id = t2.dept_id") .parentField("t2.unit_id") .build(); OpParam usertOpParam = OpParam .builder() .labelField("user_name") .valueField("user_id") .tableName("sys_user") .parentField("dept_id") .build(); opParams.add(unitOpParam); opParams.add(deptOpParam); opParams.add(usertOpParam); List