a148e0f99e
Merges CrmAssistant + OfficeAssistant into a single module with: - Floating chat (FAB) with auto office/case mode detection - CaseMemory entity for persistent per-case knowledge - save_memory AI tool for proactive memory saving - Auto-extraction hooks (status changes, hearings) - AlertCalculator for office-level alerts - Full conversation persistence via ConversationRepository - RTL/Hebrew support throughout (fa_IR) 47 files, 0 dependencies on old modules.
106 lines
3.3 KiB
PHP
106 lines
3.3 KiB
PHP
<?php
|
|
|
|
namespace Espo\Modules\SmartAssistant\Services;
|
|
|
|
use Espo\ORM\EntityManager;
|
|
use Espo\ORM\Entity;
|
|
use Espo\Core\Utils\Log;
|
|
|
|
class CaseMemoryService
|
|
{
|
|
private EntityManager $entityManager;
|
|
private Log $log;
|
|
|
|
public function __construct(EntityManager $entityManager, Log $log)
|
|
{
|
|
$this->entityManager = $entityManager;
|
|
$this->log = $log;
|
|
}
|
|
|
|
public function createMemory(
|
|
string $caseId,
|
|
string $category,
|
|
string $name,
|
|
string $content,
|
|
string $importance = 'normal',
|
|
string $source = 'manual',
|
|
?string $sourceEntityType = null,
|
|
?string $sourceEntityId = null
|
|
): Entity {
|
|
$entry = $this->entityManager->getNewEntity('CaseMemory');
|
|
$entry->set([
|
|
'name' => $name,
|
|
'caseId' => $caseId,
|
|
'category' => $category,
|
|
'content' => $content,
|
|
'importance' => $importance,
|
|
'source' => $source,
|
|
'sourceEntityType' => $sourceEntityType,
|
|
'sourceEntityId' => $sourceEntityId,
|
|
]);
|
|
$this->entityManager->saveEntity($entry);
|
|
|
|
$this->log->info("SmartAssistant: Created CaseMemory id={$entry->get('id')} for case={$caseId} category={$category}");
|
|
|
|
return $entry;
|
|
}
|
|
|
|
public function getMemoriesByCase(string $caseId, ?string $category = null, bool $includeArchived = false): array
|
|
{
|
|
$where = ['caseId' => $caseId, 'deleted' => false];
|
|
|
|
if ($category) {
|
|
$where['category'] = $category;
|
|
}
|
|
|
|
if (!$includeArchived) {
|
|
$where['isArchived'] = false;
|
|
}
|
|
|
|
$entries = $this->entityManager->getRDBRepository('CaseMemory')
|
|
->where($where)
|
|
->order([['isPinned', 'DESC'], ['createdAt', 'DESC']])
|
|
->find();
|
|
|
|
$grouped = [];
|
|
foreach ($entries as $entry) {
|
|
$cat = $entry->get('category');
|
|
if (!isset($grouped[$cat])) $grouped[$cat] = [];
|
|
$grouped[$cat][] = [
|
|
'id' => $entry->get('id'),
|
|
'name' => $entry->get('name'),
|
|
'content' => $entry->get('content'),
|
|
'category' => $cat,
|
|
'importance' => $entry->get('importance'),
|
|
'source' => $entry->get('source'),
|
|
'isPinned' => $entry->get('isPinned'),
|
|
'isArchived' => $entry->get('isArchived'),
|
|
'createdAt' => $entry->get('createdAt'),
|
|
'modifiedAt' => $entry->get('modifiedAt'),
|
|
];
|
|
}
|
|
|
|
return $grouped;
|
|
}
|
|
|
|
public function getPinnedMemories(string $caseId): array
|
|
{
|
|
$entries = $this->entityManager->getRDBRepository('CaseMemory')
|
|
->where(['caseId' => $caseId, 'isPinned' => true, 'isArchived' => false, 'deleted' => false])
|
|
->order('createdAt', 'DESC')
|
|
->find();
|
|
|
|
$result = [];
|
|
foreach ($entries as $entry) {
|
|
$result[] = [
|
|
'id' => $entry->get('id'),
|
|
'name' => $entry->get('name'),
|
|
'content' => $entry->get('content'),
|
|
'category' => $entry->get('category'),
|
|
'importance' => $entry->get('importance'),
|
|
];
|
|
}
|
|
return $result;
|
|
}
|
|
}
|