Browse Source

crontab: test

ping 6 months ago
parent
commit
dbac8bdce4

+ 0 - 23
cmd/crontab.go

@@ -19,26 +19,3 @@ var CrontabCmd = &cobra.Command{
 		})
 	},
 }
-
-/*func init() {
-	// 每日零点更新牛只日龄脚本
-	cmd.Instant(JobCmd, "update:cowAge", UpdateCowDayAge, "更新牛只日龄")
-	cmd.Instant(JobCmd, "gen:cowAge", GenerateWorkOrder, "创建每天的工单")
-}
-
-func UpdateCowDayAge(ctx context.Context, args []string) error {
-	crontab := dep.DICrontabService()
-	if err := crontab.UpdateCowInfo(); err != nil {
-		xerr.ReportSentry(ctx, err)
-	}
-	return nil
-}
-
-func GenerateWorkOrder(ctx context.Context, args []string) error {
-	crontab := dep.DICrontabService()
-	if err := crontab.GenerateAsynqWorkOrder(); err != nil {
-		xerr.ReportSentry(ctx, err)
-	}
-	return nil
-}
-*/

+ 4 - 2
config/app.develop.yaml

@@ -35,5 +35,7 @@ side_work_setting:
       default: 5
 cron:
   crontab_start_run: false
-  update_cow_dayAge: "0 */1 * * * ?"
-  generate_work_order: "0 0 22 * * ?"
+  update_cow_dayAge: "0 * * * * ?"
+  generate_work_order: "0 0 22 * * ?"
+  immunization_plan: "0 * * * * ?"
+  same_Time_plan: "0 */1 * * * ?"

+ 2 - 0
config/app.go

