Browse Source

system: user update

Yi 1 week ago
parent
commit
ac1865ceea

+ 1 - 1
go.mod

@@ -3,7 +3,7 @@ module kpt-pasture
 go 1.17
 
 require (
-	gitee.com/xuyiping_admin/go_proto v0.0.0-20250521031814-2eb66df61c58
+	gitee.com/xuyiping_admin/go_proto v0.0.0-20250523062919-b893047f10c8
 	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

+ 4 - 0
go.sum

@@ -107,6 +107,10 @@ gitee.com/xuyiping_admin/go_proto v0.0.0-20250521021325-89e4fc71dd39 h1:/7Mllucr
 gitee.com/xuyiping_admin/go_proto v0.0.0-20250521021325-89e4fc71dd39/go.mod h1:BKrFW6YLDectlQcQk3FYKBeXvjEiodAKJ5rq7O/QiPE=
 gitee.com/xuyiping_admin/go_proto v0.0.0-20250521031814-2eb66df61c58 h1:PDG62cMpWaD/V11wty6sjiM4t2/sYFBg9u2S1tiZWbs=
 gitee.com/xuyiping_admin/go_proto v0.0.0-20250521031814-2eb66df61c58/go.mod h1:BKrFW6YLDectlQcQk3FYKBeXvjEiodAKJ5rq7O/QiPE=
+gitee.com/xuyiping_admin/go_proto v0.0.0-20250523054938-90895b21e2e6 h1:g/L5Dr4bkiWijpYC15mUrwBh+VSE400njGEvzgkmwpk=
+gitee.com/xuyiping_admin/go_proto v0.0.0-20250523054938-90895b21e2e6/go.mod h1:BKrFW6YLDectlQcQk3FYKBeXvjEiodAKJ5rq7O/QiPE=
+gitee.com/xuyiping_admin/go_proto v0.0.0-20250523062919-b893047f10c8 h1:j8gu75Sg2xvbC+dZ0UGFQMcNnU8AmBQ41KRgHXOcPik=
+gitee.com/xuyiping_admin/go_proto v0.0.0-20250523062919-b893047f10c8/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=

+ 2 - 1
http/handler/system/user.go

@@ -50,6 +50,7 @@ func UserCreateOrUpdate(c *gin.Context) {
 		valid.Field(&req.Name, valid.Required),
 		valid.Field(&req.DeptId, valid.Required),
 		valid.Field(&req.Mobile, valid.Required),
+		valid.Field(&req.Password, valid.Required),
 	); err != nil {
 		apierr.AbortBadRequest(c, http.StatusBadRequest, err)
 		return
@@ -193,7 +194,7 @@ func UserRoleSave(c *gin.Context) {
 
 	if err := valid.ValidateStruct(&req,
 		valid.Field(&req.UserId, valid.Required),
-		valid.Field(&req.RoleId, valid.Required),
+		valid.Field(&req.RoleIds, valid.Required),
 	); err != nil {
 		apierr.AbortBadRequest(c, http.StatusBadRequest, err)
 		return

+ 1 - 1
http/route/system_api.go

@@ -37,7 +37,7 @@ func SystemAPI(opts ...func(engine *gin.Engine)) func(s *gin.Engine) {
 		systemRoute.POST("/role/createOrUpdate", system.RoleCreateOrUpdate)
 		systemRoute.GET("/role/menu/:id", system.GetRoleMenu)
 		systemRoute.POST("/role/menu/save", system.RoleMenuSave)
-		systemRoute.GET("/role/list", system.GetRoleList)
+		//systemRoute.GET("/role/list", system.GetRoleList)
 
 		// 系统菜单权限
 		systemRoute.POST("/menu/list", system.SearchSystemMenuList)

+ 2 - 0
model/cow.go

@@ -131,6 +131,7 @@ func (c *Cow) EventAbortionUpdate(abortionAt int64, isLact pasturePb.IsShow_Kind
 	c.BreedStatus = pasturePb.BreedStatus_Abort
 	c.AbortionTimes += 1
 	c.AllAbortionTimes += 1
+	c.PregnancyAge = 0
 	if isLact == pasturePb.IsShow_Ok {
 		c.Lact += 1
 	}
@@ -555,6 +556,7 @@ func (c CowSlice) ToPB(
 			EleEarNumber:                v.EleEarNumber,
 			AdmissionPrice:              v.AdmissionPrice,
 			PurposeName:                 purposeName,
+			BatchNumber:                 v.BatchNumber,
 		}
 	}
 	return res

+ 4 - 0
model/event_weaning.go

@@ -22,6 +22,8 @@ type EventWeaning struct {
 	BeforePenId   int32                 `json:"beforePenId"`
 	AfterPenId    int32                 `json:"afterPenId"`
 	Weight        int32                 `json:"weight"`
+	BirthWeight   int32                 `json:"birthWeight"`
+	BirthAt       int64                 `json:"birthAt"`
 	Remarks       string                `json:"remarks"`
 	OperationId   int32                 `json:"operationId"`
 	OperationName string                `json:"operationName"`
@@ -46,6 +48,8 @@ func (e *EventWeaning) EventUpdate(cowInfo *Cow, weaningAt int64, weight int32,
 	e.AfterPenId = afterPenId
 	e.Weight = weight
 	e.DayAge = cowInfo.GetEventDayAge(weaningAt)
+	e.BirthWeight = int32(cowInfo.BirthWeight)
+	e.BirthAt = cowInfo.BirthAt
 }
 
 func NewEventWeaning(pastureId int64, cowInfo *Cow, startDay, endDay string) *EventWeaning {

+ 43 - 36
model/indicators_data.go

@@ -8,48 +8,55 @@ import (
 )
 
 const (
-	AllCow                           = "all_cow"
-	AdultCow                         = "adult_cow"
-	AvgCalvingInterval               = "avg_calving_interval"
-	OutputNumber                     = "output_number"
-	InputNumber                      = "input_number"
-	SalesVolume                      = "sales_volume"
-	CalvingNumber                    = "calving_number"
-	AdultAbortionRate                = "adult_abortion_rate"
-	YouthAbortionRate                = "youth_abortion_rate"
+	AllCow                           = "all_cow"              // 全群牛头数
+	AdultCow                         = "adult_cow"            // 成母牛头数
+	AvgCalvingInterval               = "avg_calving_interval" // 产犊间隔
+	OutputNumber                     = "output_number"        // 出栏量(销售)
+	InputNumber                      = "input_number"         // 入栏量(购入)
+	SalesVolume                      = "sales_volume"         // 销售额
+	CalvingNumber                    = "calving_number"       // 产犊事件总数
+	AdultAbortionRate                = "adult_abortion_rate"  //成母牛流产率(全)
+	YouthAbortionRate                = "youth_abortion_rate"  //青年年流产率(全)
 	AllDieNumber                     = "all_die_number"
 	DiseaseNumber                    = "disease_number"
 	CureNumber                       = "cure_number"
 	OutNumber                        = "out_number"
 	CalfDieNumber                    = "calf_die_number"
 	LactationCow                     = "lactation_cow"
-	DryMilkCow                       = "dry_milk_cow"
-	ReserveCow                       = "reserve_cow"
-	FirstBornSurvivalRate            = "first_born_survival_rate"
-	FirstBornDeathRate               = "first_born_death_rate"
-	MultiparitySurvivalRate          = "multiparity_survival_rate"
-	MultiparityDeathRate             = "multiparity_death_rate"
-	AvgAgeFirstMate                  = "avg_age_first_mate"
-	YouthPregnantRate                = "youth_pregnant_rate"
-	MultiparityPregnantRate          = "multiparity_pregnant_rate"
-	MultipartyFirstCheckRate         = "multiparty_first_check_rate"
-	MultipartySecondCheckRate        = "multiparty_second_check_rate"
-	YouthFirstCheckRate              = "youth_first_check_rate"
-	YouthSecondCheckRate             = "youth_second_check_rate"
-	ForbiddenCowNumber               = "forbidden_cow_number"
-	AvgRegistrationDays              = "avg_registration_days"
-	AvgPregnancyDays                 = "avg_pregnancy_days"
-	AvgGestationalAge                = "avg_gestational_age"
-	Month17UnPregnancyRate           = "month17_un_pregnancy_rate"
-	Month20UnPregnancyRate           = "month20_un_pregnancy_rate"
-	Multiparty150DaysUnPregnancyRate = "multiparty_150_days_un_pregnancy_rate"
-	MultipartyAbortionNumber         = "multiparty_abortion_number"
-	MultipartyAbortionRate           = "multiparty_abortion_rate"
-	MultipartyPregnancyNumber        = "multiparty_pregnancy_number"
-	MultipartyOutNumber              = "multiparty_out_number"
-	MultipartyDieNumber              = "multiparty_die_number"
-	Calving60DieRate                 = "calving60_die_rate"
-	Calving60OutRate                 = "calving60_out_rate"
+	DryMilkCow                       = "dry_milk_cow"                          // 干奶牛头数
+	ReserveCow                       = "reserve_cow"                           // 后备牛头数
+	FirstBornSurvivalRate            = "first_born_survival_rate"              // 头胎牛接产成活率
+	FirstBornDeathRate               = "first_born_death_rate"                 // 头胎牛接产死亡率
+	MultiparitySurvivalRate          = "multiparity_survival_rate"             // 成母牛接产成活率
+	MultiparityDeathRate             = "multiparity_death_rate"                // 成母牛接产死亡率
+	AvgAgeFirstMate                  = "avg_age_first_mate"                    // 平均首配日龄
+	YouthPregnantRate                = "youth_pregnant_rate"                   // 后备牛怀孕率
+	MultiparityPregnantRate          = "multiparity_pregnant_rate"             // 成母牛怀孕率
+	MultipartyFirstCheckRate         = "multiparty_first_check_rate"           //成母牛初检有胎率
+	MultipartySecondCheckRate        = "multiparty_second_check_rate"          // 成母牛复检有胎率
+	YouthFirstCheckRate              = "youth_first_check_rate"                // 后备牛初检有胎率
+	YouthSecondCheckRate             = "youth_second_check_rate"               // 后备牛复检有胎率
+	ForbiddenCowNumber               = "forbidden_cow_number"                  // 禁配牛总数
+	AvgRegistrationDays              = "avg_registration_days"                 // 平均配准天数
+	AvgPregnancyDays                 = "avg_pregnancy_days"                    // 平均怀孕天数
+	AvgGestationalAge                = "avg_gestational_age"                   // 平均受孕日龄
+	Month17UnPregnancyRate           = "month17_un_pregnancy_rate"             // 17月未孕比例
+	Month20UnPregnancyRate           = "month20_un_pregnancy_rate"             // 20月未孕比例
+	Multiparty150DaysUnPregnancyRate = "multiparty_150_days_un_pregnancy_rate" // 成母牛150天未孕比例
+	MultipartyAbortionNumber         = "multiparty_abortion_number"            // 成母牛流产头数
+	MultipartyAbortionRate           = "multiparty_abortion_rate"              // 成母牛流产率(全)
+	MultipartyPregnancyNumber        = "multiparty_pregnancy_number"           // 成母牛怀孕头数
+	MultipartyOutNumber              = "multiparty_out_number"                 // 成母牛淘汰头数
+	MultipartyDieNumber              = "multiparty_die_number"                 // 成母牛死亡头数
+	Calving60DieRate                 = "calving60_die_rate"                    // 产后60天死亡率
+	Calving60OutRate                 = "calving60_out_rate"                    // 产后60天淘汰率
+	AvgDepartureWeight               = "avg_departure_weight"                  // 出栏均重
+	AvgSlaughterCycle                = "avg_slaughter_cycle"                   // 平均出栏周期
+	SurvivalLiveRate                 = "survival_live_rate"                    // 接产成活率
+	StillbirthRate                   = "stillbirth_rate"                       // 死胎率
+	FemaleCalfRate                   = "female_calf_rate"                      // 母犊率
+	WeaningDailyWeight               = "weaning_daily_weight"                  // 断奶日增重
+	Day60DieRate                     = "day60_die_rate"                        // 60日龄死亡率
 )
 
 type IndicatorsData struct {

+ 1 - 0
model/system_user.go

@@ -10,6 +10,7 @@ import (
 
 type SystemUser struct {
 	Id              int64                  `json:"id"`
+	PastureId       int64                  `json:"pastureId"`
 	Name            string                 `json:"name"`
 	NickName        string                 `json:"nickName"`
 	Mobile          string                 `json:"mobile"`

+ 1 - 1
module/backend/config_data_extend.go

@@ -390,7 +390,7 @@ func (s *StoreEntry) GenerateBatchNumber(ctx context.Context) (*model.GenerateBa
 		return nil, xerr.WithStack(err)
 	}
 
-	data := fmt.Sprintf("%s%d%s", time.Now().Local().Format(model.LayoutDate2), userModel.AppPasture.Id, util.GenerateRandomNumberString(8))
+	data := fmt.Sprintf("%s%d%s", time.Now().Local().Format(model.LayoutDate), userModel.AppPasture.Id, util.GenerateRandomNumber(10))
 	return &model.GenerateBatchNumberResponse{
 		Code: http.StatusOK,
 		Msg:  "ok",

+ 1 - 1
module/backend/event_breed_more.go

@@ -541,7 +541,7 @@ func (s *StoreEntry) WeaningBatch(ctx context.Context, req *pasturePb.EventWeani
 				item.Remarks, item.PenId, operation, userModel.SystemUser,
 			)
 			if err = tx.Model(new(model.EventWeaning)).
-				Select("status", "reality_day", "operation_id", "operation_name", "message_id", "message_name", "remarks", "after_pen_id").
+				Select("status", "reality_day", "operation_id", "operation_name", "message_id", "message_name", "remarks", "after_pen_id", "birth_weight", "birth_at").
 				Where("id = ?", eventWeaning.Id).
 				Updates(eventWeaning).Error; err != nil {
 				return xerr.WithStack(err)

+ 10 - 3
module/backend/sql.go

@@ -58,13 +58,20 @@ func (s *StoreEntry) GetCurrentSystemUser(ctx context.Context) (*model.SystemUse
 	systemUser := &model.SystemUser{Name: userName}
 	if err = s.DB.Model(new(model.SystemUser)).
 		Where("name = ?", userName).
-		Where("is_show = ? and is_delete = ?", pasturePb.IsShow_Ok, pasturePb.IsShow_Ok).
 		First(systemUser).Error; err != nil {
+		zaplog.Error("GetCurrentSystemUser", zap.Any("err", err), zap.Any("userName", userName))
+
 		if errors.Is(err, gorm.ErrRecordNotFound) {
 			return nil, xerr.Custom("当前登录用户数据不存在")
+		} else {
+			return nil, xerr.Custom("用户登录信息有误,请退出重新登录")
 		}
-		zaplog.Error("GetCurrentSystemUser", zap.Any("err", err), zap.Any("userName", userName))
-		return nil, xerr.Custom("用户登录信息有误,请退出重新登录")
+	}
+	if systemUser.IsDelete != pasturePb.IsShow_Ok {
+		return nil, xerr.Custom("当前用户数据已经删除")
+	}
+	if systemUser.IsShow != pasturePb.IsShow_Ok {
+		return nil, xerr.Custom("当前用户已禁用")
 	}
 	return systemUser, nil
 }

+ 19 - 5
module/backend/system_service.go

@@ -256,7 +256,11 @@ func (s *StoreEntry) SystemUserRole(ctx context.Context, userId int64) (*pasture
 	return &pasturePb.SystemUserRoleResponse{
 		Code: http.StatusOK,
 		Msg:  "ok",
-		Data: int32(systemUser.RoleId),
+		Data: &pasturePb.UserRoleData{
+			RoleIds: []int32{
+				int32(systemUser.RoleId),
+			},
+		},
 	}, nil
 }
 
@@ -269,14 +273,16 @@ func (s *StoreEntry) SystemUserRoleSave(ctx context.Context, req *pasturePb.Syst
 		return xerr.WithStack(err)
 	}
 
-	systemRole := &model.SystemRole{Id: int64(req.RoleId)}
+	// todo 用户觉得绑定单个还是多少q
+	systemRole := &model.SystemRole{Id: int64(req.RoleIds[0])}
 	if err := s.DB.First(systemRole).Error; err != nil {
 		if errors.Is(err, gorm.ErrRecordNotFound) {
 			return xerr.Custom("该角色不存在")
 		}
 		return xerr.WithStack(err)
 	}
-	if err := s.DB.Model(systemUser).Update("role_id", systemRole.Id).Error; err != nil {
+	if err := s.DB.Model(systemUser).
+		Update("role_id", systemRole.Id).Error; err != nil {
 		return xerr.WithStack(err)
 	}
 	return nil
@@ -606,7 +612,10 @@ func (s *StoreEntry) SearchSystemDeptList(ctx context.Context, req *pasturePb.Se
 		pref.Where("is_show = ?", req.IsShow)
 	}
 
-	if err := pref.Order("sort desc").Count(&count).Limit(int(pagination.PageSize)).Offset(int(pagination.PageOffset)).
+	if err := pref.Order("sort desc").
+		Count(&count).
+		Limit(int(pagination.PageSize)).
+		Offset(int(pagination.PageOffset)).
 		Find(&deptList).Error; err != nil {
 		return nil, xerr.WithStack(err)
 	}
@@ -616,7 +625,12 @@ func (s *StoreEntry) SearchSystemDeptList(ctx context.Context, req *pasturePb.Se
 	return &pasturePb.SearchDeptResponse{
 		Code: http.StatusOK,
 		Msg:  "ok",
-		Data: model.SystemDeptSlice(deptList).ToPB(systemUserList),
+		Data: &pasturePb.DeptData{
+			List:     model.SystemDeptSlice(deptList).ToPB(systemUserList),
+			Total:    int32(count),
+			PageSize: pagination.PageSize,
+			Page:     pagination.Page,
+		},
 	}, nil
 }
 

+ 14 - 0
module/crontab/cow_cron.go

@@ -153,6 +153,20 @@ func (e *Entry) Indicators() error {
 			pastureIndicatorList = e.CalvingDieRate(pastureList, startTime, endTime, 60)
 		case model.Calving60OutRate:
 			pastureIndicatorList = e.CalvingOutRate(pastureList, startTime, endTime, 60)
+		case model.AvgDepartureWeight:
+			pastureIndicatorList = e.AvgDepartureWeight(pastureList, startTime, endTime)
+		case model.AvgSlaughterCycle:
+			pastureIndicatorList = e.AvgSlaughterCycle(pastureList, startTime, endTime)
+		case model.SurvivalLiveRate:
+			pastureIndicatorList = e.SurvivalLiveRate(pastureList, startTime, endTime, 262)
+		case model.StillbirthRate:
+			pastureIndicatorList = e.StillbirthRate(pastureList, startTime, endTime)
+		case model.FemaleCalfRate:
+			pastureIndicatorList = e.FemaleCalfRate(pastureList, startTime, endTime)
+		case model.WeaningDailyWeight:
+			pastureIndicatorList = e.WeaningDailyWeight(pastureList, startTime, endTime)
+		case model.Day60DieRate:
+			pastureIndicatorList = e.DayDieRate(pastureList, startTime, endTime, 60)
 
 		}
 

+ 174 - 0
module/crontab/cow_indicators_calf.go

@@ -0,0 +1,174 @@
+package crontab
+
+import (
+	"fmt"
+	"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"
+)
+
+// SurvivalLiveRate 接产成活率
+func (e *Entry) SurvivalLiveRate(pastureList []*model.AppPastureList, startAt, endAt int64, pregnancyAge int32) map[int64]string {
+	res := make(map[int64]string)
+	for _, pasture := range pastureList {
+		eventCalving := make([]*model.EventCalving, 0)
+		if err := e.DB.Model(new(model.EventCalving)).
+			Where("pasture_id = ?", pasture.Id).
+			Where("status = ?", pasturePb.IsShow_Ok).
+			Where("reality_day BETWEEN ? AND ?", startAt, endAt).
+			Find(&eventCalving).Error; err != nil {
+			zaplog.Error("SurvivalLiveRate", zap.Any("pasture_id", pasture.Id), zap.Any("err", err))
+		}
+		allEventCalving := int32(len(eventCalving))
+		calvingIds := make([]int64, 0)
+		for _, v := range eventCalving {
+			if v.CalvingLevel == pasturePb.CalvingLevel_Natural_Childbirth && v.PregnancyAge > pregnancyAge {
+				calvingIds = append(calvingIds, v.Id)
+			}
+		}
+
+		var count int64
+		if err := e.DB.Model(new(model.CalvingCalf)).
+			Where("calving_id IN ?", calvingIds).
+			Where("is_live = ?", pasturePb.IsShow_Ok).
+			Count(&count).Error; err != nil {
+			zaplog.Error("SurvivalLiveRate", zap.Any("pasture_id", pasture.Id), zap.Any("err", err))
+		}
+
+		if count > 0 && allEventCalving > 0 {
+			res[pasture.Id] = fmt.Sprintf("%.2f", float64(count)/float64(allEventCalving))
+		} else {
+			res[pasture.Id] = "0"
+		}
+	}
+	return res
+}
+
+// StillbirthRate 死胎率
+func (e *Entry) StillbirthRate(pastureList []*model.AppPastureList, startAt, endAt int64) map[int64]string {
+	res := make(map[int64]string)
+	for _, pasture := range pastureList {
+		eventCalving := make([]*model.EventCalving, 0)
+		if err := e.DB.Model(new(model.EventCalving)).
+			Where("pasture_id = ?", pasture.Id).
+			Where("status = ?", pasturePb.IsShow_Ok).
+			Where("reality_day BETWEEN ? AND ?", startAt, endAt).
+			Find(&eventCalving).Error; err != nil {
+			zaplog.Error("SurvivalLiveRate", zap.Any("pasture_id", pasture.Id), zap.Any("err", err))
+		}
+		allEventCalving := int32(len(eventCalving))
+		calvingIds := make([]int64, 0)
+		for _, v := range eventCalving {
+			calvingIds = append(calvingIds, v.Id)
+		}
+
+		var count int64
+		if err := e.DB.Model(new(model.CalvingCalf)).
+			Where("calving_id IN ?", calvingIds).
+			Where("is_live = ?", pasturePb.IsShow_No).
+			Count(&count).Error; err != nil {
+			zaplog.Error("SurvivalLiveRate", zap.Any("pasture_id", pasture.Id), zap.Any("err", err))
+		}
+
+		if count > 0 && allEventCalving > 0 {
+			res[pasture.Id] = fmt.Sprintf("%.2f", float64(count)/float64(allEventCalving))
+		} else {
+			res[pasture.Id] = "0"
+		}
+	}
+	return res
+}
+
+// FemaleCalfRate 母犊率
+func (e *Entry) FemaleCalfRate(pastureList []*model.AppPastureList, startAt, endAt int64) map[int64]string {
+	res := make(map[int64]string)
+	for _, pasture := range pastureList {
+		eventCalving := make([]*model.EventCalving, 0)
+		if err := e.DB.Model(new(model.EventCalving)).
+			Where("pasture_id = ?", pasture.Id).
+			Where("status = ?", pasturePb.IsShow_Ok).
+			Where("reality_day BETWEEN ? AND ?", startAt, endAt).
+			Find(&eventCalving).Error; err != nil {
+			zaplog.Error("SurvivalLiveRate", zap.Any("pasture_id", pasture.Id), zap.Any("err", err))
+		}
+		allEventCalving := int32(len(eventCalving))
+		calvingIds := make([]int64, 0)
+		for _, v := range eventCalving {
+			calvingIds = append(calvingIds, v.Id)
+		}
+
+		var count int64
+		if err := e.DB.Model(new(model.CalvingCalf)).
+			Where("calving_id IN ?", calvingIds).
+			Where("sex = ?", pasturePb.Genders_Female).
+			Count(&count).Error; err != nil {
+			zaplog.Error("SurvivalLiveRate", zap.Any("pasture_id", pasture.Id), zap.Any("err", err))
+		}
+
+		if count > 0 && allEventCalving > 0 {
+			res[pasture.Id] = fmt.Sprintf("%.2f", float64(count)/float64(allEventCalving))
+		} else {
+			res[pasture.Id] = "0"
+		}
+	}
+	return res
+}
+
+// WeaningDailyWeight 犊牛断奶日增重
+func (e *Entry) WeaningDailyWeight(pastureList []*model.AppPastureList, startAt, endAt int64) map[int64]string {
+	res := make(map[int64]string)
+	for _, pasture := range pastureList {
+		eventWeaningList := make([]*model.EventWeaning, 0)
+		if err := e.DB.Model(new(model.EventWeaning)).
+			Where("pasture_id = ?", pasture.Id).
+			Where("status = ?", pasturePb.IsShow_Ok).
+			Where("reality_day BETWEEN ? AND ?", startAt, endAt).
+			Find(&eventWeaningList).Error; err != nil {
+			zaplog.Error("SurvivalLiveRate", zap.Any("pasture_id", pasture.Id), zap.Any("err", err))
+		}
+		allData := float64(0)
+		count := float64(len(eventWeaningList))
+		for _, v := range eventWeaningList {
+			allData += float64(v.Weight-v.BirthWeight) / 1000 / float64(v.DayAge)
+		}
+
+		if allData > 0 && count > 0 {
+			res[pasture.Id] = fmt.Sprintf("%.2f", allData/count)
+		} else {
+			res[pasture.Id] = "0"
+		}
+	}
+	return res
+}
+
+// DayDieRate 日龄死亡率
+func (e *Entry) DayDieRate(pastureList []*model.AppPastureList, startAt, endAt int64, dayAge int32) map[int64]string {
+	res := make(map[int64]string)
+	for _, pasture := range pastureList {
+		eventDeathList := make([]*model.EventDeath, 0)
+		if err := e.DB.Model(new(model.EventDeath)).
+			Where("pasture_id = ?", pasture.Id).
+			Where("death_at BETWEEN ? AND ?", startAt, endAt).
+			Find(&eventDeathList).Error; err != nil {
+			zaplog.Error("Calving60DieRate", zap.Any("pastureId", pasture.Id), zap.Any("err", err))
+		}
+
+		count := float64(len(eventDeathList))
+		target := float64(0)
+		for _, v := range eventDeathList {
+			if v.DayAge <= dayAge {
+				target++
+			}
+		}
+
+		if count > 0 && target > 0 {
+			res[pasture.Id] = fmt.Sprintf("%.2f", target/count)
+		} else {
+			res[pasture.Id] = "0"
+		}
+
+	}
+	return res
+}

+ 69 - 0
module/crontab/cow_indicators_health.go

@@ -267,3 +267,72 @@ func (e *Entry) CalvingOutRate(pastureList []*model.AppPastureList, startTime, e
 	}
 	return res
 }
+
+// AvgDepartureWeight 出栏均重
+func (e *Entry) AvgDepartureWeight(pastureList []*model.AppPastureList, startTime, endTime int64) map[int64]string {
+	res := make(map[int64]string)
+	for _, pasture := range pastureList {
+		eventSaleList := make([]*model.EventSale, 0)
+		if err := e.DB.Model(new(model.EventSale)).
+			Where("pasture_id = ?", pasture.Id).
+			Where("sale_at BETWEEN ? AND ?", startTime, endTime).
+			Find(&eventSaleList).Error; err != nil {
+			zaplog.Error("AvgDepartureWeight", zap.Any("pasture_id", pasture.Id), zap.Any("err", err))
+		}
+
+		sumWeight := int32(0)
+		sumCount := int32(0)
+		for _, v := range eventSaleList {
+			sumCount += v.SaleCowCount
+			sumWeight += v.SaleAllWeight
+		}
+
+		if sumCount > 0 {
+			res[pasture.Id] = fmt.Sprintf("%f", float32(math.Round(float64(sumCount)/float64(sumWeight/1000)*100)/100))
+		} else {
+			res[pasture.Id] = "0"
+		}
+	}
+	return res
+}
+
+// AvgSlaughterCycle 出栏周期
+func (e *Entry) AvgSlaughterCycle(pastureList []*model.AppPastureList, startTime, endTime int64) map[int64]string {
+	res := make(map[int64]string)
+	for _, pasture := range pastureList {
+		eventSaleList := make([]*model.EventSale, 0)
+		if err := e.DB.Model(new(model.EventSale)).
+			Where("pasture_id = ?", pasture.Id).
+			Where("sale_at BETWEEN ? AND ?", startTime, endTime).
+			Find(&eventSaleList).Error; err != nil {
+			zaplog.Error("AvgDepartureWeight", zap.Any("pasture_id", pasture.Id), zap.Any("err", err))
+		}
+
+		saleIds := make([]int64, len(eventSaleList))
+		sumCount := int32(0)
+		sumAdmissionAge := int32(0)
+		for i, v := range eventSaleList {
+			sumCount += v.SaleCowCount
+			saleIds[i] = v.Id
+		}
+
+		if len(saleIds) > 0 {
+			eventSaleCowList := make([]*model.EventSaleCow, 0)
+			if err := e.DB.Model(new(model.EventSaleCow)).
+				Where("sale_id IN ?", saleIds).
+				Find(&eventSaleCowList).Error; err != nil {
+				zaplog.Error("AvgDepartureWeight", zap.Any("pasture_id", pasture.Id), zap.Any("err", err))
+			}
+			for _, c := range eventSaleCowList {
+				sumAdmissionAge += c.AdmissionAge
+			}
+		}
+
+		if sumCount > 0 {
+			res[pasture.Id] = fmt.Sprintf("%f", float32(math.Round(float64(sumCount)/float64(sumAdmissionAge)*100)/100))
+		} else {
+			res[pasture.Id] = "0"
+		}
+	}
+	return res
+}

+ 5 - 1
module/crontab/pen_behavior.go

@@ -33,7 +33,11 @@ func (e *Entry) UpdatePenBehavior() error {
 
 // PenBehaviorEnter 栏舍行为曲线 对数据进行查缺补漏
 func (e *Entry) PenBehaviorEnter(pastureId int64, days int) {
-	minHeatDate := time.Now().Local().AddDate(0, 0, -days).Format(model.LayoutDate2)
+	nowTime := time.Now().Local()
+	if nowTime.Hour() == 1 || nowTime.Hour() == 2 {
+		days = 1
+	}
+	minHeatDate := nowTime.AddDate(0, 0, -days).Format(model.LayoutDate2)
 	e.PenBehavior(pastureId, minHeatDate)
 	e.UpdatePenBehaviorWeekData(pastureId, minHeatDate)
 	newDays := days - 1

+ 19 - 0
util/util.go

@@ -18,6 +18,7 @@ const (
 	LetterIdxBits = 6                    // 6 bits to represent a letter index
 	LetterIdxMask = 1<<LetterIdxBits - 1 // All 1-bits, as many as letterIdxBits
 	LetterIdxMax  = 63 / LetterIdxBits   // # of letter indices fitting in 63 bits
+	NumberVBytes  = "0123456789"
 )
 
 var (
@@ -98,6 +99,24 @@ func GenerateRandomNumberString(n int) string {
 	return string(result)
 }
 
+func GenerateRandomNumber(n int) string {
+	result := make([]byte, n)
+	// A rand.Int63() generates 63 random bits, enough for letterIdxMax characters!
+	rand.Seed(time.Now().Local().UnixNano())
+	for i, cache, remain := n-1, rand.Int63(), LetterIdxMax; i >= 0; {
+		if remain == 0 {
+			cache, remain = rand.Int63(), LetterIdxMax
+		}
+		if idx := int(cache & LetterIdxMask); idx < len(NumberVBytes) {
+			result[i] = NumberVBytes[idx]
+			i--
+		}
+		cache >>= LetterIdxBits
+		remain--
+	}
+	return string(result)
+}
+
 // TimeParseLocalUnix 获取当天零点的时间戳
 // eg 2023-02-22 => 1676995200
 func TimeParseLocalUnix(DayTime string) int64 {

+ 1 - 1
util/util_test.go

@@ -539,7 +539,7 @@ func Test_demo(t *testing.T) {
 		if frameId%10 == 8 {
 			continue
 		}
-		fmt.Println("x", frameId)
+		fmt.Println(GenerateRandomNumber(int(frameId)))
 	}
 
 }

+ 1 - 0
util/util_test1.go

@@ -0,0 +1 @@
+package util