Browse Source

analysis: CowBehaviorDistribution 牛只行为曲线分布

Yi 1 month ago
parent
commit
46733c5c23

+ 18 - 18
config/app.develop.yaml

@@ -36,25 +36,25 @@ side_work_setting:
       default: 5
       default: 5
 cron:
 cron:
   crontab_start_run: false
   crontab_start_run: false
-  update_cow_info: "0 01 1 * * ?"       # 每天凌晨1点01分执行
-  indicators: "0 30 11 * * ?"            # 每天凌晨1点03分执行
-  generate_work_order: "0 05 1 * * ?"   # 每天凌晨1点05分执行
-  immunization_plan: "0 10 1 * * ?"     # 每天凌晨1点10分执行
-  same_time_plan: "0 15 1 * * ?"        # 每天凌晨1点15分执行
-  update_same_time: "0 20 1 * * ?"      # 每天凌晨1点20分执行
-  system_basic_crontab: "0 25 1 * * ?"  # 每天凌晨1点25分执行
+  update_cow_info: "0 01 1 * * ?"
+  indicators: "0 0 23 * * ?"            # 每天凌晨1点03分执行
+  generate_work_order: "0 05 1 * * ?"
+  immunization_plan: "0 10 1 * * ?"
+  same_Time_plan: "0 15 1 * * ?"
+  update_same_time: "0 20 1 * * ?"
   delete_old_original: "0 30 1 * * ?"   # 每天凌晨1点30分执行
   delete_old_original: "0 30 1 * * ?"   # 每天凌晨1点30分执行
-  update_disease_to_calendar: "0 05 1 * * ?" # 每天凌晨1点05分执行
-  cow_pregnant: "0 00 15 * * ?"         # 每天15点执行
-  neck_ring_estrus: "0 45 * * * ?"      # 更新脖环发情数据
-  neck_ring_merge: "*/5 * * * * ?"      # 合并脖环原始2小时数据(5分钟)
-  neck_ring_calculate: "*/10 * * * * ?" # 计算脖环数据
-  neck_ring_estrus_warning: "*/50 * * * * ?"   # 脖环预警(每50分钟执行一次
-  neck_ring_health_warning: "*/50 * * * * ?"   # 脖环预警(每50分钟执行一次
-  update_pen_behavior: "0 45 * * * ?"  # 更新栏舍行行为数据
-  update_pen_behavior_daily: "0 05 2 * * ?"  # 更新栏舍饲养监测数据
-  update_milk_original: "0 */30 * * * ?"     # 更新奶厅原始数据(每30分钟执行一次
-  insert_milk_daily: "0 05 2 * * ?"          # 更新每日奶量和活动量数据
+  system_basic_crontab: "0 35 1 * * ?"
+  update_disease_to_calendar: "0 50 1 * * ?"
+  cow_pregnant: "0 01 15 * * ?"
+  neck_ring_estrus: "0 45 * * * *"     # 每小时的45分钟执行一次
+  neck_ring_merge: "*/60 * * * * ?"      # 合并脖环原始2小时数据(5分钟)
+  neck_ring_calculate: "*/300 * * * * ?"  # 计算脖环数据
+  neck_ring_estrus_warning: "* */30 * * * ?"   # 脖环发情预警(每50分钟执行一次
+  neck_ring_health_warning: "* */30 * * * ?"   # 脖环健康预警
+  update_pen_behavior: "0 */20 * * * ?"        # 更新栏舍行为数据(20分钟执行一次)
+  update_pen_behavior_daily: "0 50 15 * * ?"  # 更新栏舍饲养监测数据
+  update_milk_original: "0 */30 * * * ?"     # 更新奶厅原始数据(每30分钟执行一次)
+  insert_milk_daily: "0 05 02 * * ?"          # 更新每日奶量和活动量数据
 
 
 mqtt:
 mqtt:
   broker: "kptyun.com:1983"
   broker: "kptyun.com:1983"

+ 15 - 11
config/app.test.yaml

@@ -26,20 +26,24 @@ cache_key_suffix: "gmym"
 cron:
 cron:
   crontab_start_run: false
   crontab_start_run: false
   update_cow_info: "0 01 1 * * ?"
   update_cow_info: "0 01 1 * * ?"
-  indicators: "0 24 10 * * ?"
+  indicators: "0 0 23 * * ?"            # 每天凌晨1点03分执行
   generate_work_order: "0 05 1 * * ?"
   generate_work_order: "0 05 1 * * ?"
   immunization_plan: "0 10 1 * * ?"
   immunization_plan: "0 10 1 * * ?"
-  same_time_plan: "0 15 1 * * ?"
+  same_Time_plan: "0 15 1 * * ?"
   update_same_time: "0 20 1 * * ?"
   update_same_time: "0 20 1 * * ?"
-  system_basic_crontab: "0 25 1 * * ?"
-  cow_pregnant: "0 00 15 * * ?"
-  neck_ring_estrus: "0 45 * * * ?"      # 更新脖环发情数据
-  neck_ring_merge: "*/5 * * * * ?"      # 合并脖环原始2小时数据(5分钟)
-  neck_ring_calculate: "*/10 * * * * ?"  # 计算脖环数据
-  neck_ring_estrus_warning: "*/50 * * * * ?"   # 脖环预警(每50分钟执行一次
-  neck_ring_health_warning: "*/50 * * * * ?"   # 脖环预警(每50分钟执行一次
-  update_pen_behavior: "0 45 * * * ?"  # 更新栏舍行行为数据
-  update_pen_behavior_daily: "0 05 2 * * ?"  # 更新栏舍饲养监测数据
+  delete_old_original: "0 30 1 * * ?"   # 每天凌晨1点30分执行
+  system_basic_crontab: "0 35 1 * * ?"
+  update_disease_to_calendar: "0 50 1 * * ?"
+  cow_pregnant: "0 01 15 * * ?"
+  neck_ring_estrus: "0 45 * * * *"     # 每小时的45分钟执行一次
+  neck_ring_merge: "*/60 * * * * ?"      # 合并脖环原始2小时数据(5分钟)
+  neck_ring_calculate: "*/300 * * * * ?"  # 计算脖环数据
+  neck_ring_estrus_warning: "* */30 * * * ?"   # 脖环发情预警(每50分钟执行一次
+  neck_ring_health_warning: "* */30 * * * ?"   # 脖环健康预警
+  update_pen_behavior: "0 */20 * * * ?"        # 更新栏舍行为数据(20分钟执行一次)
+  update_pen_behavior_daily: "0 50 15 * * ?"  # 更新栏舍饲养监测数据
+  update_milk_original: "0 */30 * * * ?"     # 更新奶厅原始数据(每30分钟执行一次)
+  insert_milk_daily: "0 05 02 * * ?"          # 更新每日奶量和活动量数据
 
 
 mqtt:
 mqtt:
   broker: "kptyun.com:1983"
   broker: "kptyun.com:1983"

+ 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-20250425013204-c2e3c2cb22b5
+	gitee.com/xuyiping_admin/go_proto v0.0.0-20250425035842-15ee838b15e8
 	gitee.com/xuyiping_admin/pkg v0.0.0-20241108060137-caea58c59f5b
 	gitee.com/xuyiping_admin/pkg v0.0.0-20241108060137-caea58c59f5b
 	github.com/dgrijalva/jwt-go v3.2.0+incompatible
 	github.com/dgrijalva/jwt-go v3.2.0+incompatible
 	github.com/eclipse/paho.mqtt.golang v1.4.3
 	github.com/eclipse/paho.mqtt.golang v1.4.3

