Browse Source

feed: 配方管理

Yi 1 year ago
parent
commit
87bb315f13

+ 51 - 0
backend/operation/feed_formula.proto

@@ -0,0 +1,51 @@
+syntax = "proto3";
+package backend.operation;
+
+option go_package = ".;operationPb";
+
+import "backend/operation/pagination.proto";
+import "backend/operation/enum.proto";
+
+message AddFeedFormulaRequest {
+  int32 id = 1;
+  string name = 2;                   // 名称
+  string  encode_number = 3;         // 编码
+  string colour = 4;                 // 颜色
+  CattleCategoryParent.Kind cattle_category_id = 5;      // 畜牧类别id
+  string cattle_category_name = 6;   // 畜牧类型名称
+  int32 formula_type_id = 7;         // 配方类型id
+  string formula_type_name = 8;      // 配方类型名称
+  DataSource.Kind data_source_id = 9;   // 数据来源
+  string data_source_name = 10;   // 数据来源
+  string remarks = 11;               // 备注
+  int32 version = 12;                // 版本号
+  IsShow.Kind is_show = 13;          // 是否启用
+  IsShow.Kind is_modify = 14;        // 是否可修改
+  int32 created_at = 15;             // 创建时间
+  string created_at_format = 16;     // 创建时间格式化
+  string Pasture_name = 17;          // 牧场名称
+}
+
+message SearchFeedFormulaRequest {
+  int32 cattle_category_id = 1;    // 分类id
+  int32 formula_type_id = 2;       // 配方类型id
+  int32 data_source = 3;           // 饲料来源
+  IsShow.Kind is_show = 4;         // 是否启用
+  string name = 5;                 // 配方名称
+  string remarks = 6;              // 备注
+  PaginationModel pagination = 7;  // 分页
+}
+
+message SearchFeedFormulaListResponse {
+  int32 page = 1;
+  int32 page_size = 2;
+  int32 total = 3;
+  repeated AddFeedFormulaRequest list = 4;
+}
+
+// 是否启用
+message IsShowModifyFeedFormula {
+  int32 feed_formula_id = 1;
+  IsShow.Kind is_show = 2;
+  int32 edit_type = 3;                  // 1 更新是否启用 2 更新 modify
+}

+ 1 - 1
backend/operation/pasture.proto

