diff --git a/Server/application/api/controller/WebSocketController.php b/Server/application/api/controller/WebSocketController.php index b9b8a860..e1e97bf5 100644 --- a/Server/application/api/controller/WebSocketController.php +++ b/Server/application/api/controller/WebSocketController.php @@ -12,6 +12,7 @@ use think\facade\Env; use app\api\model\WechatFriendModel as WechatFriend; use app\api\model\WechatMomentsModel as WechatMoments; use think\facade\Cache; +use app\common\util\AliyunOSS; class WebSocketController extends BaseController @@ -473,7 +474,23 @@ class WebSocketController extends BaseController } if ($message['cmdType'] == 'CmdDownloadMomentImagesResult' && is_array($message['urls']) && count($message['urls']) > 0) { $urls = json_encode($message['urls'], 256); - Db::table('s2_wechat_moments')->where('snsId', $data['snsId'])->update(['resUrls' => $urls]); + + // 上传图片到OSS + $ossUrls = $this->uploadMomentImagesToOss($message['urls'], $data['snsId']); + + // 更新数据库:保存原始URL和OSS URL,并标记已上传 + $updateData = [ + 'resUrls' => $urls, + 'isOssUploaded' => 1, // 标识已上传到OSS + 'update_time' => time() + ]; + + // 如果有OSS URL,保存到ossUrls字段 + if (!empty($ossUrls)) { + $updateData['ossUrls'] = json_encode($ossUrls, 256); + } + Db::table('s2_wechat_moments')->where('snsId', $data['snsId'])->update($updateData); + } return json_encode(['code' => 200, 'msg' => '获取朋友圈资源链接成功', 'data' => $message]); } catch (\Exception $e) { @@ -495,6 +512,95 @@ class WebSocketController extends BaseController } } + /** + * 上传朋友圈图片到OSS + * @param array $urls 图片URL数组 + * @param string $snsId 朋友圈ID + * @return array OSS URL数组 + */ + protected function uploadMomentImagesToOss($urls, $snsId) + { + $ossUrls = []; + + if (empty($urls) || !is_array($urls)) { + return $ossUrls; + } + + try { + // 创建临时目录(兼容无 runtime_path() 辅助函数的环境) + if (function_exists('runtime_path')) { + $baseRuntimePath = rtrim(runtime_path(), DIRECTORY_SEPARATOR) . DIRECTORY_SEPARATOR; + } elseif (defined('RUNTIME_PATH')) { + $baseRuntimePath = rtrim(RUNTIME_PATH, DIRECTORY_SEPARATOR) . DIRECTORY_SEPARATOR; + } else { + // 兜底:使用项目根目录下的 runtime 目录 + $baseRuntimePath = rtrim(ROOT_PATH, DIRECTORY_SEPARATOR) . DIRECTORY_SEPARATOR . 'runtime' . DIRECTORY_SEPARATOR; + } + + $tempDir = $baseRuntimePath . 'temp' . DIRECTORY_SEPARATOR . 'moments' . DIRECTORY_SEPARATOR . date('Y' . DIRECTORY_SEPARATOR . 'm' . DIRECTORY_SEPARATOR . 'd') . DIRECTORY_SEPARATOR; + + if (!is_dir($tempDir)) { + mkdir($tempDir, 0755, true); + } + + foreach ($urls as $index => $url) { + if (empty($url)) { + continue; + } + + try { + // 下载图片到临时文件 + $tempFile = $tempDir . md5($url . $snsId . $index) . '.jpg'; + + // 使用curl下载图片 + $ch = curl_init($url); + $fp = fopen($tempFile, 'wb'); + curl_setopt($ch, CURLOPT_FILE, $fp); + curl_setopt($ch, CURLOPT_HEADER, 0); + curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true); + curl_setopt($ch, CURLOPT_TIMEOUT, 30); + curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false); + curl_exec($ch); + $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE); + curl_close($ch); + fclose($fp); + + if ($httpCode != 200 || !file_exists($tempFile) || filesize($tempFile) == 0) { + Log::warning('下载朋友圈图片失败:' . $url . ', HTTP Code: ' . $httpCode); + @unlink($tempFile); + continue; + } + + // 生成OSS对象名称 + $objectName = 'moments/' . date('Y/m/d/') . md5($snsId . $index . time()) . '.jpg'; + + // 上传到OSS + $result = AliyunOSS::uploadFile($tempFile, $objectName); + if ($result['success']) { + $ossUrls[] = $result['url']; + Log::info('朋友圈图片上传OSS成功:' . $url . ' -> ' . $result['url']); + } else { + Log::error('朋友圈图片上传OSS失败:' . $url . ', 错误:' . ($result['error'] ?? '未知错误')); + } + + // 删除临时文件 + @unlink($tempFile); + + } catch (\Exception $e) { + Log::error('上传朋友圈图片到OSS异常:' . $e->getMessage() . ', URL: ' . $url); + if (isset($tempFile) && file_exists($tempFile)) { + @unlink($tempFile); + } + } + } + + } catch (\Exception $e) { + Log::error('上传朋友圈图片到OSS异常:' . $e->getMessage()); + } + + return $ossUrls; + } + /** * 保存朋友圈数据到数据库 * @param array $momentList 朋友圈数据列表 @@ -514,11 +620,14 @@ class WebSocketController extends BaseController // 提取momentEntity中的数据 $momentEntity = $moment['momentEntity'] ?? []; - // 检查朋友圈数据是否已存在 - $momentId = WechatMoments::where('snsId', $moment['snsId']) + // 检查朋友圈数据是否已存在,并获取isOssUploaded状态 + $existingMoment = Db::table('s2_wechat_moments') + ->where('snsId', $moment['snsId']) ->where('wechatAccountId', $wechatAccountId) - ->value('id'); - + ->find(); + + $momentId = $existingMoment['id'] ?? null; + $isOssUploaded = isset($existingMoment['isOssUploaded']) ? (int)$existingMoment['isOssUploaded'] : 0; $dataToSave = [ 'commentList' => json_encode($moment['commentList'] ?? [], 256), @@ -540,7 +649,7 @@ class WebSocketController extends BaseController ]; if (!empty($momentId)) { - // 如果已存在,则更新数据 + // 如果已存在,则更新数据(保留isOssUploaded和ossUrls字段,不覆盖) Db::table('s2_wechat_moments')->where('id', $momentId)->update($dataToSave); } else { if (empty($wechatFriendId)) { @@ -550,12 +659,18 @@ class WebSocketController extends BaseController $dataToSave['wechatAccountId'] = $wechatAccountId; $dataToSave['wechatFriendId'] = $wechatFriendId ?? 0; $dataToSave['create_time'] = time(); + $dataToSave['isOssUploaded'] = 0; // 新记录默认为未上传 $res = WechatMoments::create($dataToSave); } - - // 获取资源链接 - if(empty($momentEntity['resUrls']) && !empty($momentEntity['urls']) && $moment['type'] == 1) { + // 获取资源链接(检查是否已上传到OSS,如果已上传则跳过) + if(empty($momentEntity['urls']) || $moment['type'] != 1) { + // 如果没有urls或类型不是1,跳过 + } elseif ($isOssUploaded == 1) { + // 如果已上传到OSS,跳过采集 + Log::info('朋友圈图片已上传到OSS,跳过采集。snsId: ' . $moment['snsId']); + } else { + // 未上传到OSS,执行采集 $snsData = [ 'snsId' => $moment['snsId'], 'snsUrls' => $momentEntity['urls'], diff --git a/Server/application/cunkebao/controller/ContentLibraryController.php b/Server/application/cunkebao/controller/ContentLibraryController.php index f4e079a7..039a6320 100644 --- a/Server/application/cunkebao/controller/ContentLibraryController.php +++ b/Server/application/cunkebao/controller/ContentLibraryController.php @@ -1297,10 +1297,11 @@ class ContentLibraryController extends Controller // 从s2_wechat_moments表获取朋友圈数据 $query = Db::table('s2_wechat_moments') ->where([ + //'wechatAccountId' => $friend['wechatAccountId'], 'userName' => $friend['wechatId'], - 'wechatAccountId' => $friend['wechatAccountId'] ]) - ->order('createTime', 'desc'); + ->order('createTime', 'desc') + ->group('snsId'); // 如果启用了时间限制 if ($library['timeEnabled'] && $library['timeStart'] > 0 && $library['timeEnd'] > 0) { @@ -1313,8 +1314,7 @@ class ContentLibraryController extends Controller }*/ // 获取最近20条朋友圈 - $moments = $query->page(1, 20)->select(); - + $moments = $query->page(1, 100)->select(); if (empty($moments)) { continue; } @@ -1804,9 +1804,6 @@ class ContentLibraryController extends Controller ->where('snsId', $moment['snsId'] ?? '') ->find(); - if ($exists) { - return true; - } // 解析资源URL (可能是JSON字符串) $resUrls = $moment['resUrls']; @@ -1892,30 +1889,37 @@ class ContentLibraryController extends Controller } // 如果不存在,则创建新的内容项目 - $item = new ContentItem(); - $item->libraryId = $libraryId; - $item->type = 'moment'; // 朋友圈类型 - $item->title = '来自 ' . $nickname . ' 的朋友圈'; - $item->contentData = json_encode($moment, JSON_UNESCAPED_UNICODE); - $item->snsId = $moment['snsId'] ?? ''; // 存储snsId便于后续查询 - $item->createTime = time(); - $item->wechatId = $friend['wechatId']; - $item->friendId = $friend['id']; - $item->createMomentTime = $moment['createTime'] ?? 0; - $item->content = $moment['content'] ?? ''; - $item->contentAi = $moment['contentAi'] ?? ''; - $item->coverImage = $coverImage; - $item->contentType = $contentType; // 设置内容类型 + + if(empty($exists)){ + $exists = new ContentItem(); + } + + $exists->libraryId = $libraryId; + $exists->type = 'moment'; // 朋友圈类型 + $exists->title = '来自 ' . $nickname . ' 的朋友圈'; + $exists->contentData = json_encode($moment, JSON_UNESCAPED_UNICODE); + $exists->snsId = $moment['snsId'] ?? ''; // 存储snsId便于后续查询 + $exists->createTime = time(); + $exists->wechatId = $friend['wechatId']; + $exists->friendId = $friend['id']; + $exists->createMomentTime = $moment['createTime'] ?? 0; + $exists->content = $moment['content'] ?? ''; + $exists->contentAi = $moment['contentAi'] ?? ''; + $exists->coverImage = $coverImage; + $exists->contentType = $contentType; // 设置内容类型 + $exists->ossUrls = $moment['ossUrls'] ?? json_decode([]); // 独立存储resUrls和urls字段 - $item->resUrls = is_string($moment['resUrls']) ? $moment['resUrls'] : json_encode($resUrls, JSON_UNESCAPED_UNICODE); - $item->urls = is_string($moment['urls']) ? $moment['urls'] : json_encode($urls, JSON_UNESCAPED_UNICODE); + $exists->resUrls = is_string($moment['resUrls']) ? $moment['resUrls'] : json_encode($resUrls, JSON_UNESCAPED_UNICODE); + $exists->urls = is_string($moment['urls']) ? $moment['urls'] : json_encode($urls, JSON_UNESCAPED_UNICODE); // 保存地理位置信息 - $item->location = $moment['location'] ?? ''; - $item->lat = $moment['lat'] ?? 0; - $item->lng = $moment['lng'] ?? 0; - $item->save(); + $exists->location = $moment['location'] ?? ''; + $exists->lat = $moment['lat'] ?? 0; + $exists->lng = $moment['lng'] ?? 0; + $exists->save(); + + return true; } catch (\Exception $e) { // 记录错误日志