Przeglądaj źródła

crontab: indicators 指标收集

Yi 1 miesiąc temu
rodzic
commit
b35a51dad1

+ 42 - 34
model/indicators_data.go

@@ -8,40 +8,48 @@ import (
 )
 
 const (
-	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"
-	AvgRegistrationDays       = "avg_registration_days"
-	AvgPregnancyDays          = "avg_pregnancy_days"
-	AvgGestationalAge         = "avg_gestational_age"
-	Month17UnPregnancyRate    = "month17_un_pregnancy_rate"
-	Month20UnPregnancyRate    = "month20_un_pregnancy_rate"
+	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"
+	AvgRegistrationDays              = "avg_registration_days"
+	AvgPregnancyDays                 = "avg_pregnancy_days"
+	AvgGestationalAge                = "avg_gestational_age"
+	Month17UnPregnancyRate           = "month17_un_pregnancy_rate"
+	Month20UnPregnancyRate           = "month20_un_pregnancy_rate"
+	Multiparty150DaysUnPregnancyRate = "multiparty_150_days_un_pregnancy_rate"
+	MultipartyAbortionNumber         = "multiparty_abortion_number"
+	MultipartyAbortionRate           = "multiparty_abortion_rate"
+	MultipartyPregnancyNumber        = "multiparty_pregnancy_number"
+	MultipartyOutNumber              = "multiparty_out_number"
+	MultipartyDieNumber              = "multiparty_die_number"
+	Calving60DieRate                 = "calving60_die_rate"
+	Calving60OutRate                 = "calving60_out_rate"
 )
 
 type IndicatorsData struct {

+ 5 - 15
module/backend/calendar.go

@@ -287,10 +287,8 @@ func (s *StoreEntry) ImmunisationCowList(ctx context.Context, req *pasturePb.Ite
 			Total:      int32(count),
 			Page:       pagination.Page,
 			PageSize:   pagination.PageSize,
-			HeaderSort: []string{"id", "cowId", "planDay", "planName", "penName", "dayAge", "earNumber", "planId"},
+			HeaderSort: []string{"planDay", "planName", "penName", "dayAge", "earNumber", "planId"},
 			Header: map[string]string{
-				"id":        "编号",
-				"cowId":     "牛号",
 				"earNumber": "耳标号",
 				"penName":   "栏舍",
 				"dayAge":    "日龄",
@@ -352,12 +350,10 @@ func (s *StoreEntry) SameTimeCowList(ctx context.Context, req *pasturePb.ItemsRe
 			Total:    int32(count),
 			Page:     pagination.Page,
 			PageSize: pagination.PageSize,
-			HeaderSort: []string{"id", "cowId", "earNumber", "breedStatusName", "cowTypeName", "planDayAtFormat", "penName",
+			HeaderSort: []string{"earNumber", "breedStatusName", "cowTypeName", "planDayAtFormat", "penName",
 				"lact", "calvingAge", "abortionAge", "dayAge", "status", "sameTimeTypeName", "matingTimes", "calvingAtFormat",
 				"abortionAtFormat", "sameTimeName"},
 			Header: map[string]string{
-				"id":               "编号",
-				"cowId":            "牛号",
 				"earNumber":        "耳标号",
 				"breedStatusName":  "繁殖状态",
 				"cowTypeName":      "牛只类型",
@@ -446,11 +442,9 @@ func (s *StoreEntry) PregnancyCheckCowList(ctx context.Context, req *pasturePb.I
 			Total:    int32(count),
 			Page:     pagination.Page,
 			PageSize: pagination.PageSize,
-			HeaderSort: []string{"id", "cowId", "earNumber", "cowTypeName", "penName", "lact", "dayAge", "breedStatus", "planDay",
+			HeaderSort: []string{"earNumber", "cowTypeName", "penName", "lact", "dayAge", "breedStatus", "planDay",
 				"checkTypeName", "status", "matingTimes", "calvingAtFormat", "matingAtFormat", "matingAge", "bullId", "pregnancyAge"},
 			Header: map[string]string{
-				"id":              "编号",
-				"cowId":           "牛号",
 				"earNumber":       "耳标号",
 				"cowTypeName":     "牛只类型",
 				"penName":         "栏舍",
@@ -508,10 +502,8 @@ func (s *StoreEntry) WeaningCowList(ctx context.Context, req *pasturePb.ItemsReq
 			Total:      int32(count),
 			Page:       pagination.Page,
 			PageSize:   pagination.PageSize,
-			HeaderSort: []string{"id", "cowId", "earNumber", "penName", "dayAge", "planDayFormat", "birthAtFormat", "currentWeight"},
+			HeaderSort: []string{"earNumber", "penName", "dayAge", "planDayFormat", "birthAtFormat", "currentWeight"},
 			Header: map[string]string{
-				"id":            "编号",
-				"cowId":         "牛号",
 				"earNumber":     "耳标号",
 				"penName":       "栏舍",
 				"dayAge":        "日龄",
@@ -577,11 +569,9 @@ func (s *StoreEntry) MatingCowList(ctx context.Context, req *pasturePb.ItemsRequ
 			Total:    int32(count),
 			Page:     pagination.Page,
 			PageSize: pagination.PageSize,
-			HeaderSort: []string{"id", "cowId", "earNumber", "dayAge", "lact", "penName", "planDay", "breedStatusName",
+			HeaderSort: []string{"earNumber", "dayAge", "lact", "penName", "planDay", "breedStatusName",
 				"cowTypeName", "calvingAge", "abortionAge", "exposeEstrusTypeName", "lastCalvingAtFormat"},
 			Header: map[string]string{
-				"id":                   "编号",
-				"cowId":                "牛号",
 				"earNumber":            "耳标号",
 				"breedStatusName":      "繁殖状态",
 				"cowTypeName":          "牛只类型",

+ 7 - 17
module/backend/calendar_more.go

@@ -63,13 +63,9 @@ func (s *StoreEntry) CalvingCowList(ctx context.Context, req *pasturePb.ItemsReq
 			Total:    int32(count),
 			Page:     pagination.Page,
 			PageSize: pagination.PageSize,
-			HeaderSort: []string{
-				"id", "cowId", "earNumber", "penName", "lact", "breedStatusName", "matingAge", "dayAge", "status",
-				"bullId", "planDay", "matingAtFormat", "currentWeight",
-			},
+			HeaderSort: []string{"earNumber", "penName", "lact", "breedStatusName", "matingAge", "dayAge", "status",
+				"bullId", "planDay", "matingAtFormat", "currentWeight"},
 			Header: map[string]string{
-				"id":              "编号",
-				"cowId":           "牛号",
 				"earNumber":       "耳标号",
 				"breedStatusName": "繁殖状态",
 				"penName":         "栏舍",
@@ -139,15 +135,11 @@ func (s *StoreEntry) DryMilkCowList(ctx context.Context, req *pasturePb.ItemsReq
 		Code: http.StatusOK,
 		Msg:  "ok",
 		Data: &pasturePb.DruMilkItemsData{
-			Total:    int32(count),
-			Page:     pagination.Page,
-			PageSize: pagination.PageSize,
-			HeaderSort: []string{
-				"id", "cowId", "earNumber", "dayAge", "penName", "lact", "pregnancyAge", "status", "bullNumber", "planDay", "calvingAtFormat",
-			},
+			Total:      int32(count),
+			Page:       pagination.Page,
+			PageSize:   pagination.PageSize,
+			HeaderSort: []string{"earNumber", "dayAge", "penName", "lact", "pregnancyAge", "status", "bullNumber", "planDay", "calvingAtFormat"},
 			Header: map[string]string{
-				"id":              "编号",
-				"cowId":           "牛号",
 				"earNumber":       "耳标号",
 				"breedStatusName": "繁殖状态",
 				"penName":         "栏舍",
@@ -199,10 +191,8 @@ func (s *StoreEntry) TreatmentCowList(ctx context.Context, req *pasturePb.ItemsR
 			Total:      int32(count),
 			PageSize:   pagination.PageSize,
 			Page:       pagination.Page,
-			HeaderSort: []string{"id", "cowId", "earNumber", "penName", "diagnoseName", "healthStatus", "lastPrescriptionName", "treatmentDays", "onsetDays"},
+			HeaderSort: []string{"earNumber", "penName", "diagnoseName", "healthStatus", "lastPrescriptionName", "treatmentDays", "onsetDays"},
 			Header: map[string]string{
-				"id":                   "编号",
-				"cowId":                "牛号",
 				"earNumber":            "耳标",
 				"diagnoseName":         "疾病名称",
 				"healthStatus":         "健康状态",

+ 58 - 1
module/backend/cow.go

@@ -6,9 +6,11 @@ import (
 	"fmt"
 	"kpt-pasture/model"
 	"kpt-pasture/util"
+	"log"
 	"net/http"
 	"sort"
 	"strings"
+	"sync"
 	"time"
 
 	"gorm.io/gorm"
@@ -344,7 +346,7 @@ func (s *StoreEntry) CowIQR(ctx context.Context, cowInfo *model.Cow, curveName s
 		cowIds = append(cowIds, cow.Id)
 	}
 
-	// 行为曲线数据
+	/*// 行为曲线数据
 	neckActiveHabitList := make([]*model.NeckActiveHabit, 0)
 	if err := s.DB.Table(new(model.NeckActiveHabit).TableName()).
 		Select("rumina,intake,inactive,gasp,high,active,active_time").
@@ -354,6 +356,11 @@ func (s *StoreEntry) CowIQR(ctx context.Context, cowInfo *model.Cow, curveName s
 		Where("heat_date BETWEEN ? AND ?", dayRange[0], dayRange[len(dayRange)-1]).
 		Find(&neckActiveHabitList).Error; err != nil {
 		return q1, q3
+	}*/
+
+	neckActiveHabitList, err := s.GetNeckActiveHabitsConcurrent(cowInfo, cowIds, dayRange)
+	if err != nil {
+		return q1, q3
 	}
 
 	penOriginal := make(map[string][]int32)
@@ -409,6 +416,56 @@ func (s *StoreEntry) CowIQR(ctx context.Context, cowInfo *model.Cow, curveName s
 	return q1, q3
 }
 
+func (s *StoreEntry) GetNeckActiveHabitsConcurrent(cowInfo *model.Cow, cowIds []int64, dayRange []string) ([]*model.NeckActiveHabit, error) {
+	var neckActiveHabitList []*model.NeckActiveHabit
+	var batchSize = 100
+	var wg sync.WaitGroup
+	var mu sync.Mutex
+
+	// 使用工作池控制并发数
+	workerCount := 5 // 并发数,可根据实际情况调整
+	sem := make(chan struct{}, workerCount)
+
+	for i := 0; i < len(cowIds); i += batchSize {
+		end := i + batchSize
+		if end > len(cowIds) {
+			end = len(cowIds)
+		}
+
+		batch := cowIds[i:end]
+
+		wg.Add(1)
+		go func(batch []int64) {
+			defer wg.Done()
+			sem <- struct{}{} // 获取令牌
+
+			var batchResults []*model.NeckActiveHabit
+			err := s.DB.Table(new(model.NeckActiveHabit).TableName()).
+				Select("rumina,intake,inactive,gasp,high,active,active_time").
+				Where("pasture_id = ?", cowInfo.PastureId).
+				Where("is_show = ?", pasturePb.IsShow_Ok).
+				Where("cow_id IN (?)", batch).
+				Where("heat_date BETWEEN ? AND ?", dayRange[0], dayRange[len(dayRange)-1]).
+				Find(&batchResults).Error
+
+			<-sem // 释放令牌
+
+			if err != nil {
+				// 错误处理逻辑,可根据需要调整
+				log.Printf("batch query error: %v", err)
+				return
+			}
+
+			mu.Lock()
+			neckActiveHabitList = append(neckActiveHabitList, batchResults...)
+			mu.Unlock()
+		}(batch)
+	}
+
+	wg.Wait()
+	return neckActiveHabitList, nil
+}
+
 func (s *StoreEntry) CowGrowthCurve(ctx context.Context, req *pasturePb.CowGrowthCurveRequest) (*pasturePb.CowGrowthCurveResponse, error) {
 	userModel, err := s.GetUserModel(ctx)
 	if err != nil {

+ 16 - 4
module/backend/enum_options.go

@@ -66,11 +66,17 @@ func (s *StoreEntry) DiseaseTypeOptions(ctx context.Context, isChildren string)
 }
 
 func (s *StoreEntry) DiseaseOptions(ctx context.Context) (*pasturePb.ConfigOptionsListResponse, error) {
+	userModel, err := s.GetUserModel(ctx)
+	if err != nil {
+		return nil, xerr.WithStack(err)
+	}
+
 	diseaseList := make([]*model.Disease, 0)
 	pref := s.DB.Table(new(model.Disease).TableName()).
-		Where("is_show =? ", pasturePb.IsShow_Ok)
+		Where("is_show =? ", pasturePb.IsShow_Ok).
+		Where("pasture_id =? ", userModel.AppPasture.Id)
 
-	if err := pref.Find(&diseaseList).Error; err != nil {
+	if err = pref.Find(&diseaseList).Error; err != nil {
 		return nil, xerr.WithStack(err)
 	}
 	return &pasturePb.ConfigOptionsListResponse{
@@ -81,11 +87,17 @@ func (s *StoreEntry) DiseaseOptions(ctx context.Context) (*pasturePb.ConfigOptio
 }
 
 func (s *StoreEntry) PrescriptionOptions(ctx context.Context) (*pasturePb.ConfigOptionsListResponse, error) {
+	userModel, err := s.GetUserModel(ctx)
+	if err != nil {
+		return nil, xerr.WithStack(err)
+	}
+
 	prescriptionList := make([]*model.Prescription, 0)
 	pref := s.DB.Table(new(model.Prescription).TableName()).
-		Where("is_show =? ", pasturePb.IsShow_Ok)
+		Where("pasture_id = ? ", userModel.AppPasture.Id).
+		Where("is_show = ? ", pasturePb.IsShow_Ok)
 
-	if err := pref.Find(&prescriptionList).Error; err != nil {
+	if err = pref.Find(&prescriptionList).Error; err != nil {
 		return nil, xerr.WithStack(err)
 	}
 	return &pasturePb.ConfigOptionsListResponse{

+ 17 - 0
module/crontab/cow_cron.go

@@ -137,6 +137,23 @@ func (e *Entry) Indicators() error {
 			pastureIndicatorList = e.MonthUnPregnancyRate(pastureList, 17)
 		case model.Month20UnPregnancyRate:
 			pastureIndicatorList = e.MonthUnPregnancyRate(pastureList, 20)
+		case model.Multiparty150DaysUnPregnancyRate:
+			pastureIndicatorList = e.Multiparty150DaysUnPregnancyRate(pastureList, 150)
+		case model.MultipartyAbortionNumber:
+			pastureIndicatorList = e.MultipartyAbortionNumber(pastureList, startTime, endTime, pasturePb.IsShow_No)
+		case model.MultipartyPregnancyNumber:
+			pastureIndicatorList = e.MultipartyPregnancyNumber(pastureList)
+		case model.MultipartyAbortionRate:
+			pastureIndicatorList = e.MultipartyAbortionRate(pastureList, startTime, endTime)
+		case model.MultipartyOutNumber:
+			pastureIndicatorList = e.MultipartyOutNumber(pastureList, startTime, endTime)
+		case model.MultipartyDieNumber:
+			pastureIndicatorList = e.MultipartyDieNumber(pastureList, startTime, endTime)
+		case model.Calving60DieRate:
+			pastureIndicatorList = e.CalvingDieRate(pastureList, startTime, endTime, 60)
+		case model.Calving60OutRate:
+			pastureIndicatorList = e.CalvingOutRate(pastureList, startTime, endTime, 60)
+
 		}
 
 		for pastureId, value := range pastureIndicatorList {

+ 30 - 0
module/crontab/cow_indicators_breed.go

@@ -457,3 +457,33 @@ func (e *Entry) MonthUnPregnancyRate(pastureList []*model.AppPastureList, month
 	}
 	return res
 }
+
+// Multiparty150DaysUnPregnancyRate 成母牛150天未孕比例
+func (e *Entry) Multiparty150DaysUnPregnancyRate(pastureList []*model.AppPastureList, days int32) 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("admission_status = ?", pasturePb.AdmissionStatus_Admission).
+			Where("sex = ?", pasturePb.Genders_Female).
+			Where("calving_age >= ?", days).
+			Where("lact > ?", 0).
+			Find(&cowList).Error; err != nil {
+			zaplog.Error("Multiparty150DaysUnPregnancyRate", zap.Any("err", err), zap.Any("pastureId", pasture.Id))
+		}
+
+		count := int32(0)
+		for _, cow := range cowList {
+			if cow.BreedStatus != pasturePb.BreedStatus_Pregnant {
+				count++
+			}
+		}
+		if count > 0 {
+			res[pasture.Id] = fmt.Sprintf("%.2f", float64(count)/float64(len(cowList)))
+		} else {
+			res[pasture.Id] = "0"
+		}
+	}
+	return res
+}

+ 165 - 0
module/crontab/cow_indicators_health.go

@@ -3,6 +3,8 @@ package crontab
 import (
 	"fmt"
 	"kpt-pasture/model"
+	"math"
+	"strconv"
 
 	pasturePb "gitee.com/xuyiping_admin/go_proto/proto/go/backend/cow"
 	"gitee.com/xuyiping_admin/pkg/logger/zaplog"
@@ -102,3 +104,166 @@ func (e *Entry) FindDeathNumber(pastureList []*model.AppPastureList, startTime,
 	}
 	return res
 }
+
+// MultipartyAbortionNumber 成母牛流产数
+func (e *Entry) MultipartyAbortionNumber(pastureList []*model.AppPastureList, startTime, endTime int64, isAppend pasturePb.IsShow_Kind) map[int64]string {
+	res := make(map[int64]string)
+	for _, pasture := range pastureList {
+		var count int64
+		if err := e.DB.Model(new(model.EventAbortion)).
+			Where("pasture_id = ?", pasture.Id).
+			Where("abortion_at BETWEEN ? AND ?", startTime, endTime).
+			Where("is_append = ?", pasturePb.IsShow_Ok).
+			Where("lact > ?", 0).
+			Where("is_append = ?", isAppend).
+			Count(&count).Error; err != nil {
+			zaplog.Error("multipartyAbortionNumber", zap.Any("pastureId", pasture), zap.Any("err", err))
+		}
+		res[pasture.Id] = fmt.Sprintf("%d", count)
+	}
+	return res
+}
+
+// MultipartyPregnancyNumber 成母牛怀孕头数
+func (e *Entry) MultipartyPregnancyNumber(pastureList []*model.AppPastureList) map[int64]string {
+	res := make(map[int64]string)
+	for _, pasture := range pastureList {
+		var count int64
+		if err := e.DB.Model(new(model.Cow)).
+			Where("pasture_id = ?", pasture.Id).
+			Where("sex = ?", pasturePb.Genders_Female).
+			Where("breed_status = ?", pasturePb.BreedStatus_Pregnant).
+			Where("lact > ?", 0).
+			Count(&count).Error; err != nil {
+			zaplog.Error("")
+		}
+		if count > 0 {
+			res[pasture.Id] = fmt.Sprintf("%d", count)
+		} else {
+			res[pasture.Id] = "0"
+		}
+	}
+	return res
+}
+
+// MultipartyAbortionRate 成母牛流产率
+func (e *Entry) MultipartyAbortionRate(pastureList []*model.AppPastureList, startTime, endTime int64) map[int64]string {
+	abortionNumberMap := e.MultipartyAbortionNumber(pastureList, startTime, endTime, pasturePb.IsShow_Ok)
+	pregnancyNumberMap := e.MultipartyPregnancyNumber(pastureList)
+
+	res := make(map[int64]string)
+
+	for pastureId, value1 := range abortionNumberMap {
+		var info bool
+		if value2, ok := pregnancyNumberMap[pastureId]; ok {
+			v1, _ := strconv.ParseInt(value1, 10, 64)
+			v2, _ := strconv.ParseInt(value2, 10, 64)
+			if v2 > 0 {
+				info = true
+				abortionRate := float32(math.Round(float64(v1)/float64(v2)*100) / 100)
+				res[pastureId] = fmt.Sprintf("%f", abortionRate)
+			}
+		}
+		if !info {
+			res[pastureId] = "0"
+		}
+	}
+
+	return res
+}
+
+// MultipartyOutNumber 成母牛淘汰牛头数
+func (e *Entry) MultipartyOutNumber(pastureList []*model.AppPastureList, startTime, endTime int64) map[int64]string {
+	res := make(map[int64]string)
+	for _, pasture := range pastureList {
+		var count int64
+		if err := e.DB.Table(fmt.Sprintf("%s as a", new(model.EventSaleCow).TableName())).
+			Joins(fmt.Sprintf("JOIN %s AS b on a.sale_id = b.id", new(model.EventSale).TableName())).
+			Select("COUNT(*) AS count").
+			Where("a.pasture_id = ?", pasture.Id).
+			Where("b.sale_at BETWEEN ? AND ?", startTime, endTime).
+			Where("a.out_reasons_kind = ?", pasturePb.SalesType_Out).
+			Where("a.lact > ?", 0).
+			Scan(&count).Error; err != nil {
+			zaplog.Error("MultipartyOutNumber", zap.Any("pasture_id", pasture.Id), zap.Any("err", err))
+		}
+
+		res[pasture.Id] = fmt.Sprintf("%d", count)
+	}
+	return res
+}
+
+// MultipartyDieNumber 成母牛死亡数
+func (e *Entry) MultipartyDieNumber(pastureList []*model.AppPastureList, startTime, endTime int64) map[int64]string {
+	res := make(map[int64]string)
+	for _, pasture := range pastureList {
+		var count int64
+		if err := e.DB.Model(new(model.EventDeath)).
+			Where("pasture_id = ?", pasture.Id).
+			Where("death_at BETWEEN ? AND ?", startTime, endTime).
+			Where("lact > ?", 0).
+			Scan(&count).Error; err != nil {
+			zaplog.Error("MultipartyOutNumber", zap.Any("pasture_id", pasture.Id), zap.Any("err", err))
+		}
+		res[pasture.Id] = fmt.Sprintf("%d", count)
+	}
+	return res
+}
+
+// CalvingDieRate 产后指定天数死亡率
+func (e *Entry) CalvingDieRate(pastureList []*model.AppPastureList, startTime, endTime int64, calvingAge int32) map[int64]string {
+	res := make(map[int64]string)
+	for _, pasture := range pastureList {
+		eventDeathList := make([]*model.EventDeath, 0)
+		if err := e.DB.Model(new(model.EventDeath)).
+			Where("pasture_id = ?", pasture.Id).
+			Where("death_at BETWEEN ? AND ?", startTime, endTime).
+			Find(&eventDeathList).Error; err != nil {
+			zaplog.Error("Calving60DieRate", zap.Any("pastureId", pasture.Id), zap.Any("err", err))
+		}
+		count := int32(0)
+		for _, v := range eventDeathList {
+			if v.CalvingAge <= calvingAge {
+				count++
+			}
+		}
+
+		if count > 0 {
+			res[pasture.Id] = fmt.Sprintf("%f", float32(math.Round(float64(count)/float64(len(eventDeathList))*100)/100))
+		} else {
+			res[pasture.Id] = "0"
+		}
+	}
+	return res
+}
+
+// CalvingOutRate 产后指定天数淘汰率
+func (e *Entry) CalvingOutRate(pastureList []*model.AppPastureList, startTime, endTime int64, calvingAge int32) map[int64]string {
+	res := make(map[int64]string)
+	for _, pasture := range pastureList {
+		eventSaleCowList := make([]*model.EventSaleCow, 0)
+		if err := e.DB.Table(fmt.Sprintf("%s as a", new(model.EventSaleCow).TableName())).
+			Joins(fmt.Sprintf("JOIN %s AS b on a.sale_id = b.id", new(model.EventSale).TableName())).
+			Select("a.*").
+			Where("a.pasture_id = ?", pasture.Id).
+			Where("b.sale_at BETWEEN ? AND ?", startTime, endTime).
+			Where("a.out_reasons_kind = ?", pasturePb.SalesType_Out).
+			Find(&eventSaleCowList).Error; err != nil {
+			zaplog.Error("MultipartyOutNumber", zap.Any("pasture_id", pasture.Id), zap.Any("err", err))
+		}
+
+		count := int32(0)
+		for _, v := range eventSaleCowList {
+			if v.CalvingAge <= calvingAge {
+				count++
+			}
+		}
+
+		if count > 0 {
+			res[pasture.Id] = fmt.Sprintf("%f", float32(math.Round(float64(count)/float64(len(eventSaleCowList))*100)/100))
+		} else {
+			res[pasture.Id] = "0"
+		}
+	}
+	return res
+}

+ 1 - 19
module/crontab/cow_neck_ring_error.go

@@ -66,9 +66,6 @@ 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)
@@ -80,10 +77,6 @@ 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 {
@@ -94,10 +87,6 @@ func (e *Entry) CowNeckRingError(pastureId int64) {
 			}
 		}
 
-		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 {
@@ -107,14 +96,8 @@ 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)).
@@ -154,7 +137,6 @@ func (e *Entry) NeckRingErrorOfSuspectedFallOffAndLowBattery(pastureId int64, ne
 
 	neckRingHabitList := make([]*model.NeckActiveHabit, 0)
 	if err := e.DB.Model(new(model.NeckActiveHabit)).
-		Select(``).
 		Where("id >= ?", minId).
 		Where("neck_ring_number = ?", neckRing.NeckRingNumber).
 		Where("pasture_id = ?", pastureId).

+ 15 - 11
module/crontab/health_warning.go

@@ -77,7 +77,6 @@ func (e *Entry) HealthWarning(pastureId int64, processIds []int64) {
 }
 
 func (e *Entry) updateNeckRingHealth(pastureId int64, healthWarningList []*model.NeckRingHealth) {
-	zaplog.Info("HealthWarning", zap.Any("healthWarningList", healthWarningList))
 	for _, v := range healthWarningList {
 		startAt := util.TimeParseLocalUnix(v.HeatDate)
 		endAt := util.TimeParseLocalEndUnix(v.HeatDate)
@@ -94,7 +93,7 @@ func (e *Entry) updateNeckRingHealth(pastureId int64, healthWarningList []*model
 			v.IsImmunization = pasturePb.IsShow_Ok
 		}
 	}
-
+	zaplog.Info("HealthWarning", zap.Any("healthWarningList", healthWarningList))
 	if err := e.DB.Model(new(model.NeckRingHealth)).Create(&healthWarningList).Error; err != nil {
 		zaplog.Error("HealthWarning", zap.Any("error", err), zap.Any("healthWarningList", healthWarningList))
 	}
@@ -126,7 +125,10 @@ func (e *Entry) NeckRingHealthWarning() error {
 			Delete(new(model.NeckRingHealthWarning))
 
 		if err := e.UpdateNeckRingHealth(pasture.Id); err != nil {
-			zaplog.Error("NeckRingHealthWarning", zap.Any("UpdateNeckRingHealth", err), zap.Any("pasture", pasture))
+			zaplog.Error("NeckRingHealthWarning",
+				zap.Any("UpdateNeckRingHealth", err),
+				zap.Any("pasture", pasture),
+			)
 		}
 	}
 	return nil
@@ -155,7 +157,8 @@ func (e *Entry) UpdateNeckRingHealth(pastureId int64) error {
 			Create(&newNeckRingHealthWarningList).Error; err != nil {
 			zaplog.Error("UpdateNeckRingHealth",
 				zap.Any("error", err),
-				zap.Any("newNeckRingHealthWarningList", newNeckRingHealthWarningList))
+				zap.Any("newNeckRingHealthWarningList", newNeckRingHealthWarningList),
+			)
 		}
 	}
 	return nil
@@ -221,19 +224,20 @@ func (e *Entry) FindNewNeckRingHealthWarning(pastureId int64, healthValue int32)
 
 	newNeckRingHealthWarningList := make([]*model.NeckRingHealthWarning, 0)
 	for _, v := range neckRingHealthList {
-		if v.HeatDate == "" || v.HeatDate != nowTime.Format(model.LayoutDate2) {
+		if v.HeatDate == "" {
 			continue
 		}
 		cowInfo, err := e.GetCowById(pastureId, v.CowId)
-		if err != nil {
-			continue
-		}
-
-		if cowInfo == nil {
+		if err != nil || cowInfo == nil {
 			continue
 		}
-
 		newScore := calculateNewScore(v)
+		zaplog.Info("calculateNewScore",
+			zap.Any("newScore", newScore),
+			zap.Any("v", v),
+			zap.Any("healthValue", healthValue),
+			zap.Any("cowInfo", cowInfo),
+		)
 		if newScore > healthValue {
 			continue
 		}