瀏覽代碼

eventMating: 牛只配种事件更新

Yi 3 月之前
父節點
當前提交
c986d117ab

+ 1 - 1
go.mod

@@ -3,7 +3,7 @@ module kpt-pasture
 go 1.17
 
 require (
-	gitee.com/xuyiping_admin/go_proto v0.0.0-20250107024845-37a577c13d5e
+	gitee.com/xuyiping_admin/go_proto v0.0.0-20250107064743-4115ed6f2a73
 	gitee.com/xuyiping_admin/pkg v0.0.0-20241108060137-caea58c59f5b
 	github.com/dgrijalva/jwt-go v3.2.0+incompatible
 	github.com/eclipse/paho.mqtt.golang v1.4.3

+ 2 - 0
go.sum

@@ -116,6 +116,8 @@ gitee.com/xuyiping_admin/go_proto v0.0.0-20250107022103-9539c511e1dd h1:gkUnfEcy
 gitee.com/xuyiping_admin/go_proto v0.0.0-20250107022103-9539c511e1dd/go.mod h1:BKrFW6YLDectlQcQk3FYKBeXvjEiodAKJ5rq7O/QiPE=
 gitee.com/xuyiping_admin/go_proto v0.0.0-20250107024845-37a577c13d5e h1:P3cy/uJuG9uCpWiZJsncgkEPG4fuKffscpWqXwDJXiE=
 gitee.com/xuyiping_admin/go_proto v0.0.0-20250107024845-37a577c13d5e/go.mod h1:BKrFW6YLDectlQcQk3FYKBeXvjEiodAKJ5rq7O/QiPE=
+gitee.com/xuyiping_admin/go_proto v0.0.0-20250107064743-4115ed6f2a73 h1:M53fn2/vuz8+jAi/sdKMzsubz34k5R5vdeNi4ULmp3c=
+gitee.com/xuyiping_admin/go_proto v0.0.0-20250107064743-4115ed6f2a73/go.mod h1:BKrFW6YLDectlQcQk3FYKBeXvjEiodAKJ5rq7O/QiPE=
 gitee.com/xuyiping_admin/pkg v0.0.0-20241108060137-caea58c59f5b h1:w05MxH7yqveRlaRbxHhbif5YjPrJFodRPfOjYhXn7Zk=
 gitee.com/xuyiping_admin/pkg v0.0.0-20241108060137-caea58c59f5b/go.mod h1:8tF25X6pE9WkFCczlNAC0K2mrjwKvhhp02I7o0HtDxY=
 github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=

+ 5 - 3
model/event_abortion.go

@@ -20,6 +20,7 @@ type EventAbortion struct {
 	AbortionReasons     pasturePb.AbortionReasons_Kind `json:"abortionReasons"`
 	AbortionReasonsName string                         `json:"abortionReasonsName"`
 	Remarks             string                         `json:"remarks"`
+	IsAppend            pasturePb.IsShow_Kind          `json:"isAppend"`
 	OperationId         int64                          `json:"operationId"`
 	OperationName       string                         `json:"operationName"`
 	CreatedAt           int64                          `json:"createdAt"`
@@ -30,10 +31,10 @@ func (e *EventAbortion) TableName() string {
 	return "event_abortion"
 }
 
-func NewEventAbortion(pastureId int64, cow *Cow, req *pasturePb.EventAbortionRequest, abortionReasonMap map[pasturePb.AbortionReasons_Kind]string) *EventAbortion {
+func NewEventAbortion(pastureId int64, cow *Cow, req *pasturePb.EventAbortionRequest, isAppend pasturePb.IsShow_Kind) *EventAbortion {
 	return &EventAbortion{
 		PastureId:           pastureId,
-		CowId:               int64(req.CowId),
+		CowId:               cow.Id,
 		Lact:                cow.Lact,
 		CowType:             cow.CowType,
 		PregnantAge:         cow.GetDaysPregnant(),
@@ -42,8 +43,9 @@ func NewEventAbortion(pastureId int64, cow *Cow, req *pasturePb.EventAbortionReq
 		IsAfterbirth:        req.IsAfterbirth,
 		Photos:              strings.Join(req.Photos, ","),
 		AbortionReasons:     req.AbortionReasons,
-		AbortionReasonsName: abortionReasonMap[req.AbortionReasons],
+		AbortionReasonsName: req.AbortionReasonsName,
 		Remarks:             req.Remarks,
+		IsAppend:            isAppend,
 		OperationId:         int64(req.OperationId),
 		OperationName:       req.OperationName,
 	}

+ 16 - 1
model/event_mating.go

@@ -56,6 +56,13 @@ func (e *EventMating) EventUpdate(matingAt int64, bullNumber string, isReMating
 	}
 }
 
+// EventReMatingUpdate 复配更新
+func (e *EventMating) EventReMatingUpdate(matingAt int64) {
+	e.MatingResult = pasturePb.MatingResult_ReMatch
+	e.Status = pasturePb.IsShow_Ok
+	e.MatingResultAt = matingAt
+}
+
 // EventMatingResultUpdate 配种结果更新
 func (e *EventMating) EventMatingResultUpdate(matingResult pasturePb.MatingResult_Kind, resultAt int64) {
 	e.MatingResult = matingResult
@@ -67,7 +74,15 @@ func (e *EventMating) IsReMating(cow *Cow, matingAt int64) bool {
 	lastMatingAt := time.Unix(cow.LastMatingAt, 0)
 	currentMatingAt := time.Unix(matingAt, 0)
 	daysBetween := util.DaysBetween(currentMatingAt.Unix(), lastMatingAt.Unix())
-	if (currentMatingAt.Format(LayoutDate2) == lastMatingAt.Format(LayoutDate2) || daysBetween == 1) && e.Status == pasturePb.IsShow_Ok && e.MatingResult == pasturePb.MatingResult_Unknown {
+	if (daysBetween == 1 || daysBetween == 0) && e.Status == pasturePb.IsShow_Ok && e.MatingResult == pasturePb.MatingResult_Unknown {
+		return true
+	}
+	return false
+}
+
+// IsMatingUpdate 判断是不是更新配种信息
+func (e *EventMating) IsMatingUpdate() bool {
+	if e.Status == pasturePb.IsShow_No && e.MatingResult == pasturePb.MatingResult_Unknown {
 		return true
 	}
 	return false

+ 4 - 0
module/backend/config_data_breed.go

@@ -405,6 +405,10 @@ func (s *StoreEntry) AbortionReasonsEnumList(isAll string) []*pasturePb.ConfigOp
 		Value:    int32(pasturePb.AbortionReasons_Infectious_Abortion),
 		Label:    "传染病性流产",
 		Disabled: true,
+	}, &pasturePb.ConfigOptionsList{
+		Value:    int32(pasturePb.AbortionReasons_Unknown),
+		Label:    "未知原因",
+		Disabled: true,
 	}, &pasturePb.ConfigOptionsList{
 		Value:    int32(pasturePb.AbortionReasons_Other),
 		Label:    "其他",

+ 2 - 1
module/backend/dashboard.go

@@ -13,7 +13,8 @@ import (
 func (s *StoreEntry) Bar(ctx context.Context) (*pasturePb.BarCowStructResponse, error) {
 	barCowStructList := make([]*model.BarCowStruct, 0)
 	var count int32 = 0
-	if err := s.DB.Model(new(model.Cow)).Select("COUNT(*) AS number ,cow_type").
+	if err := s.DB.Model(new(model.Cow)).
+		Select("COUNT(*) AS number ,cow_type").
 		Where("admission_status = ?", pasturePb.AdmissionStatus_Admission).
 		Group("cow_type").
 		Find(&barCowStructList).Error; err != nil {

+ 75 - 54
module/backend/event_breed.go

@@ -477,81 +477,85 @@ func (s *StoreEntry) MatingCreate(ctx context.Context, req *pasturePb.EventMatin
 	if err != nil {
 		return xerr.WithStack(err)
 	}
+
 	req.OperationName = eventCheckModel.OperationUser.Name
 	if err = s.DB.Transaction(func(tx *gorm.DB) error {
 		for _, cow := range eventCheckModel.CowList {
+			// 获取牛只最近一次配种信息
 			lastEventMating, ok, err := s.FindLastEventMatingByCowId(ctx, eventCheckModel.CurrentUser.PastureId, cow.Id)
 			if err != nil {
 				return xerr.WithStack(err)
 			}
 
-			var (
-				newMating     *model.EventMating
-				isReMating    = false
-				isEmptyMating = false
-			)
-
-			// 1. 自然发情牛只,未经过同期的牛只和未佩戴脖环的牛只
+			// 1. 没有配种信息,(第一次参加配种的牛只,并且没有参与同期的牛只 )
 			if !ok {
-				newMating = model.NewEventMatingNaturalEstrus(cow, req, eventCheckModel.CurrentUser)
+				newMating := model.NewEventMatingNaturalEstrus(cow, req, eventCheckModel.CurrentUser)
 				if err = tx.Create(newMating).Error; err != nil {
 					return xerr.WithStack(err)
 				}
-			} else {
-				// 2. 所有有配种数据的牛只
-				if lastEventMating == nil || lastEventMating.Id <= 0 {
-					zaplog.Error("MatingCreate", zap.Any("cow", cow), zap.Any("CurrentUser", eventCheckModel.CurrentUser))
-					continue
+				if err = s.MatingCowUpdate(ctx, cow, eventCheckModel.CurrentUser, req, false); err != nil {
+					return xerr.WithStack(err)
+				}
+				continue
+			}
+
+			// 2. 所有有配种数据的牛只
+			if lastEventMating == nil || lastEventMating.Id <= 0 {
+				zaplog.Error("MatingCreate", zap.Any("cow", cow), zap.Any("CurrentUser", eventCheckModel.CurrentUser))
+				return xerr.Customf("异常牛数据: %d", cow.Id)
+			}
+
+			// 2.1 复配 => 本胎次配次不加1
+			isReMating := lastEventMating.IsReMating(cow, int64(req.MatingAt))
+			if isReMating {
+				lastEventMating.EventReMatingUpdate(int64(req.MatingAt))
+				if err = tx.Model(lastEventMating).
+					Select("mating_result", "mating_result_at", "status").
+					Where("id = ?", lastEventMating.Id).
+					Updates(lastEventMating).Error; err != nil {
+				}
+			}
+
+			// 2.2.  同期初配
+			IsMatingUpdate := lastEventMating.IsMatingUpdate()
+			if IsMatingUpdate {
+				lastEventMating.EventUpdate(int64(req.MatingAt), req.FrozenSemenNumber, isReMating, eventCheckModel.OperationUser, eventCheckModel.CurrentUser)
+				if err = tx.Model(lastEventMating).
+					Select("mating_at", "status", "reality_day", "frozen_semen_number", "operation_id", "operation_name", "message_id", "message_name").
+					Where("id = ?", lastEventMating.Id).
+					Updates(lastEventMating).Error; err != nil {
+					return xerr.WithStack(err)
 				}
+			}
 
-				// 复配 => 本胎次配次不加1
-				isReMating = lastEventMating.IsReMating(cow, int64(req.MatingAt))
-				// 空怀
-				isEmptyMating = lastEventMating.IsEmptyMating(cow, int64(req.MatingAt))
-
-				// 2。1. 提交的配种数据中,定位出空怀的牛只,
-				if isEmptyMating {
-					// 把上次配种结果信息更新成空怀
-					lastEventMating.EventMatingResultUpdate(pasturePb.MatingResult_Empty, int64(req.MatingAt))
-					if err = tx.Model(lastEventMating).
-						Select("mating_result", "mating_result_at").
-						Where("id = ?", lastEventMating.Id).
-						Updates(lastEventMating).Error; err != nil {
-						return xerr.WithStack(err)
-					}
-					// 先创建一条新的配种数据
-					newMating = model.NewEventMating(eventCheckModel.CurrentUser.PastureId, cow, int64(req.MatingAt), req.ExposeEstrusType)
-					newMating.EventUpdate(int64(req.MatingAt), req.FrozenSemenNumber, isReMating, eventCheckModel.OperationUser, eventCheckModel.CurrentUser)
-					if err = tx.Model(newMating).Create(newMating).Error; err != nil {
-						return xerr.WithStack(err)
-					}
-					if err = s.UpdateMatingResultEventCowLogByCowId(ctx, cow.Id, s.MatingResultMap()[pasturePb.MatingResult_Empty]); err != nil {
-						zaplog.Error("MatingCreate", zap.Any("UpdateEventCowLogByCowId", err), zap.Any("cow", cow))
-					}
+			// 2.3. 提交的配种数据中,定位出空怀的牛只,
+			isEmptyMating := lastEventMating.IsEmptyMating(cow, int64(req.MatingAt))
+			if isEmptyMating {
+				// 把上次配种结果信息更新成空怀
+				lastEventMating.EventMatingResultUpdate(pasturePb.MatingResult_Empty, int64(req.MatingAt))
+				if err = tx.Model(lastEventMating).
+					Select("mating_result", "mating_result_at").
+					Where("id = ?", lastEventMating.Id).
+					Updates(lastEventMating).Error; err != nil {
+					return xerr.WithStack(err)
 				}
-				// 2.2.  同期初配
-				if !isEmptyMating && !isReMating {
-					lastEventMating.EventUpdate(int64(req.MatingAt), req.FrozenSemenNumber, isReMating, eventCheckModel.OperationUser, eventCheckModel.CurrentUser)
-					if err = tx.Model(lastEventMating).
-						Select("mating_at", "status", "reality_day", "frozen_semen_number", "operation_id", "operation_name", "message_id", "message_name").
-						Where("id = ?", lastEventMating.Id).
-						Updates(lastEventMating).Error; err != nil {
-						return xerr.WithStack(err)
-					}
+				// 先创建一条新的配种数据
+				newMating := model.NewEventMating(eventCheckModel.CurrentUser.PastureId, cow, int64(req.MatingAt), req.ExposeEstrusType)
+				newMating.EventUpdate(int64(req.MatingAt), req.FrozenSemenNumber, isReMating, eventCheckModel.OperationUser, eventCheckModel.CurrentUser)
+				if err = tx.Model(newMating).Create(newMating).Error; err != nil {
+					return xerr.WithStack(err)
+				}
+
+				// 更新牛只配种结果日志
+				if err = s.UpdateMatingResultEventCowLogByCowId(ctx, cow.Id, s.MatingResultMap()[pasturePb.MatingResult_Empty]); err != nil {
+					zaplog.Error("MatingCreate", zap.Any("UpdateEventCowLogByCowId", err), zap.Any("cow", cow))
 				}
 			}
 
 			// 牛只基本中配种信息更新
-			cow.EventMatingUpdate(int64(req.MatingAt), req.FrozenSemenNumber, isReMating)
-			if err = tx.Model(cow).
-				Select("last_mating_at", "mating_times", "last_bull_number", "first_mating_at", "is_pregnant", "breed_status").
-				Where("id = ?", cow.Id).
-				Updates(cow).Error; err != nil {
+			if err = s.MatingCowUpdate(ctx, cow, eventCheckModel.CurrentUser, req, isReMating); err != nil {
 				return xerr.WithStack(err)
 			}
-			// 记录日志
-			cowLogs := s.SubmitEventLog(ctx, eventCheckModel.CurrentUser, cow, pasturePb.EventType_Mating, req.ExposeEstrusType, req)
-			tx.Table(cowLogs.TableName()).Create(cowLogs)
 		}
 
 		// 创建冻精使用记录日志
@@ -576,6 +580,23 @@ func (s *StoreEntry) MatingCreate(ctx context.Context, req *pasturePb.EventMatin
 	return nil
 }
 
+func (s *StoreEntry) MatingCowUpdate(ctx context.Context, cow *model.Cow, currentUser *model.SystemUser, req *pasturePb.EventMating, isReMating bool) error {
+	// 牛只基本中配种信息更新
+	cow.EventMatingUpdate(int64(req.MatingAt), req.FrozenSemenNumber, isReMating)
+	if err := s.DB.Model(cow).
+		Select("last_mating_at", "mating_times", "last_bull_number", "first_mating_at", "is_pregnant", "breed_status").
+		Where("id = ?", cow.Id).
+		Updates(cow).Error; err != nil {
+		return xerr.WithStack(err)
+	}
+	// 记录日志
+	cowLogs := s.SubmitEventLog(ctx, currentUser, cow, pasturePb.EventType_Mating, req.ExposeEstrusType, req)
+	if err := s.DB.Table(cowLogs.TableName()).Create(cowLogs).Error; err != nil {
+		return xerr.WithStack(err)
+	}
+	return nil
+}
+
 func (s *StoreEntry) WeaningBatch(ctx context.Context, req *pasturePb.EventWeaningBatchRequest) (err error) {
 	currentUser, err := s.GetCurrentSystemUser(ctx)
 	if err != nil {

+ 22 - 2
module/backend/event_breed_more.go

@@ -58,6 +58,7 @@ func (s *StoreEntry) PregnantCheckCreateBatch(ctx context.Context, req *pastureP
 		return xerr.WithStack(err)
 	}
 
+	abortionReasonsMap := s.AbortionReasonsMap()
 	if err = s.DB.Transaction(func(tx *gorm.DB) error {
 		for _, item := range pregnantCheckBatchModelList {
 			breedStatus := pasturePb.BreedStatus_Pregnant
@@ -111,6 +112,24 @@ func (s *StoreEntry) PregnantCheckCreateBatch(ctx context.Context, req *pastureP
 			if err = s.UpdateMatingResultEventCowLogByCowId(ctx, item.Cow.Id, s.MatingResultMap()[matingResult]); err != nil {
 				zaplog.Error("PregnantCheckCreateBatch", zap.Any("UpdateMatingResultEventCowLogByCowId", err))
 			}
+
+			// 如果确定是流产
+			if matingResult == pasturePb.MatingResult_Abort {
+				newEventAbortion := model.NewEventAbortion(item.CurrentUser.PastureId, item.Cow, &pasturePb.EventAbortionRequest{
+					CowId:               int32(item.Cow.Id),
+					AbortionAt:          item.PregnantCheckAt,
+					IsAfterbirth:        pasturePb.IsShow_No,
+					Photos:              []string{},
+					AbortionReasons:     pasturePb.AbortionReasons_Unknown,
+					AbortionReasonsName: abortionReasonsMap[pasturePb.AbortionReasons_Unknown],
+					Remarks:             "追加流产数据",
+					OperationId:         int32(item.OperationUser.Id),
+					OperationName:       item.OperationUser.Name,
+				}, pasturePb.IsShow_Ok)
+				if err = tx.Create(newEventAbortion).Error; err != nil {
+					return xerr.WithStack(err)
+				}
+			}
 		}
 
 		return nil
@@ -137,8 +156,8 @@ func (s *StoreEntry) AbortionCreate(ctx context.Context, req *pasturePb.EventAbo
 	}
 
 	req.OperationName = systemUser.Name
-	abortionReasonsMap := s.AbortionReasonsMap()
-	newEventAbortion := model.NewEventAbortion(currentUser.PastureId, cow, req, abortionReasonsMap)
+	req.AbortionReasonsName = s.AbortionReasonsMap()[req.AbortionReasons]
+	newEventAbortion := model.NewEventAbortion(currentUser.PastureId, cow, req, pasturePb.IsShow_No)
 
 	lastCowMating := &model.EventMating{}
 	if err = s.DB.Model(new(model.EventMating)).Where("cow_id = ?", cow.Id).
@@ -157,6 +176,7 @@ func (s *StoreEntry) AbortionCreate(ctx context.Context, req *pasturePb.EventAbo
 			s.DB.Table(cowLogs.TableName()).Create(cowLogs)
 		}
 	}()
+
 	if err = s.DB.Transaction(func(tx *gorm.DB) error {
 		// 创建牛只流产事件数据
 		if err = tx.Create(newEventAbortion).Error; err != nil {

+ 5 - 0
module/backend/event_check.go

@@ -54,6 +54,11 @@ func (s *StoreEntry) MatingCreateCheck(ctx context.Context, req *pasturePb.Event
 	for _, v := range req.CowIds {
 		cowIds = append(cowIds, int64(v))
 	}
+
+	if len(cowIds) > 20 {
+		return nil, xerr.Custom("最多只能选择50只牛只")
+	}
+
 	cowList, err := s.GetCowInfoByCowIds(ctx, currentUser.PastureId, cowIds)
 	if err != nil {
 		return nil, xerr.WithStack(err)

+ 1 - 5
util/util.go

@@ -305,6 +305,7 @@ func RemoveDuplicates(slice []string) []string {
 	return result
 }
 
+// DaysBetween 计算两个日期之间的天数差
 func DaysBetween(startDayUnix int64, endDayUnix int64) int64 {
 	time1 := time.Unix(startDayUnix, 0)
 	time2 := time.Unix(endDayUnix, 0)
@@ -314,11 +315,6 @@ func DaysBetween(startDayUnix int64, endDayUnix int64) int64 {
 
 	// Calculate the difference in days
 	daysDiff := int64(startOfDay2.Sub(startOfDay1).Hours() / 24)
-	// Adjust sign if time2 is before time1
-	if startOfDay2.Before(startOfDay1) {
-		daysDiff = -daysDiff
-	}
-
 	return daysDiff
 }
 

+ 16 - 2
util/util_test.go

@@ -402,11 +402,25 @@ func TestDaysBetween(t *testing.T) {
 	}{
 		{
 			actual: []int64{
-				1730772000,
-				1730908800,
+				1730736000, // 2024-11-05 00:00:00
+				1730772000, // 2024-11-05 10:00:00
+			},
+			got: 0,
+		},
+		{
+			actual: []int64{
+				1730772000, // 2024-11-05 10:00:00
+				1730908800, // 2024-11-07 00:00:00
 			},
 			got: 2,
 		},
+		{
+			actual: []int64{
+				1730908800, // 2024-11-07 00:00:00
+				1730772000, // 2024-11-05 10:00:00
+			},
+			got: -2,
+		},
 	}
 
 	for _, v := range tests {