statistic.go 5.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210
  1. // Copyright 2019 getensh.com. All rights reserved.
  2. // Use of this source code is governed by getensh.com.
  3. package vote
  4. import (
  5. "context"
  6. "encoding/json"
  7. "fmt"
  8. "git.getensh.com/common/gopkgs/cache"
  9. "git.getensh.com/common/gopkgs/logger"
  10. "go.uber.org/zap"
  11. "google.golang.org/grpc/status"
  12. "gopkg.in/mgo.v2/bson"
  13. "property-garden/errors"
  14. dbmodel "property-garden/model"
  15. "property-garden/parser"
  16. pb_v1 "property-garden/pb/v1"
  17. "property-garden/utils"
  18. "strconv"
  19. )
  20. func checkVoteResultStatisticParam(req *pb_v1.VoteResultStatisticRequest) error {
  21. switch {
  22. case req.GardenId == 0:
  23. return status.Error(10003, "小区不能为空")
  24. case req.Id == 0:
  25. return status.Error(10003, "投票id不能为空")
  26. }
  27. return nil
  28. }
  29. func StatisticToCache(reply *pb_v1.VoteResultStatisticReply, gardenId int64, id int64) {
  30. key := fmt.Sprintf("vote_statisic_%d,%d", gardenId, id)
  31. bytes, _ := json.Marshal(reply)
  32. cache.Redis().SetEx(key, 30, string(bytes))
  33. }
  34. func StatisticFromCache(gardenId int64, id int64) pb_v1.VoteResultStatisticReply {
  35. key := fmt.Sprintf("vote_statisic_%d,%d", gardenId, id)
  36. str, _ := cache.Redis().Get(key)
  37. ret := pb_v1.VoteResultStatisticReply{}
  38. if str == "" {
  39. return ret
  40. }
  41. json.Unmarshal([]byte(str), &ret)
  42. return ret
  43. }
  44. func StatisticDelCache(gardenId int64, id int64) {
  45. key := fmt.Sprintf("vote_statisic_%d,%d", gardenId, id)
  46. cache.Redis().Del(key)
  47. }
  48. func VoteResultStatistic(ctx context.Context, req *pb_v1.VoteResultStatisticRequest) (reply *pb_v1.VoteResultStatisticReply, err error) {
  49. reply = &pb_v1.VoteResultStatisticReply{}
  50. // 捕获各个task中的异常并返回给调用者
  51. defer func() {
  52. if r := recover(); r != nil {
  53. err = fmt.Errorf("%+v", r)
  54. e := &status.Status{}
  55. if er := json.Unmarshal([]byte(err.Error()), e); er != nil {
  56. logger.Error("err",
  57. zap.String("system_err", err.Error()),
  58. zap.Stack("stacktrace"))
  59. }
  60. }
  61. }()
  62. // 参数检查
  63. err = checkVoteResultStatisticParam(req)
  64. if err != nil {
  65. return nil, err
  66. }
  67. dbname := utils.GetGardenDbName(req.GardenId)
  68. vote, err := getVoteOrigin(dbname, req.Id)
  69. if err != nil {
  70. return nil, err
  71. }
  72. voteM := map[int64]*pb_v1.VoteTopic{}
  73. for _, v := range vote.Topics {
  74. voteM[v.Number] = v
  75. }
  76. p := dbmodel.VoteAnswer{}
  77. exist, err := dbmodel.CheckDbExist(parser.Session, dbmodel.VoteMgoDb)
  78. if err != nil {
  79. return nil, errors.DataBaseError
  80. }
  81. collection := parser.Session.DB(dbmodel.VoteMgoDb).C(dbmodel.VoteCollection(req.GardenId, req.Id))
  82. count := int64(0)
  83. if exist {
  84. count, err = p.Count(collection, nil)
  85. if err != nil {
  86. return nil, errors.DataBaseError
  87. }
  88. }
  89. if count > 0 {
  90. tmp := StatisticFromCache(req.GardenId, req.Id)
  91. if len(tmp.List) > 0 {
  92. *reply = tmp
  93. return reply, nil
  94. }
  95. }
  96. reply.List = make([]*pb_v1.VoteResultStatisticItem, len(vote.Topics))
  97. for i, v := range vote.Topics {
  98. reply.List[i] = &pb_v1.VoteResultStatisticItem{
  99. TopicType: v.TopicType,
  100. TopicName: v.TopicName,
  101. // 总星数,当为评分题时有效
  102. Star: v.Star,
  103. // 是否必选
  104. Must: v.Must,
  105. // 题目编号
  106. Number: v.Number,
  107. // 选择题的选项内容
  108. ChoiceItems: v.ChoiceItems,
  109. // 选择题的选项回答统计,当题目类型为选择题时有效
  110. ChoiceStatistic: []*pb_v1.VoteResultStatisticChoice{},
  111. // 评分题的分数回答统计,当题目类型为评分题时有效
  112. StarStatistic: []*pb_v1.VoteResultStatisticStar{},
  113. // 平均分, 当题目类型为评分题时有效
  114. StarAvg: 0,
  115. }
  116. if count == 0 {
  117. continue
  118. }
  119. // 填空题不统计
  120. if v.TopicType == TopicTypeCompletion {
  121. continue
  122. }
  123. // 评分题统计
  124. if v.TopicType == TopicTypeStar {
  125. filter := []bson.M{
  126. {"$project": bson.M{"answers": bson.M{"$filter": bson.M{"input": "$answers", "as": "answer", "cond": bson.M{"$eq": []interface{}{"$$answer.number", v.Number}}}}}},
  127. {"$unwind": "$answers"},
  128. {"$group": bson.M{"_id": "$answers.star_answer", "count": bson.M{"$sum": 1}}}}
  129. ret := []bson.M{}
  130. err = p.Pipe(collection, filter, &ret)
  131. if err != nil {
  132. return nil, errors.DataBaseError
  133. }
  134. stars := 0
  135. reply.List[i].StarStatistic = make([]*pb_v1.VoteResultStatisticStar, len(ret))
  136. for j, w := range ret {
  137. starStr := fmt.Sprintf("%v", w["_id"])
  138. startCountStr := fmt.Sprintf("%v", w["count"])
  139. star, _ := strconv.Atoi(starStr)
  140. starCount, _ := strconv.Atoi(startCountStr)
  141. reply.List[i].StarStatistic[j] = &pb_v1.VoteResultStatisticStar{
  142. Star: int32(star),
  143. Count: int32(starCount),
  144. }
  145. stars += (star * starCount)
  146. }
  147. avg := int32(stars) / int32(count)
  148. if int32(stars)%int32(count) == 0 {
  149. reply.List[i].StarAvg = float64(avg)
  150. } else {
  151. reply.List[i].StarAvg = float64(avg) + 0.5
  152. }
  153. continue
  154. }
  155. choiceM := map[string]string{}
  156. for _, c := range v.ChoiceItems {
  157. choiceM[c.Flag] = c.Text
  158. }
  159. // 选择题统计
  160. filter := []bson.M{
  161. {"$project": bson.M{"answers": bson.M{"$filter": bson.M{"input": "$answers", "as": "answer", "cond": bson.M{"$eq": []interface{}{"$$answer.number", v.Number}}}}}},
  162. {"$unwind": "$answers"},
  163. {"$unwind": "$answers.choice_answer"},
  164. {"$group": bson.M{"_id": "$answers.choice_answer", "count": bson.M{"$sum": 1}}}}
  165. ret := []bson.M{}
  166. err = p.Pipe(collection, filter, &ret)
  167. if err != nil {
  168. logger.Error("mgo",
  169. zap.String("error", err.Error()))
  170. return nil, errors.DataBaseError
  171. }
  172. reply.List[i].ChoiceStatistic = make([]*pb_v1.VoteResultStatisticChoice, len(ret))
  173. for j, w := range ret {
  174. flag := fmt.Sprintf("%v", w["_id"])
  175. countStr := fmt.Sprintf("%v", w["count"])
  176. countInt, _ := strconv.Atoi(countStr)
  177. reply.List[i].ChoiceStatistic[j] = &pb_v1.VoteResultStatisticChoice{
  178. Flag: flag,
  179. Count: int32(countInt),
  180. Text: choiceM[flag],
  181. }
  182. }
  183. }
  184. if count > 0 {
  185. StatisticToCache(reply, req.GardenId, req.Id)
  186. }
  187. return reply, nil
  188. }