소스 검색

neckRing: 脖环数据清洗

Yi 4 달 전
부모
커밋
33cc02715c

+ 1 - 1
http/handler/goods/outbound.go

@@ -89,7 +89,7 @@ func OutboundAudit(c *gin.Context) {
 }
 
 func OutboundDetail(c *gin.Context) {
-	idStr := c.Query("id")
+	idStr := c.Param("id")
 	id, _ := strconv.ParseInt(idStr, 10, 64)
 	if err := valid.Validate(id, valid.Required); err != nil {
 		apierr.AbortBadRequest(c, http.StatusBadRequest, err)

+ 1 - 0
model/cow.go

@@ -39,6 +39,7 @@ type Cow struct {
 	AdmissionAt         int64                          `json:"admissionAt"`
 	FirstMatingAt       int64                          `json:"firstMatingAt"`
 	MatingTimes         int32                          `json:"matingTimes"`
+	WeeklyActive        int32                          `json:"weeklyActive"`
 	LastEstrusAt        int64                          `json:"lastEstrusAt"`
 	LastCalvingAt       int64                          `json:"lastCalvingAt"`
 	LastMatingAt        int64                          `json:"lastMatingAt"`

+ 9 - 4
model/neck_active_habit.go

@@ -44,11 +44,11 @@ type NeckActiveHabit struct {
 	SumIntake               int32                 `json:"sumIntake"`
 	SumInactive             int32                 `json:"sumInactive"`
 	SumAct                  int32                 `json:"sumAct"`
+	SumMinHigh              int32                 `json:"sumMinHigh"`
+	SumMaxHigh              int32                 `json:"sumMaxHigh"`
+	SumMinChew              int32                 `json:"SumMinChew"`
 	SumRuminaBeforeThreeDay int32                 `json:"sumRuminaBeforeThreeDay"`
 	SumIntakeBeforeThreeDay int32                 `json:"sumIntakeBeforeThreeDay"`
-	MinHigh                 int32                 `json:"minHigh"`
-	MaxHigh                 int32                 `json:"maxHigh"`
-	MinChew                 int32                 `json:"minChew"`
 	Score                   int32                 `json:"score"`
 	IsMaxTime               pasturePb.IsShow_Kind `json:"isMaxTime"`
 	IsShow                  pasturePb.IsShow_Kind `json:"isShow"`
@@ -63,13 +63,17 @@ func (n *NeckActiveHabit) TableName() string {
 	return "neck_active_habit"
 }
 
-func NewNeckActiveHabit(frameId int32, heatDate, neckRingNumber string, cow *Cow, data *NeckRingOriginalMerge) *NeckActiveHabit {
+func NewNeckActiveHabit(defaultWeeklyActive, frameId int32, heatDate, neckRingNumber string, cow *Cow, data *NeckRingOriginalMerge) *NeckActiveHabit {
 	cowId := int64(0)
 	lact := int32(0)
 	if cow != nil {
 		cowId = cow.Id
 		lact = cow.Lact
 	}
+	weekHigh := cow.WeeklyActive
+	if cow.WeeklyActive == 0 {
+		weekHigh = defaultWeeklyActive
+	}
 	return &NeckActiveHabit{
 		Frameid:        frameId,
 		HeatDate:       heatDate,
@@ -83,6 +87,7 @@ func NewNeckActiveHabit(frameId int32, heatDate, neckRingNumber string, cow *Cow
 		Intake:         data.Intake,
 		Other:          data.Other,
 		Rumina:         data.Rumina,
+		WeekHigh:       weekHigh,
 		IsShow:         pasturePb.IsShow_No,
 		IsMaxTime:      pasturePb.IsShow_No,
 		ActiveTime:     fmt.Sprintf("%s %02d:00:00", heatDate, frameId),

+ 2 - 2
model/neck_ring_original.go

@@ -93,7 +93,7 @@ func (n *NeckRingOriginalMerge) SumAvg() {
 
 type NeckRingOriginalMap map[string]*NeckRingOriginalMerge
 
-func (n NeckRingOriginalMap) ForMatData(getCowInfo func(string) *Cow) []*NeckActiveHabit {
+func (n NeckRingOriginalMap) ForMatData(defaultWeeklyActive int32, getCowInfo func(string) *Cow) []*NeckActiveHabit {
 	res := make([]*NeckActiveHabit, 0)
 	for key, v := range n {
 		keyStrList := strings.Split(key, JoinKey)
@@ -105,7 +105,7 @@ func (n NeckRingOriginalMap) ForMatData(getCowInfo func(string) *Cow) []*NeckAct
 		frameId := keyStrList[2]
 		frameIdInt, _ := strconv.Atoi(frameId)
 		cowInfo := getCowInfo(imei)
-		res = append(res, NewNeckActiveHabit(int32(frameIdInt), activeDate, imei, cowInfo, v))
+		res = append(res, NewNeckActiveHabit(defaultWeeklyActive, int32(frameIdInt), activeDate, imei, cowInfo, v))
 	}
 	return res
 }

+ 3 - 3
model/outbound.go

@@ -9,7 +9,7 @@ import (
 )
 
 type Outbound struct {
-	Id               int32                      `json:"id"`
+	Id               int64                      `json:"id"`
 	Number           string                     `json:"number"`
 	OutType          pasturePb.OutType_Kind     `json:"outType"`
 	AuditStatus      pasturePb.AuditStatus_Kind `json:"auditStatus"`
@@ -31,7 +31,7 @@ func (o *Outbound) TableName() string {
 
 func NewOutbound(req *pasturePb.OutboundApplyItem, currentUser *SystemUser) *Outbound {
 	return &Outbound{
-		Number:           fmt.Sprintf("%s%s", util.GenerateRandomNumberString(6), time.Now().Format(LayoutTime)),
+		Number:           fmt.Sprintf("%s%s", util.GenerateRandomNumberString(16), time.Now().Format(LayoutDate)),
 		OutType:          req.OutType,
 		AuditStatus:      pasturePb.AuditStatus_Pending,
 		ApplicantId:      int32(currentUser.Id),
@@ -63,7 +63,7 @@ func (o OutboundSlice) ToPB(outTypeMap map[pasturePb.OutType_Kind]string, auditS
 		}
 
 		res[i] = &pasturePb.OutboundApplyDetail{
-			Id:                v.Id,
+			Id:                int32(v.Id),
 			Number:            v.Number,
 			OutType:           v.OutType,
 			OutTypeName:       outTypeName,

+ 3 - 2
model/outbound_log.go

@@ -19,10 +19,10 @@ type OutboundLog struct {
 }
 
 func (o *OutboundLog) TableName() string {
-	return "outband_log"
+	return "outbound_log"
 }
 
-func NewOutboundLogList(req []*pasturePb.OutboundApplyGoodsItem, unitMap map[pasturePb.Unit_Kind]string) []*OutboundLog {
+func NewOutboundLogList(outboundId int64, req []*pasturePb.OutboundApplyGoodsItem, unitMap map[pasturePb.Unit_Kind]string) []*OutboundLog {
 	res := make([]*OutboundLog, 0)
 	for _, v := range req {
 		unitName := ""
@@ -30,6 +30,7 @@ func NewOutboundLogList(req []*pasturePb.OutboundApplyGoodsItem, unitMap map[pas
 			unitName = unit
 		}
 		res = append(res, &OutboundLog{
+			OutboundId:  outboundId,
 			GoodsId:     int64(v.GoodsId),
 			GoodsName:   v.GoodsName,
 			Specs:       v.Specs,

+ 9 - 3
model/system_configure.go

@@ -3,14 +3,20 @@ package model
 import pasturePb "gitee.com/xuyiping_admin/go_proto/proto/go/backend/cow"
 
 const (
-	ActiveLowest = "active_lowest"
-	RuminaLowest = "rumina_lowest"
+	ActiveLowest    = "active_lowest"
+	RuminaLowest    = "rumina_lowest"
+	WeeklyActive    = "weekly_active"
+	XRuminaDisc     = "x_rumina_disc"
+	XChangeDiscount = "x_change_discount"
+	ActiveLow       = "active_low"
+	ActiveMiddle    = "active_middle"
+	ActiveHigh      = "active_high"
 )
 
 type SystemConfigure struct {
 	Id        int64                 `json:"id"`
 	Name      string                `json:"name"`
-	Value     string                `json:"value"`
+	Value     int32                 `json:"value"`
 	Remarks   string                `json:"remarks"`
 	IsShow    pasturePb.IsShow_Kind `json:"is_show"`
 	CreatedAt int64                 `json:"createdAt"`

+ 13 - 9
module/backend/event_health.go

@@ -413,7 +413,8 @@ func (s *StoreEntry) CowDiseaseTreatmentDetail(
 ) (*pasturePb.EventCowTreatmentDetailResponse, error) {
 	eventCowDiseaseList := make([]*model.EventCowDisease, 0)
 	var count int64 = 0
-	pref := s.DB.Model(new(model.EventCowDisease)).Where("cow_id = ?", req.CowId)
+	pref := s.DB.Model(new(model.EventCowDisease)).
+		Where("cow_id = ?", req.CowId)
 
 	if req.DiseaseId > 0 {
 		pref.Where("disease_id = ?", req.DiseaseId)
@@ -496,16 +497,19 @@ func (s *StoreEntry) CowDiseaseCurable(ctx context.Context, req *pasturePb.Event
 
 	if err = s.DB.Transaction(func(tx *gorm.DB) error {
 		if err = tx.Model(model.EventCowDisease{}).Where("id IN ?", req.Ids).
-			Where("health_status = ?", pasturePb.HealthStatus_Treatment).Updates(map[string]interface{}{
-			"health_status":    pasturePb.HealthStatus_Curable,
-			"diagnosed_result": pasturePb.IsShow_Ok,
-			"curable_at":       req.CurableAt,
-		}).Error; err != nil {
+			Where("health_status = ?", pasturePb.HealthStatus_Treatment).
+			Updates(map[string]interface{}{
+				"health_status":    pasturePb.HealthStatus_Curable,
+				"diagnosed_result": pasturePb.IsShow_Ok,
+				"curable_at":       req.CurableAt,
+			}).Error; err != nil {
 			return xerr.WithStack(err)
 		}
-		if err = tx.Model(model.Cow{}).Where("id IN ?", req.Ids).Updates(map[string]interface{}{
-			"health_status": pasturePb.HealthStatus_Curable,
-		}).Error; err != nil {
+		if err = tx.Model(model.Cow{}).
+			Where("id IN ?", req.Ids).
+			Updates(map[string]interface{}{
+				"health_status": pasturePb.HealthStatus_Curable,
+			}).Error; err != nil {
 			return xerr.WithStack(err)
 		}
 

+ 23 - 15
module/backend/goods.go

@@ -231,14 +231,19 @@ func (s *StoreEntry) OutboundApply(ctx context.Context, req *pasturePb.OutboundA
 			newDrugs := &model.Drugs{}
 			if err = s.DB.Model(new(model.Drugs)).
 				Where("id = ?", v.GoodsId).
-				Where("quantity >= ?", v.Quantity).
+				Where("inventory >= ?", v.Quantity).
 				First(newDrugs).Error; err != nil {
 				return xerr.WithStack(err)
 			}
 			goodsItems = append(goodsItems, &pasturePb.OutboundApplyGoodsItem{
-				GoodsId:  v.GoodsId,
-				Quantity: v.Quantity,
-				Unit:     v.Unit,
+				GoodsId:     v.GoodsId,
+				Quantity:    v.Quantity,
+				Unit:        v.Unit,
+				GoodsName:   newDrugs.Name,
+				Specs:       newDrugs.Specs,
+				Producer:    newDrugs.Producer,
+				BatchNumber: newDrugs.BatchNumber,
+				Price:       float32(newDrugs.Price) / 100,
 			})
 		}
 	case pasturePb.OutType_Medical_Equipment:
@@ -253,29 +258,32 @@ func (s *StoreEntry) OutboundApply(ctx context.Context, req *pasturePb.OutboundA
 			newMedicalEquipment := &model.MedicalEquipment{}
 			if err = s.DB.Model(new(model.Drugs)).
 				Where("id = ?", v.GoodsId).
-				Where("quantity >= ?", v.Quantity).
+				Where("inventory >= ?", v.Quantity).
 				First(newMedicalEquipment).Error; err != nil {
 				return xerr.WithStack(err)
 			}
 			goodsItems = append(goodsItems, &pasturePb.OutboundApplyGoodsItem{
-				GoodsId:  v.GoodsId,
-				Quantity: v.Quantity,
-				Unit:     v.Unit,
+				GoodsId:     v.GoodsId,
+				Quantity:    v.Quantity,
+				Unit:        v.Unit,
+				GoodsName:   newMedicalEquipment.Name,
+				Specs:       newMedicalEquipment.Specs,
+				Producer:    newMedicalEquipment.Producer,
+				BatchNumber: newMedicalEquipment.BatchNumber,
+				Price:       float32(newMedicalEquipment.Price) / 100,
 			})
 		}
 	default:
 		return xerr.Custom("未知的出库类型")
 	}
 	unitMap := s.UnitMap()
-	outbound := model.NewOutbound(req, currentUser)
-	outboundLog := model.NewOutboundLogList(goodsItems, unitMap)
 	if err = s.DB.Transaction(func(tx *gorm.DB) error {
-
 		// 创建出库申请
+		outbound := model.NewOutbound(req, currentUser)
 		if err = tx.Create(outbound).Error; err != nil {
 			return xerr.WithStack(err)
 		}
-
+		outboundLog := model.NewOutboundLogList(outbound.Id, goodsItems, unitMap)
 		if err = tx.Create(outboundLog).Error; err != nil {
 			return xerr.WithStack(err)
 		}
@@ -363,7 +371,7 @@ func (s *StoreEntry) OutboundAudit(ctx context.Context, req *pasturePb.OutboundA
 		return xerr.Custom("登录人信息失效")
 	}
 
-	outboundLogs, err := s.GetOutboundLogsByOutboundId(ctx, int64(outbound.Id))
+	outboundLogs, err := s.GetOutboundLogsByOutboundId(ctx, outbound.Id)
 	if err != nil {
 		return xerr.WithStack(err)
 	}
@@ -403,7 +411,7 @@ func (s *StoreEntry) OutboundAudit(ctx context.Context, req *pasturePb.OutboundA
 			if err = tx.Table(tableName).
 				Where("id = ?", v.GoodsId).
 				Updates(map[string]interface{}{
-					"quantity": gorm.Expr("quantity - ?", v.Quantity),
+					"inventory": gorm.Expr("inventory - ?", v.Quantity),
 				}).Error; err != nil {
 				return xerr.WithStack(err)
 			}
@@ -438,7 +446,7 @@ func (s *StoreEntry) OutboundDetail(ctx context.Context, id int64) (*pasturePb.O
 		Code:    http.StatusOK,
 		Message: "ok",
 		Data: &pasturePb.OutboundApplyDetail{
-			Id:                outbound.Id,
+			Id:                int32(outbound.Id),
 			Number:            outbound.Number,
 			OutType:           outbound.OutType,
 			OutTypeName:       outTypeMap[outbound.OutType],

+ 15 - 0
module/crontab/estrus_warning.go

@@ -0,0 +1,15 @@
+package crontab
+
+import (
+	"context"
+
+	"github.com/hibiken/asynq"
+)
+
+func (e *Entry) CowEstrus(ctx context.Context, t *asynq.Task) error {
+	/*activeLowValue := e.GetSystemConfigure(model.ActiveLow)
+	activeMiddleValue := e.GetSystemConfigure(model.ActiveMiddle)
+	activeHighValue := e.GetSystemConfigure(model.ActiveHigh)*/
+
+	return nil
+}

+ 49 - 0
module/crontab/model.go

@@ -0,0 +1,49 @@
+package crontab
+
+type XToday struct {
+	XTodaySBegFrameid int64  `gorm:"xTodaySBegFrameid"`
+	XBegDate          string `gorm:"xBegDate"`
+	XTodaySMaxFrameid int64  `gorm:"xTodaySMaxFrameid"`
+	XEndDate          string `gorm:"xEndDate"`
+	xEndUpdateActId   int64  `gorm:"xEndUpdateActId"`
+}
+
+type WeeklyHabit struct {
+	CowId                int64
+	WeekAvgHighHabit     int32
+	WeekAvgRuminaHabit   int32
+	WeekAvgChewHabit     int32
+	WeekAvgInactiveHabit int32
+	WeekAvgIntakeHabit   int32
+	WeekAvgOtherHabit    int32
+}
+
+type SumHabit struct {
+	CowId       int64
+	SumRumina   int32
+	SumIntake   int32
+	SumInactive int32
+	SumActive   int32
+	SumMaxHigh  int32
+	SumMinHigh  int32
+	SumMinChew  int32
+}
+
+type ChangeFilterData struct {
+	Id           int64
+	CowId        int64
+	HighChange   int32
+	ChangeFilter int32
+	RuminaFilter int32
+	ChangeRumina int32
+	ChewFilter   int32
+	ChangeChew   int32
+	XlcDisCount  float64
+}
+
+type ActivityVolume struct {
+	CowId     int64
+	AvgFilter int32
+	StdFilter int32
+	Nb        int32
+}

+ 411 - 32
module/crontab/neck_ring.go

@@ -4,6 +4,7 @@ import (
 	"fmt"
 	"kpt-pasture/model"
 	"math"
+	"time"
 
 	"gorm.io/gorm"
 
@@ -11,6 +12,13 @@ import (
 	"gitee.com/xuyiping_admin/pkg/xerr"
 )
 
+const (
+	DefaultLimit        = 10000
+	DefaultChangeFilter = 0
+	MinChangeFilter     = -99
+	MinChangeHigh       = -99
+)
+
 // NeckRingMergeData 把
 func (e *Entry) NeckRingMergeData() error {
 	// 先看看上次任务有没有执行结束,结束在执行下面的任务
@@ -25,7 +33,7 @@ func (e *Entry) NeckRingMergeData() error {
 
 	limit := e.Cfg.NeckRingLimit
 	if limit <= 0 {
-		limit = 10000
+		limit = DefaultLimit
 	}
 	neckRingList := make([]*model.NeckRingOriginal, 0)
 	if err := e.DB.Model(new(model.NeckRingOriginal)).
@@ -58,8 +66,16 @@ func (e *Entry) NeckRingMergeData() error {
 		v.SumAvg()
 	}
 
+	weeklyActive := &model.SystemConfigure{}
+	if err := e.DB.Model(new(model.SystemConfigure)).
+		Where("name = ?", model.WeeklyActive).
+		Where("is_show = ?", pasturePb.IsShow_Ok).
+		First(weeklyActive).Error; err != nil {
+		return xerr.WithStack(err)
+	}
+
 	// 更新脖环牛只相关信息
-	newNeckActiveHabitList := model.NeckRingOriginalMap(originalMapData).ForMatData(e.GetCowInfoByImei)
+	newNeckActiveHabitList := model.NeckRingOriginalMap(originalMapData).ForMatData(weeklyActive.Value, e.GetCowInfoByImei)
 
 	if err := e.DB.Transaction(func(tx *gorm.DB) error {
 		// 更新已处理过的id
@@ -99,27 +115,66 @@ func (e *Entry) NeckRingMergeData() error {
 }
 
 func (e *Entry) ActiveHabit() error {
-
 	// 上次任务未处理完的数据
-	minNeckActiveHabit := &model.NeckActiveHabit{}
+	lastNeckActiveHabit := &model.NeckActiveHabit{}
 	if err := e.DB.Model(new(model.NeckActiveHabit)).
 		Where("is_show = ?", pasturePb.IsShow_No).
-		Order("id").Limit(1).First(minNeckActiveHabit).Error; err != nil {
+		Order("id").First(lastNeckActiveHabit).Error; err != nil {
+		return xerr.WithStack(err)
+	}
+
+	xToday := &XToday{}
+	if err := e.DB.Model(new(model.NeckActiveHabit)).
+		Select(`MIN(TO_DAYS(h.heat_date)*1000 + h.frameid) as xTodaySBegFrameid, MIN(h.heat_date) as xBegDate, 
+		MAX(TO_DAYS(h.heat_date)*1000 + h.frameid) as xTodaySMaxFrameid, MAX(h.heat_date) as xEndDate, MAX(h.id) as xEndUpdateActId`).
+		Where("id >= ?", lastNeckActiveHabit.Id).First(xToday).Error; err != nil {
 		return xerr.WithStack(err)
 	}
 
-	// 更新牛只最新数据
+	minHeatDate := struct {
+		HeatDate string
+	}{}
+
+	if err := e.DB.Model(new(model.NeckActiveHabit)).
+		Select("MIN(heat_date) as heat_date").
+		Where("id >= ?", lastNeckActiveHabit.Id).
+		First(&minHeatDate).Error; err != nil {
+		return xerr.WithStack(err)
+	}
+	minHeatDateParse, _ := time.Parse(model.LayoutDate2, minHeatDate.HeatDate)
+	xMin2Id := struct {
+		Id int64
+	}{}
+	if err := e.DB.Model(new(model.NeckActiveHabit)).
+		Select("IFNULL(MIN(id), ?) as id", lastNeckActiveHabit.Id).
+		Where("heat_date = ?", minHeatDateParse.AddDate(0, 0, -1).Format(model.LayoutDate2)).
+		First(&xMin2Id).Error; err != nil {
+		return xerr.WithStack(err)
+	}
+
+	xMin7Id := struct {
+		Id int64
+	}{}
+	XBegDateTime, _ := time.Parse(model.LayoutDate2, xToday.XBegDate)
+	if err := e.DB.Model(new(model.NeckActiveHabit)).
+		Select("MIN(id) as id").
+		Where("heat_date >= ?", XBegDateTime.AddDate(0, 0, -7).Format(model.LayoutDate2)).
+		First(&xMin7Id).Error; err != nil {
+		return xerr.WithStack(err)
+	}
+	// 更新is_max_time
 	if err := e.DB.Model(new(model.NeckActiveHabit)).
 		Where("is_max_time = ?", pasturePb.IsShow_Ok).
 		Update("is_max_time", pasturePb.IsShow_No).Error; err != nil {
 		return xerr.WithStack(err)
 	}
-
+	xBegDate, _ := time.Parse(model.LayoutDate2, xToday.XBegDate)
+	before7xBegDate := xBegDate.AddDate(0, 0, 7).Format(model.LayoutDate2)
 	sqlQuery := e.DB.Model(new(model.NeckActiveHabit)).
-		Select("Max(id) as id").
-		Where("id BETWEEN ? AND ?").
-		Where("change_filter > ?", -99).
-		Where("").Group("cow_id")
+		Select("MAX(id) as id").
+		Where("id BETWEEN ? AND ?", xMin2Id.Id, lastNeckActiveHabit.Id).
+		Where("change_filter > ?", MinChangeFilter).
+		Where("heat_date >", before7xBegDate).Group("cow_id")
 
 	if err := e.DB.Model(new(model.NeckActiveHabit)).
 		Joins("JOIN (?) bb ON neck_active_habit.id = bb.id", sqlQuery).
@@ -129,40 +184,364 @@ func (e *Entry) ActiveHabit() error {
 
 	activeLowest := e.GetSystemConfigure(model.ActiveLowest)
 	ruminaLowest := e.GetSystemConfigure(model.RuminaLowest)
+
+	// 更新活动滤波
+	if err := e.FilterUpdate(activeLowest.Value, ruminaLowest.Value); err != nil {
+		return xerr.WithStack(err)
+	}
+	// 更新周平均值
+	if err := e.WeeklyActiveAvgUpdate(xMin2Id.Id, activeLowest.Value, ruminaLowest.Value, xToday, lastNeckActiveHabit); err != nil {
+		return xerr.WithStack(err)
+	}
+	return nil
+}
+
+// FilterUpdate 更新活动滤波
+func (e *Entry) FilterUpdate(activeLowest, ruminaLowest int32) error {
 	newNeckActiveHabitList := make([]*model.NeckActiveHabit, 0)
 	if err := e.DB.Model(new(model.NeckActiveHabit)).
-		Where("is_show = ?", pasturePb.IsShow_No).
-		Where(e.DB.Where("change_filter = 0").Or("is_max_time = ?", pasturePb.IsShow_Ok)).
-		Where(e.DB.Where("high >= ?", activeLowest.Value).Or("rumina >= ?", ruminaLowest.Value)).
-		Order("cow_id,id").Find(&newNeckActiveHabitList).Error; err != nil {
+		Where(e.DB.Where("change_filter = ?", DefaultChangeFilter).Or("is_max_time = ?", pasturePb.IsShow_Ok)).
+		Where(e.DB.Where("high >= ?", activeLowest).Or("rumina >= ?", ruminaLowest)).
+		Order("cow_id,id").
+		Find(&newNeckActiveHabitList).Error; err != nil {
 		return xerr.WithStack(err)
 	}
 
-	var (
-		lastCowId    = int64(0)
-		filterValues = make(map[int64]*model.NeckActiveHabit)
-	)
+	var filterValues = make(map[int64]*model.NeckActiveHabit)
 	// 活动量滤波
 	for _, v := range newNeckActiveHabitList {
-		if v.CowId != lastCowId {
-			lastCowId = v.CowId
-			filterValues[v.CowId] = &model.NeckActiveHabit{CowId: v.CowId}
+		prev, ok := filterValues[v.CowId]
+		if !ok {
+			if v.FilterHigh <= 0 {
+				v.FilterHigh = v.High
+			}
+			if v.FilterRumina <= 0 {
+				v.FilterRumina = v.Rumina
+			}
+			if v.FilterChew <= 0 {
+				v.FilterChew = v.Rumina + v.Intake
+			}
+			filterValues[v.CowId] = v
+			continue
+		}
+		if v.FilterHigh <= 0 {
+			v.FilterHigh = int32(computeIfPositiveElse(float64(v.High), float64(prev.FilterHigh), 0.23, 0.77))
+		}
+		if v.FilterRumina <= 0 {
+			v.FilterRumina = int32(computeIfPositiveElse(float64(v.Rumina), float64(prev.FilterRumina), 0.33, 0.67))
+		}
+		if v.FilterChew <= 0 {
+			v.FilterChew = int32(computeIfPositiveElse(float64(v.Rumina+v.Intake), float64(prev.FilterChew), 0.33, 0.67))
 		}
-
-		prev := filterValues[v.CowId]
-		v.FilterHigh = int32(computeIfPositiveElse(float64(v.FilterHigh), float64(v.High), float64(prev.FilterHigh), 0.23, 0.77))
-		v.FilterRumina = int32(computeIfPositiveElse(float64(v.FilterRumina), float64(v.Rumina), float64(prev.FilterRumina), 0.33, 0.67))
-		v.FilterHigh = int32(computeIfPositiveElse(float64(v.FilterChew), float64(v.Rumina+v.Intake), float64(prev.FilterChew), 0.23, 0.77))
 		// 更新过滤值
 		filterValues[v.CowId] = v
+		if err := e.DB.Model(new(model.NeckActiveHabit)).
+			Select("filter_high", "filter_rumina", "filter_chew").
+			Where("id = ?", v.Id).
+			Updates(v).Error; err != nil {
+			return xerr.WithStack(err)
+		}
 	}
 	return nil
 }
 
-// 辅助函数来计算过滤值
-func computeIfPositiveElse(filterValue, newValue, prevFilterValue float64, weightPrev, weightNew float64) float64 {
-	if filterValue > 0 {
-		return filterValue
+func (e *Entry) WeeklyActiveAvgUpdate(xMin2Id int64, activeLowest, ruminaLowest int32, xToday *XToday, lastNeckActiveHabit *model.NeckActiveHabit) error {
+	before7DaysMinId := struct {
+		Id int64 `json:"id"`
+	}{
+		Id: 0,
+	}
+	before7DayDate, _ := time.Parse(model.LayoutDate2, xToday.XBegDate)
+	before7DayDateStr := before7DayDate.AddDate(0, 0, -7).Format(model.LayoutDate2)
+	before1DayDateStr := before7DayDate.AddDate(0, 0, -1).Format(model.LayoutDate2)
+
+	if err := e.DB.Model(new(model.NeckActiveHabit)).
+		Select("MIN(id) as id").
+		Where("heat_date >= ?", before7DayDateStr).
+		First(&before7DaysMinId).Error; err != nil {
+		return xerr.WithStack(err)
 	}
-	return math.Ceil(weightPrev*prevFilterValue + weightNew*newValue)
+
+	weeklyActive := e.GetSystemConfigure(model.WeeklyActive)
+	xframeId := int64(0)
+	maxXframeId := int64(11)
+	currDate, _ := time.Parse(model.LayoutDate2, xToday.XBegDate)
+	XEndDateTime, _ := time.Parse(model.LayoutDate2, xToday.XEndDate)
+	for currDate.Format(model.LayoutDate2) < XEndDateTime.Format(model.LayoutDate2) || (currDate == XEndDateTime && xframeId <= maxXframeId) {
+
+		//  时间点周平均
+		weeklyHabitList := make([]*WeeklyHabit, 0)
+		if err := e.DB.Model(new(model.NeckActiveHabit)).
+			Select("cow_id").
+			Select("IF(COUNT(1)>=3, ROUND((SUM(filter_high) -MIN(filter_high) -MAX(filter_high))/ABS(COUNT(1) -2),0), -1) as week_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 week_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 week_avg_chew_habit").
+			Select("ROUND(AVG(intake),0) as week_avg_intake_habit").
+			Select("ROUND(AVG(inactive),0) as week_avg_inactive_habit").
+			Where("id BETWEEN ? AND ?", xMin2Id, xToday.xEndUpdateActId).
+			Where("heat_date BETWEEN ? AND ?", before7DayDateStr, before1DayDateStr).
+			Where("frameid = ?", xframeId).
+			Where("change_filter = ?", DefaultChangeFilter).
+			Where(e.DB.Where("high > ?", activeLowest).Or("rumina > ?", ruminaLowest)).
+			Group("cow_id").
+			Find(&weeklyHabitList).Error; err != nil {
+			return xerr.WithStack(err)
+		}
+		for _, v := range weeklyHabitList {
+			if err := e.DB.Model(new(model.NeckActiveHabit)).
+				Select("week_avg_high_habit", "week_avg_rumina_habit", "week_avg_chew_habit", "week_avg_intake_habit", "week_avg_inactive_habit").
+				Where("cow_id = ?", v.CowId).
+				Where("id BETWEEN ? AND ?", lastNeckActiveHabit.Id, xToday.xEndUpdateActId).
+				Where("frameid = ?", xframeId).
+				Where("change_filter = ?", DefaultChangeFilter).
+				Where("heat_date = ?", currDate).
+				Updates(v).Error; err != nil {
+				return xerr.WithStack(err)
+			}
+		}
+
+		// 累计24小时数值
+		sumHabitList := make([]*SumHabit, 0)
+		if err := e.DB.Model(new(model.NeckActiveHabit)).
+			Select("cow_id").
+			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, MinChangeFilter).
+			Where("id BETWEEN ? AND ?", before7DaysMinId.Id, xToday.xEndUpdateActId).
+			Where("heat_date BETWEEN ? AND ?", currDate.AddDate(0, 0, -1).Format(model.LayoutDate2), currDate.Format(model.LayoutDate2)).
+			Where("created_at BETWEEN ? AND ?", currDate.Add(-23*time.Hour), currDate.Unix()).
+			Where(e.DB.Where("high > ?", activeLowest).Or("rumina >= ?", ruminaLowest)).
+			Group("cow_id").
+			Find(&sumHabitList).Error; err != nil {
+			return xerr.WithStack(err)
+		}
+
+		for _, v := range sumHabitList {
+			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").
+				Where("cow_id = ?", v.CowId).
+				Where("id BETWEEN ? AND ?", lastNeckActiveHabit.Id, xToday.xEndUpdateActId).
+				Where("heat_date = ?", currDate.Format(model.LayoutDate2)).
+				Where("frameid = ?", xframeId).
+				Where("change_filter = ?", DefaultChangeFilter).
+				Updates(v).Error; err != nil {
+				return xerr.WithStack(err)
+			}
+		}
+		// 变化百分比
+		changeHabitList := make([]*model.NeckActiveHabit, 0)
+		if err := e.DB.Model(new(model.NeckActiveHabit)).
+			Where("id BETWEEN ? AND ?", lastNeckActiveHabit.Id, xToday.xEndUpdateActId).
+			Where("heat_date = ?", currDate.Format(model.LayoutDate2)).
+			Where("frameid = ?", xframeId).
+			Where("change_filter = ?", DefaultChangeFilter).
+			Where("week_avg_high_habit > ?", 0).
+			Where(e.DB.Where("high > ?", activeLowest).Or("rumina >= ?", ruminaLowest)).
+			Find(&changeHabitList).Error; err != nil {
+			return xerr.WithStack(err)
+		}
+
+		for _, v := range changeHabitList {
+			if v.FilterHigh-v.WeekAvgHighHabit > 0 {
+				v.ChangeHigh = (v.FilterHigh - v.WeekAvgHighHabit) / int32(float64(v.WeekHigh)*0.6+float64(v.WeekAvgHighHabit)*0.2+float64(weeklyActive.Value)*0.2)
+			} else {
+				v.ChangeHigh = v.FilterHigh - v.WeekAvgHighHabit/v.WeekAvgHighHabit*100
+			}
+
+			v.ChangeRumina = v.RuminaFilter - v.WeekAvgRuminaHabit/v.WeekAvgHighHabit*100
+			v.ChangeChew = v.FilterChew - v.WeekAvgChewHabit/v.WeekAvgHighHabit*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)
+			}
+		}
+
+		if xframeId == maxXframeId {
+			xframeId = 0
+			currDate = currDate.AddDate(0, 0, 1)
+		} else {
+			xframeId++
+		}
+	}
+
+	return nil
+}
+
+// UpdateChangeFilter  变化趋势滤波
+func (e *Entry) UpdateChangeFilter(xMin2Id int64, xToday *XToday) error {
+	xRuminaDisc := e.GetSystemConfigure(model.XRuminaDisc)
+	xChangeDiscount := e.GetSystemConfigure(model.XChangeDiscount)
+	newChangeFilterList := make([]*ChangeFilterData, 0)
+	if err := e.DB.Model(new(model.NeckActiveHabit)).
+		Select("id,cow_id,change_high,change_filter,rumina_filter,change_rumina,chew_filter,change_chew").
+		Select("IF(lact=0,0.8,1) as xlc_dis_count").
+		Where("id BETWEEN ? AND ?", xMin2Id, xToday.xEndUpdateActId).
+		Where(e.DB.Where("change_filter = ?", DefaultChangeFilter).Or("is_max_time = ?", pasturePb.IsShow_Ok)).
+		Where("change_high > ?", MinChangeHigh).
+		Order("cow_id,heat_date,frameid").
+		Find(&newChangeFilterList).Error; err != nil {
+		return xerr.WithStack(err)
+	}
+	var filterValues = make(map[int64]*ChangeFilterData)
+	for _, v := range newChangeFilterList {
+		prev, ok := filterValues[v.CowId]
+		if v.ChangeFilter <= MinChangeFilter {
+			prefChangeFilter := int32(0)
+			if ok {
+				prefChangeFilter = prev.ChangeFilter
+			}
+			leastValue := v.HighChange
+			if prefChangeFilter < v.HighChange {
+				leastValue = prefChangeFilter
+			}
+			v.ChangeFilter = int32(float64(prefChangeFilter)*(1-(float64(xChangeDiscount.Value)/10)*v.XlcDisCount) +
+				float64(leastValue)*(float64(xChangeDiscount.Value)/10)*v.XlcDisCount)
+		}
+
+		if v.RuminaFilter <= MinChangeFilter {
+			prefRuminaFilter := int32(0)
+			if ok {
+				prefRuminaFilter = prev.RuminaFilter
+			}
+
+			factor := float64(1)
+			if math.Abs(float64(v.ChangeRumina)) > 60 {
+				factor = 0.5
+			}
+
+			v.RuminaFilter = int32(float64(prefRuminaFilter)*(1-float64(xRuminaDisc.Value/10)*v.XlcDisCount*factor) +
+				float64(v.ChangeRumina)*float64(xRuminaDisc.Value)/10*v.XlcDisCount*factor)
+
+		}
+		if v.RuminaFilter > 50 {
+			v.RuminaFilter = 50
+		}
+
+		if v.ChewFilter <= MinChangeFilter {
+			prefChewFilter := int32(0)
+			if ok {
+				prefChewFilter = prev.ChewFilter
+			}
+			factor := float64(1)
+			if math.Abs(float64(v.ChangeChew)) > 60 {
+				factor = 0.5
+			}
+
+			v.ChewFilter = int32(float64(prefChewFilter)*(1-float64(xRuminaDisc.Value)/10*factor) +
+				float64(v.ChangeChew)*float64(xRuminaDisc.Value)/10*factor)
+		}
+
+		if v.ChewFilter > 50 {
+			v.ChangeChew = 50
+		}
+		if err := e.DB.Model(new(model.NeckActiveHabit)).
+			Select("change_filter", "rumina_filter", "chew_filter").
+			Where("id = ?", v.Id).Where("cow_id = ?", v.CowId).
+			Updates(v).Error; err != nil {
+			return xerr.WithStack(err)
+		}
+		filterValues[v.CowId] = v
+	}
+
+	return nil
+}
+
+// ActivityVolumeChanges 计算活动量变化趋势校正值(活跃度校正)
+func (e *Entry) ActivityVolumeChanges(xMin7Id int64, activeLowest, ruminaLowest int32, xToday *XToday, lastNeckActiveHabitId int64) 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) {
+		if dayTimes == 1 {
+			ActivityVolumeList := make([]*ActivityVolume, 0)
+			if err := e.DB.Model(new(model.NeckActiveHabit)).
+				Select("cow_id").
+				Where("id BETWEEN ? AND ?", xMin7Id, xToday.xEndUpdateActId).
+				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(e.DB.Where("high > ?", activeLowest).Or("rumina >= ?", ruminaLowest)).
+				Where("created_at <= ?", currDate.Add(-12*time.Hour).Unix()).
+				Group("cow_id").
+				Having("nb > ?", 30).
+				Find(&ActivityVolumeList).Error; err != nil {
+				return xerr.WithStack(err)
+			}
+			for _, v := range ActivityVolumeList {
+				filterCorrect := 100 - math.Floor(float64(v.AvgFilter)/3+float64(v.StdFilter)/2)
+				if err := e.DB.Model(new(model.NeckActiveHabit)).
+					Where("cow_id = ?", v.CowId).
+					Where("id BETWEEN ? AND ?", lastNeckActiveHabitId, xToday.xEndUpdateActId). // todo: 待完善
+					Where("frameid = ?", xframeId).
+					Where("head_date = ?", currDate.Format(model.LayoutDate2)).
+					Update("filter_correct", filterCorrect).Error; err != nil {
+					return xerr.WithStack(err)
+				}
+			}
+		}
+
+		n := 0
+		if n <= 10 {
+
+			// todo
+
+			n += 2
+		}
+
+		if err := e.DB.Model(new(model.NeckActiveHabit)).
+			Where("id BETWEEN ? AND ?", lastNeckActiveHabitId, xToday.xEndUpdateActId).
+			Where("heat_date = ?", currDate.Format(model.LayoutDate2)).
+			Where("frameid = ?", xframeId).
+			Where("change_filter = ?", 0).
+			Update("change_filter", MinChangeHigh).Error; err != nil {
+			return xerr.WithStack(err)
+		}
+
+		if err := e.DB.Model(new(model.NeckActiveHabit)).
+			Where("id BETWEEN ? AND ?", lastNeckActiveHabitId, xToday.xEndUpdateActId).
+			Where("heat_date = ?", currDate.Format(model.LayoutDate2)).
+			Where("frameid = ?", xframeId).
+			Where("rumina_filter = ?", 0).
+			Update("rumina_filter", MinChangeHigh).Error; err != nil {
+			return xerr.WithStack(err)
+		}
+
+		if err := e.DB.Model(new(model.NeckActiveHabit)).
+			Where("id BETWEEN ? AND ?", lastNeckActiveHabitId, xToday.xEndUpdateActId).
+			Where("heat_date = ?", currDate.Format(model.LayoutDate2)).
+			Where("frameid = ?", xframeId).
+			Where("filter_correct < ?", 100).
+			Where("change_filter < ?", 0).
+			Update("filter_correct", 100).Error; err != nil {
+			return xerr.WithStack(err)
+		}
+
+		// 更新评分
+		newNeckActiveHabitList := make([]*model.NeckActiveHabit, 0)
+		if err := e.DB.Model(new(model.NeckActiveHabit)).
+			Where("id BETWEEN ? AND ?", lastNeckActiveHabitId, xToday.xEndUpdateActId).
+			Where("heat_date = ?", currDate.Format(model.LayoutDate2)).
+			Where("frameid = ?", xframeId).
+			Where("score = ?", 0).
+			Find(&newNeckActiveHabitList).Error; err != nil {
+			return xerr.WithStack(err)
+		}
+		/*for _, v := range newNeckActiveHabitList {
+
+		}*/
+	}
+
+	return nil
+}
+
+// 辅助函数来计算过滤值
+func computeIfPositiveElse(newValue, prevFilterValue float64, weightPrev, weightNew float64) float64 {
+	return math.Ceil((prevFilterValue * weightPrev) + (weightNew * newValue))
 }

+ 35 - 49
module/crontab/work_cron_test.go

@@ -10,67 +10,53 @@ import (
 func TestEntry_SameTimePlan(t *testing.T) {
 	neckActiveHabit := make([]*model.NeckActiveHabit, 0)
 	neckActiveHabit = append(neckActiveHabit, &model.NeckActiveHabit{
-		Id:           15408026,
-		CowId:        117,
-		High:         179,
+		Id:           14489593,
+		CowId:        71,
+		High:         1611,
+		Rumina:       32,
+		Intake:       67,
 		FilterHigh:   0,
-		Rumina:       61,
 		FilterRumina: 0,
-		Intake:       0,
-		FilterChew:   59,
+		FilterChew:   0,
 	}, &model.NeckActiveHabit{
-		Id:           15408028,
-		CowId:        294,
-		High:         724,
-		FilterHigh:   0,
-		Rumina:       33,
-		FilterRumina: 0,
-		Intake:       18,
-		FilterChew:   45,
-	}, /*, &model.NeckActiveHabit{
-		Id:           15406800,
-		CowId:        297,
-		High:         944,
-		FilterHigh:   0,
-		Rumina:       24,
+		Id:           14490832,
+		CowId:        71,
+		High:         1295,
+		Rumina:       26,
+		Intake:       46,
 		FilterRumina: 0,
-		Intake:       7,
-		FilterChew:   29,
-	}, &model.NeckActiveHabit{
-		Id:           15406801,
-		CowId:        299,
-		High:         615,
-		FilterHigh:   0,
-		Rumina:       37,
-		FilterRumina: 43,
-		Intake:       1,
-		FilterChew:   46,
-	}, &model.NeckActiveHabit{
-		Id:           15405632,
-		CowId:        315,
-		High:         1128,
 		FilterHigh:   0,
-		Rumina:       16,
-		FilterRumina: 18,
-		Intake:       37,
-		FilterChew:   45,
-	}*/)
+		FilterChew:   0,
+	})
 
 	var (
-		lastCowId    = int64(0)
 		filterValues = make(map[int64]*model.NeckActiveHabit)
 	)
 	// 活动量滤波
 	for _, v := range neckActiveHabit {
-		if v.CowId != lastCowId {
-			lastCowId = v.CowId
-			filterValues[v.CowId] = &model.NeckActiveHabit{CowId: v.CowId}
+		prev, ok := filterValues[v.CowId]
+		if !ok {
+			if v.FilterHigh <= 0 {
+				v.FilterHigh = v.High
+			}
+			if v.FilterRumina <= 0 {
+				v.FilterRumina = v.Rumina
+			}
+			if v.FilterChew <= 0 {
+				v.FilterChew = v.Rumina + v.Intake
+			}
+			filterValues[v.CowId] = v
+			continue
+		}
+		if v.FilterHigh <= 0 {
+			v.FilterHigh = int32(computeIfPositiveElse(float64(v.High), float64(prev.FilterHigh), 0.23, 0.77))
+		}
+		if v.FilterRumina <= 0 {
+			v.FilterRumina = int32(computeIfPositiveElse(float64(v.Rumina), float64(prev.FilterRumina), 0.33, 0.67))
+		}
+		if v.FilterChew <= 0 {
+			v.FilterChew = int32(computeIfPositiveElse(float64(v.Rumina+v.Intake), float64(prev.FilterChew), 0.33, 0.67))
 		}
-
-		prev := filterValues[v.CowId]
-		v.FilterHigh = int32(computeIfPositiveElse(float64(v.FilterHigh), float64(v.High), float64(prev.FilterHigh), 0.23, 0.77))
-		v.FilterRumina = int32(computeIfPositiveElse(float64(v.FilterRumina), float64(v.Rumina), float64(prev.FilterRumina), 0.33, 0.67))
-		v.FilterHigh = int32(computeIfPositiveElse(float64(v.FilterChew), float64(v.Rumina+v.Intake), float64(prev.FilterChew), 0.23, 0.77))
 		// 更新过滤值
 		filterValues[v.CowId] = v
 	}

+ 14 - 0
store/kptstore/rw_store.go

@@ -54,6 +54,13 @@ func MustNewStore(cfg *config.AppConfig) *DB {
 	if cfg.StoreSetting.ShowSQL {
 		db.Logger.LogMode(logger.Info)
 	}
+	sqlDb, _ := db.DB()
+	sqlDb.SetMaxOpenConns(100)             // 设置最大空闲连接数为10个
+	sqlDb.SetConnMaxIdleTime(10)           // 设置可打开的最大连接数为 100 个
+	sqlDb.SetConnMaxLifetime(300000000000) // 5分钟 设置一个连接空闲后在多长时间内可复用,上面配置文件里设置的是300000000000, 因为Go的time.Duration底层类型是int64, 一秒是1000000000,这个大家可设置一个适当的时间,一般5~15分钟,不要太长
+	if err = sqlDb.Ping(); err != nil {
+		panic(xerr.WithStack(err))
+	}
 
 	return NewStore(db)
 }
@@ -71,5 +78,12 @@ func MustMigrateStore(cfg *config.AppConfig) *gorm.DB {
 	if cfg.StoreSetting.ShowSQL {
 		db.Logger.LogMode(logger.Info)
 	}
+	sqlDb, _ := db.DB()
+	sqlDb.SetMaxOpenConns(100)             // 设置最大空闲连接数为10个
+	sqlDb.SetConnMaxIdleTime(10)           // 设置可打开的最大连接数为 100 个
+	sqlDb.SetConnMaxLifetime(300000000000) // 5分钟 设置一个连接空闲后在多长时间内可复用,上面配置文件里设置的是300000000000, 因为Go的time.Duration底层类型是int64, 一秒是1000000000,这个大家可设置一个适当的时间,一般5~15分钟,不要太长
+	if err = sqlDb.Ping(); err != nil {
+		panic(xerr.WithStack(err))
+	}
 	return db
 }