## Why

关键的系统预定义计划（如定时数据采集）应该具备高度的韧性。无论它们之前因为何种原因（例如，执行失败、手动停止、应用异常崩溃）而处于非活动状态，在应用下一次启动时，都应该被自动恢复为“启用”状态，以确保核心功能的持续、可靠运行。

当前的启动逻辑无法将在“失败”或“停止”状态的系统计划自动拉起，需要手动干预，这降低了系统的鲁棒性。

## What Changes

本次变更的核心是对 `PlanRepository` 进行一次重要的重构，以消除 `UpdatePlan` 函数的设计缺陷，并在此基础上，实现系统任务的启动自愈功能。

1.  **重构 `PlanRepository`**:
    - 将行为不符合名称预期的 `UpdatePlan` 函数重命名为 `UpdatePlanMetadataAndStructure`。
    - 创建一个全新的、行为正确的 `UpdatePlan` 函数，确保全字段更新。

2.  **修正调用点**:
    - 更新 `data_initializer.go` 中的调用，使其调用重命名后的 `UpdatePlanMetadataAndStructure`，并辅以 `UpdatePlanStatus` 来实现安全的自愈逻辑。
    - 确认 `plan_service.go` 中的调用会自然地指向新的、正确的 `UpdatePlan` 函数。

## Impact

- **Affected Specs**: `design.md` (已合并入本文档) 详细描述了重构的设计与决策。
- **Affected Code**: 
  - `internal/infra/repository/plan_repository.go`: 接口和实现将被重构。
  - `internal/core/data_initializer.go`: 调用点将被修正。
  - `internal/app/service/plan_service.go`: 调用点将隐式地指向新实现。

---

## Design Document: `UpdatePlan` Refactoring

### 1. 问题背景与根源分析

在实现“系统任务启动自愈”功能时，我们发现了一个深层次的存储库（Repository）层设计问题。

最初的尝试（在 `data_initializer.go` 中移除状态同步代码）失败了。经过深入调查，根源在于 `plan_repository.go` 中的 `UpdatePlan` 函数实现存在歧义，其行为与函数名不符。

#### `UpdatePlan` 的问题

该函数的内部实现 `reconcilePlanNode` 使用了 GORM 的 `Select(...)` 方法来指定要更新的数据库字段。然而，这个字段列表中**只包含了计划的元数据**（如 `Name`, `Description`, `CronExpression` 等），**并未包含 `Status` 字段**。

```go
// reconcilePlanNode in plan_repository.go
if err := tx.Model(plan).Select("Name", "Description", "ExecutionType", "ExecuteNum", "CronExpression", "ContentType").Updates(plan).Error; err != nil {
    return err
}
```

这导致了以下问题：

1.  **名不副实**：函数名 `UpdatePlan` 暗示了一个完整的、全字段的更新操作，但其行为却是一个不包含状态（`Status`）和执行计数（`ExecuteCount`）等运行时信息的“部分更新”。
2.  **行为不符合预期**：任何调用此函数并期望更新 `Status` 字段的上层业务逻辑，都会静默失败（即代码不报错，但数据未更新），这极易引发难以追踪的 Bug。
3.  **潜在风险**：直接修改这个函数以包含 `Status` 字段是危险的。因为我们无法确定项目中其他调用方是否依赖于它“不更新状态”这一隐性行为。任何草率的修改都可能破坏现有功能。

### 2. 重构方案：明确职责，消除歧义

为了从根本上解决此问题，并保证系统的健壮性和可维护性，我们决定对 `PlanRepository` 接口及其实现进行重构。

核心思想是**让函数的名称和行为完全匹配**。

#### 2.1. 接口与实现变更

我们将对 `plan_repository.go` 进行以下修改：

1.  **重命名旧函数**：将现有的、有问题的 `UpdatePlan` 函数重命名为 `UpdatePlanMetadataAndStructure`。这个新名字精确地描述了它的实际行为：只更新计划的元数据和结构（任务或子计划），而不触及运行时状态。

2.  **创建新函数**：创建一个新的、名为 `UpdatePlan` 的函数。这个新函数将提供一个符合开发者直觉的、真正的“全字段更新”功能。它将使用 GORM 的 `Save` 方法来实现，确保模型对象中的所有字段都被持久化到数据库。

    ```go
    // 新的、正确的 UpdatePlan 实现
    func (r *gormPlanRepository) UpdatePlan(plan *models.Plan) error {
        return r.db.Transaction(func(tx *gorm.DB) error {
            return tx.Save(plan).Error
        })
    }
    ```

#### 2.2. 调用点修正

在完成上述重构后，我们需要审查并修正所有原 `UpdatePlan` 的调用点，根据其业务意图，将其指向正确的函数：

1.  **`internal/core/data_initializer.go`**:
    *   **业务意图**：在应用启动时，用预定义模板更新系统计划的元数据，并强制重置其状态为“启用”。
    *   **修正方案**：此处的调用应该指向 `UpdatePlanMetadataAndStructure`，以确保只更新元数据。后续再通过调用专用的 `UpdatePlanStatus` 方法来安全地重置状态。这完美地分离了两个不同的关注点。

2.  **`internal/app/service/plan_service.go`**:
    *   **业务意图**：提供一个供 API 使用的公共服务，允许用户完整地更新一个计划的所有信息。
    *   **修正方案**：此处的调用应该指向新的 `UpdatePlan` 函数。由于函数名没有改变，此文件中的代码**无需修改**，但在逻辑上它已经从调用一个有问题的旧函数，变成了调用一个行为正确的新函数。

### 3. 结论

这个重构方案不仅解决了最初的“系统任务自愈”功能实现障碍，更重要的是，它消除了一处严重的设计隐患，提升了代码库的整体质量和可维护性。

通过让函数名副其实，我们为未来的开发和维护工作扫清了障碍，降低了出现类似 Bug 的风险。
