Преглед на файлове

analysis:单因素受胎率分析之按周期分析

ping преди 5 месеца
родител
ревизия
fdbc2bce46
променени са 6 файла, в които са добавени 400 реда и са изтрити 24 реда
  1. 1 1
      go.mod
  2. 2 8
      go.sum
  3. 6 1
      model/event_mating.go
  4. 385 11
      module/backend/analysis_breed.go
  5. 3 3
      module/backend/event_breed.go
  6. 3 0
      util/util.go

+ 1 - 1
go.mod

@@ -3,7 +3,7 @@ module kpt-pasture
 go 1.17
 
 require (
-	gitee.com/xuyiping_admin/go_proto v0.0.0-20241030093930-85784b846ab4
+	gitee.com/xuyiping_admin/go_proto v0.0.0-20241101012151-bbf157c372ed
 	gitee.com/xuyiping_admin/pkg v0.0.0-20241029095841-aa1fe89b557a
 	github.com/dgrijalva/jwt-go v3.2.0+incompatible
 	github.com/eko/gocache v1.1.0

+ 2 - 8
go.sum

@@ -36,16 +36,10 @@ 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-20241029090643-f4a03baaf55f h1:TUKiPo/cMn3pbGcPXGs1UOjc7Ut8B8flRZn5XGW4A7Q=
-gitee.com/xuyiping_admin/go_proto v0.0.0-20241029090643-f4a03baaf55f/go.mod h1:BKrFW6YLDectlQcQk3FYKBeXvjEiodAKJ5rq7O/QiPE=
-gitee.com/xuyiping_admin/go_proto v0.0.0-20241030025349-c1c626237949 h1:rQHN32xKgIVeWTNjrDvadRSdWDF39b+ofx0VQyFFO8o=
-gitee.com/xuyiping_admin/go_proto v0.0.0-20241030025349-c1c626237949/go.mod h1:BKrFW6YLDectlQcQk3FYKBeXvjEiodAKJ5rq7O/QiPE=
-gitee.com/xuyiping_admin/go_proto v0.0.0-20241030054226-4e23887c505f h1:e+yijGs8inTX+zmjAag5ci2UZbnXh/M2aXO8F12X+sM=
-gitee.com/xuyiping_admin/go_proto v0.0.0-20241030054226-4e23887c505f/go.mod h1:BKrFW6YLDectlQcQk3FYKBeXvjEiodAKJ5rq7O/QiPE=
-gitee.com/xuyiping_admin/go_proto v0.0.0-20241030072744-bd563f5efd21 h1:lKRO0G5QbIoNxN2pXajX6VKCTXolE/1G50m/TAeaoL4=
-gitee.com/xuyiping_admin/go_proto v0.0.0-20241030072744-bd563f5efd21/go.mod h1:BKrFW6YLDectlQcQk3FYKBeXvjEiodAKJ5rq7O/QiPE=
 gitee.com/xuyiping_admin/go_proto v0.0.0-20241030093930-85784b846ab4 h1:7GYTs67byos3e1BIdGMjJM8qlWNpho5LQovOCcrNVxo=
 gitee.com/xuyiping_admin/go_proto v0.0.0-20241030093930-85784b846ab4/go.mod h1:BKrFW6YLDectlQcQk3FYKBeXvjEiodAKJ5rq7O/QiPE=
+gitee.com/xuyiping_admin/go_proto v0.0.0-20241101012151-bbf157c372ed h1:w4R1HdbIkcHhRunjPTPT8PPk4TybANIBy4OuC89Na4c=
+gitee.com/xuyiping_admin/go_proto v0.0.0-20241101012151-bbf157c372ed/go.mod h1:BKrFW6YLDectlQcQk3FYKBeXvjEiodAKJ5rq7O/QiPE=
 gitee.com/xuyiping_admin/pkg v0.0.0-20241029095841-aa1fe89b557a h1:z6Pp4HmdcxEZ43avmbFoE3vwEYcWnIefqEEZZWCHfek=
 gitee.com/xuyiping_admin/pkg v0.0.0-20241029095841-aa1fe89b557a/go.mod h1:8tF25X6pE9WkFCczlNAC0K2mrjwKvhhp02I7o0HtDxY=
 github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=

+ 6 - 1
model/event_mating.go

@@ -153,12 +153,17 @@ type MatingTimelyChart struct {
 func (e EventMatingSlice) ToPB2() []*pasturePb.CowList {
 	res := make([]*pasturePb.CowList, len(e))
 	for i, v := range e {
+		calvingAt := ""
+		if v.CalvingAt > 0 {
+			calvingAt = time.Unix(v.CalvingAt, 0).Format(LayoutDate2)
+		}
 		res[i] = &pasturePb.CowList{
 			CowId:           int32(v.CowId),
 			DayAge:          int32(v.DayAge),
 			CalvingAge:      v.CalvingAge,
 			MatingAtFormat:  time.Unix(v.RealityDay, 0).Format(LayoutDate2),
-			CalvingAtFormat: time.Unix(v.CalvingAt, 0).Format(LayoutDate2),
+			CalvingAtFormat: calvingAt,
+			Lact:            int32(v.Lact),
 		}
 	}
 	return res

+ 385 - 11
module/backend/analysis_breed.go

@@ -2,13 +2,13 @@ package backend
 
 import (
 	"context"
+	"fmt"
 	"kpt-pasture/model"
 	"kpt-pasture/util"
 	"net/http"
 
-	"gitee.com/xuyiping_admin/pkg/xerr"
-
 	pasturePb "gitee.com/xuyiping_admin/go_proto/proto/go/backend/cow"
+	"gitee.com/xuyiping_admin/pkg/xerr"
 )
 
 // SingleFactorInfantSurvivalRateAnalysis 单因素受胎率分析
@@ -33,14 +33,23 @@ func (s *StoreEntry) SingleFactorInfantSurvivalRateAnalysis(ctx context.Context,
 	case pasturePb.SingleFactorAnalysisMethod_Months:
 		list, err = s.SingleFactorAnalysisMethodMonths(ctx, req)
 	case pasturePb.SingleFactorAnalysisMethod_Mating_Times:
+		list, err = s.SingleFactorAnalysisMethodMatingTimes(ctx, req)
 	case pasturePb.SingleFactorAnalysisMethod_Breeding_Method:
+		list, err = s.SingleFactorAnalysisMethodBreeding(ctx, req)
 	case pasturePb.SingleFactorAnalysisMethod_Breeding_Company:
+		list, err = s.SingleFactorAnalysisMethodBreedingCompany(ctx, req)
 	case pasturePb.SingleFactorAnalysisMethod_Operation:
+		list, err = s.SingleFactorAnalysisMethodOperation(ctx, req)
 	case pasturePb.SingleFactorAnalysisMethod_Mating_Interval:
+		list, err = s.SingleFactorAnalysisMethodMatingInterval(ctx, req)
 	case pasturePb.SingleFactorAnalysisMethod_Bull:
+		list, err = s.SingleFactorAnalysisMethodBull(ctx, req)
 	case pasturePb.SingleFactorAnalysisMethod_Breeding_Cycle:
+		list, err = s.SingleFactorAnalysisMethodBreedingCycle(ctx, req)
 	case pasturePb.SingleFactorAnalysisMethod_Week:
+		list, err = s.SingleFactorAnalysisMethodWeek(ctx, req)
 	case pasturePb.SingleFactorAnalysisMethod_Lact:
+		list, err = s.SingleFactorAnalysisMethodLact(ctx, req)
 	default:
 		return nil, xerr.Custom("错误的统计方式")
 	}
@@ -76,7 +85,9 @@ func (s *StoreEntry) SingleFactorInfantSurvivalRateAnalysis(ctx context.Context,
 		list[i].TotalRate = float32(util.RoundToTwoDecimals(float64(v) / float64(allTotalCount) * 100))
 	}
 
-	chart.AveragePregnantRate = float32(util.RoundToTwoDecimals(allPregnantRate/float64(len(list))) * 100)
+	if len(list) > 0 && allPregnantRate > 0 {
+		chart.AveragePregnantRate = float32(util.RoundToTwoDecimals(allPregnantRate/float64(len(list))) * 100)
+	}
 
 	return &pasturePb.SingleFactorPregnancyRateResponse{
 		Code:    http.StatusOK,
@@ -90,12 +101,47 @@ func (s *StoreEntry) SingleFactorInfantSurvivalRateAnalysis(ctx context.Context,
 }
 
 func (s *StoreEntry) SingleFactorAnalysisMethodCycle(ctx context.Context, req *pasturePb.SingleFactorPregnancyRateRequest) ([]*pasturePb.SingleFactorPregnancyRateList, error) {
-	/*dateTimeRange, err := util.GetRangeDayByDays(req.StartDayTime, req.EndDayTime, req.Value)
+	dateTimeRange, err := util.GetRangeDayByDays(req.StartDayTime, req.EndDayTime, req.Value)
 	if err != nil {
 		return nil, xerr.WithStack(err)
-	}*/
+	}
 	res := make([]*pasturePb.SingleFactorPregnancyRateList, 0)
 
+	selectSql := ""
+	for _, v := range dateTimeRange {
+		if len(v) != 2 {
+			continue
+		}
+		startDayTimeUnix := util.TimeParseLocalUnix(v[0])
+		endDayTimeUnix := util.TimeParseLocalEndUnix(v[1])
+
+		selectSql += fmt.Sprintf(`
+			SELECT
+			'%s ~ %s' as  statistic_method,   
+			COUNT(DISTINCT CASE WHEN mating_result = %d AND mating_result_at > 0 THEN cow_id END) AS pregnant_count, -- 怀孕头数
+			COUNT(DISTINCT CASE WHEN mating_result = %d AND mating_result_at > 0 THEN cow_id END) AS empty_pregnant_count, -- 空怀头数
+			COUNT(DISTINCT CASE WHEN mating_result = %d AND mating_result_at > 0 THEN cow_id END) AS abortion_count, -- 流产头数
+			COUNT(DISTINCT CASE WHEN mating_result IN (%d, %d) THEN cow_id END) AS other_count, -- 其他数
+			(
+				COUNT(DISTINCT CASE WHEN mating_result = %d AND mating_result_at > 0 THEN cow_id END) +
+				COUNT(DISTINCT CASE WHEN mating_result = %d AND mating_result_at > 0 THEN cow_id END) +
+				COUNT(DISTINCT CASE WHEN mating_result IN (%d, %d) THEN cow_id END)
+			) AS total_count -- 总数
+			FROM event_mating WHERE status = %d AND reality_day BETWEEN %d AND %d
+		UNION ALL `, v[0], v[1], pasturePb.MatingResult_Pregnant, pasturePb.MatingResult_Empty, pasturePb.MatingResult_Abort,
+			pasturePb.MatingResult_Unknown, pasturePb.MatingResult_ReMatch, pasturePb.MatingResult_Pregnant,
+			pasturePb.MatingResult_Empty, pasturePb.MatingResult_Unknown, pasturePb.MatingResult_ReMatch,
+			pasturePb.IsShow_Ok, startDayTimeUnix, endDayTimeUnix)
+	}
+
+	if len(selectSql) > 0 {
+		selectSql = selectSql[:len(selectSql)-len("UNION ALL")-3] + ";"
+	}
+
+	if err = s.DB.Raw(selectSql).Scan(&res).Error; err != nil {
+		return nil, xerr.WithStack(err)
+	}
+
 	return res, nil
 }
 
@@ -116,15 +162,15 @@ func (s *StoreEntry) SingleFactorAnalysisMethodMonths(ctx context.Context, req *
 		COUNT(DISTINCT CASE WHEN mating_result = ? AND mating_result_at > 0 THEN cow_id END) AS abortion_count, -- 流产头数  
 		COUNT(DISTINCT CASE WHEN mating_result IN (?, ?) THEN cow_id END) AS other_count, -- 其他数
 		(  
-        	COUNT(DISTINCT CASE WHEN mating_result = 3 AND mating_result_at > 0 THEN cow_id END) +  
-        	COUNT(DISTINCT CASE WHEN mating_result = 4 AND mating_result_at > 0 THEN cow_id END) +  
-        	COUNT(DISTINCT CASE WHEN mating_result IN (1, 2) THEN cow_id END)  
+        	COUNT(DISTINCT CASE WHEN mating_result = ? AND mating_result_at > 0 THEN cow_id END) +  
+        	COUNT(DISTINCT CASE WHEN mating_result = ? AND mating_result_at > 0 THEN cow_id END) +  
+        	COUNT(DISTINCT CASE WHEN mating_result IN (?, ?) THEN cow_id END)  
 		) AS total_count -- 总数   
 		`, pasturePb.MatingResult_Pregnant, pasturePb.MatingResult_Empty, pasturePb.MatingResult_Abort,
-			pasturePb.MatingResult_Unknown, pasturePb.MatingResult_ReMatch).
+			pasturePb.MatingResult_Unknown, pasturePb.MatingResult_ReMatch, pasturePb.MatingResult_Pregnant,
+			pasturePb.MatingResult_Empty, pasturePb.MatingResult_Unknown, pasturePb.MatingResult_ReMatch).
 		Where("status = ?", pasturePb.IsShow_Ok).
-		Where("reality_day BETWEEN ? AND ?", startDayTimeUnix, endDayTimeUnix).
-		Where("reality_day > 0")
+		Where("reality_day BETWEEN ? AND ?", startDayTimeUnix, endDayTimeUnix)
 	if req.CowType > 0 {
 		pref.Where("cow_type = ?", req.CowType)
 	}
@@ -159,3 +205,331 @@ func (s *StoreEntry) SingleFactorAnalysisMethodMonths(ctx context.Context, req *
 	}
 	return res, nil
 }
+
+func (s *StoreEntry) SingleFactorAnalysisMethodMatingTimes(ctx context.Context, req *pasturePb.SingleFactorPregnancyRateRequest) ([]*pasturePb.SingleFactorPregnancyRateList, error) {
+	res := make([]*pasturePb.SingleFactorPregnancyRateList, 0)
+	return res, nil
+}
+
+func (s *StoreEntry) SingleFactorAnalysisMethodBreeding(ctx context.Context, req *pasturePb.SingleFactorPregnancyRateRequest) ([]*pasturePb.SingleFactorPregnancyRateList, error) {
+	startDayTimeUnix := util.TimeParseLocalUnix(req.StartDayTime)
+	endDayTimeUnix := util.TimeParseLocalEndUnix(req.EndDayTime)
+	res := make([]*pasturePb.SingleFactorPregnancyRateList, 0)
+
+	if err := s.DB.Model(new(model.EventMating)).
+		Select(`
+		CASE  
+			WHEN expose_estrus_type = ? THEN '脖环揭发'  
+			WHEN expose_estrus_type = ? THEN '脚环/计步器'  
+			WHEN expose_estrus_type = ? THEN '自然发情'  
+			WHEN expose_estrus_type = ? THEN '同期'  
+        ELSE '未知'  
+    	END AS statistic_method,
+		COUNT(DISTINCT CASE WHEN mating_result = ? AND mating_result_at > 0 THEN cow_id END) AS pregnant_count, -- 怀孕头数  
+		COUNT(DISTINCT CASE WHEN mating_result = ? AND mating_result_at > 0 THEN cow_id END) AS empty_pregnant_count, -- 空怀头数  
+		COUNT(DISTINCT CASE WHEN mating_result = ? AND mating_result_at > 0 THEN cow_id END) AS abortion_count, -- 流产头数  
+		COUNT(DISTINCT CASE WHEN mating_result IN (?, ?) THEN cow_id END) AS other_count, -- 其他数
+		(  
+        	COUNT(DISTINCT CASE WHEN mating_result = ? AND mating_result_at > 0 THEN cow_id END) +  
+        	COUNT(DISTINCT CASE WHEN mating_result = ? AND mating_result_at > 0 THEN cow_id END) +  
+        	COUNT(DISTINCT CASE WHEN mating_result IN (?, ?) THEN cow_id END)  
+		) AS total_count -- 总数
+		`, pasturePb.ExposeEstrusType_Neck_Ring, pasturePb.ExposeEstrusType_Foot_Ring,
+			pasturePb.ExposeEstrusType_Natural_Estrus, pasturePb.ExposeEstrusType_Same_Time,
+			pasturePb.MatingResult_Pregnant, pasturePb.MatingResult_Empty, pasturePb.MatingResult_Abort,
+			pasturePb.MatingResult_Unknown, pasturePb.MatingResult_ReMatch, pasturePb.MatingResult_Pregnant,
+			pasturePb.MatingResult_Empty, pasturePb.MatingResult_Unknown, pasturePb.MatingResult_ReMatch,
+		).Where("status = ?", pasturePb.IsShow_Ok).
+		Where("reality_day BETWEEN ? AND ?", startDayTimeUnix, endDayTimeUnix).
+		Where("cow_type = ?", req.CowType).
+		Group("expose_estrus_type").
+		Order("expose_estrus_type").
+		Find(&res).Error; err != nil {
+		return nil, xerr.WithStack(err)
+	}
+	return res, nil
+}
+
+func (s *StoreEntry) SingleFactorAnalysisMethodBreedingCompany(ctx context.Context, req *pasturePb.SingleFactorPregnancyRateRequest) ([]*pasturePb.SingleFactorPregnancyRateList, error) {
+	res := make([]*pasturePb.SingleFactorPregnancyRateList, 0)
+	startDayTimeUnix := util.TimeParseLocalUnix(req.StartDayTime)
+	endDayTimeUnix := util.TimeParseLocalEndUnix(req.EndDayTime)
+	pref := s.DB.Table(fmt.Sprintf("%s as a", new(model.EventMating).TableName())).
+		Select(`
+		b.producer AS statistic_method,
+		COUNT(DISTINCT CASE WHEN mating_result = ? AND mating_result_at > 0 THEN cow_id END) AS pregnant_count, -- 怀孕头数  
+		COUNT(DISTINCT CASE WHEN mating_result = ? AND mating_result_at > 0 THEN cow_id END) AS empty_pregnant_count, -- 空怀头数  
+		COUNT(DISTINCT CASE WHEN mating_result = ? AND mating_result_at > 0 THEN cow_id END) AS abortion_count, -- 流产头数  
+		COUNT(DISTINCT CASE WHEN mating_result IN (?, ?) THEN cow_id END) AS other_count, -- 其他数
+		(  
+        	COUNT(DISTINCT CASE WHEN mating_result = ? AND mating_result_at > 0 THEN cow_id END) +  
+        	COUNT(DISTINCT CASE WHEN mating_result = ? AND mating_result_at > 0 THEN cow_id END) +  
+        	COUNT(DISTINCT CASE WHEN mating_result IN (?, ?) THEN cow_id END)  
+		) AS total_count -- 总数
+	`, pasturePb.MatingResult_Pregnant, pasturePb.MatingResult_Empty, pasturePb.MatingResult_Abort,
+			pasturePb.MatingResult_Unknown, pasturePb.MatingResult_ReMatch, pasturePb.MatingResult_Pregnant,
+			pasturePb.MatingResult_Empty, pasturePb.MatingResult_Unknown, pasturePb.MatingResult_ReMatch).
+		Joins(fmt.Sprintf("left join %s as b on a.frozen_semen_number = b.bull_id ", new(model.FrozenSemen).TableName()))
+	if req.CowType > 0 {
+		pref.Where("a.cow_type = ?", req.CowType)
+	}
+	if req.CowKind > 0 {
+		pref.Where("a.cow_kind = ?", req.CowKind)
+	}
+
+	if req.LactInterval > 0 {
+		switch req.LactInterval {
+		case pasturePb.LactIntervalSymbol_Less_Than:
+			pref.Where("a.lact < ?", req.LactIntervalStartValue)
+		case pasturePb.LactIntervalSymbol_Less_Than_Or_Equal_To:
+			pref.Where("a.lact <= ?", req.LactIntervalStartValue)
+		case pasturePb.LactIntervalSymbol_Greater_Than:
+			pref.Where("a.lact > ?", req.LactIntervalStartValue)
+		case pasturePb.LactIntervalSymbol_Greater_Than_Or_Equal_To:
+			pref.Where("a.lact >= ?", req.LactIntervalStartValue)
+		case pasturePb.LactIntervalSymbol_Equal_To:
+			pref.Where("a.lact = ?", req.LactIntervalStartValue)
+		case pasturePb.LactIntervalSymbol_Not_Equal_To:
+			pref.Where("a.lact != ?", req.LactIntervalStartValue)
+		case pasturePb.LactIntervalSymbol_Between:
+			pref.Where("a.lact BETWEEN ? AND ? ", req.LactIntervalStartValue, req.LactIntervalEndValue)
+		default:
+			return nil, xerr.Custom("错误的胎次区间符号")
+		}
+	}
+
+	if err := pref.Where("a.status = ?", pasturePb.IsShow_Ok).
+		Where("a.reality_day BETWEEN ? AND ?", startDayTimeUnix, endDayTimeUnix).
+		Group("b.producer").
+		Find(&res).Error; err != nil {
+		return nil, xerr.WithStack(err)
+	}
+
+	return res, nil
+}
+
+func (s *StoreEntry) SingleFactorAnalysisMethodOperation(ctx context.Context, req *pasturePb.SingleFactorPregnancyRateRequest) ([]*pasturePb.SingleFactorPregnancyRateList, error) {
+	res := make([]*pasturePb.SingleFactorPregnancyRateList, 0)
+	startDayTimeUnix := util.TimeParseLocalUnix(req.StartDayTime)
+	endDayTimeUnix := util.TimeParseLocalEndUnix(req.EndDayTime)
+	pref := s.DB.Model(new(model.EventMating)).Select(`
+		operation_name AS statistic_method,
+		COUNT(DISTINCT CASE WHEN mating_result = ? AND mating_result_at > 0 THEN cow_id END) AS pregnant_count, -- 怀孕头数  
+		COUNT(DISTINCT CASE WHEN mating_result = ? AND mating_result_at > 0 THEN cow_id END) AS empty_pregnant_count, -- 空怀头数  
+		COUNT(DISTINCT CASE WHEN mating_result = ? AND mating_result_at > 0 THEN cow_id END) AS abortion_count, -- 流产头数  
+		COUNT(DISTINCT CASE WHEN mating_result IN (?, ?) THEN cow_id END) AS other_count, -- 其他数
+		(  
+        	COUNT(DISTINCT CASE WHEN mating_result = ? AND mating_result_at > 0 THEN cow_id END) +  
+        	COUNT(DISTINCT CASE WHEN mating_result = ? AND mating_result_at > 0 THEN cow_id END) +  
+        	COUNT(DISTINCT CASE WHEN mating_result IN (?, ?) THEN cow_id END)  
+		) AS total_count -- 总数
+	`, pasturePb.MatingResult_Pregnant, pasturePb.MatingResult_Empty, pasturePb.MatingResult_Abort,
+		pasturePb.MatingResult_Unknown, pasturePb.MatingResult_ReMatch, pasturePb.MatingResult_Pregnant,
+		pasturePb.MatingResult_Empty, pasturePb.MatingResult_Unknown, pasturePb.MatingResult_ReMatch)
+	if req.CowType > 0 {
+		pref.Where("cow_type = ?", req.CowType)
+	}
+	if req.CowKind > 0 {
+		pref.Where("cow_kind = ?", req.CowKind)
+	}
+
+	if req.LactInterval > 0 {
+		switch req.LactInterval {
+		case pasturePb.LactIntervalSymbol_Less_Than:
+			pref.Where("lact < ?", req.LactIntervalStartValue)
+		case pasturePb.LactIntervalSymbol_Less_Than_Or_Equal_To:
+			pref.Where("lact <= ?", req.LactIntervalStartValue)
+		case pasturePb.LactIntervalSymbol_Greater_Than:
+			pref.Where("lact > ?", req.LactIntervalStartValue)
+		case pasturePb.LactIntervalSymbol_Greater_Than_Or_Equal_To:
+			pref.Where("lact >= ?", req.LactIntervalStartValue)
+		case pasturePb.LactIntervalSymbol_Equal_To:
+			pref.Where("lact = ?", req.LactIntervalStartValue)
+		case pasturePb.LactIntervalSymbol_Not_Equal_To:
+			pref.Where("lact != ?", req.LactIntervalStartValue)
+		case pasturePb.LactIntervalSymbol_Between:
+			pref.Where("lact BETWEEN ? AND ? ", req.LactIntervalStartValue, req.LactIntervalEndValue)
+		default:
+			return nil, xerr.Custom("错误的胎次区间符号")
+		}
+	}
+
+	if err := pref.Where("status = ?", pasturePb.IsShow_Ok).
+		Where("reality_day BETWEEN ? AND ?", startDayTimeUnix, endDayTimeUnix).
+		Group("operation_id").
+		Find(&res).Error; err != nil {
+		return nil, xerr.WithStack(err)
+	}
+
+	return res, nil
+}
+
+func (s *StoreEntry) SingleFactorAnalysisMethodMatingInterval(ctx context.Context, req *pasturePb.SingleFactorPregnancyRateRequest) ([]*pasturePb.SingleFactorPregnancyRateList, error) {
+	res := make([]*pasturePb.SingleFactorPregnancyRateList, 0)
+	return res, nil
+}
+
+func (s *StoreEntry) SingleFactorAnalysisMethodBull(ctx context.Context, req *pasturePb.SingleFactorPregnancyRateRequest) ([]*pasturePb.SingleFactorPregnancyRateList, error) {
+	res := make([]*pasturePb.SingleFactorPregnancyRateList, 0)
+	startDayTimeUnix := util.TimeParseLocalUnix(req.StartDayTime)
+	endDayTimeUnix := util.TimeParseLocalEndUnix(req.EndDayTime)
+	pref := s.DB.Model(new(model.EventMating)).
+		Select(`
+		frozen_semen_number AS statistic_method,
+		COUNT(DISTINCT CASE WHEN mating_result = ? AND mating_result_at > 0 THEN cow_id END) AS pregnant_count, -- 怀孕头数  
+		COUNT(DISTINCT CASE WHEN mating_result = ? AND mating_result_at > 0 THEN cow_id END) AS empty_pregnant_count, -- 空怀头数  
+		COUNT(DISTINCT CASE WHEN mating_result = ? AND mating_result_at > 0 THEN cow_id END) AS abortion_count, -- 流产头数  
+		COUNT(DISTINCT CASE WHEN mating_result IN (?, ?) THEN cow_id END) AS other_count, -- 其他数
+		(  
+        	COUNT(DISTINCT CASE WHEN mating_result = ? AND mating_result_at > 0 THEN cow_id END) +  
+        	COUNT(DISTINCT CASE WHEN mating_result = ? AND mating_result_at > 0 THEN cow_id END) +  
+        	COUNT(DISTINCT CASE WHEN mating_result IN (?, ?) THEN cow_id END)  
+		) AS total_count -- 总数
+	`, pasturePb.MatingResult_Pregnant, pasturePb.MatingResult_Empty, pasturePb.MatingResult_Abort,
+			pasturePb.MatingResult_Unknown, pasturePb.MatingResult_ReMatch, pasturePb.MatingResult_Pregnant,
+			pasturePb.MatingResult_Empty, pasturePb.MatingResult_Unknown, pasturePb.MatingResult_ReMatch)
+	if req.CowType > 0 {
+		pref.Where("cow_type = ?", req.CowType)
+	}
+	if req.CowKind > 0 {
+		pref.Where("cow_kind = ?", req.CowKind)
+	}
+
+	if req.LactInterval > 0 {
+		switch req.LactInterval {
+		case pasturePb.LactIntervalSymbol_Less_Than:
+			pref.Where("lact < ?", req.LactIntervalStartValue)
+		case pasturePb.LactIntervalSymbol_Less_Than_Or_Equal_To:
+			pref.Where("lact <= ?", req.LactIntervalStartValue)
+		case pasturePb.LactIntervalSymbol_Greater_Than:
+			pref.Where("lact > ?", req.LactIntervalStartValue)
+		case pasturePb.LactIntervalSymbol_Greater_Than_Or_Equal_To:
+			pref.Where("lact >= ?", req.LactIntervalStartValue)
+		case pasturePb.LactIntervalSymbol_Equal_To:
+			pref.Where("lact = ?", req.LactIntervalStartValue)
+		case pasturePb.LactIntervalSymbol_Not_Equal_To:
+			pref.Where("lact != ?", req.LactIntervalStartValue)
+		case pasturePb.LactIntervalSymbol_Between:
+			pref.Where("lact BETWEEN ? AND ? ", req.LactIntervalStartValue, req.LactIntervalEndValue)
+		default:
+			return nil, xerr.Custom("错误的胎次区间符号")
+		}
+	}
+
+	if err := pref.Where("status = ?", pasturePb.IsShow_Ok).
+		Where("reality_day BETWEEN ? AND ?", startDayTimeUnix, endDayTimeUnix).
+		Group("frozen_semen_number").
+		Find(&res).Error; err != nil {
+		return nil, xerr.WithStack(err)
+	}
+
+	return res, nil
+}
+
+func (s *StoreEntry) SingleFactorAnalysisMethodBreedingCycle(ctx context.Context, req *pasturePb.SingleFactorPregnancyRateRequest) ([]*pasturePb.SingleFactorPregnancyRateList, error) {
+	res := make([]*pasturePb.SingleFactorPregnancyRateList, 0)
+	return res, nil
+}
+
+func (s *StoreEntry) SingleFactorAnalysisMethodWeek(ctx context.Context, req *pasturePb.SingleFactorPregnancyRateRequest) ([]*pasturePb.SingleFactorPregnancyRateList, error) {
+	res := make([]*pasturePb.SingleFactorPregnancyRateList, 0)
+	startDayTimeUnix := util.TimeParseLocalUnix(req.StartDayTime)
+	endDayTimeUnix := util.TimeParseLocalEndUnix(req.EndDayTime)
+	pref := s.DB.Model(new(model.EventMating)).
+		Select(`
+		CASE  
+			DAYOFWEEK(DATE(FROM_UNIXTIME(reality_day)))
+			WHEN 2 THEN '星期一'  
+			WHEN 3 THEN '星期二'  
+			WHEN 4 THEN '星期三'  
+			WHEN 5 THEN '星期四'  
+			WHEN 6 THEN '星期五'  
+			WHEN 7 THEN '星期六'  
+			WHEN 1 THEN '星期日'  
+			ELSE '未知'  
+    	END AS statistic_method,
+		COUNT(DISTINCT CASE WHEN mating_result = ? AND mating_result_at > 0 THEN cow_id END) AS pregnant_count, -- 怀孕头数  
+		COUNT(DISTINCT CASE WHEN mating_result = ? AND mating_result_at > 0 THEN cow_id END) AS empty_pregnant_count, -- 空怀头数  
+		COUNT(DISTINCT CASE WHEN mating_result = ? AND mating_result_at > 0 THEN cow_id END) AS abortion_count, -- 流产头数  
+		COUNT(DISTINCT CASE WHEN mating_result IN (?, ?) THEN cow_id END) AS other_count, -- 其他数
+		(  
+        	COUNT(DISTINCT CASE WHEN mating_result = ? AND mating_result_at > 0 THEN cow_id END) +  
+        	COUNT(DISTINCT CASE WHEN mating_result = ? AND mating_result_at > 0 THEN cow_id END) +  
+        	COUNT(DISTINCT CASE WHEN mating_result IN (?, ?) THEN cow_id END)  
+		) AS total_count -- 总数
+	`, pasturePb.MatingResult_Pregnant, pasturePb.MatingResult_Empty, pasturePb.MatingResult_Abort,
+			pasturePb.MatingResult_Unknown, pasturePb.MatingResult_ReMatch, pasturePb.MatingResult_Pregnant,
+			pasturePb.MatingResult_Empty, pasturePb.MatingResult_Unknown, pasturePb.MatingResult_ReMatch)
+	if req.CowType > 0 {
+		pref.Where("cow_type = ?", req.CowType)
+	}
+	if req.CowKind > 0 {
+		pref.Where("cow_kind = ?", req.CowKind)
+	}
+
+	if req.LactInterval > 0 {
+		switch req.LactInterval {
+		case pasturePb.LactIntervalSymbol_Less_Than:
+			pref.Where("lact < ?", req.LactIntervalStartValue)
+		case pasturePb.LactIntervalSymbol_Less_Than_Or_Equal_To:
+			pref.Where("lact <= ?", req.LactIntervalStartValue)
+		case pasturePb.LactIntervalSymbol_Greater_Than:
+			pref.Where("lact > ?", req.LactIntervalStartValue)
+		case pasturePb.LactIntervalSymbol_Greater_Than_Or_Equal_To:
+			pref.Where("lact >= ?", req.LactIntervalStartValue)
+		case pasturePb.LactIntervalSymbol_Equal_To:
+			pref.Where("lact = ?", req.LactIntervalStartValue)
+		case pasturePb.LactIntervalSymbol_Not_Equal_To:
+			pref.Where("lact != ?", req.LactIntervalStartValue)
+		case pasturePb.LactIntervalSymbol_Between:
+			pref.Where("lact BETWEEN ? AND ? ", req.LactIntervalStartValue, req.LactIntervalEndValue)
+		default:
+			return nil, xerr.Custom("错误的胎次区间符号")
+		}
+	}
+
+	if err := pref.Where("status = ?", pasturePb.IsShow_Ok).
+		Where("reality_day BETWEEN ? AND ?", startDayTimeUnix, endDayTimeUnix).
+		Group("statistic_method").
+		Find(&res).Error; err != nil {
+		return nil, xerr.WithStack(err)
+	}
+
+	return res, nil
+}
+
+func (s *StoreEntry) SingleFactorAnalysisMethodLact(ctx context.Context, req *pasturePb.SingleFactorPregnancyRateRequest) ([]*pasturePb.SingleFactorPregnancyRateList, error) {
+	res := make([]*pasturePb.SingleFactorPregnancyRateList, 0)
+	startDayTimeUnix := util.TimeParseLocalUnix(req.StartDayTime)
+	endDayTimeUnix := util.TimeParseLocalEndUnix(req.EndDayTime)
+	pref := s.DB.Model(new(model.EventMating)).
+		Select(`
+		lact AS statistic_method,
+		COUNT(DISTINCT CASE WHEN mating_result = ? AND mating_result_at > 0 THEN cow_id END) AS pregnant_count, -- 怀孕头数  
+		COUNT(DISTINCT CASE WHEN mating_result = ? AND mating_result_at > 0 THEN cow_id END) AS empty_pregnant_count, -- 空怀头数  
+		COUNT(DISTINCT CASE WHEN mating_result = ? AND mating_result_at > 0 THEN cow_id END) AS abortion_count, -- 流产头数  
+		COUNT(DISTINCT CASE WHEN mating_result IN (?, ?) THEN cow_id END) AS other_count, -- 其他数
+		(  
+        	COUNT(DISTINCT CASE WHEN mating_result = ? AND mating_result_at > 0 THEN cow_id END) +  
+        	COUNT(DISTINCT CASE WHEN mating_result = ? AND mating_result_at > 0 THEN cow_id END) +  
+        	COUNT(DISTINCT CASE WHEN mating_result IN (?, ?) THEN cow_id END)  
+		) AS total_count -- 总数
+	`, pasturePb.MatingResult_Pregnant, pasturePb.MatingResult_Empty, pasturePb.MatingResult_Abort,
+			pasturePb.MatingResult_Unknown, pasturePb.MatingResult_ReMatch, pasturePb.MatingResult_Pregnant,
+			pasturePb.MatingResult_Empty, pasturePb.MatingResult_Unknown, pasturePb.MatingResult_ReMatch)
+	if req.CowType > 0 {
+		pref.Where("cow_type = ?", req.CowType)
+	}
+	if req.CowKind > 0 {
+		pref.Where("cow_kind = ?", req.CowKind)
+	}
+
+	if err := pref.Where("status = ?", pasturePb.IsShow_Ok).
+		Where("reality_day BETWEEN ? AND ?", startDayTimeUnix, endDayTimeUnix).
+		Group("lact").
+		Find(&res).Error; err != nil {
+		return nil, xerr.WithStack(err)
+	}
+
+	return res, nil
+}

+ 3 - 3
module/backend/event_breed.go

@@ -377,8 +377,8 @@ func (s *StoreEntry) MatingCreate(ctx context.Context, req *pasturePb.EventMatin
 					"mating_result":       pasturePb.MatingResult_Unknown,
 					"status":              pasturePb.IsShow_Ok,
 					"reality_day":         time.Unix(int64(req.MatingAt), 0).Format(model.LayoutTime),
-					"mating_number":       1,
-					"frozen_semen_number": req.FrozenSemenCount,
+					"mating_number":       req.FrozenSemenCount,
+					"frozen_semen_number": req.FrozenSemenNumber,
 					"operation_id":        req.StaffMemberId,
 					"operation_name":      systemUser.Name,
 				}).
@@ -395,7 +395,7 @@ func (s *StoreEntry) MatingCreate(ctx context.Context, req *pasturePb.EventMatin
 				Updates(map[string]interface{}{
 					"mating_result":       pasturePb.MatingResult_ReMatch,
 					"mating_number":       2,
-					"frozen_semen_number": req.FrozenSemenCount,
+					"frozen_semen_number": req.FrozenSemenNumber,
 				}).
 				Error; err != nil {
 				return xerr.WithStack(err)

+ 3 - 0
util/util.go

@@ -185,6 +185,9 @@ func GetRangeDayMiddleDay(dateRange []string, middleDay int32) (string, error) {
 // (2024-10-01 ~ 2024-10-31,5)=> [[2024-10-01,2024-10-05], [2024-10-06,2024-10-10], [2024-10-11,2024-10-15], [2024-10-16,2024-10-20], [2024-10-21,2024-10-25], [2024-10-26,2024-10-30],[2024-10-31,2024-10-31]]
 func GetRangeDayByDays(startDay, endDay string, days int32) ([][]string, error) {
 	var res [][]string
+	if days <= 0 {
+		return res, nil
+	}
 	startDate, err := time.Parse(Layout, startDay)
 	if err != nil {
 		return nil, err