Files
caliverse_server/UGQDataAccess/Repository/QuestContentRepository.cs
2025-05-01 07:20:41 +09:00

687 lines
26 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
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(),
};
}
}