@@ -168,7 +168,7 @@ message SearchForageListResponse {
 
 // 是否启用
 message IsShowForage {
-  uint32 forage_id = 1;
+  int32 forage_id = 1;
   IsShow.Kind is_show = 2;
 }
 

+ 250 - 0
http/handler/feed/feed_formula.go

@@ -0,0 +1,250 @@
+package feed
+
+import (
+	"fmt"
+	"kpt-tmr-group/http/middleware"
+	"kpt-tmr-group/pkg/apierr"
+	"kpt-tmr-group/pkg/ginutil"
+	"kpt-tmr-group/pkg/valid"
+	"kpt-tmr-group/pkg/xerr"
+	operationPb "kpt-tmr-group/proto/go/backend/operation"
+	"mime/multipart"
+	"net/http"
+	"path"
+	"strconv"
+	"time"
+
+	"github.com/gin-gonic/gin"
+)
+
+func AddFeedFormula(c *gin.Context) {
+	var req operationPb.AddFeedFormulaRequest
+	if err := c.BindJSON(&req); err != nil {
+		apierr.AbortBadRequest(c, http.StatusBadRequest, err)
+		return
+	}
+
+	if err := valid.ValidateStruct(&req,
+		valid.Field(&req.Name, valid.Required),
+		valid.Field(&req.EncodeNumber, valid.Required, valid.Length(1, 30)),
+		valid.Field(&req.Colour, valid.Required),
+		valid.Field(&req.CattleCategoryId, valid.Required, valid.Min(1)),
+		valid.Field(&req.CattleCategoryName, valid.Required),
+		valid.Field(&req.FormulaTypeId, valid.Required, valid.Min(1)),
+		valid.Field(&req.FormulaTypeName, valid.Required),
+		valid.Field(&req.DataSourceId, valid.Required, valid.Min(1)),
+		valid.Field(&req.DataSourceName, valid.Required),
+		valid.Field(&req.Remarks, valid.Required),
+		valid.Field(&req.IsShow, valid.Required, valid.Min(1), valid.Max(2)),
+		valid.Field(&req.IsModify, valid.Required, valid.Min(1), valid.Max(2)),
+	); err != nil {
+		apierr.AbortBadRequest(c, http.StatusBadRequest, err)
+		return
+	}
+
+	if err := middleware.BackendOperation(c).OpsService.CreateFeedFormula(c, &req); err != nil {
+		apierr.ClassifiedAbort(c, err)
+		return
+	}
+	ginutil.JSONResp(c, &operationPb.CommonOK{
+		Code: http.StatusOK,
+		Msg:  "ok",
+		Data: &operationPb.Success{Success: true},
+	})
+}
+
+func EditFeedFormula(c *gin.Context) {
+	var req operationPb.AddFeedFormulaRequest
+	if err := c.BindJSON(&req); err != nil {
+		apierr.AbortBadRequest(c, http.StatusBadRequest, err)
+		return
+	}
+
+	if err := valid.ValidateStruct(&req,
+		valid.Field(&req.Id, valid.Required, valid.Min(1)),
+		valid.Field(&req.Name, valid.Required),
+		valid.Field(&req.EncodeNumber, valid.Required, valid.Length(1, 30)),
+		valid.Field(&req.Colour, valid.Required),
+		valid.Field(&req.CattleCategoryId, valid.Required, valid.Min(1)),
+		valid.Field(&req.CattleCategoryName, valid.Required),
+		valid.Field(&req.FormulaTypeId, valid.Required, valid.Min(1)),
+		valid.Field(&req.FormulaTypeName, valid.Required),
+		valid.Field(&req.DataSourceId, valid.Required, valid.Min(1)),
+		valid.Field(&req.DataSourceName, valid.Required),
+		valid.Field(&req.Remarks, valid.Required),
+		valid.Field(&req.IsShow, valid.Required, valid.Min(1), valid.Max(2)),
+		valid.Field(&req.IsModify, valid.Required, valid.Min(1), valid.Max(2)),
+	); err != nil {
+		apierr.AbortBadRequest(c, http.StatusBadRequest, err)
+		return
+	}
+
+	if err := middleware.BackendOperation(c).OpsService.EditFeedFormula(c, &req); err != nil {
+		apierr.ClassifiedAbort(c, err)
+		return
+	}
+	ginutil.JSONResp(c, &operationPb.CommonOK{
+		Code: http.StatusOK,
+		Msg:  "ok",
+		Data: &operationPb.Success{Success: true},
+	})
+}
+
+func SearchFeedFormulaList(c *gin.Context) {
+	req := &operationPb.SearchFeedFormulaRequest{}
+	if err := ginutil.BindProto(c, req); err != nil {
+		apierr.AbortBadRequest(c, http.StatusBadRequest, err)
+		return
+	}
+
+	req.Pagination = &operationPb.PaginationModel{
+		Page:       int32(c.GetInt(middleware.Page)),
+		PageSize:   int32(c.GetInt(middleware.PageSize)),
+		PageOffset: int32(c.GetInt(middleware.PageOffset)),
+	}
+
+	res, err := middleware.BackendOperation(c).OpsService.SearchFeedFormulaList(c, req)
+	if err != nil {
+		apierr.ClassifiedAbort(c, err)
+		return
+	}
+	ginutil.JSONResp(c, res)
+}
+
+func DeleteFeedFormula(c *gin.Context) {
+	feedFormulaIdStr := c.Param("feed_formula_id")
+	feedFormulaId, _ := strconv.Atoi(feedFormulaIdStr)
+
+	if err := valid.Validate(feedFormulaId, valid.Required, valid.Min(1)); err != nil {
+		apierr.ClassifiedAbort(c, err)
+		return
+	}
+
+	if err := middleware.BackendOperation(c).OpsService.DeleteFeedFormula(c, int64(feedFormulaId)); err != nil {
+		apierr.ClassifiedAbort(c, err)
+		return
+	}
+	ginutil.JSONResp(c, &operationPb.CommonOK{
+		Code: http.StatusOK,
+		Msg:  "ok",
+		Data: &operationPb.Success{Success: true},
+	})
+}
+
+func IsShowModifyFeedFormula(c *gin.Context) {
+	var req operationPb.IsShowModifyFeedFormula
+	if err := c.BindJSON(&req); err != nil {
+		apierr.AbortBadRequest(c, http.StatusBadRequest, err)
+		return
+	}
+
+	if err := valid.ValidateStruct(&req,
+		valid.Field(&req.FeedFormulaId, valid.Required, valid.Min(1)),
+		valid.Field(&req.IsShow, valid.Required, valid.Min(1), valid.Max(2)),
+	); err != nil {
+		apierr.AbortBadRequest(c, http.StatusBadRequest, err)
+		return
+	}
+
+	if err := middleware.BackendOperation(c).OpsService.IsShowFeedFormula(c, &req); err != nil {
+		apierr.ClassifiedAbort(c, err)
+		return
+	}
+	ginutil.JSONResp(c, &operationPb.CommonOK{
+		Code: http.StatusOK,
+		Msg:  "ok",
+		Data: &operationPb.Success{Success: true},
+	})
+}
+
+func ExcelImportFeedFormula(c *gin.Context) {
+	file, err := func(c *gin.Context) (multipart.File, error) {
+		if c.ContentType() != "multipart/form-data" {
+			return nil, xerr.Custom("invalid Content-Type")
+		}
+		if c.Request.Body == nil || int(c.Request.ContentLength) <= 0 {
+			return nil, xerr.Custom("invalid body")
+		}
+
+		file, fileHeader, err := c.Request.FormFile("file")
+		if err != nil {
+			return nil, xerr.WithStack(err)
+		}
+
+		fileSuffix := path.Ext(path.Base(fileHeader.Filename))
+		if fileSuffix != ".xlsx" {
+			return nil, xerr.Custom("invalid file suffix")
+		}
+
+		return file, nil
+	}(c)
+
+	if err != nil {
+		apierr.AbortBadRequest(c, http.StatusBadRequest, err)
+		return
+	}
+
+	if err = middleware.BackendOperation(c).OpsService.ExcelImportFeedFormula(c, file); err != nil {
+		apierr.ClassifiedAbort(c, err)
+		return
+	}
+	ginutil.JSONResp(c, &operationPb.CommonOK{
+		Code: http.StatusOK,
+		Msg:  "ok",
+		Data: &operationPb.Success{Success: true},
+	})
+}
+
+func ExcelExportFeedFormula(c *gin.Context) {
+	req := &operationPb.SearchFeedFormulaRequest{}
+	if err := c.BindJSON(req); err != nil {
+		apierr.AbortBadRequest(c, http.StatusBadRequest, err)
+		return
+	}
+
+	req.Pagination = &operationPb.PaginationModel{
+		Page:       int32(c.GetInt(middleware.Page)),
+		PageSize:   int32(c.GetInt(middleware.PageSize)),
+		PageOffset: int32(c.GetInt(middleware.PageOffset)),
+	}
+
+	buffer, err := middleware.BackendOperation(c).OpsService.ExcelExportFeedFormula(c, req)
+	if err != nil {
+		apierr.ClassifiedAbort(c, err)
+		return
+	}
+
+	c.Header("Content-Type", "application/octet-stream")
+	c.Header("Content-Disposition", "attachment; filename="+(fmt.Sprintf("饲料表 %s.xlsx", time.Now().Format("200601021504"))))
+	if _, err = c.Writer.Write(buffer.Bytes()); err != nil {
+		apierr.ClassifiedAbort(c, err)
+		return
+	}
+
+	ginutil.JSONResp(c, &operationPb.CommonOK{
+		Code: http.StatusOK,
+		Msg:  "ok",
+		Data: &operationPb.Success{Success: true},
+	})
+}
+
+func ExcelTemplateFeedFormula(c *gin.Context) {
+	buffer, err := middleware.BackendOperation(c).OpsService.ExcelTemplateFeedFormula(c)
+	if err != nil {
+		apierr.ClassifiedAbort(c, err)
+		return
+	}
+
+	c.Header("Content-Type", "application/octet-stream")
+	c.Header("Content-Disposition", "attachment; filename="+(fmt.Sprintf("饲料表 %s.xlsx", time.Now().Format("200601021504"))))
+	if _, err = c.Writer.Write(buffer.Bytes()); err != nil {
+		apierr.ClassifiedAbort(c, err)
+		return
+	}
+
+	ginutil.JSONResp(c, &operationPb.CommonOK{
+		Code: http.StatusOK,
+		Msg:  "ok",
+		Data: &operationPb.Success{Success: true},
+	})
+}

+ 11 - 0
http/route/app_api.go

@@ -2,6 +2,7 @@ package route
 
 import (
 	"kpt-tmr-group/http/handler"
+	"kpt-tmr-group/http/handler/feed"
 	"kpt-tmr-group/http/handler/mobile"
 	"kpt-tmr-group/http/handler/pasture"
 	"kpt-tmr-group/http/handler/system"
@@ -87,6 +88,16 @@ func AppAPI(opts ...func(engine *gin.Engine)) func(s *gin.Engine) {
 		opsRoute.POST("forage/excel_export", pasture.ExcelExportForage)
 		opsRoute.POST("forage/excel_template", pasture.ExcelTemplateForage)
 		opsRoute.GET("/forage/enum/list", pasture.SearchForageEnumList)
+
+		// 饲料配方
+		opsRoute.POST("/feed_formula/add", feed.AddFeedFormula)
+		opsRoute.POST("/feed_formula/edit", feed.EditFeedFormula)
+		opsRoute.POST("/feed_formula/list", feed.SearchFeedFormulaList)
+		opsRoute.POST("/feed_formula/delete/:feed_formula_id", feed.DeleteFeedFormula)
+		opsRoute.POST("/feed_formula/is_modify_show", feed.IsShowModifyFeedFormula)
+		opsRoute.POST("/feed_formula/excel_export", feed.ExcelExportFeedFormula)
+		opsRoute.POST("/feed_formula/excel_import", feed.ExcelImportFeedFormula)
+		opsRoute.POST("/feed_formula/excel_template", feed.ExcelTemplateFeedFormula)
 	}
 }
 

+ 79 - 0
model/feed_formula.go

@@ -0,0 +1,79 @@
+package model
+
+import (
+	operationPb "kpt-tmr-group/proto/go/backend/operation"
+	"time"
+)
+
+type FeedFormula struct {
+	Id                 int64                                 `json:"id"`
+	Name               string                                `json:"name"`
+	Colour             string                                `json:"colour"`
+	EncodeNumber       string                                `json:"encode_number"`
+	CattleCategoryId   operationPb.CattleCategoryParent_Kind `json:"cattle_category_id"`
+	CattleCategoryName string                                `json:"cattle_category_name"`
+	FormulaTypeId      int32                                 `json:"formula_type_id"`
+	FormulaTypeName    string                                `json:"formula_type_name"`
+	DataSourceId       operationPb.DataSource_Kind           `json:"data_source_id"`
+	DataSourceName     string                                `json:"data_source_name"`
+	Remarks            string                                `json:"remarks"`
+	Version            int64                                 `json:"version"`
+	PastureId          int64                                 `json:"pasture_id"`
+	PastureName        string                                `json:"pasture_name"`
+	IsShow             operationPb.IsShow_Kind               `json:"is_show"`
+	IsModify           operationPb.IsShow_Kind               `json:"is_modify"`
+	IsDelete           operationPb.IsShow_Kind               `json:"is_delete"`
+	CreatedAt          int64                                 `json:"created_at"`
+	UpdatedAt          int64                                 `json:"updated_at"`
+}
+
+func (f *FeedFormula) TableName() string {
+	return "feed_formula"
+}
+
+func NewFeedFormula(req *operationPb.AddFeedFormulaRequest) *FeedFormula {
+	return &FeedFormula{
+		Name:               req.Name,
+		Colour:             req.Colour,
+		EncodeNumber:       req.EncodeNumber,
+		CattleCategoryId:   req.CattleCategoryId,
+		CattleCategoryName: req.CattleCategoryName,
+		FormulaTypeId:      req.FormulaTypeId,
+		FormulaTypeName:    req.FormulaTypeName,
+		DataSourceId:       req.DataSourceId,
+		DataSourceName:     req.DataSourceName,
+		Remarks:            req.Remarks,
+		Version:            0,
+		PastureId:          0,
+		PastureName:        "",
+		IsShow:             req.IsShow,
+		IsDelete:           operationPb.IsShow_OK,
+		IsModify:           operationPb.IsShow_OK,
+	}
+}
+
+type FeedFormulaSlice []*FeedFormula
+
+func (f FeedFormulaSlice) ToPB() []*operationPb.AddFeedFormulaRequest {
+	res := make([]*operationPb.AddFeedFormulaRequest, len(f))
+	for i, v := range f {
+		res[i] = &operationPb.AddFeedFormulaRequest{
+			Id:                 int32(v.Id),
+			Name:               v.Name,
+			CattleCategoryId:   v.CattleCategoryId,
+			CattleCategoryName: v.CattleCategoryName,
+			FormulaTypeId:      v.FormulaTypeId,
+			FormulaTypeName:    v.FormulaTypeName,
+			DataSourceName:     v.DataSourceName,
+			DataSourceId:       v.DataSourceId,
+			Remarks:            v.Remarks,
+			Version:            int32(v.Version),
+			PastureName:        v.PastureName,
+			IsShow:             v.IsShow,
+			IsModify:           v.IsModify,
+			CreatedAt:          int32(v.CreatedAt),
+			CreatedAtFormat:    time.Unix(v.CreatedAt, 0).Format(LayoutTime),
+		}
+	}
+	return res
+}

+ 288 - 0
module/backend/feed_service.go

@@ -0,0 +1,288 @@
+package backend
+
+import (
+	"bytes"
+	"context"
+	"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"
+
+	"github.com/xuri/excelize/v2"
+	"go.uber.org/zap"
+
+	"gorm.io/gorm"
+)
+
+// 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.Forage)).
+		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.Forage)).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{
+		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).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.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.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()
+}

