feat: nhắc gia hạn QR theo dịch vụ, failover server hosting, CCCD admin, log cấp phát dịch vụ

- Mail campaign: mẫu nhắc gia hạn thanh toán QR, chọn dịch vụ + chu kỳ từng dòng (renewal_services)
- Hosting: nhiều server Plesk cho một gói (service_plan_servers), chuyển server khi server chết
- Admin xem/sửa giấy tờ định danh khách, tuỳ chọn gia hạn cho khách, nhật ký cấp phát dịch vụ
- Đánh dấu mẫu email dịch vụ là non-marketing

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
baodev0
2026-10-30 18:36:41 +07:00
co-authored by Claude Sonnet 5.5
parent 047c23beff
commit 55a834a2f8
53 changed files with 1463 additions and 119 deletions
@@ -0,0 +1,38 @@
<?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('service_plan_servers', function (Blueprint $table) {
$table->id();
$table->foreignId('service_plan_id')->constrained('service_plans')->cascadeOnDelete();
$table->foreignId('dns_manager_server_id')->constrained('dns_manager_servers')->cascadeOnDelete();
$table->unsignedSmallInteger('priority')->default(0);
$table->timestamps();
$table->unique(['service_plan_id', 'dns_manager_server_id'], 'plan_server_unique');
});
DB::table('service_plans')
->whereNotNull('dns_manager_server_id')
->get(['id', 'dns_manager_server_id'])
->each(fn ($plan) => DB::table('service_plan_servers')->insert([
'service_plan_id' => $plan->id,
'dns_manager_server_id' => $plan->dns_manager_server_id,
'priority' => 0,
'created_at' => now(),
'updated_at' => now(),
]));
}
public function down(): void
{
Schema::dropIfExists('service_plan_servers');
}
};
@@ -0,0 +1,18 @@
<?php
use Database\Seeders\TemplateSeeder;
use Illuminate\Database\Migrations\Migration;
use Illuminate\Support\Facades\DB;
return new class extends Migration
{
public function up(): void
{
(new TemplateSeeder())->seedMarketingTemplates();
}
public function down(): void
{
DB::table('template')->where('name', 'Nhắc gia hạn dịch vụ - Thanh toán QR')->delete();
}
};
@@ -0,0 +1,22 @@
<?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::table('mail_campaigns', function (Blueprint $table) {
$table->json('renewal_services')->nullable()->after('attachments');
});
}
public function down(): void
{
Schema::table('mail_campaigns', function (Blueprint $table) {
$table->dropColumn('renewal_services');
});
}
};
@@ -0,0 +1,22 @@
<?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::table('mail', function (Blueprint $table) {
$table->unsignedBigInteger('service_id')->nullable()->after('order_id');
});
}
public function down(): void
{
Schema::table('mail', function (Blueprint $table) {
$table->dropColumn('service_id');
});
}
};
@@ -0,0 +1,16 @@
<?php
use Database\Seeders\TemplateSeeder;
use Illuminate\Database\Migrations\Migration;
return new class extends Migration
{
public function up(): void
{
(new TemplateSeeder())->markNonMarketing();
}
public function down(): void
{
}
};