118 lines
3.1 KiB
Go
118 lines
3.1 KiB
Go
|
|
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
|
||
|
|
}
|