277 lines
7.9 KiB
PHP
277 lines
7.9 KiB
PHP
|
|
<?php
|
||
|
|
|
||
|
|
namespace App\Services;
|
||
|
|
|
||
|
|
use App\Models\Process;
|
||
|
|
use App\Models\ProcessClassificationRule;
|
||
|
|
use Illuminate\Support\Facades\DB;
|
||
|
|
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
|
||
|
|
|
||
|
|
class ProcessService extends Service
|
||
|
|
{
|
||
|
|
/**
|
||
|
|
* 공정 목록 조회 (검색/페이징)
|
||
|
|
*/
|
||
|
|
public function index(array $params)
|
||
|
|
{
|
||
|
|
$tenantId = $this->tenantId();
|
||
|
|
|
||
|
|
$page = (int) ($params['page'] ?? 1);
|
||
|
|
$size = (int) ($params['size'] ?? 20);
|
||
|
|
$q = trim((string) ($params['q'] ?? ''));
|
||
|
|
$status = $params['status'] ?? null;
|
||
|
|
$processType = $params['process_type'] ?? null;
|
||
|
|
|
||
|
|
$query = Process::query()
|
||
|
|
->where('tenant_id', $tenantId)
|
||
|
|
->with('classificationRules');
|
||
|
|
|
||
|
|
// 검색어
|
||
|
|
if ($q !== '') {
|
||
|
|
$query->where(function ($qq) use ($q) {
|
||
|
|
$qq->where('process_name', 'like', "%{$q}%")
|
||
|
|
->orWhere('process_code', 'like', "%{$q}%")
|
||
|
|
->orWhere('description', 'like', "%{$q}%")
|
||
|
|
->orWhere('department', 'like', "%{$q}%");
|
||
|
|
});
|
||
|
|
}
|
||
|
|
|
||
|
|
// 상태 필터
|
||
|
|
if ($status === 'active') {
|
||
|
|
$query->where('is_active', true);
|
||
|
|
} elseif ($status === 'inactive') {
|
||
|
|
$query->where('is_active', false);
|
||
|
|
}
|
||
|
|
|
||
|
|
// 공정구분 필터
|
||
|
|
if ($processType) {
|
||
|
|
$query->where('process_type', $processType);
|
||
|
|
}
|
||
|
|
|
||
|
|
$query->orderBy('process_code');
|
||
|
|
|
||
|
|
return $query->paginate($size, ['*'], 'page', $page);
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* 공정 상세 조회
|
||
|
|
*/
|
||
|
|
public function show(int $id)
|
||
|
|
{
|
||
|
|
$tenantId = $this->tenantId();
|
||
|
|
|
||
|
|
$process = Process::where('tenant_id', $tenantId)
|
||
|
|
->with('classificationRules')
|
||
|
|
->find($id);
|
||
|
|
|
||
|
|
if (! $process) {
|
||
|
|
throw new NotFoundHttpException(__('error.not_found'));
|
||
|
|
}
|
||
|
|
|
||
|
|
return $process;
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* 공정 생성
|
||
|
|
*/
|
||
|
|
public function store(array $data)
|
||
|
|
{
|
||
|
|
$tenantId = $this->tenantId();
|
||
|
|
$userId = $this->apiUserId();
|
||
|
|
|
||
|
|
return DB::transaction(function () use ($data, $tenantId, $userId) {
|
||
|
|
// 공정코드 자동 생성
|
||
|
|
$data['process_code'] = $this->generateProcessCode($tenantId);
|
||
|
|
$data['tenant_id'] = $tenantId;
|
||
|
|
$data['created_by'] = $userId;
|
||
|
|
$data['is_active'] = $data['is_active'] ?? true;
|
||
|
|
|
||
|
|
// work_steps가 문자열이면 배열로 변환
|
||
|
|
if (isset($data['work_steps']) && is_string($data['work_steps'])) {
|
||
|
|
$data['work_steps'] = array_map('trim', explode(',', $data['work_steps']));
|
||
|
|
}
|
||
|
|
|
||
|
|
$rules = $data['classification_rules'] ?? [];
|
||
|
|
unset($data['classification_rules']);
|
||
|
|
|
||
|
|
$process = Process::create($data);
|
||
|
|
|
||
|
|
// 분류 규칙 저장
|
||
|
|
$this->syncClassificationRules($process, $rules);
|
||
|
|
|
||
|
|
return $process->load('classificationRules');
|
||
|
|
});
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* 공정 수정
|
||
|
|
*/
|
||
|
|
public function update(int $id, array $data)
|
||
|
|
{
|
||
|
|
$tenantId = $this->tenantId();
|
||
|
|
$userId = $this->apiUserId();
|
||
|
|
|
||
|
|
$process = Process::where('tenant_id', $tenantId)->find($id);
|
||
|
|
if (! $process) {
|
||
|
|
throw new NotFoundHttpException(__('error.not_found'));
|
||
|
|
}
|
||
|
|
|
||
|
|
return DB::transaction(function () use ($process, $data, $userId) {
|
||
|
|
$data['updated_by'] = $userId;
|
||
|
|
|
||
|
|
// work_steps가 문자열이면 배열로 변환
|
||
|
|
if (isset($data['work_steps']) && is_string($data['work_steps'])) {
|
||
|
|
$data['work_steps'] = array_map('trim', explode(',', $data['work_steps']));
|
||
|
|
}
|
||
|
|
|
||
|
|
$rules = $data['classification_rules'] ?? null;
|
||
|
|
unset($data['classification_rules']);
|
||
|
|
|
||
|
|
$process->update($data);
|
||
|
|
|
||
|
|
// 분류 규칙 동기화 (전달된 경우만)
|
||
|
|
if ($rules !== null) {
|
||
|
|
$this->syncClassificationRules($process, $rules);
|
||
|
|
}
|
||
|
|
|
||
|
|
return $process->fresh('classificationRules');
|
||
|
|
});
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* 공정 삭제
|
||
|
|
*/
|
||
|
|
public function destroy(int $id)
|
||
|
|
{
|
||
|
|
$tenantId = $this->tenantId();
|
||
|
|
$userId = $this->apiUserId();
|
||
|
|
|
||
|
|
$process = Process::where('tenant_id', $tenantId)->find($id);
|
||
|
|
if (! $process) {
|
||
|
|
throw new NotFoundHttpException(__('error.not_found'));
|
||
|
|
}
|
||
|
|
|
||
|
|
$process->update(['deleted_by' => $userId]);
|
||
|
|
$process->delete();
|
||
|
|
|
||
|
|
return true;
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* 공정 일괄 삭제
|
||
|
|
*/
|
||
|
|
public function destroyMany(array $ids)
|
||
|
|
{
|
||
|
|
$tenantId = $this->tenantId();
|
||
|
|
$userId = $this->apiUserId();
|
||
|
|
|
||
|
|
$count = Process::where('tenant_id', $tenantId)
|
||
|
|
->whereIn('id', $ids)
|
||
|
|
->update(['deleted_by' => $userId, 'deleted_at' => now()]);
|
||
|
|
|
||
|
|
return $count;
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* 공정 상태 토글
|
||
|
|
*/
|
||
|
|
public function toggleActive(int $id)
|
||
|
|
{
|
||
|
|
$tenantId = $this->tenantId();
|
||
|
|
$userId = $this->apiUserId();
|
||
|
|
|
||
|
|
$process = Process::where('tenant_id', $tenantId)->find($id);
|
||
|
|
if (! $process) {
|
||
|
|
throw new NotFoundHttpException(__('error.not_found'));
|
||
|
|
}
|
||
|
|
|
||
|
|
$process->update([
|
||
|
|
'is_active' => ! $process->is_active,
|
||
|
|
'updated_by' => $userId,
|
||
|
|
]);
|
||
|
|
|
||
|
|
return $process->fresh('classificationRules');
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* 공정코드 자동 생성 (P-001, P-002, ...)
|
||
|
|
*/
|
||
|
|
private function generateProcessCode(int $tenantId): string
|
||
|
|
{
|
||
|
|
$lastProcess = Process::where('tenant_id', $tenantId)
|
||
|
|
->withTrashed()
|
||
|
|
->orderByRaw("CAST(SUBSTRING(process_code, 3) AS UNSIGNED) DESC")
|
||
|
|
->first();
|
||
|
|
|
||
|
|
if ($lastProcess && preg_match('/^P-(\d+)$/', $lastProcess->process_code, $matches)) {
|
||
|
|
$nextNum = (int) $matches[1] + 1;
|
||
|
|
} else {
|
||
|
|
$nextNum = 1;
|
||
|
|
}
|
||
|
|
|
||
|
|
return sprintf('P-%03d', $nextNum);
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* 분류 규칙 동기화
|
||
|
|
*/
|
||
|
|
private function syncClassificationRules(Process $process, array $rules): void
|
||
|
|
{
|
||
|
|
// 기존 규칙 삭제
|
||
|
|
$process->classificationRules()->delete();
|
||
|
|
|
||
|
|
// 새 규칙 생성
|
||
|
|
foreach ($rules as $index => $rule) {
|
||
|
|
ProcessClassificationRule::create([
|
||
|
|
'process_id' => $process->id,
|
||
|
|
'registration_type' => $rule['registration_type'] ?? 'pattern',
|
||
|
|
'rule_type' => $rule['rule_type'],
|
||
|
|
'matching_type' => $rule['matching_type'],
|
||
|
|
'condition_value' => $rule['condition_value'],
|
||
|
|
'priority' => $rule['priority'] ?? $index,
|
||
|
|
'description' => $rule['description'] ?? null,
|
||
|
|
'is_active' => $rule['is_active'] ?? true,
|
||
|
|
]);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* 드롭다운용 공정 옵션 목록
|
||
|
|
*/
|
||
|
|
public function options()
|
||
|
|
{
|
||
|
|
$tenantId = $this->tenantId();
|
||
|
|
|
||
|
|
return Process::where('tenant_id', $tenantId)
|
||
|
|
->where('is_active', true)
|
||
|
|
->orderBy('process_code')
|
||
|
|
->select('id', 'process_code', 'process_name', 'process_type', 'department')
|
||
|
|
->get();
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* 통계
|
||
|
|
*/
|
||
|
|
public function getStats()
|
||
|
|
{
|
||
|
|
$tenantId = $this->tenantId();
|
||
|
|
|
||
|
|
$total = Process::where('tenant_id', $tenantId)->count();
|
||
|
|
$active = Process::where('tenant_id', $tenantId)->where('is_active', true)->count();
|
||
|
|
$inactive = Process::where('tenant_id', $tenantId)->where('is_active', false)->count();
|
||
|
|
|
||
|
|
$byType = Process::where('tenant_id', $tenantId)
|
||
|
|
->where('is_active', true)
|
||
|
|
->groupBy('process_type')
|
||
|
|
->selectRaw('process_type, count(*) as count')
|
||
|
|
->pluck('count', 'process_type');
|
||
|
|
|
||
|
|
return [
|
||
|
|
'total' => $total,
|
||
|
|
'active' => $active,
|
||
|
|
'inactive' => $inactive,
|
||
|
|
'by_type' => $byType,
|
||
|
|
];
|
||
|
|
}
|
||
|
|
}
|