package processor import ( "context" "fmt" "log/slog" "sync" "bj_power_mes/constants" "bj_power_mes/ent" "bj_power_mes/ent/recipestep" ) // RecipeLoader 工艺路线加载器(按 productTypeId 缓存) type RecipeLoader struct { client *ent.Client cache map[int]*RecipeRuntime // productTypeId → RecipeRuntime mu sync.RWMutex } // NewRecipeLoader 创建加载器 func NewRecipeLoader(client *ent.Client) *RecipeLoader { return &RecipeLoader{ client: client, cache: make(map[int]*RecipeRuntime), } } // Load 按 productTypeId 加载工艺路线 func (rl *RecipeLoader) Load(ctx context.Context, productTypeID int) (*RecipeRuntime, error) { rl.mu.RLock() if rr, ok := rl.cache[productTypeID]; ok { rl.mu.RUnlock() return rr, nil } rl.mu.RUnlock() // 查询 productType 关联的 recipe pt, err := rl.client.ProductType.Get(ctx, productTypeID) if err != nil { return nil, fmt.Errorf("get product type %d: %w", productTypeID, err) } recipeID := pt.RecipeId if recipeID == nil || *recipeID == 0 { return nil, fmt.Errorf("product type %d has no recipe", productTypeID) } // 查询 recipe + steps r, err := rl.client.Recipe.Get(ctx, *recipeID) if err != nil { return nil, fmt.Errorf("get recipe %d: %w", recipeID, err) } steps, err := r.QuerySteps(). Order(ent.Asc(recipestep.FieldStepIndex)). All(ctx) if err != nil { return nil, fmt.Errorf("query recipe %d steps: %w", recipeID, err) } // 构建 RecipeRuntime stepRuntimes := make([]StepRuntime, 0, len(steps)) for _, s := range steps { sr := StepRuntime{ StepID: s.ID, StepIdStr: s.StepId, StepIndex: s.StepIndex, StepName: s.StepName, StepType: constants.StepType(s.StepType), ResourceType: s.ResourceType, ToolType: s.ToolType, AllowedResources: s.AllowedResources, ProcessingParams: s.ProcessingParams, NextStepDefault: s.NextStepDefault, RestoreStep: s.RestoreStep, StepTimeout: s.StepTimeout, } if s.NextStepBranches != nil { sr.NextStepBranches = make(map[string]string, len(s.NextStepBranches)) for k, v := range s.NextStepBranches { if vs, ok := v.(string); ok { sr.NextStepBranches[k] = vs } } } stepRuntimes = append(stepRuntimes, sr) } rr := NewRecipeRuntime(r.ID, r.Code, stepRuntimes) // 缓存 rl.mu.Lock() rl.cache[productTypeID] = rr rl.mu.Unlock() slog.Info("recipe loader: loaded", "productTypeId", productTypeID, "recipeId", *recipeID, "steps", len(stepRuntimes)) return rr, nil } // Invalidate 使缓存失效 func (rl *RecipeLoader) Invalidate(productTypeID int) { rl.mu.Lock() defer rl.mu.Unlock() delete(rl.cache, productTypeID) }