package service import ( "context" "errors" "fmt" "time" "github.com/muyuqingfeng/iloom/textile-service/internal/model" "github.com/muyuqingfeng/iloom/textile-service/internal/repository" ) type ProcessService struct { stepRepo *repository.StepRepo inventoryRepo *repository.InventoryRepo } func NewProcessService(stepRepo *repository.StepRepo, inventoryRepo *repository.InventoryRepo) *ProcessService { return &ProcessService{ stepRepo: stepRepo, inventoryRepo: inventoryRepo, } } func (s *ProcessService) CompleteStep(ctx context.Context, planID, stepID, companyID, userID, notes string) error { step, err := s.stepRepo.GetByID(ctx, stepID) if err != nil { return fmt.Errorf("step not found: %w", err) } if step.PlanID != planID { return errors.New("step does not belong to this plan") } if step.Status != "pending" && step.Status != "active" { return fmt.Errorf("step status is %s, cannot complete", step.Status) } // Validate prerequisite steps are completed allSteps, err := s.stepRepo.GetByPlan(ctx, planID) if err != nil { return fmt.Errorf("failed to get plan steps: %w", err) } if err := s.validatePrerequisites(step.StepType, allSteps); err != nil { return err } var notePtr *string if notes != "" { notePtr = ¬es } if err := s.stepRepo.UpdateStatus(ctx, stepID, "completed", userID, notePtr); err != nil { return fmt.Errorf("failed to update step: %w", err) } // Check if this is the last step (fabric_warehouse) if step.StepType == "fabric_warehouse" { // All steps completed - plan production is done at textile side // Purchaser service can be notified via event/HTTP if needed _ = companyID // available for future cross-service notification } return nil } func (s *ProcessService) RejectStep(ctx context.Context, planID, stepID, companyID, userID, reason string) error { step, err := s.stepRepo.GetByID(ctx, stepID) if err != nil { return fmt.Errorf("step not found: %w", err) } if step.PlanID != planID { return errors.New("step does not belong to this plan") } if step.Status != "pending" && step.Status != "active" { return fmt.Errorf("step status is %s, cannot reject", step.Status) } return s.stepRepo.UpdateStatus(ctx, stepID, "rejected", userID, &reason) } func (s *ProcessService) validatePrerequisites(currentType string, allSteps []model.ProcessStep) error { currentIdx := -1 for i, st := range model.StepOrder { if st == currentType { currentIdx = i break } } if currentIdx < 0 { return fmt.Errorf("unknown step type: %s", currentType) } // Build a map of step type -> status statusMap := make(map[string]string, len(allSteps)) for _, step := range allSteps { statusMap[step.StepType] = step.Status } // All preceding steps must be completed for i := 0; i < currentIdx; i++ { prevType := model.StepOrder[i] if statusMap[prevType] != "completed" { return fmt.Errorf("prerequisite step %s is not completed (status: %s)", prevType, statusMap[prevType]) } } return nil } // StepTimestamp returns the completion timestamp for a step, if available. func StepTimestamp(step *model.ProcessStep) *time.Time { return step.Timestamp }