自动点赞
This commit is contained in:
@@ -2,15 +2,19 @@
|
||||
|
||||
import { useState, useEffect, use } from "react"
|
||||
import { useRouter } from "next/navigation"
|
||||
import { ChevronLeft, Search } from "lucide-react"
|
||||
import { ChevronLeft, Search, Users } from "lucide-react"
|
||||
import { Button } from "@/components/ui/button"
|
||||
import { Input } from "@/components/ui/input"
|
||||
import { StepIndicator } from "../../components/step-indicator"
|
||||
import { BasicSettings } from "../../components/basic-settings"
|
||||
import { DeviceSelectionDialog } from "../../components/device-selection-dialog"
|
||||
import { TagSelector } from "../../components/tag-selector"
|
||||
import { WechatGroupMemberSelector } from "@/components/WechatGroupMemberSelector"
|
||||
import { WechatFriendSelector } from "@/components/WechatFriendSelector"
|
||||
import { api, ApiResponse } from "@/lib/api"
|
||||
import { showToast } from "@/lib/toast"
|
||||
import { WechatGroupMember } from "@/types/wechat"
|
||||
import { WechatFriend } from "@/components/WechatFriendSelector"
|
||||
|
||||
interface TaskConfig {
|
||||
id: number
|
||||
@@ -43,6 +47,11 @@ export default function EditAutoLikePage({ params }: { params: Promise<{ id: str
|
||||
const router = useRouter()
|
||||
const [currentStep, setCurrentStep] = useState(1)
|
||||
const [deviceDialogOpen, setDeviceDialogOpen] = useState(false)
|
||||
const [isGroupMemberSelectorOpen, setIsGroupMemberSelectorOpen] = useState(false)
|
||||
const [currentGroupId, setCurrentGroupId] = useState<string>("")
|
||||
const [selectedGroupMembers, setSelectedGroupMembers] = useState<WechatGroupMember[]>([])
|
||||
const [isFriendSelectorOpen, setIsFriendSelectorOpen] = useState(false)
|
||||
const [selectedFriends, setSelectedFriends] = useState<WechatFriend[]>([])
|
||||
const [loading, setLoading] = useState(true)
|
||||
const [formData, setFormData] = useState({
|
||||
taskName: "",
|
||||
@@ -52,8 +61,7 @@ export default function EditAutoLikePage({ params }: { params: Promise<{ id: str
|
||||
contentTypes: ["text", "image", "video"],
|
||||
enabled: true,
|
||||
selectedDevices: [] as number[],
|
||||
selectedTags: [] as string[],
|
||||
tagOperator: "and" as "and" | "or",
|
||||
friends: [] as string[],
|
||||
})
|
||||
|
||||
useEffect(() => {
|
||||
@@ -78,8 +86,7 @@ export default function EditAutoLikePage({ params }: { params: Promise<{ id: str
|
||||
contentTypes: task.config.contentTypes,
|
||||
enabled: task.status === 1,
|
||||
selectedDevices: task.config.devices,
|
||||
selectedTags: task.config.targetGroups,
|
||||
tagOperator: task.config.tagOperator === 1 ? "and" : "or"
|
||||
friends: Array.isArray(task.config.friends) ? task.config.friends : [],
|
||||
})
|
||||
} else {
|
||||
showToast(response.msg || "获取任务信息失败", "error")
|
||||
@@ -121,8 +128,7 @@ export default function EditAutoLikePage({ params }: { params: Promise<{ id: str
|
||||
contentTypes: formData.contentTypes,
|
||||
enabled: formData.enabled,
|
||||
devices: formData.selectedDevices,
|
||||
targetGroups: formData.selectedTags,
|
||||
tagOperator: formData.tagOperator === 'and' ? 1 : 2
|
||||
friends: formData.friends,
|
||||
});
|
||||
|
||||
if (response.code === 200) {
|
||||
@@ -140,6 +146,17 @@ export default function EditAutoLikePage({ params }: { params: Promise<{ id: str
|
||||
}
|
||||
};
|
||||
|
||||
const handleSelectFriends = () => {
|
||||
setIsFriendSelectorOpen(true)
|
||||
}
|
||||
|
||||
const handleSaveSelectedFriends = (friends: WechatFriend[]) => {
|
||||
const ids = friends.map(f => f.id)
|
||||
setSelectedFriends(friends)
|
||||
handleUpdateFormData({ friends: ids })
|
||||
setIsFriendSelectorOpen(false)
|
||||
}
|
||||
|
||||
if (loading) {
|
||||
return null;
|
||||
}
|
||||
@@ -207,15 +224,19 @@ export default function EditAutoLikePage({ params }: { params: Promise<{ id: str
|
||||
|
||||
{currentStep === 3 && (
|
||||
<div className="px-6">
|
||||
<TagSelector
|
||||
selectedTags={formData.selectedTags}
|
||||
tagOperator={formData.tagOperator}
|
||||
onTagsChange={(tags) => handleUpdateFormData({ selectedTags: tags })}
|
||||
onOperatorChange={(operator) => handleUpdateFormData({ tagOperator: operator })}
|
||||
onBack={handlePrev}
|
||||
onComplete={handleComplete}
|
||||
/>
|
||||
|
||||
<div className="relative">
|
||||
<Users className="absolute left-3 top-4 h-5 w-5 text-gray-400" />
|
||||
<Input
|
||||
placeholder="选择微信好友"
|
||||
className="h-12 pl-11 rounded-xl border-gray-200 text-base"
|
||||
onClick={handleSelectFriends}
|
||||
readOnly
|
||||
value={formData.friends.length > 0 ? `已选择 ${formData.friends.length} 个好友` : ""}
|
||||
/>
|
||||
</div>
|
||||
{formData.friends.length > 0 && (
|
||||
<div className="text-base text-gray-500">已选好友:{formData.friends.length} 个</div>
|
||||
)}
|
||||
<div className="flex space-x-4 pt-4">
|
||||
<Button variant="outline" onClick={handlePrev} className="flex-1 h-12 rounded-xl text-base">
|
||||
上一步
|
||||
@@ -227,6 +248,12 @@ export default function EditAutoLikePage({ params }: { params: Promise<{ id: str
|
||||
完成
|
||||
</Button>
|
||||
</div>
|
||||
<WechatFriendSelector
|
||||
open={isFriendSelectorOpen}
|
||||
onOpenChange={setIsFriendSelectorOpen}
|
||||
selectedFriends={selectedFriends}
|
||||
onSelect={handleSaveSelectedFriends}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
@@ -10,7 +10,7 @@ import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar"
|
||||
import { api } from "@/lib/api"
|
||||
import { showToast } from "@/lib/toast"
|
||||
|
||||
interface WechatFriend {
|
||||
export interface WechatFriend {
|
||||
id: string
|
||||
nickname: string
|
||||
wechatId: string
|
||||
|
||||
@@ -220,8 +220,8 @@ class WebSocketController extends BaseController
|
||||
$maxPages = 20; // 最大页数限制为20
|
||||
$currentPage = 1; // 当前页码
|
||||
$allMoments = []; // 存储所有朋友圈数据
|
||||
|
||||
//过滤消息
|
||||
|
||||
//过滤消息
|
||||
if (empty($wechatAccountId)) {
|
||||
return json_encode(['code'=>400,'msg'=>'指定账号不能为空']);
|
||||
}
|
||||
@@ -243,12 +243,39 @@ class WebSocketController extends BaseController
|
||||
$message = $this->sendMessage($params);
|
||||
Log::info('获取朋友圈信成功:' . json_encode($message, 256));
|
||||
|
||||
|
||||
// 检查是否遇到频率限制
|
||||
if (isset($message['extra']) && strpos($message['extra'], '朋友圈太频繁了') !== false) {
|
||||
Log::info('遇到频率限制,休息10秒后继续');
|
||||
sleep(10);
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
// 检查返回结果
|
||||
if (!isset($message['result']) || empty($message['result']) || !is_array($message['result'])) {
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
// 检查是否遇到旧数据
|
||||
$hasOldData = false;
|
||||
foreach ($message['result'] as $moment) {
|
||||
$momentId = WechatMoments::where('snsId', $moment['snsId'])
|
||||
->where('wechatAccountId', $wechatAccountId)
|
||||
->value('id');
|
||||
|
||||
if (!empty($momentId)) {
|
||||
$hasOldData = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// 如果遇到旧数据,结束本次任务
|
||||
if ($hasOldData) {
|
||||
// Log::info('遇到旧数据,结束本次任务');
|
||||
// break;
|
||||
}
|
||||
|
||||
// 合并朋友圈数据
|
||||
$allMoments = array_merge($allMoments, $message['result']);
|
||||
@@ -299,40 +326,36 @@ class WebSocketController extends BaseController
|
||||
* 朋友圈点赞
|
||||
* @return \think\response\Json
|
||||
*/
|
||||
public function momentInteract()
|
||||
public function momentInteract($data = [])
|
||||
{
|
||||
if ($this->request->isPost()) {
|
||||
$data = $this->request->param();
|
||||
|
||||
$snsId = !empty($data['snsId']) ? $data['snsId'] : '';
|
||||
$wechatAccountId = !empty($data['wechatAccountId']) ? $data['wechatAccountId'] : '';
|
||||
$wechatFriendId = !empty($data['wechatFriendId']) ? $data['wechatFriendId'] : 0;
|
||||
|
||||
if (empty($data)) {
|
||||
return json_encode(['code'=>400,'msg'=>'参数缺失']);
|
||||
}
|
||||
|
||||
//过滤消息
|
||||
if (empty($data['snsId'])) {
|
||||
return json_encode(['code'=>400,'msg'=>'snsId不能为空']);
|
||||
}
|
||||
if (empty($data['wechatAccountId'])) {
|
||||
return json_encode(['code'=>400,'msg'=>'微信id不能为空']);
|
||||
}
|
||||
//过滤消息
|
||||
if (empty($snsId)) {
|
||||
return json_encode(['code'=>400,'msg'=>'snsId不能为空']);
|
||||
}
|
||||
if (empty($wechatAccountId)) {
|
||||
return json_encode(['code'=>400,'msg'=>'微信id不能为空']);
|
||||
}
|
||||
|
||||
try {
|
||||
try {
|
||||
$result = [
|
||||
"cmdType" => "CmdMomentInteract",
|
||||
"momentInteractType" => 1,
|
||||
"seq" => time(),
|
||||
"snsId" => $data['snsId'],
|
||||
"wechatAccountId" => $data['wechatAccountId'],
|
||||
"wechatFriendId" => 0,
|
||||
];
|
||||
"snsId" => $snsId,
|
||||
"wechatAccountId" => $wechatAccountId,
|
||||
"wechatFriendId" => $wechatFriendId,
|
||||
];
|
||||
|
||||
$message = $this->sendMessage($result);
|
||||
return json_encode(['code'=>200,'msg'=>'点赞成功','data'=>$message]);
|
||||
} catch (\Exception $e) {
|
||||
return json_encode(['code'=>500,'msg'=>$e->getMessage()]);
|
||||
}
|
||||
} else {
|
||||
return json_encode(['code'=>400,'msg'=>'非法请求']);
|
||||
$message = $this->sendMessage($result);
|
||||
return json_encode(['code'=>200,'msg'=>'点赞成功','data'=>$message]);
|
||||
} catch (\Exception $e) {
|
||||
return json_encode(['code'=>500,'msg'=>$e->getMessage()]);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -27,4 +27,5 @@ return [
|
||||
'allotrule:autocreate' => 'app\command\AutoCreateAllotRulesCommand', // 自动创建分配规则 √
|
||||
'content:collect' => 'app\command\ContentCollectCommand', // 内容采集任务 √
|
||||
'moments:collect' => 'app\command\WechatMomentsCommand', // 朋友圈采集任务
|
||||
'workbench:run' => 'app\command\WorkbenchCommand', // 工作台任务
|
||||
];
|
||||
|
||||
77
Server/application/command/WorkbenchCommand.php
Normal file
77
Server/application/command/WorkbenchCommand.php
Normal file
@@ -0,0 +1,77 @@
|
||||
<?php
|
||||
|
||||
namespace app\command;
|
||||
|
||||
use think\console\Command;
|
||||
use think\console\Input;
|
||||
use think\console\Output;
|
||||
use think\console\input\Option;
|
||||
use think\facade\Log;
|
||||
use think\Queue;
|
||||
use app\job\WorkbenchJob;
|
||||
use think\facade\Cache;
|
||||
|
||||
class WorkbenchCommand extends Command
|
||||
{
|
||||
// 队列名称
|
||||
protected $queueName = 'workbench';
|
||||
|
||||
protected function configure()
|
||||
{
|
||||
$this->setName('workbench:run')
|
||||
->setDescription('工作台任务队列')
|
||||
->addOption('jobId', null, Option::VALUE_OPTIONAL, '任务ID,用于区分不同实例', date('YmdHis') . rand(1000, 9999));
|
||||
}
|
||||
|
||||
protected function execute(Input $input, Output $output)
|
||||
{
|
||||
$output->writeln('开始处理工作台任务...');
|
||||
|
||||
try {
|
||||
// 获取任务ID
|
||||
$jobId = $input->getOption('jobId');
|
||||
|
||||
$output->writeln('任务ID: ' . $jobId);
|
||||
|
||||
// 检查队列是否已经在运行
|
||||
$queueLockKey = "queue_lock:{$this->queueName}";
|
||||
Cache::rm($queueLockKey);
|
||||
if (Cache::get($queueLockKey)) {
|
||||
$output->writeln("队列 {$this->queueName} 已经在运行中,跳过执行");
|
||||
Log::warning("队列 {$this->queueName} 已经在运行中,跳过执行");
|
||||
return false;
|
||||
}
|
||||
|
||||
// 设置队列运行锁,有效期1小时
|
||||
Cache::set($queueLockKey, $jobId, 3600);
|
||||
$output->writeln("已设置队列运行锁,键名:{$queueLockKey},值:{$jobId},有效期:1小时");
|
||||
|
||||
// 将任务添加到队列
|
||||
$this->addToQueue($jobId, $queueLockKey);
|
||||
|
||||
$output->writeln('工作台任务已添加到队列');
|
||||
} catch (\Exception $e) {
|
||||
Log::error('工作台任务添加失败:' . $e->getMessage());
|
||||
$output->writeln('工作台任务添加失败:' . $e->getMessage());
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加任务到队列
|
||||
* @param string $jobId 任务ID
|
||||
* @param string $queueLockKey 队列锁键名
|
||||
*/
|
||||
public function addToQueue($jobId = '', $queueLockKey = '')
|
||||
{
|
||||
$data = [
|
||||
'jobId' => $jobId,
|
||||
'queueLockKey' => $queueLockKey
|
||||
];
|
||||
|
||||
// 添加到队列,设置任务名为 workbench
|
||||
Queue::push(WorkbenchJob::class, $data, $this->queueName);
|
||||
}
|
||||
}
|
||||
@@ -10,7 +10,7 @@ use think\facade\Log;
|
||||
|
||||
class AuthService
|
||||
{
|
||||
const TOKEN_EXPIRE = 7200;
|
||||
const TOKEN_EXPIRE = 86400;
|
||||
|
||||
protected $smsService;
|
||||
|
||||
|
||||
@@ -71,8 +71,9 @@ class WorkbenchController extends Controller
|
||||
$config->endTime = $param['endTime'];
|
||||
$config->contentTypes = json_encode($param['contentTypes']);
|
||||
$config->devices = json_encode($param['devices']);
|
||||
$config->targetGroups = json_encode($param['targetGroups']);
|
||||
$config->tagOperator = $param['tagOperator'];
|
||||
$config->friends = json_encode($param['friends']);
|
||||
// $config->targetGroups = json_encode($param['targetGroups']);
|
||||
// $config->tagOperator = $param['tagOperator'];
|
||||
$config->createTime = time();
|
||||
$config->updateTime = time();
|
||||
$config->save();
|
||||
@@ -156,7 +157,7 @@ class WorkbenchController extends Controller
|
||||
// 定义关联关系
|
||||
$with = [
|
||||
'autoLike' => function($query) {
|
||||
$query->field('workbenchId,interval,maxLikes,startTime,endTime,contentTypes,devices,targetGroups');
|
||||
$query->field('workbenchId,interval,maxLikes,startTime,endTime,contentTypes,devices,friends');
|
||||
},
|
||||
'momentsSync' => function($query) {
|
||||
$query->field('workbenchId,syncInterval,syncCount,syncType,startTime,endTime,accountType,devices,contentLibraries');
|
||||
@@ -179,8 +180,9 @@ class WorkbenchController extends Controller
|
||||
if (!empty($item->autoLike)) {
|
||||
$item->config = $item->autoLike;
|
||||
$item->config->devices = json_decode($item->config->devices, true);
|
||||
$item->config->targetGroups = json_decode($item->config->targetGroups, true);
|
||||
//$item->config->targetGroups = json_decode($item->config->targetGroups, true);
|
||||
$item->config->contentTypes = json_decode($item->config->contentTypes, true);
|
||||
$item->config->friends = json_decode($item->config->friends, true);
|
||||
}
|
||||
unset($item->autoLike,$item->auto_like);
|
||||
break;
|
||||
@@ -256,7 +258,7 @@ class WorkbenchController extends Controller
|
||||
// 定义关联关系
|
||||
$with = [
|
||||
'autoLike' => function($query) {
|
||||
$query->field('workbenchId,interval,maxLikes,startTime,endTime,contentTypes,devices,targetGroups');
|
||||
$query->field('workbenchId,interval,maxLikes,startTime,endTime,contentTypes,devices,friends');
|
||||
},
|
||||
'momentsSync' => function($query) {
|
||||
$query->field('workbenchId,syncInterval,syncCount,syncType,startTime,endTime,accountType,devices,contentLibraries');
|
||||
@@ -288,7 +290,8 @@ class WorkbenchController extends Controller
|
||||
if (!empty($workbench->autoLike)) {
|
||||
$workbench->config = $workbench->autoLike;
|
||||
$workbench->config->devices = json_decode($workbench->config->devices, true);
|
||||
$workbench->config->targetGroups = json_decode($workbench->config->targetGroups, true);
|
||||
$workbench->config->friends = json_decode($workbench->config->friends, true);
|
||||
//$workbench->config->targetGroups = json_decode($workbench->config->targetGroups, true);
|
||||
$workbench->config->contentTypes = json_decode($workbench->config->contentTypes, true);
|
||||
unset($workbench->autoLike,$workbench->auto_like);
|
||||
}
|
||||
@@ -372,8 +375,9 @@ class WorkbenchController extends Controller
|
||||
$config->endTime = $param['endTime'];
|
||||
$config->contentTypes = json_encode($param['contentTypes']);
|
||||
$config->devices = json_encode($param['devices']);
|
||||
$config->targetGroups = json_encode($param['targetGroups']);
|
||||
$config->tagOperator = $param['tagOperator'];
|
||||
$config->friends = json_encode($param['friends']);
|
||||
// $config->targetGroups = json_encode($param['targetGroups']);
|
||||
// $config->tagOperator = $param['tagOperator'];
|
||||
$config->updateTime = time();
|
||||
$config->save();
|
||||
}
|
||||
@@ -540,7 +544,8 @@ class WorkbenchController extends Controller
|
||||
$newConfig->endTime = $config->endTime;
|
||||
$newConfig->contentTypes = $config->contentTypes;
|
||||
$newConfig->devices = $config->devices;
|
||||
$newConfig->targetGroups = $config->targetGroups;
|
||||
$newConfig->friends = $config->friends;
|
||||
//$newConfig->targetGroups = $config->targetGroups;
|
||||
$newConfig->save();
|
||||
}
|
||||
break;
|
||||
|
||||
@@ -25,7 +25,7 @@ class Workbench extends Validate
|
||||
'startTime' => 'requireIf:type,1|dateFormat:H:i',
|
||||
'endTime' => 'requireIf:type,1|dateFormat:H:i',
|
||||
'contentTypes' => 'requireIf:type,1|array|contentTypeEnum:text,image,video',
|
||||
'targetGroups' => 'requireIf:type,1|array',
|
||||
//'targetGroups' => 'requireIf:type,1|array',
|
||||
// 朋友圈同步特有参数
|
||||
'syncInterval' => 'requireIf:type,2|number|min:1',
|
||||
'syncCount' => 'requireIf:type,2|number|min:1',
|
||||
|
||||
460
Server/application/job/WorkbenchJob.php
Normal file
460
Server/application/job/WorkbenchJob.php
Normal file
@@ -0,0 +1,460 @@
|
||||
<?php
|
||||
|
||||
namespace app\job;
|
||||
|
||||
use think\queue\Job;
|
||||
use think\facade\Log;
|
||||
use think\Queue;
|
||||
use think\facade\Config;
|
||||
use think\facade\Cache;
|
||||
use app\cunkebao\model\Workbench;
|
||||
use app\cunkebao\model\WorkbenchAutoLike;
|
||||
use app\cunkebao\model\WorkbenchMomentsSync;
|
||||
use app\cunkebao\model\WorkbenchGroupPush;
|
||||
use app\cunkebao\model\WorkbenchGroupCreate;
|
||||
use think\Db;
|
||||
use app\api\controller\WebSocketController;
|
||||
|
||||
class WorkbenchJob
|
||||
{
|
||||
/**
|
||||
* 工作台类型定义
|
||||
*/
|
||||
const TYPE_AUTO_LIKE = 1; // 自动点赞
|
||||
const TYPE_MOMENTS_SYNC = 2; // 朋友圈同步
|
||||
const TYPE_GROUP_PUSH = 3; // 群消息推送
|
||||
const TYPE_GROUP_CREATE = 4; // 自动建群
|
||||
|
||||
/**
|
||||
* 最大重试次数
|
||||
*/
|
||||
const MAX_RETRY_ATTEMPTS = 3;
|
||||
|
||||
/**
|
||||
* 队列任务处理
|
||||
* @param Job $job 队列任务
|
||||
* @param array $data 任务数据
|
||||
* @return bool
|
||||
*/
|
||||
public function fire(Job $job, $data)
|
||||
{
|
||||
$jobId = $data['jobId'] ?? '';
|
||||
$queueLockKey = $data['queueLockKey'] ?? '';
|
||||
|
||||
try {
|
||||
$this->logJobStart($jobId, $queueLockKey);
|
||||
|
||||
$workbenches = $this->getActiveWorkbenches();
|
||||
if ($workbenches->isEmpty()) {
|
||||
$this->handleEmptyWorkbenches($job, $queueLockKey);
|
||||
return true;
|
||||
}
|
||||
|
||||
$this->processWorkbenches($workbenches);
|
||||
|
||||
$this->handleJobSuccess($job, $queueLockKey);
|
||||
return true;
|
||||
|
||||
} catch (\Exception $e) {
|
||||
return $this->handleJobError($e, $job, $queueLockKey);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取活跃的工作台
|
||||
* @return \think\Collection
|
||||
*/
|
||||
protected function getActiveWorkbenches()
|
||||
{
|
||||
return Workbench::where([
|
||||
['status', '=', 1],
|
||||
['isDel', '=', 0]
|
||||
])->order('id DESC')->select();
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理空工作台情况
|
||||
* @param Job $job
|
||||
* @param string $queueLockKey
|
||||
*/
|
||||
protected function handleEmptyWorkbenches(Job $job, $queueLockKey)
|
||||
{
|
||||
Log::info('没有需要处理的工作台任务');
|
||||
$job->delete();
|
||||
Cache::rm($queueLockKey);
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理工作台列表
|
||||
* @param \think\Collection $workbenches
|
||||
*/
|
||||
protected function processWorkbenches($workbenches)
|
||||
{
|
||||
foreach ($workbenches as $workbench) {
|
||||
try {
|
||||
$this->processSingleWorkbench($workbench);
|
||||
} catch (\Exception $e) {
|
||||
Log::error("处理工作台 {$workbench->id} 失败: " . $e->getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理单个工作台
|
||||
* @param Workbench $workbench
|
||||
*/
|
||||
protected function processSingleWorkbench($workbench)
|
||||
{
|
||||
$config = $this->getWorkbenchConfig($workbench);
|
||||
if (!$config) {
|
||||
Log::error("工作台 {$workbench->id} 配置获取失败");
|
||||
return;
|
||||
}
|
||||
|
||||
$handler = $this->getWorkbenchHandler($workbench->type);
|
||||
if ($handler) {
|
||||
$handler($workbench, $config);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取工作台处理器
|
||||
* @param int $type
|
||||
* @return callable|null
|
||||
*/
|
||||
protected function getWorkbenchHandler($type)
|
||||
{
|
||||
$handlers = [
|
||||
self::TYPE_AUTO_LIKE => [$this, 'handleAutoLike'],
|
||||
self::TYPE_MOMENTS_SYNC => [$this, 'handleMomentsSync'],
|
||||
self::TYPE_GROUP_PUSH => [$this, 'handleGroupPush'],
|
||||
self::TYPE_GROUP_CREATE => [$this, 'handleGroupCreate']
|
||||
];
|
||||
|
||||
return $handlers[$type] ?? null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理自动点赞任务
|
||||
* @param Workbench $workbench
|
||||
* @param WorkbenchAutoLike $config
|
||||
*/
|
||||
protected function handleAutoLike($workbench, $config)
|
||||
{
|
||||
if (!$this->validateAutoLikeConfig($workbench, $config)) {
|
||||
return;
|
||||
}
|
||||
|
||||
$likeCount = $this->getTodayLikeCount($workbench, $config);
|
||||
if ($likeCount >= $config['maxLikes']) {
|
||||
Log::info("工作台 {$workbench->id} 点赞次数已达上限");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!$this->isWithinLikeTimeRange($config)) {
|
||||
return;
|
||||
}
|
||||
|
||||
$friendList = $this->getFriendList($config['friends']);
|
||||
foreach ($friendList as $friend) {
|
||||
$this->processFriendMoments($workbench, $config, $friend, $likeCount);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 验证自动点赞配置
|
||||
* @param Workbench $workbench
|
||||
* @param WorkbenchAutoLike $config
|
||||
* @return bool
|
||||
*/
|
||||
protected function validateAutoLikeConfig($workbench, $config)
|
||||
{
|
||||
$requiredFields = ['friends', 'contentTypes', 'interval', 'maxLikes', 'startTime', 'endTime'];
|
||||
foreach ($requiredFields as $field) {
|
||||
if (empty($config[$field])) {
|
||||
Log::error("工作台 {$workbench->id} 配置字段 {$field} 为空");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
$friends = json_decode($config['friends'], true);
|
||||
if (!is_array($friends) || empty($friends)) {
|
||||
Log::error("工作台 {$workbench->id} 点赞的好友为空");
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取今日点赞次数
|
||||
* @param Workbench $workbench
|
||||
* @param WorkbenchAutoLike $config
|
||||
* @return int
|
||||
*/
|
||||
protected function getTodayLikeCount($workbench, $config)
|
||||
{
|
||||
return Db::name('workbench_auto_like_item')
|
||||
->where('workbenchId', $workbench->id)
|
||||
->whereTime('createTime', 'between', [
|
||||
strtotime(date('Y-m-d') . ' ' . $config['startTime'] . ':00'),
|
||||
strtotime(date('Y-m-d') . ' ' . $config['endTime'] . ':00')
|
||||
])
|
||||
->count();
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查是否在点赞时间范围内
|
||||
* @param WorkbenchAutoLike $config
|
||||
* @return bool
|
||||
*/
|
||||
protected function isWithinLikeTimeRange($config)
|
||||
{
|
||||
$currentTime = date('H:i');
|
||||
if ($currentTime < $config['startTime'] || $currentTime > $config['endTime']) {
|
||||
Log::info("当前时间 {$currentTime} 不在点赞时间范围内 ({$config['startTime']} - {$config['endTime']})");
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取好友列表
|
||||
* @param string $friendsJson
|
||||
* @return array
|
||||
*/
|
||||
protected function getFriendList($friendsJson)
|
||||
{
|
||||
$friends = json_decode($friendsJson, true);
|
||||
return Db::table('s2_company_account')
|
||||
->alias('ca')
|
||||
->join(['s2_wechat_account' => 'wa'], 'ca.id = wa.deviceAccountId')
|
||||
->join(['s2_wechat_friend' => 'wf'], 'ca.id = wf.accountId')
|
||||
->where('ca.passwordLocal', '<>', '')
|
||||
->where([
|
||||
'ca.status' => 0,
|
||||
'wf.isDeleted' => 0,
|
||||
'wa.deviceAlive' => 1,
|
||||
'wa.wechatAlive' => 1
|
||||
])
|
||||
->whereIn('wf.id', $friends)
|
||||
->field([
|
||||
'ca.id as accountId',
|
||||
'ca.userName',
|
||||
'ca.passwordLocal',
|
||||
'wf.id as friendId',
|
||||
'wf.wechatId',
|
||||
'wf.wechatAccountId'
|
||||
])
|
||||
->group('wf.wechatAccountId DESC')
|
||||
->order('ca.id DESC')
|
||||
->select()
|
||||
->toArray();
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理好友朋友圈
|
||||
* @param Workbench $workbench
|
||||
* @param WorkbenchAutoLike $config
|
||||
* @param array $friend
|
||||
* @param int &$likeCount
|
||||
*/
|
||||
protected function processFriendMoments($workbench, $config, $friend, &$likeCount)
|
||||
{
|
||||
$moments = $this->getUnlikedMoments($friend['friendId']);
|
||||
if ($moments->isEmpty()) {
|
||||
Log::info("好友 {$friend['friendId']} 没有需要点赞的朋友圈");
|
||||
return;
|
||||
}
|
||||
|
||||
foreach ($moments as $moment) {
|
||||
if ($likeCount >= $config['maxLikes']) {
|
||||
break;
|
||||
}
|
||||
|
||||
$this->likeMoment($workbench, $config, $friend, $moment, $likeCount);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取未点赞的朋友圈
|
||||
* @param int $friendId
|
||||
* @return \think\Collection
|
||||
*/
|
||||
protected function getUnlikedMoments($friendId)
|
||||
{
|
||||
return Db::table('s2_wechat_moments')
|
||||
->alias('wm')
|
||||
->join('workbench_auto_like_item wali', 'wali.momentsId = wm.id', 'left')
|
||||
->where([
|
||||
['wm.wechatFriendId', '=', $friendId],
|
||||
['wali.id', 'null', null]
|
||||
])
|
||||
->field('wm.id, wm.snsId')
|
||||
->order('wm.id DESC')
|
||||
->select();
|
||||
}
|
||||
|
||||
/**
|
||||
* 点赞朋友圈
|
||||
* @param Workbench $workbench
|
||||
* @param WorkbenchAutoLike $config
|
||||
* @param array $friend
|
||||
* @param array $moment
|
||||
* @param int &$likeCount
|
||||
*/
|
||||
protected function likeMoment($workbench, $config, $friend, $moment, &$likeCount)
|
||||
{
|
||||
try {
|
||||
$wsController = new WebSocketController([
|
||||
'userName' => $friend['userName'],
|
||||
'password' => localDecrypt($friend['passwordLocal']),
|
||||
'accountId' => $friend['accountId']
|
||||
]);
|
||||
|
||||
$result = $wsController->momentInteract([
|
||||
'snsId' => $moment['snsId'],
|
||||
'wechatAccountId' => $friend['wechatAccountId'],
|
||||
]);
|
||||
|
||||
$result = json_decode($result, true);
|
||||
|
||||
if ($result['code'] == 200) {
|
||||
$this->recordLike($workbench, $moment, $friend);
|
||||
$likeCount++;
|
||||
sleep($config['interval']);
|
||||
} else {
|
||||
Log::error("工作台 {$workbench->id} 点赞失败: " . ($result['msg'] ?? '未知错误'));
|
||||
}
|
||||
} catch (\Exception $e) {
|
||||
Log::error("工作台 {$workbench->id} 点赞异常: " . $e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 记录点赞
|
||||
* @param Workbench $workbench
|
||||
* @param array $moment
|
||||
* @param array $friend
|
||||
*/
|
||||
protected function recordLike($workbench, $moment, $friend)
|
||||
{
|
||||
Db::name('workbench_auto_like_item')->insert([
|
||||
'workbenchId' => $workbench->id,
|
||||
'momentsId' => $moment['id'],
|
||||
'snsId' => $moment['snsId'],
|
||||
'wechatAccountId' => $friend['wechatAccountId'],
|
||||
'wechatFriendId' => $friend['friendId'],
|
||||
'createTime' => time()
|
||||
]);
|
||||
Log::info("工作台 {$workbench->id} 点赞成功: {$moment['snsId']}");
|
||||
}
|
||||
|
||||
/**
|
||||
* 记录任务开始
|
||||
* @param string $jobId
|
||||
* @param string $queueLockKey
|
||||
*/
|
||||
protected function logJobStart($jobId, $queueLockKey)
|
||||
{
|
||||
Log::info('开始处理工作台任务: ' . json_encode([
|
||||
'jobId' => $jobId,
|
||||
'queueLockKey' => $queueLockKey
|
||||
]));
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理任务成功
|
||||
* @param Job $job
|
||||
* @param string $queueLockKey
|
||||
*/
|
||||
protected function handleJobSuccess($job, $queueLockKey)
|
||||
{
|
||||
$job->delete();
|
||||
Cache::rm($queueLockKey);
|
||||
Log::info('工作台任务执行成功');
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理任务错误
|
||||
* @param \Exception $e
|
||||
* @param Job $job
|
||||
* @param string $queueLockKey
|
||||
* @return bool
|
||||
*/
|
||||
protected function handleJobError(\Exception $e, $job, $queueLockKey)
|
||||
{
|
||||
Log::error('工作台任务异常:' . $e->getMessage());
|
||||
|
||||
if (!empty($queueLockKey)) {
|
||||
Cache::rm($queueLockKey);
|
||||
Log::info("由于异常释放队列锁: {$queueLockKey}");
|
||||
}
|
||||
|
||||
if ($job->attempts() > self::MAX_RETRY_ATTEMPTS) {
|
||||
$job->delete();
|
||||
} else {
|
||||
$job->release(Config::get('queue.failed_delay', 10));
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取工作台配置
|
||||
* @param Workbench $workbench 工作台实例
|
||||
* @return mixed
|
||||
*/
|
||||
protected function getWorkbenchConfig($workbench)
|
||||
{
|
||||
switch ($workbench->type) {
|
||||
case self::TYPE_AUTO_LIKE:
|
||||
return WorkbenchAutoLike::where('workbenchId', $workbench->id)->find();
|
||||
|
||||
case self::TYPE_MOMENTS_SYNC:
|
||||
return WorkbenchMomentsSync::where('workbenchId', $workbench->id)->find();
|
||||
|
||||
case self::TYPE_GROUP_PUSH:
|
||||
return WorkbenchGroupPush::where('workbenchId', $workbench->id)->find();
|
||||
|
||||
case self::TYPE_GROUP_CREATE:
|
||||
return WorkbenchGroupCreate::where('workbenchId', $workbench->id)->find();
|
||||
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理朋友圈同步任务
|
||||
* @param Workbench $workbench 工作台实例
|
||||
* @param WorkbenchMomentsSync $config 配置实例
|
||||
*/
|
||||
protected function handleMomentsSync($workbench, $config)
|
||||
{
|
||||
// TODO: 实现朋友圈同步逻辑
|
||||
Log::info("处理朋友圈同步任务: {$workbench->id}");
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理群消息推送任务
|
||||
* @param Workbench $workbench 工作台实例
|
||||
* @param WorkbenchGroupPush $config 配置实例
|
||||
*/
|
||||
protected function handleGroupPush($workbench, $config)
|
||||
{
|
||||
// TODO: 实现群消息推送逻辑
|
||||
Log::info("处理群消息推送任务: {$workbench->id}");
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理自动建群任务
|
||||
* @param Workbench $workbench 工作台实例
|
||||
* @param WorkbenchGroupCreate $config 配置实例
|
||||
*/
|
||||
protected function handleGroupCreate($workbench, $config)
|
||||
{
|
||||
// TODO: 实现自动建群逻辑
|
||||
Log::info("处理自动建群任务: {$workbench->id}");
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user