Merge branch 'develop' into yongpxu-dev

This commit is contained in:
乘风
2025-12-02 10:54:28 +08:00
7 changed files with 5416 additions and 63 deletions

254
Server/README_scheduler.md Normal file
View File

@@ -0,0 +1,254 @@
# 统一任务调度器使用说明
## 概述
统一任务调度器TaskSchedulerCommand是一个集中管理所有定时任务的调度系统支持
- ✅ 单条 crontab 配置管理所有任务
- ✅ 多进程并发执行任务
- ✅ 自动根据 cron 表达式判断任务执行时间
- ✅ 任务锁机制,防止重复执行
- ✅ 完善的日志记录
## 安装配置
### 1. 配置文件
任务配置位于 `config/task_scheduler.php`,格式如下:
```php
'任务标识' => [
'command' => '命令名称', // 必填:执行的命令
'schedule' => 'cron表达式', // 必填cron表达式
'options' => ['--option=value'], // 可选:命令参数
'enabled' => true, // 可选:是否启用
'max_concurrent' => 1, // 可选:最大并发数
'timeout' => 3600, // 可选:超时时间(秒)
'log_file' => 'custom.log', // 可选:自定义日志文件
]
```
### 2. Cron 表达式格式
标准 cron 格式:`分钟 小时 日 月 星期`
示例:
- `*/1 * * * *` - 每分钟执行
- `*/5 * * * *` - 每5分钟执行
- `*/30 * * * *` - 每30分钟执行
- `0 2 * * *` - 每天凌晨2点执行
- `0 3 */3 * *` - 每3天的3点执行
### 3. Crontab 配置
**只需要在 crontab 中添加一条任务:**
```bash
# 每分钟执行一次调度器(调度器内部会根据 cron 表达式判断哪些任务需要执行)
* * * * * cd /www/wwwroot/mckb_quwanzhi_com/Server && php think scheduler:run >> /www/wwwroot/mckb_quwanzhi_com/Server/runtime/log/scheduler.log 2>&1
```
### 4. 系统要求
- PHP >= 5.6.0
- 推荐启用 `pcntl` 扩展以支持多进程并发(非必需,未启用时使用单进程顺序执行)
检查 pcntl 扩展:
```bash
php -m | grep pcntl
```
## 使用方法
### 手动执行调度器
```bash
# 执行调度器(会自动判断当前时间需要执行的任务)
php think scheduler:run
```
### 查看任务配置
```bash
# 查看所有已注册的命令
php think list
```
### 启用/禁用任务
编辑 `config/task_scheduler.php`,设置 `'enabled' => false` 即可禁用任务。
## 功能特性
### 1. 多进程并发执行
- 默认最大并发数10 个进程
- 自动管理进程池
- 自动清理僵尸进程
### 2. 任务锁机制
- 每个任务在执行时会设置锁5分钟内不重复执行
- 防止任务重复执行
- 锁存储在缓存中,自动过期
### 3. 日志记录
- 调度器日志:`runtime/log/scheduler.log`
- 每个任务的日志:`runtime/log/{log_file}`
- 任务执行开始和结束都有标记
### 4. 超时控制
- 默认超时时间3600 秒1小时
- 可在配置中为每个任务单独设置超时时间
- 超时后自动终止任务
## 配置示例
### 高频任务(每分钟)
```php
'wechat_friends_active' => [
'command' => 'wechatFriends:list',
'schedule' => '*/1 * * * *',
'options' => ['--isDel=0'],
'enabled' => true,
],
```
### 中频任务每5分钟
```php
'device_active' => [
'command' => 'device:list',
'schedule' => '*/5 * * * *',
'options' => ['--isDel=0'],
'enabled' => true,
],
```
### 每日任务
```php
'wechat_calculate_score' => [
'command' => 'wechat:calculate-score',
'schedule' => '0 2 * * *', // 每天凌晨2点
'options' => [],
'enabled' => true,
],
```
### 定期任务每3天
```php
'sync_all_friends' => [
'command' => 'sync:allFriends',
'schedule' => '0 3 */3 * *', // 每3天的3点
'options' => [],
'enabled' => true,
],
```
## 从旧配置迁移
### 旧配置(多条 crontab
```bash
*/5 * * * * cd /path && php think device:list --isDel=0 >> log1.log 2>&1
*/1 * * * * cd /path && php think wechatFriends:list >> log2.log 2>&1
```
### 新配置(单条 crontab + 配置文件)
**Crontab**
```bash
* * * * * cd /path && php think scheduler:run >> scheduler.log 2>&1
```
**config/task_scheduler.php**
```php
'device_active' => [
'command' => 'device:list',
'schedule' => '*/5 * * * *',
'options' => ['--isDel=0'],
'log_file' => 'log1.log',
],
'wechat_friends' => [
'command' => 'wechatFriends:list',
'schedule' => '*/1 * * * *',
'log_file' => 'log2.log',
],
```
## 监控和调试
### 查看调度器日志
```bash
tail -f runtime/log/scheduler.log
```
### 查看任务执行日志
```bash
tail -f runtime/log/crontab_device_active.log
```
### 检查任务是否在执行
```bash
# 查看进程
ps aux | grep "php think"
```
### 手动测试任务
```bash
# 直接执行某个任务
php think device:list --isDel=0
```
## 注意事项
1. **时间同步**:确保服务器时间准确,调度器依赖系统时间判断任务执行时间
2. **资源限制**:根据服务器性能调整 `maxConcurrent` 参数
3. **日志清理**:定期清理日志文件,避免占用过多磁盘空间
4. **任务冲突**:如果任务执行时间较长,建议调整执行频率或增加并发数
5. **缓存依赖**:任务锁使用缓存,确保缓存服务正常运行
## 故障排查
### 任务未执行
1. 检查任务是否启用:`'enabled' => true`
2. 检查 cron 表达式是否正确
3. 检查调度器是否正常运行:查看 `scheduler.log`
4. 检查任务锁任务可能在5分钟内重复执行被跳过
### 任务执行失败
1. 查看任务日志:`runtime/log/{log_file}`
2. 检查命令是否正确:手动执行命令测试
3. 检查权限:确保有执行权限和日志写入权限
### 多进程不工作
1. 检查 pcntl 扩展:`php -m | grep pcntl`
2. 检查系统限制:`ulimit -u` 查看最大进程数
3. 查看调度器日志中的错误信息
## 性能优化建议
1. **合理设置并发数**:根据服务器 CPU 核心数和内存大小调整
2. **错开高频任务**:避免所有任务在同一分钟执行
3. **优化任务执行时间**:减少任务执行时长
4. **使用队列**:对于耗时任务,建议使用队列异步处理
## 更新日志
### v1.0.0 (2024-01-XX)
- 初始版本
- 支持多进程并发执行
- 支持 cron 表达式调度
- 支持任务锁机制

