Эх сурвалжийг харах

Merge branch 'feature/event' of xuyiping/kpt-pasture into develop

xuyiping 21 цаг өмнө
parent
commit
8af820bee4
100 өөрчлөгдсөн 5434 нэмэгдсэн , 1567 устгасан
  1. 9 11
      .drone.yml
  2. 1 1
      Dockerfile
  3. 16 7
      README.md
  4. 25 0
      cmd/milk.go
  5. 1 5
      cmd/mqtt.go
  6. 1 0
      cmd/root.go
  7. 40 14
      config/app.develop.yaml
  8. 48 11
      config/app.go
  9. 24 23
      config/app.test.yaml
  10. 2 3
      dep/dep.go
  11. 1 0
      dep/di_alert.go
  12. 7 7
      dep/di_asynq.go
  13. 76 16
      dep/di_crontab.go
  14. 14 20
      dep/di_mqtt.go
  15. 13 13
      docker-compose.yml
  16. 5 3
      go.mod
  17. 140 8
      go.sum
  18. 112 3
      http/handler/analysis/analysis.go
  19. 22 4
      http/handler/config/config.go
  20. 188 1
      http/handler/cow/cow.go
  21. 0 18
      http/handler/dashboard/bar.go
  22. 126 0
      http/handler/dashboard/dashboard.go
  23. 158 9
      http/handler/event/event_base.go
  24. 167 58
      http/handler/event/event_breed.go
  25. 55 9
      http/handler/event/event_health.go
  26. 16 3
      http/handler/goods/drugs.go
  27. 21 1
      http/handler/goods/outbound.go
  28. 9 8
      http/handler/milk/milk.go
  29. 205 1
      http/handler/pasture/pasture.go
  30. 0 98
      http/handler/pasture/seme_time.go
  31. 15 1
      http/handler/system/dept.go
  32. 6 57
      http/handler/system/menu.go
  33. 31 2
      http/handler/system/user.go
  34. 171 0
      http/handler/test.go
  35. 212 27
      http/handler/upload/upload.go
  36. 131 0
      http/handler/warning/warning.go
  37. 1 101
      http/handler/work/calendar.go
  38. 132 0
      http/handler/work/item.go
  39. 6 1
      http/middleware/auth.go
  40. 17 80
      http/middleware/log.go
  41. 22 14
      http/route/analysis_api.go
  42. 18 15
      http/route/config_api.go
  43. 12 2
      http/route/cow_api.go
  44. 0 17
      http/route/dashboard.go
  45. 25 0
      http/route/dashboard_api.go
  46. 21 10
      http/route/event_api.go
  47. 4 0
      http/route/files_api.go
  48. 1 0
      http/route/goods_api.go
  49. 18 0
      http/route/milk_api.go
  50. 12 2
      http/route/pasture_api.go
  51. 1 3
      http/route/root.go
  52. 3 0
      http/route/route.go
  53. 11 9
      http/route/system_api.go
  54. 26 0
      http/route/test_api.go
  55. 25 0
      http/route/warning_api.go
  56. 6 3
      http/route/work_api.go
  57. 0 3
      main.go
  58. 47 0
      model/app_pasture_list.go
  59. 13 0
      model/app_pasture_receiver.go
  60. 38 11
      model/calendar.go
  61. 40 23
      model/calving_calf.go
  62. 672 199
      model/cow.go
  63. 0 58
      model/cow_active_habit.go
  64. 90 0
      model/cow_calving.go
  65. 37 0
      model/cow_lact.go
  66. 15 21
      model/cow_pregnant.go
  67. 0 35
      model/cow_same_time.go
  68. 1 1
      model/crontab_log.go
  69. 96 0
      model/data_notice.go
  70. 252 0
      model/data_waring.go
  71. 199 0
      model/data_warning_items.go
  72. 7 6
      model/disease.go
  73. 3 1
      model/drugs.go
  74. 24 14
      model/event_abortion.go
  75. 5 3
      model/event_body_score.go
  76. 54 28
      model/event_calving.go
  77. 134 62
      model/event_cow_disease.go
  78. 86 35
      model/event_cow_log.go
  79. 50 16
      model/event_cow_same_time.go
  80. 33 12
      model/event_cow_treatment.go
  81. 86 0
      model/event_death.go
  82. 86 0
      model/event_dry_milk.go
  83. 19 4
      model/event_enter.go
  84. 76 49
      model/event_estrus.go
  85. 89 0
      model/event_forbidden_mating.go
  86. 89 49
      model/event_immunization_plan.go
  87. 0 77
      model/event_item.go
  88. 168 52
      model/event_mating.go
  89. 40 9
      model/event_pregnant_check.go
  90. 123 17
      model/event_sale.go
  91. 50 0
      model/event_sale_car.go
  92. 49 0
      model/event_sale_cow.go
  93. 0 20
      model/event_sale_vehicle.go
  94. 38 30
      model/event_transfer_group.go
  95. 40 9
      model/event_weaning.go
  96. 87 9
      model/event_weight.go
  97. 18 3
      model/frozen_semen.go
  98. 13 11
      model/frozen_semen_log.go
  99. 35 0
      model/h_active_original.go
  100. 3 1
      model/immunization_plan.go

+ 9 - 11
.drone.yml

@@ -2,17 +2,15 @@ kind: pipeline
 type: docker
 name: kptPasture
 
-#clone:
-#  depth: 1
-#  disable: true
-
+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:

+ 1 - 1
Dockerfile

@@ -15,7 +15,7 @@ RUN go env -w GO111MODULE=on && \
 
 FROM alpine:latest
 LABEL name="kpt-pasture" \
-description="pt service" \
+description="pasture service" \
 owner="yiping.xu"
 
 WORKDIR /app/kpt-pasture

+ 16 - 7
README.md

@@ -31,17 +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列表:
 - [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] 犊牛的牛只品种是根据母牛的品种来确定,还是根据公牛来确定?【目前是根据母牛品种来确定】
 
 脖环发情算法梳理:
-- [ ] 处理异常上报数据(frameid > 12)
+- [x] 处理异常上报数据(frameid > 12)

+ 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)
+}

+ 1 - 5
cmd/mqtt.go

