neck_ring_handle.go 25 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680
  1. package crontab
  2. import (
  3. "fmt"
  4. "kpt-pasture/model"
  5. "kpt-pasture/util"
  6. "math"
  7. "time"
  8. pasturePb "gitee.com/xuyiping_admin/go_proto/proto/go/backend/cow"
  9. "gitee.com/xuyiping_admin/pkg/logger/zaplog"
  10. "gitee.com/xuyiping_admin/pkg/xerr"
  11. "go.uber.org/zap"
  12. )
  13. const (
  14. MinChangeFilter = -99
  15. MinRuminaFilter = -99
  16. MinChewFilter = -99
  17. MinChangeHigh = -99
  18. DefaultNb = 30
  19. DefaultScore = 100
  20. )
  21. var (
  22. defaultLimit = int32(1000)
  23. isDelete bool
  24. )
  25. // NeckRingOriginalMergeData 把脖环数据合并成2个小时的
  26. func (e *Entry) NeckRingOriginalMergeData() error {
  27. var (
  28. err error
  29. )
  30. limit := e.Cfg.NeckRingLimit
  31. if limit <= 0 {
  32. limit = defaultLimit
  33. }
  34. newTime := time.Now()
  35. //createdAt := newTime.Add(-1 * time.Hour)
  36. neckRingList := make([]*model.NeckRingOriginal, 0)
  37. if err = e.DB.Model(new(model.NeckRingOriginal)).
  38. Where("is_show = ?", pasturePb.IsShow_No).
  39. Where("neck_ring_number IN (?)", "10027").
  40. //Where("created_at <= ?", createdAt.Unix()).
  41. Order("active_date,neck_ring_number,frameid").
  42. Limit(int(limit)).Find(&neckRingList).Error; err != nil {
  43. return xerr.WithStack(err)
  44. }
  45. if len(neckRingList) <= 0 {
  46. return nil
  47. }
  48. defer func() {
  49. if newTime.Day()%15 == 0 && !isDelete {
  50. // 原始数据删除15天前的
  51. e.DB.Model(new(model.NeckRingOriginal)).
  52. Where("created_at < ?", newTime.AddDate(0, 0, -15).Unix()).
  53. Delete(new(model.NeckRingOriginal))
  54. e.DB.Model(new(model.NeckRingProcess)).
  55. Where("created_at < ?", newTime.AddDate(0, 0, -15).Unix()).
  56. Delete(new(model.NeckRingProcess))
  57. // 活动数据删除6个月前的数据
  58. e.DB.Model(new(model.NeckActiveHabit)).
  59. Where("created_at < ?", newTime.AddDate(0, -6, 0).Unix()).
  60. Delete(new(model.NeckActiveHabit))
  61. isDelete = true
  62. }
  63. }()
  64. // 计算合并
  65. neckActiveHabitList := e.recalculate(neckRingList)
  66. if len(neckActiveHabitList) <= 0 {
  67. return nil
  68. }
  69. for _, habit := range neckActiveHabitList {
  70. //更新脖环牛只相关信息 新数据直接插入
  71. historyNeckActiveHabit, ct := e.IsExistNeckActiveHabit(habit.NeckRingNumber, habit.HeatDate, habit.Frameid)
  72. if ct <= 0 {
  73. if err = e.DB.Create(habit).Error; err != nil {
  74. zaplog.Info("NeckRingOriginalMergeData-1",
  75. zap.Any("err", err),
  76. zap.Any("neckActiveHabit", habit),
  77. )
  78. }
  79. if err = e.UpdateNeckRingOriginalIsShow(habit); err != nil {
  80. zaplog.Error("NeckRingOriginalMergeData-2",
  81. zap.Any("err", err),
  82. zap.Any("neckActiveHabit", habit),
  83. )
  84. }
  85. continue
  86. }
  87. if historyNeckActiveHabit == nil || historyNeckActiveHabit.Id <= 0 {
  88. zaplog.Error("NeckRingOriginalMergeData-3",
  89. zap.Any("ct", ct),
  90. zap.Any("historyNeckActiveHabit", historyNeckActiveHabit),
  91. zap.Any("neckActiveHabit", habit),
  92. )
  93. continue
  94. }
  95. // 重新计算
  96. newNeckActiveHabit := e.againRecalculate(historyNeckActiveHabit)
  97. if newNeckActiveHabit != nil {
  98. if err = e.DB.Model(new(model.NeckActiveHabit)).
  99. Select("rumina", "intake", "inactive", "gasp", "other", "high", "active").
  100. Where("id = ?", historyNeckActiveHabit.Id).
  101. Updates(newNeckActiveHabit).Error; err != nil {
  102. zaplog.Error("NeckRingOriginalMergeData-5",
  103. zap.Any("err", err),
  104. zap.Any("ct", ct),
  105. zap.Any("historyNeckActiveHabit", historyNeckActiveHabit),
  106. zap.Any("newNeckActiveHabit", newNeckActiveHabit),
  107. )
  108. }
  109. }
  110. }
  111. return nil
  112. }
  113. func (e *Entry) NeckRingCalculate() error {
  114. pastureList := e.FindPastureList()
  115. if pastureList == nil || len(pastureList) == 0 {
  116. return nil
  117. }
  118. for _, pasture := range pastureList {
  119. if err := e.EntryUpdateActiveHabit(pasture.Id); err != nil {
  120. zaplog.Error("PastureUpdateActiveHabit", zap.Any("err", err), zap.Any("pasture", pasture))
  121. }
  122. zaplog.Info(fmt.Sprintf("PastureUpdateActiveHabit Success %d", pasture.Id))
  123. }
  124. return nil
  125. }
  126. // recalculate 合并计算
  127. func (e *Entry) recalculate(neckRingList []*model.NeckRingOriginal) []*model.NeckActiveHabit {
  128. originalMapData := make(map[string]*model.NeckRingOriginalMerge)
  129. // 合并成2个小时的
  130. for _, v := range neckRingList {
  131. xframeId := util.XFrameId(v.Frameid)
  132. mapKey := fmt.Sprintf("%s%s%s%s%d", v.NeckRingNumber, model.JoinKey, v.ActiveDate, model.JoinKey, xframeId) // 0001/2023-12-04/0 0001/2023-12-03/4
  133. if originalMapData[mapKey] == nil {
  134. originalMapData[mapKey] = new(model.NeckRingOriginalMerge)
  135. }
  136. originalMapData[mapKey].IsMageData(v, xframeId)
  137. }
  138. currTime := time.Now()
  139. // 算平均值
  140. for k, v := range originalMapData {
  141. // 过滤掉合并后不满6条数据
  142. if v.RecordCount < 6 {
  143. currMaxXframeId := util.FrameIdMapReverse[int32(currTime.Hour())]
  144. activeDateString := fmt.Sprintf("%s %02d:00:00", v.ActiveDate, v.XframeId*2+1)
  145. activeDate, _ := time.Parse(model.LayoutTime, activeDateString)
  146. if currMaxXframeId-v.XframeId <= 1 && currTime.Add(-1*time.Hour).Unix() < activeDate.Unix() {
  147. delete(originalMapData, k)
  148. }
  149. }
  150. if v.RecordCount > 6 {
  151. zaplog.Error("recalculate", zap.Any("k", k), zap.Any("v", v))
  152. delete(originalMapData, k)
  153. }
  154. v.SumAvg()
  155. }
  156. return model.NeckRingOriginalMap(originalMapData).ForMatData()
  157. }
  158. func (e *Entry) againRecalculate(data *model.NeckActiveHabit) *model.NeckActiveHabit {
  159. originalList := make([]*model.NeckRingOriginal, 0)
  160. frameIds := util.FrameIds(data.Frameid)
  161. if err := e.DB.Model(new(model.NeckRingOriginal)).
  162. Where("neck_ring_number = ?", data.NeckRingNumber).
  163. Where("active_date = ?", data.HeatDate).
  164. Where("frameid IN (?)", frameIds).
  165. Find(&originalList).Error; err != nil {
  166. return nil
  167. }
  168. newDataList := e.recalculate(originalList)
  169. if len(newDataList) != 1 {
  170. return nil
  171. }
  172. return newDataList[0]
  173. }
  174. func (e *Entry) EntryUpdateActiveHabit(pastureId int64) (err error) {
  175. // 获取这段执行数据内最大日期和最小日期
  176. xToday := &XToday{}
  177. systemConfigureList, err := e.GetSystemConfigure(pastureId)
  178. if err != nil {
  179. return xerr.WithStack(err)
  180. }
  181. for _, v := range systemConfigureList {
  182. switch v.Name {
  183. case model.MaxHabit:
  184. xToday.LastMaxHabitId = v.Value
  185. case model.High:
  186. xToday.High = int32(v.Value)
  187. case model.Rumina:
  188. xToday.Rumina = int32(v.Value)
  189. case model.XRuminaDisc:
  190. xToday.XRuminaDisc = int32(v.Value)
  191. case model.XChangeDiscount:
  192. xToday.XChangeDiscount = int32(v.Value)
  193. case model.WeeklyActive:
  194. xToday.WeeklyActive = int32(v.Value)
  195. }
  196. }
  197. currMaxHabit := &model.NeckActiveHabit{}
  198. if err = e.DB.Model(new(model.NeckActiveHabit)).
  199. Where("id > ?", xToday.LastMaxHabitId).
  200. Where("pasture_id = ?", pastureId).
  201. Where("is_show = ?", pasturePb.IsShow_No).
  202. Order("id desc").First(currMaxHabit).Error; err != nil {
  203. return xerr.WithStack(err)
  204. }
  205. if currMaxHabit.Id <= 0 || currMaxHabit.Id <= xToday.LastMaxHabitId {
  206. return nil
  207. }
  208. xToday.CurrMaxHabitId = currMaxHabit.Id
  209. defer func() {
  210. // 更新最后一次执行的id值
  211. if err == nil {
  212. e.DB.Model(new(model.SystemConfigure)).
  213. Where("name = ?", model.MaxHabit).
  214. Where("pasture_id = ?", pastureId).
  215. Update("value", currMaxHabit.Id)
  216. }
  217. }()
  218. // 更新活动滤波
  219. if err = e.FirstFilterUpdate(pastureId, xToday); err != nil {
  220. zaplog.Error("EntryUpdateActiveHabit", zap.Any("FirstFilterUpdate", err), zap.Any("xToday", xToday))
  221. }
  222. // 二次更新滤波
  223. if err = e.SecondUpdateChangeFilter(pastureId, xToday); err != nil {
  224. zaplog.Error("EntryUpdateActiveHabit", zap.Any("SecondUpdateChangeFilter", err), zap.Any("xToday", xToday))
  225. }
  226. // 活动量校正系数和健康评分
  227. if err = e.FilterCorrectAndScoreUpdate(pastureId, xToday); err != nil {
  228. zaplog.Error("EntryUpdateActiveHabit", zap.Any("ActivityVolumeChanges", err), zap.Any("xToday", xToday))
  229. }
  230. if err = e.DB.Model(new(model.NeckActiveHabit)).
  231. Where("id BETWEEN ? AND ?", xToday.LastMaxHabitId, xToday.CurrMaxHabitId).
  232. Where("pasture_id = ?", pastureId).
  233. Where("is_show = ?", pasturePb.IsShow_No).
  234. Where("change_filter = ?", model.InitChangeFilter).
  235. Updates(map[string]interface{}{
  236. "change_filter": model.DefaultChangeFilter,
  237. "rumina_filter": model.DefaultRuminaFilter,
  238. "chew_filter": model.DefaultChewFilter,
  239. }).Error; err != nil {
  240. zaplog.Error("EntryUpdateActiveHabit", zap.Any("change_filter", err), zap.Any("xToday", xToday))
  241. }
  242. if err = e.DB.Model(new(model.NeckActiveHabit)).
  243. Where("id BETWEEN ? AND ?", xToday.LastMaxHabitId, xToday.CurrMaxHabitId).
  244. Where("pasture_id = ?", pastureId).
  245. Where("is_show = ?", pasturePb.IsShow_No).
  246. Where("change_filter < ?", 0).
  247. Where("filter_correct < ?", model.DefaultFilterCorrect).
  248. Updates(map[string]interface{}{
  249. "filter_correct": model.DefaultFilterCorrect,
  250. "is_show": pasturePb.IsShow_Ok,
  251. }).Error; err != nil {
  252. zaplog.Error("EntryUpdateActiveHabit", zap.Any("filter_correct", err), zap.Any("xToday", xToday))
  253. }
  254. // 插入群体校正表
  255. if err = e.UpdateChangeAdJust(pastureId, xToday); err != nil {
  256. zaplog.Error("EntryUpdateActiveHabit", zap.Any("UpdateChangeAdJust", err), zap.Any("xToday", xToday))
  257. }
  258. return nil
  259. }
  260. // FirstFilterUpdate 首次更新活动滤波
  261. func (e *Entry) FirstFilterUpdate(pastureId int64, xToDay *XToday) (err error) {
  262. newNeckActiveHabitList := make([]*model.NeckActiveHabit, 0)
  263. if err = e.DB.Model(new(model.NeckActiveHabit)).
  264. Where("id BETWEEN ? AND ?", xToDay.LastMaxHabitId, xToDay.CurrMaxHabitId).
  265. Where("pasture_id = ?", pastureId).
  266. Where("is_show = ?", pasturePb.IsShow_No).
  267. Where("change_filter = ?", model.InitChangeFilter).
  268. Where(e.DB.Where("high >= ?", xToDay.High).Or("rumina >= ?", xToDay.Rumina)).
  269. Order("heat_date,neck_ring_number,frameid").
  270. Limit(int(defaultLimit)).Find(&newNeckActiveHabitList).Error; err != nil {
  271. return xerr.WithStack(err)
  272. }
  273. // 活动量滤波
  274. for _, v := range newNeckActiveHabitList {
  275. // 过滤牛只未绑定的脖环的数据
  276. cowInfo := e.GetCowInfoByNeckRingNumber(v.PastureId, v.NeckRingNumber)
  277. if cowInfo == nil || cowInfo.Id <= 0 {
  278. continue
  279. }
  280. frameId := v.Frameid
  281. heatDate := v.HeatDate
  282. if v.Frameid == 0 {
  283. frameId = 11
  284. heatDateParse, _ := time.Parse(model.LayoutDate2, heatDate)
  285. heatDate = heatDateParse.AddDate(0, 0, -1).Format(model.LayoutDate2)
  286. }
  287. firstFilterData := e.FindFirstFilter(pastureId, v.NeckRingNumber, heatDate, frameId)
  288. if v.FilterHigh > 0 {
  289. firstFilterData.FilterHigh = v.FilterHigh
  290. } else {
  291. if v.NeckRingNumber == firstFilterData.NeckRingNumber {
  292. firstFilterData.FilterHigh = int32(computeIfPositiveElse(float64(v.High), float64(firstFilterData.FilterHigh), 0.23, 0.77))
  293. } else {
  294. firstFilterData.FilterHigh = v.High
  295. }
  296. }
  297. if v.FilterRumina > 0 {
  298. firstFilterData.FilterRumina = v.FilterRumina
  299. } else {
  300. if v.NeckRingNumber == firstFilterData.NeckRingNumber {
  301. firstFilterData.FilterRumina = int32(computeIfPositiveElse(float64(v.Rumina), float64(firstFilterData.FilterRumina), 0.33, 0.67))
  302. } else {
  303. firstFilterData.FilterRumina = v.Rumina
  304. }
  305. }
  306. if v.FilterChew > 0 {
  307. firstFilterData.FilterChew = v.FilterChew
  308. } else {
  309. if v.NeckRingNumber == firstFilterData.NeckRingNumber {
  310. firstFilterData.FilterChew = int32(computeIfPositiveElse(float64(v.Rumina+v.Intake), float64(firstFilterData.FilterChew), 0.33, 0.67))
  311. } else {
  312. firstFilterData.FilterChew = v.Rumina + v.Intake
  313. }
  314. }
  315. // 前七天的
  316. weekHabitData := e.FindWeekHabitData(pastureId, v.NeckRingNumber, v.HeatDate, v.Frameid, xToDay)
  317. highDiff := v.FilterHigh - weekHabitData.WeekHighHabit
  318. denominator := float64(v.WeekHigh)*0.6 + float64(weekHabitData.WeekHighHabit)*0.2 + float64(xToDay.WeeklyActive)*0.2
  319. if highDiff > 0 {
  320. v.ChangeHigh = int32(math.Round((float64(highDiff) / denominator) * 100))
  321. } else {
  322. v.ChangeHigh = int32(math.Round(float64(highDiff) / denominator * 100))
  323. }
  324. if weekHabitData.WeekRuminaHabit != 0 {
  325. v.ChangeRumina = int32(math.Round(float64(v.FilterRumina-weekHabitData.WeekRuminaHabit) / float64(weekHabitData.WeekRuminaHabit) * 100))
  326. } else {
  327. v.ChangeRumina = 0
  328. }
  329. if weekHabitData.WeekChewHabit != 0 {
  330. v.ChangeChew = int32(math.Round(float64(v.FilterChew-weekHabitData.WeekChewHabit) / float64(weekHabitData.WeekChewHabit) * 100))
  331. } else {
  332. v.ChangeChew = 0
  333. }
  334. sumHabitData := e.FindSumHabitData(pastureId, v.NeckRingNumber, v.HeatDate, v.Frameid, xToDay)
  335. before3DaysNeckActiveHabit := e.FindBefore3DaysNeckActiveHabit(pastureId, v.NeckRingNumber, v.HeatDate, v.Frameid)
  336. // 更新过滤值
  337. if err = e.DB.Model(new(model.NeckActiveHabit)).
  338. Select(
  339. "filter_high", "filter_rumina", "filter_chew", "cow_id", "lact", "calving_age",
  340. "week_high_habit", "week_rumina_habit", "week_chew_habit", "week_intake_habit", "week_inactive_habit",
  341. "sum_rumina", "sum_intake", "sum_inactive", "sum_active", "sum_max_high", "sum_min_high", "sum_min_chew",
  342. "change_high", "change_rumina", "change_chew", "before_three_sum_rumina", "before_three_sum_intake",
  343. ).Where("id = ?", v.Id).
  344. Updates(map[string]interface{}{
  345. "filter_high": firstFilterData.FilterHigh,
  346. "filter_rumina": firstFilterData.FilterRumina,
  347. "filter_chew": firstFilterData.FilterChew,
  348. "cow_id": cowInfo.Id,
  349. "lact": cowInfo.Lact,
  350. "calving_age": cowInfo.CalvingAge,
  351. "week_high_habit": weekHabitData.WeekHighHabit,
  352. "week_rumina_habit": weekHabitData.WeekRuminaHabit,
  353. "week_chew_habit": weekHabitData.WeekChewHabit,
  354. "week_intake_habit": weekHabitData.WeekIntakeHabit,
  355. "week_inactive_habit": weekHabitData.WeekIntakeHabit,
  356. "sum_rumina": sumHabitData.SumRumina,
  357. "sum_intake": sumHabitData.SumIntake,
  358. "sum_inactive": sumHabitData.SumInactive,
  359. "sum_active": sumHabitData.SumActive,
  360. "sum_max_high": sumHabitData.SumMaxHigh,
  361. "sum_min_high": sumHabitData.SumMinHigh,
  362. "sum_min_chew": sumHabitData.SumMinChew,
  363. "change_high": v.ChangeHigh,
  364. "change_rumina": v.ChangeRumina,
  365. "change_chew": v.ChangeChew,
  366. "before_three_sum_rumina": before3DaysNeckActiveHabit.SumRumina,
  367. "before_three_sum_intake": before3DaysNeckActiveHabit.SumIntake,
  368. }).Error; err != nil {
  369. return xerr.WithStack(err)
  370. }
  371. }
  372. zaplog.Info("EntryUpdateActiveHabit-FilterUpdate-Success")
  373. return nil
  374. }
  375. // SecondUpdateChangeFilter 第二次更新变化趋势滤波
  376. func (e *Entry) SecondUpdateChangeFilter(pastureId int64, xToday *XToday) (err error) {
  377. newChangeFilterList := make([]*ChangeFilterData, 0)
  378. if err = e.DB.Model(new(model.NeckActiveHabit)).
  379. Select("id", "neck_ring_number", "change_high", "change_filter", "rumina_filter", "change_rumina",
  380. "chew_filter", "change_chew", "heat_date", "frameid", "IF(lact = 0, 0.8, 1) as xlc_dis_count").
  381. Where("heat_date >= ?", time.Now().AddDate(0, 0, -2).Format(model.LayoutDate2)).
  382. Where("pasture_id = ?", pastureId).
  383. Where("change_filter = ?", model.InitChangeFilter).
  384. Where("change_high > ?", MinChangeHigh).
  385. Order("neck_ring_number,heat_date,frameid").
  386. Find(&newChangeFilterList).Error; err != nil {
  387. return xerr.WithStack(err)
  388. }
  389. for _, v := range newChangeFilterList {
  390. secondFilterData := &SecondFilterData{}
  391. frameId := v.FrameId
  392. heatDate := v.HeatDate
  393. if v.FrameId == 0 {
  394. frameId = 11
  395. heatDateParse, _ := time.Parse(model.LayoutDate2, heatDate)
  396. heatDate = heatDateParse.AddDate(0, 0, -1).Format(model.LayoutDate2)
  397. }
  398. if err = e.DB.Model(new(model.NeckActiveHabit)).
  399. Select("neck_ring_number", "filter_high", "filter_rumina", "filter_chew").
  400. Where("neck_ring_number = ?", v.NeckRingNumber).
  401. Where("heat_date = ?", heatDate).
  402. Where("frameid = ?", frameId).
  403. First(&secondFilterData).Error; err != nil {
  404. zaplog.Error("EntryUpdateActiveHabit", zap.Any("FirstFilterUpdate", err))
  405. }
  406. if v.ChangeFilter > MinChangeFilter {
  407. secondFilterData.ChangeFilter = float64(v.ChangeFilter)
  408. } else {
  409. if v.NeckRingNumber == secondFilterData.NeckRingNumber {
  410. secondFilterData.ChangeFilter = secondFilterData.ChangeFilter*(1-(float64(xToday.XChangeDiscount)/10)*v.XlcDisCount) +
  411. math.Min(float64(v.ChangeHigh), secondFilterData.ChangeFilter+135)*(float64(xToday.XChangeDiscount)/10)*v.XlcDisCount
  412. } else {
  413. secondFilterData.ChangeFilter = 0
  414. }
  415. }
  416. if v.RuminaFilter > MinRuminaFilter {
  417. secondFilterData.RuminaFilter = float64(v.ChangeFilter)
  418. } else {
  419. if v.NeckRingNumber == secondFilterData.NeckRingNumber {
  420. discount := float64(xToday.XRuminaDisc) / 10 * v.XlcDisCount
  421. if math.Abs(float64(v.ChangeRumina)) > 60 {
  422. discount *= 0.5
  423. }
  424. secondFilterData.RuminaFilter = secondFilterData.RuminaFilter*(1-discount) + float64(v.ChangeRumina)*discount
  425. } else {
  426. secondFilterData.RuminaFilter = 0
  427. }
  428. }
  429. secondFilterData.RuminaFilter = math.Min(50, secondFilterData.RuminaFilter)
  430. if v.ChewFilter > MinChewFilter {
  431. secondFilterData.ChewFilter = float64(v.ChangeChew)
  432. } else {
  433. if v.NeckRingNumber == secondFilterData.NeckRingNumber {
  434. discount := float64(xToday.XRuminaDisc) / 10
  435. if math.Abs(float64(v.ChangeChew)) > 60 {
  436. discount *= 0.5
  437. }
  438. secondFilterData.ChewFilter = secondFilterData.ChewFilter*(1-discount) + float64(v.ChangeChew)*discount
  439. } else {
  440. secondFilterData.ChewFilter = 0
  441. }
  442. }
  443. secondFilterData.ChewFilter = math.Min(50, secondFilterData.ChewFilter)
  444. if err = e.DB.Model(new(model.NeckActiveHabit)).
  445. Select("change_filter", "rumina_filter", "chew_filter").
  446. Where("id = ?", v.Id).
  447. Updates(map[string]interface{}{
  448. "change_filter": secondFilterData.ChangeFilter,
  449. "rumina_filter": secondFilterData.RuminaFilter,
  450. "chew_filter": secondFilterData.ChewFilter,
  451. }).Error; err != nil {
  452. zaplog.Error("SecondUpdateChangeFilter-1", zap.Any("error", err), zap.Any("xToday", xToday))
  453. }
  454. }
  455. zaplog.Info("SecondUpdateChangeFilter-Success")
  456. return nil
  457. }
  458. // FilterCorrectAndScoreUpdate 计算活动量变化趋势校正值(活跃度校正)和健康评分
  459. func (e *Entry) FilterCorrectAndScoreUpdate(pastureId int64, xToday *XToday) error {
  460. beginDayDate := time.Now()
  461. before7DayDate := beginDayDate.AddDate(0, 0, -7).Format(model.LayoutDate2)
  462. before1DayDate := beginDayDate.AddDate(0, 0, -1).Format(model.LayoutDate2)
  463. activityVolumeList := make([]*ActivityVolume, 0)
  464. activityVolumeMap := make(map[string]*ActivityVolume)
  465. if err := e.DB.Model(new(model.NeckActiveHabit)).
  466. Select("neck_ring_number", "AVG(IF(change_filter>=60, 60, change_filter)) as avg_filter",
  467. "ROUND(STD(IF(change_filter>=60, 60, change_filter))) as std_filter", "COUNT(1) as nb").
  468. Where("heat_date BETWEEN ? AND ?", before7DayDate, before1DayDate).
  469. Where("pasture_id = ?", pastureId).
  470. Where(e.DB.Where("high > ?", xToday.High).Or("rumina >= ?", xToday.Rumina)).
  471. Where("active_time <= ?", beginDayDate.Add(-12*time.Hour).Format(model.LayoutTime)).
  472. Where("change_filter > ?", MinChangeFilter).
  473. Having("nb > ?", DefaultNb).
  474. Group("neck_ring_number").
  475. Find(&activityVolumeList).Error; err != nil {
  476. zaplog.Error("ActivityVolumeChanges-0", zap.Any("error", err), zap.Any("xToday", xToday))
  477. }
  478. if len(activityVolumeList) > 0 {
  479. for _, v := range activityVolumeList {
  480. activityVolumeMap[v.NeckRingNumber] = v
  481. }
  482. }
  483. neckActiveHabitList := make([]*model.NeckActiveHabit, 0)
  484. if err := e.DB.Model(new(model.NeckActiveHabit)).
  485. Where("id <= ?", xToday.CurrMaxHabitId).
  486. Where("heat_date >= ?", before1DayDate).
  487. Where("pasture_id = ?", pastureId).
  488. Where(e.DB.Where("high > ?", xToday.High).Or("rumina > ?", xToday.Rumina)).
  489. Find(&neckActiveHabitList).Error; err != nil {
  490. zaplog.Error("ActivityVolumeChanges-1", zap.Any("error", err), zap.Any("xToday", xToday))
  491. return xerr.WithStack(err)
  492. }
  493. for _, v := range neckActiveHabitList {
  494. if filterCorrectMap, ok := activityVolumeMap[v.NeckRingNumber]; ok {
  495. filterCorrect := model.DefaultFilterCorrect - int(math.Floor(filterCorrectMap.AvgFilter/3+float64(filterCorrectMap.StdFilter)/2))
  496. // 活动量校正系数
  497. if err := e.DB.Model(new(model.NeckActiveHabit)).
  498. Where("id = ?", v.Id).
  499. Where("neck_ring_number = ?", v.NeckRingNumber).
  500. Update("filter_correct", filterCorrect).Error; err != nil {
  501. zaplog.Error("ActivityVolumeChanges-2", zap.Any("error", err), zap.Any("xToday", xToday))
  502. continue
  503. }
  504. }
  505. cowScore := calculateScore(v)
  506. if err := e.DB.Model(new(model.NeckActiveHabit)).
  507. Where("id = ?", v.Id).
  508. Update("score", cowScore).Error; err != nil {
  509. zaplog.Error("ActivityVolumeChanges-2", zap.Any("error", err), zap.Any("xToday", xToday))
  510. continue
  511. }
  512. }
  513. zaplog.Info("EntryUpdateActiveHabit-ActivityVolumeChanges-Success")
  514. return nil
  515. }
  516. // UpdateChangeAdJust 更新群体校正数据
  517. func (e *Entry) UpdateChangeAdJust(pastureId int64, xToday *XToday) error {
  518. res := make([]*model.NeckRingBarChange, 0)
  519. oneDayAgo := time.Now().AddDate(0, 0, -1).Format(model.LayoutDate2)
  520. //SELECT h.neck_ring_number,h.heat_date, h.frameid, c.pen_id, c.pen_name, COUNT(*) as nb, ROUND(AVG(h.change_high)) as change_high,
  521. //ROUND(AVG(h.change_filter)) as change_filter F
  522. //ROM neck_active_habit as h JOIN cow as c ON h.neck_ring_number = c.neck_ring_number
  523. //WHERE h.pasture_id = 1 AND h.heat_date >= '2025-01-16' AND h.cow_id >= 0
  524. //GROUP BY h.heat_date, h.frameid, c.pen_id ORDER BY h.heat_date, h.frameid, c.pen_name
  525. if err := e.DB.Table(fmt.Sprintf("%s as h", new(model.NeckActiveHabit).TableName())).
  526. Select("h.neck_ring_number,h.heat_date, h.frameid, c.pen_id, c.pen_name, COUNT(*) as nb, ROUND(AVG(h.change_high)) as change_high, ROUND(AVG(h.change_filter)) as change_filter").
  527. Joins("JOIN cow as c ON h.neck_ring_number = c.neck_ring_number").
  528. Where("h.pasture_id = ?", pastureId).
  529. Where("h.heat_date >= ?", oneDayAgo).
  530. Where("h.cow_id >= ?", 0).
  531. Group("h.heat_date, h.frameid, c.pen_id").
  532. Order("h.heat_date, h.frameid, c.pen_name").
  533. Find(&res).Error; err != nil {
  534. return xerr.WithStack(err)
  535. }
  536. for _, v := range res {
  537. if err := e.DB.Model(new(model.NeckActiveHabit)).
  538. Where("id > ?", xToday.LastMaxHabitId).
  539. Where("heat_date = ?", v.HeatDate).
  540. Where("frameid = ?", v.FrameId).
  541. Where("neck_ring_number = ?", v.NeckRingNumber).
  542. Update("change_adjust", v.ChangeHigh).Error; err != nil {
  543. zaplog.Error("UpdateChangeAdJust-1", zap.Any("error", err), zap.Any("xToday", xToday))
  544. }
  545. }
  546. return nil
  547. }
  548. func (e *Entry) UpdateNeckRingOriginalIsShow(neckRingList *model.NeckActiveHabit) error {
  549. if err := e.DB.Model(new(model.NeckRingOriginal)).
  550. Where("neck_ring_number = ?", neckRingList.NeckRingNumber).
  551. Where("active_date = ?", neckRingList.HeatDate).
  552. Where("frameid IN (?)", util.FrameIds(neckRingList.Frameid)).
  553. Update("is_show", pasturePb.IsShow_Ok).Error; err != nil {
  554. return xerr.WithStack(err)
  555. }
  556. return nil
  557. }
  558. // computeIfPositiveElse 辅助函数来计算过滤值
  559. func computeIfPositiveElse(newValue, prevFilterValue float64, weightPrev, weightNew float64) float64 {
  560. return math.Ceil((prevFilterValue * weightPrev) + (weightNew * newValue))
  561. }
  562. // 计算 score 的逻辑
  563. func calculateScore(habit *model.NeckActiveHabit) int {
  564. // 第一部分逻辑
  565. var part1 float64
  566. switch {
  567. case (habit.CalvingAge <= 1 && habit.Lact >= 1) ||
  568. (habit.CalvingAge >= 2 && habit.CalvingAge <= 13 && (habit.SumRumina+habit.SumIntake) == 0) ||
  569. ((habit.Lact == 0 || habit.CalvingAge >= 14) && habit.ChangeFilter == -99):
  570. part1 = -199
  571. case habit.CalvingAge >= 2 && habit.CalvingAge <= 13:
  572. part1 = math.Min((float64(habit.SumRumina+habit.SumIntake)-(100+math.Min(7, float64(habit.CalvingAge))*60))/10*2, 0)
  573. case habit.ChangeFilter > -99:
  574. part1 = math.Min(0, math.Min(getValueOrDefault(float64(habit.ChangeFilter), 0), getValueOrDefault(float64(habit.SumMinHigh), 0)))*0.2 +
  575. math.Min(0, math.Min(getValueOrDefault(float64(habit.ChangeFilter), 0), getValueOrDefault(float64(habit.SumMinChew), 0)))*0.2 +
  576. getRuminaSumIntakeSumScore(float64(habit.SumRumina+habit.SumIntake)) + getAdditionalScore(habit)
  577. default:
  578. part1 = -299
  579. }
  580. // 第二部分逻辑
  581. var part2 float64
  582. switch {
  583. case habit.FirmwareVersion%100 >= 52:
  584. part2 = 1
  585. case habit.FirmwareVersion%100 >= 30 && habit.FirmwareVersion%100 <= 43:
  586. part2 = 0.8
  587. default:
  588. part2 = 0.6
  589. }
  590. // 最终 score
  591. return DefaultScore + int(math.Floor(part1*part2))
  592. }
  593. // 获取值或默认值
  594. func getValueOrDefault(value, defaultValue float64) float64 {
  595. if value > -99 {
  596. return value
  597. }
  598. return defaultValue
  599. }
  600. // 计算累计反刍得分
  601. func getRuminaSumIntakeSumScore(sum float64) float64 {
  602. switch {
  603. case sum < 80:
  604. return -30
  605. case sum < 180:
  606. return -20
  607. case sum < 280:
  608. return -10
  609. default:
  610. return 0
  611. }
  612. }
  613. // 计算额外得分
  614. func getAdditionalScore(habit *model.NeckActiveHabit) float64 {
  615. var score float64
  616. if (habit.SumRumina+habit.SumIntake < 280 || habit.SumMinHigh+habit.SumMinChew < -50) && habit.SumMaxHigh > 50 {
  617. score += 10
  618. }
  619. if habit.ChangeFilter < -30 && habit.ChangeFilter <= habit.SumMinHigh && habit.ChewFilter < -30 && habit.ChewFilter <= habit.SumMinChew {
  620. score -= 5
  621. }
  622. return score
  623. }