Yi 1 mēnesi atpakaļ
vecāks
revīzija
ebfa66c862
45 mainītis faili ar 4807 papildinājumiem un 449 dzēšanām
  1. 2 1
      config/app.develop.yaml
  2. 1 0
      config/app.go
  3. 6 0
      dep/di_crontab.go
  4. 1 1
      go.mod
  5. 25 0
      go.sum
  6. 1 1
      http/handler/event/event_base.go
  7. 1 0
      http/handler/event/event_health.go
  8. 0 13
      model/app_mqtt.go
  9. 3 2
      model/calving_calf.go
  10. 15 5
      model/cow.go
  11. 8 4
      model/data_waring.go
  12. 4 10
      model/event_cow_treatment.go
  13. 0 1
      model/event_mating.go
  14. 1 0
      model/event_pregnant_check.go
  15. 0 6
      model/event_transfer_group.go
  16. 1 0
      model/neck_active_habit.go
  17. 33 70
      model/neck_ring_estrus.go
  18. 96 0
      model/neck_ring_estrus_warning.go
  19. 5 0
      model/neck_ring_health_warning.go
  20. 1 0
      model/system_role.go
  21. 8 16
      module/backend/analysis.go
  22. 3 6
      module/backend/analysis_other.go
  23. 46 29
      module/backend/calendar.go
  24. 1 2
      module/backend/config_data.go
  25. 13 5
      module/backend/cow.go
  26. 8 2
      module/backend/dashboard.go
  27. 1 1
      module/backend/enum_map.go
  28. 117 76
      module/backend/event_base.go
  29. 8 1
      module/backend/event_base_more.go
  30. 51 31
      module/backend/event_breed.go
  31. 38 9
      module/backend/event_breed_more.go
  32. 27 5
      module/backend/event_check.go
  33. 11 6
      module/backend/event_cow_log.go
  34. 21 24
      module/backend/event_health.go
  35. 16 0
      module/backend/sql.go
  36. 1 1
      module/crontab/cow_indicators_base.go
  37. 2 2
      module/crontab/health_warning.go
  38. 2 1
      module/crontab/interface.go
  39. 9 11
      module/crontab/neck_ring_estrus.go
  40. 3228 2
      module/crontab/neck_ring_estus_test.go
  41. 126 69
      module/crontab/neck_ring_handle.go
  42. 1 1
      module/crontab/sql.go
  43. 289 0
      module/crontab/warning_handle.go
  44. 6 33
      module/mqtt/mqtt_handle.go
  45. 570 2
      util/util_test.go

+ 2 - 1
config/app.develop.yaml

@@ -47,7 +47,8 @@ cron:
   cow_pregnant: "0 00 15 * * ?"         # 每天15点执行
   neck_ring_estrus: "0 45 * * * ?"      # 更新脖环发情数据
   neck_ring_merge: "*/5 * * * * ?"      # 合并脖环原始2小时数据(5分钟)
-  neck_ring_calculate: "*/10 * * * * ?"  # 计算脖环数据
+  neck_ring_calculate: "*/10 * * * * ?" # 计算脖环数据
+  neck_ring_warning: "*/50 * * * * ?"   # 脖环预警(每50分钟执行一次
 
 mqtt:
   broker: "kptyun.com:1983"

+ 1 - 0
config/app.go

