updte
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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):
|
||||
|
||||
Reference in New Issue
Block a user