diff --git a/app/Services/SopTemplateService.php b/app/Services/SopTemplateService.php new file mode 100644 index 0000000..73aa53d --- /dev/null +++ b/app/Services/SopTemplateService.php @@ -0,0 +1,222 @@ + [ + 'name' => $template->name, + 'description' => $template->description, + 'category' => $template->category, + 'tags' => $template->tags, + 'version' => $template->version, + 'applicable_departments' => $template->applicable_departments, + 'applicable_positions' => $template->applicable_positions, + ], + 'steps' => $template->steps->map(function ($step) { + return [ + 'step_number' => $step->step_number, + 'title' => $step->title, + 'content' => $step->content, + 'sort_order' => $step->sort_order, + 'is_required' => $step->is_required, + 'interactive_tasks' => $step->interactiveTasks->map(function ($task) { + return [ + 'task_type' => $task->task_type, + 'task_config' => $task->task_config, + 'validation_rules' => $task->validation_rules, + 'timeout_seconds' => $task->timeout_seconds, + 'is_required' => $task->is_required, + ]; + })->toArray(), + ]; + })->toArray(), + 'exported_at' => now()->toIso8601String(), + 'exported_by' => auth()->user()?->name, + ]; + + return json_encode($data, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE); + } + + /** + * 从 JSON 导入模板 + * + * @param string $json + * @return SopTemplate + * @throws ValidationException + */ + public function importFromJson(string $json): SopTemplate + { + $data = json_decode($json, true); + + if (json_last_error() !== JSON_ERROR_NONE) { + throw ValidationException::withMessages([ + 'file' => ['无效的 JSON 格式'], + ]); + } + + // 验证数据结构 + $validator = Validator::make($data, [ + 'template' => 'required|array', + 'template.name' => 'required|string|max:255', + 'template.version' => 'required|string|max:50', + 'steps' => 'required|array|min:1', + 'steps.*.step_number' => 'required|integer', + 'steps.*.title' => 'required|string|max:255', + ]); + + if ($validator->fails()) { + throw new ValidationException($validator); + } + + return DB::transaction(function () use ($data) { + // 创建模板 + $template = SopTemplate::create([ + 'name' => $data['template']['name'], + 'description' => $data['template']['description'] ?? null, + 'category' => $data['template']['category'] ?? null, + 'tags' => $data['template']['tags'] ?? [], + 'version' => $data['template']['version'], + 'status' => 'draft', + 'applicable_departments' => $data['template']['applicable_departments'] ?? [], + 'applicable_positions' => $data['template']['applicable_positions'] ?? [], + 'created_by' => auth()->id(), + ]); + + // 创建步骤 + foreach ($data['steps'] as $stepData) { + $step = $template->steps()->create([ + 'step_number' => $stepData['step_number'], + 'title' => $stepData['title'], + 'content' => $stepData['content'] ?? null, + 'sort_order' => $stepData['sort_order'] ?? $stepData['step_number'], + 'is_required' => $stepData['is_required'] ?? true, + ]); + + // 创建交互任务 + if (!empty($stepData['interactive_tasks'])) { + foreach ($stepData['interactive_tasks'] as $taskData) { + $step->interactiveTasks()->create([ + 'task_type' => $taskData['task_type'], + 'task_config' => $taskData['task_config'] ?? [], + 'validation_rules' => $taskData['validation_rules'] ?? [], + 'timeout_seconds' => $taskData['timeout_seconds'] ?? null, + 'is_required' => $taskData['is_required'] ?? true, + ]); + } + } + } + + return $template; + }); + } + + /** + * 发布模板 + * + * @param SopTemplate $template + * @param string|null $changeLog + * @return void + */ + public function publish(SopTemplate $template, ?string $changeLog = null): void + { + // 创建版本快照 + $this->createVersion($template, $changeLog); + + // 更新状态 + $template->update([ + 'status' => 'published', + 'published_at' => now(), + ]); + } + + /** + * 创建版本快照 + * + * @param SopTemplate $template + * @param string|null $changeLog + * @return SopTemplateVersion + */ + public function createVersion(SopTemplate $template, ?string $changeLog = null): SopTemplateVersion + { + return SopTemplateVersion::create([ + 'sop_template_id' => $template->id, + 'version' => $template->version, + 'change_log' => $changeLog ?? '版本快照', + 'content_snapshot' => [ + 'template' => $template->toArray(), + 'steps' => $template->steps->map(function ($step) { + return array_merge($step->toArray(), [ + 'interactive_tasks' => $step->interactiveTasks->toArray(), + ]); + })->toArray(), + ], + 'created_by' => auth()->id(), + 'created_at' => now(), + ]); + } + + /** + * 归档模板 + * + * @param SopTemplate $template + * @return void + */ + public function archive(SopTemplate $template): void + { + $template->update([ + 'status' => 'archived', + ]); + } + + /** + * 复制模板 + * + * @param SopTemplate $template + * @param string $newName + * @return SopTemplate + */ + public function duplicate(SopTemplate $template, string $newName): SopTemplate + { + return DB::transaction(function () use ($template, $newName) { + // 复制模板 + $newTemplate = $template->replicate(); + $newTemplate->name = $newName; + $newTemplate->status = 'draft'; + $newTemplate->published_at = null; + $newTemplate->created_by = auth()->id(); + $newTemplate->save(); + + // 复制步骤 + foreach ($template->steps as $step) { + $newStep = $step->replicate(); + $newStep->sop_template_id = $newTemplate->id; + $newStep->save(); + + // 复制交互任务 + foreach ($step->interactiveTasks as $task) { + $newTask = $task->replicate(); + $newTask->sop_step_id = $newStep->id; + $newTask->save(); + } + } + + return $newTemplate; + }); + } +}