Build Laravel 13 ticket assistant with Docker, Livewire admin, and helpdesk scraper command
This commit is contained in:
@@ -0,0 +1,16 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
|
||||
return new class extends Migration {
|
||||
public function up(): void
|
||||
{
|
||||
DB::statement('CREATE EXTENSION IF NOT EXISTS vector');
|
||||
}
|
||||
|
||||
public function down(): void
|
||||
{
|
||||
DB::statement('DROP EXTENSION IF EXISTS vector');
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,27 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration {
|
||||
public function up(): void
|
||||
{
|
||||
Schema::create('articles', function (Blueprint $table) {
|
||||
$table->id();
|
||||
$table->string('title');
|
||||
$table->text('content');
|
||||
$table->timestamps();
|
||||
});
|
||||
|
||||
$dimension = (int) config('services.embedding.dimension', 768);
|
||||
DB::statement("ALTER TABLE articles ADD COLUMN embedding vector({$dimension})");
|
||||
DB::statement('CREATE INDEX articles_embedding_cosine_idx ON articles USING ivfflat (embedding vector_cosine_ops)');
|
||||
}
|
||||
|
||||
public function down(): void
|
||||
{
|
||||
Schema::dropIfExists('articles');
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,26 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration {
|
||||
public function up(): void
|
||||
{
|
||||
Schema::create('tickets', function (Blueprint $table) {
|
||||
$table->id();
|
||||
$table->text('message');
|
||||
$table->timestamps();
|
||||
});
|
||||
|
||||
$dimension = (int) config('services.embedding.dimension', 768);
|
||||
DB::statement("ALTER TABLE tickets ADD COLUMN embedding vector({$dimension})");
|
||||
DB::statement('CREATE INDEX tickets_embedding_cosine_idx ON tickets USING ivfflat (embedding vector_cosine_ops)');
|
||||
}
|
||||
|
||||
public function down(): void
|
||||
{
|
||||
Schema::dropIfExists('tickets');
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,23 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration {
|
||||
public function up(): void
|
||||
{
|
||||
Schema::create('embedding_cache', function (Blueprint $table) {
|
||||
$table->id();
|
||||
$table->string('text_hash', 64)->unique();
|
||||
$table->longText('text');
|
||||
$table->json('embedding');
|
||||
$table->timestamps();
|
||||
});
|
||||
}
|
||||
|
||||
public function down(): void
|
||||
{
|
||||
Schema::dropIfExists('embedding_cache');
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,25 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration {
|
||||
public function up(): void
|
||||
{
|
||||
Schema::create('ai_decisions', function (Blueprint $table) {
|
||||
$table->id();
|
||||
$table->foreignId('ticket_id')->constrained()->cascadeOnDelete();
|
||||
$table->foreignId('article_id')->nullable()->constrained()->nullOnDelete();
|
||||
$table->float('confidence')->default(0);
|
||||
$table->text('explanation')->nullable();
|
||||
$table->json('raw_response')->nullable();
|
||||
$table->timestamps();
|
||||
});
|
||||
}
|
||||
|
||||
public function down(): void
|
||||
{
|
||||
Schema::dropIfExists('ai_decisions');
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,24 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration {
|
||||
public function up(): void
|
||||
{
|
||||
Schema::create('feedback', function (Blueprint $table) {
|
||||
$table->id();
|
||||
$table->foreignId('ticket_id')->constrained()->cascadeOnDelete();
|
||||
$table->foreignId('article_id')->nullable()->constrained()->nullOnDelete();
|
||||
$table->boolean('is_correct');
|
||||
$table->text('notes')->nullable();
|
||||
$table->timestamps();
|
||||
});
|
||||
}
|
||||
|
||||
public function down(): void
|
||||
{
|
||||
Schema::dropIfExists('feedback');
|
||||
}
|
||||
};
|
||||
24
database/seeders/ArticleSeeder.php
Normal file
24
database/seeders/ArticleSeeder.php
Normal file
@@ -0,0 +1,24 @@
|
||||
<?php
|
||||
|
||||
namespace Database\Seeders;
|
||||
|
||||
use App\Models\Article;
|
||||
use Illuminate\Database\Seeder;
|
||||
|
||||
class ArticleSeeder extends Seeder
|
||||
{
|
||||
public function run(): void
|
||||
{
|
||||
$articles = [
|
||||
['title' => 'Password Reset Instructions', 'content' => 'Use the Forgot Password link on login. A reset email arrives within 2 minutes. Check spam folder if not received.'],
|
||||
['title' => 'Refund Policy', 'content' => 'Refund requests are accepted within 14 days for annual plans and 7 days for monthly plans.'],
|
||||
['title' => 'Two-Factor Authentication Setup', 'content' => 'Enable 2FA from Account Security. Scan the QR code in your authenticator app and confirm with OTP.'],
|
||||
['title' => 'Subscription Upgrade Guide', 'content' => 'Go to Billing, click Change Plan, choose Pro or Enterprise, and confirm immediate prorated billing.'],
|
||||
['title' => 'Webhook Delivery Troubleshooting', 'content' => 'Verify endpoint HTTPS, 2xx response, and signature validation. Retry logs are available in Developer Settings.'],
|
||||
];
|
||||
|
||||
foreach ($articles as $article) {
|
||||
Article::query()->create($article);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2,24 +2,14 @@
|
||||
|
||||
namespace Database\Seeders;
|
||||
|
||||
use App\Models\User;
|
||||
use Illuminate\Database\Console\Seeds\WithoutModelEvents;
|
||||
use Illuminate\Database\Seeder;
|
||||
|
||||
class DatabaseSeeder extends Seeder
|
||||
{
|
||||
use WithoutModelEvents;
|
||||
|
||||
/**
|
||||
* Seed the application's database.
|
||||
*/
|
||||
public function run(): void
|
||||
{
|
||||
// User::factory(10)->create();
|
||||
|
||||
User::factory()->create([
|
||||
'name' => 'Test User',
|
||||
'email' => 'test@example.com',
|
||||
$this->call([
|
||||
ArticleSeeder::class,
|
||||
]);
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user