Browse Source

mqtt: update

Yi 2 months ago
parent
commit
da7910a69f

+ 1 - 1
.drone.yml

@@ -30,7 +30,7 @@ steps:
         from_secret: aliyuncs_password
       repo: registry.cn-hangzhou.aliyuncs.com/kpt-event/kpt-pasture
       registry: registry.cn-hangzhou.aliyuncs.com
-      tags: [ http.test,mqtt-test ]
+      tags: [ http-test,mqtt-test ]
 
 trigger:
   branch:

+ 6 - 7
config/app.develop.yaml

@@ -36,15 +36,14 @@ side_work_setting:
       default: 5
 cron:
   crontab_start_run: false
-  update_cow_info: "0 01 1 * * ?"      # 每天凌晨1点01分执行
-  generate_work_order: "0 05 1 * * ?"  # 每天凌晨1点05分执行
-  immunization_plan: "0 10 1 * * ?"    # 每天凌晨1点10分执行
-  same_time_plan: "0 15 1 * * ?"      # 每天凌晨1点15分执行
-  update_same_time: "0 20 1 * * ?"    # 每天凌晨1点20分执行
+  update_cow_info: "0 01 1 * * ?"       # 每天凌晨1点01分执行
+  generate_work_order: "0 05 1 * * ?"   # 每天凌晨1点05分执行
+  immunization_plan: "0 10 1 * * ?"     # 每天凌晨1点10分执行
+  same_time_plan: "0 15 1 * * ?"        # 每天凌晨1点15分执行
+  update_same_time: "0 20 1 * * ?"      # 每天凌晨1点20分执行
   system_basic_crontab: "0 25 1 * * ?"  # 每天凌晨1点25分执行
   cow_pregnant: "0 00 15 * * ?"         # 每天15点执行
-  update_active_habit: "0 */1 * * * ?"  # 每2分钟执行一次
-  neck_ring_estrus: "0 */5 * * * ?"     # 每5分钟执行一次
+  neck_ring_estrus: "0 0 */1 * * ?"     # 每1个小时执行一次
 
 mqtt:
   broker: "kptyun.com:1983"

+ 52 - 0
http/handler/test.go

@@ -0,0 +1,52 @@
+package handler
+
+import (
+	"kpt-pasture/http/middleware"
+	"net/http"
+
+	pasturePb "gitee.com/xuyiping_admin/go_proto/proto/go/backend/cow"
+
+	"gitee.com/xuyiping_admin/pkg/apierr"
+
+	operationPb "gitee.com/xuyiping_admin/go_proto/proto/go/backend/operation"
+	"gitee.com/xuyiping_admin/pkg/ginutil"
+	"github.com/gin-gonic/gin"
+)
+
+func CowNeckRingBound(c *gin.Context) {
+	pagination := &pasturePb.PaginationModel{
+		Page:       int32(c.GetInt(middleware.Page)),
+		PageSize:   int32(c.GetInt(middleware.PageSize)),
+		PageOffset: int32(c.GetInt(middleware.PageOffset)),
+	}
+
+	if err := middleware.BackendOperation(c).OpsService.CowNeckRingNumberBound(c, pagination); err != nil {
+		apierr.ClassifiedAbort(c, err)
+		return
+	}
+
+	ginutil.JSONResp(c, &operationPb.CommonOK{
+		Code: http.StatusOK,
+		Msg:  "ok",
+		Data: &operationPb.Success{Success: true},
+	})
+}
+
+func UpdateCowPen(c *gin.Context) {
+	pagination := &pasturePb.PaginationModel{
+		Page:       int32(c.GetInt(middleware.Page)),
+		PageSize:   int32(c.GetInt(middleware.PageSize)),
+		PageOffset: int32(c.GetInt(middleware.PageOffset)),
+	}
+
+	if err := middleware.BackendOperation(c).OpsService.UpdateCowPen(c, pagination); err != nil {
+		apierr.ClassifiedAbort(c, err)
+		return
+	}
+
+	ginutil.JSONResp(c, &operationPb.CommonOK{
+		Code: http.StatusOK,
+		Msg:  "ok",
+		Data: &operationPb.Success{Success: true},
+	})
+}

+ 1 - 0
http/route/route.go