+ 10 - 0
module/backend/interface.go

@@ -73,6 +73,16 @@ type Operation interface {
 	ExcelImportForage(ctx context.Context, req io.Reader) error
 	ExcelExportForage(ctx context.Context, req *operationPb.SearchForageListRequest) (*bytes.Buffer, error)
 	ExcelTemplateForage(ctx context.Context) (*bytes.Buffer, error)
+
+	// CreateFeedFormula 饲料配方
+	CreateFeedFormula(ctx context.Context, req *operationPb.AddFeedFormulaRequest) error
+	EditFeedFormula(ctx context.Context, req *operationPb.AddFeedFormulaRequest) error
+	SearchFeedFormulaList(ctx context.Context, req *operationPb.SearchFeedFormulaRequest) (*operationPb.SearchFeedFormulaListResponse, error)
+	IsShowFeedFormula(ctx context.Context, req *operationPb.IsShowModifyFeedFormula) error
+	DeleteFeedFormula(ctx context.Context, feedFormulaId int64) error
+	ExcelImportFeedFormula(ctx context.Context, req io.Reader) error
+	ExcelExportFeedFormula(ctx context.Context, req *operationPb.SearchFeedFormulaRequest) (*bytes.Buffer, error)
+	ExcelTemplateFeedFormula(ctx context.Context) (*bytes.Buffer, error)
 }
 
 type SystemOperation interface {

+ 13 - 11
module/backend/pasture_service.go

@@ -479,9 +479,10 @@ func (s *StoreEntry) SearchForageList(ctx context.Context, req *operationPb.Sear
 	}
 
 	return &operationPb.SearchForageListResponse{
-		Page:  req.Pagination.Page,
-		Total: int32(count),
-		List:  model.ForageSlice(forage).ToPB(),
+		Page:     req.Pagination.Page,
+		PageSize: req.Pagination.PageSize,
+		Total:    int32(count),
+		List:     model.ForageSlice(forage).ToPB(),
 	}, nil
 }
 
@@ -559,10 +560,16 @@ func (s *StoreEntry) ExcelImportForage(ctx context.Context, req io.Reader) error
 			forageSourceId                                int32
 			categoryId, price, jumpWeight, relayLocations int
 			allowError, packageWeight                     int
