This commit is contained in:
2025-11-20 21:42:20 +08:00
parent e2d2b0b75b
commit 150d45e3eb
10 changed files with 491 additions and 126 deletions

View File

@@ -57,6 +57,34 @@ CA_AREA_CODES = {
}
# 主要城市的区号(更精确的城市级约束)
CITY_AREA_CODES = {
"Calgary": ["403", "587", "825"],
"Edmonton": ["780", "587", "825"],
"Vancouver": ["604", "778", "236", "672"],
"Halifax": ["902", "782"],
"Toronto": ["416", "647", "437"],
}
# 邮编首字母合法性映射(按省份缩写)
POSTAL_PREFIXES = {
"AB": {"T"},
"BC": {"V"},
"MB": {"R"},
"NB": {"E"},
"NL": {"A"},
"NS": {"B"},
"ON": {"K", "L", "M"},
"PE": {"C"},
"QC": {"G", "H", "J"},
"SK": {"S"},
"NT": {"X"},
"NU": {"X"},
"YT": {"Y"},
}
REMOTE_PROVINCES = {"NL", "NT", "NU", "YT"}
@@ -261,6 +289,46 @@ def _random_phone(province_abbr: str) -> str:
return f"({area}) {exchange}-{line}"
def _random_phone_city(province_abbr: str, city: Optional[str]) -> str:
"""
按城市优先选择区号,若城市未配置则回退到省份区号
参数:
province_abbr (str): 省份缩写
city (Optional[str]): 城市名
返回值:
str: 电话,例如 "(403) 555-1234"
"""
codes = None
if city:
codes = CITY_AREA_CODES.get(city)
codes = codes or CA_AREA_CODES.get(province_abbr, ["000"])
area = random.choice(codes)
exchange = str(random.randint(200, 899)).zfill(3)
line = str(random.randint(1000, 9999)).zfill(4)
return f"(#{area}) {exchange}-{line}".replace("#", "")
def _postal_valid_for_province(province_abbr: str, postcode: str) -> bool:
"""
校验邮编首字母是否符合省份规范
参数:
province_abbr (str): 省份缩写
postcode (str): 邮编字符串
返回值:
bool: 合法返回 True否则 False
"""
if not postcode:
return False
prefixes = POSTAL_PREFIXES.get(province_abbr)
if not prefixes:
return True
return postcode[0].upper() in prefixes
def generate_canada_info(province: str, city: Optional[str] = None, max_attempts: int = 15, sleep_sec: float = 0.6) -> Dict[str, str]:
"""
随机生成加拿大个人与地址信息,可指定省份(全称或缩写)与可选城市
@@ -289,14 +357,14 @@ def generate_canada_info(province: str, city: Optional[str] = None, max_attempts
address_str = _format_address(addr, prov_abbr)
if prov_abbr in REMOTE_PROVINCES:
break
if addr.get("house_number") and (addr.get("road") or addr.get("residential") or addr.get("footway")) and city_name:
if addr.get("house_number") and (addr.get("road") or addr.get("residential") or addr.get("footway")) and city_name and _postal_valid_for_province(prov_abbr, postcode):
break
time.sleep(sleep_sec)
firstname, lastname = _random_name()
full_name = f"{firstname} {lastname}"
birthday = _random_birthday()
phone = _random_phone(prov_abbr)
phone = _random_phone_city(prov_abbr, city or chosen_city)
return {
"firstname": firstname,