Browse Source

sameTime: 同期业务优化

Yi 6 months ago
parent
commit
25192072a0

+ 3 - 0
README.md

@@ -30,3 +30,6 @@ lint:
 需要更新 go-mock:
 - 生成 mock 前,请确保你能够编译 & 编译完成
 - make generate
+
+todo列表:
+- module/crontab/crontab.go 中119行[Limit(100)] 待优化,case为产后日期类型待测试

+ 2 - 2
cmd/crontab.go

@@ -27,7 +27,7 @@ func init() {
 
 func UpdateCowDayAge(ctx context.Context, args []string) error {
 	crontab := dep.DICrontabService()
-	if err := crontab.UpdateCowDayAge(); err != nil {
+	if err := crontab.UpdateCowInfo(); err != nil {
 		xerr.ReportSentry(ctx, err)
 	}
 	return nil
@@ -35,7 +35,7 @@ func UpdateCowDayAge(ctx context.Context, args []string) error {
 
 func GenerateWorkOrder(ctx context.Context, args []string) error {
 	crontab := dep.DICrontabService()
-	if err := crontab.GenerateWorkOrder(); err != nil {
+	if err := crontab.GenerateAsynqWorkOrder(); err != nil {
 		xerr.ReportSentry(ctx, err)
 	}
 	return nil

+ 1 - 1
go.mod

@@ -3,7 +3,7 @@ module kpt-pasture
 go 1.17
 
 require (
-	gitee.com/xuyiping_admin/go_proto v0.0.0-20240902071101-20469475f323
+	gitee.com/xuyiping_admin/go_proto v0.0.0-20240903091812-dfb3f5f46972
 	gitee.com/xuyiping_admin/pkg v0.0.0-20231218082641-aac597b8a015
 	github.com/dgrijalva/jwt-go v3.2.0+incompatible
 	github.com/eko/gocache v1.1.0

+ 8 - 0
go.sum

@@ -48,6 +48,14 @@ gitee.com/xuyiping_admin/go_proto v0.0.0-20240902063446-64deca9b4509 h1:iK8ZHFws
 gitee.com/xuyiping_admin/go_proto v0.0.0-20240902063446-64deca9b4509/go.mod h1:BKrFW6YLDectlQcQk3FYKBeXvjEiodAKJ5rq7O/QiPE=
 gitee.com/xuyiping_admin/go_proto v0.0.0-20240902071101-20469475f323 h1:VcOc6Wjo/Gmd54v3bc9DhvwD/Cx8cwcjHvPvUjY5N+k=
 gitee.com/xuyiping_admin/go_proto v0.0.0-20240902071101-20469475f323/go.mod h1:BKrFW6YLDectlQcQk3FYKBeXvjEiodAKJ5rq7O/QiPE=
+gitee.com/xuyiping_admin/go_proto v0.0.0-20240903030421-d1e0bc05036e h1:Rbk1auUc8bAJEc1N5ZcVslVJrZLCkOcxlsUGMX0+hMo=
+gitee.com/xuyiping_admin/go_proto v0.0.0-20240903030421-d1e0bc05036e/go.mod h1:BKrFW6YLDectlQcQk3FYKBeXvjEiodAKJ5rq7O/QiPE=
+gitee.com/xuyiping_admin/go_proto v0.0.0-20240903062850-3be618ca9ba6 h1:Sk5PAxHXQEg6Mua7pll2QIxNkGzsqdzz5lj8EEh7X3Q=
+gitee.com/xuyiping_admin/go_proto v0.0.0-20240903062850-3be618ca9ba6/go.mod h1:BKrFW6YLDectlQcQk3FYKBeXvjEiodAKJ5rq7O/QiPE=
+gitee.com/xuyiping_admin/go_proto v0.0.0-20240903085608-021437ccc8db h1:eF1rpNr58XeIETdsIuzLxiFnYLeg/jSEB3iOwljjl8I=
+gitee.com/xuyiping_admin/go_proto v0.0.0-20240903085608-021437ccc8db/go.mod h1:BKrFW6YLDectlQcQk3FYKBeXvjEiodAKJ5rq7O/QiPE=
+gitee.com/xuyiping_admin/go_proto v0.0.0-20240903091812-dfb3f5f46972 h1:UvNVKnlH2AU0sSELWxNC3NP822VJ/5vn1lVoepo8Wp0=
+gitee.com/xuyiping_admin/go_proto v0.0.0-20240903091812-dfb3f5f46972/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=

+ 19 - 33
http/handler/pasture/seme_time.go

@@ -3,7 +3,8 @@ package pasture
 import (
 	"kpt-pasture/http/middleware"
 	"net/http"
-	"strconv"
+
+	"gitee.com/xuyiping_admin/pkg/xerr"
 
 	pasturePb "gitee.com/xuyiping_admin/go_proto/proto/go/backend/cow"
 	operationPb "gitee.com/xuyiping_admin/go_proto/proto/go/backend/operation"
@@ -15,30 +16,33 @@ import (
 
 // SemeTimeCreated 同期策略添加
 func SemeTimeCreated(c *gin.Context) {
-	var req pasturePb.SemeTimeRequest
+	var req pasturePb.SearchSameTimeList
 	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),
+		valid.Field(&req.Name, valid.Required),
+		valid.Field(&req.CowType, valid.Required),
+		valid.Field(&req.WeekType, valid.Required),
+		valid.Field(&req.PostpartumDaysStart, valid.Required),
+		valid.Field(&req.PostpartumDaysEnd, valid.Required),
+		valid.Field(&req.CollateNodes, valid.Required),
 	); err != nil {
 		apierr.AbortBadRequest(c, http.StatusBadRequest, err)
 		return
 	}
 
+	if req.PostpartumDaysStart > req.PostpartumDaysEnd {
+		apierr.AbortBadRequest(c, http.StatusBadRequest, xerr.New("开始天数不能大于结束天数"))
+		return
+	}
+
+	if len(req.CollateNodes) < 0 {
+		apierr.AbortBadRequest(c, http.StatusBadRequest, xerr.New("错误的同期策略数据"))
+		return
+	}
+
 	if err := middleware.BackendOperation(c).OpsService.CreateOrUpdateSemeTime(c, &req); err != nil {
 		apierr.ClassifiedAbort(c, err)
 		return
@@ -72,21 +76,3 @@ func SemeTimeList(c *gin.Context) {
 	}
 	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)
-}

+ 0 - 1
http/route/pasture_api.go

@@ -48,6 +48,5 @@ func PastureManageAPI(opts ...func(engine *gin.Engine)) func(s *gin.Engine) {
 		// 同期相关
 		pastureRoute.POST("/seme/time/create", pasture.SemeTimeCreated)
 		pastureRoute.POST("/seme/time/list", pasture.SemeTimeList)
-		pastureRoute.GET("/seme/time/detail/:semeTimeId", pasture.SemeTimeDetail)
 	}
 }

+ 49 - 0
model/calving_calf.go

@@ -0,0 +1,49 @@
+package model
+
+import pasturePb "gitee.com/xuyiping_admin/go_proto/proto/go/backend/cow"
+
+type CalvingCalf struct {
+	Id            int64                  `json:"id"`
+	CalvingId     int64                  `json:"calvingId"`
+	CowId         int64                  `json:"cow_id"`
+	MotherId      int64                  `json:"motherId"`
+	EarNumber     string                 `json:"earNumber"`
+	Sex           pasturePb.Genders_Kind `json:"sex"`
+	CowKind       pasturePb.CowKind_Kind `json:"cowKind"`
+	BrithWeight   int64                  `json:"brithWeight"`
+	IsLive        pasturePb.IsShow_Kind  `json:"isLive"`
+	IsAdoption    pasturePb.IsShow_Kind  `json:"isAdoption"`
+	PenId         int32                  `json:"penId"`
+	WeaningAt     int64                  `json:"weaningAt"`
+	CurrentWeight int64                  `json:"currentWeight"`
+	Remarks       string                 `json:"remarks"`
+	CreatedAt     int64                  `json:"createdAt"`
+	UpdatedAt     int64                  `json:"updatedAt"`
+}
+
+func (e *CalvingCalf) TableName() string {
+	return "calving_calf"
+}
+
+func NewEventCalvingCalf(motherId, calvingId int64, req *pasturePb.CalvingEventRequest) []*CalvingCalf {
+	calvingCalfList := make([]*CalvingCalf, 0)
+	for _, v := range req.CalfItemList {
+		isAdoption := v.IsAdoption
+		if v.IsLive == pasturePb.IsShow_No {
+			isAdoption = pasturePb.IsShow_No
+		}
+		calvingCalfList = append(calvingCalfList, &CalvingCalf{
+			EarNumber:     v.EarNumber,
+			CalvingId:     calvingId,
+			PenId:         v.PenId,
+			BrithWeight:   int64(v.Weight * 1000),
+			CurrentWeight: int64(v.Weight * 1000),
+			Sex:           v.Sex,
+			MotherId:      motherId,
+			Remarks:       v.Remarks,
+			IsAdoption:    isAdoption,
+			IsLive:        v.IsLive,
+		})
+	}
+	return calvingCalfList
+}

+ 27 - 3
model/cow.go

@@ -17,6 +17,9 @@ type Cow struct {
 	PenId               int64                      `json:"pen_id"`
 	Lact                int32                      `json:"lact"`
 	DayAge              int32                      `json:"day_age"`
+	CalvingAge          int64                      `json:"calving_age"`
+	PregnancyAge        int64                      `json:"pregnancy_age"` // 怀孕天数 孕检结果有阳性更新,产犊后至0
+	AdmissionAge        int64                      `json:"admission_age"`
 	CowType             pasturePb.CowType_Kind     `json:"cow_type"`
 	BreedStatus         pasturePb.BreedStatus_Kind `json:"breed_status"`
 	CowKind             pasturePb.CowKind_Kind     `json:"cow_kind"`
@@ -28,7 +31,9 @@ type Cow struct {
 	IsRemove            pasturePb.IsShow_Kind      `json:"is_remove"`
 	IsPregnant          pasturePb.IsShow_Kind      `json:"is_pregnant"`
 	WeaningAt           int64                      `json:"weaning_at"`
+	CalvingAt           int64                      `json:"calving_at"`
 	BirthAt             int64                      `json:"birth_at"`
+	AdmissionAt         int64                      `json:"admission_at"`
 	FirstMatingAt       int64                      `json:"first_mating_at"`
 	LastEstrusAt        int64                      `json:"last_estrus_at"`
 	LastCalvingAt       int64                      `json:"last_calving_at"`
@@ -109,12 +114,23 @@ func (b BarCowStructSlice) ToPB(cowTypeMap map[pasturePb.CowType_Kind]string, co
 
 // GetDayAge 日龄
 func (c *Cow) GetDayAge() int32 {
+	if c.BirthAt <= 0 {
+		return 0
+	}
 	return int32(math.Floor(float64(time.Now().Unix()-c.BirthAt) / 86400))
 }
 
-// GetDaysPregnant 在胎天数
+// GetCalvingAge 产后天数
+func (c *Cow) GetCalvingAge() int64 {
+	if c.CalvingAt <= 0 {
+		return 0
+	}
+	return int64(math.Floor(float64(time.Now().Unix()-c.CalvingAt) / 86400))
+}
+
+// GetDaysPregnant 怀孕天数
 func (c *Cow) GetDaysPregnant() int32 {
-	if c.BreedStatus == pasturePb.BreedStatus_Pregnant {
+	if c.BreedStatus == pasturePb.BreedStatus_Pregnant && c.IsRemove == pasturePb.IsShow_No && c.IsPregnant == pasturePb.IsShow_Ok {
 		return int32(math.Floor(float64(time.Now().Unix()-c.LastMatingAt) / 86400))
 	}
 	return 0
@@ -122,12 +138,20 @@ func (c *Cow) GetDaysPregnant() int32 {
 
 // GetLactationDays 泌乳天数
 func (c *Cow) GetLactationDays() int32 {
-	if c.BreedStatus == pasturePb.BreedStatus_Calving {
+	if c.BreedStatus == pasturePb.BreedStatus_Calving && c.IsRemove == pasturePb.IsShow_Ok {
 		return int32(math.Floor(float64(time.Now().Unix()-c.LastCalvingAt) / 86400))
 	}
 	return 0
 }
 
+// GetAdmissionAge 入场天数
+func (c *Cow) GetAdmissionAge() int32 {
+	if c.AdmissionAt > 0 && c.IsRemove == pasturePb.IsShow_Ok {
+		return int32(math.Floor(float64(time.Now().Unix()-c.AdmissionAt) / 86400))
+	}
+	return 0
+}
+
 // GetDayWeight 日增重
 func (c *Cow) GetDayWeight() float64 {
 	if c.CurrentWeight-c.LastSecondWeight > 0 && c.LastWeightAt > c.LastSecondWeightAt {

+ 0 - 46
model/event_calving_calf.go

@@ -1,46 +0,0 @@
-package model
-
-import pasturePb "gitee.com/xuyiping_admin/go_proto/proto/go/backend/cow"
-
-type EventCalvingCalf struct {
-	Id         int64                  `json:"id"`
-	CalvingId  int64                  `json:"calving_id"`
-	CowId      int64                  `json:"cow_id"`
-	MotherId   int64                  `json:"mother_id"`
-	EarNumber  string                 `json:"ear_number"`
-	Sex        pasturePb.Genders_Kind `json:"sex"`
-	CowKind    pasturePb.CowKind_Kind `json:"cow_kind"`
-	Weight     int64                  `json:"weight"`
-	IsLive     pasturePb.IsShow_Kind  `json:"is_live"`
-	IsAdoption pasturePb.IsShow_Kind  `json:"is_adoption"`
-	PenId      int32                  `json:"pen_id"`
-	Remarks    string                 `json:"remarks"`
-	CreatedAt  int64                  `json:"created_at"`
-	UpdatedAt  int64                  `json:"updated_at"`
-}
-
-func (e *EventCalvingCalf) TableName() string {
-	return "event_calving_calf"
-}
-
-func NewEventCalvingCalf(motherId, calvingId int64, req *pasturePb.CalvingEventRequest) []*EventCalvingCalf {
-	calvingCalfList := make([]*EventCalvingCalf, 0)
-	for _, v := range req.CalfItemList {
-		isAdoption := v.IsAdoption
-		if v.IsLive == pasturePb.IsShow_No {
-			isAdoption = pasturePb.IsShow_No
-		}
-		calvingCalfList = append(calvingCalfList, &EventCalvingCalf{
-			EarNumber:  v.EarNumber,
-			CalvingId:  calvingId,
-			PenId:      v.PenId,
-			Weight:     int64(v.Weight * 100),
-			Sex:        v.Sex,
-			MotherId:   motherId,
-			Remarks:    v.Remarks,
-			IsAdoption: isAdoption,
-			IsLive:     v.IsLive,
-		})
-	}
-	return calvingCalfList
-}

+ 0 - 71
model/event_seme_time.go

@@ -1,71 +0,0 @@
-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_Ok,
-		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
-}

+ 0 - 73
model/event_seme_time_flow.go

@@ -1,73 +0,0 @@
-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, hoursStr := "0", "0"
-			if len(dayHours) > 0 && len(dayHours) == 1 {
-				dayStr = regexp.MustCompile(`\d+`).FindString(dayHours[0])
-			}
-			if len(dayHours) > 0 && len(dayHours) == 2 {
-				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),
-	}
-}

+ 1 - 1
model/immunization_plan.go

@@ -9,7 +9,7 @@ import (
 type ImmunizationPlan struct {
 	Id            int64                                 `json:"id"`
 	Name          string                                `json:"name"`
-	CowType       pasturePb.ImmunizationCowType_Kind    `json:"cow_type"`
+	CowType       pasturePb.CowType_Kind                `json:"cow_type"`
 	Conditions    pasturePb.ImmunizationConditions_Kind `json:"conditions"`
 	Value         int64                                 `json:"value"`
 	Value2        int64                                 `json:"value2"`

+ 79 - 0
model/same_time.go

@@ -0,0 +1,79 @@
+package model
+
+import (
+	"encoding/json"
+
+	pasturePb "gitee.com/xuyiping_admin/go_proto/proto/go/backend/cow"
+)
+
+type SameTime struct {
+	Id                  int64                  `json:"id"`
+	Name                string                 `json:"name"`
+	WeekType            pasturePb.Week_Kind    `json:"weekType"`
+	CowType             pasturePb.CowType_Kind `json:"cowType"`
+	IsShow              pasturePb.IsShow_Kind  `json:"isShow"`
+	IsDelete            pasturePb.IsShow_Kind  `json:"isDelete"`
+	PostpartumDaysStart int32                  `json:"postpartumDaysStart"`
+	PostpartumDaysEnd   int32                  `json:"postpartumDaysEnd"`
+	SameTimeDetails     string                 `json:"sameTimeDetails"`
+	Remarks             string                 `json:"remarks"`
+	OperationId         int64                  `json:"operationId"`
+	CreatedAt           int64                  `json:"createdAt"`
+	UpdatedAt           int64                  `json:"updatedAt"`
+}
+
+func (e *SameTime) TableName() string {
+	return "same_time"
+}
+
+func NewSameTime(currentUser *SystemUser, req *pasturePb.SearchSameTimeList) *SameTime {
+	sameTimeDetails, _ := json.Marshal(req.CollateNodes)
+	return &SameTime{
+		Name:                req.Name,
+		WeekType:            req.WeekType,
+		CowType:             req.CowType,
+		IsShow:              pasturePb.IsShow_Ok,
+		IsDelete:            pasturePb.IsShow_Ok,
+		PostpartumDaysStart: req.PostpartumDaysStart,
+		PostpartumDaysEnd:   req.PostpartumDaysEnd,
+		SameTimeDetails:     string(sameTimeDetails),
+		Remarks:             req.Remarks,
+		OperationId:         currentUser.Id,
+	}
+}
+
+type SameTimeSlice []*SameTime
+
+func (e SameTimeSlice) ToPB(
+	weekMap map[pasturePb.Week_Kind]string,
+	cowTypeMap map[pasturePb.CowType_Kind]string,
+	systemUserList []*SystemUser,
+) []*pasturePb.SearchSameTimeList {
+	res := make([]*pasturePb.SearchSameTimeList, len(e))
+	for i, v := range e {
+		operationName := ""
+		for _, u := range systemUserList {
+			if u.Id != v.OperationId {
+				continue
+			}
+			operationName = u.Name
+		}
+		res[i] = &pasturePb.SearchSameTimeList{
+			Id:                  int32(v.Id),
+			Name:                v.Name,
+			WeekType:            v.WeekType,
+			WeekName:            weekMap[v.WeekType],
+			CowType:             v.CowType,
+			CowTypeName:         cowTypeMap[v.CowType],
+			IsShow:              v.IsShow,
+			PostpartumDaysStart: v.PostpartumDaysStart,
+			PostpartumDaysEnd:   v.PostpartumDaysEnd,
+			Remarks:             v.Remarks,
+			OperationId:         int32(v.OperationId),
+			OperationName:       operationName,
+			CreatedAt:           int32(v.CreatedAt),
+			UpdatedAt:           int32(v.UpdatedAt),
+		}
+	}
+	return res
+}

+ 3 - 2
model/system_role.go

@@ -21,8 +21,9 @@ func (s *SystemRole) TableName() string {
 }
 
 const (
-	LayoutTime = "2006-01-02 15:04:05"
-	LayoutDate = "20060102"
+	LayoutTime  = "2006-01-02 15:04:05"
+	LayoutDate  = "20060102"
+	LayoutDate2 = "2006-01-02"
 )
 
 type SystemRoleSlice []*SystemRole

+ 30 - 0
model/work_order_calendar.go

@@ -0,0 +1,30 @@
+package model
+
+import (
+	"time"
+
+	pasturePb "gitee.com/xuyiping_admin/go_proto/proto/go/backend/cow"
+)
+
+type WorkOrderCalendar struct {
+	Id        int64                 `json:"id"`
+	Name      string                `json:"name"`
+	Count     int32                 `json:"count"`
+	ShowDay   string                `json:"show_day"`
+	IsShow    pasturePb.IsShow_Kind `json:"is_show"`
+	CreatedAt int64                 `json:"created_at"`
+	UpdatedAt int64                 `json:"updated_at"`
+}
+
+func (w *WorkOrderCalendar) TableName() string {
+	return "work_order_calendar"
+}
+
+func NewWorkOrderCalendar(name string, count int32) *WorkOrderCalendar {
+	return &WorkOrderCalendar{
+		Name:    name,
+		Count:   count,
+		ShowDay: time.Now().Format(LayoutDate2),
+		IsShow:  pasturePb.IsShow_Ok,
+	}
+}

+ 45 - 0
model/work_order_list.go

@@ -0,0 +1,45 @@
+package model
+
+import (
+	"kpt-pasture/util"
+	"time"
+
+	pasturePb "gitee.com/xuyiping_admin/go_proto/proto/go/backend/cow"
+)
+
+type WorkOrderList struct {
+	Id           int64                 `json:"id"`
+	CalendarId   int64                 `json:"calendarId"`
+	Name         string                `json:"name"`
+	CowId        int64                 `json:"cowId"`
+	StartAt      int64                 `json:"startAt"`
+	FinishAt     int64                 `json:"finishAt"`
+	EndAt        int64                 `json:"endAt"`
+	DisUserId    int64                 `json:"disUserId"`
+	ExecUserId   int64                 `json:"execUserId"`
+	IsCompletion pasturePb.IsShow_Kind `json:"isCompletion"`
+	IsShow       pasturePb.IsShow_Kind `json:"isShow"`
+	Remarks      string                `json:"remarks"`
+	CreatedAt    int64                 `json:"createdAt"`
+	UpdatedAt    int64                 `json:"updatedAt"`
+}
+
+func (w *WorkOrderList) TableName() string {
+	return "work_order_list"
+}
+
+func NewWorkOrderList(name string, calendarId, cowId int64) *WorkOrderList {
+	return &WorkOrderList{
+		CalendarId:   calendarId,
+		Name:         name,
+		CowId:        cowId,
+		StartAt:      util.TimeParseLocalUnix(time.Now().Format(LayoutDate2)),
+		FinishAt:     0,
+		EndAt:        0,
+		DisUserId:    0,
+		ExecUserId:   0,
+		IsCompletion: pasturePb.IsShow_No,
+		IsShow:       pasturePb.IsShow_Ok,
+		Remarks:      "",
+	}
+}

+ 3 - 2
model/work_order_master.go

@@ -2,6 +2,7 @@ package model
 
 import (
 	"encoding/json"
+	"fmt"
 	"kpt-pasture/http/util"
 	"strconv"
 	"strings"
@@ -105,12 +106,12 @@ func NewTaskWorkOrderOption(processAt int64) []asynq.Option {
 	}
 }
 
-func NewTaskWorkOrderPayload(id int64, execTime time.Duration) *asynq.Task {
+func NewTaskWorkOrderPayload(farmName string, id int64, execTime time.Duration) *asynq.Task {
 	payload, _ := json.Marshal(TaskWorkOrderPayload{
 		WorkOrderId: id,
 	})
 	processAt := time.Now().Add(execTime).Unix()
-	return asynq.NewTask(TaskWorkOrder, payload, NewTaskWorkOrderOption(processAt)...)
+	return asynq.NewTask(fmt.Sprintf("%s:%s", farmName, TaskWorkOrder), payload, NewTaskWorkOrderOption(processAt)...)
 }
 
 type WorkOrderMasterSlice []*WorkOrderMaster

+ 0 - 51
model/work_order_user.go

@@ -1,51 +0,0 @@
-package model
-
-import (
-	"kpt-pasture/util"
-	"strconv"
-	"strings"
-
-	pasturePb "gitee.com/xuyiping_admin/go_proto/proto/go/backend/cow"
-)
-
-type WorkOrderUser struct {
-	Id                int64                          `json:"id"`
-	WorkOrderMasterId int64                          `json:"WorkOrderMasterId"`
-	WorkOrderSubId    int64                          `json:"workOrderSubId"`
-	UserId            int64                          `json:"userId"`
-	StartTime         int64                          `json:"startTime"`
-	ExecTime          int64                          `json:"execTime"`
-	EndTime           int64                          `json:"endTime"`
-	Status            pasturePb.WorkOrderStatus_Kind `json:"status"`
-	IsShow            pasturePb.IsShow_Kind          `json:"isShow"`
-	Remarks           string                         `json:"remarks"`
-	CreatedAt         int64                          `json:"createdAt"`
-	UpdatedAt         int64                          `json:"updatedAt"`
-}
-
-func (w *WorkOrderUser) TableName() string {
-	return "work_order_user"
-}
-
-func NewWorkOrderUser(master *WorkOrderMaster, sub *WorkOrderSub) []*WorkOrderUser {
-	res := make([]*WorkOrderUser, 0)
-	userList := strings.Split(master.ExecPersons, ",")
-	if len(userList) <= 0 {
-		return res
-	}
-	startTime, _ := util.ConvertParseLocalUnix(master.ExecTime)
-	for _, userIdStr := range userList {
-		userId, _ := strconv.ParseInt(userIdStr, 10, 64)
-		res = append(res, &WorkOrderUser{
-			WorkOrderMasterId: master.Id,
-			WorkOrderSubId:    sub.Id,
-			UserId:            userId,
-			StartTime:         startTime,
-			Status:            pasturePb.WorkOrderStatus_Distribute,
-			IsShow:            pasturePb.IsShow_Ok,
-			Remarks:           "",
-		})
-	}
-
-	return res
-}

+ 41 - 11
module/backend/config_data.go

@@ -177,6 +177,28 @@ func (s *StoreEntry) SemeTimeCowTypeEnumList() []*pasturePb.ConfigOptionsList {
 		Value:    int32(pasturePb.CowType_Breeding_Calf),
 		Label:    "种母牛",
 		Disabled: true,
+	}, &pasturePb.ConfigOptionsList{
+		Value:    int32(pasturePb.BreedStatus_Empty),
+		Label:    "空怀",
+		Disabled: true,
+	})
+	return cowTypeList
+}
+
+func (s *StoreEntry) SameTimeTypeEnumList() []*pasturePb.ConfigOptionsList {
+	cowTypeList := make([]*pasturePb.ConfigOptionsList, 0)
+	cowTypeList = append(cowTypeList, &pasturePb.ConfigOptionsList{
+		Value:    int32(pasturePb.SameTimeType_PG),
+		Label:    "PG",
+		Disabled: true,
+	}, &pasturePb.ConfigOptionsList{
+		Value:    int32(pasturePb.SameTimeType_RnGH),
+		Label:    "RnGH",
+		Disabled: true,
+	}, &pasturePb.ConfigOptionsList{
+		Value:    int32(pasturePb.SameTimeType_TAI),
+		Label:    "TAI",
+		Disabled: true,
 	})
 	return cowTypeList
 }
@@ -184,21 +206,33 @@ func (s *StoreEntry) SemeTimeCowTypeEnumList() []*pasturePb.ConfigOptionsList {
 func (s *StoreEntry) ImmunizationCowTypeEnumList() []*pasturePb.ConfigOptionsList {
 	cowTypeList := make([]*pasturePb.ConfigOptionsList, 0)
 	cowTypeList = append(cowTypeList, &pasturePb.ConfigOptionsList{
-		Value:    int32(pasturePb.ImmunizationCowType_Reserve_Cattle),
+		Value:    int32(pasturePb.CowType_Lactating_Calf),
+		Label:    "犊牛",
+		Disabled: true,
+	}, &pasturePb.ConfigOptionsList{
+		Value:    int32(pasturePb.CowType_Weaned_Calf),
+		Label:    "育成牛",
+		Disabled: true,
+	}, &pasturePb.ConfigOptionsList{
+		Value:    int32(pasturePb.CowType_Youth_Calf),
+		Label:    "青年牛",
+		Disabled: true,
+	}, &pasturePb.ConfigOptionsList{
+		Value:    int32(pasturePb.CowType_Fattening_Calf),
+		Label:    "育肥牛",
+		Disabled: true,
+	}, &pasturePb.ConfigOptionsList{
+		Value:    int32(pasturePb.CowType_Reserve_Calf),
 		Label:    "后备牛",
 		Disabled: true,
 	}, &pasturePb.ConfigOptionsList{
-		Value:    int32(pasturePb.ImmunizationCowType_Adult_Cow),
+		Value:    int32(pasturePb.CowType_Reserve_Calf),
 		Label:    "种母牛",
 		Disabled: true,
 	}, &pasturePb.ConfigOptionsList{
-		Value:    int32(pasturePb.ImmunizationCowType_Adult_bulls),
+		Value:    int32(pasturePb.CowType_Breeding_Bull),
 		Label:    "种公牛",
 		Disabled: true,
-	}, &pasturePb.ConfigOptionsList{
-		Value:    int32(pasturePb.ImmunizationCowType_Calf),
-		Label:    "犊牛",
-		Disabled: true,
 	})
 	return cowTypeList
 }
@@ -217,10 +251,6 @@ func (s *StoreEntry) ImmunizationConditionsEnumList() []*pasturePb.ConfigOptions
 		Value:    int32(pasturePb.ImmunizationConditions_Days_Of_Pregnancy),
 		Label:    "怀孕天数",
 		Disabled: true,
-	}, &pasturePb.ConfigOptionsList{
-		Value:    int32(pasturePb.ImmunizationConditions_Month),
-		Label:    "每年月份",
-		Disabled: true,
 	}, &pasturePb.ConfigOptionsList{
 		Value:    int32(pasturePb.ImmunizationConditions_Admission_Days),
 		Label:    "入场天数",

+ 2 - 0
module/backend/enum_options.go

@@ -160,6 +160,8 @@ func (s *StoreEntry) SystemBaseConfigOptions(ctx context.Context, optionsName st
 		configOptions = s.MonthEnumList()
 	case "sameTimeCowType":
 		configOptions = s.SemeTimeCowTypeEnumList()
+	case "sameTimeType":
+		configOptions = s.SameTimeTypeEnumList()
 	case "immunizationCowType":
 		configOptions = s.ImmunizationCowTypeEnumList()
 	case "workOrderFrequency":

+ 2 - 2
module/backend/event.go

@@ -257,8 +257,8 @@ func (s *StoreEntry) CalvingList(ctx context.Context, req *pasturePb.SearchEvent
 	for _, v := range lavingList {
 		calvingIds = append(calvingIds, v.Id)
 	}
-	calvingCalfList := make([]*model.EventCalvingCalf, 0)
-	if err := s.DB.Model(new(model.EventCalvingCalf)).Where("calving_id IN ?", calvingIds).Find(&calvingCalfList).Error; err != nil {
+	calvingCalfList := make([]*model.CalvingCalf, 0)
+	if err := s.DB.Model(new(model.CalvingCalf)).Where("calving_id IN ?", calvingIds).Find(&calvingCalfList).Error; err != nil {
 		return nil, xerr.WithStack(err)
 	}
 

+ 2 - 3
module/backend/interface.go

@@ -111,9 +111,8 @@ type PastureManageService interface {
 	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)
+	CreateOrUpdateSemeTime(ctx context.Context, req *pasturePb.SearchSameTimeList) error
+	SearchSemeTimeList(ctx context.Context, req *pasturePb.SearchNameRequest, pagination *pasturePb.PaginationModel) (*pasturePb.SameTimeResponse, error)
 
 	SearchDiseaseTypeList(ctx context.Context, req *pasturePb.SearchNameRequest, pagination *pasturePb.PaginationModel) (*pasturePb.SearchBaseConfigResponse, error)
 	CreateOrUpdateDiseaseType(ctx context.Context, req *pasturePb.SearchBaseConfigList) error

+ 24 - 63
module/backend/prescription.go

@@ -14,38 +14,34 @@ import (
 	"gorm.io/gorm"
 )
 
-func (s *StoreEntry) CreateOrUpdateSemeTime(ctx context.Context, req *pasturePb.SemeTimeRequest) error {
+func (s *StoreEntry) CreateOrUpdateSemeTime(ctx context.Context, req *pasturePb.SearchSameTimeList) 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)
-			}
+	semeTime := model.NewSameTime(currentUser, req)
+	if req.Id > 0 {
+		if err := s.DB.Model(new(model.SameTime)).Where("id = ?", req.Id).Updates(map[string]interface{}{
+			"name":                  semeTime.Name,
+			"week_type":             semeTime.WeekType,
+			"cow_type":              semeTime.CowType,
+			"postpartum_days_start": semeTime.PostpartumDaysStart,
+			"postpartum_days_end":   semeTime.PostpartumDaysEnd,
+			"same_time_details":     semeTime.SameTimeDetails,
+			"is_show":               semeTime.IsShow,
+			"remarks":               semeTime.Remarks,
+		}).Error; err != nil {
+			return xerr.WithStack(err)
+		}
+	} else {
+		if err := s.DB.Create(&semeTime).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)
+func (s *StoreEntry) SearchSemeTimeList(ctx context.Context, req *pasturePb.SearchNameRequest, pagination *pasturePb.PaginationModel) (*pasturePb.SameTimeResponse, error) {
+	semeTimeList := make([]*model.SameTime, 0)
 	var count int64 = 0
-	pref := s.DB.Model(new(model.EventSemeTime)).
+	pref := s.DB.Model(new(model.SameTime)).
 		Where("is_delete = ?", pasturePb.IsShow_Ok)
 	if req.Name != "" {
 		pref.Where("name like ?", fmt.Sprintf("%s%s%s", "%", req.Name, "%"))
@@ -59,11 +55,11 @@ func (s *StoreEntry) SearchSemeTimeList(ctx context.Context, req *pasturePb.Sear
 	weekMap := s.WeekMap()
 	cowTypeMap := s.CowTypeMap()
 	systemUser, _ := s.SystemUserList(ctx)
-	return &pasturePb.SemeTimeEventResponse{
+	return &pasturePb.SameTimeResponse{
 		Code:    http.StatusOK,
 		Message: "ok",
-		Data: &pasturePb.SearchSemeTimeData{
-			List:     model.EventSemeTimeSlice(semeTimeList).ToPB(weekMap, cowTypeMap, systemUser),
+		Data: &pasturePb.SearchSameTimeData{
+			List:     model.SameTimeSlice(semeTimeList).ToPB(weekMap, cowTypeMap, systemUser),
 			Total:    int32(count),
 			PageSize: pagination.PageSize,
 			Page:     pagination.Page,
@@ -71,41 +67,6 @@ func (s *StoreEntry) SearchSemeTimeList(ctx context.Context, req *pasturePb.Sear
 	}, 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
-}
-
 func (s *StoreEntry) SearchDiseaseList(ctx context.Context, req *pasturePb.SearchDiseaseRequest, pagination *pasturePb.PaginationModel) (*pasturePb.SearchDiseaseResponse, error) {
 	diseaseList := make([]*model.Disease, 0)
 	var count int64 = 0

+ 5 - 5
module/backend/work.go

@@ -185,10 +185,8 @@ func (s *StoreEntry) WorkOrderSubUpdate(tx *gorm.DB, workOrderMaster *model.Work
 			Where("exec_time > ?", zeroTime).
 			Where("status = ?", pasturePb.WorkOrderStatus_Distribute).
 			Update("status", pasturePb.WorkOrderStatus_Canceled).Error; err != nil {
-			fmt.Println("=====007==========", err)
 			return xerr.WithStack(err)
 		}
-		fmt.Println("=====008==========")
 	}
 	return nil
 }
@@ -231,7 +229,7 @@ func (s *StoreEntry) SendAsynqWorkOrder(ctx context.Context, workOrder *model.Wo
 	}
 
 	execTime := time.Now().Unix() - timeUnix
-	if _, err := s.AsynqClient.CtxEnqueue(ctx, model.NewTaskWorkOrderPayload(workOrder.Id, time.Duration(execTime)*time.Second)); err != nil {
+	if _, err := s.AsynqClient.CtxEnqueue(ctx, model.NewTaskWorkOrderPayload(s.Cfg.FarmName, workOrder.Id, time.Duration(execTime)*time.Second)); err != nil {
 		zaplog.Error("PushMessage CtxEnqueue", zap.Any("Err", err))
 	}
 }
@@ -245,7 +243,6 @@ func (s *StoreEntry) UserWorkOrderList(ctx context.Context, workOrderStatus past
 
 	var count int64 = 0
 	userWorkOrderList := make([]*model.WorkOrderSub, 0)
-	//  SELECT * FROM `work_order_sub` WHERE is_show = 1 AND status = 2 And (exec_user_id = 2  OR FIND_IN_SET(2, set_user_ids) > 0) ORDER BY id desc LIMIT 20
 	pref := s.DB.Model(userWorkOrderList).
 		Where("is_show = ?", pasturePb.IsShow_Ok)
 
@@ -253,7 +250,10 @@ func (s *StoreEntry) UserWorkOrderList(ctx context.Context, workOrderStatus past
 		pref.Where("status = ?", workOrderStatus)
 	}
 
-	if err := pref.Where(s.DB.Where("exec_user_id = ?", currUser.Id).Or("FIND_IN_SET(?, set_user_ids) > 0", currUser.Id)).
+	if err := pref.Where(
+		s.DB.Where("exec_user_id = ?", currUser.Id).
+			Or("FIND_IN_SET(?, set_user_ids) > 0", currUser.Id),
+	).
 		Order("id desc").
 		Count(&count).
 		Limit(int(pagination.PageSize)).

+ 133 - 12
module/crontab/crontab.go

@@ -2,39 +2,45 @@ package crontab
 
 import (
 	"context"
-	"fmt"
 	"kpt-pasture/model"
 	"kpt-pasture/util"
 	"time"
 
+	"gorm.io/gorm"
+
+	"gitee.com/xuyiping_admin/pkg/xerr"
+
 	"gitee.com/xuyiping_admin/pkg/logger/zaplog"
 	"go.uber.org/zap"
 
 	pasturePb "gitee.com/xuyiping_admin/go_proto/proto/go/backend/cow"
 )
 
-func (e *Entry) demo() error {
-	fmt.Println("demo crontab ok")
-	return nil
-}
-
-// UpdateCowDayAge 更新牛的日龄
-func (e *Entry) UpdateCowDayAge() error {
+// UpdateCowInfo 牛只基本信息维护
+func (e *Entry) UpdateCowInfo() error {
 	cowList := make([]*model.Cow, 0)
 	if err := e.DB.Where("is_remove = ?", pasturePb.IsShow_Ok).Find(&cowList).Error; err != nil {
 		return err
 	}
 	for _, cow := range cowList {
 		dayAge := cow.GetDayAge()
-		if err := e.DB.Where("id = ?", cow.Id).Update("day_age", dayAge).Error; err != nil {
+		calvingAge := cow.GetCalvingAge()
+		pregnancyAge := cow.GetDaysPregnant()
+		admissionAge := cow.GetAdmissionAge()
+		if err := e.DB.Where("id = ?", cow.Id).Updates(map[string]interface{}{
+			"day_age":       dayAge,
+			"calving_at":    calvingAge,
+			"pregnancy_age": pregnancyAge,
+			"admission_age": admissionAge,
+		}).Error; err != nil {
 			zaplog.Error("Crontab", zap.Any("UpdateCowDayAge", err))
 		}
 	}
 	return nil
 }
 
-// GenerateWorkOrder 生成工作单
-func (e *Entry) GenerateWorkOrder() error {
+// GenerateAsynqWorkOrder 异步生成工作单
+func (e *Entry) GenerateAsynqWorkOrder() error {
 	workOrderList := make([]*model.WorkOrderMaster, 0)
 	if err := e.DB.Where("is_show = ?", pasturePb.IsShow_Ok).Find(&workOrderList).Error; err != nil {
 		return err
@@ -46,10 +52,125 @@ func (e *Entry) GenerateWorkOrder() error {
 			continue
 		}
 		execTime := time.Now().Unix() - timeUnix
-		if _, err := e.AsynqClient.CtxEnqueue(context.Background(), model.NewTaskWorkOrderPayload(workOrder.Id, time.Duration(execTime)*time.Second)); err != nil {
+		if _, err := e.AsynqClient.CtxEnqueue(
+			context.Background(),
+			model.NewTaskWorkOrderPayload(e.Cfg.FarmName, workOrder.Id, time.Duration(execTime)*time.Second),
+		); err != nil {
 			zaplog.Error("PushMessage CtxEnqueue", zap.Any("Err", err))
 		}
+	}
+	return nil
+}
 
+// ImmunizationPlan 免疫计划,生成工作单
+func (e *Entry) ImmunizationPlan() error {
+	planList := make([]*model.ImmunizationPlan, 0)
+	if err := e.DB.Where("is_show = ?", pasturePb.IsShow_Ok).Find(&planList).Error; err != nil {
+		return xerr.WithStack(err)
+	}
+
+	for _, plan := range planList {
+		cowList := make([]*model.Cow, 0)
+		pref := e.DB.Select("id").Where("is_remove = ?", pasturePb.IsShow_Ok).
+			Where("cow_type = ?", plan.CowType)
+
+		switch plan.Conditions {
+		case pasturePb.ImmunizationConditions_Days_Age:
+			pref.Where("day_age >= ?", plan.Value).
+				Where("day_age <= ?", plan.Value2)
+		case pasturePb.ImmunizationConditions_Days_After_Delivery:
+			pref.Where("calving_age >= ?", plan.Value).
+				Where("calving_age <= ?", plan.Value2)
+		case pasturePb.ImmunizationConditions_Days_Of_Pregnancy:
+			pref.Where("pregnancy_age >= ?", plan.Value).
+				Where("pregnancy_age <= ?", plan.Value2).
+				Where("is_pregnant = ?", pasturePb.IsShow_Ok)
+		case pasturePb.ImmunizationConditions_Month:
+			// todo 待实现月份
+		case pasturePb.ImmunizationConditions_Admission_Days:
+			pref.Where("admission_age >= ?", plan.Value).
+				Where("admission_age <= ?", plan.Value2)
+		}
+
+		if err := pref.Find(&cowList).Error; err != nil {
+			return xerr.WithStack(err)
+		}
+		if len(cowList) <= 0 {
+			continue
+		}
+
+		if err := e.GenerateCalendarByImmunization(cowList, plan.Name); err != nil {
+			zaplog.Error("crontab", zap.Any("GenerateWorkOrderCalendar", err), zap.Any("cowList", cowList))
+		}
 	}
 	return nil
 }
+
+// SameTimePlan 同期计划,生成工作单
+func (e *Entry) SameTimePlan() error {
+	return nil
+}
+
+func (e *Entry) GenerateCalendarByImmunization(cowList []*model.Cow, name string) error {
+	workOrderCalendarList := e.getWorkOrderCalendar(name)
+	newCowList := make([]*model.Cow, 0)
+	if len(workOrderCalendarList) > 0 {
+		// 过滤已经存在的牛只数据,避免重复生成工作单
+		calendarIds := make([]int64, 0)
+		if err := e.DB.Model(&model.WorkOrderCalendar{}).Select("id").
+			Where("name = ?", name).
+			Where("is_show = ?", pasturePb.IsShow_Ok).
+			Order("id DESC").
+			Limit(100). // todo 默认取100条数据
+			Find(&calendarIds).Error; err != nil {
+			return xerr.WithStack(err)
+		}
+
+		workOrderList := make([]*model.WorkOrderList, 0)
+		if err := e.DB.Where("calendar_id IN ?", calendarIds).
+			Where("is_show  = ?", pasturePb.IsShow_Ok).
+			Where("is_completion = ?", pasturePb.IsShow_No).
+			Find(&workOrderList).Error; err != nil {
+			return xerr.WithStack(err)
+		}
+
+		if len(workOrderList) > 0 {
+			for _, cow := range cowList {
+				for _, workOrder := range workOrderList {
+					if workOrder.CowId == cow.Id {
+						continue
+					}
+				}
+				newCowList = append(newCowList, cow)
+			}
+		}
+	}
+	count := len(newCowList)
+	if err := e.DB.Transaction(func(tx *gorm.DB) error {
+		newWorkOrderCalendar := model.NewWorkOrderCalendar(name, int32(count))
+		if err := tx.Create(newWorkOrderCalendar).Error; err != nil {
+			return xerr.WithStack(err)
+		}
+		newWorkOrderList := make([]*model.WorkOrderList, 0)
+		for _, cow := range newCowList {
+			newWorkOrderList = append(newWorkOrderList, model.NewWorkOrderList(name, newWorkOrderCalendar.Id, cow.Id))
+		}
+		if err := tx.Create(newWorkOrderList).Error; err != nil {
+			return xerr.WithStack(err)
+		}
+		return nil
+	}); err != nil {
+		return xerr.WithStack(err)
+	}
+	return nil
+}
+
+func (e *Entry) getWorkOrderCalendar(name string) []*model.WorkOrderCalendar {
+	res := make([]*model.WorkOrderCalendar, 0)
+	if err := e.DB.Where("name = ?", name).
+		Where("is_show = ?", pasturePb.IsShow_Ok).
+		Find(&res).Error; err != nil {
+		zaplog.Error("getWorkOrderCalendar", zap.Any("err", err))
+	}
+	return res
+}

+ 4 - 3
module/crontab/interface.go

@@ -27,7 +27,8 @@ func NewCrontab(entry Entry) Crontab {
 }
 
 type Crontab interface {
-	demo() error
-	UpdateCowDayAge() error
-	GenerateWorkOrder() error
+	UpdateCowInfo() error
+	GenerateAsynqWorkOrder() error
+	ImmunizationPlan() error
+	SameTimePlan() error
 }