Browse Source

event: 冻精

ping 10 months ago
parent
commit
d39558dc0f

+ 1 - 1
go.mod

@@ -3,7 +3,7 @@ module kpt-pasture
 go 1.17
 
 require (
-	gitee.com/xuyiping_admin/go_proto v0.0.0-20240528132259-854b1b5724a7
+	gitee.com/xuyiping_admin/go_proto v0.0.0-20240529070447-01bc0d8768dd
 	gitee.com/xuyiping_admin/pkg v0.0.0-20231218082641-aac597b8a015
 	github.com/dgrijalva/jwt-go v3.2.0+incompatible
 	github.com/getsentry/sentry-go v0.23.0

+ 16 - 0
go.sum

@@ -94,6 +94,22 @@ gitee.com/xuyiping_admin/go_proto v0.0.0-20240528124602-b05c9ca54524 h1:XpGCGOdn
 gitee.com/xuyiping_admin/go_proto v0.0.0-20240528124602-b05c9ca54524/go.mod h1:BKrFW6YLDectlQcQk3FYKBeXvjEiodAKJ5rq7O/QiPE=
 gitee.com/xuyiping_admin/go_proto v0.0.0-20240528132259-854b1b5724a7 h1:ewjC7qfTvPWB/jv5mSAp8k/uveh85LRjdmDemRJX8Lk=
 gitee.com/xuyiping_admin/go_proto v0.0.0-20240528132259-854b1b5724a7/go.mod h1:BKrFW6YLDectlQcQk3FYKBeXvjEiodAKJ5rq7O/QiPE=
+gitee.com/xuyiping_admin/go_proto v0.0.0-20240529022210-3e8e11d634ed h1:+0YfBq7lzoKYbX6FdFDGTHJeFNyiTjYOlexn6u+1DH8=
+gitee.com/xuyiping_admin/go_proto v0.0.0-20240529022210-3e8e11d634ed/go.mod h1:BKrFW6YLDectlQcQk3FYKBeXvjEiodAKJ5rq7O/QiPE=
+gitee.com/xuyiping_admin/go_proto v0.0.0-20240529025132-dc07cdcc5ce7 h1:Bl+OMdqvnQoYsnqaAO0AqrznZ/FL4Uf2CofbRDjtVGs=
+gitee.com/xuyiping_admin/go_proto v0.0.0-20240529025132-dc07cdcc5ce7/go.mod h1:BKrFW6YLDectlQcQk3FYKBeXvjEiodAKJ5rq7O/QiPE=
+gitee.com/xuyiping_admin/go_proto v0.0.0-20240529044129-2367814e08f4 h1:biqD32PXZ9w9rjvAHRZYBAYXvOqcyGF1vz5EsPHG5Lc=
+gitee.com/xuyiping_admin/go_proto v0.0.0-20240529044129-2367814e08f4/go.mod h1:BKrFW6YLDectlQcQk3FYKBeXvjEiodAKJ5rq7O/QiPE=
+gitee.com/xuyiping_admin/go_proto v0.0.0-20240529045252-27c0576ca11d h1:HZX1qkLVnyYuw+NLsQyl0hOGvTUOquxUQs+0ei18Kkc=
+gitee.com/xuyiping_admin/go_proto v0.0.0-20240529045252-27c0576ca11d/go.mod h1:BKrFW6YLDectlQcQk3FYKBeXvjEiodAKJ5rq7O/QiPE=
+gitee.com/xuyiping_admin/go_proto v0.0.0-20240529050742-264793778de3 h1:mdItGgC5k5xmkjB4ylrBkr3aW4EesLuZu7h4wK6n46w=
+gitee.com/xuyiping_admin/go_proto v0.0.0-20240529050742-264793778de3/go.mod h1:BKrFW6YLDectlQcQk3FYKBeXvjEiodAKJ5rq7O/QiPE=
+gitee.com/xuyiping_admin/go_proto v0.0.0-20240529062730-b0639ee20eea h1:RLAqRhhLTWRNmfJGiFgINhZx/QyBoVebSqzZQFfdRfo=
+gitee.com/xuyiping_admin/go_proto v0.0.0-20240529062730-b0639ee20eea/go.mod h1:BKrFW6YLDectlQcQk3FYKBeXvjEiodAKJ5rq7O/QiPE=
+gitee.com/xuyiping_admin/go_proto v0.0.0-20240529064747-dcb759c3560a h1:4bHsC64oLbFcLWZPvLQ5bUU0xVCR/mORCd4tATtCTws=
+gitee.com/xuyiping_admin/go_proto v0.0.0-20240529064747-dcb759c3560a/go.mod h1:BKrFW6YLDectlQcQk3FYKBeXvjEiodAKJ5rq7O/QiPE=
+gitee.com/xuyiping_admin/go_proto v0.0.0-20240529070447-01bc0d8768dd h1:TGdQSfkaC86lsvky1aW7+b2iKN+/Rj7jDsi0o9Ku/yo=
+gitee.com/xuyiping_admin/go_proto v0.0.0-20240529070447-01bc0d8768dd/go.mod h1:BKrFW6YLDectlQcQk3FYKBeXvjEiodAKJ5rq7O/QiPE=
 gitee.com/xuyiping_admin/pkg v0.0.0-20231218082641-aac597b8a015 h1:dfb5dRd57L2HKjdwLT93UFmPYFPOmEl56gtZmqcNnaE=
 gitee.com/xuyiping_admin/pkg v0.0.0-20231218082641-aac597b8a015/go.mod h1:Fk4GYI/v0IK3XFrm1Gn+VkgCz5Y7mfswD5hsTJYOG6A=
 github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=

+ 9 - 0
http/handler/config/config.go

@@ -104,6 +104,15 @@ func SystemUserOptions(c *gin.Context) {
 	ginutil.JSONResp(c, res)
 }
 
+func BullListOptions(c *gin.Context) {
+	res, err := middleware.Dependency(c).StoreEventHub.OpsService.BullOptions(c)
+	if err != nil {
+		apierr.ClassifiedAbort(c, err)
+		return
+	}
+	ginutil.JSONResp(c, res)
+}
+
 func SystemBaseConfigOptions(c *gin.Context) {
 	optionName := c.Query("option_name")
 	if err := valid.Validate(optionName, valid.Required, valid.Length(1, 20)); err != nil {

+ 53 - 1
http/handler/event/event.go

@@ -315,7 +315,7 @@ func MatingCreate(c *gin.Context) {
 		valid.Field(&req.CowId, valid.Required),
 		valid.Field(&req.StaffMemberId, valid.Required),
 		valid.Field(&req.ExposeEstrusType, valid.Required),
-		valid.Field(&req.FrozenSemenNumber, valid.Required),
+		valid.Field(&req.BullId, valid.Required),
 		valid.Field(&req.MatingAt, valid.Required),
 	); err != nil {
 		apierr.AbortBadRequest(c, http.StatusBadRequest, err)
@@ -382,3 +382,55 @@ func EstrusCreate(c *gin.Context) {
 		Data: &operationPb.Success{Success: true},
 	})
 }
+
+func FrozenSemeList(c *gin.Context) {
+	var req pasturePb.FrozenSemenRequest
+	if err := ginutil.BindProto(c, &req); 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.FrozenSemenList(c, &req, pagination)
+	if err != nil {
+		apierr.ClassifiedAbort(c, err)
+		return
+	}
+	ginutil.JSONResp(c, res)
+}
+
+// FrozenSemeCreate 冻精
+func FrozenSemeCreate(c *gin.Context) {
+	var req pasturePb.SearchFrozenSemenList
+	if err := ginutil.BindProto(c, &req); err != nil {
+		apierr.AbortBadRequest(c, http.StatusBadRequest, err)
+		return
+	}
+
+	if err := valid.ValidateStruct(&req,
+		valid.Field(&req.BullId, valid.Required),
+		valid.Field(&req.Producer, valid.Required),
+		valid.Field(&req.Quantity, valid.Required),
+		valid.Field(&req.Unit, valid.Required),
+		valid.Field(&req.FrozenSemenType, valid.Required),
+	); err != nil {
+		apierr.AbortBadRequest(c, http.StatusBadRequest, err)
+		return
+	}
+
+	if err := middleware.BackendOperation(c).OpsService.FrozenSemenCreate(c, &req); err != nil {
+		apierr.ClassifiedAbort(c, err)
+		return
+	}
+
+	ginutil.JSONResp(c, &operationPb.CommonOK{
+		Code: http.StatusOK,
+		Msg:  "ok",
+		Data: &operationPb.Success{Success: true},
+	})
+}

+ 1 - 0
http/route/config_api.go

@@ -21,6 +21,7 @@ func ConfigAPI(opts ...func(engine *gin.Engine)) func(s *gin.Engine) {
 		pastureRoute.GET("/cow/status/options", config.CowStatusOptions)
 		pastureRoute.GET("/cow/type/options", config.CowTypeOptions)
 		pastureRoute.GET("/cow/transfer/pen/reason/options", config.CowTransferPenReasonOptions)
+		pastureRoute.GET("/bull/frozen/semen/options", config.BullListOptions)
 		pastureRoute.GET("/system/user/options", config.SystemUserOptions)
 		pastureRoute.GET("/system/base/config/options", config.SystemBaseConfigOptions)
 	}

+ 10 - 6
http/route/event_api.go

@@ -13,25 +13,29 @@ func EventAPI(opts ...func(engine *gin.Engine)) func(s *gin.Engine) {
 		}
 		// eventRoute API 组  事件管理
 		eventRoute := authRouteGroup(s, "/api/v1/event/")
+		// 入场
 		eventRoute.POST("/enter/list", event.EnterEventList)
 		eventRoute.POST("/enter/create", event.EnterEventCreate)
-
+		// 转群
 		eventRoute.POST("/group/transfer/list", event.GroupTransferEventList)
 		eventRoute.POST("/group/transfer/create", event.GroupTransferEventCreate)
-
+		// 体况评分
 		eventRoute.POST("/body/score/list", event.BodyScoreEventList)
 		eventRoute.POST("/body/score/create", event.BodyScoreEventCreate)
-
+		// 产犊
 		eventRoute.POST("/calving/list", event.CalvingEventList)
 		eventRoute.POST("/calving/create", event.CalvingEventCreate)
-
+		// 孕检
 		eventRoute.POST("/pregnant/check/list", event.PregnantCheckEventList)
 		eventRoute.POST("/pregnant/check/create", event.PregnantCheckEventCreate)
-
+		// 配种
 		eventRoute.POST("/mating/list", event.MatingEventList)
 		eventRoute.POST("/mating/create", event.MatingCreate)
-
+		// 发情
 		eventRoute.POST("/estrus/list", event.EstrusEventList)
 		eventRoute.POST("/estrus/create", event.EstrusCreate)
+		// 冻精
+		eventRoute.POST("/frozen/seme/list", event.FrozenSemeList)
+		eventRoute.POST("/frozen/seme/create", event.FrozenSemeCreate)
 	}
 }

