Files
T120B165/T120B165-ImgBoard/Services/PostService.cs
2025-10-06 10:42:32 +03:00

167 lines
5.4 KiB
C#

using System.Text.RegularExpressions;
using Microsoft.EntityFrameworkCore;
using T120B165_ImgBoard.Data;
using T120B165_ImgBoard.Models;
using T120B165_ImgBoard.Utils;
using File = T120B165_ImgBoard.Models.File;
namespace T120B165_ImgBoard.Services;
public interface IPostService
{
Task<CreatedPost> Create(
string title, string description, List<Tag> tags, User author,
string fileName, string fileContentType, long fileSize);
Task<Post?> GetById(int postId, bool includeUnfinished = false);
Task<PagedList<Post>> GetAll(int pageNumber = 1);
public Task<PagedList<Post>> FindAll(string? query, int pageNumber = 1);
Task<bool> Delete(Post post);
Task<Post> Update(Post post);
}
public record CreatedPost(Post Post, File File);
public class PostService(ImgBoardContext context): IPostService
{
private const int PageSize = 20;
public async Task<CreatedPost> Create(
string title,
string description,
List<Tag> tags,
User author,
string fileName,
string fileContentType,
long fileSize
)
{
var file = new File
{
OriginalFileName = fileName,
Size = fileSize,
ContentType = fileContentType,
FilePath = Path.GetTempFileName(),
CreatedDate = DateTime.Now,
};
await context.Files.AddAsync(file);
var entry = new Post
{
Title = title,
Description = description,
Author = author,
Created = DateTime.Now,
Tags = tags,
File = file
};
await context.Posts.AddAsync(entry);
await context.SaveChangesAsync();
return new CreatedPost(entry, file);
}
public async Task<Post?> GetById(int postId, bool includeUnfinished = false)
{
var query = context.Posts.Where(t => t.Id == postId);
if (!includeUnfinished)
{
query = query.Where(t => t.File.FinishedDate != null);
}
return await query.Include(b => b.Author)
.Include(b => b.Tags)
.Include(b => b.File)
.FirstOrDefaultAsync();
}
public async Task<PagedList<Post>> GetAll(int pageNumber = 1)
{
var totalCount = await context.Posts.Where(p => p.File.FinishedDate != null).CountAsync();
var items = await context.Posts
.Skip((pageNumber - 1) * PageSize)
.Take(PageSize)
.Where(p => p.File.FinishedDate != null)
.Include(b => b.Author)
.Include(b => b.Tags)
.Include(b => b.File)
.ToListAsync();
return new PagedList<Post>(items, pageNumber, PageSize, totalCount);
}
public async Task<PagedList<Post>> FindAll(string? query, int pageNumber = 1)
{
var postsQuery = context.Posts
.Where(p => p.File.FinishedDate != null);
// Apply query filtering if the query string is not null or empty
if (!string.IsNullOrWhiteSpace(query))
{
// Parse the query string into required and excluded tags
var (requiredTags, excludedTags) = ParseTagQuery(query);
// Filter for required tags (Post must have ALL of these tags)
foreach (var requiredTag in requiredTags)
{
// Where the post's Tags collection contains ANY tag whose name matches the requiredTag.
postsQuery = postsQuery.Where(p => p.Tags.Any(t => t.Name == requiredTag));
}
// Filter out excluded tags (Post must NOT have ANY of these tags)
foreach (var excludedTag in excludedTags)
{
// Where the post's Tags collection does NOT contain ANY tag whose name matches the excludedTag.
postsQuery = postsQuery.Where(p => p.Tags.All(t => t.Name != excludedTag));
}
}
var totalCount = await postsQuery.CountAsync();
var items = await postsQuery
.Skip((pageNumber - 1) * PageSize)
.Take(PageSize)
.Include(b => b.Author)
.Include(b => b.Tags)
.Include(b => b.File)
.ToListAsync();
return new PagedList<Post>(items, pageNumber, PageSize, totalCount);
}
private static (List<string> requiredTags, List<string> excludedTags) ParseTagQuery(string query)
{
var requiredTags = new List<string>();
var excludedTags = new List<string>();
// Regex to find all "+tag" or "-tag" segments
var matches = Regex.Matches(query, @"([+-])([\w-]+)");
foreach (Match match in matches)
{
var type = match.Groups[1].Value; // "+" or "-"
var tag = match.Groups[2].Value; // The tag name
switch (type)
{
case "+":
requiredTags.Add(tag);
break;
case "-":
excludedTags.Add(tag);
break;
}
}
return (requiredTags, excludedTags);
}
public async Task<bool> Delete(Post post)
{
context.Posts.Remove(post);
await context.SaveChangesAsync();
return true;
}
public async Task<Post> Update(Post post)
{
context.Posts.Update(post);
await context.SaveChangesAsync();
return post;
}
}