tenantId(); $model = DesignModel::query() ->where('tenant_id', $tenantId) ->find($modelId); if (!$model) { throw new NotFoundHttpException(__('error.not_found')); } return ModelVersion::query() ->where('tenant_id', $tenantId) ->where('model_id', $modelId) ->orderBy('version_no') ->get(); } /** DRAFT 생성 (version_no 자동/수동) */ public function createDraft(int $modelId, array $extra = []): ModelVersion { $tenantId = $this->tenantId(); $model = DesignModel::query() ->where('tenant_id', $tenantId) ->find($modelId); if (!$model) { throw new NotFoundHttpException(__('error.not_found')); } return DB::transaction(function () use ($tenantId, $model, $extra) { $versionNo = $extra['version_no'] ?? null; if ($versionNo === null) { $max = ModelVersion::query() ->where('model_id', $model->id) ->max('version_no'); $versionNo = (int)($max ?? 0) + 1; } else { $exists = ModelVersion::query() ->where('model_id', $model->id) ->where('version_no', $versionNo) ->exists(); if ($exists) { throw ValidationException::withMessages(['version_no' => __('error.duplicate')]); } } return ModelVersion::create([ 'tenant_id' => $tenantId, 'model_id' => $model->id, 'version_no' => $versionNo, 'status' => 'DRAFT', 'is_active' => true, 'notes' => $extra['notes'] ?? null, 'effective_from' => $extra['effective_from'] ?? null, 'effective_to' => $extra['effective_to'] ?? null, ]); }); } /** RELEASED 전환 */ public function release(int $versionId): ModelVersion { $tenantId = $this->tenantId(); $mv = ModelVersion::query() ->where('tenant_id', $tenantId) ->find($versionId); if (!$mv) { throw new NotFoundHttpException(__('error.not_found')); } if ($mv->status === 'RELEASED') { return $mv; // 멱등 } // TODO: 대표 템플릿 존재 등 사전 검증 훅 가능 $mv->status = 'RELEASED'; $mv->effective_from = $mv->effective_from ?? now(); $mv->save(); return $mv; } }