6aea051c19
When a manual, desktop-owned collect task succeeds after clearing a human verification gate, requeue the automatic monitoring tasks that were previously skipped for the same tenant/business-date/platform/client with the human-verification skip reason. Restored tasks are reset to pending (clearing lease, callback, and completion fields), stamped with a recovery marker in their payload, and their brand projections are rebuilt after commit. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
100 lines
2.8 KiB
Go
100 lines
2.8 KiB
Go
package app
|
|
|
|
import (
|
|
"context"
|
|
"strings"
|
|
|
|
"github.com/jackc/pgx/v5"
|
|
|
|
"github.com/geo-platform/tenant-api/internal/shared/response"
|
|
)
|
|
|
|
func shouldRecoverHumanVerificationTasks(
|
|
task *monitoringCollectTask,
|
|
req MonitoringTaskResultRequest,
|
|
) bool {
|
|
return task != nil &&
|
|
task.CollectorType == monitoringCollectorType &&
|
|
strings.TrimSpace(task.ExecutionOwner) == "desktop_tasks" &&
|
|
strings.TrimSpace(task.TriggerSource) == "manual" &&
|
|
normalizeRunStatus(req.Status) == "succeeded"
|
|
}
|
|
|
|
func (s *MonitoringCallbackService) restoreHumanVerificationTasks(
|
|
ctx context.Context,
|
|
tx pgx.Tx,
|
|
task *monitoringCollectTask,
|
|
installationID string,
|
|
) ([]int64, error) {
|
|
installationID = strings.TrimSpace(installationID)
|
|
if tx == nil || task == nil || installationID == "" {
|
|
return nil, nil
|
|
}
|
|
|
|
rows, err := tx.Query(ctx, `
|
|
UPDATE monitoring_collect_tasks
|
|
SET status = 'pending',
|
|
lease_token_hash = NULL,
|
|
leased_to_executor = NULL,
|
|
leased_at = NULL,
|
|
lease_expires_at = NULL,
|
|
callback_received_at = NULL,
|
|
completed_at = NULL,
|
|
skip_reason = NULL,
|
|
error_message = NULL,
|
|
dispatch_after = NOW(),
|
|
last_dispatched_at = NULL,
|
|
request_payload_json = (
|
|
CASE
|
|
WHEN request_payload_json IS NULL OR jsonb_typeof(request_payload_json) <> 'object'
|
|
THEN '{}'::jsonb
|
|
ELSE request_payload_json
|
|
END
|
|
) || jsonb_build_object(
|
|
'human_verification_recovery', jsonb_build_object(
|
|
'recovered_at', NOW(),
|
|
'recovered_by_monitoring_task_id', $8::bigint
|
|
)
|
|
),
|
|
updated_at = NOW()
|
|
WHERE tenant_id = $1
|
|
AND business_date = $2::date
|
|
AND ai_platform_id = $3
|
|
AND collector_type = $4
|
|
AND COALESCE(execution_owner, 'legacy') = $5
|
|
AND trigger_source = 'automatic'
|
|
AND status = 'skipped'
|
|
AND skip_reason = $6
|
|
AND target_client_id::text = $7
|
|
RETURNING brand_id
|
|
`,
|
|
task.TenantID,
|
|
task.BusinessDate.Format("2006-01-02"),
|
|
task.AIPlatformID,
|
|
task.CollectorType,
|
|
"desktop_tasks",
|
|
monitoringPlatformHumanVerificationSkipReason,
|
|
installationID,
|
|
task.ID,
|
|
)
|
|
if err != nil {
|
|
return nil, response.ErrInternal(50041, "human_verification_recovery_failed", "failed to restore monitoring tasks canceled by human verification")
|
|
}
|
|
defer rows.Close()
|
|
|
|
brandIDs := make([]int64, 0)
|
|
for rows.Next() {
|
|
var brandID int64
|
|
if scanErr := rows.Scan(&brandID); scanErr != nil {
|
|
return nil, response.ErrInternal(50041, "human_verification_recovery_failed", "failed to parse restored monitoring tasks")
|
|
}
|
|
if brandID > 0 {
|
|
brandIDs = append(brandIDs, brandID)
|
|
}
|
|
}
|
|
if err := rows.Err(); err != nil {
|
|
return nil, response.ErrInternal(50041, "human_verification_recovery_failed", "failed to iterate restored monitoring tasks")
|
|
}
|
|
return uniqueSortedMonitoringProjectionBrandIDs(brandIDs), nil
|
|
}
|