package backend import ( "context" "errors" "fmt" "kpt-pasture/model" "kpt-pasture/util" "net/http" "go.uber.org/zap" "gitee.com/xuyiping_admin/pkg/logger/zaplog" pasturePb "gitee.com/xuyiping_admin/go_proto/proto/go/backend/cow" "gitee.com/xuyiping_admin/pkg/xerr" "gorm.io/gorm" ) func (s *StoreEntry) PregnantCheckList(ctx context.Context, req *pasturePb.SearchEventRequest, pagination *pasturePb.PaginationModel) (*pasturePb.EventPregnantCheckResponse, error) { userModel, err := s.GetUserModel(ctx) if err != nil { return nil, xerr.Custom("当前用户信息错误,请退出重新登录") } pregnantCheckList := make([]*model.EventPregnantCheck, 0) var count int64 = 0 pref := s.DB.Table(fmt.Sprintf("%s as a", new(model.EventPregnantCheck).TableName())). Joins(fmt.Sprintf("JOIN %s AS b on a.cow_id = b.id", new(model.Cow).TableName())). Where("a.pasture_id = ?", userModel.AppPasture.Id). Where("a.status = ?", pasturePb.IsShow_Ok) if req.StartDayAt > 0 && req.EndDayAt > 0 && req.EndDayAt >= req.StartDayAt { pref.Where("a.reality_day BETWEEN ? AND ?", req.StartDayAt, req.EndDayAt) } if req.EarNumber != "" { pref.Where("a.ear_number = ?", req.EarNumber) } if req.PenId > 0 { pref.Where("b.pen_id = ?", req.PenId) } if err = pref.Order("a.id desc"). Count(&count).Limit(int(pagination.PageSize)). Offset(int(pagination.PageOffset)). Find(&pregnantCheckList).Error; err != nil { return nil, xerr.WithStack(err) } pregnantCheckResultMap := s.PregnantCheckResultMap() pregnantCheckMethodMap := s.PregnantCheckMethodMap() return &pasturePb.EventPregnantCheckResponse{ Code: http.StatusOK, Msg: "ok", Data: &pasturePb.SearchPregnantCheckData{ List: model.EventPregnantCheckSlice(pregnantCheckList).ToPB(pregnantCheckResultMap, pregnantCheckMethodMap), Total: int32(count), PageSize: pagination.PageSize, Page: pagination.Page, }, }, nil } func (s *StoreEntry) PregnantCheckCreateBatch(ctx context.Context, req *pasturePb.EventPregnantCheckBatch) (err error) { userModel, err := s.GetUserModel(ctx) if err != nil { return xerr.WithStack(err) } pregnantCheckBatchModelList, err := s.PregnantCheckDataCheck(ctx, userModel.AppPasture.Id, req) if err != nil { return xerr.WithStack(err) } abortionReasonsMap := s.AbortionReasonsMap() matingResultMap := s.MatingResultMap() if err = s.DB.Transaction(func(tx *gorm.DB) error { for _, item := range pregnantCheckBatchModelList { breedStatus := pasturePb.BreedStatus_Pregnant isPregnant := pasturePb.IsShow_Ok matingResult := pasturePb.MatingResult_Pregnant if item.PregnantCheckResult == pasturePb.PregnantCheckResult_UnPregnant { breedStatus = pasturePb.BreedStatus_Empty isPregnant = pasturePb.IsShow_No matingResult = pasturePb.MatingResult_Empty } // 更新上一次配种结果数据,如何是复检无胎则更新为流产 if item.EventPregnancyCheck.PregnantCheckName == model.PregnantCheckForSecond { breedStatus = pasturePb.BreedStatus_Abort matingResult = pasturePb.MatingResult_Abort } item.Cow.EventPregnantCheckUpdate(breedStatus, int64(item.PregnantCheckAt), isPregnant) // 更新牛只基本信息 if err = tx.Model(item.Cow). Select("breed_status", "last_pregnant_check_at", "is_pregnant"). Where("id = ?", item.Cow.Id). Updates(item.Cow).Error; err != nil { return xerr.WithStack(err) } // 更新最近一次配种结果数据 item.LastMating.EventMatingResultUpdate(matingResult, int64(item.PregnantCheckAt)) if err = tx.Model(item.LastMating). Where("id = ?", item.LastMating.Id). Select("mating_result", "mating_result_at"). Updates(item.LastMating).Error; err != nil { return xerr.WithStack(err) } // 更新孕检事件表 item.EventPregnancyCheck.EventUpdate(int64(item.PregnantCheckAt), item.PregnantCheckResult, item.PregnantCheckMethod, item.OperationUser, userModel.SystemUser, item.Remarks) if err = tx.Model(new(model.EventPregnantCheck)). Select("reality_day", "pregnant_check_result", "pregnant_check_method", "operation_id", "operation_name", "message_id", "message_name", "remarks", "status"). Where("id = ?", item.EventPregnancyCheck.Id). Updates(item.EventPregnancyCheck).Error; err != nil { return xerr.WithStack(err) } // 更新上次配种结果 matingResultName := "" if matingRName, ok := matingResultMap[matingResult]; ok { matingResultName = matingRName } if err = s.UpdateMatingResultEventCowLogByCowId(ctx, item.Cow.Id, matingResultName); err != nil { zaplog.Error("PregnantCheckCreateBatch", zap.Any("UpdateMatingResultEventCowLogByCowId", err)) return xerr.WithStack(err) } // 如果确定是流产 if matingResult == pasturePb.MatingResult_Abort { newEventAbortion := model.NewEventAbortion(userModel.AppPasture.Id, item.Cow, &pasturePb.EventAbortionItem{ 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) } } // 记录日志 cowLogs := s.SubmitEventLog(ctx, userModel.AppPasture.Id, item.Cow, pasturePb.EventType_Pregnancy_Check, item) if err = tx.Table(cowLogs.TableName()).Create(cowLogs).Error; err != nil { return xerr.WithStack(err) } } return nil }); err != nil { return xerr.WithStack(err) } return nil } func (s *StoreEntry) AbortionCreateBatch(ctx context.Context, req *pasturePb.EventAbortionBatch) error { if len(req.Items) <= 0 { return nil } userModel, err := s.GetUserModel(ctx) if err != nil { return xerr.WithStack(err) } eventAbortionModelList, err := s.AbortionEventDataCheck(ctx, userModel, req.Items) if err != nil { return xerr.WithStack(err) } if len(eventAbortionModelList) <= 0 { return xerr.Custom("数据异常") } if err = s.DB.Transaction(func(tx *gorm.DB) error { for _, item := range eventAbortionModelList { if err = tx.Create(item.EventAbortion).Error; err != nil { return xerr.WithStack(err) } item.Cow.EventAbortionUpdate(item.EventAbortion.AbortionAt, item.IsLact) // 更新牛只状态 if err = tx.Model(new(model.Cow)). Select("is_pregnant", "last_abortion_at", "breed_status"). Where("id = ?", item.Cow.Id). Updates(item.Cow).Error; err != nil { return xerr.WithStack(err) } lastCowMating := &model.EventMating{} if err = s.DB.Model(new(model.EventMating)). Where("cow_id = ?", item.Cow.Id). Where("mating_result = ?", pasturePb.MatingResult_Pregnant). Order("id desc").First(lastCowMating).Error; err != nil { return xerr.WithStack(err) } // 更新最近一次配种结果为流产 lastCowMating.EventMatingResultUpdate(pasturePb.MatingResult_Abort, item.EventAbortion.AbortionAt) // 更新最近一次配种结果为流产 if err = tx.Model(new(model.EventMating)). Select("mating_result", "mating_result_at"). Where("id = ?", lastCowMating.Id). Updates(lastCowMating).Error; err != nil { } // 记录日志 cowLog := s.SubmitEventLog(ctx, userModel.AppPasture.Id, item.Cow, pasturePb.EventType_Abort, item) if err = tx.Table(cowLog.TableName()).Create(cowLog).Error; err != nil { return xerr.WithStack(err) } } return nil }); err != nil { return xerr.WithStack(err) } return nil } func (s *StoreEntry) AbortionList( ctx context.Context, req *pasturePb.SearchEventRequest, pagination *pasturePb.PaginationModel, ) (*pasturePb.EventAbortionResponse, error) { userModel, err := s.GetUserModel(ctx) if err != nil { return nil, xerr.WithStack(err) } abortionList := make([]*model.EventAbortion, 0) var count int64 = 0 pref := s.DB.Model(new(model.EventAbortion)). Where("pasture_id = ?", userModel.AppPasture.Id) if len(req.CowId) > 0 { cowIds, err := util.ConvertCowIdsToInt64Slice(req.CowId) if err != nil { return nil, xerr.Custom("cowId转换失败") } pref.Where("cow_id IN (?)", cowIds) } if req.StartDayAt > 0 && req.EndDayAt > 0 && req.StartDayAt <= req.EndDayAt { pref.Where("abortion_at BETWEEN ? AND ?", req.StartDayAt, req.EndDayAt) } if req.EarNumber != "" { pref.Where("ear_number = ?", req.EarNumber) } if err = pref.Order("id desc"). Count(&count).Limit(int(pagination.PageSize)). Offset(int(pagination.PageOffset)). Find(&abortionList).Error; err != nil { return nil, xerr.WithStack(err) } return &pasturePb.EventAbortionResponse{ Code: http.StatusOK, Msg: "ok", Data: &pasturePb.EventAbortionData{ List: model.AbortionSlice(abortionList).ToPB(), Total: int32(count), PageSize: pagination.PageSize, Page: pagination.Page, }, }, nil } func (s *StoreEntry) MatingList(ctx context.Context, req *pasturePb.SearchEventRequest, pagination *pasturePb.PaginationModel) (*pasturePb.EventMatingResponse, error) { userModel, err := s.GetUserModel(ctx) if err != nil { return nil, xerr.WithStack(err) } matingList := make([]*model.EventMating, 0) var count int64 = 0 pref := s.DB.Model(new(model.EventMating)). Where("mating_result > ?", pasturePb.MatingResult_Invalid). Where("status = ?", pasturePb.IsShow_Ok). Where("pasture_id = ?", userModel.AppPasture.Id) if req.EarNumber != "" { pref.Where("ear_number = ?", req.EarNumber) } if req.StartDayAt > 0 && req.EndDayAt > 0 && req.StartDayAt <= req.EndDayAt { pref.Where("reality_day BETWEEN ? AND ?", req.StartDayAt, req.EndDayAt) } if err = pref.Order("id desc"). Count(&count).Limit(int(pagination.PageSize)). Offset(int(pagination.PageOffset)). Find(&matingList).Error; err != nil { return nil, xerr.WithStack(err) } exposeEstrusTypeMap := s.ExposeEstrusTypeMap() return &pasturePb.EventMatingResponse{ Code: http.StatusOK, Msg: "ok", Data: &pasturePb.SearchMatingData{ List: model.EventMatingSlice(matingList).ToPB(exposeEstrusTypeMap), Total: int32(count), PageSize: pagination.PageSize, Page: pagination.Page, }, }, nil } // MatingBatch 牛只配种 func (s *StoreEntry) MatingBatch(ctx context.Context, req *pasturePb.EventMatingBatch) (err error) { userModel, err := s.GetUserModel(ctx) if err != nil { return xerr.WithStack(err) } eventMatingCheckModelList, err := s.MatingCreateCheck(ctx, userModel.AppPasture.Id, req) if err != nil { return xerr.WithStack(err) } if err = s.DB.Transaction(func(tx *gorm.DB) error { for _, item := range eventMatingCheckModelList { // 脖环发情揭发数据 if item.ExposeEstrusType == pasturePb.ExposeEstrusType_Neck_Ring { // 新增发情事件数据 newEstrus := model.NewEventEstrus( userModel.AppPasture.Id, item.Cow, pasturePb.ExposeEstrusType_Neck_Ring, pasturePb.IsShow_Ok, pasturePb.IsShow_Ok, item.MatingAt, item.OperationUser, userModel.SystemUser, ) if err = tx.Create(newEstrus).Error; err != nil { return xerr.WithStack(err) } // 新增配种事件数据 newMating := model.NewEventMating(userModel.AppPasture.Id, item.Cow, item.MatingAt, pasturePb.ExposeEstrusType_Neck_Ring) newMating.EventUpdate(item.MatingAt, item.FrozenSemen.BullId, false, item.OperationUser, userModel.SystemUser) if err = s.MatingCowUpdate(ctx, userModel.AppPasture.Id, item, false); err != nil { return xerr.WithStack(err) } if err = tx.Model(newMating).Create(newMating).Error; err != nil { return xerr.WithStack(err) } } else { // 获取牛只最近一次配种信息 lastEventMating, err := s.FindLastEventMatingByCowId(ctx, userModel.AppPasture.Id, item.Cow.Id) if err != nil { // 1. 没有配种信息,(第一次参加配种的牛只,并且没有参与同期的牛只 ) if errors.Is(err, gorm.ErrRecordNotFound) { newMating := model.NewEventMatingNaturalEstrus(userModel.AppPasture.Id, item, userModel.SystemUser) if err = tx.Create(newMating).Error; err != nil { return xerr.WithStack(err) } if err = s.MatingCowUpdate(ctx, userModel.AppPasture.Id, item, false); err != nil { return xerr.WithStack(err) } continue } else { return xerr.WithStack(err) } } // 2. 所有有配种数据的牛只 if lastEventMating == nil || lastEventMating.Id <= 0 || lastEventMating.Status != pasturePb.IsShow_No { zaplog.Error("MatingCreate", zap.Any("cow", item.Cow), zap.Any("UserModel", userModel)) return xerr.Customf("牛只配种数据异常: %d", item.Cow.EarNumber) } // 2.1 复配 => 本胎次配次不加1 isReMating := lastEventMating.IsReMating(item.Cow, item.MatingAt) if isReMating { lastEventMating.EventReMatingUpdate(item.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(item.MatingAt, item.FrozenSemen.BullId, isReMating, item.OperationUser, userModel.SystemUser) 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) } } // 2.3. 提交的配种数据中,定位出空怀的牛只, isEmptyMating := lastEventMating.IsEmptyMating(item.Cow, item.MatingAt) if isEmptyMating { // 把上次配种结果信息更新成空怀 lastEventMating.EventMatingResultUpdate(pasturePb.MatingResult_Empty, item.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(userModel.AppPasture.Id, item.Cow, item.MatingAt, pasturePb.ExposeEstrusType_Natural_Estrus) newMating.EventUpdate(item.MatingAt, item.FrozenSemen.BullId, isReMating, item.OperationUser, userModel.SystemUser) if err = tx.Model(newMating).Create(newMating).Error; err != nil { return xerr.WithStack(err) } // 更新牛只配种结果日志 if err = s.UpdateMatingResultEventCowLogByCowId(ctx, item.Cow.Id, s.MatingResultMap()[pasturePb.MatingResult_Empty]); err != nil { zaplog.Error("MatingCreate", zap.Any("UpdateEventCowLogByCowId", err), zap.Any("cow", item.Cow)) } } // 牛只基本中配种信息更新 if err = s.MatingCowUpdate(ctx, userModel.AppPasture.Id, item, isReMating); err != nil { return xerr.WithStack(err) } } // 减去精液的数量 item.FrozenSemen.EventQuantityUpdate(item.FrozenSemenCount) if err = tx.Model(item.FrozenSemen). Select("quantity"). Where("id = ?", item.FrozenSemen.Id). Updates(item.FrozenSemen).Error; err != nil { return xerr.WithStack(err) } // 创建冻精使用记录日志 itemFrozenSemenLog := model.NewEventFrozenSemenLog(userModel.AppPasture.Id, item) if err = tx.Create(itemFrozenSemenLog).Error; err != nil { return xerr.WithStack(err) } } return nil }); err != nil { return xerr.WithStack(err) } return nil } func (s *StoreEntry) MatingCowUpdate(ctx context.Context, pastureId int64, item *model.EventMatingCheckBatchModel, isReMating bool) error { // 牛只基本中配种信息更新 item.Cow.EventMatingUpdate(item.MatingAt, item.FrozenSemen.BullId, isReMating) if err := s.DB.Model(item.Cow). Select("last_mating_at", "mating_times", "last_bull_number", "first_mating_at", "is_pregnant", "breed_status"). Where("id = ?", item.Cow.Id). Updates(item.Cow).Error; err != nil { return xerr.WithStack(err) } // 记录日志 cowLogs := s.SubmitEventLog(ctx, pastureId, item.Cow, pasturePb.EventType_Mating, item) 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.EventWeaningBatch) (err error) { userModel, err := s.GetUserModel(ctx) if err != nil { return xerr.WithStack(err) } if len(req.Items) <= 0 { return nil } cowIds := make([]int64, 0) cowWeightMap := make(map[int64]float64) for _, item := range req.Items { cowIds = append(cowIds, int64(item.CowId)) cowWeightMap[int64(item.CowId)] = float64(item.Weight) } eventWeaningList := make([]*model.EventWeaning, 0) if err = s.DB.Model(new(model.EventWeaning)). Where("cow_id IN ?", cowIds). Where("status = ?", pasturePb.IsShow_No). Find(&eventWeaningList).Error; err != nil { return xerr.WithStack(err) } operation, err := s.GetSystemUserById(ctx, int64(req.OperationId)) if err != nil { return xerr.WithStack(err) } req.OperationName = operation.Name if err = s.DB.Transaction(func(tx *gorm.DB) error { cowInfo := &model.Cow{} for _, eventWeaning := range eventWeaningList { eventWeaning.EventUpdate(int64(req.WeaningAt), int32(cowWeightMap[cowInfo.Id]*1000), req.Remarks, req.PenId, operation, userModel.SystemUser) if err = tx.Model(new(model.EventWeaning)). Select("status", "reality_day", "operation_id", "operation_name", "message_id", "message_name", "remarks", "after_pen_id"). Where("id = ?", eventWeaning.Id). Updates(eventWeaning).Error; err != nil { return xerr.WithStack(err) } cowInfo, err = s.GetCowInfoByCowId(ctx, userModel.AppPasture.Id, eventWeaning.CowId) if err != nil { return xerr.WithStack(err) } cowInfo.EventWeaningUpdate(int64(req.WeaningAt), req.PenId, int64(cowWeightMap[cowInfo.Id]*1000)) if err = tx.Model(new(model.Cow)). Select("pen_id", "current_weight", "weaning_at", "last_weight_at"). Where("id = ?", cowInfo.Id). Updates(cowInfo).Error; err != nil { return xerr.WithStack(err) } // 创建牛只的体重记录 eventWeight := &pasturePb.EventWeight{ WeightAt: req.WeaningAt, OperationId: req.OperationId, OperationName: req.OperationName, Remarks: req.Remarks, Weight: float32(cowWeightMap[cowInfo.Id]), Height: 0, } newEventWeight := model.NewEventWeight(userModel.AppPasture.Id, cowInfo, userModel.SystemUser, eventWeight) if err = tx.Create(newEventWeight).Error; err != nil { return xerr.WithStack(err) } // 记录事件日志 cowLogs := s.SubmitEventLog(ctx, userModel.AppPasture.Id, cowInfo, pasturePb.EventType_Weaning, req) if err = tx.Table(cowLogs.TableName()).Create(cowLogs).Error; err != nil { return xerr.WithStack(err) } } return nil }); err != nil { return xerr.WithStack(err) } return nil }