feat(service): huỷ dịch vụ, SSL, đổi mật khẩu hosting, lịch sử thanh toán theo hình thức

- Lịch sử thanh toán hoá đơn trả thêm `method` (tự động gia hạn / số dư / admin / chuyển khoản / hoàn tiền), bỏ đuôi `(method)` khỏi ghi chú
- `DnsServer::panelUrl()` = https://<domain>/ dùng cho email cấp hosting và nút Plesk của khách
- Order item trả thêm `uuid`/`type` của dịch vụ để FE dẫn tới trang quản lý
- Yêu cầu huỷ dịch vụ, chứng chỉ SSL, lịch sử gia hạn, đổi mật khẩu hosting cho khách

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
baodev0
2026-10-28 16:40:24 +07:00
co-authored by Claude Sonnet 5.5
parent a6b641015e
commit b44a88bb52
72 changed files with 1701 additions and 231 deletions
@@ -0,0 +1,14 @@
<?php
use Database\Seeders\TemplateSeeder;
use Illuminate\Database\Migrations\Migration;
return new class extends Migration
{
public function up(): void
{
(new TemplateSeeder())->seedServiceLifecycleTemplates();
}
public function down(): void {}
};
@@ -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('service_attachments', function (Blueprint $table) {
$table->string('audience', 20)->default('admin')->after('service_id')->index();
});
}
public function down(): void
{
Schema::table('service_attachments', function (Blueprint $table) {
$table->dropColumn('audience');
});
}
};
@@ -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('service_ssl_certificates', function (Blueprint $table) {
$table->id();
$table->foreignId('service_id')->unique()->constrained('services')->cascadeOnDelete();
$table->longText('data');
$table->timestamps();
});
}
public function down(): void
{
Schema::dropIfExists('service_ssl_certificates');
}
};
@@ -0,0 +1,31 @@
<?php
use Database\Seeders\TemplateSeeder;
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_cancellation_requests', function (Blueprint $table) {
$table->id();
$table->foreignId('service_id')->constrained('services')->cascadeOnDelete();
$table->foreignId('customer_id')->constrained('customers')->cascadeOnDelete();
$table->string('reason_code', 40)->nullable();
$table->text('reason');
$table->string('status', 20)->default('pending')->index();
$table->timestamps();
});
(new TemplateSeeder())->seedScenarioIfMissing('admin_service_cancellation_request');
}
public function down(): void
{
Schema::dropIfExists('service_cancellation_requests');
DB::table('template')->where('scenario', 'admin_service_cancellation_request')->delete();
}
};
@@ -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::table('service_cancellation_requests', function (Blueprint $table) {
$table->text('admin_note')->nullable()->after('reason');
$table->timestamp('resolved_at')->nullable()->after('status');
$table->string('resolved_by', 100)->nullable()->after('resolved_at');
});
}
public function down(): void
{
Schema::table('service_cancellation_requests', function (Blueprint $table) {
$table->dropColumn(['admin_note', 'resolved_at', 'resolved_by']);
});
}
};
@@ -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('services', function (Blueprint $table) {
$table->boolean('cancel_at_expiry')->default(false)->after('auto_renew')->index();
});
}
public function down(): void
{
Schema::table('services', function (Blueprint $table) {
$table->dropColumn('cancel_at_expiry');
});
}
};
@@ -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())->seedScenarioIfMissing('admin_customer_service_renewed');
}
public function down(): void
{
DB::table('template')->where('scenario', 'admin_customer_service_renewed')->delete();
}
};