package srv_feedback import ( "context" "hkdj-web-server/pub/common/response" "hkdj-web-server/pub/common/srvdef" "hkdj-web-server/pub/models/feedbackmod" "hkdj-web-server/pub/models/usermod" "hkdj-web-server/web/api/proto" "strings" "time" "go.mongodb.org/mongo-driver/bson" "go.mongodb.org/mongo-driver/mongo/options" ) const ( // feedbackDefaultPageSize web端意见反馈列表默认每页数量 feedbackDefaultPageSize int64 = 10 // feedbackMaxPageSize web端意见反馈列表最大每页数量,避免单次请求拉取过多数据 feedbackMaxPageSize int64 = 100 ) var ( // feedbackWebProjection 只读取web意见反馈列表需要展示的字段,减少MongoDB传输开销。 feedbackWebProjection = bson.D{ {Key: feedbackmod.FN.Feedback.Id, Value: 1}, {Key: feedbackmod.FN.Feedback.UserId, Value: 1}, {Key: feedbackmod.FN.Feedback.FeedbackType, Value: 1}, {Key: feedbackmod.FN.Feedback.Content, Value: 1}, {Key: feedbackmod.FN.Feedback.Screenshots, Value: 1}, {Key: feedbackmod.FN.Feedback.Contact, Value: 1}, {Key: feedbackmod.FN.Feedback.Status, Value: 1}, {Key: feedbackmod.FN.Feedback.Remark, Value: 1}, {Key: feedbackmod.FN.Feedback.CreatedAt, Value: 1}, {Key: feedbackmod.FN.Feedback.UpdatedAt, Value: 1}, } // feedbackWebSort web端意见反馈列表按创建时间倒序返回。 feedbackWebSort = bson.D{ {Key: feedbackmod.FN.Feedback.CreatedAt, Value: -1}, {Key: feedbackmod.FN.Feedback.Id, Value: -1}, } ) // ListFeedback 分页查询意见反馈列表 func ListFeedback(ctx context.Context, req *proto.FeedbackListReq) (*proto.FeedbackListPageResp, *srvdef.SrvErr) { if req == nil { return nil, srvdef.New(srvdef.ErrParamError, "", "") } filter, filterErr := buildFeedbackListFilter(req) if filterErr != nil { return nil, filterErr } pageNum, pageSize := normalizeFeedbackPage(req.PageNum, req.PageSize) totalCount, err := feedbackmod.CountFeedback(ctx, filter) if err != nil { return nil, err } resp := &proto.FeedbackListPageResp{ BasePageResp: response.BasePageResp{ TotalCount: totalCount, PageSize: int(pageSize), PageNum: int(pageNum), List: make([]*proto.FeedbackListResp, 0), }, } if totalCount == 0 { return resp, nil } list, err := feedbackmod.FindManyFeedback(ctx, filter, options.Find(). SetProjection(feedbackWebProjection). SetSort(feedbackWebSort). SetSkip((pageNum-1)*pageSize). SetLimit(pageSize)) if err != nil { return nil, err } userNameMap, err := buildFeedbackUserNameMap(ctx, list) if err != nil { return nil, err } resp.List = feedbackListToWebResp(list, userNameMap) return resp, nil } // HandleFeedback 批量更新意见反馈处理状态 func HandleFeedback(ctx context.Context, req *proto.FeedbackHandleReq) (*proto.FeedbackHandleResp, *srvdef.SrvErr) { if req == nil || len(req.Ids) == 0 { return nil, srvdef.New(srvdef.ErrParamError, "ids不能为空") } if req.Status != 0 && req.Status != 1 { return nil, srvdef.New(srvdef.ErrParamError, "status无效") } ids, err := normalizeFeedbackIds(req.Ids) if err != nil { return nil, err } matchedCount, modifiedCount, err := feedbackmod.UpdateManyFeedbackWithResult(ctx, bson.M{ feedbackmod.FN.Feedback.Id: bson.M{"$in": ids}, }, bson.M{ "$set": bson.M{ feedbackmod.FN.Feedback.Status: req.Status, feedbackmod.FN.Feedback.Remark: strings.TrimSpace(req.Remark), feedbackmod.FN.Feedback.UpdatedAt: time.Now(), }, }) if err != nil { return nil, err } if matchedCount == 0 { return nil, srvdef.New(srvdef.ErrNoData, "", "") } return &proto.FeedbackHandleResp{ MatchedCount: matchedCount, ModifiedCount: modifiedCount, }, nil } // buildFeedbackListFilter 构造web端意见反馈列表查询条件 func buildFeedbackListFilter(req *proto.FeedbackListReq) (bson.M, *srvdef.SrvErr) { filter := bson.M{} if req.UserId != nil && *req.UserId > 0 { filter[feedbackmod.FN.Feedback.UserId] = *req.UserId } if req.Type != nil { if !isValidFeedbackType(*req.Type) { return nil, srvdef.New(srvdef.ErrParamError, "type无效") } filter[feedbackmod.FN.Feedback.FeedbackType] = *req.Type } if req.Status != nil { if *req.Status != 0 && *req.Status != 1 { return nil, srvdef.New(srvdef.ErrParamError, "status无效") } filter[feedbackmod.FN.Feedback.Status] = *req.Status } createdAt, err := buildFeedbackTimeFilter(req.CreateStartTime, req.CreateEndTime) if err != nil { return nil, err } if len(createdAt) > 0 { filter[feedbackmod.FN.Feedback.CreatedAt] = createdAt } return filter, nil } // buildFeedbackTimeFilter 按开始时间和结束时间构造createdAt范围查询 func buildFeedbackTimeFilter(startTime, endTime string) (bson.M, *srvdef.SrvErr) { filter := bson.M{} startTime = strings.TrimSpace(startTime) endTime = strings.TrimSpace(endTime) if startTime != "" { startAt, err := parseFeedbackWebTime(startTime) if err != nil { return nil, srvdef.New(srvdef.ErrParamError, "createStartTime格式错误") } filter["$gte"] = startAt } if endTime != "" { endAt, err := parseFeedbackWebTime(endTime) if err != nil { return nil, srvdef.New(srvdef.ErrParamError, "createEndTime格式错误") } filter["$lte"] = endAt } return filter, nil } func parseFeedbackWebTime(value string) (time.Time, error) { return time.ParseInLocation("2006-01-02 15:04:05", value, time.Local) } func normalizeFeedbackPage(pageNum, pageSize int64) (int64, int64) { if pageNum <= 0 { pageNum = 1 } if pageSize <= 0 { pageSize = feedbackDefaultPageSize } if pageSize > feedbackMaxPageSize { pageSize = feedbackMaxPageSize } return pageNum, pageSize } func normalizeFeedbackIds(rawIds []int64) ([]int64, *srvdef.SrvErr) { ids := make([]int64, 0, len(rawIds)) seen := make(map[int64]struct{}, len(rawIds)) for _, id := range rawIds { if id <= 0 { return nil, srvdef.New(srvdef.ErrParamError, "ids包含无效id") } if _, ok := seen[id]; ok { continue } seen[id] = struct{}{} ids = append(ids, id) } if len(ids) == 0 { return nil, srvdef.New(srvdef.ErrParamError, "ids不能为空") } return ids, nil } func isValidFeedbackType(feedbackType int32) bool { return feedbackType >= 1 && feedbackType <= 5 } // buildFeedbackUserNameMap 批量查询反馈用户名称,避免列表转换时逐条查询用户。 func buildFeedbackUserNameMap(ctx context.Context, list feedbackmod.FeedbackList) (map[int64]string, *srvdef.SrvErr) { userIds := make([]int64, 0, len(list)) seen := make(map[int64]struct{}, len(list)) for _, item := range list { if item == nil || item.UserId <= 0 { continue } if _, ok := seen[item.UserId]; ok { continue } seen[item.UserId] = struct{}{} userIds = append(userIds, item.UserId) } userNameMap := make(map[int64]string, len(userIds)) if len(userIds) == 0 { return userNameMap, nil } users, err := usermod.FindManyUser(ctx, bson.M{ usermod.FN.User.Id: bson.M{"$in": userIds}, }, options.Find().SetProjection(bson.M{ usermod.FN.User.Id: 1, usermod.FN.User.NickName: 1, })) if err != nil { return nil, err } for _, user := range users { if user != nil { userNameMap[user.Id] = user.NickName } } return userNameMap, nil } // feedbackListToWebResp 转换意见反馈列表响应 func feedbackListToWebResp(list feedbackmod.FeedbackList, userNameMap map[int64]string) []*proto.FeedbackListResp { resp := make([]*proto.FeedbackListResp, 0, len(list)) for _, item := range list { if item == nil { continue } screenshots := item.Screenshots if screenshots == nil { screenshots = make([]string, 0) } resp = append(resp, &proto.FeedbackListResp{ Id: item.Id, UserId: item.UserId, UserName: userNameMap[item.UserId], Type: item.FeedbackType, Content: item.Content, Screenshots: screenshots, Contact: item.Contact, Status: item.Status, Remark: item.Remark, CreatedAt: item.CreatedAt, UpdatedAt: item.UpdatedAt, }) } return resp }