From b19e762d78d65708af992a58a673d2df214efe5c Mon Sep 17 00:00:00 2001 From: tleodev Date: Tue, 17 Jun 2025 10:34:40 +0200 Subject: [PATCH] Functionnal program logic --- .../Controllers/SessionsController.cs | 93 ++++-------------- .../Controllers/TrainingProgramsController.cs | 32 +++++++ src/TrainingSvc/DTOs/TrainingProgramDto.cs | 17 ++++ src/TrainingSvc/DTOs/UpdateSessionDto.cs | 1 - src/TrainingSvc/IServices/ISessionService.cs | 12 +++ .../IServices/ITrainingProgramService.cs | 9 ++ src/TrainingSvc/Program.cs | 2 + .../ITrainingProgramRepository.cs | 2 + .../Repositories/SessionRepository.cs | 17 ++++ .../Repositories/TrainingProgramRepository.cs | 19 ++++ .../RequestHelpers/SessionProfile.cs | 3 +- .../RequestHelpers/TrainingProgramProfile.cs | 13 +++ src/TrainingSvc/Services/SessionService.cs | 95 +++++++++++++++++++ .../Services/TrainingProgramService.cs | 30 ++++++ 14 files changed, 266 insertions(+), 79 deletions(-) create mode 100644 src/TrainingSvc/Controllers/TrainingProgramsController.cs create mode 100644 src/TrainingSvc/DTOs/TrainingProgramDto.cs create mode 100644 src/TrainingSvc/IServices/ISessionService.cs create mode 100644 src/TrainingSvc/IServices/ITrainingProgramService.cs create mode 100644 src/TrainingSvc/RequestHelpers/TrainingProgramProfile.cs create mode 100644 src/TrainingSvc/Services/SessionService.cs create mode 100644 src/TrainingSvc/Services/TrainingProgramService.cs diff --git a/src/TrainingSvc/Controllers/SessionsController.cs b/src/TrainingSvc/Controllers/SessionsController.cs index 8627473..0baad3a 100644 --- a/src/TrainingSvc/Controllers/SessionsController.cs +++ b/src/TrainingSvc/Controllers/SessionsController.cs @@ -1,10 +1,6 @@ -using AutoMapper; using Microsoft.AspNetCore.Mvc; -using Microsoft.EntityFrameworkCore; -using Shared.DTOs; -using TrainingSvc.Data; using TrainingSvc.DTOs; -using TrainingSvc.Entities; +using TrainingSvc.IServices; namespace TrainingSvc.Controllers; @@ -12,105 +8,48 @@ namespace TrainingSvc.Controllers; [Route("api/training/[controller]")] public class SessionsController : ControllerBase { - private readonly TrainingDbContext _context; - private readonly IMapper _mapper; + private readonly ISessionService _sessionService; - public SessionsController(TrainingDbContext context, IMapper mapper) + public SessionsController(ISessionService sessionService) { - _context = context; - _mapper = mapper; + _sessionService = sessionService; } [HttpGet] public async Task>> GetAll() { - var list = await _context.Sessions - .Include(s => s.Exercices) - .ThenInclude(e => e.ExerciceTemplate) - .ToListAsync(); - return Ok(_mapper.Map>(list)); + var list = await _sessionService.GetAllAsync(); + return Ok(list); } [HttpGet("{id}")] public async Task> GetById(string id) { - var entity = await _context.Sessions - .Include(s => s.Exercices) - .ThenInclude(e => e.ExerciceTemplate) - .FirstOrDefaultAsync(s => s.Id == id); - if (entity == null) return NotFound(); - return _mapper.Map(entity); + var dto = await _sessionService.GetByIdAsync(id); + if (dto == null) return NotFound(); + return Ok(dto); } [HttpPost] public async Task> Create([FromBody] CreateSessionDto dto) { - var session = _mapper.Map(dto); - _context.Sessions.Add(session); - await _context.SaveChangesAsync(); - - // Crée les ExerciceInstance associées - foreach (var exoDto in dto.Exercices) - { - var exo = _mapper.Map(exoDto); - exo.SessionId = session.Id; - _context.ExerciceInstances.Add(exo); - } - await _context.SaveChangesAsync(); - - // Recharge la session avec les exercices et templates - var entity = await _context.Sessions - .Include(s => s.Exercices) - .ThenInclude(e => e.ExerciceTemplate) - .FirstOrDefaultAsync(s => s.Id == session.Id); - - return CreatedAtAction(nameof(GetById), new { id = session.Id }, _mapper.Map(entity)); + var created = await _sessionService.CreateAsync(dto); + return CreatedAtAction(nameof(GetById), new { id = created.Id }, created); } [HttpPut("{id}")] public async Task Update(string id, [FromBody] UpdateSessionDto dto) { - var session = await _context.Sessions - .Include(s => s.Exercices) - .FirstOrDefaultAsync(s => s.Id == id); - if (session == null) return NotFound(); - - // Ne pas mapper TrainingProgramId pour éviter de casser la FK - session.Name = dto.Name; - session.Description = dto.Description; - session.Day = dto.Day; - session.Target = dto.Target; - // session.TrainingProgramId = dto.TrainingProgramId; // NE PAS TOUCHER - - // Supprime tous les exercices existants et sauvegarde - _context.ExerciceInstances.RemoveRange(session.Exercices); - await _context.SaveChangesAsync(); - - // Ajoute les nouveaux exercices - foreach (var exoDto in dto.Exercices) - { - var exo = _mapper.Map(exoDto); - exo.SessionId = session.Id; - _context.ExerciceInstances.Add(exo); - } - await _context.SaveChangesAsync(); - + var success = await _sessionService.UpdateAsync(id, dto); + if (!success) return NotFound(); return NoContent(); } - + [HttpDelete("{id}")] public async Task Delete(string id) { - var session = await _context.Sessions - .Include(s => s.Exercices) - .FirstOrDefaultAsync(s => s.Id == id); - - if (session == null) return NotFound(); - - _context.ExerciceInstances.RemoveRange(session.Exercices); - _context.Sessions.Remove(session); - - await _context.SaveChangesAsync(); + var success = await _sessionService.DeleteAsync(id); + if (!success) return NotFound(); return NoContent(); } } \ No newline at end of file diff --git a/src/TrainingSvc/Controllers/TrainingProgramsController.cs b/src/TrainingSvc/Controllers/TrainingProgramsController.cs new file mode 100644 index 0000000..0a92d3e --- /dev/null +++ b/src/TrainingSvc/Controllers/TrainingProgramsController.cs @@ -0,0 +1,32 @@ +using Microsoft.AspNetCore.Mvc; +using TrainingSvc.DTOs; +using TrainingSvc.IServices; + +namespace TrainingSvc.Controllers; + +[ApiController] +[Route("api/training/[controller]")] +public class TrainingProgramsController : ControllerBase +{ + private readonly ITrainingProgramService _service; + + public TrainingProgramsController(ITrainingProgramService service) + { + _service = service; + } + + [HttpGet] + public async Task>> GetAll() + { + var list = await _service.GetAllAsync(); + return Ok(list); + } + + [HttpGet("{id}")] + public async Task> GetById(string id) + { + var dto = await _service.GetByIdAsync(id); + if (dto == null) return NotFound(); + return Ok(dto); + } +} \ No newline at end of file diff --git a/src/TrainingSvc/DTOs/TrainingProgramDto.cs b/src/TrainingSvc/DTOs/TrainingProgramDto.cs new file mode 100644 index 0000000..f3469fa --- /dev/null +++ b/src/TrainingSvc/DTOs/TrainingProgramDto.cs @@ -0,0 +1,17 @@ +using Shared.Enum; + +namespace TrainingSvc.DTOs; + +public class TrainingProgramDto +{ + public string Id { get; set; } + public string Lang { get; set; } + public string Name { get; set; } + public string? Description { get; set; } + public int WeekDuration { get; set; } + public int NbDays { get; set; } + public string OwnerId { get; set; } + public EGoal Goal { get; set; } + public EDifficulty Difficulty { get; set; } + public List Sessions { get; set; } = new(); +} \ No newline at end of file diff --git a/src/TrainingSvc/DTOs/UpdateSessionDto.cs b/src/TrainingSvc/DTOs/UpdateSessionDto.cs index f770e0e..35dd6a8 100644 --- a/src/TrainingSvc/DTOs/UpdateSessionDto.cs +++ b/src/TrainingSvc/DTOs/UpdateSessionDto.cs @@ -9,5 +9,4 @@ public class UpdateSessionDto public int Day { get; set; } public ETarget? Target { get; set; } public string TrainingProgramId { get; set; } - public List Exercices { get; set; } = new(); } \ No newline at end of file diff --git a/src/TrainingSvc/IServices/ISessionService.cs b/src/TrainingSvc/IServices/ISessionService.cs new file mode 100644 index 0000000..e05587b --- /dev/null +++ b/src/TrainingSvc/IServices/ISessionService.cs @@ -0,0 +1,12 @@ +using TrainingSvc.DTOs; + +namespace TrainingSvc.IServices; + +public interface ISessionService +{ + Task> GetAllAsync(); + Task GetByIdAsync(string id); + Task CreateAsync(CreateSessionDto dto); + Task UpdateAsync(string id, UpdateSessionDto dto); + Task DeleteAsync(string id); +} \ No newline at end of file diff --git a/src/TrainingSvc/IServices/ITrainingProgramService.cs b/src/TrainingSvc/IServices/ITrainingProgramService.cs new file mode 100644 index 0000000..dfa6546 --- /dev/null +++ b/src/TrainingSvc/IServices/ITrainingProgramService.cs @@ -0,0 +1,9 @@ +using TrainingSvc.DTOs; + +namespace TrainingSvc.IServices; + +public interface ITrainingProgramService +{ + Task> GetAllAsync(); + Task GetByIdAsync(string id); +} \ No newline at end of file diff --git a/src/TrainingSvc/Program.cs b/src/TrainingSvc/Program.cs index 368aaae..cd4e8de 100644 --- a/src/TrainingSvc/Program.cs +++ b/src/TrainingSvc/Program.cs @@ -11,7 +11,9 @@ builder.Services.AddScoped(); builder.Services.AddScoped(); builder.Services.AddScoped(); +builder.Services.AddScoped(); builder.Services.AddScoped(); +builder.Services.AddScoped(); // Add services to the container. builder.Services.AddControllers() diff --git a/src/TrainingSvc/Repositories/ITrainingProgramRepository.cs b/src/TrainingSvc/Repositories/ITrainingProgramRepository.cs index d702daa..86133d3 100644 --- a/src/TrainingSvc/Repositories/ITrainingProgramRepository.cs +++ b/src/TrainingSvc/Repositories/ITrainingProgramRepository.cs @@ -5,4 +5,6 @@ namespace TrainingSvc.Repositories; public interface ITrainingProgramRepository : IRepository { + Task> GetAllWithSessionsAsync(); + Task GetByIdWithSessionsAsync(string id); } \ No newline at end of file diff --git a/src/TrainingSvc/Repositories/SessionRepository.cs b/src/TrainingSvc/Repositories/SessionRepository.cs index e941edb..8853ded 100644 --- a/src/TrainingSvc/Repositories/SessionRepository.cs +++ b/src/TrainingSvc/Repositories/SessionRepository.cs @@ -1,3 +1,4 @@ +using Microsoft.EntityFrameworkCore; using TrainingSvc.Data; using TrainingSvc.Entities; @@ -8,4 +9,20 @@ public class SessionRepository : GenericRepository, ISessionRepository public SessionRepository(TrainingDbContext context) : base(context) { } + + public async Task> GetAllWithExercicesAndTemplatesAsync() + { + return await context.Sessions + .Include(s => s.Exercices) + .ThenInclude(e => e.ExerciceTemplate) + .ToListAsync(); + } + + public async Task GetByIdWithExercicesAndTemplatesAsync(string id) + { + return await context.Sessions + .Include(s => s.Exercices) + .ThenInclude(e => e.ExerciceTemplate) + .FirstOrDefaultAsync(s => s.Id == id); + } } \ No newline at end of file diff --git a/src/TrainingSvc/Repositories/TrainingProgramRepository.cs b/src/TrainingSvc/Repositories/TrainingProgramRepository.cs index 4b771d7..3453b25 100644 --- a/src/TrainingSvc/Repositories/TrainingProgramRepository.cs +++ b/src/TrainingSvc/Repositories/TrainingProgramRepository.cs @@ -1,3 +1,4 @@ +using Microsoft.EntityFrameworkCore; using TrainingSvc.Data; using TrainingSvc.Entities; @@ -8,4 +9,22 @@ public class TrainingProgramRepository : GenericRepository, ITr public TrainingProgramRepository(TrainingDbContext context) : base(context) { } + + public async Task> GetAllWithSessionsAsync() + { + return await context.TrainingPrograms + .Include(tp => tp.Sessions) + .ThenInclude(s => s.Exercices) + .ThenInclude(e => e.ExerciceTemplate) + .ToListAsync(); + } + + public async Task GetByIdWithSessionsAsync(string id) + { + return await context.TrainingPrograms + .Include(tp => tp.Sessions) + .ThenInclude(s => s.Exercices) + .ThenInclude(e => e.ExerciceTemplate) + .FirstOrDefaultAsync(tp => tp.Id == id); + } } \ No newline at end of file diff --git a/src/TrainingSvc/RequestHelpers/SessionProfile.cs b/src/TrainingSvc/RequestHelpers/SessionProfile.cs index 752e8dc..a2601b2 100644 --- a/src/TrainingSvc/RequestHelpers/SessionProfile.cs +++ b/src/TrainingSvc/RequestHelpers/SessionProfile.cs @@ -11,7 +11,8 @@ public class SessionProfile : Profile CreateMap() .ForMember(dest => dest.Exercices, opt => opt.MapFrom(src => src.Exercices)); - CreateMap(); + CreateMap() + .ForMember(dest => dest.Exercices, opt => opt.Ignore()); // Ignore Exercices to handle them separately CreateMap(); } } \ No newline at end of file diff --git a/src/TrainingSvc/RequestHelpers/TrainingProgramProfile.cs b/src/TrainingSvc/RequestHelpers/TrainingProgramProfile.cs new file mode 100644 index 0000000..5339819 --- /dev/null +++ b/src/TrainingSvc/RequestHelpers/TrainingProgramProfile.cs @@ -0,0 +1,13 @@ +using AutoMapper; +using TrainingSvc.DTOs; +using TrainingSvc.Entities; + +namespace TrainingSvc.RequestHelpers; + +public class TrainingProgramProfile : Profile +{ + public TrainingProgramProfile() + { + CreateMap(); + } +} \ No newline at end of file diff --git a/src/TrainingSvc/Services/SessionService.cs b/src/TrainingSvc/Services/SessionService.cs new file mode 100644 index 0000000..74a9257 --- /dev/null +++ b/src/TrainingSvc/Services/SessionService.cs @@ -0,0 +1,95 @@ +using AutoMapper; +using TrainingSvc.DTOs; +using TrainingSvc.Entities; +using TrainingSvc.Repositories; +using TrainingSvc.IServices; + +namespace TrainingSvc.Services; + +public class SessionService : ISessionService +{ + private readonly ISessionRepository _sessionRepo; + private readonly IExerciceInstanceRepository _exerciceRepo; + private readonly IExerciceTemplateRepository _exerciceTemplateRepo; + private readonly IMapper _mapper; + + public SessionService( + ISessionRepository sessionRepo, + IExerciceInstanceRepository exerciceRepo, + IExerciceTemplateRepository exerciceTemplateRepo, + IMapper mapper) + { + _sessionRepo = sessionRepo; + _exerciceRepo = exerciceRepo; + _exerciceTemplateRepo = exerciceTemplateRepo; + _mapper = mapper; + } + + public async Task> GetAllAsync() + { + var sessions = await ((SessionRepository)_sessionRepo).GetAllWithExercicesAndTemplatesAsync(); + return _mapper.Map>(sessions); + } + + public async Task GetByIdAsync(string id) + { + var entity = await ((SessionRepository)_sessionRepo).GetByIdWithExercicesAndTemplatesAsync(id); + return entity == null ? null : _mapper.Map(entity); + } + + public async Task CreateAsync(CreateSessionDto dto) + { + // Ne mappe pas les exercices ici + var session = _mapper.Map(dto); + session.Exercices.Clear(); // S'assure qu'il n'y a pas d'exercices liés + + await _sessionRepo.InsertAsync(session); + await _sessionRepo.SaveChangesAsync(); + + foreach (var exoDto in dto.Exercices) + { + if (!string.IsNullOrEmpty(exoDto.ExerciceTemplateId)) + { + var exists = await _exerciceTemplateRepo.ExistsAsync(t => t.Id == exoDto.ExerciceTemplateId); + if (!exists) + throw new ArgumentException("ExerciceTemplateId invalide."); + } + var exo = _mapper.Map(exoDto); + exo.SessionId = session.Id; + await _exerciceRepo.InsertAsync(exo); + } + await _exerciceRepo.SaveChangesAsync(); + + var entity = await ((SessionRepository)_sessionRepo).GetByIdWithExercicesAndTemplatesAsync(session.Id); + return _mapper.Map(entity); + } + + public async Task UpdateAsync(string id, UpdateSessionDto dto) + { + var session = await ((SessionRepository)_sessionRepo).GetByIdWithExercicesAndTemplatesAsync(id); + if (session == null) return false; + + _mapper.Map(dto, session); + _sessionRepo.Update(session); + await _sessionRepo.SaveChangesAsync(); + + return true; + } + + public async Task DeleteAsync(string id) + { + var session = await _sessionRepo.GetByIdAsync(id, s => s.Exercices); + if (session == null) return false; + + foreach (var ex in session.Exercices.ToList()) + { + _exerciceRepo.Delete(ex.Id); + } + await _exerciceRepo.SaveChangesAsync(); + + _sessionRepo.Delete(id); + await _sessionRepo.SaveChangesAsync(); + + return true; + } +} \ No newline at end of file diff --git a/src/TrainingSvc/Services/TrainingProgramService.cs b/src/TrainingSvc/Services/TrainingProgramService.cs new file mode 100644 index 0000000..41fcd15 --- /dev/null +++ b/src/TrainingSvc/Services/TrainingProgramService.cs @@ -0,0 +1,30 @@ +using AutoMapper; +using TrainingSvc.DTOs; +using TrainingSvc.IServices; +using TrainingSvc.Repositories; + +namespace TrainingSvc.Services; + +public class TrainingProgramService : ITrainingProgramService +{ + private readonly ITrainingProgramRepository _repo; + private readonly IMapper _mapper; + + public TrainingProgramService(ITrainingProgramRepository repo, IMapper mapper) + { + _repo = repo; + _mapper = mapper; + } + + public async Task> GetAllAsync() + { + var list = await _repo.GetAllWithSessionsAsync(); + return _mapper.Map>(list); + } + + public async Task GetByIdAsync(string id) + { + var entity = await _repo.GetByIdWithSessionsAsync(id); + return entity == null ? null : _mapper.Map(entity); + } +} \ No newline at end of file