package backend import ( "context" "errors" "fmt" "kpt-pasture/model" "kpt-pasture/util" "net/http" "strings" "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) { userModel, err := s.GetUserModel(ctx) if err != nil { return nil, xerr.WithStack(err) } 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())). Where("a.pasture_id = ?", userModel.AppPasture.Id).Where("a.status = ?", pasturePb.IsShow_No) 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, Msg: "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) (err error) { userModel, err := s.GetUserModel(ctx) if err != nil { return xerr.WithStack(err) } cow, err := s.GetCowInfoByCowId(ctx, userModel.AppPasture.Id, int64(req.CowId)) if err != nil { zaplog.Error("CalvingCreate", zap.Any("cow_id", req.CowId), zap.Any("err", err)) return xerr.Custom("请选择相关牛只") } if len(req.CalfItemList) != int(req.ChildNumber) { return xerr.Custom("犊牛信息与产子数不相符") } if cow.IsPregnant != pasturePb.IsShow_Ok || cow.BreedStatus != pasturePb.BreedStatus_Pregnant { return xerr.Custom("该母牛未配种") } operationUser, err := s.GetSystemUserById(ctx, int64(req.OperationId)) if err != nil { return xerr.Customf("获取操作人员信息失败: %s", err.Error()) } req.OperationName = operationUser.Name penMap := s.PenMap(ctx, userModel.AppPasture.Id) newEventCalving := &model.EventCalving{} if err = s.DB.Model(new(model.EventCalving)). Where("cow_id = ?", cow.Id). Where("lact = ?", cow.Lact). Where("status = ?", pasturePb.IsShow_No). First(newEventCalving).Error; err != nil { return xerr.Custom("该母牛信息不存在") } if err = s.DB.Transaction(func(tx *gorm.DB) error { // 更新产犊事件表 newEventCalving.EventUpdate(operationUser, userModel.SystemUser, req, cow) if err = tx.Model(new(model.EventCalving)). Select("operation_id", "operation_name", "message_id", "message_name", "reality_day", "day_age", "pregnancy_age", "bull_number", "calving_level", "child_number", "status", "is_inducing_childbirth", "pen_id", "dystocia_reason", "remarks"). Where("id = ?", newEventCalving.Id). Updates(newEventCalving).Error; err != nil { return xerr.WithStack(err) } for _, calf := range req.CalfItemList { // 犊牛信息 newCalvingCalf := model.NewEventCalvingCalf(userModel.AppPasture.Id, int64(req.CowId), newEventCalving.Id, int64(req.CalvingAt), penMap, calf) if err = tx.Create(newCalvingCalf).Error; err != nil { return xerr.WithStack(err) } if calf.IsLive == pasturePb.IsShow_No || calf.IsAdoption == pasturePb.IsShow_No { continue } newCalfCow := model.NewCalfCow(cow.NeckRingNumber, cow.LastBullNumber, newCalvingCalf) if err = tx.Create(newCalfCow).Error; err != nil { return xerr.WithStack(err) } if err = tx.Model(new(model.CalvingCalf)).Where("id = ?", newCalvingCalf.Id).Update("cow_id", newCalfCow.Id).Error; err != nil { return xerr.WithStack(err) } // 犊牛日志 cowLogs := s.SubmitEventLog(ctx, userModel.AppPasture.Id, newCalfCow, pasturePb.EventType_Birth, pasturePb.ExposeEstrusType_Invalid, calf) if err = tx.Table(cowLogs.TableName()).Create(cowLogs).Error; err != nil { return xerr.WithStack(err) } } // 更新母牛信息 cow.EventCalvingUpdate(int64(req.CalvingAt)) if err = tx.Model(cow). Select("calving_age", "mating_times", "pregnancy_age", "lact", "breed_status", "is_pregnant", "last_calving_at"). Where("id = ?", cow.Id). Updates(cow).Error; err != nil { return xerr.WithStack(err) } // 母牛事件日志 cowLogs := s.SubmitEventLog(ctx, userModel.AppPasture.Id, cow, pasturePb.EventType_Calving, pasturePb.ExposeEstrusType_Invalid, 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 } func (s *StoreEntry) SameTimeCreate(ctx context.Context, req *pasturePb.EventSameTime) (err error) { userModel, err := s.GetUserModel(ctx) if err != nil { return xerr.WithStack(err) } operationUser, err := s.GetSystemUserById(ctx, int64(req.OperationId)) if err != nil { return xerr.WithStack(err) } eventCowSameTime, err := s.GetEventCowSameTimeByCowId(ctx, userModel.AppPasture.Id, int64(req.CowId)) if err != nil { zaplog.Error("SameTimeCreate", zap.Any("err", err), zap.Any("req", req)) return xerr.Customf("异常数据") } drugs := &model.Drugs{} if drugs, err = s.GetDrugsById(ctx, userModel.AppPasture.Id, int64(req.DrugsId)); err != nil { zaplog.Error("SameTimeCreate", zap.Any("err", err), zap.Any("req", req)) return xerr.Customf("该药品不存在: %d", req.DrugsId) } req.DrugsName = drugs.Name eventCowSameTime.EventUpdate(drugs, req.Usage, req.Remarks, operationUser, userModel.SystemUser) defer func() { if err == nil { // 记录牛只事件日志 cow, _ := s.GetCowInfoByCowId(ctx, userModel.AppPasture.Id, eventCowSameTime.CowId) cowLogs := s.SubmitEventLog(ctx, userModel.AppPasture.Id, cow, pasturePb.EventType_Seme_Time, pasturePb.ExposeEstrusType_Same_Time, req) s.DB.Table(cowLogs.TableName()).Create(cowLogs) } }() if err = s.DB.Model(new(model.EventCowSameTime)). Select("status", "drugs_id", "unit", "usage", "remarks", "operation_id", "operation_name"). Where("id = ?", eventCowSameTime.Id). Updates(eventCowSameTime).Error; err != nil { return xerr.WithStack(err) } return nil } func (s *StoreEntry) SameTimeBatch(ctx context.Context, req *pasturePb.EventSameTimeBatch) (err error) { userModel, err := s.GetUserModel(ctx) if err != nil { return xerr.WithStack(err) } operationUser, err := s.GetSystemUserById(ctx, int64(req.OperationId)) if err != nil { return xerr.Customf("异常数据") } req.OperationName = operationUser.Name drugs := &model.Drugs{} if drugs, err = s.GetDrugsById(ctx, userModel.AppPasture.Id, int64(req.DrugsId)); err != nil { zaplog.Error("SameTimeBatch", zap.Any("err", err), zap.Any("req", req)) return xerr.Customf("该药物不存在: %d", req.DrugsId) } req.DrugsName = drugs.Name eventCowSameTimeList := make([]*model.EventCowSameTime, 0) for _, v := range req.CowIds { eventCowSameTime, err := s.GetEventCowSameTimeByCowId(ctx, userModel.AppPasture.Id, int64(v)) if err != nil { zaplog.Error("SameTimeCreate", zap.Any("err", err), zap.Any("req", req)) return xerr.WithStack(err) } eventCowSameTimeList = append(eventCowSameTimeList, eventCowSameTime) } defer func() { // 记录牛只事件日志 if err == nil { for _, v := range eventCowSameTimeList { cow, _ := s.GetCowInfoByCowId(ctx, userModel.AppPasture.Id, v.CowId) cowLogs := s.SubmitEventLog( ctx, userModel.AppPasture.Id, cow, pasturePb.EventType_Seme_Time, pasturePb.ExposeEstrusType_Same_Time, &pasturePb.EventSameTime{ Id: int32(v.Id), CowId: int32(v.CowId), SameTimeId: int32(v.SameTimeId), SameTimeType: v.SameTimeType, SameTimeTypeName: v.SameTimeName, DrugsId: int32(v.DrugsId), DrugsName: drugs.Name, Usage: req.Usage, OperationId: int32(operationUser.Id), OperationName: operationUser.Name, SameTimeAt: req.SameTimeAt, }) s.DB.Table(cowLogs.TableName()).Create(cowLogs) } } }() if err = s.DB.Transaction(func(tx *gorm.DB) error { for _, v := range eventCowSameTimeList { // 更新SameTime v.EventUpdate(drugs, req.Usage, req.Remarks, userModel.SystemUser, operationUser) if err = tx.Model(new(model.EventCowSameTime)). Select("status", "drugs_id", "unit", "usage", "remarks", "operation_id", "operation_name"). Where("id = ?", v.Id). Updates(v).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) { userModel, err := s.GetUserModel(ctx) if err != nil { return nil, xerr.WithStack(err) } cowSameTimeList := make([]*model.EventCowSameTime, 0) var count int64 = 0 pref := s.DB.Table(new(model.EventCowSameTime).TableName()).Where("pasture_id = ?", userModel.AppPasture.Id) 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, Msg: "ok", Data: &pasturePb.SameTimeData{ List: model.EventCowSameTimeSlice(cowSameTimeList).ToPB(sameTimeTypeMap), Total: int32(count), PageSize: pagination.PageSize, Page: pagination.Page, }, }, nil } func (s *StoreEntry) EstrusList(ctx context.Context, req *pasturePb.EstrusItemsRequest, pagination *pasturePb.PaginationModel) (*pasturePb.SearchEventEstrusResponse, error) { userModel, err := s.GetUserModel(ctx) if err != nil { return nil, xerr.WithStack(err) } estrusList := make([]*model.EventEstrus, 0) var count int64 = 0 pref := s.DB.Table(new(model.EventEstrus).TableName()). Where("is_show = ?", pasturePb.IsShow_Ok). Where("pasture_id = ?", userModel.AppPasture.Id). Where("expose_estrus_type = ?", pasturePb.ExposeEstrusType_Natural_Estrus) if len(req.CowIds) > 0 { cowIds := strings.Split(util.ArrayInt32ToStrings(req.CowIds, ","), ",") pref.Where("cow_id IN ?", cowIds) } if req.Level > 0 { pref.Where("level = ?", req.Level) } if len(req.PenIds) > 0 { penIds := strings.Split(util.ArrayInt32ToStrings(req.PenIds, ","), ",") pref.Where("pen_id IN ?", penIds) } 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.SearchEventEstrusResponse{ Code: http.StatusOK, Msg: "ok", Data: &pasturePb.SearchEventEstrusData{ List: model.EventEstrusSlice(estrusList).ToPB(), Total: int32(count), PageSize: pagination.PageSize, Page: pagination.Page, }, }, nil } func (s *StoreEntry) EstrusBatchMating(ctx context.Context, req *pasturePb.EventNaturalEstrusBatch) (err error) { userModel, err := s.GetUserModel(ctx) if err != nil { return xerr.WithStack(err) } eventEstrusList, eventMatingList, err := s.EstrusCheckDataCheck(ctx, userModel, req.Items) if err != nil { return xerr.WithStack(err) } if err = s.DB.Transaction(func(tx *gorm.DB) error { if len(eventMatingList) > 0 { if err = tx.Model(new(model.EventMating)).Create(eventMatingList).Error; err != nil { return xerr.WithStack(err) } } if len(eventEstrusList) > 0 { if err = tx.Model(new(model.EventEstrus)).Create(eventEstrusList).Error; err != nil { return xerr.WithStack(err) } } // 记录事件日志 for _, v := range eventEstrusList { cow, _ := s.GetCowInfoByCowId(ctx, userModel.AppPasture.Id, v.CowId) cowLogs := s.SubmitEventLog(ctx, userModel.AppPasture.Id, cow, pasturePb.EventType_Estrus, v.ExposeEstrusType, v) 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) 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 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.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 } // MatingCreate 牛只配种 func (s *StoreEntry) MatingCreate(ctx context.Context, req *pasturePb.EventMating) (err error) { userModel, err := s.GetUserModel(ctx) if err != nil { return xerr.WithStack(err) } eventMatingCheckModel, err := s.MatingCreateCheck(ctx, userModel.AppPasture.Id, req) if err != nil { return xerr.WithStack(err) } req.OperationName = eventMatingCheckModel.OperationUser.Name if err = s.DB.Transaction(func(tx *gorm.DB) error { for _, cow := range eventMatingCheckModel.CowList { // 获取牛只最近一次配种信息 lastEventMating, err := s.FindLastEventMatingByCowId(ctx, userModel.AppPasture.Id, cow.Id) if err != nil { // 1. 没有配种信息,(第一次参加配种的牛只,并且没有参与同期的牛只 ) if errors.Is(err, gorm.ErrRecordNotFound) { newMating := model.NewEventMatingNaturalEstrus(userModel.AppPasture.Id, cow, req, userModel.SystemUser) if err = tx.Create(newMating).Error; err != nil { return xerr.WithStack(err) } if err = s.MatingCowUpdate(ctx, userModel.AppPasture.Id, cow, req, 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", cow), zap.Any("UserModel", userModel)) return xerr.Customf("牛只配种数据异常: %d", cow.EarNumber) } // 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, eventMatingCheckModel.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(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) } // 先创建一条新的配种数据 newMating := model.NewEventMating(userModel.AppPasture.Id, cow, int64(req.MatingAt), req.ExposeEstrusType) newMating.EventUpdate(int64(req.MatingAt), req.FrozenSemenNumber, isReMating, eventMatingCheckModel.OperationUser, userModel.SystemUser) 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)) } } // 牛只基本中配种信息更新 if err = s.MatingCowUpdate(ctx, userModel.AppPasture.Id, cow, req, isReMating); err != nil { return xerr.WithStack(err) } } // 创建冻精使用记录日志 itemFrozenSemenLog := model.NewEventFrozenSemenLog(eventMatingCheckModel.FrozenSemen.PastureId, req) if err = tx.Create(itemFrozenSemenLog).Error; err != nil { return xerr.WithStack(err) } // 减去精液的数量 eventMatingCheckModel.FrozenSemen.EventQuantityUpdate(req.FrozenSemenCount) if err = tx.Model(eventMatingCheckModel.FrozenSemen). Select("quantity"). Where("id = ?", eventMatingCheckModel.FrozenSemen.Id). Updates(eventMatingCheckModel.FrozenSemen).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, cow *model.Cow, 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, pastureId, 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) { userModel, err := s.GetUserModel(ctx) if err != nil { return xerr.WithStack(err) } 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) } defer func() { if err != nil { for _, cowId := range cowIds { cow, _ := s.GetCowInfoByCowId(ctx, userModel.AppPasture.Id, cowId) cowLogs := s.SubmitEventLog(ctx, userModel.AppPasture.Id, cow, pasturePb.EventType_Weaning, pasturePb.ExposeEstrusType_Invalid, req) s.DB.Table(cowLogs.TableName()).Create(cowLogs) } } }() 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) } // 创建牛只的体重记录 newEventWeight := model.NewEventWeight( userModel.AppPasture.Id, cowInfo, userModel.SystemUser, &pasturePb.EventWeight{ WeightAt: req.WeaningAt, OperationId: req.OperationId, OperationName: req.OperationName, Remarks: req.Remarks, Weight: float32(cowWeightMap[cowInfo.Id]), Height: 0, }) if err = tx.Create(newEventWeight).Error; err != nil { return xerr.WithStack(err) } } return nil }); err != nil { return xerr.WithStack(err) } return nil }