fix: [payroll] 급여 등록 중복 체크 Race Condition 수정

- 중복 체크를 트랜잭션 내부로 이동 + lockForUpdate()
- UniqueConstraintViolationException 방어 처리 (500→422)
This commit is contained in:
김보곤
2026-02-27 15:48:29 +09:00
parent 66ceb06b4b
commit cc3aed004c
2 changed files with 23 additions and 18 deletions

View File

@@ -103,6 +103,11 @@ public function store(Request $request): JsonResponse
'success' => false,
'message' => $e->getMessage(),
], 422);
} catch (\Illuminate\Database\UniqueConstraintViolationException $e) {
return response()->json([
'success' => false,
'message' => '해당 직원의 동일 기간 급여가 이미 등록되어 있습니다.',
], 422);
} catch (\Throwable $e) {
report($e);

View File

@@ -110,25 +110,25 @@ public function storePayroll(array $data): Payroll
{
$tenantId = session('selected_tenant_id', 1);
// 동일 대상/기간 중복 체크 (soft-deleted 포함 — DB 유니크 제약과 일치)
$existing = Payroll::withTrashed()
->where('tenant_id', $tenantId)
->where('user_id', $data['user_id'])
->where('pay_year', $data['pay_year'])
->where('pay_month', $data['pay_month'])
->first();
if ($existing) {
if ($existing->trashed()) {
$existing->forceDelete();
} else {
throw new \InvalidArgumentException(
"해당 직원의 {$data['pay_year']}{$data['pay_month']}월 급여가 이미 등록되어 있습니다."
);
}
}
return DB::transaction(function () use ($data, $tenantId) {
// 동일 대상/기간 중복 체크 (트랜잭션 내 + 행 잠금으로 Race Condition 방지)
$existing = Payroll::withTrashed()
->where('tenant_id', $tenantId)
->where('user_id', $data['user_id'])
->where('pay_year', $data['pay_year'])
->where('pay_month', $data['pay_month'])
->lockForUpdate()
->first();
if ($existing) {
if ($existing->trashed()) {
$existing->forceDelete();
} else {
throw new \InvalidArgumentException(
"해당 직원의 {$data['pay_year']}{$data['pay_month']}월 급여가 이미 등록되어 있습니다."
);
}
}
$familyCount = $data['family_count'] ?? $this->resolveFamilyCount($data['user_id']);
$calculated = $this->calculateAmounts($data, null, $familyCount);
$this->applyDeductionOverrides($calculated, $data['deduction_overrides'] ?? null);