Browse Source

baseConfig: 基本接口新增周 options

ping 10 months ago
parent
commit
50cbdb00a8

+ 1 - 1
go.mod

@@ -3,7 +3,7 @@ module kpt-pasture
 go 1.17
 
 require (
-	gitee.com/xuyiping_admin/go_proto v0.0.0-20240529070447-01bc0d8768dd
+	gitee.com/xuyiping_admin/go_proto v0.0.0-20240604100629-0c14a75c8db9
 	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

@@ -110,6 +110,22 @@ gitee.com/xuyiping_admin/go_proto v0.0.0-20240529064747-dcb759c3560a h1:4bHsC64o
 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/go_proto v0.0.0-20240531082345-3dd14b125d48 h1:0JlsUnkHN+E7HrHkBr/EGMTzkZHgIbarf79sGdrySSs=
+gitee.com/xuyiping_admin/go_proto v0.0.0-20240531082345-3dd14b125d48/go.mod h1:BKrFW6YLDectlQcQk3FYKBeXvjEiodAKJ5rq7O/QiPE=
+gitee.com/xuyiping_admin/go_proto v0.0.0-20240603094532-849b4dfdebd7 h1:fkoS0sn9tfTgmaWOttgUT1T7yXl+dJxu1fz9OOX2+VE=
+gitee.com/xuyiping_admin/go_proto v0.0.0-20240603094532-849b4dfdebd7/go.mod h1:BKrFW6YLDectlQcQk3FYKBeXvjEiodAKJ5rq7O/QiPE=
+gitee.com/xuyiping_admin/go_proto v0.0.0-20240604025425-2b79576735f0 h1:6yxtv2VaXp2AOl302N01p5Y9wnxiXs41Tv1v9Nzt784=
+gitee.com/xuyiping_admin/go_proto v0.0.0-20240604025425-2b79576735f0/go.mod h1:BKrFW6YLDectlQcQk3FYKBeXvjEiodAKJ5rq7O/QiPE=
+gitee.com/xuyiping_admin/go_proto v0.0.0-20240604035106-f92a83aaee74 h1:CDk6DsE43sq+DkY7yhz59M+VO6rjZ3wMbG8RVnmT/BE=
+gitee.com/xuyiping_admin/go_proto v0.0.0-20240604035106-f92a83aaee74/go.mod h1:BKrFW6YLDectlQcQk3FYKBeXvjEiodAKJ5rq7O/QiPE=
+gitee.com/xuyiping_admin/go_proto v0.0.0-20240604062457-9ab13e7bd22a h1:fjyjGIy8CL0cfe3TOrXD34LeA/jsM85WMq+RMrGfcqg=
+gitee.com/xuyiping_admin/go_proto v0.0.0-20240604062457-9ab13e7bd22a/go.mod h1:BKrFW6YLDectlQcQk3FYKBeXvjEiodAKJ5rq7O/QiPE=
+gitee.com/xuyiping_admin/go_proto v0.0.0-20240604073438-879692b5ae4f h1:37W6UnS3QU4hpy1D2Jsmo8LQ+SrLMgKFSX/wK/uwbsg=
+gitee.com/xuyiping_admin/go_proto v0.0.0-20240604073438-879692b5ae4f/go.mod h1:BKrFW6YLDectlQcQk3FYKBeXvjEiodAKJ5rq7O/QiPE=
+gitee.com/xuyiping_admin/go_proto v0.0.0-20240604090708-739d4174a0e4 h1:KIzkTpmjuoUKVX2vYAQK1+4ftezu6WCMsVrEw4CqGL0=
+gitee.com/xuyiping_admin/go_proto v0.0.0-20240604090708-739d4174a0e4/go.mod h1:BKrFW6YLDectlQcQk3FYKBeXvjEiodAKJ5rq7O/QiPE=
+gitee.com/xuyiping_admin/go_proto v0.0.0-20240604100629-0c14a75c8db9 h1:DnST6fvYcKzIBaOkivc2/WqlvYQcwK/nrRs2a4HDT/8=
+gitee.com/xuyiping_admin/go_proto v0.0.0-20240604100629-0c14a75c8db9/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=

+ 92 - 0
http/handler/pasture/seme_time.go

@@ -0,0 +1,92 @@
+package pasture
+
+import (
+	"kpt-pasture/http/middleware"
+	"net/http"
+	"strconv"
+
+	pasturePb "gitee.com/xuyiping_admin/go_proto/proto/go/backend/cow"
+	operationPb "gitee.com/xuyiping_admin/go_proto/proto/go/backend/operation"
+	"gitee.com/xuyiping_admin/pkg/apierr"
+	"gitee.com/xuyiping_admin/pkg/ginutil"
+	"gitee.com/xuyiping_admin/pkg/valid"
+	"github.com/gin-gonic/gin"
+)
+
+// SemeTimeCreated 同期策略添加
+func SemeTimeCreated(c *gin.Context) {
+	var req pasturePb.SemeTimeRequest
+	if err := ginutil.BindProto(c, &req); err != nil {
+		apierr.AbortBadRequest(c, http.StatusBadRequest, err)
+		return
+	}
+	if err := valid.ValidateStruct(&req,
+		valid.Field(&req.Form, valid.Required, valid.By(func(value interface{}) error {
+			form := value.(*pasturePb.SemeTimeFormData)
+			if err := valid.ValidateStruct(form,
+				valid.Field(&form.Name, valid.Required),
+				valid.Field(&form.CowType, valid.Required),
+				valid.Field(&form.WeekType, valid.Required),
+				valid.Field(&form.PostpartumDays, valid.Required),
+			); err != nil {
+				return err
+			}
+			return nil
+		})),
+		valid.Field(&req.Graph, valid.Required),
+	); err != nil {
+		apierr.AbortBadRequest(c, http.StatusBadRequest, err)
+		return
+	}
+
+	if err := middleware.BackendOperation(c).OpsService.CreateOrUpdateSemeTime(c, &req); err != nil {
+		apierr.ClassifiedAbort(c, err)
+		return
+	}
+
+	ginutil.JSONResp(c, &operationPb.CommonOK{
+		Code: http.StatusOK,
+		Msg:  "ok",
+		Data: &operationPb.Success{Success: true},
+	})
+}
+
+// SemeTimeList 同步策略列表
+func SemeTimeList(c *gin.Context) {
+	var req pasturePb.SearchNameRequest
+	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.BackendOperation(c).OpsService.SearchSemeTimeList(c, &req, pagination)
+	if err != nil {
+		apierr.ClassifiedAbort(c, err)
+		return
+	}
+	ginutil.JSONResp(c, res)
+}
+
+// SemeTimeDetail 同步策略详情
+func SemeTimeDetail(c *gin.Context) {
+	semeTimeIdStr := c.Param("semeTimeId")
+	semeTimeId, _ := strconv.Atoi(semeTimeIdStr)
+
+	if err := valid.Validate(semeTimeId, valid.Required, valid.Min(1)); err != nil {
+		apierr.ClassifiedAbort(c, err)
+		return
+	}
+
+	res, err := middleware.BackendOperation(c).OpsService.SemeTimeDetail(c, int64(semeTimeId))
+	if err != nil {
+		apierr.ClassifiedAbort(c, err)
+		return
+	}
+	ginutil.JSONResp(c, res)
+}

+ 4 - 0
http/route/pasture_api.go

@@ -29,5 +29,9 @@ func PastureManageAPI(opts ...func(engine *gin.Engine)) func(s *gin.Engine) {
 		pastureRoute.POST("/transfer/pen/reason/createOrUpdate", pasture.CreatedOrUpdateTransferPenReason)
 		pastureRoute.POST("/cow/source/list", pasture.SearchCowSourceList)
 		pastureRoute.POST("/cow/source/createOrUpdate", pasture.CreatedOrUpdateCowSource)
+
+		pastureRoute.POST("/seme/time/create", pasture.SemeTimeCreated)
+		pastureRoute.POST("/seme/time/list", pasture.SemeTimeList)
+		pastureRoute.GET("/seme/time/detail/:semeTimeId", pasture.SemeTimeDetail)
 	}
 }

