'integer', 'metadata' => 'array', 'created_at' => 'datetime', ]; /** * 关联对话 */ public function conversation(): BelongsTo { return $this->belongsTo(AIConversation::class); } /** * 获取消息摘要 */ public function getSummaryAttribute(): string { $content = $this->content; if (strlen($content) <= 100) { return $content; } return substr($content, 0, 100) . '...'; } /** * 获取消息类型 */ public function getTypeAttribute(): string { $types = [ 'system' => '系统', 'user' => '用户', 'assistant' => 'AI助手', ]; return $types[$this->role] ?? '未知'; } /** * 获取消息图标 */ public function getIconAttribute(): string { $icons = [ 'system' => 'settings', 'user' => 'person', 'assistant' => 'smart_toy', ]; return $icons[$this->role] ?? 'chat'; } /** * 获取消息颜色 */ public function getColorAttribute(): string { $colors = [ 'system' => 'secondary', 'user' => 'primary', 'assistant' => 'success', ]; return $colors[$this->role] ?? 'default'; } /** * 检查是否为AI消息 */ public function getIsAiAttribute(): bool { return $this->role === 'assistant'; } /** * 检查是否为用户消息 */ public function getIsUserAttribute(): bool { return $this->role === 'user'; } /** * 检查是否为系统消息 */ public function getIsSystemAttribute(): bool { return $this->role === 'system'; } /** * 获取消息时间格式 */ public function getTimeFormattedAttribute(): string { $now = now(); $messageTime = $this->created_at; if ($messageTime->isToday()) { return $messageTime->format('H:i'); } elseif ($messageTime->isYesterday()) { return '昨天 ' . $messageTime->format('H:i'); } elseif ($messageTime->diffInDays($now) < 7) { return $messageTime->format('m-d H:i'); } else { return $messageTime->format('Y-m-d H:i'); } } /** * 获取消息相对时间 */ public function getRelativeTimeAttribute(): string { $diff = $this->created_at->diff(now()); if ($diff->y > 0) { return $diff->y . '年前'; } elseif ($diff->m > 0) { return $diff->m . '个月前'; } elseif ($diff->d > 0) { return $diff->d . '天前'; } elseif ($diff->h > 0) { return $diff->h . '小时前'; } elseif ($diff->i > 0) { return $diff->i . '分钟前'; } else { return '刚刚'; } } /** * 获取消息内容格式(HTML安全) */ public function getContentFormattedAttribute(): string { $content = htmlspecialchars($this->content, ENT_QUOTES, 'UTF-8'); // 将换行转换为
$content = nl2br($content); // 将URL转换为链接 $content = preg_replace( '/(https?:\/\/[^\s]+)/', '$1', $content ); // 将代码块格式化 $content = preg_replace_callback( '/```(\w+)?\n([\s\S]*?)```/', function ($matches) { $language = $matches[1] ?? ''; $code = htmlspecialchars($matches[2], ENT_QUOTES, 'UTF-8'); return '
' . $code . '
'; }, $content ); // 将行内代码格式化 $content = preg_replace( '/`([^`]+)`/', '$1', $content ); return $content; } /** * 获取消息中的代码块 */ public function getCodeBlocksAttribute(): array { preg_match_all('/```(\w+)?\n([\s\S]*?)```/', $this->content, $matches, PREG_SET_ORDER); $codeBlocks = []; foreach ($matches as $match) { $codeBlocks[] = [ 'language' => $match[1] ?? '', 'code' => $match[2], ]; } return $codeBlocks; } /** * 获取消息中的链接 */ public function getLinksAttribute(): array { preg_match_all('/https?:\/\/[^\s]+/', $this->content, $matches); return $matches[0] ?? []; } /** * 获取消息中的关键词 */ public function getKeywordsAttribute(): array { // 简单关键词提取 $content = strtolower($this->content); $words = preg_split('/\s+/', $content); $wordCount = array_count_values($words); arsort($wordCount); // 过滤常见词 $commonWords = ['the', 'and', 'you', 'for', 'are', 'this', 'that', 'with', 'have', 'from']; $keywords = array_diff(array_keys($wordCount), $commonWords); return array_slice($keywords, 0, 10); } /** * 获取消息情感分析(简单版) */ public function getSentimentAttribute(): string { $positiveWords = ['好', '优秀', '成功', '满意', '高兴', '喜欢', '感谢', '完美', '棒', '赞']; $negativeWords = ['差', '失败', '不满意', '生气', '讨厌', '问题', '错误', '糟糕', '慢', '贵']; $content = $this->content; $positiveCount = 0; $negativeCount = 0; foreach ($positiveWords as $word) { $positiveCount += substr_count($content, $word); } foreach ($negativeWords as $word) { $negativeCount += substr_count($content, $word); } if ($positiveCount > $negativeCount) { return 'positive'; } elseif ($negativeCount > $positiveCount) { return 'negative'; } else { return 'neutral'; } } /** * 获取消息长度等级 */ public function getLengthLevelAttribute(): string { $length = strlen($this->content); if ($length < 50) { return 'short'; } elseif ($length < 200) { return 'medium'; } elseif ($length < 500) { return 'long'; } else { return 'very-long'; } } /** * 获取消息复杂度 */ public function getComplexityAttribute(): float { $content = $this->content; // 计算平均句子长度 $sentences = preg_split('/[。!?.!?]/', $content); $sentences = array_filter($sentences, function($sentence) { return strlen(trim($sentence)) > 0; }); if (empty($sentences)) { return 0; } $totalWords = 0; foreach ($sentences as $sentence) { $words = preg_split('/\s+/', $sentence); $totalWords += count($words); } $avgSentenceLength = $totalWords / count($sentences); // 计算生词比例(简单版) $commonWords = ['的', '了', '在', '是', '和', '与', '或', '等', '这个', '那个']; $allWords = preg_split('/\s+/', $content); $uniqueWords = array_unique($allWords); $uncommonWords = array_diff($uniqueWords, $commonWords); $uncommonRatio = count($uncommonWords) / max(1, count($uniqueWords)); // 综合复杂度 $complexity = ($avgSentenceLength * 0.6) + ($uncommonRatio * 40); return min(100, max(0, $complexity)); } /** * 获取对话中的消息统计 */ public static function getConversationStatistics($conversationId) { $messages = self::where('conversation_id', $conversationId)->get(); $totalMessages = $messages->count(); $userMessages = $messages->where('role', 'user')->count(); $aiMessages = $messages->where('role', 'assistant')->count(); $totalTokens = $messages->sum('tokens'); $userTokens = $messages->where('role', 'user')->sum('tokens'); $aiTokens = $messages->where('role', 'assistant')->sum('tokens'); $avgTokensPerMessage = $totalMessages > 0 ? round($totalTokens / $totalMessages, 1) : 0; $avgResponseTime = self::calculateAvgResponseTime($messages); return [ 'total_messages' => $totalMessages, 'user_messages' => $userMessages, 'ai_messages' => $aiMessages, 'message_ratio' => $userMessages > 0 ? round($aiMessages / $userMessages, 2) : 0, 'total_tokens' => $totalTokens, 'user_tokens' => $userTokens, 'ai_tokens' => $aiTokens, 'avg_tokens_per_message' => $avgTokensPerMessage, 'avg_response_time' => $avgResponseTime, ]; } /** * 计算平均响应时间 */ private static function calculateAvgResponseTime($messages) { $aiMessages = $messages->where('role', 'assistant'); if ($aiMessages->isEmpty()) { return null; } $totalSeconds = 0; $count = 0; foreach ($aiMessages as $aiMessage) { $previousMessage = $messages ->where('created_at', '<', $aiMessage->created_at) ->sortByDesc('created_at') ->first(); if ($previousMessage) { $diff = $aiMessage->created_at->diffInSeconds($previousMessage->created_at); $totalSeconds += $diff; $count++; } } if ($count === 0) { return null; } $avgSeconds = $totalSeconds / $count; if ($avgSeconds < 60) { return round($avgSeconds) . '秒'; } else { return round($avgSeconds / 60, 1) . '分钟'; } } }