refactor: make subscription lifecycle events durable

This commit is contained in:
jxxghp
2026-08-22 07:43:41 +08:00
parent 8f94fd620d
commit c5de1c7b1b
27 changed files with 1106 additions and 230 deletions
+20 -27
View File
@@ -6,11 +6,15 @@ from pydantic import BaseModel, Field
from app.agent.tools.base import MoviePilotTool
from app.agent.tools.tags import ToolTag
from app.runtime.events import eventmanager
from app.application.agentdata import SubscribePort as SubscribeOper
from app.adapters.external.server import MoviePilotServerHelper
from app.application.subscription.delete import (
SubscribeDeletionActor,
get_delete_subscribe_scope,
)
from app.application.subscription.mutation import (
SubscriptionActor,
get_subscription_mutation_scope,
)
from app.runtime.log import logger
from app.schemas.types import EventType
class DeleteSubscribeInput(BaseModel):
@@ -45,32 +49,21 @@ class DeleteSubscribeTool(MoviePilotTool):
logger.info(f"执行工具: {self.name}, 参数: subscribe_id={subscribe_id}")
try:
subscribe_oper = SubscribeOper()
# 获取订阅信息
subscribe = await subscribe_oper.async_get(subscribe_id)
async with get_subscription_mutation_scope() as mutation:
subscribe = await mutation.get_accessible(
subscribe_id,
SubscriptionActor(name="agent", is_superuser=True),
)
if not subscribe:
return f"订阅 ID {subscribe_id} 不存在"
# 在删除之前获取订阅信息(用于事件)
subscribe_info = subscribe.to_dict()
await subscribe_oper.async_delete(subscribe_id)
# 分享订阅统计刷新本身已异步化,这里只需要在删除后触发即可。
MoviePilotServerHelper.sub_done_async(
{
"media_source": subscribe.media_source,
"media_id": subscribe.media_id,
"music_type": subscribe.music_type,
"total_tracks": subscribe.total_tracks,
"season": subscribe.season,
}
)
# 发送事件
await eventmanager.async_send_event(
EventType.SubscribeDeleted,
{"subscribe_id": subscribe_id, "subscribe_info": subscribe_info},
)
async with get_delete_subscribe_scope() as command:
deleted = await command.execute(
subscribe_id,
SubscribeDeletionActor(username="agent", is_superuser=True),
)
if not deleted:
return f"订阅 ID {subscribe_id} 不存在"
return f"成功删除订阅:{subscribe.name} ({subscribe.year})"
except Exception as e:
+39 -43
View File
@@ -7,11 +7,12 @@ from pydantic import BaseModel, Field
from app.agent.tools.base import MoviePilotTool
from app.agent.tools.tags import ToolTag
from app.runtime.events import eventmanager
from app.application.agentdata import SubscribePort as SubscribeOper
from app.application.subscription.mutation import (
SubscriptionActor,
get_subscription_mutation_scope,
)
from app.runtime.log import logger
from app.schemas.event import SubscribeModifiedEventData
from app.schemas.types import EventType, media_type_to_agent
from app.schemas.types import media_type_to_agent
class UpdateSubscribeInput(BaseModel):
@@ -172,8 +173,9 @@ class UpdateSubscribeTool(MoviePilotTool):
logger.info(f"执行工具: {self.name}, 参数: subscribe_id={subscribe_id}")
try:
subscribe_oper = SubscribeOper()
subscribe = await subscribe_oper.async_get(subscribe_id)
actor = SubscriptionActor(name="agent", is_superuser=True)
async with get_subscription_mutation_scope() as mutation:
subscribe = await mutation.get_accessible(subscribe_id, actor)
if not subscribe:
return json.dumps(
{"success": False, "message": f"订阅不存在: {subscribe_id}"},
@@ -206,9 +208,6 @@ class UpdateSubscribeTool(MoviePilotTool):
ensure_ascii=False,
)
# 保存旧数据用于事件
old_subscribe_dict = subscribe.to_dict()
# 构建更新字典
subscribe_dict = {}
@@ -306,24 +305,21 @@ class UpdateSubscribeTool(MoviePilotTool):
ensure_ascii=False,
)
# 更新订阅
await subscribe_oper.async_update(subscribe_id, subscribe_dict)
# 重新获取更新后的订阅数据
updated_subscribe = await subscribe_oper.async_get(subscribe_id)
# 发送订阅调整事件
await eventmanager.async_send_event(
EventType.SubscribeModified,
SubscribeModifiedEventData(
subscribe_id=subscribe_id,
old_subscribe_info=old_subscribe_dict,
subscribe_info=updated_subscribe.to_dict()
if updated_subscribe
else {},
# Agent 工具没有 FastAPI 请求会话,由组合根提供一次独占事务作用域;
# 更新和 durable intent 必须共享同一 AsyncSession。
async with get_subscription_mutation_scope() as mutation:
change = await mutation.update(
subscribe_id,
subscribe_dict,
actor,
scene="agent_update",
).to_dict(),
)
)
if not change:
return json.dumps(
{"success": False, "message": f"订阅不存在: {subscribe_id}"},
ensure_ascii=False,
)
updated_subscribe = change.new
# 构建返回结果
result = {
@@ -335,23 +331,23 @@ class UpdateSubscribeTool(MoviePilotTool):
if updated_subscribe:
result["subscribe"] = {
"id": updated_subscribe.id,
"name": updated_subscribe.name,
"year": updated_subscribe.year,
"type": media_type_to_agent(updated_subscribe.type),
"music_type": updated_subscribe.music_type,
"total_tracks": updated_subscribe.total_tracks,
"media_source": updated_subscribe.media_source,
"media_id": updated_subscribe.media_id,
"season": updated_subscribe.season,
"state": updated_subscribe.state,
"total_episode": updated_subscribe.total_episode,
"manual_total_episode": updated_subscribe.manual_total_episode,
"lack_episode": updated_subscribe.lack_episode,
"start_episode": updated_subscribe.start_episode,
"quality": updated_subscribe.quality,
"resolution": updated_subscribe.resolution,
"effect": updated_subscribe.effect,
"id": updated_subscribe.get("id"),
"name": updated_subscribe.get("name"),
"year": updated_subscribe.get("year"),
"type": media_type_to_agent(updated_subscribe.get("type")),
"music_type": updated_subscribe.get("music_type"),
"total_tracks": updated_subscribe.get("total_tracks"),
"media_source": updated_subscribe.get("media_source"),
"media_id": updated_subscribe.get("media_id"),
"season": updated_subscribe.get("season"),
"state": updated_subscribe.get("state"),
"total_episode": updated_subscribe.get("total_episode"),
"manual_total_episode": updated_subscribe.get("manual_total_episode"),
"lack_episode": updated_subscribe.get("lack_episode"),
"start_episode": updated_subscribe.get("start_episode"),
"quality": updated_subscribe.get("quality"),
"resolution": updated_subscribe.get("resolution"),
"effect": updated_subscribe.get("effect"),
}
return json.dumps(result, ensure_ascii=False, indent=2)