using System.Globalization; using Asp.Versioning; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc; using MongoDB.Bson.Serialization.Attributes; using Swashbuckle.AspNetCore.Annotations; using Swashbuckle.AspNetCore.Filters; using UGQApiServer.Models; using UGQDatabase.Models; using UGQDataAccess.Service; using UGQApiServer.Converter; using UGQApiServer.Storage; using UGQDataAccess.Repository.Models; using Microsoft.AspNetCore.Authorization; using UGQDataAccess; using UGQApiServer.Validation; using UGQApiServer.UGQData; using UGQApiServer.Auth; using JwtRoleAuthentication.Services; using System.Security.Claims; using Newtonsoft.Json.Linq; using ServerCommon; using ServerBase; using ServerCommon.UGQ; using ServerCommon.UGQ.Models; using Microsoft.Extensions.Caching.Distributed; using Microsoft.IdentityModel.Tokens; using System.Collections.Generic; namespace UGQApiServer.Controllers.Common; public class QuestEditorApi { QuestEditorService _questEditorService; UGQMetaData _ugqMetaData; DynamoDbClient _dynamoDbClient; IStorageService _storageService; public QuestEditorApi(QuestEditorService questEditorService, UGQMetaData ugqMetaData, DynamoDbClient dynamoDbClient, IStorageService storageService) { _questEditorService = questEditorService; _ugqMetaData = ugqMetaData; _dynamoDbClient = dynamoDbClient; _storageService = storageService; } public async Task addQuest(string userGuid, UGQSaveQuestModel saveQuestModel, string adminUsername = "") { if (string.IsNullOrEmpty(saveQuestModel.UgcBeaconGuid) == false) { (Result result, List? ugcNpcs) = await EntitySearchHelper.findUgcNpcAttribAllByUserGuid(_dynamoDbClient, userGuid, ""); if (result.isFail()) return Results.BadRequest(ApiResponseFactory.error(ServerErrorCode.UgqGameDbaccessError)); if (ugcNpcs == null) return Results.BadRequest(ApiResponseFactory.error(ServerErrorCode.UgqGameDbaccessError)); UgcNpcAttrib? found = ugcNpcs.Where(x => x.UgcNpcMetaGuid == saveQuestModel.UgcBeaconGuid).FirstOrDefault(); if (found == null) return Results.BadRequest(ApiResponseFactory.error(ServerErrorCode.UgqNotOwnUgcNpc)); saveQuestModel.UgcBeaconNickname = found.Nickname; } foreach (var task in saveQuestModel.Tasks) { if (string.IsNullOrEmpty(task.UgcActionValueGuid) == false) { (Result result, List? ugcNpcs) = await EntitySearchHelper.findUgcNpcAttribAllByUserGuid(_dynamoDbClient, userGuid, ""); if (result.isFail()) return Results.BadRequest(ApiResponseFactory.error(ServerErrorCode.UgqGameDbaccessError)); if (ugcNpcs == null) return Results.BadRequest(ApiResponseFactory.error(ServerErrorCode.UgqGameDbaccessError)); UgcNpcAttrib? found = ugcNpcs.Where(x => x.UgcNpcMetaGuid == task.UgcActionValueGuid).FirstOrDefault(); if (found == null) return Results.BadRequest(ApiResponseFactory.error(ServerErrorCode.UgqNotOwnUgcNpc)); task.UgcActionValueName = found.Nickname; } } (ServerErrorCode errorCode, var entity) = await _questEditorService.addQuest(userGuid, saveQuestModel, adminUsername); if (errorCode != ServerErrorCode.Success) return Results.BadRequest(ApiResponseFactory.error(errorCode)); if (entity == null) return Results.BadRequest(ApiResponseFactory.error(ServerErrorCode.UgqNullEntity)); LangEnum langEnum = Culture.ToLangEnum(CultureInfo.CurrentCulture.Name); return Results.Ok(entity.ToDTO(langEnum)); } public async Task getQuest(string userGuid, string questContentId) { (ServerErrorCode errorCode, var entity) = await _questEditorService.getQuest(userGuid, questContentId); if (errorCode != ServerErrorCode.Success) return Results.BadRequest(ApiResponseFactory.error(errorCode)); if (entity == null) return Results.BadRequest(ApiResponseFactory.error(ServerErrorCode.UgqNullEntity)); LangEnum langEnum = Culture.ToLangEnum(CultureInfo.CurrentCulture.Name); return Results.Ok(entity.ToDTO(langEnum)); } public async Task editQuest(string userGuid, string questContentId) { (ServerErrorCode errorCode, var entity) = await _questEditorService.editQuest(userGuid, questContentId); if (errorCode != ServerErrorCode.Success) return Results.BadRequest(ApiResponseFactory.error(errorCode)); if (entity == null) return Results.BadRequest(ApiResponseFactory.error(ServerErrorCode.UgqNullEntity)); LangEnum langEnum = Culture.ToLangEnum(CultureInfo.CurrentCulture.Name); return Results.Ok(entity.ToDTO(langEnum)); } public async Task saveQuest(string userGuid, string questContentId, UGQSaveQuestModel saveQuestModel) { if (string.IsNullOrEmpty(saveQuestModel.UgcBeaconGuid) == false) { (Result result, List? ugcNpcs) = await EntitySearchHelper.findUgcNpcAttribAllByUserGuid(_dynamoDbClient, userGuid, ""); if (result.isFail()) return Results.BadRequest(ApiResponseFactory.error(ServerErrorCode.UgqGameDbaccessError)); if (ugcNpcs == null) return Results.BadRequest(ApiResponseFactory.error(ServerErrorCode.UgqGameDbaccessError)); UgcNpcAttrib? found = ugcNpcs.Where(x => x.UgcNpcMetaGuid == saveQuestModel.UgcBeaconGuid).FirstOrDefault(); if (found == null) return Results.BadRequest(ApiResponseFactory.error(ServerErrorCode.UgqNotOwnUgcNpc)); saveQuestModel.UgcBeaconNickname = found.Nickname; } foreach (var task in saveQuestModel.Tasks) { if (string.IsNullOrEmpty(task.UgcActionValueGuid) == false) { (Result result, List? ugcNpcs) = await EntitySearchHelper.findUgcNpcAttribAllByUserGuid(_dynamoDbClient, userGuid, ""); if (result.isFail()) return Results.BadRequest(ApiResponseFactory.error(ServerErrorCode.UgqGameDbaccessError)); if (ugcNpcs == null) return Results.BadRequest(ApiResponseFactory.error(ServerErrorCode.UgqGameDbaccessError)); UgcNpcAttrib? found = ugcNpcs.Where(x => x.UgcNpcMetaGuid == task.UgcActionValueGuid).FirstOrDefault(); if (found == null) return Results.BadRequest(ApiResponseFactory.error(ServerErrorCode.UgqNotOwnUgcNpc)); task.UgcActionValueName = found.Nickname; } } (ServerErrorCode errorCode, var updated) = await _questEditorService.saveQuest(userGuid, questContentId, saveQuestModel); if (errorCode != ServerErrorCode.Success) return Results.BadRequest(ApiResponseFactory.error(errorCode)); if (updated == null) return Results.BadRequest(ApiResponseFactory.error(ServerErrorCode.UgqNullEntity)); LangEnum langEnum = Culture.ToLangEnum(CultureInfo.CurrentCulture.Name); return Results.Ok(updated.ToDTO(langEnum)); } public async Task deleteQuest(string userGuid, string questContentId) { var errorCode = await _questEditorService.deleteQuest(userGuid, questContentId); if (errorCode != ServerErrorCode.Success) return Results.BadRequest(ApiResponseFactory.error(errorCode)); return Results.Ok(); } public async Task uploadQuestImage(string userGuid, string questContentId, int titleImageId, int bannerImageId, IFormFile? title, IFormFile? banner) { (ServerErrorCode errorCode, var entity) = await _questEditorService.editQuest(userGuid, questContentId); if (errorCode != ServerErrorCode.Success) return Results.BadRequest(ApiResponseFactory.error(errorCode)); if (entity == null) return Results.BadRequest(ApiResponseFactory.error(ServerErrorCode.UgqNullEntity)); string titleFile = entity.TitleImagePath; string bannerFile = entity.BannerImagePath; List deleteFiles = new(); if (titleImageId != 0) { var data = _ugqMetaData.getPresetImage(titleImageId); if (data != null) titleFile = data.FileName; } else { if (title != null) { FileInfo fileInfo = new FileInfo(title.FileName); (errorCode, int uploadCounter) = await _questEditorService.incUploadCounter(userGuid, questContentId); if (errorCode != ServerErrorCode.Success) return Results.BadRequest(ApiResponseFactory.error(errorCode)); string filename = $"{questContentId}_title_{uploadCounter}{fileInfo.Extension}"; await _storageService.uploadFile(filename, title); if (string.IsNullOrEmpty(entity.TitleImagePath) == false && entity.TitleImagePath.StartsWith("preset/") == false) deleteFiles.Add(entity.TitleImagePath); titleFile = filename; } } if (bannerImageId != 0) { var data = _ugqMetaData.getPresetImage(bannerImageId); if (data != null) bannerFile = data.FileName; } else { if (banner != null) { FileInfo fileInfo = new FileInfo(banner.FileName); (errorCode, int uploadCounter) = await _questEditorService.incUploadCounter(userGuid, questContentId); if (errorCode != ServerErrorCode.Success) return Results.BadRequest(ApiResponseFactory.error(errorCode)); string filename = $"{questContentId}_banner_{uploadCounter}{fileInfo.Extension}"; await _storageService.uploadFile(filename, banner); if (string.IsNullOrEmpty(entity.BannerImagePath) == false && entity.BannerImagePath.StartsWith("preset/") == false) deleteFiles.Add(entity.BannerImagePath); bannerFile = filename; } } (errorCode, var updated) = await _questEditorService.updateQuestImages(questContentId, titleFile, bannerFile); if (errorCode != ServerErrorCode.Success) return Results.BadRequest(ApiResponseFactory.error(errorCode)); if (updated == null) return Results.BadRequest(ApiResponseFactory.error(ServerErrorCode.UgqNullEntity)); foreach (var file in deleteFiles) await _storageService.deleteFile(file); LangEnum langEnum = Culture.ToLangEnum(CultureInfo.CurrentCulture.Name); return Results.Ok(updated.ToDTO(langEnum)); } public async Task getQuestDialog(string userGuid, string questContentId, string dialogId) { (ServerErrorCode errorCode, var entity) = await _questEditorService.getQuestDialog(userGuid, questContentId, dialogId); if (errorCode != ServerErrorCode.Success) return Results.BadRequest(ApiResponseFactory.error(errorCode)); if (entity == null) return Results.BadRequest(ApiResponseFactory.error(ServerErrorCode.UgqNullEntity)); LangEnum langEnum = Culture.ToLangEnum(CultureInfo.CurrentCulture.Name); return Results.Ok(entity.ToDTO(langEnum)); } public async Task addQuestDialog(string userGuid, string questContentId, int taskIndex, UGQSaveDialogModel questDialog) { var sequences = questDialog.Sequences.Select(x => new DialogSequenceEntity { SequenceId = x.SequenceId, Actions = x.Actions.Select(x => new DialogSequenceActionEntity { Talker = x.Talker, Type = x.Type, Talk = new TextEntity { Kr = x.Talk.Kr, En = x.Talk.En, Jp = x.Talk.Jp, }, NpcAction = x.NpcAction, Condition = x.Condition, ConditionValue = x.ConditionValue, NextSequence = x.NextSequence, }).ToList(), }).ToList(); (ServerErrorCode errorCode, var updated, var inserted) = await _questEditorService.addQuestDialog(userGuid, questContentId, taskIndex, sequences); if (errorCode != ServerErrorCode.Success) return Results.BadRequest(ApiResponseFactory.error(errorCode)); if (updated == null || inserted == null) return Results.BadRequest(ApiResponseFactory.error(ServerErrorCode.UgqNullEntity)); LangEnum langEnum = Culture.ToLangEnum(CultureInfo.CurrentCulture.Name); return Results.Ok(new UGQAddQuestDialogResponse { QuestContent = updated.ToDTO(langEnum), QuestDialog = inserted.ToDTO(langEnum), }); } public async Task saveQuestDialog(string userGuid, string questContentId, string dialogId, [FromBody] UGQSaveDialogModel questDialog) { var sequences = questDialog.Sequences.Select(x => new DialogSequenceEntity { SequenceId = x.SequenceId, Actions = x.Actions.Select(x => new DialogSequenceActionEntity { Talker = x.Talker, Type = x.Type, Talk = new TextEntity { Kr = x.Talk.Kr, En = x.Talk.En, Jp = x.Talk.Jp, }, NpcAction = x.NpcAction, Condition = x.Condition, ConditionValue = x.ConditionValue, NextSequence = x.NextSequence, }).ToList(), }).ToList(); (ServerErrorCode errorCode, var updated) = await _questEditorService.saveQuestDialog(userGuid, questContentId, dialogId, sequences); if (errorCode != ServerErrorCode.Success) return Results.BadRequest(ApiResponseFactory.error(errorCode)); if (updated == null) return Results.BadRequest(ApiResponseFactory.error(ServerErrorCode.UgqNullEntity)); LangEnum langEnum = Culture.ToLangEnum(CultureInfo.CurrentCulture.Name); return Results.Ok(updated.ToDTO(langEnum)); } List getAvailablesState(QuestContentState state) { List availables = []; switch (state) { case QuestContentState.Editable: availables = [ QuestContentState.Test, QuestContentState.Standby, QuestContentState.Shutdown ]; break; case QuestContentState.Uncomplate: case QuestContentState.Test: availables = [ QuestContentState.Editable ]; break; case QuestContentState.Standby: availables = [ QuestContentState.Live ]; break; case QuestContentState.Live: availables = [ QuestContentState.Standby ]; break; case QuestContentState.Shutdown: availables = []; break; } return availables; } public async Task changeQuestState(string userGuid, string questContentId, QuestContentState state, string adminUsername = "") { (ServerErrorCode errorCode, var questContentEntity) = await _questEditorService.getQuest(userGuid, questContentId); if (errorCode != ServerErrorCode.Success) return Results.BadRequest(ApiResponseFactory.error(errorCode)); if (questContentEntity == null) return Results.BadRequest(ApiResponseFactory.error(ServerErrorCode.UgqNullEntity)); // adminÀÎ °æ¿ì Á¦ÇѾøÀÌ º¯°æ °¡´ÉÇϵµ·Ï ó¸® - Uncomplate »óŶó¸é º¯°æ ºÒ°¡´É List before = []; if (string.IsNullOrEmpty(adminUsername) == false) { before = [ QuestContentState.Editable, QuestContentState.Test, QuestContentState.Standby, QuestContentState.Live, QuestContentState.Shutdown, ]; } else { before = getAvailablesState(state); } if (before.Any(x => x == questContentEntity.State) == false) return Results.BadRequest(ApiResponseFactory.error(ServerErrorCode.UgqStateChangeError)); QuestContentEntity? updated = null; var questDialogIds = questContentEntity.Tasks .Where(x => string.IsNullOrEmpty(x.DialogId) == false) .Select(x => x.DialogId!) .ToList(); var questDialogs = await _questEditorService.getQuestDialogs(questDialogIds); if (state == QuestContentState.Test) { UGQValidator validator = new UGQValidator(_ugqMetaData); var errors = validator.Validate(questContentEntity, questDialogs); if (errors.Count > 0) { return Results.BadRequest( ApiResponseFactory.validationError(ServerErrorCode.UgqValidationError, errors.Select(x => x.ToString()).ToList())); } } (errorCode, updated) = await _questEditorService.changeQuestStateForEditor(userGuid, questContentEntity, questDialogs, before, state, adminUsername); if (errorCode != ServerErrorCode.Success) return Results.BadRequest(ApiResponseFactory.error(errorCode)); if (updated == null) return Results.BadRequest(ApiResponseFactory.error(ServerErrorCode.UgqNullEntity)); LangEnum langEnum = Culture.ToLangEnum(CultureInfo.CurrentCulture.Name); return Results.Ok(updated.ToDTO(langEnum)); } public async Task getCreatorPointHistory(string userGuid, int pageNumber, int pageSize, CreatorPointHistoryKind kind, DateTimeOffset startDate, DateTimeOffset endDate) { pageSize = Math.Clamp(pageSize, 1, UGQConstants.MAX_INPUT_PAGE_SIZE); var result = await _questEditorService.getCreatorPointHistories( userGuid, pageNumber, pageSize, kind, startDate, endDate); LangEnum langEnum = Culture.ToLangEnum(CultureInfo.CurrentCulture.Name); return Results.Ok(result.ToDTO(langEnum)); } public async Task getQuestProfitStats(string userGuid, int pageNumber, int pageSize) { pageSize = Math.Clamp(pageSize, 1, UGQConstants.MAX_INPUT_PAGE_SIZE); var result = await _questEditorService.getQuestProfitStats(userGuid, pageNumber, pageSize); LangEnum langEnum = Culture.ToLangEnum(CultureInfo.CurrentCulture.Name); return Results.Ok(new UGQQuestProfitStatsResult { PageNumber = result.PageNumber, PageSize = result.PageSize, TotalPages = result.TotalPages, Items = result.Items.Select(x => x.ToDTO(langEnum)).ToList() }); } }