Browse Source

analysis: 21天怀孕率

Yi 5 tháng trước cách đây
mục cha
commit
0551a6416e

+ 1 - 1
go.mod

@@ -3,7 +3,7 @@ module kpt-pasture
 go 1.17
 
 require (
-	gitee.com/xuyiping_admin/go_proto v0.0.0-20241023073355-19a0c4c38a5a
+	gitee.com/xuyiping_admin/go_proto v0.0.0-20241023085318-8fa74469f8b9
 	gitee.com/xuyiping_admin/pkg v0.0.0-20241010101255-0c6bd229b939
 	github.com/dgrijalva/jwt-go v3.2.0+incompatible
 	github.com/eko/gocache v1.1.0

+ 2 - 0
go.sum

@@ -110,6 +110,8 @@ gitee.com/xuyiping_admin/go_proto v0.0.0-20241023064328-b40bdd7fbde6 h1:0NflcKQr
 gitee.com/xuyiping_admin/go_proto v0.0.0-20241023064328-b40bdd7fbde6/go.mod h1:BKrFW6YLDectlQcQk3FYKBeXvjEiodAKJ5rq7O/QiPE=
 gitee.com/xuyiping_admin/go_proto v0.0.0-20241023073355-19a0c4c38a5a h1:7GFvstT8R48anOv757i18W4H0EsbHwvh/+v1o5ZiycU=
 gitee.com/xuyiping_admin/go_proto v0.0.0-20241023073355-19a0c4c38a5a/go.mod h1:BKrFW6YLDectlQcQk3FYKBeXvjEiodAKJ5rq7O/QiPE=
+gitee.com/xuyiping_admin/go_proto v0.0.0-20241023085318-8fa74469f8b9 h1:WRlynGQz0czTgLBRNUcxFa/h6ahvxn6XibXW/P19TQs=
+gitee.com/xuyiping_admin/go_proto v0.0.0-20241023085318-8fa74469f8b9/go.mod h1:BKrFW6YLDectlQcQk3FYKBeXvjEiodAKJ5rq7O/QiPE=
 gitee.com/xuyiping_admin/pkg v0.0.0-20231218082641-aac597b8a015 h1:dfb5dRd57L2HKjdwLT93UFmPYFPOmEl56gtZmqcNnaE=
 gitee.com/xuyiping_admin/pkg v0.0.0-20231218082641-aac597b8a015/go.mod h1:Fk4GYI/v0IK3XFrm1Gn+VkgCz5Y7mfswD5hsTJYOG6A=
 gitee.com/xuyiping_admin/pkg v0.0.0-20241008063555-121a776fd331 h1:qJcpJ3o+O7uxDqR0I9LijQmi607IKvhf4mGum/ZUPT0=

+ 24 - 0
http/handler/analysis/analysis.go

@@ -97,6 +97,30 @@ func PenWeight(c *gin.Context) {
 	ginutil.JSONResp(c, res)
 }
 
+func TwentyonePregnantRate(c *gin.Context) {
+	var req pasturePb.TwentyOnePregnantRateRequest
+	if err := ginutil.BindProto(c, &req); err != nil {
+		apierr.AbortBadRequest(c, http.StatusBadRequest, err)
+		return
+	}
+
+	if err := valid.ValidateStruct(&req,
+		valid.Field(&req.StartDate, valid.Required),
+		valid.Field(&req.EndDate, valid.Required),
+		valid.Field(&req.CowType, valid.Required),
+	); err != nil {
+		apierr.AbortBadRequest(c, http.StatusBadRequest, err)
+		return
+	}
+
+	res, err := middleware.Dependency(c).StoreEventHub.OpsService.TwentyonePregnantRate(c, &req)
+	if err != nil {
+		apierr.ClassifiedAbort(c, err)
+		return
+	}
+	ginutil.JSONResp(c, res)
+}
+
 func AbortionRate(c *gin.Context) {
 	var req pasturePb.AbortionRateRequest
 	if err := ginutil.BindProto(c, &req); err != nil {

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

@@ -74,7 +74,6 @@ func CowDiseaseDiagnose(c *gin.Context) {
 		valid.Field(&req.Id, valid.Required),
 		valid.Field(&req.CowId, valid.Required),
 		valid.Field(&req.DiagnosedResult, valid.Required),
-		valid.Field(&req.OperationId, valid.Required),
 	); err != nil {
 		apierr.AbortBadRequest(c, http.StatusBadRequest, err)
 		return

+ 1 - 1
http/route/analysis_api.go

@@ -18,6 +18,6 @@ func AnalysisAPI(opts ...func(engine *gin.Engine)) func(s *gin.Engine) {
 		pastureRoute.POST("/mating/timely", analysis.MatingTimeLy)
 		pastureRoute.POST("/pen/weight", analysis.PenWeight)
 		pastureRoute.POST("/abortion/rate", analysis.AbortionRate)
-		pastureRoute.POST("/twentyone/rate", analysis.AbortionRate)
+		pastureRoute.POST("/twentyone/pregnant/rate", analysis.TwentyonePregnantRate)
 	}
 }

+ 55 - 0
module/backend/analysis.go

@@ -402,3 +402,58 @@ func (s *StoreEntry) AbortionRate(ctx context.Context, req *pasturePb.AbortionRa
 		},
 	}, nil
 }
+
+func (s *StoreEntry) TwentyonePregnantRate(ctx context.Context, req *pasturePb.TwentyOnePregnantRateRequest) (*pasturePb.TwentyOnePregnantRateResponse, error) {
+	dataRange, err := util.Get21DayPeriods(req.StartDate, req.EndDate)
+	if err != nil {
+		return nil, xerr.WithStack(err)
+	}
+	chart := &pasturePb.TwentyOnePregnantRateChart{
+		Header:       make([]string, 0),
+		PregnantRate: make([]float32, 0),
+		MatingRate:   make([]float32, 0),
+	}
+
+	// 牛只主动停配期
+	systemBasicName := ""
+	switch req.CowType {
+	case pasturePb.CowType_Breeding_Calf:
+		systemBasicName = "proactively_stop_breeding_for_adult"
+	case pasturePb.CowType_Reserve_Calf:
+		systemBasicName = "proactively_stop_breeding_for_backup"
+	default:
+		return nil, xerr.Customf("不支持的牛只类型: %d", req.CowType)
+	}
+
+	systemBasic, err := s.GetSystemBasicByName(ctx, systemBasicName)
+	if err != nil {
+		return nil, xerr.WithStack(err)
+	}
+
+	stopBreedingDay := systemBasic.MinValue
+
+	fmt.Println(stopBreedingDay)
+
+	for _, v := range dataRange {
+		middleDay, err := util.GetRangeDayMiddleDay(v, 11)
+		if err != nil {
+			return nil, xerr.WithStack(err)
+		}
+
+		chart.Header = append(chart.Header, middleDay)
+	}
+
+	return &pasturePb.TwentyOnePregnantRateResponse{
+		Code:    http.StatusOK,
+		Message: "ok",
+		Data: &pasturePb.TwentyOnePregnantRateData{
+			Chart: chart,
+			Table: &pasturePb.TwentyOnePregnantRateTable{
+				List:     nil,
+				Total:    0,
+				PageSize: 0,
+				Page:     0,
+			},
+		},
+	}, nil
+}

+ 7 - 3
module/backend/event_health.go

@@ -173,7 +173,7 @@ func (s *StoreEntry) CowDiseaseDiagnose(ctx context.Context, req *pasturePb.CowD
 		return xerr.Custom("异常牛只数据")
 	}
 
-	systemUser, err := s.GetSystemUserById(ctx, int64(req.OperationId))
+	currentUser, err := s.GetCurrentSystemUser(ctx)
 	if err != nil {
 		return xerr.WithStack(err)
 	}
@@ -184,8 +184,8 @@ func (s *StoreEntry) CowDiseaseDiagnose(ctx context.Context, req *pasturePb.CowD
 			Where("cow_id = ?", req.CowId).
 			Updates(map[string]interface{}{
 				"diagnosed_result":        pasturePb.IsShow_No,
-				"diagnose_operation_id":   req.OperationId,
-				"diagnose_operation_name": systemUser.Name,
+				"diagnose_operation_id":   currentUser.Id,
+				"diagnose_operation_name": currentUser.Name,
 				"remarks":                 req.Remarks,
 			}).Error; err != nil {
 			return xerr.WithStack(err)
@@ -198,6 +198,10 @@ func (s *StoreEntry) CowDiseaseDiagnose(ctx context.Context, req *pasturePb.CowD
 	if err != nil {
 		return xerr.WithStack(err)
 	}
+	systemUser, err := s.GetSystemUserById(ctx, int64(req.OperationId))
+	if err != nil {
+		return xerr.WithStack(err)
+	}
 
 	if err = s.DB.Transaction(func(tx *gorm.DB) error {
 		if err = tx.Model(eventCowDisease).

+ 1 - 0
module/backend/interface.go

@@ -211,6 +211,7 @@ type AnalyseService interface {
 	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)
+	TwentyonePregnantRate(ctx context.Context, req *pasturePb.TwentyOnePregnantRateRequest) (*pasturePb.TwentyOnePregnantRateResponse, error)
 }
 
 //go:generate mockgen -destination mock/DashboardService.go -package kptservicemock kpt-pasture/module/backend DashboardService

+ 8 - 0
module/backend/sql.go

@@ -249,3 +249,11 @@ func (s *StoreEntry) GetSystemDeptListById(ctx context.Context, id int64) (*mode
 	}
 	return systemDept, nil
 }
+
+func (s *StoreEntry) GetSystemBasicByName(ctx context.Context, name string) (*model.SystemBasic, error) {
+	systemBasic := &model.SystemBasic{}
+	if err := s.DB.Where("name = ?", name).Where("is_show = ?", pasturePb.IsShow_Ok).First(systemBasic).Error; err != nil {
+		return nil, xerr.WithStack(err)
+	}
+	return systemBasic, nil
+}

+ 57 - 0
util/util.go

@@ -4,6 +4,8 @@ import (
 	"fmt"
 	"math"
 	"time"
+
+	"gitee.com/xuyiping_admin/pkg/xerr"
 )
 
 const (
@@ -127,3 +129,58 @@ func RoundToTwoDecimals(num float64) float64 {
 	// 先乘以 100,然后四舍五入,最后除以 100
 	return math.Round(num*100) / 100
 }
+
+// Get21DayPeriods 获取范围时间内21天周期的切片
+// 超过开始时间,继续向前推算,直至凑整21天
+func Get21DayPeriods(startDay, endDay string) ([][]string, error) {
+	startDate, err := time.Parse(Layout, startDay)
+	if err != nil {
+		return nil, err
+	}
+
+	endDate, err := time.Parse(Layout, endDay)
+	if err != nil {
+		return nil, err
+	}
+
+	if startDate.After(endDate) {
+		return nil, xerr.Custom("start date is after end date")
+	}
+
+	var periods [][]string
+	for date := endDate; date.After(startDate); {
+		// Calculate the end of the current period
+		periodEnd := date.AddDate(0, 0, -20)
+		// Append the current period to the result slice
+		periods = append(periods, []string{periodEnd.Format(Layout), date.Format(Layout)})
+		// Move on to the next day
+		date = periodEnd.AddDate(0, 0, -1)
+	}
+	reverseRows(periods)
+	return periods, nil
+}
+
+func reverseRows(matrix [][]string) {
+	// 获取矩阵的行数
+	rows := len(matrix)
+	if rows == 0 {
+		return // 如果矩阵为空,则直接返回
+	}
+	// 初始化两个指针,一个指向开始,一个指向末尾
+	for i, j := 0, rows-1; i < j; i, j = i+1, j-1 {
+		// 交换当前行和对应行
+		matrix[i], matrix[j] = matrix[j], matrix[i]
+	}
+}
+
+// GetRangeDayMiddleDay 获取指定日期范围中间的某一天
+func GetRangeDayMiddleDay(dateRange []string, middleDay int32) (string, error) {
+	if len(dateRange) < 2 {
+		return "", xerr.Custom("date range is not enough")
+	}
+	if middleDay < 0 {
+		return "", xerr.Custom("middle day is not enough")
+	}
+	startDate, _ := time.Parse(Layout, dateRange[0])
+	return startDate.AddDate(0, 0, int(middleDay)-1).Format(Layout), nil
+}

+ 76 - 1
util/util_test.go

@@ -6,7 +6,7 @@ import (
 	"github.com/stretchr/testify/assert"
 )
 
-func TestUtil_ConvertParseLocalUnix(t *testing.T) {
+func TestConvertParseLocalUnix(t *testing.T) {
 	type args struct {
 		unix int64
 	}
@@ -173,3 +173,78 @@ func TestRoundToTwoDecimals(t *testing.T) {
 		})
 	}
 }
+
+func TestGet21DayPeriods(t *testing.T) {
+	tests := []struct {
+		startDay string
+		endDay   string
+		got      struct {
+			DateRange       [][]string
+			Day             int32
+			MiddleDayString []string
+		}
+	}{
+		{
+			startDay: "2023-01-01",
+			endDay:   "2023-01-02",
+			got: struct {
+				DateRange       [][]string
+				Day             int32
+				MiddleDayString []string
+			}{
+				DateRange: [][]string{
+					{"2022-12-13", "2023-01-02"},
+				},
+				Day:             11,
+				MiddleDayString: []string{"2022-12-23"},
+			},
+		},
+		{
+			startDay: "2024-10-23",
+			endDay:   "2024-10-24",
+			got: struct {
+				DateRange       [][]string
+				Day             int32
+				MiddleDayString []string
+			}{
+				DateRange: [][]string{
+					{"2024-10-04", "2024-10-24"},
+				},
+				Day:             11,
+				MiddleDayString: []string{"2024-10-14"},
+			},
+		},
+		{
+			startDay: "2024-06-22",
+			endDay:   "2024-10-24",
+			got: struct {
+				DateRange       [][]string
+				Day             int32
+				MiddleDayString []string
+			}{
+				DateRange: [][]string{
+					{"2024-06-21", "2024-07-11"},
+					{"2024-07-12", "2024-08-01"},
+					{"2024-08-02", "2024-08-22"},
+					{"2024-08-23", "2024-09-12"},
+					{"2024-09-13", "2024-10-03"},
+					{"2024-10-04", "2024-10-24"},
+				}, Day: 11,
+				MiddleDayString: []string{"2024-07-01", "2024-07-22", "2024-08-12", "2024-09-02", "2024-09-23", "2024-10-14"},
+			},
+		},
+	}
+
+	for _, tt := range tests {
+		t.Run(tt.startDay, func(t *testing.T) {
+			got, err := Get21DayPeriods(tt.startDay, tt.endDay)
+			assert.Nil(t, err)
+			assert.Equal(t, tt.got.DateRange, got)
+			for i, v := range tt.got.DateRange {
+				middleDate, err := GetRangeDayMiddleDay(v, tt.got.Day)
+				assert.Nil(t, err)
+				assert.Equal(t, tt.got.MiddleDayString[i], middleDate)
+			}
+		})
+	}
+}