@@ -51,6 +51,8 @@ type CronSetting struct {
 	// CRONTAB 表达式
 	UpdateCowDayAge   string `yaml:"update_cow_dayAge"`
 	GenerateWorkOrder string `yaml:"generate_work_order"`
+	ImmunizationPlan  string `yaml:"immunization_plan"`
+	SameTimePlan      string `yaml:"same_time_plan"`
 }
 
 type JwtTokenKeyConfig struct {

+ 2 - 0
config/app.test.yaml

@@ -36,3 +36,5 @@ cron:
   crontab_start_run: false
   update_cow_dayAge: "0 */1 * * * ?"
   generate_work_order: "0 0 22 * * ?"
+  immunization_plan: "0 * * * * ?"
+  same_Time_plan: "0 */1 * * * ?"

+ 10 - 0
dep/di_crontab.go

@@ -45,10 +45,20 @@ func EntryCrontab(dependency CrontabDependency) *cron.Crontab {
 	if err != nil {
 		panic(err)
 	}
+
 	err = newCrontab.Bind("GenerateWorkOrder", cs.GenerateWorkOrder, dependency.CrontabHub.GenerateAsynqWorkOrder)
 	if err != nil {
 		panic(err)
 	}
 
+	err = newCrontab.Bind("ImmunizationPlan", cs.ImmunizationPlan, dependency.CrontabHub.ImmunizationPlan)
+	if err != nil {
+		panic(err)
+	}
+
+	err = newCrontab.Bind("SameTimePlan", cs.SameTimePlan, dependency.CrontabHub.SameTimePlan)
+	if err != nil {
+		panic(err)
+	}
 	return newCrontab
 }

+ 3 - 2
go.mod

@@ -3,7 +3,7 @@ module kpt-pasture
 go 1.17
 
 require (
-	gitee.com/xuyiping_admin/go_proto v0.0.0-20240905100333-97e1bcf51bd3
+	gitee.com/xuyiping_admin/go_proto v0.0.0-20240906085821-e407cf34db4d
 	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
@@ -18,6 +18,7 @@ require (
 	github.com/golang/protobuf v1.5.4
 	github.com/hibiken/asynq v0.23.0
 	github.com/mitchellh/mapstructure v1.5.0
+	github.com/prometheus/client_golang v1.16.0
 	github.com/spf13/cobra v1.7.0
 	github.com/spf13/viper v1.16.0
 	github.com/stretchr/testify v1.8.4
@@ -70,12 +71,12 @@ require (
 	github.com/pelletier/go-toml/v2 v2.1.0 // indirect
 	github.com/pkg/errors v0.9.1 // indirect
 	github.com/pmezard/go-difflib v1.0.0 // indirect
-	github.com/prometheus/client_golang v1.16.0 // indirect
 	github.com/prometheus/client_model v0.3.0 // indirect
 	github.com/prometheus/common v0.42.0 // indirect
 	github.com/prometheus/procfs v0.10.1 // indirect
 	github.com/richardlehane/mscfb v1.0.4 // indirect
 	github.com/richardlehane/msoleps v1.0.3 // indirect
+	github.com/robfig/cron v1.2.0 // indirect
 	github.com/robfig/cron/v3 v3.0.1 // indirect
 	github.com/sirupsen/logrus v1.9.3 // indirect
 	github.com/spf13/afero v1.9.5 // indirect

+ 6 - 30
go.sum

@@ -36,38 +36,12 @@ cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RX
 cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0=
 cloud.google.com/go/storage v1.14.0/go.mod h1:GrKmX003DSIwi9o29oFT7YDnHYwZoctc3fOKtUw0Xmo=
 dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
-gitee.com/xuyiping_admin/go_proto v0.0.0-20240827035612-bee09217eaa1 h1:8J2NBwEMMcAc8qwSlC9cNrhVcvVVhkRh046XO9IVvZY=
-gitee.com/xuyiping_admin/go_proto v0.0.0-20240827035612-bee09217eaa1/go.mod h1:BKrFW6YLDectlQcQk3FYKBeXvjEiodAKJ5rq7O/QiPE=
-gitee.com/xuyiping_admin/go_proto v0.0.0-20240828092937-15bc6159f952 h1:OFehpdF2GliEB40foszA6VfKob5TE+YL2dqJefUl3MU=
-gitee.com/xuyiping_admin/go_proto v0.0.0-20240828092937-15bc6159f952/go.mod h1:BKrFW6YLDectlQcQk3FYKBeXvjEiodAKJ5rq7O/QiPE=
-gitee.com/xuyiping_admin/go_proto v0.0.0-20240829061040-237e8a327a2a h1:ppuGlLKWQp4GyH52Zslazul1oMRG61t1q3xD0yQKbwA=
-gitee.com/xuyiping_admin/go_proto v0.0.0-20240829061040-237e8a327a2a/go.mod h1:BKrFW6YLDectlQcQk3FYKBeXvjEiodAKJ5rq7O/QiPE=
-gitee.com/xuyiping_admin/go_proto v0.0.0-20240830073743-52b13a51bc4d h1:t78Yqy/oBGUvkYKNh4DAS4MmYBd9ey9RIdI8ghd4/wg=
-gitee.com/xuyiping_admin/go_proto v0.0.0-20240830073743-52b13a51bc4d/go.mod h1:BKrFW6YLDectlQcQk3FYKBeXvjEiodAKJ5rq7O/QiPE=
-gitee.com/xuyiping_admin/go_proto v0.0.0-20240902063446-64deca9b4509 h1:iK8ZHFwsFQ8iyLHzSzrLRgYZlJOLHGiIJPyogZAtS8k=
-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/go_proto v0.0.0-20240904074804-21d4cb43dda8 h1:QYpogX3+y6/W8WIv/QoqUg/x69MIuWgEkNturvioV4c=
-gitee.com/xuyiping_admin/go_proto v0.0.0-20240904074804-21d4cb43dda8/go.mod h1:BKrFW6YLDectlQcQk3FYKBeXvjEiodAKJ5rq7O/QiPE=
-gitee.com/xuyiping_admin/go_proto v0.0.0-20240905015138-86162dede551 h1:2VOWlximGeR3X2llS4vSvE08YDsAonuRTElTowiD0rA=
-gitee.com/xuyiping_admin/go_proto v0.0.0-20240905015138-86162dede551/go.mod h1:BKrFW6YLDectlQcQk3FYKBeXvjEiodAKJ5rq7O/QiPE=
-gitee.com/xuyiping_admin/go_proto v0.0.0-20240905023855-2b00583fae76 h1:ErVpREHmwqZLQI9ILsJtqtM25k8Yi2CqbiswjlIVa8A=
-gitee.com/xuyiping_admin/go_proto v0.0.0-20240905023855-2b00583fae76/go.mod h1:BKrFW6YLDectlQcQk3FYKBeXvjEiodAKJ5rq7O/QiPE=
-gitee.com/xuyiping_admin/go_proto v0.0.0-20240905024709-477a20f21d1e h1:YnUaLh7OfaRdp75etn+8iz18U7mArP4BKFw7ReTrPqo=
-gitee.com/xuyiping_admin/go_proto v0.0.0-20240905024709-477a20f21d1e/go.mod h1:BKrFW6YLDectlQcQk3FYKBeXvjEiodAKJ5rq7O/QiPE=
-gitee.com/xuyiping_admin/go_proto v0.0.0-20240905070443-31141edfb80d h1:YXjVQZuGK1J36FKPYh0rYxIyjm79tOnikkYG68O1rck=
-gitee.com/xuyiping_admin/go_proto v0.0.0-20240905070443-31141edfb80d/go.mod h1:BKrFW6YLDectlQcQk3FYKBeXvjEiodAKJ5rq7O/QiPE=
 gitee.com/xuyiping_admin/go_proto v0.0.0-20240905100333-97e1bcf51bd3 h1:hNO95yUSYiSGfDKRper22iC+UdZFlgfLuwPigAy1WM0=
 gitee.com/xuyiping_admin/go_proto v0.0.0-20240905100333-97e1bcf51bd3/go.mod h1:BKrFW6YLDectlQcQk3FYKBeXvjEiodAKJ5rq7O/QiPE=
+gitee.com/xuyiping_admin/go_proto v0.0.0-20240906022358-d28cc0317fcc h1:q5QHNfCRLFiQnWReEweKch01/2wopT03QBlRN4DQT/4=
+gitee.com/xuyiping_admin/go_proto v0.0.0-20240906022358-d28cc0317fcc/go.mod h1:BKrFW6YLDectlQcQk3FYKBeXvjEiodAKJ5rq7O/QiPE=
+gitee.com/xuyiping_admin/go_proto v0.0.0-20240906085821-e407cf34db4d h1:vUz+UjLU0FgtH0EWdm2B7DWImbY/KF5R0DaCRY1B5Ao=
+gitee.com/xuyiping_admin/go_proto v0.0.0-20240906085821-e407cf34db4d/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=
@@ -523,6 +497,8 @@ github.com/richardlehane/mscfb v1.0.4/go.mod h1:YzVpcZg9czvAuhk9T+a3avCpcFPMUWm7
 github.com/richardlehane/msoleps v1.0.1/go.mod h1:BWev5JBpU9Ko2WAgmZEuiz4/u3ZYTKbjLycmwiWUfWg=
 github.com/richardlehane/msoleps v1.0.3 h1:aznSZzrwYRl3rLKRT3gUk9am7T/mLNSnJINvN0AQoVM=
 github.com/richardlehane/msoleps v1.0.3/go.mod h1:BWev5JBpU9Ko2WAgmZEuiz4/u3ZYTKbjLycmwiWUfWg=
+github.com/robfig/cron v1.2.0 h1:ZjScXvvxeQ63Dbyxy76Fj3AT3Ut0aKsyd2/tl3DTMuQ=
+github.com/robfig/cron v1.2.0/go.mod h1:JGuDeoQd7Z6yL4zQhZ3OPEVHB7fL6Ka6skscFHfmt2k=
 github.com/robfig/cron/v3 v3.0.1 h1:WdRxkvbJztn8LMz/QEvLN5sBU+xKpSqwwUO1Pjr4qDs=
 github.com/robfig/cron/v3 v3.0.1/go.mod h1:eQICP3HwyT7UooqI/z+Ov+PtYAWygg1TEWWzGIFLtro=
 github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg=

+ 40 - 0
model/cow.go

@@ -52,6 +52,46 @@ func (c *Cow) TableName() string {
 	return "cow"
 }
 
+type CowSlice []*Cow
+
+func (c CowSlice) ToPB(
+	penList []*Pen,
+	cowTypeMap map[pasturePb.CowType_Kind]string,
+	breedStatusMap map[pasturePb.BreedStatus_Kind]string,
+	cowKindMap map[pasturePb.CowKind_Kind]string,
+) []*pasturePb.SearchCowList {
+	res := make([]*pasturePb.SearchCowList, len(c))
+	for i, v := range c {
+		penName := ""
+		for _, pen := range penList {
+			if v.PenId != int64(pen.Id) {
+				continue
+			}
+			penName = pen.Name
+		}
+		res[i] = &pasturePb.SearchCowList{
+			CowId:           int32(v.Id),
+			Sex:             v.Sex,
+			NeckRingNumber:  v.NeckRingNumber,
+			EarNumber:       v.EarNumber,
+			PenId:           int32(v.PenId),
+			PenName:         penName,
+			CowType:         int32(v.CowType),
+			Lact:            v.Lact,
+			CowTypeName:     cowTypeMap[v.CowType],
+			BreedStatus:     v.BreedStatus,
+			BreedStatusName: breedStatusMap[v.BreedStatus],
+			CowKind:         v.CowKind,
+			CowKindName:     cowKindMap[v.CowKind],
+			BirthAt:         int32(v.BirthAt),
+			BirthWeight:     int32(v.BirthWeight) / 1000,
+			CurrentWeight:   int32(v.CurrentWeight) / 1000,
+			LastWeightAt:    int32(v.LastWeightAt),
+		}
+	}
+	return res
+}
+
 func NewCow(req *pasturePb.SearchEnterData) *Cow {
 	var isPregnant = pasturePb.IsShow_No
 	if req.BreedStatusId == pasturePb.BreedStatus_Pregnant {

+ 14 - 12
model/work_order_calendar.go

@@ -7,24 +7,26 @@ import (
 )
 
 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"`
+	Id           int64                       `json:"id"`
+	Name         string                      `json:"name"`
+	CalendarType pasturePb.CalendarType_Kind `json:"calendarType"`
+	Count        int32                       `json:"count"`
+	ShowDay      string                      `json:"showDay"`
+	IsShow       pasturePb.IsShow_Kind       `json:"isShow"`
+	CreatedAt    int64                       `json:"createdAt"`
+	UpdatedAt    int64                       `json:"updatedAt"`
 }
 
 func (w *WorkOrderCalendar) TableName() string {
 	return "work_order_calendar"
 }
 
-func NewWorkOrderCalendar(name string, count int32) *WorkOrderCalendar {
+func NewWorkOrderCalendar(name string, calendarType pasturePb.CalendarType_Kind, count int32) *WorkOrderCalendar {
 	return &WorkOrderCalendar{
-		Name:    name,
-		Count:   count,
-		ShowDay: time.Now().Format(LayoutDate2),
-		IsShow:  pasturePb.IsShow_Ok,
+		Name:         name,
+		Count:        count,
+		CalendarType: calendarType,
+		ShowDay:      time.Now().Format(LayoutDate2),
+		IsShow:       pasturePb.IsShow_Ok,
 	}
 }

+ 48 - 2
module/backend/config_data.go

@@ -193,11 +193,11 @@ func (s *StoreEntry) SameTimeTypeEnumList() []*pasturePb.ConfigOptionsList {
 		Disabled: true,
 	}, &pasturePb.ConfigOptionsList{
 		Value:    int32(pasturePb.SameTimeType_RnGH),
-		Label:    "RnGh",
+		Label:    "RnGH",
 		Disabled: true,
 	}, &pasturePb.ConfigOptionsList{
 		Value:    int32(pasturePb.SameTimeType_TAI),
-		Label:    "TAI",
+		Label:    "TAI输精",
 		Disabled: true,
 	})
 	return cowTypeList
@@ -715,3 +715,49 @@ func (s *StoreEntry) WorkOrderCategoryEnumList() []*pasturePb.ConfigOptionsList
 	})
 	return configOptions
 }
+
+func CalendarTypeEnumList() []*pasturePb.ConfigOptionsList {
+	configOptions := make([]*pasturePb.ConfigOptionsList, 0)
+	configOptions = append(configOptions, &pasturePb.ConfigOptionsList{
+		Value:    int32(pasturePb.CalendarType_Immunisation),
+		Label:    "免疫",
+		Disabled: true,
+	}, &pasturePb.ConfigOptionsList{
+		Value:    int32(pasturePb.CalendarType_PG),
+		Label:    "PG",
+		Disabled: true,
+	}, &pasturePb.ConfigOptionsList{
+		Value:    int32(pasturePb.CalendarType_RnGH),
+		Label:    "RnGH",
+		Disabled: true,
+	}, &pasturePb.ConfigOptionsList{
+		Value:    int32(pasturePb.CalendarType_First_Pregnancy_Check),
+		Label:    "孕检-初检",
+		Disabled: true,
+	}, &pasturePb.ConfigOptionsList{
+		Value:    int32(pasturePb.CalendarType_Second_Pregnancy_Check),
+		Label:    "孕检-二检",
+		Disabled: true,
+	}, &pasturePb.ConfigOptionsList{
+		Value:    int32(pasturePb.CalendarType_Third_Pregnancy_Check),
+		Label:    "孕检-三检",
+		Disabled: true,
+	}, &pasturePb.ConfigOptionsList{
+		Value:    int32(pasturePb.CalendarType_Fourth_Pregnancy_Check),
+		Label:    "孕检-四检",
+		Disabled: true,
+	}, &pasturePb.ConfigOptionsList{
+		Value:    int32(pasturePb.CalendarType_WorkOrder),
+		Label:    "工单",
+		Disabled: true,
+	}, &pasturePb.ConfigOptionsList{
+		Value:    int32(pasturePb.CalendarType_Weaning),
+		Label:    "断奶",
+		Disabled: true,
+	}, &pasturePb.ConfigOptionsList{
+		Value:    int32(pasturePb.CalendarType_Treatment),
+		Label:    "治疗",
+		Disabled: true,
+	})
+	return configOptions
+}

+ 9 - 14
module/backend/cow.go

@@ -2,7 +2,6 @@ package backend
 
 import (
 	"context"
-	"fmt"
 	"kpt-pasture/model"
 	"net/http"
 	"strings"
@@ -13,35 +12,31 @@ import (
 )
 
 func (s *StoreEntry) CowList(ctx context.Context, req *pasturePb.SearchEventRequest, pagination *pasturePb.PaginationModel) (*pasturePb.SearchCowListResponse, error) {
-	cowList := make([]*pasturePb.SearchCowList, 0)
+	cowList := make([]*model.Cow, 0)
 	var count int64 = 0
-	pref := s.DB.Table(fmt.Sprintf("%s as a", new(model.Cow).TableName())).
-		Select(`a.id as cow_id,a.sex,a.neck_ring_number,a.ear_number,a.pen_id,a.lact,a.type_id as cow_type_id,a.breed_status_id,
-			a.status as status_id,a.kind_id as cow_kind_id,a.birth_at,b.name as breed_status_name,d.name as cow_type_name,
-			e.name as cow_kind_name,f.name as pen_name,g.name as status_name`).
-		Joins(fmt.Sprintf("JOIN %s AS b ON a.breed_status_id = b.id", new(model.ConfigBreedStatus).TableName())).
-		Joins(fmt.Sprintf("JOIN %s AS d on a.type_id = d.id", new(model.ConfigCowType).TableName())).
-		Joins(fmt.Sprintf("JOIN %s AS e ON a.kind_id = e.id", new(model.ConfigCowKind).TableName())).
-		Joins(fmt.Sprintf("JOIN %s AS f on a.pen_id = f.id", new(model.Pen).TableName())).
-		Joins(fmt.Sprintf("JOIN %s AS g on a.status = g.id", new(model.ConfigCowStatus).TableName()))
+	pref := s.DB.Model(&model.Cow{}).Where("is_remove = ?", pasturePb.IsShow_Ok)
 
 	if len(req.CowId) > 0 {
 		cowIds := strings.Split(req.CowId, ",")
-		pref.Where("a.id IN ?", cowIds)
+		pref.Where("id IN ?", cowIds)
 	}
 
-	if err := pref.Order("a.id desc").
+	if err := pref.Order("id desc").
 		Count(&count).Limit(int(pagination.PageSize)).
 		Offset(int(pagination.PageOffset)).
 		Find(&cowList).Error; err != nil {
 		return nil, xerr.WithStack(err)
 	}
 
+	penList, _ := s.GetPenList(ctx)
+	cowTypeMap := s.CowTypeMap()
+	breedStatusMap := s.CowBreedStatusMap()
+	cowKindMap := s.CowKindMap()
 	return &pasturePb.SearchCowListResponse{
 		Code:    http.StatusOK,
 		Message: "ok",
 		Data: &pasturePb.SearchCowData{
-			List:     cowList,
+			List:     model.CowSlice(cowList).ToPB(penList, cowTypeMap, breedStatusMap, cowKindMap),
 			Total:    int32(count),
 			PageSize: pagination.PageSize,
 			Page:     pagination.Page,

+ 16 - 0
module/backend/enum_map.go

@@ -66,6 +66,14 @@ func (s *StoreEntry) CowTypeMap() map[pasturePb.CowType_Kind]string {
 	return res
 }
 
+func (s *StoreEntry) CowBreedStatusMap() map[pasturePb.BreedStatus_Kind]string {
+	res := make(map[pasturePb.BreedStatus_Kind]string)
+	for _, v := range s.BreedStatusEnumList() {
+		res[pasturePb.BreedStatus_Kind(v.Value)] = v.Label
+	}
+	return res
+}
+
 func (s *StoreEntry) CowKindMap() map[pasturePb.CowKind_Kind]string {
 	res := make(map[pasturePb.CowKind_Kind]string)
 	for _, v := range s.CowKindEnumList() {
@@ -105,3 +113,11 @@ func (s *StoreEntry) WorkOrderPriorityMap() map[pasturePb.Priority_Kind]string {
 	}
 	return res
 }
+
+func CalendarTypeMap() map[pasturePb.CalendarType_Kind]string {
+	res := make(map[pasturePb.CalendarType_Kind]string)
+	for _, v := range CalendarTypeEnumList() {
+		res[pasturePb.CalendarType_Kind(v.Value)] = v.Label
+	}
+	return res
+}

+ 90 - 52
module/crontab/crontab.go

@@ -3,7 +3,9 @@ package crontab
 import (
 	"context"
 	"errors"
+	"fmt"
 	"kpt-pasture/model"
+	"kpt-pasture/module/backend"
 	"kpt-pasture/util"
 	"time"
 
@@ -28,7 +30,7 @@ func (e *Entry) UpdateCowInfo() error {
 		calvingAge := cow.GetCalvingAge()
 		pregnancyAge := cow.GetDaysPregnant()
 		admissionAge := cow.GetAdmissionAge()
-		if err := e.DB.Where("id = ?", cow.Id).Updates(map[string]interface{}{
+		if err := e.DB.Model(new(model.Cow)).Where("id = ?", cow.Id).Updates(map[string]interface{}{
 			"day_age":       dayAge,
 			"calving_at":    calvingAge,
 			"pregnancy_age": pregnancyAge,
@@ -101,7 +103,7 @@ func (e *Entry) ImmunizationPlan() error {
 			continue
 		}
 
-		if err := e.GenerateCalendarByImmunization(cowList, plan.Name); err != nil {
+		if err := e.GenerateCalendarByImmunization(cowList, plan); err != nil {
 			zaplog.Error("crontab", zap.Any("GenerateWorkOrderCalendar", err), zap.Any("cowList", cowList))
 		}
 	}
@@ -115,34 +117,20 @@ func (e *Entry) SameTimePlan() error {
 		return xerr.WithStack(err)
 	}
 
-	cowSameTimeList := make([]*model.SameTimeCow, 0)
-	if err := e.DB.Select("cow_id").Where("status = ?", pasturePb.IsShow_Ok).Find(&cowSameTimeList).Error; err != nil {
-		if !errors.Is(err, gorm.ErrRecordNotFound) {
-			return xerr.WithStack(err)
-		}
-	}
-	cowIds := make([]int64, 0)
-	if len(cowSameTimeList) > 0 {
-		for _, cowSameTime := range cowSameTimeList {
-			cowIds = append(cowIds, cowSameTime.CowId)
-		}
-	}
+	pref := e.DB.Select("id").
+		Where("is_remove = ?", pasturePb.IsShow_Ok).
+		Where("is_pregnant = ?", pasturePb.IsShow_No)
 
 	for _, plan := range sameTimeList {
 		cowList := make([]*model.Cow, 0)
-		pref := e.DB.Select("id").
-			Where("is_remove = ?", pasturePb.IsShow_Ok).
-			Where("is_pregnant = ?", pasturePb.IsShow_No).
-			Where("calving_age >= ?", plan.PostpartumDaysStart).
-			Where("calving_age <= ?", plan.PostpartumDaysEnd)
-		if len(cowIds) > 0 {
-			pref.Where("id NOT IN ?", cowIds)
-		}
 
-		if err := pref.Find(&cowList); err != nil {
+		pref.Where("calving_age >= ?", plan.PostpartumDaysStart).
+			Where("calving_age <= ?", plan.PostpartumDaysEnd)
+		if err := pref.Find(&cowList).Error; err != nil {
 			zaplog.Error("crontab", zap.Any("SameTimePlan", err), zap.Any("plan", plan))
-			continue
+			return xerr.WithStack(err)
 		}
+
 		if len(cowList) <= 0 {
 			continue
 		}
@@ -154,14 +142,82 @@ func (e *Entry) SameTimePlan() error {
 	return nil
 }
 
-func (e *Entry) GenerateCalendarByImmunization(cowList []*model.Cow, name string) error {
-	workOrderCalendarList := e.getWorkOrderCalendar(name)
+// GenerateCalendarBySameTimePlan 生成同期计划工作单
+func (e *Entry) GenerateCalendarBySameTimePlan(cowList []*model.Cow, sameTime *model.SameTime) error {
+	if len(cowList) <= 0 {
+		return nil
+	}
+
+	cowSameTimeList := make([]*model.SameTimeCow, 0)
+	for _, cow := range cowList {
+		newCowSameTime, err := e.createNewCowSameTime(cow, sameTime)
+		if err != nil {
+			zaplog.Error("crontab", zap.Any("GenerateCalendarBySameTimePlan", err), zap.Any("cow", cow))
+		}
+		cowSameTimeList = append(cowSameTimeList, newCowSameTime)
+	}
+
+	if len(cowSameTimeList) <= 0 {
+		return nil
+	}
+
+	workOrderCalendarList := make([]*model.WorkOrderCalendar, 0)
+	calendarMap := backend.CalendarTypeMap()
+	workOrderCalendarList = append(workOrderCalendarList,
+		model.NewWorkOrderCalendar(
+			calendarMap[pasturePb.CalendarType_Immunisation],
+			pasturePb.CalendarType_Immunisation,
+			int32(len(cowSameTimeList)),
+		))
+	if len(sameTime.CollateNodes) > 0 {
+		for _, collateNode := range sameTime.CollateNodes {
+			workOrderCalendarList = append(workOrderCalendarList, nil)
+			// todo 待实现待实现待实现
+			fmt.Println("==collateNode==", collateNode)
+		}
+	}
+
+	if err := e.DB.Transaction(func(tx *gorm.DB) error {
+		// 创建新的牛只同期计划详情
+		if err := tx.Create(cowSameTimeList).Error; err != nil {
+			return xerr.WithStack(err)
+		}
+
+		return nil
+	}); err != nil {
+		return xerr.WithStack(err)
+	}
+
+	return nil
+}
+
+func (e *Entry) createNewCowSameTime(cow *model.Cow, sameTime *model.SameTime) (*model.SameTimeCow, error) {
+	cowSameTime := &model.SameTimeCow{}
+	if err := e.DB.Where("cow_id = ?", cow.Id).Where("lact = ?", cow.Lact).First(cowSameTime).Error; err != nil {
+		if errors.Is(err, gorm.ErrRecordNotFound) {
+			return &model.SameTimeCow{
+				CowId:      cow.Id,
+				SameTimeId: sameTime.Id,
+				Status:     pasturePb.IsShow_Ok,
+				StartAt:    time.Now().Unix(),
+				EndAt:      0,
+			}, nil
+		} else {
+			zaplog.Error("crontab", zap.Error(err), zap.Any("GenerateCalendarBySameTimePlan", "error"), zap.Any("cow", cow.Id), zap.Any("lact", cow.Lact))
+			return nil, xerr.WithStack(err)
+		}
+	}
+	return cowSameTime, nil
+}
+
+func (e *Entry) GenerateCalendarByImmunization(cowList []*model.Cow, plan *model.ImmunizationPlan) error {
+	workOrderCalendarList := e.getWorkOrderCalendar(plan.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("name = ?", plan.Name).
 			Where("is_show = ?", pasturePb.IsShow_Ok).
 			Order("id DESC").
 			Limit(100). // todo 默认取100条数据
@@ -190,13 +246,18 @@ func (e *Entry) GenerateCalendarByImmunization(cowList []*model.Cow, name string
 	}
 	count := len(newCowList)
 	if err := e.DB.Transaction(func(tx *gorm.DB) error {
-		newWorkOrderCalendar := model.NewWorkOrderCalendar(name, int32(count))
+		calendarTypeMap := backend.CalendarTypeMap()
+		newWorkOrderCalendar := model.NewWorkOrderCalendar(
+			calendarTypeMap[pasturePb.CalendarType_Immunisation],
+			pasturePb.CalendarType_Immunisation,
+			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))
+			newWorkOrderList = append(newWorkOrderList, model.NewWorkOrderList(plan.Name, newWorkOrderCalendar.Id, cow.Id))
 		}
 		if err := tx.Create(newWorkOrderList).Error; err != nil {
 			return xerr.WithStack(err)
@@ -208,29 +269,6 @@ func (e *Entry) GenerateCalendarByImmunization(cowList []*model.Cow, name string
 	return nil
 }
 
-func (e *Entry) GenerateCalendarBySameTimePlan(cowList []*model.Cow, sameTime *model.SameTime) error {
-	if len(cowList) <= 0 {
-		return nil
-	}
-
-	cowSameTimeList := make([]*model.SameTimeCow, 0)
-	for _, cow := range cowList {
-		cowSameTimeList = append(cowSameTimeList, &model.SameTimeCow{
-			CowId:      cow.Id,
-			SameTimeId: sameTime.Id,
-			Status:     pasturePb.IsShow_Ok,
-			StartAt:    time.Now().Unix(),
-			EndAt:      0,
-		})
-	}
-
-	if err := e.DB.Create(cowSameTimeList).Error; 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).

+ 1 - 0
module/crontab/interface.go

@@ -20,6 +20,7 @@ type Entry struct {
 	DB          *kptstore.DB
 	AsynqClient asynqsvc.Client
 	Redis       *redis.CacheStoreRedisEntry
+	//BackendEntry *backend.StoreEntry
 }
 
 func NewCrontab(entry Entry) Crontab {