Kaynağa Gözat

Merge branch 'develop' of xuyiping/kpt-pasture into master

xuyiping 22 saat önce
ebeveyn
işleme
de8450f89c
100 değiştirilmiş dosya ile 7533 ekleme ve 1207 silme
  1. 10 12
      .drone.yml
  2. 10 7
      Dockerfile
  3. 22 1
      README.md
  4. 2 15
      cmd/consumer.go
  5. 25 0
      cmd/milk.go
  6. 15 0
      cmd/mqtt.go
  7. 3 3
      cmd/root.go
  8. 55 10
      config/app.develop.yaml
  9. 66 8
      config/app.go
  10. 36 16
      config/app.test.yaml
  11. 6 2
      dep/dep.go
  12. 1 0
      dep/di_alert.go
  13. 13 9
      dep/di_asynq.go
  14. 88 7
      dep/di_crontab.go
  15. 34 0
      dep/di_mqtt.go
  16. 25 10
      docker-compose.yml
  17. 20 11
      go.mod
  18. 163 31
      go.sum
  19. 388 2
      http/handler/analysis/analysis.go
  20. 46 8
      http/handler/config/config.go
  21. 188 1
      http/handler/cow/cow.go
  22. 0 18
      http/handler/dashboard/bar.go
  23. 126 0
      http/handler/dashboard/dashboard.go
  24. 168 17
      http/handler/event/event_base.go
  25. 275 43
      http/handler/event/event_breed.go
  26. 86 61
      http/handler/event/event_health.go
  27. 0 1
      http/handler/event/event_vet.go
  28. 60 0
      http/handler/goods/drugs.go
  29. 178 0
      http/handler/goods/outbound.go
  30. 9 8
      http/handler/milk/milk.go
  31. 429 0
      http/handler/pasture/pasture.go
  32. 0 98
      http/handler/pasture/seme_time.go
  33. 15 1
      http/handler/system/dept.go
  34. 6 57
      http/handler/system/menu.go
  35. 31 2
      http/handler/system/user.go
  36. 171 0
      http/handler/test.go
  37. 212 27
      http/handler/upload/upload.go
  38. 131 0
      http/handler/warning/warning.go
  39. 104 0
      http/handler/work/calendar.go
  40. 132 0
      http/handler/work/item.go
  41. 17 1
      http/middleware/auth.go
  42. 5 0
      http/middleware/bus.go
  43. 1 34
      http/middleware/cors.go
  44. 0 11
      http/middleware/hub.go
  45. 16 48
      http/middleware/log.go
  46. 58 0
      http/middleware/rate_limit.go
  47. 22 3
      http/route/analysis_api.go
  48. 18 14
      http/route/config_api.go
  49. 12 2
      http/route/cow_api.go
  50. 0 17
      http/route/dashboard.go
  51. 25 0
      http/route/dashboard_api.go
  52. 41 10
      http/route/event_api.go
  53. 4 0
      http/route/files_api.go
  54. 16 0
      http/route/goods_api.go
  55. 18 0
      http/route/milk_api.go
  56. 12 2
      http/route/pasture_api.go
  57. 1 3
      http/route/root.go
  58. 3 0
      http/route/route.go
  59. 11 9
      http/route/system_api.go
  60. 26 0
      http/route/test_api.go
  61. 25 0
      http/route/warning_api.go
  62. 39 0
      http/route/work_api.go
  63. 0 21
      http/route/work_order.go
  64. 2 6
      main.go
  65. 47 0
      model/app_pasture_list.go
  66. 13 0
      model/app_pasture_receiver.go
  67. 91 0
      model/calendar.go
  68. 42 22
      model/calving_calf.go
  69. 15 1
      model/config_disease_type.go
  70. 771 141
      model/cow.go
  71. 90 0
      model/cow_calving.go
  72. 37 0
      model/cow_lact.go
  73. 39 0
      model/cow_pregnant.go
  74. 0 38
      model/cow_weight.go
  75. 22 0
      model/crontab_log.go
  76. 96 0
      model/data_notice.go
  77. 252 0
      model/data_waring.go
  78. 199 0
      model/data_warning_items.go
  79. 14 12
      model/disease.go
  80. 31 24
      model/drugs.go
  81. 84 0
      model/event_abortion.go
  82. 17 11
      model/event_body_score.go
  83. 122 48
      model/event_calving.go
  84. 158 0
      model/event_cow_disease.go
  85. 112 0
      model/event_cow_log.go
  86. 175 0
      model/event_cow_same_time.go
  87. 114 0
      model/event_cow_treatment.go
  88. 86 0
      model/event_death.go
  89. 86 0
      model/event_dry_milk.go
  90. 80 42
      model/event_enter.go
  91. 70 42
      model/event_estrus.go
  92. 89 0
      model/event_forbidden_mating.go
  93. 0 22
      model/event_frozen_semen_log.go
  94. 139 0
      model/event_immunization_plan.go
  95. 326 43
      model/event_mating.go
  96. 165 48
      model/event_pregnant_check.go
  97. 131 0
      model/event_sale.go
  98. 50 0
      model/event_sale_car.go
  99. 49 0
      model/event_sale_cow.go
  100. 0 46
      model/event_same_time.go

+ 10 - 12
.drone.yml

@@ -1,18 +1,16 @@
 kind: pipeline
 type: docker
-name: kptTmrGroup
-
-#clone:
-#  depth: 1
-#  disable: true
+name: kptPasture
 
+clone:
+  depth: 1
+  disable: true
 steps:
-  #- name: clone
-  #  image: alpine/git
-  #  commands:
-  #    - git clone -b develop http://kpt.kptyun.cn:3000/xuyiping/kpt-pasture.git
-  #    - ls -l
-  #    - pwd
+  - name: clone
+    image: alpine/git
+    commands:
+      - git clone -b feature/event http://192.168.1.8:3000/xuyiping/kpt-pasture.git
+      - cp -R kpt-pasture/* ./
   - name: build
     image: plugins/docker:20.14.2
     volumes:
@@ -30,7 +28,7 @@ steps:
         from_secret: aliyuncs_password
       repo: registry.cn-hangzhou.aliyuncs.com/kpt-event/kpt-pasture
       registry: registry.cn-hangzhou.aliyuncs.com
-      tags: [ test ]
+      tags: [ http-test,mqtt-test,cron-test]
 
 trigger:
   branch:

+ 10 - 7
Dockerfile

@@ -6,25 +6,28 @@ COPY . .
 RUN mkdir -p ./bin
 
 RUN go env -w GO111MODULE=on && \
-    go env -w GOPROXY=https://goproxy.cn,direct && \
+    go env -w GOPROXY=https://mirrors.aliyun.com/goproxy/,direct && \
     go env -w CGO_ENABLED=0 && \
     go env -w GOARCH=amd64 && \
     go env -w GOOS=linux && \
-    go build -o ./bin/kptTmrGroup -ldflags "-X kpt.kptyun.cn:3000/kpt-event/kpt-pasture/pod.appVersion=beef" main.go
+    go build -o ./kptPasture -ldflags "-X kpt.kptyun.cn:3000/kpt-event/kpt-pasture/pod.appVersion=beef" main.go
 
 
 FROM alpine:latest
 LABEL name="kpt-pasture" \
-description="pt service" \
+description="pasture service" \
 owner="yiping.xu"
 
 WORKDIR /app/kpt-pasture
 
+RUN rm -f /etc/localtime \
+&& ln -sv /usr/share/zoneinfo/Asia/Shanghai /etc/localtime \
+&& echo "Asia/Shanghai" > /etc/timezone
 
-COPY --from=0 /app/kpt-pasture/config/ /app/kpt-pasture/bin/config/
-COPY --from=0  /app/kpt-pasture/bin/kptPasture /app/kpt-pasture/bin/kptPasture
+COPY --from=0 /app/kpt-pasture/config/ /app/kpt-pasture/config/
+COPY --from=0  /app/kpt-pasture/kptPasture /app/kpt-pasture/kptPasture
 
 EXPOSE 8090
-VOLUME ["/app/kpt-pasture/logger","/app/kpt-pasture/bin/config","/app/kpt-pasture/files"]
+VOLUME ["/app/kpt-pasture/logger","/app/kpt-pasture/config","/app/kpt-pasture/files"]
 
-CMD ["/app/kpt-pasture/bin/kptPasture","http"]
+CMD ["/app/kpt-pasture/kptPasture","http"]

+ 22 - 1
README.md

@@ -31,5 +31,26 @@ lint:
 - 生成 mock 前,请确保你能够编译 & 编译完成
 - make generate
 
+## 初始化系统表
+1. data_warning
+2. data_warning_items
+3. system_basic
+4. neck_ring_config
+5. app_pasture_list
+6. app_pasture_receiver
+
+
 todo列表:
-- module/crontab/crontab.go 中119行[Limit(100)] 待优化,case为产后日期类型待测试
+- [x] module/crontab/crontab.go 中119行[Limit(100)] 待优化,case为产后日期类型待测试
+- [x] 后台添加配种数据时候,不知道该牛只是同期还是自然发情还是人工揭发?
+- [x] 青年牛转后备牛事件(到达主动停配期主动转?)
+- [x] 后备牛到达主动停配期后的牛只放在哪个模块(配种清单,发情清单)
+- [x] 发情清单和配种清单更新机制
+- [ ] 前后端部署架构【k8s,docker-compose,docker-swarm】namespace隔离,需要考虑的问题【1.一次性任务,2. 定时任务 3. 数据收集,4. 日志收集 5. 报警介入】
+- [x] 所有事件录入梳理【批量录入,excel导入,信息人员与操作人员统一规范】
+- [x] 药品优化成药品名称关联生产商
+- [x] 框架logrus日志优化【未按照指定天数的日志自动删除,待验证】
+- [x] 犊牛的牛只品种是根据母牛的品种来确定,还是根据公牛来确定?【目前是根据母牛品种来确定】
+
+脖环发情算法梳理:
+- [x] 处理异常上报数据(frameid > 12)

+ 2 - 15
cmd/consumer.go

@@ -4,31 +4,18 @@ import (
 	"kpt-pasture/dep"
 
 	"gitee.com/xuyiping_admin/pkg/logger/zaplog"
-	"go.uber.org/zap"
-
 	"github.com/spf13/cobra"
+	"go.uber.org/zap"
 )
 
 // ConsumerCmd represents the consumer command
 var ConsumerCmd = &cobra.Command{
 	Use:   "consumer",
 	Short: "start consumer",
-	PersistentPreRun: func(cmd *cobra.Command, args []string) {
-
-	},
-}
-
-func init() {
-	ConsumerCmd.AddCommand(workOrder)
-}
-
-var workOrder = &cobra.Command{
-	Use:   "workOrder",
-	Short: "kpt pasture work order",
 	Run: func(cmd *cobra.Command, args []string) {
 		srv := dep.DIAsynqWorkOrder()
 		if err := srv.Server.Run(srv.Mux); err != nil {
-			zaplog.Error("eventWorkflow", zap.Any("err", err))
+			zaplog.Error("consumer", zap.Any("err", err))
 		}
 	},
 }

+ 25 - 0
cmd/milk.go

@@ -0,0 +1,25 @@
+package cmd
+
+import (
+	"kpt-pasture/config"
+	"kpt-pasture/service/milk"
+
+	"github.com/spf13/cobra"
+)
+
+var MilkCmd = &cobra.Command{
+	Use:   "milk",
+	Short: "Milk Hall Data",
+	Run: func(cmd *cobra.Command, args []string) {
+		bootMilk(config.Options())
+	},
+}
+
+func bootMilk(cfg *config.AppConfig) {
+	findMilkDataService := milk.NewMilkDataService(cfg.MilkHall)
+	files, err := findMilkDataService.ReadFiles()
+	if err != nil {
+		panic(err)
+	}
+	findMilkDataService.Run(files)
+}

+ 15 - 0
cmd/mqtt.go

@@ -0,0 +1,15 @@
+package cmd
+
+import (
+	"kpt-pasture/dep"
+
+	"github.com/spf13/cobra"
+)
+
+var MqttCmd = &cobra.Command{
+	Use:   "mqtt",
+	Short: "mqtt server",
+	Run: func(cmd *cobra.Command, args []string) {
+		dep.DIMqttService()
+	},
+}

+ 3 - 3
cmd/root.go

@@ -7,12 +7,10 @@ import (
 )
 
 var RootCmd = &cobra.Command{
-	Use:   "kpt-pasture",
+	Use:   "kptPasture",
 	Short: "科湃腾牛群系统",
 }
 
-// Execute adds all child commands to the root command and sets flags appropriately.
-// This is called by main.main(). It only needs to happen once to the rootCmd.
 func Execute() {
 	err := RootCmd.Execute()
 	if err != nil {
@@ -25,4 +23,6 @@ func init() {
 	RootCmd.AddCommand(JobCmd)
 	RootCmd.AddCommand(CrontabCmd)
 	RootCmd.AddCommand(ConsumerCmd)
+	RootCmd.AddCommand(MqttCmd)
+	RootCmd.AddCommand(MilkCmd)
 }

+ 55 - 10
config/app.develop.yaml

@@ -5,22 +5,22 @@ debug: true
 http_server_addr: ':8090'
 http_metrics_addr: ':23332'
 jwt_expire_time: 172800
+neck_ring_limit: 100
 
 store:
   show_sql: true
   driver_name: mysql
-  kpt_rw: "kpt_pasture:4~H@InK6jK@tcp(47.92.95.119:3326)/kpt_pasture?charset=utf8mb4&parseTime=true&loc=Local&allowNativePasswords=true&timeout=300s&readTimeout=300s&writeTimeout=300s"
-  kpt_migr: "kpt_pasture:4~H@InK6jK@tcp(47.92.95.119:3326)/kpt_pasture?charset=utf8mb4&parseTime=true&loc=Local&allowNativePasswords=true&timeout=300s&readTimeout=300s&writeTimeout=300s"
-
+  kpt_rw: "kpt_pasture:4~H@InK6jK@tcp(47.92.95.119:3326)/kpt_pasture?charset=utf8mb4&parseTime=true&loc=Local&allowNativePasswords=true&timeout=600s&readTimeout=600s&writeTimeout=600s"
+  kpt_migr: "kpt_pasture:4~H@InK6jK@tcp(47.92.95.119:3326)/kpt_pasture?charset=utf8mb4&parseTime=true&loc=Local&allowNativePasswords=true&timeout=600s&readTimeout=600s&writeTimeout=600s"
+  kpt_mqtt: "kpt_mqtt:kpt_mqtt!1234@tcp(47.92.95.119:3326)/kpt_pasture?charset=utf8mb4&parseTime=true&loc=Local&allowNativePasswords=true&timeout=600s&readTimeout=600s&writeTimeout=600s"
 redis_setting:
   cache_redis:
     addr: '47.92.95.119:6389'
     db: 11
-    requirepass: ""
+    requirepass: "root"
     expiry: 120
 
 jwt_secret: "sUd7j%UfJMt59ywh"
-cache_key_suffix: "gmym"
 
 side_work_setting:
   asynq_setting:
@@ -28,14 +28,59 @@ side_work_setting:
       addr: '47.92.95.119:6389'
       db: 0
       pool_size: 10
+      password: "root"
     concurrency: 5
     queues:
-      workflow: 20
+      work: 20
       low: 10
       default: 5
 cron:
   crontab_start_run: false
-  update_cow_info: "0 * * * * ?"
-  generate_work_order: "0 0 22 * * ?"
-  immunization_plan: "0 * * * * ?"
-  same_Time_plan: "0 */1 * * * ?"
+  update_cow_info: "0 01 1 * * ?"
+  indicators: "0 0 23 * * ?"            # 每天凌晨1点03分执行
+  generate_work_order: "0 05 1 * * ?"
+  immunization_plan: "0 10 1 * * ?"
+  same_Time_plan: "0 15 1 * * ?"
+  update_same_time: "0 20 1 * * ?"
+  delete_old_original: "0 30 1 * * ?"   # 每天凌晨1点30分执行
+  system_basic_crontab: "0 35 1 * * ?"
+  update_disease_to_calendar: "0 50 1 * * ?"
+  cow_pregnant: "0 01 15 * * ?"
+  neck_ring_estrus: "0 45 * * * *"             # 每小时的45分钟执行一次
+  neck_ring_merge: "*/60 * * * * ?"            # 合并脖环原始2小时数据(5分钟)
+  neck_ring_calculate: "*/300 * * * * ?"       # 计算脖环数据
+  neck_ring_estrus_warning: "* */30 * * * ?"   # 脖环发情预警(每50分钟执行一次
+  neck_ring_health_warning: "* */30 * * * ?"   # 脖环健康预警
+  update_pen_behavior: "0 */20 * * * ?"        # 更新栏舍行为数据(20分钟执行一次)
+  update_pen_behavior_daily: "0 50 15 * * ?"   # 更新栏舍饲养监测数据
+  update_milk_original: "0 */30 * * * ?"       # 更新奶厅原始数据(每30分钟执行一次)
+  insert_milk_daily: "0 05 02 * * ?"           # 更新每日奶量和活动量数据
+  cow_neck_ring_error: "0 0 */2 * * ?"         # 异常脖环数据上报
+
+mqtt:
+  broker: "kptyun.com:1983"
+  username: "kptmqtt"
+  password: "kepaiteng"
+  sub_topic: "/user/heatwatch/neckring/post"
+  qos: 0
+  retain: false
+  keep_alive: 60
+  connect_timeout: 10
+  auto_reconnect: true
+  reconnect_interval: 10
+  work_number: 1
+  merge_data_ticker: 2   # 2分钟合并一次数据
+
+milk_hall:
+  base_path: "/milk_hall"
+  file_path:
+    - "/file1"
+    - "/file2"
+  back_path: "http://your-api-endpoint"
+  url_path: "/api/v1/milk/hall/original"
+  brand: "gea"
+  milk_hall_number: "三期"
+  farm_id: "c7357ce63cc7dddf6aa75c5baa37c507"
+
+
+

+ 66 - 8
config/app.go

@@ -21,11 +21,12 @@ var (
 
 // AppConfig store all configuration options
 type AppConfig struct {
-	FarmName       string `json:"farm_name"`
+	FarmName       string `yaml:"farm_name"`
 	AppName        string `yaml:"app_name"`
 	AppEnv         string `yaml:"app_environment"`
-	Debug          bool   `yaml:"debug" env:"APP_DEBUG"`
-	HTTPServerAddr string `yaml:"http_server_addr" env:"HTTP_SERVER_ADDR"`
+	Debug          bool   `yaml:"debug"`
+	HTTPServerAddr string `yaml:"http_server_addr"`
+	NeckRingLimit  int32  `yaml:"neck_ring_limit"`
 
 	// 数据库配置 额外加载文件部分 database.yaml
 	StoreSetting StoreSetting `json:"storeSetting" yaml:"store"`
@@ -41,18 +42,39 @@ type AppConfig struct {
 
 	// asynq 相关配置
 	SideWorkSetting SideWorkSetting `yaml:"side_work_setting"`
+	CronSetting     CronSetting     `json:"cron_setting" yaml:"cron"`
+	Mqtt            MqttSetting     `json:"mqtt"`
+	MilkHall        MilkHallSetting `json:"milkHall"`
 
-	CronSetting CronSetting `json:"cron_setting" yaml:"cron"`
+	EmailConfig EmailConfig `json:"emailConfig" yaml:"email_config"`
 }
 
 type CronSetting struct {
 	// 是否启动任务时先跑一次
 	CrontabStartRun bool `yaml:"crontab_start_run"`
 	// CRONTAB 表达式
-	UpdateCowInfo     string `yaml:"update_cow_info"`
-	GenerateWorkOrder string `yaml:"generate_work_order"`
-	ImmunizationPlan  string `yaml:"immunization_plan"`
-	SameTimePlan      string `yaml:"same_time_plan"`
+	UpdateCowInfo           string `yaml:"update_cow_info"`            //  更新牛只信息
+	Indicators              string `yaml:"indicators"`                 //  牛只指标
+	GenerateWorkOrder       string `yaml:"generate_work_order"`        //  生成工作单
+	ImmunizationPlan        string `yaml:"immunization_plan"`          //  免疫计划
+	SameTimePlan            string `yaml:"same_time_plan"`             //  同期
+	UpdateSameTime          string `yaml:"update_same_time"`           //  更新同期
+	SystemBasicCrontab      string `yaml:"system_basic_crontab"`       //  系统基础定时任务
+	DeleteOldOriginal       string `yaml:"delete_old_original"`        //  删除脖环历史数据
+	UpdateDiseaseToCalendar string `yaml:"update_disease_to_calendar"` //  更新每天治疗中牛头数到日历表中
+	CowPregnant             string `yaml:"cow_pregnant"`               //  月度牛只怀孕清单
+	UpdateActiveHabit       string `yaml:"update_active_habit"`        //  脖环2小时数据重新整合
+	NeckRingEstrus          string `yaml:"neck_ring_estrus"`           //  脖环牛只发情
+	NeckRingMerge           string `yaml:"neck_ring_merge"`            //  脖环原始数据合并
+	NeckRingCalculate       string `yaml:"neck_ring_calculate"`        //  脖环数据计算
+	NeckRingEstrusWarning   string `yaml:"neck_ring_estrus_warning"`   //  脖环发情预警
+	NeckRingHealthWarning   string `yaml:"neck_ring_health_warning"`   //  脖环健康预警
+	UpdatePenBehavior       string `yaml:"update_pen_behavior"`        //  栏舍行为数据
+	UpdatePenBehaviorDaily  string `yaml:"update_pen_behavior_daily"`  //  栏舍饲养监测
+	UpdateMilkOriginal      string `yaml:"update_milk_original"`       //  奶厅原始数据更新
+	InsertMilkDaily         string `yaml:"insert_milk_daily"`          //  牛只每日奶量数据
+	TwentyOnePregnantRate   string `yaml:"twenty_one_pregnant_rate"`   //  21怀孕率
+	CowNeckRingError        string `yaml:"cow_neck_ring_error"`        //  异常脖环数据
 }
 
 type JwtTokenKeyConfig struct {
@@ -77,6 +99,7 @@ type StoreSetting struct {
 	ShowSQL    bool   `yaml:"show_sql" json:"show_sql"`
 	KptRW      string `yaml:"kpt_rw" json:"kpt_rw"`
 	KptMigr    string `yaml:"kpt_migr" json:"kpt_migr"`
+	KptMqtt    string `yaml:"kpt_mqtt" json:"kpt_mqtt"`
 }
 
 type RedisSetting struct {
@@ -152,6 +175,41 @@ type AsynqRedisSetting struct {
 	TLSConfig *tls.Config `json:"tlsConfig,omitempty" yaml:"tls_config"`
 }
 
+type MqttSetting struct {
+	Broker            string `json:"broker" yaml:"broker"`
+	UserName          string `json:"username" yaml:"username"`
+	Password          string `json:"password" yaml:"password"`
+	SubTopic          string `json:"sub_topic"  yaml:"sub_topic"`
+	Retain            bool   `json:"retain" yaml:"retain"`
+	Qos               int    `json:"qos" yaml:"qos"`
+	KeepAlive         int    `json:"keepAlive" yaml:"keep_alive"`
+	ConnectTimeout    int    `json:"connectTimeout" yaml:"connect_timeout"`
+	AutoReconnect     bool   `json:"autoReconnect" yaml:"auto_reconnect"`
+	ReconnectInterval int    `json:"reconnectInterval" yaml:"reconnect_interval"`
+	WorkNumber        int    `json:"workNumber" yaml:"work_number"`
+	MergeDataTicker   int    `json:"mergeDataTicker" yaml:"merge_data_ticker"`
+}
+
+type MilkHallSetting struct {
+	BasePath       string   `yaml:"base_path"`
+	FilePath       []string `yaml:"file_path"`
+	BackPath       string   `json:"back_path"`
+	UrlPath        string   `yaml:"url_path"`
+	Brand          string   `yaml:"brand"`
+	FarmId         string   `yaml:"farm_id"`
+	MilkHallNumber string   `yaml:"milk_hall_number"`
+}
+
+// EmailConfig 邮件配置
+type EmailConfig struct {
+	Host     string   `yaml:"host"`     // SMTP服务器地址
+	Port     int      `json:"port"`     // SMTP服务器端口
+	Username string   `yaml:"username"` // 发件人邮箱
+	Password string   `json:"password"` // 发件人密码
+	From     string   `yaml:"from"`     // 发件人名称
+	To       []string `yaml:"to"`       // 收件人列表
+}
+
 func (a *AppConfig) Name() string {
 	return fmt.Sprintf("%s-%s", a.AppName, a.AppEnv)
 }

+ 36 - 16
config/app.test.yaml

@@ -4,37 +4,57 @@ debug: true
 http_server_addr: ':8090'
 http_metrics_addr: ':23332'
 jwt_expire_time: 172800
+neck_ring_limit: 100
 
 store:
   show_sql: true
   driver_name: mysql
   kpt_rw: "kpt_pasture:4~H@InK6jK@tcp(47.92.95.119:3326)/kpt_pasture?charset=utf8mb4&parseTime=true&loc=Local&allowNativePasswords=true&timeout=300s&readTimeout=300s&writeTimeout=300s"
   kpt_migr: "kpt_pasture:4~H@InK6jK@tcp(47.92.95.119:3326)/kpt_pasture?charset=utf8mb4&parseTime=true&loc=Local&allowNativePasswords=true&timeout=300s&readTimeout=300s&writeTimeout=300s"
+  kpt_mqtt: "kpt_mqtt:kpt_mqtt!1234@tcp(47.92.95.119:3326)/kpt_pasture?charset=utf8mb4&parseTime=true&loc=Local&allowNativePasswords=true&timeout=600s&readTimeout=600s&writeTimeout=600s"
 
 redis_setting:
   cache_redis:
     addr: 47.92.95.119:6389'
     db: 0
-    requirepass: ""
+    requirepass: "root"
     expiry: 120
 
 jwt_secret: "sUd7j%UfJMt59ywh"
 cache_key_suffix: "gmym"
 
-side_work_setting:
-  asynq_setting:
-    redis:
-      addr: '47.92.95.119:6389'
-      db: 16
-      pool_size: 10
-    concurrency: 5
-    queues:
-      workflow: 20
-      low: 10
-      default: 5
 cron:
   crontab_start_run: false
-  update_cow_info: "0 */1 * * * ?"
-  generate_work_order: "0 0 22 * * ?"
-  immunization_plan: "0 * * * * ?"
-  same_Time_plan: "0 */1 * * * ?"
+  update_cow_info: "0 01 1 * * ?"
+  indicators: "0 0 23 * * ?"            # 每天凌晨1点03分执行
+  generate_work_order: "0 05 1 * * ?"
+  immunization_plan: "0 10 1 * * ?"
+  same_Time_plan: "0 15 1 * * ?"
+  update_same_time: "0 20 1 * * ?"
+  delete_old_original: "0 30 1 * * ?"   # 每天凌晨1点30分执行
+  system_basic_crontab: "0 35 1 * * ?"
+  update_disease_to_calendar: "0 50 1 * * ?"
+  cow_pregnant: "0 01 15 * * ?"
+  neck_ring_estrus: "0 45 * * * *"     # 每小时的45分钟执行一次
+  neck_ring_merge: "*/60 * * * * ?"      # 合并脖环原始2小时数据(5分钟)
+  neck_ring_calculate: "*/300 * * * * ?"  # 计算脖环数据
+  neck_ring_estrus_warning: "* */30 * * * ?"   # 脖环发情预警(每50分钟执行一次
+  neck_ring_health_warning: "* */30 * * * ?"   # 脖环健康预警
+  update_pen_behavior: "0 */20 * * * ?"        # 更新栏舍行为数据(20分钟执行一次)
+  update_pen_behavior_daily: "0 50 15 * * ?"  # 更新栏舍饲养监测数据
+  update_milk_original: "0 */30 * * * ?"     # 更新奶厅原始数据(每30分钟执行一次)
+  insert_milk_daily: "0 05 02 * * ?"          # 更新每日奶量和活动量数据
+
+mqtt:
+  broker: "kptyun.com:1983"
+  username: "kptmqtt"
+  password: "kepaiteng"
+  sub_topic: "/user/heatwatch/neckring/post"
+  qos: 0
+  retain: false
+  keep_alive: 60
+  connect_timeout: 10
+  auto_reconnect: true
+  reconnect_interval: 10
+  work_number: 1
+  merge_data_ticker: 2   # 2分钟合并一次数据

+ 6 - 2
dep/dep.go

@@ -2,14 +2,16 @@ package dep
 
 import (
 	"kpt-pasture/config"
+	"kpt-pasture/module/asynq"
 	"kpt-pasture/module/backend"
-	"kpt-pasture/module/consumer"
 	"kpt-pasture/module/crontab"
+	moduleMqtt "kpt-pasture/module/mqtt"
 	"kpt-pasture/service/asynqsvc"
 	"kpt-pasture/service/redis"
 	"kpt-pasture/service/sso"
 	"kpt-pasture/service/wechat"
 	"kpt-pasture/store/kptstore"
+	"kpt-pasture/store/mqttstore"
 
 	"gitee.com/xuyiping_admin/pkg/di"
 )
@@ -33,8 +35,10 @@ func Options() []di.HubOption {
 		sso.Module,
 		wechat.Module,
 		asynqsvc.Module,
-		consumer.Module,
+		asynq.Module,
 		redis.Module,
 		crontab.Module,
+		moduleMqtt.Module,
+		mqttstore.Module,
 	}
 }

+ 1 - 0
dep/di_alert.go

@@ -0,0 +1 @@
+package dep

+ 13 - 9
dep/di_asynq.go

@@ -1,9 +1,10 @@
 package dep
 
 import (
+	"fmt"
 	"kpt-pasture/config"
 	"kpt-pasture/model"
-	"kpt-pasture/module/consumer"
+	"kpt-pasture/module/asynq"
 	"kpt-pasture/service/asynqsvc"
 
 	"go.uber.org/dig"
@@ -20,16 +21,19 @@ func DIAsynqWorkOrder() (out *asynqsvc.Server) {
 	return
 }
 
-// AsynqWorkOrder 相关消费
-func AsynqWorkOrder(dep AsyncDependency) *asynqsvc.Server {
-	srv := asynqsvc.NewServer(config.Options())
-	srv.Mux.HandleFunc(model.TaskWorkOrder, dep.WorkOrder.DayWorkOrder) // 工单
-	return srv
-}
-
 // AsyncDependency is the dependency for worker and kafka
 type AsyncDependency struct {
 	dig.In
 
-	WorkOrder consumer.BizExec // BizExec 工单
+	WorkOrder asynq.BizExec // BizExec 工单
+}
+
+// AsynqWorkOrder 相关消费
+func AsynqWorkOrder(dep AsyncDependency) *asynqsvc.Server {
+	cfg := config.Options()
+	srv := asynqsvc.NewServer(cfg)
+	pref := cfg.FarmName
+	pattern := fmt.Sprintf("%s:%s", pref, model.TaskWorkOrder)
+	srv.Mux.HandleFunc(pattern, dep.WorkOrder.DayWorkOrder) // 工单
+	return srv
 }

+ 88 - 7
dep/di_crontab.go

@@ -4,6 +4,9 @@ import (
 	"kpt-pasture/config"
 	"kpt-pasture/module/crontab"
 
+	"gitee.com/xuyiping_admin/pkg/logger/zaplog"
+	"go.uber.org/zap"
+
 	"go.uber.org/dig"
 
 	"gitee.com/xuyiping_admin/pkg/cron"
@@ -40,25 +43,103 @@ type CrontabDependency struct {
 func EntryCrontab(dependency CrontabDependency) *cron.Crontab {
 	cfg := config.Options()
 	cs := cfg.CronSetting
+
 	newCrontab := cron.NewCrontab(DataCenterCrontabCounterVec)
-	err := newCrontab.Bind("UpdateCowInfo", cs.UpdateCowInfo, dependency.CrontabHub.UpdateCowInfo)
-	if err != nil {
+	if err := newCrontab.Bind("indicators", cs.Indicators, dependency.CrontabHub.Indicators); err != nil {
 		panic(err)
 	}
 
-	err = newCrontab.Bind("GenerateWorkOrder", cs.GenerateWorkOrder, dependency.CrontabHub.GenerateAsynqWorkOrder)
-	if err != nil {
+	if err := newCrontab.Bind("UpdateCowInfo", cs.UpdateCowInfo, dependency.CrontabHub.UpdateCowInfo); err != nil {
 		panic(err)
 	}
 
-	err = newCrontab.Bind("ImmunizationPlan", cs.ImmunizationPlan, dependency.CrontabHub.ImmunizationPlan)
-	if err != nil {
+	if err := newCrontab.Bind("ImmunizationPlan", cs.ImmunizationPlan, dependency.CrontabHub.ImmunizationPlan); err != nil {
+		panic(err)
+	}
+
+	if err := newCrontab.Bind("TwentyOnePregnantRate", cs.TwentyOnePregnantRate, dependency.CrontabHub.TwentyOnePregnantRate); err != nil {
+		panic(err)
+	}
+
+	if err := newCrontab.Bind("SameTimePlan", cs.SameTimePlan, dependency.CrontabHub.SameTimePlan); err != nil {
+		panic(err)
+	}
+
+	if err := newCrontab.Bind("UpdateSameTime", cs.UpdateSameTime, dependency.CrontabHub.UpdateSameTime); err != nil {
+		panic(err)
+	}
+
+	if err := newCrontab.Bind("SystemBasicCrontab", cs.SystemBasicCrontab, dependency.CrontabHub.SystemBasicCrontab); err != nil {
+		panic(err)
+	}
+
+	if err := newCrontab.Bind("DeleteOldOriginal", cs.DeleteOldOriginal, dependency.CrontabHub.DeleteOldOriginal); err != nil {
+		panic(err)
+	}
+
+	if err := newCrontab.Bind("UpdateDiseaseToCalendar", cs.UpdateDiseaseToCalendar, dependency.CrontabHub.UpdateDiseaseToCalendar); err != nil {
+		zaplog.Error("EntryCrontab", zap.Any("UpdateCowEstrus", err))
+		panic(err)
+	}
+
+	if err := newCrontab.Bind("CowNeckRingErrorEnter", cs.CowNeckRingError, dependency.CrontabHub.CowNeckRingErrorEnter); err != nil {
+		zaplog.Error("EntryCrontab", zap.Any("CowNeckRingErrorEnter", err))
+		panic(err)
+	}
+
+	if err := newCrontab.Bind("UpdateCowEstrus", cs.NeckRingEstrus, dependency.CrontabHub.UpdateCowEstrus); err != nil {
+		zaplog.Error("EntryCrontab", zap.Any("UpdateCowEstrus", err))
+		panic(err)
+	}
+
+	if err := newCrontab.Bind("NeckRingCalculate", cs.NeckRingCalculate, dependency.CrontabHub.NeckRingCalculate); err != nil {
+		zaplog.Error("EntryCrontab", zap.Any("NeckRingCalculate", err))
+		panic(err)
+	}
+
+	if err := newCrontab.Bind("NeckRingMerge", cs.NeckRingMerge, dependency.CrontabHub.NeckRingOriginalMerge); err != nil {
+		zaplog.Error("EntryCrontab", zap.Any("NeckRingOriginalMergeData", err))
+		panic(err)
+	}
+
+	if err := newCrontab.Bind("NeckRingEstrusWarning", cs.NeckRingEstrusWarning, dependency.CrontabHub.NeckRingEstrusWarning); err != nil {
+		zaplog.Error("EntryCrontab", zap.Any("NeckRingEstrusWarning", err))
+		panic(err)
+	}
+
+	if err := newCrontab.Bind("NeckRingHealthWarning", cs.NeckRingHealthWarning, dependency.CrontabHub.NeckRingHealthWarning); err != nil {
+		zaplog.Error("EntryCrontab", zap.Any("NeckRingHealthWarning", err))
+		panic(err)
+	}
+
+	if err := newCrontab.Bind("UpdatePenBehavior", cs.UpdatePenBehavior, dependency.CrontabHub.UpdatePenBehavior); err != nil {
+		zaplog.Error("EntryCrontab", zap.Any("UpdatePenBehavior", err))
+		panic(err)
+	}
+
+	if err := newCrontab.Bind("UpdatePenBehaviorDaily", cs.UpdatePenBehaviorDaily, dependency.CrontabHub.UpdatePenBehaviorDaily); err != nil {
+		zaplog.Error("EntryCrontab", zap.Any("UpdatePenBehaviorDaily", err))
+		panic(err)
+	}
+
+	if err := newCrontab.Bind("UpdateMilkOriginal", cs.UpdateMilkOriginal, dependency.CrontabHub.UpdateMilkOriginal); err != nil {
+		zaplog.Error("EntryCrontab", zap.Any("UpdateMilkOriginal", err))
+		panic(err)
+	}
+
+	if err := newCrontab.Bind("UpdateActiveHabit", cs.InsertMilkDaily, dependency.CrontabHub.InsertMilkDaily); err != nil {
+		zaplog.Error("EntryCrontab", zap.Any("InsertMilkDaily", err))
 		panic(err)
 	}
 
-	err = newCrontab.Bind("SameTimePlan", cs.SameTimePlan, dependency.CrontabHub.SameTimePlan)
+	/*err = newCrontab.Bind("GenerateWorkOrder", cs.GenerateWorkOrder, dependency.CrontabHub.GenerateAsynqWorkOrder)
 	if err != nil {
 		panic(err)
 	}
+	err = newCrontab.Bind("CowPregnant", cs.CowPregnant, dependency.CrontabHub.CowPregnant)
+	if err != nil {
+		panic(err)
+	}*/
+
 	return newCrontab
 }

+ 34 - 0
dep/di_mqtt.go

@@ -0,0 +1,34 @@
+package dep
+
+import (
+	"kpt-pasture/config"
+	mqttHandle "kpt-pasture/module/mqtt"
+	"kpt-pasture/service/mqtt"
+
+	"go.uber.org/dig"
+)
+
+func DIMqttService() (out mqtt.IMqttServer) {
+	container := DI()
+	if err := container.Provide(MqttHandel); err != nil {
+		panic(err)
+	}
+	if err := container.Invoke(func(c mqtt.IMqttServer) { out = c }); err != nil {
+		panic(err)
+	}
+	return
+}
+
+// MqttHandel 相关消费
+func MqttHandel(dep MqttDependency) mqtt.IMqttServer {
+	cfg := config.Options()
+	sev := mqtt.NewServer(cfg)
+	sev.Run(dep.MqttHub)
+	return sev
+}
+
+type MqttDependency struct {
+	dig.In
+
+	MqttHub mqttHandle.Entry // 处理数据
+}

+ 25 - 10
docker-compose.yml

@@ -1,29 +1,44 @@
 version : '3'
 services:
-  kpt-dtm-crontab:
+  kpt-pasture-crontab:
     privileged: true
     container_name: xdmy001_kpt_pasture_crontab
     restart: always
     image: registry.cn-hangzhou.aliyuncs.com/kpt-event/kpt-pasture:test
     volumes:
-      - /var/logger/kpt-dtm/:/app/kpt-dtm/logger
+      - /var/logger/kpt-pasture/:/app/kpt-pasture/logger
       - /etc/localtime:/etc/localtime
       - /data/docker-compose/kpt-pasture/config:/app/kpt-pasture/config
     environment:
       - APP_ENVIRONMENT=production
-      - DTM_WORK_DIR=/app/kpt-pasture/bin
-    command: [ "/app/kpt-pasture/bin/kptPasture","crontab" ]
-  kpt-dtm-http:
+      - PASTURE_WORK_DIR=/app/kpt-pasture
+    command: [ "/app/kpt-pasture/kptPasture","crontab" ]
+  kpt-pasture-http:
     privileged: true
     container_name: xdmy001_kpt_pasture_http
     restart: always
-    image: registry.cn-hangzhou.aliyuncs.com/kpt-event/kpt-dtm:test
+    image: registry.cn-hangzhou.aliyuncs.com/kpt-event/kpt-pasture:test
     volumes:
-      - /var/logger/kpt-dtm/:/app/kpt-dtm/logger
+      - /var/logger/kpt-pasture/:/app/kpt-pasture/logger
       - /etc/localtime:/etc/localtime
-      - /data/docker-compose/kpt-dtm/config:/app/kpt-dtm/config
+      - /data/docker-compose/kpt-pasture/config:/app/kpt-pasture/config
       - /data/docker-compose/kpt-pasture/files:/app/kpt-pasture/files
+    ports:
+      - "8091:8090"
+    environment:
+      - APP_ENVIRONMENT=production
+      - PASTURE_WORK_DIR=/app/kpt-pasture
+    command: [ "/app/kpt-pasture/kptPasture","http" ]
+  kpt-pasture-mqtt:
+    privileged: true
+    container_name: xdmy001_kpt_pasture_mqtt
+    restart: always
+    image: registry.cn-hangzhou.aliyuncs.com/kpt-event/kpt-pasture:test
+    volumes:
+      - /var/logger/kpt-pasture/:/app/kpt-pasture/logger
+      - /etc/localtime:/etc/localtime
+      - /data/docker-compose/kpt-pasture/config:/app/kpt-pasture/config
     environment:
       - APP_ENVIRONMENT=production
-      - DTM_WORK_DIR=/app/kpt-pasture/bin
-    command: [ "/app/kpt-pasture/bin/kptPasture","http" ]
+      - PASTURE_WORK_DIR=/app/kpt-pasture
+    command: [ "/app/kpt-pasture/kptPasture","mqtt" ]

+ 20 - 11
go.mod

@@ -3,9 +3,10 @@ module kpt-pasture
 go 1.17
 
 require (
-	gitee.com/xuyiping_admin/go_proto v0.0.0-20240909083429-fbc0524f090a
-	gitee.com/xuyiping_admin/pkg v0.0.0-20231218082641-aac597b8a015
+	gitee.com/xuyiping_admin/go_proto v0.0.0-20250616080546-3ebf4d3f0874
+	gitee.com/xuyiping_admin/pkg v0.0.0-20250613101634-36c36a2d27d0
 	github.com/dgrijalva/jwt-go v3.2.0+incompatible
+	github.com/eclipse/paho.mqtt.golang v1.4.3
 	github.com/eko/gocache v1.1.0
 	github.com/getsentry/sentry-go v0.23.0
 	github.com/gin-contrib/cors v1.4.0
@@ -19,17 +20,26 @@ require (
 	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/cobra v1.8.1
 	github.com/spf13/viper v1.16.0
 	github.com/stretchr/testify v1.8.4
 	github.com/xuri/excelize/v2 v2.8.0
+	github.com/yangxikun/gin-limit-by-key v0.0.0-20190512072151-520697354d5f
 	go.uber.org/dig v1.15.0
 	go.uber.org/zap v1.24.0
-	golang.org/x/sync v0.6.0
+	golang.org/x/sync v0.7.0
 	gorm.io/driver/mysql v1.5.1
 	gorm.io/gorm v1.25.2
 )
 
+require (
+	github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2 // indirect
+	github.com/gorilla/websocket v1.5.3 // indirect
+	github.com/nyaruka/phonenumbers v1.1.7 // indirect
+	github.com/patrickmn/go-cache v2.1.0+incompatible // indirect
+	golang.org/x/arch v0.3.0 // indirect
+)
+
 require (
 	github.com/beorn7/perks v1.0.1 // indirect
 	github.com/bradfitz/gomemcache v0.0.0-20190913173617-a41fca850d0b // indirect
@@ -54,7 +64,7 @@ require (
 	github.com/hashicorp/hcl v1.0.0 // indirect
 	github.com/huandu/xstrings v1.4.0 // indirect
 	github.com/inconshreveable/mousetrap v1.1.0 // indirect
-	github.com/jinzhu/copier v0.3.5 // indirect
+	github.com/jinzhu/copier v0.3.5
 	github.com/jinzhu/inflection v1.0.0 // indirect
 	github.com/jinzhu/now v1.1.5 // indirect
 	github.com/json-iterator/go v1.1.12 // indirect
@@ -72,7 +82,7 @@ require (
 	github.com/pkg/errors v0.9.1 // indirect
 	github.com/pmezard/go-difflib v1.0.0 // indirect
 	github.com/prometheus/client_model v0.3.0 // indirect
-	github.com/prometheus/common v0.42.0 // indirect
+	github.com/prometheus/common v0.42.0 // indirect; indirec
 	github.com/prometheus/procfs v0.10.1 // indirect
 	github.com/richardlehane/mscfb v1.0.4 // indirect
 	github.com/richardlehane/msoleps v1.0.3 // indirect
@@ -92,12 +102,11 @@ require (
 	github.com/xwb1989/sqlparser v0.0.0-20180606152119-120387863bf2 // indirect
 	go.uber.org/atomic v1.9.0 // indirect
 	go.uber.org/multierr v1.8.0 // indirect
-	golang.org/x/arch v0.5.0 // indirect
-	golang.org/x/crypto v0.21.0 // indirect
-	golang.org/x/net v0.22.0 // indirect
-	golang.org/x/sys v0.18.0 // indirect
+	golang.org/x/crypto v0.23.0 // indirect
+	golang.org/x/net v0.25.0 // indirect
+	golang.org/x/sys v0.20.0 // indirect
 	golang.org/x/text v0.15.0 // indirect
-	golang.org/x/time v0.3.0 // indirect
+	golang.org/x/time v0.3.0
 	google.golang.org/appengine v1.6.8 // indirect
 	google.golang.org/genproto/googleapis/rpc v0.0.0-20240513163218-0867130af1f8 // indirect
 	google.golang.org/grpc v1.64.0 // indirect

+ 163 - 31
go.sum

@@ -36,24 +36,146 @@ 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-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/go_proto v0.0.0-20240909032240-4bb8d1ae9a74 h1:EiIqP3VKAgfxAfTcYb0pahRDF8HTJszMS+6FT69kx5o=
-gitee.com/xuyiping_admin/go_proto v0.0.0-20240909032240-4bb8d1ae9a74/go.mod h1:BKrFW6YLDectlQcQk3FYKBeXvjEiodAKJ5rq7O/QiPE=
-gitee.com/xuyiping_admin/go_proto v0.0.0-20240909053609-bc77419ef76f h1:oRK0jUyGYwyF2hnMPskhWLvlMqlTkUeNwuI/HlTROZ0=
-gitee.com/xuyiping_admin/go_proto v0.0.0-20240909053609-bc77419ef76f/go.mod h1:BKrFW6YLDectlQcQk3FYKBeXvjEiodAKJ5rq7O/QiPE=
-gitee.com/xuyiping_admin/go_proto v0.0.0-20240909054215-306813604abd h1:j34BDfJy91vtzhhEYHU0k5cANdMerKj7Ctjg+n1XwBg=
-gitee.com/xuyiping_admin/go_proto v0.0.0-20240909054215-306813604abd/go.mod h1:BKrFW6YLDectlQcQk3FYKBeXvjEiodAKJ5rq7O/QiPE=
-gitee.com/xuyiping_admin/go_proto v0.0.0-20240909070422-d8e24115eea0 h1:WlkXLe5NFBAg57PLasrp3XvO/TB25+UqKGSXnWrbFBI=
-gitee.com/xuyiping_admin/go_proto v0.0.0-20240909070422-d8e24115eea0/go.mod h1:BKrFW6YLDectlQcQk3FYKBeXvjEiodAKJ5rq7O/QiPE=
-gitee.com/xuyiping_admin/go_proto v0.0.0-20240909083429-fbc0524f090a h1:Vz40n7q51ZKrI2P5BCT0eY1I2Du5ApRaaFkkzqDO1bg=
-gitee.com/xuyiping_admin/go_proto v0.0.0-20240909083429-fbc0524f090a/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=
+gitee.com/xuyiping_admin/go_proto v0.0.0-20250416085805-501c91e70d45 h1:O4vnwyLdVPFXGoiqfQqeaYAzdgTwwwphyVJ/0ws/UqU=
+gitee.com/xuyiping_admin/go_proto v0.0.0-20250416085805-501c91e70d45/go.mod h1:BKrFW6YLDectlQcQk3FYKBeXvjEiodAKJ5rq7O/QiPE=
+gitee.com/xuyiping_admin/go_proto v0.0.0-20250416102510-604a13fe9d52 h1:aCqqT2Y/po3OtkUqzogDOIjqwny03iu1rp47CU1yiz0=
+gitee.com/xuyiping_admin/go_proto v0.0.0-20250416102510-604a13fe9d52/go.mod h1:BKrFW6YLDectlQcQk3FYKBeXvjEiodAKJ5rq7O/QiPE=
+gitee.com/xuyiping_admin/go_proto v0.0.0-20250418054121-409ac98e3475 h1:zGCW7bHN9o67VxxK3eZr0k0h5HB+twDjx4cIc9q3dEI=
+gitee.com/xuyiping_admin/go_proto v0.0.0-20250418054121-409ac98e3475/go.mod h1:BKrFW6YLDectlQcQk3FYKBeXvjEiodAKJ5rq7O/QiPE=
+gitee.com/xuyiping_admin/go_proto v0.0.0-20250420125055-0d4da4a5a6fe h1:FSRq0AItdCCIisvxG0Ki+l7PxxaPFJSkOXN49x3LpnQ=
+gitee.com/xuyiping_admin/go_proto v0.0.0-20250420125055-0d4da4a5a6fe/go.mod h1:BKrFW6YLDectlQcQk3FYKBeXvjEiodAKJ5rq7O/QiPE=
+gitee.com/xuyiping_admin/go_proto v0.0.0-20250421082915-83b5518d3a0c h1:CuiDP9SfrSEiJ/wG0iz7wgIt9n38SeckUXscY6X5zBU=
+gitee.com/xuyiping_admin/go_proto v0.0.0-20250421082915-83b5518d3a0c/go.mod h1:BKrFW6YLDectlQcQk3FYKBeXvjEiodAKJ5rq7O/QiPE=
+gitee.com/xuyiping_admin/go_proto v0.0.0-20250423062840-8ac2189a39de h1:HNb5GAbIF+e9lDQ4e+AUSzmwQxpoEEsCbS0iDuC536Y=
+gitee.com/xuyiping_admin/go_proto v0.0.0-20250423062840-8ac2189a39de/go.mod h1:BKrFW6YLDectlQcQk3FYKBeXvjEiodAKJ5rq7O/QiPE=
+gitee.com/xuyiping_admin/go_proto v0.0.0-20250423064947-57beca75393d h1:1UsWm3g4/DXLG8c+GrzYGFLKnFAfDLsV+fvPMryd9CU=
+gitee.com/xuyiping_admin/go_proto v0.0.0-20250423064947-57beca75393d/go.mod h1:BKrFW6YLDectlQcQk3FYKBeXvjEiodAKJ5rq7O/QiPE=
+gitee.com/xuyiping_admin/go_proto v0.0.0-20250423065632-fc64fb76f946 h1:K41rOlpITHu+tn2SINKpBpk052Txj67l50/hQtXeZ7A=
+gitee.com/xuyiping_admin/go_proto v0.0.0-20250423065632-fc64fb76f946/go.mod h1:BKrFW6YLDectlQcQk3FYKBeXvjEiodAKJ5rq7O/QiPE=
+gitee.com/xuyiping_admin/go_proto v0.0.0-20250423070335-3521c22802be h1:fcqMFBsu9P1YwMjGqOh8NppxhBWTvu0sbOfrjmDm6no=
+gitee.com/xuyiping_admin/go_proto v0.0.0-20250423070335-3521c22802be/go.mod h1:BKrFW6YLDectlQcQk3FYKBeXvjEiodAKJ5rq7O/QiPE=
+gitee.com/xuyiping_admin/go_proto v0.0.0-20250423080234-2c477a4a0bec h1:iOaGH+q4slB4oUliGCXVnmBDGLM/+80A2+7VSWPdp0U=
+gitee.com/xuyiping_admin/go_proto v0.0.0-20250423080234-2c477a4a0bec/go.mod h1:BKrFW6YLDectlQcQk3FYKBeXvjEiodAKJ5rq7O/QiPE=
+gitee.com/xuyiping_admin/go_proto v0.0.0-20250424024203-073d6537225b h1:GUHRd7BvPy/dL+heSFd1r16Y3yZlnzqsD/FHDHAOfsg=
+gitee.com/xuyiping_admin/go_proto v0.0.0-20250424024203-073d6537225b/go.mod h1:BKrFW6YLDectlQcQk3FYKBeXvjEiodAKJ5rq7O/QiPE=
+gitee.com/xuyiping_admin/go_proto v0.0.0-20250424025835-b9c007f15d44 h1:bxYqBSegVy9cVdjTtjvLKK4HVT40moltROoaHVXNmZM=
+gitee.com/xuyiping_admin/go_proto v0.0.0-20250424025835-b9c007f15d44/go.mod h1:BKrFW6YLDectlQcQk3FYKBeXvjEiodAKJ5rq7O/QiPE=
+gitee.com/xuyiping_admin/go_proto v0.0.0-20250425011443-a83a84c81b7d h1:/2tQo6LnyvkvN+6ZhD/n5243lDvA25dcc35q+xbCl7A=
+gitee.com/xuyiping_admin/go_proto v0.0.0-20250425011443-a83a84c81b7d/go.mod h1:BKrFW6YLDectlQcQk3FYKBeXvjEiodAKJ5rq7O/QiPE=
+gitee.com/xuyiping_admin/go_proto v0.0.0-20250425013204-c2e3c2cb22b5 h1:9HdkH0TLT69nYrSpVNpK+AqRsEj6zsSAI0wyn3txEJo=
+gitee.com/xuyiping_admin/go_proto v0.0.0-20250425013204-c2e3c2cb22b5/go.mod h1:BKrFW6YLDectlQcQk3FYKBeXvjEiodAKJ5rq7O/QiPE=
+gitee.com/xuyiping_admin/go_proto v0.0.0-20250425022013-aea428d7468b h1:HsSfifHMvaNok/W1Q22axMJuYDSkpFnpFAIoO4Qo0NA=
+gitee.com/xuyiping_admin/go_proto v0.0.0-20250425022013-aea428d7468b/go.mod h1:BKrFW6YLDectlQcQk3FYKBeXvjEiodAKJ5rq7O/QiPE=
+gitee.com/xuyiping_admin/go_proto v0.0.0-20250425025152-491564f7b245 h1:WSBs/sMmKJBm+GBlFrvlAy9hYDgnxOSo9sFFsqdkvpo=
+gitee.com/xuyiping_admin/go_proto v0.0.0-20250425025152-491564f7b245/go.mod h1:BKrFW6YLDectlQcQk3FYKBeXvjEiodAKJ5rq7O/QiPE=
+gitee.com/xuyiping_admin/go_proto v0.0.0-20250425035842-15ee838b15e8 h1:8EsgPwBQn44Ntxxk8Ysat0IAAlFaEGwl/fszj38I+wc=
+gitee.com/xuyiping_admin/go_proto v0.0.0-20250425035842-15ee838b15e8/go.mod h1:BKrFW6YLDectlQcQk3FYKBeXvjEiodAKJ5rq7O/QiPE=
+gitee.com/xuyiping_admin/go_proto v0.0.0-20250427081352-93a6cd4b4694 h1:S4N8wO1YcY7Up+fPH6ggDsVaqRDxWKP3wT36m5sH3Ac=
+gitee.com/xuyiping_admin/go_proto v0.0.0-20250427081352-93a6cd4b4694/go.mod h1:BKrFW6YLDectlQcQk3FYKBeXvjEiodAKJ5rq7O/QiPE=
+gitee.com/xuyiping_admin/go_proto v0.0.0-20250427084730-8129eb58bb83 h1:BlREMV1B0/dRYN5Z9ju6FWzGnx6caPxzqwL/aMY/+XE=
+gitee.com/xuyiping_admin/go_proto v0.0.0-20250427084730-8129eb58bb83/go.mod h1:BKrFW6YLDectlQcQk3FYKBeXvjEiodAKJ5rq7O/QiPE=
+gitee.com/xuyiping_admin/go_proto v0.0.0-20250427091240-4d1fa51de52c h1:oRoz0BJWx7zMsXWA1x/2EILg9e0iPFprs23NWDcU2zA=
+gitee.com/xuyiping_admin/go_proto v0.0.0-20250427091240-4d1fa51de52c/go.mod h1:BKrFW6YLDectlQcQk3FYKBeXvjEiodAKJ5rq7O/QiPE=
+gitee.com/xuyiping_admin/go_proto v0.0.0-20250428030038-1fa928429b8b h1:hBfSqTM9XibBmMG5y7X3rQx2uvPJZge/SP/HP0/FDXA=
+gitee.com/xuyiping_admin/go_proto v0.0.0-20250428030038-1fa928429b8b/go.mod h1:BKrFW6YLDectlQcQk3FYKBeXvjEiodAKJ5rq7O/QiPE=
+gitee.com/xuyiping_admin/go_proto v0.0.0-20250428072657-d9a291b08ae9 h1:UQV4c+yPVVz50fDyCUweeezb3crtIV05nJq9cAGvPEs=
+gitee.com/xuyiping_admin/go_proto v0.0.0-20250428072657-d9a291b08ae9/go.mod h1:BKrFW6YLDectlQcQk3FYKBeXvjEiodAKJ5rq7O/QiPE=
+gitee.com/xuyiping_admin/go_proto v0.0.0-20250428074830-fb40d5445257 h1:zDyc5DgdWguRUqWYAL5nbRC3o5G30x+tRdjD+4VTUS0=
+gitee.com/xuyiping_admin/go_proto v0.0.0-20250428074830-fb40d5445257/go.mod h1:BKrFW6YLDectlQcQk3FYKBeXvjEiodAKJ5rq7O/QiPE=
+gitee.com/xuyiping_admin/go_proto v0.0.0-20250429024839-b282b4e1465a h1:slKL5wf3QH5ufX5CkJ+HDqkFjdp7VEujNV9C+q7nAbw=
+gitee.com/xuyiping_admin/go_proto v0.0.0-20250429024839-b282b4e1465a/go.mod h1:BKrFW6YLDectlQcQk3FYKBeXvjEiodAKJ5rq7O/QiPE=
+gitee.com/xuyiping_admin/go_proto v0.0.0-20250429032832-f43ab362a21b h1:9Shzi/Jp8uHcb47IVRtbuWPqbgv483UJaxsEoy3RD5s=
+gitee.com/xuyiping_admin/go_proto v0.0.0-20250429032832-f43ab362a21b/go.mod h1:BKrFW6YLDectlQcQk3FYKBeXvjEiodAKJ5rq7O/QiPE=
+gitee.com/xuyiping_admin/go_proto v0.0.0-20250430023822-7c5bf763451e h1:IWZC/FOxItSt33n+qzMdJhclW9oFzOnrze0SGC3jT+c=
+gitee.com/xuyiping_admin/go_proto v0.0.0-20250430023822-7c5bf763451e/go.mod h1:BKrFW6YLDectlQcQk3FYKBeXvjEiodAKJ5rq7O/QiPE=
+gitee.com/xuyiping_admin/go_proto v0.0.0-20250513060115-1841a323de9e h1:w+6CKL+iJYBwsmaLIUby7xeqlZKHlbL+GV/BeiEZBYo=
+gitee.com/xuyiping_admin/go_proto v0.0.0-20250513060115-1841a323de9e/go.mod h1:BKrFW6YLDectlQcQk3FYKBeXvjEiodAKJ5rq7O/QiPE=
+gitee.com/xuyiping_admin/go_proto v0.0.0-20250514013843-42f268c5cc98 h1:xAEEFgktRY0mQWtHcM4czFrR0ArCTH/5g2oQw2+qe5o=
+gitee.com/xuyiping_admin/go_proto v0.0.0-20250514013843-42f268c5cc98/go.mod h1:BKrFW6YLDectlQcQk3FYKBeXvjEiodAKJ5rq7O/QiPE=
+gitee.com/xuyiping_admin/go_proto v0.0.0-20250514015819-8b4ba0765b3c h1:F1lFnBnt1AIPzERyzGOGuMjZLEUkU2XDEeBH2MKMlK4=
+gitee.com/xuyiping_admin/go_proto v0.0.0-20250514015819-8b4ba0765b3c/go.mod h1:BKrFW6YLDectlQcQk3FYKBeXvjEiodAKJ5rq7O/QiPE=
+gitee.com/xuyiping_admin/go_proto v0.0.0-20250514022148-95b29436d061 h1:hKrOp5DfZp2F+VPG44YKP7DcmgcvAYz14yx7lLrMNMQ=
+gitee.com/xuyiping_admin/go_proto v0.0.0-20250514022148-95b29436d061/go.mod h1:BKrFW6YLDectlQcQk3FYKBeXvjEiodAKJ5rq7O/QiPE=
+gitee.com/xuyiping_admin/go_proto v0.0.0-20250514080018-cb20fb9800f8 h1:FWbFRaJGr2cvOyr7hbwC3DFBKWM1Y+5+UK+xliShcpU=
+gitee.com/xuyiping_admin/go_proto v0.0.0-20250514080018-cb20fb9800f8/go.mod h1:BKrFW6YLDectlQcQk3FYKBeXvjEiodAKJ5rq7O/QiPE=
+gitee.com/xuyiping_admin/go_proto v0.0.0-20250514085954-501d0d6cb26c h1:PkMErJiu4y8ft7jgjzVkfnV3OnqT2tKer5YnjS6fMRc=
+gitee.com/xuyiping_admin/go_proto v0.0.0-20250514085954-501d0d6cb26c/go.mod h1:BKrFW6YLDectlQcQk3FYKBeXvjEiodAKJ5rq7O/QiPE=
+gitee.com/xuyiping_admin/go_proto v0.0.0-20250515070041-363e94a8dbac h1:i6cw9nC8OCbBR+3lB5XX8o4NabcVR6gJ04dbEQxkmC0=
+gitee.com/xuyiping_admin/go_proto v0.0.0-20250515070041-363e94a8dbac/go.mod h1:BKrFW6YLDectlQcQk3FYKBeXvjEiodAKJ5rq7O/QiPE=
+gitee.com/xuyiping_admin/go_proto v0.0.0-20250521020200-04806a79ad46/go.mod h1:BKrFW6YLDectlQcQk3FYKBeXvjEiodAKJ5rq7O/QiPE=
+gitee.com/xuyiping_admin/go_proto v0.0.0-20250521021325-89e4fc71dd39 h1:/7MllucrQJnIxXY9fapeEOucVejTYY1IxsyIe8tLhlA=
+gitee.com/xuyiping_admin/go_proto v0.0.0-20250521021325-89e4fc71dd39/go.mod h1:BKrFW6YLDectlQcQk3FYKBeXvjEiodAKJ5rq7O/QiPE=
+gitee.com/xuyiping_admin/go_proto v0.0.0-20250521031814-2eb66df61c58 h1:PDG62cMpWaD/V11wty6sjiM4t2/sYFBg9u2S1tiZWbs=
+gitee.com/xuyiping_admin/go_proto v0.0.0-20250521031814-2eb66df61c58/go.mod h1:BKrFW6YLDectlQcQk3FYKBeXvjEiodAKJ5rq7O/QiPE=
+gitee.com/xuyiping_admin/go_proto v0.0.0-20250523054938-90895b21e2e6 h1:g/L5Dr4bkiWijpYC15mUrwBh+VSE400njGEvzgkmwpk=
+gitee.com/xuyiping_admin/go_proto v0.0.0-20250523054938-90895b21e2e6/go.mod h1:BKrFW6YLDectlQcQk3FYKBeXvjEiodAKJ5rq7O/QiPE=
+gitee.com/xuyiping_admin/go_proto v0.0.0-20250523062919-b893047f10c8 h1:j8gu75Sg2xvbC+dZ0UGFQMcNnU8AmBQ41KRgHXOcPik=
+gitee.com/xuyiping_admin/go_proto v0.0.0-20250523062919-b893047f10c8/go.mod h1:BKrFW6YLDectlQcQk3FYKBeXvjEiodAKJ5rq7O/QiPE=
+gitee.com/xuyiping_admin/go_proto v0.0.0-20250526035313-54b0f5e98983 h1:KWF+5I/HFeT27kiqn9DNhQrV4kIC9uxas9aalU7/LAE=
+gitee.com/xuyiping_admin/go_proto v0.0.0-20250526035313-54b0f5e98983/go.mod h1:BKrFW6YLDectlQcQk3FYKBeXvjEiodAKJ5rq7O/QiPE=
+gitee.com/xuyiping_admin/go_proto v0.0.0-20250526062319-25f58c4f9504 h1:0CUkSlzaAkVhwqWFdGRimK9WdycyDpeDPiMg6M2pBLI=
+gitee.com/xuyiping_admin/go_proto v0.0.0-20250526062319-25f58c4f9504/go.mod h1:BKrFW6YLDectlQcQk3FYKBeXvjEiodAKJ5rq7O/QiPE=
+gitee.com/xuyiping_admin/go_proto v0.0.0-20250526073830-beed24bc2e26 h1:XzWzg5AXdJYJeiS+jwfqapmIbrJ92ETi/qZ3iqjrC4c=
+gitee.com/xuyiping_admin/go_proto v0.0.0-20250526073830-beed24bc2e26/go.mod h1:BKrFW6YLDectlQcQk3FYKBeXvjEiodAKJ5rq7O/QiPE=
+gitee.com/xuyiping_admin/go_proto v0.0.0-20250526081009-3766cd67d900 h1:+QN7wEwmtLwY89R3iE6kMLgp/jS1QgsPczJdZUCoFe8=
+gitee.com/xuyiping_admin/go_proto v0.0.0-20250526081009-3766cd67d900/go.mod h1:BKrFW6YLDectlQcQk3FYKBeXvjEiodAKJ5rq7O/QiPE=
+gitee.com/xuyiping_admin/go_proto v0.0.0-20250526084133-ef8acddd1868 h1:JTXSBH8VO9uUweNyEy5o/OngSJ4xx2WmYVBn+/1vp1w=
+gitee.com/xuyiping_admin/go_proto v0.0.0-20250526084133-ef8acddd1868/go.mod h1:BKrFW6YLDectlQcQk3FYKBeXvjEiodAKJ5rq7O/QiPE=
+gitee.com/xuyiping_admin/go_proto v0.0.0-20250526090502-532d86d767de h1:+jPEpEljv/JyEPKFuNCeDbqbnyjCoGB14fucLMX+nv0=
+gitee.com/xuyiping_admin/go_proto v0.0.0-20250526090502-532d86d767de/go.mod h1:BKrFW6YLDectlQcQk3FYKBeXvjEiodAKJ5rq7O/QiPE=
+gitee.com/xuyiping_admin/go_proto v0.0.0-20250526091836-9e663a273679 h1:Ulh1ArxGRApui9E5UVNbCa3Cv3NnZIs3vbIpNy/3lA0=
+gitee.com/xuyiping_admin/go_proto v0.0.0-20250526091836-9e663a273679/go.mod h1:BKrFW6YLDectlQcQk3FYKBeXvjEiodAKJ5rq7O/QiPE=
+gitee.com/xuyiping_admin/go_proto v0.0.0-20250526094651-0cef600addd0 h1:nGmhS9BKCMgCz9zEVmygDIPNrolI9lb9uLV6NfoTxWE=
+gitee.com/xuyiping_admin/go_proto v0.0.0-20250526094651-0cef600addd0/go.mod h1:BKrFW6YLDectlQcQk3FYKBeXvjEiodAKJ5rq7O/QiPE=
+gitee.com/xuyiping_admin/go_proto v0.0.0-20250530040106-85ff23fe88f6 h1:UjId/C6diIwgYo/gh5cqVNpa9TSYyAsi1H/zlMqy/vs=
+gitee.com/xuyiping_admin/go_proto v0.0.0-20250530040106-85ff23fe88f6/go.mod h1:BKrFW6YLDectlQcQk3FYKBeXvjEiodAKJ5rq7O/QiPE=
+gitee.com/xuyiping_admin/go_proto v0.0.0-20250530051007-6ce1b83221af h1:3SBlTKUWyBz4l86klizGDt/xda/7j9PGsRiUyZb+yX8=
+gitee.com/xuyiping_admin/go_proto v0.0.0-20250530051007-6ce1b83221af/go.mod h1:BKrFW6YLDectlQcQk3FYKBeXvjEiodAKJ5rq7O/QiPE=
+gitee.com/xuyiping_admin/go_proto v0.0.0-20250530090709-1e32a1ade71d h1:upETgWdn+raGyxXdP1S70Z4gMuGZep9RjcLsWLIWeF0=
+gitee.com/xuyiping_admin/go_proto v0.0.0-20250530090709-1e32a1ade71d/go.mod h1:BKrFW6YLDectlQcQk3FYKBeXvjEiodAKJ5rq7O/QiPE=
+gitee.com/xuyiping_admin/go_proto v0.0.0-20250603022546-465f63606fa5/go.mod h1:BKrFW6YLDectlQcQk3FYKBeXvjEiodAKJ5rq7O/QiPE=
+gitee.com/xuyiping_admin/go_proto v0.0.0-20250603023510-4787586eb99f h1:ESNXR2m+12UOrrBMD1EnC732hf2H4LEwEhON/n1p6RU=
+gitee.com/xuyiping_admin/go_proto v0.0.0-20250603023510-4787586eb99f/go.mod h1:BKrFW6YLDectlQcQk3FYKBeXvjEiodAKJ5rq7O/QiPE=
+gitee.com/xuyiping_admin/go_proto v0.0.0-20250603030738-6435257183f9 h1:WDBhCOK21ffgnRsgrjYZHQY7LQ3nboMZFJ3P8BYEokQ=
+gitee.com/xuyiping_admin/go_proto v0.0.0-20250603030738-6435257183f9/go.mod h1:BKrFW6YLDectlQcQk3FYKBeXvjEiodAKJ5rq7O/QiPE=
+gitee.com/xuyiping_admin/go_proto v0.0.0-20250603061552-ea096c7a318d h1:9pPdB8ogY6KxRxjCJNMTss2IpsCAivGxEwWcRxBkGfA=
+gitee.com/xuyiping_admin/go_proto v0.0.0-20250603061552-ea096c7a318d/go.mod h1:BKrFW6YLDectlQcQk3FYKBeXvjEiodAKJ5rq7O/QiPE=
+gitee.com/xuyiping_admin/go_proto v0.0.0-20250603093243-82ebd7095700 h1:4s7hJ6Vr2FUydlb8gglDoJaPixKD4hnpvN78COUlAsw=
+gitee.com/xuyiping_admin/go_proto v0.0.0-20250603093243-82ebd7095700/go.mod h1:BKrFW6YLDectlQcQk3FYKBeXvjEiodAKJ5rq7O/QiPE=
+gitee.com/xuyiping_admin/go_proto v0.0.0-20250603093658-9b2144518ce7 h1:hAbUSi6s55gRk7nJfWq+d66rN5phpOggRqKxyaj1T/M=
+gitee.com/xuyiping_admin/go_proto v0.0.0-20250603093658-9b2144518ce7/go.mod h1:BKrFW6YLDectlQcQk3FYKBeXvjEiodAKJ5rq7O/QiPE=
+gitee.com/xuyiping_admin/go_proto v0.0.0-20250609030041-2973960e699d h1:OSpVELaFRbVykh3MJ4kfHaB6zd8b6Ibj22jDIl92nrU=
+gitee.com/xuyiping_admin/go_proto v0.0.0-20250609030041-2973960e699d/go.mod h1:BKrFW6YLDectlQcQk3FYKBeXvjEiodAKJ5rq7O/QiPE=
+gitee.com/xuyiping_admin/go_proto v0.0.0-20250609031915-1c289a676298 h1:oqXkLG8jwAxVl7uTF1o/C2MQVzhd7NrAL/ZNb3FyGQU=
+gitee.com/xuyiping_admin/go_proto v0.0.0-20250609031915-1c289a676298/go.mod h1:BKrFW6YLDectlQcQk3FYKBeXvjEiodAKJ5rq7O/QiPE=
+gitee.com/xuyiping_admin/go_proto v0.0.0-20250609060156-a9a9e557016e h1:xDIjht8Ol7BICYL3+Y9WZmM8JSM5bO+X8L09RskPkW0=
+gitee.com/xuyiping_admin/go_proto v0.0.0-20250609060156-a9a9e557016e/go.mod h1:BKrFW6YLDectlQcQk3FYKBeXvjEiodAKJ5rq7O/QiPE=
+gitee.com/xuyiping_admin/go_proto v0.0.0-20250611092629-ca524a76d63c h1:qmsW30h4rIb9gmyHp+zgIK58TIyFElUo9ntH0SRgU2Y=
+gitee.com/xuyiping_admin/go_proto v0.0.0-20250611092629-ca524a76d63c/go.mod h1:BKrFW6YLDectlQcQk3FYKBeXvjEiodAKJ5rq7O/QiPE=
+gitee.com/xuyiping_admin/go_proto v0.0.0-20250612024540-e99718a813f5 h1:wamhokmjikNkC3y4VPhgCGHZMmiRdIFykEm4nEucjvA=
+gitee.com/xuyiping_admin/go_proto v0.0.0-20250612024540-e99718a813f5/go.mod h1:BKrFW6YLDectlQcQk3FYKBeXvjEiodAKJ5rq7O/QiPE=
+gitee.com/xuyiping_admin/go_proto v0.0.0-20250612030131-3c8b446ca813 h1:ttDGhXoyHlmBy+R7WiiDU95VjVYjZfR2sx16VQ5B9KI=
+gitee.com/xuyiping_admin/go_proto v0.0.0-20250612030131-3c8b446ca813/go.mod h1:BKrFW6YLDectlQcQk3FYKBeXvjEiodAKJ5rq7O/QiPE=
+gitee.com/xuyiping_admin/go_proto v0.0.0-20250613015007-39c38d700b04 h1:sl0O3loQUllFaQKpEWzLxYo6X3JSA1XkkZyG1+jAelA=
+gitee.com/xuyiping_admin/go_proto v0.0.0-20250613015007-39c38d700b04/go.mod h1:BKrFW6YLDectlQcQk3FYKBeXvjEiodAKJ5rq7O/QiPE=
+gitee.com/xuyiping_admin/go_proto v0.0.0-20250613080456-430ffc4a0af3 h1:c5JtCkem6L1VsCw3QkkjLBrmVhWOOZfkcOhV23aVnKo=
+gitee.com/xuyiping_admin/go_proto v0.0.0-20250613080456-430ffc4a0af3/go.mod h1:BKrFW6YLDectlQcQk3FYKBeXvjEiodAKJ5rq7O/QiPE=
+gitee.com/xuyiping_admin/go_proto v0.0.0-20250616015302-2c053d0c13a9 h1:wJWnnwiFEk/Xz5sKTEFBZfzVdmIoK558cZ+WE6YR41o=
+gitee.com/xuyiping_admin/go_proto v0.0.0-20250616015302-2c053d0c13a9/go.mod h1:BKrFW6YLDectlQcQk3FYKBeXvjEiodAKJ5rq7O/QiPE=
+gitee.com/xuyiping_admin/go_proto v0.0.0-20250616065021-b2676e19b8b0 h1:f0mTagOzCakrey5Y945Kz1ws1pRuZJTTZtGSSpKIsjA=
+gitee.com/xuyiping_admin/go_proto v0.0.0-20250616065021-b2676e19b8b0/go.mod h1:BKrFW6YLDectlQcQk3FYKBeXvjEiodAKJ5rq7O/QiPE=
+gitee.com/xuyiping_admin/go_proto v0.0.0-20250616072406-218faa46b2cf h1:ttzKwwveo/hL43KVVFXipdXsQnFXbOM5zNkyi0ZNqdQ=
+gitee.com/xuyiping_admin/go_proto v0.0.0-20250616072406-218faa46b2cf/go.mod h1:BKrFW6YLDectlQcQk3FYKBeXvjEiodAKJ5rq7O/QiPE=
+gitee.com/xuyiping_admin/go_proto v0.0.0-20250616072843-0099184eb8ca h1:VrwccBb2bAZdXR+GZhUALHs8Jb79qKbBa1dn6MNcZGs=
+gitee.com/xuyiping_admin/go_proto v0.0.0-20250616072843-0099184eb8ca/go.mod h1:BKrFW6YLDectlQcQk3FYKBeXvjEiodAKJ5rq7O/QiPE=
+gitee.com/xuyiping_admin/go_proto v0.0.0-20250616080546-3ebf4d3f0874 h1:jD/wa9PorrqH0TDiasHboBdLgmlXw1oFOc0Fly7Fw/s=
+gitee.com/xuyiping_admin/go_proto v0.0.0-20250616080546-3ebf4d3f0874/go.mod h1:BKrFW6YLDectlQcQk3FYKBeXvjEiodAKJ5rq7O/QiPE=
+gitee.com/xuyiping_admin/pkg v0.0.0-20241108060137-caea58c59f5b h1:w05MxH7yqveRlaRbxHhbif5YjPrJFodRPfOjYhXn7Zk=
+gitee.com/xuyiping_admin/pkg v0.0.0-20241108060137-caea58c59f5b/go.mod h1:8tF25X6pE9WkFCczlNAC0K2mrjwKvhhp02I7o0HtDxY=
+gitee.com/xuyiping_admin/pkg v0.0.0-20250514071642-f92d2ac9a85d h1:vBXmMRggF7mZVPGRDgavZ87igJgkezwX0a3v1/XtIMQ=
+gitee.com/xuyiping_admin/pkg v0.0.0-20250514071642-f92d2ac9a85d/go.mod h1:8tF25X6pE9WkFCczlNAC0K2mrjwKvhhp02I7o0HtDxY=
+gitee.com/xuyiping_admin/pkg v0.0.0-20250613101634-36c36a2d27d0 h1:ZCOqEAnGm6+DTAhACigzWKbwMKtleb8/7OzP2xfHG7g=
+gitee.com/xuyiping_admin/pkg v0.0.0-20250613101634-36c36a2d27d0/go.mod h1:8tF25X6pE9WkFCczlNAC0K2mrjwKvhhp02I7o0HtDxY=
 github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
 github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
 github.com/Knetic/govaluate v3.0.1-0.20171022003610-9aa49832a739+incompatible/go.mod h1:r7JcOSlj0wfOMncg0iLm8Leh48TZaKVeNIfJntJ2wa0=
@@ -76,6 +198,7 @@ github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmV
 github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8=
 github.com/aryann/difflib v0.0.0-20170710044230-e206f873d14a/go.mod h1:DAHtR1m6lCRdSC2Tm3DSWRPvIPr6xNKyeHdqDQSQT+A=
 github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2 h1:DklsrG3dyBCFEj5IhUbnKptjxatkF07cF2ak3yi77so=
+github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2/go.mod h1:WaHUgvxTVq04UNunO+XhnAqY/wQc+bxr74GqbsZ/Jqw=
 github.com/aws/aws-lambda-go v1.13.3/go.mod h1:4UKl9IzQMoD+QF79YdCuzCwp8VbmG4VAQwij/eHl5CU=
 github.com/aws/aws-sdk-go v1.27.0/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo=
 github.com/aws/aws-sdk-go-v2 v0.18.0/go.mod h1:JWVYvqSMppoMJC0x5wdwiImzgXTI9FuZwxzkQq9wy+g=
@@ -122,7 +245,7 @@ github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3Ee
 github.com/coreos/go-systemd v0.0.0-20180511133405-39ca1b05acc7/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
 github.com/coreos/pkg v0.0.0-20160727233714-3ac0863d7acf/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA=
 github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
-github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
+github.com/cpuguy83/go-md2man/v2 v2.0.4/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
 github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY=
 github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
 github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
@@ -139,6 +262,8 @@ github.com/dustin/go-humanize v0.0.0-20171111073723-bb3d318650d4/go.mod h1:Htrtb
 github.com/eapache/go-resiliency v1.1.0/go.mod h1:kFI+JgMyC7bLPUVY133qvEBtVayf5mFgVsvEsIPBvNs=
 github.com/eapache/go-xerial-snappy v0.0.0-20180814174437-776d5712da21/go.mod h1:+020luEh2TKB4/GOp8oxxtq0Daoen/Cii55CzbTV6DU=
 github.com/eapache/queue v1.1.0/go.mod h1:6eCeP0CKFpHLu8blIFXhExK/dRa7WDZfr6jVFPTqq+I=
+github.com/eclipse/paho.mqtt.golang v1.4.3 h1:2kwcUGn8seMUfWndX0hGbvH8r7crgcJguQNCyp70xik=
+github.com/eclipse/paho.mqtt.golang v1.4.3/go.mod h1:CSYvoAlsMkhYOXh/oKyxa8EcBci6dVkLCbo5tTC1RIE=
 github.com/edsrzf/mmap-go v1.0.0/go.mod h1:YO35OhQPt3KJa3ryjFM5Bs14WD66h8eGKpfaBNrHW5M=
 github.com/eko/gocache v1.1.0 h1:FeER4gxA+lneYNeg/56obO6itD903LhPI0fT38J01WI=
 github.com/eko/gocache v1.1.0/go.mod h1:Q/KMUBMhv7CO4VahJStlTzMfFzP5dxTqs7D34NmJmVM=
@@ -291,6 +416,8 @@ github.com/gorilla/context v1.1.1/go.mod h1:kBGZzfjB9CEq2AlWe17Uuf7NDRt0dE0s8S51
 github.com/gorilla/mux v1.6.2/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs=
 github.com/gorilla/mux v1.7.3/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs=
 github.com/gorilla/websocket v0.0.0-20170926233335-4201258b820c/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ=
+github.com/gorilla/websocket v1.5.3 h1:saDtZ6Pbx/0u+bgYQ3q96pZgCzfhKXGPqt7kZ72aNNg=
+github.com/gorilla/websocket v1.5.3/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
 github.com/grpc-ecosystem/go-grpc-middleware v1.0.1-0.20190118093823-f849b5445de4/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs=
 github.com/grpc-ecosystem/go-grpc-middleware v1.4.0 h1:UH//fgunKIs4JdUbpDl1VZCDaL56wXCB/5+wF6uHfaI=
 github.com/grpc-ecosystem/go-grpc-middleware v1.4.0/go.mod h1:g5qyo/la0ALbONm6Vbp88Yd8NsDy6rZz+RcrMPxvld8=
@@ -426,6 +553,7 @@ github.com/nats-io/nuid v1.0.1/go.mod h1:19wcPz3Ph3q0Jbyiqsd0kePYG7A95tJPxeL+1OS
 github.com/nxadm/tail v1.4.4 h1:DQuhQpB1tVlglWS2hLQ5OV6B5r8aGxSrPc5Qo6uTN78=
 github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A=
 github.com/nyaruka/phonenumbers v1.1.7 h1:5UUI9hE79Kk0dymSquXbMYB7IlNDNhvu2aNlJpm9et8=
+github.com/nyaruka/phonenumbers v1.1.7/go.mod h1:DC7jZd321FqUe+qWSNcHi10tyIyGNXGcNbfkPvdp1Vs=
 github.com/oklog/oklog v0.3.2/go.mod h1:FCV+B7mhrz4o+ueLpx+KqkyXRGMWOYEvfiXtdGtbWGs=
 github.com/oklog/run v1.0.0/go.mod h1:dlhp/R75TPv97u0XWUtDeV/lRKWPKSdTuV0TZvrmrQA=
 github.com/olekukonko/tablewriter v0.0.0-20170122224234-a0225b3f23b5/go.mod h1:vsDQFd/mU46D+Z4whnwzcISnGGzXWMclvtLoiIKAKIo=
@@ -452,6 +580,8 @@ github.com/openzipkin/zipkin-go v0.2.1/go.mod h1:NaW6tEwdmWMaCDZzg8sh+IBNOxHMPnh
 github.com/openzipkin/zipkin-go v0.2.2/go.mod h1:NaW6tEwdmWMaCDZzg8sh+IBNOxHMPnhQw8ySjnjRyN4=
 github.com/pact-foundation/pact-go v1.0.4/go.mod h1:uExwJY4kCzNPcHRj+hCR/HBbOOIwwtUjcrb0b5/5kLM=
 github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc=
+github.com/patrickmn/go-cache v2.1.0+incompatible h1:HRMgzkcYKYpi3C8ajMPV8OFXaaRUnok+kx1WdO15EQc=
+github.com/patrickmn/go-cache v2.1.0+incompatible/go.mod h1:3Qf8kWWT7OJRJbdiICTKqZju1ZixQ/KpMGzzAfe6+WQ=
 github.com/pborman/uuid v1.2.0/go.mod h1:X/NO0urCmaxf9VXbdlT7C2Yzkj2IKimNn4k+gtPdI/k=
 github.com/pelletier/go-toml/v2 v2.0.1/go.mod h1:r9LEWfGN8R5k0VXJ+0BkIe7MYkRdwZOjgMj2KwnJFUo=
 github.com/pelletier/go-toml/v2 v2.1.0 h1:FnwAJ4oYMvbT/34k9zzHuZNrhlz48GB3/s6at6/MHO4=
@@ -538,8 +668,8 @@ github.com/spf13/cast v1.3.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkU
 github.com/spf13/cast v1.5.1 h1:R+kOtfhWQE6TVQzY+4D7wJLBgkdVasCEFxSUBYBYIlA=
 github.com/spf13/cast v1.5.1/go.mod h1:b9PdjNptOpzXr7Rq1q9gJML/2cdGQAo69NKzQ10KN48=
 github.com/spf13/cobra v0.0.3/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ=
-github.com/spf13/cobra v1.7.0 h1:hyqWnYt1ZQShIddO5kBpj3vu05/++x6tJ6dg8EC572I=
-github.com/spf13/cobra v1.7.0/go.mod h1:uLxZILRyS/50WlhOIKD7W6V5bgeIt+4sICxh6uRMrb0=
+github.com/spf13/cobra v1.8.1 h1:e5/vxKd/rZsfSJMUX1agtjeTDf+qv1/JdBF8gg5k9ZM=
+github.com/spf13/cobra v1.8.1/go.mod h1:wHxEcudfqmLYa8iTfL+OuZPbBZkmvliBWKIezN3kD9Y=
 github.com/spf13/jwalterweatherman v1.1.0 h1:ue6voC5bR5F8YxI5S67j9i582FU4Qvo2bmqnqMYADFk=
 github.com/spf13/jwalterweatherman v1.1.0/go.mod h1:aNWZUN0dPAAO/Ljvb5BEdw96iTZ0EXowPYD95IqWIGo=
 github.com/spf13/pflag v1.0.1/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
@@ -590,6 +720,8 @@ github.com/xuri/nfp v0.0.0-20230919160717-d98342af3f05 h1:qhbILQo1K3mphbwKh1vNm4
 github.com/xuri/nfp v0.0.0-20230919160717-d98342af3f05/go.mod h1:WwHg+CVyzlv/TX9xqBFXEZAuxOPxn2k1GNHwG41IIUQ=
 github.com/xwb1989/sqlparser v0.0.0-20180606152119-120387863bf2 h1:zzrxE1FKn5ryBNl9eKOeqQ58Y/Qpo3Q9QNxKHX5uzzQ=
 github.com/xwb1989/sqlparser v0.0.0-20180606152119-120387863bf2/go.mod h1:hzfGeIUDq/j97IG+FhNqkowIyEcD88LrW6fyU3K3WqY=
+github.com/yangxikun/gin-limit-by-key v0.0.0-20190512072151-520697354d5f h1:ERcGMTmr8QfJ2KPgKGnyKG5QEEK+YxraUch0I0gN8uc=
+github.com/yangxikun/gin-limit-by-key v0.0.0-20190512072151-520697354d5f/go.mod h1:ysnqe7upAAVOSwxQZHAMPXbO80SFzg/ArkjnIJIcuGE=
 github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
 github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
 github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
@@ -629,8 +761,8 @@ go.uber.org/zap v1.18.1/go.mod h1:xg/QME4nWcxGxrpdeYfq7UvYrLh66cuVKdrbD1XF/NI=
 go.uber.org/zap v1.24.0 h1:FiJd5l1UOLj0wCgbSE0rwwXHzEdAZS6hiiSnxJN/D60=
 go.uber.org/zap v1.24.0/go.mod h1:2kMP+WWQ8aoFoedH3T2sq6iJ2yDWpHbP0f6MQbS9Gkg=
 golang.org/x/arch v0.0.0-20210923205945-b76863e36670/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8=
-golang.org/x/arch v0.5.0 h1:jpGode6huXQxcskEIpOCvrU+tzo81b6+oFLUYXWtH/Y=
-golang.org/x/arch v0.5.0/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8=
+golang.org/x/arch v0.3.0 h1:02VY4/ZcO/gBOH6PUaoiptASxtXU10jazRCP865E97k=
+golang.org/x/arch v0.3.0/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8=
 golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
 golang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
 golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
@@ -644,8 +776,8 @@ golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97/go.mod h1:GvvjBRRGRdwPK5y
 golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
 golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
 golang.org/x/crypto v0.12.0/go.mod h1:NF0Gs7EO5K4qLn+Ylc+fih8BSTeIjAP05siRnAh98yw=
-golang.org/x/crypto v0.21.0 h1:X31++rzVUdKhX5sWmSOFZxx8UW/ldWx55cbf08iNAMA=
-golang.org/x/crypto v0.21.0/go.mod h1:0BP7YvVV9gBbVKyeTG0Gyn+gZm94bibOW5BjDEYAOMs=
+golang.org/x/crypto v0.23.0 h1:dIJU/v2J8Mdglj/8rJ6UUOM3Zc9zLZxVZwwxMooUSAI=
+golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8=
 golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
 golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
 golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
@@ -732,8 +864,8 @@ golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug
 golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
 golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg=
 golang.org/x/net v0.14.0/go.mod h1:PpSgVXXLK0OxS0F31C1/tv6XNguvCrnXIDrFMspZIUI=
-golang.org/x/net v0.22.0 h1:9sGLhx7iRIHEiX0oAJ3MRZMUCElJgy7Br1nO+AMN3Tc=
-golang.org/x/net v0.22.0/go.mod h1:JKghWKKOSdJwpW2GEx0Ja7fmaKnMsbu+MWVZTokSYmg=
+golang.org/x/net v0.25.0 h1:d/OCCoBEUq33pjydKrGQhw7IlUPI2Oylr+8qLx49kac=
+golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM=
 golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
 golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
 golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
@@ -756,8 +888,8 @@ golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJ
 golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
-golang.org/x/sync v0.6.0 h1:5BMeUDZ7vkXGfEr1x9B4bRcTH4lpkTkpdh0T/J+qjbQ=
-golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
+golang.org/x/sync v0.7.0 h1:YsImfSBoP9QPYL0xyKJPq0gcaJdG3rInoqxTWbfQu9M=
+golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
 golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
 golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
 golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
@@ -825,8 +957,8 @@ golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.18.0 h1:DBdB3niSjOA/O0blCZBqDefyWNYveAYMNF1Wum0DYQ4=
-golang.org/x/sys v0.18.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
+golang.org/x/sys v0.20.0 h1:Od9JTbYCk261bKm4M/mw7AklTlFYIa0bIp9BgSm1S8Y=
+golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
 golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
 golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
 golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=

+ 388 - 2
http/handler/analysis/analysis.go

@@ -4,23 +4,409 @@ import (
 	"kpt-pasture/http/middleware"
 	"net/http"
 
+	"gitee.com/xuyiping_admin/pkg/valid"
+
 	pasturePb "gitee.com/xuyiping_admin/go_proto/proto/go/backend/cow"
 	"gitee.com/xuyiping_admin/pkg/apierr"
 	"gitee.com/xuyiping_admin/pkg/ginutil"
 	"github.com/gin-gonic/gin"
 )
 
-func GrowthCurve(c *gin.Context) {
+func WeightScatterPlot(c *gin.Context) {
 	var req pasturePb.SearchGrowthCurvesRequest
 	if err := ginutil.BindProto(c, &req); err != nil {
 		apierr.AbortBadRequest(c, http.StatusBadRequest, err)
 		return
 	}
 
-	res, err := middleware.Dependency(c).StoreEventHub.OpsService.GrowthCurve(c, &req)
+	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.WeightScatterPlot(c, &req, pagination)
+	if err != nil {
+		apierr.ClassifiedAbort(c, err)
+		return
+	}
+
+	ginutil.JSONResp(c, res)
+}
+
+func WeightRange(c *gin.Context) {
+	var req pasturePb.WeightRangeRequest
+	if err := ginutil.BindProto(c, &req); err != nil {
+		apierr.AbortBadRequest(c, http.StatusBadRequest, err)
+		return
+	}
+
+	if err := valid.ValidateStruct(&req,
+		valid.Field(&req.MaxWeight, valid.Required),
+	); err != nil {
+		apierr.AbortBadRequest(c, http.StatusBadRequest, err)
+		return
+	}
+
+	pagination := &pasturePb.PaginationModel{
+		Page:       int32(c.GetInt(middleware.Page)),
+		PageSize:   int32(c.GetInt(middleware.PageSize)),
+		PageOffset: int32(c.GetInt(middleware.PageOffset)),
+	}
+
+	res, err := middleware.Dependency(c).StoreEventHub.OpsService.WeightRange(c, &req, pagination)
+	if err != nil {
+		apierr.ClassifiedAbort(c, err)
+		return
+	}
+
+	ginutil.JSONResp(c, res)
+}
+
+func MatingTimeLy(c *gin.Context) {
+	var req pasturePb.MatingTimelyRequest
+	if err := ginutil.BindProto(c, &req); err != nil {
+		apierr.AbortBadRequest(c, http.StatusBadRequest, err)
+		return
+	}
+
+	if err := valid.ValidateStruct(&req,
+		valid.Field(&req.StartDayAt, valid.Required),
+		valid.Field(&req.EndDayAt, valid.Required),
+		valid.Field(&req.CowType, valid.Required),
+	); err != nil {
+		apierr.AbortBadRequest(c, http.StatusBadRequest, err)
+		return
+	}
+
+	res, err := middleware.Dependency(c).StoreEventHub.OpsService.MatingTimely(c, &req)
+	if err != nil {
+		apierr.ClassifiedAbort(c, err)
+		return
+	}
+
+	c.JSON(http.StatusOK, res)
+}
+
+func PenWeight(c *gin.Context) {
+	var req pasturePb.PenWeightRequest
+	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.PenWeight(c, &req, pagination)
+	if err != nil {
+		apierr.ClassifiedAbort(c, err)
+		return
+	}
+	ginutil.JSONResp(c, res)
+}
+
+func TwentyOnePregnantRate(c *gin.Context) {
+	var req pasturePb.TwentyOnePregnantRateRequest
+	if err := ginutil.BindProto(c, &req); err != nil {
+		apierr.AbortBadRequest(c, http.StatusBadRequest, err)
+		return
+	}
+
+	if err := valid.ValidateStruct(&req,
+		valid.Field(&req.StartDate, valid.Required),
+		valid.Field(&req.EndDate, valid.Required),
+		valid.Field(&req.CowType, valid.Required),
+	); err != nil {
+		apierr.AbortBadRequest(c, http.StatusBadRequest, err)
+		return
+	}
+
+	res, err := middleware.Dependency(c).StoreEventHub.OpsService.TwentyOnePregnantRate(c, &req)
+	if err != nil {
+		apierr.ClassifiedAbort(c, err)
+		return
+	}
+	ginutil.JSONResp(c, res)
+}
+
+func TwentyOnePregnantDetail(c *gin.Context) {
+	var req pasturePb.TwentyOnePregnantDetailsRequest
+	if err := ginutil.BindProto(c, &req); err != nil {
+		apierr.AbortBadRequest(c, http.StatusBadRequest, err)
+		return
+	}
+
+	if err := valid.ValidateStruct(&req,
+		valid.Field(&req.StartDate, valid.Required),
+		valid.Field(&req.EndDate, valid.Required),
+		valid.Field(&req.CowType, valid.Required),
+		valid.Field(&req.Type, valid.Required),
+	); err != nil {
+		apierr.AbortBadRequest(c, http.StatusBadRequest, err)
+		return
+	}
+
+	res, err := middleware.Dependency(c).StoreEventHub.OpsService.TwentyOnePregnantDetail(c, &req)
+	if err != nil {
+		apierr.ClassifiedAbort(c, err)
+		return
+	}
+	ginutil.JSONResp(c, res)
+}
+
+func AbortionRate(c *gin.Context) {
+	var req pasturePb.AbortionRateRequest
+	if err := ginutil.BindProto(c, &req); err != nil {
+		apierr.AbortBadRequest(c, http.StatusBadRequest, err)
+		return
+	}
+
+	if err := valid.ValidateStruct(&req,
+		valid.Field(&req.StartDayTime, valid.Required),
+		valid.Field(&req.EndDayTime, valid.Required),
+		valid.Field(&req.CowType, valid.Required),
+	); err != nil {
+		apierr.AbortBadRequest(c, http.StatusBadRequest, err)
+		return
+	}
+
+	res, err := middleware.Dependency(c).StoreEventHub.OpsService.AbortionRate(c, &req)
+	if err != nil {
+		apierr.ClassifiedAbort(c, err)
+		return
+	}
+	ginutil.JSONResp(c, res)
+}
+
+func PregnancyReport(c *gin.Context) {
+	var req pasturePb.PregnancyReportRequest
+	if err := ginutil.BindProto(c, &req); err != nil {
+		apierr.AbortBadRequest(c, http.StatusBadRequest, err)
+		return
+	}
+
+	if err := valid.ValidateStruct(&req,
+		valid.Field(&req.StartDayTime, valid.Required),
+		valid.Field(&req.EndDayTime, valid.Required),
+		valid.Field(&req.CowType, valid.Required),
+	); err != nil {
+		apierr.AbortBadRequest(c, http.StatusBadRequest, err)
+		return
+	}
+
+	pagination := &pasturePb.PaginationModel{
+		Page:       int32(c.GetInt(middleware.Page)),
+		PageSize:   int32(c.GetInt(middleware.PageSize)),
+		PageOffset: int32(c.GetInt(middleware.PageOffset)),
+	}
+
+	res, err := middleware.Dependency(c).StoreEventHub.OpsService.PregnancyReport(c, &req, pagination)
+	if err != nil {
+		apierr.ClassifiedAbort(c, err)
+		return
+	}
+	ginutil.JSONResp(c, res)
+}
+
+func CalvingReport(c *gin.Context) {
+	var req pasturePb.CalvingReportRequest
+	if err := ginutil.BindProto(c, &req); err != nil {
+		apierr.AbortBadRequest(c, http.StatusBadRequest, err)
+		return
+	}
+
+	if err := valid.ValidateStruct(&req,
+		valid.Field(&req.StartDayTime, valid.Required),
+		valid.Field(&req.EndDayTime, valid.Required),
+		valid.Field(&req.LateLabor, valid.Required),
+		valid.Field(&req.PrematureLabor, valid.Required),
+		valid.Field(&req.AnalysisMethod, valid.Required),
+	); err != nil {
+		apierr.AbortBadRequest(c, http.StatusBadRequest, err)
+		return
+	}
+
+	res, err := middleware.Dependency(c).StoreEventHub.OpsService.CalvingReport(c, &req)
+	if err != nil {
+		apierr.ClassifiedAbort(c, err)
+		return
+	}
+	ginutil.JSONResp(c, res)
+}
+
+func DiseaseCureReport(c *gin.Context) {
+	var req pasturePb.DiseaseCureRateRequest
+	if err := ginutil.BindProto(c, &req); err != nil {
+		apierr.AbortBadRequest(c, http.StatusBadRequest, err)
+		return
+	}
+
+	if err := valid.ValidateStruct(&req,
+		valid.Field(&req.StartDayTime, valid.Required),
+		valid.Field(&req.EndDayTime, valid.Required),
+		valid.Field(&req.AnalysisMethod, valid.Required),
+	); err != nil {
+		apierr.AbortBadRequest(c, http.StatusBadRequest, err)
+		return
+	}
+
+	res, err := middleware.Dependency(c).StoreEventHub.OpsService.DiseaseCureReport(c, &req)
+	if err != nil {
+		apierr.ClassifiedAbort(c, err)
+		return
+	}
+	ginutil.JSONResp(c, res)
+}
+
+func SaleCowReport(c *gin.Context) {
+	var req pasturePb.SaleCowReportRequest
+	if err := ginutil.BindProto(c, &req); err != nil {
+		apierr.AbortBadRequest(c, http.StatusBadRequest, err)
+		return
+	}
+
+	if err := valid.ValidateStruct(&req,
+		valid.Field(&req.StartDay, valid.Required),
+		valid.Field(&req.EndDay, valid.Required),
+		valid.Field(&req.AnalysisMethod, valid.Required),
+	); err != nil {
+		apierr.AbortBadRequest(c, http.StatusBadRequest, err)
+		return
+	}
+
+	res, err := middleware.Dependency(c).StoreEventHub.OpsService.SaleCowReport(c, &req)
+	if err != nil {
+		apierr.ClassifiedAbort(c, err)
+		return
+	}
+	ginutil.JSONResp(c, res)
+}
+
+func SingleFactorInfantSurvivalRate(c *gin.Context) {
+	var req pasturePb.SingleFactorPregnancyRateRequest
+	if err := ginutil.BindProto(c, &req); err != nil {
+		apierr.AbortBadRequest(c, http.StatusBadRequest, err)
+		return
+	}
+
+	if err := valid.ValidateStruct(&req,
+		valid.Field(&req.StartDayTime, valid.Required),
+		valid.Field(&req.EndDayTime, valid.Required),
+		valid.Field(&req.AnalysisMethod, valid.Required),
+		valid.Field(&req.CowType, valid.Required),
+		valid.Field(&req.LactInterval, valid.Required),
+	); err != nil {
+		apierr.AbortBadRequest(c, http.StatusBadRequest, err)
+		return
+	}
+
+	res, err := middleware.Dependency(c).StoreEventHub.OpsService.SingleFactorInfantSurvivalRateAnalysis(c, &req)
 	if err != nil {
 		apierr.ClassifiedAbort(c, err)
 		return
 	}
+	ginutil.JSONResp(c, res)
+}
+
+func MultiFactorInfantSurvivalRate(c *gin.Context) {
+	var req pasturePb.MultiFactorPregnancyRateRequest
+	if err := ginutil.BindProto(c, &req); err != nil {
+		apierr.AbortBadRequest(c, http.StatusBadRequest, err)
+		return
+	}
+
+	if err := valid.ValidateStruct(&req,
+		valid.Field(&req.StartDayTime, valid.Required),
+		valid.Field(&req.EndDayTime, valid.Required),
+		valid.Field(&req.CowType, valid.Required),
+		valid.Field(&req.XAxle, valid.Required),
+		valid.Field(&req.YAxle, valid.Required),
+	); err != nil {
+		apierr.AbortBadRequest(c, http.StatusBadRequest, err)
+		return
+	}
+
+	res, err := middleware.Dependency(c).StoreEventHub.OpsService.MultipleFactorAnalysis(c, &req)
+	if err != nil {
+		apierr.ClassifiedAbort(c, err)
+		return
+	}
+
 	c.JSON(http.StatusOK, res)
 }
+
+func PenBehaviorAnalysis(c *gin.Context) {
+	var req pasturePb.BarnBehaviorCurveRequest
+	if err := ginutil.BindProto(c, &req); err != nil {
+		apierr.AbortBadRequest(c, http.StatusBadRequest, err)
+		return
+	}
+
+	if err := valid.ValidateStruct(&req,
+		valid.Field(&req.StartAt, valid.Required),
+		valid.Field(&req.EndAt, valid.Required),
+		valid.Field(&req.PenId, valid.Required),
+	); err != nil {
+		apierr.AbortBadRequest(c, http.StatusBadRequest, err)
+		return
+	}
+
+	res, err := middleware.Dependency(c).StoreEventHub.OpsService.PenBehavior(c, &req)
+	if err != nil {
+		apierr.ClassifiedAbort(c, err)
+		return
+	}
+
+	ginutil.JSONResp(c, res)
+}
+
+func PenBehaviorDaily(c *gin.Context) {
+	var req pasturePb.BarnMonitorRequest
+	if err := ginutil.BindProto(c, &req); err != nil {
+		apierr.AbortBadRequest(c, http.StatusBadRequest, err)
+		return
+	}
+
+	if err := valid.ValidateStruct(&req,
+		valid.Field(&req.StartAt, valid.Required),
+		valid.Field(&req.EndAt, valid.Required),
+	); err != nil {
+		apierr.AbortBadRequest(c, http.StatusBadRequest, err)
+		return
+	}
+
+	res, err := middleware.Dependency(c).StoreEventHub.OpsService.PenBehaviorDaily(c, &req)
+	if err != nil {
+		apierr.ClassifiedAbort(c, err)
+		return
+	}
+	c.JSON(http.StatusOK, res)
+}
+
+func CowBehaviorDistribution(c *gin.Context) {
+	var req pasturePb.CowBehaviorDistributionRequest
+	if err := ginutil.BindProto(c, &req); err != nil {
+		apierr.AbortBadRequest(c, http.StatusBadRequest, err)
+		return
+	}
+
+	if err := valid.ValidateStruct(&req,
+		valid.Field(&req.DateTime, valid.Required),
+		valid.Field(&req.BehaviorKind, valid.Required),
+	); err != nil {
+		apierr.AbortBadRequest(c, http.StatusBadRequest, err)
+		return
+	}
+
+	res, err := middleware.Dependency(c).StoreEventHub.OpsService.CowBehaviorDistribution(c, &req)
+	if err != nil {
+		apierr.ClassifiedAbort(c, err)
+		return
+	}
+
+	ginutil.JSONResp(c, res)
+}

+ 46 - 8
http/handler/config/config.go

@@ -2,6 +2,7 @@ package config
 
 import (
 	"kpt-pasture/http/middleware"
+	"net/http"
 	"strconv"
 
 	"gitee.com/xuyiping_admin/pkg/valid"
@@ -23,11 +24,12 @@ func BarnTypeOptions(c *gin.Context) {
 func BarnListOptions(c *gin.Context) {
 	penTypeStr := c.Query("pen_type")
 	penTypeId, _ := strconv.Atoi(penTypeStr)
+	isAll := c.Query("is_all")
 	if err := valid.Validate(penTypeId, valid.Required, valid.Min(-1)); err != nil {
 		apierr.ClassifiedAbort(c, err)
 		return
 	}
-	res, err := middleware.Dependency(c).StoreEventHub.OpsService.BarnListOptions(c, penTypeId)
+	res, err := middleware.Dependency(c).StoreEventHub.OpsService.BarnListOptions(c, penTypeId, isAll)
 	if err != nil {
 		apierr.ClassifiedAbort(c, err)
 		return
@@ -36,7 +38,8 @@ func BarnListOptions(c *gin.Context) {
 }
 
 func DiseaseTypeOptions(c *gin.Context) {
-	res, err := middleware.Dependency(c).StoreEventHub.OpsService.DiseaseTypeOptions(c)
+	isChildren := c.Query("is_children")
+	res, err := middleware.Dependency(c).StoreEventHub.OpsService.DiseaseTypeOptions(c, isChildren)
 	if err != nil {
 		apierr.ClassifiedAbort(c, err)
 		return
@@ -51,6 +54,16 @@ func DiseaseOptions(c *gin.Context) {
 	}
 	ginutil.JSONResp(c, res)
 }
+
+func PrescriptionOptions(c *gin.Context) {
+	res, err := middleware.Dependency(c).StoreEventHub.OpsService.PrescriptionOptions(c)
+	if err != nil {
+		apierr.ClassifiedAbort(c, err)
+		return
+	}
+	ginutil.JSONResp(c, res)
+}
+
 func BreedStatusOptions(c *gin.Context) {
 	res, err := middleware.Dependency(c).StoreEventHub.OpsService.BreedStatusOptions(c)
 	if err != nil {
@@ -79,7 +92,14 @@ func CowSourceOptions(c *gin.Context) {
 }
 
 func CowTypeOptions(c *gin.Context) {
-	res, err := middleware.Dependency(c).StoreEventHub.OpsService.CowTypeOptions(c)
+	optionName := c.Query("option_name")
+	isAll := c.Query("is_all")
+	if err := valid.Validate(optionName, valid.Required, valid.Length(1, 50)); err != nil {
+		apierr.ClassifiedAbort(c, err)
+		return
+	}
+
+	res, err := middleware.Dependency(c).StoreEventHub.OpsService.CowTypeOptions(c, optionName, isAll)
 	if err != nil {
 		apierr.ClassifiedAbort(c, err)
 		return
@@ -97,13 +117,12 @@ func CowTransferPenReasonOptions(c *gin.Context) {
 }
 
 func SystemUserOptions(c *gin.Context) {
-	depIdStr := c.Query("dept_id")
-	depId, _ := strconv.Atoi(depIdStr)
-	if err := valid.Validate(depId, valid.Required, valid.Min(-1)); err != nil {
+	depName := c.Query("dept_name")
+	if err := valid.Validate(depName, valid.Required); err != nil {
 		apierr.ClassifiedAbort(c, err)
 		return
 	}
-	res, err := middleware.Dependency(c).StoreEventHub.OpsService.SystemUserOptions(c, depId)
+	res, err := middleware.Dependency(c).StoreEventHub.OpsService.SystemUserOptions(c, depName)
 	if err != nil {
 		apierr.ClassifiedAbort(c, err)
 		return
@@ -122,14 +141,33 @@ func BullListOptions(c *gin.Context) {
 
 func SystemBaseConfigOptions(c *gin.Context) {
 	optionName := c.Query("option_name")
+	isAll := c.Query("is_all")
 	if err := valid.Validate(optionName, valid.Required, valid.Length(1, 50)); err != nil {
 		apierr.ClassifiedAbort(c, err)
 		return
 	}
-	res, err := middleware.Dependency(c).StoreEventHub.OpsService.SystemBaseConfigOptions(c, optionName)
+	res, err := middleware.Dependency(c).StoreEventHub.OpsService.SystemBaseConfigOptions(c, optionName, isAll)
 	if err != nil {
 		apierr.ClassifiedAbort(c, err)
 		return
 	}
 	ginutil.JSONResp(c, res)
 }
+
+func FindCowHistoryBatchNumber(c *gin.Context) {
+	res, err := middleware.BackendOperation(c).OpsService.FindCowHistoryBatchNumber(c)
+	if err != nil {
+		apierr.ClassifiedAbort(c, err)
+		return
+	}
+	c.JSON(http.StatusOK, res)
+}
+
+func GenerateBatchNumber(c *gin.Context) {
+	res, err := middleware.BackendOperation(c).OpsService.GenerateBatchNumber(c)
+	if err != nil {
+		apierr.ClassifiedAbort(c, err)
+		return
+	}
+	c.JSON(http.StatusOK, res)
+}

+ 188 - 1
http/handler/cow/cow.go

@@ -4,12 +4,29 @@ import (
 	"kpt-pasture/http/middleware"
 	"net/http"
 
+	"gitee.com/xuyiping_admin/pkg/valid"
+
 	pasturePb "gitee.com/xuyiping_admin/go_proto/proto/go/backend/cow"
 	"gitee.com/xuyiping_admin/pkg/apierr"
 	"gitee.com/xuyiping_admin/pkg/ginutil"
 	"github.com/gin-gonic/gin"
 )
 
+func Detail(c *gin.Context) {
+	var req pasturePb.SearchEventRequest
+	if err := ginutil.BindProto(c, &req); err != nil {
+		apierr.AbortBadRequest(c, http.StatusBadRequest, err)
+		return
+	}
+
+	res, err := middleware.Dependency(c).StoreEventHub.OpsService.Detail(c, &req)
+	if err != nil {
+		apierr.AbortBadRequest(c, http.StatusOK, err)
+		return
+	}
+	ginutil.JSONResp(c, res)
+}
+
 func List(c *gin.Context) {
 	var req pasturePb.SearchEventRequest
 	if err := ginutil.BindProto(c, &req); err != nil {
@@ -23,7 +40,177 @@ func List(c *gin.Context) {
 		PageOffset: int32(c.GetInt(middleware.PageOffset)),
 	}
 
-	res, err := middleware.Dependency(c).StoreEventHub.OpsService.CowList(c, &req, pagination)
+	res, err := middleware.Dependency(c).StoreEventHub.OpsService.List(c, &req, pagination)
+	if err != nil {
+		apierr.ClassifiedAbort(c, err)
+		return
+	}
+	ginutil.JSONResp(c, res)
+}
+
+func EventList(c *gin.Context) {
+	var req pasturePb.SearchCowEventListRequest
+	if err := ginutil.BindProto(c, &req); err != nil {
+		apierr.AbortBadRequest(c, http.StatusBadRequest, err)
+		return
+	}
+
+	if err := valid.ValidateStruct(&req,
+		valid.Field(&req.EarNumber, valid.Required),
+	); err != nil {
+		apierr.AbortBadRequest(c, http.StatusBadRequest, err)
+		return
+	}
+
+	pagination := &pasturePb.PaginationModel{
+		Page:       int32(c.GetInt(middleware.Page)),
+		PageSize:   int32(c.GetInt(middleware.PageSize)),
+		PageOffset: int32(c.GetInt(middleware.PageOffset)),
+	}
+
+	res, err := middleware.Dependency(c).StoreEventHub.OpsService.EventList(c, &req, pagination)
+	if err != nil {
+		apierr.ClassifiedAbort(c, err)
+		return
+	}
+	ginutil.JSONResp(c, res)
+}
+
+func BehaviorCurve(c *gin.Context) {
+	var req pasturePb.CowBehaviorCurveRequest
+	if err := ginutil.BindProto(c, &req); err != nil {
+		apierr.AbortBadRequest(c, http.StatusBadRequest, err)
+		return
+	}
+
+	if err := valid.ValidateStruct(&req,
+		valid.Field(&req.EarNumber, valid.Required),
+	); err != nil {
+		apierr.AbortBadRequest(c, http.StatusBadRequest, err)
+		return
+	}
+
+	res, err := middleware.Dependency(c).StoreEventHub.OpsService.BehaviorCurve(c, &req)
+	if err != nil {
+		apierr.ClassifiedAbort(c, err)
+		return
+	}
+	c.JSON(http.StatusOK, res)
+}
+
+func GrowthCurve(c *gin.Context) {
+	var req pasturePb.CowGrowthCurveRequest
+	if err := ginutil.BindProto(c, &req); err != nil {
+		apierr.AbortBadRequest(c, http.StatusBadRequest, err)
+		return
+	}
+
+	if err := valid.ValidateStruct(&req,
+		valid.Field(&req.EarNumber, valid.Required),
+	); err != nil {
+		apierr.AbortBadRequest(c, http.StatusBadRequest, err)
+		return
+	}
+
+	res, err := middleware.Dependency(c).StoreEventHub.OpsService.CowGrowthCurve(c, &req)
+	if err != nil {
+		apierr.AbortBadRequest(c, http.StatusOK, err)
+		return
+	}
+	ginutil.JSONResp(c, res)
+}
+
+func LactCurve(c *gin.Context) {
+	var req pasturePb.CowLactCurveRequest
+	if err := ginutil.BindProto(c, &req); err != nil {
+		apierr.AbortBadRequest(c, http.StatusBadRequest, err)
+		return
+	}
+
+	if err := valid.ValidateStruct(&req,
+		valid.Field(&req.CowId, valid.Required),
+	); err != nil {
+		apierr.AbortBadRequest(c, http.StatusBadRequest, err)
+		return
+	}
+
+	res, err := middleware.Dependency(c).StoreEventHub.OpsService.CowLactCurve(c, &req)
+	if err != nil {
+		apierr.ClassifiedAbort(c, err)
+		return
+	}
+	ginutil.JSONResp(c, res)
+}
+
+func BehaviorRate(c *gin.Context) {
+	var req pasturePb.CowBehaviorRateRequest
+	if err := ginutil.BindProto(c, &req); err != nil {
+		apierr.AbortBadRequest(c, http.StatusBadRequest, err)
+		return
+	}
+
+	if err := valid.ValidateStruct(&req,
+		valid.Field(&req.EarNumber, valid.Required),
+		valid.Field(&req.StartAt, valid.Required),
+		valid.Field(&req.EndAt, valid.Required),
+	); err != nil {
+		apierr.AbortBadRequest(c, http.StatusBadRequest, err)
+		return
+	}
+
+	res, err := middleware.Dependency(c).StoreEventHub.OpsService.BehaviorRate(c, &req)
+	if err != nil {
+		apierr.ClassifiedAbort(c, err)
+		return
+	}
+	ginutil.JSONResp(c, res)
+}
+
+// IndicatorsComparison 指标对比
+func IndicatorsComparison(c *gin.Context) {
+	var req pasturePb.IndicatorsComparisonRequest
+	if err := ginutil.BindProto(c, &req); err != nil {
+		apierr.AbortBadRequest(c, http.StatusBadRequest, err)
+		return
+	}
+
+	if err := valid.ValidateStruct(&req,
+		valid.Field(&req.DateList, valid.Required),
+	); err != nil {
+		apierr.AbortBadRequest(c, http.StatusBadRequest, err)
+		return
+	}
+
+	res, err := middleware.Dependency(c).StoreEventHub.OpsService.IndicatorsComparison(c, &req)
+	if err != nil {
+		apierr.ClassifiedAbort(c, err)
+		return
+	}
+	c.JSON(http.StatusOK, res)
+}
+
+// LongTermInfertility 长期不孕
+func LongTermInfertility(c *gin.Context) {
+	var req pasturePb.LongTermInfertilityRequest
+	if err := ginutil.BindProto(c, &req); err != nil {
+		apierr.AbortBadRequest(c, http.StatusBadRequest, err)
+		return
+	}
+
+	if err := valid.ValidateStruct(&req,
+		valid.Field(&req.CalvingAge, valid.Required),
+	); err != nil {
+		apierr.AbortBadRequest(c, http.StatusBadRequest, err)
+		return
+	}
+
+	pagination := &pasturePb.PaginationModel{
+		Page:       int32(c.GetInt(middleware.Page)),
+		PageSize:   int32(c.GetInt(middleware.PageSize)),
+		PageOffset: int32(c.GetInt(middleware.PageOffset)),
+	}
+
+	res, err := middleware.Dependency(c).StoreEventHub.OpsService.LongTermInfertility(c, &req, pagination)
 	if err != nil {
 		apierr.ClassifiedAbort(c, err)
 		return

+ 0 - 18
http/handler/dashboard/bar.go

@@ -1,18 +0,0 @@
-package dashboard
-
-import (
-	"kpt-pasture/http/middleware"
-
-	"gitee.com/xuyiping_admin/pkg/apierr"
-	"gitee.com/xuyiping_admin/pkg/ginutil"
-	"github.com/gin-gonic/gin"
-)
-
-func Bar(c *gin.Context) {
-	res, err := middleware.Dependency(c).StoreEventHub.OpsService.Bar(c)
-	if err != nil {
-		apierr.ClassifiedAbort(c, err)
-		return
-	}
-	ginutil.JSONResp(c, res)
-}

+ 126 - 0
http/handler/dashboard/dashboard.go

@@ -0,0 +1,126 @@
+package dashboard
+
+import (
+	"kpt-pasture/http/middleware"
+	"net/http"
+
+	operationPb "gitee.com/xuyiping_admin/go_proto/proto/go/backend/operation"
+
+	pasturePb "gitee.com/xuyiping_admin/go_proto/proto/go/backend/cow"
+
+	"gitee.com/xuyiping_admin/pkg/valid"
+
+	"gitee.com/xuyiping_admin/pkg/apierr"
+	"gitee.com/xuyiping_admin/pkg/ginutil"
+	"github.com/gin-gonic/gin"
+)
+
+func NeckRingWarning(c *gin.Context) {
+	res, err := middleware.Dependency(c).StoreEventHub.OpsService.NeckRingWarning(c)
+	if err != nil {
+		apierr.ClassifiedAbort(c, err)
+		return
+	}
+	ginutil.JSONResp(c, res)
+}
+
+func FocusIndicators(c *gin.Context) {
+	dimension := c.Param("dimension")
+	if err := valid.Validate(dimension, valid.Required); err != nil {
+		apierr.AbortBadRequest(c, http.StatusBadRequest, err)
+		return
+	}
+	res, err := middleware.Dependency(c).StoreEventHub.OpsService.FocusIndicatorsList(c, dimension)
+	if err != nil {
+		apierr.ClassifiedAbort(c, err)
+		return
+	}
+	ginutil.JSONResp(c, res)
+}
+
+func FocusIndicatorsSet(c *gin.Context) {
+	var req pasturePb.IndexFocusIndicatorsSetRequest
+	if err := ginutil.BindProto(c, &req); err != nil {
+		apierr.AbortBadRequest(c, http.StatusBadRequest, err)
+		return
+	}
+
+	if err := middleware.Dependency(c).StoreEventHub.OpsService.FocusIndicatorsSet(c, &req); err != nil {
+		apierr.ClassifiedAbort(c, err)
+		return
+	}
+
+	ginutil.JSONResp(c, &operationPb.CommonOK{
+		Code: http.StatusOK,
+		Msg:  "ok",
+		Data: &operationPb.Success{Success: true},
+	})
+}
+
+func TodoCount(c *gin.Context) {
+	res, err := middleware.Dependency(c).StoreEventHub.OpsService.CalendarToDoCount(c)
+	if err != nil {
+		apierr.ClassifiedAbort(c, err)
+		return
+	}
+
+	ginutil.JSONResp(c, res)
+}
+
+func DataWarningSet(c *gin.Context) {
+	var req pasturePb.IndexDataWarningSetRequest
+	if err := ginutil.BindProto(c, &req); err != nil {
+		apierr.AbortBadRequest(c, http.StatusBadRequest, err)
+		return
+	}
+
+	if err := middleware.Dependency(c).StoreEventHub.OpsService.DataWarningSet(c, &req); err != nil {
+		apierr.ClassifiedAbort(c, err)
+		return
+	}
+
+	ginutil.JSONResp(c, &operationPb.CommonOK{
+		Code: http.StatusOK,
+		Msg:  "ok",
+		Data: &operationPb.Success{Success: true},
+	})
+}
+
+func DataWarningList(c *gin.Context) {
+	res, err := middleware.Dependency(c).StoreEventHub.OpsService.DataWarningList(c)
+	if err != nil {
+		apierr.ClassifiedAbort(c, err)
+		return
+	}
+	ginutil.JSONResp(c, res)
+}
+
+func DataWarningPop(c *gin.Context) {
+	var req pasturePb.WarningDataListRequest
+	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.DataWarningPop(c, &req, pagination)
+	if err != nil {
+		apierr.ClassifiedAbort(c, err)
+		return
+	}
+	c.JSON(http.StatusOK, res)
+}
+
+func Equipment(c *gin.Context) {
+	res, err := middleware.Dependency(c).StoreEventHub.OpsService.Equipment(c)
+	if err != nil {
+		apierr.ClassifiedAbort(c, err)
+		return
+	}
+
+	ginutil.JSONResp(c, res)
+}

+ 168 - 17
http/handler/event/event_base.go

@@ -35,7 +35,7 @@ func EnterEventList(c *gin.Context) {
 }
 
 func EnterEventCreate(c *gin.Context) {
-	var req pasturePb.EventEnterData
+	var req pasturePb.EventEnterRequest
 	if err := ginutil.BindProto(c, &req); err != nil {
 		apierr.AbortBadRequest(c, http.StatusBadRequest, err)
 		return
@@ -45,11 +45,15 @@ func EnterEventCreate(c *gin.Context) {
 		valid.Field(&req.EarNumber, valid.Required),
 		valid.Field(&req.BirthAt, valid.Required),
 		valid.Field(&req.Sex, valid.Required),
-		valid.Field(&req.CowTypeId, valid.Required),
-		valid.Field(&req.CowKindId, valid.Required),
-		valid.Field(&req.CowSourceId, valid.Required),
-		valid.Field(&req.Lact, valid.Required),
+		valid.Field(&req.CowType, valid.Required),
+		valid.Field(&req.CowKind, valid.Required),
+		valid.Field(&req.CowSource, valid.Required),
+		valid.Field(&req.Price, valid.Required),
 		valid.Field(&req.EnterAt, valid.Required),
+		valid.Field(&req.PenId, valid.Required),
+		valid.Field(&req.OperationId, valid.Required),
+		valid.Field(&req.Weight, valid.Required),
+		valid.Field(&req.BatchNumber, valid.Required),
 	); err != nil {
 		apierr.AbortBadRequest(c, http.StatusBadRequest, err)
 		return
@@ -99,11 +103,11 @@ func GroupTransferEventCreate(c *gin.Context) {
 		valid.Field(&req.Body, valid.Required, valid.Each(valid.By(func(value interface{}) error {
 			s := value.(pasturePb.TransferGroupEventData)
 			return valid.ValidateStruct(&s,
-				valid.Field(&s.CowId, valid.Required),
+				valid.Field(&s.EarNumber, valid.Required),
 				valid.Field(&s.TransferDate, valid.Required),
 				valid.Field(&s.TransferInPenId, valid.Required),
 				valid.Field(&s.TransferReasonId, valid.Required),
-				valid.Field(&s.StaffMemberId, valid.Required),
+				valid.Field(&s.OperationId, valid.Required),
 			)
 		}))),
 	); err != nil {
@@ -154,7 +158,7 @@ func BodyScoreEventCreate(c *gin.Context) {
 		valid.Field(&req.CowId, valid.Required),
 		valid.Field(&req.ScoreAt, valid.Required),
 		valid.Field(&req.Score, valid.Required),
-		valid.Field(&req.StaffMemberId, valid.Required),
+		valid.Field(&req.OperationId, valid.Required),
 	); err != nil {
 		apierr.AbortBadRequest(c, http.StatusBadRequest, err)
 		return
@@ -172,7 +176,7 @@ func BodyScoreEventCreate(c *gin.Context) {
 	})
 }
 
-func WeightEventList(c *gin.Context) {
+func WeightList(c *gin.Context) {
 	var req pasturePb.SearchEventRequest
 	if err := ginutil.BindProto(c, &req); err != nil {
 		apierr.AbortBadRequest(c, http.StatusBadRequest, err)
@@ -193,21 +197,21 @@ func WeightEventList(c *gin.Context) {
 	ginutil.JSONResp(c, res)
 }
 
-func WeightEventCreate(c *gin.Context) {
-	var req pasturePb.EventWeight
+func WeightBatch(c *gin.Context) {
+	var req pasturePb.BatchEventWeight
 	if err := ginutil.BindProto(c, &req); err != nil {
 		apierr.AbortBadRequest(c, http.StatusBadRequest, err)
 		return
 	}
 
 	if err := valid.ValidateStruct(&req,
-		valid.Field(&req.CowId, valid.Required),
-		valid.Field(&req.WeightAt, valid.Required),
-		valid.Field(&req.StaffMemberId, valid.Required),
-		valid.Field(&req.WeightItems, valid.Required, valid.Each(valid.By(func(value interface{}) error {
-			s := value.(pasturePb.WeightItem)
+		valid.Field(&req.Items, valid.Required, valid.Each(valid.By(func(value interface{}) error {
+			s := value.(pasturePb.EventWeight)
 			return valid.ValidateStruct(&s,
 				valid.Field(&s.Weight, valid.Required),
+				valid.Field(&s.EarNumber, valid.Required),
+				valid.Field(&s.WeightAt, valid.Required),
+				valid.Field(&s.OperationId, valid.Required),
 			)
 		}))),
 	); err != nil {
@@ -215,7 +219,7 @@ func WeightEventCreate(c *gin.Context) {
 		return
 	}
 
-	if err := middleware.BackendOperation(c).OpsService.WeightCreate(c, &req); err != nil {
+	if err := middleware.BackendOperation(c).OpsService.WeightBatch(c, &req); err != nil {
 		apierr.ClassifiedAbort(c, err)
 		return
 	}
@@ -226,3 +230,150 @@ func WeightEventCreate(c *gin.Context) {
 		Data: &operationPb.Success{Success: true},
 	})
 }
+
+func DeathBatch(c *gin.Context) {
+	var req pasturePb.EventDeathBatch
+	if err := ginutil.BindProto(c, &req); err != nil {
+		apierr.AbortBadRequest(c, http.StatusBadRequest, err)
+		return
+	}
+
+	if err := valid.ValidateStruct(&req,
+		valid.Field(&req.Items, valid.Required, valid.Each(valid.By(func(value interface{}) error {
+			s := value.(pasturePb.EventDeath)
+			return valid.ValidateStruct(&s,
+				valid.Field(&s.DeathAt, valid.Required),
+				valid.Field(&s.EarNumber, valid.Required),
+				valid.Field(&s.DeathDestinationKind, valid.Required),
+				valid.Field(&s.OperationId, valid.Required),
+				valid.Field(&s.DeathReasonKind, valid.Required),
+			)
+		}))),
+	); err != nil {
+		apierr.AbortBadRequest(c, http.StatusBadRequest, err)
+		return
+	}
+
+	if err := middleware.BackendOperation(c).OpsService.DeathBatch(c, &req); err != nil {
+		apierr.ClassifiedAbort(c, err)
+		return
+	}
+
+	ginutil.JSONResp(c, &operationPb.CommonOK{
+		Code: http.StatusOK,
+		Msg:  "ok",
+		Data: &operationPb.Success{Success: true},
+	})
+}
+
+func DeathList(c *gin.Context) {
+	var req pasturePb.SearchEventRequest
+	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.DeathList(c, &req, pagination)
+	if err != nil {
+		apierr.ClassifiedAbort(c, err)
+		return
+	}
+	ginutil.JSONResp(c, res)
+}
+
+func CowEarNumber(c *gin.Context) {
+	var req pasturePb.EventReplaceEarNumber
+	if err := ginutil.BindProto(c, &req); err != nil {
+		apierr.AbortBadRequest(c, http.StatusBadRequest, err)
+		return
+	}
+
+	if err := valid.ValidateStruct(&req,
+		valid.Field(&req.CowId, valid.Required),
+		valid.Field(&req.EarNumber, valid.Required),
+	); err != nil {
+		apierr.ClassifiedAbort(c, err)
+		return
+	}
+
+	if err := middleware.BackendOperation(c).OpsService.CowEarNumberUpdate(c, &req); err != nil {
+		apierr.ClassifiedAbort(c, err)
+		return
+	}
+
+	ginutil.JSONResp(c, &operationPb.CommonOK{
+		Code: http.StatusOK,
+		Msg:  "ok",
+		Data: &operationPb.Success{Success: true},
+	})
+}
+
+func CowSale(c *gin.Context) {
+	var req pasturePb.EventCowSale
+	if err := ginutil.BindProto(c, &req); err != nil {
+		apierr.AbortBadRequest(c, http.StatusBadRequest, err)
+		return
+	}
+
+	if err := valid.ValidateStruct(&req,
+		valid.Field(&req.DealerId, valid.Required),
+		valid.Field(&req.SaleAt, valid.Required),
+		valid.Field(&req.SaleAllPrice, valid.Required),
+		valid.Field(&req.SaleAllWeight, valid.Required),
+		valid.Field(&req.SalePrice, valid.Required),
+		valid.Field(&req.SaleTicket, valid.Required),
+		valid.Field(&req.EarNumbers, valid.Required),
+		valid.Field(&req.QuarantineReport, valid.Required),
+		valid.Field(&req.SalesType, valid.Required),
+		valid.Field(&req.SaleVehicleItems, valid.Required, valid.Each(valid.By(func(value interface{}) error {
+			s := value.(pasturePb.SaleVehicleItem)
+			return valid.ValidateStruct(&s,
+				valid.Field(&s.CarNumber, valid.Required),
+				valid.Field(&s.CowCount, valid.Required),
+				valid.Field(&s.CowWeight, valid.Required),
+			)
+		}))),
+	); err != nil {
+		apierr.ClassifiedAbort(c, err)
+		return
+	}
+
+	if err := middleware.BackendOperation(c).OpsService.CowSaleCreate(c, &req); err != nil {
+		apierr.ClassifiedAbort(c, err)
+		return
+	}
+
+	ginutil.JSONResp(c, &operationPb.CommonOK{
+		Code: http.StatusOK,
+		Msg:  "ok",
+		Data: &operationPb.Success{Success: true},
+	})
+}
+
+func CowSaleList(c *gin.Context) {
+	var req pasturePb.EventCowSaleRequest
+	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.CowSaleList(c, &req, pagination)
+	if err != nil {
+		apierr.ClassifiedAbort(c, err)
+		return
+	}
+
+	ginutil.JSONResp(c, res)
+}

+ 275 - 43
http/handler/event/event_breed.go

@@ -41,7 +41,7 @@ func CalvingEventCreate(c *gin.Context) {
 	}
 
 	if err := valid.ValidateStruct(&req,
-		valid.Field(&req.CowId, valid.Required),
+		valid.Field(&req.EarNumber, valid.Required),
 		valid.Field(&req.CalvingAt, valid.Required),
 		valid.Field(&req.ChildNumber, valid.Required),
 		valid.Field(&req.CalvingLevel, valid.Required),
@@ -54,7 +54,7 @@ func CalvingEventCreate(c *gin.Context) {
 				valid.Field(&s.IsAdoption, valid.Required),
 			)
 		}))),
-		valid.Field(&req.StaffMemberId, valid.Required),
+		valid.Field(&req.OperationId, valid.Required),
 	); err != nil {
 		apierr.AbortBadRequest(c, http.StatusBadRequest, err)
 		return
@@ -93,25 +93,30 @@ func PregnantCheckEventList(c *gin.Context) {
 	ginutil.JSONResp(c, res)
 }
 
-func PregnantCheckEventCreate(c *gin.Context) {
-	var req pasturePb.EventPregnantCheck
+func PregnantCheckEventCreateBatch(c *gin.Context) {
+	var req pasturePb.EventPregnantCheckBatch
 	if err := ginutil.BindProto(c, &req); err != nil {
 		apierr.AbortBadRequest(c, http.StatusBadRequest, err)
 		return
 	}
 
 	if err := valid.ValidateStruct(&req,
-		valid.Field(&req.CowId, valid.Required),
-		valid.Field(&req.PregnantCheckAt, valid.Required),
-		valid.Field(&req.PregnantCheckResult, valid.Required),
-		valid.Field(&req.PregnantCheckAt, valid.Required),
-		valid.Field(&req.StaffMemberId, valid.Required),
+		valid.Field(&req.Items, valid.Required, valid.Each(valid.By(func(value interface{}) error {
+			item := value.(pasturePb.EventPregnantCheckRequest)
+			return valid.ValidateStruct(&item,
+				valid.Field(&item.EarNumber, valid.Required),
+				valid.Field(&item.PregnantCheckAt, valid.Required),
+				valid.Field(&item.PregnantCheckResult, valid.Required),
+				valid.Field(&item.PregnantCheckMethod, valid.Required),
+				valid.Field(&item.OperationId, valid.Required),
+			)
+		}))),
 	); err != nil {
 		apierr.AbortBadRequest(c, http.StatusBadRequest, err)
 		return
 	}
 
-	if err := middleware.BackendOperation(c).OpsService.PregnantCheckCreate(c, &req); err != nil {
+	if err := middleware.BackendOperation(c).OpsService.PregnantCheckCreateBatch(c, &req); err != nil {
 		apierr.ClassifiedAbort(c, err)
 		return
 	}
@@ -144,25 +149,30 @@ func MatingEventList(c *gin.Context) {
 	ginutil.JSONResp(c, res)
 }
 
-func MatingCreate(c *gin.Context) {
-	var req pasturePb.EventMating
+func MatingBatch(c *gin.Context) {
+	var req pasturePb.EventMatingBatch
 	if err := ginutil.BindProto(c, &req); err != nil {
 		apierr.AbortBadRequest(c, http.StatusBadRequest, err)
 		return
 	}
 
 	if err := valid.ValidateStruct(&req,
-		valid.Field(&req.CowId, valid.Required),
-		valid.Field(&req.StaffMemberId, valid.Required),
-		valid.Field(&req.ExposeEstrusType, valid.Required),
-		valid.Field(&req.BullId, valid.Required),
-		valid.Field(&req.MatingAt, valid.Required),
+		valid.Field(&req.Items, valid.Required, valid.Each(valid.By(func(value interface{}) error {
+			item := value.(pasturePb.EventMating)
+			return valid.ValidateStruct(&item,
+				valid.Field(&item.EarNumber, valid.Required),
+				valid.Field(&item.OperationId, valid.Required),
+				valid.Field(&item.FrozenSemenNumber, valid.Required),
+				valid.Field(&item.MatingAt, valid.Required),
+				valid.Field(&item.FrozenSemenCount, valid.Required, valid.Min(1)),
+			)
+		}))),
 	); err != nil {
 		apierr.AbortBadRequest(c, http.StatusBadRequest, err)
 		return
 	}
 
-	if err := middleware.BackendOperation(c).OpsService.MatingCreate(c, &req); err != nil {
+	if err := middleware.BackendOperation(c).OpsService.MatingBatch(c, &req); err != nil {
 		apierr.ClassifiedAbort(c, err)
 		return
 	}
@@ -195,23 +205,29 @@ func EstrusEventList(c *gin.Context) {
 	ginutil.JSONResp(c, res)
 }
 
-func EstrusCreate(c *gin.Context) {
-	var req pasturePb.EventEstrus
+func EstrusBatchMating(c *gin.Context) {
+	var req pasturePb.EventEstrusBatch
 	if err := ginutil.BindProto(c, &req); err != nil {
 		apierr.AbortBadRequest(c, http.StatusBadRequest, err)
 		return
 	}
 
 	if err := valid.ValidateStruct(&req,
-		valid.Field(&req.CowId, valid.Required),
-		valid.Field(&req.StaffMemberId, valid.Required),
-		valid.Field(&req.EstrusAt, valid.Required),
+		valid.Field(&req.Items, valid.Required, valid.Each(valid.By(func(value interface{}) error {
+			item := value.(pasturePb.EventEstrusItems)
+			return valid.ValidateStruct(&item,
+				valid.Field(&item.EarNumber, valid.Required),
+				valid.Field(&item.EstrusAt, valid.Required),
+				valid.Field(&item.IsMating, valid.Required),
+				valid.Field(&item.OperationId, valid.Required),
+			)
+		}))),
 	); err != nil {
 		apierr.AbortBadRequest(c, http.StatusBadRequest, err)
 		return
 	}
 
-	if err := middleware.BackendOperation(c).OpsService.EstrusCreate(c, &req); err != nil {
+	if err := middleware.BackendOperation(c).OpsService.EstrusBatchMating(c, &req); err != nil {
 		apierr.ClassifiedAbort(c, err)
 		return
 	}
@@ -223,8 +239,66 @@ func EstrusCreate(c *gin.Context) {
 	})
 }
 
-func FrozenSemeList(c *gin.Context) {
-	var req pasturePb.FrozenSemenRequest
+func SameTimeCreate(c *gin.Context) {
+	var req pasturePb.EventSameTime
+	if err := ginutil.BindProto(c, &req); err != nil {
+		apierr.AbortBadRequest(c, http.StatusBadRequest, err)
+		return
+	}
+
+	if err := valid.ValidateStruct(&req,
+		valid.Field(&req.Id, valid.Required),
+		valid.Field(&req.DrugsId, valid.Required),
+		valid.Field(&req.OperationId, valid.Required),
+		valid.Field(&req.SameTimeAt, valid.Required),
+	); err != nil {
+		apierr.AbortBadRequest(c, http.StatusBadRequest, err)
+		return
+	}
+
+	if err := middleware.BackendOperation(c).OpsService.SameTimeCreate(c, &req); err != nil {
+		apierr.ClassifiedAbort(c, err)
+		return
+	}
+
+	ginutil.JSONResp(c, &operationPb.CommonOK{
+		Code: http.StatusOK,
+		Msg:  "ok",
+		Data: &operationPb.Success{Success: true},
+	})
+}
+
+func SameTimeBatch(c *gin.Context) {
+	var req pasturePb.EventSameTimeBatch
+	if err := ginutil.BindProto(c, &req); err != nil {
+		apierr.AbortBadRequest(c, http.StatusBadRequest, err)
+		return
+	}
+
+	if err := valid.ValidateStruct(&req,
+		valid.Field(&req.OperationId, valid.Required),
+		valid.Field(&req.DrugsId, valid.Required),
+		valid.Field(&req.SameTimeAt, valid.Required),
+		valid.Field(&req.CowIds, valid.Required),
+	); err != nil {
+		apierr.AbortBadRequest(c, http.StatusBadRequest, err)
+		return
+	}
+
+	if err := middleware.BackendOperation(c).OpsService.SameTimeBatch(c, &req); err != nil {
+		apierr.ClassifiedAbort(c, err)
+		return
+	}
+
+	ginutil.JSONResp(c, &operationPb.CommonOK{
+		Code: http.StatusOK,
+		Msg:  "ok",
+		Data: &operationPb.Success{Success: true},
+	})
+}
+
+func SameTimeList(c *gin.Context) {
+	var req pasturePb.SearchEventRequest
 	if err := ginutil.BindProto(c, &req); err != nil {
 		apierr.AbortBadRequest(c, http.StatusBadRequest, err)
 		return
@@ -236,7 +310,7 @@ func FrozenSemeList(c *gin.Context) {
 		PageOffset: int32(c.GetInt(middleware.PageOffset)),
 	}
 
-	res, err := middleware.Dependency(c).StoreEventHub.OpsService.FrozenSemenList(c, &req, pagination)
+	res, err := middleware.Dependency(c).StoreEventHub.OpsService.SameTimeList(c, &req, pagination)
 	if err != nil {
 		apierr.ClassifiedAbort(c, err)
 		return
@@ -244,26 +318,30 @@ func FrozenSemeList(c *gin.Context) {
 	ginutil.JSONResp(c, res)
 }
 
-// FrozenSemeCreate 冻精
-func FrozenSemeCreate(c *gin.Context) {
-	var req pasturePb.SearchFrozenSemenList
+func AbortionCreateBatch(c *gin.Context) {
+	var req pasturePb.EventAbortionBatch
 	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),
+		valid.Field(&req.Items, valid.Required, valid.Each(valid.By(func(value interface{}) error {
+			item := value.(pasturePb.EventAbortionItem)
+			return valid.ValidateStruct(&item,
+				valid.Field(&item.EarNumber, valid.Required),
+				valid.Field(&item.IsAfterbirth, valid.Required),
+				valid.Field(&item.AbortionReasons, valid.Required),
+				valid.Field(&item.AbortionAt, valid.Required),
+				valid.Field(&item.OperationId, valid.Required),
+			)
+		}))),
 	); err != nil {
 		apierr.AbortBadRequest(c, http.StatusBadRequest, err)
 		return
 	}
 
-	if err := middleware.BackendOperation(c).OpsService.FrozenSemenCreate(c, &req); err != nil {
+	if err := middleware.BackendOperation(c).OpsService.AbortionCreateBatch(c, &req); err != nil {
 		apierr.ClassifiedAbort(c, err)
 		return
 	}
@@ -275,23 +353,84 @@ func FrozenSemeCreate(c *gin.Context) {
 	})
 }
 
-func SameTimeCreate(c *gin.Context) {
-	var req pasturePb.EventSameTime
+func AbortionList(c *gin.Context) {
+	var req pasturePb.SearchEventRequest
+	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.AbortionList(c, &req, pagination)
+	if err != nil {
+		apierr.ClassifiedAbort(c, err)
+		return
+	}
+	ginutil.JSONResp(c, res)
+}
+
+func WeaningCreateBatch(c *gin.Context) {
+	var req pasturePb.EventWeaningBatch
 	if err := ginutil.BindProto(c, &req); err != nil {
 		apierr.AbortBadRequest(c, http.StatusBadRequest, err)
 		return
 	}
 
 	if err := valid.ValidateStruct(&req,
-		valid.Field(&req.SameTimeId, valid.Required),
-		valid.Field(&req.SameTimeType, valid.Required),
-		valid.Field(&req.SameTimeAt, valid.Required),
+		valid.Field(&req.Items, valid.Required, valid.Each(valid.By(func(value interface{}) error {
+			item := value.(pasturePb.WeaningItem)
+			return valid.ValidateStruct(&item,
+				valid.Field(&item.EarNumber, valid.Required),
+				valid.Field(&item.Weight, valid.Required),
+				valid.Field(&item.WeaningAt, valid.Required),
+				valid.Field(&item.OperationId, valid.Required),
+				valid.Field(&item.PenId, valid.Required),
+			)
+		}))),
 	); err != nil {
 		apierr.AbortBadRequest(c, http.StatusBadRequest, err)
 		return
 	}
 
-	if err := middleware.BackendOperation(c).OpsService.SameTimeCreate(c, &req); err != nil {
+	if err := middleware.BackendOperation(c).OpsService.WeaningBatch(c, &req); err != nil {
+		apierr.ClassifiedAbort(c, err)
+		return
+	}
+	ginutil.JSONResp(c, &operationPb.CommonOK{
+		Code: http.StatusOK,
+		Msg:  "ok",
+		Data: &operationPb.Success{Success: true},
+	})
+}
+
+func DryMilkBatch(c *gin.Context) {
+	var req pasturePb.EventMilkBatch
+	if err := ginutil.BindProto(c, &req); err != nil {
+		apierr.AbortBadRequest(c, http.StatusBadRequest, err)
+		return
+	}
+
+	if err := valid.ValidateStruct(&req,
+		valid.Field(&req.Items, valid.Required, valid.Each(valid.By(func(value interface{}) error {
+			item := value.(pasturePb.EventMilkItem)
+			return valid.ValidateStruct(&item,
+				valid.Field(&item.CowId, valid.Required),
+				valid.Field(&item.DryMilkAt, valid.Required),
+				valid.Field(&item.PenId, valid.Required),
+				valid.Field(&item.OperationId, valid.Required),
+			)
+		}))),
+	); err != nil {
+		apierr.AbortBadRequest(c, http.StatusBadRequest, err)
+		return
+	}
+
+	if err := middleware.Dependency(c).StoreEventHub.OpsService.DryMilkBatch(c, &req); err != nil {
 		apierr.ClassifiedAbort(c, err)
 		return
 	}
@@ -301,9 +440,102 @@ func SameTimeCreate(c *gin.Context) {
 		Msg:  "ok",
 		Data: &operationPb.Success{Success: true},
 	})
+}
+
+func DryMilkList(c *gin.Context) {
+	var req pasturePb.SearchEventRequest
+	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.DryMilkList(c, &req, pagination)
+	if err != nil {
+		apierr.ClassifiedAbort(c, err)
+		return
+	}
 
+	ginutil.JSONResp(c, res)
 }
 
-func SameTimeList(c *gin.Context) {
+func ForbiddenMatingBatch(c *gin.Context) {
+	var req pasturePb.EventForbiddenMatingBatch
+	if err := ginutil.BindProto(c, &req); err != nil {
+		apierr.AbortBadRequest(c, http.StatusBadRequest, err)
+		return
+	}
+
+	if err := valid.ValidateStruct(&req,
+		valid.Field(&req.Items, valid.Required, valid.Each(valid.By(func(value interface{}) error {
+			item := value.(pasturePb.ForbiddenMatingItem)
+			return valid.ValidateStruct(&item,
+				valid.Field(&item.EarNumber, valid.Required),
+				valid.Field(&item.ForbiddenMatingAt, valid.Required),
+				valid.Field(&item.ForbiddenMatingReasonsKind, valid.Required),
+				valid.Field(&item.OperationId, valid.Required),
+			)
+		}))),
+	); err != nil {
+		apierr.AbortBadRequest(c, http.StatusBadRequest, err)
+		return
+	}
 
+	if err := middleware.Dependency(c).StoreEventHub.OpsService.ForbiddenMatingBatch(c, &req); err != nil {
+		apierr.ClassifiedAbort(c, err)
+		return
+	}
+
+	ginutil.JSONResp(c, &operationPb.CommonOK{
+		Code: http.StatusOK,
+		Msg:  "ok",
+		Data: &operationPb.Success{Success: true},
+	})
+}
+
+func ForbiddenMatingList(c *gin.Context) {
+	var req pasturePb.SearchEventRequest
+	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.ForbiddenMatingList(c, &req, pagination)
+	if err != nil {
+		apierr.ClassifiedAbort(c, err)
+		return
+	}
+
+	ginutil.JSONResp(c, res)
+}
+
+func UnForbiddenMating(c *gin.Context) {
+	var req pasturePb.EventUnForbiddenMatingRequest
+	if err := ginutil.BindProto(c, &req); err != nil {
+		apierr.AbortBadRequest(c, http.StatusBadRequest, err)
+		return
+	}
+
+	err := middleware.Dependency(c).StoreEventHub.OpsService.UnForbiddenMating(c, &req)
+	if err != nil {
+		apierr.ClassifiedAbort(c, err)
+		return
+	}
+
+	ginutil.JSONResp(c, &operationPb.CommonOK{
+		Code: http.StatusOK,
+		Msg:  "ok",
+		Data: &operationPb.Success{Success: true},
+	})
 }

+ 86 - 61
http/handler/pasture/prescription.go → http/handler/event/event_health.go

@@ -1,4 +1,4 @@
-package pasture
+package event
 
 import (
 	"kpt-pasture/http/middleware"
@@ -13,8 +13,38 @@ import (
 	"github.com/gin-gonic/gin"
 )
 
-func SearchDiseaseList(c *gin.Context) {
-	var req pasturePb.SearchDiseaseRequest
+func CowDiseaseCreate(c *gin.Context) {
+	var req pasturePb.EventCowDiseaseRequest
+	if err := ginutil.BindProto(c, &req); err != nil {
+		apierr.AbortBadRequest(c, http.StatusBadRequest, err)
+		return
+	}
+
+	if err := valid.ValidateStruct(&req,
+		valid.Field(&req.EarNumber, valid.Required),
+		valid.Field(&req.DiseaseAt, valid.Required),
+		valid.Field(&req.DiseaseId, valid.Required),
+		valid.Field(&req.OperationId, valid.Required),
+	); err != nil {
+		apierr.AbortBadRequest(c, http.StatusBadRequest, err)
+		return
+	}
+
+	source := c.GetHeader("Source")
+	if err := middleware.BackendOperation(c).OpsService.CowDiseaseCreate(c, &req, source); err != nil {
+		apierr.ClassifiedAbort(c, err)
+		return
+	}
+
+	ginutil.JSONResp(c, &operationPb.CommonOK{
+		Code: http.StatusOK,
+		Msg:  "ok",
+		Data: &operationPb.Success{Success: true},
+	})
+}
+
+func CowDiseaseList(c *gin.Context) {
+	var req pasturePb.SearchEventCowTreatmentRequest
 	if err := ginutil.BindProto(c, &req); err != nil {
 		apierr.AbortBadRequest(c, http.StatusBadRequest, err)
 		return
@@ -26,30 +56,32 @@ func SearchDiseaseList(c *gin.Context) {
 		PageOffset: int32(c.GetInt(middleware.PageOffset)),
 	}
 
-	res, err := middleware.Dependency(c).StoreEventHub.OpsService.SearchDiseaseList(c, &req, pagination)
+	res, err := middleware.Dependency(c).StoreEventHub.OpsService.CowDiseaseList(c, &req, pagination)
 	if err != nil {
 		apierr.ClassifiedAbort(c, err)
 		return
 	}
 	ginutil.JSONResp(c, res)
+
 }
 
-func CreatedOrUpdateDisease(c *gin.Context) {
-	var req pasturePb.SearchDiseaseList
+func CowDiseaseDiagnose(c *gin.Context) {
+	var req pasturePb.CowDiagnosedRequest
 	if err := ginutil.BindProto(c, &req); err != nil {
 		apierr.AbortBadRequest(c, http.StatusBadRequest, err)
 		return
 	}
 
 	if err := valid.ValidateStruct(&req,
-		valid.Field(&req.Name, valid.Required),
-		valid.Field(&req.DiseaseTypeId, valid.Required),
+		valid.Field(&req.Id, valid.Required),
+		valid.Field(&req.CowId, valid.Required),
+		valid.Field(&req.DiagnosedResult, valid.Required),
 	); err != nil {
 		apierr.AbortBadRequest(c, http.StatusBadRequest, err)
 		return
 	}
 
-	if err := middleware.BackendOperation(c).OpsService.CreateOrUpdateDisease(c, &req); err != nil {
+	if err := middleware.BackendOperation(c).OpsService.CowDiseaseDiagnose(c, &req); err != nil {
 		apierr.ClassifiedAbort(c, err)
 		return
 	}
@@ -61,42 +93,44 @@ func CreatedOrUpdateDisease(c *gin.Context) {
 	})
 }
 
-func SearchDiseaseTypeList(c *gin.Context) {
-	var req pasturePb.SearchNameRequest
-	if err := ginutil.BindProto(c, &req); err != nil {
-		apierr.AbortBadRequest(c, http.StatusBadRequest, err)
-		return
-	}
+func DiseaseSuggestPrescription(c *gin.Context) {
+	diseaseIdStr := c.Param("id")
+	diseaseId, _ := strconv.Atoi(diseaseIdStr)
 
-	pagination := &pasturePb.PaginationModel{
-		Page:       int32(c.GetInt(middleware.Page)),
-		PageSize:   int32(c.GetInt(middleware.PageSize)),
-		PageOffset: int32(c.GetInt(middleware.PageOffset)),
+	if err := valid.Validate(diseaseId, valid.Required, valid.Min(1)); err != nil {
+		apierr.ClassifiedAbort(c, err)
+		return
 	}
 
-	res, err := middleware.Dependency(c).StoreEventHub.OpsService.SearchDiseaseTypeList(c, &req, pagination)
+	res, err := middleware.BackendOperation(c).OpsService.DiseaseSuggestPrescription(c, int64(diseaseId))
 	if err != nil {
 		apierr.ClassifiedAbort(c, err)
 		return
 	}
+
 	ginutil.JSONResp(c, res)
 }
 
-func CreatedOrUpdateDiseaseType(c *gin.Context) {
-	var req pasturePb.SearchBaseConfigList
+func CowDiseaseTreatment(c *gin.Context) {
+	var req pasturePb.CowTreatmentRequest
 	if err := ginutil.BindProto(c, &req); err != nil {
 		apierr.AbortBadRequest(c, http.StatusBadRequest, err)
 		return
 	}
 
 	if err := valid.ValidateStruct(&req,
-		valid.Field(&req.Name, valid.Required),
+		valid.Field(&req.Id, valid.Required),
+		valid.Field(&req.CowId, valid.Required),
+		valid.Field(&req.DiseaseId, valid.Required),
+		valid.Field(&req.PrescriptionId, valid.Required),
+		valid.Field(&req.TreatmentAt, valid.Required),
+		valid.Field(&req.OperationId, valid.Required),
 	); err != nil {
 		apierr.AbortBadRequest(c, http.StatusBadRequest, err)
 		return
 	}
 
-	if err := middleware.BackendOperation(c).OpsService.CreateOrUpdateDiseaseType(c, &req); err != nil {
+	if err := middleware.BackendOperation(c).OpsService.CowDiseaseTreatment(c, &req); err != nil {
 		apierr.ClassifiedAbort(c, err)
 		return
 	}
@@ -108,43 +142,53 @@ func CreatedOrUpdateDiseaseType(c *gin.Context) {
 	})
 }
 
-func SearchPrescriptionList(c *gin.Context) {
-	var req pasturePb.SearchPrescriptionRequest
+func CowDiseaseTreatmentDetail(c *gin.Context) {
+	var req pasturePb.EventCowTreatmentDetailRequest
 	if err := ginutil.BindProto(c, &req); err != nil {
 		apierr.AbortBadRequest(c, http.StatusBadRequest, err)
 		return
 	}
 
+	if err := valid.ValidateStruct(&req,
+		valid.Field(&req.CowId, valid.Required),
+		valid.Field(&req.Id, valid.Required),
+	); err != nil {
+		apierr.AbortBadRequest(c, http.StatusBadRequest, err)
+		return
+	}
+
 	pagination := &pasturePb.PaginationModel{
 		Page:       int32(c.GetInt(middleware.Page)),
 		PageSize:   int32(c.GetInt(middleware.PageSize)),
 		PageOffset: int32(c.GetInt(middleware.PageOffset)),
 	}
 
-	res, err := middleware.Dependency(c).StoreEventHub.OpsService.SearchPrescriptionList(c, &req, pagination)
+	res, err := middleware.BackendOperation(c).OpsService.CowDiseaseTreatmentDetail(c, &req, pagination)
 	if err != nil {
 		apierr.ClassifiedAbort(c, err)
 		return
 	}
+
 	ginutil.JSONResp(c, res)
 }
 
-func CreatedOrUpdatePrescription(c *gin.Context) {
-	var req pasturePb.PrescriptionRequest
+func CowDiseaseCurable(c *gin.Context) {
+	var req pasturePb.EventCowCurableRequest
 	if err := ginutil.BindProto(c, &req); err != nil {
 		apierr.AbortBadRequest(c, http.StatusBadRequest, err)
 		return
 	}
 
 	if err := valid.ValidateStruct(&req,
-		valid.Field(&req.Name, valid.Required),
-		valid.Field(&req.DrugsList, valid.Required),
+		valid.Field(&req.Ids, valid.Required),
+		valid.Field(&req.OperationId, valid.Required),
+		valid.Field(&req.CurableAt, valid.Required),
 	); err != nil {
 		apierr.AbortBadRequest(c, http.StatusBadRequest, err)
 		return
 	}
 
-	if err := middleware.BackendOperation(c).OpsService.CreateOrUpdatePrescription(c, &req); err != nil {
+	if err := middleware.BackendOperation(c).OpsService.CowDiseaseCurable(c, &req); err != nil {
 		apierr.ClassifiedAbort(c, err)
 		return
 	}
@@ -156,8 +200,8 @@ func CreatedOrUpdatePrescription(c *gin.Context) {
 	})
 }
 
-func SearchImmunizationList(c *gin.Context) {
-	var req pasturePb.ImmunizationRequest
+func ImmunizationList(c *gin.Context) {
+	var req pasturePb.SearchEventImmunizationRequest
 	if err := ginutil.BindProto(c, &req); err != nil {
 		apierr.AbortBadRequest(c, http.StatusBadRequest, err)
 		return
@@ -177,25 +221,25 @@ func SearchImmunizationList(c *gin.Context) {
 	ginutil.JSONResp(c, res)
 }
 
-func CreatedOrUpdateImmunization(c *gin.Context) {
-	var req pasturePb.ImmunizationRequest
+func ImmunizationBatch(c *gin.Context) {
+	var req pasturePb.ImmunizationItem
 	if err := ginutil.BindProto(c, &req); err != nil {
 		apierr.AbortBadRequest(c, http.StatusBadRequest, err)
 		return
 	}
 
 	if err := valid.ValidateStruct(&req,
-		valid.Field(&req.Name, valid.Required),
-		valid.Field(&req.CowType, valid.Required),
-		valid.Field(&req.Conditions, valid.Required),
-		valid.Field(&req.Value, valid.Required),
-		valid.Field(&req.Value2, valid.Required),
+		valid.Field(&req.PlanId, valid.Required),
+		valid.Field(&req.OperationId, valid.Required),
+		valid.Field(&req.EarNumbers, valid.Required),
+		valid.Field(&req.ImmunizationAt, valid.Required),
+		valid.Field(&req.Usage, valid.Required),
 	); err != nil {
 		apierr.AbortBadRequest(c, http.StatusBadRequest, err)
 		return
 	}
 
-	if err := middleware.BackendOperation(c).OpsService.CreatedOrUpdateImmunization(c, &req); err != nil {
+	if err := middleware.Dependency(c).StoreEventHub.OpsService.ImmunizationBatch(c, &req); err != nil {
 		apierr.ClassifiedAbort(c, err)
 		return
 	}
@@ -206,22 +250,3 @@ func CreatedOrUpdateImmunization(c *gin.Context) {
 		Data: &operationPb.Success{Success: true},
 	})
 }
-
-func ImmunizationIsShow(c *gin.Context) {
-	immunizationIdStr := c.Param("id")
-	immunizationId, _ := strconv.Atoi(immunizationIdStr)
-
-	if err := valid.Validate(immunizationId, valid.Required, valid.Min(1)); err != nil {
-		apierr.ClassifiedAbort(c, err)
-		return
-	}
-	if err := middleware.Dependency(c).StoreEventHub.OpsService.ImmunizationIsShow(c, int64(immunizationId)); err != nil {
-		apierr.ClassifiedAbort(c, err)
-		return
-	}
-	ginutil.JSONResp(c, &operationPb.CommonOK{
-		Code: http.StatusOK,
-		Msg:  "ok",
-		Data: &operationPb.Success{Success: true},
-	})
-}

+ 0 - 1
http/handler/event/event_vet.go

@@ -1 +0,0 @@
-package event

+ 60 - 0
http/handler/goods/drugs.go

@@ -119,3 +119,63 @@ func MedicalEquipmentCreateOrUpdate(c *gin.Context) {
 		Data: &operationPb.Success{Success: true},
 	})
 }
+
+func NeckRingList(c *gin.Context) {
+	var req pasturePb.SearchNeckRingRequest
+	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.NeckRingList(c, &req, pagination)
+	if err != nil {
+		apierr.ClassifiedAbort(c, err)
+		return
+	}
+	ginutil.JSONResp(c, res)
+}
+
+func NeckRingCreateOrUpdate(c *gin.Context) {
+	var req pasturePb.NeckRingCreateRequest
+	if err := ginutil.BindProto(c, &req); err != nil {
+		apierr.AbortBadRequest(c, http.StatusBadRequest, err)
+		return
+	}
+
+	if err := valid.ValidateStruct(&req,
+		valid.Field(&req.Items, valid.Required, valid.Each(valid.By(func(value interface{}) error {
+			item := value.(pasturePb.NeckRingCreateItem)
+			if req.Status == pasturePb.NeckRingOperationStatus_Bind {
+				return valid.ValidateStruct(&item,
+					valid.Field(&item.Number, valid.Required),
+					valid.Field(&item.EarNumber, valid.Required),
+				)
+			} else {
+				return valid.ValidateStruct(&item,
+					valid.Field(&item.Number, valid.Required),
+				)
+			}
+		}))),
+		valid.Field(&req.Status, valid.Required),
+	); err != nil {
+		apierr.AbortBadRequest(c, http.StatusBadRequest, err)
+		return
+	}
+
+	if err := middleware.BackendOperation(c).OpsService.NeckRingCreateOrUpdate(c, &req); err != nil {
+		apierr.ClassifiedAbort(c, err)
+		return
+	}
+
+	ginutil.JSONResp(c, &operationPb.CommonOK{
+		Code: http.StatusOK,
+		Msg:  "ok",
+		Data: &operationPb.Success{Success: true},
+	})
+}

+ 178 - 0
http/handler/goods/outbound.go

@@ -0,0 +1,178 @@
+package goods
+
+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"
+)
+
+func OutboundApply(c *gin.Context) {
+	var req pasturePb.OutboundApplyItem
+	if err := ginutil.BindProto(c, &req); err != nil {
+		apierr.AbortBadRequest(c, http.StatusBadRequest, err)
+		return
+	}
+
+	if err := valid.ValidateStruct(&req,
+		valid.Field(&req.OutType, valid.Required),
+		valid.Field(&req.Goods, valid.Required),
+	); err != nil {
+		apierr.AbortBadRequest(c, http.StatusBadRequest, err)
+		return
+	}
+
+	if err := middleware.BackendOperation(c).OpsService.OutboundApply(c, &req); err != nil {
+		apierr.ClassifiedAbort(c, err)
+		return
+	}
+
+	ginutil.JSONResp(c, &operationPb.CommonOK{
+		Code: http.StatusOK,
+		Msg:  "ok",
+		Data: &operationPb.Success{Success: true},
+	})
+}
+
+func OutboundList(c *gin.Context) {
+	var req pasturePb.SearchOutboundApplyRequest
+	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.OutboundList(c, &req, pagination)
+	if err != nil {
+		apierr.ClassifiedAbort(c, err)
+		return
+	}
+	ginutil.JSONResp(c, res)
+}
+
+func OutboundAudit(c *gin.Context) {
+	var req pasturePb.OutboundApplyAuditRequest
+	if err := ginutil.BindProto(c, &req); err != nil {
+		apierr.AbortBadRequest(c, http.StatusBadRequest, err)
+		return
+	}
+
+	if err := valid.ValidateStruct(&req,
+		valid.Field(&req.Id, valid.Required),
+		valid.Field(&req.AuditStatus, valid.Required),
+	); err != nil {
+		apierr.AbortBadRequest(c, http.StatusBadRequest, err)
+		return
+	}
+
+	if err := middleware.BackendOperation(c).OpsService.OutboundAudit(c, &req); err != nil {
+		apierr.ClassifiedAbort(c, err)
+		return
+	}
+
+	ginutil.JSONResp(c, &operationPb.CommonOK{
+		Code: http.StatusOK,
+		Msg:  "ok",
+		Data: &operationPb.Success{Success: true},
+	})
+}
+
+func OutboundDetail(c *gin.Context) {
+	idStr := c.Param("id")
+	id, _ := strconv.ParseInt(idStr, 10, 64)
+	if err := valid.Validate(id, valid.Required); err != nil {
+		apierr.AbortBadRequest(c, http.StatusBadRequest, err)
+		return
+	}
+
+	res, err := middleware.BackendOperation(c).OpsService.OutboundDetail(c, id)
+	if err != nil {
+		apierr.ClassifiedAbort(c, err)
+		return
+	}
+
+	ginutil.JSONResp(c, res)
+}
+
+func OutboundDelete(c *gin.Context) {
+	idStr := c.Param("id")
+	id, _ := strconv.ParseInt(idStr, 10, 64)
+	if err := valid.Validate(id, valid.Required); err != nil {
+		apierr.AbortBadRequest(c, http.StatusBadRequest, err)
+		return
+	}
+
+	if err := middleware.BackendOperation(c).OpsService.OutboundDelete(c, id); err != nil {
+		apierr.ClassifiedAbort(c, err)
+		return
+	}
+
+	ginutil.JSONResp(c, &operationPb.CommonOK{
+		Code: http.StatusOK,
+		Msg:  "ok",
+		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},
+	})
+}

+ 9 - 8
http/handler/user.go → http/handler/milk/milk.go

@@ -1,4 +1,4 @@
-package handler
+package milk
 
 import (
 	"kpt-pasture/http/middleware"
@@ -10,18 +10,19 @@ import (
 	"github.com/gin-gonic/gin"
 )
 
-// GetUserMenu 获取系统用户菜单权限
-func GetUserMenu(c *gin.Context) {
-	res, err := middleware.BackendOperation(c).OpsService.GetSystemUserMenu(c)
+func ProcessOriginal(c *gin.Context) {
+	// 获取请求体数据
+	body, err := c.GetRawData()
 	if err != nil {
 		apierr.ClassifiedAbort(c, err)
 		return
 	}
-	ginutil.JSONResp(c, res)
-}
 
-// Logout 用户登出,
-func Logout(c *gin.Context) {
+	err = middleware.Dependency(c).StoreEventHub.OpsService.MilkHallOriginal(c, body)
+	if err != nil {
+		apierr.ClassifiedAbort(c, err)
+		return
+	}
 	ginutil.JSONResp(c, &operationPb.CommonOK{
 		Code: http.StatusOK,
 		Msg:  "ok",

+ 429 - 0
http/handler/pasture/pasture.go

@@ -0,0 +1,429 @@
+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"
+	"gitee.com/xuyiping_admin/pkg/valid/is"
+	"gitee.com/xuyiping_admin/pkg/xerr"
+	"github.com/gin-gonic/gin"
+)
+
+func SearchDiseaseList(c *gin.Context) {
+	var req pasturePb.SearchDiseaseRequest
+	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.SearchDiseaseList(c, &req, pagination)
+	if err != nil {
+		apierr.ClassifiedAbort(c, err)
+		return
+	}
+	ginutil.JSONResp(c, res)
+}
+
+func CreatedOrUpdateDisease(c *gin.Context) {
+	var req pasturePb.SearchDiseaseList
+	if err := ginutil.BindProto(c, &req); err != nil {
+		apierr.AbortBadRequest(c, http.StatusBadRequest, err)
+		return
+	}
+
+	if err := valid.ValidateStruct(&req,
+		valid.Field(&req.Name, valid.Required),
+		valid.Field(&req.DiseaseTypeId, valid.Required),
+	); err != nil {
+		apierr.AbortBadRequest(c, http.StatusBadRequest, err)
+		return
+	}
+
+	if err := middleware.BackendOperation(c).OpsService.CreateOrUpdateDisease(c, &req); err != nil {
+		apierr.ClassifiedAbort(c, err)
+		return
+	}
+
+	ginutil.JSONResp(c, &operationPb.CommonOK{
+		Code: http.StatusOK,
+		Msg:  "ok",
+		Data: &operationPb.Success{Success: true},
+	})
+}
+
+func SearchDiseaseTypeList(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.Dependency(c).StoreEventHub.OpsService.SearchDiseaseTypeList(c, &req, pagination)
+	if err != nil {
+		apierr.ClassifiedAbort(c, err)
+		return
+	}
+	ginutil.JSONResp(c, res)
+}
+
+func CreatedOrUpdateDiseaseType(c *gin.Context) {
+	var req pasturePb.SearchBaseConfigList
+	if err := ginutil.BindProto(c, &req); err != nil {
+		apierr.AbortBadRequest(c, http.StatusBadRequest, err)
+		return
+	}
+
+	if err := valid.ValidateStruct(&req,
+		valid.Field(&req.Name, valid.Required),
+	); err != nil {
+		apierr.AbortBadRequest(c, http.StatusBadRequest, err)
+		return
+	}
+
+	if err := middleware.BackendOperation(c).OpsService.CreateOrUpdateDiseaseType(c, &req); err != nil {
+		apierr.ClassifiedAbort(c, err)
+		return
+	}
+
+	ginutil.JSONResp(c, &operationPb.CommonOK{
+		Code: http.StatusOK,
+		Msg:  "ok",
+		Data: &operationPb.Success{Success: true},
+	})
+}
+
+func SearchPrescriptionList(c *gin.Context) {
+	var req pasturePb.SearchPrescriptionRequest
+	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.SearchPrescriptionList(c, &req, pagination)
+	if err != nil {
+		apierr.ClassifiedAbort(c, err)
+		return
+	}
+	ginutil.JSONResp(c, res)
+}
+
+func CreatedOrUpdatePrescription(c *gin.Context) {
+	var req pasturePb.PrescriptionRequest
+	if err := ginutil.BindProto(c, &req); err != nil {
+		apierr.AbortBadRequest(c, http.StatusBadRequest, err)
+		return
+	}
+
+	if err := valid.ValidateStruct(&req,
+		valid.Field(&req.Name, valid.Required),
+		valid.Field(&req.DrugsList, valid.Required),
+	); err != nil {
+		apierr.AbortBadRequest(c, http.StatusBadRequest, err)
+		return
+	}
+
+	if err := middleware.BackendOperation(c).OpsService.CreateOrUpdatePrescription(c, &req); err != nil {
+		apierr.ClassifiedAbort(c, err)
+		return
+	}
+
+	ginutil.JSONResp(c, &operationPb.CommonOK{
+		Code: http.StatusOK,
+		Msg:  "ok",
+		Data: &operationPb.Success{Success: true},
+	})
+}
+
+func PrescriptionDetail(c *gin.Context) {
+	idStr := c.Param("id")
+	id, _ := strconv.ParseInt(idStr, 10, 64)
+	if err := valid.Validate(id, valid.Required); err != nil {
+		apierr.AbortBadRequest(c, http.StatusBadRequest, err)
+		return
+	}
+
+	res, err := middleware.BackendOperation(c).OpsService.PrescriptionDetail(c, id)
+	if err != nil {
+		apierr.ClassifiedAbort(c, err)
+		return
+	}
+
+	ginutil.JSONResp(c, res)
+}
+
+func SearchImmunizationList(c *gin.Context) {
+	var req pasturePb.ImmunizationRequest
+	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.ImmunizationSetList(c, &req, pagination)
+	if err != nil {
+		apierr.ClassifiedAbort(c, err)
+		return
+	}
+	ginutil.JSONResp(c, res)
+}
+
+func CreatedOrUpdateImmunization(c *gin.Context) {
+	var req pasturePb.ImmunizationRequest
+	if err := ginutil.BindProto(c, &req); err != nil {
+		apierr.AbortBadRequest(c, http.StatusBadRequest, err)
+		return
+	}
+
+	if err := valid.ValidateStruct(&req,
+		valid.Field(&req.Name, valid.Required),
+		valid.Field(&req.Conditions, valid.Required),
+		valid.Field(&req.Value, valid.Required),
+	); err != nil {
+		apierr.AbortBadRequest(c, http.StatusBadRequest, err)
+		return
+	}
+
+	if err := middleware.BackendOperation(c).OpsService.CreatedOrUpdateImmunization(c, &req); err != nil {
+		apierr.ClassifiedAbort(c, err)
+		return
+	}
+
+	ginutil.JSONResp(c, &operationPb.CommonOK{
+		Code: http.StatusOK,
+		Msg:  "ok",
+		Data: &operationPb.Success{Success: true},
+	})
+}
+
+func ImmunizationIsShow(c *gin.Context) {
+	immunizationIdStr := c.Param("id")
+	immunizationId, _ := strconv.Atoi(immunizationIdStr)
+
+	if err := valid.Validate(immunizationId, valid.Required, valid.Min(1)); err != nil {
+		apierr.ClassifiedAbort(c, err)
+		return
+	}
+	if err := middleware.Dependency(c).StoreEventHub.OpsService.ImmunizationIsShow(c, int64(immunizationId)); err != nil {
+		apierr.ClassifiedAbort(c, err)
+		return
+	}
+	ginutil.JSONResp(c, &operationPb.CommonOK{
+		Code: http.StatusOK,
+		Msg:  "ok",
+		Data: &operationPb.Success{Success: true},
+	})
+}
+
+// SameTimeCreatedOrUpdate 同期策略添加或者更新
+func SameTimeCreatedOrUpdate(c *gin.Context) {
+	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.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.CreateOrUpdateSameTime(c, &req); err != nil {
+		apierr.ClassifiedAbort(c, err)
+		return
+	}
+
+	ginutil.JSONResp(c, &operationPb.CommonOK{
+		Code: http.StatusOK,
+		Msg:  "ok",
+		Data: &operationPb.Success{Success: true},
+	})
+}
+
+// SameTimeList 同步策略列表
+func SameTimeList(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.SearchSameTimeList(c, &req, pagination)
+	if err != nil {
+		apierr.ClassifiedAbort(c, err)
+		return
+	}
+	ginutil.JSONResp(c, res)
+}
+
+func SameTimeIsShow(c *gin.Context) {
+	sameTimeIdStr := c.Param("id")
+	sameTimeId, _ := strconv.Atoi(sameTimeIdStr)
+
+	if err := valid.Validate(sameTimeId, valid.Required, valid.Min(1)); err != nil {
+		apierr.ClassifiedAbort(c, err)
+		return
+	}
+	if err := middleware.BackendOperation(c).OpsService.SameTimeIsShow(c, int64(sameTimeId)); err != nil {
+		apierr.ClassifiedAbort(c, err)
+		return
+	}
+	ginutil.JSONResp(c, &operationPb.CommonOK{
+		Code: http.StatusOK,
+		Msg:  "ok",
+		Data: &operationPb.Success{Success: true},
+	})
+}
+
+func SystemBasicList(c *gin.Context) {
+	res, err := middleware.BackendOperation(c).OpsService.SystemBasicList(c)
+	if err != nil {
+		apierr.ClassifiedAbort(c, err)
+		return
+	}
+	ginutil.JSONResp(c, res)
+}
+
+func SystemBasicEdit(c *gin.Context) {
+	var req pasturePb.BaseDataConfigBatch
+	if err := ginutil.BindProto(c, &req); err != nil {
+		apierr.AbortBadRequest(c, http.StatusBadRequest, err)
+		return
+	}
+
+	if err := valid.ValidateStruct(&req,
+		valid.Field(&req.Item, valid.Required, valid.Each(valid.By(func(value interface{}) error {
+			s := value.(pasturePb.BaseDataConfig)
+			return valid.ValidateStruct(&s,
+				valid.Field(&s.Id, valid.Required),
+				valid.Field(&s.Name, valid.Required),
+			)
+		}))),
+	); err != nil {
+		apierr.AbortBadRequest(c, http.StatusBadRequest, err)
+		return
+	}
+
+	err := middleware.BackendOperation(c).OpsService.SystemBasicEdit(c, &req)
+	if err != nil {
+		apierr.ClassifiedAbort(c, err)
+		return
+	}
+	ginutil.JSONResp(c, &operationPb.CommonOK{
+		Code: http.StatusOK,
+		Msg:  "ok",
+		Data: &operationPb.Success{Success: true},
+	})
+}
+
+func SaleDealerCreateOrUpdate(c *gin.Context) {
+	var req pasturePb.DealerItem
+	if err := ginutil.BindProto(c, &req); err != nil {
+		apierr.AbortBadRequest(c, http.StatusBadRequest, err)
+		return
+	}
+
+	if err := valid.ValidateStruct(&req,
+		valid.Field(&req.Name, valid.Required),
+		valid.Field(&req.Phone, valid.Required, is.E164),
+		valid.Field(&req.Contacts, valid.Required),
+	); err != nil {
+		apierr.ClassifiedAbort(c, err)
+		return
+	}
+
+	if err := middleware.BackendOperation(c).OpsService.CreateOrUpdateDealer(c, &req); err != nil {
+		apierr.ClassifiedAbort(c, err)
+		return
+	}
+
+	ginutil.JSONResp(c, &operationPb.CommonOK{
+		Code: http.StatusOK,
+		Msg:  "ok",
+		Data: &operationPb.Success{Success: true},
+	})
+}
+
+func SaleDealerList(c *gin.Context) {
+	var req pasturePb.SearchNameRequest
+	if err := ginutil.BindProto(c, &req); err != nil {
+		apierr.AbortBadRequest(c, http.StatusBadRequest, err)
+		return
+	}
+
+	res, err := middleware.BackendOperation(c).OpsService.SearchDealerList(c, &req)
+	if err != nil {
+		apierr.ClassifiedAbort(c, err)
+		return
+	}
+	ginutil.JSONResp(c, res)
+}
+
+func SaleDealerIsShow(c *gin.Context) {
+	dealerIdStr := c.Param("id")
+	dealerId, _ := strconv.Atoi(dealerIdStr)
+
+	if err := valid.Validate(dealerId, valid.Required, valid.Min(1)); err != nil {
+		apierr.ClassifiedAbort(c, err)
+		return
+	}
+	if err := middleware.BackendOperation(c).OpsService.DeleteDealer(c, int64(dealerId)); err != nil {
+		apierr.ClassifiedAbort(c, err)
+		return
+	}
+	ginutil.JSONResp(c, &operationPb.CommonOK{
+		Code: http.StatusOK,
+		Msg:  "ok",
+		Data: &operationPb.Success{Success: true},
+	})
+}

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

@@ -1,98 +0,0 @@
-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"
-	"gitee.com/xuyiping_admin/pkg/apierr"
-	"gitee.com/xuyiping_admin/pkg/ginutil"
-	"gitee.com/xuyiping_admin/pkg/valid"
-	"github.com/gin-gonic/gin"
-)
-
-// SameTimeCreatedOrUpdate 同期策略添加或者更新
-func SameTimeCreatedOrUpdate(c *gin.Context) {
-	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.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.CreateOrUpdateSameTime(c, &req); err != nil {
-		apierr.ClassifiedAbort(c, err)
-		return
-	}
-
-	ginutil.JSONResp(c, &operationPb.CommonOK{
-		Code: http.StatusOK,
-		Msg:  "ok",
-		Data: &operationPb.Success{Success: true},
-	})
-}
-
-// SameTimeList 同步策略列表
-func SameTimeList(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.SearchSameTimeList(c, &req, pagination)
-	if err != nil {
-		apierr.ClassifiedAbort(c, err)
-		return
-	}
-	ginutil.JSONResp(c, res)
-}
-
-func SameTimeIsShow(c *gin.Context) {
-	sameTimeIdStr := c.Param("id")
-	sameTimeId, _ := strconv.Atoi(sameTimeIdStr)
-
-	if err := valid.Validate(sameTimeId, valid.Required, valid.Min(1)); err != nil {
-		apierr.ClassifiedAbort(c, err)
-		return
-	}
-	if err := middleware.BackendOperation(c).OpsService.SameTimeIsShow(c, int64(sameTimeId)); err != nil {
-		apierr.ClassifiedAbort(c, err)
-		return
-	}
-	ginutil.JSONResp(c, &operationPb.CommonOK{
-		Code: http.StatusOK,
-		Msg:  "ok",
-		Data: &operationPb.Success{Success: true},
-	})
-}

+ 15 - 1
http/handler/system/dept.go

@@ -45,7 +45,7 @@ func DepIsShow(c *gin.Context) {
 		return
 	}
 
-	if err := middleware.Dependency(c).StoreEventHub.OpsService.SystemDepDelete(c, int64(Id)); err != nil {
+	if err := middleware.Dependency(c).StoreEventHub.OpsService.SystemDepthDelete(c, int64(Id)); err != nil {
 		apierr.ClassifiedAbort(c, err)
 		return
 	}
@@ -72,3 +72,17 @@ func DepCreateOrUpdate(c *gin.Context) {
 		Data: &operationPb.Success{Success: true},
 	})
 }
+
+func DeptTree(c *gin.Context) {
+	var req pasturePb.SearchDeptRequest
+	if err := ginutil.BindProto(c, &req); err != nil {
+		apierr.ClassifiedAbort(c, err)
+		return
+	}
+	res, err := middleware.Dependency(c).StoreEventHub.OpsService.SystemDeptTree(c, &req)
+	if err != nil {
+		apierr.ClassifiedAbort(c, err)
+		return
+	}
+	ginutil.JSONResp(c, res)
+}

+ 6 - 57
http/handler/system/menu.go

@@ -3,47 +3,13 @@ package system
 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"
 )
 
-// CreatedOrUpdateSystemMenu 添加或者更新系统菜单权限
-func CreatedOrUpdateSystemMenu(c *gin.Context) {
-	var req pasturePb.SearchMenuRequest
-	if err := ginutil.BindProto(c, &req); err != nil {
-		apierr.AbortBadRequest(c, http.StatusBadRequest, err)
-		return
-	}
-
-	if err := valid.ValidateStruct(&req,
-		valid.Field(&req.Name, valid.Required),
-		valid.Field(&req.Title, valid.Required),
-		valid.Field(&req.MenuType, valid.Required, valid.Min(0), valid.Max(2)),
-		valid.Field(&req.Path, valid.Required),
-	); err != nil {
-		apierr.AbortBadRequest(c, http.StatusBadRequest, err)
-		return
-	}
-
-	if err := middleware.BackendOperation(c).OpsService.CreateOrUpdateSystemMenu(c, &req); err != nil {
-		apierr.ClassifiedAbort(c, err)
-		return
-	}
-
-	ginutil.JSONResp(c, &operationPb.CommonOK{
-		Code: http.StatusOK,
-		Msg:  "ok",
-		Data: &operationPb.Success{Success: true},
-	})
-}
-
 // SearchSystemMenuList 菜单列表查询
 func SearchSystemMenuList(c *gin.Context) {
 	var req pasturePb.SearchMenuRequest
@@ -66,31 +32,14 @@ func SearchSystemMenuList(c *gin.Context) {
 	ginutil.JSONResp(c, res)
 }
 
-// DeleteSystemMenu 删除菜单
-func DeleteSystemMenu(c *gin.Context) {
-	menuIdStr := c.Param("id")
-	menuId, _ := strconv.Atoi(menuIdStr)
-
-	if err := valid.Validate(menuId, valid.Required, valid.Min(1)); err != nil {
-		apierr.ClassifiedAbort(c, err)
-		return
-	}
-
-	if err := middleware.BackendOperation(c).OpsService.DeleteSystemMenu(c, int64(menuId)); err != nil {
-		apierr.ClassifiedAbort(c, err)
-		return
-	}
-
-	ginutil.JSONResp(c, &operationPb.CommonOK{
-		Code: http.StatusOK,
-		Msg:  "ok",
-		Data: &operationPb.Success{Success: true},
-	})
-}
-
 // GetSystemMenuTree 角色菜单
 func GetSystemMenuTree(c *gin.Context) {
-	res, err := middleware.Dependency(c).StoreEventHub.OpsService.SystemMenuTree(c)
+	var req pasturePb.SearchMenuRequest
+	if err := ginutil.BindProto(c, &req); err != nil {
+		apierr.AbortBadRequest(c, http.StatusBadRequest, err)
+		return
+	}
+	res, err := middleware.Dependency(c).StoreEventHub.OpsService.SystemMenuTree(c, &req)
 	if err != nil {
 		apierr.ClassifiedAbort(c, err)
 		return

+ 31 - 2
http/handler/system/user.go

@@ -48,8 +48,9 @@ func UserCreateOrUpdate(c *gin.Context) {
 
 	if err := valid.ValidateStruct(&req,
 		valid.Field(&req.Name, valid.Required),
-		valid.Field(&req.DeptId, valid.Required),
+		valid.Field(&req.PastureDepthList, valid.Required),
 		valid.Field(&req.Mobile, valid.Required),
+		valid.Field(&req.Password, valid.Required),
 	); err != nil {
 		apierr.AbortBadRequest(c, http.StatusBadRequest, err)
 		return
@@ -193,7 +194,7 @@ func UserRoleSave(c *gin.Context) {
 
 	if err := valid.ValidateStruct(&req,
 		valid.Field(&req.UserId, valid.Required),
-		valid.Field(&req.RoleId, valid.Required),
+		valid.Field(&req.RoleIds, valid.Required),
 	); err != nil {
 		apierr.AbortBadRequest(c, http.StatusBadRequest, err)
 		return
@@ -210,3 +211,31 @@ func UserRoleSave(c *gin.Context) {
 		Data: &operationPb.Success{Success: true},
 	})
 }
+
+// GetUserMenu 获取系统用户菜单权限
+func GetUserMenu(c *gin.Context) {
+	res, err := middleware.BackendOperation(c).OpsService.GetSystemUserMenu(c)
+	if err != nil {
+		apierr.ClassifiedAbort(c, err)
+		return
+	}
+	ginutil.JSONResp(c, res)
+}
+
+// Logout 用户登出,
+func Logout(c *gin.Context) {
+	ginutil.JSONResp(c, &operationPb.CommonOK{
+		Code: http.StatusOK,
+		Msg:  "ok",
+		Data: &operationPb.Success{Success: true},
+	})
+}
+
+func PastureList(c *gin.Context) {
+	res, err := middleware.BackendOperation(c).OpsService.SearchUserPastureList(c)
+	if err != nil {
+		apierr.ClassifiedAbort(c, err)
+		return
+	}
+	ginutil.JSONResp(c, res)
+}

+ 171 - 0
http/handler/test.go

@@ -0,0 +1,171 @@
+package handler
+
+import (
+	"kpt-pasture/http/middleware"
+	"net/http"
+	"strconv"
+
+	"gitee.com/xuyiping_admin/pkg/valid"
+
+	pasturePb "gitee.com/xuyiping_admin/go_proto/proto/go/backend/cow"
+
+	"gitee.com/xuyiping_admin/pkg/apierr"
+
+	operationPb "gitee.com/xuyiping_admin/go_proto/proto/go/backend/operation"
+	"gitee.com/xuyiping_admin/pkg/ginutil"
+	"github.com/gin-gonic/gin"
+)
+
+func CowNeckRingBound(c *gin.Context) {
+	pagination := &pasturePb.PaginationModel{
+		Page:       int32(c.GetInt(middleware.Page)),
+		PageSize:   int32(c.GetInt(middleware.PageSize)),
+		PageOffset: int32(c.GetInt(middleware.PageOffset)),
+	}
+
+	if err := middleware.BackendOperation(c).OpsService.CowNeckRingNumberBound(c, pagination); err != nil {
+		apierr.ClassifiedAbort(c, err)
+		return
+	}
+
+	ginutil.JSONResp(c, &operationPb.CommonOK{
+		Code: http.StatusOK,
+		Msg:  "ok",
+		Data: &operationPb.Success{Success: true},
+	})
+}
+
+func CowNeckRingBound2(c *gin.Context) {
+	pagination := &pasturePb.PaginationModel{
+		Page:       int32(c.GetInt(middleware.Page)),
+		PageSize:   int32(c.GetInt(middleware.PageSize)),
+		PageOffset: int32(c.GetInt(middleware.PageOffset)),
+	}
+
+	if err := middleware.BackendOperation(c).OpsService.CowNeckRingNumberBound2(c, pagination); err != nil {
+		apierr.ClassifiedAbort(c, err)
+		return
+	}
+
+	ginutil.JSONResp(c, &operationPb.CommonOK{
+		Code: http.StatusOK,
+		Msg:  "ok",
+		Data: &operationPb.Success{Success: true},
+	})
+}
+
+func UpdateCowPen(c *gin.Context) {
+	pagination := &pasturePb.PaginationModel{
+		Page:       int32(c.GetInt(middleware.Page)),
+		PageSize:   int32(c.GetInt(middleware.PageSize)),
+		PageOffset: int32(c.GetInt(middleware.PageOffset)),
+	}
+
+	if err := middleware.BackendOperation(c).OpsService.UpdateCowPen(c, pagination); err != nil {
+		apierr.ClassifiedAbort(c, err)
+		return
+	}
+
+	ginutil.JSONResp(c, &operationPb.CommonOK{
+		Code: http.StatusOK,
+		Msg:  "ok",
+		Data: &operationPb.Success{Success: true},
+	})
+}
+
+func DataWarning(c *gin.Context) {
+	userIdStr := c.Param("userId")
+	if err := valid.Validate(userIdStr, valid.Required); err != nil {
+		apierr.AbortBadRequest(c, http.StatusBadRequest, err)
+		return
+	}
+	userId, _ := strconv.Atoi(userIdStr)
+	if err := middleware.BackendOperation(c).OpsService.TestDataWaring(c, int64(userId)); err != nil {
+		apierr.ClassifiedAbort(c, err)
+		return
+	}
+
+	ginutil.JSONResp(c, &operationPb.CommonOK{
+		Code: http.StatusOK,
+		Msg:  "ok",
+		Data: &operationPb.Success{Success: true},
+	})
+}
+
+func NeckRingOriginalAsync(c *gin.Context) {
+	pastureIdStr := c.Query("pasture_id")
+	if err := valid.Validate(pastureIdStr, valid.Required); err != nil {
+		apierr.AbortBadRequest(c, http.StatusBadRequest, err)
+		return
+	}
+	pastureId, _ := strconv.Atoi(pastureIdStr)
+	pagination := &pasturePb.PaginationModel{
+		Page:       int32(c.GetInt(middleware.Page)),
+		PageSize:   int32(c.GetInt(middleware.PageSize)),
+		PageOffset: int32(c.GetInt(middleware.PageOffset)),
+	}
+	if err := middleware.BackendOperation(c).OpsService.NeckRingOriginalAsync(c, int64(pastureId), pagination); err != nil {
+		apierr.ClassifiedAbort(c, err)
+		return
+	}
+
+	ginutil.JSONResp(c, &operationPb.CommonOK{
+		Code: http.StatusOK,
+		Msg:  "ok",
+		Data: &operationPb.Success{Success: true},
+	})
+}
+
+func PastureInit(c *gin.Context) {
+	pastureIdStr := c.Param("pasture_id")
+	pastureId, _ := strconv.Atoi(pastureIdStr)
+	if err := middleware.BackendOperation(c).OpsService.PastureInit(c, int64(pastureId)); err != nil {
+		apierr.ClassifiedAbort(c, err)
+		return
+	}
+
+	ginutil.JSONResp(c, &operationPb.CommonOK{
+		Code: http.StatusOK,
+		Msg:  "ok",
+		Data: &operationPb.Success{Success: true},
+	})
+}
+
+func CalvingAge(c *gin.Context) {
+	if err := middleware.BackendOperation(c).OpsService.CalvingAge(c); err != nil {
+		apierr.ClassifiedAbort(c, err)
+		return
+	}
+
+	ginutil.JSONResp(c, &operationPb.CommonOK{
+		Code: http.StatusOK,
+		Msg:  "ok",
+		Data: &operationPb.Success{Success: true},
+	})
+}
+
+func AdmissionAge(c *gin.Context) {
+	if err := middleware.BackendOperation(c).OpsService.AdmissionAge(c); err != nil {
+		apierr.ClassifiedAbort(c, err)
+		return
+	}
+
+	ginutil.JSONResp(c, &operationPb.CommonOK{
+		Code: http.StatusOK,
+		Msg:  "ok",
+		Data: &operationPb.Success{Success: true},
+	})
+}
+
+func SystemMenuInit(c *gin.Context) {
+	if err := middleware.BackendOperation(c).OpsService.SystemMenuInit(c); err != nil {
+		apierr.ClassifiedAbort(c, err)
+		return
+	}
+
+	ginutil.JSONResp(c, &operationPb.CommonOK{
+		Code: http.StatusOK,
+		Msg:  "ok",
+		Data: &operationPb.Success{Success: true},
+	})
+}

+ 212 - 27
http/handler/upload/upload.go

@@ -3,16 +3,26 @@ package upload
 import (
 	"fmt"
 	"kpt-pasture/config"
+	"kpt-pasture/http/middleware"
+	"mime/multipart"
 	"net/http"
 	"os"
+	"path/filepath"
+	"strings"
 	"time"
 
+	"github.com/xuri/excelize/v2"
+
+	operationPb "gitee.com/xuyiping_admin/go_proto/proto/go/backend/operation"
+	"gitee.com/xuyiping_admin/pkg/ginutil"
+
 	"gitee.com/xuyiping_admin/pkg/apierr"
 	"gitee.com/xuyiping_admin/pkg/xerr"
 	"github.com/gin-gonic/gin"
 )
 
 func Photos(c *gin.Context) {
+
 	form, err := c.MultipartForm()
 	if err != nil {
 		apierr.AbortBadRequest(c, http.StatusBadRequest, xerr.Customf("No multipartForm: %s", err.Error()))
@@ -25,41 +35,216 @@ func Photos(c *gin.Context) {
 		return
 	}
 
-	workDir := fmt.Sprintf("%s", config.WorkDir)
-	pathDir := fmt.Sprintf("/files/photos/%s", time.Now().Format("20060102"))
-	saveDir := fmt.Sprintf("%s/%s", workDir, pathDir)
-	if _, err = os.Stat(saveDir); os.IsNotExist(err) {
-		if err = os.MkdirAll(saveDir, 0755); err != nil {
-			apierr.AbortBadRequest(c, http.StatusBadRequest, xerr.Customf("创建目录失败: %s", err.Error()))
-			return
+	res, err := middleware.BackendOperation(c).OpsService.Photos(c, files)
+	if err != nil {
+		apierr.AbortBadRequest(c, http.StatusBadRequest, err)
+		return
+	}
+
+	c.JSON(http.StatusOK, gin.H{"code": http.StatusOK, "Msg": "ok", "data": res})
+}
+
+func Files(c *gin.Context) {
+	// 1. 获取上传的文件
+	file, err := c.FormFile("file")
+	if err != nil {
+		apierr.AbortBadRequest(c, http.StatusBadRequest, xerr.Customf("获取文件失败: %s", err.Error()))
+		return
+	}
+
+	cf := GetExcelImportConfig(c.GetHeader("farmid"))
+	// 2. 验证文件基本属性
+	if err = validateFile(file, cf); err != nil {
+		apierr.AbortBadRequest(c, http.StatusBadRequest, err)
+		return
+	}
+
+	// 3. 保存文件到临时目录
+	filePath, err := saveUploadedFile(c, file)
+	if err != nil {
+		apierr.AbortBadRequest(c, http.StatusInternalServerError, err)
+		return
+	}
+	defer os.Remove(filePath) // 处理完成后删除临时文件
+
+	// 4. 读取并验证Excel文件
+	excelData, err := readExcelFile(filePath, filepath.Ext(file.Filename))
+	if err != nil {
+		apierr.AbortBadRequest(c, http.StatusInternalServerError, err)
+		return
+	}
+	// 5. 验证表头
+	if err = validateHeaders(excelData[0], cf.RequiredHeaders); err != nil {
+		apierr.AbortBadRequest(c, http.StatusInternalServerError, err)
+		return
+	}
+	// 实现处理逻辑
+	if err = middleware.BackendOperation(c).OpsService.ImportExcel2(c, excelData, cf.RequiredHeaders); err != nil {
+		apierr.AbortBadRequest(c, http.StatusBadRequest, err)
+		return
+	}
+
+	ginutil.JSONResp(c, &operationPb.CommonOK{
+		Code: http.StatusOK,
+		Msg:  "ok",
+		Data: &operationPb.Success{Success: true},
+	})
+}
+
+// ExcelImportConfig 导入配置
+type ExcelImportConfig struct {
+	AllowedExtensions []string
+	MaxFileSize       int64
+	RequiredHeaders   []string // 必须包含的表头
+	HeadOfField       []string
+}
+
+// readExcelFile 读取Excel文件
+func readExcelFile(filePath, ext string) ([][]string, error) {
+	// 尝试使用excelize读取
+	f, err := excelize.OpenFile(filePath)
+	if err != nil {
+		// 如果是xls格式且读取失败,尝试使用xls库
+		if ext == ".xls" {
+			return readXlsFile(filePath)
 		}
+		return nil, xerr.Customf("读取Excel文件失败: %s", err.Error())
+	}
+	defer f.Close()
+
+	// 获取第一个工作表
+	sheetName := f.GetSheetName(0)
+	rows, err := f.GetRows(sheetName)
+	if err != nil {
+		return nil, xerr.Customf("读取工作表失败: %s", err.Error())
+	}
+
+	if len(rows) == 0 {
+		return nil, xerr.Custom("Excel文件为空")
 	}
 
-	// 处理每个文件
-	filePaths := make([]string, len(files))
-	timestamp := time.Now().Unix()
-	for i, file := range files {
-		if file.Header.Get("Content-Type") != "image/jpeg" &&
-			file.Header.Get("Content-Type") != "image/png" &&
-			file.Header.Get("Content-Type") != "image/gif" {
-			apierr.AbortBadRequest(c, http.StatusBadRequest, xerr.Customf("图片格式错误: %s", file.Filename))
-			return
+	return rows, nil
+}
+
+// readXlsFile 使用xls库读取旧版Excel文件
+func readXlsFile(filePath string) ([][]string, error) {
+	// 这里需要实现xls文件的读取逻辑
+	// 可以使用第三方库如github.com/extrame/xls
+	return nil, xerr.Custom("暂不支持.xls格式文件")
+}
+
+func GetExcelImportConfig(farmId string) *ExcelImportConfig {
+	switch farmId {
+	case "e50cd455dec93edf3d8135abbc770680":
+		return &ExcelImportConfig{
+			AllowedExtensions: []string{".xlsx", ".xls"},
+			RequiredHeaders: []string{"", "", "耳号", "牛舍名称", "性别", "出生日期", "胎次", "进场日期", "父号", "母号", "最近配种日期", "预产期", "配后天数",
+				"累计配次", "最近妊检日期", "最近产犊日期", "产后天数", "品种", "出生重量", "断奶日期", "是否禁配", "体重", "体高", "流产日期", "流产原因"},
+			HeadOfField: []string{"", "", "earNumber", "penName", "sex", "birthAt", "lact", "admissionAt", "fatherNumber", "motherNumber",
+				"lastMatingAt", "", "matingAge", "allMatingTimes", "lastPregnantCheckAt", "lastCalvingAt", "lactationAge",
+				"cowKind", "birthWeight", "weaningAt", "isForbiddenMating", "currentWeight", "currenWtHeight", "lastAbortionAt", ""},
+			MaxFileSize: 10 << 20,
+		}
+	default:
+		return nil
+	}
+}
+
+// ExcelHandler Excel处理接口
+type ExcelHandler interface {
+	ProcessExcelData([][]string) error
+}
+
+// validateFile 验证文件基本属性
+func validateFile(file *multipart.FileHeader, cfg *ExcelImportConfig) error {
+	// 检查文件扩展名
+	ext := strings.ToLower(filepath.Ext(file.Filename))
+	validExt := false
+	for _, allowedExt := range cfg.AllowedExtensions {
+		if ext == allowedExt {
+			validExt = true
+			break
 		}
-		if file.Size > 1024*1024*5 {
-			apierr.AbortBadRequest(c, http.StatusBadRequest, xerr.Custom("单个图片文件不能超过5MB"))
-			return
+	}
+	if !validExt {
+		return xerr.Customf("不支持的文件类型,仅支持: %v", cfg.AllowedExtensions)
+	}
+
+	// 检查文件大小
+	if file.Size > cfg.MaxFileSize {
+		return xerr.Customf("文件大小超过限制(最大%dMB)", cfg.MaxFileSize>>20)
+	}
+
+	return nil
+}
+
+// saveUploadedFile 保存上传的文件
+func saveUploadedFile(c *gin.Context, file *multipart.FileHeader) (string, error) {
+	// 创建临时目录
+	uploadDir := filepath.Join(config.WorkDir, "temp", "excel")
+	if err := os.MkdirAll(uploadDir, 0755); err != nil {
+		return "", xerr.Customf("创建临时目录失败: %s", err.Error())
+	}
+
+	// 生成唯一文件名
+	filename := fmt.Sprintf("%d%s", time.Now().UnixNano(), filepath.Ext(file.Filename))
+	filePath := filepath.Join(uploadDir, filename)
+
+	// 保存文件
+	if err := c.SaveUploadedFile(file, filePath); err != nil {
+		return "", xerr.Customf("保存文件失败: %s", err.Error())
+	}
+
+	return filePath, nil
+}
+
+// validateHeaders 验证表头
+func validateHeaders(headers []string, requiredHeaders []string) error {
+	if len(requiredHeaders) == 0 {
+		return nil
+	}
+
+	headerMap := make(map[string]bool)
+	for _, h := range headers {
+		headerMap[strings.TrimSpace(h)] = true
+	}
+
+	var missingHeaders []string
+	for _, req := range requiredHeaders {
+		if req == "" {
+			continue
 		}
-		fpath := fmt.Sprintf("%s/%d_%d_%s", saveDir, timestamp, i+1, file.Filename)
-		urlPath := fmt.Sprintf("%s/%d_%d_%s", pathDir, timestamp, i+1, file.Filename)
-		if err = c.SaveUploadedFile(file, fpath); err != nil {
-			apierr.AbortBadRequest(c, http.StatusBadRequest, xerr.Customf("保存文件失败: %s", err.Error()))
-			return
+		if !headerMap[req] {
+			missingHeaders = append(missingHeaders, req)
 		}
-		filePaths[i] = urlPath
 	}
-	c.JSON(http.StatusOK, gin.H{"code": http.StatusOK, "message": "ok", "data": filePaths})
+	if len(missingHeaders) > 0 {
+		return xerr.Customf("缺少必要表头: %v", missingHeaders)
+	}
+	return nil
 }
 
-func Files(c *gin.Context) {
+func OssVideo(c *gin.Context) {
+	filename := c.Param("name")
+	videoPath := fmt.Sprintf("%s/files/video/%s", config.WorkDir, filename)
+
+	// 打开视频文件
+	file, err := os.Open(videoPath)
+	if err != nil {
+		c.JSON(http.StatusNotFound, gin.H{"error": "Video not found"})
+		return
+	}
+	defer file.Close()
+	fileInfo, err := file.Stat()
+	if err != nil {
+		c.JSON(http.StatusInternalServerError, gin.H{"error": "Could not get file info"})
+		return
+	}
+
+	// 设置响应头
+	c.Header("Content-Type", "video/mp4")
+	c.Header("Content-Length", fmt.Sprintf("%d", fileInfo.Size()))
 
+	// 流式传输文件内容
+	http.ServeContent(c.Writer, c.Request, fileInfo.Name(), fileInfo.ModTime(), file)
 }

+ 131 - 0
http/handler/warning/warning.go

@@ -0,0 +1,131 @@
+package warning
+
+import (
+	"kpt-pasture/http/middleware"
+	"net/http"
+	"strconv"
+
+	operationPb "gitee.com/xuyiping_admin/go_proto/proto/go/backend/operation"
+	"gitee.com/xuyiping_admin/pkg/valid"
+
+	pasturePb "gitee.com/xuyiping_admin/go_proto/proto/go/backend/cow"
+	"gitee.com/xuyiping_admin/pkg/apierr"
+	"gitee.com/xuyiping_admin/pkg/ginutil"
+	"github.com/gin-gonic/gin"
+)
+
+func NeckRingWarningEstrusItem(c *gin.Context) {
+	var req pasturePb.WarningItemsRequest
+	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.NeckRingWarningEstrusOrAbortionCowList(c, &req, pagination)
+	if err != nil {
+		apierr.ClassifiedAbort(c, err)
+		return
+	}
+
+	ginutil.JSONResp(c, res)
+}
+
+func NeckRingNoEstrusBatch(c *gin.Context) {
+	var req pasturePb.NeckRingNoEstrusBatchRequest
+	if err := ginutil.BindProto(c, &req); err != nil {
+		apierr.AbortBadRequest(c, http.StatusBadRequest, err)
+		return
+	}
+
+	if err := valid.ValidateStruct(&req,
+		valid.Field(&req.EarNumbers, valid.Required, valid.Required),
+	); err != nil {
+		apierr.AbortBadRequest(c, http.StatusBadRequest, err)
+		return
+	}
+
+	if err := middleware.BackendOperation(c).OpsService.NeckRingNoEstrusBatch(c, &req); err != nil {
+		apierr.ClassifiedAbort(c, err)
+		return
+	}
+
+	ginutil.JSONResp(c, &operationPb.CommonOK{
+		Code: http.StatusOK,
+		Msg:  "ok",
+		Data: &operationPb.Success{Success: true},
+	})
+}
+
+func NeckRingWarningHealthItem(c *gin.Context) {
+	var req pasturePb.HealthWarningRequest
+	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.NeckRingWarningHealthCowList(c, &req, pagination)
+	if err != nil {
+		apierr.ClassifiedAbort(c, err)
+		return
+	}
+
+	ginutil.JSONResp(c, res)
+}
+
+func NeckRingNoHealthBatch(c *gin.Context) {
+	var req pasturePb.NeckRingNoEstrusBatchRequest
+	if err := ginutil.BindProto(c, &req); err != nil {
+		apierr.AbortBadRequest(c, http.StatusBadRequest, err)
+		return
+	}
+
+	if err := valid.ValidateStruct(&req,
+		valid.Field(&req.EarNumbers, valid.Required, valid.Required),
+	); err != nil {
+		apierr.AbortBadRequest(c, http.StatusBadRequest, err)
+		return
+	}
+
+	if err := middleware.BackendOperation(c).OpsService.NeckRingNoDiseaseBatch(c, &req); err != nil {
+		apierr.ClassifiedAbort(c, err)
+		return
+	}
+
+	ginutil.JSONResp(c, &operationPb.CommonOK{
+		Code: http.StatusOK,
+		Msg:  "ok",
+		Data: &operationPb.Success{Success: true},
+	})
+}
+
+func DataNoticeDetail(c *gin.Context) {
+	res := middleware.BackendOperation(c).OpsService.DataNoticeDetail(c)
+	c.JSON(http.StatusOK, res)
+}
+
+func DataNoticeKnown(c *gin.Context) {
+	idStr := c.Param("id")
+	dataNoticeId, _ := strconv.Atoi(idStr)
+	if err := middleware.BackendOperation(c).OpsService.DataNoticeKnown(c, int64(dataNoticeId)); err != nil {
+		apierr.ClassifiedAbort(c, err)
+		return
+	}
+
+	ginutil.JSONResp(c, &operationPb.CommonOK{
+		Code: http.StatusOK,
+		Msg:  "ok",
+		Data: &operationPb.Success{Success: true},
+	})
+}

+ 104 - 0
http/handler/work/calendar.go

@@ -0,0 +1,104 @@
+package work
+
+import (
+	"kpt-pasture/http/middleware"
+	"net/http"
+
+	pasturePb "gitee.com/xuyiping_admin/go_proto/proto/go/backend/cow"
+	"gitee.com/xuyiping_admin/pkg/apierr"
+	"gitee.com/xuyiping_admin/pkg/ginutil"
+	"gitee.com/xuyiping_admin/pkg/valid"
+	"github.com/gin-gonic/gin"
+)
+
+func CalendarList(c *gin.Context) {
+	var req pasturePb.CalendarRequest
+	if err := ginutil.BindProto(c, &req); err != nil {
+		apierr.AbortBadRequest(c, http.StatusBadRequest, err)
+		return
+	}
+
+	if err := valid.ValidateStruct(&req,
+		valid.Field(&req.ShowStartDay, valid.Required),
+		valid.Field(&req.ShowEndDay, valid.Required),
+	); err != nil {
+		apierr.AbortBadRequest(c, http.StatusBadRequest, err)
+		return
+	}
+
+	res, err := middleware.Dependency(c).StoreEventHub.OpsService.CalendarList(c, &req)
+	if err != nil {
+		apierr.ClassifiedAbort(c, err)
+		return
+	}
+	ginutil.JSONResp(c, res)
+}
+
+func CalendarTableDetail(c *gin.Context) {
+	var req pasturePb.CalendarTableRequest
+	if err := ginutil.BindProto(c, &req); err != nil {
+		apierr.AbortBadRequest(c, http.StatusBadRequest, err)
+		return
+	}
+
+	if err := valid.ValidateStruct(&req,
+		valid.Field(&req.CalendarType, valid.Required),
+		valid.Field(&req.Start, valid.Required),
+	); err != nil {
+		apierr.AbortBadRequest(c, http.StatusBadRequest, err)
+		return
+	}
+
+	pagination := &pasturePb.PaginationModel{
+		Page:       int32(c.GetInt(middleware.Page)),
+		PageSize:   int32(c.GetInt(middleware.PageSize)),
+		PageOffset: int32(c.GetInt(middleware.PageOffset)),
+	}
+
+	res, err := middleware.Dependency(c).StoreEventHub.OpsService.CalendarTableDetail(c, &req, pagination)
+	if err != nil {
+		apierr.ClassifiedAbort(c, err)
+		return
+	}
+	c.JSON(http.StatusOK, res)
+}
+
+func CalendarToDoList(c *gin.Context) {
+	var req pasturePb.CalendarToDoRequest
+	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.CalendarToDoList(c, &req, pagination)
+	if err != nil {
+		apierr.ClassifiedAbort(c, err)
+		return
+	}
+	ginutil.JSONResp(c, res)
+}
+
+func ImmunizationItems(c *gin.Context) {
+	var req pasturePb.ItemsRequest
+	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.ImmunisationCowList(c, &req, pagination)
+	if err != nil {
+		apierr.ClassifiedAbort(c, err)
+		return
+	}
+	ginutil.JSONResp(c, res)
+}

+ 132 - 0
http/handler/work/item.go

@@ -0,0 +1,132 @@
+package work
+
+import (
+	"kpt-pasture/http/middleware"
+	"net/http"
+
+	pasturePb "gitee.com/xuyiping_admin/go_proto/proto/go/backend/cow"
+	"gitee.com/xuyiping_admin/pkg/apierr"
+	"gitee.com/xuyiping_admin/pkg/ginutil"
+	"github.com/gin-gonic/gin"
+)
+
+func SameTimeCowList(c *gin.Context) {
+	var req pasturePb.ItemsRequest
+	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.SameTimeCowList(c, &req, pagination)
+	if err != nil {
+		apierr.ClassifiedAbort(c, err)
+		return
+	}
+	ginutil.JSONResp(c, res)
+}
+
+func PregnancyCheckCowList(c *gin.Context) {
+	var req pasturePb.ItemsRequest
+	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.PregnancyCheckCowList(c, &req, pagination)
+	if err != nil {
+		apierr.ClassifiedAbort(c, err)
+		return
+	}
+	ginutil.JSONResp(c, res)
+}
+
+func WeaningCowList(c *gin.Context) {
+	var req pasturePb.ItemsRequest
+	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.WeaningCowList(c, &req, pagination)
+	if err != nil {
+		apierr.ClassifiedAbort(c, err)
+		return
+	}
+	ginutil.JSONResp(c, res)
+}
+
+func MatingCowList(c *gin.Context) {
+	var req pasturePb.ItemsRequest
+	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.MatingCowList(c, &req, pagination)
+	if err != nil {
+		apierr.ClassifiedAbort(c, err)
+		return
+	}
+	ginutil.JSONResp(c, res)
+}
+
+func CalvingList(c *gin.Context) {
+	var req pasturePb.ItemsRequest
+	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.CalvingCowList(c, &req, pagination)
+	if err != nil {
+		apierr.ClassifiedAbort(c, err)
+		return
+	}
+	ginutil.JSONResp(c, res)
+}
+
+func TaskDetailList(c *gin.Context) {
+	var req pasturePb.CalendarToDoRequest
+	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.TaskDetail(c, &req, pagination)
+	if err != nil {
+		apierr.ClassifiedAbort(c, err)
+		return
+	}
+
+	ginutil.JSONResp(c, res)
+}

+ 17 - 1
http/middleware/sso.go → http/middleware/auth.go

@@ -17,6 +17,7 @@ const (
 	Authorization = "Authorization"
 	ToKenPrefix   = "Bearer "
 	UserName      = "userName"
+	FarmId        = "FarmId"
 	XRequestId    = "X-Request-Id"
 )
 
@@ -33,10 +34,25 @@ func GetXRequestId(c *gin.Context) string {
 	return item
 }
 
+func GetFarmId(c *gin.Context) string {
+	return c.Request.Header.Get(FarmId)
+}
+
 func unauthorized(c *gin.Context) {
 	c.AbortWithStatusJSON(http.StatusUnauthorized, apierr.WithContext(c, commonPb.Error_UNAUTHORIZED))
 }
 
+func authorization(c *gin.Context) string {
+	if v := c.GetHeader("Authorization"); v != "" {
+		return v
+	}
+	if v := c.GetHeader("authorization"); v != "" {
+		return v
+	}
+
+	return ""
+}
+
 // RequireAdmin ...
 func RequireAdmin() gin.HandlerFunc {
 	return func(c *gin.Context) {
@@ -52,8 +68,8 @@ func RequireAdmin() gin.HandlerFunc {
 			unauthorized(c)
 			return
 		}
-
 		c.Set(UserName, userName)
+		c.Set(FarmId, GetFarmId(c))
 		c.Set(XRequestId, GetXRequestId(c))
 		c.Next()
 	}

+ 5 - 0
http/middleware/bus.go

@@ -2,6 +2,7 @@ package middleware
 
 import (
 	"kpt-pasture/dep"
+	"kpt-pasture/module/backend"
 
 	"github.com/gin-gonic/gin"
 )
@@ -18,3 +19,7 @@ func WithDependency(s *dep.HttpDependency) gin.HandlerFunc {
 func Dependency(c *gin.Context) *dep.HttpDependency {
 	return c.MustGet(KeyDep).(*dep.HttpDependency)
 }
+
+func BackendOperation(c *gin.Context) *backend.Hub {
+	return &(Dependency(c).StoreEventHub)
+}

+ 1 - 34
http/middleware/cors.go

@@ -1,14 +1,7 @@
 package middleware
 
 import (
-	"io/ioutil"
 	"net/http"
-	"path/filepath"
-	"runtime"
-
-	"gitee.com/xuyiping_admin/pkg/logger/zaplog"
-
-	"go.uber.org/zap"
 
 	"github.com/gin-contrib/cors"
 	"github.com/gin-gonic/gin"
@@ -28,7 +21,7 @@ func CORS(configs ...cors.Config) gin.HandlerFunc {
 			//服务器支持的所有跨域请求的方法
 			c.Header("Access-Control-Allow-Methods", "POST, GET, OPTIONS, PUT, DELETE,UPDATE")
 			//允许跨域设置可以返回其他子段,可以自定义字段
-			c.Header("Access-Control-Allow-Headers", "Authorization, Content-Length, X-CSRF-Token, Token,session")
+			c.Header("Access-Control-Allow-Headers", "Authorization, Content-Length")
 			// 允许浏览器(客户端)可以解析的头部 (重要)
 			c.Header("Access-Control-Expose-Headers", "Content-Length, Access-Control-Allow-Origin, Access-Control-Allow-Headers")
 			//设置缓存时间
@@ -42,32 +35,6 @@ func CORS(configs ...cors.Config) gin.HandlerFunc {
 			c.JSON(http.StatusOK, "ok!")
 			return
 		}
-
-		defer func() {
-			if err := recover(); err != nil {
-				body, _ := ioutil.ReadAll(c.Request.Body)
-
-				// 获取 panic 发生的位置
-				pc, file, line, ok := runtime.Caller(1)
-				funcName := ""
-				if ok {
-					funcName := runtime.FuncForPC(pc).Name()
-					// 去除包路径,只保留函数名
-					funcName = filepath.Base(funcName)
-					file = filepath.Base(file)
-				}
-				zaplog.Error("cors",
-					zap.Any("recover", err),
-					zap.Any("url", c.Request.URL),
-					zap.Any("method", method),
-					zap.Any("file", file),
-					zap.Any("line", line),
-					zap.Any("func", funcName),
-					zap.Any("request", string(body)),
-				)
-			}
-		}()
-
 		c.Next()
 	}
 }

+ 0 - 11
http/middleware/hub.go

@@ -1,11 +0,0 @@
-package middleware
-
-import (
-	"kpt-pasture/module/backend"
-
-	"github.com/gin-gonic/gin"
-)
-
-func BackendOperation(c *gin.Context) *backend.Hub {
-	return &(Dependency(c).StoreEventHub)
-}

+ 16 - 48
http/middleware/log.go

@@ -3,12 +3,8 @@ package middleware
 import (
 	"bytes"
 	"io/ioutil"
-	"net"
 	"net/http"
-	"net/http/httputil"
-	"os"
 	"runtime/debug"
-	"strings"
 	"time"
 
 	"gitee.com/xuyiping_admin/pkg/logger/zaplog"
@@ -21,15 +17,6 @@ type responseBodyWriter struct {
 	body *bytes.Buffer
 }
 
-type stackErr struct {
-	Err   error
-	Stack string
-}
-
-func (s *stackErr) Error() string {
-	return s.Err.Error()
-}
-
 func (r responseBodyWriter) Write(b []byte) (int, error) {
 	r.body.Write(b)
 	return r.ResponseWriter.Write(b)
@@ -47,7 +34,7 @@ func GinLogger() gin.HandlerFunc {
 			requestBody, _ = ioutil.ReadAll(c.Request.Body)
 			c.Request.Body = ioutil.NopCloser(bytes.NewBuffer(requestBody))
 		}
-		start := time.Now()
+		start := time.Now().Local()
 		c.Next()
 		cost := time.Since(start)
 		logFields := []zap.Field{
@@ -56,10 +43,10 @@ func GinLogger() gin.HandlerFunc {
 			zap.String("query", c.Request.URL.RawQuery),
 			zap.String("ip", c.ClientIP()),
 			zap.String("user-agent", c.Request.UserAgent()),
-			//zap.String("errors", c.Errors.ByType(gin.ErrorTypePrivate).String()),
 			zap.String("time", cost.String()),
 			zap.String("Request body", string(requestBody)),
 			zap.String("Response body", w.body.String()),
+			zap.String("x-request-id", c.GetHeader("X-Request-ID")),
 		}
 		if len(c.Errors) > 0 {
 			logFields = append(logFields, zap.Any("stack", string(debug.Stack())))
@@ -76,42 +63,23 @@ func GinRecovery(stack bool) gin.HandlerFunc {
 	return func(c *gin.Context) {
 		defer func() {
 			if err := recover(); err != nil {
-				// Check for a broken connection, as it is not really a
-				// condition that warrants a panic stack trace.
-				var brokenPipe bool
-				if ne, ok := err.(*net.OpError); ok {
-					if se, ok := ne.Err.(*os.SyscallError); ok {
-						if strings.Contains(strings.ToLower(se.Error()), "broken pipe") || strings.Contains(strings.ToLower(se.Error()), "connection reset by peer") {
-							brokenPipe = true
-						}
-					}
-				}
-
-				httpRequest, _ := httputil.DumpRequest(c.Request, false)
-				if brokenPipe {
-					zaplog.Error(c.Request.URL.Path,
-						zap.Any("error", err),
-						zap.String("request", string(httpRequest)),
-					)
-					// If the connection is dead, we can't write a status to it.
-					c.Error(err.(error)) // nolint: errcheck
-					c.Abort()
-					return
-				}
-
+				body, _ := ioutil.ReadAll(c.Request.Body)
+				// 获取 panic 发生的位置
+				var stackTrace []byte
 				if stack {
-					zaplog.Error("[Recovery from panic]",
-						zap.Any("error", err),
-						zap.String("request", string(httpRequest)),
-						zap.String("stack", string(debug.Stack())),
-					)
-				} else {
-					zaplog.Error("[Recovery from panic]",
-						zap.Any("error", err),
-						zap.String("request", string(httpRequest)),
-					)
+					// 获取调用栈
+					stackTrace = debug.Stack()
 				}
+				zaplog.Error("panic",
+					zap.Any("recover", err),
+					zap.Any("url", c.Request.URL),
+					zap.Any("stackTrace", string(stackTrace)),
+					zap.Any("request", string(body)),
+					zap.String("x-request-id", c.GetHeader("X-Request-ID")),
+				)
 				c.AbortWithStatus(http.StatusInternalServerError)
+				c.Abort()
+				return
 			}
 		}()
 		c.Next()

+ 58 - 0
http/middleware/rate_limit.go

@@ -0,0 +1,58 @@
+package middleware
+
+import (
+	"fmt"
+	"time"
+
+	"gitee.com/xuyiping_admin/pkg/apierr"
+	"gitee.com/xuyiping_admin/pkg/xerr"
+	"github.com/gin-gonic/gin"
+	limit "github.com/yangxikun/gin-limit-by-key"
+	"golang.org/x/time/rate"
+)
+
+// RateLimit limit rate by client ip
+// limit 10 qps/client_ip and permit bursts of at most 10 tokens, and the limiter live time duration is 3 minutes
+// handle exceed rate limit request
+func RateLimit(opts ...*RateLimitOption) gin.HandlerFunc {
+	var opt *RateLimitOption
+	if len(opts) == 0 {
+		opt = defaultRateLimitOption
+	} else {
+		opt = opts[0]
+	}
+
+	return limit.NewRateLimiter(func(c *gin.Context) string {
+		return fmt.Sprintf("%s-%s", c.ClientIP(), authorization(c))
+	}, func(c *gin.Context) (*rate.Limiter, time.Duration) {
+		return rate.NewLimiter(rate.Every(opt.Interval), opt.Tokens), opt.LiveTime
+	}, func(c *gin.Context) {
+		apierr.AbortBadRequest(c, 429, xerr.Customf("request too many %s", c.ClientIP()))
+	})
+}
+
+type RateLimitOption struct {
+	Interval time.Duration
+	Tokens   int
+	LiveTime time.Duration
+}
+
+var defaultRateLimitOption = &RateLimitOption{
+	Interval: 100 * time.Millisecond,
+	Tokens:   10,
+	LiveTime: 3 * time.Minute,
+}
+
+var testRateLimitOption = &RateLimitOption{
+	Interval: 10 * time.Millisecond,
+	Tokens:   1000,
+	LiveTime: 1 * time.Minute,
+}
+
+func RateLimitOptionLow() *RateLimitOption {
+	return &RateLimitOption{
+		Interval: 1 * time.Second,
+		Tokens:   10,
+		LiveTime: 3 * time.Minute,
+	}
+}

+ 22 - 3
http/route/analysis_api.go

@@ -11,8 +11,27 @@ func AnalysisAPI(opts ...func(engine *gin.Engine)) func(s *gin.Engine) {
 		for _, opt := range opts {
 			opt(s)
 		}
-		// pasture API 组  牧场管理
-		pastureRoute := authRouteGroup(s, "/api/v1/analysis/")
-		pastureRoute.POST("/growth/curve", analysis.GrowthCurve)
+		// analysis API 组
+		analysisRoute := authRouteGroup(s, "/api/v1/analysis/")
+		analysisRoute.POST("/weight/scatter/plot", analysis.WeightScatterPlot) // 体重散点图
+		analysisRoute.POST("/weight/range", analysis.WeightRange)              // 体重区间图
+
+		analysisRoute.POST("/mating/timely", analysis.MatingTimeLy) // 配种及时性
+		analysisRoute.POST("/pen/weight", analysis.PenWeight)       // 栏舍体重
+		analysisRoute.POST("/abortion/rate", analysis.AbortionRate) // 流产率
+
+		analysisRoute.POST("/twenty/one/pregnant/rate", analysis.TwentyOnePregnantRate)     // 21天怀孕率
+		analysisRoute.POST("/twenty/one/pregnant/detail", analysis.TwentyOnePregnantDetail) // 21天怀孕率详情
+		analysisRoute.POST("/pregnancy/report", analysis.PregnancyReport)                   // 孕检报告
+		analysisRoute.POST("/calving/report", analysis.CalvingReport)                       // 产犊报告
+
+		analysisRoute.POST("/disease/cure/report", analysis.DiseaseCureReport)                        // 疾病治疗报告
+		analysisRoute.POST("/sale/cow/report", analysis.SaleCowReport)                                // 销售牛牛报告
+		analysisRoute.POST("/single/factor/pregnant/report", analysis.SingleFactorInfantSurvivalRate) // 单因素受胎率
+
+		analysisRoute.POST("/multi/factor/pregnant/report", analysis.MultiFactorInfantSurvivalRate) // 多因素受胎率
+		analysisRoute.POST("/pen/behavior", analysis.PenBehaviorAnalysis)                           // 栏舍行为数据
+		analysisRoute.POST("/pen/behavior/monitor", analysis.PenBehaviorDaily)                      // 栏舍饲喂监测
+		analysisRoute.POST("/cow/behavior/distribution", analysis.CowBehaviorDistribution)          // 牛只行为分布
 	}
 }

+ 18 - 14
http/route/config_api.go

@@ -11,19 +11,23 @@ func ConfigAPI(opts ...func(engine *gin.Engine)) func(s *gin.Engine) {
 		for _, opt := range opts {
 			opt(s)
 		}
-		// pasture API 组  牧场管理
-		pastureRoute := authRouteGroup(s, "/api/v1/config/")
-		pastureRoute.GET("/barn/type/options", config.BarnTypeOptions)
-		pastureRoute.GET("/barn/list/options", config.BarnListOptions)
-		pastureRoute.GET("/breed/status/options", config.BreedStatusOptions)
-		pastureRoute.GET("/cow/kind/options", config.CowKindOptions)
-		pastureRoute.GET("/cow/source/options", config.CowSourceOptions)
-		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)
-		pastureRoute.GET("/disease/type/options", config.DiseaseTypeOptions)
-		pastureRoute.GET("/disease/options", config.DiseaseOptions)
+		// config API 组
+		configRoute := authRouteGroup(s, "/api/v1/config/")
+		configRoute.GET("/barn/type/options", config.BarnTypeOptions)
+		configRoute.GET("/barn/list/options", config.BarnListOptions)
+		configRoute.GET("/breed/status/options", config.BreedStatusOptions)
+		configRoute.GET("/cow/kind/options", config.CowKindOptions)
+		configRoute.GET("/cow/source/options", config.CowSourceOptions)
+		configRoute.GET("/cow/type/options", config.CowTypeOptions)
+		configRoute.GET("/cow/transfer/pen/reason/options", config.CowTransferPenReasonOptions)
+		configRoute.GET("/bull/frozen/semen/options", config.BullListOptions)
+		configRoute.GET("/system/user/options", config.SystemUserOptions)
+		configRoute.GET("/system/base/config/options", config.SystemBaseConfigOptions)
+		configRoute.GET("/disease/type/options", config.DiseaseTypeOptions)
+		configRoute.GET("/disease/options", config.DiseaseOptions)
+		configRoute.GET("/prescription/options", config.PrescriptionOptions)
+
+		configRoute.GET("/cow/batch/number", config.FindCowHistoryBatchNumber)
+		configRoute.GET("/generate/batch/number", config.GenerateBatchNumber)
 	}
 }

+ 12 - 2
http/route/cow_api.go

@@ -12,7 +12,17 @@ func CowAPI(opts ...func(engine *gin.Engine)) func(s *gin.Engine) {
 			opt(s)
 		}
 		// CowAPI API 组  牛只信息
-		eventRoute := authRouteGroup(s, "/api/v1/cow/")
-		eventRoute.POST("/list", cow.List)
+		cowRoute := authRouteGroup(s, "/api/v1/cow/")
+		cowRoute.POST("/detail", cow.Detail)
+		cowRoute.POST("/list", cow.List)
+		cowRoute.POST("/event/list", cow.EventList)
+		cowRoute.POST("/behavior/curve", cow.BehaviorCurve)
+		cowRoute.POST("/growth/curve", cow.GrowthCurve)
+		cowRoute.POST("/lact/curve", cow.LactCurve)
+		cowRoute.POST("/behavior/rate", cow.BehaviorRate)
+
+		searchRoute := authRouteGroup(s, "/api/v1/search/")
+		searchRoute.POST("/indicators/comparison", cow.IndicatorsComparison)
+		searchRoute.POST("/cow/long/term/infertility", cow.LongTermInfertility)
 	}
 }

+ 0 - 17
http/route/dashboard.go

@@ -1,17 +0,0 @@
-package route
-
-import (
-	"github.com/gin-gonic/gin"
-	"kpt-pasture/http/handler/dashboard"
-)
-
-func DashboardApi(opts ...func(engine *gin.Engine)) func(s *gin.Engine) {
-	return func(s *gin.Engine) {
-		for _, opt := range opts {
-			opt(s)
-		}
-		// CowAPI API 组  牛只信息
-		eventRoute := authRouteGroup(s, "/api/v1/dashboard/")
-		eventRoute.GET("/bar", dashboard.Bar)
-	}
-}

+ 25 - 0
http/route/dashboard_api.go

@@ -0,0 +1,25 @@
+package route
+
+import (
+	"kpt-pasture/http/handler/dashboard"
+
+	"github.com/gin-gonic/gin"
+)
+
+func DashboardApi(opts ...func(engine *gin.Engine)) func(s *gin.Engine) {
+	return func(s *gin.Engine) {
+		for _, opt := range opts {
+			opt(s)
+		}
+		// dashboard API 组
+		dashboardRoute := authRouteGroup(s, "/api/v1/dashboard/")
+		dashboardRoute.GET("/neck_ring/warning", dashboard.NeckRingWarning)
+		dashboardRoute.GET("/focus/indicators/:dimension", dashboard.FocusIndicators)
+		dashboardRoute.POST("/focus/indicators/set", dashboard.FocusIndicatorsSet)
+		dashboardRoute.GET("/data/warning/list", dashboard.DataWarningList)
+		dashboardRoute.POST("/data/warning/pop", dashboard.DataWarningPop)
+		dashboardRoute.POST("/data/warning/set", dashboard.DataWarningSet)
+		dashboardRoute.GET("/todo/count", dashboard.TodoCount)
+		dashboardRoute.GET("/equipment/list", dashboard.Equipment)
+	}
+}

+ 41 - 10
http/route/event_api.go

@@ -23,27 +23,58 @@ func EventAPI(opts ...func(engine *gin.Engine)) func(s *gin.Engine) {
 		eventRoute.POST("/body/score/list", event.BodyScoreEventList)
 		eventRoute.POST("/body/score/create", event.BodyScoreEventCreate)
 		// 称重
-		eventRoute.POST("/weight/list", event.WeightEventList)
-		eventRoute.POST("/weight/create", event.WeightEventCreate)
-
+		eventRoute.POST("/weight/list", event.WeightList)
+		eventRoute.POST("/weight/batch", event.WeightBatch)
 		// 产犊
 		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("/pregnant/check/batch", event.PregnantCheckEventCreateBatch)
 		// 配种
 		eventRoute.POST("/mating/list", event.MatingEventList)
-		eventRoute.POST("/mating/create", event.MatingCreate)
+		eventRoute.POST("/mating/batch", event.MatingBatch)
 		// 发情
 		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)
+		eventRoute.POST("/estrus/mating/batch", event.EstrusBatchMating)
+
 		// 同期
 		eventRoute.POST("/same/time/list", event.SameTimeList)
 		eventRoute.POST("/same/time/create", event.SameTimeCreate)
-
+		eventRoute.POST("/same/time/batch", event.SameTimeBatch)
+		// 流产
+		eventRoute.POST("/abortion/list", event.AbortionList)
+		eventRoute.POST("/abortion/batch", event.AbortionCreateBatch)
+		// 断奶
+		eventRoute.POST("/weaning/batch", event.WeaningCreateBatch)
+		// 发病
+		eventRoute.POST("/disease/create", event.CowDiseaseCreate)
+		// 疾病诊断
+		eventRoute.POST("/disease/diagnose", event.CowDiseaseDiagnose)
+		// 疾病治疗
+		eventRoute.POST("/disease/treatment", event.CowDiseaseTreatment)
+		eventRoute.GET("/disease/suggest/prescription/:id", event.DiseaseSuggestPrescription)
+		// 治疗详情
+		eventRoute.POST("/disease/treatment/details", event.CowDiseaseTreatmentDetail)
+		// 批量治愈
+		eventRoute.POST("/disease/curable/batch", event.CowDiseaseCurable)
+		// 死亡
+		eventRoute.POST("/death/batch", event.DeathBatch)
+		eventRoute.POST("/death/list", event.DeathList)
+		// 更换耳标号
+		eventRoute.PUT("/cow/ear/number", event.CowEarNumber)
+		// 牛只销售
+		eventRoute.POST("/sale/create", event.CowSale)
+		eventRoute.POST("/sale/list", event.CowSaleList)
+		// 免疫
+		eventRoute.POST("/immunization/batch", event.ImmunizationBatch)
+		eventRoute.POST("/immunization/list", event.ImmunizationList)
+		// 干奶
+		eventRoute.POST("/dry/milk/batch", event.DryMilkBatch)
+		eventRoute.POST("/dry/milk/list", event.DryMilkList)
+		// 禁配
+		eventRoute.POST("/forbidden/mating/batch", event.ForbiddenMatingBatch)
+		eventRoute.POST("/forbidden/mating/list", event.ForbiddenMatingList)
+		eventRoute.POST("/un_forbidden/mating/batch", event.UnForbiddenMating)
 	}
 }

+ 4 - 0
http/route/files_api.go

@@ -15,5 +15,9 @@ func FilesManageAPI(opts ...func(engine *gin.Engine)) func(s *gin.Engine) {
 		uploadRoute := authRouteGroup(s, "/api/v1/upload/")
 		uploadRoute.POST("/photos", upload.Photos)
 		uploadRoute.POST("/files", upload.Files)
+
+		// 获取文件列表
+		//ossRoute := authRouteGroup(s, "/api/v1/oss/")
+		s.Static("api/v1/oss/video", "./files/video/")
 	}
 }

+ 16 - 0
http/route/goods_api.go

@@ -13,9 +13,25 @@ func GoodsManageAPI(opts ...func(engine *gin.Engine)) func(s *gin.Engine) {
 		}
 		// goods API 组  物品管理
 		goodsRoute := authRouteGroup(s, "/api/v1/goods/")
+		// 药品相关
 		goodsRoute.POST("/drugs/list", goods.DrugsList)
 		goodsRoute.POST("/drugs/createOrUpdate", goods.DrugsCreateOrUpdate)
+		// 医疗器械相关
 		goodsRoute.POST("/medical/equipment/list", goods.MedicalEquipmentList)
 		goodsRoute.POST("/medical/equipment/createOrUpdate", goods.MedicalEquipmentCreateOrUpdate)
+		// 脖环相关
+		goodsRoute.POST("/neck/ring/list", goods.NeckRingList)
+		goodsRoute.POST("/neck/ring/createOrUpdate", goods.NeckRingCreateOrUpdate)
+		// 出库相关
+		goodsRoute.POST("/outbound/apply", goods.OutboundApply)
+		goodsRoute.POST("/outbound/list", goods.OutboundList)
+		goodsRoute.POST("/outbound/audit", goods.OutboundAudit)
+		goodsRoute.POST("/outbound/cancel", goods.OutboundAudit)
+		goodsRoute.GET("/outbound/detail/:id", goods.OutboundDetail)
+		goodsRoute.DELETE("/outbound/:id", goods.OutboundDelete)
+
+		// 冻精
+		goodsRoute.POST("/frozen/seme/list", goods.FrozenSemeList)
+		goodsRoute.POST("/frozen/seme/create", goods.FrozenSemeCreate)
 	}
 }

+ 18 - 0
http/route/milk_api.go

@@ -0,0 +1,18 @@
+package route
+
+import (
+	"kpt-pasture/http/handler/milk"
+
+	"github.com/gin-gonic/gin"
+)
+
+func MilkManageAPI(opts ...func(engine *gin.Engine)) func(s *gin.Engine) {
+	return func(s *gin.Engine) {
+		for _, opt := range opts {
+			opt(s)
+		}
+		// milk API 组  奶厅数据管理
+		pastureRoute := authRouteGroup(s, "/api/v1/milk/")
+		pastureRoute.POST("/hall/original", milk.ProcessOriginal)
+	}
+}

+ 12 - 2
http/route/pasture_api.go

@@ -39,15 +39,25 @@ func PastureManageAPI(opts ...func(engine *gin.Engine)) func(s *gin.Engine) {
 		// 处方相关
 		pastureRoute.POST("/prescription/list", pasture.SearchPrescriptionList)
 		pastureRoute.POST("/prescription/createOrUpdate", pasture.CreatedOrUpdatePrescription)
+		pastureRoute.GET("/prescription/detail/:id", pasture.PrescriptionDetail)
 
 		// 免疫计划相关
 		pastureRoute.POST("/immunization/plan/list", pasture.SearchImmunizationList)
 		pastureRoute.POST("/immunization/plan/createOrUpdate", pasture.CreatedOrUpdateImmunization)
-		pastureRoute.PUT("/immunization/is_show/:id", pasture.ImmunizationIsShow)
+		pastureRoute.PUT("/immunization/isShow/:id", pasture.ImmunizationIsShow)
 
 		// 同期相关
 		pastureRoute.POST("/same/time/createOrUpdate", pasture.SameTimeCreatedOrUpdate)
 		pastureRoute.POST("/same/time/list", pasture.SameTimeList)
-		pastureRoute.PUT("/same/time/is_show/:id", pasture.SameTimeIsShow)
+		pastureRoute.PUT("/same/time/isShow/:id", pasture.SameTimeIsShow)
+
+		// 经销商相关
+		pastureRoute.POST("/dealer/createOrUpdate", pasture.SaleDealerCreateOrUpdate)
+		pastureRoute.POST("/dealer/list", pasture.SaleDealerList)
+		pastureRoute.PUT("/dealer/isShow/:id", pasture.SaleDealerIsShow)
+
+		// 基础参数
+		pastureRoute.POST("/system/basic/list", pasture.SystemBasicList)
+		pastureRoute.POST("/system/basic/edit", pasture.SystemBasicEdit)
 	}
 }

+ 1 - 3
http/route/root.go

@@ -13,14 +13,12 @@ func Root(opts ...func(engine *gin.Engine)) func(s *gin.Engine) {
 		for _, opt := range opts {
 			opt(s)
 		}
-
-		// common middleware
+		s.Use(middleware.GinRecovery(true))
 		s.Use(
 			middleware.CORS(),
 			middleware.Pagination(),
 			requestid.New(),
 			gzip.Gzip(gzip.DefaultCompression),
-			middleware.GinRecovery(true),
 		)
 	}
 }

+ 3 - 0
http/route/route.go

@@ -16,6 +16,9 @@ func HTTPServerRoute(opts ...func(engine *gin.Engine)) func(s *gin.Engine) {
 		DashboardApi(opts...),
 		WorkOrderAPI(opts...),
 		FilesManageAPI(opts...),
+		MilkManageAPI(opts...),
+		WarningAPI(opts...),
+		TestAPI(opts...),
 	}
 
 	return func(s *gin.Engine) {

+ 11 - 9
http/route/system_api.go

@@ -23,7 +23,7 @@ func SystemAPI(opts ...func(engine *gin.Engine)) func(s *gin.Engine) {
 		// system API 组  系统用户
 		systemRoute := authRouteGroup(s, "/api/v1/system/")
 		systemRoute.POST("/user/list", system.SearchSystemUserList)
-		systemRoute.PUT("/user/is_show/:id", system.IsShowSystemUser)
+		systemRoute.PUT("/user/isShow/:id", system.IsShowSystemUser)
 		systemRoute.DELETE("/user/:id", system.DeleteUser)
 		systemRoute.POST("/user/createOrUpdate", system.UserCreateOrUpdate)
 		systemRoute.POST("/user/rest/password", system.ResetPasswordSystemUser)
@@ -33,32 +33,34 @@ func SystemAPI(opts ...func(engine *gin.Engine)) func(s *gin.Engine) {
 		// 系统角色
 		systemRoute.POST("/role/list", system.SearchSystemRoleList)
 		systemRoute.DELETE("/role/:id", system.DeleteSystemRole)
-		systemRoute.PUT("/role/is_show/:id", system.IsShowSystemRole)
+		systemRoute.PUT("/role/isShow/:id", system.IsShowSystemRole)
 		systemRoute.POST("/role/createOrUpdate", system.RoleCreateOrUpdate)
 		systemRoute.GET("/role/menu/:id", system.GetRoleMenu)
 		systemRoute.POST("/role/menu/save", system.RoleMenuSave)
-		systemRoute.GET("/role/list", system.GetRoleList)
 
 		// 系统菜单权限
 		systemRoute.POST("/menu/list", system.SearchSystemMenuList)
-		systemRoute.DELETE("/menu/:id", system.DeleteSystemMenu)
-		systemRoute.POST("/menu/createOrUpdate", system.CreatedOrUpdateSystemMenu)
-		systemRoute.POST("/menu/tree", system.GetSystemMenuTree)
+		systemRoute.GET("/menu/tree", system.GetSystemMenuTree)
+		//systemRoute.DELETE("/menu/:id", system.DeleteSystemMenu)
 
 		// 系统部门
 		systemRoute.POST("/dept/list", system.DeptList)
 		systemRoute.DELETE("/dept/:id", system.DepIsShow)
 		systemRoute.POST("/dept/createOrUpdate", system.DepCreateOrUpdate)
+		systemRoute.POST("/dept/tree", system.DeptTree)
 
 		// 登录用户的相关接口
-		systemRoute.GET("/user/menu", handler.GetUserMenu)
-		systemRoute.POST("/user/logout", handler.Logout)
+		systemRoute.GET("/user/menu", system.GetUserMenu)
+		systemRoute.POST("/user/logout", system.Logout)
+
+		// 牧场
+		systemRoute.POST("/pasture/list", system.PastureList)
 	}
 }
 
 func authRouteGroup(s *gin.Engine, relativePath string) *gin.RouterGroup {
 	group := s.Group(relativePath)
 	// 中间件鉴权
-	group.Use(middleware.RequireAdmin(), middleware.CORS(), middleware.GinLogger())
+	group.Use(middleware.RequireAdmin(), middleware.GinLogger(), middleware.Pagination())
 	return group
 }

+ 26 - 0
http/route/test_api.go

@@ -0,0 +1,26 @@
+package route
+
+import (
+	"kpt-pasture/http/handler"
+
+	"github.com/gin-gonic/gin"
+)
+
+func TestAPI(opts ...func(engine *gin.Engine)) func(s *gin.Engine) {
+	return func(s *gin.Engine) {
+		for _, opt := range opts {
+			opt(s)
+		}
+		// test API 组  测试接口
+		testRoute := authRouteGroup(s, "/api/v1/test/")
+		testRoute.POST("/cow/neck_ring/bound", handler.CowNeckRingBound)
+		testRoute.POST("/cow/neck_ring/bound2", handler.CowNeckRingBound2)
+		testRoute.POST("/cow/pen/update", handler.UpdateCowPen)
+		testRoute.GET("/data/warning", handler.DataWarning)
+		testRoute.GET("/neck_ring_original/sync", handler.NeckRingOriginalAsync)
+		testRoute.GET("/pasture/init", handler.PastureInit)
+		testRoute.GET("/calving/age", handler.CalvingAge)
+		testRoute.GET("/admission/age", handler.AdmissionAge)
+		testRoute.GET("/system/menu/init", handler.SystemMenuInit)
+	}
+}

+ 25 - 0
http/route/warning_api.go

@@ -0,0 +1,25 @@
+package route
+
+import (
+	"kpt-pasture/http/handler/warning"
+
+	"github.com/gin-gonic/gin"
+)
+
+func WarningAPI(opts ...func(engine *gin.Engine)) func(s *gin.Engine) {
+	return func(s *gin.Engine) {
+		for _, opt := range opts {
+			opt(s)
+		}
+		// warning API 组  工作清单
+		waringRoute := authRouteGroup(s, "/api/v1/warning/")
+
+		// 发情和流产预警
+		waringRoute.POST("/estrus/items", warning.NeckRingWarningEstrusItem)
+		waringRoute.POST("/no/estrus/batch", warning.NeckRingNoEstrusBatch)
+		waringRoute.POST("/health/items", warning.NeckRingWarningHealthItem)
+		waringRoute.POST("/no/disease/batch", warning.NeckRingNoHealthBatch)
+		waringRoute.GET("/data/notice", warning.DataNoticeDetail)
+		waringRoute.PUT("/data/notice/known/:id", warning.DataNoticeKnown)
+	}
+}

+ 39 - 0
http/route/work_api.go

@@ -0,0 +1,39 @@
+package route
+
+import (
+	"kpt-pasture/http/handler/event"
+	"kpt-pasture/http/handler/work"
+
+	"github.com/gin-gonic/gin"
+)
+
+func WorkOrderAPI(opts ...func(engine *gin.Engine)) func(s *gin.Engine) {
+	return func(s *gin.Engine) {
+		for _, opt := range opts {
+			opt(s)
+		}
+		// work API 组  工作清单
+		workRoute := authRouteGroup(s, "/api/v1/work/")
+		/*workRoute.POST("/order/list", work.OrderList)
+		workRoute.POST("/order/createOrUpdate", work.OrderCreateOrUpdate)
+		workRoute.PUT("/order/is_show/:id", work.OrderIsShow)
+		workRoute.GET("/user/order/list/:status", work.UserOrderList)*/
+
+		// 日历
+		workRoute.POST("/calendar/list", work.CalendarList)
+		workRoute.POST("/calendar/todo/list", work.CalendarToDoList)
+		workRoute.POST("/calendar/detail", work.CalendarTableDetail)
+
+		// 清单相关接口
+		workRoute.POST("/same/time/items", work.SameTimeCowList)
+		workRoute.POST("/immunization/items", work.ImmunizationItems)
+		workRoute.POST("/pregnancy/check/items", work.PregnancyCheckCowList)
+		workRoute.POST("/weaning/items", work.WeaningCowList)
+		workRoute.POST("/mating/items", work.MatingCowList)
+		workRoute.POST("/calving/items", work.CalvingList)
+		workRoute.POST("/disease/items", event.CowDiseaseList)
+
+		// 任务明细
+		workRoute.POST("/task/detail", work.TaskDetailList)
+	}
+}

+ 0 - 21
http/route/work_order.go

@@ -1,21 +0,0 @@
-package route
-
-import (
-	"kpt-pasture/http/handler/work"
-
-	"github.com/gin-gonic/gin"
-)
-
-func WorkOrderAPI(opts ...func(engine *gin.Engine)) func(s *gin.Engine) {
-	return func(s *gin.Engine) {
-		for _, opt := range opts {
-			opt(s)
-		}
-		// goods API 组  物品管理
-		workRoute := authRouteGroup(s, "/api/v1/work/")
-		workRoute.POST("/order/list", work.OrderList)
-		workRoute.POST("/order/createOrUpdate", work.OrderCreateOrUpdate)
-		workRoute.PUT("/order/is_show/:id", work.OrderIsShow)
-		workRoute.GET("/user/order/list/:status", work.UserOrderList)
-	}
-}

+ 2 - 6
main.go

@@ -1,16 +1,12 @@
-/*
-Copyright © 2022 NAME HERE <EMAIL ADDRESS>
-
-*/
 package main
 
 import (
-	"gitee.com/xuyiping_admin/pkg/logger/logrus"
 	"kpt-pasture/cmd"
+
+	"gitee.com/xuyiping_admin/pkg/logger/logrus"
 )
 
 func main() {
-	logrus.Info("kpt-pasture: is starting")
 	cmd.Execute()
 	logrus.Error("kpt-pasture: is shut down")
 }

+ 47 - 0
model/app_pasture_list.go

@@ -0,0 +1,47 @@
+package model
+
+import pasturePb "gitee.com/xuyiping_admin/go_proto/proto/go/backend/cow"
+
+type AppPastureList struct {
+	Id                   int64                          `json:"id"`
+	FarmId               string                         `json:"farmId"`
+	Name                 string                         `json:"name"`
+	ShortName            string                         `json:"shortName"`
+	GroupId              int64                          `json:"groupId"`
+	Province             string                         `json:"province"`
+	City                 string                         `json:"city"`
+	County               string                         `json:"county"`
+	Address              string                         `json:"address"`
+	LegalPersonName      string                         `json:"legalPersonName"`
+	LegalPersonPhone     string                         `json:"legalPersonPhone"`
+	FactoryDirectorName  string                         `json:"factoryDirectorName"`
+	FactoryDirectorPhone string                         `json:"factoryDirectorPhone"`
+	Category             pasturePb.PastureCategory_Kind `json:"category"`
+	CurrentScale         string                         `json:"currentScale"`
+	PlanScale            string                         `json:"planScale"`
+	AppId                string                         `json:"appId"`
+	Status               int32                          `json:"status"`
+	IsShow               pasturePb.IsShow_Kind          `json:"isShow"`
+	ProductionModel      int32                          `json:"productionModel"`
+	Remarks              string                         `json:"remarks"`
+	CreatedName          string                         `json:"createdName"`
+	CreatedAt            int64                          `json:"createdAt"`
+	UpdatedAt            string                         `json:"updatedAt"`
+}
+
+func (a *AppPastureList) TableName() string {
+	return "app_pasture_list"
+}
+
+type AppPastureListSlice []*AppPastureList
+
+func (a AppPastureListSlice) ToPB() []*pasturePb.UserPasture {
+	res := make([]*pasturePb.UserPasture, len(a))
+	for i, d := range a {
+		res[i] = &pasturePb.UserPasture{
+			FarmId: d.FarmId,
+			Name:   d.Name,
+		}
+	}
+	return res
+}

+ 13 - 0
model/app_pasture_receiver.go

@@ -0,0 +1,13 @@
+package model
+
+type AppPastureReceiver struct {
+	Id             int64  `json:"id"`
+	PastureId      int64  `json:"pastureId"`
+	ReceiverNumber string `json:"receiverNumber"`
+	CreatedAt      int64  `json:"createdAt"`
+	UpdatedAt      int64  `json:"updatedAt"`
+}
+
+func (a *AppPastureReceiver) TableName() string {
+	return "app_pasture_receiver"
+}

+ 91 - 0
model/calendar.go

@@ -0,0 +1,91 @@
+package model
+
+import (
+	"fmt"
+
+	pasturePb "gitee.com/xuyiping_admin/go_proto/proto/go/backend/cow"
+)
+
+type Calendar struct {
+	Id           int64                       `json:"id"`
+	PastureId    int64                       `json:"pastureId"`
+	Name         string                      `json:"name"`
+	CalendarType pasturePb.CalendarType_Kind `json:"calendarType"`
+	Count        int32                       `json:"count"`
+	StartDay     string                      `json:"startDay"`
+	EndDay       string                      `json:"endDay"`
+	IsShow       pasturePb.IsShow_Kind       `json:"isShow"`
+	Backup       string                      `json:"backup"`
+	CreatedAt    int64                       `json:"createdAt"`
+	UpdatedAt    int64                       `json:"updatedAt"`
+}
+
+func (w *Calendar) TableName() string {
+	return "calendar"
+}
+
+func NewCalendar(pastureId int64, name string, calendarType pasturePb.CalendarType_Kind, startDay, endDay string, count int32) *Calendar {
+	return &Calendar{
+		PastureId:    pastureId,
+		Name:         name,
+		Count:        count,
+		CalendarType: calendarType,
+		StartDay:     startDay,
+		EndDay:       endDay,
+		IsShow:       pasturePb.IsShow_Ok,
+	}
+}
+
+var CalendarTypeColorMap = map[pasturePb.CalendarType_Kind]string{
+	pasturePb.CalendarType_Immunisation:    "#00B42A",
+	pasturePb.CalendarType_PG:              "#722ED1",
+	pasturePb.CalendarType_RnGH:            "#722ED1",
+	pasturePb.CalendarType_Pregnancy_Check: "#F5319D",
+	pasturePb.CalendarType_WorkOrder:       "#5d6d7e",
+	pasturePb.CalendarType_Disease:         "#00B42A",
+	pasturePb.CalendarType_Calving:         "#F5319D",
+	pasturePb.CalendarType_Weaning:         "#F5319D",
+	pasturePb.CalendarType_Mating:          "#F5319D",
+	pasturePb.CalendarType_DryMilk:         "#165DFF",
+}
+
+var CalendarTypeEndDaysMap = map[pasturePb.CalendarType_Kind]int{
+	pasturePb.CalendarType_Immunisation:    7,
+	pasturePb.CalendarType_PG:              0,
+	pasturePb.CalendarType_RnGH:            0,
+	pasturePb.CalendarType_Pregnancy_Check: 7,
+	pasturePb.CalendarType_WorkOrder:       -1,
+	pasturePb.CalendarType_Disease:         -1,
+	pasturePb.CalendarType_Calving:         15,
+	pasturePb.CalendarType_Weaning:         7,
+	pasturePb.CalendarType_Mating:          0,
+	pasturePb.CalendarType_DryMilk:         15,
+}
+
+type CalendarSlice []*Calendar
+
+func (c CalendarSlice) ToPB() []*pasturePb.Calendar {
+	res := make([]*pasturePb.Calendar, len(c))
+	for i, v := range c {
+		res[i] = &pasturePb.Calendar{
+			Id:      int32(v.Id),
+			Title:   fmt.Sprintf("%s - %d", v.Name, v.Count),
+			GroupId: v.CalendarType,
+			Count:   v.Count,
+			Start:   v.StartDay,
+			End:     v.EndDay,
+			Color:   CalendarTypeColorMap[v.CalendarType],
+			ExtendedProps: &pasturePb.ExtendedProps{
+				StartDay: v.StartDay,
+				Backup:   v.Backup,
+			},
+		}
+	}
+	return res
+}
+
+type CompletedData struct {
+	Count            int32
+	CalendarTypeKind pasturePb.CalendarType_Kind
+	CalendarTypeName string
+}

+ 42 - 22
model/calving_calf.go

@@ -2,48 +2,68 @@ package model
 
 import pasturePb "gitee.com/xuyiping_admin/go_proto/proto/go/backend/cow"
 
+var CalfDefaultDayAge = int32(60)
+
 type CalvingCalf struct {
 	Id            int64                  `json:"id"`
+	PastureId     int64                  `json:"pastureId"`
 	CalvingId     int64                  `json:"calvingId"`
 	CowId         int64                  `json:"cow_id"`
+	BirthAt       int64                  `json:"birthAt"`
 	MotherId      int64                  `json:"motherId"`
 	EarNumber     string                 `json:"earNumber"`
 	Sex           pasturePb.Genders_Kind `json:"sex"`
 	CowKind       pasturePb.CowKind_Kind `json:"cowKind"`
-	BrithWeight   int64                  `json:"brithWeight"`
+	BirthWeight   int64                  `json:"birthWeight"`
 	IsLive        pasturePb.IsShow_Kind  `json:"isLive"`
 	IsAdoption    pasturePb.IsShow_Kind  `json:"isAdoption"`
 	PenId         int32                  `json:"penId"`
+	PenName       string                 `json:"penName"`
 	WeaningAt     int64                  `json:"weaningAt"`
 	CurrentWeight int64                  `json:"currentWeight"`
+	DeathAt       int64                  `json:"deathAt"`
 	Remarks       string                 `json:"remarks"`
 	CreatedAt     int64                  `json:"createdAt"`
 	UpdatedAt     int64                  `json:"updatedAt"`
 }
 
-func (e *CalvingCalf) TableName() string {
+func (c *CalvingCalf) TableName() string {
 	return "calving_calf"
 }
 
-func NewEventCalvingCalf(motherId, calvingId int64, req *pasturePb.EventCalving) []*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,
-		})
+func (c *CalvingCalf) DeathEventUpdate(deathAt int64) {
+	c.DeathAt = deathAt
+	c.IsLive = pasturePb.IsShow_No
+}
+
+func NewEventCalvingCalf(pastureId, calvingId, calvingAt int64, motherInfo *Cow, penMap map[int32]*Pen, req *pasturePb.CalfItem) *CalvingCalf {
+	isAdoption := req.IsAdoption
+	if req.IsLive == pasturePb.IsShow_No {
+		isAdoption = pasturePb.IsShow_No
+	}
+	penId := int32(0)
+	penName := ""
+	if req.PenId > 0 {
+		pen := penMap[req.PenId]
+		penId = pen.Id
+		penName = pen.Name
+	}
+
+	return &CalvingCalf{
+		PastureId:     pastureId,
+		EarNumber:     req.EarNumber,
+		CalvingId:     calvingId,
+		CowId:         int64(req.CowId),
+		BirthAt:       calvingAt,
+		PenId:         penId,
+		PenName:       penName,
+		BirthWeight:   int64(req.Weight * 1000),
+		CurrentWeight: int64(req.Weight * 1000),
+		Sex:           req.Sex,
+		MotherId:      motherInfo.Id,
+		Remarks:       req.Remarks,
+		IsAdoption:    isAdoption,
+		IsLive:        req.IsLive,
+		CowKind:       motherInfo.CowKind,
 	}
-	return calvingCalfList
 }

+ 15 - 1
model/config_disease_type.go

@@ -34,13 +34,27 @@ func (c ConfigDiseaseTypeSlice) ToPB() []*pasturePb.SearchBaseConfigList {
 	return res
 }
 
-func (c ConfigDiseaseTypeSlice) ToPB2() []*pasturePb.ConfigOptionsList {
+func (c ConfigDiseaseTypeSlice) ToPB2(diseaseList []*Disease) []*pasturePb.ConfigOptionsList {
 	res := make([]*pasturePb.ConfigOptionsList, len(c))
 	for i, d := range c {
+		children := make([]*pasturePb.ConfigOptionsList, 0)
+		if len(diseaseList) > 0 {
+			for _, v := range diseaseList {
+				if int64(v.DiseaseType) != d.Id {
+					continue
+				}
+				children = append(children, &pasturePb.ConfigOptionsList{
+					Value:    int32(v.Id),
+					Label:    v.Name,
+					Disabled: true,
+				})
+			}
+		}
 		res[i] = &pasturePb.ConfigOptionsList{
 			Value:    int32(d.Id),
 			Label:    d.Name,
 			Disabled: true,
+			Children: children,
 		}
 	}
 	return res

+ 771 - 141
model/cow.go

@@ -9,130 +9,733 @@ import (
 )
 
 type Cow struct {
-	Id                  int64                      `json:"id"`
-	Sex                 pasturePb.Genders_Kind     `json:"sex"`
-	NeckRingNumber      string                     `json:"neck_ring_number"`
-	EarNumber           string                     `json:"ear_number"`
-	EarOldNumber        string                     `json:"ear_old_number"`
-	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"`
-	BirthWeight         int64                      `json:"birth_weight"`
-	CurrentWeight       int64                      `json:"current_weight"`
-	SourceId            pasturePb.CowSource_Kind   `json:"source_id"`
-	FatherId            int64                      `json:"father_id"`
-	MotherId            int64                      `json:"mother_id"`
-	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"`
-	LastMatingAt        int64                      `json:"last_mating_at"`
-	LastBullId          int64                      `json:"last_bull_id"`
-	LastPregnantCheckAt int64                      `json:"last_pregnant_check_at"`
-	LastDryMilkAt       int64                      `json:"last_dry_milk_at"`
-	LastSecondWeight    int64                      `json:"last_second_weight"`
-	LastSecondWeightAt  int64                      `json:"last_second_weight_at"`
-	LastWeightAt        int64                      `json:"last_weight_at"`
-	CreatedAt           int64                      `json:"created_at"`
-	UpdatedAt           int64                      `json:"updated_at"`
+	Id                    int64                          `json:"id"`
+	PastureId             int64                          `json:"pastureId"`             // 牧场id
+	Sex                   pasturePb.Genders_Kind         `json:"sex"`                   // 性别
+	NeckRingNumber        string                         `json:"neckRingNumber"`        // 脖环号
+	EarNumber             string                         `json:"earNumber"`             // 耳标号
+	EleEarNumber          string                         `json:"eleEarNumber"`          // 电子耳标号
+	EarOldNumber          string                         `json:"earOldNumber"`          // 旧耳标号
+	PenId                 int32                          `json:"penId"`                 // 栏舍id
+	PenName               string                         `json:"penName"`               // 栏舍名称
+	Lact                  int32                          `json:"lact"`                  // 胎次
+	DayAge                int32                          `json:"dayAge"`                // 日龄
+	CalvingAge            int32                          `json:"calvingAge"`            // 产后天使
+	PregnancyAge          int32                          `json:"pregnancyAge"`          // 怀孕天数 孕检结果有阳性更新,产犊后至0
+	MatingAge             int32                          `json:"matingAge"`             // 配种天数
+	AdmissionAge          int32                          `json:"admissionAge"`          // 入场日龄
+	AbortionAge           int32                          `json:"abortionAge"`           // 流产天数
+	LactationAge          int32                          `json:"lactationAge"`          // 泌乳天数
+	DryMilkAge            int32                          `json:"dryMilkAge"`            // 干奶天数
+	CowType               pasturePb.CowType_Kind         `json:"cowType"`               // 牛只类型
+	MilkKind              pasturePb.CowMilk_Kind         `json:"milkKind"`              // 牛只奶属性
+	BreedStatus           pasturePb.BreedStatus_Kind     `json:"breedStatus"`           // 繁殖状态
+	CowKind               pasturePb.CowKind_Kind         `json:"cowKind"`               // 牛只品种
+	BirthWeight           int64                          `json:"birthWeight"`           // 出生体重
+	CurrentWeight         int64                          `json:"currentWeight"`         // 当前体重
+	CurrentHeight         int64                          `json:"currentHeight"`         // 当前身高
+	AdmissionWeight       int64                          `json:"admissionWeight"`       // 入场体重
+	AdmissionPrice        float32                        `json:"admissionPrice"`        // 入场价格
+	SourceKind            pasturePb.CowSource_Kind       `json:"sourceKind"`            // 来源哪里
+	PurposeKind           pasturePb.Purpose_Kind         `json:"purposeKind"`           // 用途
+	FatherNumber          string                         `json:"fatherNumber"`          // 父号
+	MotherNumber          string                         `json:"motherNumber"`          // 母号
+	AdmissionStatus       pasturePb.AdmissionStatus_Kind `json:"admissionStatus"`       // 在场状态
+	IsPregnant            pasturePb.IsShow_Kind          `json:"isPregnant"`            // 是否怀孕
+	IsForbiddenMating     pasturePb.IsShow_Kind          `json:"isForbiddenMating"`     // 是否禁配 1 是 2 否
+	HealthStatus          pasturePb.HealthStatus_Kind    `json:"healthStatus"`          // 健康状态
+	WeaningAt             int64                          `json:"weaningAt"`             // 断奶时间
+	BirthAt               int64                          `json:"birthAt"`               // 出生时间
+	AdmissionAt           int64                          `json:"admissionAt"`           // 入场时间
+	DepartureAt           int64                          `json:"departureAt"`           // 离场时间
+	DeparturePrice        float32                        `json:"departurePrice"`        // 离场价格
+	DepartureAvgWeight    int32                          `json:"departureAvgWeight"`    // 离场平均体重
+	FirstMatingAt         int64                          `json:"firstMatingAt"`         // 首次配种时间
+	AllMatingTimes        int32                          `json:"allMatingTimes"`        // 总配次
+	AllAbortionTimes      int32                          `json:"allAbortionTimes"`      // 总流产次数
+	MatingTimes           int32                          `json:"matingTimes"`           // 配种次数
+	AbortionTimes         int32                          `json:"abortionTimes"`         // 流产次数
+	PregnancyCheckName    string                         `json:"pregnancyCheckName"`    // 孕检名称
+	WeeklyActive          int32                          `json:"weeklyActive"`          // 每周活跃度
+	LastEstrusAt          int64                          `json:"lastEstrusAt"`          // 最后一次发情时间
+	LastCalvingAt         int64                          `json:"lastCalvingAt"`         // 最后一次产犊时间
+	LastMatingAt          int64                          `json:"lastMatingAt"`          // 最后一次配种时间
+	LastBullNumber        string                         `json:"lastBullNumber"`        // 最后一次配种牛号
+	LastPregnantCheckAt   int64                          `json:"lastPregnantCheckAt"`   // 最后一次孕检时间
+	LastDryMilkAt         int64                          `json:"lastDryMilkAt"`         // 最近一次干奶日期
+	LastForbiddenMatingAt int64                          `json:"lastForbiddenMatingAt"` // 最近一次禁配时间
+	LastSecondWeight      int64                          `json:"lastSecondWeight"`      // 最后第二次称重
+	LastSecondWeightAt    int64                          `json:"lastSecondWeightAt"`    // 最后第二次称重时间
+	LastAbortionAt        int64                          `json:"lastAbortionAt"`        // 最近一次流产时间
+	LastWeightAt          int64                          `json:"lastWeightAt"`          // 最近一次称重时间
+	BatchNumber           string                         `json:"batchNumber"`           // 批次号
+	CreatedAt             int64                          `json:"createdAt"`
+	UpdatedAt             int64                          `json:"updatedAt"`
 }
 
 func (c *Cow) TableName() string {
 	return "cow"
 }
 
+// EventUpdate 牛只基本信息维护
+func (c *Cow) EventUpdate(weeklyActive int32) {
+	c.DayAge = c.GetDayAge()             // 日龄
+	c.CalvingAge = c.GetCalvingAge()     // 产后天数
+	c.PregnancyAge = c.GetDaysPregnant() // 怀孕天数
+	c.AdmissionAge = c.GetAdmissionAge() // 入场天数
+	c.AbortionAge = c.GetAbortionAge()   // 流产天数
+	c.WeeklyActive = weeklyActive        // 周活动量
+	c.LactationAge = c.GetLactationAge() // 泌乳天数
+	c.DryMilkAge = c.GetDryMilkAge()     // 干奶天数
+	c.MatingAge = c.GetMatingAge()       // 配后天数
+	if c.DayAge == 60 {
+		c.CowType = pasturePb.CowType_Weaned_Calf
+	}
+}
+
+// EventCalvingUpdate 产犊更新
+func (c *Cow) EventCalvingUpdate(calvingAt int64) {
+	c.Lact += 1
+	c.MatingTimes = 0
+	c.PregnancyAge = 0
+	c.AbortionTimes = 0
+	c.BreedStatus = pasturePb.BreedStatus_Calving
+	c.IsPregnant = pasturePb.IsShow_No
+	c.LastCalvingAt = calvingAt
+	c.CalvingAge = c.GetCalvingAge()
+	c.CowType = pasturePb.CowType_Breeding_Calf
+	c.MilkKind = pasturePb.CowMilk_Lactation
+}
+
+// EventWeaningUpdate 断奶更新
+func (c *Cow) EventWeaningUpdate(weaningAt int64, penId int32, currentWeight int64) {
+	c.PenId = penId
+	c.WeaningAt = weaningAt
+	c.CurrentWeight = currentWeight
+	c.LastWeightAt = weaningAt
+}
+
+// EventPregnantCheckUpdate 孕检更新
+func (c *Cow) EventPregnantCheckUpdate(breedStatus pasturePb.BreedStatus_Kind, pregnantCheckAt int64,
+	isPregnant pasturePb.IsShow_Kind, pregnancyCheckName string) {
+	c.BreedStatus = breedStatus
+	c.LastPregnantCheckAt = pregnantCheckAt
+	c.IsPregnant = isPregnant
+	c.PregnancyCheckName = pregnancyCheckName
+}
+
+// EventAbortionUpdate 流产更新
+func (c *Cow) EventAbortionUpdate(abortionAt int64, isLact pasturePb.IsShow_Kind) {
+	c.IsPregnant = pasturePb.IsShow_No
+	c.LastAbortionAt = abortionAt
+	c.BreedStatus = pasturePb.BreedStatus_Abort
+	c.AbortionTimes += 1
+	c.AllAbortionTimes += 1
+	c.PregnancyAge = 0
+	if isLact == pasturePb.IsShow_Ok {
+		c.Lact += 1
+	}
+}
+
+// EventWeightUpdate 称重更新
+func (c *Cow) EventWeightUpdate(weight, height, weightAt int64) {
+	c.LastSecondWeight = c.CurrentWeight
+	c.LastSecondWeightAt = c.LastWeightAt
+	c.LastWeightAt = weightAt
+	c.CurrentWeight = weight
+	c.CurrentHeight = height
+}
+
+// EventHealthStatusUpdate 健康状态更新
+func (c *Cow) EventHealthStatusUpdate(healthStatus pasturePb.HealthStatus_Kind) {
+	c.HealthStatus = healthStatus
+}
+
+// EventPenUpdate 更新栏舍
+func (c *Cow) EventPenUpdate(pen *Pen) {
+	c.PenId = pen.Id
+	c.PenName = pen.Name
+}
+
+// EventEarNumberUpdate 更新耳标号
+func (c *Cow) EventEarNumberUpdate(newEarNumber string) {
+	c.EarOldNumber = c.EarNumber
+	c.EarNumber = newEarNumber
+}
+
+// EventDeathUpdate 更新牛只死亡信息
+func (c *Cow) EventDeathUpdate(eventAt int64) {
+	c.AdmissionStatus = pasturePb.AdmissionStatus_Die
+	c.HealthStatus = pasturePb.HealthStatus_Dead
+	c.DepartureAt = eventAt
+	c.NeckRingNumber = ""
+}
+
+// EventSaleUpdate 更新牛只销售信息
+func (c *Cow) EventSaleUpdate(eventSale *EventSale) {
+	c.DepartureAt = eventSale.SaleAt
+	c.NeckRingNumber = ""
+	c.DeparturePrice = float32(eventSale.SalePrice)
+	if eventSale.SaleKind == pasturePb.SalesType_Out {
+		c.HealthStatus = pasturePb.HealthStatus_Out
+		c.AdmissionStatus = pasturePb.AdmissionStatus_Out
+	}
+	if eventSale.SaleKind == pasturePb.SalesType_Sales {
+		c.AdmissionStatus = pasturePb.AdmissionStatus_Sale
+	}
+	if eventSale.SaleCowCount > 0 {
+		c.DepartureAvgWeight = eventSale.SaleAllWeight / eventSale.SaleCowCount
+	}
+}
+
+// EventMatingUpdate 配种更新
+func (c *Cow) EventMatingUpdate(matingAt int64, bullNumber string, isReMating bool) {
+	c.LastMatingAt = matingAt
+	c.LastBullNumber = bullNumber
+	c.IsPregnant = pasturePb.IsShow_No
+	c.BreedStatus = pasturePb.BreedStatus_Breeding
+	if c.FirstMatingAt <= 0 {
+		c.FirstMatingAt = matingAt
+	}
+	if !isReMating {
+		c.MatingTimes += 1
+		c.AllMatingTimes += 1
+	}
+	if c.Lact == 0 {
+		c.CowType = pasturePb.CowType_Reserve_Calf
+	}
+}
+
+// EstrusUpdate 发情更新
+func (c *Cow) EstrusUpdate(estrusAt int64) {
+	c.LastEstrusAt = estrusAt
+}
+
+// EventDryMilkUpdate 干奶更新
+func (c *Cow) EventDryMilkUpdate(dryMilkAt int64, pen *Pen) {
+	c.LastDryMilkAt = dryMilkAt
+	c.MilkKind = pasturePb.CowMilk_Dry_Milk
+	c.PenId = pen.Id
+	c.PenName = pen.Name
+}
+
+// ForbiddenMatingUpdate 禁配更新
+func (c *Cow) ForbiddenMatingUpdate(forbiddenMatingAt int64) {
+	c.IsForbiddenMating = pasturePb.IsShow_Ok
+	c.LastForbiddenMatingAt = forbiddenMatingAt
+	c.BreedStatus = pasturePb.BreedStatus_No_Mating
+}
+
+// UnForbiddenMatingUpdate 解禁配更新
+func (c *Cow) UnForbiddenMatingUpdate() {
+	c.IsForbiddenMating = pasturePb.IsShow_No
+	c.LastForbiddenMatingAt = 0
+	c.BreedStatus = pasturePb.BreedStatus_UnBreed
+}
+
+// GetAvgDailyWeight 牛只平均日增重
+func (c *Cow) GetAvgDailyWeight() float32 {
+	if c.AdmissionAge <= 0 {
+		c.AdmissionAge = c.GetAdmissionAge()
+	}
+
+	if c.AdmissionAge <= 0 {
+		return 0
+	}
+	return float32(c.CurrentWeight-c.AdmissionWeight) / 1000 / float32(c.AdmissionAge)
+}
+
+// GetDayAge 日龄
+func (c *Cow) GetDayAge() int32 {
+	if c.BirthAt <= 0 {
+		return 0
+	}
+	return int32(math.Floor(float64(time.Now().Local().Unix()-c.BirthAt) / 86400))
+}
+
+// GetEventDayAge 获取事件发生的日龄
+func (c *Cow) GetEventDayAge(eventAt int64) int32 {
+	if c.BirthAt <= 0 {
+		return 0
+	}
+	return int32(math.Floor(float64(eventAt-c.BirthAt) / 86400))
+}
+
+// GetCalvingAge 产后天数
+func (c *Cow) GetCalvingAge() int32 {
+	if c.LastCalvingAt <= 0 {
+		return 0
+	}
+	return int32(math.Floor(float64(time.Now().Local().Unix()-c.LastCalvingAt) / 86400))
+}
+
+// GetDaysPregnant 怀孕天数
+func (c *Cow) GetDaysPregnant() int32 {
+	if c.BreedStatus == pasturePb.BreedStatus_Pregnant &&
+		c.AdmissionStatus == pasturePb.AdmissionStatus_Admission &&
+		c.IsPregnant == pasturePb.IsShow_Ok {
+		return int32(math.Floor(float64(time.Now().Local().Unix()-c.LastMatingAt) / 86400))
+	}
+	return 0
+}
+
+// GetDaysPregnancy 牛只预产日期
+func (c *Cow) GetDaysPregnancy(pregnancyAgeValue int32) int32 {
+	if c.BreedStatus == pasturePb.BreedStatus_Pregnant &&
+		c.AdmissionStatus == pasturePb.AdmissionStatus_Admission &&
+		c.IsPregnant == pasturePb.IsShow_Ok {
+		return int32(math.Floor(float64(c.LastMatingAt + int64(pregnancyAgeValue)*86400)))
+	}
+	return 0
+}
+
+// GetLactationDays 泌乳天数
+func (c *Cow) GetLactationDays() int32 {
+	if c.BreedStatus == pasturePb.BreedStatus_Calving && c.AdmissionStatus == pasturePb.AdmissionStatus_Admission {
+		return int32(math.Floor(float64(time.Now().Local().Unix()-c.LastCalvingAt) / 86400))
+	}
+	return 0
+}
+
+// GetAdmissionAge 入场天数
+func (c *Cow) GetAdmissionAge() int32 {
+	if c.AdmissionAt > 0 && c.AdmissionStatus == pasturePb.AdmissionStatus_Admission {
+		return int32(math.Floor(float64(time.Now().Local().Unix()-c.AdmissionAt) / 86400))
+	}
+	return 0
+}
+
+func (c *Cow) GetEventAdmissionAge(eventAt int64) int32 {
+	if eventAt > 0 && c.AdmissionStatus == pasturePb.AdmissionStatus_Admission {
+		return int32(math.Floor(float64(eventAt-c.AdmissionAt) / 86400))
+	}
+	return 0
+}
+
+// GetAverageDailyWeight 平均日增重(最后一次称重 - 入场体重)÷在群天数
+func (c *Cow) GetAverageDailyWeight() float64 {
+	if c.AdmissionAge <= 0 {
+		c.AdmissionAge = c.GetAdmissionAge()
+	}
+
+	if c.AdmissionAge <= 0 {
+		return 0
+	}
+
+	if c.CurrentWeight-c.AdmissionWeight <= 0 {
+		return 0
+	}
+	return float64(c.CurrentWeight-c.AdmissionWeight) / 1000 / float64(c.AdmissionAge)
+}
+
+// GetPreviousStageDailyWeight 上一个阶段日增重
+func (c *Cow) GetPreviousStageDailyWeight() float64 {
+	if c.CurrentWeight-c.LastSecondWeight > 0 && c.LastWeightAt-c.LastSecondWeightAt > 0 {
+		days := int32(math.Floor(float64(c.LastWeightAt-c.LastSecondWeightAt) / 86400))
+		if days <= 0 {
+			return float64(c.CurrentWeight - c.LastSecondWeight)
+		}
+		dayWeight := math.Round(1.0 * float64(c.CurrentWeight-c.LastSecondWeight) / float64(days))
+		return dayWeight / 1000
+	}
+	return 0
+}
+
+// GetAbortionAge 流产天数
+func (c *Cow) GetAbortionAge() int32 {
+	if c.LastAbortionAt > 0 && c.AdmissionStatus == pasturePb.AdmissionStatus_Admission {
+		return int32(math.Floor(float64(time.Now().Local().Unix()-c.LastAbortionAt) / 86400))
+	}
+	return 0
+}
+
+// GetLactationAge 泌乳天数
+func (c *Cow) GetLactationAge() int32 {
+	if c.LastCalvingAt <= 0 {
+		return 0
+	}
+
+	if c.MilkKind == pasturePb.CowMilk_Lactation ||
+		c.BreedStatus == pasturePb.BreedStatus_Calving ||
+		c.BreedStatus == pasturePb.BreedStatus_Abort {
+		return int32(math.Floor(float64(time.Now().Local().Unix()-c.LastCalvingAt) / 86400))
+	}
+	return c.LactationAge
+}
+
+// GetDryMilkAge 干奶天数
+func (c *Cow) GetDryMilkAge() int32 {
+	if c.MilkKind == pasturePb.CowMilk_Dry_Milk {
+		return int32(math.Floor(float64(time.Now().Local().Unix()-c.LastDryMilkAt) / 86400))
+	}
+	return c.DryMilkAge
+}
+
+func (c *Cow) GetMatingAge() int32 {
+	if c.LastMatingAt <= 0 {
+		return 0
+	}
+	return int32(math.Floor(float64(time.Now().Local().Unix()-c.LastMatingAt) / 86400))
+}
+
 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))
+	cowSourceMap map[pasturePb.CowSource_Kind]string,
+	admissionStatusMap map[pasturePb.AdmissionStatus_Kind]string,
+	healthStatusMap map[pasturePb.HealthStatus_Kind]string,
+	purposeMap map[pasturePb.Purpose_Kind]string,
+	pregnancyAge int32,
+) []*pasturePb.CowDetails {
+	res := make([]*pasturePb.CowDetails, len(c))
 	for i, v := range c {
-		penName := ""
-		for _, pen := range penList {
-			if v.PenId != int64(pen.Id) {
-				continue
-			}
-			penName = pen.Name
+		sex := "公"
+		if v.Sex == pasturePb.Genders_Female {
+			sex = "母"
+		}
+		lastWeightAtFormat := ""
+		if v.LastWeightAt > 0 {
+			lastWeightAtFormat = time.Unix(v.LastWeightAt, 0).Local().Format(LayoutDate2)
+		}
+
+		isPregnantName := ""
+		if v.IsPregnant == pasturePb.IsShow_Ok {
+			isPregnantName = "已孕"
+		} else {
+			isPregnantName = "未孕"
+		}
+
+		admissionAtFormat := ""
+		if v.AdmissionAt > 0 {
+			admissionAtFormat = time.Unix(v.AdmissionAt, 0).Local().Format(LayoutDate2)
+		}
+
+		birthAtFormat := ""
+		if v.BirthAt > 0 {
+			birthAtFormat = time.Unix(v.BirthAt, 0).Local().Format(LayoutDate2)
+		}
+
+		weaningAtFormat := ""
+		if v.WeaningAt > 0 {
+			weaningAtFormat = time.Unix(v.WeaningAt, 0).Local().Format(LayoutDate2)
+		}
+
+		firstMatingAtFormat := ""
+		if v.FirstMatingAt > 0 {
+			firstMatingAtFormat = time.Unix(v.FirstMatingAt, 0).Local().Format(LayoutDate2)
+		}
+
+		lastMatingAtFormat := ""
+		if v.LastMatingAt > 0 {
+			lastMatingAtFormat = time.Unix(v.LastMatingAt, 0).Local().Format(LayoutDate2)
+		}
+
+		lastPregnantCheckAtFormat := ""
+		if v.LastPregnantCheckAt > 0 {
+			lastPregnantCheckAtFormat = time.Unix(v.LastPregnantCheckAt, 0).Local().Format(LayoutDate2)
+		}
+
+		lastCalvingAtFormat := ""
+		if v.LastCalvingAt > 0 {
+			lastCalvingAtFormat = time.Unix(v.LastCalvingAt, 0).Local().Format(LayoutDate2)
+		}
+
+		lastAbortionAtFormat := ""
+		if v.LastAbortionAt > 0 {
+			lastAbortionAtFormat = time.Unix(v.LastAbortionAt, 0).Local().Format(LayoutDate2)
+		}
+
+		lastSecondWeightAtFormat := ""
+		if v.LastSecondWeightAt > 0 {
+			lastSecondWeightAtFormat = time.Unix(v.LastSecondWeightAt, 0).Local().Format(LayoutDate2)
+		}
+
+		departureAtFormat := ""
+		if v.DepartureAt > 0 {
+			departureAtFormat = time.Unix(v.DepartureAt, 0).Local().Format(LayoutDate2)
+		}
+
+		lastForbiddenMatingAtFormat := ""
+		if v.LastForbiddenMatingAt > 0 {
+			lastForbiddenMatingAtFormat = time.Unix(v.LastForbiddenMatingAt, 0).Local().Format(LayoutDate2)
+		}
+
+		lastEstrusAtFormat := ""
+		if v.LastEstrusAt > 0 {
+			lastEstrusAtFormat = time.Unix(v.LastEstrusAt, 0).Local().Format(LayoutDate2)
+		}
+
+		cowTypeName := ""
+		if cn, ok := cowTypeMap[v.CowType]; ok {
+			cowTypeName = cn
 		}
-		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),
+
+		breedStatusName := ""
+		if bs, ok := breedStatusMap[v.BreedStatus]; ok {
+			breedStatusName = bs
+		}
+
+		cowKindName := ""
+		if ck, ok := cowKindMap[v.CowKind]; ok {
+			cowKindName = ck
+		}
+
+		sourceName := ""
+		if sn, ok := cowSourceMap[v.SourceKind]; ok {
+			sourceName = sn
+		}
+
+		admissionStatusName := ""
+		if as, ok := admissionStatusMap[v.AdmissionStatus]; ok {
+			admissionStatusName = as
+		}
+
+		healthStatusName := ""
+		if hs, ok := healthStatusMap[v.HealthStatus]; ok {
+			healthStatusName = hs
+		}
+
+		pregnancyDate := ""
+		if pregnancyAgeAt := v.GetDaysPregnancy(pregnancyAge); pregnancyAgeAt > 0 {
+			pregnancyDate = time.Unix(int64(pregnancyAge), 0).Local().Format(LayoutDate2)
+		}
+
+		purposeName := ""
+		if pn, ok := purposeMap[v.PurposeKind]; ok {
+			purposeName = pn
+		}
+
+		pregnancyCheckTimes := "初检"
+		if v.PregnancyCheckName == PregnantCheckForSecond {
+			pregnancyCheckTimes = "复检"
+		}
+
+		res[i] = &pasturePb.CowDetails{
+			CowId:                       int32(v.Id),
+			Sex:                         sex,
+			NeckRingNumber:              v.NeckRingNumber,
+			PenName:                     v.PenName,
+			Lact:                        v.Lact,
+			CowTypeName:                 cowTypeName,
+			CowType:                     v.CowType,
+			BreedStatusName:             breedStatusName,
+			BreedStatus:                 v.BreedStatus,
+			CowKindName:                 cowKindName,
+			EarNumber:                   v.EarNumber,
+			BirthWeight:                 float32(v.BirthWeight) / 1000,
+			CurrentWeight:               float32(v.CurrentWeight) / 1000,
+			CurrentHeight:               int32(v.CurrentHeight),
+			DayAge:                      v.DayAge,
+			AdmissionAge:                v.AdmissionAge,
+			SourceName:                  sourceName,
+			MotherNumber:                v.MotherNumber,
+			FatherNumber:                v.FatherNumber,
+			AdmissionStatusName:         admissionStatusName,
+			HealthStatusName:            healthStatusName,
+			IsPregnantName:              isPregnantName,
+			AdmissionAtFormat:           admissionAtFormat,
+			BirthAtFormat:               birthAtFormat,
+			WeaningAtFormat:             weaningAtFormat,
+			CalvingAge:                  v.GetCalvingAge(),
+			AbortionAge:                 v.AbortionAge,
+			MatingTimes:                 v.MatingTimes,
+			AbortionTimes:               v.AbortionTimes,
+			FirstMatingAtFormat:         firstMatingAtFormat,
+			LastMatingAtFormat:          lastMatingAtFormat,
+			LastBullNumber:              v.LastBullNumber,
+			LastPregnantCheckAtFormat:   lastPregnantCheckAtFormat,
+			LastWeightAtFormat:          lastWeightAtFormat,
+			LastCalvingAtFormat:         lastCalvingAtFormat,
+			LastAbortionAtFormat:        lastAbortionAtFormat,
+			LastSecondWeight:            float32(v.LastSecondWeight) / 1000,
+			LastSecondWeightAtFormat:    lastSecondWeightAtFormat,
+			DepartureAtFormat:           departureAtFormat,
+			DeparturePrice:              v.DeparturePrice,
+			DepartureWeight:             float32(v.DepartureAvgWeight / 1000),
+			LastForbiddenMatingAtFormat: lastForbiddenMatingAtFormat,
+			LastEstrusAtFormat:          lastEstrusAtFormat,
+			PregnancyDate:               pregnancyDate,
+			PregnancyCheckTimes:         pregnancyCheckTimes,
+			AvgDailyWeight:              float32(v.GetAverageDailyWeight()),
+			EleEarNumber:                v.EleEarNumber,
+			AdmissionPrice:              v.AdmissionPrice,
+			PurposeName:                 purposeName,
+			BatchNumber:                 v.BatchNumber,
+			AdmissionWeight:             float32(v.AdmissionWeight / 1000),
 		}
 	}
 	return res
 }
 
-func NewCow(req *pasturePb.EventEnterData) *Cow {
+func (c CowSlice) ToPB2(penWeightSlice PenWeightSlice) []*pasturePb.CowList {
+	res := make([]*pasturePb.CowList, len(c))
+	for i, v := range c {
+		penWeight := penWeightSlice.GetPenWeight(v.PenId)
+		penAvgWeight := float32(0)
+		cowPenAvgWeightDiffValue := float32(0)
+
+		if penWeight != nil {
+			penAvgWeight = float32(penWeight.AvgWeight) / 1000
+			cowPenAvgWeightDiffValue = float32(v.CurrentWeight-int64(penWeight.AvgWeight)) / 1000
+		}
+
+		res[i] = &pasturePb.CowList{
+			CowId:                    int32(v.Id),
+			DayAge:                   v.DayAge,
+			AverageDailyWeightGain:   float32(v.GetAverageDailyWeight()),
+			EarNumber:                v.EarNumber,
+			PenName:                  v.PenName,
+			BirthAt:                  int32(v.BirthAt),
+			BirthWeight:              float32(v.BirthWeight) / 1000,
+			CurrentWeight:            float32(v.CurrentWeight) / 1000,
+			LastWeightAt:             int32(v.LastWeightAt),
+			AdmissionAge:             v.AdmissionAge,
+			AdmissionWeight:          float32(v.AbortionAge) / 1000,
+			PreviousStageDailyWeight: float32(v.GetPreviousStageDailyWeight()),
+			PenAvgWeight:             penAvgWeight,
+			CowPenAvgWeightDiffValue: cowPenAvgWeightDiffValue,
+		}
+	}
+	return res
+}
+
+// NewEnterCow 入场新增牛只
+func NewEnterCow(pastureId int64, req *pasturePb.EventEnterRequest, penMap map[int32]*Pen) *Cow {
 	var isPregnant = pasturePb.IsShow_No
-	if req.BreedStatusId == pasturePb.BreedStatus_Pregnant {
+	if req.BreedStatus == pasturePb.BreedStatus_Pregnant {
 		isPregnant = pasturePb.IsShow_Ok
 	}
 
-	return &Cow{
+	admissionAt := int64(0)
+	switch req.CowSource {
+	case pasturePb.CowSource_Calving:
+		admissionAt = int64(req.BirthAt)
+	case pasturePb.CowSource_Transfer_In:
+		admissionAt = int64(req.EnterAt)
+	case pasturePb.CowSource_Buy:
+		admissionAt = int64(req.EnterAt)
+	}
+
+	breedStatus := pasturePb.BreedStatus_Invalid
+	isForbiddenMating := pasturePb.IsShow_No
+	cowType := pasturePb.CowType_Invalid
+	if req.Sex == pasturePb.Genders_Female {
+
+		if req.Lact == 0 && req.MatingAt <= 0 {
+			breedStatus = pasturePb.BreedStatus_UnBreed
+			cowType = pasturePb.CowType_Reserve_Calf
+		}
+
+		if req.MatingAt > 0 && (req.PregnantCheckResult != pasturePb.PregnantCheckResult_Pregnant &&
+			req.PregnantCheckResult != pasturePb.PregnantCheckResult_UnPregnant) &&
+			req.MatingAt >= req.CalvingAt && req.MatingAt >= req.AbortionAt {
+			breedStatus = pasturePb.BreedStatus_Breeding
+			if req.Lact == 0 {
+				cowType = pasturePb.CowType_Reserve_Calf
+			} else {
+				cowType = pasturePb.CowType_Breeding_Calf
+			}
+		}
+
+		if req.MatingAt > 0 && req.PregnantCheckResult == pasturePb.PregnantCheckResult_Pregnant &&
+			req.MatingAt >= req.CalvingAt && req.MatingAt >= req.AbortionAt {
+			breedStatus = pasturePb.BreedStatus_Pregnant
+			if req.Lact == 0 {
+				cowType = pasturePb.CowType_Reserve_Calf
+			} else {
+				cowType = pasturePb.CowType_Breeding_Calf
+			}
+		}
+
+		if req.MatingAt > 0 && req.PregnantCheckResult == pasturePb.PregnantCheckResult_UnPregnant &&
+			req.MatingAt >= req.CalvingAt && req.MatingAt >= req.AbortionAt {
+			breedStatus = pasturePb.BreedStatus_Empty
+			if req.Lact == 0 {
+				cowType = pasturePb.CowType_Reserve_Calf
+			} else {
+				cowType = pasturePb.CowType_Breeding_Calf
+			}
+		}
+
+		if req.CalvingAt > 0 && req.CalvingAt >= req.MatingAt && req.CalvingAt >= req.AbortionAt {
+			breedStatus = pasturePb.BreedStatus_Calving
+			cowType = pasturePb.CowType_Breeding_Calf
+		}
+
+		if req.AbortionAt > 0 && req.AbortionAt >= req.CalvingAt && req.AbortionAt >= req.MatingAt {
+			breedStatus = pasturePb.BreedStatus_Abort
+			cowType = pasturePb.CowType_Breeding_Calf
+		}
+	}
+
+	if breedStatus == pasturePb.BreedStatus_No_Mating {
+		isForbiddenMating = pasturePb.IsShow_Ok
+	}
+
+	cow := &Cow{
+		PastureId:           pastureId,
 		Sex:                 req.Sex,
 		EarNumber:           req.EarNumber,
-		PenId:               int64(req.PenId),
+		PenId:               req.PenId,
+		PenName:             penMap[req.PenId].Name,
 		Lact:                req.Lact,
-		CowType:             req.CowTypeId,
-		BreedStatus:         req.BreedStatusId,
-		CowKind:             req.CowKindId,
-		SourceId:            req.CowSourceId,
-		FatherId:            int64(req.FatherId),
-		MotherId:            int64(req.MotherId),
-		IsRemove:            pasturePb.IsShow_Ok,
+		CowType:             cowType,
+		BreedStatus:         breedStatus,
+		CowKind:             req.CowKind,
+		SourceKind:          req.CowSource,
+		FatherNumber:        req.FatherNumber,
+		MotherNumber:        req.MotherNumber,
+		AdmissionStatus:     pasturePb.AdmissionStatus_Admission,
+		HealthStatus:        pasturePb.HealthStatus_Health,
+		PurposeKind:         req.PurposeKind,
+		EleEarNumber:        req.EleEarNumber,
 		IsPregnant:          isPregnant,
+		IsForbiddenMating:   isForbiddenMating,
 		WeaningAt:           int64(req.WeaningAt),
 		BirthAt:             int64(req.BirthAt),
+		AdmissionWeight:     int64(req.Weight * 1000),
 		FirstMatingAt:       int64(req.MatingAt),
 		LastMatingAt:        int64(req.MatingAt),
 		LastPregnantCheckAt: int64(req.PregnancyCheckAt),
+		AdmissionAt:         admissionAt,
+		BirthWeight:         int64(req.Weight * 1000),
+		LastWeightAt:        int64(req.EstrusAt),
+		CurrentWeight:       int64(req.Weight * 1000),
+		LastDryMilkAt:       int64(req.DryMilkAt),
+		MatingTimes:         req.MatingTimes,
+		LastCalvingAt:       int64(req.CalvingAt),
+		LastBullNumber:      req.BullNumber,
+		LastAbortionAt:      int64(req.AbortionAt),
+		AdmissionPrice:      req.Price,
+		BatchNumber:         req.BatchNumber,
 	}
+	cow.AdmissionAge = cow.GetAdmissionAge()
+	cow.DayAge = cow.GetDayAge()
+	return cow
 }
 
-func NewCalfCow(motherId, fatherId int64, calf *CalvingCalf) *Cow {
+// NewCalfCow 产犊新增
+func NewCalfCow(matherInfo *Cow, calf *CalvingCalf) *Cow {
 	return &Cow{
-		Sex:         calf.Sex,
-		EarNumber:   calf.EarNumber,
-		PenId:       int64(calf.PenId),
-		CowType:     pasturePb.CowType_Lactating_Calf, // 哺乳犊牛
-		BreedStatus: pasturePb.BreedStatus_UnBreed,    // 未配
-		CowKind:     calf.CowKind,                     // 牛只品种
-		BirthWeight: calf.BrithWeight,
-		SourceId:    pasturePb.CowSource_Calving, // 产犊方式
-		FatherId:    fatherId,
-		MotherId:    motherId,
-		IsRemove:    pasturePb.IsShow_Ok,
-		IsPregnant:  pasturePb.IsShow_No,
+		PastureId:       calf.PastureId,
+		Sex:             calf.Sex,
+		EarNumber:       calf.EarNumber,
+		PenId:           calf.PenId,
+		PenName:         calf.PenName,
+		CowType:         pasturePb.CowType_Lactating_Calf, // 哺乳犊牛
+		BreedStatus:     pasturePb.BreedStatus_UnBreed,    // 未配
+		CowKind:         matherInfo.CowKind,               // 牛只品种
+		BirthWeight:     calf.BirthWeight,
+		BirthAt:         calf.BirthAt,
+		SourceKind:      pasturePb.CowSource_Calving, // 产犊方式
+		FatherNumber:    matherInfo.LastBullNumber,
+		MotherNumber:    matherInfo.EarNumber,
+		AdmissionStatus: pasturePb.AdmissionStatus_Admission,
+		IsPregnant:      pasturePb.IsShow_No,
+		AdmissionAt:     calf.BirthAt,
 	}
 }
 
@@ -141,6 +744,7 @@ type BarCowStruct struct {
 	TypeId pasturePb.CowType_Kind `json:"type_id"`
 }
 
+// BarCowStructSlice 首页牛群结构
 type BarCowStructSlice []*BarCowStruct
 
 func (b BarCowStructSlice) ToPB(cowTypeMap map[pasturePb.CowType_Kind]string, count int32) []*pasturePb.BarCowStruct {
@@ -152,67 +756,93 @@ func (b BarCowStructSlice) ToPB(cowTypeMap map[pasturePb.CowType_Kind]string, co
 	return pb
 }
 
-// GetDayAge 日龄
-func (c *Cow) GetDayAge() int32 {
-	if c.BirthAt <= 0 {
-		return 0
-	}
-	return int32(math.Floor(float64(time.Now().Unix()-c.BirthAt) / 86400))
-}
-
-// 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 && c.IsRemove == pasturePb.IsShow_No && c.IsPregnant == pasturePb.IsShow_Ok {
-		return int32(math.Floor(float64(time.Now().Unix()-c.LastMatingAt) / 86400))
-	}
-	return 0
+type CowWeightRange struct {
+	WeightRange string `json:"weight_range"`
+	Count       int32  `json:"count"`
 }
 
-// GetLactationDays 泌乳天数
-func (c *Cow) GetLactationDays() int32 {
-	if c.BreedStatus == pasturePb.BreedStatus_Calving && c.IsRemove == pasturePb.IsShow_Ok {
-		return int32(math.Floor(float64(time.Now().Unix()-c.LastCalvingAt) / 86400))
+func (c CowSlice) WeightRangeToPB(penMap map[int32]*Pen) []*pasturePb.CowList {
+	res := make([]*pasturePb.CowList, len(c))
+	for i, v := range c {
+		penName := ""
+		if pen, ok := penMap[v.PenId]; ok {
+			penName = pen.Name
+		}
+		res[i] = &pasturePb.CowList{
+			CowId:                    int32(v.Id),
+			DayAge:                   v.DayAge,
+			AverageDailyWeightGain:   float32(v.GetAverageDailyWeight()),
+			PreviousStageDailyWeight: float32(v.GetPreviousStageDailyWeight()),
+			EarNumber:                v.EarNumber,
+			PenName:                  penName,
+			BirthAt:                  int32(v.BirthAt),
+			BirthWeight:              float32(v.BirthWeight) / 1000,
+			CurrentWeight:            float32(v.CurrentWeight) / 1000,
+			LastWeightAt:             int32(v.LastWeightAt),
+			AdmissionAge:             v.AdmissionAge,
+		}
 	}
-	return 0
+	return res
 }
 
-// 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))
+func (c CowSlice) LongTermInfertilityToPB(breedStatusMap map[pasturePb.BreedStatus_Kind]string) []*pasturePb.LongTermInfertility {
+	res := make([]*pasturePb.LongTermInfertility, len(c))
+	for i, v := range c {
+		breedStatusName := ""
+		if breedStatus, ok := breedStatusMap[v.BreedStatus]; ok {
+			breedStatusName = breedStatus
+		}
+		lastCalvingAtFormat := ""
+		if v.LastCalvingAt > 0 {
+			lastCalvingAtFormat = time.Unix(v.LastCalvingAt, 0).Local().Format(LayoutDate2)
+		}
+		lastAbortionAtFormat := ""
+		if v.LastAbortionAt > 0 {
+			lastAbortionAtFormat = time.Unix(v.LastAbortionAt, 0).Local().Format(LayoutDate2)
+		}
+		lastMatingAtFormat := ""
+		if v.LastMatingAt > 0 {
+			lastMatingAtFormat = time.Unix(v.LastMatingAt, 0).Local().Format(LayoutDate2)
+		}
+		res[i] = &pasturePb.LongTermInfertility{
+			CowId:                int32(v.Id),
+			EarNumber:            v.EarNumber,
+			Lact:                 v.Lact,
+			PenId:                v.PenId,
+			CalvingAge:           v.CalvingAge,
+			PenName:              v.PenName,
+			BreedStatusName:      breedStatusName,
+			BreedStatus:          v.BreedStatus,
+			LastCalvingAtFormat:  lastCalvingAtFormat,
+			LastAbortionAtFormat: lastAbortionAtFormat,
+			LastMatingAtFormat:   lastMatingAtFormat,
+			MatingTimes:          v.MatingTimes,
+			LastBullNumber:       v.LastBullNumber,
+			AbortionTimes:        v.AbortionTimes,
+		}
 	}
-	return 0
+	return res
 }
 
-// GetDayWeight 日增重
-func (c *Cow) GetDayWeight() float64 {
-	if c.CurrentWeight-c.LastSecondWeight > 0 && c.LastWeightAt > c.LastSecondWeightAt {
-		days := int32(math.Floor(float64(c.LastWeightAt-c.LastSecondWeightAt) / 86400))
-		if days <= 0 {
-			return 0
-		}
-		return math.Round(1.0 * float64(c.CurrentWeight-c.LastSecondWeight) / float64(days))
-	}
-	return 0
+// CowBehaviorCurveResponse 脖环行为数据
+type CowBehaviorCurveResponse struct {
+	Code int32                 `json:"code"`
+	Msg  string                `json:"msg"`
+	Data *CowBehaviorCurveData `json:"data"`
 }
 
-// GetAverageDailyWeight 平均日增重
-func (c *Cow) GetAverageDailyWeight() float64 {
-	if c.CurrentWeight-c.BirthWeight > 0 && c.LastWeightAt > c.BirthAt {
-		days := int32(math.Floor(float64(c.LastWeightAt-c.BirthAt) / 86400))
-		if days <= 0 {
-			return 0
-		}
-		dailyWeight := math.Round(1.0 * float64(c.CurrentWeight-c.BirthWeight) / float64(days))
-		return dailyWeight
-	}
-	return 0
+type CowBehaviorCurveData struct {
+	OriginalDateList []int32                                 `json:"originalDateList"` // 原始行为数据
+	ChangeDateList   []int32                                 `json:"changeDateList"`   // 变化数据
+	SumDateList      []int32                                 `json:"sumDateList"`      // 累计24小时数据
+	SumChewList      []int32                                 `json:"sumChewList"`      // 累计24小时咀嚼
+	DateTimeList     []string                                `json:"dateTimeList"`     // 时间数据
+	EstrusList       map[pasturePb.EstrusLevel_Kind][]string `json:"estrusList"`       // 发情预警
+	EventList        []*pasturePb.CowEvent                   `json:"eventList"`        // 事件数据
+	EventMap         map[pasturePb.EventType_Kind]string     `json:"eventMap"`         // 所有事件
+	RuminaChange     []int32                                 `json:"ruminaChange"`     // 反刍变化
+	LowActivity      int32                                   `json:"lowActivity"`      // 低活动量参数
+	MiddleActivity   int32                                   `json:"middleActivity"`   // 中活动量行数
+	IQR1             []int32                                 `json:"IQR1"`
+	IQR3             []int32                                 `json:"IQR3"`
 }

+ 90 - 0
model/cow_calving.go

@@ -0,0 +1,90 @@
+package model
+
+import pasturePb "gitee.com/xuyiping_admin/go_proto/proto/go/backend/cow"
+
+type CowCalving struct {
+	Id                int32                  `json:"id"`
+	PastureId         int64                  `json:"pastureId"`
+	DateTime          string                 `json:"dateTime"`
+	CowId             int64                  `json:"cowId"`
+	Lact              int32                  `json:"lact"`
+	EarNumber         string                 `json:"earNumber"`
+	NeckRingNumber    string                 `json:"neckRingNumber"`
+	CowType           pasturePb.CowType_Kind `json:"cowType"`
+	CowKind           pasturePb.CowKind_Kind `json:"cowKind"`
+	CalvingAge        int32                  `json:"calvingAge"`
+	MatingAge         int32                  `json:"matingAge"`
+	IsMating          pasturePb.IsShow_Kind  `json:"isMating"`
+	IsPregnantCheck   pasturePb.IsShow_Kind  `json:"isPregnantCheck"`
+	PregnantCheckDate string                 `json:"pregnantCheckDate"`
+	IsAbortion        pasturePb.IsShow_Kind  `json:"isAbortion"`
+	IsDeparture       pasturePb.IsShow_Kind  `json:"isDeparture"`
+	IsForbidden       pasturePb.IsShow_Kind  `json:"isForbidden"`
+	CreatedAt         int64                  `json:"createdAt"`
+	UpdatedAt         int64                  `json:"updatedAt"`
+}
+
+func (e *CowCalving) TableName() string {
+	return "cow_calving"
+}
+
+func NewEveryDayCalving(pastureId int64, dateTime string, cowInfo *Cow) *CowCalving {
+	isMating, isPregnantCheck, isAbortion, isDeparture, isForbidden := pasturePb.IsShow_No, pasturePb.IsShow_No, pasturePb.IsShow_No, pasturePb.IsShow_No, pasturePb.IsShow_No
+	switch cowInfo.BreedStatus {
+	case pasturePb.BreedStatus_Abort:
+		isAbortion = pasturePb.IsShow_Ok
+	case pasturePb.BreedStatus_Breeding:
+		isMating = pasturePb.IsShow_Ok
+	case pasturePb.BreedStatus_Pregnant, pasturePb.BreedStatus_Empty:
+		isPregnantCheck = pasturePb.IsShow_Ok
+	case pasturePb.BreedStatus_No_Mating:
+		isPregnantCheck = pasturePb.IsShow_Ok
+	}
+
+	if cowInfo.AdmissionStatus != pasturePb.AdmissionStatus_Admission {
+		isDeparture = pasturePb.IsShow_Ok
+	}
+
+	return &CowCalving{
+		PastureId:         pastureId,
+		DateTime:          dateTime,
+		CowId:             cowInfo.Id,
+		Lact:              cowInfo.Lact,
+		EarNumber:         cowInfo.EarNumber,
+		NeckRingNumber:    cowInfo.NeckRingNumber,
+		CowType:           cowInfo.CowType,
+		CowKind:           cowInfo.CowKind,
+		CalvingAge:        cowInfo.CalvingAge,
+		MatingAge:         cowInfo.MatingAge,
+		IsMating:          isMating,
+		IsPregnantCheck:   isPregnantCheck,
+		PregnantCheckDate: "",
+		IsAbortion:        isAbortion,
+		IsDeparture:       isDeparture,
+		IsForbidden:       isForbidden,
+	}
+}
+
+func NewEveryDayCalvingList(pastureId int64, dateTime string, cowList []*Cow) []*CowCalving {
+	res := make([]*CowCalving, 0)
+	for _, cow := range cowList {
+		res = append(res, NewEveryDayCalving(pastureId, dateTime, cow))
+	}
+	return res
+}
+
+type CowCalvingSlice []*CowCalving
+
+func (c CowCalvingSlice) ToPB() []*pasturePb.TwentyOnePregnantItems {
+	res := make([]*pasturePb.TwentyOnePregnantItems, 0)
+	for _, v := range c {
+		res = append(res, &pasturePb.TwentyOnePregnantItems{
+			CowId:     int32(v.CowId),
+			Lact:      v.Lact,
+			EarNumber: v.EarNumber,
+			//CowTypeName: v.CowType,
+		})
+	}
+
+	return res
+}

+ 37 - 0
model/cow_lact.go

@@ -0,0 +1,37 @@
+package model
+
+import (
+	"time"
+)
+
+type CowLact struct {
+	Id        int64  `json:"id"`
+	PastureId int64  `json:"pastureId"`
+	CowId     int64  `json:"cowId"`
+	EarNumber string `json:"earNumber"`
+	Lact      int32  `json:"lact"`
+	StartTime string `json:"startTime"`
+	CreatedAt int64  `json:"createdAt"`
+	UpdatedAt int64  `json:"updatedAt"`
+}
+
+func (c *CowLact) TableName() string {
+	return "cow_lact"
+}
+
+func NewCowLact(pastureId int64, cow *Cow) *CowLact {
+	startTime := ""
+	if cow.Lact == 0 {
+		startTime = time.Unix(cow.AdmissionAt, 0).Local().Format(LayoutDate2)
+	} else {
+		startTime = time.Unix(cow.LastCalvingAt, 0).Local().Format(LayoutDate2)
+	}
+
+	return &CowLact{
+		PastureId: pastureId,
+		CowId:     cow.Id,
+		EarNumber: cow.EarNumber,
+		Lact:      cow.Lact,
+		StartTime: startTime,
+	}
+}

+ 39 - 0
model/cow_pregnant.go

@@ -0,0 +1,39 @@
+package model
+
+import (
+	pasturePb "gitee.com/xuyiping_admin/go_proto/proto/go/backend/cow"
+)
+
+type CowPregnant struct {
+	Id           int64                  `json:"id"`
+	PastureId    int64                  `json:"pastureId"`
+	CowId        int64                  `json:"cowId"`
+	EarNumber    string                 `json:"earNumber"`
+	Lact         int32                  `json:"lact"`
+	PregnancyAge int32                  `json:"pregnancyAge"`
+	CowType      pasturePb.CowType_Kind `json:"cowType"`
+	DateTime     string                 `json:"dateTime"`
+	CreatedAt    int64                  `json:"createdAt"`
+	UpdatedAt    int64                  `json:"updatedAt"`
+}
+
+func (c *CowPregnant) TableName() string {
+	return "cow_pregnant"
+}
+
+func NewCowPregnant(pastureId int64, cowInfo *Cow, dateTime string) *CowPregnant {
+	return &CowPregnant{
+		PastureId:    pastureId,
+		CowId:        cowInfo.Id,
+		EarNumber:    cowInfo.EarNumber,
+		Lact:         cowInfo.Lact,
+		CowType:      cowInfo.CowType,
+		PregnancyAge: cowInfo.GetDaysPregnant(),
+		DateTime:     dateTime,
+	}
+}
+
+type CowPregnantMonth struct {
+	Month    string `json:"month"`
+	CowCount int32  `json:"cowCount"`
+}

+ 0 - 38
model/cow_weight.go

@@ -1,38 +0,0 @@
-package model
-
-import pasturePb "gitee.com/xuyiping_admin/go_proto/proto/go/backend/cow"
-
-type EventWeight struct {
-	ID            int64  `json:"id"`
-	CowId         int64  `json:"cow_id"`
-	EarNumber     string `json:"ear_number"`
-	DayAge        int32  `json:"day_age"`
-	LactationDay  int64  `json:"lactation_day"`
-	PenId         int32  `json:"pen_id"`
-	Lact          int32  `json:"lact"`
-	Weight        int32  `json:"weight"`
-	WeightAt      int64  `json:"weight_at"`
-	Remarks       string `json:"remarks"`
-	OperationId   int32  `json:"operation_id"`
-	StaffMemberId int32  `json:"staff_member_id"`
-	CreatedAt     int64  `json:"created_at"`
-	UpdatedAt     int64  `json:"updated_at"`
-}
-
-func (c *EventWeight) TableName() string {
-	return "event_weight"
-}
-
-func NewEventWeight(cow *Cow, systemUser *SystemUser, weight int32, req *pasturePb.EventWeight) *EventWeight {
-	return &EventWeight{
-		CowId:         cow.Id,
-		EarNumber:     cow.EarNumber,
-		Weight:        weight,
-		Lact:          cow.Lact,
-		DayAge:        cow.GetDayAge(),
-		WeightAt:      int64(req.WeightAt),
-		Remarks:       req.Remarks,
-		StaffMemberId: req.StaffMemberId,
-		OperationId:   int32(systemUser.Id),
-	}
-}

+ 22 - 0
model/crontab_log.go

@@ -0,0 +1,22 @@
+package model
+
+import "time"
+
+type CronLog struct {
+	Id        int64  `json:"id"`
+	Name      string `json:"name"`
+	Date      string `json:"date"`
+	CreatedAt int64  `json:"created_at"`
+	UpdatedAt int64  `json:"updated_at"`
+}
+
+func (c CronLog) TableName() string {
+	return "cron_log"
+}
+
+func NewCronLog(name string) *CronLog {
+	return &CronLog{
+		Name: name,
+		Date: time.Now().Local().Format(LayoutDate2),
+	}
+}

+ 96 - 0
model/data_notice.go

@@ -0,0 +1,96 @@
+package model
+
+import (
+	"kpt-pasture/util"
+	"strconv"
+	"strings"
+	"time"
+
+	pasturePb "gitee.com/xuyiping_admin/go_proto/proto/go/backend/cow"
+)
+
+const (
+	DefaultDataLatencyMinutes = 40
+	DataLatencyTitle          = "脖环数据延迟"
+)
+
+type DataNotice struct {
+	Id         int64                     `json:"id"`
+	PastureId  int64                     `json:"pastureId"`
+	Title      string                    `json:"title"`
+	Content    string                    `json:"content"`
+	KnownUsers string                    `json:"knownUsers"`
+	NoticeKind pasturePb.NoticeType_Kind `json:"noticeKind"`
+	StartDate  string                    `json:"startDate"`
+	EndDate    string                    `json:"endDate"`
+	IsShow     pasturePb.IsShow_Kind     `json:"isShow"`
+	CreatedAt  int64                     `json:"createdAt"`
+	UpdatedAt  int64                     `json:"updatedAt"`
+}
+
+func (d *DataNotice) TableName() string {
+	return "data_notice"
+}
+
+func (d *DataNotice) GetUserIds() []int64 {
+	userIds := make([]int64, 0)
+	if d.KnownUsers != "" {
+		userIdStr := strings.Split(d.KnownUsers, ",")
+		for _, u := range userIdStr {
+			uId, _ := strconv.ParseInt(u, 10, 64)
+			userIds = append(userIds, uId)
+		}
+	}
+	return userIds
+}
+
+func (d *DataNotice) UpdateNotice(content string) {
+	d.Content = content
+	d.KnownUsers = ""
+}
+
+func NewDataNotice(pastureId int64, title, content string, noticeKind pasturePb.NoticeType_Kind) *DataNotice {
+	nowTime := time.Now().Local()
+	return &DataNotice{
+		Title:      title,
+		Content:    content,
+		StartDate:  nowTime.Format(LayoutDate2),
+		EndDate:    nowTime.Format(LayoutDate2),
+		IsShow:     pasturePb.IsShow_Ok,
+		PastureId:  pastureId,
+		NoticeKind: noticeKind,
+		KnownUsers: "",
+	}
+}
+
+type DataNoticeSlice []*DataNotice
+
+func (d DataNoticeSlice) ToPB(currentUserId int64) []*NoticeContent {
+	res := make([]*NoticeContent, 0)
+	for _, v := range d {
+		uIds := v.GetUserIds()
+		if len(uIds) > 0 && util.InArray(currentUserId, uIds) {
+			continue
+		}
+		res = append(res, &NoticeContent{
+			Id:         v.Id,
+			Title:      v.Title,
+			Content:    v.Content,
+			NoticeKind: v.NoticeKind,
+		})
+	}
+	return res
+}
+
+type DataNoticeResponse struct {
+	Code int32            `json:"code"`
+	Msg  string           `json:"msg"`
+	Data []*NoticeContent `json:"data"`
+}
+
+type NoticeContent struct {
+	Id         int64                     `json:"id"`
+	Title      string                    `json:"title"`
+	Content    string                    `json:"content"`
+	NoticeKind pasturePb.NoticeType_Kind `json:"noticeKind"`
+}

+ 252 - 0
model/data_waring.go

@@ -0,0 +1,252 @@
+package model
+
+import (
+	"time"
+
+	"gitee.com/xuyiping_admin/pkg/xerr"
+
+	pasturePb "gitee.com/xuyiping_admin/go_proto/proto/go/backend/cow"
+)
+
+const DefaultUserId = 0
+
+type DataWarning struct {
+	Id                int64                          `json:"id"`
+	PastureId         int64                          `json:"pastureId"`
+	UserId            int64                          `json:"userId"`
+	Kind              pasturePb.DataWarningType_Kind `json:"kind"`
+	KindName          string                         `json:"kindName"`
+	Name              string                         `json:"name"`
+	Description       string                         `json:"description"`
+	DataValue         string                         `json:"dataValue"`
+	DataUpdateAt      int64                          `json:"dataUpdateAt"`
+	ConditionUpdateAt int64                          `json:"conditionUpdateAt"`
+	IsShow            pasturePb.IsShow_Kind          `json:"isShow"`
+	CreatedAt         int64                          `json:"createdAt"`
+	UpdatedAt         int64                          `json:"updatedAt"`
+}
+
+func (d *DataWarning) TableName() string {
+	return "data_warning"
+}
+
+func DataWarningInitData(pastureId int64, dataWarningList []*pasturePb.ConfigOptionsList) []*DataWarning {
+	res := make([]*DataWarning, 0)
+	for _, v := range dataWarningList {
+		res = append(res,
+			NewDataWarning(
+				pastureId,
+				DefaultUserId,
+				pasturePb.DataWarningType_Kind(v.Value),
+				pasturePb.IsShow_Ok,
+				&DataWarning{
+					Name:        v.Label,
+					Description: v.Props,
+				}))
+	}
+	return res
+}
+
+func (d *DataWarning) GetWarningColumn() (headers map[string]string, headerSort []string, err error) {
+	switch d.Kind {
+	case pasturePb.DataWarningType_Sale_Standard:
+		headers = map[string]string{
+			"cowId":              "编号",
+			"earNumber":          "牛号",
+			"penName":            "栏舍",
+			"admissionAge":       "在群天数",
+			"lastWeightAtFormat": "最后称重时间",
+			"currentWeight":      "当前体重(kg)",
+			"avgDailyWeight":     "日增重(kg)",
+		}
+		headerSort = []string{
+			"cowId",
+			"earNumber",
+			"penName",
+			"admissionAge",
+			"lastWeightAtFormat",
+			"currentWeight",
+			"avgDailyWeight",
+		}
+	case pasturePb.DataWarningType_UnPregnant_Mating_Time:
+		headers = map[string]string{
+			"cowId":              "编号",
+			"earNumber":          "牛号",
+			"penName":            "栏舍",
+			"birthAtFormat":      "出生日期",
+			"cowTypeName":        "牛只类型",
+			"dayAge":             "日龄",
+			"breedStatusName":    "繁殖状态",
+			"lastMatingAtFormat": "最近配种时间",
+			"matingTimes":        "配次",
+			"abortionTimes":      "流产次数",
+		}
+		headerSort = []string{
+			"cowId",
+			"earNumber",
+			"penName",
+			"birthAtFormat",
+			"cowTypeName",
+			"dayAge",
+			"breedStatusName",
+			"lastMatingAtFormat",
+			"matingTimes",
+			"abortionTimes",
+		}
+	case pasturePb.DataWarningType_Abortion_Time:
+		headers = map[string]string{
+			"cowId":                "编号",
+			"earNumber":            "牛号",
+			"penName":              "栏舍",
+			"lact":                 "胎次",
+			"matingAge":            "配后天数",
+			"breedStatusName":      "繁殖状态",
+			"abortionTimes":        "流产次数",
+			"lastAbortionAtFormat": "流产时间",
+			"matingTimes":          "配次",
+		}
+		headerSort = []string{
+			"cowId",
+			"earNumber",
+			"penName",
+			"lact",
+			"breedStatusName",
+			"matingAge",
+			"abortionTimes",
+			"lastAbortionAtFormat",
+			"matingTimes",
+		}
+
+	case pasturePb.DataWarningType_Over_Age_UnPaired_Young:
+		headers = map[string]string{
+			"cowId":         "编号",
+			"earNumber":     "牛号",
+			"penName":       "栏舍",
+			"birthAtFormat": "出生日期",
+			"dayAge":        "日龄",
+		}
+		headerSort = []string{
+			"cowId",
+			"earNumber",
+			"penName",
+			"birthAtFormat",
+			"dayAge",
+		}
+	case pasturePb.DataWarningType_Over_Age_UnPregnant_Young:
+		headers = map[string]string{
+			"cowId":              "编号",
+			"earNumber":          "牛号",
+			"penName":            "栏舍",
+			"birthAtFormat":      "出生日期",
+			"dayAge":             "日龄",
+			"breedStatusName":    "繁殖状态",
+			"lastMatingAtFormat": "配种日期",
+			"matingTimes":        "配次",
+			"abortionTimes":      "流产次数",
+		}
+		headerSort = []string{
+			"cowId",
+			"earNumber",
+			"penName",
+			"birthAtFormat",
+			"dayAge",
+			"breedStatusName",
+			"lastMatingAtFormat",
+			"matingTimes",
+			"abortionTimes",
+		}
+	case pasturePb.DataWarningType_Over_Month_UnSale:
+		headers = map[string]string{
+			"cowId":              "编号",
+			"earNumber":          "牛号",
+			"penName":            "栏舍",
+			"admissionAtFormat":  "入场日期",
+			"admissionAge":       "在群天数",
+			"avgDailyWeight":     "平均日增重",
+			"lastWeightAtFormat": "上次称重日期",
+			"currentWeight":      "上次体重",
+			"cowKindName":        "品种",
+		}
+		headerSort = []string{
+			"cowId",
+			"earNumber",
+			"penName",
+			"admissionAtFormat",
+			"admissionAge",
+			"avgDailyWeight",
+			"lastWeightAtFormat",
+			"currentWeight",
+			"cowKindName",
+		}
+	default:
+		return nil, nil, xerr.Custom("暂不支持该预警数据")
+	}
+	return headers, headerSort, nil
+}
+
+func NewDataWarningList(pastureId, userId int64, req []*pasturePb.WarningDataSet, warningMap map[pasturePb.DataWarningType_Kind]*DataWarning) []*DataWarning {
+	res := make([]*DataWarning, 0)
+	isAdd := make(map[pasturePb.DataWarningType_Kind]bool)
+	for _, v := range req {
+		if _, ok := isAdd[v.Kind]; ok {
+			continue
+		}
+		isAdd[v.Kind] = true
+		defaultDataWarning := warningMap[v.Kind]
+		res = append(res, NewDataWarning(pastureId, userId, v.Kind, v.IsShow, defaultDataWarning))
+	}
+	return res
+}
+
+func NewDataWarning(pastureId, userId int64, Kind pasturePb.DataWarningType_Kind, isShow pasturePb.IsShow_Kind, defaultDataWarning *DataWarning) *DataWarning {
+	kindName := pasturePb.DataWarningType_Kind_name[int32(Kind)]
+	return &DataWarning{
+		PastureId:   pastureId,
+		UserId:      userId,
+		Kind:        Kind,
+		KindName:    kindName,
+		Name:        defaultDataWarning.Name,
+		Description: defaultDataWarning.Description,
+		IsShow:      isShow,
+	}
+}
+
+type DataWarningSlice []*DataWarning
+
+func (d DataWarningSlice) ToPB() []*pasturePb.WarningDataShow {
+	res := make([]*pasturePb.WarningDataShow, 0)
+	for _, warningData := range d {
+		if warningData.DataValue == "0" {
+			continue
+		}
+		dataUpdateTimeFormat := ""
+		if warningData.DataUpdateAt > 0 {
+			dataUpdateTimeFormat = time.Unix(warningData.DataUpdateAt, 0).Local().Format(LayoutTime)
+		}
+		res = append(res, &pasturePb.WarningDataShow{
+			Name:                 warningData.Name,
+			Number:               warningData.DataValue,
+			Describe:             warningData.Description,
+			DataUpdateTimeFormat: dataUpdateTimeFormat,
+			Kind:                 warningData.Kind,
+		})
+	}
+	return res
+}
+
+type WarningDataPopResponse struct {
+	Code int32       `json:"code"`
+	Msg  string      `json:"msg"`
+	Data *WarningPop `json:"data"`
+}
+
+type WarningPop struct {
+	Title      string                         `json:"title"`
+	Kind       pasturePb.DataWarningType_Kind `json:"kind"`
+	Headers    map[string]string              `json:"headers"`
+	HeaderSort []string                       `json:"headerSort"`
+	Total      int32                          `json:"total"`
+	Page       int32                          `json:"page"`
+	PageSize   int32                          `json:"pageSize"`
+	DataList   interface{}                    `json:"list"`
+}

+ 199 - 0
model/data_warning_items.go

@@ -0,0 +1,199 @@
+package model
+
+import (
+	"fmt"
+	"strconv"
+
+	pasturePb "gitee.com/xuyiping_admin/go_proto/proto/go/backend/cow"
+)
+
+const (
+	FieldName = "day_age"
+)
+
+type DataWarningItems struct {
+	Id          int64                 `json:"id"`
+	PastureId   int64                 `json:"pastureId"`
+	UserId      int64                 `json:"userId"`
+	WarningId   int64                 `json:"warningId"`
+	GroupId     int32                 `json:"groupId"`
+	FieldName   string                `json:"fieldName"`
+	FieldDesc   string                `json:"fieldDesc"`
+	Operator    string                `json:"operator"`
+	Value       string                `json:"value"`
+	IsCondition pasturePb.IsShow_Kind `json:"isCondition"`
+	IsShow      pasturePb.IsShow_Kind `json:"isShow"`
+	CreatedAt   int64                 `json:"createdAt"`
+	UpdatedAt   int64                 `json:"updatedAt"`
+}
+
+func (d *DataWarningItems) TableName() string {
+	return "data_warning_items"
+}
+
+var DefaultDataWarningItemsMap = map[pasturePb.DataWarningType_Kind][]*pasturePb.WarningDataSet{
+	pasturePb.DataWarningType_Sale_Standard: {
+		{
+			GroupId:   1,
+			FieldName: "admission_age",
+			FieldDesc: "在群天数",
+			Operator:  ">=",
+			Value:     "100",
+			IsShow:    pasturePb.IsShow_Ok,
+		},
+		{
+			GroupId:   1,
+			FieldName: "current_weight",
+			FieldDesc: "体重",
+			Operator:  ">=",
+			Value:     "700",
+			IsShow:    pasturePb.IsShow_Ok,
+		},
+	},
+	pasturePb.DataWarningType_UnPregnant_Mating_Time: {
+		{
+			GroupId:   1,
+			FieldName: "mating_times",
+			FieldDesc: "配次",
+			Operator:  ">",
+			Value:     "2",
+			IsShow:    pasturePb.IsShow_Ok,
+		},
+	},
+	pasturePb.DataWarningType_Abortion_Time: {
+		{
+			GroupId:   1,
+			FieldName: "abortion_times",
+			FieldDesc: "流产次数",
+			Operator:  ">",
+			Value:     "3",
+			IsShow:    pasturePb.IsShow_Ok,
+		},
+	},
+	pasturePb.DataWarningType_Over_Age_UnPaired_Young: {
+		{
+			GroupId:   1,
+			FieldName: "day_age",
+			FieldDesc: "月龄",
+			Operator:  ">=",
+			Value:     "150",
+			IsShow:    pasturePb.IsShow_Ok,
+		},
+	},
+	pasturePb.DataWarningType_Over_Age_UnPregnant_Young: {
+		{
+			GroupId:   1,
+			FieldName: "day_age",
+			FieldDesc: "月龄",
+			Operator:  ">=",
+			Value:     "660",
+			IsShow:    pasturePb.IsShow_Ok,
+		},
+		{
+			GroupId:   2,
+			FieldName: "cow_type",
+			FieldDesc: "牛只类型",
+			Operator:  "=",
+			Value:     fmt.Sprintf("%d", pasturePb.CowType_Fattening_Calf),
+			IsShow:    pasturePb.IsShow_Ok,
+		},
+		{
+			GroupId:   2,
+			FieldName: "cow_type",
+			FieldDesc: "牛只类型",
+			Operator:  "=",
+			Value:     fmt.Sprintf("%d", pasturePb.CowType_Reserve_Calf),
+			IsShow:    pasturePb.IsShow_Ok,
+		},
+		{
+			GroupId:   3,
+			FieldName: "is_pregnant",
+			FieldDesc: "是否怀孕",
+			Operator:  "=",
+			Value:     fmt.Sprintf("%d", pasturePb.IsShow_No),
+			IsShow:    pasturePb.IsShow_Ok,
+		},
+	},
+	pasturePb.DataWarningType_Over_Month_UnSale: {
+		{
+			GroupId:   1,
+			FieldName: "day_age",
+			FieldDesc: "月龄",
+			Operator:  ">=",
+			Value:     "540",
+			IsShow:    pasturePb.IsShow_Ok,
+		},
+		{
+			GroupId:   2,
+			FieldName: "purpose_kind",
+			FieldDesc: "用途",
+			Operator:  "=",
+			Value:     fmt.Sprintf("%d", pasturePb.Purpose_Fatten),
+			IsShow:    pasturePb.IsShow_Ok,
+		},
+	},
+}
+
+func DataWarningItemsInitData(pastureId int64, dataWarning *DataWarning) []*DataWarningItems {
+	dataWarningItems := make([]*DataWarningItems, 0)
+	for _, v := range DefaultDataWarningItemsMap[dataWarning.Kind] {
+		dataWarningItems = append(dataWarningItems, NewDataWarningItems(pastureId, DefaultUserId, dataWarning, v))
+	}
+	return dataWarningItems
+}
+
+func NewDataWarningItems(pastureId, userId int64, dataWarning *DataWarning, req *pasturePb.WarningDataSet) *DataWarningItems {
+	if req.FieldName == FieldName {
+		v1, _ := strconv.ParseInt(req.Value, 10, 64)
+		req.Value = fmt.Sprintf("%d", v1*30)
+	}
+	return &DataWarningItems{
+		PastureId: pastureId,
+		UserId:    userId,
+		WarningId: dataWarning.Id,
+		GroupId:   req.GroupId,
+		FieldName: req.FieldName,
+		FieldDesc: req.FieldDesc,
+		Operator:  req.Operator,
+		Value:     req.Value,
+		IsShow:    req.IsShow,
+	}
+}
+
+type DataWarningItemsSlice []*DataWarningItems
+
+func (d DataWarningItemsSlice) ToPB(dataWarning []*DataWarning) []*pasturePb.WarningDataSet {
+	res := make([]*pasturePb.WarningDataSet, 0)
+	for _, v := range d {
+		name := ""
+		kind := pasturePb.DataWarningType_Invalid
+		for _, w := range dataWarning {
+			if w.Id == v.WarningId {
+				name = w.Name
+				kind = w.Kind
+			}
+		}
+
+		if name == "" || v.Id <= 0 || v.IsCondition == pasturePb.IsShow_Ok {
+			continue
+		}
+
+		if v.FieldName == FieldName {
+			v1, _ := strconv.ParseInt(v.Value, 10, 64)
+			v.Value = fmt.Sprintf("%d", v1/30)
+		}
+		res = append(res, &pasturePb.WarningDataSet{
+			Id:        int32(v.Id),
+			WarningId: int32(v.WarningId),
+			GroupId:   v.GroupId,
+			Name:      name,
+			Kind:      kind,
+			FieldDesc: v.FieldDesc,
+			FieldName: v.FieldName,
+			Operator:  v.Operator,
+			Value:     v.Value,
+			IsShow:    v.IsShow,
+		})
+	}
+	return res
+}

+ 14 - 12
model/disease.go

@@ -7,15 +7,17 @@ import (
 )
 
 type Disease struct {
-	Id          int64                 `json:"id"`
-	Name        string                `json:"name"`
-	Symptoms    string                `json:"symptoms"`
-	DiseaseType int32                 `json:"disease_type"`
-	IsShow      pasturePb.IsShow_Kind `json:"is_show"`
-	Remarks     string                `json:"remarks"`
-	OperationId int64                 `json:"operation_id"`
-	CreatedAt   int64                 `json:"created_at"`
-	UpdatedAt   int64                 `json:"updated_at"`
+	Id              int64                 `json:"id"`
+	PastureId       int64                 `json:"pastureId"`
+	Name            string                `json:"name"`
+	Symptoms        string                `json:"symptoms"`
+	DiseaseType     int32                 `json:"diseaseType"`
+	DiseaseTypeName string                `json:"diseaseTypeName"`
+	IsShow          pasturePb.IsShow_Kind `json:"isShow"`
+	Remarks         string                `json:"remarks"`
+	OperationId     int64                 `json:"operationId"`
+	CreatedAt       int64                 `json:"createdAt"`
+	UpdatedAt       int64                 `json:"updatedAt"`
 }
 
 func (d *Disease) TableName() string {
@@ -59,9 +61,9 @@ func (d DiseaseSlice) ToPB(user []*SystemUser, diseaseTypeList []*ConfigDiseaseT
 	return res
 }
 
-func (c DiseaseSlice) ToPB2() []*pasturePb.ConfigOptionsList {
-	res := make([]*pasturePb.ConfigOptionsList, len(c))
-	for i, d := range c {
+func (d DiseaseSlice) ToPB2() []*pasturePb.ConfigOptionsList {
+	res := make([]*pasturePb.ConfigOptionsList, len(d))
+	for i, d := range d {
 		res[i] = &pasturePb.ConfigOptionsList{
 			Value:    int32(d.Id),
 			Label:    d.Name,

+ 31 - 24
model/drugs.go

@@ -4,16 +4,20 @@ import pasturePb "gitee.com/xuyiping_admin/go_proto/proto/go/backend/cow"
 
 type Drugs struct {
 	Id              int64                       `json:"id"`
+	PastureId       int64                       `json:"pastureId"`
 	Name            string                      `json:"name"`
-	CategoryId      pasturePb.DrugCategory_Kind `json:"category_id"`
+	CategoryId      pasturePb.DrugCategory_Kind `json:"categoryId"`
+	CategoryName    string                      `json:"categoryName"`
 	Producer        string                      `json:"producer"`
-	BatchNumber     string                      `json:"batch_number"`
-	ProductionAt    int64                       `json:"production_at"`
-	ExpirationAt    int64                       `json:"expiration_at"`
+	BatchNumber     string                      `json:"batchNumber"`
+	ProductionAt    int64                       `json:"productionAt"`
+	ExpirationAt    int64                       `json:"expirationAt"`
 	Unit            pasturePb.Unit_Kind         `json:"unit"`
+	UnitName        string                      `json:"unitName"`
 	Specs           string                      `json:"specs"`
 	Inventory       int32                       `json:"inventory"`
 	UsageMethod     pasturePb.DrugUsage_Kind    `json:"usage_method"`
+	UsageMethodName string                      `json:"usageMethodName"`
 	Price           int32                       `json:"price"`
 	MilkExpiredDays int32                       `json:"milk_expired_days"`
 	MeatExpiredDays int32                       `json:"meat_expired_days"`
@@ -28,46 +32,49 @@ func (d *Drugs) TableName() string {
 	return "drugs"
 }
 
-func NewDrugs(req *pasturePb.SearchDrugsList, currentUser *SystemUser) *Drugs {
+func NewDrugs(pastureId int64, req *pasturePb.SearchDrugsList, currentUser *SystemUser) *Drugs {
 	return &Drugs{
-		Name:          req.Name,
-		CategoryId:    req.CategoryId,
-		Producer:      req.Producer,
-		BatchNumber:   req.BatchNumber,
-		ProductionAt:  int64(req.ProductionAt),
-		ExpirationAt:  int64(req.ExpirationAt),
-		Unit:          req.Unit,
-		Specs:         req.Specs,
-		Inventory:     req.Inventory,
-		UsageMethod:   req.Usage,
-		Price:         int32(req.Price * 100),
-		Remarks:       req.Remarks,
-		OperationId:   int32(currentUser.Id),
-		OperationName: currentUser.Name,
+		PastureId:       pastureId,
+		Name:            req.Name,
+		CategoryId:      req.CategoryId,
+		CategoryName:    req.CategoryName,
+		Producer:        req.Producer,
+		BatchNumber:     req.BatchNumber,
+		ProductionAt:    int64(req.ProductionAt),
+		ExpirationAt:    int64(req.ExpirationAt),
+		Unit:            req.Unit,
+		UnitName:        req.UnitName,
+		Specs:           req.Specs,
+		Inventory:       req.Inventory,
+		UsageMethod:     req.Usage,
+		UsageMethodName: req.UsageName,
+		Price:           int32(req.Price * 100),
+		Remarks:         req.Remarks,
+		OperationId:     int32(currentUser.Id),
+		OperationName:   currentUser.Name,
 	}
 }
 
 type DrugsSlice []*Drugs
 
-func (d DrugsSlice) ToPB(drugsCategoryMap map[pasturePb.DrugCategory_Kind]string,
-	unitMap map[pasturePb.Unit_Kind]string, drugUsageMap map[pasturePb.DrugUsage_Kind]string) []*pasturePb.SearchDrugsList {
+func (d DrugsSlice) ToPB() []*pasturePb.SearchDrugsList {
 	res := make([]*pasturePb.SearchDrugsList, len(d))
 	for i, v := range d {
 		res[i] = &pasturePb.SearchDrugsList{
 			Id:              int32(v.Id),
 			Name:            v.Name,
 			CategoryId:      v.CategoryId,
-			CategoryName:    drugsCategoryMap[v.CategoryId],
+			CategoryName:    v.CategoryName,
 			Producer:        v.Producer,
 			BatchNumber:     v.BatchNumber,
 			ProductionAt:    int32(v.ProductionAt),
 			ExpirationAt:    int32(v.ExpirationAt),
 			Unit:            v.Unit,
-			UnitName:        unitMap[v.Unit],
+			UnitName:        v.UnitName,
 			Specs:           v.Specs,
 			Inventory:       v.Inventory,
 			Usage:           v.UsageMethod,
-			UsageName:       drugUsageMap[v.UsageMethod],
+			UsageName:       v.UsageMethodName,
 			Price:           float32(v.Price) / 100,
 			MeatExpiredDays: v.MeatExpiredDays,
 			MilkExpiredDays: v.MilkExpiredDays,

+ 84 - 0
model/event_abortion.go

@@ -0,0 +1,84 @@
+package model
+
+import (
+	"strings"
+
+	pasturePb "gitee.com/xuyiping_admin/go_proto/proto/go/backend/cow"
+)
+
+type EventAbortion struct {
+	Id                  int64                          `json:"id"`
+	PastureId           int64                          `json:"pastureId"`
+	CowId               int64                          `json:"cowId"`
+	EarNumber           string                         `json:"earNumber"`
+	Lact                int32                          `json:"lact"`
+	CowType             pasturePb.CowType_Kind         `json:"cowType"`
+	DayAge              int32                          `json:"dayAge"`
+	PregnantAge         int32                          `json:"pregnantAge"`
+	AbortionAt          int64                          `json:"abortionAt"`
+	IsAfterbirth        pasturePb.IsShow_Kind          `json:"isAfterbirth"`
+	IsLact              pasturePb.IsShow_Kind          `json:"isLact"`
+	Photos              string                         `json:"photos"`
+	AbortionReasons     pasturePb.AbortionReasons_Kind `json:"abortionReasons"`
+	AbortionReasonsName string                         `json:"abortionReasonsName"`
+	Remarks             string                         `json:"remarks"`
+	IsAppend            pasturePb.IsShow_Kind          `json:"isAppend"`
+	OperationId         int64                          `json:"operationId"`
+	OperationName       string                         `json:"operationName"`
+	CreatedAt           int64                          `json:"createdAt"`
+	UpdatedAt           int64                          `json:"updatedAt"`
+}
+
+func (e *EventAbortion) TableName() string {
+	return "event_abortion"
+}
+
+func NewEventAbortion(pastureId int64, cow *Cow, item *pasturePb.EventAbortionItem, isAppend pasturePb.IsShow_Kind) *EventAbortion {
+	return &EventAbortion{
+		PastureId:           pastureId,
+		CowId:               cow.Id,
+		EarNumber:           cow.EarNumber,
+		Lact:                cow.Lact,
+		CowType:             cow.CowType,
+		PregnantAge:         cow.GetDaysPregnant(),
+		DayAge:              cow.GetEventDayAge(int64(item.AbortionAt)),
+		AbortionAt:          int64(item.AbortionAt),
+		IsAfterbirth:        item.IsAfterbirth,
+		IsLact:              item.IsLact,
+		Photos:              strings.Join(item.Photos, ","),
+		AbortionReasons:     item.AbortionReasons,
+		AbortionReasonsName: item.AbortionReasonsName,
+		Remarks:             item.Remarks,
+		IsAppend:            isAppend,
+		OperationId:         int64(item.OperationId),
+		OperationName:       item.OperationName,
+	}
+}
+
+type AbortionSlice []*EventAbortion
+
+func (a AbortionSlice) ToPB() []*pasturePb.EventAbortionItem {
+	res := make([]*pasturePb.EventAbortionItem, len(a))
+	for i, v := range a {
+		res[i] = &pasturePb.EventAbortionItem{
+			Id:                  int32(v.Id),
+			CowId:               int32(v.CowId),
+			EarNumber:           v.EarNumber,
+			DayAge:              v.DayAge,
+			Lact:                v.Lact,
+			AbortionAt:          int32(v.AbortionAt),
+			IsAfterbirth:        v.IsAfterbirth,
+			IsLact:              v.IsLact,
+			Photos:              strings.Split(v.Photos, ","),
+			AbortionReasons:     v.AbortionReasons,
+			AbortionReasonsName: v.AbortionReasonsName,
+			PregnancyAge:        v.PregnantAge,
+			Remarks:             v.Remarks,
+			OperationId:         int32(v.OperationId),
+			OperationName:       v.OperationName,
+			CreatedAt:           int32(v.CreatedAt),
+			UpdatedAt:           int32(v.UpdatedAt),
+		}
+	}
+	return res
+}

+ 17 - 11
model/event_body_score.go

@@ -4,25 +4,29 @@ import pasturePb "gitee.com/xuyiping_admin/go_proto/proto/go/backend/cow"
 
 type EventBodyScore struct {
 	Id            int64  `json:"id"`
-	CowId         int64  `json:"cow_id"`
-	EarNumber     string `json:"ear_number"`
+	PastureId     int64  `json:"pastureId"`
+	CowId         int64  `json:"cowId"`
+	EarNumber     string `json:"earNumber"`
 	Score         int32  `json:"score"`
 	Lact          int32  `json:"lact"`
-	DayAge        int32  `json:"day_age"`
-	ScoreAt       int64  `json:"score_at"`
+	DayAge        int32  `json:"dayAge"`
+	ScoreAt       int64  `json:"scoreAt"`
 	Remarks       string `json:"remarks"`
-	StaffMemberId int64  `json:"staff_member_id"`
-	OperationId   int64  `json:"operation_id"`
-	CreatedAt     int64  `json:"created_at"`
-	UpdatedAt     int64  `json:"updated_at"`
+	MessageId     int64  `json:"messageId"`
+	MessageName   string `json:"messageName"`
+	OperationId   int64  `json:"operationId"`
+	OperationName string `json:"operationName"`
+	CreatedAt     int64  `json:"createdAt"`
+	UpdatedAt     int64  `json:"updatedAt"`
 }
 
 func (e *EventBodyScore) TableName() string {
 	return "event_body_score"
 }
 
-func NewEventBodyScore(cow *Cow, operationId int64, req *pasturePb.BodyScoreEventRequest) *EventBodyScore {
+func NewEventBodyScore(cow *Cow, pastureId int64, currentUser *SystemUser, req *pasturePb.BodyScoreEventRequest) *EventBodyScore {
 	return &EventBodyScore{
+		PastureId:     pastureId,
 		CowId:         cow.Id,
 		EarNumber:     cow.EarNumber,
 		Score:         req.Score,
@@ -30,7 +34,9 @@ func NewEventBodyScore(cow *Cow, operationId int64, req *pasturePb.BodyScoreEven
 		DayAge:        cow.GetDayAge(),
 		ScoreAt:       int64(req.ScoreAt),
 		Remarks:       req.Remarks,
-		StaffMemberId: int64(req.StaffMemberId),
-		OperationId:   operationId,
+		MessageId:     currentUser.Id,
+		MessageName:   currentUser.Name,
+		OperationId:   int64(req.OperationId),
+		OperationName: req.OperationName,
 	}
 }

+ 122 - 48
model/event_calving.go

@@ -1,46 +1,86 @@
 package model
 
-import pasturePb "gitee.com/xuyiping_admin/go_proto/proto/go/backend/cow"
+import (
+	"kpt-pasture/util"
+
+	pasturePb "gitee.com/xuyiping_admin/go_proto/proto/go/backend/cow"
+)
 
 type EventCalving struct {
-	Id             int64                         `json:"int_64"`
-	CowId          int64                         `json:"cow_id"`
-	EarNumber      string                        `json:"ear_number"`
-	Lact           int32                         `json:"lact"`
-	DayAge         int32                         `json:"day_age"`
-	PenId          int32                         `json:"pen_id"`
-	CalvingAt      int64                         `json:"calving_at"`
-	ChildNumber    int8                          `json:"child_number"`
-	BullId         int64                         `json:"bull_id"`
-	DaysPregnant   int32                         `json:"days_pregnant"`
-	CalvingLevel   pasturePb.CalvingLevel_Kind   `json:"calving_level"`
-	StaffMemberId  int64                         `json:"staff_member_id"`
-	DystociaReason pasturePb.DystociaReason_Kind `json:"dystocia_reason"`
-	Remarks        string                        `json:"remarks"`
-	CreatedAt      int64                         `json:"created_at"`
-	UpdatedAt      int64                         `json:"updated_at"`
+	Id                   int64                         `json:"id"`
+	PastureId            int64                         `json:"pastureId"`
+	CowId                int64                         `json:"cowId"`
+	CowType              pasturePb.CowType_Kind        `json:"cowType"`
+	CowKind              pasturePb.CowKind_Kind        `json:"cowKind"`
+	EarNumber            string                        `json:"earNumber"`
+	Lact                 int32                         `json:"lact"`
+	DayAge               int32                         `json:"dayAge"`
+	PlanDay              int64                         `json:"planDay"`
+	RealityDay           int64                         `json:"realityDay"`
+	EndDay               int64                         `json:"endDay"`
+	Status               pasturePb.IsShow_Kind         `json:"status"`
+	PenId                int32                         `json:"penId"`
+	ChildNumber          int32                         `json:"childNumber"`
+	BullNumber           string                        `json:"bullNumber"`
+	PregnancyAge         int32                         `json:"pregnancyAge"`
+	CalvingLevel         pasturePb.CalvingLevel_Kind   `json:"calvingLevel"`
+	OperationId          int64                         `json:"operationId"`
+	OperationName        string                        `json:"operationName"`
+	MessageId            int64                         `json:"messageId"`
+	MessageName          string                        `json:"messageName"`
+	DystociaReason       pasturePb.DystociaReason_Kind `json:"dystociaReason"`
+	IsInducingChildbirth pasturePb.IsShow_Kind         `json:"isInducingChildbirth"`
+	Remarks              string                        `json:"remarks"`
+	CreatedAt            int64                         `json:"created_at"`
+	UpdatedAt            int64                         `json:"updated_at"`
 }
 
 func (e *EventCalving) TableName() string {
 	return "event_calving"
 }
 
-func NewEventCalving(cow *Cow, req *pasturePb.EventCalving) *EventCalving {
+func (e *EventCalving) EventUpdate(operationUser, currentUser *SystemUser, req *pasturePb.EventCalving, cow *Cow) {
+	if operationUser != nil {
+		e.OperationId = operationUser.Id
+		e.OperationName = operationUser.Name
+	}
+	e.RealityDay = int64(req.CalvingAt)
+	e.DayAge = cow.GetEventDayAge(int64(req.CalvingAt))
+	e.PregnancyAge = cow.GetDaysPregnant()
+	e.CalvingLevel = req.CalvingLevel
+	e.BullNumber = cow.LastBullNumber
+	e.ChildNumber = req.ChildNumber
+	e.Status = pasturePb.IsShow_Ok
+	e.PenId = cow.PenId
+	e.IsInducingChildbirth = req.IsInducingChildbirth
+	e.Remarks = req.Remarks
+	e.DystociaReason = req.DystociaReason
+	e.MessageId = currentUser.Id
+	e.MessageName = currentUser.Name
+}
+
+func NewEventCalving(pastureId int64, cow *Cow, startDay, endDay string) *EventCalving {
 	return &EventCalving{
-		CowId:          cow.Id,
-		EarNumber:      cow.EarNumber,
-		Lact:           cow.Lact,
-		DayAge:         cow.GetDayAge(),
-		PenId:          int32(cow.PenId),
-		CalvingAt:      int64(req.CalvingAt),
-		BullId:         cow.LastBullId,
-		CalvingLevel:   req.CalvingLevel,
-		DystociaReason: req.DystociaReason,
-		DaysPregnant:   cow.GetDaysPregnant(),
-		ChildNumber:    int8(req.ChildNumber),
-		Remarks:        req.Remarks,
-		StaffMemberId:  int64(req.StaffMemberId),
+		PastureId:  pastureId,
+		CowId:      cow.Id,
+		Lact:       cow.Lact,
+		CowKind:    cow.CowKind,
+		CowType:    cow.CowType,
+		EarNumber:  cow.EarNumber,
+		PenId:      cow.PenId,
+		BullNumber: cow.LastBullNumber,
+		Status:     pasturePb.IsShow_No,
+		PlanDay:    util.TimeParseLocalUnix(startDay),
+		EndDay:     util.TimeParseLocalEndUnix(endDay),
+	}
+}
+
+func NewEventCalvingList(pastureId int64, cowList []*Cow, startDay, endDay string) []*EventCalving {
+	eventCalvingList := make([]*EventCalving, 0)
+	for _, cow := range cowList {
+		eventCalvingList = append(eventCalvingList, NewEventCalving(pastureId, cow, startDay, endDay))
 	}
+	return eventCalvingList
 }
 
 type EventCalvingList struct {
@@ -62,7 +102,7 @@ func (e EventCalvingListSlice) ToPB(req []*CalvingCalf) []*pasturePb.LavingList
 			CalfItemList = append(CalfItemList, &pasturePb.CalfItem{
 				EarNumber:  v.EarNumber,
 				Sex:        v.Sex,
-				Weight:     float32(v.BrithWeight) / 1000,
+				Weight:     float32(v.BirthWeight) / 1000,
 				IsAdoption: v.IsAdoption,
 				IsLive:     v.IsLive,
 				CreatedAt:  int32(v.CreatedAt),
@@ -74,23 +114,57 @@ func (e EventCalvingListSlice) ToPB(req []*CalvingCalf) []*pasturePb.LavingList
 			})
 		}
 		list = append(list, &pasturePb.LavingList{
-			Id:              int32(item.Id),
-			CowId:           int32(item.CowId),
-			Lact:            item.Lact,
-			PenId:           item.PenId,
-			PenName:         item.PenName,
-			CalvingAt:       int32(item.CalvingAt),
-			ChildNumber:     int32(item.ChildNumber),
-			DaysPregnant:    item.DaysPregnant,
-			StaffMemberId:   int32(item.StaffMemberId),
-			StaffMemberName: item.StaffMemberName,
-			CalvingLevel:    item.CalvingLevel,
-			Remarks:         item.Remarks,
-			CreatedAt:       int32(item.CreatedAt),
-			UpdatedAt:       int32(item.UpdatedAt),
-			DystociaReason:  item.DystociaReason,
-			CalfItemList:    CalfItemList,
+			Id:             int32(item.Id),
+			CowId:          int32(item.CowId),
+			Lact:           item.Lact,
+			PenId:          item.PenId,
+			PenName:        item.PenName,
+			CalvingAt:      int32(item.PlanDay),
+			ChildNumber:    int32(item.ChildNumber),
+			DaysPregnant:   int32(item.PregnancyAge),
+			OperationId:    int32(item.OperationId),
+			OperationName:  item.OperationName,
+			CalvingLevel:   item.CalvingLevel,
+			Remarks:        item.Remarks,
+			CreatedAt:      int32(item.CreatedAt),
+			UpdatedAt:      int32(item.UpdatedAt),
+			DystociaReason: item.DystociaReason,
+			CalfItemList:   CalfItemList,
 		})
 	}
 	return list
 }
+
+type EventCalvingSlice []*EventCalving
+
+func (e EventCalvingSlice) ToPB() []*pasturePb.CalvingReportTable {
+	res := make([]*pasturePb.CalvingReportTable, len(e))
+	for i, v := range e {
+		res[i] = &pasturePb.CalvingReportTable{
+			StatisticMethod:     v.Remarks,
+			Twins:               0,
+			TwinsRate:           0,
+			Bulls:               0,
+			BullsRate:           0,
+			Cows:                0,
+			CowsRate:            0,
+			SurviveCount:        0,
+			SurviveRate:         0,
+			DieCount:            0,
+			DieRate:             0,
+			BullsDieCount:       0,
+			BullsDieRate:        0,
+			CowsDieCount:        0,
+			CowsDieRate:         0,
+			AdoptCount:          0,
+			AdoptRate:           0,
+			PrematureLaborCount: 0,
+			PrematureLaborRate:  0,
+			LateLaborCount:      0,
+			LateLaborRate:       0,
+			NormalLaborCount:    0,
+		}
+	}
+
+	return res
+}

+ 158 - 0
model/event_cow_disease.go

@@ -0,0 +1,158 @@
+package model
+
+import (
+	"time"
+
+	pasturePb "gitee.com/xuyiping_admin/go_proto/proto/go/backend/cow"
+)
+
+type EventCowDisease struct {
+	Id                    int64                            `json:"id"`
+	PastureId             int64                            `json:"pastureId"`
+	CowId                 int64                            `json:"cowId"`
+	EarNumber             string                           `json:"earNumber"`
+	CowType               pasturePb.CowType_Kind           `json:"cowType"`
+	Lact                  int32                            `json:"lact"`
+	DayAge                int32                            `json:"dayAge"`
+	PenId                 int32                            `json:"penId"`
+	PenName               string                           `json:"penName"`
+	DiseaseId             int32                            `json:"diseaseId"`
+	DiseaseName           string                           `json:"diseaseName"`
+	DiseaseType           int32                            `json:"diseaseType"`
+	DiseaseTypeName       string                           `json:"diseaseTypeName"`
+	DiagnoseId            int32                            `json:"diagnoseId"`
+	DiagnoseName          string                           `json:"diagnoseName"`
+	HealthStatus          pasturePb.HealthStatus_Kind      `json:"healthStatus"`
+	DiagnosedResult       pasturePb.IsShow_Kind            `json:"diagnosedResult"`
+	DiagnosedAt           int64                            `json:"diagnosedAt"`
+	DiseaseAt             int64                            `json:"diseaseAt"`
+	FirstTreatmentAt      int64                            `json:"firstTreatmentAt"`
+	LastTreatmentAt       int64                            `json:"lastTreatmentAt"`
+	LastPrescriptionName  string                           `json:"LastPrescriptionName"`
+	Temperature           int32                            `json:"temperature"`
+	ExposeDiseaseType     pasturePb.ExposeDiseaseType_Kind `json:"exposeDiseaseType"`
+	Remarks               string                           `json:"remarks"`
+	OperationId           int32                            `json:"operationId"`
+	OperationName         string                           `json:"operationName"`
+	MessageId             int32                            `json:"messageId"`
+	MessageName           string                           `json:"messageName"`
+	DiagnoseOperationId   int32                            `json:"diagnoseOperationId"`
+	DiagnoseOperationName string                           `json:"diagnoseOperationName"`
+	CurableAt             int64                            `json:"curableAt"`
+	Source                string                           `json:"source"`
+	CreatedAt             int64                            `json:"createdAt"`
+	UpdatedAt             int64                            `json:"updatedAt"`
+}
+
+func (e *EventCowDisease) TableName() string {
+	return "event_cow_disease"
+}
+
+// EventUnDiseaseUpdate 诊断未生病
+func (e *EventCowDisease) EventUnDiseaseUpdate(operation *SystemUser, remarks string) {
+	e.DiagnosedResult = pasturePb.IsShow_No
+	e.DiagnoseOperationId = int32(operation.Id)
+	e.DiagnoseOperationName = operation.Name
+	e.Remarks = remarks
+}
+
+// EventDiseaseUpdate 诊断有生病
+func (e *EventCowDisease) EventDiseaseUpdate(disease *Disease, operationUser *SystemUser, temp float32) {
+	e.HealthStatus = pasturePb.HealthStatus_Disease
+	e.DiagnosedResult = pasturePb.IsShow_Ok
+	e.DiagnoseId = int32(disease.Id)
+	e.DiagnoseName = disease.Name
+	e.Temperature = int32(temp * 100)
+	e.DiagnoseOperationId = int32(operationUser.Id)
+	e.DiagnoseOperationName = operationUser.Name
+	e.DiseaseAt = time.Now().Local().Unix()
+}
+
+// EventTreatmentUpdate 治疗更新
+func (e *EventCowDisease) EventTreatmentUpdate(healthStatus pasturePb.HealthStatus_Kind, treatmentAt int64, prescriptionName string) {
+	if e.FirstTreatmentAt <= 0 {
+		e.FirstTreatmentAt = treatmentAt
+	}
+	e.LastTreatmentAt = treatmentAt
+	e.LastPrescriptionName = prescriptionName
+	e.HealthStatus = healthStatus
+}
+
+// EventCurableUpdate 治愈
+func (e *EventCowDisease) EventCurableUpdate(cureAt int64) {
+	e.HealthStatus = pasturePb.HealthStatus_Curable
+	e.CurableAt = cureAt
+	e.DiagnosedResult = pasturePb.IsShow_Ok
+}
+
+func NewEventCowDisease(pastureId int64, cow *Cow, disease *Disease, req *pasturePb.EventCowDiseaseRequest, operation, currUser *SystemUser) *EventCowDisease {
+	return &EventCowDisease{
+		PastureId:         pastureId,
+		CowId:             cow.Id,
+		EarNumber:         cow.EarNumber,
+		CowType:           cow.CowType,
+		Lact:              cow.Lact,
+		DayAge:            cow.DayAge,
+		DiseaseId:         int32(disease.Id),
+		DiseaseName:       disease.Name,
+		DiseaseType:       disease.DiseaseType,
+		DiseaseTypeName:   disease.DiseaseTypeName,
+		PenId:             cow.PenId,
+		PenName:           cow.PenName,
+		HealthStatus:      pasturePb.HealthStatus_Health,
+		DiseaseAt:         int64(req.DiseaseAt),
+		Temperature:       int32(req.Temperature * 10),
+		OperationId:       int32(operation.Id),
+		OperationName:     operation.Name,
+		MessageId:         int32(currUser.Id),
+		MessageName:       currUser.Name,
+		Remarks:           req.Remarks,
+		DiagnosedResult:   pasturePb.IsShow_No,
+		Source:            SourceApp,
+		DiagnosedAt:       int64(req.DiseaseAt),
+		ExposeDiseaseType: req.ExposeDiseaseType,
+	}
+}
+
+type EventCowDiseaseSlice []*EventCowDisease
+
+func (e EventCowDiseaseSlice) ToPB(healthStatusMap map[pasturePb.HealthStatus_Kind]string) []*pasturePb.EventCowDisease {
+	res := make([]*pasturePb.EventCowDisease, len(e))
+	for i, v := range e {
+		lastTreatedTimeFormat := ""
+		if v.LastTreatmentAt > 0 {
+			lastTreatedTimeFormat = time.Unix(v.LastTreatmentAt, 0).Local().Format(LayoutDate2)
+		}
+		onsetDays := int32(0)
+		if v.FirstTreatmentAt > 0 {
+			firstTime := time.Unix(v.FirstTreatmentAt, 0).Local()
+			diff := time.Now().Local().Sub(firstTime)
+			onsetDays = int32(diff.Hours() / 24)
+		}
+		res[i] = &pasturePb.EventCowDisease{
+			Id:                    int32(v.Id),
+			CowId:                 int32(v.CowId),
+			EarNumber:             v.EarNumber,
+			Lact:                  v.Lact,
+			DayAge:                v.DayAge,
+			DiseaseId:             v.DiseaseId,
+			DiseaseName:           v.DiseaseName,
+			DiagnoseId:            v.DiagnoseId,
+			DiagnoseName:          v.DiagnoseName,
+			PenId:                 v.PenId,
+			PenName:               v.PenName,
+			HealthStatus:          v.HealthStatus,
+			HealthStatusName:      healthStatusMap[v.HealthStatus],
+			Temperature:           float32(v.HealthStatus / 10),
+			DiseaseAt:             int32(v.DiseaseAt),
+			Remarks:               v.Remarks,
+			OperationId:           v.OperationId,
+			OperationName:         v.OperationName,
+			LastPrescriptionName:  v.LastPrescriptionName,
+			LastTreatedTimeFormat: lastTreatedTimeFormat,
+			OnsetDays:             onsetDays,
+			ExposeDiseaseType:     v.ExposeDiseaseType,
+		}
+	}
+	return res
+}

+ 112 - 0
model/event_cow_log.go

@@ -0,0 +1,112 @@
+package model
+
+import (
+	"fmt"
+	"time"
+
+	pasturePb "gitee.com/xuyiping_admin/go_proto/proto/go/backend/cow"
+)
+
+const ShardTableNumber = 6
+
+type EventCowLog struct {
+	Id                int64                        `json:"id"`
+	PastureId         int64                        `json:"pastureId"`
+	CowId             int64                        `json:"cowId"`
+	DayAge            int32                        `json:"dayAge"`
+	Lact              int32                        `json:"lact"`
+	PenId             int32                        `json:"penId"`
+	PenName           string                       `json:"penName"`
+	CowType           pasturePb.CowType_Kind       `json:"cowType"`
+	CowTypeName       string                       `json:"cowTypeName"`
+	EventCategoryKind pasturePb.EventCategory_Kind `json:"eventCategoryKind"`
+	EventType         pasturePb.EventType_Kind     `json:"eventType"`
+	EventTypeName     string                       `json:"eventTypeName"`
+	EventDescription  string                       `json:"eventDescription"`
+	OperationId       int64                        `json:"operationId"`
+	OperationName     string                       `json:"operationName"`
+	EventAt           int64                        `json:"eventAt"`
+	Remarks           string                       `json:"remarks"`
+	CreatedAt         int64                        `json:"createdAt"`
+	UpdatedAt         int64                        `json:"updatedAt"`
+}
+
+func (e *EventCowLog) UnShardTableName() string {
+	return "event_cow_log"
+}
+
+// TableName 表名
+func (e *EventCowLog) TableName() string {
+	return fmt.Sprintf("%s_%04d", e.UnShardTableName(), e.CowId%ShardTableNumber)
+}
+
+type EventCowLogModel struct {
+	Cow               *Cow
+	EventType         pasturePb.EventType_Kind
+	OperationUser     *SystemUser
+	CurrentUser       *SystemUser
+	EventAt           int64
+	ExposeEstrusType  pasturePb.ExposeEstrusType_Kind
+	EventCategoryKind pasturePb.EventCategory_Kind
+	PenName           string
+	Remarks           string
+	CowTypeName       string
+	EventTypeName     string
+	Description       string
+}
+
+func NewEventCowLog(req *EventCowLogModel) *EventCowLog {
+	operationId := int64(0)
+	operationName := ""
+	if req.OperationUser != nil && req.OperationUser.Id > 0 {
+		operationId = req.OperationUser.Id
+		operationName = req.OperationUser.Name
+	}
+	return &EventCowLog{
+		PastureId:         req.Cow.PastureId,
+		CowId:             req.Cow.Id,
+		DayAge:            req.Cow.GetDayAge(),
+		Lact:              req.Cow.Lact,
+		PenId:             req.Cow.PenId,
+		PenName:           req.PenName,
+		CowType:           req.Cow.CowType,
+		CowTypeName:       req.CowTypeName,
+		EventCategoryKind: req.EventCategoryKind,
+		EventType:         req.EventType,
+		EventTypeName:     req.EventTypeName,
+		EventDescription:  req.Description,
+		OperationId:       operationId,
+		OperationName:     operationName,
+		EventAt:           req.EventAt,
+		Remarks:           req.Remarks,
+	}
+}
+
+type EventCowLogSlice []*EventCowLog
+
+func (e EventCowLogSlice) ToPB(eventCategoryMap map[pasturePb.EventCategory_Kind]string) []*pasturePb.CowEvent {
+	res := make([]*pasturePb.CowEvent, len(e))
+	for i, v := range e {
+		eventAtFormat := ""
+		if v.EventAt > 0 {
+			eventAtFormat = time.Unix(v.EventAt, 0).Local().Format(LayoutDate2)
+		}
+		res[i] = &pasturePb.CowEvent{
+			Id:                int32(v.Id),
+			PastureId:         int32(v.PastureId),
+			CowId:             int32(v.CowId),
+			Lact:              v.Lact,
+			DayAge:            v.DayAge,
+			PenName:           v.PenName,
+			CowTypeName:       v.CowTypeName,
+			EventTypeName:     v.EventTypeName,
+			EventDescription:  v.EventDescription,
+			OperationName:     v.OperationName,
+			Remarks:           v.Remarks,
+			EventAtFormat:     eventAtFormat,
+			EventCategoryKind: v.EventCategoryKind,
+			EventCategoryName: eventCategoryMap[v.EventCategoryKind],
+		}
+	}
+	return res
+}

+ 175 - 0
model/event_cow_same_time.go

@@ -0,0 +1,175 @@
+package model
+
+import (
+	"time"
+
+	pasturePb "gitee.com/xuyiping_admin/go_proto/proto/go/backend/cow"
+)
+
+type EventCowSameTime struct {
+	Id            int64                       `json:"id"`
+	PastureId     int64                       `json:"pastureId"`
+	CowId         int64                       `json:"cowId"`
+	EarNumber     string                      `json:"earNumber"`
+	CowType       pasturePb.CowType_Kind      `json:"cowType"`
+	PenId         int32                       `json:"penId"`
+	PenName       string                      `json:"penName"`
+	Lact          int32                       `json:"lact"`
+	SameTimeId    int64                       `json:"sameTimeId"`
+	SameTimeName  string                      `json:"sameTimeName"`
+	SameTimeType  pasturePb.SameTimeType_Kind `json:"sameTimeType"`
+	PlanDay       int64                       `json:"planDay"`
+	EndDay        int64                       `json:"endDay"`
+	RealityDay    int64                       `json:"realityDay"`
+	Status        pasturePb.IsShow_Kind       `json:"status"`
+	DrugsId       int64                       `json:"drugsId"`
+	Unit          pasturePb.Unit_Kind         `json:"unit"`
+	Usage         float32                     `json:"usage"`
+	Remarks       string                      `json:"remarks"`
+	OperationId   int64                       `json:"operationId"`
+	OperationName string                      `json:"operationName"`
+	MessageId     int64                       `json:"messageId"`
+	MessageName   string                      `json:"messageName"`
+	CreatedAt     int64                       `json:"createdAt"`
+	UpdatedAt     int64                       `json:"updatedAt"`
+}
+
+func (s *EventCowSameTime) TableName() string {
+	return "event_cow_same_time"
+}
+
+func (s *EventCowSameTime) EventUpdate(drugs *Drugs, usage float32, realityAt int64, remarks string, currentUser, operationUser *SystemUser) {
+	s.Status = pasturePb.IsShow_Ok
+	s.DrugsId = drugs.Id
+	s.Unit = drugs.Unit
+	s.Usage = usage
+	s.Remarks = remarks
+	s.OperationId = operationUser.Id
+	s.OperationName = operationUser.Name
+	s.MessageId = currentUser.Id
+	s.MessageName = currentUser.Name
+	s.RealityDay = realityAt
+}
+
+func NewEventCowSameTime(pastureId int64, cow *Cow, planTime int64, sameTime *SameTime, sameTimeType pasturePb.SameTimeType_Kind) *EventCowSameTime {
+	return &EventCowSameTime{
+		PastureId:    pastureId,
+		CowId:        cow.Id,
+		EarNumber:    cow.EarNumber,
+		Lact:         cow.Lact,
+		CowType:      cow.CowType,
+		PenId:        cow.PenId,
+		SameTimeId:   sameTime.Id,
+		SameTimeName: sameTime.Name,
+		SameTimeType: sameTimeType,
+		Status:       pasturePb.IsShow_No,
+		PlanDay:      planTime,
+		EndDay:       planTime + 86399,
+	}
+}
+
+func NewEventCowSameTimeList(pastureId int64, cowList []*Cow, sameTime *SameTime, planTime int64, sameTimeType pasturePb.SameTimeType_Kind) []*EventCowSameTime {
+	res := make([]*EventCowSameTime, len(cowList))
+	for i, cow := range cowList {
+		res[i] = NewEventCowSameTime(pastureId, cow, planTime, sameTime, sameTimeType)
+	}
+	return res
+}
+
+type SameTimeItemBody struct {
+	Id              int64                       `json:"id"`
+	CowId           int64                       `json:"cowId"`
+	EarNumber       string                      `json:"earNumber"`
+	BreedStatus     pasturePb.BreedStatus_Kind  `json:"breedStatus"`
+	BreedStatusName string                      `json:"breedStatusName"`
+	CowType         pasturePb.CowType_Kind      `json:"cowType"`
+	CowTypeName     string                      `json:"cowTypeName"`
+	PenId           int32                       `json:"penId"`
+	PenName         string                      `json:"penName"`
+	Lact            int32                       `json:"lact"`
+	CalvingAge      int32                       `json:"calvingAge"`
+	AbortionAge     int32                       `json:"abortionAge"`
+	DayAge          int32                       `json:"dayAge"`
+	Status          pasturePb.IsShow_Kind       `json:"status"`
+	SameTimeType    pasturePb.SameTimeType_Kind `json:"sameTimeType"`
+	LastCalvingAt   int64                       `json:"lastCalvingAt"`
+	LastAbortionAt  int64                       `json:"lastAbortionAt"`
+	MatingTimes     int32                       `json:"matingTimes"`
+	SameTimeName    string                      `json:"sameTimeName"`
+	PlanDay         int64                       `json:"planDay"`
+}
+
+type SameTimeBodySlice []*SameTimeItemBody
+
+func (s SameTimeBodySlice) ToPB(
+	breedStatusMap map[pasturePb.BreedStatus_Kind]string,
+	sameTimeTypeMap map[pasturePb.SameTimeType_Kind]string,
+) []*pasturePb.SameTimeItems {
+	res := make([]*pasturePb.SameTimeItems, len(s))
+	for i, v := range s {
+		calvingAtFormat, abortionAtFormat, playDayAtFormat := "", "", ""
+		if v.LastCalvingAt > 0 {
+			calvingAtFormat = time.Unix(v.LastCalvingAt, 0).Local().Format(LayoutDate2)
+		}
+
+		if v.LastAbortionAt > 0 {
+			abortionAtFormat = time.Unix(v.LastAbortionAt, 0).Local().Format(LayoutDate2)
+		}
+
+		if v.PlanDay > 0 {
+			playDayAtFormat = time.Unix(v.PlanDay, 0).Local().Format(LayoutDate2)
+		}
+
+		breedStatusName := ""
+		if bm, ok := breedStatusMap[v.BreedStatus]; ok {
+			breedStatusName = bm
+		}
+
+		sameTimeTypeName := ""
+		if sm, ok := sameTimeTypeMap[v.SameTimeType]; ok {
+			sameTimeTypeName = sm
+		}
+
+		res[i] = &pasturePb.SameTimeItems{
+			Id:               int32(v.Id),
+			CowId:            int32(v.CowId),
+			EarNumber:        v.EarNumber,
+			BreedStatus:      v.BreedStatus,
+			BreedStatusName:  breedStatusName,
+			PenName:          v.PenName,
+			PenId:            v.PenId,
+			Lact:             v.Lact,
+			CalvingAge:       v.CalvingAge,
+			AbortionAge:      v.AbortionAge,
+			DayAge:           v.DayAge,
+			Status:           v.Status,
+			SameTimeTypeName: sameTimeTypeName,
+			SameTimeType:     v.SameTimeType,
+			CalvingAtFormat:  calvingAtFormat,
+			AbortionAtFormat: abortionAtFormat,
+			MatingTimes:      v.MatingTimes,
+			SameTimeName:     v.SameTimeName,
+			PlanDay:          playDayAtFormat,
+		}
+	}
+	return res
+}
+
+type EventCowSameTimeSlice []*EventCowSameTime
+
+func (s EventCowSameTimeSlice) ToPB(sameTimeTypeMap map[pasturePb.SameTimeType_Kind]string) []*pasturePb.EventSameTime {
+	res := make([]*pasturePb.EventSameTime, len(s))
+	for i, v := range s {
+		res[i] = &pasturePb.EventSameTime{
+			CowId:            int32(v.CowId),
+			PenId:            v.PenId,
+			DrugsId:          int32(v.DrugsId),
+			Usage:            v.Usage,
+			Lact:             v.Lact,
+			SameTimeId:       int32(v.SameTimeId),
+			SameTimeType:     v.SameTimeType,
+			SameTimeTypeName: sameTimeTypeMap[v.SameTimeType],
+		}
+	}
+	return res
+}

+ 114 - 0
model/event_cow_treatment.go

@@ -0,0 +1,114 @@
+package model
+
+import (
+	"encoding/json"
+
+	"gitee.com/xuyiping_admin/pkg/logger/zaplog"
+	"go.uber.org/zap"
+
+	pasturePb "gitee.com/xuyiping_admin/go_proto/proto/go/backend/cow"
+)
+
+type EventCowTreatment struct {
+	Id                 int64                          `json:"id"`
+	PastureId          int64                          `json:"pastureId"`
+	CowId              int64                          `json:"cowId"`
+	CowDiseaseId       int64                          `json:"cowDiseaseId"`
+	DiseaseId          int64                          `json:"diseaseId"`
+	DiseaseName        string                         `json:"diseaseName"`
+	DiseaseType        int32                          `json:"diseaseType"`
+	DiseaseTypeName    string                         `json:"diseaseTypeName"`
+	PrescriptionId     int32                          `json:"prescriptionId"`
+	PrescriptionName   string                         `json:"prescriptionName"`
+	PrescriptionDetail string                         `json:"prescriptionDetail"`
+	TreatmentResult    pasturePb.TreatmentResult_Kind `json:"treatmentResult"`
+	OperationId        int64                          `json:"operationId"`
+	OperationName      string                         `json:"operationName"`
+	MessageId          int64                          `json:"messageId"`
+	MessageName        string                         `json:"messageName"`
+	Remarks            string                         `json:"remarks"`
+	TreatmentAt        int64                          `json:"treatmentAt"`
+	CreatedAt          int64                          `json:"createdAt"`
+	UpdatedAt          int64                          `json:"updatedAt"`
+}
+
+func (e *EventCowTreatment) TableName() string {
+	return "event_cow_treatment"
+}
+
+func NewEventCowTreatment(
+	pastureId int64,
+	prescription *Prescription,
+	req *pasturePb.CowTreatmentRequest,
+	diseaseTypeMap map[int32]string,
+	operation, currentUser *SystemUser,
+) *EventCowTreatment {
+	b, _ := json.Marshal(req.PrescriptionDetail)
+	return &EventCowTreatment{
+		PastureId:          pastureId,
+		CowId:              int64(req.CowId),
+		CowDiseaseId:       int64(req.Id),
+		DiseaseId:          int64(req.DiseaseId),
+		DiseaseName:        req.DiseaseName,
+		DiseaseType:        req.DiseaseType,
+		DiseaseTypeName:    diseaseTypeMap[req.DiseaseType],
+		PrescriptionId:     req.PrescriptionId,
+		PrescriptionName:   prescription.Name,
+		PrescriptionDetail: string(b),
+		TreatmentResult:    req.TreatmentResult,
+		OperationId:        operation.Id,
+		OperationName:      operation.Name,
+		MessageId:          currentUser.Id,
+		MessageName:        currentUser.Name,
+		Remarks:            req.Remarks,
+	}
+}
+
+func NewEventCowCurableTreatment(pastureId int64, currUser, operationUser *SystemUser, cowDisease *EventCowDisease, remarks string, curableAt int64) *EventCowTreatment {
+	return &EventCowTreatment{
+		PastureId:       pastureId,
+		CowId:           cowDisease.CowId,
+		CowDiseaseId:    cowDisease.Id,
+		DiseaseId:       int64(cowDisease.DiseaseId),
+		DiseaseName:     cowDisease.DiseaseName,
+		TreatmentResult: pasturePb.TreatmentResult_Curable,
+		OperationId:     operationUser.Id,
+		OperationName:   operationUser.Name,
+		Remarks:         remarks,
+		TreatmentAt:     curableAt,
+	}
+}
+
+type EventCowTreatmentSlice []*EventCowTreatment
+
+func (e EventCowTreatmentSlice) ToPB(eventCowDisease *EventCowDisease) []*pasturePb.EventCowTreatment {
+	res := make([]*pasturePb.EventCowTreatment, len(e))
+	for i, v := range e {
+		prescriptionDetail := make([]*pasturePb.PrescriptionDrugsList, 0)
+		if v.PrescriptionDetail != "" {
+			err := json.Unmarshal([]byte(v.PrescriptionDetail), &prescriptionDetail)
+			if err != nil {
+				zaplog.Error("EventCowTreatmentSlice", zap.Any("err", err))
+			}
+		}
+
+		res[i] = &pasturePb.EventCowTreatment{
+			CowId:              int32(v.CowId),
+			CowDiseaseId:       int32(v.CowDiseaseId),
+			PrescriptionId:     v.PrescriptionId,
+			PrescriptionName:   v.PrescriptionName,
+			PrescriptionDetail: prescriptionDetail,
+			TreatmentResult:    0,
+			OperationId:        int32(v.OperationId),
+			OperationName:      v.OperationName,
+			Remarks:            v.Remarks,
+			CreatedAt:          int32(v.CreatedAt),
+			UpdatedAt:          int32(v.UpdatedAt),
+			DiseaseId:          int32(v.DiseaseId),
+			Id:                 int32(v.Id),
+			DiseaseName:        eventCowDisease.DiseaseName,
+			DiseaseAt:          int32(eventCowDisease.DiseaseAt),
+		}
+	}
+	return res
+}

+ 86 - 0
model/event_death.go

@@ -0,0 +1,86 @@
+package model
+
+import pasturePb "gitee.com/xuyiping_admin/go_proto/proto/go/backend/cow"
+
+type EventDeath struct {
+	Id                   int64                           `json:"id"`
+	PastureId            int64                           `json:"pastureId"`
+	EarNumber            string                          `json:"earNumber"`
+	CowId                int64                           `json:"cowId"`
+	Lact                 int32                           `json:"lact"`
+	CowType              pasturePb.CowType_Kind          `json:"cowType"`
+	DayAge               int32                           `json:"dayAge"`
+	PregnancyAge         int32                           `json:"pregnancyAge"`
+	CalvingAge           int32                           `json:"calvingAge"`
+	AdmissionAge         int32                           `json:"admissionAge"`
+	DeathReasonKind      pasturePb.DeathReason_Kind      `json:"deathReasonKind"`
+	DeathReasonName      string                          `json:"deathReasonName"`
+	DeathDestinationKind pasturePb.DeathDestination_Kind `json:"deathDestinationKind"`
+	DeathDestinationName string                          `json:"deathDestinationName"`
+	DeathAt              int64                           `json:"deathAt"`
+	Remarks              string                          `json:"remarks"`
+	Weight               int32                           `json:"weight"`
+	OperationId          int64                           `json:"operationId"`
+	OperationName        string                          `json:"operationName"`
+	MessageId            int64                           `json:"messageId"`
+	MessageName          string                          `json:"messageName"`
+	CreatedAt            int64                           `json:"createdAt"`
+	UpdatedAt            int64                           `json:"updatedAt"`
+}
+
+func (e *EventDeath) TableName() string {
+	return "event_death"
+}
+
+func NewEventDeath(pastureId int64, cow *Cow, req *pasturePb.EventDeath, currentUser, operationUser *SystemUser) *EventDeath {
+	return &EventDeath{
+		PastureId:            pastureId,
+		CowId:                cow.Id,
+		EarNumber:            cow.EarNumber,
+		Lact:                 cow.Lact,
+		CowType:              cow.CowType,
+		DayAge:               cow.GetEventDayAge(int64(req.DeathAt)),
+		DeathAt:              int64(req.DeathAt),
+		DeathReasonKind:      req.DeathReasonKind,
+		DeathReasonName:      req.DeathReasonName,
+		Remarks:              req.Remarks,
+		Weight:               int32(req.Weight * 1000),
+		DeathDestinationKind: req.DeathDestinationKind,
+		DeathDestinationName: req.DeathDestinationName,
+		OperationId:          operationUser.Id,
+		OperationName:        operationUser.Name,
+		MessageId:            currentUser.Id,
+		MessageName:          currentUser.Name,
+	}
+}
+
+type EventDeathSlice []*EventDeath
+
+func (e EventDeathSlice) ToPB() []*pasturePb.EventDeath {
+	res := make([]*pasturePb.EventDeath, len(e))
+	for i, v := range e {
+		res[i] = &pasturePb.EventDeath{
+			Id:                   int32(v.Id),
+			EarNumber:            v.EarNumber,
+			DeathReasonKind:      v.DeathReasonKind,
+			DeathReasonName:      v.DeathReasonName,
+			DeathAt:              int32(v.DeathAt),
+			OperationId:          int32(v.OperationId),
+			OperationName:        v.OperationName,
+			Remarks:              v.Remarks,
+			Weight:               float32(v.Weight / 1000),
+			DeathDestinationKind: v.DeathDestinationKind,
+			DeathDestinationName: v.DeathDestinationName,
+			CreatedAt:            int32(v.CreatedAt),
+			UpdatedAt:            int32(v.UpdatedAt),
+		}
+	}
+	return res
+}
+
+type EventDeathModel struct {
+	Cow         *Cow
+	EventDeath  *EventDeath
+	NeckRing    *NeckRing
+	CalvingCalf *CalvingCalf
+}

+ 86 - 0
model/event_dry_milk.go

@@ -0,0 +1,86 @@
+package model
+
+import (
+	"kpt-pasture/util"
+
+	pasturePb "gitee.com/xuyiping_admin/go_proto/proto/go/backend/cow"
+)
+
+type EventDryMilk struct {
+	Id            int64                 `json:"id"`
+	PastureId     int64                 `json:"pastureId"`
+	CowId         int64                 `json:"cowId"`
+	EarNumber     string                `json:"earNumber"`
+	DayAge        int32                 `json:"dayAge"`
+	Lact          int32                 `json:"lact"`
+	PenId         int32                 `json:"penId"`
+	PenName       string                `json:"penName"`
+	PlanDay       int64                 `json:"planDay"`
+	RealityDay    int64                 `json:"realityDay"`
+	EndDay        int64                 `json:"endDay"`
+	Status        pasturePb.IsShow_Kind `json:"status"`
+	Remarks       string                `json:"remarks"`
+	OperationId   int64                 `json:"operationId"`
+	OperationName string                `json:"operationName"`
+	MessageId     int64                 `json:"messageId"`
+	MessageName   string                `json:"messageName"`
+	CreatedAt     int64                 `json:"createdAt"`
+	UpdatedAt     int64                 `json:"updatedAt"`
+}
+
+func (e *EventDryMilk) TableName() string {
+	return "event_dry_milk"
+}
+
+func (e *EventDryMilk) EventDryMilkUpdate(cow *Cow, dryMilkAt int64, pen *Pen, operation, message *SystemUser, remarks string) {
+	e.Lact = cow.Lact
+	e.RealityDay = dryMilkAt
+	e.Remarks = remarks
+	e.Status = pasturePb.IsShow_Ok
+	e.OperationId = operation.Id
+	e.OperationName = operation.Name
+	e.MessageId = message.Id
+	e.MessageName = message.Name
+	e.PenId = pen.Id
+	e.PenName = pen.Name
+	e.DayAge = cow.GetEventDayAge(dryMilkAt)
+}
+
+func NewEventDryMilk(pastureId int64, cow *Cow, startDay, endDay string) *EventDryMilk {
+	return &EventDryMilk{
+		PastureId: pastureId,
+		CowId:     cow.Id,
+		EarNumber: cow.EarNumber,
+		Lact:      cow.Lact,
+		PlanDay:   util.TimeParseLocalUnix(startDay),
+		EndDay:    util.TimeParseLocalUnix(endDay),
+		Status:    pasturePb.IsShow_No,
+	}
+}
+
+func NewEventDryMilkList(pastureId int64, cowList []*Cow, startDay, endDay string) []*EventDryMilk {
+	eventDryMilkList := make([]*EventDryMilk, 0)
+	for _, cow := range cowList {
+		eventDryMilkList = append(eventDryMilkList, NewEventDryMilk(pastureId, cow, startDay, endDay))
+	}
+	return eventDryMilkList
+}
+
+type EventDryMilkSlice []*EventDryMilk
+
+func (e EventDryMilkSlice) ToPB() []*pasturePb.EventMilkItem {
+	res := make([]*pasturePb.EventMilkItem, len(e))
+	for i, v := range e {
+		res[i] = &pasturePb.EventMilkItem{
+			Id:            int32(v.Id),
+			CowId:         int32(v.CowId),
+			EarNumber:     v.EarNumber,
+			Lact:          v.Lact,
+			PenName:       v.PenName,
+			DryMilkAt:     int32(v.RealityDay),
+			OperationName: v.OperationName,
+			Remarks:       v.Remarks,
+		}
+	}
+	return res
+}

+ 80 - 42
model/event_enter.go

@@ -9,32 +9,35 @@ import (
 
 type EventEnter struct {
 	Id               int64                      `json:"id"`
-	BatchNumber      string                     `json:"batch_number"`
-	EarNumber        string                     `json:"ear_number"`
-	CowId            int64                      `json:"cow_id"`
+	PastureId        int64                      `json:"pastureId"`
+	BatchNumber      string                     `json:"batchNumber"`
+	EarNumber        string                     `json:"earNumber"`
+	CowId            int64                      `json:"cowId"`
 	Sex              pasturePb.Genders_Kind     `json:"sex"`
-	BirthAt          int64                      `json:"birth_at"`
-	CowSourceId      pasturePb.CowSource_Kind   `json:"cow_source_id"`
-	OldEarNumber     string                     `json:"old_ear_number"`
-	CowType          pasturePb.CowType_Kind     `json:"cow_type"`
-	BreedStatus      pasturePb.BreedStatus_Kind `json:"breed_status"`
+	BirthAt          int64                      `json:"birthAt"`
+	CowSource        pasturePb.CowSource_Kind   `json:"cowSource"`
+	OldEarNumber     string                     `json:"oldEarNumber"`
+	CowType          pasturePb.CowType_Kind     `json:"cowType"`
+	BreedStatus      pasturePb.BreedStatus_Kind `json:"breedStatus"`
 	Lact             int32                      `json:"lact"`
-	DayAge           int32                      `json:"day_age"`
-	PenId            int32                      `json:"pen_id"`
-	CowKind          pasturePb.CowKind_Kind     `json:"cow_kind"`
-	FatherId         int64                      `json:"father_id"`
-	MotherId         int64                      `json:"mother_id"`
-	MatingAt         int64                      `json:"mating_at"`
-	PregnancyCheckAt int64                      `json:"pregnancy_check_at"`
-	DryMilkAt        int64                      `json:"dry_milk_at"`
-	WeaningAt        int64                      `json:"weaning_at"`
-	EstrusAt         int64                      `json:"estrus_at"`
-	EnterAt          int64                      `json:"enter_at"`
+	DayAge           int32                      `json:"dayAge"`
+	PenId            int32                      `json:"penId"`
+	CowKind          pasturePb.CowKind_Kind     `json:"cowKind"`
+	FatherNumber     string                     `json:"fatherNumber"`
+	MotherNumber     string                     `json:"motherNumber"`
+	MatingAt         int64                      `json:"matingAt"`
+	PregnancyCheckAt int64                      `json:"pregnancyCheckAt"`
+	DryMilkAt        int64                      `json:"dryMilkAt"`
+	WeaningAt        int64                      `json:"weaningAt"`
+	EstrusAt         int64                      `json:"estrusAt"`
+	EnterAt          int64                      `json:"enterAt"`
 	Remarks          string                     `json:"remarks"`
 	Weight           int64                      `json:"weight"`
 	Price            int64                      `json:"price"`
-	OperationId      int64                      `json:"operation_id"`
-	StaffMemberId    int64                      `json:"staff_member_id"`
+	OperationId      int64                      `json:"operationId"`
+	OperationName    string                     `json:"operationName"`
+	MessengerId      int64                      `json:"messengerId"`
+	MessengerName    string                     `json:"messengerName"`
 	CreatedAt        int64                      `json:"created_at"`
 	UpdatedAt        int64                      `json:"updated_at"`
 }
@@ -42,21 +45,22 @@ type EventEnter struct {
 func (e *EventEnter) TableName() string {
 	return "event_enter"
 }
-func NewEventEnter(cowId, operationId int64, req *pasturePb.EventEnterData) *EventEnter {
+func NewEventEnter(pastureId, cowId int64, req *pasturePb.EventEnterRequest) *EventEnter {
 	return &EventEnter{
+		PastureId:        pastureId,
 		EarNumber:        req.EarNumber,
 		CowId:            cowId,
 		Sex:              req.Sex,
 		BirthAt:          int64(req.BirthAt),
-		CowSourceId:      req.CowSourceId,
-		CowType:          req.CowTypeId,
-		BreedStatus:      req.BreedStatusId,
+		CowSource:        req.CowSource,
+		CowType:          req.CowType,
+		BreedStatus:      req.BreedStatus,
 		Lact:             req.Lact,
-		DayAge:           int32(math.Floor(float64(int32(time.Now().Unix())-req.BirthAt) / 86400)),
+		DayAge:           int32(math.Floor(float64(int32(time.Now().Local().Unix())-req.BirthAt) / 86400)),
 		PenId:            req.PenId,
-		CowKind:          req.CowKindId,
-		FatherId:         int64(req.FatherId),
-		MotherId:         int64(req.MotherId),
+		CowKind:          req.CowKind,
+		FatherNumber:     req.FatherNumber,
+		MotherNumber:     req.MotherNumber,
 		MatingAt:         int64(req.MatingAt),
 		PregnancyCheckAt: int64(req.PregnancyCheckAt),
 		DryMilkAt:        int64(req.DryMilkAt),
@@ -64,44 +68,78 @@ func NewEventEnter(cowId, operationId int64, req *pasturePb.EventEnterData) *Eve
 		EstrusAt:         int64(req.EstrusAt),
 		EnterAt:          int64(req.EnterAt),
 		Remarks:          req.Remarks,
-		Weight:           int64(req.Weight * 100),
+		Weight:           int64(req.Weight * 1000),
 		Price:            int64(req.Price * 100),
-		StaffMemberId:    int64(req.StaffMemberId),
-		OperationId:      operationId,
+		MessengerId:      int64(req.MessengerId),
+		MessengerName:    req.MessengerName,
+		OperationId:      int64(req.OperationId),
+		OperationName:    req.OperationName,
+		BatchNumber:      req.BatchNumber,
 	}
 }
 
 type EventEnterSlice []*EventEnter
 
-func (e EventEnterSlice) ToPB() []*pasturePb.EventEnterData {
-	res := make([]*pasturePb.EventEnterData, len(e))
+func (e EventEnterSlice) ToPB(
+	penMap map[int32]*Pen,
+	breedStatusMap map[pasturePb.BreedStatus_Kind]string,
+	cowSourceMap map[pasturePb.CowSource_Kind]string,
+	cowTypeMap map[pasturePb.CowType_Kind]string,
+	cowKindMap map[pasturePb.CowKind_Kind]string,
+) []*pasturePb.EventEnterRequest {
+	res := make([]*pasturePb.EventEnterRequest, len(e))
 	for i, d := range e {
-		res[i] = &pasturePb.EventEnterData{
+		penName := ""
+		if pen, ok := penMap[d.PenId]; ok {
+			penName = pen.Name
+		}
+		res[i] = &pasturePb.EventEnterRequest{
 			Id:               int32(d.Id),
 			EarNumber:        d.EarNumber,
 			CowId:            int32(d.CowId),
 			Sex:              d.Sex,
 			BirthAt:          int32(d.BirthAt),
-			CowSourceId:      d.CowSourceId,
-			CowTypeId:        d.CowType,
-			BreedStatusId:    d.BreedStatus,
+			CowSource:        d.CowSource,
+			CowSourceName:    cowSourceMap[d.CowSource],
+			CowType:          d.CowType,
+			CowTypeName:      cowTypeMap[d.CowType],
+			BreedStatus:      d.BreedStatus,
+			BreedStatusName:  breedStatusMap[d.BreedStatus],
 			Lact:             d.Lact,
 			PenId:            d.PenId,
-			CowKindId:        d.CowKind,
-			FatherId:         int32(d.FatherId),
-			MotherId:         int32(d.MotherId),
+			PenName:          penName,
+			CowKind:          d.CowKind,
+			CowKindName:      cowKindMap[d.CowKind],
+			FatherNumber:     d.FatherNumber,
+			MotherNumber:     d.MotherNumber,
 			MatingAt:         int32(d.MatingAt),
 			PregnancyCheckAt: int32(d.PregnancyCheckAt),
 			DryMilkAt:        int32(d.DryMilkAt),
 			WeaningAt:        int32(d.WeaningAt),
 			EstrusAt:         int32(d.EstrusAt),
 			EnterAt:          int32(d.EnterAt),
-			Weight:           float32(d.Weight) / 100,
+			Weight:           float32(d.Weight) / 1000,
 			Price:            float32(d.Price) / 100,
 			Remarks:          d.Remarks,
+			MessengerId:      int32(d.MessengerId),
+			MessengerName:    d.MessengerName,
+			OperationId:      int32(d.OperationId),
+			OperationName:    d.OperationName,
 			CreatedAt:        int32(d.CreatedAt),
 			UpdatedAt:        int32(d.UpdatedAt),
 		}
 	}
 	return res
 }
+
+type HistoryBatchNumberResponse struct {
+	Code int32    `json:"code"`
+	Msg  string   `json:"msg"`
+	Data []string `json:"data"`
+}
+
+type GenerateBatchNumberResponse struct {
+	Code int32  `json:"code"`
+	Msg  string `json:"msg"`
+	Data string `json:"data"`
+}

+ 70 - 42
model/event_estrus.go

@@ -1,68 +1,96 @@
 package model
 
-import pasturePb "gitee.com/xuyiping_admin/go_proto/proto/go/backend/cow"
+import (
+	pasturePb "gitee.com/xuyiping_admin/go_proto/proto/go/backend/cow"
+)
 
 type EventEstrus struct {
-	Id            int64  `json:"id"`
-	CowId         int64  `json:"cow_id"`
-	DayAge        int32  `json:"day_age"`
-	Lact          int8   `json:"lact"`
-	LactationDays int32  `json:"lactation_days"`
-	EstrusAt      int64  `json:"estrus_at"`
-	Remarks       string `json:"remarks"`
-	StallNumberId int64  `json:"stall_number_id"`
-	OperationId   int64  `json:"operation_id"`
-	CreatedAt     int64  `json:"created_at"`
-	UpdatedAt     int64  `json:"updated_at"`
+	Id                  int64                           `json:"id"`
+	PastureId           int64                           `json:"pastureId"`
+	CowId               int64                           `json:"cowId"`
+	NeckRingNumber      string                          `json:"neckRingNumber"`
+	EarNumber           string                          `json:"earNumber"`
+	Lact                int32                           `json:"lact"`
+	DayAge              int32                           `json:"dayAge"`
+	CalvingAge          int32                           `json:"calvingAge"`
+	ExposeEstrusType    pasturePb.ExposeEstrusType_Kind `json:"exposeEstrusType"`
+	LastEstrusDay       int64                           `json:"lastEstrusDay"`
+	PlanDay             int64                           `json:"planDay"`
+	RealityDay          int64                           `json:"realityDay"`
+	EndDay              int64                           `json:"endDay"`
+	IsMating            pasturePb.IsShow_Kind           `json:"isMating"`
+	UnMatingReasonsKind pasturePb.UnMatingReasons_Kind  `json:"unMatingReasonsKind"`
+	UnMatingReasonsName string                          `json:"unMatingReasonsName"`
+	Level               pasturePb.EstrusLevel_Kind      `json:"level"`
+	Remarks             string                          `json:"remarks"`
+	IsShow              pasturePb.IsShow_Kind           `json:"isShow"`
+	OperationId         int64                           `json:"operationId"`
+	OperationName       string                          `json:"operationName"`
+	MessageId           int64                           `json:"messageId"`
+	MessageName         string                          `json:"messageName"`
+	CreatedAt           int64                           `json:"createdAt"`
+	UpdatedAt           int64                           `json:"updatedAt"`
 }
 
 func (e *EventEstrus) TableName() string {
 	return "event_estrus"
 }
 
-func NewEventEstrus(cow *Cow, currentUser *SystemUser, req *pasturePb.EventEstrus) *EventEstrus {
+func NewEventEstrus(
+	pastureId int64,
+	cow *Cow,
+	exposeEstrusType pasturePb.ExposeEstrusType_Kind,
+	isShow pasturePb.IsShow_Kind,
+	isMating pasturePb.IsShow_Kind,
+	PlanDay int64,
+	operationUser, currentUser *SystemUser,
+) *EventEstrus {
 	return &EventEstrus{
-		CowId:         cow.Id,
-		DayAge:        cow.GetDayAge(),
-		Lact:          int8(cow.Lact),
-		LactationDays: cow.GetLactationDays(),
-		EstrusAt:      int64(req.EstrusAt),
-		Remarks:       req.Remarks,
-		StallNumberId: int64(req.StaffMemberId),
-		OperationId:   currentUser.Id,
+		PastureId:        pastureId,
+		CowId:            cow.Id,
+		Lact:             cow.Lact,
+		DayAge:           cow.DayAge,
+		CalvingAge:       cow.CalvingAge,
+		NeckRingNumber:   cow.NeckRingNumber,
+		EarNumber:        cow.EarNumber,
+		ExposeEstrusType: exposeEstrusType,
+		PlanDay:          PlanDay,
+		RealityDay:       PlanDay,
+		IsMating:         isMating,
+		IsShow:           isShow,
+		OperationId:      operationUser.Id,
+		OperationName:    operationUser.Name,
+		MessageId:        currentUser.Id,
+		MessageName:      currentUser.Name,
 	}
 }
 
-type EstrusSlice []*EventEstrus
+type EventEstrusSlice []*EventEstrus
 
-func (e EstrusSlice) ToPB(userList []*SystemUser) []*pasturePb.SearchEstrusList {
-	res := make([]*pasturePb.SearchEstrusList, len(e))
+func (e EventEstrusSlice) ToPB() []*pasturePb.SearchEventEstrusList {
+	res := make([]*pasturePb.SearchEventEstrusList, len(e))
 	for i, v := range e {
-		staffMemberName, operationName := "", ""
-		for _, u := range userList {
-			if u.Id == v.StallNumberId {
-				staffMemberName = u.Name
-			}
-			if u.Id == v.OperationId {
-				operationName = u.Name
-			}
-		}
-
-		res[i] = &pasturePb.SearchEstrusList{
+		res[i] = &pasturePb.SearchEventEstrusList{
 			Id:              int32(v.Id),
 			CowId:           int32(v.CowId),
+			EarNumber:       v.EarNumber,
 			DayAge:          v.DayAge,
-			Lact:            int32(v.Lact),
-			EstrusAt:        int32(v.EstrusAt),
-			LactationDays:   v.LactationDays,
-			StaffMemberId:   int32(v.StallNumberId),
-			StaffMemberName: staffMemberName,
+			Lact:            v.Lact,
+			EstrusAt:        int32(v.RealityDay),
+			IsMating:        v.IsMating,
+			UnMatingReasons: v.UnMatingReasonsName,
+			OperationName:   v.OperationName,
 			Remarks:         v.Remarks,
-			OperationId:     int32(v.OperationId),
-			OperationName:   operationName,
 			CreatedAt:       int32(v.CreatedAt),
 			UpdatedAt:       int32(v.UpdatedAt),
 		}
 	}
 	return res
 }
+
+type EventEstrusBatch struct {
+	EventEstrusList  []*EventEstrus
+	EventMatingList  []*EventMating
+	ExposeEstrusType pasturePb.ExposeEstrusType_Kind
+	EarNumbers       []string
+}

+ 89 - 0
model/event_forbidden_mating.go

@@ -0,0 +1,89 @@
+package model
+
+import (
+	pasturePb "gitee.com/xuyiping_admin/go_proto/proto/go/backend/cow"
+)
+
+type EventForbiddenMating struct {
+	Id                         int64                                 `json:"id"`
+	PastureId                  int64                                 `json:"pastureId"`
+	CowId                      int64                                 `json:"cowId"`
+	EarNumber                  string                                `json:"earNumber"`
+	DayAge                     int32                                 `json:"dayAge"`
+	ForbiddenMatingAt          int64                                 `json:"forbiddenMatingAt"`
+	ForbiddenMatingReasonsKind pasturePb.ForbiddenMatingReasons_Kind `json:"forbiddenMatingReasonsKind"`
+	ForbiddenMatingReasonsName string                                `json:"forbiddenMatingReasonsName"`
+	UnForbiddenMatingAt        int64                                 `json:"unForbiddenMatingAt"`
+	OperationId                int64                                 `json:"operationId"`
+	OperationName              string                                `json:"operationName"`
+	UnForbiddenOperationId     int64                                 `json:"unForbiddenOperationId"`
+	UnForbiddenOperationName   string                                `json:"unForbiddenOperationName"`
+	Remarks                    string                                `json:"remarks"`
+	IsShow                     pasturePb.IsShow_Kind                 `json:"isShow"`
+	MessageId                  int64                                 `json:"messageId"`
+	MessageName                string                                `json:"messageName"`
+	CreatedAt                  int64                                 `json:"createdAt"`
+	UpdatedAt                  int64                                 `json:"updatedAt"`
+}
+
+func (e *EventForbiddenMating) TableName() string {
+	return "event_forbidden_mating"
+}
+
+func (e *EventForbiddenMating) UnForbiddenMatingUpdate(unForbiddenMatingAt int64, currentUser *SystemUser) {
+	e.UnForbiddenMatingAt = unForbiddenMatingAt
+	e.IsShow = pasturePb.IsShow_No
+	e.UnForbiddenOperationId = currentUser.Id
+	e.UnForbiddenOperationName = currentUser.Name
+}
+
+func NewEventForbiddenMating(pastureId int64, cow *Cow, forbiddenMatingAt int64, forbiddenMatingReasonsKind pasturePb.ForbiddenMatingReasons_Kind,
+	forbiddenMatingReasonsName, remarks string, operationUser, currentUser *SystemUser) *EventForbiddenMating {
+	return &EventForbiddenMating{
+		PastureId:                  pastureId,
+		CowId:                      cow.Id,
+		EarNumber:                  cow.EarNumber,
+		DayAge:                     cow.GetEventDayAge(forbiddenMatingAt),
+		ForbiddenMatingAt:          forbiddenMatingAt,
+		ForbiddenMatingReasonsKind: forbiddenMatingReasonsKind,
+		ForbiddenMatingReasonsName: forbiddenMatingReasonsName,
+		OperationId:                operationUser.Id,
+		OperationName:              operationUser.Name,
+		Remarks:                    remarks,
+		MessageId:                  currentUser.Id,
+		MessageName:                currentUser.Name,
+		IsShow:                     pasturePb.IsShow_Ok,
+	}
+}
+
+type EventForbiddenMatingSlice []*EventForbiddenMating
+
+func (e EventForbiddenMatingSlice) ToPB() []*pasturePb.ForbiddenMatingItem {
+	res := make([]*pasturePb.ForbiddenMatingItem, len(e))
+	for i, v := range e {
+		res[i] = &pasturePb.ForbiddenMatingItem{
+			Id:                         int32(v.Id),
+			CowId:                      int32(v.CowId),
+			EarNumber:                  v.EarNumber,
+			ForbiddenMatingAt:          int32(v.ForbiddenMatingAt),
+			ForbiddenMatingReasonsKind: v.ForbiddenMatingReasonsKind,
+			ForbiddenMatingReasonsName: v.ForbiddenMatingReasonsName,
+			Remarks:                    v.Remarks,
+			OperationId:                int32(v.OperationId),
+			OperationName:              v.OperationName,
+			CreatedAt:                  int32(v.CreatedAt),
+			UpdatedAt:                  int32(v.UpdatedAt),
+		}
+	}
+	return res
+}
+
+type EventForbiddenMatingItem struct {
+	Cow                        *Cow                                  `json:"cow"`
+	ForbiddenMatingAt          int64                                 `json:"forbiddenMatingAt"`
+	ForbiddenMatingReasonsKind pasturePb.ForbiddenMatingReasons_Kind `json:"forbiddenMatingReasonsKind"`
+	ForbiddenMatingReasonsName string                                `json:"forbiddenMatingReasonsName"`
+	Remarks                    string                                `json:"remarks"`
+	OperationUser              *SystemUser                           `json:"operationUser"`
+	MessageUser                *SystemUser                           `json:"messageUser"`
+}

+ 0 - 22
model/event_frozen_semen_log.go

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

+ 139 - 0
model/event_immunization_plan.go

@@ -0,0 +1,139 @@
+package model
+
+import (
+	"kpt-pasture/util"
+	"time"
+
+	pasturePb "gitee.com/xuyiping_admin/go_proto/proto/go/backend/cow"
+)
+
+type EventImmunizationPlan struct {
+	Id            int64                  `json:"id"`
+	PastureId     int64                  `json:"pastureId"`
+	CowId         int64                  `json:"cowId"`
+	EarNumber     string                 `json:"earNumber"`
+	Lact          int32                  `json:"lact"`
+	DayAge        int32                  `json:"dayAge"`
+	CowType       pasturePb.CowType_Kind `json:"cowType"`
+	CowKind       pasturePb.CowKind_Kind `json:"cowKind"`
+	PenId         int32                  `json:"penId"`
+	PenName       string                 `json:"penName"`
+	PlanId        int64                  `json:"planId"`
+	PlanName      string                 `json:"planName"`
+	PlanDay       int64                  `json:"planDay"`
+	RealityDay    int64                  `json:"realityDay"`
+	EndDay        int64                  `json:"endDay"`
+	Status        pasturePb.IsShow_Kind  `json:"status"`
+	DrugsId       int64                  `json:"drugsId"`
+	DrugsName     string                 `json:"drugsName"`
+	Unit          pasturePb.Unit_Kind    `json:"unit"`
+	UnitName      string                 `json:"unitName"`
+	Usage         int32                  `json:"usage"`
+	Remarks       string                 `json:"remarks"`
+	OperationId   int64                  `json:"operationId"`
+	OperationName string                 `json:"operationName"`
+	MessageId     int64                  `json:"messageId"`
+	MessageName   string                 `json:"messageName"`
+	CreatedAt     int64                  `json:"createdAt"`
+	UpdatedAt     int64                  `json:"updatedAt"`
+}
+
+func (e *EventImmunizationPlan) TableName() string {
+	return "event_immunization_plan"
+}
+
+func (e *EventImmunizationPlan) EventUpdate(immunizationAt int64, cowInfo *Cow, drugs *Drugs, usage int32, operationUser, currUser *SystemUser, remark string) {
+	e.OperationId = operationUser.Id
+	e.OperationName = operationUser.Name
+	e.MessageId = currUser.Id
+	e.MessageName = currUser.Name
+	e.RealityDay = immunizationAt
+	e.Remarks = remark
+	e.Lact = cowInfo.Lact
+	e.DayAge = cowInfo.GetEventDayAge(immunizationAt)
+	e.EarNumber = cowInfo.EarNumber
+	e.Status = pasturePb.IsShow_Ok
+	e.DrugsId = drugs.Id
+	e.DrugsName = drugs.Name
+	e.Unit = drugs.Unit
+	e.UnitName = drugs.UnitName
+	e.Usage = usage
+}
+
+func NewEventImmunizationPlan(cow *Cow, immunizationPlan *ImmunizationPlan) *EventImmunizationPlan {
+	todayTime := time.Now().Local()
+	todayStartTime := util.TimeParseLocalUnix(todayTime.Format(LayoutDate2))
+	todayEndTime := util.TimeParseLocalEndUnix(todayTime.AddDate(0, 0, 6).Format(LayoutDate2))
+	return &EventImmunizationPlan{
+		PastureId: immunizationPlan.PastureId,
+		CowId:     cow.Id,
+		EarNumber: cow.EarNumber,
+		Lact:      cow.Lact,
+		DayAge:    cow.DayAge,
+		CowKind:   cow.CowKind,
+		CowType:   cow.CowType,
+		PenId:     cow.PenId,
+		PenName:   cow.PenName,
+		PlanId:    immunizationPlan.Id,
+		PlanName:  immunizationPlan.Name,
+		PlanDay:   todayStartTime,
+		EndDay:    todayEndTime,
+		Status:    pasturePb.IsShow_No,
+	}
+}
+
+func NewCowImmunizationPlanList(cowList []*Cow, immunizationPlan *ImmunizationPlan) []*EventImmunizationPlan {
+	eventImmunizationPlanList := make([]*EventImmunizationPlan, len(cowList))
+	for i, cow := range cowList {
+		eventImmunizationPlanList[i] = NewEventImmunizationPlan(cow, immunizationPlan)
+	}
+	return eventImmunizationPlanList
+}
+
+type EventImmunizationPlanSlice []*EventImmunizationPlan
+
+func (e EventImmunizationPlanSlice) ToPB() []*pasturePb.ImmunizationItems {
+	res := make([]*pasturePb.ImmunizationItems, len(e))
+	for i, v := range e {
+		planDay := ""
+		if v.PlanDay > 0 {
+			planDay = time.Unix(v.PlanDay, 0).Local().Format(LayoutDate2)
+		}
+		res[i] = &pasturePb.ImmunizationItems{
+			Id:        int32(v.Id),
+			CowId:     int32(v.CowId),
+			EarNumber: v.EarNumber,
+			Lact:      v.Lact,
+			PenName:   v.PenName,
+			DayAge:    v.DayAge,
+			PlanDay:   planDay,
+			Status:    v.Status,
+			PlanName:  v.PlanName,
+			PlanId:    int32(v.PlanId),
+		}
+	}
+	return res
+}
+
+func (e EventImmunizationPlanSlice) ToPB2() []*pasturePb.ImmunizationItem {
+	res := make([]*pasturePb.ImmunizationItem, len(e))
+	for i, v := range e {
+		res[i] = &pasturePb.ImmunizationItem{
+			Id:             int32(v.Id),
+			ImmunizationAt: int32(v.RealityDay),
+			OperationId:    int32(v.OperationId),
+			OperationName:  v.OperationName,
+			Remarks:        v.Remarks,
+			DrugsId:        int32(v.DrugsId),
+			DrugsName:      v.DrugsName,
+			Unit:           v.Unit,
+			UnitName:       v.UnitName,
+			Usage:          v.Usage,
+			PlanId:         int32(v.PlanId),
+			PlanName:       v.PlanName,
+			CowId:          int32(v.CowId),
+			EarNumber:      v.EarNumber,
+		}
+	}
+	return res
+}

+ 326 - 43
model/event_mating.go

@@ -1,77 +1,360 @@
 package model
 
-import pasturePb "gitee.com/xuyiping_admin/go_proto/proto/go/backend/cow"
+import (
+	"kpt-pasture/util"
+	"time"
+
+	pasturePb "gitee.com/xuyiping_admin/go_proto/proto/go/backend/cow"
+)
 
 type EventMating struct {
 	Id                int64                           `json:"id"`
-	CowId             int64                           `json:"cow_id"`
-	DayAge            int64                           `json:"day_age"`
-	Lact              int8                            `json:"lact"`
-	LactationDays     int32                           `json:"lactation_days"`
-	ExposeEstrusType  pasturePb.ExposeEstrusType_Kind `json:"expose_estrus_type"`
-	MatingAt          int64                           `json:"mating_at"`
-	BullId            string                          `json:"bull_id"`
-	FrozenSemenNumber string                          `json:"frozen_semen_number"`
-	StallNumberId     int64                           `json:"stall_number_id"`
-	OperationId       int64                           `json:"operation_id"`
+	PastureId         int64                           `json:"pastureId"`
+	CowId             int64                           `json:"cowId"`
+	EarNumber         string                          `json:"earNumber"`
+	DayAge            int32                           `json:"dayAge"`
+	Lact              int32                           `json:"lact"`
+	PenId             int32                           `json:"penId"`
+	PenName           string                          `json:"penName"`
+	CowType           pasturePb.CowType_Kind          `json:"cowType"`
+	CowKind           pasturePb.CowKind_Kind          `json:"cowKind"`
+	CalvingAge        int32                           `json:"calvingAge"`
+	PlanDay           int64                           `json:"planDay"`
+	EndDay            int64                           `json:"endDay"`
+	CalvingAt         int64                           `json:"calvingAt"`
+	RealityDay        int64                           `json:"realityDay"`
+	Status            pasturePb.IsShow_Kind           `json:"status"`
+	MatingTimes       int32                           `json:"matingTimes"`
+	MatingResult      pasturePb.MatingResult_Kind     `json:"matingResult"`
+	MatingResultAt    int64                           `json:"matingResultAt"`
+	RematingAt        int64                           `json:"rematingAt"`
+	ExposeEstrusType  pasturePb.ExposeEstrusType_Kind `json:"exposeEstrusType"`
+	FrozenSemenNumber string                          `json:"frozenSemenNumber"`
+	OperationId       int64                           `json:"operationId"`
+	OperationName     string                          `json:"operationName"`
+	MessageId         int64                           `json:"messageId"`
+	MessageName       string                          `json:"messageName"`
 	Remarks           string                          `json:"remarks"`
-	CreatedAt         int64                           `json:"created_at"`
-	UpdatedAt         int64                           `json:"updated_at"`
+	CreatedAt         int64                           `json:"createdAt"`
+	UpdatedAt         int64                           `json:"updatedAt"`
 }
 
 func (e *EventMating) TableName() string {
 	return "event_mating"
 }
 
-func NewEventMating(cow *Cow, currentUser *SystemUser, req *pasturePb.EventMating) *EventMating {
+func (e *EventMating) EventUpdate(cowInfo *Cow, matingAt int64, bullNumber string, isReMating bool, operationUser, currentUser *SystemUser) {
+	e.MatingResult = pasturePb.MatingResult_Unknown
+	e.Status = pasturePb.IsShow_Ok
+	e.RealityDay = matingAt
+	e.FrozenSemenNumber = bullNumber
+	e.OperationName = operationUser.Name
+	e.OperationId = operationUser.Id
+	e.MessageName = currentUser.Name
+	e.MessageId = currentUser.Id
+	if !isReMating {
+		e.MatingTimes += 1
+	}
+	e.DayAge = cowInfo.GetEventDayAge(matingAt)
+}
+
+// EventReMatingUpdate 复配更新
+func (e *EventMating) EventReMatingUpdate(matingAt int64) {
+	e.MatingResult = pasturePb.MatingResult_ReMatch
+	e.Status = pasturePb.IsShow_Ok
+	e.MatingResultAt = matingAt
+	e.RematingAt = matingAt
+}
+
+// EventMatingResultUpdate 配种结果更新
+func (e *EventMating) EventMatingResultUpdate(matingResult pasturePb.MatingResult_Kind, resultAt int64) {
+	e.MatingResult = matingResult
+	e.MatingResultAt = resultAt
+}
+
+// IsReMating 判断是不是复配
+func (e *EventMating) IsReMating(cow *Cow, matingAt int64) bool {
+	lastMatingAt := time.Unix(cow.LastMatingAt, 0).Local()
+	currentMatingAt := time.Unix(matingAt, 0).Local()
+	daysBetween := util.DaysBetween(currentMatingAt.Unix(), lastMatingAt.Unix())
+	if (daysBetween == 1 || daysBetween == 0) && e.Status == pasturePb.IsShow_Ok && e.MatingResult == pasturePb.MatingResult_Unknown {
+		return true
+	}
+	return false
+}
+
+// IsMatingUpdate 判断是不是更新配种信息
+func (e *EventMating) IsMatingUpdate() bool {
+	if e.Status == pasturePb.IsShow_No && e.MatingResult == pasturePb.MatingResult_Unknown {
+		return true
+	}
+	return false
+}
+
+// IsEmptyMating 判断上次配种结果是不是空怀
+func (e *EventMating) IsEmptyMating(cow *Cow, matingAt int64) bool {
+	lastMatingAt := time.Unix(cow.LastMatingAt, 0).Local()
+	currentMatingAt := time.Unix(matingAt, 0).Local()
+	daysBetween := util.DaysBetween(currentMatingAt.Unix(), lastMatingAt.Unix())
+	if (e.MatingResult == pasturePb.MatingResult_Unknown || e.MatingResult == pasturePb.MatingResult_ReMatch) && daysBetween >= 2 {
+		return true
+	}
+	return false
+}
+
+func NewEventMating(pastureId int64, cow *Cow, planDay int64, exposeEstrusType pasturePb.ExposeEstrusType_Kind) *EventMating {
 	return &EventMating{
-		CowId:             cow.Id,
-		DayAge:            int64(cow.GetDayAge()),
-		Lact:              int8(cow.Lact),
-		LactationDays:     cow.GetLactationDays(),
-		ExposeEstrusType:  req.ExposeEstrusType,
-		MatingAt:          int64(req.MatingAt),
-		BullId:            req.BullId,
-		FrozenSemenNumber: req.FrozenSemenNumber,
-		StallNumberId:     int64(req.StaffMemberId),
-		OperationId:       currentUser.Id,
-		Remarks:           req.Remarks,
+		PastureId:        pastureId,
+		CowId:            cow.Id,
+		EarNumber:        cow.EarNumber,
+		Lact:             cow.Lact,
+		PenId:            cow.PenId,
+		PenName:          cow.PenName,
+		CowType:          cow.CowType,
+		CowKind:          cow.CowKind,
+		DayAge:           cow.DayAge,
+		CalvingAt:        cow.LastMatingAt,
+		PlanDay:          planDay,
+		EndDay:           planDay + 86399,
+		MatingResult:     pasturePb.MatingResult_Unknown,
+		ExposeEstrusType: exposeEstrusType,
+		Status:           pasturePb.IsShow_No,
 	}
 }
 
+// NewEventMatingNaturalEstrus 自然发情的牛只
+func NewEventMatingNaturalEstrus(pastureId int64, item *EventMatingCheckBatchModel, currentUser *SystemUser) *EventMating {
+	return &EventMating{
+		PastureId:         pastureId,
+		CowId:             item.Cow.Id,
+		EarNumber:         item.Cow.EarNumber,
+		Lact:              item.Cow.Lact,
+		DayAge:            item.Cow.GetDayAge(),
+		CowType:           item.Cow.CowType,
+		CowKind:           item.Cow.CowKind,
+		CalvingAt:         item.Cow.LastMatingAt,
+		PlanDay:           item.MatingAt,
+		RealityDay:        item.MatingAt,
+		EndDay:            item.MatingAt,
+		MatingResult:      pasturePb.MatingResult_Unknown,
+		ExposeEstrusType:  pasturePb.ExposeEstrusType_Natural_Estrus,
+		Status:            pasturePb.IsShow_Ok,
+		OperationId:       item.OperationUser.Id,
+		OperationName:     item.OperationUser.Name,
+		MessageId:         currentUser.Id,
+		MessageName:       currentUser.Name,
+		FrozenSemenNumber: item.FrozenSemen.BullId,
+		Remarks:           item.Remarks,
+		MatingTimes:       item.Cow.MatingTimes + 1,
+	}
+}
+
+// NewEventMatingList 同期配种
+func NewEventMatingList(pastureId int64, cowList []*Cow, planDay int64, exposeEstrusType pasturePb.ExposeEstrusType_Kind) []*EventMating {
+	var matingList []*EventMating
+	for _, cow := range cowList {
+		matingList = append(matingList, NewEventMating(pastureId, cow, planDay, exposeEstrusType))
+	}
+	return matingList
+}
+
 type EventMatingSlice []*EventMating
 
-func (e EventMatingSlice) ToPB(user []*SystemUser, exposeEstrusTypeMap map[pasturePb.ExposeEstrusType_Kind]string) []*pasturePb.SearchMatingList {
+func (e EventMatingSlice) ToPB(exposeEstrusTypeMap map[pasturePb.ExposeEstrusType_Kind]string) []*pasturePb.SearchMatingList {
 	res := make([]*pasturePb.SearchMatingList, len(e))
 	for i, v := range e {
-		operationName, staffMemberName := "", ""
-		for _, u := range user {
-			if v.OperationId == u.Id {
-				operationName = u.Name
-			}
-			if v.StallNumberId == u.Id {
-				staffMemberName = u.Name
-			}
-		}
 		res[i] = &pasturePb.SearchMatingList{
 			Id:                   int32(v.Id),
 			CowId:                int32(v.CowId),
-			DayAge:               int32(v.DayAge),
-			Lact:                 int32(v.Lact),
+			EarNumber:            v.EarNumber,
+			DayAge:               v.DayAge,
+			Lact:                 v.Lact,
+			CalvingAge:           v.CalvingAge,
+			PlanDay:              time.Unix(v.PlanDay, 0).Local().Format(LayoutDate2),
+			RealityDay:           time.Unix(v.RealityDay, 0).Local().Format(LayoutDate2),
 			ExposeEstrusType:     v.ExposeEstrusType,
 			ExposeEstrusTypeName: exposeEstrusTypeMap[v.ExposeEstrusType],
-			MatingAt:             int32(v.MatingAt),
-			BullId:               v.BullId,
 			FrozenSemenNumber:    v.FrozenSemenNumber,
-			LactationDays:        v.LactationDays,
-			StaffMemberId:        int32(v.StallNumberId),
-			StaffMemberName:      staffMemberName,
 			Remarks:              v.Remarks,
 			OperationId:          int32(v.OperationId),
-			OperationName:        operationName,
+			OperationName:        v.OperationName,
 			CreatedAt:            int32(v.CreatedAt),
 			UpdatedAt:            int32(v.UpdatedAt),
 		}
 	}
 	return res
 }
+
+func (e EventMatingSlice) ToPB2() []*pasturePb.CowList {
+	res := make([]*pasturePb.CowList, len(e))
+	for i, v := range e {
+		calvingAt, matingAtFormat := "", ""
+		if v.CalvingAt > 0 {
+			calvingAt = time.Unix(v.CalvingAt, 0).Local().Format(LayoutDate2)
+		}
+		if v.RealityDay > 0 {
+			matingAtFormat = time.Unix(v.RealityDay, 0).Local().Format(LayoutDate2)
+		}
+		res[i] = &pasturePb.CowList{
+			CowId:           int32(v.CowId),
+			DayAge:          int32(v.DayAge),
+			CalvingAge:      v.CalvingAge,
+			MatingAtFormat:  matingAtFormat,
+			CalvingAtFormat: calvingAt,
+			Lact:            int32(v.Lact),
+		}
+	}
+	return res
+}
+
+func (e EventMatingSlice) ToPB3(isMating bool, cowTypeMap map[pasturePb.CowType_Kind]string) []*pasturePb.TwentyOnePregnantItems {
+	res := make([]*pasturePb.TwentyOnePregnantItems, 0)
+	for _, v := range e {
+		if !isMating {
+			continue
+		}
+		res = append(res, &pasturePb.TwentyOnePregnantItems{
+			CowId:       int32(v.CowId),
+			EarNumber:   v.EarNumber,
+			CowTypeName: cowTypeMap[v.CowType],
+			Lact:        v.Lact,
+		})
+	}
+	return res
+}
+
+func (e EventMatingSlice) ToPB4(cowTypeMap map[pasturePb.CowType_Kind]string) []*pasturePb.TwentyOnePregnantItems {
+	res := make([]*pasturePb.TwentyOnePregnantItems, 0)
+	for _, v := range e {
+		if v.MatingResult != pasturePb.MatingResult_Abort {
+			continue
+		}
+		res = append(res, &pasturePb.TwentyOnePregnantItems{
+			CowId:       int32(v.CowId),
+			EarNumber:   v.EarNumber,
+			CowTypeName: cowTypeMap[v.CowType],
+			Lact:        v.Lact,
+		})
+	}
+	return res
+}
+
+type CowMatingBody struct {
+	Id              int64                      `json:"id"`
+	CowId           int64                      `json:"cowId"`
+	BreedStatus     pasturePb.BreedStatus_Kind `json:"breedStatus"`
+	BreedStatusName string                     `json:"breedStatusName"`
+	CowType         pasturePb.CowType_Kind     `json:"cowType"`
+	CowTypeName     string                     `json:"cowTypeName"`
+	PenId           int32                      `json:"penId"`
+	PenName         string                     `json:"penName"`
+	Lact            int32                      `json:"lact"`
+	CalvingAge      int32                      `json:"calvingAge"`
+	AbortionAge     int32                      `json:"abortionAge"`
+	DayAge          int32                      `json:"dayAge"`
+	Status          pasturePb.IsShow_Kind      `json:"status"`
+}
+
+type CowMatingBodySlice []*CowMatingBody
+
+func (s CowMatingBodySlice) ToPB(
+	cowTypeMap map[pasturePb.CowType_Kind]string,
+	breedStatusMap map[pasturePb.BreedStatus_Kind]string,
+	penMap map[int32]*Pen,
+) []*CowMatingBody {
+	res := make([]*CowMatingBody, len(s))
+	for i, v := range s {
+		res[i] = &CowMatingBody{
+			Id:              v.Id,
+			CowId:           v.CowId,
+			CowType:         v.CowType,
+			CowTypeName:     cowTypeMap[v.CowType],
+			BreedStatus:     v.BreedStatus,
+			BreedStatusName: breedStatusMap[v.BreedStatus],
+			PenName:         penMap[v.PenId].Name,
+			PenId:           v.PenId,
+			Lact:            v.Lact,
+			CalvingAge:      v.CalvingAge,
+			AbortionAge:     v.AbortionAge,
+			DayAge:          v.DayAge,
+			Status:          v.Status,
+		}
+	}
+	return res
+}
+
+type MatingTimelyChart struct {
+	CalvingAge int32  `json:"calvingAge"`
+	RealityDay string `json:"realityDay"`
+	LactGroup  string `json:"lactGroup"`
+}
+
+type MatingTimelyResponse struct {
+	Code int32             `json:"code"`
+	Msg  string            `json:"msg"`
+	Data *MatingTimelyData `json:"data"`
+}
+
+type MatingTimelyData struct {
+	CowList []*pasturePb.CowList `json:"cowList"`
+	Chart   *CowMatingChart      `json:"chart"`
+}
+
+type CowMatingChart struct {
+	Lact0 [][]string `json:"lact0"`
+	Lact1 [][]string `json:"lact1"`
+	Lact2 [][]string `json:"lact2"`
+	Lact3 [][]string `json:"lact3"`
+}
+
+// MultiFactorPregnancyRateResponse 多维度受胎率
+type MultiFactorPregnancyRateResponse struct {
+	Code int32                         `json:"code"`
+	Msg  string                        `json:"msg"`
+	Data *MultiFactorPregnancyRateData `json:"data"`
+}
+
+// MultiFactorPregnancyRateList 多维度受胎率分析
+type MultiFactorPregnancyRateList struct {
+	StatisticMethod1   string  `json:"statisticMethod1"`   // 统计方式名称1 (月度、品种)
+	StatisticMethod2   string  `json:"statisticMethod2"`   // 统计方式名称2 (月度、品种)
+	PregnantRate       float32 `json:"pregnantRate"`       // 受胎率%(怀孕数 / 怀孕数 + 空怀数)
+	PregnantCount      int32   `json:"pregnantCount"`      // 怀孕总数
+	EmptyPregnantCount int32   `json:"emptyPregnantCount"` // 空怀数
+	OtherCount         int32   `json:"otherCount"`         // 其他数 (配种后结果未知的个数,小于等于三次配种后,尚未孕检已经淘汰的个数)
+	AbortionCount      int32   `json:"abortionCount"`      // 流产数 (已经怀孕后流产的个数)
+	TotalCount         int32   `json:"totalCount"`         // 合计( 怀孕总数+空怀数+其他数)
+	SpcRate            float32 `json:"spcRate"`            // spc(1 / 受胎率)
+	Months             string  `json:"months"`             // 月份
+	OperationName      string  `json:"operationName"`      // 配种员名称
+	Bull               string  `json:"bull"`               // 公牛
+	Lact               string  `json:"lact"`               // 胎次
+	MatingTimes        string  `json:"matingTimes"`        // 配次
+	ExposeEstrusType   string  `json:"exposeEstrusType"`   // 发情揭发方式
+	Week               string  `json:"week"`               // 周
+}
+
+type MultiFactorPregnancyRateData struct {
+	Total    int32                           `json:"total"`
+	PageSize int32                           `json:"pageSize"`
+	Page     int32                           `json:"page"`
+	List     []*MultiFactorPregnancyRateList `json:"list"`
+	Chart    *MultiFactorPregnancyRateChart  `json:"chart"`
+}
+
+type MultiFactorPregnancyRateChart struct {
+	Header          []string                     `json:"header"` // 标题头
+	PregnantRateMap map[string]map[string]string `json:"pregnantRateMap"`
+	KepMap          []string                     `json:"kepMap"`
+}
+
+// EventMatingCheckBatchModel 批量配种
+type EventMatingCheckBatchModel struct {
+	Cow              *Cow
+	FrozenSemen      *FrozenSemen
+	OperationUser    *SystemUser
+	MatingAt         int64
+	FrozenSemenCount int32
+	Remarks          string
+	ExposeEstrusType pasturePb.ExposeEstrusType_Kind
+}

+ 165 - 48
model/event_pregnant_check.go

@@ -1,87 +1,204 @@
 package model
 
-import pasturePb "gitee.com/xuyiping_admin/go_proto/proto/go/backend/cow"
+import (
+	"kpt-pasture/util"
+	"time"
+
+	pasturePb "gitee.com/xuyiping_admin/go_proto/proto/go/backend/cow"
+)
+
+const (
+	PregnantCheckForFirst  = "pregnant_check_for_first"  // 初检
+	PregnantCheckForSecond = "pregnant_check_for_second" // 复检
+)
 
 type EventPregnantCheck struct {
 	Id                  int64                              `json:"id"`
-	CowId               int64                              `json:"cow_id"`
-	DayAge              int32                              `json:"day_age"`
+	PastureId           int64                              `json:"pastureId"`
+	CowId               int64                              `json:"cowId"`
+	EarNumber           string                             `json:"earNumber"`
+	CowType             pasturePb.CowType_Kind             `json:"cowType"`
+	PenId               int32                              `json:"penId"`
+	PenName             string                             `json:"penName"`
+	DayAge              int32                              `json:"dayAge"`
 	Lact                int8                               `json:"lact"`
-	PregnantCheckAt     int64                              `json:"pregnant_check_at"`
-	PregnantCheckResult pasturePb.PregnantCheckResult_Kind `json:"pregnant_check_result"`
-	PregnantCheckMethod pasturePb.PregnantCheckMethod_Kind `json:"pregnant_check_method"`
-	StaffMemberId       int64                              `json:"staff_member_id"`
-	OperationId         int64                              `json:"operation_id"`
+	MatingAge           int32                              `json:"matingAge"`
+	PlanDay             int64                              `json:"planDay"`
+	RealityDay          int64                              `json:"realityDay"`
+	EndDay              int64                              `json:"endDay"`
+	PregnantCheckName   string                             `json:"pregnantCheckName"`
+	PregnantCheckResult pasturePb.PregnantCheckResult_Kind `json:"pregnantCheckResult"`
+	PregnantCheckMethod pasturePb.PregnantCheckMethod_Kind `json:"pregnantCheckMethod"`
+	BullId              string                             `json:"bullId"`
+	Status              pasturePb.IsShow_Kind              `json:"status"`
+	OperationId         int64                              `json:"operationId"`
+	OperationName       string                             `json:"operationName"`
+	MessageId           int64                              `json:"messageId"`
+	MessageName         string                             `json:"messageName"`
 	Remarks             string                             `json:"remarks"`
-	CreatedAt           int64                              `json:"created_at"`
-	UpdatedAt           int64                              `json:"updated_at"`
+	CreatedAt           int64                              `json:"createdAt"`
+	UpdatedAt           int64                              `json:"updatedAt"`
 }
 
 func (e *EventPregnantCheck) TableName() string {
 	return "event_pregnant_check"
 }
 
-func NewEventPregnantCheck(cow *Cow, systemUser *SystemUser, req *pasturePb.EventPregnantCheck) *EventPregnantCheck {
+func (e *EventPregnantCheck) EventUpdate(
+	pregnantCheckAt int64,
+	pregnantCheckResult pasturePb.PregnantCheckResult_Kind,
+	pregnantCheckMethod pasturePb.PregnantCheckMethod_Kind,
+	operationUser, currentUser *SystemUser,
+	remarks string,
+	cowInfo *Cow,
+) {
+	e.RealityDay = pregnantCheckAt
+	e.PregnantCheckResult = pregnantCheckResult
+	e.PregnantCheckMethod = pregnantCheckMethod
+	e.OperationId = operationUser.Id
+	e.OperationName = operationUser.Name
+	e.MessageId = currentUser.Id
+	e.MessageName = currentUser.Name
+	e.Remarks = remarks
+	e.Status = pasturePb.IsShow_Ok
+	e.DayAge = cowInfo.GetEventDayAge(pregnantCheckAt)
+}
+
+func NewEventPregnantCheck(pastureId int64, cow *Cow, penMap map[int32]*Pen, pregnantCheckName, startDay, endDay string) *EventPregnantCheck {
+	penName := ""
+	if pen, ok := penMap[cow.PenId]; ok {
+		penName = pen.Name
+	}
 	return &EventPregnantCheck{
-		CowId:               cow.Id,
-		DayAge:              cow.GetDayAge(),
-		Lact:                int8(cow.Lact),
-		PregnantCheckAt:     int64(req.PregnantCheckAt),
+		PastureId:         pastureId,
+		CowId:             cow.Id,
+		EarNumber:         cow.EarNumber,
+		CowType:           cow.CowType,
+		PenId:             cow.PenId,
+		PenName:           penName,
+		DayAge:            0,
+		Lact:              int8(cow.Lact),
+		PlanDay:           util.TimeParseLocalUnix(startDay),
+		EndDay:            util.TimeParseLocalEndUnix(endDay),
+		PregnantCheckName: pregnantCheckName,
+		BullId:            cow.LastBullNumber,
+		Status:            pasturePb.IsShow_No,
+	}
+}
+
+func NewEventPregnantCheckList(pastureId int64, cowList []*Cow, penMap map[int32]*Pen, pregnantCheckName, startDay, endDay string) []*EventPregnantCheck {
+	res := make([]*EventPregnantCheck, len(cowList))
+	for i, cow := range cowList {
+		if cow.BreedStatus != pasturePb.BreedStatus_Breeding && cow.BreedStatus != pasturePb.BreedStatus_Pregnant {
+			continue
+		}
+		res[i] = NewEventPregnantCheck(pastureId, cow, penMap, pregnantCheckName, startDay, endDay)
+	}
+	return res
+}
+
+type EventPregnantCheck2 struct {
+	Cow                 *Cow
+	OperationUser       *SystemUser
+	CurrentUser         *SystemUser
+	PregnantCheckAt     int64
+	PregnantCheckMethod pasturePb.PregnantCheckMethod_Kind
+	PregnantCheckResult pasturePb.PregnantCheckResult_Kind
+	Remarks             string
+}
+
+func NewEventPregnantCheck2(req *EventPregnantCheck2, penMap map[int32]*Pen) *EventPregnantCheck {
+	penName := ""
+	if pen, ok := penMap[req.Cow.PenId]; ok {
+		penName = pen.Name
+	}
+	return &EventPregnantCheck{
+		CowId:               req.Cow.Id,
+		CowType:             req.Cow.CowType,
+		PenId:               req.Cow.PenId,
+		PenName:             penName,
+		DayAge:              req.Cow.GetDayAge(),
+		Lact:                int8(req.Cow.Lact),
+		PlanDay:             req.PregnantCheckAt,
+		RealityDay:          req.PregnantCheckAt,
+		EndDay:              req.PregnantCheckAt,
 		PregnantCheckResult: req.PregnantCheckResult,
 		PregnantCheckMethod: req.PregnantCheckMethod,
-		StaffMemberId:       int64(req.StaffMemberId),
-		OperationId:         systemUser.Id,
+		PregnantCheckName:   PregnantCheckForFirst,
+		BullId:              req.Cow.LastBullNumber,
+		Status:              pasturePb.IsShow_Ok,
+		OperationId:         req.OperationUser.Id,
+		OperationName:       req.OperationUser.Name,
 		Remarks:             req.Remarks,
+		MessageId:           req.CurrentUser.Id,
+		MessageName:         req.CurrentUser.Name,
 	}
 }
 
 type EventPregnantCheckSlice []*EventPregnantCheck
 
-func (e EventPregnantCheckSlice) ToPB(systemUserList []*SystemUser, pregnantCheckResult, pregnantCheckMethod []*pasturePb.ConfigOptionsList) []*pasturePb.SearchPregnantCheckList {
+func (e EventPregnantCheckSlice) ToPB(
+	pregnantCheckResultMap map[pasturePb.PregnantCheckResult_Kind]string,
+	pregnantCheckMethodMap map[pasturePb.PregnantCheckMethod_Kind]string,
+) []*pasturePb.SearchPregnantCheckList {
 	result := make([]*pasturePb.SearchPregnantCheckList, len(e))
 	for i, v := range e {
-		var pregnantCheckResultName, pregnantCheckMethodName, staffMemberName, operationName = "", "", "", ""
-		for _, u := range systemUserList {
-			if v.StaffMemberId == u.Id {
-				staffMemberName = u.Name
-			}
-			if v.OperationId == u.Id {
-				operationName = u.Name
-			}
-		}
-
-		for _, s := range pregnantCheckResult {
-			if int32(v.PregnantCheckResult) != s.Value {
-				continue
-			}
-			pregnantCheckResultName = s.Label
-		}
-
-		for _, m := range pregnantCheckMethod {
-			if int32(v.PregnantCheckMethod) != m.Value {
-				continue
-			}
-			pregnantCheckMethodName = m.Label
-		}
-
 		result[i] = &pasturePb.SearchPregnantCheckList{
 			Id:                      int32(v.Id),
 			CowId:                   int32(v.CowId),
 			DayAge:                  v.DayAge,
 			Lact:                    int32(v.Lact),
-			PregnantCheckAt:         int32(v.PregnantCheckAt),
+			EarNumber:               v.EarNumber,
+			PregnantCheckAt:         int32(v.RealityDay),
 			PregnantCheckResult:     v.PregnantCheckResult,
-			PregnantCheckResultName: pregnantCheckResultName,
+			PregnantCheckResultName: pregnantCheckResultMap[v.PregnantCheckResult],
 			PregnantCheckMethod:     v.PregnantCheckMethod,
-			PregnantCheckMethodName: pregnantCheckMethodName,
-			StaffMemberId:           int32(v.StaffMemberId),
-			StaffMemberName:         staffMemberName,
+			PregnantCheckMethodName: pregnantCheckMethodMap[v.PregnantCheckMethod],
 			Remarks:                 v.Remarks,
 			OperationId:             int32(v.OperationId),
-			OperationName:           operationName,
+			OperationName:           v.OperationName,
 			CreatedAt:               int32(v.CreatedAt),
 			UpdatedAt:               int32(v.UpdatedAt),
 		}
 	}
 	return result
 }
+
+func (e EventPregnantCheckSlice) ToPB3(
+	pregnantCheckResultMap map[pasturePb.PregnantCheckResult_Kind]string,
+	pregnantCheckMethodMap map[pasturePb.PregnantCheckMethod_Kind]string,
+) []*pasturePb.PregnancyReportTable {
+	res := make([]*pasturePb.PregnancyReportTable, len(e))
+	for i, v := range e {
+		pregnancyCheckName := ""
+		if checkName, ok := PregnantCheckNameKeyMap[v.PregnantCheckName]; ok {
+			pregnancyCheckName = checkName
+		}
+
+		pregnantCheckMethodName := ""
+		if checkMethodName, ok := pregnantCheckMethodMap[v.PregnantCheckMethod]; ok {
+			pregnantCheckMethodName = checkMethodName
+		}
+
+		pregnantCheckResultName := ""
+		if checkResultName, ok := pregnantCheckResultMap[v.PregnantCheckResult]; ok {
+			pregnantCheckResultName = checkResultName
+		}
+
+		res[i] = &pasturePb.PregnancyReportTable{
+			Id:                      int32(v.Id),
+			CowId:                   int32(v.CowId),
+			EarNumber:               v.EarNumber,
+			Lact:                    int32(v.Lact),
+			PregnancyCheckName:      pregnancyCheckName,
+			PregnancyCheckAtFormat:  time.Unix(v.RealityDay, 0).Local().Format(LayoutDate2),
+			MatingAge:               v.MatingAge,
+			PregnantCheckMethod:     v.PregnantCheckMethod,
+			PregnantCheckMethodName: pregnantCheckMethodName,
+			PregnantCheckResult:     v.PregnantCheckResult,
+			PregnantCheckResultName: pregnantCheckResultName,
+			OperationName:           v.OperationName,
+		}
+	}
+	return res
+}

+ 131 - 0
model/event_sale.go

@@ -0,0 +1,131 @@
+package model
+
+import (
+	"strings"
+
+	pasturePb "gitee.com/xuyiping_admin/go_proto/proto/go/backend/cow"
+)
+
+type EventSale struct {
+	Id               int64                     `json:"id"`
+	PastureId        int64                     `json:"pastureId"`
+	DealerId         int32                     `json:"dealerId"`
+	DealerName       string                    `json:"dealerName"`
+	SalePrice        float64                   `json:"salePrice"`
+	SaleAllWeight    int32                     `json:"saleAllWeight"`
+	SaleAllAmount    int64                     `json:"saleAllAmount"`
+	SaleCowCount     int32                     `json:"saleCowCount"`
+	SaleKind         pasturePb.SalesType_Kind  `json:"saleKind"`
+	OutReasonsKind   pasturePb.OutReasons_Kind `json:"outReasonsKind"`
+	OutReasonsName   string                    `json:"outReasonsName"`
+	SaleAt           int64                     `json:"saleAt"`
+	SaleTicker       string                    `json:"saleTicker"`
+	QuarantineReport string                    `json:"quarantineReport"`
+	Remarks          string                    `json:"remarks"`
+	OperationId      int64                     `json:"operationId"`
+	OperationName    string                    `json:"operationName"`
+	MessageId        int64                     `json:"messageId"`
+	MessageName      string                    `json:"messageName"`
+	CreatedAt        int64                     `json:"createdAt"`
+	UpdatedAt        int64                     `json:"updatedAt"`
+}
+
+func (e *EventSale) TableName() string {
+	return "event_sale"
+}
+
+func NewEventSale(pastureId int64, dealerInfo *SaleDealer, req *pasturePb.EventCowSale, operationUser, currUser *SystemUser) *EventSale {
+	return &EventSale{
+		PastureId:        pastureId,
+		DealerId:         dealerInfo.Id,
+		DealerName:       dealerInfo.Name,
+		SalePrice:        float64(req.SalePrice),
+		SaleAllWeight:    int32(req.SaleAllWeight * 1000),
+		SaleAllAmount:    int64(req.SaleAllPrice * 100),
+		SaleCowCount:     int32(len(req.EarNumbers)),
+		SaleKind:         req.SalesType,
+		OutReasonsKind:   req.OutReasonsKind,
+		OutReasonsName:   req.OutReasonsName,
+		SaleAt:           int64(req.SaleAt),
+		SaleTicker:       strings.Join(req.SaleTicket, ","),
+		QuarantineReport: strings.Join(req.QuarantineReport, ","),
+		Remarks:          req.Remarks,
+		OperationId:      operationUser.Id,
+		OperationName:    operationUser.Name,
+		MessageId:        currUser.Id,
+		MessageName:      currUser.Name,
+	}
+}
+
+type EventSaleSlice []*EventSale
+
+func (e EventSaleSlice) ToPB(eventSaleCarMap map[int64][]*EventSaleCar, eventSaleCowMap map[int64][]*EventSaleCow) []*pasturePb.EventCowSale {
+	res := make([]*pasturePb.EventCowSale, len(e))
+	for i, v := range e {
+		saleVehicleItems := make([]*pasturePb.SaleVehicleItem, 0)
+		if eventSaleCarItems, ok := eventSaleCarMap[v.Id]; ok {
+			for _, item := range eventSaleCarItems {
+				es, weighbridgePhotos, vehiclePhotos := make([]string, 0), make([]string, 0), make([]string, 0)
+				if item.EarNumbers != "" {
+					es = strings.Split(item.EarNumbers, ",")
+				}
+				if item.WeighbridgePhotos != "" {
+					weighbridgePhotos = strings.Split(item.WeighbridgePhotos, ",")
+				}
+				if item.CarPhotos != "" {
+					vehiclePhotos = strings.Split(item.CarPhotos, ",")
+				}
+				saleVehicleItem := &pasturePb.SaleVehicleItem{
+					CarNumber:         item.CarNumber,
+					CowCount:          item.CowCount,
+					CowWeight:         float32(item.CowWeight) / 1000,
+					OutboundTicket:    item.OutboundTicket,
+					WeighbridgePhotos: weighbridgePhotos,
+					VehiclePhotos:     vehiclePhotos,
+					EarNumbers:        es,
+				}
+				saleVehicleItems = append(saleVehicleItems, saleVehicleItem)
+			}
+		}
+
+		earNumbers := make([]string, 0)
+		saleCount := int32(0)
+		if cowList, ok := eventSaleCowMap[v.Id]; ok {
+			for _, cow := range cowList {
+				earNumbers = append(earNumbers, cow.EarNumber)
+				saleCount++
+			}
+		}
+
+		res[i] = &pasturePb.EventCowSale{
+			DealerId:         v.DealerId,
+			DealerName:       v.DealerName,
+			SaleAt:           int32(v.SaleAt),
+			EarNumbers:       earNumbers,
+			SalesType:        v.SaleKind,
+			OutReasonsKind:   v.OutReasonsKind,
+			OutReasonsName:   v.OutReasonsName,
+			SaleAllWeight:    float32(v.SaleAllWeight) / 1000,
+			SaleAllPrice:     float32(v.SaleAllAmount) / 100,
+			SalePrice:        float32(v.SalePrice),
+			OperationId:      int32(v.OperationId),
+			OperationName:    v.OperationName,
+			Remarks:          v.Remarks,
+			SaleTicket:       strings.Split(v.SaleTicker, ","),
+			QuarantineReport: strings.Split(v.QuarantineReport, ","),
+			SaleVehicleItems: saleVehicleItems,
+			SaleCount:        saleCount,
+		}
+	}
+	return res
+}
+
+type EventSaleModel struct {
+	CowList          []*Cow
+	SalesType        pasturePb.SalesType_Kind
+	SaleAt           int64
+	EventSaleCarList []*EventSaleCar
+	EventSaleCowList []*EventSaleCow
+	NeckRingList     []*NeckRing
+	EventCowLog      []*EventCowLog
+}

+ 50 - 0
model/event_sale_car.go

@@ -0,0 +1,50 @@
+package model
+
+import (
+	"strings"
+
+	pasturePb "gitee.com/xuyiping_admin/go_proto/proto/go/backend/cow"
+)
+
+type EventSaleCar struct {
+	Id                int64  `json:"id"`
+	PastureId         int64  `json:"pastureId"`
+	SaleId            int64  `json:"saleId"`
+	SaleAt            int64  `json:"saleAt"`
+	CarNumber         string `json:"carNumber"`
+	CowCount          int32  `json:"cowCount"`
+	CowWeight         int64  `json:"cowWeight"`
+	EarNumbers        string `json:"earNumbers"`
+	OutboundTicket    string `json:"outbound_ticket"`
+	WeighbridgePhotos string `json:"weighbridgePhotos"`
+	CarPhotos         string `json:"carPhotos"`
+	CreatedAt         int64  `json:"createdAt"`
+	UpdatedAt         int64  `json:"updatedAt"`
+}
+
+func (e *EventSaleCar) TableName() string {
+	return "event_sale_car"
+}
+
+func NewEventSaleCar(pastureId, saleId, saleAt int64, saleVehicleItem *pasturePb.SaleVehicleItem) *EventSaleCar {
+	return &EventSaleCar{
+		PastureId:         pastureId,
+		SaleId:            saleId,
+		SaleAt:            saleAt,
+		CarNumber:         saleVehicleItem.CarNumber,
+		CowCount:          saleVehicleItem.CowCount,
+		EarNumbers:        strings.Join(saleVehicleItem.EarNumbers, ","),
+		OutboundTicket:    saleVehicleItem.OutboundTicket,
+		WeighbridgePhotos: strings.Join(saleVehicleItem.WeighbridgePhotos, ","),
+		CarPhotos:         strings.Join(saleVehicleItem.VehiclePhotos, ","),
+		CowWeight:         int64(saleVehicleItem.CowWeight * 1000),
+	}
+}
+
+func NewEventSaleCarList(pastureId, saleId, saleAt int64, req []*pasturePb.SaleVehicleItem) []*EventSaleCar {
+	res := make([]*EventSaleCar, 0)
+	for _, saleVehicleItem := range req {
+		res = append(res, NewEventSaleCar(pastureId, saleId, saleAt, saleVehicleItem))
+	}
+	return res
+}

+ 49 - 0
model/event_sale_cow.go

@@ -0,0 +1,49 @@
+package model
+
+import pasturePb "gitee.com/xuyiping_admin/go_proto/proto/go/backend/cow"
+
+type EventSaleCow struct {
+	Id           int64                      `json:"id"`
+	PastureId    int64                      `json:"pastureId"`
+	SaleId       int64                      `json:"saleId"`
+	CowId        int64                      `json:"cowId"`
+	CowType      pasturePb.CowType_Kind     `json:"cowType"`
+	EarNumber    string                     `json:"earNumber"`
+	DayAge       int32                      `json:"dayAge"`
+	Lact         int32                      `json:"lact"`
+	PregnancyAge int32                      `json:"pregnancyAge"`
+	BreedStatus  pasturePb.BreedStatus_Kind `json:"breedStatus"`
+	LactationAge int32                      `json:"lactationAge"`
+	CalvingAge   int32                      `json:"calvingAge"`
+	AdmissionAge int32                      `json:"admissionAge"`
+	CreatedAt    int64                      `json:"createdAt"`
+	UpdatedAt    int64                      `json:"updatedAt"`
+}
+
+func (e *EventSaleCow) TableName() string {
+	return "event_sale_cow"
+}
+
+func NewEventSaleCow(pastureId int64, saleId, saleAt int64, cowInfo *Cow) *EventSaleCow {
+	return &EventSaleCow{
+		PastureId:    pastureId,
+		SaleId:       saleId,
+		CowId:        cowInfo.Id,
+		Lact:         cowInfo.Lact,
+		EarNumber:    cowInfo.EarNumber,
+		PregnancyAge: cowInfo.PregnancyAge,
+		BreedStatus:  cowInfo.BreedStatus,
+		LactationAge: cowInfo.LactationAge,
+		AdmissionAge: cowInfo.AdmissionAge,
+		DayAge:       cowInfo.GetEventDayAge(saleAt),
+		CowType:      cowInfo.CowType,
+	}
+}
+
+func NewEventSaleCowList(pastureId int64, eventSale *EventSale, cowList []*Cow) []*EventSaleCow {
+	res := make([]*EventSaleCow, 0)
+	for _, cow := range cowList {
+		res = append(res, NewEventSaleCow(pastureId, eventSale.Id, eventSale.SaleAt, cow))
+	}
+	return res
+}

+ 0 - 46
model/event_same_time.go

@@ -1,46 +0,0 @@
-package model
-
-import pasturePb "gitee.com/xuyiping_admin/go_proto/proto/go/backend/cow"
-
-type EventSameTime struct {
-	Id               int64                       `json:"id"`
-	CowId            int64                       `json:"cowId"`
-	Lact             int32                       `json:"lact"`
-	DayAge           int32                       `json:"dayAge"`
-	CalvingAge       int64                       `json:"calvingAge"`
-	SameTimeId       int64                       `json:"sameTimeId"`
-	SameTimeType     pasturePb.SameTimeType_Kind `json:"sameTimeType"`
-	SameTimeTypeName string                      `json:"sameTimeTypeName"`
-	DrugsId          int64                       `json:"drugsId"`
-	DrugsName        string                      `json:"drugsName"`
-	Unit             pasturePb.Unit_Kind         `json:"unit"`
-	Usage            int32                       `json:"usage"`
-	Remarks          string                      `json:"remarks"`
-	StallNumberId    int64                       `json:"stallNumberId"`
-	OperationId      int64                       `json:"operation_id"`
-	CreatedAt        int64                       `json:"created_at"`
-	UpdatedAt        int64                       `json:"updated_at"`
-}
-
-func (e *EventSameTime) TableName() string {
-	return "event_same_time"
-}
-
-func NewEventSameTime(cow *Cow, req *pasturePb.EventSameTime, drugs *Drugs) *EventSameTime {
-	eventSameTime := &EventSameTime{
-		CowId:            cow.Id,
-		CalvingAge:       cow.CalvingAge,
-		SameTimeId:       int64(req.SameTimeId),
-		SameTimeType:     req.SameTimeType,
-		SameTimeTypeName: req.SameTimeTypeName,
-		Remarks:          req.Remarks,
-	}
-
-	if drugs != nil && drugs.Id > 0 {
-		eventSameTime.DrugsId = drugs.Id
-		eventSameTime.DrugsName = drugs.Name
-		eventSameTime.Unit = drugs.Unit
-		eventSameTime.Usage = req.Usage
-	}
-	return eventSameTime
-}

Bu fark içinde çok fazla dosya değişikliği olduğu için bazı dosyalar gösterilmiyor