package service import ( "errors" "time" "web-qudo-be/app/database/entity" approvalWorkflowStepsRepo "web-qudo-be/app/module/approval_workflow_steps/repository" approvalWorkflowsRepo "web-qudo-be/app/module/approval_workflows/repository" "web-qudo-be/app/module/article_approval_flows/repository" "web-qudo-be/app/module/article_approval_flows/request" approvalStepLogsRepo "web-qudo-be/app/module/article_approval_step_logs/repository" articlesRepo "web-qudo-be/app/module/articles/repository" usersRepo "web-qudo-be/app/module/users/repository" "web-qudo-be/utils/paginator" "github.com/google/uuid" "github.com/rs/zerolog" ) type articleApprovalFlowsService struct { ArticleApprovalFlowsRepository repository.ArticleApprovalFlowsRepository ApprovalWorkflowsRepository approvalWorkflowsRepo.ApprovalWorkflowsRepository ApprovalWorkflowStepsRepository approvalWorkflowStepsRepo.ApprovalWorkflowStepsRepository ArticleApprovalStepLogsRepository approvalStepLogsRepo.ArticleApprovalStepLogsRepository ArticlesRepository articlesRepo.ArticlesRepository UsersRepository usersRepo.UsersRepository Log zerolog.Logger } // ArticleApprovalFlowsService define interface of IArticleApprovalFlowsService type ArticleApprovalFlowsService interface { // Basic CRUD GetAll(clientId *uuid.UUID, req request.ArticleApprovalFlowsQueryRequest) (flows []*entity.ArticleApprovalFlows, paging paginator.Pagination, err error) FindOne(clientId *uuid.UUID, id uint) (flow *entity.ArticleApprovalFlows, err error) Create(clientId *uuid.UUID, flow *entity.ArticleApprovalFlows) (flowReturn *entity.ArticleApprovalFlows, err error) Update(id uint, flow *entity.ArticleApprovalFlows) (err error) Delete(clientId *uuid.UUID, id uint) (err error) // Article submission and approval workflow SubmitArticleForApproval(clientId *uuid.UUID, articleId uint, submittedById uint, workflowId *uint) (flow *entity.ArticleApprovalFlows, err error) ApproveStep(clientId *uuid.UUID, flowId uint, approvedById uint, message string) (err error) RejectArticle(clientId *uuid.UUID, flowId uint, rejectedById uint, reason string) (err error) RequestRevision(clientId *uuid.UUID, flowId uint, requestedById uint, revisionMessage string) (err error) ResubmitAfterRevision(clientId *uuid.UUID, flowId uint, resubmittedById uint) (err error) // Dashboard and queue methods GetPendingApprovals(clientId *uuid.UUID, userLevelId uint, page, limit int, filters map[string]interface{}) (flows []*entity.ArticleApprovalFlows, paging paginator.Pagination, err error) GetMyApprovalQueue(clientId *uuid.UUID, userLevelId uint, page, limit int, includePreview bool, urgentOnly bool) (flows []*entity.ArticleApprovalFlows, paging paginator.Pagination, err error) GetApprovalHistory(clientId *uuid.UUID, articleId uint, page, limit int) (logs []*entity.ArticleApprovalStepLogs, paging paginator.Pagination, err error) // Statistics and analytics GetPendingCountByLevel(clientId *uuid.UUID, userLevelId uint) (count int64, err error) GetOverdueCountByLevel(clientId *uuid.UUID, userLevelId uint) (count int64, err error) GetApprovalStatistics(clientId *uuid.UUID, userLevelId uint, startDate, endDate time.Time) (stats map[string]interface{}, err error) GetWorkloadAnalytics(clientId *uuid.UUID, userLevelId uint) (analytics map[string]interface{}, err error) // Workflow management CanUserApproveStep(clientId *uuid.UUID, flowId uint, userId uint, userLevelId uint) (canApprove bool, reason string, err error) GetCurrentStepInfo(clientId *uuid.UUID, flowId uint) (stepInfo map[string]interface{}, err error) GetNextStepPreview(clientId *uuid.UUID, flowId uint) (nextStep *entity.ApprovalWorkflowSteps, err error) } func NewArticleApprovalFlowsService( articleApprovalFlowsRepository repository.ArticleApprovalFlowsRepository, approvalWorkflowsRepository approvalWorkflowsRepo.ApprovalWorkflowsRepository, approvalWorkflowStepsRepository approvalWorkflowStepsRepo.ApprovalWorkflowStepsRepository, articleApprovalStepLogsRepository approvalStepLogsRepo.ArticleApprovalStepLogsRepository, articlesRepository articlesRepo.ArticlesRepository, usersRepository usersRepo.UsersRepository, log zerolog.Logger, ) ArticleApprovalFlowsService { return &articleApprovalFlowsService{ ArticleApprovalFlowsRepository: articleApprovalFlowsRepository, ApprovalWorkflowsRepository: approvalWorkflowsRepository, ApprovalWorkflowStepsRepository: approvalWorkflowStepsRepository, ArticleApprovalStepLogsRepository: articleApprovalStepLogsRepository, ArticlesRepository: articlesRepository, UsersRepository: usersRepository, Log: log, } } // Basic CRUD implementations func (_i *articleApprovalFlowsService) GetAll(clientId *uuid.UUID, req request.ArticleApprovalFlowsQueryRequest) (flows []*entity.ArticleApprovalFlows, paging paginator.Pagination, err error) { return _i.ArticleApprovalFlowsRepository.GetAll(clientId, req) } func (_i *articleApprovalFlowsService) FindOne(clientId *uuid.UUID, id uint) (flow *entity.ArticleApprovalFlows, err error) { return _i.ArticleApprovalFlowsRepository.FindOne(clientId, id) } func (_i *articleApprovalFlowsService) Create(clientId *uuid.UUID, flow *entity.ArticleApprovalFlows) (flowReturn *entity.ArticleApprovalFlows, err error) { return _i.ArticleApprovalFlowsRepository.Create(clientId, flow) } func (_i *articleApprovalFlowsService) Update(id uint, flow *entity.ArticleApprovalFlows) (err error) { return _i.ArticleApprovalFlowsRepository.Update(id, flow) } func (_i *articleApprovalFlowsService) Delete(clientId *uuid.UUID, id uint) (err error) { return _i.ArticleApprovalFlowsRepository.Delete(clientId, id) } // Article submission and approval workflow func (_i *articleApprovalFlowsService) SubmitArticleForApproval(clientId *uuid.UUID, articleId uint, submittedById uint, workflowId *uint) (flow *entity.ArticleApprovalFlows, err error) { // Check if article already has an active approval flow existingFlow, err := _i.ArticleApprovalFlowsRepository.FindActiveByArticleId(articleId) if err == nil && existingFlow != nil { return nil, errors.New("article already has an active approval flow") } // Get workflow (use default if not specified) var workflow *entity.ApprovalWorkflows if workflowId != nil { workflow, err = _i.ApprovalWorkflowsRepository.FindOne(clientId, *workflowId) } else { workflow, err = _i.ApprovalWorkflowsRepository.FindDefault(clientId) } if err != nil { return nil, err } if workflow == nil { return nil, errors.New("no workflow found") } if workflow.IsActive != nil && !*workflow.IsActive { return nil, errors.New("workflow is not active") } // Get first step of workflow firstStep, err := _i.ApprovalWorkflowStepsRepository.FindByWorkflowAndStep(clientId, workflow.ID, 1) if err != nil { return nil, err } if firstStep == nil { return nil, errors.New("workflow has no steps") } // Create approval flow flow = &entity.ArticleApprovalFlows{ ArticleId: articleId, WorkflowId: workflow.ID, CurrentStep: 1, StatusId: 1, // pending SubmittedById: submittedById, SubmittedAt: time.Now(), } flow, err = _i.ArticleApprovalFlowsRepository.Create(clientId, flow) if err != nil { return nil, err } // Get current article data first currentArticle, err := _i.ArticlesRepository.FindOne(clientId, articleId) if err != nil { return nil, err } // Update only the necessary fields currentArticle.WorkflowId = &workflow.ID currentArticle.CurrentApprovalStep = &flow.CurrentStep currentArticle.StatusId = &[]int{1}[0] // pending approval err = _i.ArticlesRepository.UpdateSkipNull(clientId, articleId, currentArticle) if err != nil { return nil, err } // Process auto-skip logic based on user level err = _i.processAutoSkipSteps(clientId, flow, submittedById) if err != nil { return nil, err } return flow, nil } // processAutoSkipSteps handles automatic step skipping based on user level func (_i *articleApprovalFlowsService) processAutoSkipSteps(clientId *uuid.UUID, flow *entity.ArticleApprovalFlows, submittedById uint) error { // Get user level of the submitter userLevelId, err := _i.getUserLevelId(clientId, submittedById) if err != nil { return err } // Get all workflow steps steps, err := _i.ApprovalWorkflowStepsRepository.GetByWorkflowId(clientId, flow.WorkflowId) if err != nil { return err } // Sort steps by step order sortStepsByOrder(steps) // Process each step to determine if it should be auto-skipped for _, step := range steps { shouldSkip := _i.shouldSkipStep(userLevelId, step.RequiredUserLevelId) if shouldSkip { // Create skip log stepLog := &entity.ArticleApprovalStepLogs{ ApprovalFlowId: flow.ID, StepOrder: step.StepOrder, StepName: step.StepName, ApprovedById: &submittedById, Action: "auto_skip", Message: &[]string{"Step auto-skipped due to user level"}[0], ProcessedAt: time.Now(), UserLevelId: step.RequiredUserLevelId, } _, err = _i.ArticleApprovalStepLogsRepository.Create(clientId, stepLog) if err != nil { return err } // Update flow to next step (handle step order starting from 0) nextStepOrder := step.StepOrder + 1 flow.CurrentStep = nextStepOrder } else { // Stop at first step that cannot be skipped break } } // Update flow with final current step err = _i.ArticleApprovalFlowsRepository.Update(flow.ID, flow) if err != nil { return err } // Get current article data first currentArticle, err := _i.ArticlesRepository.FindOne(clientId, flow.ArticleId) if err != nil { return err } // Update only the necessary fields currentArticle.CurrentApprovalStep = &flow.CurrentStep err = _i.ArticlesRepository.UpdateSkipNull(clientId, flow.ArticleId, currentArticle) if err != nil { return err } // Check if all steps were skipped (workflow complete) // Find the highest step order maxStepOrder := 0 for _, step := range steps { if step.StepOrder > maxStepOrder { maxStepOrder = step.StepOrder } } if flow.CurrentStep > maxStepOrder { // All steps completed, mark as approved flow.StatusId = 2 // approved flow.CurrentStep = 0 // Set to 0 to indicate completion flow.CompletedAt = &[]time.Time{time.Now()}[0] err = _i.ArticleApprovalFlowsRepository.Update(flow.ID, flow) if err != nil { return err } // Get current article data first currentArticle, err := _i.ArticlesRepository.FindOne(clientId, flow.ArticleId) if err != nil { return err } // Update only the necessary fields currentArticle.StatusId = &[]int{2}[0] // approved currentArticle.CurrentApprovalStep = &[]int{0}[0] // Set to 0 to indicate completion err = _i.ArticlesRepository.UpdateSkipNull(clientId, flow.ArticleId, currentArticle) if err != nil { return err } } return nil } // getUserLevelId gets the user level ID for a given user func (_i *articleApprovalFlowsService) getUserLevelId(clientId *uuid.UUID, userId uint) (uint, error) { // Get user from database to retrieve user level user, err := _i.UsersRepository.FindOne(clientId, userId) if err != nil { _i.Log.Error().Err(err).Uint("userId", userId).Msg("Failed to find user") return 0, err } if user.UserLevel == nil { _i.Log.Error().Uint("userId", userId).Msg("User has no user level") return 0, errors.New("user has no user level") } _i.Log.Info(). Uint("userId", userId). Uint("userLevelId", user.UserLevel.ID). Str("userLevelName", user.UserLevel.Name). Msg("Retrieved user level from database") return user.UserLevel.ID, nil } // shouldSkipStep determines if a step should be auto-skipped based on user level func (_i *articleApprovalFlowsService) shouldSkipStep(userLevelId, requiredLevelId uint) bool { // Get user level details to compare level numbers // User level with lower level_number (higher authority) can skip steps requiring higher level_number // For now, we'll use a simple comparison based on IDs // In production, this should compare level_number fields // Simple logic: if user level ID is less than required level ID, they can skip // This assumes level 1 (ID=1) has higher authority than level 2 (ID=2), etc. return userLevelId < requiredLevelId } // sortStepsByOrder sorts workflow steps by their step order func sortStepsByOrder(steps []*entity.ApprovalWorkflowSteps) { // Simple bubble sort for step order n := len(steps) for i := 0; i < n-1; i++ { for j := 0; j < n-i-1; j++ { if steps[j].StepOrder > steps[j+1].StepOrder { steps[j], steps[j+1] = steps[j+1], steps[j] } } } } func (_i *articleApprovalFlowsService) ApproveStep(clientId *uuid.UUID, flowId uint, approvedById uint, message string) (err error) { // Get approval flow flow, err := _i.ArticleApprovalFlowsRepository.FindOne(clientId, flowId) if err != nil { return err } if flow == nil { return errors.New("approval flow not found") } if flow.StatusId != 1 && flow.StatusId != 4 { // not pending or revision_requested return errors.New("approval flow is not in pending state") } // Get current step currentStep, err := _i.ApprovalWorkflowStepsRepository.FindByWorkflowAndStep(clientId, flow.WorkflowId, flow.CurrentStep) if err != nil { return err } if currentStep == nil { return errors.New("current step not found") } // Create step log stepLog := &entity.ArticleApprovalStepLogs{ ApprovalFlowId: flow.ID, StepOrder: flow.CurrentStep, StepName: currentStep.StepName, ApprovedById: &approvedById, Action: "approve", Message: &message, ProcessedAt: time.Now(), UserLevelId: currentStep.RequiredUserLevelId, } _, err = _i.ArticleApprovalStepLogsRepository.Create(clientId, stepLog) if err != nil { return err } // Check if there's a next step nextStep, err := _i.ApprovalWorkflowStepsRepository.GetNextStep(clientId, flow.WorkflowId, flow.CurrentStep) if err != nil && err.Error() != "record not found" { return err } if nextStep == nil || nextStep.ID == 0 { // No next step - approval complete flowUpdate := &entity.ArticleApprovalFlows{ StatusId: 2, // approved CurrentStep: 0, // Set to 0 to indicate completion CompletedAt: &[]time.Time{time.Now()}[0], } // Debug logging _i.Log.Info(). Interface("flowUpdate :: ", flowUpdate). Msg("Retrieved next step from database") err = _i.ArticleApprovalFlowsRepository.Update(flowId, flowUpdate) if err != nil { return err } // Get current article data first currentArticle, err := _i.ArticlesRepository.FindOne(clientId, flow.ArticleId) if err != nil { return err } // Update only the necessary fields currentArticle.StatusId = &[]int{2}[0] // approved currentArticle.CurrentApprovalStep = &[]int{0}[0] // Set to 0 to indicate completion currentArticle.IsPublish = &[]bool{true}[0] // Set to true to indicate publication currentArticle.IsDraft = &[]bool{false}[0] // Set to false to indicate publication err = _i.ArticlesRepository.UpdateSkipNull(clientId, flow.ArticleId, currentArticle) if err != nil { return err } } else { // Move to next step flowUpdate := &entity.ArticleApprovalFlows{ CurrentStep: nextStep.StepOrder, StatusId: 1, // pending } err = _i.ArticleApprovalFlowsRepository.Update(flowId, flowUpdate) if err != nil { return err } // Get current article data first currentArticle, err := _i.ArticlesRepository.FindOne(clientId, flow.ArticleId) if err != nil { return err } // Update only the necessary fields currentArticle.CurrentApprovalStep = &nextStep.StepOrder err = _i.ArticlesRepository.UpdateSkipNull(clientId, flow.ArticleId, currentArticle) if err != nil { return err } } return nil } func (_i *articleApprovalFlowsService) RejectArticle(clientId *uuid.UUID, flowId uint, rejectedById uint, reason string) (err error) { // Get approval flow flow, err := _i.ArticleApprovalFlowsRepository.FindOne(clientId, flowId) if err != nil { return err } if flow == nil { return errors.New("approval flow not found") } if flow.StatusId != 1 && flow.StatusId != 4 { // not pending or revision_requested return errors.New("approval flow is not in pending state") } // Get current step currentStep, err := _i.ApprovalWorkflowStepsRepository.FindByWorkflowAndStep(clientId, flow.WorkflowId, flow.CurrentStep) if err != nil { return err } // Create step log stepLog := &entity.ArticleApprovalStepLogs{ ApprovalFlowId: flow.ID, StepOrder: flow.CurrentStep, StepName: currentStep.StepName, ApprovedById: &rejectedById, Action: "reject", Message: &reason, ProcessedAt: time.Now(), UserLevelId: currentStep.RequiredUserLevelId, } _, err = _i.ArticleApprovalStepLogsRepository.Create(clientId, stepLog) if err != nil { return err } // Update approval flow status flowUpdate := &entity.ArticleApprovalFlows{ StatusId: 3, // rejected RejectionReason: &reason, CompletedAt: &[]time.Time{time.Now()}[0], } err = _i.ArticleApprovalFlowsRepository.Update(flowId, flowUpdate) if err != nil { return err } // Get current article data first currentArticle, err := _i.ArticlesRepository.FindOne(clientId, flow.ArticleId) if err != nil { return err } // Update only the necessary fields currentArticle.StatusId = &[]int{3}[0] // rejected currentArticle.CurrentApprovalStep = nil err = _i.ArticlesRepository.UpdateSkipNull(clientId, flow.ArticleId, currentArticle) if err != nil { return err } return nil } func (_i *articleApprovalFlowsService) RequestRevision(clientId *uuid.UUID, flowId uint, requestedById uint, revisionMessage string) (err error) { // Get approval flow flow, err := _i.ArticleApprovalFlowsRepository.FindOne(clientId, flowId) if err != nil { return err } if flow == nil { return errors.New("approval flow not found") } if flow.StatusId != 1 { // not pending return errors.New("approval flow is not in pending state") } // Get current step currentStep, err := _i.ApprovalWorkflowStepsRepository.FindByWorkflowAndStep(clientId, flow.WorkflowId, flow.CurrentStep) if err != nil { return err } // Create step log stepLog := &entity.ArticleApprovalStepLogs{ ApprovalFlowId: flow.ID, StepOrder: flow.CurrentStep, StepName: currentStep.StepName, ApprovedById: &requestedById, Action: "request_revision", Message: &revisionMessage, ProcessedAt: time.Now(), UserLevelId: currentStep.RequiredUserLevelId, } _, err = _i.ArticleApprovalStepLogsRepository.Create(clientId, stepLog) if err != nil { return err } // Update approval flow status flowUpdate := &entity.ArticleApprovalFlows{ StatusId: 4, // revision_requested RevisionRequested: &[]bool{true}[0], RevisionMessage: &revisionMessage, } err = _i.ArticleApprovalFlowsRepository.Update(flowId, flowUpdate) if err != nil { return err } // Get current article data first currentArticle, err := _i.ArticlesRepository.FindOne(clientId, flow.ArticleId) if err != nil { return err } // Update only the necessary fields currentArticle.StatusId = &[]int{4}[0] // revision_requested err = _i.ArticlesRepository.UpdateSkipNull(clientId, flow.ArticleId, currentArticle) if err != nil { return err } return nil } func (_i *articleApprovalFlowsService) ResubmitAfterRevision(clientId *uuid.UUID, flowId uint, resubmittedById uint) (err error) { // Get approval flow flow, err := _i.ArticleApprovalFlowsRepository.FindOne(clientId, flowId) if err != nil { return err } if flow == nil { return errors.New("approval flow not found") } if flow.StatusId != 4 { // not revision_requested return errors.New("approval flow is not in revision requested state") } // Reset approval flow to pending flowUpdate := &entity.ArticleApprovalFlows{ StatusId: 1, // pending RevisionRequested: &[]bool{false}[0], RevisionMessage: nil, CurrentStep: 1, // restart from first step } err = _i.ArticleApprovalFlowsRepository.Update(flowId, flowUpdate) if err != nil { return err } // Get current article data first currentArticle, err := _i.ArticlesRepository.FindOne(clientId, flow.ArticleId) if err != nil { return err } // Update only the necessary fields currentArticle.StatusId = &[]int{1}[0] // pending approval currentArticle.CurrentApprovalStep = &[]int{1}[0] err = _i.ArticlesRepository.UpdateSkipNull(clientId, flow.ArticleId, currentArticle) if err != nil { return err } // Create resubmission log stepLog := &entity.ArticleApprovalStepLogs{ ApprovalFlowId: flow.ID, StepOrder: 1, StepName: "Resubmission", ApprovedById: &resubmittedById, Action: "resubmit", Message: &[]string{"Article resubmitted after revision"}[0], ProcessedAt: time.Now(), } _, err = _i.ArticleApprovalStepLogsRepository.Create(clientId, stepLog) if err != nil { return err } return nil } // Dashboard and queue methods func (_i *articleApprovalFlowsService) GetPendingApprovals(clientId *uuid.UUID, userLevelId uint, page, limit int, filters map[string]interface{}) (flows []*entity.ArticleApprovalFlows, paging paginator.Pagination, err error) { return _i.ArticleApprovalFlowsRepository.GetPendingApprovals(clientId, userLevelId, page, limit, filters) } func (_i *articleApprovalFlowsService) GetMyApprovalQueue(clientId *uuid.UUID, userLevelId uint, page, limit int, includePreview bool, urgentOnly bool) (flows []*entity.ArticleApprovalFlows, paging paginator.Pagination, err error) { return _i.ArticleApprovalFlowsRepository.GetMyApprovalQueue(clientId, userLevelId, page, limit, includePreview, urgentOnly) } func (_i *articleApprovalFlowsService) GetApprovalHistory(clientId *uuid.UUID, articleId uint, page, limit int) (logs []*entity.ArticleApprovalStepLogs, paging paginator.Pagination, err error) { return _i.ArticleApprovalStepLogsRepository.GetApprovalHistory(clientId, articleId, page, limit) } // Statistics and analytics func (_i *articleApprovalFlowsService) GetPendingCountByLevel(clientId *uuid.UUID, userLevelId uint) (count int64, err error) { return _i.ArticleApprovalFlowsRepository.GetPendingCountByLevel(clientId, userLevelId) } func (_i *articleApprovalFlowsService) GetOverdueCountByLevel(clientId *uuid.UUID, userLevelId uint) (count int64, err error) { return _i.ArticleApprovalFlowsRepository.GetOverdueCountByLevel(clientId, userLevelId) } func (_i *articleApprovalFlowsService) GetApprovalStatistics(clientId *uuid.UUID, userLevelId uint, startDate, endDate time.Time) (stats map[string]interface{}, err error) { stats = make(map[string]interface{}) // Get approved count approvedCount, err := _i.ArticleApprovalFlowsRepository.GetApprovedCountByPeriod(clientId, userLevelId, startDate, endDate) if err != nil { return nil, err } // Get rejected count rejectedCount, err := _i.ArticleApprovalFlowsRepository.GetRejectedCountByPeriod(clientId, userLevelId, startDate, endDate) if err != nil { return nil, err } // Get revision request count revisionCount, err := _i.ArticleApprovalFlowsRepository.GetRevisionRequestCountByPeriod(clientId, userLevelId, startDate, endDate) if err != nil { return nil, err } stats["approved_count"] = approvedCount stats["rejected_count"] = rejectedCount stats["revision_requested_count"] = revisionCount stats["total_processed"] = approvedCount + rejectedCount + revisionCount stats["period_start"] = startDate stats["period_end"] = endDate return stats, nil } func (_i *articleApprovalFlowsService) GetWorkloadAnalytics(clientId *uuid.UUID, userLevelId uint) (analytics map[string]interface{}, err error) { return _i.ArticleApprovalFlowsRepository.GetLevelWorkload(clientId, userLevelId) } // Workflow management func (_i *articleApprovalFlowsService) CanUserApproveStep(clientId *uuid.UUID, flowId uint, userId uint, userLevelId uint) (canApprove bool, reason string, err error) { // Get approval flow flow, err := _i.ArticleApprovalFlowsRepository.FindOne(clientId, flowId) if err != nil { return false, "", err } if flow == nil { return false, "approval flow not found", nil } if flow.StatusId != 1 && flow.StatusId != 4 { // not pending or revision_requested return false, "approval flow is not in pending state", nil } // Get current step currentStep, err := _i.ApprovalWorkflowStepsRepository.FindByWorkflowAndStep(clientId, flow.WorkflowId, flow.CurrentStep) if err != nil { return false, "", err } if currentStep == nil { return false, "current step not found", nil } // Check if user level matches required level if currentStep.RequiredUserLevelId != userLevelId { return false, "user level does not match required level for this step", nil } // Check if user submitted the article (cannot approve own submission) if flow.SubmittedById == userId { return false, "cannot approve own submission", nil } return true, "", nil } func (_i *articleApprovalFlowsService) GetCurrentStepInfo(clientId *uuid.UUID, flowId uint) (stepInfo map[string]interface{}, err error) { stepInfo = make(map[string]interface{}) // Get approval flow flow, err := _i.ArticleApprovalFlowsRepository.FindOne(clientId, flowId) if err != nil { return nil, err } if flow == nil { return nil, errors.New("approval flow not found") } // Get current step currentStep, err := _i.ApprovalWorkflowStepsRepository.FindByWorkflowAndStep(clientId, flow.WorkflowId, flow.CurrentStep) if err != nil { return nil, err } stepInfo["current_step"] = flow.CurrentStep stepInfo["step_name"] = currentStep.StepName stepInfo["required_user_level_id"] = currentStep.RequiredUserLevelId stepInfo["can_skip"] = currentStep.CanSkip stepInfo["auto_approve_after_hours"] = currentStep.AutoApproveAfterHours stepInfo["status"] = flow.StatusId return stepInfo, nil } func (_i *articleApprovalFlowsService) GetNextStepPreview(clientId *uuid.UUID, flowId uint) (nextStep *entity.ApprovalWorkflowSteps, err error) { // Get approval flow flow, err := _i.ArticleApprovalFlowsRepository.FindOne(clientId, flowId) if err != nil { return nil, err } if flow == nil { return nil, errors.New("approval flow not found") } // Get next step nextStep, err = _i.ApprovalWorkflowStepsRepository.GetNextStep(clientId, flow.WorkflowId, flow.CurrentStep) if err != nil { return nil, err } return nextStep, nil }