+ 6 - 0
go.sum

@@ -64,6 +64,12 @@ gitee.com/xuyiping_admin/go_proto v0.0.0-20250425011443-a83a84c81b7d h1:/2tQo6Ln
 gitee.com/xuyiping_admin/go_proto v0.0.0-20250425011443-a83a84c81b7d/go.mod h1:BKrFW6YLDectlQcQk3FYKBeXvjEiodAKJ5rq7O/QiPE=
 gitee.com/xuyiping_admin/go_proto v0.0.0-20250425011443-a83a84c81b7d/go.mod h1:BKrFW6YLDectlQcQk3FYKBeXvjEiodAKJ5rq7O/QiPE=
 gitee.com/xuyiping_admin/go_proto v0.0.0-20250425013204-c2e3c2cb22b5 h1:9HdkH0TLT69nYrSpVNpK+AqRsEj6zsSAI0wyn3txEJo=
 gitee.com/xuyiping_admin/go_proto v0.0.0-20250425013204-c2e3c2cb22b5 h1:9HdkH0TLT69nYrSpVNpK+AqRsEj6zsSAI0wyn3txEJo=
 gitee.com/xuyiping_admin/go_proto v0.0.0-20250425013204-c2e3c2cb22b5/go.mod h1:BKrFW6YLDectlQcQk3FYKBeXvjEiodAKJ5rq7O/QiPE=
 gitee.com/xuyiping_admin/go_proto v0.0.0-20250425013204-c2e3c2cb22b5/go.mod h1:BKrFW6YLDectlQcQk3FYKBeXvjEiodAKJ5rq7O/QiPE=
+gitee.com/xuyiping_admin/go_proto v0.0.0-20250425022013-aea428d7468b h1:HsSfifHMvaNok/W1Q22axMJuYDSkpFnpFAIoO4Qo0NA=
+gitee.com/xuyiping_admin/go_proto v0.0.0-20250425022013-aea428d7468b/go.mod h1:BKrFW6YLDectlQcQk3FYKBeXvjEiodAKJ5rq7O/QiPE=
+gitee.com/xuyiping_admin/go_proto v0.0.0-20250425025152-491564f7b245 h1:WSBs/sMmKJBm+GBlFrvlAy9hYDgnxOSo9sFFsqdkvpo=
+gitee.com/xuyiping_admin/go_proto v0.0.0-20250425025152-491564f7b245/go.mod h1:BKrFW6YLDectlQcQk3FYKBeXvjEiodAKJ5rq7O/QiPE=
+gitee.com/xuyiping_admin/go_proto v0.0.0-20250425035842-15ee838b15e8 h1:8EsgPwBQn44Ntxxk8Ysat0IAAlFaEGwl/fszj38I+wc=
+gitee.com/xuyiping_admin/go_proto v0.0.0-20250425035842-15ee838b15e8/go.mod h1:BKrFW6YLDectlQcQk3FYKBeXvjEiodAKJ5rq7O/QiPE=
 gitee.com/xuyiping_admin/pkg v0.0.0-20241108060137-caea58c59f5b h1:w05MxH7yqveRlaRbxHhbif5YjPrJFodRPfOjYhXn7Zk=
 gitee.com/xuyiping_admin/pkg v0.0.0-20241108060137-caea58c59f5b h1:w05MxH7yqveRlaRbxHhbif5YjPrJFodRPfOjYhXn7Zk=
 gitee.com/xuyiping_admin/pkg v0.0.0-20241108060137-caea58c59f5b/go.mod h1:8tF25X6pE9WkFCczlNAC0K2mrjwKvhhp02I7o0HtDxY=
 gitee.com/xuyiping_admin/pkg v0.0.0-20241108060137-caea58c59f5b/go.mod h1:8tF25X6pE9WkFCczlNAC0K2mrjwKvhhp02I7o0HtDxY=
 github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
 github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=

+ 50 - 1
http/handler/analysis/analysis.go

@@ -336,6 +336,31 @@ func PenBehaviorAnalysis(c *gin.Context) {
 		return
 		return
 	}
 	}
 
 
+	ginutil.JSONResp(c, res)
+}
+
+func PenBehaviorAnalysis2(c *gin.Context) {
+	var req pasturePb.BarnBehaviorCurveRequest
+	if err := ginutil.BindProto(c, &req); err != nil {
+		apierr.AbortBadRequest(c, http.StatusBadRequest, err)
+		return
+	}
+
+	if err := valid.ValidateStruct(&req,
+		valid.Field(&req.StartAt, valid.Required),
+		valid.Field(&req.EndAt, valid.Required),
+		valid.Field(&req.PenId, valid.Required),
+	); err != nil {
+		apierr.AbortBadRequest(c, http.StatusBadRequest, err)
+		return
+	}
+
+	res, err := middleware.Dependency(c).StoreEventHub.OpsService.PenBehavior2(c, &req)
+	if err != nil {
+		apierr.ClassifiedAbort(c, err)
+		return
+	}
+
 	c.JSON(http.StatusOK, res)
 	c.JSON(http.StatusOK, res)
 }
 }
 
 
@@ -360,5 +385,29 @@ func PenBehaviorDaily(c *gin.Context) {
 		return
 		return
 	}
 	}
 
 
-	c.JSON(http.StatusOK, res)
+	ginutil.JSONResp(c, res)
+}
+
+func CowBehaviorDistribution(c *gin.Context) {
+	var req pasturePb.CowBehaviorDistributionRequest
+	if err := ginutil.BindProto(c, &req); err != nil {
+		apierr.AbortBadRequest(c, http.StatusBadRequest, err)
+		return
+	}
+
+	if err := valid.ValidateStruct(&req,
+		valid.Field(&req.DateTime, valid.Required),
+		valid.Field(&req.BehaviorKind, valid.Required),
+	); err != nil {
+		apierr.AbortBadRequest(c, http.StatusBadRequest, err)
+		return
+	}
+
+	res, err := middleware.Dependency(c).StoreEventHub.OpsService.CowBehaviorDistribution(c, &req)
+	if err != nil {
+		apierr.ClassifiedAbort(c, err)
+		return
+	}
+
+	ginutil.JSONResp(c, res)
 }
 }

+ 4 - 2
http/route/analysis_api.go

@@ -27,7 +27,9 @@ func AnalysisAPI(opts ...func(engine *gin.Engine)) func(s *gin.Engine) {
 		analysisRoute.POST("/single/factor/pregnant/report", analysis.SingleFactorInfantSurvivalRate) // 单因素受胎率
 		analysisRoute.POST("/single/factor/pregnant/report", analysis.SingleFactorInfantSurvivalRate) // 单因素受胎率
 		analysisRoute.POST("/multi/factor/pregnant/report", analysis.MultiFactorInfantSurvivalRate)   // 多因素受胎率
 		analysisRoute.POST("/multi/factor/pregnant/report", analysis.MultiFactorInfantSurvivalRate)   // 多因素受胎率
 
 
-		analysisRoute.POST("/pen/behavior", analysis.PenBehaviorAnalysis)      // 栏舍行为数据
-		analysisRoute.POST("/pen/behavior/monitor", analysis.PenBehaviorDaily) // 栏舍饲喂监测
+		analysisRoute.POST("/pen/behavior", analysis.PenBehaviorAnalysis)                  // 栏舍行为数据
+		analysisRoute.POST("/pen/behavior2", analysis.PenBehaviorAnalysis2)                // 栏舍行为数据
+		analysisRoute.POST("/pen/behavior/monitor", analysis.PenBehaviorDaily)             // 栏舍饲喂监测
+		analysisRoute.POST("/cow/behavior/distribution", analysis.CowBehaviorDistribution) // 牛只行为分布
 	}
 	}
 }
 }