-			backup1, backup2, backup3                     string
+			name, uniqueEncode, backup1, backup2, backup3 string
 		)
 
 		for k, v := range row {
+			if k == 0 {
+				name = v
+			}
+			if k == 2 {
+				uniqueEncode = v
+			}
 			if k == 3 {
 				forageSourceId = operationPb.ForageSource_Kind_value[v]
 			}
@@ -600,10 +607,10 @@ func (s *StoreEntry) ExcelImportForage(ctx context.Context, req io.Reader) error
 		}
 
 		forageItem := &model.Forage{
-			Name:               row[0],
+			Name:               name,
 			CategoryId:         int64(categoryId),
 			MaterialType:       0,
-			UniqueEncode:       row[2],
+			UniqueEncode:       uniqueEncode,
 			ForageSourceId:     operationPb.ForageSource_Kind(forageSourceId),
 			PlanTypeId:         0,
 			SmallMaterialScale: "",
@@ -645,11 +652,6 @@ func (s *StoreEntry) ExcelExportForage(ctx context.Context, req *operationPb.Sea
 		return nil, xerr.Custom("数据为空")
 	}
 
-	data := make([]interface{}, 0)
-	for _, v := range res.List {
-		data = append(data, v)
-	}
-
 	file := excelize.NewFile()
 	defer file.Close()
 

+ 643 - 0
proto/go/backend/operation/feed_formula.pb.go

@@ -0,0 +1,643 @@
+// Code generated by protoc-gen-go. DO NOT EDIT.
+// versions:
+// 	protoc-gen-go v1.28.1
+// 	protoc        v3.21.9
+// source: backend/operation/feed_formula.proto
+
+package operationPb
+
+import (
+	protoreflect "google.golang.org/protobuf/reflect/protoreflect"
+	protoimpl "google.golang.org/protobuf/runtime/protoimpl"
+	reflect "reflect"
+	sync "sync"
+)
+
+const (
+	// Verify that this generated code is sufficiently up-to-date.
+	_ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)
+	// Verify that runtime/protoimpl is sufficiently up-to-date.
+	_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
+)
+
+type AddFeedFormulaRequest struct {
+	state         protoimpl.MessageState
+	sizeCache     protoimpl.SizeCache
+	unknownFields protoimpl.UnknownFields
+
+	Id                 int32                     `protobuf:"varint,1,opt,name=id,proto3" json:"id,omitempty"`
+	Name               string                    `protobuf:"bytes,2,opt,name=name,proto3" json:"name,omitempty"`                                                                                                     // 名称
+	EncodeNumber       string                    `protobuf:"bytes,3,opt,name=encode_number,json=encodeNumber,proto3" json:"encode_number,omitempty"`                                                                 // 编码
+	Colour             string                    `protobuf:"bytes,4,opt,name=colour,proto3" json:"colour,omitempty"`                                                                                                 // 颜色
+	CattleCategoryId   CattleCategoryParent_Kind `protobuf:"varint,5,opt,name=cattle_category_id,json=cattleCategoryId,proto3,enum=backend.operation.CattleCategoryParent_Kind" json:"cattle_category_id,omitempty"` // 畜牧类别id
+	CattleCategoryName string                    `protobuf:"bytes,6,opt,name=cattle_category_name,json=cattleCategoryName,proto3" json:"cattle_category_name,omitempty"`                                             // 畜牧类型名称
+	FormulaTypeId      int32                     `protobuf:"varint,7,opt,name=formula_type_id,json=formulaTypeId,proto3" json:"formula_type_id,omitempty"`                                                           // 配方类型id
+	FormulaTypeName    string                    `protobuf:"bytes,8,opt,name=formula_type_name,json=formulaTypeName,proto3" json:"formula_type_name,omitempty"`                                                      // 配方类型名称
+	DataSourceId       DataSource_Kind           `protobuf:"varint,9,opt,name=data_source_id,json=dataSourceId,proto3,enum=backend.operation.DataSource_Kind" json:"data_source_id,omitempty"`                       // 数据来源
+	DataSourceName     string                    `protobuf:"bytes,10,opt,name=data_source_name,json=dataSourceName,proto3" json:"data_source_name,omitempty"`                                                        // 数据来源
+	Remarks            string                    `protobuf:"bytes,11,opt,name=remarks,proto3" json:"remarks,omitempty"`                                                                                              // 备注
+	Version            int32                     `protobuf:"varint,12,opt,name=version,proto3" json:"version,omitempty"`                                                                                             // 版本号
+	IsShow             IsShow_Kind               `protobuf:"varint,13,opt,name=is_show,json=isShow,proto3,enum=backend.operation.IsShow_Kind" json:"is_show,omitempty"`                                              // 是否启用
+	IsModify           IsShow_Kind               `protobuf:"varint,14,opt,name=is_modify,json=isModify,proto3,enum=backend.operation.IsShow_Kind" json:"is_modify,omitempty"`                                        // 是否可修改
+	CreatedAt          int32                     `protobuf:"varint,15,opt,name=created_at,json=createdAt,proto3" json:"created_at,omitempty"`                                                                        // 创建时间
+	CreatedAtFormat    string                    `protobuf:"bytes,16,opt,name=created_at_format,json=createdAtFormat,proto3" json:"created_at_format,omitempty"`                                                     // 创建时间格式化
+	PastureName        string                    `protobuf:"bytes,17,opt,name=Pasture_name,json=PastureName,proto3" json:"Pasture_name,omitempty"`                                                                   // 牧场名称
+}
+
+func (x *AddFeedFormulaRequest) Reset() {
+	*x = AddFeedFormulaRequest{}
+	if protoimpl.UnsafeEnabled {
+		mi := &file_backend_operation_feed_formula_proto_msgTypes[0]
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		ms.StoreMessageInfo(mi)
+	}
+}
+
+func (x *AddFeedFormulaRequest) String() string {
+	return protoimpl.X.MessageStringOf(x)
+}
+
+func (*AddFeedFormulaRequest) ProtoMessage() {}
+
+func (x *AddFeedFormulaRequest) ProtoReflect() protoreflect.Message {
+	mi := &file_backend_operation_feed_formula_proto_msgTypes[0]
+	if protoimpl.UnsafeEnabled && x != nil {
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		if ms.LoadMessageInfo() == nil {
+			ms.StoreMessageInfo(mi)
+		}
+		return ms
+	}
+	return mi.MessageOf(x)
+}
+
+// Deprecated: Use AddFeedFormulaRequest.ProtoReflect.Descriptor instead.
+func (*AddFeedFormulaRequest) Descriptor() ([]byte, []int) {
+	return file_backend_operation_feed_formula_proto_rawDescGZIP(), []int{0}
+}
+
+func (x *AddFeedFormulaRequest) GetId() int32 {
+	if x != nil {
+		return x.Id
+	}
+	return 0
+}
+
+func (x *AddFeedFormulaRequest) GetName() string {
+	if x != nil {
+		return x.Name
+	}
+	return ""
+}
+
+func (x *AddFeedFormulaRequest) GetEncodeNumber() string {
+	if x != nil {
+		return x.EncodeNumber
+	}
+	return ""
+}
+
+func (x *AddFeedFormulaRequest) GetColour() string {
+	if x != nil {
+		return x.Colour
+	}
+	return ""
+}
+
+func (x *AddFeedFormulaRequest) GetCattleCategoryId() CattleCategoryParent_Kind {
+	if x != nil {
+		return x.CattleCategoryId
+	}
+	return CattleCategoryParent_INVALID
+}
+
+func (x *AddFeedFormulaRequest) GetCattleCategoryName() string {
+	if x != nil {
+		return x.CattleCategoryName
+	}
+	return ""
+}
+
+func (x *AddFeedFormulaRequest) GetFormulaTypeId() int32 {
+	if x != nil {
+		return x.FormulaTypeId
+	}
+	return 0
+}
+
+func (x *AddFeedFormulaRequest) GetFormulaTypeName() string {
+	if x != nil {
+		return x.FormulaTypeName
+	}
+	return ""
+}
+
+func (x *AddFeedFormulaRequest) GetDataSourceId() DataSource_Kind {
+	if x != nil {
+		return x.DataSourceId
+	}
+	return DataSource_INVALID
+}
+
+func (x *AddFeedFormulaRequest) GetDataSourceName() string {
+	if x != nil {
+		return x.DataSourceName
+	}
+	return ""
+}
+
+func (x *AddFeedFormulaRequest) GetRemarks() string {
+	if x != nil {
+		return x.Remarks
+	}
+	return ""
+}
+
+func (x *AddFeedFormulaRequest) GetVersion() int32 {
+	if x != nil {
+		return x.Version
+	}
+	return 0
+}
+
+func (x *AddFeedFormulaRequest) GetIsShow() IsShow_Kind {
+	if x != nil {
+		return x.IsShow
+	}
+	return IsShow_INVALID
+}
+
+func (x *AddFeedFormulaRequest) GetIsModify() IsShow_Kind {
+	if x != nil {
+		return x.IsModify
+	}
+	return IsShow_INVALID
+}
+
+func (x *AddFeedFormulaRequest) GetCreatedAt() int32 {
+	if x != nil {
+		return x.CreatedAt
+	}
+	return 0
+}
+
+func (x *AddFeedFormulaRequest) GetCreatedAtFormat() string {
+	if x != nil {
+		return x.CreatedAtFormat
+	}
+	return ""
+}
+
+func (x *AddFeedFormulaRequest) GetPastureName() string {
+	if x != nil {
+		return x.PastureName
+	}
+	return ""
+}
+
+type SearchFeedFormulaRequest struct {
+	state         protoimpl.MessageState
+	sizeCache     protoimpl.SizeCache
+	unknownFields protoimpl.UnknownFields
+
+	CattleCategoryId int32            `protobuf:"varint,1,opt,name=cattle_category_id,json=cattleCategoryId,proto3" json:"cattle_category_id,omitempty"`    // 分类id
+	FormulaTypeId    int32            `protobuf:"varint,2,opt,name=formula_type_id,json=formulaTypeId,proto3" json:"formula_type_id,omitempty"`             // 配方类型id
+	DataSource       int32            `protobuf:"varint,3,opt,name=data_source,json=dataSource,proto3" json:"data_source,omitempty"`                        // 饲料来源
+	IsShow           IsShow_Kind      `protobuf:"varint,4,opt,name=is_show,json=isShow,proto3,enum=backend.operation.IsShow_Kind" json:"is_show,omitempty"` // 是否启用
+	Name             string           `protobuf:"bytes,5,opt,name=name,proto3" json:"name,omitempty"`                                                       // 配方名称
+	Remarks          string           `protobuf:"bytes,6,opt,name=remarks,proto3" json:"remarks,omitempty"`                                                 // 备注
+	Pagination       *PaginationModel `protobuf:"bytes,7,opt,name=pagination,proto3" json:"pagination,omitempty"`                                           // 分页
+}
+
+func (x *SearchFeedFormulaRequest) Reset() {
+	*x = SearchFeedFormulaRequest{}
+	if protoimpl.UnsafeEnabled {
+		mi := &file_backend_operation_feed_formula_proto_msgTypes[1]
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		ms.StoreMessageInfo(mi)
+	}
+}
+
+func (x *SearchFeedFormulaRequest) String() string {
+	return protoimpl.X.MessageStringOf(x)
+}
+
+func (*SearchFeedFormulaRequest) ProtoMessage() {}
+
+func (x *SearchFeedFormulaRequest) ProtoReflect() protoreflect.Message {
+	mi := &file_backend_operation_feed_formula_proto_msgTypes[1]
+	if protoimpl.UnsafeEnabled && x != nil {
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		if ms.LoadMessageInfo() == nil {
+			ms.StoreMessageInfo(mi)
+		}
+		return ms
+	}
+	return mi.MessageOf(x)
+}
+
+// Deprecated: Use SearchFeedFormulaRequest.ProtoReflect.Descriptor instead.
+func (*SearchFeedFormulaRequest) Descriptor() ([]byte, []int) {
+	return file_backend_operation_feed_formula_proto_rawDescGZIP(), []int{1}
+}
+
+func (x *SearchFeedFormulaRequest) GetCattleCategoryId() int32 {
+	if x != nil {
+		return x.CattleCategoryId
+	}
+	return 0
+}
+
+func (x *SearchFeedFormulaRequest) GetFormulaTypeId() int32 {
+	if x != nil {
+		return x.FormulaTypeId
+	}
+	return 0
+}
+
+func (x *SearchFeedFormulaRequest) GetDataSource() int32 {
+	if x != nil {
+		return x.DataSource
+	}
+	return 0
+}
+
+func (x *SearchFeedFormulaRequest) GetIsShow() IsShow_Kind {
+	if x != nil {
+		return x.IsShow
+	}
+	return IsShow_INVALID
+}
+
+func (x *SearchFeedFormulaRequest) GetName() string {
+	if x != nil {
+		return x.Name
+	}
+	return ""
+}
+
+func (x *SearchFeedFormulaRequest) GetRemarks() string {
+	if x != nil {
+		return x.Remarks
+	}
+	return ""
+}
+
+func (x *SearchFeedFormulaRequest) GetPagination() *PaginationModel {
+	if x != nil {
+		return x.Pagination
+	}
+	return nil
+}
+
+type SearchFeedFormulaListResponse struct {
+	state         protoimpl.MessageState
+	sizeCache     protoimpl.SizeCache
+	unknownFields protoimpl.UnknownFields
+
+	Page     int32                    `protobuf:"varint,1,opt,name=page,proto3" json:"page,omitempty"`
+	PageSize int32                    `protobuf:"varint,2,opt,name=page_size,json=pageSize,proto3" json:"page_size,omitempty"`
+	Total    int32                    `protobuf:"varint,3,opt,name=total,proto3" json:"total,omitempty"`
+	List     []*AddFeedFormulaRequest `protobuf:"bytes,4,rep,name=list,proto3" json:"list,omitempty"`
+}
+
+func (x *SearchFeedFormulaListResponse) Reset() {
+	*x = SearchFeedFormulaListResponse{}
+	if protoimpl.UnsafeEnabled {
+		mi := &file_backend_operation_feed_formula_proto_msgTypes[2]
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		ms.StoreMessageInfo(mi)
+	}
+}
+
+func (x *SearchFeedFormulaListResponse) String() string {
+	return protoimpl.X.MessageStringOf(x)
+}
+
+func (*SearchFeedFormulaListResponse) ProtoMessage() {}
+
+func (x *SearchFeedFormulaListResponse) ProtoReflect() protoreflect.Message {
+	mi := &file_backend_operation_feed_formula_proto_msgTypes[2]
+	if protoimpl.UnsafeEnabled && x != nil {
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		if ms.LoadMessageInfo() == nil {
+			ms.StoreMessageInfo(mi)
+		}
+		return ms
+	}
+	return mi.MessageOf(x)
+}
+
+// Deprecated: Use SearchFeedFormulaListResponse.ProtoReflect.Descriptor instead.
+func (*SearchFeedFormulaListResponse) Descriptor() ([]byte, []int) {
+	return file_backend_operation_feed_formula_proto_rawDescGZIP(), []int{2}
+}
+
+func (x *SearchFeedFormulaListResponse) GetPage() int32 {
+	if x != nil {
+		return x.Page
+	}
+	return 0
+}
+
+func (x *SearchFeedFormulaListResponse) GetPageSize() int32 {
+	if x != nil {
+		return x.PageSize
+	}
+	return 0
+}
+
+func (x *SearchFeedFormulaListResponse) GetTotal() int32 {
+	if x != nil {
+		return x.Total
+	}
+	return 0
+}
+
+func (x *SearchFeedFormulaListResponse) GetList() []*AddFeedFormulaRequest {
+	if x != nil {
+		return x.List
+	}
+	return nil
+}
+
+// 是否启用
+type IsShowModifyFeedFormula struct {
+	state         protoimpl.MessageState
+	sizeCache     protoimpl.SizeCache
+	unknownFields protoimpl.UnknownFields
+
+	FeedFormulaId int32       `protobuf:"varint,1,opt,name=feed_formula_id,json=feedFormulaId,proto3" json:"feed_formula_id,omitempty"`
+	IsShow        IsShow_Kind `protobuf:"varint,2,opt,name=is_show,json=isShow,proto3,enum=backend.operation.IsShow_Kind" json:"is_show,omitempty"`
+	EditType      int32       `protobuf:"varint,3,opt,name=edit_type,json=editType,proto3" json:"edit_type,omitempty"` // 1 更新是否启用 2 更新 modify
+}
+
+func (x *IsShowModifyFeedFormula) Reset() {
+	*x = IsShowModifyFeedFormula{}
+	if protoimpl.UnsafeEnabled {
+		mi := &file_backend_operation_feed_formula_proto_msgTypes[3]
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		ms.StoreMessageInfo(mi)
+	}
+}
+
+func (x *IsShowModifyFeedFormula) String() string {
+	return protoimpl.X.MessageStringOf(x)
+}
+
+func (*IsShowModifyFeedFormula) ProtoMessage() {}
+
+func (x *IsShowModifyFeedFormula) ProtoReflect() protoreflect.Message {
+	mi := &file_backend_operation_feed_formula_proto_msgTypes[3]
+	if protoimpl.UnsafeEnabled && x != nil {
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		if ms.LoadMessageInfo() == nil {
+			ms.StoreMessageInfo(mi)
+		}
+		return ms
+	}
+	return mi.MessageOf(x)
+}
+
+// Deprecated: Use IsShowModifyFeedFormula.ProtoReflect.Descriptor instead.
+func (*IsShowModifyFeedFormula) Descriptor() ([]byte, []int) {
+	return file_backend_operation_feed_formula_proto_rawDescGZIP(), []int{3}
+}
+
+func (x *IsShowModifyFeedFormula) GetFeedFormulaId() int32 {
+	if x != nil {
+		return x.FeedFormulaId
+	}
+	return 0
+}
+
+func (x *IsShowModifyFeedFormula) GetIsShow() IsShow_Kind {
+	if x != nil {
+		return x.IsShow
+	}
+	return IsShow_INVALID
+}
+
+func (x *IsShowModifyFeedFormula) GetEditType() int32 {
+	if x != nil {
+		return x.EditType
+	}
+	return 0
+}
+
+var File_backend_operation_feed_formula_proto protoreflect.FileDescriptor
+
+var file_backend_operation_feed_formula_proto_rawDesc = []byte{
+	0x0a, 0x24, 0x62, 0x61, 0x63, 0x6b, 0x65, 0x6e, 0x64, 0x2f, 0x6f, 0x70, 0x65, 0x72, 0x61, 0x74,
+	0x69, 0x6f, 0x6e, 0x2f, 0x66, 0x65, 0x65, 0x64, 0x5f, 0x66, 0x6f, 0x72, 0x6d, 0x75, 0x6c, 0x61,
+	0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x11, 0x62, 0x61, 0x63, 0x6b, 0x65, 0x6e, 0x64, 0x2e,
+	0x6f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x1a, 0x22, 0x62, 0x61, 0x63, 0x6b, 0x65,
+	0x6e, 0x64, 0x2f, 0x6f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2f, 0x70, 0x61, 0x67,
+	0x69, 0x6e, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x1c, 0x62,
+	0x61, 0x63, 0x6b, 0x65, 0x6e, 0x64, 0x2f, 0x6f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e,
+	0x2f, 0x65, 0x6e, 0x75, 0x6d, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0xe6, 0x05, 0x0a, 0x15,
+	0x41, 0x64, 0x64, 0x46, 0x65, 0x65, 0x64, 0x46, 0x6f, 0x72, 0x6d, 0x75, 0x6c, 0x61, 0x52, 0x65,
+	0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28,
+	0x05, 0x52, 0x02, 0x69, 0x64, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20,
+	0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x23, 0x0a, 0x0d, 0x65, 0x6e, 0x63,
+	0x6f, 0x64, 0x65, 0x5f, 0x6e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09,
+	0x52, 0x0c, 0x65, 0x6e, 0x63, 0x6f, 0x64, 0x65, 0x4e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x12, 0x16,
+	0x0a, 0x06, 0x63, 0x6f, 0x6c, 0x6f, 0x75, 0x72, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06,
+	0x63, 0x6f, 0x6c, 0x6f, 0x75, 0x72, 0x12, 0x5a, 0x0a, 0x12, 0x63, 0x61, 0x74, 0x74, 0x6c, 0x65,
+	0x5f, 0x63, 0x61, 0x74, 0x65, 0x67, 0x6f, 0x72, 0x79, 0x5f, 0x69, 0x64, 0x18, 0x05, 0x20, 0x01,
+	0x28, 0x0e, 0x32, 0x2c, 0x2e, 0x62, 0x61, 0x63, 0x6b, 0x65, 0x6e, 0x64, 0x2e, 0x6f, 0x70, 0x65,
+	0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x43, 0x61, 0x74, 0x74, 0x6c, 0x65, 0x43, 0x61, 0x74,
+	0x65, 0x67, 0x6f, 0x72, 0x79, 0x50, 0x61, 0x72, 0x65, 0x6e, 0x74, 0x2e, 0x4b, 0x69, 0x6e, 0x64,
+	0x52, 0x10, 0x63, 0x61, 0x74, 0x74, 0x6c, 0x65, 0x43, 0x61, 0x74, 0x65, 0x67, 0x6f, 0x72, 0x79,
+	0x49, 0x64, 0x12, 0x30, 0x0a, 0x14, 0x63, 0x61, 0x74, 0x74, 0x6c, 0x65, 0x5f, 0x63, 0x61, 0x74,
+	0x65, 0x67, 0x6f, 0x72, 0x79, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09,
+	0x52, 0x12, 0x63, 0x61, 0x74, 0x74, 0x6c, 0x65, 0x43, 0x61, 0x74, 0x65, 0x67, 0x6f, 0x72, 0x79,
+	0x4e, 0x61, 0x6d, 0x65, 0x12, 0x26, 0x0a, 0x0f, 0x66, 0x6f, 0x72, 0x6d, 0x75, 0x6c, 0x61, 0x5f,
+	0x74, 0x79, 0x70, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x07, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0d, 0x66,
+	0x6f, 0x72, 0x6d, 0x75, 0x6c, 0x61, 0x54, 0x79, 0x70, 0x65, 0x49, 0x64, 0x12, 0x2a, 0x0a, 0x11,
+	0x66, 0x6f, 0x72, 0x6d, 0x75, 0x6c, 0x61, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x5f, 0x6e, 0x61, 0x6d,
+	0x65, 0x18, 0x08, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0f, 0x66, 0x6f, 0x72, 0x6d, 0x75, 0x6c, 0x61,
+	0x54, 0x79, 0x70, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x48, 0x0a, 0x0e, 0x64, 0x61, 0x74, 0x61,
+	0x5f, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x09, 0x20, 0x01, 0x28, 0x0e,
+	0x32, 0x22, 0x2e, 0x62, 0x61, 0x63, 0x6b, 0x65, 0x6e, 0x64, 0x2e, 0x6f, 0x70, 0x65, 0x72, 0x61,
+	0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x44, 0x61, 0x74, 0x61, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x2e,
+	0x4b, 0x69, 0x6e, 0x64, 0x52, 0x0c, 0x64, 0x61, 0x74, 0x61, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65,
+	0x49, 0x64, 0x12, 0x28, 0x0a, 0x10, 0x64, 0x61, 0x74, 0x61, 0x5f, 0x73, 0x6f, 0x75, 0x72, 0x63,
+	0x65, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0e, 0x64, 0x61,
+	0x74, 0x61, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x18, 0x0a, 0x07,
+	0x72, 0x65, 0x6d, 0x61, 0x72, 0x6b, 0x73, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x72,
+	0x65, 0x6d, 0x61, 0x72, 0x6b, 0x73, 0x12, 0x18, 0x0a, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f,
+	0x6e, 0x18, 0x0c, 0x20, 0x01, 0x28, 0x05, 0x52, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e,
+	0x12, 0x37, 0x0a, 0x07, 0x69, 0x73, 0x5f, 0x73, 0x68, 0x6f, 0x77, 0x18, 0x0d, 0x20, 0x01, 0x28,
+	0x0e, 0x32, 0x1e, 0x2e, 0x62, 0x61, 0x63, 0x6b, 0x65, 0x6e, 0x64, 0x2e, 0x6f, 0x70, 0x65, 0x72,
+	0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x49, 0x73, 0x53, 0x68, 0x6f, 0x77, 0x2e, 0x4b, 0x69, 0x6e,
+	0x64, 0x52, 0x06, 0x69, 0x73, 0x53, 0x68, 0x6f, 0x77, 0x12, 0x3b, 0x0a, 0x09, 0x69, 0x73, 0x5f,
+	0x6d, 0x6f, 0x64, 0x69, 0x66, 0x79, 0x18, 0x0e, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x1e, 0x2e, 0x62,
+	0x61, 0x63, 0x6b, 0x65, 0x6e, 0x64, 0x2e, 0x6f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e,
+	0x2e, 0x49, 0x73, 0x53, 0x68, 0x6f, 0x77, 0x2e, 0x4b, 0x69, 0x6e, 0x64, 0x52, 0x08, 0x69, 0x73,
+	0x4d, 0x6f, 0x64, 0x69, 0x66, 0x79, 0x12, 0x1d, 0x0a, 0x0a, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65,
+	0x64, 0x5f, 0x61, 0x74, 0x18, 0x0f, 0x20, 0x01, 0x28, 0x05, 0x52, 0x09, 0x63, 0x72, 0x65, 0x61,
+	0x74, 0x65, 0x64, 0x41, 0x74, 0x12, 0x2a, 0x0a, 0x11, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64,
+	0x5f, 0x61, 0x74, 0x5f, 0x66, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x18, 0x10, 0x20, 0x01, 0x28, 0x09,
+	0x52, 0x0f, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x41, 0x74, 0x46, 0x6f, 0x72, 0x6d, 0x61,
+	0x74, 0x12, 0x21, 0x0a, 0x0c, 0x50, 0x61, 0x73, 0x74, 0x75, 0x72, 0x65, 0x5f, 0x6e, 0x61, 0x6d,
+	0x65, 0x18, 0x11, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x50, 0x61, 0x73, 0x74, 0x75, 0x72, 0x65,
+	0x4e, 0x61, 0x6d, 0x65, 0x22, 0xbc, 0x02, 0x0a, 0x18, 0x53, 0x65, 0x61, 0x72, 0x63, 0x68, 0x46,
+	0x65, 0x65, 0x64, 0x46, 0x6f, 0x72, 0x6d, 0x75, 0x6c, 0x61, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73,
+	0x74, 0x12, 0x2c, 0x0a, 0x12, 0x63, 0x61, 0x74, 0x74, 0x6c, 0x65, 0x5f, 0x63, 0x61, 0x74, 0x65,
+	0x67, 0x6f, 0x72, 0x79, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x05, 0x52, 0x10, 0x63,
+	0x61, 0x74, 0x74, 0x6c, 0x65, 0x43, 0x61, 0x74, 0x65, 0x67, 0x6f, 0x72, 0x79, 0x49, 0x64, 0x12,
+	0x26, 0x0a, 0x0f, 0x66, 0x6f, 0x72, 0x6d, 0x75, 0x6c, 0x61, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x5f,
+	0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0d, 0x66, 0x6f, 0x72, 0x6d, 0x75, 0x6c,
+	0x61, 0x54, 0x79, 0x70, 0x65, 0x49, 0x64, 0x12, 0x1f, 0x0a, 0x0b, 0x64, 0x61, 0x74, 0x61, 0x5f,
+	0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0a, 0x64, 0x61,
+	0x74, 0x61, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x12, 0x37, 0x0a, 0x07, 0x69, 0x73, 0x5f, 0x73,
+	0x68, 0x6f, 0x77, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x1e, 0x2e, 0x62, 0x61, 0x63, 0x6b,
+	0x65, 0x6e, 0x64, 0x2e, 0x6f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x49, 0x73,
+	0x53, 0x68, 0x6f, 0x77, 0x2e, 0x4b, 0x69, 0x6e, 0x64, 0x52, 0x06, 0x69, 0x73, 0x53, 0x68, 0x6f,
+	0x77, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52,
+	0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x72, 0x65, 0x6d, 0x61, 0x72, 0x6b, 0x73,
+	0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x72, 0x65, 0x6d, 0x61, 0x72, 0x6b, 0x73, 0x12,
+	0x42, 0x0a, 0x0a, 0x70, 0x61, 0x67, 0x69, 0x6e, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x07, 0x20,
+	0x01, 0x28, 0x0b, 0x32, 0x22, 0x2e, 0x62, 0x61, 0x63, 0x6b, 0x65, 0x6e, 0x64, 0x2e, 0x6f, 0x70,
+	0x65, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x50, 0x61, 0x67, 0x69, 0x6e, 0x61, 0x74, 0x69,
+	0x6f, 0x6e, 0x4d, 0x6f, 0x64, 0x65, 0x6c, 0x52, 0x0a, 0x70, 0x61, 0x67, 0x69, 0x6e, 0x61, 0x74,
+	0x69, 0x6f, 0x6e, 0x22, 0xa4, 0x01, 0x0a, 0x1d, 0x53, 0x65, 0x61, 0x72, 0x63, 0x68, 0x46, 0x65,
+	0x65, 0x64, 0x46, 0x6f, 0x72, 0x6d, 0x75, 0x6c, 0x61, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x65, 0x73,
+	0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x70, 0x61, 0x67, 0x65, 0x18, 0x01, 0x20,
+	0x01, 0x28, 0x05, 0x52, 0x04, 0x70, 0x61, 0x67, 0x65, 0x12, 0x1b, 0x0a, 0x09, 0x70, 0x61, 0x67,
+	0x65, 0x5f, 0x73, 0x69, 0x7a, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x05, 0x52, 0x08, 0x70, 0x61,
+	0x67, 0x65, 0x53, 0x69, 0x7a, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x18,
+	0x03, 0x20, 0x01, 0x28, 0x05, 0x52, 0x05, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x12, 0x3c, 0x0a, 0x04,
+	0x6c, 0x69, 0x73, 0x74, 0x18, 0x04, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x28, 0x2e, 0x62, 0x61, 0x63,
+	0x6b, 0x65, 0x6e, 0x64, 0x2e, 0x6f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x41,
+	0x64, 0x64, 0x46, 0x65, 0x65, 0x64, 0x46, 0x6f, 0x72, 0x6d, 0x75, 0x6c, 0x61, 0x52, 0x65, 0x71,
+	0x75, 0x65, 0x73, 0x74, 0x52, 0x04, 0x6c, 0x69, 0x73, 0x74, 0x22, 0x97, 0x01, 0x0a, 0x17, 0x49,
+	0x73, 0x53, 0x68, 0x6f, 0x77, 0x4d, 0x6f, 0x64, 0x69, 0x66, 0x79, 0x46, 0x65, 0x65, 0x64, 0x46,
+	0x6f, 0x72, 0x6d, 0x75, 0x6c, 0x61, 0x12, 0x26, 0x0a, 0x0f, 0x66, 0x65, 0x65, 0x64, 0x5f, 0x66,
+	0x6f, 0x72, 0x6d, 0x75, 0x6c, 0x61, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x05, 0x52,
+	0x0d, 0x66, 0x65, 0x65, 0x64, 0x46, 0x6f, 0x72, 0x6d, 0x75, 0x6c, 0x61, 0x49, 0x64, 0x12, 0x37,
+	0x0a, 0x07, 0x69, 0x73, 0x5f, 0x73, 0x68, 0x6f, 0x77, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0e, 0x32,
+	0x1e, 0x2e, 0x62, 0x61, 0x63, 0x6b, 0x65, 0x6e, 0x64, 0x2e, 0x6f, 0x70, 0x65, 0x72, 0x61, 0x74,
+	0x69, 0x6f, 0x6e, 0x2e, 0x49, 0x73, 0x53, 0x68, 0x6f, 0x77, 0x2e, 0x4b, 0x69, 0x6e, 0x64, 0x52,
+	0x06, 0x69, 0x73, 0x53, 0x68, 0x6f, 0x77, 0x12, 0x1b, 0x0a, 0x09, 0x65, 0x64, 0x69, 0x74, 0x5f,
+	0x74, 0x79, 0x70, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x05, 0x52, 0x08, 0x65, 0x64, 0x69, 0x74,
+	0x54, 0x79, 0x70, 0x65, 0x42, 0x0f, 0x5a, 0x0d, 0x2e, 0x3b, 0x6f, 0x70, 0x65, 0x72, 0x61, 0x74,
+	0x69, 0x6f, 0x6e, 0x50, 0x62, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
+}
+
+var (
+	file_backend_operation_feed_formula_proto_rawDescOnce sync.Once
+	file_backend_operation_feed_formula_proto_rawDescData = file_backend_operation_feed_formula_proto_rawDesc
+)
+
+func file_backend_operation_feed_formula_proto_rawDescGZIP() []byte {
+	file_backend_operation_feed_formula_proto_rawDescOnce.Do(func() {
+		file_backend_operation_feed_formula_proto_rawDescData = protoimpl.X.CompressGZIP(file_backend_operation_feed_formula_proto_rawDescData)
+	})
+	return file_backend_operation_feed_formula_proto_rawDescData
+}
+
+var file_backend_operation_feed_formula_proto_msgTypes = make([]protoimpl.MessageInfo, 4)
+var file_backend_operation_feed_formula_proto_goTypes = []interface{}{
+	(*AddFeedFormulaRequest)(nil),         // 0: backend.operation.AddFeedFormulaRequest
+	(*SearchFeedFormulaRequest)(nil),      // 1: backend.operation.SearchFeedFormulaRequest
+	(*SearchFeedFormulaListResponse)(nil), // 2: backend.operation.SearchFeedFormulaListResponse
+	(*IsShowModifyFeedFormula)(nil),       // 3: backend.operation.IsShowModifyFeedFormula
+	(CattleCategoryParent_Kind)(0),        // 4: backend.operation.CattleCategoryParent.Kind
+	(DataSource_Kind)(0),                  // 5: backend.operation.DataSource.Kind
+	(IsShow_Kind)(0),                      // 6: backend.operation.IsShow.Kind
+	(*PaginationModel)(nil),               // 7: backend.operation.PaginationModel
+}
+var file_backend_operation_feed_formula_proto_depIdxs = []int32{
+	4, // 0: backend.operation.AddFeedFormulaRequest.cattle_category_id:type_name -> backend.operation.CattleCategoryParent.Kind
+	5, // 1: backend.operation.AddFeedFormulaRequest.data_source_id:type_name -> backend.operation.DataSource.Kind
+	6, // 2: backend.operation.AddFeedFormulaRequest.is_show:type_name -> backend.operation.IsShow.Kind
+	6, // 3: backend.operation.AddFeedFormulaRequest.is_modify:type_name -> backend.operation.IsShow.Kind
+	6, // 4: backend.operation.SearchFeedFormulaRequest.is_show:type_name -> backend.operation.IsShow.Kind
+	7, // 5: backend.operation.SearchFeedFormulaRequest.pagination:type_name -> backend.operation.PaginationModel
+	0, // 6: backend.operation.SearchFeedFormulaListResponse.list:type_name -> backend.operation.AddFeedFormulaRequest
+	6, // 7: backend.operation.IsShowModifyFeedFormula.is_show:type_name -> backend.operation.IsShow.Kind
+	8, // [8:8] is the sub-list for method output_type
+	8, // [8:8] is the sub-list for method input_type
+	8, // [8:8] is the sub-list for extension type_name
+	8, // [8:8] is the sub-list for extension extendee
+	0, // [0:8] is the sub-list for field type_name
+}
+
+func init() { file_backend_operation_feed_formula_proto_init() }
+func file_backend_operation_feed_formula_proto_init() {
+	if File_backend_operation_feed_formula_proto != nil {
+		return
+	}
+	file_backend_operation_pagination_proto_init()
+	file_backend_operation_enum_proto_init()
+	if !protoimpl.UnsafeEnabled {
+		file_backend_operation_feed_formula_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} {
+			switch v := v.(*AddFeedFormulaRequest); i {
+			case 0:
+				return &v.state
+			case 1:
+				return &v.sizeCache
+			case 2:
+				return &v.unknownFields
+			default:
+				return nil
+			}
+		}
+		file_backend_operation_feed_formula_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} {
+			switch v := v.(*SearchFeedFormulaRequest); i {
+			case 0:
+				return &v.state
+			case 1:
+				return &v.sizeCache
+			case 2:
+				return &v.unknownFields
+			default:
+				return nil
+			}
+		}
+		file_backend_operation_feed_formula_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} {
+			switch v := v.(*SearchFeedFormulaListResponse); i {
+			case 0:
+				return &v.state
+			case 1:
+				return &v.sizeCache
+			case 2:
+				return &v.unknownFields
+			default:
+				return nil
+			}
+		}
+		file_backend_operation_feed_formula_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} {
+			switch v := v.(*IsShowModifyFeedFormula); i {
+			case 0:
+				return &v.state
+			case 1:
+				return &v.sizeCache
+			case 2:
+				return &v.unknownFields
+			default:
+				return nil
+			}
+		}
+	}
+	type x struct{}
+	out := protoimpl.TypeBuilder{
+		File: protoimpl.DescBuilder{
+			GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
+			RawDescriptor: file_backend_operation_feed_formula_proto_rawDesc,
+			NumEnums:      0,
+			NumMessages:   4,
+			NumExtensions: 0,
+			NumServices:   0,
+		},
+		GoTypes:           file_backend_operation_feed_formula_proto_goTypes,
+		DependencyIndexes: file_backend_operation_feed_formula_proto_depIdxs,
+		MessageInfos:      file_backend_operation_feed_formula_proto_msgTypes,
+	}.Build()
+	File_backend_operation_feed_formula_proto = out.File
+	file_backend_operation_feed_formula_proto_rawDesc = nil
+	file_backend_operation_feed_formula_proto_goTypes = nil
+	file_backend_operation_feed_formula_proto_depIdxs = nil
+}

