2025-09-21 13:33:09 +08:00
|
|
|
|
"""
|
|
|
|
|
|
用户ID生成工具
|
|
|
|
|
|
提供用户名验证和user_id自动生成功能
|
|
|
|
|
|
"""
|
|
|
|
|
|
|
|
|
|
|
|
import re
|
|
|
|
|
|
|
2025-09-22 17:13:52 +08:00
|
|
|
|
from pypinyin import lazy_pinyin, Style
|
2025-09-21 13:33:09 +08:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def to_pinyin(text: str) -> str:
|
|
|
|
|
|
"""
|
|
|
|
|
|
将中文转换为拼音
|
2025-09-22 17:13:52 +08:00
|
|
|
|
使用pypinyin库进行转换
|
2025-09-21 13:33:09 +08:00
|
|
|
|
"""
|
2025-09-22 17:13:52 +08:00
|
|
|
|
# 使用pypinyin进行转换
|
|
|
|
|
|
pinyin_list = lazy_pinyin(text, style=Style.NORMAL)
|
|
|
|
|
|
return "".join(pinyin_list)
|
2025-09-21 13:33:09 +08:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def validate_username(username: str) -> tuple[bool, str]:
|
|
|
|
|
|
"""
|
|
|
|
|
|
验证用户名格式
|
|
|
|
|
|
|
|
|
|
|
|
Args:
|
|
|
|
|
|
username: 用户名
|
|
|
|
|
|
|
|
|
|
|
|
Returns:
|
|
|
|
|
|
Tuple[bool, str]: (是否有效, 错误信息)
|
|
|
|
|
|
"""
|
|
|
|
|
|
if not username:
|
|
|
|
|
|
return False, "用户名不能为空"
|
|
|
|
|
|
|
|
|
|
|
|
if len(username) < 2:
|
|
|
|
|
|
return False, "用户名长度不能少于2个字符"
|
|
|
|
|
|
|
|
|
|
|
|
if len(username) > 20:
|
|
|
|
|
|
return False, "用户名长度不能超过20个字符"
|
|
|
|
|
|
|
|
|
|
|
|
# 检查是否包含不允许的字符
|
|
|
|
|
|
# 允许中文、英文、数字、下划线
|
2025-09-22 17:13:52 +08:00
|
|
|
|
if not re.match(r"^[\u4e00-\u9fa5a-zA-Z0-9_]+$", username):
|
2025-09-21 13:33:09 +08:00
|
|
|
|
return False, "用户名只能包含中文、英文、数字和下划线"
|
|
|
|
|
|
|
|
|
|
|
|
return True, ""
|
|
|
|
|
|
|
|
|
|
|
|
|
2026-05-17 22:44:05 +08:00
|
|
|
|
def generate_uid(username: str) -> str:
|
2025-09-21 13:33:09 +08:00
|
|
|
|
"""
|
|
|
|
|
|
根据用户名生成user_id
|
|
|
|
|
|
|
|
|
|
|
|
Args:
|
|
|
|
|
|
username: 用户名
|
|
|
|
|
|
|
|
|
|
|
|
Returns:
|
|
|
|
|
|
str: 生成的user_id
|
|
|
|
|
|
"""
|
|
|
|
|
|
# 1. 基本清理
|
|
|
|
|
|
username = username.strip()
|
|
|
|
|
|
|
|
|
|
|
|
# 2. 转换为拼音(如果包含中文)
|
2026-05-17 22:44:05 +08:00
|
|
|
|
uid = to_pinyin(username)
|
2025-09-21 13:33:09 +08:00
|
|
|
|
|
|
|
|
|
|
# 3. 处理特殊字符,只保留字母、数字和下划线
|
2026-05-17 22:44:05 +08:00
|
|
|
|
uid = re.sub(r"[^a-zA-Z0-9_]", "", uid)
|
2025-09-21 13:33:09 +08:00
|
|
|
|
|
|
|
|
|
|
# 4. 确保不以数字开头
|
2026-05-17 22:44:05 +08:00
|
|
|
|
if uid and uid[0].isdigit():
|
|
|
|
|
|
uid = "u" + uid
|
2025-09-21 13:33:09 +08:00
|
|
|
|
|
|
|
|
|
|
# 5. 如果为空或太短,使用默认前缀
|
2026-05-17 22:44:05 +08:00
|
|
|
|
if len(uid) < 2:
|
|
|
|
|
|
uid = "user" + str(hash(username) % 10000).zfill(4)
|
2025-09-21 13:33:09 +08:00
|
|
|
|
|
|
|
|
|
|
# 6. 长度限制
|
2026-05-17 22:44:05 +08:00
|
|
|
|
if len(uid) > 20:
|
|
|
|
|
|
uid = uid[:20]
|
2025-09-21 13:33:09 +08:00
|
|
|
|
|
2026-05-17 22:44:05 +08:00
|
|
|
|
return uid.lower()
|
2025-09-21 13:33:09 +08:00
|
|
|
|
|
|
|
|
|
|
|
2026-05-17 22:44:05 +08:00
|
|
|
|
def generate_unique_uid(username: str, existing_uids: list[str]) -> str:
|
2025-09-21 13:33:09 +08:00
|
|
|
|
"""
|
|
|
|
|
|
生成唯一的user_id,如果重复则添加数字后缀
|
|
|
|
|
|
|
|
|
|
|
|
Args:
|
|
|
|
|
|
username: 用户名
|
2026-05-17 22:44:05 +08:00
|
|
|
|
existing_uids: 已存在的user_id列表
|
2025-09-21 13:33:09 +08:00
|
|
|
|
|
|
|
|
|
|
Returns:
|
|
|
|
|
|
str: 唯一的user_id
|
|
|
|
|
|
"""
|
2026-05-17 22:44:05 +08:00
|
|
|
|
base_uid = generate_uid(username)
|
2025-09-21 13:33:09 +08:00
|
|
|
|
|
|
|
|
|
|
# 如果不重复,直接返回
|
2026-05-17 22:44:05 +08:00
|
|
|
|
if base_uid not in existing_uids:
|
|
|
|
|
|
return base_uid
|
2025-09-21 13:33:09 +08:00
|
|
|
|
|
|
|
|
|
|
# 如果重复,添加数字后缀
|
|
|
|
|
|
counter = 1
|
|
|
|
|
|
while True:
|
2026-05-17 22:44:05 +08:00
|
|
|
|
candidate = f"{base_uid}{counter}"
|
|
|
|
|
|
if candidate not in existing_uids:
|
2025-09-21 13:33:09 +08:00
|
|
|
|
return candidate
|
|
|
|
|
|
counter += 1
|
|
|
|
|
|
|
|
|
|
|
|
# 防止无限循环
|
|
|
|
|
|
if counter > 9999:
|
|
|
|
|
|
# 使用时间戳作为后缀
|
|
|
|
|
|
import time
|
2025-09-22 17:13:52 +08:00
|
|
|
|
|
2026-05-17 22:44:05 +08:00
|
|
|
|
candidate = f"{base_uid}{int(time.time()) % 10000}"
|
2025-09-21 13:33:09 +08:00
|
|
|
|
return candidate
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def is_valid_phone_number(phone: str) -> bool:
|
|
|
|
|
|
"""
|
|
|
|
|
|
验证手机号格式(支持中国大陆手机号)
|
|
|
|
|
|
|
|
|
|
|
|
Args:
|
|
|
|
|
|
phone: 手机号字符串
|
|
|
|
|
|
|
|
|
|
|
|
Returns:
|
|
|
|
|
|
bool: 是否为有效手机号
|
|
|
|
|
|
"""
|
|
|
|
|
|
if not phone:
|
|
|
|
|
|
return False
|
|
|
|
|
|
|
|
|
|
|
|
# 移除空格和特殊字符
|
2025-09-22 17:13:52 +08:00
|
|
|
|
phone = re.sub(r"[\s\-\(\)]", "", phone)
|
2025-09-21 13:33:09 +08:00
|
|
|
|
|
|
|
|
|
|
# 中国大陆手机号格式:1开头,第二位是3-9,总共11位
|
2025-09-22 17:13:52 +08:00
|
|
|
|
pattern = r"^1[3-9]\d{9}$"
|
2025-09-21 13:33:09 +08:00
|
|
|
|
|
|
|
|
|
|
return bool(re.match(pattern, phone))
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def normalize_phone_number(phone: str) -> str:
|
|
|
|
|
|
"""
|
|
|
|
|
|
标准化手机号格式
|
|
|
|
|
|
|
|
|
|
|
|
Args:
|
|
|
|
|
|
phone: 原始手机号
|
|
|
|
|
|
|
|
|
|
|
|
Returns:
|
|
|
|
|
|
str: 标准化后的手机号
|
|
|
|
|
|
"""
|
|
|
|
|
|
if not phone:
|
|
|
|
|
|
return ""
|
|
|
|
|
|
|
|
|
|
|
|
# 移除所有非数字字符
|
2025-09-22 17:13:52 +08:00
|
|
|
|
phone = re.sub(r"\D", "", phone)
|
2025-09-21 13:33:09 +08:00
|
|
|
|
|
|
|
|
|
|
# 如果是中国大陆手机号,确保格式正确
|
2025-09-22 17:13:52 +08:00
|
|
|
|
if len(phone) == 11 and phone.startswith("1"):
|
2025-09-21 13:33:09 +08:00
|
|
|
|
return phone
|
|
|
|
|
|
|
|
|
|
|
|
return phone
|