初始化提交
This commit is contained in:
commit
887c1f449e
963 files changed
+166869
No files matched your search
@@ -0,0 +1,143 @@
|
||||
"""内驱力审核服务(次生目标 approve → 自动创建太一目标)。
|
||||
|
||||
审核 approve 时自动派生 Goal(太一枢任务级目标),
|
||||
延后 / 驳回仅标记不变更目标树。
|
||||
"""
|
||||
|
||||
from app.core.errors import AppError
|
||||
from app.core.ids import new_goal_id
|
||||
from app.core.time import utc_now
|
||||
from app.db.session import AsyncSessionLocal
|
||||
from app.models.drive import CuriosityQuestion, ExploratorySubgoal
|
||||
from app.models.governance import Goal
|
||||
from app.repositories import drive as drive_repo
|
||||
from app.repositories.json_utils import loads_list
|
||||
from app.schemas.common import Page
|
||||
from app.schemas.drive import CuriosityResponse, SubgoalResponse
|
||||
from app.services.audit import record_audit_event
|
||||
|
||||
|
||||
async def list_curiosities(
|
||||
*,
|
||||
character_id: str,
|
||||
source: str | None = None,
|
||||
page: int = 1,
|
||||
page_size: int = 20,
|
||||
) -> Page[CuriosityResponse]:
|
||||
async with AsyncSessionLocal() as session:
|
||||
items, total = await drive_repo.list_curiosities(
|
||||
session, character_id, source=source, page=page, page_size=page_size
|
||||
)
|
||||
return Page(
|
||||
items=[_curiosity_to_response(i) for i in items],
|
||||
page=page,
|
||||
page_size=page_size,
|
||||
total=total,
|
||||
)
|
||||
|
||||
|
||||
async def list_subgoals(
|
||||
*,
|
||||
character_id: str,
|
||||
status: str | None = None,
|
||||
page: int = 1,
|
||||
page_size: int = 20,
|
||||
) -> Page[SubgoalResponse]:
|
||||
async with AsyncSessionLocal() as session:
|
||||
items, total = await drive_repo.list_subgoals(
|
||||
session, character_id, status=status, page=page, page_size=page_size
|
||||
)
|
||||
return Page(
|
||||
items=[_subgoal_to_response(i) for i in items],
|
||||
page=page,
|
||||
page_size=page_size,
|
||||
total=total,
|
||||
)
|
||||
|
||||
|
||||
async def approve_subgoal(subgoal_id: str) -> SubgoalResponse:
|
||||
return await _review_subgoal(subgoal_id, "approved", decision="auto", create_goal=True)
|
||||
|
||||
|
||||
async def defer_subgoal(subgoal_id: str) -> SubgoalResponse:
|
||||
return await _review_subgoal(subgoal_id, "deferred", decision="auto", create_goal=False)
|
||||
|
||||
|
||||
async def reject_subgoal(subgoal_id: str) -> SubgoalResponse:
|
||||
return await _review_subgoal(subgoal_id, "rejected", decision="denied", create_goal=False)
|
||||
|
||||
|
||||
async def _review_subgoal(
|
||||
subgoal_id: str, target_status: str, *, decision: str, create_goal: bool
|
||||
) -> SubgoalResponse:
|
||||
async with AsyncSessionLocal() as session:
|
||||
sub = await drive_repo.get_subgoal(session, subgoal_id)
|
||||
if sub is None:
|
||||
raise AppError("not_found", "探索性次生目标不存在。")
|
||||
if sub.status != "candidate":
|
||||
raise AppError("conflict", f"次生目标当前状态为 {sub.status},不可审核。")
|
||||
sub.status = target_status
|
||||
sub.reviewed_at = utc_now()
|
||||
sub.reviewed_by = "user"
|
||||
|
||||
# approve 时自动创建太一任务级目标
|
||||
if create_goal and sub.character_id:
|
||||
goal = Goal(
|
||||
id=new_goal_id(),
|
||||
character_id=sub.character_id,
|
||||
title=sub.title,
|
||||
description=sub.description or "由次生目标自动生成",
|
||||
level="task",
|
||||
type="exploration",
|
||||
status="active",
|
||||
)
|
||||
session.add(goal)
|
||||
|
||||
await record_audit_event(
|
||||
session,
|
||||
action=f"drive.subgoal.{target_status}",
|
||||
actor="user",
|
||||
risk_level=sub.risk_level,
|
||||
decision=decision,
|
||||
character_id=sub.character_id,
|
||||
input_summary=sub.title[:200],
|
||||
resource_type="exploratory_subgoal",
|
||||
resource_id=sub.id,
|
||||
)
|
||||
await session.commit()
|
||||
await session.refresh(sub)
|
||||
return _subgoal_to_response(sub)
|
||||
|
||||
|
||||
def _curiosity_to_response(c: CuriosityQuestion) -> CuriosityResponse:
|
||||
return CuriosityResponse(
|
||||
id=c.id,
|
||||
character_id=c.character_id,
|
||||
cycle_id=c.cycle_id,
|
||||
question=c.question,
|
||||
source=c.source,
|
||||
related_entities=loads_list(c.related_entities_json),
|
||||
expected_value=c.expected_value,
|
||||
urgency=c.urgency,
|
||||
risk_level=c.risk_level,
|
||||
created_at=c.created_at,
|
||||
)
|
||||
|
||||
|
||||
def _subgoal_to_response(s: ExploratorySubgoal) -> SubgoalResponse:
|
||||
return SubgoalResponse(
|
||||
id=s.id,
|
||||
character_id=s.character_id,
|
||||
cycle_id=s.cycle_id,
|
||||
parent_goal_id=s.parent_goal_id,
|
||||
curiosity_question_id=s.curiosity_question_id,
|
||||
title=s.title,
|
||||
description=s.description,
|
||||
expected_information_gain=s.expected_information_gain,
|
||||
interruption_cost=s.interruption_cost,
|
||||
risk_level=s.risk_level,
|
||||
status=s.status,
|
||||
reviewed_at=s.reviewed_at,
|
||||
reviewed_by=s.reviewed_by,
|
||||
created_at=s.created_at,
|
||||
)
|
||||
Reference in new issue
Block a user