- 新增 MediaArchiveJob、MediaOssArchiveService、WechatMediaArchiveService 与回填命令 - Message/DataProcessing 等支持归档调度与持久化下载 URL - WebSocket 控制器整理;朋友圈与文档/定时任务说明更新 Made-with: Cursor
373 lines
12 KiB
PHP
373 lines
12 KiB
PHP
<?php
|
|
|
|
namespace app\common\service;
|
|
|
|
use think\Db;
|
|
use think\facade\Log;
|
|
|
|
class WechatMediaArchiveService
|
|
{
|
|
public static function archiveMessageById($messageId)
|
|
{
|
|
$messageId = (int)$messageId;
|
|
if ($messageId <= 0) {
|
|
return false;
|
|
}
|
|
|
|
$row = Db::table('s2_wechat_message')->where('id', $messageId)->find();
|
|
if (empty($row)) {
|
|
return false;
|
|
}
|
|
|
|
$update = self::buildMessageArchiveUpdate($row);
|
|
if (empty($update)) {
|
|
return false;
|
|
}
|
|
|
|
Db::table('s2_wechat_message')->where('id', $messageId)->update($update);
|
|
return true;
|
|
}
|
|
|
|
public static function archiveMomentById($momentId)
|
|
{
|
|
$momentId = (int)$momentId;
|
|
if ($momentId <= 0) {
|
|
return false;
|
|
}
|
|
|
|
$row = Db::table('s2_wechat_moments')->where('id', $momentId)->find();
|
|
if (empty($row)) {
|
|
return false;
|
|
}
|
|
|
|
$displayUrls = self::decodeJsonArray($row['resUrls'] ?? '');
|
|
$rawUrls = self::decodeJsonArray($row['urls'] ?? '');
|
|
$candidateUrls = !empty($displayUrls) ? $displayUrls : $rawUrls;
|
|
if (empty($candidateUrls)) {
|
|
return false;
|
|
}
|
|
|
|
$ossUrls = [];
|
|
foreach ($candidateUrls as $index => $url) {
|
|
if (!MediaOssArchiveService::isRemoteHttpUrl($url)) {
|
|
continue;
|
|
}
|
|
|
|
$resourceType = self::isVideoUrl($url) ? 'video' : 'image';
|
|
$result = MediaOssArchiveService::archiveRemoteUrl($url, 'moments', $resourceType, (string)($row['snsId'] ?? $momentId), [
|
|
'index' => $index,
|
|
]);
|
|
if (!empty($result['success']) && !empty($result['url'])) {
|
|
$ossUrls[] = $result['url'];
|
|
}
|
|
}
|
|
|
|
if (empty($ossUrls)) {
|
|
return false;
|
|
}
|
|
|
|
Db::table('s2_wechat_moments')->where('id', $momentId)->update([
|
|
'ossUrls' => json_encode($ossUrls, JSON_UNESCAPED_UNICODE),
|
|
'isOssUploaded' => 1,
|
|
'update_time' => time(),
|
|
]);
|
|
|
|
return true;
|
|
}
|
|
|
|
public static function updateDownloadedMessageMedia($messageId, $downloadUrl)
|
|
{
|
|
$messageId = (int)$messageId;
|
|
$downloadUrl = trim((string)$downloadUrl);
|
|
if ($messageId <= 0 || empty($downloadUrl)) {
|
|
return false;
|
|
}
|
|
|
|
$row = Db::table('s2_wechat_message')->where('id', $messageId)->find();
|
|
if (empty($row)) {
|
|
return false;
|
|
}
|
|
|
|
$content = (string)($row['content'] ?? '');
|
|
$originalContent = (string)($row['originalContent'] ?? '');
|
|
$msgType = (int)($row['msgType'] ?? 0);
|
|
|
|
$update = [];
|
|
if (empty($originalContent)) {
|
|
$update['originalContent'] = $content;
|
|
}
|
|
|
|
if ($msgType === 43) {
|
|
$payload = self::decodeJsonObject($content);
|
|
if (empty($payload)) {
|
|
$payload = [];
|
|
}
|
|
$payload['videoUrl'] = $downloadUrl;
|
|
$payload['isLoading'] = false;
|
|
$update['content'] = self::encodeJson($payload);
|
|
} elseif ($msgType === 49) {
|
|
$payload = self::decodeJsonObject($content);
|
|
if (empty($payload) || strtolower((string)($payload['type'] ?? '')) !== 'file') {
|
|
$payload = [
|
|
'type' => 'file',
|
|
'title' => self::extractFileTitle($content),
|
|
];
|
|
}
|
|
$payload['url'] = $downloadUrl;
|
|
$payload['isDownloading'] = false;
|
|
$update['content'] = self::encodeJson($payload);
|
|
} else {
|
|
return false;
|
|
}
|
|
|
|
Db::table('s2_wechat_message')->where('id', $messageId)->update($update);
|
|
return true;
|
|
}
|
|
|
|
protected static function buildMessageArchiveUpdate(array $row)
|
|
{
|
|
$msgType = (int)($row['msgType'] ?? 0);
|
|
$content = (string)($row['content'] ?? '');
|
|
$originalContent = (string)($row['originalContent'] ?? '');
|
|
$sourceOriginalContent = $originalContent !== '' ? $originalContent : $content;
|
|
|
|
$update = [];
|
|
switch ($msgType) {
|
|
case 3:
|
|
case 47:
|
|
$imageUpdate = self::archiveImageLikeContent($content, $msgType, $row['id']);
|
|
if (!empty($imageUpdate)) {
|
|
$update = array_merge($update, $imageUpdate);
|
|
}
|
|
break;
|
|
case 34:
|
|
$audioUpdate = self::archiveAudioContent($content, $row['id']);
|
|
if (!empty($audioUpdate)) {
|
|
$update = array_merge($update, $audioUpdate);
|
|
}
|
|
break;
|
|
case 43:
|
|
$videoUpdate = self::archiveVideoContent($content, $row['id']);
|
|
if (!empty($videoUpdate)) {
|
|
$update = array_merge($update, $videoUpdate);
|
|
}
|
|
break;
|
|
case 49:
|
|
$fileUpdate = self::archiveFileContent($content, $row['id']);
|
|
if (!empty($fileUpdate)) {
|
|
$update = array_merge($update, $fileUpdate);
|
|
}
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
if (!empty($update) && empty($originalContent) && !empty($sourceOriginalContent)) {
|
|
$update['originalContent'] = $sourceOriginalContent;
|
|
}
|
|
|
|
return $update;
|
|
}
|
|
|
|
protected static function archiveImageLikeContent($content, $msgType, $messageId)
|
|
{
|
|
$payload = self::decodeJsonObject($content);
|
|
if (!empty($payload) && !empty($payload['url'])) {
|
|
$sourceUrl = (string)($payload['originUrl'] ?? $payload['url']);
|
|
$result = MediaOssArchiveService::archiveRemoteUrl(
|
|
$sourceUrl,
|
|
'messages',
|
|
$msgType == 47 ? 'emoji' : 'image',
|
|
(string)$messageId
|
|
);
|
|
if (empty($result['success']) || empty($result['url'])) {
|
|
return [];
|
|
}
|
|
|
|
$payload['originUrl'] = $payload['originUrl'] ?? $sourceUrl;
|
|
$payload['ossUrl'] = $result['url'];
|
|
$payload['url'] = $result['url'];
|
|
return ['content' => self::encodeJson($payload)];
|
|
}
|
|
|
|
if (!MediaOssArchiveService::isRemoteHttpUrl($content)) {
|
|
return [];
|
|
}
|
|
|
|
$result = MediaOssArchiveService::archiveRemoteUrl(
|
|
$content,
|
|
'messages',
|
|
$msgType == 47 ? 'emoji' : 'image',
|
|
(string)$messageId
|
|
);
|
|
if (empty($result['success']) || empty($result['url'])) {
|
|
return [];
|
|
}
|
|
|
|
return ['content' => $result['url']];
|
|
}
|
|
|
|
protected static function archiveAudioContent($content, $messageId)
|
|
{
|
|
$payload = self::decodeJsonObject($content);
|
|
if (!empty($payload) && !empty($payload['url'])) {
|
|
$sourceUrl = (string)($payload['originUrl'] ?? $payload['url']);
|
|
$result = MediaOssArchiveService::archiveRemoteUrl($sourceUrl, 'messages', 'audio', (string)$messageId);
|
|
if (empty($result['success']) || empty($result['url'])) {
|
|
return [];
|
|
}
|
|
|
|
$payload['originUrl'] = $payload['originUrl'] ?? $sourceUrl;
|
|
$payload['ossUrl'] = $result['url'];
|
|
$payload['url'] = $result['url'];
|
|
return ['content' => self::encodeJson($payload)];
|
|
}
|
|
|
|
if (!MediaOssArchiveService::isRemoteHttpUrl($content)) {
|
|
return [];
|
|
}
|
|
|
|
$result = MediaOssArchiveService::archiveRemoteUrl($content, 'messages', 'audio', (string)$messageId);
|
|
if (empty($result['success']) || empty($result['url'])) {
|
|
return [];
|
|
}
|
|
|
|
return ['content' => $result['url']];
|
|
}
|
|
|
|
protected static function archiveVideoContent($content, $messageId)
|
|
{
|
|
$payload = self::decodeJsonObject($content);
|
|
if (empty($payload)) {
|
|
if (!MediaOssArchiveService::isRemoteHttpUrl($content)) {
|
|
return [];
|
|
}
|
|
|
|
$result = MediaOssArchiveService::archiveRemoteUrl($content, 'messages', 'video', (string)$messageId);
|
|
if (empty($result['success']) || empty($result['url'])) {
|
|
return [];
|
|
}
|
|
|
|
return ['content' => $result['url']];
|
|
}
|
|
|
|
$changed = false;
|
|
$previewSource = (string)($payload['previewImageOriginUrl'] ?? $payload['previewImage'] ?? '');
|
|
if (MediaOssArchiveService::isRemoteHttpUrl($previewSource)) {
|
|
$previewResult = MediaOssArchiveService::archiveRemoteUrl($previewSource, 'messages', 'video_cover', (string)$messageId);
|
|
if (!empty($previewResult['success']) && !empty($previewResult['url'])) {
|
|
$payload['previewImageOriginUrl'] = $payload['previewImageOriginUrl'] ?? $previewSource;
|
|
$payload['previewImageOssUrl'] = $previewResult['url'];
|
|
$payload['previewImage'] = $previewResult['url'];
|
|
$changed = true;
|
|
}
|
|
}
|
|
|
|
$videoSource = (string)($payload['originUrl'] ?? $payload['videoUrl'] ?? $payload['ossUrl'] ?? $payload['tencentUrl'] ?? $payload['url'] ?? '');
|
|
if (MediaOssArchiveService::isRemoteHttpUrl($videoSource)) {
|
|
$videoResult = MediaOssArchiveService::archiveRemoteUrl($videoSource, 'messages', 'video', (string)$messageId);
|
|
if (!empty($videoResult['success']) && !empty($videoResult['url'])) {
|
|
$payload['originUrl'] = $payload['originUrl'] ?? $videoSource;
|
|
$payload['ossUrl'] = $videoResult['url'];
|
|
$payload['videoUrl'] = $videoResult['url'];
|
|
$payload['url'] = $videoResult['url'];
|
|
$payload['isLoading'] = false;
|
|
$changed = true;
|
|
}
|
|
}
|
|
|
|
if (!$changed) {
|
|
return [];
|
|
}
|
|
|
|
return ['content' => self::encodeJson($payload)];
|
|
}
|
|
|
|
protected static function archiveFileContent($content, $messageId)
|
|
{
|
|
$payload = self::decodeJsonObject($content);
|
|
if (empty($payload) || strtolower((string)($payload['type'] ?? '')) !== 'file') {
|
|
return [];
|
|
}
|
|
|
|
$sourceUrl = (string)($payload['originUrl'] ?? $payload['url'] ?? '');
|
|
if (!MediaOssArchiveService::isRemoteHttpUrl($sourceUrl)) {
|
|
return [];
|
|
}
|
|
|
|
$result = MediaOssArchiveService::archiveRemoteUrl(
|
|
$sourceUrl,
|
|
'messages',
|
|
'file',
|
|
(string)$messageId,
|
|
[
|
|
'extension' => $payload['fileext'] ?? '',
|
|
]
|
|
);
|
|
if (empty($result['success']) || empty($result['url'])) {
|
|
return [];
|
|
}
|
|
|
|
$payload['originUrl'] = $payload['originUrl'] ?? $sourceUrl;
|
|
$payload['ossUrl'] = $result['url'];
|
|
$payload['url'] = $result['url'];
|
|
$payload['isDownloading'] = false;
|
|
return ['content' => self::encodeJson($payload)];
|
|
}
|
|
|
|
protected static function decodeJsonObject($value)
|
|
{
|
|
if (!is_string($value)) {
|
|
return [];
|
|
}
|
|
|
|
$decoded = json_decode(trim($value), true);
|
|
return is_array($decoded) ? $decoded : [];
|
|
}
|
|
|
|
protected static function decodeJsonArray($value)
|
|
{
|
|
if (is_array($value)) {
|
|
return $value;
|
|
}
|
|
|
|
if (!is_string($value) || trim($value) === '') {
|
|
return [];
|
|
}
|
|
|
|
$decoded = json_decode($value, true);
|
|
return is_array($decoded) ? $decoded : [];
|
|
}
|
|
|
|
protected static function encodeJson(array $payload)
|
|
{
|
|
return json_encode($payload, JSON_UNESCAPED_UNICODE);
|
|
}
|
|
|
|
protected static function extractFileTitle($rawContent)
|
|
{
|
|
if (!is_string($rawContent) || trim($rawContent) === '') {
|
|
return '文件';
|
|
}
|
|
|
|
if (preg_match('/<title><!\[CDATA\[(.*?)\]\]><\/title>/i', $rawContent, $matches) && !empty($matches[1])) {
|
|
return trim($matches[1]);
|
|
}
|
|
|
|
if (preg_match('/<title>([^<]+)<\/title>/i', $rawContent, $matches) && !empty($matches[1])) {
|
|
return trim($matches[1]);
|
|
}
|
|
|
|
return '文件';
|
|
}
|
|
|
|
protected static function isVideoUrl($url)
|
|
{
|
|
if (!is_string($url)) {
|
|
return false;
|
|
}
|
|
|
|
return (bool)preg_match('/\.(mp4|mov|avi|webm|mkv)(\?.*)?$/i', $url);
|
|
}
|
|
}
|