Files
geo/server/internal/tenant/app/monitoring_human_verification_recovery.go
T
root 6aea051c19 feat(monitoring): requeue automatic tasks after a manual human-verification run succeeds
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>
2026-07-22 23:57:28 +08:00

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
}