View File

@@ -61,10 +61,8 @@ class MessageController extends BaseController
// 发送请求获取好友列表
$result = requestCurl($this->baseUrl . 'api/WechatFriend/listWechatFriendForMsgPagination', $params, 'POST', $header, 'json');
$response = handleApiResponse($result);
// 获取同步消息标志
$syncMessages = $this->request->param('syncMessages', true);
// 如果需要同步消息,则获取每个好友的消息
if ($syncMessages && !empty($response['results'])) {
$from = strtotime($fromTime) * 1000;
@@ -77,7 +75,7 @@ class MessageController extends BaseController
'keyword' => '',
'msgType' => '',
'accountId' => '',
'count' => 100,
'count' => 20,
'messageId' => '',
'olderData' => true,
'wechatAccountId' => $friend['wechatAccountId'],
@@ -90,7 +88,6 @@ class MessageController extends BaseController
// 调用获取消息的接口
$messageResult = requestCurl($this->baseUrl . 'api/FriendMessage/searchMessage', $messageParams, 'GET', $header, 'json');
$messageResponse = handleApiResponse($messageResult);
// 保存消息到数据库
if (!empty($messageResponse)) {
foreach ($messageResponse as $item) {
@@ -241,7 +238,7 @@ class MessageController extends BaseController
'keyword' => '',
'msgType' =>'',
'accountId' => '',
'count' => 100,
'count' => 20,
'messageId' => '',
'olderData' => true,
'wechatId' => '',
@@ -353,12 +350,12 @@ class MessageController extends BaseController
{
// 检查消息是否已存在
$exists = WechatMessageModel::where('id', $item['id']) ->find();
// 如果消息已存在,直接返回
if ($exists) {
return;
if (!empty($exists) && $exists['sendStatus'] == 0){
return true;
}
// 将毫秒时间戳转换为秒级时间戳
$createTime = isset($item['createTime']) ? strtotime($item['createTime']) : null;
$deleteTime = !empty($item['isDeleted']) ? strtotime($item['deleteTime']) : null;
@@ -387,7 +384,8 @@ class MessageController extends BaseController
'wechatTime' => $wechatTime
];
//已被删除
//已被删除
if ($item['msgType'] == 10000 && strpos($item['content'],'开启了朋友验证') !== false) {
Db::table('s2_wechat_friend')->where('id',$item['wechatFriendId'])->update(['isDeleted'=> 1,'deleteTime' => $wechatTime]);
}else{
@@ -425,8 +423,19 @@ class MessageController extends BaseController
}
}
}
// 创建新记录
$res = WechatMessageModel::create($data);
$id = '';
if (empty($exists)){
// 创建新记录
$res = WechatMessageModel::create($data);
$id= $res['id'];
}else{
$id = $data['id'];
unset($data['id']);
$res = $exists->save($data);
}
// 1 文字 3图片 47动态图片 34语言 43视频 42名片 40/20链接 49文件
if (!empty($res) && empty($item['isSend']) && in_array($item['msgType'],[1,3,20,34,40,42,43,47,49])){
@@ -439,13 +448,14 @@ class MessageController extends BaseController
'companyId' => $friend['companyId'],
'trafficPoolId' => $trafficPoolId,
'source' => 0,
'uniqueId' => $res['id'],
'uniqueId' => $id,
'sourceData' => json_encode([]),
'remark' => '用户发送了消息',
'createTime' => time(),
'updateTime' => time()
];
Db::name('user_portrait')->insert($data);
Db::name('user_portrait')->insert($data);
}
}
}
@@ -461,9 +471,8 @@ class MessageController extends BaseController
{
// 检查消息是否已存在
$exists = WechatMessageModel::where('id', $item['id'])->find();
// 如果消息已存在,直接返回
if ($exists) {
if (!empty($exists) && $exists['sendStatus'] == 0){
return true;
}
@@ -515,7 +524,12 @@ class MessageController extends BaseController
// 创建新记录
try {
WechatMessageModel::create($data);
if(empty($exists)){
WechatMessageModel::create($data);
}else{
unset($data['id']);
$exists->save($data);
}
return true;
} catch (\Exception $e) {
return false;

View File

@@ -42,4 +42,7 @@ return [
'wechat:calculate-score' => 'app\command\CalculateWechatAccountScoreCommand', // 统一计算微信账号健康分
'wechat:update-score' => 'app\command\UpdateWechatAccountScoreCommand', // 更新微信账号评分记录
// 统一任务调度器
'scheduler:run' => 'app\command\TaskSchedulerCommand', // 统一任务调度器,支持多进程并发执行
];

View File

@@ -0,0 +1,478 @@
<?php
namespace app\command;
use think\console\Command;
use think\console\Input;
use think\console\Output;
use think\facade\Config;
use think\facade\Log;
use think\facade\Cache;
/**
* 统一任务调度器
* 支持多进程并发执行任务
*
* 使用方法:
* php think scheduler:run
*
* 在 crontab 中配置:
* * * * * * cd /path/to/project && php think scheduler:run >> /path/to/log/scheduler.log 2>&1
*/
class TaskSchedulerCommand extends Command
{
/**
* 任务配置
*/
protected $tasks = [];
/**
* 最大并发进程数
*/
protected $maxConcurrent = 10;
/**
* 当前运行的进程数
*/
protected $runningProcesses = [];
/**
* 日志目录
*/
protected $logDir = '';
protected function configure()
{
$this->setName('scheduler:run')
->setDescription('统一任务调度器,支持多进程并发执行所有定时任务');
}
protected function execute(Input $input, Output $output)
{
$output->writeln('==========================================');
$output->writeln('任务调度器启动');
$output->writeln('时间: ' . date('Y-m-d H:i:s'));
$output->writeln('==========================================');
// 检查是否支持 pcntl 扩展
if (!function_exists('pcntl_fork')) {
$output->writeln('<error>错误:系统不支持 pcntl 扩展,无法使用多进程功能</error>');
$output->writeln('<info>提示:将使用单进程顺序执行任务</info>');
$this->maxConcurrent = 1;
}
// 加载任务配置(优先使用框架配置,其次直接引入配置文件,避免加载失败)
$this->tasks = Config::get('task_scheduler', []);
// 如果通过 Config 没有读到,再尝试直接 include 配置文件
if (empty($this->tasks)) {
// 以项目根目录为基准查找 config/task_scheduler.php
$configFile = dirname(__DIR__, 2) . DIRECTORY_SEPARATOR . 'config' . DIRECTORY_SEPARATOR . 'task_scheduler.php';
if (is_file($configFile)) {
$config = include $configFile;
if (is_array($config) && !empty($config)) {
$this->tasks = $config;
}
}
}
if (empty($this->tasks)) {
$output->writeln('<error>错误未找到任务配置task_scheduler请检查 config/task_scheduler.php 是否存在且返回数组</error>');
return false;
}
// 设置日志目录ThinkPHP5 中无 runtime_path 辅助函数,直接使用 ROOT_PATH/runtime/log
if (!defined('ROOT_PATH')) {
// CLI 下正常情况下 ROOT_PATH 已在入口脚本 define这里兜底一次
define('ROOT_PATH', dirname(__DIR__, 2));
}
$this->logDir = ROOT_PATH . DIRECTORY_SEPARATOR . 'runtime' . DIRECTORY_SEPARATOR . 'log' . DIRECTORY_SEPARATOR;
if (!is_dir($this->logDir)) {
mkdir($this->logDir, 0755, true);
}
// 获取当前时间
$currentTime = time();
$currentMinute = date('i', $currentTime);
$currentHour = date('H', $currentTime);
$currentDay = date('d', $currentTime);
$currentMonth = date('m', $currentTime);
$currentWeekday = date('w', $currentTime); // 0=Sunday, 6=Saturday
$output->writeln("当前时间: {$currentHour}:{$currentMinute}");
$output->writeln("已加载 " . count($this->tasks) . " 个任务配置");
// 筛选需要执行的任务
$tasksToRun = [];
foreach ($this->tasks as $taskId => $task) {
if (!isset($task['enabled']) || !$task['enabled']) {
continue;
}
if ($this->shouldRun($task['schedule'], $currentMinute, $currentHour, $currentDay, $currentMonth, $currentWeekday)) {
$tasksToRun[$taskId] = $task;
}
}
if (empty($tasksToRun)) {
$output->writeln('<info>当前时间没有需要执行的任务</info>');
return true;
}
$output->writeln("找到 " . count($tasksToRun) . " 个需要执行的任务");
// 执行任务
if ($this->maxConcurrent > 1 && function_exists('pcntl_fork')) {
$this->executeConcurrent($tasksToRun, $output);
} else {
$this->executeSequential($tasksToRun, $output);
}
// 清理僵尸进程
$this->cleanupZombieProcesses();
$output->writeln('==========================================');
$output->writeln('任务调度器执行完成');
$output->writeln('==========================================');
return true;
}
/**
* 判断任务是否应该执行
*
* @param string $schedule cron表达式格式分钟 小时 日 月 星期
* @param int $minute 当前分钟
* @param int $hour 当前小时
* @param int $day 当前日期
* @param int $month 当前月份
* @param int $weekday 当前星期
* @return bool
*/
protected function shouldRun($schedule, $minute, $hour, $day, $month, $weekday)
{
$parts = preg_split('/\s+/', trim($schedule));
if (count($parts) < 5) {
return false;
}
list($scheduleMinute, $scheduleHour, $scheduleDay, $scheduleMonth, $scheduleWeekday) = $parts;
// 解析分钟
if (!$this->matchCronField($scheduleMinute, $minute)) {
return false;
}
// 解析小时
if (!$this->matchCronField($scheduleHour, $hour)) {
return false;
}
// 解析日期
if (!$this->matchCronField($scheduleDay, $day)) {
return false;
}
// 解析月份
if (!$this->matchCronField($scheduleMonth, $month)) {
return false;
}
// 解析星期注意cron中0和7都表示星期日
if ($scheduleWeekday !== '*') {
$scheduleWeekday = str_replace('7', '0', $scheduleWeekday);
if (!$this->matchCronField($scheduleWeekday, $weekday)) {
return false;
}
}
return true;
}
/**
* 匹配cron字段
*
* @param string $field cron字段表达式
* @param int $value 当前值
* @return bool
*/
protected function matchCronField($field, $value)
{
// 通配符
if ($field === '*') {
return true;
}
// 列表(逗号分隔)
if (strpos($field, ',') !== false) {
$values = explode(',', $field);
foreach ($values as $v) {
if ($this->matchCronField(trim($v), $value)) {
return true;
}
}
return false;
}
// 范围(如 1-5
if (strpos($field, '-') !== false) {
list($start, $end) = explode('-', $field);
return $value >= (int)$start && $value <= (int)$end;
}
// 步长(如 */5 或 0-59/5
if (strpos($field, '/') !== false) {
$parts = explode('/', $field);
$base = $parts[0];
$step = (int)$parts[1];
if ($base === '*') {
return $value % $step === 0;
} else {
// 处理范围步长,如 0-59/5
if (strpos($base, '-') !== false) {
list($start, $end) = explode('-', $base);
if ($value >= (int)$start && $value <= (int)$end) {
return ($value - (int)$start) % $step === 0;
}
return false;
} else {
return $value % $step === 0;
}
}
}
// 精确匹配
return (int)$field === $value;
}
/**
* 并发执行任务(多进程)
*
* @param array $tasks 任务列表
* @param Output $output 输出对象
*/
protected function executeConcurrent($tasks, Output $output)
{
$output->writeln('<info>使用多进程并发执行任务(最大并发数:' . $this->maxConcurrent . '</info>');
foreach ($tasks as $taskId => $task) {
// 等待可用进程槽
while (count($this->runningProcesses) >= $this->maxConcurrent) {
$this->waitForProcesses();
usleep(100000); // 等待100ms
}
// 检查任务是否已经在运行(防止重复执行)
$lockKey = "scheduler_task_lock:{$taskId}";
$lockTime = Cache::get($lockKey);
if ($lockTime && (time() - $lockTime) < 300) { // 5分钟内不重复执行
$output->writeln("<comment>任务 {$taskId} 正在运行中,跳过</comment>");
continue;
}
// 创建子进程
$pid = pcntl_fork();
if ($pid == -1) {
// 创建进程失败
$output->writeln("<error>创建子进程失败:{$taskId}</error>");
Log::error("任务调度器:创建子进程失败", ['task' => $taskId]);
continue;
} elseif ($pid == 0) {
// 子进程:执行任务
$this->runTask($taskId, $task);
exit(0);
} else {
// 父进程记录子进程PID
$this->runningProcesses[$pid] = [
'task_id' => $taskId,
'start_time' => time(),
];
$output->writeln("<info>启动任务:{$taskId} (PID: {$pid})</info>");
// 设置任务锁
Cache::set($lockKey, time(), 600); // 10分钟过期
}
}
// 等待所有子进程完成
while (!empty($this->runningProcesses)) {
$this->waitForProcesses();
usleep(500000); // 等待500ms
}
}
/**
* 顺序执行任务(单进程)
*
* @param array $tasks 任务列表
* @param Output $output 输出对象
*/
protected function executeSequential($tasks, Output $output)
{
$output->writeln('<info>使用单进程顺序执行任务</info>');
foreach ($tasks as $taskId => $task) {
$output->writeln("<info>执行任务:{$taskId}</info>");
$this->runTask($taskId, $task);
}
}
/**
* 执行单个任务
*
* @param string $taskId 任务ID
* @param array $task 任务配置
*/
protected function runTask($taskId, $task)
{
$startTime = microtime(true);
$logFile = $this->logDir . ($task['log_file'] ?? "scheduler_{$taskId}.log");
// 确保日志目录存在
$logDir = dirname($logFile);
if (!is_dir($logDir)) {
mkdir($logDir, 0755, true);
}
// 构建命令
// 使用项目根目录下的 think 脚本(同命令行 php think
if (!defined('ROOT_PATH')) {
define('ROOT_PATH', dirname(__DIR__, 2));
}
$thinkPath = ROOT_PATH . DIRECTORY_SEPARATOR . 'think';
$command = "php {$thinkPath} {$task['command']}";
if (!empty($task['options'])) {
foreach ($task['options'] as $option) {
$command .= ' ' . escapeshellarg($option);
}
}
// 添加日志重定向
$command .= " >> " . escapeshellarg($logFile) . " 2>&1";
// 记录任务开始
$logMessage = "\n" . str_repeat('=', 60) . "\n";
$logMessage .= "任务开始执行: {$taskId}\n";
$logMessage .= "执行时间: " . date('Y-m-d H:i:s') . "\n";
$logMessage .= "命令: {$command}\n";
$logMessage .= str_repeat('=', 60) . "\n";
file_put_contents($logFile, $logMessage, FILE_APPEND);
// 执行命令
$descriptorspec = [
0 => ['file', (PHP_OS_FAMILY === 'Windows' ? 'NUL' : '/dev/null'), 'r'], // stdin
1 => ['file', $logFile, 'a'], // stdout
2 => ['file', $logFile, 'a'], // stderr
];
$process = @proc_open($command, $descriptorspec, $pipes, ROOT_PATH);
if (is_resource($process)) {
// 关闭管道
if (isset($pipes[0])) @fclose($pipes[0]);
if (isset($pipes[1])) @fclose($pipes[1]);
if (isset($pipes[2])) @fclose($pipes[2]);
// 设置超时
$timeout = $task['timeout'] ?? 3600;
$startWaitTime = time();
// 等待进程完成或超时
while (true) {
$status = proc_get_status($process);
if (!$status['running']) {
break;
}
// 检查超时
if ((time() - $startWaitTime) > $timeout) {
if (function_exists('proc_terminate')) {
proc_terminate($process, SIGTERM);
// 等待进程终止
sleep(2);
$status = proc_get_status($process);
if ($status['running']) {
// 强制终止
proc_terminate($process, SIGKILL);
}
}
Log::warning("任务执行超时", [
'task' => $taskId,
'timeout' => $timeout,
]);
break;
}
usleep(500000); // 等待500ms
}
// 关闭进程
proc_close($process);
} else {
// 如果 proc_open 失败,尝试直接执行(后台执行)
if (PHP_OS_FAMILY === 'Windows') {
pclose(popen("start /B " . $command, "r"));
} else {
exec($command . ' > /dev/null 2>&1 &');
}
}
$endTime = microtime(true);
$duration = round($endTime - $startTime, 2);
// 记录任务完成
$logMessage = "\n" . str_repeat('=', 60) . "\n";
$logMessage .= "任务执行完成: {$taskId}\n";
$logMessage .= "完成时间: " . date('Y-m-d H:i:s') . "\n";
$logMessage .= "执行时长: {$duration}\n";
$logMessage .= str_repeat('=', 60) . "\n";
file_put_contents($logFile, $logMessage, FILE_APPEND);
Log::info("任务执行完成", [
'task' => $taskId,
'duration' => $duration,
]);
}
/**
* 等待进程完成
*/
protected function waitForProcesses()
{
foreach ($this->runningProcesses as $pid => $info) {
$status = 0;
$result = pcntl_waitpid($pid, $status, WNOHANG);
if ($result == $pid || $result == -1) {
// 进程已结束
unset($this->runningProcesses[$pid]);
$duration = time() - $info['start_time'];
Log::info("子进程执行完成", [
'pid' => $pid,
'task' => $info['task_id'],
'duration' => $duration,
]);
}
}
}
/**
* 清理僵尸进程
*/
protected function cleanupZombieProcesses()
{
if (!function_exists('pcntl_waitpid')) {
return;
}
$status = 0;
while (($pid = pcntl_waitpid(-1, $status, WNOHANG)) > 0) {
// 清理僵尸进程
}
}
}

View File

@@ -47,26 +47,74 @@
"topthink/think-migration": "^2.0",
"phpunit/phpunit": "^5.0|^6.0"
},
"autoload": {
"psr-4": {
"app\\": "application",
"Eison\\": "extend/Eison"
},
"files": [
"application/common.php"
],
"classmap": []
},
"extra": {
"think-path": "thinkphp"
},
"config": {
"preferred-install": "dist",
"allow-plugins": {
"topthink/think-installer": true,
"easywechat-composer/easywechat-composer": true
}
},
"minimum-stability": "dev",
"prefer-stable": true
}
"autoload": {
"psr-4": {
"app\\": "application",
"Eison\\": "extend/Eison"
},
"files": [
"application/common.php"
],
"homepage": "http://thinkphp.cn/",
"license": "Apache-2.0",
"authors": [
{
"name": "liu21st",
"email": "liu21st@gmail.com"
},
{
"name": "yunwuxin",
"email": "448901948@qq.com"
}
],
"require": {
"php": ">=5.6.0",
"topthink/framework": "5.1.41",
"topthink/think-installer": "~1.0",
"topthink/think-captcha": "^2.0",
"topthink/think-helper": "^3.0",
"topthink/think-image": "^1.0",
"topthink/think-queue": "^2.0",
"topthink/think-worker": "^2.0",
"textalk/websocket": "^1.2",
"aliyuncs/oss-sdk-php": "^2.3",
"monolog/monolog": "^1.24",
"guzzlehttp/guzzle": "^6.3",
"overtrue/wechat": "~4.0",
"endroid/qr-code": "^3.5",
"phpoffice/phpspreadsheet": "^1.8",
"workerman/workerman": "^3.5",
"workerman/gateway-worker": "^3.0",
"hashids/hashids": "^2.0",
"khanamiryan/qrcode-detector-decoder": "^1.0",
"lizhichao/word": "^2.0",
"adbario/php-dot-notation": "^2.2"
},
"require-dev": {
"symfony/var-dumper": "^3.4",
"topthink/think-migration": "^2.0"
},
"autoload": {
"psr-4": {
"app\\": "application",
"Eison\\": "extend/Eison"
},
"files": [
"application/common.php"
],
"classmap": []
},
"extra": {
"think-path": "thinkphp"
},
"config": {
"preferred-install": "dist",
"allow-plugins": {
"topthink/think-installer": true,
"easywechat-composer/easywechat-composer": true
}
},
"minimum-stability": "dev",
"prefer-stable": true
}
}

