Przeglądaj źródła

event: enter update

Yi 6 dni temu
rodzic
commit
4a58897a5a
48 zmienionych plików z 814 dodań i 392 usunięć
  1. 1 0
      config/app.develop.yaml
  2. 1 0
      config/app.go
  3. 24 31
      dep/di_crontab.go
  4. 4 2
      go.mod
  5. 4 126
      go.sum
  6. 7 1
      http/handler/analysis/analysis.go
  7. 2 2
      http/handler/event/event_base.go
  8. 28 2
      http/handler/event/event_breed.go
  9. 22 3
      http/handler/pasture/pasture.go
  10. 1 0
      http/route/event_api.go
  11. 1 0
      http/route/pasture_api.go
  12. 1 1
      model/calendar.go
  13. 24 1
      model/cow.go
  14. 15 4
      model/event_cow_same_time.go
  15. 1 0
      model/event_death.go
  16. 7 0
      model/event_estrus.go
  17. 39 11
      model/event_sale.go
  18. 1 1
      model/neck_active_habit.go
  19. 35 6
      model/neck_ring.go
  20. 13 11
      model/neck_ring_bind_log.go
  21. 2 0
      model/neck_ring_estrus.go
  22. 3 3
      model/pen_behavior.go
  23. 16 3
      module/backend/analysis.go
  24. 7 5
      module/backend/analysis_other.go
  25. 61 24
      module/backend/calendar.go
  26. 13 8
      module/backend/calendar_more.go
  27. 1 1
      module/backend/config_data.go
  28. 22 12
      module/backend/config_data_breed.go
  29. 17 3
      module/backend/cow.go
  30. 16 0
      module/backend/enum_map.go
  31. 1 1
      module/backend/enum_options.go
  32. 0 3
      module/backend/event_base.go
  33. 95 20
      module/backend/event_base_more.go
  34. 149 11
      module/backend/event_breed.go
  35. 3 1
      module/backend/event_breed_more_more.go
  36. 29 19
      module/backend/event_check.go
  37. 30 24
      module/backend/goods.go
  38. 5 2
      module/backend/interface.go
  39. 1 3
      module/backend/neck_ring_warning.go
  40. 24 0
      module/backend/pasture.go
  41. 21 5
      module/backend/sql.go
  42. 2 2
      module/backend/test_service.go
  43. 1 0
      module/crontab/interface.go
  44. 31 0
      module/crontab/milk_daily.go
  45. 24 0
      module/crontab/neck_ring_calculate.go
  46. 8 22
      module/crontab/neck_ring_estrus.go
  47. 1 1
      module/mqtt/sql.go
  48. 0 17
      util/util_test.go

+ 1 - 0
config/app.develop.yaml

@@ -54,6 +54,7 @@ cron:
   update_pen_behavior: "0 45 * * * ?"  # 更新栏舍行行为数据
   update_pen_behavior_daily: "0 05 2 * * ?"  # 更新栏舍饲养监测数据
   update_milk_original: "0 */30 * * * ?"     # 更新奶厅原始数据(每30分钟执行一次
+  insert_milk_daily: "0 05 2 * * ?"          # 更新奶厅日数据
 
 mqtt:
   broker: "kptyun.com:1983"

+ 1 - 0
config/app.go

