Parcourir la source

analysis: 栏舍体重统计图

Yi il y a 5 mois
Parent
commit
2d77a07500

+ 1 - 1
go.mod

@@ -3,7 +3,7 @@ module kpt-pasture
 go 1.17
 go 1.17
 
 
 require (
 require (
-	gitee.com/xuyiping_admin/go_proto v0.0.0-20241011085352-37867c4fa0d6
+	gitee.com/xuyiping_admin/go_proto v0.0.0-20241015015352-8c7829c17318
 	gitee.com/xuyiping_admin/pkg v0.0.0-20241010101255-0c6bd229b939
 	gitee.com/xuyiping_admin/pkg v0.0.0-20241010101255-0c6bd229b939
 	github.com/dgrijalva/jwt-go v3.2.0+incompatible
 	github.com/dgrijalva/jwt-go v3.2.0+incompatible
 	github.com/eko/gocache v1.1.0
 	github.com/eko/gocache v1.1.0

+ 6 - 0
go.sum

@@ -76,6 +76,12 @@ gitee.com/xuyiping_admin/go_proto v0.0.0-20241011072822-c977a14e2254 h1:yH8qiJU8
 gitee.com/xuyiping_admin/go_proto v0.0.0-20241011072822-c977a14e2254/go.mod h1:BKrFW6YLDectlQcQk3FYKBeXvjEiodAKJ5rq7O/QiPE=
 gitee.com/xuyiping_admin/go_proto v0.0.0-20241011072822-c977a14e2254/go.mod h1:BKrFW6YLDectlQcQk3FYKBeXvjEiodAKJ5rq7O/QiPE=
 gitee.com/xuyiping_admin/go_proto v0.0.0-20241011085352-37867c4fa0d6 h1:FnfIC2wrku60IFnSLZz1WSiYOJrfQwpNeDMepp/h8hI=
 gitee.com/xuyiping_admin/go_proto v0.0.0-20241011085352-37867c4fa0d6 h1:FnfIC2wrku60IFnSLZz1WSiYOJrfQwpNeDMepp/h8hI=
 gitee.com/xuyiping_admin/go_proto v0.0.0-20241011085352-37867c4fa0d6/go.mod h1:BKrFW6YLDectlQcQk3FYKBeXvjEiodAKJ5rq7O/QiPE=
 gitee.com/xuyiping_admin/go_proto v0.0.0-20241011085352-37867c4fa0d6/go.mod h1:BKrFW6YLDectlQcQk3FYKBeXvjEiodAKJ5rq7O/QiPE=
+gitee.com/xuyiping_admin/go_proto v0.0.0-20241012025939-3e8d9977f9bb h1:o+7xGq/Z5nud9SUXwKasysVAQvc4VoLIPVzDNvE8okg=
+gitee.com/xuyiping_admin/go_proto v0.0.0-20241012025939-3e8d9977f9bb/go.mod h1:BKrFW6YLDectlQcQk3FYKBeXvjEiodAKJ5rq7O/QiPE=
+gitee.com/xuyiping_admin/go_proto v0.0.0-20241012064018-55ade50d8afc h1:AAMP3tanWglQrrE+UITOsxyRi8W0SyEHvcqSK5MjBaQ=
+gitee.com/xuyiping_admin/go_proto v0.0.0-20241012064018-55ade50d8afc/go.mod h1:BKrFW6YLDectlQcQk3FYKBeXvjEiodAKJ5rq7O/QiPE=
+gitee.com/xuyiping_admin/go_proto v0.0.0-20241015015352-8c7829c17318 h1:u07PUyPLqJ+2Bzf9KD6bfJxp1vvXRTQG4Nq7N/J+ni8=
+gitee.com/xuyiping_admin/go_proto v0.0.0-20241015015352-8c7829c17318/go.mod h1:BKrFW6YLDectlQcQk3FYKBeXvjEiodAKJ5rq7O/QiPE=
 gitee.com/xuyiping_admin/pkg v0.0.0-20231218082641-aac597b8a015 h1:dfb5dRd57L2HKjdwLT93UFmPYFPOmEl56gtZmqcNnaE=
 gitee.com/xuyiping_admin/pkg v0.0.0-20231218082641-aac597b8a015 h1:dfb5dRd57L2HKjdwLT93UFmPYFPOmEl56gtZmqcNnaE=
 gitee.com/xuyiping_admin/pkg v0.0.0-20231218082641-aac597b8a015/go.mod h1:Fk4GYI/v0IK3XFrm1Gn+VkgCz5Y7mfswD5hsTJYOG6A=
 gitee.com/xuyiping_admin/pkg v0.0.0-20231218082641-aac597b8a015/go.mod h1:Fk4GYI/v0IK3XFrm1Gn+VkgCz5Y7mfswD5hsTJYOG6A=
 gitee.com/xuyiping_admin/pkg v0.0.0-20241008063555-121a776fd331 h1:qJcpJ3o+O7uxDqR0I9LijQmi607IKvhf4mGum/ZUPT0=
 gitee.com/xuyiping_admin/pkg v0.0.0-20241008063555-121a776fd331 h1:qJcpJ3o+O7uxDqR0I9LijQmi607IKvhf4mGum/ZUPT0=

+ 16 - 0
http/handler/analysis/analysis.go

@@ -72,5 +72,21 @@ func MatingTimeLy(c *gin.Context) {
 		apierr.ClassifiedAbort(c, err)
 		apierr.ClassifiedAbort(c, err)
 		return
 		return
 	}
 	}
+
+	c.JSON(http.StatusOK, res)
+}
+
+func PenWeight(c *gin.Context) {
+	var req pasturePb.PenWeightRequest
+	if err := ginutil.BindProto(c, &req); err != nil {
+		apierr.AbortBadRequest(c, http.StatusBadRequest, err)
+		return
+	}
+
+	res, err := middleware.Dependency(c).StoreEventHub.OpsService.PenWeight(c, &req)
+	if err != nil {
+		apierr.ClassifiedAbort(c, err)
+		return
+	}
 	ginutil.JSONResp(c, res)
 	ginutil.JSONResp(c, res)
 }
 }

