package crontab import ( "fmt" "kpt-pasture/model" "kpt-pasture/util" "math" "sort" "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 ) // NeckRingOriginalMergeData 把脖环数据合并成2个小时的 func (e *Entry) NeckRingOriginalMergeData() (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)) } limit := e.Cfg.NeckRingLimit if limit <= 0 { limit = defaultLimit } //createdAt := newTime.Add(-1 * time.Hour) neckRingNumber := []string{ /*"10026", "10027", "10028", "10029", "10030", "10031", "10032", "10033", "10034", "10035", "10036", "10037", "10038", "10039", "10040", "10041", "10042", "10043", "10044", "10046", "10047", "10048", "10049", "10050", "10051", "10052", "10053", "10054", "10055", "10056", "9120007", "9120029", "9120040", "9120123", "9120164", "9120184", "9120219", "9120246", "9120321", "9120355", "9120359", "9120375", "9120379", "9120391", "9120446", "9120493", "9120497", "9120512", "9120531", "9120533", "9120540", "9120542", "9120612", "9120614", "9120615", "9120623", "9120625", "9120630", "9120653", "9120662", "211670", "9121372", "3204736", "3212694", "3204532", "3214082", "9121667", "3212275", "3210345", "3217879",*/ "9422", "3206975", "9496", "3204907", "212194", "3211061", "9120840", "3207787", "3210201", "404", "3207104", "3219173", "3216771", "3216872", "3209614", "3205455", "416", "9121026", "211622", "3207868", "3210340", "9120132", "3207092", "3209390", "3205872", "3207367", "3219700", "9120022", "211246", "466", "3207565", "3208779", "3204863", "3207963", "3204259", "3207966", "2640", "3214316", "3205778", "3206897", "3207745", "3209052", "3208045", "211627", "3212873", "2100766", "9121950", "3206076", "3206438", "9466", "9121195", "9122044", "3209430", "3205599", "2100794", "3048", "9467", "3207480", "3216011", "9121725", "9120340", "9121597", "427", "3209387", "3209490", "3214311", "3206044", "211253", "3207553", "3215616", "211350", "3207551", "3205896", "417", "9121008", "3207694", "3209372", "3217873", "3207227", "3207568", "3210615", "3204769", "3216095", "9121338", "3209124", "211340", "3219695", "9121802", "3205517", "3210676", "9123325", "3204328", "9409", "211349", "3208860", "9121769", "3209221", "3210916", "3205880", "438", "3205557", "9421", "211229", "444", "9123260", "9121464", "3212818", "3204834", "3205408", "3207486", "498", "3206232", "3206315", "2100759", "9121955", "3204338", "3207606", "3208767", "450", "9123330", "9121278", "9121011", "9122280", "479", "434", "3207614", "2355", "211644", "453", "9121308", "3209449", "405", "3204240", "9120165", "9445", "9120456", "9123057", "471", "3206830", "464", "403", "2100749", "3218278", "3218600", "212168", "9470", "428", "3205448", "3209790", "3208163", "213134", "3207603", "3206394", "3204476", "9121569", "3206447", "3209456", "9120533", "3209044", "3214232", "3209337", "212193", "3216684", "3218450", "3207194", "3204853", "3205858", "3207922", "3204645", "212157", "3214707", "213126", "3219468", "9120766", "3218611", "3215948", "3211568", "9120227", "211348", "3212621", "3209898", "3207637", "9121071", "9123113", "3206491", "3209922", "9440", "3207075", "423", "3204875", "9121888", "3210395", "9120630", "9120214", "3208166", "211285", "3206880", "3209461", "3205597", "3216262", "3204942", "9123224", "3205918", "3204889", "3209047", "3207024", "3207277", "3207699", "3205771", "9500", "3204450", "3205495", "9483", "3209876", "3205533", "3216075", "9469", "3209827", "9122454", "413", "3216308", "3219301", "3210042", "3205064", "211390", "9121305", "3218596", "3204955", "3215274", "212171", "3209460", "3211376", "3213812", "3205184", "3209287", "3211769", "3205926", "3216585", "3213332", "3209340", "9123175", "3205843", "3207663", "3210829", "3209481", "9450", "9463", "3209763", "3215627", "9121424", "3212746", "3218533", "3209072", "3207359", "3205888", "3214228", "3204884", "3218613", "3209352", "3208192", "3216211", "3211454", "3217082", "3212728", "3206984", "3217750", "3213406", "3206305", "9122130", "9121695", "9492", "3207856", "3218263", "9121058", "9123309", "9122466", "9122287", "9120614", "3084", "3205559", "3205840", "9121444", "9121777", "3209308", "9122313", "9121672", "3210180", "3207645", "3206419", "9413", "211576", "3209201", "2601", "3211708", "3206969", "3206871", "3210430", "211674", "9122441", "3214386", "3206927", "3209332", "9462", "3206430", "3207485", "3204519", "3216214", "9123371", "9120847", "9123355", "211690", "3210362", "3218862", "3213687", "3066", "3209787", "9120359", "9468", "2315", "3207031", "211353", "211250", "3207688", "9122447", "3218688", "445", "3205848", "3214915", "3208866", "1739", "3204990", "2100716", "212130", "3204214", "3208985", "211388", "9123166", "3208856", "211648", "2311", "3204411", "3217860", "9523", "9524", "3209134", "3209212", "211630", "9123151", "3207375", "441", "9525", "3205815", "3205527", "3215344", "3207185", "211332", "3217466", "9526", "1783", "9122414", "3207617", "212150", "3204248", "3216316", "3209535", "3206873", "3208974", "9406", "9122407", "9121925", "2351", "3214481", "3204826", "3205906", "3205109", "3209014", "211240", "3070", "474", "3204667", "3205511", "409", "3209061", "3205074", "3206009", "9123010", "3207536", "3219231", "3207198", "9475", "9423", "9121871", "3204643", "3205471", "3206095", "9121981", "9120835", "3206427", "3218217", "3207493", "2100732", "3204886", "3208174", "9486", "2100772", "3209419", "3207087", "2328", "3207371", "3210597", "3206231", "3206931", "3204236", "3207411", "3206374", "3206452", "3207472", "9429", "3218802", "211381", "9474", "3204420", "3207026", "3206124", "3209608", "3209330", "3209485", "3216593", "3205921", "2375", "3204818", "3215544", "3213632", "3216924", "3204395", "3207111", "3206961", "212169", "9123027", "2100744", "9520", "3208708", "3214104", "3206329", "9512", "3211187", "3207674", "3206004", "3207748", "3209328", "3209033", "3205601", "3205776", "9438", "3205269", "3204664", "9514", "3204897", "9446", "3208827", "3209611", "3208050", "9501", "9121120", "3213788", "9121699", "3204409", "3210073", "3207546", "2100758", "3206082", "3208975", "9122051", "9518", "3207751", "9408", "3206437", "3207343", "3207021", "3216998", "3205349", "3214744", "3205905", "3212646", "3209740", "3206239", "3207473", "3207236", "3209730", "3204360", "3206895", "9120696", "3204901", "9508", "3207935", "3213977", "3214166", "448", "3205893", "3212052", "3205552", "3211112", "3213551", "3217077", "3206322", "9465", "3208807", "3205211", "3215051", "3207121", "3215725", "3207229", "3219426", "3213345", "3217541", "3216876", "3215675", "3214245", "3207012", "3218806", "3217024", "3212486", "3207620", "3211045", "3213712", "3215268", "3215061", "3209099", "3218820", "3212817", "3204659", "3210976", "3213266", "3211716", "3207860", "3213350", "9517", "3215001", "3217667", "3213039", "3207248", "9510", "3215056", "3218732", "3216053", "3209995", "3215773", "3211035", "3213295", "3205725", "9464", "3205636", } neckRingList := make([]*model.NeckRingOriginal, 0) if err = e.DB.Model(new(model.NeckRingOriginal)). Where("is_show = ?", pasturePb.IsShow_No). Where("neck_ring_number IN (?)", neckRingNumber). Where("active_date = ?", time.Now().Format(model.LayoutDate2)). //Where("created_at <= ?", createdAt.Unix()). Order("active_date,frameid,neck_ring_number"). Limit(int(limit)).Find(&neckRingList).Error; err != nil { return xerr.WithStack(err) } if len(neckRingList) <= 0 { return nil } // 计算合并 neckActiveHabitList := e.recalculate(neckRingList) if len(neckActiveHabitList) <= 0 { return nil } for _, habit := range neckActiveHabitList { //更新脖环牛只相关信息 新数据直接插入 historyNeckActiveHabit, ct := e.IsExistNeckActiveHabit(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-3", 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-2", 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 defer func() { // 更新最后一次执行的id值 if err == nil { e.DB.Model(new(model.NeckRingConfigure)). Where("name = ?", model.MaxHabit). Where("pasture_id = ?", pastureId). Update("value", 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)) } if len(processIds) > 0 { 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 BETWEEN ? AND ?", xToday.LastMaxHabitId, xToday.CurrMaxHabitId). 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 BETWEEN ? AND ?", xToday.LastMaxHabitId, xToday.CurrMaxHabitId). 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 len(processIds) > 0 { 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 BETWEEN ? AND ?", xToDay.LastMaxHabitId, xToDay.CurrMaxHabitId). Where("pasture_id = ?", pastureId). Where("is_show = ?", pasturePb.IsShow_No). 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 { 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) } 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 } 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) 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.WeekIntakeHabit, "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("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"). 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)) } } // 更新所有的显示状态为否的记录为是 if err := e.DB.Model(new(model.NeckActiveHabit)). Where("id BETWEEN ? AND ?", xToday.LastMaxHabitId, xToday.CurrMaxHabitId). 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)) } 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 } // recalculate 合并计算 func (e *Entry) 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 > 6 { delete(originalMapData, k) continue } else if v.RecordCount < 6 { 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) 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 } newDataList := e.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 }