瀏覽代碼

indicators: 指标收集

Yi 2 周之前
父節點
當前提交
4f6f1cd673

+ 9 - 1
model/calving_calf.go

@@ -2,6 +2,8 @@ package model
 
 import pasturePb "gitee.com/xuyiping_admin/go_proto/proto/go/backend/cow"
 
+var CalfDefaultDayAge = int32(60)
+
 type CalvingCalf struct {
 	Id            int64                  `json:"id"`
 	PastureId     int64                  `json:"pastureId"`
@@ -19,15 +21,21 @@ type CalvingCalf struct {
 	PenName       string                 `json:"penName"`
 	WeaningAt     int64                  `json:"weaningAt"`
 	CurrentWeight int64                  `json:"currentWeight"`
+	DeathAt       int64                  `json:"deathAt"`
 	Remarks       string                 `json:"remarks"`
 	CreatedAt     int64                  `json:"createdAt"`
 	UpdatedAt     int64                  `json:"updatedAt"`
 }
 
-func (e *CalvingCalf) TableName() string {
+func (c *CalvingCalf) TableName() string {
 	return "calving_calf"
 }
 
+func (c *CalvingCalf) DeathEventUpdate(deathAt int64) {
+	c.DeathAt = deathAt
+	c.IsLive = pasturePb.IsShow_No
+}
+
 func NewEventCalvingCalf(pastureId, calvingId, calvingAt int64, motherInfo *Cow, penMap map[int32]*Pen, req *pasturePb.CalfItem) *CalvingCalf {
 	isAdoption := req.IsAdoption
 	if req.IsLive == pasturePb.IsShow_No {

+ 2 - 2
model/cow.go

@@ -719,8 +719,8 @@ func NewCalfCow(matherInfo *Cow, calf *CalvingCalf) *Cow {
 		BirthWeight:     calf.BirthWeight,
 		BirthAt:         calf.BirthAt,
 		SourceKind:      pasturePb.CowSource_Calving, // 产犊方式
-		FatherNumber:    matherInfo.EarNumber,
-		MotherNumber:    matherInfo.LastBullNumber,
+		FatherNumber:    matherInfo.LastBullNumber,
+		MotherNumber:    matherInfo.EarNumber,
 		AdmissionStatus: pasturePb.AdmissionStatus_Admission,
 		IsPregnant:      pasturePb.IsShow_No,
 		AdmissionAt:     calf.BirthAt,

+ 0 - 49
model/cow_neck_ring_error.go

@@ -1,49 +0,0 @@
-package model
-
-import pasturePb "gitee.com/xuyiping_admin/go_proto/proto/go/backend/cow"
-
-type CowNeckRingError struct {
-	Id              int64                              `json:"id"`
-	PastureId       int64                              `json:"pastureId"`
-	NeckRingNumber  string                             `json:"neckRingNumber"`
-	DateTime        string                             `json:"dateTime"`
-	ErrorKind       pasturePb.NeckRingNumberError_Kind `json:"errorKind"`
-	ErrorName       string                             `json:"errorName"`
-	AcceptedNumber  int64                              `json:"acceptedNumber"`
-	HighNumber      int64                              `json:"highNumber"`
-	CowId           int64                              `json:"cowId"`
-	EarNumber       string                             `json:"earNumber"`
-	PenName         string                             `json:"penName"`
-	Voltage         int32                              `json:"voltage"`
-	FirmwareVersion int32                              `json:"firmwareVersion"`
-	ReceiveNumber   string                             `json:"receiveNumber"`
-	CreatedAt       int64                              `json:"createdAt"`
-	UpdatedAt       int64                              `json:"updatedAt"`
-}
-
-func (c *CowNeckRingError) TableName() string {
-	return "cow_neck_ring_error"
-}
-
-func NewCowNeckRingError(
-	pastureId int64,
-	cowInfo *Cow,
-	errorKind pasturePb.NeckRingNumberError_Kind,
-	errorMap map[pasturePb.NeckRingNumberError_Kind]string,
-) *CowNeckRingError {
-	return &CowNeckRingError{
-		PastureId:       pastureId,
-		NeckRingNumber:  cowInfo.NeckRingNumber,
-		DateTime:        "",
-		ErrorKind:       errorKind,
-		ErrorName:       errorMap[errorKind],
-		AcceptedNumber:  0,
-		HighNumber:      0,
-		CowId:           cowInfo.Id,
-		EarNumber:       cowInfo.EarNumber,
-		PenName:         cowInfo.PenName,
-		Voltage:         0,
-		FirmwareVersion: 0,
-		ReceiveNumber:   "",
-	}
-}

+ 4 - 3
model/event_death.go

@@ -77,7 +77,8 @@ func (e EventDeathSlice) ToPB() []*pasturePb.EventDeath {
 }
 
 type EventDeathModel struct {
-	Cow        *Cow
-	EventDeath *EventDeath
-	NeckRing   *NeckRing
+	Cow         *Cow
+	EventDeath  *EventDeath
+	NeckRing    *NeckRing
+	CalvingCalf *CalvingCalf
 }

+ 5 - 0
model/event_pregnant_check.go

@@ -7,6 +7,11 @@ import (
 	pasturePb "gitee.com/xuyiping_admin/go_proto/proto/go/backend/cow"
 )
 
+const (
+	PregnantCheckForFirst  = "pregnant_check_for_first"  // 初检
+	PregnantCheckForSecond = "pregnant_check_for_second" // 复检
+)
+
 type EventPregnantCheck struct {
 	Id                  int64                              `json:"id"`
 	PastureId           int64                              `json:"pastureId"`

+ 29 - 17
model/indicators_data.go

@@ -8,23 +8,35 @@ import (
 )
 
 const (
-	AllCow            = "all_cow"
-	AdultCow          = "adult_cow"
-	CalvingInterval   = "calving_interval"
-	OutputNumber      = "output_number"
-	InputNumber       = "input_number"
-	SalesVolume       = "sales_volume"
-	CalvingNumber     = "calving_number"
-	AdultAbortionRate = "adult_abortion_rate"
-	YouthAbortionRate = "youth_abortion_rate"
-	AllDieNumber      = "all_die_number"
-	DiseaseNumber     = "disease_number"
-	CureNumber        = "cure_number"
-	OutNumber         = "out_number"
-	CalfDieNumber     = "calf_die_number"
-	LactationCow      = "lactation_cow"
-	DryMilkCow        = "dry_milk_cow"
-	ReserveCow        = "reserve_cow"
+	AllCow                    = "all_cow"
+	AdultCow                  = "adult_cow"
+	AvgCalvingInterval        = "avg_calving_interval"
+	OutputNumber              = "output_number"
+	InputNumber               = "input_number"
+	SalesVolume               = "sales_volume"
+	CalvingNumber             = "calving_number"
+	AdultAbortionRate         = "adult_abortion_rate"
+	YouthAbortionRate         = "youth_abortion_rate"
+	AllDieNumber              = "all_die_number"
+	DiseaseNumber             = "disease_number"
+	CureNumber                = "cure_number"
+	OutNumber                 = "out_number"
+	CalfDieNumber             = "calf_die_number"
+	LactationCow              = "lactation_cow"
+	DryMilkCow                = "dry_milk_cow"
+	ReserveCow                = "reserve_cow"
+	FirstBornSurvivalRate     = "first_born_survival_rate"
+	FirstBornDeathRate        = "first_born_death_rate"
+	MultiparitySurvivalRate   = "multiparity_survival_rate"
+	MultiparityDeathRate      = "multiparity_death_rate"
+	AvgAgeFirstMate           = "avg_age_first_mate"
+	YouthPregnantRate         = "youth_pregnant_rate"
+	MultiparityPregnantRate   = "multiparity_pregnant_rate"
+	MultipartyFirstCheckRate  = "multiparty_first_check_rate"
+	MultipartySecondCheckRate = "multiparty_second_check_rate"
+	YouthFirstCheckRate       = "youth_first_check_rate"
+	YouthSecondCheckRate      = "youth_second_check_rate"
+	ForbiddenCowNumber        = "forbidden_cow_number"
 )
 
 type IndicatorsData struct {

+ 4 - 4
model/indicators_details.go

@@ -23,21 +23,21 @@ func (IndicatorsDetails) TableName() string {
 type IndicatorsDetailsSlice []*IndicatorsDetails
 
 func (i IndicatorsDetailsSlice) ToPB(userFocusIndicators []string) []*pasturePb.FocusIndicatorsSetData {
-	res := make([]*pasturePb.FocusIndicatorsSetData, len(i))
-	for k, v := range i {
+	res := make([]*pasturePb.FocusIndicatorsSetData, 0)
+	for _, v := range i {
 		isShow := pasturePb.IsShow_No
 		for _, useIndicators := range userFocusIndicators {
 			if useIndicators == v.Kind {
 				isShow = pasturePb.IsShow_Ok
 			}
 		}
-		res[k] = &pasturePb.FocusIndicatorsSetData{
+		res = append(res, &pasturePb.FocusIndicatorsSetData{
 			Name:              v.Name,
 			Kind:              v.Kind,
 			IndicatorTypeKind: v.CategoryType,
 			IndicatorTypeName: v.CategoryName,
 			IsShow:            isShow,
-		}
+		})
 	}
 	return res
 }

+ 0 - 2
model/system_basic.go

@@ -5,8 +5,6 @@ import pasturePb "gitee.com/xuyiping_admin/go_proto/proto/go/backend/cow"
 const (
 	ProactivelyStopBreedingForBackup = "proactively_stop_breeding_for_backup" // 后备牛主动停配
 	ProactivelyStopBreedingForAdult  = "proactively_stop_breeding_for_adult"  // 成母牛主动停配
-	PregnantCheckForFirst            = "pregnant_check_for_first"             // 初检
-	PregnantCheckForSecond           = "pregnant_check_for_second"            // 复检
 	PregnancyAge                     = "pregnancy_age"                        // 怀孕天数
 	WeaningAge                       = "weaning_age"                          // 断奶天数
 	EstrusWaringDays                 = "estrus_waring_days"                   // 发情预警产后天数

+ 22 - 3
module/backend/event_base_more.go

@@ -47,9 +47,18 @@ func (s *StoreEntry) DeathBatch(ctx context.Context, req *pasturePb.EventDeathBa
 
 		newEventDeath := model.NewEventDeath(userModel.AppPasture.Id, cow, item, userModel.SystemUser, operationUser)
 		eventDeathModel := &model.EventDeathModel{
-			Cow:        cow,
-			EventDeath: newEventDeath,
-			NeckRing:   nil,
+			Cow:         cow,
+			EventDeath:  newEventDeath,
+			NeckRing:    nil,
+			CalvingCalf: nil,
+		}
+
+		// 犊牛死亡更新
+		if cow.DayAge <= model.CalfDefaultDayAge {
+			calvingCalf, ok := s.IsExistCalvingCalf(userModel.AppPasture.Id, cow.Id)
+			if ok && calvingCalf != nil {
+				eventDeathModel.CalvingCalf = calvingCalf
+			}
 		}
 
 		if neckRing, ok := s.NeckRingIsExist(userModel.AppPasture.Id, item.EarNumber); ok && neckRing != nil {
@@ -90,6 +99,16 @@ func (s *StoreEntry) DeathBatch(ctx context.Context, req *pasturePb.EventDeathBa
 				}
 			}
 
+			// 犊牛死亡更新
+			if item.CalvingCalf != nil {
+				item.CalvingCalf.DeathEventUpdate(item.Cow.DepartureAt)
+				if err = tx.Model(new(model.CalvingCalf)).
+					Select("death_at", "is_live").
+					Where("id = ?", item.CalvingCalf.Id).
+					Updates(item.CalvingCalf).Error; err != nil {
+				}
+			}
+
 			// 记录事件日志
 			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 {

+ 7 - 22
module/backend/neck_ring_warning.go

@@ -256,7 +256,7 @@ func (s *StoreEntry) NeckRingNoDiseaseBatch(ctx context.Context, req *pasturePb.
 	neckRingHealthWarningList := make([]*model.NeckRingHealthWarning, 0)
 	if err = s.DB.Model(new(model.NeckRingHealthWarning)).
 		Where("pasture_id = ?", userModel.AppPasture.Id).
-		Where("date_time BETWEEN ? AND ?", startTime, endTime).
+		Where("heat_date BETWEEN ? AND ?", startTime, endTime).
 		Where("ear_number IN ?", req.EarNumbers).
 		Where("is_show = ?", pasturePb.IsShow_Ok).
 		Find(&neckRingHealthWarningList).Error; err != nil {
@@ -267,28 +267,12 @@ func (s *StoreEntry) NeckRingNoDiseaseBatch(ctx context.Context, req *pasturePb.
 		return nil
 	}
 
-	neckRingHealthIds := make([]int64, 0)
-	for _, v := range neckRingHealthWarningList {
-		neckRingHealth := &model.NeckRingHealth{}
-		if err = s.DB.Model(new(model.NeckRingHealth)).
-			Where("pasture_id = ?", userModel.AppPasture.Id).
-			Where("id = ?", v.NeckRingHealthId).
-			Where("is_show = ?", pasturePb.IsShow_Ok).
-			First(neckRingHealth).Error; err != nil {
-			zaplog.Error("NeckRingNoEstrusBatch", zap.Any("err", err), zap.Any("neckRingEstrusWarning", v))
-			return xerr.Customf("数据异常: %s", v.EarNumber)
-		}
-		neckRingHealthIds = append(neckRingHealthIds, neckRingHealth.Id)
-	}
-
-	if len(neckRingHealthIds) <= 0 {
-		return nil
-	}
-
 	if err = s.DB.Transaction(func(tx *gorm.DB) error {
-		for _, id := range neckRingHealthIds {
+		for _, v := range neckRingHealthWarningList {
 			if err = tx.Model(new(model.NeckRingHealth)).
-				Where("id = ?", id).
+				Where("pasture_id = ?", userModel.AppPasture.Id).
+				Where("heat_date = ?", v.HeatDate).
+				Where("cow_id = ?", v.CowId).
 				Updates(map[string]interface{}{
 					"check_result":    pasturePb.CheckResult_Fail,
 					"check_user_id":   userModel.SystemUser.Id,
@@ -300,13 +284,14 @@ func (s *StoreEntry) NeckRingNoDiseaseBatch(ctx context.Context, req *pasturePb.
 			}
 
 			if err = tx.Model(new(model.NeckRingHealthWarning)).
-				Where("neck_ring_health_id = ?", id).
+				Where("neck_ring_health_id = ?", v.NeckRingHealthId).
 				Updates(map[string]interface{}{
 					"is_show": pasturePb.IsShow_No,
 				}).Error; err != nil {
 				return xerr.WithStack(err)
 			}
 		}
+
 		return nil
 	}); err != nil {
 		return xerr.WithStack(err)

+ 17 - 0
module/backend/sql.go

@@ -544,6 +544,7 @@ func (s *StoreEntry) FindIndicatorsDetailsList(ctx context.Context) ([]*model.In
 	}
 	return list, nil
 }
+
 func (s *StoreEntry) GetCowLastEvent(pastureId, cowId int64, eventCategoryId pasturePb.EventCategory_Kind) *model.EventCowLog {
 	newEventCowLog := &model.EventCowLog{CowId: cowId}
 	pref := s.DB.Table(newEventCowLog.TableName()).
@@ -558,3 +559,19 @@ func (s *StoreEntry) GetCowLastEvent(pastureId, cowId int64, eventCategoryId pas
 	}
 	return newEventCowLog
 }
+
+// IsExistCalvingCalf 根据cowId查询犊牛信息
+func (s *StoreEntry) IsExistCalvingCalf(pastureId, cowId int64) (*model.CalvingCalf, bool) {
+	calvingCalf := &model.CalvingCalf{}
+	if err := s.DB.Model(new(model.CalvingCalf)).
+		Where("cow_id = ?", cowId).
+		Where("pasture_id = ?", pastureId).
+		First(calvingCalf).Error; err != nil {
+		if errors.Is(err, gorm.ErrRecordNotFound) {
+			return nil, false
+		} else {
+			return nil, false
+		}
+	}
+	return calvingCalf, true
+}

+ 26 - 2
module/crontab/cow_cron.go

@@ -73,8 +73,8 @@ func (e *Entry) Indicators() error {
 			pastureIndicatorList = e.FindPastureAllCow(pastureList)
 		case model.AdultCow:
 			pastureIndicatorList = e.FindPastureAdultCow(pastureList)
-		case model.CalvingInterval:
-			pastureIndicatorList = e.FindCalvingInterval(pastureList, startTime, endTime)
+		case model.AvgCalvingInterval:
+			pastureIndicatorList = e.FindAvgCalvingInterval(pastureList, startTime, endTime)
 		case model.OutputNumber:
 			pastureIndicatorList = e.FindOutputNumber(pastureList, startTime, endTime)
 		case model.InputNumber:
@@ -103,6 +103,30 @@ func (e *Entry) Indicators() error {
 			pastureIndicatorList = e.LactationCow(pastureList, pasturePb.CowMilk_Dry_Milk)
 		case model.ReserveCow:
 			pastureIndicatorList = e.LactationCow(pastureList, pasturePb.CowMilk_Reserve)
+		case model.FirstBornSurvivalRate:
+			pastureIndicatorList = e.FirstBornSurvivalRate(pastureList, startTime, endTime)
+		case model.FirstBornDeathRate:
+			pastureIndicatorList = e.FirstBornDeathRate(pastureList, startTime, endTime)
+		case model.MultiparitySurvivalRate:
+			pastureIndicatorList = e.MultiparitySurvivalRate(pastureList, startTime, endTime)
+		case model.MultiparityDeathRate:
+			pastureIndicatorList = e.MultiparityDeathRate(pastureList, startTime, endTime)
+		case model.AvgAgeFirstMate:
+			pastureIndicatorList = e.AvgAgeFirstMate(pastureList, startTime, endTime)
+		case model.YouthPregnantRate:
+			pastureIndicatorList = e.CowPregnantRate(pastureList, indicatorsDetail.Kind)
+		case model.MultiparityPregnantRate:
+			pastureIndicatorList = e.CowPregnantRate(pastureList, indicatorsDetail.Kind)
+		case model.MultipartyFirstCheckRate:
+			pastureIndicatorList = e.PregnantCheckRate(pastureList, startTime, endTime, true, model.PregnantCheckForFirst)
+		case model.MultipartySecondCheckRate:
+			pastureIndicatorList = e.PregnantCheckRate(pastureList, startTime, endTime, true, model.PregnantCheckForSecond)
+		case model.YouthFirstCheckRate:
+			pastureIndicatorList = e.PregnantCheckRate(pastureList, startTime, endTime, false, model.PregnantCheckForFirst)
+		case model.YouthSecondCheckRate:
+			pastureIndicatorList = e.PregnantCheckRate(pastureList, startTime, endTime, false, model.PregnantCheckForSecond)
+		case model.ForbiddenCowNumber:
+			pastureIndicatorList = e.ForbiddenCowNumber(pastureList, startTime, endTime)
 		}
 
 		for pastureId, value := range pastureIndicatorList {

+ 2 - 2
module/crontab/cow_indicators_base.go

@@ -47,8 +47,8 @@ func (e *Entry) FindPastureAdultCow(pastureList []*model.AppPastureList) map[int
 	return res
 }
 
-// FindCalvingInterval 产犊间隔
-func (e *Entry) FindCalvingInterval(pastureList []*model.AppPastureList, startTime, endTime int64) map[int64]string {
+// FindAvgCalvingInterval 平均产犊间隔
+func (e *Entry) FindAvgCalvingInterval(pastureList []*model.AppPastureList, startTime, endTime int64) map[int64]string {
 	res := make(map[int64]string)
 	for _, pasture := range pastureList {
 		eventCalvingList := make([]*model.EventCalving, 0)

+ 281 - 0
module/crontab/cow_indicators_breed.go

@@ -43,3 +43,284 @@ func (e *Entry) LactationCow(pastureList []*model.AppPastureList, milkKind pastu
 	}
 	return res
 }
+
+// FirstBornSurvivalRate 头胎接产成活率
+func (e *Entry) FirstBornSurvivalRate(pastureList []*model.AppPastureList, startAt, endAt int64) map[int64]string {
+	res := make(map[int64]string)
+	for _, pasture := range pastureList {
+		eventCalvingList := make([]*model.EventCalving, 0)
+		if err := e.DB.Model(new(model.EventCalving)).
+			Where("pasture_id = ?", pasture.Id).
+			Where("status = ?", pasturePb.IsShow_Ok).
+			Where("lact = ?", 0).
+			Where("reality_day BETWEEN ? AND ?", startAt, endAt).
+			Find(&eventCalvingList).Error; err != nil {
+			zaplog.Error("FirstBornSurvivalRate", zap.Any("err", err))
+		}
+		calvingIds := make([]int64, 0)
+		for _, v := range eventCalvingList {
+			calvingIds = append(calvingIds, v.Id)
+		}
+
+		var allLiveCow int64
+		if err := e.DB.Model(new(model.CalvingCalf)).
+			Select("count(*) as all_live_cow").
+			Where("pasture_id = ?", pasture.Id).
+			Where("calving_id IN (?)", calvingIds).
+			Where("is_live = ?", pasturePb.IsShow_Ok).
+			Where("birth_at BETWEEN ? AND ?", startAt, endAt).
+			Scan(&allLiveCow).Error; err != nil {
+			zaplog.Error("FirstBornSurvivalRate", zap.Any("err", err))
+		}
+
+		allCowCalving := len(eventCalvingList)
+		if allCowCalving > 0 {
+			res[pasture.Id] = fmt.Sprintf("%.2f", float64(allLiveCow)/float64(allCowCalving))
+		} else {
+			res[pasture.Id] = "0"
+		}
+	}
+	return res
+}
+
+// FirstBornDeathRate 头胎接产死亡率
+func (e *Entry) FirstBornDeathRate(pastureList []*model.AppPastureList, startAt, endAt int64) map[int64]string {
+	res := make(map[int64]string)
+
+	for _, pasture := range pastureList {
+		eventCalvingList := make([]*model.EventCalving, 0)
+		if err := e.DB.Model(new(model.EventCalving)).
+			Where("pasture_id = ?", pasture.Id).
+			Where("status = ?", pasturePb.IsShow_Ok).
+			Where("lact = ?", 0).
+			Where("reality_day BETWEEN ? AND ?", startAt, endAt).
+			Find(&eventCalvingList).Error; err != nil {
+			zaplog.Error("FirstBornSurvivalRate", zap.Any("err", err))
+		}
+		calvingIds := make([]int64, 0)
+		for _, v := range eventCalvingList {
+			calvingIds = append(calvingIds, v.Id)
+		}
+
+		var allDeathCow int64
+		if err := e.DB.Model(new(model.CalvingCalf)).
+			Select("count(*) as all_live_cow").
+			Where("pasture_id = ?", pasture.Id).
+			Where("calving_id IN (?)", calvingIds).
+			Where("is_live = ?", pasturePb.IsShow_No).
+			Where("birth_at BETWEEN ? AND ?", startAt, endAt).
+			Scan(&allDeathCow).Error; err != nil {
+			zaplog.Error("FirstBornSurvivalRate", zap.Any("err", err))
+		}
+
+		allCowCalving := len(eventCalvingList)
+		if allCowCalving > 0 {
+			res[pasture.Id] = fmt.Sprintf("%.2f", float64(allDeathCow)/float64(allCowCalving))
+		} else {
+			res[pasture.Id] = "0"
+		}
+	}
+	return res
+}
+
+// MultiparitySurvivalRate 经产牛接产成活率
+func (e *Entry) MultiparitySurvivalRate(pastureList []*model.AppPastureList, startAt, endAt int64) map[int64]string {
+	res := make(map[int64]string)
+	for _, pasture := range pastureList {
+		eventCalvingList := make([]*model.EventCalving, 0)
+		if err := e.DB.Model(new(model.EventCalving)).
+			Where("pasture_id = ?", pasture.Id).
+			Where("status = ?", pasturePb.IsShow_Ok).
+			Where("lact > ?", 0).
+			Where("reality_day BETWEEN ? AND ?", startAt, endAt).
+			Find(&eventCalvingList).Error; err != nil {
+			zaplog.Error("FirstBornSurvivalRate", zap.Any("err", err))
+		}
+
+		calvingIds := make([]int64, 0)
+		for _, v := range eventCalvingList {
+			calvingIds = append(calvingIds, v.Id)
+		}
+
+		var allLiveCow int64
+		if err := e.DB.Model(new(model.CalvingCalf)).
+			Select("count(*) as all_live_cow").
+			Where("pasture_id = ?", pasture.Id).
+			Where("calving_id IN (?)", calvingIds).
+			Where("is_live = ?", pasturePb.IsShow_Ok).
+			Where("birth_at BETWEEN ? AND ?", startAt, endAt).
+			Scan(&allLiveCow).Error; err != nil {
+			zaplog.Error("FirstBornSurvivalRate", zap.Any("err", err))
+		}
+
+		allCowCalving := len(eventCalvingList)
+		if allCowCalving > 0 {
+			res[pasture.Id] = fmt.Sprintf("%.2f", float64(allLiveCow)/float64(allCowCalving))
+		} else {
+			res[pasture.Id] = "0"
+		}
+	}
+	return res
+}
+
+// MultiparityDeathRate 经产牛接产死亡率
+func (e *Entry) MultiparityDeathRate(pastureList []*model.AppPastureList, startAt, endAt int64) map[int64]string {
+	res := make(map[int64]string)
+
+	for _, pasture := range pastureList {
+		eventCalvingList := make([]*model.EventCalving, 0)
+		if err := e.DB.Model(new(model.EventCalving)).
+			Where("pasture_id = ?", pasture.Id).
+			Where("status = ?", pasturePb.IsShow_Ok).
+			Where("lact > ?", 0).
+			Where("reality_day BETWEEN ? AND ?", startAt, endAt).
+			Find(&eventCalvingList).Error; err != nil {
+			zaplog.Error("FirstBornSurvivalRate", zap.Any("err", err))
+		}
+
+		calvingIds := make([]int64, 0)
+		for _, v := range eventCalvingList {
+			calvingIds = append(calvingIds, v.Id)
+		}
+
+		var allDeathCow int64
+		if err := e.DB.Model(new(model.CalvingCalf)).
+			Select("count(*) as all_live_cow").
+			Where("pasture_id = ?", pasture.Id).
+			Where("calving_id IN (?)", calvingIds).
+			Where("is_live = ?", pasturePb.IsShow_No).
+			Where("birth_at BETWEEN ? AND ?", startAt, endAt).
+			Scan(&allDeathCow).Error; err != nil {
+			zaplog.Error("FirstBornSurvivalRate", zap.Any("err", err))
+		}
+
+		allCowCalving := len(eventCalvingList)
+		if allCowCalving > 0 {
+			res[pasture.Id] = fmt.Sprintf("%.2f", float64(allDeathCow)/float64(allCowCalving))
+		} else {
+			res[pasture.Id] = "0"
+		}
+	}
+	return res
+}
+
+// AvgAgeFirstMate 平均首配日龄
+func (e *Entry) AvgAgeFirstMate(pastureList []*model.AppPastureList, startTime, endTime int64) map[int64]string {
+	res := make(map[int64]string)
+	for _, pasture := range pastureList {
+		eventMatingList := make([]*model.EventMating, 0)
+		if err := e.DB.Model(new(model.EventMating)).
+			Select("day_age,cow_id").
+			Where("pasture_id = ?", pasture.Id).
+			Where("status = ?", pasturePb.IsShow_Ok).
+			Where("lact = ?", 0).
+			Where("reality_day BETWEEN ? AND ?", startTime, endTime).
+			Find(&eventMatingList).Error; err != nil {
+			zaplog.Error("FindCalvingInterval", zap.Any("pasture_id", pasture.Id), zap.Any("err", err))
+		}
+		cowMatingDayAge := int32(0)
+		for _, v := range eventMatingList {
+			cowMatingDayAge += v.DayAge
+		}
+
+		if cowMatingDayAge > 0 {
+			res[pasture.Id] = fmt.Sprintf("%d", cowMatingDayAge/int32(len(eventMatingList)))
+		} else {
+			res[pasture.Id] = "0"
+		}
+	}
+	return res
+}
+
+// CowPregnantRate 后备牛怀孕比例
+func (e *Entry) CowPregnantRate(pastureList []*model.AppPastureList, caseName string) map[int64]string {
+	res := make(map[int64]string)
+	for _, pasture := range pastureList {
+		cowList := make([]*model.Cow, 0)
+		if err := e.DB.Model(new(model.Cow)).
+			Where("pasture_id = ?", pasture.Id).
+			Where("breed_status = ?", pasturePb.BreedStatus_Pregnant).
+			Where("admission_status = ?", pasturePb.AdmissionStatus_Admission).
+			Find(&cowList).Error; err != nil {
+			zaplog.Error("YouthPregnantRate", zap.Any("pasture_id", pasture.Id), zap.Any("error", err))
+		}
+
+		allYouthCow, allMultiCow := 0, 0
+		for _, cow := range cowList {
+			if cow.Lact == 0 {
+				allYouthCow += 1
+			} else {
+				allMultiCow += 1
+			}
+		}
+
+		if caseName == "youth_pregnant_rate" {
+			if allYouthCow > 0 {
+				res[pasture.Id] = fmt.Sprintf("%.2f", float64(allYouthCow)/float64(len(cowList)))
+			} else {
+				res[pasture.Id] = "0"
+			}
+		} else if caseName == "multiparty_pregnant_rate" {
+			if allMultiCow > 0 {
+				res[pasture.Id] = fmt.Sprintf("%.2f", float64(allMultiCow)/float64(len(cowList)))
+			} else {
+				res[pasture.Id] = "0"
+			}
+		} else {
+			res[pasture.Id] = "0"
+		}
+	}
+	return res
+}
+
+// PregnantCheckRate 孕检有胎率
+func (e *Entry) PregnantCheckRate(pastureList []*model.AppPastureList, startAt, endAt int64, isLact bool, pregnantCheckName string) map[int64]string {
+	res := make(map[int64]string)
+	for _, pasture := range pastureList {
+		eventPregnantCheckList := make([]*model.EventPregnantCheck, 0)
+		pref := e.DB.Model(new(model.EventPregnantCheck)).
+			Where("pasture_id = ?", pasture.Id).
+			Where("status = ?", pasturePb.IsShow_Ok).
+			Where("pregnant_check_name = ?", pregnantCheckName).
+			Where("reality_day BETWEEN ? AND ?", startAt, endAt)
+		if isLact {
+			pref = pref.Where("is_lact > ?", 0)
+		} else {
+			pref = pref.Where("is_lact = ?", 0)
+		}
+		if err := pref.Find(&eventPregnantCheckList).Error; err != nil {
+			zaplog.Error("FirstBornSurvivalRate", zap.Any("err", err))
+		}
+
+		isOK := 0
+		for _, v := range eventPregnantCheckList {
+			if v.PregnantCheckResult == pasturePb.PregnantCheckResult_Pregnant {
+				isOK += 1
+			}
+		}
+
+		if len(eventPregnantCheckList) > 0 {
+			res[pasture.Id] = fmt.Sprintf("%.2f", float64(isOK)/float64(len(eventPregnantCheckList)))
+		} else {
+			res[pasture.Id] = "0"
+		}
+	}
+	return res
+
+}
+
+func (e *Entry) ForbiddenCowNumber(pastureList []*model.AppPastureList, startAt, endAt int64) map[int64]string {
+	res := make(map[int64]string)
+	for _, pasture := range pastureList {
+		var count int64
+		if err := e.DB.Model(new(model.EventForbiddenMating)).
+			Where("pasture_id = ?", pasture.Id).
+			Where("is_show = ?", pasturePb.IsShow_Ok).
+			Where("forbidden_mating_at BETWEEN ? AND ?", startAt, endAt).
+			Count(&count).Error; err != nil {
+			zaplog.Error("FirstBornSurvivalRate", zap.Any("err", err))
+		}
+		res[pasture.Id] = fmt.Sprintf("%d", count)
+	}
+	return res
+}

+ 27 - 6
module/crontab/cow_neck_ring_error.go

@@ -17,7 +17,6 @@ func (e *Entry) CowNeckRingErrorEnter() (err error) {
 		return nil
 	}
 	for _, pasture := range pastureList {
-		e.DB.Model(new(model.NeckRingError)).Delete(new(model.NeckRingError)).Where("pasture_id = ?", pasture.Id)
 		e.CowNeckRingError(pasture.Id)
 		zaplog.Error("CowNeckRingErrorEnter-Success", zap.Any("pasture", pasture))
 	}
@@ -28,7 +27,7 @@ func (e *Entry) CowNeckRingError(pastureId int64) {
 	yesterday := time.Now().Local().AddDate(0, 0, -1).Format(model.LayoutDate2)
 	habitMinId, originalMinId := 0, 0
 	if err := e.DB.Model(new(model.NeckActiveHabit)).
-		Select("MIN(id) as id").
+		Select("IFNULL(MIN(id),0) as id").
 		Where("pasture_id = ?", pastureId).
 		Where("heat_date = ?", yesterday).
 		Scan(&habitMinId).Error; err != nil {
@@ -37,9 +36,9 @@ func (e *Entry) CowNeckRingError(pastureId int64) {
 	}
 
 	if err := e.DB.Model(new(model.NeckRingOriginal)).
-		Select("MIN(id) as id").
+		Select("IFNULL(MIN(id),0) as id").
 		Where("pasture_id = ?", pastureId).
-		Where("heat_date = ?", yesterday).
+		Where("active_date = ?", yesterday).
 		Scan(&originalMinId).Error; err != nil {
 		zaplog.Error("CowNeckRingError-Error", zap.Any("pastureId", pastureId), zap.Any("err", err))
 		return
@@ -58,6 +57,7 @@ func (e *Entry) CowNeckRingError(pastureId int64) {
 	updateNeckRingMap := make(map[int64]*model.NeckRingStats)
 	errorMap := e.NeckRingErrorMap()
 	for _, neckRing := range neckRingList {
+		// 佩戴后无信号
 		c1 := e.NeckRingErrorOfNoSignal(pastureId, neckRing, int64(habitMinId), yesterday)
 		if c1 > 0 {
 			updateNeckRingMap[neckRing.Id] = &model.NeckRingStats{
@@ -66,7 +66,11 @@ func (e *Entry) CowNeckRingError(pastureId int64) {
 				Describe:    "",
 			}
 		}
+		if neckRing.NeckRingNumber == "304" {
+			zaplog.Info("NeckRingErrorOfNoSignal", zap.Any("c1", c1), zap.Any("pastureId", pastureId))
+		}
 
+		// '疑似脱落', '电量低','接收少'
 		c2 := e.NeckRingErrorOfSuspectedFallOffAndLowBattery(pastureId, neckRing, int64(habitMinId), yesterday)
 		if c2 > 0 {
 			updateNeckRingMap[neckRing.Id] = &model.NeckRingStats{
@@ -76,6 +80,11 @@ func (e *Entry) CowNeckRingError(pastureId int64) {
 			}
 		}
 
+		if neckRing.NeckRingNumber == "304" {
+			zaplog.Info("NeckRingErrorOfNoSignal", zap.Any("c2", c2), zap.Any("pastureId", pastureId))
+		}
+
+		// 接收少
 		c3 := e.NeckRingErrorOfReceivingLess(pastureId, neckRing, int64(habitMinId), int64(originalMinId), yesterday)
 		if c3 > 0 {
 			updateNeckRingMap[neckRing.Id] = &model.NeckRingStats{
@@ -84,6 +93,12 @@ func (e *Entry) CowNeckRingError(pastureId int64) {
 				Describe:    "",
 			}
 		}
+
+		if neckRing.NeckRingNumber == "304" {
+			zaplog.Info("NeckRingErrorOfNoSignal", zap.Any("c3", c3), zap.Any("pastureId", pastureId))
+		}
+
+		// 数据延迟
 		c4 := e.NeckRingErrorOfDataLatency(pastureId, neckRing, int64(habitMinId), yesterday)
 		if c4 > 0 {
 			updateNeckRingMap[neckRing.Id] = &model.NeckRingStats{
@@ -92,8 +107,14 @@ func (e *Entry) CowNeckRingError(pastureId int64) {
 				Describe:    "",
 			}
 		}
-	}
 
+		if neckRing.NeckRingNumber == "304" {
+			zaplog.Info("NeckRingErrorOfNoSignal", zap.Any("c4", c4), zap.Any("pastureId", pastureId))
+		}
+	}
+	if v, ok := updateNeckRingMap[12372]; ok {
+		zaplog.Info("NeckRingErrorOfNoSignal", zap.Any("update", v), zap.Any("pastureId", pastureId))
+	}
 	if len(updateNeckRingMap) > 0 {
 		for id, v := range updateNeckRingMap {
 			if err := e.DB.Model(new(model.NeckRing)).
@@ -127,7 +148,7 @@ func (e *Entry) NeckRingErrorOfNoSignal(pastureId int64, neckRing *model.NeckRin
 	return pasturePb.NeckRingNumberError_Invalid
 }
 
-// NeckRingErrorOfSuspectedFallOffAndLowBattery 插入异常脖环 '疑似脱落', '电量低','接收少'
+// NeckRingErrorOfSuspectedFallOffAndLowBattery '疑似脱落', '电量低','接收少'
 func (e *Entry) NeckRingErrorOfSuspectedFallOffAndLowBattery(pastureId int64, neckRing *model.NeckRing, minId int64, dateTime string) pasturePb.NeckRingNumberError_Kind {
 	nowTime := time.Now().Local()