implement

This commit is contained in:
2026-02-09 21:21:17 +01:00
parent ff18d7b913
commit 2032a74ecd
14 changed files with 441 additions and 6 deletions

View File

@@ -0,0 +1,13 @@
using AipsCore.Application.Abstract.Command;
using AipsCore.Domain.Models.Shape.ValueObjects;
namespace AipsCore.Application.Models.Shape.Command.CreateRectangle;
public record CreateRectangleCommand(
string WhiteboardId,
int PositionX,
int PositionY,
string Color,
int EndPositionX,
int EndPositionY,
int BorderThickness) : ICommand<ShapeId>;

View File

@@ -0,0 +1,35 @@
using AipsCore.Application.Abstract.Command;
using AipsCore.Domain.Abstract;
using AipsCore.Domain.Models.Shape.External;
using AipsCore.Domain.Models.Shape.Sub.Rectangle;
using AipsCore.Domain.Models.Shape.ValueObjects;
namespace AipsCore.Application.Models.Shape.Command.CreateRectangle;
public class CreateRectangleCommandHandler : ICommandHandler<CreateRectangleCommand, ShapeId>
{
private readonly IShapeRepository _shapeRepository;
private readonly IUnitOfWork _unitOfWork;
public CreateRectangleCommandHandler(IShapeRepository shapeRepository, IUnitOfWork unitOfWork)
{
_shapeRepository = shapeRepository;
_unitOfWork = unitOfWork;
}
public async Task<ShapeId> Handle(CreateRectangleCommand command, CancellationToken cancellationToken = default)
{
var rectangle = Rectangle.Create(
command.WhiteboardId,
command.PositionX, command.PositionY,
command.Color,
command.EndPositionX,
command.EndPositionY,
command.BorderThickness);
await _shapeRepository.Add(rectangle, cancellationToken);
await _unitOfWork.SaveChangesAsync(cancellationToken);
return rectangle.Id;
}
}

View File

@@ -0,0 +1,9 @@
using AipsCore.Domain.Models.Shape.ValueObjects;
namespace AipsCore.Domain.Models.Shape.External;
public interface IShapeRepository
{
Task<Shape?> Get(ShapeId id, CancellationToken cancellationToken = default);
Task Add(Shape shape, CancellationToken cancellationToken = default);
}

View File

@@ -7,7 +7,7 @@ namespace AipsCore.Domain.Models.Shape;
public abstract class Shape
{
public ShapeId Id { get; }
public ShapeId Id { get; init; }
public WhiteboardId WhiteboardId { get; private set; }
@@ -24,4 +24,16 @@ public abstract class Shape
Color = color;
WhiteboardId = whiteboardId;
}
protected Shape(
string id,
string whiteboardId,
int positionX, int positionY,
string color)
{
Id = new ShapeId(id);
Position = new Position(positionX, positionY);
Color = new Color(color);
WhiteboardId = new WhiteboardId(whiteboardId);
}
}

View File

@@ -17,4 +17,21 @@ public class Arrow : Shape
}
public override ShapeType ShapeType => ShapeType.Arrow;
public static Arrow Create(
string id,
string whiteboardId,
int positionX, int positionY,
string color,
int endPositionX, int endPositionY,
int borderThickness)
{
return new Arrow(
new ShapeId(id),
new WhiteboardId(whiteboardId),
new Position(positionX, positionY),
new Color(color),
new Position(endPositionX, endPositionY),
new Thickness(borderThickness));
}
}

View File

@@ -17,4 +17,21 @@ public class Line : Shape
}
public override ShapeType ShapeType => ShapeType.Line;
public static Line Create(
string id,
string whiteboardId,
int positionX, int positionY,
string color,
int endPositionX, int endPositionY,
int borderThickness)
{
return new Line(
new ShapeId(id),
new WhiteboardId(whiteboardId),
new Position(positionX, positionY),
new Color(color),
new Position(endPositionX, endPositionY),
new Thickness(borderThickness));
}
}

View File

@@ -20,4 +20,36 @@ public class Rectangle : Shape
BorderThickness = borderThickness;
}
public static Rectangle Create(
string id,
string whiteboardId,
int positionX, int positionY,
string color,
int endPositionX, int endPositionY,
int borderThickness)
{
return new Rectangle(
new ShapeId(id),
new WhiteboardId(whiteboardId),
new Position(positionX, positionY),
new Color(color),
new Position(endPositionX, endPositionY),
new Thickness(borderThickness));
}
public static Rectangle Create(
string whiteboardId,
int positionX, int positionY,
string color,
int endPositionX, int endPositionY,
int borderThickness)
{
return new Rectangle(
ShapeId.Any(),
new WhiteboardId(whiteboardId),
new Position(positionX, positionY),
new Color(color),
new Position(endPositionX, endPositionY),
new Thickness(borderThickness));
}
}

View File

@@ -20,4 +20,20 @@ public class TextShape : Shape
}
public override ShapeType ShapeType => ShapeType.Text;
public static TextShape Create(
string id,
string whiteboardId,
int positionX, int positionY,
string color,
string textValue, int textSize)
{
return new TextShape(
new ShapeId(id),
new WhiteboardId(whiteboardId),
new Position(positionX, positionY),
new Color(color),
new TextShapeValue(textValue),
new TextShapeSize(textSize));
}
}

