Merge pull request #686 from SuanmoSuanyangTechnology/feature/user-alias

Feature/user alias
This commit is contained in:
Ke Sun
2026-03-26 17:34:00 +08:00
committed by GitHub
17 changed files with 908 additions and 258 deletions

View File

@@ -0,0 +1,71 @@
"""
终端用户信息仓储层
"""
import uuid
from typing import List, Optional
from sqlalchemy.orm import Session
from app.models.end_user_info_model import EndUserInfo
from app.core.logging_config import get_logger
logger = get_logger(__name__)
class EndUserInfoRepository:
"""终端用户信息仓储类"""
def __init__(self, db: Session):
self.db = db
def create(self, end_user_id: uuid.UUID, other_name: str, aliases: List[str] = None, meta_data: dict = None) -> EndUserInfo:
"""创建终端用户信息"""
end_user_info = EndUserInfo(
end_user_id=end_user_id,
other_name=other_name,
aliases=aliases or [],
meta_data=meta_data
)
self.db.add(end_user_info)
self.db.commit()
self.db.refresh(end_user_info)
logger.info(f"创建终端用户信息: end_user_id={end_user_id}, aliases={aliases}")
return end_user_info
def get_by_id(self, info_id: uuid.UUID) -> Optional[EndUserInfo]:
"""根据ID获取用户信息"""
return self.db.query(EndUserInfo).filter(EndUserInfo.id == info_id).first()
def get_by_end_user_id(self, end_user_id: uuid.UUID) -> Optional[EndUserInfo]:
"""获取用户的信息记录"""
return self.db.query(EndUserInfo).filter(EndUserInfo.end_user_id == end_user_id).first()
def update(self, info_id: uuid.UUID, aliases: List[str] = None, meta_data: dict = None) -> Optional[EndUserInfo]:
"""更新用户信息"""
end_user_info = self.get_by_id(info_id)
if end_user_info:
if aliases is not None:
end_user_info.aliases = aliases
if meta_data is not None:
end_user_info.meta_data = meta_data
self.db.commit()
self.db.refresh(end_user_info)
logger.info(f"更新终端用户信息: info_id={info_id}")
return end_user_info
def delete(self, info_id: uuid.UUID) -> bool:
"""删除用户信息"""
end_user_info = self.get_by_id(info_id)
if end_user_info:
self.db.delete(end_user_info)
self.db.commit()
logger.info(f"删除终端用户信息: info_id={info_id}")
return True
return False
def delete_by_end_user_id(self, end_user_id: uuid.UUID) -> int:
"""删除用户的所有信息记录"""
count = self.db.query(EndUserInfo).filter(EndUserInfo.end_user_id == end_user_id).delete()
self.db.commit()
logger.info(f"删除用户所有信息记录: end_user_id={end_user_id}, count={count}")
return count

View File

@@ -7,6 +7,7 @@ from sqlalchemy.orm import Session
from app.core.logging_config import get_db_logger
from app.models.app_model import App
from app.models.end_user_model import EndUser
from app.models.end_user_info_model import EndUserInfo
from app.models.workspace_model import Workspace
# 获取数据库专用日志器
@@ -70,7 +71,8 @@ class EndUserRepository:
app_id: uuid.UUID,
workspace_id: uuid.UUID,
other_id: str,
original_user_id: Optional[str] = None
original_user_id: Optional[str] = None,
other_name: Optional[str] = None
) -> EndUser:
"""获取或创建终端用户
@@ -79,6 +81,7 @@ class EndUserRepository:
workspace_id: 工作空间ID
other_id: 第三方ID
original_user_id: 原始用户ID (存储到 other_id)
other_name: 用户名称(用于创建 EndUserInfo
"""
try:
# 尝试查找现有用户
@@ -106,10 +109,22 @@ class EndUserRepository:
other_id=other_id
)
self.db.add(end_user)
self.db.flush() # 刷新以获取 end_user.id但不提交事务
# 创建对应的 EndUserInfo 记录
end_user_info = EndUserInfo(
end_user_id=end_user.id,
other_name=other_name or "", # 如果没有提供 other_name使用空字符串
aliases=[],
meta_data={}
)
self.db.add(end_user_info)
# 一起提交
self.db.commit()
self.db.refresh(end_user)
db_logger.info(f"创建新终端用户: (other_id: {other_id}) for workspace {workspace_id}")
db_logger.info(f"创建新终端用户及其信息: (other_id: {other_id}) for workspace {workspace_id}")
return end_user
except Exception as e:

View File

@@ -336,6 +336,48 @@ ORDER BY score DESC
LIMIT $limit
"""
SEARCH_ENTITIES_BY_NAME_OR_ALIAS = """
CALL db.index.fulltext.queryNodes("entitiesFulltext", $q) YIELD node AS e, score
WHERE ($end_user_id IS NULL OR e.end_user_id = $end_user_id)
WITH e, score
UNION
MATCH (e:ExtractedEntity)
WHERE ($end_user_id IS NULL OR e.end_user_id = $end_user_id)
AND e.aliases IS NOT NULL
AND ANY(alias IN e.aliases WHERE toLower(alias) CONTAINS toLower($q))
WITH e,
CASE
WHEN ANY(alias IN e.aliases WHERE toLower(alias) = toLower($q)) THEN 1.0
WHEN ANY(alias IN e.aliases WHERE toLower(alias) STARTS WITH toLower($q)) THEN 0.9
ELSE 0.8
END AS score
WITH DISTINCT e, MAX(score) AS score
OPTIONAL MATCH (s:Statement)-[:REFERENCES_ENTITY]->(e)
OPTIONAL MATCH (c:Chunk)-[:CONTAINS]->(s)
RETURN e.id AS id,
e.name AS name,
e.end_user_id AS end_user_id,
e.entity_type AS entity_type,
e.created_at AS created_at,
e.expired_at AS expired_at,
e.entity_idx AS entity_idx,
e.statement_id AS statement_id,
e.description AS description,
e.aliases AS aliases,
e.name_embedding AS name_embedding,
e.connect_strength AS connect_strength,
collect(DISTINCT s.id) AS statement_ids,
collect(DISTINCT c.id) AS chunk_ids,
COALESCE(e.activation_value, e.importance_score, 0.5) AS activation_value,
COALESCE(e.importance_score, 0.5) AS importance_score,
e.last_access_time AS last_access_time,
COALESCE(e.access_count, 0) AS access_count,
score
ORDER BY score DESC
LIMIT $limit
"""
SEARCH_CHUNKS_BY_CONTENT = """
CALL db.index.fulltext.queryNodes("chunksFulltext", $q) YIELD node AS c, score
WHERE ($end_user_id IS NULL OR c.end_user_id = $end_user_id)

View File

@@ -13,6 +13,7 @@ from app.repositories.neo4j.cypher_queries import (
SEARCH_COMMUNITIES_BY_KEYWORD,
SEARCH_DIALOGUE_BY_DIALOG_ID,
SEARCH_ENTITIES_BY_NAME,
SEARCH_ENTITIES_BY_NAME_OR_ALIAS,
SEARCH_MEMORY_SUMMARIES_BY_KEYWORD,
SEARCH_STATEMENTS_BY_CREATED_AT,
SEARCH_STATEMENTS_BY_KEYWORD,
@@ -264,7 +265,7 @@ async def search_graph(
if "entities" in include:
tasks.append(connector.execute_query(
SEARCH_ENTITIES_BY_NAME,
SEARCH_ENTITIES_BY_NAME_OR_ALIAS,
q=q,
end_user_id=end_user_id,
limit=limit,