diff --git a/backend/alembic/versions/012_schedule_fk_set_null.py b/backend/alembic/versions/012_schedule_fk_set_null.py new file mode 100644 index 0000000..2fb28dc --- /dev/null +++ b/backend/alembic/versions/012_schedule_fk_set_null.py @@ -0,0 +1,43 @@ +"""alter executions.schedule_id FK to ON DELETE SET NULL + +Revision ID: 012_schedule_fk_set_null +Revises: 011_add_agent_market_fields +Create Date: 2026-05-07 +""" +from alembic import op + +revision = "012_schedule_fk_set_null" +down_revision = "011_add_agent_market_fields" +branch_labels = None +depends_on = None + + +def upgrade() -> None: + """将 executions.schedule_id 外键改为 SET NULL,删除定时任务时自动保留执行记录。""" + try: + op.drop_constraint("executions_ibfk_3", "executions", type_="foreignkey") + except Exception: + # FK name may vary; try alternate patterns + try: + op.drop_constraint("executions_ibfk_4", "executions", type_="foreignkey") + except Exception: + pass + op.create_foreign_key( + "executions_ibfk_3", + "executions", "agent_schedules", + ["schedule_id"], ["id"], + ondelete="SET NULL", + ) + + +def downgrade() -> None: + try: + op.drop_constraint("executions_ibfk_3", "executions", type_="foreignkey") + except Exception: + pass + op.create_foreign_key( + "executions_ibfk_3", + "executions", "agent_schedules", + ["schedule_id"], ["id"], + ondelete="RESTRICT", + ) diff --git a/backend/app/services/builtin_tools.py b/backend/app/services/builtin_tools.py index bc49cc7..11cad43 100644 --- a/backend/app/services/builtin_tools.py +++ b/backend/app/services/builtin_tools.py @@ -1894,6 +1894,11 @@ async def schedule_delete_tool( }, ensure_ascii=False) name = schedule.name + # 先将引用此 schedule 的 execution 记录的 schedule_id 置空,避免外键约束 + from app.models.execution import Execution + db.query(Execution).filter(Execution.schedule_id == schedule_id).update( + {Execution.schedule_id: None}, synchronize_session=False + ) db.delete(schedule) db.commit() db.close() @@ -1915,6 +1920,12 @@ async def schedule_delete_tool( names = [s.name for s in schedules] count = len(schedules) + # 先解除所有 execution 记录的 schedule_id 外键引用 + from app.models.execution import Execution + sids = [s.id for s in schedules] + db.query(Execution).filter(Execution.schedule_id.in_(sids)).update( + {Execution.schedule_id: None}, synchronize_session=False + ) for s in schedules: db.delete(s) db.commit()