+ 59 - 0
model/event_frozen_semen.go

@@ -0,0 +1,59 @@
+package model
+
+import pasturePb "gitee.com/xuyiping_admin/go_proto/proto/go/backend/cow"
+
+type EventFrozenSemen struct {
+	Id              int64                          `json:"id"`
+	ParentId        int64                          `json:"parent_id"`
+	Producer        string                         `json:"producer"`
+	BullId          string                         `json:"bull_id"`
+	FrozenSemenType pasturePb.FrozenSemenType_Kind `json:"frozen_semen_type"`
+	Quantity        int32                          `json:"quantity"`
+	Unit            pasturePb.Unit_Kind            `json:"unit"`
+	Remarks         string                         `json:"remarks"`
+	OperationId     int64                          `json:"operation_id"`
+	CreatedAt       int64                          `json:"created_at"`
+	UpdatedAt       int64                          `json:"updated_at"`
+}
+
+func NewEventFrozenSemen(req *pasturePb.SearchFrozenSemenList, currentUser *SystemUser) *EventFrozenSemen {
+	return &EventFrozenSemen{
+		Producer:        req.Producer,
+		BullId:          req.BullId,
+		FrozenSemenType: req.FrozenSemenType,
+		Quantity:        req.Quantity,
+		Unit:            req.Unit,
+		Remarks:         req.Remarks,
+		OperationId:     currentUser.Id,
+	}
+}
+
+func (e *EventFrozenSemen) TableName() string {
+	return "event_frozen_semen"
+}
+
+type EventFrozenSemenSlice []*EventFrozenSemen
+
+func (e EventFrozenSemenSlice) ToPB(
+	frozenSemenTypeMap map[pasturePb.FrozenSemenType_Kind]string,
+	unitMap map[pasturePb.Unit_Kind]string) []*pasturePb.SearchFrozenSemenList {
+	res := make([]*pasturePb.SearchFrozenSemenList, len(e))
+	for i, v := range e {
+		res[i] = &pasturePb.SearchFrozenSemenList{
+			Id:                  int32(v.Id),
+			ParentId:            int32(v.ParentId),
+			Producer:            v.Producer,
+			BullId:              v.BullId,
+			FrozenSemenType:     v.FrozenSemenType,
+			FrozenSemenTypeName: frozenSemenTypeMap[v.FrozenSemenType],
+			Quantity:            v.Quantity,
+			Unit:                v.Unit,
+			UnitName:            unitMap[v.Unit],
+			Remarks:             v.Remarks,
+			OperationId:         int32(v.OperationId),
+			CreatedAt:           int32(v.CreatedAt),
+			UpdatedAt:           int32(v.UpdatedAt),
+		}
+	}
+	return res
+}