+ 10 - 0
http/handler/config/config.go

@@ -51,6 +51,16 @@ func DiseaseOptions(c *gin.Context) {
 	}
 	}
 	ginutil.JSONResp(c, res)
 	ginutil.JSONResp(c, res)
 }
 }
+
+func PrescriptionOptions(c *gin.Context) {
+	res, err := middleware.Dependency(c).StoreEventHub.OpsService.PrescriptionOptions(c)
+	if err != nil {
+		apierr.ClassifiedAbort(c, err)
+		return
+	}
+	ginutil.JSONResp(c, res)
+}
+
 func BreedStatusOptions(c *gin.Context) {
 func BreedStatusOptions(c *gin.Context) {
 	res, err := middleware.Dependency(c).StoreEventHub.OpsService.BreedStatusOptions(c)
 	res, err := middleware.Dependency(c).StoreEventHub.OpsService.BreedStatusOptions(c)
 	if err != nil {
 	if err != nil {

+ 1 - 0
http/route/analysis_api.go

@@ -16,5 +16,6 @@ func AnalysisAPI(opts ...func(engine *gin.Engine)) func(s *gin.Engine) {
 		pastureRoute.POST("/growth/curve", analysis.GrowthCurve)
 		pastureRoute.POST("/growth/curve", analysis.GrowthCurve)
 		pastureRoute.POST("/weight/range", analysis.WeightRange)
 		pastureRoute.POST("/weight/range", analysis.WeightRange)
 		pastureRoute.POST("/mating/timely", analysis.MatingTimeLy)
 		pastureRoute.POST("/mating/timely", analysis.MatingTimeLy)
+		pastureRoute.POST("/pen/weight", analysis.PenWeight)
 	}
 	}
 }
 }

+ 1 - 0
http/route/config_api.go

@@ -25,5 +25,6 @@ func ConfigAPI(opts ...func(engine *gin.Engine)) func(s *gin.Engine) {
 		pastureRoute.GET("/system/base/config/options", config.SystemBaseConfigOptions)
 		pastureRoute.GET("/system/base/config/options", config.SystemBaseConfigOptions)
 		pastureRoute.GET("/disease/type/options", config.DiseaseTypeOptions)
 		pastureRoute.GET("/disease/type/options", config.DiseaseTypeOptions)
 		pastureRoute.GET("/disease/options", config.DiseaseOptions)
 		pastureRoute.GET("/disease/options", config.DiseaseOptions)
+		pastureRoute.GET("/prescription/options", config.PrescriptionOptions)
 	}
 	}
 }
 }

