pen_behavior.go 8.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264
  1. package crontab
  2. import (
  3. "fmt"
  4. "kpt-pasture/model"
  5. "sort"
  6. "time"
  7. "gitee.com/xuyiping_admin/pkg/logger/zaplog"
  8. "go.uber.org/zap"
  9. )
  10. func (e *Entry) UpdatePenBehavior() error {
  11. pastureList := e.FindPastureList()
  12. if pastureList == nil || len(pastureList) == 0 {
  13. return nil
  14. }
  15. for _, pasture := range pastureList {
  16. conf, err := e.GetSystemNeckRingConfigure(pasture.Id, model.MaxPenBehavior)
  17. if err != nil {
  18. zaplog.Error("UpdatePenBehavior", zap.Any("pasture", pasture), zap.Any("err", err))
  19. continue
  20. }
  21. e.PenBehavior(pasture.Id, conf.Value)
  22. e.UpdatePenBehaviorWeekData(pasture.Id)
  23. }
  24. return nil
  25. }
  26. // PenBehavior 栏舍行为曲线
  27. func (e *Entry) PenBehavior(pastureId, maxPenBehavior int64) {
  28. // 1. 获取颈环原始数据
  29. neckRingOriginalList, err := e.getNeckRingOriginalList(pastureId, maxPenBehavior)
  30. if err != nil {
  31. zaplog.Error("PenBehavior", zap.Any("pastureId", pastureId), zap.Any("maxPenBehavior", maxPenBehavior), zap.Any("err", err))
  32. return
  33. }
  34. if len(neckRingOriginalList) <= 0 {
  35. return
  36. }
  37. // 2. 获取牛只信息
  38. cowMap, err := e.getCowMap(pastureId, neckRingOriginalList)
  39. if err != nil {
  40. zaplog.Error("PenBehavior", zap.Any("pastureId", pastureId), zap.Any("neckRingOriginalList", neckRingOriginalList), zap.Any("err", err))
  41. return
  42. }
  43. // 3. 处理栏舍行为数据
  44. penData := e.processPenBehaviorData(neckRingOriginalList, cowMap)
  45. // 4. 计算平均值和百分比
  46. e.calculateAveragesAndRates(penData)
  47. // 5. 保存数据
  48. if err = e.savePenBehaviorData(penData); err != nil {
  49. zaplog.Error("PenBehavior", zap.Any("penData", penData), zap.Any("err", err))
  50. return
  51. }
  52. sort.Slice(neckRingOriginalList, func(i, j int) bool {
  53. return neckRingOriginalList[i].Id > neckRingOriginalList[j].Id
  54. })
  55. if err = e.UpdateSystemNeckRingConfigure(pastureId, model.MaxPenBehavior, neckRingOriginalList[0].Id); err != nil {
  56. zaplog.Error("PenBehavior", zap.Any("UpdateSystemNeckRingConfigure", err), zap.Any("neckRingOriginalList", neckRingOriginalList))
  57. }
  58. }
  59. // getNeckRingOriginalList 获取颈环原始数据
  60. func (e *Entry) getNeckRingOriginalList(pastureId, maxPenBehavior int64) ([]*model.NeckRingOriginal, error) {
  61. var neckRingOriginalList []*model.NeckRingOriginal
  62. if err := e.DB.Model(new(model.NeckRingOriginal)).
  63. Where("id > ? AND pasture_id = ?", maxPenBehavior, pastureId).
  64. Order("active_date,neck_ring_number,frameid").
  65. Limit(int(defaultLimit)).
  66. Find(&neckRingOriginalList).Error; err != nil {
  67. return nil, err
  68. }
  69. return neckRingOriginalList, nil
  70. }
  71. // getCowMap 获取牛只信息映射
  72. func (e *Entry) getCowMap(pastureId int64, neckRingOriginalList []*model.NeckRingOriginal) (map[string]*model.Cow, error) {
  73. // 提取牛只ID
  74. neckRingNumberList := make([]string, 0, len(neckRingOriginalList))
  75. for _, v := range neckRingOriginalList {
  76. neckRingNumberList = append(neckRingNumberList, v.NeckRingNumber)
  77. }
  78. // 获取牛只信息
  79. cowInfoList, err := e.GetCowByNeckRingNumbers(pastureId, neckRingNumberList)
  80. if err != nil {
  81. return nil, err
  82. }
  83. // 构建牛只信息映射
  84. cowMap := make(map[string]*model.Cow, len(cowInfoList))
  85. for _, v := range cowInfoList {
  86. if v.NeckRingNumber == "" {
  87. continue
  88. }
  89. cowMap[v.NeckRingNumber] = v
  90. }
  91. return cowMap, nil
  92. }
  93. // processPenBehaviorData 处理栏舍行为数据
  94. func (e *Entry) processPenBehaviorData(neckRingOriginalList []*model.NeckRingOriginal, cowMap map[string]*model.Cow) map[string]*model.PenBehaviorData {
  95. penData := make(map[string]*model.PenBehaviorData, len(neckRingOriginalList))
  96. for _, v := range neckRingOriginalList {
  97. cowInfo, ok := cowMap[v.NeckRingNumber]
  98. if !ok {
  99. zaplog.Error("PenBehavior", zap.Any("neckRingNumber", v.NeckRingNumber))
  100. continue
  101. }
  102. key := fmt.Sprintf("%s_%d_%d", v.ActiveDate, cowInfo.PenId, v.Frameid)
  103. if data, exists := penData[key]; exists {
  104. data.CowCount++
  105. data.AvgHigh += v.High
  106. data.SumRumina += ifThenElse(v.Rumina >= 8, 1, 0)
  107. data.SumIntake += ifThenElse(v.Intake >= 8, 1, 0)
  108. data.SumRest += ifThenElse(v.Inactive >= 8, 1, 0)
  109. data.SumGasp += ifThenElse(v.Gasp >= 8, 1, 0)
  110. } else {
  111. penData[key] = &model.PenBehaviorData{
  112. PastureId: cowInfo.PastureId,
  113. PenId: cowInfo.PenId,
  114. PenName: cowInfo.PenName,
  115. HeatDate: v.ActiveDate,
  116. Frameid: v.Frameid,
  117. CowCount: 1,
  118. AvgHigh: v.High,
  119. }
  120. }
  121. }
  122. return penData
  123. }
  124. // calculateAveragesAndRates 计算平均值和百分比
  125. func (e *Entry) calculateAveragesAndRates(penData map[string]*model.PenBehaviorData) {
  126. for _, data := range penData {
  127. // 计算平均值
  128. data.AvgHigh = data.AvgHigh / data.CowCount
  129. // 计算百分比
  130. if data.CowCount > 0 {
  131. data.RuminaRate = int32(float64(data.SumRumina) / float64(data.CowCount) * 100)
  132. data.IntakeRate = int32(float64(data.SumIntake) / float64(data.CowCount) * 100)
  133. data.RestRate = int32(float64(data.SumRest) / float64(data.CowCount) * 100)
  134. data.GaspRate = int32(float64(data.SumGasp) / float64(data.CowCount) * 100)
  135. }
  136. }
  137. }
  138. // savePenBehaviorData 保存栏舍行为数据
  139. func (e *Entry) savePenBehaviorData(penData map[string]*model.PenBehaviorData) error {
  140. for _, data := range penData {
  141. // 构建活动时间
  142. activeTime := e.calculateActiveTime(data.HeatDate, data.Frameid)
  143. // 构建保存数据
  144. penBehavior := model.NewPenBehavior(data, activeTime)
  145. // 使用 Upsert 操作
  146. if err := e.DB.Model(new(model.PenBehavior)).
  147. Where("pasture_id = ? AND heat_date = ? AND pen_id = ? AND active_time = ?",
  148. penBehavior.PastureId, penBehavior.HeatDate, penBehavior.PenId, penBehavior.ActiveTime).
  149. Assign(map[string]interface{}{
  150. "frameid": penBehavior.Frameid,
  151. "cow_count": penBehavior.CowCount,
  152. "avg_high": penBehavior.AvgHigh,
  153. "sum_rumina": penBehavior.SumRumina,
  154. "sum_intake": penBehavior.SumIntake,
  155. "sum_rest": penBehavior.SumRest,
  156. "sum_gasp": penBehavior.SumGasp,
  157. "rumina_rate": penBehavior.RuminaRate,
  158. "intake_rate": penBehavior.IntakeRate,
  159. "rest_rate": penBehavior.RestRate,
  160. "gasp_rate": penBehavior.GaspRate,
  161. }).
  162. FirstOrCreate(penBehavior).Error; err != nil {
  163. return err
  164. }
  165. }
  166. return nil
  167. }
  168. func (e *Entry) UpdatePenBehaviorWeekData(pastureId int64) {
  169. nowTime := time.Now().Local()
  170. currTime := nowTime.Format(model.LayoutDate2)
  171. startTime := nowTime.AddDate(0, 0, -7).Format(model.LayoutDate2)
  172. endTime := nowTime.AddDate(0, 0, -1).Format(model.LayoutDate2)
  173. penBehaviorList := make([]*model.PenBehavior, 0)
  174. if err := e.DB.Table(fmt.Sprintf("%s as b1", new(model.PenBehavior).TableName())).
  175. Joins(fmt.Sprintf("JOIN %s as b0 ON b1.pen_id = b0.pen_id", new(model.PenBehavior).TableName())).
  176. Select(`b1.id, ROUND(AVG(b0.rumina_rate)) AS week_rumina_rate,
  177. ROUND(AVG(b0.intake_rate)) AS week_intake_rate,
  178. ROUND(AVG(b0.rest_rate)) AS week_rest_rate,
  179. ROUND(AVG(b0.gasp_rate)) AS week_gasp_rate,
  180. ROUND(STD(b0.rumina_rate)) AS rumina_std,
  181. ROUND(STD(b0.intake_rate)) AS intake_std,
  182. ROUND(STD(b0.rest_rate)) AS rest_std,
  183. ROUND(STD(b0.gasp_rate)) AS gasp_std`).
  184. Where("b1.pasture_id = ?", pastureId).
  185. Where("b1.heat_date = ?", currTime).
  186. Where("b1.week_rumina_rate = ?", 0).
  187. Where("b1.frameid = b0.frameid").
  188. Where("b0.heat_date BETWEEN ? AND ?", startTime, endTime).
  189. Find(&penBehaviorList).Error; err != nil {
  190. zaplog.Error("PenBehavior", zap.Any("penBehaviorList", penBehaviorList), zap.Any("err", err))
  191. return
  192. }
  193. for _, v := range penBehaviorList {
  194. if err := e.DB.Model(new(model.PenBehavior)).
  195. Where("id = ?", v.Id).
  196. Updates(map[string]interface{}{
  197. "week_rumina_rate": v.WeekRuminaRate,
  198. "week_intake_rate": v.WeekIntakeRate,
  199. "week_rest_rate": v.WeekRestRate,
  200. "week_gasp_rate": v.WeekGaspRate,
  201. "rumina_std": v.RuminaStd,
  202. "intake_std": v.IntakeStd,
  203. "rest_std": v.RestStd,
  204. "gasp_std": v.GaspStd,
  205. }).Error; err != nil {
  206. zaplog.Error("PenBehavior", zap.Any("penBehaviorWeekData", v), zap.Any("err", err))
  207. continue
  208. }
  209. }
  210. }
  211. // calculateActiveTime 计算活动时间
  212. func (e *Entry) calculateActiveTime(heatDate string, frameid int32) string {
  213. // 计算小时和分钟
  214. hour := (frameid / 10) * 2
  215. minute := (frameid%10)*20 - 1
  216. if minute < 0 {
  217. minute = 0
  218. }
  219. baseDate, err := time.Parse(model.LayoutDate2, heatDate)
  220. if err != nil {
  221. zaplog.Error("PenBehavior", zap.Any("calculateActiveTime", err))
  222. return ""
  223. }
  224. baseTime := time.Date(baseDate.Year(), baseDate.Month(), baseDate.Day(), int(hour), 0, 0, 0, baseDate.Location())
  225. finalTime := baseTime.Add(time.Duration(minute) * time.Minute)
  226. // 构建时间字符串
  227. return finalTime.Format(model.LayoutTime)
  228. }
  229. // ifThenElse 条件判断函数
  230. func ifThenElse(condition bool, a, b int32) int32 {
  231. if condition {
  232. return a
  233. }
  234. return b
  235. }