초기커밋

This commit is contained in:
2025-05-01 07:20:41 +09:00
commit 98bb2e3c5c
2747 changed files with 646947 additions and 0 deletions

View File

@@ -0,0 +1,686 @@
using Microsoft.Extensions.Options;
using MongoDB.Driver;
using UGQDatabase.Models;
using UGQDataAccess.Settings;
using UGQDataAccess.Repository.Models;
using Amazon.SecurityToken.Model;
using Microsoft.AspNetCore.Mvc.RazorPages;
using UGQDataAccess.Repository.Query;
using System.Linq.Expressions;
using ServerCommon.UGQ;
using ServerCommon.UGQ.Models;
using UGQDataAccess;
using MongoDB.Bson;
using MongoDB.Bson.Serialization;
using static System.Net.Mime.MediaTypeNames;
namespace UGQDataAccess.Repository;
public class QuestContentRepository : BaseRepository<QuestContentEntity>
{
internal enum SpotlightSort
{
MostLiked,
MostBookmarked,
}
internal class SportlightQuery
{
internal SpotlightSort Sort { get; set; }
internal DateRange DateRange { get; set; }
internal List<QuestBoardItemResult> Items { get; set; } = new();
internal SportlightQuery(SpotlightSort sort, DateRange dateRange)
{
Sort = sort;
DateRange = dateRange;
}
}
private const string CollectionName = "QuestContent";
public QuestContentRepository(IMongoClient mongoClient, IOptions<UGQDatabaseSettings> settings) :
base(mongoClient, settings.Value.DatabaseName, CollectionName)
{
}
public async Task<QuestContentEntity> insert(QuestContentEntity entity)
{
await Collection.InsertOneAsync(entity);
return entity;
}
public async Task<List<QuestContentEntity>> getAll(string userGuid)
{
var builder = Builders<QuestContentEntity>.Filter;
var filter = builder.Eq(x => x.IsDeleted, false);
filter &= builder.Eq(x => x.UserGuid, userGuid);
var result = await Collection.Find(filter).ToListAsync();
return result;
}
public async Task<long> getAllCount()
{
var builder = Builders<QuestContentEntity>.Filter;
var filter = builder.Eq(x => x.IsDeleted, false);
var result = await Collection.Find(filter).CountDocumentsAsync();
return result;
}
public async Task<List<QuestContentEntity>> getUserQuests(string userGuid, IEnumerable<QuestContentState> states)
{
var builder = Builders<QuestContentEntity>.Filter;
var filter = builder.Eq(x => x.IsDeleted, false);
filter = builder.Eq(x => x.UserGuid, userGuid) &
builder.In(x => x.State, states);
return await Collection.Find(filter).ToListAsync();
}
public async Task<SummaryQueryResult> getSummaries(int pageNumber, int pageSize, string userGuid, QuestContentState state, string? searchText)
{
var accountCollection = _mongoDatabase.GetCollection<AccountEntity>("Account");
var likeCollection = _mongoDatabase.GetCollection<LikeEntity>("Like");
var bookmarkCollection = _mongoDatabase.GetCollection<BookmarkEntity>("Bookmark");
var npcNameCollection = _mongoDatabase.GetCollection<NpcNameEntity>("NpcName");
pageNumber = pageNumber < 1 ? 1 : pageNumber;
var filter = await QuestContentQuery.summariesFilter(userGuid,
UgqSearchType.Title, searchText, state,
accountCollection, npcNameCollection);
var countFacet = AggregateFacet.Create("count",
new EmptyPipelineDefinition<SummaryItemResult>()
.Count()
);
var pagingFacet = AggregateFacet.Create("paging",
new EmptyPipelineDefinition<SummaryItemResult>()
.Skip((pageNumber - 1) * pageSize)
.Limit(pageSize)
);
var pipeline = QuestContentQuery.summariesPipeline<SummaryItemResult>(
likeCollection, bookmarkCollection,
filter,
QuestContentQuery.SummaryItemResultProjection)
.Facet(countFacet, pagingFacet);
var aggregation = await (await Collection.AggregateAsync(pipeline)).ToListAsync();
var count = aggregation.First()
.Facets.First(x => x.Name == "count")
.Output<AggregateCountResult>()
?.FirstOrDefault()
?.Count;
var paging = aggregation.First()
.Facets.First(x => x.Name == "paging")
.Output<SummaryItemResult>();
int totalPages = 0;
if (count != null)
totalPages = (int)Math.Ceiling((double)count / pageSize);
var result = await (await Collection.AggregateAsync(pipeline)).ToListAsync();
return new SummaryQueryResult
{
PageNumber = pageNumber,
PageSize = pageSize,
TotalPages = totalPages,
Items = paging.ToList(),
};
}
public async Task<AllSummaryQueryResult> getAllSummaries(int pageNumber, int pageSize, QuestContentState state, string? searchText)
{
var accountCollection = _mongoDatabase.GetCollection<AccountEntity>("Account");
var likeCollection = _mongoDatabase.GetCollection<LikeEntity>("Like");
var bookmarkCollection = _mongoDatabase.GetCollection<BookmarkEntity>("Bookmark");
var npcNameCollection = _mongoDatabase.GetCollection<NpcNameEntity>("NpcName");
pageNumber = pageNumber < 1 ? 1 : pageNumber;
var filter = await QuestContentQuery.summariesFilter(null,
UgqSearchType.Title, searchText, state,
accountCollection, npcNameCollection);
var countFacet = AggregateFacet.Create("count",
new EmptyPipelineDefinition<SummaryItemResult>()
.Count()
);
var pagingFacet = AggregateFacet.Create("paging",
new EmptyPipelineDefinition<SummaryItemResult>()
.Skip((pageNumber - 1) * pageSize)
.Limit(pageSize)
);
var pipeline = QuestContentQuery.summariesPipeline<SummaryItemResult>(
likeCollection, bookmarkCollection,
filter,
QuestContentQuery.SummaryItemResultProjection)
.Facet(countFacet, pagingFacet);
var aggregation = await (await Collection.AggregateAsync(pipeline)).ToListAsync();
var count = aggregation.First()
.Facets.First(x => x.Name == "count")
.Output<AggregateCountResult>()
?.FirstOrDefault()
?.Count;
var paging = aggregation.First()
.Facets.First(x => x.Name == "paging")
.Output<SummaryItemResult>();
int totalPages = 0;
if (count != null)
totalPages = (int)Math.Ceiling((double)count / pageSize);
return new AllSummaryQueryResult
{
PageNumber = pageNumber,
PageSize = pageSize,
TotalPages = totalPages,
Items = paging.ToList(),
};
}
public async Task<QuestContentEntity?> get(string id)
{
var builder = Builders<QuestContentEntity>.Filter;
var filter = builder.Eq(x => x.IsDeleted, false);
filter &= builder.Eq(x => x.Id, id);
return await Collection.Find(filter).FirstOrDefaultAsync();
}
public async Task<QuestContentEntity?> getByQuestId(long questId)
{
var builder = Builders<QuestContentEntity>.Filter;
var filter = builder.Eq(x => x.IsDeleted, false);
filter &= builder.Eq(x => x.QuestId, questId);
return await Collection.Find(filter).FirstOrDefaultAsync();
}
public async Task<QuestContentEntity?> getByQuestIdRevision(long questId, long revision)
{
var builder = Builders<QuestContentEntity>.Filter;
var filter = builder.Eq(x => x.IsDeleted, false);
filter &= builder.Eq(x => x.QuestId, questId) & builder.Eq(x => x.Revision, revision);
return await Collection.Find(filter).FirstOrDefaultAsync();
}
public async Task<QuestContentEntity?> updateContent(string id, UGQSaveQuestModel model, int? contentVersion)
{
var builder = Builders<QuestContentEntity>.Filter;
var filter = builder.Eq(x => x.IsDeleted, false);
filter &= builder.Eq(x => x.Id, id) &
builder.Eq(x => x.ContentVersion, contentVersion);
var State = QuestContentState.Editable;
foreach (var task in model.Tasks)
{
if (task.ActionId == 0)
{
State = QuestContentState.Uncomplate;
break;
}
}
var update = Builders<QuestContentEntity>.Update
.Set(x => x.BeaconId, model.BeaconId)
.Set(x => x.UgcBeaconGuid, model.UgcBeaconGuid)
.Set(x => x.UgcBeaconNickname, model.UgcBeaconNickname)
.Set(x => x.Title, model.Title)
.Set(x => x.Langs, model.Languages)
.Set(x => x.Savelanguage, model.Savelanguage)
.Set(x => x.Description, model.Description)
.Set(x => x.Cost, model.Cost)
.Set(x => x.Tasks, model.Tasks)
.Set(x => x.State, State)
.Inc(x => x.ContentVersion, 1)
.Set(x => x.UpdatedAt, DateTime.UtcNow);
var options = new FindOneAndUpdateOptions<QuestContentEntity>
{
ReturnDocument = ReturnDocument.After,
};
return await Collection.FindOneAndUpdateAsync(filter, update, options);
}
public async Task<QuestContentEntity?> updateTasks(string id, List<TaskEntity> tasks, int? contentVersion)
{
var builder = Builders<QuestContentEntity>.Filter;
var filter = builder.Eq(x => x.IsDeleted, false);
filter &= builder.Eq(x => x.Id, id) &
builder.Eq(x => x.ContentVersion, contentVersion);
var update = Builders<QuestContentEntity>.Update
.Set(x => x.Tasks, tasks)
.Inc(x => x.ContentVersion, 1)
.Set(x => x.UpdatedAt, DateTime.UtcNow);
var options = new FindOneAndUpdateOptions<QuestContentEntity>
{
ReturnDocument = ReturnDocument.After,
};
return await Collection.FindOneAndUpdateAsync(filter, update, options);
}
public async Task<QuestContentEntity?> updateImages(string id, string? titleImage, string? bannerImage)
{
var builder = Builders<QuestContentEntity>.Filter;
var filter = builder.Eq(x => x.IsDeleted, false);
filter &= builder.Eq(x => x.Id, id);
var updateBuilder = Builders<QuestContentEntity>.Update;
var update = updateBuilder.Set(x => x.UpdatedAt, DateTime.UtcNow);
if (titleImage != null)
update = updateBuilder.Combine(update, updateBuilder.Set(x => x.TitleImagePath, titleImage));
if (bannerImage != null)
update = updateBuilder.Combine(update, updateBuilder.Set(x => x.BannerImagePath, bannerImage));
var options = new FindOneAndUpdateOptions<QuestContentEntity>
{
ReturnDocument = ReturnDocument.After,
};
return await Collection.FindOneAndUpdateAsync(filter, update, options);
}
public async Task<QuestContentEntity?> updateState(string id, long questId, long revision,
IEnumerable<QuestContentState> before, QuestContentState after, UgqGradeType? gradeType = null)
{
var builder = Builders<QuestContentEntity>.Filter;
var filter = builder.Eq(x => x.IsDeleted, false);
filter &= builder.Eq(x => x.Id, id) &
builder.In(x => x.State, before);
// Test->Standby <20><> <20><> toNextRevision<6F><6E> true<75><65> <20>ؼ<EFBFBD> Live <20><> <20><> Revision + 1 ó<><C3B3> <20>Ѵ<EFBFBD>.
bool toNextRevision = before.Any(x => x == QuestContentState.Test) && (after == QuestContentState.Standby);
var updateBuilder = Builders<QuestContentEntity>.Update;
var update = updateBuilder
.Set(x => x.UpdatedAt, DateTime.UtcNow)
.Set(x => x.QuestId, questId)
.Set(x => x.Revision, revision)
.Set(x => x.State, after)
.Set(x => x.ToNextRevision, toNextRevision);
if (gradeType != null)
update = updateBuilder.Combine(update, updateBuilder.Set(x => x.GradeType, gradeType));
var options = new FindOneAndUpdateOptions<QuestContentEntity>
{
ReturnDocument = ReturnDocument.After,
};
return await Collection.FindOneAndUpdateAsync(filter, update, options);
}
public async Task<(ServerErrorCode, int)> incUploadCounter(string questContentId)
{
var builder = Builders<QuestContentEntity>.Filter;
var filter = builder.Eq(x => x.IsDeleted, false);
filter &= builder.Eq(x => x.Id, questContentId);
var update = Builders<QuestContentEntity>.Update
.Inc(x => x.UploadCounter, 1)
.Set(x => x.UpdatedAt, DateTime.UtcNow);
var options = new FindOneAndUpdateOptions<QuestContentEntity>()
{
ReturnDocument = ReturnDocument.After
};
var updated = await Collection.FindOneAndUpdateAsync(filter, update, options);
return (ServerErrorCode.Success, updated.UploadCounter);
}
public async Task<ServerErrorCode> deleteQuest(string userGuid, string questContentId)
{
var builder = Builders<QuestContentEntity>.Filter;
var filter = builder.Eq(x => x.IsDeleted, false);
filter &= builder.Eq(x => x.UserGuid, userGuid) & builder.Eq(x => x.Id, questContentId);
var update = Builders<QuestContentEntity>.Update
.Set(x => x.UpdatedAt, DateTime.UtcNow)
.Set(x => x.DeletedAt, DateTime.UtcNow)
.Set(x => x.IsDeleted, true);
var result = await Collection.UpdateOneAsync(filter, update);
if(result.ModifiedCount == 0)
return ServerErrorCode.UgqNullEntity;
return ServerErrorCode.Success;
}
public async Task<QuestBoardQueryResult> getQuestBoard(string? userGuid,
UgqQuestBoardRequest request, int pageSize)
{
var accountCollection = _mongoDatabase.GetCollection<AccountEntity>("Account");
var likeCollection = _mongoDatabase.GetCollection<LikeEntity>("Like");
var bookmarkCollection = _mongoDatabase.GetCollection<BookmarkEntity>("Bookmark");
var npcNameCollection = _mongoDatabase.GetCollection<NpcNameEntity>("NpcName");
request.PageNumber = request.PageNumber < 1 ? 1 : request.PageNumber;
var filter = await QuestContentQuery.questBoardFilter(userGuid, request.GradeType,
request.SearchType, request.SearchText, accountCollection, npcNameCollection);
var sort = QuestContentQuery.questBoardSort<QuestBoardItemResult>(request.SortType);
var countFacet = AggregateFacet.Create("count",
new EmptyPipelineDefinition<QuestBoardItemResult>()
.Count()
);
var pagingFacet = AggregateFacet.Create("paging",
new EmptyPipelineDefinition<QuestBoardItemResult>()
.Sort(sort)
.Skip((request.PageNumber - 1) * pageSize)
.Limit(pageSize)
);
var pipeline = QuestContentQuery.questBoardPipeline<QuestBoardItemResult>(
likeCollection, bookmarkCollection, filter,
QuestContentQuery.QuestBoardItemResultProjection)
.Facet(countFacet, pagingFacet);
var aggregation = await (await Collection.AggregateAsync(pipeline)).ToListAsync();
var count = aggregation.First()
.Facets.First(x => x.Name == "count")
.Output<AggregateCountResult>()
?.FirstOrDefault()
?.Count;
var paging = aggregation.First()
.Facets.First(x => x.Name == "paging")
.Output<QuestBoardItemResult>();
int totalPages = 0;
if(count != null)
totalPages = (int)Math.Ceiling((double)count / pageSize);
return new QuestBoardQueryResult
{
PageNumber = request.PageNumber,
PageSize = pageSize,
TotalPages = totalPages,
Items = paging.ToList(),
};
}
public async Task<QuestBoardDetailItemResult?> getQuestBoardDetail(string userGuid, long questId, long revision)
{
var likeCollection = _mongoDatabase.GetCollection<LikeEntity>("Like");
var bookmarkCollection = _mongoDatabase.GetCollection<BookmarkEntity>("Bookmark");
var filterBuilder = Builders<QuestContentEntity>.Filter;
var filter = filterBuilder.Eq(x => x.IsDeleted, false);
filter &= filterBuilder.Eq(x => x.QuestId, questId);
filter &= filterBuilder.Eq(x => x.Revision, revision);
filter &= filterBuilder.Eq(x => x.State, QuestContentState.Live);
Expression<Func<QuestContentQuery.QuestBoardQueryJoin, QuestBoardDetailItemResult>> questBoardDetailItemProjection =
x => new QuestBoardDetailItemResult
{
QuestId = x.QuestId,
Revision = x.Revision,
Author = x.Author,
Title = x.Title,
Languages = x.Langs,
Description = x.Description,
Cost = x.Cost,
GradeType = x.GradeType,
TitleImagePath = x.TitleImagePath,
BannerImagePath = x.BannerImagePath,
UpdatedAt = x.UpdatedAt,
LikeCount = x.LikeCount,
BookmarkCount = x.BookmarkCount,
QuestAcceptedCount = x.QuestAcceptedCount,
QuestCompletedCount = x.QuestCompletedCount,
BeaconId = x.BeaconId,
UgcBeaconGuid = x.UgcBeaconGuid,
UgcBeaconNickname = x.UgcBeaconNickname,
Liked = x.Likes.Any(x => x.UserGuid == userGuid),
Bookmarked = x.Bookmarks.Any(x => x.UserGuid == userGuid),
};
var pipeline = QuestContentQuery.questBoardDetailPipeline<QuestBoardDetailItemResult>(
likeCollection, bookmarkCollection,
filter,
questBoardDetailItemProjection);
return await (await Collection.AggregateAsync(pipeline)).FirstOrDefaultAsync();
}
public async Task<QuestBoardQueryResult> getBookmarkQuests(string userGuid, UgqQuestBoardRequest request, int pageSize)
{
var likeCollection = _mongoDatabase.GetCollection<LikeEntity>("Like");
var bookmarkCollection = _mongoDatabase.GetCollection<BookmarkEntity>("Bookmark");
var npcNameCollection = _mongoDatabase.GetCollection<NpcNameEntity>("NpcName");
request.PageNumber = request.PageNumber < 1 ? 1 : request.PageNumber;
var filter = await QuestContentQuery.questBoardBookmarkFilter(userGuid,
request.GradeType, request.SearchType, request.SearchText,
npcNameCollection, bookmarkCollection);
var sort = QuestContentQuery.questBoardSort<QuestBoardItemResult>(request.SortType);
var countFacet = AggregateFacet.Create("count",
new EmptyPipelineDefinition<QuestBoardItemResult>()
.Count()
);
var pagingFacet = AggregateFacet.Create("paging",
new EmptyPipelineDefinition<QuestBoardItemResult>()
.Sort(sort)
.Skip((request.PageNumber - 1) * pageSize)
.Limit(pageSize)
);
var pipeline = QuestContentQuery.questBoardPipeline<QuestBoardItemResult>(
likeCollection, bookmarkCollection, filter,
QuestContentQuery.QuestBoardItemResultProjection)
.Facet(countFacet, pagingFacet);
var aggregation = await (await Collection.AggregateAsync(pipeline)).ToListAsync();
var count = aggregation.First()
.Facets.First(x => x.Name == "count")
.Output<AggregateCountResult>()
?.FirstOrDefault()
?.Count;
var paging = aggregation.First()
.Facets.First(x => x.Name == "paging")
.Output<QuestBoardItemResult>();
int totalPages = 0;
if (count != null)
totalPages = (int)Math.Ceiling((double)count / pageSize);
return new QuestBoardQueryResult
{
PageNumber = request.PageNumber,
PageSize = pageSize,
TotalPages = totalPages,
Items = paging.ToList(),
};
}
public async Task<QuestBoardSportlightQueryResult> getQuestBoardSpotlight()
{
var accountCollection = _mongoDatabase.GetCollection<AccountEntity>("Account");
var likeCollection = _mongoDatabase.GetCollection<LikeEntity>("Like");
var bookmarkCollection = _mongoDatabase.GetCollection<BookmarkEntity>("Bookmark");
SportlightQuery[] queries = [
new SportlightQuery(SpotlightSort.MostLiked, DataRangeHelper.Yesterday(DateTime.Now)),
new SportlightQuery(SpotlightSort.MostBookmarked, DataRangeHelper.Yesterday(DateTime.Now)),
new SportlightQuery(SpotlightSort.MostLiked, DataRangeHelper.LastWeek(DateTime.Now)),
new SportlightQuery(SpotlightSort.MostBookmarked, DataRangeHelper.LastWeek(DateTime.Now)),
new SportlightQuery(SpotlightSort.MostLiked, DataRangeHelper.LastMonth(DateTime.Now)),
new SportlightQuery(SpotlightSort.MostBookmarked, DataRangeHelper.LastMonth(DateTime.Now)),
];
var filterBuilder = Builders<QuestContentEntity>.Filter;
var filter = filterBuilder.Eq(x => x.IsDeleted, false);
foreach (var (value, i) in queries.Select((value, i) => (value, i)))
{
int limit = 1;
switch(value.Sort)
{
case SpotlightSort.MostLiked:
{
var sort = Builders<QuestBoardItemResult>.Sort.Descending("LikeCount").Descending("CreatedAt");
var pipeline = QuestContentQuery.questBoardPipeline<QuestBoardItemResult>(
likeCollection, bookmarkCollection, filter,
QuestContentQuery.QuestBoardItemResultProjection)
.Sort(sort)
.Limit(limit);
value.Items = await (await Collection.AggregateAsync(pipeline)).ToListAsync();
}
break;
case SpotlightSort.MostBookmarked:
{
var sort = Builders<QuestBoardItemResult>.Sort.Descending("BookmarkCount").Descending("CreatedAt");
var pipeline = QuestContentQuery.questBoardPipeline<QuestBoardItemResult>(
likeCollection, bookmarkCollection, filter,
QuestContentQuery.QuestBoardItemResultProjection)
.Sort(sort)
.Limit(limit);
value.Items = await (await Collection.AggregateAsync(pipeline)).ToListAsync();
}
break;
}
}
/*
// <20>ߺ<EFBFBD> <20><><EFBFBD><EFBFBD>
foreach (var (value, i) in queries.Select((value, i) => (value, i)))
{
if (value.Items.Count == 0)
continue;
long questId = value.Items[0].QuestId;
long revision = value.Items[0].Revision;
for (int s = i + 1; s < queries.Length; s++)
{
queries[s].Items.RemoveAll(x => x.QuestId == questId && x.Revision == revision);
}
}
*/
return new QuestBoardSportlightQueryResult
{
MostLiked = new()
{
Today = queries[0].Items.FirstOrDefault(),
ThisWeek = queries[2].Items.FirstOrDefault(),
ThisMonth = queries[4].Items.FirstOrDefault(),
},
MostBookmarked = new()
{
Today = queries[1].Items.FirstOrDefault(),
ThisWeek = queries[3].Items.FirstOrDefault(),
ThisMonth = queries[5].Items.FirstOrDefault(),
},
};
}
public async Task<QuestProfitStatsQueryResult> getQuestProfitStats(string userGuid, int pageNumber, int pageSize)
{
var accountCollection = _mongoDatabase.GetCollection<AccountEntity>("Account");
var likeCollection = _mongoDatabase.GetCollection<LikeEntity>("Like");
var bookmarkCollection = _mongoDatabase.GetCollection<BookmarkEntity>("Bookmark");
var npcNameCollection = _mongoDatabase.GetCollection<NpcNameEntity>("NpcName");
var questAcceptedCollection = _mongoDatabase.GetCollection<QuestAcceptedEntity>("QuestAccepted");
var questCompletedCollection = _mongoDatabase.GetCollection<QuestCompletedEntity>("QuestCompleted");
var questAbortedCollection = _mongoDatabase.GetCollection<QuestAbortedEntity>("QuestAborted");
var creatorPointHistoryCollection = _mongoDatabase.GetCollection<CreatorPointHistoryEntity>("CreatorPointHistory");
pageNumber = pageNumber < 1 ? 1 : pageNumber;
var filterBuilder = Builders<QuestContentEntity>.Filter;
var filter = filterBuilder.Eq(x => x.IsDeleted, false);
filter &= filterBuilder.Eq(x => x.UserGuid, userGuid);
var countFacet = AggregateFacet.Create("count",
new EmptyPipelineDefinition<QuestProfitStatsItemResult>()
.Count()
);
var pagingFacet = AggregateFacet.Create("paging",
new EmptyPipelineDefinition<QuestProfitStatsItemResult>()
.Skip((pageNumber - 1) * pageSize)
.Limit(pageSize)
);
var pipeline = QuestContentQuery.questProfitStatsPipeline<QuestProfitStatsItemResult>(
filter,
QuestContentQuery.QuestProfitStatsResultProjection)
.Facet(countFacet, pagingFacet);
var aggregation = await (await Collection.AggregateAsync(pipeline)).ToListAsync();
var count = aggregation.First()
.Facets.First(x => x.Name == "count")
.Output<AggregateCountResult>()
?.FirstOrDefault()
?.Count;
var paging = aggregation.First()
.Facets.First(x => x.Name == "paging")
.Output<QuestProfitStatsItemResult>();
int totalPages = 0;
if (count != null)
totalPages = (int)Math.Ceiling((double)count / pageSize);
return new QuestProfitStatsQueryResult
{
PageNumber = pageNumber,
PageSize = pageSize,
TotalPages = totalPages,
Items = paging.ToList(),
};
}
}