@@ -63,6 +63,7 @@ type CronSetting struct {
 	NeckRingEstrus     string `yaml:"neck_ring_estrus"`     //  脖环牛只发情
 	NeckRingMerge      string `yaml:"neck_ring_merge"`      //  脖环原始数据合并
 	NeckRingCalculate  string `yaml:"neck_ring_calculate"`  //  脖环数据计算
+	NeckRingWarning    string `yaml:"neck_ring_warning"`    //  脖环预警
 }
 
 type JwtTokenKeyConfig struct {

+ 6 - 0
dep/di_crontab.go

@@ -98,6 +98,12 @@ func EntryCrontab(dependency CrontabDependency) *cron.Crontab {
 		panic(err)
 	}
 
+	err = newCrontab.Bind("NeckRingWarning", cs.NeckRingWarning, dependency.CrontabHub.UpdateNeckRingWarning)
+	if err != nil {
+		zaplog.Error("EntryCrontab", zap.Any("NeckRingOriginalMergeData", err))
+		panic(err)
+	}
+
 	/*err = newCrontab.Bind("GenerateWorkOrder", cs.GenerateWorkOrder, dependency.CrontabHub.GenerateAsynqWorkOrder)
 	if err != nil {
 		panic(err)

+ 1 - 1
go.mod

@@ -3,7 +3,7 @@ module kpt-pasture
 go 1.17
 
 require (
-	gitee.com/xuyiping_admin/go_proto v0.0.0-20250224130629-8acdbff851e5
+	gitee.com/xuyiping_admin/go_proto v0.0.0-20250228083339-f6b9576918dd
 	gitee.com/xuyiping_admin/pkg v0.0.0-20241108060137-caea58c59f5b
 	github.com/dgrijalva/jwt-go v3.2.0+incompatible
 	github.com/eclipse/paho.mqtt.golang v1.4.3

+ 25 - 0
go.sum

@@ -246,6 +246,31 @@ gitee.com/xuyiping_admin/go_proto v0.0.0-20250224130350-5bbe5630870a h1:wG/q1duu
 gitee.com/xuyiping_admin/go_proto v0.0.0-20250224130350-5bbe5630870a/go.mod h1:BKrFW6YLDectlQcQk3FYKBeXvjEiodAKJ5rq7O/QiPE=
 gitee.com/xuyiping_admin/go_proto v0.0.0-20250224130629-8acdbff851e5 h1:r11lo90UNSksUGhfhpxzOva2g91z9fFXk9TNhHBFvy8=
 gitee.com/xuyiping_admin/go_proto v0.0.0-20250224130629-8acdbff851e5/go.mod h1:BKrFW6YLDectlQcQk3FYKBeXvjEiodAKJ5rq7O/QiPE=
+gitee.com/xuyiping_admin/go_proto v0.0.0-20250225013948-765a2467f706 h1:rZ7hscSiajeXQnO2+4vkO9Vmm8qlphY4SgtWxqmWjp8=
+gitee.com/xuyiping_admin/go_proto v0.0.0-20250225013948-765a2467f706/go.mod h1:BKrFW6YLDectlQcQk3FYKBeXvjEiodAKJ5rq7O/QiPE=
+gitee.com/xuyiping_admin/go_proto v0.0.0-20250225022411-1e7c5a6e9e23 h1:m3QzUjWJuUobE9Pk6s2b9nsYLzjYWs1RVj/KalLqEGI=
+gitee.com/xuyiping_admin/go_proto v0.0.0-20250225022411-1e7c5a6e9e23/go.mod h1:BKrFW6YLDectlQcQk3FYKBeXvjEiodAKJ5rq7O/QiPE=
+gitee.com/xuyiping_admin/go_proto v0.0.0-20250225100328-cf8cdf9dc5eb h1:JxEedbwLuzyhJIKTUtlZloUotoU9x+U+eF7vyCdmJjE=
+gitee.com/xuyiping_admin/go_proto v0.0.0-20250225100328-cf8cdf9dc5eb/go.mod h1:BKrFW6YLDectlQcQk3FYKBeXvjEiodAKJ5rq7O/QiPE=
+gitee.com/xuyiping_admin/go_proto v0.0.0-20250226010813-bf031c8abb4b h1:kXph//3sWQTSLKG6gqsBt+i90g/NFIIcL8lDEctTBgg=
+gitee.com/xuyiping_admin/go_proto v0.0.0-20250226010813-bf031c8abb4b/go.mod h1:BKrFW6YLDectlQcQk3FYKBeXvjEiodAKJ5rq7O/QiPE=
+gitee.com/xuyiping_admin/go_proto v0.0.0-20250226025759-0b8545dc8254 h1:H+WZ8r7nTguIAv2m4wGkHyxQZrjTFgGfxxpNeRsQ6Kk=
+gitee.com/xuyiping_admin/go_proto v0.0.0-20250226025759-0b8545dc8254/go.mod h1:BKrFW6YLDectlQcQk3FYKBeXvjEiodAKJ5rq7O/QiPE=
+gitee.com/xuyiping_admin/go_proto v0.0.0-20250227023705-ca70c0b4a71c h1:6uBVCPNUO70wMPF9NklLmQXNfbNFTv3xpfza5UmXhsc=
+gitee.com/xuyiping_admin/go_proto v0.0.0-20250227023705-ca70c0b4a71c/go.mod h1:BKrFW6YLDectlQcQk3FYKBeXvjEiodAKJ5rq7O/QiPE=
+gitee.com/xuyiping_admin/go_proto v0.0.0-20250228024549-cbb245fed491 h1:UkG2VOHupyqS6F83tp6ryJ0NoaWrcFwwCm8CfAfR8k4=
+gitee.com/xuyiping_admin/go_proto v0.0.0-20250228024549-cbb245fed491/go.mod h1:BKrFW6YLDectlQcQk3FYKBeXvjEiodAKJ5rq7O/QiPE=
+gitee.com/xuyiping_admin/go_proto v0.0.0-20250228024930-7175605e33bd h1:8oa74WCD2mu9I7FHXW4MZftUGMtauy4CclrAzQytU5s=
+gitee.com/xuyiping_admin/go_proto v0.0.0-20250228024930-7175605e33bd/go.mod h1:BKrFW6YLDectlQcQk3FYKBeXvjEiodAKJ5rq7O/QiPE=
+gitee.com/xuyiping_admin/go_proto v0.0.0-20250228032637-7b92e2ab4093 h1:qToZSPcyoZbw905RQDUavtOaaUycFU2vXT85RT3G7ZY=
+gitee.com/xuyiping_admin/go_proto v0.0.0-20250228032637-7b92e2ab4093/go.mod h1:BKrFW6YLDectlQcQk3FYKBeXvjEiodAKJ5rq7O/QiPE=
+gitee.com/xuyiping_admin/go_proto v0.0.0-20250228074230-29f485ce3854 h1:Mo2B8XwgrdJqrlBSahle2NC1z3jl2E00FyOGyB6yGXQ=
+gitee.com/xuyiping_admin/go_proto v0.0.0-20250228074230-29f485ce3854/go.mod h1:BKrFW6YLDectlQcQk3FYKBeXvjEiodAKJ5rq7O/QiPE=
+gitee.com/xuyiping_admin/go_proto v0.0.0-20250228081242-8794bd89a603 h1:15ZmN1TmK+XgSqce67/GUnMJvdhRjOcNzzUTyYns/28=
+gitee.com/xuyiping_admin/go_proto v0.0.0-20250228081242-8794bd89a603/go.mod h1:BKrFW6YLDectlQcQk3FYKBeXvjEiodAKJ5rq7O/QiPE=
+gitee.com/xuyiping_admin/go_proto v0.0.0-20250228082905-a9a0b77f1247/go.mod h1:BKrFW6YLDectlQcQk3FYKBeXvjEiodAKJ5rq7O/QiPE=
+gitee.com/xuyiping_admin/go_proto v0.0.0-20250228083339-f6b9576918dd h1:hqSeKVOL1YQHz7WoHmGKlm+T3MoczJvJ9kDd7qoPdMM=
+gitee.com/xuyiping_admin/go_proto v0.0.0-20250228083339-f6b9576918dd/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/go.mod h1:8tF25X6pE9WkFCczlNAC0K2mrjwKvhhp02I7o0HtDxY=
 github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=

+ 1 - 1
http/handler/event/event_base.go

@@ -102,7 +102,7 @@ func GroupTransferEventCreate(c *gin.Context) {
 		valid.Field(&req.Body, valid.Required, valid.Each(valid.By(func(value interface{}) error {
 			s := value.(pasturePb.TransferGroupEventData)
 			return valid.ValidateStruct(&s,
-				valid.Field(&s.CowId, valid.Required),
+				valid.Field(&s.EarNumber, valid.Required),
 				valid.Field(&s.TransferDate, valid.Required),
 				valid.Field(&s.TransferInPenId, valid.Required),
 				valid.Field(&s.TransferReasonId, valid.Required),

+ 1 - 0
http/handler/event/event_health.go

@@ -151,6 +151,7 @@ func CowDiseaseTreatmentDetail(c *gin.Context) {
 
 	if err := valid.ValidateStruct(&req,
 		valid.Field(&req.CowId, valid.Required),
+		valid.Field(&req.Id, valid.Required),
 	); err != nil {
 		apierr.AbortBadRequest(c, http.StatusBadRequest, err)
 		return

+ 0 - 13
model/app_mqtt.go

@@ -1,13 +0,0 @@
-package model
-
-type AppMqtt struct {
-	Id            int64  `json:"id"`
-	PastureId     int64  `json:"pastureId"`
-	ReceiveNumber string `json:"receiveNumber"`
-	CreatedAt     int64  `json:"createdAt"`
-	UpdatedAt     int64  `json:"updatedAt"`
-}
-
-func (a *AppMqtt) TableName() string {
-	return "app_mqtt"
-}

+ 3 - 2
model/calving_calf.go

@@ -28,7 +28,7 @@ func (e *CalvingCalf) TableName() string {
 	return "calving_calf"
 }
 
-func NewEventCalvingCalf(pastureId, motherId, calvingId, calvingAt int64, penMap map[int32]*Pen, req *pasturePb.CalfItem) *CalvingCalf {
+func NewEventCalvingCalf(pastureId, calvingId, calvingAt int64, motherInfo *Cow, penMap map[int32]*Pen, req *pasturePb.CalfItem) *CalvingCalf {
 	isAdoption := req.IsAdoption
 	if req.IsLive == pasturePb.IsShow_No {
 		isAdoption = pasturePb.IsShow_No
@@ -52,9 +52,10 @@ func NewEventCalvingCalf(pastureId, motherId, calvingId, calvingAt int64, penMap
 		BirthWeight:   int64(req.Weight * 1000),
 		CurrentWeight: int64(req.Weight * 1000),
 		Sex:           req.Sex,
-		MotherId:      motherId,
+		MotherId:      motherInfo.Id,
 		Remarks:       req.Remarks,
 		IsAdoption:    isAdoption,
 		IsLive:        req.IsLive,
+		CowKind:       motherInfo.CowKind,
 	}
 }

+ 15 - 5
model/cow.go

@@ -102,11 +102,14 @@ func (c *Cow) EventPregnantCheckUpdate(breedStatus pasturePb.BreedStatus_Kind, p
 }
 
 // EventAbortionUpdate 流产更新
-func (c *Cow) EventAbortionUpdate(abortionAt int64) {
+func (c *Cow) EventAbortionUpdate(abortionAt int64, isLact pasturePb.IsShow_Kind) {
 	c.IsPregnant = pasturePb.IsShow_No
 	c.LastAbortionAt = abortionAt
 	c.BreedStatus = pasturePb.BreedStatus_Abort
 	c.AbortionTimes += 1
+	if isLact == pasturePb.IsShow_Ok {
+		c.Lact += 1
+	}
 }
 
 // EventWeightUpdate 称重更新
@@ -160,6 +163,13 @@ func (c *Cow) EventMatingUpdate(matingAt int64, bullNumber string, isReMating bo
 	if !isReMating {
 		c.MatingTimes += 1
 	}
+	if c.Lact == 0 {
+		c.CowType = pasturePb.CowType_Reserve_Calf
+	}
+}
+
+func (c *Cow) EstrusUpdate(estrusAt int64) {
+	c.LastEstrusAt = estrusAt
 }
 
 type CowSlice []*Cow
@@ -368,7 +378,7 @@ func NewEnterCow(pastureId int64, req *pasturePb.EventEnterRequest, penMap map[i
 }
 
 // NewCalfCow 产犊新增
-func NewCalfCow(motherNumber, fatherNumber string, calf *CalvingCalf) *Cow {
+func NewCalfCow(matherInfo *Cow, calf *CalvingCalf) *Cow {
 	return &Cow{
 		PastureId:       calf.PastureId,
 		Sex:             calf.Sex,
@@ -377,12 +387,12 @@ func NewCalfCow(motherNumber, fatherNumber string, calf *CalvingCalf) *Cow {
 		PenName:         calf.PenName,
 		CowType:         pasturePb.CowType_Lactating_Calf, // 哺乳犊牛
 		BreedStatus:     pasturePb.BreedStatus_UnBreed,    // 未配
-		CowKind:         calf.CowKind,                     // 牛只品种
+		CowKind:         matherInfo.CowKind,               // 牛只品种
 		BirthWeight:     calf.BirthWeight,
 		BirthAt:         calf.BirthAt,
 		SourceId:        pasturePb.CowSource_Calving, // 产犊方式
-		FatherNumber:    fatherNumber,
-		MotherNumber:    motherNumber,
+		FatherNumber:    matherInfo.EarNumber,
+		MotherNumber:    matherInfo.LastBullNumber,
 		AdmissionStatus: pasturePb.AdmissionStatus_Admission,
 		IsPregnant:      pasturePb.IsShow_No,
 		AdmissionAt:     calf.BirthAt,

+ 8 - 4
model/data_waring.go

@@ -53,18 +53,22 @@ func NewDataWarning(userId int64, Kind string, isShow pasturePb.IsShow_Kind, def
 type DataWarningSlice []*DataWarning
 
 func (d DataWarningSlice) ToPB() []*pasturePb.WarningDataShow {
-	res := make([]*pasturePb.WarningDataShow, len(d))
-	for i, warningData := range d {
+	res := make([]*pasturePb.WarningDataShow, 0)
+	for _, warningData := range d {
+		if warningData.IsShow != pasturePb.IsShow_Ok {
+			continue
+		}
 		dataUpdateTimeFormat := ""
 		if warningData.DataUpdateAt > 0 {
 			dataUpdateTimeFormat = time.Unix(warningData.DataUpdateAt, 0).Format(LayoutTime)
 		}
-		res[i] = &pasturePb.WarningDataShow{
+		res = append(res, &pasturePb.WarningDataShow{
 			Name:                 warningData.Name,
 			Number:               warningData.DataValue,
 			Describe:             warningData.Description,
 			DataUpdateTimeFormat: dataUpdateTimeFormat,
-		}
+			Kind:                 warningData.Kind,
+		})
 	}
 	return res
 }

+ 4 - 10
model/event_cow_treatment.go

@@ -81,7 +81,7 @@ func NewEventCowCurableTreatment(pastureId int64, currUser, operationUser *Syste
 
 type EventCowTreatmentSlice []*EventCowTreatment
 
-func (e EventCowTreatmentSlice) ToPB(eventCowDiseaseList []*EventCowDisease) []*pasturePb.EventCowTreatment {
+func (e EventCowTreatmentSlice) ToPB(eventCowDisease *EventCowDisease) []*pasturePb.EventCowTreatment {
 	res := make([]*pasturePb.EventCowTreatment, len(e))
 	for i, v := range e {
 		prescriptionDetail := make([]*pasturePb.PrescriptionDrugsList, 0)
@@ -92,7 +92,7 @@ func (e EventCowTreatmentSlice) ToPB(eventCowDiseaseList []*EventCowDisease) []*
 			}
 		}
 
-		eventCowTreatment := &pasturePb.EventCowTreatment{
+		res[i] = &pasturePb.EventCowTreatment{
 			CowId:              int32(v.CowId),
 			CowDiseaseId:       int32(v.CowDiseaseId),
 			PrescriptionId:     v.PrescriptionId,
@@ -106,15 +106,9 @@ func (e EventCowTreatmentSlice) ToPB(eventCowDiseaseList []*EventCowDisease) []*
 			UpdatedAt:          int32(v.UpdatedAt),
 			DiseaseId:          int32(v.DiseaseId),
 			Id:                 int32(v.Id),
+			DiseaseName:        eventCowDisease.DiseaseName,
+			DiseaseAt:          int32(eventCowDisease.DiseaseAt),
 		}
-		for _, d := range eventCowDiseaseList {
-			if v.CowDiseaseId != d.Id {
-				continue
-			}
-			eventCowTreatment.DiseaseName = d.DiseaseName
-			eventCowTreatment.DiseaseAt = int32(d.DiseaseAt)
-		}
-		res[i] = eventCowTreatment
 	}
 	return res
 }

+ 0 - 1
model/event_mating.go

@@ -34,7 +34,6 @@ type EventMating struct {
 	MessageId         int64                           `json:"messageId"`
 	MessageName       string                          `json:"messageName"`
 	Remarks           string                          `json:"remarks"`
-	EventEstrusId     int64                           `json:"eventEstrusId"`
 	CreatedAt         int64                           `json:"createdAt"`
 	UpdatedAt         int64                           `json:"updatedAt"`
 }

+ 1 - 0
model/event_pregnant_check.go

@@ -141,6 +141,7 @@ func (e EventPregnantCheckSlice) ToPB(
 			CowId:                   int32(v.CowId),
 			DayAge:                  v.DayAge,
 			Lact:                    int32(v.Lact),
+			EarNumber:               v.EarNumber,
 			PregnantCheckAt:         int32(v.RealityDay),
 			PregnantCheckResult:     v.PregnantCheckResult,
 			PregnantCheckResultName: pregnantCheckResultMap[v.PregnantCheckResult],

+ 0 - 6
model/event_transfer_group.go

@@ -25,12 +25,6 @@ type EventTransferGroup struct {
 func (e *EventTransferGroup) TableName() string {
 	return "event_transfer_group"
 }
-
-type EventTransferGroupModel struct {
-	Cow                *Cow
-	EventTransferGroup *EventTransferGroup
-}
-
 func NewEventTransferGroup(pastureId int64, cow *Cow, req *pasturePb.TransferGroupEventData, transferPenMap map[int32]string, currentUser *SystemUser, operationUser *SystemUser) *EventTransferGroup {
 	return &EventTransferGroup{
 		PastureId:          pastureId,

+ 1 - 0
model/neck_active_habit.go

@@ -25,6 +25,7 @@ type NeckActiveHabit struct {
 	PastureId            int64                 `json:"pastureId"`
 	NeckRingNumber       string                `json:"neckRingNumber"`
 	CowId                int64                 `json:"cowId"`
+	EarNumber            string                `json:"earNumber"`
 	Lact                 int32                 `json:"lact"`
 	CalvingAge           int32                 `json:"calvingAge"`
 	ActiveTime           string                `json:"activeTime"`

+ 33 - 70
model/neck_ring_estrus.go

@@ -5,84 +5,47 @@ import (
 )
 
 type NeckRingEstrus struct {
-	Id               int64                           `json:"id"`
-	PastureId        int64                           `json:"pastureId"`
-	CowId            int64                           `json:"cowId"`
-	NeckRingNumber   string                          `json:"neckRingNumber"`
-	EarNumber        string                          `json:"earNumber"`
-	Lact             int32                           `json:"lact"`
-	ExposeEstrusType pasturePb.ExposeEstrusType_Kind `json:"exposeEstrusType"`
-	EstrusStartDate  string                          `json:"estrusStartDate"`
-	ActiveDate       string                          `json:"activeDate"`
-	LastEstrusDate   string                          `json:"lastEstrusDate"`
-	Level            pasturePb.EstrusLevel_Kind      `json:"level"`
-	IsPeak           pasturePb.IsShow_Kind           `json:"isPeak"`
-	DayHigh          int32                           `json:"dayHigh"`
-	MaxHigh          int32                           `json:"maxHigh"`
-	CheckResult      pasturePb.CheckResult_Kind      `json:"checkResult"`
-	Remarks          string                          `json:"remarks"`
-	IsShow           pasturePb.IsShow_Kind           `json:"isShow"`
-	CreatedAt        int64                           `json:"createdAt"`
-	UpdatedAt        int64                           `json:"updatedAt"`
+	Id              int64                      `json:"id"`
+	PastureId       int64                      `json:"pastureId"`
+	CowId           int64                      `json:"cowId"`
+	NeckRingNumber  string                     `json:"neckRingNumber"`
+	EarNumber       string                     `json:"earNumber"`
+	Lact            int32                      `json:"lact"`
+	EstrusStartDate string                     `json:"estrusStartDate"`
+	LastEstrusDate  string                     `json:"lastEstrusDate"`
+	ActiveDate      string                     `json:"activeDate"`
+	ActiveLevel     pasturePb.EstrusLevel_Kind `json:"activeLevel"`
+	IsPeak          pasturePb.IsShow_Kind      `json:"isPeak"`
+	DayHigh         int32                      `json:"dayHigh"`
+	MaxHigh         int32                      `json:"maxHigh"`
+	CheckResult     pasturePb.CheckResult_Kind `json:"checkResult"`
+	CheckAt         int64                      `json:"checkAt"`
+	IsShow          pasturePb.IsShow_Kind      `json:"isShow"`
+	CreatedAt       int64                      `json:"createdAt"`
+	UpdatedAt       int64                      `json:"updatedAt"`
 }
 
 func (n *NeckRingEstrus) TableName() string {
 	return "neck_ring_estrus"
 }
 
-func NewNeckRingEstrus(
-	pastureId int64,
-	cow *Cow,
-	exposeEstrusType pasturePb.ExposeEstrusType_Kind,
-	level pasturePb.EstrusLevel_Kind,
-	checkResult pasturePb.CheckResult_Kind,
-	isShow pasturePb.IsShow_Kind,
-) *NeckRingEstrus {
+func NewNeckRingEstrus(pastureId int64, cow *Cow, level pasturePb.EstrusLevel_Kind, checkResult pasturePb.CheckResult_Kind, isShow pasturePb.IsShow_Kind) *NeckRingEstrus {
 	return &NeckRingEstrus{
-		PastureId:        pastureId,
-		CowId:            cow.Id,
-		NeckRingNumber:   cow.NeckRingNumber,
-		EarNumber:        cow.EarNumber,
-		Lact:             cow.Lact,
-		ExposeEstrusType: exposeEstrusType,
-		Level:            level,
-		IsShow:           isShow,
-		CheckResult:      checkResult,
+		PastureId:      pastureId,
+		CowId:          cow.Id,
+		NeckRingNumber: cow.NeckRingNumber,
+		EarNumber:      cow.EarNumber,
+		Lact:           cow.Lact,
+		ActiveLevel:    level,
+		IsShow:         isShow,
+		CheckResult:    checkResult,
 	}
 }
 
-type NeckRingEstrusSlice []*NeckRingEstrus
-
-func (n NeckRingEstrusSlice) ToPB(cowMap map[int64]*Cow, eventLogMap map[int64]string) []*pasturePb.EstrusItems {
-	res := make([]*pasturePb.EstrusItems, len(n))
-	for i, v := range n {
-		cow, ok := cowMap[v.CowId]
-		if !ok {
-			cow = &Cow{Id: v.CowId}
-		}
-		lastBreedEventDetails := ""
-		desc, ok := eventLogMap[cow.Id]
-		if ok {
-			lastBreedEventDetails = desc
-		}
-
-		res[i] = &pasturePb.EstrusItems{
-			Id:                     int32(v.Id),
-			CowId:                  int32(v.CowId),
-			EarNumber:              v.EarNumber,
-			PenId:                  cow.PenId,
-			PenName:                cow.PenName,
-			DayAge:                 cow.DayAge,
-			MatingTimes:            cow.MatingTimes,
-			Lact:                   cow.Lact,
-			CalvingAge:             cow.CalvingAge,
-			AbortionAge:            cow.AbortionAge,
-			OptimumMatingStartTime: "",
-			OptimumMatingEndTime:   "",
-			LastBreedEventDetails:  lastBreedEventDetails,
-			Level:                  v.Level,
-			EstrusInterval:         0,
-		}
-	}
-	return res
+type GroupEstrusData struct {
+	CowId     int64
+	PastureId int64
+	EarNumber string
+	Records   []*NeckRingEstrus
+	Moved     bool
 }

+ 96 - 0
model/neck_ring_estrus_warning.go

@@ -0,0 +1,96 @@
+package model
+
+import pasturePb "gitee.com/xuyiping_admin/go_proto/proto/go/backend/cow"
+
+type NeckRingEstrusWarning struct {
+	Id               int64                      `json:"id"`
+	NeckRingEstrusId int64                      `json:"neckRingEstrusId"`
+	PastureId        int64                      `json:"pastureId"`
+	CowId            int64                      `json:"cowId"`
+	EarNumber        string                     `json:"earNumber"`
+	FirstTime        string                     `json:"firstTime"`
+	DateTime         string                     `json:"dateTime"`
+	LastTime         string                     `json:"lastTime"`
+	IsPeak           pasturePb.IsShow_Kind      `json:"isPeak"`
+	WarningKind      pasturePb.Warning_Kind     `json:"warningKind"`
+	Level            pasturePb.EstrusLevel_Kind `json:"level"`
+	HighChange       string                     `json:"highChange"`
+	IsShow           pasturePb.IsShow_Kind      `json:"isShow"`
+	CreatedAt        int64                      `json:"createdAt"`
+	UpdatedAt        int64                      `json:"updatedAt"`
+}
+
+func (n *NeckRingEstrusWarning) TableName() string {
+	return "neck_ring_estrus_warning"
+}
+
+func NewNeckRingEstrusWarning(
+	neckRingEstrusId, pastureId, cowId int64,
+	earNumber, firstTime, dateTime, lastTime string,
+	warningKind pasturePb.Warning_Kind,
+	level pasturePb.EstrusLevel_Kind,
+) *NeckRingEstrusWarning {
+	return &NeckRingEstrusWarning{
+		NeckRingEstrusId: neckRingEstrusId,
+		PastureId:        pastureId,
+		CowId:            cowId,
+		EarNumber:        earNumber,
+		FirstTime:        firstTime,
+		DateTime:         dateTime,
+		LastTime:         lastTime,
+		WarningKind:      warningKind,
+		Level:            level,
+		IsShow:           pasturePb.IsShow_Ok,
+	}
+}
+
+type NeckRingEstrusWarningSlice []*NeckRingEstrusWarning
+
+func (n NeckRingEstrusWarningSlice) ToPB(cowMap map[int64]*Cow, eventLogMap map[int64]string) []*pasturePb.EstrusItems {
+	res := make([]*pasturePb.EstrusItems, len(n))
+	for i, v := range n {
+		cow, ok := cowMap[v.CowId]
+		if !ok {
+			cow = &Cow{Id: v.CowId}
+		}
+		lastBreedEventDetails := ""
+		desc, ok := eventLogMap[cow.Id]
+		if ok {
+			lastBreedEventDetails = desc
+		}
+
+		res[i] = &pasturePb.EstrusItems{
+			Id:                     int32(v.Id),
+			CowId:                  int32(v.CowId),
+			EarNumber:              v.EarNumber,
+			PenId:                  cow.PenId,
+			PenName:                cow.PenName,
+			DayAge:                 cow.DayAge,
+			MatingTimes:            cow.MatingTimes,
+			Lact:                   cow.Lact,
+			CalvingAge:             cow.CalvingAge,
+			AbortionAge:            cow.AbortionAge,
+			OptimumMatingStartTime: "",
+			OptimumMatingEndTime:   "",
+			LastBreedEventDetails:  lastBreedEventDetails,
+			Level:                  v.Level,
+			EstrusInterval:         0,
+		}
+	}
+	return res
+}
+
+const (
+	Nb1    = 12
+	Nb2    = 8
+	MinNb1 = 10
+)
+
+type EstrusWarning struct {
+	NeckRingEstrusId int64  `json:"neckRingEstrusId"`
+	HighChange       string `json:"highChange"`
+	CowId            int64  `json:"cowId"`
+	DateTime         string `json:"dateTime"`
+	Nb1              int32  `json:"nb1"`
+	Nb2              int32  `json:"nb2"`
+}

+ 5 - 0
model/neck_ring_health_warning.go

@@ -2,6 +2,11 @@ package model
 
 import pasturePb "gitee.com/xuyiping_admin/go_proto/proto/go/backend/cow"
 
+const (
+	MinScore = 1
+	MaxScore = 84
+)
+
 type NeckRingHealthWarning struct {
 	Id                 int64                      `json:"id"`
 	PastureId          int64                      `json:"pastureId"`

+ 1 - 0
model/system_role.go

@@ -26,6 +26,7 @@ const (
 	LayoutDate2 = "2006-01-02"
 	LayoutMonth = "2006-01"
 	LayoutHour  = "2006-01-02 15"
+	LayoutYear  = "2006"
 )
 
 type SystemRoleSlice []*SystemRole

+ 8 - 16
module/backend/analysis.go

@@ -69,8 +69,8 @@ func (s *StoreEntry) WeightScatterPlot(ctx context.Context, req *pasturePb.Searc
 			BirthAt:                  int32(cow.BirthAt),
 			BirthWeight:              float32(cow.BirthWeight) / 1000,
 			LastWeightAt:             int32(cow.LastWeightAt),
-			AverageDailyWeightGain:   float32(cow.GetAverageDailyWeight() / 1000),
-			PreviousStageDailyWeight: float32(cow.GetPreviousStageDailyWeight() / 1000),
+			AverageDailyWeightGain:   float32(cow.GetAverageDailyWeight()),
+			PreviousStageDailyWeight: float32(cow.GetPreviousStageDailyWeight()),
 			AdmissionAge:             cow.GetAdmissionAge(),
 		})
 
@@ -355,22 +355,15 @@ func (s *StoreEntry) AbortionRate(ctx context.Context, req *pasturePb.AbortionRa
 		return nil, xerr.WithStack(err)
 	}
 
-	lastDayForMonth := make([]string, 0)
-	for _, v := range dayTimeList {
-		lastDayTime, _ := util.GetLastDayOfMonth(v)
-		lastDayForMonth = append(lastDayForMonth, lastDayTime)
-	}
 	// 历史每月怀孕牛头数量
 	cowPregnantMonthList := make([]*model.CowPregnantMonth, 0)
 	pref := s.DB.Model(new(model.EventMating)).
-		Select(`
-			COUNT(cow_id) AS cow_count,
-			DATE_FORMAT(FROM_UNIXTIME(reality_day),'%Y-%m') as month`,
-		).Where("cow_type = ?", req.CowType).
+		Select(`COUNT(cow_id) AS cow_count,DATE_FORMAT(FROM_UNIXTIME(reality_day),'%Y-%m') as month`).
+		Where("cow_type = ?", req.CowType).
 		Where("pasture_id = ?", userModel.AppPasture.Id).
 		Where("status = ?", pasturePb.IsShow_Ok).
 		Where("mating_result = ?", pasturePb.MatingResult_Pregnant).
-		Where("DATE_FORMAT(FROM_UNIXTIME(`reality_day`),'%Y-%m-%d') IN ?", lastDayForMonth)
+		Where("DATE_FORMAT(FROM_UNIXTIME(reality_day),'%Y-%m') IN (?)", dayTimeList)
 
 	if req.Lact >= 0 && req.Lact <= 3 {
 		pref.Where("lact = ?", req.Lact)
@@ -385,15 +378,14 @@ func (s *StoreEntry) AbortionRate(ctx context.Context, req *pasturePb.AbortionRa
 	// 历史每月流产牛头数量
 	cowAbortionMonthList := make([]*model.CowPregnantMonth, 0)
 	pref2 := s.DB.Model(new(model.EventAbortion)).
-		Select(`
-			COUNT(cow_id) AS cow_count,
-			DATE_FORMAT(FROM_UNIXTIME(abortion_at),'%Y-%m') as month`,
-		).Where("cow_type = ?", req.CowType).
+		Select(`COUNT(cow_id) AS cow_count,DATE_FORMAT(FROM_UNIXTIME(abortion_at),'%Y-%m') as month`).
+		Where("cow_type = ?", req.CowType).
 		Where("DATE_FORMAT(FROM_UNIXTIME(abortion_at),'%Y-%m') IN ?", dayTimeList)
 
 	if req.Lact >= 0 {
 		pref2.Where("lact = ?", req.Lact)
 	}
+
 	if err = pref2.Group("month").Find(&cowAbortionMonthList).Error; err != nil {
 		return nil, xerr.WithStack(err)
 	}

+ 3 - 6
module/backend/analysis_other.go

@@ -303,12 +303,9 @@ func (s *StoreEntry) SaleCowReport(ctx context.Context, req *pasturePb.SaleCowRe
 
 	list := make([]*pasturePb.SaleCowReportList, 0)
 	pref := s.DB.Model(new(model.EventSale)).
-		Select(
-			`SUM(sale_cow_count) AS sale_all_count,
-			SUM(sale_all_amount) AS sale_all_amount,
-			SUM(sale_cow_count) AS sale_all_count,
-			SUM(sale_all_weight) AS sale_all_weight`,
-		).Where("sale_at BETWEEN ? AND ?", startDayTimeUnix, endDayTimeUnix).
+		Select("SUM(sale_cow_count) AS sale_all_count",
+			"SUM(sale_all_amount) AS sale_all_amount", "SUM(sale_cow_count) AS sale_all_count", "SUM(sale_all_weight) AS sale_all_weight").
+		Where("sale_at BETWEEN ? AND ?", startDayTimeUnix, endDayTimeUnix).
 		Where("pasture_id = ?", userModel.AppPasture.Id)
 	if req.AnalysisMethod == pasturePb.SaleCowAnalysisMethod_Months {
 		pref.Select(`DATE_FORMAT(FROM_UNIXTIME(sale_at), '%Y-%m') AS statisticMethod`)

+ 46 - 29
module/backend/calendar.go

@@ -7,7 +7,6 @@ import (
 	"kpt-pasture/util"
 	"net/http"
 	"regexp"
-	"time"
 
 	pasturePb "gitee.com/xuyiping_admin/go_proto/proto/go/backend/cow"
 	"gitee.com/xuyiping_admin/pkg/xerr"
@@ -52,7 +51,7 @@ func (s *StoreEntry) CalendarToDoList(ctx context.Context, req *pasturePb.Calend
 		SELECT cow_id,plan_day ,'产犊' as calendar_type_name FROM event_calving WHERE status = 2 ` + pastureWhereSql + `
 	) as a JOIN cow b ON a.cow_id = b.id WHERE 1 = 1 `
 
-	completeSql := fmt.Sprintf("%s %s ORDER BY a.plan_day ASC", sql, whereSql)
+	completeSql := fmt.Sprintf("%s %s ORDER BY a.plan_day DESC", sql, whereSql)
 	if err = s.DB.Raw(completeSql).Find(&calendarToDoList).Error; err != nil {
 		return nil, err
 	}
@@ -91,20 +90,12 @@ func (s *StoreEntry) CalendarList(ctx context.Context, req *pasturePb.CalendarRe
 	}, nil
 }
 
-func (s *StoreEntry) CalendarTableDetail(
-	ctx context.Context,
-	req *pasturePb.CalendarTableRequest,
-	pagination *pasturePb.PaginationModel,
-) (interface{}, error) {
+func (s *StoreEntry) CalendarTableDetail(ctx context.Context, req *pasturePb.CalendarTableRequest, pagination *pasturePb.PaginationModel) (interface{}, error) {
 	userModel, err := s.GetUserModel(ctx)
 	if err != nil {
 		return nil, xerr.WithStack(err)
 	}
 
-	if req.Start != time.Now().Format(model.LayoutDate2) {
-		return nil, xerr.Custom("只能获取当天的数据")
-	}
-
 	newCalendar := &model.Calendar{}
 	if err = s.DB.Model(&model.Calendar{}).
 		Where("calendar_type = ?", req.CalendarType).
@@ -180,11 +171,10 @@ func (s *StoreEntry) ImmunisationCowList(ctx context.Context, req *pasturePb.Ite
 		pref.Where("b.pen_id = ?", req.PenId)
 	}
 
-	if err = pref.Order("id desc").
-		Count(&count).
+	if err = pref.Count(&count).
 		Limit(int(pagination.PageSize)).
 		Offset(int(pagination.PageOffset)).
-		Order("id desc").
+		Order("a.plan_day DESC").
 		Find(&eventImmunizationPlanList).Error; err != nil {
 		return nil, xerr.WithStack(err)
 	}
@@ -235,7 +225,15 @@ func (s *StoreEntry) SameTimeCowList(ctx context.Context, req *pasturePb.ItemsRe
 		pref.Where("b.cow_type = ?", req.CowType)
 	}
 
-	if err = pref.Order("a.plan_day ASC").Count(&count).
+	if req.SameTimeId > 0 {
+		pref.Where("a.same_time_id = ?", req.SameTimeId)
+	}
+
+	if req.SameTimeType > 0 {
+		pref.Where("a.same_time_type = ?", req.SameTimeType)
+	}
+
+	if err = pref.Order("a.plan_day DESC").Count(&count).
 		Limit(int(pagination.PageSize)).
 		Offset(int(pagination.PageOffset)).
 		Find(&sameTimeBodyList).Error; err != nil {
@@ -283,7 +281,12 @@ func (s *StoreEntry) PregnancyCheckCowList(ctx context.Context, req *pasturePb.I
 	newPregnancyCheckItems := make([]*pasturePb.PregnancyCheckItems, 0)
 	var count int64
 	pref := s.DB.Table(fmt.Sprintf("%s as a", new(model.EventPregnantCheck).TableName())).
-		Select("a.id,a.cow_id,a.ear_number,a.pen_id,a.status,b.breed_status,b.pen_name,b.cow_type,b.day_age,b.calving_age,b.abortion_age,a.bull_id").
+		Select(`a.id,a.cow_id,a.ear_number,a.pen_id,a.status,b.breed_status,b.pen_name,b.cow_type,
+		CASE a.pregnant_check_name
+        WHEN 'pregnant_check_for_first' THEN '初检'
+        WHEN 'pregnant_check_for_second' THEN '复检'
+        ELSE '其他'
+		END AS check_type_name,b.day_age,b.calving_age,b.abortion_age,a.bull_id`).
 		Joins("left join cow as b on a.cow_id = b.id").
 		Where("b.admission_status = ?", pasturePb.AdmissionStatus_Admission).
 		Where("a.pasture_id = ?", userModel.AppPasture.Id).
@@ -294,19 +297,19 @@ func (s *StoreEntry) PregnancyCheckCowList(ctx context.Context, req *pasturePb.I
 		pref.Where("a.plan_day <= ?", dateTime)
 	}
 
-	if req.CowType > 0 {
-		pref.Where("a.cow_type = ?", req.CowType)
+	if req.PenId > 0 {
+		pref.Where("b.pen_id = ?", req.PenId)
 	}
 
-	if req.Status > 0 {
-		pref.Where("a.status = ?", req.Status)
+	if req.CowType > 0 {
+		pref.Where("a.cow_type = ?", req.CowType)
 	}
 
 	if req.PregnantCheckType > 0 {
 		pref.Where("a.pregnant_check_name = ?", model.PregnantCheckNameValueMap[req.PregnantCheckType])
 	}
 
-	if err = pref.Order("a.plan_day ASC").
+	if err = pref.Order("a.plan_day DESC").
 		Count(&count).
 		Limit(int(pagination.PageSize)).
 		Offset(int(pagination.PageOffset)).
@@ -364,7 +367,7 @@ func (s *StoreEntry) WeaningCowList(ctx context.Context, req *pasturePb.ItemsReq
 		pref.Where("a.plan_day <= ?", dateTime)
 	}
 
-	if err = pref.Order("a.plan_day ASC").Count(&count).
+	if err = pref.Order("a.plan_day DESC").Count(&count).
 		Limit(int(pagination.PageSize)).
 		Offset(int(pagination.PageOffset)).
 		Find(&weaningItems).Error; err != nil {
@@ -403,12 +406,16 @@ func (s *StoreEntry) MatingCowList(ctx context.Context, req *pasturePb.ItemsRequ
 	pref := s.DB.Table(fmt.Sprintf("%s as a", new(model.EventMating).TableName())).
 		Select(`a.id,a.cow_id,a.status,a.ear_number,
 		CASE a.expose_estrus_type
-        WHEN 1 THEN '脖环揭发'
-        WHEN 2 THEN '脚环/计步器'
-        WHEN 3 THEN '自然发情'
-        WHEN 4 THEN '同期'
+        	WHEN 1 THEN '脖环揭发'
+        	WHEN 2 THEN '脚环/计步器'
+        	WHEN 3 THEN '自然发情'
+        	WHEN 4 THEN '同期'
         ELSE '其他'
 		END AS expose_estrus_type_name,
+		CASE 
+        	WHEN last_calving_at = 0 THEN ""
+        	ELSE DATE_FORMAT(FROM_UNIXTIME(last_calving_at), '%Y-%m-%d %H:%i:%s')
+    	END AS last_calving_at_format,
 		b.breed_status,b.cow_type,b.pen_id,b.day_age,b.calving_age,b.abortion_age,b.pen_name`).
 		Joins("left join cow as b on a.cow_id = b.id").
 		Where("a.pasture_id = ?", userModel.AppPasture.Id).
@@ -423,7 +430,11 @@ func (s *StoreEntry) MatingCowList(ctx context.Context, req *pasturePb.ItemsRequ
 		pref.Where("b.pen_id = ?", req.PenId)
 	}
 
-	if err = pref.Order("a.plan_day ASC").
+	if req.EarNumber != "" {
+		pref.Where("a.ear_number = ?", req.EarNumber)
+	}
+
+	if err = pref.Order("a.plan_day DESC").
 		Count(&count).
 		Limit(int(pagination.PageSize)).
 		Offset(int(pagination.PageOffset)).
@@ -451,6 +462,7 @@ func (s *StoreEntry) MatingCowList(ctx context.Context, req *pasturePb.ItemsRequ
 				"dayAge":               "日龄",
 				"status":               "状态",
 				"exposeEstrusTypeName": "发情揭发方式",
+				"lastCalvingAtFormat":  "产犊日期",
 			},
 			List: matingItems,
 		},
@@ -465,7 +477,7 @@ func (s *StoreEntry) CalvingCowList(ctx context.Context, req *pasturePb.ItemsReq
 	calvingItems := make([]*pasturePb.CalvingItems, 0)
 	count := int64(0)
 	pref := s.DB.Table(fmt.Sprintf("%s as a", new(model.EventCalving).TableName())).
-		Select(`a.id,a.cow_id,a.ear_number,a.status,b.breed_status,b.pen_id,DATE_FORMAT(FROM_UNIXTIME(last_mating_at), '%Y-%m-%d') AS mating_at_format,
+		Select(`a.id,a.cow_id,a.ear_number,a.status,b.breed_status,b.pen_id,ROUND(b.current_weight/100,2) as current_weight,DATE_FORMAT(FROM_UNIXTIME(last_mating_at), '%Y-%m-%d') AS mating_at_format,
 		b.day_age,b.last_bull_number as bull_id,b.pen_name,DATEDIFF(NOW(),FROM_UNIXTIME(last_mating_at)) AS mating_age,DATE_FORMAT(FROM_UNIXTIME(a.plan_day), '%Y-%m-%d') AS plan_day`).
 		Joins("left join cow as b on a.cow_id = b.id").
 		Where("a.status = ?", pasturePb.IsShow_No).
@@ -481,7 +493,11 @@ func (s *StoreEntry) CalvingCowList(ctx context.Context, req *pasturePb.ItemsReq
 		pref.Where("a.status = ?", req.Status)
 	}
 
-	if err = pref.Order("a.plan_day ASC").
+	if req.PenId > 0 {
+		pref.Where("b.pen_id = ?", req.PenId)
+	}
+
+	if err = pref.Order("a.plan_day DESC").
 		Count(&count).
 		Limit(int(pagination.PageSize)).
 		Offset(int(pagination.PageOffset)).
@@ -518,6 +534,7 @@ func (s *StoreEntry) CalvingCowList(ctx context.Context, req *pasturePb.ItemsReq
 				"bullId":          "配种公牛号",
 				"planDay":         "预产时间",
 				"matingAtFormat":  "配种时间",
+				"currentWeight":   "体重",
 			},
 			List: calvingItems,
 		},

+ 1 - 2
module/backend/config_data.go

@@ -150,7 +150,7 @@ func (s *StoreEntry) CowTypeEnumList(optionName, isAll string) []*pasturePb.Conf
 			Disabled: true,
 		}, &pasturePb.ConfigOptionsList{
 			Value:    int32(pasturePb.CowType_Breeding_Calf),
-			Label:    "经产牛",
+			Label:    "种母牛",
 			Disabled: true,
 		})
 		return cowTypeList
@@ -185,7 +185,6 @@ func (s *StoreEntry) CowTypeEnumList(optionName, isAll string) []*pasturePb.Conf
 		Label:    "种公牛",
 		Disabled: true,
 	})
-
 	return cowTypeList
 }
 

+ 13 - 5
module/backend/cow.go

@@ -81,6 +81,10 @@ func (s *StoreEntry) List(ctx context.Context, req *pasturePb.SearchEventRequest
 		pref.Where("id IN ?", cowIds)
 	}
 
+	if req.EarNumber != "" {
+		pref.Where("ear_number = ?", req.EarNumber)
+	}
+
 	if req.Id > 0 {
 		pref.Where("id = ?", req.Id)
 	}
@@ -155,7 +159,11 @@ func (s *StoreEntry) EventList(ctx context.Context, req *pasturePb.SearchCowEven
 	eventCowLogList := make([]*model.EventCowLog, 0)
 	eventCowLog := &model.EventCowLog{CowId: int64(req.CowId)}
 	pref := s.DB.Table(eventCowLog.TableName()).
-		Where("cow_id = ?", req.CowId).Where("pasture_id = ?", userModel.AppPasture.Id)
+		Where("pasture_id = ?", userModel.AppPasture.Id)
+
+	if req.EarNumber != "" {
+		pref.Where("ear_number = ?", req.EarNumber)
+	}
 
 	if req.Lact >= 0 {
 		pref.Where("lact = ?", req.Lact)
@@ -190,7 +198,7 @@ func (s *StoreEntry) BehaviorCurve(ctx context.Context, req *pasturePb.CowBehavi
 	if err != nil {
 		return nil, xerr.WithStack(err)
 	}
-	cowInfo, err := s.GetCowInfoByCowId(ctx, userModel.AppPasture.Id, int64(req.CowId))
+	cowInfo, err := s.GetCowInfoByEarNumber(ctx, userModel.AppPasture.Id, req.EarNumber)
 	if err != nil {
 		return nil, xerr.Customf("错误的牛只信息: %d", req.CowId)
 	}
@@ -246,14 +254,14 @@ func (s *StoreEntry) BehaviorCurve(ctx context.Context, req *pasturePb.CowBehavi
 	}
 
 	for _, v := range estrusList {
-		if data.EstrusList[v.Level] == nil {
-			data.EstrusList[v.Level] = make([]string, 0)
+		if data.EstrusList[v.ActiveLevel] == nil {
+			data.EstrusList[v.ActiveLevel] = make([]string, 0)
 		}
 		if len(v.EstrusStartDate) > 0 {
 			// 格式化为到小时的字符串
 			parsedTime, _ := time.Parse(model.LayoutTime, v.EstrusStartDate)
 			hourStr := parsedTime.Format(model.LayoutHour)
-			data.EstrusList[v.Level] = append(data.EstrusList[v.Level], hourStr)
+			data.EstrusList[v.ActiveLevel] = append(data.EstrusList[v.ActiveLevel], hourStr)
 		}
 	}
 

+ 8 - 2
module/backend/dashboard.go

@@ -39,7 +39,7 @@ func (s *StoreEntry) NeckRingWarning(ctx context.Context) (*pasturePb.IndexNeckR
 	}
 
 	for _, v := range estrusWarningCowList {
-		estrusWarningLevelItems[int32(v.Level)] += estrusWarningLevelItems[int32(v.Level)]
+		estrusWarningLevelItems[int32(v.ActiveLevel)] += estrusWarningLevelItems[int32(v.ActiveLevel)]
 	}
 
 	healthWarningNumber := int64(0)
@@ -105,6 +105,7 @@ func (s *StoreEntry) FocusIndicatorsList(ctx context.Context, dimension string)
 			continue
 		}
 		onYear, onMonth := "", ""
+		isUp := pasturePb.IsShow_Ok
 		if dimension == "Year" {
 			return nil, xerr.Custom("暂不支持该维度")
 		}
@@ -118,6 +119,9 @@ func (s *StoreEntry) FocusIndicatorsList(ctx context.Context, dimension string)
 					currValue, _ := strconv.ParseFloat(v.Value, 64)
 					onMonthValue := (oldValue - currValue) / oldValue * 100
 					omv := util.RoundToTwoDecimals(onMonthValue)
+					if omv < 0 {
+						isUp = pasturePb.IsShow_No
+					}
 					onMonth = strconv.FormatFloat(omv, 'f', 2, 64) + "%"
 				}
 			}
@@ -130,6 +134,7 @@ func (s *StoreEntry) FocusIndicatorsList(ctx context.Context, dimension string)
 			UnitName: indicatorsDetails.Unit,
 			OnMonth:  onMonth,
 			OnYear:   onYear,
+			IsUp:     isUp,
 		})
 	}
 
@@ -171,7 +176,7 @@ func (s *StoreEntry) DataWarningSet(ctx context.Context, req *pasturePb.IndexDat
 	}
 
 	if len(req.WarningDataSet) <= 0 {
-		return nil
+		return xerr.Custom("请选择预警数据")
 	}
 
 	defaultDataWarning, _ := s.FindDataWarning(ctx, model.DefaultUserId)
@@ -300,6 +305,7 @@ func (s *StoreEntry) updateUserDataWarning(ctx context.Context, userId int64, us
 	if err != nil {
 		return xerr.WithStack(err)
 	}
+
 	if len(userDataWarningItems) == 0 {
 		return xerr.Custom("预警数据有误,请联系管理员!")
 	}

+ 1 - 1
module/backend/enum_map.go

@@ -287,7 +287,7 @@ func (s *StoreEntry) EventCategoryMap() map[pasturePb.EventCategory_Kind]string
 	return res
 }
 
-func (s *StoreEntry) TransferPenMap() map[int32]string {
+func (s *StoreEntry) GroupTransferReasonMap() map[int32]string {
 	res := make(map[int32]string)
 	for _, v := range s.TransferPenEnumList("") {
 		res[v.Value] = v.Label

+ 117 - 76
module/backend/event_base.go

@@ -49,6 +49,10 @@ func (s *StoreEntry) EnterList(ctx context.Context, req *pasturePb.SearchEventRe
 		pref.Where("cow_id IN ?", cowIds)
 	}
 
+	if req.EarNumber != "" {
+		pref.Where("ear_number = ?", req.EarNumber)
+	}
+
 	if err = pref.Order("id desc").
 		Count(&count).Limit(int(pagination.PageSize)).
 		Offset(int(pagination.PageOffset)).
@@ -129,6 +133,11 @@ func (s *StoreEntry) CreateEnter(ctx context.Context, req *pasturePb.EventEnterR
 }
 
 func (s *StoreEntry) GroupTransferList(ctx context.Context, req *pasturePb.SearchEventRequest, pagination *pasturePb.PaginationModel) (*pasturePb.SearchTransferGroupEventResponse, error) {
+	userModel, err := s.GetUserModel(ctx)
+	if err != nil {
+		return nil, xerr.WithStack(err)
+	}
+
 	eventGroupTransferList := make([]*pasturePb.EventTransferGroupData, 0)
 	var count int64 = 0
 	pref := s.DB.Table(fmt.Sprintf("%s as a", new(model.EventTransferGroup).TableName())).
@@ -137,13 +146,19 @@ func (s *StoreEntry) GroupTransferList(ctx context.Context, req *pasturePb.Searc
 		b.name as transfer_in_pen_name,c.name as transfer_out_pen_name,f.lact,f.ear_number`).
 		Joins(fmt.Sprintf("JOIN %s AS b ON a.pen_in_id = b.id", new(model.Pen).TableName())).
 		Joins(fmt.Sprintf("JOIN %s AS c on a.pen_out_id = c.id", new(model.Pen).TableName())).
-		Joins(fmt.Sprintf("JOIN %s AS f ON a.cow_id = f.id", new(model.Cow).TableName()))
+		Joins(fmt.Sprintf("JOIN %s AS f ON a.cow_id = f.id", new(model.Cow).TableName())).
+		Where("a.pasture_id = ?", userModel.AppPasture.Id)
+
 	if len(req.CowId) > 0 {
 		cowIds := strings.Split(req.CowId, ",")
 		pref.Where("a.cow_id IN ?", cowIds)
 	}
 
-	if err := pref.Order("a.id desc").Group("a.id").
+	if req.EarNumber != "" {
+		pref.Where("a.ear_number = ?", req.EarNumber)
+	}
+
+	if err = pref.Order("a.id desc").Group("a.id").
 		Count(&count).Limit(int(pagination.PageSize)).
 		Offset(int(pagination.PageOffset)).
 		Find(&eventGroupTransferList).Error; err != nil {
@@ -168,41 +183,47 @@ func (s *StoreEntry) CreateGroupTransfer(ctx context.Context, req *pasturePb.Tra
 		return xerr.WithStack(err)
 	}
 
-	newEventTransferGroupModelList := make([]*model.EventTransferGroupModel, 0)
-	transferPenMap := s.TransferPenMap()
-	for _, v := range req.Body {
-		cow, err := s.GetCowInfoByCowId(ctx, userModel.AppPasture.Id, int64(v.CowId))
-		if err != nil {
-			return xerr.WithStack(err)
-		}
-		// 转去栏舍和当前栏舍相同,则不处理
-		if cow.PenId == v.TransferInPenId {
-			return xerr.Custom("转入栏舍和牛只当前栏舍不能一致")
-		}
-		operationUser, err := s.GetSystemUserById(ctx, int64(v.OperationId))
-		if err != nil {
-			return xerr.WithStack(err)
-		}
-		newEventTransferGroup := model.NewEventTransferGroup(userModel.AppPasture.Id, cow, v, transferPenMap, userModel.SystemUser, operationUser)
-		newEventTransferGroupModelList = append(newEventTransferGroupModelList, &model.EventTransferGroupModel{
-			Cow:                cow,
-			EventTransferGroup: newEventTransferGroup,
-		})
+	if len(req.Body) <= 0 {
+		return xerr.Custom("请选择牛只数据")
 	}
-	if len(newEventTransferGroupModelList) <= 0 {
-		return nil
+
+	if len(req.Body) > 50 {
+		return xerr.Custom("一次最多只能转移50头牛")
 	}
 
+	transferReasonMap := s.GroupTransferReasonMap()
+	penMap := s.PenMap(ctx, userModel.AppPasture.Id)
+
 	if err = s.DB.Transaction(func(tx *gorm.DB) error {
-		for _, etg := range newEventTransferGroupModelList {
-			if err = tx.Create(etg.EventTransferGroup).Error; err != nil {
+		for _, v := range req.Body {
+			cow, err := s.GetCowInfoByEarNumber(ctx, userModel.AppPasture.Id, v.EarNumber)
+			if err != nil {
 				return xerr.WithStack(err)
 			}
-			if err = tx.Model(etg.Cow).Update("pen_id", etg.EventTransferGroup.PenInId).Error; err != nil {
+			// 转去栏舍和当前栏舍相同,则不处理
+			if cow.PenId == v.TransferInPenId {
+				return xerr.Custom("转入栏舍和牛只当前栏舍不能一致")
+			}
+			operationUser, err := s.GetSystemUserById(ctx, int64(v.OperationId))
+			if err != nil {
+				return xerr.WithStack(err)
+			}
+
+			newEventTransferGroup := model.NewEventTransferGroup(userModel.AppPasture.Id, cow, v, transferReasonMap, userModel.SystemUser, operationUser)
+			if err = tx.Model(new(model.EventTransferGroup)).Create(newEventTransferGroup).Error; err != nil {
+				return xerr.WithStack(err)
+			}
+
+			if err = s.DB.Model(cow).
+				Updates(map[string]interface{}{
+					"pen_id":   v.TransferInPenId,
+					"pen_name": penMap[v.TransferReasonId],
+				}).Error; err != nil {
 				return xerr.WithStack(err)
 			}
+
 			// 事件日志
-			cowLogs := s.SubmitEventLog(ctx, userModel.AppPasture.Id, etg.Cow, pasturePb.EventType_Transfer_Ben, pasturePb.ExposeEstrusType_Invalid, etg.EventTransferGroup)
+			cowLogs := s.SubmitEventLog(ctx, userModel.AppPasture.Id, cow, pasturePb.EventType_Transfer_Ben, pasturePb.ExposeEstrusType_Invalid, newEventTransferGroup)
 			if err = tx.Table(cowLogs.TableName()).Create(cowLogs).Error; err != nil {
 				return xerr.WithStack(err)
 			}
@@ -216,15 +237,26 @@ func (s *StoreEntry) CreateGroupTransfer(ctx context.Context, req *pasturePb.Tra
 }
 
 func (s *StoreEntry) BodyScoreList(ctx context.Context, req *pasturePb.SearchEventRequest, pagination *pasturePb.PaginationModel) (*pasturePb.SearchBodyScoreEventResponse, error) {
+	userModel, err := s.GetUserModel(ctx)
+	if err != nil {
+		return nil, xerr.WithStack(err)
+	}
+
 	bodyScoreList := make([]*pasturePb.BodyScoreList, 0)
 	var count int64 = 0
-	pref := s.DB.Model(new(model.EventBodyScore)).Select("*,score as body_score")
+	pref := s.DB.Model(new(model.EventBodyScore)).
+		Select("*,score as body_score").
+		Where("pasture_id = ?", userModel.AppPasture.Id)
 	if len(req.CowId) > 0 {
 		cowIds := strings.Split(req.CowId, ",")
 		pref.Where("cow_id IN ?", cowIds)
 	}
 
-	if err := pref.Order("id desc").
+	if req.EarNumber != "" {
+		pref.Where("ear_number = ?", req.EarNumber)
+	}
+
+	if err = pref.Order("id desc").
 		Count(&count).Limit(int(pagination.PageSize)).
 		Offset(int(pagination.PageOffset)).
 		Find(&bodyScoreList).Error; err != nil {
@@ -244,7 +276,7 @@ func (s *StoreEntry) BodyScoreList(ctx context.Context, req *pasturePb.SearchEve
 }
 
 func (s *StoreEntry) CreateBodyScore(ctx context.Context, req *pasturePb.BodyScoreEventRequest) error {
-	if len(req.CowId) <= 0 {
+	if req.CowId <= 0 {
 		return xerr.Custom("请选择相关牛只")
 	}
 	userModel, err := s.GetUserModel(ctx)
@@ -257,33 +289,37 @@ func (s *StoreEntry) CreateBodyScore(ctx context.Context, req *pasturePb.BodySco
 		return xerr.WithStack(err)
 	}
 	req.OperationName = operationUser.Name
-	bodyScourEvent := make([]*model.EventBodyScore, 0)
-	cowList, err := s.ParseCowIds(ctx, userModel.AppPasture.Id, req.CowId)
+	cowInfo, err := s.GetCowInfoByCowId(ctx, userModel.AppPasture.Id, int64(req.CowId))
 	if err != nil {
 		return xerr.WithStack(err)
 	}
-	for _, cow := range cowList {
-		bodyScourEvent = append(bodyScourEvent, model.NewEventBodyScore(cow, userModel.AppPasture.Id, userModel.SystemUser, req))
-	}
-	if len(bodyScourEvent) <= 0 {
-		return nil
-	}
 
-	return s.DB.Create(bodyScourEvent).Error
+	bodyScourEvent := model.NewEventBodyScore(cowInfo, userModel.AppPasture.Id, userModel.SystemUser, req)
+	return s.DB.Model(new(model.EventBodyScore)).Create(bodyScourEvent).Error
 }
 
 func (s *StoreEntry) WeightList(ctx context.Context, req *pasturePb.SearchEventRequest, pagination *pasturePb.PaginationModel) (*pasturePb.SearchWeightEventResponse, error) {
+	userModel, err := s.GetUserModel(ctx)
+	if err != nil {
+		return nil, xerr.WithStack(err)
+	}
+
 	weightList := make([]*pasturePb.SearchWeightList, 0)
 	var count int64 = 0
 	pref := s.DB.Table(fmt.Sprintf("%s as a", new(model.EventWeight).TableName())).
 		Select(`a.id,a.cow_id,a.ear_number,ROUND(a.weight/1000, 2) as weight,a.height,a.lact,a.day_age,a.weight_at,a.remarks,a.created_at,
-		a.updated_at,a.message_id,a.operation_id,a.message_name,a.operation_name`)
+		a.updated_at,a.message_id,a.operation_id,a.message_name,a.operation_name`).
+		Where("a.pasture_id = ?", userModel.AppPasture.Id)
 	if len(req.CowId) > 0 {
 		cowIds := strings.Split(req.CowId, ",")
 		pref.Where("a.cow_id IN ?", cowIds)
 	}
 
-	if err := pref.Order("a.id desc").
+	if req.EarNumber != "" {
+		pref.Where("a.ear_number = ?", req.EarNumber)
+	}
+
+	if err = pref.Order("a.id desc").
 		Count(&count).Limit(int(pagination.PageSize)).
 		Offset(int(pagination.PageOffset)).
 		Find(&weightList).Error; err != nil {
@@ -303,48 +339,53 @@ func (s *StoreEntry) WeightList(ctx context.Context, req *pasturePb.SearchEventR
 }
 
 func (s *StoreEntry) WeightBatch(ctx context.Context, req *pasturePb.BatchEventWeight) (err error) {
+	userModel, err := s.GetUserModel(ctx)
+	if err != nil {
+		return xerr.WithStack(err)
+	}
+
 	if len(req.Items) <= 0 {
 		return xerr.Custom("称重数据不能为空")
 	}
 
-	userModel, err := s.GetUserModel(ctx)
-	if err != nil {
-		return xerr.WithStack(err)
+	if len(req.Items) > 50 {
+		return xerr.Custom("一次最多只能转移50头牛")
 	}
 
-	defer func() {
-		if err == nil {
-			// 记录事件日志
-			for _, item := range req.Items {
-				cow, _ := s.GetCowInfoByCowId(ctx, userModel.AppPasture.Id, int64(item.CowId))
-				cowLogs := s.SubmitEventLog(ctx, userModel.AppPasture.Id, cow, pasturePb.EventType_Weight, pasturePb.ExposeEstrusType_Invalid, item)
-				s.DB.Table(cowLogs.TableName()).Create(cowLogs)
+	if err = s.DB.Transaction(func(tx *gorm.DB) error {
+		for _, item := range req.Items {
+			cow, err := s.GetCowInfoByEarNumber(ctx, userModel.AppPasture.Id, item.EarNumber)
+			if err != nil {
+				return xerr.WithStack(err)
 			}
-		}
-	}()
-	cow := &model.Cow{}
-	for _, item := range req.Items {
-		cow, err = s.GetCowInfoByCowId(ctx, userModel.AppPasture.Id, int64(item.CowId))
-		if err != nil {
-			return xerr.WithStack(err)
-		}
 
-		// 更新牛只信息
-		cow.EventWeightUpdate(int64(item.Weight*1000), int64(item.WeightAt))
-		if err = s.DB.Model(new(model.Cow)).
-			Select("last_second_weight_at", "last_second_weight", "last_weight_at", "current_weight").
-			Where("id = ?", cow.Id).
-			Where("admission_status = ?", pasturePb.AdmissionStatus_Admission).
-			Updates(cow).Error; err != nil {
-			return xerr.WithStack(err)
-		}
-		operationUser, _ := s.GetSystemUserById(ctx, int64(item.OperationId))
-		item.OperationName = operationUser.Name
-		// 创建牛只的体重记录
-		eventWeight := model.NewEventWeight(userModel.AppPasture.Id, cow, userModel.SystemUser, item)
-		if err = s.DB.Create(eventWeight).Error; err != nil {
-			return xerr.WithStack(err)
+			// 更新牛只信息
+			cow.EventWeightUpdate(int64(item.Weight*1000), int64(item.WeightAt))
+			if err = tx.Model(new(model.Cow)).
+				Select("last_second_weight_at", "last_second_weight", "last_weight_at", "current_weight").
+				Where("id = ?", cow.Id).
+				Where("admission_status = ?", pasturePb.AdmissionStatus_Admission).
+				Updates(cow).Error; err != nil {
+				return xerr.WithStack(err)
+			}
+
+			operationUser, _ := s.GetSystemUserById(ctx, int64(item.OperationId))
+			item.OperationName = operationUser.Name
+			// 创建牛只的体重记录
+			eventWeight := model.NewEventWeight(userModel.AppPasture.Id, cow, userModel.SystemUser, item)
+			if err = tx.Create(eventWeight).Error; err != nil {
+				return xerr.WithStack(err)
+			}
+
+			cowLogs := s.SubmitEventLog(ctx, userModel.AppPasture.Id, cow, pasturePb.EventType_Weight, pasturePb.ExposeEstrusType_Invalid, item)
+			if err = tx.Table(cowLogs.TableName()).Create(cowLogs).Error; err != nil {
+				return xerr.WithStack(err)
+			}
 		}
+		return nil
+	}); err != nil {
+		return xerr.WithStack(err)
 	}
-	return err
+
+	return nil
 }

+ 8 - 1
module/backend/event_base_more.go

@@ -198,7 +198,8 @@ func (s *StoreEntry) CowSaleList(ctx context.Context, req *pasturePb.EventCowSal
 
 	eventSale := make([]*model.EventSale, 0)
 	var count int64
-	pref := s.DB.Model(new(model.EventSale)).Where("pasture_id = ?", userModel.AppPasture.Id)
+	pref := s.DB.Model(new(model.EventSale)).
+		Where("pasture_id = ?", userModel.AppPasture.Id)
 	if req.StartDayAt > 0 && req.EndDayAt > 0 && req.EndDayAt >= req.StartDayAt {
 		pref.Where("sale_at BETWEEN ? AND ?", req.StartDayAt, req.EndDayAt)
 	}
@@ -334,6 +335,7 @@ func (s *StoreEntry) ImmunizationBatch(ctx context.Context, req *pasturePb.Immun
 				return xerr.WithStack(err)
 			}
 
+			// 更新数据
 			eventImmunization.EventUpdate(int64(req.ImmunizationAt), cowInfo, drugsInfo, req.Usage, operationUser, userModel.SystemUser, req.Remarks)
 			if err = tx.Model(new(model.EventImmunizationPlan)).
 				Select("ear_number", "lact", "day_age", "status", "operation_id", "operation_name", "message_id",
@@ -343,6 +345,11 @@ func (s *StoreEntry) ImmunizationBatch(ctx context.Context, req *pasturePb.Immun
 				return xerr.WithStack(err)
 			}
 
+			// 记录日志
+			cowLog := s.SubmitEventLog(ctx, userModel.AppPasture.Id, cowInfo, pasturePb.EventType_Immunication, pasturePb.ExposeEstrusType_Invalid, eventImmunization)
+			if err = tx.Table(cowLog.TableName()).Create(cowLog).Error; err != nil {
+				return xerr.WithStack(err)
+			}
 		}
 
 		return nil

+ 51 - 31
module/backend/event_breed.go

@@ -7,6 +7,7 @@ import (
 	"kpt-pasture/util"
 	"net/http"
 	"strings"
+	"time"
 
 	"gitee.com/xuyiping_admin/pkg/logger/zaplog"
 	"go.uber.org/zap"
@@ -35,6 +36,10 @@ func (s *StoreEntry) CalvingList(ctx context.Context, req *pasturePb.SearchEvent
 		pref.Where("a.cow_id IN ?", cowIds)
 	}
 
+	if req.EarNumber != "" {
+		pref.Where("a.ear_number = ?", req.EarNumber)
+	}
+
 	if err = pref.Order("a.id desc").
 		Count(&count).Limit(int(pagination.PageSize)).
 		Offset(int(pagination.PageOffset)).
@@ -113,7 +118,7 @@ func (s *StoreEntry) CalvingCreate(ctx context.Context, req *pasturePb.EventCalv
 
 		for _, calf := range req.CalfItemList {
 			// 犊牛信息
-			newCalvingCalf := model.NewEventCalvingCalf(userModel.AppPasture.Id, int64(req.CowId), newEventCalving.Id, int64(req.CalvingAt), penMap, calf)
+			newCalvingCalf := model.NewEventCalvingCalf(userModel.AppPasture.Id, newEventCalving.Id, int64(req.CalvingAt), cow, penMap, calf)
 			if err = tx.Create(newCalvingCalf).Error; err != nil {
 				return xerr.WithStack(err)
 			}
@@ -122,7 +127,7 @@ func (s *StoreEntry) CalvingCreate(ctx context.Context, req *pasturePb.EventCalv
 				continue
 			}
 
-			newCalfCow := model.NewCalfCow(cow.NeckRingNumber, cow.LastBullNumber, newCalvingCalf)
+			newCalfCow := model.NewCalfCow(cow, newCalvingCalf)
 			if err = tx.Create(newCalfCow).Error; err != nil {
 				return xerr.WithStack(err)
 			}
@@ -221,6 +226,7 @@ func (s *StoreEntry) SameTimeBatch(ctx context.Context, req *pasturePb.EventSame
 	}
 	req.DrugsName = drugs.Name
 
+	nowTime := time.Now().Format(model.LayoutDate2)
 	eventCowSameTimeList := make([]*model.EventCowSameTime, 0)
 	for _, v := range req.CowIds {
 		eventCowSameTime, err := s.GetEventCowSameTimeByCowId(ctx, userModel.AppPasture.Id, int64(v))
@@ -228,38 +234,12 @@ func (s *StoreEntry) SameTimeBatch(ctx context.Context, req *pasturePb.EventSame
 			zaplog.Error("SameTimeCreate", zap.Any("err", err), zap.Any("req", req))
 			return xerr.WithStack(err)
 		}
+		if time.Unix(eventCowSameTime.PlanDay, 0).Format(model.LayoutDate2) != nowTime {
+			return xerr.Customf("该牛只不是今日计划: %d", eventCowSameTime.EarNumber)
+		}
 		eventCowSameTimeList = append(eventCowSameTimeList, eventCowSameTime)
 	}
 
-	defer func() {
-		// 记录牛只事件日志
-		if err == nil {
-			for _, v := range eventCowSameTimeList {
-				cow, _ := s.GetCowInfoByCowId(ctx, userModel.AppPasture.Id, v.CowId)
-				cowLogs := s.SubmitEventLog(
-					ctx,
-					userModel.AppPasture.Id,
-					cow,
-					pasturePb.EventType_Seme_Time,
-					pasturePb.ExposeEstrusType_Same_Time,
-					&pasturePb.EventSameTime{
-						Id:               int32(v.Id),
-						CowId:            int32(v.CowId),
-						SameTimeId:       int32(v.SameTimeId),
-						SameTimeType:     v.SameTimeType,
-						SameTimeTypeName: v.SameTimeName,
-						DrugsId:          int32(v.DrugsId),
-						DrugsName:        drugs.Name,
-						Usage:            req.Usage,
-						OperationId:      int32(operationUser.Id),
-						OperationName:    operationUser.Name,
-						SameTimeAt:       req.SameTimeAt,
-					})
-				s.DB.Table(cowLogs.TableName()).Create(cowLogs)
-			}
-		}
-	}()
-
 	if err = s.DB.Transaction(func(tx *gorm.DB) error {
 		for _, v := range eventCowSameTimeList {
 			// 更新SameTime
@@ -270,6 +250,30 @@ func (s *StoreEntry) SameTimeBatch(ctx context.Context, req *pasturePb.EventSame
 				Updates(v).Error; err != nil {
 				return xerr.WithStack(err)
 			}
+
+			cow, _ := s.GetCowInfoByCowId(ctx, userModel.AppPasture.Id, v.CowId)
+			cowLogs := s.SubmitEventLog(
+				ctx,
+				userModel.AppPasture.Id,
+				cow,
+				pasturePb.EventType_Seme_Time,
+				pasturePb.ExposeEstrusType_Same_Time,
+				&pasturePb.EventSameTime{
+					Id:               int32(v.Id),
+					CowId:            int32(v.CowId),
+					SameTimeId:       int32(v.SameTimeId),
+					SameTimeType:     v.SameTimeType,
+					SameTimeTypeName: v.SameTimeName,
+					DrugsId:          int32(v.DrugsId),
+					DrugsName:        drugs.Name,
+					Usage:            req.Usage,
+					OperationId:      int32(operationUser.Id),
+					OperationName:    operationUser.Name,
+					SameTimeAt:       req.SameTimeAt,
+				})
+			if err = tx.Table(cowLogs.TableName()).Create(cowLogs).Error; err != nil {
+				return xerr.WithStack(err)
+			}
 		}
 		return nil
 	}); err != nil {
@@ -296,6 +300,10 @@ func (s *StoreEntry) SameTimeList(
 		pref.Where("cow_id IN ?", cowIds)
 	}
 
+	if req.EarNumber != "" {
+		pref.Where("ear_number = ?", req.EarNumber)
+	}
+
 	if err = pref.Count(&count).
 		Limit(int(pagination.PageSize)).
 		Offset(int(pagination.PageOffset)).
@@ -333,6 +341,10 @@ func (s *StoreEntry) EstrusList(ctx context.Context, req *pasturePb.EstrusItemsR
 		pref.Where("cow_id IN ?", cowIds)
 	}
 
+	if req.EarNumber != "" {
+		pref.Where("ear_number = ?", req.EarNumber)
+	}
+
 	if req.Level > 0 {
 		pref.Where("level = ?", req.Level)
 	}
@@ -387,6 +399,14 @@ func (s *StoreEntry) EstrusBatchMating(ctx context.Context, req *pasturePb.Event
 		// 记录事件日志
 		for _, v := range eventEstrusList {
 			cow, _ := s.GetCowInfoByCowId(ctx, userModel.AppPasture.Id, v.CowId)
+			// 更新牛最近发情时间
+			cow.EstrusUpdate(v.RealityDay)
+			if err = tx.Model(cow).
+				Select("last_estrus_at").
+				Where("id = ?", cow.Id).
+				Updates(cow).Error; err != nil {
+				return xerr.WithStack(err)
+			}
 			cowLogs := s.SubmitEventLog(ctx, userModel.AppPasture.Id, cow, pasturePb.EventType_Estrus, v.ExposeEstrusType, v)
 			if err = tx.Table(cowLogs.TableName()).Create(cowLogs).Error; err != nil {
 				return xerr.WithStack(err)

+ 38 - 9
module/backend/event_breed_more.go

@@ -3,6 +3,7 @@ package backend
 import (
 	"context"
 	"errors"
+	"fmt"
 	"kpt-pasture/model"
 	"kpt-pasture/util"
 	"net/http"
@@ -25,15 +26,24 @@ func (s *StoreEntry) PregnantCheckList(ctx context.Context, req *pasturePb.Searc
 
 	pregnantCheckList := make([]*model.EventPregnantCheck, 0)
 	var count int64 = 0
-	pref := s.DB.Table(new(model.EventPregnantCheck).TableName()).
-		Where("pasture_id = ?", userModel.AppPasture.Id).
-		Where("status = ?", pasturePb.IsShow_Ok)
+	pref := s.DB.Table(fmt.Sprintf("%s as a", new(model.EventPregnantCheck).TableName())).
+		Joins(fmt.Sprintf("JOIN %s AS b on a.cow_id = b.id", new(model.Cow).TableName())).
+		Where("a.pasture_id = ?", userModel.AppPasture.Id).
+		Where("a.status = ?", pasturePb.IsShow_Ok)
 	if len(req.CowId) > 0 {
 		cowIds := strings.Split(req.CowId, ",")
-		pref.Where("cow_id IN ?", cowIds)
+		pref.Where("a.cow_id IN ?", cowIds)
 	}
 
-	if err = pref.Order("id desc").
+	if req.EarNumber != "" {
+		pref.Where("a.ear_number = ?", req.EarNumber)
+	}
+
+	if req.PenId > 0 {
+		pref.Where("b.pen_id = ?", req.PenId)
+	}
+
+	if err = pref.Order("a.id desc").
 		Count(&count).Limit(int(pagination.PageSize)).
 		Offset(int(pagination.PageOffset)).
 		Find(&pregnantCheckList).Error; err != nil {
@@ -169,14 +179,17 @@ func (s *StoreEntry) AbortionCreateBatch(ctx context.Context, req *pasturePb.Eve
 		return xerr.WithStack(err)
 	}
 
+	if len(eventAbortionModelList) <= 0 {
+		return xerr.Custom("数据异常")
+	}
+
 	if err = s.DB.Transaction(func(tx *gorm.DB) error {
 		for _, item := range eventAbortionModelList {
 			if err = tx.Create(item.EventAbortion).Error; err != nil {
 				return xerr.WithStack(err)
 			}
 
-			item.Cow.EventAbortionUpdate(item.EventAbortion.AbortionAt)
-
+			item.Cow.EventAbortionUpdate(item.EventAbortion.AbortionAt, item.IsLact)
 			// 更新牛只状态
 			if err = tx.Model(new(model.Cow)).
 				Select("is_pregnant", "last_abortion_at", "breed_status").
@@ -186,7 +199,8 @@ func (s *StoreEntry) AbortionCreateBatch(ctx context.Context, req *pasturePb.Eve
 			}
 
 			lastCowMating := &model.EventMating{}
-			if err = s.DB.Model(new(model.EventMating)).Where("cow_id = ?", item.Cow.Id).
+			if err = s.DB.Model(new(model.EventMating)).
+				Where("cow_id = ?", item.Cow.Id).
 				Where("mating_result = ?", pasturePb.MatingResult_Pregnant).
 				Order("id desc").First(lastCowMating).Error; err != nil {
 				return xerr.WithStack(err)
@@ -199,12 +213,17 @@ func (s *StoreEntry) AbortionCreateBatch(ctx context.Context, req *pasturePb.Eve
 				Where("id = ?", lastCowMating.Id).
 				Updates(lastCowMating).Error; err != nil {
 			}
+
+			// 记录日志
+			cowLog := s.SubmitEventLog(ctx, userModel.AppPasture.Id, item.Cow, pasturePb.EventType_Abort, pasturePb.ExposeEstrusType_Invalid, item)
+			if err = tx.Table(cowLog.TableName()).Create(cowLog).Error; err != nil {
+				return xerr.WithStack(err)
+			}
 		}
 		return nil
 	}); err != nil {
 		return xerr.WithStack(err)
 	}
-
 	return nil
 }
 
@@ -235,6 +254,10 @@ func (s *StoreEntry) AbortionList(
 		pref.Where("abortion_at BETWEEN ? AND ?", req.StartDayAt, req.EndDayAt)
 	}
 
+	if req.EarNumber != "" {
+		pref.Where("ear_number = ?", req.EarNumber)
+	}
+
 	if err = pref.Order("id desc").
 		Count(&count).Limit(int(pagination.PageSize)).
 		Offset(int(pagination.PageOffset)).
@@ -271,6 +294,10 @@ func (s *StoreEntry) MatingList(ctx context.Context, req *pasturePb.SearchEventR
 		pref.Where("cow_id IN ?", cowIds)
 	}
 
+	if req.EarNumber != "" {
+		pref.Where("ear_number = ?", req.EarNumber)
+	}
+
 	if err = pref.Order("id desc").
 		Count(&count).Limit(int(pagination.PageSize)).
 		Offset(int(pagination.PageOffset)).
@@ -312,9 +339,11 @@ func (s *StoreEntry) MatingCreate(ctx context.Context, req *pasturePb.EventMatin
 				// 1. 没有配种信息,(第一次参加配种的牛只,并且没有参与同期的牛只 )
 				if errors.Is(err, gorm.ErrRecordNotFound) {
 					newMating := model.NewEventMatingNaturalEstrus(userModel.AppPasture.Id, cow, req, userModel.SystemUser)
+
 					if err = tx.Create(newMating).Error; err != nil {
 						return xerr.WithStack(err)
 					}
+
 					if err = s.MatingCowUpdate(ctx, userModel.AppPasture.Id, cow, req, false); err != nil {
 						return xerr.WithStack(err)
 					}

+ 27 - 5
module/backend/event_check.go

@@ -2,7 +2,6 @@ package backend
 
 import (
 	"context"
-	"fmt"
 	"kpt-pasture/model"
 	"time"
 
@@ -44,6 +43,7 @@ type AbortionCheckBatchModel struct {
 	EventAbortion *model.EventAbortion
 	Cow           *model.Cow
 	OperationUser *model.SystemUser
+	IsLact        pasturePb.IsShow_Kind
 }
 
 func (s *StoreEntry) EnterCheck(ctx context.Context, req *pasturePb.EventEnterRequest) error {
@@ -67,7 +67,7 @@ func (s *StoreEntry) MatingCreateCheck(ctx context.Context, pastureId int64, req
 		cowIds = append(cowIds, int64(v))
 	}
 
-	if len(cowIds) > 20 {
+	if len(cowIds) > 50 {
 		return nil, xerr.Custom("最多只能选择50只牛只")
 	}
 
@@ -129,6 +129,10 @@ func (s *StoreEntry) PregnantCheckDataCheck(ctx context.Context, pastureId int64
 		return nil, xerr.Custom("请选择相关牛只数据")
 	}
 
+	if len(req.Items) > 50 {
+		return nil, xerr.Custom("一次性最多限制提交50牛数据")
+	}
+
 	pregnantCheckBatchModelList := make([]*PregnantCheckBatchModel, 0)
 	cowInfo := &model.Cow{}
 	var err error
@@ -196,7 +200,7 @@ func (s *StoreEntry) EstrusCheckDataCheck(ctx context.Context, userModel *model.
 	unMatingReasonsMap := s.UnMatingReasonsMap()
 
 	for _, item := range items {
-		cowInfo, err := s.GetCowInfoByCowId(ctx, userModel.AppPasture.Id, int64(item.CowId))
+		cowInfo, err := s.GetCowInfoByEarNumber(ctx, userModel.AppPasture.Id, item.EarNumber)
 		if err != nil {
 			return nil, nil, xerr.Custom("牛只信息不存在")
 		}
@@ -205,6 +209,18 @@ func (s *StoreEntry) EstrusCheckDataCheck(ctx context.Context, userModel *model.
 			return nil, nil, xerr.Custom("该牛只不是母牛")
 		}
 
+		if item.EstrusAt <= 0 {
+			return nil, nil, xerr.Custom("发情时间不能为空")
+		}
+
+		if int64(item.EstrusAt) <= cowInfo.BirthAt {
+			return nil, nil, xerr.Custom("发情时间不能小于出生时间")
+		}
+
+		if int64(item.EstrusAt) <= cowInfo.LastCalvingAt {
+			return nil, nil, xerr.Custom("发情时间不能小于产犊时间")
+		}
+
 		existsEventEstrus, isExists, err := s.FindEventEstrusByCowId(ctx, userModel.AppPasture.Id, int64(item.CowId))
 		if err != nil {
 			return nil, nil, xerr.WithStack(err)
@@ -228,7 +244,6 @@ func (s *StoreEntry) EstrusCheckDataCheck(ctx context.Context, userModel *model.
 		newEventEstrus := model.NewEventEstrus(userModel.AppPasture.Id, cowInfo, pasturePb.ExposeEstrusType_Natural_Estrus,
 			pasturePb.IsShow_Ok, item.IsMating, item.EstrusAt, operationUser, userModel.SystemUser)
 
-		fmt.Println("item.IsMating", item.IsMating, item.UnMatingReasonsKind)
 		if item.IsMating == pasturePb.IsShow_Ok {
 			newEventMating := model.NewEventMating(userModel.AppPasture.Id, cowInfo, time.Now().Unix(), pasturePb.ExposeEstrusType_Natural_Estrus)
 			eventMatingList = append(eventMatingList, newEventMating)
@@ -244,9 +259,15 @@ func (s *StoreEntry) EstrusCheckDataCheck(ctx context.Context, userModel *model.
 }
 
 func (s *StoreEntry) AbortionEventDataCheck(ctx context.Context, userModel *model.UserModel, items []*pasturePb.EventAbortionItem) ([]*AbortionCheckBatchModel, error) {
+	if len(items) <= 0 {
+		return nil, xerr.Custom("请选择相关数据")
+	}
+	if len(items) > 50 {
+		return nil, xerr.Custom("一次性最多限制提交50牛数据")
+	}
 	abortionCheckBatchModelList := make([]*AbortionCheckBatchModel, 0)
 	for _, item := range items {
-		cow, err := s.GetCowInfoByCowId(ctx, userModel.AppPasture.Id, int64(item.CowId))
+		cow, err := s.GetCowInfoByEarNumber(ctx, userModel.AppPasture.Id, item.EarNumber)
 		if err != nil {
 			return nil, xerr.WithStack(err)
 		}
@@ -275,6 +296,7 @@ func (s *StoreEntry) AbortionEventDataCheck(ctx context.Context, userModel *mode
 			EventAbortion: newEventAbortion,
 			Cow:           cow,
 			OperationUser: operationUser,
+			IsLact:        item.IsLact,
 		})
 	}
 	return abortionCheckBatchModelList, nil

+ 11 - 6
module/backend/event_cow_log.go

@@ -140,6 +140,11 @@ func (s *StoreEntry) SubmitEventLog(
 		operationUser.Id = data.OperationId
 		operationUser.Name = data.OperationName
 	case pasturePb.EventType_Immunication:
+		data := req.(*model.EventImmunizationPlan)
+		eventAt = data.RealityDay
+		desc = fmt.Sprintf("免疫名称: %s;药物名称: %s;剂量: %d %s", data.PlanName, data.DrugsName, data.Usage, data.UnitName)
+		operationUser.Id = data.OperationId
+		operationUser.Name = data.OperationName
 	case pasturePb.EventType_Weaning:
 		data := req.(*pasturePb.EventWeaningBatch)
 		eventAt = int64(data.WeaningAt)
@@ -162,12 +167,12 @@ func (s *StoreEntry) SubmitEventLog(
 			}
 		}
 	case pasturePb.EventType_Abort:
-		data := req.(*pasturePb.EventAbortionItem)
-		eventAt = int64(data.AbortionAt)
-		operationUser.Id = int64(data.OperationId)
-		operationUser.Name = data.OperationName
-		remarks = data.Remarks
-		desc = fmt.Sprintf("流产原因: %s", s.AbortionReasonsMap()[data.AbortionReasons])
+		data := req.(*AbortionCheckBatchModel)
+		eventAt = data.EventAbortion.AbortionAt
+		operationUser.Id = data.OperationUser.Id
+		operationUser.Name = data.OperationUser.Name
+		remarks = data.EventAbortion.Remarks
+		desc = fmt.Sprintf("流产原因: %s", data.EventAbortion.AbortionReasonsName)
 	case pasturePb.EventType_Weight:
 		data := req.(*pasturePb.EventWeight)
 		eventAt = int64(data.WeightAt)

+ 21 - 24
module/backend/event_health.go

@@ -25,7 +25,7 @@ func (s *StoreEntry) CowDiseaseCreate(ctx context.Context, req *pasturePb.EventC
 		return xerr.WithStack(err)
 	}
 	// 牛只信息
-	cow, err := s.GetCowInfoByCowId(ctx, userModel.AppPasture.Id, int64(req.CowId))
+	cow, err := s.GetCowInfoByEarNumber(ctx, userModel.AppPasture.Id, req.EarNumber)
 	if err != nil {
 		return xerr.Customf("牛只信息错误: %d", req.CowId)
 	}
@@ -186,8 +186,8 @@ func (s *StoreEntry) EstrusCowList(ctx context.Context, req *pasturePb.EstrusIte
 	}
 
 	var count int64
-	neckRingEstrusList := make([]*model.NeckRingEstrus, 0)
-	pref := s.DB.Model(new(model.NeckRingEstrus)).
+	neckRingEstrusList := make([]*model.NeckRingEstrusWarning, 0)
+	pref := s.DB.Model(new(model.NeckRingEstrusWarning)).
 		Where("pasture_id = ?", userModel.AppPasture.Id).
 		Where("is_show = ?", pasturePb.IsShow_Ok)
 
@@ -205,7 +205,7 @@ func (s *StoreEntry) EstrusCowList(ctx context.Context, req *pasturePb.EstrusIte
 		pref.Where("pen_id IN ?", penIds)
 	}
 
-	if err = pref.Order("level desc").
+	if err = pref.Order("level DESC").
 		Count(&count).
 		Limit(int(pagination.PageSize)).
 		Offset(int(pagination.PageOffset)).
@@ -235,7 +235,7 @@ func (s *StoreEntry) EstrusCowList(ctx context.Context, req *pasturePb.EstrusIte
 		Code: http.StatusOK,
 		Msg:  "ok",
 		Data: &pasturePb.EstrusItemsData{
-			List:     model.NeckRingEstrusSlice(neckRingEstrusList).ToPB(cowMap, eventLogMap),
+			List:     model.NeckRingEstrusWarningSlice(neckRingEstrusList).ToPB(cowMap, eventLogMap),
 			Total:    int32(count),
 			PageSize: pagination.PageSize,
 			Page:     pagination.Page,
@@ -255,12 +255,17 @@ func (s *StoreEntry) CowDiseaseList(ctx context.Context, req *pasturePb.SearchEv
 	pref := s.DB.Table(fmt.Sprintf("%s as a", new(model.EventCowDisease).TableName())).
 		Joins(fmt.Sprintf("JOIN %s AS b on a.cow_id = b.id", new(model.Cow).TableName())).
 		Select("a.*,b.pen_name").
-		Where("a.pasture_id = ?", userModel.AppPasture.Id)
+		Where("a.pasture_id = ?", userModel.AppPasture.Id).
+		Where("a.health_status != ?", pasturePb.HealthStatus_Curable)
 
 	if len(req.CowIds) > 0 {
 		pref.Where("a.cow_id IN ?", req.CowIds)
 	}
 
+	if req.EarNumber != "" {
+		pref.Where("a.ear_number = ?", req.EarNumber)
+	}
+
 	if req.DiseaseId > 0 {
 		pref.Where("a.disease_id = ?", req.DiseaseId)
 	}
@@ -277,7 +282,7 @@ func (s *StoreEntry) CowDiseaseList(ctx context.Context, req *pasturePb.SearchEv
 		pref.Where("a.disease_at BETWEEN ? AND ?", req.DiseasedStartAt, req.DiseaseEndAt)
 	}
 
-	if err = pref.Order("a.id desc").
+	if err = pref.Order("a.id DESC").
 		Count(&count).Limit(int(pagination.PageSize)).
 		Offset(int(pagination.PageOffset)).
 		Find(&cowDiseaseList).Error; err != nil {
@@ -523,20 +528,18 @@ func (s *StoreEntry) DiseaseSuggestPrescription(ctx context.Context, diseaseId i
 }
 
 // CowDiseaseTreatmentDetail 发病牛只治疗详情列表
-func (s *StoreEntry) CowDiseaseTreatmentDetail(
-	ctx context.Context,
-	req *pasturePb.EventCowTreatmentDetailRequest,
-	pagination *pasturePb.PaginationModel,
-) (*pasturePb.EventCowTreatmentDetailResponse, error) {
+func (s *StoreEntry) CowDiseaseTreatmentDetail(ctx context.Context, req *pasturePb.EventCowTreatmentDetailRequest, pagination *pasturePb.PaginationModel) (*pasturePb.EventCowTreatmentDetailResponse, error) {
 	userModel, err := s.GetUserModel(ctx)
 	if err != nil {
 		return nil, xerr.WithStack(err)
 	}
 
-	eventCowDiseaseList := make([]*model.EventCowDisease, 0)
+	eventCowDisease := &model.EventCowDisease{}
 	var count int64 = 0
 	pref := s.DB.Model(new(model.EventCowDisease)).
-		Where("cow_id = ?", req.CowId).Where("pasture_id = ?", userModel.AppPasture.Id)
+		Where("cow_id = ?", req.CowId).
+		Where("pasture_id = ?", userModel.AppPasture.Id).
+		Where("id = ?", req.Id)
 
 	if req.DiseaseId > 0 {
 		pref.Where("disease_id = ?", req.DiseaseId)
@@ -550,20 +553,14 @@ func (s *StoreEntry) CowDiseaseTreatmentDetail(
 		Limit(int(pagination.PageSize)).
 		Offset(int(pagination.PageOffset)).
 		Order("id desc").
-		Find(&eventCowDiseaseList).Error; err != nil {
+		First(&eventCowDisease).Error; err != nil {
 		return nil, xerr.WithStack(err)
 	}
 
 	eventCowTreatmentList := make([]*model.EventCowTreatment, 0)
-	cowDiseaseIds := make([]int64, len(eventCowDiseaseList))
-	for i, v := range eventCowDiseaseList {
-		cowDiseaseIds[i] = v.Id
-	}
-
-	if err := s.DB.Model(new(model.EventCowTreatment)).
-		Where("cow_disease_id IN ?", cowDiseaseIds).
+	if err = s.DB.Model(new(model.EventCowTreatment)).
+		Where("cow_disease_id = ?", req.Id).
 		Where("cow_id = ?", req.CowId).
-		Group("cow_disease_id").
 		Order("id desc").
 		Find(&eventCowTreatmentList).Error; err != nil {
 		return nil, xerr.WithStack(err)
@@ -573,7 +570,7 @@ func (s *StoreEntry) CowDiseaseTreatmentDetail(
 		Code: http.StatusOK,
 		Msg:  "ok",
 		Data: &pasturePb.EventCowTreatmentDetail{
-			List:     model.EventCowTreatmentSlice(eventCowTreatmentList).ToPB(eventCowDiseaseList),
+			List:     model.EventCowTreatmentSlice(eventCowTreatmentList).ToPB(eventCowDisease),
 			Total:    int32(count),
 			PageSize: pagination.Page,
 			Page:     pagination.PageSize,

+ 16 - 0
module/backend/sql.go

@@ -163,6 +163,22 @@ func (s *StoreEntry) PenMap(ctx context.Context, pastureId int64) map[int32]*mod
 	return penMap
 }
 
+func (s *StoreEntry) GetCowInfoByEarNumber(ctx context.Context, pastureId int64, earNumber string) (*model.Cow, error) {
+	cowInfo := &model.Cow{}
+	if err := s.DB.Model(new(model.Cow)).
+		Where("pasture_id = ?", pastureId).
+		Where("ear_number = ?", earNumber).
+		Where("admission_status = ?", pasturePb.AdmissionStatus_Admission).
+		First(cowInfo).Error; err != nil {
+		if errors.Is(err, gorm.ErrRecordNotFound) {
+			return nil, xerr.Customf("该牛只数据不存在: %d", earNumber)
+		} else {
+			return nil, xerr.WithStack(err)
+		}
+	}
+	return cowInfo, nil
+}
+
 func (s *StoreEntry) GetCowInfoByCowId(ctx context.Context, pastureId, cowId int64) (*model.Cow, error) {
 	cowData := &model.Cow{Id: cowId}
 	if err := s.DB.Model(new(model.Cow)).

+ 1 - 1
module/crontab/cow_indicators_base.go

@@ -19,7 +19,7 @@ func (e *Entry) FindPastureAllCow(pastureList []*model.AppPastureList) map[int64
 	res := make(map[int64]string)
 	for _, pasture := range pastureList {
 		var count int64
-		if err := e.DB.Model(&model.Cow{}).
+		if err := e.DB.Model(new(model.Cow)).
 			Where("pasture_id = ?", pasture.Id).
 			Where("admission_status = ?", pasturePb.AdmissionStatus_Admission).
 			Count(&count).Error; err != nil {

+ 2 - 2
module/crontab/health_warning.go

@@ -11,8 +11,8 @@ func (e *Entry) HealthWarning(pastureId int64, processIds []int64) (err error) {
 	if err = e.DB.Model(new(model.NeckActiveHabit)).
 		Where("pasture_id = ?", pastureId).
 		Where("id IN (?)", processIds).
-		Where("score BETWEEN ? AND ?", 1, 84).
-		Order("heat_date,neck_ring_number,frameid").
+		Where("score BETWEEN ? AND ?", model.MinScore, model.MaxScore).
+		Order("neck_ring_number,heat_date,frameid").
 		Find(&newNeckActiveHabitList).Error; err != nil {
 		return xerr.WithStack(err)
 	}

+ 2 - 1
module/crontab/interface.go

@@ -35,7 +35,8 @@ type Crontab interface {
 	DeleteOldOriginal() error
 
 	// UpdateCowEstrus 脖环数据
-	UpdateCowEstrus() error           // 获取牛只发情数据 2小时执行一下
+	UpdateCowEstrus() error           // 获取牛只疑似发情数据
 	NeckRingOriginalMergeData() error // 合并脖环数据
 	NeckRingCalculate() error         // 更新脖环数据
+	UpdateNeckRingWarning() error     // 发情和健康预警
 }

+ 9 - 11
module/crontab/neck_ring_estrus.go

@@ -40,7 +40,7 @@ func (e *Entry) UpdateCowEstrus() (err error) {
 
 func (e *Entry) EntryCowEstrus(pastureId int64) (err error) {
 	xToday := &XToday{}
-	systemConfigureList, err := e.GetSystemConfigure(pastureId)
+	systemConfigureList, err := e.GetSystemNeckRingConfigure(pastureId)
 	for _, v := range systemConfigureList {
 		switch v.Name {
 		case model.ActiveLow:
@@ -134,7 +134,7 @@ func (e *Entry) CowEstrusWarning(pastureId int64, xToday *XToday) (err error) {
 		}
 
 		if (int32(maxCft) > before3Data.DayHigh || b48 > B48) && int32(maxCft)+cowEstrus.HadJust > xToday.ActiveLow {
-			level := calculateLevel(maxCft, cowEstrus, xToday)
+			level := calculateActiveLevel(maxCft, cowEstrus, xToday)
 			cowInfo := e.FindCowInfoByNeckRingNumber(cowHabitList[0].NeckRingNumber)
 			isShow := pasturePb.IsShow_Ok
 			if cowInfo.IsPregnant == pasturePb.IsShow_Ok && level == pasturePb.EstrusLevel_Low {
@@ -158,7 +158,7 @@ func (e *Entry) CowEstrusWarning(pastureId int64, xToday *XToday) (err error) {
 				zap.Any("cowInfo", cowInfo),
 				zap.Any("cowHabitList", cowHabitList),
 			)
-			newNeckRingEstrus := model.NewNeckRingEstrus(pastureId, cowInfo, pasturePb.ExposeEstrusType_Neck_Ring, level, checkResult, isShow)
+			newNeckRingEstrus := model.NewNeckRingEstrus(pastureId, cowInfo, level, checkResult, isShow)
 			newNeckRingEstrus.LastEstrusDate = lastEstrusDate
 			newNeckRingEstrus.ActiveDate = activeDate
 			newNeckRingEstrus.DayHigh = dayHigh
@@ -169,7 +169,6 @@ func (e *Entry) CowEstrusWarning(pastureId int64, xToday *XToday) (err error) {
 	}
 
 	zaplog.Info("CowEstrusWarning", zap.Any("neckRingEstrusList", neckRingEstrusList))
-
 	if len(neckRingEstrusList) > 0 {
 		if err = e.DB.Model(new(model.NeckRingEstrus)).Create(neckRingEstrusList).Error; err != nil {
 			zaplog.Error("CowEstrusWarningNew", zap.Any("eventEstrusList", neckRingEstrusList), zap.Any("err", err))
@@ -189,7 +188,6 @@ func (e *Entry) UpdateEstrusStartDate(pastureId int64, xToday time.Time) (err er
 		Select("cow_id,MIN(estrus_start_date) as estrus_start_date").
 		Where("active_date BETWEEN  ? AND  ?", xToday.Add(-24*time.Hour).Format(model.LayoutTime), xToday.Format(model.LayoutTime)).
 		Where("estrus_start_date != ?", "").
-		Where("expose_estrus_type = ?", pasturePb.ExposeEstrusType_Neck_Ring).
 		Where("pasture_id = ?", pastureId).
 		Group("cow_id").Find(&beforeEventEstrus).Error; err != nil {
 		return xerr.WithStack(err)
@@ -234,17 +232,17 @@ func calculateCFT(habit *model.NeckActiveHabit) (cft float32) {
 	return cft
 }
 
-// calculateLevel 计算发情等级
-func calculateLevel(cft float32, cowEstrus *CowEstrus, xToday *XToday) pasturePb.EstrusLevel_Kind {
-	level := pasturePb.EstrusLevel_High
+// calculateActiveLevel 计算活动量等级
+func calculateActiveLevel(cft float32, cowEstrus *CowEstrus, xToday *XToday) pasturePb.EstrusLevel_Kind {
+	activeLevel := pasturePb.EstrusLevel_High
 	if int32(cft)+cowEstrus.HadJust < xToday.ActiveMiddle {
-		level = pasturePb.EstrusLevel_Low
+		activeLevel = pasturePb.EstrusLevel_Low
 	}
 
 	if int32(cft)+cowEstrus.HadJust >= xToday.ActiveHigh {
-		level = pasturePb.EstrusLevel_Middle
+		activeLevel = pasturePb.EstrusLevel_Middle
 	}
-	return level
+	return activeLevel
 }
 
 // getResult 根据b3数据计算结果 0 1 2 3 -1 -2

+ 3228 - 2
module/crontab/neck_ring_estus_test.go

@@ -1,11 +1,3237 @@
 package crontab
 
 import (
+	"encoding/json"
 	"fmt"
+	"kpt-pasture/model"
 	"testing"
 )
 
 func TestCalculateCFT(t *testing.T) {
-	str := `event_mating`
-	fmt.Println(str)
+	neckActiveHabitList := make([]*model.NeckActiveHabit, 0)
+	str := `[
+	    {
+	        "id": 1725734,
+	        "pastureId": 1,
+	        "neckRingNumber": "10054",
+	        "cowId": 109,
+	        "lact": 3,
+	        "calvingAge": 166,
+	        "activeTime": "2025-02-25 01:00:00",
+	        "frameid": 0,
+	        "heatDate": "2025-02-25",
+	        "rumina": 34,
+	        "intake": 51,
+	        "inactive": 49,
+	        "gasp": 0,
+	        "other": 0,
+	        "high": 2119,
+	        "active": 81,
+	        "filterHigh": 43,
+	        "filterRumina": 16,
+	        "filterChew": 16,
+	        "weekHigh": 1500,
+	        "weekHighHabit": 268,
+	        "weekRuminaHabit": 10,
+	        "weekIntakeHabit": 24,
+	        "weekChewHabit": 14,
+	        "weekInactiveHabit": 24,
+	        "weekOtherHabit": 0,
+	        "changeHigh": -19,
+	        "changeRumina": 60,
+	        "changeChew": 14,
+	        "changeAdjust": -19,
+	        "changeFilter": -6,
+	        "ruminaFilter": 18,
+	        "chewFilter": 4,
+	        "filterCorrect": 97,
+	        "sumRumina": 62,
+	        "sumIntake": 269,
+	        "sumInactive": 445,
+	        "sumActive": 659,
+	        "sumMinHigh": -10000,
+	        "sumMaxHigh": 18,
+	        "SumMinChew": 0,
+	        "beforeThreeSumRumina": 70,
+	        "beforeThreeSumIntake": 200,
+	        "score": 97,
+	        "isMaxTime": 2,
+	        "isShow": 2,
+	        "recordCount": 6,
+	        "firmwareVersion": 57,
+	        "createdAt": 1740414165,
+	        "updatedAt": 1740466954
+	    },
+	    {
+	        "id": 1725740,
+	        "pastureId": 1,
+	        "neckRingNumber": "10055",
+	        "cowId": 110,
+	        "lact": 2,
+	        "calvingAge": 61,
+	        "activeTime": "2025-02-25 01:00:00",
+	        "frameid": 0,
+	        "heatDate": "2025-02-25",
+	        "rumina": 61,
+	        "intake": 8,
+	        "inactive": 48,
+	        "gasp": 0,
+	        "other": 0,
+	        "high": 1851,
+	        "active": 59,
+	        "filterHigh": 326,
+	        "filterRumina": 27,
+	        "filterChew": 35,
+	        "weekHigh": 1500,
+	        "weekHighHabit": 744,
+	        "weekRuminaHabit": 16,
+	        "weekIntakeHabit": 19,
+	        "weekChewHabit": 23,
+	        "weekInactiveHabit": 19,
+	        "weekOtherHabit": 0,
+	        "changeHigh": -33,
+	        "changeRumina": 69,
+	        "changeChew": 52,
+	        "changeAdjust": 0,
+	        "changeFilter": -10,
+	        "ruminaFilter": 10,
+	        "chewFilter": 16,
+	        "filterCorrect": 95,
+	        "sumRumina": 120,
+	        "sumIntake": 235,
+	        "sumInactive": 605,
+	        "sumActive": 623,
+	        "sumMinHigh": -10000,
+	        "sumMaxHigh": 18,
+	        "SumMinChew": 0,
+	        "beforeThreeSumRumina": 105,
+	        "beforeThreeSumIntake": 272,
+	        "score": 96,
+	        "isMaxTime": 2,
+	        "isShow": 2,
+	        "recordCount": 6,
+	        "firmwareVersion": 57,
+	        "createdAt": 1740415350,
+	        "updatedAt": 1740466954
+	    },
+	    {
+	        "id": 1725743,
+	        "pastureId": 1,
+	        "neckRingNumber": "10032",
+	        "cowId": 88,
+	        "lact": 0,
+	        "calvingAge": 0,
+	        "activeTime": "2025-02-25 01:00:00",
+	        "frameid": 0,
+	        "heatDate": "2025-02-25",
+	        "rumina": 5,
+	        "intake": 12,
+	        "inactive": 31,
+	        "gasp": 0,
+	        "other": 0,
+	        "high": 2273,
+	        "active": 86,
+	        "filterHigh": 513,
+	        "filterRumina": 3,
+	        "filterChew": 8,
+	        "weekHigh": 1500,
+	        "weekHighHabit": 878,
+	        "weekRuminaHabit": 12,
+	        "weekIntakeHabit": 29,
+	        "weekChewHabit": 22,
+	        "weekInactiveHabit": 29,
+	        "weekOtherHabit": 0,
+	        "changeHigh": -28,
+	        "changeRumina": -75,
+	        "changeChew": -64,
+	        "changeAdjust": 0,
+	        "changeFilter": -7,
+	        "ruminaFilter": -9,
+	        "chewFilter": -10,
+	        "filterCorrect": 97,
+	        "sumRumina": 61,
+	        "sumIntake": 220,
+	        "sumInactive": 632,
+	        "sumActive": 504,
+	        "sumMinHigh": -10000,
+	        "sumMaxHigh": 11,
+	        "SumMinChew": 0,
+	        "beforeThreeSumRumina": 140,
+	        "beforeThreeSumIntake": 206,
+	        "score": 97,
+	        "isMaxTime": 2,
+	        "isShow": 2,
+	        "recordCount": 6,
+	        "firmwareVersion": 57,
+	        "createdAt": 1740415395,
+	        "updatedAt": 1740466954
+	    },
+	    {
+	        "id": 1725746,
+	        "pastureId": 1,
+	        "neckRingNumber": "10028",
+	        "cowId": 84,
+	        "lact": 2,
+	        "calvingAge": 299,
+	        "activeTime": "2025-02-25 01:00:00",
+	        "frameid": 0,
+	        "heatDate": "2025-02-25",
+	        "rumina": 7,
+	        "intake": 23,
+	        "inactive": 11,
+	        "gasp": 0,
+	        "other": 0,
+	        "high": 3126,
+	        "active": 109,
+	        "filterHigh": 1004,
+	        "filterRumina": 9,
+	        "filterChew": 24,
+	        "weekHigh": 1500,
+	        "weekHighHabit": 802,
+	        "weekRuminaHabit": 8,
+	        "weekIntakeHabit": 12,
+	        "weekChewHabit": 18,
+	        "weekInactiveHabit": 12,
+	        "weekOtherHabit": 0,
+	        "changeHigh": 16,
+	        "changeRumina": 13,
+	        "changeChew": 33,
+	        "changeAdjust": 0,
+	        "changeFilter": 5,
+	        "ruminaFilter": 4,
+	        "chewFilter": 10,
+	        "filterCorrect": 95,
+	        "sumRumina": 123,
+	        "sumIntake": 271,
+	        "sumInactive": 563,
+	        "sumActive": 569,
+	        "sumMinHigh": -10000,
+	        "sumMaxHigh": 15,
+	        "SumMinChew": 7,
+	        "beforeThreeSumRumina": 175,
+	        "beforeThreeSumIntake": 275,
+	        "score": 100,
+	        "isMaxTime": 2,
+	        "isShow": 2,
+	        "recordCount": 6,
+	        "firmwareVersion": 57,
+	        "createdAt": 1740415655,
+	        "updatedAt": 1740466954
+	    },
+	    {
+	        "id": 1725748,
+	        "pastureId": 1,
+	        "neckRingNumber": "10030",
+	        "cowId": 86,
+	        "lact": 2,
+	        "calvingAge": 299,
+	        "activeTime": "2025-02-25 01:00:00",
+	        "frameid": 0,
+	        "heatDate": "2025-02-25",
+	        "rumina": 38,
+	        "intake": 13,
+	        "inactive": 33,
+	        "gasp": 0,
+	        "other": 0,
+	        "high": 2514,
+	        "active": 81,
+	        "filterHigh": 270,
+	        "filterRumina": 18,
+	        "filterChew": 22,
+	        "weekHigh": 1500,
+	        "weekHighHabit": 336,
+	        "weekRuminaHabit": 20,
+	        "weekIntakeHabit": 17,
+	        "weekChewHabit": 27,
+	        "weekInactiveHabit": 17,
+	        "weekOtherHabit": 0,
+	        "changeHigh": -6,
+	        "changeRumina": -10,
+	        "changeChew": -19,
+	        "changeAdjust": 0,
+	        "changeFilter": -2,
+	        "ruminaFilter": -3,
+	        "chewFilter": -6,
+	        "filterCorrect": 97,
+	        "sumRumina": 159,
+	        "sumIntake": 254,
+	        "sumInactive": 637,
+	        "sumActive": 587,
+	        "sumMinHigh": -10000,
+	        "sumMaxHigh": 18,
+	        "SumMinChew": 3,
+	        "beforeThreeSumRumina": 139,
+	        "beforeThreeSumIntake": 251,
+	        "score": 99,
+	        "isMaxTime": 2,
+	        "isShow": 2,
+	        "recordCount": 6,
+	        "firmwareVersion": 57,
+	        "createdAt": 1740415655,
+	        "updatedAt": 1740466954
+	    },
+	    {
+	        "id": 1725749,
+	        "pastureId": 1,
+	        "neckRingNumber": "10038",
+	        "cowId": 94,
+	        "lact": 0,
+	        "calvingAge": 0,
+	        "activeTime": "2025-02-25 01:00:00",
+	        "frameid": 0,
+	        "heatDate": "2025-02-25",
+	        "rumina": 0,
+	        "intake": 0,
+	        "inactive": 41,
+	        "gasp": 0,
+	        "other": 0,
+	        "high": 76,
+	        "active": 3,
+	        "filterHigh": 28,
+	        "filterRumina": 1,
+	        "filterChew": 1,
+	        "weekHigh": 1500,
+	        "weekHighHabit": 430,
+	        "weekRuminaHabit": 9,
+	        "weekIntakeHabit": 25,
+	        "weekChewHabit": 12,
+	        "weekInactiveHabit": 25,
+	        "weekOtherHabit": 0,
+	        "changeHigh": -34,
+	        "changeRumina": -89,
+	        "changeChew": -92,
+	        "changeAdjust": 0,
+	        "changeFilter": 0,
+	        "ruminaFilter": 0,
+	        "chewFilter": 0,
+	        "filterCorrect": 98,
+	        "sumRumina": 11,
+	        "sumIntake": 0,
+	        "sumInactive": 432,
+	        "sumActive": 153,
+	        "sumMinHigh": -7,
+	        "sumMaxHigh": 1,
+	        "SumMinChew": 0,
+	        "beforeThreeSumRumina": 58,
+	        "beforeThreeSumIntake": 252,
+	        "score": 68,
+	        "isMaxTime": 2,
+	        "isShow": 2,
+	        "recordCount": 6,
+	        "firmwareVersion": 57,
+	        "createdAt": 1740415660,
+	        "updatedAt": 1740466954
+	    },
+	    {
+	        "id": 1725756,
+	        "pastureId": 1,
+	        "neckRingNumber": "10053",
+	        "cowId": 108,
+	        "lact": 0,
+	        "calvingAge": 0,
+	        "activeTime": "2025-02-25 01:00:00",
+	        "frameid": 0,
+	        "heatDate": "2025-02-25",
+	        "rumina": 25,
+	        "intake": 17,
+	        "inactive": 51,
+	        "gasp": 0,
+	        "other": 0,
+	        "high": 1263,
+	        "active": 54,
+	        "filterHigh": 318,
+	        "filterRumina": 8,
+	        "filterChew": 19,
+	        "weekHigh": 1500,
+	        "weekHighHabit": 511,
+	        "weekRuminaHabit": 15,
+	        "weekIntakeHabit": 21,
+	        "weekChewHabit": 29,
+	        "weekInactiveHabit": 21,
+	        "weekOtherHabit": 0,
+	        "changeHigh": -16,
+	        "changeRumina": -47,
+	        "changeChew": -34,
+	        "changeAdjust": 0,
+	        "changeFilter": -4,
+	        "ruminaFilter": -11,
+	        "chewFilter": -10,
+	        "filterCorrect": 96,
+	        "sumRumina": 140,
+	        "sumIntake": 293,
+	        "sumInactive": 709,
+	        "sumActive": 578,
+	        "sumMinHigh": -10000,
+	        "sumMaxHigh": 19,
+	        "SumMinChew": 10,
+	        "beforeThreeSumRumina": 183,
+	        "beforeThreeSumIntake": 266,
+	        "score": 98,
+	        "isMaxTime": 2,
+	        "isShow": 2,
+	        "recordCount": 6,
+	        "firmwareVersion": 57,
+	        "createdAt": 1740416500,
+	        "updatedAt": 1740466954
+	    },
+	    {
+	        "id": 1725759,
+	        "pastureId": 1,
+	        "neckRingNumber": "10027",
+	        "cowId": 30,
+	        "lact": 0,
+	        "calvingAge": 0,
+	        "activeTime": "2025-02-25 01:00:00",
+	        "frameid": 0,
+	        "heatDate": "2025-02-25",
+	        "rumina": 23,
+	        "intake": 43,
+	        "inactive": 5,
+	        "gasp": 0,
+	        "other": 0,
+	        "high": 2761,
+	        "active": 100,
+	        "filterHigh": 1279,
+	        "filterRumina": 10,
+	        "filterChew": 20,
+	        "weekHigh": 1500,
+	        "weekHighHabit": 1121,
+	        "weekRuminaHabit": 8,
+	        "weekIntakeHabit": 30,
+	        "weekChewHabit": 16,
+	        "weekInactiveHabit": 30,
+	        "weekOtherHabit": 0,
+	        "changeHigh": 12,
+	        "changeRumina": 25,
+	        "changeChew": 25,
+	        "changeAdjust": 0,
+	        "changeFilter": 3,
+	        "ruminaFilter": 6,
+	        "chewFilter": 8,
+	        "filterCorrect": 94,
+	        "sumRumina": 121,
+	        "sumIntake": 268,
+	        "sumInactive": 644,
+	        "sumActive": 621,
+	        "sumMinHigh": -10000,
+	        "sumMaxHigh": 20,
+	        "SumMinChew": 5,
+	        "beforeThreeSumRumina": 121,
+	        "beforeThreeSumIntake": 205,
+	        "score": 100,
+	        "isMaxTime": 2,
+	        "isShow": 2,
+	        "recordCount": 6,
+	        "firmwareVersion": 57,
+	        "createdAt": 1740416525,
+	        "updatedAt": 1740466955
+	    },
+	    {
+	        "id": 1725764,
+	        "pastureId": 1,
+	        "neckRingNumber": "10054",
+	        "cowId": 109,
+	        "lact": 3,
+	        "calvingAge": 167,
+	        "activeTime": "2025-02-25 03:00:00",
+	        "frameid": 1,
+	        "heatDate": "2025-02-25",
+	        "rumina": 36,
+	        "intake": 20,
+	        "inactive": 20,
+	        "gasp": 0,
+	        "other": 0,
+	        "high": 2019,
+	        "active": 77,
+	        "filterHigh": 318,
+	        "filterRumina": 7,
+	        "filterChew": 8,
+	        "weekHigh": 1500,
+	        "weekHighHabit": 356,
+	        "weekRuminaHabit": 5,
+	        "weekIntakeHabit": 20,
+	        "weekChewHabit": 10,
+	        "weekInactiveHabit": 20,
+	        "weekOtherHabit": 0,
+	        "changeHigh": -3,
+	        "changeRumina": 40,
+	        "changeChew": -20,
+	        "changeAdjust": 0,
+	        "changeFilter": -1,
+	        "ruminaFilter": 12,
+	        "chewFilter": -6,
+	        "filterCorrect": 97,
+	        "sumRumina": 69,
+	        "sumIntake": 303,
+	        "sumInactive": 517,
+	        "sumActive": 674,
+	        "sumMinHigh": -10000,
+	        "sumMaxHigh": 18,
+	        "SumMinChew": 0,
+	        "beforeThreeSumRumina": 71,
+	        "beforeThreeSumIntake": 241,
+	        "score": 99,
+	        "isMaxTime": 2,
+	        "isShow": 2,
+	        "recordCount": 6,
+	        "firmwareVersion": 57,
+	        "createdAt": 1740421320,
+	        "updatedAt": 1740466955
+	    },
+	    {
+	        "id": 1725771,
+	        "pastureId": 1,
+	        "neckRingNumber": "10055",
+	        "cowId": 110,
+	        "lact": 2,
+	        "calvingAge": 62,
+	        "activeTime": "2025-02-25 03:00:00",
+	        "frameid": 1,
+	        "heatDate": "2025-02-25",
+	        "rumina": 4,
+	        "intake": 31,
+	        "inactive": 46,
+	        "gasp": 0,
+	        "other": 0,
+	        "high": 2875,
+	        "active": 73,
+	        "filterHigh": 1212,
+	        "filterRumina": 0,
+	        "filterChew": 12,
+	        "weekHigh": 1500,
+	        "weekHighHabit": 1042,
+	        "weekRuminaHabit": 2,
+	        "weekIntakeHabit": 32,
+	        "weekChewHabit": 18,
+	        "weekInactiveHabit": 32,
+	        "weekOtherHabit": 0,
+	        "changeHigh": 13,
+	        "changeRumina": -100,
+	        "changeChew": -33,
+	        "changeAdjust": 0,
+	        "changeFilter": 4,
+	        "ruminaFilter": -15,
+	        "chewFilter": -10,
+	        "filterCorrect": 95,
+	        "sumRumina": 120,
+	        "sumIntake": 237,
+	        "sumInactive": 567,
+	        "sumActive": 666,
+	        "sumMinHigh": -10000,
+	        "sumMaxHigh": 18,
+	        "SumMinChew": 0,
+	        "beforeThreeSumRumina": 102,
+	        "beforeThreeSumIntake": 253,
+	        "score": 100,
+	        "isMaxTime": 2,
+	        "isShow": 2,
+	        "recordCount": 6,
+	        "firmwareVersion": 57,
+	        "createdAt": 1740422555,
+	        "updatedAt": 1740466955
+	    },
+	    {
+	        "id": 1725774,
+	        "pastureId": 1,
+	        "neckRingNumber": "10028",
+	        "cowId": 84,
+	        "lact": 2,
+	        "calvingAge": 300,
+	        "activeTime": "2025-02-25 03:00:00",
+	        "frameid": 1,
+	        "heatDate": "2025-02-25",
+	        "rumina": 65,
+	        "intake": 16,
+	        "inactive": 66,
+	        "gasp": 0,
+	        "other": 0,
+	        "high": 1436,
+	        "active": 50,
+	        "filterHigh": 382,
+	        "filterRumina": 17,
+	        "filterChew": 21,
+	        "weekHigh": 1500,
+	        "weekHighHabit": 138,
+	        "weekRuminaHabit": 13,
+	        "weekIntakeHabit": 8,
+	        "weekChewHabit": 10,
+	        "weekInactiveHabit": 8,
+	        "weekOtherHabit": 0,
+	        "changeHigh": 21,
+	        "changeRumina": 31,
+	        "changeChew": 110,
+	        "changeAdjust": 0,
+	        "changeFilter": 6,
+	        "ruminaFilter": 9,
+	        "chewFilter": 16,
+	        "filterCorrect": 95,
+	        "sumRumina": 131,
+	        "sumIntake": 277,
+	        "sumInactive": 566,
+	        "sumActive": 655,
+	        "sumMinHigh": -10000,
+	        "sumMaxHigh": 15,
+	        "SumMinChew": 7,
+	        "beforeThreeSumRumina": 193,
+	        "beforeThreeSumIntake": 290,
+	        "score": 100,
+	        "isMaxTime": 2,
+	        "isShow": 2,
+	        "recordCount": 6,
+	        "firmwareVersion": 57,
+	        "createdAt": 1740422555,
+	        "updatedAt": 1740466955
+	    },
+	    {
+	        "id": 1725778,
+	        "pastureId": 1,
+	        "neckRingNumber": "10032",
+	        "cowId": 88,
+	        "lact": 0,
+	        "calvingAge": 0,
+	        "activeTime": "2025-02-25 03:00:00",
+	        "frameid": 1,
+	        "heatDate": "2025-02-25",
+	        "rumina": 0,
+	        "intake": 46,
+	        "inactive": 49,
+	        "gasp": 0,
+	        "other": 0,
+	        "high": 1726,
+	        "active": 55,
+	        "filterHigh": 1027,
+	        "filterRumina": 0,
+	        "filterChew": 25,
+	        "weekHigh": 1500,
+	        "weekHighHabit": 689,
+	        "weekRuminaHabit": 9,
+	        "weekIntakeHabit": 27,
+	        "weekChewHabit": 21,
+	        "weekInactiveHabit": 27,
+	        "weekOtherHabit": 0,
+	        "changeHigh": 27,
+	        "changeRumina": -100,
+	        "changeChew": 19,
+	        "changeAdjust": 0,
+	        "changeFilter": 6,
+	        "ruminaFilter": -12,
+	        "chewFilter": 6,
+	        "filterCorrect": 97,
+	        "sumRumina": 56,
+	        "sumIntake": 242,
+	        "sumInactive": 576,
+	        "sumActive": 587,
+	        "sumMinHigh": -10000,
+	        "sumMaxHigh": 11,
+	        "SumMinChew": 0,
+	        "beforeThreeSumRumina": 136,
+	        "beforeThreeSumIntake": 224,
+	        "score": 100,
+	        "isMaxTime": 2,
+	        "isShow": 2,
+	        "recordCount": 6,
+	        "firmwareVersion": 57,
+	        "createdAt": 1740422595,
+	        "updatedAt": 1740466955
+	    },
+	    {
+	        "id": 1725782,
+	        "pastureId": 1,
+	        "neckRingNumber": "10030",
+	        "cowId": 86,
+	        "lact": 2,
+	        "calvingAge": 300,
+	        "activeTime": "2025-02-25 03:00:00",
+	        "frameid": 1,
+	        "heatDate": "2025-02-25",
+	        "rumina": 27,
+	        "intake": 64,
+	        "inactive": 12,
+	        "gasp": 0,
+	        "other": 0,
+	        "high": 2781,
+	        "active": 96,
+	        "filterHigh": 592,
+	        "filterRumina": 17,
+	        "filterChew": 21,
+	        "weekHigh": 1500,
+	        "weekHighHabit": 807,
+	        "weekRuminaHabit": 7,
+	        "weekIntakeHabit": 37,
+	        "weekChewHabit": 21,
+	        "weekInactiveHabit": 37,
+	        "weekOtherHabit": 0,
+	        "changeHigh": -17,
+	        "changeRumina": 143,
+	        "changeChew": 0,
+	        "changeAdjust": 0,
+	        "changeFilter": -5,
+	        "ruminaFilter": 21,
+	        "chewFilter": 0,
+	        "filterCorrect": 97,
+	        "sumRumina": 176,
+	        "sumIntake": 243,
+	        "sumInactive": 659,
+	        "sumActive": 642,
+	        "sumMinHigh": -10000,
+	        "sumMaxHigh": 18,
+	        "SumMinChew": 3,
+	        "beforeThreeSumRumina": 130,
+	        "beforeThreeSumIntake": 265,
+	        "score": 98,
+	        "isMaxTime": 2,
+	        "isShow": 2,
+	        "recordCount": 6,
+	        "firmwareVersion": 57,
+	        "createdAt": 1740422850,
+	        "updatedAt": 1740466955
+	    },
+	    {
+	        "id": 1725784,
+	        "pastureId": 1,
+	        "neckRingNumber": "10038",
+	        "cowId": 94,
+	        "lact": 0,
+	        "calvingAge": 0,
+	        "activeTime": "2025-02-25 03:00:00",
+	        "frameid": 1,
+	        "heatDate": "2025-02-25",
+	        "rumina": 1,
+	        "intake": 0,
+	        "inactive": 43,
+	        "gasp": 0,
+	        "other": 0,
+	        "high": 266,
+	        "active": 11,
+	        "filterHigh": 24,
+	        "filterRumina": 0,
+	        "filterChew": 0,
+	        "weekHigh": 1500,
+	        "weekHighHabit": 145,
+	        "weekRuminaHabit": 4,
+	        "weekIntakeHabit": 6,
+	        "weekChewHabit": 9,
+	        "weekInactiveHabit": 6,
+	        "weekOtherHabit": 0,
+	        "changeHigh": -11,
+	        "changeRumina": -100,
+	        "changeChew": -100,
+	        "changeAdjust": 0,
+	        "changeFilter": -3,
+	        "ruminaFilter": -12,
+	        "chewFilter": -15,
+	        "filterCorrect": 98,
+	        "sumRumina": 11,
+	        "sumIntake": 0,
+	        "sumInactive": 513,
+	        "sumActive": 149,
+	        "sumMinHigh": -10000,
+	        "sumMaxHigh": 1,
+	        "SumMinChew": 0,
+	        "beforeThreeSumRumina": 0,
+	        "beforeThreeSumIntake": 0,
+	        "score": 68,
+	        "isMaxTime": 2,
+	        "isShow": 2,
+	        "recordCount": 6,
+	        "firmwareVersion": 57,
+	        "createdAt": 1740422915,
+	        "updatedAt": 1740466955
+	    },
+	    {
+	        "id": 1725786,
+	        "pastureId": 1,
+	        "neckRingNumber": "10053",
+	        "cowId": 108,
+	        "lact": 0,
+	        "calvingAge": 0,
+	        "activeTime": "2025-02-25 03:00:00",
+	        "frameid": 1,
+	        "heatDate": "2025-02-25",
+	        "rumina": 50,
+	        "intake": 3,
+	        "inactive": 96,
+	        "gasp": 0,
+	        "other": 0,
+	        "high": 763,
+	        "active": 28,
+	        "filterHigh": 532,
+	        "filterRumina": 14,
+	        "filterChew": 16,
+	        "weekHigh": 1500,
+	        "weekHighHabit": 471,
+	        "weekRuminaHabit": 13,
+	        "weekIntakeHabit": 19,
+	        "weekChewHabit": 24,
+	        "weekInactiveHabit": 19,
+	        "weekOtherHabit": 0,
+	        "changeHigh": 5,
+	        "changeRumina": 8,
+	        "changeChew": -33,
+	        "changeAdjust": 0,
+	        "changeFilter": 1,
+	        "ruminaFilter": 2,
+	        "chewFilter": -10,
+	        "filterCorrect": 96,
+	        "sumRumina": 140,
+	        "sumIntake": 272,
+	        "sumInactive": 734,
+	        "sumActive": 606,
+	        "sumMinHigh": -10000,
+	        "sumMaxHigh": 19,
+	        "SumMinChew": 10,
+	        "beforeThreeSumRumina": 181,
+	        "beforeThreeSumIntake": 294,
+	        "score": 100,
+	        "isMaxTime": 2,
+	        "isShow": 2,
+	        "recordCount": 6,
+	        "firmwareVersion": 57,
+	        "createdAt": 1740423705,
+	        "updatedAt": 1740466955
+	    },
+	    {
+	        "id": 1725789,
+	        "pastureId": 1,
+	        "neckRingNumber": "10027",
+	        "cowId": 30,
+	        "lact": 0,
+	        "calvingAge": 0,
+	        "activeTime": "2025-02-25 03:00:00",
+	        "frameid": 1,
+	        "heatDate": "2025-02-25",
+	        "rumina": 45,
+	        "intake": 33,
+	        "inactive": 2,
+	        "gasp": 0,
+	        "other": 0,
+	        "high": 1855,
+	        "active": 66,
+	        "filterHigh": 1145,
+	        "filterRumina": 11,
+	        "filterChew": 31,
+	        "weekHigh": 1500,
+	        "weekHighHabit": 499,
+	        "weekRuminaHabit": 7,
+	        "weekIntakeHabit": 23,
+	        "weekChewHabit": 14,
+	        "weekInactiveHabit": 23,
+	        "weekOtherHabit": 0,
+	        "changeHigh": 53,
+	        "changeRumina": 57,
+	        "changeChew": 121,
+	        "changeAdjust": 0,
+	        "changeFilter": 13,
+	        "ruminaFilter": 14,
+	        "chewFilter": 18,
+	        "filterCorrect": 94,
+	        "sumRumina": 132,
+	        "sumIntake": 328,
+	        "sumInactive": 631,
+	        "sumActive": 688,
+	        "sumMinHigh": -10000,
+	        "sumMaxHigh": 20,
+	        "SumMinChew": 5,
+	        "beforeThreeSumRumina": 120,
+	        "beforeThreeSumIntake": 265,
+	        "score": 100,
+	        "isMaxTime": 2,
+	        "isShow": 2,
+	        "recordCount": 6,
+	        "firmwareVersion": 57,
+	        "createdAt": 1740423730,
+	        "updatedAt": 1740466955
+	    },
+	    {
+	        "id": 1725794,
+	        "pastureId": 1,
+	        "neckRingNumber": "10054",
+	        "cowId": 109,
+	        "lact": 3,
+	        "calvingAge": 167,
+	        "activeTime": "2025-02-25 05:00:00",
+	        "frameid": 2,
+	        "heatDate": "2025-02-25",
+	        "rumina": 41,
+	        "intake": 11,
+	        "inactive": 106,
+	        "gasp": 0,
+	        "other": 0,
+	        "high": 485,
+	        "active": 18,
+	        "filterHigh": 327,
+	        "filterRumina": 1,
+	        "filterChew": 9,
+	        "weekHigh": 1500,
+	        "weekHighHabit": 143,
+	        "weekRuminaHabit": 7,
+	        "weekIntakeHabit": 5,
+	        "weekChewHabit": 7,
+	        "weekInactiveHabit": 5,
+	        "weekOtherHabit": 0,
+	        "changeHigh": 16,
+	        "changeRumina": -86,
+	        "changeChew": 29,
+	        "changeAdjust": 0,
+	        "changeFilter": 5,
+	        "ruminaFilter": -13,
+	        "chewFilter": 9,
+	        "filterCorrect": 97,
+	        "sumRumina": 57,
+	        "sumIntake": 333,
+	        "sumInactive": 521,
+	        "sumActive": 738,
+	        "sumMinHigh": -10000,
+	        "sumMaxHigh": 18,
+	        "SumMinChew": 0,
+	        "beforeThreeSumRumina": 62,
+	        "beforeThreeSumIntake": 241,
+	        "score": 100,
+	        "isMaxTime": 2,
+	        "isShow": 2,
+	        "recordCount": 6,
+	        "firmwareVersion": 57,
+	        "createdAt": 1740428520,
+	        "updatedAt": 1740466955
+	    },
+	    {
+	        "id": 1725801,
+	        "pastureId": 1,
+	        "neckRingNumber": "10030",
+	        "cowId": 86,
+	        "lact": 2,
+	        "calvingAge": 300,
+	        "activeTime": "2025-02-25 05:00:00",
+	        "frameid": 2,
+	        "heatDate": "2025-02-25",
+	        "rumina": 65,
+	        "intake": 0,
+	        "inactive": 116,
+	        "gasp": 0,
+	        "other": 0,
+	        "high": 160,
+	        "active": 6,
+	        "filterHigh": 29,
+	        "filterRumina": 9,
+	        "filterChew": 9,
+	        "weekHigh": 1500,
+	        "weekHighHabit": 22,
+	        "weekRuminaHabit": 18,
+	        "weekIntakeHabit": 1,
+	        "weekChewHabit": 18,
+	        "weekInactiveHabit": 1,
+	        "weekOtherHabit": 0,
+	        "changeHigh": 1,
+	        "changeRumina": -50,
+	        "changeChew": -50,
+	        "changeAdjust": 0,
+	        "changeFilter": 0,
+	        "ruminaFilter": -15,
+	        "chewFilter": -15,
+	        "filterCorrect": 97,
+	        "sumRumina": 182,
+	        "sumIntake": 300,
+	        "sumInactive": 638,
+	        "sumActive": 698,
+	        "sumMinHigh": -10000,
+	        "sumMaxHigh": 18,
+	        "SumMinChew": 5,
+	        "beforeThreeSumRumina": 142,
+	        "beforeThreeSumIntake": 291,
+	        "score": 100,
+	        "isMaxTime": 2,
+	        "isShow": 2,
+	        "recordCount": 6,
+	        "firmwareVersion": 57,
+	        "createdAt": 1740429745,
+	        "updatedAt": 1740466955
+	    },
+	    {
+	        "id": 1725802,
+	        "pastureId": 1,
+	        "neckRingNumber": "10028",
+	        "cowId": 84,
+	        "lact": 2,
+	        "calvingAge": 300,
+	        "activeTime": "2025-02-25 05:00:00",
+	        "frameid": 2,
+	        "heatDate": "2025-02-25",
+	        "rumina": 36,
+	        "intake": 0,
+	        "inactive": 115,
+	        "gasp": 0,
+	        "other": 0,
+	        "high": 199,
+	        "active": 7,
+	        "filterHigh": 31,
+	        "filterRumina": 0,
+	        "filterChew": 0,
+	        "weekHigh": 1500,
+	        "weekHighHabit": 407,
+	        "weekRuminaHabit": 5,
+	        "weekIntakeHabit": 32,
+	        "weekChewHabit": 14,
+	        "weekInactiveHabit": 32,
+	        "weekOtherHabit": 0,
+	        "changeHigh": -32,
+	        "changeRumina": -100,
+	        "changeChew": -100,
+	        "changeAdjust": 0,
+	        "changeFilter": -10,
+	        "ruminaFilter": -15,
+	        "chewFilter": -15,
+	        "filterCorrect": 95,
+	        "sumRumina": 108,
+	        "sumIntake": 285,
+	        "sumInactive": 583,
+	        "sumActive": 688,
+	        "sumMinHigh": -10000,
+	        "sumMaxHigh": 15,
+	        "SumMinChew": 0,
+	        "beforeThreeSumRumina": 190,
+	        "beforeThreeSumIntake": 220,
+	        "score": 96,
+	        "isMaxTime": 2,
+	        "isShow": 2,
+	        "recordCount": 6,
+	        "firmwareVersion": 57,
+	        "createdAt": 1740429750,
+	        "updatedAt": 1740466955
+	    },
+	    {
+	        "id": 1725805,
+	        "pastureId": 1,
+	        "neckRingNumber": "10055",
+	        "cowId": 110,
+	        "lact": 2,
+	        "calvingAge": 62,
+	        "activeTime": "2025-02-25 05:00:00",
+	        "frameid": 2,
+	        "heatDate": "2025-02-25",
+	        "rumina": 92,
+	        "intake": 0,
+	        "inactive": 108,
+	        "gasp": 1,
+	        "other": 0,
+	        "high": 260,
+	        "active": 14,
+	        "filterHigh": 41,
+	        "filterRumina": 24,
+	        "filterChew": 24,
+	        "weekHigh": 1500,
+	        "weekHighHabit": 39,
+	        "weekRuminaHabit": 18,
+	        "weekIntakeHabit": 0,
+	        "weekChewHabit": 18,
+	        "weekInactiveHabit": 0,
+	        "weekOtherHabit": 0,
+	        "changeHigh": 0,
+	        "changeRumina": 33,
+	        "changeChew": 33,
+	        "changeAdjust": 0,
+	        "changeFilter": 0,
+	        "ruminaFilter": 10,
+	        "chewFilter": 10,
+	        "filterCorrect": 95,
+	        "sumRumina": 134,
+	        "sumIntake": 251,
+	        "sumInactive": 533,
+	        "sumActive": 695,
+	        "sumMinHigh": -10000,
+	        "sumMaxHigh": 18,
+	        "SumMinChew": 0,
+	        "beforeThreeSumRumina": 102,
+	        "beforeThreeSumIntake": 260,
+	        "score": 100,
+	        "isMaxTime": 2,
+	        "isShow": 2,
+	        "recordCount": 6,
+	        "firmwareVersion": 57,
+	        "createdAt": 1740429755,
+	        "updatedAt": 1740466955
+	    },
+	    {
+	        "id": 1725807,
+	        "pastureId": 1,
+	        "neckRingNumber": "10038",
+	        "cowId": 94,
+	        "lact": 0,
+	        "calvingAge": 0,
+	        "activeTime": "2025-02-25 05:00:00",
+	        "frameid": 2,
+	        "heatDate": "2025-02-25",
+	        "rumina": 0,
+	        "intake": 0,
+	        "inactive": 18,
+	        "gasp": 0,
+	        "other": 0,
+	        "high": 230,
+	        "active": 9,
+	        "filterHigh": 162,
+	        "filterRumina": 0,
+	        "filterChew": 0,
+	        "weekHigh": 1500,
+	        "weekHighHabit": 23,
+	        "weekRuminaHabit": 8,
+	        "weekIntakeHabit": 0,
+	        "weekChewHabit": 8,
+	        "weekInactiveHabit": 0,
+	        "weekOtherHabit": 0,
+	        "changeHigh": 12,
+	        "changeRumina": -100,
+	        "changeChew": -100,
+	        "changeAdjust": 0,
+	        "changeFilter": 3,
+	        "ruminaFilter": -12,
+	        "chewFilter": -15,
+	        "filterCorrect": 98,
+	        "sumRumina": 11,
+	        "sumIntake": 0,
+	        "sumInactive": 515,
+	        "sumActive": 162,
+	        "sumMinHigh": -10000,
+	        "sumMaxHigh": 1,
+	        "SumMinChew": 0,
+	        "beforeThreeSumRumina": 31,
+	        "beforeThreeSumIntake": 240,
+	        "score": 70,
+	        "isMaxTime": 2,
+	        "isShow": 2,
+	        "recordCount": 6,
+	        "firmwareVersion": 57,
+	        "createdAt": 1740429780,
+	        "updatedAt": 1740466955
+	    },
+	    {
+	        "id": 1725810,
+	        "pastureId": 1,
+	        "neckRingNumber": "10032",
+	        "cowId": 88,
+	        "lact": 0,
+	        "calvingAge": 0,
+	        "activeTime": "2025-02-25 05:00:00",
+	        "frameid": 2,
+	        "heatDate": "2025-02-25",
+	        "rumina": 59,
+	        "intake": 0,
+	        "inactive": 104,
+	        "gasp": 0,
+	        "other": 0,
+	        "high": 370,
+	        "active": 11,
+	        "filterHigh": 149,
+	        "filterRumina": 9,
+	        "filterChew": 9,
+	        "weekHigh": 1500,
+	        "weekHighHabit": 123,
+	        "weekRuminaHabit": 12,
+	        "weekIntakeHabit": 16,
+	        "weekChewHabit": 14,
+	        "weekInactiveHabit": 16,
+	        "weekOtherHabit": 0,
+	        "changeHigh": 2,
+	        "changeRumina": -25,
+	        "changeChew": -36,
+	        "changeAdjust": 0,
+	        "changeFilter": 0,
+	        "ruminaFilter": -6,
+	        "chewFilter": -11,
+	        "filterCorrect": 97,
+	        "sumRumina": 47,
+	        "sumIntake": 227,
+	        "sumInactive": 578,
+	        "sumActive": 572,
+	        "sumMinHigh": -10000,
+	        "sumMaxHigh": 11,
+	        "SumMinChew": 0,
+	        "beforeThreeSumRumina": 138,
+	        "beforeThreeSumIntake": 211,
+	        "score": 90,
+	        "isMaxTime": 2,
+	        "isShow": 2,
+	        "recordCount": 6,
+	        "firmwareVersion": 57,
+	        "createdAt": 1740429795,
+	        "updatedAt": 1740466956
+	    },
+	    {
+	        "id": 1725815,
+	        "pastureId": 1,
+	        "neckRingNumber": "10053",
+	        "cowId": 108,
+	        "lact": 0,
+	        "calvingAge": 0,
+	        "activeTime": "2025-02-25 05:00:00",
+	        "frameid": 2,
+	        "heatDate": "2025-02-25",
+	        "rumina": 45,
+	        "intake": 48,
+	        "inactive": 54,
+	        "gasp": 0,
+	        "other": 0,
+	        "high": 1908,
+	        "active": 69,
+	        "filterHigh": 148,
+	        "filterRumina": 26,
+	        "filterChew": 26,
+	        "weekHigh": 1500,
+	        "weekHighHabit": 191,
+	        "weekRuminaHabit": 15,
+	        "weekIntakeHabit": 28,
+	        "weekChewHabit": 20,
+	        "weekInactiveHabit": 28,
+	        "weekOtherHabit": 0,
+	        "changeHigh": -4,
+	        "changeRumina": 73,
+	        "changeChew": 30,
+	        "changeAdjust": 0,
+	        "changeFilter": -1,
+	        "ruminaFilter": 9,
+	        "chewFilter": 9,
+	        "filterCorrect": 96,
+	        "sumRumina": 154,
+	        "sumIntake": 272,
+	        "sumInactive": 740,
+	        "sumActive": 611,
+	        "sumMinHigh": -10000,
+	        "sumMaxHigh": 19,
+	        "SumMinChew": 10,
+	        "beforeThreeSumRumina": 179,
+	        "beforeThreeSumIntake": 247,
+	        "score": 99,
+	        "isMaxTime": 2,
+	        "isShow": 2,
+	        "recordCount": 6,
+	        "firmwareVersion": 57,
+	        "createdAt": 1740430900,
+	        "updatedAt": 1740466956
+	    },
+	    {
+	        "id": 1725819,
+	        "pastureId": 1,
+	        "neckRingNumber": "10027",
+	        "cowId": 30,
+	        "lact": 0,
+	        "calvingAge": 0,
+	        "activeTime": "2025-02-25 05:00:00",
+	        "frameid": 2,
+	        "heatDate": "2025-02-25",
+	        "rumina": 36,
+	        "intake": 0,
+	        "inactive": 84,
+	        "gasp": 0,
+	        "other": 0,
+	        "high": 490,
+	        "active": 41,
+	        "filterHigh": 76,
+	        "filterRumina": 0,
+	        "filterChew": 0,
+	        "weekHigh": 1500,
+	        "weekHighHabit": 590,
+	        "weekRuminaHabit": 14,
+	        "weekIntakeHabit": 23,
+	        "weekChewHabit": 23,
+	        "weekInactiveHabit": 23,
+	        "weekOtherHabit": 0,
+	        "changeHigh": -42,
+	        "changeRumina": -100,
+	        "changeChew": -100,
+	        "changeAdjust": 0,
+	        "changeFilter": -10,
+	        "ruminaFilter": -12,
+	        "chewFilter": -15,
+	        "filterCorrect": 94,
+	        "sumRumina": 107,
+	        "sumIntake": 331,
+	        "sumInactive": 562,
+	        "sumActive": 699,
+	        "sumMinHigh": -10000,
+	        "sumMaxHigh": 20,
+	        "SumMinChew": 0,
+	        "beforeThreeSumRumina": 136,
+	        "beforeThreeSumIntake": 254,
+	        "score": 96,
+	        "isMaxTime": 2,
+	        "isShow": 2,
+	        "recordCount": 6,
+	        "firmwareVersion": 57,
+	        "createdAt": 1740430925,
+	        "updatedAt": 1740466956
+	    },
+	    {
+	        "id": 1725824,
+	        "pastureId": 1,
+	        "neckRingNumber": "10054",
+	        "cowId": 109,
+	        "lact": 3,
+	        "calvingAge": 167,
+	        "activeTime": "2025-02-25 07:00:00",
+	        "frameid": 3,
+	        "heatDate": "2025-02-25",
+	        "rumina": 82,
+	        "intake": 0,
+	        "inactive": 109,
+	        "gasp": 0,
+	        "other": 0,
+	        "high": 232,
+	        "active": 8,
+	        "filterHigh": 168,
+	        "filterRumina": 9,
+	        "filterChew": 9,
+	        "weekHigh": 1500,
+	        "weekHighHabit": 28,
+	        "weekRuminaHabit": 7,
+	        "weekIntakeHabit": 28,
+	        "weekChewHabit": 7,
+	        "weekInactiveHabit": 28,
+	        "weekOtherHabit": 0,
+	        "changeHigh": 13,
+	        "changeRumina": 29,
+	        "changeChew": 29,
+	        "changeAdjust": 0,
+	        "changeFilter": 4,
+	        "ruminaFilter": 9,
+	        "chewFilter": 9,
+	        "filterCorrect": 97,
+	        "sumRumina": 66,
+	        "sumIntake": 293,
+	        "sumInactive": 560,
+	        "sumActive": 700,
+	        "sumMinHigh": -10000,
+	        "sumMaxHigh": 18,
+	        "SumMinChew": 0,
+	        "beforeThreeSumRumina": 62,
+	        "beforeThreeSumIntake": 198,
+	        "score": 100,
+	        "isMaxTime": 2,
+	        "isShow": 2,
+	        "recordCount": 6,
+	        "firmwareVersion": 57,
+	        "createdAt": 1740435720,
+	        "updatedAt": 1740466956
+	    },
+	    {
+	        "id": 1725831,
+	        "pastureId": 1,
+	        "neckRingNumber": "10030",
+	        "cowId": 86,
+	        "lact": 2,
+	        "calvingAge": 300,
+	        "activeTime": "2025-02-25 07:00:00",
+	        "frameid": 3,
+	        "heatDate": "2025-02-25",
+	        "rumina": 91,
+	        "intake": 0,
+	        "inactive": 110,
+	        "gasp": 0,
+	        "other": 0,
+	        "high": 214,
+	        "active": 10,
+	        "filterHigh": 121,
+	        "filterRumina": 27,
+	        "filterChew": 27,
+	        "weekHigh": 1500,
+	        "weekHighHabit": 27,
+	        "weekRuminaHabit": 24,
+	        "weekIntakeHabit": 3,
+	        "weekChewHabit": 24,
+	        "weekInactiveHabit": 3,
+	        "weekOtherHabit": 0,
+	        "changeHigh": 8,
+	        "changeRumina": 13,
+	        "changeChew": 13,
+	        "changeAdjust": 0,
+	        "changeFilter": 2,
+	        "ruminaFilter": 4,
+	        "chewFilter": 4,
+	        "filterCorrect": 97,
+	        "sumRumina": 182,
+	        "sumIntake": 297,
+	        "sumInactive": 638,
+	        "sumActive": 694,
+	        "sumMinHigh": -10000,
+	        "sumMaxHigh": 18,
+	        "SumMinChew": 5,
+	        "beforeThreeSumRumina": 128,
+	        "beforeThreeSumIntake": 291,
+	        "score": 100,
+	        "isMaxTime": 2,
+	        "isShow": 2,
+	        "recordCount": 6,
+	        "firmwareVersion": 57,
+	        "createdAt": 1740436955,
+	        "updatedAt": 1740466956
+	    },
+	    {
+	        "id": 1725832,
+	        "pastureId": 1,
+	        "neckRingNumber": "10055",
+	        "cowId": 110,
+	        "lact": 2,
+	        "calvingAge": 62,
+	        "activeTime": "2025-02-25 07:00:00",
+	        "frameid": 3,
+	        "heatDate": "2025-02-25",
+	        "rumina": 95,
+	        "intake": 0,
+	        "inactive": 110,
+	        "gasp": 0,
+	        "other": 0,
+	        "high": 264,
+	        "active": 16,
+	        "filterHigh": 144,
+	        "filterRumina": 23,
+	        "filterChew": 23,
+	        "weekHigh": 1500,
+	        "weekHighHabit": 55,
+	        "weekRuminaHabit": 19,
+	        "weekIntakeHabit": 9,
+	        "weekChewHabit": 20,
+	        "weekInactiveHabit": 9,
+	        "weekOtherHabit": 0,
+	        "changeHigh": 8,
+	        "changeRumina": 21,
+	        "changeChew": 15,
+	        "changeAdjust": 0,
+	        "changeFilter": 2,
+	        "ruminaFilter": 6,
+	        "chewFilter": 4,
+	        "filterCorrect": 95,
+	        "sumRumina": 143,
+	        "sumIntake": 251,
+	        "sumInactive": 579,
+	        "sumActive": 706,
+	        "sumMinHigh": -10000,
+	        "sumMaxHigh": 18,
+	        "SumMinChew": 0,
+	        "beforeThreeSumRumina": 78,
+	        "beforeThreeSumIntake": 260,
+	        "score": 100,
+	        "isMaxTime": 2,
+	        "isShow": 2,
+	        "recordCount": 6,
+	        "firmwareVersion": 57,
+	        "createdAt": 1740436960,
+	        "updatedAt": 1740466956
+	    },
+	    {
+	        "id": 1725839,
+	        "pastureId": 1,
+	        "neckRingNumber": "10032",
+	        "cowId": 88,
+	        "lact": 0,
+	        "calvingAge": 0,
+	        "activeTime": "2025-02-25 07:00:00",
+	        "frameid": 3,
+	        "heatDate": "2025-02-25",
+	        "rumina": 13,
+	        "intake": 0,
+	        "inactive": 118,
+	        "gasp": 0,
+	        "other": 0,
+	        "high": 126,
+	        "active": 2,
+	        "filterHigh": 78,
+	        "filterRumina": 3,
+	        "filterChew": 3,
+	        "weekHigh": 1500,
+	        "weekHighHabit": 133,
+	        "weekRuminaHabit": 4,
+	        "weekIntakeHabit": 13,
+	        "weekChewHabit": 7,
+	        "weekInactiveHabit": 13,
+	        "weekOtherHabit": 0,
+	        "changeHigh": -5,
+	        "changeRumina": -25,
+	        "changeChew": -57,
+	        "changeAdjust": 0,
+	        "changeFilter": -1,
+	        "ruminaFilter": -6,
+	        "chewFilter": -17,
+	        "filterCorrect": 97,
+	        "sumRumina": 50,
+	        "sumIntake": 200,
+	        "sumInactive": 628,
+	        "sumActive": 526,
+	        "sumMinHigh": -10000,
+	        "sumMaxHigh": 11,
+	        "SumMinChew": 0,
+	        "beforeThreeSumRumina": 0,
+	        "beforeThreeSumIntake": 0,
+	        "score": 89,
+	        "isMaxTime": 2,
+	        "isShow": 2,
+	        "recordCount": 6,
+	        "firmwareVersion": 57,
+	        "createdAt": 1740436990,
+	        "updatedAt": 1740466956
+	    },
+	    {
+	        "id": 1725845,
+	        "pastureId": 1,
+	        "neckRingNumber": "10053",
+	        "cowId": 108,
+	        "lact": 0,
+	        "calvingAge": 0,
+	        "activeTime": "2025-02-25 07:00:00",
+	        "frameid": 3,
+	        "heatDate": "2025-02-25",
+	        "rumina": 33,
+	        "intake": 1,
+	        "inactive": 112,
+	        "gasp": 0,
+	        "other": 0,
+	        "high": 231,
+	        "active": 6,
+	        "filterHigh": 127,
+	        "filterRumina": 23,
+	        "filterChew": 23,
+	        "weekHigh": 1500,
+	        "weekHighHabit": 91,
+	        "weekRuminaHabit": 20,
+	        "weekIntakeHabit": 12,
+	        "weekChewHabit": 21,
+	        "weekInactiveHabit": 12,
+	        "weekOtherHabit": 0,
+	        "changeHigh": 3,
+	        "changeRumina": 15,
+	        "changeChew": 10,
+	        "changeAdjust": 0,
+	        "changeFilter": 1,
+	        "ruminaFilter": 4,
+	        "chewFilter": 3,
+	        "filterCorrect": 96,
+	        "sumRumina": 159,
+	        "sumIntake": 301,
+	        "sumInactive": 700,
+	        "sumActive": 646,
+	        "sumMinHigh": -10000,
+	        "sumMaxHigh": 19,
+	        "SumMinChew": 10,
+	        "beforeThreeSumRumina": 180,
+	        "beforeThreeSumIntake": 286,
+	        "score": 100,
+	        "isMaxTime": 2,
+	        "isShow": 2,
+	        "recordCount": 6,
+	        "firmwareVersion": 57,
+	        "createdAt": 1740438100,
+	        "updatedAt": 1740466956
+	    },
+	    {
+	        "id": 1725849,
+	        "pastureId": 1,
+	        "neckRingNumber": "10027",
+	        "cowId": 30,
+	        "lact": 0,
+	        "calvingAge": 0,
+	        "activeTime": "2025-02-25 07:00:00",
+	        "frameid": 3,
+	        "heatDate": "2025-02-25",
+	        "rumina": 71,
+	        "intake": 9,
+	        "inactive": 0,
+	        "gasp": 0,
+	        "other": 0,
+	        "high": 936,
+	        "active": 49,
+	        "filterHigh": 595,
+	        "filterRumina": 18,
+	        "filterChew": 24,
+	        "weekHigh": 1500,
+	        "weekHighHabit": 77,
+	        "weekRuminaHabit": 23,
+	        "weekIntakeHabit": 7,
+	        "weekChewHabit": 23,
+	        "weekInactiveHabit": 7,
+	        "weekOtherHabit": 0,
+	        "changeHigh": 46,
+	        "changeRumina": -22,
+	        "changeChew": 4,
+	        "changeAdjust": 0,
+	        "changeFilter": 11,
+	        "ruminaFilter": -5,
+	        "chewFilter": 1,
+	        "filterCorrect": 94,
+	        "sumRumina": 109,
+	        "sumIntake": 334,
+	        "sumInactive": 485,
+	        "sumActive": 756,
+	        "sumMinHigh": -10000,
+	        "sumMaxHigh": 20,
+	        "SumMinChew": 0,
+	        "beforeThreeSumRumina": 126,
+	        "beforeThreeSumIntake": 248,
+	        "score": 100,
+	        "isMaxTime": 2,
+	        "isShow": 2,
+	        "recordCount": 6,
+	        "firmwareVersion": 57,
+	        "createdAt": 1740438130,
+	        "updatedAt": 1740466956
+	    },
+	    {
+	        "id": 1725853,
+	        "pastureId": 1,
+	        "neckRingNumber": "10028",
+	        "cowId": 84,
+	        "lact": 2,
+	        "calvingAge": 300,
+	        "activeTime": "2025-02-25 07:00:00",
+	        "frameid": 3,
+	        "heatDate": "2025-02-25",
+	        "rumina": 3,
+	        "intake": 9,
+	        "inactive": 105,
+	        "gasp": 0,
+	        "other": 0,
+	        "high": 583,
+	        "active": 20,
+	        "filterHigh": 16,
+	        "filterRumina": 0,
+	        "filterChew": 0,
+	        "weekHigh": 1500,
+	        "weekHighHabit": 341,
+	        "weekRuminaHabit": 12,
+	        "weekIntakeHabit": 30,
+	        "weekChewHabit": 16,
+	        "weekInactiveHabit": 30,
+	        "weekOtherHabit": 0,
+	        "changeHigh": -28,
+	        "changeRumina": -100,
+	        "changeChew": -100,
+	        "changeAdjust": 0,
+	        "changeFilter": -8,
+	        "ruminaFilter": -15,
+	        "chewFilter": -15,
+	        "filterCorrect": 95,
+	        "sumRumina": 103,
+	        "sumIntake": 251,
+	        "sumInactive": 654,
+	        "sumActive": 633,
+	        "sumMinHigh": -10000,
+	        "sumMaxHigh": 15,
+	        "SumMinChew": 0,
+	        "beforeThreeSumRumina": 163,
+	        "beforeThreeSumIntake": 255,
+	        "score": 96,
+	        "isMaxTime": 2,
+	        "isShow": 2,
+	        "recordCount": 6,
+	        "firmwareVersion": 57,
+	        "createdAt": 1740442795,
+	        "updatedAt": 1740466956
+	    },
+	    {
+	        "id": 1725854,
+	        "pastureId": 1,
+	        "neckRingNumber": "10054",
+	        "cowId": 109,
+	        "lact": 3,
+	        "calvingAge": 167,
+	        "activeTime": "2025-02-25 09:00:00",
+	        "frameid": 4,
+	        "heatDate": "2025-02-25",
+	        "rumina": 62,
+	        "intake": 1,
+	        "inactive": 86,
+	        "gasp": 0,
+	        "other": 0,
+	        "high": 783,
+	        "active": 34,
+	        "filterHigh": 329,
+	        "filterRumina": 7,
+	        "filterChew": 7,
+	        "weekHigh": 1500,
+	        "weekHighHabit": 258,
+	        "weekRuminaHabit": 0,
+	        "weekIntakeHabit": 40,
+	        "weekChewHabit": 10,
+	        "weekInactiveHabit": 40,
+	        "weekOtherHabit": 0,
+	        "changeHigh": 6,
+	        "changeRumina": 0,
+	        "changeChew": -30,
+	        "changeAdjust": 0,
+	        "changeFilter": 2,
+	        "ruminaFilter": 0,
+	        "chewFilter": -9,
+	        "filterCorrect": 97,
+	        "sumRumina": 73,
+	        "sumIntake": 234,
+	        "sumInactive": 661,
+	        "sumActive": 628,
+	        "sumMinHigh": -10000,
+	        "sumMaxHigh": 18,
+	        "SumMinChew": 3,
+	        "beforeThreeSumRumina": 64,
+	        "beforeThreeSumIntake": 205,
+	        "score": 100,
+	        "isMaxTime": 2,
+	        "isShow": 2,
+	        "recordCount": 6,
+	        "firmwareVersion": 57,
+	        "createdAt": 1740442915,
+	        "updatedAt": 1740466956
+	    },
+	    {
+	        "id": 1725862,
+	        "pastureId": 1,
+	        "neckRingNumber": "10055",
+	        "cowId": 110,
+	        "lact": 2,
+	        "calvingAge": 62,
+	        "activeTime": "2025-02-25 09:00:00",
+	        "frameid": 4,
+	        "heatDate": "2025-02-25",
+	        "rumina": 43,
+	        "intake": 7,
+	        "inactive": 29,
+	        "gasp": 0,
+	        "other": 0,
+	        "high": 2458,
+	        "active": 77,
+	        "filterHigh": 1077,
+	        "filterRumina": 7,
+	        "filterChew": 11,
+	        "weekHigh": 1500,
+	        "weekHighHabit": 892,
+	        "weekRuminaHabit": 10,
+	        "weekIntakeHabit": 29,
+	        "weekChewHabit": 21,
+	        "weekInactiveHabit": 29,
+	        "weekOtherHabit": 0,
+	        "changeHigh": 14,
+	        "changeRumina": -30,
+	        "changeChew": -48,
+	        "changeAdjust": 0,
+	        "changeFilter": 4,
+	        "ruminaFilter": -9,
+	        "chewFilter": -14,
+	        "filterCorrect": 95,
+	        "sumRumina": 132,
+	        "sumIntake": 230,
+	        "sumInactive": 648,
+	        "sumActive": 681,
+	        "sumMinHigh": -10000,
+	        "sumMaxHigh": 18,
+	        "SumMinChew": 0,
+	        "beforeThreeSumRumina": 56,
+	        "beforeThreeSumIntake": 276,
+	        "score": 100,
+	        "isMaxTime": 2,
+	        "isShow": 2,
+	        "recordCount": 6,
+	        "firmwareVersion": 57,
+	        "createdAt": 1740444160,
+	        "updatedAt": 1740466956
+	    },
+	    {
+	        "id": 1725865,
+	        "pastureId": 1,
+	        "neckRingNumber": "10032",
+	        "cowId": 88,
+	        "lact": 0,
+	        "calvingAge": 0,
+	        "activeTime": "2025-02-25 09:00:00",
+	        "frameid": 4,
+	        "heatDate": "2025-02-25",
+	        "rumina": 0,
+	        "intake": 34,
+	        "inactive": 31,
+	        "gasp": 0,
+	        "other": 0,
+	        "high": 1540,
+	        "active": 71,
+	        "filterHigh": 239,
+	        "filterRumina": 0,
+	        "filterChew": 0,
+	        "weekHigh": 1500,
+	        "weekHighHabit": 357,
+	        "weekRuminaHabit": 3,
+	        "weekIntakeHabit": 19,
+	        "weekChewHabit": 6,
+	        "weekInactiveHabit": 19,
+	        "weekOtherHabit": 0,
+	        "changeHigh": -10,
+	        "changeRumina": -100,
+	        "changeChew": -100,
+	        "changeAdjust": 0,
+	        "changeFilter": -2,
+	        "ruminaFilter": -12,
+	        "chewFilter": -15,
+	        "filterCorrect": 97,
+	        "sumRumina": 50,
+	        "sumIntake": 186,
+	        "sumInactive": 739,
+	        "sumActive": 469,
+	        "sumMinHigh": -10000,
+	        "sumMaxHigh": 11,
+	        "SumMinChew": 0,
+	        "beforeThreeSumRumina": 141,
+	        "beforeThreeSumIntake": 215,
+	        "score": 89,
+	        "isMaxTime": 2,
+	        "isShow": 2,
+	        "recordCount": 6,
+	        "firmwareVersion": 57,
+	        "createdAt": 1740444195,
+	        "updatedAt": 1740466956
+	    },
+	    {
+	        "id": 1725870,
+	        "pastureId": 1,
+	        "neckRingNumber": "10030",
+	        "cowId": 86,
+	        "lact": 2,
+	        "calvingAge": 300,
+	        "activeTime": "2025-02-25 09:00:00",
+	        "frameid": 4,
+	        "heatDate": "2025-02-25",
+	        "rumina": 66,
+	        "intake": 0,
+	        "inactive": 46,
+	        "gasp": 0,
+	        "other": 0,
+	        "high": 1275,
+	        "active": 57,
+	        "filterHigh": 329,
+	        "filterRumina": 18,
+	        "filterChew": 18,
+	        "weekHigh": 1500,
+	        "weekHighHabit": 412,
+	        "weekRuminaHabit": 13,
+	        "weekIntakeHabit": 18,
+	        "weekChewHabit": 20,
+	        "weekInactiveHabit": 18,
+	        "weekOtherHabit": 0,
+	        "changeHigh": -7,
+	        "changeRumina": 38,
+	        "changeChew": -10,
+	        "changeAdjust": 0,
+	        "changeFilter": -2,
+	        "ruminaFilter": 11,
+	        "chewFilter": -3,
+	        "filterCorrect": 97,
+	        "sumRumina": 200,
+	        "sumIntake": 267,
+	        "sumInactive": 691,
+	        "sumActive": 651,
+	        "sumMinHigh": -10000,
+	        "sumMaxHigh": 10,
+	        "SumMinChew": 5,
+	        "beforeThreeSumRumina": 116,
+	        "beforeThreeSumIntake": 283,
+	        "score": 99,
+	        "isMaxTime": 2,
+	        "isShow": 2,
+	        "recordCount": 6,
+	        "firmwareVersion": 57,
+	        "createdAt": 1740444450,
+	        "updatedAt": 1740466956
+	    },
+	    {
+	        "id": 1725873,
+	        "pastureId": 1,
+	        "neckRingNumber": "10038",
+	        "cowId": 94,
+	        "lact": 0,
+	        "calvingAge": 0,
+	        "activeTime": "2025-02-25 09:00:00",
+	        "frameid": 4,
+	        "heatDate": "2025-02-25",
+	        "rumina": 4,
+	        "intake": 0,
+	        "inactive": 0,
+	        "gasp": 0,
+	        "other": 0,
+	        "high": 209,
+	        "active": 13,
+	        "filterHigh": 77,
+	        "filterRumina": 3,
+	        "filterChew": 3,
+	        "weekHigh": 1500,
+	        "weekHighHabit": 359,
+	        "weekRuminaHabit": 2,
+	        "weekIntakeHabit": 17,
+	        "weekChewHabit": 4,
+	        "weekInactiveHabit": 17,
+	        "weekOtherHabit": 0,
+	        "changeHigh": -24,
+	        "changeRumina": 50,
+	        "changeChew": -25,
+	        "changeAdjust": 0,
+	        "changeFilter": -6,
+	        "ruminaFilter": 12,
+	        "chewFilter": -8,
+	        "filterCorrect": 98,
+	        "sumRumina": 12,
+	        "sumIntake": 0,
+	        "sumInactive": 502,
+	        "sumActive": 132,
+	        "sumMinHigh": -10000,
+	        "sumMaxHigh": 3,
+	        "SumMinChew": 0,
+	        "beforeThreeSumRumina": 19,
+	        "beforeThreeSumIntake": 157,
+	        "score": 67,
+	        "isMaxTime": 2,
+	        "isShow": 2,
+	        "recordCount": 6,
+	        "firmwareVersion": 57,
+	        "createdAt": 1740445065,
+	        "updatedAt": 1740466956
+	    },
+	    {
+	        "id": 1725874,
+	        "pastureId": 1,
+	        "neckRingNumber": "10053",
+	        "cowId": 108,
+	        "lact": 0,
+	        "calvingAge": 0,
+	        "activeTime": "2025-02-25 09:00:00",
+	        "frameid": 4,
+	        "heatDate": "2025-02-25",
+	        "rumina": 33,
+	        "intake": 26,
+	        "inactive": 3,
+	        "gasp": 0,
+	        "other": 0,
+	        "high": 2339,
+	        "active": 96,
+	        "filterHigh": 1061,
+	        "filterRumina": 7,
+	        "filterChew": 25,
+	        "weekHigh": 1500,
+	        "weekHighHabit": 475,
+	        "weekRuminaHabit": 12,
+	        "weekIntakeHabit": 26,
+	        "weekChewHabit": 26,
+	        "weekInactiveHabit": 26,
+	        "weekOtherHabit": 0,
+	        "changeHigh": 49,
+	        "changeRumina": -42,
+	        "changeChew": -4,
+	        "changeAdjust": 0,
+	        "changeFilter": 12,
+	        "ruminaFilter": -10,
+	        "chewFilter": -1,
+	        "filterCorrect": 96,
+	        "sumRumina": 163,
+	        "sumIntake": 263,
+	        "sumInactive": 763,
+	        "sumActive": 589,
+	        "sumMinHigh": -10000,
+	        "sumMaxHigh": 9,
+	        "SumMinChew": 10,
+	        "beforeThreeSumRumina": 193,
+	        "beforeThreeSumIntake": 284,
+	        "score": 100,
+	        "isMaxTime": 2,
+	        "isShow": 2,
+	        "recordCount": 6,
+	        "firmwareVersion": 57,
+	        "createdAt": 1740445305,
+	        "updatedAt": 1740466956
+	    },
+	    {
+	        "id": 1725879,
+	        "pastureId": 1,
+	        "neckRingNumber": "10027",
+	        "cowId": 30,
+	        "lact": 0,
+	        "calvingAge": 0,
+	        "activeTime": "2025-02-25 09:00:00",
+	        "frameid": 4,
+	        "heatDate": "2025-02-25",
+	        "rumina": 34,
+	        "intake": 27,
+	        "inactive": 15,
+	        "gasp": 0,
+	        "other": 0,
+	        "high": 1563,
+	        "active": 64,
+	        "filterHigh": 720,
+	        "filterRumina": 23,
+	        "filterChew": 27,
+	        "weekHigh": 1500,
+	        "weekHighHabit": 796,
+	        "weekRuminaHabit": 2,
+	        "weekIntakeHabit": 26,
+	        "weekChewHabit": 7,
+	        "weekInactiveHabit": 26,
+	        "weekOtherHabit": 0,
+	        "changeHigh": -6,
+	        "changeRumina": 1050,
+	        "changeChew": 286,
+	        "changeAdjust": 0,
+	        "changeFilter": -1,
+	        "ruminaFilter": 50,
+	        "chewFilter": 43,
+	        "filterCorrect": 94,
+	        "sumRumina": 132,
+	        "sumIntake": 295,
+	        "sumInactive": 473,
+	        "sumActive": 719,
+	        "sumMinHigh": -10000,
+	        "sumMaxHigh": 20,
+	        "SumMinChew": 0,
+	        "beforeThreeSumRumina": 127,
+	        "beforeThreeSumIntake": 232,
+	        "score": 99,
+	        "isMaxTime": 2,
+	        "isShow": 2,
+	        "recordCount": 6,
+	        "firmwareVersion": 57,
+	        "createdAt": 1740445320,
+	        "updatedAt": 1740466956
+	    },
+	    {
+	        "id": 1725883,
+	        "pastureId": 1,
+	        "neckRingNumber": "10028",
+	        "cowId": 84,
+	        "lact": 2,
+	        "calvingAge": 300,
+	        "activeTime": "2025-02-25 09:00:00",
+	        "frameid": 4,
+	        "heatDate": "2025-02-25",
+	        "rumina": 15,
+	        "intake": 43,
+	        "inactive": 2,
+	        "gasp": 0,
+	        "other": 0,
+	        "high": 2796,
+	        "active": 99,
+	        "filterHigh": 1259,
+	        "filterRumina": 4,
+	        "filterChew": 21,
+	        "weekHigh": 1500,
+	        "weekHighHabit": 598,
+	        "weekRuminaHabit": 5,
+	        "weekIntakeHabit": 15,
+	        "weekChewHabit": 11,
+	        "weekInactiveHabit": 15,
+	        "weekOtherHabit": 0,
+	        "changeHigh": 54,
+	        "changeRumina": -20,
+	        "changeChew": 91,
+	        "changeAdjust": 0,
+	        "changeFilter": 16,
+	        "ruminaFilter": -6,
+	        "chewFilter": 14,
+	        "filterCorrect": 95,
+	        "sumRumina": 105,
+	        "sumIntake": 265,
+	        "sumInactive": 717,
+	        "sumActive": 651,
+	        "sumMinHigh": -13,
+	        "sumMaxHigh": 16,
+	        "SumMinChew": 0,
+	        "beforeThreeSumRumina": 160,
+	        "beforeThreeSumIntake": 282,
+	        "score": 97,
+	        "isMaxTime": 2,
+	        "isShow": 1,
+	        "recordCount": 6,
+	        "firmwareVersion": 57,
+	        "createdAt": 1740445670,
+	        "updatedAt": 1740466957
+	    },
+	    {
+	        "id": 1725884,
+	        "pastureId": 1,
+	        "neckRingNumber": "10054",
+	        "cowId": 109,
+	        "lact": 3,
+	        "calvingAge": 167,
+	        "activeTime": "2025-02-25 11:00:00",
+	        "frameid": 5,
+	        "heatDate": "2025-02-25",
+	        "rumina": 35,
+	        "intake": 39,
+	        "inactive": 1,
+	        "gasp": 0,
+	        "other": 0,
+	        "high": 2761,
+	        "active": 95,
+	        "filterHigh": 247,
+	        "filterRumina": 6,
+	        "filterChew": 6,
+	        "weekHigh": 1500,
+	        "weekHighHabit": 388,
+	        "weekRuminaHabit": 1,
+	        "weekIntakeHabit": 21,
+	        "weekChewHabit": 8,
+	        "weekInactiveHabit": 21,
+	        "weekOtherHabit": 0,
+	        "changeHigh": -12,
+	        "changeRumina": 500,
+	        "changeChew": -25,
+	        "changeAdjust": 0,
+	        "changeFilter": -4,
+	        "ruminaFilter": 50,
+	        "chewFilter": -8,
+	        "filterCorrect": 97,
+	        "sumRumina": 70,
+	        "sumIntake": 210,
+	        "sumInactive": 741,
+	        "sumActive": 577,
+	        "sumMinHigh": -10000,
+	        "sumMaxHigh": 18,
+	        "SumMinChew": 3,
+	        "beforeThreeSumRumina": 68,
+	        "beforeThreeSumIntake": 225,
+	        "score": 98,
+	        "isMaxTime": 2,
+	        "isShow": 2,
+	        "recordCount": 6,
+	        "firmwareVersion": 57,
+	        "createdAt": 1740450120,
+	        "updatedAt": 1740466957
+	    },
+	    {
+	        "id": 1725891,
+	        "pastureId": 1,
+	        "neckRingNumber": "10030",
+	        "cowId": 86,
+	        "lact": 2,
+	        "calvingAge": 300,
+	        "activeTime": "2025-02-25 11:00:00",
+	        "frameid": 5,
+	        "heatDate": "2025-02-25",
+	        "rumina": 48,
+	        "intake": 41,
+	        "inactive": 51,
+	        "gasp": 0,
+	        "other": 0,
+	        "high": 1893,
+	        "active": 71,
+	        "filterHigh": 803,
+	        "filterRumina": 1,
+	        "filterChew": 18,
+	        "weekHigh": 1500,
+	        "weekHighHabit": 719,
+	        "weekRuminaHabit": 6,
+	        "weekIntakeHabit": 36,
+	        "weekChewHabit": 18,
+	        "weekInactiveHabit": 36,
+	        "weekOtherHabit": 0,
+	        "changeHigh": 7,
+	        "changeRumina": -83,
+	        "changeChew": 0,
+	        "changeAdjust": 0,
+	        "changeFilter": 2,
+	        "ruminaFilter": -12,
+	        "chewFilter": 0,
+	        "filterCorrect": 97,
+	        "sumRumina": 192,
+	        "sumIntake": 250,
+	        "sumInactive": 697,
+	        "sumActive": 640,
+	        "sumMinHigh": -10000,
+	        "sumMaxHigh": 10,
+	        "SumMinChew": 5,
+	        "beforeThreeSumRumina": 128,
+	        "beforeThreeSumIntake": 246,
+	        "score": 100,
+	        "isMaxTime": 2,
+	        "isShow": 2,
+	        "recordCount": 6,
+	        "firmwareVersion": 57,
+	        "createdAt": 1740451350,
+	        "updatedAt": 1740466957
+	    },
+	    {
+	        "id": 1725894,
+	        "pastureId": 1,
+	        "neckRingNumber": "10055",
+	        "cowId": 110,
+	        "lact": 2,
+	        "calvingAge": 62,
+	        "activeTime": "2025-02-25 11:00:00",
+	        "frameid": 5,
+	        "heatDate": "2025-02-25",
+	        "rumina": 0,
+	        "intake": 45,
+	        "inactive": 52,
+	        "gasp": 0,
+	        "other": 0,
+	        "high": 2748,
+	        "active": 66,
+	        "filterHigh": 1309,
+	        "filterRumina": 0,
+	        "filterChew": 19,
+	        "weekHigh": 1500,
+	        "weekHighHabit": 884,
+	        "weekRuminaHabit": 5,
+	        "weekIntakeHabit": 31,
+	        "weekChewHabit": 14,
+	        "weekInactiveHabit": 31,
+	        "weekOtherHabit": 0,
+	        "changeHigh": 33,
+	        "changeRumina": -100,
+	        "changeChew": 36,
+	        "changeAdjust": 0,
+	        "changeFilter": 10,
+	        "ruminaFilter": -15,
+	        "chewFilter": 11,
+	        "filterCorrect": 95,
+	        "sumRumina": 122,
+	        "sumIntake": 214,
+	        "sumInactive": 670,
+	        "sumActive": 656,
+	        "sumMinHigh": -10000,
+	        "sumMaxHigh": 18,
+	        "SumMinChew": 0,
+	        "beforeThreeSumRumina": 52,
+	        "beforeThreeSumIntake": 277,
+	        "score": 100,
+	        "isMaxTime": 2,
+	        "isShow": 2,
+	        "recordCount": 6,
+	        "firmwareVersion": 57,
+	        "createdAt": 1740451355,
+	        "updatedAt": 1740466957
+	    },
+	    {
+	        "id": 1725896,
+	        "pastureId": 1,
+	        "neckRingNumber": "10038",
+	        "cowId": 94,
+	        "lact": 0,
+	        "calvingAge": 0,
+	        "activeTime": "2025-02-25 11:00:00",
+	        "frameid": 5,
+	        "heatDate": "2025-02-25",
+	        "rumina": 2,
+	        "intake": 0,
+	        "inactive": 76,
+	        "gasp": 0,
+	        "other": 0,
+	        "high": 249,
+	        "active": 6,
+	        "filterHigh": 20,
+	        "filterRumina": 2,
+	        "filterChew": 2,
+	        "weekHigh": 1500,
+	        "weekHighHabit": 48,
+	        "weekRuminaHabit": 7,
+	        "weekIntakeHabit": 11,
+	        "weekChewHabit": 11,
+	        "weekInactiveHabit": 11,
+	        "weekOtherHabit": 0,
+	        "changeHigh": -3,
+	        "changeRumina": -71,
+	        "changeChew": -82,
+	        "changeAdjust": 0,
+	        "changeFilter": -1,
+	        "ruminaFilter": -9,
+	        "chewFilter": -12,
+	        "filterCorrect": 98,
+	        "sumRumina": 13,
+	        "sumIntake": 0,
+	        "sumInactive": 515,
+	        "sumActive": 136,
+	        "sumMinHigh": -10000,
+	        "sumMaxHigh": 3,
+	        "SumMinChew": 0,
+	        "beforeThreeSumRumina": 20,
+	        "beforeThreeSumIntake": 121,
+	        "score": 69,
+	        "isMaxTime": 2,
+	        "isShow": 2,
+	        "recordCount": 6,
+	        "firmwareVersion": 57,
+	        "createdAt": 1740451385,
+	        "updatedAt": 1740466957
+	    },
+	    {
+	        "id": 1725901,
+	        "pastureId": 1,
+	        "neckRingNumber": "10032",
+	        "cowId": 88,
+	        "lact": 0,
+	        "calvingAge": 0,
+	        "activeTime": "2025-02-25 11:00:00",
+	        "frameid": 5,
+	        "heatDate": "2025-02-25",
+	        "rumina": 7,
+	        "intake": 24,
+	        "inactive": 66,
+	        "gasp": 0,
+	        "other": 0,
+	        "high": 1082,
+	        "active": 51,
+	        "filterHigh": 365,
+	        "filterRumina": 3,
+	        "filterChew": 8,
+	        "weekHigh": 1500,
+	        "weekHighHabit": 113,
+	        "weekRuminaHabit": 16,
+	        "weekIntakeHabit": 7,
+	        "weekChewHabit": 20,
+	        "weekInactiveHabit": 7,
+	        "weekOtherHabit": 0,
+	        "changeHigh": 22,
+	        "changeRumina": -81,
+	        "changeChew": -60,
+	        "changeAdjust": 0,
+	        "changeFilter": 0,
+	        "ruminaFilter": 0,
+	        "chewFilter": 0,
+	        "filterCorrect": 97,
+	        "sumRumina": 52,
+	        "sumIntake": 204,
+	        "sumInactive": 686,
+	        "sumActive": 506,
+	        "sumMinHigh": -11,
+	        "sumMaxHigh": 11,
+	        "SumMinChew": 0,
+	        "beforeThreeSumRumina": 123,
+	        "beforeThreeSumIntake": 225,
+	        "score": 87,
+	        "isMaxTime": 2,
+	        "isShow": 2,
+	        "recordCount": 6,
+	        "firmwareVersion": 57,
+	        "createdAt": 1740451390,
+	        "updatedAt": 1740466957
+	    },
+	    {
+	        "id": 1725904,
+	        "pastureId": 1,
+	        "neckRingNumber": "10053",
+	        "cowId": 108,
+	        "lact": 0,
+	        "calvingAge": 0,
+	        "activeTime": "2025-02-25 11:00:00",
+	        "frameid": 5,
+	        "heatDate": "2025-02-25",
+	        "rumina": 48,
+	        "intake": 42,
+	        "inactive": 68,
+	        "gasp": 0,
+	        "other": 0,
+	        "high": 1135,
+	        "active": 49,
+	        "filterHigh": 847,
+	        "filterRumina": 2,
+	        "filterChew": 30,
+	        "weekHigh": 1500,
+	        "weekHighHabit": 651,
+	        "weekRuminaHabit": 13,
+	        "weekIntakeHabit": 20,
+	        "weekChewHabit": 26,
+	        "weekInactiveHabit": 20,
+	        "weekOtherHabit": 0,
+	        "changeHigh": 16,
+	        "changeRumina": -85,
+	        "changeChew": 15,
+	        "changeAdjust": 0,
+	        "changeFilter": 4,
+	        "ruminaFilter": -10,
+	        "chewFilter": 4,
+	        "filterCorrect": 96,
+	        "sumRumina": 158,
+	        "sumIntake": 299,
+	        "sumInactive": 700,
+	        "sumActive": 637,
+	        "sumMinHigh": -10000,
+	        "sumMaxHigh": 12,
+	        "SumMinChew": 10,
+	        "beforeThreeSumRumina": 193,
+	        "beforeThreeSumIntake": 270,
+	        "score": 100,
+	        "isMaxTime": 2,
+	        "isShow": 2,
+	        "recordCount": 6,
+	        "firmwareVersion": 57,
+	        "createdAt": 1740452505,
+	        "updatedAt": 1740466957
+	    },
+	    {
+	        "id": 1725908,
+	        "pastureId": 1,
+	        "neckRingNumber": "10027",
+	        "cowId": 30,
+	        "lact": 0,
+	        "calvingAge": 0,
+	        "activeTime": "2025-02-25 11:00:00",
+	        "frameid": 5,
+	        "heatDate": "2025-02-25",
+	        "rumina": 43,
+	        "intake": 25,
+	        "inactive": 93,
+	        "gasp": 0,
+	        "other": 0,
+	        "high": 821,
+	        "active": 30,
+	        "filterHigh": 32,
+	        "filterRumina": 29,
+	        "filterChew": 29,
+	        "weekHigh": 1500,
+	        "weekHighHabit": 277,
+	        "weekRuminaHabit": 18,
+	        "weekIntakeHabit": 11,
+	        "weekChewHabit": 24,
+	        "weekInactiveHabit": 11,
+	        "weekOtherHabit": 0,
+	        "changeHigh": -21,
+	        "changeRumina": 61,
+	        "changeChew": 21,
+	        "changeAdjust": 0,
+	        "changeFilter": -5,
+	        "ruminaFilter": 7,
+	        "chewFilter": 6,
+	        "filterCorrect": 94,
+	        "sumRumina": 144,
+	        "sumIntake": 285,
+	        "sumInactive": 533,
+	        "sumActive": 654,
+	        "sumMinHigh": -10000,
+	        "sumMaxHigh": 20,
+	        "SumMinChew": 0,
+	        "beforeThreeSumRumina": 127,
+	        "beforeThreeSumIntake": 297,
+	        "score": 98,
+	        "isMaxTime": 2,
+	        "isShow": 2,
+	        "recordCount": 6,
+	        "firmwareVersion": 57,
+	        "createdAt": 1740452530,
+	        "updatedAt": 1740466957
+	    },
+	    {
+	        "id": 1725913,
+	        "pastureId": 1,
+	        "neckRingNumber": "10028",
+	        "cowId": 84,
+	        "lact": 2,
+	        "calvingAge": 300,
+	        "activeTime": "2025-02-25 11:00:00",
+	        "frameid": 5,
+	        "heatDate": "2025-02-25",
+	        "rumina": 64,
+	        "intake": 36,
+	        "inactive": 67,
+	        "gasp": 0,
+	        "other": 0,
+	        "high": 1391,
+	        "active": 57,
+	        "filterHigh": 512,
+	        "filterRumina": 20,
+	        "filterChew": 31,
+	        "weekHigh": 1500,
+	        "weekHighHabit": 559,
+	        "weekRuminaHabit": 4,
+	        "weekIntakeHabit": 29,
+	        "weekChewHabit": 13,
+	        "weekInactiveHabit": 29,
+	        "weekOtherHabit": 0,
+	        "changeHigh": -4,
+	        "changeRumina": 400,
+	        "changeChew": 138,
+	        "changeAdjust": 0,
+	        "changeFilter": -1,
+	        "ruminaFilter": 50,
+	        "chewFilter": 21,
+	        "filterCorrect": 95,
+	        "sumRumina": 110,
+	        "sumIntake": 264,
+	        "sumInactive": 725,
+	        "sumActive": 645,
+	        "sumMinHigh": -13,
+	        "sumMaxHigh": 16,
+	        "SumMinChew": 0,
+	        "beforeThreeSumRumina": 157,
+	        "beforeThreeSumIntake": 266,
+	        "score": 97,
+	        "isMaxTime": 2,
+	        "isShow": 1,
+	        "recordCount": 6,
+	        "firmwareVersion": 57,
+	        "createdAt": 1740452850,
+	        "updatedAt": 1740466957
+	    },
+	    {
+	        "id": 1725914,
+	        "pastureId": 1,
+	        "neckRingNumber": "10054",
+	        "cowId": 109,
+	        "lact": 3,
+	        "calvingAge": 167,
+	        "activeTime": "2025-02-25 13:00:00",
+	        "frameid": 6,
+	        "heatDate": "2025-02-25",
+	        "rumina": 26,
+	        "intake": 27,
+	        "inactive": 69,
+	        "gasp": 0,
+	        "other": 0,
+	        "high": 1001,
+	        "active": 46,
+	        "filterHigh": 349,
+	        "filterRumina": 0,
+	        "filterChew": 14,
+	        "weekHigh": 1500,
+	        "weekHighHabit": 200,
+	        "weekRuminaHabit": 8,
+	        "weekIntakeHabit": 8,
+	        "weekChewHabit": 9,
+	        "weekInactiveHabit": 8,
+	        "weekOtherHabit": 0,
+	        "changeHigh": 13,
+	        "changeRumina": -100,
+	        "changeChew": 56,
+	        "changeAdjust": 0,
+	        "changeFilter": 4,
+	        "ruminaFilter": -15,
+	        "chewFilter": 17,
+	        "filterCorrect": 97,
+	        "sumRumina": 67,
+	        "sumIntake": 254,
+	        "sumInactive": 674,
+	        "sumActive": 627,
+	        "sumMinHigh": -10000,
+	        "sumMaxHigh": 5,
+	        "SumMinChew": 3,
+	        "beforeThreeSumRumina": 73,
+	        "beforeThreeSumIntake": 227,
+	        "score": 100,
+	        "isMaxTime": 2,
+	        "isShow": 2,
+	        "recordCount": 6,
+	        "firmwareVersion": 57,
+	        "createdAt": 1740457320,
+	        "updatedAt": 1740466957
+	    },
+	    {
+	        "id": 1725921,
+	        "pastureId": 1,
+	        "neckRingNumber": "10030",
+	        "cowId": 86,
+	        "lact": 2,
+	        "calvingAge": 300,
+	        "activeTime": "2025-02-25 13:00:00",
+	        "frameid": 6,
+	        "heatDate": "2025-02-25",
+	        "rumina": 54,
+	        "intake": 0,
+	        "inactive": 105,
+	        "gasp": 0,
+	        "other": 0,
+	        "high": 198,
+	        "active": 10,
+	        "filterHigh": 48,
+	        "filterRumina": 15,
+	        "filterChew": 15,
+	        "weekHigh": 1500,
+	        "weekHighHabit": 32,
+	        "weekRuminaHabit": 13,
+	        "weekIntakeHabit": 2,
+	        "weekChewHabit": 13,
+	        "weekInactiveHabit": 2,
+	        "weekOtherHabit": 0,
+	        "changeHigh": 1,
+	        "changeRumina": 15,
+	        "changeChew": 15,
+	        "changeAdjust": 0,
+	        "changeFilter": 0,
+	        "ruminaFilter": 4,
+	        "chewFilter": 4,
+	        "filterCorrect": 97,
+	        "sumRumina": 180,
+	        "sumIntake": 266,
+	        "sumInactive": 677,
+	        "sumActive": 664,
+	        "sumMinHigh": -10000,
+	        "sumMaxHigh": 10,
+	        "SumMinChew": 5,
+	        "beforeThreeSumRumina": 128,
+	        "beforeThreeSumIntake": 278,
+	        "score": 100,
+	        "isMaxTime": 2,
+	        "isShow": 2,
+	        "recordCount": 6,
+	        "firmwareVersion": 57,
+	        "createdAt": 1740458550,
+	        "updatedAt": 1740466957
+	    },
+	    {
+	        "id": 1725926,
+	        "pastureId": 1,
+	        "neckRingNumber": "10055",
+	        "cowId": 110,
+	        "lact": 2,
+	        "calvingAge": 62,
+	        "activeTime": "2025-02-25 13:00:00",
+	        "frameid": 6,
+	        "heatDate": "2025-02-25",
+	        "rumina": 23,
+	        "intake": 29,
+	        "inactive": 71,
+	        "gasp": 0,
+	        "other": 0,
+	        "high": 1844,
+	        "active": 50,
+	        "filterHigh": 402,
+	        "filterRumina": 16,
+	        "filterChew": 19,
+	        "weekHigh": 1500,
+	        "weekHighHabit": 296,
+	        "weekRuminaHabit": 13,
+	        "weekIntakeHabit": 12,
+	        "weekChewHabit": 17,
+	        "weekInactiveHabit": 12,
+	        "weekOtherHabit": 0,
+	        "changeHigh": 9,
+	        "changeRumina": 23,
+	        "changeChew": 12,
+	        "changeAdjust": 0,
+	        "changeFilter": 3,
+	        "ruminaFilter": 7,
+	        "chewFilter": 4,
+	        "filterCorrect": 95,
+	        "sumRumina": 121,
+	        "sumIntake": 236,
+	        "sumInactive": 627,
+	        "sumActive": 696,
+	        "sumMinHigh": -10000,
+	        "sumMaxHigh": 18,
+	        "SumMinChew": 0,
+	        "beforeThreeSumRumina": 68,
+	        "beforeThreeSumIntake": 282,
+	        "score": 100,
+	        "isMaxTime": 2,
+	        "isShow": 2,
+	        "recordCount": 6,
+	        "firmwareVersion": 57,
+	        "createdAt": 1740458585,
+	        "updatedAt": 1740466957
+	    },
+	    {
+	        "id": 1725932,
+	        "pastureId": 1,
+	        "neckRingNumber": "10032",
+	        "cowId": 88,
+	        "lact": 0,
+	        "calvingAge": 0,
+	        "activeTime": "2025-02-25 13:00:00",
+	        "frameid": 6,
+	        "heatDate": "2025-02-25",
+	        "rumina": 66,
+	        "intake": 0,
+	        "inactive": 111,
+	        "gasp": 0,
+	        "other": 0,
+	        "high": 90,
+	        "active": 6,
+	        "filterHigh": 4,
+	        "filterRumina": 27,
+	        "filterChew": 27,
+	        "weekHigh": 1500,
+	        "weekHighHabit": 216,
+	        "weekRuminaHabit": 9,
+	        "weekIntakeHabit": 13,
+	        "weekChewHabit": 14,
+	        "weekInactiveHabit": 13,
+	        "weekOtherHabit": 0,
+	        "changeHigh": -18,
+	        "changeRumina": 200,
+	        "changeChew": 93,
+	        "changeAdjust": 0,
+	        "changeFilter": -4,
+	        "ruminaFilter": 24,
+	        "chewFilter": 14,
+	        "filterCorrect": 97,
+	        "sumRumina": 62,
+	        "sumIntake": 220,
+	        "sumInactive": 664,
+	        "sumActive": 530,
+	        "sumMinHigh": -10000,
+	        "sumMaxHigh": 11,
+	        "SumMinChew": 0,
+	        "beforeThreeSumRumina": 123,
+	        "beforeThreeSumIntake": 249,
+	        "score": 98,
+	        "isMaxTime": 2,
+	        "isShow": 2,
+	        "recordCount": 6,
+	        "firmwareVersion": 57,
+	        "createdAt": 1740458625,
+	        "updatedAt": 1740466957
+	    },
+	    {
+	        "id": 1725934,
+	        "pastureId": 1,
+	        "neckRingNumber": "10053",
+	        "cowId": 108,
+	        "lact": 0,
+	        "calvingAge": 0,
+	        "activeTime": "2025-02-25 13:00:00",
+	        "frameid": 6,
+	        "heatDate": "2025-02-25",
+	        "rumina": 45,
+	        "intake": 31,
+	        "inactive": 62,
+	        "gasp": 0,
+	        "other": 0,
+	        "high": 1464,
+	        "active": 58,
+	        "filterHigh": 267,
+	        "filterRumina": 14,
+	        "filterChew": -13,
+	        "changeAdjust": 0,
+	        "changeFilter": 3,
+	        "ruminaFilter": -7,
+	        "chewFilter": -4,
+	        "filterCorrect": 96,
+	        "sumRumina": 151,
+	        "sumIntake": 294,
+	        "sumInactive": 715,
+	        "sumActive": 625,
+	        "sumMinHigh": -10000,
+	        "sumMaxHigh": 12,
+	        "SumMinChew": 10,
+	        "beforeThreeSumRumina": 185,
+	        "beforeThreeSumIntake": 253,
+	        "score": 100,
+	        "isMaxTime": 2,
+	        "isShow": 2,
+	        "recordCount": 6,
+	        "firmwareVersion": 57,
+	        "createdAt": 1740459700,
+	        "updatedAt": 1740466957
+	    },
+	    {
+	        "id": 1725938,
+	        "pastureId": 1,
+	        "neckRingNumber": "10027",
+	        "cowId": 30,
+	        "lact": 0,
+	        "calvingAge": 0,
+	        "activeTime": "2025-02-25 13:00:00",
+	        "frameid": 6,
+	        "heatDate": "2025-02-25",
+	        "rumina": 41,
+	        "intake": 13,
+	        "inactive": 94,
+	        "gasp": 0,
+	        "other": 0,
+	        "high": 752,
+	        "active": 30,
+	        "filterHigh": 498,
+	        "filterRumina": 17,
+	        "filterChew": 25,
+	        "weekHigh": 1500,
+	        "weekHighHabit": 470,
+	        "weekRuminaHabit": 9,
+	        "weekIntakeHabit": 27,
+	        "weekChewHabit": 22,
+	        "weekInactiveHabit": 27,
+	        "weekOtherHabit": 0,
+	        "changeHigh": 2,
+	        "changeRumina": 89,
+	        "changeChew": 14,
+	        "changeAdjust": 0,
+	        "changeFilter": 0,
+	        "ruminaFilter": 11,
+	        "chewFilter": 4,
+	        "filterCorrect": 94,
+	        "sumRumina": 146,
+	        "sumIntake": 323,
+	        "sumInactive": 480,
+	        "sumActive": 702,
+	        "sumMinHigh": -10000,
+	        "sumMaxHigh": 20,
+	        "SumMinChew": 0,
+	        "beforeThreeSumRumina": 126,
+	        "beforeThreeSumIntake": 274,
+	        "score": 100,
+	        "isMaxTime": 2,
+	        "isShow": 2,
+	        "recordCount": 6,
+	        "firmwareVersion": 57,
+	        "createdAt": 1740459725,
+	        "updatedAt": 1740466957
+	    },
+	    {
+	        "id": 1725943,
+	        "pastureId": 1,
+	        "neckRingNumber": "10028",
+	        "cowId": 84,
+	        "lact": 2,
+	        "calvingAge": 300,
+	        "activeTime": "2025-02-25 13:00:00",
+	        "frameid": 6,
+	        "heatDate": "2025-02-25",
+	        "rumina": 30,
+	        "intake": 0,
+	        "inactive": 109,
+	        "gasp": 0,
+	        "other": 0,
+	        "high": 223,
+	        "active": 8,
+	        "filterHigh": 162,
+	        "filterRumina": 17,
+	        "filterChew": 17,
+	        "weekHigh": 1500,
+	        "weekHighHabit": 639,
+	        "weekRuminaHabit": 2,
+	        "weekIntakeHabit": 32,
+	        "weekChewHabit": 13,
+	        "weekInactiveHabit": 32,
+	        "weekOtherHabit": 0,
+	        "changeHigh": -39,
+	        "changeRumina": 750,
+	        "changeChew": 31,
+	        "changeAdjust": 0,
+	        "changeFilter": -12,
+	        "ruminaFilter": 50,
+	        "chewFilter": 9,
+	        "filterCorrect": 95,
+	        "sumRumina": 124,
+	        "sumIntake": 246,
+	        "sumInactive": 821,
+	        "sumActive": 597,
+	        "sumMinHigh": -13,
+	        "sumMaxHigh": 16,
+	        "SumMinChew": 0,
+	        "beforeThreeSumRumina": 151,
+	        "beforeThreeSumIntake": 244,
+	        "score": 95,
+	        "isMaxTime": 2,
+	        "isShow": 1,
+	        "recordCount": 6,
+	        "firmwareVersion": 57,
+	        "createdAt": 1740460350,
+	        "updatedAt": 1740466957
+	    },
+	    {
+	        "id": 1725944,
+	        "pastureId": 1,
+	        "neckRingNumber": "10054",
+	        "cowId": 109,
+	        "lact": 3,
+	        "calvingAge": 167,
+	        "activeTime": "2025-02-25 15:00:00",
+	        "frameid": 7,
+	        "heatDate": "2025-02-25",
+	        "rumina": 38,
+	        "intake": 20,
+	        "inactive": 43,
+	        "gasp": 0,
+	        "other": 0,
+	        "high": 1131,
+	        "active": 39,
+	        "filterHigh": 1,
+	        "filterRumina": 9,
+	        "filterChew": 9,
+	        "weekHigh": 1500,
+	        "weekHighHabit": 110,
+	        "weekRuminaHabit": 6,
+	        "weekIntakeHabit": 45,
+	        "weekChewHabit": 6,
+	        "weekInactiveHabit": 45,
+	        "weekOtherHabit": 0,
+	        "changeHigh": -10,
+	        "changeRumina": 50,
+	        "changeChew": 50,
+	        "changeAdjust": 0,
+	        "changeFilter": -3,
+	        "ruminaFilter": 15,
+	        "chewFilter": 15,
+	        "filterCorrect": 97,
+	        "sumRumina": 73,
+	        "sumIntake": 253,
+	        "sumInactive": 695,
+	        "sumActive": 606,
+	        "sumMinHigh": -10000,
+	        "sumMaxHigh": 5,
+	        "SumMinChew": 5,
+	        "beforeThreeSumRumina": 75,
+	        "beforeThreeSumIntake": 196,
+	        "score": 98,
+	        "isMaxTime": 2,
+	        "isShow": 2,
+	        "recordCount": 4,
+	        "firmwareVersion": 57,
+	        "createdAt": 1740464515,
+	        "updatedAt": 1740468120
+	    },
+	    {
+	        "id": 1725950,
+	        "pastureId": 1,
+	        "neckRingNumber": "10030",
+	        "cowId": 86,
+	        "lact": 2,
+	        "calvingAge": 300,
+	        "activeTime": "2025-02-25 15:00:00",
+	        "frameid": 7,
+	        "heatDate": "2025-02-25",
+	        "rumina": 50,
+	        "intake": 26,
+	        "inactive": 62,
+	        "gasp": 0,
+	        "other": 0,
+	        "high": 1140,
+	        "active": 43,
+	        "filterHigh": 24,
+	        "filterRumina": 27,
+	        "filterChew": 27,
+	        "weekHigh": 1500,
+	        "weekHighHabit": 307,
+	        "weekRuminaHabit": 8,
+	        "weekIntakeHabit": 42,
+	        "weekChewHabit": 15,
+	        "weekInactiveHabit": 42,
+	        "weekOtherHabit": 0,
+	        "changeHigh": -24,
+	        "changeRumina": 238,
+	        "changeChew": 80,
+	        "changeAdjust": 0,
+	        "changeFilter": -7,
+	        "ruminaFilter": 36,
+	        "chewFilter": 12,
+	        "filterCorrect": 97,
+	        "sumRumina": 188,
+	        "sumIntake": 238,
+	        "sumInactive": 713,
+	        "sumActive": 621,
+	        "sumMinHigh": -10000,
+	        "sumMaxHigh": 10,
+	        "SumMinChew": 5,
+	        "beforeThreeSumRumina": 154,
+	        "beforeThreeSumIntake": 230,
+	        "score": 97,
+	        "isMaxTime": 2,
+	        "isShow": 2,
+	        "recordCount": 5,
+	        "firmwareVersion": 57,
+	        "createdAt": 1740465745,
+	        "updatedAt": 1740469355
+	    },
+	    {
+	        "id": 1725955,
+	        "pastureId": 1,
+	        "neckRingNumber": "10038",
+	        "cowId": 94,
+	        "lact": 0,
+	        "calvingAge": 0,
+	        "activeTime": "2025-02-25 15:00:00",
+	        "frameid": 7,
+	        "heatDate": "2025-02-25",
+	        "rumina": 2,
+	        "intake": 0,
+	        "inactive": 50,
+	        "gasp": 0,
+	        "other": 0,
+	        "high": 532,
+	        "active": 20,
+	        "filterHigh": 23,
+	        "filterRumina": 0,
+	        "filterChew": 0,
+	        "weekHigh": 1500,
+	        "weekHighHabit": 287,
+	        "weekRuminaHabit": 6,
+	        "weekIntakeHabit": 35,
+	        "weekChewHabit": 12,
+	        "weekInactiveHabit": 35,
+	        "weekOtherHabit": 0,
+	        "changeHigh": -23,
+	        "changeRumina": -100,
+	        "changeChew": -100,
+	        "changeAdjust": 0,
+	        "changeFilter": -6,
+	        "ruminaFilter": -12,
+	        "chewFilter": -15,
+	        "filterCorrect": 98,
+	        "sumRumina": 10,
+	        "sumIntake": 0,
+	        "sumInactive": 679,
+	        "sumActive": 124,
+	        "sumMinHigh": -10000,
+	        "sumMaxHigh": 3,
+	        "SumMinChew": 0,
+	        "beforeThreeSumRumina": 8,
+	        "beforeThreeSumIntake": 7,
+	        "score": 67,
+	        "isMaxTime": 2,
+	        "isShow": 2,
+	        "recordCount": 5,
+	        "firmwareVersion": 57,
+	        "createdAt": 1740465775,
+	        "updatedAt": 1740469385
+	    },
+	    {
+	        "id": 1725957,
+	        "pastureId": 1,
+	        "neckRingNumber": "10055",
+	        "cowId": 110,
+	        "lact": 2,
+	        "calvingAge": 62,
+	        "activeTime": "2025-02-25 15:00:00",
+	        "frameid": 7,
+	        "heatDate": "2025-02-25",
+	        "rumina": 24,
+	        "intake": 39,
+	        "inactive": 32,
+	        "gasp": 0,
+	        "other": 0,
+	        "high": 1902,
+	        "active": 63,
+	        "filterHigh": 47,
+	        "filterRumina": 15,
+	        "filterChew": 15,
+	        "weekHigh": 1500,
+	        "weekHighHabit": 980,
+	        "weekRuminaHabit": 3,
+	        "weekIntakeHabit": 34,
+	        "weekChewHabit": 14,
+	        "weekInactiveHabit": 34,
+	        "weekOtherHabit": 0,
+	        "changeHigh": -71,
+	        "changeRumina": 400,
+	        "changeChew": 7,
+	        "changeAdjust": 0,
+	        "changeFilter": -21,
+	        "ruminaFilter": 50,
+	        "chewFilter": 2,
+	        "filterCorrect": 95,
+	        "sumRumina": 125,
+	        "sumIntake": 208,
+	        "sumInactive": 690,
+	        "sumActive": 627,
+	        "sumMinHigh": -10000,
+	        "sumMaxHigh": 18,
+	        "SumMinChew": 0,
+	        "beforeThreeSumRumina": 71,
+	        "beforeThreeSumIntake": 279,
+	        "score": 91,
+	        "isMaxTime": 2,
+	        "isShow": 2,
+	        "recordCount": 5,
+	        "firmwareVersion": 57,
+	        "createdAt": 1740465780,
+	        "updatedAt": 1740469380
+	    },
+	    {
+	        "id": 1725961,
+	        "pastureId": 1,
+	        "neckRingNumber": "10032",
+	        "cowId": 88,
+	        "lact": 0,
+	        "calvingAge": 0,
+	        "activeTime": "2025-02-25 15:00:00",
+	        "frameid": 7,
+	        "heatDate": "2025-02-25",
+	        "rumina": 40,
+	        "intake": 34,
+	        "inactive": 39,
+	        "gasp": 0,
+	        "other": 0,
+	        "high": 1908,
+	        "active": 62,
+	        "filterHigh": 52,
+	        "filterRumina": 25,
+	        "filterChew": 25,
+	        "weekHigh": 1500,
+	        "weekHighHabit": 393,
+	        "weekRuminaHabit": 5,
+	        "weekIntakeHabit": 44,
+	        "weekChewHabit": 16,
+	        "weekInactiveHabit": 44,
+	        "weekOtherHabit": 0,
+	        "changeHigh": -29,
+	        "changeRumina": 400,
+	        "changeChew": 56,
+	        "changeAdjust": 0,
+	        "changeFilter": -7,
+	        "ruminaFilter": 48,
+	        "chewFilter": 17,
+	        "filterCorrect": 97,
+	        "sumRumina": 72,
+	        "sumIntake": 184,
+	        "sumInactive": 734,
+	        "sumActive": 464,
+	        "sumMinHigh": -10000,
+	        "sumMaxHigh": 11,
+	        "SumMinChew": 0,
+	        "beforeThreeSumRumina": 87,
+	        "beforeThreeSumIntake": 206,
+	        "score": 87,
+	        "isMaxTime": 2,
+	        "isShow": 2,
+	        "recordCount": 5,
+	        "firmwareVersion": 57,
+	        "createdAt": 1740465815,
+	        "updatedAt": 1740469420
+	    },
+	    {
+	        "id": 1725965,
+	        "pastureId": 1,
+	        "neckRingNumber": "10053",
+	        "cowId": 108,
+	        "lact": 0,
+	        "calvingAge": 0,
+	        "activeTime": "2025-02-25 15:00:00",
+	        "frameid": 7,
+	        "heatDate": "2025-02-25",
+	        "rumina": 44,
+	        "intake": 0,
+	        "inactive": 60,
+	        "gasp": 0,
+	        "other": 0,
+	        "high": 37,
+	        "active": 0,
+	        "filterHigh": 29,
+	        "filterRumina": 30,
+	        "filterChew": 30,
+	        "weekHigh": 1500,
+	        "weekHighHabit": 576,
+	        "weekRuminaHabit": 14,
+	        "weekIntakeHabit": 39,
+	        "weekChewHabit": 29,
+	        "weekInactiveHabit": 39,
+	        "weekOtherHabit": 0,
+	        "changeHigh": -45,
+	        "changeRumina": 114,
+	        "changeChew": 3,
+	        "changeAdjust": 0,
+	        "changeFilter": -11,
+	        "ruminaFilter": 14,
+	        "chewFilter": 1,
+	        "filterCorrect": 96,
+	        "sumRumina": 162,
+	        "sumIntake": 274,
+	        "sumInactive": 762,
+	        "sumActive": 583,
+	        "sumMinHigh": -10000,
+	        "sumMaxHigh": 12,
+	        "SumMinChew": 10,
+	        "beforeThreeSumRumina": 201,
+	        "beforeThreeSumIntake": 245,
+	        "score": 95,
+	        "isMaxTime": 2,
+	        "isShow": 1,
+	        "recordCount": 3,
+	        "firmwareVersion": 57,
+	        "createdAt": 1740466900,
+	        "updatedAt": 1740466958
+	    },
+	    {
+	        "id": 1725968,
+	        "pastureId": 1,
+	        "neckRingNumber": "10027",
+	        "cowId": 30,
+	        "lact": 0,
+	        "calvingAge": 0,
+	        "activeTime": "2025-02-25 15:00:00",
+	        "frameid": 7,
+	        "heatDate": "2025-02-25",
+	        "rumina": 22,
+	        "intake": 0,
+	        "inactive": 60,
+	        "gasp": 0,
+	        "other": 0,
+	        "high": 89,
+	        "active": 6,
+	        "filterHigh": 69,
+	        "filterRumina": 15,
+	        "filterChew": 15,
+	        "weekHigh": 1500,
+	        "weekHighHabit": 588,
+	        "weekRuminaHabit": 14,
+	        "weekIntakeHabit": 28,
+	        "weekChewHabit": 25,
+	        "weekInactiveHabit": 28,
+	        "weekOtherHabit": 0,
+	        "changeHigh": -42,
+	        "changeRumina": 7,
+	        "changeChew": -40,
+	        "changeAdjust": 0,
+	        "changeFilter": -10,
+	        "ruminaFilter": 2,
+	        "chewFilter": -12,
+	        "filterCorrect": 94,
+	        "sumRumina": 156,
+	        "sumIntake": 300,
+	        "sumInactive": 515,
+	        "sumActive": 677,
+	        "sumMinHigh": -10000,
+	        "sumMaxHigh": 20,
+	        "SumMinChew": 0,
+	        "beforeThreeSumRumina": 96,
+	        "beforeThreeSumIntake": 316,
+	        "score": 96,
+	        "isMaxTime": 2,
+	        "isShow": 1,
+	        "recordCount": 3,
+	        "firmwareVersion": 57,
+	        "createdAt": 1740466925,
+	        "updatedAt": 1740466958
+	    },
+	    {
+	        "id": 1725973,
+	        "pastureId": 1,
+	        "neckRingNumber": "10028",
+	        "cowId": 84,
+	        "lact": 2,
+	        "calvingAge": 300,
+	        "activeTime": "2025-02-25 15:00:00",
+	        "frameid": 7,
+	        "heatDate": "2025-02-25",
+	        "rumina": 14,
+	        "intake": 36,
+	        "inactive": 12,
+	        "gasp": 0,
+	        "other": 0,
+	        "high": 1541,
+	        "active": 50,
+	        "filterHigh": 1187,
+	        "filterRumina": 10,
+	        "filterChew": 34,
+	        "weekHigh": 1500,
+	        "weekHighHabit": 203,
+	        "weekRuminaHabit": 14,
+	        "weekIntakeHabit": 36,
+	        "weekChewHabit": 16,
+	        "weekInactiveHabit": 36,
+	        "weekOtherHabit": 0,
+	        "changeHigh": 86,
+	        "changeRumina": -29,
+	        "changeChew": 113,
+	        "changeAdjust": 0,
+	        "changeFilter": 26,
+	        "ruminaFilter": -9,
+	        "chewFilter": 17,
+	        "filterCorrect": 95,
+	        "sumRumina": 127,
+	        "sumIntake": 231,
+	        "sumInactive": 791,
+	        "sumActive": 567,
+	        "sumMinHigh": -10000,
+	        "sumMaxHigh": 16,
+	        "SumMinChew": 0,
+	        "beforeThreeSumRumina": 134,
+	        "beforeThreeSumIntake": 243,
+	        "score": 100,
+	        "isMaxTime": 2,
+	        "isShow": 1,
+	        "recordCount": 3,
+	        "firmwareVersion": 57,
+	        "createdAt": 1740466940,
+	        "updatedAt": 1740466958
+	    }
+	]`
+
+	if err := json.Unmarshal([]byte(str), &neckActiveHabitList); err != nil {
+		panic(err)
+	}
+
+	for _, habit := range neckActiveHabitList {
+		cft := calculateCFT(habit)
+		fmt.Println(habit.Id, cft)
+	}
 }

+ 126 - 69
module/crontab/neck_ring_handle.go

@@ -50,17 +50,74 @@ func (e *Entry) NeckRingOriginalMergeData() (err error) {
 
 	//createdAt := newTime.Add(-1 * time.Hour)
 	neckRingNumber := []string{
-		"10026", "10027", "10028", "10029", "10030",
+		/*"10026", "10027", "10028", "10029", "10030",
 		"10031", "10032", "10033", "10034", "10035",
 		"10036", "10037", "10038", "10039", "10040",
 		"10041", "10042", "10043", "10044", "10046",
 		"10047", "10048", "10049", "10050", "10051",
 		"10052", "10053", "10054", "10055", "10056",
+		"9120007", "9120029", "9120040", "9120123", "9120164", "9120184", "9120219", "9120246", "9120321", "9120355",
+		"9120359", "9120375", "9120379", "9120391", "9120446", "9120493", "9120497", "9120512", "9120531", "9120533",
+		"9120540", "9120542", "9120612", "9120614", "9120615", "9120623", "9120625", "9120630", "9120653", "9120662",
+		"211670", "9121372", "3204736", "3212694", "3204532", "3214082", "9121667", "3212275", "3210345", "3217879",*/
+		"9422", "3206975", "9496", "3204907", "212194", "3211061", "9120840", "3207787", "3210201", "404", "3207104",
+		"3219173", "3216771", "3216872", "3209614", "3205455", "416", "9121026", "211622", "3207868", "3210340", "9120132",
+		"3207092", "3209390", "3205872", "3207367", "3219700", "9120022", "211246", "466", "3207565", "3208779", "3204863",
+		"3207963", "3204259", "3207966", "2640", "3214316", "3205778", "3206897", "3207745", "3209052", "3208045", "211627",
+		"3212873", "2100766", "9121950", "3206076", "3206438", "9466", "9121195", "9122044", "3209430", "3205599", "2100794",
+		"3048", "9467", "3207480", "3216011", "9121725", "9120340", "9121597", "427", "3209387", "3209490", "3214311",
+		"3206044", "211253", "3207553", "3215616", "211350", "3207551", "3205896", "417", "9121008", "3207694", "3209372",
+		"3217873", "3207227", "3207568", "3210615", "3204769", "3216095", "9121338", "3209124", "211340", "3219695",
+		"9121802", "3205517", "3210676", "9123325", "3204328", "9409", "211349", "3208860", "9121769", "3209221",
+		"3210916", "3205880", "438", "3205557", "9421", "211229", "444", "9123260", "9121464", "3212818", "3204834",
+		"3205408", "3207486", "498", "3206232", "3206315", "2100759", "9121955", "3204338", "3207606", "3208767", "450",
+		"9123330", "9121278", "9121011", "9122280", "479", "434", "3207614", "2355", "211644", "453", "9121308", "3209449",
+		"405", "3204240", "9120165", "9445", "9120456", "9123057", "471", "3206830", "464", "403", "2100749", "3218278",
+		"3218600", "212168", "9470", "428", "3205448", "3209790", "3208163", "213134", "3207603", "3206394", "3204476",
+		"9121569", "3206447", "3209456", "9120533", "3209044", "3214232", "3209337", "212193", "3216684", "3218450",
+		"3207194", "3204853", "3205858", "3207922", "3204645", "212157", "3214707", "213126", "3219468", "9120766",
+		"3218611", "3215948", "3211568", "9120227", "211348", "3212621", "3209898", "3207637", "9121071", "9123113",
+		"3206491", "3209922", "9440", "3207075", "423", "3204875", "9121888", "3210395", "9120630", "9120214", "3208166",
+		"211285", "3206880", "3209461", "3205597", "3216262", "3204942", "9123224", "3205918", "3204889", "3209047",
+		"3207024", "3207277", "3207699", "3205771", "9500", "3204450", "3205495", "9483", "3209876", "3205533", "3216075",
+		"9469", "3209827", "9122454", "413", "3216308", "3219301", "3210042", "3205064", "211390", "9121305", "3218596",
+		"3204955", "3215274", "212171", "3209460", "3211376", "3213812", "3205184", "3209287", "3211769", "3205926",
+		"3216585", "3213332", "3209340", "9123175", "3205843", "3207663", "3210829", "3209481", "9450", "9463",
+		"3209763", "3215627", "9121424", "3212746", "3218533", "3209072", "3207359", "3205888", "3214228", "3204884",
+		"3218613", "3209352", "3208192", "3216211", "3211454", "3217082", "3212728", "3206984", "3217750", "3213406",
+		"3206305", "9122130", "9121695", "9492", "3207856", "3218263", "9121058", "9123309", "9122466", "9122287",
+		"9120614", "3084", "3205559", "3205840", "9121444", "9121777", "3209308", "9122313", "9121672", "3210180",
+		"3207645", "3206419", "9413", "211576", "3209201", "2601", "3211708", "3206969", "3206871", "3210430",
+		"211674", "9122441", "3214386", "3206927", "3209332", "9462", "3206430", "3207485", "3204519", "3216214",
+		"9123371", "9120847", "9123355", "211690", "3210362", "3218862", "3213687", "3066", "3209787", "9120359",
+		"9468", "2315", "3207031", "211353", "211250", "3207688", "9122447", "3218688", "445", "3205848", "3214915",
+		"3208866", "1739", "3204990", "2100716", "212130", "3204214", "3208985", "211388", "9123166", "3208856", "211648",
+		"2311", "3204411", "3217860", "9523", "9524", "3209134", "3209212", "211630", "9123151", "3207375", "441", "9525",
+		"3205815", "3205527", "3215344", "3207185", "211332", "3217466", "9526", "1783", "9122414", "3207617", "212150",
+		"3204248", "3216316", "3209535", "3206873", "3208974", "9406", "9122407", "9121925", "2351", "3214481", "3204826",
+		"3205906", "3205109", "3209014", "211240", "3070", "474", "3204667", "3205511", "409", "3209061", "3205074", "3206009",
+		"9123010", "3207536", "3219231", "3207198", "9475", "9423", "9121871", "3204643", "3205471", "3206095", "9121981",
+		"9120835", "3206427", "3218217", "3207493", "2100732", "3204886", "3208174", "9486", "2100772", "3209419",
+		"3207087", "2328", "3207371", "3210597", "3206231", "3206931", "3204236", "3207411", "3206374", "3206452",
+		"3207472", "9429", "3218802", "211381", "9474", "3204420", "3207026", "3206124", "3209608", "3209330", "3209485",
+		"3216593", "3205921", "2375", "3204818", "3215544", "3213632", "3216924", "3204395", "3207111", "3206961", "212169",
+		"9123027", "2100744", "9520", "3208708", "3214104", "3206329", "9512", "3211187", "3207674", "3206004", "3207748",
+		"3209328", "3209033", "3205601", "3205776", "9438", "3205269", "3204664", "9514", "3204897", "9446", "3208827",
+		"3209611", "3208050", "9501", "9121120", "3213788", "9121699", "3204409", "3210073", "3207546", "2100758", "3206082",
+		"3208975", "9122051", "9518", "3207751", "9408", "3206437", "3207343", "3207021", "3216998", "3205349", "3214744",
+		"3205905", "3212646", "3209740", "3206239", "3207473", "3207236", "3209730", "3204360", "3206895", "9120696", "3204901",
+		"9508", "3207935", "3213977", "3214166", "448", "3205893", "3212052", "3205552", "3211112", "3213551", "3217077",
+		"3206322", "9465", "3208807", "3205211", "3215051", "3207121", "3215725", "3207229", "3219426", "3213345", "3217541",
+		"3216876", "3215675", "3214245", "3207012", "3218806", "3217024", "3212486", "3207620", "3211045", "3213712", "3215268",
+		"3215061", "3209099", "3218820", "3212817", "3204659", "3210976", "3213266", "3211716", "3207860", "3213350", "9517",
+		"3215001", "3217667", "3213039", "3207248", "9510", "3215056", "3218732", "3216053", "3209995", "3215773", "3211035",
+		"3213295", "3205725", "9464", "3205636",
 	}
 	neckRingList := make([]*model.NeckRingOriginal, 0)
 	if err = e.DB.Model(new(model.NeckRingOriginal)).
 		Where("is_show = ?", pasturePb.IsShow_No).
 		Where("neck_ring_number IN (?)", neckRingNumber).