+ 7 - 1
model/cow.go

@@ -703,7 +703,13 @@ func (c *Cow) GetAbortionAge() int32 {
 
 
 // GetLactationAge 泌乳天数
 // GetLactationAge 泌乳天数
 func (c *Cow) GetLactationAge() int32 {
 func (c *Cow) GetLactationAge() int32 {
-	if c.MilkKind == pasturePb.CowMilk_Lactation {
+	if c.LastCalvingAt <= 0 {
+		return 0
+	}
+
+	if c.MilkKind == pasturePb.CowMilk_Lactation ||
+		c.BreedStatus == pasturePb.BreedStatus_Calving ||
+		c.BreedStatus == pasturePb.BreedStatus_Abort {
 		return int32(math.Floor(float64(time.Now().Local().Unix()-c.LastCalvingAt) / 86400))
 		return int32(math.Floor(float64(time.Now().Local().Unix()-c.LastCalvingAt) / 86400))
 	}
 	}
 	return c.LactationAge
 	return c.LactationAge

+ 61 - 0
model/pen_behavior.go

@@ -1,6 +1,7 @@
 package model
 package model
 
 
 import (
 import (
+	"fmt"
 	"kpt-pasture/util"
 	"kpt-pasture/util"
 
 
 	pasturePb "gitee.com/xuyiping_admin/go_proto/proto/go/backend/cow"
 	pasturePb "gitee.com/xuyiping_admin/go_proto/proto/go/backend/cow"
@@ -63,6 +64,12 @@ func NewPenBehavior(data *PenBehaviorData, activeTime string) *PenBehavior {
 
 
 type PenBehaviorSlice []*PenBehavior
 type PenBehaviorSlice []*PenBehavior
 
 
+type BarnBehaviorCurveResponse struct {
+	Code int32                  `json:"code"`
+	Msg  string                 `json:"msg"`
+	Data *BarnBehaviorCurveItem `json:"data"`
+}
+
 func (p PenBehaviorSlice) ToPB() *pasturePb.BarnBehaviorCurveItem {
 func (p PenBehaviorSlice) ToPB() *pasturePb.BarnBehaviorCurveItem {
 	res := &pasturePb.BarnBehaviorCurveItem{
 	res := &pasturePb.BarnBehaviorCurveItem{
 		EventTime: &pasturePb.EventTime{
 		EventTime: &pasturePb.EventTime{
@@ -113,3 +120,57 @@ type PenBehaviorData struct {
 	RestRate   int32  `json:"restRate"`
 	RestRate   int32  `json:"restRate"`
 	GaspRate   int32  `json:"gaspRate"`
 	GaspRate   int32  `json:"gaspRate"`
 }
 }
+
+type BarnBehaviorCurveItem struct {
+	EventTime     *EventTime `json:"eventTime"`
+	DateTime      []string   `json:"dateTime"`
+	Headers       []string   `json:"headers"`
+	Rumina        []int32    `json:"rumina"`
+	Intake        []int32    `json:"intake"`
+	Rest          []int32    `json:"rest"`
+	WeekAvgRumina [][]string `json:"weekAvgRumina"`
+	WeekAvgIntake [][]string `json:"weekAvgIntake"`
+	WeekAvgReset  [][]string `json:"weekAvgReset"`
+}
+
+type EventTime struct {
+	FeedTime []string `json:"feedTime"`
+	MilkTime []string `json:"milkTime"`
+}
+
+func (p PenBehaviorSlice) ToPB2() *BarnBehaviorCurveItem {
+	res := &BarnBehaviorCurveItem{
+		EventTime: &EventTime{
+			FeedTime: make([]string, 0),
+			MilkTime: make([]string, 0),
+		},
+		DateTime:      make([]string, 0),
+		Rumina:        make([]int32, 0),
+		Intake:        make([]int32, 0),
+		Rest:          make([]int32, 0),
+		WeekAvgRumina: make([][]string, 0),
+		WeekAvgIntake: make([][]string, 0),
+		WeekAvgReset:  make([][]string, 0),
+	}
+
+	for i, v := range p {
+		dateTime := ""
+		if v.ActiveTime != "" {
+			dt, _ := util.TimeParseLocal(LayoutTime, v.ActiveTime)
+			dateTime = dt.Format(LayoutMinute)
+		}
+		res.DateTime = append(res.DateTime, dateTime)
+		res.Rumina = append(res.Rumina, v.RuminaRate)
+		res.Intake = append(res.Intake, v.IntakeRate)
+		res.Rest = append(res.Rest, v.RestRate)
+		weekRuminaRate, weekIntakeRate, weekRestRate := make([]string, 0), make([]string, 0), make([]string, 0)
+		weekRuminaRate = append(weekRuminaRate, v.ActiveTime, fmt.Sprintf("%d", v.WeekRuminaRate), fmt.Sprintf("%d", v.PenId), v.HeatDate, fmt.Sprintf("%d", v.Frameid), fmt.Sprintf("%d", v.RuminaStd))
+		weekRuminaRate = append(weekRuminaRate, v.ActiveTime, fmt.Sprintf("%d", v.WeekIntakeRate), fmt.Sprintf("%d", v.PenId), v.HeatDate, fmt.Sprintf("%d", v.Frameid), fmt.Sprintf("%d", v.IntakeStd))
+		weekRestRate = append(weekRestRate, v.ActiveTime, fmt.Sprintf("%d", v.WeekRestRate), fmt.Sprintf("%d", v.PenId), v.HeatDate, fmt.Sprintf("%d", v.Frameid), fmt.Sprintf("%d", v.RestStd))
+		res.WeekAvgRumina[i] = append(res.WeekAvgRumina[i], weekRuminaRate...)
+		res.WeekAvgIntake[i] = append(res.WeekAvgIntake[i], weekIntakeRate...)
+		res.WeekAvgReset[i] = append(res.WeekAvgReset[i], weekRestRate...)
+	}
+
+	return res
+}

+ 8 - 8
model/pen_behavior_day.go

@@ -9,15 +9,15 @@ type PenBehaviorDay struct {
 	PenId       int32   `json:"penId"`
 	PenId       int32   `json:"penId"`
 	PenName     string  `json:"penName"`
 	PenName     string  `json:"penName"`
 	CowCount    int32   `json:"cowCount"`
 	CowCount    int32   `json:"cowCount"`
-	DayAvgMilk  float32 `json:"dayAvgMilk"`
+	DayMilk     float32 `json:"dayMilk"`
 	DayHigh     int32   `json:"dayHigh"`
 	DayHigh     int32   `json:"dayHigh"`
 	DayRumina   int32   `json:"dayRumina"`
 	DayRumina   int32   `json:"dayRumina"`
 	DayIntake   int32   `json:"dayIntake"`
 	DayIntake   int32   `json:"dayIntake"`
 	DayInactive int32   `json:"dayInactive"`
 	DayInactive int32   `json:"dayInactive"`
 	DayGasp     int32   `json:"dayGasp"`
 	DayGasp     int32   `json:"dayGasp"`
 	DayActive   int32   `json:"dayActive"`
 	DayActive   int32   `json:"dayActive"`
-	WeekAvgMilk float32 `json:"weekAvgMilk"`
-	RuminaStd   int32   `json:"ruminaStd"`
+	WeekMilk    float32 `json:"weekMilk"`
+	RuminaStd   float64 `json:"ruminaStd"`
 	CreatedAt   int64   `json:"createdAt"`
 	CreatedAt   int64   `json:"createdAt"`
 	UpdatedAt   int64   `json:"updatedAt"`
 	UpdatedAt   int64   `json:"updatedAt"`
 }
 }
@@ -28,21 +28,21 @@ func (p *PenBehaviorDay) TableName() string {
 
 
 func NewPenBehaviorDay(pastureId int64, heatDate string, penId int32, penName string, cowCount int32, dayAvgMilk float32,
 func NewPenBehaviorDay(pastureId int64, heatDate string, penId int32, penName string, cowCount int32, dayAvgMilk float32,
 	dayHigh int32, dayRumina int32, dayIntake int32, dayInactive int32, dayGasp int32, dayActive int32, weekAvgMilk float32,
 	dayHigh int32, dayRumina int32, dayIntake int32, dayInactive int32, dayGasp int32, dayActive int32, weekAvgMilk float32,
-	ruminaStd int32) *PenBehaviorDay {
+	ruminaStd float64) *PenBehaviorDay {
 	return &PenBehaviorDay{
 	return &PenBehaviorDay{
 		PastureId:   pastureId,
 		PastureId:   pastureId,
 		HeatDate:    heatDate,
 		HeatDate:    heatDate,
 		PenId:       penId,
 		PenId:       penId,
 		PenName:     penName,
 		PenName:     penName,
 		CowCount:    cowCount,
 		CowCount:    cowCount,
-		DayAvgMilk:  dayAvgMilk,
+		DayMilk:     dayAvgMilk,
 		DayHigh:     dayHigh,
 		DayHigh:     dayHigh,
 		DayRumina:   dayRumina,
 		DayRumina:   dayRumina,
 		DayIntake:   dayIntake,
 		DayIntake:   dayIntake,
 		DayInactive: dayInactive,
 		DayInactive: dayInactive,
 		DayGasp:     dayGasp,
 		DayGasp:     dayGasp,
 		DayActive:   dayActive,
 		DayActive:   dayActive,
-		WeekAvgMilk: weekAvgMilk,
+		WeekMilk:    weekAvgMilk,
 		RuminaStd:   ruminaStd,
 		RuminaStd:   ruminaStd,
 	}
 	}
 }
 }
@@ -62,7 +62,7 @@ type PenBehaviorDayModel struct {
 	DayChew       int32   `json:"dayChew"`
 	DayChew       int32   `json:"dayChew"`
 	DayActive     int32   `json:"dayActive"`
 	DayActive     int32   `json:"dayActive"`
 	DayImmobility int32   `json:"dayImmobility"`
 	DayImmobility int32   `json:"dayImmobility"`
-	RuminaStd     int32   `json:"ruminaStd"`
+	RuminaStd     float64 `json:"ruminaStd"`
 }
 }
 
 
 type PenBehaviorDayModelSlice []*PenBehaviorDayModel
 type PenBehaviorDayModelSlice []*PenBehaviorDayModel
@@ -88,7 +88,7 @@ func (p PenBehaviorDayModelSlice) ToPB(dataTimeRange []string) *pasturePb.BarnMo
 				res.DayIntake = append(res.DayIntake, v.DayIntake)
 				res.DayIntake = append(res.DayIntake, v.DayIntake)
 				res.DayImmobility = append(res.DayImmobility, v.DayImmobility)
 				res.DayImmobility = append(res.DayImmobility, v.DayImmobility)
 				res.DayChew = append(res.DayChew, v.DayChew)
 				res.DayChew = append(res.DayChew, v.DayChew)
-				res.DayStd = append(res.DayStd, v.RuminaStd)
+				res.DayStd = append(res.DayStd, int32(v.RuminaStd))
 			}
 			}
 		}
 		}
 	}
 	}

+ 203 - 0
module/backend/analysis_more.go

@@ -2,9 +2,11 @@ package backend
 
 
 import (
 import (
 	"context"
 	"context"
+	"fmt"
 	"kpt-pasture/model"
 	"kpt-pasture/model"
 	"kpt-pasture/util"
 	"kpt-pasture/util"
 	"net/http"
 	"net/http"
+	"sort"
 	"time"
 	"time"
 
 
 	"gitee.com/xuyiping_admin/pkg/xerr"
 	"gitee.com/xuyiping_admin/pkg/xerr"
@@ -39,6 +41,33 @@ func (s *StoreEntry) PenBehavior(ctx context.Context, req *pasturePb.BarnBehavio
 	}, nil
 	}, nil
 }
 }
 
 
+func (s *StoreEntry) PenBehavior2(ctx context.Context, req *pasturePb.BarnBehaviorCurveRequest) (*model.BarnBehaviorCurveResponse, error) {
+	userModel, err := s.GetUserModel(ctx)
+	if err != nil {
+		return nil, err
+	}
+
+	if req.StartAt == 0 || req.EndAt == 0 || req.EndAt < req.StartAt {
+		return nil, xerr.Customf("时间范围错误")
+	}
+	startTime := time.Unix(int64(req.StartAt), 0).Local().Format(model.LayoutDate2)
+	endTime := time.Unix(int64(req.EndAt), 0).Local().Format(model.LayoutDate2)
+	penBehaviorList := make([]*model.PenBehavior, 0)
+	if err = s.DB.Model(new(model.PenBehavior)).
+		Where("pasture_id = ?", userModel.AppPasture.Id).
+		Where("pen_id = ?", req.PenId).
+		Where("heat_date BETWEEN ? AND ?", startTime, endTime).
+		Find(&penBehaviorList).Error; err != nil {
+		return nil, err
+	}
+
+	return &model.BarnBehaviorCurveResponse{
+		Code: http.StatusOK,
+		Msg:  "ok",
+		Data: model.PenBehaviorSlice(penBehaviorList).ToPB2(),
+	}, nil
+}
+
 func (s *StoreEntry) PenBehaviorDaily(ctx context.Context, req *pasturePb.BarnMonitorRequest) (*pasturePb.BarnMonitorResponse, error) {
 func (s *StoreEntry) PenBehaviorDaily(ctx context.Context, req *pasturePb.BarnMonitorRequest) (*pasturePb.BarnMonitorResponse, error) {
 	userModel, err := s.GetUserModel(ctx)
 	userModel, err := s.GetUserModel(ctx)
 	if err != nil {
 	if err != nil {
@@ -82,3 +111,177 @@ func (s *StoreEntry) PenBehaviorDaily(ctx context.Context, req *pasturePb.BarnMo
 		Data: model.PenBehaviorDayModelSlice(penBehaviorDayModelList).ToPB(dataTimeRange),
 		Data: model.PenBehaviorDayModelSlice(penBehaviorDayModelList).ToPB(dataTimeRange),
 	}, err
 	}, err
 }
 }
+
+func (s *StoreEntry) CowBehaviorDistribution(ctx context.Context, req *pasturePb.CowBehaviorDistributionRequest) (*pasturePb.CowBehaviorDistributionResponse, error) {
+	userModel, err := s.GetUserModel(ctx)
+	if err != nil {
+		return nil, xerr.WithStack(err)
+	}
+
+	// 校验时间必须比当天时间小一天
+	if time.Now().Local().Format(model.LayoutDate2) == req.DateTime {
+		return nil, xerr.Customf("时间范围错误")
+	}
+
+	milkDailList := make([]*model.MilkDaily, 0)
+	pref := s.DB.Table(fmt.Sprintf("%s as a", new(model.Cow).TableName())).
+		Joins(fmt.Sprintf("JOIN %s AS b on a.id = b.cow_id", new(model.MilkDaily).TableName())).
+		Select("b.*").
+		Where("a.pasture_id = ?", userModel.AppPasture.Id).
+		Where("a.neck_ring_number != ?", "").
+		Where("a.sex = ?", pasturePb.Genders_Female).
+		Where("b.heat_date = ? ", req.DateTime).
+		Where("b.day_high > ?", 0)
+
+	if len(req.PenIds) > 0 {
+		pref.Where("a.pen_id IN (?)", req.PenIds)
+	}
+
+	if err = pref.Order("b.breed_status,b.lactation_age").
+		Find(&milkDailList).Error; err != nil {
+		return nil, xerr.WithStack(err)
+	}
+
+	// 未配 空怀 怀孕 配种
+	data := &pasturePb.CowBehaviorDistributionItem{
+		Headers:    make([]string, 0),
+		Color:      make([]string, 0),
+		MedianLine: make(map[string]float32),
+		CalvingAge: make([]int32, 0),
+		UnBreed:    make([]*pasturePb.CowBehaviorData, 0),
+		Breed:      make([]*pasturePb.CowBehaviorData, 0),
+		Pregnant:   make([]*pasturePb.CowBehaviorData, 0),
+		Empty:      make([]*pasturePb.CowBehaviorData, 0),
+	}
+
+	breedStatus := s.BreedStatusEnumList()
+	for _, v := range breedStatus {
+		if v.Value == int32(pasturePb.BreedStatus_Abort) ||
+			v.Value == int32(pasturePb.BreedStatus_Calving) ||
+			v.Value == int32(pasturePb.BreedStatus_No_Mating) ||
+			v.Value == int32(pasturePb.BreedStatus_Invalid) {
+			continue
+		}
+		data.Headers = append(data.Headers, v.Label)
+		switch v.Label {
+		case "未配":
+			data.Color = append(data.Color, "#b53827")
+		case "空怀":
+			data.Color = append(data.Color, "#2784b5")
+		case "怀孕":
+			data.Color = append(data.Color, "#2757b5")
+		case "配种":
+			data.Color = append(data.Color, "#27b560")
+		}
+	}
+
+	if len(milkDailList) <= 0 {
+		return &pasturePb.CowBehaviorDistributionResponse{
+			Code: http.StatusOK,
+			Msg:  "ok",
+			Data: data,
+		}, nil
+	}
+
+	for _, v := range milkDailList {
+		dayData := int32(0)
+		switch req.BehaviorKind {
+		case pasturePb.Behavior_Rumina:
+			dayData = v.DayRumina
+		case pasturePb.Behavior_Intake:
+			dayData = v.DayIntake
+		case pasturePb.Behavior_Reset:
+			dayData = v.DayInactive
+		case pasturePb.Behavior_Immobility:
+			dayData = 24*60 - v.DayActive
+		case pasturePb.Behavior_Chew:
+			dayData = v.DayRumina + v.DayIntake
+		}
+		switch v.BreedStatus {
+		case pasturePb.BreedStatus_Calving:
+			data.UnBreed = append(data.UnBreed, &pasturePb.CowBehaviorData{
+				EarNumber:  v.EarNumber,
+				CalvingAge: v.LactationAge,
+				DayData:    dayData,
+			})
+		case pasturePb.BreedStatus_Empty:
+			data.Empty = append(data.Empty, &pasturePb.CowBehaviorData{
+				EarNumber:  v.EarNumber,
+				CalvingAge: v.LactationAge,
+				DayData:    dayData,
+			})
+		case pasturePb.BreedStatus_UnBreed:
+			data.UnBreed = append(data.UnBreed, &pasturePb.CowBehaviorData{
+				EarNumber:  v.EarNumber,
+				CalvingAge: v.LactationAge,
+				DayData:    dayData,
+			})
+		case pasturePb.BreedStatus_Breeding:
+			data.Breed = append(data.Breed, &pasturePb.CowBehaviorData{
+				EarNumber:  v.EarNumber,
+				CalvingAge: v.LactationAge,
+				DayData:    dayData,
+			})
+		case pasturePb.BreedStatus_Pregnant:
+			data.Pregnant = append(data.Pregnant, &pasturePb.CowBehaviorData{
+				EarNumber:  v.EarNumber,
+				CalvingAge: v.LactationAge,
+				DayData:    dayData,
+			})
+		}
+	}
+
+	// 获取Breed的中位数
+	if len(data.Breed) > 0 {
+		data.MedianLine["breed"] = float32(getMedian(data.Breed, func(p *pasturePb.CowBehaviorData) int {
+			return int(p.DayData)
+		}))
+	}
+
+	if len(data.Pregnant) > 0 {
+		data.MedianLine["pregnant"] = float32(getMedian(data.Pregnant, func(p *pasturePb.CowBehaviorData) int {
+			return int(p.DayData)
+		}))
+	}
+
+	if len(data.Empty) > 0 {
+		data.MedianLine["empty"] = float32(getMedian(data.Empty, func(p *pasturePb.CowBehaviorData) int {
+			return int(p.DayData)
+		}))
+	}
+
+	if len(data.UnBreed) > 0 {
+		data.MedianLine["unBreed"] = float32(getMedian(data.UnBreed, func(p *pasturePb.CowBehaviorData) int {
+			return int(p.DayData)
+		}))
+	}
+
+	return &pasturePb.CowBehaviorDistributionResponse{
+		Code: http.StatusOK,
+		Msg:  "ok",
+		Data: data,
+	}, err
+}
+
+// 获取结构体切片中某个int字段的中位值
+func getMedian(dataList []*pasturePb.CowBehaviorData, getField func(*pasturePb.CowBehaviorData) int) float64 {
+	// 1. 提取字段值
+	values := make([]int, len(dataList))
+	for i, p := range dataList {
+		values[i] = getField(p)
+	}
+
+	// 2. 排序
+	sort.Ints(values)
+
+	// 3. 计算中位数
+	n := len(values)
+	if n == 0 {
+		return 0
+	}
+
+	if n%2 == 1 {
+		return float64(values[n/2])
+	}
+	return float64(values[n/2-1]+values[n/2]) / 2.0
+}

+ 3 - 0
module/backend/calendar.go

@@ -411,6 +411,9 @@ func (s *StoreEntry) PregnancyCheckCowList(ctx context.Context, req *pasturePb.I
 		Where("b.admission_status = ?", pasturePb.AdmissionStatus_Admission).
 		Where("b.admission_status = ?", pasturePb.AdmissionStatus_Admission).
 		Where("a.pasture_id = ?", userModel.AppPasture.Id).
 		Where("a.pasture_id = ?", userModel.AppPasture.Id).
 		Where("a.status = ?", pasturePb.IsShow_No)
 		Where("a.status = ?", pasturePb.IsShow_No)
+	if req.EarNumber != "" {
+		pref.Where("a.ear_number = ?", req.EarNumber)
+	}
 
 
 	if req.EndDay != "" {
 	if req.EndDay != "" {
 		dateTime := util.TimeParseLocalEndUnix(req.EndDay)
 		dateTime := util.TimeParseLocalEndUnix(req.EndDay)

+ 3 - 3
module/backend/config_data_base.go

@@ -433,12 +433,12 @@ func (s *StoreEntry) Behavior(isAll string) []*pasturePb.ConfigOptionsList {
 		Label:    "休息",
 		Label:    "休息",
 		Disabled: true,
 		Disabled: true,
 	}, &pasturePb.ConfigOptionsList{
 	}, &pasturePb.ConfigOptionsList{
-		Value:    int32(pasturePb.Behavior_Stationary),
+		Value:    int32(pasturePb.Behavior_Immobility),
 		Label:    "静止",
 		Label:    "静止",
 		Disabled: true,
 		Disabled: true,
 	}, &pasturePb.ConfigOptionsList{
 	}, &pasturePb.ConfigOptionsList{
-		Value:    int32(pasturePb.Behavior_Milk),
-		Label:    "奶量",
+		Value:    int32(pasturePb.Behavior_Chew),
+		Label:    "咀嚼",
 		Disabled: true,
 		Disabled: true,
 	})
 	})
 	return configOptions
 	return configOptions

+ 2 - 0
module/backend/interface.go

@@ -296,7 +296,9 @@ type AnalyseService interface {
 	MultipleFactorAnalysis(ctx context.Context, req *pasturePb.MultiFactorPregnancyRateRequest) (*model.MultiFactorPregnancyRateResponse, error)
 	MultipleFactorAnalysis(ctx context.Context, req *pasturePb.MultiFactorPregnancyRateRequest) (*model.MultiFactorPregnancyRateResponse, error)
 
 
 	PenBehavior(ctx context.Context, req *pasturePb.BarnBehaviorCurveRequest) (*pasturePb.BarnBehaviorCurveResponse, error)
 	PenBehavior(ctx context.Context, req *pasturePb.BarnBehaviorCurveRequest) (*pasturePb.BarnBehaviorCurveResponse, error)
+	PenBehavior2(ctx context.Context, req *pasturePb.BarnBehaviorCurveRequest) (*model.BarnBehaviorCurveResponse, error)
 	PenBehaviorDaily(ctx context.Context, req *pasturePb.BarnMonitorRequest) (*pasturePb.BarnMonitorResponse, error)
 	PenBehaviorDaily(ctx context.Context, req *pasturePb.BarnMonitorRequest) (*pasturePb.BarnMonitorResponse, error)
+	CowBehaviorDistribution(ctx context.Context, req *pasturePb.CowBehaviorDistributionRequest) (*pasturePb.CowBehaviorDistributionResponse, 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

+ 68 - 15
module/crontab/milk_daily.go

@@ -5,6 +5,8 @@ import (
 	"kpt-pasture/util"
 	"kpt-pasture/util"
 	"time"
 	"time"
 
 
+	"gitee.com/xuyiping_admin/pkg/xerr"
+
 	pasturePb "gitee.com/xuyiping_admin/go_proto/proto/go/backend/cow"
 	pasturePb "gitee.com/xuyiping_admin/go_proto/proto/go/backend/cow"
 
 
 	"gitee.com/xuyiping_admin/pkg/logger/zaplog"
 	"gitee.com/xuyiping_admin/pkg/logger/zaplog"
@@ -43,30 +45,79 @@ func (e *Entry) InsertMilkDaily() error {
 
 
 func (e *Entry) ProcessMilkDaily(pastureId int64, maxDate time.Time) {
 func (e *Entry) ProcessMilkDaily(pastureId int64, maxDate time.Time) {
 	nowTime := time.Now().Local()
 	nowTime := time.Now().Local()
+
 	// 处理每一天的数据
 	// 处理每一天的数据
 	for maxDate.Before(nowTime) {
 	for maxDate.Before(nowTime) {
 		// 处理有胎次的奶牛
 		// 处理有胎次的奶牛
-		if err := e.processCowsWithLact(pastureId, maxDate); err != nil {
+		processCowIds := make([]int64, 0)
+		cowIds, err := e.processCowsWithLact(pastureId, maxDate)
+		if err != nil {
 			zaplog.Error("ProcessMilkDaily", zap.Any("processCowsWithFetal", err))
 			zaplog.Error("ProcessMilkDaily", zap.Any("processCowsWithFetal", err))
 		}
 		}
-
+		if len(cowIds) > 0 {
+			processCowIds = append(processCowIds, cowIds...)
+		}
 		// 处理无胎次的奶牛
 		// 处理无胎次的奶牛
-		if err := e.processCowsWithNoLact(pastureId, maxDate); err != nil {
+		cowIds, err = e.processCowsWithNoLact(pastureId, maxDate)
+		if err != nil {
 			zaplog.Error("ProcessMilkDaily", zap.Any("processCowsWithoutFetal", err))
 			zaplog.Error("ProcessMilkDaily", zap.Any("processCowsWithoutFetal", err))
 		}
 		}
+
+		if len(cowIds) > 0 {
+			processCowIds = append(processCowIds, cowIds...)
+		}
+
+		if len(processCowIds) > 0 {
+			e.UpdateMilkDaily(pastureId, processCowIds, maxDate.Format(model.LayoutDate2))
+		}
 		// 日期加1天
 		// 日期加1天
 		maxDate = maxDate.AddDate(0, 0, 1)
 		maxDate = maxDate.AddDate(0, 0, 1)
 	}
 	}
 }
 }
 
 
-func (e *Entry) UpdateMilkDaily(pastureId int64, maxDateTime string) error {
-	//yesterday := time.Now().Local().AddDate(0, 0, -1).Format(model.LayoutDate2)
+// UpdateMilkDaily
+// SELECT h.intCowId, ROUND(AVG(h.filterhigh), 0) high, ROUND(AVG( h.rumina)*12, 0) rumina, ROUND(AVG( h.intake)*12, 0) intake,
+// //				ROUND(AVG( h.inactive)*12, 0) inactive, ROUND(AVG( h.act)*12, 0) act, COUNT(1) nb
+// //			FROM h_activehabit h WHERE h.intPastureId=PastuId AND h.heatdate=xDate GROUP BY h.intCowId HAVING nb>=8
+func (e *Entry) UpdateMilkDaily(pastureId int64, processCowIds []int64, heatDate string) error {
 
 
+	neckActiveHabitList := make([]*model.NeckActiveHabit, 0)
+	if err := e.DB.Model(new(model.NeckActiveHabit)).
+		Select(`h.cow_id, ROUND(AVG(h.filter_high), 0) AS high, ROUND(AVG( h.rumina)*12, 0) AS rumina, ROUND(AVG( h.intake)*12, 0) AS intake,
+			ROUND(AVG( h.inactive)*12, 0) AS inactive, ROUND(AVG( h.active)*12, 0) AS active, COUNT(1) AS record_count`).
+		Where("pasture_id = ?", pastureId).
+		Where("cow_id IN ?", processCowIds).
+		Where("heat_date = ?", heatDate).
+		Group("cow_id").
+		Find(&neckActiveHabitList).Error; err != nil {
+		return xerr.WithStack(err)
+	}
+
+	for _, v := range neckActiveHabitList {
+		// todo 先不过滤
+		/*if v.RecordCount < 8 {
+			continue
+		}*/
+		if err := e.DB.Model(new(model.MilkDaily)).
+			Where("pasture_id = ?", pastureId).
+			Where("cow_id = ?", v.CowId).
+			Where("heat_date = ?", heatDate).
+			Updates(map[string]interface{}{
+				"day_high":     v.High,
+				"day_rumina":   v.Rumina,
+				"day_intake":   v.Intake,
+				"day_inactive": v.Inactive,
+				"day_active":   v.Active,
+			}).Error; err != nil {
+			zaplog.Error("UpdateMilkDaily", zap.Any("pastureId", pastureId), zap.Any("cowId", v.CowId), zap.Any("heatDate", heatDate), zap.Any("err", err))
+			continue
+		}
+	}
 	return nil
 	return nil
 }
 }
 
 
 // 处理有胎次的奶牛
 // 处理有胎次的奶牛
-func (e *Entry) processCowsWithLact(pastureId int64, recordDate time.Time) error {
+func (e *Entry) processCowsWithLact(pastureId int64, recordDate time.Time) ([]int64, error) {
 	// 查询有胎次且在记录日期前有记录的奶牛
 	// 查询有胎次且在记录日期前有记录的奶牛
 	cowList := make([]*model.Cow, 0)
 	cowList := make([]*model.Cow, 0)
 	if err := e.DB.Model(new(model.Cow)).
 	if err := e.DB.Model(new(model.Cow)).
@@ -75,11 +126,12 @@ func (e *Entry) processCowsWithLact(pastureId int64, recordDate time.Time) error
 		Where("lact > ?", 0).
 		Where("lact > ?", 0).
 		Where("last_calving_at <= ?", recordDate.Local().Unix()).
 		Where("last_calving_at <= ?", recordDate.Local().Unix()).
 		Find(&cowList).Error; err != nil {
 		Find(&cowList).Error; err != nil {
-		return err
+		return nil, err
 	}
 	}
 
 
 	// 批量插入数据
 	// 批量插入数据
 	milkDailyList := make([]*model.MilkDaily, 0)
 	milkDailyList := make([]*model.MilkDaily, 0)
+	cowIds := make([]int64, 0)
 	for _, cow := range cowList {
 	for _, cow := range cowList {
 		calvingDate := ""
 		calvingDate := ""
 		if cow.LastCalvingAt > 0 {
 		if cow.LastCalvingAt > 0 {
@@ -98,6 +150,7 @@ func (e *Entry) processCowsWithLact(pastureId int64, recordDate time.Time) error
 			BreedStatus:  cow.BreedStatus,
 			BreedStatus:  cow.BreedStatus,
 		}
 		}
 		milkDailyList = append(milkDailyList, milkDaily)
 		milkDailyList = append(milkDailyList, milkDaily)
+		cowIds = append(cowIds, cow.Id)
 	}
 	}
 	if len(milkDailyList) > 0 {
 	if len(milkDailyList) > 0 {
 		// 分批次插入数据
 		// 分批次插入数据
@@ -108,16 +161,16 @@ func (e *Entry) processCowsWithLact(pastureId int64, recordDate time.Time) error
 			}
 			}
 			if err := e.DB.Model(new(model.MilkDaily)).
 			if err := e.DB.Model(new(model.MilkDaily)).
 				Create(milkDailyList[i:end]).Error; err != nil {
 				Create(milkDailyList[i:end]).Error; err != nil {
-				return err
+				return nil, err
 			}
 			}
 		}
 		}
 	}
 	}
 
 
-	return nil
+	return cowIds, nil
 }
 }
 
 
 // 处理无胎次的奶牛
 // 处理无胎次的奶牛
-func (e *Entry) processCowsWithNoLact(pastureId int64, recordDate time.Time) error {
+func (e *Entry) processCowsWithNoLact(pastureId int64, recordDate time.Time) ([]int64, error) {
 	// 查询无胎次且EID1>0的奶牛
 	// 查询无胎次且EID1>0的奶牛
 	cowList := make([]*model.Cow, 0)
 	cowList := make([]*model.Cow, 0)
 	err := e.DB.Model(new(model.Cow)).
 	err := e.DB.Model(new(model.Cow)).
@@ -129,13 +182,13 @@ func (e *Entry) processCowsWithNoLact(pastureId int64, recordDate time.Time) err
 		Where("neck_ring_number != ?", "").
 		Where("neck_ring_number != ?", "").
 		Find(&cowList).Error
 		Find(&cowList).Error
 	if err != nil {
 	if err != nil {
-		return err
+		return nil, err
 	}
 	}
 
 
 	// 批量插入数据
 	// 批量插入数据
 	milkDailyList := make([]*model.MilkDaily, 0)
 	milkDailyList := make([]*model.MilkDaily, 0)
+	cowIds := make([]int64, 0)
 	for _, cow := range cowList {
 	for _, cow := range cowList {
-
 		birthDate, calvingDate := "", ""
 		birthDate, calvingDate := "", ""
 		if cow.BirthAt > 0 {
 		if cow.BirthAt > 0 {
 			birthDate = time.Unix(cow.BirthAt, 0).Local().Format(model.LayoutDate2)
 			birthDate = time.Unix(cow.BirthAt, 0).Local().Format(model.LayoutDate2)
@@ -158,14 +211,14 @@ func (e *Entry) processCowsWithNoLact(pastureId int64, recordDate time.Time) err
 			BreedStatus:  cow.BreedStatus,
 			BreedStatus:  cow.BreedStatus,
 		}
 		}
 		milkDailyList = append(milkDailyList, milkDaily)
 		milkDailyList = append(milkDailyList, milkDaily)
+		cowIds = append(cowIds, cow.Id)
 	}
 	}
 
 
 	if len(milkDailyList) > 0 {
 	if len(milkDailyList) > 0 {
 		if err = e.DB.Model(new(model.MilkDaily)).
 		if err = e.DB.Model(new(model.MilkDaily)).
 			Create(&milkDailyList).Error; err != nil {
 			Create(&milkDailyList).Error; err != nil {
-			return err
+			return nil, err
 		}
 		}
 	}
 	}
-
-	return nil
+	return cowIds, nil
 }
 }

+ 5 - 14
module/crontab/neck_ring_calculate.go

@@ -19,15 +19,6 @@ func (e *Entry) NeckRingCalculate() error {
 	if pastureList == nil || len(pastureList) == 0 {
 	if pastureList == nil || len(pastureList) == 0 {
 		return nil
 		return nil
 	}
 	}
-
-	if calculateIsRunning {
-		return nil
-	}
-	defer func() {
-		calculateIsRunning = false
-	}()
-
-	calculateIsRunning = true
 	for _, pasture := range pastureList {
 	for _, pasture := range pastureList {
 		if err := e.EntryUpdateActiveHabit(pasture.Id); err != nil {
 		if err := e.EntryUpdateActiveHabit(pasture.Id); err != nil {
 			zaplog.Error("NeckRingCalculate", zap.Any("err", err), zap.Any("pasture", pasture))
 			zaplog.Error("NeckRingCalculate", zap.Any("err", err), zap.Any("pasture", pasture))
@@ -56,7 +47,7 @@ func (e *Entry) EntryUpdateActiveHabit(pastureId int64) (err error) {
 		zaplog.Error("NeckRingCalculate", zap.Any("pastureId", pastureId), zap.Any("FirstFilterUpdate", err), zap.Any("xToday", xToday))
 		zaplog.Error("NeckRingCalculate", zap.Any("pastureId", pastureId), zap.Any("FirstFilterUpdate", err), zap.Any("xToday", xToday))
 	}
 	}
 
 
-	zaplog.Info("NeckRingCalculate", zap.Any("xToday", xToday), zap.Any("processIds", processIds))
+	zaplog.Info("NeckRingCalculate", zap.Any("pastureId", pastureId), zap.Any("xToday", xToday), zap.Any("processIds", processIds))
 	if len(processIds) <= 0 {
 	if len(processIds) <= 0 {
 		return nil
 		return nil
 	}
 	}
@@ -100,7 +91,7 @@ func (e *Entry) FirstFilterUpdate(pastureId int64, xToDay *XToday) (processIds [
 		Where("heat_date >= ?", time.Now().Local().AddDate(0, 0, -30).Format(model.LayoutDate2)).
 		Where("heat_date >= ?", time.Now().Local().AddDate(0, 0, -30).Format(model.LayoutDate2)).
 		Where("pasture_id = ?", pastureId).
 		Where("pasture_id = ?", pastureId).
 		Where("is_show = ?", pasturePb.IsShow_No).
 		Where("is_show = ?", pasturePb.IsShow_No).
-		//Where(e.DB.Where("high >= ?", xToDay.High).Or("rumina >= ?", xToDay.Rumina)).
+		Where(e.DB.Where("high >= ?", xToDay.High).Or("rumina >= ?", xToDay.Rumina)).
 		Order("heat_date,neck_ring_number,frameid").
 		Order("heat_date,neck_ring_number,frameid").
 		Limit(int(limit)).
 		Limit(int(limit)).
 		Find(&newNeckActiveHabitList).Error; err != nil {
 		Find(&newNeckActiveHabitList).Error; err != nil {
@@ -109,9 +100,9 @@ func (e *Entry) FirstFilterUpdate(pastureId int64, xToDay *XToday) (processIds [
 
 
 	// 活动量滤波
 	// 活动量滤波
 	for _, v := range newNeckActiveHabitList {
 	for _, v := range newNeckActiveHabitList {
-		if !(v.High >= xToDay.High || v.Rumina >= xToDay.Rumina) {
+		/*if !(v.High >= xToDay.High || v.Rumina >= xToDay.Rumina) {
 			continue
 			continue
-		}
+		}*/
 		// 4小时数据不全的不参与滤波
 		// 4小时数据不全的不参与滤波
 		activeTime, _ := util.TimeParseLocal(model.LayoutTime, v.ActiveTime)
 		activeTime, _ := util.TimeParseLocal(model.LayoutTime, v.ActiveTime)
 		if v.RecordCount != model.DefaultRecordCount && time.Now().Local().Sub(activeTime).Hours() <= 4 {
 		if v.RecordCount != model.DefaultRecordCount && time.Now().Local().Sub(activeTime).Hours() <= 4 {
@@ -405,7 +396,7 @@ func (e *Entry) UpdateChangeAdJust(pastureId int64, xToday *XToday) {
 			Where("pasture_id = ?", pastureId).
 			Where("pasture_id = ?", pastureId).
 			Where("neck_ring_number = ?", v.NeckRingNumber).
 			Where("neck_ring_number = ?", v.NeckRingNumber).
 			Where("heat_date = ?", v.HeatDate).
 			Where("heat_date = ?", v.HeatDate).
-			Where("pen_id = ?", v.PenId).
+			//Where("pen_id = ?", v.PenId).
 			Where("frameid = ?", v.FrameId).
 			Where("frameid = ?", v.FrameId).
 			Update("change_adjust", v.ChangeFilter).Error; err != nil {
 			Update("change_adjust", v.ChangeFilter).Error; err != nil {
 			zaplog.Error("UpdateChangeAdJust-1", zap.Any("error", err), zap.Any("xToday", xToday))
 			zaplog.Error("UpdateChangeAdJust-1", zap.Any("error", err), zap.Any("xToday", xToday))

+ 46 - 0
module/crontab/pen_behavior.go

@@ -23,6 +23,7 @@ func (e *Entry) UpdatePenBehavior() error {
 			continue
 			continue
 		}
 		}
 		e.PenBehavior(pasture.Id, conf.Value)
 		e.PenBehavior(pasture.Id, conf.Value)
+		e.UpdatePenBehaviorWeekData(pasture.Id)
 	}
 	}
 	return nil
 	return nil
 }
 }
@@ -189,6 +190,51 @@ func (e *Entry) savePenBehaviorData(penData map[string]*model.PenBehaviorData) e
 	return nil
 	return nil
 }
 }
 
 
+func (e *Entry) UpdatePenBehaviorWeekData(pastureId int64) {
+	nowTime := time.Now().Local()
+	currTime := nowTime.Format(model.LayoutDate2)
+	startTime := nowTime.AddDate(0, 0, -7).Format(model.LayoutDate2)
+	endTime := nowTime.AddDate(0, 0, -1).Format(model.LayoutDate2)
+	penBehaviorList := make([]*model.PenBehavior, 0)
+	if err := e.DB.Table(fmt.Sprintf("%s as b1", new(model.PenBehavior).TableName())).
+		Joins(fmt.Sprintf("JOIN %s as b0 ON b1.pen_id = b0.pen_id", new(model.PenBehavior).TableName())).
+		Select(`b1.id, ROUND(AVG(b0.rumina_rate)) AS week_rumina_rate, 
+		ROUND(AVG(b0.intake_rate)) AS week_intake_rate,
+		ROUND(AVG(b0.rest_rate)) AS week_rest_rate, 
+		ROUND(AVG(b0.gasp_rate)) AS week_gasp_rate, 
+		ROUND(STD(b0.rumina_rate)) AS rumina_std, 		
+		ROUND(STD(b0.intake_rate)) AS  intake_std, 		
+		ROUND(STD(b0.rest_rate)) AS rest_std, 	
+		ROUND(STD(b0.gasp_rate)) AS gasp_std`).
+		Where("b1.pasture_id = ?", pastureId).
+		Where("b1.heat_date = ?", currTime).
+		Where("b1.week_rumina_rate = ?", 0).
+		Where("b1.frameid = b0.frameid").
+		Where("b0.heat_date BETWEEN ? AND ?", startTime, endTime).
+		Find(&penBehaviorList).Error; err != nil {
+		zaplog.Error("PenBehavior", zap.Any("penBehaviorList", penBehaviorList), zap.Any("err", err))
+		return
+	}
+
+	for _, v := range penBehaviorList {
+		if err := e.DB.Model(new(model.PenBehavior)).
+			Where("id = ?", v.Id).
+			Updates(map[string]interface{}{
+				"week_rumina_rate": v.WeekRuminaRate,
+				"week_intake_rate": v.WeekIntakeRate,
+				"week_rest_rate":   v.WeekRestRate,
+				"week_gasp_rate":   v.WeekGaspRate,
+				"rumina_std":       v.RuminaStd,
+				"intake_std":       v.IntakeStd,
+				"rest_std":         v.RestStd,
+				"gasp_std":         v.GaspStd,
+			}).Error; err != nil {
+			zaplog.Error("PenBehavior", zap.Any("penBehaviorWeekData", v), zap.Any("err", err))
+			continue
+		}
+	}
+}
+
 // calculateActiveTime 计算活动时间
 // calculateActiveTime 计算活动时间
 func (e *Entry) calculateActiveTime(heatDate string, frameid int32) string {
 func (e *Entry) calculateActiveTime(heatDate string, frameid int32) string {
 	// 计算小时和分钟
 	// 计算小时和分钟

+ 0 - 1
module/crontab/pen_behavior_day.go

@@ -110,6 +110,5 @@ func (e *Entry) insertBarBehaviorDay(pastureId int64, targetDate string) error {
 			return xerr.WithStack(err)
 			return xerr.WithStack(err)
 		}
 		}
 	}
 	}
-
 	return nil
 	return nil
 }
 }