package logic import ( "context" "errors" "fmt" "strings" "bj_power_mes/ent" "bj_power_mes/ent/bomitem" "bj_power_mes/ent/producttype" "bj_power_mes/ent/workorder" "bj_power_mes/internal/wmsclient" ) // 产品型号(成品档案):编码主数据单一来源 = WMS(materials item_type=3)。 // MES 本页改为实时代理 WMS;WMS 不可达时降级读本地 product_types 只读缓存(仅查询, // 写操作必须 WMS 可用),前端以 wmsOnline 标识提示。 type ProductTypeReq struct { Id int `json:"id"` Name string `json:"name"` Code string `json:"code"` Category string `json:"category"` Remark string `json:"remark"` IsActive bool `json:"isActive"` } func (s *Service) CreateProductType(ctx context.Context, req ProductTypeReq) error { active := req.IsActive if !req.IsActive { active = true } if err := s.ctx.Wms.CreateProductType(ctx, req.Code, req.Name, req.Category, req.Remark, active); err != nil { return errors.New("WMS 不可达或拒绝写入(编码主数据以 WMS 为单一来源):" + err.Error()) } return nil } func (s *Service) UpdateProductType(ctx context.Context, req ProductTypeReq) error { if req.Id == 0 { return errors.New("缺少 id") } if err := s.ctx.Wms.UpdateProductType(ctx, req.Id, req.Name, req.Category, req.Remark, req.IsActive); err != nil { return errors.New("WMS 不可达或拒绝写入:" + err.Error()) } return nil } // DeleteProductType 删除保护:被工单/BOM 引用过的型号禁删(MES 侧引用校验, // WMS 侧库存引用由 WMS 内部接口再校验一层) func (s *Service) DeleteProductType(ctx context.Context, id int) error { row, err := s.productTypeByID(ctx, id) if err != nil { return err } woCnt, _ := s.ctx.EntClient.WorkOrder.Query().Where(workorder.ProductCode(row.Code)).Count(ctx) if woCnt > 0 { return fmt.Errorf("该产品型号已被 %d 张工单引用,不可删除;建议停用(下架)", woCnt) } bomCnt, _ := s.ctx.EntClient.BomItem.Query().Where(bomitem.ProductCode(row.Code)).Count(ctx) if bomCnt > 0 { return fmt.Errorf("该产品型号在物料清单中有 %d 条配置,不可删除;建议停用(下架)", bomCnt) } if err := s.ctx.Wms.DeleteProductType(ctx, id); err != nil { return errors.New("WMS 不可达或拒绝删除:" + err.Error()) } return nil } // ListProductTypes 全量列表(下拉/兼容形态,id asc 新建在底部)。 // degraded=false 表示 WMS 不可达、数据来自本地只读缓存。 func (s *Service) ListProductTypes(ctx context.Context) ([]wmsclient.ProductTypeRow, bool, error) { list, err := s.ctx.Wms.ProductTypes(ctx, "") if err == nil { return list, true, nil } fallback, ferr := s.localProductTypes(ctx, "", "", "") if ferr != nil { return nil, false, errors.New("WMS 不可达且本地缓存读取失败") } return fallback, false, nil } // ListProductTypesPage 分页+搜索(管理页真分页)。 // 2026-09-08 禁止关键字混搜:编码/名称/类别各自独立模糊。 func (s *Service) ListProductTypesPage(ctx context.Context, code, name, category, isActive string, page, pageSize int) (int64, []wmsclient.ProductTypeRow, bool, error) { total, list, err := s.ctx.Wms.ProductTypePage(ctx, code, name, category, isActive, page, pageSize) if err == nil { return total, list, true, nil } fallback, ferr := s.localProductTypes(ctx, code, name, category) if ferr != nil { return 0, nil, false, errors.New("WMS 不可达且本地缓存读取失败") } start := (page - 1) * pageSize if start > len(fallback) { start = len(fallback) } end := start + pageSize if end > len(fallback) { end = len(fallback) } return int64(len(fallback)), fallback[start:end], false, nil } func (s *Service) productTypeByID(ctx context.Context, id int) (*wmsclient.ProductTypeRow, error) { list, err := s.ctx.Wms.ProductTypes(ctx, "") if err == nil { for i := range list { if list[i].ID == id { return &list[i], nil } } return nil, errors.New("产品型号不存在") } // 降级:本地缓存 pt, err := s.ctx.EntClient.ProductType.Get(ctx, id) if err != nil { return nil, errors.New("产品型号不存在(WMS 不可达,无法校验引用)") } return &wmsclient.ProductTypeRow{ID: pt.ID, Code: pt.Code, Name: pt.Name}, nil } // localProductTypes 本地只读缓存过滤(降级用):编码/名称/类别各自独立模糊匹配 func (s *Service) localProductTypes(ctx context.Context, code, name, category string) ([]wmsclient.ProductTypeRow, error) { list, err := s.ctx.EntClient.ProductType.Query().Order(ent.Asc(producttype.FieldID)).All(ctx) if err != nil { return nil, err } codeKw := strings.ToLower(strings.TrimSpace(code)) nameKw := strings.ToLower(strings.TrimSpace(name)) catKw := strings.ToLower(strings.TrimSpace(category)) rows := make([]wmsclient.ProductTypeRow, 0, len(list)) for _, p := range list { if codeKw != "" && !strings.Contains(strings.ToLower(p.Code), codeKw) { continue } if nameKw != "" && !strings.Contains(strings.ToLower(p.Name), nameKw) { continue } if catKw != "" && !strings.Contains(strings.ToLower(p.Category), catKw) { continue } rows = append(rows, wmsclient.ProductTypeRow{ ID: p.ID, Code: p.Code, Name: p.Name, Category: p.Category, Remark: p.Remark, IsActive: p.IsActive, CreatedAt: p.CreatedAt.Unix(), }) } return rows, nil }