Fase 3: Implementación de Listings (Avisos) - Entidades, Repositorio, API y Frontend Wizard integración
This commit is contained in:
46
src/SIGCM.API/Controllers/ListingsController.cs
Normal file
46
src/SIGCM.API/Controllers/ListingsController.cs
Normal file
@@ -0,0 +1,46 @@
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using SIGCM.Application.DTOs;
|
||||
using SIGCM.Domain.Entities;
|
||||
using SIGCM.Domain.Interfaces;
|
||||
|
||||
namespace SIGCM.API.Controllers;
|
||||
|
||||
[ApiController]
|
||||
[Route("api/[controller]")]
|
||||
public class ListingsController : ControllerBase
|
||||
{
|
||||
private readonly IListingRepository _repository;
|
||||
|
||||
public ListingsController(IListingRepository repository)
|
||||
{
|
||||
_repository = repository;
|
||||
}
|
||||
|
||||
[HttpPost]
|
||||
public async Task<IActionResult> Create(CreateListingDto dto)
|
||||
{
|
||||
var listing = new Listing
|
||||
{
|
||||
CategoryId = dto.CategoryId,
|
||||
OperationId = dto.OperationId,
|
||||
Title = dto.Title,
|
||||
Description = dto.Description,
|
||||
Price = dto.Price,
|
||||
Currency = dto.Currency,
|
||||
UserId = dto.UserId,
|
||||
Status = "Draft",
|
||||
CreatedAt = DateTime.UtcNow
|
||||
};
|
||||
|
||||
var id = await _repository.CreateAsync(listing, dto.Attributes);
|
||||
return Ok(new { id });
|
||||
}
|
||||
|
||||
[HttpGet("{id}")]
|
||||
public async Task<IActionResult> Get(int id)
|
||||
{
|
||||
var listing = await _repository.GetByIdAsync(id);
|
||||
if (listing == null) return NotFound();
|
||||
return Ok(listing);
|
||||
}
|
||||
}
|
||||
22
src/SIGCM.Application/DTOs/ListingDtos.cs
Normal file
22
src/SIGCM.Application/DTOs/ListingDtos.cs
Normal file
@@ -0,0 +1,22 @@
|
||||
namespace SIGCM.Application.DTOs;
|
||||
|
||||
public class CreateListingDto
|
||||
{
|
||||
public int CategoryId { get; set; }
|
||||
public int OperationId { get; set; }
|
||||
public required string Title { get; set; }
|
||||
public string? Description { get; set; }
|
||||
public decimal Price { get; set; }
|
||||
public string Currency { get; set; } = "ARS";
|
||||
public int? UserId { get; set; }
|
||||
|
||||
// Dictionary of AttributeDefinitionId -> Value
|
||||
public Dictionary<int, string> Attributes { get; set; } = new();
|
||||
}
|
||||
|
||||
public class ListingDto : CreateListingDto
|
||||
{
|
||||
public int Id { get; set; }
|
||||
public DateTime CreatedAt { get; set; }
|
||||
public string Status { get; set; }
|
||||
}
|
||||
17
src/SIGCM.Domain/Entities/Listing.cs
Normal file
17
src/SIGCM.Domain/Entities/Listing.cs
Normal file
@@ -0,0 +1,17 @@
|
||||
namespace SIGCM.Domain.Entities;
|
||||
|
||||
public class Listing
|
||||
{
|
||||
public int Id { get; set; }
|
||||
public int CategoryId { get; set; }
|
||||
public int OperationId { get; set; }
|
||||
public required string Title { get; set; }
|
||||
public string? Description { get; set; }
|
||||
public decimal Price { get; set; }
|
||||
public string Currency { get; set; } = "ARS"; // ARS, USD
|
||||
public DateTime CreatedAt { get; set; } = DateTime.UtcNow;
|
||||
public string Status { get; set; } = "Draft"; // Draft, Published, Sold, Paused
|
||||
public int? UserId { get; set; }
|
||||
|
||||
// Navigation properties logic will be handled manually via repositories in Dapper
|
||||
}
|
||||
9
src/SIGCM.Domain/Entities/ListingAttributeValue.cs
Normal file
9
src/SIGCM.Domain/Entities/ListingAttributeValue.cs
Normal file
@@ -0,0 +1,9 @@
|
||||
namespace SIGCM.Domain.Entities;
|
||||
|
||||
public class ListingAttributeValue
|
||||
{
|
||||
public int Id { get; set; }
|
||||
public int ListingId { get; set; }
|
||||
public int AttributeDefinitionId { get; set; }
|
||||
public required string Value { get; set; }
|
||||
}
|
||||
10
src/SIGCM.Domain/Interfaces/IListingRepository.cs
Normal file
10
src/SIGCM.Domain/Interfaces/IListingRepository.cs
Normal file
@@ -0,0 +1,10 @@
|
||||
using SIGCM.Domain.Entities;
|
||||
|
||||
namespace SIGCM.Domain.Interfaces;
|
||||
|
||||
public interface IListingRepository
|
||||
{
|
||||
Task<int> CreateAsync(Listing listing, Dictionary<int, string> attributes);
|
||||
Task<Listing?> GetByIdAsync(int id);
|
||||
Task<IEnumerable<Listing>> GetAllAsync();
|
||||
}
|
||||
@@ -123,6 +123,37 @@ BEGIN
|
||||
FOREIGN KEY (CategoryId) REFERENCES Categories(Id) ON DELETE CASCADE
|
||||
);
|
||||
END
|
||||
|
||||
IF NOT EXISTS (SELECT * FROM sys.tables WHERE name = 'Listings')
|
||||
BEGIN
|
||||
CREATE TABLE Listings (
|
||||
Id INT IDENTITY(1,1) PRIMARY KEY,
|
||||
CategoryId INT NOT NULL,
|
||||
OperationId INT NOT NULL,
|
||||
Title NVARCHAR(200) NOT NULL,
|
||||
Description NVARCHAR(MAX) NULL,
|
||||
Price DECIMAL(18,2) NOT NULL DEFAULT 0,
|
||||
Currency NVARCHAR(3) DEFAULT 'ARS',
|
||||
CreatedAt DATETIME2 DEFAULT GETUTCDATE(),
|
||||
Status NVARCHAR(20) DEFAULT 'Draft',
|
||||
UserId INT NULL,
|
||||
FOREIGN KEY (CategoryId) REFERENCES Categories(Id),
|
||||
FOREIGN KEY (OperationId) REFERENCES Operations(Id),
|
||||
FOREIGN KEY (UserId) REFERENCES Users(Id)
|
||||
);
|
||||
END
|
||||
|
||||
IF NOT EXISTS (SELECT * FROM sys.tables WHERE name = 'ListingAttributeValues')
|
||||
BEGIN
|
||||
CREATE TABLE ListingAttributeValues (
|
||||
Id INT IDENTITY(1,1) PRIMARY KEY,
|
||||
ListingId INT NOT NULL,
|
||||
AttributeDefinitionId INT NOT NULL,
|
||||
Value NVARCHAR(MAX) NOT NULL,
|
||||
FOREIGN KEY (ListingId) REFERENCES Listings(Id) ON DELETE CASCADE,
|
||||
FOREIGN KEY (AttributeDefinitionId) REFERENCES AttributeDefinitions(Id) ON DELETE NO ACTION
|
||||
);
|
||||
END
|
||||
";
|
||||
await connection.ExecuteAsync(schemaSql);
|
||||
}
|
||||
|
||||
@@ -18,6 +18,7 @@ public static class DependencyInjection
|
||||
services.AddScoped<ITokenService, Services.TokenService>();
|
||||
services.AddScoped<IAuthService, Services.AuthService>();
|
||||
services.AddScoped<IAttributeDefinitionRepository, AttributeDefinitionRepository>();
|
||||
services.AddScoped<IListingRepository, ListingRepository>();
|
||||
return services;
|
||||
}
|
||||
}
|
||||
@@ -25,3 +26,4 @@ public static class DependencyInjection
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
73
src/SIGCM.Infrastructure/Repositories/ListingRepository.cs
Normal file
73
src/SIGCM.Infrastructure/Repositories/ListingRepository.cs
Normal file
@@ -0,0 +1,73 @@
|
||||
using System.Data;
|
||||
using Dapper;
|
||||
using SIGCM.Application.DTOs;
|
||||
using SIGCM.Domain.Entities;
|
||||
using SIGCM.Domain.Interfaces;
|
||||
using SIGCM.Infrastructure.Data;
|
||||
|
||||
namespace SIGCM.Infrastructure.Repositories;
|
||||
|
||||
public class ListingRepository : IListingRepository
|
||||
{
|
||||
private readonly IDbConnectionFactory _connectionFactory;
|
||||
|
||||
public ListingRepository(IDbConnectionFactory connectionFactory)
|
||||
{
|
||||
_connectionFactory = connectionFactory;
|
||||
}
|
||||
|
||||
public async Task<int> CreateAsync(Listing listing, Dictionary<int, string> attributes)
|
||||
{
|
||||
using var conn = _connectionFactory.CreateConnection();
|
||||
conn.Open();
|
||||
using var transaction = conn.BeginTransaction();
|
||||
|
||||
try
|
||||
{
|
||||
var sqlListing = @"
|
||||
INSERT INTO Listings (CategoryId, OperationId, Title, Description, Price, Currency, CreatedAt, Status, UserId)
|
||||
VALUES (@CategoryId, @OperationId, @Title, @Description, @Price, @Currency, @CreatedAt, @Status, @UserId);
|
||||
SELECT CAST(SCOPE_IDENTITY() as int);";
|
||||
|
||||
var listingId = await conn.QuerySingleAsync<int>(sqlListing, listing, transaction);
|
||||
|
||||
if (attributes != null && attributes.Any())
|
||||
{
|
||||
var sqlAttr = @"
|
||||
INSERT INTO ListingAttributeValues (ListingId, AttributeDefinitionId, Value)
|
||||
VALUES (@ListingId, @AttributeDefinitionId, @Value)";
|
||||
|
||||
foreach (var attr in attributes)
|
||||
{
|
||||
await conn.ExecuteAsync(sqlAttr, new { ListingId = listingId, AttributeDefinitionId = attr.Key, Value = attr.Value }, transaction);
|
||||
}
|
||||
}
|
||||
|
||||
transaction.Commit();
|
||||
return listingId;
|
||||
}
|
||||
catch
|
||||
{
|
||||
transaction.Rollback();
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<Listing?> GetByIdAsync(int id)
|
||||
{
|
||||
using var conn = _connectionFactory.CreateConnection();
|
||||
return await conn.QuerySingleOrDefaultAsync<Listing>("SELECT * FROM Listings WHERE Id = @Id", new { Id = id });
|
||||
}
|
||||
|
||||
public async Task<IEnumerable<Listing>> GetAllAsync()
|
||||
{
|
||||
using var conn = _connectionFactory.CreateConnection();
|
||||
// A simple query for now
|
||||
var sql = @"
|
||||
SELECT l.*
|
||||
FROM Listings l
|
||||
ORDER BY l.CreatedAt DESC";
|
||||
|
||||
return await conn.QueryAsync<Listing>(sql);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user