+ 71 - 0
model/event_seme_time.go

@@ -0,0 +1,71 @@
+package model
+
+import (
+	pasturePb "gitee.com/xuyiping_admin/go_proto/proto/go/backend/cow"
+)
+
+type EventSemeTime struct {
+	Id             int64                  `json:"id"`
+	Name           string                 `json:"name"`
+	WeekType       pasturePb.Week_Kind    `json:"week_type"`
+	CowType        pasturePb.CowType_Kind `json:"cow_type"`
+	IsShow         pasturePb.IsShow_Kind  `json:"is_show"`
+	IsDelete       pasturePb.IsShow_Kind  `json:"is_delete"`
+	PostpartumDays int32                  `json:"postpartum_days"`
+	Remarks        string                 `json:"remarks"`
+	OperationId    int64                  `json:"operation_id"`
+	CreatedAt      int64                  `json:"created_at"`
+	UpdatedAt      int64                  `json:"updated_at"`
+}
+
+func (e *EventSemeTime) TableName() string {
+	return "event_seme_time"
+}
+
+func NewEventSemeTime(currentUser *SystemUser, req *pasturePb.SemeTimeRequest) *EventSemeTime {
+	return &EventSemeTime{
+		Name:           req.Form.Name,
+		WeekType:       req.Form.WeekType,
+		CowType:        req.Form.CowType,
+		IsShow:         pasturePb.IsShow_No,
+		IsDelete:       pasturePb.IsShow_Ok,
+		PostpartumDays: req.Form.PostpartumDays,
+		Remarks:        req.Form.Remarks,
+		OperationId:    currentUser.Id,
+	}
+}
+
+type EventSemeTimeSlice []*EventSemeTime
+
+func (e EventSemeTimeSlice) ToPB(
+	weekMap map[pasturePb.Week_Kind]string,
+	cowTypeMap map[pasturePb.CowType_Kind]string,
+	systemUserList []*SystemUser,
+) []*pasturePb.SearchSemeTimeList {
+	res := make([]*pasturePb.SearchSemeTimeList, len(e))
+	for i, v := range e {
+		operationName := ""
+		for _, u := range systemUserList {
+			if u.Id != v.OperationId {
+				continue
+			}
+			operationName = u.Name
+		}
+		res[i] = &pasturePb.SearchSemeTimeList{
+			Id:             int32(v.Id),
+			Name:           v.Name,
+			WeekType:       v.WeekType,
+			WeekName:       weekMap[v.WeekType],
+			CowType:        v.CowType,
+			CowTypeName:    cowTypeMap[v.CowType],
+			IsShow:         v.IsShow,
+			PostpartumDays: v.PostpartumDays,
+			Remarks:        v.Remarks,
+			OperationId:    int32(v.OperationId),
+			OperationName:  operationName,
+			CreatedAt:      int32(v.CreatedAt),
+			UpdatedAt:      int32(v.UpdatedAt),
+		}
+	}
+	return res
+}

