|
@@ -9,27 +9,61 @@ import (
|
|
|
)
|
|
|
|
|
|
// PenBehavior 栏舍行为曲线
|
|
|
-func (e *Entry) PenBehavior(pastureId int64, processIds []int64) {
|
|
|
- neckRingOriginalList := make([]*model.NeckRingOriginal, 0)
|
|
|
+func (e *Entry) PenBehavior(pastureId int64, processIds []int64) error {
|
|
|
+ // 1. 获取颈环原始数据
|
|
|
+ neckRingOriginalList, err := e.getNeckRingOriginalList(pastureId, processIds)
|
|
|
+ if err != nil {
|
|
|
+ return fmt.Errorf("获取颈环原始数据失败: %w", err)
|
|
|
+ }
|
|
|
+
|
|
|
+ // 2. 获取牛只信息
|
|
|
+ cowMap, err := e.getCowMap(pastureId, neckRingOriginalList)
|
|
|
+ if err != nil {
|
|
|
+ return fmt.Errorf("获取牛只信息失败: %w", err)
|
|
|
+ }
|
|
|
+
|
|
|
+ // 3. 处理栏舍行为数据
|
|
|
+ penData := e.processPenBehaviorData(neckRingOriginalList, cowMap)
|
|
|
+
|
|
|
+ // 4. 计算平均值和百分比
|
|
|
+ e.calculateAveragesAndRates(penData)
|
|
|
+
|
|
|
+ // 5. 保存数据
|
|
|
+ if err := e.savePenBehaviorData(penData); err != nil {
|
|
|
+ return fmt.Errorf("保存栏舍行为数据失败: %w", err)
|
|
|
+ }
|
|
|
+
|
|
|
+ return nil
|
|
|
+}
|
|
|
+
|
|
|
+// getNeckRingOriginalList 获取颈环原始数据
|
|
|
+func (e *Entry) getNeckRingOriginalList(pastureId int64, processIds []int64) ([]*model.NeckRingOriginal, error) {
|
|
|
+ var neckRingOriginalList []*model.NeckRingOriginal
|
|
|
if err := e.DB.Model(new(model.NeckRingOriginal)).
|
|
|
- Where("id IN (?)", processIds).
|
|
|
- Where("pasture_id = ?", pastureId).
|
|
|
+ Where("id IN (?) AND pasture_id = ?", processIds, pastureId).
|
|
|
Order("heat_date,neck_ring_number,frameid").
|
|
|
Find(&neckRingOriginalList).Error; err != nil {
|
|
|
- zaplog.Error("PenBehavior", zap.Any("error", err), zap.Any("processIds", processIds))
|
|
|
+ return nil, err
|
|
|
}
|
|
|
+ return neckRingOriginalList, nil
|
|
|
+}
|
|
|
|
|
|
- cowIds := make([]int64, 0)
|
|
|
+// 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 {
|
|
|
- zaplog.Error("PenBehavior", zap.Any("error", err), zap.Any("cowIds", cowIds))
|
|
|
- return
|
|
|
+ return nil, err
|
|
|
}
|
|
|
- cowMap := make(map[string]*model.Cow)
|
|
|
+
|
|
|
+ // 构建牛只信息映射
|
|
|
+ cowMap := make(map[string]*model.Cow, len(cowInfoList))
|
|
|
for _, v := range cowInfoList {
|
|
|
if v.NeckRingNumber == "" {
|
|
|
continue
|
|
@@ -37,18 +71,30 @@ func (e *Entry) PenBehavior(pastureId int64, processIds []int64) {
|
|
|
cowMap[v.NeckRingNumber] = v
|
|
|
}
|
|
|
|
|
|
- penData := make(map[string]*model.PenBehaviorData)
|
|
|
+ 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("error", err), zap.Any("neckRingNumber", v.NeckRingNumber))
|
|
|
+ zaplog.Error("PenBehavior", zap.Any("neckRingNumber", v.NeckRingNumber))
|
|
|
continue
|
|
|
}
|
|
|
|
|
|
key := fmt.Sprintf("%s_%d_%d", v.ActiveDate, cowInfo.PenId, v.Frameid)
|
|
|
- if penData[key] == nil {
|
|
|
+ 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: pastureId,
|
|
|
+ PastureId: cowInfo.PastureId,
|
|
|
PenId: cowInfo.PenId,
|
|
|
PenName: cowInfo.PenName,
|
|
|
HeatDate: v.ActiveDate,
|
|
@@ -56,22 +102,87 @@ func (e *Entry) PenBehavior(pastureId int64, processIds []int64) {
|
|
|
CowCount: 1,
|
|
|
AvgHigh: v.High,
|
|
|
}
|
|
|
- } else {
|
|
|
- penData[key].CowCount++
|
|
|
- penData[key].AvgHigh += v.High
|
|
|
- penData[key].SumRumina += ifThenElse(v.Rumina >= 8, 1, 0)
|
|
|
- penData[key].SumIntake += ifThenElse(v.Intake >= 8, 1, 0)
|
|
|
- penData[key].SumRest += ifThenElse(v.Inactive >= 8, 1, 0)
|
|
|
- penData[key].SumGasp += ifThenElse(v.Gasp >= 8, 1, 0)
|
|
|
+
|
|
|
}
|
|
|
}
|
|
|
- // 计算平均值
|
|
|
+ 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
|