Przeglądaj źródła

search: cowSale add

Yi 2 dni temu
rodzic
commit
5d76f1b2da

+ 1 - 1
go.mod

@@ -3,7 +3,7 @@ module kpt-pasture
 go 1.17
 
 require (
-	gitee.com/xuyiping_admin/go_proto v0.0.0-20250623073137-994f2b6ab389
+	gitee.com/xuyiping_admin/go_proto v0.0.0-20250624035747-6854c41ebc86
 	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

+ 6 - 0
go.sum

@@ -42,6 +42,12 @@ gitee.com/xuyiping_admin/go_proto v0.0.0-20250623070841-590ca50f5b8d h1:wCiJ6yav
 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/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=

+ 59 - 0
http/handler/cow/cow.go

@@ -217,3 +217,62 @@ func LongTermInfertility(c *gin.Context) {
 	}
 	ginutil.JSONResp(c, res)
 }
+
+func AlreadySale(c *gin.Context) {
+	var req pasturePb.AlreadySalesReportRequest
+	if err := ginutil.BindProto(c, &req); err != nil {
+		apierr.AbortBadRequest(c, http.StatusBadRequest, err)
+		return
+	}
+
+	if err := valid.ValidateStruct(&req,
+		valid.Field(&req.StartAt, valid.Required),
+		valid.Field(&req.EndAt, valid.Required),
+	); err != nil {
+		apierr.AbortBadRequest(c, http.StatusBadRequest, err)
+		return
+	}
+
+	pagination := &pasturePb.PaginationModel{
+		Page:       int32(c.GetInt(middleware.Page)),
+		PageSize:   int32(c.GetInt(middleware.PageSize)),
+		PageOffset: int32(c.GetInt(middleware.PageOffset)),
+	}
+
+	res, err := middleware.Dependency(c).StoreEventHub.OpsService.AlreadySale(c, &req, pagination)
+	if err != nil {
+		apierr.ClassifiedAbort(c, err)
+		return
+	}
+	ginutil.JSONResp(c, res)
+}
+
+func CanSale(c *gin.Context) {
+	var req pasturePb.CanSalesReportRequest
+	if err := ginutil.BindProto(c, &req); err != nil {
+		apierr.AbortBadRequest(c, http.StatusBadRequest, err)
+		return
+	}
+
+	if err := valid.ValidateStruct(&req,
+		valid.Field(&req.WeightStart, valid.Required),
+		valid.Field(&req.WeightEnd, valid.Required),
+		valid.Field(&req.CurrentPrice, valid.Required),
+	); err != nil {
+		apierr.AbortBadRequest(c, http.StatusBadRequest, err)
+		return
+	}
+
+	pagination := &pasturePb.PaginationModel{
+		Page:       int32(c.GetInt(middleware.Page)),
+		PageSize:   int32(c.GetInt(middleware.PageSize)),
+		PageOffset: int32(c.GetInt(middleware.PageOffset)),
+	}
+
+	res, err := middleware.Dependency(c).StoreEventHub.OpsService.CanSale(c, &req, pagination)
+	if err != nil {
+		apierr.ClassifiedAbort(c, err)
+		return
+	}
+	ginutil.JSONResp(c, res)
+}

+ 2 - 0
http/route/cow_api.go

@@ -24,5 +24,7 @@ func CowAPI(opts ...func(engine *gin.Engine)) func(s *gin.Engine) {
 		searchRoute := authRouteGroup(s, "/api/v1/search/")
 		searchRoute.POST("/indicators/comparison", cow.IndicatorsComparison)
 		searchRoute.POST("/cow/long/term/infertility", cow.LongTermInfertility)
+		searchRoute.POST("/already/sale", cow.AlreadySale)
+		searchRoute.POST("/can/sale", cow.CanSale)
 	}
 }

+ 31 - 0
model/cow.go