+ 46 - 0
model/cow.go

@@ -2,6 +2,7 @@ package model
 
 
 import (
 import (
 	"fmt"
 	"fmt"
+	"kpt-pasture/util"
 	"math"
 	"math"
 	"time"
 	"time"
 
 
@@ -26,6 +27,7 @@ type Cow struct {
 	CowKind             pasturePb.CowKind_Kind      `json:"cowKind"`
 	CowKind             pasturePb.CowKind_Kind      `json:"cowKind"`
 	BirthWeight         int64                       `json:"birthWeight"`
 	BirthWeight         int64                       `json:"birthWeight"`
 	CurrentWeight       int64                       `json:"currentWeight"`
 	CurrentWeight       int64                       `json:"currentWeight"`
+	AdmissionWeight     int64                       `json:"admissionWeight"`
 	SourceId            pasturePb.CowSource_Kind    `json:"sourceId"`
 	SourceId            pasturePb.CowSource_Kind    `json:"sourceId"`
 	FatherNumber        string                      `json:"fatherNumber"`
 	FatherNumber        string                      `json:"fatherNumber"`
 	MotherNumber        string                      `json:"motherNumber"`
 	MotherNumber        string                      `json:"motherNumber"`
@@ -92,6 +94,49 @@ func (c CowSlice) ToPB(
 	return res
 	return res
 }
 }
 
 
+func (c CowSlice) ToPB2(penMap map[int32]*Pen, penWeightSlice PenWeightSlice) []*pasturePb.CowList {
+	res := make([]*pasturePb.CowList, len(c))
+	for i, v := range c {
+		penName := ""
+		if pen, ok := penMap[v.PenId]; ok {
+			penName = pen.Name
+		}
+
+		penWeight := penWeightSlice.GetPenWeight(v.PenId)
+		lastWeightDay := util.Ceil(float64(v.LastWeightAt-v.LastSecondWeightAt) / 86400)
+		penAvgWeight := float32(0)
+		previousStageDailyWeight := float32(0)
+		cowPenAvgWeightDiffValue := float32(0)
+
+		if penWeight != nil {
+			penAvgWeight = float32(penWeight.AvgWeight) / 1000
+			cowPenAvgWeightDiffValue = float32(v.CurrentWeight-int64(penWeight.AvgWeight)) / 1000
+			if lastWeightDay > 0 {
+				previousStageDailyWeight = float32(v.CurrentWeight-v.LastSecondWeight) / 1000 / float32(lastWeightDay)
+			}
+		}
+
+		res[i] = &pasturePb.CowList{
+			CowId:                    int32(v.Id),
+			DayAge:                   v.DayAge,
+			DailyWeightGain:          float32(v.GetDayWeight()),
+			AverageDailyWeightGain:   float32(v.GetAverageDailyWeight()),
+			EarNumber:                v.EarNumber,
+			PenName:                  penName,
+			BirthAt:                  int32(v.BirthAt),
+			BirthWeight:              float32(v.BirthWeight) / 1000,
+			CurrentWeight:            float32(v.CurrentWeight) / 1000,
+			LastWeightAt:             int32(v.LastWeightAt),
+			AdmissionAge:             int32(v.AdmissionAge),
+			AdmissionWeight:          float32(v.AbortionAge) / 1000,
+			PreviousStageDailyWeight: previousStageDailyWeight,
+			PenAvgWeight:             penAvgWeight,
+			CowPenAvgWeightDiffValue: cowPenAvgWeightDiffValue,
+		}
+	}
+	return res
+}
+
 func NewCow(req *pasturePb.EventEnterRequest) *Cow {
 func NewCow(req *pasturePb.EventEnterRequest) *Cow {
 	var isPregnant = pasturePb.IsShow_No
 	var isPregnant = pasturePb.IsShow_No
 	if req.BreedStatus == pasturePb.BreedStatus_Pregnant {
 	if req.BreedStatus == pasturePb.BreedStatus_Pregnant {
@@ -113,6 +158,7 @@ func NewCow(req *pasturePb.EventEnterRequest) *Cow {
 		IsPregnant:          isPregnant,
 		IsPregnant:          isPregnant,
 		WeaningAt:           int64(req.WeaningAt),
 		WeaningAt:           int64(req.WeaningAt),
 		BirthAt:             int64(req.BirthAt),
 		BirthAt:             int64(req.BirthAt),
+		AdmissionWeight:     int64(req.Weight * 1000),
 		FirstMatingAt:       int64(req.MatingAt),
 		FirstMatingAt:       int64(req.MatingAt),
 		LastMatingAt:        int64(req.MatingAt),
 		LastMatingAt:        int64(req.MatingAt),
 		LastPregnantCheckAt: int64(req.PregnancyCheckAt),
 		LastPregnantCheckAt: int64(req.PregnancyCheckAt),

+ 3 - 3
model/disease.go

@@ -59,9 +59,9 @@ func (d DiseaseSlice) ToPB(user []*SystemUser, diseaseTypeList []*ConfigDiseaseT
 	return res
 	return res
 }
 }
 
 
-func (c DiseaseSlice) ToPB2() []*pasturePb.ConfigOptionsList {
+func (d DiseaseSlice) ToPB2() []*pasturePb.ConfigOptionsList {
-	res := make([]*pasturePb.ConfigOptionsList, len(c))
+	res := make([]*pasturePb.ConfigOptionsList, len(d))
-	for i, d := range c {
+	for i, d := range d {
 		res[i] = &pasturePb.ConfigOptionsList{
 		res[i] = &pasturePb.ConfigOptionsList{
 			Value:    int32(d.Id),
 			Value:    int32(d.Id),
 			Label:    d.Name,
 			Label:    d.Name,

+ 2 - 0
model/event_abortion.go

@@ -10,6 +10,7 @@ type EventAbortion struct {
 	Id                  int64                          `json:"id"`
 	Id                  int64                          `json:"id"`
 	CowId               int64                          `json:"cowId"`
 	CowId               int64                          `json:"cowId"`
 	Lact                int32                          `json:"lact"`
 	Lact                int32                          `json:"lact"`
+	CowType             pasturePb.CowType_Kind         `json:"cowType"`
 	DayAge              int32                          `json:"dayAge"`
 	DayAge              int32                          `json:"dayAge"`
 	PregnantAge         int32                          `json:"pregnantAge"`
 	PregnantAge         int32                          `json:"pregnantAge"`
 	AbortionAt          int64                          `json:"abortionAt"`
 	AbortionAt          int64                          `json:"abortionAt"`
@@ -32,6 +33,7 @@ func NewEventAbortion(cow *Cow, req *pasturePb.EventAbortionRequest, abortionRea
 	return &EventAbortion{
 	return &EventAbortion{
 		CowId:               int64(req.CowId),
 		CowId:               int64(req.CowId),
 		Lact:                cow.Lact,
 		Lact:                cow.Lact,
+		CowType:             cow.CowType,
 		PregnantAge:         cow.GetDaysPregnant(),
 		PregnantAge:         cow.GetDaysPregnant(),
 		DayAge:              cow.DayAge,
 		DayAge:              cow.DayAge,
 		AbortionAt:          int64(req.AbortionAt),
 		AbortionAt:          int64(req.AbortionAt),

+ 18 - 0
model/event_mating.go

@@ -160,3 +160,21 @@ func (e EventMatingSlice) ToPB2() []*pasturePb.CowList {
 	}
 	}
 	return res
 	return res
 }
 }
+
+type MatingTimelyResponse struct {
+	Code    int32             `json:"code"`
+	Message string            `json:"message"`
+	Data    *MatingTimelyData `json:"data"`
+}
+
+type MatingTimelyData struct {
+	CowList []*pasturePb.CowList `json:"cowList"`
+	Chart   *CowMatingChart      `json:"chart"`
+}
+
+type CowMatingChart struct {
+	Lact0 [][]string `json:"lact0"`
+	Lact1 [][]string `json:"lact1"`
+	Lact2 [][]string `json:"lact2"`
+	Lact3 [][]string `json:"lact3"`
+}

+ 33 - 0
model/pen.go

@@ -78,3 +78,36 @@ func (p PenSlice) ToPB2(req []*pasturePb.ConfigOptionsList) []*pasturePb.ConfigO
 	}
 	}
 	return res
 	return res
 }
 }
+
+type PenWeight struct {
+	PenId     int32 `json:"penId"`
+	CowCount  int32 `json:"cowCount"`
+	AllWeight int32 `json:"allWeight"`
+	AvgWeight int32 `json:"avgWeight"`
+}
+
+type PenWeightSlice []*PenWeight
+
+func (p PenWeightSlice) ToPB(penMap map[int32]*Pen) *pasturePb.PenWeightChart {
+	res := &pasturePb.PenWeightChart{}
+	for _, v := range p {
+		penName := ""
+		if pen, ok := penMap[v.PenId]; ok {
+			penName = pen.Name
+		}
+		res.Header = append(res.Header, penName)
+		res.AllWeight = append(res.AllWeight, v.AllWeight)
+		res.AvgWeight = append(res.AvgWeight, v.AvgWeight)
+		res.CowCount = append(res.CowCount, v.CowCount)
+	}
+	return res
+}
+
+func (p PenWeightSlice) GetPenWeight(penId int32) *PenWeight {
+	for _, v := range p {
+		if v.PenId == penId {
+			return v
+		}
+	}
+	return nil
+}

+ 12 - 0
model/prescription.go

@@ -104,3 +104,15 @@ func (p PrescriptionSlice) ToPB(prescriptionDrugsList []*PrescriptionDrugs) []*p
 	}
 	}
 	return res
 	return res
 }
 }
+
+func (p PrescriptionSlice) ToPB2() []*pasturePb.ConfigOptionsList {
+	res := make([]*pasturePb.ConfigOptionsList, len(p))
+	for i, d := range p {
+		res[i] = &pasturePb.ConfigOptionsList{
+			Value:    int32(d.Id),
+			Label:    d.Name,
+			Disabled: true,
+		}
+	}
+	return res
+}

+ 76 - 17
module/backend/analysis.go

@@ -114,7 +114,17 @@ func (s *StoreEntry) WeightRange(ctx context.Context, req *pasturePb.WeightRange
 	}
 	}
 
 
 	if len(cowWeightRange) == 0 {
 	if len(cowWeightRange) == 0 {
-		return nil, xerr.Customf("没有数据")
+		return &pasturePb.WeightRangeResponse{
+			Code:    http.StatusOK,
+			Message: "ok",
+			Data: &pasturePb.WeightRangeData{
+				CowList: make([]*pasturePb.CowList, 0),
+				WeightBarChart: &pasturePb.WeightBarChart{
+					Header: make([]string, 0),
+					Data:   make([]int32, 0),
+				},
+			},
+		}, nil
 	}
 	}
 	header := make([]string, 0)
 	header := make([]string, 0)
 	data := make([]int32, 0)
 	data := make([]int32, 0)
@@ -150,7 +160,7 @@ func (s *StoreEntry) WeightRange(ctx context.Context, req *pasturePb.WeightRange
 	}, nil
 	}, nil
 }
 }
 
 
-func (s *StoreEntry) MatingTimely(ctx context.Context, req *pasturePb.MatingTimelyRequest) (*pasturePb.MatingTimelyResponse, error) {
+func (s *StoreEntry) MatingTimely(ctx context.Context, req *pasturePb.MatingTimelyRequest) (*model.MatingTimelyResponse, error) {
 	matingTimelyChart := make([]*model.MatingTimelyChart, 0)
 	matingTimelyChart := make([]*model.MatingTimelyChart, 0)
 	sql := `SELECT calving_age,cow_type, DATE_FORMAT(FROM_UNIXTIME(reality_day), '%Y-%m-%d') AS reality_day, lact_group
 	sql := `SELECT calving_age,cow_type, DATE_FORMAT(FROM_UNIXTIME(reality_day), '%Y-%m-%d') AS reality_day, lact_group
 		FROM (
 		FROM (
@@ -179,37 +189,36 @@ func (s *StoreEntry) MatingTimely(ctx context.Context, req *pasturePb.MatingTime
 	if req.StartDayAt > 0 && req.EndDayAt > 0 {
 	if req.StartDayAt > 0 && req.EndDayAt > 0 {
 		whereSql += fmt.Sprintf("AND reality_day BETWEEN %d AND %d", req.StartDayAt, req.EndDayAt)
 		whereSql += fmt.Sprintf("AND reality_day BETWEEN %d AND %d", req.StartDayAt, req.EndDayAt)
 	}
 	}
-	if err := s.DB.Raw(sql).Find(&matingTimelyChart).Error; err != nil {
+	if err := s.DB.Raw(fmt.Sprintf("%s %s", sql, whereSql)).Find(&matingTimelyChart).Error; err != nil {
 		return nil, err
 		return nil, err
 	}
 	}
 
 
-	chart := &pasturePb.MatingTimelyChart{
+	chart := &model.CowMatingChart{
-		Lact0: make([]int32, 0),
+		Lact0: make([][]string, 0),
-		Lact1: make([]int32, 0),
+		Lact1: make([][]string, 0),
-		Lact2: make([]int32, 0),
+		Lact2: make([][]string, 0),
-		Lact3: make([]int32, 0),
+		Lact3: make([][]string, 0),
 	}
 	}
 	if len(matingTimelyChart) == 0 {
 	if len(matingTimelyChart) == 0 {
-		return &pasturePb.MatingTimelyResponse{
+		return &model.MatingTimelyResponse{
 			Code:    http.StatusOK,
 			Code:    http.StatusOK,
 			Message: "ok",
 			Message: "ok",
-			Data: &pasturePb.MatingTimelyData{
+			Data: &model.MatingTimelyData{
 				CowList: make([]*pasturePb.CowList, 0),
 				CowList: make([]*pasturePb.CowList, 0),
 				Chart:   chart,
 				Chart:   chart,
 			},
 			},
 		}, nil
 		}, nil
 	}
 	}
-
 	for _, v := range matingTimelyChart {
 	for _, v := range matingTimelyChart {
 		switch v.LactGroup {
 		switch v.LactGroup {
 		case "0":
 		case "0":
-			chart.Lact0 = append(chart.Lact0, v.CalvingAge)
+			chart.Lact0 = append(chart.Lact0, []string{fmt.Sprintf("%d", v.CalvingAge), v.RealityDay})
 		case "1":
 		case "1":
-			chart.Lact1 = append(chart.Lact1, v.CalvingAge)
+			chart.Lact1 = append(chart.Lact1, []string{fmt.Sprintf("%d", v.CalvingAge), v.RealityDay})
 		case "2":
 		case "2":
-			chart.Lact2 = append(chart.Lact2, v.CalvingAge)
+			chart.Lact2 = append(chart.Lact2, []string{fmt.Sprintf("%d", v.CalvingAge), v.RealityDay})
 		case "3+":
 		case "3+":
-			chart.Lact3 = append(chart.Lact3, v.CalvingAge)
+			chart.Lact3 = append(chart.Lact3, []string{fmt.Sprintf("%d", v.CalvingAge), v.RealityDay})
 		}
 		}
 	}
 	}
 
 
@@ -229,12 +238,62 @@ func (s *StoreEntry) MatingTimely(ctx context.Context, req *pasturePb.MatingTime
 		return nil, err
 		return nil, err
 	}
 	}
 
 
-	return &pasturePb.MatingTimelyResponse{
+	return &model.MatingTimelyResponse{
 		Code:    http.StatusOK,
 		Code:    http.StatusOK,
 		Message: "ok",
 		Message: "ok",
-		Data: &pasturePb.MatingTimelyData{
+		Data: &model.MatingTimelyData{
 			CowList: model.EventMatingSlice(eventMatingList).ToPB2(),
 			CowList: model.EventMatingSlice(eventMatingList).ToPB2(),
 			Chart:   chart,
 			Chart:   chart,
 		},
 		},
 	}, nil
 	}, nil
 }
 }
+
+func (s *StoreEntry) PenWeight(ctx context.Context, req *pasturePb.PenWeightRequest) (*pasturePb.PenWeightResponse, error) {
+	penWeightList := make([]*model.PenWeight, 0)
+	if err := s.DB.Model(new(model.Cow)).
+		Select(`
+			pen_id,
+			CEILING(AVG(current_weight) / 1000 ) AS avg_weight,
+			CEILING(SUM(current_weight) / 1000 ) AS all_weight,
+			COUNT(*) AS cow_count`,
+		).Where("is_remove = ?", pasturePb.IsShow_Ok).
+		Group("pen_id").
+		Find(&penWeightList).Error; err != nil {
+		return nil, err
+	}
+
+	chart := &pasturePb.PenWeightChart{
+		Header:    make([]string, 0),
+		AllWeight: make([]int32, 0),
+		AvgWeight: make([]int32, 0),
+		CowCount:  make([]int32, 0),
+	}
+	if len(penWeightList) == 0 {
+		return &pasturePb.PenWeightResponse{
+			Code:    http.StatusOK,
+			Message: "ok",
+			Data: &pasturePb.PenWeightData{
+				CowList: make([]*pasturePb.CowList, 0),
+				Chart:   chart,
+			},
+		}, nil
+	}
+	cowList := make([]*model.Cow, 0)
+	pref := s.DB.Model(new(model.Cow)).
+		Where("is_remove = ?", pasturePb.IsShow_Ok)
+	if err := pref.Where("pen_id IN ?", req.PenId).
+		Order("pen_id").
+		Find(&cowList).Error; err != nil {
+		return nil, xerr.WithStack(err)
+	}
+
+	penMap := s.PenMap(ctx)
+	return &pasturePb.PenWeightResponse{
+		Code:    http.StatusOK,
+		Message: "ok",
+		Data: &pasturePb.PenWeightData{
+			CowList: model.CowSlice(cowList).ToPB2(penMap, penWeightList),
+			Chart:   model.PenWeightSlice(penWeightList).ToPB(penMap),
+		},
+	}, nil
+}

+ 15 - 0
module/backend/enum_options.go

@@ -67,6 +67,21 @@ func (s *StoreEntry) DiseaseOptions(ctx context.Context) (*pasturePb.ConfigOptio
 	}, nil
 	}, nil
 }
 }
 
 