@@ -1,7 +1,6 @@
 package cmd
 
 import (
-	"kpt-pasture/config"
 	"kpt-pasture/dep"
 
 	"github.com/spf13/cobra"
@@ -11,9 +10,6 @@ var MqttCmd = &cobra.Command{
 	Use:   "mqtt",
 	Short: "mqtt server",
 	Run: func(cmd *cobra.Command, args []string) {
-		conf := config.Options().Mqtt
-		dataEvent := dep.DIMqtt()
-		mqttClient := dataEvent.DataEventEntry.NewMqtt(conf)
-		dataEvent.DataEventEntry.SubMsg(conf, mqttClient)
+		dep.DIMqttService()
 	},
 }

+ 1 - 0
cmd/root.go

@@ -24,4 +24,5 @@ func init() {
 	RootCmd.AddCommand(CrontabCmd)
 	RootCmd.AddCommand(ConsumerCmd)
 	RootCmd.AddCommand(MqttCmd)
+	RootCmd.AddCommand(MilkCmd)
 }

+ 40 - 14
config/app.develop.yaml

@@ -5,14 +5,14 @@ debug: true
 http_server_addr: ':8090'
 http_metrics_addr: ':23332'
 jwt_expire_time: 172800
-neck_ring_limit: 10000
+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'
@@ -37,20 +37,31 @@ side_work_setting:
 cron:
   crontab_start_run: false
   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 * * ?"
+  same_Time_plan: "0 15 1 * * ?"
   update_same_time: "0 20 1 * * ?"
-  system_basic_crontab: "0 25 1 * * ?"
-  cow_pregnant: "0 00 15 * * ?"
-  neck_ring: "0 30 * * * ?"
+  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: "47.92.95.119"
-  port: 1883
-  username: ""
-  password: ""
-  client_id: "ping"
-  topic: "a"
+  broker: "kptyun.com:1983"
+  username: "kptmqtt"
+  password: "kepaiteng"
+  sub_topic: "/user/heatwatch/neckring/post"
   qos: 0
   retain: false
   keep_alive: 60
@@ -58,3 +69,18 @@ mqtt:
   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"
+
+
+

+ 48 - 11
config/app.go

@@ -44,20 +44,37 @@ type AppConfig struct {
 	SideWorkSetting SideWorkSetting `yaml:"side_work_setting"`
 	CronSetting     CronSetting     `json:"cron_setting" yaml:"cron"`
 	Mqtt            MqttSetting     `json:"mqtt"`
+	MilkHall        MilkHallSetting `json:"milkHall"`
+
+	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"`       //  同期
-	UpdateSameTime     string `yaml:"update_same_time"`     //  更新同期
-	SystemBasicCrontab string `yaml:"system_basic_crontab"` //  系统基础定时任务
-	CowPregnant        string `yaml:"cow_pregnant"`         //  月度牛只怀孕清单
-	NeckRing           string `yaml:"neck_ring"`            //  脖环数据更新
+	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 {
@@ -82,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 {
@@ -159,11 +177,9 @@ type AsynqRedisSetting struct {
 
 type MqttSetting struct {
 	Broker            string `json:"broker" yaml:"broker"`
-	Port              int    `json:"port" yaml:"port"`
 	UserName          string `json:"username" yaml:"username"`
 	Password          string `json:"password" yaml:"password"`
-	ClientId          string `json:"client" yaml:"client_id"`
-	Topic             string `json:"topic"  yaml:"topic"`
+	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"`
@@ -171,6 +187,27 @@ type MqttSetting struct {
 	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 {

+ 24 - 23
config/app.test.yaml

@@ -4,13 +4,14 @@ debug: true
 http_server_addr: ':8090'
 http_metrics_addr: ':23332'
 jwt_expire_time: 172800
-neck_ring_limit: 10000
+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:
@@ -22,38 +23,38 @@ redis_setting:
 jwt_secret: "sUd7j%UfJMt59ywh"
 cache_key_suffix: "gmym"
 
-side_work_setting:
-  asynq_setting:
-    redis:
-      addr: '47.92.95.119:6389'
-      db: 0
-      pool_size: 10
-    concurrency: 5
-    queues:
-      work: 20
-      low: 10
-      default: 5
 cron:
   crontab_start_run: false
   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 * * ?"
+  same_Time_plan: "0 15 1 * * ?"
   update_same_time: "0 20 1 * * ?"
-  system_basic_crontab: "0 25 1 * * ?"
-  cow_pregnant: "0 00 15 * * ?"
-  neck_ring: "0 30 * * * ?"
+  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: "47.92.95.119"
-  port: 1883
-  username: ""
-  password: ""
-  client_id: "ping"
-  topic: "a"
+  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
+  work_number: 1
+  merge_data_ticker: 2   # 2分钟合并一次数据

+ 2 - 3
dep/dep.go

@@ -7,11 +7,11 @@ import (
 	"kpt-pasture/module/crontab"
 	moduleMqtt "kpt-pasture/module/mqtt"
 	"kpt-pasture/service/asynqsvc"
-	"kpt-pasture/service/mqtt"
 	"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"
 )
@@ -38,8 +38,7 @@ func Options() []di.HubOption {
 		asynq.Module,
 		redis.Module,
 		crontab.Module,
-		mqtt.Module,
-		//mqtt2.Module,
 		moduleMqtt.Module,
+		mqttstore.Module,
 	}
 }

+ 1 - 0
dep/di_alert.go

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

+ 7 - 7
dep/di_asynq.go

@@ -21,6 +21,13 @@ func DIAsynqWorkOrder() (out *asynqsvc.Server) {
 	return
 }
 
+// AsyncDependency is the dependency for worker and kafka
+type AsyncDependency struct {
+	dig.In
+
+	WorkOrder asynq.BizExec // BizExec 工单
+}
+
 // AsynqWorkOrder 相关消费
 func AsynqWorkOrder(dep AsyncDependency) *asynqsvc.Server {
 	cfg := config.Options()
@@ -30,10 +37,3 @@ func AsynqWorkOrder(dep AsyncDependency) *asynqsvc.Server {
 	srv.Mux.HandleFunc(pattern, dep.WorkOrder.DayWorkOrder) // 工单
 	return srv
 }
-
-// AsyncDependency is the dependency for worker and kafka
-type AsyncDependency struct {
-	dig.In
-
-	WorkOrder asynq.BizExec // BizExec 工单
-}

+ 76 - 16
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"
@@ -42,44 +45,101 @@ func EntryCrontab(dependency CrontabDependency) *cron.Crontab {
 	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)
 	}
 
-	err = newCrontab.Bind("SameTimePlan", cs.SameTimePlan, dependency.CrontabHub.SameTimePlan)
-	if err != nil {
+	if err := newCrontab.Bind("TwentyOnePregnantRate", cs.TwentyOnePregnantRate, dependency.CrontabHub.TwentyOnePregnantRate); err != nil {
 		panic(err)
 	}
 
-	err = newCrontab.Bind("UpdateSameTime", cs.UpdateSameTime, dependency.CrontabHub.UpdateSameTime)
-	if err != nil {
+	if err := newCrontab.Bind("SameTimePlan", cs.SameTimePlan, dependency.CrontabHub.SameTimePlan); err != nil {
 		panic(err)
 	}
 
-	err = newCrontab.Bind("SystemBasicCrontab", cs.SystemBasicCrontab, dependency.CrontabHub.SystemBasicCrontab)
-	if err != nil {
+	if err := newCrontab.Bind("UpdateSameTime", cs.UpdateSameTime, dependency.CrontabHub.UpdateSameTime); err != nil {
 		panic(err)
 	}
 
-	err = newCrontab.Bind("CowPregnant", cs.CowPregnant, dependency.CrontabHub.CowPregnant)
-	if err != nil {
+	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)
 	}
 
-	err = newCrontab.Bind("NeckRing", cs.NeckRing, dependency.CrontabHub.NeckRingMergeData)
+	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("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
 }

+ 14 - 20
dep/di_mqtt.go

@@ -1,40 +1,34 @@
 package dep
 
 import (
+	"kpt-pasture/config"
+	mqttHandle "kpt-pasture/module/mqtt"
 	"kpt-pasture/service/mqtt"
 
 	"go.uber.org/dig"
 )
 
-func DIMqtt() (out MqttDependency) {
+func DIMqttService() (out mqtt.IMqttServer) {
 	container := DI()
-	if err := container.Invoke(func(c MqttDependency) { out = c }); err != nil {
+	if err := container.Provide(MqttHandel); err != nil {
 		panic(err)
 	}
-	return
-}
-
-type MqttDependency struct {
-	dig.In
-
-	DataEventEntry mqtt.DataEvent
-}
-
-/*func DIMqtt() (out MqttDependency) {
-	container := DI()
-	if err := container.Provide(MqttDependency); err != nil {
-		panic(err)
-	}
-	if err := container.Invoke(func(c *mqtt2.MqttServer) { out = c }); err != nil {
+	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
 
-	//DataEventEntry mqtt.DataEvent
-	MqttClient mqtt2.MqttServer
+	MqttHub mqttHandle.Entry // 处理数据
 }
-*/

+ 13 - 13
docker-compose.yml

@@ -29,16 +29,16 @@ services:
       - APP_ENVIRONMENT=production
       - PASTURE_WORK_DIR=/app/kpt-pasture
     command: [ "/app/kpt-pasture/kptPasture","http" ]
-  #kpt-pasture-consumer:
-    #privileged: true
-    #container_name: xdmy001_kpt_pasture_consumer
-    #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
-      #- PASTURE_WORK_DIR=/app/kpt-pasture
-    #command: [ "/app/kpt-pasture/kptPasture","consumer" ]
+  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
+      - PASTURE_WORK_DIR=/app/kpt-pasture
+    command: [ "/app/kpt-pasture/kptPasture","mqtt" ]

+ 5 - 3
go.mod

@@ -3,8 +3,8 @@ module kpt-pasture
 go 1.17
 
 require (
-	gitee.com/xuyiping_admin/go_proto v0.0.0-20241204021755-ac6c028d07a3
-	gitee.com/xuyiping_admin/pkg v0.0.0-20241108060137-caea58c59f5b
+	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
@@ -33,7 +33,9 @@ require (
 )
 
 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
 )
@@ -62,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

+ 140 - 8
go.sum

@@ -36,16 +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-20241128102506-727966a0f004 h1:0kHmrqRNqiJuzIGIk+dkAsTd/7iAlPRKPYN5h2aaeTo=
-gitee.com/xuyiping_admin/go_proto v0.0.0-20241128102506-727966a0f004/go.mod h1:BKrFW6YLDectlQcQk3FYKBeXvjEiodAKJ5rq7O/QiPE=
-gitee.com/xuyiping_admin/go_proto v0.0.0-20241203073346-ba8687d6c8de h1:dIATo9IIIOfcQaSISrclzRF0piVoy7bydeG0psN1Lt0=
-gitee.com/xuyiping_admin/go_proto v0.0.0-20241203073346-ba8687d6c8de/go.mod h1:BKrFW6YLDectlQcQk3FYKBeXvjEiodAKJ5rq7O/QiPE=
-gitee.com/xuyiping_admin/go_proto v0.0.0-20241204014219-67b3a86c6073 h1:9lzMO1lwmQfgGRmpZkTLHjGcmU3hc6+uMOgEuPiyuuo=
-gitee.com/xuyiping_admin/go_proto v0.0.0-20241204014219-67b3a86c6073/go.mod h1:BKrFW6YLDectlQcQk3FYKBeXvjEiodAKJ5rq7O/QiPE=
-gitee.com/xuyiping_admin/go_proto v0.0.0-20241204021755-ac6c028d07a3 h1:05zSqGf+j1PFXULInvcc610amPNWH+CJk9Jd9h/kHhs=
-gitee.com/xuyiping_admin/go_proto v0.0.0-20241204021755-ac6c028d07a3/go.mod h1:BKrFW6YLDectlQcQk3FYKBeXvjEiodAKJ5rq7O/QiPE=
+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=
@@ -68,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=
@@ -422,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=

+ 112 - 3
http/handler/analysis/analysis.go

@@ -12,14 +12,20 @@ import (
 	"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
@@ -42,7 +48,13 @@ func WeightRange(c *gin.Context) {
 		return
 	}
 
-	res, err := middleware.Dependency(c).StoreEventHub.OpsService.WeightRange(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.WeightRange(c, &req, pagination)
 	if err != nil {
 		apierr.ClassifiedAbort(c, err)
 		return
@@ -121,6 +133,31 @@ func TwentyOnePregnantRate(c *gin.Context) {
 	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 {
@@ -301,3 +338,75 @@ func MultiFactorInfantSurvivalRate(c *gin.Context) {
 
 	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)
+}

+ 22 - 4
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"
@@ -116,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
@@ -153,3 +153,21 @@ func SystemBaseConfigOptions(c *gin.Context) {
 	}
 	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)
+}

+ 158 - 9
http/handler/event/event_base.go

@@ -48,10 +48,12 @@ func EnterEventCreate(c *gin.Context) {
 		valid.Field(&req.CowType, valid.Required),
 		valid.Field(&req.CowKind, valid.Required),
 		valid.Field(&req.CowSource, valid.Required),
-		valid.Field(&req.Lact, valid.Required),
+		valid.Field(&req.Price, valid.Required),
 		valid.Field(&req.EnterAt, valid.Required),
-		valid.Field(&req.CowSource, 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
@@ -101,7 +103,7 @@ 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),
@@ -196,20 +198,20 @@ func WeightList(c *gin.Context) {
 }
 
 func WeightBatch(c *gin.Context) {
-	var req pasturePb.EventWeight
+	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.WeightAt, valid.Required),
-		valid.Field(&req.OperationId, 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.CowId, valid.Required),
+				valid.Field(&s.EarNumber, valid.Required),
+				valid.Field(&s.WeightAt, valid.Required),
+				valid.Field(&s.OperationId, valid.Required),
 			)
 		}))),
 	); err != nil {
@@ -228,3 +230,150 @@ func WeightBatch(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)
+}

+ 167 - 58
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),
@@ -101,10 +101,10 @@ func PregnantCheckEventCreateBatch(c *gin.Context) {
 	}
 
 	if err := valid.ValidateStruct(&req,
-		valid.Field(&req.Item, valid.Required, valid.Each(valid.By(func(value interface{}) error {
+		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.CowId, valid.Required),
+				valid.Field(&item.EarNumber, valid.Required),
 				valid.Field(&item.PregnantCheckAt, valid.Required),
 				valid.Field(&item.PregnantCheckResult, valid.Required),
 				valid.Field(&item.PregnantCheckMethod, valid.Required),
@@ -149,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.CowIds, valid.Required),
-		valid.Field(&req.OperationId, valid.Required),
-		valid.Field(&req.FrozenSemenNumber, valid.Required),
-		valid.Field(&req.MatingAt, valid.Required),
-		valid.Field(&req.FrozenSemenCount, valid.Required, valid.Min(1)),
+		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
 	}
@@ -200,44 +205,21 @@ 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.OperationId, valid.Required),
-		valid.Field(&req.EstrusAt, valid.Required),
-	); err != nil {
-		apierr.AbortBadRequest(c, http.StatusBadRequest, err)
-		return
-	}
-
-	if err := middleware.BackendOperation(c).OpsService.EstrusCreate(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 EstrusBatch(c *gin.Context) {
-	var req pasturePb.EventEstrusBatch
-	if err := valid.ValidateStruct(&req,
-		valid.Field(&req.Item, valid.Required, valid.Each(valid.By(func(value interface{}) error {
-			item := value.(pasturePb.EventEstrus)
+		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.CowId, valid.Required),
+				valid.Field(&item.EarNumber, valid.Required),
+				valid.Field(&item.EstrusAt, valid.Required),
+				valid.Field(&item.IsMating, valid.Required),
 				valid.Field(&item.OperationId, valid.Required),
-				valid.Field(&item.IsMathing, valid.Required),
-				valid.Field(&item.UnMatingReasons, valid.Required),
 			)
 		}))),
 	); err != nil {
@@ -245,7 +227,7 @@ func EstrusBatch(c *gin.Context) {
 		return
 	}
 
-	if err := middleware.BackendOperation(c).OpsService.EstrusBatch(c, &req); err != nil {
+	if err := middleware.BackendOperation(c).OpsService.EstrusBatchMating(c, &req); err != nil {
 		apierr.ClassifiedAbort(c, err)
 		return
 	}
@@ -256,6 +238,7 @@ func EstrusBatch(c *gin.Context) {
 		Data: &operationPb.Success{Success: true},
 	})
 }
+
 func SameTimeCreate(c *gin.Context) {
 	var req pasturePb.EventSameTime
 	if err := ginutil.BindProto(c, &req); err != nil {
@@ -296,12 +279,7 @@ func SameTimeBatch(c *gin.Context) {
 		valid.Field(&req.OperationId, valid.Required),
 		valid.Field(&req.DrugsId, valid.Required),
 		valid.Field(&req.SameTimeAt, valid.Required),
-		valid.Field(&req.Item, valid.Required, valid.Each(valid.By(func(value interface{}) error {
-			item := value.(pasturePb.EventSameTimeItem)
-			return valid.ValidateStruct(&item,
-				valid.Field(&item.CowId, valid.Required),
-			)
-		}))),
+		valid.Field(&req.CowIds, valid.Required),
 	); err != nil {
 		apierr.AbortBadRequest(c, http.StatusBadRequest, err)
 		return
@@ -348,10 +326,10 @@ func AbortionCreateBatch(c *gin.Context) {
 	}
 
 	if err := valid.ValidateStruct(&req,
-		valid.Field(&req.Item, valid.Required, valid.Each(valid.By(func(value interface{}) error {
-			item := value.(pasturePb.EventAbortionRequest)
+		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.CowId, valid.Required),
+				valid.Field(&item.EarNumber, valid.Required),
 				valid.Field(&item.IsAfterbirth, valid.Required),
 				valid.Field(&item.AbortionReasons, valid.Required),
 				valid.Field(&item.AbortionAt, valid.Required),
@@ -397,21 +375,21 @@ func AbortionList(c *gin.Context) {
 }
 
 func WeaningCreateBatch(c *gin.Context) {
-	var req pasturePb.EventWeaningBatchRequest
+	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.WeaningAt, valid.Required),
-		valid.Field(req.OperationId, valid.Required),
-		valid.Field(req.PenId, valid.Required),
-		valid.Field(&req.Item, valid.Required, valid.Each(valid.By(func(value interface{}) error {
-			item := value.(pasturePb.WeaningBatch)
+		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.CowId, valid.Required),
+				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 {
@@ -423,6 +401,137 @@ func WeaningCreateBatch(c *gin.Context) {
 		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
+	}
+
+	ginutil.JSONResp(c, &operationPb.CommonOK{
+		Code: http.StatusOK,
+		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 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,

+ 55 - 9
http/handler/event/event_health.go

@@ -21,23 +21,17 @@ func CowDiseaseCreate(c *gin.Context) {
 	}
 
 	if err := valid.ValidateStruct(&req,
-		valid.Field(&req.CowId, valid.Required),
+		valid.Field(&req.EarNumber, valid.Required),
 		valid.Field(&req.DiseaseAt, valid.Required),
 		valid.Field(&req.DiseaseId, valid.Required),
 		valid.Field(&req.OperationId, valid.Required),
-		valid.Field(&req.PrescriptionDetail, valid.Required, valid.Each(valid.By(func(value interface{}) error {
-			item := value.(pasturePb.TreatmentDrugs)
-			return valid.ValidateStruct(&item,
-				valid.Field(&item.DrugsId, valid.Required),
-				valid.Field(&item.UseNum, valid.Required),
-			)
-		}))),
 	); err != nil {
 		apierr.AbortBadRequest(c, http.StatusBadRequest, err)
 		return
 	}
 
-	if err := middleware.BackendOperation(c).OpsService.CowDiseaseCreate(c, &req); err != nil {
+	source := c.GetHeader("Source")
+	if err := middleware.BackendOperation(c).OpsService.CowDiseaseCreate(c, &req, source); err != nil {
 		apierr.ClassifiedAbort(c, err)
 		return
 	}
@@ -157,6 +151,7 @@ func CowDiseaseTreatmentDetail(c *gin.Context) {
 
 	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
@@ -204,3 +199,54 @@ func CowDiseaseCurable(c *gin.Context) {
 		Data: &operationPb.Success{Success: true},
 	})
 }
+
+func ImmunizationList(c *gin.Context) {
+	var req pasturePb.SearchEventImmunizationRequest
+	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.ImmunizationList(c, &req, pagination)
+	if err != nil {
+		apierr.ClassifiedAbort(c, err)
+		return
+	}
+	ginutil.JSONResp(c, res)
+}
+
+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.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.Dependency(c).StoreEventHub.OpsService.ImmunizationBatch(c, &req); err != nil {
+		apierr.ClassifiedAbort(c, err)
+		return
+	}
+
+	ginutil.JSONResp(c, &operationPb.CommonOK{
+		Code: http.StatusOK,
+		Msg:  "ok",
+		Data: &operationPb.Success{Success: true},
+	})
+}

+ 16 - 3
http/handler/goods/drugs.go

@@ -133,7 +133,7 @@ func NeckRingList(c *gin.Context) {
 		PageOffset: int32(c.GetInt(middleware.PageOffset)),
 	}
 
-	res, err := middleware.Dependency(c).StoreEventHub.OpsService.NeckRingLogList(c, &req, pagination)
+	res, err := middleware.Dependency(c).StoreEventHub.OpsService.NeckRingList(c, &req, pagination)
 	if err != nil {
 		apierr.ClassifiedAbort(c, err)
 		return
@@ -149,13 +149,26 @@ func NeckRingCreateOrUpdate(c *gin.Context) {
 	}
 
 	if err := valid.ValidateStruct(&req,
-		valid.Field(&req.Items, valid.Required),
+		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.NeckRingLogCreateOrUpdate(c, &req); err != nil {
+	if err := middleware.BackendOperation(c).OpsService.NeckRingCreateOrUpdate(c, &req); err != nil {
 		apierr.ClassifiedAbort(c, err)
 		return
 	}

+ 21 - 1
http/handler/goods/outbound.go

@@ -89,7 +89,7 @@ func OutboundAudit(c *gin.Context) {
 }
 
 func OutboundDetail(c *gin.Context) {
-	idStr := c.Query("id")
+	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)
@@ -105,6 +105,26 @@ func OutboundDetail(c *gin.Context) {
 	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 {

+ 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",

+ 205 - 1
http/handler/pasture/prescription.go → http/handler/pasture/pasture.go

@@ -10,6 +10,8 @@ import (
 	"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"
 )
 
@@ -156,6 +158,23 @@ func CreatedOrUpdatePrescription(c *gin.Context) {
 	})
 }
 
+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 {
@@ -169,7 +188,7 @@ func SearchImmunizationList(c *gin.Context) {
 		PageOffset: int32(c.GetInt(middleware.PageOffset)),
 	}
 
-	res, err := middleware.Dependency(c).StoreEventHub.OpsService.ImmunizationList(c, &req, pagination)
+	res, err := middleware.Dependency(c).StoreEventHub.OpsService.ImmunizationSetList(c, &req, pagination)
 	if err != nil {
 		apierr.ClassifiedAbort(c, err)
 		return
@@ -223,3 +242,188 @@ func ImmunizationIsShow(c *gin.Context) {
 		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},
+	})
+}

+ 1 - 101
http/handler/work/calendar.go

@@ -80,7 +80,7 @@ func CalendarToDoList(c *gin.Context) {
 		apierr.ClassifiedAbort(c, err)
 		return
 	}
-	c.JSON(http.StatusOK, res)
+	ginutil.JSONResp(c, res)
 }
 
 func ImmunizationItems(c *gin.Context) {
@@ -102,103 +102,3 @@ func ImmunizationItems(c *gin.Context) {
 	}
 	ginutil.JSONResp(c, res)
 }
-
-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)
-}

+ 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)
+}

+ 6 - 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,6 +34,10 @@ 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))
 }
@@ -63,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()
 	}

+ 17 - 80
http/middleware/log.go

@@ -2,12 +2,9 @@ package middleware
 
 import (
 	"bytes"
-	"io"
 	"io/ioutil"
 	"net/http"
-	"runtime"
 	"runtime/debug"
-	"strings"
 	"time"
 
 	"gitee.com/xuyiping_admin/pkg/logger/zaplog"
@@ -20,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)
@@ -46,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{
@@ -58,6 +46,7 @@ func GinLogger() gin.HandlerFunc {
 			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())))
@@ -74,75 +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)),
-					)
-				}*/
-				defer func() {
-					if err = recover(); err != nil {
-						body, _ := ioutil.ReadAll(c.Request.Body)
-
-						// 获取 panic 发生的位置
-						pc, file, line, ok := runtime.Caller(2)
-						funcName := ""
-						if ok {
-							fn := runtime.FuncForPC(pc).Name()
-							// 去除包路径,只保留函数名
-							/*funcName = filepath.Base(fn)
-							file = filepath.Base(file)*/
-							parts := strings.Split(fn, "/")
-							if len(parts) > 0 {
-								lastPart := parts[len(parts)-1]
-								parts = strings.Split(lastPart, ".")
-								if len(parts) > 0 {
-									funcName = parts[len(parts)-1]
-								}
-							}
-							file = strings.TrimPrefix(file, c.Request.Context().Value(gin.ContextKey).(string)+"/") // 尝试去除项目路径前缀(可选)
-						}
-						zaplog.Error("cors",
-							zap.Any("recover", err),
-							zap.Any("url", c.Request.URL),
-							zap.Any("file", file),
-							zap.Any("line", line),
-							zap.Any("func", funcName),
-							zap.Any("request", string(body)),
-						)
-						c.Request.Body = io.NopCloser(bytes.NewBuffer(body))
-					}
-				}()
+					// 获取调用栈
+					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()

+ 22 - 14
http/route/analysis_api.go

@@ -11,19 +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)
-		pastureRoute.POST("/weight/range", analysis.WeightRange)
-		pastureRoute.POST("/mating/timely", analysis.MatingTimeLy)
-		pastureRoute.POST("/pen/weight", analysis.PenWeight)
-		pastureRoute.POST("/abortion/rate", analysis.AbortionRate)
-		pastureRoute.POST("/twenty/one/pregnant/rate", analysis.TwentyOnePregnantRate)
-		pastureRoute.POST("/pregnancy/report", analysis.PregnancyReport)
-		pastureRoute.POST("/calving/report", analysis.CalvingReport)
-		pastureRoute.POST("/disease/cure/report", analysis.DiseaseCureReport)
-		pastureRoute.POST("/sale/cow/report", analysis.SaleCowReport)
-		pastureRoute.POST("/single/factor/pregnant/report", analysis.SingleFactorInfantSurvivalRate)
-		pastureRoute.POST("/multi/factor/pregnant/report", analysis.MultiFactorInfantSurvivalRate)
+		// 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 - 15
http/route/config_api.go

@@ -11,20 +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)
-		pastureRoute.GET("/prescription/options", config.PrescriptionOptions)
+		// 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)
+	}
+}

+ 21 - 10
http/route/event_api.go

@@ -19,15 +19,12 @@ func EventAPI(opts ...func(engine *gin.Engine)) func(s *gin.Engine) {
 		// 转群
 		eventRoute.POST("/group/transfer/list", event.GroupTransferEventList)
 		eventRoute.POST("/group/transfer/create", event.GroupTransferEventCreate)
-
 		// 体况评分
 		eventRoute.POST("/body/score/list", event.BodyScoreEventList)
 		eventRoute.POST("/body/score/create", event.BodyScoreEventCreate)
-
 		// 称重
 		eventRoute.POST("/weight/list", event.WeightList)
 		eventRoute.POST("/weight/batch", event.WeightBatch)
-
 		// 产犊
 		eventRoute.POST("/calving/list", event.CalvingEventList)
 		eventRoute.POST("/calving/create", event.CalvingEventCreate)
@@ -36,26 +33,22 @@ func EventAPI(opts ...func(engine *gin.Engine)) func(s *gin.Engine) {
 		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("/estrus/batch", event.EstrusBatch)
+		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)
 		// 疾病治疗
@@ -65,5 +58,23 @@ func EventAPI(opts ...func(engine *gin.Engine)) func(s *gin.Engine) {
 		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/")
 	}
 }

+ 1 - 0
http/route/goods_api.go

@@ -28,6 +28,7 @@ func GoodsManageAPI(opts ...func(engine *gin.Engine)) func(s *gin.Engine) {
 		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)

+ 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)
+	}
+}