@@ -825,6 +825,37 @@ func (c CowSlice) LongTermInfertilityToPB(breedStatusMap map[pasturePb.BreedStat
 	return res
 }
 
+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 := "", ""
+		if name, ok := cowKindMap[v.CowKind]; ok {
+			cowKindName = name
+		}
+		if v.LastWeightAt > 0 {
+			lastWeightAtFormat = time.Unix(v.LastWeightAt, 0).Local().Format(LayoutDate2)
+		}
+		res[i] = &pasturePb.CanSalesReport{
+			CowId:              int32(v.Id),
+			EarNumber:          v.EarNumber,
+			BatchNumber:        v.BatchNumber,
+			CowKindName:        cowKindName,
+			PenName:            v.PenName,
+			Weight:             float32(v.CurrentWeight) / 1000,
+			AdmissionAge:       v.AdmissionAge,
+			EnterWeight:        0,
+			EnterPrice:         0,
+			LastWeightAtFormat: lastWeightAtFormat,
+			DayAvgFeedCost:     0,
+			AllFeedCost:        0,
+			AllMedicalCharge:   0,
+			OtherCost:          0,
+			ProfitAndLoss:      0,
+		}
+	}
+	return res
+}
+
 // CowBehaviorCurveResponse 脖环行为数据
 type CowBehaviorCurveResponse struct {
 	Code int32                 `json:"code"`

+ 59 - 1
model/event_sale_cow.go

@@ -1,14 +1,22 @@
 package model
 
-import pasturePb "gitee.com/xuyiping_admin/go_proto/proto/go/backend/cow"
+import (
+	"time"
+
+	pasturePb "gitee.com/xuyiping_admin/go_proto/proto/go/backend/cow"
+)
 
 type EventSaleCow struct {
 	Id           int64                      `json:"id"`
+	BatchNumber  string                     `json:"batchNumber"`
 	PastureId    int64                      `json:"pastureId"`
 	SaleId       int64                      `json:"saleId"`
 	CowId        int64                      `json:"cowId"`
 	CowType      pasturePb.CowType_Kind     `json:"cowType"`
+	CowKind      pasturePb.CowKind_Kind     `json:"cowKind"`
 	EarNumber    string                     `json:"earNumber"`
+	PenId        int32                      `json:"penId"`
+	PenName      string                     `json:"penName"`
 	DayAge       int32                      `json:"dayAge"`
 	Lact         int32                      `json:"lact"`
 	PregnancyAge int32                      `json:"pregnancyAge"`
@@ -27,16 +35,20 @@ func (e *EventSaleCow) TableName() string {
 func NewEventSaleCow(pastureId int64, saleId, saleAt int64, cowInfo *Cow) *EventSaleCow {
 	return &EventSaleCow{
 		PastureId:    pastureId,
+		BatchNumber:  cowInfo.BatchNumber,
 		SaleId:       saleId,
 		CowId:        cowInfo.Id,
 		Lact:         cowInfo.Lact,
 		EarNumber:    cowInfo.EarNumber,
+		PenId:        cowInfo.PenId,
+		PenName:      cowInfo.PenName,
 		PregnancyAge: cowInfo.PregnancyAge,
 		BreedStatus:  cowInfo.BreedStatus,
 		LactationAge: cowInfo.LactationAge,
 		AdmissionAge: cowInfo.AdmissionAge,
 		DayAge:       cowInfo.GetEventDayAge(saleAt),
 		CowType:      cowInfo.CowType,
+		CowKind:      cowInfo.CowKind,
 	}
 }
 
@@ -47,3 +59,49 @@ func NewEventSaleCowList(pastureId int64, eventSale *EventSale, cowList []*Cow)
 	}
 	return res
 }
+
+type EventSaleCowSlice []*EventSaleCow
+
+func (e EventSaleCowSlice) ToPB(eventSaleMap map[int64]*EventSale, cowKindMap map[pasturePb.CowKind_Kind]string) []*pasturePb.AlreadySalesReport {
+	res := make([]*pasturePb.AlreadySalesReport, 0)
+	for _, v := range e {
+		cowKindName, saleAtFormat, dealerName := "", "", ""
+		if name, ok := cowKindMap[v.CowKind]; ok {
+			cowKindName = name
+		}
+
+		salePrice, saleTotalAmount, saleAllWeight, saleAvgWeight := float32(0), float32(0), float32(0), float32(0)
+		if eventSale, ok := eventSaleMap[v.SaleId]; ok {
+			if eventSale.SaleAt > 0 {
+				saleAtFormat = time.Unix(eventSale.SaleAt, 0).Local().Format(LayoutDate2)
+			}
+			saleTotalAmount = float32(eventSale.SaleAllAmount)
+			dealerName = eventSale.DealerName
+		}
+
+		res = append(res, &pasturePb.AlreadySalesReport{
+			CowId:            int32(v.CowId),
+			EarNumber:        v.EarNumber,
+			BatchNumber:      v.BatchNumber,
+			CowKindName:      cowKindName,
+			PenName:          v.PenName,
+			SaleAtFormat:     saleAtFormat,
+			SalePrice:        salePrice,
+			SaleTotalAmount:  saleTotalAmount,
+			SaleAvgWeight:    saleAvgWeight,
+			SaleAllWeight:    saleAllWeight,
+			AdmissionAge:     v.AdmissionAge,
+			CowSourceName:    "",
+			EnterWeight:      0,
+			EnterPrice:       0,
+			GrowthWeight:     0,
+			AllFeedCost:      0,
+			DayAvgFeedCost:   0,
+			AllMedicalCharge: 0,
+			OtherCost:        0,
+			DealerName:       dealerName,
+			ProfitAndLoss:    0,
+		})
+	}
+	return res
+}

+ 112 - 0
module/backend/indicators.go

@@ -2,6 +2,7 @@ package backend
 
 import (
 	"context"
+	"fmt"
 	"kpt-pasture/model"
 	"kpt-pasture/util"
 	"net/http"
@@ -135,3 +136,114 @@ func (s *StoreEntry) LongTermInfertility(ctx context.Context, req *pasturePb.Lon
 	}, nil
 
 }
+
+func (s *StoreEntry) AlreadySale(ctx context.Context, req *pasturePb.AlreadySalesReportRequest, pagination *pasturePb.PaginationModel) (*pasturePb.AlreadySalesReportResponse, error) {
+	userModel, err := s.GetUserModel(ctx)
+	if err != nil {
+		return nil, xerr.WithStack(err)
+	}
+
+	pastureId := userModel.AppPasture.Id
+	eventSaleList := make([]*model.EventSale, 0)
+	pref := s.DB.Table(fmt.Sprintf("%s AS a", new(model.EventSale).TableName())).
+		Select("a.id,a.dealer_name,a.sale_kind,a.sale_price,a.sale_all_weight,a.sale_all_amount,sale_cow_count,a.sale_at,a.remarks").
+		Joins(fmt.Sprintf("JOIN %s AS b on b.sale_id = a.id", new(model.EventSaleCow).TableName())).
+		Where("a.pasture_id = ?", pastureId).
+		Where("a.sale_at BETWEEN ? AND ?", req.StartAt, req.EndAt)
+
+	if req.BatchNumber != "" {
+		pref.Where("b.batch_number = ?", req.BatchNumber)
+	}
+
+	if len(req.PenId) > 0 {
+		pref.Where("b.pen_id IN ?", req.PenId)
+	}
+
+	if req.CowKind > pasturePb.CowKind_Invalid {
+		pref.Where("b.cow_kind = ?", req.CowKind)
+	}
+
+	if err = pref.
+		Limit(int(pagination.PageSize)).
+		Offset(int(pagination.PageOffset)).
+		Group("a.id").
+		Find(&eventSaleList).Error; err != nil {
+		return nil, xerr.WithStack(err)
+	}
+
+	result := &pasturePb.AlreadySalesReportResponse{
+		Code: http.StatusOK,
+		Msg:  "ok",
+		Data: &pasturePb.AlreadySalesReportData{
+			List:     make([]*pasturePb.AlreadySalesReport, 0),
+			Total:    0,
+			PageSize: pagination.PageSize,
+			Page:     pagination.Page,
+		},
+	}
+
+	if len(eventSaleList) <= 0 {
+		return result, nil
+	}
+
+	saleIds := make([]int64, 0)
+	eventSaleMap := make(map[int64]*model.EventSale)
+	for _, v := range eventSaleList {
+		saleIds = append(saleIds, v.Id)
+		eventSaleMap[v.Id] = v
+	}
+
+	eventSaleCowList := make([]*model.EventSaleCow, 0)
+	if err = s.DB.Model(new(model.EventSaleCow)).
+		Where("sale_id IN ?", saleIds).
+		Where("pasture_id = ?", pastureId).
+		Find(&eventSaleCowList).Error; err != nil {
+		return nil, xerr.WithStack(err)
+	}
+
+	cowKindMap := s.CowKindMap()
+
+	result.Data.Total = int32(len(eventSaleCowList))
+	result.Data.List = model.EventSaleCowSlice(eventSaleCowList).ToPB(eventSaleMap, cowKindMap)
+	return result, nil
+}
+
+func (s *StoreEntry) CanSale(ctx context.Context, req *pasturePb.CanSalesReportRequest, pagination *pasturePb.PaginationModel) (*pasturePb.CanSalesReportResponse, error) {
+	userModel, err := s.GetUserModel(ctx)
+	if err != nil {
+		return nil, xerr.WithStack(err)
+	}
+
+	pastureId := userModel.AppPasture.Id
+	cowList := make([]*model.Cow, 0)
+	var count int64
+	pref := s.DB.Model(new(model.Cow)).
+		Where("pasture_id = ?", pastureId).
+		Where("current_weight BETWEEN ? AND ?", req.WeightStart, req.WeightEnd)
+
+	if err = pref.Count(&count).
+		Limit(int(pagination.PageSize)).
+		Offset(int(pagination.PageOffset)).
+		Find(&cowList).Error; err != nil {
+		return nil, xerr.WithStack(err)
+	}
+
+	result := &pasturePb.CanSalesReportResponse{
+		Code: http.StatusOK,
+		Msg:  "ok",
+		Data: &pasturePb.CanSalesReportData{
+			List:     make([]*pasturePb.CanSalesReport, 0),
+			Total:    0,
+			PageSize: pagination.PageSize,
+			Page:     pagination.Page,
+		},
+	}
+
+	if len(cowList) <= 0 {
+		return result, nil
+	}
+	cowKindMap := s.CowKindMap()
+	result.Data.Total = int32(count)
+	result.Data.List = model.CowSlice(cowList).CanSaleToPB(cowKindMap)
+	return result, nil
+}

+ 2 - 0
module/backend/interface.go

@@ -258,6 +258,8 @@ type CowService interface {
 
 	IndicatorsComparison(ctx context.Context, req *pasturePb.IndicatorsComparisonRequest) (*model.IndicatorsComparisonResponse, error)
 	LongTermInfertility(ctx context.Context, req *pasturePb.LongTermInfertilityRequest, pagination *pasturePb.PaginationModel) (*pasturePb.LongTermInfertilityResponse, error)
+	AlreadySale(ctx context.Context, req *pasturePb.AlreadySalesReportRequest, pagination *pasturePb.PaginationModel) (*pasturePb.AlreadySalesReportResponse, error)
+	CanSale(ctx context.Context, req *pasturePb.CanSalesReportRequest, pagination *pasturePb.PaginationModel) (*pasturePb.CanSalesReportResponse, error)
 }
 
 //go:generate mockgen -destination mock/GoodsService.go -package kptservicemock kpt-pasture/module/backend GoodsService