129 lines
6.1 KiB
C#
129 lines
6.1 KiB
C#
using Microsoft.EntityFrameworkCore;
|
|
using UniVerse.Application.DTOs.Common;
|
|
using UniVerse.Application.DTOs.Lectures;
|
|
using UniVerse.Application.Interfaces;
|
|
using UniVerse.Application.Mappings;
|
|
using UniVerse.Domain.Entities;
|
|
using UniVerse.Domain.Exceptions;
|
|
using UniVerse.Infrastructure.Data;
|
|
|
|
namespace UniVerse.Infrastructure.Services;
|
|
|
|
public class LectureService : ILectureService
|
|
{
|
|
private readonly AppDbContext _db;
|
|
public LectureService(AppDbContext db) => _db = db;
|
|
|
|
private IQueryable<Lecture> BaseQuery() => _db.Lectures
|
|
.Include(l => l.Course).Include(l => l.Teacher)
|
|
.Include(l => l.Location).Include(l => l.Enrollments);
|
|
|
|
public async Task<PagedResult<LectureDto>> GetAllAsync(LectureFilterRequest filter)
|
|
{
|
|
var query = BaseQuery();
|
|
if (filter.CourseId.HasValue) query = query.Where(l => l.CourseId == filter.CourseId);
|
|
if (filter.TeacherId.HasValue) query = query.Where(l => l.TeacherId == filter.TeacherId);
|
|
if (filter.Format.HasValue) query = query.Where(l => l.Format == filter.Format);
|
|
if (filter.IsOpen.HasValue) query = query.Where(l => l.IsOpen == filter.IsOpen);
|
|
if (filter.DateFrom.HasValue)
|
|
query = query.Where(l => DateOnly.FromDateTime(l.StartsAt) >= filter.DateFrom);
|
|
if (filter.DateTo.HasValue)
|
|
query = query.Where(l => DateOnly.FromDateTime(l.StartsAt) <= filter.DateTo);
|
|
if (!string.IsNullOrEmpty(filter.Search))
|
|
query = query.Where(l => l.Title.ToLower().Contains(filter.Search.ToLower()));
|
|
if (filter.TagId.HasValue)
|
|
query = query.Where(l => l.Course.CourseTags.Any(ct => ct.TagId == filter.TagId));
|
|
|
|
var total = await query.CountAsync();
|
|
var items = await query.OrderBy(l => l.StartsAt)
|
|
.Skip((filter.Page - 1) * filter.PageSize).Take(filter.PageSize).ToListAsync();
|
|
return PagedResult<LectureDto>.Create(items.Select(l => l.ToDto()).ToList(), total, filter.Page, filter.PageSize);
|
|
}
|
|
|
|
public async Task<LectureDetailDto> GetByIdAsync(int id, int? currentUserId = null)
|
|
{
|
|
var lecture = await BaseQuery().FirstOrDefaultAsync(l => l.Id == id)
|
|
?? throw new NotFoundException("Lecture", id);
|
|
var isEnrolled = currentUserId.HasValue &&
|
|
lecture.Enrollments.Any(e => e.UserId == currentUserId.Value);
|
|
return lecture.ToDetailDto(isEnrolled);
|
|
}
|
|
|
|
public async Task<LectureDto> CreateAsync(CreateLectureRequest req)
|
|
{
|
|
_ = await _db.Courses.FindAsync(req.CourseId) ?? throw new NotFoundException("Course", req.CourseId);
|
|
var lecture = new Lecture
|
|
{
|
|
CourseId = req.CourseId, TeacherId = req.TeacherId, LocationId = req.LocationId,
|
|
Title = req.Title, Description = req.Description, Format = req.Format,
|
|
StartsAt = req.StartsAt, EndsAt = req.EndsAt, IsOpen = req.IsOpen,
|
|
MaxEnrollments = req.MaxEnrollments, OnlineUrl = req.OnlineUrl
|
|
};
|
|
_db.Lectures.Add(lecture);
|
|
await _db.SaveChangesAsync();
|
|
var full = await BaseQuery().FirstAsync(l => l.Id == lecture.Id);
|
|
return full.ToDto();
|
|
}
|
|
|
|
public async Task<LectureDto> UpdateAsync(int id, UpdateLectureRequest req)
|
|
{
|
|
var lecture = await _db.Lectures.FindAsync(id) ?? throw new NotFoundException("Lecture", id);
|
|
lecture.TeacherId = req.TeacherId; lecture.LocationId = req.LocationId;
|
|
lecture.Title = req.Title; lecture.Description = req.Description;
|
|
lecture.Format = req.Format; lecture.StartsAt = req.StartsAt; lecture.EndsAt = req.EndsAt;
|
|
lecture.IsOpen = req.IsOpen; lecture.MaxEnrollments = req.MaxEnrollments;
|
|
lecture.OnlineUrl = req.OnlineUrl; lecture.UpdatedAt = DateTime.UtcNow;
|
|
await _db.SaveChangesAsync();
|
|
var full = await BaseQuery().FirstAsync(l => l.Id == id);
|
|
return full.ToDto();
|
|
}
|
|
|
|
public async Task DeleteAsync(int id)
|
|
{
|
|
var lecture = await _db.Lectures.FindAsync(id) ?? throw new NotFoundException("Lecture", id);
|
|
_db.Lectures.Remove(lecture);
|
|
await _db.SaveChangesAsync();
|
|
}
|
|
|
|
public async Task EnrollAsync(int lectureId, int userId)
|
|
{
|
|
var lecture = await _db.Lectures.Include(l => l.Enrollments)
|
|
.FirstOrDefaultAsync(l => l.Id == lectureId) ?? throw new NotFoundException("Lecture", lectureId);
|
|
if (!lecture.IsOpen) throw new ConflictException("Lecture is not open for enrollment.");
|
|
if (lecture.MaxEnrollments > 0 && lecture.Enrollments.Count >= lecture.MaxEnrollments)
|
|
throw new ConflictException("Lecture is full.");
|
|
if (lecture.Enrollments.Any(e => e.UserId == userId))
|
|
throw new ConflictException("Already enrolled.");
|
|
_db.LectureEnrollments.Add(new LectureEnrollment { LectureId = lectureId, UserId = userId });
|
|
await _db.SaveChangesAsync();
|
|
}
|
|
|
|
public async Task UnenrollAsync(int lectureId, int userId)
|
|
{
|
|
var enrollment = await _db.LectureEnrollments
|
|
.FirstOrDefaultAsync(e => e.LectureId == lectureId && e.UserId == userId)
|
|
?? throw new NotFoundException("Enrollment not found.");
|
|
_db.LectureEnrollments.Remove(enrollment);
|
|
await _db.SaveChangesAsync();
|
|
}
|
|
|
|
public async Task MarkAttendanceAsync(int lectureId, int userId, bool attended)
|
|
{
|
|
var enrollment = await _db.LectureEnrollments
|
|
.FirstOrDefaultAsync(e => e.LectureId == lectureId && e.UserId == userId)
|
|
?? throw new NotFoundException("Enrollment not found.");
|
|
enrollment.Attended = attended;
|
|
await _db.SaveChangesAsync();
|
|
}
|
|
|
|
public async Task<PagedResult<EnrollmentDto>> GetEnrollmentsAsync(int lectureId, PaginationRequest pagination)
|
|
{
|
|
var query = _db.LectureEnrollments.Include(e => e.User)
|
|
.Where(e => e.LectureId == lectureId);
|
|
var total = await query.CountAsync();
|
|
var items = await query.OrderBy(e => e.CreatedAt)
|
|
.Skip((pagination.Page - 1) * pagination.PageSize).Take(pagination.PageSize).ToListAsync();
|
|
return PagedResult<EnrollmentDto>.Create(items.Select(e => e.ToDto()).ToList(), total, pagination.Page, pagination.PageSize);
|
|
}
|
|
}
|