Files
portal-api/routes/api/admin/system.php
T
baodev0andClaude Opus 5.5 bf78f3b810 feat: hồ sơ khách (ngày sinh, tỉnh/phường theo mã), gộp menu + nút tắt, thông báo theo uuid
- PUT v1/client/update: gender theo enum ClientGender (0 Nam/1 Nữ), thêm
  birthday (cột mới), province_code/ward_code validate theo bảng provinces/wards
  (phường phải thuộc tỉnh). Migration nạp dữ liệu hành chính 2025 từ
  province_ward_import.sql (chỉ bảng trống), API GET v1/provinces[/{code}/wards].
- CustomerObserver/OrderObserver log giá trị cũ bằng getRawOriginal: getOriginal
  trả enum object, nối chuỗi gây 500 khi khách đổi giới tính.
- ClientResource: các cờ trả boolean, CCCD chỉ trả cccd_updated (không lộ ảnh/số).
- GET v1/setting/menu trả cả items + quick_links, bỏ v1/setting/quick-links.
- Thông báo admin/khách tra theo uuid thay vì id tự tăng.
- Chặn gửi template marketing cho khách đã tắt nhận email marketing.
- Xoá ProvinceWardSeeder và Customer::$updatable không dùng.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-13 16:03:18 +07:00

169 lines
6.4 KiB
PHP

<?php
use App\Http\Controllers\Api\Admin\ActivityLog\ActivityLogController;
use App\Http\Controllers\Api\Admin\EmailLog\EmailLogController;
use App\Http\Controllers\Api\Admin\EmailTemplate\EmailTemplateController;
use App\Http\Controllers\Api\Admin\MailCampaign\MailCampaignController;
use App\Http\Controllers\Api\Admin\Setting\ClientMenuController;
use App\Http\Controllers\Api\Admin\Setting\PopupBannerController;
use App\Http\Controllers\Api\Admin\Setting\SettingController as AdminSettingController;
use App\Http\Controllers\Api\Admin\Staff\StaffOnlineController;
use App\Http\Controllers\Api\Admin\Ticket\TicketController as AdminTicketController;
use App\Http\Controllers\Api\NotificationController;
use App\Http\Controllers\Api\QueueMonitorController;
use Illuminate\Support\Facades\Route;
// Settings (module: administrator)
Route::prefix('settings')
->controller(AdminSettingController::class)
->middleware('perm:setting.manager')
->group(function () {
Route::get('/', 'index');
Route::put('/', 'update');
Route::post('test-telegram', 'testTelegram');
});
Route::prefix('client-menu')
->controller(ClientMenuController::class)
->middleware('perm:setting.manager')
->group(function () {
Route::get('/', 'show');
Route::put('/', 'update');
Route::get('quick-links', 'showQuickLinks');
Route::put('quick-links', 'updateQuickLinks');
});
Route::prefix('popup-banners')
->controller(PopupBannerController::class)
->middleware('perm:setting.manager')
->group(function () {
Route::get('/', 'index');
Route::post('/', 'store');
Route::post('{id}', 'update')->whereNumber('id');
Route::delete('{id}', 'destroy')->whereNumber('id');
});
// VAT settings per service type (module: settings)
Route::prefix('vat-settings')
->controller(AdminSettingController::class)
->middleware('perm:setting.manager')
->group(function () {
Route::get('/', 'vatIndex');
Route::put('/', 'vatUpdate');
});
// Dashboard Stats
Route::prefix('dashboard')
->controller(\App\Http\Controllers\Api\Admin\Dashboard\DashboardController::class)
->middleware('perm:dashboard.view')
->group(function () {
Route::get('stats', 'stats');
Route::get('monthly-stats', 'monthlyStats');
Route::get('daily-stats', 'dailyStats');
Route::get('recent-orders', 'recentOrders');
Route::get('recent-deposits', 'recentDeposits');
Route::get('top-services', 'topServices');
Route::get('expiring-services', 'expiringServices');
Route::get('activities', 'activities');
Route::get('latest-customers', 'latestCustomers');
});
// Staff Online
Route::prefix('staff-online')
->controller(StaffOnlineController::class)
->group(function () {
Route::get('/', 'index');
Route::post('ping', 'ping');
});
// Queue Monitor (Horizon)
Route::prefix('queue')
->controller(QueueMonitorController::class)
->middleware('perm:queue.manager')
->group(function () {
Route::get('jobs/recent', 'recent');
Route::get('jobs/pending', 'pending');
Route::get('jobs/failed', 'failed');
Route::post('jobs/failed/{id}/retry', 'retry');
Route::get('jobs/metrics', 'metrics');
});
// Email Templates (module: settings)
Route::prefix('email-templates')
->controller(EmailTemplateController::class)
->middleware('perm:email-template.manager')
->group(function () {
Route::get('/', 'index');
Route::post('/', 'store');
Route::get('scenarios', 'scenarios');
Route::put('scenarios/{key}/label', 'updateScenarioLabel');
Route::get('email-settings', 'getEmailSettings');
Route::put('email-settings', 'updateEmailSettings');
Route::get('{id}', 'show');
Route::put('{id}', 'update');
Route::delete('{id}', 'destroy');
Route::post('{id}/preview', 'preview');
Route::post('{id}/send-test', 'sendTest');
});
// Tickets (support tickets)
Route::prefix('tickets')
->controller(AdminTicketController::class)
->group(function () {
Route::get('/', 'index')->middleware('perm:support.view');
Route::get('{ticket_num}', 'show')->middleware('perm:support.view');
Route::patch('{ticket_num}/status', 'updateStatus')->middleware('perm:support.edit');
Route::post('{ticket_num}/reply', 'reply')->middleware('perm:support.edit');
});
Route::prefix('notifications')
->controller(NotificationController::class)
->group(function () {
Route::get('/', fn() => app(NotificationController::class)->index(request(), 'admin'));
Route::get(
'{uuid}',
fn($uuid) => app(NotificationController::class)->show(request(), 'admin', $uuid),
)->whereUuid('uuid');
Route::patch(
'{uuid}/read',
fn($uuid) => app(NotificationController::class)->markRead(request(), 'admin', $uuid),
)->whereUuid('uuid');
Route::patch(
'read-all',
fn() => app(NotificationController::class)->markAllRead(request(), 'admin'),
);
});
// Activity Logs
Route::prefix('activity-logs')
->controller(ActivityLogController::class)
->middleware('perm:system-log.manager')
->group(function () {
Route::get('/', 'index');
Route::get('modules', 'modules');
Route::get('stats', 'stats');
Route::get('{id}', 'show');
});
// Email Logs
Route::prefix('email-logs')
->controller(EmailLogController::class)
->middleware('perm:system-log.manager')
->group(function () {
Route::get('/', 'index');
Route::get('{id}', 'show')->whereNumber('id');
});
// Mail Campaigns (module: mail-campaign)
Route::prefix('mail-campaigns')
->controller(MailCampaignController::class)
->group(function () {
Route::get('/', 'index')->middleware('perm:mail-campaign.view');
Route::get('variable-suggestions', 'variableSuggestions')->middleware('perm:mail-campaign.create');
Route::post('preview', 'preview')->middleware('perm:mail-campaign.create');
Route::get('{uuid}/preview', 'previewCampaign')->middleware('perm:mail-campaign.view');
Route::get('{uuid}/logs/{id}', 'showLog')->whereNumber('id')->middleware('perm:mail-campaign.view');
Route::get('{uuid}', 'show')->middleware('perm:mail-campaign.view');
Route::post('/', 'store')->middleware('perm:mail-campaign.create');
});