package srv_contactus import ( "context" "regexp" "strings" "time" "videoweb/pub/common/response" "videoweb/pub/common/srvdef" "videoweb/pub/models/contactusmod" "videoweb/pub/models/usermod" "videoweb/web/api/proto" "go.mongodb.org/mongo-driver/bson" "go.mongodb.org/mongo-driver/mongo/options" ) const ( // contactUsDefaultPageSize web端联系我们列表默认每页数量 contactUsDefaultPageSize int64 = 10 // contactUsMaxPageSize web端联系我们列表最大每页数量,避免单次请求拉取过多数据 contactUsMaxPageSize int64 = 100 ) var ( // contactUsWebProjection 只读取web联系我们列表需要展示的字段,减少MongoDB传输开销。 contactUsWebProjection = bson.D{ {Key: contactusmod.FN.ContactUs.Id, Value: 1}, {Key: contactusmod.FN.ContactUs.UserId, Value: 1}, {Key: contactusmod.FN.ContactUs.Email, Value: 1}, {Key: contactusmod.FN.ContactUs.QuestionType, Value: 1}, {Key: contactusmod.FN.ContactUs.Title, Value: 1}, {Key: contactusmod.FN.ContactUs.Content, Value: 1}, {Key: contactusmod.FN.ContactUs.Attachments, Value: 1}, {Key: contactusmod.FN.ContactUs.Status, Value: 1}, {Key: contactusmod.FN.ContactUs.Remark, Value: 1}, {Key: contactusmod.FN.ContactUs.CreatedAt, Value: 1}, {Key: contactusmod.FN.ContactUs.UpdatedAt, Value: 1}, } // contactUsWebSort web端联系我们列表按创建时间倒序返回。 contactUsWebSort = bson.D{ {Key: contactusmod.FN.ContactUs.CreatedAt, Value: -1}, {Key: contactusmod.FN.ContactUs.Id, Value: -1}, } ) // ListContactUs 分页查询联系我们列表 func ListContactUs(ctx context.Context, req *proto.ContactUsListReq) (*proto.ContactUsListPageResp, *srvdef.SrvErr) { if req == nil { return nil, srvdef.New(srvdef.ErrParamError, "", "") } filter, filterErr := buildContactUsListFilter(req) if filterErr != nil { return nil, filterErr } pageNum, pageSize := normalizeContactUsPage(req.PageNum, req.PageSize) totalCount, err := contactusmod.CountContactUs(ctx, filter) if err != nil { return nil, err } resp := &proto.ContactUsListPageResp{ BasePageResp: response.BasePageResp{ TotalCount: totalCount, PageSize: int(pageSize), PageNum: int(pageNum), List: make([]*proto.ContactUsListResp, 0), }, } if totalCount == 0 { return resp, nil } list, err := contactusmod.FindManyContactUs(ctx, filter, options.Find(). SetProjection(contactUsWebProjection). SetSort(contactUsWebSort). SetSkip((pageNum-1)*pageSize). SetLimit(pageSize)) if err != nil { return nil, err } userNameMap, err := buildContactUsUserNameMap(ctx, list) if err != nil { return nil, err } resp.List = contactUsListToWebResp(list, userNameMap) return resp, nil } // HandleContactUs 批量更新联系我们处理状态 func HandleContactUs(ctx context.Context, req *proto.ContactUsHandleReq) (*proto.ContactUsHandleResp, *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 := normalizeContactUsIds(req.Ids) if err != nil { return nil, err } matchedCount, modifiedCount, err := contactusmod.UpdateManyContactUsWithResult(ctx, bson.M{ contactusmod.FN.ContactUs.Id: bson.M{"$in": ids}, }, bson.M{ "$set": bson.M{ contactusmod.FN.ContactUs.Status: req.Status, contactusmod.FN.ContactUs.Remark: strings.TrimSpace(req.Remark), contactusmod.FN.ContactUs.UpdatedAt: time.Now(), }, }) if err != nil { return nil, err } if matchedCount == 0 { return nil, srvdef.New(srvdef.ErrNoData, "", "") } return &proto.ContactUsHandleResp{ MatchedCount: matchedCount, ModifiedCount: modifiedCount, }, nil } // buildContactUsListFilter 构造web端联系我们列表查询条件 func buildContactUsListFilter(req *proto.ContactUsListReq) (bson.M, *srvdef.SrvErr) { filter := bson.M{} if req.UserId != nil && *req.UserId > 0 { filter[contactusmod.FN.ContactUs.UserId] = *req.UserId } if email := strings.TrimSpace(req.Email); email != "" { filter[contactusmod.FN.ContactUs.Email] = bson.M{"$regex": regexp.QuoteMeta(email)} } if req.Type != nil { if !isValidContactUsType(*req.Type) { return nil, srvdef.New(srvdef.ErrParamError, "type无效") } filter[contactusmod.FN.ContactUs.QuestionType] = *req.Type } if req.Status != nil { if *req.Status != 0 && *req.Status != 1 { return nil, srvdef.New(srvdef.ErrParamError, "status无效") } filter[contactusmod.FN.ContactUs.Status] = *req.Status } createdAt, err := buildContactUsTimeFilter(req.CreateStartTime, req.CreateEndTime) if err != nil { return nil, err } if len(createdAt) > 0 { filter[contactusmod.FN.ContactUs.CreatedAt] = createdAt } return filter, nil } // buildContactUsTimeFilter 按开始时间和结束时间构造createdAt范围查询 func buildContactUsTimeFilter(startTime, endTime string) (bson.M, *srvdef.SrvErr) { filter := bson.M{} startTime = strings.TrimSpace(startTime) endTime = strings.TrimSpace(endTime) if startTime != "" { startAt, err := parseContactUsWebTime(startTime) if err != nil { return nil, srvdef.New(srvdef.ErrParamError, "createStartTime格式错误") } filter["$gte"] = startAt } if endTime != "" { endAt, err := parseContactUsWebTime(endTime) if err != nil { return nil, srvdef.New(srvdef.ErrParamError, "createEndTime格式错误") } filter["$lte"] = endAt } return filter, nil } func parseContactUsWebTime(value string) (time.Time, error) { return time.ParseInLocation("2006-01-02 15:04:05", value, time.Local) } func normalizeContactUsPage(pageNum, pageSize int64) (int64, int64) { if pageNum <= 0 { pageNum = 1 } if pageSize <= 0 { pageSize = contactUsDefaultPageSize } if pageSize > contactUsMaxPageSize { pageSize = contactUsMaxPageSize } return pageNum, pageSize } func normalizeContactUsIds(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 isValidContactUsType(questionType int32) bool { return questionType >= 1 && questionType <= 5 } // buildContactUsUserNameMap 批量查询用户名称,避免列表转换时逐条查询用户。 func buildContactUsUserNameMap(ctx context.Context, list contactusmod.ContactUsList) (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 } // contactUsListToWebResp 转换联系我们列表响应 func contactUsListToWebResp(list contactusmod.ContactUsList, userNameMap map[int64]string) []*proto.ContactUsListResp { resp := make([]*proto.ContactUsListResp, 0, len(list)) for _, item := range list { if item == nil { continue } resp = append(resp, &proto.ContactUsListResp{ Id: item.Id, UserId: item.UserId, UserName: userNameMap[item.UserId], Email: item.Email, Type: item.QuestionType, Title: item.Title, Content: item.Content, Attachments: item.Attachments, Status: item.Status, Remark: item.Remark, CreatedAt: item.CreatedAt, UpdatedAt: item.UpdatedAt, }) } return resp }