Files
sam-manage/app/Http/Controllers/Api/Admin/HR/BusinessIncomeEarnerController.php

390 lines
14 KiB
PHP
Raw Normal View History

<?php
namespace App\Http\Controllers\Api\Admin\HR;
use App\Http\Controllers\Controller;
use App\Models\Boards\File;
use App\Services\GoogleCloudStorageService;
use App\Services\HR\BusinessIncomeEarnerService;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
use Illuminate\Http\Response;
use Illuminate\Support\Facades\Storage;
use Illuminate\Support\Str;
class BusinessIncomeEarnerController extends Controller
{
public function __construct(
private BusinessIncomeEarnerService $service
) {}
/**
* 사업소득자 목록 조회 (HTMX HTML / 일반 JSON)
*/
public function index(Request $request): JsonResponse|Response
{
$earners = $this->service->getBusinessIncomeEarners(
$request->all(),
$request->integer('per_page', 20)
);
if ($request->header('HX-Request')) {
return response(view('hr.business-income-earners.partials.table', compact('earners')));
}
return response()->json([
'success' => true,
'data' => $earners->items(),
'meta' => [
'current_page' => $earners->currentPage(),
'last_page' => $earners->lastPage(),
'per_page' => $earners->perPage(),
'total' => $earners->total(),
],
]);
}
/**
* 기존 사용자 검색 (사업소득자 미등록, 테넌트 소속)
*/
public function searchUsers(Request $request): JsonResponse
{
$users = $this->service->searchTenantUsers($request->get('q') ?? '');
return response()->json([
'success' => true,
'data' => $users,
]);
}
/**
* 사업소득자 통계
*/
public function stats(): JsonResponse
{
$stats = $this->service->getStats();
return response()->json([
'success' => true,
'data' => $stats,
]);
}
/**
* 사업소득자 등록
*/
public function store(Request $request): JsonResponse
{
$rules = [
'existing_user_id' => 'nullable|integer|exists:users,id',
'name' => 'required|string|max:50',
'email' => 'nullable|email|max:100',
'phone' => 'nullable|string|max:20',
'department_id' => 'nullable|integer|exists:departments,id',
'position_key' => 'nullable|string|max:50',
'job_title_key' => 'nullable|string|max:50',
'work_location_key' => 'nullable|string|max:50',
'employment_type_key' => 'nullable|string|max:50',
'employee_status' => 'nullable|string|in:active,leave,resigned',
'manager_user_id' => 'nullable|integer|exists:users,id',
'display_name' => 'nullable|string|max:50',
'hire_date' => 'nullable|date',
'resign_date' => 'nullable|date',
'address' => 'nullable|string|max:200',
'emergency_contact' => 'nullable|string|max:100',
'resident_number' => 'nullable|string|max:14',
'bank_account.bank_code' => 'nullable|string|max:20',
'bank_account.bank_name' => 'nullable|string|max:50',
'bank_account.account_holder' => 'nullable|string|max:50',
'bank_account.account_number' => 'nullable|string|max:30',
'dependents' => 'nullable|array',
'dependents.*.name' => 'required_with:dependents|string|max:50',
'dependents.*.nationality' => 'nullable|string|in:korean,foreigner',
'dependents.*.resident_number' => 'nullable|string|max:14',
'dependents.*.relationship' => 'nullable|string|max:20',
'dependents.*.is_disabled' => 'nullable|boolean',
'dependents.*.is_dependent' => 'nullable|boolean',
// 사업장등록정보
'business_registration_number' => 'nullable|string|max:12',
'business_name' => 'nullable|string|max:100',
'business_representative' => 'nullable|string|max:50',
'business_type' => 'nullable|string|max:50',
'business_category' => 'nullable|string|max:50',
'business_address' => 'nullable|string|max:200',
];
if (! $request->filled('existing_user_id')) {
$rules['email'] = 'nullable|email|max:100|unique:users,email';
}
$validated = $request->validate($rules);
try {
$earner = $this->service->create($validated);
return response()->json([
'success' => true,
'message' => '사업소득자가 등록되었습니다.',
'data' => $earner,
], 201);
} catch (\Throwable $e) {
report($e);
return response()->json([
'success' => false,
'message' => '사업소득자 등록 중 오류가 발생했습니다.',
'error' => config('app.debug') ? $e->getMessage() : null,
], 500);
}
}
/**
* 사업소득자 상세 조회
*/
public function show(int $id): JsonResponse
{
$earner = $this->service->getById($id);
if (! $earner) {
return response()->json([
'success' => false,
'message' => '사업소득자 정보를 찾을 수 없습니다.',
], 404);
}
return response()->json([
'success' => true,
'data' => $earner,
]);
}
/**
* 사업소득자 수정
*/
public function update(Request $request, int $id): JsonResponse
{
$validated = $request->validate([
'name' => 'sometimes|required|string|max:50',
'email' => 'nullable|email|max:100',
'phone' => 'nullable|string|max:20',
'department_id' => 'nullable|integer|exists:departments,id',
'position_key' => 'nullable|string|max:50',
'job_title_key' => 'nullable|string|max:50',
'work_location_key' => 'nullable|string|max:50',
'employment_type_key' => 'nullable|string|max:50',
'employee_status' => 'nullable|string|in:active,leave,resigned',
'manager_user_id' => 'nullable|integer|exists:users,id',
'display_name' => 'nullable|string|max:50',
'hire_date' => 'nullable|date',
'resign_date' => 'nullable|date',
'address' => 'nullable|string|max:200',
'emergency_contact' => 'nullable|string|max:100',
'resident_number' => 'nullable|string|max:14',
'bank_account.bank_code' => 'nullable|string|max:20',
'bank_account.bank_name' => 'nullable|string|max:50',
'bank_account.account_holder' => 'nullable|string|max:50',
'bank_account.account_number' => 'nullable|string|max:30',
'dependents' => 'nullable|array',
'dependents.*.name' => 'required_with:dependents|string|max:50',
'dependents.*.nationality' => 'nullable|string|in:korean,foreigner',
'dependents.*.resident_number' => 'nullable|string|max:14',
'dependents.*.relationship' => 'nullable|string|max:20',
'dependents.*.is_disabled' => 'nullable|boolean',
'dependents.*.is_dependent' => 'nullable|boolean',
// 사업장등록정보
'business_registration_number' => 'nullable|string|max:12',
'business_name' => 'nullable|string|max:100',
'business_representative' => 'nullable|string|max:50',
'business_type' => 'nullable|string|max:50',
'business_category' => 'nullable|string|max:50',
'business_address' => 'nullable|string|max:200',
]);
if ($request->has('dependents_submitted') && ! array_key_exists('dependents', $validated)) {
$validated['dependents'] = [];
}
try {
$earner = $this->service->update($id, $validated);
if (! $earner) {
return response()->json([
'success' => false,
'message' => '사업소득자 정보를 찾을 수 없습니다.',
], 404);
}
return response()->json([
'success' => true,
'message' => '사업소득자 정보가 수정되었습니다.',
'data' => $earner,
]);
} catch (\Throwable $e) {
report($e);
return response()->json([
'success' => false,
'message' => '사업소득자 수정 중 오류가 발생했습니다.',
'error' => config('app.debug') ? $e->getMessage() : null,
], 500);
}
}
/**
* 사업소득자 삭제 (퇴직 처리)
*/
public function destroy(Request $request, int $id): JsonResponse|Response
{
try {
$result = $this->service->delete($id);
if (! $result) {
return response()->json([
'success' => false,
'message' => '사업소득자 정보를 찾을 수 없습니다.',
], 404);
}
if ($request->header('HX-Request')) {
$earners = $this->service->getBusinessIncomeEarners($request->all(), $request->integer('per_page', 20));
return response(view('hr.business-income-earners.partials.table', compact('earners')));
}
return response()->json([
'success' => true,
'message' => '퇴직 처리되었습니다.',
]);
} catch (\Throwable $e) {
report($e);
return response()->json([
'success' => false,
'message' => '퇴직 처리 중 오류가 발생했습니다.',
'error' => config('app.debug') ? $e->getMessage() : null,
], 500);
}
}
/**
* 첨부파일 업로드
*/
public function uploadFile(Request $request, int $id, GoogleCloudStorageService $gcs): JsonResponse
{
$earner = $this->service->getById($id);
if (! $earner) {
return response()->json(['success' => false, 'message' => '사업소득자 정보를 찾을 수 없습니다.'], 404);
}
$request->validate([
'files' => 'required|array|max:10',
'files.*' => 'file|max:20480',
]);
$tenantId = session('selected_tenant_id');
$uploaded = [];
foreach ($request->file('files') as $file) {
$storedName = Str::random(40).'.'.$file->getClientOriginalExtension();
$storagePath = "business-income-earners/{$tenantId}/{$earner->id}/{$storedName}";
Storage::disk('tenant')->put($storagePath, file_get_contents($file));
$gcsUri = null;
$gcsObjectName = null;
if ($gcs->isAvailable()) {
$gcsObjectName = $storagePath;
$gcsUri = $gcs->upload($file->getRealPath(), $gcsObjectName);
}
$fileRecord = File::create([
'tenant_id' => $tenantId,
'document_id' => $earner->id,
'document_type' => 'business_income_earner_profile',
'original_name' => $file->getClientOriginalName(),
'stored_name' => $storedName,
'file_path' => $storagePath,
'mime_type' => $file->getMimeType(),
'file_size' => $file->getSize(),
'file_type' => strtolower($file->getClientOriginalExtension()),
'gcs_object_name' => $gcsObjectName,
'gcs_uri' => $gcsUri,
'uploaded_by' => auth()->id(),
'created_by' => auth()->id(),
]);
$uploaded[] = $fileRecord;
}
return response()->json([
'success' => true,
'message' => count($uploaded).'개 파일이 업로드되었습니다.',
'data' => $uploaded,
], 201);
}
/**
* 첨부파일 삭제
*/
public function deleteFile(int $id, int $fileId, GoogleCloudStorageService $gcs): JsonResponse
{
$tenantId = session('selected_tenant_id');
$file = File::where('id', $fileId)
->where('document_id', $id)
->where('document_type', 'business_income_earner_profile')
->where('tenant_id', $tenantId)
->first();
if (! $file) {
return response()->json(['success' => false, 'message' => '파일을 찾을 수 없습니다.'], 404);
}
if ($gcs->isAvailable() && $file->gcs_object_name) {
$gcs->delete($file->gcs_object_name);
}
if ($file->file_path && Storage::disk('tenant')->exists($file->file_path)) {
Storage::disk('tenant')->delete($file->file_path);
}
$file->deleted_by = auth()->id();
$file->save();
$file->delete();
return response()->json(['success' => true, 'message' => '파일이 삭제되었습니다.']);
}
/**
* 첨부파일 다운로드
*/
public function downloadFile(int $id, int $fileId, GoogleCloudStorageService $gcs)
{
$tenantId = session('selected_tenant_id');
$file = File::where('id', $fileId)
->where('document_id', $id)
->where('document_type', 'business_income_earner_profile')
->where('tenant_id', $tenantId)
->first();
if (! $file) {
abort(404, '파일을 찾을 수 없습니다.');
}
if ($gcs->isAvailable() && $file->gcs_object_name) {
$signedUrl = $gcs->getSignedUrl($file->gcs_object_name, 60);
if ($signedUrl) {
return redirect($signedUrl);
}
}
$disk = Storage::disk('tenant');
if ($file->file_path && $disk->exists($file->file_path)) {
return $disk->download($file->file_path, $file->original_name);
}
abort(404, '파일이 서버에 존재하지 않습니다.');
}
}