| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954 | package crontabimport (	"fmt"	"kpt-pasture/model"	"kpt-pasture/util"	"math"	"sort"	"strings"	"time"	pasturePb "gitee.com/xuyiping_admin/go_proto/proto/go/backend/cow"	"gitee.com/xuyiping_admin/pkg/logger/zaplog"	"gitee.com/xuyiping_admin/pkg/xerr"	"go.uber.org/zap")const (	MinChangeFilter = -99	MinRuminaFilter = -99	MinChewFilter   = -99	MinChangeHigh   = -99	DefaultNb       = 30	DefaultScore    = 100)var (	defaultLimit       = int32(1000)	calculateIsRunning bool)// NeckRingOriginalMerge 把脖环数据合并成2个小时的func (e *Entry) NeckRingOriginalMerge() (err error) {	if ok := e.IsExistCrontabLog(NeckRingOriginal); !ok {		newTime := time.Now()		e.CreateCrontabLog(NeckRingOriginal)		// 原始数据删除15天前的		e.DB.Model(new(model.NeckRingOriginal)).			Where("created_at < ?", newTime.AddDate(0, 0, -7).Unix()).			Delete(new(model.NeckRingOriginal))		// 活动数据删除6个月前的数据		e.DB.Model(new(model.NeckActiveHabit)).			Where("created_at < ?", newTime.AddDate(0, -2, 0).Unix()).			Delete(new(model.NeckActiveHabit))	}	pastureList := e.FindPastureList()	if pastureList == nil || len(pastureList) == 0 {		return nil	}	for _, pasture := range pastureList {		if err = e.OriginalMergeData(pasture.Id); err != nil {			zaplog.Error("NeckRingOriginalMerge", zap.Any("OriginalMergeData", err), zap.Any("pasture", pasture))		}	}	return nil}func (e *Entry) OriginalMergeData(pastureId int64) error {	limit := e.Cfg.NeckRingLimit	if limit <= 0 {		limit = defaultLimit	}	neckRingList := make([]*model.NeckRingOriginal, 0)	if err := e.DB.Model(new(model.NeckRingOriginal)).		Where("is_show = ?", pasturePb.IsShow_No).		Where("pasture_id = ?", pastureId).		Limit(int(limit)).Find(&neckRingList).Error; err != nil {		return xerr.WithStack(err)	}	if len(neckRingList) <= 0 {		return nil	}	// 去重	neckRingList = RemoveDuplicates(neckRingList)	// 计算合并	neckActiveHabitList := Recalculate(neckRingList)	if len(neckActiveHabitList) <= 0 {		return nil	}	for _, habit := range neckActiveHabitList {		//更新脖环牛只相关信息 新数据直接插入		historyNeckActiveHabit, ct := e.IsExistNeckActiveHabit(pastureId, habit.NeckRingNumber, habit.HeatDate, habit.Frameid)		if ct <= 0 {			if err := e.DB.Create(habit).Error; err != nil {				zaplog.Info("NeckRingOriginalMergeData-1",					zap.Any("err", err),					zap.Any("neckActiveHabit", habit),				)			}		} else {			// 重新计算			newNeckActiveHabit := e.againRecalculate(historyNeckActiveHabit)			if newNeckActiveHabit == nil {				continue			}			if err := e.DB.Model(new(model.NeckActiveHabit)).				Select("rumina", "intake", "inactive", "gasp", "other", "high", "active", "is_show", "record_count").				Where("id = ?", historyNeckActiveHabit.Id).				Updates(newNeckActiveHabit).Error; err != nil {				zaplog.Error("NeckRingOriginalMergeData-2",					zap.Any("err", err),					zap.Any("ct", ct),					zap.Any("historyNeckActiveHabit", historyNeckActiveHabit),					zap.Any("newNeckActiveHabit", newNeckActiveHabit),				)			}		}		if err := e.UpdateNeckRingOriginalIsShow(habit); err != nil {			zaplog.Error("NeckRingOriginalMergeData-4",				zap.Any("err", err),				zap.Any("neckActiveHabit", habit),			)		}	}	return nil}func (e *Entry) NeckRingCalculate() error {	pastureList := e.FindPastureList()	if pastureList == nil || len(pastureList) == 0 {		return nil	}	if calculateIsRunning {		return nil	}	defer func() {		calculateIsRunning = false	}()	calculateIsRunning = true	for _, pasture := range pastureList {		if err := e.EntryUpdateActiveHabit(pasture.Id); err != nil {			zaplog.Error("NeckRingCalculate", zap.Any("err", err), zap.Any("pasture", pasture))		}		zaplog.Info(fmt.Sprintf("NeckRingCalculate Success %d", pasture.Id))	}	return nil}func (e *Entry) EntryUpdateActiveHabit(pastureId int64) (err error) {	// 获取这段执行数据内最大日期和最小日期	xToday := &XToday{}	systemConfigureList, err := e.GetSystemNeckRingConfigure(pastureId)	if err != nil {		return xerr.WithStack(err)	}	for _, v := range systemConfigureList {		switch v.Name {		case model.MaxHabit:			xToday.LastMaxHabitId = v.Value		case model.High:			xToday.High = int32(v.Value)		case model.Rumina:			xToday.Rumina = int32(v.Value)		case model.XRuminaDisc:			xToday.XRuminaDisc = int32(v.Value)		case model.XChangeDiscount:			xToday.XChangeDiscount = int32(v.Value)		case model.WeeklyActive:			xToday.WeeklyActive = int32(v.Value)		}	}	currMaxHabit := &model.NeckActiveHabit{}	if err = e.DB.Model(new(model.NeckActiveHabit)).		Where("id > ?", xToday.LastMaxHabitId).		Where("pasture_id = ?", pastureId).		Where("is_show = ?", pasturePb.IsShow_No).		Order("id desc").		First(currMaxHabit).Error; err != nil {		return xerr.WithStack(err)	}	if currMaxHabit.Id <= 0 || currMaxHabit.Id <= xToday.LastMaxHabitId {		return nil	}	xToday.CurrMaxHabitId = currMaxHabit.Id	var processIds []int64	// 更新活动滤波	processIds, err = e.FirstFilterUpdate(pastureId, xToday)	if err != nil {		zaplog.Error("NeckRingCalculate", zap.Any("FirstFilterUpdate", err), zap.Any("xToday", xToday))	}	zaplog.Info("NeckRingCalculate", zap.Any("xToday", xToday), zap.Any("processIds", processIds))	if len(processIds) <= 0 {		return nil	}	if err = e.WeeklyUpdateActiveHabit(pastureId, processIds, xToday); err != nil {		zaplog.Error("NeckRingCalculate", zap.Any("WeeklyUpdateActiveHabit", err), zap.Any("xToday", xToday))	}	if err = e.Before3DaysNeckActiveHabit(pastureId, processIds, xToday); err != nil {		zaplog.Error("NeckRingCalculate", zap.Any("Before3DaysNeckActiveHabit", err), zap.Any("xToday", xToday))	}	// 二次更新滤波	if err = e.SecondUpdateChangeFilter(pastureId, xToday); err != nil {		zaplog.Error("NeckRingCalculate", zap.Any("SecondUpdateChangeFilter", err), zap.Any("xToday", xToday))	}	// 活动量校正系数和健康评分	if err = e.FilterCorrectAndScoreUpdate(pastureId, xToday); err != nil {		zaplog.Error("NeckRingCalculate", zap.Any("ActivityVolumeChanges", err), zap.Any("xToday", xToday))	}	if err = e.DB.Model(new(model.NeckActiveHabit)).		Where("id IN (?)", processIds).		Where("pasture_id = ?", pastureId).		Where("is_show = ?", pasturePb.IsShow_No).		Where("change_filter = ?", model.InitChangeFilter).		Updates(map[string]interface{}{			"change_filter": model.DefaultChangeFilter,			"rumina_filter": model.DefaultRuminaFilter,			"chew_filter":   model.DefaultChewFilter,		}).Error; err != nil {		zaplog.Error("EntryUpdateActiveHabit", zap.Any("change_filter", err), zap.Any("xToday", xToday))	}	if err = e.DB.Model(new(model.NeckActiveHabit)).		Where("id IN (?)", processIds).		Where("pasture_id = ?", pastureId).		Where("change_filter < ?", 0).		Where("filter_correct < ?", model.DefaultFilterCorrect).		Updates(map[string]interface{}{			"filter_correct": model.DefaultFilterCorrect,		}).Error; err != nil {		zaplog.Error("EntryUpdateActiveHabit", zap.Any("filter_correct", err), zap.Any("xToday", xToday))	}	// 插入群体校正表	if err = e.UpdateChangeAdJust(pastureId, xToday); err != nil {		zaplog.Error("EntryUpdateActiveHabit", zap.Any("UpdateChangeAdJust", err), zap.Any("xToday", xToday))	}	// 更新所有的显示状态为否的记录为是	if err = e.DB.Model(new(model.NeckActiveHabit)).		Where("id IN (?)", processIds).		Where("pasture_id = ?", pastureId).		Update("is_show", pasturePb.IsShow_Ok).Error; err != nil {		zaplog.Error("UpdateChangeAdJust-2", zap.Any("error", err), zap.Any("xToday", xToday))	}	// 更新日志	if err = e.DB.Model(new(model.NeckRingConfigure)).		Where("name = ?", model.MaxHabit).		Where("pasture_id = ?", pastureId).		Update("value", processIds[len(processIds)-1]).Error; err != nil {		zaplog.Error("NeckRingCalculate", zap.Any("MaxHabit", err), zap.Any("xToday", xToday))	}	// 健康预警	if err = e.HealthWarning(pastureId, processIds); err != nil {		zaplog.Error("EntryUpdateActiveHabit", zap.Any("HealthWarning", err))	}	return nil}// FirstFilterUpdate 首次更新活动滤波func (e *Entry) FirstFilterUpdate(pastureId int64, xToDay *XToday) (processIds []int64, err error) {	newNeckActiveHabitList := make([]*model.NeckActiveHabit, 0)	if err = e.DB.Model(new(model.NeckActiveHabit)).		Where("id > ?", xToDay.LastMaxHabitId).		Where("pasture_id = ?", pastureId).		Where("is_show = ?", pasturePb.IsShow_No).		//Where("record_count = ?", model.DefaultRecordCount).		Where(e.DB.Where("high >= ?", xToDay.High).Or("rumina >= ?", xToDay.Rumina)).		Order("heat_date,neck_ring_number,frameid").		Limit(int(defaultLimit)).		Find(&newNeckActiveHabitList).Error; err != nil {		return nil, xerr.WithStack(err)	}	// 活动量滤波	for _, v := range newNeckActiveHabitList {		// 过滤牛只未绑定的脖环的数据		cowInfo := e.GetCowInfoByNeckRingNumber(v.PastureId, v.NeckRingNumber)		if cowInfo == nil || cowInfo.Id <= 0 {			if err = e.DB.Model(new(model.NeckActiveHabit)).				Where("id = ?", v.Id).				Where("pasture_id = ?", v.PastureId).				Update("is_show", pasturePb.IsShow_Ok).Error; err != nil {				zaplog.Error("EntryUpdateActiveHabit", zap.Any("error", err))			}			continue		}		frameId := v.Frameid		heatDate := v.HeatDate		if v.Frameid == 0 {			frameId = 11			heatDateParse, _ := time.Parse(model.LayoutDate2, heatDate)			heatDate = heatDateParse.AddDate(0, 0, -1).Format(model.LayoutDate2)		} else {			frameId -= 1		}		firstFilterData := e.FindFirstFilter(pastureId, v.NeckRingNumber, heatDate, frameId)		if v.FilterHigh > 0 {			firstFilterData.FilterHigh = v.FilterHigh		} else {			if v.NeckRingNumber == firstFilterData.NeckRingNumber {				firstFilterData.FilterHigh = int32(computeIfPositiveElse(float64(v.High), float64(firstFilterData.FilterHigh), 0.23, 0.77))			} else {				firstFilterData.FilterHigh = v.High			}		}		if v.FilterRumina > 0 {			firstFilterData.FilterRumina = v.FilterRumina		} else {			if v.NeckRingNumber == firstFilterData.NeckRingNumber {				firstFilterData.FilterRumina = int32(computeIfPositiveElse(float64(v.Rumina), float64(firstFilterData.FilterRumina), 0.33, 0.67))			} else {				firstFilterData.FilterRumina = v.Rumina			}		}		if v.FilterChew > 0 {			firstFilterData.FilterChew = v.FilterChew		} else {			if v.NeckRingNumber == firstFilterData.NeckRingNumber {				firstFilterData.FilterChew = int32(computeIfPositiveElse(float64(v.Rumina+v.Intake), float64(firstFilterData.FilterChew), 0.33, 0.67))			} else {				firstFilterData.FilterChew = v.Rumina + v.Intake			}		}		processIds = append(processIds, v.Id)		// 更新过滤值 // todo 记得更新胎次为牛只胎次,现在为了测试特意改成0		if err = e.DB.Model(new(model.NeckActiveHabit)).			Select("filter_high", "filter_rumina", "filter_chew", "cow_id", "lact", "calving_age", "ear_number").			Where("id = ?", v.Id).			Updates(map[string]interface{}{				"filter_high":   firstFilterData.FilterHigh,				"filter_rumina": firstFilterData.FilterRumina,				"filter_chew":   firstFilterData.FilterChew,				"cow_id":        cowInfo.Id,				"lact":          0,				"calving_age":   cowInfo.CalvingAge,				"ear_number":    cowInfo.EarNumber,			}).Error; err != nil {			zaplog.Error("FirstFilterUpdate",				zap.Any("error", err),				zap.Any("firstFilterData", firstFilterData),				zap.Any("NeckActiveHabit", v),				zap.Any("cowInfo", cowInfo),				zap.Any("xToday", xToDay),			)		}	}	return processIds, nil}// WeeklyUpdateActiveHabit  时间点周平均值计算func (e *Entry) WeeklyUpdateActiveHabit(pastureId int64, processIds []int64, xToDay *XToday) (err error) {	newNeckActiveHabitList := make([]*model.NeckActiveHabit, 0)	if err = e.DB.Model(new(model.NeckActiveHabit)).		Where("id IN (?)", processIds).		Order("heat_date,neck_ring_number,frameid").		Find(&newNeckActiveHabitList).Error; err != nil {		return xerr.WithStack(err)	}	for _, v := range newNeckActiveHabitList {		// 前七天的		weekHabitData := e.FindWeekHabitData(pastureId, v.NeckRingNumber, v.HeatDate, v.Frameid, xToDay)		// 更新过滤值		if err = e.DB.Model(new(model.NeckActiveHabit)).			Select("week_high_habit", "week_rumina_habit", "week_chew_habit", "week_intake_habit", "week_inactive_habit").			Where("id = ?", v.Id).			Updates(map[string]interface{}{				"week_high_habit":     weekHabitData.WeekHighHabit,				"week_rumina_habit":   weekHabitData.WeekRuminaHabit,				"week_chew_habit":     weekHabitData.WeekChewHabit,				"week_intake_habit":   weekHabitData.WeekIntakeHabit,				"week_inactive_habit": weekHabitData.WeekInactiveHabit,			}).Error; err != nil {			zaplog.Error("WeeklyUpdateActiveHabit",				zap.Error(err),				zap.Any("NeckActiveHabit", v),				zap.Any("pastureId", pastureId),			)		}	}	if err = e.SumUpdateActiveHabit(pastureId, newNeckActiveHabitList, xToDay); err != nil {		zaplog.Error("WeeklyUpdateActiveHabit",			zap.Any("SumUpdateActiveHabit", err),			zap.Any("newNeckActiveHabitList", newNeckActiveHabitList),			zap.Any("pastureId", pastureId),		)	}	if err = e.ActiveChange(pastureId, processIds, xToDay); err != nil {		zaplog.Error("WeeklyUpdateActiveHabit",			zap.Any("ActiveChange", err),			zap.Any("processIds", processIds),			zap.Any("xToDay", xToDay),			zap.Any("pastureId", pastureId),		)	}	return nil}// SumUpdateActiveHabit -- 累计24小时数值func (e *Entry) SumUpdateActiveHabit(pastureId int64, newNeckActiveHabitList []*model.NeckActiveHabit, xToDay *XToday) (err error) {	for _, v := range newNeckActiveHabitList {		sumHabitData := e.FindSumHabitData(pastureId, v.NeckRingNumber, v.HeatDate, v.Frameid, xToDay)		// 更新过滤值		if err = e.DB.Model(new(model.NeckActiveHabit)).			Select("sum_rumina", "sum_intake", "sum_inactive", "sum_active", "sum_max_high", "sum_min_high", "sum_min_chew").			Where("id = ?", v.Id).			Updates(map[string]interface{}{				"sum_rumina":   sumHabitData.SumRumina,				"sum_intake":   sumHabitData.SumIntake,				"sum_inactive": sumHabitData.SumInactive,				"sum_active":   sumHabitData.SumActive,				"sum_max_high": sumHabitData.SumMaxHigh,				"sum_min_high": sumHabitData.SumMinHigh,				"sum_min_chew": sumHabitData.SumMinChew,			}).Error; err != nil {			zaplog.Error("WeeklyUpdateActiveHabit",				zap.Any("err", err),				zap.Any("NeckActiveHabit", v),				zap.Any("pastureId", pastureId),			)		}	}	return err}// ActiveChange -- 变化百分比func (e *Entry) ActiveChange(pastureId int64, processIds []int64, xToDay *XToday) (err error) {	newNeckActiveHabitList := make([]*model.NeckActiveHabit, 0)	if err = e.DB.Model(new(model.NeckActiveHabit)).		Where("id IN (?)", processIds).		Where("week_high_habit > ?", 0).		Where(e.DB.Where("high >= ?", xToDay.High).Or("rumina >= ?", xToDay.Rumina)).		Find(&newNeckActiveHabitList).Error; err != nil {		return xerr.WithStack(err)	}	for _, v := range newNeckActiveHabitList {		highDiff := v.FilterHigh - v.WeekHighHabit		denominator := float64(v.WeekHigh)*0.6 + float64(v.WeekHighHabit)*0.2 + float64(xToDay.WeeklyActive)*0.2		if highDiff > 0 {			v.ChangeHigh = int32(math.Round((float64(highDiff) / denominator / float64(v.WeekHighHabit)) * 100))		} else {			v.ChangeHigh = int32(math.Round(float64(highDiff) / float64(v.WeekHighHabit) * 100))		}		if v.WeekRuminaHabit != 0 {			v.ChangeRumina = int32(math.Round(float64(v.FilterRumina-v.WeekRuminaHabit) / float64(v.WeekRuminaHabit) * 100))		}		if v.WeekChewHabit != 0 {			v.ChangeChew = int32(math.Round(float64(v.FilterChew-v.WeekChewHabit) / float64(v.WeekChewHabit) * 100))		}		// 更新过滤值		if err = e.DB.Model(new(model.NeckActiveHabit)).			Select("change_high", "change_rumina", "change_chew").			Where("id = ?", v.Id).			Updates(map[string]interface{}{				"change_high":   v.ChangeHigh,				"change_rumina": v.ChangeRumina,				"change_chew":   v.ChangeChew,			}).Error; err != nil {			zaplog.Error("WeeklyUpdateActiveHabit",				zap.Error(err),				zap.Any("NeckActiveHabit", v),				zap.Any("pastureId", pastureId),			)		}	}	return err}func (e *Entry) WeeklyUpdateActiveHabitOld(pastureId int64, newNeckActiveHabitList []*model.NeckActiveHabit, xToDay *XToday) (err error) {	for _, v := range newNeckActiveHabitList {		// 前七天的		weekHabitData := e.FindWeekHabitData(pastureId, v.NeckRingNumber, v.HeatDate, v.Frameid, xToDay)		highDiff := v.FilterHigh - weekHabitData.WeekHighHabit		denominator := float64(v.WeekHigh)*0.6 + float64(weekHabitData.WeekHighHabit)*0.2 + float64(xToDay.WeeklyActive)*0.2		if highDiff > 0 {			v.ChangeHigh = int32(math.Round((float64(highDiff) / denominator) * 100))		} else {			v.ChangeHigh = int32(math.Round(float64(highDiff) / denominator * 100))		}		if weekHabitData.WeekRuminaHabit != 0 {			v.ChangeRumina = int32(math.Round(float64(v.FilterRumina-weekHabitData.WeekRuminaHabit) / float64(weekHabitData.WeekRuminaHabit) * 100))		} else {			v.ChangeRumina = 0		}		if weekHabitData.WeekChewHabit != 0 {			v.ChangeChew = int32(math.Round(float64(v.FilterChew-weekHabitData.WeekChewHabit) / float64(weekHabitData.WeekChewHabit) * 100))		} else {			v.ChangeChew = 0		}		sumHabitData := e.FindSumHabitData(pastureId, v.NeckRingNumber, v.HeatDate, v.Frameid, xToDay)		// 更新过滤值		if err = e.DB.Model(new(model.NeckActiveHabit)).			Select(				"week_high_habit", "week_rumina_habit", "week_chew_habit", "week_intake_habit", "week_inactive_habit",				"sum_rumina", "sum_intake", "sum_inactive", "sum_active", "sum_max_high", "sum_min_high", "sum_min_chew",				"change_high", "change_rumina", "change_chew", "before_three_sum_rumina", "before_three_sum_intake",			).Where("id = ?", v.Id).			Updates(map[string]interface{}{				"week_high_habit":     weekHabitData.WeekHighHabit,				"week_rumina_habit":   weekHabitData.WeekRuminaHabit,				"week_chew_habit":     weekHabitData.WeekChewHabit,				"week_intake_habit":   weekHabitData.WeekIntakeHabit,				"week_inactive_habit": weekHabitData.WeekInactiveHabit,				"sum_rumina":          sumHabitData.SumRumina,				"sum_intake":          sumHabitData.SumIntake,				"sum_inactive":        sumHabitData.SumInactive,				"sum_active":          sumHabitData.SumActive,				"sum_max_high":        sumHabitData.SumMaxHigh,				"sum_min_high":        sumHabitData.SumMinHigh,				"sum_min_chew":        sumHabitData.SumMinChew,				"change_high":         v.ChangeHigh,				"change_rumina":       v.ChangeRumina,				"change_chew":         v.ChangeChew,			}).Error; err != nil {			zaplog.Error("WeeklyUpdateActiveHabit",				zap.Error(err),				zap.Any("NeckActiveHabit", v),				zap.Any("pastureId", pastureId),			)		}	}	return err}func (e *Entry) Before3DaysNeckActiveHabit(pastureId int64, processIds []int64, xToDay *XToday) (err error) {	newNeckActiveHabitList := make([]*model.NeckActiveHabit, 0)	if err = e.DB.Model(new(model.NeckActiveHabit)).		Where("id IN (?)", processIds).		Order("heat_date,neck_ring_number,frameid").		Find(&newNeckActiveHabitList).Error; err != nil {		return xerr.WithStack(err)	}	for _, v := range newNeckActiveHabitList {		before3DaysNeckActiveHabit := e.FindBefore3DaysNeckActiveHabit(pastureId, v.NeckRingNumber, v.HeatDate, v.Frameid)		// 更新过滤值		if err = e.DB.Model(new(model.NeckActiveHabit)).			Select("before_three_sum_rumina", "before_three_sum_intake").			Where("id = ?", v.Id).			Updates(map[string]interface{}{				"before_three_sum_rumina": before3DaysNeckActiveHabit.SumRumina,				"before_three_sum_intake": before3DaysNeckActiveHabit.SumIntake,			}).Error; err != nil {			zaplog.Error("Before3DaysNeckActiveHabit",				zap.Error(err),				zap.Any("NeckActiveHabit", v),				zap.Any("pastureId", pastureId),			)		}	}	return nil}// SecondUpdateChangeFilter 第二次更新变化趋势滤波func (e *Entry) SecondUpdateChangeFilter(pastureId int64, xToday *XToday) (err error) {	newChangeFilterList := make([]*ChangeFilterData, 0)	if err = e.DB.Model(new(model.NeckActiveHabit)).		Select("id", "neck_ring_number", "change_high", "change_filter", "rumina_filter", "change_rumina",			"chew_filter", "change_chew", "heat_date", "frameid", "IF(lact = 0, 0.8, 1) as xlc_dis_count").		Where("id > ?", xToday.LastMaxHabitId).		Where("heat_date >= ?", time.Now().AddDate(0, 0, -2).Format(model.LayoutDate2)).		Where("pasture_id = ?", pastureId).		Where("change_filter = ?", model.InitChangeFilter).		Where("change_high > ?", MinChangeHigh).		Order("neck_ring_number,heat_date,frameid").		Limit(int(defaultLimit)).		Find(&newChangeFilterList).Error; err != nil {		return xerr.WithStack(err)	}	for _, v := range newChangeFilterList {		secondFilterData := &SecondFilterData{}		frameId := v.FrameId		heatDate := v.HeatDate		if v.FrameId == 0 {			frameId = 11			heatDateParse, _ := time.Parse(model.LayoutDate2, heatDate)			heatDate = heatDateParse.AddDate(0, 0, -1).Format(model.LayoutDate2)		}		if err = e.DB.Model(new(model.NeckActiveHabit)).			Select("neck_ring_number", "filter_high", "filter_rumina", "filter_chew").			Where("neck_ring_number = ?", v.NeckRingNumber).			Where("heat_date = ?", heatDate).			Where("frameid = ?", frameId).			First(&secondFilterData).Error; err != nil {			zaplog.Error("EntryUpdateActiveHabit", zap.Any("FirstFilterUpdate", err))		}		if v.ChangeFilter > MinChangeFilter {			secondFilterData.ChangeFilter = float64(v.ChangeFilter)		} else {			if v.NeckRingNumber == secondFilterData.NeckRingNumber {				secondFilterData.ChangeFilter = secondFilterData.ChangeFilter*(1-(float64(xToday.XChangeDiscount)/10)*v.XlcDisCount) +					math.Min(float64(v.ChangeHigh), secondFilterData.ChangeFilter+135)*(float64(xToday.XChangeDiscount)/10)*v.XlcDisCount			} else {				secondFilterData.ChangeFilter = 0			}		}		if v.RuminaFilter > MinRuminaFilter {			secondFilterData.RuminaFilter = float64(v.ChangeFilter)		} else {			if v.NeckRingNumber == secondFilterData.NeckRingNumber {				discount := float64(xToday.XRuminaDisc) / 10 * v.XlcDisCount				if math.Abs(float64(v.ChangeRumina)) > 60 {					discount *= 0.5				}				secondFilterData.RuminaFilter = secondFilterData.RuminaFilter*(1-discount) + float64(v.ChangeRumina)*discount			} else {				secondFilterData.RuminaFilter = 0			}		}		secondFilterData.RuminaFilter = math.Min(50, secondFilterData.RuminaFilter)		if v.ChewFilter > MinChewFilter {			secondFilterData.ChewFilter = float64(v.ChangeChew)		} else {			if v.NeckRingNumber == secondFilterData.NeckRingNumber {				discount := float64(xToday.XRuminaDisc) / 10				if math.Abs(float64(v.ChangeChew)) > 60 {					discount *= 0.5				}				secondFilterData.ChewFilter = secondFilterData.ChewFilter*(1-discount) + float64(v.ChangeChew)*discount			} else {				secondFilterData.ChewFilter = 0			}		}		secondFilterData.ChewFilter = math.Min(50, secondFilterData.ChewFilter)		if err = e.DB.Model(new(model.NeckActiveHabit)).			Select("change_filter", "rumina_filter", "chew_filter").			Where("id = ?", v.Id).			Updates(map[string]interface{}{				"change_filter": secondFilterData.ChangeFilter,				"rumina_filter": secondFilterData.RuminaFilter,				"chew_filter":   secondFilterData.ChewFilter,			}).Error; err != nil {			zaplog.Error("SecondUpdateChangeFilter-1", zap.Any("error", err), zap.Any("xToday", xToday))		}	}	return nil}// FilterCorrectAndScoreUpdate 计算活动量变化趋势校正值(活跃度校正)和健康评分func (e *Entry) FilterCorrectAndScoreUpdate(pastureId int64, xToday *XToday) error {	beginDayDate := time.Now()	before7DayDate := beginDayDate.AddDate(0, 0, -7).Format(model.LayoutDate2)	before1DayDate := beginDayDate.AddDate(0, 0, -1).Format(model.LayoutDate2)	activityVolumeList := make([]*ActivityVolume, 0)	activityVolumeMap := make(map[string]*ActivityVolume)	if err := e.DB.Model(new(model.NeckActiveHabit)).		Select("neck_ring_number", "AVG(IF(change_filter>=60, 60, change_filter)) as avg_filter",			"ROUND(STD(IF(change_filter>=60, 60, change_filter))) as std_filter", "COUNT(1) as nb").		Where("heat_date BETWEEN ? AND ?", before7DayDate, before1DayDate).		Where("pasture_id = ?", pastureId).		Where(e.DB.Where("high > ?", xToday.High).Or("rumina >= ?", xToday.Rumina)).		Where("active_time <= ?", beginDayDate.Add(-12*time.Hour).Format(model.LayoutTime)).		Where("change_filter > ?", MinChangeFilter).		Having("nb > ?", DefaultNb).		Group("neck_ring_number").		Find(&activityVolumeList).Error; err != nil {		zaplog.Error("ActivityVolumeChanges-0", zap.Any("error", err), zap.Any("xToday", xToday))	}	if len(activityVolumeList) > 0 {		for _, v := range activityVolumeList {			activityVolumeMap[v.NeckRingNumber] = v		}	}	neckActiveHabitList := make([]*model.NeckActiveHabit, 0)	if err := e.DB.Model(new(model.NeckActiveHabit)).		Where("id <= ?", xToday.CurrMaxHabitId).		Where("heat_date >= ?", before1DayDate).		Where("pasture_id = ?", pastureId).		Where(e.DB.Where("high > ?", xToday.High).Or("rumina > ?", xToday.Rumina)).		Find(&neckActiveHabitList).Error; err != nil {		zaplog.Error("ActivityVolumeChanges-1", zap.Any("error", err), zap.Any("xToday", xToday))		return xerr.WithStack(err)	}	for _, v := range neckActiveHabitList {		if filterCorrectMap, ok := activityVolumeMap[v.NeckRingNumber]; ok {			filterCorrect := model.DefaultFilterCorrect - int(math.Floor(filterCorrectMap.AvgFilter/3+float64(filterCorrectMap.StdFilter)/2))			// 活动量校正系数			if err := e.DB.Model(new(model.NeckActiveHabit)).				Where("id = ?", v.Id).				Where("neck_ring_number = ?", v.NeckRingNumber).				Update("filter_correct", filterCorrect).Error; err != nil {				zaplog.Error("ActivityVolumeChanges-2", zap.Any("error", err), zap.Any("xToday", xToday))				continue			}		}		cowScore := calculateScore(v)		if err := e.DB.Model(new(model.NeckActiveHabit)).			Where("id = ?", v.Id).			Update("score", cowScore).Error; err != nil {			zaplog.Error("ActivityVolumeChanges-2", zap.Any("error", err), zap.Any("xToday", xToday))			continue		}	}	return nil}// UpdateChangeAdJust 更新群体校正数据func (e *Entry) UpdateChangeAdJust(pastureId int64, xToday *XToday) error {	/*-- 插入群体校正表	INSERT INTO data_bar_change(heatdate, frameid, intCurBar, intCurBarName, nb, highchange, changefilter)	SELECT h.heatdate, h.frameid, c.intCurBar, c.intCurBarName, COUNT(*) nb, ROUND(AVG(h.highchange)) highchange, ROUND(AVG(h.changefilter) ) changefilter	FROM h_activehabit h JOIN cow c ON h.intCowId=c.intCowId	WHERE h.heatdate>=(CURDATE() -INTERVAL 1 DAY )	GROUP BY h.heatdate, h.frameid, c.intCurBar	ORDER BY h.heatdate, h.frameid, c.intCurBarName	ON DUPLICATE KEY UPDATE nb = VALUES(nb), highchange = VALUES(highchange), changefilter = VALUES(changefilter);	UPDATE h_activehabit h	JOIN cow c ON h.intCowId=c.intCowId	JOIN data_bar_change cg ON h.heatdate=cg.heatdate AND h.frameid=cg.frameid AND c.intCurBar=cg.intCurBar	SET h.changeadjust=cg.changefilter	WHERE h.id>xBeg_update_act_Id AND h.heatdate>=CURRENT_DATE() - INTERVAL 1 DAY AND ABS(cg.changefilter)>=10;	*/	res := make([]*model.NeckRingBarChange, 0)	oneDayAgo := time.Now().AddDate(0, 0, -1).Format(model.LayoutDate2)	if err := e.DB.Table(fmt.Sprintf("%s as h", new(model.NeckActiveHabit).TableName())).		Select("h.neck_ring_number,h.heat_date, h.frameid, c.pen_id, c.pen_name, COUNT(*) as nb, ROUND(AVG(h.change_high)) as change_high, ROUND(AVG(h.change_filter)) as change_filter").		Joins("JOIN cow as c ON h.neck_ring_number = c.neck_ring_number").		Where("h.pasture_id = ?", pastureId).		Where("h.heat_date >= ?", oneDayAgo).		Where("h.cow_id >= ?", 0).		Where("h.cow_id >= ?", 0).		Group("h.heat_date, h.frameid, c.pen_id").		Order("h.heat_date, h.frameid, c.pen_name").		Find(&res).Error; err != nil {		return xerr.WithStack(err)	}	// todo ABS(cg.changefilter)>=10;	for _, v := range res {		if err := e.DB.Model(new(model.NeckActiveHabit)).			Where("id > ?", xToday.LastMaxHabitId).			Where("heat_date = ?", v.HeatDate).			Where("frameid = ?", v.FrameId).			Where("neck_ring_number = ?", v.NeckRingNumber).			Update("change_adjust", v.ChangeHigh).Error; err != nil {			zaplog.Error("UpdateChangeAdJust-1", zap.Any("error", err), zap.Any("xToday", xToday))		}	}	return nil}func (e *Entry) UpdateNeckRingOriginalIsShow(habit *model.NeckActiveHabit) error {	if err := e.DB.Model(new(model.NeckRingOriginal)).		Where("pasture_id = ?", habit.PastureId).		Where("neck_ring_number = ?", habit.NeckRingNumber).		Where("active_date = ?", habit.HeatDate).		Where("frameid IN (?)", util.FrameIds(habit.Frameid)).		Update("is_show", pasturePb.IsShow_Ok).Error; err != nil {		return xerr.WithStack(err)	}	return nil}// RemoveDuplicates 清洗一下数据,去掉重复的,如果有重复的,取最新的一条数据func RemoveDuplicates(records []*model.NeckRingOriginal) []*model.NeckRingOriginal {	uniqueRecords := make(map[string]*model.NeckRingOriginal)	// 遍历原始数组	for _, record := range records {		mapKey := fmt.Sprintf("%s%s%s%s%d", record.NeckRingNumber, model.JoinKey, record.ActiveDate, model.JoinKey, record.Frameid) // 0001/2023-12-04/0 0001/2023-12-03/4		if existing, exists := uniqueRecords[mapKey]; exists {			if record.CreatedAt > existing.CreatedAt {				uniqueRecords[mapKey] = record			}		} else {			uniqueRecords[mapKey] = record		}	}	// 将 map 中的记录转换为切片	result := make([]*model.NeckRingOriginal, 0, len(uniqueRecords))	for _, record := range uniqueRecords {		result = append(result, record)	}	return result}// Recalculate 合并计算func Recalculate(neckRingList []*model.NeckRingOriginal) []*model.NeckActiveHabit {	originalMapData := make(map[string]*model.NeckRingOriginalMerge)	// 合并成2个小时的	for _, v := range neckRingList {		xframeId := util.XFrameId(v.Frameid)		mapKey := fmt.Sprintf("%s%s%s%s%d", v.NeckRingNumber, model.JoinKey, v.ActiveDate, model.JoinKey, xframeId) // 0001/2023-12-04/0 0001/2023-12-03/4		if originalMapData[mapKey] == nil {			originalMapData[mapKey] = new(model.NeckRingOriginalMerge)		}		originalMapData[mapKey].IsMageData(v, xframeId)	}	currTime := time.Now()	res := make([]*model.NeckActiveHabit, 0)	// 算平均值	for k, v := range originalMapData {		// 过滤掉合并后<6条数据,如果时间太短就晚点再算		if v.RecordCount < model.DefaultRecordCount {			currMaxXframeId := util.FrameIdMapReverse[int32(currTime.Hour())]			activeDateString := fmt.Sprintf("%s %02d:00:00", v.ActiveDate, v.XframeId*2+1)			activeDate, _ := time.Parse(model.LayoutTime, activeDateString)			if currMaxXframeId-v.XframeId <= 1 && currTime.Add(-1*time.Hour).Unix() < activeDate.Unix() {				delete(originalMapData, k)				continue			}		}		v.SumAvg()	}	if len(originalMapData) <= 0 {		return res	}	res = model.NeckRingOriginalMap(originalMapData).ForMatData()	sort.Sort(model.NeckActiveHabitSlice(res))	return res}func (e *Entry) againRecalculate(data *model.NeckActiveHabit) *model.NeckActiveHabit {	originalList := make([]*model.NeckRingOriginal, 0)	frameIds := util.FrameIds(data.Frameid)	sql := ""	for _, frameId := range frameIds {		sql += fmt.Sprintf(`SELECT * FROM neck_ring_original WHERE pasture_id = %d AND neck_ring_number = '%s' AND  active_date = '%s' AND frameid = %d UNION ALL `, data.PastureId, data.NeckRingNumber, data.HeatDate, frameId)	}	if len(sql) > 0 {		sql = strings.TrimSuffix(sql, "UNION ALL ")	}	if err := e.DB.Raw(sql).Find(&originalList).Error; err != nil {		return nil	}	/*if err := e.DB.Model(new(model.NeckRingOriginal)).		Where("pasture_id = ?", data.PastureId).		Where("neck_ring_number = ?", data.NeckRingNumber).		Where("active_date = ?", data.HeatDate).		Where("frameid IN (?)", frameIds).		Find(&originalList).Error; err != nil {		return nil	}*/	originalList = RemoveDuplicates(originalList)	newDataList := Recalculate(originalList)	if len(newDataList) != 1 {		return nil	}	res := newDataList[0]	res.IsShow = pasturePb.IsShow_No	return res}// computeIfPositiveElse 辅助函数来计算过滤值func computeIfPositiveElse(newValue, prevFilterValue float64, weightPrev, weightNew float64) float64 {	return math.Ceil((prevFilterValue * weightPrev) + (weightNew * newValue))}// 计算 score 的逻辑func calculateScore(habit *model.NeckActiveHabit) int {	// 第一部分逻辑	var part1 float64	switch {	case (habit.CalvingAge <= 1 && habit.Lact >= 1) ||		(habit.CalvingAge >= 2 && habit.CalvingAge <= 13 && (habit.SumRumina+habit.SumIntake) == 0) ||		((habit.Lact == 0 || habit.CalvingAge >= 14) && habit.ChangeFilter == -99):		part1 = -199	case habit.CalvingAge >= 2 && habit.CalvingAge <= 13:		part1 = math.Min((float64(habit.SumRumina+habit.SumIntake)-(100+math.Min(7, float64(habit.CalvingAge))*60))/10*2, 0)	case habit.ChangeFilter > -99:		part1 = math.Min(0, math.Min(getValueOrDefault(float64(habit.ChangeFilter), 0), getValueOrDefault(float64(habit.SumMinHigh), 0)))*0.2 +			math.Min(0, math.Min(getValueOrDefault(float64(habit.ChangeFilter), 0), getValueOrDefault(float64(habit.SumMinChew), 0)))*0.2 +			getRuminaSumIntakeSumScore(float64(habit.SumRumina+habit.SumIntake)) + getAdditionalScore(habit)	default:		part1 = -299	}	// 第二部分逻辑	var part2 float64	switch {	case habit.FirmwareVersion%100 >= 52:		part2 = 1	case habit.FirmwareVersion%100 >= 30 && habit.FirmwareVersion%100 <= 43:		part2 = 0.8	default:		part2 = 0.6	}	// 最终 score	return DefaultScore + int(math.Floor(part1*part2))}// 获取值或默认值func getValueOrDefault(value, defaultValue float64) float64 {	if value > -99 {		return value	}	return defaultValue}// 计算累计反刍得分func getRuminaSumIntakeSumScore(sum float64) float64 {	switch {	case sum < 80:		return -30	case sum < 180:		return -20	case sum < 280:		return -10	default:		return 0	}}// 计算额外得分func getAdditionalScore(habit *model.NeckActiveHabit) float64 {	var score float64	if (habit.SumRumina+habit.SumIntake < 280 || habit.SumMinHigh+habit.SumMinChew < -50) && habit.SumMaxHigh > 50 {		score += 10	}	if habit.ChangeFilter < -30 && habit.ChangeFilter <= habit.SumMinHigh && habit.ChewFilter < -30 && habit.ChewFilter <= habit.SumMinChew {		score -= 5	}	return score}
 |