$data['uid'] ?? $uid, '$cpid' => $data['cpid'] ?? $cpid, 'task_type' => $type, 'title' => $data['title'] ?? '', 'ref_task_id' => $data['ref_task_id'] ?? 0, 'status' => $data['status'] ?? MpTaskCenter::STATUS_PROCESSING, 'result' => $data['result'] ?? null, 'error_message'=> $data['error_message'] ?? null, 'prompt' => $data['prompt'] ?? null, 'params' => $data['params'] ?? null, ]); } /** * 更新任务中心记录 * * @param int $taskId * @param array $data * @return bool */ public function updateTask(int $taskId, array $data = []): bool { return (bool)MpTaskCenter::where('id', $taskId)->update($data); } /** * 查询任务详情 * * @param int $taskId * @return MpTaskCenter|null */ public function getTaskDetail(int $taskId) { $query = MpTaskCenter::where('id', $taskId); // 请求上下文下按当前用户过滤 $uid = 0; try { $uid = (int)Site::getUid(); } catch (\Throwable $e) { } if ($uid > 0) { $query->where('uid', $uid); } return $query->first(); } /** * 分页查询任务列表 * * @param array $params * @return \Illuminate\Contracts\Pagination\LengthAwarePaginator */ public function getTaskList(array $params = []) { $query = MpTaskCenter::query(); // 请求上下文下按当前用户过滤 $uid = 0; try { $uid = (int)Site::getUid(); } catch (\Throwable $e) { } if ($uid > 0) { $query->where('uid', $uid); } // 按任务ID筛选 if (!empty($params['task_id'])) { $query->where('id', (int)$params['task_id']); } // 按任务状态筛选(支持逗号分隔多状态) if (!empty($params['status'])) { $statuses = is_array($params['status']) ? $params['status'] : array_filter(array_map('trim', explode(',', (string)$params['status']))); if (!empty($statuses)) { $query->whereIn('status', $statuses); } } // 按任务类型筛选 if (!empty($params['task_type'])) { $query->where('task_type', $params['task_type']); } $pageSize = (int)($params['page_size'] ?? 20); if ($pageSize <= 0 || $pageSize > 100) { $pageSize = 20; } return $query->orderBy('created_at', 'desc')->orderBy('id', 'desc')->paginate($pageSize); } /** * 定时同步任务中心状态和结果 * * 图片任务关联 mp_generate_pic_tasks,视频任务关联 mp_generate_video_tasks, * 将底层任务的最新状态、结果和错误信息同步到任务中心。 * * @return int 同步更新的记录数 */ public function syncTaskStatus(): int { $updated = 0; // 只同步尚未结束的任务(避免重复扫描已完成记录) $tasks = MpTaskCenter::whereIn('status', [ MpTaskCenter::STATUS_PENDING, MpTaskCenter::STATUS_PROCESSING, ]) ->where('ref_task_id', '>', 0) ->orderBy('id', 'desc') ->limit(500) ->get(); foreach ($tasks as $task) { try { if ($task->task_type === MpTaskCenter::TYPE_IMAGE) { $updated += $this->syncImageTask($task); } elseif ($task->task_type === MpTaskCenter::TYPE_VIDEO) { $updated += $this->syncVideoTask($task); } } catch (\Exception $e) { dLog('command')->error('任务中心同步失败: ' . $e->getMessage(), [ 'task_id' => $task->id, 'ref_task_id' => $task->ref_task_id, 'task_type' => $task->task_type, ]); } } return $updated; } /** * 同步图片任务状态到任务中心 * * @param MpTaskCenter $task * @return int */ private function syncImageTask(MpTaskCenter $task): int { $ref = DB::table('mp_generate_pic_tasks')->where('id', $task->ref_task_id)->first(); if (!$ref) { return 0; } $result = null; if (!empty($ref->result_url)) { $urls = $this->normalizeResultUrls($ref->result_url); $urls = array_values(array_filter($urls)); // 与文生图 completed 返回格式保持一致 $resultData = [ 'msg' => '', 'code' => 0, 'data' => $urls[0] ?? '', 'task_center_id' => $task->id, ]; if (count($urls) > 1) { $resultData['image_urls'] = $urls; } $result = json_encode($resultData, JSON_UNESCAPED_UNICODE); } return $this->applyRefStatus($task, $ref->status, $result, $ref->error_message); } /** * 规范化图片结果URL(兼容字符串/数组/JSON字符串/双重编码) * * @param mixed $resultUrl * @return array */ private function normalizeResultUrls($resultUrl): array { if (is_array($resultUrl)) { return array_values(array_filter($resultUrl)); } $value = (string)$resultUrl; // 最多解析两层 JSON(防御双重编码) for ($i = 0; $i < 2; $i++) { if (!is_string($value) || !is_json($value)) { break; } $decoded = json_decode($value, true); if (!is_array($decoded)) { // 解码结果是字符串且仍是JSON,继续解析下一层 if (is_string($decoded) && is_json($decoded)) { $value = $decoded; continue; } $value = $decoded; break; } $value = $decoded; } if (is_array($value)) { return array_values(array_filter($value)); } if (is_string($value) && $value !== '') { return [$value]; } return []; } /** * 同步视频任务状态到任务中心 * * @param MpTaskCenter $task * @return int */ private function syncVideoTask(MpTaskCenter $task): int { $ref = DB::table('mp_generate_video_tasks')->where('id', $task->ref_task_id)->first(); if (!$ref) { return 0; } $result = null; if (!empty($ref->result_url) || !empty($ref->compressed_url) || !empty($ref->last_frame_url)) { // 与文生视频 completed 返回格式保持一致 $result = json_encode([ 'task_id' => $ref->id, 'status' => $ref->status, 'video_url' => $ref->compressed_url ?: $ref->result_url, 'origin_video_url' => $ref->result_url, 'last_frame_url' => $ref->last_frame_url, 'error_message' => $ref->error_message ? mapErrorMessage($ref->error_message) : '', ], JSON_UNESCAPED_UNICODE); } return $this->applyRefStatus($task, $ref->status, $result, $ref->error_message); } /** * 将底层任务状态应用到任务中心记录 * * @param MpTaskCenter $task * @param string $refStatus * @param string|null $result * @param string|null $errorMessage * @return int */ private function applyRefStatus(MpTaskCenter $task, string $refStatus, $result, $errorMessage): int { $statusMap = [ 'pending' => MpTaskCenter::STATUS_PENDING, 'processing' => MpTaskCenter::STATUS_PROCESSING, 'success' => MpTaskCenter::STATUS_SUCCESS, 'failed' => MpTaskCenter::STATUS_FAILED, ]; $newStatus = $statusMap[$refStatus] ?? $task->status; $updateData = ['status' => $newStatus]; if ($result !== null) { $updateData['result'] = $result; } if ($errorMessage !== null) { $updateData['error_message'] = $errorMessage; } return $this->updateTask((int)$task->id, $updateData) ? 1 : 0; } }