Yi пре 2 дана
родитељ
комит
56456004b0
8 измењених фајлова са 74 додато и 25 уклоњено
  1. 1 1
      README.md
  2. 1 1
      go.mod
  3. 4 10
      go.sum
  4. 8 3
      model/cow.go
  5. 22 2
      model/event_sale_cow.go
  6. 4 4
      module/backend/analysis_other.go
  7. 33 3
      module/backend/indicators.go
  8. 1 1
      module/backend/neck_ring_warning.go

+ 1 - 1
README.md

@@ -4,7 +4,7 @@ kpt-pasture- 科湃腾牧场管理系统
 
 ## Requirements
 
-- Go >= 1.17
+- Go >= 1.19
 - MySQL >= 5.7
 - Docker CE >= 19.03
 - Docker compose

+ 1 - 1
go.mod

@@ -3,7 +3,7 @@ module kpt-pasture
 go 1.17
 
 require (
-	gitee.com/xuyiping_admin/go_proto v0.0.0-20250624035747-6854c41ebc86
+	gitee.com/xuyiping_admin/go_proto v0.0.0-20250624095218-69693701e5c3
 	gitee.com/xuyiping_admin/pkg v0.0.0-20250613101634-36c36a2d27d0
 	github.com/dgrijalva/jwt-go v3.2.0+incompatible
 	github.com/eclipse/paho.mqtt.golang v1.4.3

+ 4 - 10
go.sum

@@ -36,18 +36,12 @@ cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RX
 cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0=
 cloud.google.com/go/storage v1.14.0/go.mod h1:GrKmX003DSIwi9o29oFT7YDnHYwZoctc3fOKtUw0Xmo=
 dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
-gitee.com/xuyiping_admin/go_proto v0.0.0-20250616080546-3ebf4d3f0874 h1:jD/wa9PorrqH0TDiasHboBdLgmlXw1oFOc0Fly7Fw/s=
-gitee.com/xuyiping_admin/go_proto v0.0.0-20250616080546-3ebf4d3f0874/go.mod h1:BKrFW6YLDectlQcQk3FYKBeXvjEiodAKJ5rq7O/QiPE=
-gitee.com/xuyiping_admin/go_proto v0.0.0-20250623070841-590ca50f5b8d h1:wCiJ6yav1+Q182ntip8yXxxzGhqW+KQStlJw1uRfZZY=
-gitee.com/xuyiping_admin/go_proto v0.0.0-20250623070841-590ca50f5b8d/go.mod h1:BKrFW6YLDectlQcQk3FYKBeXvjEiodAKJ5rq7O/QiPE=
-gitee.com/xuyiping_admin/go_proto v0.0.0-20250623073137-994f2b6ab389 h1:832/HoapM95MaKgjLpVRALQDnoDzw2XE8hy14Pe6kbo=
-gitee.com/xuyiping_admin/go_proto v0.0.0-20250623073137-994f2b6ab389/go.mod h1:BKrFW6YLDectlQcQk3FYKBeXvjEiodAKJ5rq7O/QiPE=
-gitee.com/xuyiping_admin/go_proto v0.0.0-20250624020830-f96f63c10ff8 h1:gNUZXJ/EnJcB0CLWybAkuNTw/4RAXephOJFsCJitpkw=
-gitee.com/xuyiping_admin/go_proto v0.0.0-20250624020830-f96f63c10ff8/go.mod h1:BKrFW6YLDectlQcQk3FYKBeXvjEiodAKJ5rq7O/QiPE=
-gitee.com/xuyiping_admin/go_proto v0.0.0-20250624025254-1e386eee1969 h1:eI1nwlLaac8pckZBPIhTdQC3EsBpVNgK3hVvb6gkJOg=
-gitee.com/xuyiping_admin/go_proto v0.0.0-20250624025254-1e386eee1969/go.mod h1:BKrFW6YLDectlQcQk3FYKBeXvjEiodAKJ5rq7O/QiPE=
 gitee.com/xuyiping_admin/go_proto v0.0.0-20250624035747-6854c41ebc86 h1:UblLdMMaG8umGXUEDj23Uv4zO5ireKh4tm5FSDqTiVo=
 gitee.com/xuyiping_admin/go_proto v0.0.0-20250624035747-6854c41ebc86/go.mod h1:BKrFW6YLDectlQcQk3FYKBeXvjEiodAKJ5rq7O/QiPE=
+gitee.com/xuyiping_admin/go_proto v0.0.0-20250624083202-b4e70fad3c6f h1:KfgRw6vaWKPJX3w6lOOUSlnJDLelE24YAVWBvsu9UrI=
+gitee.com/xuyiping_admin/go_proto v0.0.0-20250624083202-b4e70fad3c6f/go.mod h1:BKrFW6YLDectlQcQk3FYKBeXvjEiodAKJ5rq7O/QiPE=
+gitee.com/xuyiping_admin/go_proto v0.0.0-20250624095218-69693701e5c3 h1:vc+2kAaoBLBnA6oY1yyVVlhK2xc5ea6IH9F5+jbz62A=
+gitee.com/xuyiping_admin/go_proto v0.0.0-20250624095218-69693701e5c3/go.mod h1:BKrFW6YLDectlQcQk3FYKBeXvjEiodAKJ5rq7O/QiPE=
 gitee.com/xuyiping_admin/pkg v0.0.0-20250613101634-36c36a2d27d0 h1:ZCOqEAnGm6+DTAhACigzWKbwMKtleb8/7OzP2xfHG7g=
 gitee.com/xuyiping_admin/pkg v0.0.0-20250613101634-36c36a2d27d0/go.mod h1:8tF25X6pE9WkFCczlNAC0K2mrjwKvhhp02I7o0HtDxY=
 github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=

+ 8 - 3
model/cow.go

@@ -828,13 +828,16 @@ func (c CowSlice) LongTermInfertilityToPB(breedStatusMap map[pasturePb.BreedStat
 func (c CowSlice) CanSaleToPB(cowKindMap map[pasturePb.CowKind_Kind]string) []*pasturePb.CanSalesReport {
 	res := make([]*pasturePb.CanSalesReport, len(c))
 	for i, v := range c {
-		cowKindName, lastWeightAtFormat := "", ""
+		cowKindName, lastWeightAtFormat, admissionAtFormat := "", "", ""
 		if name, ok := cowKindMap[v.CowKind]; ok {
 			cowKindName = name
 		}
 		if v.LastWeightAt > 0 {
 			lastWeightAtFormat = time.Unix(v.LastWeightAt, 0).Local().Format(LayoutDate2)
 		}
+		if v.AdmissionAt > 0 {
+			admissionAtFormat = time.Unix(v.AdmissionAt, 0).Local().Format(LayoutDate2)
+		}
 		res[i] = &pasturePb.CanSalesReport{
 			CowId:              int32(v.Id),
 			EarNumber:          v.EarNumber,
@@ -851,6 +854,8 @@ func (c CowSlice) CanSaleToPB(cowKindMap map[pasturePb.CowKind_Kind]string) []*p
 			AllMedicalCharge:   0,
 			OtherCost:          0,
 			ProfitAndLoss:      0,
+			AdmissionAtFormat:  admissionAtFormat,
+			DayAvgWeight:       0,
 		}
 	}
 	return res
@@ -875,6 +880,6 @@ type CowBehaviorCurveData struct {
 	RuminaChange     []int32                                 `json:"ruminaChange"`     // 反刍变化
 	LowActivity      int32                                   `json:"lowActivity"`      // 低活动量参数
 	MiddleActivity   int32                                   `json:"middleActivity"`   // 中活动量行数
-	IQR1             []int32                                 `json:"IQR1"`
-	IQR3             []int32                                 `json:"IQR3"`
+	IQR1             []int32                                 `json:"IQR1"`             // 1IQR
+	IQR3             []int32                                 `json:"IQR3"`             // 3IQR
 }

+ 22 - 2
model/event_sale_cow.go

@@ -1,6 +1,7 @@
 package model
 
 import (
+	"fmt"
 	"time"
 
 	pasturePb "gitee.com/xuyiping_admin/go_proto/proto/go/backend/cow"
@@ -62,7 +63,12 @@ func NewEventSaleCowList(pastureId int64, eventSale *EventSale, cowList []*Cow)
 
 type EventSaleCowSlice []*EventSaleCow
 
-func (e EventSaleCowSlice) ToPB(eventSaleMap map[int64]*EventSale, cowKindMap map[pasturePb.CowKind_Kind]string) []*pasturePb.AlreadySalesReport {
+func (e EventSaleCowSlice) ToPB(
+	eventSaleMap map[int64]*EventSale,
+	cowKindMap map[pasturePb.CowKind_Kind]string,
+	cowMap map[int64]*Cow,
+	sourceMap map[pasturePb.CowSource_Kind]string,
+) []*pasturePb.AlreadySalesReport {
 	res := make([]*pasturePb.AlreadySalesReport, 0)
 	for _, v := range e {
 		cowKindName, saleAtFormat, dealerName := "", "", ""
@@ -79,6 +85,19 @@ func (e EventSaleCowSlice) ToPB(eventSaleMap map[int64]*EventSale, cowKindMap ma
 			dealerName = eventSale.DealerName
 		}
 
+		feedCycle, cowSourceName := "", ""
+		if cowInfo, ok := cowMap[v.CowId]; ok {
+			admissionAtFormat := ""
+			if cowInfo.AdmissionAt > 0 {
+				feedCycle = time.Unix(cowInfo.AdmissionAt, 0).Local().Format(LayoutDate2)
+			}
+			feedCycle = fmt.Sprintf("%s-%s", admissionAtFormat, saleAtFormat)
+
+			if name, ok := sourceMap[cowInfo.SourceKind]; ok {
+				cowSourceName = name
+			}
+		}
+
 		res = append(res, &pasturePb.AlreadySalesReport{
 			CowId:            int32(v.CowId),
 			EarNumber:        v.EarNumber,
@@ -91,7 +110,7 @@ func (e EventSaleCowSlice) ToPB(eventSaleMap map[int64]*EventSale, cowKindMap ma
 			SaleAvgWeight:    saleAvgWeight,
 			SaleAllWeight:    saleAllWeight,
 			AdmissionAge:     v.AdmissionAge,
-			CowSourceName:    "",
+			CowSourceName:    cowSourceName,
 			EnterWeight:      0,
 			EnterPrice:       0,
 			GrowthWeight:     0,
@@ -101,6 +120,7 @@ func (e EventSaleCowSlice) ToPB(eventSaleMap map[int64]*EventSale, cowKindMap ma
 			OtherCost:        0,
 			DealerName:       dealerName,
 			ProfitAndLoss:    0,
+			FeedCycle:        feedCycle,
 		})
 	}
 	return res

+ 4 - 4
module/backend/analysis_other.go

@@ -306,13 +306,13 @@ func (s *StoreEntry) SaleCowReport(ctx context.Context, req *pasturePb.SaleCowRe
 		Where("sale_at BETWEEN ? AND ?", startDayTimeUnix, endDayTimeUnix).
 		Where("pasture_id = ?", userModel.AppPasture.Id)
 	if req.AnalysisMethod == pasturePb.SaleCowAnalysisMethod_Months {
-		pref.Select(`ROUND(SUM(sale_all_amount) /100,2) AS sale_all_amount,SUM(sale_cow_count) AS sale_all_count,
-		SUM(sale_all_weight) AS sale_all_weight,DATE_FORMAT(FROM_UNIXTIME(sale_at), '%Y-%m') AS statistic_method`)
+		pref.Select(`ROUND(SUM(sale_all_amount)/100,2) AS sale_all_amount,SUM(sale_cow_count) AS sale_all_count,
+		ROUND(SUM(sale_all_weight)/1000,2 ) AS sale_all_weight,DATE_FORMAT(FROM_UNIXTIME(sale_at), '%Y-%m') AS statistic_method`)
 	}
 
 	if req.AnalysisMethod == pasturePb.SaleCowAnalysisMethod_Dealer {
-		pref.Select(`ROUND(SUM(sale_all_amount) /100,2) AS sale_all_amount,SUM(sale_cow_count) AS sale_all_count,
-		SUM(sale_all_weight) AS sale_all_weight,dealer_name as statistic_method`)
+		pref.Select(`ROUND(SUM(sale_all_amount)/100,2) AS sale_all_amount,SUM(sale_cow_count) AS sale_all_count,
+		ROUND(SUM(sale_all_weight)/1000,2) AS sale_all_weight,dealer_name as statistic_method`)
 	}
 
 	if err = pref.Group("statistic_method").

+ 33 - 3
module/backend/indicators.go

@@ -201,10 +201,28 @@ func (s *StoreEntry) AlreadySale(ctx context.Context, req *pasturePb.AlreadySale
 		return nil, xerr.WithStack(err)
 	}
 
-	cowKindMap := s.CowKindMap()
+	cowIds := make([]int64, 0)
+	for _, v := range eventSaleCowList {
+		cowIds = append(cowIds, v.CowId)
+	}
+
+	cowList := make([]*model.Cow, 0)
+	if err = s.DB.Model(new(model.Cow)).
+		Where("id IN ?", cowIds).
+		Where("pasture_id = ?", pastureId).
+		Find(&cowList).Error; err != nil {
+		return nil, xerr.WithStack(err)
+	}
+
+	cowMap := make(map[int64]*model.Cow)
+	for _, v := range cowList {
+		cowMap[v.Id] = v
+	}
 
+	sourceMap := s.CowSourceMap()
+	cowKindMap := s.CowKindMap()
 	result.Data.Total = int32(len(eventSaleCowList))
-	result.Data.List = model.EventSaleCowSlice(eventSaleCowList).ToPB(eventSaleMap, cowKindMap)
+	result.Data.List = model.EventSaleCowSlice(eventSaleCowList).ToPB(eventSaleMap, cowKindMap, cowMap, sourceMap)
 	return result, nil
 }
 
@@ -219,7 +237,19 @@ func (s *StoreEntry) CanSale(ctx context.Context, req *pasturePb.CanSalesReportR
 	var count int64
 	pref := s.DB.Model(new(model.Cow)).
 		Where("pasture_id = ?", pastureId).
-		Where("current_weight BETWEEN ? AND ?", req.WeightStart, req.WeightEnd)
+		Where("current_weight BETWEEN ? AND ?", req.WeightStart*1000, req.WeightEnd*1000)
+
+	if req.BatchNumber != "" {
+		pref.Where("batch_number = ?", req.BatchNumber)
+	}
+
+	if req.CowKind > pasturePb.CowKind_Invalid {
+		pref.Where("cow_kind = ?", req.CowKind)
+	}
+
+	if len(req.PenId) > 0 {
+		pref.Where("pen_id IN ?", req.PenId)
+	}
 
 	if err = pref.Count(&count).
 		Limit(int(pagination.PageSize)).

+ 1 - 1
module/backend/neck_ring_warning.go

@@ -301,7 +301,7 @@ func (s *StoreEntry) NeckRingNoDiseaseBatch(ctx context.Context, req *pasturePb.
 }
 func (s *StoreEntry) EstrusWarningQuery(ctx context.Context, pastureId int64) (*gorm.DB, error) {
 	nowTime := time.Now().Local()
-	startTime := time.Unix(util.TimeParseLocalUnix(nowTime.Format(model.LayoutDate2)), 0).Format(model.LayoutTime)
+	startTime := time.Unix(util.TimeParseLocalUnix(nowTime.AddDate(0, 0, -1).Format(model.LayoutDate2)), 0).Format(model.LayoutTime)
 	entTime := time.Unix(util.TimeParseLocalEndUnix(nowTime.Format(model.LayoutDate2)), 0).Format(model.LayoutTime)
 	systemBasic, err := s.FindSystemBasic(ctx, pastureId, model.EstrusWaringDays)
 	if err != nil {