1
0
Files
services_core/app/Http/Controllers/Customer/CustomerController.php
T
2026-08-07 09:16:13 +07:00

214 lines
9.1 KiB
PHP

<?php
namespace App\Http\Controllers\Customer;
use App\Http\Controllers\ApiController;
use App\Http\Requests\Customer\ActivateCustomerRequest;
use App\Http\Requests\Customer\IndexCustomerRequest;
use App\Http\Requests\Customer\StoreCustomerRequest;
use App\Http\Requests\Customer\UpdateCustomerRequest;
use App\Http\Resources\Customer\CustomerResource;
use App\Services\Customer\CustomerService;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Http;
use Illuminate\Validation\ValidationException;
class CustomerController extends ApiController
{
public function __construct(protected CustomerService $service) {}
public function index(IndexCustomerRequest $request)
{
return $this->successPaginated(
$this->service->list($this->scope($request), $request->validated(), $request->user(), $this->tenantId($request)),
CustomerResource::class,
'Daftar customer berhasil diambil'
);
}
public function options(Request $request)
{
return $this->success(
$this->service->options($request->user(), $this->tenantId($request)),
'Pilihan relasi customer berhasil diambil'
);
}
public function odpPorts(Request $request, int $node)
{
$data=$request->validate(['customer_id'=>['nullable','integer','exists:customers,id']]);
return $this->success($this->service->availableOdpPorts($node,$request->user(),$this->tenantId($request),$data['customer_id'] ?? null),'Port ODP tersedia berhasil diambil');
}
public function ocrKtp(Request $request)
{
$data = $request->validate([
'image' => ['required', 'image', 'mimes:jpeg,jpg,png,webp', 'max:5120'],
'tenant_id' => ['nullable', 'integer', 'exists:tenants,id'],
]);
$tenantId = $request->user()->isMasterAdmin()
? ($data['tenant_id'] ?? $request->attributes->get('tenant_id'))
: $request->attributes->get('tenant_id');
abort_unless($tenantId, 422, 'Tenant wajib dipilih sebelum menjalankan OCR.');
if (! $request->user()->isMasterAdmin()) {
abort_unless($request->user()->userTenants()->where('tenant_id', $tenantId)->exists(), 403);
}
$setting = \App\Models\TenantInstallationSetting::where('tenant_id', $tenantId)->first();
abort_unless($setting?->ktp_ocr_enabled, 422, 'OCR KTP belum diaktifkan untuk tenant ini.');
abort_unless($setting->ocr_provider && $setting->ocr_api_key, 422, 'Provider atau API key OCR belum dikonfigurasi.');
$file = $data['image'];
$mime = $file->getMimeType();
$base64 = base64_encode(file_get_contents($file->getRealPath()));
$prompt = 'Baca KTP Indonesia ini. Kembalikan hanya JSON valid tanpa markdown dengan field: nik, nama, alamat, rt_rw, kelurahan_desa, kecamatan, kabupaten_kota, provinsi. Gunakan null bila tidak terbaca.';
if ($setting->ocr_provider === 'openai') {
$response = Http::withToken($setting->ocr_api_key)->timeout(60)->post('https://api.openai.com/v1/chat/completions', [
'model' => 'gpt-4.1-mini',
'response_format' => ['type' => 'json_object'],
'messages' => [['role' => 'user', 'content' => [
['type' => 'text', 'text' => $prompt],
['type' => 'image_url', 'image_url' => ['url' => "data:{$mime};base64,{$base64}"]],
]]],
]);
$content = $response->successful() ? $response->json('choices.0.message.content') : null;
} else {
$response = Http::timeout(60)->post('https://generativelanguage.googleapis.com/v1beta/models/gemini-2.5-flash:generateContent?key='.urlencode($setting->ocr_api_key), [
'contents' => [['parts' => [['text' => $prompt], ['inline_data' => ['mime_type' => $mime, 'data' => $base64]]]]],
'generationConfig' => ['responseMimeType' => 'application/json'],
]);
$content = $response->successful() ? $response->json('candidates.0.content.parts.0.text') : null;
}
if (! $response->successful() || ! $content) {
throw ValidationException::withMessages(['image' => 'Provider AI gagal membaca KTP. Periksa API key, kuota, dan kualitas gambar.']);
}
$result = json_decode(trim(str_replace(['```json', '```'], '', $content)), true);
if (! is_array($result)) throw ValidationException::withMessages(['image' => 'Respons OCR tidak dapat diproses.']);
return $this->success($result, 'KTP berhasil dibaca');
}
public function store(StoreCustomerRequest $request)
{
return $this->created(
new CustomerResource($this->service->create($request->validated(), $request->user(), $this->tenantId($request))),
'Customer berhasil didaftarkan'
);
}
public function show(Request $request, int $id)
{
$customer = $this->service->find($id, $this->scope($request) === 'trash');
$this->authorizeTenant($request, $customer->tenant_id);
return $this->success(new CustomerResource($customer), 'Detail customer berhasil diambil');
}
public function update(UpdateCustomerRequest $request, int $id)
{
$customer = $this->service->find($id);
$this->authorizeTenant($request, $customer->tenant_id);
return $this->updated(
new CustomerResource($this->service->update($customer, $request->validated(), $request->user())),
'Data customer berhasil diperbarui'
);
}
public function prepareActivation(UpdateCustomerRequest $request, int $id)
{
$customer = $this->service->find($id);
$this->authorizeTenant($request, $customer->tenant_id);
$mode = \App\Models\TenantInstallationSetting::firstOrCreate(
['tenant_id' => $customer->tenant_id],
['activation_mode' => 'list_order']
)->activation_mode;
abort_unless($mode === 'list_order', 422, 'Tenant mengatur persiapan aktivasi melalui Ticket.');
$customer = $this->service->update($customer, $request->validated(), $request->user());
$customer = $this->service->ensureActivationCode($customer);
return $this->updated(
new CustomerResource($customer),
'Persiapan aktivasi berhasil disimpan'
);
}
public function updateRegistration(UpdateCustomerRequest $request, int $id)
{
$customer = $this->service->find($id);
$this->authorizeTenant($request, $customer->tenant_id);
return $this->updated(
new CustomerResource($this->service->update($customer, $request->validated(), $request->user())),
'Data registrasi berhasil disimpan'
);
}
public function activate(ActivateCustomerRequest $request, int $id)
{
$customer = $this->service->find($id);
$this->authorizeTenant($request, $customer->tenant_id);
return $this->updated(new CustomerResource($this->service->activate($customer, $request->validated(), $request->user())), 'Customer berhasil diaktifkan');
}
public function deactivate(Request $request, int $id)
{
$customer = $this->service->find($id);
$this->authorizeTenant($request, $customer->tenant_id);
return $this->updated(new CustomerResource($this->service->deactivate($customer)), 'Customer berhasil dinonaktifkan');
}
public function destroy(Request $request, int $id)
{
$customer = $this->service->find($id);
$this->authorizeTenant($request, $customer->tenant_id);
$this->service->trash($customer);
return $this->deleted('Customer dipindahkan ke Sampah');
}
public function cancelOrder(Request $request, int $id)
{
$customer = $this->service->find($id);
$this->authorizeTenant($request, $customer->tenant_id);
abort_unless($customer->status === 'order', 422, 'Hanya customer dalam List Order yang dapat dihapus melalui aksi ini.');
$this->service->trash($customer);
return $this->deleted('List Order dipindahkan ke Sampah');
}
public function restore(Request $request, int $id)
{
$customer = $this->service->find($id, true);
$this->authorizeTenant($request, $customer->tenant_id);
return $this->updated(new CustomerResource($this->service->restore($customer)), 'Customer berhasil dipulihkan');
}
public function forceDelete(Request $request, int $id)
{
$customer = $this->service->find($id, true);
$this->authorizeTenant($request, $customer->tenant_id);
$this->service->forceDelete($customer);
return $this->deleted('Customer dihapus permanen');
}
private function scope(Request $request): string
{
return $request->route()->defaults['customer_scope'] ?? 'orders';
}
private function tenantId(Request $request): ?int
{
return $request->attributes->get('tenant_id');
}
private function authorizeTenant(Request $request, int $tenantId): void
{
abort_unless($request->user()->isMasterAdmin() || $tenantId === $this->tenantId($request), 403);
}
}