+ 6 - 3
http/route/work_api.go

@@ -14,14 +14,15 @@ func WorkOrderAPI(opts ...func(engine *gin.Engine)) func(s *gin.Engine) {
 		}
 		// work API 组  工作清单
 		workRoute := authRouteGroup(s, "/api/v1/work/")
-		workRoute.POST("/order/list", work.OrderList)
+		/*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.GET("/user/order/list/:status", work.UserOrderList)*/
 
+		// 日历
 		workRoute.POST("/calendar/list", work.CalendarList)
-		workRoute.POST("/calendar/detail", work.CalendarTableDetail)
 		workRoute.POST("/calendar/todo/list", work.CalendarToDoList)
+		workRoute.POST("/calendar/detail", work.CalendarTableDetail)
 
 		// 清单相关接口
 		workRoute.POST("/same/time/items", work.SameTimeCowList)
@@ -32,5 +33,7 @@ func WorkOrderAPI(opts ...func(engine *gin.Engine)) func(s *gin.Engine) {
 		workRoute.POST("/calving/items", work.CalvingList)
 		workRoute.POST("/disease/items", event.CowDiseaseList)
 
+		// 任务明细
+		workRoute.POST("/task/detail", work.TaskDetailList)
 	}
 }

+ 0 - 3
main.go

@@ -1,6 +1,3 @@
-/*
-Copyright © 2022 NAME HERE <EMAIL ADDRESS>
-*/
 package main
 
 import (

+ 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"
+}

+ 38 - 11
model/calendar.go

@@ -2,17 +2,18 @@ package model
 
 import (
 	"fmt"
-	"time"
 
 	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"`
-	ShowDay      string                      `json:"showDay"`
+	StartDay     string                      `json:"startDay"`
+	EndDay       string                      `json:"endDay"`
 	IsShow       pasturePb.IsShow_Kind       `json:"isShow"`
 	Backup       string                      `json:"backup"`
 	CreatedAt    int64                       `json:"createdAt"`
@@ -23,23 +24,42 @@ func (w *Calendar) TableName() string {
 	return "calendar"
 }
 
-func NewCalendar(name string, calendarType pasturePb.CalendarType_Kind, count int32) *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,
-		ShowDay:      time.Now().Format(LayoutDate2),
+		StartDay:     startDay,
+		EndDay:       endDay,
 		IsShow:       pasturePb.IsShow_Ok,
 	}
 }
 
 var CalendarTypeColorMap = map[pasturePb.CalendarType_Kind]string{
-	pasturePb.CalendarType_Immunisation:    "#85c1e9",
-	pasturePb.CalendarType_PG:              "#48C9B0",
-	pasturePb.CalendarType_RnGH:            "#d35400",
-	pasturePb.CalendarType_Pregnancy_Check: "#8E44AD",
+	pasturePb.CalendarType_Immunisation:    "#00B42A",
+	pasturePb.CalendarType_PG:              "#722ED1",
+	pasturePb.CalendarType_RnGH:            "#722ED1",
+	pasturePb.CalendarType_Pregnancy_Check: "#F5319D",
 	pasturePb.CalendarType_WorkOrder:       "#5d6d7e",
-	pasturePb.CalendarType_Treatment:       "#c0392b",
+	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
@@ -52,13 +72,20 @@ func (c CalendarSlice) ToPB() []*pasturePb.Calendar {
 			Title:   fmt.Sprintf("%s - %d", v.Name, v.Count),
 			GroupId: v.CalendarType,
 			Count:   v.Count,
-			Start:   v.ShowDay,
+			Start:   v.StartDay,
+			End:     v.EndDay,
 			Color:   CalendarTypeColorMap[v.CalendarType],
 			ExtendedProps: &pasturePb.ExtendedProps{
-				StartDay: v.ShowDay,
+				StartDay: v.StartDay,
 				Backup:   v.Backup,
 			},
 		}
 	}
 	return res
 }
+
+type CompletedData struct {
+	Count            int32
+	CalendarTypeKind pasturePb.CalendarType_Kind
+	CalendarTypeName string
+}

+ 40 - 23
model/calving_calf.go

@@ -2,8 +2,11 @@ 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"`
@@ -15,38 +18,52 @@ type CalvingCalf struct {
 	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,
-			CowId:         int64(v.CowId),
-			BirthAt:       int64(req.CalvingAt),
-			PenId:         v.PenId,
-			BirthWeight:   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
 }

+ 672 - 199
model/cow.go

