Sfoglia il codice sorgente

dashboard: indicators optimize

Yi 1 mese fa
parent
commit
b646c7fd32

+ 1 - 1
go.mod

@@ -3,7 +3,7 @@ module kpt-pasture
 go 1.17
 
 require (
-	gitee.com/xuyiping_admin/go_proto v0.0.0-20250609060156-a9a9e557016e
+	gitee.com/xuyiping_admin/go_proto v0.0.0-20250611092629-ca524a76d63c
 	gitee.com/xuyiping_admin/pkg v0.0.0-20250514071642-f92d2ac9a85d
 	github.com/dgrijalva/jwt-go v3.2.0+incompatible
 	github.com/eclipse/paho.mqtt.golang v1.4.3

+ 2 - 0
go.sum

@@ -150,6 +150,8 @@ gitee.com/xuyiping_admin/go_proto v0.0.0-20250609031915-1c289a676298 h1:oqXkLG8j
 gitee.com/xuyiping_admin/go_proto v0.0.0-20250609031915-1c289a676298/go.mod h1:BKrFW6YLDectlQcQk3FYKBeXvjEiodAKJ5rq7O/QiPE=
 gitee.com/xuyiping_admin/go_proto v0.0.0-20250609060156-a9a9e557016e h1:xDIjht8Ol7BICYL3+Y9WZmM8JSM5bO+X8L09RskPkW0=
 gitee.com/xuyiping_admin/go_proto v0.0.0-20250609060156-a9a9e557016e/go.mod h1:BKrFW6YLDectlQcQk3FYKBeXvjEiodAKJ5rq7O/QiPE=
+gitee.com/xuyiping_admin/go_proto v0.0.0-20250611092629-ca524a76d63c h1:qmsW30h4rIb9gmyHp+zgIK58TIyFElUo9ntH0SRgU2Y=
+gitee.com/xuyiping_admin/go_proto v0.0.0-20250611092629-ca524a76d63c/go.mod h1:BKrFW6YLDectlQcQk3FYKBeXvjEiodAKJ5rq7O/QiPE=
 gitee.com/xuyiping_admin/pkg v0.0.0-20241108060137-caea58c59f5b h1:w05MxH7yqveRlaRbxHhbif5YjPrJFodRPfOjYhXn7Zk=
 gitee.com/xuyiping_admin/pkg v0.0.0-20241108060137-caea58c59f5b/go.mod h1:8tF25X6pE9WkFCczlNAC0K2mrjwKvhhp02I7o0HtDxY=
 gitee.com/xuyiping_admin/pkg v0.0.0-20250514071642-f92d2ac9a85d h1:vBXmMRggF7mZVPGRDgavZ87igJgkezwX0a3v1/XtIMQ=

+ 1 - 1
http/route/system_api.go

@@ -23,7 +23,7 @@ func SystemAPI(opts ...func(engine *gin.Engine)) func(s *gin.Engine) {
 		// system API 组  系统用户
 		systemRoute := authRouteGroup(s, "/api/v1/system/")
 		systemRoute.POST("/user/list", system.SearchSystemUserList)
-		systemRoute.PUT("/user/is_show/:id", system.IsShowSystemUser)
+		systemRoute.PUT("/user/isShow/:id", system.IsShowSystemUser)
 		systemRoute.DELETE("/user/:id", system.DeleteUser)
 		systemRoute.POST("/user/createOrUpdate", system.UserCreateOrUpdate)
 		systemRoute.POST("/user/rest/password", system.ResetPasswordSystemUser)

+ 2 - 3
model/system_user.go

@@ -2,7 +2,6 @@ package model
 
 import (
 	"encoding/json"
-	"fmt"
 	"strconv"
 	"strings"
 	"time"
@@ -44,7 +43,7 @@ func (s *SystemUser) UserUpdate(req *pasturePb.SearchUserRequest, pastureIds str
 	s.PastureIds = pastureIds
 }
 
-func NewSystemUser(req *pasturePb.SearchUserRequest, deptIds, pastureIds string, pastureDepthList []*pasturePb.PastureDepthDetail) *SystemUser {
+func NewSystemUser(req *pasturePb.SearchUserRequest, pastureIds string, pastureDepthList []*pasturePb.PastureDepthDetail) *SystemUser {
 	spare := ""
 	if len(pastureDepthList) > 0 {
 		bt, _ := json.Marshal(pastureDepthList)
@@ -57,7 +56,7 @@ func NewSystemUser(req *pasturePb.SearchUserRequest, deptIds, pastureIds string,
 		Mobile:          req.Mobile,
 		Password:        req.Password,
 		Avatar:          "https://avatars.githubusercontent.com/u/9510375",
-		IndicatorsKinds: fmt.Sprintf("%s,%s,%s,%s,%s,%s", AllCow, OutNumber, InputNumber, SalesVolume, FattenCattleNumber, AdultCow),
+		IndicatorsKinds: DefaultFocusIndicators,
 		PastureIds:      pastureIds,
 		IsShow:          pasturePb.IsShow_Ok,
 		IsDelete:        pasturePb.IsShow_Ok,

+ 19 - 43
module/backend/config_data.go

@@ -6,50 +6,25 @@ import (
 	pasturePb "gitee.com/xuyiping_admin/go_proto/proto/go/backend/cow"
 )
 
-func (s *StoreEntry) BarnTypeEnumList() []*pasturePb.ConfigOptionsList {
+func (s *StoreEntry) BarnTypeEnumList(excludeTypes []pasturePb.PenType_Kind) []*pasturePb.ConfigOptionsList {
+	allBarnTypes := s.BarnTypeMap()
 	barnTypeList := make([]*pasturePb.ConfigOptionsList, 0)
-	barnTypeList = append(barnTypeList, &pasturePb.ConfigOptionsList{
-		Value:    int32(pasturePb.PenType_Lactating_Calves),
-		Label:    "哺乳牛舍",
-		Disabled: true,
-	}, &pasturePb.ConfigOptionsList{
-		Value:    int32(pasturePb.PenType_Weaned_Calves),
-		Label:    "断奶牛舍",
-		Disabled: true,
-	}, &pasturePb.ConfigOptionsList{
-		Value:    int32(pasturePb.PenType_Youth),
-		Label:    "青年牛舍",
-		Disabled: true,
-	}, &pasturePb.ConfigOptionsList{
-		Value:    int32(pasturePb.PenType_Nurturing),
-		Label:    "育成牛舍",
-		Disabled: true,
-	}, &pasturePb.ConfigOptionsList{
-		Value:    int32(pasturePb.PenType_Lactation),
-		Label:    "成母牛舍",
-		Disabled: true,
-	}, &pasturePb.ConfigOptionsList{
-		Value:    int32(pasturePb.PenType_Peripartum),
-		Label:    "围产牛舍",
-		Disabled: true,
-	}, &pasturePb.ConfigOptionsList{
-		Value:    int32(pasturePb.PenType_Dry_Milking),
-		Label:    "干奶牛舍",
-		Disabled: true,
-	}, &pasturePb.ConfigOptionsList{
-		Value:    int32(pasturePb.PenType_Sick_Cow),
-		Label:    "病牛舍",
-		Disabled: true,
-	}, &pasturePb.ConfigOptionsList{
-		Value:    int32(pasturePb.PenType_Out),
-		Label:    "淘汰牛舍",
-		Disabled: true,
-	}, &pasturePb.ConfigOptionsList{
-		Value:    int32(pasturePb.PenType_Segregate),
-		Label:    "隔离牛舍",
-		Disabled: true,
-	})
-
+	for v1 := range allBarnTypes {
+		var info bool
+		for _, v2 := range excludeTypes {
+			if v1 == v2 {
+				info = true
+				break
+			}
+		}
+		if !info {
+			barnTypeList = append(barnTypeList, &pasturePb.ConfigOptionsList{
+				Value:    int32(v1),
+				Label:    allBarnTypes[v1],
+				Disabled: true,
+			})
+		}
+	}
 	return barnTypeList
 }
 
@@ -134,6 +109,7 @@ func (s *StoreEntry) CowSourceEnumList() []*pasturePb.ConfigOptionsList {
 	})
 	return cowSourceList
 }
+
 func (s *StoreEntry) CowTypeEnumList(optionName, isAll string) []*pasturePb.ConfigOptionsList {
 	cowTypeList := make([]*pasturePb.ConfigOptionsList, 0)
 	if isAll == model.IsAllYes {

+ 1 - 2
module/backend/config_data_base.go

@@ -1,7 +1,6 @@
 package backend
 
 import (
-	"context"
 	"kpt-pasture/model"
 
 	pasturePb "gitee.com/xuyiping_admin/go_proto/proto/go/backend/cow"
@@ -270,7 +269,7 @@ func (s *StoreEntry) IndicatorsDetailsList(isAll string) []*pasturePb.ConfigOpti
 			})
 	}
 
-	indicatorsDetailsList, _ := s.FindIndicatorsDetailsList(context.Background())
+	indicatorsDetailsList, _ := s.FindIndicatorsDetailsList()
 	if len(indicatorsDetailsList) <= 0 {
 		return configOptions
 	}

+ 86 - 46
module/backend/dashboard_more.go

@@ -2,6 +2,7 @@ package backend
 
 import (
 	"context"
+	"fmt"
 	"kpt-pasture/model"
 	"kpt-pasture/util"
 	"net/http"
@@ -33,7 +34,8 @@ func (s *StoreEntry) NeckRingWarning(ctx context.Context) (*pasturePb.IndexNeckR
 	if err != nil {
 		return nil, xerr.Customf("系统错误!")
 	}
-	if err = pref.Order("a.level DESC").
+	if err = pref.Group("a.cow_id").
+		Order("a.level,a.id DESC").
 		Count(&count).
 		Find(&neckRingEstrusList).Error; err != nil {
 		return nil, xerr.WithStack(err)
@@ -46,10 +48,33 @@ func (s *StoreEntry) NeckRingWarning(ctx context.Context) (*pasturePb.IndexNeckR
 		"middle": 0,
 		"behind": 0,
 	}
+
+	cowIds := make([]int64, 0)
+	for _, v := range neckRingEstrusList {
+		cowIds = append(cowIds, v.CowId)
+	}
+
+	cowList := make([]*model.Cow, 0)
+	if err = s.DB.Model(new(model.Cow)).
+		Where("pasture_id = ?", userModel.AppPasture.Id).
+		Where("admission_status = ?", pasturePb.AdmissionStatus_Admission).
+		Where("id IN ?", cowIds).
+		Find(&cowList).Error; err != nil {
+		zaplog.Error("NeckRingWarning", zap.Any("err", err))
+	}
+	cowByIdMap := map[int64]*model.Cow{}
+	for _, v := range cowList {
+		cowByIdMap[v.Id] = v
+	}
+
 	for _, v := range neckRingEstrusList {
 		estrusWarningLevelItems[int32(v.Level)] += 1
 		countEstrusWarning += 1
-		cowInfo, _ := s.GetCowInfoByEarNumber(ctx, userModel.AppPasture.Id, v.EarNumber)
+		cowInfo, ok := cowByIdMap[v.CowId]
+		if !ok {
+			zaplog.Info("NeckRingWarning", zap.Any("v", v), zap.Any("cowByIdMap", cowByIdMap))
+			continue
+		}
 		pzHour := v.CalculatePzHour(cowInfo.Lact)
 		optimumMatingStartTime := pzHour.Add(-4 * time.Hour)
 		optimumMatingEndTime := pzHour.Add(4 * time.Hour)
@@ -72,7 +97,8 @@ func (s *StoreEntry) NeckRingWarning(ctx context.Context) (*pasturePb.IndexNeckR
 		return nil, xerr.Customf("系统错误!")
 	}
 
-	if err = pref.Group("cow_id").Count(&abortionCount).Error; err != nil {
+	if err = pref.Group("cow_id").
+		Count(&abortionCount).Error; err != nil {
 		return nil, xerr.WithStack(err)
 	}
 
@@ -111,73 +137,87 @@ func (s *StoreEntry) FocusIndicatorsList(ctx context.Context, dimension string)
 		userFocusIndicators = model.DefaultFocusIndicators
 	}
 	userFocusIndicatorsList := strings.Split(userFocusIndicators, ",")
-	indicatorsDataList := make([]*model.IndicatorsData, 0)
-	pref := s.DB.Model(new(model.IndicatorsData)).
-		Where("pasture_id = ?", userModel.AppPasture.Id).
-		Where("kind in (?)", userFocusIndicatorsList)
-
-	/*if dimension == "Year" {
-		pref.Where("date = ?", time.Now().Local().Format(model.LayoutMonth))
-	}*/
-
-	nowTime := time.Now().Local()
-	if dimension == "Month" {
-		pref.Where("date = ?", nowTime.Format(model.LayoutMonth))
+	// 获取用户关注的指标
+	userFocusIndicatorsDetailsList := make([]*model.IndicatorsDetails, 0)
+	if err = s.DB.Model(new(model.IndicatorsDetails)).
+		Where("kind in (?)", userFocusIndicatorsList).
+		Find(&userFocusIndicatorsDetailsList).Error; err != nil {
+		zaplog.Error("FocusIndicators", zap.Any("err", err))
 	}
 
-	if err = pref.Find(&indicatorsDataList).Error; err != nil {
+	nowTime := time.Now().Local()
+	currentMonth := nowTime.Format(model.LayoutMonth)
+	lastMonth := nowTime.AddDate(0, -1, 0).Format(model.LayoutMonth)
+	lastYear := nowTime.AddDate(-1, 0, 0).Format(model.LayoutYear)
+	indicatorsDataList := make([]*model.IndicatorsData, 0)
+	if err = s.DB.Model(new(model.IndicatorsData)).
+		Where("pasture_id = ?", userModel.AppPasture.Id).
+		Where("kind in (?)", userFocusIndicatorsList).
+		Where("date IN ?", []string{currentMonth, lastMonth, lastYear}).
+		Order("kind,date").
+		Find(&indicatorsDataList).Error; err != nil {
 		zaplog.Error("FocusIndicators", zap.Any("err", err))
 	}
 
-	indicatorsDetailsMap, _, err := s.GetIndicatorsDetailsMap(ctx)
-	if err != nil {
-		return nil, xerr.WithStack(err)
-	}
 	focusIndicatorsList := make([]*pasturePb.FocusIndicators, 0)
+	indicatorsDataByKindMap := map[string][]*model.IndicatorsData{}
 	for _, v := range indicatorsDataList {
-		indicatorsDetails, ok := indicatorsDetailsMap[v.Kind]
-		if !ok {
-			continue
+		if indicatorsDataByKindMap[v.Kind] == nil {
+			indicatorsDataByKindMap[v.Kind] = make([]*model.IndicatorsData, 0)
 		}
-		onYear, onMonth := "", ""
+		indicatorsDataByKindMap[v.Kind] = append(indicatorsDataByKindMap[v.Kind], v)
+	}
+
+	for _, indicatorsDetails := range userFocusIndicatorsDetailsList {
+		indicatorsList := indicatorsDataByKindMap[indicatorsDetails.Kind]
+		onYear, onMonth, currValue, onValue := "", "", float64(0), ""
 		isUp := pasturePb.IsShow_Ok
+		for _, v := range indicatorsList {
+			if v.Date == currentMonth {
+				currValue, _ = strconv.ParseFloat(v.Value, 64)
+			}
+			if v.Date == lastYear {
+				onYear = v.Value
+			}
+			if v.Date == lastMonth {
+				onMonth = v.Value
+			}
+		}
+
+		oldValue := float64(0)
 		if dimension == "Year" {
-			return nil, xerr.Custom("暂不支持该维度")
+			oldValue, _ = strconv.ParseFloat(onYear, 64)
 		}
 
 		if dimension == "Month" {
-			lastMonth := nowTime.AddDate(0, -1, 0).Format(model.LayoutMonth)
-			oldIndicators, _ := s.GetIndicatorsDataByDate(userModel.AppPasture.Id, v.Kind, lastMonth)
-			if oldIndicators != nil {
-				if oldIndicators.Value != "" && oldIndicators.Value != "0" {
-					oldValue, _ := strconv.ParseFloat(oldIndicators.Value, 64)
-					currValue, _ := strconv.ParseFloat(v.Value, 64)
-					onMonthValue := float64(0)
-					if oldValue > 0 {
-						onMonthValue = (oldValue - currValue) / oldValue * 100
-					}
-					omv := util.RoundToTwoDecimals(onMonthValue)
-					if omv < 0 {
-						isUp = pasturePb.IsShow_No
-					}
-					onMonth = strconv.FormatFloat(omv, 'f', 2, 64) + "%"
-				}
+			oldValue, _ = strconv.ParseFloat(onMonth, 64)
+		}
+
+		if currValue > 0 && oldValue > 0 {
+			b := float64(0)
+			if oldValue > 0 {
+				b = (oldValue - currValue) / oldValue * 100
+			}
+			omv := util.RoundToTwoDecimals(b)
+			if omv < 0 {
+				isUp = pasturePb.IsShow_No
 			}
+			onValue = strconv.FormatFloat(omv, 'f', 2, 64) + "%"
 		}
+
 		focusIndicatorsList = append(focusIndicatorsList, &pasturePb.FocusIndicators{
 			Kind:     indicatorsDetails.Kind,
 			Name:     indicatorsDetails.Name,
-			Value:    v.Value,
+			Value:    fmt.Sprintf("%f", currValue),
 			Describe: indicatorsDetails.Zh,
 			UnitName: indicatorsDetails.Unit,
-			OnMonth:  onMonth,
-			OnYear:   onYear,
 			IsUp:     isUp,
+			OnYear:   onValue,
+			OnMonth:  onValue,
 		})
 	}
 
-	indicatorsDetailsList, _ := s.FindIndicatorsDetailsList(ctx)
-
+	indicatorsDetailsList, _ := s.FindIndicatorsDetailsList()
 	return &pasturePb.IndexFocusIndicatorsResponse{
 		Code: http.StatusOK,
 		Msg:  "ok",

+ 21 - 13
module/backend/enum_map.go

@@ -188,15 +188,15 @@ func (s *StoreEntry) DiseaseTypeMap() map[int32]string {
 }
 
 func (s *StoreEntry) MultiFactorAnalysisMethodMap() map[pasturePb.MultiFactorAnalysisMethod_Kind]string {
-	res := make(map[pasturePb.MultiFactorAnalysisMethod_Kind]string)
-	res[pasturePb.MultiFactorAnalysisMethod_Months] = "months"
-	res[pasturePb.MultiFactorAnalysisMethod_Week] = "week"
-	res[pasturePb.MultiFactorAnalysisMethod_Operation] = "operation_name"
-	res[pasturePb.MultiFactorAnalysisMethod_Bull] = "frozen_semen_number"
-	res[pasturePb.MultiFactorAnalysisMethod_Lact] = "lact"
-	res[pasturePb.MultiFactorAnalysisMethod_Mating_Times] = "mating_times"
-	res[pasturePb.MultiFactorAnalysisMethod_Breeding_Method] = "expose_estrus_type"
-	return res
+	return map[pasturePb.MultiFactorAnalysisMethod_Kind]string{
+		pasturePb.MultiFactorAnalysisMethod_Months:          "months",
+		pasturePb.MultiFactorAnalysisMethod_Week:            "week",
+		pasturePb.MultiFactorAnalysisMethod_Operation:       "operation_name",
+		pasturePb.MultiFactorAnalysisMethod_Bull:            "frozen_semen_number",
+		pasturePb.MultiFactorAnalysisMethod_Lact:            "lact",
+		pasturePb.MultiFactorAnalysisMethod_Mating_Times:    "mating_times",
+		pasturePb.MultiFactorAnalysisMethod_Breeding_Method: "expose_estrus_type",
+	}
 }
 
 func (s *StoreEntry) NeckRingIsBindMap() map[pasturePb.NeckRingIsBind_Kind]string {
@@ -320,11 +320,19 @@ func (s *StoreEntry) GroupTransferReasonMap() map[int32]string {
 }
 
 func (s *StoreEntry) BarnTypeMap() map[pasturePb.PenType_Kind]string {
-	res := make(map[pasturePb.PenType_Kind]string)
-	for _, v := range s.BarnTypeEnumList() {
-		res[pasturePb.PenType_Kind(v.Value)] = v.Label
+	return map[pasturePb.PenType_Kind]string{
+		pasturePb.PenType_Lactating_Calves: "哺乳牛舍",
+		pasturePb.PenType_Weaned_Calves:    "断奶牛舍",
+		pasturePb.PenType_Youth:            "青年牛舍",
+		pasturePb.PenType_Nurturing:        "育成牛舍",
+		pasturePb.PenType_Lactation:        "成母牛舍",
+		pasturePb.PenType_Peripartum:       "围产牛舍",
+		pasturePb.PenType_Dry_Milking:      "干奶牛舍",
+		pasturePb.PenType_Sick_Cow:         "病牛舍",
+		pasturePb.PenType_Out:              "淘汰牛舍",
+		pasturePb.PenType_Segregate:        "隔离牛舍",
+		pasturePb.PenType_Bull:             "公牛牛舍",
 	}
-	return res
 }
 
 func (s *StoreEntry) UnMatingReasonsMap() map[pasturePb.UnMatingReasons_Kind]string {

+ 36 - 15
module/backend/enum_options.go

@@ -12,10 +12,20 @@ import (
 )
 
 func (s *StoreEntry) BarnTypeOptions(ctx context.Context) (*pasturePb.ConfigOptionsListResponse, error) {
+	userModel, err := s.GetUserModel(ctx)
+	if err != nil {
+		return nil, xerr.WithStack(err)
+	}
+
+	excludeTypes := make([]pasturePb.PenType_Kind, 0)
+	if userModel.AppPasture.Category == pasturePb.PastureCategory_Beef {
+		excludeTypes = append(excludeTypes, pasturePb.PenType_Dry_Milking, pasturePb.PenType_Peripartum)
+	}
+
 	return &pasturePb.ConfigOptionsListResponse{
 		Code: http.StatusOK,
 		Msg:  "ok",
-		Data: s.BarnTypeEnumList(),
+		Data: s.BarnTypeEnumList(excludeTypes),
 	}, nil
 }
 
@@ -36,24 +46,37 @@ func (s *StoreEntry) BarnListOptions(ctx context.Context, penType int, isAll str
 		return nil, err
 	}
 
+	excludeTypes := make([]pasturePb.PenType_Kind, 0)
+	if userModel.AppPasture.Category == pasturePb.PastureCategory_Beef {
+		excludeTypes = append(excludeTypes, pasturePb.PenType_Dry_Milking, pasturePb.PenType_Peripartum)
+	}
+
+	barnTypeList := s.BarnTypeEnumList(excludeTypes)
 	return &pasturePb.ConfigOptionsListResponse{
 		Code: http.StatusOK,
 		Msg:  "ok",
-		Data: model.PenSlice(penList).ToPB2(s.BarnTypeEnumList(), isAll),
+		Data: model.PenSlice(penList).ToPB2(barnTypeList, isAll),
 	}, nil
 }
 
 func (s *StoreEntry) DiseaseTypeOptions(ctx context.Context, isChildren string) (*pasturePb.ConfigOptionsListResponse, error) {
+	userModel, err := s.GetUserModel(ctx)
+	if err != nil {
+		return nil, xerr.WithStack(err)
+	}
 	diseaseTypeList := make([]*model.ConfigDiseaseType, 0)
-	pref := s.DB.Table(new(model.ConfigDiseaseType).TableName()).
-		Where("is_show =? ", pasturePb.IsShow_Ok)
-	if err := pref.Find(&diseaseTypeList).Error; err != nil {
+	if err = s.DB.Table(new(model.ConfigDiseaseType).TableName()).
+		Where("is_show =? ", pasturePb.IsShow_Ok).
+		Find(&diseaseTypeList).Error; err != nil {
 		return nil, xerr.WithStack(err)
 	}
+
 	diseaseList := make([]*model.Disease, 0)
 	if isChildren == model.IsAllYes {
-		if err := s.DB.Table(new(model.Disease).TableName()).
-			Where("is_show =? ", pasturePb.IsShow_Ok).Find(&diseaseList).Error; err != nil {
+		if err = s.DB.Model(new(model.Disease)).
+			Where("pasture_id =? ", userModel.AppPasture.Id).
+			Where("is_show =? ", pasturePb.IsShow_Ok).
+			Find(&diseaseList).Error; err != nil {
 			return nil, xerr.WithStack(err)
 		}
 	}
@@ -72,11 +95,10 @@ func (s *StoreEntry) DiseaseOptions(ctx context.Context) (*pasturePb.ConfigOptio
 	}
 
 	diseaseList := make([]*model.Disease, 0)
-	pref := s.DB.Table(new(model.Disease).TableName()).
+	if err = s.DB.Model(new(model.Disease)).
 		Where("is_show =? ", pasturePb.IsShow_Ok).
-		Where("pasture_id =? ", userModel.AppPasture.Id)
-
-	if err = pref.Find(&diseaseList).Error; err != nil {
+		Where("pasture_id =? ", userModel.AppPasture.Id).
+		Find(&diseaseList).Error; err != nil {
 		return nil, xerr.WithStack(err)
 	}
 	return &pasturePb.ConfigOptionsListResponse{
@@ -93,11 +115,10 @@ func (s *StoreEntry) PrescriptionOptions(ctx context.Context) (*pasturePb.Config
 	}
 
 	prescriptionList := make([]*model.Prescription, 0)
-	pref := s.DB.Table(new(model.Prescription).TableName()).
+	if err = s.DB.Model(new(model.Prescription)).
 		Where("pasture_id = ? ", userModel.AppPasture.Id).
-		Where("is_show = ? ", pasturePb.IsShow_Ok)
-
-	if err = pref.Find(&prescriptionList).Error; err != nil {
+		Where("is_show = ? ", pasturePb.IsShow_Ok).
+		Find(&prescriptionList).Error; err != nil {
 		return nil, xerr.WithStack(err)
 	}
 	return &pasturePb.ConfigOptionsListResponse{

+ 1 - 1
module/backend/indicators.go

@@ -69,7 +69,7 @@ func (s *StoreEntry) IndicatorsComparison(ctx context.Context, req *pasturePb.In
 
 func (s *StoreEntry) GetIndicatorsDetailsMap(ctx context.Context) (map[string]*model.IndicatorsDetails, []string, error) {
 	indicatorsDetails := make(map[string]*model.IndicatorsDetails)
-	indicatorsDetailsList, err := s.FindIndicatorsDetailsList(ctx)
+	indicatorsDetailsList, err := s.FindIndicatorsDetailsList()
 	if err != nil {
 		return nil, nil, xerr.WithStack(err)
 	}

+ 8 - 4
module/backend/neck_ring_warning.go

@@ -50,7 +50,8 @@ func (s *StoreEntry) NeckRingWarningEstrusOrAbortionCowList(ctx context.Context,
 		pref.Where("b.pen_id IN ?", req.PenIds)
 	}
 
-	if err = pref.Order("a.level DESC").
+	if err = pref.Group("a.cow_id").
+		Order("a.level DESC").
 		Count(&count).
 		Limit(int(pagination.PageSize)).
 		Offset(int(pagination.PageOffset)).
@@ -63,9 +64,12 @@ func (s *StoreEntry) NeckRingWarningEstrusOrAbortionCowList(ctx context.Context,
 	cowIds := make([]int64, 0)
 	for _, v := range neckRingEstrusList {
 		cowIds = append(cowIds, v.CowId)
-		lastEventLog := s.GetCowLastEvent(userModel.AppPasture.Id, v.CowId, pasturePb.EventCategory_Breed)
-		if lastEventLog != nil {
-			eventLogMap[v.CowId] = lastEventLog.EventDescription
+	}
+
+	if len(cowIds) > 0 {
+		lastEventLogList := s.GetCowLastEventByCowIds(userModel.AppPasture.Id, cowIds, pasturePb.EventCategory_Breed)
+		for _, log := range lastEventLogList {
+			eventLogMap[log.CowId] = log.EventDescription
 		}
 	}
 

+ 22 - 3
module/backend/sql_more.go

@@ -139,9 +139,11 @@ func (s *StoreEntry) GetEventLogList(
 	return eventLogList, nil
 }
 
-func (s *StoreEntry) FindIndicatorsDetailsList(ctx context.Context) ([]*model.IndicatorsDetails, error) {
+func (s *StoreEntry) FindIndicatorsDetailsList() ([]*model.IndicatorsDetails, error) {
 	list := make([]*model.IndicatorsDetails, 0)
-	if err := s.DB.Model(new(model.IndicatorsDetails)).Find(&list).Error; err != nil {
+	if err := s.DB.Model(new(model.IndicatorsDetails)).
+		Where("is_show = ?", pasturePb.IsShow_Ok).
+		Find(&list).Error; err != nil {
 		return nil, xerr.WithStack(err)
 	}
 	return list, nil
@@ -150,7 +152,8 @@ func (s *StoreEntry) FindIndicatorsDetailsList(ctx context.Context) ([]*model.In
 func (s *StoreEntry) GetCowLastEvent(pastureId, cowId int64, eventCategoryId pasturePb.EventCategory_Kind) *model.EventCowLog {
 	newEventCowLog := &model.EventCowLog{CowId: cowId}
 	pref := s.DB.Table(newEventCowLog.TableName()).
-		Where("cow_id = ?", cowId).Where("pasture_id = ?", pastureId)
+		Where("cow_id = ?", cowId).
+		Where("pasture_id = ?", pastureId)
 	if eventCategoryId > 0 {
 		pref.Where("event_category_kind = ?", eventCategoryId)
 	}
@@ -162,6 +165,22 @@ func (s *StoreEntry) GetCowLastEvent(pastureId, cowId int64, eventCategoryId pas
 	return newEventCowLog
 }
 
+func (s *StoreEntry) GetCowLastEventByCowIds(pastureId int64, cowIds []int64, eventCategoryId pasturePb.EventCategory_Kind) []*model.EventCowLog {
+	newEventCowLogList := make([]*model.EventCowLog, 0)
+	pref := s.DB.Model(new(model.EventCowLog)).
+		Where("cow_id IN ?", cowIds).
+		Where("pasture_id = ?", pastureId)
+	if eventCategoryId > 0 {
+		pref.Where("event_category_kind = ?", eventCategoryId)
+	}
+
+	if err := pref.Order("id desc").
+		Find(&newEventCowLogList); err != nil {
+		return nil
+	}
+	return newEventCowLogList
+}
+
 // IsExistCalvingCalf 根据cowId查询犊牛信息
 func (s *StoreEntry) IsExistCalvingCalf(pastureId, cowId int64) (*model.CalvingCalf, bool) {
 	calvingCalf := &model.CalvingCalf{}

+ 28 - 5
module/backend/system_service.go

@@ -185,18 +185,41 @@ func (s *StoreEntry) SearchSystemUserList(ctx context.Context, req *pasturePb.Se
 
 // DeleteSystemUser 删除系统用户
 func (s *StoreEntry) DeleteSystemUser(ctx context.Context, userId int64) error {
+	userModel, err := s.GetUserModel(ctx)
+	if err != nil {
+		return xerr.WithStack(err)
+	}
 	systemUser := &model.SystemUser{Id: userId}
-	if err := s.DB.Model(new(model.SystemUser)).First(systemUser).Error; err != nil {
+	if err = s.DB.Model(new(model.SystemUser)).First(systemUser).Error; err != nil {
 		if errors.Is(err, gorm.ErrRecordNotFound) {
 			return xerr.Custom("该用户不存在")
 		}
 		return xerr.WithStack(err)
 	}
 
-	if err := s.DB.Model(systemUser).
-		Update("is_delete", operationPb.IsShow_NO).Error; err != nil {
-		return xerr.WithStack(err)
+	pastureIds := systemUser.GetPastureIds()
+	if len(pastureIds) == 1 {
+		if err = s.DB.Model(systemUser).
+			Update("is_delete", operationPb.IsShow_NO).Error; err != nil {
+			return xerr.WithStack(err)
+		}
 	}
+	if len(pastureIds) > 1 {
+		newPastureIds := ""
+		for _, v := range pastureIds {
+			if int64(v) == userModel.AppPasture.Id {
+				continue
+			}
+			newPastureIds += fmt.Sprintf("%d,", v)
+		}
+		if len(newPastureIds) > 0 {
+			if err = s.DB.Model(systemUser).
+				Update("pasture_ids", strings.TrimRight(newPastureIds, ",")).Error; err != nil {
+				return xerr.WithStack(err)
+			}
+		}
+	}
+
 	return nil
 }
 
@@ -299,7 +322,7 @@ func (s *StoreEntry) SystemUserCreateOrUpdate(ctx context.Context, req *pastureP
 			return xerr.Customf("系统中该用户名称已经存在: %s_%s", req.Name, req.Mobile)
 		}
 
-		newSystemUser := model.NewSystemUser(req, deptIds, pastureIds, req.PastureDepthList)
+		newSystemUser := model.NewSystemUser(req, pastureIds, req.PastureDepthList)
 		if err = s.DB.Transaction(func(tx *gorm.DB) error {
 			if err = tx.Model(new(model.SystemUser)).
 				Create(newSystemUser).Error; err != nil {

+ 5 - 2
module/crontab/cow_cron.go

@@ -64,7 +64,9 @@ func (e *Entry) Indicators() error {
 	}()
 	// 获取所有指标
 	indicatorsDetailsList := make([]*model.IndicatorsDetails, 0)
-	if err := e.DB.Model(new(model.IndicatorsDetails)).Find(&indicatorsDetailsList).Error; err != nil {
+	if err := e.DB.Model(new(model.IndicatorsDetails)).
+		Where("is_show = ?", pasturePb.IsShow_Ok).
+		Find(&indicatorsDetailsList).Error; err != nil {
 		return err
 	}
 	// 获取所有牧场
@@ -602,7 +604,8 @@ func (e *Entry) InitEventData(cowList []*model.Cow, systemBasic *model.SystemBas
 		calendarType = pasturePb.CalendarType_Weaning
 		startDay, endDay = nowTime.Format(model.LayoutDate2), nowTime.AddDate(0, 0, model.CalendarTypeEndDaysMap[calendarType]).Format(model.LayoutDate2)
 		eventWeaningDataList := model.NewEventWeaningList(systemBasic.PastureId, cowList, startDay, endDay)
-		if err := e.DB.Model(new(model.EventWeaning)).Create(eventWeaningDataList).Error; err != nil {
+		if err := e.DB.Model(new(model.EventWeaning)).
+			Create(eventWeaningDataList).Error; err != nil {
 			zaplog.Error("crontab", zap.Any("InitEventData", err), zap.Any("eventWeaningDataList", eventWeaningDataList))
 			return
 		}

+ 35 - 3
module/crontab/estrus_warning.go

@@ -77,7 +77,40 @@ func (e *Entry) UpdateNeckRingWarning(pastureId int64) (err error) {
 }
 
 func (e *Entry) UpdateNeckRingWarningIsPeak(pastureId, minId int64) {
-	sqlQuery := e.DB.Table(fmt.Sprintf("%s as a", new(model.NeckActiveHabit).TableName())).
+	cowIds := make([]int64, 0)
+	if err := e.DB.Table(fmt.Sprintf("%s as a", new(model.NeckActiveHabit).TableName())).
+		Joins("JOIN %s as b ON a.cow_id = b.cow_id", new(model.NeckRingEstrusWarning).TableName()).
+		Where("a.id >= ?", minId).
+		Where("b.pasture_id = ?", pastureId).
+		Where("a.active_time > b.date_time").Pluck("DISTINCT b.cow_id", &cowIds).Error; err != nil {
+		zaplog.Error("UpdateNeckRingWarningIsPeak: query cow_ids failed", zap.Any("err", err))
+		return
+	}
+	if len(cowIds) <= 0 {
+		return
+	}
+
+	batchSize := 100 // 每批处理100条,防止一次更新过多数据
+	for i := 0; i < len(cowIds); i += batchSize {
+		end := i + batchSize
+		if end > len(cowIds) {
+			end = len(cowIds)
+		}
+
+		batch := cowIds[i:end]
+		if err := e.DB.Table(new(model.NeckRingEstrusWarning).TableName()).
+			Where("pasture_id = ?", pastureId).
+			Where("cow_id IN (?)", batch).
+			Update("is_peak", pasturePb.IsShow_Ok).Error; err != nil {
+			zaplog.Error("UpdateNeckRingWarningIsPeak: batch update failed",
+				zap.Any("err", err),
+				zap.Any("batch", batch),
+				zap.Int("batch_start", i),
+				zap.Int("batch_end", end))
+			// 继续尝试下一批,而不是直接返回
+		}
+	}
+	/*sqlQuery := e.DB.Table(fmt.Sprintf("%s as a", new(model.NeckActiveHabit).TableName())).
 		Select("1").
 		Where("a.id >= ?", minId).
 		Where("a.cow_id = b.cow_id").
@@ -88,8 +121,7 @@ func (e *Entry) UpdateNeckRingWarningIsPeak(pastureId, minId int64) {
 		Where("EXISTS (?)", sqlQuery).
 		Update("is_peak", pasturePb.IsShow_Ok).Error; err != nil {
 		zaplog.Error("UpdateNeckRingWarningIsPeak", zap.Any("err", err))
-	}
-
+	}*/
 	// UPDATE v_v_hact v JOIN estrusact e ON v.inteaid=e.inteaid SET e.isPeak=1 WHERE v.isPeak=1;
 	if err := e.DB.Raw(`UPDATE neck_ring_estrus_warning v JOIN neck_ring_estrus e ON v.neck_ring_estrus_id = e.id SET e.is_peak = ? WHERE v.is_peak = ?`,
 		pasturePb.IsShow_Ok, pasturePb.IsShow_Ok).Error; err != nil {