4299
Server/composer.lock generated

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,301 @@
<?php
// +----------------------------------------------------------------------
// | 任务调度器配置文件
// +----------------------------------------------------------------------
// | 定义所有需要定时执行的任务及其执行频率
// +----------------------------------------------------------------------
return [
// 任务配置格式:
// '任务标识' => [
// 'command' => '命令名称', // 必填:执行的 ThinkPHP 命令(见 application/command.php
// 'schedule' => 'cron表达式', // 必填cron 表达式,如 '*/5 * * * *' 表示每5分钟
// 'options' => ['--option=value'], // 可选:命令参数(原来 crontab 里的 --xxx=yyy
// 'enabled' => true, // 可选:是否启用,默认 true
// 'max_concurrent'=> 1, // 可选:单任务最大并发数(目前由调度器统一控制,可预留)
// 'timeout' => 3600, // 可选:超时时间(秒),默认 3600
// 'log_file' => 'custom.log', // 可选:日志文件名,默认使用任务标识
// ]
// ===========================
// 高频任务(每分钟或更频繁)
// ===========================
// 同步微信好友列表(未删除好友),用于保持系统中好友数据实时更新
'wechat_friends_active' => [
'command' => 'wechatFriends:list',
'schedule' => '*/1 * * * *', // 每1分钟
'options' => ['--isDel=0'],
'enabled' => true,
'max_concurrent' => 1,
'log_file' => 'crontab_wechatFriends_active.log',
],
// 拉取“添加好友任务”列表,驱动自动加好友的任务队列
'friend_task' => [
'command' => 'friendTask:list',
'schedule' => '*/1 * * * *', // 每1分钟
'options' => [],
'enabled' => true,
'max_concurrent' => 1,
'log_file' => 'crontab_friendTask.log',
],
// 同步微信好友私聊消息列表,写入消息表,供客服工作台使用
'message_friends' => [
'command' => 'message:friendsList',
'schedule' => '*/1 * * * *', // 每1分钟
'options' => [],
'enabled' => true,
'max_concurrent' => 1,
'log_file' => 'crontab_messageFriends.log',
],
// 同步微信群聊消息列表,写入消息表,供群聊记录与风控分析
'message_chatroom' => [
'command' => 'message:chatroomList',
'schedule' => '*/1 * * * *', // 每1分钟
'options' => [],
'enabled' => true,
'max_concurrent' => 1,
'log_file' => 'crontab_messageChatroom.log',
],
// 客服端消息提醒任务,负责给在线客服推送新消息通知
'kf_notice' => [
'command' => 'kf:notice',
'schedule' => '*/1 * * * *', // 每1分钟
'options' => [],
'enabled' => true,
'max_concurrent' => 1,
'log_file' => 'kf_notice.log',
],
// ===========================
// 中频任务(每 2-5 分钟)
// ===========================
// 同步微信设备列表(未删除设备),用于设备管理与监控
'device_active' => [
'command' => 'device:list',
'schedule' => '*/5 * * * *', // 每5分钟
'options' => ['--isDel=0'],
'enabled' => true,
'max_concurrent' => 1,
'log_file' => 'crontab_device_active.log',
],
// 同步微信群聊列表(未删除群),用于群管理与后续任务分配
'wechat_chatroom_active' => [
'command' => 'wechatChatroom:list',
'schedule' => '*/5 * * * *', // 每5分钟
'options' => ['--isDel=0'],
'enabled' => true,
'max_concurrent' => 1,
'log_file' => 'crontab_wechatChatroom_active.log',
],
// 同步微信群成员列表(群好友),维持群成员明细数据
'group_friends' => [
'command' => 'groupFriends:list',
'schedule' => '*/5 * * * *', // 每5分钟
'options' => [],
'enabled' => true,
'max_concurrent' => 1,
'log_file' => 'crontab_groupFriends.log',
],
// 同步“微信客服列表”,获取绑定到公司的微信号,用于工作台与分配规则
'wechat_list' => [
'command' => 'wechatList:list',
'schedule' => '*/5 * * * *', // 每5分钟
'options' => [],
'enabled' => true,
'max_concurrent' => 1,
'log_file' => 'crontab_wechatList.log',
],
// 同步公司账号列表(企业/租户账号),供后台管理与统计
'account_list' => [
'command' => 'account:list',
'schedule' => '*/5 * * * *', // 每5分钟
'options' => [],
'enabled' => true,
'max_concurrent' => 1,
'log_file' => 'crontab_account.log',
],
// 内容采集任务,将外部或设备内容同步到系统内容库
'content_collect' => [
'command' => 'content:collect',
'schedule' => '*/5 * * * *', // 每5分钟
'options' => [],
'enabled' => true,
'max_concurrent' => 1,
'log_file' => 'crontab_contentCollect.log',
],
// 工作台:自动点赞好友/客户朋友圈,提高账号活跃度
'workbench_auto_like' => [
'command' => 'workbench:autoLike',
'schedule' => '*/6 * * * *', // 每6分钟
'options' => [],
'enabled' => true,
'max_concurrent' => 1,
'log_file' => 'crontab_workbench_autoLike.log',
],
// 工作台:自动建群任务,按规则批量创建微信群
'workbench_group_create' => [
'command' => 'workbench:groupCreate',
'schedule' => '*/5 * * * *', // 每5分钟
'options' => [],
'enabled' => true,
'max_concurrent' => 1,
'log_file' => 'workbench_groupCreate.log',
],
// 工作台:自动导入通讯录到系统,生成加粉/建群等任务
'workbench_import_contact' => [
'command' => 'workbench:import-contact',
'schedule' => '*/5 * * * *', // 每5分钟
'options' => [],
'enabled' => true,
'max_concurrent' => 1,
'log_file' => 'import_contact.log',
],
// ===========================
// 低频任务(每 2 分钟)
// ===========================
// 清洗并同步微信原始数据到存客宝业务表(数据治理任务)
'sync_wechat_data' => [
'command' => 'sync:wechatData',
'schedule' => '*/2 * * * *', // 每2分钟
'options' => [],
'enabled' => true,
'max_concurrent' => 1,
'log_file' => 'sync_wechat_data.log',
],
// 工作台:流量分发任务,把流量池中的线索按规则分配给微信号或员工
'workbench_traffic_distribute' => [
'command' => 'workbench:trafficDistribute',
'schedule' => '*/2 * * * *', // 每2分钟
'options' => [],
'enabled' => true,
'max_concurrent' => 1,
'log_file' => 'traffic_distribute.log',
],
// 工作台:朋友圈同步任务,拉取并落库朋友圈内容
'workbench_moments' => [
'command' => 'workbench:moments',
'schedule' => '*/2 * * * *', // 每2分钟
'options' => [],
'enabled' => true,
'max_concurrent' => 1,
'log_file' => 'workbench_moments.log',
],
// 预防性切换好友任务,监控频繁/风控风险,自动切换加人对象,保护微信号
'switch_friends' => [
'command' => 'switch:friends',
'schedule' => '*/2 * * * *', // 每2分钟
'options' => [],
'enabled' => true,
'max_concurrent' => 1,
'log_file' => 'switch_friends.log',
],
// ===========================
// 低频任务(每 30 分钟)
// ===========================
// 拉取设备通话记录(语音/电话),用于质检、统计或标签打分
'call_recording' => [
'command' => 'call-recording:list',
'schedule' => '*/30 * * * *', // 每30分钟
'options' => [],
'enabled' => true,
'max_concurrent' => 1,
'log_file' => 'call_recording.log',
],
// ===========================
// 每日 / 每几天任务
// ===========================
// 每日 1:00 同步“已删除设备”列表,补齐历史状态
'device_deleted' => [
'command' => 'device:list',
'schedule' => '0 1 * * *', // 每天1点
'options' => ['--isDel=1'],
'enabled' => true,
'max_concurrent' => 1,
'log_file' => 'crontab_device_deleted.log',
],
// 每日 1:10 同步“已停用设备”列表,更新停用状态
'device_stopped' => [
'command' => 'device:list',
'schedule' => '10 1 * * *', // 每天1:10
'options' => ['--isDel=2'],
'enabled' => true,
'max_concurrent' => 1,
'log_file' => 'crontab_device_stopped.log',
],
// 每日 1:30 同步“已删除微信好友”,用于历史恢复与报表
'wechat_friends_deleted' => [
'command' => 'wechatFriends:list',
'schedule' => '30 1 * * *', // 每天1:30
'options' => ['--isDel=1'],
'enabled' => true,
'max_concurrent' => 1,
'log_file' => 'crontab_wechatFriends_deleted.log',
],
// 每日 1:30 同步“已删除微信群聊”,用于统计与留痕
'wechat_chatroom_deleted' => [
'command' => 'wechatChatroom:list',
'schedule' => '30 1 * * *', // 每天1:30
'options' => ['--isDel=1'],
'enabled' => true,
'max_concurrent' => 1,
'log_file' => 'crontab_wechatChatroom_deleted.log',
],
// 每日 2:00 统一计算所有微信账号健康分(基础分 + 动态分)
'wechat_calculate_score' => [
'command' => 'wechat:calculate-score',
'schedule' => '0 2 * * *', // 每天2点
'options' => [],
'enabled' => true,
'max_concurrent' => 1,
'log_file' => 'calculate_score.log',
],
// 每 3 天执行的全量任务
// 每 3 天 3:00 全量同步所有在线好友,做一次大规模校准
'sync_all_friends' => [
'command' => 'sync:allFriends',
'schedule' => '0 3 */3 * *', // 每3天的3点
'options' => [],
'enabled' => true,
'max_concurrent' => 1,
'log_file' => 'all_friends.log',
],
// 已禁用的任务(注释掉的任务)
// 'workbench_group_push' => [
// 'command' => 'workbench:groupPush',
// 'schedule' => '*/2 * * * *',
// 'options' => [],
// 'enabled' => false,
// 'log_file' => 'workbench_groupPush.log',
// ],
];