package backend import ( "context" "fmt" "kpt-pasture/model" "kpt-pasture/util" "net/http" "strings" "time" "gitee.com/xuyiping_admin/pkg/logger/zaplog" "go.uber.org/zap" pasturePb "gitee.com/xuyiping_admin/go_proto/proto/go/backend/cow" "gitee.com/xuyiping_admin/pkg/xerr" "gorm.io/gorm" ) func (s *StoreEntry) CalvingList(ctx context.Context, req *pasturePb.SearchEventRequest, pagination *pasturePb.PaginationModel) (*pasturePb.SearchLavingEventResponse, error) { lavingList := make([]*model.EventCalvingList, 0) var count int64 = 0 pref := s.DB.Table(fmt.Sprintf("%s as a", new(model.EventCalving).TableName())). Select(`a.*,b.name as pen_name,c.name as staff_member_name`). Joins(fmt.Sprintf("JOIN %s AS b on a.pen_id = b.id", new(model.Pen).TableName())). Joins(fmt.Sprintf("JOIN %s AS c on a.operation_id = c.id", new(model.SystemUser).TableName())) if len(req.CowId) > 0 { cowIds := strings.Split(req.CowId, ",") pref.Where("a.cow_id IN ?", cowIds) } if err := pref.Order("a.id desc"). Count(&count).Limit(int(pagination.PageSize)). Offset(int(pagination.PageOffset)). Find(&lavingList).Error; err != nil { return nil, xerr.WithStack(err) } calvingIds := make([]int64, 0) for _, v := range lavingList { calvingIds = append(calvingIds, v.Id) } calvingCalfList := make([]*model.CalvingCalf, 0) if err := s.DB.Model(new(model.CalvingCalf)). Where("calving_id IN ?", calvingIds). Find(&calvingCalfList).Error; err != nil { return nil, xerr.WithStack(err) } return &pasturePb.SearchLavingEventResponse{ Code: http.StatusOK, Message: "ok", Data: &pasturePb.SearchLavingData{ List: model.EventCalvingListSlice(lavingList).ToPB(calvingCalfList), Total: int32(count), PageSize: pagination.PageSize, Page: pagination.Page, }, }, nil } func (s *StoreEntry) CalvingCreate(ctx context.Context, req *pasturePb.EventCalving) error { if len(req.CowId) <= 0 { return xerr.Custom("请选择相关牛只") } cowList, err := s.ParseCowIds(ctx, req.CowId) if err != nil { return xerr.WithStack(err) } if len(cowList) <= 0 { return xerr.Custom("请选择相关牛只") } cow := cowList[0] newEventCalving := &model.EventCalving{} if err = s.DB.Model(new(model.EventCalving)).Where("cow_id = ?", cow.Id).Where("lact = ?", cow.Lact).First(newEventCalving).Error; err != nil { return xerr.Custom("该母牛信息不存在") } if err = s.DB.Transaction(func(tx *gorm.DB) error { // 母牛信息 if err = tx.Model(new(model.EventCalving)). Where("id = ?", newEventCalving.Id). Updates(map[string]interface{}{ "reality_day": int64(req.CalvingAt), "day_age": cow.DayAge, "lact": cow.Lact + 1, "pregnancy_age": cow.PregnancyAge, "calving_level": req.CalvingLevel, "bull_number": cow.LastBullNumber, "child_number": len(req.CalfItemList), }). Error; err != nil { return xerr.WithStack(err) } // 犊牛信息 newCalvingCalfList := model.NewEventCalvingCalf(cow.Id, newEventCalving.Id, req) for _, v := range newCalvingCalfList { if v.IsLive == pasturePb.IsShow_No || v.IsAdoption == pasturePb.IsShow_No { continue } // 留养犊牛 newCow := model.NewCalfCow(cow.Id, cow.LastBullNumber, v) if err = tx.Create(newCow).Error; err != nil { return xerr.WithStack(err) } v.CowId = newCow.Id } if err = tx.Create(newCalvingCalfList).Error; err != nil { return xerr.WithStack(err) } if err = tx.Model(new(model.Cow)).Where("id = ?", cow.Id). Updates(map[string]interface{}{ "calving_age": 0, "mating_times": 0, "lact": cow.Lact + 1, "breed_status": pasturePb.BreedStatus_Calving, "is_pregnant": pasturePb.IsShow_No, "last_calving_at": int64(req.CalvingAt), }).Error; err != nil { return xerr.WithStack(err) } return nil }); err != nil { return xerr.WithStack(err) } return nil } func (s *StoreEntry) EstrusList(ctx context.Context, req *pasturePb.SearchEventRequest, pagination *pasturePb.PaginationModel) (*pasturePb.EstrusEventResponse, error) { estrusList := make([]*model.EventEstrus, 0) var count int64 = 0 pref := s.DB.Table(new(model.EventEstrus).TableName()) if len(req.CowId) > 0 { cowIds := strings.Split(req.CowId, ",") pref.Where("cow_id IN ?", cowIds) } if err := pref.Order("id desc"). Count(&count).Limit(int(pagination.PageSize)). Offset(int(pagination.PageOffset)). Find(&estrusList).Error; err != nil { return nil, xerr.WithStack(err) } return &pasturePb.EstrusEventResponse{ Code: http.StatusOK, Message: "ok", Data: &pasturePb.SearchEstrusData{ List: model.EstrusSlice(estrusList).ToPB(), Total: int32(count), PageSize: pagination.PageSize, Page: pagination.Page, }, }, nil } func (s *StoreEntry) EstrusCreate(ctx context.Context, req *pasturePb.EventEstrus) error { estrusInfo, err := s.EventEstrusCheck(ctx, req) if err != nil { return xerr.Custom("获取当前登录用户失败") } if err = s.DB.Create(estrusInfo).Error; err != nil { return xerr.WithStack(err) } return nil } func (s *StoreEntry) EstrusBatch(ctx context.Context, req *pasturePb.EventEstrusBatch) error { estrusList := make([]*model.EventEstrus, 0) for _, v := range req.Item { estrusInfo, err := s.EventEstrusCheck(ctx, v) if err != nil { return xerr.Custom("获取当前登录用户失败") } estrusList = append(estrusList, estrusInfo) } if err := s.DB.Create(estrusList).Error; err != nil { return xerr.WithStack(err) } return nil } func (s *StoreEntry) SameTimeCreate(ctx context.Context, req *pasturePb.EventSameTime) error { eventCowSameTime, err := s.GetEventCowSameTimeById(ctx, int64(req.Id)) if err != nil { return xerr.WithStack(err) } if eventCowSameTime.Status == pasturePb.IsShow_Ok { return xerr.Custom("该事件已处理") } if eventCowSameTime.CowId != int64(req.CowId) { return xerr.Custom("牛只Id 不匹配") } drugs := &model.Drugs{} if req.DrugsId > 0 { if drugs, err = s.GetDrugsById(ctx, int64(req.DrugsId)); err != nil { return xerr.WithStack(err) } } operationUser, 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(new(model.CowSameTime)). Where("cow_id = ?", eventCowSameTime.CowId). Where("same_time_id = ?", eventCowSameTime.Id). Where("same_time_status = ?", pasturePb.SameTimeStatus_No_Start). Update("same_time_status", pasturePb.SameTimeStatus_In_Progress).Error; err != nil { zaplog.Error("SameTimeCreate", zap.Any("err", err), zap.Any("req", req)) } if err = tx.Model(new(model.EventCowSameTime)). Where("id = ?", eventCowSameTime.Id). Where("cow_id = ?", eventCowSameTime.CowId). Where("status = ?", pasturePb.IsShow_No). Updates(map[string]interface{}{ "status": pasturePb.IsShow_Ok, "drug_id": drugs.Id, "unit": drugs.Unit, "usage": req.Usage, "remarks": req.Remarks, "operation_id": operationUser.Id, "operation_name": operationUser.Name, }).Error; err != nil { return xerr.WithStack(err) } return nil }); err != nil { return xerr.WithStack(err) } return nil } func (s *StoreEntry) SameTimeBatch(ctx context.Context, req *pasturePb.EventSameTimeBatch) error { drugs := &model.Drugs{} var err error if req.DrugsId > 0 { if drugs, err = s.GetDrugsById(ctx, int64(req.DrugsId)); err != nil { return xerr.WithStack(err) } } operationUser, err := s.GetSystemUserById(ctx, int64(req.OperationId)) if err != nil { return xerr.WithStack(err) } eventCowSameTimeList := make([]*model.EventCowSameTime, 0) for _, v := range req.Item { eventCowSameTime, err := s.GetEventCowSameTimeById(ctx, int64(v.Id)) if err != nil { return xerr.WithStack(err) } if eventCowSameTime.Status == pasturePb.IsShow_Ok { return xerr.Custom("该事件已处理") } if eventCowSameTime.CowId != int64(v.CowId) { return xerr.Custom(" 牛只Id 不匹配") } eventCowSameTimeList = append(eventCowSameTimeList, eventCowSameTime) } if err = s.DB.Transaction(func(tx *gorm.DB) error { for _, v := range eventCowSameTimeList { if err = tx.Model(new(model.CowSameTime)). Where("cow_id = ?", v.CowId). Where("same_time_id = ?", v.Id). Where("same_time_status = ?", pasturePb.SameTimeStatus_No_Start). Update("same_time_status", pasturePb.SameTimeStatus_In_Progress).Error; err != nil { zaplog.Error("SameTimeCreate", zap.Any("err", err), zap.Any("req", req)) } if err = tx.Model(new(model.EventCowSameTime)). Where("id = ?", v.Id). Where("cow_id = ?", v.CowId). Where("status = ?", pasturePb.IsShow_No). Updates(map[string]interface{}{ "status": pasturePb.IsShow_Ok, "drugs_id": drugs.Id, "unit": drugs.Unit, "usage": req.Usage, "remarks": req.Remarks, "operation_id": operationUser.Id, "operation_name": operationUser.Name, }).Error; err != nil { return xerr.WithStack(err) } } return nil }); err != nil { return xerr.WithStack(err) } return nil } func (s *StoreEntry) SameTimeList( ctx context.Context, req *pasturePb.SearchEventRequest, pagination *pasturePb.PaginationModel, ) (*pasturePb.SearchSameTimeResponse, error) { cowSameTimeList := make([]*model.EventCowSameTime, 0) var count int64 = 0 pref := s.DB.Table(new(model.EventCowSameTime).TableName()) if req.CowId != "" { cowIds := strings.Split(req.CowId, ",") pref.Where("cow_id IN ?", cowIds) } if err := pref.Count(&count). Limit(int(pagination.PageSize)). Offset(int(pagination.PageOffset)). Find(&cowSameTimeList).Error; err != nil { return nil, xerr.WithStack(err) } sameTimeTypeMap := s.SameTimeTypeMap() return &pasturePb.SearchSameTimeResponse{ Code: http.StatusOK, Message: "ok", Data: &pasturePb.SameTimeData{ List: model.EventCowSameTimeSlice(cowSameTimeList).ToPB(sameTimeTypeMap), Total: int32(count), PageSize: pagination.PageSize, Page: pagination.Page, }, }, nil } func (s *StoreEntry) MatingList(ctx context.Context, req *pasturePb.SearchEventRequest, pagination *pasturePb.PaginationModel) (*pasturePb.MatingEventResponse, error) { 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_No) if len(req.CowId) > 0 { cowIds := strings.Split(req.CowId, ",") pref.Where("cow_id IN ?", cowIds) } 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.MatingEventResponse{ Code: http.StatusOK, Message: "ok", Data: &pasturePb.SearchMatingData{ List: model.EventMatingSlice(matingList).ToPB(exposeEstrusTypeMap), Total: int32(count), PageSize: pagination.PageSize, Page: pagination.Page, }, }, nil } func (s *StoreEntry) MatingCreate(ctx context.Context, req *pasturePb.EventMating) error { eventCheckModel, err := s.MatingCreateCheck(ctx, req) if err != nil { return xerr.WithStack(err) } frozenSemen := &model.FrozenSemen{} if err = s.DB.Where("bull_id = ?", req.FrozenSemenNumber). First(frozenSemen).Error; err != nil { return xerr.Custom("未找到冻精信息") } if frozenSemen.Quantity < req.FrozenSemenCount { return xerr.Custom("冻精数量不足") } // 更新复配的牛只 matingReMatchIds := make([]int64, 0) // 新建配种信息的牛只 newMatingList := make([]*model.EventMating, 0) // 更新配种信息牛只 updateMatingList := make([]int64, 0) // 所有牛只 cowIds := make([]int64, 0) // 需要更新配次的牛只 matingTimes := make([]*MatingTimes, 0) // 需要更新空怀的牛只 emptyCowIds := make([]int64, 0) for _, cow := range eventCheckModel.CowList { // 1. 第一次配种,创建配种信息(自然发情牛只,未经过同期的牛只) if cow.LastMatingAt <= 0 { newMatingList = append(newMatingList, model.NewEventMating2(cow, req, eventCheckModel.CurrentUser)) continue } eventMatingHistory, err := s.GetEventMatingIsExIstByCowId(ctx, cow) if err != nil { return xerr.WithStack(err) } // 2. 同期更新配种信息 牛号&& 胎次 && 未配 && 配种结果未知 ==> 同期初配 if eventMatingHistory.Status == pasturePb.IsShow_No && eventMatingHistory.MatingResult == pasturePb.MatingResult_Unknown { updateMatingList = append(updateMatingList, eventMatingHistory.Id) continue } lastMatingAt := time.Unix(cow.LastMatingAt, 0) currentMatingAt := time.Unix(int64(req.MatingAt), 0) daysBetween := util.DaysBetween(currentMatingAt.Unix(), lastMatingAt.Unix()) // 3. 如何两次配种时间为连续两天之内&&有过一次配种记录,则更新为复配状态 牛号&& 胎次 && 已配 && 配种结果未知 ==> 复配 if (currentMatingAt.Format(model.LayoutDate2) == lastMatingAt.Format(model.LayoutDate2) || daysBetween == 1) && eventMatingHistory.Status == pasturePb.IsShow_Ok && eventMatingHistory.MatingResult == pasturePb.MatingResult_Unknown { matingReMatchIds = append(matingReMatchIds, eventMatingHistory.Id) } else { matingTimes = append(matingTimes, &MatingTimes{ Mt: cow.MatingTimes + 1, CowId: cow.Id, EventMatingId: eventMatingHistory.Id, }) } // 4. 提交的配种数据中,如何定位出空怀的牛只, if (eventMatingHistory.MatingResult == pasturePb.MatingResult_Unknown || eventMatingHistory.MatingResult == pasturePb.MatingResult_ReMatch) && daysBetween >= 2 { emptyCowIds = append(emptyCowIds, eventMatingHistory.Id) newMatingList = append(newMatingList, model.NewEventMating2(cow, req, eventCheckModel.CurrentUser)) } cowIds = append(cowIds, cow.Id) } if len(cowIds) != len(updateMatingList)+len(matingReMatchIds)+len(newMatingList) { zaplog.Error("MatingCreate", zap.Any("cowIds", cowIds), zap.Any("updateMatingList", updateMatingList), zap.Any("newMatingList", newMatingList), zap.Any("matingReMatchIds", matingReMatchIds), zap.Any("req", req), ) return xerr.Custom("配种信息有误") } itemFrozenSemenLog := model.NewEventFrozenSemenLog(req) if err = s.DB.Transaction(func(tx *gorm.DB) error { // 更新配种事件数据(初配) if len(updateMatingList) > 0 { if err = tx.Model(new(model.EventMating)). Where("id IN ?", updateMatingList). Where("status = ?", pasturePb.IsShow_No). Updates(map[string]interface{}{ "mating_result": pasturePb.MatingResult_Unknown, "status": pasturePb.IsShow_Ok, "reality_day": int64(req.MatingAt), "frozen_semen_number": req.FrozenSemenNumber, "operation_id": eventCheckModel.OperationUser.Id, "operation_name": eventCheckModel.OperationUser.Name, "message_id": eventCheckModel.CurrentUser.Id, "message_name": eventCheckModel.CurrentUser.Name, }).Error; err != nil { return xerr.WithStack(err) } } // 更新已配种的牛只为复配状态 if len(matingReMatchIds) > 0 { if err = tx.Model(new(model.EventMating)). Where("id IN ?", matingReMatchIds). Where("mating_result = ?", pasturePb.MatingResult_Unknown). Where("status = ?", pasturePb.IsShow_Ok). Update("mating_result", pasturePb.MatingResult_ReMatch). Error; err != nil { return xerr.WithStack(err) } } // 更新上次已配种的牛只为空怀 if len(emptyCowIds) > 0 { if err = tx.Model(new(model.EventMating)). Where("id IN ?", emptyCowIds). Update("mating_result", pasturePb.BreedStatus_Empty). Error; err != nil { return xerr.WithStack(err) } } // 创建配种事件数据 if len(newMatingList) > 0 { if err = tx.Create(newMatingList).Error; err != nil { return xerr.WithStack(err) } } // 创建冻精使用记录日志 if err = tx.Create(itemFrozenSemenLog).Error; err != nil { return xerr.WithStack(err) } // 如果有同期牛只,则修改为已结束状态 if err = tx.Model(new(model.CowSameTime)). Where("cow_id IN ?", cowIds). UpdateColumn("same_time_status", pasturePb.SameTimeStatus_End).Error; err != nil { return xerr.WithStack(err) } // 更新牛只的繁殖状态为配种 if err = tx.Model(new(model.Cow)). Where("id IN ?", cowIds). Where("admission_status = ?", pasturePb.AdmissionStatus_Admission). Updates(map[string]interface{}{ "breed_status": pasturePb.BreedStatus_Breeding, "last_bull_number": req.FrozenSemenNumber, "last_mating_at": int64(req.MatingAt), }).Error; err != nil { return xerr.WithStack(err) } // 减去精液的数量 if err = tx.Model(frozenSemen). Where("id = ?", frozenSemen.Id). Where("quantity > 0"). UpdateColumn("quantity", gorm.Expr("quantity - ?", req.FrozenSemenCount), ).Error; err != nil { return xerr.WithStack(err) } // 更新配次 if len(matingTimes) > 0 { for _, mt := range matingTimes { if err = tx.Model(new(model.EventMating)). Where("id = ?", mt.EventMatingId). Update("mating_times", mt.Mt).Error; err != nil { return xerr.WithStack(err) } if err = tx.Model(new(model.Cow)). Where("id = ?", mt.CowId). Where("admission_status = ?", pasturePb.AdmissionStatus_Admission). Where("breed_status = ?", pasturePb.BreedStatus_Breeding). Update("mating_times", mt.Mt).Error; err != nil { return xerr.WithStack(err) } } } return nil }); err != nil { return xerr.WithStack(err) } return nil } func (s *StoreEntry) WeaningBatch(ctx context.Context, req *pasturePb.EventWeaningBatchRequest) error { if len(req.Item) <= 0 { return nil } cowIds := make([]int64, 0) cowWeightMap := make(map[int64]float64) for _, item := range req.Item { 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) } currentUser, err := s.GetCurrentSystemUser(ctx) if err != nil { return xerr.WithStack(err) } if err = s.DB.Transaction(func(tx *gorm.DB) error { for _, v := range eventWeaningList { if err = tx.Model(new(model.EventWeaning)).Where("id = ?", v.Id).Updates(map[string]interface{}{ "status": pasturePb.IsShow_Ok, "reality_day": int64(req.WeaningAt), "operation_id": req.OperationId, "operation_name": operation.Name, "message_id": currentUser.Id, "message_name": currentUser.Name, "remarks": req.Remarks, "after_pen_id": req.PenId, }).Error; err != nil { return xerr.WithStack(err) } if err = tx.Model(new(model.Cow)).Where("id = ?", v.CowId).Updates(map[string]interface{}{ "pen_id": req.PenId, "current_weight": cowWeightMap[v.CowId], "weaning_at": req.WeaningAt, "last_weight_at": req.WeaningAt, }).Error; err != nil { return xerr.WithStack(err) } } return nil }); err != nil { return xerr.WithStack(err) } return nil }