@@ -70,6 +70,7 @@ type CronSetting struct {
 	UpdatePenBehavior       string `yaml:"update_pen_behavior"`        //  栏舍行为数据
 	UpdatePenBehaviorDaily  string `yaml:"update_pen_behavior_daily"`  //  栏舍饲养监测
 	UpdateMilkOriginal      string `yaml:"update_milk_original"`       //  奶厅原始数据更新
+	InsertMilkDaily         string `yaml:"insert_milk_daily"`          //  牛只每日奶量数据
 }
 
 type JwtTokenKeyConfig struct {

+ 24 - 31
dep/di_crontab.go

@@ -45,90 +45,83 @@ func EntryCrontab(dependency CrontabDependency) *cron.Crontab {
 	cs := cfg.CronSetting
 
 	newCrontab := cron.NewCrontab(DataCenterCrontabCounterVec)
-	err := newCrontab.Bind("indicators", cs.Indicators, dependency.CrontabHub.Indicators)
-	if err != nil {
+	if err := newCrontab.Bind("indicators", cs.Indicators, dependency.CrontabHub.Indicators); err != nil {
 		panic(err)
 	}
 
-	err = newCrontab.Bind("UpdateCowInfo", cs.UpdateCowInfo, dependency.CrontabHub.UpdateCowInfo)
-	if err != nil {
+	if err := newCrontab.Bind("UpdateCowInfo", cs.UpdateCowInfo, dependency.CrontabHub.UpdateCowInfo); err != nil {
 		panic(err)
 	}
 
-	err = newCrontab.Bind("ImmunizationPlan", cs.ImmunizationPlan, dependency.CrontabHub.ImmunizationPlan)
-	if err != nil {
+	if err := newCrontab.Bind("ImmunizationPlan", cs.ImmunizationPlan, dependency.CrontabHub.ImmunizationPlan); err != nil {
 		panic(err)
 	}
 
-	err = newCrontab.Bind("SameTimePlan", cs.SameTimePlan, dependency.CrontabHub.SameTimePlan)
-	if err != nil {
+	if err := newCrontab.Bind("SameTimePlan", cs.SameTimePlan, dependency.CrontabHub.SameTimePlan); err != nil {
 		panic(err)
 	}
 
-	err = newCrontab.Bind("UpdateSameTime", cs.UpdateSameTime, dependency.CrontabHub.UpdateSameTime)
-	if err != nil {
+	if err := newCrontab.Bind("UpdateSameTime", cs.UpdateSameTime, dependency.CrontabHub.UpdateSameTime); err != nil {
 		panic(err)
 	}
 
-	err = newCrontab.Bind("SystemBasicCrontab", cs.SystemBasicCrontab, dependency.CrontabHub.SystemBasicCrontab)
-	if err != nil {
+	if err := newCrontab.Bind("SystemBasicCrontab", cs.SystemBasicCrontab, dependency.CrontabHub.SystemBasicCrontab); err != nil {
 		panic(err)
 	}
 
-	err = newCrontab.Bind("DeleteOldOriginal", cs.DeleteOldOriginal, dependency.CrontabHub.DeleteOldOriginal)
-	if err != nil {
+	if err := newCrontab.Bind("DeleteOldOriginal", cs.DeleteOldOriginal, dependency.CrontabHub.DeleteOldOriginal); err != nil {
 		panic(err)
 	}
 
-	err = newCrontab.Bind("UpdateDiseaseToCalendar", cs.UpdateDiseaseToCalendar, dependency.CrontabHub.UpdateDiseaseToCalendar)
-	if err != nil {
+	if err := newCrontab.Bind("UpdateDiseaseToCalendar", cs.UpdateDiseaseToCalendar, dependency.CrontabHub.UpdateDiseaseToCalendar); err != nil {
 		zaplog.Error("EntryCrontab", zap.Any("UpdateCowEstrus", err))
 		panic(err)
 	}
 
-	err = newCrontab.Bind("UpdateCowEstrus", cs.NeckRingEstrus, dependency.CrontabHub.UpdateCowEstrus)
-	if err != nil {
+	if err := newCrontab.Bind("UpdateCowEstrus", cs.NeckRingEstrus, dependency.CrontabHub.UpdateCowEstrus); err != nil {
 		zaplog.Error("EntryCrontab", zap.Any("UpdateCowEstrus", err))
 		panic(err)
 	}
 
-	err = newCrontab.Bind("NeckRingCalculate", cs.NeckRingCalculate, dependency.CrontabHub.NeckRingCalculate)
-	if err != nil {
+	if err := newCrontab.Bind("NeckRingCalculate", cs.NeckRingCalculate, dependency.CrontabHub.NeckRingCalculate); err != nil {
 		zaplog.Error("EntryCrontab", zap.Any("NeckRingCalculate", err))
 		panic(err)
 	}
 
-	err = newCrontab.Bind("NeckRingMerge", cs.NeckRingMerge, dependency.CrontabHub.NeckRingOriginalMerge)
-	if err != nil {
+	if err := newCrontab.Bind("NeckRingMerge", cs.NeckRingMerge, dependency.CrontabHub.NeckRingOriginalMerge); err != nil {
 		zaplog.Error("EntryCrontab", zap.Any("NeckRingOriginalMergeData", err))
 		panic(err)
 	}
 
-	err = newCrontab.Bind("NeckRingEstrusWarning", cs.NeckRingEstrusWarning, dependency.CrontabHub.NeckRingEstrusWarning)
-	if err != nil {
+	if err := newCrontab.Bind("NeckRingEstrusWarning", cs.NeckRingEstrusWarning, dependency.CrontabHub.NeckRingEstrusWarning); err != nil {
 		zaplog.Error("EntryCrontab", zap.Any("NeckRingEstrusWarning", err))
 		panic(err)
 	}
 
-	err = newCrontab.Bind("NeckRingHealthWarning", cs.NeckRingHealthWarning, dependency.CrontabHub.NeckRingHealthWarning)
-	if err != nil {
+	if err := newCrontab.Bind("NeckRingHealthWarning", cs.NeckRingHealthWarning, dependency.CrontabHub.NeckRingHealthWarning); err != nil {
 		zaplog.Error("EntryCrontab", zap.Any("NeckRingHealthWarning", err))
 		panic(err)
 	}
 
-	err = newCrontab.Bind("UpdatePenBehavior", cs.UpdatePenBehavior, dependency.CrontabHub.UpdatePenBehavior)
-	if err != nil {
+	if err := newCrontab.Bind("UpdatePenBehavior", cs.UpdatePenBehavior, dependency.CrontabHub.UpdatePenBehavior); err != nil {
 		zaplog.Error("EntryCrontab", zap.Any("UpdatePenBehavior", err))
 		panic(err)
 	}
 
-	err = newCrontab.Bind("UpdatePenBehaviorDaily", cs.UpdatePenBehaviorDaily, dependency.CrontabHub.UpdatePenBehaviorDaily)
-	if err != nil {
+	if err := newCrontab.Bind("UpdatePenBehaviorDaily", cs.UpdatePenBehaviorDaily, dependency.CrontabHub.UpdatePenBehaviorDaily); err != nil {
 		zaplog.Error("EntryCrontab", zap.Any("UpdatePenBehaviorDaily", err))
 		panic(err)
 	}
 
-	err = newCrontab.Bind("UpdateMilkOriginal", cs.UpdateMilkOriginal, dependency.CrontabHub.UpdateMilkOriginal)
+	if err := newCrontab.Bind("UpdateMilkOriginal", cs.UpdateMilkOriginal, dependency.CrontabHub.UpdateMilkOriginal); err != nil {
+		zaplog.Error("EntryCrontab", zap.Any("UpdateMilkOriginal", err))
+		panic(err)
+	}
+
+	if err := newCrontab.Bind("UpdateActiveHabit", cs.InsertMilkDaily, dependency.CrontabHub.InsertMilkDaily); err != nil {
+		zaplog.Error("EntryCrontab", zap.Any("InsertMilkDaily", err))
+		panic(err)
+	}
 
 	/*err = newCrontab.Bind("GenerateWorkOrder", cs.GenerateWorkOrder, dependency.CrontabHub.GenerateAsynqWorkOrder)
 	if err != nil {

+ 4 - 2
go.mod

@@ -3,7 +3,7 @@ module kpt-pasture
 go 1.17
 
 require (
-	gitee.com/xuyiping_admin/go_proto v0.0.0-20250410061346-7010a8affda4
+	gitee.com/xuyiping_admin/go_proto v0.0.0-20250412093307-d7a38b54919d
 	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
@@ -33,7 +33,9 @@ require (
 )
 
 require (
+	github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2 // indirect
 	github.com/gorilla/websocket v1.5.3 // indirect
+	github.com/nyaruka/phonenumbers v1.1.7 // indirect
 	github.com/patrickmn/go-cache v2.1.0+incompatible // indirect
 	golang.org/x/arch v0.3.0 // indirect
 )
@@ -86,7 +88,7 @@ require (
 	github.com/richardlehane/msoleps v1.0.3 // indirect
 	github.com/robfig/cron v1.2.0 // indirect
 	github.com/robfig/cron/v3 v3.0.1 // indirect
-	github.com/sirupsen/logrus v1.9.3 // indirect
+	github.com/sirupsen/logrus v1.9.3
 	github.com/spf13/afero v1.9.5 // indirect
 	github.com/spf13/cast v1.5.1 // indirect
 	github.com/spf13/jwalterweatherman v1.1.0 // indirect

+ 4 - 126
go.sum

@@ -36,130 +36,8 @@ cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RX
 cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0=
 cloud.google.com/go/storage v1.14.0/go.mod h1:GrKmX003DSIwi9o29oFT7YDnHYwZoctc3fOKtUw0Xmo=
 dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
-gitee.com/xuyiping_admin/go_proto v0.0.0-20250309141840-4e483354df00 h1:ejRZ6U1vTFjo1VzcHpnraCrqZ6IhkqxM7iMgof2jE90=
-gitee.com/xuyiping_admin/go_proto v0.0.0-20250309141840-4e483354df00/go.mod h1:BKrFW6YLDectlQcQk3FYKBeXvjEiodAKJ5rq7O/QiPE=
-gitee.com/xuyiping_admin/go_proto v0.0.0-20250313095848-b25e11bbb7d0 h1:OqYENfy4l9CwmAqvBqL88K5eAb7CVQC4QHdxCU67du8=
-gitee.com/xuyiping_admin/go_proto v0.0.0-20250313095848-b25e11bbb7d0/go.mod h1:BKrFW6YLDectlQcQk3FYKBeXvjEiodAKJ5rq7O/QiPE=
-gitee.com/xuyiping_admin/go_proto v0.0.0-20250319100822-e116d54d9576 h1:GdTqhxbWyJFWdvf6nfyj7sOhLoXV/T/wdBLJXWwB4C8=
-gitee.com/xuyiping_admin/go_proto v0.0.0-20250319100822-e116d54d9576/go.mod h1:BKrFW6YLDectlQcQk3FYKBeXvjEiodAKJ5rq7O/QiPE=
-gitee.com/xuyiping_admin/go_proto v0.0.0-20250319101442-da4bbb71c8f0 h1:5sTGq6O8p7Wjb79bDGfIthCANaRj7A+/ENzlQa59OR4=
-gitee.com/xuyiping_admin/go_proto v0.0.0-20250319101442-da4bbb71c8f0/go.mod h1:BKrFW6YLDectlQcQk3FYKBeXvjEiodAKJ5rq7O/QiPE=
-gitee.com/xuyiping_admin/go_proto v0.0.0-20250319101753-c158097ddaa7 h1:Ex4WiajO+A5O3vvstrsONguSN0SAyI0tpvn35cP0Olw=
-gitee.com/xuyiping_admin/go_proto v0.0.0-20250319101753-c158097ddaa7/go.mod h1:BKrFW6YLDectlQcQk3FYKBeXvjEiodAKJ5rq7O/QiPE=
-gitee.com/xuyiping_admin/go_proto v0.0.0-20250319122035-8a8593d0cae9 h1:4HUr1cFR5aIFjiiSgyoL0s5zTWwUFC79Jf0JDEyUCgU=
-gitee.com/xuyiping_admin/go_proto v0.0.0-20250319122035-8a8593d0cae9/go.mod h1:BKrFW6YLDectlQcQk3FYKBeXvjEiodAKJ5rq7O/QiPE=
-gitee.com/xuyiping_admin/go_proto v0.0.0-20250320024624-258ecc3b44f0 h1:Mrc0nMlNXGcMkCVzK8+aTXzoB/8t/9VDBuEYsiz8II8=
-gitee.com/xuyiping_admin/go_proto v0.0.0-20250320024624-258ecc3b44f0/go.mod h1:BKrFW6YLDectlQcQk3FYKBeXvjEiodAKJ5rq7O/QiPE=
-gitee.com/xuyiping_admin/go_proto v0.0.0-20250320032539-3a62ca0c9a40 h1:B6D9VigfBV0MX0tB+8ga8GduOSC22ZBcGOomj2jAtaw=
-gitee.com/xuyiping_admin/go_proto v0.0.0-20250320032539-3a62ca0c9a40/go.mod h1:BKrFW6YLDectlQcQk3FYKBeXvjEiodAKJ5rq7O/QiPE=
-gitee.com/xuyiping_admin/go_proto v0.0.0-20250320061610-ddbb79e7b9d3 h1:ZiwSIB+CkDyvPtQRT93EXljd3hem0YExL4KQvlQdhWw=
-gitee.com/xuyiping_admin/go_proto v0.0.0-20250320061610-ddbb79e7b9d3/go.mod h1:BKrFW6YLDectlQcQk3FYKBeXvjEiodAKJ5rq7O/QiPE=
-gitee.com/xuyiping_admin/go_proto v0.0.0-20250320071242-e07b97a9838c h1:Hlx3n1bi+hwO+Z13CMxn8MHpcj5vIiCtntmtiyi4AEA=
-gitee.com/xuyiping_admin/go_proto v0.0.0-20250320071242-e07b97a9838c/go.mod h1:BKrFW6YLDectlQcQk3FYKBeXvjEiodAKJ5rq7O/QiPE=
-gitee.com/xuyiping_admin/go_proto v0.0.0-20250320073403-e107b8471f5f h1:CBWuJ1ZS64rckGk/b4SWBIEviXlAXuSN6uwPaQksh7U=
-gitee.com/xuyiping_admin/go_proto v0.0.0-20250320073403-e107b8471f5f/go.mod h1:BKrFW6YLDectlQcQk3FYKBeXvjEiodAKJ5rq7O/QiPE=
-gitee.com/xuyiping_admin/go_proto v0.0.0-20250320080904-b83f36355fdd h1:yklG8s9PxBhNhPZf3W70FXfmWMLqkXsz/keZWqcoPSk=
-gitee.com/xuyiping_admin/go_proto v0.0.0-20250320080904-b83f36355fdd/go.mod h1:BKrFW6YLDectlQcQk3FYKBeXvjEiodAKJ5rq7O/QiPE=
-gitee.com/xuyiping_admin/go_proto v0.0.0-20250320085006-505f81257a63 h1:hnLXdz5JvaIGD6/HE/k72dm756th4a/392xhbMLdD/o=
-gitee.com/xuyiping_admin/go_proto v0.0.0-20250320085006-505f81257a63/go.mod h1:BKrFW6YLDectlQcQk3FYKBeXvjEiodAKJ5rq7O/QiPE=
-gitee.com/xuyiping_admin/go_proto v0.0.0-20250320093538-d89d5835caf8 h1:af0uUTZDRxknoYZCBVJYycTgVOyfz0GiqCeLKgrTmQw=
-gitee.com/xuyiping_admin/go_proto v0.0.0-20250320093538-d89d5835caf8/go.mod h1:BKrFW6YLDectlQcQk3FYKBeXvjEiodAKJ5rq7O/QiPE=
-gitee.com/xuyiping_admin/go_proto v0.0.0-20250320095321-6b7069667e91 h1:GBpZpPJV/5FPsv3pUZ6AYEEMjoYrVj4clyFvzjC1t0Y=
-gitee.com/xuyiping_admin/go_proto v0.0.0-20250320095321-6b7069667e91/go.mod h1:BKrFW6YLDectlQcQk3FYKBeXvjEiodAKJ5rq7O/QiPE=
-gitee.com/xuyiping_admin/go_proto v0.0.0-20250324061924-bf4abbba60b1 h1:9fg6SXor/LJ94S4oQhrApKq/M2fVSai0i+FrprJ9W90=
-gitee.com/xuyiping_admin/go_proto v0.0.0-20250324061924-bf4abbba60b1/go.mod h1:BKrFW6YLDectlQcQk3FYKBeXvjEiodAKJ5rq7O/QiPE=
-gitee.com/xuyiping_admin/go_proto v0.0.0-20250324063941-72d937ade669 h1:RWi9BpB1kW42E3A84YsZzaN/+O0C795i2uiLReD7KQs=
-gitee.com/xuyiping_admin/go_proto v0.0.0-20250324063941-72d937ade669/go.mod h1:BKrFW6YLDectlQcQk3FYKBeXvjEiodAKJ5rq7O/QiPE=
-gitee.com/xuyiping_admin/go_proto v0.0.0-20250324081525-c0737d6d27b0 h1:YJu1wFMVQ5pOl0tYLg2DNaGxbBBSOt1/SVFeMOxc2f8=
-gitee.com/xuyiping_admin/go_proto v0.0.0-20250324081525-c0737d6d27b0/go.mod h1:BKrFW6YLDectlQcQk3FYKBeXvjEiodAKJ5rq7O/QiPE=
-gitee.com/xuyiping_admin/go_proto v0.0.0-20250324082653-a738e7b57d34 h1:yj12kf9/II1d4s1yXv/IebhFlKfI2AZiEp67A48MQLI=
-gitee.com/xuyiping_admin/go_proto v0.0.0-20250324082653-a738e7b57d34/go.mod h1:BKrFW6YLDectlQcQk3FYKBeXvjEiodAKJ5rq7O/QiPE=
-gitee.com/xuyiping_admin/go_proto v0.0.0-20250324083036-3b6babd2ba15 h1:mKQaCaSiqfai+msdKi5P+r+Nk8FT8K5jPTZAu/kAFSU=
-gitee.com/xuyiping_admin/go_proto v0.0.0-20250324083036-3b6babd2ba15/go.mod h1:BKrFW6YLDectlQcQk3FYKBeXvjEiodAKJ5rq7O/QiPE=
-gitee.com/xuyiping_admin/go_proto v0.0.0-20250324084120-e6352deeb1c5 h1:lSms9Fs2UgkAr7a5rKYGpdUbb9lSDFgtcnNiuRusR+0=
-gitee.com/xuyiping_admin/go_proto v0.0.0-20250324084120-e6352deeb1c5/go.mod h1:BKrFW6YLDectlQcQk3FYKBeXvjEiodAKJ5rq7O/QiPE=
-gitee.com/xuyiping_admin/go_proto v0.0.0-20250324091419-f7fa3ad96c0e h1:Dv1OgQfYuzw6+O1FBaSg86CiAOiGPnUIWV2FeWoPYdM=
-gitee.com/xuyiping_admin/go_proto v0.0.0-20250324091419-f7fa3ad96c0e/go.mod h1:BKrFW6YLDectlQcQk3FYKBeXvjEiodAKJ5rq7O/QiPE=
-gitee.com/xuyiping_admin/go_proto v0.0.0-20250324120913-88e4f53314db h1:QLLT24YPsIii/vCJYawzA2t5uwpoYb77y83WOqvlwtY=
-gitee.com/xuyiping_admin/go_proto v0.0.0-20250324120913-88e4f53314db/go.mod h1:BKrFW6YLDectlQcQk3FYKBeXvjEiodAKJ5rq7O/QiPE=
-gitee.com/xuyiping_admin/go_proto v0.0.0-20250325011618-727f0797ea27 h1:DruuERLrwZPshZoCQjgRDTYzsQd1CgAFJmoSq8PdVVk=
-gitee.com/xuyiping_admin/go_proto v0.0.0-20250325011618-727f0797ea27/go.mod h1:BKrFW6YLDectlQcQk3FYKBeXvjEiodAKJ5rq7O/QiPE=
-gitee.com/xuyiping_admin/go_proto v0.0.0-20250325071609-113a7f4c6da5 h1:gOfJObe2DLEfFjO+8+atJ2mLWAo6HAkOnYQIFW4P7tk=
-gitee.com/xuyiping_admin/go_proto v0.0.0-20250325071609-113a7f4c6da5/go.mod h1:BKrFW6YLDectlQcQk3FYKBeXvjEiodAKJ5rq7O/QiPE=
-gitee.com/xuyiping_admin/go_proto v0.0.0-20250325093632-db9c4586260e h1:d5OaTPXG1JP/2k11WBgj/pggSMrBYYT67R9pD/Z9la4=
-gitee.com/xuyiping_admin/go_proto v0.0.0-20250325093632-db9c4586260e/go.mod h1:BKrFW6YLDectlQcQk3FYKBeXvjEiodAKJ5rq7O/QiPE=
-gitee.com/xuyiping_admin/go_proto v0.0.0-20250326020249-4550f6f60bbe h1:gR2DdJByndC/DBKOIV0c6yA6PiPkMaL9BSRREGdl/tw=
-gitee.com/xuyiping_admin/go_proto v0.0.0-20250326020249-4550f6f60bbe/go.mod h1:BKrFW6YLDectlQcQk3FYKBeXvjEiodAKJ5rq7O/QiPE=
-gitee.com/xuyiping_admin/go_proto v0.0.0-20250326020740-d3d1bc9a851f h1:vQm+DMaNM40JJAkx/KUgfBbDYZxrLv4dAUbqS8k7Y68=
-gitee.com/xuyiping_admin/go_proto v0.0.0-20250326020740-d3d1bc9a851f/go.mod h1:BKrFW6YLDectlQcQk3FYKBeXvjEiodAKJ5rq7O/QiPE=
-gitee.com/xuyiping_admin/go_proto v0.0.0-20250326060130-a292235a6f1a h1:GMMirOkHyFlcj8l5DgPWc5CR2qFDg/nWU+iNi2kL9Aw=
-gitee.com/xuyiping_admin/go_proto v0.0.0-20250326060130-a292235a6f1a/go.mod h1:BKrFW6YLDectlQcQk3FYKBeXvjEiodAKJ5rq7O/QiPE=
-gitee.com/xuyiping_admin/go_proto v0.0.0-20250326061245-9a4fbec39fb6 h1:mfOzWgdB2NIgdunlgKAQGuZWGYidEJizQRHu2T4HXR4=
-gitee.com/xuyiping_admin/go_proto v0.0.0-20250326061245-9a4fbec39fb6/go.mod h1:BKrFW6YLDectlQcQk3FYKBeXvjEiodAKJ5rq7O/QiPE=
-gitee.com/xuyiping_admin/go_proto v0.0.0-20250331022421-15889f6c46c3 h1:4bIc13fiux6y03FOqptxj8VhHtvHMCltkF4CNuROMKg=
-gitee.com/xuyiping_admin/go_proto v0.0.0-20250331022421-15889f6c46c3/go.mod h1:BKrFW6YLDectlQcQk3FYKBeXvjEiodAKJ5rq7O/QiPE=
-gitee.com/xuyiping_admin/go_proto v0.0.0-20250331025336-cc1744596fab h1:Y4tbyz3hLuoyLjixwDK3JMIIWGfLH94UxUuaFXZxEtI=
-gitee.com/xuyiping_admin/go_proto v0.0.0-20250331025336-cc1744596fab/go.mod h1:BKrFW6YLDectlQcQk3FYKBeXvjEiodAKJ5rq7O/QiPE=
-gitee.com/xuyiping_admin/go_proto v0.0.0-20250331103026-c284e7da7be0 h1:NEiteoQE+x7Rzs0sjip9LOqJDhgqc3YnsTREo/dcnVo=
-gitee.com/xuyiping_admin/go_proto v0.0.0-20250331103026-c284e7da7be0/go.mod h1:BKrFW6YLDectlQcQk3FYKBeXvjEiodAKJ5rq7O/QiPE=
-gitee.com/xuyiping_admin/go_proto v0.0.0-20250401012917-fe0b01a85574 h1:acpexjsGZYca4Bug25z/UMcbEgeotZbg5dkqrEASUus=
-gitee.com/xuyiping_admin/go_proto v0.0.0-20250401012917-fe0b01a85574/go.mod h1:BKrFW6YLDectlQcQk3FYKBeXvjEiodAKJ5rq7O/QiPE=
-gitee.com/xuyiping_admin/go_proto v0.0.0-20250401013759-480302d9deb1 h1:KTIt5nZAQGhcs6BQCjRbcNsPipFf0iF96IzubNnuaMo=
-gitee.com/xuyiping_admin/go_proto v0.0.0-20250401013759-480302d9deb1/go.mod h1:BKrFW6YLDectlQcQk3FYKBeXvjEiodAKJ5rq7O/QiPE=
-gitee.com/xuyiping_admin/go_proto v0.0.0-20250401071526-0bfd7f4af6d3 h1:DlhNaXi3Sp1PIT8Lb5m8/BL89MV4z2eyaYnZANWU6OU=
-gitee.com/xuyiping_admin/go_proto v0.0.0-20250401071526-0bfd7f4af6d3/go.mod h1:BKrFW6YLDectlQcQk3FYKBeXvjEiodAKJ5rq7O/QiPE=
-gitee.com/xuyiping_admin/go_proto v0.0.0-20250401072057-a324cc76215b h1:kdALC/8+x5bYGQxBBSAq9FvsneuwfMSu0x6evwzJoP4=
-gitee.com/xuyiping_admin/go_proto v0.0.0-20250401072057-a324cc76215b/go.mod h1:BKrFW6YLDectlQcQk3FYKBeXvjEiodAKJ5rq7O/QiPE=
-gitee.com/xuyiping_admin/go_proto v0.0.0-20250403015930-728ac52191ed h1:jdfxjd7TdP8TLfOBaQxtA5IcnXI9/+FRuJ9hHPt1gM4=
-gitee.com/xuyiping_admin/go_proto v0.0.0-20250403015930-728ac52191ed/go.mod h1:BKrFW6YLDectlQcQk3FYKBeXvjEiodAKJ5rq7O/QiPE=
-gitee.com/xuyiping_admin/go_proto v0.0.0-20250403023152-3351c34ff8e2 h1:DxuqalLwzBemnGnXn3NK/WvtH3k1elUIVXEHRP2QSvA=
-gitee.com/xuyiping_admin/go_proto v0.0.0-20250403023152-3351c34ff8e2/go.mod h1:BKrFW6YLDectlQcQk3FYKBeXvjEiodAKJ5rq7O/QiPE=
-gitee.com/xuyiping_admin/go_proto v0.0.0-20250407020514-55972b3e4a6e h1:fTLVj1uD3o0VzxS0G2n3j8uqHGTGFmaEcJAAGum/rDs=
-gitee.com/xuyiping_admin/go_proto v0.0.0-20250407020514-55972b3e4a6e/go.mod h1:BKrFW6YLDectlQcQk3FYKBeXvjEiodAKJ5rq7O/QiPE=
-gitee.com/xuyiping_admin/go_proto v0.0.0-20250407030104-bbed5685c433 h1:cD1+JL5SH7p3VDc/Ih+r3J1nvluUD7qmPdLoUAaZk8g=
-gitee.com/xuyiping_admin/go_proto v0.0.0-20250407030104-bbed5685c433/go.mod h1:BKrFW6YLDectlQcQk3FYKBeXvjEiodAKJ5rq7O/QiPE=
-gitee.com/xuyiping_admin/go_proto v0.0.0-20250407030911-c3f05890c9db h1:isImECfyemll26Bd/aeGKCdTCC6W1cfLYWDKbozNMf4=
-gitee.com/xuyiping_admin/go_proto v0.0.0-20250407030911-c3f05890c9db/go.mod h1:BKrFW6YLDectlQcQk3FYKBeXvjEiodAKJ5rq7O/QiPE=
-gitee.com/xuyiping_admin/go_proto v0.0.0-20250407031505-811f84cb2ef7 h1:pzfT/1FwyhgoNvn7V6Y7H5o8K2gb7l/btxJKJGHP754=
-gitee.com/xuyiping_admin/go_proto v0.0.0-20250407031505-811f84cb2ef7/go.mod h1:BKrFW6YLDectlQcQk3FYKBeXvjEiodAKJ5rq7O/QiPE=
-gitee.com/xuyiping_admin/go_proto v0.0.0-20250407033055-a88907fe2237 h1:MrPG3W7f/Mbb7x3JpNnngDgHr/YQD6OOuGAyX3tqXyg=
-gitee.com/xuyiping_admin/go_proto v0.0.0-20250407033055-a88907fe2237/go.mod h1:BKrFW6YLDectlQcQk3FYKBeXvjEiodAKJ5rq7O/QiPE=
-gitee.com/xuyiping_admin/go_proto v0.0.0-20250407062358-b0e1d2556279 h1:x8G2ihVpIzP8hclf9Fihwz7hQoscYvm+Tp3tpMUj830=
-gitee.com/xuyiping_admin/go_proto v0.0.0-20250407062358-b0e1d2556279/go.mod h1:BKrFW6YLDectlQcQk3FYKBeXvjEiodAKJ5rq7O/QiPE=
-gitee.com/xuyiping_admin/go_proto v0.0.0-20250407062707-261478382cce h1:ho0ZTyNjnd5uGgB9YCGifW2SDGYWGOjipsWOhk2LM3o=
-gitee.com/xuyiping_admin/go_proto v0.0.0-20250407062707-261478382cce/go.mod h1:BKrFW6YLDectlQcQk3FYKBeXvjEiodAKJ5rq7O/QiPE=
-gitee.com/xuyiping_admin/go_proto v0.0.0-20250407091052-1c45232ed0aa h1:JK8PEgl8XDRKeM59OCGR/KcEqn/8vqREYUyIQcqseyE=
-gitee.com/xuyiping_admin/go_proto v0.0.0-20250407091052-1c45232ed0aa/go.mod h1:BKrFW6YLDectlQcQk3FYKBeXvjEiodAKJ5rq7O/QiPE=
-gitee.com/xuyiping_admin/go_proto v0.0.0-20250407093743-ab033b9b99a3 h1:zAqrn31oJ/vma/u6PH2FUXypG8GOTs/3ObyniENlXE8=
-gitee.com/xuyiping_admin/go_proto v0.0.0-20250407093743-ab033b9b99a3/go.mod h1:BKrFW6YLDectlQcQk3FYKBeXvjEiodAKJ5rq7O/QiPE=
-gitee.com/xuyiping_admin/go_proto v0.0.0-20250407095058-f8caa961fa43 h1:pPnc99fNtmJ4J2B5VrHMB7wwJmiNjc+CBHDcVjDG7T8=
-gitee.com/xuyiping_admin/go_proto v0.0.0-20250407095058-f8caa961fa43/go.mod h1:BKrFW6YLDectlQcQk3FYKBeXvjEiodAKJ5rq7O/QiPE=
-gitee.com/xuyiping_admin/go_proto v0.0.0-20250407102009-df085f2ca575 h1:xIww6nDUvoR961NthJbI4S/12/IkyPDez5oo/EFgqPU=
-gitee.com/xuyiping_admin/go_proto v0.0.0-20250407102009-df085f2ca575/go.mod h1:BKrFW6YLDectlQcQk3FYKBeXvjEiodAKJ5rq7O/QiPE=
-gitee.com/xuyiping_admin/go_proto v0.0.0-20250408010625-534dc8ac81a1 h1:y7GI42qIScacVzlwCrvXqy5QjIxJhm/4vRWyx3WCxwA=
-gitee.com/xuyiping_admin/go_proto v0.0.0-20250408010625-534dc8ac81a1/go.mod h1:BKrFW6YLDectlQcQk3FYKBeXvjEiodAKJ5rq7O/QiPE=
-gitee.com/xuyiping_admin/go_proto v0.0.0-20250408020446-8115565fe86e h1:wETx6NGE8Xsf2p8nvhq2mg8zujiOxCfequWhcCCGZ5s=
-gitee.com/xuyiping_admin/go_proto v0.0.0-20250408020446-8115565fe86e/go.mod h1:BKrFW6YLDectlQcQk3FYKBeXvjEiodAKJ5rq7O/QiPE=
-gitee.com/xuyiping_admin/go_proto v0.0.0-20250408032224-57e63f411526 h1:oe8HolRGDYj30IltPmUMFq6i0+H8pJsj533WkjdIHSM=
-gitee.com/xuyiping_admin/go_proto v0.0.0-20250408032224-57e63f411526/go.mod h1:BKrFW6YLDectlQcQk3FYKBeXvjEiodAKJ5rq7O/QiPE=
-gitee.com/xuyiping_admin/go_proto v0.0.0-20250408072509-1117ad78b23f h1:VWi6G/NWr/FxORMXpie4Sgg2+cmlh/ozVwJWdsyr4+g=
-gitee.com/xuyiping_admin/go_proto v0.0.0-20250408072509-1117ad78b23f/go.mod h1:BKrFW6YLDectlQcQk3FYKBeXvjEiodAKJ5rq7O/QiPE=
-gitee.com/xuyiping_admin/go_proto v0.0.0-20250408075038-dd76bfdd8a73 h1:QNo+OSvJtuCgxvYreVcqZJXHFafnaD3/NTzzQbWNigo=
-gitee.com/xuyiping_admin/go_proto v0.0.0-20250408075038-dd76bfdd8a73/go.mod h1:BKrFW6YLDectlQcQk3FYKBeXvjEiodAKJ5rq7O/QiPE=
-gitee.com/xuyiping_admin/go_proto v0.0.0-20250409012212-55a38ade5fbe h1:dUQ3PYq07bX0q8E3ZF/sq6TdS12IfE4ogLzCmK1Im5U=
-gitee.com/xuyiping_admin/go_proto v0.0.0-20250409012212-55a38ade5fbe/go.mod h1:BKrFW6YLDectlQcQk3FYKBeXvjEiodAKJ5rq7O/QiPE=
-gitee.com/xuyiping_admin/go_proto v0.0.0-20250409054106-c044078978f9 h1:HjAO7MAZz/Vu3dRE6GI7kxx31RBHH0lQ5WXaWvtwz3k=
-gitee.com/xuyiping_admin/go_proto v0.0.0-20250409054106-c044078978f9/go.mod h1:BKrFW6YLDectlQcQk3FYKBeXvjEiodAKJ5rq7O/QiPE=
-gitee.com/xuyiping_admin/go_proto v0.0.0-20250409060243-3b624c8d5ece h1:tHo+IVad4bQ9eQDSdVEGfQZ3hbGr3w6c/vi4ibQzt08=
-gitee.com/xuyiping_admin/go_proto v0.0.0-20250409060243-3b624c8d5ece/go.mod h1:BKrFW6YLDectlQcQk3FYKBeXvjEiodAKJ5rq7O/QiPE=
-gitee.com/xuyiping_admin/go_proto v0.0.0-20250409093335-d8b013bd5bab h1:9nfap+BBJeP17KlVGViBUXDf1u84YKGN1Qliz/2VvBo=
-gitee.com/xuyiping_admin/go_proto v0.0.0-20250409093335-d8b013bd5bab/go.mod h1:BKrFW6YLDectlQcQk3FYKBeXvjEiodAKJ5rq7O/QiPE=
-gitee.com/xuyiping_admin/go_proto v0.0.0-20250409093847-cb281b5a01b2 h1:KPl+ZJuQKgGQfgxfJn/RtX1+YwzFI5x0iEDeAvHV6Cg=
-gitee.com/xuyiping_admin/go_proto v0.0.0-20250409093847-cb281b5a01b2/go.mod h1:BKrFW6YLDectlQcQk3FYKBeXvjEiodAKJ5rq7O/QiPE=
-gitee.com/xuyiping_admin/go_proto v0.0.0-20250409095220-1fc7ef026108 h1:Sz/paz5RJjGg2C50xE3xuFl2aVkQs31zRstVep+xejQ=
-gitee.com/xuyiping_admin/go_proto v0.0.0-20250409095220-1fc7ef026108/go.mod h1:BKrFW6YLDectlQcQk3FYKBeXvjEiodAKJ5rq7O/QiPE=
-gitee.com/xuyiping_admin/go_proto v0.0.0-20250410061346-7010a8affda4 h1:CJbOeCcCLtUX2ht3wPqdgrSUd3RTvtSNPeiHX8rlUtQ=
-gitee.com/xuyiping_admin/go_proto v0.0.0-20250410061346-7010a8affda4/go.mod h1:BKrFW6YLDectlQcQk3FYKBeXvjEiodAKJ5rq7O/QiPE=
+gitee.com/xuyiping_admin/go_proto v0.0.0-20250412093307-d7a38b54919d h1:WhYvj5DAdpH4lDALEDOB29JnRwuc3Zk1nP+5K3bddZE=
+gitee.com/xuyiping_admin/go_proto v0.0.0-20250412093307-d7a38b54919d/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=
@@ -184,6 +62,7 @@ github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmV
 github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8=
 github.com/aryann/difflib v0.0.0-20170710044230-e206f873d14a/go.mod h1:DAHtR1m6lCRdSC2Tm3DSWRPvIPr6xNKyeHdqDQSQT+A=
 github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2 h1:DklsrG3dyBCFEj5IhUbnKptjxatkF07cF2ak3yi77so=
+github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2/go.mod h1:WaHUgvxTVq04UNunO+XhnAqY/wQc+bxr74GqbsZ/Jqw=
 github.com/aws/aws-lambda-go v1.13.3/go.mod h1:4UKl9IzQMoD+QF79YdCuzCwp8VbmG4VAQwij/eHl5CU=
 github.com/aws/aws-sdk-go v1.27.0/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo=
 github.com/aws/aws-sdk-go-v2 v0.18.0/go.mod h1:JWVYvqSMppoMJC0x5wdwiImzgXTI9FuZwxzkQq9wy+g=
@@ -294,7 +173,6 @@ github.com/go-kit/log v0.1.0/go.mod h1:zbhenjAZHb184qTLMA9ZjW7ThYL0H2mk7Q6pNt4vb
 github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE=
 github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk=
 github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A=
-github.com/go-logfmt/logfmt v0.5.1/go.mod h1:WYhtIu8zTZfxdn5+rREduYbwxfcBr/Vr6KEVveWlfTs=
 github.com/go-playground/assert/v2 v2.0.1/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4=
 github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s=
 github.com/go-playground/locales v0.14.0/go.mod h1:sawfccIbzZTqEDETgFXqTho0QybSa7l++s0DH+LDiLs=
@@ -538,6 +416,7 @@ github.com/nats-io/nuid v1.0.1/go.mod h1:19wcPz3Ph3q0Jbyiqsd0kePYG7A95tJPxeL+1OS
 github.com/nxadm/tail v1.4.4 h1:DQuhQpB1tVlglWS2hLQ5OV6B5r8aGxSrPc5Qo6uTN78=
 github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A=
 github.com/nyaruka/phonenumbers v1.1.7 h1:5UUI9hE79Kk0dymSquXbMYB7IlNDNhvu2aNlJpm9et8=
+github.com/nyaruka/phonenumbers v1.1.7/go.mod h1:DC7jZd321FqUe+qWSNcHi10tyIyGNXGcNbfkPvdp1Vs=
 github.com/oklog/oklog v0.3.2/go.mod h1:FCV+B7mhrz4o+ueLpx+KqkyXRGMWOYEvfiXtdGtbWGs=
 github.com/oklog/run v1.0.0/go.mod h1:dlhp/R75TPv97u0XWUtDeV/lRKWPKSdTuV0TZvrmrQA=
 github.com/olekukonko/tablewriter v0.0.0-20170122224234-a0225b3f23b5/go.mod h1:vsDQFd/mU46D+Z4whnwzcISnGGzXWMclvtLoiIKAKIo=
@@ -732,7 +611,6 @@ go.uber.org/dig v1.15.0/go.mod h1:pKHs0wMynzL6brANhB2hLMro+zalv1osARTviTcqHLM=
 go.uber.org/goleak v0.10.0/go.mod h1:VCZuO8V8mFPlL0F5J5GK1rtHV3DrFcQ1R8ryq7FK0aI=
 go.uber.org/goleak v1.1.10/go.mod h1:8a7PlsEVH3e/a/GLqe5IIrQx6GzcnRmZEufDUTk4A7A=
 go.uber.org/goleak v1.1.11 h1:wy28qYRKZgnJTxGxvye5/wgWr1EKjmUDGYox5mGlRlI=
-go.uber.org/goleak v1.1.11/go.mod h1:cwTWslyiVhfpKIDGSZEM2HlOvcqm+tG4zioyIeLoqMQ=
 go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0=
 go.uber.org/multierr v1.3.0/go.mod h1:VgVr7evmIr6uPjLBxg28wmKNXyqE9akIJ5XnfpiKl+4=
 go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU=

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

@@ -48,7 +48,13 @@ func WeightRange(c *gin.Context) {
 		return
 	}
 
-	res, err := middleware.Dependency(c).StoreEventHub.OpsService.WeightRange(c, &req)
+	pagination := &pasturePb.PaginationModel{
+		Page:       int32(c.GetInt(middleware.Page)),
+		PageSize:   int32(c.GetInt(middleware.PageSize)),
+		PageOffset: int32(c.GetInt(middleware.PageOffset)),
+	}
+
+	res, err := middleware.Dependency(c).StoreEventHub.OpsService.WeightRange(c, &req, pagination)
 	if err != nil {
 		apierr.ClassifiedAbort(c, err)
 		return

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

@@ -329,14 +329,13 @@ func CowSale(c *gin.Context) {
 		valid.Field(&req.SaleTicket, valid.Required),
 		valid.Field(&req.EarNumbers, valid.Required),
 		valid.Field(&req.QuarantineReport, valid.Required),
+		valid.Field(&req.SalesType, valid.Required),
 		valid.Field(&req.SaleVehicleItems, valid.Required, valid.Each(valid.By(func(value interface{}) error {
 			s := value.(pasturePb.SaleVehicleItem)
 			return valid.ValidateStruct(&s,
 				valid.Field(&s.CarNumber, valid.Required),
 				valid.Field(&s.CowCount, valid.Required),
 				valid.Field(&s.CowWeight, valid.Required),
-				valid.Field(&s.WeighbridgePhotos, valid.Required),
-				valid.Field(&s.VehiclePhotos, valid.Required),
 			)
 		}))),
 	); err != nil {
@@ -374,5 +373,6 @@ func CowSaleList(c *gin.Context) {
 		apierr.ClassifiedAbort(c, err)
 		return
 	}
+
 	ginutil.JSONResp(c, res)
 }

+ 28 - 2
http/handler/event/event_breed.go

@@ -206,7 +206,7 @@ func EstrusEventList(c *gin.Context) {
 }
 
 func EstrusBatchMating(c *gin.Context) {
-	var req pasturePb.EventNaturalEstrusBatch
+	var req pasturePb.EventEstrusBatch
 	if err := ginutil.BindProto(c, &req); err != nil {
 		apierr.AbortBadRequest(c, http.StatusBadRequest, err)
 		return
@@ -214,7 +214,7 @@ func EstrusBatchMating(c *gin.Context) {
 
 	if err := valid.ValidateStruct(&req,
 		valid.Field(&req.Items, valid.Required, valid.Each(valid.By(func(value interface{}) error {
-			item := value.(pasturePb.EventNaturalEstrusItems)
+			item := value.(pasturePb.EventEstrusItems)
 			return valid.ValidateStruct(&item,
 				valid.Field(&item.EarNumber, valid.Required),
 				valid.Field(&item.EstrusAt, valid.Required),
@@ -238,6 +238,32 @@ func EstrusBatchMating(c *gin.Context) {
 		Data: &operationPb.Success{Success: true},
 	})
 }
+
+func NeckRingNoEstrusBatch(c *gin.Context) {
+	var req pasturePb.NeckRingNoEstrusBatchRequest
+	if err := ginutil.BindProto(c, &req); err != nil {
+		apierr.AbortBadRequest(c, http.StatusBadRequest, err)
+		return
+	}
+
+	if err := valid.ValidateStruct(&req,
+		valid.Field(&req.EarNumbers, valid.Required, valid.Required),
+	); err != nil {
+		apierr.AbortBadRequest(c, http.StatusBadRequest, err)
+		return
+	}
+
+	if err := middleware.BackendOperation(c).OpsService.NeckRingNoEstrusBatch(c, &req); err != nil {
+		apierr.ClassifiedAbort(c, err)
+		return
+	}
+
+	ginutil.JSONResp(c, &operationPb.CommonOK{
+		Code: http.StatusOK,
+		Msg:  "ok",
+		Data: &operationPb.Success{Success: true},
+	})
+}
 func SameTimeCreate(c *gin.Context) {
 	var req pasturePb.EventSameTime
 	if err := ginutil.BindProto(c, &req); err != nil {

+ 22 - 3
http/handler/pasture/pasture.go

@@ -5,13 +5,13 @@ import (
 	"net/http"
 	"strconv"
 
-	"gitee.com/xuyiping_admin/pkg/xerr"
-
 	pasturePb "gitee.com/xuyiping_admin/go_proto/proto/go/backend/cow"
 	operationPb "gitee.com/xuyiping_admin/go_proto/proto/go/backend/operation"
 	"gitee.com/xuyiping_admin/pkg/apierr"
 	"gitee.com/xuyiping_admin/pkg/ginutil"
 	"gitee.com/xuyiping_admin/pkg/valid"
+	"gitee.com/xuyiping_admin/pkg/valid/is"
+	"gitee.com/xuyiping_admin/pkg/xerr"
 	"github.com/gin-gonic/gin"
 )
 
@@ -375,7 +375,7 @@ func SaleDealerCreateOrUpdate(c *gin.Context) {
 
 	if err := valid.ValidateStruct(&req,
 		valid.Field(&req.Name, valid.Required),
-		valid.Field(&req.Phone, valid.Required),
+		valid.Field(&req.Phone, valid.Required, is.E164),
 		valid.Field(&req.Contacts, valid.Required),
 	); err != nil {
 		apierr.ClassifiedAbort(c, err)
@@ -408,3 +408,22 @@ func SaleDealerList(c *gin.Context) {
 	}
 	ginutil.JSONResp(c, res)
 }
+
+func SaleDealerIsShow(c *gin.Context) {
+	dealerIdStr := c.Param("id")
+	dealerId, _ := strconv.Atoi(dealerIdStr)
+
+	if err := valid.Validate(dealerId, valid.Required, valid.Min(1)); err != nil {
+		apierr.ClassifiedAbort(c, err)
+		return
+	}
+	if err := middleware.BackendOperation(c).OpsService.DeleteDealer(c, int64(dealerId)); err != nil {
+		apierr.ClassifiedAbort(c, err)
+		return
+	}
+	ginutil.JSONResp(c, &operationPb.CommonOK{
+		Code: http.StatusOK,
+		Msg:  "ok",
+		Data: &operationPb.Success{Success: true},
+	})
+}

+ 1 - 0
http/route/event_api.go

@@ -37,6 +37,7 @@ func EventAPI(opts ...func(engine *gin.Engine)) func(s *gin.Engine) {
 		// 发情
 		eventRoute.POST("/estrus/list", event.EstrusEventList)
 		eventRoute.POST("/estrus/mating/batch", event.EstrusBatchMating)
+		eventRoute.POST("/neck/ring/no/estrus/batch", event.NeckRingNoEstrusBatch)
 		// 同期
 		eventRoute.POST("/same/time/list", event.SameTimeList)
 		eventRoute.POST("/same/time/create", event.SameTimeCreate)

+ 1 - 0
http/route/pasture_api.go

@@ -54,6 +54,7 @@ func PastureManageAPI(opts ...func(engine *gin.Engine)) func(s *gin.Engine) {
 		// 经销商相关
 		pastureRoute.POST("/dealer/createOrUpdate", pasture.SaleDealerCreateOrUpdate)
 		pastureRoute.POST("/dealer/list", pasture.SaleDealerList)
+		pastureRoute.PUT("/dealer/is_show/:id", pasture.SaleDealerIsShow)
 
 		// 基础参数
 		pastureRoute.POST("/system/basic/list", pasture.SystemBasicList)

+ 1 - 1
model/calendar.go

@@ -84,7 +84,7 @@ func (c CalendarSlice) ToPB() []*pasturePb.Calendar {
 	return res
 }
 
-type TodayCompletedData struct {
+type CompletedData struct {
 	Count            int32
 	CalendarTypeKind pasturePb.CalendarType_Kind
 	CalendarTypeName string

+ 24 - 1
model/cow.go

@@ -155,6 +155,20 @@ func (c *Cow) EventDeathUpdate(eventAt int64) {
 	c.AdmissionStatus = pasturePb.AdmissionStatus_Die
 	c.HealthStatus = pasturePb.HealthStatus_Dead
 	c.DepartureAt = eventAt
+	c.NeckRingNumber = ""
+}
+
+// EventSaleUpdate 更新牛只销售信息
+func (c *Cow) EventSaleUpdate(eventAt int64, salesType pasturePb.SalesType_Kind) {
+	if salesType == pasturePb.SalesType_Out {
+		c.HealthStatus = pasturePb.HealthStatus_Out
+		c.AdmissionStatus = pasturePb.AdmissionStatus_Out
+	}
+	if salesType == pasturePb.SalesType_Sales {
+		c.AdmissionStatus = pasturePb.AdmissionStatus_Sale
+	}
+	c.DepartureAt = eventAt
+	c.NeckRingNumber = ""
 }
 
 // EventMatingUpdate 配种更新
@@ -371,7 +385,11 @@ func NewEnterCow(pastureId int64, req *pasturePb.EventEnterRequest, penMap map[i
 		isForbiddenMating = pasturePb.IsShow_Ok
 	}
 
-	return &Cow{
+	if req.PregnantCheckResult == pasturePb.PregnantCheckResult_Pregnant {
+		req.BreedStatus = pasturePb.BreedStatus_Pregnant
+	}
+
+	cow := &Cow{
 		PastureId:           pastureId,
 		Sex:                 req.Sex,
 		EarNumber:           req.EarNumber,
@@ -403,7 +421,12 @@ func NewEnterCow(pastureId int64, req *pasturePb.EventEnterRequest, penMap map[i
 		LastDryMilkAt:       int64(req.DryMilkAt),
 		MatingTimes:         req.MatingTimes,
 		LastCalvingAt:       int64(req.CalvingAt),
+		LastBullNumber:      req.BullNumber,
+		LastAbortionAt:      int64(req.AbortionAt),
 	}
+	cow.AdmissionAge = cow.GetAdmissionAge()
+	cow.DayAge = cow.GetDayAge()
+	return cow
 }
 
 // NewCalfCow 产犊新增

+ 15 - 4
model/event_cow_same_time.go

@@ -38,7 +38,7 @@ func (s *EventCowSameTime) TableName() string {
 	return "event_cow_same_time"
 }
 
-func (s *EventCowSameTime) EventUpdate(drugs *Drugs, usage float32, remarks string, currentUser, operationUser *SystemUser) {
+func (s *EventCowSameTime) EventUpdate(drugs *Drugs, usage float32, realityAt int64, remarks string, currentUser, operationUser *SystemUser) {
 	s.Status = pasturePb.IsShow_Ok
 	s.DrugsId = drugs.Id
 	s.Unit = drugs.Unit
@@ -48,6 +48,7 @@ func (s *EventCowSameTime) EventUpdate(drugs *Drugs, usage float32, remarks stri
 	s.OperationName = operationUser.Name
 	s.MessageId = currentUser.Id
 	s.MessageName = currentUser.Name
+	s.RealityDay = realityAt
 }
 
 func NewEventCowSameTime(pastureId int64, cow *Cow, planTime int64, sameTime *SameTime, sameTimeType pasturePb.SameTimeType_Kind) *EventCowSameTime {
@@ -102,7 +103,6 @@ type SameTimeBodySlice []*SameTimeItemBody
 
 func (s SameTimeBodySlice) ToPB(
 	breedStatusMap map[pasturePb.BreedStatus_Kind]string,
-	penMap map[int32]*Pen,
 	sameTimeTypeMap map[pasturePb.SameTimeType_Kind]string,
 ) []*pasturePb.SameTimeItems {
 	res := make([]*pasturePb.SameTimeItems, len(s))
@@ -120,12 +120,22 @@ func (s SameTimeBodySlice) ToPB(
 			playDayAtFormat = time.Unix(v.PlanDay, 0).Format(LayoutDate2)
 		}
 
+		breedStatusName := ""
+		if bm, ok := breedStatusMap[v.BreedStatus]; ok {
+			breedStatusName = bm
+		}
+
+		sameTimeTypeName := ""
+		if sm, ok := sameTimeTypeMap[v.SameTimeType]; ok {
+			sameTimeTypeName = sm
+		}
+
 		res[i] = &pasturePb.SameTimeItems{
 			Id:               int32(v.Id),
 			CowId:            int32(v.CowId),
 			EarNumber:        v.EarNumber,
 			BreedStatus:      v.BreedStatus,
-			BreedStatusName:  breedStatusMap[v.BreedStatus],
+			BreedStatusName:  breedStatusName,
 			PenName:          v.PenName,
 			PenId:            v.PenId,
 			Lact:             v.Lact,
@@ -133,7 +143,8 @@ func (s SameTimeBodySlice) ToPB(
 			AbortionAge:      v.AbortionAge,
 			DayAge:           v.DayAge,
 			Status:           v.Status,
-			SameTimeTypeName: sameTimeTypeMap[v.SameTimeType],
+			SameTimeTypeName: sameTimeTypeName,
+			SameTimeType:     v.SameTimeType,
 			CalvingAtFormat:  calvingAtFormat,
 			AbortionAtFormat: abortionAtFormat,
 			MatingTimes:      v.MatingTimes,

+ 1 - 0
model/event_death.go

@@ -79,4 +79,5 @@ func (e EventDeathSlice) ToPB() []*pasturePb.EventDeath {
 type EventDeathModel struct {
 	Cow        *Cow
 	EventDeath *EventDeath
+	NeckRing   *NeckRing
 }

+ 7 - 0
model/event_estrus.go

@@ -87,3 +87,10 @@ func (e EventEstrusSlice) ToPB() []*pasturePb.SearchEventEstrusList {
 	}
 	return res
 }
+
+type EventEstrusBatch struct {
+	EventEstrusList  []*EventEstrus
+	EventMatingList  []*EventMating
+	ExposeEstrusType pasturePb.ExposeEstrusType_Kind
+	EarNumbers       []string
+}

+ 39 - 11
model/event_sale.go

@@ -59,27 +59,44 @@ func NewEventSale(pastureId int64, dealerInfo *SaleDealer, req *pasturePb.EventC
 
 type EventSaleSlice []*EventSale
 
-func (e EventSaleSlice) ToPB(eventSaleCarMap map[int64][]*EventSaleCar, eventSaleCowList []*EventSaleCow) []*pasturePb.EventCowSale {
+func (e EventSaleSlice) ToPB(eventSaleCarMap map[int64][]*EventSaleCar, eventSaleCowMap map[int64][]*EventSaleCow) []*pasturePb.EventCowSale {
 	res := make([]*pasturePb.EventCowSale, len(e))
 	for i, v := range e {
 		saleVehicleItems := make([]*pasturePb.SaleVehicleItem, 0)
-		eventSaleCarItems, ok := eventSaleCarMap[v.Id]
-		earNumbers := make([]string, 0)
-		if ok {
+		if eventSaleCarItems, ok := eventSaleCarMap[v.Id]; ok {
 			for _, item := range eventSaleCarItems {
-				es := strings.Split(item.EarNumbers, ",")
-				saleVehicleItems = append(saleVehicleItems, &pasturePb.SaleVehicleItem{
+				es, weighbridgePhotos, vehiclePhotos := make([]string, 0), make([]string, 0), make([]string, 0)
+				if item.EarNumbers != "" {
+					es = strings.Split(item.EarNumbers, ",")
+				}
+				if item.WeighbridgePhotos != "" {
+					weighbridgePhotos = strings.Split(item.WeighbridgePhotos, ",")
+				}
+				if item.CarPhotos != "" {
+					vehiclePhotos = strings.Split(item.CarPhotos, ",")
+				}
+				saleVehicleItem := &pasturePb.SaleVehicleItem{
 					CarNumber:         item.CarNumber,
 					CowCount:          item.CowCount,
 					CowWeight:         float32(item.CowWeight) / 1000,
 					OutboundTicket:    item.OutboundTicket,
-					WeighbridgePhotos: strings.Split(item.WeighbridgePhotos, ","),
-					VehiclePhotos:     strings.Split(item.CarPhotos, ","),
+					WeighbridgePhotos: weighbridgePhotos,
+					VehiclePhotos:     vehiclePhotos,
 					EarNumbers:        es,
-				})
-				earNumbers = append(earNumbers, es...)
+				}
+				saleVehicleItems = append(saleVehicleItems, saleVehicleItem)
+			}
+		}
+
+		earNumbers := make([]string, 0)
+		saleCount := int32(0)
+		if cowList, ok := eventSaleCowMap[v.Id]; ok {
+			for _, cow := range cowList {
+				earNumbers = append(earNumbers, cow.EarNumber)
+				saleCount++
 			}
 		}
+
 		res[i] = &pasturePb.EventCowSale{
 			DealerId:         v.DealerId,
 			DealerName:       v.DealerName,
@@ -97,8 +114,19 @@ func (e EventSaleSlice) ToPB(eventSaleCarMap map[int64][]*EventSaleCar, eventSal
 			SaleTicket:       strings.Split(v.SaleTicker, ","),
 			QuarantineReport: strings.Split(v.QuarantineReport, ","),
 			SaleVehicleItems: saleVehicleItems,
-			SaleCount:        int32(len(eventSaleCowList)),
+			SaleCount:        saleCount,
 		}
 	}
 	return res
 }
+
+type EventSaleModel struct {
+	CowList          []*Cow
+	SalesType        pasturePb.SalesType_Kind
+	SaleAt           int64
+	EventSale        *EventSale
+	EventSaleCarList []*EventSaleCar
+	EventSaleCowList []*EventSaleCow
+	NeckRingList     []*NeckRing
+	EventCowLog      []*EventCowLog
+}

+ 1 - 1
model/neck_active_habit.go

@@ -67,7 +67,7 @@ type NeckActiveHabit struct {
 	BeforeThreeSumIntake int32                 `json:"beforeThreeSumIntake"`
 	Score                int32                 `json:"score"`
 	IsShow               pasturePb.IsShow_Kind `json:"isShow"`
-	IsEstrus             pasturePb.IsShow_Kind `json:"isEstrus"`
+	Cft                  float32               `json:"cft"`
 	RecordCount          int32                 `json:"recordCount"`
 	FirmwareVersion      int32                 `json:"firmwareVersion"`
 	CreatedAt            int64                 `json:"createdAt"`

+ 35 - 6
model/neck_ring.go

@@ -13,14 +13,13 @@ type NeckRing struct {
 	CowId          int64                         `json:"cowId"`
 	EarNumber      string                        `json:"earNumber"`
 	WearAt         int64                         `json:"wearAt"`
+	IsBind         pasturePb.NeckRingIsBind_Kind `json:"isBind"`
 	Status         pasturePb.NeckRingStatus_Kind `json:"status"`
 	ErrorReason    string                        `json:"errorReason"`
 	OperationId    int32                         `json:"operationId"`
 	OperationName  string                        `json:"operationName"`
 	CreatedAt      int64                         `json:"createdAt"`
 	UpdatedAt      int64                         `json:"updatedAt"`
-
-	PenName string `json:"penName" gorm:"-"`
 }
 
 func (n *NeckRing) TableName() string {
@@ -32,6 +31,16 @@ func (n *NeckRing) EventBindUpdate(cowId int64) {
 	n.WearAt = time.Now().Unix()
 }
 
+func (n *NeckRing) EventUnBindUpdate() {
+	n.IsBind = pasturePb.NeckRingIsBind_Unbind
+	n.CowId = 0
+	n.WearAt = 0
+	n.ErrorReason = ""
+	n.EarNumber = ""
+	n.OperationId = 0
+	n.OperationName = ""
+}
+
 func NewNeckRing(pastureId int64, number string, cowInfo *Cow, operationUser *SystemUser) *NeckRing {
 	return &NeckRing{
 		PastureId:      pastureId,
@@ -39,15 +48,16 @@ func NewNeckRing(pastureId int64, number string, cowInfo *Cow, operationUser *Sy
 		CowId:          cowInfo.Id,
 		EarNumber:      cowInfo.EarNumber,
 		WearAt:         time.Now().Unix(),
-		Status:         pasturePb.NeckRingStatus_Bind,
+		IsBind:         pasturePb.NeckRingIsBind_Bind,
+		Status:         pasturePb.NeckRingStatus_Normal,
 		OperationId:    int32(operationUser.Id),
 		OperationName:  operationUser.Name,
 	}
 }
 
-type NeckRingSlice []*NeckRing
+type NeckRingSlice []*NeckRingModel
 
-func (n NeckRingSlice) ToPB(neckRingStatus map[pasturePb.NeckRingStatus_Kind]string) []*pasturePb.SearchNeckRingList {
+func (n NeckRingSlice) ToPB(neckRingIsBind map[pasturePb.NeckRingIsBind_Kind]string, neckRingStatus map[pasturePb.NeckRingStatus_Kind]string) []*pasturePb.SearchNeckRingList {
 	res := make([]*pasturePb.SearchNeckRingList, len(n))
 	for i, v := range n {
 		wearAtFormat := ""
@@ -64,10 +74,29 @@ func (n NeckRingSlice) ToPB(neckRingStatus map[pasturePb.NeckRingStatus_Kind]str
 			CowId:        int32(v.CowId),
 			WearAtFormat: wearAtFormat,
 			WearDays:     wearDays,
+			IsBind:       v.IsBind,
+			IsBindName:   neckRingIsBind[v.IsBind],
+			ErrorReason:  v.ErrorReason,
 			Status:       v.Status,
 			StatusName:   neckRingStatus[v.Status],
-			ErrorReason:  v.ErrorReason,
 		}
 	}
 	return res
 }
+
+type NeckRingModel struct {
+	Id             int64                         `json:"id"`
+	PastureId      int64                         `json:"pastureId"`
+	NeckRingNumber string                        `json:"neckRingNumber"`
+	CowId          int64                         `json:"cowId"`
+	PenName        string                        `json:"penName"`
+	EarNumber      string                        `json:"earNumber"`
+	WearAt         int64                         `json:"wearAt"`
+	IsBind         pasturePb.NeckRingIsBind_Kind `json:"isBind"`
+	Status         pasturePb.NeckRingStatus_Kind `json:"status"`
+	ErrorReason    string                        `json:"errorReason"`
+	OperationId    int32                         `json:"operationId"`
+	OperationName  string                        `json:"operationName"`
+	CreatedAt      int64                         `json:"createdAt"`
+	UpdatedAt      int64                         `json:"updatedAt"`
+}

+ 13 - 11
model/neck_ring_bind_log.go

@@ -1,23 +1,24 @@
 package model
 
 type NeckRingBindLog struct {
-	Id                  int64  `json:"id"`
-	PastureId           int64  `json:"pastureId"`
-	NeckRingNumber      string `json:"neckRingNumber"`
-	CowId               int64  `json:"cowId"`
-	EarNumber           string `json:"earNumber"`
-	OperationStatusName string `json:"operationStatusName"`
-	MessageId           int64  `json:"messageId"`
-	MessageName         string `json:"messageName"`
-	CreatedAt           int64  `json:"createdAt"`
-	UpdatedAt           int64  `json:"updatedAt"`
+	Id             int64  `json:"id"`
+	PastureId      int64  `json:"pastureId"`
+	NeckRingNumber string `json:"neckRingNumber"`
+	CowId          int64  `json:"cowId"`
+	EarNumber      string `json:"earNumber"`
+	OperationName  string `json:"operationName"`
+	Remarks        string `json:"remarks"`
+	MessageId      int64  `json:"messageId"`
+	MessageName    string `json:"messageName"`
+	CreatedAt      int64  `json:"createdAt"`
+	UpdatedAt      int64  `json:"updatedAt"`
 }
 
 func (n *NeckRingBindLog) TableName() string {
 	return "neck_ring_bind_log"
 }
 
-func NewNeckRingBindLog(pastureId int64, number string, cowInfo *Cow, currUser *SystemUser) *NeckRingBindLog {
+func NewNeckRingBindLog(pastureId int64, number string, cowInfo *Cow, currUser *SystemUser, remarks string) *NeckRingBindLog {
 	return &NeckRingBindLog{
 		PastureId:      pastureId,
 		NeckRingNumber: number,
@@ -25,5 +26,6 @@ func NewNeckRingBindLog(pastureId int64, number string, cowInfo *Cow, currUser *
 		EarNumber:      cowInfo.EarNumber,
 		MessageId:      currUser.Id,
 		MessageName:    currUser.Name,
+		Remarks:        remarks,
 	}
 }

+ 2 - 0
model/neck_ring_estrus.go

@@ -20,6 +20,8 @@ type NeckRingEstrus struct {
 	MaxHigh        int32                      `json:"maxHigh"`
 	MaxCft         int32                      `json:"maxCft"`
 	CheckResult    pasturePb.CheckResult_Kind `json:"checkResult"`
+	CheckUserId    int64                      `json:"checkUserId"`
+	CheckUserName  string                     `json:"checkUserName"`
 	CheckAt        int64                      `json:"checkAt"`
 	IsShow         pasturePb.IsShow_Kind      `json:"isShow"`
 	CreatedAt      int64                      `json:"createdAt"`

+ 3 - 3
model/pen_behavior.go

@@ -85,9 +85,9 @@ func (p PenBehaviorSlice) ToPB() *pasturePb.BarnBehaviorCurveItem {
 			dateTime = dt.Format(LayoutMinute)
 		}
 		res.DateTime = append(res.DateTime, dateTime)
-		res.Rumina = append(res.Rumina, v.RuminaStd)
-		res.Intake = append(res.Intake, v.IntakeStd)
-		res.Rest = append(res.Rest, v.RestStd)
+		res.Rumina = append(res.Rumina, v.RuminaRate)
+		res.Intake = append(res.Intake, v.IntakeRate)
+		res.Rest = append(res.Rest, v.RestRate)
 		res.WeekAvgRumina = append(res.WeekAvgRumina, v.WeekRuminaRate)
 		res.WeekAvgIntake = append(res.WeekAvgIntake, v.WeekIntakeRate)
 		res.WeekAvgReset = append(res.WeekAvgReset, v.WeekRestRate)

+ 16 - 3
module/backend/analysis.go

@@ -94,7 +94,7 @@ func (s *StoreEntry) WeightScatterPlot(ctx context.Context, req *pasturePb.Searc
 
 }
 
-func (s *StoreEntry) WeightRange(ctx context.Context, req *pasturePb.WeightRangeRequest) (*pasturePb.WeightRangeResponse, error) {
+func (s *StoreEntry) WeightRange(ctx context.Context, req *pasturePb.WeightRangeRequest, pagination *pasturePb.PaginationModel) (*pasturePb.WeightRangeResponse, error) {
 	userModel, err := s.GetUserModel(ctx)
 	if err != nil {
 		return nil, xerr.WithStack(err)
@@ -127,7 +127,9 @@ func (s *StoreEntry) WeightRange(ctx context.Context, req *pasturePb.WeightRange
 			ELSE '750+'  
 		END AS weight_range, 
 		COUNT(*) AS count `,
-	).Group("weight_range").Order("MIN(current_weight)").Find(&cowWeightRange).Error; err != nil {
+	).Group("weight_range").
+		Order("MIN(current_weight)").
+		Find(&cowWeightRange).Error; err != nil {
 		return nil, err
 	}
 
@@ -141,6 +143,9 @@ func (s *StoreEntry) WeightRange(ctx context.Context, req *pasturePb.WeightRange
 					Header: make([]string, 0),
 					Data:   make([]int32, 0),
 				},
+				Total:    0,
+				PageSize: pagination.PageSize,
+				Page:     pagination.Page,
 			},
 		}, nil
 	}
@@ -162,7 +167,12 @@ func (s *StoreEntry) WeightRange(ctx context.Context, req *pasturePb.WeightRange
 		pref.Where("current_weight BETWEEN  ? AND ? ", req.MinWeight*1000, req.MaxWeight*1000)
 	}
 
-	if err = pref.Find(&cowList).Error; err != nil {
+	var count int64
+	if err = pref.Order("id desc").
+		Count(&count).
+		Limit(int(pagination.PageSize)).
+		Offset(int(pagination.PageOffset)).
+		Find(&cowList).Error; err != nil {
 		return nil, err
 	}
 
@@ -176,6 +186,9 @@ func (s *StoreEntry) WeightRange(ctx context.Context, req *pasturePb.WeightRange
 				Header: header,
 				Data:   data,
 			},
+			Total:    int32(count),
+			PageSize: pagination.PageSize,
+			Page:     pagination.Page,
 		},
 	}, nil
 }

+ 7 - 5
module/backend/analysis_other.go

@@ -303,19 +303,21 @@ 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).
 		Where("pasture_id = ?", userModel.AppPasture.Id)
 	if req.AnalysisMethod == pasturePb.SaleCowAnalysisMethod_Months {
-		pref.Select(`DATE_FORMAT(FROM_UNIXTIME(sale_at), '%Y-%m') AS statisticMethod`)
+		pref.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,DATE_FORMAT(FROM_UNIXTIME(sale_at), '%Y-%m') AS statistic_method`)
 	}
 
 	if req.AnalysisMethod == pasturePb.SaleCowAnalysisMethod_Dealer {
-		pref.Select(`dealer_name as statisticMethod`)
+		pref.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,dealer_name as statistic_method`)
 	}
 
-	if err = pref.Group("statisticMethod").Order("statisticMethod ASC").Find(&list).Error; err != nil {
+	if err = pref.Group("statistic_method").
+		Order("statistic_method ASC").
+		Find(&list).Error; err != nil {
 		return nil, xerr.WithStack(err)
 	}
 

+ 61 - 24
module/backend/calendar.go

@@ -59,12 +59,9 @@ func (s *StoreEntry) CalendarToDoHistoryList(ctx context.Context, pastureId int6
 		SELECT cow_id,plan_day,'产犊' as calendar_type_name,9 as calendar_type_kind,TIMESTAMPDIFF(DAY, NOW(), FROM_UNIXTIME(end_day)) AS remaining_days 
 			FROM event_calving WHERE status = 2` + whereSql1 + `
 		UNION ALL
-		SELECT cow_id,plan_day,'干奶' as calendar_type_name,9 as calendar_type_kind,TIMESTAMPDIFF(DAY, NOW(), FROM_UNIXTIME(end_day)) AS remaining_days 
-			FROM event_dry_milk WHERE status = 2` + whereSql1 + `
-		UNION ALL
 		SELECT cow_id,disease_at as plan_day,'疾病' as calendar_type_name,7 as calendar_type_kind,0 AS remaining_days 
 			FROM event_cow_disease WHERE health_status IN (2,3) ` + whereSql + `
-	) as a JOIN cow b ON a.cow_id = b.id WHERE 1 = 1 `
+	) as a JOIN cow b ON a.cow_id = b.id `
 
 	completeSql := fmt.Sprintf("%s ORDER BY a.plan_day DESC", sql)
 	if err := s.DB.Raw(completeSql).Find(&calendarToDoList).Error; err != nil {
@@ -89,25 +86,31 @@ func (s *StoreEntry) CalendarToDoList(ctx context.Context, req *pasturePb.Calend
 
 	nowTime := time.Now().Format(model.LayoutDate2)
 	todayCompletedSql := `SELECT a.count as count,a.calendar_type_name as calendar_type_name,a.calendar_type_kind as calendar_type_kind FROM (
-		SELECT count('cow_id') as count,'免疫' as calendar_type_name,1 as calendar_type_kind FROM event_immunization_plan WHERE status = 1 AND DATE_FORMAT(FROM_UNIXTIME(reality_day), '%Y-%m-%d') = ? AND pasture_id = ?
-		UNION ALL
-		SELECT count('cow_id') as count,'同期' as calendar_type_name,2 as calendar_type_kind FROM event_cow_same_time WHERE status = 1 AND DATE_FORMAT(FROM_UNIXTIME(reality_day), '%Y-%m-%d') = ? AND pasture_id = ?
+		SELECT count('cow_id') as count,'免疫' as calendar_type_name,1 as calendar_type_kind FROM event_immunization_plan 
+			WHERE status = 1 AND DATE_FORMAT(FROM_UNIXTIME(reality_day), '%Y-%m-%d') = ? AND pasture_id = ?
 		UNION ALL
-		SELECT count('cow_id') as count,'孕检' as calendar_type_name,4 as calendar_type_kind FROM event_pregnant_check WHERE status = 1 AND DATE_FORMAT(FROM_UNIXTIME(reality_day), '%Y-%m-%d') = ? AND pasture_id = ?
+		SELECT count('cow_id') as count,'同期' as calendar_type_name,2 as calendar_type_kind FROM event_cow_same_time 
+			WHERE status = 1 AND DATE_FORMAT(FROM_UNIXTIME(reality_day), '%Y-%m-%d') = ? AND pasture_id = ?
 		UNION ALL
-		SELECT count('cow_id') as count,'断奶' as calendar_type_name,6 as calendar_type_kind FROM event_weaning WHERE status = 1 AND DATE_FORMAT(FROM_UNIXTIME(reality_day), '%Y-%m-%d') = ? AND pasture_id = ?
+		SELECT count('cow_id') as count,'孕检' as calendar_type_name,4 as calendar_type_kind FROM event_pregnant_check 
+			WHERE status = 1 AND DATE_FORMAT(FROM_UNIXTIME(reality_day), '%Y-%m-%d') = ? AND pasture_id = ?
 		UNION ALL
-		SELECT count('cow_id') as count,'配种' as calendar_type_name,8 as calendar_type_kind FROM event_mating WHERE status = 1 AND DATE_FORMAT(FROM_UNIXTIME(reality_day), '%Y-%m-%d') = ? AND pasture_id = ?
+		SELECT count('cow_id') as count,'断奶' as calendar_type_name,6 as calendar_type_kind FROM event_weaning 
+			WHERE status = 1 AND DATE_FORMAT(FROM_UNIXTIME(reality_day), '%Y-%m-%d') = ? AND pasture_id = ?
 		UNION ALL
-		SELECT count('cow_id') as count,'产犊' as calendar_type_name,9 as calendar_type_kind FROM event_calving WHERE status = 1 AND DATE_FORMAT(FROM_UNIXTIME(reality_day), '%Y-%m-%d') = ? AND pasture_id = ?
+		SELECT count('cow_id') as count,'配种' as calendar_type_name,8 as calendar_type_kind FROM event_mating 
+			WHERE status = 1 AND DATE_FORMAT(FROM_UNIXTIME(reality_day), '%Y-%m-%d') = ? AND pasture_id = ?
 		UNION ALL
-		SELECT count('cow_id') as count,'干奶' as calendar_type_name,10 as calendar_type_kind FROM event_dry_milk WHERE status = 1 AND DATE_FORMAT(FROM_UNIXTIME(reality_day), '%Y-%m-%d') = ? AND pasture_id = ?
+		SELECT count('cow_id') as count,'产犊' as calendar_type_name,9 as calendar_type_kind FROM event_calving 
+			WHERE status = 1 AND DATE_FORMAT(FROM_UNIXTIME(reality_day), '%Y-%m-%d') = ? AND pasture_id = ?
 		UNION ALL
-		SELECT count('cow_id') as count,'疾病' as calendar_type_name,7 as calendar_type_kind FROM event_cow_disease WHERE diagnosed_result = 4 AND DATE_FORMAT(FROM_UNIXTIME(curable_at), '%Y-%m-%d') = ? AND pasture_id = ?
+		SELECT count('cow_id') as count,'疾病' as calendar_type_name,7 as calendar_type_kind FROM event_cow_disease 
+			WHERE health_status = 4 AND DATE_FORMAT(FROM_UNIXTIME(curable_at), '%Y-%m-%d') = ? AND pasture_id = ?
 	) as a `
 
-	toDayCompletedList := make([]*model.TodayCompletedData, 0)
-	if err = s.DB.Raw(todayCompletedSql, nowTime, pastureId, nowTime, pastureId, nowTime, pastureId, nowTime, pastureId, nowTime, pastureId, nowTime, pastureId, nowTime, pastureId, nowTime, pastureId).
+	toDayCompletedList := make([]*model.CompletedData, 0)
+	if err = s.DB.Raw(todayCompletedSql, nowTime, pastureId, nowTime, pastureId, nowTime, pastureId, nowTime,
+		pastureId, nowTime, pastureId, nowTime, pastureId, nowTime, pastureId).
 		Find(&toDayCompletedList).Error; err != nil {
 		return nil, xerr.WithStack(err)
 	}
@@ -121,13 +124,45 @@ func (s *StoreEntry) CalendarToDoList(ctx context.Context, req *pasturePb.Calend
 		}
 	}
 
+	historyCount := make([]*model.CompletedData, 0)
+	todayStartTime := util.TimeParseLocalUnix(nowTime)
+	todayEndTime := util.TimeParseLocalEndUnix(nowTime)
+	whereSql := fmt.Sprintf(` WHERE pasture_id = %d AND end_day <= %d  AND (status = %d OR (status = %d AND reality_day BETWEEN %d AND %d ))`,
+		pastureId, todayEndTime, pasturePb.IsShow_No, pasturePb.IsShow_Ok, todayStartTime, todayEndTime)
+	historyCountSql := `SELECT a.count as count,a.calendar_type_kind as calendar_type_kind FROM (
+		SELECT count(cow_id) as count,1 as calendar_type_kind FROM event_immunization_plan ` + whereSql + `
+		UNION ALL
+		SELECT count(cow_id) as count,2 as calendar_type_kind FROM event_cow_same_time ` + whereSql + `
+		UNION ALL
+		SELECT count(cow_id) as count,4 as calendar_type_kind FROM event_pregnant_check ` + whereSql + `
+		UNION ALL
+		SELECT count(cow_id) as count,6 as calendar_type_kind FROM event_weaning ` + whereSql + `
+		UNION ALL
+		SELECT count(cow_id) as count,8 as calendar_type_kind FROM event_mating ` + whereSql + `
+		UNION ALL
+		SELECT count(cow_id) as count,9 as calendar_type_kind FROM event_calving ` + whereSql + `
+		UNION ALL
+		SELECT count(cow_id) as count,7 as calendar_type_kind FROM event_cow_disease WHERE ` +
+		fmt.Sprintf("pasture_id = %d AND (health_status IN (%d,%d) OR (health_status = %d AND curable_at BETWEEN %d AND %d))",
+			pastureId, pasturePb.HealthStatus_Disease, pasturePb.HealthStatus_Treatment, pasturePb.HealthStatus_Curable, todayStartTime, todayEndTime) + `
+	) as a
+`
+	if err = s.DB.Raw(historyCountSql).Find(&historyCount).Error; err != nil {
+		return nil, xerr.WithStack(err)
+	}
+
+	dMap := make(map[pasturePb.CalendarType_Kind]int32)
+	for _, v := range historyCount {
+		dMap[v.CalendarTypeKind] = v.Count
+	}
+
 	list, total := Paginate(calendarToDoList, req, pagination)
 	return &pasturePb.CalendarToDoResponse{
 		Code: http.StatusOK,
 		Msg:  "ok",
 		Data: &pasturePb.CalendarToDoData{
 			List:     list,
-			Progress: ProgressList(calendarToDoList, toDayCompletedCountMap),
+			Progress: ProgressList(dMap, toDayCompletedCountMap),
 			Total:    total,
 			PageSize: pagination.PageSize,
 			Page:     pagination.Page,
@@ -283,7 +318,8 @@ func (s *StoreEntry) SameTimeCowList(ctx context.Context, req *pasturePb.ItemsRe
 		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).
-		Where("a.status = ?", pasturePb.IsShow_No)
+		Where("a.status = ?", pasturePb.IsShow_No).
+		Where("a.plan_day <= ?", time.Now().Unix())
 
 	if req.EndDay != "" {
 		dateTime := util.TimeParseLocalEndUnix(req.EndDay)
@@ -309,7 +345,6 @@ func (s *StoreEntry) SameTimeCowList(ctx context.Context, req *pasturePb.ItemsRe
 		return nil, xerr.WithStack(err)
 	}
 	breedStatusMap := s.CowBreedStatusMap()
-	penMap := s.PenMap(ctx, int64(req.PastureId))
 	sameTimeTypeMap := s.SameTimeTypeMap()
 	return &pasturePb.SameTimeItemResponse{
 		Code: http.StatusOK,
@@ -340,7 +375,7 @@ func (s *StoreEntry) SameTimeCowList(ctx context.Context, req *pasturePb.ItemsRe
 				"abortionAtFormat": "流产日期",
 				"sameTimeName":     "同期名称",
 			},
-			List: model.SameTimeBodySlice(sameTimeBodyList).ToPB(breedStatusMap, penMap, sameTimeTypeMap),
+			List: model.SameTimeBodySlice(sameTimeBodyList).ToPB(breedStatusMap, sameTimeTypeMap),
 		},
 	}, nil
 }
@@ -482,7 +517,7 @@ func (s *StoreEntry) MatingCowList(ctx context.Context, req *pasturePb.ItemsRequ
 	matingItems := make([]*pasturePb.MatingItems, 0)
 	count := int64(0)
 	pref := s.DB.Table(fmt.Sprintf("%s as a", new(model.EventMating).TableName())).
-		Select(`a.id,a.cow_id,a.status,a.ear_number,
+		Select(`a.id,a.cow_id,a.status,a.ear_number,DATE_FORMAT(FROM_UNIXTIME(plan_day), '%Y-%m-%d') as plan_day,
 		CASE a.expose_estrus_type
         	WHEN 1 THEN '脖环揭发'
         	WHEN 2 THEN '脚环/计步器'
@@ -524,10 +559,11 @@ func (s *StoreEntry) MatingCowList(ctx context.Context, req *pasturePb.ItemsRequ
 		Code: http.StatusOK,
 		Msg:  "ok",
 		Data: &pasturePb.MatingItemsData{
-			Total:      int32(count),
-			Page:       pagination.Page,
-			PageSize:   pagination.PageSize,
-			HeaderSort: []string{"id", "cowId", "earNumber", "dayAge", "lact", "penName", "breedStatusName", "cowTypeName", "calvingAge", "abortionAge", "exposeEstrusTypeName", "lastCalvingAtFormat"},
+			Total:    int32(count),
+			Page:     pagination.Page,
+			PageSize: pagination.PageSize,
+			HeaderSort: []string{"id", "cowId", "earNumber", "dayAge", "lact", "penName", "planDay", "breedStatusName",
+				"cowTypeName", "calvingAge", "abortionAge", "exposeEstrusTypeName", "lastCalvingAtFormat"},
 			Header: map[string]string{
 				"id":                   "编号",
 				"cowId":                "牛号",
@@ -542,6 +578,7 @@ func (s *StoreEntry) MatingCowList(ctx context.Context, req *pasturePb.ItemsRequ
 				"status":               "状态",
 				"exposeEstrusTypeName": "发情揭发方式",
 				"lastCalvingAtFormat":  "产犊日期",
+				"planDay":              "计划配种时间",
 			},
 			List: matingItems,
 		},

+ 13 - 8
module/backend/calendar_more.go

@@ -271,27 +271,32 @@ func Paginate(slice []*pasturePb.CalendarToDoList, req *pasturePb.CalendarToDoRe
 	return newSlice[start:end], total
 }
 
-func ProgressList(dataList []*pasturePb.CalendarToDoList, toDayCompletedCountMap map[pasturePb.CalendarType_Kind]*pasturePb.ProgressList) map[string]*pasturePb.ProgressList {
+func ProgressList(dMap map[pasturePb.CalendarType_Kind]int32, toDayCompletedCountMap map[pasturePb.CalendarType_Kind]*pasturePb.ProgressList) map[string]*pasturePb.ProgressList {
 	res := make(map[string]*pasturePb.ProgressList)
-	dMap := map[pasturePb.CalendarType_Kind][]*pasturePb.CalendarToDoList{}
-
-	for _, v := range dataList {
-		dMap[v.CalendarType] = append(dMap[v.CalendarType], v)
-	}
 
 	for kind, cc := range toDayCompletedCountMap {
+		incompleteTotal := int32(0)
+		if it, ok := dMap[kind]; ok {
+			incompleteTotal = it
+		}
 		if res[cc.CalendarName] == nil {
 			res[cc.CalendarName] = &pasturePb.ProgressList{
 				CalendarTypeKind: kind,
 				CalendarName:     cc.CalendarName,
 				Color:            model.CalendarTypeColorMap[kind],
 				CompletedCount:   cc.CompletedCount,
-				IncompleteTotal:  int32(len(dMap[kind])),
+				IncompleteTotal:  incompleteTotal,
 			}
 		}
 
 		if res[cc.CalendarName].IncompleteTotal > 0 && res[cc.CalendarName].CompletedCount > 0 {
-			res[cc.CalendarName].Progress = strconv.FormatFloat(float64(res[cc.CalendarName].CompletedCount)/float64(res[cc.CalendarName].IncompleteTotal)*100, 'f', 2, 64)
+			res[cc.CalendarName].Progress = strconv.FormatFloat(
+				float64(res[cc.CalendarName].CompletedCount)/
+					float64(res[cc.CalendarName].IncompleteTotal)*100,
+				'f',
+				2,
+				64,
+			) + "%"
 		} else {
 			res[cc.CalendarName].Progress = "0%"
 		}

+ 1 - 1
module/backend/config_data.go

@@ -261,7 +261,7 @@ func (s *StoreEntry) ImmunizationCowTypeEnumList(isAll string) []*pasturePb.Conf
 		Label:    "后备牛",
 		Disabled: true,
 	}, &pasturePb.ConfigOptionsList{
-		Value:    int32(pasturePb.CowType_Reserve_Calf),
+		Value:    int32(pasturePb.CowType_Breeding_Calf),
 		Label:    "种母牛",
 		Disabled: true,
 	}, &pasturePb.ConfigOptionsList{

+ 22 - 12
module/backend/config_data_breed.go

@@ -234,34 +234,44 @@ func (s *StoreEntry) SaleCowAnalysisMethodEnumList(isAll string) []*pasturePb.Co
 	return configOptions
 }
 
-func (s *StoreEntry) NeckRingStatusEnumList(isAll string) []*pasturePb.ConfigOptionsList {
+func (s *StoreEntry) NeckRingIsBindEnumList(isAll string) []*pasturePb.ConfigOptionsList {
 	configOptions := make([]*pasturePb.ConfigOptionsList, 0)
 	if isAll == model.IsAllYes {
 		configOptions = append(configOptions,
 			&pasturePb.ConfigOptionsList{
-				Value:    int32(pasturePb.NeckRingStatus_Invalid),
+				Value:    int32(pasturePb.NeckRingIsBind_Invalid),
 				Label:    "全部",
 				Disabled: true,
 			})
 	}
 	configOptions = append(configOptions, &pasturePb.ConfigOptionsList{
-		Value:    int32(pasturePb.NeckRingStatus_Unbind),
-		Label:    "绑定",
+		Value:    int32(pasturePb.NeckRingIsBind_Bind),
+		Label:    "绑定",
 		Disabled: true,
 	}, &pasturePb.ConfigOptionsList{
-		Value:    int32(pasturePb.NeckRingStatus_Bind),
-		Label:    "绑定",
+		Value:    int32(pasturePb.NeckRingIsBind_Unbind),
+		Label:    "绑定",
 		Disabled: true,
-	}, &pasturePb.ConfigOptionsList{
+	})
+	return configOptions
+}
+
+func (s *StoreEntry) NeckRingStatusEnumList(isAll string) []*pasturePb.ConfigOptionsList {
+	configOptions := make([]*pasturePb.ConfigOptionsList, 0)
+	if isAll == model.IsAllYes {
+		configOptions = append(configOptions,
+			&pasturePb.ConfigOptionsList{
+				Value:    int32(pasturePb.NeckRingStatus_Invalid),
+				Label:    "全部",
+				Disabled: true,
+			})
+	}
+	configOptions = append(configOptions, &pasturePb.ConfigOptionsList{
 		Value:    int32(pasturePb.NeckRingStatus_Normal),
 		Label:    "正常",
 		Disabled: true,
 	}, &pasturePb.ConfigOptionsList{
-		Value:    int32(pasturePb.NeckRingStatus_Offline),
-		Label:    "在线",
-		Disabled: true,
-	}, &pasturePb.ConfigOptionsList{
-		Value:    int32(pasturePb.NeckRingStatus_Error),
+		Value:    int32(pasturePb.NeckRingStatus_Abnormal),
 		Label:    "异常",
 		Disabled: true,
 	})

+ 17 - 3
module/backend/cow.go

@@ -139,7 +139,7 @@ func (s *StoreEntry) EventList(ctx context.Context, req *pasturePb.SearchCowEven
 		return nil, xerr.WithStack(err)
 	}
 	eventCowLogList := make([]*model.EventCowLog, 0)
-	cowInfo, err := s.GetCowInfoByEarNumber(ctx, userModel.AppPasture.Id, req.EarNumber)
+	cowInfo, err := s.GetCowEventByEarNumber(ctx, userModel.AppPasture.Id, req.EarNumber)
 	if err != nil {
 		return nil, xerr.Customf("错误的牛只信息: %s", req.EarNumber)
 	}
@@ -182,7 +182,7 @@ func (s *StoreEntry) BehaviorCurve(ctx context.Context, req *pasturePb.CowBehavi
 	if err != nil {
 		return nil, xerr.WithStack(err)
 	}
-	cowInfo, err := s.GetCowInfoByEarNumber(ctx, userModel.AppPasture.Id, req.EarNumber)
+	cowInfo, err := s.GetCowEventByEarNumber(ctx, userModel.AppPasture.Id, req.EarNumber)
 	if err != nil {
 		return nil, xerr.Customf("错误的牛只信息: %d", req.CowId)
 	}
@@ -206,7 +206,15 @@ func (s *StoreEntry) BehaviorCurve(ctx context.Context, req *pasturePb.CowBehavi
 	}
 
 	data := model.NeckActiveHabitSlice(neckActiveHabitList).ToPB(req.CurveName)
-	data.EventMap = s.EventTypeMap()
+	eventMapList := s.EventTypeMap()
+	for k, v := range eventMapList {
+		if k == pasturePb.EventType_Enter || k == pasturePb.EventType_Body_Score || k == pasturePb.EventType_Birth ||
+			k == pasturePb.EventType_Weaning || k == pasturePb.EventType_Sale || k == pasturePb.EventType_Weight ||
+			k == pasturePb.EventType_Castrated {
+			continue
+		}
+		data.EventMap[k] = v
+	}
 	data.LowActivity = 62
 	data.MiddleActivity = 80
 	// 牛只事件列表
@@ -222,6 +230,12 @@ func (s *StoreEntry) BehaviorCurve(ctx context.Context, req *pasturePb.CowBehavi
 	}
 
 	for _, v := range eventLogList {
+		if v.EventType == pasturePb.EventType_Enter || v.EventType == pasturePb.EventType_Body_Score ||
+			v.EventType == pasturePb.EventType_Birth || v.EventType == pasturePb.EventType_Weaning ||
+			v.EventType == pasturePb.EventType_Sale || v.EventType == pasturePb.EventType_Weight ||
+			v.EventType == pasturePb.EventType_Castrated {
+			continue
+		}
 		eventAt := time.Unix(v.EventAt, 0)
 		if data.EventList[v.EventTypeName] == nil {
 			data.EventList[v.EventTypeName] = make([]string, 0)

+ 16 - 0
module/backend/enum_map.go

@@ -191,6 +191,14 @@ func (s *StoreEntry) MultiFactorAnalysisMethodMap() map[pasturePb.MultiFactorAna
 	return res
 }
 
+func (s *StoreEntry) NeckRingIsBindMap() map[pasturePb.NeckRingIsBind_Kind]string {
+	res := make(map[pasturePb.NeckRingIsBind_Kind]string)
+	for _, v := range s.NeckRingIsBindEnumList("") {
+		res[pasturePb.NeckRingIsBind_Kind(v.Value)] = v.Label
+	}
+	return res
+}
+
 func (s *StoreEntry) NeckRingStatusMap() map[pasturePb.NeckRingStatus_Kind]string {
 	res := make(map[pasturePb.NeckRingStatus_Kind]string)
 	for _, v := range s.NeckRingStatusEnumList("") {
@@ -199,6 +207,14 @@ func (s *StoreEntry) NeckRingStatusMap() map[pasturePb.NeckRingStatus_Kind]strin
 	return res
 }
 
+func (s *StoreEntry) SaleCowAnalysisMap() map[pasturePb.SaleCowAnalysisMethod_Kind]string {
+	res := make(map[pasturePb.SaleCowAnalysisMethod_Kind]string)
+	for _, v := range s.SaleCowAnalysisMethodEnumList("") {
+		res[pasturePb.SaleCowAnalysisMethod_Kind(v.Value)] = v.Label
+	}
+	return res
+}
+
 func (s *StoreEntry) OutTypeMap() map[pasturePb.OutType_Kind]string {
 	res := make(map[pasturePb.OutType_Kind]string)
 	for _, v := range s.OutTypeEnumList("") {

+ 1 - 1
module/backend/enum_options.go

@@ -206,7 +206,7 @@ func (s *StoreEntry) SystemBaseConfigOptions(ctx context.Context, optionsName, i
 		"lactIntervalSymbol":         s.LactIntervalSymbolEnumList,
 		"multiFactorAnalysisMethod":  s.MultiFactorAnalysisMethodEnumList,
 		"saleCowAnalysisMethod":      s.SaleCowAnalysisMethodEnumList,
-		"neckRingStatus":             s.NeckRingStatusEnumList,
+		"neckRingIsBind":             s.NeckRingIsBindEnumList,
 		"outType":                    s.OutTypeEnumList,
 		"auditStatus":                s.AuditStatusEnumList,
 		"pregnantCheckName":          s.PregnantCheckNameEnumList,

+ 0 - 3
module/backend/event_base.go

@@ -106,9 +106,6 @@ func (s *StoreEntry) CreateEnter(ctx context.Context, req *pasturePb.EventEnterR
 	}
 
 	newCow := model.NewEnterCow(userModel.AppPasture.Id, req, penMap)
-	if req.BirthAt > 0 {
-		newCow.DayAge = newCow.GetDayAge()
-	}
 	if err = s.DB.Transaction(func(tx *gorm.DB) error {
 		// 新增牛只信息
 		if err = tx.Model(new(model.Cow)).Create(newCow).Error; err != nil {

+ 95 - 20
module/backend/event_base_more.go

@@ -44,11 +44,18 @@ func (s *StoreEntry) DeathBatch(ctx context.Context, req *pasturePb.EventDeathBa
 		if name, ok := s.CowDeathDestinationMap()[item.DeathDestinationKind]; ok {
 			item.DeathDestinationName = name
 		}
+
 		newEventDeath := model.NewEventDeath(userModel.AppPasture.Id, cow, item, userModel.SystemUser, operationUser)
-		newEventDeathList = append(newEventDeathList, &model.EventDeathModel{
+		eventDeathModel := &model.EventDeathModel{
 			Cow:        cow,
 			EventDeath: newEventDeath,
-		})
+			NeckRing:   nil,
+		}
+
+		if neckRing, ok := s.NeckRingIsExist(userModel.AppPasture.Id, item.EarNumber); ok && neckRing != nil {
+			eventDeathModel.NeckRing = neckRing
+		}
+		newEventDeathList = append(newEventDeathList, eventDeathModel)
 	}
 
 	if err = s.DB.Transaction(func(tx *gorm.DB) error {
@@ -67,6 +74,22 @@ func (s *StoreEntry) DeathBatch(ctx context.Context, req *pasturePb.EventDeathBa
 				return xerr.WithStack(err)
 			}
 
+			// 解绑脖环
+			if item.NeckRing != nil {
+				item.NeckRing.EventUnBindUpdate()
+				if err = tx.Model(new(model.NeckRing)).
+					Select("is_bind", "cow_id", "ear_number", "wear_at", "error_reason", "operation_id", "operation_name").
+					Where("id = ?", item.NeckRing.Id).
+					Updates(item.NeckRing).Error; err != nil {
+					return xerr.WithStack(err)
+				}
+
+				newNeckRingBindLog := model.NewNeckRingBindLog(userModel.AppPasture.Id, item.NeckRing.NeckRingNumber, item.Cow, userModel.SystemUser, "死亡解绑")
+				if err = tx.Model(new(model.NeckRingBindLog)).Create(newNeckRingBindLog).Error; err != nil {
+					return xerr.WithStack(err)
+				}
+			}
+
 			// 记录事件日志
 			cowLogs := s.SubmitEventLog(ctx, userModel.AppPasture.Id, item.Cow, pasturePb.EventType_Death, item.EventDeath)
 			if err = tx.Table(cowLogs.TableName()).Create(cowLogs).Error; err != nil {
@@ -184,6 +207,7 @@ func (s *StoreEntry) CowSaleCreate(ctx context.Context, req *pasturePb.EventCowS
 
 	newEventSale := model.NewEventSale(userModel.AppPasture.Id, dealerInfo, req, operationUser, userModel.SystemUser)
 	eventCowLogList := make([]*model.EventCowLog, 0)
+	neckRingList := make([]*model.NeckRing, 0)
 	for _, cow := range cowList {
 		var newEventCowLog *model.EventCowLog
 		if req.SalesType == pasturePb.SalesType_Sales {
@@ -193,38 +217,81 @@ func (s *StoreEntry) CowSaleCreate(ctx context.Context, req *pasturePb.EventCowS
 			newEventCowLog = s.SubmitEventLog(ctx, userModel.AppPasture.Id, cow, pasturePb.EventType_Out, req)
 		}
 		eventCowLogList = append(eventCowLogList, newEventCowLog)
+		if cow.NeckRingNumber != "" {
+			if neckRing, ok := s.NeckRingIsExist(userModel.AppPasture.Id, cow.NeckRingNumber); ok && neckRing != nil {
+				neckRingList = append(neckRingList, neckRing)
+			}
+		}
+	}
+
+	eventSaleModel := &model.EventSaleModel{
+		CowList:          cowList,
+		SalesType:        req.SalesType,
+		EventSale:        newEventSale,
+		EventSaleCarList: model.NewEventSaleCarList(userModel.AppPasture.Id, newEventSale.Id, newEventSale.SaleAt, req.SaleVehicleItems),
+		EventSaleCowList: model.NewEventSaleCowList(userModel.AppPasture.Id, newEventSale.Id, cowList),
+		NeckRingList:     neckRingList,
+		EventCowLog:      eventCowLogList,
+		SaleAt:           int64(req.SaleAt),
 	}
 
 	if err = s.DB.Transaction(func(tx *gorm.DB) error {
 		// 创建销售数据
 		if err = tx.Model(new(model.EventSale)).
-			Create(newEventSale).Error; err != nil {
+			Create(eventSaleModel.EventSale).Error; err != nil {
 			return xerr.WithStack(err)
 		}
 
-		newEventSaleCarList := model.NewEventSaleCarList(userModel.AppPasture.Id, newEventSale.Id, newEventSale.SaleAt, req.SaleVehicleItems)
-		if err = tx.Model(new(model.EventSaleCar)).
-			Create(&newEventSaleCarList).Error; err != nil {
-			return xerr.WithStack(err)
+		if len(eventSaleModel.EventSaleCarList) > 0 {
+			if err = tx.Model(new(model.EventSaleCar)).
+				Create(&eventSaleModel.EventSaleCarList).Error; err != nil {
+				return xerr.WithStack(err)
+			}
 		}
 
-		// 更新牛只状态
-		if err = tx.Model(new(model.Cow)).Where("ear_number IN (?)", req.EarNumbers).
-			Update("admission_status", pasturePb.AdmissionStatus_Sale).Error; err != nil {
-			return xerr.WithStack(err)
+		// 新增销售牛只数据
+		if len(eventSaleModel.EventSaleCowList) > 0 {
+			if err = tx.Model(new(model.EventSaleCow)).
+				Create(&eventSaleModel.EventSaleCowList).Error; err != nil {
+				return xerr.WithStack(err)
+			}
 		}
 
-		// 新增销售牛只数据
-		newEventSaleCowList := model.NewEventSaleCowList(userModel.AppPasture.Id, newEventSale.Id, cowList)
-		if err = tx.Model(new(model.EventSaleCow)).
-			Create(&newEventSaleCowList).Error; err != nil {
-			return xerr.WithStack(err)
+		if len(eventSaleModel.CowList) > 0 {
+			for _, cow := range eventSaleModel.CowList {
+				cow.EventSaleUpdate(eventSaleModel.SaleAt, eventSaleModel.SalesType)
+				if err = tx.Model(cow).
+					Select("admission_status", "departure_at", "neck_ring_number", "HealthStatus").
+					Where("id = ?", cow.Id).
+					Updates(cow).Error; err != nil {
+					return xerr.WithStack(err)
+				}
+			}
+		}
+
+		if len(eventSaleModel.NeckRingList) > 0 {
+			for _, neckRing := range eventSaleModel.NeckRingList {
+				cowInfo := &model.Cow{Id: neckRing.CowId, EarNumber: neckRing.EarNumber}
+				neckRing.EventUnBindUpdate()
+				if err = tx.Model(new(model.NeckRing)).
+					Select("is_bind", "cow_id", "ear_number", "wear_at", "error_reason", "operation_id", "operation_name").
+					Where("id = ?", neckRing).
+					Updates(neckRing).Error; err != nil {
+					return xerr.WithStack(err)
+				}
+				newNeckRingBindLog := model.NewNeckRingBindLog(userModel.AppPasture.Id, neckRing.NeckRingNumber, cowInfo, userModel.SystemUser, "死亡解绑")
+				if err = tx.Model(new(model.NeckRingBindLog)).Create(newNeckRingBindLog).Error; err != nil {
+					return xerr.WithStack(err)
+				}
+			}
 		}
 
 		//  记录日志
-		if len(eventCowLogList) > 0 {
-			if err = tx.Model(new(model.EventCowLog)).Create(&eventCowLogList).Error; err != nil {
-				return xerr.WithStack(err)
+		if len(eventSaleModel.EventCowLog) > 0 {
+			for _, cowLog := range eventSaleModel.EventCowLog {
+				if err = tx.Table(cowLog.TableName()).Create(cowLog).Error; err != nil {
+					return xerr.WithStack(err)
+				}
 			}
 		}
 
@@ -288,11 +355,19 @@ func (s *StoreEntry) CowSaleList(ctx context.Context, req *pasturePb.EventCowSal
 		return nil, xerr.WithStack(err)
 	}
 
+	eventSaleCowMap := make(map[int64][]*model.EventSaleCow)
+	for _, cow := range eventSaleCowList {
+		if eventSaleCowMap[cow.SaleId] == nil {
+			eventSaleCowMap[cow.SaleId] = make([]*model.EventSaleCow, 0)
+		}
+		eventSaleCowMap[cow.SaleId] = append(eventSaleCowMap[cow.SaleId], cow)
+	}
+
 	return &pasturePb.EventCowSaleResponse{
 		Code: http.StatusOK,
 		Msg:  "ok",
 		Data: &pasturePb.EventCowSaleData{
-			List:     model.EventSaleSlice(eventSale).ToPB(eventSaleCarMap, eventSaleCowList),
+			List:     model.EventSaleSlice(eventSale).ToPB(eventSaleCarMap, eventSaleCowMap),
 			Total:    int32(count),
 			PageSize: pagination.PageSize,
 			Page:     pagination.Page,

+ 149 - 11
module/backend/event_breed.go

@@ -4,6 +4,7 @@ import (
 	"context"
 	"fmt"
 	"kpt-pasture/model"
+	"kpt-pasture/util"
 	"net/http"
 	"strings"
 	"time"
@@ -199,11 +200,11 @@ func (s *StoreEntry) SameTimeCreate(ctx context.Context, req *pasturePb.EventSam
 		return xerr.Customf("该药品不存在: %d", req.DrugsId)
 	}
 	req.DrugsName = drugs.Name
-	eventCowSameTime.EventUpdate(drugs, req.Usage, req.Remarks, operationUser, userModel.SystemUser)
+	eventCowSameTime.EventUpdate(drugs, req.Usage, int64(req.SameTimeAt), req.Remarks, operationUser, userModel.SystemUser)
 
 	if err = s.DB.Transaction(func(tx *gorm.DB) error {
 		if err = tx.Model(new(model.EventCowSameTime)).
-			Select("status", "drugs_id", "unit", "usage", "remarks", "operation_id", "operation_name").
+			Select("status", "drugs_id", "reality_day", "unit", "usage", "remarks", "operation_id", "operation_name").
 			Where("id = ?", eventCowSameTime.Id).
 			Updates(eventCowSameTime).Error; err != nil {
 			return xerr.WithStack(err)
@@ -258,9 +259,9 @@ func (s *StoreEntry) SameTimeBatch(ctx context.Context, req *pasturePb.EventSame
 	if err = s.DB.Transaction(func(tx *gorm.DB) error {
 		for _, v := range eventCowSameTimeList {
 			// 更新SameTime
-			v.EventUpdate(drugs, req.Usage, req.Remarks, userModel.SystemUser, operationUser)
+			v.EventUpdate(drugs, req.Usage, int64(req.SameTimeAt), req.Remarks, userModel.SystemUser, operationUser)
 			if err = tx.Model(new(model.EventCowSameTime)).
-				Select("status", "drugs_id", "unit", "usage", "remarks", "operation_id", "operation_name").
+				Select("status", "drugs_id", "unit", "reality_day", "usage", "remarks", "operation_id", "operation_name").
 				Where("id = ?", v.Id).
 				Updates(v).Error; err != nil {
 				return xerr.WithStack(err)
@@ -374,31 +375,34 @@ func (s *StoreEntry) EstrusList(ctx context.Context, req *pasturePb.SearchEventR
 	}, nil
 }
 
-func (s *StoreEntry) EstrusBatchMating(ctx context.Context, req *pasturePb.EventNaturalEstrusBatch) (err error) {
+func (s *StoreEntry) EstrusBatchMating(ctx context.Context, req *pasturePb.EventEstrusBatch) (err error) {
 	userModel, err := s.GetUserModel(ctx)
 	if err != nil {
 		return xerr.WithStack(err)
 	}
 
-	eventEstrusList, eventMatingList, err := s.EstrusCheckDataCheck(ctx, userModel, req.Items)
+	eventEstrusBatch, err := s.EstrusCheckDataCheck(ctx, userModel, req)
 	if err != nil {
 		return xerr.WithStack(err)
 	}
 
 	if err = s.DB.Transaction(func(tx *gorm.DB) error {
-		if len(eventMatingList) > 0 {
-			if err = tx.Model(new(model.EventMating)).Create(eventMatingList).Error; err != nil {
+		if len(eventEstrusBatch.EventMatingList) > 0 {
+			if err = tx.Model(new(model.EventMating)).
+				Create(eventEstrusBatch.EventMatingList).Error; err != nil {
 				return xerr.WithStack(err)
 			}
 		}
 
-		if len(eventEstrusList) > 0 {
-			if err = tx.Model(new(model.EventEstrus)).Create(eventEstrusList).Error; err != nil {
+		if len(eventEstrusBatch.EventEstrusList) > 0 {
+			if err = tx.Model(new(model.EventEstrus)).
+				Create(eventEstrusBatch.EventEstrusList).Error; err != nil {
 				return xerr.WithStack(err)
 			}
 		}
+
 		// 记录事件日志
-		for _, v := range eventEstrusList {
+		for _, v := range eventEstrusBatch.EventEstrusList {
 			cow, _ := s.GetCowInfoByCowId(ctx, userModel.AppPasture.Id, v.CowId)
 			// 更新牛最近发情时间
 			cow.EstrusUpdate(v.RealityDay)
@@ -413,9 +417,143 @@ func (s *StoreEntry) EstrusBatchMating(ctx context.Context, req *pasturePb.Event
 				return xerr.WithStack(err)
 			}
 		}
+
+		// 判断是否是脖环揭发的数据
+		if eventEstrusBatch.ExposeEstrusType != pasturePb.ExposeEstrusType_Neck_Ring || len(eventEstrusBatch.EarNumbers) <= 0 {
+			return nil
+		}
+
+		nowTime := time.Now().Local()
+		startTime := nowTime.AddDate(0, 0, -1).Format(model.LayoutDate2)
+		endTime := nowTime.AddDate(0, 0, 1).Format(model.LayoutDate2)
+
+		startTimeUnix := util.TimeParseLocalUnix(startTime)
+		endTimeUnix := util.TimeParseLocalUnix(endTime)
+
+		startTime = time.Unix(startTimeUnix, 0).Format(model.LayoutTime)
+		endTime = time.Unix(endTimeUnix, 0).Format(model.LayoutTime)
+
+		neckRingEstrusWarningList := make([]*model.NeckRingEstrusWarning, 0)
+		if err = tx.Model(new(model.NeckRingEstrusWarning)).
+			Where("pasture_id = ?", userModel.AppPasture.Id).
+			Where("ear_number IN ?", eventEstrusBatch.EarNumbers).
+			Where("date_time BETWEEN ? AND ?", startTime, endTime).
+			Where("is_show = ?", pasturePb.IsShow_Ok).
+			Find(&neckRingEstrusWarningList).Error; err != nil {
+			return xerr.WithStack(err)
+		}
+
+		if len(neckRingEstrusWarningList) <= 0 {
+			return nil
+		}
+
+		for _, v := range neckRingEstrusWarningList {
+			if err = tx.Model(new(model.NeckRingEstrusWarning)).
+				Select("is_show").
+				Where("id = ?", v.Id).
+				Updates(map[string]interface{}{
+					"is_show": pasturePb.IsShow_No,
+				}).Error; err != nil {
+				return xerr.WithStack(err)
+			}
+
+			if err = tx.Model(new(model.NeckRingEstrus)).
+				Where("pasture_id = ?", userModel.AppPasture.Id).
+				Where("id = ?", v.NeckRingEstrusId).
+				Updates(map[string]interface{}{
+					"check_result":    pasturePb.CheckResult_Correct,
+					"check_user_id":   userModel.SystemUser.Id,
+					"check_user_name": userModel.SystemUser.Name,
+					"check_at":        time.Now().Local().Unix(),
+					"is_show":         pasturePb.IsShow_No,
+				}).Error; err != nil {
+				return xerr.WithStack(err)
+			}
+		}
+
 		return nil
 	}); err != nil {
 		return xerr.WithStack(err)
 	}
 	return nil
 }
+
+func (s *StoreEntry) NeckRingNoEstrusBatch(ctx context.Context, req *pasturePb.NeckRingNoEstrusBatchRequest) error {
+	userModel, err := s.GetUserModel(ctx)
+	if err != nil {
+		return xerr.WithStack(err)
+	}
+	if len(req.EarNumbers) <= 0 {
+		return xerr.Custom("请选择牛号")
+	}
+
+	nowTime := time.Now().Local()
+	startTime := nowTime.AddDate(0, 0, -1).Format(model.LayoutDate2)
+	endTime := nowTime.AddDate(0, 0, 1).Format(model.LayoutDate2)
+
+	startTimeUnix := util.TimeParseLocalUnix(startTime)
+	endTimeUnix := util.TimeParseLocalUnix(endTime)
+
+	startTime = time.Unix(startTimeUnix, 0).Format(model.LayoutTime)
+	endTime = time.Unix(endTimeUnix, 0).Format(model.LayoutTime)
+	neckRingEstrusWarningList := make([]*model.NeckRingEstrusWarning, 0)
+	if err = s.DB.Model(new(model.NeckRingEstrusWarning)).
+		Where("pasture_id = ?", userModel.AppPasture.Id).
+		Where("date_time BETWEEN ? AND ?", startTime, endTime).
+		Where("ear_number IN ?", req.EarNumbers).
+		Where("is_show = ?", pasturePb.IsShow_Ok).
+		Find(&neckRingEstrusWarningList).Error; err != nil {
+		return xerr.WithStack(err)
+	}
+
+	if len(neckRingEstrusWarningList) <= 0 {
+		return nil
+	}
+
+	neckRingEstrusIds := make([]int64, 0)
+	for _, v := range neckRingEstrusWarningList {
+		neckRingEstrus := &model.NeckRingEstrus{}
+		if err = s.DB.Model(neckRingEstrus).
+			Where("pasture_id = ?", userModel.AppPasture.Id).
+			Where("id = ?", v.NeckRingEstrusId).
+			Where("is_show = ?", pasturePb.IsShow_Ok).
+			First(neckRingEstrus).Error; err != nil {
+			zaplog.Error("NeckRingNoEstrusBatch", zap.Any("err", err), zap.Any("neckRingEstrusWarning", v))
+			return xerr.Customf("数据异常: %s", v.EarNumber)
+		}
+		neckRingEstrusIds = append(neckRingEstrusIds, neckRingEstrus.Id)
+	}
+
+	if len(neckRingEstrusIds) <= 0 {
+		return nil
+	}
+
+	if err = s.DB.Transaction(func(tx *gorm.DB) error {
+		for _, id := range neckRingEstrusIds {
+			if err = tx.Model(new(model.NeckRingEstrus)).
+				Where("id = ?", id).
+				Updates(map[string]interface{}{
+					"check_result":    pasturePb.CheckResult_Fail,
+					"check_user_id":   userModel.SystemUser.Id,
+					"check_user_name": userModel.SystemUser.Name,
+					"check_at":        time.Now().Local().Unix(),
+					"is_show":         pasturePb.IsShow_No,
+				}).Error; err != nil {
+				return xerr.WithStack(err)
+			}
+
+			if err = tx.Model(new(model.NeckRingEstrusWarning)).
+				Where("neck_ring_estrus_id = ?", id).
+				Updates(map[string]interface{}{
+					"is_show": pasturePb.IsShow_No,
+				}).Error; err != nil {
+				return xerr.WithStack(err)
+			}
+		}
+		return nil
+	}); err != nil {
+		return xerr.WithStack(err)
+	}
+
+	return nil
+}

+ 3 - 1
module/backend/event_breed_more_more.go

@@ -153,6 +153,7 @@ func (s *StoreEntry) ForbiddenMatingBatch(ctx context.Context, req *pasturePb.Ev
 			item.Cow.ForbiddenMatingUpdate(item.ForbiddenMatingAt)
 			if err = tx.Model(new(model.Cow)).
 				Select("is_forbidden_mating", "last_forbidden_mating_at", "breed_status").
+				Where("id = ?", item.Cow.Id).
 				Updates(item.Cow).Error; err != nil {
 				return xerr.WithStack(err)
 			}
@@ -194,7 +195,7 @@ func (s *StoreEntry) ForbiddenMatingList(ctx context.Context, req *pasturePb.Sea
 	}
 
 	if req.StartDayAt > 0 && req.EndDayAt > 0 && req.StartDayAt <= req.EndDayAt {
-		pref.Where("reality_day BETWEEN ? AND ?", req.StartDayAt, req.EndDayAt)
+		pref.Where("forbidden_mating_at BETWEEN ? AND ?", req.StartDayAt, req.EndDayAt)
 	}
 
 	if err = pref.Order("id desc").
@@ -256,6 +257,7 @@ func (s *StoreEntry) UnForbiddenMating(ctx context.Context, req *pasturePb.Event
 			cowInfo.UnForbiddenMatingUpdate()
 			if err = tx.Model(new(model.Cow)).
 				Select("is_forbidden_mating", "last_forbidden_mating_at", "breed_status").
+				Where("id = ?", cowInfo.Id).
 				Updates(cowInfo).Error; err != nil {
 				return xerr.WithStack(err)
 			}

+ 29 - 19
module/backend/event_check.go

@@ -191,68 +191,78 @@ func (s *StoreEntry) PregnantCheckDataCheck(ctx context.Context, pastureId int64
 	return pregnantCheckBatchModelList, nil
 }
 
-func (s *StoreEntry) EstrusCheckDataCheck(ctx context.Context, userModel *model.UserModel, items []*pasturePb.EventNaturalEstrusItems) ([]*model.EventEstrus, []*model.EventMating, error) {
-	eventEstrusList := make([]*model.EventEstrus, 0)
-	eventMatingList := make([]*model.EventMating, 0)
+func (s *StoreEntry) EstrusCheckDataCheck(ctx context.Context, userModel *model.UserModel, req *pasturePb.EventEstrusBatch) (*model.EventEstrusBatch, error) {
+	exposeEstrusType := pasturePb.ExposeEstrusType_Natural_Estrus
+	if req.ExposeEstrusType == pasturePb.ExposeEstrusType_Neck_Ring {
+		exposeEstrusType = pasturePb.ExposeEstrusType_Neck_Ring
+	}
 	unMatingReasonsMap := s.UnMatingReasonsMap()
 
-	for _, item := range items {
+	res := &model.EventEstrusBatch{
+		ExposeEstrusType: exposeEstrusType,
+		EventEstrusList:  make([]*model.EventEstrus, 0),
+		EventMatingList:  make([]*model.EventMating, 0),
+		EarNumbers:       make([]string, 0),
+	}
+
+	for _, item := range req.Items {
 		cowInfo, err := s.GetCowInfoByEarNumber(ctx, userModel.AppPasture.Id, item.EarNumber)
 		if err != nil {
-			return nil, nil, xerr.Custom("牛只信息不存在")
+			return nil, xerr.Custom("牛只信息不存在")
 		}
 
 		if cowInfo.Sex != pasturePb.Genders_Female {
-			return nil, nil, xerr.Custom("该牛只不是母牛")
+			return nil, xerr.Custom("该牛只不是母牛")
 		}
 
 		if item.EstrusAt <= 0 {
-			return nil, nil, xerr.Custom("发情时间不能为空")
+			return nil, xerr.Custom("发情时间不能为空")
 		}
 
 		if int64(item.EstrusAt) <= cowInfo.BirthAt {
-			return nil, nil, xerr.Custom("发情时间不能小于出生时间")
+			return nil, xerr.Custom("发情时间不能小于出生时间")
 		}
 
 		if int64(item.EstrusAt) <= cowInfo.LastCalvingAt {
-			return nil, nil, xerr.Custom("发情时间不能小于产犊时间")
+			return nil, xerr.Custom("发情时间不能小于产犊时间")
 		}
 
 		existsEventEstrus, isExists, err := s.FindEventEstrusByCowId(ctx, userModel.AppPasture.Id, int64(item.CowId))
 		if err != nil {
-			return nil, nil, xerr.WithStack(err)
+			return nil, xerr.WithStack(err)
 		}
 
-		// 如果存在,并且是脖环揭发则跳过
+		// 如果存在,并且当天已经提交过则跳过
 		if isExists {
-			if existsEventEstrus.ExposeEstrusType == pasturePb.ExposeEstrusType_Neck_Ring {
+			/*if existsEventEstrus.ExposeEstrusType == pasturePb.ExposeEstrusType_Neck_Ring {
 				zaplog.Info("EventNaturalEstrusBatch", zap.Any("existsEventEstrus", existsEventEstrus), zap.Any("item", item))
 				continue
-			}
+			}*/
 			realityDay := time.Unix(existsEventEstrus.RealityDay, 0).Format(model.LayoutDate2)
 			planDay := time.Unix(int64(item.EstrusAt), 0).Format(model.LayoutDate2)
 			if realityDay == planDay {
-				return nil, nil, xerr.Customf("该牛只:%d,今天已经提交过发情数据", item.CowId)
+				return nil, xerr.Customf("该牛只:%d,今天已经提交过发情数据", item.CowId)
 			}
 		}
 
 		operationUser, _ := s.GetSystemUserById(ctx, int64(item.OperationId))
 		item.OperationName = operationUser.Name
-		newEventEstrus := model.NewEventEstrus(userModel.AppPasture.Id, cowInfo, pasturePb.ExposeEstrusType_Natural_Estrus,
+		newEventEstrus := model.NewEventEstrus(userModel.AppPasture.Id, cowInfo, exposeEstrusType,
 			pasturePb.IsShow_Ok, item.IsMating, int64(item.EstrusAt), operationUser, userModel.SystemUser)
 
 		if item.IsMating == pasturePb.IsShow_Ok {
-			newEventMating := model.NewEventMating(userModel.AppPasture.Id, cowInfo, time.Now().Unix(), pasturePb.ExposeEstrusType_Natural_Estrus)
-			eventMatingList = append(eventMatingList, newEventMating)
+			newEventMating := model.NewEventMating(userModel.AppPasture.Id, cowInfo, time.Now().Unix(), exposeEstrusType)
+			res.EventMatingList = append(res.EventMatingList, newEventMating)
 		} else {
 			newEventEstrus.UnMatingReasonsKind = item.UnMatingReasonsKind
 			newEventEstrus.UnMatingReasonsName = unMatingReasonsMap[item.UnMatingReasonsKind]
 		}
 
 		newEventEstrus.Remarks = item.Remarks
-		eventEstrusList = append(eventEstrusList, newEventEstrus)
+		res.EventEstrusList = append(res.EventEstrusList, newEventEstrus)
+		res.EarNumbers = append(res.EarNumbers, item.EarNumber)
 	}
-	return eventEstrusList, eventMatingList, nil
+	return res, nil
 }
 
 func (s *StoreEntry) AbortionEventDataCheck(ctx context.Context, userModel *model.UserModel, items []*pasturePb.EventAbortionItem) ([]*AbortionCheckBatchModel, error) {

+ 30 - 24
module/backend/goods.go

@@ -140,20 +140,23 @@ func (s *StoreEntry) NeckRingCreateOrUpdate(ctx context.Context, req *pasturePb.
 	if err = s.DB.Transaction(func(tx *gorm.DB) error {
 		for _, item := range req.Items {
 			number := ""
+
 			cowInfo, err := s.GetCowInfoByEarNumber(ctx, userModel.AppPasture.Id, item.EarNumber)
 			if err != nil {
-				return xerr.Customf("该牛: %d 不存在", item.CowId)
+				return xerr.Customf("该牛: %s 不存在", item.EarNumber)
 			}
-			newNeckRingLog := model.NewNeckRingBindLog(userModel.AppPasture.Id, item.Number, cowInfo, userModel.SystemUser)
-			neckRing, ok, err := s.NeckRingIsExist(ctx, userModel.AppPasture.Id, item.Number)
+
+			newNeckRingLog := model.NewNeckRingBindLog(userModel.AppPasture.Id, item.Number, cowInfo, userModel.SystemUser, "")
+			neckRing, ok := s.NeckRingIsExist(userModel.AppPasture.Id, item.Number)
+
 			switch req.Status {
 			case pasturePb.NeckRingOperationStatus_Bind: // 绑定
-				if err != nil {
-					return xerr.WithStack(err)
+				if ok && neckRing.IsBind == pasturePb.NeckRingIsBind_Bind {
+					return xerr.Customf("该脖环: %s已经绑定牛只: %s", item.Number, neckRing.EarNumber)
 				}
 
-				if ok && neckRing.Status == pasturePb.NeckRingStatus_Bind {
-					return xerr.Customf("该脖环: %s已经绑定牛只: %s", item.Number, neckRing.EarNumber)
+				if cowInfo.NeckRingNumber != "" {
+					return xerr.Customf("该牛只: %s,已经绑定:%s", cowInfo.EarNumber, cowInfo.NeckRingNumber)
 				}
 
 				newNeckRing := model.NewNeckRing(userModel.AppPasture.Id, item.Number, cowInfo, userModel.SystemUser)
@@ -161,10 +164,10 @@ func (s *StoreEntry) NeckRingCreateOrUpdate(ctx context.Context, req *pasturePb.
 					return xerr.WithStack(err)
 				}
 				number = item.Number
-				newNeckRingLog.OperationStatusName = "绑定"
+				newNeckRingLog.OperationName = "绑定"
 				// 解绑
 			case pasturePb.NeckRingOperationStatus_UnBind:
-				if ok && neckRing.Status != pasturePb.NeckRingStatus_Bind {
+				if ok && neckRing.IsBind != pasturePb.NeckRingIsBind_Bind {
 					return xerr.Customf("该脖环: %s,未绑定牛只", item.Number)
 				}
 				if err = tx.Model(new(model.NeckRing)).
@@ -173,34 +176,36 @@ func (s *StoreEntry) NeckRingCreateOrUpdate(ctx context.Context, req *pasturePb.
 						"cow_id":     0,
 						"ear_number": "",
 						"wear_at":    0,
-						"status":     pasturePb.NeckRingStatus_Unbind,
+						"is_bind":    pasturePb.NeckRingIsBind_Unbind,
 					}).Error; err != nil {
 					return xerr.WithStack(err)
 				}
-				newNeckRingLog.OperationStatusName = "解绑"
+				newNeckRingLog.OperationName = "解绑"
 				// 编辑
 			case pasturePb.NeckRingOperationStatus_Edit:
+				if cowInfo.NeckRingNumber != "" && item.Number != cowInfo.NeckRingNumber {
+					return xerr.Customf("该牛只: %s,已经绑定:%s", cowInfo.EarNumber, cowInfo.NeckRingNumber)
+				}
+
 				if err = tx.Model(new(model.NeckRing)).
 					Where("neck_ring_number = ?", item.Number).
 					Updates(map[string]interface{}{
 						"cow_id":     cowInfo.Id,
 						"ear_number": cowInfo.EarNumber,
 						"wear_at":    time.Now().Unix(),
-						"status":     pasturePb.NeckRingStatus_Bind,
+						"is_bind":    pasturePb.NeckRingIsBind_Bind,
 					}).Error; err != nil {
 					return xerr.WithStack(err)
 				}
 				number = item.Number
-				newNeckRingLog.OperationStatusName = "编辑"
-			default:
-				continue
+				newNeckRingLog.OperationName = "编辑"
 			}
 
 			if err = tx.Create(newNeckRingLog).Error; err != nil {
 				return xerr.WithStack(err)
 			}
 			if err = tx.Model(new(model.Cow)).
-				Where("id = ?", item.CowId).
+				Where("id = ?", cowInfo.Id).
 				Update("neck_ring_number", number).
 				Error; err != nil {
 				return xerr.WithStack(err)
@@ -210,7 +215,6 @@ func (s *StoreEntry) NeckRingCreateOrUpdate(ctx context.Context, req *pasturePb.
 	}); err != nil {
 		return xerr.WithStack(err)
 	}
-
 	return nil
 }
 
@@ -220,21 +224,22 @@ func (s *StoreEntry) NeckRingList(ctx context.Context, req *pasturePb.SearchNeck
 		return nil, xerr.WithStack(err)
 	}
 
-	neckRingLogList := make([]*model.NeckRing, 0)
+	neckRingLogList := make([]*model.NeckRingModel, 0)
 	var count int64 = 0
 
 	pref := s.DB.Table(fmt.Sprintf("%s as a", new(model.NeckRing).TableName())).
 		Select("a.*,COALESCE(b.pen_name, '') AS pen_name").
 		Joins("LEFT JOIN cow as b ON a.cow_id = b.id").
-		Where("a.status >= ?", pasturePb.NeckRingStatus_Unbind).
+		//Where("a.is_bind = ?", pasturePb.NeckRingIsBind_Bind).
 		Where("a.pasture_id = ?", userModel.AppPasture.Id).
 		Where("a.neck_ring_number != ''")
-	if req.Status > 0 {
-		pref.Where("a.status = ?", req.Status)
+
+	if req.IsBind > 0 {
+		pref.Where("a.is_bind = ?", req.IsBind)
 	}
 
-	if req.CowId > 0 {
-		pref.Where("a.cow_id = ?", req.CowId)
+	if req.EarNumber != "" {
+		pref.Where("a.ear_number = ?", req.EarNumber)
 	}
 
 	if req.Number != "" {
@@ -249,12 +254,13 @@ func (s *StoreEntry) NeckRingList(ctx context.Context, req *pasturePb.SearchNeck
 		return nil, xerr.WithStack(err)
 	}
 
+	neckRingIsBindMap := s.NeckRingIsBindMap()
 	neckRingStatusMap := s.NeckRingStatusMap()
 	return &pasturePb.SearchNeckRingResponse{
 		Code: http.StatusOK,
 		Msg:  "ok",
 		Data: &pasturePb.SearchNeckRingData{
-			List:     model.NeckRingSlice(neckRingLogList).ToPB(neckRingStatusMap),
+			List:     model.NeckRingSlice(neckRingLogList).ToPB(neckRingIsBindMap, neckRingStatusMap),
 			Total:    int32(count),
 			PageSize: pagination.PageSize,
 			Page:     pagination.Page,

+ 5 - 2
module/backend/interface.go

@@ -136,6 +136,7 @@ type PastureManageService interface {
 
 	CreateOrUpdateDealer(crx context.Context, req *pasturePb.DealerItem) error
 	SearchDealerList(crx context.Context, req *pasturePb.SearchNameRequest) (*pasturePb.SearchDealerResponse, error)
+	DeleteDealer(crx context.Context, id int64) error
 
 	SystemBasicEdit(ctx context.Context, req *pasturePb.BaseDataConfigBatch) error
 	SystemBasicList(ctx context.Context) (*pasturePb.SearchBaseDataConfigResponse, error)
@@ -183,9 +184,11 @@ type EventService interface {
 	// MatingList 配种
 	MatingList(ctx context.Context, req *pasturePb.SearchEventRequest, pagination *pasturePb.PaginationModel) (*pasturePb.EventMatingResponse, error)
 	MatingBatch(ctx context.Context, req *pasturePb.EventMatingBatch) error
+
 	// EstrusBatchMating 发情批量处理配种
-	EstrusBatchMating(ctx context.Context, req *pasturePb.EventNaturalEstrusBatch) error
+	EstrusBatchMating(ctx context.Context, req *pasturePb.EventEstrusBatch) error
 	EstrusList(ctx context.Context, req *pasturePb.SearchEventRequest, pagination *pasturePb.PaginationModel) (*pasturePb.SearchEventEstrusResponse, error)
+	NeckRingNoEstrusBatch(ctx context.Context, req *pasturePb.NeckRingNoEstrusBatchRequest) error
 
 	// AbortionList 流产
 	AbortionList(ctx context.Context, req *pasturePb.SearchEventRequest, pagination *pasturePb.PaginationModel) (*pasturePb.EventAbortionResponse, error)
@@ -280,7 +283,7 @@ type GoodsService interface {
 //go:generate mockgen -destination mock/AnalyseService.go -package kptservicemock kpt-pasture/module/backend AnalyseService
 type AnalyseService interface {
 	WeightScatterPlot(ctx context.Context, req *pasturePb.SearchGrowthCurvesRequest, pagination *pasturePb.PaginationModel) (*pasturePb.GrowthCurvesResponse, error)
-	WeightRange(ctx context.Context, req *pasturePb.WeightRangeRequest) (*pasturePb.WeightRangeResponse, error)
+	WeightRange(ctx context.Context, req *pasturePb.WeightRangeRequest, pagination *pasturePb.PaginationModel) (*pasturePb.WeightRangeResponse, error)
 	MatingTimely(ctx context.Context, req *pasturePb.MatingTimelyRequest) (*model.MatingTimelyResponse, error)
 	PenWeight(ctx context.Context, req *pasturePb.PenWeightRequest, pagination *pasturePb.PaginationModel) (*pasturePb.PenWeightResponse, error)
 	AbortionRate(ctx context.Context, req *pasturePb.AbortionRateRequest) (*pasturePb.AbortionRateResponse, error)

+ 1 - 3
module/backend/neck_ring_warning.go

@@ -6,7 +6,6 @@ import (
 	"kpt-pasture/model"
 	"kpt-pasture/util"
 	"net/http"
-	"strings"
 	"time"
 
 	"gorm.io/gorm"
@@ -46,8 +45,7 @@ func (s *StoreEntry) EstrusOrAbortionCowList(ctx context.Context, req *pasturePb
 	}
 
 	if len(req.PenIds) > 0 {
-		penIds := strings.Split(util.ArrayInt32ToStrings(req.PenIds, ","), ",")
-		pref.Where("b.pen_id IN ?", penIds)
+		pref.Where("b.pen_id IN ?", req.PenIds)
 	}
 
 	if err = pref.Order("a.level DESC").

+ 24 - 0
module/backend/pasture.go

@@ -481,3 +481,27 @@ func (s *StoreEntry) SearchDealerList(crx context.Context, req *pasturePb.Search
 	}, nil
 
 }
+
+func (s *StoreEntry) DeleteDealer(crx context.Context, id int64) error {
+	userModel, err := s.GetUserModel(crx)
+	if err != nil {
+		return xerr.WithStack(err)
+	}
+
+	saleDealer := &model.SaleDealer{}
+	if err = s.DB.Model(new(model.SaleDealer)).
+		Where("id = ? and pasture_id = ?", id, userModel.AppPasture.Id).
+		First(saleDealer).Error; err != nil {
+		if errors.Is(err, gorm.ErrRecordNotFound) {
+			return xerr.Custom("未找到该经销商信息")
+		}
+		return xerr.WithStack(err)
+	}
+
+	if err = s.DB.Model(new(model.SaleDealer)).
+		Where("id = ? and pasture_id = ?", saleDealer.Id, userModel.AppPasture.Id).
+		Update("is_show", pasturePb.IsShow_No).Error; err != nil {
+		return xerr.WithStack(err)
+	}
+	return nil
+}

+ 21 - 5
module/backend/sql.go

@@ -171,7 +171,22 @@ func (s *StoreEntry) GetCowInfoByEarNumber(ctx context.Context, pastureId int64,
 		Where("admission_status = ?", pasturePb.AdmissionStatus_Admission).
 		First(cowInfo).Error; err != nil {
 		if errors.Is(err, gorm.ErrRecordNotFound) {
-			return nil, xerr.Customf("该牛只数据不存在: %d", earNumber)
+			return nil, xerr.Customf("该牛只数据不存在: %s", earNumber)
+		} else {
+			return nil, xerr.WithStack(err)
+		}
+	}
+	return cowInfo, nil
+}
+
+func (s *StoreEntry) GetCowEventByEarNumber(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).
+		First(cowInfo).Error; err != nil {
+		if errors.Is(err, gorm.ErrRecordNotFound) {
+			return nil, xerr.Customf("该牛只数据不存在: %s", earNumber)
 		} else {
 			return nil, xerr.WithStack(err)
 		}
@@ -459,19 +474,20 @@ func (s *StoreEntry) DiseaseMaps(ctx context.Context) (map[int64]*model.Disease,
 	return res, nil
 }
 
-func (s *StoreEntry) NeckRingIsExist(ctx context.Context, pastureId int64, number string) (*model.NeckRing, bool, error) {
+// NeckRingIsExist 根据neckRingNumber判断该耳环是否存在
+func (s *StoreEntry) NeckRingIsExist(pastureId int64, number string) (*model.NeckRing, bool) {
 	neckRing := &model.NeckRing{}
 	if err := s.DB.Model(new(model.NeckRing)).
 		Where("neck_ring_number = ?", number).
 		Where("pasture_id = ?", pastureId).
 		First(neckRing).Error; err != nil {
 		if errors.Is(err, gorm.ErrRecordNotFound) {
-			return nil, false, nil
+			return nil, false
 		} else {
-			return nil, false, err
+			return nil, false
 		}
 	}
-	return neckRing, true, nil
+	return neckRing, true
 }
 
 func (s *StoreEntry) GetEventLogList(

+ 2 - 2
module/backend/test_service.go

@@ -42,7 +42,7 @@ func (s *StoreEntry) CowNeckRingNumberBound(ctx context.Context, pagination *pas
 	for _, cow := range cowList {
 		newNeckRing := model.NewNeckRing(userModel.AppPasture.Id, cow.NeckRingNumber, cow, userModel.SystemUser)
 		newNeckRingList = append(newNeckRingList, newNeckRing)
-		newNeckRingBindLog := model.NewNeckRingBindLog(userModel.AppPasture.Id, cow.NeckRingNumber, cow, userModel.SystemUser)
+		newNeckRingBindLog := model.NewNeckRingBindLog(userModel.AppPasture.Id, cow.NeckRingNumber, cow, userModel.SystemUser, "")
 		newNeckRingBindLogList = append(newNeckRingBindLogList, newNeckRingBindLog)
 	}
 
@@ -93,7 +93,7 @@ func (s *StoreEntry) CowNeckRingNumberBound2(ctx context.Context, pagination *pa
 				Where("id = ?", oldNeckRing.Id).Updates(map[string]interface{}{
 				"cow_id":     cow.Id,
 				"ear_number": cow.EarNumber,
-				"status":     pasturePb.NeckRingStatus_Bind,
+				"is_bind":    pasturePb.NeckRingIsBind_Bind,
 			}).Error; err != nil {
 				zaplog.Error("CowNeckRingNumberBound2-OldNeckRing", zap.Any("error", err))
 			}

+ 1 - 0
module/crontab/interface.go

@@ -46,4 +46,5 @@ type Crontab interface {
 	UpdatePenBehaviorDaily() error // 栏舍饲养监测
 
 	UpdateMilkOriginal() error
+	InsertMilkDaily() error
 }

+ 31 - 0
module/crontab/milk_daily.go

@@ -0,0 +1,31 @@
+package crontab
+
+import (
+	"kpt-pasture/model"
+	"time"
+)
+
+func (e *Entry) InsertMilkDaily() error {
+	pastureList := e.FindPastureList()
+	if pastureList == nil || len(pastureList) == 0 {
+		return nil
+	}
+
+	for _, pasture := range pastureList {
+		e.ProcessMilkDaily(pasture.Id)
+	}
+
+	return nil
+}
+
+func (e *Entry) ProcessMilkDaily(pastureId int64) {
+	nowTime := time.Now()
+	newMilkDaily := &model.MilkDaily{}
+	if err := e.DB.Model(new(model.MilkDaily)).
+		Where("pasture_id = ? AND DATE_FORMAT(created_at,'%Y-%m-%d') = ?", pastureId, nowTime.Format("2006-01-02")).
+		First(newMilkDaily).Error; err != nil {
+		newMilkDaily.PastureId = pastureId
+		newMilkDaily.CreatedAt = nowTime.Unix()
+		newMilkDaily.UpdatedAt = nowTime.Unix()
+	}
+}

+ 24 - 0
module/crontab/neck_ring_calculate.go

@@ -5,6 +5,7 @@ import (
 	"kpt-pasture/model"
 	"kpt-pasture/util"
 	"math"
+	"strconv"
 	"time"
 
 	pasturePb "gitee.com/xuyiping_admin/go_proto/proto/go/backend/cow"
@@ -77,6 +78,9 @@ func (e *Entry) EntryUpdateActiveHabit(pastureId int64) (err error) {
 	// 插入群体校正表
 	e.UpdateChangeAdJust(pastureId, xToday)
 
+	// 更新 Cft
+	e.UpdateCft(pastureId, processIds)
+
 	// 更新所有的显示状态为否的记录为是
 	e.UpdateIsShow(pastureId, processIds)
 
@@ -389,6 +393,26 @@ func (e *Entry) UpdateChangeAdJust(pastureId int64, xToday *XToday) {
 	}
 }
 
+func (e *Entry) UpdateCft(pastureId int64, processIds []int64) {
+	neckActiveHabitList := make([]*model.NeckActiveHabit, 0)
+	if err := e.DB.Model(new(model.NeckActiveHabit)).
+		Where("id IN (?)", processIds).
+		Where("pasture_id = ?", pastureId).
+		Where("is_show = ?", pasturePb.IsShow_No).
+		Find(&neckActiveHabitList).Error; err != nil {
+		zaplog.Error("UpdateCft-1", zap.Any("error", err))
+	}
+	for _, v := range neckActiveHabitList {
+		cft := CalculateCFT(v)
+		if err := e.DB.Model(new(model.NeckActiveHabit)).
+			Where("id = ?", v.Id).
+			Where("neck_ring_number = ?", v.NeckRingNumber).
+			Update("cft", strconv.FormatFloat(float64(cft), 'f', 2, 64)).Error; err != nil {
+			zaplog.Error("UpdateCft-2", zap.Any("error", err))
+		}
+	}
+}
+
 func (e *Entry) UpdateIsShow(pastureId int64, processIds []int64) {
 	if err := e.DB.Model(new(model.NeckActiveHabit)).
 		Where("id IN (?)", processIds).

+ 8 - 22
module/crontab/neck_ring_estrus.go

@@ -68,14 +68,14 @@ func (e *Entry) EntryCowEstrus(pastureId int64) (err error) {
 
 // CowEstrusWarning 发情预警
 func (e *Entry) CowEstrusWarning(pastureId int64, xToday *XToday, nowTime time.Time) {
-	isAlreadIds := make([]int64, 0)
+	cft := xToday.ActiveLow - XAdjust21
 	neckActiveHabitList := make([]*model.NeckActiveHabit, 0)
 	if err := e.DB.Model(new(model.NeckActiveHabit)).
-		Where("heat_date <= ?", nowTime.Format(model.LayoutDate2)).
+		Where("heat_date BETWEEN ? AND ?", nowTime.AddDate(0, 0, -1).Format(model.LayoutDate2), nowTime.Format(model.LayoutDate2)).
 		Where("pasture_id = ?", pastureId).
 		Where("filter_high > 0 AND change_filter > ?", model.DefaultChangeFilter).
 		Where("cow_id > ?", 0).
-		Where("is_estrus = ?", pasturePb.IsShow_No).
+		Where("cft >= ?", cft).
 		Where(e.DB.Where("calving_age >= ?", MinCalvingAge).Or("lact = ?", MinLact)). // 排除产后20天内的发情牛
 		Order("cow_id").
 		Find(&neckActiveHabitList).Error; err != nil {
@@ -85,13 +85,7 @@ func (e *Entry) CowEstrusWarning(pastureId int64, xToday *XToday, nowTime time.T
 
 	neckActiveHabitMap := make(map[int64][]*model.NeckActiveHabit)
 	for _, habit := range neckActiveHabitList {
-		isAlreadIds = append(isAlreadIds, habit.Id)
-		cft := calculateCFT(habit)
-		if cft < float32(xToday.ActiveLow-XAdjust21) {
-			continue
-		}
-
-		zaplog.Info("CowEstrusWarning", zap.Any("cft", cft), zap.Any("habit", habit))
+		zaplog.Info("CowEstrusWarning", zap.Any("habit", habit))
 		if neckActiveHabitMap[habit.CowId] == nil {
 			neckActiveHabitMap[habit.CowId] = make([]*model.NeckActiveHabit, 0)
 		}
@@ -119,9 +113,8 @@ func (e *Entry) CowEstrusWarning(pastureId int64, xToday *XToday, nowTime time.T
 		maxHigh := int32(0)
 		lastActiveDate := time.Time{}
 		for _, habit := range cowHabitList {
-			cft := calculateCFT(habit)
-			if cft > maxCft {
-				maxCft = cft
+			if habit.Cft > maxCft {
+				maxCft = habit.Cft
 			}
 			if habit.FilterHigh > maxHigh {
 				maxHigh = habit.FilterHigh
@@ -191,13 +184,6 @@ func (e *Entry) CowEstrusWarning(pastureId int64, xToday *XToday, nowTime time.T
 			zaplog.Error("CowEstrusWarningNew", zap.Any("eventEstrusList", neckRingEstrusList), zap.Any("err", err))
 		}
 	}
-	if len(isAlreadIds) > 0 {
-		if err := e.DB.Model(new(model.NeckActiveHabit)).
-			Where("id IN (?)", isAlreadIds).
-			Update("is_estrus", pasturePb.IsShow_Ok).Error; err != nil {
-			zaplog.Error("CowEstrusWarning", zap.Any("err", err))
-		}
-	}
 }
 
 func (e *Entry) UpdateNewNeckRingEstrus(pastureId int64, nowTime time.Time) {
@@ -314,8 +300,8 @@ func (e *Entry) HistoryNeckRingEstrus(pastureId int64, neckRingNumber string, ac
 	return count > 0
 }
 
-// calculateCFT 计算cft值
-func calculateCFT(habit *model.NeckActiveHabit) (cft float32) {
+// CalculateCFT 计算cft值
+func CalculateCFT(habit *model.NeckActiveHabit) (cft float32) {
 	if habit.ChangeAdjust >= 10 {
 		cft = (float32(habit.ChangeFilter) - float32(habit.ChangeAdjust) + 3) * float32(habit.FilterCorrect) / 100
 	} else {

+ 1 - 1
module/mqtt/sql.go

@@ -10,7 +10,7 @@ import (
 func (e *Entry) NeckRingIsBind(number string) bool {
 	var count int64 = 0
 	if err := e.DB.Model(new(model.NeckRing)).Where("number = ?", number).
-		Where("status != ?", pasturePb.NeckRingStatus_Unbind).
+		Where("is_bind != ?", pasturePb.NeckRingIsBind_Unbind).
 		Count(&count).Error; err != nil {
 		return false
 	}

+ 0 - 17
util/util_test.go

@@ -2,14 +2,11 @@ package util
 
 import (
 	"fmt"
-	"sort"
 	"strconv"
 	"strings"
 	"testing"
 	"time"
 
-	pasturePb "gitee.com/xuyiping_admin/go_proto/proto/go/backend/cow"
-
 	"github.com/stretchr/testify/assert"
 )
 
@@ -531,18 +528,4 @@ func TestSubstr(t *testing.T) {
 }
 
 func Test_demo(t *testing.T) {
-
-	initialTime, err := TimeParseLocal(LayoutTime, "")
-	fmt.Println(initialTime, err)
-
-	type MyInt pasturePb.Week_Kind
-
-	nums := []MyInt{3, 1, 4, 5, 2, 6}
-
-	// 使用sort.Slice排序
-	sort.Slice(nums, func(i, j int) bool {
-		return nums[i] < nums[j]
-	})
-
-	fmt.Println(nums) // 输出: [1 1 2 3 4 5 6 9]
 }