Files
baodev0andClaude Opus 5.5 40bf6b54f9 feat(admin): star/tags marks, service product-type filter, campaign bulk actions, SMTP test
- Star (is_starred) and colored tags for services, customers, orders, invoices:
  Markable trait, tags/taggables tables, PATCH administrator/marks/{type}/{key},
  tag CRUD, list filters starred / tag_ids[]
- GET administrator/services accepts product_type (falls back to product type
  for legacy rows without services.type)
- Mail campaigns: bulk cancel / retry failed / delete; cancelled campaigns are
  skipped by SendMailCampaignRecipient
- POST administrator/settings/test-smtp sends a test email through the saved
  mail transport

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-15 21:53:41 +07:00

141 lines
5.4 KiB
PHP

<?php
namespace App\Http\Controllers\Api\Admin\Customer;
use App\Http\Controllers\Controller;
use App\Http\Requests\CreateCustomerRequest;
use App\Http\Resources\Api\Admin\CustomerResource;
use App\Models\Customer;
use App\Repositories\Customer\CustomerRepositoryInterface;
use App\Repositories\Module\Domain\DomainRepositoryInterface;
use App\Services\Customer\Concerns\ResolvesCustomer;
use App\Services\Customer\CustomerAccountService;
use App\Services\Customer\CustomerBillingService;
use App\Services\Customer\CustomerImpersonationService;
use App\Services\Customer\CustomerMailService;
use App\Services\Customer\CustomerNoteService;
use App\Services\Customer\CustomerServiceProvisioningService;
use App\Services\Customer\CustomerWalletService;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
use App\Http\Controllers\Api\Admin\Customer\Concerns\ManagesCustomerAccount;
use App\Http\Controllers\Api\Admin\Customer\Concerns\ManagesCustomerBilling;
use App\Http\Controllers\Api\Admin\Customer\Concerns\ManagesCustomerDomains;
use App\Http\Controllers\Api\Admin\Customer\Concerns\ManagesCustomerServices;
use App\Http\Controllers\Api\Admin\Customer\Concerns\ManagesCustomerBulkActions;
use App\Http\Controllers\Api\Admin\Customer\Concerns\ManagesCustomerNotes;
use App\Http\Controllers\Api\Admin\Customer\Concerns\ListsCustomerEmails;
class CustomerController extends Controller
{
use ResolvesCustomer;
use ManagesCustomerAccount;
use ManagesCustomerBilling;
use ManagesCustomerDomains;
use ManagesCustomerServices;
use ManagesCustomerBulkActions;
use ManagesCustomerNotes;
use ListsCustomerEmails;
public function __construct(
private readonly CustomerRepositoryInterface $customerRepository,
private readonly DomainRepositoryInterface $moduleDomainRepository,
private readonly CustomerAccountService $accountService,
private readonly CustomerWalletService $walletService,
private readonly CustomerMailService $mailService,
private readonly CustomerImpersonationService $impersonationService,
private readonly CustomerBillingService $billingService,
private readonly CustomerServiceProvisioningService $serviceProvisioning,
private readonly CustomerNoteService $noteService,
) {}
public function index(Request $request): JsonResponse
{
$perPage = min((int) $request->get('per_page', 15), 500);
$paginator = $this->customerRepository
->filter($request->all(), ['tags'], ['hosting', 'vps', 'domain'], ['*'], true)
->paginate($perPage);
return $this->paginatedResponse($paginator);
}
public function store(CreateCustomerRequest $request): JsonResponse
{
$customer = $this->accountService->create($request->validated());
return response()->json([
'error' => false,
'message' => 'Customer created successfully',
'data' => new CustomerResource($customer),
], 201);
}
public function show(string $uuid): JsonResponse
{
$customer = $this->resolveCustomer($uuid);
$customer = $this->customerRepository->findWithRelationships(
$customer->id,
['mail', 'file'],
[
'invoice' => [
'invoices_status_count_1' => ['key' => 'status', 'condition' => '=', 'value' => 1],
'invoices_status_count_0' => ['key' => 'status', 'condition' => '=', 'value' => 0],
],
'hosting' => 'hosting_count',
'vps' => 'vps_count',
'domain' => 'domain_count',
'ticket' => 'ticket_count',
],
[
'invoice' => [
'invoices_status_1' => ['key' => 'status', 'condition' => '=', 'value' => 1, 'sum' => 'total'],
'invoices_status_0' => ['key' => 'status', 'condition' => '=', 'value' => 0, 'sum' => 'total'],
],
],
);
abort_if(!$customer, 404, "Can't find customer by UUID #{$uuid}");
$customer->refresh();
return response()->json(['error' => false, 'data' => new CustomerResource($customer)]);
}
public function update(string $uuid, CreateCustomerRequest $request): JsonResponse
{
$data = $request->validated();
if (!empty($request->password)) {
$data['password_raw'] = $request->password;
}
$customer = $this->accountService->update($uuid, $data);
return response()->json([
'error' => false,
'message' => 'Customer updated successfully',
'data' => new CustomerResource($customer),
]);
}
public function destroy(string $uuid): JsonResponse
{
$this->accountService->destroy($uuid);
return response()->json(['error' => false, 'message' => 'Customer deleted successfully']);
}
private function paginatedResponse(\Illuminate\Contracts\Pagination\LengthAwarePaginator $paginator): JsonResponse
{
return response()->json([
'error' => false,
'data' => [
'items' => CustomerResource::collection($paginator),
'total' => $paginator->total(),
'per_page' => $paginator->perPage(),
'current_page' => $paginator->currentPage(),
'last_page' => $paginator->lastPage(),
],
]);
}
}