Implement creator studio and upload updates
This commit is contained in:
89
app/Services/Moderation/ModerationClusterService.php
Normal file
89
app/Services/Moderation/ModerationClusterService.php
Normal file
@@ -0,0 +1,89 @@
|
||||
<?php
|
||||
|
||||
namespace App\Services\Moderation;
|
||||
|
||||
use App\Data\Moderation\ModerationResultData;
|
||||
use App\Models\ContentModerationCluster;
|
||||
use App\Models\ContentModerationFinding;
|
||||
|
||||
class ModerationClusterService
|
||||
{
|
||||
/**
|
||||
* @param array<string, mixed> $context
|
||||
* @param array<string, mixed> $suggestion
|
||||
* @return array{campaign_key:string,cluster_score:int,cluster_reason:string}
|
||||
*/
|
||||
public function classify(string $content, ModerationResultData $result, array $context = [], array $suggestion = []): array
|
||||
{
|
||||
$domains = array_values(array_filter($result->matchedDomains));
|
||||
$keywords = array_values(array_filter($result->matchedKeywords));
|
||||
$reason = 'normalized_content';
|
||||
|
||||
if ($domains !== [] && $keywords !== []) {
|
||||
$reason = 'domain_keyword_cta';
|
||||
$key = 'campaign:' . sha1(implode('|', [implode(',', array_slice($domains, 0, 3)), implode(',', array_slice($keywords, 0, 3))]));
|
||||
} elseif ($domains !== []) {
|
||||
$reason = 'domain_fingerprint';
|
||||
$key = 'campaign:' . sha1(implode(',', array_slice($domains, 0, 3)) . '|' . ($result->contentHashNormalized ?? $result->contentHash));
|
||||
} elseif (! empty($suggestion['campaign_tags'])) {
|
||||
$reason = 'suggested_cluster';
|
||||
$key = 'campaign:' . sha1(implode('|', (array) $suggestion['campaign_tags']));
|
||||
} else {
|
||||
$key = 'campaign:' . sha1((string) ($result->groupKey ?? $result->contentHashNormalized ?? $result->contentHash));
|
||||
}
|
||||
|
||||
$clusterScore = min(100, $result->score + (count($domains) * 8) + (count($keywords) * 4));
|
||||
|
||||
return [
|
||||
'campaign_key' => $key,
|
||||
'cluster_score' => $clusterScore,
|
||||
'cluster_reason' => $reason,
|
||||
];
|
||||
}
|
||||
|
||||
public function syncFinding(ContentModerationFinding $finding): void
|
||||
{
|
||||
if (! $finding->campaign_key) {
|
||||
return;
|
||||
}
|
||||
|
||||
$query = ContentModerationFinding::query()->where('campaign_key', $finding->campaign_key);
|
||||
$findings = $query->get(['id', 'user_id', 'matched_domains_json', 'matched_keywords_json', 'review_bucket', 'cluster_score', 'created_at']);
|
||||
|
||||
$domains = $findings
|
||||
->flatMap(static fn (ContentModerationFinding $item): array => (array) $item->matched_domains_json)
|
||||
->filter()
|
||||
->unique()
|
||||
->values();
|
||||
|
||||
$keywords = $findings
|
||||
->flatMap(static fn (ContentModerationFinding $item): array => (array) $item->matched_keywords_json)
|
||||
->filter()
|
||||
->unique()
|
||||
->take(8)
|
||||
->values();
|
||||
|
||||
ContentModerationCluster::query()->updateOrCreate(
|
||||
['campaign_key' => $finding->campaign_key],
|
||||
[
|
||||
'cluster_reason' => $finding->cluster_reason,
|
||||
'review_bucket' => $finding->review_bucket,
|
||||
'escalation_status' => $finding->escalation_status?->value ?? (string) $finding->escalation_status,
|
||||
'cluster_score' => (int) ($findings->max('cluster_score') ?? $finding->cluster_score ?? 0),
|
||||
'findings_count' => $findings->count(),
|
||||
'unique_users_count' => $findings->pluck('user_id')->filter()->unique()->count(),
|
||||
'unique_domains_count' => $domains->count(),
|
||||
'latest_finding_at' => $findings->max('created_at') ?: now(),
|
||||
'summary_json' => [
|
||||
'domains' => $domains->take(8)->all(),
|
||||
'keywords' => $keywords->all(),
|
||||
],
|
||||
],
|
||||
);
|
||||
|
||||
$clusterSize = $findings->count();
|
||||
if ($clusterSize > 1) {
|
||||
$query->update(['priority_score' => $finding->priority_score + min(25, ($clusterSize - 1) * 3)]);
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user