using Microsoft.AspNetCore.Mvc; using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore.Update.Internal; using Microsoft.AspNetCore.Http.HttpResults; using AcaMate.Common.Data; using AcaMate.V1.Services; using AcaMate.Common.Models; using AcaMate.V1.Models; namespace AcaMate.V1.Controllers; [ApiController] [Route("/api/v1/in/push")] [ApiExplorerSettings(GroupName = "공통")] public class PushController : ControllerBase { private readonly ILogger _logger; private readonly IPushQueue _pushQueue; private readonly AppDbContext _dbContext; private readonly IRepositoryService _repositoryService; public PushController(ILogger logger, IPushQueue pushQueue, AppDbContext dbContext, IRepositoryService repositoryService) { _logger = logger; _pushQueue = pushQueue; _dbContext = dbContext; _repositoryService = repositoryService; } [HttpGet()] [CustomOperation("푸시 확인", "저장된 양식을 확인 할 수 있다.", "푸시")] public async Task GetPush(string bid, string? pid, string? category) { if (!(await _dbContext.Academy.AnyAsync(a=>a.bid == bid))) return Ok(APIResponse.Send("100", "존재하지 않는 BID", Empty)); List pushData = new List(); if (pid == null && category == null) { pushData = await _dbContext.DBPayload .Where(p => p.bid == bid) .ToListAsync(); } else if (pid != null && category == null) { pushData = await _dbContext.DBPayload .Where(p => p.bid == bid && p.pid == pid) .ToListAsync(); } else if (pid == null && category != null) { pushData = await _dbContext.DBPayload .Where(p => p.bid == bid && p.category == category) .ToListAsync(); } else //if (pid != null && category != null) { pushData = await _dbContext.DBPayload .Where(p => p.bid == bid && p.pid == pid && p.category == category) .ToListAsync(); } try { if (pushData.Count > 0) { return Ok(APIResponse.Send("000", "정상", pushData)); } return Ok(APIResponse.Send("001", "PUSH 데이터가 없음", Empty)); } catch (Exception ex) { _logger.LogError($"[푸시] {ex.Message}"); return StatusCode(500, APIResponse.UnknownError()); } } [HttpPost("send")] [CustomOperation("푸시 발송", "저장된 양식으로, 사용자에게 푸시를 송신한다.", "푸시")] [ProducesResponseType(StatusCodes.Status200OK, Type = typeof(APIResponseStatus))] public async Task SendPush([FromBody] PushRequest pushRequest) { string summary = String.Empty; try { summary = _repositoryService.ReadSummary(typeof(PushController), "SendPush"); if (!ModelState.IsValid) return BadRequest(APIResponse.InvalidInputError()); var payload = await _dbContext.DBPayload .Where(p => p.pid == pushRequest.pid && p.bid == pushRequest.bid) .Select(p => new Payload { aps = new Aps { alert = new Alert { title = p.title, body = p.body, subtitle = p.subtitle ?? "" }, category = p.category }, pid = pushRequest.pid, bid = pushRequest.bid, content = pushRequest.content ?? (p.content ?? ""), }) .FirstOrDefaultAsync(); await Task.Run(async () => { foreach (var uid in pushRequest.uids) { // 학원 내부에 해당 uid의 일원이 존재하는지 확인 if ( await _dbContext.UserAcademy .Where(ua => ua.uid == uid && ua.bid == pushRequest.bid) .AnyAsync() ) { // 유저한테 온 모든 푸시에 대해서 안 읽은 것에 대한 뱃지 갯수 확인 var badge = await _dbContext.PushCabinet .Where(c => c.uid == uid && c.check_yn == false) .CountAsync(); // 푸시를 보내야 하니 푸시 토큰 확인 var pushToken = await _dbContext.User .Where(u => u.uid == uid) .Select(u => u.push_token) .FirstOrDefaultAsync() ?? ""; var pushCabinet = new PushCabinet { uid = uid, bid = pushRequest.bid, pid = pushRequest.pid, send_date = DateTime.Now, content = pushRequest.content ?? null, }; if (payload != null) payload.aps.badge = badge + 1; var pushData = new PushData { pushToken = pushToken, payload = payload ?? throw new PushInvalidException("payload is NULL") }; if (await _repositoryService.SaveData(pushCabinet)) { var logPush = new LogPush { bid = pushRequest.bid, pid = pushRequest.pid, create_date = DateTime.Now, create_uid = "System", log = $"[{summary}] : 푸시 캐비닛 저장 성공" }; if (await _repositoryService.SaveData(logPush)) _logger.LogInformation($"[{summary}] : 로그 추가"); } _pushQueue.Enqueue(pushData); } else { // 존재하지 않는 경우에는 지나가서 다른 uid 로 확인 하겠지 var logPush = new LogPush { bid = pushRequest.bid, pid = pushRequest.pid, create_date = DateTime.Now, create_uid = "System", log = $"[{summary}] : 푸시 전송 실패" }; if (await _repositoryService.SaveData(logPush)) _logger.LogInformation($"[{summary}] : 로그 추가"); } } }); return Ok(APIResponse.Send("000", "정상", Empty)); } catch (PushInvalidException ex) { _logger.LogError(ex.Message); return Ok(APIResponse.Send("001", $"[{summary}]: 푸시 송신 중 문제 발생",Empty)); } catch (Exception ex) { _logger.LogError($"[푸시] {ex.Message}"); return StatusCode(500, APIResponse.UnknownError()); } } [HttpPost("set")] [CustomOperation("[푸시 변경]", "저장된 양식을 변경한다.", "푸시")] [ProducesResponseType(StatusCodes.Status200OK, Type = typeof(APIResponseStatus))] public async Task SetPush([FromBody] DBPayload request) { if (!ModelState.IsValid) return BadRequest(APIResponse.InvalidInputError()); try { var dbPayload = await _dbContext.DBPayload .FirstOrDefaultAsync(p => p.pid == request.pid && p.bid == request.bid); if (dbPayload != null) { if (dbPayload.title != request.title && request.title != "") dbPayload.title = request.title; if (dbPayload.body != request.body && request.body != "") dbPayload.body = request.body; if (dbPayload.subtitle != request.subtitle) dbPayload.subtitle = request.subtitle; if (dbPayload.alert_yn != request.alert_yn) dbPayload.alert_yn = request.alert_yn; if (dbPayload.category != request.category && request.category != "") dbPayload.category = request.category; if (dbPayload.content != request.content) dbPayload.content = request.content; // if (await _repositoryService.SaveData(dbPayload, p => p.pid)) if (await _repositoryService.SaveData(dbPayload)) return Ok(APIResponse.Send("000", "[푸시 변경] : PUSH 정보 변경 완료", Empty)); } return Ok(APIResponse.Send("100", "PID, BID 또는 Cabinet 오류", Empty)); } catch (Exception ex) { _logger.LogError($"[푸시] {ex.Message}"); return StatusCode(500, APIResponse.UnknownError()); } } [HttpPost("create")] [CustomOperation("푸시 생성", "새로운 푸시 양식을 생성한다.", "푸시")] [ProducesResponseType(StatusCodes.Status200OK, Type = typeof(APIResponseStatus))] public async Task CreatePush(string token, string refresh, [FromBody] CreatePush createPush) { if (string.IsNullOrEmpty(token) || string.IsNullOrEmpty(refresh)) return BadRequest(APIResponse.InvalidInputError()); if(!ModelState.IsValid) return BadRequest(APIResponse.InvalidInputError()); var validateToken = await _repositoryService.ValidateToken(token, refresh); var uid = validateToken.uid; Func randomLetter = (letters, count) => new string(Enumerable.Range(0, count).Select(_ => letters[new Random().Next(letters.Length)]).ToArray()); var letters = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"; var digits = "0123456789"; var frontLetters = $"{randomLetter(letters, 1)}{randomLetter(digits, 1)}{randomLetter(letters, 1)}"; var afterLetters = $"{randomLetter(letters, 1)}{randomLetter(digits, 1)}{randomLetter(letters, 1)}"; string summary = String.Empty; try { summary = _repositoryService.ReadSummary(typeof(PushController), "CreatePush"); if (await _dbContext.Academy.AnyAsync(a => a.bid == createPush.bid)) { DBPayload payload = new DBPayload { bid = createPush.bid, pid = $"AP{DateTime.Now:yyyyMMdd}{frontLetters}{DateTime.Now:HHmmss}{afterLetters}", title = createPush.title, subtitle = createPush.subtitle, body = createPush.body, alert_yn = createPush.alert_yn, category = createPush.category, content = createPush.content, }; if (await _repositoryService.SaveData(payload)) { var logPush = new LogPush { bid = payload.bid, pid = payload.pid, create_uid = uid, create_date = DateTime.Now, log = $"[{summary}] {payload.pid} 최초 생성 - {uid}" }; // 로그를 이제 만들어서 추가를 해야 합니다. if (await _repositoryService.SaveData(logPush)) _logger.LogInformation("[푸시 생성] 로그 추가"); return Ok(APIResponse.Send("000", "정상, push 저장 완료", Empty)); } } return Ok(APIResponse.Send("100", "학원 정보(BID) 확인 불가", Empty)); } catch (TokenException tokenEx) { _logger.LogInformation($"[푸시 생성] : {tokenEx}"); return Ok(APIResponse.Send("001", "[푸시 생성] : 토큰에 문제가 있음",Empty)); } catch (RefreshRevokeException refreshEx) { _logger.LogInformation($"[푸시 생성] : {refreshEx}"); return Ok(APIResponse.Send("001", "[푸시 생성] : 폐기된 리프레시 토큰",Empty)); } catch (Exception ex) { _logger.LogError($"[푸시] {ex.Message}"); return StatusCode(500, APIResponse.UnknownError()); } } [HttpDelete("delete")] [CustomOperation("푸시 삭제", "저장된 푸시 양식을 삭제 한다.", "푸시")] [ProducesResponseType(StatusCodes.Status200OK, Type = typeof(APIResponseStatus))] public async Task DeletePush(string token, string refresh, string bid, string pid) { if (string.IsNullOrEmpty(token) || string.IsNullOrEmpty(refresh)) return BadRequest(APIResponse.InvalidInputError()); if(!ModelState.IsValid) return BadRequest(APIResponse.InvalidInputError()); var validateToken = await _repositoryService.ValidateToken(token, refresh); var uid = validateToken.uid; string summary = String.Empty; try { summary = _repositoryService.ReadSummary(typeof(PushController), "DeletePush"); var payload = await _dbContext.DBPayload.FirstOrDefaultAsync(p => p.bid == bid && p.pid == pid); if (payload == null) return Ok(APIResponse.Send("001", $"[{summary}], 삭제 할 PUSH 없음", Empty)); if (!await _repositoryService.DeleteData(payload)) return Ok(APIResponse.Send("002", $"[{summary}], PUSH 삭제 실패", Empty)); // 로그를 이제 만들어서 추가를 해야 합니다. var logPush = new LogPush { bid = bid, pid = pid, create_uid = uid, create_date = DateTime.Now, log = $"[{summary}] : {pid} 삭제 - {uid}" }; // 로그를 이제 만들어서 추가를 해야 합니다. if (await _repositoryService.SaveData(logPush)) _logger.LogInformation($"[{summary}] : 로그 추가"); return Ok(APIResponse.Send("000", $"[{summary}], 정상", Empty)); } /* */ catch (Exception ex) { _logger.LogError($"[{summary}] : {ex.Message}"); return BadRequest(APIResponse.UnknownError()); } } [HttpGet("list")] [CustomOperation("사용자 푸시 목록 조회", "해당 사용자가 받은 푸시의 정보를 조회한다.", "푸시")] [ProducesResponseType(StatusCodes.Status200OK, Type = typeof(APIResponseStatus))] public async Task SearchToUserPush(string token, string refresh, string? pid) { if (string.IsNullOrEmpty(token) || string.IsNullOrEmpty(refresh)) return BadRequest(APIResponse.InvalidInputError()); if (!ModelState.IsValid) return BadRequest(APIResponse.InvalidInputError()); var validateToken = await _repositoryService.ValidateToken(token, refresh); var uid = validateToken.uid; string summary = String.Empty; try { summary = _repositoryService.ReadSummary(typeof(PushController), "SearchToUserPush"); if (pid == null) { var cabinet = await _dbContext.PushCabinet.Where(c => c.uid == uid).ToListAsync(); } else { int pageSize = 5; var sort = await _dbContext.PushCabinet.Where(p=> p.pid == pid).Select(p => p.send_date).FirstOrDefaultAsync(); var query = _dbContext.PushCabinet.OrderBy(c => c.send_date).AsQueryable(); query = query.Where(c => c.send_date > sort); var pagedData = await query.Take(pageSize).ToListAsync(); foreach (var p in pagedData) { _logger.LogInformation($"[{summary}] : {p.pid}"); } } return Ok(APIResponse.Send("000", $"[{summary}], 정상", Empty)); } catch (Exception ex) { _logger.LogError($"[{summary}] : {ex.Message}"); return BadRequest(APIResponse.UnknownError()); } } }// END PUSH CONTROLLER