+ 22 - 0
model/event_frozen_semen_log.go

@@ -0,0 +1,22 @@
+package model
+
+type EventFrozenSemenLog struct {
+	Id            int64  `json:"id"`
+	BullId        string `json:"bull_id"`
+	CowId         int64  `json:"cow_id"`
+	StallNumberId int64  `json:"stall_number_id"`
+	CreatedAt     int64  `json:"created_at"`
+	UpdatedAt     int64  `json:"updated_at"`
+}
+
+func (e *EventFrozenSemenLog) TableName() string {
+	return "event_frozen_semen_log"
+}
+
+func NewEventFrozenSemenLog(bullId string, cow *Cow, stallNumberId int64) *EventFrozenSemenLog {
+	return &EventFrozenSemenLog{
+		BullId:        bullId,
+		CowId:         cow.Id,
+		StallNumberId: stallNumberId,
+	}
+}

+ 3 - 3
model/event_mating.go

@@ -10,7 +10,7 @@ type EventMating struct {
 	LactationDays     int32                           `json:"lactation_days"`
 	ExposeEstrusType  pasturePb.ExposeEstrusType_Kind `json:"expose_estrus_type"`
 	MatingAt          int64                           `json:"mating_at"`
-	BullId            int64                           `json:"bull_id"`
+	BullId            string                          `json:"bull_id"`
 	FrozenSemenNumber string                          `json:"frozen_semen_number"`
 	StallNumberId     int64                           `json:"stall_number_id"`
 	OperationId       int64                           `json:"operation_id"`
@@ -31,7 +31,7 @@ func NewEventMating(cow *Cow, currentUser *SystemUser, req *pasturePb.MatingEven
 		LactationDays:     cow.GetLactationDays(),
 		ExposeEstrusType:  req.ExposeEstrusType,
 		MatingAt:          int64(req.MatingAt),
-		BullId:            int64(req.BullId),
+		BullId:            req.BullId,
 		FrozenSemenNumber: req.FrozenSemenNumber,
 		StallNumberId:     int64(req.StaffMemberId),
 		OperationId:       currentUser.Id,
@@ -61,7 +61,7 @@ func (e EventMatingSlice) ToPB(user []*SystemUser, exposeEstrusTypeMap map[pastu
 			ExposeEstrusType:     v.ExposeEstrusType,
 			ExposeEstrusTypeName: exposeEstrusTypeMap[v.ExposeEstrusType],
 			MatingAt:             int32(v.MatingAt),
-			BullId:               int32(v.BullId),
+			BullId:               v.BullId,
 			FrozenSemenNumber:    v.FrozenSemenNumber,
 			LactationDays:        v.LactationDays,
 			StaffMemberId:        int32(v.StallNumberId),

+ 59 - 1
module/backend/config_data.go

@@ -5,6 +5,9 @@ import (
 	"kpt-pasture/model"
 	"net/http"
 
+	"gitee.com/xuyiping_admin/pkg/logger/zaplog"
+	"go.uber.org/zap"
+
 	xxerr "gitee.com/xuyiping_admin/pkg/xerr"
 
 	pasturePb "gitee.com/xuyiping_admin/go_proto/proto/go/backend/cow"
@@ -510,7 +513,37 @@ func (s *StoreEntry) ExposeEstrusTypeEnumList() []*pasturePb.ConfigOptionsList {
 		})
 	return configOptions
 }
+func (s *StoreEntry) FrozenSemenTypeEnumList() []*pasturePb.ConfigOptionsList {
+	configOptions := make([]*pasturePb.ConfigOptionsList, 0)
+	configOptions = append(configOptions,
+		&pasturePb.ConfigOptionsList{
+			Value:    int32(pasturePb.FrozenSemenType_Ordinary),
+			Label:    "常规冻精",
+			Disabled: true,
+		}, &pasturePb.ConfigOptionsList{
+			Value:    int32(pasturePb.FrozenSemenType_Gender_Control),
+			Label:    "性控冻精",
+			Disabled: true,
+		})
+	return configOptions
+}
+
+func (s *StoreEntry) BullNumberEnumList() []*pasturePb.BullOptionsList {
+	frozenSemenList := make([]*model.EventFrozenSemen, 0)
+	bullNumberList := make([]*pasturePb.BullOptionsList, 0)
+	if err := s.DB.Where("quantity > 0").Group("bull_id").Find(&frozenSemenList).Error; err != nil {
+		zaplog.Error("BullNumberEnumList", zap.Any("Find", err))
+	}
 
+	for _, v := range frozenSemenList {
+		bullNumberList = append(bullNumberList, &pasturePb.BullOptionsList{
+			Value:    v.BullId,
+			Label:    v.BullId,
+			Disabled: true,
+		})
+	}
+	return bullNumberList
+}
 func (s *StoreEntry) DrugUsageMaps() map[pasturePb.DrugUsage_Kind]string {
 	res := make(map[pasturePb.DrugUsage_Kind]string)
 	for _, v := range s.DrugUsageEnumList() {
@@ -518,7 +551,6 @@ func (s *StoreEntry) DrugUsageMaps() map[pasturePb.DrugUsage_Kind]string {
 	}
 	return res
 }
-
 func (s *StoreEntry) ExposeEstrusTypeMap() map[pasturePb.ExposeEstrusType_Kind]string {
 	res := make(map[pasturePb.ExposeEstrusType_Kind]string)
 	for _, v := range s.ExposeEstrusTypeEnumList() {
@@ -526,6 +558,21 @@ func (s *StoreEntry) ExposeEstrusTypeMap() map[pasturePb.ExposeEstrusType_Kind]s
 	}
 	return res
 }
+func (s *StoreEntry) FrozenSemenTypeMap() map[pasturePb.FrozenSemenType_Kind]string {
+	res := make(map[pasturePb.FrozenSemenType_Kind]string)
+	for _, v := range s.FrozenSemenTypeEnumList() {
+		res[pasturePb.FrozenSemenType_Kind(v.Value)] = v.Label
+	}
+	return res
+}
+
+func (s *StoreEntry) UnitMap() map[pasturePb.Unit_Kind]string {
+	res := make(map[pasturePb.Unit_Kind]string)
+	for _, v := range s.UnitEnumList() {
+		res[pasturePb.Unit_Kind(v.Value)] = v.Label
+	}
+	return res
+}
 func (s *StoreEntry) BarnTypeOptions(ctx context.Context) (*pasturePb.ConfigOptionsListResponse, error) {
 	return &pasturePb.ConfigOptionsListResponse{
 		Code:    http.StatusOK,
@@ -615,6 +662,14 @@ func (s *StoreEntry) SystemUserOptions(ctx context.Context, depId int) (*pasture
 	}, nil
 }
 
+func (s *StoreEntry) BullOptions(ctx context.Context) (*pasturePb.BullOptionsListResponse, error) {
+	return &pasturePb.BullOptionsListResponse{
+		Code:    http.StatusOK,
+		Message: "ok",
+		Data:    s.BullNumberEnumList(),
+	}, nil
+}
+
 func (s *StoreEntry) SystemBaseConfigOptions(ctx context.Context, optionsName string) (*pasturePb.ConfigOptionsListResponse, error) {
 	configOptions := make([]*pasturePb.ConfigOptionsList, 0)
 	switch optionsName {
@@ -636,7 +691,10 @@ func (s *StoreEntry) SystemBaseConfigOptions(ctx context.Context, optionsName st
 		configOptions = s.PregnantCheckMethodEnumList()
 	case "exposeEstrusType":
 		configOptions = s.ExposeEstrusTypeEnumList()
+	case "frozenSemenType":
+		configOptions = s.FrozenSemenTypeEnumList()
 	}
+
 	return &pasturePb.ConfigOptionsListResponse{
 		Code:    http.StatusOK,
 		Message: "ok",

+ 71 - 4
module/backend/event.go

@@ -406,6 +406,7 @@ func (s *StoreEntry) MatingCreate(ctx context.Context, req *pasturePb.MatingEven
 	}
 	cowList := strings.Split(req.CowId, ",")
 	matingList := make([]*model.EventMating, 0)
+	eventFrozenSemenLogList := make([]*model.EventFrozenSemenLog, 0)
 	currentUser, _ := s.GetCurrentSystemUser(ctx)
 
 	for _, v := range cowList {
@@ -415,11 +416,34 @@ func (s *StoreEntry) MatingCreate(ctx context.Context, req *pasturePb.MatingEven
 			return xerr.WithStack(err)
 		}
 		matingList = append(matingList, model.NewEventMating(cow, currentUser, req))
+		eventFrozenSemenLogList = append(eventFrozenSemenLogList, model.NewEventFrozenSemenLog(req.BullId, cow, int64(req.StaffMemberId)))
 	}
 
-	if err := s.DB.Create(matingList).Error; err != nil {
+	frozenSemen := &model.EventFrozenSemen{}
+	if err := s.DB.Where("bull_id = ?", req.BullId).First(frozenSemen).Error; err != nil {
 		return xerr.WithStack(err)
 	}
+
+	if err := s.DB.Transaction(func(tx *gorm.DB) error {
+		if err := tx.Create(matingList).Error; err != nil {
+			return xerr.WithStack(err)
+		}
+
+		// 创建冻精使用记录日志
+		if err := tx.Create(eventFrozenSemenLogList).Error; err != nil {
+			return xerr.WithStack(err)
+		}
+
+		if err := tx.Table(new(model.EventFrozenSemen).TableName()).
+			Where("bull_id = ?", req.BullId).
+			Where("quantity > 0").UpdateColumn("quantity", gorm.Expr("quantity - ?", len(matingList))).Error; err != nil {
+			return xerr.WithStack(err)
+		}
+		return nil
+	}); err != nil {
+		return xerr.WithStack(err)
+	}
+
 	return nil
 }
 
@@ -457,7 +481,7 @@ func (s *StoreEntry) EstrusCreate(ctx context.Context, req *pasturePb.EstrusEven
 		return xerr.Custom("请选择相关牛只")
 	}
 	cowList := strings.Split(req.CowId, ",")
-	estruList := make([]*model.EventEstrus, 0)
+	estrusList := make([]*model.EventEstrus, 0)
 	currentUser, _ := s.GetCurrentSystemUser(ctx)
 
 	for _, v := range cowList {
@@ -466,10 +490,53 @@ func (s *StoreEntry) EstrusCreate(ctx context.Context, req *pasturePb.EstrusEven
 		if err != nil {
 			return xerr.WithStack(err)
 		}
-		estruList = append(estruList, model.NewEventEstrus(cow, currentUser, req))
+		estrusList = append(estrusList, model.NewEventEstrus(cow, currentUser, req))
 	}
 
-	if err := s.DB.Create(estruList).Error; err != nil {
+	if err := s.DB.Create(estrusList).Error; err != nil {
+		return xerr.WithStack(err)
+	}
+	return nil
+}
+
+func (s *StoreEntry) FrozenSemenList(ctx context.Context, req *pasturePb.FrozenSemenRequest, pagination *pasturePb.PaginationModel) (*pasturePb.FrozenSemenResponse, error) {
+	frozenSemenList := make([]*model.EventFrozenSemen, 0)
+	var count int64 = 0
+	pref := s.DB.Table(new(model.EventFrozenSemen).TableName())
+	if req.BullId != "" {
+		pref.Where("bull_id = ?", req.BullId)
+	}
+
+	if req.Producer != "" {
+		pref.Where("producer = ?", req.Producer)
+	}
+
+	if err := pref.Order("id desc").
+		Count(&count).Limit(int(pagination.PageSize)).
+		Offset(int(pagination.PageOffset)).
+		Find(&frozenSemenList).Error; err != nil {
+		return nil, xerr.WithStack(err)
+	}
+
+	frozenSemenTypeMap := s.FrozenSemenTypeMap()
+	unitMap := s.UnitMap()
+
+	return &pasturePb.FrozenSemenResponse{
+		Code:    http.StatusOK,
+		Message: "ok",
+		Data: &pasturePb.SearchFrozenSemenData{
+			List:     model.EventFrozenSemenSlice(frozenSemenList).ToPB(frozenSemenTypeMap, unitMap),
+			Total:    int32(count),
+			PageSize: pagination.PageSize,
+			Page:     pagination.Page,
+		},
+	}, nil
+}
+
+func (s *StoreEntry) FrozenSemenCreate(ctx context.Context, req *pasturePb.SearchFrozenSemenList) error {
+	currentUser, _ := s.GetCurrentSystemUser(ctx)
+	newFrozenSemen := model.NewEventFrozenSemen(req, currentUser)
+	if err := s.DB.Create(newFrozenSemen).Error; err != nil {
 		return xerr.WithStack(err)
 	}
 	return nil

+ 3 - 0
module/backend/interface.go

@@ -118,6 +118,7 @@ type ConfigDataService interface {
 	CowTypeOptions(ctx context.Context) (*pasturePb.ConfigOptionsListResponse, error)
 	CowTransferPenReasonOptions(ctx context.Context) (*pasturePb.ConfigOptionsListResponse, error)
 	SystemUserOptions(ctx context.Context, depId int) (*pasturePb.ConfigOptionsListResponse, error)
+	BullOptions(ctx context.Context) (*pasturePb.BullOptionsListResponse, error)
 	SystemBaseConfigOptions(ctx context.Context, optionName string) (*pasturePb.ConfigOptionsListResponse, error)
 }
 
@@ -137,6 +138,8 @@ type EventService interface {
 	MatingCreate(ctx context.Context, req *pasturePb.MatingEventRequest) error
 	EstrusList(ctx context.Context, req *pasturePb.SearchEventRequest, pagination *pasturePb.PaginationModel) (*pasturePb.EstrusEventResponse, error)
 	EstrusCreate(ctx context.Context, req *pasturePb.EstrusEventRequest) error
+	FrozenSemenList(ctx context.Context, req *pasturePb.FrozenSemenRequest, pagination *pasturePb.PaginationModel) (*pasturePb.FrozenSemenResponse, error)
+	FrozenSemenCreate(ctx context.Context, req *pasturePb.SearchFrozenSemenList) error
 }
 
 //go:generate mockgen -destination mock/CowService.go -package kptservicemock kpt-pasture/module/backend CowService