+ 68 - 0
model/event_seme_time_flow.go

@@ -0,0 +1,68 @@
+package model
+
+import (
+	"encoding/json"
+	"regexp"
+	"strconv"
+	"strings"
+
+	pasturePb "gitee.com/xuyiping_admin/go_proto/proto/go/backend/cow"
+)
+
+type EventSemeTimeFlow struct {
+	Id               int64  `json:"id"`
+	SemeTimeId       int64  `json:"seme_time_id"`
+	CollateFlowData  string `json:"collate_flow_data"`
+	OriginalFlowData string `json:"original_flow_data"`
+	CreatedAt        int64  `json:"created_at"`
+	UpdatedAt        int64  `json:"updated_at"`
+}
+
+func (e *EventSemeTimeFlow) TableName() string {
+	return "event_seme_time_flow"
+}
+
+func NewEventSemeTimeFlow(semeTimeId int64, req *pasturePb.SemeTimeRequest) *EventSemeTimeFlow {
+	originalFlowData, _ := json.Marshal(req.Graph)
+	collateFlowData := make([]*pasturePb.CollateFlowData, len(req.Graph.Nodes))
+	for i, v := range req.Graph.Nodes {
+		conditions := &pasturePb.FlowConditions{
+			Day:   0,
+			Hours: 0,
+		}
+
+		if i != len(req.Graph.Nodes)-1 && i < len(req.Graph.Edges) {
+			edgesTextValue := req.Graph.Edges[i].Text.Value
+			dayHours := strings.Split(edgesTextValue, "/")
+			dayStr := regexp.MustCompile(`\d+`).FindString(dayHours[0])
+			hoursStr := regexp.MustCompile(`\d+`).FindString(dayHours[1])
+			day, _ := strconv.Atoi(dayStr)
+			hours, _ := strconv.Atoi(hoursStr)
+			conditions.Day = int32(day)
+			conditions.Hours = int32(hours)
+		}
+
+		newCollateFlowData := &pasturePb.CollateFlowData{
+			Id:         v.Id,
+			Sort:       int32(i + 1),
+			NodeName:   v.Text.Value,
+			Conditions: conditions,
+		}
+
+		switch i {
+		case 0:
+			newCollateFlowData.NodeType = pasturePb.FlowerNodeType_Start
+		case len(req.Graph.Nodes) - 1:
+			newCollateFlowData.NodeType = pasturePb.FlowerNodeType_End
+		default:
+			newCollateFlowData.NodeType = pasturePb.FlowerNodeType_Middle
+		}
+		collateFlowData[i] = newCollateFlowData
+	}
+	collateFlowDataByte, _ := json.Marshal(collateFlowData)
+	return &EventSemeTimeFlow{
+		SemeTimeId:       semeTimeId,
+		CollateFlowData:  string(collateFlowDataByte),
+		OriginalFlowData: string(originalFlowData),
+	}
+}

