fix(ai): address issues from second code review

- Add STATUS_APPLIED and STATUS_DISMISSED constants to model
- Add markApplied() and markDismissed() methods
- Update event status after apply/dismiss actions (prevent duplicate ops)
- Validate related_task_id exists and user has permission
- Filter empty or overly long subtask names before creation

Generated with [Claude Code](https://claude.ai/code)
via [Happy](https://happy.engineering)

Co-Authored-By: Claude <noreply@anthropic.com>
Co-Authored-By: Happy <yesreply@happy.engineering>
This commit is contained in:
kuaifan 2026-01-21 03:12:52 +00:00
parent 75073d4320
commit 452af4bd2f
2 changed files with 41 additions and 0 deletions

View File

@ -3889,6 +3889,14 @@ class ProjectController extends AbstractController
case ProjectTaskAiEvent::EVENT_SUBTASKS:
// 创建子任务
$subtasks = $result['content'] ?? [];
// 过滤无效的子任务名称
$subtasks = array_filter(array_map(function ($name) {
$name = trim((string)$name);
return (empty($name) || mb_strlen($name) > 100) ? null : $name;
}, $subtasks));
if (empty($subtasks)) {
return Base::retError('没有有效的子任务名称');
}
// 检查子任务数量限制
$existingCount = ProjectTask::where('parent_id', $task->id)
->whereNull('deleted_at')
@ -3930,6 +3938,11 @@ class ProjectController extends AbstractController
if ($relatedTaskId <= 0) {
return Base::retError('请选择关联任务');
}
// 验证关联任务存在且有权限
$relatedTask = ProjectTask::userTask($relatedTaskId);
if (!$relatedTask) {
return Base::retError('关联任务不存在或无权限');
}
ProjectTaskRelation::firstOrCreate([
'task_id' => $task->id,
'related_task_id' => $relatedTaskId,
@ -3944,6 +3957,9 @@ class ProjectController extends AbstractController
return Base::retError('未知的建议类型');
}
// 标记事件为已采纳
$event->markApplied();
// 更新消息状态
if ($msgId > 0 && $task->dialog_id) {
AiTaskSuggestion::updateMessageStatus($msgId, $task->dialog_id, $type, 'applied');
@ -3996,6 +4012,9 @@ class ProjectController extends AbstractController
return Base::retError('建议不存在或已处理');
}
// 标记事件为已忽略
$event->markDismissed();
// 更新消息状态
if ($msgId > 0 && $task->dialog_id) {
AiTaskSuggestion::updateMessageStatus($msgId, $task->dialog_id, $type, 'dismissed');

View File

@ -31,6 +31,8 @@ class ProjectTaskAiEvent extends AbstractModel
const STATUS_COMPLETED = 'completed';
const STATUS_FAILED = 'failed';
const STATUS_SKIPPED = 'skipped';
const STATUS_APPLIED = 'applied';
const STATUS_DISMISSED = 'dismissed';
const MAX_RETRY = 3;
@ -129,4 +131,24 @@ class ProjectTaskAiEvent extends AbstractModel
return $this->status === self::STATUS_FAILED
&& $this->retry_count < self::MAX_RETRY;
}
/**
* 标记为已采纳
*/
public function markApplied(): bool
{
return $this->update([
'status' => self::STATUS_APPLIED,
]);
}
/**
* 标记为已忽略
*/
public function markDismissed(): bool
{
return $this->update([
'status' => self::STATUS_DISMISSED,
]);
}
}