docs/API_REVIEW.md
API Review
本文件记录 Under-Utils public API 审计结论,用于持续收敛命名、配置 key、异常语义和模块边界。
本文件记录 Under-Utils public API 审计结论,用于持续收敛命名、配置 key、异常语义和模块边界。
审计原则
- 优先稳定发布后难以修改的入口:包名、类名、方法签名、配置属性、注解属性和异常类型。
- 不把低复杂度通用工具方法作为新增方向,历史 core 工具只做兼容维护。
- starter 默认行为必须可解释,配置 key 一经发布尽量不破坏。
- 对存在安全边界的 API,默认路径应使用白名单、显式配置或封装模板。
第一轮结论
HTTP
- 将底层 OkHttp 执行入口从
OkHttpClient收敛为OkHttpRequestExecutor,避免与okhttp3.OkHttpClient同名并误导使用者。 HttpRequest继续作为兼容的便捷请求模型,OpenAPI 治理能力仍以OpenApiClient、OpenApiRequest、OpenApiResponse为主线。
Redis
LogicalExpireCacheOptions明确校验physicalTtl > logicalTtl,避免逻辑过期缓存失去旧值兜底窗口。LogicalExpireCacheTemplate.LogicalCachePayload收回为包内实现细节,不作为 public API 暴露。
MyBatis
SafePageQuery+SortFieldMapping是对外推荐分页排序入口。PageQuery直接接收数据库列名,已标记为不推荐 API,只适合内部可信代码,不建议暴露为 Web 入参。
Starter
under.utils.*配置前缀保持稳定。- Redis cache-aside 与 logical-cache 使用独立开关,共享
CacheValueCodec。 - GPG 签名、release profile 与 Testcontainers 集成测试已进入发布准备链路。
第二轮结论
Spring Legacy AOP
OperationLog、Retry、TimeLog保留为兼容维护 API,并标记为@Deprecated。OperationLogAspect、RetryAspect、TimeLogAspect不再声明为 Spring@Component,避免直接扫描com.undernine.utils.spring时意外启用历史切面;仍需兼容时应显式@Import或声明为@Bean。OperationLog.recordParams默认值改为false,降低误记录请求参数和敏感信息的风险;兼容旧行为需要显式设置recordParams = true。RetryAspect仍保留同步 sleep 语义,但会规整非法maxAttempts、delay和空异常列表,避免配置异常造成不可预期循环或等待。- 新项目的审计、重试和耗时观测不再以轻量历史切面为主线,应优先使用业务统一审计、专用客户端治理、Micrometer 或 OpenTelemetry。
第三轮结论
Core Historical Helpers
under-utils-core不再按“基础工具大全”表达,README 与包级 Javadoc 改为强调低耦合基础能力和历史工具兼容维护。StringUtils、CollectionUtils、LocalDateTimeUtils、ValidationUtils、UUIDUtils、JsonUtils、MD5Utils、SHA256Utils、AESUtils标记为@Deprecated兼容 API,保留调用但不再扩展。IdGenerator与MoneyUtils暂作为 core 主线保留能力:前者承担本地趋势递增 ID 生成,后者固化 BigDecimal 金额计算与分/元转换语义。JsonUtils明确为历史兼容入口;复杂应用应注入业务自己的ObjectMapper或 codec,getObjectMapper()标记为不推荐。AESUtils.encryptECB与AESUtils.decryptECB标记为不推荐,ECB 仅保留历史兼容;MD5/SHA-256/AES 文档明确不作为推荐安全治理入口。- 移除
under-utils-corePOM 中未使用的 Lombok、SLF4J、Apache Commons Lang、Guava 和 Bouncy Castle 可选依赖,避免模块边界和开源定位被误读。
第四轮结论
Spring Rate Limit And Repeat Submit
- 明确
@RateLimit失败语义:超过窗口额度时抛出BizException,消息来自message;limit <= 0等价于拒绝所有请求,period <= 0按 1 秒窗口处理。 - 明确
@PreventRepeat失败语义:同一 key 在窗口内重复提交时抛出BizException,消息来自message;timeout <= 0按最小 1ms 处理。 - 明确
releaseOnFailure只影响业务方法抛异常后的 key 释放;方法成功后 key 保持到窗口过期。 - 明确 key 解析语义:空
key使用租户、用户、URI、方法名和参数摘要;SpEL 解析失败不会中断业务,会退回到表达式和参数摘要生成的兜底 key。 - 明确集群语义:local store 只在当前 JVM 内生效;多实例部署必须切换 Redis store 或自定义
RateLimitStore/RepeatSubmitStore。 - 明确 Redis 失败语义:Redisson 调用异常向外传播,如需 Redis 故障时放行或降级,应由业务自定义 store。
第五轮结论
Maven Central Release
- 接入 Central Publisher Portal 的 Maven 发布链路,新增
central-publishprofile;默认central.skipPublishing=true,用于本地验证 deploy 生命周期,避免误上传。 - 发布插件固定为
org.sonatype.central:central-publishing-maven-plugin:0.10.0,不再引入旧 OSSRH / nexus-staging 发布路径。 central-publish通过excludeArtifacts排除under-utils-samples,示例工程继续只参与构建和测试验证。- 新增
docs/RELEASE.md,明确 namespace、Central Portal token、GPG/PGP 签名、手动校验发布和 CI secrets 要求。 - 新增手动触发的 GitHub Actions 发布工作流,默认上传后等待
validated,是否自动发布需要人工选择published模式。
第六轮结论
Maven Coordinates
- Maven
groupId从com.undernineplaces收敛为 GitHub namespaceio.github.yexianglun-d,避免依赖未持有域名的 DNS namespace 验证。 - Java 包名仍保持
com.undernine.utils,本轮只调整 Maven 坐标,不引入包名级破坏性迁移。
第七轮结论
1.0.0 Release
v1.0.0已发布到 Maven Central,发布源码 tag 固定在实际上传构件对应提交。main分支进入1.0.1-SNAPSHOT开发周期,避免重复发布 Maven Central 已存在的1.0.0坐标。- 发布后文档改为面向 Maven Central 使用,不再要求业务项目先本地
mvn clean install。
第八轮结论
文档与发布后测试覆盖
- README、快速开始、贡献指南、发布指南、路线图和模块 README 改为更接近社区维护文档的写法:少形容词,优先说明边界、安装、运行命令、失败语义和维护约束。
- 为
under-utils-redis、under-utils-starter和under-utils-biz补齐模块 README,避免发布到 Maven Central 后缺少模块级入口说明。 under-utils-starter自动装配测试覆盖 Redis store 切换、缺少RedissonClient的失败路径,以及用户自定义 store、lock template、cache options/template 的退让行为。LogicalExpireCacheOptions自动配置增加对用户自定义LogicalExpireCacheTemplate的退让,避免自定义模板场景下继续创建无用默认 options。under-utils-test增加 Redis Testcontainers 集成测试,覆盖 cache-aside 命中复用、空值占位缓存和逻辑过期缓存后台刷新。
第九轮结论
兼容性策略
- 新增
docs/COMPATIBILITY.md,明确1.0.x、1.x.0和2.0.0的版本语义,patch/minor 版本默认保持源码兼容。 - 明确 public API 范围:Maven 坐标、public/protected Java API、注解属性、
under.utils.*配置 key、starter 自动装配 Bean、SPI 接口和已文档化的失败语义。 - 明确破坏性变更定义:删除/重命名 public API、修改签名或默认值、改变 starter 默认副作用、无迁移改变缓存 payload、改变 key/锁/缓存/错误解码语义等。
- 明确弃用流程:使用
@Deprecated(since = "x.y.z", forRemoval = false),JavaDoc 说明替代方案,默认保留到下一 major 版本,并在 CHANGELOG/API Review 中记录。 - 将兼容性影响接入 README、贡献指南、PR 模板和功能建议模板,后续面向用户的变更需要显式标注 patch-compatible、minor-compatible、deprecation 或 breaking。
第十轮结论
文档语言
- 对外文档统一使用中文,保留 GitHub 开源项目常见的信息结构:边界、安装、配置、示例、兼容性、测试和贡献规则。
- 保留必要英文技术名词和代码标识,例如 Maven Central、public API、starter、SPI、Release Notes。
- Issue/PR 模板同步改为中文,降低中文贡献者提交成本。
第十一轮结论
1.0.1 增强 API
under-utils-biz新增AsyncImportTaskTemplate、ImportProgress、ImportProgressListener、ImportTaskStatus和ImportErrorExporter。这些 API 只新增能力,不改变同步ImportTaskTemplate的既有返回结果和失败语义。ImportOptions增加progressListener和toBuilder(),默认 listener 为 no-op;listener 运行时异常会被记录并忽略,不影响导入主流程。AsyncImportTaskTemplate默认使用 JVM 内存保存任务状态。跨实例查询、重启恢复和任务审计不进入默认实现,应由业务项目通过ImportProgressListener持久化。under-utils-redis新增缓存观测 SPI:CacheOperationObserver、CacheOperationEvent和CacheOperationType。模板构造器新增 observer 重载,旧构造器继续可用并使用 no-op observer。- starter 在存在
CacheOperationObserverBean 时自动注入 cache-aside 与 logical-cache 模板,不新增默认 Bean,避免污染应用上下文。 under-utils-http新增RefreshingAccessTokenProvider。它只负责当前 JVM 内 token 缓存、提前刷新和并发收敛;分布式 token 共享和刷新失败兜底仍属于业务实现边界。
第十二轮结论
1.0.1 Release Prep
- Maven reactor 版本从
1.0.1-SNAPSHOT收敛为1.0.1,用于 patch release 验证与 Central Portal 上传。 - 用户文档依赖版本同步更新为
1.0.1。 CHANGELOG.md将当前 Unreleased 内容归档到1.0.1,并新增docs/releases/v1.0.1.md作为 GitHub Release Notes 草稿。
1.0.1 Release
v1.0.1已发布到 Maven Central,发布源码 tag 固定在实际上传构件对应提交。main分支进入1.0.2-SNAPSHOT开发周期,避免重复发布 Maven Central 已存在的1.0.1坐标。
第十三轮结论
Starter 轻量化
- 新增
under-utils-spring-starter,只提供请求上下文、限流、防重复提交、本地 store 和基础 SPI 自动装配,避免只接入 Spring 横切能力的项目被动引入 Redis/Redisson。 - 新增
under-utils-redis-starter,依赖 Spring starter 并承载 Redis store、分布式锁、缓存模板和缓存观测自动装配。 under-utils-starter保留为兼容聚合坐标,不删除旧 Maven artifact;旧用户可以继续使用,新用户按需选择轻量 starter。under.utils.*配置 key 保持不变,本轮只调整自动装配模块边界,不改变现有默认行为和失败语义。
第十四轮结论
API 兼容性门禁
- 新增
api-compatMaven profile,并接入 GitHub Actions CI,使用 japicmp 将当前构件和1.0.1已发布构件做 public API 对比。 - 默认纳入检查的模块为
under-utils-core、under-utils-http、under-utils-spring、under-utils-redis、under-utils-mybatis和under-utils-biz。 - 检查在
verify阶段执行,并对二进制不兼容和源码不兼容修改失败退出。 under-utils-starter、under-utils-spring-starter和under-utils-redis-starter暂不纳入单 jar 对比。starter 拆分会产生类移动误报,兼容性先由旧聚合坐标、自动装配测试和配置 key 文档共同保证。- 本地 Maven 如果配置了无法解析 japicmp 的镜像,可以使用
docs/central-dry-run-settings.xml绕过全局镜像后再运行检查。
第十五轮结论
Core JSON 解耦
JsonUtils继续留在under-utils-core,只作为1.x兼容维护 API,不在 patch 版本移出 Jackson 依赖,避免老用户升级后缺少 transitive dependency。under-utils-http、under-utils-spring和under-utils-redis内部不再调用JsonUtils,改为模块内自有 Jackson mapper;异常仍沿用既有 unchecked 类型,避免改变用户可感知失败路径。- 受影响模块显式声明
jackson-datatype-jsr310,确保脱离JsonUtils后仍支持 Java Time 类型。 - 本轮不是删除 core JSON,而是先降低运行时模块对历史工具入口的耦合。真正从
under-utils-core移除 Jackson 需要等待 major 版本或提供明确迁移模块。
第十六轮结论
Biz 依赖收敛
under-utils-biz当前 public API 只覆盖导入任务、CSV reader、进度查询和错误导出,不提供 Excel/POI/Jackson 类型签名。- 移除
under-utils-bizPOM 中未使用的 EasyExcel、POI 和 Jackson optional 依赖,不影响已发布 Java API。 - 后续如果提供 Excel 流式导入,应进入独立扩展模块,避免基础 biz 模块重新带入 Excel 栈。
第十七轮结论
HTTP 客户端边界
under-utils-http当前 public API 基于HttpRequest、HttpResponse、HttpUtils、OkHttpRequestExecutor和OpenApiClient系列类型,不提供 HttpClient5 适配器。- 移除 POM 中未实现的 HttpClient5 optional 依赖,不影响已发布 Java API,也避免用户误以为模块内置 HttpClient5 执行器。
- 后续如果需要多客户端实现,应新增明确的执行器抽象或独立适配模块,不能只通过 optional 依赖暗示能力。
第十八轮结论
Runtime Boundary Fixes
RedisRateLimitStore不再忽略已存在 limiter 的配置漂移:trySetRate失败后会读取当前配置,参数不一致时调用setRate更新,并改为只在 key 未设置 TTL 时补过期时间。IdGenerator默认构造器不再固定使用datacenterId=0, workerId=0;默认值优先来自系统属性或环境变量,未配置时按主机和进程派生。生产多节点仍应显式分配稳定节点 ID。LocalRateLimitStore和LocalRepeatSubmitStore增加默认容量上限和节流过期清理,避免按用户、租户或业务 key 无限增长;LocalRateLimitStore内部计数改为synchronized保护的普通int。AsyncImportTaskTemplate增加完成/失败任务状态保留期,默认 24 小时,并支持构造器自定义,避免结果和错误明细永久驻留当前 JVM。SafePageQuery限制单次请求最多 5 个排序字段,排序白名单继续负责列名安全,数量上限负责防滥用。DefaultOpenApiClient保持同步 API,但默认不再在重试前Thread.sleep;需要同步等待的兼容调用方必须显式开启blockingRetryDelayEnabled。- Maven 坐标继续使用
io.github.yexianglun-d,Java 包名在1.x内保持com.undernine.utils,避免包名级破坏性迁移;文档中明确这一兼容取舍。 AESUtils类和 CBC/ECB 方法均表达为历史兼容 API,不再给 CBC 方法保留“推荐”信号。
第十九轮结论
Redis Cache Metrics
CacheAsideTemplate和LogicalExpireCacheTemplate内置CountingCacheOperationObserver,调用方可以通过getMetrics()零配置读取缓存命中、未命中、加载、写入、重建锁和后台刷新计数。- 新增
CacheMetrics作为只读快照对象,派生lookupCount、hitRate、missRate、loadCount和errorCount,避免业务侧重复聚合最常见的缓存指标。 - 保留
CacheOperationObserverSPI。外部 observer 继续接收所有事件,模板内置指标与外部 observer 相互独立;observer 抛出的运行时异常仍不会影响缓存主流程。 - starter 不新增默认
CacheOperationObserverBean,避免污染应用上下文。Spring 应用可以直接从已装配的缓存模板 Bean 读取指标。
第二十轮结论
Local State Active Cleanup
LocalRateLimitStore、LocalRepeatSubmitStore和AsyncImportTaskTemplate增加每实例后台清理线程,过期本地状态不再只依赖下一次访问或容量触发时被动清理。- 三个类型均实现
AutoCloseable。后台清理线程为 daemon 线程,Spring 管理的 Bean 会在上下文销毁时关闭;手动创建的实例应在不再使用时调用close()。 - 本轮不引入全局静态清理线程池,避免多个应用上下文、测试上下文或手动实例共享不可控生命周期。
under-utils-spring-starter为本地限流和本地防重复提交增加local-max-entries与local-cleanup-interval配置,继续保持 Redis store 配置语义不变。RateLimitAspect和PreventRepeatAspect的默认本地 store 改为懒加载,避免 starter 注入外部 store 前先创建一个无用的后台清理线程。
第二十一轮结论
Regression And Changelog Traceability
- 贡献指南明确 Bug 修复必须补独立回归测试。有外部 issue 时,测试类或方法应能追溯到 issue 编号;没有 issue 时,使用
Regression...Test或regression_...命名,并标明来源类型。 - PR 模板增加追溯来源字段,区分 issue、PR、review-doc、internal-review、user-report 或 none,避免
CHANGELOG.md只留下功能描述而缺少原因。 - Bug issue 模板增加建议回归测试段落,release notes 模板增加可追溯性段落,后续 patch/minor 发布需要同步说明 Issue/PR、Review 来源和回归测试。
第二十二轮结论
Fluent API Experience
HttpRequest增加get/post/put/delete/patch/head/options方法级快捷 builder,直接 HTTP 调用可以从请求方法开始链式声明,不再必须先写builder().url(...).method(...)。HttpRequest.Builder增加execute()和executeAsync(),常见一次性请求可以由 builder 直接构建并执行;原有build().execute()路径继续可用。HttpRequest增加toBuilder(),复制修改请求时会拷贝 headers、params、files 和 formParams,避免修改派生请求时污染原请求。HttpConfig和OpenApiClientOptions增加toBuilder()与Duration友好的链式方法。为保持 Lombok builder 原有int/longsetter 兼容,新增方法使用connectTimeoutDuration、retryIntervalDuration等不冲突命名。CacheOptions、LogicalExpireCacheOptions和ImportOptions已具备 builder/toBuilder。CacheAsideTemplate新增cache(key, type)/key(key, type)链式入口,可在单次调用上配置 TTL、空值缓存、jitter 和重建锁后直接getOrLoad(...)。LogicalExpireCacheTemplate新增cache(key, type)/key(key, type)链式入口,可在单次调用上配置 logical TTL、physical TTL、刷新 executor 和失败处理器。
第二十三轮结论
AI Basic Client
- 新增
under-utils-ai模块,第一阶段只提供 OpenAI-compatible 同步文本对话客户端,不进入 Agent、RAG、流式响应、工具调用或厂商私有完整参数封装。 - 对外入口为
AiClient.builder()和AiClientOptions。调用方配置baseUrl、apiKey、model、timeout等基础参数后,可通过chat(ChatRequest)或chat(String)调用模型。 - 请求/响应模型使用
ChatRequest、ChatMessage、ChatResponse和TokenUsage,不直接暴露under-utils-http的HttpRequest/HttpResponse,降低后续替换底层执行器的 API 风险。 - 错误统一映射为
AiException,通过AiErrorType区分鉴权失败、限流、超时、客户端错误、服务端错误、网络错误和响应解析失败,并暴露statusCode、errorCode与retryable。 - 安全边界明确:异常信息和
toString()不输出 API key、Authorization header 或完整 prompt/模型回复。 - 新增独立
under-utils-ai-starter,配置前缀为under.utils.ai,默认enabled=false,避免应用只引入 starter 坐标后就自动访问外部模型服务。 under-utils-ai-starter在存在用户自定义AiClientBean 时退让;当前provider只支持openai-compatible,不通过配置暗示未实现的厂商原生协议。- AI 模块复用
under-utils-http的 HTTP 执行能力,因此会带入 OkHttp/Jackson;AI starter 没有放入under-utils-starter聚合入口,避免普通 Spring/Redis 用户被动引入 AI 依赖。 - 新模块没有
1.0.1基线构件,当前japicmp暂跳过;正式发布后再纳入 public API 兼容检查。
第二十四轮结论
Redis Micrometer Observation
- 新增
MicrometerCacheOperationObserver,将CacheOperationObserver事件桥接为 Micrometer counter、duration timer 和 observation。 - 新 observer 只使用低基数 tag:
cache.type、cache.operation、cache.outcome、cache.null和exception;不会把业务 key 或实际 cache key 写入 tag。 under-utils-redis和under-utils-redis-starter仅以 optional dependency 引入 Micrometer,不改变普通 Redis 用户的默认依赖面。under-utils-redis-starter在存在MeterRegistry、没有用户自定义CacheOperationObserver且缓存能力启用时,自动创建MicrometerCacheOperationObserver;配置under.utils.redis.observation.enabled=false可关闭。- 如果用户已经声明自己的
CacheOperationObserver,starter 继续退让,不会叠加第二个 observer。
第二十五轮结论
AI Streaming and Provider Extension
- 新增
StreamingAiClient、ChatStream和ChatStreamEvent,流式文本对话与同步AiClient分离,避免把同步入口扩成复杂回调 API。 OpenAiCompatibleAiClient实现 OpenAI-compatible SSE 流式响应,请求体强制设置stream=true,按data:分片产出增量事件。ChatStream实现AutoCloseable,调用方可以通过 try-with-resources 或close()主动取消底层请求;同一个 stream 只能消费一次。- 新增
AiResponseMetadata,同步ChatResponse和流式事件均可暴露 provider、调用方 request id、模型服务 response id、模型指纹和耗时。 ChatResponse#getRequestId()继续保留兼容语义;新增getResponseId()、getModelFingerprint()、getDuration()和getMetadata()用于更清晰地区分元数据。- 新增
AiClientProvider扩展点和OpenAiCompatibleAiClientProvider,业务侧可接入非兼容协议,项目本身不引入具体厂商 SDK。 under-utils-ai-starter支持根据under.utils.ai.provider匹配自定义AiClientProviderBean;用户自定义AiClientBean 时继续退让。- 相关测试通过 MockWebServer 覆盖流式正常分片、HTTP 错误、连接中断、主动取消、provider 扩展和 starter provider 路由。
第二十六轮结论
AI Named Client Registry
- 新增
AiClientRegistry和DefaultAiClientRegistry,只做命名客户端管理和默认客户端选择,不引入模型自动路由、负载均衡或健康检查。 AiClientRegistry暴露getDefaultClient()、get(name)、find(name)、names()、getStreaming(name)和getDefaultStreaming(),同步与流式边界继续分离。under-utils-ai-starter支持under.utils.ai.clients.<name>.*多客户端配置,并通过under.utils.ai.default-client指定默认客户端。- 未配置
clients时,starter 继续按旧的顶层provider/base-url/api-key/model配置创建名为default的客户端,保持已有单客户端配置兼容。 - 顶层配置可作为命名客户端默认值,单个客户端可覆盖 provider、base URL、API key、model、chat completions path、timeout、retry、temperature、max tokens 和 headers。
- 默认客户端仍作为
AiClientBean 暴露,业务代码只需要单模型时无需感知 registry;用户自定义AiClientBean 时自动装配继续退让。 - 相关测试覆盖 registry 行为、多客户端自动装配、默认客户端选择、headers 继承、自定义 provider 和缺失/不支持流式客户端的失败语义。