erp-backend/app/Models/AIConversation.php
2026-04-01 17:07:04 +08:00

276 lines
7.2 KiB
PHP

<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
use Illuminate\Database\Eloquent\Relations\HasMany;
class AIConversation extends Model
{
use HasFactory;
protected $fillable = [
'user_id',
'title',
'model',
'message_count',
'total_tokens',
'last_message_at',
'status',
];
protected $casts = [
'message_count' => 'integer',
'total_tokens' => 'integer',
'last_message_at' => 'datetime',
];
/**
* 关联用户
*/
public function user(): BelongsTo
{
return $this->belongsTo(User::class);
}
/**
* 关联消息
*/
public function messages(): HasMany
{
return $this->hasMany(AIMessage::class);
}
/**
* 最后一条消息
*/
public function lastMessage()
{
return $this->hasOne(AIMessage::class)->latest();
}
/**
* 获取对话摘要
*/
public function getSummaryAttribute(): string
{
if ($this->title) {
return $this->title;
}
$firstMessage = $this->messages()->orderBy('created_at', 'asc')->first();
if ($firstMessage && $firstMessage->role === 'user') {
$content = $firstMessage->content;
return strlen($content) > 50 ? substr($content, 0, 50) . '...' : $content;
}
return 'AI对话';
}
/**
* 获取对话时长
*/
public function getDurationAttribute(): string
{
if (!$this->last_message_at) {
return '0分钟';
}
$duration = $this->last_message_at->diffInMinutes($this->created_at);
if ($duration < 60) {
return $duration . '分钟';
} elseif ($duration < 1440) {
return floor($duration / 60) . '小时';
} else {
return floor($duration / 1440) . '天';
}
}
/**
* 获取平均响应时间
*/
public function getAvgResponseTimeAttribute(): ?string
{
$aiMessages = $this->messages()->where('role', 'assistant')->get();
if ($aiMessages->isEmpty()) {
return null;
}
$totalSeconds = 0;
$count = 0;
foreach ($aiMessages as $message) {
$previousMessage = AIMessage::where('conversation_id', $this->id)
->where('created_at', '<', $message->created_at)
->orderBy('created_at', 'desc')
->first();
if ($previousMessage) {
$diff = $message->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) . '分钟';
}
}
/**
* 获取对话统计
*/
public function getStatisticsAttribute(): array
{
$userMessages = $this->messages()->where('role', 'user')->count();
$aiMessages = $this->messages()->where('role', 'assistant')->count();
$userTokens = $this->messages()->where('role', 'user')->sum('tokens');
$aiTokens = $this->messages()->where('role', 'assistant')->sum('tokens');
return [
'total_messages' => $userMessages + $aiMessages,
'user_messages' => $userMessages,
'ai_messages' => $aiMessages,
'total_tokens' => $userTokens + $aiTokens,
'user_tokens' => $userTokens,
'ai_tokens' => $aiTokens,
'avg_tokens_per_message' => $this->message_count > 0 ?
round(($userTokens + $aiTokens) / $this->message_count, 1) : 0,
];
}
/**
* 检查对话是否活跃
*/
public function getIsActiveAttribute(): bool
{
if (!$this->last_message_at) {
return false;
}
// 24小时内活跃
return $this->last_message_at->diffInHours(now()) < 24;
}
/**
* 获取对话标签
*/
public function getTagsAttribute(): array
{
$tags = [];
if ($this->is_active) {
$tags[] = ['label' => '活跃', 'color' => 'success'];
}
if ($this->message_count > 20) {
$tags[] = ['label' => '长篇', 'color' => 'warning'];
}
if ($this->total_tokens > 10000) {
$tags[] = ['label' => '深度', 'color' => 'primary'];
}
if ($this->status === 'archived') {
$tags[] = ['label' => '已归档', 'color' => 'secondary'];
}
return $tags;
}
/**
* 归档对话
*/
public function archive(): void
{
$this->update(['status' => 'archived']);
}
/**
* 恢复对话
*/
public function restore(): void
{
$this->update(['status' => 'active']);
}
/**
* 获取用户对话统计
*/
public static function getUserStatistics($userId)
{
$conversations = self::where('user_id', $userId)->get();
$totalConversations = $conversations->count();
$activeConversations = $conversations->where('is_active', true)->count();
$totalMessages = $conversations->sum('message_count');
$totalTokens = $conversations->sum('total_tokens');
$avgMessagesPerConversation = $totalConversations > 0 ?
round($totalMessages / $totalConversations, 1) : 0;
$avgTokensPerConversation = $totalConversations > 0 ?
round($totalTokens / $totalConversations, 1) : 0;
return [
'total_conversations' => $totalConversations,
'active_conversations' => $activeConversations,
'total_messages' => $totalMessages,
'total_tokens' => $totalTokens,
'avg_messages_per_conversation' => $avgMessagesPerConversation,
'avg_tokens_per_conversation' => $avgTokensPerConversation,
];
}
/**
* 清理过期对话
*/
public static function cleanupExpired($days = 30)
{
$expiredDate = now()->subDays($days);
$conversations = self::where('last_message_at', '<', $expiredDate)
->where('status', '!=', 'archived')
->get();
$deletedCount = 0;
$failedConversations = [];
foreach ($conversations as $conversation) {
try {
// 删除相关消息
$conversation->messages()->delete();
// 删除对话
$conversation->delete();
$deletedCount++;
} catch (\Exception $e) {
$failedConversations[] = [
'id' => $conversation->id,
'title' => $conversation->title,
'error' => $e->getMessage(),
];
}
}
return [
'deleted_count' => $deletedCount,
'failed_conversations' => $failedConversations,
];
}
}