feature:新增关系图谱显示功能,支持查看组织、角色、职业的所有关系连接。

This commit is contained in:
xiamuceer-j
2026-03-04 16:25:37 +08:00
parent ca75ec1a0a
commit cfbc32505e
3 changed files with 1256 additions and 293 deletions
+13 -11
View File
@@ -81,7 +81,7 @@ async def _build_relationships_summary(character_id: str, project_id: str, db: A
async def _build_org_members_summary(character_id: str, db: AsyncSession) -> str: async def _build_org_members_summary(character_id: str, db: AsyncSession) -> str:
"""从 organization_members 表构建组织成员摘要文本""" """从 organization_members 表构建组织成员JSON字符串(与schema契约保持一致)"""
# 先查找该角色对应的 Organization 记录 # 先查找该角色对应的 Organization 记录
org_result = await db.execute( org_result = await db.execute(
select(Organization).where(Organization.character_id == character_id) select(Organization).where(Organization.character_id == character_id)
@@ -89,30 +89,32 @@ async def _build_org_members_summary(character_id: str, db: AsyncSession) -> str
org = org_result.scalar_one_or_none() org = org_result.scalar_one_or_none()
if not org: if not org:
return "" return ""
# 查询该组织的所有成员 # 查询该组织的所有成员(按职级倒序,保证展示顺序稳定)
members_result = await db.execute( members_result = await db.execute(
select(OrganizationMember).where(OrganizationMember.organization_id == org.id) select(OrganizationMember)
.where(OrganizationMember.organization_id == org.id)
.order_by(OrganizationMember.rank.desc(), OrganizationMember.created_at)
) )
members = members_result.scalars().all() members = members_result.scalars().all()
if not members: if not members:
return "" return ""
# 批量查询成员角色名称 # 批量查询成员角色名称
member_char_ids = [m.character_id for m in members] member_char_ids = [m.character_id for m in members]
chars_result = await db.execute( chars_result = await db.execute(
select(Character.id, Character.name).where(Character.id.in_(member_char_ids)) select(Character.id, Character.name).where(Character.id.in_(member_char_ids))
) )
name_map = {row.id: row.name for row in chars_result} name_map = {row.id: row.name for row in chars_result}
# 构建摘要 # 返回 JSON 字符串数组,避免前端 JSON.parse 报错
parts = [] member_items = []
for m in members: for m in members:
name = name_map.get(m.character_id, "未知") name = name_map.get(m.character_id, "未知")
position = m.position or "成员" position = m.position or "成员"
parts.append(f"{name}{position}") member_items.append(f"{name}{position}")
return "".join(parts) return json.dumps(member_items, ensure_ascii=False)
@router.get("", response_model=CharacterListResponse, summary="获取角色列表") @router.get("", response_model=CharacterListResponse, summary="获取角色列表")
+30 -4
View File
@@ -108,14 +108,14 @@ async def get_relationship_graph(
for c in characters for c in characters
] ]
# 获取所有关系(边) # 获取所有角色关系(边)
rels_result = await db.execute( rels_result = await db.execute(
select(CharacterRelationship).where( select(CharacterRelationship).where(
CharacterRelationship.project_id == project_id CharacterRelationship.project_id == project_id
) )
) )
relationships = rels_result.scalars().all() relationships = rels_result.scalars().all()
links = [ links = [
RelationshipGraphLink( RelationshipGraphLink(
source=r.character_from_id, source=r.character_from_id,
@@ -126,8 +126,34 @@ async def get_relationship_graph(
) )
for r in relationships for r in relationships
] ]
logger.info(f"获取项目 {project_id} 的关系图谱:{len(nodes)} 个节点,{len(links)} 条关系") # 获取组织成员关系(组织 -> 成员)并追加到图谱边
# source 使用组织对应的角色IDOrganization.character_id),确保与节点ID一致
members_result = await db.execute(
select(OrganizationMember, Organization).join(
Organization,
OrganizationMember.organization_id == Organization.id
).where(Organization.project_id == project_id)
)
org_members = members_result.all()
member_links = [
RelationshipGraphLink(
source=org.character_id,
target=member.character_id,
relationship=f"组织成员·{member.position}",
intimacy=member.loyalty,
status=member.status
)
for member, org in org_members
]
links.extend(member_links)
logger.info(
f"获取项目 {project_id} 的关系图谱:{len(nodes)} 个节点,"
f"{len(relationships)} 条角色关系,{len(member_links)} 条组织成员关系"
)
return RelationshipGraphData(nodes=nodes, links=links) return RelationshipGraphData(nodes=nodes, links=links)
File diff suppressed because it is too large Load Diff