package service import ( "errors" "fmt" "github.com/google/uuid" "github.com/rs/zerolog" "web-qudo-be/app/database/entity" "web-qudo-be/app/module/approval_workflows/repository" "web-qudo-be/app/module/approval_workflows/request" stepRepo "web-qudo-be/app/module/approval_workflow_steps/repository" "web-qudo-be/utils/paginator" ) type approvalWorkflowsService struct { ApprovalWorkflowsRepository repository.ApprovalWorkflowsRepository ApprovalWorkflowStepsRepository stepRepo.ApprovalWorkflowStepsRepository Log zerolog.Logger } // ApprovalWorkflowsService define interface of IApprovalWorkflowsService type ApprovalWorkflowsService interface { // Basic CRUD GetAll(clientId *uuid.UUID, req request.ApprovalWorkflowsQueryRequest) (workflows []*entity.ApprovalWorkflows, paging paginator.Pagination, err error) FindOne(clientId *uuid.UUID, id uint) (workflow *entity.ApprovalWorkflows, err error) Create(clientId *uuid.UUID, workflow *entity.ApprovalWorkflows, steps []*entity.ApprovalWorkflowSteps) (workflowReturn *entity.ApprovalWorkflows, err error) Update(clientId *uuid.UUID, id uint, workflow *entity.ApprovalWorkflows) (err error) Delete(clientId *uuid.UUID, id uint) (err error) // Workflow management GetDefault(clientId *uuid.UUID) (workflow *entity.ApprovalWorkflows, err error) SetDefault(clientId *uuid.UUID, id uint) (err error) ActivateWorkflow(clientId *uuid.UUID, id uint) (err error) DeactivateWorkflow(clientId *uuid.UUID, id uint) (err error) // Workflow with steps GetWorkflowWithSteps(clientId *uuid.UUID, id uint) (workflow *entity.ApprovalWorkflows, steps []*entity.ApprovalWorkflowSteps, err error) CreateWorkflowWithSteps(clientId *uuid.UUID, workflow *entity.ApprovalWorkflows, steps []*entity.ApprovalWorkflowSteps) (workflowReturn *entity.ApprovalWorkflows, err error) UpdateWorkflowWithSteps(clientId *uuid.UUID, id uint, workflow *entity.ApprovalWorkflows, steps []*entity.ApprovalWorkflowSteps) (err error) // Validation ValidateWorkflow(clientId *uuid.UUID, workflow *entity.ApprovalWorkflows, steps []*entity.ApprovalWorkflowSteps) (isValid bool, errors []string, err error) CanDeleteWorkflow(clientId *uuid.UUID, id uint) (canDelete bool, reason string, err error) } func NewApprovalWorkflowsService( approvalWorkflowsRepository repository.ApprovalWorkflowsRepository, approvalWorkflowStepsRepository stepRepo.ApprovalWorkflowStepsRepository, log zerolog.Logger, ) ApprovalWorkflowsService { return &approvalWorkflowsService{ ApprovalWorkflowsRepository: approvalWorkflowsRepository, ApprovalWorkflowStepsRepository: approvalWorkflowStepsRepository, Log: log, } } // Basic CRUD implementations func (_i *approvalWorkflowsService) GetAll(clientId *uuid.UUID, req request.ApprovalWorkflowsQueryRequest) (workflows []*entity.ApprovalWorkflows, paging paginator.Pagination, err error) { return _i.ApprovalWorkflowsRepository.GetAll(clientId, req) } func (_i *approvalWorkflowsService) FindOne(clientId *uuid.UUID, id uint) (workflow *entity.ApprovalWorkflows, err error) { return _i.ApprovalWorkflowsRepository.FindOne(clientId, id) } func (_i *approvalWorkflowsService) Create(clientId *uuid.UUID, workflow *entity.ApprovalWorkflows, steps []*entity.ApprovalWorkflowSteps) (workflowReturn *entity.ApprovalWorkflows, err error) { // Validate workflow and steps isValid, validationErrors, err := _i.ValidateWorkflow(clientId, workflow, steps) if err != nil { return nil, err } if !isValid { return nil, errors.New(fmt.Sprintf("Validation failed: %v", validationErrors)) } // Create workflow workflowReturn, err = _i.ApprovalWorkflowsRepository.Create(clientId, workflow) if err != nil { return nil, err } // Create steps for i, step := range steps { step.WorkflowId = workflowReturn.ID step.StepOrder = i + 1 _, err = _i.ApprovalWorkflowStepsRepository.Create(clientId, step) if err != nil { // Rollback workflow creation if step creation fails _i.ApprovalWorkflowsRepository.Delete(clientId, workflowReturn.ID) return nil, err } } return workflowReturn, nil } func (_i *approvalWorkflowsService) Update(clientId *uuid.UUID, id uint, workflow *entity.ApprovalWorkflows) (err error) { // Check if workflow exists existingWorkflow, err := _i.ApprovalWorkflowsRepository.FindOne(clientId, id) if err != nil { return err } if existingWorkflow == nil { return errors.New("workflow not found") } return _i.ApprovalWorkflowsRepository.Update(clientId, id, workflow) } func (_i *approvalWorkflowsService) Delete(clientId *uuid.UUID, id uint) (err error) { // Check if workflow can be deleted canDelete, reason, err := _i.CanDeleteWorkflow(clientId, id) if err != nil { return err } if !canDelete { return errors.New(reason) } return _i.ApprovalWorkflowsRepository.Delete(clientId, id) } // Workflow management func (_i *approvalWorkflowsService) GetDefault(clientId *uuid.UUID) (workflow *entity.ApprovalWorkflows, err error) { return _i.ApprovalWorkflowsRepository.FindDefault(clientId) } func (_i *approvalWorkflowsService) SetDefault(clientId *uuid.UUID, id uint) (err error) { // Check if workflow exists and is active workflow, err := _i.ApprovalWorkflowsRepository.FindOne(clientId, id) if err != nil { return err } if workflow == nil { return errors.New("workflow not found") } if workflow.IsActive == nil || !*workflow.IsActive { return errors.New("cannot set inactive workflow as default") } return _i.ApprovalWorkflowsRepository.SetDefault(clientId, id) } func (_i *approvalWorkflowsService) ActivateWorkflow(clientId *uuid.UUID, id uint) (err error) { // Validate workflow before activation workflow, err := _i.ApprovalWorkflowsRepository.FindOne(clientId, id) if err != nil { return err } if workflow == nil { return errors.New("workflow not found") } // Get workflow steps and validate steps, err := _i.ApprovalWorkflowStepsRepository.GetByWorkflowId(clientId, id) if err != nil { return err } isValid, validationErrors, err := _i.ValidateWorkflow(clientId, workflow, steps) if err != nil { return err } if !isValid { return errors.New(fmt.Sprintf("Cannot activate invalid workflow: %v", validationErrors)) } // Activate workflow isActive := true updateData := &entity.ApprovalWorkflows{IsActive: &isActive} return _i.ApprovalWorkflowsRepository.Update(clientId, id, updateData) } func (_i *approvalWorkflowsService) DeactivateWorkflow(clientId *uuid.UUID, id uint) (err error) { // Check if this is the default workflow defaultWorkflow, err := _i.ApprovalWorkflowsRepository.FindDefault(clientId) if err != nil { return err } if defaultWorkflow != nil && defaultWorkflow.ID == id { return errors.New("cannot deactivate default workflow") } // Check if workflow is being used in active approval flows canDelete, reason, err := _i.CanDeleteWorkflow(clientId, id) if err != nil { return err } if !canDelete { return errors.New(fmt.Sprintf("Cannot deactivate workflow: %s", reason)) } // Deactivate workflow isActive := false updateData := &entity.ApprovalWorkflows{IsActive: &isActive} return _i.ApprovalWorkflowsRepository.Update(clientId, id, updateData) } // Workflow with steps func (_i *approvalWorkflowsService) GetWorkflowWithSteps(clientId *uuid.UUID, id uint) (workflow *entity.ApprovalWorkflows, steps []*entity.ApprovalWorkflowSteps, err error) { workflow, err = _i.ApprovalWorkflowsRepository.FindOne(clientId, id) if err != nil { return nil, nil, err } steps, err = _i.ApprovalWorkflowStepsRepository.GetByWorkflowId(clientId, id) if err != nil { return nil, nil, err } return workflow, steps, nil } func (_i *approvalWorkflowsService) CreateWorkflowWithSteps(clientId *uuid.UUID, workflow *entity.ApprovalWorkflows, steps []*entity.ApprovalWorkflowSteps) (workflowReturn *entity.ApprovalWorkflows, err error) { return _i.Create(clientId, workflow, steps) } func (_i *approvalWorkflowsService) UpdateWorkflowWithSteps(clientId *uuid.UUID, id uint, workflow *entity.ApprovalWorkflows, steps []*entity.ApprovalWorkflowSteps) (err error) { // Update workflow err = _i.Update(clientId, id, workflow) if err != nil { return err } // Get existing steps existingSteps, err := _i.ApprovalWorkflowStepsRepository.GetByWorkflowId(clientId, id) if err != nil { return err } // Delete existing steps (simplified approach - in production, you might want to update/merge) for _, existingStep := range existingSteps { err = _i.ApprovalWorkflowStepsRepository.Delete(clientId, existingStep.ID) if err != nil { return err } } // Create new steps for i, step := range steps { step.WorkflowId = id step.StepOrder = i + 1 _, err = _i.ApprovalWorkflowStepsRepository.Create(clientId, step) if err != nil { return err } } return nil } // Validation func (_i *approvalWorkflowsService) ValidateWorkflow(clientId *uuid.UUID, workflow *entity.ApprovalWorkflows, steps []*entity.ApprovalWorkflowSteps) (isValid bool, errors []string, err error) { errors = make([]string, 0) // Validate workflow if workflow.Name == "" { errors = append(errors, "Workflow name is required") } // Validate steps if len(steps) == 0 { errors = append(errors, "Workflow must have at least one step") } else { // Check for duplicate step orders stepOrderMap := make(map[int]bool) for i, step := range steps { expectedOrder := i + 1 if step.StepOrder != 0 && step.StepOrder != expectedOrder { errors = append(errors, fmt.Sprintf("Step %d has incorrect order %d, expected %d", i+1, step.StepOrder, expectedOrder)) } if stepOrderMap[step.StepOrder] { errors = append(errors, fmt.Sprintf("Duplicate step order: %d", step.StepOrder)) } stepOrderMap[step.StepOrder] = true if step.StepName == "" { errors = append(errors, fmt.Sprintf("Step %d name is required", i+1)) } if step.RequiredUserLevelId == 0 { errors = append(errors, fmt.Sprintf("Step %d must have a required user level", i+1)) } } } isValid = len(errors) == 0 return isValid, errors, nil } func (_i *approvalWorkflowsService) CanDeleteWorkflow(clientId *uuid.UUID, id uint) (canDelete bool, reason string, err error) { // Check if workflow is default defaultWorkflow, err := _i.ApprovalWorkflowsRepository.FindDefault(clientId) if err != nil { return false, "", err } if defaultWorkflow != nil && defaultWorkflow.ID == id { return false, "Cannot delete default workflow", nil } // Check if workflow is being used in active approval flows // This would require a method in ArticleApprovalFlowsRepository // For now, we'll assume it can be deleted // TODO: Implement check for active approval flows return true, "", nil }