- 缘契域 domains/yuanqi:covenants(当前版)+ covenant_events(不可变历史);提议—问心—铭契—撤回全链;边界变更立即生效;多维关系并存,服务层拒绝 affinity/好感度单一键;浪漫维度成人资格标记位 - 道体铸魂与分灵 domains/jingsheng/soul.py:soul_forgings 确认流(锚点变更 pending→confirmed 应用并签发新魂籍版本,确认前人物锚点不变);分灵创建新人物(继承资料与谱系 lineage 链、不继承原身份,魂籍从 v1 起) - 河图域 domains/hetu:persistent_worlds 境界 + world_entities/world_relations 境界作用域重建 + 世谱挂接;境界正史视图(scope=persistent_world 归真集合);canonize 后置推进正史计数 - 三界分流:reality→息壤提取(不变);persistent_world→境界实录+正史推进;simulation→只记试演实录;factual_records 增 scope 列——境界/幻境实录与真界本相隔离 - 正影打通:引文 target_type=memory 时被引记忆 superseded → 返回 successor_memory_id 提示,不静默替换 - 前端:人物详情页缘契面板(提议维度选择/铭契/撤回/事件史)+ 世界页重建(境界目录/实体/正史概览)+ covenants/worlds api 库 - 退役:旧 personality/drive/evolution/world 模块、服务、端点、仓储、schema、前端五页与组件样式;迁移 007 退役九张旧表与 characters.current_personality_version_id - 验收(test_m6_domains 六用例):多维无单一好感度/铭契版本链/事件不可抹除/撤回立即生效/边界即时/铸魂确认流+魂籍版本/分灵谱系/境界正史不进真界(实录 scope 隔离+真界候选=0)/引文正影后继 - 四套件:后端 379、前端 199、BFF 4、神识 5 包全绿(2 个预存环境失败不变);迁移 007 净库+±1 干净
151 lines
4.1 KiB
Python
151 lines
4.1 KiB
Python
"""司命台治理对象数据访问(api-spec §19.4)。
|
|
|
|
目标 / 感知通道 / 世界实体 / 计划 / 天工阁角色的 CRUD 数据访问;
|
|
均为本地单用户全局对象,不挂 character_id。
|
|
"""
|
|
|
|
from sqlalchemy import func, select
|
|
from sqlalchemy.ext.asyncio import AsyncSession
|
|
|
|
from app.core.time import utc_now
|
|
from app.models.governance import (
|
|
AgentRosterEntry,
|
|
Goal,
|
|
PerceptionChannel,
|
|
Plan,
|
|
)
|
|
|
|
# —— 太一枢 · 目标 ——
|
|
|
|
|
|
async def list_goals(session: AsyncSession) -> list[Goal]:
|
|
result = await session.execute(
|
|
select(Goal).order_by(Goal.priority.desc(), Goal.updated_at.desc())
|
|
)
|
|
return list(result.scalars().all())
|
|
|
|
|
|
async def get_goal(session: AsyncSession, goal_id: str) -> Goal | None:
|
|
return await session.get(Goal, goal_id)
|
|
|
|
|
|
async def add_goal(session: AsyncSession, goal: Goal) -> Goal:
|
|
session.add(goal)
|
|
await session.flush()
|
|
return goal
|
|
|
|
|
|
async def list_goals_by_character(
|
|
session: AsyncSession,
|
|
character_id: str,
|
|
status_filter: str | None = None,
|
|
) -> list[Goal]:
|
|
"""按 character_id 获取目标列表;可选的 status 过滤。"""
|
|
stmt = select(Goal).where(Goal.character_id == character_id)
|
|
if status_filter:
|
|
stmt = stmt.where(Goal.status == status_filter)
|
|
stmt = stmt.order_by(Goal.priority.desc(), Goal.updated_at.desc())
|
|
result = await session.execute(stmt)
|
|
return list(result.scalars().all())
|
|
|
|
|
|
async def update_goal_progress(
|
|
session: AsyncSession,
|
|
goal_id: str,
|
|
progress: float,
|
|
) -> Goal | None:
|
|
"""更新目标的 progress 与 updated_at 字段。"""
|
|
goal = await session.get(Goal, goal_id)
|
|
if goal is None:
|
|
return None
|
|
goal.progress = progress
|
|
goal.updated_at = utc_now()
|
|
await session.flush()
|
|
return goal
|
|
|
|
|
|
async def list_active_session_goal(session: AsyncSession, character_id: str) -> Goal | None:
|
|
"""查找指定人物活跃的 session 级目标(各人物至多一个活跃 session 目标)。"""
|
|
result = await session.execute(
|
|
select(Goal)
|
|
.where(
|
|
Goal.character_id == character_id,
|
|
Goal.level == "session",
|
|
Goal.status == "active",
|
|
)
|
|
.limit(1)
|
|
)
|
|
return result.scalar_one_or_none()
|
|
|
|
|
|
# —— 烛照枢 · 感知通道 ——
|
|
|
|
|
|
async def list_perception_channels(session: AsyncSession) -> list[PerceptionChannel]:
|
|
result = await session.execute(select(PerceptionChannel).order_by(PerceptionChannel.channel))
|
|
return list(result.scalars().all())
|
|
|
|
|
|
async def get_perception_channel(session: AsyncSession, channel: str) -> PerceptionChannel | None:
|
|
return await session.get(PerceptionChannel, channel)
|
|
|
|
|
|
async def add_perception_channel(
|
|
session: AsyncSession, channel: PerceptionChannel
|
|
) -> PerceptionChannel:
|
|
session.add(channel)
|
|
await session.flush()
|
|
return channel
|
|
|
|
|
|
# —— 河图枢 · 世界实体 ——
|
|
|
|
|
|
|
|
|
|
|
|
async def list_plans(session: AsyncSession) -> list[Plan]:
|
|
result = await session.execute(select(Plan).order_by(Plan.updated_at.desc()))
|
|
return list(result.scalars().all())
|
|
|
|
|
|
async def get_plan(session: AsyncSession, plan_id: str) -> Plan | None:
|
|
return await session.get(Plan, plan_id)
|
|
|
|
|
|
async def add_plan(session: AsyncSession, plan: Plan) -> Plan:
|
|
session.add(plan)
|
|
await session.flush()
|
|
return plan
|
|
|
|
|
|
# —— 天工阁枢 · 角色在岗 ——
|
|
|
|
|
|
async def list_agent_roster(session: AsyncSession) -> list[AgentRosterEntry]:
|
|
result = await session.execute(select(AgentRosterEntry).order_by(AgentRosterEntry.role))
|
|
return list(result.scalars().all())
|
|
|
|
|
|
async def get_agent(session: AsyncSession, role: str) -> AgentRosterEntry | None:
|
|
return await session.get(AgentRosterEntry, role)
|
|
|
|
|
|
async def add_agent(session: AsyncSession, agent: AgentRosterEntry) -> AgentRosterEntry:
|
|
session.add(agent)
|
|
await session.flush()
|
|
return agent
|
|
|
|
|
|
# —— 百工枢 · 工具策略(§15/§16 共享)——
|
|
|
|
|
|
|
|
|
|
# —— 计数(供九枢健康度聚合)——
|
|
|
|
|
|
async def count(session: AsyncSession, model: type) -> int:
|
|
result = await session.execute(select(func.count()).select_from(model))
|
|
return int(result.scalar_one())
|