From f134b784c38dfee4a6451238ed33828046a95de7 Mon Sep 17 00:00:00 2001 From: wong <106998207@qq.com> Date: Thu, 3 Jul 2025 15:36:10 +0800 Subject: [PATCH 01/29] =?UTF-8?q?=E4=BB=A3=E7=A0=81=E6=8F=90=E4=BA=A4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Cunkebao/app/content/[id]/materials/new/page.tsx | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Cunkebao/app/content/[id]/materials/new/page.tsx b/Cunkebao/app/content/[id]/materials/new/page.tsx index 80d07ac7..2d021c51 100644 --- a/Cunkebao/app/content/[id]/materials/new/page.tsx +++ b/Cunkebao/app/content/[id]/materials/new/page.tsx @@ -115,10 +115,10 @@ export default function NewMaterialPage({ params }: { params: { id: string } }) showToast("请输入内容", "error") return } - if (!comment) { - showToast("请输入评论内容", "error") - return - } + // if (!comment) { + // showToast("请输入评论内容", "error") + // return + // } if (materialType === 1 && images.length === 0) { showToast("请上传图片", "error") return From 5e4cdbedbfc60c308804eb69821cabdad962e26c Mon Sep 17 00:00:00 2001 From: wong <106998207@qq.com> Date: Thu, 3 Jul 2025 16:12:45 +0800 Subject: [PATCH 02/29] =?UTF-8?q?=E5=9B=BE=E7=89=87=E4=B8=8A=E4=BC=A0?= =?UTF-8?q?=E6=9C=80=E5=A4=A7=E5=8F=AA=E8=83=BD=E4=B8=8A=E4=BC=A09?= =?UTF-8?q?=E5=BC=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../[id]/materials/edit/[materialId]/page.tsx | 76 +++++++++++++++---- .../app/content/[id]/materials/new/page.tsx | 8 +- 2 files changed, 66 insertions(+), 18 deletions(-) diff --git a/Cunkebao/app/content/[id]/materials/edit/[materialId]/page.tsx b/Cunkebao/app/content/[id]/materials/edit/[materialId]/page.tsx index f22c944f..50d6cf24 100644 --- a/Cunkebao/app/content/[id]/materials/edit/[materialId]/page.tsx +++ b/Cunkebao/app/content/[id]/materials/edit/[materialId]/page.tsx @@ -15,6 +15,7 @@ import { showToast } from "@/lib/toast" import Image from "next/image" import { Input } from "@/components/ui/input" import { cn } from "@/lib/utils" +import { useRef } from "react" interface ApiResponse { code: number @@ -77,6 +78,7 @@ export default function EditMaterialPage({ params }: { params: Promise<{ id: str const [iconUrl, setIconUrl] = useState("") const [videoUrl, setVideoUrl] = useState("") const [comment, setComment] = useState("") + const fileInputRef = useRef(null) // 获取素材详情 useEffect(() => { @@ -153,22 +155,56 @@ export default function EditMaterialPage({ params }: { params: Promise<{ id: str fetchMaterialDetail() }, [resolvedParams.materialId, router]) - // 模拟上传图片 + // 替换handleUploadImage为: const handleUploadImage = () => { - // 这里应该是真实的图片上传逻辑 - // 为了演示,这里模拟添加一些示例图片URL - const mockImageUrls = [ - "https://picsum.photos/id/237/200/300", - "https://picsum.photos/id/238/200/300", - "https://picsum.photos/id/239/200/300" - ] - - const randomIndex = Math.floor(Math.random() * mockImageUrls.length) - const newImage = mockImageUrls[randomIndex] - - if (!images.includes(newImage)) { - setImages([...images, newImage]) - setPreviewUrls([...previewUrls, newImage]) + if (images.length >= 9) { + showToast("最多只能上传9张图片", "error") + return + } + fileInputRef.current?.click() + } + + // 新增真实上传逻辑 + const handleFileChange = async (event: React.ChangeEvent) => { + if (images.length >= 9) { + showToast("最多只能上传9张图片", "error") + return + } + const file = event.target.files?.[0] + if (!file) return + + if (!file.type.startsWith('image/')) { + showToast("请选择图片文件", "error") + return + } + + showToast("正在上传图片...", "loading") + const formData = new FormData() + formData.append("file", file) + + try { + const token = localStorage.getItem('token'); + const headers: HeadersInit = {} + if (token) headers['Authorization'] = `Bearer ${token}` + + const response = await fetch(`${process.env.NEXT_PUBLIC_API_BASE_URL}/v1/attachment/upload`, { + method: 'POST', + headers, + body: formData, + }) + + const result = await response.json() + if (result.code === 200 && result.data?.url) { + setImages((prev) => [...prev, result.data.url]) + setPreviewUrls((prev) => [...prev, result.data.url]) + showToast("图片上传成功", "success") + } else { + showToast(result.msg || "图片上传失败", "error") + } + } catch (error: any) { + showToast(error?.message || "图片上传失败", "error") + } finally { + if (fileInputRef.current) fileInputRef.current.value = '' } } @@ -450,12 +486,20 @@ export default function EditMaterialPage({ params }: { params: Promise<{ id: str variant="outline" onClick={handleUploadImage} className="w-full py-8 flex flex-col items-center justify-center rounded-2xl border-2 border-dashed border-blue-300 bg-white hover:bg-blue-50" + disabled={images.length >= 9} > 点击上传图片 - 支持 JPG、PNG 格式 + {`已上传${images.length}张,最多可上传9张`} + {previewUrls.length > 0 && (
diff --git a/Cunkebao/app/content/[id]/materials/new/page.tsx b/Cunkebao/app/content/[id]/materials/new/page.tsx index 2d021c51..e0d75751 100644 --- a/Cunkebao/app/content/[id]/materials/new/page.tsx +++ b/Cunkebao/app/content/[id]/materials/new/page.tsx @@ -52,6 +52,10 @@ export default function NewMaterialPage({ params }: { params: { id: string } }) } const handleFileChange = async (event: React.ChangeEvent) => { + if (images.length >= 9) { + showToast("最多只能上传9张图片", "error") + return + } const file = event.target.files?.[0] if (!file) return @@ -352,11 +356,11 @@ export default function NewMaterialPage({ params }: { params: { id: string } }) variant="outline" onClick={handleUploadImage} className="w-full py-8 flex flex-col items-center justify-center rounded-2xl border-2 border-dashed border-blue-300 bg-white hover:bg-blue-50" - disabled={loading} + disabled={loading || images.length >= 9} > 点击上传图片 - 支持 JPG、PNG 格式 + {`已上传${images.length}张,最多可上传9张`} Date: Thu, 3 Jul 2025 18:14:53 +0800 Subject: [PATCH 03/29] =?UTF-8?q?=E8=AE=A2=E5=8D=95=E6=96=87=E4=BB=B6?= =?UTF-8?q?=E4=B8=8A=E4=BC=A0=E5=8A=9F=E8=83=BD=E5=AE=8C=E5=96=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../common/controller/Attachment.php | 14 +-- .../PostCreateAddFriendPlanV1Controller.php | 105 +++++++++++++++++- .../PostUpdateAddFriendPlanV1Controller.php | 105 ++++++++++++++++++ 3 files changed, 213 insertions(+), 11 deletions(-) diff --git a/Server/application/common/controller/Attachment.php b/Server/application/common/controller/Attachment.php index 062bde02..e866d968 100644 --- a/Server/application/common/controller/Attachment.php +++ b/Server/application/common/controller/Attachment.php @@ -28,17 +28,17 @@ class Attachment extends Controller $validate = \think\facade\Validate::rule([ 'file' => [ 'fileSize' => 10485760, // 10MB - 'fileExt' => 'jpg,jpeg,png,gif,doc,docx,pdf,zip,rar,mp4,mp3', + 'fileExt' => 'jpg,jpeg,png,gif,doc,docx,pdf,zip,rar,mp4,mp3,csv,xls,xlsx', 'fileMime' => 'image/jpeg,image/png,image/gif,application/msword,application/vnd.openxmlformats-officedocument.wordprocessingml.document,application/pdf,application/zip,application/x-rar-compressed,video/mp4,audio/mp3' ] ]); - if (!$validate->check(['file' => $file])) { - return json([ - 'code' => 400, - 'msg' => $validate->getError() - ]); - } + // if (!$validate->check(['file' => $file])) { + // return json([ + // 'code' => 400, + // 'msg' => $validate->getError() + // ]); + // } // 生成文件hash $hashKey = md5_file($file->getRealPath()); diff --git a/Server/application/cunkebao/controller/plan/PostCreateAddFriendPlanV1Controller.php b/Server/application/cunkebao/controller/plan/PostCreateAddFriendPlanV1Controller.php index a2303056..340a3ddf 100644 --- a/Server/application/cunkebao/controller/plan/PostCreateAddFriendPlanV1Controller.php +++ b/Server/application/cunkebao/controller/plan/PostCreateAddFriendPlanV1Controller.php @@ -13,10 +13,6 @@ use think\facade\Request; class PostCreateAddFriendPlanV1Controller extends Controller { - protected function yyyyyyy() - { - - } /** * 生成唯一API密钥 @@ -60,6 +56,7 @@ class PostCreateAddFriendPlanV1Controller extends Controller try { $params = $this->request->param(); + // 验证必填字段 if (empty($params['name'])) { return ResponseHelper::error('计划名称不能为空', 400); @@ -132,6 +129,106 @@ class PostCreateAddFriendPlanV1Controller extends Controller throw new \Exception('添加计划失败'); } + + //订单 + if($params['sceneId'] == 2){ + if(!empty($params['orderTableFile'])){ + // 先下载到本地临时文件,再分析,最后删除 + $originPath = $params['orderTableFile']; + $tmpFile = tempnam(sys_get_temp_dir(), 'order_'); + // 判断是否为远程文件 + if (preg_match('/^https?:\/\//i', $originPath)) { + // 远程URL,下载到本地 + $fileContent = file_get_contents($originPath); + if ($fileContent === false) { + exit('远程文件下载失败: ' . $originPath); + } + file_put_contents($tmpFile, $fileContent); + } else { + // 本地文件,直接copy + if (!file_exists($originPath)) { + exit('文件不存在: ' . $originPath); + } + copy($originPath, $tmpFile); + } + // 解析临时文件 + $ext = strtolower(pathinfo($originPath, PATHINFO_EXTENSION)); + $rows = []; + if (in_array($ext, ['xls', 'xlsx'])) { + // 直接用composer自动加载的PHPExcel + $excel = \PHPExcel_IOFactory::load($tmpFile); + $sheet = $excel->getActiveSheet(); + $data = $sheet->toArray(); + if (count($data) > 1) { + array_shift($data); // 去掉表头 + } + foreach ($data as $cols) { + if (count($cols) >= 6) { + $rows[] = [ + 'name' => trim($cols[0]), + 'phone' => trim($cols[1]), + 'wechat' => trim($cols[2]), + 'source' => trim($cols[3]), + 'orderAmount' => trim($cols[4]), + 'orderDate' => trim($cols[5]), + ]; + } + } + } elseif ($ext === 'csv') { + $content = file_get_contents($tmpFile); + $lines = preg_split('/\r\n|\r|\n/', $content); + if (count($lines) > 1) { + array_shift($lines); // 去掉表头 + foreach ($lines as $line) { + if (trim($line) === '') continue; + $cols = str_getcsv($line); + if (count($cols) >= 6) { + $rows[] = [ + 'name' => trim($cols[0]), + 'phone' => trim($cols[1]), + 'wechat' => trim($cols[2]), + 'source' => trim($cols[3]), + 'orderAmount' => trim($cols[4]), + 'orderDate' => trim($cols[5]), + ]; + } + } + } + } else { + unlink($tmpFile); + exit('暂不支持的文件类型: ' . $ext); + } + // 删除临时文件 + unlink($tmpFile); + + foreach($rows as $row){ + $phone = !empty($row['phone']) ? $row['phone'] : $row['wechat']; + if(empty($phone)){ + continue; + } + $ck_task_customer = Db::name('task_customer') + ->where(['phone' => $phone,'task_id' => $planId]) + ->find(); + if(!$ck_task_customer){ + $task_customer = Db::name('task_customer') + ->insert([ + 'task_id' => $planId, + 'name' => $row['name'] ?? '', + 'source' => $row['source'] ?? '', + 'phone' => $phone, + 'tags' => json_encode([],JSON_UNESCAPED_UNICODE), + 'siteTags' => json_encode([],JSON_UNESCAPED_UNICODE), + 'created_at' => time(), + ]); + } + } + + } + } + + + + // 提交事务 Db::commit(); diff --git a/Server/application/cunkebao/controller/plan/PostUpdateAddFriendPlanV1Controller.php b/Server/application/cunkebao/controller/plan/PostUpdateAddFriendPlanV1Controller.php index 4b7499bb..655180ed 100644 --- a/Server/application/cunkebao/controller/plan/PostUpdateAddFriendPlanV1Controller.php +++ b/Server/application/cunkebao/controller/plan/PostUpdateAddFriendPlanV1Controller.php @@ -102,6 +102,111 @@ class PostUpdateAddFriendPlanV1Controller extends Controller throw new \Exception('更新计划失败'); } + //订单 + if($params['sceneId'] == 2){ + if(!empty($params['orderTableFile'])){ + // 先下载到本地临时文件,再分析,最后删除 + $originPath = $params['orderTableFile']; + $tmpFile = tempnam(sys_get_temp_dir(), 'order_'); + // 判断是否为远程文件 + if (preg_match('/^https?:\/\//i', $originPath)) { + // 远程URL,下载到本地 + $fileContent = file_get_contents($originPath); + if ($fileContent === false) { + exit('远程文件下载失败: ' . $originPath); + } + file_put_contents($tmpFile, $fileContent); + } else { + // 本地文件,直接copy + if (!file_exists($originPath)) { + exit('文件不存在: ' . $originPath); + } + copy($originPath, $tmpFile); + } + // 解析临时文件 + $ext = strtolower(pathinfo($originPath, PATHINFO_EXTENSION)); + $rows = []; + if (in_array($ext, ['xls', 'xlsx'])) { + // 直接用composer自动加载的PHPExcel + $excel = \PHPExcel_IOFactory::load($tmpFile); + $sheet = $excel->getActiveSheet(); + $data = $sheet->toArray(); + if (count($data) > 1) { + array_shift($data); // 去掉表头 + } + foreach ($data as $cols) { + if (count($cols) >= 6) { + $rows[] = [ + 'name' => trim($cols[0]), + 'phone' => trim($cols[1]), + 'wechat' => trim($cols[2]), + 'source' => trim($cols[3]), + 'orderAmount' => trim($cols[4]), + 'orderDate' => trim($cols[5]), + ]; + } + } + } elseif ($ext === 'csv') { + $content = file_get_contents($tmpFile); + $lines = preg_split('/\r\n|\r|\n/', $content); + if (count($lines) > 1) { + array_shift($lines); // 去掉表头 + foreach ($lines as $line) { + if (trim($line) === '') continue; + $cols = str_getcsv($line); + if (count($cols) >= 6) { + $rows[] = [ + 'name' => trim($cols[0]), + 'phone' => trim($cols[1]), + 'wechat' => trim($cols[2]), + 'source' => trim($cols[3]), + 'orderAmount' => trim($cols[4]), + 'orderDate' => trim($cols[5]), + ]; + } + } + } + } else { + unlink($tmpFile); + exit('暂不支持的文件类型: ' . $ext); + } + // 删除临时文件 + unlink($tmpFile); + + foreach($rows as $row){ + $phone = !empty($row['phone']) ? $row['phone'] : $row['wechat']; + if(empty($phone)){ + continue; + } + $ck_task_customer = Db::name('task_customer') + ->where(['phone' => $phone,'task_id' => $params['planId']]) + ->find(); + if(!$ck_task_customer){ + $task_customer = Db::name('task_customer') + ->insert([ + 'task_id' => $params['planId'], + 'name' => $row['name'] ?? '', + 'source' => $row['source'] ?? '', + 'phone' => $phone, + 'tags' => json_encode([],JSON_UNESCAPED_UNICODE), + 'siteTags' => json_encode([],JSON_UNESCAPED_UNICODE), + 'created_at' => time(), + ]); + } + } + + } + } + + + + + + + + + + // 提交事务 Db::commit(); From 3b1267a79b1b6787dfad7c7116a67b350c0d0487 Mon Sep 17 00:00:00 2001 From: wong <106998207@qq.com> Date: Fri, 4 Jul 2025 15:12:36 +0800 Subject: [PATCH 04/29] =?UTF-8?q?=E4=BB=A3=E7=A0=81=E6=8F=90=E4=BA=A4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../controller/ContentLibraryController.php | 21 +++++++++++++++++-- 1 file changed, 19 insertions(+), 2 deletions(-) diff --git a/Server/application/cunkebao/controller/ContentLibraryController.php b/Server/application/cunkebao/controller/ContentLibraryController.php index 20bccc7e..a18327bf 100644 --- a/Server/application/cunkebao/controller/ContentLibraryController.php +++ b/Server/application/cunkebao/controller/ContentLibraryController.php @@ -1390,8 +1390,25 @@ class ContentLibraryController extends Controller } // 判断内容类型 (0=未知, 1=图片, 2=链接, 3=视频, 4=文本, 5=小程序, 6=图文) - $contentType = $this->determineContentType($moment['content'] ?? '', $resUrls, $urls); - + if($moment['type'] == 1) { + //图文 + $contentType = 6; + }elseif ($moment['type'] == 3){ + //链接 + $contentType = 2; + }elseif ($moment['type'] == 15){ + //视频 + $contentType = 3; + }elseif ($moment['type'] == 2){ + //纯文本 + $contentType = 4; + }elseif ($moment['type'] == 30){ + //小程序 + $contentType = 5; + }else{ + $contentType = 1; + } + // 如果不存在,则创建新的内容项目 $item = new ContentItem(); $item->libraryId = $libraryId; From 8303b5fc55c0730c7dfc3d8b8130b98782302106 Mon Sep 17 00:00:00 2001 From: wong <106998207@qq.com> Date: Sat, 5 Jul 2025 11:29:44 +0800 Subject: [PATCH 05/29] =?UTF-8?q?=E4=BB=A3=E7=A0=81=E4=BC=98=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Server/extend/WeChatDeviceApi/Adapters/ChuKeBao/Adapter.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Server/extend/WeChatDeviceApi/Adapters/ChuKeBao/Adapter.php b/Server/extend/WeChatDeviceApi/Adapters/ChuKeBao/Adapter.php index e1cc561e..bc17565b 100644 --- a/Server/extend/WeChatDeviceApi/Adapters/ChuKeBao/Adapter.php +++ b/Server/extend/WeChatDeviceApi/Adapters/ChuKeBao/Adapter.php @@ -235,7 +235,7 @@ class Adapter implements WeChatServiceInterface { $tasks = Db::name('task_customer') - ->whereIn('status', [1,3]) + ->whereIn('status', [1,2,3]) ->limit(50) ->select(); @@ -451,6 +451,7 @@ class Adapter implements WeChatServiceInterface try { $id = Db::table('s2_wechat_friend') ->where('ownerWechatId', $wxId) + ->where(['isPassed' => 1,'isDeleted' => 0]) ->where('phone|alias|wechatId', 'like', $phone . '%') ->order('createTime', 'desc') ->value('id'); From 1f29f6880c096dc16b8ebe394bbeca69b929f070 Mon Sep 17 00:00:00 2001 From: wong <106998207@qq.com> Date: Sat, 5 Jul 2025 16:09:24 +0800 Subject: [PATCH 06/29] =?UTF-8?q?=E8=A1=A8=E5=8D=95=E5=AF=BC=E5=85=A5?= =?UTF-8?q?=E4=BC=98=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../PostUpdateAddFriendPlanV1Controller.php | 114 ++++++++++-------- 1 file changed, 66 insertions(+), 48 deletions(-) diff --git a/Server/application/cunkebao/controller/plan/PostUpdateAddFriendPlanV1Controller.php b/Server/application/cunkebao/controller/plan/PostUpdateAddFriendPlanV1Controller.php index 655180ed..832fd208 100644 --- a/Server/application/cunkebao/controller/plan/PostUpdateAddFriendPlanV1Controller.php +++ b/Server/application/cunkebao/controller/plan/PostUpdateAddFriendPlanV1Controller.php @@ -90,8 +90,7 @@ class PostUpdateAddFriendPlanV1Controller extends Controller 'updateTime'=> time(), ]; - // 开启事务 - Db::startTrans(); + try { // 更新数据 $result = Db::name('customer_acquisition_task') @@ -134,17 +133,16 @@ class PostUpdateAddFriendPlanV1Controller extends Controller if (count($data) > 1) { array_shift($data); // 去掉表头 } + foreach ($data as $cols) { - if (count($cols) >= 6) { - $rows[] = [ - 'name' => trim($cols[0]), - 'phone' => trim($cols[1]), - 'wechat' => trim($cols[2]), - 'source' => trim($cols[3]), - 'orderAmount' => trim($cols[4]), - 'orderDate' => trim($cols[5]), - ]; - } + $rows[] = [ + 'name' => isset($cols[0]) ? trim($cols[0]) : '', + 'phone' => isset($cols[1]) ? trim($cols[1]) : '', + 'wechat' => isset($cols[2]) ? trim($cols[2]) : '', + 'source' => isset($cols[3]) ? trim($cols[3]) : '', + 'orderAmount' => isset($cols[4]) ? trim($cols[4]) : '', + 'orderDate' => isset($cols[5]) ? trim($cols[5]) : '', + ]; } } elseif ($ext === 'csv') { $content = file_get_contents($tmpFile); @@ -156,12 +154,12 @@ class PostUpdateAddFriendPlanV1Controller extends Controller $cols = str_getcsv($line); if (count($cols) >= 6) { $rows[] = [ - 'name' => trim($cols[0]), - 'phone' => trim($cols[1]), - 'wechat' => trim($cols[2]), - 'source' => trim($cols[3]), - 'orderAmount' => trim($cols[4]), - 'orderDate' => trim($cols[5]), + 'name' => isset($cols[0]) ? trim($cols[0]) : '', + 'phone' => isset($cols[1]) ? trim($cols[1]) : '', + 'wechat' => isset($cols[2]) ? trim($cols[2]) : '', + 'source' => isset($cols[3]) ? trim($cols[3]) : '', + 'orderAmount' => isset($cols[4]) ? trim($cols[4]) : '', + 'orderDate' => isset($cols[5]) ? trim($cols[5]) : '', ]; } } @@ -172,43 +170,63 @@ class PostUpdateAddFriendPlanV1Controller extends Controller } // 删除临时文件 unlink($tmpFile); - - foreach($rows as $row){ - $phone = !empty($row['phone']) ? $row['phone'] : $row['wechat']; - if(empty($phone)){ - continue; - } - $ck_task_customer = Db::name('task_customer') - ->where(['phone' => $phone,'task_id' => $params['planId']]) - ->find(); - if(!$ck_task_customer){ - $task_customer = Db::name('task_customer') - ->insert([ - 'task_id' => $params['planId'], - 'name' => $row['name'] ?? '', - 'source' => $row['source'] ?? '', - 'phone' => $phone, - 'tags' => json_encode([],JSON_UNESCAPED_UNICODE), - 'siteTags' => json_encode([],JSON_UNESCAPED_UNICODE), - 'created_at' => time(), - ]); + + // 1000条为一组进行批量处理 + $batchSize = 1000; + $totalRows = count($rows); + + for ($i = 0; $i < $totalRows; $i += $batchSize) { + $batchRows = array_slice($rows, $i, $batchSize); + + if (!empty($batchRows)) { + // 1. 提取当前批次的phone + $phones = []; + foreach ($batchRows as $row) { + $phone = !empty($row['phone']) ? $row['phone'] : $row['wechat']; + if (!empty($phone)) { + $phones[] = $phone; + } + } + + // 2. 批量查询已存在的phone + $existingPhones = []; + if (!empty($phones)) { + $existing = Db::name('task_customer') + ->where('task_id', $params['planId']) + ->where('phone', 'in', $phones) + ->field('phone') + ->select(); + $existingPhones = array_column($existing, 'phone'); + } + + // 3. 过滤出新数据,批量插入 + $newData = []; + foreach ($batchRows as $row) { + $phone = !empty($row['phone']) ? $row['phone'] : $row['wechat']; + if (!empty($phone) && !in_array($phone, $existingPhones)) { + $newData[] = [ + 'task_id' => $params['planId'], + 'name' => $row['name'] ?? '', + 'source' => $row['source'] ?? '', + 'phone' => $phone, + 'tags' => json_encode([], JSON_UNESCAPED_UNICODE), + 'siteTags' => json_encode([], JSON_UNESCAPED_UNICODE), + 'created_at' => time(), + ]; + } + } + + // 4. 批量插入新数据 + if (!empty($newData)) { + Db::name('task_customer')->insertAll($newData); + } } } } } - - - - - - - - - // 提交事务 - Db::commit(); return ResponseHelper::success(['planId' => $params['planId']], '更新计划任务成功'); From e808ee1a90de6e90d774184f7692afc259bda47f Mon Sep 17 00:00:00 2001 From: wong <106998207@qq.com> Date: Sat, 5 Jul 2025 16:34:57 +0800 Subject: [PATCH 07/29] =?UTF-8?q?=E8=AE=A2=E5=8D=95=E6=B7=BB=E5=8A=A0?= =?UTF-8?q?=E4=BC=98=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../PostCreateAddFriendPlanV1Controller.php | 109 +++++++++++------- 1 file changed, 66 insertions(+), 43 deletions(-) diff --git a/Server/application/cunkebao/controller/plan/PostCreateAddFriendPlanV1Controller.php b/Server/application/cunkebao/controller/plan/PostCreateAddFriendPlanV1Controller.php index 340a3ddf..efa53fa9 100644 --- a/Server/application/cunkebao/controller/plan/PostCreateAddFriendPlanV1Controller.php +++ b/Server/application/cunkebao/controller/plan/PostCreateAddFriendPlanV1Controller.php @@ -119,8 +119,7 @@ class PostCreateAddFriendPlanV1Controller extends Controller - // 开启事务 - Db::startTrans(); + try { // 插入数据 $planId = Db::name('customer_acquisition_task')->insertGetId($data); @@ -162,17 +161,16 @@ class PostCreateAddFriendPlanV1Controller extends Controller if (count($data) > 1) { array_shift($data); // 去掉表头 } + foreach ($data as $cols) { - if (count($cols) >= 6) { - $rows[] = [ - 'name' => trim($cols[0]), - 'phone' => trim($cols[1]), - 'wechat' => trim($cols[2]), - 'source' => trim($cols[3]), - 'orderAmount' => trim($cols[4]), - 'orderDate' => trim($cols[5]), - ]; - } + $rows[] = [ + 'name' => isset($cols[0]) ? trim($cols[0]) : '', + 'phone' => isset($cols[1]) ? trim($cols[1]) : '', + 'wechat' => isset($cols[2]) ? trim($cols[2]) : '', + 'source' => isset($cols[3]) ? trim($cols[3]) : '', + 'orderAmount' => isset($cols[4]) ? trim($cols[4]) : '', + 'orderDate' => isset($cols[5]) ? trim($cols[5]) : '', + ]; } } elseif ($ext === 'csv') { $content = file_get_contents($tmpFile); @@ -184,12 +182,12 @@ class PostCreateAddFriendPlanV1Controller extends Controller $cols = str_getcsv($line); if (count($cols) >= 6) { $rows[] = [ - 'name' => trim($cols[0]), - 'phone' => trim($cols[1]), - 'wechat' => trim($cols[2]), - 'source' => trim($cols[3]), - 'orderAmount' => trim($cols[4]), - 'orderDate' => trim($cols[5]), + 'name' => isset($cols[0]) ? trim($cols[0]) : '', + 'phone' => isset($cols[1]) ? trim($cols[1]) : '', + 'wechat' => isset($cols[2]) ? trim($cols[2]) : '', + 'source' => isset($cols[3]) ? trim($cols[3]) : '', + 'orderAmount' => isset($cols[4]) ? trim($cols[4]) : '', + 'orderDate' => isset($cols[5]) ? trim($cols[5]) : '', ]; } } @@ -201,36 +199,61 @@ class PostCreateAddFriendPlanV1Controller extends Controller // 删除临时文件 unlink($tmpFile); - foreach($rows as $row){ - $phone = !empty($row['phone']) ? $row['phone'] : $row['wechat']; - if(empty($phone)){ - continue; - } - $ck_task_customer = Db::name('task_customer') - ->where(['phone' => $phone,'task_id' => $planId]) - ->find(); - if(!$ck_task_customer){ - $task_customer = Db::name('task_customer') - ->insert([ - 'task_id' => $planId, - 'name' => $row['name'] ?? '', - 'source' => $row['source'] ?? '', - 'phone' => $phone, - 'tags' => json_encode([],JSON_UNESCAPED_UNICODE), - 'siteTags' => json_encode([],JSON_UNESCAPED_UNICODE), - 'created_at' => time(), - ]); + // 1000条为一组进行批量处理 + $batchSize = 1000; + $totalRows = count($rows); + + for ($i = 0; $i < $totalRows; $i += $batchSize) { + $batchRows = array_slice($rows, $i, $batchSize); + + if (!empty($batchRows)) { + // 1. 提取当前批次的phone + $phones = []; + foreach ($batchRows as $row) { + $phone = !empty($row['phone']) ? $row['phone'] : $row['wechat']; + if (!empty($phone)) { + $phones[] = $phone; + } + } + + // 2. 批量查询已存在的phone + $existingPhones = []; + if (!empty($phones)) { + $existing = Db::name('task_customer') + ->where('task_id', $params['planId']) + ->where('phone', 'in', $phones) + ->field('phone') + ->select(); + $existingPhones = array_column($existing, 'phone'); + } + + // 3. 过滤出新数据,批量插入 + $newData = []; + foreach ($batchRows as $row) { + $phone = !empty($row['phone']) ? $row['phone'] : $row['wechat']; + if (!empty($phone) && !in_array($phone, $existingPhones)) { + $newData[] = [ + 'task_id' => $params['planId'], + 'name' => $row['name'] ?? '', + 'source' => $row['source'] ?? '', + 'phone' => $phone, + 'tags' => json_encode([], JSON_UNESCAPED_UNICODE), + 'siteTags' => json_encode([], JSON_UNESCAPED_UNICODE), + 'created_at' => time(), + ]; + } + } + + // 4. 批量插入新数据 + if (!empty($newData)) { + Db::name('task_customer')->insertAll($newData); + } } } - + } } - - - - // 提交事务 - Db::commit(); return ResponseHelper::success(['planId' => $planId], '添加计划任务成功'); From 15b899b0e8df774f092734645c4da3ab2a558de7 Mon Sep 17 00:00:00 2001 From: wong <106998207@qq.com> Date: Sun, 6 Jul 2025 00:17:24 +0800 Subject: [PATCH 08/29] =?UTF-8?q?=E4=BB=A3=E7=A0=81=E6=8F=90=E4=BA=A4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Cunkebao/app/content/[id]/materials/page.tsx | 17 +- .../app/scenarios/[channel]/devices/page.tsx | 180 +++++++++-- .../app/scenarios/new/steps/BasicSettings.tsx | 214 +++++++------ .../app/workspace/moments-sync/[id]/page.tsx | 70 +---- Server/application/cunkebao/config/route.php | 2 +- .../controller/WorkbenchController.php | 45 ++- .../controller/plan/PlanSceneV1Controller.php | 292 +++++++++++++++--- .../PostCreateAddFriendPlanV1Controller.php | 6 +- .../Adapters/ChuKeBao/Adapter.php | 10 +- 9 files changed, 606 insertions(+), 230 deletions(-) diff --git a/Cunkebao/app/content/[id]/materials/page.tsx b/Cunkebao/app/content/[id]/materials/page.tsx index 13ac8da8..f96c97a1 100644 --- a/Cunkebao/app/content/[id]/materials/page.tsx +++ b/Cunkebao/app/content/[id]/materials/page.tsx @@ -153,22 +153,28 @@ export default function MaterialsPage({ params }: { params: Promise<{ id: string href={first.url} target="_blank" rel="noopener noreferrer" - className="flex items-center bg-white rounded p-2 hover:bg-gray-50 transition group" + className=" items-center bg-white rounded p-2 hover:bg-gray-50 transition group" style={{ textDecoration: 'none' }} > - {first.image && ( +
+

{material.content}

+
+ + +
封面图
- )} +
-
{first.desc}
+
{first.desc ?? '这是一条链接'}
+
); @@ -179,6 +185,7 @@ export default function MaterialsPage({ params }: { params: Promise<{ id: string const videoUrl = typeof first === "string" ? first : (first.url || ""); return videoUrl ? (
+

{material.content}

) : null; diff --git a/Cunkebao/app/scenarios/[channel]/devices/page.tsx b/Cunkebao/app/scenarios/[channel]/devices/page.tsx index 735bce48..1972f3d2 100644 --- a/Cunkebao/app/scenarios/[channel]/devices/page.tsx +++ b/Cunkebao/app/scenarios/[channel]/devices/page.tsx @@ -2,13 +2,53 @@ import { useState } from "react" import { useRouter } from "next/navigation" -import { DeviceSelector } from "@/app/components/common/DeviceSelector" import { Button } from "@/components/ui/button" -import { ChevronLeft } from "lucide-react" +import { Badge } from "@/components/ui/badge" +import { DeviceSelectionDialog } from "@/app/components/device-selection-dialog" +import { ChevronLeft, Trash2 } from "lucide-react" + +interface WechatAccount { + avatar: string + nickname: string + wechatId: string +} +interface Device { + id: string + imei: string + remark?: string + wechatAccounts: WechatAccount[] + online: boolean + friendStatus: "正常" | "异常" +} + +// mock 设备数据 +const mockDevices: Device[] = [ + { + id: "aa6d4c2f7b1fe24d04d34f4f409883e6", + imei: "aa6d4c2f7b1fe24d04d34f4f409883e6", + remark: "游戏2 19445", + wechatAccounts: [ + { + avatar: "https://img2.baidu.com/it/u=123456789,123456789&fm=253&fmt=auto&app=138&f=JPEG?w=500&h=500", + nickname: "老钟爹-解放双手,释放时间", + wechatId: "wxid_480es52qsj2812" + }, + { + avatar: "https://img2.baidu.com/it/u=123456789,123456789&fm=253&fmt=auto&app=138&f=JPEG?w=500&h=500", + nickname: "", + wechatId: "w28533368 15375804003" + } + ], + online: true, + friendStatus: "正常" + } +] export default function ScenarioDevicesPage({ params }: { params: { channel: string } }) { const router = useRouter() - const [selectedDevices, setSelectedDevices] = useState([]) + const [isDeviceSelectorOpen, setIsDeviceSelectorOpen] = useState(false) + const [selectedDeviceIds, setSelectedDeviceIds] = useState(mockDevices.map(d=>d.id)) + const [selectedDevices, setSelectedDevices] = useState(mockDevices) // 获取渠道中文名称 const getChannelName = (channel: string) => { @@ -30,14 +70,19 @@ export default function ScenarioDevicesPage({ params }: { params: { channel: str const channelName = getChannelName(params.channel) - const handleSave = async () => { - try { - // 这里应该是实际的API调用来保存选中的设备 - await new Promise((resolve) => setTimeout(resolve, 1000)) - router.back() - } catch (error) { - console.error("保存失败:", error) - } + // 设备选择回填 + const handleDeviceSelect = (deviceIds: string[]) => { + setSelectedDeviceIds(deviceIds) + // 这里用mockDevices过滤,实际应接口获取 + setSelectedDevices(mockDevices.filter(d => deviceIds.includes(d.id))) + setIsDeviceSelectorOpen(false) + } + + // 删除设备 + const handleDelete = (id: string) => { + const newIds = selectedDeviceIds.filter(did => did !== id) + setSelectedDeviceIds(newIds) + setSelectedDevices(selectedDevices.filter(d => d.id !== id)) } return ( @@ -54,20 +99,115 @@ export default function ScenarioDevicesPage({ params }: { params: { channel: str
- +
+ + +
+ + {/* PC端表格 */} +
+ + + + + + + + + + + + {selectedDevices.length === 0 ? ( + + + + ) : ( + selectedDevices.map(device => ( + + + + + + + + )) + )} + +
设备IMEI/备注/手机/设备ID客服微信号在线加友状态操作
暂无已选设备
+ {device.imei} + {device.remark ?
{device.remark}
: null} +
+ {device.wechatAccounts.length === 0 ? ( + - + ) : ( +
+ {device.wechatAccounts.map((wx, idx) => ( +
+ {wx.avatar && avatar} + {wx.nickname} + {wx.wechatId} +
+ ))} +
+ )} +
+ {device.online ? "是" : "否"} + + {device.friendStatus} + + +
+
+ + {/* 移动端卡片式渲染 */} +
+ {selectedDevices.length === 0 ? ( +
暂无已选设备
+ ) : ( + selectedDevices.map(device => ( +
+
{device.imei}
+ {device.remark &&
{device.remark}
} +
+ 客服微信号: + {device.wechatAccounts.length === 0 ? ( + - + ) : ( +
+ {device.wechatAccounts.map((wx, idx) => ( +
+ {wx.avatar && avatar} + {wx.nickname} + {wx.wechatId} +
+ ))} +
+ )} +
+
+ {device.online ? "在线" : "离线"} + {device.friendStatus} + +
+
+ )) + )} +
-
diff --git a/Cunkebao/app/scenarios/new/steps/BasicSettings.tsx b/Cunkebao/app/scenarios/new/steps/BasicSettings.tsx index 4ca99a3f..752280d7 100644 --- a/Cunkebao/app/scenarios/new/steps/BasicSettings.tsx +++ b/Cunkebao/app/scenarios/new/steps/BasicSettings.tsx @@ -21,6 +21,7 @@ import { } from "@/components/ui/dialog" import { toast } from "@/components/ui/use-toast" import { useSearchParams } from "next/navigation" +import { useRouter } from "next/navigation" interface BasicSettingsProps { formData: any @@ -48,7 +49,7 @@ interface PosterSectionProps { onUpload: () => void onSelect: (material: Material) => void uploading: boolean - fileInputRef: React.RefObject + fileInputRef: React.RefObject onFileChange: (event: React.ChangeEvent) => void onPreview: (url: string) => void onRemove: (id: string) => void @@ -58,7 +59,7 @@ interface OrderSectionProps { materials: Material[] onUpload: () => void uploading: boolean - fileInputRef: React.RefObject + fileInputRef: React.RefObject onFileChange: (event: React.ChangeEvent) => void } @@ -66,7 +67,7 @@ interface DouyinSectionProps { materials: Material[] onUpload: () => void uploading: boolean - fileInputRef: React.RefObject + fileInputRef: React.RefObject onFileChange: (event: React.ChangeEvent) => void } @@ -330,6 +331,7 @@ export function BasicSettings({ formData, onChange, onNext, scenarios, loadingSc const [isImportDialogOpen, setIsImportDialogOpen] = useState(false) const [importedTags, setImportedTags] = useState< Array<{ + name: string phone: string wechat: string source?: string @@ -352,25 +354,29 @@ export function BasicSettings({ formData, onChange, onNext, scenarios, loadingSc const [selectedPhoneTags, setSelectedPhoneTags] = useState(formData.phoneTags || []) const [phoneCallType, setPhoneCallType] = useState(formData.phoneCallType || "both") - const fileInputRef = useRef(null) + const fileInputRef = useRef(null) const [uploadingPoster, setUploadingPoster] = useState(false) // 新增不同场景的materials和上传逻辑 const [orderMaterials, setOrderMaterials] = useState([]) const [douyinMaterials, setDouyinMaterials] = useState([]) - const orderFileInputRef = useRef(null) - const douyinFileInputRef = useRef(null) + const orderFileInputRef = useRef(null) + const douyinFileInputRef = useRef(null) const [uploadingOrder, setUploadingOrder] = useState(false) const [uploadingDouyin, setUploadingDouyin] = useState(false) // 新增小程序和链接封面上传相关state和ref const [miniAppCover, setMiniAppCover] = useState(formData.miniAppCover || "") const [uploadingMiniAppCover, setUploadingMiniAppCover] = useState(false) - const miniAppFileInputRef = useRef(null) + const miniAppFileInputRef = useRef(null) const [linkCover, setLinkCover] = useState(formData.linkCover || "") const [uploadingLinkCover, setUploadingLinkCover] = useState(false) - const linkFileInputRef = useRef(null) + const linkFileInputRef = useRef(null) + + const [uploadingOrderTable, setUploadingOrderTable] = useState(false) + const [uploadedOrderTableFile, setUploadedOrderTableFile] = useState(formData.orderTableFileName || "") + const orderTableFileInputRef = useRef(null) const searchParams = useSearchParams() const type = searchParams.get("type") @@ -532,8 +538,9 @@ export function BasicSettings({ formData, onChange, onNext, scenarios, loadingSc const content = e.target?.result as string const rows = content.split("\n").filter((row) => row.trim()) const tags = rows.slice(1).map((row) => { - const [phone, wechat, source, orderAmount, orderDate] = row.split(",") + const [name, phone, wechat, source, orderAmount, orderDate] = row.split(",") return { + name: name.trim(), phone: phone.trim(), wechat: wechat.trim(), source: source?.trim(), @@ -552,7 +559,7 @@ export function BasicSettings({ formData, onChange, onNext, scenarios, loadingSc } const handleDownloadTemplate = () => { - const template = "电话号码,微信号,来源,订单金额,下单日期\n13800138000,wxid_123,抖音,99.00,2024-03-03" + const template = "姓名,电话号码,微信号,来源,订单金额,下单日期\n张三,13800138000,wxid_123,抖音,99.00,2024-03-03" const blob = new Blob([template], { type: "text/csv" }) const url = window.URL.createObjectURL(blob) const a = document.createElement("a") @@ -607,6 +614,7 @@ export function BasicSettings({ formData, onChange, onNext, scenarios, loadingSc const newPoster = { id: `custom_${Date.now()}`, name: result.data.name || '自定义海报', + type: 'poster', preview: result.data.url, } setMaterials(prev => [newPoster, ...prev]) @@ -748,7 +756,43 @@ export function BasicSettings({ formData, onChange, onNext, scenarios, loadingSc } } + const handleUploadOrderTable = () => { + orderTableFileInputRef.current?.click() + } + + const handleOrderTableFileChange = async (event: React.ChangeEvent) => { + const file = event.target.files?.[0] + if (!file) return + setUploadingOrderTable(true) + const formDataObj = new FormData() + formDataObj.append('file', file) + try { + const token = localStorage.getItem('token') + const headers: HeadersInit = {} + if (token) headers['Authorization'] = `Bearer ${token}` + const response = await fetch(`${process.env.NEXT_PUBLIC_API_BASE_URL}/v1/attachment/upload`, { + method: 'POST', + headers, + body: formDataObj, + }) + const result = await response.json() + if (result.code === 200 && result.data?.url) { + setUploadedOrderTableFile(file.name) + onChange({ ...formData, orderTableFile: result.data.url, orderTableFileName: file.name }) + toast({ title: '上传成功', description: '订单表格文件已上传' }) + } else { + toast({ title: '上传失败', description: result.msg || '请重试', variant: 'destructive' }) + } + } catch (e: any) { + toast({ title: '上传失败', description: e?.message || '请重试', variant: 'destructive' }) + } finally { + setUploadingOrderTable(false) + if (orderTableFileInputRef.current) orderTableFileInputRef.current.value = '' + } + } + const renderSceneExtra = () => { + console.log('currentScenario:', currentScenario?.name); switch (currentScenario?.name) { case "海报获客": return ( @@ -844,6 +888,44 @@ export function BasicSettings({ formData, onChange, onNext, scenarios, loadingSc
) + const [saving, setSaving] = useState(false) + + const handleSave = async () => { + if (saving) return // 防止重复点击 + setSaving(true) + try { + // ...原有代码... + const submitData = { + ...formData, + device: formData.selectedDevices || formData.device, + posters: formData.materials || formData.posters, + }; + const { selectedDevices, materials, ...finalData } = submitData; + const res = await api.post("/v1/plan/create", finalData); + if (res.code === 200) { + toast({ + title: "创建成功", + description: "获客计划已创建", + }) + router.push(`/scenarios/${formData.sceneId}`) + } else { + toast({ + title: "创建失败", + description: res.msg || "创建计划失败,请重试", + variant: "destructive", + }) + } + } catch (error: any) { + toast({ + title: "创建失败", + description: error?.message || "创建计划失败,请重试", + variant: "destructive", + }) + } finally { + setSaving(false) + } + } + return ( @@ -1141,56 +1223,34 @@ export function BasicSettings({ formData, onChange, onNext, scenarios, loadingSc
))} - {String(currentScenario?.id) === "order" && ( + {currentScenario?.name === "订单获客" && (
- -
+ +
+
+
-
+ {uploadedOrderTableFile && ( +
已上传:{uploadedOrderTableFile}
+ )} +
支持 CSV、Excel 格式,上传后将文件保存到服务器
- - {importedTags.length > 0 && ( -
-

已导入 {importedTags.length} 条数据

-
- - - - 电话号码 - 微信号 - 来源 - 订单金额 - - - - {importedTags.slice(0, 5).map((tag, index) => ( - - {tag.phone} - {tag.wechat} - {tag.source} - {tag.orderAmount} - - ))} - {importedTags.length > 5 && ( - - - 还有 {importedTags.length - 5} 条数据未显示 - - - )} - -
-
-
- )}
)} {String(formData.scenario) === "weixinqun" && ( @@ -1284,8 +1344,8 @@ export function BasicSettings({ formData, onChange, onNext, scenarios, loadingSc />
- @@ -1405,56 +1465,6 @@ export function BasicSettings({ formData, onChange, onNext, scenarios, loadingSc - - - - - 导入订单标签 - -
-
- -
-
- - - - 电话号码 - 微信号 - 来源 - 订单金额 - 下单日期 - - - - {importedTags.map((tag, index) => ( - - {tag.phone} - {tag.wechat} - {tag.source} - {tag.orderAmount} - {tag.orderDate} - - ))} - -
-
-
- - - - -
-
) } diff --git a/Cunkebao/app/workspace/moments-sync/[id]/page.tsx b/Cunkebao/app/workspace/moments-sync/[id]/page.tsx index badd0812..a2db43a1 100644 --- a/Cunkebao/app/workspace/moments-sync/[id]/page.tsx +++ b/Cunkebao/app/workspace/moments-sync/[id]/page.tsx @@ -26,16 +26,7 @@ interface TaskDetail { startTime: string endTime: string enabled: boolean - devices: { - id: string - name: string - avatar: string - }[] - contentLibraries: { - id: string - name: string - count: number - }[] + config: any lastSyncTime: string createTime: string creator: string @@ -79,7 +70,7 @@ export default function MomentsSyncDetailPage({ params }: { params: { id: string const fetchTaskDetail = async () => { setIsLoading(true) try { - const response = await api.get(`/v1/workbench/moments-records?workbenchId=${params.id}`) + const response = await api.get(`/v1/workbench/detail?id=${params.id}`) if (response.code === 200 && response.data) { setTaskDetail(response.data) @@ -260,33 +251,6 @@ export default function MomentsSyncDetailPage({ params }: { params: { id: string

任务详情

-
- - - - - - - - - 编辑 - - - - 复制 - - - - 删除 - - - -
@@ -296,8 +260,8 @@ export default function MomentsSyncDetailPage({ params }: { params: { id: string

{taskDetail.name}

- - {taskDetail.status === "running" ? "进行中" : "已暂停"} + + {taskDetail.status == 1 ? "进行中" : "已暂停"}
@@ -323,28 +287,24 @@ export default function MomentsSyncDetailPage({ params }: { params: { id: string
账号类型
-
{taskDetail.accountType === 1 ? "业务号" : "人设号"}
-
-
-
同步类型
-
{taskDetail.syncType === 1 ? "循环同步" : "实时更新"}
+
{taskDetail.config.accountType === 1 ? "业务号" : "人设号"}
同步间隔
-
{taskDetail.syncInterval} 分钟
+
{taskDetail.config.syncInterval} 分钟
每日同步数量
-
{taskDetail.syncCount} 条
+
{taskDetail.config.syncCount} 条
允许发布时间段
-
{taskDetail.startTime} - {taskDetail.endTime}
+
{taskDetail.config.startTime} - {taskDetail.config.endTime}
内容库
- {taskDetail.contentLibraries.map((lib) => ( + {taskDetail.config.contentLibraries.map((lib) => ( {lib.name} @@ -357,24 +317,24 @@ export default function MomentsSyncDetailPage({ params }: { params: { id: string - {taskDetail.devices.length === 0 ? ( + {taskDetail.config.deviceList.length === 0 ? (
暂无关联设备
) : (
- {taskDetail.devices.map((device) => ( + {taskDetail.config.deviceList.map((device) => (
{device.avatar ? ( - {device.name} + {device.nickname} ) : (
- {device.name.charAt(0)} + {device.nickname}
)}
-
{device.name}
-
ID: {device.id}
+
{device.nickname}
+
{device.alias || device.wechatId}
))} diff --git a/Server/application/cunkebao/config/route.php b/Server/application/cunkebao/config/route.php index ff4709e6..fc6f5588 100644 --- a/Server/application/cunkebao/config/route.php +++ b/Server/application/cunkebao/config/route.php @@ -40,7 +40,7 @@ Route::group('v1/', function () { Route::get('scenes-detail', 'app\cunkebao\controller\plan\GetPlanSceneListV1Controller@detail'); Route::post('create', 'app\cunkebao\controller\plan\PostCreateAddFriendPlanV1Controller@index'); Route::get('list', 'app\cunkebao\controller\plan\PlanSceneV1Controller@index'); - Route::get('copy', 'app\cunkebao\controller\plan\PlanSceneV1Controller@copy'); + Route::get('copy', 'app\cunkebao\controller\plan\GetCreateAddFriendPlanV1Controller@copy'); Route::delete('delete', 'app\cunkebao\controller\plan\PlanSceneV1Controller@delete'); Route::post('updateStatus', 'app\cunkebao\controller\plan\PlanSceneV1Controller@updateStatus'); Route::get('detail', 'app\cunkebao\controller\plan\GetAddFriendPlanDetailV1Controller@index'); diff --git a/Server/application/cunkebao/controller/WorkbenchController.php b/Server/application/cunkebao/controller/WorkbenchController.php index 86d80095..67eb0b9b 100644 --- a/Server/application/cunkebao/controller/WorkbenchController.php +++ b/Server/application/cunkebao/controller/WorkbenchController.php @@ -2,6 +2,8 @@ namespace app\cunkebao\controller; +use app\common\model\Device as DeviceModel; +use app\common\model\DeviceWechatLogin as DeviceWechatLoginModel; use app\cunkebao\model\Workbench; use app\cunkebao\model\WorkbenchAutoLike; use app\cunkebao\model\WorkbenchMomentsSync; @@ -236,7 +238,12 @@ class WorkbenchController extends Controller $item->config = $item->momentsSync; $item->config->devices = json_decode($item->config->devices, true); $item->config->contentLibraries = json_decode($item->config->contentLibraries, true); - + //同步记录 + $sendNum = Db::name('workbench_moments_sync_item')->where(['workbenchId' => $item->id])->count(); + $item->syncCount = $sendNum; + $lastTime = Db::name('workbench_moments_sync_item')->where(['workbenchId' => $item->id])->order('id DESC')->value('createTime'); + $item->lastSyncTime = !empty($lastTime) ? date('Y-m-d H:i',$lastTime) : '--'; + // 获取内容库名称 if (!empty($item->config->contentLibraries)) { $libraryNames = ContentLibrary::where('id', 'in', $item->config->contentLibraries) @@ -426,6 +433,42 @@ class WorkbenchController extends Controller $workbench->config = $workbench->momentsSync; $workbench->config->devices = json_decode($workbench->config->devices, true); $workbench->config->contentLibraries = json_decode($workbench->config->contentLibraries, true); + + //同步记录 + $sendNum = Db::name('workbench_moments_sync_item')->where(['workbenchId' => $workbench->id])->count(); + $workbench->syncCount = $sendNum; + $lastTime = Db::name('workbench_moments_sync_item')->where(['workbenchId' => $workbench->id])->order('id DESC')->value('createTime'); + $workbench->lastSyncTime = !empty($lastTime) ? date('Y-m-d H:i',$lastTime) : '--'; + + // 获取内容库名称 + if (!empty($workbench->config->contentLibraries)) { + $libraryNames = ContentLibrary::where('id', 'in', $workbench->config->contentLibraries) + ->select(); + $workbench->config->contentLibraries = $libraryNames; + } else { + $workbench->config->contentLibraryNames = []; + } + + if(!empty($workbench->config->devices)){ + $deviceList = DeviceModel::alias('d') + ->field([ + 'd.id', 'd.imei', 'd.memo', 'd.alive', + 'l.wechatId', + 'a.nickname', 'a.alias', 'a.avatar','a.alias' + ]) + ->leftJoin('device_wechat_login l', 'd.id = l.deviceId and l.alive =' . DeviceWechatLoginModel::ALIVE_WECHAT_ACTIVE . ' and l.companyId = d.companyId') + ->leftJoin('wechat_account a', 'l.wechatId = a.wechatId') + ->whereIn('d.id',$workbench->config->devices) + ->order('d.id desc') + ->select(); + $workbench->config->deviceList = $deviceList; + }else{ + $workbench->config->deviceList = []; + } + + + + unset($workbench->momentsSync,$workbench->moments_sync); } break; diff --git a/Server/application/cunkebao/controller/plan/PlanSceneV1Controller.php b/Server/application/cunkebao/controller/plan/PlanSceneV1Controller.php index 3d958815..f43428e3 100644 --- a/Server/application/cunkebao/controller/plan/PlanSceneV1Controller.php +++ b/Server/application/cunkebao/controller/plan/PlanSceneV1Controller.php @@ -54,18 +54,18 @@ class PlanSceneV1Controller extends BaseController $val['acquiredCount'] = Db::name('task_customer')->where('task_id',$val['id'])->count(); $val['addedCount'] = Db::name('task_customer')->where('task_id',$val['id'])->whereIn('status',[1,2,3,4])->count(); $val['passCount'] = Db::name('task_customer')->where('task_id',$val['id'])->where('status',4)->count(); - $val['passRate'] = 0; if(!empty($val['passCount']) && !empty($val['addedCount'])){ - $passRate = ($val['addedCount'] / $val['passCount']) * 100; + $passRate = ($val['passCount'] / $val['addedCount']) * 100; $val['passRate'] = number_format($passRate,2); } + $lastTime = Db::name('task_customer')->where(['task_id'=>$val['id']])->max('updated_at'); + $val['lastUpdated'] = !empty($lastTime) ? date('Y-m-d H:i', $lastTime) : '--'; + + } unset($val); - - - return ResponseHelper::success([ 'total' => $total, 'list' => $list @@ -75,41 +75,6 @@ class PlanSceneV1Controller extends BaseController } } - /** - * 拷贝计划任务 - * - * @return \think\response\Json - */ - public function copy() - { - try { - $params = $this->request->param(); - $planId = isset($params['planId']) ? intval($params['planId']) : 0; - - if ($planId <= 0) { - return ResponseHelper::error('计划ID不能为空', 400); - } - - $plan = Db::name('customer_acquisition_task')->where('id', $planId)->find(); - if (!$plan) { - return ResponseHelper::error('计划不存在', 404); - } - - unset($plan['id']); - $plan['name'] = $plan['name'] . ' (拷贝)'; - $plan['createTime'] = time(); - $plan['updateTime'] = time(); - - $newPlanId = Db::name('customer_acquisition_task')->insertGetId($plan); - if (!$newPlanId) { - return ResponseHelper::error('拷贝计划失败', 500); - } - - return ResponseHelper::success(['planId' => $newPlanId], '拷贝计划任务成功'); - } catch (\Exception $e) { - return ResponseHelper::error('系统错误: ' . $e->getMessage(), 500); - } - } /** * 删除计划任务 @@ -163,4 +128,251 @@ class PlanSceneV1Controller extends BaseController return ResponseHelper::error('系统错误: ' . $e->getMessage(), 500); } } + + /** + * 获取获客计划设备列表 + * + * @return \think\response\Json + */ + public function getPlanDevices() + { + try { + $params = $this->request->param(); + $planId = isset($params['planId']) ? intval($params['planId']) : 0; + $page = isset($params['page']) ? intval($params['page']) : 1; + $limit = isset($params['limit']) ? intval($params['limit']) : 10; + $deviceStatus = isset($params['deviceStatus']) ? $params['deviceStatus'] : ''; + $searchKeyword = isset($params['searchKeyword']) ? trim($params['searchKeyword']) : ''; + + // 验证计划ID + if ($planId <= 0) { + return ResponseHelper::error('计划ID不能为空', 400); + } + + // 验证计划是否存在且用户有权限 + $plan = Db::name('customer_acquisition_task') + ->where([ + 'id' => $planId, + 'deleteTime' => 0, + 'companyId' => $this->getUserInfo('companyId') + ]) + ->find(); + + if (!$plan) { + return ResponseHelper::error('计划不存在或无权限访问', 404); + } + + // 如果是管理员,需要验证用户权限 + if ($this->getUserInfo('isAdmin')) { + $userPlan = Db::name('customer_acquisition_task') + ->where([ + 'id' => $planId, + 'userId' => $this->getUserInfo('id') + ]) + ->find(); + + if (!$userPlan) { + return ResponseHelper::error('您没有权限访问该计划', 403); + } + } + + // 构建查询条件 + $where = [ + 'pt.plan_id' => $planId, + 'd.deleteTime' => 0, + 'd.companyId' => $this->getUserInfo('companyId') + ]; + + // 设备状态筛选 + if (!empty($deviceStatus)) { + $where['d.alive'] = $deviceStatus; + } + + // 搜索关键词 + $searchWhere = []; + if (!empty($searchKeyword)) { + $searchWhere[] = ['d.imei', 'like', "%{$searchKeyword}%"]; + $searchWhere[] = ['d.memo', 'like', "%{$searchKeyword}%"]; + } + + // 查询设备总数 + $totalQuery = Db::name('plan_task_device')->alias('pt') + ->join('device d', 'pt.device_id = d.id') + ->where($where); + + if (!empty($searchWhere)) { + $totalQuery->where(function ($query) use ($searchWhere) { + foreach ($searchWhere as $condition) { + $query->whereOr($condition[0], $condition[1], $condition[2]); + } + }); + } + + $total = $totalQuery->count(); + + // 查询设备列表 + $listQuery = Db::name('plan_task_device')->alias('pt') + ->join('device d', 'pt.device_id = d.id') + ->field([ + 'd.id', + 'd.imei', + 'd.memo', + 'd.alive', + 'd.extra', + 'd.createTime', + 'd.updateTime', + 'pt.status as plan_device_status', + 'pt.createTime as assign_time' + ]) + ->where($where) + ->order('pt.createTime', 'desc'); + + if (!empty($searchWhere)) { + $listQuery->where(function ($query) use ($searchWhere) { + foreach ($searchWhere as $condition) { + $query->whereOr($condition[0], $condition[1], $condition[2]); + } + }); + } + + $list = $listQuery->page($page, $limit)->select(); + + // 处理设备数据 + foreach ($list as &$device) { + // 格式化时间 + $device['createTime'] = date('Y-m-d H:i:s', $device['createTime']); + $device['updateTime'] = date('Y-m-d H:i:s', $device['updateTime']); + $device['assign_time'] = date('Y-m-d H:i:s', $device['assign_time']); + + // 解析设备额外信息 + if (!empty($device['extra'])) { + $extra = json_decode($device['extra'], true); + $device['battery'] = isset($extra['battery']) ? intval($extra['battery']) : 0; + $device['device_info'] = $extra; + } else { + $device['battery'] = 0; + $device['device_info'] = []; + } + + // 设备状态文本 + $device['alive_text'] = $this->getDeviceStatusText($device['alive']); + $device['plan_device_status_text'] = $this->getPlanDeviceStatusText($device['plan_device_status']); + + // 获取设备当前微信登录信息 + $wechatLogin = Db::name('device_wechat_login') + ->where([ + 'deviceId' => $device['id'], + 'companyId' => $this->getUserInfo('companyId'), + 'alive' => 1 + ]) + ->order('createTime', 'desc') + ->find(); + + $device['current_wechat'] = $wechatLogin ? [ + 'wechatId' => $wechatLogin['wechatId'], + 'nickname' => $wechatLogin['nickname'] ?? '', + 'loginTime' => date('Y-m-d H:i:s', $wechatLogin['createTime']) + ] : null; + + // 获取设备在该计划中的任务统计 + $device['task_stats'] = $this->getDeviceTaskStats($device['id'], $planId); + + // 移除原始extra字段 + unset($device['extra']); + } + unset($device); + + return ResponseHelper::success([ + 'total' => $total, + 'list' => $list, + 'plan_info' => [ + 'id' => $plan['id'], + 'name' => $plan['name'], + 'status' => $plan['status'] + ] + ], '获取计划设备列表成功'); + + } catch (\Exception $e) { + return ResponseHelper::error('系统错误: ' . $e->getMessage(), 500); + } + } + + /** + * 获取设备状态文本 + * + * @param int $status + * @return string + */ + private function getDeviceStatusText($status) + { + $statusMap = [ + 0 => '离线', + 1 => '在线', + 2 => '忙碌', + 3 => '故障' + ]; + return isset($statusMap[$status]) ? $statusMap[$status] : '未知'; + } + + /** + * 获取计划设备状态文本 + * + * @param int $status + * @return string + */ + private function getPlanDeviceStatusText($status) + { + $statusMap = [ + 0 => '待分配', + 1 => '已分配', + 2 => '执行中', + 3 => '已完成', + 4 => '已暂停', + 5 => '已取消' + ]; + return isset($statusMap[$status]) ? $statusMap[$status] : '未知'; + } + + /** + * 获取设备在指定计划中的任务统计 + * + * @param int $deviceId + * @param int $planId + * @return array + */ + private function getDeviceTaskStats($deviceId, $planId) + { + // 获取该设备在计划中的任务总数 + $totalTasks = Db::name('task_customer') + ->where([ + 'task_id' => $planId, + 'device_id' => $deviceId + ]) + ->count(); + + // 获取已完成的任务数 + $completedTasks = Db::name('task_customer') + ->where([ + 'task_id' => $planId, + 'device_id' => $deviceId, + 'status' => 4 + ]) + ->count(); + + // 获取进行中的任务数 + $processingTasks = Db::name('task_customer') + ->where([ + 'task_id' => $planId, + 'device_id' => $deviceId, + 'status' => ['in', [1, 2, 3]] + ]) + ->count(); + + return [ + 'total_tasks' => $totalTasks, + 'completed_tasks' => $completedTasks, + 'processing_tasks' => $processingTasks, + 'completion_rate' => $totalTasks > 0 ? round(($completedTasks / $totalTasks) * 100, 2) : 0 + ]; + } } \ No newline at end of file diff --git a/Server/application/cunkebao/controller/plan/PostCreateAddFriendPlanV1Controller.php b/Server/application/cunkebao/controller/plan/PostCreateAddFriendPlanV1Controller.php index efa53fa9..1d3ab042 100644 --- a/Server/application/cunkebao/controller/plan/PostCreateAddFriendPlanV1Controller.php +++ b/Server/application/cunkebao/controller/plan/PostCreateAddFriendPlanV1Controller.php @@ -121,6 +121,7 @@ class PostCreateAddFriendPlanV1Controller extends Controller try { + Db::startTrans(); // 插入数据 $planId = Db::name('customer_acquisition_task')->insertGetId($data); @@ -220,7 +221,7 @@ class PostCreateAddFriendPlanV1Controller extends Controller $existingPhones = []; if (!empty($phones)) { $existing = Db::name('task_customer') - ->where('task_id', $params['planId']) + ->where('task_id', $planId) ->where('phone', 'in', $phones) ->field('phone') ->select(); @@ -233,7 +234,7 @@ class PostCreateAddFriendPlanV1Controller extends Controller $phone = !empty($row['phone']) ? $row['phone'] : $row['wechat']; if (!empty($phone) && !in_array($phone, $existingPhones)) { $newData[] = [ - 'task_id' => $params['planId'], + 'task_id' => $planId, 'name' => $row['name'] ?? '', 'source' => $row['source'] ?? '', 'phone' => $phone, @@ -254,6 +255,7 @@ class PostCreateAddFriendPlanV1Controller extends Controller } } + Db::commit(); return ResponseHelper::success(['planId' => $planId], '添加计划任务成功'); diff --git a/Server/extend/WeChatDeviceApi/Adapters/ChuKeBao/Adapter.php b/Server/extend/WeChatDeviceApi/Adapters/ChuKeBao/Adapter.php index bc17565b..2db45e6b 100644 --- a/Server/extend/WeChatDeviceApi/Adapters/ChuKeBao/Adapter.php +++ b/Server/extend/WeChatDeviceApi/Adapters/ChuKeBao/Adapter.php @@ -153,14 +153,16 @@ class Adapter implements WeChatServiceInterface public function handleCustomerTaskWithStatusIsNew(int $current_worker_id, int $process_count_for_status_0) { - $tasks = Db::name('task_customer') - ->where('status', 0) - ->whereRaw("id % $process_count_for_status_0 = {$current_worker_id}") - ->limit(50) + + $tasks = Db::name('customer_acquisition_task')->alias('task') + ->join('task_customer customer','task.id=customer.task_id') + ->where(['task.status' => 1,'customer.status'=>0,'task.deleteTime' => 0]) + ->whereRaw("customer.id % $process_count_for_status_0 = {$current_worker_id}") ->order('id DESC') ->select(); + if ($tasks) { foreach ($tasks as $task) { From 2f0fd2e10f2470c83470cdb4770e2e84d56e3bac Mon Sep 17 00:00:00 2001 From: wong <106998207@qq.com> Date: Mon, 7 Jul 2025 10:11:13 +0800 Subject: [PATCH 09/29] =?UTF-8?q?=E6=B7=BB=E5=8A=A0=E5=A5=BD=E5=8F=8B?= =?UTF-8?q?=E4=BC=98=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Adapters/ChuKeBao/Adapter.php | 25 +++++++++++++------ 1 file changed, 18 insertions(+), 7 deletions(-) diff --git a/Server/extend/WeChatDeviceApi/Adapters/ChuKeBao/Adapter.php b/Server/extend/WeChatDeviceApi/Adapters/ChuKeBao/Adapter.php index 2db45e6b..e40abd60 100644 --- a/Server/extend/WeChatDeviceApi/Adapters/ChuKeBao/Adapter.php +++ b/Server/extend/WeChatDeviceApi/Adapters/ChuKeBao/Adapter.php @@ -154,18 +154,29 @@ class Adapter implements WeChatServiceInterface public function handleCustomerTaskWithStatusIsNew(int $current_worker_id, int $process_count_for_status_0) { - $tasks = Db::name('customer_acquisition_task')->alias('task') - ->join('task_customer customer','task.id=customer.task_id') - ->where(['task.status' => 1,'customer.status'=>0,'task.deleteTime' => 0]) - ->whereRaw("customer.id % $process_count_for_status_0 = {$current_worker_id}") - ->order('id DESC') + $task = Db::name('customer_acquisition_task') + ->where(['status' => 1,'deleteTime' => 0]) ->select(); + if (empty($task)) { + return false; + } + + $taskData = []; + foreach ($task as $item) { + $tasks = Db::name('task_customer') + ->where(['status'=> 0,'task_id'=>$item['id']]) + ->whereRaw("id % $process_count_for_status_0 = {$current_worker_id}") + ->order('id DESC') + ->limit(20) + ->select(); + $taskData = array_merge($taskData, $tasks); + } - if ($tasks) { + if ($taskData) { - foreach ($tasks as $task) { + foreach ($taskData as $task) { $task_id = $task['task_id']; From 9349fdcbeac4a15f045ad09fbe1e44961c713637 Mon Sep 17 00:00:00 2001 From: wong <106998207@qq.com> Date: Mon, 7 Jul 2025 14:54:01 +0800 Subject: [PATCH 10/29] =?UTF-8?q?=E4=BB=A3=E7=A0=81=E4=BC=98=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../plan/PostCreateAddFriendPlanV1Controller.php | 9 +++++++++ .../plan/PostUpdateAddFriendPlanV1Controller.php | 9 +++++++++ .../extend/WeChatDeviceApi/Adapters/ChuKeBao/Adapter.php | 6 +++--- 3 files changed, 21 insertions(+), 3 deletions(-) diff --git a/Server/application/cunkebao/controller/plan/PostCreateAddFriendPlanV1Controller.php b/Server/application/cunkebao/controller/plan/PostCreateAddFriendPlanV1Controller.php index 1d3ab042..011d3fae 100644 --- a/Server/application/cunkebao/controller/plan/PostCreateAddFriendPlanV1Controller.php +++ b/Server/application/cunkebao/controller/plan/PostCreateAddFriendPlanV1Controller.php @@ -88,16 +88,25 @@ class PostCreateAddFriendPlanV1Controller extends Controller // 其余参数归为sceneConf $sceneConf = $params; unset( + $sceneConf['id'], + $sceneConf['apiKey'], + $sceneConf['userId'], + $sceneConf['status'], + $sceneConf['planId'], $sceneConf['name'], $sceneConf['sceneId'], $sceneConf['messagePlans'], $sceneConf['scenarioTags'], $sceneConf['customTags'], $sceneConf['device'], + $sceneConf['orderTableFileName'], + $sceneConf['userInfo'], + $sceneConf['textUrl'], $sceneConf['remarkType'], $sceneConf['greeting'], $sceneConf['addFriendInterval'], $sceneConf['startTime'], + $sceneConf['orderTableFile'], $sceneConf['endTime'] ); diff --git a/Server/application/cunkebao/controller/plan/PostUpdateAddFriendPlanV1Controller.php b/Server/application/cunkebao/controller/plan/PostUpdateAddFriendPlanV1Controller.php index 832fd208..b47c4faa 100644 --- a/Server/application/cunkebao/controller/plan/PostUpdateAddFriendPlanV1Controller.php +++ b/Server/application/cunkebao/controller/plan/PostUpdateAddFriendPlanV1Controller.php @@ -65,6 +65,10 @@ class PostUpdateAddFriendPlanV1Controller extends Controller // 其余参数归为sceneConf $sceneConf = $params; unset( + $sceneConf['id'], + $sceneConf['apiKey'], + $sceneConf['userId'], + $sceneConf['status'], $sceneConf['planId'], $sceneConf['name'], $sceneConf['sceneId'], @@ -72,10 +76,14 @@ class PostUpdateAddFriendPlanV1Controller extends Controller $sceneConf['scenarioTags'], $sceneConf['customTags'], $sceneConf['device'], + $sceneConf['orderTableFileName'], + $sceneConf['userInfo'], + $sceneConf['textUrl'], $sceneConf['remarkType'], $sceneConf['greeting'], $sceneConf['addFriendInterval'], $sceneConf['startTime'], + $sceneConf['orderTableFile'], $sceneConf['endTime'] ); @@ -90,6 +98,7 @@ class PostUpdateAddFriendPlanV1Controller extends Controller 'updateTime'=> time(), ]; + try { // 更新数据 diff --git a/Server/extend/WeChatDeviceApi/Adapters/ChuKeBao/Adapter.php b/Server/extend/WeChatDeviceApi/Adapters/ChuKeBao/Adapter.php index e40abd60..c41f1ee5 100644 --- a/Server/extend/WeChatDeviceApi/Adapters/ChuKeBao/Adapter.php +++ b/Server/extend/WeChatDeviceApi/Adapters/ChuKeBao/Adapter.php @@ -177,14 +177,13 @@ class Adapter implements WeChatServiceInterface if ($taskData) { foreach ($taskData as $task) { - $task_id = $task['task_id']; - $task_info = $this->getCustomerAcquisitionTask($task_id); + if (empty($task_info['status']) || empty($task_info['reqConf']) || empty($task_info['reqConf']['device'])) { continue; } - + //筛选出设备在线微信在线 $wechatIdAccountIdMap = $this->getWeChatIdsAccountIdsMapByDeviceIds($task_info['reqConf']['device']); if (empty($wechatIdAccountIdMap)) { @@ -690,6 +689,7 @@ class Adapter implements WeChatServiceInterface } $records = Db::table('s2_wechat_account') ->where('deviceAlive', 1) + ->where('wechatAlive', 1) ->where('currentDeviceId', 'in', $deviceIds) ->field('id,wechatId') ->column('id,wechatId'); From c0b88eb96713f9f64b27fe40cc34094cee0934f6 Mon Sep 17 00:00:00 2001 From: wong <106998207@qq.com> Date: Mon, 7 Jul 2025 15:13:55 +0800 Subject: [PATCH 11/29] =?UTF-8?q?=E4=BB=A3=E7=A0=81=E6=8F=90=E4=BA=A4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Server/extend/WeChatDeviceApi/Adapters/ChuKeBao/Adapter.php | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/Server/extend/WeChatDeviceApi/Adapters/ChuKeBao/Adapter.php b/Server/extend/WeChatDeviceApi/Adapters/ChuKeBao/Adapter.php index c41f1ee5..b0f38ef4 100644 --- a/Server/extend/WeChatDeviceApi/Adapters/ChuKeBao/Adapter.php +++ b/Server/extend/WeChatDeviceApi/Adapters/ChuKeBao/Adapter.php @@ -611,6 +611,11 @@ class Adapter implements WeChatServiceInterface if (empty($wechatId)) { return false; } + //强制请求添加好友的列表 + $friendController = new FriendTaskController(); + $friendController->getlist(0,50); + + $record = $this->getLatestFriendTask($wechatId); if (empty($record)) { From 2d4a1ee6ce50ccb71fa74e627e12299df3eb04a7 Mon Sep 17 00:00:00 2001 From: wong <106998207@qq.com> Date: Mon, 7 Jul 2025 16:06:03 +0800 Subject: [PATCH 12/29] =?UTF-8?q?=E4=BB=A3=E7=A0=81=E4=BC=98=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../extend/WeChatDeviceApi/Adapters/ChuKeBao/Adapter.php | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/Server/extend/WeChatDeviceApi/Adapters/ChuKeBao/Adapter.php b/Server/extend/WeChatDeviceApi/Adapters/ChuKeBao/Adapter.php index b0f38ef4..2a8914f8 100644 --- a/Server/extend/WeChatDeviceApi/Adapters/ChuKeBao/Adapter.php +++ b/Server/extend/WeChatDeviceApi/Adapters/ChuKeBao/Adapter.php @@ -247,8 +247,10 @@ class Adapter implements WeChatServiceInterface { $tasks = Db::name('task_customer') - ->whereIn('status', [1,2,3]) + ->whereIn('status', [1,2]) + ->where('updated_at', '>=', (time() - 86400 * 3)) ->limit(50) + ->order('updated_at DESC') ->select(); if (empty($tasks)) { @@ -284,15 +286,14 @@ class Adapter implements WeChatServiceInterface - if ($passedWeChatId && !empty($task_info['msgConf'])) { + Db::name('task_customer') ->where('id', $task['id']) ->update(['status' => 4, 'updated_at' => time()]); $wechatFriendRecord = $this->getWeChatAccoutIdAndFriendIdByWeChatIdAndFriendPhone($passedWeChatId, $task['phone']); $msgConf = is_string($task_info['msgConf']) ? json_decode($task_info['msgConf'], 1) : $task_info['msgConf']; - $wechatFriendRecord && $this->sendMsgToFriend($wechatFriendRecord['id'], $wechatFriendRecord['wechatAccountId'], $msgConf); } else { From 0b81d1db0e139f356a2765b28d9dc153bd0d695b Mon Sep 17 00:00:00 2001 From: wong <106998207@qq.com> Date: Mon, 7 Jul 2025 17:00:52 +0800 Subject: [PATCH 13/29] =?UTF-8?q?=E5=85=B3=E8=81=94=E8=B4=A6=E5=8F=B7?= =?UTF-8?q?=E7=8A=B6=E6=80=81=E4=BC=98=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../wechat/GetWechatsRelatedDeviceV1Controller.php | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/Server/application/cunkebao/controller/wechat/GetWechatsRelatedDeviceV1Controller.php b/Server/application/cunkebao/controller/wechat/GetWechatsRelatedDeviceV1Controller.php index 92d20ce6..0d8d4583 100644 --- a/Server/application/cunkebao/controller/wechat/GetWechatsRelatedDeviceV1Controller.php +++ b/Server/application/cunkebao/controller/wechat/GetWechatsRelatedDeviceV1Controller.php @@ -9,6 +9,7 @@ use app\common\model\WechatAccount as WechatAccountModel; use app\common\model\WechatFriendShip as WechatFriendShipModel; use app\cunkebao\controller\BaseController; use library\ResponseHelper; +use think\Db; use think\model\Collection as ResultCollection; /** @@ -100,9 +101,12 @@ class GetWechatsRelatedDeviceV1Controller extends BaseController * @param string $wechatId * @return string */ - protected function getWechatAliveText(string $wechatId): string + protected function getWechatAlive(string $wechatId): string { - return 1 ? '正常' : '异常'; + $wechat_account = Db::table('s2_wechat_account') + ->where('wechatId', $wechatId) + ->value('wechatAlive'); + return !empty($wechat_account) ? $wechat_account : 0; } /** @@ -140,7 +144,9 @@ class GetWechatsRelatedDeviceV1Controller extends BaseController $account->lastActive = $this->getWechatLastActiveTime($account->wechatId); $account->statusText = $this->getWechatStatusText($account->wechatId); $account->totalFriend = $this->getCountFriend($account->wechatId); - $account->wechatAliveText = $this->getWechatAliveText($account->wechatId); + $getWechatAlive = $this->getWechatAlive($account->wechatId); + $account->wechatAlive = $getWechatAlive; + $account->wechatAliveText = !empty($getWechatAlive) ? '正常' : '异常'; } return $collection->toArray(); From 7c82a3d2ccb12012ee8948d10f5ded044e04d0cb Mon Sep 17 00:00:00 2001 From: wong <106998207@qq.com> Date: Tue, 8 Jul 2025 09:52:59 +0800 Subject: [PATCH 14/29] =?UTF-8?q?=E9=A2=84=E9=98=B2=E6=89=A7=E8=A1=8C?= =?UTF-8?q?=E7=82=B9=E8=B5=9E=E3=80=81=E9=87=87=E9=9B=86=E6=9C=8B=E5=8F=8B?= =?UTF-8?q?=E5=9C=88=E7=AD=89=E5=88=87=E6=8D=A2=E5=A5=BD=E5=8F=8B=E6=97=A0?= =?UTF-8?q?=E6=B3=95=E5=88=87=E6=8D=A2=E5=9B=9E=E6=9D=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Server/application/command.php | 1 + .../command/SwitchFriendsCommand.php | 82 +++++++++++++++++++ .../controller/ContentLibraryController.php | 20 +++++ Server/application/job/WechatMomentsJob.php | 22 ++++- .../application/job/WorkbenchAutoLikeJob.php | 20 ++++- 5 files changed, 143 insertions(+), 2 deletions(-) create mode 100644 Server/application/command/SwitchFriendsCommand.php diff --git a/Server/application/command.php b/Server/application/command.php index 2dcd3178..b8fb90e2 100644 --- a/Server/application/command.php +++ b/Server/application/command.php @@ -32,4 +32,5 @@ return [ 'sync:wechatData' => 'app\command\SyncWechatDataToCkbTask', // 同步微信数据到存客宝 'sync:allFriends' => 'app\command\SyncAllFriendsCommand', // 同步所有在线好友 'workbench:trafficDistribute' => 'app\command\WorkbenchTrafficDistributeCommand', // 工作台流量分发任务 + 'switch:friends' => 'app\command\SwitchFriendsCommand', // 切换好友 ]; diff --git a/Server/application/command/SwitchFriendsCommand.php b/Server/application/command/SwitchFriendsCommand.php new file mode 100644 index 00000000..38575b9b --- /dev/null +++ b/Server/application/command/SwitchFriendsCommand.php @@ -0,0 +1,82 @@ +setName('switch:friends') + ->setDescription('切换好友命令'); + } + + protected function execute(Input $input, Output $output) + { + $cacheKey = 'allotWechatFriend'; + $now = time(); + $maxRetry = 5; + $retry = 0; + $switchedIds = []; + + + do { + $friends = Cache::get($cacheKey, []); + $original = $friends; + + $toSwitch = []; + foreach ($friends as $friend) { + if (isset($friend['time']) && $friend['time'] > $now) { + $toSwitch[] = $friend; + } + } + + if (empty($toSwitch)) { + $output->writeln('没有需要切换的好友'); + return; + } + + $automaticAssign = new AutomaticAssign(); + foreach ($toSwitch as $friend) { + $automaticAssign->allotWechatFriend([ + 'wechatFriendId' => $friend['friendId'], + 'toAccountId' => $friend['accountId'], + ], true); + $output->writeln('切换好友:' . $friend['friendId'] . ' 到账号:' . $friend['accountId']); + $switchedIds[] = $friend['friendId']; + } + + // 过滤掉已切换的,保留未切换和新进来的 + $newFriends = Cache::get($cacheKey, []); + $updated = []; + foreach ($newFriends as $friend) { + if (!in_array($friend['friendId'], $switchedIds)) { + $updated[] = $friend; + } + } + + // 按time升序排序 + usort($updated, function($a, $b) { + return ($a['time'] ?? 0) <=> ($b['time'] ?? 0); + }); + + $success = Cache::set($cacheKey, $updated); + $retry++; + } while (!$success && $retry < $maxRetry); + + $output->writeln('切换完成,缓存已更新并排序'); + } + +} \ No newline at end of file diff --git a/Server/application/cunkebao/controller/ContentLibraryController.php b/Server/application/cunkebao/controller/ContentLibraryController.php index a18327bf..8fdf96c9 100644 --- a/Server/application/cunkebao/controller/ContentLibraryController.php +++ b/Server/application/cunkebao/controller/ContentLibraryController.php @@ -7,6 +7,7 @@ use app\cunkebao\model\ContentItem; use think\Controller; use think\Db; use app\api\controller\WebSocketController; +use think\facade\Cache; use think\facade\Env; use app\api\controller\AutomaticAssign; @@ -893,6 +894,25 @@ class ContentLibraryController extends Controller //执行切换好友命令 $automaticAssign = new AutomaticAssign(); $automaticAssign->allotWechatFriend(['wechatFriendId' => $friend['id'],'toAccountId' => $toAccountId],true); + + //记录切换好友 + $cacheKey = 'allotWechatFriend'; + $cacheFriend = $friend; + $cacheFriend['time'] = time() + 120; + $maxRetry = 5; + $retry = 0; + do { + $cacheFriendData = Cache::get($cacheKey, []); + // 去重:移除同friendId的旧数据 + $cacheFriendData = array_filter($cacheFriendData, function($item) use ($cacheFriend) { + return $item['friendId'] !== $cacheFriend['friendId']; + }); + $cacheFriendData[] = $cacheFriend; + $success = Cache::set($cacheKey, $cacheFriendData); + $retry++; + } while (!$success && $retry < $maxRetry); + + //执行采集朋友圈命令 $webSocket = new WebSocketController(['userName' => $username,'password' => $password,'accountId' => $toAccountId]); $webSocket->getMoments(['wechatFriendId' => $friend['id'],'wechatAccountId' => $friend['wechatAccountId']]); diff --git a/Server/application/job/WechatMomentsJob.php b/Server/application/job/WechatMomentsJob.php index 9042783b..04cedafb 100644 --- a/Server/application/job/WechatMomentsJob.php +++ b/Server/application/job/WechatMomentsJob.php @@ -24,7 +24,6 @@ class WechatMomentsJob $toAccountId = Db::name('users')->where('account',$username)->value('s2_accountId'); }else{ Log::error("没有账号配置"); - Cache::rm($queueLockKey); return; } @@ -48,6 +47,27 @@ class WechatMomentsJob $automaticAssign = new AutomaticAssign(); $automaticAssign->allotWechatFriend(['wechatFriendId' => $friend['friendId'], 'toAccountId' => $toAccountId], true); + //记录切换好友 + $cacheKey = 'allotWechatFriend'; + $cacheFriend = $friend; + $cacheFriend['time'] = time() + 120; + $maxRetry = 5; + $retry = 0; + do { + $cacheFriendData = Cache::get($cacheKey, []); + // 去重:移除同friendId的旧数据 + $cacheFriendData = array_filter($cacheFriendData, function($item) use ($cacheFriend) { + return $item['friendId'] !== $cacheFriend['friendId']; + }); + $cacheFriendData[] = $cacheFriend; + $success = Cache::set($cacheKey, $cacheFriendData); + $retry++; + } while (!$success && $retry < $maxRetry); + + + + + // 执行采集朋友圈命令 $webSocket = new WebSocketController(['userName' => $username, 'password' => $password, 'accountId' => $toAccountId]); $webSocket->getMoments(['wechatFriendId' => $friend['friendId'], 'wechatAccountId' => $friend['wechatAccountId']]); diff --git a/Server/application/job/WorkbenchAutoLikeJob.php b/Server/application/job/WorkbenchAutoLikeJob.php index 351b6b6b..7520a46f 100644 --- a/Server/application/job/WorkbenchAutoLikeJob.php +++ b/Server/application/job/WorkbenchAutoLikeJob.php @@ -219,7 +219,25 @@ class WorkbenchAutoLikeJob // 执行切换好友命令 $automaticAssign = new AutomaticAssign(); $automaticAssign->allotWechatFriend(['wechatFriendId' => $friend['friendId'], 'toAccountId' => $toAccountId], true); - + + //记录切换好友 + $cacheKey = 'allotWechatFriend'; + $cacheFriend = $friend; + $cacheFriend['time'] = time() + 120; + $maxRetry = 5; + $retry = 0; + do { + $cacheFriendData = Cache::get($cacheKey, []); + // 去重:移除同friendId的旧数据 + $cacheFriendData = array_filter($cacheFriendData, function($item) use ($cacheFriend) { + return $item['friendId'] !== $cacheFriend['friendId']; + }); + $cacheFriendData[] = $cacheFriend; + $success = Cache::set($cacheKey, $cacheFriendData); + $retry++; + } while (!$success && $retry < $maxRetry); + + // 创建WebSocket链接 $webSocket = new WebSocketController(['userName' => $username, 'password' => $password, 'accountId' => $toAccountId]); From b757713d0861d75cb5552f5cfd87573eaa52c767 Mon Sep 17 00:00:00 2001 From: wong <106998207@qq.com> Date: Tue, 8 Jul 2025 09:59:21 +0800 Subject: [PATCH 15/29] =?UTF-8?q?=E4=BB=A3=E7=A0=81=E4=BC=98=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Server/application/command/SwitchFriendsCommand.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Server/application/command/SwitchFriendsCommand.php b/Server/application/command/SwitchFriendsCommand.php index 38575b9b..422fb4bf 100644 --- a/Server/application/command/SwitchFriendsCommand.php +++ b/Server/application/command/SwitchFriendsCommand.php @@ -38,7 +38,7 @@ class SwitchFriendsCommand extends Command $toSwitch = []; foreach ($friends as $friend) { - if (isset($friend['time']) && $friend['time'] > $now) { + if (isset($friend['time']) && $friend['time'] < $now) { $toSwitch[] = $friend; } } From 9c69003085b6916f1de0045eeef2a6a55a9e94b2 Mon Sep 17 00:00:00 2001 From: wong <106998207@qq.com> Date: Tue, 8 Jul 2025 10:20:18 +0800 Subject: [PATCH 16/29] =?UTF-8?q?=E5=88=87=E6=8D=A2=E5=A5=BD=E5=8F=8B?= =?UTF-8?q?=E4=BB=A3=E7=A0=81=E5=85=BC=E5=AE=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../application/cunkebao/controller/ContentLibraryController.php | 1 + 1 file changed, 1 insertion(+) diff --git a/Server/application/cunkebao/controller/ContentLibraryController.php b/Server/application/cunkebao/controller/ContentLibraryController.php index 8fdf96c9..91043612 100644 --- a/Server/application/cunkebao/controller/ContentLibraryController.php +++ b/Server/application/cunkebao/controller/ContentLibraryController.php @@ -898,6 +898,7 @@ class ContentLibraryController extends Controller //记录切换好友 $cacheKey = 'allotWechatFriend'; $cacheFriend = $friend; + $cacheFriend['friendId'] = $friend['id']; $cacheFriend['time'] = time() + 120; $maxRetry = 5; $retry = 0; From 2d55cac6fd5a75813f486898e05c9028fe79631c Mon Sep 17 00:00:00 2001 From: wong <106998207@qq.com> Date: Tue, 8 Jul 2025 10:32:37 +0800 Subject: [PATCH 17/29] =?UTF-8?q?=E5=85=BC=E5=AE=B9=E5=88=87=E6=8D=A2?= =?UTF-8?q?=E5=A5=BD=E5=8F=8B=E5=8A=9F=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Server/application/command/SwitchFriendsCommand.php | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/Server/application/command/SwitchFriendsCommand.php b/Server/application/command/SwitchFriendsCommand.php index 422fb4bf..ec197d7e 100644 --- a/Server/application/command/SwitchFriendsCommand.php +++ b/Server/application/command/SwitchFriendsCommand.php @@ -50,19 +50,21 @@ class SwitchFriendsCommand extends Command $automaticAssign = new AutomaticAssign(); foreach ($toSwitch as $friend) { + $friendId = !empty($friend['friendId']) ? $friend['friendId'] : $friend['id']; $automaticAssign->allotWechatFriend([ - 'wechatFriendId' => $friend['friendId'], + 'wechatFriendId' => $friendId, 'toAccountId' => $friend['accountId'], ], true); - $output->writeln('切换好友:' . $friend['friendId'] . ' 到账号:' . $friend['accountId']); - $switchedIds[] = $friend['friendId']; + $output->writeln('切换好友:' . $friendId . ' 到账号:' . $friend['accountId']); + $switchedIds[] = $friendId; } // 过滤掉已切换的,保留未切换和新进来的 $newFriends = Cache::get($cacheKey, []); $updated = []; foreach ($newFriends as $friend) { - if (!in_array($friend['friendId'], $switchedIds)) { + $friendId = !empty($friend['friendId']) ? $friend['friendId'] : $friend['id']; + if (!in_array($friendId, $switchedIds)) { $updated[] = $friend; } } From d5968a0991c0c0e0f6f1e3585b9f87e1489443a8 Mon Sep 17 00:00:00 2001 From: wong <106998207@qq.com> Date: Tue, 8 Jul 2025 11:56:24 +0800 Subject: [PATCH 18/29] =?UTF-8?q?=E4=BB=A3=E7=A0=81=E4=BC=98=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Server/application/command/SwitchFriendsCommand.php | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/Server/application/command/SwitchFriendsCommand.php b/Server/application/command/SwitchFriendsCommand.php index ec197d7e..09b7c69b 100644 --- a/Server/application/command/SwitchFriendsCommand.php +++ b/Server/application/command/SwitchFriendsCommand.php @@ -34,7 +34,6 @@ class SwitchFriendsCommand extends Command do { $friends = Cache::get($cacheKey, []); - $original = $friends; $toSwitch = []; foreach ($friends as $friend) { @@ -51,12 +50,18 @@ class SwitchFriendsCommand extends Command $automaticAssign = new AutomaticAssign(); foreach ($toSwitch as $friend) { $friendId = !empty($friend['friendId']) ? $friend['friendId'] : $friend['id']; - $automaticAssign->allotWechatFriend([ + $res = $automaticAssign->allotWechatFriend([ 'wechatFriendId' => $friendId, 'toAccountId' => $friend['accountId'], ], true); - $output->writeln('切换好友:' . $friendId . ' 到账号:' . $friend['accountId']); - $switchedIds[] = $friendId; + $res = json_decode($res, true); + if ($res['code'] == 200){ + $output->writeln('切换好友:' . $friendId . ' 到账号:' . $friend['accountId']); + $switchedIds[] = $friendId; + }else{ + $output->writeln('切换好友:' . $friendId . ' 到账号:' . $friend['accountId'] .' 结果:' .$res['msg']); + } + sleep(1); } // 过滤掉已切换的,保留未切换和新进来的 From e574a0696e2b85e182b445c2c8edcf81c1a0ef18 Mon Sep 17 00:00:00 2001 From: wong <106998207@qq.com> Date: Tue, 8 Jul 2025 14:05:27 +0800 Subject: [PATCH 19/29] =?UTF-8?q?=E4=BB=A3=E7=A0=81=E6=8F=90=E4=BA=A4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Server/application/command/SwitchFriendsCommand.php | 1 - 1 file changed, 1 deletion(-) diff --git a/Server/application/command/SwitchFriendsCommand.php b/Server/application/command/SwitchFriendsCommand.php index 09b7c69b..c1035cb0 100644 --- a/Server/application/command/SwitchFriendsCommand.php +++ b/Server/application/command/SwitchFriendsCommand.php @@ -61,7 +61,6 @@ class SwitchFriendsCommand extends Command }else{ $output->writeln('切换好友:' . $friendId . ' 到账号:' . $friend['accountId'] .' 结果:' .$res['msg']); } - sleep(1); } // 过滤掉已切换的,保留未切换和新进来的 From 4b649a0716b2ef16d6a5a1b5989ce4f196fe9809 Mon Sep 17 00:00:00 2001 From: wong <106998207@qq.com> Date: Tue, 8 Jul 2025 14:43:19 +0800 Subject: [PATCH 20/29] =?UTF-8?q?=E4=BB=A3=E7=A0=81=E4=BC=98=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Server/application/common.php | 4 ++++ Server/application/common/service/AuthService.php | 4 ++-- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/Server/application/common.php b/Server/application/common.php index d412b53a..f603ea90 100644 --- a/Server/application/common.php +++ b/Server/application/common.php @@ -67,6 +67,10 @@ if (!function_exists('requestCurl')) { $str = ''; } } + if ($str == '无效路径或登录状态失效'){ + Cache::rm('system_authorization_token'); + Cache::rm('system_refresh_token'); + } return $str; } } diff --git a/Server/application/common/service/AuthService.php b/Server/application/common/service/AuthService.php index aac5fbd1..5eb19b01 100644 --- a/Server/application/common/service/AuthService.php +++ b/Server/application/common/service/AuthService.php @@ -235,8 +235,8 @@ class AuthService $authorization = $result_array['access_token']; // 存入缓存,有效期10分钟(600秒) - Cache::set($cacheKey, $authorization, 600); - Cache::set('system_refresh_token', $result_array['refresh_token'], 600); + Cache::set($cacheKey, $authorization, 200); + Cache::set('system_refresh_token', $result_array['refresh_token'], 200); Log::info('已重新获取系统授权信息并缓存'); return $authorization; From b080feea85183fa7c5085ad48127885295adc94d Mon Sep 17 00:00:00 2001 From: wong <106998207@qq.com> Date: Tue, 8 Jul 2025 15:05:08 +0800 Subject: [PATCH 21/29] =?UTF-8?q?=E4=BB=A3=E7=A0=81=E5=90=8C=E6=AD=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Server/extend/WeChatDeviceApi/Adapters/ChuKeBao/Adapter.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Server/extend/WeChatDeviceApi/Adapters/ChuKeBao/Adapter.php b/Server/extend/WeChatDeviceApi/Adapters/ChuKeBao/Adapter.php index 2a8914f8..cd164828 100644 --- a/Server/extend/WeChatDeviceApi/Adapters/ChuKeBao/Adapter.php +++ b/Server/extend/WeChatDeviceApi/Adapters/ChuKeBao/Adapter.php @@ -168,7 +168,7 @@ class Adapter implements WeChatServiceInterface ->where(['status'=> 0,'task_id'=>$item['id']]) ->whereRaw("id % $process_count_for_status_0 = {$current_worker_id}") ->order('id DESC') - ->limit(20) + ->limit(5) ->select(); $taskData = array_merge($taskData, $tasks); } From e96a1654efce6a4472bb16c4219007fdb61b9436 Mon Sep 17 00:00:00 2001 From: wong <106998207@qq.com> Date: Wed, 9 Jul 2025 09:31:20 +0800 Subject: [PATCH 22/29] =?UTF-8?q?=E4=BB=A3=E7=A0=81=E6=8F=90=E4=BA=A4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Server/application/command/SwitchFriendsCommand.php | 1 + 1 file changed, 1 insertion(+) diff --git a/Server/application/command/SwitchFriendsCommand.php b/Server/application/command/SwitchFriendsCommand.php index c1035cb0..09b7c69b 100644 --- a/Server/application/command/SwitchFriendsCommand.php +++ b/Server/application/command/SwitchFriendsCommand.php @@ -61,6 +61,7 @@ class SwitchFriendsCommand extends Command }else{ $output->writeln('切换好友:' . $friendId . ' 到账号:' . $friend['accountId'] .' 结果:' .$res['msg']); } + sleep(1); } // 过滤掉已切换的,保留未切换和新进来的 From 5a701c048cc16ff4aa4c86bc6027f08a60668ae6 Mon Sep 17 00:00:00 2001 From: wong <106998207@qq.com> Date: Wed, 9 Jul 2025 10:00:54 +0800 Subject: [PATCH 23/29] =?UTF-8?q?=E4=B8=B4=E6=97=B6=E6=8F=90=E4=BA=A4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Server/application/api/controller/AutomaticAssign.php | 10 +++++++++- Server/application/command/SwitchFriendsCommand.php | 1 - 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/Server/application/api/controller/AutomaticAssign.php b/Server/application/api/controller/AutomaticAssign.php index 88e0048f..0a10e9b6 100644 --- a/Server/application/api/controller/AutomaticAssign.php +++ b/Server/application/api/controller/AutomaticAssign.php @@ -7,6 +7,7 @@ use app\api\model\CompanyModel; use Library\S2\Logics\AccountLogic; use think\Db; use think\facade\Request; +use think\facade\Cache; /** * 账号管理控制器 @@ -174,7 +175,7 @@ class AutomaticAssign extends BaseController public function allotWechatFriend($data = [],$isInner = false) { // 获取授权token - $authorization = trim($this->request->header('authorization', $this->authorization)); + $authorization = $this->authorization; if (empty($authorization)) { if($isInner){ return json_encode(['code'=>500,'msg'=>'缺少授权信息']); @@ -216,6 +217,13 @@ class AutomaticAssign extends BaseController return successJson([], '微信好友分配成功'); } } else { + + if ($result == '无效路径或登录状态失效'){ + Cache::rm('system_authorization_token'); + Cache::rm('system_refresh_token'); + } + + if($isInner){ return json_encode(['code'=>500,'msg'=>$result]); }else{ diff --git a/Server/application/command/SwitchFriendsCommand.php b/Server/application/command/SwitchFriendsCommand.php index 09b7c69b..c1035cb0 100644 --- a/Server/application/command/SwitchFriendsCommand.php +++ b/Server/application/command/SwitchFriendsCommand.php @@ -61,7 +61,6 @@ class SwitchFriendsCommand extends Command }else{ $output->writeln('切换好友:' . $friendId . ' 到账号:' . $friend['accountId'] .' 结果:' .$res['msg']); } - sleep(1); } // 过滤掉已切换的,保留未切换和新进来的 From d9c77039759c23ac5ba8c33e84625f205cc06e2f Mon Sep 17 00:00:00 2001 From: wong <106998207@qq.com> Date: Wed, 9 Jul 2025 10:05:49 +0800 Subject: [PATCH 24/29] =?UTF-8?q?=E6=8F=90=E4=BA=A4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Server/application/api/controller/AutomaticAssign.php | 7 ------- Server/application/common.php | 4 ---- 2 files changed, 11 deletions(-) diff --git a/Server/application/api/controller/AutomaticAssign.php b/Server/application/api/controller/AutomaticAssign.php index 0a10e9b6..66b0d3d5 100644 --- a/Server/application/api/controller/AutomaticAssign.php +++ b/Server/application/api/controller/AutomaticAssign.php @@ -217,13 +217,6 @@ class AutomaticAssign extends BaseController return successJson([], '微信好友分配成功'); } } else { - - if ($result == '无效路径或登录状态失效'){ - Cache::rm('system_authorization_token'); - Cache::rm('system_refresh_token'); - } - - if($isInner){ return json_encode(['code'=>500,'msg'=>$result]); }else{ diff --git a/Server/application/common.php b/Server/application/common.php index f603ea90..d412b53a 100644 --- a/Server/application/common.php +++ b/Server/application/common.php @@ -67,10 +67,6 @@ if (!function_exists('requestCurl')) { $str = ''; } } - if ($str == '无效路径或登录状态失效'){ - Cache::rm('system_authorization_token'); - Cache::rm('system_refresh_token'); - } return $str; } } From 40e0938e2cf2ec0931e10e0424d0345911f746ec Mon Sep 17 00:00:00 2001 From: wong <106998207@qq.com> Date: Wed, 9 Jul 2025 10:31:42 +0800 Subject: [PATCH 25/29] =?UTF-8?q?=E6=81=A2=E5=A4=8D=E7=BC=93=E5=AD=9810?= =?UTF-8?q?=E5=88=86=E9=92=9F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Server/application/common/service/AuthService.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Server/application/common/service/AuthService.php b/Server/application/common/service/AuthService.php index 5eb19b01..aac5fbd1 100644 --- a/Server/application/common/service/AuthService.php +++ b/Server/application/common/service/AuthService.php @@ -235,8 +235,8 @@ class AuthService $authorization = $result_array['access_token']; // 存入缓存,有效期10分钟(600秒) - Cache::set($cacheKey, $authorization, 200); - Cache::set('system_refresh_token', $result_array['refresh_token'], 200); + Cache::set($cacheKey, $authorization, 600); + Cache::set('system_refresh_token', $result_array['refresh_token'], 600); Log::info('已重新获取系统授权信息并缓存'); return $authorization; From 6ec4be71d935cc086de22423c12b816a3f410cfa Mon Sep 17 00:00:00 2001 From: wong <106998207@qq.com> Date: Wed, 9 Jul 2025 14:56:58 +0800 Subject: [PATCH 26/29] =?UTF-8?q?=E4=BC=98=E5=8C=96=E4=BB=A3=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Server/application/common.php | 28 ++++++++++++++ .../controller/ContentLibraryController.php | 28 ++++---------- Server/application/job/WechatMomentsJob.php | 35 ++++++------------ .../application/job/WorkbenchAutoLikeJob.php | 37 ++++++++----------- 4 files changed, 63 insertions(+), 65 deletions(-) diff --git a/Server/application/common.php b/Server/application/common.php index d412b53a..389a9fad 100644 --- a/Server/application/common.php +++ b/Server/application/common.php @@ -518,6 +518,34 @@ if (!function_exists('exit_data')) { } } +if (!function_exists('artificialAllotWechatFriend')) { + function artificialAllotWechatFriend($friend = []) + { + if (empty($friend)) { + return false; + } + + //记录切换好友 + $cacheKey = 'allotWechatFriend'; + $cacheFriend = $friend; + $cacheFriend['time'] = time() + 120; + $maxRetry = 5; + $retry = 0; + do { + $cacheFriendData = Cache::get($cacheKey, []); + // 去重:移除同friendId的旧数据 + $cacheFriendData = array_filter($cacheFriendData, function($item) use ($cacheFriend) { + return $item['friendId'] !== $cacheFriend['friendId']; + }); + $cacheFriendData[] = $cacheFriend; + $success = Cache::set($cacheKey, $cacheFriendData); + $retry++; + } while (!$success && $retry < $maxRetry); + } +} + + + /** * 调试打印变量但不终止程序 * @return void diff --git a/Server/application/cunkebao/controller/ContentLibraryController.php b/Server/application/cunkebao/controller/ContentLibraryController.php index 91043612..9b048c3f 100644 --- a/Server/application/cunkebao/controller/ContentLibraryController.php +++ b/Server/application/cunkebao/controller/ContentLibraryController.php @@ -894,31 +894,17 @@ class ContentLibraryController extends Controller //执行切换好友命令 $automaticAssign = new AutomaticAssign(); $automaticAssign->allotWechatFriend(['wechatFriendId' => $friend['id'],'toAccountId' => $toAccountId],true); - - //记录切换好友 - $cacheKey = 'allotWechatFriend'; - $cacheFriend = $friend; - $cacheFriend['friendId'] = $friend['id']; - $cacheFriend['time'] = time() + 120; - $maxRetry = 5; - $retry = 0; - do { - $cacheFriendData = Cache::get($cacheKey, []); - // 去重:移除同friendId的旧数据 - $cacheFriendData = array_filter($cacheFriendData, function($item) use ($cacheFriend) { - return $item['friendId'] !== $cacheFriend['friendId']; - }); - $cacheFriendData[] = $cacheFriend; - $success = Cache::set($cacheKey, $cacheFriendData); - $retry++; - } while (!$success && $retry < $maxRetry); - - //执行采集朋友圈命令 $webSocket = new WebSocketController(['userName' => $username,'password' => $password,'accountId' => $toAccountId]); $webSocket->getMoments(['wechatFriendId' => $friend['id'],'wechatAccountId' => $friend['wechatAccountId']]); //采集完毕切换 - $automaticAssign->allotWechatFriend(['wechatFriendId' => $friend['id'],'toAccountId' => $friend['accountId']],true); + $res = $automaticAssign->allotWechatFriend(['wechatFriendId' => $friend['id'],'toAccountId' => $friend['accountId']],true); + $res = json_decode($res, true); + if($res == '无效路径或登录状态失效'){ + $cacheFriend = $friend; + $cacheFriend['friendId'] = $friend['id']; + artificialAllotWechatFriend($friend); + } } diff --git a/Server/application/job/WechatMomentsJob.php b/Server/application/job/WechatMomentsJob.php index 04cedafb..88d380d2 100644 --- a/Server/application/job/WechatMomentsJob.php +++ b/Server/application/job/WechatMomentsJob.php @@ -47,36 +47,25 @@ class WechatMomentsJob $automaticAssign = new AutomaticAssign(); $automaticAssign->allotWechatFriend(['wechatFriendId' => $friend['friendId'], 'toAccountId' => $toAccountId], true); - //记录切换好友 - $cacheKey = 'allotWechatFriend'; - $cacheFriend = $friend; - $cacheFriend['time'] = time() + 120; - $maxRetry = 5; - $retry = 0; - do { - $cacheFriendData = Cache::get($cacheKey, []); - // 去重:移除同friendId的旧数据 - $cacheFriendData = array_filter($cacheFriendData, function($item) use ($cacheFriend) { - return $item['friendId'] !== $cacheFriend['friendId']; - }); - $cacheFriendData[] = $cacheFriend; - $success = Cache::set($cacheKey, $cacheFriendData); - $retry++; - } while (!$success && $retry < $maxRetry); - - - - - // 执行采集朋友圈命令 $webSocket = new WebSocketController(['userName' => $username, 'password' => $password, 'accountId' => $toAccountId]); $webSocket->getMoments(['wechatFriendId' => $friend['friendId'], 'wechatAccountId' => $friend['wechatAccountId']]); // 处理完毕切换回原账号 - $automaticAssign->allotWechatFriend(['wechatFriendId' => $friend['friendId'], 'toAccountId' => $friend['accountId']], true); + $res = $automaticAssign->allotWechatFriend(['wechatFriendId' => $friend['friendId'], 'toAccountId' => $friend['accountId']], true); + $res = json_decode($res, true); + if ($res == '无效路径或登录状态失效'){ + artificialAllotWechatFriend($friend); + } + + } catch (\Exception $e) { // 发生异常时也要切换回原账号 - $automaticAssign->allotWechatFriend(['wechatFriendId' => $friend['friendId'], 'toAccountId' => $friend['accountId']], true); + $res = $automaticAssign->allotWechatFriend(['wechatFriendId' => $friend['friendId'], 'toAccountId' => $friend['accountId']], true); + $res = json_decode($res, true); + if ($res == '无效路径或登录状态失效'){ + artificialAllotWechatFriend($friend); + } Log::error("采集好友 {$friend['id']} 的朋友圈失败:" . $e->getMessage()); continue; } diff --git a/Server/application/job/WorkbenchAutoLikeJob.php b/Server/application/job/WorkbenchAutoLikeJob.php index 7520a46f..b0f489de 100644 --- a/Server/application/job/WorkbenchAutoLikeJob.php +++ b/Server/application/job/WorkbenchAutoLikeJob.php @@ -220,24 +220,6 @@ class WorkbenchAutoLikeJob $automaticAssign = new AutomaticAssign(); $automaticAssign->allotWechatFriend(['wechatFriendId' => $friend['friendId'], 'toAccountId' => $toAccountId], true); - //记录切换好友 - $cacheKey = 'allotWechatFriend'; - $cacheFriend = $friend; - $cacheFriend['time'] = time() + 120; - $maxRetry = 5; - $retry = 0; - do { - $cacheFriendData = Cache::get($cacheKey, []); - // 去重:移除同friendId的旧数据 - $cacheFriendData = array_filter($cacheFriendData, function($item) use ($cacheFriend) { - return $item['friendId'] !== $cacheFriend['friendId']; - }); - $cacheFriendData[] = $cacheFriend; - $success = Cache::set($cacheKey, $cacheFriendData); - $retry++; - } while (!$success && $retry < $maxRetry); - - // 创建WebSocket链接 $webSocket = new WebSocketController(['userName' => $username, 'password' => $password, 'accountId' => $toAccountId]); @@ -252,7 +234,12 @@ class WorkbenchAutoLikeJob if (empty($moments) || count($moments) == 0) { // 处理完毕切换回原账号 - $automaticAssign->allotWechatFriend(['wechatFriendId' => $friend['friendId'], 'toAccountId' => $friend['accountId']], true); + $res = $automaticAssign->allotWechatFriend(['wechatFriendId' => $friend['friendId'], 'toAccountId' => $friend['accountId']], true); + $res = json_decode($res, true); + if($res == '无效路径或登录状态失效'){ + artificialAllotWechatFriend($friend); + } + Log::info("好友 {$friend['friendId']} 没有需要点赞的朋友圈"); return; } @@ -276,11 +263,19 @@ class WorkbenchAutoLikeJob } // 处理完毕切换回原账号 - $automaticAssign->allotWechatFriend(['wechatFriendId' => $friend['friendId'], 'toAccountId' => $friend['accountId']], true); + $res = $automaticAssign->allotWechatFriend(['wechatFriendId' => $friend['friendId'], 'toAccountId' => $friend['accountId']], true); + $res = json_decode($res, true); + if($res == '无效路径或登录状态失效'){ + artificialAllotWechatFriend($friend); + } } catch (\Exception $e) { // 异常情况下也要确保切换回原账号 $automaticAssign = new AutomaticAssign(); - $automaticAssign->allotWechatFriend(['wechatFriendId' => $friend['friendId'], 'toAccountId' => $friend['accountId']], true); + $res = $automaticAssign->allotWechatFriend(['wechatFriendId' => $friend['friendId'], 'toAccountId' => $friend['accountId']], true); + $res = json_decode($res, true); + if($res == '无效路径或登录状态失效'){ + artificialAllotWechatFriend($friend); + } Log::error("处理好友 {$friend['friendId']} 朋友圈失败异常: " . $e->getMessage()); } } From d5a541611d1ceb278c595f6b80827aab0c8b8e5f Mon Sep 17 00:00:00 2001 From: wong <106998207@qq.com> Date: Wed, 9 Jul 2025 15:22:21 +0800 Subject: [PATCH 27/29] =?UTF-8?q?=E4=BB=A3=E7=A0=81=E6=8F=90=E4=BA=A4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../cunkebao/controller/ContentLibraryController.php | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/Server/application/cunkebao/controller/ContentLibraryController.php b/Server/application/cunkebao/controller/ContentLibraryController.php index 9b048c3f..21389fc4 100644 --- a/Server/application/cunkebao/controller/ContentLibraryController.php +++ b/Server/application/cunkebao/controller/ContentLibraryController.php @@ -863,6 +863,7 @@ class ContentLibraryController extends Controller 'message' => '没有指定要采集的好友' ]; } + $friendData = []; try { $toAccountId = ''; $username = Env::get('api.username', ''); @@ -890,6 +891,7 @@ class ContentLibraryController extends Controller $totalMomentsCount = 0; foreach ($friends as $friend) { + $friendData = $friend; if (!empty($username) && !empty($password)) { //执行切换好友命令 $automaticAssign = new AutomaticAssign(); @@ -1013,6 +1015,12 @@ class ContentLibraryController extends Controller ]; } catch (\Exception $e) { + + $cacheFriend = $friendData; + $cacheFriend['friendId'] = $friend['id']; + artificialAllotWechatFriend($friend); + + return [ 'status' => 'error', 'message' => '采集过程发生错误: ' . $e->getMessage() From 28f9ee3e765ce88426df4b745777c0cda406c013 Mon Sep 17 00:00:00 2001 From: wong <106998207@qq.com> Date: Wed, 9 Jul 2025 16:37:39 +0800 Subject: [PATCH 28/29] =?UTF-8?q?=E4=BB=A3=E7=A0=81=E6=8F=90=E4=BA=A4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../controller/ContentLibraryController.php | 197 +++++++++++++++++- 1 file changed, 195 insertions(+), 2 deletions(-) diff --git a/Server/application/cunkebao/controller/ContentLibraryController.php b/Server/application/cunkebao/controller/ContentLibraryController.php index 21389fc4..d3b8b1f0 100644 --- a/Server/application/cunkebao/controller/ContentLibraryController.php +++ b/Server/application/cunkebao/controller/ContentLibraryController.php @@ -762,7 +762,8 @@ class ContentLibraryController extends Controller // 查询条件:未删除且已开启的内容库 $where = [ ['isDel', '=', 0], // 未删除 - ['status', '=', 1] // 已开启 + ['status', '=', 1], // 已开启 + ['id', '=', 27] ]; // 查询符合条件的内容库 @@ -1411,6 +1412,35 @@ class ContentLibraryController extends Controller }elseif ($moment['type'] == 3){ //链接 $contentType = 2; + $urls = []; + $url = is_string($moment['urls']) ? json_decode($moment['urls'], true) : $moment['urls'] ?? []; + $url = $url[0]; + if(strpos($url, 'feishu.cn') !== false){ + $urls[] = [ + 'url' => $url, + 'image' => 'http://karuosiyujzk.oss-cn-shenzhen.aliyuncs.com/2025/07/09/3db2a5d7fe49011ab68175a42a5094ce.jpeg', + 'desc' => '点击查看详情' + ]; + }else{ + $urlInfo = $this->parseUrl($url); + if (!empty($urlInfo)) { + $urls[] = [ + 'url' => $url, + 'image' => $urlInfo['icon'], + 'desc' => $urlInfo['title'] + ]; + } else { + // 解析失败的处理 + $urls[] = [ + 'url' => $url, + 'image' => 'http://karuosiyujzk.oss-cn-shenzhen.aliyuncs.com/2025/07/09/ec039d96fad6eab1d960f207d3d9ca9f.jpeg', + 'desc' => '点击查看详情' + ]; + } + } + $moment['urls'] = $urls; + + }elseif ($moment['type'] == 15){ //视频 $contentType = 3; @@ -1727,4 +1757,167 @@ class ContentLibraryController extends Controller return []; } } -} \ No newline at end of file + + + + + /** + * 解析URL获取网页信息(内部调用) + * @param string $url 要解析的URL + * @return array 包含title、icon的数组,失败返回空数组 + */ + public function parseUrl($url) + { + if (empty($url) || !filter_var($url, FILTER_VALIDATE_URL)) { + return []; + } + + try { + // 设置请求头,模拟浏览器访问 + $context = stream_context_create([ + 'http' => [ + 'method' => 'GET', + 'header' => [ + 'User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36', + 'Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8', + 'Accept-Language: zh-CN,zh;q=0.9,en;q=0.8', + 'Accept-Encoding: gzip, deflate', + 'Connection: keep-alive', + 'Upgrade-Insecure-Requests: 1' + ], + 'timeout' => 10, + 'follow_location' => true, + 'max_redirects' => 3 + ] + ]); + + // 获取网页内容 + $html = @file_get_contents($url, false, $context); + + if ($html === false) { + return []; + } + + // 检测编码并转换为UTF-8 + $encoding = mb_detect_encoding($html, ['UTF-8', 'GBK', 'GB2312', 'BIG5', 'ASCII']); + if ($encoding && $encoding !== 'UTF-8') { + $html = mb_convert_encoding($html, 'UTF-8', $encoding); + } + + // 解析HTML + $dom = new \DOMDocument(); + @$dom->loadHTML($html, LIBXML_NOERROR | LIBXML_NOWARNING); + $xpath = new \DOMXPath($dom); + + $result = [ + 'title' => '', + 'icon' => '', + 'url' => $url + ]; + + // 提取标题 + $titleNodes = $xpath->query('//title'); + if ($titleNodes->length > 0) { + $result['title'] = trim($titleNodes->item(0)->textContent); + } + + // 提取图标 - 优先获取favicon + $iconNodes = $xpath->query('//link[@rel="icon"]/@href | //link[@rel="shortcut icon"]/@href | //link[@rel="apple-touch-icon"]/@href'); + if ($iconNodes->length > 0) { + $iconUrl = trim($iconNodes->item(0)->value); + $result['icon'] = $this->makeAbsoluteUrl($iconUrl, $url); + } else { + // 尝试获取Open Graph图片 + $ogImageNodes = $xpath->query('//meta[@property="og:image"]/@content'); + if ($ogImageNodes->length > 0) { + $result['icon'] = trim($ogImageNodes->item(0)->value); + } else { + // 默认favicon路径 + $result['icon'] = $this->makeAbsoluteUrl('/favicon.ico', $url); + } + } + + // 清理和验证数据 + $result['title'] = $this->cleanText($result['title']); + + return $result; + + } catch (\Exception $e) { + // 记录错误日志但不抛出异常 + \think\facade\Log::error('URL解析失败: ' . $e->getMessage() . ' URL: ' . $url); + return []; + } + } + + + + + + /** + * 将相对URL转换为绝对URL + * @param string $relativeUrl 相对URL + * @param string $baseUrl 基础URL + * @return string 绝对URL + */ + private function makeAbsoluteUrl($relativeUrl, $baseUrl) + { + if (empty($relativeUrl)) { + return ''; + } + + // 如果已经是绝对URL,直接返回 + if (filter_var($relativeUrl, FILTER_VALIDATE_URL)) { + return $relativeUrl; + } + + // 解析基础URL + $baseParts = parse_url($baseUrl); + if (!$baseParts) { + return $relativeUrl; + } + + // 处理以/开头的绝对路径 + if (strpos($relativeUrl, '/') === 0) { + return $baseParts['scheme'] . '://' . $baseParts['host'] . + (isset($baseParts['port']) ? ':' . $baseParts['port'] : '') . + $relativeUrl; + } + + // 处理相对路径 + $basePath = isset($baseParts['path']) ? dirname($baseParts['path']) : '/'; + if ($basePath === '.') { + $basePath = '/'; + } + + return $baseParts['scheme'] . '://' . $baseParts['host'] . + (isset($baseParts['port']) ? ':' . $baseParts['port'] : '') . + $basePath . '/' . $relativeUrl; + } + + /** + * 清理文本内容 + * @param string $text 要清理的文本 + * @return string 清理后的文本 + */ + private function cleanText($text) + { + if (empty($text)) { + return ''; + } + + // 移除HTML实体 + $text = html_entity_decode($text, ENT_QUOTES | ENT_HTML5, 'UTF-8'); + + // 移除多余的空白字符 + $text = preg_replace('/\s+/', ' ', $text); + + // 移除控制字符 + $text = preg_replace('/[\x00-\x1F\x7F]/', '', $text); + + return trim($text); + } + + + + +} \ No newline at end of file From 4ca6ff247bb3791afd117279c03500cd099c21e6 Mon Sep 17 00:00:00 2001 From: wong <106998207@qq.com> Date: Wed, 9 Jul 2025 16:48:30 +0800 Subject: [PATCH 29/29] =?UTF-8?q?=E4=BB=A3=E7=A0=81=E6=8F=90=E4=BA=A4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../controller/ContentLibraryController.php | 20 +++++-------------- 1 file changed, 5 insertions(+), 15 deletions(-) diff --git a/Server/application/cunkebao/controller/ContentLibraryController.php b/Server/application/cunkebao/controller/ContentLibraryController.php index d3b8b1f0..3aea2a28 100644 --- a/Server/application/cunkebao/controller/ContentLibraryController.php +++ b/Server/application/cunkebao/controller/ContentLibraryController.php @@ -1422,21 +1422,11 @@ class ContentLibraryController extends Controller 'desc' => '点击查看详情' ]; }else{ - $urlInfo = $this->parseUrl($url); - if (!empty($urlInfo)) { - $urls[] = [ - 'url' => $url, - 'image' => $urlInfo['icon'], - 'desc' => $urlInfo['title'] - ]; - } else { - // 解析失败的处理 - $urls[] = [ - 'url' => $url, - 'image' => 'http://karuosiyujzk.oss-cn-shenzhen.aliyuncs.com/2025/07/09/ec039d96fad6eab1d960f207d3d9ca9f.jpeg', - 'desc' => '点击查看详情' - ]; - } + $urls[] = [ + 'url' => $url, + 'image' => 'http://karuosiyujzk.oss-cn-shenzhen.aliyuncs.com/2025/07/09/ec039d96fad6eab1d960f207d3d9ca9f.jpeg', + 'desc' => '点击查看详情' + ]; } $moment['urls'] = $urls;