| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333 | package crontabimport (	"fmt"	"kpt-pasture/model"	"time"	pasturePb "gitee.com/xuyiping_admin/go_proto/proto/go/backend/cow"	"gitee.com/xuyiping_admin/pkg/logger/zaplog"	"go.uber.org/zap"	"gitee.com/xuyiping_admin/pkg/xerr")const (	MaxRuminaAdJust = 20	XAdjust21       = 15	XAdjust42       = 10	RumtoHeat       = 0.5	MinCalvingAge   = 20	MinLact         = 0	NormalChangJust = 10	B48             = 48)func (e *Entry) UpdateCowEstrus() (err error) {	pastureList := e.FindPastureList()	if pastureList == nil || len(pastureList) == 0 {		return nil	}	for _, pasture := range pastureList {		if err = e.EntryCowEstrus(pasture.Id); err != nil {			zaplog.Error("EntryCrontab", zap.Any("PastureUpdateCowEstrus", err), zap.Any("pasture", pasture))		}		zaplog.Info("PastureUpdateCowEstrus-success", zap.Any("pasture", pasture.Id))	}	return nil}func (e *Entry) EntryCowEstrus(pastureId int64) (err error) {	xToday := &XToday{}	systemConfigureList, err := e.GetSystemNeckRingConfigure(pastureId)	for _, v := range systemConfigureList {		switch v.Name {		case model.ActiveLow:			xToday.ActiveLow = int32(v.Value)		case model.ActiveMiddle:			xToday.ActiveMiddle = int32(v.Value)		case model.ActiveHigh:			xToday.ActiveHigh = int32(v.Value)		}	}	if err = e.CowEstrusWarning(pastureId, xToday); err != nil {		zaplog.Error("EntryCowEstrus", zap.Any("CowEstrusWarning", err), zap.Any("xToday", xToday))	}	// 将3天前历史发情预警数据更新为已过期	if err = e.DB.Model(new(model.NeckRingEstrus)).		Where("first_time != ?", "").		Where("first_time <= ?", time.Now().AddDate(0, 0, -3).Format(model.LayoutTime)).		Update("check_result", pasturePb.CheckResult_Overdue).Error; err != nil {		zaplog.Error("EntryCowEstrus", zap.Any("UpdateEventEstrus", err))	}	return nil}// CowEstrusWarning 发情预警func (e *Entry) CowEstrusWarning(pastureId int64, xToday *XToday) (err error) {	nowTime := time.Now()	neckActiveHabitList := make([]*model.NeckActiveHabit, 0) // todo 需要考虑到数据量太大的情况	if err = e.DB.Model(new(model.NeckActiveHabit)).		Where("heat_date = ?", nowTime.Format(model.LayoutDate2)).		Where("pasture_id = ?", pastureId).		Where("filter_high > 0 AND change_filter > ?", model.DefaultChangeFilter).		Where("cow_id > ?", 0).		Where(e.DB.Where("calving_age >= ?", MinCalvingAge).Or("lact = ?", MinLact)). // 排除产后20天内的发情牛		Order("cow_id").		Find(&neckActiveHabitList).Error; err != nil {		return xerr.WithStack(err)	}	zaplog.Info("CowEstrusWarning", zap.Any("neckActiveHabitList", neckActiveHabitList))	neckActiveHabitMap := make(map[int64][]*model.NeckActiveHabit)	for _, habit := range neckActiveHabitList {		cft := calculateCFT(habit)		if cft < float32(xToday.ActiveLow-XAdjust21) {			continue		}		if neckActiveHabitMap[habit.CowId] == nil {			neckActiveHabitMap[habit.CowId] = make([]*model.NeckActiveHabit, 0)		}		neckActiveHabitMap[habit.CowId] = append(neckActiveHabitMap[habit.CowId], habit)	}	zaplog.Info("CowEstrusWarning", zap.Any("neckActiveHabitMap", neckActiveHabitMap))	neckRingEstrusList := make([]*model.NeckRingEstrus, 0)	for cowId, cowHabitList := range neckActiveHabitMap {		// 最近3天最大发情记录,小于该变化趋势的不再插入		before3Data := e.GetBeforeThreeDaysCowEstrus(cowId, nowTime.AddDate(0, 0, -2).Format(model.LayoutTime))		// 判断最近50天内是否存在发情记录(发情等级>=2),如果18~25天@xadjust21,如果36~50天@xadjust42		cowEstrus := e.GetTwoEstrus(pastureId, cowId, nowTime.AddDate(0, 0, -100).Format(model.LayoutTime), nowTime.AddDate(0, 0, -2).Format(model.LayoutTime))		activeDateTime, _ := time.Parse(model.LayoutTime, cowEstrus.ActiveDate)		if activeDateTime.Unix() >= nowTime.AddDate(0, 0, -25).Unix() && activeDateTime.Unix() <= nowTime.AddDate(0, 0, -18).Unix() {			cowEstrus.HadJust = XAdjust21		}		if activeDateTime.Unix() >= nowTime.AddDate(0, 0, -50).Unix() && activeDateTime.Unix() <= nowTime.AddDate(0, 0, -36).Unix() {			cowEstrus.HadJust = XAdjust42		}		maxCft := float32(0)		maxHigh := int32(0)		lastActiveDate := time.Time{}		for _, habit := range cowHabitList {			cft := calculateCFT(habit)			if cft > maxCft {				maxCft = cft			}			if habit.FilterHigh > maxHigh {				maxHigh = habit.FilterHigh			}			// 获取最新的 CreateTime			activeTimeParse, _ := time.Parse(model.LayoutTime, habit.ActiveTime)			if activeTimeParse.After(lastActiveDate) {				lastActiveDate = activeTimeParse			}		}		b48 := float64(0)		t3, _ := time.Parse(model.LayoutTime, before3Data.ActiveTime)		b48 = t3.Sub(lastActiveDate).Hours()		if (int32(maxCft) > before3Data.DayHigh || before3Data.CowId == 0 || b48 > B48) && int32(maxCft)+cowEstrus.HadJust > xToday.ActiveLow {			level := calculateActiveLevel(maxCft, cowEstrus, xToday)			cowInfo := e.FindCowInfoByCowId(cowId)			if cowInfo == nil {				zaplog.Error("CowEstrusWarning", zap.Any("FindCowInfoByCowId", cowId))				continue			}			isShow := pasturePb.IsShow_Ok			if cowInfo != nil && cowInfo.IsPregnant == pasturePb.IsShow_Ok && level == pasturePb.EstrusLevel_Low {				isShow = pasturePb.IsShow_No			}			dayHigh := int32(maxCft) + cowEstrus.HadJust			lastEstrusDate := cowEstrus.ActiveDate			checkResult := getResult(before3Data, maxCft, cowEstrus)			isPeak := pasturePb.IsShow_Ok			zaplog.Info("CowEstrusWarning",				zap.Any("level", level),				zap.Any("b48", b48),				zap.Any("checkResult", checkResult),				zap.Any("isShow", isShow),				zap.Any("isPeak", isPeak),				zap.Any("lastEstrusDate", lastEstrusDate),				zap.Any("activeDate", lastActiveDate),				zap.Any("dayHigh", dayHigh),				zap.Any("cft", maxCft),				zap.Any("before3Data", before3Data),				zap.Any("cowEstrus", cowEstrus),				zap.Any("cowInfo", cowInfo),				zap.Any("cowHabitList", cowHabitList),			)			newNeckRingEstrus := model.NewNeckRingEstrus(pastureId, cowInfo, level, checkResult, isShow)			newNeckRingEstrus.LastTime = lastEstrusDate			newNeckRingEstrus.ActiveTime = lastActiveDate.Format(model.LayoutTime)			newNeckRingEstrus.DayHigh = dayHigh			newNeckRingEstrus.MaxHigh = maxHigh			newNeckRingEstrus.IsPeak = isPeak			neckRingEstrusList = append(neckRingEstrusList, newNeckRingEstrus)		}	}	zaplog.Info("CowEstrusWarning", zap.Any("neckRingEstrusList", neckRingEstrusList))	if len(neckRingEstrusList) > 0 {		if err = e.DB.Model(new(model.NeckRingEstrus)).Create(neckRingEstrusList).Error; err != nil {			zaplog.Error("CowEstrusWarningNew", zap.Any("eventEstrusList", neckRingEstrusList), zap.Any("err", err))		}	}	// 更新牛只首次发情时间	e.UpdateEstrusFirstTime1(pastureId, nowTime)	e.UpdateEstrusIsPeak(pastureId, nowTime)	e.UpdateEstrusFirstTime2(pastureId, nowTime)	e.UpdateEstrusFirstTime3(pastureId, nowTime)	return nil}// UpdateEstrusFirstTime1 更新牛只首次发情时间func (e *Entry) UpdateEstrusFirstTime1(pastureId int64, xToday time.Time) {	neckRingEstrusList := e.FindNeckRingEstrusByFirstTimeEmpty(pastureId)	for _, v := range neckRingEstrusList {		cowEstrusStartData := e.FindCowEstrusFirstTime1(pastureId, v.CowId, xToday)		if cowEstrusStartData != nil && cowEstrusStartData.FirstTime != "" {			if err := e.DB.Model(new(model.NeckRingEstrus)).				Where("id = ?", v.Id).				Update("first_time", cowEstrusStartData.FirstTime).Error; err != nil {				zaplog.Error("UpdateEstrusFirstTime1",					zap.Any("v", v),					zap.Any("err", err),					zap.Any("cowEstrusStartData", cowEstrusStartData),				)			}		}	}}func (e *Entry) UpdateEstrusFirstTime2(pastureId int64, xToday time.Time) {	neckRingEstrusList := e.FindNeckRingEstrusByFirstTimeEmpty(pastureId)	for _, v := range neckRingEstrusList {		zaplog.Info("UpdateEstrusFirstTime2", zap.Any("v", v))	}}func (e *Entry) UpdateEstrusFirstTime3(pastureId int64, xToday time.Time) {	neckRingEstrusList := e.FindNeckRingEstrusByFirstTimeEmpty(pastureId)	for _, v := range neckRingEstrusList {		activeTime, _ := time.Parse(model.LayoutTime, v.ActiveTime)		if activeTime.After(xToday.AddDate(0, 0, -2)) {			if err := e.DB.Model(new(model.NeckRingEstrus)).				Where("id = ?", v.Id).				Update("first_time", v.ActiveTime).Error; err != nil {				zaplog.Error("UpdateEstrusFirstTime1", zap.Any("v", v), zap.Any("err", err))			}		}	}}func (e *Entry) UpdateEstrusIsPeak(pastureId int64, xToday time.Time) {	neckRingEstrusList := make([]*model.NeckRingEstrus, 0)	if err := e.DB.Model(new(model.NeckRingEstrus)).		Where("first_time != ?", "").		Where("is_peak = ?", pasturePb.IsShow_Ok).		Where("is_show = ?", pasturePb.IsShow_Ok).		Where("pasture_id = ?", pastureId).		Find(&neckRingEstrusList).Error; err != nil {		zaplog.Error("UpdateEstrusIsPeak", zap.Any("Find", err))	}	for _, estrus := range neckRingEstrusList {		activeTime, _ := time.Parse(model.LayoutTime, estrus.ActiveTime)		if activeTime.Before(xToday) || activeTime.After(xToday.AddDate(0, 0, 1)) {			continue		}		var exists bool		if err := e.DB.Model(new(model.NeckRingEstrus)).			Where("cow_id = ?", estrus.CowId).			Where("first_time != ?", "").			Where("active_time BETWEEN ? AND ?", xToday, xToday.AddDate(0, 0, 1)).			Where("active_time BETWEEN ? AND ?", estrus.FirstTime, activeTime.Add(-2*time.Hour)).			Select("1").			Limit(1).			Scan(&exists).Error; err != nil {			zaplog.Error("UpdateEstrusIsPeak", zap.Any("exists", err))			continue		}		if exists {			if err := e.DB.Model(estrus).				Update("is_peak", pasturePb.IsShow_No).Error; err != nil {				zaplog.Error("UpdateEstrusIsPeak", zap.Any("v", estrus), zap.Any("err", err))			}		}	}}// FindCowEstrusFirstTime1 查找牛只昨天是否有发情数据func (e *Entry) FindCowEstrusFirstTime1(pastureId, cowId int64, xToday time.Time) *EstrusStartData {	firstTimeEventEstrus := &EstrusStartData{}	if err := e.DB.Model(new(model.NeckRingEstrus)).		Select("cow_id,MIN(STR_TO_DATE(first_time, '%Y-%m-%d %H:%i:%s')) as first_time").		Where("active_time BETWEEN ? AND ?",			fmt.Sprintf("%s 00:00:00", xToday.AddDate(0, 0, -1).Format(model.LayoutDate2)),			fmt.Sprintf("%s 23:59:59", xToday.Format(model.LayoutDate2)),		).Where("pasture_id = ?", pastureId).		Where("cow_id = ?", cowId).		First(&firstTimeEventEstrus).Error; err != nil {		return nil	}	return firstTimeEventEstrus}// calculateCFT 计算cft值func calculateCFT(habit *model.NeckActiveHabit) (cft float32) {	if habit.ChangeAdjust >= 10 {		cft = (float32(habit.ChangeFilter) - float32(habit.ChangeAdjust) + 3) * float32(habit.FilterCorrect) / 100	} else {		cft = float32(habit.ChangeFilter) * float32(habit.FilterCorrect) / 100	}	switch {	case habit.RuminaFilter > MaxRuminaAdJust:		cft -= float32(5)	case habit.RuminaFilter > 0:		cft -= float32(habit.RuminaFilter) * 0.25	case habit.RuminaFilter < -MaxRuminaAdJust:		cft -= -MaxRuminaAdJust * RumtoHeat	default:		cft -= float32(habit.RuminaFilter) * RumtoHeat	}	return cft}// calculateActiveLevel 计算活动量等级func calculateActiveLevel(cft float32, cowEstrus *CowEstrus, xToday *XToday) pasturePb.EstrusLevel_Kind {	if int32(cft)+cowEstrus.HadJust < xToday.ActiveMiddle {		return pasturePb.EstrusLevel_Low	} else if int32(cft)+cowEstrus.HadJust >= xToday.ActiveHigh {		return pasturePb.EstrusLevel_Middle	} else {		return pasturePb.EstrusLevel_High	}}// getResult 根据b3数据计算结果func getResult(b3 *model.NeckRingEstrus, cft float32, cowEstrus *CowEstrus) pasturePb.CheckResult_Kind {	result := pasturePb.CheckResult_Pending	if b3.CheckResult == pasturePb.CheckResult_Correct {		result = pasturePb.CheckResult_Correct	}	if b3.CheckResult == pasturePb.CheckResult_Fail && b3.DayHigh > int32(cft)+cowEstrus.HadJust {		result = pasturePb.CheckResult_Fail	}	return result}
 |