@@ -2,7 +2,6 @@ package model
 
 import (
 	"fmt"
-	"kpt-pasture/util"
 	"math"
 	"time"
 
@@ -10,78 +9,396 @@ import (
 )
 
 type Cow struct {
-	Id                  int64                          `json:"id"`
-	Sex                 pasturePb.Genders_Kind         `json:"sex"`
-	NeckRingNumber      string                         `json:"neckRingNumber"`
-	EarNumber           string                         `json:"earNumber"`
-	EarOldNumber        string                         `json:"earOldNumber"`
-	PenId               int32                          `json:"penId"`
-	Lact                int32                          `json:"lact"`
-	DayAge              int32                          `json:"dayAge"`
-	CalvingAge          int64                          `json:"calvingAge"`
-	PregnancyAge        int32                          `json:"pregnancyAge"` // 怀孕天数 孕检结果有阳性更新,产犊后至0
-	AdmissionAge        int32                          `json:"admissionAge"`
-	AbortionAge         int64                          `json:"abortionAge"` // 流产天数
-	CowType             pasturePb.CowType_Kind         `json:"cowType"`
-	BreedStatus         pasturePb.BreedStatus_Kind     `json:"breedStatus"`
-	CowKind             pasturePb.CowKind_Kind         `json:"cowKind"`
-	BirthWeight         int64                          `json:"birthWeight"`
-	CurrentWeight       int64                          `json:"currentWeight"`
-	AdmissionWeight     int64                          `json:"admissionWeight"`
-	SourceId            pasturePb.CowSource_Kind       `json:"sourceId"`
-	FatherNumber        string                         `json:"fatherNumber"`
-	MotherNumber        string                         `json:"motherNumber"`
-	AdmissionStatus     pasturePb.AdmissionStatus_Kind `json:"admissionStatus"`
-	IsPregnant          pasturePb.IsShow_Kind          `json:"isPregnant"`
-	HealthStatus        pasturePb.HealthStatus_Kind    `json:"healthStatus"`
-	WeaningAt           int64                          `json:"weaningAt"`
-	BirthAt             int64                          `json:"birthAt"`
-	AdmissionAt         int64                          `json:"admissionAt"`
-	FirstMatingAt       int64                          `json:"firstMatingAt"`
-	MatingTimes         int32                          `json:"matingTimes"`
-	LastEstrusAt        int64                          `json:"lastEstrusAt"`
-	LastCalvingAt       int64                          `json:"lastCalvingAt"`
-	LastMatingAt        int64                          `json:"lastMatingAt"`
-	LastBullNumber      string                         `json:"lastBullNumber"`
-	LastPregnantCheckAt int64                          `json:"lastPregnantCheckAt"`
-	LastDryMilkAt       int64                          `json:"lastDryMilkAt"`
-	LastSecondWeight    int64                          `json:"lastSecondWeight"`
-	LastSecondWeightAt  int64                          `json:"lastSecondWeightAt"`
-	LastAbortionAt      int64                          `json:"lastAbortionAt"`
-	LastWeightAt        int64                          `json:"lastWeightAt"`
-	CreatedAt           int64                          `json:"createdAt"`
-	UpdatedAt           int64                          `json:"updatedAt"`
+	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(
-	penMap map[int32]*Pen,
 	cowTypeMap map[pasturePb.CowType_Kind]string,
 	breedStatusMap map[pasturePb.BreedStatus_Kind]string,
 	cowKindMap map[pasturePb.CowKind_Kind]string,
 	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 := ""
-		if pen, ok := penMap[v.PenId]; ok {
-			penName = pen.Name
-		}
-
 		sex := "公"
 		if v.Sex == pasturePb.Genders_Female {
 			sex = "母"
 		}
 		lastWeightAtFormat := ""
 		if v.LastWeightAt > 0 {
-			lastWeightAtFormat = time.Unix(v.LastWeightAt, 0).Format(LayoutDate2)
+			lastWeightAtFormat = time.Unix(v.LastWeightAt, 0).Local().Format(LayoutDate2)
 		}
 
 		isPregnantName := ""
@@ -93,111 +410,191 @@ func (c CowSlice) ToPB(
 
 		admissionAtFormat := ""
 		if v.AdmissionAt > 0 {
-			admissionAtFormat = time.Unix(v.AdmissionAt, 0).Format(LayoutDate2)
+			admissionAtFormat = time.Unix(v.AdmissionAt, 0).Local().Format(LayoutDate2)
 		}
 
 		birthAtFormat := ""
 		if v.BirthAt > 0 {
-			birthAtFormat = time.Unix(v.BirthAt, 0).Format(LayoutDate2)
+			birthAtFormat = time.Unix(v.BirthAt, 0).Local().Format(LayoutDate2)
 		}
 
 		weaningAtFormat := ""
 		if v.WeaningAt > 0 {
-			weaningAtFormat = time.Unix(v.WeaningAt, 0).Format(LayoutDate2)
+			weaningAtFormat = time.Unix(v.WeaningAt, 0).Local().Format(LayoutDate2)
 		}
 
 		firstMatingAtFormat := ""
 		if v.FirstMatingAt > 0 {
-			firstMatingAtFormat = time.Unix(v.FirstMatingAt, 0).Format(LayoutDate2)
+			firstMatingAtFormat = time.Unix(v.FirstMatingAt, 0).Local().Format(LayoutDate2)
 		}
 
 		lastMatingAtFormat := ""
 		if v.LastMatingAt > 0 {
-			lastMatingAtFormat = time.Unix(v.LastMatingAt, 0).Format(LayoutDate2)
+			lastMatingAtFormat = time.Unix(v.LastMatingAt, 0).Local().Format(LayoutDate2)
 		}
 
 		lastPregnantCheckAtFormat := ""
 		if v.LastPregnantCheckAt > 0 {
-			lastPregnantCheckAtFormat = time.Unix(v.LastPregnantCheckAt, 0).Format(LayoutDate2)
+			lastPregnantCheckAtFormat = time.Unix(v.LastPregnantCheckAt, 0).Local().Format(LayoutDate2)
 		}
 
 		lastCalvingAtFormat := ""
 		if v.LastCalvingAt > 0 {
-			lastCalvingAtFormat = time.Unix(v.LastCalvingAt, 0).Format(LayoutDate2)
+			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
+		}
+
+		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:                   penName,
-			Lact:                      v.Lact,
-			CowTypeName:               cowTypeMap[v.CowType],
-			BreedStatusName:           breedStatusMap[v.BreedStatus],
-			CowKindName:               cowKindMap[v.CowKind],
-			EarNumber:                 v.EarNumber,
-			BirthWeight:               float32(v.BirthWeight) / 1000,
-			CurrentWeight:             float32(v.CurrentWeight) / 1000,
-			DayAge:                    v.DayAge,
-			SourceName:                cowSourceMap[v.SourceId],
-			MontherNumber:             v.MotherNumber,
-			FatherNumber:              v.FatherNumber,
-			AdmissionStatusName:       admissionStatusMap[v.AdmissionStatus],
-			HealthStatusName:          healthStatusMap[v.HealthStatus],
-			IsPregnantName:            isPregnantName,
-			AdmissionAtFormat:         admissionAtFormat,
-			BirthAtFormat:             birthAtFormat,
-			WeaningAtFormat:           weaningAtFormat,
-			CalvingAge:                int32(v.GetCalvingAge()),
-			AbortionAge:               int32(v.AbortionAge),
-			MatingTimes:               v.MatingTimes,
-			FirstMatingAtFormat:       firstMatingAtFormat,
-			LastMatingAtFormat:        lastMatingAtFormat,
-			LastBullNumber:            v.LastBullNumber,
-			LastPregnantCheckAtFormat: lastPregnantCheckAtFormat,
-			LastWeightAtFormat:        lastWeightAtFormat,
-			LastCalvingAtFormat:       lastCalvingAtFormat,
-			//PregnancyAge:              v.PregnancyAge,
+			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 (c CowSlice) ToPB2(penMap map[int32]*Pen, penWeightSlice PenWeightSlice) []*pasturePb.CowList {
+func (c CowSlice) ToPB2(penWeightSlice PenWeightSlice) []*pasturePb.CowList {
 	res := make([]*pasturePb.CowList, len(c))
 	for i, v := range c {
-		penName := ""
-		if pen, ok := penMap[v.PenId]; ok {
-			penName = pen.Name
-		}
-
 		penWeight := penWeightSlice.GetPenWeight(v.PenId)
-		lastWeightDay := util.Ceil(float64(v.LastWeightAt-v.LastSecondWeightAt) / 86400)
 		penAvgWeight := float32(0)
-		previousStageDailyWeight := float32(0)
 		cowPenAvgWeightDiffValue := float32(0)
 
 		if penWeight != nil {
 			penAvgWeight = float32(penWeight.AvgWeight) / 1000
 			cowPenAvgWeightDiffValue = float32(v.CurrentWeight-int64(penWeight.AvgWeight)) / 1000
-			if lastWeightDay > 0 {
-				previousStageDailyWeight = float32(v.CurrentWeight-v.LastSecondWeight) / 1000 / float32(lastWeightDay)
-			}
 		}
 
 		res[i] = &pasturePb.CowList{
 			CowId:                    int32(v.Id),
 			DayAge:                   v.DayAge,
-			DailyWeightGain:          float32(v.GetDayWeight()),
 			AverageDailyWeightGain:   float32(v.GetAverageDailyWeight()),
 			EarNumber:                v.EarNumber,
-			PenName:                  penName,
+			PenName:                  v.PenName,
 			BirthAt:                  int32(v.BirthAt),
 			BirthWeight:              float32(v.BirthWeight) / 1000,
 			CurrentWeight:            float32(v.CurrentWeight) / 1000,
 			LastWeightAt:             int32(v.LastWeightAt),
-			AdmissionAge:             int32(v.AdmissionAge),
+			AdmissionAge:             v.AdmissionAge,
 			AdmissionWeight:          float32(v.AbortionAge) / 1000,
-			PreviousStageDailyWeight: previousStageDailyWeight,
+			PreviousStageDailyWeight: float32(v.GetPreviousStageDailyWeight()),
 			PenAvgWeight:             penAvgWeight,
 			CowPenAvgWeightDiffValue: cowPenAvgWeightDiffValue,
 		}
@@ -205,48 +602,137 @@ func (c CowSlice) ToPB2(penMap map[int32]*Pen, penWeightSlice PenWeightSlice) []
 	return res
 }
 
-func NewCow(req *pasturePb.EventEnterRequest) *Cow {
+// NewEnterCow 入场新增牛只
+func NewEnterCow(pastureId int64, req *pasturePb.EventEnterRequest, penMap map[int32]*Pen) *Cow {
 	var isPregnant = pasturePb.IsShow_No
 	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:               req.PenId,
+		PenName:             penMap[req.PenId].Name,
 		Lact:                req.Lact,
-		CowType:             req.CowType,
-		BreedStatus:         req.BreedStatus,
+		CowType:             cowType,
+		BreedStatus:         breedStatus,
 		CowKind:             req.CowKind,
-		SourceId:            req.CowSource,
+		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:         time.Now().Unix(),
+		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 int64, fatherNumber string, calf *CalvingCalf) *Cow {
+// NewCalfCow 产犊新增
+func NewCalfCow(matherInfo *Cow, calf *CalvingCalf) *Cow {
 	return &Cow{
+		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:         calf.CowKind,                     // 牛只品种
+		CowKind:         matherInfo.CowKind,               // 牛只品种
 		BirthWeight:     calf.BirthWeight,
 		BirthAt:         calf.BirthAt,
-		SourceId:        pasturePb.CowSource_Calving, // 产犊方式
-		FatherNumber:    fatherNumber,
-		MotherNumber:    fmt.Sprintf("%d", motherId),
+		SourceKind:      pasturePb.CowSource_Calving, // 产犊方式
+		FatherNumber:    matherInfo.LastBullNumber,
+		MotherNumber:    matherInfo.EarNumber,
 		AdmissionStatus: pasturePb.AdmissionStatus_Admission,
 		IsPregnant:      pasturePb.IsShow_No,
 		AdmissionAt:     calf.BirthAt,
@@ -270,82 +756,6 @@ 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.LastMatingAt <= 0 {
-		return 0
-	}
-	return int64(math.Floor(float64(time.Now().Unix()-c.LastMatingAt) / 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().Unix()-c.LastMatingAt) / 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().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().Unix()-c.AdmissionAt) / 86400))
-	}
-	return 0
-}
-
-// 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
-		}
-		dayWeight := math.Round(1.0 * float64(c.CurrentWeight-c.LastSecondWeight) / float64(days))
-		return dayWeight / 1000
-	}
-	return 0
-}
-
-// 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 / 1000
-	}
-	return 0
-}
-
-func (c *Cow) GetAbortionAge() int32 {
-	if c.LastAbortionAt > 0 && c.AdmissionStatus == pasturePb.AdmissionStatus_Admission {
-		return int32(math.Floor(float64(time.Now().Unix()-c.LastAbortionAt) / 86400))
-	}
-	return 0
-
-}
-
 type CowWeightRange struct {
 	WeightRange string `json:"weight_range"`
 	Count       int32  `json:"count"`
@@ -359,17 +769,80 @@ func (c CowSlice) WeightRangeToPB(penMap map[int32]*Pen) []*pasturePb.CowList {
 			penName = pen.Name
 		}
 		res[i] = &pasturePb.CowList{
-			CowId:                  int32(v.Id),
-			DayAge:                 v.DayAge,
-			DailyWeightGain:        float32(v.GetDayWeight()),
-			AverageDailyWeightGain: float32(v.GetAverageDailyWeight()),
-			EarNumber:              v.EarNumber,
-			PenName:                penName,
-			BirthAt:                int32(v.BirthAt),
-			BirthWeight:            float32(v.BirthWeight) / 1000,
-			CurrentWeight:          float32(v.CurrentWeight) / 1000,
-			LastWeightAt:           int32(v.LastWeightAt),
+			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 res
 }
+
+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 res
+}
+
+// CowBehaviorCurveResponse 脖环行为数据
+type CowBehaviorCurveResponse struct {
+	Code int32                 `json:"code"`
+	Msg  string                `json:"msg"`
+	Data *CowBehaviorCurveData `json:"data"`
+}
+
+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"`
+}

+ 0 - 58
model/cow_active_habit.go

@@ -1,58 +0,0 @@
-package model
-
-import pasturePb "gitee.com/xuyiping_admin/go_proto/proto/go/backend/cow"
-
-type CowActiveHabit struct {
-	Id                       int64                 `json:"id"`
-	CowId                    int64                 `json:"cowId"`
-	NeckRingNumber           string                `json:"neckRingNumber"`
-	Lact                     int32                 `json:"lact"`
-	FrameId                  int32                 `json:"frameId"`
-	Rumina                   int32                 `json:"rumina"`
-	Intake                   int32                 `json:"intake"`
-	Inactive                 int32                 `json:"inactive"`
-	Gasp                     int32                 `json:"gasp"`
-	Other                    int32                 `json:"other"`
-	High                     int32                 `json:"high"`
-	Active                   int32                 `json:"active"`
-	Voltage                  int32                 `json:"voltage"`
-	Version                  int32                 `json:"version"`
-	FilterHigh               int32                 `json:"filterHigh"`
-	FilterRumina             int32                 `json:"filterRumina"`
-	FilterChew               int32                 `json:"filterChew"`
-	WeekHigh                 int32                 `json:"weekHigh"`
-	WeekAvgHighHabit         int32                 `json:"weekAvgHighHabit"`
-	WeekAvgRuminaHabit       int32                 `json:"WeekAvgRuminaHabit"`
-	WeekAvgIntakeHabit       int32                 `json:"weekAvgIntakeHabit"`
-	WeekAvgChewHabit         int32                 `json:"weekAvgChewHabit"`
-	WeekAvgInactiveHabit     int32                 `json:"weekAvgInactiveHabit"`
-	WeekAvgOtherHabit        int32                 `json:"weekAvgOtherHabit"`
-	ChangeHigh               int32                 `json:"changeHigh"`
-	ChangeRumina             int32                 `json:"changeRumina"`
-	ChangeChew               int32                 `json:"changeChew"`
-	ChangeAdjust             int32                 `json:"changeAdjust"`
-	ChangeFilter             int32                 `json:"changeFilter"`
-	RuminaFilter             int32                 `json:"ruminaFilter"`
-	ChewFilter               int32                 `json:"chewFilter"`
-	FilterCorrect            int32                 `json:"filterCorrect"`
-	SumRumina                int32                 `json:"sumRumina"`
-	SumIntake                int32                 `json:"sumIntake"`
-	SumInactive              int32                 `json:"sumInactive"`
-	SumAct                   int32                 `json:"sumAct"`
-	SumRuminaBeforeThreeDays int32                 `json:"sumRuminaBeforeThreeDays"`
-	SumIntakeBeforeThreeDays int32                 `json:"sumIntakeBeforeThreeDays"`
-	MinHigh                  int32                 `json:"minHigh"`
-	MaxHigh                  int32                 `json:"maxHigh"`
-	MinChew                  int32                 `json:"minChew"`
-	Score                    int32                 `json:"score"`
-	IsMaxTime                pasturePb.IsShow_Kind `json:"isMaxTime"`
-	ReceiveNumber            int32                 `json:"receiveNumber"`
-	RecodeCount              int32                 `json:"recodeCount"`
-	ActiveTime               string                `json:"activeTime"`
-	CreatedAt                int64                 `json:"createdAt"`
-	UpdatedAt                int64                 `json:"updatedAt"`
-}
-
-func (c *CowActiveHabit) TableName() string {
-	return "cow_active_habit"
-}

+ 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,
+	}
+}

+ 15 - 21
model/cow_pregnant.go

@@ -1,16 +1,18 @@
 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 CowPregnant 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"`
-	PenId        int32                  `json:"penId"`
-	AdmissionAge int32                  `json:"admissionAge"`
-	CowType      pasturePb.CowType_Kind `json:"cowType"`
 	PregnancyAge int32                  `json:"pregnancyAge"`
+	CowType      pasturePb.CowType_Kind `json:"cowType"`
+	DateTime     string                 `json:"dateTime"`
 	CreatedAt    int64                  `json:"createdAt"`
 	UpdatedAt    int64                  `json:"updatedAt"`
 }
@@ -19,24 +21,16 @@ func (c *CowPregnant) TableName() string {
 	return "cow_pregnant"
 }
 
