You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

67 lines
3.4 KiB

<?php
namespace App\Services\ContentSecurity;
use App\Models\Upload;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Log;
use InvalidArgumentException;
class AvatarCheck
{
public function submit($user, Upload $upload): array
{
if (!$user->openid) throw new InvalidArgumentException('请重新进入小程序后再试');
$id = DB::table('content_security_checks')->insertGetId([
'user_id' => $user->id, 'upload_id' => $upload->id, 'url' => $upload->url,
'status' => 'pending', 'created_at' => now(), 'updated_at' => now(),
]);
try {
// The SDK convenience method uses v1; explicitly send all v2 parameters.
$result = miniProgramApp()->content_security->httpPostJson('wxa/media_check_async', [
'media_url' => $upload->url, 'media_type' => 2, 'version' => 2,
'scene' => 1, 'openid' => $user->openid,
]);
if ((int) ($result['errcode'] ?? -1) !== 0 || empty($result['trace_id'])) throw new \RuntimeException('WeChat media check submission failed');
DB::table('content_security_checks')->where('id', $id)->update(['trace_id' => $result['trace_id'], 'updated_at' => now()]);
} catch (\Throwable $e) {
DB::table('content_security_checks')->where('id', $id)->update(['status' => 'error', 'updated_at' => now()]);
Log::warning('Avatar check submission failed', ['check_id' => $id, 'exception' => get_class($e)]);
throw new InvalidArgumentException('内容检测暂不可用,请稍后重新上传');
}
return ['check_id' => $id, 'status' => 'pending'];
}
public function result(int $userId, int $id): array
{
$row = DB::table('content_security_checks')->where('id', $id)->where('user_id', $userId)->first();
if (!$row) throw new InvalidArgumentException('检测记录不存在');
$status = $row->status;
if ($status === 'pending' && strtotime($row->created_at) < time() - 600) $status = 'error';
return ['check_id' => $row->id, 'status' => $status, 'url' => $status === 'pass' ? $row->url : null];
}
public function receive(array $message): void
{
if (($message['Event'] ?? '') !== 'wxa_media_check') return;
$trace = $message['trace_id'] ?? '';
if (!$trace) return;
if (!DB::table('content_security_checks')->where('trace_id', $trace)->exists()) {
// A callback may arrive before the submission response is stored; request a retry.
throw new \RuntimeException('Unknown media check trace');
}
$suggest = $message['result']['suggest'] ?? '';
$error = (int) ($message['errcode'] ?? -1);
$status = $error !== 0 ? 'error' : ($suggest === 'pass' ? 'pass' : 'reject');
// Only a signed callback can reach here. Duplicates cannot overwrite a final decision.
DB::table('content_security_checks')->where('trace_id', $trace)->where('status', 'pending')
->update(['status' => $status, 'suggest' => $suggest, 'error_code' => $error, 'updated_at' => now()]);
}
public function assertApproved(int $userId, string $url): void
{
$approved = DB::table('content_security_checks')->where('user_id', $userId)->where('url', $url)
->where('status', 'pass')->exists();
if (!$approved) throw new InvalidArgumentException('头像尚未通过内容检测,请重新上传并等待检测通过');
}
}