forked from AcaMate/AcaMate_API
[♻️] 전체적인 구조 리팩토링 진행중
This commit is contained in:
parent
d7a3703e29
commit
b0606a44bb
|
@ -18,5 +18,9 @@
|
||||||
<PackageReference Include="Swashbuckle.AspNetCore.Annotations" Version="7.1.0" />
|
<PackageReference Include="Swashbuckle.AspNetCore.Annotations" Version="7.1.0" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<Folder Include="Program\Controllers\V1\Interfaces\" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
|
||||||
</Project>
|
</Project>
|
||||||
|
|
36
Program.cs
36
Program.cs
|
@ -1,6 +1,5 @@
|
||||||
using Pomelo.EntityFrameworkCore;
|
using Pomelo.EntityFrameworkCore;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using AcaMate.Common.Chat;
|
|
||||||
using Microsoft.EntityFrameworkCore;
|
using Microsoft.EntityFrameworkCore;
|
||||||
using Microsoft.AspNetCore.Authentication.JwtBearer;
|
using Microsoft.AspNetCore.Authentication.JwtBearer;
|
||||||
using Microsoft.IdentityModel.Tokens;
|
using Microsoft.IdentityModel.Tokens;
|
||||||
|
@ -10,13 +9,18 @@ using Microsoft.Extensions.DependencyInjection;
|
||||||
using System.Net.Http;
|
using System.Net.Http;
|
||||||
using System.Security.Cryptography.X509Certificates;
|
using System.Security.Cryptography.X509Certificates;
|
||||||
using System.Text.Json;
|
using System.Text.Json;
|
||||||
|
using Back;
|
||||||
using AcaMate.Common.Models;
|
using Back.Program.Common.Auth;
|
||||||
using AcaMate.V1.Services;
|
using Back.Program.Common.Auth.Interface;
|
||||||
using AcaMate.Common.Data;
|
using Back.Program.Common.Chat;
|
||||||
using AcaMate.Common.Token;
|
using Back.Program.Common.Data;
|
||||||
using AcaMate.V1.Controllers;
|
using Back.Program.Common.Middleware;
|
||||||
using AcaMate.V1.Models;
|
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;
|
||||||
|
using Back.Program.Services.V1.Interfaces;
|
||||||
|
|
||||||
|
|
||||||
var builder = WebApplication.CreateBuilder(args);
|
var builder = WebApplication.CreateBuilder(args);
|
||||||
|
@ -24,6 +28,8 @@ var builder = WebApplication.CreateBuilder(args);
|
||||||
|
|
||||||
// DB 설정부 시작
|
// DB 설정부 시작
|
||||||
builder.Configuration.AddJsonFile("private/dbSetting.json", optional: true, reloadOnChange: true);
|
builder.Configuration.AddJsonFile("private/dbSetting.json", optional: true, reloadOnChange: true);
|
||||||
|
|
||||||
|
builder.Services.AddHttpContextAccessor();
|
||||||
// var connectionString = builder.Configuration.GetConnectionString("MariaDbConnection");
|
// var connectionString = builder.Configuration.GetConnectionString("MariaDbConnection");
|
||||||
// builder.Services.AddDbContext<AppDbContext>(options => options.UseMySql(connectionString, ServerVersion.AutoDetect(connectionString)));
|
// builder.Services.AddDbContext<AppDbContext>(options => options.UseMySql(connectionString, ServerVersion.AutoDetect(connectionString)));
|
||||||
builder.Services.AddDbContext<AppDbContext>(optionsAction: (serviceProvider, options) =>
|
builder.Services.AddDbContext<AppDbContext>(optionsAction: (serviceProvider, options) =>
|
||||||
|
@ -38,7 +44,7 @@ builder.Services.AddDbContext<AppDbContext>(optionsAction: (serviceProvider, opt
|
||||||
|
|
||||||
options.UseMySql(baseConnectionString, ServerVersion.AutoDetect(baseConnectionString));
|
options.UseMySql(baseConnectionString, ServerVersion.AutoDetect(baseConnectionString));
|
||||||
});
|
});
|
||||||
builder.Services.AddHttpContextAccessor();
|
|
||||||
// DB 설정부 끝
|
// DB 설정부 끝
|
||||||
|
|
||||||
|
|
||||||
|
@ -113,9 +119,12 @@ builder.Services.AddHostedService<PushBackgroundService>();
|
||||||
builder.Services.AddControllers();
|
builder.Services.AddControllers();
|
||||||
|
|
||||||
// 여기다가 API 있는 컨트롤러들 AddScoped 하면 되는건가?
|
// 여기다가 API 있는 컨트롤러들 AddScoped 하면 되는건가?
|
||||||
builder.Services.AddScoped<AcaMate.Common.Token.JwtTokenService>();
|
builder.Services.AddScoped<JwtTokenService>();
|
||||||
builder.Services.AddScoped<IRepositoryService, AcaMate.V1.Services.RepositoryService>();
|
builder.Services.AddScoped<ILogRepository, LogRepository>();
|
||||||
|
builder.Services.AddScoped<IRepositoryService, RepositoryService>();
|
||||||
builder.Services.AddScoped<IHeaderConfig, HeaderConfigRepository>();
|
builder.Services.AddScoped<IHeaderConfig, HeaderConfigRepository>();
|
||||||
|
builder.Services.AddScoped<IUserService, UserService>();
|
||||||
|
builder.Services.AddScoped<IUserRepository, UserRepository>();
|
||||||
// builder.Services.AddScoped<UserService>(); //
|
// builder.Services.AddScoped<UserService>(); //
|
||||||
// builder.Services.AddScoped<UserController>();
|
// builder.Services.AddScoped<UserController>();
|
||||||
|
|
||||||
|
@ -151,10 +160,6 @@ else
|
||||||
builder.Logging.SetMinimumLevel(LogLevel.Warning);
|
builder.Logging.SetMinimumLevel(LogLevel.Warning);
|
||||||
}
|
}
|
||||||
|
|
||||||
//헤더 부분
|
|
||||||
builder.Services.AddScoped<IHeaderConfig, HeaderConfigRepository>();
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// 로컬 테스트 위한 부분 (올릴때는 꺼두기)
|
// 로컬 테스트 위한 부분 (올릴때는 꺼두기)
|
||||||
// builder.WebHost.UseUrls("http://0.0.0.0:5144");
|
// builder.WebHost.UseUrls("http://0.0.0.0:5144");
|
||||||
|
@ -181,6 +186,7 @@ app.UseHttpsRedirection();
|
||||||
|
|
||||||
// 헤더 미들웨어 부분
|
// 헤더 미들웨어 부분
|
||||||
app.UseMiddleware<APIHeaderMiddleware>((object)new string[] { "iOS_AM_Connect_Key", "And_AM_Connect_Key", "Web_AM_Connect_Key" });
|
app.UseMiddleware<APIHeaderMiddleware>((object)new string[] { "iOS_AM_Connect_Key", "And_AM_Connect_Key", "Web_AM_Connect_Key" });
|
||||||
|
// app.UseMiddleware<ExceptionMiddleware>();
|
||||||
// app.UseMiddleware<CustomHeaderMiddleware>("X-MyHeader");
|
// app.UseMiddleware<CustomHeaderMiddleware>("X-MyHeader");
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -1,38 +1,12 @@
|
||||||
using System.Threading.Tasks;
|
using Back.Program.Common.Auth.Interface;
|
||||||
using AcaMate.Common.Data;
|
using Back.Program.Common.Data;
|
||||||
using Microsoft.AspNetCore.Http;
|
|
||||||
using Microsoft.EntityFrameworkCore;
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
|
||||||
namespace AcaMate.Common.Token;
|
namespace Back.Program.Common.Auth
|
||||||
|
|
||||||
public interface IHeaderConfig
|
|
||||||
{
|
{
|
||||||
Task<string> GetExpectedHeaderValueAsync(string headerName);
|
///
|
||||||
}
|
public class APIHeaderMiddleware
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// DB에서 헤더 키값 찾아서 그 밸류 값 빼오기 위해서 사용
|
|
||||||
/// </summary>
|
|
||||||
public class HeaderConfigRepository : IHeaderConfig
|
|
||||||
{
|
|
||||||
private readonly AppDbContext _dbContext;
|
|
||||||
|
|
||||||
public HeaderConfigRepository(AppDbContext dbContext)
|
|
||||||
{
|
{
|
||||||
_dbContext = dbContext;
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task<string> GetExpectedHeaderValueAsync(string headerValue)
|
|
||||||
{
|
|
||||||
var config = await _dbContext.APIHeader
|
|
||||||
.FirstOrDefaultAsync(h => h.h_value == headerValue);
|
|
||||||
return config?.h_key ?? string.Empty;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
public class APIHeaderMiddleware
|
|
||||||
{
|
|
||||||
|
|
||||||
private readonly RequestDelegate _next;
|
private readonly RequestDelegate _next;
|
||||||
private readonly string[] _headerNames;
|
private readonly string[] _headerNames;
|
||||||
|
@ -46,7 +20,6 @@ public class APIHeaderMiddleware
|
||||||
|
|
||||||
public async Task Invoke(HttpContext context)
|
public async Task Invoke(HttpContext context)
|
||||||
{
|
{
|
||||||
|
|
||||||
if (context.Request.Path.Equals("/api/v1/in/app", StringComparison.OrdinalIgnoreCase))
|
if (context.Request.Path.Equals("/api/v1/in/app", StringComparison.OrdinalIgnoreCase))
|
||||||
{
|
{
|
||||||
await _next(context);
|
await _next(context);
|
||||||
|
@ -83,4 +56,5 @@ public class APIHeaderMiddleware
|
||||||
|
|
||||||
await _next(context);
|
await _next(context);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
27
Program/Common/Auth/HeaderConfigRepository.cs
Normal file
27
Program/Common/Auth/HeaderConfigRepository.cs
Normal file
|
@ -0,0 +1,27 @@
|
||||||
|
using Back.Program.Common.Auth.Interface;
|
||||||
|
using Back.Program.Common.Data;
|
||||||
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
|
||||||
|
namespace Back.Program.Common.Auth
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// DB에서 헤더 키값 찾아서 그 밸류 값 빼오기 위해서 사용
|
||||||
|
/// </summary>
|
||||||
|
public class HeaderConfigRepository : IHeaderConfig
|
||||||
|
{
|
||||||
|
private readonly AppDbContext _dbContext;
|
||||||
|
|
||||||
|
public HeaderConfigRepository(AppDbContext dbContext)
|
||||||
|
{
|
||||||
|
_dbContext = dbContext;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<string> GetExpectedHeaderValueAsync(string headerValue)
|
||||||
|
{
|
||||||
|
var config = await _dbContext.APIHeader
|
||||||
|
.FirstOrDefaultAsync(h => h.h_value == headerValue);
|
||||||
|
return config?.h_key ?? string.Empty;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
7
Program/Common/Auth/Interface/IHeaderConfig.cs
Normal file
7
Program/Common/Auth/Interface/IHeaderConfig.cs
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
namespace Back.Program.Common.Auth.Interface
|
||||||
|
{
|
||||||
|
public interface IHeaderConfig
|
||||||
|
{
|
||||||
|
Task<string> GetExpectedHeaderValueAsync(string headerName);
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,21 +1,18 @@
|
||||||
using System;
|
|
||||||
using System.IdentityModel.Tokens.Jwt;
|
using System.IdentityModel.Tokens.Jwt;
|
||||||
using System.Security.Claims;
|
using System.Security.Claims;
|
||||||
using System.Text;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using AcaMate.Common.Models;
|
|
||||||
using Microsoft.IdentityModel.Tokens;
|
|
||||||
using Microsoft.Extensions.Options;
|
|
||||||
using Microsoft.AspNetCore.Authentication;
|
|
||||||
using Microsoft.AspNetCore.Mvc;
|
|
||||||
|
|
||||||
|
|
||||||
using System.Security.Cryptography;
|
using System.Security.Cryptography;
|
||||||
|
using System.Text;
|
||||||
|
using Back.Program.Common.Model;
|
||||||
|
using Microsoft.Extensions.Options;
|
||||||
|
using Microsoft.IdentityModel.Tokens;
|
||||||
|
|
||||||
namespace AcaMate.Common.Token;
|
namespace Back.Program.Common.Auth
|
||||||
|
|
||||||
public class JwtTokenService
|
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 사용자의 정보를 바탕으로 JWT 생성
|
||||||
|
/// </summary>
|
||||||
|
public class JwtTokenService
|
||||||
|
{
|
||||||
private readonly JwtSettings _jwtSettings;
|
private readonly JwtSettings _jwtSettings;
|
||||||
private readonly ILogger<JwtTokenService> _logger;
|
private readonly ILogger<JwtTokenService> _logger;
|
||||||
|
|
||||||
|
@ -106,4 +103,5 @@ public class JwtTokenService
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
|
@ -1,10 +1,9 @@
|
||||||
using Microsoft.AspNetCore.SignalR;
|
using Microsoft.AspNetCore.SignalR;
|
||||||
using System.Threading.Tasks;
|
|
||||||
|
|
||||||
namespace AcaMate.Common.Chat;
|
namespace Back.Program.Common.Chat
|
||||||
|
|
||||||
public class ChatHub : Hub
|
|
||||||
{
|
{
|
||||||
|
public class ChatHub : Hub
|
||||||
|
{
|
||||||
// 클라이언트에서 메시지를 보내면 모든 사용자에게 전송
|
// 클라이언트에서 메시지를 보내면 모든 사용자에게 전송
|
||||||
public async Task SendMessage(string user, string message)
|
public async Task SendMessage(string user, string message)
|
||||||
{
|
{
|
||||||
|
@ -34,4 +33,10 @@ public class ChatHub : Hub
|
||||||
Console.WriteLine("OnDisconnectedAsync");
|
Console.WriteLine("OnDisconnectedAsync");
|
||||||
await base.OnDisconnectedAsync(exception);
|
await base.OnDisconnectedAsync(exception);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
|
||||||
|
|
||||||
|
*/
|
|
@ -1,12 +1,13 @@
|
||||||
using AcaMate.Common.Models;
|
using Back.Program.Common.Model;
|
||||||
|
using Back.Program.Models.Entities;
|
||||||
using Microsoft.EntityFrameworkCore;
|
using Microsoft.EntityFrameworkCore;
|
||||||
using AcaMate.V1.Models;
|
using Version = Back.Program.Models.Entities.Version;
|
||||||
using Version = AcaMate.V1.Models.Version;
|
|
||||||
|
|
||||||
namespace AcaMate.Common.Data;
|
namespace Back.Program.Common.Data
|
||||||
//database=AcaMate;
|
|
||||||
public class AppDbContext: DbContext
|
|
||||||
{
|
{
|
||||||
|
//database=AcaMate;
|
||||||
|
public class AppDbContext: DbContext
|
||||||
|
{
|
||||||
public AppDbContext(DbContextOptions<AppDbContext> options) : base(options)
|
public AppDbContext(DbContextOptions<AppDbContext> options) : base(options)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
@ -17,7 +18,7 @@ public class AppDbContext: DbContext
|
||||||
//MARK: Program
|
//MARK: Program
|
||||||
public DbSet<Version> Version { get; set; }
|
public DbSet<Version> Version { get; set; }
|
||||||
public DbSet<Academy> Academy { get; set; }
|
public DbSet<Academy> Academy { get; set; }
|
||||||
public DbSet<RefreshToken> RefreshTokens { get; set; }
|
public DbSet<RefreshToken> RefreshToken { get; set; }
|
||||||
|
|
||||||
//MARK: USER
|
//MARK: USER
|
||||||
public DbSet<Login> Login { get; set; }
|
public DbSet<Login> Login { get; set; }
|
||||||
|
@ -33,6 +34,8 @@ public class AppDbContext: DbContext
|
||||||
public DbSet<PushCabinet> PushCabinet { get; set; }
|
public DbSet<PushCabinet> PushCabinet { get; set; }
|
||||||
|
|
||||||
|
|
||||||
|
//MARK: CHATTING
|
||||||
|
// public DbSet<>
|
||||||
|
|
||||||
|
|
||||||
//MARK: LOG
|
//MARK: LOG
|
||||||
|
@ -53,4 +56,5 @@ public class AppDbContext: DbContext
|
||||||
|
|
||||||
// modelBuilder.Entity<LogPush>().HasNoKey();
|
// modelBuilder.Entity<LogPush>().HasNoKey();
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
50
Program/Common/Middleware/ExceptionMiddleware.cs
Normal file
50
Program/Common/Middleware/ExceptionMiddleware.cs
Normal file
|
@ -0,0 +1,50 @@
|
||||||
|
using System.Text.Json;
|
||||||
|
using Microsoft.AspNetCore.Http;
|
||||||
|
using Microsoft.Extensions.Logging;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using Back.Program.Common.Model;
|
||||||
|
|
||||||
|
namespace Back.Program.Common.Middleware
|
||||||
|
{
|
||||||
|
public class ExceptionMiddleware
|
||||||
|
{
|
||||||
|
private readonly RequestDelegate _next;
|
||||||
|
private readonly ILogger<ExceptionMiddleware> _logger;
|
||||||
|
|
||||||
|
public ExceptionMiddleware(RequestDelegate next, ILogger<ExceptionMiddleware> logger)
|
||||||
|
{
|
||||||
|
_next = next;
|
||||||
|
_logger = logger;
|
||||||
|
}
|
||||||
|
|
||||||
|
// public async Task Invoke(HttpContext context)
|
||||||
|
// {
|
||||||
|
// try
|
||||||
|
// {
|
||||||
|
// await _next(context); // 다음 미들웨어 호출
|
||||||
|
// }
|
||||||
|
// catch (AcaException ex)
|
||||||
|
// {
|
||||||
|
// _logger.LogWarning(ex, "AcaException 발생");
|
||||||
|
// context.Response.StatusCode = 400;
|
||||||
|
//
|
||||||
|
// var response = APIResponse.Send<string>(ex.Code, ex.Message, "");
|
||||||
|
// var json = JsonSerializer.Serialize(response);
|
||||||
|
//
|
||||||
|
// context.Response.ContentType = "application/json; charset=utf-8";
|
||||||
|
// await context.Response.WriteAsync(json);
|
||||||
|
// }
|
||||||
|
// catch (Exception ex)
|
||||||
|
// {
|
||||||
|
// _logger.LogError(ex, "Unhandled Exception");
|
||||||
|
// context.Response.StatusCode = 500;
|
||||||
|
//
|
||||||
|
// var response = APIResponse.InternalSeverError("서버 내부 오류가 발생했습니다.");
|
||||||
|
// var json = JsonSerializer.Serialize(response);
|
||||||
|
//
|
||||||
|
// context.Response.ContentType = "application/json; charset=utf-8";
|
||||||
|
// await context.Response.WriteAsync(json);
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,17 +1,18 @@
|
||||||
using System.ComponentModel.DataAnnotations.Schema;
|
|
||||||
using System.ComponentModel.DataAnnotations;
|
using System.ComponentModel.DataAnnotations;
|
||||||
|
using System.ComponentModel.DataAnnotations.Schema;
|
||||||
|
|
||||||
namespace AcaMate.Common.Models;
|
namespace Back.Program.Common.Model
|
||||||
|
|
||||||
[Table("api_header")]
|
|
||||||
public class APIHeader
|
|
||||||
{
|
{
|
||||||
|
[Table("api_header")]
|
||||||
|
public class APIHeader
|
||||||
|
{
|
||||||
[Key]
|
[Key]
|
||||||
public string specific_id { get; set; }
|
public string specific_id { get; set; }
|
||||||
|
|
||||||
public DateTime connect_date { get; set; }
|
public DateTime connect_date { get; set; }
|
||||||
public string h_key { get; set; }
|
public string h_key { get; set; }
|
||||||
public string h_value { get; set; }
|
public string h_value { get; set; }
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|
74
Program/Common/Model/AcaException.cs
Normal file
74
Program/Common/Model/AcaException.cs
Normal file
|
@ -0,0 +1,74 @@
|
||||||
|
namespace Back.Program.Common.Model
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 입력 받은 토큰들(Access & Refresh) 자체에 문제가 있는 경우
|
||||||
|
/// </summary>
|
||||||
|
public class TokenException: Exception
|
||||||
|
{
|
||||||
|
public TokenException(string message) : base(message)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 리프레시 토큰이 만료가 나있는 경우
|
||||||
|
/// </summary>
|
||||||
|
public class RefreshRevokeException: Exception
|
||||||
|
{
|
||||||
|
public RefreshRevokeException(string message) : base(message)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 참조해야 하는 파일에서 오류가 발생하는 경우
|
||||||
|
/// </summary>
|
||||||
|
public class FileNotValidException : Exception
|
||||||
|
{
|
||||||
|
public FileNotValidException(string message) : base(message)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 파일 내부에 값을 읽을 때 오류가 발생하는 경우
|
||||||
|
/// </summary>
|
||||||
|
public class FileContentNotFoundException : Exception
|
||||||
|
{
|
||||||
|
public FileContentNotFoundException(string message) : base(message)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 외부 서비스에 연결시 연결 실패시
|
||||||
|
/// </summary>
|
||||||
|
public class ServiceConnectionFailedException : Exception
|
||||||
|
{
|
||||||
|
public ServiceConnectionFailedException(string message) : base(message)
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// PUSH 서비스 중 데이터 사용에 문제가 발생했을시
|
||||||
|
/// </summary>
|
||||||
|
public class PushInvalidException : Exception
|
||||||
|
{
|
||||||
|
public PushInvalidException(string message) : base(message)
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/// <summary>
|
||||||
|
/// 값이 있어야 하는데 NULL인 경우
|
||||||
|
/// </summary>
|
||||||
|
public class OutNULLException : Exception
|
||||||
|
{
|
||||||
|
public OutNULLException(string message) : base(message)
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,22 +1,22 @@
|
||||||
using System.ComponentModel.DataAnnotations.Schema;
|
|
||||||
using System.ComponentModel.DataAnnotations;
|
using System.ComponentModel.DataAnnotations;
|
||||||
|
using System.ComponentModel.DataAnnotations.Schema;
|
||||||
|
|
||||||
namespace AcaMate.Common.Models;
|
namespace Back.Program.Common.Model
|
||||||
|
|
||||||
public class JwtSettings
|
|
||||||
{
|
{
|
||||||
|
public class JwtSettings
|
||||||
|
{
|
||||||
public string SecretKey { get; set; }
|
public string SecretKey { get; set; }
|
||||||
public string Issuer { get; set; }
|
public string Issuer { get; set; }
|
||||||
public string Audience { get; set; }
|
public string Audience { get; set; }
|
||||||
public int ExpiryMinutes { get; set; }
|
public int ExpiryMinutes { get; set; }
|
||||||
public int ClockSkewMinutes { get; set; }
|
public int ClockSkewMinutes { get; set; }
|
||||||
public int RefreshTokenExpiryDays { get; set; }
|
public int RefreshTokenExpiryDays { get; set; }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
[Table("refresh_token")]
|
[Table("refresh_token")]
|
||||||
public class RefreshToken
|
public class RefreshToken
|
||||||
{
|
{
|
||||||
[Key]
|
[Key]
|
||||||
[Required(ErrorMessage = "필수 항목 누락")]
|
[Required(ErrorMessage = "필수 항목 누락")]
|
||||||
public string uid { get; set; }
|
public string uid { get; set; }
|
||||||
|
@ -26,14 +26,16 @@ public class RefreshToken
|
||||||
|
|
||||||
// 이건 로그아웃시에 폐기 시킬예정이니 그떄 변경하는걸로 합시다.
|
// 이건 로그아웃시에 폐기 시킬예정이니 그떄 변경하는걸로 합시다.
|
||||||
public DateTime? revoke_Date { get; set; }
|
public DateTime? revoke_Date { get; set; }
|
||||||
}
|
}
|
||||||
|
|
||||||
public class ValidateToken
|
public class ValidateToken
|
||||||
{
|
{
|
||||||
public string token { get; set; }
|
public string token { get; set; }
|
||||||
public string refresh { get; set; }
|
public string refresh { get; set; }
|
||||||
public string uid { get; set; }
|
public string uid { get; set; }
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
"""
|
"""
|
||||||
토큰 동작 관련
|
토큰 동작 관련
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
using System.Text.Json;
|
using System.Text.Json;
|
||||||
|
|
||||||
namespace AcaMate.Common.Models;
|
namespace Back.Program.Common.Model
|
||||||
|
|
||||||
public class APIResponseStatus<T>
|
|
||||||
{
|
{
|
||||||
|
public class APIResponseStatus<T>
|
||||||
|
{
|
||||||
public Status status { get; set; }
|
public Status status { get; set; }
|
||||||
public T? data { get; set; }
|
public T? data { get; set; }
|
||||||
|
|
||||||
|
@ -11,17 +11,17 @@ public class APIResponseStatus<T>
|
||||||
{
|
{
|
||||||
return JsonSerializer.Serialize(this);
|
return JsonSerializer.Serialize(this);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public class Status
|
public class Status
|
||||||
{
|
{
|
||||||
public string code { get; set; }
|
public string code { get; set; }
|
||||||
public string message { get; set; }
|
public string message { get; set; }
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static class APIResponse
|
public static class APIResponse
|
||||||
{
|
{
|
||||||
public static APIResponseStatus<T> Send<T>(string code, string message, T data)
|
public static APIResponseStatus<T> Send<T>(string code, string message, T data)
|
||||||
{
|
{
|
||||||
return new APIResponseStatus<T>
|
return new APIResponseStatus<T>
|
||||||
|
@ -38,36 +38,37 @@ public static class APIResponse
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 반환값 없는 API 정상 동작시
|
/// 반환값 없는 API 정상 동작시
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public static APIResponseStatus<string> Success (){
|
public static APIResponseStatus<object> Success (){
|
||||||
return Send("000", "정상", "");
|
return Send<object>("000", "정상", string.Empty);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static APIResponseStatus<string> InvalidInputError(string? msg = null)
|
public static APIResponseStatus<object> InvalidInputError(string? msg = null)
|
||||||
{
|
{
|
||||||
return Send("100", msg ?? "입력 값이 유효하지 않습니다.", "");
|
return Send<object>("100", msg ?? "입력 값이 유효하지 않습니다.", string.Empty);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static APIResponseStatus<string> AccessExpireError(string? msg = null)
|
public static APIResponseStatus<object> AccessExpireError(string? msg = null)
|
||||||
{
|
{
|
||||||
return Send("101", msg ?? "엑세스 토큰이 유효하지 않습니다.", "");
|
return Send<object>("101", msg ?? "엑세스 토큰이 유효하지 않습니다.", string.Empty);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// -- -- -- OUTPUT ERROR -- -- -- //
|
// -- -- -- OUTPUT ERROR -- -- -- //
|
||||||
public static APIResponseStatus<string> NotFoundError(string? msg = null)
|
public static APIResponseStatus<object> NotFoundError(string? msg = null)
|
||||||
{
|
{
|
||||||
return Send("200", msg ?? "알맞은 값을 찾을 수 없습니다.", "");
|
return Send<object>("200", msg ?? "알맞은 값을 찾을 수 없습니다.", string.Empty);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
public static APIResponseStatus<string> InternalSeverError(string? msg = null)
|
public static APIResponseStatus<object> InternalSeverError(string? msg = null)
|
||||||
{
|
{
|
||||||
return Send("300", msg ?? "통신에 오류가 발생하였습니다.", "");
|
return Send<object>("300", msg ?? "통신에 오류가 발생하였습니다.", string.Empty);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static APIResponseStatus<string> UnknownError(string? msg = null)
|
public static APIResponseStatus<object> UnknownError(string? msg = null)
|
||||||
{
|
{
|
||||||
return Send("999", msg ?? "알 수 없는 오류가 발생하였습니다.", "");
|
return Send<object>("999", msg ?? "알 수 없는 오류가 발생하였습니다.", string.Empty);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
270
Program/Controllers/V1/AppController.cs
Normal file
270
Program/Controllers/V1/AppController.cs
Normal file
|
@ -0,0 +1,270 @@
|
||||||
|
using System.Security.Cryptography;
|
||||||
|
using System.Text;
|
||||||
|
using System.Text.Json;
|
||||||
|
using Back.Program.Common.Auth;
|
||||||
|
using Back.Program.Common.Data;
|
||||||
|
using Back.Program.Common.Model;
|
||||||
|
using Back.Program.Models.Entities;
|
||||||
|
using Back.Program.Services.V1;
|
||||||
|
using Microsoft.AspNetCore.Mvc;
|
||||||
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
using Version = Back.Program.Models.Entities.Version;
|
||||||
|
|
||||||
|
namespace Back.Program.Controllers.V1
|
||||||
|
{
|
||||||
|
[ApiController]
|
||||||
|
[Route("/api/v1/in/app")]
|
||||||
|
[ApiExplorerSettings(GroupName = "공통")]
|
||||||
|
public class AppController : ControllerBase
|
||||||
|
{
|
||||||
|
private readonly AppDbContext _dbContext;
|
||||||
|
private readonly ILogger<AppController> _logger;
|
||||||
|
private readonly IRepositoryService _repositoryService;
|
||||||
|
private readonly JwtTokenService _jwtTokenService;
|
||||||
|
|
||||||
|
public AppController(AppDbContext dbContext, ILogger<AppController> logger, IRepositoryService repositoryService, JwtTokenService jwtTokenService)
|
||||||
|
{
|
||||||
|
_dbContext = dbContext;
|
||||||
|
_logger = logger;
|
||||||
|
_repositoryService = repositoryService;
|
||||||
|
_jwtTokenService = jwtTokenService;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// 이 키값의 제한 시간은 24h이다
|
||||||
|
[HttpGet]
|
||||||
|
[CustomOperation("헤더 정보 생성", "헤더에 접근하기 위한 키 값 받아오기", "시스템")]
|
||||||
|
public async Task<IActionResult> GetHeaderValue(string type, string specific, string project)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrEmpty(specific) || string.IsNullOrEmpty(type) || string.IsNullOrEmpty(project))
|
||||||
|
return BadRequest(APIResponse.InvalidInputError());
|
||||||
|
if (!ModelState.IsValid) return BadRequest(APIResponse.InvalidInputError());
|
||||||
|
string summary = String.Empty;
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
summary = _repositoryService.ReadSummary(typeof(AppController), "GetHeaderValue");
|
||||||
|
bool valid = false;
|
||||||
|
|
||||||
|
switch (type)
|
||||||
|
{
|
||||||
|
case "I":
|
||||||
|
if (project == "me.myds.ipstein.acamate.AcaMate") valid = true;
|
||||||
|
break;
|
||||||
|
case "A":
|
||||||
|
break;
|
||||||
|
case "W":
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
return BadRequest(APIResponse.InvalidInputError($"[{summary}], 타입 에러"));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (valid)
|
||||||
|
{
|
||||||
|
|
||||||
|
var apiHeader = await _dbContext.APIHeader.FirstOrDefaultAsync(h => h.specific_id == specific);
|
||||||
|
|
||||||
|
string nowTime = DateTime.Now.ToString("o");
|
||||||
|
string combineText = $"{project}_{nowTime}_{specific}";
|
||||||
|
string headerValue = KeyGenerator(combineText);
|
||||||
|
|
||||||
|
if (apiHeader != null)
|
||||||
|
{
|
||||||
|
if (DateTime.Now - apiHeader.connect_date > TimeSpan.FromHours(24))
|
||||||
|
{
|
||||||
|
_logger.LogInformation($"[{summary}] : 해당 키 유효기간 경과");
|
||||||
|
apiHeader.h_value = headerValue;
|
||||||
|
apiHeader.connect_date = DateTime.Now;
|
||||||
|
|
||||||
|
if (await _repositoryService.SaveData<APIHeader>(apiHeader))
|
||||||
|
{
|
||||||
|
string msg = "정상 - 로그 저장 실패";
|
||||||
|
var logProject = new LogProject
|
||||||
|
{
|
||||||
|
create_date = DateTime.Now ,
|
||||||
|
log = $"[{summary}] : 해당 키 유효시간 만료로 인한 새 키 부여"
|
||||||
|
};
|
||||||
|
if (await _repositoryService.SaveData<LogProject>(logProject))
|
||||||
|
msg = "정상";
|
||||||
|
return Ok(APIResponse.Send("001", msg, new { header = headerValue }));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// 저장이 안된거니 서버 오류
|
||||||
|
return StatusCode(500, APIResponse.InternalSeverError());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// 유효기간 만료 이상 없이 다 잘 됨
|
||||||
|
return Ok(APIResponse.Send("000", "정상", new { header = apiHeader.h_value }));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_logger.LogInformation($"[{summary}] : 저장 된게 없음");
|
||||||
|
|
||||||
|
var newHeader = new APIHeader
|
||||||
|
{
|
||||||
|
h_key = type == "I" ? "iOS_AM_Connect_Key"
|
||||||
|
: (type == "A" ? "And_AM_Connect_Key"
|
||||||
|
: (type == "W" ? "Web_AM_Connect_Key": throw new Exception("ERROR"))),
|
||||||
|
h_value = headerValue,
|
||||||
|
connect_date = DateTime.Now,
|
||||||
|
specific_id = specific
|
||||||
|
};
|
||||||
|
|
||||||
|
if (await _repositoryService.SaveData<APIHeader>(newHeader))
|
||||||
|
{
|
||||||
|
string msg = "정상 - 로그 저장 실패";
|
||||||
|
var logProject = new LogProject
|
||||||
|
{
|
||||||
|
create_date = DateTime.Now ,
|
||||||
|
log = $"[{summary}] : 새로운 등록으로 인한 새 키 부여"
|
||||||
|
};
|
||||||
|
// 이거 로그 저장 안되는거 확인!
|
||||||
|
_logger.LogInformation($"[{summary}] : {logProject.log}");
|
||||||
|
if (await _repositoryService.SaveData<LogProject>(logProject))
|
||||||
|
msg = "정상";
|
||||||
|
|
||||||
|
return Ok(APIResponse.Send("001", msg, new { header = headerValue }));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// 저장이 안된거니 서버 오류
|
||||||
|
return StatusCode(500, APIResponse.InternalSeverError());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return BadRequest(APIResponse.InvalidInputError());
|
||||||
|
|
||||||
|
|
||||||
|
// return Ok(APIResponse.Send("000", "정상", Empty));
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
_logger.LogError($"[{summary}] : {ex.Message}");
|
||||||
|
return StatusCode(500, APIResponse.UnknownError(ex.Message));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 스웨거는 퍼블릭으로 선언된걸 죄다 API로 인식하는 경향이 있음
|
||||||
|
// 방법은 private 같이 접근 제한자를 변경하거나 [NonAction]을 붙여주면 됨
|
||||||
|
[NonAction]
|
||||||
|
private string KeyGenerator(string combineText)
|
||||||
|
{
|
||||||
|
using (SHA256 sha256 = SHA256.Create())
|
||||||
|
{
|
||||||
|
byte[] hashBytes = sha256.ComputeHash(Encoding.UTF8.GetBytes(combineText));
|
||||||
|
return BitConverter.ToString(hashBytes).Replace("-", string.Empty).ToLowerInvariant();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[HttpGet("version")]
|
||||||
|
[CustomOperation("앱 버전 확인","앱 버전을 확인해서 업데이트 여부 판단", "시스템")]
|
||||||
|
public async Task<IActionResult> GetVersionData(string type)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrEmpty(type))
|
||||||
|
{
|
||||||
|
return BadRequest(APIResponse.InvalidInputError());
|
||||||
|
}
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var version = await _dbContext.Version.FirstOrDefaultAsync(v => v.os_type == (type == "I" ? "VO01" : "VO02"));
|
||||||
|
|
||||||
|
if (version == null)
|
||||||
|
{
|
||||||
|
return NotFound(APIResponse.NotFoundError());
|
||||||
|
}
|
||||||
|
|
||||||
|
var response = new APIResponseStatus<Version>
|
||||||
|
{
|
||||||
|
status = new Status()
|
||||||
|
{
|
||||||
|
code = "000",
|
||||||
|
message = "정상"
|
||||||
|
},
|
||||||
|
data = new Version()
|
||||||
|
{
|
||||||
|
os_type = (version.os_type == "VO01" ? "I" : (version.os_type == "VO02" ? "A" : "W")),
|
||||||
|
final_ver = version.final_ver,
|
||||||
|
force_ver = version.force_ver,
|
||||||
|
dev_ver = version.dev_ver,
|
||||||
|
choice_update_yn = version.choice_update_yn
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
string jsonString = JsonSerializer.Serialize(response);
|
||||||
|
|
||||||
|
// return Ok(jsonString);
|
||||||
|
return Ok(response.JsonToString());
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Console.WriteLine($"{ex.Message}\n{ex.StackTrace}");
|
||||||
|
return StatusCode(500, APIResponse.UnknownError());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
[HttpGet("auth")]
|
||||||
|
[CustomOperation("서버 접근 권한 확인", "서버 기능을 사용하기 위한 접근에 대해 권한 확인", "시스템")]
|
||||||
|
public async Task<IActionResult> AuthProgram([FromBody] AuthKey keys)
|
||||||
|
{
|
||||||
|
string summary = String.Empty;
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
summary = _repositoryService.ReadSummary(typeof(AppController), "AuthProgram");
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
_logger.LogError($"[{summary}] : {ex.Message}");
|
||||||
|
return StatusCode(500, APIResponse.UnknownError(ex.Message));
|
||||||
|
}
|
||||||
|
|
||||||
|
return Ok(APIResponse.Send("000", "OK", Empty));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
[HttpGet("retryAccess")]
|
||||||
|
[CustomOperation("엑세스 토큰 재발급", "액세스 토큰 재발급 동작 수행", "시스템")]
|
||||||
|
public async Task<IActionResult> RetryAccessToken(string refresh)
|
||||||
|
{
|
||||||
|
string summary = String.Empty;
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
summary = _repositoryService.ReadSummary(typeof(AppController), "AuthProgram");
|
||||||
|
var refreshToken = await _dbContext.RefreshToken
|
||||||
|
.FirstOrDefaultAsync(t => t.refresh_token == refresh);
|
||||||
|
if (refreshToken == null) throw new TokenException($"[{summary}] : 리프레시 토큰의 문제");
|
||||||
|
if (refreshToken.revoke_Date < DateTime.Now) throw new TokenException($"[{summary}] : 리프레시 토큰 만료");
|
||||||
|
if (refreshToken.expire_date < DateTime.Now) throw new TokenException($"[{summary}] : 리프레시 토큰 폐기");
|
||||||
|
string access = _jwtTokenService.GenerateJwtToken(refreshToken.uid);
|
||||||
|
return Ok(APIResponse.Send("000", $"[{summary}], 토큰 생성 완료",
|
||||||
|
new {
|
||||||
|
access = access
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
catch (TokenException ex)
|
||||||
|
{
|
||||||
|
_logger.LogError($"[{summary}] : {ex.Message}");
|
||||||
|
return Ok(APIResponse.InvalidInputError(ex.Message));
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
_logger.LogError($"[{summary}] : {ex.Message}");
|
||||||
|
return StatusCode(500, APIResponse.UnknownError(ex.Message));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
15
Program/Controllers/V1/ErrorController.cs
Normal file
15
Program/Controllers/V1/ErrorController.cs
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
using Microsoft.AspNetCore.Mvc;
|
||||||
|
|
||||||
|
namespace Back.Program.Controllers.V1
|
||||||
|
{
|
||||||
|
[ApiController]
|
||||||
|
[Route("/api/error")]
|
||||||
|
public class ErrorController: ControllerBase
|
||||||
|
{
|
||||||
|
[HttpGet]
|
||||||
|
public IActionResult HandleError()
|
||||||
|
{
|
||||||
|
return Problem("오류가 발생하였습니다. 잠시후 다시 시도해주세요.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
47
Program/Controllers/V1/MemberController.cs
Normal file
47
Program/Controllers/V1/MemberController.cs
Normal file
|
@ -0,0 +1,47 @@
|
||||||
|
using Back.Program.Common.Auth;
|
||||||
|
using Back.Program.Common.Data;
|
||||||
|
using Back.Program.Services.V1;
|
||||||
|
using Microsoft.AspNetCore.Mvc;
|
||||||
|
|
||||||
|
namespace Back.Program.Controllers.V1
|
||||||
|
{
|
||||||
|
[ApiController]
|
||||||
|
[Route("/api/v1/in/member")]
|
||||||
|
[ApiExplorerSettings(GroupName = "사업자 정보")]
|
||||||
|
public class MemberController: ControllerBase
|
||||||
|
{
|
||||||
|
private readonly ILogger<MemberController> _logger;
|
||||||
|
private readonly AppDbContext _dbContext;
|
||||||
|
private readonly IRepositoryService _repositoryService;
|
||||||
|
private readonly JwtTokenService _jwtTokenService;
|
||||||
|
|
||||||
|
public MemberController(AppDbContext dbContext, ILogger<MemberController> logger, IRepositoryService repositoryService, JwtTokenService jwtTokenService)
|
||||||
|
{
|
||||||
|
_dbContext = dbContext;
|
||||||
|
_logger = logger;
|
||||||
|
_repositoryService = repositoryService;
|
||||||
|
_jwtTokenService = jwtTokenService;
|
||||||
|
}
|
||||||
|
|
||||||
|
[HttpGet("business")]
|
||||||
|
public IActionResult GetBusinessData()
|
||||||
|
{
|
||||||
|
// return Ok("GOOD");
|
||||||
|
return Ok("DB 참조");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// -- -- -- -- -- -- -- -- -- -- -- -- //
|
||||||
|
|
||||||
|
[HttpGet("/api/v1/out/member/business")]
|
||||||
|
public IActionResult SearchBusinessNo()
|
||||||
|
{
|
||||||
|
return Ok("외부 참조");
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
475
Program/Controllers/V1/PushController.cs
Normal file
475
Program/Controllers/V1/PushController.cs
Normal file
|
@ -0,0 +1,475 @@
|
||||||
|
using System.Security.Claims;
|
||||||
|
using Back.Program.Common.Auth;
|
||||||
|
using Back.Program.Common.Data;
|
||||||
|
using Back.Program.Common.Model;
|
||||||
|
using Back.Program.Models.Entities;
|
||||||
|
using Back.Program.Services.V1;
|
||||||
|
using Microsoft.AspNetCore.Mvc;
|
||||||
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
|
||||||
|
namespace Back.Program.Controllers.V1
|
||||||
|
{
|
||||||
|
[ApiController]
|
||||||
|
[Route("/api/v1/in/push")]
|
||||||
|
[ApiExplorerSettings(GroupName = "공통")]
|
||||||
|
public class PushController : ControllerBase
|
||||||
|
{
|
||||||
|
private readonly ILogger<PushController> _logger;
|
||||||
|
private readonly IPushQueue _pushQueue;
|
||||||
|
private readonly AppDbContext _dbContext;
|
||||||
|
private readonly IRepositoryService _repositoryService;
|
||||||
|
private readonly JwtTokenService _jwtTokenService;
|
||||||
|
public PushController(ILogger<PushController> logger, IPushQueue pushQueue, AppDbContext dbContext, IRepositoryService repositoryService, JwtTokenService jwtTokenService)
|
||||||
|
{
|
||||||
|
_logger = logger;
|
||||||
|
_pushQueue = pushQueue;
|
||||||
|
_dbContext = dbContext;
|
||||||
|
_repositoryService = repositoryService;
|
||||||
|
_jwtTokenService = jwtTokenService;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 추가 사항
|
||||||
|
// 카테고리 별 조회 하는 부분도 추가를 할 지 고민을 해야 할 것 같음
|
||||||
|
|
||||||
|
[HttpGet()]
|
||||||
|
[CustomOperation("푸시 확인", "저장된 양식을 확인 할 수 있다.", "푸시")]
|
||||||
|
[ProducesResponseType(StatusCodes.Status200OK, Type = typeof(APIResponseStatus<object>))]
|
||||||
|
public async Task<IActionResult> GetPush(string bid, string? pid, string? category)
|
||||||
|
{
|
||||||
|
string summary = String.Empty;
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
summary = _repositoryService.ReadSummary(typeof(PushController), "GetPush");
|
||||||
|
|
||||||
|
if (!(await _dbContext.Academy.AnyAsync(a=>a.bid == bid)))
|
||||||
|
return Ok(APIResponse.Send("100", $"[{summary}], 존재하지 않는 BID", Empty));
|
||||||
|
|
||||||
|
List<DBPayload> pushData = new List<DBPayload>();
|
||||||
|
var pushQuery = _dbContext.DBPayload.Where(p => p.bid == bid);
|
||||||
|
if (pid != null)
|
||||||
|
pushQuery = pushQuery.Where(p=>p.pid == pid);
|
||||||
|
if (category != null)
|
||||||
|
pushQuery = pushQuery.Where(p=>p.category == category);
|
||||||
|
pushData = await pushQuery.ToListAsync();
|
||||||
|
|
||||||
|
if (pushData.Count > 0)
|
||||||
|
{
|
||||||
|
return Ok(APIResponse.Send("000", $"[{summary}, 정상", pushData));
|
||||||
|
}
|
||||||
|
|
||||||
|
return Ok(APIResponse.Send("001", $"[{summary}], PUSH 데이터 없음", Empty));
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
_logger.LogError($"[{summary}] : {ex.Message}");
|
||||||
|
return StatusCode(500, APIResponse.UnknownError());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
[HttpPost("send")]
|
||||||
|
[CustomOperation("푸시 발송", "저장된 양식으로, 사용자에게 푸시를 송신한다.", "푸시")]
|
||||||
|
[ProducesResponseType(StatusCodes.Status200OK, Type = typeof(APIResponseStatus<object>))]
|
||||||
|
public async Task<IActionResult> 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 () =>
|
||||||
|
{
|
||||||
|
if (payload == null)
|
||||||
|
throw new PushInvalidException("payload is NULL");
|
||||||
|
|
||||||
|
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();
|
||||||
|
payload.aps.badge = badge + 1;
|
||||||
|
|
||||||
|
// 푸시를 보내야 하니 푸시 토큰 확인
|
||||||
|
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 = payload.content != "" ? payload.content : null,
|
||||||
|
};
|
||||||
|
|
||||||
|
var pushData = new PushData
|
||||||
|
{
|
||||||
|
pushToken = pushToken,
|
||||||
|
payload = payload
|
||||||
|
};
|
||||||
|
|
||||||
|
if (await _repositoryService.SaveData<PushCabinet>(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>(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>(logPush))
|
||||||
|
_logger.LogInformation($"[{summary}] : 로그 추가");
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return Ok(APIResponse.Send("000", $"[{summary}], 정상", Empty));
|
||||||
|
}
|
||||||
|
catch (PushInvalidException ex)
|
||||||
|
{
|
||||||
|
_logger.LogError($"[{summary}] : {ex.Message}");
|
||||||
|
return Ok(APIResponse.Send("001", $"[{summary}], 푸시 송신 중 문제 발생",Empty));
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
_logger.LogError($"[{summary}] : {ex.Message}");
|
||||||
|
return StatusCode(500, APIResponse.UnknownError(ex.Message));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[HttpPost("set")]
|
||||||
|
[CustomOperation("푸시 변경", "저장된 양식을 변경한다.", "푸시")]
|
||||||
|
[ProducesResponseType(StatusCodes.Status200OK, Type = typeof(APIResponseStatus<object>))]
|
||||||
|
public async Task<IActionResult> SetPush(string token, [FromBody] DBPayload request)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrEmpty(token)) return BadRequest(APIResponse.InvalidInputError());
|
||||||
|
if (!ModelState.IsValid) return BadRequest(APIResponse.InvalidInputError());
|
||||||
|
string summary = String.Empty;
|
||||||
|
string uid = String.Empty;
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (token == "System") uid = "System";
|
||||||
|
else
|
||||||
|
{
|
||||||
|
var validateToken = await _jwtTokenService.ValidateToken(token);
|
||||||
|
if (validateToken == null) return Ok(APIResponse.AccessExpireError());
|
||||||
|
uid = validateToken.FindFirst(ClaimTypes.NameIdentifier)?.Value ?? string.Empty;
|
||||||
|
}
|
||||||
|
|
||||||
|
summary = _repositoryService.ReadSummary(typeof(PushController), "SetPush");
|
||||||
|
var dbPayload = await _dbContext.DBPayload
|
||||||
|
.FirstOrDefaultAsync(p => p.pid == request.pid && p.bid == request.bid);
|
||||||
|
|
||||||
|
|
||||||
|
if (dbPayload != null)
|
||||||
|
{
|
||||||
|
var logPush = new LogPush
|
||||||
|
{
|
||||||
|
bid = dbPayload.bid,
|
||||||
|
pid = dbPayload.pid,
|
||||||
|
create_uid = uid,
|
||||||
|
create_date = DateTime.Now,
|
||||||
|
};
|
||||||
|
|
||||||
|
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>(dbPayload))
|
||||||
|
{
|
||||||
|
logPush.log = $"[{summary} : 정상 변경";
|
||||||
|
return Ok(APIResponse.Send("000", $"[{summary}], 정상", Empty));
|
||||||
|
}
|
||||||
|
|
||||||
|
// 로그를 이제 만들어서 추가를 해야 합니다.
|
||||||
|
if (await _repositoryService.SaveData<LogPush>(logPush))
|
||||||
|
_logger.LogInformation($"[{summary}] : 로그 추가");
|
||||||
|
}
|
||||||
|
|
||||||
|
return Ok(APIResponse.Send("100", $"[{summary}], PID, BID 또는 Cabinet 오류", Empty));
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
_logger.LogError($"[{summary}] : {ex.Message}");
|
||||||
|
return StatusCode(500, APIResponse.UnknownError(ex.Message));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[HttpPost("create")]
|
||||||
|
[CustomOperation("푸시 생성", "새로운 푸시 양식을 생성한다.", "푸시")]
|
||||||
|
[ProducesResponseType(StatusCodes.Status200OK, Type = typeof(APIResponseStatus<object>))]
|
||||||
|
public async Task<IActionResult> CreatePush(string token, [FromBody] CreatePush request)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrEmpty(token)) return BadRequest(APIResponse.InvalidInputError());
|
||||||
|
if (!ModelState.IsValid) return BadRequest(APIResponse.InvalidInputError());
|
||||||
|
string summary = String.Empty;
|
||||||
|
string uid = "";
|
||||||
|
|
||||||
|
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)}";
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (token == "System") uid = "System";
|
||||||
|
else {
|
||||||
|
var validateToken = await _jwtTokenService.ValidateToken(token);
|
||||||
|
if (validateToken == null) return Ok(APIResponse.AccessExpireError());
|
||||||
|
uid = validateToken.FindFirst(ClaimTypes.NameIdentifier)?.Value ?? string.Empty;
|
||||||
|
}
|
||||||
|
|
||||||
|
summary = _repositoryService.ReadSummary(typeof(PushController), "CreatePush");
|
||||||
|
|
||||||
|
if (await _dbContext.Academy.AnyAsync(a => a.bid == request.bid))
|
||||||
|
{
|
||||||
|
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,
|
||||||
|
};
|
||||||
|
|
||||||
|
if (await _repositoryService.SaveData<DBPayload>(payload))
|
||||||
|
{
|
||||||
|
var logPush = new LogPush
|
||||||
|
{
|
||||||
|
bid = payload.bid,
|
||||||
|
pid = payload.pid,
|
||||||
|
create_uid = uid,
|
||||||
|
create_date = DateTime.Now,
|
||||||
|
log = $"[{summary}] : 정상 생성"
|
||||||
|
};
|
||||||
|
|
||||||
|
// 로그를 이제 만들어서 추가를 해야 합니다.
|
||||||
|
if (await _repositoryService.SaveData<LogPush>(logPush))
|
||||||
|
_logger.LogInformation($"[{summary}] : 로그 추가");
|
||||||
|
|
||||||
|
return Ok(APIResponse.Send("000", $"[{summary}], 정상", Empty));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
return Ok(APIResponse.Send("100", $"[{summary}], 학원 정보(BID) 확인 불가", Empty));
|
||||||
|
}
|
||||||
|
catch (TokenException tokenEx)
|
||||||
|
{
|
||||||
|
_logger.LogInformation($"[{summary}] : {tokenEx}");
|
||||||
|
return Ok(APIResponse.Send("001", $"[{summary}], 토큰에 문제가 있음",Empty));
|
||||||
|
}
|
||||||
|
catch (RefreshRevokeException refreshEx)
|
||||||
|
{
|
||||||
|
_logger.LogInformation($"[{summary}] : {refreshEx}");
|
||||||
|
return Ok(APIResponse.Send("001", $"[{summary}], 폐기된 리프레시 토큰",Empty));
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
_logger.LogError($"[{summary}] : {ex.Message}");
|
||||||
|
return StatusCode(500, APIResponse.UnknownError());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
[HttpDelete("delete")]
|
||||||
|
[CustomOperation("푸시 삭제", "저장된 푸시 양식을 삭제 한다.", "푸시")]
|
||||||
|
[ProducesResponseType(StatusCodes.Status200OK, Type = typeof(APIResponseStatus<object>))]
|
||||||
|
public async Task<IActionResult> DeletePush(string token, string bid, string pid)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrEmpty(token)) return BadRequest(APIResponse.InvalidInputError());
|
||||||
|
if (!ModelState.IsValid) return BadRequest(APIResponse.InvalidInputError());
|
||||||
|
string uid = "";
|
||||||
|
string summary = String.Empty;
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (token == "System") uid = "System";
|
||||||
|
else {
|
||||||
|
var validateToken = await _jwtTokenService.ValidateToken(token);
|
||||||
|
if (validateToken == null) return Ok(APIResponse.AccessExpireError());
|
||||||
|
uid = validateToken.FindFirst(ClaimTypes.NameIdentifier)?.Value ?? string.Empty;
|
||||||
|
}
|
||||||
|
|
||||||
|
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<DBPayload>(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>(logPush)) _logger.LogInformation($"[{summary}] : 로그 추가");
|
||||||
|
|
||||||
|
return Ok(APIResponse.Send("000", $"[{summary}], 정상", Empty));
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
_logger.LogError($"[{summary}] : {ex.Message}");
|
||||||
|
return BadRequest(APIResponse.UnknownError(ex.Message));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
[HttpDelete("delete/list")]
|
||||||
|
[CustomOperation("사용자 푸시 목록 삭제", "사용자가 받은 푸시목록에서 푸시를 삭제한다..", "푸시")]
|
||||||
|
[ProducesResponseType(StatusCodes.Status200OK, Type = typeof(APIResponseStatus<object>))]
|
||||||
|
public async Task<IActionResult> DeleteListPush(string token, int id)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrEmpty(token)) return BadRequest(APIResponse.InvalidInputError());
|
||||||
|
if (!ModelState.IsValid) return BadRequest(APIResponse.InvalidInputError());
|
||||||
|
string uid = "";
|
||||||
|
string summary = String.Empty;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (token == "System") uid = "System";
|
||||||
|
else {
|
||||||
|
var validateToken = await _jwtTokenService.ValidateToken(token);
|
||||||
|
if (validateToken == null) return Ok(APIResponse.AccessExpireError());
|
||||||
|
uid = validateToken.FindFirst(ClaimTypes.NameIdentifier)?.Value ?? string.Empty;
|
||||||
|
}
|
||||||
|
|
||||||
|
summary = _repositoryService.ReadSummary(typeof(PushController), "DeleteListPush");
|
||||||
|
var cabinetPush = await _dbContext.PushCabinet.FirstOrDefaultAsync(c => c.id == id);
|
||||||
|
if (cabinetPush == null) return Ok(APIResponse.Send("001", $"[{summary}], 삭제 할 PUSH 없음", Empty));
|
||||||
|
if (!await _repositoryService.DeleteData<PushCabinet>(cabinetPush))
|
||||||
|
return Ok(APIResponse.Send("002", $"[{summary}], PUSH 삭제 실패", Empty));
|
||||||
|
|
||||||
|
// // 로그를 이제 만들어서 추가를 해야 합니다.
|
||||||
|
var logPush = new LogPush
|
||||||
|
{
|
||||||
|
bid = cabinetPush.bid,
|
||||||
|
pid = cabinetPush.pid,
|
||||||
|
create_uid = uid,
|
||||||
|
create_date = DateTime.Now,
|
||||||
|
log = $"[{summary}] : {cabinetPush.pid} 삭제 - {uid}"
|
||||||
|
};
|
||||||
|
if (await _repositoryService.SaveData<LogPush>(logPush)) _logger.LogInformation($"[{summary}] : 로그 추가");
|
||||||
|
|
||||||
|
return Ok(APIResponse.Send("000", $"[{summary}], 정상", Empty));
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
_logger.LogError($"[{summary}] : {ex.Message}");
|
||||||
|
return BadRequest(APIResponse.UnknownError(ex.Message));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
[HttpPost("list")]
|
||||||
|
[CustomOperation("사용자 푸시 목록 조회", "해당 사용자가 받은 푸시의 정보를 조회한다.", "푸시")]
|
||||||
|
[ProducesResponseType(StatusCodes.Status200OK, Type = typeof(APIResponseStatus<object>))]
|
||||||
|
public async Task<IActionResult> SearchToUserPush(string token, int size, [FromBody] PushCabinet? request)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrEmpty(token)) return BadRequest(APIResponse.InvalidInputError());
|
||||||
|
if (!ModelState.IsValid) return BadRequest(APIResponse.InvalidInputError());
|
||||||
|
string uid = "";
|
||||||
|
string summary = String.Empty;
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (token == "System") uid = "System";
|
||||||
|
else {
|
||||||
|
var validateToken = await _jwtTokenService.ValidateToken(token);
|
||||||
|
if (validateToken == null) return Ok(APIResponse.AccessExpireError());
|
||||||
|
uid = validateToken.FindFirst(ClaimTypes.NameIdentifier)?.Value ?? string.Empty;
|
||||||
|
}
|
||||||
|
|
||||||
|
summary = _repositoryService.ReadSummary(typeof(PushController), "SearchToUserPush");
|
||||||
|
if (request == null)
|
||||||
|
{
|
||||||
|
var pagedData = await _dbContext.PushCabinet.Where(c => c.uid == uid)
|
||||||
|
.OrderBy(c=> c.send_date)
|
||||||
|
.Take(size)
|
||||||
|
.ToListAsync();
|
||||||
|
|
||||||
|
return Ok(APIResponse.Send("000", $"[{summary}], 정상", pagedData));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
var sort = await _dbContext.PushCabinet.Where(p=> p.id == request.id)
|
||||||
|
.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(size).ToListAsync();
|
||||||
|
return Ok(APIResponse.Send("000", $"[{summary}], 정상", pagedData));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
_logger.LogError($"[{summary}] : {ex.Message}");
|
||||||
|
return BadRequest(APIResponse.UnknownError(ex.Message));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
} // END PUSH CONTROLLER
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
178
Program/Controllers/V1/UserController.cs
Normal file
178
Program/Controllers/V1/UserController.cs
Normal file
|
@ -0,0 +1,178 @@
|
||||||
|
using System.Security.Claims;
|
||||||
|
using Back.Program.Common.Auth;
|
||||||
|
using Back.Program.Common.Data;
|
||||||
|
using Back.Program.Common.Model;
|
||||||
|
using Back.Program.Models.Entities;
|
||||||
|
using Back.Program.Services.V1.Interfaces;
|
||||||
|
using Back.Program.Services.V1;
|
||||||
|
using Microsoft.AspNetCore.Mvc;
|
||||||
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
|
||||||
|
namespace Back.Program.Controllers.V1
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// USER는 사용자가 자신의 데이터를 보거나 만들거나 하는 등 직접 사용하는 경우에 사용
|
||||||
|
/// </summary>
|
||||||
|
[ApiController]
|
||||||
|
[Route("/api/v1/in/user")]
|
||||||
|
[ApiExplorerSettings(GroupName = "사용자")]
|
||||||
|
public class UserController : ControllerBase
|
||||||
|
{
|
||||||
|
private readonly AppDbContext _dbContext;
|
||||||
|
private readonly ILogger<UserController> _logger;
|
||||||
|
private readonly JwtTokenService _jwtTokenService;
|
||||||
|
private readonly IRepositoryService _repositoryService;
|
||||||
|
private readonly IUserService _userService;
|
||||||
|
|
||||||
|
public UserController(AppDbContext dbContext, ILogger<UserController> logger, JwtTokenService jwtTokenService,
|
||||||
|
IRepositoryService repositoryService, IUserService userService)
|
||||||
|
{
|
||||||
|
_dbContext = dbContext;
|
||||||
|
_logger = logger;
|
||||||
|
_jwtTokenService = jwtTokenService;
|
||||||
|
_repositoryService = repositoryService;
|
||||||
|
_userService = userService;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
_logger.LogInformation($"[{summary}] : {ex.Message}");
|
||||||
|
return BadRequest(APIResponse.UnknownError(ex.Message));
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
[HttpGet]
|
||||||
|
[CustomOperation("회원 정보 조회", "회원 정보 조회 (자기자신)", "사용자")]
|
||||||
|
public async Task<IActionResult> GetUserData(string token)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrEmpty(token)) return BadRequest(APIResponse.InvalidInputError());
|
||||||
|
if (!ModelState.IsValid) return BadRequest(APIResponse.InvalidInputError());
|
||||||
|
string summary = _repositoryService.ReadSummary(typeof(UserController), "GetUserData");
|
||||||
|
|
||||||
|
var result = _userService.GetUser(summary, token);
|
||||||
|
return Ok(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
[HttpGet("login")]
|
||||||
|
[CustomOperation("SNS 로그인", "로그인 후 회원이 있는지 확인", "사용자")]
|
||||||
|
public async Task<IActionResult> Login(string accType, string snsId)
|
||||||
|
{
|
||||||
|
// API 동작 파라미터 입력 값 확인
|
||||||
|
if (string.IsNullOrEmpty(accType) && string.IsNullOrEmpty(snsId)) return BadRequest(APIResponse.InvalidInputError());
|
||||||
|
if (!ModelState.IsValid) return BadRequest(APIResponse.InvalidInputError());
|
||||||
|
|
||||||
|
string summary = _repositoryService.ReadSummary(typeof(UserController), "Login");
|
||||||
|
var result = await _userService.Login(summary, accType, snsId);
|
||||||
|
return Ok(result);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
[HttpPost("register")]
|
||||||
|
[CustomOperation("회원 가입", "사용자 회원 가입", "사용자")]
|
||||||
|
public async Task<IActionResult> UserRegister([FromBody] UserAll request)
|
||||||
|
{
|
||||||
|
if (!ModelState.IsValid) return BadRequest(APIResponse.InvalidInputError());
|
||||||
|
string summary = _repositoryService.ReadSummary(typeof(UserController), "UserRegister");
|
||||||
|
|
||||||
|
var result = _userService.Register(summary, request);
|
||||||
|
return Ok(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
[HttpGet("logout")]
|
||||||
|
[CustomOperation("로그아웃", "사용자 로그아웃", "사용자")]
|
||||||
|
public async Task<IActionResult> Logout(string token)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrEmpty(token)) return BadRequest(APIResponse.InvalidInputError());
|
||||||
|
if (!ModelState.IsValid) return BadRequest(APIResponse.InvalidInputError());
|
||||||
|
string summary = _repositoryService.ReadSummary(typeof(UserController), "Logout");
|
||||||
|
|
||||||
|
var result = await _userService.Logout(summary, token);
|
||||||
|
return Ok(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
[HttpGet("cancel")]
|
||||||
|
[CustomOperation("회원 탈퇴", "사용자 탈퇴", "사용자")]
|
||||||
|
public async Task<IActionResult> Cancel(string token)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrEmpty(token)) return BadRequest(APIResponse.InvalidInputError());
|
||||||
|
if (!ModelState.IsValid) return BadRequest(APIResponse.InvalidInputError());
|
||||||
|
string summary = _repositoryService.ReadSummary(typeof(UserController), "Cancel");
|
||||||
|
var result = await _userService.Cancel(summary, token);
|
||||||
|
return Ok(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
[HttpGet("academy")]
|
||||||
|
[CustomOperation("학원 리스트 확인", "사용자가 등록된 학원 리스트 확인", "사용자")]
|
||||||
|
public async Task<IActionResult> GetAcademyData(string token)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrEmpty(token)) return BadRequest(APIResponse.InvalidInputError());
|
||||||
|
if (!ModelState.IsValid) return BadRequest(APIResponse.InvalidInputError());
|
||||||
|
string summary = _repositoryService.ReadSummary(typeof(UserController), "ReadAcademyInfo");
|
||||||
|
|
||||||
|
var result = _userService.GetAcademy(summary, token);
|
||||||
|
return Ok(result);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// 근데 회원 정보를 변경하는게 뭐뭐를 변경해야 하는지 아직 정해진게 없어서 이건 일단 보류
|
||||||
|
/*
|
||||||
|
[HttpGet("set")]
|
||||||
|
[CustomOperation("회원 정보 변경", "회원 정보 변경", "사용자")]
|
||||||
|
public async Task<IActionResult> SetUserData(string token, string refresh) //, [FromBody])
|
||||||
|
{
|
||||||
|
if (string.IsNullOrEmpty(token) || string.IsNullOrEmpty(refresh))
|
||||||
|
return BadRequest(APIResponse.InvalidInputError());
|
||||||
|
if (!ModelState.IsValid) return BadRequest(APIResponse.InvalidInputError());
|
||||||
|
string summary = String.Empty;
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
summary = _repositoryService.ReadSummary(typeof(UserController), "Cancel");
|
||||||
|
|
||||||
|
|
||||||
|
// 여기서 애초에 토큰 관련 에러가 2개가 나오게 만들어져 있음
|
||||||
|
var validateToken = await _repositoryService.ValidateToken(token, refresh);
|
||||||
|
var user = await _dbContext.User.FirstOrDefaultAsync(u => u.uid == validateToken.uid);
|
||||||
|
|
||||||
|
}
|
||||||
|
catch (TokenException tokenEx)
|
||||||
|
{
|
||||||
|
return Ok(APIResponse.Send("101", $"[{summary}], 입력 받은 토큰의 문제", Empty));
|
||||||
|
}
|
||||||
|
catch (RefreshRevokeException refreshEx)
|
||||||
|
{
|
||||||
|
return Ok(APIResponse.Send("102", $"[{summary}], 폐기된 리프레시 토큰", Empty));
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
return StatusCode(500, APIResponse.UnknownError($"[{summary}], {ex.Message}"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
string uid = "";
|
||||||
|
if (token == "System") uid = "System";
|
||||||
|
else {
|
||||||
|
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);
|
||||||
|
uid = validateToken.uid;
|
||||||
|
}
|
||||||
|
|
||||||
|
string summary = String.Empty;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
summary = _repositoryService.ReadSummary(typeof(PushController), "GetUserData");
|
||||||
|
}
|
||||||
|
*/
|
16
Program/Models/Entities/APIResult.cs
Normal file
16
Program/Models/Entities/APIResult.cs
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
using System.Text.Json;
|
||||||
|
|
||||||
|
namespace Back.Program.Models.Entities
|
||||||
|
{
|
||||||
|
public class APIResult
|
||||||
|
{
|
||||||
|
public bool Success { get; set; }
|
||||||
|
public string Code { get; set; }
|
||||||
|
public string Message { get; set; }
|
||||||
|
|
||||||
|
public string JsonToString()
|
||||||
|
{
|
||||||
|
return JsonSerializer.Serialize(this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
32
Program/Models/Entities/Academy.cs
Normal file
32
Program/Models/Entities/Academy.cs
Normal file
|
@ -0,0 +1,32 @@
|
||||||
|
using System.ComponentModel.DataAnnotations;
|
||||||
|
using System.ComponentModel.DataAnnotations.Schema;
|
||||||
|
|
||||||
|
namespace Back.Program.Models.Entities
|
||||||
|
{
|
||||||
|
[Table("academy")]
|
||||||
|
public class Academy
|
||||||
|
{
|
||||||
|
[Key]
|
||||||
|
public string bid { get; set; }
|
||||||
|
public string business_name { get; set; }
|
||||||
|
public string business_owner { get; set; }
|
||||||
|
public string business_number { get; set; }
|
||||||
|
public DateTime business_date { get; set; }
|
||||||
|
public string business_address { get; set; }
|
||||||
|
public string business_contact { get; set; }
|
||||||
|
public string uid { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
// -- -- -- -- -- DB 테이블 -- -- -- -- -- //
|
||||||
|
|
||||||
|
public class AcademyName
|
||||||
|
{
|
||||||
|
public string bid { get; set; }
|
||||||
|
public string name { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public class RequestAcademy
|
||||||
|
{
|
||||||
|
public List<string> bids { get; set; }
|
||||||
|
}
|
||||||
|
}
|
10
Program/Models/Entities/AuthKey.cs
Normal file
10
Program/Models/Entities/AuthKey.cs
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
using System.ComponentModel.DataAnnotations.Schema;
|
||||||
|
|
||||||
|
namespace Back.Program.Models.Entities
|
||||||
|
{
|
||||||
|
[Table("authkey")]
|
||||||
|
public class AuthKey
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
147
Program/Models/Entities/Chatting.cs
Normal file
147
Program/Models/Entities/Chatting.cs
Normal file
|
@ -0,0 +1,147 @@
|
||||||
|
using System.ComponentModel.DataAnnotations;
|
||||||
|
using System.ComponentModel.DataAnnotations.Schema;
|
||||||
|
|
||||||
|
namespace Back.Program.Models.Entities
|
||||||
|
{
|
||||||
|
[Table("chat_room")]
|
||||||
|
public class Chat_Room
|
||||||
|
{
|
||||||
|
[Required(ErrorMessage = "필수 항목 누락")]
|
||||||
|
[MaxLength(16)]
|
||||||
|
public required string cid { get; set; } // bid + yyyyMMdd + count
|
||||||
|
|
||||||
|
[Required(ErrorMessage = "필수 항목 누락")]
|
||||||
|
[MaxLength(6)]
|
||||||
|
public required string bid { get; set; }
|
||||||
|
|
||||||
|
[Required(ErrorMessage = "필수 항목 누락")]
|
||||||
|
public required string name { get; set; }
|
||||||
|
|
||||||
|
[Required(ErrorMessage = "필수 항목 누락")]
|
||||||
|
[MaxLength(4)]
|
||||||
|
public required string type { get; set; }
|
||||||
|
|
||||||
|
[Required(ErrorMessage = "필수 항목 누락")]
|
||||||
|
public required DateTime create_date { get; set; }
|
||||||
|
|
||||||
|
[Required(ErrorMessage = "필수 항목 누락")]
|
||||||
|
public required DateTime open_date { get; set; }
|
||||||
|
|
||||||
|
public DateTime? close_date { get; set; }
|
||||||
|
|
||||||
|
public Chat_Room(string cid, string bid, string name, string type, DateTime create_date, DateTime open_date)
|
||||||
|
{
|
||||||
|
this.cid = cid;
|
||||||
|
this.bid = bid;
|
||||||
|
this.name = name;
|
||||||
|
this.type = type;
|
||||||
|
this.create_date = create_date;
|
||||||
|
this.open_date = open_date;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[Table("chat_join")]
|
||||||
|
public class Chat_Join
|
||||||
|
{
|
||||||
|
[Required(ErrorMessage = "필수 항목 누락")]
|
||||||
|
[MaxLength(16)]
|
||||||
|
public required string cid { get; set; }
|
||||||
|
|
||||||
|
[Required(ErrorMessage = "필수 항목 누락")]
|
||||||
|
[MaxLength(70)]
|
||||||
|
public required string uid { get; set; }
|
||||||
|
|
||||||
|
public DateTime? join_date { get; set; }
|
||||||
|
public string? mid { get; set; }
|
||||||
|
|
||||||
|
[Required(ErrorMessage = "필수 항목 누락")]
|
||||||
|
public required bool is_notice { get; set; }
|
||||||
|
|
||||||
|
public Chat_Join(string cid, string uid, bool is_notice)
|
||||||
|
{
|
||||||
|
this.cid = cid;
|
||||||
|
this.uid = uid;
|
||||||
|
this.is_notice = is_notice;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[Table("chat_message")]
|
||||||
|
public class Chat_Message
|
||||||
|
{
|
||||||
|
|
||||||
|
[Required(ErrorMessage = "필수 항목 누락")]
|
||||||
|
[MaxLength(36)]
|
||||||
|
public required string mid { get; set; } // UUID
|
||||||
|
|
||||||
|
[Required(ErrorMessage = "필수 항목 누락")]
|
||||||
|
[MaxLength(16)]
|
||||||
|
public required string cid { get; set; }
|
||||||
|
|
||||||
|
[Required(ErrorMessage = "필수 항목 누락")]
|
||||||
|
[MaxLength(70)]
|
||||||
|
public required string uid { get; set; }
|
||||||
|
|
||||||
|
[Required(ErrorMessage = "필수 항목 누락")]
|
||||||
|
public required DateTime create_date { get; set; }
|
||||||
|
[Required(ErrorMessage = "필수 항목 누락")]
|
||||||
|
public required string content { get; set; }
|
||||||
|
[Required(ErrorMessage = "필수 항목 누락")]
|
||||||
|
public required bool is_hidden { get; set; }
|
||||||
|
|
||||||
|
public string? media_url { get; set; }
|
||||||
|
|
||||||
|
[Required(ErrorMessage = "필수 항목 누락")]
|
||||||
|
public required int read_count { get; set; }
|
||||||
|
|
||||||
|
[Required(ErrorMessage = "필수 항목 누락")]
|
||||||
|
public required bool is_blind { get; set; }
|
||||||
|
|
||||||
|
[Required(ErrorMessage = "필수 항목 누락")]
|
||||||
|
public required bool check_report { get; set; }
|
||||||
|
|
||||||
|
public Chat_Message(string mid, string cid, string uid, DateTime create_date, string content, bool is_hidden,
|
||||||
|
int read_count, bool is_blind, bool check_report)
|
||||||
|
{
|
||||||
|
this.mid = mid;
|
||||||
|
this.cid = cid;
|
||||||
|
this.uid = uid;
|
||||||
|
this.create_date = create_date;
|
||||||
|
this.content = content;
|
||||||
|
this.is_hidden = is_hidden;
|
||||||
|
this.read_count = read_count;
|
||||||
|
this.check_report = check_report;
|
||||||
|
this.is_blind = is_blind;
|
||||||
|
this.check_report = check_report;
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
[Table("chat_report_list")]
|
||||||
|
public class Chat_Report_List
|
||||||
|
{
|
||||||
|
|
||||||
|
[Required(ErrorMessage = "필수 항목 누락")]
|
||||||
|
[MaxLength(36)]
|
||||||
|
public required string mid { get; set; } // UUID
|
||||||
|
|
||||||
|
[Required(ErrorMessage = "필수 항목 누락")]
|
||||||
|
[MaxLength(16)]
|
||||||
|
public required string cid { get; set; }
|
||||||
|
|
||||||
|
[Required(ErrorMessage = "필수 항목 누락")]
|
||||||
|
[MaxLength(70)]
|
||||||
|
public required string uid { get; set; }
|
||||||
|
|
||||||
|
[Required(ErrorMessage = "필수 항목 누락")]
|
||||||
|
public required string report { get; set; }
|
||||||
|
|
||||||
|
public Chat_Report_List(string mid, string cid, string uid, string report)
|
||||||
|
{
|
||||||
|
this.mid = mid;
|
||||||
|
this.cid = cid;
|
||||||
|
this.uid = uid;
|
||||||
|
this.report = report;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
44
Program/Models/Entities/Log.cs
Normal file
44
Program/Models/Entities/Log.cs
Normal file
|
@ -0,0 +1,44 @@
|
||||||
|
using System.ComponentModel.DataAnnotations;
|
||||||
|
using System.ComponentModel.DataAnnotations.Schema;
|
||||||
|
|
||||||
|
namespace Back.Program.Models.Entities
|
||||||
|
{
|
||||||
|
[Table("log_project")]
|
||||||
|
public class LogProject
|
||||||
|
{
|
||||||
|
[Key]
|
||||||
|
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
|
||||||
|
public int lid { get; set; }
|
||||||
|
public DateTime create_date {get; set;}
|
||||||
|
public string log { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
[Table("log_push")]
|
||||||
|
public class LogPush
|
||||||
|
{
|
||||||
|
[Key]
|
||||||
|
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
|
||||||
|
public int lid { get; set; }
|
||||||
|
public string bid {get; set;}
|
||||||
|
public string pid {get; set;}
|
||||||
|
public DateTime create_date {get; set;}
|
||||||
|
public string create_uid {get; set;}
|
||||||
|
public string log { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
[Table("log_user")]
|
||||||
|
public class LogUser
|
||||||
|
{
|
||||||
|
[Key]
|
||||||
|
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
|
||||||
|
public int lid { get; set; }
|
||||||
|
public string uid {get; set;}
|
||||||
|
public DateTime create_date {get; set;}
|
||||||
|
public string create_uid {get; set;}
|
||||||
|
public string log { get; set; }
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
177
Program/Models/Entities/PushPayload.cs
Normal file
177
Program/Models/Entities/PushPayload.cs
Normal file
|
@ -0,0 +1,177 @@
|
||||||
|
using System.ComponentModel.DataAnnotations;
|
||||||
|
using System.ComponentModel.DataAnnotations.Schema;
|
||||||
|
|
||||||
|
namespace Back.Program.Models.Entities
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
* iOS Payload 정의
|
||||||
|
* aps 딕셔너리 구성
|
||||||
|
* 1. alert : 실질적으로 사용자에게 노출되는 항목
|
||||||
|
* 1.1. title : 제목
|
||||||
|
* 1.2. subtitle : 부제목
|
||||||
|
* 1.3. body : 본문 내용
|
||||||
|
* 2. badge : 앱 아이콘에 표시할 숫자
|
||||||
|
* 3. sound : 소리인데 "default" 가능
|
||||||
|
* 4. content-available : 백그라운드 업데이트나 사일런트 푸시 송신시 사용한다.
|
||||||
|
* 1로 설정해 사용자가 직접 보지 않아도 앱이 업데이트 되게 할 수 있으며,
|
||||||
|
* UI에 알림이 표시되지 않고 데이터만 전달할 때 필요하다.
|
||||||
|
* 5. mutable-content : 값을 1로 설정해두면 Notification Service Extension을 통해 알림의 내용을 변경할 수 있다.
|
||||||
|
* 6. category : 사용자 인터렉션 구성시에 사용하는 식별자이다.
|
||||||
|
* 7. thread-id : 관련 알림들을 그룹화해 관리할 때 사용한다.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* FCM Payload
|
||||||
|
* https://firebase.google.com/docs/reference/fcm/rest/v1/projects.messages?hl=ko&_gl=1*3awp3i*_up*MQ..*_ga*MTMyNDk4ODU5MC4xNzQxMTM1MzI3*_ga_CW55HF8NVT*MTc0MTEzNTMyNy4xLjAuMTc0MTEzNTM4My4wLjAuMA..#Notification
|
||||||
|
* {
|
||||||
|
"name": string,
|
||||||
|
"data": { // 입력 전용으로 임의의 가
|
||||||
|
string: string,
|
||||||
|
...
|
||||||
|
},
|
||||||
|
"notification": {
|
||||||
|
object (Notification)
|
||||||
|
},
|
||||||
|
"android": {
|
||||||
|
object (AndroidConfig)
|
||||||
|
},
|
||||||
|
"webpush": {
|
||||||
|
object (WebpushConfig)
|
||||||
|
},
|
||||||
|
"apns": {
|
||||||
|
object (ApnsConfig)
|
||||||
|
},
|
||||||
|
"fcm_options": {
|
||||||
|
object (FcmOptions)
|
||||||
|
},
|
||||||
|
|
||||||
|
// Union field target can be only one of the following:
|
||||||
|
"token": string,
|
||||||
|
"topic": string,
|
||||||
|
"condition": string
|
||||||
|
// End of list of possible types for union field target.
|
||||||
|
}
|
||||||
|
* 1. Notification 영역
|
||||||
|
* 1.1. title: 알림 제목
|
||||||
|
* 1.2. body: 알림 내용
|
||||||
|
* 1.3. image: 알림에 표시할 이미지 URL
|
||||||
|
* 1.4. sound: 알림 재생 시 사용할 사운드
|
||||||
|
* 1.5. click_action: 알림 클릭 시 실행할 액션(인텐트 액션 문자열)
|
||||||
|
* 1.6. tag: 동일 태그를 가진 알림끼리 대체 또는 그룹화할 때 사용
|
||||||
|
* 1.7. color: 알림 아이콘 배경색 (16진수 코드 등)
|
||||||
|
* 1.8. body_loc_key 및 body_loc_args: 본문에 사용할 지역화 키와 인자 배열
|
||||||
|
* 1.9. title_loc_key 및 title_loc_args: 제목에 사용할 지역화 키와 인자 배열
|
||||||
|
* 1.10. channel_id: Android Oreo 이상에서 알림 채널 식별자
|
||||||
|
* 1.11. ticker, sticky, event_time, notification_priority, visibility, notification_count, light_settings, vibrate_timings 등: 사용자 경험이나 알림의 동작 방식을 세밀하게 제어할 때 사용
|
||||||
|
* 2. Data 영역 : 임의의 키-값 쌍을 포함하고 UI에 표시되는 내용이 아닌 앱의 로직에 활용할 데이터를 보낼 때 사용한다.
|
||||||
|
* 3. 기타 전송 옵션
|
||||||
|
* 3.1. priority: 메시지 전송 우선순위 (“high” 또는 “normal”)
|
||||||
|
* 3.2. time_to_live (ttl): 메시지 유효 시간(초 단위)
|
||||||
|
* 3.3. collapse_key: 동일 collapse_key를 가진 메시지는 최신 하나로 교체됨
|
||||||
|
* 3.4. restricted_package_name: 메시지를 수신할 앱의 패키지 이름 (Android 전용)
|
||||||
|
*/
|
||||||
|
|
||||||
|
[Table("payload")]
|
||||||
|
public class DBPayload
|
||||||
|
{
|
||||||
|
public string bid { get; set; }
|
||||||
|
public string pid { get; set; }
|
||||||
|
public string title {get; set;}
|
||||||
|
public string? subtitle {get; set;}
|
||||||
|
public string body {get; set;}
|
||||||
|
public bool alert_yn {get; set;}
|
||||||
|
public string category {get; set;}
|
||||||
|
public string? content {get; set;}
|
||||||
|
}
|
||||||
|
|
||||||
|
[Table("push_cabinet")]
|
||||||
|
public class PushCabinet
|
||||||
|
{
|
||||||
|
[Key]
|
||||||
|
public int id { get; set; }
|
||||||
|
public string uid { get; set; }
|
||||||
|
public string pid { get; set; }
|
||||||
|
public string bid { get; set; }
|
||||||
|
public DateTime send_date { get; set; }
|
||||||
|
public bool check_yn { get; set; }
|
||||||
|
public string? content {get; set;}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class PushRequest
|
||||||
|
{
|
||||||
|
public string bid { get; set; }
|
||||||
|
public List<string> uids { get; set; }
|
||||||
|
public string pid { get; set; }
|
||||||
|
|
||||||
|
public string? content { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public class Payload
|
||||||
|
{
|
||||||
|
public Aps aps { get; set; }
|
||||||
|
public string pid { get; set; }
|
||||||
|
public string bid { get; set; }
|
||||||
|
public string content { get; set; }
|
||||||
|
// public string customKey { get; set; } 이런식으로 추가도 가능
|
||||||
|
|
||||||
|
public string ToJson()
|
||||||
|
{
|
||||||
|
return System.Text.Json.JsonSerializer.Serialize(this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class Aps
|
||||||
|
{
|
||||||
|
public Aps()
|
||||||
|
{
|
||||||
|
sound = "default";
|
||||||
|
content_available = 1;
|
||||||
|
}
|
||||||
|
[Required(ErrorMessage = "필수 입력 누락 (alert)")]
|
||||||
|
public Alert alert { get; set; }
|
||||||
|
|
||||||
|
[Required(ErrorMessage = "필수 입력 누락 (badge")]
|
||||||
|
public int badge { get; set; } // 앱 아이콘 표시 배지 숫자 설정
|
||||||
|
public string sound { get; set; } // 사운드 파일 이름 default = "default"
|
||||||
|
public int content_available { get; set; } // 백그라운드 알림 활성화: 필수 (1)
|
||||||
|
public string? category { get; set; } // 알림에 대한 특정 액션을 정의
|
||||||
|
}
|
||||||
|
|
||||||
|
public class Alert
|
||||||
|
{
|
||||||
|
[Required(ErrorMessage = "필수 입력 누락 (title")]
|
||||||
|
public string title { get; set; } // 제목
|
||||||
|
[Required(ErrorMessage = "필수 입력 누락 (body)")]
|
||||||
|
public string body { get; set; } // 내용
|
||||||
|
public string? subtitle { get; set; } // 부제목 (선택)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 푸시 등록하기 위한 apns 여러 데이터 목록
|
||||||
|
/// </summary>
|
||||||
|
public class PushFileSetting
|
||||||
|
{
|
||||||
|
public string uri { get; set; }
|
||||||
|
public string p12Path { get; set; }
|
||||||
|
public string p12PWPath { get; set; }
|
||||||
|
public string apnsTopic { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public class PushData
|
||||||
|
{
|
||||||
|
public string pushToken { get; set; }
|
||||||
|
public Payload payload { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public class CreatePush
|
||||||
|
{
|
||||||
|
public string bid { get; set; }
|
||||||
|
public string title { get; set; }
|
||||||
|
public string? subtitle { get; set; }
|
||||||
|
public string body { get; set; }
|
||||||
|
public bool alert_yn { get; set; } = true;
|
||||||
|
public string category { get; set; }
|
||||||
|
public string? content { get; set; }
|
||||||
|
}
|
||||||
|
}
|
149
Program/Models/Entities/User.cs
Normal file
149
Program/Models/Entities/User.cs
Normal file
|
@ -0,0 +1,149 @@
|
||||||
|
using System.ComponentModel.DataAnnotations;
|
||||||
|
using System.ComponentModel.DataAnnotations.Schema;
|
||||||
|
|
||||||
|
namespace Back.Program.Models.Entities
|
||||||
|
{
|
||||||
|
[Table("login")]
|
||||||
|
public class Login
|
||||||
|
{
|
||||||
|
[Key]
|
||||||
|
[Required(ErrorMessage = "필수 항목 누락")]
|
||||||
|
[MaxLength(100)]
|
||||||
|
public string sns_id {get; set;}
|
||||||
|
|
||||||
|
[Required(ErrorMessage = "필수 항목 누락")]
|
||||||
|
[MaxLength(70)]
|
||||||
|
public string uid {get; set;}
|
||||||
|
|
||||||
|
[Required(ErrorMessage = "필수 항목 누락")]
|
||||||
|
[MaxLength(4)]
|
||||||
|
public string sns_type {get; set;}
|
||||||
|
}
|
||||||
|
|
||||||
|
[Table("user_academy")]
|
||||||
|
public class User_Academy
|
||||||
|
{
|
||||||
|
[Key]
|
||||||
|
[Required(ErrorMessage = "필수 항목 누락")]
|
||||||
|
public string uid { get; set; }
|
||||||
|
|
||||||
|
[Required(ErrorMessage = "필수 항목 누락")]
|
||||||
|
public string bid { get; set; }
|
||||||
|
|
||||||
|
[Required(ErrorMessage = "필수 항목 누락")]
|
||||||
|
public DateTime register_date { get; set; }
|
||||||
|
|
||||||
|
[Required(ErrorMessage = "필수 항목 누락")]
|
||||||
|
public bool status { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
[Table("user")]
|
||||||
|
public class User
|
||||||
|
{
|
||||||
|
[Key]
|
||||||
|
[Required(ErrorMessage = "필수 항목 누락")]
|
||||||
|
public string uid { get; set; }
|
||||||
|
|
||||||
|
[Required(ErrorMessage = "필수 항목 누락")]
|
||||||
|
public string name { get; set; }
|
||||||
|
public DateTime? birth { get; set; }
|
||||||
|
|
||||||
|
[Required(ErrorMessage = "필수 항목 누락")]
|
||||||
|
public string type { get; set; }
|
||||||
|
|
||||||
|
public string? device_id { get; set; }
|
||||||
|
|
||||||
|
[Required(ErrorMessage = "필수 항목 누락")]
|
||||||
|
public bool auto_login_yn { get; set; }
|
||||||
|
|
||||||
|
[Required(ErrorMessage = "필수 항목 누락")]
|
||||||
|
public DateTime login_date { get; set; }
|
||||||
|
public string? push_token { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
[Table("permission")]
|
||||||
|
public class Permission
|
||||||
|
{
|
||||||
|
[Key]
|
||||||
|
|
||||||
|
[Required(ErrorMessage = "필수 항목 누락")]
|
||||||
|
public string uid { get; set; }
|
||||||
|
|
||||||
|
public bool location_yn {get; set;}
|
||||||
|
public bool camera_yn {get; set;}
|
||||||
|
public bool photo_yn {get; set;}
|
||||||
|
public bool push_yn {get; set;}
|
||||||
|
public bool market_app_yn {get; set;}
|
||||||
|
public bool market_sms_yn {get; set;}
|
||||||
|
public bool market_email_yn {get; set;}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
[Table("location")]
|
||||||
|
public class Location
|
||||||
|
{
|
||||||
|
[Key]
|
||||||
|
|
||||||
|
[Required(ErrorMessage = "필수 항목 누락")]
|
||||||
|
public string uid { get; set; }
|
||||||
|
|
||||||
|
[Required(ErrorMessage = "필수 항목 누락")]
|
||||||
|
public string lat { get; set; }
|
||||||
|
|
||||||
|
[Required(ErrorMessage = "필수 항목 누락")]
|
||||||
|
public string lng { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
[Table("contact")]
|
||||||
|
public class Contact
|
||||||
|
{
|
||||||
|
[Key]
|
||||||
|
|
||||||
|
[Required(ErrorMessage = "필수 항목 누락")]
|
||||||
|
public string uid { get; set; }
|
||||||
|
|
||||||
|
[Required(ErrorMessage = "필수 항목 누락")]
|
||||||
|
public string email { get; set; }
|
||||||
|
public string? phone { get; set; }
|
||||||
|
public string? address { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
// -- -- -- -- -- DB 테이블 -- -- -- -- -- //
|
||||||
|
|
||||||
|
public class UserAll
|
||||||
|
{
|
||||||
|
[Required(ErrorMessage = "필수 항목 누락")]
|
||||||
|
public string name { get; set; }
|
||||||
|
|
||||||
|
public DateTime? birth { get; set; }
|
||||||
|
|
||||||
|
[Required(ErrorMessage = "필수 항목 누락")]
|
||||||
|
public string type { get; set; }
|
||||||
|
|
||||||
|
public string? device_id { get; set; }
|
||||||
|
|
||||||
|
public bool auto_login_yn { get; set; }
|
||||||
|
public DateTime login_date { get; set; }
|
||||||
|
public string? push_token { get; set; }
|
||||||
|
|
||||||
|
[Required(ErrorMessage = "필수 항목 누락")]
|
||||||
|
public string email { get; set; }
|
||||||
|
public string? phone { get; set; }
|
||||||
|
public string? address { get; set; }
|
||||||
|
|
||||||
|
public bool location_yn {get; set;}
|
||||||
|
public bool camera_yn {get; set;}
|
||||||
|
public bool photo_yn {get; set;}
|
||||||
|
public bool push_yn {get; set;}
|
||||||
|
public bool market_app_yn {get; set;}
|
||||||
|
public bool market_sms_yn {get; set;}
|
||||||
|
public bool market_email_yn {get; set;}
|
||||||
|
|
||||||
|
[Required(ErrorMessage = "필수 항목 누락")]
|
||||||
|
public string sns_id {get; set;}
|
||||||
|
|
||||||
|
[Required(ErrorMessage = "필수 항목 누락")]
|
||||||
|
public string sns_type {get; set;}
|
||||||
|
}
|
||||||
|
}
|
21
Program/Models/Entities/Version.cs
Normal file
21
Program/Models/Entities/Version.cs
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
using System.ComponentModel.DataAnnotations;
|
||||||
|
using System.ComponentModel.DataAnnotations.Schema;
|
||||||
|
|
||||||
|
namespace Back.Program.Models.Entities
|
||||||
|
{
|
||||||
|
[Table("version")]
|
||||||
|
public class Version
|
||||||
|
{
|
||||||
|
[Key]
|
||||||
|
[MaxLength(4)]
|
||||||
|
public string os_type { get; set; }
|
||||||
|
[MaxLength(8)]
|
||||||
|
public string final_ver { get; set; }
|
||||||
|
[MaxLength(8)]
|
||||||
|
public string dev_ver { get; set; }
|
||||||
|
[MaxLength(8)]
|
||||||
|
public string force_ver { get; set; }
|
||||||
|
public bool choice_update_yn { get; set; }
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
8
Program/Repositories/V1/Interfaces/ILogRepository.cs
Normal file
8
Program/Repositories/V1/Interfaces/ILogRepository.cs
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
using Back.Program.Models.Entities;
|
||||||
|
|
||||||
|
namespace Back.Program.Repositories.V1.Interfaces;
|
||||||
|
|
||||||
|
public interface ILogRepository
|
||||||
|
{
|
||||||
|
Task<bool> SaveLogUser(LogUser log);
|
||||||
|
}
|
14
Program/Repositories/V1/Interfaces/IUserRepository.cs
Normal file
14
Program/Repositories/V1/Interfaces/IUserRepository.cs
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
using Back.Program.Common.Model;
|
||||||
|
using Back.Program.Models.Entities;
|
||||||
|
|
||||||
|
namespace Back.Program.Repositories.V1.Interfaces
|
||||||
|
{
|
||||||
|
public interface IUserRepository
|
||||||
|
{
|
||||||
|
Task<Login?> FindLogin(string accType, string snsId);
|
||||||
|
Task<User?> FindUser(string uid);
|
||||||
|
Task<RefreshToken?> FindRefreshToken(string uid);
|
||||||
|
Task<List<AcademyName>> FindAcademies(string uid);
|
||||||
|
Task<bool> SaveChanges();
|
||||||
|
}
|
||||||
|
}
|
24
Program/Repositories/V1/LogRepository.cs
Normal file
24
Program/Repositories/V1/LogRepository.cs
Normal file
|
@ -0,0 +1,24 @@
|
||||||
|
using Back.Program.Common.Data;
|
||||||
|
using Back.Program.Models.Entities;
|
||||||
|
using Back.Program.Repositories.V1.Interfaces;
|
||||||
|
|
||||||
|
namespace Back.Program.Repositories.V1;
|
||||||
|
|
||||||
|
public class LogRepository: ILogRepository
|
||||||
|
{
|
||||||
|
private readonly ILogger<LogRepository> _logger;
|
||||||
|
private readonly AppDbContext _context;
|
||||||
|
|
||||||
|
|
||||||
|
public LogRepository(ILogger<LogRepository> logger, AppDbContext context)
|
||||||
|
{
|
||||||
|
_logger = logger;
|
||||||
|
_context = context;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<bool> SaveLogUser(LogUser log)
|
||||||
|
{
|
||||||
|
_context.LogUser.Add(log);
|
||||||
|
return await _context.SaveChangesAsync() > 0;
|
||||||
|
}
|
||||||
|
}
|
49
Program/Repositories/V1/UserRepository.cs
Normal file
49
Program/Repositories/V1/UserRepository.cs
Normal file
|
@ -0,0 +1,49 @@
|
||||||
|
using Back.Program.Common.Data;
|
||||||
|
using Back.Program.Common.Model;
|
||||||
|
using Back.Program.Models.Entities;
|
||||||
|
using Back.Program.Repositories.V1.Interfaces;
|
||||||
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
|
||||||
|
namespace Back.Program.Repositories.V1
|
||||||
|
{
|
||||||
|
public class UserRepository: IUserRepository
|
||||||
|
{
|
||||||
|
private readonly ILogger<UserRepository> _logger;
|
||||||
|
private readonly AppDbContext _context;
|
||||||
|
|
||||||
|
public UserRepository(ILogger<UserRepository> logger,AppDbContext context) {
|
||||||
|
_logger = logger;
|
||||||
|
_context = context;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Task<Login?> FindLogin(string accType, string snsId)
|
||||||
|
{
|
||||||
|
return _context.Login.FirstOrDefaultAsync(l => l.sns_type == accType && l.sns_id == snsId);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Task<User?> FindUser(string uid)
|
||||||
|
{
|
||||||
|
return _context.User.FirstOrDefaultAsync(u => u.uid == uid);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Task<RefreshToken?> FindRefreshToken(string uid)
|
||||||
|
{
|
||||||
|
return _context.RefreshToken.FirstOrDefaultAsync(r => r.uid == uid);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Task<List<AcademyName>> FindAcademies(string uid)
|
||||||
|
{
|
||||||
|
var academyList = _context.UserAcademy
|
||||||
|
.Join(_context.Academy, ua => ua.uid, a => a.uid, (ua, a) => new { ua, a })
|
||||||
|
.Where(s => s.ua.uid == uid)
|
||||||
|
.Select(s => new AcademyName { bid = s.a.bid, name = s.a.business_name })
|
||||||
|
.ToListAsync();
|
||||||
|
return academyList;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<bool> SaveChanges()
|
||||||
|
{
|
||||||
|
return await _context.SaveChangesAsync() > 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
60
Program/Services/V1/InMemoryPushQueue.cs
Normal file
60
Program/Services/V1/InMemoryPushQueue.cs
Normal file
|
@ -0,0 +1,60 @@
|
||||||
|
using System.Collections.Concurrent;
|
||||||
|
using Back.Program.Models.Entities;
|
||||||
|
|
||||||
|
namespace Back.Program.Services.V1
|
||||||
|
{
|
||||||
|
public interface IPushQueue
|
||||||
|
{
|
||||||
|
void Enqueue(PushData pushData);
|
||||||
|
Task<PushData> DequeueAsync(CancellationToken cancellationToken);
|
||||||
|
}
|
||||||
|
|
||||||
|
public class InMemoryPushQueue: IPushQueue
|
||||||
|
{
|
||||||
|
private readonly ConcurrentQueue<PushData> _queue = new ConcurrentQueue<PushData>();
|
||||||
|
private readonly SemaphoreSlim _signal = new SemaphoreSlim(0);
|
||||||
|
|
||||||
|
public void Enqueue(PushData pushData)
|
||||||
|
{
|
||||||
|
if( pushData is null )
|
||||||
|
throw new ArgumentNullException(nameof(pushData));
|
||||||
|
_queue.Enqueue(pushData);
|
||||||
|
_signal.Release();
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<PushData> DequeueAsync(CancellationToken cancellationToken)
|
||||||
|
{
|
||||||
|
await _signal.WaitAsync(cancellationToken);
|
||||||
|
_queue.TryDequeue(out var pushData);
|
||||||
|
return pushData;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class PushBackgroundService : BackgroundService
|
||||||
|
{
|
||||||
|
private readonly IPushQueue _queue;
|
||||||
|
private readonly IApnsPushService _pushService;
|
||||||
|
|
||||||
|
public PushBackgroundService(IPushQueue queue, IApnsPushService pushService)
|
||||||
|
{
|
||||||
|
_queue = queue;
|
||||||
|
_pushService = pushService;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
|
||||||
|
{
|
||||||
|
while (!stoppingToken.IsCancellationRequested)
|
||||||
|
{
|
||||||
|
var pushData = await _queue.DequeueAsync(stoppingToken);
|
||||||
|
try
|
||||||
|
{
|
||||||
|
await _pushService.SendPushNotificationAsync(pushData.pushToken, pushData.payload);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Console.WriteLine($"푸시 전송 실패: {ex.Message}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
16
Program/Services/V1/Interfaces/IUserService.cs
Normal file
16
Program/Services/V1/Interfaces/IUserService.cs
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
using Back.Program.Common.Model;
|
||||||
|
using Back.Program.Models.Entities;
|
||||||
|
|
||||||
|
namespace Back.Program.Services.V1.Interfaces
|
||||||
|
{
|
||||||
|
public interface IUserService
|
||||||
|
{
|
||||||
|
Task<APIResponseStatus<object>> GetUser(string summary, string token);
|
||||||
|
Task<APIResponseStatus<object>> Login(string summary, string accType, string snsId);
|
||||||
|
Task<APIResponseStatus<object>> Register(string summary, UserAll request);
|
||||||
|
Task<APIResponseStatus<object>> Logout(string summary, string token);
|
||||||
|
Task<APIResponseStatus<object>> Cancel(string summary, string token);
|
||||||
|
Task<APIResponseStatus<object>> GetAcademy(string summary, string token);
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,26 +1,19 @@
|
||||||
using System;
|
|
||||||
using System.Diagnostics;
|
|
||||||
using System.Net.Http;
|
|
||||||
using System.Security.Cryptography;
|
|
||||||
using System.Security.Cryptography.X509Certificates;
|
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using System.Text.Json;
|
using System.Text.Json;
|
||||||
using System.Threading.Tasks;
|
using Back.Program.Common.Model;
|
||||||
|
using Back.Program.Models.Entities;
|
||||||
using Microsoft.Extensions.Options;
|
using Microsoft.Extensions.Options;
|
||||||
using Polly;
|
using Polly;
|
||||||
|
|
||||||
using AcaMate.V1.Models;
|
|
||||||
using Microsoft.Extensions.Options;
|
|
||||||
using Version = System.Version;
|
using Version = System.Version;
|
||||||
|
|
||||||
namespace AcaMate.V1.Services;
|
namespace Back.Program.Services.V1
|
||||||
|
|
||||||
public interface IApnsPushService
|
|
||||||
{
|
{
|
||||||
|
public interface IApnsPushService
|
||||||
|
{
|
||||||
Task SendPushNotificationAsync(string deviceToken, Payload payload);
|
Task SendPushNotificationAsync(string deviceToken, Payload payload);
|
||||||
}
|
}
|
||||||
public class ApnsPushService: IApnsPushService
|
public class ApnsPushService: IApnsPushService
|
||||||
{
|
{
|
||||||
private readonly HttpClient _httpClient;
|
private readonly HttpClient _httpClient;
|
||||||
private readonly PushFileSetting _setting;
|
private readonly PushFileSetting _setting;
|
||||||
|
|
||||||
|
@ -95,4 +88,5 @@ public class ApnsPushService: IApnsPushService
|
||||||
// throw new ServiceConnectionFailedException($"[푸시] : APNS 통신 실패 - {httpEx.Message}");
|
// throw new ServiceConnectionFailedException($"[푸시] : APNS 통신 실패 - {httpEx.Message}");
|
||||||
// }
|
// }
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
231
Program/Services/V1/RepositoryService.cs
Normal file
231
Program/Services/V1/RepositoryService.cs
Normal file
|
@ -0,0 +1,231 @@
|
||||||
|
using System.Linq.Expressions;
|
||||||
|
using System.Reflection;
|
||||||
|
using Back.Program.Common.Auth;
|
||||||
|
using Back.Program.Common.Data;
|
||||||
|
using Back.Program.Common.Model;
|
||||||
|
using Back.Program.Models.Entities;
|
||||||
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
|
||||||
|
namespace Back.Program.Services.V1
|
||||||
|
{
|
||||||
|
public interface IRepositoryService
|
||||||
|
{
|
||||||
|
// Task<ValidateToken> ValidateToken(string token, string refresh);
|
||||||
|
Task<bool> SaveData<T>(T entity, Expression<Func<T, object>> key = null) where T : class;
|
||||||
|
Task<bool> DeleteData<T>(T entity, Expression<Func<T, object>> key = null) where T : class;
|
||||||
|
String ReadSummary(Type type, String name);
|
||||||
|
}
|
||||||
|
|
||||||
|
public class RepositoryService: IRepositoryService
|
||||||
|
{
|
||||||
|
private readonly AppDbContext _dbContext;
|
||||||
|
private readonly ILogger<RepositoryService> _logger;
|
||||||
|
private readonly JwtTokenService _jwtTokenService;
|
||||||
|
|
||||||
|
public RepositoryService(AppDbContext dbContext, ILogger<RepositoryService> logger, JwtTokenService jwtTokenService)
|
||||||
|
{
|
||||||
|
_dbContext = dbContext;
|
||||||
|
_logger = logger;
|
||||||
|
_jwtTokenService = jwtTokenService;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// public async Task<ValidateToken> ValidateToken(string token, string refresh)
|
||||||
|
// {
|
||||||
|
// var principalToken = await _jwtTokenService.ValidateToken(token);
|
||||||
|
// if (principalToken != null)
|
||||||
|
// {
|
||||||
|
// var uid = principalToken.FindFirst(ClaimTypes.NameIdentifier)?.Value ?? string.Empty;
|
||||||
|
// _logger.LogInformation($"토큰 변환 - {uid}");
|
||||||
|
// return new ValidateToken
|
||||||
|
// {
|
||||||
|
// token = token,
|
||||||
|
// refresh = refresh,
|
||||||
|
// uid = uid
|
||||||
|
// };
|
||||||
|
// }
|
||||||
|
// else
|
||||||
|
// {
|
||||||
|
// _logger.LogInformation("엑세스 토큰 만료");
|
||||||
|
// // var refreshToken = await _dbContext.RefreshTokens
|
||||||
|
// // .FirstOrDefaultAsync(t => t.refresh_token == refresh);
|
||||||
|
// // if (refreshToken == null)
|
||||||
|
// // throw new TokenException("입력 받은 토큰 자체의 문제");
|
||||||
|
// //
|
||||||
|
// // var uid = refreshToken.uid;
|
||||||
|
// //
|
||||||
|
// // if (refreshToken.revoke_Date < DateTime.Now)
|
||||||
|
// // throw new RefreshRevokeException("리프레시 토큰 해지");
|
||||||
|
// //
|
||||||
|
// // if (refreshToken.expire_date > DateTime.Now)
|
||||||
|
// // {
|
||||||
|
// // _logger.LogInformation($"인증 완료 리프레시 : {uid}");
|
||||||
|
// // var access = _jwtTokenService.GenerateJwtToken(uid);
|
||||||
|
// //
|
||||||
|
// // return new ValidateToken
|
||||||
|
// // {
|
||||||
|
// // token = access,
|
||||||
|
// // refresh = refreshToken.refresh_token,
|
||||||
|
// // uid = uid
|
||||||
|
// // };
|
||||||
|
// // }
|
||||||
|
// // else
|
||||||
|
// // {
|
||||||
|
// // refreshToken = _jwtTokenService.GenerateRefreshToken(uid);
|
||||||
|
// // _logger.LogInformation("리프레시 토큰 만료");
|
||||||
|
// // // await SaveData<RefreshToken, string>(refreshToken, rt => rt.uid);
|
||||||
|
// // return new ValidateToken
|
||||||
|
// // {
|
||||||
|
// // token = token,
|
||||||
|
// // refresh = refreshToken.refresh_token,
|
||||||
|
// // uid = uid
|
||||||
|
// // };
|
||||||
|
// // }
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
public async Task<bool> SaveData<T>(T entity, Expression<Func<T, object>> key = null) where T : class
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (key != null)
|
||||||
|
{
|
||||||
|
// key를 가지고 EF 로 돌리는게 아니라 내가 조건을 넣어서 하는 경우에 사용함
|
||||||
|
var value = key.Compile()(entity);
|
||||||
|
var parameter = Expression.Parameter(typeof(T), "x");
|
||||||
|
var invokedExpr = Expression.Invoke(key, parameter);
|
||||||
|
var constantExpr = Expression.Constant(value, key.Body.Type);
|
||||||
|
var equalsExpr = Expression.Equal(invokedExpr, constantExpr);
|
||||||
|
var predicate = Expression.Lambda<Func<T, bool>>(equalsExpr, parameter);
|
||||||
|
var entityData = await _dbContext.Set<T>().FirstOrDefaultAsync(predicate);
|
||||||
|
|
||||||
|
if (entityData != null)
|
||||||
|
{
|
||||||
|
_logger.LogInformation($"[{typeof(T)}] 해당 PK 존재 = [{value}]: 계속");
|
||||||
|
_dbContext.Entry(entityData).CurrentValues.SetValues(entity);
|
||||||
|
if (!(_dbContext.Entry(entityData).Properties.Any(p => p.IsModified)))
|
||||||
|
{
|
||||||
|
_logger.LogInformation($"[{typeof(T)}] 변경 사항 없음");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
_logger.LogInformation($"[{typeof(T)}] 변경 사항이 존재");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_logger.LogInformation($"[{typeof(T)}] 처음등록");
|
||||||
|
_dbContext.Set<T>().Add(entity);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// EF 로 직접 키를 잡아서 사용 (관계키나 이런거 할때도 노상관됨)
|
||||||
|
|
||||||
|
// 모델이 존재하지 않거나 기본 키 정의가 되지 않은 오류가 발생할 건데 그건 운영 단계에서는 오류 나면 안되는거니
|
||||||
|
var keyProperties = _dbContext.Model.FindEntityType(typeof(T)).FindPrimaryKey().Properties;
|
||||||
|
|
||||||
|
// 각 키 속성에 대해, entity에서 실제 키 값을 추출
|
||||||
|
var keyValues = keyProperties.Select(p => typeof(T).GetProperty(p.Name).GetValue(entity)).ToArray();
|
||||||
|
|
||||||
|
// 기본 키 값을 이용해서 기존 엔티티를 찾음 (복합 키도 자동으로 처리됨)
|
||||||
|
var existingEntity = await _dbContext.Set<T>().FindAsync(keyValues);
|
||||||
|
|
||||||
|
if (existingEntity != null)
|
||||||
|
{
|
||||||
|
_logger.LogInformation($"[{typeof(T)}] 기존 데이터 발견: 기본 키 값({string.Join(", ", keyValues)})");
|
||||||
|
// 기존 엔티티를 업데이트: 새 entity의 값으로 교체
|
||||||
|
_dbContext.Entry(existingEntity).CurrentValues.SetValues(entity);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_logger.LogInformation($"[{typeof(T)}] 신규 데이터 등록: 기본 키 값({string.Join(", ", keyValues)})");
|
||||||
|
// 데이터가 없으면 새 엔티티 추가
|
||||||
|
_dbContext.Set<T>().Add(entity);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
await _dbContext.SaveChangesAsync();
|
||||||
|
_logger.LogInformation($"[{typeof(T)}] DB 저장 완료: 종료");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
_logger.LogInformation($"[{typeof(T)}] 저장 중 알 수 없는 오류 발생: {ex}");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public async Task<bool> DeleteData<T>(T entity, Expression<Func<T, object>> key = null) where T : class
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (key != null)
|
||||||
|
{
|
||||||
|
// key를 통해 조건식을 만들어 삭제할 엔티티를 찾는 경우
|
||||||
|
var value = key.Compile()(entity);
|
||||||
|
var parameter = Expression.Parameter(typeof(T), "x");
|
||||||
|
var invokedExpr = Expression.Invoke(key, parameter);
|
||||||
|
var constantExpr = Expression.Constant(value, key.Body.Type);
|
||||||
|
var equalsExpr = Expression.Equal(invokedExpr, constantExpr);
|
||||||
|
var predicate = Expression.Lambda<Func<T, bool>>(equalsExpr, parameter);
|
||||||
|
|
||||||
|
var entityData = await _dbContext.Set<T>().FirstOrDefaultAsync(predicate);
|
||||||
|
if (entityData == null)
|
||||||
|
{
|
||||||
|
_logger.LogInformation($"[{typeof(T)}] 삭제 대상 데이터가 존재하지 않습니다. (값 = {value})");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
_logger.LogInformation($"[{typeof(T)}] 조건에 맞는 데이터 발견 (값 = {value}): 삭제 진행");
|
||||||
|
_dbContext.Set<T>().Remove(entityData);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// key가 없는 경우 EF Core 메타데이터를 사용하여 기본 키를 통한 삭제
|
||||||
|
var entityType = _dbContext.Model.FindEntityType(typeof(T));
|
||||||
|
if (entityType == null)
|
||||||
|
{
|
||||||
|
throw new InvalidOperationException($"Entity type '{typeof(T).Name}'이 모델에 존재하지 않습니다.");
|
||||||
|
}
|
||||||
|
|
||||||
|
var primaryKey = entityType.FindPrimaryKey();
|
||||||
|
if (primaryKey == null)
|
||||||
|
{
|
||||||
|
throw new InvalidOperationException($"Entity type '{typeof(T).Name}'에 기본 키가 정의되어 있지 않습니다.");
|
||||||
|
}
|
||||||
|
|
||||||
|
var keyProperties = primaryKey.Properties;
|
||||||
|
var keyValues = keyProperties.Select(p => typeof(T).GetProperty(p.Name).GetValue(entity)).ToArray();
|
||||||
|
|
||||||
|
var existingEntity = await _dbContext.Set<T>().FindAsync(keyValues);
|
||||||
|
if (existingEntity == null)
|
||||||
|
{
|
||||||
|
_logger.LogInformation($"[{typeof(T)}] 기본 키 값({string.Join(", ", keyValues)})에 해당하는 삭제 대상 데이터가 존재하지 않습니다.");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
_logger.LogInformation($"[{typeof(T)}] 기본 키 값({string.Join(", ", keyValues)})에 해당하는 데이터 발견: 삭제 진행");
|
||||||
|
_dbContext.Set<T>().Remove(existingEntity);
|
||||||
|
}
|
||||||
|
|
||||||
|
await _dbContext.SaveChangesAsync();
|
||||||
|
_logger.LogInformation($"[{typeof(T)}] DB에서 삭제 완료");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
_logger.LogError($"[{typeof(T)}] 삭제 중 오류 발생: {ex}");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public string ReadSummary(Type type, string name)
|
||||||
|
{
|
||||||
|
var method = type.GetMethod(name) ?? throw new OutNULLException("swagger summary Load ERROR: NULL");
|
||||||
|
var att = method.GetCustomAttribute<CustomOperationAttribute>() ?? throw new OutNULLException("swagger summary Load ERROR: NULL");
|
||||||
|
return att.Summary;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
238
Program/Services/V1/UserService.cs
Normal file
238
Program/Services/V1/UserService.cs
Normal file
|
@ -0,0 +1,238 @@
|
||||||
|
using System.Security.Claims;
|
||||||
|
using Back.Program.Common.Auth;
|
||||||
|
using Back.Program.Common.Model;
|
||||||
|
using Back.Program.Models.Entities;
|
||||||
|
using Back.Program.Repositories.V1.Interfaces;
|
||||||
|
using Back.Program.Services.V1.Interfaces;
|
||||||
|
|
||||||
|
namespace Back.Program.Services.V1
|
||||||
|
{
|
||||||
|
public class UserService : IUserService
|
||||||
|
{
|
||||||
|
private readonly ILogger<IUserService> _logger;
|
||||||
|
private readonly IUserRepository _userRepository;
|
||||||
|
private readonly JwtTokenService _jwtTokenService;
|
||||||
|
private readonly IRepositoryService _repositoryService;
|
||||||
|
private readonly ILogRepository _logRepository;
|
||||||
|
|
||||||
|
public UserService(ILogger<IUserService> logger, IUserRepository userRepository,
|
||||||
|
JwtTokenService jwtTokenService,
|
||||||
|
IRepositoryService repositoryService, ILogRepository logRepository)
|
||||||
|
{
|
||||||
|
_logger = logger;
|
||||||
|
_userRepository = userRepository;
|
||||||
|
_jwtTokenService = jwtTokenService;
|
||||||
|
_repositoryService = repositoryService;
|
||||||
|
_logRepository = logRepository;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<APIResponseStatus<object>> GetUser(string summary, string token)
|
||||||
|
{
|
||||||
|
var validToken = await _jwtTokenService.ValidateToken(token);
|
||||||
|
if (validToken == null) return APIResponse.AccessExpireError();
|
||||||
|
|
||||||
|
var uid = validToken.FindFirst(ClaimTypes.NameIdentifier)?.Value ?? string.Empty;
|
||||||
|
var user = await _userRepository.FindUser(uid);
|
||||||
|
|
||||||
|
return APIResponse.Send<object>("000", $"[{summary}], 정상", user);
|
||||||
|
// user 없는 경우가 없네? 그거도 만들것
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<APIResponseStatus<object>> Login(string summary, string accType, string snsId)
|
||||||
|
{
|
||||||
|
var login = await _userRepository.FindLogin(accType, snsId);
|
||||||
|
if (login == null)
|
||||||
|
return APIResponse.Send<object>("001", $"[{summary}], 로그인 정보 없음", string.Empty);
|
||||||
|
|
||||||
|
var user = await _userRepository.FindUser(login.uid);
|
||||||
|
if (user == null)
|
||||||
|
return APIResponse.Send<object>("002", $"[{summary}], 회원 정보 오류", string.Empty);
|
||||||
|
|
||||||
|
user.login_date = DateTime.Now;
|
||||||
|
var token = _jwtTokenService.GenerateJwtToken(user.uid);
|
||||||
|
var refresh = _jwtTokenService.GenerateRefreshToken(user.uid);
|
||||||
|
|
||||||
|
if (await _repositoryService.SaveData<RefreshToken>(refresh))
|
||||||
|
{
|
||||||
|
return APIResponse.Send<object>("000", $"[{summary}], 정상",
|
||||||
|
new { token = token, refresh = refresh.refresh_token });
|
||||||
|
}
|
||||||
|
|
||||||
|
// 토큰 저장에 실패 및 로그인도 실패
|
||||||
|
return APIResponse.InternalSeverError($"[{summary}], 로그인 동작 실패");
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<APIResponseStatus<object>> Register(string summary, UserAll request)
|
||||||
|
{
|
||||||
|
var localPartEmail = request.email.Substring(0, request.email.IndexOf('@'));
|
||||||
|
var uid = $"AM{localPartEmail}{DateTime.Now:yyyyMMdd}";
|
||||||
|
|
||||||
|
var user = new User
|
||||||
|
{
|
||||||
|
uid = uid,
|
||||||
|
name = request.name,
|
||||||
|
birth = request.birth,
|
||||||
|
type = request.type,
|
||||||
|
device_id = request.device_id,
|
||||||
|
auto_login_yn = request.auto_login_yn,
|
||||||
|
login_date = request.login_date,
|
||||||
|
push_token = request.push_token
|
||||||
|
};
|
||||||
|
var login = new Login
|
||||||
|
{
|
||||||
|
uid = uid,
|
||||||
|
sns_id = request.sns_id,
|
||||||
|
sns_type = request.sns_type
|
||||||
|
};
|
||||||
|
|
||||||
|
var permission = new Permission
|
||||||
|
{
|
||||||
|
uid = uid,
|
||||||
|
location_yn = request.location_yn,
|
||||||
|
camera_yn = request.camera_yn,
|
||||||
|
photo_yn = request.photo_yn,
|
||||||
|
push_yn = request.push_yn,
|
||||||
|
market_app_yn = request.market_app_yn,
|
||||||
|
market_sms_yn = request.market_sms_yn,
|
||||||
|
market_email_yn = request.market_email_yn
|
||||||
|
};
|
||||||
|
|
||||||
|
var contact = new Contact
|
||||||
|
{
|
||||||
|
uid = uid,
|
||||||
|
email = request.email,
|
||||||
|
phone = request.phone,
|
||||||
|
address = request.address
|
||||||
|
};
|
||||||
|
|
||||||
|
var logUser = new LogUser
|
||||||
|
{
|
||||||
|
uid = login.uid,
|
||||||
|
create_date = DateTime.Now,
|
||||||
|
create_uid = "System",
|
||||||
|
log = ""
|
||||||
|
};
|
||||||
|
|
||||||
|
var saveUser = await _repositoryService.SaveData<User>(user);
|
||||||
|
var saveLogin = await _repositoryService.SaveData<Login>(login);
|
||||||
|
var savePermission = await _repositoryService.SaveData<Permission>(permission);
|
||||||
|
var saveContact = await _repositoryService.SaveData<Contact>(contact);
|
||||||
|
|
||||||
|
if (saveUser && saveLogin && savePermission && saveContact)
|
||||||
|
{
|
||||||
|
var token = _jwtTokenService.GenerateJwtToken(uid);
|
||||||
|
var refresh = _jwtTokenService.GenerateRefreshToken(uid);
|
||||||
|
|
||||||
|
if (await _repositoryService.SaveData<RefreshToken>(refresh))
|
||||||
|
{
|
||||||
|
logUser.log = $"[{summary}] : 정상";
|
||||||
|
if (await _logRepository.SaveLogUser(logUser))
|
||||||
|
{
|
||||||
|
_logger.LogInformation($"[{summary}]: 성공");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_logger.LogInformation($"[{summary}]: 성공 - 로그 저장 실패");
|
||||||
|
}
|
||||||
|
|
||||||
|
return APIResponse.Send<object>("000", $"[{summary}], 정상", new
|
||||||
|
{
|
||||||
|
token = token,
|
||||||
|
refresh = refresh.refresh_token
|
||||||
|
});
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
logUser.log = $"[{summary}] : 실패";
|
||||||
|
if (await _logRepository.SaveLogUser(logUser))
|
||||||
|
{
|
||||||
|
_logger.LogInformation($"[{summary}]: 실패");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_logger.LogInformation($"[{summary}]: 실패 - 로그 저장 실패");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return APIResponse.InternalSeverError($"[{summary}], 회원가입 동작 실패");
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<APIResponseStatus<object>> Logout(string summary, string token)
|
||||||
|
{
|
||||||
|
var validToken = await _jwtTokenService.ValidateToken(token);
|
||||||
|
if (validToken == null) return APIResponse.AccessExpireError();
|
||||||
|
|
||||||
|
var uid = validToken.FindFirst(ClaimTypes.NameIdentifier)?.Value ?? string.Empty;
|
||||||
|
var refresh = await _userRepository.FindRefreshToken(uid);
|
||||||
|
if (refresh != null)
|
||||||
|
{
|
||||||
|
refresh.revoke_Date = DateTime.Now;
|
||||||
|
if (await _repositoryService.SaveData<RefreshToken>(refresh))
|
||||||
|
{
|
||||||
|
return APIResponse.Send<object>("000", $"[{summary}], 로그아웃 정상", string.Empty);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return APIResponse.UnknownError();
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<APIResponseStatus<object>> Cancel(string summary, string token)
|
||||||
|
{
|
||||||
|
var validToken = await _jwtTokenService.ValidateToken(token);
|
||||||
|
if (validToken == null) return APIResponse.AccessExpireError();
|
||||||
|
|
||||||
|
var uid = validToken.FindFirst(ClaimTypes.NameIdentifier)?.Value ?? string.Empty;
|
||||||
|
var user = await _userRepository.FindUser(uid);
|
||||||
|
if (user == null) return APIResponse.Send<object>("001", $"[{summary}], 회원 정보 확인 오류", string.Empty);
|
||||||
|
|
||||||
|
if (await _repositoryService.DeleteData<User>(user))
|
||||||
|
{
|
||||||
|
if (await _logRepository.SaveLogUser(new LogUser
|
||||||
|
{
|
||||||
|
uid = user.uid, create_date = DateTime.Now, create_uid = "System", log = $"[{summary}] : 정상"
|
||||||
|
}))
|
||||||
|
{
|
||||||
|
_logger.LogInformation($"[{summary}]: 성공");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_logger.LogInformation($"[{summary}]: 성공 - 로그 저장 실패");
|
||||||
|
}
|
||||||
|
|
||||||
|
return APIResponse.Send<object>("000", $"[{summary}], 정상", string.Empty);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (await _logRepository.SaveLogUser(new LogUser
|
||||||
|
{
|
||||||
|
uid = user.uid, create_date = DateTime.Now, create_uid = "System", log = $"[{summary}] : 실패"
|
||||||
|
}))
|
||||||
|
{
|
||||||
|
_logger.LogInformation($"[{summary}]: 실패");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_logger.LogInformation($"[{summary}]: 실패 - 로그 저장 실패");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return APIResponse.InternalSeverError();
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<APIResponseStatus<object>> GetAcademy(string summary, string token)
|
||||||
|
{
|
||||||
|
var validToken = await _jwtTokenService.ValidateToken(token);
|
||||||
|
if (validToken == null) return APIResponse.AccessExpireError();
|
||||||
|
|
||||||
|
var uid = validToken.FindFirst(ClaimTypes.NameIdentifier)?.Value ?? string.Empty;
|
||||||
|
var user = await _userRepository.FindUser(uid);
|
||||||
|
if (user == null) return APIResponse.Send<object>("001", $"[{summary}], 회원 정보 확인 오류", string.Empty);
|
||||||
|
|
||||||
|
var academyList = await _userRepository.FindAcademies(uid);
|
||||||
|
|
||||||
|
_logger.LogInformation($"[{summary}]: 성공");
|
||||||
|
return APIResponse.Send<object>("000", $"[{summary}], 정상.", academyList);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,271 +0,0 @@
|
||||||
using System.Diagnostics;
|
|
||||||
using Microsoft.AspNetCore.Mvc;
|
|
||||||
using System.Text.Json;
|
|
||||||
using System.Security.Cryptography;
|
|
||||||
using System.Text;
|
|
||||||
using AcaMate.Common.Data;
|
|
||||||
using AcaMate.Common.Models;
|
|
||||||
using AcaMate.Common.Token;
|
|
||||||
using AcaMate.V1.Models;
|
|
||||||
using AcaMate.V1.Services;
|
|
||||||
using Microsoft.AspNetCore.Http.HttpResults;
|
|
||||||
using Microsoft.EntityFrameworkCore;
|
|
||||||
using Microsoft.IdentityModel.Tokens;
|
|
||||||
using Version = AcaMate.V1.Models.Version;
|
|
||||||
|
|
||||||
namespace AcaMate.V1.Controllers;
|
|
||||||
|
|
||||||
[ApiController]
|
|
||||||
[Route("/api/v1/in/app")]
|
|
||||||
[ApiExplorerSettings(GroupName = "공통")]
|
|
||||||
public class AppController : ControllerBase
|
|
||||||
{
|
|
||||||
private readonly AppDbContext _dbContext;
|
|
||||||
private readonly ILogger<AppController> _logger;
|
|
||||||
private readonly IRepositoryService _repositoryService;
|
|
||||||
private readonly JwtTokenService _jwtTokenService;
|
|
||||||
|
|
||||||
public AppController(AppDbContext dbContext, ILogger<AppController> logger, IRepositoryService repositoryService, JwtTokenService jwtTokenService)
|
|
||||||
{
|
|
||||||
_dbContext = dbContext;
|
|
||||||
_logger = logger;
|
|
||||||
_repositoryService = repositoryService;
|
|
||||||
_jwtTokenService = jwtTokenService;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// 이 키값의 제한 시간은 24h이다
|
|
||||||
[HttpGet]
|
|
||||||
[CustomOperation("헤더 정보 생성", "헤더에 접근하기 위한 키 값 받아오기", "시스템")]
|
|
||||||
public async Task<IActionResult> GetHeaderValue(string type, string specific, string project)
|
|
||||||
{
|
|
||||||
if (string.IsNullOrEmpty(specific) || string.IsNullOrEmpty(type) || string.IsNullOrEmpty(project))
|
|
||||||
return BadRequest(APIResponse.InvalidInputError());
|
|
||||||
if (!ModelState.IsValid) return BadRequest(APIResponse.InvalidInputError());
|
|
||||||
string summary = String.Empty;
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
summary = _repositoryService.ReadSummary(typeof(AppController), "GetHeaderValue");
|
|
||||||
bool valid = false;
|
|
||||||
|
|
||||||
switch (type)
|
|
||||||
{
|
|
||||||
case "I":
|
|
||||||
if (project == "me.myds.ipstein.acamate.AcaMate") valid = true;
|
|
||||||
break;
|
|
||||||
case "A":
|
|
||||||
break;
|
|
||||||
case "W":
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
return BadRequest(APIResponse.InvalidInputError($"[{summary}], 타입 에러"));
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (valid)
|
|
||||||
{
|
|
||||||
|
|
||||||
var apiHeader = await _dbContext.APIHeader.FirstOrDefaultAsync(h => h.specific_id == specific);
|
|
||||||
|
|
||||||
string nowTime = DateTime.Now.ToString("o");
|
|
||||||
string combineText = $"{project}_{nowTime}_{specific}";
|
|
||||||
string headerValue = KeyGenerator(combineText);
|
|
||||||
|
|
||||||
if (apiHeader != null)
|
|
||||||
{
|
|
||||||
if (DateTime.Now - apiHeader.connect_date > TimeSpan.FromHours(24))
|
|
||||||
{
|
|
||||||
_logger.LogInformation($"[{summary}] : 해당 키 유효기간 경과");
|
|
||||||
apiHeader.h_value = headerValue;
|
|
||||||
apiHeader.connect_date = DateTime.Now;
|
|
||||||
|
|
||||||
if (await _repositoryService.SaveData<APIHeader>(apiHeader))
|
|
||||||
{
|
|
||||||
string msg = "정상 - 로그 저장 실패";
|
|
||||||
var logProject = new LogProject
|
|
||||||
{
|
|
||||||
create_date = DateTime.Now ,
|
|
||||||
log = $"[{summary}] : 해당 키 유효시간 만료로 인한 새 키 부여"
|
|
||||||
};
|
|
||||||
if (await _repositoryService.SaveData<LogProject>(logProject))
|
|
||||||
msg = "정상";
|
|
||||||
return Ok(APIResponse.Send("001", msg, new { header = headerValue }));
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// 저장이 안된거니 서버 오류
|
|
||||||
return StatusCode(500, APIResponse.InternalSeverError());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// 유효기간 만료 이상 없이 다 잘 됨
|
|
||||||
return Ok(APIResponse.Send("000", "정상", new { header = apiHeader.h_value }));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
_logger.LogInformation($"[{summary}] : 저장 된게 없음");
|
|
||||||
|
|
||||||
var newHeader = new APIHeader
|
|
||||||
{
|
|
||||||
h_key = type == "I" ? "iOS_AM_Connect_Key"
|
|
||||||
: (type == "A" ? "And_AM_Connect_Key"
|
|
||||||
: (type == "W" ? "Web_AM_Connect_Key": throw new Exception("ERROR"))),
|
|
||||||
h_value = headerValue,
|
|
||||||
connect_date = DateTime.Now,
|
|
||||||
specific_id = specific
|
|
||||||
};
|
|
||||||
|
|
||||||
if (await _repositoryService.SaveData<APIHeader>(newHeader))
|
|
||||||
{
|
|
||||||
string msg = "정상 - 로그 저장 실패";
|
|
||||||
var logProject = new LogProject
|
|
||||||
{
|
|
||||||
create_date = DateTime.Now ,
|
|
||||||
log = $"[{summary}] : 새로운 등록으로 인한 새 키 부여"
|
|
||||||
};
|
|
||||||
// 이거 로그 저장 안되는거 확인!
|
|
||||||
_logger.LogInformation($"[{summary}] : {logProject.log}");
|
|
||||||
if (await _repositoryService.SaveData<LogProject>(logProject))
|
|
||||||
msg = "정상";
|
|
||||||
|
|
||||||
return Ok(APIResponse.Send("001", msg, new { header = headerValue }));
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// 저장이 안된거니 서버 오류
|
|
||||||
return StatusCode(500, APIResponse.InternalSeverError());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return BadRequest(APIResponse.InvalidInputError());
|
|
||||||
|
|
||||||
|
|
||||||
// return Ok(APIResponse.Send("000", "정상", Empty));
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
_logger.LogError($"[{summary}] : {ex.Message}");
|
|
||||||
return StatusCode(500, APIResponse.UnknownError(ex.Message));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 스웨거는 퍼블릭으로 선언된걸 죄다 API로 인식하는 경향이 있음
|
|
||||||
// 방법은 private 같이 접근 제한자를 변경하거나 [NonAction]을 붙여주면 됨
|
|
||||||
[NonAction]
|
|
||||||
private string KeyGenerator(string combineText)
|
|
||||||
{
|
|
||||||
using (SHA256 sha256 = SHA256.Create())
|
|
||||||
{
|
|
||||||
byte[] hashBytes = sha256.ComputeHash(Encoding.UTF8.GetBytes(combineText));
|
|
||||||
return BitConverter.ToString(hashBytes).Replace("-", string.Empty).ToLowerInvariant();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
[HttpGet("version")]
|
|
||||||
[CustomOperation("앱 버전 확인","앱 버전을 확인해서 업데이트 여부 판단", "시스템")]
|
|
||||||
public async Task<IActionResult> GetVersionData(string type)
|
|
||||||
{
|
|
||||||
if (string.IsNullOrEmpty(type))
|
|
||||||
{
|
|
||||||
return BadRequest(APIResponse.InvalidInputError());
|
|
||||||
}
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
var version = await _dbContext.Version.FirstOrDefaultAsync(v => v.os_type == (type == "I" ? "VO01" : "VO02"));
|
|
||||||
|
|
||||||
if (version == null)
|
|
||||||
{
|
|
||||||
return NotFound(APIResponse.NotFoundError());
|
|
||||||
}
|
|
||||||
|
|
||||||
var response = new APIResponseStatus<Version>
|
|
||||||
{
|
|
||||||
status = new Status()
|
|
||||||
{
|
|
||||||
code = "000",
|
|
||||||
message = "정상"
|
|
||||||
},
|
|
||||||
data = new Version()
|
|
||||||
{
|
|
||||||
os_type = (version.os_type == "VO01" ? "I" : (version.os_type == "VO02" ? "A" : "W")),
|
|
||||||
final_ver = version.final_ver,
|
|
||||||
force_ver = version.force_ver,
|
|
||||||
dev_ver = version.dev_ver,
|
|
||||||
choice_update_yn = version.choice_update_yn
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
string jsonString = JsonSerializer.Serialize(response);
|
|
||||||
|
|
||||||
// return Ok(jsonString);
|
|
||||||
return Ok(response.JsonToString());
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
Console.WriteLine($"{ex.Message}\n{ex.StackTrace}");
|
|
||||||
return StatusCode(500, APIResponse.UnknownError());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
[HttpGet("auth")]
|
|
||||||
[CustomOperation("서버 접근 권한 확인", "서버 기능을 사용하기 위한 접근에 대해 권한 확인", "시스템")]
|
|
||||||
public async Task<IActionResult> AuthProgram([FromBody] AuthKey keys)
|
|
||||||
{
|
|
||||||
string summary = String.Empty;
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
summary = _repositoryService.ReadSummary(typeof(AppController), "AuthProgram");
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
_logger.LogError($"[{summary}] : {ex.Message}");
|
|
||||||
return StatusCode(500, APIResponse.UnknownError(ex.Message));
|
|
||||||
}
|
|
||||||
|
|
||||||
return Ok(APIResponse.Send("000", "OK", Empty));
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
[HttpGet("retryAccess")]
|
|
||||||
[CustomOperation("엑세스 토큰 재발급", "액세스 토큰 재발급 동작 수행", "시스템")]
|
|
||||||
public async Task<IActionResult> RetryAccessToken(string refresh)
|
|
||||||
{
|
|
||||||
string summary = String.Empty;
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
summary = _repositoryService.ReadSummary(typeof(AppController), "AuthProgram");
|
|
||||||
var refreshToken = await _dbContext.RefreshTokens
|
|
||||||
.FirstOrDefaultAsync(t => t.refresh_token == refresh);
|
|
||||||
if (refreshToken == null) throw new TokenException($"[{summary}] : 리프레시 토큰의 문제");
|
|
||||||
if (refreshToken.revoke_Date < DateTime.Now) throw new TokenException($"[{summary}] : 리프레시 토큰 만료");
|
|
||||||
if (refreshToken.expire_date < DateTime.Now) throw new TokenException($"[{summary}] : 리프레시 토큰 폐기");
|
|
||||||
string access = _jwtTokenService.GenerateJwtToken(refreshToken.uid);
|
|
||||||
return Ok(APIResponse.Send("000", $"[{summary}], 토큰 생성 완료",
|
|
||||||
new {
|
|
||||||
access = access
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
catch (TokenException ex)
|
|
||||||
{
|
|
||||||
_logger.LogError($"[{summary}] : {ex.Message}");
|
|
||||||
return Ok(APIResponse.InvalidInputError(ex.Message));
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
_logger.LogError($"[{summary}] : {ex.Message}");
|
|
||||||
return StatusCode(500, APIResponse.UnknownError(ex.Message));
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
|
@ -1,15 +0,0 @@
|
||||||
using Microsoft.AspNetCore.Mvc;
|
|
||||||
|
|
||||||
namespace AcaMate.V1.Controllers;
|
|
||||||
|
|
||||||
|
|
||||||
[ApiController]
|
|
||||||
[Route("/api/error")]
|
|
||||||
public class ErrorController: ControllerBase
|
|
||||||
{
|
|
||||||
[HttpGet]
|
|
||||||
public IActionResult HandleError()
|
|
||||||
{
|
|
||||||
return Problem("오류가 발생하였습니다. 잠시후 다시 시도해주세요.");
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,51 +0,0 @@
|
||||||
|
|
||||||
using System.Text.Json;
|
|
||||||
using AcaMate.Common.Data;
|
|
||||||
using AcaMate.Common.Models;
|
|
||||||
using AcaMate.Common.Token;
|
|
||||||
using AcaMate.V1.Models;
|
|
||||||
using AcaMate.V1.Services;
|
|
||||||
using Microsoft.AspNetCore.Http.HttpResults;
|
|
||||||
using Microsoft.AspNetCore.Mvc;
|
|
||||||
|
|
||||||
namespace AcaMate.V1.Controllers;
|
|
||||||
|
|
||||||
[ApiController]
|
|
||||||
[Route("/api/v1/in/member")]
|
|
||||||
[ApiExplorerSettings(GroupName = "사업자 정보")]
|
|
||||||
public class MemberController: ControllerBase
|
|
||||||
{
|
|
||||||
private readonly ILogger<MemberController> _logger;
|
|
||||||
private readonly AppDbContext _dbContext;
|
|
||||||
private readonly IRepositoryService _repositoryService;
|
|
||||||
private readonly JwtTokenService _jwtTokenService;
|
|
||||||
|
|
||||||
public MemberController(AppDbContext dbContext, ILogger<MemberController> logger, IRepositoryService repositoryService, JwtTokenService jwtTokenService)
|
|
||||||
{
|
|
||||||
_dbContext = dbContext;
|
|
||||||
_logger = logger;
|
|
||||||
_repositoryService = repositoryService;
|
|
||||||
_jwtTokenService = jwtTokenService;
|
|
||||||
}
|
|
||||||
|
|
||||||
[HttpGet("business")]
|
|
||||||
public IActionResult GetBusinessData()
|
|
||||||
{
|
|
||||||
// return Ok("GOOD");
|
|
||||||
return Ok("DB 참조");
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// -- -- -- -- -- -- -- -- -- -- -- -- //
|
|
||||||
|
|
||||||
[HttpGet("/api/v1/out/member/business")]
|
|
||||||
public IActionResult SearchBusinessNo()
|
|
||||||
{
|
|
||||||
return Ok("외부 참조");
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,481 +0,0 @@
|
||||||
|
|
||||||
using System.Security.Claims;
|
|
||||||
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.Common.Token;
|
|
||||||
using AcaMate.V1.Models;
|
|
||||||
|
|
||||||
|
|
||||||
namespace AcaMate.V1.Controllers;
|
|
||||||
|
|
||||||
|
|
||||||
[ApiController]
|
|
||||||
[Route("/api/v1/in/push")]
|
|
||||||
[ApiExplorerSettings(GroupName = "공통")]
|
|
||||||
public class PushController : ControllerBase
|
|
||||||
{
|
|
||||||
private readonly ILogger<PushController> _logger;
|
|
||||||
private readonly IPushQueue _pushQueue;
|
|
||||||
private readonly AppDbContext _dbContext;
|
|
||||||
private readonly IRepositoryService _repositoryService;
|
|
||||||
private readonly JwtTokenService _jwtTokenService;
|
|
||||||
public PushController(ILogger<PushController> logger, IPushQueue pushQueue, AppDbContext dbContext, IRepositoryService repositoryService, JwtTokenService jwtTokenService)
|
|
||||||
{
|
|
||||||
_logger = logger;
|
|
||||||
_pushQueue = pushQueue;
|
|
||||||
_dbContext = dbContext;
|
|
||||||
_repositoryService = repositoryService;
|
|
||||||
_jwtTokenService = jwtTokenService;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 추가 사항
|
|
||||||
// 카테고리 별 조회 하는 부분도 추가를 할 지 고민을 해야 할 것 같음
|
|
||||||
|
|
||||||
[HttpGet()]
|
|
||||||
[CustomOperation("푸시 확인", "저장된 양식을 확인 할 수 있다.", "푸시")]
|
|
||||||
[ProducesResponseType(StatusCodes.Status200OK, Type = typeof(APIResponseStatus<object>))]
|
|
||||||
public async Task<IActionResult> GetPush(string bid, string? pid, string? category)
|
|
||||||
{
|
|
||||||
string summary = String.Empty;
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
summary = _repositoryService.ReadSummary(typeof(PushController), "GetPush");
|
|
||||||
|
|
||||||
if (!(await _dbContext.Academy.AnyAsync(a=>a.bid == bid)))
|
|
||||||
return Ok(APIResponse.Send("100", $"[{summary}], 존재하지 않는 BID", Empty));
|
|
||||||
|
|
||||||
List<DBPayload> pushData = new List<DBPayload>();
|
|
||||||
var pushQuery = _dbContext.DBPayload.Where(p => p.bid == bid);
|
|
||||||
if (pid != null)
|
|
||||||
pushQuery = pushQuery.Where(p=>p.pid == pid);
|
|
||||||
if (category != null)
|
|
||||||
pushQuery = pushQuery.Where(p=>p.category == category);
|
|
||||||
pushData = await pushQuery.ToListAsync();
|
|
||||||
|
|
||||||
if (pushData.Count > 0)
|
|
||||||
{
|
|
||||||
return Ok(APIResponse.Send("000", $"[{summary}, 정상", pushData));
|
|
||||||
}
|
|
||||||
|
|
||||||
return Ok(APIResponse.Send("001", $"[{summary}], PUSH 데이터 없음", Empty));
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
_logger.LogError($"[{summary}] : {ex.Message}");
|
|
||||||
return StatusCode(500, APIResponse.UnknownError());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
[HttpPost("send")]
|
|
||||||
[CustomOperation("푸시 발송", "저장된 양식으로, 사용자에게 푸시를 송신한다.", "푸시")]
|
|
||||||
[ProducesResponseType(StatusCodes.Status200OK, Type = typeof(APIResponseStatus<object>))]
|
|
||||||
public async Task<IActionResult> 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 () =>
|
|
||||||
{
|
|
||||||
if (payload == null)
|
|
||||||
throw new PushInvalidException("payload is NULL");
|
|
||||||
|
|
||||||
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();
|
|
||||||
payload.aps.badge = badge + 1;
|
|
||||||
|
|
||||||
// 푸시를 보내야 하니 푸시 토큰 확인
|
|
||||||
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 = payload.content != "" ? payload.content : null,
|
|
||||||
};
|
|
||||||
|
|
||||||
var pushData = new PushData
|
|
||||||
{
|
|
||||||
pushToken = pushToken,
|
|
||||||
payload = payload
|
|
||||||
};
|
|
||||||
|
|
||||||
if (await _repositoryService.SaveData<PushCabinet>(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>(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>(logPush))
|
|
||||||
_logger.LogInformation($"[{summary}] : 로그 추가");
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
});
|
|
||||||
return Ok(APIResponse.Send("000", $"[{summary}], 정상", Empty));
|
|
||||||
}
|
|
||||||
catch (PushInvalidException ex)
|
|
||||||
{
|
|
||||||
_logger.LogError($"[{summary}] : {ex.Message}");
|
|
||||||
return Ok(APIResponse.Send("001", $"[{summary}], 푸시 송신 중 문제 발생",Empty));
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
_logger.LogError($"[{summary}] : {ex.Message}");
|
|
||||||
return StatusCode(500, APIResponse.UnknownError(ex.Message));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
[HttpPost("set")]
|
|
||||||
[CustomOperation("푸시 변경", "저장된 양식을 변경한다.", "푸시")]
|
|
||||||
[ProducesResponseType(StatusCodes.Status200OK, Type = typeof(APIResponseStatus<object>))]
|
|
||||||
public async Task<IActionResult> SetPush(string token, [FromBody] DBPayload request)
|
|
||||||
{
|
|
||||||
if (string.IsNullOrEmpty(token)) return BadRequest(APIResponse.InvalidInputError());
|
|
||||||
if (!ModelState.IsValid) return BadRequest(APIResponse.InvalidInputError());
|
|
||||||
string summary = String.Empty;
|
|
||||||
string uid = String.Empty;
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
if (token == "System") uid = "System";
|
|
||||||
else
|
|
||||||
{
|
|
||||||
var validateToken = await _jwtTokenService.ValidateToken(token);
|
|
||||||
if (validateToken == null) return Ok(APIResponse.AccessExpireError());
|
|
||||||
uid = validateToken.FindFirst(ClaimTypes.NameIdentifier)?.Value ?? string.Empty;
|
|
||||||
}
|
|
||||||
|
|
||||||
summary = _repositoryService.ReadSummary(typeof(PushController), "SetPush");
|
|
||||||
var dbPayload = await _dbContext.DBPayload
|
|
||||||
.FirstOrDefaultAsync(p => p.pid == request.pid && p.bid == request.bid);
|
|
||||||
|
|
||||||
|
|
||||||
if (dbPayload != null)
|
|
||||||
{
|
|
||||||
var logPush = new LogPush
|
|
||||||
{
|
|
||||||
bid = dbPayload.bid,
|
|
||||||
pid = dbPayload.pid,
|
|
||||||
create_uid = uid,
|
|
||||||
create_date = DateTime.Now,
|
|
||||||
};
|
|
||||||
|
|
||||||
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>(dbPayload))
|
|
||||||
{
|
|
||||||
logPush.log = $"[{summary} : 정상 변경";
|
|
||||||
return Ok(APIResponse.Send("000", $"[{summary}], 정상", Empty));
|
|
||||||
}
|
|
||||||
|
|
||||||
// 로그를 이제 만들어서 추가를 해야 합니다.
|
|
||||||
if (await _repositoryService.SaveData<LogPush>(logPush))
|
|
||||||
_logger.LogInformation($"[{summary}] : 로그 추가");
|
|
||||||
}
|
|
||||||
|
|
||||||
return Ok(APIResponse.Send("100", $"[{summary}], PID, BID 또는 Cabinet 오류", Empty));
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
_logger.LogError($"[{summary}] : {ex.Message}");
|
|
||||||
return StatusCode(500, APIResponse.UnknownError(ex.Message));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
[HttpPost("create")]
|
|
||||||
[CustomOperation("푸시 생성", "새로운 푸시 양식을 생성한다.", "푸시")]
|
|
||||||
[ProducesResponseType(StatusCodes.Status200OK, Type = typeof(APIResponseStatus<object>))]
|
|
||||||
public async Task<IActionResult> CreatePush(string token, [FromBody] CreatePush request)
|
|
||||||
{
|
|
||||||
if (string.IsNullOrEmpty(token)) return BadRequest(APIResponse.InvalidInputError());
|
|
||||||
if (!ModelState.IsValid) return BadRequest(APIResponse.InvalidInputError());
|
|
||||||
string summary = String.Empty;
|
|
||||||
string uid = "";
|
|
||||||
|
|
||||||
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)}";
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
if (token == "System") uid = "System";
|
|
||||||
else {
|
|
||||||
var validateToken = await _jwtTokenService.ValidateToken(token);
|
|
||||||
if (validateToken == null) return Ok(APIResponse.AccessExpireError());
|
|
||||||
uid = validateToken.FindFirst(ClaimTypes.NameIdentifier)?.Value ?? string.Empty;
|
|
||||||
}
|
|
||||||
|
|
||||||
summary = _repositoryService.ReadSummary(typeof(PushController), "CreatePush");
|
|
||||||
|
|
||||||
if (await _dbContext.Academy.AnyAsync(a => a.bid == request.bid))
|
|
||||||
{
|
|
||||||
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,
|
|
||||||
};
|
|
||||||
|
|
||||||
if (await _repositoryService.SaveData<DBPayload>(payload))
|
|
||||||
{
|
|
||||||
var logPush = new LogPush
|
|
||||||
{
|
|
||||||
bid = payload.bid,
|
|
||||||
pid = payload.pid,
|
|
||||||
create_uid = uid,
|
|
||||||
create_date = DateTime.Now,
|
|
||||||
log = $"[{summary}] : 정상 생성"
|
|
||||||
};
|
|
||||||
|
|
||||||
// 로그를 이제 만들어서 추가를 해야 합니다.
|
|
||||||
if (await _repositoryService.SaveData<LogPush>(logPush))
|
|
||||||
_logger.LogInformation($"[{summary}] : 로그 추가");
|
|
||||||
|
|
||||||
return Ok(APIResponse.Send("000", $"[{summary}], 정상", Empty));
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
return Ok(APIResponse.Send("100", $"[{summary}], 학원 정보(BID) 확인 불가", Empty));
|
|
||||||
}
|
|
||||||
catch (TokenException tokenEx)
|
|
||||||
{
|
|
||||||
_logger.LogInformation($"[{summary}] : {tokenEx}");
|
|
||||||
return Ok(APIResponse.Send("001", $"[{summary}], 토큰에 문제가 있음",Empty));
|
|
||||||
}
|
|
||||||
catch (RefreshRevokeException refreshEx)
|
|
||||||
{
|
|
||||||
_logger.LogInformation($"[{summary}] : {refreshEx}");
|
|
||||||
return Ok(APIResponse.Send("001", $"[{summary}], 폐기된 리프레시 토큰",Empty));
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
_logger.LogError($"[{summary}] : {ex.Message}");
|
|
||||||
return StatusCode(500, APIResponse.UnknownError());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
[HttpDelete("delete")]
|
|
||||||
[CustomOperation("푸시 삭제", "저장된 푸시 양식을 삭제 한다.", "푸시")]
|
|
||||||
[ProducesResponseType(StatusCodes.Status200OK, Type = typeof(APIResponseStatus<object>))]
|
|
||||||
public async Task<IActionResult> DeletePush(string token, string bid, string pid)
|
|
||||||
{
|
|
||||||
if (string.IsNullOrEmpty(token)) return BadRequest(APIResponse.InvalidInputError());
|
|
||||||
if (!ModelState.IsValid) return BadRequest(APIResponse.InvalidInputError());
|
|
||||||
string uid = "";
|
|
||||||
string summary = String.Empty;
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
if (token == "System") uid = "System";
|
|
||||||
else {
|
|
||||||
var validateToken = await _jwtTokenService.ValidateToken(token);
|
|
||||||
if (validateToken == null) return Ok(APIResponse.AccessExpireError());
|
|
||||||
uid = validateToken.FindFirst(ClaimTypes.NameIdentifier)?.Value ?? string.Empty;
|
|
||||||
}
|
|
||||||
|
|
||||||
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<DBPayload>(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>(logPush)) _logger.LogInformation($"[{summary}] : 로그 추가");
|
|
||||||
|
|
||||||
return Ok(APIResponse.Send("000", $"[{summary}], 정상", Empty));
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
_logger.LogError($"[{summary}] : {ex.Message}");
|
|
||||||
return BadRequest(APIResponse.UnknownError(ex.Message));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
[HttpDelete("delete/list")]
|
|
||||||
[CustomOperation("사용자 푸시 목록 삭제", "사용자가 받은 푸시목록에서 푸시를 삭제한다..", "푸시")]
|
|
||||||
[ProducesResponseType(StatusCodes.Status200OK, Type = typeof(APIResponseStatus<object>))]
|
|
||||||
public async Task<IActionResult> DeleteListPush(string token, int id)
|
|
||||||
{
|
|
||||||
if (string.IsNullOrEmpty(token)) return BadRequest(APIResponse.InvalidInputError());
|
|
||||||
if (!ModelState.IsValid) return BadRequest(APIResponse.InvalidInputError());
|
|
||||||
string uid = "";
|
|
||||||
string summary = String.Empty;
|
|
||||||
try
|
|
||||||
{
|
|
||||||
if (token == "System") uid = "System";
|
|
||||||
else {
|
|
||||||
var validateToken = await _jwtTokenService.ValidateToken(token);
|
|
||||||
if (validateToken == null) return Ok(APIResponse.AccessExpireError());
|
|
||||||
uid = validateToken.FindFirst(ClaimTypes.NameIdentifier)?.Value ?? string.Empty;
|
|
||||||
}
|
|
||||||
|
|
||||||
summary = _repositoryService.ReadSummary(typeof(PushController), "DeleteListPush");
|
|
||||||
var cabinetPush = await _dbContext.PushCabinet.FirstOrDefaultAsync(c => c.id == id);
|
|
||||||
if (cabinetPush == null) return Ok(APIResponse.Send("001", $"[{summary}], 삭제 할 PUSH 없음", Empty));
|
|
||||||
if (!await _repositoryService.DeleteData<PushCabinet>(cabinetPush))
|
|
||||||
return Ok(APIResponse.Send("002", $"[{summary}], PUSH 삭제 실패", Empty));
|
|
||||||
|
|
||||||
// // 로그를 이제 만들어서 추가를 해야 합니다.
|
|
||||||
var logPush = new LogPush
|
|
||||||
{
|
|
||||||
bid = cabinetPush.bid,
|
|
||||||
pid = cabinetPush.pid,
|
|
||||||
create_uid = uid,
|
|
||||||
create_date = DateTime.Now,
|
|
||||||
log = $"[{summary}] : {cabinetPush.pid} 삭제 - {uid}"
|
|
||||||
};
|
|
||||||
if (await _repositoryService.SaveData<LogPush>(logPush)) _logger.LogInformation($"[{summary}] : 로그 추가");
|
|
||||||
|
|
||||||
return Ok(APIResponse.Send("000", $"[{summary}], 정상", Empty));
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
_logger.LogError($"[{summary}] : {ex.Message}");
|
|
||||||
return BadRequest(APIResponse.UnknownError(ex.Message));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
[HttpPost("list")]
|
|
||||||
[CustomOperation("사용자 푸시 목록 조회", "해당 사용자가 받은 푸시의 정보를 조회한다.", "푸시")]
|
|
||||||
[ProducesResponseType(StatusCodes.Status200OK, Type = typeof(APIResponseStatus<object>))]
|
|
||||||
public async Task<IActionResult> SearchToUserPush(string token, int size, [FromBody] PushCabinet? request)
|
|
||||||
{
|
|
||||||
if (string.IsNullOrEmpty(token)) return BadRequest(APIResponse.InvalidInputError());
|
|
||||||
if (!ModelState.IsValid) return BadRequest(APIResponse.InvalidInputError());
|
|
||||||
string uid = "";
|
|
||||||
string summary = String.Empty;
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
if (token == "System") uid = "System";
|
|
||||||
else {
|
|
||||||
var validateToken = await _jwtTokenService.ValidateToken(token);
|
|
||||||
if (validateToken == null) return Ok(APIResponse.AccessExpireError());
|
|
||||||
uid = validateToken.FindFirst(ClaimTypes.NameIdentifier)?.Value ?? string.Empty;
|
|
||||||
}
|
|
||||||
|
|
||||||
summary = _repositoryService.ReadSummary(typeof(PushController), "SearchToUserPush");
|
|
||||||
if (request == null)
|
|
||||||
{
|
|
||||||
var pagedData = await _dbContext.PushCabinet.Where(c => c.uid == uid)
|
|
||||||
.OrderBy(c=> c.send_date)
|
|
||||||
.Take(size)
|
|
||||||
.ToListAsync();
|
|
||||||
|
|
||||||
return Ok(APIResponse.Send("000", $"[{summary}], 정상", pagedData));
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
var sort = await _dbContext.PushCabinet.Where(p=> p.id == request.id)
|
|
||||||
.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(size).ToListAsync();
|
|
||||||
return Ok(APIResponse.Send("000", $"[{summary}], 정상", pagedData));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
_logger.LogError($"[{summary}] : {ex.Message}");
|
|
||||||
return BadRequest(APIResponse.UnknownError(ex.Message));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
}// END PUSH CONTROLLER
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -1,443 +0,0 @@
|
||||||
using System.IdentityModel.Tokens.Jwt;
|
|
||||||
using System.Security.Claims;
|
|
||||||
using Microsoft.IdentityModel.Tokens;
|
|
||||||
using Microsoft.AspNetCore.Mvc;
|
|
||||||
using Microsoft.EntityFrameworkCore;
|
|
||||||
using System.Linq.Expressions;
|
|
||||||
using Microsoft.Extensions.Logging;
|
|
||||||
using Microsoft.EntityFrameworkCore.Query;
|
|
||||||
using MySqlConnector;
|
|
||||||
using System.Linq;
|
|
||||||
|
|
||||||
using AcaMate.Common.Data;
|
|
||||||
using AcaMate.Common.Token;
|
|
||||||
using AcaMate.Common.Models;
|
|
||||||
|
|
||||||
using AcaMate.V1.Models;
|
|
||||||
using AcaMate.V1.Services;
|
|
||||||
|
|
||||||
namespace AcaMate.V1.Controllers;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// USER는 사용자가 자신의 데이터를 보거나 만들거나 하는 등 직접 사용하는 경우에 사용
|
|
||||||
/// </summary>
|
|
||||||
[ApiController]
|
|
||||||
[Route("/api/v1/in/user")]
|
|
||||||
[ApiExplorerSettings(GroupName = "사용자")]
|
|
||||||
public class UserController : ControllerBase
|
|
||||||
{
|
|
||||||
private readonly AppDbContext _dbContext;
|
|
||||||
private readonly ILogger<UserController> _logger;
|
|
||||||
private readonly JwtTokenService _jwtTokenService;
|
|
||||||
private readonly IRepositoryService _repositoryService;
|
|
||||||
|
|
||||||
public UserController(AppDbContext dbContext, ILogger<UserController> logger, JwtTokenService jwtTokenService,
|
|
||||||
IRepositoryService repositoryService)
|
|
||||||
{
|
|
||||||
_dbContext = dbContext;
|
|
||||||
_logger = logger;
|
|
||||||
_jwtTokenService = jwtTokenService;
|
|
||||||
_repositoryService = repositoryService;
|
|
||||||
}
|
|
||||||
|
|
||||||
[HttpGet]
|
|
||||||
[CustomOperation("회원 정보 조회", "회원 정보 조회 (자기자신)", "사용자")]
|
|
||||||
public async Task<IActionResult> GetUserData(string token)
|
|
||||||
{
|
|
||||||
if (string.IsNullOrEmpty(token)) return BadRequest(APIResponse.InvalidInputError());
|
|
||||||
if (!ModelState.IsValid) return BadRequest(APIResponse.InvalidInputError());
|
|
||||||
string summary = String.Empty;
|
|
||||||
try
|
|
||||||
{
|
|
||||||
var validateToken = await _jwtTokenService.ValidateToken(token);
|
|
||||||
if (validateToken == null) return Ok(APIResponse.AccessExpireError());
|
|
||||||
var uid = validateToken.FindFirst(ClaimTypes.NameIdentifier)?.Value ?? string.Empty;
|
|
||||||
summary = _repositoryService.ReadSummary(typeof(UserController), "GetUserData");
|
|
||||||
|
|
||||||
var user = await _dbContext.User
|
|
||||||
.Where(u => u.uid == uid)
|
|
||||||
.Select(u => new User
|
|
||||||
{
|
|
||||||
uid = u.uid,
|
|
||||||
name = u.name,
|
|
||||||
auto_login_yn = u.auto_login_yn,
|
|
||||||
birth = u.birth,
|
|
||||||
device_id = u.device_id,
|
|
||||||
login_date = u.login_date,
|
|
||||||
type = u.type
|
|
||||||
})
|
|
||||||
.FirstOrDefaultAsync();
|
|
||||||
|
|
||||||
return Ok(APIResponse.Send("000", $"[{summary}], 정상", user));
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
return StatusCode(500, APIResponse.UnknownError(ex.Message));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
[HttpGet("academy")]
|
|
||||||
[CustomOperation("학원 리스트 확인", "사용자가 등록된 학원 리스트 확인", "사용자")]
|
|
||||||
public async Task<IActionResult> ReadAcademyInfo(string token)
|
|
||||||
{
|
|
||||||
if (string.IsNullOrEmpty(token)) return BadRequest(APIResponse.InvalidInputError());
|
|
||||||
if (!ModelState.IsValid) return BadRequest(APIResponse.InvalidInputError());
|
|
||||||
string summary = String.Empty;
|
|
||||||
try
|
|
||||||
{
|
|
||||||
var validateToken = await _jwtTokenService.ValidateToken(token);
|
|
||||||
if (validateToken == null) return Ok(APIResponse.AccessExpireError());
|
|
||||||
var uid = validateToken.FindFirst(ClaimTypes.NameIdentifier)?.Value ?? string.Empty;
|
|
||||||
|
|
||||||
summary = _repositoryService.ReadSummary(typeof(UserController), "ReadAcademyInfo");
|
|
||||||
|
|
||||||
var academies = await (from ua in _dbContext.UserAcademy
|
|
||||||
join a in _dbContext.Academy on ua.bid equals a.bid
|
|
||||||
where ua.uid == uid
|
|
||||||
select new AcademyName
|
|
||||||
{
|
|
||||||
bid = a.bid,
|
|
||||||
name = a.business_name
|
|
||||||
}).ToListAsync();
|
|
||||||
|
|
||||||
|
|
||||||
return Ok(APIResponse.Send("000", $"[{summary}], 정상.", academies));
|
|
||||||
}
|
|
||||||
catch (TokenException tokenEx)
|
|
||||||
{
|
|
||||||
_logger.LogInformation($"[{summary}] : {tokenEx}");
|
|
||||||
return Ok(APIResponse.Send("001", $"[{summary}], 토큰에 문제가 있음", Empty));
|
|
||||||
}
|
|
||||||
catch (RefreshRevokeException refreshEx)
|
|
||||||
{
|
|
||||||
_logger.LogInformation($"[{summary}] : {refreshEx}");
|
|
||||||
return Ok(APIResponse.Send("001", $"[{summary}], 폐기된 리프레시 토큰", Empty));
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
_logger.LogInformation($"[{summary}] : {ex}");
|
|
||||||
return StatusCode(500, APIResponse.UnknownError(ex.Message));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
[HttpGet("login")]
|
|
||||||
[CustomOperation("SNS 로그인", "로그인 후 회원이 있는지 확인", "사용자")]
|
|
||||||
public async Task<IActionResult> Login(string acctype, string snsId)
|
|
||||||
{
|
|
||||||
// API 동작 파라미터 입력 값 확인
|
|
||||||
if (string.IsNullOrEmpty(acctype) && string.IsNullOrEmpty(snsId))
|
|
||||||
return BadRequest(APIResponse.InvalidInputError());
|
|
||||||
if (!ModelState.IsValid) return BadRequest(APIResponse.InvalidInputError());
|
|
||||||
string summary = String.Empty;
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
summary = _repositoryService.ReadSummary(typeof(UserController), "Login");
|
|
||||||
|
|
||||||
var login = await _dbContext.Login
|
|
||||||
.FirstOrDefaultAsync(l => l.sns_type == acctype && l.sns_id == snsId);
|
|
||||||
|
|
||||||
if (login != null)
|
|
||||||
{
|
|
||||||
// 로그인 정보가 존재 하는 상황
|
|
||||||
var user = await _dbContext.User
|
|
||||||
.FirstOrDefaultAsync(u => u.uid == login.uid);
|
|
||||||
|
|
||||||
// 회원 정보 없음
|
|
||||||
if (user == null) return Ok(APIResponse.Send("002", $"[{summary}], 회원 정보 오류", Empty));
|
|
||||||
|
|
||||||
// 정상적으로 User 테이블에도 있는것이 확인 됨
|
|
||||||
user.login_date = DateTime.Now;
|
|
||||||
await _dbContext.SaveChangesAsync();
|
|
||||||
|
|
||||||
// 토큰 생성은 로그인이 이제 되고 나서 한다.
|
|
||||||
var accessToken = _jwtTokenService.GenerateJwtToken(login.uid);
|
|
||||||
var refreshToken = _jwtTokenService.GenerateRefreshToken(login.uid);
|
|
||||||
_logger.LogInformation($"[{summary}] : {login.uid} = {accessToken}, {refreshToken}");
|
|
||||||
|
|
||||||
if (await _repositoryService.SaveData<RefreshToken>(refreshToken))
|
|
||||||
{
|
|
||||||
var logUser = new LogUser
|
|
||||||
{
|
|
||||||
uid = login.uid,
|
|
||||||
create_date = DateTime.Now,
|
|
||||||
create_uid = "System",
|
|
||||||
log = $"[{summary}] : 정상"
|
|
||||||
};
|
|
||||||
await _repositoryService.SaveData<LogUser>(logUser);
|
|
||||||
return Ok(APIResponse.Send("000", $"[{summary}], 정상",
|
|
||||||
new { token = accessToken, refresh = refreshToken.refresh_token }));
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
var logUser = new LogUser
|
|
||||||
{
|
|
||||||
uid = login.uid,
|
|
||||||
create_date = DateTime.Now,
|
|
||||||
create_uid = "System",
|
|
||||||
log = $"[{summary}] : 실패"
|
|
||||||
};
|
|
||||||
await _repositoryService.SaveData<LogUser>(logUser);
|
|
||||||
|
|
||||||
return Ok(APIResponse.InternalSeverError($"[{summary}], 로그인 저장 실패"));
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
return Ok(APIResponse.Send("001", $"[{summary}], 로그인 정보 없음", Empty));
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
_logger.LogInformation($"[{summary}] : {ex}");
|
|
||||||
return StatusCode(500, APIResponse.UnknownError(ex.Message));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
[HttpPost("register")]
|
|
||||||
[CustomOperation("회원 가입", "사용자 회원 가입", "사용자")]
|
|
||||||
public async Task<IActionResult> UserRegister([FromBody] UserAll request)
|
|
||||||
{
|
|
||||||
if (!ModelState.IsValid) return BadRequest(APIResponse.InvalidInputError());
|
|
||||||
string summary = String.Empty;
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
summary = _repositoryService.ReadSummary(typeof(UserController), "UserRegister");
|
|
||||||
var localPartEmail = request.email.Substring(0, request.email.IndexOf('@'));
|
|
||||||
var uid = $"AM{localPartEmail}{DateTime.Now:yyyyMMdd}";
|
|
||||||
|
|
||||||
var user = new User
|
|
||||||
{
|
|
||||||
uid = uid,
|
|
||||||
name = request.name,
|
|
||||||
birth = request.birth,
|
|
||||||
type = request.type,
|
|
||||||
device_id = request.device_id,
|
|
||||||
auto_login_yn = request.auto_login_yn,
|
|
||||||
login_date = request.login_date,
|
|
||||||
push_token = request.push_token
|
|
||||||
};
|
|
||||||
var login = new Login
|
|
||||||
{
|
|
||||||
uid = uid,
|
|
||||||
sns_id = request.sns_id,
|
|
||||||
sns_type = request.sns_type
|
|
||||||
};
|
|
||||||
|
|
||||||
var permission = new Permission
|
|
||||||
{
|
|
||||||
uid = uid,
|
|
||||||
location_yn = request.location_yn,
|
|
||||||
camera_yn = request.camera_yn,
|
|
||||||
photo_yn = request.photo_yn,
|
|
||||||
push_yn = request.push_yn,
|
|
||||||
market_app_yn = request.market_app_yn,
|
|
||||||
market_sms_yn = request.market_sms_yn,
|
|
||||||
market_email_yn = request.market_email_yn
|
|
||||||
};
|
|
||||||
|
|
||||||
var contact = new Contact
|
|
||||||
{
|
|
||||||
uid = uid,
|
|
||||||
email = request.email,
|
|
||||||
phone = request.phone,
|
|
||||||
address = request.address
|
|
||||||
};
|
|
||||||
|
|
||||||
var logUser = new LogUser
|
|
||||||
{
|
|
||||||
uid = login.uid,
|
|
||||||
create_date = DateTime.Now,
|
|
||||||
create_uid = "System",
|
|
||||||
log = ""
|
|
||||||
};
|
|
||||||
|
|
||||||
var saveUser = await _repositoryService.SaveData<User>(user);
|
|
||||||
var saveLogin = await _repositoryService.SaveData<Login>(login);
|
|
||||||
var savePermission = await _repositoryService.SaveData<Permission>(permission);
|
|
||||||
var saveContact = await _repositoryService.SaveData<Contact>(contact);
|
|
||||||
if (saveUser && saveLogin && savePermission && saveContact)
|
|
||||||
{
|
|
||||||
|
|
||||||
var token = _jwtTokenService.GenerateJwtToken(uid);
|
|
||||||
var refreshToken = _jwtTokenService.GenerateRefreshToken(uid);
|
|
||||||
|
|
||||||
if (await _repositoryService.SaveData<RefreshToken>(refreshToken))
|
|
||||||
{
|
|
||||||
logUser.log = $"[{summary}] : 정상";
|
|
||||||
|
|
||||||
if (await _repositoryService.SaveData<LogUser>(logUser))
|
|
||||||
_logger.LogInformation($"[{summary}] : 로그 저장 성공");
|
|
||||||
|
|
||||||
return Ok(APIResponse.Send("000", $"[{summary}], 정상", new
|
|
||||||
{
|
|
||||||
token = token,
|
|
||||||
refresh = refreshToken.refresh_token
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
_logger.LogError($"[{summary}] : 토큰 저장 실패");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
logUser.log = $"[{summary}] : 동작 실패";
|
|
||||||
await _repositoryService.SaveData<LogUser>(logUser);
|
|
||||||
|
|
||||||
return Ok(APIResponse.InternalSeverError());
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
_logger.LogInformation($"[{summary}] : {ex.Message}");
|
|
||||||
return BadRequest(APIResponse.UnknownError(ex.Message));
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
[HttpGet("logout")]
|
|
||||||
[CustomOperation("로그아웃", "사용자 로그아웃", "사용자")]
|
|
||||||
public async Task<IActionResult> Logout(string token)
|
|
||||||
{
|
|
||||||
if (string.IsNullOrEmpty(token)) return BadRequest(APIResponse.InvalidInputError());
|
|
||||||
if (!ModelState.IsValid) return BadRequest(APIResponse.InvalidInputError());
|
|
||||||
|
|
||||||
string summary = String.Empty;
|
|
||||||
try
|
|
||||||
{
|
|
||||||
var validateToken = await _jwtTokenService.ValidateToken(token);
|
|
||||||
if (validateToken == null) return Ok(APIResponse.AccessExpireError());
|
|
||||||
var uid = validateToken.FindFirst(ClaimTypes.NameIdentifier)?.Value ?? string.Empty;
|
|
||||||
|
|
||||||
summary = _repositoryService.ReadSummary(typeof(UserController), "UserRegister");
|
|
||||||
|
|
||||||
var refreshToken = await _dbContext.RefreshTokens.FirstOrDefaultAsync(r => r.uid == uid);
|
|
||||||
|
|
||||||
if (refreshToken != null)
|
|
||||||
{
|
|
||||||
refreshToken.revoke_Date = DateTime.Now;
|
|
||||||
await _repositoryService.SaveData<RefreshToken>(refreshToken);
|
|
||||||
return Ok(APIResponse.Send("000", $"[{summary}], 로그아웃 정상", Empty));
|
|
||||||
}
|
|
||||||
|
|
||||||
// 리프레시 토큰이 없다?? 그럼 이거 무조건 문제지 (이유를 알 수 없는)
|
|
||||||
return Ok(APIResponse.UnknownError());
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
return StatusCode(500, APIResponse.UnknownError($"[{summary}], {ex.Message}"));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
[HttpGet("cancel")]
|
|
||||||
[CustomOperation("회원 탈퇴", "사용자 탈퇴", "사용자")]
|
|
||||||
public async Task<IActionResult> Cancel(string token)
|
|
||||||
{
|
|
||||||
if (string.IsNullOrEmpty(token)) return BadRequest(APIResponse.InvalidInputError());
|
|
||||||
if (!ModelState.IsValid) return BadRequest(APIResponse.InvalidInputError());
|
|
||||||
string summary = String.Empty;
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
var validateToken = await _jwtTokenService.ValidateToken(token);
|
|
||||||
if (validateToken == null) return Ok(APIResponse.AccessExpireError());
|
|
||||||
var uid = validateToken.FindFirst(ClaimTypes.NameIdentifier)?.Value ?? string.Empty;
|
|
||||||
|
|
||||||
summary = _repositoryService.ReadSummary(typeof(UserController), "Cancel");
|
|
||||||
|
|
||||||
|
|
||||||
// 여기서 애초에 토큰 관련 에러가 2개가 나오게 만들어져 있음
|
|
||||||
var user = await _dbContext.User.FirstOrDefaultAsync(u => u.uid == uid);
|
|
||||||
|
|
||||||
if (user == null)
|
|
||||||
return Ok(APIResponse.Send("001", $"[{summary}], 회원 정보 확인 오류", Empty));
|
|
||||||
|
|
||||||
|
|
||||||
var logUser = new LogUser
|
|
||||||
{
|
|
||||||
uid = uid,
|
|
||||||
create_date = DateTime.Now,
|
|
||||||
create_uid = "System",
|
|
||||||
log = ""
|
|
||||||
};
|
|
||||||
|
|
||||||
string returnCode = "000";
|
|
||||||
string returnMsg = $"[{summary}], 정상";
|
|
||||||
if (await _repositoryService.DeleteData<User>(user))
|
|
||||||
logUser.log = $"[{summary}] : 정상";
|
|
||||||
else
|
|
||||||
{
|
|
||||||
logUser.log = $"[{summary}] : 실패";
|
|
||||||
returnMsg = $"[{summary}], 실패";
|
|
||||||
returnCode = "001";
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!(await _repositoryService.SaveData<LogUser>(logUser))) _logger.LogError($"[{summary}] : 로그 저장 실패");
|
|
||||||
|
|
||||||
return Ok(APIResponse.Send(returnCode, returnMsg, Empty));
|
|
||||||
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
return StatusCode(500, APIResponse.UnknownError($"[{summary}], {ex.Message}"));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// 근데 회원 정보를 변경하는게 뭐뭐를 변경해야 하는지 아직 정해진게 없어서 이건 일단 보류
|
|
||||||
/*
|
|
||||||
[HttpGet("set")]
|
|
||||||
[CustomOperation("회원 정보 변경", "회원 정보 변경", "사용자")]
|
|
||||||
public async Task<IActionResult> SetUserData(string token, string refresh) //, [FromBody])
|
|
||||||
{
|
|
||||||
if (string.IsNullOrEmpty(token) || string.IsNullOrEmpty(refresh))
|
|
||||||
return BadRequest(APIResponse.InvalidInputError());
|
|
||||||
if (!ModelState.IsValid) return BadRequest(APIResponse.InvalidInputError());
|
|
||||||
string summary = String.Empty;
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
summary = _repositoryService.ReadSummary(typeof(UserController), "Cancel");
|
|
||||||
|
|
||||||
|
|
||||||
// 여기서 애초에 토큰 관련 에러가 2개가 나오게 만들어져 있음
|
|
||||||
var validateToken = await _repositoryService.ValidateToken(token, refresh);
|
|
||||||
var user = await _dbContext.User.FirstOrDefaultAsync(u => u.uid == validateToken.uid);
|
|
||||||
|
|
||||||
}
|
|
||||||
catch (TokenException tokenEx)
|
|
||||||
{
|
|
||||||
return Ok(APIResponse.Send("101", $"[{summary}], 입력 받은 토큰의 문제", Empty));
|
|
||||||
}
|
|
||||||
catch (RefreshRevokeException refreshEx)
|
|
||||||
{
|
|
||||||
return Ok(APIResponse.Send("102", $"[{summary}], 폐기된 리프레시 토큰", Empty));
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
return StatusCode(500, APIResponse.UnknownError($"[{summary}], {ex.Message}"));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
string uid = "";
|
|
||||||
if (token == "System") uid = "System";
|
|
||||||
else {
|
|
||||||
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);
|
|
||||||
uid = validateToken.uid;
|
|
||||||
}
|
|
||||||
|
|
||||||
string summary = String.Empty;
|
|
||||||
try
|
|
||||||
{
|
|
||||||
summary = _repositoryService.ReadSummary(typeof(PushController), "GetUserData");
|
|
||||||
}
|
|
||||||
*/
|
|
|
@ -1,15 +0,0 @@
|
||||||
using System.Text.Json;
|
|
||||||
|
|
||||||
namespace AcaMate.V1.Models;
|
|
||||||
|
|
||||||
public class APIResult
|
|
||||||
{
|
|
||||||
public bool Success { get; set; }
|
|
||||||
public string Code { get; set; }
|
|
||||||
public string Message { get; set; }
|
|
||||||
|
|
||||||
public string JsonToString()
|
|
||||||
{
|
|
||||||
return JsonSerializer.Serialize(this);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,76 +0,0 @@
|
||||||
using Microsoft.IdentityModel.Tokens;
|
|
||||||
using System;
|
|
||||||
|
|
||||||
namespace AcaMate.V1.Models;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 입력 받은 토큰들(Access & Refresh) 자체에 문제가 있는 경우
|
|
||||||
/// </summary>
|
|
||||||
public class TokenException: Exception
|
|
||||||
{
|
|
||||||
public TokenException(string message) : base(message)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 리프레시 토큰이 만료가 나있는 경우
|
|
||||||
/// </summary>
|
|
||||||
public class RefreshRevokeException: Exception
|
|
||||||
{
|
|
||||||
public RefreshRevokeException(string message) : base(message)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 참조해야 하는 파일에서 오류가 발생하는 경우
|
|
||||||
/// </summary>
|
|
||||||
public class FileNotValidException : Exception
|
|
||||||
{
|
|
||||||
public FileNotValidException(string message) : base(message)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 파일 내부에 값을 읽을 때 오류가 발생하는 경우
|
|
||||||
/// </summary>
|
|
||||||
public class FileContentNotFoundException : Exception
|
|
||||||
{
|
|
||||||
public FileContentNotFoundException(string message) : base(message)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 외부 서비스에 연결시 연결 실패시
|
|
||||||
/// </summary>
|
|
||||||
public class ServiceConnectionFailedException : Exception
|
|
||||||
{
|
|
||||||
public ServiceConnectionFailedException(string message) : base(message)
|
|
||||||
{
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// PUSH 서비스 중 데이터 사용에 문제가 발생했을시
|
|
||||||
/// </summary>
|
|
||||||
public class PushInvalidException : Exception
|
|
||||||
{
|
|
||||||
public PushInvalidException(string message) : base(message)
|
|
||||||
{
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
/// <summary>
|
|
||||||
/// 값이 있어야 하는데 NULL인 경우
|
|
||||||
/// </summary>
|
|
||||||
public class OutNULLException : Exception
|
|
||||||
{
|
|
||||||
public OutNULLException(string message) : base(message)
|
|
||||||
{
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,31 +0,0 @@
|
||||||
using System.ComponentModel.DataAnnotations;
|
|
||||||
using System.ComponentModel.DataAnnotations.Schema;
|
|
||||||
|
|
||||||
namespace AcaMate.V1.Models;
|
|
||||||
|
|
||||||
[Table("academy")]
|
|
||||||
public class Academy
|
|
||||||
{
|
|
||||||
[Key]
|
|
||||||
public string bid { get; set; }
|
|
||||||
public string business_name { get; set; }
|
|
||||||
public string business_owner { get; set; }
|
|
||||||
public string business_number { get; set; }
|
|
||||||
public DateTime business_date { get; set; }
|
|
||||||
public string business_address { get; set; }
|
|
||||||
public string business_contact { get; set; }
|
|
||||||
public string uid { get; set; }
|
|
||||||
}
|
|
||||||
|
|
||||||
// -- -- -- -- -- DB 테이블 -- -- -- -- -- //
|
|
||||||
|
|
||||||
public class AcademyName
|
|
||||||
{
|
|
||||||
public string bid { get; set; }
|
|
||||||
public string name { get; set; }
|
|
||||||
}
|
|
||||||
|
|
||||||
public class RequestAcademy
|
|
||||||
{
|
|
||||||
public List<string> bids { get; set; }
|
|
||||||
}
|
|
|
@ -1,11 +0,0 @@
|
||||||
using System.ComponentModel.DataAnnotations;
|
|
||||||
using System.ComponentModel.DataAnnotations.Schema;
|
|
||||||
|
|
||||||
|
|
||||||
namespace AcaMate.V1.Models;
|
|
||||||
|
|
||||||
[Table("authkey")]
|
|
||||||
public class AuthKey
|
|
||||||
{
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,147 +0,0 @@
|
||||||
using System.ComponentModel.DataAnnotations;
|
|
||||||
using System.ComponentModel.DataAnnotations.Schema;
|
|
||||||
using System.Diagnostics.CodeAnalysis;
|
|
||||||
|
|
||||||
namespace AcaMate.V1.Models;
|
|
||||||
|
|
||||||
[Table("chat_room")]
|
|
||||||
public class Chat_Room
|
|
||||||
{
|
|
||||||
[Required(ErrorMessage = "필수 항목 누락")]
|
|
||||||
[MaxLength(16)]
|
|
||||||
public required string cid { get; set; } // bid + yyyyMMdd + count
|
|
||||||
|
|
||||||
[Required(ErrorMessage = "필수 항목 누락")]
|
|
||||||
[MaxLength(6)]
|
|
||||||
public required string bid { get; set; }
|
|
||||||
|
|
||||||
[Required(ErrorMessage = "필수 항목 누락")]
|
|
||||||
public required string name { get; set; }
|
|
||||||
|
|
||||||
[Required(ErrorMessage = "필수 항목 누락")]
|
|
||||||
[MaxLength(4)]
|
|
||||||
public required string type { get; set; }
|
|
||||||
|
|
||||||
[Required(ErrorMessage = "필수 항목 누락")]
|
|
||||||
public required DateTime create_date { get; set; }
|
|
||||||
|
|
||||||
[Required(ErrorMessage = "필수 항목 누락")]
|
|
||||||
public required DateTime open_date { get; set; }
|
|
||||||
|
|
||||||
public DateTime? close_date { get; set; }
|
|
||||||
|
|
||||||
public Chat_Room(string cid, string bid, string name, string type, DateTime create_date, DateTime open_date)
|
|
||||||
{
|
|
||||||
this.cid = cid;
|
|
||||||
this.bid = bid;
|
|
||||||
this.name = name;
|
|
||||||
this.type = type;
|
|
||||||
this.create_date = create_date;
|
|
||||||
this.open_date = open_date;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
[Table("chat_join")]
|
|
||||||
public class Chat_Join
|
|
||||||
{
|
|
||||||
[Required(ErrorMessage = "필수 항목 누락")]
|
|
||||||
[MaxLength(16)]
|
|
||||||
public required string cid { get; set; }
|
|
||||||
|
|
||||||
[Required(ErrorMessage = "필수 항목 누락")]
|
|
||||||
[MaxLength(70)]
|
|
||||||
public required string uid { get; set; }
|
|
||||||
|
|
||||||
public DateTime? join_date { get; set; }
|
|
||||||
public string? mid { get; set; }
|
|
||||||
|
|
||||||
[Required(ErrorMessage = "필수 항목 누락")]
|
|
||||||
public required bool is_notice { get; set; }
|
|
||||||
|
|
||||||
public Chat_Join(string cid, string uid, bool is_notice)
|
|
||||||
{
|
|
||||||
this.cid = cid;
|
|
||||||
this.uid = uid;
|
|
||||||
this.is_notice = is_notice;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
[Table("chat_message")]
|
|
||||||
public class Chat_Message
|
|
||||||
{
|
|
||||||
|
|
||||||
[Required(ErrorMessage = "필수 항목 누락")]
|
|
||||||
[MaxLength(36)]
|
|
||||||
public required string mid { get; set; } // UUID
|
|
||||||
|
|
||||||
[Required(ErrorMessage = "필수 항목 누락")]
|
|
||||||
[MaxLength(16)]
|
|
||||||
public required string cid { get; set; }
|
|
||||||
|
|
||||||
[Required(ErrorMessage = "필수 항목 누락")]
|
|
||||||
[MaxLength(70)]
|
|
||||||
public required string uid { get; set; }
|
|
||||||
|
|
||||||
[Required(ErrorMessage = "필수 항목 누락")]
|
|
||||||
public required DateTime create_date { get; set; }
|
|
||||||
[Required(ErrorMessage = "필수 항목 누락")]
|
|
||||||
public required string content { get; set; }
|
|
||||||
[Required(ErrorMessage = "필수 항목 누락")]
|
|
||||||
public required bool is_hidden { get; set; }
|
|
||||||
|
|
||||||
public string? media_url { get; set; }
|
|
||||||
|
|
||||||
[Required(ErrorMessage = "필수 항목 누락")]
|
|
||||||
public required int read_count { get; set; }
|
|
||||||
|
|
||||||
[Required(ErrorMessage = "필수 항목 누락")]
|
|
||||||
public required bool is_blind { get; set; }
|
|
||||||
|
|
||||||
[Required(ErrorMessage = "필수 항목 누락")]
|
|
||||||
public required bool check_report { get; set; }
|
|
||||||
|
|
||||||
public Chat_Message(string mid, string cid, string uid, DateTime create_date, string content, bool is_hidden,
|
|
||||||
int read_count, bool is_blind, bool check_report)
|
|
||||||
{
|
|
||||||
this.mid = mid;
|
|
||||||
this.cid = cid;
|
|
||||||
this.uid = uid;
|
|
||||||
this.create_date = create_date;
|
|
||||||
this.content = content;
|
|
||||||
this.is_hidden = is_hidden;
|
|
||||||
this.read_count = read_count;
|
|
||||||
this.check_report = check_report;
|
|
||||||
this.is_blind = is_blind;
|
|
||||||
this.check_report = check_report;
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
[Table("chat_report_list")]
|
|
||||||
public class Chat_Report_List
|
|
||||||
{
|
|
||||||
|
|
||||||
[Required(ErrorMessage = "필수 항목 누락")]
|
|
||||||
[MaxLength(36)]
|
|
||||||
public required string mid { get; set; } // UUID
|
|
||||||
|
|
||||||
[Required(ErrorMessage = "필수 항목 누락")]
|
|
||||||
[MaxLength(16)]
|
|
||||||
public required string cid { get; set; }
|
|
||||||
|
|
||||||
[Required(ErrorMessage = "필수 항목 누락")]
|
|
||||||
[MaxLength(70)]
|
|
||||||
public required string uid { get; set; }
|
|
||||||
|
|
||||||
[Required(ErrorMessage = "필수 항목 누락")]
|
|
||||||
public required string report { get; set; }
|
|
||||||
|
|
||||||
public Chat_Report_List(string mid, string cid, string uid, string report)
|
|
||||||
{
|
|
||||||
this.mid = mid;
|
|
||||||
this.cid = cid;
|
|
||||||
this.uid = uid;
|
|
||||||
this.report = report;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,44 +0,0 @@
|
||||||
using System.ComponentModel.DataAnnotations;
|
|
||||||
using System.ComponentModel.DataAnnotations.Schema;
|
|
||||||
using System.Security.Cryptography.X509Certificates;
|
|
||||||
using Microsoft.EntityFrameworkCore.Metadata.Internal;
|
|
||||||
|
|
||||||
namespace AcaMate.V1.Models;
|
|
||||||
|
|
||||||
|
|
||||||
[Table("log_project")]
|
|
||||||
public class LogProject
|
|
||||||
{
|
|
||||||
[Key]
|
|
||||||
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
|
|
||||||
public int lid { get; set; }
|
|
||||||
public DateTime create_date {get; set;}
|
|
||||||
public string log { get; set; }
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
[Table("log_push")]
|
|
||||||
public class LogPush
|
|
||||||
{
|
|
||||||
[Key]
|
|
||||||
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
|
|
||||||
public int lid { get; set; }
|
|
||||||
public string bid {get; set;}
|
|
||||||
public string pid {get; set;}
|
|
||||||
public DateTime create_date {get; set;}
|
|
||||||
public string create_uid {get; set;}
|
|
||||||
public string log { get; set; }
|
|
||||||
}
|
|
||||||
|
|
||||||
[Table("log_user")]
|
|
||||||
public class LogUser
|
|
||||||
{
|
|
||||||
[Key]
|
|
||||||
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
|
|
||||||
public int lid { get; set; }
|
|
||||||
public string uid {get; set;}
|
|
||||||
public DateTime create_date {get; set;}
|
|
||||||
public string create_uid {get; set;}
|
|
||||||
public string log { get; set; }
|
|
||||||
}
|
|
|
@ -1,178 +0,0 @@
|
||||||
using System.ComponentModel.DataAnnotations;
|
|
||||||
using System.ComponentModel.DataAnnotations.Schema;
|
|
||||||
using System.Security.Cryptography.X509Certificates;
|
|
||||||
using Microsoft.EntityFrameworkCore.Metadata.Internal;
|
|
||||||
|
|
||||||
namespace AcaMate.V1.Models;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* iOS Payload 정의
|
|
||||||
* aps 딕셔너리 구성
|
|
||||||
* 1. alert : 실질적으로 사용자에게 노출되는 항목
|
|
||||||
* 1.1. title : 제목
|
|
||||||
* 1.2. subtitle : 부제목
|
|
||||||
* 1.3. body : 본문 내용
|
|
||||||
* 2. badge : 앱 아이콘에 표시할 숫자
|
|
||||||
* 3. sound : 소리인데 "default" 가능
|
|
||||||
* 4. content-available : 백그라운드 업데이트나 사일런트 푸시 송신시 사용한다.
|
|
||||||
* 1로 설정해 사용자가 직접 보지 않아도 앱이 업데이트 되게 할 수 있으며,
|
|
||||||
* UI에 알림이 표시되지 않고 데이터만 전달할 때 필요하다.
|
|
||||||
* 5. mutable-content : 값을 1로 설정해두면 Notification Service Extension을 통해 알림의 내용을 변경할 수 있다.
|
|
||||||
* 6. category : 사용자 인터렉션 구성시에 사용하는 식별자이다.
|
|
||||||
* 7. thread-id : 관련 알림들을 그룹화해 관리할 때 사용한다.
|
|
||||||
*/
|
|
||||||
|
|
||||||
/*
|
|
||||||
* FCM Payload
|
|
||||||
* https://firebase.google.com/docs/reference/fcm/rest/v1/projects.messages?hl=ko&_gl=1*3awp3i*_up*MQ..*_ga*MTMyNDk4ODU5MC4xNzQxMTM1MzI3*_ga_CW55HF8NVT*MTc0MTEzNTMyNy4xLjAuMTc0MTEzNTM4My4wLjAuMA..#Notification
|
|
||||||
* {
|
|
||||||
"name": string,
|
|
||||||
"data": { // 입력 전용으로 임의의 가
|
|
||||||
string: string,
|
|
||||||
...
|
|
||||||
},
|
|
||||||
"notification": {
|
|
||||||
object (Notification)
|
|
||||||
},
|
|
||||||
"android": {
|
|
||||||
object (AndroidConfig)
|
|
||||||
},
|
|
||||||
"webpush": {
|
|
||||||
object (WebpushConfig)
|
|
||||||
},
|
|
||||||
"apns": {
|
|
||||||
object (ApnsConfig)
|
|
||||||
},
|
|
||||||
"fcm_options": {
|
|
||||||
object (FcmOptions)
|
|
||||||
},
|
|
||||||
|
|
||||||
// Union field target can be only one of the following:
|
|
||||||
"token": string,
|
|
||||||
"topic": string,
|
|
||||||
"condition": string
|
|
||||||
// End of list of possible types for union field target.
|
|
||||||
}
|
|
||||||
* 1. Notification 영역
|
|
||||||
* 1.1. title: 알림 제목
|
|
||||||
* 1.2. body: 알림 내용
|
|
||||||
* 1.3. image: 알림에 표시할 이미지 URL
|
|
||||||
* 1.4. sound: 알림 재생 시 사용할 사운드
|
|
||||||
* 1.5. click_action: 알림 클릭 시 실행할 액션(인텐트 액션 문자열)
|
|
||||||
* 1.6. tag: 동일 태그를 가진 알림끼리 대체 또는 그룹화할 때 사용
|
|
||||||
* 1.7. color: 알림 아이콘 배경색 (16진수 코드 등)
|
|
||||||
* 1.8. body_loc_key 및 body_loc_args: 본문에 사용할 지역화 키와 인자 배열
|
|
||||||
* 1.9. title_loc_key 및 title_loc_args: 제목에 사용할 지역화 키와 인자 배열
|
|
||||||
* 1.10. channel_id: Android Oreo 이상에서 알림 채널 식별자
|
|
||||||
* 1.11. ticker, sticky, event_time, notification_priority, visibility, notification_count, light_settings, vibrate_timings 등: 사용자 경험이나 알림의 동작 방식을 세밀하게 제어할 때 사용
|
|
||||||
* 2. Data 영역 : 임의의 키-값 쌍을 포함하고 UI에 표시되는 내용이 아닌 앱의 로직에 활용할 데이터를 보낼 때 사용한다.
|
|
||||||
* 3. 기타 전송 옵션
|
|
||||||
* 3.1. priority: 메시지 전송 우선순위 (“high” 또는 “normal”)
|
|
||||||
* 3.2. time_to_live (ttl): 메시지 유효 시간(초 단위)
|
|
||||||
* 3.3. collapse_key: 동일 collapse_key를 가진 메시지는 최신 하나로 교체됨
|
|
||||||
* 3.4. restricted_package_name: 메시지를 수신할 앱의 패키지 이름 (Android 전용)
|
|
||||||
*/
|
|
||||||
|
|
||||||
[Table("payload")]
|
|
||||||
public class DBPayload
|
|
||||||
{
|
|
||||||
public string bid { get; set; }
|
|
||||||
public string pid { get; set; }
|
|
||||||
public string title {get; set;}
|
|
||||||
public string? subtitle {get; set;}
|
|
||||||
public string body {get; set;}
|
|
||||||
public bool alert_yn {get; set;}
|
|
||||||
public string category {get; set;}
|
|
||||||
public string? content {get; set;}
|
|
||||||
}
|
|
||||||
|
|
||||||
[Table("push_cabinet")]
|
|
||||||
public class PushCabinet
|
|
||||||
{
|
|
||||||
[Key]
|
|
||||||
public int id { get; set; }
|
|
||||||
public string uid { get; set; }
|
|
||||||
public string pid { get; set; }
|
|
||||||
public string bid { get; set; }
|
|
||||||
public DateTime send_date { get; set; }
|
|
||||||
public bool check_yn { get; set; }
|
|
||||||
public string? content {get; set;}
|
|
||||||
}
|
|
||||||
|
|
||||||
public class PushRequest
|
|
||||||
{
|
|
||||||
public string bid { get; set; }
|
|
||||||
public List<string> uids { get; set; }
|
|
||||||
public string pid { get; set; }
|
|
||||||
|
|
||||||
public string? content { get; set; }
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
public class Payload
|
|
||||||
{
|
|
||||||
public Aps aps { get; set; }
|
|
||||||
public string pid { get; set; }
|
|
||||||
public string bid { get; set; }
|
|
||||||
public string content { get; set; }
|
|
||||||
// public string customKey { get; set; } 이런식으로 추가도 가능
|
|
||||||
|
|
||||||
public string ToJson()
|
|
||||||
{
|
|
||||||
return System.Text.Json.JsonSerializer.Serialize(this);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public class Aps
|
|
||||||
{
|
|
||||||
public Aps()
|
|
||||||
{
|
|
||||||
sound = "default";
|
|
||||||
content_available = 1;
|
|
||||||
}
|
|
||||||
[Required(ErrorMessage = "필수 입력 누락 (alert)")]
|
|
||||||
public Alert alert { get; set; }
|
|
||||||
|
|
||||||
[Required(ErrorMessage = "필수 입력 누락 (badge")]
|
|
||||||
public int badge { get; set; } // 앱 아이콘 표시 배지 숫자 설정
|
|
||||||
public string sound { get; set; } // 사운드 파일 이름 default = "default"
|
|
||||||
public int content_available { get; set; } // 백그라운드 알림 활성화: 필수 (1)
|
|
||||||
public string? category { get; set; } // 알림에 대한 특정 액션을 정의
|
|
||||||
}
|
|
||||||
|
|
||||||
public class Alert
|
|
||||||
{
|
|
||||||
[Required(ErrorMessage = "필수 입력 누락 (title")]
|
|
||||||
public string title { get; set; } // 제목
|
|
||||||
[Required(ErrorMessage = "필수 입력 누락 (body)")]
|
|
||||||
public string body { get; set; } // 내용
|
|
||||||
public string? subtitle { get; set; } // 부제목 (선택)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 푸시 등록하기 위한 apns 여러 데이터 목록
|
|
||||||
/// </summary>
|
|
||||||
public class PushFileSetting
|
|
||||||
{
|
|
||||||
public string uri { get; set; }
|
|
||||||
public string p12Path { get; set; }
|
|
||||||
public string p12PWPath { get; set; }
|
|
||||||
public string apnsTopic { get; set; }
|
|
||||||
}
|
|
||||||
|
|
||||||
public class PushData
|
|
||||||
{
|
|
||||||
public string pushToken { get; set; }
|
|
||||||
public Payload payload { get; set; }
|
|
||||||
}
|
|
||||||
|
|
||||||
public class CreatePush
|
|
||||||
{
|
|
||||||
public string bid { get; set; }
|
|
||||||
public string title { get; set; }
|
|
||||||
public string? subtitle { get; set; }
|
|
||||||
public string body { get; set; }
|
|
||||||
public bool alert_yn { get; set; } = true;
|
|
||||||
public string category { get; set; }
|
|
||||||
public string? content { get; set; }
|
|
||||||
}
|
|
|
@ -1,149 +0,0 @@
|
||||||
using System.ComponentModel.DataAnnotations;
|
|
||||||
using System.ComponentModel.DataAnnotations.Schema;
|
|
||||||
using System.Runtime.InteropServices.JavaScript;
|
|
||||||
|
|
||||||
namespace AcaMate.V1.Models;
|
|
||||||
|
|
||||||
[Table("login")]
|
|
||||||
public class Login
|
|
||||||
{
|
|
||||||
[Key]
|
|
||||||
[Required(ErrorMessage = "필수 항목 누락")]
|
|
||||||
[MaxLength(100)]
|
|
||||||
public string sns_id {get; set;}
|
|
||||||
|
|
||||||
[Required(ErrorMessage = "필수 항목 누락")]
|
|
||||||
[MaxLength(70)]
|
|
||||||
public string uid {get; set;}
|
|
||||||
|
|
||||||
[Required(ErrorMessage = "필수 항목 누락")]
|
|
||||||
[MaxLength(4)]
|
|
||||||
public string sns_type {get; set;}
|
|
||||||
}
|
|
||||||
|
|
||||||
[Table("user_academy")]
|
|
||||||
public class User_Academy
|
|
||||||
{
|
|
||||||
[Key]
|
|
||||||
[Required(ErrorMessage = "필수 항목 누락")]
|
|
||||||
public string uid { get; set; }
|
|
||||||
|
|
||||||
[Required(ErrorMessage = "필수 항목 누락")]
|
|
||||||
public string bid { get; set; }
|
|
||||||
|
|
||||||
[Required(ErrorMessage = "필수 항목 누락")]
|
|
||||||
public DateTime register_date { get; set; }
|
|
||||||
|
|
||||||
[Required(ErrorMessage = "필수 항목 누락")]
|
|
||||||
public bool status { get; set; }
|
|
||||||
}
|
|
||||||
|
|
||||||
[Table("user")]
|
|
||||||
public class User
|
|
||||||
{
|
|
||||||
[Key]
|
|
||||||
[Required(ErrorMessage = "필수 항목 누락")]
|
|
||||||
public string uid { get; set; }
|
|
||||||
|
|
||||||
[Required(ErrorMessage = "필수 항목 누락")]
|
|
||||||
public string name { get; set; }
|
|
||||||
public DateTime? birth { get; set; }
|
|
||||||
|
|
||||||
[Required(ErrorMessage = "필수 항목 누락")]
|
|
||||||
public string type { get; set; }
|
|
||||||
|
|
||||||
public string? device_id { get; set; }
|
|
||||||
|
|
||||||
[Required(ErrorMessage = "필수 항목 누락")]
|
|
||||||
public bool auto_login_yn { get; set; }
|
|
||||||
|
|
||||||
[Required(ErrorMessage = "필수 항목 누락")]
|
|
||||||
public DateTime login_date { get; set; }
|
|
||||||
public string? push_token { get; set; }
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
[Table("permission")]
|
|
||||||
public class Permission
|
|
||||||
{
|
|
||||||
[Key]
|
|
||||||
|
|
||||||
[Required(ErrorMessage = "필수 항목 누락")]
|
|
||||||
public string uid { get; set; }
|
|
||||||
|
|
||||||
public bool location_yn {get; set;}
|
|
||||||
public bool camera_yn {get; set;}
|
|
||||||
public bool photo_yn {get; set;}
|
|
||||||
public bool push_yn {get; set;}
|
|
||||||
public bool market_app_yn {get; set;}
|
|
||||||
public bool market_sms_yn {get; set;}
|
|
||||||
public bool market_email_yn {get; set;}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
[Table("location")]
|
|
||||||
public class Location
|
|
||||||
{
|
|
||||||
[Key]
|
|
||||||
|
|
||||||
[Required(ErrorMessage = "필수 항목 누락")]
|
|
||||||
public string uid { get; set; }
|
|
||||||
|
|
||||||
[Required(ErrorMessage = "필수 항목 누락")]
|
|
||||||
public string lat { get; set; }
|
|
||||||
|
|
||||||
[Required(ErrorMessage = "필수 항목 누락")]
|
|
||||||
public string lng { get; set; }
|
|
||||||
}
|
|
||||||
|
|
||||||
[Table("contact")]
|
|
||||||
public class Contact
|
|
||||||
{
|
|
||||||
[Key]
|
|
||||||
|
|
||||||
[Required(ErrorMessage = "필수 항목 누락")]
|
|
||||||
public string uid { get; set; }
|
|
||||||
|
|
||||||
[Required(ErrorMessage = "필수 항목 누락")]
|
|
||||||
public string email { get; set; }
|
|
||||||
public string? phone { get; set; }
|
|
||||||
public string? address { get; set; }
|
|
||||||
}
|
|
||||||
|
|
||||||
// -- -- -- -- -- DB 테이블 -- -- -- -- -- //
|
|
||||||
|
|
||||||
public class UserAll
|
|
||||||
{
|
|
||||||
[Required(ErrorMessage = "필수 항목 누락")]
|
|
||||||
public string name { get; set; }
|
|
||||||
|
|
||||||
public DateTime? birth { get; set; }
|
|
||||||
|
|
||||||
[Required(ErrorMessage = "필수 항목 누락")]
|
|
||||||
public string type { get; set; }
|
|
||||||
|
|
||||||
public string? device_id { get; set; }
|
|
||||||
|
|
||||||
public bool auto_login_yn { get; set; }
|
|
||||||
public DateTime login_date { get; set; }
|
|
||||||
public string? push_token { get; set; }
|
|
||||||
|
|
||||||
[Required(ErrorMessage = "필수 항목 누락")]
|
|
||||||
public string email { get; set; }
|
|
||||||
public string? phone { get; set; }
|
|
||||||
public string? address { get; set; }
|
|
||||||
|
|
||||||
public bool location_yn {get; set;}
|
|
||||||
public bool camera_yn {get; set;}
|
|
||||||
public bool photo_yn {get; set;}
|
|
||||||
public bool push_yn {get; set;}
|
|
||||||
public bool market_app_yn {get; set;}
|
|
||||||
public bool market_sms_yn {get; set;}
|
|
||||||
public bool market_email_yn {get; set;}
|
|
||||||
|
|
||||||
[Required(ErrorMessage = "필수 항목 누락")]
|
|
||||||
public string sns_id {get; set;}
|
|
||||||
|
|
||||||
[Required(ErrorMessage = "필수 항목 누락")]
|
|
||||||
public string sns_type {get; set;}
|
|
||||||
}
|
|
|
@ -1,21 +0,0 @@
|
||||||
|
|
||||||
using System.ComponentModel.DataAnnotations;
|
|
||||||
using System.ComponentModel.DataAnnotations.Schema;
|
|
||||||
|
|
||||||
namespace AcaMate.V1.Models;
|
|
||||||
|
|
||||||
[Table("version")]
|
|
||||||
public class Version
|
|
||||||
{
|
|
||||||
[Key]
|
|
||||||
[MaxLength(4)]
|
|
||||||
public string os_type { get; set; }
|
|
||||||
[MaxLength(8)]
|
|
||||||
public string final_ver { get; set; }
|
|
||||||
[MaxLength(8)]
|
|
||||||
public string dev_ver { get; set; }
|
|
||||||
[MaxLength(8)]
|
|
||||||
public string force_ver { get; set; }
|
|
||||||
public bool choice_update_yn { get; set; }
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,28 +0,0 @@
|
||||||
using Microsoft.AspNetCore.Mvc;
|
|
||||||
|
|
||||||
using System.Linq;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
using Microsoft.EntityFrameworkCore;
|
|
||||||
using AcaMate.Common.Data;
|
|
||||||
using AcaMate.Common.Models;
|
|
||||||
using AcaMate.V1.Models;
|
|
||||||
|
|
||||||
namespace AcaMate.V1.Repositories;
|
|
||||||
|
|
||||||
public class UserRepository
|
|
||||||
{
|
|
||||||
|
|
||||||
private readonly AppDbContext _context;
|
|
||||||
|
|
||||||
|
|
||||||
public UserRepository(AppDbContext context) {
|
|
||||||
_context = context;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
public async Task<IEnumerable<UserAcademyResult>> GetUserAcademyInfoBySnsIdAsync(string snsId)
|
|
||||||
{
|
|
||||||
|
|
||||||
}*/
|
|
||||||
}
|
|
|
@ -1,63 +0,0 @@
|
||||||
|
|
||||||
using System.Collections.Concurrent;
|
|
||||||
using System.Threading;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
using AcaMate.V1.Models;
|
|
||||||
using Microsoft.Extensions.Hosting;
|
|
||||||
|
|
||||||
namespace AcaMate.V1.Services;
|
|
||||||
|
|
||||||
public interface IPushQueue
|
|
||||||
{
|
|
||||||
void Enqueue(PushData pushData);
|
|
||||||
Task<PushData> DequeueAsync(CancellationToken cancellationToken);
|
|
||||||
}
|
|
||||||
|
|
||||||
public class InMemoryPushQueue: IPushQueue
|
|
||||||
{
|
|
||||||
private readonly ConcurrentQueue<PushData> _queue = new ConcurrentQueue<PushData>();
|
|
||||||
private readonly SemaphoreSlim _signal = new SemaphoreSlim(0);
|
|
||||||
|
|
||||||
public void Enqueue(PushData pushData)
|
|
||||||
{
|
|
||||||
if( pushData is null )
|
|
||||||
throw new ArgumentNullException(nameof(pushData));
|
|
||||||
_queue.Enqueue(pushData);
|
|
||||||
_signal.Release();
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task<PushData> DequeueAsync(CancellationToken cancellationToken)
|
|
||||||
{
|
|
||||||
await _signal.WaitAsync(cancellationToken);
|
|
||||||
_queue.TryDequeue(out var pushData);
|
|
||||||
return pushData;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public class PushBackgroundService : BackgroundService
|
|
||||||
{
|
|
||||||
private readonly IPushQueue _queue;
|
|
||||||
private readonly IApnsPushService _pushService;
|
|
||||||
|
|
||||||
public PushBackgroundService(IPushQueue queue, IApnsPushService pushService)
|
|
||||||
{
|
|
||||||
_queue = queue;
|
|
||||||
_pushService = pushService;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
|
|
||||||
{
|
|
||||||
while (!stoppingToken.IsCancellationRequested)
|
|
||||||
{
|
|
||||||
var pushData = await _queue.DequeueAsync(stoppingToken);
|
|
||||||
try
|
|
||||||
{
|
|
||||||
await _pushService.SendPushNotificationAsync(pushData.pushToken, pushData.payload);
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
Console.WriteLine($"푸시 전송 실패: {ex.Message}");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,238 +0,0 @@
|
||||||
using AcaMate.Common.Data;
|
|
||||||
using AcaMate.Common.Token;
|
|
||||||
using AcaMate.Common.Models;
|
|
||||||
|
|
||||||
|
|
||||||
using Microsoft.AspNetCore.Mvc;
|
|
||||||
using Microsoft.EntityFrameworkCore;
|
|
||||||
using Microsoft.IdentityModel.Tokens;
|
|
||||||
using Microsoft.VisualBasic;
|
|
||||||
|
|
||||||
using System.Linq.Expressions;
|
|
||||||
using System.Security.Claims;
|
|
||||||
using System.Reflection;
|
|
||||||
|
|
||||||
using AcaMate.V1.Models;
|
|
||||||
|
|
||||||
namespace AcaMate.V1.Services;
|
|
||||||
|
|
||||||
public interface IRepositoryService
|
|
||||||
{
|
|
||||||
// Task<ValidateToken> ValidateToken(string token, string refresh);
|
|
||||||
Task<bool> SaveData<T>(T entity, Expression<Func<T, object>> key = null) where T : class;
|
|
||||||
Task<bool> DeleteData<T>(T entity, Expression<Func<T, object>> key = null) where T : class;
|
|
||||||
String ReadSummary(Type type, String name);
|
|
||||||
}
|
|
||||||
|
|
||||||
public class RepositoryService: IRepositoryService
|
|
||||||
{
|
|
||||||
private readonly AppDbContext _dbContext;
|
|
||||||
private readonly ILogger<RepositoryService> _logger;
|
|
||||||
private readonly JwtTokenService _jwtTokenService;
|
|
||||||
|
|
||||||
public RepositoryService(AppDbContext dbContext, ILogger<RepositoryService> logger, JwtTokenService jwtTokenService)
|
|
||||||
{
|
|
||||||
_dbContext = dbContext;
|
|
||||||
_logger = logger;
|
|
||||||
_jwtTokenService = jwtTokenService;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// public async Task<ValidateToken> ValidateToken(string token, string refresh)
|
|
||||||
// {
|
|
||||||
// var principalToken = await _jwtTokenService.ValidateToken(token);
|
|
||||||
// if (principalToken != null)
|
|
||||||
// {
|
|
||||||
// var uid = principalToken.FindFirst(ClaimTypes.NameIdentifier)?.Value ?? string.Empty;
|
|
||||||
// _logger.LogInformation($"토큰 변환 - {uid}");
|
|
||||||
// return new ValidateToken
|
|
||||||
// {
|
|
||||||
// token = token,
|
|
||||||
// refresh = refresh,
|
|
||||||
// uid = uid
|
|
||||||
// };
|
|
||||||
// }
|
|
||||||
// else
|
|
||||||
// {
|
|
||||||
// _logger.LogInformation("엑세스 토큰 만료");
|
|
||||||
// // var refreshToken = await _dbContext.RefreshTokens
|
|
||||||
// // .FirstOrDefaultAsync(t => t.refresh_token == refresh);
|
|
||||||
// // if (refreshToken == null)
|
|
||||||
// // throw new TokenException("입력 받은 토큰 자체의 문제");
|
|
||||||
// //
|
|
||||||
// // var uid = refreshToken.uid;
|
|
||||||
// //
|
|
||||||
// // if (refreshToken.revoke_Date < DateTime.Now)
|
|
||||||
// // throw new RefreshRevokeException("리프레시 토큰 해지");
|
|
||||||
// //
|
|
||||||
// // if (refreshToken.expire_date > DateTime.Now)
|
|
||||||
// // {
|
|
||||||
// // _logger.LogInformation($"인증 완료 리프레시 : {uid}");
|
|
||||||
// // var access = _jwtTokenService.GenerateJwtToken(uid);
|
|
||||||
// //
|
|
||||||
// // return new ValidateToken
|
|
||||||
// // {
|
|
||||||
// // token = access,
|
|
||||||
// // refresh = refreshToken.refresh_token,
|
|
||||||
// // uid = uid
|
|
||||||
// // };
|
|
||||||
// // }
|
|
||||||
// // else
|
|
||||||
// // {
|
|
||||||
// // refreshToken = _jwtTokenService.GenerateRefreshToken(uid);
|
|
||||||
// // _logger.LogInformation("리프레시 토큰 만료");
|
|
||||||
// // // await SaveData<RefreshToken, string>(refreshToken, rt => rt.uid);
|
|
||||||
// // return new ValidateToken
|
|
||||||
// // {
|
|
||||||
// // token = token,
|
|
||||||
// // refresh = refreshToken.refresh_token,
|
|
||||||
// // uid = uid
|
|
||||||
// // };
|
|
||||||
// // }
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
public async Task<bool> SaveData<T>(T entity, Expression<Func<T, object>> key = null) where T : class
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
if (key != null)
|
|
||||||
{
|
|
||||||
// key를 가지고 EF 로 돌리는게 아니라 내가 조건을 넣어서 하는 경우에 사용함
|
|
||||||
var value = key.Compile()(entity);
|
|
||||||
var parameter = Expression.Parameter(typeof(T), "x");
|
|
||||||
var invokedExpr = Expression.Invoke(key, parameter);
|
|
||||||
var constantExpr = Expression.Constant(value, key.Body.Type);
|
|
||||||
var equalsExpr = Expression.Equal(invokedExpr, constantExpr);
|
|
||||||
var predicate = Expression.Lambda<Func<T, bool>>(equalsExpr, parameter);
|
|
||||||
var entityData = await _dbContext.Set<T>().FirstOrDefaultAsync(predicate);
|
|
||||||
|
|
||||||
if (entityData != null)
|
|
||||||
{
|
|
||||||
_logger.LogInformation($"[{typeof(T)}] 해당 PK 존재 = [{value}]: 계속");
|
|
||||||
_dbContext.Entry(entityData).CurrentValues.SetValues(entity);
|
|
||||||
if (!(_dbContext.Entry(entityData).Properties.Any(p => p.IsModified)))
|
|
||||||
{
|
|
||||||
_logger.LogInformation($"[{typeof(T)}] 변경 사항 없음");
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
_logger.LogInformation($"[{typeof(T)}] 변경 사항이 존재");
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
_logger.LogInformation($"[{typeof(T)}] 처음등록");
|
|
||||||
_dbContext.Set<T>().Add(entity);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// EF 로 직접 키를 잡아서 사용 (관계키나 이런거 할때도 노상관됨)
|
|
||||||
|
|
||||||
// 모델이 존재하지 않거나 기본 키 정의가 되지 않은 오류가 발생할 건데 그건 운영 단계에서는 오류 나면 안되는거니
|
|
||||||
var keyProperties = _dbContext.Model.FindEntityType(typeof(T)).FindPrimaryKey().Properties;
|
|
||||||
|
|
||||||
// 각 키 속성에 대해, entity에서 실제 키 값을 추출
|
|
||||||
var keyValues = keyProperties.Select(p => typeof(T).GetProperty(p.Name).GetValue(entity)).ToArray();
|
|
||||||
|
|
||||||
// 기본 키 값을 이용해서 기존 엔티티를 찾음 (복합 키도 자동으로 처리됨)
|
|
||||||
var existingEntity = await _dbContext.Set<T>().FindAsync(keyValues);
|
|
||||||
|
|
||||||
if (existingEntity != null)
|
|
||||||
{
|
|
||||||
_logger.LogInformation($"[{typeof(T)}] 기존 데이터 발견: 기본 키 값({string.Join(", ", keyValues)})");
|
|
||||||
// 기존 엔티티를 업데이트: 새 entity의 값으로 교체
|
|
||||||
_dbContext.Entry(existingEntity).CurrentValues.SetValues(entity);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
_logger.LogInformation($"[{typeof(T)}] 신규 데이터 등록: 기본 키 값({string.Join(", ", keyValues)})");
|
|
||||||
// 데이터가 없으면 새 엔티티 추가
|
|
||||||
_dbContext.Set<T>().Add(entity);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
await _dbContext.SaveChangesAsync();
|
|
||||||
_logger.LogInformation($"[{typeof(T)}] DB 저장 완료: 종료");
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
_logger.LogInformation($"[{typeof(T)}] 저장 중 알 수 없는 오류 발생: {ex}");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
public async Task<bool> DeleteData<T>(T entity, Expression<Func<T, object>> key = null) where T : class
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
if (key != null)
|
|
||||||
{
|
|
||||||
// key를 통해 조건식을 만들어 삭제할 엔티티를 찾는 경우
|
|
||||||
var value = key.Compile()(entity);
|
|
||||||
var parameter = Expression.Parameter(typeof(T), "x");
|
|
||||||
var invokedExpr = Expression.Invoke(key, parameter);
|
|
||||||
var constantExpr = Expression.Constant(value, key.Body.Type);
|
|
||||||
var equalsExpr = Expression.Equal(invokedExpr, constantExpr);
|
|
||||||
var predicate = Expression.Lambda<Func<T, bool>>(equalsExpr, parameter);
|
|
||||||
|
|
||||||
var entityData = await _dbContext.Set<T>().FirstOrDefaultAsync(predicate);
|
|
||||||
if (entityData == null)
|
|
||||||
{
|
|
||||||
_logger.LogInformation($"[{typeof(T)}] 삭제 대상 데이터가 존재하지 않습니다. (값 = {value})");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
_logger.LogInformation($"[{typeof(T)}] 조건에 맞는 데이터 발견 (값 = {value}): 삭제 진행");
|
|
||||||
_dbContext.Set<T>().Remove(entityData);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// key가 없는 경우 EF Core 메타데이터를 사용하여 기본 키를 통한 삭제
|
|
||||||
var entityType = _dbContext.Model.FindEntityType(typeof(T));
|
|
||||||
if (entityType == null)
|
|
||||||
{
|
|
||||||
throw new InvalidOperationException($"Entity type '{typeof(T).Name}'이 모델에 존재하지 않습니다.");
|
|
||||||
}
|
|
||||||
|
|
||||||
var primaryKey = entityType.FindPrimaryKey();
|
|
||||||
if (primaryKey == null)
|
|
||||||
{
|
|
||||||
throw new InvalidOperationException($"Entity type '{typeof(T).Name}'에 기본 키가 정의되어 있지 않습니다.");
|
|
||||||
}
|
|
||||||
|
|
||||||
var keyProperties = primaryKey.Properties;
|
|
||||||
var keyValues = keyProperties.Select(p => typeof(T).GetProperty(p.Name).GetValue(entity)).ToArray();
|
|
||||||
|
|
||||||
var existingEntity = await _dbContext.Set<T>().FindAsync(keyValues);
|
|
||||||
if (existingEntity == null)
|
|
||||||
{
|
|
||||||
_logger.LogInformation($"[{typeof(T)}] 기본 키 값({string.Join(", ", keyValues)})에 해당하는 삭제 대상 데이터가 존재하지 않습니다.");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
_logger.LogInformation($"[{typeof(T)}] 기본 키 값({string.Join(", ", keyValues)})에 해당하는 데이터 발견: 삭제 진행");
|
|
||||||
_dbContext.Set<T>().Remove(existingEntity);
|
|
||||||
}
|
|
||||||
|
|
||||||
await _dbContext.SaveChangesAsync();
|
|
||||||
_logger.LogInformation($"[{typeof(T)}] DB에서 삭제 완료");
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
_logger.LogError($"[{typeof(T)}] 삭제 중 오류 발생: {ex}");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
public string ReadSummary(Type type, string name)
|
|
||||||
{
|
|
||||||
var method = type.GetMethod(name) ?? throw new OutNULLException("swagger summary Load ERROR: NULL");
|
|
||||||
var att = method.GetCustomAttribute<CustomOperationAttribute>() ?? throw new OutNULLException("swagger summary Load ERROR: NULL");
|
|
||||||
return att.Summary;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,7 +0,0 @@
|
||||||
namespace AcaMate.V1.Services;
|
|
||||||
|
|
||||||
public class UserService
|
|
||||||
{
|
|
||||||
// priva
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,12 +1,11 @@
|
||||||
using Microsoft.Extensions.DependencyInjection;
|
|
||||||
using Microsoft.OpenApi.Models;
|
using Microsoft.OpenApi.Models;
|
||||||
using Swashbuckle.AspNetCore.Annotations;
|
|
||||||
using Swashbuckle.AspNetCore.SwaggerGen;
|
using Swashbuckle.AspNetCore.SwaggerGen;
|
||||||
|
|
||||||
|
namespace Back
|
||||||
[AttributeUsage(AttributeTargets.Method, AllowMultiple = false)]
|
|
||||||
public class CustomOperationAttribute : Attribute
|
|
||||||
{
|
{
|
||||||
|
[AttributeUsage(AttributeTargets.Method, AllowMultiple = false)]
|
||||||
|
public class CustomOperationAttribute : Attribute
|
||||||
|
{
|
||||||
public string Summary { get; }
|
public string Summary { get; }
|
||||||
public string Description { get; }
|
public string Description { get; }
|
||||||
public string[] Tags { get; }
|
public string[] Tags { get; }
|
||||||
|
@ -17,12 +16,12 @@ public class CustomOperationAttribute : Attribute
|
||||||
Description = description;
|
Description = description;
|
||||||
Tags = tags;
|
Tags = tags;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
public class CustomSwaggerOperationFilter : IOperationFilter
|
public class CustomSwaggerOperationFilter : IOperationFilter
|
||||||
{
|
{
|
||||||
public void Apply(OpenApiOperation operation, OperationFilterContext context)
|
public void Apply(OpenApiOperation operation, OperationFilterContext context)
|
||||||
{
|
{
|
||||||
var customSwaggerAttribute = context.MethodInfo.GetCustomAttributes(typeof(CustomOperationAttribute), false)
|
var customSwaggerAttribute = context.MethodInfo.GetCustomAttributes(typeof(CustomOperationAttribute), false)
|
||||||
|
@ -37,10 +36,10 @@ public class CustomSwaggerOperationFilter : IOperationFilter
|
||||||
.ToList();
|
.ToList();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static class SwaggerConfigure
|
public static class SwaggerConfigure
|
||||||
{
|
{
|
||||||
private static OpenApiInfo DocName(string title, string version)
|
private static OpenApiInfo DocName(string title, string version)
|
||||||
{
|
{
|
||||||
return new OpenApiInfo
|
return new OpenApiInfo
|
||||||
|
@ -89,4 +88,5 @@ public static class SwaggerConfigure
|
||||||
options.SwaggerEndpoint("/swagger/사업자 정보/swagger.json", "사업자 정보 API");
|
options.SwaggerEndpoint("/swagger/사업자 정보/swagger.json", "사업자 정보 API");
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
Loading…
Reference in New Issue
Block a user