221 lines
8.2 KiB
C#
221 lines
8.2 KiB
C#
using Fitness_core_BE.Exceptions;
|
|
using Fitness_core_BE.Interfaces;
|
|
using Fitness_core_BE.Models;
|
|
using Microsoft.EntityFrameworkCore;
|
|
using System.Linq.Expressions;
|
|
|
|
|
|
namespace Fitness_core_BE.Repos
|
|
{
|
|
public class FeedRepository : IFeedRepository
|
|
{
|
|
// FeedPost/FeedLike are central-bucket (poster identity spans gyms); Staff and
|
|
// CustomerSubscriptions used to scope a feed to one client live in the tenant DB.
|
|
private readonly CentralDbContext _central;
|
|
private readonly TenantDbContext _dbContext;
|
|
|
|
public FeedRepository(CentralDbContext central, TenantDbContext dbContext)
|
|
{
|
|
_central = central;
|
|
_dbContext = dbContext;
|
|
}
|
|
|
|
public async Task<FeedPost> GetFeedPostByIdAsync(Guid creatorId, Guid postId)
|
|
{
|
|
FeedPost feedPost = await _central.FeedPosts
|
|
.Where(p => p.FKUserId == creatorId && p.FeedPostId == postId)
|
|
.FirstOrDefaultAsync();
|
|
|
|
if (feedPost == null)
|
|
throw new NotFoundException("Post : " + postId + " not found");
|
|
|
|
return feedPost;
|
|
}
|
|
|
|
public async Task<List<FeedPost>> GetFeedListAsync(int skip, int take)
|
|
{
|
|
return await _central.FeedPosts
|
|
.Include(fp => fp.User)
|
|
.Where(fp => fp.IsActive)
|
|
.OrderByDescending(fp => fp.CreatedAt)
|
|
.Skip(skip)
|
|
.Take(take)
|
|
.ToListAsync();
|
|
}
|
|
|
|
public async Task<bool> IsLikedAsync(Guid postId, Guid userId)
|
|
{
|
|
return await _central.FeedLikes
|
|
.AnyAsync(x => x.FKFeedPostId == postId && x.FKUserId == userId);
|
|
}
|
|
|
|
public async Task AddLikeAsync(Guid postId, Guid userId)
|
|
{
|
|
_central.FeedLikes.Add(new FeedLike
|
|
{
|
|
FKFeedPostId = postId,
|
|
FKUserId = userId
|
|
});
|
|
}
|
|
|
|
|
|
public async Task RemoveLikeAsync(Guid postId, Guid userId)
|
|
{
|
|
var like = await _central.FeedLikes
|
|
.FirstOrDefaultAsync(x => x.FKFeedPostId == postId && x.FKUserId == userId);
|
|
|
|
if (like != null)
|
|
{
|
|
_central.FeedLikes.Remove(like);
|
|
}
|
|
}
|
|
|
|
public async Task<int> GetLikeCountAsync(Guid postId)
|
|
{
|
|
return await _central.FeedLikes
|
|
.CountAsync(x => x.FKFeedPostId == postId);
|
|
}
|
|
|
|
public async Task<object> CreateFeedAsync(FeedPost feedPost, CancellationToken ct = default)
|
|
{
|
|
await _central.FeedPosts.AddAsync(feedPost, ct);
|
|
return true;
|
|
}
|
|
|
|
public async Task<List<FeedPost>> GetFeedByClientAsync(Guid clientId)
|
|
{
|
|
// Client (central) and Staff (tenant) can't be joined in one query anymore --
|
|
// resolve the set of poster user ids (owner + staff) first, then filter posts.
|
|
var posterUserIds = new List<Guid>();
|
|
|
|
var clientOwnerUserId = await _central.Client
|
|
.Where(client => client.ClientId == clientId)
|
|
.Select(client => (Guid?)client.FKUserId)
|
|
.FirstOrDefaultAsync();
|
|
if (clientOwnerUserId != null)
|
|
posterUserIds.Add(clientOwnerUserId.Value);
|
|
|
|
var staffUserIds = await _dbContext.Staff
|
|
.Where(staff => staff.FKClientId == clientId)
|
|
.Select(staff => staff.FKUserId)
|
|
.ToListAsync();
|
|
posterUserIds.AddRange(staffUserIds);
|
|
|
|
return await _central.FeedPosts
|
|
.Include(fp => fp.User)
|
|
.Where(fp => fp.IsActive && posterUserIds.Contains(fp.FKUserId))
|
|
.OrderByDescending(fp => fp.CreatedAt)
|
|
.ToListAsync();
|
|
}
|
|
|
|
public async Task<(Guid? ClientId, List<FeedPost> Posts)> GetFeedForCustomerAsync(
|
|
Guid customerUserId,
|
|
int skip,
|
|
int take,
|
|
CancellationToken ct = default)
|
|
{
|
|
var cusId = await _central.Customer
|
|
.Where(customer => customer.FKUserId == customerUserId)
|
|
.Select(customer => (Guid?)customer.CusId)
|
|
.FirstOrDefaultAsync(ct);
|
|
|
|
var clientId = Guid.Empty;
|
|
if (cusId != null)
|
|
{
|
|
clientId = await _dbContext.CustomerSubscriptions
|
|
.Where(subscription =>
|
|
subscription.FKCustomerId == cusId.Value &&
|
|
subscription.IsActive)
|
|
.Select(subscription => subscription.CustomerPlan.FKClientId)
|
|
.FirstOrDefaultAsync(ct);
|
|
}
|
|
|
|
// No active gym: there's no single gym feed to scope to, so fall back to the
|
|
// platform-wide feed (every active post) instead of showing nothing.
|
|
if (clientId == Guid.Empty)
|
|
{
|
|
var globalPosts = await _central.FeedPosts
|
|
.AsNoTracking()
|
|
.Include(post => post.User)
|
|
.Where(post => post.IsActive)
|
|
.OrderByDescending(post => post.CreatedAt)
|
|
.Skip(skip)
|
|
.Take(take)
|
|
.ToListAsync(ct);
|
|
|
|
return (null, globalPosts);
|
|
}
|
|
|
|
// Poster set for this customer's gym = owner + staff (mirrors GetFeedByClientAsync,
|
|
// the client-owner's own view) -- otherwise a staff member's post would show up on
|
|
// the gym's own dashboard but never reach that gym's customers.
|
|
var posterUserIds = new List<Guid>();
|
|
|
|
var clientOwnerUserId = await _central.Client
|
|
.Where(client => client.ClientId == clientId)
|
|
.Select(client => (Guid?)client.FKUserId)
|
|
.FirstOrDefaultAsync(ct);
|
|
if (clientOwnerUserId != null)
|
|
posterUserIds.Add(clientOwnerUserId.Value);
|
|
|
|
var staffUserIds = await _dbContext.Staff
|
|
.Where(staff => staff.FKClientId == clientId)
|
|
.Select(staff => staff.FKUserId)
|
|
.ToListAsync(ct);
|
|
posterUserIds.AddRange(staffUserIds);
|
|
|
|
// Show the gym's own posts alongside this customer's own posts, so a customer
|
|
// who creates a post can see it here too, not just their gym's feed.
|
|
var posts = await _central.FeedPosts
|
|
.AsNoTracking()
|
|
.Include(post => post.User)
|
|
.Where(post => post.IsActive && (posterUserIds.Contains(post.FKUserId) || post.FKUserId == customerUserId))
|
|
.OrderByDescending(post => post.CreatedAt)
|
|
.Skip(skip)
|
|
.Take(take)
|
|
.ToListAsync(ct);
|
|
|
|
return (clientId, posts);
|
|
}
|
|
|
|
public async Task<List<Guid>> GetActiveCustomerIdsByPosterUserIdAsync(Guid posterUserId, CancellationToken ct = default)
|
|
{
|
|
var clientId = await _central.Client
|
|
.Where(client => client.FKUserId == posterUserId)
|
|
.Select(client => (Guid?)client.ClientId)
|
|
.FirstOrDefaultAsync(ct);
|
|
|
|
if (clientId == null || clientId == Guid.Empty)
|
|
return new List<Guid>();
|
|
|
|
return await _dbContext.CustomerSubscriptions
|
|
.Where(subscription => subscription.IsActive && subscription.CustomerPlan.FKClientId == clientId)
|
|
.Select(subscription => subscription.FKCustomerId)
|
|
.Distinct()
|
|
.ToListAsync(ct);
|
|
}
|
|
|
|
public async Task<object> DeletePostPostIdAsync(Guid creatorId, Guid postId)
|
|
{
|
|
FeedPost post = await _central.FeedPosts
|
|
.Where(p => p.FeedPostId == postId && p.FKUserId == creatorId)
|
|
.FirstOrDefaultAsync();
|
|
|
|
if (post == null)
|
|
throw new NotFoundException("Post : " + postId + "not found");
|
|
|
|
post.IsActive = false;
|
|
|
|
try
|
|
{
|
|
_central.Update(post);
|
|
return new { process = "success" };
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
throw new Exception(ex.Message, ex);
|
|
}
|
|
}
|
|
}
|
|
}
|