feat(person_info): 实施基于稳健 ID 的用户信息同步。本次提交重构了用户识别和信息检索系统,使其基于稳定的平台和用户 ID,不再依赖脆弱的姓名解析机制。同时引入了自动后台进程,以保持用户信息的实时更新。主要变更包括:

- 在 `PersonInfoManager` 中新增 `sync_user_info` 方法,根据 `platform` 和 `user_id` 来创建和更新用户记录。
- `ChatManager` 现在会在处理消息时触发该同步作为非阻塞后台任务,确保用户数据(如昵称)保持最新。
- 提示生成逻辑,特别是关系和上下文信息的生成,已重构为使用稳定的 `user_id`,而非从回复消息内容中解析姓名。
- `PromptParameters` 已被扩展,以在整个回复生成流程中传递 `platform` 和 `user_id`。
- 弃用依赖名称到 ID 查找的脆弱方法。
This commit is contained in:
tt-P607
2025-11-25 22:01:41 +08:00
parent 714bef7c2b
commit fd65d8c4eb
5 changed files with 216 additions and 80 deletions

View File

@@ -135,6 +135,113 @@ class PersonInfoManager:
logger.error(f"根据用户名 {person_name} 获取用户ID时出错: {e}")
return ""
@staticmethod
@cached(ttl=600, key_prefix="person_info_by_user_id", use_kwargs=False)
async def get_person_info_by_user_id(platform: str, user_id: str) -> dict | None:
"""[新] 根据 platform 和 user_id 获取用户信息字典"""
if not platform or not user_id:
return None
person_id = PersonInfoManager.get_person_id(platform, user_id)
crud = CRUDBase(PersonInfo)
record = await crud.get_by(person_id=person_id)
if not record:
return None
# 将 SQLAlchemy 模型对象转换为字典
return {c.name: getattr(record, c.name) for c in record.__table__.columns}
@staticmethod
@cached(ttl=600, key_prefix="person_info_by_person_id", use_kwargs=False)
async def get_person_info_by_person_id(person_id: str) -> dict | None:
"""[新] 根据 person_id 获取用户信息字典"""
if not person_id:
return None
crud = CRUDBase(PersonInfo)
record = await crud.get_by(person_id=person_id)
if not record:
return None
# 将 SQLAlchemy 模型对象转换为字典
return {c.name: getattr(record, c.name) for c in record.__table__.columns}
@staticmethod
async def get_person_id_by_name_robust(name: str) -> str | None:
"""[新] 稳健地根据名称获取 person_id按 person_name -> nickname 顺序回退"""
if not name:
return None
crud = CRUDBase(PersonInfo)
# 1. 按 person_name 查询
records = await crud.get_multi(person_name=name, limit=1)
if records:
return records[0].person_id
# 2. 按 nickname 查询
records = await crud.get_multi(nickname=name, limit=1)
if records:
return records[0].person_id
return None
@staticmethod
@staticmethod
@cached(ttl=600, key_prefix="person_info_by_name_robust", use_kwargs=False)
async def get_person_info_by_name_robust(name: str) -> dict | None:
"""[新] 稳健地根据名称获取用户信息,按 person_name -> nickname 顺序回退"""
person_id = await PersonInfoManager.get_person_id_by_name_robust(name)
if person_id:
return await PersonInfoManager.get_person_info_by_person_id(person_id)
return None
@staticmethod
async def sync_user_info(platform: str, user_id: str, nickname: str | None, cardname: str | None) -> str:
"""
[新] 同步用户信息。查询或创建用户,并更新易变信息(如昵称)。
返回 person_id。
"""
if not platform or not user_id:
raise ValueError("platform 和 user_id 不能为空")
person_id = PersonInfoManager.get_person_id(platform, user_id)
crud = CRUDBase(PersonInfo)
record = await crud.get_by(person_id=person_id)
effective_name = cardname or nickname or "未知用户"
if record:
# 用户已存在,检查是否需要更新
updates = {}
if nickname and record.nickname != nickname:
updates["nickname"] = nickname
if updates:
await crud.update(record.id, updates)
logger.debug(f"用户 {person_id} 信息已更新: {updates}")
else:
# 用户不存在,创建新用户
logger.info(f"新用户 {platform}:{user_id},将创建记录。")
unique_person_name = await PersonInfoManager._generate_unique_person_name(effective_name)
new_person_data = {
"person_id": person_id,
"platform": platform,
"user_id": str(user_id),
"nickname": nickname,
"person_name": unique_person_name,
"name_reason": "首次遇见时自动设置",
"know_since": int(time.time()),
"last_know": int(time.time()),
}
await PersonInfoManager._safe_create_person_info(person_id, new_person_data)
return person_id
@staticmethod
@staticmethod
async def first_knowing_some_one(platform: str, user_id: str, user_nickname: str, user_cardname: str):
"""判断是否认识某人"""