+ 76 - 0
module/backend/config_data.go

@@ -87,6 +87,7 @@ func (s *StoreEntry) BarnTypeEnumList() []*pasturePb.ConfigOptionsList {
 	})
 	return barnTypeList
 }
+
 func (s *StoreEntry) BreedStatusEnumList() []*pasturePb.ConfigOptionsList {
 	breedStatusList := make([]*pasturePb.ConfigOptionsList, 0)
 	breedStatusList = append(breedStatusList, &pasturePb.ConfigOptionsList{
@@ -120,6 +121,7 @@ func (s *StoreEntry) BreedStatusEnumList() []*pasturePb.ConfigOptionsList {
 	})
 	return breedStatusList
 }
+
 func (s *StoreEntry) CowKindEnumList() []*pasturePb.ConfigOptionsList {
 	cowKindList := make([]*pasturePb.ConfigOptionsList, 0)
 	cowKindList = append(cowKindList, &pasturePb.ConfigOptionsList{
@@ -232,6 +234,20 @@ func (s *StoreEntry) CowTypeEnumList() []*pasturePb.ConfigOptionsList {
 	return cowTypeList
 }
 
+func (s *StoreEntry) SemeTimeCowTypeEnumList() []*pasturePb.ConfigOptionsList {
+	cowTypeList := make([]*pasturePb.ConfigOptionsList, 0)
+	cowTypeList = append(cowTypeList, &pasturePb.ConfigOptionsList{
+		Value:    int32(pasturePb.CowType_Nurturing),
+		Label:    "育成牛",
+		Disabled: true,
+	}, &pasturePb.ConfigOptionsList{
+		Value:    int32(pasturePb.CowType_Lactation),
+		Label:    "泌乳牛",
+		Disabled: true,
+	})
+	return cowTypeList
+}
+
 func (s *StoreEntry) TransferPenEnumList() []*pasturePb.ConfigOptionsList {
 	transferPenList := make([]*pasturePb.ConfigOptionsList, 0)
 	transferPenList = append(transferPenList, &pasturePb.ConfigOptionsList{
@@ -513,6 +529,7 @@ func (s *StoreEntry) ExposeEstrusTypeEnumList() []*pasturePb.ConfigOptionsList {
 		})
 	return configOptions
 }
+
 func (s *StoreEntry) FrozenSemenTypeEnumList() []*pasturePb.ConfigOptionsList {
 	configOptions := make([]*pasturePb.ConfigOptionsList, 0)
 	configOptions = append(configOptions,
@@ -544,6 +561,42 @@ func (s *StoreEntry) BullNumberEnumList() []*pasturePb.BullOptionsList {
 	}
 	return bullNumberList
 }
+
+func (s *StoreEntry) WeekEnumList() []*pasturePb.ConfigOptionsList {
+	configOptions := make([]*pasturePb.ConfigOptionsList, 0)
+	configOptions = append(configOptions,
+		&pasturePb.ConfigOptionsList{
+			Value:    int32(pasturePb.Week_Monday),
+			Label:    "周一",
+			Disabled: true,
+		}, &pasturePb.ConfigOptionsList{
+			Value:    int32(pasturePb.Week_Tuesday),
+			Label:    "周二",
+			Disabled: true,
+		}, &pasturePb.ConfigOptionsList{
+			Value:    int32(pasturePb.Week_Wednesday),
+			Label:    "周三",
+			Disabled: true,
+		}, &pasturePb.ConfigOptionsList{
+			Value:    int32(pasturePb.Week_Thursday),
+			Label:    "周四",
+			Disabled: true,
+		}, &pasturePb.ConfigOptionsList{
+			Value:    int32(pasturePb.Week_Friday),
+			Label:    "周五",
+			Disabled: true,
+		}, &pasturePb.ConfigOptionsList{
+			Value:    int32(pasturePb.Week_Saturday),
+			Label:    "周六",
+			Disabled: true,
+		}, &pasturePb.ConfigOptionsList{
+			Value:    int32(pasturePb.Week_Sunday),
+			Label:    "周日",
+			Disabled: true,
+		})
+	return configOptions
+}
+
 func (s *StoreEntry) DrugUsageMaps() map[pasturePb.DrugUsage_Kind]string {
 	res := make(map[pasturePb.DrugUsage_Kind]string)
 	for _, v := range s.DrugUsageEnumList() {
@@ -551,6 +604,7 @@ 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() {
@@ -558,6 +612,7 @@ 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() {
@@ -573,6 +628,23 @@ func (s *StoreEntry) UnitMap() map[pasturePb.Unit_Kind]string {
 	}
 	return res
 }
+
+func (s *StoreEntry) WeekMap() map[pasturePb.Week_Kind]string {
+	res := make(map[pasturePb.Week_Kind]string)
+	for _, v := range s.WeekEnumList() {
+		res[pasturePb.Week_Kind(v.Value)] = v.Label
+	}
+	return res
+}
+
+func (s *StoreEntry) CowTypeMap() map[pasturePb.CowType_Kind]string {
+	res := make(map[pasturePb.CowType_Kind]string)
+	for _, v := range s.CowTypeEnumList() {
+		res[pasturePb.CowType_Kind(v.Value)] = v.Label
+	}
+	return res
+}
+
 func (s *StoreEntry) BarnTypeOptions(ctx context.Context) (*pasturePb.ConfigOptionsListResponse, error) {
 	return &pasturePb.ConfigOptionsListResponse{
 		Code:    http.StatusOK,
@@ -693,6 +765,10 @@ func (s *StoreEntry) SystemBaseConfigOptions(ctx context.Context, optionsName st
 		configOptions = s.ExposeEstrusTypeEnumList()
 	case "frozenSemenType":
 		configOptions = s.FrozenSemenTypeEnumList()
+	case "week":
+		configOptions = s.WeekEnumList()
+	case "sameTimeCowType":
+		configOptions = s.SemeTimeCowTypeEnumList()
 	}
 
 	return &pasturePb.ConfigOptionsListResponse{

+ 4 - 0
module/backend/interface.go

@@ -105,6 +105,10 @@ type PastureManageService interface {
 	CreateOrUpdateTransferPenReason(ctx context.Context, req *pasturePb.SearchBaseConfigList) error
 	SearchCowSourceList(ctx context.Context, req *pasturePb.SearchNameRequest, pagination *pasturePb.PaginationModel) (*pasturePb.SearchBaseConfigResponse, error)
 	CreateOrUpdateCowSource(ctx context.Context, req *pasturePb.SearchBaseConfigList) error
+
+	CreateOrUpdateSemeTime(ctx context.Context, req *pasturePb.SemeTimeRequest) error
+	SearchSemeTimeList(ctx context.Context, req *pasturePb.SearchNameRequest, pagination *pasturePb.PaginationModel) (*pasturePb.SemeTimeEventResponse, error)
+	SemeTimeDetail(ctx context.Context, semeTimeId int64) (*pasturePb.SemeTimeDetailResponse, error)
 }
 
 //go:generate mockgen -destination mock/ConfigDataService.go -package kptservicemock kpt-pasture/module/backend ConfigDataService

+ 95 - 0
module/backend/pasture.go

@@ -2,6 +2,7 @@ package backend
 
 import (
 	"context"
+	"encoding/json"
 	"errors"
 	"fmt"
 	"kpt-pasture/model"
@@ -408,3 +409,97 @@ func (s *StoreEntry) CreateOrUpdateCowSource(ctx context.Context, req *pasturePb
 	}
 	return nil
 }
+
+func (s *StoreEntry) CreateOrUpdateSemeTime(ctx context.Context, req *pasturePb.SemeTimeRequest) error {
+	currentUser, _ := s.GetCurrentSystemUser(ctx)
+	if err := s.DB.Transaction(func(tx *gorm.DB) error {
+		semeTime := model.NewEventSemeTime(currentUser, req)
+		semeTimeFlow := model.NewEventSemeTimeFlow(semeTime.Id, req)
+
+		if req.Form.Id > 0 {
+			if err := tx.Model(new(model.EventSemeTime)).Where("id = ?", req.Form.Id).Updates(semeTime).Error; err != nil {
+				return xerr.WithStack(err)
+			}
+
+			if err := tx.Model(new(model.EventSemeTimeFlow)).Where("seme_time_id = ?", req.Form.Id).Updates(semeTimeFlow).Error; err != nil {
+				return xerr.WithStack(err)
+			}
+		} else {
+			if err := tx.Create(&semeTime).Error; err != nil {
+				return xerr.WithStack(err)
+			}
+			if err := tx.Create(&semeTimeFlow).Error; err != nil {
+				return xerr.WithStack(err)
+			}
+		}
+
+		return nil
+	}); err != nil {
+		return xerr.WithStack(err)
+	}
+	return nil
+}
+
+func (s *StoreEntry) SearchSemeTimeList(ctx context.Context, req *pasturePb.SearchNameRequest, pagination *pasturePb.PaginationModel) (*pasturePb.SemeTimeEventResponse, error) {
+	semeTimeList := make([]*model.EventSemeTime, 0)
+	var count int64 = 0
+	pref := s.DB.Model(new(model.EventSemeTime)).
+		Where("is_delete = ?", pasturePb.IsShow_Ok)
+	if req.Name != "" {
+		pref.Where("name like ?", fmt.Sprintf("%s%s%s", "%", req.Name, "%"))
+	}
+
+	if err := pref.Order("id desc").Count(&count).Limit(int(pagination.PageSize)).Offset(int(pagination.PageOffset)).
+		Find(&semeTimeList).Error; err != nil {
+		return nil, xerr.WithStack(err)
+	}
+
+	weekMap := s.WeekMap()
+	cowTypeMap := s.CowTypeMap()
+	systemUser, _ := s.SystemUserList(ctx)
+	return &pasturePb.SemeTimeEventResponse{
+		Code:    http.StatusOK,
+		Message: "ok",
+		Data: &pasturePb.SearchSemeTimeData{
+			List:     model.EventSemeTimeSlice(semeTimeList).ToPB(weekMap, cowTypeMap, systemUser),
+			Total:    int32(count),
+			PageSize: pagination.PageSize,
+			Page:     pagination.Page,
+		},
+	}, nil
+}
+
+func (s *StoreEntry) SemeTimeDetail(ctx context.Context, semeTimeId int64) (*pasturePb.SemeTimeDetailResponse, error) {
+	semeTime := &model.EventSemeTime{Id: semeTimeId}
+	if err := s.DB.Model(new(model.EventSemeTime)).
+		Where("is_delete = ?", pasturePb.IsShow_Ok).First(semeTime).Error; err != nil {
+		return nil, xerr.WithStack(err)
+	}
+
+	semeTimeFlow := &model.EventSemeTimeFlow{}
+	if err := s.DB.Model(new(model.EventSemeTimeFlow)).Where("seme_time_id = ?", semeTimeId).
+		First(&semeTimeFlow).Error; err != nil {
+		return nil, xerr.WithStack(err)
+	}
+
+	graph := &pasturePb.SemeTimeGraphData{}
+	if err := json.Unmarshal([]byte(semeTimeFlow.OriginalFlowData), graph); err != nil {
+		return nil, xerr.WithStack(err)
+	}
+
+	return &pasturePb.SemeTimeDetailResponse{
+		Code:    http.StatusOK,
+		Message: "ok",
+		Data: &pasturePb.SemeTimeRequest{
+			Form: &pasturePb.SemeTimeFormData{
+				Id:             int32(semeTime.Id),
+				Name:           semeTime.Name,
+				WeekType:       semeTime.WeekType,
+				PostpartumDays: semeTime.PostpartumDays,
+				CowType:        semeTime.CowType,
+				Remarks:        semeTime.Remarks,
+			},
+			Graph: graph,
+		},
+	}, nil
+}