Files

242 lines
7.3 KiB
Python
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import json
import os
from typing import Optional, List, Dict
# 缓存文件路径
CACHE_FILE_PATH = os.path.join(os.path.dirname(__file__), 'area_cache.json')
class AreaInfo:
def __init__(self, code: str, name: str, level: str, geoname_id: int = None):
self.code = code
self.name = name
self.level = level
self.geoname_id = geoname_id
self.sub_districts: List[AreaInfo] = []
def to_dict(self):
return {
"code": self.code,
"name": self.name,
"level": self.level,
"geoname_id": self.geoname_id,
"sub_districts": [sd.to_dict() for sd in self.sub_districts] if self.sub_districts else [],
}
@classmethod
def from_dict(cls, data: dict):
area = cls(
code=data.get("code"),
name=data.get("name"),
level=data.get("level"),
geoname_id=data.get("geoname_id"),
)
sub_districts = data.get("sub_districts", [])
for sub in sub_districts:
area.sub_districts.append(cls.from_dict(sub))
return area
def parse_district_data(district_data: dict) -> AreaInfo:
"""解析巨量接口返回的区域数据"""
area = AreaInfo(
code=district_data.get("code"),
name=district_data.get("name"),
level=district_data.get("level"),
geoname_id=district_data.get("geoname_id"),
)
sub_districts = district_data.get("sub_districts")
if sub_districts:
for sub in sub_districts:
area.sub_districts.append(parse_district_data(sub))
return area
def save_area_cache(areas: List[AreaInfo]) -> None:
"""将区域数据保存到缓存文件"""
data = [area.to_dict() for area in areas]
with open(CACHE_FILE_PATH, 'w', encoding='utf-8') as f:
json.dump(data, f, ensure_ascii=False, indent=2)
def load_area_cache() -> Optional[List[AreaInfo]]:
"""从缓存文件加载区域数据"""
if not os.path.exists(CACHE_FILE_PATH):
return None
try:
with open(CACHE_FILE_PATH, 'r', encoding='utf-8') as f:
data = json.load(f)
return [AreaInfo.from_dict(item) for item in data]
except (json.JSONDecodeError, IOError):
return None
def clear_area_cache() -> None:
"""清除缓存文件"""
if os.path.exists(CACHE_FILE_PATH):
os.remove(CACHE_FILE_PATH)
def filter_by_level(areas: List[AreaInfo], target_level: str) -> List[AreaInfo]:
"""
根据级别过滤区域信息
:param areas: 区域列表
:param target_level: ONE_LEVEL / TWO_LEVEL / THREE_LEVEL
:return: 指定级别的区域列表
"""
result = []
def traverse(area: AreaInfo):
if area.level == target_level:
filtered = AreaInfo(
code=area.code,
name=area.name,
level=area.level,
geoname_id=area.geoname_id,
)
result.append(filtered)
if area.sub_districts:
for sub in area.sub_districts:
traverse(sub)
for area in areas:
traverse(area)
return result
def get_first_level_areas(areas: List[AreaInfo]) -> List[AreaInfo]:
"""获取所有一级区域(省/直辖市)"""
return filter_by_level(areas, "ONE_LEVEL")
def get_second_level_areas(areas: List[AreaInfo], parent_code: str = None) -> List[AreaInfo]:
"""
获取二级区域(市)
:param areas: 区域列表
:param parent_code: 一级区域code,不传则返回所有二级区域
:return: 二级区域列表
"""
if parent_code:
def find_parent_and_get_children(area: AreaInfo):
if area.code == parent_code:
return [AreaInfo(
code=sub.code,
name=sub.name,
level=sub.level,
geoname_id=sub.geoname_id,
) for sub in area.sub_districts] if area.sub_districts else []
if area.sub_districts:
for sub in area.sub_districts:
result = find_parent_and_get_children(sub)
if result:
return result
return []
for area in areas:
result = find_parent_and_get_children(area)
if result:
return result
return []
else:
return filter_by_level(areas, "TWO_LEVEL")
def get_third_level_areas(areas: List[AreaInfo], parent_code: str) -> List[AreaInfo]:
"""
获取三级区域(区/县)
:param areas: 区域列表
:param parent_code: 二级区域code
:return: 三级区域列表
"""
def find_parent_and_get_children(area: AreaInfo):
if area.code == parent_code:
return [AreaInfo(
code=sub.code,
name=sub.name,
level=sub.level,
geoname_id=sub.geoname_id,
) for sub in area.sub_districts] if area.sub_districts else []
if area.sub_districts:
for sub in area.sub_districts:
result = find_parent_and_get_children(sub)
if result:
return result
return []
for area in areas:
result = find_parent_and_get_children(area)
if result:
return result
return []
def get_area_by_level(areas: List[AreaInfo], level: str, parent_code: str = None) -> List[AreaInfo]:
"""
根据级别获取区域信息
:param areas: 区域列表
:param level: ONE_LEVEL / TWO_LEVEL / THREE_LEVEL
:param parent_code: 父级区域codeTWO_LEVEL和THREE_LEVEL时可选/必填)
:return: 区域列表
"""
level = level.upper()
if level == "ONE_LEVEL":
return get_first_level_areas(areas)
elif level == "TWO_LEVEL":
return get_second_level_areas(areas, parent_code)
elif level == "THREE_LEVEL":
if not parent_code:
raise ValueError("获取三级区域需要提供二级区域code")
return get_third_level_areas(areas, parent_code)
else:
raise ValueError(f"不支持的级别: {level}")
async def fetch_and_cache_area_data(oauth_id: str, advertiser_id: str = 1836693172153543, code: str = "CN") -> List[AreaInfo]:
"""
从接口获取区域数据并缓存到文件
:param oauth_id: 授权ID
:param code: 行政区域编码,默认中国CN
:return: 区域列表
"""
from app.utils.douyinApi import DouyinApi
params = {
"advertiser_id": advertiser_id,
"codes": json.dumps([code]),
"language": "ZH_CN",
"sub_district": "THREE_LEVEL",
"version": "V2_3_2"
}
area_response = await DouyinApi().get_area(oauth_id=oauth_id, params=params)
if area_response.get("code") != 0:
raise Exception(f"获取区域信息失败: {area_response.get('message', '未知错误')}")
districts_data = area_response.get("data", {}).get("districts", [])
if not districts_data:
raise Exception("接口返回的区域数据为空")
area_list = [parse_district_data(d) for d in districts_data]
save_area_cache(area_list)
return area_list
def get_cached_area_data() -> Optional[List[AreaInfo]]:
"""获取缓存的区域数据"""
return load_area_cache()