fix(HEL-484): 中枢分时接口空 date 按当天查询
缺少或为空的 date 不再 400,按当天处理;显式历史日期保持原行为。 Co-authored-by: Cursor <cursoragent@cursor.com> Co-authored-by: multica-agent <github@multica.ai>
This commit is contained in:
co-authored by
Cursor
multica-agent
parent
3d2c1252f1
commit
acde4de40d
@@ -269,8 +269,13 @@ class V1API:
|
||||
code = str(q.get("code") or "").strip()
|
||||
if not code:
|
||||
raise ApiError("INVALID_ARGUMENT", "code is required")
|
||||
raw_date = str(q.get("date") or "").strip()
|
||||
try:
|
||||
return fetch_intraday(self.db, code, yyyymmdd(q.get("date") or ""))
|
||||
trade_date = yyyymmdd(raw_date or now_shanghai())
|
||||
except ValueError as exc:
|
||||
raise ApiError("INVALID_ARGUMENT", str(exc)) from exc
|
||||
try:
|
||||
return fetch_intraday(self.db, code, trade_date)
|
||||
except RealtimeApiError as exc:
|
||||
raise ApiError(exc.code, exc.message) from exc
|
||||
|
||||
|
||||
@@ -9,6 +9,8 @@ from datahub.adapters.base import AdapterError
|
||||
from datahub.adapters.eastmoney import HIS_TRENDS_URL, TRENDS_URL, EastmoneyAdapter
|
||||
from datahub.db import HubDB
|
||||
from datahub.realtime_serve import fetch_intraday
|
||||
from datahub.serving import ApiError, V1API
|
||||
from datahub.timeutil import now_shanghai, yyyymmdd
|
||||
|
||||
|
||||
class FakeEastmoney(EastmoneyAdapter):
|
||||
@@ -98,5 +100,77 @@ class IntradayLkgTests(unittest.TestCase):
|
||||
self.assertIn("intraday unavailable", str(ctx.exception))
|
||||
|
||||
|
||||
class ServingIntradayDateTests(unittest.TestCase):
|
||||
def setUp(self) -> None:
|
||||
self.tmp = tempfile.TemporaryDirectory()
|
||||
self.db = HubDB(Path(self.tmp.name) / "hub.db")
|
||||
self.api = V1API(self.db, pipeline=None, settings=None)
|
||||
|
||||
def tearDown(self) -> None:
|
||||
self.tmp.cleanup()
|
||||
|
||||
def _assert_usable_intraday(self, payload: dict) -> None:
|
||||
data = payload["data"]
|
||||
points = [point for point in data.get("points") or [] if float(point.get("close") or 0) > 0]
|
||||
self.assertGreaterEqual(len(points), 1)
|
||||
self.assertTrue(str(data.get("trade_date") or ""))
|
||||
self.assertFalse((payload.get("meta") or {}).get("stale"))
|
||||
|
||||
def test_serving_omitted_or_empty_date_uses_today_and_returns_points(self) -> None:
|
||||
today = yyyymmdd(now_shanghai())
|
||||
omitted = self.api.handle("/v1/intraday/points", {"code": ["601318"]})
|
||||
empty = self.api.handle("/v1/intraday/points", {"code": ["601318"], "date": [""]})
|
||||
explicit = self.api.handle("/v1/intraday/points", {"code": ["601318"], "date": [today]})
|
||||
self._assert_usable_intraday(omitted)
|
||||
self._assert_usable_intraday(empty)
|
||||
self._assert_usable_intraday(explicit)
|
||||
self.assertEqual(omitted["data"]["trade_date"], empty["data"]["trade_date"])
|
||||
self.assertEqual(explicit["data"]["trade_date"], omitted["data"]["trade_date"])
|
||||
|
||||
def test_serving_normalizes_empty_date_to_today_and_keeps_history(self) -> None:
|
||||
today = yyyymmdd(now_shanghai())
|
||||
captured: list[str] = []
|
||||
|
||||
def fake_fetch(db, code, date=""):
|
||||
captured.append(date)
|
||||
return {
|
||||
"schema_version": 1,
|
||||
"data": {
|
||||
"trade_date": f"{date[:4]}-{date[4:6]}-{date[6:8]}",
|
||||
"points": [{"date": f"{date[:4]}-{date[4:6]}-{date[6:8]}", "time": "09:30", "close": 55.9}],
|
||||
},
|
||||
"meta": {"stale": False, "trade_date": date},
|
||||
}
|
||||
|
||||
with patch("datahub.realtime_serve.fetch_intraday", side_effect=fake_fetch):
|
||||
omitted = self.api.handle("/v1/intraday/points", {"code": ["601318"]})
|
||||
empty = self.api.handle("/v1/intraday/points", {"code": ["601318"], "date": [" "]})
|
||||
history = self.api.handle("/v1/intraday/points", {"code": ["601318"], "date": ["20260907"]})
|
||||
self.assertEqual(captured, [today, today, "20260907"])
|
||||
self.assertEqual(omitted["data"]["trade_date"], f"{today[:4]}-{today[4:6]}-{today[6:8]}")
|
||||
self.assertEqual(empty["data"]["trade_date"], omitted["data"]["trade_date"])
|
||||
self.assertEqual(history["data"]["trade_date"], "2026-09-07")
|
||||
|
||||
def test_serving_invalid_date_is_invalid_argument(self) -> None:
|
||||
with self.assertRaises(ApiError) as ctx:
|
||||
self.api.handle("/v1/intraday/points", {"code": ["601318"], "date": ["not-a-date"]})
|
||||
self.assertEqual(ctx.exception.code, "INVALID_ARGUMENT")
|
||||
self.assertIn("invalid trade_date", ctx.exception.message)
|
||||
|
||||
def test_serving_missing_code_is_invalid_argument(self) -> None:
|
||||
with self.assertRaises(ApiError) as ctx:
|
||||
self.api.handle("/v1/intraday/points", {"date": [yyyymmdd(now_shanghai())]})
|
||||
self.assertEqual(ctx.exception.code, "INVALID_ARGUMENT")
|
||||
self.assertIn("code is required", ctx.exception.message)
|
||||
|
||||
def test_serving_no_data_keeps_source_unavailable(self) -> None:
|
||||
with patch("datahub.realtime_serve.EastmoneyAdapter") as mocked:
|
||||
mocked.return_value.fetch_intraday.side_effect = AdapterError("No intraday chart data returned")
|
||||
with self.assertRaises(ApiError) as ctx:
|
||||
self.api.handle("/v1/intraday/points", {"code": ["000001"]})
|
||||
self.assertEqual(ctx.exception.code, "SOURCE_UNAVAILABLE")
|
||||
self.assertIn("intraday unavailable", ctx.exception.message)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
|
||||
Reference in New Issue
Block a user