forked from AcaMate/AcaMate_API
318 lines
14 KiB
C#
318 lines
14 KiB
C#
using System.Security.Claims;
|
|
using System.Text;
|
|
using System.Text.Json;
|
|
using Back.Program.Common.Auth;
|
|
using Back.Program.Common.Model;
|
|
using Back.Program.Models.Entities;
|
|
using Back.Program.Repositories.V1;
|
|
using Back.Program.Repositories.V1.Interfaces;
|
|
using Back.Program.Services.V1.Interfaces;
|
|
using Microsoft.AspNetCore.Http.HttpResults;
|
|
using Microsoft.Extensions.Options;
|
|
using Polly;
|
|
using Version = System.Version;
|
|
|
|
namespace Back.Program.Services.V1
|
|
{
|
|
public class PushService : IPushService
|
|
{
|
|
private readonly ILogger<PushService> _logger;
|
|
private readonly HttpClient _httpClient;
|
|
private readonly PushFileSetting _setting;
|
|
private readonly JwtTokenService _jwtTokenService;
|
|
private readonly IRepositoryService _repositoryService;
|
|
private readonly IPushQueue _pushQueue;
|
|
|
|
private readonly ILogRepository _logRepository;
|
|
private readonly IPushRepository _pushRepository;
|
|
|
|
public PushService(ILogger<PushService> logger, HttpClient httpClient, IOptions<PushFileSetting> options,
|
|
IPushRepository pushRepository, IRepositoryService repositoryService, IPushQueue pushQueue,
|
|
ILogRepository logRepository, JwtTokenService jwtTokenService)
|
|
{
|
|
_logger = logger;
|
|
_httpClient = httpClient;
|
|
_setting = options.Value;
|
|
_pushRepository = pushRepository;
|
|
_repositoryService = repositoryService;
|
|
_pushQueue = pushQueue;
|
|
_logRepository = logRepository;
|
|
_jwtTokenService = jwtTokenService;
|
|
}
|
|
|
|
public async Task SendPushNotificationAsync(string deviceToken, Payload payload)
|
|
{
|
|
// 존재 안하면 예외 던져 버리는거
|
|
if (!File.Exists(_setting.p12Path) || !File.Exists(_setting.p12PWPath))
|
|
throw new FileNotFoundException("[푸시] : p12 관련 파일 확인 필요");
|
|
|
|
var jsonPayload = JsonSerializer.Serialize(payload);
|
|
|
|
var request = new HttpRequestMessage(HttpMethod.Post, $"/3/device/{deviceToken}")
|
|
{
|
|
Content = new StringContent(jsonPayload, Encoding.UTF8, "application/json"),
|
|
Version = new Version(2, 0) // HTTP/2 사용
|
|
};
|
|
|
|
// 필수 헤더 추가
|
|
request.Headers.Add("apns-topic", _setting.apnsTopic);
|
|
request.Headers.Add("apns-push-type", "alert");
|
|
|
|
var policy = Policy.Handle<HttpRequestException>()
|
|
.WaitAndRetryAsync(3, retryAttempt =>
|
|
TimeSpan.FromSeconds(Math.Pow(2, retryAttempt)));
|
|
|
|
await policy.ExecuteAsync(async () =>
|
|
{
|
|
var response = await _httpClient.SendAsync(request);
|
|
if (!response.IsSuccessStatusCode)
|
|
{
|
|
var errorContent = await response.Content.ReadAsStringAsync();
|
|
throw new ServiceConnectionFailedException($"[푸시] : APNS 통신 실패 - {errorContent}");
|
|
}
|
|
});
|
|
}
|
|
|
|
public async Task<APIResponseStatus<object>> GetPush(string summary, string bid, string? pid, string? category)
|
|
{
|
|
if (!(await _pushRepository.FindAcademy(bid)))
|
|
return APIResponse.Send<object>("100", $"[{summary}], 존재하지 않는 BID", string.Empty);
|
|
|
|
var pushData = await _pushRepository.FindPushList(bid, pid, category);
|
|
|
|
if (pushData.Count > 0)
|
|
return APIResponse.Send<object>("000", $"[{summary}, 정상", pushData);
|
|
return APIResponse.Send<object>("001", $"[{summary}], PUSH 데이터 없음", string.Empty);
|
|
}
|
|
|
|
public async Task<APIResponseStatus<object>> SendPush(string summary, PushRequest pushRequest)
|
|
{
|
|
var payload = await _pushRepository.FindPushPayload(pushRequest.bid, pushRequest.pid);
|
|
if (payload == null) return APIResponse.InvalidInputError($"[{summary}], 저장된 payload 없음");
|
|
|
|
var pushTasks = pushRequest.uids.Select(async uid =>
|
|
{
|
|
if (!await _pushRepository.FindUserAcademy(uid, pushRequest.bid)) return;
|
|
var badge = await _pushRepository.CountBadge(uid);
|
|
var pushToken = await _pushRepository.FindPushToken(uid);
|
|
if (pushToken == null) return;
|
|
|
|
var newPayload = new Payload
|
|
{
|
|
aps = new Aps
|
|
{
|
|
alert = new Alert
|
|
{ title = payload.title, body = payload.body, subtitle = payload.subtitle ?? "" },
|
|
category = payload.category,
|
|
badge = badge + 1
|
|
},
|
|
pid = pushRequest.pid,
|
|
bid = pushRequest.bid,
|
|
content = pushRequest.content ?? (payload.content ?? "")
|
|
};
|
|
|
|
var pushCabinet = new PushCabinet
|
|
{
|
|
uid = uid,
|
|
bid = pushRequest.bid,
|
|
pid = pushRequest.pid,
|
|
send_date = DateTime.Now,
|
|
content = newPayload.content != "" ? newPayload.content : null
|
|
};
|
|
|
|
var pushData = new PushData
|
|
{
|
|
pushToken = pushToken,
|
|
payload = newPayload
|
|
};
|
|
|
|
var log = new LogPush
|
|
{
|
|
bid = pushRequest.bid,
|
|
pid = pushRequest.pid,
|
|
create_date = DateTime.Now,
|
|
create_uid = "System",
|
|
log = ""
|
|
};
|
|
|
|
var saved = await _repositoryService.SaveData<PushCabinet>(pushCabinet);
|
|
log.log = saved ? $"[{summary}]: 푸시 캐비닛 저장 성공 및 푸시 전송 시도" : $"[{summary}]: 푸시 전송 실패";
|
|
var logSaved = await _repositoryService.SaveData<LogPush>(log);
|
|
_logger.LogInformation($"[{summary}]: 캐비닛 저장 = {saved} : 로그 저장 = {logSaved}");
|
|
|
|
if(saved) _pushQueue.Enqueue(pushData);
|
|
});
|
|
|
|
await Task.WhenAll(pushTasks);
|
|
|
|
return APIResponse.Send<object>("000", $"[{summary}], 정상", string.Empty);
|
|
}
|
|
|
|
|
|
public async Task<APIResponseStatus<object>> SetPush(string summary, string token, DBPayload request)
|
|
{
|
|
string uid = String.Empty;
|
|
if (token == "System") uid = "System";
|
|
else
|
|
{
|
|
var validToken = await _jwtTokenService.ValidateToken(token);
|
|
if (validToken == null) return APIResponse.AccessExpireError();
|
|
uid = validToken.FindFirst(ClaimTypes.NameIdentifier)?.Value ?? string.Empty;
|
|
}
|
|
var payload = await _pushRepository.FindPushPayload(request.bid, request.pid);
|
|
if (payload == null)
|
|
return APIResponse.Send<object>("100", $"[{summary}], PID, BID 또는 Cabinet 오류", string.Empty);
|
|
|
|
var log = new LogPush
|
|
{
|
|
bid = request.bid,
|
|
pid = request.pid,
|
|
create_date = DateTime.Now,
|
|
create_uid = uid
|
|
};
|
|
|
|
if (payload.title != request.title && request.title != "") payload.title = request.title;
|
|
if (payload.body != request.body && request.body != "") payload.body = request.body;
|
|
if (payload.subtitle != request.subtitle) payload.subtitle = request.subtitle;
|
|
if (payload.alert_yn != request.alert_yn) payload.alert_yn = request.alert_yn;
|
|
if (payload.category != request.category) payload.category = request.category;
|
|
if (request.content != request.content) payload.content = request.content;
|
|
|
|
var saved = (await _repositoryService.SaveData<DBPayload>(payload));
|
|
log.log = $"[{summary}] : 상태 = {saved}";
|
|
var logSaved = await _repositoryService.SaveData<LogPush>(log);
|
|
_logger.LogInformation($"[{summary}]: 상태 = {saved} : 로그 저장 = {logSaved}");
|
|
|
|
if (!saved) return APIResponse.Send<object>("001", $"[{summary}], 실패", string.Empty);
|
|
return APIResponse.Send<object>("000", $"[{summary}], 정상", string.Empty);
|
|
}
|
|
|
|
|
|
public async Task<APIResponseStatus<object>> CreatePush(string summary, string token, CreatePush request)
|
|
{
|
|
Func<string, int, string> 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 uid = String.Empty;
|
|
if (token == "System") uid = "System";
|
|
else
|
|
{
|
|
var validToken = await _jwtTokenService.ValidateToken(token);
|
|
if (validToken == null) return APIResponse.AccessExpireError();
|
|
uid = validToken.FindFirst(ClaimTypes.NameIdentifier)?.Value ?? string.Empty;
|
|
}
|
|
|
|
if (!(await _pushRepository.FindAcademy(request.bid)))
|
|
return APIResponse.Send<object>("100", $"[{summary}], 학원 정보(BID) 확인 불가", string.Empty);
|
|
|
|
DBPayload payload = new DBPayload
|
|
{
|
|
bid = request.bid,
|
|
pid = $"AP{DateTime.Now:yyyyMMdd}{frontLetters}{DateTime.Now:HHmmss}{afterLetters}",
|
|
title = request.title,
|
|
subtitle = request.subtitle,
|
|
body = request.body,
|
|
alert_yn = request.alert_yn,
|
|
category = request.category,
|
|
content = request.content,
|
|
};
|
|
|
|
var log = new LogPush
|
|
{
|
|
bid = payload.bid,
|
|
pid = payload.pid,
|
|
create_date = DateTime.Now,
|
|
create_uid = uid
|
|
};
|
|
|
|
var saved = await _repositoryService.SaveData<DBPayload>(payload);
|
|
log.log = $"[{summary}] : 푸시 생성 = {saved}";
|
|
var logSaved = await _repositoryService.SaveData<LogPush>(log);
|
|
_logger.LogInformation($"[{summary}]: 푸시 생성 = {saved} : 로그 저장 = {logSaved}");
|
|
if (!saved) return APIResponse.Send<object>("001", $"[{summary}], 실패", string.Empty);
|
|
return APIResponse.Send<object>("000", $"[{summary}], 정상", string.Empty);
|
|
|
|
|
|
}
|
|
|
|
public async Task<APIResponseStatus<object>> DeletePush(string summary, string token, string bid, string pid)
|
|
{
|
|
string uid = String.Empty;
|
|
if (token == "System") uid = "System";
|
|
else
|
|
{
|
|
var validToken = await _jwtTokenService.ValidateToken(token);
|
|
if (validToken == null) return APIResponse.AccessExpireError();
|
|
uid = validToken.FindFirst(ClaimTypes.NameIdentifier)?.Value ?? string.Empty;
|
|
}
|
|
|
|
var payload = await _pushRepository.FindPushPayload(bid, pid);
|
|
if (payload == null) return APIResponse.Send<object>("001", $"[{summary}], 삭제 할 PUSH 없음", string.Empty);
|
|
|
|
var log = new LogPush
|
|
{
|
|
bid = payload.bid,
|
|
pid = payload.pid,
|
|
create_date = DateTime.Now,
|
|
create_uid = uid
|
|
};
|
|
var delete = await _repositoryService.DeleteData<DBPayload>(payload);
|
|
|
|
log.log = $"[{summary}] : 삭제 = {delete}";
|
|
var logSaved = await _repositoryService.SaveData<LogPush>(log);
|
|
_logger.LogInformation($"[{summary}]: 삭제 = {delete} : 로그 저장 = {logSaved}");
|
|
if (!delete) return APIResponse.Send<object>("002", $"[{summary}], 실패", string.Empty);
|
|
return APIResponse.Send<object>("000", $"[{summary}], 정상", string.Empty);
|
|
}
|
|
|
|
public async Task<APIResponseStatus<object>> DeleteListPush(string summary, string token, int id)
|
|
{
|
|
string uid = String.Empty;
|
|
if (token == "System") uid = "System";
|
|
else
|
|
{
|
|
var validToken = await _jwtTokenService.ValidateToken(token);
|
|
if (validToken == null) return APIResponse.AccessExpireError();
|
|
uid = validToken.FindFirst(ClaimTypes.NameIdentifier)?.Value ?? string.Empty;
|
|
}
|
|
|
|
var cabinet = await _pushRepository.FindPushCabinet(id);
|
|
if (cabinet == null) return APIResponse.Send<object>("001", $"[{summary}], 삭제 할 PUSH 없음", string.Empty);
|
|
|
|
|
|
var log = new LogPush
|
|
{
|
|
bid = cabinet.bid,
|
|
pid = cabinet.pid,
|
|
create_date = DateTime.Now,
|
|
create_uid = uid
|
|
};
|
|
|
|
var delete = await _repositoryService.DeleteData<PushCabinet>(cabinet);
|
|
log.log = $"[{summary}] : {cabinet.pid} 삭제 = {delete}";
|
|
var logSaved = await _repositoryService.SaveData<LogPush>(log);
|
|
_logger.LogInformation($"[{summary}]: {cabinet.pid} 삭제 = {delete} : 로그 저장 = {logSaved}");
|
|
if (!delete) return APIResponse.Send<object>("002", $"[{summary}], 실패", string.Empty);
|
|
return APIResponse.Send<object>("000", $"[{summary}], 정상", string.Empty);
|
|
}
|
|
|
|
|
|
public async Task<APIResponseStatus<object>> SearchToUserPush(string summary, string token, int size, PushCabinet? request)
|
|
{
|
|
string uid = String.Empty;
|
|
if (token == "System") uid = "System";
|
|
else
|
|
{
|
|
var validToken = await _jwtTokenService.ValidateToken(token);
|
|
if (validToken == null) return APIResponse.AccessExpireError();
|
|
uid = validToken.FindFirst(ClaimTypes.NameIdentifier)?.Value ?? string.Empty;
|
|
}
|
|
|
|
return APIResponse.Send<object>("000", $"[{summary}], 정상", string.Empty);
|
|
}
|
|
}
|
|
|
|
} |