From ee628c1b345c5560601888fb3dec5eae230d52e2 Mon Sep 17 00:00:00 2001 From: Justin Gu <97915@qq.com> Date: Sat, 4 Jul 2026 20:50:07 +0800 Subject: [PATCH] =?UTF-8?q?release:=20v1.17.12=20=E2=80=94=20=E4=BF=AE?= =?UTF-8?q?=E5=A4=8D=20CI=20=E5=9C=A8=E6=96=B0=E7=89=88=20FastAPI=20?= =?UTF-8?q?=E4=B8=8A=E8=B7=AF=E7=94=B1=E6=B3=A8=E5=86=8C=E5=A4=B1=E8=B4=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit DELETE /api/v1/strategies/{id} 原用 status_code=204,较新 FastAPI/Starlette 在路由注册阶段(add_api_route)就抛 AssertionError: Status code 204 must not have a response body,导致 CI ubuntu 矩阵 21 个 web 测试因 fixture 导入 router 连带 ERROR。改为返回 200 + {"deleted": id} 确认体。 --- CHANGELOG.md | 8 ++++++++ pyproject.toml | 2 +- src/easy_tdx/web/routers/strategies.py | 12 +++++++++--- tests/unit/test_strategy_store.py | 5 +++-- 4 files changed, 21 insertions(+), 6 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index cdd1bd4..575fd17 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,14 @@ 本文件记录 easy-tdx 的版本变更。格式遵循 [Keep a Changelog](https://keepachangelog.com/zh-CN/)。 +## [1.17.12] — 2026-07-04 + +**修复 CI 在新版 FastAPI 上路由注册失败** —— v1.17.11 的 `DELETE /api/v1/strategies/{id}` 用 `status_code=204`,较新 FastAPI/Starlette 在路由注册阶段(`add_api_route`)就抛 `AssertionError: Status code 204 must not have a response body`,导致 CI 的 ubuntu 矩阵(py3.10/3.12/3.13)整片 ERROR(21 个 web 测试因 fixture 导入 router 而连带失败)。改为返回 `200 + {"deleted": id}` 确认体,既消除注册期断言又给前端明确反馈。 + +### 修复 + +- **DELETE 路由不再用 204**(`src/easy_tdx/web/routers/strategies.py`)—— `status_code=204` 改为默认 200,返回 `{"deleted": strategy_id}`;同步更新测试断言(`tests/unit/test_strategy_store.py`)。 + ## [1.17.11] — 2026-07-04 **Web UI 新增「策略库」与「多策略组合回测」** —— 此前回测结果存在进程内存,重启即丢,用户无法留存自己反复验证过的好策略。本次落地两层能力:(1) **策略库**——在单标的/组合回测结果区点「保存策略」,把策略 + 标的上下文 + 成绩快照(总收益/夏普/回撤/胜率)一起存进本地 SQLite 单文件(`~/.easy_tdx/strategies.db`),策略库页可载入回填、一键重跑、删除;(2) **多策略组合回测**——策略库勾选 N 个单标的策略,各拿 1/N 资金、各跑原标的(取最新行情),净值曲线按日期并集对齐求和,组合结果复用单标的的 19 项完整绩效指标(基于合并净值曲线 + 汇总成交用 `PerformanceAnalyzer` 算出),并展示各策略当前持仓。**895 单测全绿**(+24 新增),ruff format/check / mypy strict / 前端 vue-tsc 全通过。 diff --git a/pyproject.toml b/pyproject.toml index 24a9591..e72bc73 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "hatchling.build" [project] name = "easy-tdx" -version = "1.17.11" +version = "1.17.12" description = "通达信 TCP 协议行情数据客户端,支持在线行情、离线数据读取与写入同步" readme = "README.md" requires-python = ">=3.10" diff --git a/src/easy_tdx/web/routers/strategies.py b/src/easy_tdx/web/routers/strategies.py index 939d6d3..f48e2f2 100644 --- a/src/easy_tdx/web/routers/strategies.py +++ b/src/easy_tdx/web/routers/strategies.py @@ -80,9 +80,15 @@ async def create_saved_strategy(req: SavedStrategyCreate) -> SavedStrategy: return _to_response(saved) -@router.delete("/strategies/{strategy_id}", status_code=204) -async def delete_saved_strategy(strategy_id: str) -> None: - """按 id 删除一条已保存策略。不存在则 404。""" +@router.delete("/strategies/{strategy_id}") +async def delete_saved_strategy(strategy_id: str) -> dict[str, str]: + """按 id 删除一条已保存策略。不存在则 400。 + + 注:不用 204(No Content),因较新 FastAPI 在路由注册阶段就拒绝 + status_code=204 且有响应模型的端点("204 must not have a response body"), + 返回简单确认体更兼容、前端无需特判。 + """ store = get_store() if not store.delete(strategy_id): raise ValueError(f"策略 '{strategy_id}' 不存在") + return {"deleted": strategy_id} diff --git a/tests/unit/test_strategy_store.py b/tests/unit/test_strategy_store.py index 871fab5..74d2d04 100644 --- a/tests/unit/test_strategy_store.py +++ b/tests/unit/test_strategy_store.py @@ -214,9 +214,10 @@ def test_router_create_then_list_get_delete(client: TestClient): assert resp.status_code == 200 assert resp.json()["snapshot"]["total_return"] == pytest.approx(0.35) - # 4. 删除 + # 4. 删除(返回 200 + 确认体,非 204,见路由注释) resp = client.delete(f"/api/v1/strategies/{sid}") - assert resp.status_code == 204 + assert resp.status_code == 200 + assert resp.json()["deleted"] == sid # 5. 列表为空 assert client.get("/api/v1/strategies").json()["count"] == 0