Files
TicketAssistent/app/Services/AdminArticleService.php
your name 9244899f9b feat: Enhance Support Reply Service with tone instructions and article details
- Added tone instruction retrieval to SupportReplyService.
- Improved user feedback when no relevant article is found.
- Included article URL and tone instruction in LLM prompt.
- Updated response format to include source information.
- Enhanced article management UI with search functionality and editing capabilities.
- Introduced a new API endpoint for nearest articles based on vector search.
- Added confidence badge component to display article confidence levels.
- Implemented tests for article searching, editing, and nearest article API.
- Removed obsolete .htaccess file.
2026-05-13 22:25:45 +02:00

129 lines
3.9 KiB
PHP

<?php
namespace App\Services;
use App\Models\Article;
use App\Models\QuickReply;
use Illuminate\Contracts\Pagination\LengthAwarePaginator;
use Illuminate\Support\Facades\DB;
class AdminArticleService
{
public function paginate(int $perPage = 10, ?string $search = null): LengthAwarePaginator
{
$search = $search !== null ? trim($search) : '';
$numericSearch = ltrim($search, '#');
return Article::query()
->with('quickReplies')
->when($search !== '', function ($query) use ($numericSearch, $search): void {
$like = "%{$search}%";
$query->where(function ($query) use ($numericSearch, $like): void {
$query
->where('title', 'ilike', $like)
->orWhere('content', 'ilike', $like)
->orWhere('source_url', 'ilike', $like)
->orWhere('source_article_id', 'ilike', $like);
if (ctype_digit($numericSearch)) {
$query->orWhere('id', (int) $numericSearch);
}
});
})
->latest()
->paginate($perPage);
}
public function create(string $title, string $content, ?string $note = null, array $allowedActions = []): Article
{
return Article::query()->create([
'title' => trim($title),
'content' => trim($content),
'note' => $note !== null && trim($note) !== '' ? trim($note) : null,
'allowed_actions' => $this->sanitizeAllowedActions($allowedActions),
'status' => 'published',
'is_ai_draft' => false,
]);
}
public function deleteById(int $articleId): bool
{
return (bool) Article::query()->whereKey($articleId)->delete();
}
public function findById(int $articleId): ?Article
{
return Article::query()->find($articleId);
}
public function updateContent(int $articleId, string $title, string $content): bool
{
$article = Article::query()->find($articleId);
if ($article === null) {
return false;
}
$article->title = trim($title);
$article->content = trim($content);
$article->save();
return true;
}
public function updateMetadata(int $articleId, ?string $note, array $allowedActions, array $quickReplyIds = []): bool
{
$article = Article::query()->find($articleId);
if ($article === null) {
return false;
}
DB::transaction(function () use ($article, $note, $allowedActions, $quickReplyIds): void {
$article->note = $note !== null && trim($note) !== '' ? trim($note) : null;
$article->allowed_actions = $this->sanitizeAllowedActions($allowedActions);
$article->save();
$article->quickReplies()->sync($this->existingQuickReplyIds($quickReplyIds));
});
return true;
}
public function approveDraft(int $articleId): bool
{
$article = Article::query()->find($articleId);
if ($article === null) {
return false;
}
$article->status = 'published';
$article->is_ai_draft = false;
$article->save();
return true;
}
private function sanitizeAllowedActions(array $allowedActions): array
{
return array_values(array_intersect(array_unique($allowedActions), ['domain_inf']));
}
private function existingQuickReplyIds(array $quickReplyIds): array
{
$ids = array_values(array_unique(array_filter(
array_map(static fn ($id) => (int) $id, $quickReplyIds),
static fn ($id) => $id > 0
)));
if ($ids === []) {
return [];
}
return QuickReply::query()
->whereIn('id', $ids)
->pluck('id')
->map(static fn ($id) => (int) $id)
->all();
}
}