using System.Globalization; using System.Security.Claims; using Asp.Versioning; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; using ServerCommon; using ServerCommon.UGQ.Models; using Swashbuckle.AspNetCore.Annotations; using UGQApiServer.Controllers.Common; using UGQApiServer.Converter; using UGQApiServer.Models; using UGQDataAccess.Repository.Models; using UGQDataAccess.Service; using UGQDatabase.Models; using ServerBase; namespace UGQApiServer.Controllers.Admin; [Authorize] [ApiController] [ApiVersion("1.0")] [ApiExplorerSettings(GroupName = "admin")] [Route("api/v{version:apiVersion}/Admin/QuestEditor")] [ControllerName("QuestEditor")] [UGQAdminApi] [Authorize(Roles = "Admin,Service")] public class AdminQuestEditorController : ControllerBase { const int MAX_PAGE_SIZE = 100; AccountService _accountService; QuestEditorService _questEditorService; MetaDataApi _metaDataApi; QuestEditorApi _questEditorApi; DynamoDbClient _dynamoDbClient; public AdminQuestEditorController( AccountService accountService, QuestEditorService questEditorService, MetaDataApi metaDataApi, QuestEditorApi questEditorApi, DynamoDbClient dynamoDbClient) { _accountService = accountService; _questEditorService = questEditorService; _metaDataApi = metaDataApi; _questEditorApi = questEditorApi; _dynamoDbClient = dynamoDbClient; } string? adminUsernameFromToken() { return User.Claims.FirstOrDefault(c => c.Type == ClaimTypes.Name)?.Value; } /// /// 퀘스트 생성 /// /// /// 빈 슬롯에 퀘스트 추가하는 경우에 호출 /// [HttpPost] [Route("quest")] [Produces("application/json")] [SwaggerResponse(StatusCodes.Status200OK, Type = typeof(UGQContent))] [SwaggerResponse(StatusCodes.Status400BadRequest, Type = typeof(ApiErrorResponse))] public async Task addQuest([FromQuery] string userGuid, [FromBody] UGQSaveQuestModel saveQuestModel) { string? adminUsername = adminUsernameFromToken(); if(adminUsername == null) return Results.Unauthorized(); return await _questEditorApi.addQuest(userGuid, saveQuestModel, adminUsername); } /// /// 퀘스트 내용 가져오기 /// /// UGQSummary의 QuestContentId 값으로 api 호출 /// 유저 Guid [HttpGet] [Route("quest/{questContentId}")] [Produces("application/json")] [SwaggerResponse(StatusCodes.Status200OK, Type = typeof(UGQContent))] [SwaggerResponse(StatusCodes.Status400BadRequest, Type = typeof(ApiErrorResponse))] public async Task getQuest(string questContentId, [FromQuery] string userGuid) { return await _questEditorApi.getQuest(userGuid, questContentId); } /// /// 편집 가능한 경우 퀘스트 내용 가져오기 /// /// /// GET api와는 다르게 편집 불가능한 state인 경우에는 실패 리턴 /// /// UGQSummary의 QuestContentId 값으로 api 호출 /// 유저 Guid [HttpPost] [Route("quest/{questContentId}/edit")] [Produces("application/json")] [SwaggerResponse(StatusCodes.Status200OK, Type = typeof(UGQContent))] [SwaggerResponse(StatusCodes.Status400BadRequest, Type = typeof(ApiErrorResponse))] public async Task editQuest(string questContentId, [FromQuery] string userGuid) { return await _questEditorApi.editQuest(userGuid, questContentId); } /// /// 퀘스트 내용 업데이트 /// /// /// Task의 DialogId는 업데이트하지 않음. /// 편집 불가능 state인 경우 에러 리턴 /// /// GET 또는 edit 에서 얻은 questContentId 사용 /// 유저 Guid /// 웹에서 입력한 값 [HttpPut] [Route("quest/{questContentId}")] [Produces("application/json")] [SwaggerResponse(StatusCodes.Status200OK, Type = typeof(UGQContent))] [SwaggerResponse(StatusCodes.Status400BadRequest, Type = typeof(ApiErrorResponse))] public async Task saveQuest(string questContentId, [FromQuery] string userGuid, [FromBody] UGQSaveQuestModel saveQuestModel) { return await _questEditorApi.saveQuest(userGuid, questContentId, saveQuestModel); } /// /// 퀘스트 삭제 /// /// GET또는 edit 에서 얻은 questContentId 사용 /// 유저 Guid [HttpDelete] [Route("quest/{questContentId}")] [Produces("application/json")] [SwaggerResponse(StatusCodes.Status200OK)] [SwaggerResponse(StatusCodes.Status400BadRequest, Type = typeof(ApiErrorResponse))] public async Task deleteQuest(string questContentId, [FromQuery] string userGuid) { return await _questEditorApi.deleteQuest(userGuid, questContentId); } /// /// 퀘스트 이미지 업로드 /// /// /// 세부사항은 정리 예정 /// /// GET 또는 edit 에서 얻은 questContentId 사용 /// 유저 Guid /// 기본 이미지 인덱스 /// 기본 이미지 인덱스 /// 업로드 파일 /// 업로드 파일 [HttpPost] [Route("quest-image/{questContentId}")] [Produces("application/json")] public async Task uploadQuestImage(string questContentId, [FromQuery] string userGuid, [FromQuery] int? titleImageId, [FromQuery] int? bannerImageId, IFormFile? title, IFormFile? banner) { return await _questEditorApi.uploadQuestImage(userGuid, questContentId, titleImageId ?? 0, bannerImageId ?? 0, title, banner); } /// /// 퀘스트 타이틀 이미지 업로드 /// /// /// 세부사항은 정리 예정 /// /// 유저 Guid /// GET 또는 edit 에서 얻은 questContentId 사용 /// 기본 이미지 인덱스 /// 업로드 파일 [HttpPost] [Route("quest-title-image/{questContentId}")] [Produces("application/json")] public async Task uploadQuestTitleImage(string questContentId, [FromQuery] string userGuid, [FromQuery] int? titleImageId, IFormFile? title) { return await _questEditorApi.uploadQuestImage(userGuid, questContentId, titleImageId ?? 0, 0, title, null); } /// /// 퀘스트 배너 이미지 업로드 /// /// /// 세부사항은 정리 예정 /// /// 유저 Guid /// GET 또는 edit 에서 얻은 questContentId 사용 /// 기본 이미지 인덱스 /// 업로드 파일 [HttpPost] [Route("quest-banner-image/{questContentId}")] [Produces("application/json")] public async Task uploadQuestBannerImage(string questContentId, [FromQuery] string userGuid, [FromQuery] int? bannerImageId, IFormFile? banner) { return await _questEditorApi.uploadQuestImage(userGuid, questContentId, 0, bannerImageId ?? 0, null, banner); } /// /// 퀘스트 다이얼로그 가져오기 /// /// GET 또는 edit 에서 얻은 questContentId 사용 /// UGQContent.Tasks에 있는 DialogId 사용 /// 유저 Guid [HttpGet] [Route("quest-dialog/{questContentId}/{dialogId}")] [Produces("application/json")] [SwaggerResponse(StatusCodes.Status200OK, Type = typeof(UGQDialog))] [SwaggerResponse(StatusCodes.Status400BadRequest, Type = typeof(ApiErrorResponse))] public async Task getQuestDialog(string questContentId, string dialogId, [FromQuery] string userGuid) { return await _questEditorApi.getQuestDialog(userGuid, questContentId, dialogId); } /// /// 퀘스트 다이얼로그 새로 생성 /// /// /// Dialog 를 추가하고, UGQContent.Tasks의 DialogId를 저장 /// /// GET 또는 edit 에서 얻은 questContentId 사용 /// 유저 Guid /// 몇번째 Task에 추가되는 다이얼로그인지. 0부터 시작 /// questDialog [HttpPost] [Route("quest-dialog/{questContentId}")] [Produces("application/json")] [SwaggerResponse(StatusCodes.Status200OK, Type = typeof(UGQAddQuestDialogResponse))] [SwaggerResponse(StatusCodes.Status400BadRequest, Type = typeof(ApiErrorResponse))] public async Task addQuestDialog(string questContentId, [FromQuery] string userGuid, [FromQuery] int taskIndex, [FromBody] UGQSaveDialogModel questDialog) { return await _questEditorApi.addQuestDialog(userGuid, questContentId, taskIndex, questDialog); } /// /// 퀘스트 다이얼로그 업데이트 /// /// GET 또는 edit 에서 얻은 questContentId 사용 /// UGQContent.Tasks에 있는 DialogId 사용 /// 유저 Guid /// /// questDialog [HttpPut] [Route("quest-dialog/{questContentId}/{dialogId}")] [Produces("application/json")] [SwaggerResponse(StatusCodes.Status200OK, Type = typeof(UGQDialog))] [SwaggerResponse(StatusCodes.Status400BadRequest, Type = typeof(ApiErrorResponse))] public async Task saveQuestDialog(string questContentId, string dialogId, [FromQuery] string userGuid, [FromBody] UGQSaveDialogModel questDialog) { return await _questEditorApi.saveQuestDialog(userGuid, questContentId, dialogId, questDialog); } /// /// 퀘스트 state 변경 /// /// /// Test 로 변경 실패 시 ValidationErrorResponse로 응답 /// /// GET또는 edit 에서 얻은 questContentId 사용 /// 유저 Guid /// UGQState [HttpPatch] [Route("quest-state/{questContentId}")] [Produces("application/json")] [SwaggerResponse(StatusCodes.Status200OK, Type = typeof(UGQContent))] [SwaggerResponse(StatusCodes.Status400BadRequest, Type = typeof(ApiErrorResponse))] [SwaggerResponse(StatusCodes.Status400BadRequest, Type = typeof(ValidationErrorResponse))] public async Task changeQuestState(string questContentId, [FromQuery] string userGuid, [FromQuery] QuestContentState state) { string? adminUsername = adminUsernameFromToken(); if (adminUsername == null) return Results.Unauthorized(); return await _questEditorApi.changeQuestState(userGuid, questContentId, state, adminUsername); } [HttpGet] [Route("creator-point-history")] [Produces("application/json")] [SwaggerResponse(StatusCodes.Status200OK, Type = typeof(UGQCreatorPointHistoryResult))] [SwaggerResponse(StatusCodes.Status400BadRequest, Type = typeof(ApiErrorResponse))] public async Task getCreatorPointHistory([FromQuery] string userGuid, [FromQuery] int pageNumber, [FromQuery] int pageSize, [FromQuery] CreatorPointHistoryKind kind, DateTimeOffset startDate, DateTimeOffset endDate) { return await _questEditorApi.getCreatorPointHistory(userGuid, pageNumber, pageSize, kind, startDate, endDate); } /// /// 모든 퀘스트 보기 /// [HttpGet] [Route("all-quests")] [Produces("application/json")] public async Task getAllQuests([FromQuery] QuestContentState state, [FromQuery] int pageNumber, [FromQuery] int pageSize, [FromQuery] string? searchText) { pageSize = Math.Clamp(pageSize, 1, MAX_PAGE_SIZE); var result = await _questEditorService.getAllQuests(pageNumber, pageSize, state, searchText); long allCount = await _questEditorService.getAllCount(); LangEnum langEnum = Culture.ToLangEnum(CultureInfo.CurrentCulture.Name); return Results.Ok(new UQGAllQuestResponse { TotalCount = allCount, PageNumber = result.PageNumber, PageSize = result.PageSize, TotalPages = result.TotalPages, Summaries = result.Items.Select(x => x.ToDTO(langEnum)).ToList() }); } /// /// 계정의 모든 퀘스트 보기 /// [HttpGet] [Route("account-quest")] [Produces("application/json")] [SwaggerResponse(StatusCodes.Status200OK, Type = typeof(List))] [SwaggerResponse(StatusCodes.Status400BadRequest, Type = typeof(ApiErrorResponse))] public async Task getQuests([FromQuery] int pageNumber, [FromQuery] int pageSize, [FromQuery] string userGuid, [FromQuery] QuestContentState state, [FromQuery] string? searchText) { pageSize = Math.Clamp(pageSize, 1, MAX_PAGE_SIZE); List all = await _questEditorService.getAll(userGuid); var result = await _questEditorService.getSummaries(pageNumber, pageSize, userGuid, state, searchText); LangEnum langEnum = Culture.ToLangEnum(CultureInfo.CurrentCulture.Name); return Results.Ok(new UGQSummaryResponse { TotalCount = all.Count, PageNumber = result.PageNumber, PageSize = result.PageSize, TotalPages = result.TotalPages, Summaries = result.Items.Select(x => x.ToDTO(langEnum)).ToList(), }); } }