Browse Source

dashboardï: dataWarning update

Yi 1 month ago
parent
commit
80cb6a5853

+ 14 - 0
model/data_warning_items.go

@@ -2,10 +2,15 @@ package model
 
 import (
 	"fmt"
+	"strconv"
 
 	pasturePb "gitee.com/xuyiping_admin/go_proto/proto/go/backend/cow"
 )
 
+const (
+	FieldName = "day_age"
+)
+
 type DataWarningItems struct {
 	Id          int64                 `json:"id"`
 	PastureId   int64                 `json:"pastureId"`
@@ -138,6 +143,10 @@ func DataWarningItemsInitData(pastureId int64, dataWarning *DataWarning) []*Data
 }
 
 func NewDataWarningItems(pastureId, userId int64, dataWarning *DataWarning, req *pasturePb.WarningDataSet) *DataWarningItems {
+	if req.FieldName == FieldName {
+		v1, _ := strconv.ParseInt(req.Value, 10, 64)
+		req.Value = fmt.Sprintf("%d", v1*30)
+	}
 	return &DataWarningItems{
 		PastureId: pastureId,
 		UserId:    userId,
@@ -168,6 +177,11 @@ func (d DataWarningItemsSlice) ToPB(dataWarning []*DataWarning) []*pasturePb.War
 		if name == "" || v.Id <= 0 || v.IsCondition == pasturePb.IsShow_Ok {
 			continue
 		}
+
+		if v.FieldName == FieldName {
+			v1, _ := strconv.ParseInt(v.Value, 10, 64)
+			v.Value = fmt.Sprintf("%d", v1/30)
+		}
 		res = append(res, &pasturePb.WarningDataSet{
 			Id:        int32(v.Id),
 			WarningId: int32(v.WarningId),

+ 57 - 27
model/pen_behavior.go

@@ -7,34 +7,37 @@ import (
 	pasturePb "gitee.com/xuyiping_admin/go_proto/proto/go/backend/cow"
 )
 
+const PenBehaviorMinCowCount = 20
+
 type PenBehavior struct {
-	Id             int64  `json:"id"`
-	PastureId      int64  `json:"pastureId"`
-	HeatDate       string `json:"heatDate"`
-	ActiveTime     string `json:"activeTime"`
-	Frameid        int32  `json:"frameid"`
-	PenId          int32  `json:"penId"`
-	PenName        string `json:"penName"`
-	CowCount       int32  `json:"cowCount"`
-	AvgHigh        int32  `json:"avgHigh"`
-	SumRumina      int32  `json:"sumRumina"`
-	SumIntake      int32  `json:"sumIntake"`
-	SumRest        int32  `json:"sumRest"`
-	SumGasp        int32  `json:"sumGasp"`
-	RuminaRate     int32  `json:"ruminaRate"`
-	IntakeRate     int32  `json:"intakeRate"`
-	RestRate       int32  `json:"restRate"`
-	GaspRate       int32  `json:"gaspRate"`
-	WeekRuminaRate int32  `json:"weekRuminaRate"`
-	RuminaStd      int32  `json:"ruminaStd"`
-	WeekIntakeRate int32  `json:"weekIntakeRate"`
-	IntakeStd      int32  `json:"intakeStd"`
-	WeekRestRate   int32  `json:"weekRestRate"`
-	RestStd        int32  `json:"restStd"`
-	WeekGaspRate   int32  `json:"weekGaspRate"`
-	GaspStd        int32  `json:"gaspStd"`
-	CreatedAt      int64  `json:"createdAt"`
-	UpdatedAt      int64  `json:"updatedAt"`
+	Id             int64                 `json:"id"`
+	PastureId      int64                 `json:"pastureId"`
+	HeatDate       string                `json:"heatDate"`
+	ActiveTime     string                `json:"activeTime"`
+	Frameid        int32                 `json:"frameid"`
+	PenId          int32                 `json:"penId"`
+	PenName        string                `json:"penName"`
+	CowCount       int32                 `json:"cowCount"`
+	AvgHigh        int32                 `json:"avgHigh"`
+	SumRumina      int32                 `json:"sumRumina"`
+	SumIntake      int32                 `json:"sumIntake"`
+	SumRest        int32                 `json:"sumRest"`
+	SumGasp        int32                 `json:"sumGasp"`
+	RuminaRate     int32                 `json:"ruminaRate"`
+	IntakeRate     int32                 `json:"intakeRate"`
+	RestRate       int32                 `json:"restRate"`
+	GaspRate       int32                 `json:"gaspRate"`
+	WeekRuminaRate int32                 `json:"weekRuminaRate"`
+	RuminaStd      int32                 `json:"ruminaStd"`
+	WeekIntakeRate int32                 `json:"weekIntakeRate"`
+	IntakeStd      int32                 `json:"intakeStd"`
+	WeekRestRate   int32                 `json:"weekRestRate"`
+	RestStd        int32                 `json:"restStd"`
+	WeekGaspRate   int32                 `json:"weekGaspRate"`
+	GaspStd        int32                 `json:"gaspStd"`
+	IsShow         pasturePb.IsShow_Kind `json:"isShow"`
+	CreatedAt      int64                 `json:"createdAt"`
+	UpdatedAt      int64                 `json:"updatedAt"`
 }
 
 func (p *PenBehavior) TableName() string {
@@ -125,6 +128,21 @@ func (p PenBehaviorSlice) ToPB() *pasturePb.BarnBehaviorCurveItem {
 	return res
 }
 
+type PenBehaviorModel struct {
+	Id         int64
+	PastureId  int64
+	ActiveDate string
+	PenId      int32
+	PenName    string
+	Frameid    int32
+	Rumina     int32
+	Intake     int32
+	Inactive   int32
+	Gasp       int32
+	High       int32
+	Active     int32
+}
+
 type PenBehaviorData struct {
 	PastureId  int64  `json:"pastureId"`
 	PenId      int32  `json:"penId"`
@@ -196,3 +214,15 @@ func (p PenBehaviorSlice) ToPB2() *BarnBehaviorCurveItem {
 
 	return res
 }
+
+type PenBehaviorWeekModel struct {
+	CowCount   int32     `json:"cowCount"`
+	SumRumina  int32     `json:"sumRumina"`
+	SumIntake  int32     `json:"sumIntake"`
+	SumRest    int32     `json:"sumRest"`
+	SumGasp    int32     `json:"sumGasp"`
+	RuminaRate []float64 `json:"ruminaRate"`
+	IntakeRate []float64 `json:"intakeRate"`
+	RestRate   []float64 `json:"restRate"`
+	GaspRate   []float64 `json:"gaspRate"`
+}

+ 2 - 3
module/backend/calendar.go

@@ -127,7 +127,7 @@ func (s *StoreEntry) CalendarToDoList(ctx context.Context, req *pasturePb.Calend
 	historyCount := make([]*model.CompletedData, 0)
 	todayStartTime := util.TimeParseLocalUnix(nowTime)
 	todayEndTime := util.TimeParseLocalEndUnix(nowTime)
-	whereSql := fmt.Sprintf(` WHERE pasture_id = %d AND end_day > %d  AND (status = %d OR (status = %d AND reality_day BETWEEN %d AND %d ))`,
+	whereSql := fmt.Sprintf(` WHERE pasture_id = %d AND end_day >= %d  AND (status = %d OR (status = %d AND reality_day BETWEEN %d AND %d ))`,
 		pastureId, todayEndTime, pasturePb.IsShow_No, pasturePb.IsShow_Ok, todayStartTime, todayEndTime)
 	historyCountSql := `SELECT a.count as count,a.calendar_type_kind as calendar_type_kind FROM (
 		SELECT count(cow_id) as count,1 as calendar_type_kind FROM event_immunization_plan ` + whereSql + `
@@ -145,8 +145,7 @@ func (s *StoreEntry) CalendarToDoList(ctx context.Context, req *pasturePb.Calend
 		SELECT count(cow_id) as count,7 as calendar_type_kind FROM event_cow_disease WHERE ` +
 		fmt.Sprintf("pasture_id = %d AND (health_status IN (%d,%d) OR (health_status = %d AND curable_at BETWEEN %d AND %d))",
 			pastureId, pasturePb.HealthStatus_Disease, pasturePb.HealthStatus_Treatment, pasturePb.HealthStatus_Curable, todayStartTime, todayEndTime) + `
-	) as a
-`
+	) as a`
 	if err = s.DB.Raw(historyCountSql).Find(&historyCount).Error; err != nil {
 		return nil, xerr.WithStack(err)
 	}

+ 11 - 3
module/backend/dashboard.go

@@ -3,6 +3,7 @@ package backend
 import (
 	"context"
 	"errors"
+	"fmt"
 	"kpt-pasture/model"
 	"kpt-pasture/util"
 	"net/http"
@@ -397,7 +398,8 @@ func (s *StoreEntry) addUserDataWarning(ctx context.Context, pastureId, userId i
 			// 如果该 Kind 已添加,跳过
 			if !addedKinds[set.Kind] {
 				// 创建新的预警数据
-				if err := tx.Create(dataWarning).Error; err != nil {
+				if err := tx.Model(new(model.DataWarning)).
+					Create(dataWarning).Error; err != nil {
 					return xerr.WithStack(err)
 				}
 			} else {
@@ -412,7 +414,8 @@ func (s *StoreEntry) addUserDataWarning(ctx context.Context, pastureId, userId i
 			}
 
 			// 创建预警项数据
-			if err := tx.Create(model.NewDataWarningItems(pastureId, userId, dataWarning, set)).Error; err != nil {
+			if err := tx.Model(new(model.DataWarningItems)).
+				Create(model.NewDataWarningItems(pastureId, userId, dataWarning, set)).Error; err != nil {
 				return xerr.WithStack(err)
 			}
 			addedKinds[set.Kind] = true
@@ -467,11 +470,16 @@ func (s *StoreEntry) updateUserDataWarning(ctx context.Context, pastureId, userI
 		// 更新预警项数据的 IsShow 和 Value 字段
 		for _, item := range userDataWarningItems {
 			if set, ok := warningItemDataMap[int32(item.Id)]; ok {
+				value := set.Value
+				if set.FieldName == model.FieldName {
+					v1, _ := strconv.ParseInt(set.Value, 10, 64)
+					value = fmt.Sprintf("%d", v1*30)
+				}
 				if err = tx.Model(&model.DataWarningItems{}).
 					Where("id = ?", item.Id).
 					Updates(map[string]interface{}{
 						"is_show": set.IsShow,
-						"value":   set.Value,
+						"value":   value,
 					}).Error; err != nil {
 					return xerr.WithStack(err)
 				}

+ 1 - 1
module/crontab/neck_ring_calculate.go

@@ -327,7 +327,7 @@ func (e *Entry) FilterCorrectAndScoreUpdate(pastureId int64, processIds []int64,
 			// 活动量校正系数
 			if err := e.DB.Model(new(model.NeckActiveHabit)).
 				Where("id = ?", v.Id).
-				Where("neck_ring_number = ?", v.NeckRingNumber).
+				//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

+ 1 - 2
module/crontab/neck_ring_merge.go

@@ -25,8 +25,7 @@ const (
 )
 
 var (
-	defaultLimit       = int32(300)
-	calculateIsRunning bool
+	defaultLimit = int32(1000)
 )
 
 // NeckRingOriginalMerge 把脖环数据合并成2个小时的

+ 277 - 129
module/crontab/pen_behavior.go

@@ -3,9 +3,12 @@ package crontab
 import (
 	"fmt"
 	"kpt-pasture/model"
+	"math"
 	"sort"
 	"time"
 
+	pasturePb "gitee.com/xuyiping_admin/go_proto/proto/go/backend/cow"
+
 	"gitee.com/xuyiping_admin/pkg/logger/zaplog"
 	"go.uber.org/zap"
 )
@@ -31,113 +34,107 @@ func (e *Entry) UpdatePenBehavior() error {
 // PenBehavior 栏舍行为曲线
 func (e *Entry) PenBehavior(pastureId, maxPenBehavior int64) {
 	// 1. 获取颈环原始数据
-	neckRingOriginalList, err := e.getNeckRingOriginalList(pastureId, maxPenBehavior)
+	penBehaviorModelList, err := e.getNeckRingOriginalList(pastureId, maxPenBehavior)
 	if err != nil {
-		zaplog.Error("PenBehavior", zap.Any("pastureId", pastureId), zap.Any("maxPenBehavior", maxPenBehavior), zap.Any("err", err))
+		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))
+	if len(penBehaviorModelList) <= 0 {
 		return
 	}
 
-	// 3. 处理栏舍行为数据
-	penData := e.processPenBehaviorData(neckRingOriginalList, cowMap)
+	// 2. 处理栏舍行为数据
+	penData := e.processPenBehaviorData(penBehaviorModelList)
 
-	// 4. 计算平均值和百分比
+	// 3. 计算平均值和百分比
 	e.calculateAveragesAndRates(penData)
 
-	// 5. 保存数据
+	// 4. 保存数据
 	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
+	sort.Slice(penBehaviorModelList, func(i, j int) bool {
+		return penBehaviorModelList[i].Id > penBehaviorModelList[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))
+	if err = e.UpdateSystemNeckRingConfigure(pastureId, model.MaxPenBehavior, penBehaviorModelList[0].Id); err != nil {
+		zaplog.Error("PenBehavior", zap.Any("MaxPenBehavior", err), zap.Any("penBehaviorModelList", penBehaviorModelList))
 	}
 }
 
 // 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").
+func (e *Entry) getNeckRingOriginalList(pastureId, maxPenBehavior int64) ([]*model.PenBehaviorModel, error) {
+	var penBehaviorModelList []*model.PenBehaviorModel
+	if err := e.DB.Table(fmt.Sprintf("%s as h", new(model.NeckRingOriginal).TableName())).
+		Joins("JOIN cow as c ON h.pasture_id = c.pasture_id AND h.neck_ring_number = c.neck_ring_number").
+		Select("h.id,c.pasture_id, c.pen_id, c.pen_name, h.active_date, h.frameid, h.high, h.rumina, h.intake, h.inactive, h.gasp").
+		Where("h.id > ? AND h.pasture_id = ?", maxPenBehavior, pastureId).
+		Order("h.active_date,h.frameid").
 		Limit(int(defaultLimit)).
-		Find(&neckRingOriginalList).Error; err != nil {
+		Find(&penBehaviorModelList).Error; err != nil {
 		return nil, err
 	}
-	return neckRingOriginalList, nil
+	return penBehaviorModelList, nil
 }
 
-// getCowMap 获取牛只信息映射
-func (e *Entry) getCowMap(pastureId int64, neckRingOriginalList []*model.NeckRingOriginal) (map[string]*model.Cow, error) {
-	// 提取牛只ID
-	neckRingNumberList := make([]string, 0, len(neckRingOriginalList))
-	for _, v := range neckRingOriginalList {
-		neckRingNumberList = append(neckRingNumberList, v.NeckRingNumber)
-	}
-
-	// 获取牛只信息
-	cowInfoList, err := e.GetCowByNeckRingNumbers(pastureId, neckRingNumberList)
-	if err != nil {
-		return nil, err
-	}
-
-	// 构建牛只信息映射
-	cowMap := make(map[string]*model.Cow, len(cowInfoList))
-	for _, v := range cowInfoList {
-		if v.NeckRingNumber == "" {
-			continue
+// processPenBehaviorData 处理栏舍行为数据
+func (e *Entry) processPenBehaviorData(penBehaviorModelList []*model.PenBehaviorModel) map[string]*model.PenBehaviorData {
+	// 按active_date和frameid分组
+	activeDateFrameIdMap := make(map[string][]*model.PenBehaviorModel)
+	for _, v := range penBehaviorModelList {
+		key := fmt.Sprintf("%s_%d", v.ActiveDate, v.Frameid)
+		if activeDateFrameIdMap[key] == nil {
+			activeDateFrameIdMap[key] = make([]*model.PenBehaviorModel, 0)
 		}
-		cowMap[v.NeckRingNumber] = v
+		activeDateFrameIdMap[key] = append(activeDateFrameIdMap[key], 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
+	// 按pen_id分组统计
+	penData := make(map[string]*model.PenBehaviorData)
+	for _, v := range activeDateFrameIdMap {
+		// 按pen_id分组
+		penIdMap := make(map[int32]*model.PenBehaviorData)
+		for _, item := range v {
+			if data, exists := penIdMap[item.PenId]; exists {
+				// 更新计数
+				data.CowCount++
+				// 更新平均值
+				data.AvgHigh += item.High
+				// 更新行为统计
+				data.SumRumina += ifThenElse(item.Rumina >= 8, 1, 0)
+				data.SumIntake += ifThenElse(item.Intake >= 8, 1, 0)
+				data.SumRest += ifThenElse(item.Inactive >= 8, 1, 0)
+				data.SumGasp += ifThenElse(item.Gasp >= 8, 1, 0)
+			} else {
+				penIdMap[item.PenId] = &model.PenBehaviorData{
+					PastureId: item.PastureId,
+					PenId:     item.PenId,
+					PenName:   item.PenName,
+					HeatDate:  item.ActiveDate,
+					Frameid:   item.Frameid,
+					CowCount:  1,
+					AvgHigh:   item.High,
+					SumRumina: ifThenElse(item.Rumina >= 8, 1, 0),
+					SumIntake: ifThenElse(item.Intake >= 8, 1, 0),
+					SumRest:   ifThenElse(item.Inactive >= 8, 1, 0),
+					SumGasp:   ifThenElse(item.Gasp >= 8, 1, 0),
+				}
+			}
 		}
 
-		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,
-			}
+		// 将penIdMap的数据合并到penData中
+		for penId, data := range penIdMap {
+			key := fmt.Sprintf("%s_%d_%d", data.HeatDate, penId, data.Frameid)
+			penData[key] = data
 		}
 	}
+
 	return penData
 }
 
@@ -146,7 +143,6 @@ func (e *Entry) calculateAveragesAndRates(penData map[string]*model.PenBehaviorD
 	for _, data := range penData {
 		// 计算平均值
 		data.AvgHigh = data.AvgHigh / data.CowCount
-
 		// 计算百分比
 		if data.CowCount > 0 {
 			data.RuminaRate = int32(float64(data.SumRumina) / float64(data.CowCount) * 100)
@@ -166,73 +162,191 @@ func (e *Entry) savePenBehaviorData(penData map[string]*model.PenBehaviorData) e
 		// 构建保存数据
 		penBehavior := model.NewPenBehavior(data, activeTime)
 
+		if e.isExistByPenBehavior(data.PastureId, data.HeatDate, data.PenId, data.Frameid) {
+			historyData := e.findPenBehavior(data.PastureId, data.HeatDate, data.PenId, data.Frameid)
+			if historyData == nil || historyData.Id <= 0 {
+				continue
+			}
+			// 计算新的总和和平均值
+			newCowCount := historyData.CowCount + penBehavior.CowCount
+			newAvgHigh := (historyData.AvgHigh*historyData.CowCount + penBehavior.AvgHigh*penBehavior.CowCount) / newCowCount
+			newSumRumina := historyData.SumRumina + penBehavior.SumRumina
+			newSumIntake := historyData.SumIntake + penBehavior.SumIntake
+			newSumRest := historyData.SumRest + penBehavior.SumRest
+			newSumGasp := historyData.SumGasp + penBehavior.SumGasp
+
+			if err := e.DB.Model(new(model.PenBehavior)).
+				Where("id = ?", historyData.Id).
+				Updates(map[string]interface{}{
+					"cow_count":   newCowCount,
+					"avg_high":    newAvgHigh,
+					"sum_rumina":  newSumRumina,
+					"sum_intake":  newSumIntake,
+					"sum_rest":    newSumRest,
+					"sum_gasp":    newSumGasp,
+					"rumina_rate": int32(float64(newSumRumina) / float64(newCowCount) * 100),
+					"intake_rate": int32(float64(newSumIntake) / float64(newCowCount) * 100),
+					"rest_rate":   int32(float64(newSumRest) / float64(newCowCount) * 100),
+					"gasp_rate":   int32(float64(newSumGasp) / float64(newCowCount) * 100),
+				}).Error; err != nil {
+				zaplog.Error("savePenBehaviorData", zap.Any("penBehavior", penBehavior), zap.Any("err", err))
+			}
+			continue
+		}
+
 		// 使用 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{}{
-				"frameid":     penBehavior.Frameid,
-				"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
+			Create(penBehavior).Error; err != nil {
+			zaplog.Error("savePenBehaviorData", zap.Any("penBehavior", penBehavior), zap.Any("err", err))
 		}
 	}
 	return nil
 }
 
 func (e *Entry) UpdatePenBehaviorWeekData(pastureId int64) {
-	nowTime := time.Now().Local()
-	currTime := nowTime.Format(model.LayoutDate2)
-	startTime := nowTime.AddDate(0, 0, -7).Format(model.LayoutDate2)
-	endTime := nowTime.AddDate(0, 0, -1).Format(model.LayoutDate2)
-	penBehaviorList := make([]*model.PenBehavior, 0)
-	if err := e.DB.Table(fmt.Sprintf("%s as b1", new(model.PenBehavior).TableName())).
-		Joins(fmt.Sprintf("JOIN %s as b0 ON b1.pen_id = b0.pen_id", new(model.PenBehavior).TableName())).
-		Select(`b1.id, ROUND(AVG(b0.rumina_rate)) AS week_rumina_rate, 
-		ROUND(AVG(b0.intake_rate)) AS week_intake_rate,
-		ROUND(AVG(b0.rest_rate)) AS week_rest_rate, 
-		ROUND(AVG(b0.gasp_rate)) AS week_gasp_rate, 
-		ROUND(STD(b0.rumina_rate)) AS rumina_std, 		
-		ROUND(STD(b0.intake_rate)) AS  intake_std, 		
-		ROUND(STD(b0.rest_rate)) AS rest_std, 	
-		ROUND(STD(b0.gasp_rate)) AS gasp_std`).
-		Where("b1.pasture_id = ?", pastureId).
-		Where("b1.heat_date = ?", currTime).
-		Where("b1.week_rumina_rate = ?", 0).
-		Where("b1.frameid = b0.frameid").
-		Where("b0.heat_date BETWEEN ? AND ?", startTime, endTime).
-		Find(&penBehaviorList).Error; err != nil {
-		zaplog.Error("PenBehavior", zap.Any("penBehaviorList", penBehaviorList), zap.Any("err", err))
+	penBehaviorList := e.findWeekPenBehaviorList(pastureId)
+	if len(penBehaviorList) == 0 {
 		return
 	}
 
-	for _, v := range penBehaviorList {
-		if err := e.DB.Model(new(model.PenBehavior)).
-			Where("id = ?", v.Id).
-			Updates(map[string]interface{}{
-				"week_rumina_rate": v.WeekRuminaRate,
-				"week_intake_rate": v.WeekIntakeRate,
-				"week_rest_rate":   v.WeekRestRate,
-				"week_gasp_rate":   v.WeekGaspRate,
-				"rumina_std":       v.RuminaStd,
-				"intake_std":       v.IntakeStd,
-				"rest_std":         v.RestStd,
-				"gasp_std":         v.GaspStd,
-			}).Error; err != nil {
-			zaplog.Error("PenBehavior", zap.Any("penBehaviorWeekData", v), zap.Any("err", err))
+	// 按日期和frameid排序
+	sort.Slice(penBehaviorList, func(i, j int) bool {
+		if penBehaviorList[i].HeatDate == penBehaviorList[j].HeatDate {
+			return penBehaviorList[i].Frameid < penBehaviorList[j].Frameid
+		}
+		return penBehaviorList[i].HeatDate < penBehaviorList[j].HeatDate
+	})
+
+	// 处理每个日期和frameid的数据
+	for _, item := range penBehaviorList {
+		currDate := item.HeatDate
+		currFrameid := item.Frameid
+		// 计算开始和结束日期
+		currTime, err := time.Parse(model.LayoutDate2, currDate)
+		if err != nil {
+			zaplog.Error("UpdatePenBehaviorWeekData", zap.Error(err))
 			continue
 		}
+		startTime := currTime.AddDate(0, 0, -7).Format(model.LayoutDate2)
+		endTime := currTime.AddDate(0, 0, -1).Format(model.LayoutDate2)
+
+		// 获取历史数据
+		historyList := e.findHistoryPenBehaviorList(pastureId, startTime, endTime, currFrameid)
+		if len(historyList) == 0 {
+			// 如果没有历史数据,将所有记录标记为-1
+			if err = e.DB.Model(new(model.PenBehavior)).
+				Where("id = ?", item.Id).
+				Updates(map[string]interface{}{
+					"week_rumina_rate": -1,
+					"week_intake_rate": -1,
+					"week_rest_rate":   -1,
+					"week_gasp_rate":   -1,
+					"is_show":          pasturePb.IsShow_Ok,
+				}).Error; err != nil {
+				zaplog.Error("UpdatePenBehaviorWeekData", zap.Error(err))
+			}
+			continue
+		}
+
+		// 按pen_id分组计算统计数据
+		penStats := make(map[int32]*model.PenBehaviorWeekModel)
+		for _, v := range historyList {
+			if stats, exists := penStats[v.PenId]; exists {
+				stats.CowCount++
+				stats.SumRumina += v.RuminaRate
+				stats.SumIntake += v.IntakeRate
+				stats.SumRest += v.RestRate
+				stats.SumGasp += v.GaspRate
+				// 计算标准差
+				stats.RuminaRate = append(stats.RuminaRate, float64(v.RuminaRate))
+				stats.IntakeRate = append(stats.IntakeRate, float64(v.IntakeRate))
+				stats.RestRate = append(stats.RestRate, float64(v.RestRate))
+				stats.GaspRate = append(stats.GaspRate, float64(v.GaspRate))
+			} else {
+				penStats[v.PenId] = &model.PenBehaviorWeekModel{
+					CowCount:   1,
+					SumRumina:  v.RuminaRate,
+					SumIntake:  v.IntakeRate,
+					SumRest:    v.RestRate,
+					SumGasp:    v.GaspRate,
+					RuminaRate: []float64{float64(v.RuminaRate)},
+					IntakeRate: []float64{float64(v.IntakeRate)},
+					RestRate:   []float64{float64(v.RestRate)},
+					GaspRate:   []float64{float64(v.GaspRate)},
+				}
+			}
+		}
+
+		// 更新当前记录
+		if stats, exists := penStats[item.PenId]; exists {
+			if err = e.DB.Model(new(model.PenBehavior)).
+				Where("id = ?", item.Id).
+				Updates(map[string]interface{}{
+					"week_rumina_rate": int32(float64(stats.SumRumina) / float64(stats.CowCount)),
+					"week_intake_rate": int32(float64(stats.SumIntake) / float64(stats.CowCount)),
+					"week_rest_rate":   int32(float64(stats.SumRest) / float64(stats.CowCount)),
+					"week_gasp_rate":   int32(float64(stats.SumGasp) / float64(stats.CowCount)),
+					"rumina_std":       int32(calculateStd(stats.RuminaRate)),
+					"intake_std":       int32(calculateStd(stats.IntakeRate)),
+					"rest_std":         int32(calculateStd(stats.RuminaRate)),
+					"gasp_std":         int32(calculateStd(stats.GaspRate)),
+					"is_show":          pasturePb.IsShow_Ok,
+				}).Error; err != nil {
+				zaplog.Error("UpdatePenBehaviorWeekData", zap.Error(err))
+			}
+		} else {
+			// 如果没有历史数据,标记为-1
+			if err = e.DB.Model(new(model.PenBehavior)).
+				Where("id = ?", item.Id).
+				Updates(map[string]interface{}{
+					"week_rumina_rate": -1,
+					"week_intake_rate": -1,
+					"week_rest_rate":   -1,
+					"week_gasp_rate":   -1,
+					"is_show":          pasturePb.IsShow_Ok,
+				}).Error; err != nil {
+				zaplog.Error("UpdatePenBehaviorWeekData", zap.Error(err))
+			}
+		}
+	}
+}
+
+// findHistoryPenBehaviorList 获取历史数据
+func (e *Entry) findHistoryPenBehaviorList(pastureId int64, startTime, endTime string, frameid int32) []*model.PenBehavior {
+	res := make([]*model.PenBehavior, 0)
+	if err := e.DB.Model(new(model.PenBehavior)).
+		Where("pasture_id = ?", pastureId).
+		Where("heat_date BETWEEN ? AND ?", startTime, endTime).
+		Where("frameid = ?", frameid).
+		Find(&res).Error; err != nil {
+		zaplog.Error("findHistoryPenBehaviorList", zap.Error(err))
+		return nil
+	}
+	return res
+}
+
+// calculateStd 计算标准差
+func calculateStd(values []float64) float64 {
+	if len(values) == 0 {
+		return 0
+	}
+	// 计算平均值
+	var sum float64
+	for _, v := range values {
+		sum += v
 	}
+	mean := sum / float64(len(values))
+
+	// 计算方差
+	var variance float64
+	for _, v := range values {
+		diff := v - mean
+		variance += diff * diff
+	}
+	variance /= float64(len(values))
+
+	// 返回标准差
+	return math.Sqrt(variance)
 }
 
 // calculateActiveTime 计算活动时间
@@ -255,6 +369,40 @@ func (e *Entry) calculateActiveTime(heatDate string, frameid int32) string {
 	return finalTime.Format(model.LayoutTime)
 }
 
+// isExistByPenBehavior 是否存在
+func (e *Entry) isExistByPenBehavior(pastureId int64, heatDate string, penId int32, frameid int32) bool {
+	var count int64
+	if err := e.DB.Model(new(model.PenBehavior)).
+		Where("pasture_id = ? AND heat_date = ? AND frameid = ? AND pen_id = ?", pastureId, heatDate, penId, frameid).
+		Count(&count).Error; err != nil {
+		return false
+	}
+	return count > 0
+}
+
+func (e *Entry) findPenBehavior(pastureId int64, heatDate string, penId int32, frameid int32) *model.PenBehavior {
+	res := &model.PenBehavior{}
+	if err := e.DB.Model(new(model.PenBehavior)).
+		Where("pasture_id = ? AND heat_date = ? AND frameid = ? AND pen_id = ?", pastureId, heatDate, penId, frameid).
+		First(res).Error; err != nil {
+		return nil
+	}
+	return res
+}
+
+func (e *Entry) findWeekPenBehaviorList(pastureId int64) []*model.PenBehavior {
+	res := make([]*model.PenBehavior, 0)
+	if err := e.DB.Model(new(model.PenBehavior)).
+		Where("pasture_id = ?", pastureId).
+		Where("is_show = ?", pasturePb.IsShow_No).
+		Where("cow_count >= ?", model.PenBehaviorMinCowCount).
+		Limit(int(defaultLimit)).
+		Find(&res).Error; err != nil {
+		return nil
+	}
+	return res
+}
+
 // ifThenElse 条件判断函数
 func ifThenElse(condition bool, a, b int32) int32 {
 	if condition {