// Copyright 2019 getensh.com. All rights reserved. // Use of this source code is governed by getensh.com. package vote import ( "context" "encoding/json" "fmt" "git.getensh.com/common/gopkgs/cache" "git.getensh.com/common/gopkgs/logger" "go.uber.org/zap" "google.golang.org/grpc/status" "gopkg.in/mgo.v2/bson" "property-garden/errors" dbmodel "property-garden/model" "property-garden/parser" pb_v1 "property-garden/pb/v1" "property-garden/utils" "strconv" ) func checkVoteResultStatisticParam(req *pb_v1.VoteResultStatisticRequest) error { switch { case req.GardenId == 0: return status.Error(10003, "小区不能为空") case req.Id == 0: return status.Error(10003, "投票id不能为空") } return nil } func StatisticToCache(reply *pb_v1.VoteResultStatisticReply, gardenId int64, id int64) { key := fmt.Sprintf("vote_statisic_%d,%d", gardenId, id) bytes, _ := json.Marshal(reply) cache.Redis().SetEx(key, 30, string(bytes)) } func StatisticFromCache(gardenId int64, id int64) pb_v1.VoteResultStatisticReply { key := fmt.Sprintf("vote_statisic_%d,%d", gardenId, id) str, _ := cache.Redis().Get(key) ret := pb_v1.VoteResultStatisticReply{} if str == "" { return ret } json.Unmarshal([]byte(str), &ret) return ret } func StatisticDelCache(gardenId int64, id int64) { key := fmt.Sprintf("vote_statisic_%d,%d", gardenId, id) cache.Redis().Del(key) } func VoteResultStatistic(ctx context.Context, req *pb_v1.VoteResultStatisticRequest) (reply *pb_v1.VoteResultStatisticReply, err error) { reply = &pb_v1.VoteResultStatisticReply{} // 捕获各个task中的异常并返回给调用者 defer func() { if r := recover(); r != nil { err = fmt.Errorf("%+v", r) e := &status.Status{} if er := json.Unmarshal([]byte(err.Error()), e); er != nil { logger.Error("err", zap.String("system_err", err.Error()), zap.Stack("stacktrace")) } } }() // 参数检查 err = checkVoteResultStatisticParam(req) if err != nil { return nil, err } dbname := utils.GetGardenDbName(req.GardenId) vote, err := getVoteOrigin(dbname, req.Id) if err != nil { return nil, err } voteM := map[int64]*pb_v1.VoteTopic{} for _, v := range vote.Topics { voteM[v.Number] = v } p := dbmodel.VoteAnswer{} exist, err := dbmodel.CheckDbExist(parser.Session, dbmodel.VoteMgoDb) if err != nil { return nil, errors.DataBaseError } collection := parser.Session.DB(dbmodel.VoteMgoDb).C(dbmodel.VoteCollection(req.GardenId, req.Id)) count := int64(0) if exist { count, err = p.Count(collection, nil) if err != nil { return nil, errors.DataBaseError } } if count > 0 { tmp := StatisticFromCache(req.GardenId, req.Id) if len(tmp.List) > 0 { *reply = tmp return reply, nil } } reply.List = make([]*pb_v1.VoteResultStatisticItem, len(vote.Topics)) for i, v := range vote.Topics { reply.List[i] = &pb_v1.VoteResultStatisticItem{ TopicType: v.TopicType, TopicName: v.TopicName, // 总星数,当为评分题时有效 Star: v.Star, // 是否必选 Must: v.Must, // 题目编号 Number: v.Number, // 选择题的选项内容 ChoiceItems: v.ChoiceItems, // 选择题的选项回答统计,当题目类型为选择题时有效 ChoiceStatistic: []*pb_v1.VoteResultStatisticChoice{}, // 评分题的分数回答统计,当题目类型为评分题时有效 StarStatistic: []*pb_v1.VoteResultStatisticStar{}, // 平均分, 当题目类型为评分题时有效 StarAvg: 0, } if count == 0 { continue } // 填空题不统计 if v.TopicType == TopicTypeCompletion { continue } // 评分题统计 if v.TopicType == TopicTypeStar { filter := []bson.M{ {"$project": bson.M{"answers": bson.M{"$filter": bson.M{"input": "$answers", "as": "answer", "cond": bson.M{"$eq": []interface{}{"$$answer.number", v.Number}}}}}}, {"$unwind": "$answers"}, {"$group": bson.M{"_id": "$answers.star_answer", "count": bson.M{"$sum": 1}}}} ret := []bson.M{} err = p.Pipe(collection, filter, &ret) if err != nil { return nil, errors.DataBaseError } stars := 0 reply.List[i].StarStatistic = make([]*pb_v1.VoteResultStatisticStar, len(ret)) for j, w := range ret { starStr := fmt.Sprintf("%v", w["_id"]) startCountStr := fmt.Sprintf("%v", w["count"]) star, _ := strconv.Atoi(starStr) starCount, _ := strconv.Atoi(startCountStr) reply.List[i].StarStatistic[j] = &pb_v1.VoteResultStatisticStar{ Star: int32(star), Count: int32(starCount), } stars += (star * starCount) } avg := int32(stars) / int32(count) if int32(stars)%int32(count) == 0 { reply.List[i].StarAvg = float64(avg) } else { reply.List[i].StarAvg = float64(avg) + 0.5 } continue } choiceM := map[string]string{} for _, c := range v.ChoiceItems { choiceM[c.Flag] = c.Text } // 选择题统计 filter := []bson.M{ {"$project": bson.M{"answers": bson.M{"$filter": bson.M{"input": "$answers", "as": "answer", "cond": bson.M{"$eq": []interface{}{"$$answer.number", v.Number}}}}}}, {"$unwind": "$answers"}, {"$unwind": "$answers.choice_answer"}, {"$group": bson.M{"_id": "$answers.choice_answer", "count": bson.M{"$sum": 1}}}} ret := []bson.M{} err = p.Pipe(collection, filter, &ret) if err != nil { logger.Error("mgo", zap.String("error", err.Error())) return nil, errors.DataBaseError } reply.List[i].ChoiceStatistic = make([]*pb_v1.VoteResultStatisticChoice, len(ret)) for j, w := range ret { flag := fmt.Sprintf("%v", w["_id"]) countStr := fmt.Sprintf("%v", w["count"]) countInt, _ := strconv.Atoi(countStr) reply.List[i].ChoiceStatistic[j] = &pb_v1.VoteResultStatisticChoice{ Flag: flag, Count: int32(countInt), Text: choiceM[flag], } } } if count > 0 { StatisticToCache(reply, req.GardenId, req.Id) } return reply, nil }