From a9bc06fc2f6601d6e2d474ae5b8dd3ff65281127 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=A0=E5=AE=B6=E8=AE=AD?= Date: Mon, 3 Aug 2026 10:42:20 +0800 Subject: [PATCH] Document API proxy startup design --- ...26-08-03-api-debug-proxy-startup-design.md | 79 +++++++++++++++++++ 1 file changed, 79 insertions(+) create mode 100644 docs/superpowers/specs/2026-08-03-api-debug-proxy-startup-design.md diff --git a/docs/superpowers/specs/2026-08-03-api-debug-proxy-startup-design.md b/docs/superpowers/specs/2026-08-03-api-debug-proxy-startup-design.md new file mode 100644 index 0000000..5df7705 --- /dev/null +++ b/docs/superpowers/specs/2026-08-03-api-debug-proxy-startup-design.md @@ -0,0 +1,79 @@ +# WorkUtils API 本地调试代理启动设计 + +## 目标 + +WorkUtils 进程启动时自动启动 API 文档调试所需的本地 HTTP 代理。用户无需进入 API 导出页面、勾选复选框或点击导出按钮来启动代理。API 导出页面删除代理开关,并持续展示代理已启动或启动失败的只读状态。 + +## 范围 + +- 代理随 WorkUtils 的 `CommandLineRunner` 生命周期启动,GUI 和命令行入口使用同一套逻辑。 +- 代理失败不得阻止 WorkUtils 主界面或命令行功能继续运行。 +- API 导出始终读取应用启动阶段确定的代理端点;启动失败时端点为空,文档仍可导出,但接口调试功能不可用。 +- WorkUtils 运行结束时主动停止 HTTP Server 和代理线程池。 +- 不新增配置项,不持久化代理端口,不改变代理的随机本机端口策略。 + +## 架构 + +新增一个 Spring 单例生命周期服务,作为应用入口、API 插件和代理实现之间的唯一边界。服务负责: + +1. 幂等启动 `DubboDebugProxy`; +2. 捕获启动异常并保存不可变状态; +3. 向 UI 和导出用例提供端点或失败原因; +4. 在应用运行结束时幂等关闭代理。 + +`WorkUtilsApplication.run(...)` 在分派 GUI 或命令行操作前调用生命周期服务的启动方法,并在运行结束的 `finally` 中关闭代理。这样代理会在任何 API 页面构造之前启动,也不会依赖可能晚于阻塞式窗口循环的 `ApplicationReadyEvent`。 + +`ApiDocPlugin` 注入同一个生命周期服务并传给 `ApiDocWindow`。页面只读取状态,不创建、重启或关闭代理。 + +## 状态模型 + +生命周期服务公开不可变状态,状态仅允许以下三种结果: + +- `NOT_STARTED`:仅用于应用启动调用发生前和单元测试初始化; +- `RUNNING`:包含非空的 `http://127.0.0.1:{port}/dubbo/invoke` 地址; +- `FAILED`:包含可展示的失败原因,端点为空。 + +重复调用启动方法不得创建第二个代理实例。重复调用关闭方法不得抛出异常。 + +## API 导出页面 + +删除“启用接口调试(启动本地代理)”复选框。原位置显示只读状态标签: + +- 成功:`● 本地代理已启动:http://127.0.0.1:{port}/dubbo/invoke` +- 失败:`● 本地代理启动失败:{错误原因}` + +成功状态使用现有成功色,失败状态使用现有危险色。失败标签提供包含完整错误原因的 Tooltip 和 accessible text。状态标签不提供启动、重试或关闭操作。 + +## 导出数据流 + +`ApiDocExportRequest` 删除 `debugProxyEnabled` 字段和所有对应工厂参数。`RunApiDocExportUseCase` 每次执行都从注入的端点供应器读取当前地址,并将非空地址交给导出器。代理启动失败时供应器返回空字符串,导出照常进行,API 页面日志追加“本地代理不可用,导出文档的接口调试功能已禁用”。 + +页面不再持有代理复选框,也不再从 UI 控件构造代理选择状态。 + +## 代理关闭 + +`DubboDebugProxy` 增加幂等 `stop()`:停止 `HttpServer`,关闭其专用 `ExecutorService`。代理不复用全局任务执行器,防止关闭代理影响其他业务任务。 + +## 错误处理 + +- 启动失败:生命周期服务捕获异常、记录错误、保存 `FAILED` 状态,WorkUtils 继续运行。 +- UI 展示:API 页面读取失败状态并展示短原因;完整原因放入 Tooltip。 +- 导出降级:空端点不阻止 HTML/Markdown 生成,只禁用生成文档中的发送调试请求能力。 +- 关闭失败:记录警告,不覆盖原始业务异常,也不阻止进程退出。 + +## 测试策略 + +- 生命周期服务:启动成功、启动失败、重复启动、重复关闭、关闭释放代理。 +- 应用架构:`WorkUtilsApplication` 在业务分派前启动,并在 `finally` 中关闭。 +- API 页面:不存在代理复选框;成功和失败状态文案、颜色语义、Tooltip 与 accessible text 正确。 +- 导出请求:三个模式不再接收或保存代理布尔值。 +- 导出用例:成功时始终传递代理地址;失败时传递空地址且导出仍执行。 +- 聚焦测试后运行 Maven 全量测试、编译和 `git diff --check`。 + +## 验收标准 + +1. 启动 WorkUtils 后,无需打开 API 导出页面即可在日志中看到本地代理启动地址。 +2. API 导出页没有代理复选框,仅显示已启动或失败状态。 +3. 代理成功时,任何 API 导出都自动获得代理地址。 +4. 代理失败时,WorkUtils 和普通文档导出仍可使用,页面清楚提示调试功能不可用。 +5. WorkUtils 运行结束时代理端口和线程池被主动释放。