Files
rootandClaude Opus 4.7 79ac89b458 Initial commit
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-11-07 10:47:18 +08:00

272 lines
7.8 KiB
Go

package srv_feedback
import (
"context"
"strings"
"time"
"videoweb/pub/common/response"
"videoweb/pub/common/srvdef"
"videoweb/pub/models/feedbackmod"
"videoweb/pub/models/usermod"
"videoweb/web/api/proto"
"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
}