@@ -16,6 +16,7 @@ func HTTPServerRoute(opts ...func(engine *gin.Engine)) func(s *gin.Engine) {
 		DashboardApi(opts...),
 		WorkOrderAPI(opts...),
 		FilesManageAPI(opts...),
+		TestAPI(opts...),
 	}
 
 	return func(s *gin.Engine) {

+ 19 - 0
http/route/test_api.go

@@ -0,0 +1,19 @@
+package route
+
+import (
+	"kpt-pasture/http/handler"
+
+	"github.com/gin-gonic/gin"
+)
+
+func TestAPI(opts ...func(engine *gin.Engine)) func(s *gin.Engine) {
+	return func(s *gin.Engine) {
+		for _, opt := range opts {
+			opt(s)
+		}
+		// test API 组  测试接口
+		testRoute := authRouteGroup(s, "/api/v1/test/")
+		testRoute.POST("/cow/neck_ring/bound", handler.CowNeckRingBound)
+		testRoute.POST("/cow/pen/update", handler.UpdateCowPen)
+	}
+}

+ 26 - 21
model/event_estrus.go

@@ -8,27 +8,28 @@ import (
 )
 
 type EventEstrus struct {
-	Id                int64                           `json:"id"`
-	PastureId         int64                           `json:"pastureId"`
-	CowId             int64                           `json:"cowId"`
-	Lact              int32                           `json:"lact"`
-	ExposeEstrusType  pasturePb.ExposeEstrusType_Kind `json:"exposeEstrusType"`
-	FilterHigh        int32                           `json:"filter_high"`
-	EstrusDate        string                          `json:"estrusDate"`
-	ActiveDate        string                          `json:"activeDate"`
-	LastEstrusDate    string                          `json:"lastEstrusDate"`
-	Level             pasturePb.EstrusLevel_Kind      `json:"level"`
-	IsPeak            pasturePb.IsShow_Kind           `json:"isPeak"`
-	PerTwentyFourHigh int32                           `json:"perTwentyFourHigh"`
-	Result            pasturePb.EstrusResult_Kind     `json:"result"`
-	Remarks           string                          `json:"remarks"`
-	IsShow            pasturePb.IsShow_Kind           `json:"isShow"`
-	OperationId       int64                           `json:"operationId"`
-	OperationName     string                          `json:"operationName"`
-	MessageId         int64                           `json:"messageId"`
-	MessageName       string                          `json:"messageName"`
-	CreatedAt         int64                           `json:"createdAt"`
-	UpdatedAt         int64                           `json:"updatedAt"`
+	Id               int64                           `json:"id"`
+	PastureId        int64                           `json:"pastureId"`
+	NeckRingNumber   string                          `json:"neckRingNumber"`
+	CowId            int64                           `json:"cowId"`
+	Lact             int32                           `json:"lact"`
+	ExposeEstrusType pasturePb.ExposeEstrusType_Kind `json:"exposeEstrusType"`
+	FilterHigh       int32                           `json:"filter_high"`
+	EstrusDate       string                          `json:"estrusDate"`
+	ActiveDate       string                          `json:"activeDate"`
+	LastEstrusDate   string                          `json:"lastEstrusDate"`
+	Level            pasturePb.EstrusLevel_Kind      `json:"level"`
+	IsPeak           pasturePb.IsShow_Kind           `json:"isPeak"`
+	DaylongHigh      int32                           `json:"daylongHigh"`
+	Result           pasturePb.EstrusResult_Kind     `json:"result"`
+	Remarks          string                          `json:"remarks"`
+	IsShow           pasturePb.IsShow_Kind           `json:"isShow"`
+	OperationId      int64                           `json:"operationId"`
+	OperationName    string                          `json:"operationName"`
+	MessageId        int64                           `json:"messageId"`
+	MessageName      string                          `json:"messageName"`
+	CreatedAt        int64                           `json:"createdAt"`
+	UpdatedAt        int64                           `json:"updatedAt"`
 }
 
 func (e *EventEstrus) TableName() string {
@@ -37,6 +38,8 @@ func (e *EventEstrus) TableName() string {
 
 func NewEventEstrus(
 	exposeEstrusType pasturePb.ExposeEstrusType_Kind,
+	level pasturePb.EstrusLevel_Kind,
+	isShow pasturePb.IsShow_Kind,
 	cow *Cow,
 	estrusDate, remarks string,
 ) *EventEstrus {
@@ -45,6 +48,8 @@ func NewEventEstrus(
 		Lact:             cow.Lact,
 		ExposeEstrusType: exposeEstrusType,
 		EstrusDate:       estrusDate,
+		Level:            level,
+		IsShow:           isShow,
 		Remarks:          remarks,
 	}
 }

+ 27 - 20
model/neck_active_habit.go

@@ -9,6 +9,8 @@ import (
 const (
 	InitChangeFilter     = -10000
 	DefaultChangeFilter  = -99
+	DefaultRuminaFilter  = -99
+	DefaultChewFilter    = -99
 	DefaultFilterCorrect = 100
 	DefaultWeeklyActive  = 1500
 )
@@ -17,6 +19,9 @@ type NeckActiveHabit struct {
 	Id                      int64                 `json:"id"`
 	PastureId               int64                 `json:"pastureId"`
 	NeckRingNumber          string                `json:"neckRingNumber"`
+	CowId                   int64                 `json:"cowId"`
+	Lact                    int32                 `json:"lact"`
+	CalvingAge              int32                 `json:"calvingAge"`
 	ActiveTime              string                `json:"activeTime"`
 	Frameid                 int32                 `json:"frameid"`
 	HeatDate                string                `json:"heatDate"`
@@ -58,6 +63,7 @@ type NeckActiveHabit struct {
 	IsMaxTime               pasturePb.IsShow_Kind `json:"isMaxTime"`
 	IsShow                  pasturePb.IsShow_Kind `json:"isShow"`
 	RecordCount             int32                 `json:"recordCount"`
+	FirmwareVersion         int32                 `json:"firmwareVersion"`
 	CreatedAt               int64                 `json:"createdAt"`
 	UpdatedAt               int64                 `json:"updatedAt"`
 }
@@ -68,26 +74,27 @@ func (n *NeckActiveHabit) TableName() string {
 
 func NewNeckActiveHabit(data *NeckRingOriginalMerge) *NeckActiveHabit {
 	return &NeckActiveHabit{
-		PastureId:      data.PastureId,
-		Frameid:        data.XframeId,
-		HeatDate:       data.ActiveDate,
-		NeckRingNumber: data.NeckRingNumber,
-		Active:         data.Active,
-		Gasp:           data.Gasp,
-		High:           data.High,
-		Inactive:       data.Inactive,
-		Intake:         data.Intake,
-		Other:          data.Other,
-		Rumina:         data.Rumina,
-		WeekHigh:       DefaultWeeklyActive,
-		IsShow:         pasturePb.IsShow_No,
-		IsMaxTime:      pasturePb.IsShow_No,
-		ChangeFilter:   InitChangeFilter,
-		FilterCorrect:  DefaultFilterCorrect,
-		RuminaFilter:   InitChangeFilter,
-		ChewFilter:     InitChangeFilter,
-		ActiveTime:     fmt.Sprintf("%s %02d:00:00", data.ActiveDate, data.XframeId*2),
-		RecordCount:    data.RecordCount,
+		PastureId:       data.PastureId,
+		Frameid:         data.XframeId,
+		HeatDate:        data.ActiveDate,
+		NeckRingNumber:  data.NeckRingNumber,
+		Active:          data.Active,
+		Gasp:            data.Gasp,
+		High:            data.High,
+		Inactive:        data.Inactive,
+		Intake:          data.Intake,
+		Other:           data.Other,
+		Rumina:          data.Rumina,
+		WeekHigh:        DefaultWeeklyActive,
+		IsShow:          pasturePb.IsShow_No,
+		IsMaxTime:       pasturePb.IsShow_No,
+		ChangeFilter:    InitChangeFilter,
+		FilterCorrect:   InitChangeFilter,
+		RuminaFilter:    InitChangeFilter,
+		ChewFilter:      InitChangeFilter,
+		ActiveTime:      fmt.Sprintf("%s %02d:00:00", data.ActiveDate, data.XframeId*2+1),
+		RecordCount:     data.RecordCount,
+		FirmwareVersion: data.FirmwareVersion,
 	}
 }
 

+ 33 - 0
model/neck_ring_bar_change.go

@@ -0,0 +1,33 @@
+package model
+
+type NeckRingBarChange struct {
+	Id             int64  `json:"id"`
+	PastureId      int64  `json:"pastureId"`
+	NeckRingNumber string `json:"neckRingNumber"`
+	HeatDate       string `json:"heatDate"`
+	FrameId        int32  `json:"frameId"`
+	PenId          int32  `json:"penId"`
+	PenName        string `json:"penName"`
+	Nb             int32  `json:"nb"`
+	ChangeHigh     int32  `json:"changeHigh"`
+	ChangeFilter   int32  `json:"changeFilter"`
+	CreatedAt      int64  `json:"createdAt"`
+	UpdatedAt      int64  `json:"updatedAt"`
+}
+
+func (n *NeckRingBarChange) TableName() string {
+	return "neck_ring_bar_change"
+}
+
+func NewNeckRingBarChange(neckRingNumber, heatDate string, frameId, nb, changeHigh, changeFilter int32, pen *Pen) *NeckRingBarChange {
+	return &NeckRingBarChange{
+		PastureId:    pen.PastureId,
+		HeatDate:     heatDate,
+		FrameId:      frameId,
+		PenId:        pen.Id,
+		PenName:      pen.Name,
+		ChangeHigh:   changeHigh,
+		ChangeFilter: changeFilter,
+		Nb:           nb,
+	}
+}

+ 15 - 13
model/neck_ring_original.go

@@ -80,19 +80,20 @@ const (
 )
 
 type NeckRingOriginalMerge struct {
-	Rumina         int32
-	Inactive       int32
-	Active         int32
-	Intake         int32
-	Other          int32
-	High           int32
-	Gasp           int32
-	ActiveDate     string
-	NeckRingNumber string
-	XframeId       int32
-	ActiveDateType pasturePb.ActiveTimeType_Kind
-	RecordCount    int32
-	PastureId      int64
+	Rumina          int32
+	Inactive        int32
+	Active          int32
+	Intake          int32
+	Other           int32
+	High            int32
+	Gasp            int32
+	ActiveDate      string
+	NeckRingNumber  string
+	XframeId        int32
+	ActiveDateType  pasturePb.ActiveTimeType_Kind
+	RecordCount     int32
+	PastureId       int64
+	FirmwareVersion int32
 }
 
 func (n *NeckRingOriginalMerge) IsMageData(data *NeckRingOriginal, xframeId int32) {
@@ -118,6 +119,7 @@ func (n *NeckRingOriginalMerge) IsMageData(data *NeckRingOriginal, xframeId int3
 	n.NeckRingNumber = data.NeckRingNumber
 	n.XframeId = xframeId
 	n.PastureId = data.PastureId
+	n.FirmwareVersion = data.FirmwareVersion
 }
 
 func (n *NeckRingOriginalMerge) SumAvg() {

+ 10 - 9
model/pen.go

@@ -10,19 +10,20 @@ const IsAllYes = "Yes"
 
 type Pen struct {
 	Id                int32                 `json:"id"`
+	PastureId         int64                 `json:"pastureId"`
 	Name              string                `json:"name"`
 	Remarks           string                `json:"remarks"`
-	PenType           int32                 `json:"pen_type"`
+	PenType           int32                 `json:"penType"`
 	Lengths           int32                 `json:"lengths"`
 	Widths            int32                 `json:"widths"`
-	DoctrinalCapacity int32                 `json:"doctrinal_capacity"`
-	ActualCapacity    int32                 `json:"actual_capacity"`
-	BedNumber         int32                 `json:"bed_number"`
-	NeckNumber        int32                 `json:"neck_number"`
-	IsShow            pasturePb.IsShow_Kind `json:"is_show"`
-	IsDelete          pasturePb.IsShow_Kind `json:"is_delete"`
-	CreatedAt         int64                 `json:"created_at"`
-	UpdatedAt         int64                 `json:"updated_at"`
+	DoctrinalCapacity int32                 `json:"doctrinalCapacity"`
+	ActualCapacity    int32                 `json:"actualCapacity"`
+	BedNumber         int32                 `json:"bedNumber"`
+	NeckNumber        int32                 `json:"neckNumber"`
+	IsShow            pasturePb.IsShow_Kind `json:"isShow"`
+	IsDelete          pasturePb.IsShow_Kind `json:"isDelete"`
+	CreatedAt         int64                 `json:"createdAt"`
+	UpdatedAt         int64                 `json:"updatedAt"`
 }
 
 func (p *Pen) TableName() string {

+ 2 - 2
module/backend/goods.go

@@ -234,7 +234,7 @@ func (s *StoreEntry) NeckRingList(ctx context.Context, req *pasturePb.SearchNeck
 	pref := s.DB.Model(new(model.NeckRing)).
 		Where("status >= ?", pasturePb.NeckRingStatus_Unbind).
 		Where("pasture_id = ?", currentUser.PastureId).
-		Where("number != ''")
+		Where("neck_ring_number != ''")
 	if req.Status > 0 {
 		pref.Where("status = ?", req.Status)
 	}
@@ -247,7 +247,7 @@ func (s *StoreEntry) NeckRingList(ctx context.Context, req *pasturePb.SearchNeck
 		pref.Where("number like ?", fmt.Sprintf("%s%s%s", "%", req.Number, "%"))
 	}
 
-	if err := pref.Order("id desc").
+	if err = pref.Order("id desc").
 		Count(&count).
 		Limit(int(pagination.PageSize)).
 		Offset(int(pagination.PageOffset)).

+ 6 - 0
module/backend/interface.go

@@ -52,6 +52,7 @@ type KptService interface {
 	AnalyseService       // 分析相关
 	DashboardService     // 牧场统计相关
 	WorkService          // 日常工作相关
+	TestService          // 测试相关
 }
 
 //go:generate mockgen -destination mock/SystemService.go -package kptservicemock kpt-pasture/module/backend SystemService
@@ -277,3 +278,8 @@ type WorkService interface {
 	CowDiseaseList(ctx context.Context, req *pasturePb.SearchEventCowTreatmentRequest, pagination *pasturePb.PaginationModel) (*pasturePb.EventCowDiseaseResponse, error)
 	EstrusList(ctx context.Context, req *pasturePb.EstrusItemsRequest, pagination *pasturePb.PaginationModel) (*pasturePb.EstrusItemsResponse, error)
 }
+
+type TestService interface {
+	CowNeckRingNumberBound(ctx context.Context, pagination *pasturePb.PaginationModel) error
+	UpdateCowPen(ctx context.Context, pagination *pasturePb.PaginationModel) error
+}

+ 8 - 2
module/backend/pasture.go

@@ -45,16 +45,21 @@ func (s *StoreEntry) SearchBarnList(ctx context.Context, req *pasturePb.SearchNa
 }
 
 func (s *StoreEntry) CreateOrUpdateBarn(ctx context.Context, req *pasturePb.SearchBarnList) error {
+	currentUser, err := s.GetCurrentSystemUser(ctx)
+	if err != nil {
+		return xerr.Custom("当前用户信息错误,请退出重新登录")
+	}
+
 	if req.Id > 0 {
 		barn := &model.Pen{Id: req.Id}
-		if err := s.DB.Model(&model.Pen{}).First(barn).Error; err != nil {
+		if err = s.DB.Model(&model.Pen{}).First(barn).Error; err != nil {
 			if !errors.Is(err, gorm.ErrRecordNotFound) {
 				return xerr.WithStack(err)
 			}
 		}
 	}
 
-	if err := s.DB.Model(&model.Pen{}).Where(map[string]interface{}{
+	if err = s.DB.Model(&model.Pen{}).Where(map[string]interface{}{
 		"id": req.Id,
 	}).Assign(map[string]interface{}{
 		"name":               req.Name,
@@ -65,6 +70,7 @@ func (s *StoreEntry) CreateOrUpdateBarn(ctx context.Context, req *pasturePb.Sear
 		"doctrinal_capacity": req.DoctrinalCapacity,
 		"bed_number":         req.BedNumber,
 		"neck_number":        req.NeckNumber,
+		"pasture_id":         currentUser.PastureId,
 		"is_delete":          pasturePb.IsShow_Ok,
 		"is_show":            pasturePb.IsShow_Ok,
 	}).FirstOrCreate(&model.Pen{}).Error; err != nil {

+ 1 - 1
module/backend/sql.go

@@ -376,7 +376,7 @@ func (s *StoreEntry) DiseaseMaps(ctx context.Context) (map[int64]*model.Disease,
 func (s *StoreEntry) NeckRingIsExist(ctx context.Context, pastureId int64, number string) (*model.NeckRing, bool) {
 	neckRing := &model.NeckRing{}
 	if err := s.DB.Model(new(model.NeckRing)).
-		Where("number = ?", number).
+		Where("neck_ring_number = ?", number).
 		Where("pasture_id = ?", pastureId).
 		First(neckRing).Error; err != nil {
 		if errors.Is(err, gorm.ErrRecordNotFound) {

+ 80 - 0
module/backend/test_service.go

@@ -0,0 +1,80 @@
+package backend
+
+import (
+	"context"
+	"kpt-pasture/model"
+
+	pasturePb "gitee.com/xuyiping_admin/go_proto/proto/go/backend/cow"
+
+	"gitee.com/xuyiping_admin/pkg/logger/zaplog"
+	"go.uber.org/zap"
+
+	"gitee.com/xuyiping_admin/pkg/xerr"
+)
+
+func (s *StoreEntry) CowNeckRingNumberBound(ctx context.Context, pagination *pasturePb.PaginationModel) error {
+	currentUser, err := s.GetCurrentSystemUser(ctx)
+	if err != nil {
+		return xerr.Custom("当前用户信息错误,请退出重新登录")
+	}
+
+	lastNeckRing := &model.NeckRing{}
+	if err = s.DB.Model(new(model.NeckRing)).
+		Order("cow_id desc").
+		First(lastNeckRing).Error; err != nil {
+		return xerr.WithStack(err)
+	}
+
+	cowList := make([]*model.Cow, 0)
+	if err = s.DB.Model(new(model.Cow)).
+		Where("id > ?", lastNeckRing.CowId).
+		Limit(int(pagination.PageSize)).
+		Offset(int(pagination.PageOffset)).
+		Find(&cowList).Error; err != nil {
+		return xerr.WithStack(err)
+	}
+
+	newNeckRingList := make([]*model.NeckRing, 0)
+	newNeckRingBindLogList := make([]*model.NeckRingBindLog, 0)
+	for _, v := range cowList {
+		newNeckRing := model.NewNeckRing(currentUser.PastureId, v.NeckRingNumber, v.Id, currentUser)
+		newNeckRingList = append(newNeckRingList, newNeckRing)
+		newNeckRingBindLog := model.NewNeckRingBindLog(v.NeckRingNumber, v.Id, currentUser)
+		newNeckRingBindLogList = append(newNeckRingBindLogList, newNeckRingBindLog)
+	}
+
+	if err = s.DB.Model(new(model.NeckRing)).
+		Create(newNeckRingList).Error; err != nil {
+		zaplog.Error("CowNeckRingNumberBound-NewNeckRing", zap.Any("error", err))
+	}
+
+	if err = s.DB.Model(new(model.NeckRingBindLog)).
+		Create(newNeckRingBindLogList).Error; err != nil {
+		zaplog.Error("CowNeckRingNumberBound-NeckRingBindLog", zap.Any("error", err))
+	}
+	return nil
+}
+
+func (s *StoreEntry) UpdateCowPen(ctx context.Context, pagination *pasturePb.PaginationModel) error {
+	cowList := make([]*model.Cow, 0)
+	if err := s.DB.Model(new(model.Cow)).
+		Where("pen_name = ?", "").
+		Limit(int(pagination.PageSize)).
+		Offset(int(pagination.PageOffset)).
+		Find(&cowList).Error; err != nil {
+		return xerr.WithStack(err)
+	}
+	penMap := s.PenMap(ctx, 1)
+	for _, v := range cowList {
+		pen := penMap[v.PenId]
+		if pen == nil {
+			continue
+		}
+		if err := s.DB.Model(new(model.Cow)).
+			Where("id = ?", v.Id).
+			Update("pen_name", pen.Name).Error; err != nil {
+			zaplog.Error("UpdateCowPen", zap.Any("error", err))
+		}
+	}
+	return nil
+}

+ 57 - 79
module/crontab/neck_ring_estrus.go

@@ -38,27 +38,11 @@ func (e *Entry) PastureUpdateCowEstrus() (err error) {
 }
 
 func (e *Entry) EntryCowEstrus(pastureId int64) (err error) {
-	activeLow, err := e.GetSystemConfigure(pastureId, model.ActiveLow)
-	if err != nil {
-		return xerr.WithStack(err)
-	}
-	activeLowValue := int64(activeLow.Value)
-	activeMiddle, err := e.GetSystemConfigure(pastureId, model.ActiveMiddle)
-	if err != nil {
-		return xerr.WithStack(err)
-	}
-	activeMiddleValue := int64(activeMiddle.Value)
-	activeHigh, err := e.GetSystemConfigure(pastureId, model.ActiveHigh)
-	if err != nil {
-		return xerr.WithStack(err)
-	}
-	activeHighValue := int64(activeHigh.Value)
-	lastMaxEstrus, err := e.GetSystemConfigure(pastureId, model.MaxEstrus)
-	if err != nil {
-		return xerr.WithStack(err)
-	}
-	lastMaxEstrusId := int64(lastMaxEstrus.Value)
-	zaplog.Info("EntryCowEstrus-001", zap.Any("lastMaxEstrusId", lastMaxEstrusId))
+	activeLowValue := e.GetSystemConfigure(pastureId, model.ActiveLow).Value
+	activeMiddleValue := e.GetSystemConfigure(pastureId, model.ActiveMiddle).Value
+	activeHighValue := e.GetSystemConfigure(pastureId, model.ActiveHigh).Value
+	lastMaxEstrusId := e.GetSystemConfigure(pastureId, model.MaxEstrus).Value
+
 	currentMaxHabit := &model.NeckActiveHabit{}
 	if err = e.DB.Model(new(model.NeckActiveHabit)).
 		Where("id > ?", lastMaxEstrusId).
@@ -66,7 +50,11 @@ func (e *Entry) EntryCowEstrus(pastureId int64) (err error) {
 		First(currentMaxHabit).Error; err != nil {
 		return xerr.WithStack(err)
 	}
-	zaplog.Info("EntryCowEstrus-002", zap.Any("currentMaxHabit", currentMaxHabit))
+	// 当前Id<=上次执行的id,则不执行
+	if currentMaxHabit.Id <= lastMaxEstrusId {
+		return nil
+	}
+
 	defer func() {
 		if err == nil {
 			e.DB.Model(new(model.SystemConfigure)).
@@ -82,34 +70,22 @@ func (e *Entry) EntryCowEstrus(pastureId int64) (err error) {
 		First(xToday).Error; err != nil {
 		return xerr.WithStack(err)
 	}
-	zaplog.Info("EntryCowEstrus-003", zap.Any("xToday", xToday))
-	// 当前Id<=上次执行的id,则不执行
-	if currentMaxHabit.Id <= int64(lastMaxEstrusId) {
-		return nil
-	}
 
-	xToday.LastMaxHabitId = int64(lastMaxEstrusId)
+	xToday.LastMaxHabitId = lastMaxEstrusId
 	xToday.CurrMaxHabitId = currentMaxHabit.Id
-	xToday.ActiveLow = int64(activeLowValue)
-	xToday.ActiveMiddle = int64(activeMiddleValue)
-	xToday.ActiveHigh = int64(activeHighValue)
-	zaplog.Info("EntryCowEstrus-005", zap.Any("xToday", xToday))
+	xToday.ActiveLow = activeLowValue
+	xToday.ActiveMiddle = activeMiddleValue
+	xToday.ActiveHigh = activeHighValue
 	if err = e.CowEstrusWarning(xToday); err != nil {
-		return xerr.WithStack(err)
+		zaplog.Error("EntryCowEstrus", zap.Any("CowEstrusWarning", err), zap.Any("xToday", xToday))
 	}
-	zaplog.Info("EntryCowEstrus-006", zap.Any("xToday", xToday))
 	return nil
 }
 
-func (e *Entry) CowEstrusWarning(xToday *XToday) error {
-	startDate, err := time.Parse(model.LayoutDate2, xToday.XBegDate)
-	if err != nil {
-		return xerr.WithStack(err)
-	}
-	endDate, err := time.Parse(model.LayoutDate2, xToday.XEndDate)
-	if err != nil {
-		return xerr.WithStack(err)
-	}
+// CowEstrusWarning 发情预警
+func (e *Entry) CowEstrusWarning(xToday *XToday) (err error) {
+	startDate, _ := time.Parse(model.LayoutDate2, xToday.XBegDate)
+	endDate, _ := time.Parse(model.LayoutDate2, xToday.XEndDate)
 
 	for startDate.Format(model.LayoutDate2) <= endDate.Format(model.LayoutDate2) {
 		neckActiveHabitList := make([]*model.NeckActiveHabit, 0)
@@ -119,10 +95,11 @@ func (e *Entry) CowEstrusWarning(xToday *XToday) error {
 			Where("heat_date = ?", startDate.Format(model.LayoutDate2)).
 			Where("cow_id > ?", 0).
 			Where(e.DB.Where("calving_age > ?", MinCalvingAge).Or("lact = ?", MinLact)).
-			Group("cow_id").
+			Group("neck_ring_number").
 			Find(&neckActiveHabitList).
 			Error; err != nil {
-			return xerr.WithStack(err)
+			zaplog.Error("CowEstrusWarning", zap.Any("FindNeckActiveHabit", err), zap.Any("xToday", xToday))
+			continue
 		}
 
 		eventEstrusList := make([]*model.EventEstrus, 0)
@@ -147,34 +124,28 @@ func (e *Entry) CowEstrusWarning(xToday *XToday) error {
 				}
 				cft = float32(v.ChangeFilter)*float32(v.FilterCorrect)/100 - value
 			}
-			cowInfo := e.FindCowInfoByNeckRingNumber(v.NeckRingNumber)
-			if cowInfo == nil {
-				zaplog.Error("CowEstrusWarning", zap.Any("FindCowInfoByNeckRingNumber", err), zap.Any("NeckRingNumber", v.NeckRingNumber))
-				continue
-			}
+
 			// 最近3天最大发情记录,小于该变化趋势的不再插入
-			eventEstrus := e.GetBeforeThreeDaysCowEstrus(cowInfo.Id, startDate.AddDate(0, 0, -2).Format(model.LayoutTime))
-			if eventEstrus.CowId != cowInfo.Id {
-				if int32(cft) <= eventEstrus.PerTwentyFourHigh {
-					continue
-				}
+			eventEstrus := e.GetBeforeThreeDaysCowEstrus(v.CowId, startDate.AddDate(0, 0, -2).Format(model.LayoutTime))
+			if int32(cft) <= eventEstrus.DaylongHigh {
+				continue
 			}
+
 			// 判断最近50天内是否存在发情记录(发情等级>=2),如果18~25天@xadjust21,如果36~50天@xadjust42
-			cowEstrus := e.GetTwoEstrus(cowInfo.Id, startDate.AddDate(0, 0, -100).Format(model.LayoutTime), startDate.AddDate(0, 0, -2).Format(model.LayoutTime))
-			if cowEstrus.CowId == cowInfo.Id {
-				activeDateTime, _ := time.Parse(model.LayoutTime, cowEstrus.ActiveDate)
-				if activeDateTime.Unix() >= startDate.AddDate(0, 0, -25).Unix() && activeDateTime.Unix() <= startDate.AddDate(0, 0, -18).Unix() {
-					cowEstrus.HadJust = XAdjust21
-				}
-				if activeDateTime.Unix() >= startDate.AddDate(0, 0, -50).Unix() && activeDateTime.Unix() <= startDate.AddDate(0, 0, -36).Unix() {
-					cowEstrus.HadJust = XAdjust42
-				}
+			cowEstrus := e.GetTwoEstrus(v.CowId, startDate.AddDate(0, 0, -100).Format(model.LayoutTime), startDate.AddDate(0, 0, -2).Format(model.LayoutTime))
+			activeDateTime, _ := time.Parse(model.LayoutTime, cowEstrus.ActiveDate)
+			if activeDateTime.Unix() >= startDate.AddDate(0, 0, -25).Unix() && activeDateTime.Unix() <= startDate.AddDate(0, 0, -18).Unix() {
+				cowEstrus.HadJust = XAdjust21
+			}
+			if activeDateTime.Unix() >= startDate.AddDate(0, 0, -50).Unix() && activeDateTime.Unix() <= startDate.AddDate(0, 0, -36).Unix() {
+				cowEstrus.HadJust = XAdjust42
 			}
 
 			if int32(cft)+cowEstrus.HadJust <= int32(xToday.ActiveLow) {
 				continue
 			}
 
+			cowInfo := e.FindCowInfoByNeckRingNumber(v.NeckRingNumber)
 			level := pasturePb.EstrusLevel_High
 			if int32(cft)+cowEstrus.HadJust < int32(xToday.ActiveMiddle) {
 				level = pasturePb.EstrusLevel_Low
@@ -184,7 +155,7 @@ func (e *Entry) CowEstrusWarning(xToday *XToday) error {
 			}
 
 			result := pasturePb.EstrusResult_Invalid
-			if eventEstrus.Result == pasturePb.EstrusResult_Fail && eventEstrus.PerTwentyFourHigh > int32(cft)+cowEstrus.HadJust {
+			if eventEstrus.Result == pasturePb.EstrusResult_Fail && eventEstrus.DaylongHigh > int32(cft)+cowEstrus.HadJust {
 				result = pasturePb.EstrusResult_Fail
 			}
 			// todo 待定
@@ -192,24 +163,25 @@ func (e *Entry) CowEstrusWarning(xToday *XToday) error {
 				result = pasturePb.EstrusResult_Correct
 			}
 
+			// 更新 已孕 + 不显示
 			isShow := pasturePb.IsShow_Ok
-			if cowInfo.IsPregnant == pasturePb.IsShow_Ok {
+			if cowInfo.IsPregnant == pasturePb.IsShow_Ok && level == pasturePb.EstrusLevel_Low {
 				isShow = pasturePb.IsShow_No
 			}
 
 			eventEstrusList = append(eventEstrusList, &model.EventEstrus{
-				CowId:             cowInfo.Id,
-				Lact:              cowInfo.Lact,
-				ExposeEstrusType:  pasturePb.ExposeEstrusType_Natural_Estrus,
-				FilterHigh:        v.FilterHigh,
-				EstrusDate:        v.ActiveTime,
-				ActiveDate:        v.ActiveTime,
-				LastEstrusDate:    cowEstrus.ActiveDate,
-				Level:             level,
-				IsPeak:            pasturePb.IsShow_No,
-				PerTwentyFourHigh: int32(cft) + cowEstrus.HadJust,
-				Result:            result,
-				IsShow:            isShow,
+				CowId:            cowInfo.Id,
+				Lact:             cowInfo.Lact,
+				ExposeEstrusType: pasturePb.ExposeEstrusType_Natural_Estrus,
+				FilterHigh:       v.FilterHigh,
+				EstrusDate:       v.ActiveTime,
+				ActiveDate:       v.ActiveTime,
+				LastEstrusDate:   cowEstrus.ActiveDate,
+				Level:            level,
+				IsPeak:           pasturePb.IsShow_No,
+				DaylongHigh:      int32(cft) + cowEstrus.HadJust,
+				Result:           result,
+				IsShow:           isShow,
 			})
 		}
 		if len(eventEstrusList) > 0 {
@@ -217,11 +189,17 @@ func (e *Entry) CowEstrusWarning(xToday *XToday) error {
 				zaplog.Error("CowEstrusWarning", zap.Any("eventEstrusList", eventEstrusList), zap.Any("err", err))
 			}
 		}
-		startDate.AddDate(0, 0, 1)
+		startDate = startDate.AddDate(0, 0, 1)
 	}
 	return nil
 }
 
+// UpdateNewEstrus 更新新发情数据
+func (e *Entry) UpdateNewEstrus(xToday *XToday) (err error) {
+
+	return nil
+}
+
 // IsAdJustLow 是否低于最低活动量
 func (e *Entry) IsAdJustLow(xToday *XToday, habit *model.NeckActiveHabit) bool {
 	ruminaAdJust := float64(0)

+ 5 - 5
module/crontab/sql.go

@@ -45,24 +45,24 @@ func (e *Entry) GetPenMapList() (map[int32]*model.Pen, error) {
 	return penMap, nil
 }
 
-func (e *Entry) GetSystemConfigure(pastureId int64, name string) (*model.SystemConfigure, error) {
+func (e *Entry) GetSystemConfigure(pastureId int64, name string) *model.SystemConfigure {
 	res := &model.SystemConfigure{}
 	if err := e.DB.Model(new(model.SystemConfigure)).
 		Where("name = ?", name).
 		Where("pasture_id = ?", pastureId).
 		Where("is_show = ?", pasturePb.IsShow_Ok).
 		First(res).Error; err != nil {
-		return nil, xerr.WithStack(err)
+		return nil
 	}
-	return res, nil
+	return res
 }
 
 // GetBeforeThreeDaysCowEstrus 获取值得时间之前三天内最大发情记录
 func (e *Entry) GetBeforeThreeDaysCowEstrus(cowId int64, activeTime string) *model.EventEstrus {
 	eventEstrus := &model.EventEstrus{}
 	if err := e.DB.Model(new(model.EventEstrus)).
-		Select("MAX(filter_high) as filter_high,cow_id,MAX(per_twenty_four_high) as per_twenty_four_high").
-		Select("MAX(IF(e16.result=1,3,e16.result))  AS result").
+		Select("MAX(filter_high) as filter_high,cow_id,MAX(daylong_high) as daylong_high").
+		Select("MAX(IF(result=1,3,result)) AS result").
 		Where("cow_id = ?", cowId).
 		Where("active_date >= ?", activeTime).
 		First(eventEstrus).Error; err != nil {

+ 23 - 22
module/mqtt/merge_handle.go

@@ -42,11 +42,13 @@ func (e *Entry) NeckRingHandle(data []byte) {
 	if newData == nil {
 		return
 	}
-
-	// 写入数据
-	if err := e.CreatedData(newData); err != nil {
-		zaplog.Error("Failed to create data", zap.Any("err", err), zap.Any("dataList", newData))
+	if len(newData.NeckRingErrorData) > 0 || len(newData.NeckRingOriginalData) > 0 {
+		// 写入数据
+		if err := e.CreatedData(newData); err != nil {
+			zaplog.Error("Failed to create data", zap.Any("err", err), zap.Any("dataList", newData))
+		}
 	}
+	return
 }
 
 // NeckRingOriginalMergeData 把脖环数据合并成2个小时的
@@ -61,10 +63,8 @@ func (e *Entry) NeckRingOriginalMergeData() {
 	newTime := time.Now()
 	neckRingList := make([]*model.NeckRingOriginal, 0)
 	if err = e.DB.Model(new(model.NeckRingOriginal)).
-		//Where("is_show = ?", pasturePb.IsShow_No).
 		Where("created_at <= ?", newTime.Add(-1*time.Hour).Unix()).
 		Where("id > ?", mergeDataMaxId).
-		Where("neck_ring_number = ?", "211690").
 		Order("id asc").Limit(int(limit)).
 		Find(&neckRingList).Error; err != nil {
 		return
@@ -108,18 +108,19 @@ func (e *Entry) NeckRingOriginalMergeData() {
 				if err = tx.Create(neckActiveHabit).Error; err != nil {
 					return xerr.WithStack(err)
 				}
-				tx.Create(model.NewNeckRingProcess(neckActiveHabit))
+				if err = tx.Create(model.NewNeckRingProcess(neckActiveHabit)).Error; err != nil {
+					return xerr.WithStack(err)
+				}
 				continue
 			}
 
-			if historyNeckActiveHabit == nil {
-				zaplog.Error("NeckRingOriginalMergeData", zap.Any("historyNeckActiveHabit", historyNeckActiveHabit))
+			if historyNeckActiveHabit == nil || historyNeckActiveHabit.Id <= 0 {
+				zaplog.Error("NeckRingOriginalMergeData", zap.Any("historyNeckActiveHabit", historyNeckActiveHabit), zap.Any("ct", ct), zap.Any("neckActiveHabit", neckActiveHabit))
 				continue
 			}
-
 			// 重新计算
 			newNeckActiveHabit := e.againRecalculate(historyNeckActiveHabit)
-			if neckActiveHabit != nil {
+			if newNeckActiveHabit != nil {
 				if err = tx.Model(new(model.NeckActiveHabit)).
 					Select("rumina", "intake", "inactive", "gasp", "other", "high", "active").
 					Where("id = ?", historyNeckActiveHabit.Id).
@@ -191,13 +192,13 @@ func (e *Entry) processBatch(batchList []*model.NeckRingOriginal) {
 func (e *Entry) CreatedData(DSMLog *DataInsertNeckRingLog) error {
 	if err := e.DB.Transaction(func(tx *gorm.DB) error {
 		if len(DSMLog.NeckRingErrorData) > 0 {
-			if err := e.DB.Create(DSMLog.NeckRingErrorData).Error; err != nil {
+			if err := e.DB.Model(new(model.NeckRingError)).Create(DSMLog.NeckRingErrorData).Error; err != nil {
 				return xerr.WithStack(err)
 			}
 		}
 
 		if len(DSMLog.NeckRingOriginalData) > 0 {
-			if err := e.DB.Create(DSMLog.NeckRingOriginalData).Error; err != nil {
+			if err := e.DB.Model(new(model.NeckRingOriginal)).Create(DSMLog.NeckRingOriginalData).Error; err != nil {
 				return xerr.WithStack(err)
 			}
 		}
@@ -217,19 +218,23 @@ func (e *Entry) MsgDataFormat2(msg []byte) *DataInsertNeckRingLog {
 
 	normalOriginal := make([]*model.NeckRingOriginal, 0)
 	errorOriginal := make([]*model.NeckRingError, 0)
+	pastureMqttMap = e.FindPastureMqttMap()
+
 	for _, neckLog := range neckLogList.NeckRing.NeckPck {
 		newOriginal := model.NewNeckRingOriginal(neckLog, pastureMqttMap)
 		if ok := util.IsValidFrameId(neckLog.Frameid); !ok {
 			var ed model.NeckRingError
 			if err := copier.Copy(&ed, &newOriginal); err != nil {
 				zaplog.Error("MsgDataFormat2", zap.Any("copier", err), zap.Any("neckLog", neckLog))
+				continue
 			}
 			errorOriginal = append(errorOriginal, &ed)
+		} else {
+			activeDate, hours := util.GetNeckRingActiveTimer(neckLog.Frameid)
+			newOriginal.ActiveDate = activeDate
+			newOriginal.Hours = int32(hours)
+			normalOriginal = append(normalOriginal, newOriginal)
 		}
-		activeDate, hours := util.GetNeckRingActiveTimer(neckLog.Frameid)
-		newOriginal.ActiveDate = activeDate
-		newOriginal.Hours = int32(hours)
-		normalOriginal = append(normalOriginal, newOriginal)
 	}
 	return &DataInsertNeckRingLog{
 		NeckRingErrorData:    errorOriginal,
@@ -242,9 +247,6 @@ func (e *Entry) recalculate(neckRingList []*model.NeckRingOriginal) []*model.Nec
 	originalMapData := make(map[string]*model.NeckRingOriginalMerge)
 	// 合并成2个小时的
 	for _, v := range neckRingList {
-		if v.NeckRingNumber != "211690" {
-			continue
-		}
 		xframeId := int32(math.Floor(float64(v.Frameid) / 10))
 		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 _, ok := originalMapData[mapKey]; !ok {
@@ -275,13 +277,12 @@ func (e *Entry) againRecalculate(data *model.NeckActiveHabit) *model.NeckActiveH
 	if err := e.DB.Model(new(model.NeckRingOriginal)).
 		Where("neck_ring_number = ?", data.NeckRingNumber).
 		Where("active_date = ?", data.HeatDate).
-		Where("frameid IN ?", frameIds).
+		Where("frameid IN (?)", frameIds).
 		Find(&originalList).Error; err != nil {
 		return nil
 	}
 
 	newDataList := e.recalculate(originalList)
-	zaplog.Info("againRecalculate", zap.Any("newDataList", newDataList))
 	if len(newDataList) != 1 {
 		return nil
 	}

+ 55 - 0
module/mqtt/model.go

@@ -0,0 +1,55 @@
+package mqtt
+
+type XToday struct {
+	XBegDate       string
+	XEndDate       string
+	LastMaxHabitId int64
+	CurrMaxHabitId int64
+	XMin2Id        int64
+	XMin7Id        int64
+	ActiveLowest   int64
+	RuminaLowest   int64
+	ActiveLow      int64
+	ActiveMiddle   int64
+	ActiveHigh     int64
+}
+
+type AvgHabit struct {
+	NeckRingNumber   string
+	AvgHighHabit     int32
+	AvgRuminaHabit   int32
+	AvgChewHabit     int32
+	AvgInactiveHabit int32
+	AvgIntakeHabit   int32
+	AvgOtherHabit    int32
+}
+
+type SumHabit struct {
+	NeckRingNumber string
+	SumRumina      int32
+	SumIntake      int32
+	SumInactive    int32
+	SumActive      int32
+	SumMaxHigh     int32
+	SumMinHigh     int32
+	SumMinChew     int32
+}
+
+type ChangeFilterData struct {
+	Id             int64
+	NeckRingNumber string
+	HighChange     int32
+	ChangeFilter   int32
+	RuminaFilter   int32
+	ChangeRumina   int32
+	ChewFilter     int32
+	ChangeChew     int32
+	XlcDisCount    float64
+}
+
+type ActivityVolume struct {
+	NeckRingNumber string
+	AvgFilter      int32
+	StdFilter      int32
+	Nb             int32
+}

+ 332 - 197
module/mqtt/neck_ring_habit.go

@@ -7,6 +7,8 @@ import (
 	"math"
 	"time"
 
+	"gorm.io/gorm"
+
 	"gitee.com/xuyiping_admin/pkg/logger/zaplog"
 	"go.uber.org/zap"
 
@@ -17,9 +19,9 @@ import (
 const (
 	MinChangeFilter = -99
 	MinRuminaFilter = -99
-	MinChewFilter   = -99
 	MinChangeHigh   = -99
 	DefaultNb       = 30
+	DefaultScore    = 100
 )
 
 func (e *Entry) PastureUpdateActiveHabit() {
@@ -63,7 +65,7 @@ func (e *Entry) EntryUpdateActiveHabit(pastureId int64) (err error) {
 	}
 
 	// 获取这段执行数据内最大日期和最小日期
-	xToday := &crontab.XToday{}
+	xToday := &XToday{}
 	if err = e.DB.Model(new(model.NeckActiveHabit)).
 		Select(`MIN(heat_date) as x_beg_date, MAX(heat_date) as x_end_date`).
 		Where("id BETWEEN ? AND ?", lastMaxHabitId, currentMaxHabit.Id).
@@ -71,10 +73,10 @@ func (e *Entry) EntryUpdateActiveHabit(pastureId int64) (err error) {
 		return xerr.WithStack(err)
 	}
 
-	xToday.LastMaxHabitId = lastMaxHabitId
-	xToday.CurrMaxHabitId = currentMaxHabit.Id
+	xToday.LastMaxHabitId = lastMaxHabitId     // 上次执行的id
+	xToday.CurrMaxHabitId = currentMaxHabit.Id // 本次执行的id
 
-	minHeatDateParse, err := time.Parse(model.LayoutDate2, xToday.XBegDate)
+	minHeatDateParse, err := time.Parse(model.LayoutDate2, xToday.XBegDate) // 开始日期
 	if err != nil {
 		return xerr.WithStack(err)
 	}
@@ -89,6 +91,7 @@ func (e *Entry) EntryUpdateActiveHabit(pastureId int64) (err error) {
 	if err != nil {
 		return xerr.WithStack(err)
 	}
+
 	defer func() {
 		// 更新最后一次执行的id值
 		if err == nil {
@@ -135,30 +138,58 @@ func (e *Entry) EntryUpdateActiveHabit(pastureId int64) (err error) {
 	xToday.ActiveLowest = activeLowest.Value
 	xToday.RuminaLowest = ruminaLowest.Value
 	// 更新活动滤波
-	if err = e.FilterUpdate(pastureId, xToday); err != nil {
-		zaplog.Error("EntryUpdateActiveHabit", zap.Any("FilterUpdate", err), zap.Any("xToday", xToday))
-		return xerr.WithStack(err)
+	if err = e.FirstFilterUpdate(pastureId, xToday); err != nil {
+		zaplog.Error("EntryUpdateActiveHabit", zap.Any("FirstFilterUpdate", err), zap.Any("xToday", xToday))
 	}
 	// 更新周平均值
 	if err = e.WeeklyActiveAvgUpdate(pastureId, xToday); err != nil {
 		zaplog.Error("EntryUpdateActiveHabit", zap.Any("WeeklyActiveAvgUpdate", err), zap.Any("xToday", xToday))
-		return xerr.WithStack(err)
+	}
+
+	if err = e.SecondUpdateChangeFilter(pastureId, xToday); err != nil {
+		zaplog.Error("EntryUpdateActiveHabit", zap.Any("SecondUpdateChangeFilter", err), zap.Any("xToday", xToday))
 	}
 
 	if err = e.ActivityVolumeChanges(pastureId, xToday); err != nil {
 		zaplog.Error("EntryUpdateActiveHabit", zap.Any("ActivityVolumeChanges", err), zap.Any("xToday", xToday))
-		return xerr.WithStack(err)
+	}
+
+	if err = e.DB.Model(new(model.NeckActiveHabit)).
+		Where("pasture_id = ?", pastureId).
+		Where("id BETWEEN ? AND ?", xToday.LastMaxHabitId, xToday.CurrMaxHabitId).
+		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("pasture_id = ?", pastureId).
+		Where("id BETWEEN ? AND ?", xToday.LastMaxHabitId, xToday.CurrMaxHabitId).
+		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))
 	}
 	return nil
 }
 
-// FilterUpdate 更新活动滤波
-func (e *Entry) FilterUpdate(pastureId int64, xToDay *crontab.XToday) error {
+// FirstFilterUpdate 首次更新活动滤波
+func (e *Entry) FirstFilterUpdate(pastureId int64, xToDay *XToday) error {
 	newNeckActiveHabitList := make([]*model.NeckActiveHabit, 0)
 	if err := e.DB.Model(new(model.NeckActiveHabit)).
 		Where("pasture_id = ?", pastureId).
 		Where(e.DB.Where("change_filter = ?", model.InitChangeFilter).Or("is_max_time = ?", pasturePb.IsShow_Ok)).
-		Where(e.DB.Where("high >= ?", xToDay.ActiveLowest).Or("rumina >= ?", xToDay.RuminaLowest)).
 		Order("neck_ring_number,id").
 		Find(&newNeckActiveHabitList).Error; err != nil {
 		return xerr.WithStack(err)
@@ -167,6 +198,10 @@ func (e *Entry) FilterUpdate(pastureId int64, xToDay *crontab.XToday) error {
 	var filterValues = make(map[string]*model.NeckActiveHabit)
 	// 活动量滤波
 	for _, v := range newNeckActiveHabitList {
+		// 活动量过低牛只不参与计算
+		if v.High < int32(xToDay.ActiveLowest) || v.Rumina < int32(xToDay.RuminaLowest) {
+			continue
+		}
 		prev, ok := filterValues[v.NeckRingNumber]
 		if !ok {
 			if v.FilterHigh <= 0 {
@@ -203,132 +238,152 @@ func (e *Entry) FilterUpdate(pastureId int64, xToDay *crontab.XToday) error {
 	return nil
 }
 
-func (e *Entry) WeeklyActiveAvgUpdate(pastureId int64, xToday *crontab.XToday) error {
-	beginDayDate, err := time.Parse(model.LayoutDate2, xToday.XBegDate)
-	if err != nil {
-		return xerr.WithStack(err)
-	}
-	before7DayDate := beginDayDate.AddDate(0, 0, -7).Format(model.LayoutDate2)
-	before1DayDate := beginDayDate.AddDate(0, 0, -1).Format(model.LayoutDate2)
-
+// WeeklyActiveAvgUpdate 更新周平均值
+func (e *Entry) WeeklyActiveAvgUpdate(pastureId int64, xToday *XToday) (err error) {
 	weeklyActive, err := e.GetSystemConfigure2(pastureId, model.WeeklyActive)
 	if err != nil {
 		return xerr.WithStack(err)
 	}
-	xframeId := int64(0)
-	maxXframeId := int64(11)
-	xStartDate, _ := time.Parse(model.LayoutDate2, xToday.XBegDate)
-	xEndDate, _ := time.Parse(model.LayoutDate2, xToday.XEndDate)
-	for xStartDate.Format(model.LayoutDate2) < xEndDate.Format(model.LayoutDate2) || (xStartDate == xEndDate && xframeId <= maxXframeId) {
-		//  时间点周平均
-		AvgHabitList := make([]*crontab.AvgHabit, 0)
-		if err = e.DB.Model(new(model.NeckActiveHabit)).
-			Select("neck_ring_number").
-			Select("IF(COUNT(1)>=3, ROUND((SUM(filter_high) -MIN(filter_high) -MAX(filter_high))/ABS(COUNT(1) -2),0), -1) as avg_high_habit").
-			Select("IF(COUNT(1)>=3, ROUND((SUM(filter_rumina) -MIN(filter_rumina) -MAX(filter_rumina))/ABS(COUNT(1) -2),0), -1) as avg_rumina_habit").
-			Select("IF(COUNT(1)>=3, ROUND((SUM(filter_chew) -MIN(filter_chew) -MAX(filter_chew))/ABS(COUNT(1) -2),0), -1) as avg_chew_habit").
-			Select("ROUND(AVG(intake),0) as avg_intake_habit").
-			Select("ROUND(AVG(inactive),0) as avg_inactive_habit").
-			Where("id BETWEEN ? AND ?", xToday.XMin7Id, xToday.CurrMaxHabitId).
-			Where("heat_date BETWEEN ? AND ?", before7DayDate, before1DayDate).
-			Where("frameid = ?", xframeId).
+
+	if err = e.DB.Transaction(func(tx *gorm.DB) error {
+		neckActiveHabitList := make([]*model.NeckActiveHabit, 0)
+		if err = tx.Model(new(model.NeckActiveHabit)).
+			Where("id BETWEEN ? AND ?", xToday.XMin2Id, xToday.CurrMaxHabitId).
 			Where("pasture_id = ?", pastureId).
 			Where("change_filter = ?", model.InitChangeFilter).
 			Where(e.DB.Where("high > ?", xToday.ActiveLowest).Or("rumina > ?", xToday.RuminaLowest)).
-			Group("neck_ring_number").
-			Find(&AvgHabitList).Error; err != nil {
+			Group("neck_ring_number,frameid").
+			Find(&neckActiveHabitList).Error; err != nil {
+			zaplog.Error("WeeklyActiveAvgUpdate-0", zap.Any("error", err), zap.Any("xToday", xToday))
 			return xerr.WithStack(err)
 		}
-		for _, v := range AvgHabitList {
-			if err = e.DB.Model(new(model.NeckActiveHabit)).
-				Select("avg_high_habit", "avg_rumina_habit", "avg_chew_habit", "avg_intake_habit", "avg_inactive_habit").
-				Where("neck_ring_number = ?", v.NeckRingNumber).
-				Where("id BETWEEN ? AND ?", xToday.LastMaxHabitId, xToday.CurrMaxHabitId).
-				Where("frameid = ?", xframeId).
-				Where("change_filter = ?", model.InitChangeFilter).
-				Where("heat_date = ?", xStartDate).
-				Updates(v).Error; err != nil {
-				return xerr.WithStack(err)
+		for _, v := range neckActiveHabitList {
+			beginDayDate, _ := time.Parse(model.LayoutDate2, v.HeatDate)
+			before7DayDate := beginDayDate.AddDate(0, 0, -7).Format(model.LayoutDate2)
+			before1DayDate := beginDayDate.AddDate(0, 0, -1).Format(model.LayoutDate2)
+
+			avgHabitData := &crontab.AvgHabit{}
+			if err = tx.Model(new(model.NeckActiveHabit)).
+				Select("neck_ring_number").
+				Select("IF(COUNT(1)>=3, ROUND((SUM(filter_high) -MIN(filter_high) -MAX(filter_high))/ABS(COUNT(1) -2),0), -1) as avg_high_habit").
+				Select("IF(COUNT(1)>=3, ROUND((SUM(filter_rumina) -MIN(filter_rumina) -MAX(filter_rumina))/ABS(COUNT(1) -2),0), -1) as avg_rumina_habit").
+				Select("IF(COUNT(1)>=3, ROUND((SUM(filter_chew) -MIN(filter_chew) -MAX(filter_chew))/ABS(COUNT(1) -2),0), -1) as avg_chew_habit").
+				Select("ROUND(AVG(intake),0) as avg_intake_habit").
+				Select("ROUND(AVG(inactive),0) as avg_inactive_habit").
+				Where("id BETWEEN ? AND ?", xToday.XMin7Id, xToday.CurrMaxHabitId).
+				Where("heat_date BETWEEN ? AND ?", before7DayDate, before1DayDate).
+				Where("pasture_id = ?", pastureId).
+				Where("neck_ring_number = ? AND frameid = ?", v.NeckRingNumber, v.Frameid).
+				Where(e.DB.Where("high > ?", xToday.ActiveLowest).Or("rumina > ?", xToday.RuminaLowest)).
+				First(avgHabitData).Error; err != nil {
+				zaplog.Error("WeeklyActiveAvgUpdate-1", zap.Any("error", err), zap.Any("xToday", xToday))
 			}
-		}
 
-		// 累计24小时数值
-		sumHabitList := make([]*crontab.SumHabit, 0)
-		if err = e.DB.Model(new(model.NeckActiveHabit)).
-			Select("neck_ring_number").
-			Select("IF(COUNT(1)>6, ROUND(AVG( h2.filter_rumina)*12,0), 0) as sum_rumina").
-			Select("IF(COUNT(1)>6, ROUND(AVG( h2.intake)*12,0), 0) as sum_intake").
-			Select("IF(COUNT(1)>6, ROUND(AVG( h2.inactive)*12,0), 0) as sum_inactive").
-			Select("IF(COUNT(1)>6, ROUND(AVG( h2.active)*12,0), 0) as sum_active").
-			Select("MAX(h2.change_filter) as sum_max_high").
-			Select("MIN(IF(change_filter > ?, change_filter, 0)) as sum_min_high", MinChangeFilter).
-			Select("MIN( CASE WHEN filter_chew > ? THEN filter_chew WHEN filter_rumina >= ? THEN filter_rumina ELSE 0 END) as sum_min_chew", MinChangeFilter, MinRuminaFilter).
-			Where("id BETWEEN ? AND ?", xToday.XMin2Id, xToday.CurrMaxHabitId).
-			Where("pasture_id = ?", pastureId).
-			Where("heat_date BETWEEN ? AND ?", xStartDate.AddDate(0, 0, -1).Format(model.LayoutDate2), xStartDate.Format(model.LayoutDate2)).
-			Where("created_at BETWEEN ? AND ?", xStartDate.Add(-23*time.Hour).Unix(), xStartDate.Unix()).
-			Where(e.DB.Where("high > ?", xToday.ActiveLowest).Or("rumina >= ?", xToday.RuminaLowest)).
-			Group("neck_ring_number").
-			Find(&sumHabitList).Error; err != nil {
-			return xerr.WithStack(err)
-		}
+			// 累计24小时数值
+			sumHabitData := &SumHabit{}
+			if err = tx.Model(new(model.NeckActiveHabit)).
+				Select("neck_ring_number").
+				Select("IF(COUNT(1)>6, ROUND(AVG( h2.filter_rumina)*12,0), 0) as sum_rumina").
+				Select("IF(COUNT(1)>6, ROUND(AVG( h2.intake)*12,0), 0) as sum_intake").
+				Select("IF(COUNT(1)>6, ROUND(AVG( h2.inactive)*12,0), 0) as sum_inactive").
+				Select("IF(COUNT(1)>6, ROUND(AVG( h2.active)*12,0), 0) as sum_active").
+				Select("MAX(h2.change_filter) as sum_max_high").
+				Select("MIN(IF(change_filter > ?, change_filter, ?)) as sum_min_high", model.DefaultChangeFilter, model.InitChangeFilter).
+				Select("MIN( CASE WHEN filter_chew > ? THEN filter_chew WHEN filter_rumina >= ? THEN filter_rumina ELSE 0 END) as sum_min_chew", model.DefaultChangeFilter, model.DefaultRuminaFilter).
+				Where("id BETWEEN ? AND ?", xToday.XMin2Id, xToday.CurrMaxHabitId).
+				Where("pasture_id = ?", pastureId).
+				Where("heat_date BETWEEN ? AND ?", before1DayDate, beginDayDate).
+				Where("active_time BETWEEN ? AND ?", beginDayDate.Add(-23*time.Hour).Format(model.LayoutTime), beginDayDate.Format(model.LayoutTime)).
+				Where(e.DB.Where("high > ?", xToday.ActiveLowest).Or("rumina >= ?", xToday.RuminaLowest)).
+				Where("neck_ring_number = ? AND frameid = ?", v.NeckRingNumber, v.Frameid).
+				First(sumHabitData).Error; err != nil {
+				zaplog.Error("WeeklyActiveAvgUpdate-2", zap.Any("error", err), zap.Any("xToday", xToday))
+			}
+
+			// 变化百分比
+			if v.AvgHighHabit > 0 {
+				if v.FilterHigh-v.AvgHighHabit > 0 {
+					v.ChangeHigh = (v.FilterHigh - v.AvgHighHabit) / int32(float64(v.WeekHigh)*0.6+float64(v.AvgHighHabit)*0.2+float64(weeklyActive.Value)*0.2)
+				} else {
+					v.ChangeHigh = v.FilterHigh - v.AvgHighHabit/v.AvgHighHabit*100
+				}
+
+				v.ChangeRumina = v.RuminaFilter - v.AvgRuminaHabit/v.AvgHighHabit*100
+				v.ChangeChew = v.FilterChew - v.AvgChewHabit/v.AvgHighHabit*100
+				if err = e.DB.Model(new(model.NeckActiveHabit)).
+					Select("change_high", "change_rumina", "change_chew").
+					Where("id = ?", v.Id).
+					Updates(v).Error; err != nil {
+					return xerr.WithStack(err)
+				}
+			}
 
-		for _, v := range sumHabitList {
+			// 三天前的反刍和采食
+			before3DaysNeckActiveHabit := &model.NeckActiveHabit{}
+			before3DayDate := beginDayDate.AddDate(0, 0, -3).Format(model.LayoutDate2)
 			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").
+				Select("sum_rumina", "sum_intake").
+				Where("pasture_id = ?", pastureId).
 				Where("neck_ring_number = ?", v.NeckRingNumber).
-				Where("id BETWEEN ? AND ?", xToday.LastMaxHabitId, xToday.CurrMaxHabitId).
-				Where("heat_date = ?", xStartDate.Format(model.LayoutDate2)).
-				Where("frameid = ?", xframeId).
-				Where("change_filter = ?", model.InitChangeFilter).
-				Updates(v).Error; err != nil {
-				return xerr.WithStack(err)
+				Where("heat_date = ?", before3DayDate).
+				Where("frameid = ? ", v.Frameid).First(before3DaysNeckActiveHabit).Error; err != nil {
+				zaplog.Error("WeeklyActiveAvgUpdate-3", zap.Any("error", err), zap.Any("xToday", xToday))
 			}
-		}
-		// 变化百分比
-		changeHabitList := make([]*model.NeckActiveHabit, 0)
-		if err = e.DB.Model(new(model.NeckActiveHabit)).
-			Where("id BETWEEN ? AND ?", xToday.LastMaxHabitId, xToday.CurrMaxHabitId).
-			Where("heat_date = ?", xStartDate.Format(model.LayoutDate2)).
-			Where("frameid = ?", xframeId).
-			Where("pasture_id = ?", pastureId).
-			Where("change_filter = ?", model.InitChangeFilter).
-			Where("avg_high_habit > ?", 0).
-			Where(e.DB.Where("high > ?", xToday.ActiveLowest).Or("rumina >= ?", xToday.RuminaLowest)).
-			Find(&changeHabitList).Error; err != nil {
-			return xerr.WithStack(err)
-		}
 
-		for _, v := range changeHabitList {
-			if v.FilterHigh-v.AvgHighHabit > 0 {
-				v.ChangeHigh = (v.FilterHigh - v.AvgHighHabit) / int32(float64(v.WeekHigh)*0.6+float64(v.AvgHighHabit)*0.2+float64(weeklyActive.Value)*0.2)
+			cowId := int64(0)
+			Lact := int32(0)
+			calvingAge := int64(0)
+			cow := e.GetCowInfoByNeckRingNumber(pastureId, v.NeckRingNumber)
+			if cow != nil {
+				cowId = cow.Id
+				Lact = cow.Lact
+				calvingAge = cow.CalvingAge
 			} else {
-				v.ChangeHigh = v.FilterHigh - v.AvgHighHabit/v.AvgHighHabit*100
+				zaplog.Error("WeeklyActiveAvgUpdate-4", zap.Any("error", err), zap.Any("xToday", xToday))
 			}
 
-			v.ChangeRumina = v.RuminaFilter - v.AvgRuminaHabit/v.AvgHighHabit*100
-			v.ChangeChew = v.FilterChew - v.AvgChewHabit/v.AvgHighHabit*100
-			if err = e.DB.Model(new(model.NeckActiveHabit)).
-				Select("change_high", "change_rumina", "change_chew").
+			if err = tx.Model(new(model.NeckActiveHabit)).
+				Select("avg_high_habit", "avg_rumina_habit", "avg_chew_habit", "avg_intake_habit", "avg_inactive_habit").
+				Select("sum_rumina", "sum_intake", "sum_inactive", "sum_active", "sum_max_high", "sum_min_high", "sum_min_chew").
+				Select("change_high", "change_rumina", "change_chew", "cow_id", "lact", "calving_age").
+				Select("sum_rumina_before_three_day,sum_intake_before_three_day").
 				Where("id = ?", v.Id).
-				Updates(v).Error; err != nil {
-				return xerr.WithStack(err)
+				Updates(map[string]interface{}{
+					"cow_id":                      cowId,
+					"lact":                        Lact,
+					"calving_age":                 calvingAge,
+					"avg_high_habit":              avgHabitData.AvgHighHabit,
+					"avg_rumina_habit":            avgHabitData.AvgRuminaHabit,
+					"avg_chew_habit":              avgHabitData.AvgChewHabit,
+					"avg_intake_habit":            avgHabitData.AvgIntakeHabit,
+					"avg_inactive_habit":          avgHabitData.AvgIntakeHabit,
+					"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,
+					"sum_rumina_before_three_day": before3DaysNeckActiveHabit.SumRumina,
+					"sum_intake_before_three_day": before3DaysNeckActiveHabit.SumIntake,
+				}).Error; err != nil {
+				zaplog.Error("WeeklyActiveAvgUpdate-6", zap.Any("error", err), zap.Any("xToday", xToday))
+				continue
 			}
 		}
-
-		if xframeId == maxXframeId {
-			xframeId = 0
-			xStartDate = xStartDate.AddDate(0, 0, 1)
-		} else {
-			xframeId++
-		}
+		return nil
+	}); err != nil {
+		return xerr.WithStack(err)
 	}
-	zaplog.Info("EntryUpdateActiveHabit-WeeklyActiveAvgUpdate-Success")
+
 	return nil
 }
 
-// UpdateChangeFilter  变化趋势滤波
-func (e *Entry) UpdateChangeFilter(pastureId int64, xToday *crontab.XToday) (err error) {
+// SecondUpdateChangeFilter 第二次更新变化趋势滤波
+func (e *Entry) SecondUpdateChangeFilter(pastureId int64, xToday *XToday) (err error) {
 	xRuminaDisc, err := e.GetSystemConfigure2(pastureId, model.XRuminaDisc)
 	if err != nil {
 		return err
@@ -340,7 +395,7 @@ func (e *Entry) UpdateChangeFilter(pastureId int64, xToday *crontab.XToday) (err
 	newChangeFilterList := make([]*crontab.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").
-		Select("IF(lact=0,0.8,1) as xlc_dis_count").
+		Select("IF(lact = 0, 0.8, 1) as xlc_dis_count").
 		Where("id BETWEEN ? AND ?", xToday.XMin2Id, xToday.CurrMaxHabitId).
 		Where("pasture_id = ?", pastureId).
 		Where(e.DB.Where("change_filter = ?", model.InitChangeFilter).Or("is_max_time = ?", pasturePb.IsShow_Ok)).
@@ -407,114 +462,194 @@ func (e *Entry) UpdateChangeFilter(pastureId int64, xToday *crontab.XToday) (err
 			Where("neck_ring_number = ?", v.NeckRingNumber).
 			Where("change_filter = ?", model.InitChangeFilter).
 			Updates(v).Error; err != nil {
-			return xerr.WithStack(err)
+			zaplog.Error("SecondUpdateChangeFilter-1", zap.Any("error", err), zap.Any("xToday", xToday))
 		}
 		filterValues[v.NeckRingNumber] = v
 	}
-
 	return nil
 }
 
 // ActivityVolumeChanges 计算活动量变化趋势校正值(活跃度校正)
-func (e *Entry) ActivityVolumeChanges(pastureId int64, xToday *crontab.XToday) error {
-	currDate, _ := time.Parse(model.LayoutDate2, xToday.XBegDate)
-	XEndDateTime, _ := time.Parse(model.LayoutDate2, xToday.XEndDate)
-	xframeId := int64(0)
-	maxXframeId := int64(11)
-	dayTimes := int64(1)
-	for currDate.Format(model.LayoutDate2) < XEndDateTime.Format(model.LayoutDate2) || (currDate == XEndDateTime && xframeId <= maxXframeId) {
-		activityVolumeList := make([]*crontab.ActivityVolume, 0)
-		activeTime := fmt.Sprintf("%s %02d:00:00", currDate.Format(model.LayoutDate2), xframeId*2)
-		activeTimeParse, err := time.Parse(model.LayoutTime, activeTime)
-		if err != nil {
-			return xerr.WithStack(err)
-		}
-		if dayTimes == 1 {
-			if err = e.DB.Model(new(model.NeckActiveHabit)).
-				Select("neck_ring_number").
-				Select("AVG(IF(change_filter>=60, 60, change_filter)) as avg_filter").
-				Select("ROUND(STD(IF(change_filter>=60, 60, change_filter))) as std_filter").
-				Select("COUNT(1) as nb").
-				Where("id BETWEEN ? AND ?", xToday.XMin7Id, xToday.CurrMaxHabitId).
-				Where("heat_date BETWEEN ? AND ?", currDate.AddDate(0, 0, -7).Format(model.LayoutDate2), currDate.AddDate(0, 0, -1).Format(model.LayoutDate2)).
-				Where("frameid = ?", xframeId).
-				Where("pasture_id = ?", pastureId).
-				Where(e.DB.Where("high > ?", xToday.ActiveLowest).Or("rumina >= ?", xToday.RuminaLowest)).
-				Where("active_time <= ?", activeTimeParse.Add(-12*time.Hour)).
-				Where("change_filter > ?", MinChangeFilter).
-				Having("nb > ?", DefaultNb).
-				Group("neck_ring_number").
-				Find(&activityVolumeList).Error; err != nil {
-				return xerr.WithStack(err)
-			}
-		}
+func (e *Entry) ActivityVolumeChanges(pastureId int64, xToday *XToday) error {
+	beginDayDate, err := time.Parse(model.LayoutDate2, xToday.XBegDate)
+	if err != nil {
+		return xerr.WithStack(err)
+	}
+	before7DayDate := beginDayDate.AddDate(0, 0, -7).Format(model.LayoutDate2)
+	before1DayDate := beginDayDate.AddDate(0, 0, -1).Format(model.LayoutDate2)
 
+	activityVolumeList := make([]*crontab.ActivityVolume, 0)
+	activityVolumeMap := make(map[string]*crontab.ActivityVolume)
+	if err = e.DB.Model(new(model.NeckActiveHabit)).
+		Select("neck_ring_number").
+		Select("AVG(IF(change_filter>=60, 60, change_filter)) as avg_filter").
+		Select("ROUND(STD(IF(change_filter>=60, 60, change_filter))) as std_filter").
+		Select("COUNT(1) as nb").
+		Where("id BETWEEN ? AND ?", xToday.XMin7Id, xToday.CurrMaxHabitId).
+		Where("heat_date BETWEEN ? AND ?", before7DayDate, before1DayDate).
+		Where("pasture_id = ?", pastureId).
+		Where(e.DB.Where("high > ?", xToday.ActiveLowest).Or("rumina >= ?", xToday.RuminaLowest)).
+		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 {
-			filterCorrect := model.DefaultFilterCorrect - int(math.Floor(float64(v.AvgFilter)/3+float64(v.StdFilter)/2))
+			activityVolumeMap[v.NeckRingNumber] = v
+		}
+	}
+
+	neckActiveHabitList := make([]*model.NeckActiveHabit, 0)
+	if err = e.DB.Model(new(model.NeckActiveHabit)).
+		Where("id BETWEEN ? AND ?", xToday.XMin2Id, xToday.CurrMaxHabitId).
+		Where("pasture_id = ?", pastureId).
+		Where(e.DB.Where("high > ?", xToday.ActiveLowest).Or("rumina > ?", xToday.RuminaLowest)).
+		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(float64(filterCorrectMap.AvgFilter)/3+float64(filterCorrectMap.StdFilter)/2))
+			v.FilterCorrect = int32(filterCorrect)
+
+			// 活动量校正系数
 			if err = e.DB.Model(new(model.NeckActiveHabit)).
+				Where("id = ?", v.Id).
 				Where("neck_ring_number = ?", v.NeckRingNumber).
-				Where("id BETWEEN ? AND ?", xToday.LastMaxHabitId, xToday.CurrMaxHabitId).
-				Where("frameid = ?", xframeId).
-				Where("head_date = ?", currDate.Format(model.LayoutDate2)).
 				Update("filter_correct", filterCorrect).Error; err != nil {
-				return xerr.WithStack(err)
+				zaplog.Error("ActivityVolumeChanges-2", zap.Any("error", err), zap.Any("xToday", xToday))
+				continue
 			}
 		}
 
-		/*n := 0
-		if n <= 10 {
-
-			// todo
-
-			n += 2
-		}*/
-
-		zaplog.Info("ActivityVolumeChanges",
-			zap.Any("xToday", xToday),
-			zap.Any("currDate", currDate.Format(model.LayoutDate2)),
-			zap.Any("xframeId", xframeId),
-			zap.Any("activityVolumeList", activityVolumeList),
-		)
-
-		if err = e.DB.Model(new(model.NeckActiveHabit)).
-			Where("id BETWEEN ? AND ?", xToday.LastMaxHabitId, xToday.CurrMaxHabitId).
-			Where("heat_date = ?", currDate.Format(model.LayoutDate2)).
-			Where("frameid = ?", xframeId).
-			Where("change_filter = ?", model.InitChangeFilter).
-			Updates(map[string]interface{}{
-				"change_filter":  MinChangeFilter,
-				"rumina_filter":  MinRuminaFilter,
-				"chew_filter":    MinChewFilter,
-				"filter_correct": model.DefaultFilterCorrect,
-			}).Error; err != nil {
-			return xerr.WithStack(err)
+		// 健康评分
+		if v.Score != 0 {
+			continue
 		}
-		if xframeId == maxXframeId {
-			xframeId = 0
-			currDate = currDate.AddDate(0, 0, 1)
-			dayTimes = 1
-		} else {
-			xframeId++
-			dayTimes++
+
+		cow := e.GetCowInfoByNeckRingNumber(v.PastureId, v.NeckRingNumber)
+		if cow == nil {
+			continue
 		}
 
-		/*// 更新评分
-		newNeckActiveHabitList := make([]*model.NeckActiveHabit, 0)
+		cowScore := calculateScore(v)
 		if err = e.DB.Model(new(model.NeckActiveHabit)).
-			Where("id BETWEEN ? AND ?", xToday.LastMaxHabitId, xToday.CurrMaxHabitId).
-			Where("heat_date = ?", currDate.Format(model.LayoutDate2)).
-			Where("frameid = ?", xframeId).
-			Where("score = ?", 0).
-			Find(&newNeckActiveHabitList).Error; err != nil {
-			return xerr.WithStack(err)
-		}*/
-		// todo 待开发
+			Where("id = ?", v.Id).
+			Where("neck_ring_number = ?", v.NeckRingNumber).
+			Update("score", cowScore).Error; err != nil {
+			zaplog.Error("ActivityVolumeChanges-2", zap.Any("error", err), zap.Any("xToday", xToday))
+			continue
+		}
 	}
 	zaplog.Info("EntryUpdateActiveHabit-ActivityVolumeChanges-Success")
 	return nil
 }
 
+// UpdateChangeAdJust 更新群体校正数据
+func (e *Entry) UpdateChangeAdJust(pastureId int64, xToday *XToday) error {
+	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).
+		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)
+	}
+
+	for _, v := range res {
+		if err := e.DB.Model(new(model.NeckActiveHabit)).
+			Where("id > ?", xToday.LastMaxHabitId).
+			Where("heat_date = ?", v.HeatDate).
+			Where("heat_date >= ?", oneDayAgo).
+			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 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
+}

+ 8 - 5
module/mqtt/sql.go

@@ -27,9 +27,13 @@ func (e *Entry) NeckRingIsBind(number string) bool {
 	return false
 }
 
-func (e *Entry) GetCowInfoByImei(imei string) *model.Cow {
+func (e *Entry) GetCowInfoByNeckRingNumber(pastureId int64, neckRingNumber string) *model.Cow {
 	res := &model.Cow{}
-	if err := e.DB.Model(new(model.Cow)).Where("neck_ring_number = ?", imei).First(res).Error; err != nil {
+	if err := e.DB.Model(new(model.Cow)).
+		Where("pasture_id = ?", pastureId).
+		Where("neck_ring_number = ?", neckRingNumber).
+		Where("admission_status = ?", pasturePb.AdmissionStatus_Admission).
+		First(res).Error; err != nil {
 		return nil
 	}
 	return res
@@ -53,7 +57,7 @@ func (e *Entry) IsExistNeckActiveHabit(neckRingNumber, heatDate string, frameId
 		Where("neck_ring_number = ?", neckRingNumber).
 		Where("active_date = ?", heatDate).
 		Where("frameid = ?", frameId).
-		Count(&count).Error; err != nil {
+		Count(&count).First(neckRingProcess).Error; err != nil {
 		return nil, 0
 	}
 	res := &model.NeckActiveHabit{}
@@ -74,8 +78,7 @@ func (e *Entry) GetMinIdByHeatDate(heatDate string, defaultId int64) (int64, err
 	}{}
 	if err := e.DB.Model(new(model.NeckActiveHabit)).
 		Select("MIN(id) as id").
-		//Where("heat_date = ?", minHeatDateParse.AddDate(0, 0, -1).Format(model.LayoutDate2)).
-		Where("heat_date >= ?", heatDate).
+		Where("heat_date = ?", heatDate).
 		First(&xMinId).Error; err != nil {
 		if errors.Is(err, gorm.ErrRecordNotFound) {
 			xMinId.Id = defaultId

+ 1 - 3
util/util.go

@@ -368,9 +368,7 @@ func GetNeckRingActiveTimer(frameId int32) (dateTime string, hours int) {
 	if _, ok := FrameIdMap[frameId]; !ok {
 		return "", 0
 	}
-
-	//nowTime := time.Now()
-	nowTime := time.Unix(1736440903, 0)
+	nowTime := time.Now()
 	currHour := nowTime.Hour()
 	// 处理2小时的特殊 farmId
 	hours, ok := SpecialHours[int(frameId)]

+ 19 - 19
util/util_test.go

@@ -2,6 +2,7 @@ package util
 
 import (
 	"fmt"
+	"math"
 	"testing"
 	"time"
 
@@ -521,25 +522,24 @@ type XToday struct {
 }
 
 func Test_demo(t *testing.T) {
-	xToday := &XToday{
-		XBegDate: "2025-01-08",
-		XEndDate: "2025-01-10",
+	frameId := []int32{
+		1, 2, 3, 4, 5, 6, 8,
+		11, 12, 13, 14, 15, 16, 18,
+		21, 22, 23, 24, 25, 26, 28,
+		31, 32, 33, 34, 35, 36, 38,
+		41, 42, 43, 44, 45, 46, 48,
+		51, 52, 53, 54, 55, 56, 58,
+		61, 62, 63, 64, 65, 66, 68,
+		71, 72, 73, 74, 75, 76, 78,
+		81, 82, 83, 84, 85, 86, 88,
+		91, 92, 93, 94, 95, 96, 98,
+		101, 102, 103, 104, 105, 106, 108,
+		111, 112, 113, 114, 115, 116, 118,
+		8, 18, 28, 38, 48, 58, 68, 78, 88, 98, 108, 118,
 	}
-
-	currDate, _ := time.Parse("2006-01-02", xToday.XBegDate)
-	XEndDateTime, _ := time.Parse("2006-01-02", xToday.XEndDate)
-	xframeId := int64(0)
-	maxXframeId := int64(11)
-	dayTimes := int64(1)
-	for currDate.Format("2006-01-02") < XEndDateTime.Format("2006-01-02") || (currDate == XEndDateTime && xframeId <= maxXframeId) {
-		fmt.Println(currDate.Format("2006-01-02"), xframeId, dayTimes)
-		if xframeId == maxXframeId {
-			xframeId = 0
-			currDate = currDate.AddDate(0, 0, 1)
-			dayTimes = 1
-		} else {
-			xframeId++
-			dayTimes++
-		}
+	for _, v := range frameId {
+		activeDate, hour := GetNeckRingActiveTimer(v)
+		xframeId := int32(math.Floor(float64(v) / 10))
+		fmt.Println("v", v, "activeDate", activeDate, "hour", hour, "xframeId", xframeId)
 	}
 }