-func NewCowPregnant(cow *Cow) *CowPregnant {
+func NewCowPregnant(pastureId int64, cowInfo *Cow, dateTime string) *CowPregnant {
 	return &CowPregnant{
-		CowId:        cow.Id,
-		Lact:         cow.Lact,
-		DayAge:       cow.DayAge,
-		PenId:        cow.PenId,
-		AdmissionAge: cow.AdmissionAge,
-		CowType:      cow.CowType,
-		PregnancyAge: cow.PregnancyAge,
-	}
-}
-
-func NewCowPregnantList(cow []*Cow) []*CowPregnant {
-	res := make([]*CowPregnant, len(cow))
-	for i, v := range cow {
-		res[i] = NewCowPregnant(v)
+		PastureId:    pastureId,
+		CowId:        cowInfo.Id,
+		EarNumber:    cowInfo.EarNumber,
+		Lact:         cowInfo.Lact,
+		CowType:      cowInfo.CowType,
+		PregnancyAge: cowInfo.GetDaysPregnant(),
+		DateTime:     dateTime,
 	}
-	return res
 }
 
 type CowPregnantMonth struct {

+ 0 - 35
model/cow_same_time.go

@@ -1,35 +0,0 @@
-package model
-
-import pasturePb "gitee.com/xuyiping_admin/go_proto/proto/go/backend/cow"
-
-type CowSameTime struct {
-	Id             int64                         `json:"id"`
-	SameTimeId     int64                         `json:"sameTimeId"`
-	CowId          int64                         `json:"cowId"`
-	Lact           int32                         `json:"lact"`
-	SameTimeStatus pasturePb.SameTimeStatus_Kind `json:"sameTimeStatus"`
-	CreatedAt      int64                         `json:"createdAt"`
-	UpdatedAt      int64                         `json:"updatedAt"`
-}
-
-func (c *CowSameTime) TableName() string {
-	return "cow_same_time"
-}
-
-func NewCowSameTime(cow *Cow, sameTime *SameTime) *CowSameTime {
-	return &CowSameTime{
-		SameTimeId:     sameTime.Id,
-		CowId:          cow.Id,
-		Lact:           cow.Lact,
-		SameTimeStatus: pasturePb.SameTimeStatus_No_Start,
-	}
-}
-
-func NewCowSameTimeList(cowList []*Cow, sameTime *SameTime) []*CowSameTime {
-	sameTimeCowList := make([]*CowSameTime, 0)
-	for _, cow := range cowList {
-		sameTimeCowList = append(sameTimeCowList, NewCowSameTime(cow, sameTime))
-
-	}
-	return sameTimeCowList
-}

+ 1 - 1
model/crontab_log.go

@@ -17,6 +17,6 @@ func (c CronLog) TableName() string {
 func NewCronLog(name string) *CronLog {
 	return &CronLog{
 		Name: name,
-		Date: time.Now().Format(LayoutDate2),
+		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
+}

+ 7 - 6
model/disease.go

@@ -8,15 +8,16 @@ import (
 
 type Disease struct {
 	Id              int64                 `json:"id"`
+	PastureId       int64                 `json:"pastureId"`
 	Name            string                `json:"name"`
 	Symptoms        string                `json:"symptoms"`
-	DiseaseType     int32                 `json:"disease_type"`
-	DiseaseTypeName string                `json:"disease_type_name"`
-	IsShow          pasturePb.IsShow_Kind `json:"is_show"`
+	DiseaseType     int32                 `json:"diseaseType"`
+	DiseaseTypeName string                `json:"diseaseTypeName"`
+	IsShow          pasturePb.IsShow_Kind `json:"isShow"`
 	Remarks         string                `json:"remarks"`
-	OperationId     int64                 `json:"operation_id"`
-	CreatedAt       int64                 `json:"created_at"`
-	UpdatedAt       int64                 `json:"updated_at"`
+	OperationId     int64                 `json:"operationId"`
+	CreatedAt       int64                 `json:"createdAt"`
+	UpdatedAt       int64                 `json:"updatedAt"`
 }
 
 func (d *Disease) TableName() string {

+ 3 - 1
model/drugs.go

@@ -4,6 +4,7 @@ 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:"categoryId"`
 	CategoryName    string                      `json:"categoryName"`
@@ -31,8 +32,9 @@ 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{
+		PastureId:       pastureId,
 		Name:            req.Name,
 		CategoryId:      req.CategoryId,
 		CategoryName:    req.CategoryName,

+ 24 - 14
model/event_abortion.go

@@ -8,17 +8,21 @@ import (
 
 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"`
@@ -29,36 +33,42 @@ func (e *EventAbortion) TableName() string {
 	return "event_abortion"
 }
 
-func NewEventAbortion(cow *Cow, req *pasturePb.EventAbortionRequest, abortionReasonMap map[pasturePb.AbortionReasons_Kind]string) *EventAbortion {
+func NewEventAbortion(pastureId int64, cow *Cow, item *pasturePb.EventAbortionItem, isAppend pasturePb.IsShow_Kind) *EventAbortion {
 	return &EventAbortion{
-		CowId:               int64(req.CowId),
+		PastureId:           pastureId,
+		CowId:               cow.Id,
+		EarNumber:           cow.EarNumber,
 		Lact:                cow.Lact,
 		CowType:             cow.CowType,
 		PregnantAge:         cow.GetDaysPregnant(),
-		DayAge:              cow.DayAge,
-		AbortionAt:          int64(req.AbortionAt),
-		IsAfterbirth:        req.IsAfterbirth,
-		Photos:              strings.Join(req.Photos, ","),
-		AbortionReasons:     req.AbortionReasons,
-		AbortionReasonsName: abortionReasonMap[req.AbortionReasons],
-		Remarks:             req.Remarks,
-		OperationId:         int64(req.OperationId),
-		OperationName:       req.OperationName,
+		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.EventAbortionRequest {
-	res := make([]*pasturePb.EventAbortionRequest, len(a))
+func (a AbortionSlice) ToPB() []*pasturePb.EventAbortionItem {
+	res := make([]*pasturePb.EventAbortionItem, len(a))
 	for i, v := range a {
-		res[i] = &pasturePb.EventAbortionRequest{
+		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,

+ 5 - 3
model/event_body_score.go

@@ -4,6 +4,7 @@ import pasturePb "gitee.com/xuyiping_admin/go_proto/proto/go/backend/cow"
 
 type EventBodyScore struct {
 	Id            int64  `json:"id"`
+	PastureId     int64  `json:"pastureId"`
 	CowId         int64  `json:"cowId"`
 	EarNumber     string `json:"earNumber"`
 	Score         int32  `json:"score"`
@@ -23,8 +24,9 @@ func (e *EventBodyScore) TableName() string {
 	return "event_body_score"
 }
 
-func NewEventBodyScore(cow *Cow, currentSystemUser *SystemUser, 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,
@@ -32,8 +34,8 @@ func NewEventBodyScore(cow *Cow, currentSystemUser *SystemUser, req *pasturePb.B
 		DayAge:        cow.GetDayAge(),
 		ScoreAt:       int64(req.ScoreAt),
 		Remarks:       req.Remarks,
-		MessageId:     currentSystemUser.Id,
-		MessageName:   currentSystemUser.Name,
+		MessageId:     currentUser.Id,
+		MessageName:   currentUser.Name,
 		OperationId:   int64(req.OperationId),
 		OperationName: req.OperationName,
 	}

+ 54 - 28
model/event_calving.go

@@ -2,58 +2,83 @@ package model
 
 import (
 	"kpt-pasture/util"
-	"time"
 
 	pasturePb "gitee.com/xuyiping_admin/go_proto/proto/go/backend/cow"
 )
 
 type EventCalving struct {
-	Id             int64                         `json:"id"`
-	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    int8                          `json:"childNumber"`
-	BullNumber     string                        `json:"bullNumber"`
-	PregnancyAge   int64                         `json:"pregnancyAge"`
-	CalvingLevel   pasturePb.CalvingLevel_Kind   `json:"calvingLevel"`
-	OperationId    int64                         `json:"operationId"`
-	OperationName  string                        `json:"operationName"`
-	DystociaReason pasturePb.DystociaReason_Kind `json:"dystociaReason"`
-	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) *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{
+		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(time.Now().Format(LayoutTime)),
-		EndDay:     util.TimeParseLocalEndUnix(time.Now().Format(LayoutTime)),
+		PlanDay:    util.TimeParseLocalUnix(startDay),
+		EndDay:     util.TimeParseLocalEndUnix(endDay),
 	}
 }
 
-func NewEventCalvingList(cowList []*Cow) []*EventCalving {
+func NewEventCalvingList(pastureId int64, cowList []*Cow, startDay, endDay string) []*EventCalving {
 	eventCalvingList := make([]*EventCalving, 0)
 	for _, cow := range cowList {
-		eventCalvingList = append(eventCalvingList, NewEventCalving(cow))
+		eventCalvingList = append(eventCalvingList, NewEventCalving(pastureId, cow, startDay, endDay))
 	}
 	return eventCalvingList
 }
@@ -140,5 +165,6 @@ func (e EventCalvingSlice) ToPB() []*pasturePb.CalvingReportTable {
 			NormalLaborCount:    0,
 		}
 	}
+
 	return res
 }

+ 134 - 62
model/event_cow_disease.go

@@ -1,60 +1,116 @@
 package model
 
-import pasturePb "gitee.com/xuyiping_admin/go_proto/proto/go/backend/cow"
+import (
+	"time"
+
+	pasturePb "gitee.com/xuyiping_admin/go_proto/proto/go/backend/cow"
+)
 
 type EventCowDisease struct {
-	Id                    int64                       `json:"id"`
-	CowId                 int64                       `json:"cowId"`
-	CowType               pasturePb.CowType_Kind      `json:"cowType"`
-	Lact                  int32                       `json:"lact"`
-	DayAge                int32                       `json:"dayAge"`
-	DiseaseId             int32                       `json:"diseaseId"`
-	DiseaseName           string                      `json:"diseaseName"`
-	DiseaseType           int32                       `json:"diseaseType"`
-	DiseaseTypeName       string                      `json:"diseaseTypeName"`
-	DiagnoseId            int32                       `json:"diagnoseId"`
-	DiagnoseName          string                      `json:"diagnoseName"`
-	PenId                 int32                       `json:"penId"`
-	HealthStatus          pasturePb.HealthStatus_Kind `json:"healthStatus"`
-	DiagnosedResult       pasturePb.IsShow_Kind       `json:"diagnosedResult"`
-	DiagnosedAt           int64                       `json:"diagnosedAt"`
-	DiseaseAt             int64                       `json:"diseaseAt"`
-	Temperature           int32                       `json:"temperature"`
-	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"`
-	CreatedAt             int64                       `json:"createdAt"`
-	UpdatedAt             int64                       `json:"updatedAt"`
+	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"
 }
 
-func NewEventCowDisease(cow *Cow, disease *Disease, req *pasturePb.EventCowDiseaseRequest, operation, currUser *SystemUser) *EventCowDisease {
+// 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{
-		CowId:           cow.Id,
-		CowType:         cow.CowType,
-		Lact:            cow.Lact,
-		DayAge:          cow.DayAge,
-		DiseaseId:       int32(disease.Id),
-		DiseaseName:     disease.Name,
-		DiseaseType:     disease.DiseaseType,
-		DiseaseTypeName: disease.DiseaseTypeName,
-		PenId:           req.PenId,
-		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,
+		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,
 	}
 }
 
@@ -63,23 +119,39 @@ 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),
-			Lact:             v.Lact,
-			DayAge:           v.DayAge,
-			DiseaseId:        v.DiseaseId,
-			DiseaseName:      v.DiseaseName,
-			DiagnoseId:       v.DiagnoseId,
-			DiagnoseName:     v.DiagnoseName,
-			PenId:            v.PenId,
-			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,
+			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

+ 86 - 35
model/event_cow_log.go

@@ -2,6 +2,7 @@ package model
 
 import (
 	"fmt"
+	"time"
 
 	pasturePb "gitee.com/xuyiping_admin/go_proto/proto/go/backend/cow"
 )
@@ -9,23 +10,25 @@ import (
 const ShardTableNumber = 6
 
 type EventCowLog struct {
-	Id               int64                  `json:"id"`
-	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"`
-	EventType        int64                  `json:"eventType"`
-	EventTypeName    string                 `json:"eventTypeName"`
-	EventDescription string                 `json:"eventDescription"`
-	OperationId      int64                  `json:"operationId"`
-	OperationName    string                 `json:"operationName"`
-	EventAt          string                 `json:"eventAt"`
-	Remarks          string                 `json:"remarks"`
-	CreatedAt        int64                  `json:"createdAt"`
-	UpdatedAt        int64                  `json:"updatedAt"`
+	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 {
@@ -37,25 +40,73 @@ func (e *EventCowLog) TableName() string {
 	return fmt.Sprintf("%s_%04d", e.UnShardTableName(), e.CowId%ShardTableNumber)
 }
 
-func NewCowEventLog(cow *Cow, penMap map[int32]*Pen, cowType map[pasturePb.CowType_Kind]string, operation *SystemUser) *EventCowLog {
-	penName := ""
-	if pen, ok := penMap[cow.PenId]; ok {
-		penName = pen.Name
+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{
-		CowId:            cow.Id,
-		DayAge:           cow.GetDayAge(),
-		Lact:             cow.Lact,
-		PenId:            cow.PenId,
-		PenName:          penName,
-		CowType:          cow.CowType,
-		CowTypeName:      cowType[cow.CowType],
-		EventType:        0,
-		EventTypeName:    "",
-		EventDescription: "",
-		OperationId:      operation.Id,
-		OperationName:    operation.Name,
-		EventAt:          "",
-		Remarks:          "",
+		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
 }

+ 50 - 16
model/event_cow_same_time.go

@@ -8,7 +8,9 @@ import (
 
 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"`
@@ -22,9 +24,12 @@ type EventCowSameTime struct {
 	Status        pasturePb.IsShow_Kind       `json:"status"`
 	DrugsId       int64                       `json:"drugsId"`
 	Unit          pasturePb.Unit_Kind         `json:"unit"`
-	Usage         int32                       `json:"usage"`
+	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"`
 }
@@ -33,9 +38,24 @@ func (s *EventCowSameTime) TableName() string {
 	return "event_cow_same_time"
 }
 
-func NewEventCowSameTime(cow *Cow, planTime int64, sameTime *SameTime, sameTimeType pasturePb.SameTimeType_Kind) *EventCowSameTime {
+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,
@@ -44,14 +64,14 @@ func NewEventCowSameTime(cow *Cow, planTime int64, sameTime *SameTime, sameTimeT
 		SameTimeType: sameTimeType,
 		Status:       pasturePb.IsShow_No,
 		PlanDay:      planTime,
-		EndDay:       planTime,
+		EndDay:       planTime + 86399,
 	}
 }
 
-func NewEventCowSameTimeList(cowList []*Cow, sameTime *SameTime, planTime int64, sameTimeType pasturePb.SameTimeType_Kind) []*EventCowSameTime {
+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(cow, planTime, sameTime, sameTimeType)
+		res[i] = NewEventCowSameTime(pastureId, cow, planTime, sameTime, sameTimeType)
 	}
 	return res
 }
@@ -59,6 +79,7 @@ func NewEventCowSameTimeList(cowList []*Cow, sameTime *SameTime, planTime int64,
 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"`
@@ -75,47 +96,60 @@ type SameTimeItemBody struct {
 	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,
-	penMap map[int32]*Pen,
 	sameTimeTypeMap map[pasturePb.SameTimeType_Kind]string,
 ) []*pasturePb.SameTimeItems {
 	res := make([]*pasturePb.SameTimeItems, len(s))
 	for i, v := range s {
-		penName, calvingAtFormat, abortionAtFormat := "", "", ""
-		if pen, ok := penMap[v.PenId]; ok {
-			penName = pen.Name
-		}
-
+		calvingAtFormat, abortionAtFormat, playDayAtFormat := "", "", ""
 		if v.LastCalvingAt > 0 {
-			calvingAtFormat = time.Unix(v.LastCalvingAt, 0).Format(LayoutDate2)
+			calvingAtFormat = time.Unix(v.LastCalvingAt, 0).Local().Format(LayoutDate2)
 		}
 
 		if v.LastAbortionAt > 0 {
-			abortionAtFormat = time.Unix(v.LastAbortionAt, 0).Format(LayoutDate2)
+			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:  breedStatusMap[v.BreedStatus],
-			PenName:          penName,
+			BreedStatusName:  breedStatusName,
+			PenName:          v.PenName,
 			PenId:            v.PenId,
 			Lact:             v.Lact,
 			CalvingAge:       v.CalvingAge,
 			AbortionAge:      v.AbortionAge,
 			DayAge:           v.DayAge,
 			Status:           v.Status,
-			SameTimeTypeName: sameTimeTypeMap[v.SameTimeType],
+			SameTimeTypeName: sameTimeTypeName,
+			SameTimeType:     v.SameTimeType,
 			CalvingAtFormat:  calvingAtFormat,
 			AbortionAtFormat: abortionAtFormat,
 			MatingTimes:      v.MatingTimes,
 			SameTimeName:     v.SameTimeName,
+			PlanDay:          playDayAtFormat,
 		}
 	}
 	return res

+ 33 - 12
model/event_cow_treatment.go

@@ -11,6 +11,7 @@ import (
 
 type EventCowTreatment struct {
 	Id                 int64                          `json:"id"`
+	PastureId          int64                          `json:"pastureId"`
 	CowId              int64                          `json:"cowId"`
 	CowDiseaseId       int64                          `json:"cowDiseaseId"`
 	DiseaseId          int64                          `json:"diseaseId"`
@@ -23,6 +24,8 @@ type EventCowTreatment struct {
 	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"`
@@ -33,9 +36,16 @@ func (e *EventCowTreatment) TableName() string {
 	return "event_cow_treatment"
 }
 
-func NewEventCowTreatment(prescription *Prescription, req *pasturePb.CowTreatmentRequest, diseaseTypeMap map[int32]string, operation *SystemUser) *EventCowTreatment {
+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),
@@ -48,16 +58,33 @@ func NewEventCowTreatment(prescription *Prescription, req *pasturePb.CowTreatmen
 		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(eventCowDiseaseList []*EventCowDisease) []*pasturePb.EventCowTreatment {
+func (e EventCowTreatmentSlice) ToPB(eventCowDisease *EventCowDisease) []*pasturePb.EventCowTreatment {
 	res := make([]*pasturePb.EventCowTreatment, len(e))
 	for i, v := range e {
-		prescriptionDetail := make([]*pasturePb.TreatmentDrugs, 0)
+		prescriptionDetail := make([]*pasturePb.PrescriptionDrugsList, 0)
 		if v.PrescriptionDetail != "" {
 			err := json.Unmarshal([]byte(v.PrescriptionDetail), &prescriptionDetail)
 			if err != nil {
@@ -65,7 +92,7 @@ func (e EventCowTreatmentSlice) ToPB(eventCowDiseaseList []*EventCowDisease) []*
 			}
 		}
 
-		eventCowTreatment := &pasturePb.EventCowTreatment{
+		res[i] = &pasturePb.EventCowTreatment{
 			CowId:              int32(v.CowId),
 			CowDiseaseId:       int32(v.CowDiseaseId),
 			PrescriptionId:     v.PrescriptionId,
@@ -79,15 +106,9 @@ func (e EventCowTreatmentSlice) ToPB(eventCowDiseaseList []*EventCowDisease) []*
 			UpdatedAt:          int32(v.UpdatedAt),
 			DiseaseId:          int32(v.DiseaseId),
 			Id:                 int32(v.Id),
+			DiseaseName:        eventCowDisease.DiseaseName,
+			DiseaseAt:          int32(eventCowDisease.DiseaseAt),
 		}
-		for _, d := range eventCowDiseaseList {
-			if v.CowDiseaseId != d.Id {
-				continue
-			}
-			eventCowTreatment.DiseaseName = d.DiseaseName
-			eventCowTreatment.DiseaseAt = int32(d.DiseaseAt)
-		}
-		res[i] = eventCowTreatment
 	}
 	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
+}

+ 19 - 4
model/event_enter.go

@@ -9,6 +9,7 @@ import (
 
 type EventEnter struct {
 	Id               int64                      `json:"id"`
+	PastureId        int64                      `json:"pastureId"`
 	BatchNumber      string                     `json:"batchNumber"`
 	EarNumber        string                     `json:"earNumber"`
 	CowId            int64                      `json:"cowId"`
@@ -44,8 +45,9 @@ type EventEnter struct {
 func (e *EventEnter) TableName() string {
 	return "event_enter"
 }
-func NewEventEnter(cowId int64, req *pasturePb.EventEnterRequest) *EventEnter {
+func NewEventEnter(pastureId, cowId int64, req *pasturePb.EventEnterRequest) *EventEnter {
 	return &EventEnter{
+		PastureId:        pastureId,
 		EarNumber:        req.EarNumber,
 		CowId:            cowId,
 		Sex:              req.Sex,
@@ -54,7 +56,7 @@ func NewEventEnter(cowId int64, req *pasturePb.EventEnterRequest) *EventEnter {
 		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.CowKind,
 		FatherNumber:     req.FatherNumber,
@@ -66,12 +68,13 @@ func NewEventEnter(cowId int64, req *pasturePb.EventEnterRequest) *EventEnter {
 		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),
 		MessengerId:      int64(req.MessengerId),
 		MessengerName:    req.MessengerName,
 		OperationId:      int64(req.OperationId),
 		OperationName:    req.OperationName,
+		BatchNumber:      req.BatchNumber,
 	}
 }
 
@@ -115,7 +118,7 @@ func (e EventEnterSlice) ToPB(
 			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),
@@ -128,3 +131,15 @@ func (e EventEnterSlice) ToPB(
 	}
 	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"`
+}

+ 76 - 49
model/event_estrus.go

@@ -1,69 +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:"cowId"`
-	DayAge              int32                          `json:"dayAge"`
-	Lact                int8                           `json:"lact"`
-	LactationDays       int32                          `json:"lactationDays"`
-	EstrusAt            int64                          `json:"estrusAt"`
-	UnMatingReasons     pasturePb.UnMatingReasons_Kind `json:"unMatingReasons"`
-	UnMatingReasonsName string                         `json:"unMatingReasonsName"`
-	IsMating            pasturePb.IsShow_Kind          `json:"isMating"`
-	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"`
+	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, operation *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,
-		IsMating:        req.IsMathing,
-		UnMatingReasons: req.UnMatingReasons,
-		MessageId:       currentUser.Id,
-		MessageName:     currentUser.Name,
-		OperationId:     operation.Id,
-		OperationName:   operation.Name,
+		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() []*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 {
-		res[i] = &pasturePb.SearchEstrusList{
-			Id:                  int32(v.Id),
-			CowId:               int32(v.CowId),
-			DayAge:              v.DayAge,
-			Lact:                int32(v.Lact),
-			EstrusAt:            int32(v.EstrusAt),
-			IsMathing:           v.IsMating,
-			UnMatingReasonsName: v.UnMatingReasonsName,
-			LactationDays:       v.LactationDays,
-			MessengerId:         int32(v.MessageId),
-			MessengerName:       v.MessageName,
-			Remarks:             v.Remarks,
-			OperationId:         int32(v.OperationId),
-			OperationName:       v.OperationName,
-			CreatedAt:           int32(v.CreatedAt),
-			UpdatedAt:           int32(v.UpdatedAt),
+		res[i] = &pasturePb.SearchEventEstrusList{
+			Id:              int32(v.Id),
+			CowId:           int32(v.CowId),
+			EarNumber:       v.EarNumber,
+			DayAge:          v.DayAge,
+			Lact:            v.Lact,
+			EstrusAt:        int32(v.RealityDay),
+			IsMating:        v.IsMating,
+			UnMatingReasons: v.UnMatingReasonsName,
+			OperationName:   v.OperationName,
+			Remarks:         v.Remarks,
+			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"`
+}

+ 89 - 49
model/event_immunization_plan.go

@@ -9,7 +9,9 @@ import (
 
 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"`
@@ -22,77 +24,115 @@ type EventImmunizationPlan struct {
 	RealityDay    int64                  `json:"realityDay"`
 	EndDay        int64                  `json:"endDay"`
 	Status        pasturePb.IsShow_Kind  `json:"status"`
-	OperationId   int64                  `json:"operationId"`
-	OperationName string                 `json:"operationName"`
 	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"`
-	CreateAt      int64                  `json:"createAt"`
-	UpdateAt      int64                  `json:"updateAt"`
+	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 (c *EventImmunizationPlan) TableName() string {
+func (e *EventImmunizationPlan) TableName() string {
 	return "event_immunization_plan"
 }
 
-func NewCowImmunizationPlan(cow *Cow, pen *Pen, immunizationPlan *ImmunizationPlan) *EventImmunizationPlan {
-	todayTime := time.Now().Format(LayoutDate2)
-	todayStartTime := util.TimeParseLocalUnix(todayTime)
-	todayEndTime := util.TimeParseLocalEndUnix(todayTime)
+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{
-		CowId:    cow.Id,
-		Lact:     cow.Lact,
-		DayAge:   cow.DayAge,
-		CowKind:  cow.CowKind,
-		CowType:  cow.CowType,
-		PenId:    cow.PenId,
-		PenName:  pen.Name,
-		PlanId:   immunizationPlan.Id,
-		PlanName: immunizationPlan.Name,
-		PlanDay:  todayStartTime,
-		EndDay:   todayEndTime,
-		Status:   pasturePb.IsShow_No,
+		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, penMap map[int32]*Pen, immunizationPlan *ImmunizationPlan) []*EventImmunizationPlan {
-	cowImmunizationPlanList := make([]*EventImmunizationPlan, len(cowList))
+func NewCowImmunizationPlanList(cowList []*Cow, immunizationPlan *ImmunizationPlan) []*EventImmunizationPlan {
+	eventImmunizationPlanList := make([]*EventImmunizationPlan, len(cowList))
 	for i, cow := range cowList {
-		pen := penMap[cow.PenId]
-		cowImmunizationPlanList[i] = NewCowImmunizationPlan(cow, pen, immunizationPlan)
+		eventImmunizationPlanList[i] = NewEventImmunizationPlan(cow, immunizationPlan)
 	}
-	return cowImmunizationPlanList
+	return eventImmunizationPlanList
 }
 
 type EventImmunizationPlanSlice []*EventImmunizationPlan
 
-func (I EventImmunizationPlanSlice) ToPB() []*pasturePb.ImmunizationItems {
-	res := make([]*pasturePb.ImmunizationItems, len(I))
-	for i, v := range I {
+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),
-			PenId:            v.PenId,
-			Lact:             v.Lact,
-			PenName:          v.PenName,
-			DayAge:           v.DayAge,
-			PlanDay:          time.Unix(v.PlanDay, 0).Format(LayoutDate2),
-			Status:           v.Status,
-			ImmunizationName: v.PlanName,
-			ImmunizationId:   int32(v.PlanId),
-			Remarks:          v.Remarks,
-			OperatorId:       int32(v.OperationId),
-			OperatorName:     v.OperationName,
-			DrugId:           int32(v.DrugsId),
-			DrugName:         v.DrugsName,
-			Unit:             v.Unit,
-			UnitName:         v.UnitName,
-			Usage:            v.Usage,
-			CreatedAt:        int32(v.CreateAt),
-			UpdatedAt:        int32(v.UpdateAt),
+			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

+ 0 - 77
model/event_item.go

@@ -1,77 +0,0 @@
-package model
-
-import (
-	"kpt-pasture/util"
-	"time"
-
-	pasturePb "gitee.com/xuyiping_admin/go_proto/proto/go/backend/cow"
-)
-
-type EventItem struct {
-	Id            int64                       `json:"id"`
-	CowId         int64                       `json:"cowId"`
-	PenId         int32                       `json:"penId"`
-	Lact          int32                       `json:"lact"`
-	CalendarType  pasturePb.CalendarType_Kind `json:"calendarType"`
-	PlanDay       int64                       `json:"planDay"`
-	EndDay        int64                       `json:"endDay"`
-	RealityDay    int64                       `json:"realityDay"`
-	IsFinish      pasturePb.IsShow_Kind       `json:"isFinish"`
-	IsExpire      pasturePb.IsShow_Kind       `json:"isExpire"`
-	OperationId   int64                       `json:"operationId"`
-	OperationName string                      `json:"operationName"`
-	Remarks       string                      `json:"remarks"`
-	CreatedAt     int64                       `json:"createdAt"`
-	UpdatedAt     int64                       `json:"updatedAt"`
-}
-
-func (e *EventItem) TableName() string {
-	return "event_item"
-}
-
-func NewEventItem(cow *Cow, calendarType pasturePb.CalendarType_Kind) *EventItem {
-	return &EventItem{
-		CowId:        cow.Id,
-		PenId:        cow.PenId,
-		Lact:         cow.Lact,
-		CalendarType: calendarType,
-		PlanDay:      util.TimeParseLocalUnix(time.Now().Format(LayoutDate2)),
-		EndDay:       util.TimeParseLocalEndUnix(time.Now().Format(LayoutDate2)),
-		IsFinish:     pasturePb.IsShow_Ok,
-	}
-}
-
-func NewEventItemList(cowList []*Cow, calendarType pasturePb.CalendarType_Kind) []*EventItem {
-	res := make([]*EventItem, len(cowList))
-	for i, v := range cowList {
-		res[i] = NewEventItem(v, calendarType)
-	}
-	return res
-}
-
-type EventItemSlice []*EventItem
-
-func (e EventItemSlice) ToPB(penMap map[int32]*Pen, calendarType map[pasturePb.CalendarType_Kind]string) []*pasturePb.CalendarToDoList {
-	res := make([]*pasturePb.CalendarToDoList, len(e))
-	for i, v := range e {
-		penName := ""
-		if pen, ok := penMap[v.PenId]; ok {
-			penName = pen.Name
-		}
-		res[i] = &pasturePb.CalendarToDoList{
-			Id:               int32(v.Id),
-			Lact:             v.Lact,
-			CalendarType:     v.CalendarType,
-			CalendarTypeName: calendarType[v.CalendarType],
-			PlanDay:          time.Unix(v.PlanDay, 0).Format(LayoutDate2),
-			EndDay:           time.Unix(v.EndDay, 0).Format(LayoutDate2),
-			RealityDay:       time.Unix(v.RealityDay, 0).Format(LayoutDate2),
-			IsFinish:         v.IsFinish,
-			IsExpire:         v.IsExpire,
-			CowId:            int32(v.CowId),
-			PenName:          penName,
-			Remarks:          v.Remarks,
-		}
-	}
-	return res
-}

+ 168 - 52
model/event_mating.go

@@ -1,6 +1,7 @@
 package model
 
 import (
+	"kpt-pasture/util"
 	"time"
 
 	pasturePb "gitee.com/xuyiping_admin/go_proto/proto/go/backend/cow"
@@ -8,10 +9,13 @@ import (
 
 type EventMating 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"`
 	CowType           pasturePb.CowType_Kind          `json:"cowType"`
 	CowKind           pasturePb.CowKind_Kind          `json:"cowKind"`
 	CalvingAge        int32                           `json:"calvingAge"`
@@ -23,6 +27,7 @@ type EventMating struct {
 	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"`
@@ -38,50 +43,117 @@ func (e *EventMating) TableName() string {
 	return "event_mating"
 }
 
-func NewEventMating(cow *Cow, planDay int64) *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{
+		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,
+		EndDay:           planDay + 86399,
 		MatingResult:     pasturePb.MatingResult_Unknown,
-		ExposeEstrusType: pasturePb.ExposeEstrusType_Same_Time,
+		ExposeEstrusType: exposeEstrusType,
 		Status:           pasturePb.IsShow_No,
 	}
 }
 
-// NewEventMating2 自然发情的牛只
-func NewEventMating2(cow *Cow, req *pasturePb.EventMating, currentUser *SystemUser) *EventMating {
+// NewEventMatingNaturalEstrus 自然发情的牛只
+func NewEventMatingNaturalEstrus(pastureId int64, item *EventMatingCheckBatchModel, currentUser *SystemUser) *EventMating {
 	return &EventMating{
-		CowId:             cow.Id,
-		Lact:              cow.Lact,
-		DayAge:            cow.GetDayAge(),
-		CowType:           cow.CowType,
-		CowKind:           cow.CowKind,
-		CalvingAt:         cow.LastMatingAt,
-		PlanDay:           int64(req.MatingAt),
-		RealityDay:        int64(req.MatingAt),
-		EndDay:            int64(req.MatingAt),
+		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:       int64(req.OperationId),
-		OperationName:     req.OperationName,
+		OperationId:       item.OperationUser.Id,
+		OperationName:     item.OperationUser.Name,
 		MessageId:         currentUser.Id,
 		MessageName:       currentUser.Name,
-		FrozenSemenNumber: req.FrozenSemenNumber,
-		Remarks:           req.Remarks,
+		FrozenSemenNumber: item.FrozenSemen.BullId,
+		Remarks:           item.Remarks,
+		MatingTimes:       item.Cow.MatingTimes + 1,
 	}
 }
 
-func NewEventMatingList(cowList []*Cow, planDay int64) []*EventMating {
+// 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(cow, planDay))
+		matingList = append(matingList, NewEventMating(pastureId, cow, planDay, exposeEstrusType))
 	}
 	return matingList
 }
@@ -94,11 +166,12 @@ func (e EventMatingSlice) ToPB(exposeEstrusTypeMap map[pasturePb.ExposeEstrusTyp
 		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).Format(LayoutDate2),
-			RealityDay:           time.Unix(v.RealityDay, 0).Format(LayoutDate2),
+			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],
 			FrozenSemenNumber:    v.FrozenSemenNumber,
@@ -112,6 +185,60 @@ func (e EventMatingSlice) ToPB(exposeEstrusTypeMap map[pasturePb.ExposeEstrusTyp
 	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"`
@@ -162,32 +289,10 @@ type MatingTimelyChart struct {
 	LactGroup  string `json:"lactGroup"`
 }
 
-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).Format(LayoutDate2)
-		}
-		if v.RealityDay > 0 {
-			matingAtFormat = time.Unix(v.RealityDay, 0).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
-}
-
 type MatingTimelyResponse struct {
-	Code    int32             `json:"code"`
-	Message string            `json:"message"`
-	Data    *MatingTimelyData `json:"data"`
+	Code int32             `json:"code"`
+	Msg  string            `json:"msg"`
+	Data *MatingTimelyData `json:"data"`
 }
 
 type MatingTimelyData struct {
@@ -204,9 +309,9 @@ type CowMatingChart struct {
 
 // MultiFactorPregnancyRateResponse 多维度受胎率
 type MultiFactorPregnancyRateResponse struct {
-	Code    int32                         `json:"code"`
-	Message string                        `json:"message"`
-	Data    *MultiFactorPregnancyRateData `json:"data"`
+	Code int32                         `json:"code"`
+	Msg  string                        `json:"msg"`
+	Data *MultiFactorPregnancyRateData `json:"data"`
 }
 
 // MultiFactorPregnancyRateList 多维度受胎率分析
@@ -242,3 +347,14 @@ type MultiFactorPregnancyRateChart struct {
 	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
+}

+ 40 - 9
model/event_pregnant_check.go

@@ -7,9 +7,16 @@ import (
 	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"`
+	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"`
@@ -37,33 +44,55 @@ func (e *EventPregnantCheck) TableName() string {
 	return "event_pregnant_check"
 }
 
-func NewEventPregnantCheck(cow *Cow, penMap map[int32]*Pen, pregnantCheckName string) *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{
+		PastureId:         pastureId,
 		CowId:             cow.Id,
+		EarNumber:         cow.EarNumber,
 		CowType:           cow.CowType,
 		PenId:             cow.PenId,
 		PenName:           penName,
-		DayAge:            cow.GetDayAge(),
+		DayAge:            0,
 		Lact:              int8(cow.Lact),
-		PlanDay:           util.TimeParseLocalUnix(time.Now().Format(LayoutDate2)),
-		EndDay:            util.TimeParseLocalEndUnix(time.Now().Format(LayoutDate2)),
+		PlanDay:           util.TimeParseLocalUnix(startDay),
+		EndDay:            util.TimeParseLocalEndUnix(endDay),
 		PregnantCheckName: pregnantCheckName,
 		BullId:            cow.LastBullNumber,
 		Status:            pasturePb.IsShow_No,
 	}
 }
 
-func NewEventPregnantCheckList(cowList []*Cow, penMap map[int32]*Pen, pregnantCheckName string) []*EventPregnantCheck {
+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 {
+		if cow.BreedStatus != pasturePb.BreedStatus_Breeding && cow.BreedStatus != pasturePb.BreedStatus_Pregnant {
 			continue
 		}
-		res[i] = NewEventPregnantCheck(cow, penMap, pregnantCheckName)
+		res[i] = NewEventPregnantCheck(pastureId, cow, penMap, pregnantCheckName, startDay, endDay)
 	}
 	return res
 }
@@ -119,7 +148,8 @@ func (e EventPregnantCheckSlice) ToPB(
 			CowId:                   int32(v.CowId),
 			DayAge:                  v.DayAge,
 			Lact:                    int32(v.Lact),
-			PregnantCheckAt:         int32(v.PlanDay),
+			EarNumber:               v.EarNumber,
+			PregnantCheckAt:         int32(v.RealityDay),
 			PregnantCheckResult:     v.PregnantCheckResult,
 			PregnantCheckResultName: pregnantCheckResultMap[v.PregnantCheckResult],
 			PregnantCheckMethod:     v.PregnantCheckMethod,
@@ -158,9 +188,10 @@ func (e EventPregnantCheckSlice) ToPB3(
 		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).Format(LayoutDate2),
+			PregnancyCheckAtFormat:  time.Unix(v.RealityDay, 0).Local().Format(LayoutDate2),
 			MatingAge:               v.MatingAge,
 			PregnantCheckMethod:     v.PregnantCheckMethod,
 			PregnantCheckMethodName: pregnantCheckMethodName,

+ 123 - 17
model/event_sale.go

@@ -1,25 +1,131 @@
 package model
 
+import (
+	"strings"
+
+	pasturePb "gitee.com/xuyiping_admin/go_proto/proto/go/backend/cow"
+)
+
 type EventSale struct {
-	Id            int64  `json:"id"`
-	DealerId      int32  `json:"dealerId"`
-	DealerName    string `json:"dealerName"`
-	VehicleId     int32  `json:"vehicleId"`
-	SalePrice     int32  `json:"salePrice"`
-	SaleAllWeight int32  `json:"saleAllWeight"`
-	SaleAllAmount int32  `json:"saleAllAmount"`
-	SaleCowCount  int32  `json:"saleCowCount"`
-	CowIds        string `json:"cowIds"`
-	SaleAt        int64  `json:"saleAt"`
-	Remarks       string `json:"remarks"`
-	OperationId   int32  `json:"operationId"`
-	OperationName string `json:"operationName"`
-	MessageId     int64  `json:"messageId"`
-	MessageName   string `json:"messageName"`
-	CreatedAt     int64  `json:"createdAt"`
-	UpdatedAt     int64  `json:"updatedAt"`
+	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 - 20
model/event_sale_vehicle.go

@@ -1,20 +0,0 @@
-package model
-
-type EventSaleVehicle struct {
-	Id              int64  `json:"id"`
-	DealerId        int32  `json:"dealerId"`
-	DealerName      string `json:"dealerName"`
-	SaleAt          int64  `json:"saleAt"`
-	VehicleNumber   string `json:"vehicleNumber"`
-	VehicleCowCount int32  `json:"vehicleCowCount"`
-	CowIds          string `json:"cowIds"`
-	Photo1          string `json:"photo1"`
-	Photo2          string `json:"photo2"`
-	Photo3          string `json:"photo3"`
-	CreatedAt       int64  `json:"createdAt"`
-	UpdatedAt       int64  `json:"updatedAt"`
-}
-
-func (e *EventSaleVehicle) TableName() string {
-	return "event_sale_vehicle"
-}

+ 38 - 30
model/event_transfer_group.go

@@ -1,42 +1,50 @@
 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 EventTransferGroup struct {
-	Id               int64  `json:"id"`
-	CowId            int64  `json:"cowId"`
-	PenInId          int32  `json:"penInId"`
-	PenOutId         int32  `json:"penOutId"`
-	DayAge           int32  `json:"dayAge"`
-	Lact             int32  `json:"lact"`
-	TransferDate     string `json:"transferDate"`
-	TransferReasonId int64  `json:"transferReasonId"`
-	Remarks          string `json:"remarks"`
-	MessageId        int64  `json:"messageId"`
-	MessageName      string `json:"messageName"`
-	OperationId      int64  `json:"operationId"`
-	OperationName    string `json:"operationName"`
-	CreatedAt        int64  `json:"createdAt"`
-	UpdatedAt        int64  `json:"updatedAt"`
+	Id                 int64  `json:"id"`
+	PastureId          int64  `json:"pastureId"`
+	CowId              int64  `json:"cowId"`
+	PenInId            int32  `json:"penInId"`
+	PenOutId           int32  `json:"penOutId"`
+	DayAge             int32  `json:"dayAge"`
+	Lact               int32  `json:"lact"`
+	TransferDate       string `json:"transferDate"`
+	TransferReasonId   int64  `json:"transferReasonId"`
+	TransferReasonName string `json:"TransferReasonName"`
+	Remarks            string `json:"remarks"`
+	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 *EventTransferGroup) TableName() string {
 	return "event_transfer_group"
 }
-
-func NewEventTransferGroup(cow *Cow, req *pasturePb.TransferGroupEventData, currentUser *SystemUser, operationUser *SystemUser) *EventTransferGroup {
+func NewEventTransferGroup(pastureId int64, cow *Cow, req *pasturePb.TransferGroupEventData, transferPenMap map[int32]string, currentUser *SystemUser, operationUser *SystemUser) *EventTransferGroup {
+	eventAt := util.DateTimeParseLocalUnix(req.TransferDate)
 	return &EventTransferGroup{
-		CowId:            int64(req.CowId),
-		PenInId:          req.TransferInPenId,
-		PenOutId:         cow.PenId,
-		Lact:             cow.Lact,
-		DayAge:           cow.GetDayAge(),
-		TransferDate:     req.TransferDate,
-		TransferReasonId: int64(req.TransferReasonId),
-		Remarks:          req.Remarks,
-		MessageId:        currentUser.Id,
-		MessageName:      currentUser.Name,
-		OperationId:      operationUser.Id,
-		OperationName:    operationUser.Name,
+		PastureId:          pastureId,
+		CowId:              cow.Id,
+		PenInId:            req.TransferInPenId,
+		PenOutId:           cow.PenId,
+		Lact:               cow.Lact,
+		DayAge:             cow.GetEventDayAge(eventAt),
+		TransferDate:       req.TransferDate,
+		TransferReasonId:   int64(req.TransferReasonId),
+		TransferReasonName: transferPenMap[req.TransferReasonId],
+		Remarks:            req.Remarks,
+		MessageId:          currentUser.Id,
+		MessageName:        currentUser.Name,
+		OperationId:        operationUser.Id,
+		OperationName:      operationUser.Name,
 	}
 }

+ 40 - 9
model/event_weaning.go

@@ -2,45 +2,76 @@ package model
 
 import (
 	"kpt-pasture/util"
-	"time"
+	"strconv"
 
 	pasturePb "gitee.com/xuyiping_admin/go_proto/proto/go/backend/cow"
 )
 
 type EventWeaning struct {
 	Id            int64                 `json:"id"`
+	PastureId     int64                 `json:"pastureId"`
 	CowId         int64                 `json:"cowId"`
+	EarNumber     string                `json:"earNumber"`
+	DayAge        int32                 `json:"dayAge"`
+	MotherId      int64                 `json:"motherId"`
+	BullNumber    string                `json:"bullNumber"`
 	PlanDay       int64                 `json:"planDay"`
 	EndDay        int64                 `json:"endDay"`
 	RealityDay    int64                 `json:"realityDay"`
 	Status        pasturePb.IsShow_Kind `json:"status"`
 	BeforePenId   int32                 `json:"beforePenId"`
 	AfterPenId    int32                 `json:"afterPenId"`
+	Weight        int32                 `json:"weight"`
+	BirthWeight   int32                 `json:"birthWeight"`
+	BirthAt       int64                 `json:"birthAt"`
 	Remarks       string                `json:"remarks"`
 	OperationId   int32                 `json:"operationId"`
 	OperationName string                `json:"operationName"`
+	MessageId     int32                 `json:"messageId"`
+	MessageName   string                `json:"messageName"`
 	CreatedAt     int64                 `json:"createdAt"`
 	UpdatedAt     int64                 `json:"updatedAt"`
 }
 
-func (c *EventWeaning) TableName() string {
+func (e *EventWeaning) TableName() string {
 	return "event_weaning"
 }
 
-func NewEventWeaning(cowId int64, penId int32) *EventWeaning {
+func (e *EventWeaning) EventUpdate(cowInfo *Cow, weaningAt int64, weight int32, remarks string, afterPenId int32, operationUser, currentUser *SystemUser) {
+	e.Status = pasturePb.IsShow_Ok
+	e.RealityDay = weaningAt
+	e.OperationId = int32(operationUser.Id)
+	e.OperationName = operationUser.Name
+	e.MessageId = int32(currentUser.Id)
+	e.MessageName = currentUser.Name
+	e.Remarks = remarks
+	e.AfterPenId = afterPenId
+	e.Weight = weight
+	e.DayAge = cowInfo.GetEventDayAge(weaningAt)
+	e.BirthWeight = int32(cowInfo.BirthWeight)
+	e.BirthAt = cowInfo.BirthAt
+}
+
+func NewEventWeaning(pastureId int64, cowInfo *Cow, startDay, endDay string) *EventWeaning {
+	motherId, _ := strconv.ParseInt(cowInfo.MotherNumber, 10, 64)
 	return &EventWeaning{
-		CowId:       cowId,
-		PlanDay:     util.TimeParseLocalUnix(time.Now().Format(LayoutDate2)),
-		EndDay:      util.TimeParseLocalEndUnix(time.Now().Format(LayoutDate2)),
+		PastureId:   pastureId,
+		MotherId:    motherId,
+		BullNumber:  cowInfo.LastBullNumber,
+		CowId:       cowInfo.Id,
+		EarNumber:   cowInfo.EarNumber,
+		PlanDay:     util.TimeParseLocalUnix(startDay),
+		EndDay:      util.TimeParseLocalEndUnix(endDay),
 		Status:      pasturePb.IsShow_No,
-		BeforePenId: penId,
+		BeforePenId: cowInfo.PenId,
+		Weight:      0,
 	}
 }
 
-func NewEventWeaningList(cowList []*Cow) []*EventWeaning {
+func NewEventWeaningList(pastureId int64, cowList []*Cow, startDay, endDay string) []*EventWeaning {
 	var weaningList = make([]*EventWeaning, 0)
 	for _, cow := range cowList {
-		weaningList = append(weaningList, NewEventWeaning(cow.Id, cow.PenId))
+		weaningList = append(weaningList, NewEventWeaning(pastureId, cow, startDay, endDay))
 	}
 	return weaningList
 }

+ 87 - 9
model/event_weight.go

@@ -1,15 +1,23 @@
 package model
 
-import pasturePb "gitee.com/xuyiping_admin/go_proto/proto/go/backend/cow"
+import (
+	"kpt-pasture/util"
+	"math"
+	"time"
+
+	pasturePb "gitee.com/xuyiping_admin/go_proto/proto/go/backend/cow"
+)
 
 type EventWeight struct {
 	ID            int64  `json:"id"`
+	PastureId     int64  `json:"pastureId"`
 	CowId         int64  `json:"cowId"`
 	EarNumber     string `json:"earNumber"`
 	DayAge        int32  `json:"dayAge"`
 	LactationDay  int64  `json:"lactationDay"`
 	PenId         int32  `json:"penId"`
 	Lact          int32  `json:"lact"`
+	AdmissionAge  int32  `json:"admissionAge"`
 	Weight        int32  `json:"weight"`
 	Height        int32  `json:"height"`
 	WeightAt      int64  `json:"weightAt"`
@@ -26,19 +34,89 @@ func (c *EventWeight) TableName() string {
 	return "event_weight"
 }
 
-func NewEventWeight(cow *Cow, currentUser *SystemUser, weight, height int32, req *pasturePb.EventWeight) *EventWeight {
+func NewEventWeight(pastureId int64, cow *Cow, currentUser *SystemUser, item *pasturePb.EventWeight) *EventWeight {
 	return &EventWeight{
+		PastureId:     pastureId,
 		CowId:         cow.Id,
 		EarNumber:     cow.EarNumber,
-		Weight:        weight,
-		Height:        height,
+		Weight:        int32(item.Weight * 1000),
+		Height:        item.Height,
 		Lact:          cow.Lact,
-		DayAge:        cow.GetDayAge(),
-		WeightAt:      int64(req.WeightAt),
-		Remarks:       req.Remarks,
+		DayAge:        cow.GetEventDayAge(int64(item.WeightAt)),
+		WeightAt:      int64(item.WeightAt),
+		Remarks:       item.Remarks,
 		MessageId:     currentUser.Id,
 		MessageName:   currentUser.Name,
-		OperationId:   req.OperationId,
-		OperationName: req.OperationName,
+		OperationId:   item.OperationId,
+		OperationName: item.OperationName,
+		AdmissionAge:  cow.GetEventAdmissionAge(int64(item.WeightAt)),
 	}
 }
+
+type EventWeightSlice []*EventWeight
+
+func (e EventWeightSlice) ToPB(eventCowLogList []*EventCowLog) *pasturePb.CowGrowthCurveData {
+	res := &pasturePb.CowGrowthCurveData{
+		Chart: &pasturePb.CowBehaviorCurveChart{
+			Headers:   make([]string, 0),
+			Weight:    make([]float32, 0),
+			EventList: make([]*pasturePb.EventHappen, 0),
+		},
+		Table: make([]*pasturePb.CowBehaviorCurveTable, len(e)),
+	}
+
+	for i, v := range e {
+		weightAtFormat := ""
+		if v.WeightAt > 0 {
+			weightAtFormat = time.Unix(v.WeightAt, 0).Local().Format(LayoutDate2)
+		}
+		weight := float32(0)
+		if v.Weight > 0 {
+			weight = float32(v.Weight) / 1000
+		}
+
+		avgWeight := float64(0)
+		if i > 0 {
+			avgWeight = float64(v.Weight-e[i-1].Weight) / float64(v.WeightAt-e[i-1].WeightAt) / 24 / 1000
+		}
+
+		// 阶段日增重
+		stageDayAddWeight, monthAvgAddWeight := float32(0), float32(0)
+		if i > 0 {
+			stageWeight := float64(v.Weight-e[i-1].Weight) / 1000
+			t1 := time.Unix(v.WeightAt, 0).Local().Truncate(24 * time.Hour)
+			t2 := time.Unix(e[i-1].WeightAt, 0).Local().Truncate(24 * time.Hour)
+			diff := t1.Sub(t2)
+			days := int32(diff.Hours() / 24)
+			stageDayAddWeight = float32(math.Round(stageWeight/float64(days)*100) / 100)
+		}
+
+		if stageDayAddWeight > 0 {
+			monthAvgAddWeight = stageDayAddWeight * 30
+		}
+		res.Table[i] = &pasturePb.CowBehaviorCurveTable{
+			Weight:            weight,
+			AvgWeight:         float32(util.RoundToTwoDecimals(avgWeight)),
+			StageDayAddWeight: stageDayAddWeight,
+			MonthAvgAddWeight: monthAvgAddWeight,
+			WeightAtFormat:    weightAtFormat,
+			AdmissionAge:      v.AdmissionAge,
+		}
+		res.Chart.Headers = append(res.Chart.Headers, weightAtFormat)
+		res.Chart.Weight = append(res.Chart.Weight, weight)
+	}
+
+	for _, event := range eventCowLogList {
+		dateTime := ""
+		if event.EventAt > 0 {
+			dateTime = time.Unix(event.EventAt, 0).Local().Format(LayoutDate2)
+		}
+		res.Chart.EventList = append(res.Chart.EventList, &pasturePb.EventHappen{
+			Name:          event.EventTypeName,
+			DateTime:      dateTime,
+			EventTypeKind: event.EventType,
+		})
+	}
+
+	return res
+}

+ 18 - 3
model/frozen_semen.go

@@ -1,9 +1,12 @@
 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 FrozenSemen struct {
 	Id              int64                          `json:"id"`
+	PastureId       int64                          `json:"pasture_id"`
 	ParentId        int64                          `json:"parent_id"`
 	Producer        string                         `json:"producer"`
 	BullId          string                         `json:"bull_id"`
@@ -18,8 +21,9 @@ type FrozenSemen struct {
 	UpdatedAt       int64                          `json:"updated_at"`
 }
 
-func NewFrozenSemen(req *pasturePb.SearchFrozenSemenList, currentUser *SystemUser) *FrozenSemen {
+func NewFrozenSemen(pastureId int64, req *pasturePb.SearchFrozenSemenList, currentUser *SystemUser) *FrozenSemen {
 	return &FrozenSemen{
+		PastureId:       pastureId,
 		Producer:        req.Producer,
 		BullId:          req.BullId,
 		KindId:          req.CowKind,
@@ -32,10 +36,21 @@ func NewFrozenSemen(req *pasturePb.SearchFrozenSemenList, currentUser *SystemUse
 	}
 }
 
-func (e *FrozenSemen) TableName() string {
+func (f *FrozenSemen) TableName() string {
 	return "frozen_semen"
 }
 
+func (f *FrozenSemen) EventQuantityUpdate(quantity int32) {
+	f.Quantity -= quantity
+}
+
+func (f *FrozenSemen) UpdateData(req *pasturePb.SearchFrozenSemenList) {
+	f.Quantity = req.Quantity
+	f.KindId = req.CowKind
+	f.KindName = req.CowKindName
+	f.FrozenSemenType = req.FrozenSemenType
+}
+
 type FrozenSemenSlice []*FrozenSemen
 
 func (e FrozenSemenSlice) ToPB(

+ 13 - 11
model/frozen_semen_log.go

@@ -1,11 +1,11 @@
 package model
 
-import pasturePb "gitee.com/xuyiping_admin/go_proto/proto/go/backend/cow"
-
 type FrozenSemenLog struct {
 	Id            int64  `json:"id"`
+	PastureId     int64  `json:"pastureId"`
 	BullId        string `json:"bullId"`
-	CowIds        string `json:"cowId"`
+	CowId         int64  `json:"cowId"`
+	EarNumber     string `json:"earNumber"`
 	Quantity      int32  `json:"quantity"`
 	MatingAt      int64  `json:"matingAt"`
 	OperationId   int64  `json:"operationId"`
@@ -19,14 +19,16 @@ func (e *FrozenSemenLog) TableName() string {
 	return "frozen_semen_log"
 }
 
-func NewEventFrozenSemenLog(req *pasturePb.EventMating) *FrozenSemenLog {
+func NewEventFrozenSemenLog(pastureId int64, item *EventMatingCheckBatchModel) *FrozenSemenLog {
 	return &FrozenSemenLog{
-		BullId:        req.FrozenSemenNumber,
-		CowIds:        req.CowIds,
-		OperationId:   int64(req.OperationId),
-		OperationName: req.OperationName,
-		Quantity:      req.FrozenSemenCount,
-		MatingAt:      int64(req.MatingAt),
-		Remarks:       req.Remarks,
+		PastureId:     pastureId,
+		BullId:        item.FrozenSemen.BullId,
+		CowId:         item.Cow.Id,
+		EarNumber:     item.Cow.EarNumber,
+		OperationId:   item.OperationUser.Id,
+		OperationName: item.OperationUser.Name,
+		Quantity:      item.FrozenSemenCount,
+		MatingAt:      item.MatingAt,
+		Remarks:       item.Remarks,
 	}
 }

+ 35 - 0
model/h_active_original.go

@@ -0,0 +1,35 @@
+package model
+
+type HActiveOriginal struct {
+	Id           int64  `gorm:"column:id"`
+	UUID         int64  `gorm:"column:UUID"`
+	IntPastureId int32  `gorm:"column:intPastureId"`
+	EID1         int64  `gorm:"column:EID1"`
+	HeatDate     string `gorm:"column:heatdate;type:date"` // 明确指定列名
+	CreateTime   string `gorm:"column:createtime"`         // 明确指定列名
+	Low          int32  `gorm:"column:low"`
+	High         int32  `gorm:"column:high"`
+	Rumina       int32  `gorm:"column:Rumina"`
+	Active       int32  `gorm:"column:active"`
+	Intake       int32  `gorm:"column:intake"`
+	Inactive     int32  `gorm:"column:inactive"`
+	Other        int32  `gorm:"column:Other"`
+	Gasp         int32  `gorm:"column:Gasp"`
+	Voltage      int32  `gorm:"column:voltage"`
+	UpPec        int32  `gorm:"column:UpPec"`
+	Version      int32  `gorm:"column:Version"`
+	Sign         int32  `gorm:"column:Sign"`
+	Remain       int32  `gorm:"column:Remain"`
+	Feed         int32  `gorm:"column:feed"`
+	FrameId      int32  `gorm:"column:frameid"` // 明确指定列名
+	Imei         int64  `gorm:"column:Imei"`
+	ReceiveNum   int32  `gorm:"column:receivenum"`
+	FrameNb      int32  `gorm:"column:frameNb"`
+	Senser       int32  `gorm:"column:senser"`
+	HIB          string `gorm:"column:HIB"`
+	Nb           int32  `gorm:"column:nb"`
+}
+
+func (h *HActiveOriginal) TableName() string {
+	return "h_active_original"
+}

+ 3 - 1
model/immunization_plan.go

@@ -6,6 +6,7 @@ import (
 
 type ImmunizationPlan struct {
 	Id                   int64                                 `json:"id"`
+	PastureId            int64                                 `json:"pastureId"`
 	Name                 string                                `json:"name"`
 	CowType              pasturePb.CowType_Kind                `json:"cowType"`
 	Conditions           pasturePb.ImmunizationConditions_Kind `json:"conditions"`
@@ -23,8 +24,9 @@ func (i *ImmunizationPlan) TableName() string {
 	return "immunization_plan"
 }
 
-func NewImmunizationPlan(systemUser *SystemUser, req *pasturePb.ImmunizationRequest) *ImmunizationPlan {
+func NewImmunizationPlan(pastureId int64, systemUser *SystemUser, req *pasturePb.ImmunizationRequest) *ImmunizationPlan {
 	return &ImmunizationPlan{
+		PastureId:            pastureId,
 		Name:                 req.Name,
 		CowType:              req.CowType,
 		Conditions:           req.Conditions,

Энэ ялгаанд хэт олон файл өөрчлөгдсөн тул зарим файлыг харуулаагүй болно