feat: 메시지 CRUD API 구현 (#128)
All checks were successful
SPMS_API/pipeline/head This commit looks good
All checks were successful
SPMS_API/pipeline/head This commit looks good
Reviewed-on: https://git.ipstein.myds.me/SPMS/SPMS_API/pulls/129
This commit is contained in:
commit
dc487609b3
|
|
@ -3,6 +3,7 @@ using Swashbuckle.AspNetCore.Annotations;
|
||||||
using SPMS.Application.DTOs.Message;
|
using SPMS.Application.DTOs.Message;
|
||||||
using SPMS.Application.Interfaces;
|
using SPMS.Application.Interfaces;
|
||||||
using SPMS.Domain.Common;
|
using SPMS.Domain.Common;
|
||||||
|
using SPMS.Domain.Exceptions;
|
||||||
|
|
||||||
namespace SPMS.API.Controllers;
|
namespace SPMS.API.Controllers;
|
||||||
|
|
||||||
|
|
@ -20,6 +21,43 @@ public class MessageController : ControllerBase
|
||||||
_messageService = messageService;
|
_messageService = messageService;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[HttpPost("save")]
|
||||||
|
[SwaggerOperation(Summary = "메시지 저장", Description = "메시지 템플릿을 저장합니다. 메시지 코드가 자동 생성됩니다.")]
|
||||||
|
public async Task<IActionResult> SaveAsync([FromBody] MessageSaveRequestDto request)
|
||||||
|
{
|
||||||
|
var serviceId = GetServiceId();
|
||||||
|
var adminId = GetAdminId();
|
||||||
|
var result = await _messageService.SaveAsync(serviceId, adminId, request);
|
||||||
|
return Ok(ApiResponse<MessageSaveResponseDto>.Success(result, "메시지 저장 성공"));
|
||||||
|
}
|
||||||
|
|
||||||
|
[HttpPost("list")]
|
||||||
|
[SwaggerOperation(Summary = "메시지 목록 조회", Description = "서비스별 메시지 목록을 페이지 단위로 조회합니다.")]
|
||||||
|
public async Task<IActionResult> GetListAsync([FromBody] MessageListRequestDto request)
|
||||||
|
{
|
||||||
|
var serviceId = GetServiceId();
|
||||||
|
var result = await _messageService.GetListAsync(serviceId, request);
|
||||||
|
return Ok(ApiResponse<MessageListResponseDto>.Success(result));
|
||||||
|
}
|
||||||
|
|
||||||
|
[HttpPost("info")]
|
||||||
|
[SwaggerOperation(Summary = "메시지 상세 조회", Description = "메시지 코드로 상세 정보를 조회합니다. 템플릿 변수 목록을 포함합니다.")]
|
||||||
|
public async Task<IActionResult> GetInfoAsync([FromBody] MessageInfoRequestDto request)
|
||||||
|
{
|
||||||
|
var serviceId = GetServiceId();
|
||||||
|
var result = await _messageService.GetInfoAsync(serviceId, request);
|
||||||
|
return Ok(ApiResponse<MessageInfoResponseDto>.Success(result));
|
||||||
|
}
|
||||||
|
|
||||||
|
[HttpPost("delete")]
|
||||||
|
[SwaggerOperation(Summary = "메시지 삭제", Description = "메시지를 소프트 삭제합니다. 30일 후 스케줄러에 의해 완전 삭제됩니다.")]
|
||||||
|
public async Task<IActionResult> DeleteAsync([FromBody] MessageDeleteRequestDto request)
|
||||||
|
{
|
||||||
|
var serviceId = GetServiceId();
|
||||||
|
await _messageService.DeleteAsync(serviceId, request);
|
||||||
|
return Ok(ApiResponse<object?>.Success(null, "메시지 삭제 성공"));
|
||||||
|
}
|
||||||
|
|
||||||
[HttpPost("validate")]
|
[HttpPost("validate")]
|
||||||
[SwaggerOperation(Summary = "메시지 유효성 검사", Description = "메시지 내용의 유효성을 검사합니다.")]
|
[SwaggerOperation(Summary = "메시지 유효성 검사", Description = "메시지 내용의 유효성을 검사합니다.")]
|
||||||
public IActionResult ValidateAsync([FromBody] MessageValidateRequestDto request)
|
public IActionResult ValidateAsync([FromBody] MessageValidateRequestDto request)
|
||||||
|
|
@ -42,6 +80,15 @@ public class MessageController : ControllerBase
|
||||||
if (HttpContext.Items.TryGetValue("ServiceId", out var serviceIdObj) && serviceIdObj is long serviceId)
|
if (HttpContext.Items.TryGetValue("ServiceId", out var serviceIdObj) && serviceIdObj is long serviceId)
|
||||||
return serviceId;
|
return serviceId;
|
||||||
|
|
||||||
throw new Domain.Exceptions.SpmsException(ErrorCodes.BadRequest, "서비스 식별 정보가 없습니다.", 400);
|
throw new SpmsException(ErrorCodes.BadRequest, "서비스 식별 정보가 없습니다.", 400);
|
||||||
|
}
|
||||||
|
|
||||||
|
private long GetAdminId()
|
||||||
|
{
|
||||||
|
var adminIdClaim = User.FindFirst("adminId")?.Value;
|
||||||
|
if (string.IsNullOrEmpty(adminIdClaim) || !long.TryParse(adminIdClaim, out var adminId))
|
||||||
|
throw new SpmsException(ErrorCodes.Unauthorized, "인증 정보가 올바르지 않습니다.", 401);
|
||||||
|
|
||||||
|
return adminId;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
9
SPMS.Application/DTOs/Message/MessageDeleteRequestDto.cs
Normal file
9
SPMS.Application/DTOs/Message/MessageDeleteRequestDto.cs
Normal file
|
|
@ -0,0 +1,9 @@
|
||||||
|
using System.Text.Json.Serialization;
|
||||||
|
|
||||||
|
namespace SPMS.Application.DTOs.Message;
|
||||||
|
|
||||||
|
public class MessageDeleteRequestDto
|
||||||
|
{
|
||||||
|
[JsonPropertyName("message_code")]
|
||||||
|
public string MessageCode { get; set; } = string.Empty;
|
||||||
|
}
|
||||||
9
SPMS.Application/DTOs/Message/MessageInfoRequestDto.cs
Normal file
9
SPMS.Application/DTOs/Message/MessageInfoRequestDto.cs
Normal file
|
|
@ -0,0 +1,9 @@
|
||||||
|
using System.Text.Json.Serialization;
|
||||||
|
|
||||||
|
namespace SPMS.Application.DTOs.Message;
|
||||||
|
|
||||||
|
public class MessageInfoRequestDto
|
||||||
|
{
|
||||||
|
[JsonPropertyName("message_code")]
|
||||||
|
public string MessageCode { get; set; } = string.Empty;
|
||||||
|
}
|
||||||
36
SPMS.Application/DTOs/Message/MessageInfoResponseDto.cs
Normal file
36
SPMS.Application/DTOs/Message/MessageInfoResponseDto.cs
Normal file
|
|
@ -0,0 +1,36 @@
|
||||||
|
using System.Text.Json.Serialization;
|
||||||
|
|
||||||
|
namespace SPMS.Application.DTOs.Message;
|
||||||
|
|
||||||
|
public class MessageInfoResponseDto
|
||||||
|
{
|
||||||
|
[JsonPropertyName("message_code")]
|
||||||
|
public string MessageCode { get; set; } = string.Empty;
|
||||||
|
|
||||||
|
[JsonPropertyName("title")]
|
||||||
|
public string Title { get; set; } = string.Empty;
|
||||||
|
|
||||||
|
[JsonPropertyName("body")]
|
||||||
|
public string Body { get; set; } = string.Empty;
|
||||||
|
|
||||||
|
[JsonPropertyName("image_url")]
|
||||||
|
public string? ImageUrl { get; set; }
|
||||||
|
|
||||||
|
[JsonPropertyName("link_url")]
|
||||||
|
public string? LinkUrl { get; set; }
|
||||||
|
|
||||||
|
[JsonPropertyName("link_type")]
|
||||||
|
public string? LinkType { get; set; }
|
||||||
|
|
||||||
|
[JsonPropertyName("data")]
|
||||||
|
public object? Data { get; set; }
|
||||||
|
|
||||||
|
[JsonPropertyName("variables")]
|
||||||
|
public List<string> Variables { get; set; } = new();
|
||||||
|
|
||||||
|
[JsonPropertyName("is_active")]
|
||||||
|
public bool IsActive { get; set; }
|
||||||
|
|
||||||
|
[JsonPropertyName("created_at")]
|
||||||
|
public DateTime CreatedAt { get; set; }
|
||||||
|
}
|
||||||
18
SPMS.Application/DTOs/Message/MessageListRequestDto.cs
Normal file
18
SPMS.Application/DTOs/Message/MessageListRequestDto.cs
Normal file
|
|
@ -0,0 +1,18 @@
|
||||||
|
using System.Text.Json.Serialization;
|
||||||
|
|
||||||
|
namespace SPMS.Application.DTOs.Message;
|
||||||
|
|
||||||
|
public class MessageListRequestDto
|
||||||
|
{
|
||||||
|
[JsonPropertyName("page")]
|
||||||
|
public int Page { get; set; } = 1;
|
||||||
|
|
||||||
|
[JsonPropertyName("size")]
|
||||||
|
public int Size { get; set; } = 20;
|
||||||
|
|
||||||
|
[JsonPropertyName("keyword")]
|
||||||
|
public string? Keyword { get; set; }
|
||||||
|
|
||||||
|
[JsonPropertyName("is_active")]
|
||||||
|
public bool? IsActive { get; set; }
|
||||||
|
}
|
||||||
28
SPMS.Application/DTOs/Message/MessageListResponseDto.cs
Normal file
28
SPMS.Application/DTOs/Message/MessageListResponseDto.cs
Normal file
|
|
@ -0,0 +1,28 @@
|
||||||
|
using System.Text.Json.Serialization;
|
||||||
|
using SPMS.Application.DTOs.Notice;
|
||||||
|
|
||||||
|
namespace SPMS.Application.DTOs.Message;
|
||||||
|
|
||||||
|
public class MessageListResponseDto
|
||||||
|
{
|
||||||
|
[JsonPropertyName("items")]
|
||||||
|
public List<MessageSummaryDto> Items { get; set; } = new();
|
||||||
|
|
||||||
|
[JsonPropertyName("pagination")]
|
||||||
|
public PaginationDto Pagination { get; set; } = new();
|
||||||
|
}
|
||||||
|
|
||||||
|
public class MessageSummaryDto
|
||||||
|
{
|
||||||
|
[JsonPropertyName("message_code")]
|
||||||
|
public string MessageCode { get; set; } = string.Empty;
|
||||||
|
|
||||||
|
[JsonPropertyName("title")]
|
||||||
|
public string Title { get; set; } = string.Empty;
|
||||||
|
|
||||||
|
[JsonPropertyName("is_active")]
|
||||||
|
public bool IsActive { get; set; }
|
||||||
|
|
||||||
|
[JsonPropertyName("created_at")]
|
||||||
|
public DateTime CreatedAt { get; set; }
|
||||||
|
}
|
||||||
24
SPMS.Application/DTOs/Message/MessageSaveRequestDto.cs
Normal file
24
SPMS.Application/DTOs/Message/MessageSaveRequestDto.cs
Normal file
|
|
@ -0,0 +1,24 @@
|
||||||
|
using System.Text.Json.Serialization;
|
||||||
|
|
||||||
|
namespace SPMS.Application.DTOs.Message;
|
||||||
|
|
||||||
|
public class MessageSaveRequestDto
|
||||||
|
{
|
||||||
|
[JsonPropertyName("title")]
|
||||||
|
public string Title { get; set; } = string.Empty;
|
||||||
|
|
||||||
|
[JsonPropertyName("body")]
|
||||||
|
public string Body { get; set; } = string.Empty;
|
||||||
|
|
||||||
|
[JsonPropertyName("image_url")]
|
||||||
|
public string? ImageUrl { get; set; }
|
||||||
|
|
||||||
|
[JsonPropertyName("link_url")]
|
||||||
|
public string? LinkUrl { get; set; }
|
||||||
|
|
||||||
|
[JsonPropertyName("link_type")]
|
||||||
|
public string? LinkType { get; set; }
|
||||||
|
|
||||||
|
[JsonPropertyName("data")]
|
||||||
|
public object? Data { get; set; }
|
||||||
|
}
|
||||||
12
SPMS.Application/DTOs/Message/MessageSaveResponseDto.cs
Normal file
12
SPMS.Application/DTOs/Message/MessageSaveResponseDto.cs
Normal file
|
|
@ -0,0 +1,12 @@
|
||||||
|
using System.Text.Json.Serialization;
|
||||||
|
|
||||||
|
namespace SPMS.Application.DTOs.Message;
|
||||||
|
|
||||||
|
public class MessageSaveResponseDto
|
||||||
|
{
|
||||||
|
[JsonPropertyName("message_code")]
|
||||||
|
public string MessageCode { get; set; } = string.Empty;
|
||||||
|
|
||||||
|
[JsonPropertyName("created_at")]
|
||||||
|
public DateTime CreatedAt { get; set; }
|
||||||
|
}
|
||||||
|
|
@ -4,5 +4,9 @@ namespace SPMS.Application.Interfaces;
|
||||||
|
|
||||||
public interface IMessageService
|
public interface IMessageService
|
||||||
{
|
{
|
||||||
|
Task<MessageSaveResponseDto> SaveAsync(long serviceId, long adminId, MessageSaveRequestDto request);
|
||||||
|
Task<MessageListResponseDto> GetListAsync(long serviceId, MessageListRequestDto request);
|
||||||
|
Task<MessageInfoResponseDto> GetInfoAsync(long serviceId, MessageInfoRequestDto request);
|
||||||
|
Task DeleteAsync(long serviceId, MessageDeleteRequestDto request);
|
||||||
Task<MessagePreviewResponseDto> PreviewAsync(long serviceId, MessagePreviewRequestDto request);
|
Task<MessagePreviewResponseDto> PreviewAsync(long serviceId, MessagePreviewRequestDto request);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,7 @@
|
||||||
|
using System.Text.Json;
|
||||||
|
using System.Text.RegularExpressions;
|
||||||
using SPMS.Application.DTOs.Message;
|
using SPMS.Application.DTOs.Message;
|
||||||
|
using SPMS.Application.DTOs.Notice;
|
||||||
using SPMS.Application.Interfaces;
|
using SPMS.Application.Interfaces;
|
||||||
using SPMS.Domain.Common;
|
using SPMS.Domain.Common;
|
||||||
using SPMS.Domain.Exceptions;
|
using SPMS.Domain.Exceptions;
|
||||||
|
|
@ -9,10 +12,133 @@ namespace SPMS.Application.Services;
|
||||||
public class MessageService : IMessageService
|
public class MessageService : IMessageService
|
||||||
{
|
{
|
||||||
private readonly IMessageRepository _messageRepository;
|
private readonly IMessageRepository _messageRepository;
|
||||||
|
private readonly IUnitOfWork _unitOfWork;
|
||||||
|
private static readonly Regex VariablePattern = new(@"\{\{(\w+)\}\}", RegexOptions.Compiled);
|
||||||
|
|
||||||
public MessageService(IMessageRepository messageRepository)
|
public MessageService(IMessageRepository messageRepository, IUnitOfWork unitOfWork)
|
||||||
{
|
{
|
||||||
_messageRepository = messageRepository;
|
_messageRepository = messageRepository;
|
||||||
|
_unitOfWork = unitOfWork;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<MessageSaveResponseDto> SaveAsync(long serviceId, long adminId, MessageSaveRequestDto request)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrWhiteSpace(request.Title))
|
||||||
|
throw new SpmsException(ErrorCodes.BadRequest, "제목은 필수입니다.", 400);
|
||||||
|
if (string.IsNullOrWhiteSpace(request.Body))
|
||||||
|
throw new SpmsException(ErrorCodes.BadRequest, "본문은 필수입니다.", 400);
|
||||||
|
|
||||||
|
var messageCode = await GenerateMessageCodeAsync(serviceId);
|
||||||
|
|
||||||
|
string? customData = null;
|
||||||
|
if (request.Data != null)
|
||||||
|
{
|
||||||
|
customData = request.Data is JsonElement jsonElement
|
||||||
|
? jsonElement.GetRawText()
|
||||||
|
: JsonSerializer.Serialize(request.Data);
|
||||||
|
}
|
||||||
|
|
||||||
|
var message = new Domain.Entities.Message
|
||||||
|
{
|
||||||
|
ServiceId = serviceId,
|
||||||
|
MessageCode = messageCode,
|
||||||
|
Title = request.Title,
|
||||||
|
Body = request.Body,
|
||||||
|
ImageUrl = request.ImageUrl,
|
||||||
|
LinkUrl = request.LinkUrl,
|
||||||
|
LinkType = request.LinkType,
|
||||||
|
CustomData = customData,
|
||||||
|
CreatedAt = DateTime.UtcNow,
|
||||||
|
CreatedBy = adminId,
|
||||||
|
IsDeleted = false
|
||||||
|
};
|
||||||
|
|
||||||
|
await _messageRepository.AddAsync(message);
|
||||||
|
await _unitOfWork.SaveChangesAsync();
|
||||||
|
|
||||||
|
return new MessageSaveResponseDto
|
||||||
|
{
|
||||||
|
MessageCode = messageCode,
|
||||||
|
CreatedAt = message.CreatedAt
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<MessageListResponseDto> GetListAsync(long serviceId, MessageListRequestDto request)
|
||||||
|
{
|
||||||
|
var page = Math.Max(1, request.Page);
|
||||||
|
var size = Math.Clamp(request.Size, 1, 100);
|
||||||
|
|
||||||
|
var (items, totalCount) = await _messageRepository.GetPagedByServiceAsync(
|
||||||
|
serviceId, page, size,
|
||||||
|
m => (request.Keyword == null || m.Title.Contains(request.Keyword) || m.Body.Contains(request.Keyword))
|
||||||
|
&& (request.IsActive == null || m.IsDeleted != request.IsActive));
|
||||||
|
|
||||||
|
var totalPages = (int)Math.Ceiling((double)totalCount / size);
|
||||||
|
|
||||||
|
return new MessageListResponseDto
|
||||||
|
{
|
||||||
|
Items = items.Select(m => new MessageSummaryDto
|
||||||
|
{
|
||||||
|
MessageCode = m.MessageCode,
|
||||||
|
Title = m.Title,
|
||||||
|
IsActive = !m.IsDeleted,
|
||||||
|
CreatedAt = m.CreatedAt
|
||||||
|
}).ToList(),
|
||||||
|
Pagination = new PaginationDto
|
||||||
|
{
|
||||||
|
Page = page,
|
||||||
|
Size = size,
|
||||||
|
TotalCount = totalCount,
|
||||||
|
TotalPages = totalPages
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<MessageInfoResponseDto> GetInfoAsync(long serviceId, MessageInfoRequestDto request)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrWhiteSpace(request.MessageCode))
|
||||||
|
throw new SpmsException(ErrorCodes.BadRequest, "메시지 코드는 필수입니다.", 400);
|
||||||
|
|
||||||
|
var message = await _messageRepository.GetByMessageCodeAndServiceAsync(request.MessageCode, serviceId);
|
||||||
|
if (message == null)
|
||||||
|
throw new SpmsException(ErrorCodes.MessageNotFound, "존재하지 않는 메시지 코드입니다.", 404);
|
||||||
|
|
||||||
|
var variables = ExtractVariables(message.Title, message.Body);
|
||||||
|
|
||||||
|
object? data = null;
|
||||||
|
if (!string.IsNullOrEmpty(message.CustomData))
|
||||||
|
{
|
||||||
|
data = JsonSerializer.Deserialize<JsonElement>(message.CustomData);
|
||||||
|
}
|
||||||
|
|
||||||
|
return new MessageInfoResponseDto
|
||||||
|
{
|
||||||
|
MessageCode = message.MessageCode,
|
||||||
|
Title = message.Title,
|
||||||
|
Body = message.Body,
|
||||||
|
ImageUrl = message.ImageUrl,
|
||||||
|
LinkUrl = message.LinkUrl,
|
||||||
|
LinkType = message.LinkType,
|
||||||
|
Data = data,
|
||||||
|
Variables = variables,
|
||||||
|
IsActive = !message.IsDeleted,
|
||||||
|
CreatedAt = message.CreatedAt
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task DeleteAsync(long serviceId, MessageDeleteRequestDto request)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrWhiteSpace(request.MessageCode))
|
||||||
|
throw new SpmsException(ErrorCodes.BadRequest, "메시지 코드는 필수입니다.", 400);
|
||||||
|
|
||||||
|
var message = await _messageRepository.GetByMessageCodeAndServiceAsync(request.MessageCode, serviceId);
|
||||||
|
if (message == null)
|
||||||
|
throw new SpmsException(ErrorCodes.MessageNotFound, "존재하지 않는 메시지 코드입니다.", 404);
|
||||||
|
|
||||||
|
message.IsDeleted = true;
|
||||||
|
message.DeletedAt = DateTime.UtcNow;
|
||||||
|
_messageRepository.Update(message);
|
||||||
|
await _unitOfWork.SaveChangesAsync();
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<MessagePreviewResponseDto> PreviewAsync(long serviceId, MessagePreviewRequestDto request)
|
public async Task<MessagePreviewResponseDto> PreviewAsync(long serviceId, MessagePreviewRequestDto request)
|
||||||
|
|
@ -33,6 +159,41 @@ public class MessageService : IMessageService
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private async Task<string> GenerateMessageCodeAsync(long serviceId)
|
||||||
|
{
|
||||||
|
var today = DateTime.UtcNow.Date;
|
||||||
|
var seed = (int)(today.Ticks ^ serviceId);
|
||||||
|
var random = new Random(seed);
|
||||||
|
|
||||||
|
var prefix = GenerateRandomChars(random, 3);
|
||||||
|
var suffix = GenerateRandomChars(new Random(seed + 1), 3);
|
||||||
|
|
||||||
|
var sequence = await _messageRepository.GetTodaySequenceAsync(serviceId) + 1;
|
||||||
|
if (sequence > 9999)
|
||||||
|
throw new SpmsException(ErrorCodes.LimitExceeded, "일일 메시지 생성 한도(9999건)를 초과했습니다.", 429);
|
||||||
|
|
||||||
|
return $"{prefix}{sequence:D4}{suffix}";
|
||||||
|
}
|
||||||
|
|
||||||
|
private static string GenerateRandomChars(Random random, int length)
|
||||||
|
{
|
||||||
|
const string chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
|
||||||
|
var result = new char[length];
|
||||||
|
for (var i = 0; i < length; i++)
|
||||||
|
result[i] = chars[random.Next(chars.Length)];
|
||||||
|
return new string(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static List<string> ExtractVariables(string title, string body)
|
||||||
|
{
|
||||||
|
var variables = new HashSet<string>();
|
||||||
|
foreach (Match match in VariablePattern.Matches(title))
|
||||||
|
variables.Add(match.Groups[1].Value);
|
||||||
|
foreach (Match match in VariablePattern.Matches(body))
|
||||||
|
variables.Add(match.Groups[1].Value);
|
||||||
|
return variables.ToList();
|
||||||
|
}
|
||||||
|
|
||||||
private static string ApplyVariables(string template, Dictionary<string, string>? variables)
|
private static string ApplyVariables(string template, Dictionary<string, string>? variables)
|
||||||
{
|
{
|
||||||
if (variables == null || variables.Count == 0)
|
if (variables == null || variables.Count == 0)
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,4 @@
|
||||||
|
using System.Linq.Expressions;
|
||||||
using SPMS.Domain.Entities;
|
using SPMS.Domain.Entities;
|
||||||
|
|
||||||
namespace SPMS.Domain.Interfaces;
|
namespace SPMS.Domain.Interfaces;
|
||||||
|
|
@ -6,4 +7,8 @@ public interface IMessageRepository : IRepository<Message>
|
||||||
{
|
{
|
||||||
Task<Message?> GetByMessageCodeAsync(string messageCode);
|
Task<Message?> GetByMessageCodeAsync(string messageCode);
|
||||||
Task<Message?> GetByMessageCodeAndServiceAsync(string messageCode, long serviceId);
|
Task<Message?> GetByMessageCodeAndServiceAsync(string messageCode, long serviceId);
|
||||||
|
Task<int> GetTodaySequenceAsync(long serviceId);
|
||||||
|
Task<(IReadOnlyList<Message> Items, int TotalCount)> GetPagedByServiceAsync(
|
||||||
|
long serviceId, int page, int size,
|
||||||
|
Expression<Func<Message, bool>>? predicate = null);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,4 @@
|
||||||
|
using System.Linq.Expressions;
|
||||||
using Microsoft.EntityFrameworkCore;
|
using Microsoft.EntityFrameworkCore;
|
||||||
using SPMS.Domain.Entities;
|
using SPMS.Domain.Entities;
|
||||||
using SPMS.Domain.Interfaces;
|
using SPMS.Domain.Interfaces;
|
||||||
|
|
@ -19,4 +20,36 @@ public class MessageRepository : Repository<Message>, IMessageRepository
|
||||||
return await _dbSet
|
return await _dbSet
|
||||||
.FirstOrDefaultAsync(m => m.MessageCode == messageCode && m.ServiceId == serviceId && !m.IsDeleted);
|
.FirstOrDefaultAsync(m => m.MessageCode == messageCode && m.ServiceId == serviceId && !m.IsDeleted);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public async Task<int> GetTodaySequenceAsync(long serviceId)
|
||||||
|
{
|
||||||
|
var todayStart = DateTime.UtcNow.Date;
|
||||||
|
var todayEnd = todayStart.AddDays(1);
|
||||||
|
|
||||||
|
return await _dbSet
|
||||||
|
.CountAsync(m => m.ServiceId == serviceId
|
||||||
|
&& m.CreatedAt >= todayStart
|
||||||
|
&& m.CreatedAt < todayEnd);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<(IReadOnlyList<Message> Items, int TotalCount)> GetPagedByServiceAsync(
|
||||||
|
long serviceId, int page, int size,
|
||||||
|
Expression<Func<Message, bool>>? predicate = null)
|
||||||
|
{
|
||||||
|
var query = _dbSet
|
||||||
|
.Where(m => m.ServiceId == serviceId && !m.IsDeleted);
|
||||||
|
|
||||||
|
if (predicate != null)
|
||||||
|
query = query.Where(predicate);
|
||||||
|
|
||||||
|
var totalCount = await query.CountAsync();
|
||||||
|
|
||||||
|
var items = await query
|
||||||
|
.OrderByDescending(m => m.CreatedAt)
|
||||||
|
.Skip((page - 1) * size)
|
||||||
|
.Take(size)
|
||||||
|
.ToListAsync();
|
||||||
|
|
||||||
|
return (items, totalCount);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user