+		Where("active_date = ?", time.Now().Format(model.LayoutDate2)).
 		//Where("created_at <= ?", createdAt.Unix()).
 		Order("active_date,frameid,neck_ring_number").
 		Limit(int(limit)).Find(&neckRingList).Error; err != nil {
@@ -138,72 +195,10 @@ func (e *Entry) NeckRingCalculate() error {
 	return nil
 }
 
-// recalculate 合并计算
-func (e *Entry) recalculate(neckRingList []*model.NeckRingOriginal) []*model.NeckActiveHabit {
-	originalMapData := make(map[string]*model.NeckRingOriginalMerge)
-	// 合并成2个小时的
-	for _, v := range neckRingList {
-		xframeId := util.XFrameId(v.Frameid)
-		mapKey := fmt.Sprintf("%s%s%s%s%d", v.NeckRingNumber, model.JoinKey, v.ActiveDate, model.JoinKey, xframeId) // 0001/2023-12-04/0 0001/2023-12-03/4
-		if originalMapData[mapKey] == nil {
-			originalMapData[mapKey] = new(model.NeckRingOriginalMerge)
-		}
-		originalMapData[mapKey].IsMageData(v, xframeId)
-	}
-
-	currTime := time.Now()
-	res := make([]*model.NeckActiveHabit, 0)
-	// 算平均值
-	for k, v := range originalMapData {
-		// 过滤掉合并后不等于6条数据
-		if v.RecordCount > 6 {
-			delete(originalMapData, k)
-			continue
-		} else if v.RecordCount < 6 {
-			currMaxXframeId := util.FrameIdMapReverse[int32(currTime.Hour())]
-			activeDateString := fmt.Sprintf("%s %02d:00:00", v.ActiveDate, v.XframeId*2+1)
-			activeDate, _ := time.Parse(model.LayoutTime, activeDateString)
-			if currMaxXframeId-v.XframeId <= 1 && currTime.Add(-1*time.Hour).Unix() < activeDate.Unix() {
-				delete(originalMapData, k)
-				continue
-			}
-		}
-		v.SumAvg()
-	}
-
-	if len(originalMapData) <= 0 {
-		return res
-	}
-	res = model.NeckRingOriginalMap(originalMapData).ForMatData()
-	sort.Sort(model.NeckActiveHabitSlice(res))
-	return res
-}
-
-func (e *Entry) againRecalculate(data *model.NeckActiveHabit) *model.NeckActiveHabit {
-	originalList := make([]*model.NeckRingOriginal, 0)
-	frameIds := util.FrameIds(data.Frameid)
-	if err := e.DB.Model(new(model.NeckRingOriginal)).
-		Where("pasture_id = ?", data.PastureId).
-		Where("neck_ring_number = ?", data.NeckRingNumber).
-		Where("active_date = ?", data.HeatDate).
-		Where("frameid IN (?)", frameIds).
-		Find(&originalList).Error; err != nil {
-		return nil
-	}
-
-	newDataList := e.recalculate(originalList)
-	if len(newDataList) != 1 {
-		return nil
-	}
-	res := newDataList[0]
-	res.IsShow = pasturePb.IsShow_No
-	return res
-}
-
 func (e *Entry) EntryUpdateActiveHabit(pastureId int64) (err error) {
 	// 获取这段执行数据内最大日期和最小日期
 	xToday := &XToday{}
-	systemConfigureList, err := e.GetSystemConfigure(pastureId)
+	systemConfigureList, err := e.GetSystemNeckRingConfigure(pastureId)
 	if err != nil {
 		return xerr.WithStack(err)
 	}
@@ -372,18 +367,18 @@ func (e *Entry) FirstFilterUpdate(pastureId int64, xToDay *XToday) (processIds [
 
 		processIds = append(processIds, v.Id)
 
-		// 更新过滤值
+		// 更新过滤值 // todo 记得更新胎次为牛只胎次,现在为了测试特意改成0
 		if err = e.DB.Model(new(model.NeckActiveHabit)).
-			Select(
-				"filter_high", "filter_rumina", "filter_chew", "cow_id", "lact", "calving_age",
-			).Where("id = ?", v.Id).
+			Select("filter_high", "filter_rumina", "filter_chew", "cow_id", "lact", "calving_age", "ear_number").
+			Where("id = ?", v.Id).
 			Updates(map[string]interface{}{
 				"filter_high":   firstFilterData.FilterHigh,
 				"filter_rumina": firstFilterData.FilterRumina,
 				"filter_chew":   firstFilterData.FilterChew,
 				"cow_id":        cowInfo.Id,
-				"lact":          cowInfo.Lact,
+				"lact":          0,
 				"calving_age":   cowInfo.CalvingAge,
+				"ear_number":    cowInfo.EarNumber,
 			}).Error; err != nil {
 			zaplog.Error("FirstFilterUpdate",
 				zap.Any("error", err),
@@ -708,6 +703,68 @@ func (e *Entry) UpdateNeckRingOriginalIsShow(habit *model.NeckActiveHabit) error
 	return nil
 }
 
+// recalculate 合并计算
+func (e *Entry) recalculate(neckRingList []*model.NeckRingOriginal) []*model.NeckActiveHabit {
+	originalMapData := make(map[string]*model.NeckRingOriginalMerge)
+	// 合并成2个小时的
+	for _, v := range neckRingList {
+		xframeId := util.XFrameId(v.Frameid)
+		mapKey := fmt.Sprintf("%s%s%s%s%d", v.NeckRingNumber, model.JoinKey, v.ActiveDate, model.JoinKey, xframeId) // 0001/2023-12-04/0 0001/2023-12-03/4
+		if originalMapData[mapKey] == nil {
+			originalMapData[mapKey] = new(model.NeckRingOriginalMerge)
+		}
+		originalMapData[mapKey].IsMageData(v, xframeId)
+	}
+
+	currTime := time.Now()
+	res := make([]*model.NeckActiveHabit, 0)
+	// 算平均值
+	for k, v := range originalMapData {
+		// 过滤掉合并后不等于6条数据
+		if v.RecordCount > 6 {
+			delete(originalMapData, k)
+			continue
+		} else if v.RecordCount < 6 {
+			currMaxXframeId := util.FrameIdMapReverse[int32(currTime.Hour())]
+			activeDateString := fmt.Sprintf("%s %02d:00:00", v.ActiveDate, v.XframeId*2+1)
+			activeDate, _ := time.Parse(model.LayoutTime, activeDateString)
+			if currMaxXframeId-v.XframeId <= 1 && currTime.Add(-1*time.Hour).Unix() < activeDate.Unix() {
+				delete(originalMapData, k)
+				continue
+			}
+		}
+		v.SumAvg()
+	}
+
+	if len(originalMapData) <= 0 {
+		return res
+	}
+	res = model.NeckRingOriginalMap(originalMapData).ForMatData()
+	sort.Sort(model.NeckActiveHabitSlice(res))
+	return res
+}
+
+func (e *Entry) againRecalculate(data *model.NeckActiveHabit) *model.NeckActiveHabit {
+	originalList := make([]*model.NeckRingOriginal, 0)
+	frameIds := util.FrameIds(data.Frameid)
+	if err := e.DB.Model(new(model.NeckRingOriginal)).
+		Where("pasture_id = ?", data.PastureId).
+		Where("neck_ring_number = ?", data.NeckRingNumber).
+		Where("active_date = ?", data.HeatDate).
+		Where("frameid IN (?)", frameIds).
+		Find(&originalList).Error; err != nil {
+		return nil
+	}
+
+	newDataList := e.recalculate(originalList)
+	if len(newDataList) != 1 {
+		return nil
+	}
+	res := newDataList[0]
+	res.IsShow = pasturePb.IsShow_No
+	return res
+}
+
 // computeIfPositiveElse 辅助函数来计算过滤值
 func computeIfPositiveElse(newValue, prevFilterValue float64, weightPrev, weightNew float64) float64 {
 	return math.Ceil((prevFilterValue * weightPrev) + (weightNew * newValue))

+ 1 - 1
module/crontab/sql.go

@@ -116,7 +116,7 @@ func (e *Entry) IsExistNeckActiveHabit(neckRingNumber, heatDate string, frameId
 	return neckActiveHabit, count
 }
 
-func (e *Entry) GetSystemConfigure(pastureId int64) ([]*model.NeckRingConfigure, error) {
+func (e *Entry) GetSystemNeckRingConfigure(pastureId int64) ([]*model.NeckRingConfigure, error) {
 	res := make([]*model.NeckRingConfigure, 0)
 	if err := e.DB.Model(new(model.NeckRingConfigure)).
 		Where("pasture_id = ?", pastureId).

+ 289 - 0
module/crontab/warning_handle.go

@@ -0,0 +1,289 @@
+package crontab
+
+import (
+	"fmt"
+	"kpt-pasture/model"
+	"sort"
+	"time"
+
+	pasturePb "gitee.com/xuyiping_admin/go_proto/proto/go/backend/cow"
+	"gitee.com/xuyiping_admin/pkg/logger/zaplog"
+	"gitee.com/xuyiping_admin/pkg/xerr"
+	"go.uber.org/zap"
+)
+
+func (e *Entry) UpdateNeckRingWarning() (err error) {
+	pastureList := e.FindPastureList()
+	if pastureList == nil || len(pastureList) == 0 {
+		return nil
+	}
+
+	for _, pasture := range pastureList {
+		// 先删除历史数据
+		if err = e.DB.Model(new(model.NeckRingEstrusWarning)).
+			Where("pasture_id = ?", pasture.Id).
+			Delete(new(model.NeckRingEstrusWarning)).Error; err != nil {
+			zaplog.Error("UpdateNeckRingWarning", zap.Any("delete", err), zap.Any("pasture", pasture))
+		}
+		if err = e.NeckRingWarning(pasture.Id); err != nil {
+			zaplog.Error("EntryCrontab", zap.Any("NeckRingWarning", err), zap.Any("pasture", pasture))
+		}
+		zaplog.Info("UpdateNeckRingWarning-success", zap.Any("pasture", pasture.Id))
+	}
+	return nil
+}
+
+func (e *Entry) NeckRingWarning(pastureId int64) (err error) {
+	// 计算时间范围
+	now := time.Now()
+	startTime := now.Add(-24 * time.Hour)
+	neckRingEstrusList := make([]*model.NeckRingEstrus, 0)
+	if err = e.DB.Table(fmt.Sprintf("%s as a", new(model.NeckRingEstrus).TableName())).
+		Select("a.*").
+		Joins("JOIN cow as b ON a.cow_id = b.id AND a.pasture_id = b.pasture_id").
+		Where("a.pasture_id = ?", pastureId).
+		Where("a.active_date >= ?", startTime.Format(model.LayoutTime)).
+		Where("a.active_level >= ?", pasturePb.EstrusLevel_Low).
+		Where("a.check_result IN (?)", []pasturePb.CheckResult_Kind{pasturePb.CheckResult_Pending, pasturePb.CheckResult_Correct}).
+		Where("a.is_show = ?", pasturePb.IsShow_Ok).
+		Where("a.is_peak >= ?", pasturePb.IsShow_Ok).
+		Where("b.admission_status = ?", pasturePb.AdmissionStatus_Admission).
+		Find(&neckRingEstrusList).Error; err != nil {
+		return xerr.WithStack(err)
+	}
+
+	if len(neckRingEstrusList) == 0 {
+		return nil
+	}
+	neckRingEstrusWarningList := e.GroupAndProcessData(neckRingEstrusList)
+	if len(neckRingEstrusWarningList) > 0 {
+		if err = e.DB.CreateInBatches(neckRingEstrusWarningList, 50).Error; err != nil {
+			return xerr.WithStack(err)
+		}
+	} else {
+		return nil
+	}
+
+	minId := e.getMinId(pastureId)
+	estrusWarningList, err := e.GetCowHighChange(pastureId, minId)
+	if err != nil {
+		return xerr.WithStack(err)
+	}
+
+	for _, v := range estrusWarningList {
+		neckRingEstrusWarning := &model.NeckRingEstrusWarning{}
+		if err = e.DB.Model(new(model.NeckRingEstrusWarning)).
+			Where("neck_ring_estrus_id = ?", v.NeckRingEstrusId).
+			Find(neckRingEstrusWarning).Error; err != nil {
+			zaplog.Error("UpdateNeckRingWarning", zap.Any("Find", err), zap.Any("v", v))
+			continue
+		}
+
+		warningKind := pasturePb.Warning_Estrus
+		if v.Nb1 <= model.MinNb1 {
+			count := e.getCowHigh(pastureId, v.CowId, minId, v.DateTime)
+			if count <= 0 {
+				if err = e.DB.Model(new(model.NeckRingEstrusWarning)).
+					Where("neck_ring_estrus_id = ?", v.NeckRingEstrusId).
+					Where("cow_id = ?", v.CowId).
+					Where("pasture_id = ?", pastureId).Delete(new(model.NeckRingEstrusWarning)).Error; err != nil {
+					zaplog.Error("UpdateNeckRingWarning", zap.Any("Delete", err), zap.Any("v", v))
+				}
+				continue
+			}
+		}
+
+		if err = e.DB.Model(new(model.NeckRingEstrusWarning)).
+			Where("neck_ring_estrus_id = ?", v.NeckRingEstrusId).
+			Updates(map[string]interface{}{
+				"warning_kind": warningKind,
+				"high_change":  v.HighChange,
+			}).Error; err != nil {
+			zaplog.Error("UpdateNeckRingWarning", zap.Any("Updates", err), zap.Any("v", v))
+			continue
+		}
+
+	}
+
+	if err = e.UpdateNeckRingWarningIsPeak(pastureId, minId); err != nil {
+		zaplog.Error("UpdateNeckRingWarning", zap.Any("UpdateNeckRingWarningIsPeak", err), zap.Any("pastureId", pastureId))
+	}
+
+	return nil
+}
+
+func (e *Entry) UpdateNeckRingWarningIsPeak(pastureId, minId int64) error {
+	sqlQuery := e.DB.Table(fmt.Sprintf("%s as a", new(model.NeckActiveHabit).TableName())).
+		Select("1").
+		Where("a.id >= ?", minId).
+		Where("a.cow_id = b.cow_id").
+		Where("a.created_at > UNIX_TIMESTAMP(b.date_time)")
+
+	if err := e.DB.Table(fmt.Sprintf("%s as b", new(model.NeckRingEstrusWarning).TableName())).
+		Where("b.pasture_id = ?", pastureId).
+		Where("EXISTS (?)", sqlQuery).Update("is_peak", pasturePb.IsShow_Ok).Error; err != nil {
+		return xerr.WithStack(err)
+	}
+	return nil
+}
+
+func (e *Entry) GroupAndProcessData(records []*model.NeckRingEstrus) []*model.NeckRingEstrusWarning {
+	groups := make(map[int64]*model.GroupEstrusData)
+
+	// 分组处理
+	for _, record := range records {
+		// 从关联的Cow表获取信息(这里需要根据实际关联方式调整)
+		// 假设已通过JOIN获取到CowID等信息
+		// 实际实现可能需要预加载Cow信息
+		key := record.CowId
+		if _, exist := groups[key]; !exist {
+			groups[key] = &model.GroupEstrusData{
+				CowId:     record.CowId,
+				PastureId: record.PastureId,
+				Records:   make([]*model.NeckRingEstrus, 0),
+				EarNumber: record.EarNumber,
+				Moved:     false,
+			}
+		}
+
+		// 检查是否在移牛列表中
+		// 假设通过CowID判断,需要根据实际关联关系调整
+		/*if _, moved := movedCows[record.CowId]; moved {
+			groups[key].Moved = true
+		}*/
+		groups[key].Records = append(groups[key].Records, record)
+	}
+
+	// 处理每个分组
+	var neckRingEstrusWarningList []*model.NeckRingEstrusWarning
+	for _, group := range groups {
+		if len(group.Records) == 0 {
+			continue
+		}
+
+		// 排序记录
+		sort.Slice(group.Records, func(i, j int) bool {
+			return group.Records[i].Id > group.Records[j].Id
+		})
+
+		// 计算字段
+		latest := group.Records[0]
+		maxId := findMaxId(group.Records)
+		firstTime := findMaxTime(group.Records, func(r *model.NeckRingEstrus) string { return r.EstrusStartDate })
+		dateTime := findMaxTime(group.Records, func(r *model.NeckRingEstrus) string { return r.ActiveDate })
+		neckRingEstrusWarning := model.NewNeckRingEstrusWarning(
+			maxId, group.PastureId, group.CowId, group.EarNumber,
+			firstTime, dateTime, latest.LastEstrusDate,
+			pasturePb.Warning_Estrus, latest.ActiveLevel,
+		)
+
+		neckRingEstrusWarningList = append(neckRingEstrusWarningList, neckRingEstrusWarning)
+	}
+	return neckRingEstrusWarningList
+}
+
+func (e *Entry) GetCowHighChange(pastureId, minId int64) ([]*model.EstrusWarning, error) {
+	nowTime := time.Now().Add(-48 * time.Hour)
+	estrusWarningList := make([]*model.EstrusWarning, 0)
+	if err := e.DB.Table(fmt.Sprintf("%s as a", new(model.NeckActiveHabit).TableName())).
+		Select(
+			"GROUP_CONCAT( IF(ROUND(a.change_filter*a.filter_correct/100,0)=-99,'',ROUND(a.change_filter*a.filter_correct/100,0)) ) as high_change",
+			"b.neck_ring_estrus_id", "b.cow_id", "b.date_time", "COUNT(a.change_filter>-99) as nb1",
+			"COUNT(a.change_filter=-99 AND a.created_at>=(STR_TO_DATE(b.date_time,'%Y-%m-%d %H:%i:%s') -INTERVAL 48 HOUR )) as nb2").
+		Joins("JOIN neck_ring_estrus_warning as b ON a.cow_id = b.cow_id AND a.pasture_id = b.pasture_id").
+		Where("a.id > ?", minId).
+		Where("a.pasture_id = ?", pastureId).
+		Where("a.created_at > ?", nowTime.Unix()).
+		Group("b.neck_ring_estrus_id").
+		Having("nb1 <= ? AND nb2 >=", model.Nb1, model.Nb2).
+		Find(&estrusWarningList).
+		Error; err != nil {
+		return nil, xerr.WithStack(err)
+	}
+	return estrusWarningList, nil
+}
+
+// getRecentMovedCows 辅助函数:检查是否在移牛事件
+func (e *Entry) getRecentMovedCows(pastureId, cowId int64) bool {
+	var count int64
+	startDate := time.Now().AddDate(0, 0, -2)
+	table := &model.EventCowLog{CowId: cowId}
+	if err := e.DB.Table(table.TableName()).
+		Where("cow_id = ?", cowId).
+		Where("pasture = ?", pastureId).
+		Where("event_type = ?", pasturePb.EventType_Transfer_Ben).
+		Where("event_at >= ?", startDate.Unix()).
+		Count(&count).Error; err != nil {
+		return false
+	}
+
+	if count > 0 {
+		return true
+	}
+	return false
+}
+
+func (e *Entry) getMinId(pastureId int64) int64 {
+	var minId int64
+	nowTime := time.Now().AddDate(0, 0, -2).Format(model.LayoutDate2)
+	if err := e.DB.Model(new(model.NeckActiveHabit)).
+		Select("MIN(id) as id").
+		Where("heat_date = ?", nowTime).
+		Where("pasture_id = ?", pastureId).
+		Scan(&minId).Error; err != nil {
+		return 1
+	}
+	return minId
+}
+
+func (e *Entry) getCowHigh(pastureId, cowId, minId int64, dateTime string) int64 {
+	dateTimeUnix, _ := time.Parse(model.LayoutTime, dateTime)
+	dateTimeUnixStart := time.Time{}
+	if dateTimeUnix.IsZero() {
+		dateTimeUnixStart = time.Now().Add(-22 * time.Hour)
+	} else {
+		dateTimeUnixStart = dateTimeUnix.Add(-22 * time.Hour)
+	}
+
+	var count int64
+	if err := e.DB.Model(new(model.NeckActiveHabit)).
+		Select("COUNT(0) as count").
+		Where("created_at BETWEEN ? AND ?", dateTimeUnixStart.Unix(), dateTimeUnix).
+		Where("pasture_id = ?", pastureId).
+		Where("cow_id = ?", cowId).
+		Where("id >= ?", minId).
+		Scan(&count).Error; err != nil {
+		return 0
+	}
+
+	return count
+}
+
+// 辅助函数:计算最大时间
+func findMaxTime(records []*model.NeckRingEstrus, getter func(*model.NeckRingEstrus) string) string {
+	var max time.Time
+	for _, r := range records {
+		t1 := getter(r)
+		if t1 == "" {
+			continue
+		}
+		t, err := time.Parse(model.LayoutTime, t1)
+		if err != nil {
+			continue
+		}
+		if t.After(max) {
+			max = t
+		}
+	}
+	return max.Format(model.LayoutTime)
+}
+
+func findMaxId(records []*model.NeckRingEstrus) int64 {
+	maxId := int64(0)
+	for _, v := range records {
+		if v.Id > maxId {
+			maxId = v.Id
+		}
+	}
+	return maxId
+}

+ 6 - 33
module/mqtt/mqtt_handle.go

@@ -7,7 +7,6 @@ import (
 	"strconv"
 	"strings"
 	"sync"
-	"time"
 
 	"github.com/jinzhu/copier"
 
@@ -27,9 +26,7 @@ var (
 		NeckRingOriginalData: make([]*model.NeckRingOriginal, 0),
 		NeckRingErrorData:    make([]*model.NeckRingError, 0),
 	}
-	pastureMqttMap       = make(map[string]int64)
-	isFindPastureMqttMap bool
-	mu                   sync.Mutex
+	mu sync.Mutex
 )
 
 func (e *Entry) NeckRingHandle(data []byte) {
@@ -45,21 +42,6 @@ func (e *Entry) NeckRingHandle(data []byte) {
 	}
 }
 
-func (e *Entry) FindPastureMqttMap() map[string]int64 {
-	if isFindPastureMqttMap {
-		return pastureMqttMap
-	}
-	appMqttList := make([]*model.NeckRing, 0)
-	if err := e.DB.Model(new(model.AppMqtt)).Find(&appMqttList).Error; err != nil {
-		zaplog.Error("FindPastureMqttMap", zap.Any("err", err))
-	}
-	for _, v := range appMqttList {
-		pastureMqttMap[v.NeckRingNumber] = v.PastureId
-	}
-	isFindPastureMqttMap = true
-	return pastureMqttMap
-}
-
 // 处理批量数据
 func (e *Entry) processBatch(batchList []*model.NeckRingOriginal) {
 	// 初始化分类数据
@@ -101,25 +83,17 @@ func (e *Entry) processBatch(batchList []*model.NeckRingOriginal) {
 func (e *Entry) CreatedData(DSMLog *DataInsertNeckRingLog) error {
 	if err := e.DB.Transaction(func(tx *gorm.DB) error {
 		if len(DSMLog.NeckRingErrorData) > 0 {
-			if err := e.DB.Model(new(model.NeckRingError)).Create(DSMLog.NeckRingErrorData).Error; err != nil {
+			if err := e.DB.Model(new(model.NeckRingError)).
+				Create(DSMLog.NeckRingErrorData).Error; err != nil {
 				return xerr.WithStack(err)
 			}
 		}
 
 		if len(DSMLog.NeckRingOriginalData) > 0 {
 			// 批量写入数据
-			if len(DSMLog.NeckRingOriginalData) > 50 {
-				if err := e.DB.Model(new(model.NeckRingOriginal)).Create(DSMLog.NeckRingOriginalData[:50]).Error; err != nil {
-					return xerr.WithStack(err)
-				}
-				time.Sleep(100 * time.Millisecond)
-				if err := e.DB.Model(new(model.NeckRingOriginal)).Create(DSMLog.NeckRingOriginalData[50:]).Error; err != nil {
-					return xerr.WithStack(err)
-				}
-			} else {
-				if err := e.DB.Model(new(model.NeckRingOriginal)).Create(DSMLog.NeckRingOriginalData).Error; err != nil {
-					return xerr.WithStack(err)
-				}
+			if err := e.DB.Model(new(model.NeckRingOriginal)).
+				CreateInBatches(DSMLog.NeckRingOriginalData, 50).Error; err != nil {
+				return xerr.WithStack(err)
 			}
 		}
 		return nil
@@ -143,7 +117,6 @@ func (e *Entry) MsgDataFormat2(msg []byte) *DataInsertNeckRingLog {
 
 	normalOriginal := make([]*model.NeckRingOriginal, 0)
 	errorOriginal := make([]*model.NeckRingError, 0)
-	pastureMqttMap = e.FindPastureMqttMap()
 
 	for _, neckLog := range neckLogList.NeckRing.NeckPck {
 		newOriginal := model.NewNeckRingOriginal(neckLog)

+ 570 - 2
util/util_test.go

@@ -508,6 +508,574 @@ func TestGetNeckRingActiveTimer(t *testing.T) {
 
 func Test_demo(t *testing.T) {
 
-	a := []int32{}
-	fmt.Println(ArrayInt32ToStrings(a, ","))
+	a := []int32{
+		211670,
+		9121372,
+		3204736,
+		3212694,
+		3204532,
+		3214082,
+		9121667,
+		3212275,
+		3210345,
+		3217879,
+		9422,
+		3206975,
+		9496,
+		3204907,
+		212194,
+		3211061,
+		9120840,
+		3207787,
+		3210201,
+		404,
+		3207104,
+		3219173,
+		3216771,
+		3216872,
+		3209614,
+		3205455,
+		416,
+		9121026,
+		211622,
+		3207868,
+		3210340,
+		9120132,
+		3207092,
+		3209390,
+		3205872,
+		3207367,
+		3219700,
+		9120022,
+		211246,
+		466,
+		3207565,
+		3208779,
+		3204863,
+		3207963,
+		3204259,
+		3207966,
+		2640,
+		3214316,
+		3205778,
+		3206897,
+		3207745,
+		3209052,
+		3208045,
+		211627,
+		3212873,
+		2100766,
+		9121950,
+		3206076,
+		3206438,
+		9466,
+		9121195,
+		9122044,
+		3209430,
+		3205599,
+		2100794,
+		3048,
+		9467,
+		3207480,
+		3216011,
+		9121725,
+		9120340,
+		9121597,
+		427,
+		3209387,
+		3209490,
+		3214311,
+		3206044,
+		211253,
+		3207553,
+		3215616,
+		211350,
+		3207551,
+		3205896,
+		417,
+		9121008,
+		3207694,
+		3209372,
+		3217873,
+		3207227,
+		3207568,
+		3210615,
+		3204769,
+		3216095,
+		9121338,
+		3209124,
+		211340,
+		3219695,
+		9121802,
+		3205517,
+		3210676,
+		9123325,
+		3204328,
+		9409,
+		211349,
+		3208860,
+		9121769,
+		3209221,
+		3210916,
+		3205880,
+		438,
+		3205557,
+		9421,
+		211229,
+		444,
+		9123260,
+		9121464,
+		3212818,
+		3204834,
+		3205408,
+		3207486,
+		498,
+		3206232,
+		3206315,
+		2100759,
+		9121955,
+		3204338,
+		3207606,
+		3208767,
+		450,
+		9123330,
+		9121278,
+		9121011,
+		9122280,
+		479,
+		434,
+		3207614,
+		2355,
+		211644,
+		453,
+		9121308,
+		3209449,
+		405,
+		3204240,
+		9120165,
+		9445,
+		9120456,
+		9123057,
+		471,
+		3206830,
+		464,
+		403,
+		2100749,
+		3218278,
+		3218600,
+		212168,
+		9470,
+		428,
+		3205448,
+		3209790,
+		3208163,
+		213134,
+		3207603,
+		3206394,
+		3204476,
+		9121569,
+		3206447,
+		3209456,
+		9120533,
+		3209044,
+		3214232,
+		3209337,
+		212193,
+		3216684,
+		3218450,
+		3207194,
+		3204853,
+		3205858,
+		3207922,
+		3204645,
+		212157,
+		3214707,
+		213126,
+		3219468,
+		9120766,
+		3218611,
+		3215948,
+		3211568,
+		9120227,
+		211348,
+		3212621,
+		3209898,
+		3207637,
+		9121071,
+		9123113,
+		3206491,
+		3209922,
+		9440,
+		3207075,
+		423,
+		3204875,
+		9121888,
+		3210395,
+		9120630,
+		9120214,
+		3208166,
+		211285,
+		3206880,
+		3209461,
+		3205597,
+		3216262,
+		3204942,
+		9123224,
+		3205918,
+		3204889,
+		3209047,
+		3207024,
+		3207277,
+		3207699,
+		3205771,
+		9500,
+		3204450,
+		3205495,
+		9483,
+		3209876,
+		3205533,
+		3216075,
+		9469,
+		3209827,
+		9122454,
+		413,
+		3216308,
+		3219301,
+		3210042,
+		3205064,
+		211390,
+		9121305,
+		3218596,
+		3204955,
+		3215274,
+		212171,
+		3209460,
+		3211376,
+		3213812,
+		3205184,
+		3209287,
+		3211769,
+		3205926,
+		3216585,
+		3213332,
+		3209340,
+		9123175,
+		3205843,
+		3207663,
+		3210829,
+		3209481,
+		9450,
+		9463,
+		3209763,
+		3215627,
+		9121424,
+		3212746,
+		3218533,
+		3209072,
+		3207359,
+		3205888,
+		3214228,
+		3204884,
+		3218613,
+		3209352,
+		3208192,
+		3216211,
+		3211454,
+		3217082,
+		3212728,
+		3206984,
+		3217750,
+		3213406,
+		3206305,
+		9122130,
+		9121695,
+		9492,
+		3207856,
+		3218263,
+		9121058,
+		9123309,
+		9122466,
+		9122287,
+		9120614,
+		3084,
+		3205559,
+		3205840,
+		9121444,
+		9121777,
+		3209308,
+		9122313,
+		9121672,
+		3210180,
+		3207645,
+		3206419,
+		9413,
+		211576,
+		3209201,
+		2601,
+		3211708,
+		3206969,
+		3206871,
+		3210430,
+		211674,
+		9122441,
+		3214386,
+		3206927,
+		3209332,
+		9462,
+		3206430,
+		3207485,
+		3204519,
+		3216214,
+		9123371,
+		9120847,
+		9123355,
+		211690,
+		3210362,
+		3218862,
+		3213687,
+		3066,
+		3209787,
+		9120359,
+		9468,
+		2315,
+		3207031,
+		211353,
+		211250,
+		3207688,
+		9122447,
+		3218688,
+		445,
+		3205848,
+		3214915,
+		3208866,
+		1739,
+		3204990,
+		2100716,
+		212130,
+		3204214,
+		3208985,
+		211388,
+		9123166,
+		3208856,
+		211648,
+		2311,
+		3204411,
+		3217860,
+		9523,
+		9524,
+		3209134,
+		3209212,
+		211630,
+		9123151,
+		3207375,
+		441,
+		9525,
+		3205815,
+		3205527,
+		3215344,
+		3207185,
+		211332,
+		3217466,
+		9526,
+		1783,
+		9122414,
+		3207617,
+		212150,
+		3204248,
+		3216316,
+		3209535,
+		3206873,
+		3208974,
+		9406,
+		9122407,
+		9121925,
+		2351,
+		3214481,
+		3204826,
+		3205906,
+		3205109,
+		3209014,
+		211240,
+		3070,
+		474,
+		3204667,
+		3205511,
+		409,
+		3209061,
+		3205074,
+		3206009,
+		9123010,
+		3207536,
+		3219231,
+		3207198,
+		9475,
+		9423,
+		9121871,
+		3204643,
+		3205471,
+		3206095,
+		9121981,
+		9120835,
+		3206427,
+		3218217,
+		3207493,
+		2100732,
+		3204886,
+		3208174,
+		9486,
+		2100772,
+		3209419,
+		3207087,
+		2328,
+		3207371,
+		3210597,
+		3206231,
+		3206931,
+		3204236,
+		3207411,
+		3206374,
+		3206452,
+		3207472,
+		9429,
+		3218802,
+		211381,
+		9474,
+		3204420,
+		3207026,
+		3206124,
+		3209608,
+		3209330,
+		3209485,
+		3216593,
+		3205921,
+		2375,
+		3204818,
+		3215544,
+		3213632,
+		3216924,
+		3204395,
+		3207111,
+		3206961,
+		212169,
+		9123027,
+		2100744,
+		9520,
+		3208708,
+		3214104,
+		3206329,
+		9512,
+		3211187,
+		3207674,
+		3206004,
+		3207748,
+		3209328,
+		3209033,
+		3205601,
+		3205776,
+		9438,
+		3205269,
+		3204664,
+		9514,
+		3204897,
+		9446,
+		3208827,
+		3209611,
+		3208050,
+		9501,
+		9121120,
+		3213788,
+		9121699,
+		3204409,
+		3210073,
+		3207546,
+		2100758,
+		3206082,
+		3208975,
+		9122051,
+		9518,
+		3207751,
+		9408,
+		3206437,
+		3207343,
+		3207021,
+		3216998,
+		3205349,
+		3214744,
+		3205905,
+		3212646,
+		3209740,
+		3206239,
+		3207473,
+		3207236,
+		3209730,
+		3204360,
+		3206895,
+		9120696,
+		3204901,
+		9508,
+		3207935,
+		3213977,
+		3214166,
+		448,
+		3205893,
+		3212052,
+		3205552,
+		3211112,
+		3213551,
+		3217077,
+		3206322,
+		9465,
+		3208807,
+		3205211,
+		3215051,
+		3207121,
+		3215725,
+		3207229,
+		3219426,
+		3213345,
+		3217541,
+		3216876,
+		3215675,
+		3214245,
+		3207012,
+		3218806,
+		3217024,
+		3212486,
+		3207620,
+		3211045,
+		3213712,
+		3215268,
+		3215061,
+		3209099,
+		3218820,
+		3212817,
+		3204659,
+		3210976,
+		3213266,
+		3211716,
+		3207860,
+		3213350,
+		9517,
+		3215001,
+		3217667,
+		3213039,
+		3207248,
+		9510,
+		3215056,
+		3218732,
+		3216053,
+		3209995,
+		3215773,
+		3211035,
+		3213295,
+		3205725,
+		9464,
+		3205636}
+
+	value := ``
+	for _, v := range a {
+		value += fmt.Sprintf(`"%d",`, v)
+	}
+
+	fmt.Println(value)
 }