package backend import ( "bytes" "context" "encoding/json" "errors" "fmt" "io" "kpt-tmr-group/model" "kpt-tmr-group/pkg/logger/zaplog" "kpt-tmr-group/pkg/xerr" operationPb "kpt-tmr-group/proto/go/backend/operation" "net/http" "strconv" "sync" "time" "go.uber.org/multierr" "github.com/xuri/excelize/v2" "go.uber.org/zap" "gorm.io/gorm" ) const EncodeNumberPrefix = "encode_number" var PastureDataLogType = map[string]int32{ "FeedFormula_Distribute": 1, } // CreateFeedFormula 添加数据 func (s *StoreEntry) CreateFeedFormula(ctx context.Context, req *operationPb.AddFeedFormulaRequest) error { forage := model.NewFeedFormula(req) if err := s.DB.Create(forage).Error; err != nil { return xerr.WithStack(err) } return nil } // EditFeedFormula 编辑数据 func (s *StoreEntry) EditFeedFormula(ctx context.Context, req *operationPb.AddFeedFormulaRequest) error { forage := model.FeedFormula{Id: int64(req.Id)} if err := s.DB.Where("is_delete = ?", operationPb.IsShow_OK).First(&forage).Error; err != nil { if errors.Is(err, gorm.ErrRecordNotFound) { return xerr.Custom("该数据不存在") } return xerr.WithStack(err) } updateData := &model.FeedFormula{ Name: req.Name, Colour: req.Colour, CattleCategoryId: req.CattleCategoryId, CattleCategoryName: req.CattleCategoryName, FormulaTypeId: req.FormulaTypeId, FormulaTypeName: req.FormulaTypeName, DataSourceId: req.DataSourceId, DataSourceName: req.DataSourceName, Remarks: req.Remarks, IsShow: req.IsShow, } if err := s.DB.Model(new(model.FeedFormula)). Omit("is_show", "is_delete", "encode_number", "formula_type_id", "formula_type_name", "data_source", "version", "is_modify"). Where("id = ?", req.Id). Updates(updateData).Error; err != nil { return xerr.WithStack(err) } return nil } // SearchFeedFormulaList 查询数据列表 func (s *StoreEntry) SearchFeedFormulaList(ctx context.Context, req *operationPb.SearchFeedFormulaRequest) (*operationPb.SearchFeedFormulaListResponse, error) { feedFormula := make([]*model.FeedFormula, 0) var count int64 = 0 pref := s.DB.Model(new(model.FeedFormula)).Where("is_delete = ?", operationPb.IsShow_OK) if req.Name != "" { pref.Where("name like ?", fmt.Sprintf("%s%s%s", "%", req.Name, "%")) } if req.CattleCategoryId > 0 { pref.Where("cattle_category_id = ?", req.CattleCategoryId) } if req.FormulaTypeId > 0 { pref.Where("formula_type_id = ?", req.FormulaTypeId) } if req.IsShow > 0 { pref.Where("is_show = ?", req.IsShow) } if req.DataSource > 0 { pref.Where("data_source = ?", req.DataSource) } if req.Remarks != "" { pref.Where("remarks = ?", req.Remarks) } if err := pref.Order("id desc").Count(&count).Limit(int(req.Pagination.PageSize)).Offset(int(req.Pagination.PageOffset)). Find(&feedFormula).Error; err != nil { return nil, xerr.WithStack(err) } return &operationPb.SearchFeedFormulaListResponse{ Code: http.StatusOK, Msg: "ok", Data: &operationPb.SearchFeedFormulaListData{ Page: req.Pagination.Page, PageSize: req.Pagination.PageSize, Total: int32(count), List: model.FeedFormulaSlice(feedFormula).ToPB(), }, }, nil } // IsShowFeedFormula 是否启用和是否可修改 func (s *StoreEntry) IsShowFeedFormula(ctx context.Context, req *operationPb.IsShowModifyFeedFormula) error { feedFormula := &model.FeedFormula{Id: int64(req.FeedFormulaId)} if err := s.DB.First(feedFormula).Error; err != nil { if errors.Is(err, gorm.ErrRecordNotFound) { return xerr.Custom("该数据不存在") } return xerr.WithStack(err) } if req.EditType == 1 { if err := s.DB.Model(new(model.FeedFormula)).Where("id = ?", req.FeedFormulaId).Update("is_show", req.IsShow).Error; err != nil { return xerr.WithStack(err) } } if req.EditType == 2 { if err := s.DB.Model(new(model.FeedFormula)).Where("id = ?", req.FeedFormulaId).Update("is_modify", req.IsShow).Error; err != nil { return xerr.WithStack(err) } } return nil } // DeleteFeedFormula 是否删除 func (s *StoreEntry) DeleteFeedFormula(ctx context.Context, feedFormulaId int64) error { feedFormula := &model.FeedFormula{Id: feedFormulaId} if err := s.DB.First(feedFormula).Error; err != nil { if errors.Is(err, gorm.ErrRecordNotFound) { return xerr.Custom("该数据不存在") } return xerr.WithStack(err) } if err := s.DB.Model(new(model.FeedFormula)).Where("id = ?", feedFormula.Id).Update("is_delete", operationPb.IsShow_NO).Error; err != nil { return xerr.WithStack(err) } return nil } // ExcelImportFeedFormula 导入excel func (s *StoreEntry) ExcelImportFeedFormula(ctx context.Context, req io.Reader) error { xlsx, err := excelize.OpenReader(req) if err != nil { return xerr.WithStack(err) } defer xlsx.Close() rows, err := xlsx.GetRows(xlsx.GetSheetName(xlsx.GetActiveSheetIndex())) if err != nil { return xerr.WithStack(err) } if len(rows) > 10000 { rows = rows[:10000] } feedFormulaList := make([]*model.FeedFormula, 0) for i, row := range rows { if i == 0 { continue } var ( name, encodeNumber, cattleCategoryName, formulaTypeName, dataSourceName, remarks, isShowStr string isShow operationPb.IsShow_Kind ) for k, v := range row { if k == 0 { name = v } if k == 1 { encodeNumber = v } if k == 2 { cattleCategoryName = v } if k == 3 { formulaTypeName = v } if k == 4 { dataSourceName = v } if k == 5 { remarks = v } if k == 6 { isShowStr = v } } if isShowStr == "是" { isShow = operationPb.IsShow_OK } else { isShow = operationPb.IsShow_NO } feedFormulaItem := &model.FeedFormula{ Name: name, EncodeNumber: encodeNumber, CattleCategoryName: cattleCategoryName, FormulaTypeName: formulaTypeName, Remarks: remarks, IsShow: isShow, IsDelete: operationPb.IsShow_OK, DataSourceId: operationPb.DataSource_EXCEL_IMPORT, DataSourceName: dataSourceName, } feedFormulaList = append(feedFormulaList, feedFormulaItem) } if len(feedFormulaList) > 0 { if err = s.DB.Create(feedFormulaList).Error; err != nil { return xerr.WithStack(err) } } return nil } // ExcelExportFeedFormula 流式导出excel func (s *StoreEntry) ExcelExportFeedFormula(ctx context.Context, req *operationPb.SearchFeedFormulaRequest) (*bytes.Buffer, error) { res, err := s.SearchFeedFormulaList(ctx, req) if err != nil { return nil, xerr.WithStack(err) } if len(res.Data.List) <= 0 { return nil, xerr.Custom("数据为空") } file := excelize.NewFile() defer file.Close() streamWriter, err := file.NewStreamWriter("Sheet1") if err != nil { return nil, xerr.WithStack(err) } titles := []interface{}{"配方名称", "配方编码", "畜牧类别", "配方类别", "来源", "备注", "是否启用", "饲料组", "饲料名称", "重量(kg)", "搅拌延迟(min)", "是否锁定牛头数比例", "顺序"} if err = streamWriter.SetRow("A1", titles); err != nil { return nil, xerr.WithStack(err) } for i, item := range res.Data.List { cell, err := excelize.CoordinatesToCellName(1, i+2) if err != nil { zaplog.Error("excelize.CoordinatesToCellName", zap.Any("Err", err)) continue } row := make([]interface{}, 0) row = append(row, item.Name, item.EncodeNumber, item.CattleCategoryName, item.FormulaTypeName, item.DataSourceName, item.Remarks, item.IsShow) if err = streamWriter.SetRow(cell, row); err != nil { return nil, xerr.WithStack(err) } } if err = streamWriter.Flush(); err != nil { return nil, xerr.WithStack(err) } return file.WriteToBuffer() } // ExcelTemplateFeedFormula 导出模板 func (s *StoreEntry) ExcelTemplateFeedFormula(ctx context.Context) (*bytes.Buffer, error) { file := excelize.NewFile() defer file.Close() streamWriter, err := file.NewStreamWriter("Sheet1") if err != nil { return nil, xerr.WithStack(err) } titles := []interface{}{"配方名称", "配方编码", "畜牧类别", "配方类别", "来源", "备注", "是否启用", "饲料组", "饲料名称", "重量(kg)", "搅拌延迟(min)", "是否锁定牛头数比例", "顺序"} if err = streamWriter.SetRow("A1", titles); err != nil { return nil, xerr.WithStack(err) } if err = streamWriter.Flush(); err != nil { return nil, xerr.WithStack(err) } return file.WriteToBuffer() } // EncodeNumber 配方编码 func (s *StoreEntry) EncodeNumber(ctx context.Context) string { currTime := time.Now().Format(model.LayoutDate) prefix := fmt.Sprintf("%s_%s", EncodeNumberPrefix, currTime) data := &model.UniqueData{} if err := s.DB.Order("id desc").Where("prefix = ?", prefix).First(data).Error; err != nil { if !errors.Is(err, gorm.ErrRecordNotFound) { return "" } ud, _ := strconv.Atoi(currTime) result := ud*100 + 1 newData := &model.UniqueData{ Prefix: prefix, Data: int64(result), } if err = s.DB.Create(newData).Error; err != nil { zaplog.Error("EncodeNumber Create", zap.Any("data", newData), zap.Any("Err", err)) return "" } return fmt.Sprintf("%d", newData.Data) } data.Data += 1 if err := s.DB.Model(new(model.UniqueData)).Where("prefix = ?", prefix).Update("data", data.Data).Error; err != nil { return "" } else { return fmt.Sprintf("%d", data.Data) } } // DistributeFeedFormula 配方下发牧场 func (s *StoreEntry) DistributeFeedFormula(ctx context.Context, req *operationPb.DistributeFeedFormulaRequest) error { distributeData, err := s.checkoutDistributeData(ctx, req) if err != nil { return xerr.WithStack(err) } if len(distributeData.PastureList) <= 0 { return nil } feedFormulaList := make([]*model.FeedFormula, 0) if err = s.DB.Where("id IN ?", req.FeedFormulaIds).Find(&feedFormulaList).Error; err != nil { return xerr.WithStack(err) } wg := sync.WaitGroup{} wg.Add(len(distributeData.PastureList)) var muError error for _, pasture := range distributeData.PastureList { go func(p *model.GroupPasture) { response := &model.PastureResponse{} body := &model.DistributeFeedFormulaRequest{ PastureId: p.Id, Body: feedFormulaList, } if err = s.PastureHttpClient(ctx, FeedFormulaDistributeUrl, p.Id, body, response); err != nil { muError = multierr.Append(muError, err) zaplog.Error("DistributeFeedFormula", zap.Any("pasture", p), zap.Any("body", feedFormulaList), zap.Any("err", err), zap.Any("response", response)) b, _ := json.Marshal(body) res, _ := json.Marshal(response) pastureDataLog := model.NewPastureDataLog(p.Id, PastureDataLogType["FeedFormula_Distribute"], FeedFormulaDistributeUrl, string(b), string(res)) s.DB.Create(pastureDataLog) } if response.Code != http.StatusOK { muError = multierr.Append(muError, xerr.Custom(response.Msg)) } wg.Done() }(pasture) } wg.Wait() return muError } func (s *StoreEntry) checkoutDistributeData(ctx context.Context, req *operationPb.DistributeFeedFormulaRequest) (*model.DistributeData, error) { result := &model.DistributeData{ PastureList: make([]*model.GroupPasture, 0), FeedFormulaList: make([]*model.FeedFormula, 0), } if err := s.DB.Where("id IN ?", req.PastureIds).Where("is_delete = ?", operationPb.IsShow_OK).Find(&result.PastureList).Error; err != nil { return result, xerr.WithStack(err) } if err := s.DB.Where("id IN ?", req.FeedFormulaIds).Find(&result.FeedFormulaList).Error; err != nil { return result, xerr.WithStack(err) } if len(result.PastureList) <= 0 || len(result.FeedFormulaList) <= 0 { return result, xerr.Customf("数据错误") } return result, nil }