analysis.go 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397
  1. package backend
  2. import (
  3. "context"
  4. "fmt"
  5. "kpt-pasture/model"
  6. "kpt-pasture/util"
  7. "net/http"
  8. "strings"
  9. "time"
  10. "gitee.com/xuyiping_admin/pkg/xerr"
  11. pasturePb "gitee.com/xuyiping_admin/go_proto/proto/go/backend/cow"
  12. )
  13. // GrowthCurve 生长曲线 获取图表数据
  14. func (s *StoreEntry) GrowthCurve(ctx context.Context, req *pasturePb.SearchGrowthCurvesRequest) (*pasturePb.GrowthCurvesResponse, error) {
  15. // 查询数据
  16. cowList := make([]*model.Cow, 0)
  17. pref := s.DB.Model(new(model.Cow)).Where("admission_status = ?", pasturePb.AdmissionStatus_Admission)
  18. if req.GetCowId() != "" {
  19. pref.Where("id IN ?", strings.Split(req.CowId, ","))
  20. }
  21. if len(req.BirthDate) == 2 && req.BirthDate[0] != "" && req.BirthDate[1] != "" {
  22. t0, _ := time.Parse(time.RFC3339, req.BirthDate[0])
  23. t1, _ := time.Parse(time.RFC3339, req.BirthDate[1])
  24. pref.Where("birth_at BETWEEN ? AND ?", t0.Unix(), t1.Unix()+86399)
  25. }
  26. if err := pref.Find(&cowList).Error; err != nil {
  27. return nil, err
  28. }
  29. penList, err := s.GetPenList(ctx)
  30. if err != nil {
  31. return nil, xerr.WithStack(err)
  32. }
  33. // 计算图表数据
  34. chartsList := &pasturePb.Charts{
  35. CowId: make([]int32, 0),
  36. Weight: make([]float32, 0),
  37. DayAge: make([]int32, 0),
  38. }
  39. cowData := make([]*pasturePb.CowList, 0)
  40. for _, cow := range cowList {
  41. currentWeight := float32(cow.CurrentWeight) / 100
  42. penName := ""
  43. for _, v := range penList {
  44. if int64(cow.PenId) != v.Id {
  45. continue
  46. }
  47. penName = v.Name
  48. }
  49. cowData = append(cowData, &pasturePb.CowList{
  50. CowId: int32(cow.Id),
  51. EarNumber: cow.EarNumber,
  52. DayAge: cow.GetDayAge(),
  53. PenName: penName,
  54. CurrentWeight: currentWeight,
  55. BirthAt: int32(cow.BirthAt),
  56. BirthWeight: float32(cow.BirthWeight) / 100,
  57. LastWeightAt: int32(cow.LastWeightAt),
  58. DailyWeightGain: float32(cow.GetDayWeight() / 100),
  59. AverageDailyWeightGain: float32(cow.GetAverageDailyWeight() / 100),
  60. })
  61. chartsList.CowId = append(chartsList.CowId, int32(cow.Id))
  62. chartsList.Weight = append(chartsList.Weight, currentWeight)
  63. chartsList.DayAge = append(chartsList.DayAge, cow.GetDayAge())
  64. }
  65. // 返回数据
  66. return &pasturePb.GrowthCurvesResponse{
  67. Code: http.StatusOK,
  68. Message: "success",
  69. Data: &pasturePb.GrowthCurveData{
  70. Table: cowData,
  71. Charts: chartsList,
  72. },
  73. }, nil
  74. }
  75. func (s *StoreEntry) WeightRange(ctx context.Context, req *pasturePb.WeightRangeRequest) (*pasturePb.WeightRangeResponse, error) {
  76. cowWeightRange := make([]*model.CowWeightRange, 0)
  77. prefix := s.DB.Model(new(model.Cow)).Where("admission_status = ?", pasturePb.AdmissionStatus_Admission)
  78. if req.CowKind > 0 {
  79. prefix.Where("cow_kind = ?", req.CowKind)
  80. }
  81. if err := prefix.Select(`
  82. CASE
  83. WHEN current_weight BETWEEN 0 AND 50000 THEN '0-50'
  84. WHEN current_weight BETWEEN 50001 AND 100000 THEN '51-100'
  85. WHEN current_weight BETWEEN 100001 AND 150000 THEN '101-150'
  86. WHEN current_weight BETWEEN 150001 AND 200000 THEN '151-200'
  87. WHEN current_weight BETWEEN 200001 AND 250000 THEN '201-250'
  88. WHEN current_weight BETWEEN 250001 AND 300000 THEN '251-300'
  89. WHEN current_weight BETWEEN 300001 AND 350000 THEN '301-350'
  90. WHEN current_weight BETWEEN 350001 AND 400000 THEN '351-400'
  91. WHEN current_weight BETWEEN 400001 AND 450000 THEN '401-450'
  92. WHEN current_weight BETWEEN 450001 AND 500000 THEN '451-500'
  93. WHEN current_weight BETWEEN 500001 AND 550000 THEN '500-550'
  94. WHEN current_weight BETWEEN 550001 AND 600000 THEN '551-600'
  95. WHEN current_weight BETWEEN 600001 AND 650000 THEN '601-650'
  96. WHEN current_weight BETWEEN 650001 AND 700000 THEN '651-700'
  97. WHEN current_weight BETWEEN 700001 AND 750000 THEN '701-750'
  98. ELSE '750+'
  99. END AS weight_range,
  100. COUNT(*) AS count `,
  101. ).Group("weight_range").Order("MIN(current_weight)").Find(&cowWeightRange).Error; err != nil {
  102. return nil, err
  103. }
  104. if len(cowWeightRange) == 0 {
  105. return &pasturePb.WeightRangeResponse{
  106. Code: http.StatusOK,
  107. Message: "ok",
  108. Data: &pasturePb.WeightRangeData{
  109. CowList: make([]*pasturePb.CowList, 0),
  110. WeightBarChart: &pasturePb.WeightBarChart{
  111. Header: make([]string, 0),
  112. Data: make([]int32, 0),
  113. },
  114. },
  115. }, nil
  116. }
  117. header := make([]string, 0)
  118. data := make([]int32, 0)
  119. for _, v := range cowWeightRange {
  120. header = append(header, v.WeightRange)
  121. data = append(data, v.Count)
  122. }
  123. // 牛只详情列表
  124. pref := s.DB.Model(new(model.Cow)).Where("admission_status = ?", pasturePb.AdmissionStatus_Admission)
  125. if req.CowKind > 0 {
  126. pref.Where("cow_kind = ?", req.CowKind)
  127. }
  128. cowList := make([]*model.Cow, 0)
  129. if req.MinWeight >= 0 && req.MaxWeight >= 0 && req.MinWeight < req.MaxWeight {
  130. pref.Where("current_weight BETWEEN ? AND ? ", req.MinWeight*1000, req.MaxWeight*1000)
  131. }
  132. if err := pref.Find(&cowList).Error; err != nil {
  133. return nil, err
  134. }
  135. penMap := s.PenMap(ctx)
  136. return &pasturePb.WeightRangeResponse{
  137. Code: http.StatusOK,
  138. Message: "ok",
  139. Data: &pasturePb.WeightRangeData{
  140. CowList: model.CowSlice(cowList).WeightRangeToPB(penMap),
  141. WeightBarChart: &pasturePb.WeightBarChart{
  142. Header: header,
  143. Data: data,
  144. },
  145. },
  146. }, nil
  147. }
  148. func (s *StoreEntry) MatingTimely(ctx context.Context, req *pasturePb.MatingTimelyRequest) (*model.MatingTimelyResponse, error) {
  149. matingTimelyChart := make([]*model.MatingTimelyChart, 0)
  150. sql := `SELECT calving_age,cow_type, DATE_FORMAT(FROM_UNIXTIME(reality_day), '%Y-%m-%d') AS reality_day, lact_group
  151. FROM (
  152. SELECT calving_age, cow_type,reality_day, '0' AS lact_group
  153. FROM event_mating
  154. WHERE lact = 0 AND status = 1
  155. UNION ALL
  156. SELECT calving_age,cow_type, reality_day, '1' AS lact_group
  157. FROM event_mating
  158. WHERE lact = 1 AND status = 1
  159. UNION ALL
  160. SELECT calving_age,cow_type, reality_day, '2' AS lact_group
  161. FROM event_mating
  162. WHERE lact = 2 AND status = 1
  163. UNION ALL
  164. SELECT calving_age, cow_type, reality_day, '3+' AS lact_group
  165. FROM event_mating
  166. WHERE lact >= 3 AND status = 1
  167. ) AS subquery WHERE 1 = 1 `
  168. whereSql := ""
  169. if req.CowType > 0 {
  170. whereSql += fmt.Sprintf("AND cow_type = %d ", req.CowType)
  171. }
  172. if req.StartDayAt > 0 && req.EndDayAt > 0 {
  173. whereSql += fmt.Sprintf("AND reality_day BETWEEN %d AND %d", req.StartDayAt, req.EndDayAt)
  174. }
  175. if err := s.DB.Raw(fmt.Sprintf("%s %s", sql, whereSql)).Find(&matingTimelyChart).Error; err != nil {
  176. return nil, err
  177. }
  178. chart := &model.CowMatingChart{
  179. Lact0: make([][]string, 0),
  180. Lact1: make([][]string, 0),
  181. Lact2: make([][]string, 0),
  182. Lact3: make([][]string, 0),
  183. }
  184. if len(matingTimelyChart) == 0 {
  185. return &model.MatingTimelyResponse{
  186. Code: http.StatusOK,
  187. Message: "ok",
  188. Data: &model.MatingTimelyData{
  189. CowList: make([]*pasturePb.CowList, 0),
  190. Chart: chart,
  191. },
  192. }, nil
  193. }
  194. for _, v := range matingTimelyChart {
  195. switch v.LactGroup {
  196. case "0":
  197. chart.Lact0 = append(chart.Lact0, []string{fmt.Sprintf("%d", v.CalvingAge), v.RealityDay})
  198. case "1":
  199. chart.Lact1 = append(chart.Lact1, []string{fmt.Sprintf("%d", v.CalvingAge), v.RealityDay})
  200. case "2":
  201. chart.Lact2 = append(chart.Lact2, []string{fmt.Sprintf("%d", v.CalvingAge), v.RealityDay})
  202. case "3+":
  203. chart.Lact3 = append(chart.Lact3, []string{fmt.Sprintf("%d", v.CalvingAge), v.RealityDay})
  204. }
  205. }
  206. // 牛只详情列表
  207. eventMatingList := make([]*model.EventMating, 0)
  208. pref := s.DB.Model(new(model.EventMating)).
  209. Where("status = ?", pasturePb.IsShow_Ok)
  210. if req.CowType > 0 {
  211. pref.Where("cow_type = ?", req.CowType)
  212. }
  213. if req.StartDayAt > 0 && req.EndDayAt > 0 {
  214. pref.Where("reality_day BETWEEN ? AND ?", req.StartDayAt, req.EndDayAt)
  215. }
  216. if err := pref.Find(&eventMatingList).Error; err != nil {
  217. return nil, err
  218. }
  219. return &model.MatingTimelyResponse{
  220. Code: http.StatusOK,
  221. Message: "ok",
  222. Data: &model.MatingTimelyData{
  223. CowList: model.EventMatingSlice(eventMatingList).ToPB2(),
  224. Chart: chart,
  225. },
  226. }, nil
  227. }
  228. func (s *StoreEntry) PenWeight(ctx context.Context, req *pasturePb.PenWeightRequest, pagination *pasturePb.PaginationModel) (*pasturePb.PenWeightResponse, error) {
  229. penWeightList := make([]*model.PenWeight, 0)
  230. pref := s.DB.Model(new(model.Cow)).
  231. Select(`
  232. pen_id,
  233. CEILING(AVG(current_weight) / 1000 ) AS avg_weight,
  234. CEILING(SUM(current_weight) / 1000 ) AS all_weight,
  235. COUNT(*) AS cow_count`,
  236. ).Where("admission_status = ?", pasturePb.AdmissionStatus_Admission)
  237. if len(req.PenId) > 0 && req.BarId <= 0 {
  238. pref.Where("pen_id IN ?", req.PenId)
  239. }
  240. if err := pref.Group("pen_id").
  241. Order("pen_id").
  242. Find(&penWeightList).Error; err != nil {
  243. return nil, err
  244. }
  245. chart := &pasturePb.PenWeightChart{
  246. Header: make([]string, 0),
  247. AllWeight: make([]int32, 0),
  248. AvgWeight: make([]int32, 0),
  249. CowCount: make([]int32, 0),
  250. }
  251. if len(penWeightList) <= 0 {
  252. return &pasturePb.PenWeightResponse{
  253. Code: http.StatusOK,
  254. Message: "ok",
  255. Data: &pasturePb.PenWeightData{
  256. CowList: make([]*pasturePb.CowList, 0),
  257. Chart: chart,
  258. },
  259. }, nil
  260. }
  261. cowList := make([]*model.Cow, 0)
  262. var count int64 = 0
  263. prefList := s.DB.Model(new(model.Cow)).
  264. Where("admission_status = ?", pasturePb.AdmissionStatus_Admission)
  265. if len(req.PenId) <= 0 && req.BarId > 0 {
  266. prefList.Where("pen_id IN ?", []int32{req.BarId})
  267. } else {
  268. prefList.Where("pen_id IN ?", req.PenId)
  269. }
  270. if err := prefList.Count(&count).Limit(int(pagination.PageSize)).
  271. Offset(int(pagination.PageOffset)).Order("pen_id").
  272. Find(&cowList).Error; err != nil {
  273. return nil, xerr.WithStack(err)
  274. }
  275. penMap := s.PenMap(ctx)
  276. return &pasturePb.PenWeightResponse{
  277. Code: http.StatusOK,
  278. Message: "ok",
  279. Data: &pasturePb.PenWeightData{
  280. CowList: model.CowSlice(cowList).ToPB2(penMap, penWeightList),
  281. Total: int32(count),
  282. Page: pagination.Page,
  283. PageSize: pagination.PageSize,
  284. Chart: model.PenWeightSlice(penWeightList).ToPB(penMap),
  285. },
  286. }, nil
  287. }
  288. func (s *StoreEntry) AbortionRate(ctx context.Context, req *pasturePb.AbortionRateRequest) (*pasturePb.AbortionRateResponse, error) {
  289. dayTimeList, err := util.GetMonthsInRange(req.StartDayTime, req.EndDayTime)
  290. if err != nil {
  291. return nil, xerr.WithStack(err)
  292. }
  293. lastDayForMonth := make([]string, 0)
  294. for _, v := range dayTimeList {
  295. lastDayTime, _ := util.GetLastDayOfMonth(v)
  296. lastDayForMonth = append(lastDayForMonth, lastDayTime)
  297. }
  298. // 历史每月怀孕牛头数量
  299. cowPregnantMonthList := make([]*model.CowPregnantMonth, 0)
  300. if err = s.DB.Model(new(model.Cow)).
  301. Select(`
  302. COUNT(cow_id) AS cow_count,
  303. DATE_FORMAT(FROM_UNIXTIME(updated_at),'%Y-%m-%d') as month`,
  304. ).Where("status = ?", pasturePb.IsShow_Ok).
  305. Where("cow_type = ?", req.CowType).
  306. Where("DATE_FORMAT(FROM_UNIXTIME(`created_at`),'%Y-%m-%d') IN ?", lastDayForMonth).
  307. Group("month").Find(&cowPregnantMonthList).Error; err != nil {
  308. return nil, xerr.WithStack(err)
  309. }
  310. // 历史每月流产牛头数量
  311. cowAbortionMonthList := make([]*model.CowPregnantMonth, 0)
  312. if err = s.DB.Model(new(model.EventAbortion)).
  313. Select(`
  314. COUNT(cow_id) AS cow_count,
  315. DATE_FORMAT(FROM_UNIXTIME(abortion_at),'%Y-%m') as month`,
  316. ).Where("status = ?", pasturePb.IsShow_Ok).
  317. Where("cow_type = ?", req.CowType).
  318. Where("DATE_FORMAT(FROM_UNIXTIME(`created_at`),'%Y-%m') IN = ?", dayTimeList).
  319. Group("month").Find(&cowAbortionMonthList).Error; err != nil {
  320. return nil, xerr.WithStack(err)
  321. }
  322. chart := &pasturePb.AbortionRateChart{
  323. Header: make([]string, 0),
  324. AbortionCountMonth: make([]int32, 0),
  325. PregnantCountMonth: make([]int32, 0),
  326. AbortionRateMonth: make([]float32, 0),
  327. }
  328. table := make([]*pasturePb.AbortionRateTable, 0)
  329. for _, v2 := range cowAbortionMonthList {
  330. pregnantCountMonth := int32(0)
  331. for _, v := range cowPregnantMonthList {
  332. if v.Month == v2.Month {
  333. v.CowCount = v2.CowCount
  334. pregnantCountMonth = v.CowCount
  335. }
  336. }
  337. abortionRateMonth := float64(0)
  338. if pregnantCountMonth > 0 && v2.CowCount > 0 {
  339. abortionRateMonth = util.Ceil(float64(pregnantCountMonth) / float64(v2.CowCount) * 100)
  340. }
  341. chart.Header = append(chart.Header, v2.Month)
  342. chart.AbortionCountMonth = append(chart.AbortionCountMonth, v2.CowCount)
  343. chart.PregnantCountMonth = append(chart.PregnantCountMonth, pregnantCountMonth)
  344. chart.AbortionRateMonth = append(chart.AbortionRateMonth, float32(abortionRateMonth))
  345. table = append(table, &pasturePb.AbortionRateTable{
  346. AbortionCount: v2.CowCount,
  347. MonthName: v2.Month,
  348. PregnantCount: pregnantCountMonth,
  349. AbortionRate: float32(abortionRateMonth),
  350. })
  351. }
  352. return &pasturePb.AbortionRateResponse{
  353. Code: http.StatusOK,
  354. Message: "ok",
  355. Data: &pasturePb.AbortionRateData{
  356. Chart: chart,
  357. Table: table,
  358. },
  359. }, nil
  360. }