+ 3 - 3
proto/go/backend/operation/pasture.pb.go

@@ -1527,7 +1527,7 @@ type IsShowForage struct {
 	sizeCache     protoimpl.SizeCache
 	unknownFields protoimpl.UnknownFields
 
-	ForageId uint32      `protobuf:"varint,1,opt,name=forage_id,json=forageId,proto3" json:"forage_id,omitempty"`
+	ForageId int32       `protobuf:"varint,1,opt,name=forage_id,json=forageId,proto3" json:"forage_id,omitempty"`
 	IsShow   IsShow_Kind `protobuf:"varint,2,opt,name=is_show,json=isShow,proto3,enum=backend.operation.IsShow_Kind" json:"is_show,omitempty"`
 }
 
@@ -1563,7 +1563,7 @@ func (*IsShowForage) Descriptor() ([]byte, []int) {
 	return file_backend_operation_pasture_proto_rawDescGZIP(), []int{18}
 }
 
-func (x *IsShowForage) GetForageId() uint32 {
+func (x *IsShowForage) GetForageId() int32 {
 	if x != nil {
 		return x.ForageId
 	}
@@ -2276,7 +2276,7 @@ var file_backend_operation_pasture_proto_rawDesc = []byte{
 	0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x41, 0x64, 0x64, 0x46, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x52,
 	0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x52, 0x04, 0x6c, 0x69, 0x73, 0x74, 0x22, 0x64, 0x0a, 0x0c,
 	0x49, 0x73, 0x53, 0x68, 0x6f, 0x77, 0x46, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x12, 0x1b, 0x0a, 0x09,
-	0x66, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52,
+	0x66, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x05, 0x52,
 	0x08, 0x66, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x49, 0x64, 0x12, 0x37, 0x0a, 0x07, 0x69, 0x73, 0x5f,
 	0x73, 0x68, 0x6f, 0x77, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x1e, 0x2e, 0x62, 0x61, 0x63,
 	0x6b, 0x65, 0x6e, 0x64, 0x2e, 0x6f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x49,