+func (s *StoreEntry) PrescriptionOptions(ctx context.Context) (*pasturePb.ConfigOptionsListResponse, error) {
+	prescriptionList := make([]*model.Prescription, 0)
+	pref := s.DB.Table(new(model.Prescription).TableName()).
+		Where("is_show =? ", pasturePb.IsShow_Ok)
+
+	if err := pref.Find(&prescriptionList).Error; err != nil {
+		return nil, xerr.WithStack(err)
+	}
+	return &pasturePb.ConfigOptionsListResponse{
+		Code:    http.StatusOK,
+		Message: "ok",
+		Data:    model.PrescriptionSlice(prescriptionList).ToPB2(),
+	}, nil
+}
+
 func (s *StoreEntry) BreedStatusOptions(ctx context.Context) (*pasturePb.ConfigOptionsListResponse, error) {
 func (s *StoreEntry) BreedStatusOptions(ctx context.Context) (*pasturePb.ConfigOptionsListResponse, error) {
 	return &pasturePb.ConfigOptionsListResponse{
 	return &pasturePb.ConfigOptionsListResponse{
 		Code:    http.StatusOK,
 		Code:    http.StatusOK,

+ 4 - 1
module/backend/interface.go

@@ -3,6 +3,7 @@ package backend
 import (
 import (
 	"context"
 	"context"
 	"kpt-pasture/config"
 	"kpt-pasture/config"
+	"kpt-pasture/model"
 	"kpt-pasture/service/asynqsvc"
 	"kpt-pasture/service/asynqsvc"
 	"kpt-pasture/service/wechat"
 	"kpt-pasture/service/wechat"
 	"kpt-pasture/store/kptstore"
 	"kpt-pasture/store/kptstore"
@@ -142,6 +143,7 @@ type ConfigDataService interface {
 	SystemBaseConfigOptions(ctx context.Context, optionName string) (*pasturePb.ConfigOptionsListResponse, error)
 	SystemBaseConfigOptions(ctx context.Context, optionName string) (*pasturePb.ConfigOptionsListResponse, error)
 	DiseaseTypeOptions(ctx context.Context) (*pasturePb.ConfigOptionsListResponse, error)
 	DiseaseTypeOptions(ctx context.Context) (*pasturePb.ConfigOptionsListResponse, error)
 	DiseaseOptions(ctx context.Context) (*pasturePb.ConfigOptionsListResponse, error)
 	DiseaseOptions(ctx context.Context) (*pasturePb.ConfigOptionsListResponse, error)
+	PrescriptionOptions(ctx context.Context) (*pasturePb.ConfigOptionsListResponse, error)
 }
 }
 
 
 //go:generate mockgen -destination mock/EventService.go -package kptservicemock kpt-pasture/module/backend EventService
 //go:generate mockgen -destination mock/EventService.go -package kptservicemock kpt-pasture/module/backend EventService
@@ -205,7 +207,8 @@ type GoodsService interface {
 type AnalyseService interface {
 type AnalyseService interface {
 	GrowthCurve(ctx context.Context, req *pasturePb.SearchGrowthCurvesRequest) (*pasturePb.GrowthCurvesResponse, error)
 	GrowthCurve(ctx context.Context, req *pasturePb.SearchGrowthCurvesRequest) (*pasturePb.GrowthCurvesResponse, error)
 	WeightRange(ctx context.Context, req *pasturePb.WeightRangeRequest) (*pasturePb.WeightRangeResponse, error)
 	WeightRange(ctx context.Context, req *pasturePb.WeightRangeRequest) (*pasturePb.WeightRangeResponse, error)
-	MatingTimely(ctx context.Context, req *pasturePb.MatingTimelyRequest) (*pasturePb.MatingTimelyResponse, error)
+	MatingTimely(ctx context.Context, req *pasturePb.MatingTimelyRequest) (*model.MatingTimelyResponse, error)
+	PenWeight(ctx context.Context, req *pasturePb.PenWeightRequest) (*pasturePb.PenWeightResponse, error)
 }
 }
 
 
 //go:generate mockgen -destination mock/DashboardService.go -package kptservicemock kpt-pasture/module/backend DashboardService
 //go:generate mockgen -destination mock/DashboardService.go -package kptservicemock kpt-pasture/module/backend DashboardService

+ 14 - 0
util/util.go

@@ -2,6 +2,7 @@ package util
 
 
 import (
 import (
 	"fmt"
 	"fmt"
+	"math"
 	"time"
 	"time"
 )
 )
 
 
@@ -58,3 +59,16 @@ func GetMonthRemainDay() int {
 	lastDayOfMonth := time.Date(now.Year(), now.Month()+1, 0, 23, 59, 59, 999999999, now.Location())
 	lastDayOfMonth := time.Date(now.Year(), now.Month()+1, 0, 23, 59, 59, 999999999, now.Location())
 	return int(lastDayOfMonth.Sub(now).Hours()/24) + 1
 	return int(lastDayOfMonth.Sub(now).Hours()/24) + 1
 }
 }
+
+// Ceil 向上取整函数
+func Ceil(x float64) float64 {
+	// 使用 math.Floor 计算小于或等于 x 的最大整数
+	// 然后检查 x 是否为整数,如果不是,则结果加 1
+	// 注意:math.Floor 返回的是 float64 类型,所以我们需要进行比较
+	// 来确定是否需要加 1
+	intPart := math.Floor(x)
+	if x-intPart > 0 {
+		return intPart + 1
+	}
+	return intPart
+}