5.7 KiB
		
	
	
	
	
	
	
	
			
		
		
	
	Why
关键的系统预定义计划(如定时数据采集)应该具备高度的韧性。无论它们之前因为何种原因(例如,执行失败、手动停止、应用异常崩溃)而处于非活动状态,在应用下一次启动时,都应该被自动恢复为“启用”状态,以确保核心功能的持续、可靠运行。
当前的启动逻辑无法将在“失败”或“停止”状态的系统计划自动拉起,需要手动干预,这降低了系统的鲁棒性。
What Changes
本次变更的核心是对 PlanRepository 进行一次重要的重构,以消除 UpdatePlan 函数的设计缺陷,并在此基础上,实现系统任务的启动自愈功能。
- 
重构
PlanRepository:- 将行为不符合名称预期的 
UpdatePlan函数重命名为UpdatePlanMetadataAndStructure。 - 创建一个全新的、行为正确的 
UpdatePlan函数,确保全字段更新。 
 - 将行为不符合名称预期的 
 - 
修正调用点:
- 更新 
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 字段。
// reconcilePlanNode in plan_repository.go
if err := tx.Model(plan).Select("Name", "Description", "ExecutionType", "ExecuteNum", "CronExpression", "ContentType").Updates(plan).Error; err != nil {
    return err
}
这导致了以下问题:
- 名不副实:函数名 
UpdatePlan暗示了一个完整的、全字段的更新操作,但其行为却是一个不包含状态(Status)和执行计数(ExecuteCount)等运行时信息的“部分更新”。 - 行为不符合预期:任何调用此函数并期望更新 
Status字段的上层业务逻辑,都会静默失败(即代码不报错,但数据未更新),这极易引发难以追踪的 Bug。 - 潜在风险:直接修改这个函数以包含 
Status字段是危险的。因为我们无法确定项目中其他调用方是否依赖于它“不更新状态”这一隐性行为。任何草率的修改都可能破坏现有功能。 
2. 重构方案:明确职责,消除歧义
为了从根本上解决此问题,并保证系统的健壮性和可维护性,我们决定对 PlanRepository 接口及其实现进行重构。
核心思想是让函数的名称和行为完全匹配。
2.1. 接口与实现变更
我们将对 plan_repository.go 进行以下修改:
- 
重命名旧函数:将现有的、有问题的
UpdatePlan函数重命名为UpdatePlanMetadataAndStructure。这个新名字精确地描述了它的实际行为:只更新计划的元数据和结构(任务或子计划),而不触及运行时状态。 - 
创建新函数:创建一个新的、名为
UpdatePlan的函数。这个新函数将提供一个符合开发者直觉的、真正的“全字段更新”功能。它将使用 GORM 的Save方法来实现,确保模型对象中的所有字段都被持久化到数据库。// 新的、正确的 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 的调用点,根据其业务意图,将其指向正确的函数:
- 
internal/core/data_initializer.go:- 业务意图:在应用启动时,用预定义模板更新系统计划的元数据,并强制重置其状态为“启用”。
 - 修正方案:此处的调用应该指向 
UpdatePlanMetadataAndStructure,以确保只更新元数据。后续再通过调用专用的UpdatePlanStatus方法来安全地重置状态。这完美地分离了两个不同的关注点。 
 - 
internal/app/service/plan_service.go:- 业务意图:提供一个供 API 使用的公共服务,允许用户完整地更新一个计划的所有信息。
 - 修正方案:此处的调用应该指向新的 
UpdatePlan函数。由于函数名没有改变,此文件中的代码无需修改,但在逻辑上它已经从调用一个有问题的旧函数,变成了调用一个行为正确的新函数。 
 
3. 结论
这个重构方案不仅解决了最初的“系统任务自愈”功能实现障碍,更重要的是,它消除了一处严重的设计隐患,提升了代码库的整体质量和可维护性。
通过让函数名副其实,我们为未来的开发和维护工作扫清了障碍,降低了出现类似 Bug 的风险。