Files
sam-api/app/Services/Calculation/ParameterValidator.php
hskwon bd678dfea9 feat: 업체별 동적 BOM 계산 시스템 구현
- 데이터베이스 스키마 확장: BOM 테이블에 계산 관련 필드 추가
- 계산 엔진 구현: CalculationEngine, FormulaParser, ParameterValidator
- API 구현: 견적 파라미터 추출, 실시간 BOM 계산, 업체별 산출식 관리
- FormRequest 검증: 모든 입력 데이터 검증 및 한국어 에러 메시지
- 라우트 등록: 5개 BOM 계산 API 엔드포인트 추가

주요 기능:
• BOM에서 필요한 조건만 동적 추출하여 견적 화면에 표시
• 경동기업 하드코딩 산출식을 동적 시스템으로 전환
• 업체별 산출식 버전 관리 및 실시간 테스트 지원

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-09-22 22:09:42 +09:00

218 lines
7.1 KiB
PHP

<?php
namespace App\Services\Calculation;
use Illuminate\Support\Facades\Validator;
use Illuminate\Validation\ValidationException;
class ParameterValidator
{
/**
* 파라미터 검증
* @param array $schema 파라미터 스키마
* @param array $parameters 검증할 파라미터
* @throws ValidationException
*/
public function validate(array $schema, array $parameters): void
{
$rules = $this->buildValidationRules($schema);
$messages = $this->buildValidationMessages($schema);
$validator = Validator::make($parameters, $rules, $messages);
if ($validator->fails()) {
throw new ValidationException($validator);
}
}
/**
* 검증 규칙 생성
* @param array $schema 파라미터 스키마
* @return array 라라벨 검증 규칙
*/
protected function buildValidationRules(array $schema): array
{
$rules = [];
if (!isset($schema['required_parameters'])) {
return $rules;
}
foreach ($schema['required_parameters'] as $param) {
$key = $param['key'];
$paramRules = [];
// 필수 여부
if ($param['required'] ?? false) {
$paramRules[] = 'required';
} else {
$paramRules[] = 'nullable';
}
// 데이터 타입
$type = $param['type'] ?? 'string';
switch ($type) {
case 'integer':
$paramRules[] = 'integer';
break;
case 'numeric':
case 'decimal':
$paramRules[] = 'numeric';
break;
case 'boolean':
$paramRules[] = 'boolean';
break;
case 'select':
if (isset($param['options'])) {
$validOptions = is_array($param['options']) ? array_keys($param['options']) : $param['options'];
$paramRules[] = 'in:' . implode(',', $validOptions);
}
break;
default:
$paramRules[] = 'string';
}
// 최소값/최대값
if (isset($param['min'])) {
$paramRules[] = 'min:' . $param['min'];
}
if (isset($param['max'])) {
$paramRules[] = 'max:' . $param['max'];
}
// 정규표현식
if (isset($param['pattern'])) {
$paramRules[] = 'regex:' . $param['pattern'];
}
$rules[$key] = implode('|', $paramRules);
}
return $rules;
}
/**
* 검증 메시지 생성
* @param array $schema 파라미터 스키마
* @return array 검증 메시지
*/
protected function buildValidationMessages(array $schema): array
{
$messages = [];
if (!isset($schema['required_parameters'])) {
return $messages;
}
foreach ($schema['required_parameters'] as $param) {
$key = $param['key'];
$label = $param['label'] ?? $key;
$messages["{$key}.required"] = "{$label}은(는) 필수 입력 항목입니다.";
$messages["{$key}.integer"] = "{$label}은(는) 정수여야 합니다.";
$messages["{$key}.numeric"] = "{$label}은(는) 숫자여야 합니다.";
$messages["{$key}.boolean"] = "{$label}은(는) 참/거짓 값이어야 합니다.";
$messages["{$key}.string"] = "{$label}은(는) 문자열이어야 합니다.";
if (isset($param['min'])) {
$messages["{$key}.min"] = "{$label}은(는) 최소 {$param['min']} 이상이어야 합니다.";
}
if (isset($param['max'])) {
$messages["{$key}.max"] = "{$label}은(는) 최대 {$param['max']} 이하여야 합니다.";
}
if (isset($param['options'])) {
$validOptions = is_array($param['options']) ? array_values($param['options']) : $param['options'];
$messages["{$key}.in"] = "{$label}은(는) 다음 중 하나여야 합니다: " . implode(', ', $validOptions);
}
if (isset($param['pattern'])) {
$messages["{$key}.regex"] = "{$label}의 형식이 올바르지 않습니다.";
}
}
return $messages;
}
/**
* 범위 검증
* @param array $parameters 파라미터
* @param array $ranges 범위 정의
*/
public function validateRanges(array $parameters, array $ranges): void
{
foreach ($ranges as $key => $range) {
if (!isset($parameters[$key])) continue;
$value = $parameters[$key];
if (isset($range['min']) && $value < $range['min']) {
throw new ValidationException(validator([], []), [
$key => ["{$key}는 최소 {$range['min']} 이상이어야 합니다."]
]);
}
if (isset($range['max']) && $value > $range['max']) {
throw new ValidationException(validator([], []), [
$key => ["{$key}는 최대 {$range['max']} 이하여야 합니다."]
]);
}
}
}
/**
* 업체별 특수 검증
* @param array $parameters 파라미터
* @param string $companyType 업체 타입
*/
public function validateCompanySpecific(array $parameters, string $companyType): void
{
switch ($companyType) {
case '경동기업':
$this->validateKyungdongRules($parameters);
break;
case '삼성물산':
$this->validateSamsungRules($parameters);
break;
default:
// 기본 검증만 수행
break;
}
}
/**
* 경동기업 특수 검증 규칙
*/
protected function validateKyungdongRules(array $parameters): void
{
// 경동기업 특수 규칙 예시
if (isset($parameters['W0']) && isset($parameters['H0'])) {
$area = ($parameters['W0'] * $parameters['H0']) / 1000000;
// 면적이 너무 크면 경고
if ($area > 50) {
throw new ValidationException(validator([], []), [
'area' => ['면적이 50㎡를 초과합니다. 특수 산출식이 필요할 수 있습니다.']
]);
}
}
// 제품타입별 사이즈 제한
if ($parameters['product_type'] === 'screen' && isset($parameters['W0'])) {
if ($parameters['W0'] > 12000) {
throw new ValidationException(validator([], []), [
'W0' => ['스크린 제품은 가로 12,000mm를 초과할 수 없습니다.']
]);
}
}
}
/**
* 삼성물산 특수 검증 규칙
*/
protected function validateSamsungRules(array $parameters): void
{
// 삼성물산 특수 규칙 (예시)
// 실제로는 해당 업체의 요구사항에 따라 구현
}
}