feat: [hr] 사원등록 기능 확장

- 기본정보에 주민등록번호 필드 추가
- 급여이체정보 섹션 추가 (이체은행, 예금주, 계좌번호)
- 부양가족 정보 섹션 추가 (동적 행 추가/삭제)
- 첨부파일 업로드/다운로드/삭제 기능 추가
- 은행 목록 config/banks.php 설정 파일 생성
- show 페이지 주민등록번호 뒷자리 마스킹 처리
This commit is contained in:
김보곤
2026-02-26 19:59:15 +09:00
parent 5e06f53d2d
commit 2edce0d282
9 changed files with 739 additions and 12 deletions

View File

@@ -3,10 +3,13 @@
namespace App\Http\Controllers\Api\Admin\HR;
use App\Http\Controllers\Controller;
use App\Models\Boards\File;
use App\Services\HR\EmployeeService;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
use Illuminate\Http\Response;
use Illuminate\Support\Facades\Storage;
use Illuminate\Support\Str;
class EmployeeController extends Controller
{
@@ -88,6 +91,18 @@ public function store(Request $request): JsonResponse
'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',
];
// 신규 사용자일 때만 이메일 unique 검증
@@ -157,6 +172,18 @@ public function update(Request $request, int $id): JsonResponse
'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',
]);
try {
@@ -228,6 +255,101 @@ public function destroy(Request $request, int $id): JsonResponse|Response
}
}
/**
* 사원 첨부파일 업로드
*/
public function uploadFile(Request $request, int $id): JsonResponse
{
$employee = $this->employeeService->getEmployeeById($id);
if (! $employee) {
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();
$path = "tenants/{$tenantId}/employees/{$employee->id}";
$file->storeAs($path, $storedName, 'tenant');
$fileRecord = File::create([
'tenant_id' => $tenantId,
'document_id' => $employee->id,
'document_type' => 'employee_profile',
'original_name' => $file->getClientOriginalName(),
'stored_name' => $storedName,
'file_path' => $path.'/'.$storedName,
'mime_type' => $file->getMimeType(),
'file_size' => $file->getSize(),
'file_type' => strtolower($file->getClientOriginalExtension()),
'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): JsonResponse
{
$tenantId = session('selected_tenant_id');
$file = File::where('id', $fileId)
->where('document_id', $id)
->where('document_type', 'employee_profile')
->where('tenant_id', $tenantId)
->first();
if (! $file) {
return response()->json(['success' => false, 'message' => '파일을 찾을 수 없습니다.'], 404);
}
Storage::disk('tenant')->delete($file->file_path);
$file->delete();
return response()->json(['success' => true, 'message' => '파일이 삭제되었습니다.']);
}
/**
* 사원 첨부파일 다운로드
*/
public function downloadFile(int $id, int $fileId)
{
$tenantId = session('selected_tenant_id');
$file = File::where('id', $fileId)
->where('document_id', $id)
->where('document_type', 'employee_profile')
->where('tenant_id', $tenantId)
->first();
if (! $file) {
abort(404, '파일을 찾을 수 없습니다.');
}
$disk = Storage::disk('tenant');
if (! $disk->exists($file->file_path)) {
abort(404, '파일이 서버에 존재하지 않습니다.');
}
return $disk->download($file->file_path, $file->original_name);
}
/**
* 직급/직책 추가
*/