View File

@@ -2,4 +2,7 @@ using AipsCore.Domain.Common.ValueObjects;
namespace AipsCore.Domain.Models.Shape.ValueObjects;
public record ShapeId(string Value) : DomainId(Value);
public record ShapeId(string Value) : DomainId(Value)
{
public static ShapeId Any() => new ShapeId(Guid.NewGuid().ToString());
}

View File

@@ -6,22 +6,23 @@ namespace AipsCore.Domain.Models.Shape.ValueObjects;
public record Thickness : AbstractValueObject
{
public int Value { get; }
private const int MaxThickness = 8;
private const int MinThickness = 1;
private readonly int _value;
public Thickness(int value)
{
_value = value;
Value = value;
Validate();
}
protected override ICollection<IRule> GetValidationRules()
{
return
[
new MinValueRule<int>(_value, MinThickness),
new MaxValueRule<int>(_value, MaxThickness),
new MinValueRule<int>(Value, MinThickness),
new MaxValueRule<int>(Value, MaxThickness),
];
}
}

View File

@@ -6,6 +6,7 @@ public class AipsDbContext : DbContext
{
public DbSet<User.User> Users { get; set; }
public DbSet<Whiteboard.Whiteboard> Whiteboards { get; set; }
public DbSet<Shape.Shape> Shapes { get; set; }
public AipsDbContext(DbContextOptions<AipsDbContext> options)
: base(options)

View File

@@ -0,0 +1,33 @@
using System.ComponentModel.DataAnnotations;
using AipsCore.Domain.Models.Shape.Enums;
namespace AipsCore.Infrastructure.Persistence.Shape;
public class Shape
{
[Key]
public Guid Id { get; set; }
// NAV TO WHITEBOARD
public Guid WhiteboardId { get; set; }
public Whiteboard.Whiteboard Whiteboard { get; set; } = null!;
public ShapeType Type { get; set; }
public int PositionX { get; set; }
public int PositionY { get; set; }
[MaxLength(10)] public string Color { get; set; } = null!;
// END POSITION (Rectangle, Line, Arrow)
public int? EndPositionX { get; set; }
public int? EndPositionY { get; set; }
// THICKNESS (Rectangle, Line, Arrow)
public int? Thickness { get; set; }
// TEXT (Text)
public string? TextValue { get; set; }
public int? TextSize { get; set; }
}

View File

@@ -0,0 +1,205 @@
using AipsCore.Domain.Models.Shape.Enums;
using AipsCore.Domain.Models.Shape.Sub.Arrow;
using AipsCore.Domain.Models.Shape.Sub.Line;
using AipsCore.Domain.Models.Shape.Sub.Rectangle;
using AipsCore.Domain.Models.Shape.Sub.TextShape;
namespace AipsCore.Infrastructure.Persistence.Shape;
public static class ShapeMappers
{
#region FROM_ENTITY
public static Domain.Models.Shape.Shape EntityToModel(Shape shape)
{
return shape.Type switch
{
ShapeType.Rectangle => EntityToRectangle(shape),
ShapeType.Line => EntityToLine(shape),
ShapeType.Arrow => EntityToArrow(shape),
ShapeType.Text => EntityToTextShape(shape),
_ => throw new ArgumentOutOfRangeException()
};
}
public static Rectangle EntityToRectangle(Shape shape)
{
return Rectangle.Create(
shape.Id.ToString(),
shape.WhiteboardId.ToString(),
shape.PositionX, shape.PositionY,
shape.Color,
shape.EndPositionX!.Value, shape.EndPositionY!.Value,
shape.Thickness!.Value);
}
public static Line EntityToLine(Shape shape)
{
return Line.Create(
shape.Id.ToString(),
shape.WhiteboardId.ToString(),
shape.PositionX, shape.PositionY,
shape.Color,
shape.EndPositionX!.Value, shape.EndPositionY!.Value,
shape.Thickness!.Value);
}
public static Arrow EntityToArrow(Shape shape)
{
return Arrow.Create(
shape.Id.ToString(),
shape.WhiteboardId.ToString(),
shape.PositionX, shape.PositionY,
shape.Color,
shape.EndPositionX!.Value, shape.EndPositionY!.Value,
shape.Thickness!.Value);
}
public static TextShape EntityToTextShape(Shape shape)
{
return TextShape.Create(
shape.Id.ToString(),
shape.WhiteboardId.ToString(),
shape.PositionX, shape.PositionY,
shape.Color,
shape.TextValue!, shape.TextSize!.Value);
}
#endregion
#region TO_ENTITY
public static Shape ModelToEntity(Domain.Models.Shape.Shape model)
{
return model.ShapeType switch
{
ShapeType.Rectangle => RectangleToEntity((Rectangle)model),
ShapeType.Line => LineToEntity((Line)model),
ShapeType.Arrow => ArrowToEntity((Arrow)model),
ShapeType.Text => TextShapeToEntity((TextShape)model),
_ => throw new ArgumentOutOfRangeException()
};
}
public static Shape RectangleToEntity(Rectangle rectangle)
{
return new Shape()
{
Id = new Guid(rectangle.Id.Value),
Type = rectangle.ShapeType,
WhiteboardId = new Guid(rectangle.WhiteboardId.IdValue),
PositionX = rectangle.Position.X,
PositionY = rectangle.Position.Y,
Color = rectangle.Color.Value,
//SPECIFIC
EndPositionX = rectangle.EndPosition.X,
EndPositionY = rectangle.EndPosition.Y,
Thickness = rectangle.BorderThickness.Value,
};
}
public static Shape LineToEntity(Line line)
{
return new Shape()
{
Id = new Guid(line.Id.Value),
Type = line.ShapeType,
WhiteboardId = new Guid(line.WhiteboardId.IdValue),
PositionX = line.Position.X,
PositionY = line.Position.Y,
Color = line.Color.Value,
//SPECIFIC
EndPositionX = line.EndPosition.X,
EndPositionY = line.EndPosition.Y,
Thickness = line.Thickness.Value,
};
}
public static Shape ArrowToEntity(Arrow arrow)
{
return new Shape()
{
Id = new Guid(arrow.Id.Value),
Type = arrow.ShapeType,
WhiteboardId = new Guid(arrow.WhiteboardId.IdValue),
PositionX = arrow.Position.X,
PositionY = arrow.Position.Y,
Color = arrow.Color.Value,
//SPECIFIC
EndPositionX = arrow.EndPosition.X,
EndPositionY = arrow.EndPosition.Y,
Thickness = arrow.Thickness.Value,
};
}
public static Shape TextShapeToEntity(TextShape textShape)
{
return new Shape()
{
Id = new Guid(textShape.Id.Value),
Type = textShape.ShapeType,
WhiteboardId = new Guid(textShape.WhiteboardId.IdValue),
PositionX = textShape.Position.X,
PositionY = textShape.Position.Y,
Color = textShape.Color.Value,
//SPECIFIC
TextValue = textShape.TextShapeValue.Text,
TextSize = textShape.TextShapeSize.Size,
};
}
#endregion
#region UPDATE_ENTITY
public static void UpdateEntity(Shape entity, Domain.Models.Shape.Shape model)
{
entity.WhiteboardId = new Guid(model.WhiteboardId.IdValue);
entity.PositionX = model.Position.X;
entity.PositionY = model.Position.Y;
entity.Color = model.Color.Value;
switch (model.ShapeType)
{
case ShapeType.Rectangle:
UpdateEntityFromRectangle(entity, (Rectangle)model);
break;
case ShapeType.Line:
UpdateEntityFromLine(entity, (Line)model);
break;
case ShapeType.Arrow:
UpdateEntityFromArrow(entity, (Arrow)model);
break;
case ShapeType.Text:
UpdateEntityFromTextShape(entity, (TextShape)model);
break;
};
}
public static void UpdateEntityFromRectangle(Shape entity, Rectangle rectangle)
{
entity.EndPositionX = rectangle.EndPosition.X;
entity.EndPositionY = rectangle.EndPosition.Y;
entity.Thickness = rectangle.BorderThickness.Value;
}
public static void UpdateEntityFromLine(Shape entity, Line line)
{
entity.EndPositionX = line.EndPosition.X;
entity.EndPositionY = line.EndPosition.Y;
entity.Thickness = line.Thickness.Value;
}
public static void UpdateEntityFromArrow(Shape entity, Arrow arrow)
{
entity.EndPositionX = arrow.EndPosition.X;
entity.EndPositionY = arrow.EndPosition.Y;
entity.Thickness = arrow.Thickness.Value;
}
public static void UpdateEntityFromTextShape(Shape entity, TextShape textShape)
{
entity.TextValue = textShape.TextShapeValue.Text;
entity.TextSize = textShape.TextShapeSize.Size;
}
#endregion
}

View File

@@ -0,0 +1,41 @@
using AipsCore.Domain.Models.Shape.External;
using AipsCore.Domain.Models.Shape.ValueObjects;
using AipsCore.Infrastructure.Persistence.Db;
namespace AipsCore.Infrastructure.Persistence.Shape;
public class ShapeRepository : IShapeRepository
{
private readonly AipsDbContext _context;
public ShapeRepository(AipsDbContext context)
{
_context = context;
}
public async Task<Domain.Models.Shape.Shape?> Get(ShapeId id, CancellationToken cancellationToken = default)
{
var entity = await _context.Shapes.FindAsync([new Guid(id.Value)], cancellationToken);
if (entity is null) return null;
return ShapeMappers.EntityToModel(entity);
}
public async Task Add(Domain.Models.Shape.Shape shape, CancellationToken cancellationToken = default)
{
var entity = await _context.Shapes.FindAsync([new Guid(shape.Id.Value)], cancellationToken);
if (entity is not null)
{
ShapeMappers.UpdateEntity(entity, shape);
}
else
{
var newEntity = ShapeMappers.ModelToEntity(shape);
await _context.Shapes.AddAsync(newEntity, cancellationToken);
}
}
}