package backend import ( "bytes" "context" "encoding/json" "errors" "fmt" "io" "kpt-tmr-group/model" "net/http" "strconv" "sync" "time" operationPb "gitee.com/xuyiping_admin/go_proto/proto/go/backend/operation" "gitee.com/xuyiping_admin/pkg/logger/zaplog" "gitee.com/xuyiping_admin/pkg/xerr" "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, "FeedFormula_IsModify": 2, "FeedFormula_Cancel_Distribute": 3, } var EditRecodeMap = map[string]string{ "forage_name": "饲料名称", "weight": "重量", "stir_delay": "搅拌延迟", "allow_error": "允许误差", "sort": "排序", } // 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 { feedFormula := &model.FeedFormula{Id: int64(req.Id)} if err := s.DB.Where("is_delete = ?", operationPb.IsShow_OK).First(feedFormula).Error; err != nil { if errors.Is(err, gorm.ErrRecordNotFound) { return xerr.Custom("该数据不存在") } return xerr.WithStack(err) } // 更新版本号 defer s.UpdateFeedFormalVersion(ctx, feedFormula) if err := s.DB.Model(new(model.FeedFormula)). Omit("is_show", "is_delete", "encode_number", "formula_type_id", "formula_type_name", "data_source", "is_modify"). Where("id = ?", req.Id). Updates(map[string]interface{}{ "name": req.Name, "colour": req.Colour, "cattle_category_id": req.CattleCategoryId, "cattle_category_name": req.CattleCategoryName, "data_source_id": req.DataSourceId, "data_source_name": req.DataSourceName, "remarks": req.Remarks, }).Error; err != nil { return xerr.WithStack(err) } return nil } // AddFeedByFeedFormula 配方添加饲料 func (s *StoreEntry) AddFeedByFeedFormula(ctx context.Context, req *operationPb.GroupAddFeedFormulaDetail) error { feedFormulaData := &model.FeedFormula{Id: int64(req.FeedFormulaId)} if err := s.DB.Model(new(model.FeedFormula)).First(feedFormulaData).Error; err != nil { return xerr.WithStack(err) } // 更新修改记录 defer s.addFeedFormulaDetailAddRecode(ctx, req) // 更新版本号 defer s.UpdateFeedFormalVersion(ctx, feedFormulaData) insertData := make([]*model.FeedFormulaDetail, 0) for _, v := range req.List { feedData := &model.Forage{Id: int64(v.ForageId)} if err := s.DB.Model(new(model.Forage)).First(feedData).Error; err != nil { return xerr.WithStack(err) } if v.AllowError > v.StirDelay { return xerr.Customf("允许误差不能大于搅拌延迟") } insertData = append(insertData, &model.FeedFormulaDetail{ PastureName: "集团", FeedFormulaId: int64(req.FeedFormulaId), ForageId: int64(v.ForageId), ForageName: v.ForageName, ForageGroupName: v.ForageGroupName, Weight: int32(v.Weight * 100), StirDelay: v.StirDelay, AllowError: v.AllowError, IsShow: operationPb.IsShow_OK, IsModify: v.IsModify, Sort: v.Sort, }) } if err := s.DB.Model(new(model.FeedFormulaDetail)).Save(insertData).Error; err != nil { return xerr.WithStack(err) } return nil } // addFeedFormulaDetailAddRecode 添加配方记录 func (s *StoreEntry) addFeedFormulaDetailAddRecode(ctx context.Context, req *operationPb.GroupAddFeedFormulaDetail) { editRecord, _ := s.GetEditRecordLastGroupId(ctx) editRecordList := make([]*model.FeedFormulaEditRecord, 0) operationName := s.GetCurrentUserName(ctx) for _, v := range req.List { editRecordList = append(editRecordList, &model.FeedFormulaEditRecord{ FeedFormulaId: int64(req.FeedFormulaId), PastureName: "集团", ForageName: v.ForageName, Status: operationPb.FeedFormulaEditRecordType_INSERT, GroupId: editRecord.GroupId + 1, OperationName: operationName, }) } if err := s.CreateFeedFormulaEditRecord(ctx, editRecordList); err != nil { zaplog.Error("deleteFeedFormulaDetailAddRecode", zap.Any("CreateFeedFormulaEditRecord", err)) } } // EditFeedByFeedFormula 配方饲料编辑 func (s *StoreEntry) EditFeedByFeedFormula(ctx context.Context, req *operationPb.AddFeedFormulaDetail) error { feedFormulaData := &model.FeedFormula{Id: int64(req.FeedFormulaId)} if err := s.DB.Model(new(model.FeedFormula)).First(feedFormulaData).Error; err != nil { return xerr.WithStack(err) } feedFormulaDetail := &model.FeedFormulaDetail{Id: int64(req.Id)} if err := s.DB.Model(new(model.FeedFormulaDetail)). Where("is_show = ?", operationPb.IsShow_OK). First(feedFormulaDetail).Error; err != nil { return xerr.WithStack(err) } // 添加修改记录 defer s.editFeedFormulaDetailAddRecode(ctx, req, feedFormulaDetail) // 更新版本号 defer s.UpdateFeedFormalVersion(ctx, feedFormulaData) // 更新数据 updateData := &model.FeedFormulaDetail{ ForageId: int64(req.ForageId), ForageName: req.ForageName, ForageGroupName: req.ForageGroupName, Weight: int32(req.Weight * 100), StirDelay: req.StirDelay, AllowError: req.AllowError, Sort: req.Sort, } if err := s.DB.Model(new(model.FeedFormulaDetail)).Where("id = ?", req.Id).Updates(updateData).Error; err != nil { return xerr.WithStack(err) } return nil } // EditFeedFormulaDetailAddRecode 更新饲料配方修改记录 func (s *StoreEntry) editFeedFormulaDetailAddRecode(ctx context.Context, req *operationPb.AddFeedFormulaDetail, feedFormulaDetail *model.FeedFormulaDetail) { editRecordList := make([]*model.FeedFormulaEditRecord, 0) editRecordData := &model.FeedFormulaEditRecord{ FeedFormulaId: int64(req.FeedFormulaId), PastureName: "集团", ForageName: req.ForageName, Status: operationPb.FeedFormulaEditRecordType_UPDATE, } operationName := s.GetCurrentUserName(ctx) editRecordData.OperationName = operationName lastGroupIdData := &model.FeedFormulaEditRecord{} if err := s.DB.Model(new(model.FeedFormulaEditRecord)). Order("group_id desc"). First(&lastGroupIdData).Error; err != nil { zaplog.Error("EditFeedByFeedFormula", zap.Any("lastGroupIdData", err)) } else { editRecordData.GroupId = lastGroupIdData.GroupId + 1 editRecordData.BeforeValue = lastGroupIdData.BeforeValue } if feedFormulaDetail.ForageName != req.ForageName { editRecordData.FieldName = EditRecodeMap["forage_name"] editRecordData.BeforeValue = lastGroupIdData.ForageName editRecordData.AfterValue = req.ForageName editRecordList = append(editRecordList, editRecordData) } if feedFormulaDetail.Weight != int32(req.Weight*100) { editRecordData.FieldName = EditRecodeMap["weight"] editRecordData.AfterValue = fmt.Sprintf("%d", int32(req.Weight*100)) editRecordList = append(editRecordList, editRecordData) } if feedFormulaDetail.AllowError != req.AllowError { editRecordData.FieldName = EditRecodeMap["allow_error"] editRecordData.AfterValue = fmt.Sprintf("%d", req.AllowError) editRecordList = append(editRecordList, editRecordData) } if feedFormulaDetail.StirDelay != req.StirDelay { editRecordData.FieldName = EditRecodeMap["stir_delay"] editRecordData.AfterValue = fmt.Sprintf("%d", req.StirDelay) editRecordList = append(editRecordList, editRecordData) } if feedFormulaDetail.Sort != req.Sort { editRecordData.FieldName = EditRecodeMap["sort"] editRecordData.AfterValue = fmt.Sprintf("%d", req.Sort) editRecordList = append(editRecordList, editRecordData) } if err := s.CreateFeedFormulaEditRecord(ctx, editRecordList); err != nil { zaplog.Error("EditFeedByFeedFormula", zap.Any("CreateFeedFormulaEditRecord", err)) } } // CreateFeedFormulaEditRecord 创建配方修改记录 func (s *StoreEntry) CreateFeedFormulaEditRecord(ctx context.Context, req []*model.FeedFormulaEditRecord) error { if req == nil { return nil } if err := s.DB.Model(new(model.FeedFormulaEditRecord)).Save(req).Error; err != nil { return xerr.WithStack(err) } return nil } // FeedFormulaDetailBySort 配方饲料排序 func (s *StoreEntry) FeedFormulaDetailBySort(ctx context.Context, req *operationPb.GroupAddFeedFormulaDetail) error { feedFormulaData := &model.FeedFormula{Id: int64(req.FeedFormulaId)} if err := s.DB.Model(new(model.FeedFormula)).First(feedFormulaData).Error; err != nil { return xerr.WithStack(err) } // 更新版本号 defer s.UpdateFeedFormalVersion(ctx, feedFormulaData) tx := s.DB.Transaction(func(tx *gorm.DB) error { for _, v := range req.List { if err := tx.Model(new(model.FeedFormulaDetail)). Where("id = ?", v.Id). Updates(map[string]interface{}{ "sort": v.Sort, }).Error; err != nil { return xerr.WithStack(err) } } return nil }) return tx } // FeedFormulaDetailIsModify 配方饲料是否可修改 func (s *StoreEntry) FeedFormulaDetailIsModify(ctx context.Context, req *operationPb.AddFeedFormulaDetail) error { feedFormulaData := &model.FeedFormula{Id: int64(req.FeedFormulaId)} if err := s.DB.Model(new(model.FeedFormula)).First(feedFormulaData).Error; err != nil { return xerr.WithStack(err) } // 更新版本号 defer s.UpdateFeedFormalVersion(ctx, feedFormulaData) return s.DB.Model(new(model.FeedFormulaDetail)). Where("id = ?", req.Id). Where("feed_formula_id = ?", req.FeedFormulaId). Updates(map[string]interface{}{ "is_modify": req.IsModify, }).Error } // DeleteFeedFormulaDetail 配方删除饲料 func (s *StoreEntry) DeleteFeedFormulaDetail(ctx context.Context, req *operationPb.GroupAddFeedFormulaDetail) error { feedFormulaData := &model.FeedFormula{Id: int64(req.FeedFormulaId)} if err := s.DB.Model(new(model.FeedFormula)).First(feedFormulaData).Error; err != nil { return xerr.WithStack(err) } // 增加配方记录 defer s.deleteFeedFormulaDetailAddRecode(ctx, req) tr := s.DB.Transaction(func(tx *gorm.DB) error { for _, v := range req.List { if err := tx.Model(new(model.FeedFormulaDetail)). Where("id = ?", v.Id). Updates(map[string]interface{}{ "is_show": operationPb.IsShow_NO, }).Error; err != nil { return xerr.WithStack(err) } } return nil }) return tr } // deleteFeedFormulaDetailAddRecode 删除配方增加修改记录 func (s *StoreEntry) deleteFeedFormulaDetailAddRecode(ctx context.Context, req *operationPb.GroupAddFeedFormulaDetail) { editRecordList := make([]*model.FeedFormulaEditRecord, 0) for _, v := range req.List { editRecordList = append(editRecordList, &model.FeedFormulaEditRecord{ FeedFormulaId: int64(req.FeedFormulaId), PastureName: "集团", ForageName: v.ForageName, Status: operationPb.FeedFormulaEditRecordType_DELETE, }) } if err := s.CreateFeedFormulaEditRecord(ctx, editRecordList); err != nil { zaplog.Error("deleteFeedFormulaDetailAddRecode", zap.Any("CreateFeedFormulaEditRecord", err)) } } // SearchFeedFormulaDetail 查询配方饲料详情 func (s *StoreEntry) SearchFeedFormulaDetail(ctx context.Context, req *operationPb.AddFeedFormulaDetail) (*operationPb.FeedFormulaDetailResponse, error) { feedFormulaData := &model.FeedFormula{Id: int64(req.FeedFormulaId)} if err := s.DB.Model(new(model.FeedFormula)).First(feedFormulaData).Debug().Error; err != nil { return nil, xerr.WithStack(err) } var count int64 feedFormulaDetailList := make([]*model.FeedFormulaDetail, 0) pref := s.DB.Model(new(model.FeedFormulaDetail)). Where("is_show = ?", operationPb.IsShow_OK). Where("feed_formula_id = ?", req.FeedFormulaId) if req.ForageName != "" { pref.Where("forage_name = ?", req.ForageName) } if req.ForageGroupName != "" { pref.Where("forage_group_name = ?", req.ForageGroupName) } if req.Weight > 0 { pref.Where("weight = ?", int64(req.Weight*100)) } if req.IsLockCowCountRatio > 0 { pref.Where("is_lock_cow_count_ratio = ?", req.IsLockCowCountRatio) } if req.StirDelay > 0 { pref.Where("stir_delay = ?", req.StirDelay) } if req.Sort > 0 { pref.Where("sort = ?", req.Sort) } if err := pref.Order("sort").Count(&count).Limit(int(req.Pagination.PageSize)). Offset(int(req.Pagination.PageOffset)).Find(&feedFormulaDetailList).Debug().Error; err != nil { return nil, xerr.WithStack(err) } return &operationPb.FeedFormulaDetailResponse{ Code: http.StatusOK, Msg: "ok", Data: model.FeedFormulaDetailSlice(feedFormulaDetailList).ToPB(), }, nil } // MixedFeedFormula 合成预混料 func (s *StoreEntry) MixedFeedFormula(ctx context.Context, req *operationPb.MixedFeedFormulaRequest) error { feedFormulaData := &model.FeedFormula{Id: int64(req.FeedFormulaId)} if err := s.DB.Model(new(model.FeedFormula)).First(feedFormulaData).Error; err != nil { return xerr.WithStack(err) } if feedFormulaData.FormulaTypeId == operationPb.FormulaType_PREMIXED_FORMULA { return xerr.Customf("预混料配方不能合成预混料") } tr := s.DB.Transaction(func(tx *gorm.DB) error { newFeedFormulaData := model.NewNewFeedFormulaByMixed(req) newFeedFormulaData.EncodeNumber = s.EncodeNumber(ctx) if err := s.DB.Model(new(model.FeedFormula)).Create(newFeedFormulaData).Error; err != nil { return xerr.WithStack(err) } feedFormulaDetailList := make([]*model.FeedFormulaDetail, 0) for _, v := range req.FeedList { feedFormulaDetailList = append(feedFormulaDetailList, &model.FeedFormulaDetail{ PastureName: "集团", FeedFormulaId: newFeedFormulaData.Id, ForageId: int64(v.ForageId), ForageName: v.ForageName, ForageGroupName: v.ForageGroupName, Weight: int32(v.Weight * 100), StirDelay: v.StirDelay, AllowError: v.AllowError, IsShow: operationPb.IsShow_OK, Sort: v.Sort, }) } if err := s.DB.Model(new(model.FeedFormulaDetail)).Save(feedFormulaDetailList).Error; err != nil { return xerr.WithStack(err) } return nil }) return tr } // 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 } // SearchFeedFormulaById 查询指定数据 func (s *StoreEntry) SearchFeedFormulaById(ctx context.Context, foodFormulaId int64) (*model.FeedFormula, error) { feedFormula := &model.FeedFormula{} if err := s.DB.Model(new(model.FeedFormula)). Where("is_delete = ?", operationPb.IsShow_OK). Where("id = ?", foodFormulaId). First(feedFormula).Error; err != nil { return nil, xerr.WithStack(err) } return feedFormula, 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) } else { s.PastureFeedFormulaIsModify(ctx, req.FeedFormulaId, req.IsShow) } } 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("exclude 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) } wg := sync.WaitGroup{} wg.Add(len(distributeData.PastureList)) var muError error for _, pasture := range distributeData.PastureList { go func(p *operationPb.AddPastureRequest) { defer wg.Done() // 过滤掉自己本牧场上报的配方数据 newDistributeFeedRequest := make([]*operationPb.DistributeFeedRequest, 0) for _, v := range distributeData.FeedFormulaList { if v.PastureId != p.Id { newDistributeFeedRequest = append(newDistributeFeedRequest, v) } } if len(newDistributeFeedRequest) <= 0 { return } // 过滤掉已经下发过配方 putDistributeFeedRequest := make([]*operationPb.DistributeFeedRequest, 0) for _, v := range newDistributeFeedRequest { if !s.CheckFeedFormulaDistribute(ctx, int64(p.Id), int64(v.Id)) { putDistributeFeedRequest = append(putDistributeFeedRequest, v) } } if len(putDistributeFeedRequest) <= 0 { return } // 请求参数 request, response := &operationPb.DistributeDataRequest{ PastureId: p.PastureId, FeedFormulaList: newDistributeFeedRequest, }, &model.PastureResponse{ Code: 0, Msg: "", Data: &model.PastureSuccess{Success: false}, } defer func() { if response.Code == http.StatusOK { feedFormulaDistributeLog := model.NewFeedFormulaDistributeLogList(distributeData.FeedFormulaList, int64(p.Id), p.Name, operationPb.IsShow_OK) if err = s.DB.Create(feedFormulaDistributeLog).Error; err != nil { zaplog.Error("DistributeFeedFormula", zap.Any("feedFormulaDistributeLog", feedFormulaDistributeLog), zap.Any("err", err)) } } else { muError = multierr.Append(muError, err) } }() if err = s.PastureHttpClient(ctx, model.FeedFormulaDistributeUrl, int64(p.Id), request, response); err != nil { muError = multierr.Append(muError, err) zaplog.Error("DistributeFeedFormula", zap.Any("pasture", p), zap.Any("body", distributeData.FeedFormulaList), zap.Any("err", err), zap.Any("response", response), ) b, _ := json.Marshal(request) res, _ := json.Marshal(response) pastureDataLog := model.NewPastureDataLog(int64(p.Id), PastureDataLogType["FeedFormula_Distribute"], model.FeedFormulaDistributeUrl, string(b), string(res)) if err = s.DB.Create(pastureDataLog).Error; err != nil { zaplog.Error("DistributeFeedFormula", zap.Any("pastureDataLog", pastureDataLog), zap.Any("err", err)) } } }(pasture) } wg.Wait() return xerr.WithStack(muError) } // CancelDistributeFeedFormula 取消配方下发牧场 func (s *StoreEntry) CancelDistributeFeedFormula(ctx context.Context, req *operationPb.DistributeFeedFormulaRequest) error { distributeData, err := s.checkoutDistributeData(ctx, req) if 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 *operationPb.AddPastureRequest) { defer wg.Done() pastureDataId := make([]int64, 0) for _, v := range distributeData.FeedFormulaList { if v.PastureId == p.Id { pastureDataId = append(pastureDataId, int64(v.PastureDataId)) } } if len(pastureDataId) <= 0 { return } request := &model.CancelDistributeFeedFormulaRequest{ PastureId: int64(p.Id), PastureDataId: pastureDataId, } response := &model.PastureResponse{} if err = s.PastureHttpClient(ctx, model.FeedFormulaCancelDistributeUrl, int64(p.Id), request, response); err != nil { zaplog.Error("DistributeFeedFormula", zap.String("url", model.FeedFormulaCancelDistributeUrl), zap.Any("pasture", p), zap.Any("body", distributeData.FeedFormulaList), zap.Any("err", err), zap.Any("response", response)) b, _ := json.Marshal(request) res, _ := json.Marshal(response) pastureDataLog := model.NewPastureDataLog(int64(p.Id), PastureDataLogType["FeedFormula_Cancel_Distribute"], model.FeedFormulaCancelDistributeUrl, string(b), string(res)) s.DB.Create(pastureDataLog) } }(pasture) } wg.Wait() return muError } // EditRecodeFeedFormula 配方修改记录 func (s *StoreEntry) EditRecodeFeedFormula(ctx context.Context, req *operationPb.EditRecodeFeedFormulaRequest) (*operationPb.EditRecodeFeedFormulaResponse, error) { feedFormulaData := &model.FeedFormula{Id: int64(req.FeedFormulaId)} if err := s.DB.Model(new(model.FeedFormula)).First(feedFormulaData).Error; err != nil { return nil, xerr.WithStack(err) } res := &operationPb.EditRecodeFeedFormulaResponse{ Code: http.StatusOK, Msg: "ok", Data: make([]*operationPb.EditRecodeFeedFormulaData, 0), } feedFormulaEditRecordList := make([]*model.FeedFormulaEditRecord, 0) pref := s.DB.Model(new(model.FeedFormulaEditRecord)).Where("status > 0").Where("feed_formula_id = ?", req.FeedFormulaId) if req.PastureId > 0 { pref.Where("pasture_id = ?", req.PastureId) } if req.StartTime > 0 && req.EndTime > 0 && req.EndTime >= req.StartTime { pref.Where("created_at >= ?", req.StartTime).Where("created_at <= ?", req.EndTime) } if err := pref.Order("group_id").Find(&feedFormulaEditRecordList).Error; err != nil { return res, xerr.WithStack(err) } groupByFeedFormulaEditRecordList := make(map[int64][]*model.FeedFormulaEditRecord) for _, v := range feedFormulaEditRecordList { if groupByFeedFormulaEditRecordList[v.GroupId] == nil { groupByFeedFormulaEditRecordList[v.GroupId] = make([]*model.FeedFormulaEditRecord, 0) } groupByFeedFormulaEditRecordList[v.GroupId] = append(groupByFeedFormulaEditRecordList[v.GroupId], v) } editRecodeFeedFormulaDataList := make([]*operationPb.EditRecodeFeedFormulaData, 0) for _, data := range groupByFeedFormulaEditRecordList { var modifyDetail = "" var pastureId int32 = 0 var pastureName = "" var createTime int64 = 0 for i, v := range data { if i == 0 { modifyDetail += fmt.Sprintf("%s\n ", v.PastureName) pastureId = int32(v.PastureId) pastureName = v.PastureName createTime = v.CreatedAt } switch v.Status { case operationPb.FeedFormulaEditRecordType_INSERT: modifyDetail += fmt.Sprintf(`%s新增了饲料%s\n `, v.OperationName, v.ForageName) case operationPb.FeedFormulaEditRecordType_UPDATE: modifyDetail += fmt.Sprintf(`%s将%s的%s"%s"更新为"%s"\n `, v.OperationName, v.ForageName, v.FieldName, v.BeforeValue, v.AfterValue) case operationPb.FeedFormulaEditRecordType_DELETE: modifyDetail += fmt.Sprintf(`%s删除了%s\n `, v.OperationName, v.ForageName) } } editRecodeFeedFormulaDataList = append(editRecodeFeedFormulaDataList, &operationPb.EditRecodeFeedFormulaData{ PastureId: pastureId, PastureName: pastureName, ModifyTime: time.Unix(createTime, 0).Format(model.LayoutTime), ModifyDetail: modifyDetail, }) } res.Data = editRecodeFeedFormulaDataList return res, nil } func (s *StoreEntry) FeedFormulaDetailList(ctx context.Context, req *operationPb.FeedFormulaDetailRequest) (*operationPb.FeedFormulaDetailResponse, error) { res := &operationPb.FeedFormulaDetailResponse{ Code: http.StatusOK, Msg: "ok", Data: make([]*operationPb.AddFeedFormulaDetail, 0), } feedFormula, err := s.SearchFeedFormulaById(ctx, int64(req.FeedFormulaId)) if err != nil { return nil, xerr.WithStack(err) } feedFormulaId := feedFormula.Id if feedFormula.PastureDataId > 0 { feedFormulaId = feedFormula.PastureDataId } list, err := s.SearchFeedFormalDetailById(ctx, feedFormulaId, feedFormula.PastureId) if err != nil { return nil, xerr.WithStack(err) } if len(list) <= 0 { list, err = s.SearchFeedFormalDetailById(ctx, feedFormula.Id, 0) if err != nil { return nil, xerr.WithStack(err) } } res.Data = model.FeedFormulaDetailSlice(list).ToPB() return res, nil } // FeedFormulaUsage 配方使用概况 func (s *StoreEntry) FeedFormulaUsage(ctx context.Context, req *operationPb.FeedFormulaUsageRequest) (*operationPb.FeedFormulaUsageResponse, error) { feedFormulaDistributeLogList := make([]*model.FeedFormulaDistributeLog, 0) if err := s.DB.Model(new(model.FeedFormulaDistributeLog)). Where("feed_formula_id = ?", req.FeedFormulaId). Where("is_show = ?", operationPb.IsShow_OK).Group("pasture_id"). Find(&feedFormulaDistributeLogList).Error; err != nil { return nil, xerr.WithStack(err) } res := &operationPb.FeedFormulaUsageResponse{ Code: http.StatusOK, Msg: "ok", Data: make([]*operationPb.FeedFormulaUsageList, 0), } wg := sync.WaitGroup{} wg.Add(len(feedFormulaDistributeLogList)) for _, list := range feedFormulaDistributeLogList { go func(l *model.FeedFormulaDistributeLog) { defer wg.Done() groupDetail, err := s.PastureDetailById(ctx, l.PastureId) if err != nil { zaplog.Error("FeedFormulaUsage", zap.Any("PastureDetailById", err)) return } req.PastureId = int32(groupDetail.PastureId) response := &operationPb.PastureFeedFormulaUsageResponse{} if err = s.PastureHttpClient(ctx, model.FeedUsageURl, groupDetail.Id, req, response); err != nil { zaplog.Error("FeedFormulaUsage", zap.Any("PastureDetailById", err)) return } if response.Code == http.StatusOK { data := &operationPb.FeedFormulaUsageList{ PastureId: int32(groupDetail.Id), PastureName: groupDetail.Name, MixedFodderAccurateRatio: response.Data.MixedFodderAccurateRatio, MixedFodderCorrectRatio: response.Data.MixedFodderCorrectRatio, SprinkleFodderAccurateRatio: response.Data.SprinkleFodderAccurateRatio, SprinkleFodderCorrectRatio: response.Data.SprinkleFodderCorrectRatio, AddFeedTime: response.Data.AddFeedTime, SprinkleTime: response.Data.SprinkleTime, StirTime: response.Data.StirTime, LastEditTime: response.Data.LastEditTime, } res.Data = append(res.Data, data) } else { zaplog.Error("FeedFormulaUsage-http", zap.Any("response", response)) return } }(list) } wg.Wait() return res, nil } func (s *StoreEntry) PastureFeedFormulaIsModify(ctx context.Context, feedFormulaId int32, isModify operationPb.IsShow_Kind) { feedFormulaDistributeLogList := make([]*model.FeedFormulaDistributeLog, 0) if err := s.DB.Where("is_show = ?", operationPb.IsShow_OK). Where("feed_formula_id = ?", feedFormulaId). Group("pasture_id").Find(&feedFormulaDistributeLogList).Error; err != nil { zaplog.Error("PastureFeedFormulaIsModify", zap.Any("err", err), zap.Any("feed_formula_id", feedFormulaId)) return } for _, v := range feedFormulaDistributeLogList { response := &model.PastureResponse{} request := &model.FeedFormulaIsModifyRequest{ PastureId: v.PastureId, FeedFormulaId: v.FeedFormulaId, IsModify: int32(isModify), } if err := s.PastureHttpClient(ctx, model.FeedFormulaIsModifyUrl, v.Id, request, response); err != nil { zaplog.Error("PastureFeedFormulaIsModify", zap.Any("request", request), zap.Any("err", err), zap.Any("response", response)) b, _ := json.Marshal(request) res, _ := json.Marshal(response) pastureDataLog := model.NewPastureDataLog(v.PastureId, PastureDataLogType["FeedFormula_IsModify"], model.FeedFormulaIsModifyUrl, string(b), string(res)) s.DB.Create(pastureDataLog) } } } func (s *StoreEntry) checkoutDistributeData(ctx context.Context, req *operationPb.DistributeFeedFormulaRequest) (*operationPb.CheckDistributeData, error) { result := &operationPb.CheckDistributeData{ PastureList: make([]*operationPb.AddPastureRequest, 0), FeedFormulaList: make([]*operationPb.DistributeFeedRequest, 0), } newGroupPastureList := make([]*model.GroupPasture, 0) if err := s.DB.Model(new(model.GroupPasture)).Where("id IN ?", req.PastureIds).Where("is_delete = ?", operationPb.IsShow_OK).Find(&newGroupPastureList).Error; err != nil { return result, xerr.WithStack(err) } result.PastureList = model.GroupPastureSlice(newGroupPastureList).ToPB() newFeedFormulaList := make([]*model.FeedFormula, 0) if err := s.DB.Model(new(model.FeedFormula)).Where("id IN ?", req.FeedFormulaIds).Where("is_show = ?", operationPb.IsShow_OK).Find(&newFeedFormulaList).Error; err != nil { return result, xerr.WithStack(err) } result.FeedFormulaList = model.FeedFormulaSlice(newFeedFormulaList).ToDistributePB() if len(result.FeedFormulaList) != len(req.FeedFormulaIds) { return result, xerr.Customf("有禁用的配方数据或者数据错误") } for _, v := range result.FeedFormulaList { feedFormulaDetail := make([]*model.FeedFormulaDetail, 0) if err := s.DB.Model(new(model.FeedFormulaDetail)).Where("feed_formula_id = ?", v.Id).Find(&feedFormulaDetail).Error; err != nil { zaplog.Error("checkoutDistributeData", zap.Any("feed_formula_id", v.Id), zap.Any("err", err)) return result, xerr.Customf("%v", err) } if len(feedFormulaDetail) <= 0 { return result, xerr.Customf("请先添加配方饲料信息: %s", v.Name) } v.FeedFormulaDetail = model.FeedFormulaDetailSlice(feedFormulaDetail).ToPB() } if len(result.PastureList) <= 0 || len(result.FeedFormulaList) <= 0 { return result, xerr.Customf("数据错误") } return result, nil } func (s *StoreEntry) checkoutDistributeLog(ctx context.Context, pastureId, feedFormulaId int64) bool { res := &model.FeedFormulaDistributeLog{} if err := s.DB.Model(new(model.FeedFormulaDistributeLog)).Where("feed_formula_id = ?", feedFormulaId). Where("pasture_id = ?", pastureId).Where("is_show = ?", operationPb.IsShow_OK).First(res).Error; err != nil { return false } if res.IsShow == operationPb.IsShow_OK { return true } return false } func (s *StoreEntry) SearchFeedFormalDetailById(ctx context.Context, feedFormulaId, pastureId int64) ([]*model.FeedFormulaDetail, error) { res := make([]*model.FeedFormulaDetail, 0) if err := s.DB.Model(new(model.FeedFormulaDetail)).Where("pasture_id = ?", pastureId). Where("feed_formula_id = ?", feedFormulaId). Where("is_show = ?", operationPb.IsShow_OK). Order("id desc").Find(&res).Error; err != nil { return nil, xerr.WithStack(err) } return res, nil } // UpdateFeedFormalVersion 更新版本库并通知牧场端 func (s *StoreEntry) UpdateFeedFormalVersion(ctx context.Context, req *model.FeedFormula) { if err := s.DB.Model(req).UpdateColumn("version", gorm.Expr("version + ?", 1)).Error; err != nil { zaplog.Error("UpdateFeedFormalVersion-UpdateColumn", zap.Any("err", err)) } // 获取该配方下发记录表 feedFormulaDistributeLogList := make([]*model.FeedFormulaDistributeLog, 0) if err := s.DB.Table(new(model.FeedFormulaDistributeLog).TableName()). Where("feed_formula_id = ?", req.Id). Where("is_show = ?", operationPb.IsShow_OK). Group("pasture_id"). Find(&feedFormulaDistributeLogList).Error; err != nil { if !errors.Is(err, gorm.ErrRecordNotFound) { zaplog.Error("UpdateFeedFormalVersion-feedFormulaDistributeLog", zap.Any("err", err)) } return } if len(feedFormulaDistributeLogList) > 0 { wg := sync.WaitGroup{} wg.Add(len(feedFormulaDistributeLogList)) for _, v := range feedFormulaDistributeLogList { go func(feedFormulaDistributeLog *model.FeedFormulaDistributeLog) { defer wg.Done() // 更新牧场端配方版本 s.UpdatePastureFeedDetailVersionLog(ctx, feedFormulaDistributeLog, req) }(v) } wg.Wait() } } func (s *StoreEntry) UpdatePastureFeedDetailVersionLog(ctx context.Context, distributeLog *model.FeedFormulaDistributeLog, req *model.FeedFormula) { pastureId := distributeLog.PastureId groupPasture, err := s.GetGroupPastureById(ctx, pastureId) if err != nil { zaplog.Error("UpdateFeedFormalVersion", zap.Any("GetGroupPastureById", pastureId), zap.Any("err", err)) return } if groupPasture.IsDistribution != operationPb.IsShow_OK { return } var ( belong int32 = 1 feedTemplateId = req.Id ) if req.PastureDataId > 0 { belong = 2 feedTemplateId = req.PastureDataId } list := make([]*operationPb.AddFeedFormulaDetail, 0) if err = s.DB.Model(new(model.FeedFormulaDetail)).Where("feed_formula_id = ?", req.Id).Find(&list).Error; err != nil { zaplog.Error("UpdatePastureFeedDetailVersionLog-getFeedFormulaDetail", zap.Any("err", err), zap.Any("feed_formula_id", req.Id)) return } response := &model.FeedFormulaUpdateVersionResponse{} body := &model.FeedFormulaUpdateVersionRequest{ FeedTemplateId: feedTemplateId, Version: req.Version, Belong: belong, Data: make([]*operationPb.AddFeedFormulaDetail, 0), } body.Data = list zaplog.Info("UpdateFeedFormalVersion", zap.Any("body", body)) if err = s.PastureHttpClient(ctx, model.FeedFormulaVersionUpdateUrl, pastureId, body, response); err != nil { zaplog.Error("UpdateFeedFormalVersion-http", zap.String("url", model.FeedFormulaVersionUpdateUrl), zap.Any("pasture", groupPasture), zap.Any("body", body), zap.Any("err", err), zap.Any("response", response)) return } if response.Code != http.StatusOK { zaplog.Error("UpdateFeedFormalVersion-response", zap.String("url", model.DashboardExecTimeUrl), zap.Any("pasture", groupPasture), zap.Any("body", body), zap.Any("err", err), zap.Any("response", response)) return } } // CheckFeedFormulaDistribute 检查该配方是否下发牧场端 func (s *StoreEntry) CheckFeedFormulaDistribute(ctx context.Context, pastureId, feedFormulaId int64) bool { res := &model.FeedFormulaDistributeLog{} if err := s.DB.Where("feed_formula_id = ?", feedFormulaId).Where("pasture_id = ?", pastureId).First(res).Error; err != nil { return false } if res.IsShow == operationPb.IsShow_OK { return true } return false } // ForageListByGroup 查询集团端饲料的列表数据 func (s *StoreEntry) ForageListByGroup(ctx context.Context) (*operationPb.SearchForageListResponse, error) { forage := make([]*model.Forage, 0) var count int64 = 0 if err := s.DB.Model(new(model.Forage)).Where("is_delete = ?", operationPb.IsShow_OK). Where("pasture_id = 0").Where("is_show = ?", operationPb.IsShow_OK). Order("id DESC").Count(&count).Find(&forage).Error; err != nil { return nil, xerr.WithStack(err) } return &operationPb.SearchForageListResponse{ Code: http.StatusOK, Msg: "ok", Data: &operationPb.SearchForageList{ Total: int32(count), List: model.ForageSlice(forage).ToPB(), }, }, nil }