analysis_more.go 8.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287
  1. package backend
  2. import (
  3. "context"
  4. "fmt"
  5. "kpt-pasture/model"
  6. "kpt-pasture/util"
  7. "net/http"
  8. "sort"
  9. "time"
  10. "gitee.com/xuyiping_admin/pkg/xerr"
  11. pasturePb "gitee.com/xuyiping_admin/go_proto/proto/go/backend/cow"
  12. )
  13. func (s *StoreEntry) PenBehavior(ctx context.Context, req *pasturePb.BarnBehaviorCurveRequest) (*pasturePb.BarnBehaviorCurveResponse, error) {
  14. userModel, err := s.GetUserModel(ctx)
  15. if err != nil {
  16. return nil, err
  17. }
  18. if req.StartAt == 0 || req.EndAt == 0 || req.EndAt < req.StartAt {
  19. return nil, xerr.Customf("时间范围错误")
  20. }
  21. startTime := time.Unix(int64(req.StartAt), 0).Local().Format(model.LayoutDate2)
  22. endTime := time.Unix(int64(req.EndAt), 0).Local().Format(model.LayoutDate2)
  23. penBehaviorList := make([]*model.PenBehavior, 0)
  24. if err = s.DB.Model(new(model.PenBehavior)).
  25. Where("pasture_id = ?", userModel.AppPasture.Id).
  26. Where("pen_id = ?", req.PenId).
  27. Where("heat_date BETWEEN ? AND ?", startTime, endTime).
  28. Find(&penBehaviorList).Error; err != nil {
  29. return nil, err
  30. }
  31. return &pasturePb.BarnBehaviorCurveResponse{
  32. Code: http.StatusOK,
  33. Msg: "ok",
  34. Data: model.PenBehaviorSlice(penBehaviorList).ToPB(),
  35. }, nil
  36. }
  37. func (s *StoreEntry) PenBehavior2(ctx context.Context, req *pasturePb.BarnBehaviorCurveRequest) (*model.BarnBehaviorCurveResponse, error) {
  38. userModel, err := s.GetUserModel(ctx)
  39. if err != nil {
  40. return nil, err
  41. }
  42. if req.StartAt == 0 || req.EndAt == 0 || req.EndAt < req.StartAt {
  43. return nil, xerr.Customf("时间范围错误")
  44. }
  45. startTime := time.Unix(int64(req.StartAt), 0).Local().Format(model.LayoutDate2)
  46. endTime := time.Unix(int64(req.EndAt), 0).Local().Format(model.LayoutDate2)
  47. penBehaviorList := make([]*model.PenBehavior, 0)
  48. if err = s.DB.Model(new(model.PenBehavior)).
  49. Where("pasture_id = ?", userModel.AppPasture.Id).
  50. Where("pen_id = ?", req.PenId).
  51. Where("heat_date BETWEEN ? AND ?", startTime, endTime).
  52. Find(&penBehaviorList).Error; err != nil {
  53. return nil, err
  54. }
  55. return &model.BarnBehaviorCurveResponse{
  56. Code: http.StatusOK,
  57. Msg: "ok",
  58. Data: model.PenBehaviorSlice(penBehaviorList).ToPB2(),
  59. }, nil
  60. }
  61. func (s *StoreEntry) PenBehaviorDaily(ctx context.Context, req *pasturePb.BarnMonitorRequest) (*pasturePb.BarnMonitorResponse, error) {
  62. userModel, err := s.GetUserModel(ctx)
  63. if err != nil {
  64. return nil, xerr.WithStack(err)
  65. }
  66. if req.StartAt == 0 || req.EndAt == 0 || req.EndAt < req.StartAt {
  67. return nil, xerr.Customf("时间范围错误")
  68. }
  69. startDate := time.Unix(int64(req.StartAt), 0).Local().Format(model.LayoutDate2)
  70. endDate := time.Unix(int64(req.EndAt), 0).Local().Format(model.LayoutDate2)
  71. dataTimeRange, err := util.GetDaysBetween(startDate, endDate)
  72. if err != nil {
  73. return nil, xerr.WithStack(err)
  74. }
  75. penBehaviorDayModelList := make([]*model.PenBehaviorDayModel, 0)
  76. pref := s.DB.Model(new(model.PenBehaviorDay)).
  77. Select(`distinct(heat_date) as heat_date,pen_id,pen_name,rumina_std,cow_count,day_rumina,day_intake,
  78. day_inactive,day_milk,day_rumina+day_intake as day_chew,24*60 - day_active as day_immobility`).
  79. Where("pasture_id = ?", userModel.AppPasture.Id).
  80. Where("heat_date BETWEEN ? AND ?", startDate, endDate)
  81. if len(req.BarnIds) == 0 && req.BehaviorKind > 0 {
  82. if err = pref.Where("pen_id IN (?)", req.BarnIds).
  83. Order("heat_date").
  84. Find(&penBehaviorDayModelList).Error; err != nil {
  85. return nil, xerr.WithStack(err)
  86. }
  87. } else {
  88. if err = pref.Group("heat_date,pen_id").
  89. Order("heat_date,pen_id").
  90. Find(&penBehaviorDayModelList).Error; err != nil {
  91. return nil, xerr.WithStack(err)
  92. }
  93. }
  94. return &pasturePb.BarnMonitorResponse{
  95. Code: http.StatusOK,
  96. Msg: "ok",
  97. Data: model.PenBehaviorDayModelSlice(penBehaviorDayModelList).ToPB(dataTimeRange),
  98. }, err
  99. }
  100. func (s *StoreEntry) CowBehaviorDistribution(ctx context.Context, req *pasturePb.CowBehaviorDistributionRequest) (*pasturePb.CowBehaviorDistributionResponse, error) {
  101. userModel, err := s.GetUserModel(ctx)
  102. if err != nil {
  103. return nil, xerr.WithStack(err)
  104. }
  105. // 校验时间必须比当天时间小一天
  106. if time.Now().Local().Format(model.LayoutDate2) == req.DateTime {
  107. return nil, xerr.Customf("时间范围错误")
  108. }
  109. milkDailList := make([]*model.MilkDaily, 0)
  110. pref := s.DB.Table(fmt.Sprintf("%s as a", new(model.Cow).TableName())).
  111. Joins(fmt.Sprintf("JOIN %s AS b on a.id = b.cow_id", new(model.MilkDaily).TableName())).
  112. Select("b.*").
  113. Where("a.pasture_id = ?", userModel.AppPasture.Id).
  114. Where("a.neck_ring_number != ?", "").
  115. Where("a.sex = ?", pasturePb.Genders_Female).
  116. Where("b.heat_date = ? ", req.DateTime).
  117. Where("b.day_high > ?", 0)
  118. if len(req.PenIds) > 0 {
  119. pref.Where("a.pen_id IN (?)", req.PenIds)
  120. }
  121. if err = pref.Order("b.breed_status,b.lactation_age").
  122. Find(&milkDailList).Error; err != nil {
  123. return nil, xerr.WithStack(err)
  124. }
  125. // 未配 空怀 怀孕 配种
  126. data := &pasturePb.CowBehaviorDistributionItem{
  127. Headers: make([]string, 0),
  128. Color: make([]string, 0),
  129. MedianLine: make(map[string]float32),
  130. CalvingAge: make([]int32, 0),
  131. UnBreed: make([]*pasturePb.CowBehaviorData, 0),
  132. Breed: make([]*pasturePb.CowBehaviorData, 0),
  133. Pregnant: make([]*pasturePb.CowBehaviorData, 0),
  134. Empty: make([]*pasturePb.CowBehaviorData, 0),
  135. }
  136. breedStatus := s.BreedStatusEnumList()
  137. for _, v := range breedStatus {
  138. if v.Value == int32(pasturePb.BreedStatus_Abort) ||
  139. v.Value == int32(pasturePb.BreedStatus_Calving) ||
  140. v.Value == int32(pasturePb.BreedStatus_No_Mating) ||
  141. v.Value == int32(pasturePb.BreedStatus_Invalid) {
  142. continue
  143. }
  144. data.Headers = append(data.Headers, v.Label)
  145. switch v.Label {
  146. case "未配":
  147. data.Color = append(data.Color, "#b53827")
  148. case "空怀":
  149. data.Color = append(data.Color, "#2784b5")
  150. case "怀孕":
  151. data.Color = append(data.Color, "#2757b5")
  152. case "配种":
  153. data.Color = append(data.Color, "#27b560")
  154. }
  155. }
  156. if len(milkDailList) <= 0 {
  157. return &pasturePb.CowBehaviorDistributionResponse{
  158. Code: http.StatusOK,
  159. Msg: "ok",
  160. Data: data,
  161. }, nil
  162. }
  163. for _, v := range milkDailList {
  164. dayData := int32(0)
  165. switch req.BehaviorKind {
  166. case pasturePb.Behavior_Rumina:
  167. dayData = v.DayRumina
  168. case pasturePb.Behavior_Intake:
  169. dayData = v.DayIntake
  170. case pasturePb.Behavior_Reset:
  171. dayData = v.DayInactive
  172. case pasturePb.Behavior_Immobility:
  173. dayData = 24*60 - v.DayActive
  174. case pasturePb.Behavior_Chew:
  175. dayData = v.DayRumina + v.DayIntake
  176. }
  177. switch v.BreedStatus {
  178. case pasturePb.BreedStatus_Calving:
  179. data.UnBreed = append(data.UnBreed, &pasturePb.CowBehaviorData{
  180. EarNumber: v.EarNumber,
  181. CalvingAge: v.LactationAge,
  182. DayData: dayData,
  183. })
  184. case pasturePb.BreedStatus_Empty:
  185. data.Empty = append(data.Empty, &pasturePb.CowBehaviorData{
  186. EarNumber: v.EarNumber,
  187. CalvingAge: v.LactationAge,
  188. DayData: dayData,
  189. })
  190. case pasturePb.BreedStatus_UnBreed:
  191. data.UnBreed = append(data.UnBreed, &pasturePb.CowBehaviorData{
  192. EarNumber: v.EarNumber,
  193. CalvingAge: v.LactationAge,
  194. DayData: dayData,
  195. })
  196. case pasturePb.BreedStatus_Breeding:
  197. data.Breed = append(data.Breed, &pasturePb.CowBehaviorData{
  198. EarNumber: v.EarNumber,
  199. CalvingAge: v.LactationAge,
  200. DayData: dayData,
  201. })
  202. case pasturePb.BreedStatus_Pregnant:
  203. data.Pregnant = append(data.Pregnant, &pasturePb.CowBehaviorData{
  204. EarNumber: v.EarNumber,
  205. CalvingAge: v.LactationAge,
  206. DayData: dayData,
  207. })
  208. }
  209. }
  210. // 获取Breed的中位数
  211. if len(data.Breed) > 0 {
  212. data.MedianLine["breed"] = float32(getMedian(data.Breed, func(p *pasturePb.CowBehaviorData) int {
  213. return int(p.DayData)
  214. }))
  215. }
  216. if len(data.Pregnant) > 0 {
  217. data.MedianLine["pregnant"] = float32(getMedian(data.Pregnant, func(p *pasturePb.CowBehaviorData) int {
  218. return int(p.DayData)
  219. }))
  220. }
  221. if len(data.Empty) > 0 {
  222. data.MedianLine["empty"] = float32(getMedian(data.Empty, func(p *pasturePb.CowBehaviorData) int {
  223. return int(p.DayData)
  224. }))
  225. }
  226. if len(data.UnBreed) > 0 {
  227. data.MedianLine["unBreed"] = float32(getMedian(data.UnBreed, func(p *pasturePb.CowBehaviorData) int {
  228. return int(p.DayData)
  229. }))
  230. }
  231. return &pasturePb.CowBehaviorDistributionResponse{
  232. Code: http.StatusOK,
  233. Msg: "ok",
  234. Data: data,
  235. }, err
  236. }
  237. // 获取结构体切片中某个int字段的中位值
  238. func getMedian(dataList []*pasturePb.CowBehaviorData, getField func(*pasturePb.CowBehaviorData) int) float64 {
  239. // 1. 提取字段值
  240. values := make([]int, len(dataList))
  241. for i, p := range dataList {
  242. values[i] = getField(p)
  243. }
  244. // 2. 排序
  245. sort.Ints(values)
  246. // 3. 计算中位数
  247. n := len(values)
  248. if n == 0 {
  249. return 0
  250. }
  251. if n%2 == 1 {
  252. return float64(values[n/2])
  253. }
  254. return float64(values[n/2-1]+values[n/2]) / 2.0
  255. }