188 lines
7.4 KiB
Python
188 lines
7.4 KiB
Python
from __future__ import annotations
|
|
|
|
from .shared import *
|
|
from .helpers_resume import _queue_resume_work_item, _reject_work_item
|
|
from .schemas import PMODraftDecisionRequest, PMOResumeWorkItemRequest, PMOWorkItemDecisionRequest
|
|
from ...services.ai_pmo.draft_confirm_handler import DraftConfirmHandler
|
|
|
|
@router.post("/work-items/{work_item_id}/resume")
|
|
async def resume_work_item(
|
|
work_item_id: int,
|
|
request: PMOResumeWorkItemRequest,
|
|
current_user: User = Depends(get_current_user),
|
|
db: AsyncSession = Depends(get_db),
|
|
tenant_id: int = Depends(get_tenant_id),
|
|
):
|
|
target = (
|
|
await db.execute(
|
|
select(WorkItem).where(
|
|
and_(
|
|
WorkItem.tenant_id == tenant_id,
|
|
WorkItem.id == int(work_item_id),
|
|
)
|
|
).limit(1)
|
|
)
|
|
).scalar_one_or_none()
|
|
if target is None:
|
|
raise HTTPException(status_code=404, detail="work_item_not_found")
|
|
action_text_map = {
|
|
"continue": "继续处理",
|
|
"commit": "确认并提交候选",
|
|
"refresh": "换一批候选",
|
|
"cancel": "取消本次候选",
|
|
}
|
|
action_message = action_text_map.get(str(request.action or "").strip().lower(), "继续处理")
|
|
service = AIPMOKernelService(db, tenant_id=tenant_id)
|
|
raw_result = await service.process_ingress(
|
|
source_system="ai_pmo_ui",
|
|
source_message_id=request.source_message_id or f"resume-{int(work_item_id)}-{int(datetime.now().timestamp() * 1000)}",
|
|
message_text=f"{action_message} work_item #{int(work_item_id)}",
|
|
sender=str(current_user.name or current_user.username or "PMFlow用户"),
|
|
channel_ref="ai_pmo_ui",
|
|
source_sender_open_id=None,
|
|
captured_at=None,
|
|
chat_type="manual",
|
|
project_hint_id=request.project_id or _safe_int(target.project_id),
|
|
)
|
|
result = _normalize_resume_result(raw=raw_result, target_work_item=target)
|
|
return success_response(result)
|
|
|
|
@router.post("/work-items/{work_item_id}/decision")
|
|
async def decide_work_item(
|
|
work_item_id: int,
|
|
request: PMOWorkItemDecisionRequest,
|
|
current_user: User = Depends(get_current_user),
|
|
db: AsyncSession = Depends(get_db),
|
|
tenant_id: int = Depends(get_tenant_id),
|
|
):
|
|
target = (
|
|
await db.execute(
|
|
select(WorkItem).where(
|
|
and_(
|
|
WorkItem.tenant_id == tenant_id,
|
|
WorkItem.id == int(work_item_id),
|
|
)
|
|
).limit(1)
|
|
)
|
|
).scalar_one_or_none()
|
|
if target is None:
|
|
raise HTTPException(status_code=404, detail="work_item_not_found")
|
|
|
|
if request.action == "reject":
|
|
result = await _reject_work_item(
|
|
db=db,
|
|
tenant_id=tenant_id,
|
|
target=target,
|
|
current_user=current_user,
|
|
reason=request.reason,
|
|
)
|
|
return success_response(result)
|
|
|
|
if str(target.state or "").strip() == "waiting_manual" and request.project_id is not None:
|
|
target.project_id = int(request.project_id)
|
|
target.state = "executing"
|
|
target.reply_template_key = "action_processing"
|
|
current_execution = target.execution_result if isinstance(target.execution_result, dict) else {}
|
|
target.execution_result = {
|
|
**current_execution,
|
|
"status": "executing",
|
|
"manual_decision": "approve",
|
|
"resume_trigger": "queued_background",
|
|
"project_id": int(request.project_id),
|
|
"queued_at": datetime.now().isoformat(),
|
|
}
|
|
decision_row = (
|
|
await db.execute(
|
|
select(DecisionRecord)
|
|
.where(
|
|
and_(
|
|
DecisionRecord.tenant_id == tenant_id,
|
|
DecisionRecord.work_item_id == int(target.id),
|
|
)
|
|
)
|
|
.order_by(DecisionRecord.id.desc())
|
|
.limit(1)
|
|
)
|
|
).scalar_one_or_none()
|
|
if decision_row is not None and str(decision_row.policy_gate_code or "").strip() == "missing_project_scope":
|
|
decision_row.policy_gate_hit = False
|
|
decision_row.policy_gate_code = None
|
|
decision_row.approval_requirement = {
|
|
"required": False,
|
|
"reason": "project_scope_confirmed",
|
|
"message": "项目归属已确认,正在后台继续执行。",
|
|
}
|
|
decision_row.final_outcome = "executing"
|
|
await db.commit()
|
|
_queue_resume_work_item(
|
|
tenant_id=tenant_id,
|
|
work_item_id=int(work_item_id),
|
|
source_message_id=request.source_message_id,
|
|
sender=str(current_user.name or current_user.username or "PMFlow用户"),
|
|
project_id=int(request.project_id),
|
|
)
|
|
return success_response(
|
|
{
|
|
"handled": True,
|
|
"status": "pending_manual",
|
|
"followup_reply": "已确认关联项目,正在后台继续执行。",
|
|
"target_work_item_id": int(target.id),
|
|
"target_state": str(target.state or "").strip() or "executing",
|
|
"target_intent": str(target.intent or "").strip() or None,
|
|
"project_id": int(request.project_id),
|
|
"signal_id": _safe_int(target.signal_id),
|
|
"auto_tasks": [],
|
|
}
|
|
)
|
|
|
|
service = AIPMOKernelService(db, tenant_id=tenant_id)
|
|
raw_result = await service.process_ingress(
|
|
source_system="ai_pmo_ui",
|
|
source_message_id=request.source_message_id or f"decision-approve-{int(work_item_id)}-{int(datetime.now().timestamp() * 1000)}",
|
|
message_text=f"继续处理 work_item #{int(work_item_id)}",
|
|
sender=str(current_user.name or current_user.username or "PMFlow用户"),
|
|
channel_ref="ai_pmo_ui",
|
|
source_sender_open_id=None,
|
|
captured_at=None,
|
|
chat_type="manual",
|
|
project_hint_id=request.project_id or _safe_int(target.project_id),
|
|
)
|
|
result = _normalize_resume_result(raw=raw_result, target_work_item=target)
|
|
return success_response(result)
|
|
|
|
|
|
@router.post("/work-items/{work_item_id}/draft-decision")
|
|
async def decide_draft_work_item(
|
|
work_item_id: int,
|
|
request: PMODraftDecisionRequest,
|
|
current_user: User = Depends(get_current_user),
|
|
db: AsyncSession = Depends(get_db),
|
|
tenant_id: int = Depends(get_tenant_id),
|
|
):
|
|
target = (
|
|
await db.execute(
|
|
select(WorkItem).where(
|
|
and_(
|
|
WorkItem.tenant_id == tenant_id,
|
|
WorkItem.id == int(work_item_id),
|
|
)
|
|
).limit(1)
|
|
)
|
|
).scalar_one_or_none()
|
|
if target is None:
|
|
raise HTTPException(status_code=404, detail="work_item_not_found")
|
|
|
|
handler = DraftConfirmHandler(AIPMOKernelService(db, tenant_id=tenant_id))
|
|
result = await handler.handle_api_decision(
|
|
work_item_id=int(work_item_id),
|
|
action=str(request.action or "").strip().lower(),
|
|
operator_sender=str(current_user.name or current_user.username or "PMFlow用户"),
|
|
source_message_id=request.source_message_id,
|
|
revision_note=request.revision_note,
|
|
draft_version=request.draft_version,
|
|
)
|
|
await db.commit()
|
|
if not bool(result.get("handled")):
|
|
raise HTTPException(status_code=400, detail=str(result.get("followup_reply") or "draft_decision_failed"))
|
|
return success_response(result)
|