course
This commit is contained in:
@@ -10,7 +10,6 @@ message UploadFileCommand{
|
||||
UploadFile file = 1;
|
||||
Id mediaGroupId = 2;
|
||||
string clientId = 3;
|
||||
string fileName = 4;
|
||||
}
|
||||
message CreateMediaGroupCommand{
|
||||
string key = 1;
|
||||
|
||||
@@ -0,0 +1,61 @@
|
||||
using Mapster;
|
||||
using System.Net.Http.Headers;
|
||||
using System.Net.Sockets;
|
||||
using VizhTalk.BuildingBlocks.Application.Configuration.Commnad;
|
||||
using VizhTalk.Grpc.CDN;
|
||||
using VizhTalk.Grpc.Learning.Course;
|
||||
using VizhTalk.Learning.Application.Exceptions;
|
||||
using VizhTalk.Learning.Domain.Entities;
|
||||
using VizhTalk.Learning.Domain.TypeIds;
|
||||
using VizhTalk.Learning.Domain.ValueObjects;
|
||||
|
||||
namespace VizhTalk.Learning.Application.CommandHandlers;
|
||||
|
||||
|
||||
public class CreateCourseCommandHandler(IUnitOfWork unitOfWork, CDNGrpc.CDNGrpcClient cdnClient) : ICommandHandler<CreateCourseCommand, CourseDTO>
|
||||
{
|
||||
public async Task<CourseDTO> Handle(CreateCourseCommand request, CancellationToken cancellationToken)
|
||||
{
|
||||
var language = await unitOfWork.Language.GetLanguageWithCoursesById(new LanguageId(request.Languageid), cancellationToken);
|
||||
if(language == null)
|
||||
throw new LanguageNotFoundException();
|
||||
|
||||
var fileGroup = await unitOfWork.FileGroup.GetByFileGroupKey(FileGroupKey.ImageCourse);
|
||||
if(fileGroup == null)
|
||||
{
|
||||
var group = await cdnClient.CreateMediaGroupAsync(new CreateMediaGroupCommand()
|
||||
{
|
||||
IsSecure = false,
|
||||
Key = FileGroupKey.ImageCourse.Value,
|
||||
Title = nameof(FileGroupKey.ImageCourse)
|
||||
});
|
||||
|
||||
fileGroup = FileGroup.CreateNew(FileGroupKey.IconLanguage, new FileGroupTitle(nameof(FileGroupKey.IconLanguage)), false, group.Id);
|
||||
await unitOfWork.FileGroup.AddAsync(fileGroup, cancellationToken);
|
||||
}
|
||||
var upload = await cdnClient.UploadFileAsync(new UploadFileCommand()
|
||||
{
|
||||
File = new UploadFile()
|
||||
{
|
||||
ContentType = request.Image.ContentType,
|
||||
File = request.Image.File,
|
||||
Name = request.Image.Name,
|
||||
},
|
||||
MediaGroupId = fileGroup.MediaGroupId,
|
||||
}, cancellationToken: cancellationToken);
|
||||
|
||||
var media = Media.CreateNew(request.Image.Name,upload.ContentType, upload.Size, upload.MediaGroupMediaId, new EntityType(EntityTypeEnum.Course));
|
||||
var course = language.AddCourse(request.Title, media, request.Description);
|
||||
await unitOfWork.Language.UpdateAsync(language,cancellationToken);
|
||||
await unitOfWork.SaveChangeAsync(cancellationToken);
|
||||
return course.Adapt<CourseDTO>();
|
||||
}
|
||||
}
|
||||
|
||||
public class UpdateCourseCommandHandler(IUnitOfWork unitOfWork) : ICommandHandler<UpdateCourseCommand, CourseDTO>
|
||||
{
|
||||
public Task<CourseDTO> Handle(UpdateCourseCommand request, CancellationToken cancellationToken)
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
@@ -47,7 +47,6 @@ public class CreateLanguageCommandHandler(IUnitOfWork unitOfWork,
|
||||
File = request.Icon.File,
|
||||
Name = request.Icon.Name,
|
||||
},
|
||||
FileName = title,
|
||||
MediaGroupId = fileGroup.MediaGroupId
|
||||
}, cancellationToken: cancellationToken);
|
||||
|
||||
@@ -86,7 +85,6 @@ public class UpdateLanguageCommandHandler(IUnitOfWork unitOfWork, CDNGrpc.CDNGrp
|
||||
File = request.Icon.File,
|
||||
Name = request.Icon.Name,
|
||||
},
|
||||
FileName = request.Title,
|
||||
MediaGroupId = fileGroup.MediaGroupId
|
||||
}, cancellationToken: cancellationToken);
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
using Mapster;
|
||||
using VizhTalk.Grpc.Learning;
|
||||
using VizhTalk.Grpc.Learning.Course;
|
||||
using VizhTalk.Grpc.Learning.Language;
|
||||
using VizhTalk.Learning.Domain.Entities;
|
||||
|
||||
@@ -9,16 +10,20 @@ namespace VizhTalk.Learning.Application.Mappers
|
||||
{
|
||||
public static void Process()
|
||||
{
|
||||
TypeAdapterConfig<Language, LanguageDTO>.NewConfig()
|
||||
.MapWith(lan => new LanguageDTO()
|
||||
{
|
||||
Id = lan.Id.Value,
|
||||
Title = lan.Title.Value,
|
||||
}).Compile();
|
||||
|
||||
var config = TypeAdapterConfig<Language, LanguageDTO>.NewConfig()
|
||||
.MapWith(lan => new LanguageDTO()
|
||||
{
|
||||
Id = lan.Id.Value,
|
||||
Title = lan.Title.Value,
|
||||
});
|
||||
TypeAdapterConfig<Course, CourseDTO>.NewConfig()
|
||||
.MapWith(lan => new CourseDTO()
|
||||
{
|
||||
Id = lan.Id.Value,
|
||||
Title = lan.Title.Value,
|
||||
}).Compile();
|
||||
|
||||
|
||||
config.Compile();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,21 +10,26 @@ public class Course : Entity<CourseId>
|
||||
{
|
||||
|
||||
}
|
||||
private Course(CourseTitle title, int order)
|
||||
private Course(CourseTitle title, int order, Media image, CourseDescription? description)
|
||||
{
|
||||
Title = title;
|
||||
Order = order;
|
||||
Image = image;
|
||||
Description = description;
|
||||
}
|
||||
public CourseTitle Title { get; private set; }
|
||||
public Media Image { get; private set; }
|
||||
public CourseDescription? Description { get; set; }
|
||||
public int Order { get; private set; }
|
||||
|
||||
internal static Course CreateNew(CourseTitle title, int order)
|
||||
=> new(title, order);
|
||||
internal static Course CreateNew(CourseTitle title, int order, Media image, CourseDescription? description)
|
||||
=> new(title, order, image, description);
|
||||
public void SetTitle(CourseTitle title)
|
||||
=> Title = title;
|
||||
public void SetOrder(int order)
|
||||
=> Order = order;
|
||||
public void SetImage(Media media)
|
||||
=> Image = media;
|
||||
public void SetDescription(CourseDescription? description)
|
||||
=> Description = description;
|
||||
}
|
||||
|
||||
@@ -20,7 +20,7 @@ public class Language : Entity<LanguageId>, IAggregateRoot
|
||||
public LanguageTitle Title { get; private set; }
|
||||
public Media Icon { get; private set; }
|
||||
public IReadOnlyList<Course> Courses => _courses;
|
||||
public static Language CreateNew(LanguageTitle title,Media icon)
|
||||
public static Language CreateNew(LanguageTitle title, Media icon)
|
||||
=> new(title, icon);
|
||||
|
||||
public void SetTitle(LanguageTitle title)
|
||||
@@ -32,21 +32,22 @@ public class Language : Entity<LanguageId>, IAggregateRoot
|
||||
|
||||
Icon = icon;
|
||||
}
|
||||
public Course AddCourse(CourseTitle courseTitle)
|
||||
public Course AddCourse(CourseTitle courseTitle, Media image, CourseDescription? courseDescription)
|
||||
{
|
||||
var order = Courses.Count;
|
||||
var course = Course.CreateNew(courseTitle, order);
|
||||
var course = Course.CreateNew(courseTitle, order, image, courseDescription);
|
||||
_courses.Add(course);
|
||||
return course;
|
||||
}
|
||||
public Course UpdateCourse(Course course)
|
||||
{
|
||||
var _course = _courses.FirstOrDefault(x => x.Id == course.Id) ?? throw new CourseNotFoundException(Title);
|
||||
if(course.Image != null)
|
||||
if (course.Image != null)
|
||||
_course.SetImage(course.Image);
|
||||
_course.SetTitle(course.Title);
|
||||
_course.SetDescription(course.Description);
|
||||
|
||||
if(_course.Order != course.Order)
|
||||
if (_course.Order != course.Order)
|
||||
{
|
||||
var orderCourses = _courses.Where(x => x.Id != course.Id)
|
||||
.OrderBy(x => x.Order)
|
||||
|
||||
@@ -6,5 +6,5 @@ namespace VizhTalk.Learning.Domain.Repositories;
|
||||
|
||||
public interface ILanguageRepository : IRepositoryBase<Language,LanguageId>
|
||||
{
|
||||
|
||||
Task<Language?> GetLanguageWithCoursesById(LanguageId languageId ,CancellationToken cancellationToken = default);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
using VizhTalk.BuildingBlocks.Domain;
|
||||
using VizhTalk.BuildingBlocks.Domain.Guards;
|
||||
|
||||
namespace VizhTalk.Learning.Domain.ValueObjects;
|
||||
|
||||
public class CourseDescription : ValueObject
|
||||
{
|
||||
public const short MaxLength = 800;
|
||||
public CourseDescription(string description)
|
||||
{
|
||||
Guard.NullOrEmpty(description,nameof(CourseDescription));
|
||||
Guard.MaxLength(description, nameof(CourseDescription), MaxLength);
|
||||
Value = description;
|
||||
}
|
||||
public string Value { get; set; }
|
||||
|
||||
public static implicit operator string(CourseDescription title) => title.Value;
|
||||
public static implicit operator CourseDescription(string title) => new(title);
|
||||
}
|
||||
@@ -14,5 +14,6 @@ public class CourseTitle
|
||||
public string Value { get; private set; }
|
||||
|
||||
public static implicit operator string(CourseTitle title) => title.Value;
|
||||
public static implicit operator CourseTitle(string title) => new(title);
|
||||
|
||||
}
|
||||
|
||||
@@ -12,5 +12,6 @@ public class EntityType : ValueObject
|
||||
}
|
||||
public enum EntityTypeEnum
|
||||
{
|
||||
Language = 0
|
||||
Language = 0,
|
||||
Course = 1
|
||||
}
|
||||
|
||||
@@ -11,6 +11,7 @@ namespace VizhTalk.Learning.Domain.ValueObjects
|
||||
public class FileGroupKey : ValueObject
|
||||
{
|
||||
public static FileGroupKey IconLanguage = new("IconLanguage");
|
||||
public static FileGroupKey ImageCourse = new("ImageCourse");
|
||||
public FileGroupKey(string entityName, string? entityId = null)
|
||||
{
|
||||
Guard.NullOrEmpty(entityName, entityName);
|
||||
|
||||
@@ -1,11 +1,7 @@
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.EntityFrameworkCore.Metadata.Builders;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using VizhTalk.Learning.Domain.Entities;
|
||||
using VizhTalk.Learning.Domain.ValueObjects;
|
||||
|
||||
namespace VizhTalk.Learning.Infrastructure.EF.Configurations
|
||||
{
|
||||
@@ -23,6 +19,12 @@ namespace VizhTalk.Learning.Infrastructure.EF.Configurations
|
||||
builder.Property(x => x.Title)
|
||||
.HasConversion(x => x.Value, x => new(x));
|
||||
|
||||
builder.Property(x => x.Description)
|
||||
.HasMaxLength(CourseDescription.MaxLength)
|
||||
.HasConversion(x => x.Value, x => new(x));
|
||||
|
||||
builder.Navigation(x => x.Image)
|
||||
.AutoInclude();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -24,6 +24,9 @@ namespace VizhTalk.Learning.Infrastructure.EF.Configurations
|
||||
builder.Property(x => x.Title)
|
||||
.HasConversion(x => x.Value, x => new LanguageTitle(x))
|
||||
.HasMaxLength(60);
|
||||
|
||||
builder.Navigation(x => x.Icon)
|
||||
.AutoInclude();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
|
||||
using VizhTalk.BuildingBlocks.Domain;
|
||||
using VizhTalk.BuildingBlocks.Infrastructure.EF.Helpers;
|
||||
using VizhTalk.Learning.Domain.Entities;
|
||||
using VizhTalk.Learning.Infrastructure.Configuration;
|
||||
|
||||
namespace VizhTalk.Learning.Infrastructure.EF;
|
||||
@@ -22,5 +23,6 @@ public class LearningDbContext : DbContext
|
||||
modelBuilder.FindEntities(Assemblies.Domain)
|
||||
.ApplyConfigurationsFromAssembly(Assemblies.Infrastructure, x => x.FullName.Contains("EF.Configurations"))
|
||||
.PluralizeEntities();
|
||||
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,5 @@
|
||||
using VizhTalk.BuildingBlocks.Infrastructure;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using VizhTalk.BuildingBlocks.Infrastructure;
|
||||
using VizhTalk.Learning.Domain.Entities;
|
||||
using VizhTalk.Learning.Domain.Repositories;
|
||||
using VizhTalk.Learning.Domain.TypeIds;
|
||||
@@ -8,8 +9,18 @@ namespace VizhTalk.Learning.Infrastructure.Repositories
|
||||
{
|
||||
internal class LanguageRepository : RepositoryBase<Language, LanguageId>, ILanguageRepository
|
||||
{
|
||||
private readonly LearningDbContext dbContext;
|
||||
|
||||
public LanguageRepository(LearningDbContext dbContext) : base(dbContext)
|
||||
{
|
||||
this.dbContext = dbContext;
|
||||
}
|
||||
|
||||
public async Task<Language?> GetLanguageWithCoursesById(LanguageId languageId, CancellationToken cancellationToken = default)
|
||||
{
|
||||
return await dbContext.Set<Language>()
|
||||
.Include(x => x.Courses)
|
||||
.FirstOrDefaultAsync(x => x.Id == languageId, cancellationToken);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
using VizhTalk.BuildingBlocks.Domain.Contract;
|
||||
|
||||
namespace VizhTalk.Grpc.Learning.Course;
|
||||
|
||||
public sealed partial class CreateCourseCommand: ICommand<CourseDTO>;
|
||||
public sealed partial class UpdateCourseCommand : ICommand<CourseDTO>;
|
||||
@@ -1,5 +1,4 @@
|
||||
using VizhTalk.BuildingBlocks.Domain.Contract;
|
||||
using VizhTalk.Grpc.Learning.Language;
|
||||
|
||||
namespace VizhTalk.Grpc.Learning.Language;
|
||||
|
||||
|
||||
@@ -8,11 +8,15 @@ import "Proto/Shared.proto";
|
||||
|
||||
message CreateCourseCommand{
|
||||
string title = 1;
|
||||
bytes icon = 2;
|
||||
string description = 2;
|
||||
UploadFile image = 3;
|
||||
Id languageid = 4;
|
||||
}
|
||||
|
||||
message UpdateCourseCommand{
|
||||
Id Id = 1;
|
||||
string title = 2;
|
||||
optional bytes icon = 3;
|
||||
string description = 3;
|
||||
optional UploadFile image = 4;
|
||||
Id languageid = 5;
|
||||
}
|
||||
@@ -1,20 +1,30 @@
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Vizhtalk.CDN.Presentation.Application;
|
||||
using Vizhtalk.CDN.Presentation.Domain.TypeIds;
|
||||
|
||||
namespace Vizhtalk.CDN.Presentation.Controllers
|
||||
{
|
||||
[Route("api/[controller]")]
|
||||
[Route("[controller]")]
|
||||
[ApiController]
|
||||
public class FileController : ControllerBase
|
||||
public class FileController(IUnitOfWork unitOfWork) : ControllerBase
|
||||
{
|
||||
|
||||
[HttpGet("{id}")]
|
||||
public IActionResult Get(MediaGroupMediaId id)
|
||||
public async Task<IActionResult> Get(Guid id)
|
||||
{
|
||||
var mediaGroupMedia = await unitOfWork.MediaGroupMedia
|
||||
.GetDetailsById(new MediaGroupMediaId(id));
|
||||
|
||||
|
||||
if (mediaGroupMedia == null)
|
||||
return NotFound();
|
||||
|
||||
return Unauthorized();
|
||||
if(!mediaGroupMedia.MediaGroup.IsSecure)
|
||||
{
|
||||
var fileBytes = System.IO.File.ReadAllBytes(mediaGroupMedia.Media.Path);
|
||||
return File(fileBytes, mediaGroupMedia.Media.ContentType.Value);
|
||||
}
|
||||
|
||||
return NotFound();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -23,8 +23,8 @@ public class MediaGroupMedia : Entity<MediaGroupMediaId>
|
||||
public FileName Name { get; private set; }
|
||||
public MediaId MediaId { get; private set; }
|
||||
public MediaGroupId MediaGroupId { get; private set; }
|
||||
public Media Media { get; private set; }
|
||||
public MediaGroup MediaGroup { get; private set; }
|
||||
public Media Media { get; set; }
|
||||
public MediaGroup MediaGroup { get; set; }
|
||||
public IReadOnlyList<MediaPermission> MediaPermissions => _mediaPermissions;
|
||||
|
||||
internal static MediaGroupMedia CreateNew(FileName fileName,
|
||||
|
||||
@@ -4,4 +4,7 @@ using VizhTalk.BuildingBlocks.Domain;
|
||||
|
||||
namespace Vizhtalk.CDN.Presentation.Domain.Repositories;
|
||||
|
||||
public interface IMediaGroupMediaRepository : IRepositoryBase<MediaGroupMedia, MediaGroupMediaId>;
|
||||
public interface IMediaGroupMediaRepository : IRepositoryBase<MediaGroupMedia, MediaGroupMediaId>
|
||||
{
|
||||
Task<MediaGroupMedia?> GetDetailsById(MediaGroupMediaId mediaGroupMediaId,CancellationToken cancellationToken = default);
|
||||
}
|
||||
|
||||
@@ -9,8 +9,29 @@ namespace Vizhtalk.CDN.Presentation.Infrastructure.Repositories
|
||||
{
|
||||
public class MediaGroupMediaRepository : RepositoryBase<MediaGroupMedia, MediaGroupMediaId>, IMediaGroupMediaRepository
|
||||
{
|
||||
private readonly CDNDbContext dbContext;
|
||||
|
||||
public MediaGroupMediaRepository(CDNDbContext dbContext) : base(dbContext)
|
||||
{
|
||||
this.dbContext = dbContext;
|
||||
}
|
||||
|
||||
public async Task<MediaGroupMedia?> GetDetailsById(MediaGroupMediaId mediaGroupMediaId, CancellationToken cancellationToken = default)
|
||||
{
|
||||
var mediaGroupMedia = await dbContext.Set<MediaGroupMedia>()
|
||||
.FirstOrDefaultAsync(x => x.Id == mediaGroupMediaId, cancellationToken);
|
||||
|
||||
if (mediaGroupMedia == null)
|
||||
return null;
|
||||
|
||||
// پر کردن دستی navigation properties
|
||||
mediaGroupMedia.MediaGroup = await dbContext.Set<MediaGroup>()
|
||||
.FirstOrDefaultAsync(mg => mg.Id == mediaGroupMedia.MediaGroupId, cancellationToken);
|
||||
|
||||
mediaGroupMedia.Media = await dbContext.Set<Media>()
|
||||
.FirstOrDefaultAsync(m => m.Id == mediaGroupMedia.MediaId, cancellationToken);
|
||||
|
||||
return mediaGroupMedia;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,6 +8,7 @@ var builder = WebApplication.CreateBuilder(args);
|
||||
var connectionString = builder.Configuration.GetConnectionString("VizhTalk.CDN")!;
|
||||
var databaseName = builder.Configuration.GetConnectionString("VizhTalk.CDN.DatabaseName")!;
|
||||
|
||||
builder.Services.AddControllers();
|
||||
builder.Services.Initialize(connectionString, databaseName);
|
||||
builder.Services.AddGrpc(op =>
|
||||
{
|
||||
@@ -18,10 +19,10 @@ var app = builder.Build();
|
||||
|
||||
|
||||
|
||||
app.MapGet("/", () => "Hello World!");
|
||||
app.UseRouting();
|
||||
|
||||
app.MapGrpcService<CDNService>();
|
||||
|
||||
app.MapControllers();
|
||||
//app.UseCDNModule();
|
||||
|
||||
app.Run();
|
||||
|
||||
Reference in New Issue
Block a user