From c2775b732eb95f1774864d99480683f1d9b6ca9d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=A5=BF=E7=93=9C=C2=B0?= Date: Wed, 4 Nov 2026 16:21:18 +0800 Subject: [PATCH] updte --- backend/README.md | 3 +++ backend/api/routes/colg.py | 8 +++---- backend/tests/unit/test_thirdpart_gold.py | 26 ++++++++++++++++++----- 3 files changed, 28 insertions(+), 9 deletions(-) diff --git a/backend/README.md b/backend/README.md index cff286fa..20aeca51 100644 --- a/backend/README.md +++ b/backend/README.md @@ -39,6 +39,9 @@ uv run ruff check . `updataTime` types remain number for realtime and string for official. Redis locks prevent duplicate work across workers. Realtime region pages stay 8 seconds apart. Failures retain the last successful data for up to 24 hours. + Before all realtime regions are available, the response body has `code=202` + and `data=[]`; progress appears only in `message`. A successful response never + substitutes a partially populated region list for the complete dataset. - Realtime selects rows 4–10 (slice `[3:10]`), converts each original unit price with `1 / Price`, then averages those ratios in 万金币/元. Rounding to 2 decimals happens only after averaging; prices are not rounded before conversion. A new diff --git a/backend/api/routes/colg.py b/backend/api/routes/colg.py index 13e04696..c6470e3f 100644 --- a/backend/api/routes/colg.py +++ b/backend/api/routes/colg.py @@ -152,8 +152,7 @@ async def get_thirdpart_gold(redis: Annotated[AsyncRedis, Depends(thirdpart_gold 四舍五入至两位小数,不按库存或份数加权。 每30分钟自动后台刷新,跨区页面最短间隔8秒(每分钟不超过8次),Redis锁避免重复刷新。 - 初始化跨区未齐时响应体code=202并注明进度;9区缓存齐全才返回code=200。 - 刷新中可返回部分已完成跨区,缺失跨区不补0。 + 初始化跨区未齐时响应体code=202、data=[]并注明进度;9区缓存齐全才返回code=200及完整数据。 已有缓存即时返回,失败保留原更新时间,旧值最多保留24小时。 该接口在DEBUG模式下也使用缓存,Redis不可用时不直接抓取上游。 """ @@ -166,8 +165,9 @@ async def get_thirdpart_gold(redis: Annotated[AsyncRedis, Depends(thirdpart_gold await thirdpart_gold.schedule_refresh(redis, background_tasks, data) except Exception: logger.exception('schedule thirdpart gold refresh failed') - return response(code=200 if data else 503, data=data, message='第三方金价刷新暂不可用') + complete = len(data) == len(thirdpart_gold.REGIONS) + return response(code=200 if complete else 503, data=data if complete else [], message='第三方金价刷新暂不可用') if len(data) < len(thirdpart_gold.REGIONS): - return response(code=202, data=data, + return response(code=202, data=[], message=f'第三方金价缓存尚未齐全({len(data)}/{len(thirdpart_gold.REGIONS)}跨区),后台刷新中或等待重试') return response(data=data) diff --git a/backend/tests/unit/test_thirdpart_gold.py b/backend/tests/unit/test_thirdpart_gold.py index 7da1bed8..7e6505b0 100644 --- a/backend/tests/unit/test_thirdpart_gold.py +++ b/backend/tests/unit/test_thirdpart_gold.py @@ -318,7 +318,7 @@ def test_realtime_uses_thirdpart_cache_and_returns_only_defined_fields(monkeypat assert isinstance(row['goldPrice'], (int, float)) -def test_realtime_initialization_still_returns_contract_fields(monkeypatch, client): +def test_realtime_initialization_returns_empty_array_instead_of_partial_regions(monkeypatch, client): async def read(redis): return [{**cached_item('跨2'), 'stale': False}] @@ -329,9 +329,8 @@ def test_realtime_initialization_still_returns_contract_fields(monkeypatch, clie monkeypatch.setattr(gold, 'schedule_refresh', schedule) body = client.get('/api/open/gold/realtime/').json() assert body['code'] == 202 - assert len(body['data']) == 1 - assert set(body['data'][0]) == {'updataTime', 'region', 'code', 'goldPrice'} - assert body['data'][0]['code'] == 'k2' + assert body['data'] == [] + assert '1/9' in body['message'] def test_route_cold_start_and_redis_failure_do_not_block_or_scrape(monkeypatch): @@ -363,7 +362,24 @@ def test_route_partial_cache_reports_initialization_progress(monkeypatch): result = asyncio.run(colg.get_thirdpart_gold(None, BackgroundTasks())) assert result.code == 202 assert '1/9' in result.message - assert len(result.data) == 1 + assert result.data == [] + + +@pytest.mark.parametrize(('count', 'expected_code'), [(1, 503), (9, 200)]) +def test_refresh_schedule_failure_serves_only_complete_cache(monkeypatch, count, expected_code): + data = [{**cached_item(name), 'stale': False} for name in list(gold.REGIONS.values())[:count]] + + async def read(redis): + return data + + async def schedule(*args): + raise ConnectionError('Redis unavailable') + + monkeypatch.setattr(gold, 'read_cache', read) + monkeypatch.setattr(gold, 'schedule_refresh', schedule) + result = asyncio.run(colg.get_thirdpart_gold(None, BackgroundTasks())) + assert result.code == expected_code + assert result.data == (data if count == 9 else []) def test_background_launcher_finishes_before_refresh_completes(monkeypatch):