package crontab import ( "fmt" "kpt-pasture/model" "sort" "gitee.com/xuyiping_admin/pkg/logger/zaplog" "go.uber.org/zap" ) func (e *Entry) UpdatePenBehavior() error { pastureList := e.FindPastureList() if pastureList == nil || len(pastureList) == 0 { return nil } for _, pasture := range pastureList { conf, err := e.GetSystemNeckRingConfigure(pasture.Id, model.MaxPenBehavior) if err != nil { zaplog.Error("UpdatePenBehavior", zap.Any("pasture", pasture), zap.Any("err", err)) continue } e.PenBehavior(pasture.Id, conf.Value) } return nil } // PenBehavior 栏舍行为曲线 func (e *Entry) PenBehavior(pastureId, maxPenBehavior int64) { // 1. 获取颈环原始数据 neckRingOriginalList, err := e.getNeckRingOriginalList(pastureId, maxPenBehavior) if err != nil { zaplog.Error("PenBehavior", zap.Any("pastureId", pastureId), zap.Any("maxPenBehavior", maxPenBehavior), zap.Any("err", err)) return } if len(neckRingOriginalList) <= 0 { return } // 2. 获取牛只信息 cowMap, err := e.getCowMap(pastureId, neckRingOriginalList) if err != nil { zaplog.Error("PenBehavior", zap.Any("pastureId", pastureId), zap.Any("neckRingOriginalList", neckRingOriginalList), zap.Any("err", err)) return } // 3. 处理栏舍行为数据 penData := e.processPenBehaviorData(neckRingOriginalList, cowMap) // 4. 计算平均值和百分比 e.calculateAveragesAndRates(penData) // 5. 保存数据 if err = e.savePenBehaviorData(penData); err != nil { zaplog.Error("PenBehavior", zap.Any("penData", penData), zap.Any("err", err)) return } sort.Slice(neckRingOriginalList, func(i, j int) bool { return neckRingOriginalList[i].Id > neckRingOriginalList[j].Id }) if err = e.UpdateSystemNeckRingConfigure(pastureId, model.MaxPenBehavior, neckRingOriginalList[0].Id); err != nil { zaplog.Error("PenBehavior", zap.Any("UpdateSystemNeckRingConfigure", err), zap.Any("neckRingOriginalList", neckRingOriginalList)) } } // getNeckRingOriginalList 获取颈环原始数据 func (e *Entry) getNeckRingOriginalList(pastureId, maxPenBehavior int64) ([]*model.NeckRingOriginal, error) { var neckRingOriginalList []*model.NeckRingOriginal if err := e.DB.Model(new(model.NeckRingOriginal)). Where("id > ? AND pasture_id = ?", maxPenBehavior, pastureId). Order("active_date,neck_ring_number,frameid"). Limit(int(defaultLimit)). Find(&neckRingOriginalList).Error; err != nil { return nil, err } return neckRingOriginalList, nil } // getCowMap 获取牛只信息映射 func (e *Entry) getCowMap(pastureId int64, neckRingOriginalList []*model.NeckRingOriginal) (map[string]*model.Cow, error) { // 提取牛只ID cowIds := make([]int64, 0, len(neckRingOriginalList)) for _, v := range neckRingOriginalList { cowIds = append(cowIds, v.Id) } // 获取牛只信息 cowInfoList, err := e.GetCowByIds(pastureId, cowIds) if err != nil { return nil, err } // 构建牛只信息映射 cowMap := make(map[string]*model.Cow, len(cowInfoList)) for _, v := range cowInfoList { if v.NeckRingNumber == "" { continue } cowMap[v.NeckRingNumber] = v } return cowMap, nil } // processPenBehaviorData 处理栏舍行为数据 func (e *Entry) processPenBehaviorData(neckRingOriginalList []*model.NeckRingOriginal, cowMap map[string]*model.Cow) map[string]*model.PenBehaviorData { penData := make(map[string]*model.PenBehaviorData, len(neckRingOriginalList)) for _, v := range neckRingOriginalList { cowInfo, ok := cowMap[v.NeckRingNumber] if !ok { zaplog.Error("PenBehavior", zap.Any("neckRingNumber", v.NeckRingNumber)) continue } key := fmt.Sprintf("%s_%d_%d", v.ActiveDate, cowInfo.PenId, v.Frameid) if data, exists := penData[key]; exists { data.CowCount++ data.AvgHigh += v.High data.SumRumina += ifThenElse(v.Rumina >= 8, 1, 0) data.SumIntake += ifThenElse(v.Intake >= 8, 1, 0) data.SumRest += ifThenElse(v.Inactive >= 8, 1, 0) data.SumGasp += ifThenElse(v.Gasp >= 8, 1, 0) } else { penData[key] = &model.PenBehaviorData{ PastureId: cowInfo.PastureId, PenId: cowInfo.PenId, PenName: cowInfo.PenName, HeatDate: v.ActiveDate, Frameid: v.Frameid, CowCount: 1, AvgHigh: v.High, } } } return penData } // calculateAveragesAndRates 计算平均值和百分比 func (e *Entry) calculateAveragesAndRates(penData map[string]*model.PenBehaviorData) { for _, data := range penData { // 计算平均值 data.AvgHigh = data.AvgHigh / data.CowCount // 计算百分比 if data.CowCount > 0 { data.RuminaRate = int32(float64(data.SumRumina) / float64(data.CowCount) * 100) data.IntakeRate = int32(float64(data.SumIntake) / float64(data.CowCount) * 100) data.RestRate = int32(float64(data.SumRest) / float64(data.CowCount) * 100) data.GaspRate = int32(float64(data.SumGasp) / float64(data.CowCount) * 100) } } } // savePenBehaviorData 保存栏舍行为数据 func (e *Entry) savePenBehaviorData(penData map[string]*model.PenBehaviorData) error { for _, data := range penData { // 构建活动时间 activeTime := e.calculateActiveTime(data.HeatDate, data.Frameid) // 构建保存数据 penBehavior := &model.PenBehavior{ PastureId: data.PastureId, HeatDate: data.HeatDate, ActiveTime: activeTime, PenId: data.PenId, PenName: data.PenName, CowCount: data.CowCount, AvgHigh: data.AvgHigh, SumRumina: data.SumRumina, SumIntake: data.SumIntake, SumRest: data.SumRest, SumGasp: data.SumGasp, RuminaRate: data.RuminaRate, IntakeRate: data.IntakeRate, RestRate: data.RestRate, GaspRate: data.GaspRate, } // 使用 Upsert 操作 if err := e.DB.Model(new(model.PenBehavior)). Where("pasture_id = ? AND heat_date = ? AND pen_id = ? AND active_time = ?", penBehavior.PastureId, penBehavior.HeatDate, penBehavior.PenId, penBehavior.ActiveTime). Assign(map[string]interface{}{ "cow_count": penBehavior.CowCount, "avg_high": penBehavior.AvgHigh, "sum_rumina": penBehavior.SumRumina, "sum_intake": penBehavior.SumIntake, "sum_rest": penBehavior.SumRest, "sum_gasp": penBehavior.SumGasp, "rumina_rate": penBehavior.RuminaRate, "intake_rate": penBehavior.IntakeRate, "rest_rate": penBehavior.RestRate, "gasp_rate": penBehavior.GaspRate, }). FirstOrCreate(penBehavior).Error; err != nil { return err } } return nil } // calculateActiveTime 计算活动时间 func (e *Entry) calculateActiveTime(heatDate string, frameid int32) string { // 计算小时和分钟 hour := (frameid / 10) * 2 minute := (frameid%10)*20 - 1 // 构建时间字符串 return fmt.Sprintf("%s %02d:%02d", heatDate, hour, minute) } // ifThenElse 条件判断函数 func ifThenElse(condition bool, a, b int32) int32 { if condition { return a } return b }