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>
This commit is contained in:
2026-07-22 23:57:28 +08:00
parent fc7495aba0
commit 6aea051c19
3 changed files with 196 additions and 2 deletions
@@ -1332,7 +1332,7 @@ func (s *MonitoringCallbackService) ProcessQueuedTaskResult(ctx context.Context,
return nil, nil
}
return s.processTaskResult(ctx, task, envelope.CallbackResult)
return s.processTaskResult(ctx, task, envelope.CallbackResult, envelope.InstallationID)
}
func (s *MonitoringCallbackService) authenticateInstallation(ctx context.Context, installationID string, installationToken string) (*monitoringInstallationIdentity, error) {
@@ -2327,7 +2327,12 @@ func (s *MonitoringCallbackService) upsertParseResult(ctx context.Context, tx pg
return nil
}
func (s *MonitoringCallbackService) processTaskResult(ctx context.Context, task *monitoringCollectTask, req MonitoringTaskResultRequest) (*MonitoringTaskResultResponse, error) {
func (s *MonitoringCallbackService) processTaskResult(
ctx context.Context,
task *monitoringCollectTask,
req MonitoringTaskResultRequest,
installationID string,
) (*MonitoringTaskResultResponse, error) {
if task == nil || strings.TrimSpace(task.QuestionText) == "" {
return nil, response.ErrInternal(50041, "missing_question_text_snapshot", "monitoring task question text is empty")
}
@@ -2421,6 +2426,14 @@ func (s *MonitoringCallbackService) processTaskResult(ctx context.Context, task
}
}
recoveredBrandIDs := []int64(nil)
if shouldRecoverHumanVerificationTasks(task, req) {
recoveredBrandIDs, err = s.restoreHumanVerificationTasks(ctx, tx, task, installationID)
if err != nil {
return nil, err
}
}
if err := s.rebuildBrandDailyProjection(ctx, tx, task.TenantID, task.BrandID, task.BusinessDate); err != nil {
return nil, err
}
@@ -2429,6 +2442,19 @@ func (s *MonitoringCallbackService) processTaskResult(ctx context.Context, task
return nil, response.ErrInternal(50041, "commit_failed", "failed to commit monitoring result ingest")
}
recoveredProjectionBrandIDs := make(map[int64]struct{}, len(recoveredBrandIDs))
for _, brandID := range recoveredBrandIDs {
if brandID > 0 && brandID != task.BrandID {
recoveredProjectionBrandIDs[brandID] = struct{}{}
}
}
s.dispatchMonitoringProjectionRebuilds(
task.TenantID,
task.BusinessDate,
recoveredProjectionBrandIDs,
"human_verification_recovery",
)
var runIDPtr *int64
if !failedOutcome.Requeue {
runIDPtr = &runID