Files
laravel_swoole/app/Services/System/TaskService.php
2026-02-18 19:41:03 +08:00

292 lines
8.9 KiB
PHP
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<?php
namespace App\Services\System;
use App\Models\System\Task;
use App\Services\System\NotificationService;
use Illuminate\Support\Facades\Validator;
use Illuminate\Support\Facades\Log;
class TaskService
{
/**
* @var NotificationService
*/
protected $notificationService;
/**
* TaskService constructor
*/
public function __construct(NotificationService $notificationService)
{
$this->notificationService = $notificationService;
}
public function getList(array $params): array
{
$query = Task::query();
if (!empty($params['keyword'])) {
$query->where(function ($q) use ($params) {
$q->where('name', 'like', '%' . $params['keyword'] . '%')
->orWhere('command', 'like', '%' . $params['keyword'] . '%');
});
}
if (isset($params['is_active']) && $params['is_active'] !== '') {
$query->where('is_active', $params['is_active']);
}
if (!empty($params['type'])) {
$query->where('type', $params['type']);
}
$query->orderBy('id');
$pageSize = $params['page_size'] ?? 20;
$list = $query->paginate($pageSize);
return [
'list' => $list->items(),
'total' => $list->total(),
'page' => $list->currentPage(),
'page_size' => $list->perPage(),
];
}
public function getAll(): array
{
return Task::all()->toArray();
}
public function getById(int $id): ?Task
{
return Task::find($id);
}
public function create(array $data): Task
{
Validator::make($data, [
'name' => 'required|string|max:100',
'command' => 'required|string|max:255',
'type' => 'required|string|in:command,job,closure',
'expression' => 'required|string',
])->validate();
return Task::create($data);
}
public function update(int $id, array $data): Task
{
$task = Task::findOrFail($id);
Validator::make($data, [
'name' => 'sometimes|required|string|max:100',
'command' => 'sometimes|required|string|max:255',
'type' => 'sometimes|required|string|in:command,job,closure',
'expression' => 'sometimes|required|string',
])->validate();
$task->update($data);
return $task;
}
public function delete(int $id): bool
{
$task = Task::findOrFail($id);
return $task->delete();
}
public function batchDelete(array $ids): bool
{
Task::whereIn('id', $ids)->delete();
return true;
}
public function batchUpdateStatus(array $ids, bool $status): bool
{
Task::whereIn('id', $ids)->update(['is_active' => $status]);
return true;
}
public function run(int $id): array
{
$task = Task::findOrFail($id);
$startTime = microtime(true);
$output = '';
$status = 'success';
$errorMessage = '';
try {
switch ($task->type) {
case 'command':
$command = $task->command;
if ($task->run_in_background) {
$command .= ' > /dev/null 2>&1 &';
}
exec($command, $output, $statusCode);
$output = implode("\n", $output);
if ($statusCode !== 0) {
throw new \Exception('Command failed with status code: ' . $statusCode);
}
break;
case 'job':
$jobClass = $task->command;
if (class_exists($jobClass)) {
dispatch(new $jobClass());
} else {
throw new \Exception('Job class not found: ' . $jobClass);
}
break;
case 'closure':
throw new \Exception('Closure type tasks cannot be run directly');
}
$task->increment('run_count');
} catch (\Exception $e) {
$status = 'error';
$errorMessage = $e->getMessage();
$task->increment('failed_count');
}
$executionTime = round((microtime(true) - $startTime) * 1000);
$task->update([
'last_run_at' => now(),
'last_output' => substr($output, 0, 10000),
]);
// 发送任务执行结果通知
$this->sendTaskNotification($task, $status, $errorMessage, $executionTime);
return [
'status' => $status,
'output' => $output,
'error_message' => $errorMessage,
'execution_time' => $executionTime,
];
}
public function getStatistics(): array
{
$total = Task::count();
$active = Task::where('is_active', true)->count();
$inactive = Task::where('is_active', false)->count();
return [
'total' => $total,
'active' => $active,
'inactive' => $inactive,
];
}
/**
* 发送任务执行结果通知
*
* @param Task $task
* @param string $status
* @param string|null $errorMessage
* @param int $executionTime
* @return void
*/
protected function sendTaskNotification(Task $task, string $status, ?string $errorMessage, int $executionTime): void
{
try {
// 只对失败的任务或重要的成功任务发送通知
// 这里可以根据实际需求调整通知策略
if ($status === 'error') {
// 任务失败通知
$title = '任务执行失败: ' . $task->name;
$content = sprintf(
"任务 %s 执行失败,错误信息:%s\n执行时间:%d 毫秒",
$task->name,
$errorMessage ?: '未知错误',
$executionTime
);
// 获取管理员用户ID列表
$adminUserIds = $this->getAdminUserIds();
if (!empty($adminUserIds)) {
$this->notificationService->sendToUsers(
$adminUserIds,
$title,
$content,
\App\Models\System\Notification::TYPE_ERROR,
\App\Models\System\Notification::CATEGORY_TASK,
[
'task_id' => $task->id,
'task_name' => $task->name,
'command' => $task->command,
'error_message' => $errorMessage,
'execution_time' => $executionTime,
'last_run_at' => $task->last_run_at?->toDateTimeString()
]
);
}
} elseif ($executionTime > 60000) {
// 执行时间超过1分钟的成功任务发送通知
$title = '任务执行完成: ' . $task->name;
$content = sprintf(
"任务 %s 执行成功,耗时:%.2f 秒",
$task->name,
$executionTime / 1000
);
$adminUserIds = $this->getAdminUserIds();
if (!empty($adminUserIds)) {
$this->notificationService->sendToUsers(
$adminUserIds,
$title,
$content,
\App\Models\System\Notification::TYPE_SUCCESS,
\App\Models\System\Notification::CATEGORY_TASK,
[
'task_id' => $task->id,
'task_name' => $task->name,
'execution_time' => $executionTime,
'last_run_at' => $task->last_run_at?->toDateTimeString()
]
);
}
}
} catch (\Exception $e) {
Log::error('发送任务通知失败', [
'task_id' => $task->id,
'error' => $e->getMessage()
]);
}
}
/**
* 获取管理员用户ID列表
*
* @return array
*/
protected function getAdminUserIds(): array
{
try {
// 获取拥有管理员权限的用户
// 这里可以根据实际业务逻辑调整,例如获取特定角色的用户
$adminUserIds = \App\Models\Auth\User::where('status', 1)
->whereHas('roles', function ($query) {
$query->where('name', 'admin');
})
->pluck('id')
->toArray();
return $adminUserIds;
} catch (\Exception $e) {
Log::error('获取管理员用户列表失败', [
'error' => $e->getMessage()
]);
return [];
}
}
}