152 lines
4.8 KiB
C#
152 lines
4.8 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);
|
|
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 = 8;
|
|
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>> 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;
|
|
}
|
|
}
|