Phase 2: Backend API Implementation - DTOs, Services, and Controllers

This commit is contained in:
steinhelge
2025-11-23 15:55:03 +01:00
parent 0661bebd87
commit 9ed5adbfb5
22 changed files with 1066 additions and 33 deletions
@@ -0,0 +1,55 @@
using Hospitality.Backend.DTOs;
using Hospitality.Backend.Services;
using Microsoft.AspNetCore.Mvc;
namespace Hospitality.Backend.Controllers;
[ApiController]
[Route("api/[controller]")]
public class EventsController : ControllerBase
{
private readonly IEventService _eventService;
public EventsController(IEventService eventService)
{
_eventService = eventService;
}
[HttpGet]
public async Task<ActionResult<IEnumerable<EventListResponse>>> GetAll()
{
var events = await _eventService.GetAllEventsAsync();
return Ok(events);
}
[HttpGet("{id}")]
public async Task<ActionResult<EventResponse>> GetById(Guid id)
{
var evt = await _eventService.GetEventByIdAsync(id);
if (evt == null) return NotFound();
return Ok(evt);
}
[HttpPost]
public async Task<ActionResult<EventResponse>> Create(CreateEventRequest request)
{
var evt = await _eventService.CreateEventAsync(request);
return CreatedAtAction(nameof(GetById), new { id = evt.Id }, evt);
}
[HttpPut("{id}")]
public async Task<ActionResult<EventResponse>> Update(Guid id, UpdateEventRequest request)
{
var evt = await _eventService.UpdateEventAsync(id, request);
if (evt == null) return NotFound();
return Ok(evt);
}
[HttpDelete("{id}")]
public async Task<IActionResult> Delete(Guid id)
{
var result = await _eventService.DeleteEventAsync(id);
if (!result) return NotFound();
return NoContent();
}
}
@@ -0,0 +1,96 @@
using Hospitality.Backend.DTOs;
using Hospitality.Backend.Services;
using Microsoft.AspNetCore.Mvc;
namespace Hospitality.Backend.Controllers;
[ApiController]
[Route("api")]
public class GroupsController : ControllerBase
{
private readonly IGroupService _groupService;
public GroupsController(IGroupService groupService)
{
_groupService = groupService;
}
[HttpGet("events/{eventId}/groups")]
public async Task<ActionResult<IEnumerable<GroupResponse>>> GetGroupsByEvent(Guid eventId)
{
var groups = await _groupService.GetGroupsByEventIdAsync(eventId);
return Ok(groups);
}
[HttpGet("groups/{id}")]
public async Task<ActionResult<GroupDetailResponse>> GetGroupById(Guid id)
{
var group = await _groupService.GetGroupByIdAsync(id);
if (group == null) return NotFound();
return Ok(group);
}
[HttpPost("events/{eventId}/groups")]
public async Task<ActionResult<GroupResponse>> CreateGroup(Guid eventId, CreateGroupRequest request)
{
var group = await _groupService.CreateGroupAsync(eventId, request);
return CreatedAtAction(nameof(GetGroupById), new { id = group.Id }, group);
}
[HttpPut("groups/{id}")]
public async Task<ActionResult<GroupResponse>> UpdateGroup(Guid id, UpdateGroupRequest request)
{
var group = await _groupService.UpdateGroupAsync(id, request);
if (group == null) return NotFound();
return Ok(group);
}
[HttpDelete("groups/{id}")]
public async Task<IActionResult> DeleteGroup(Guid id)
{
var result = await _groupService.DeleteGroupAsync(id);
if (!result) return NotFound();
return NoContent();
}
[HttpPost("groups/{groupId}/people")]
public async Task<ActionResult<PersonResponse>> AddPerson(Guid groupId, CreatePersonRequest request)
{
var person = await _groupService.AddPersonToGroupAsync(groupId, request);
return CreatedAtAction(nameof(GetPersonById), new { id = person.Id }, person);
}
[HttpGet("people/{id}")]
public async Task<ActionResult<PersonDetailResponse>> GetPersonById(Guid id)
{
var person = await _groupService.GetPersonByIdAsync(id);
if (person == null) return NotFound();
return Ok(person);
}
[HttpPut("people/{id}")]
public async Task<ActionResult<PersonResponse>> UpdatePerson(Guid id, UpdatePersonRequest request)
{
var person = await _groupService.UpdatePersonAsync(id, request);
if (person == null) return NotFound();
return Ok(person);
}
[HttpDelete("people/{id}")]
public async Task<IActionResult> DeletePerson(Guid id)
{
var result = await _groupService.DeletePersonAsync(id);
if (!result) return NotFound();
return NoContent();
}
[HttpPost("people/{personId}/quotas")]
public async Task<IActionResult> AssignQuota(Guid personId, [FromBody] AssignQuotaRequest request)
{
var result = await _groupService.AssignQuotaAsync(personId, request.ProductId, request.InitialAmount);
if (!result) return NotFound();
return NoContent();
}
}
public record AssignQuotaRequest(Guid ProductId, int InitialAmount);
@@ -1,33 +0,0 @@
using Hospitality.Domain.Entities;
using Hospitality.Infrastructure.Data;
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
namespace Hospitality.Backend.Controllers;
[ApiController]
[Route("api/[controller]")]
public class PeopleController : ControllerBase
{
private readonly HospitalityDbContext _context;
public PeopleController(HospitalityDbContext context)
{
_context = context;
}
[HttpGet]
public async Task<ActionResult<IEnumerable<Person>>> GetPeople()
{
return await _context.People.ToListAsync();
}
[HttpPost]
public async Task<ActionResult<Person>> PostPerson(Person person)
{
_context.People.Add(person);
await _context.SaveChangesAsync();
return CreatedAtAction(nameof(GetPeople), new { id = person.Id }, person);
}
}
@@ -0,0 +1,47 @@
using Hospitality.Backend.DTOs;
using Hospitality.Backend.Services;
using Microsoft.AspNetCore.Mvc;
namespace Hospitality.Backend.Controllers;
[ApiController]
[Route("api")]
public class ProductsController : ControllerBase
{
private readonly IProductService _productService;
public ProductsController(IProductService productService)
{
_productService = productService;
}
[HttpGet("events/{eventId}/products")]
public async Task<ActionResult<IEnumerable<ProductResponse>>> GetProductsByEvent(Guid eventId)
{
var products = await _productService.GetProductsByEventIdAsync(eventId);
return Ok(products);
}
[HttpPost("events/{eventId}/products")]
public async Task<ActionResult<ProductResponse>> CreateProduct(Guid eventId, CreateProductRequest request)
{
var product = await _productService.CreateProductAsync(eventId, request);
return CreatedAtAction(nameof(GetProductsByEvent), new { eventId }, product);
}
[HttpPut("products/{id}")]
public async Task<ActionResult<ProductResponse>> UpdateProduct(Guid id, UpdateProductRequest request)
{
var product = await _productService.UpdateProductAsync(id, request);
if (product == null) return NotFound();
return Ok(product);
}
[HttpDelete("products/{id}")]
public async Task<IActionResult> DeleteProduct(Guid id)
{
var result = await _productService.DeleteProductAsync(id);
if (!result) return NotFound();
return NoContent();
}
}
@@ -0,0 +1,32 @@
using Hospitality.Backend.DTOs;
using Hospitality.Backend.Services;
using Microsoft.AspNetCore.Mvc;
namespace Hospitality.Backend.Controllers;
[ApiController]
[Route("api/qr")]
public class QrCodeController : ControllerBase
{
private readonly IQrCodeService _qrCodeService;
public QrCodeController(IQrCodeService qrCodeService)
{
_qrCodeService = qrCodeService;
}
[HttpGet("{qrCode}")]
public async Task<ActionResult<PersonDetailResponse>> GetPersonByQrCode(Guid qrCode)
{
var person = await _qrCodeService.GetPersonByQrCodeAsync(qrCode);
if (person == null) return NotFound();
return Ok(person);
}
[HttpGet("{qrCode}/quotas")]
public async Task<ActionResult<IEnumerable<QuotaResponse>>> GetQuotasByQrCode(Guid qrCode)
{
var quotas = await _qrCodeService.GetQuotasByQrCodeAsync(qrCode);
return Ok(quotas);
}
}
@@ -0,0 +1,46 @@
using Hospitality.Backend.DTOs;
using Hospitality.Backend.Services;
using Microsoft.AspNetCore.Mvc;
namespace Hospitality.Backend.Controllers;
[ApiController]
[Route("api/transactions")]
public class TransactionsController : ControllerBase
{
private readonly ITransactionService _transactionService;
public TransactionsController(ITransactionService transactionService)
{
_transactionService = transactionService;
}
[HttpPost]
public async Task<ActionResult<TransactionResponse>> RecordTransaction(CreateTransactionRequest request)
{
try
{
var transaction = await _transactionService.RecordTransactionAsync(request);
if (transaction == null) return NotFound("Person or product not found");
return CreatedAtAction(nameof(GetTransactionsByPerson), new { personId = transaction.PersonId }, transaction);
}
catch (InvalidOperationException ex)
{
return BadRequest(ex.Message);
}
}
[HttpGet("person/{personId}")]
public async Task<ActionResult<IEnumerable<TransactionResponse>>> GetTransactionsByPerson(Guid personId)
{
var transactions = await _transactionService.GetTransactionsByPersonIdAsync(personId);
return Ok(transactions);
}
[HttpGet("event/{eventId}")]
public async Task<ActionResult<IEnumerable<TransactionResponse>>> GetTransactionsByEvent(Guid eventId)
{
var transactions = await _transactionService.GetTransactionsByEventIdAsync(eventId);
return Ok(transactions);
}
}
+33
View File
@@ -0,0 +1,33 @@
namespace Hospitality.Backend.DTOs;
public record CreateEventRequest(
string Name,
DateTime StartDate,
DateTime EndDate,
string Location
);
public record UpdateEventRequest(
string Name,
DateTime StartDate,
DateTime EndDate,
string Location
);
public record EventResponse(
Guid Id,
string Name,
DateTime StartDate,
DateTime EndDate,
string Location,
int GroupCount,
int ProductCount
);
public record EventListResponse(
Guid Id,
string Name,
DateTime StartDate,
DateTime EndDate,
string Location
);
+31
View File
@@ -0,0 +1,31 @@
namespace Hospitality.Backend.DTOs;
public record CreateGroupRequest(
string Name,
string? ContactPersonName,
string? ContactEmail
);
public record UpdateGroupRequest(
string Name,
string? ContactPersonName,
string? ContactEmail
);
public record GroupResponse(
Guid Id,
Guid EventId,
string Name,
string? ContactPersonName,
string? ContactEmail,
int PeopleCount
);
public record GroupDetailResponse(
Guid Id,
Guid EventId,
string Name,
string? ContactPersonName,
string? ContactEmail,
List<PersonResponse> People
);
+41
View File
@@ -0,0 +1,41 @@
namespace Hospitality.Backend.DTOs;
public record CreatePersonRequest(
string Name,
string? Email,
string? PhoneNumber
);
public record UpdatePersonRequest(
string Name,
string? Email,
string? PhoneNumber
);
public record PersonResponse(
Guid Id,
Guid GroupId,
Guid QrCode,
string Name,
string? Email,
string? PhoneNumber
);
public record PersonDetailResponse(
Guid Id,
Guid GroupId,
Guid QrCode,
string Name,
string? Email,
string? PhoneNumber,
List<QuotaResponse> Quotas
);
public record QuotaResponse(
Guid ProductId,
string ProductName,
string ProductType,
int InitialAmount,
int UsedAmount,
int RemainingAmount
);
@@ -0,0 +1,20 @@
using Hospitality.Domain.Enums;
namespace Hospitality.Backend.DTOs;
public record CreateProductRequest(
string Name,
ProductType Type
);
public record UpdateProductRequest(
string Name,
ProductType Type
);
public record ProductResponse(
Guid Id,
Guid EventId,
string Name,
ProductType Type
);
@@ -0,0 +1,18 @@
namespace Hospitality.Backend.DTOs;
public record CreateTransactionRequest(
Guid QrCode,
Guid ProductId,
int Amount
);
public record TransactionResponse(
Guid Id,
Guid PersonId,
string PersonName,
Guid ProductId,
string ProductName,
int Amount,
DateTime Timestamp,
Guid? StaffId
);
+9
View File
@@ -1,3 +1,4 @@
using Hospitality.Backend.Services;
using Hospitality.Infrastructure.Data;
using Microsoft.EntityFrameworkCore;
@@ -11,6 +12,14 @@ builder.Services.AddSwaggerGen();
builder.Services.AddDbContext<HospitalityDbContext>(options =>
options.UseNpgsql(builder.Configuration.GetConnectionString("DefaultConnection")));
// Register services
builder.Services.AddScoped<IEventService, EventService>();
builder.Services.AddScoped<IGroupService, GroupService>();
builder.Services.AddScoped<IProductService, ProductService>();
builder.Services.AddScoped<IQrCodeService, QrCodeService>();
builder.Services.AddScoped<ITransactionService, TransactionService>();
var app = builder.Build();
// Configure the HTTP request pipeline.
@@ -0,0 +1,110 @@
using Hospitality.Backend.DTOs;
using Hospitality.Domain.Entities;
using Hospitality.Infrastructure.Data;
using Microsoft.EntityFrameworkCore;
namespace Hospitality.Backend.Services;
public class EventService : IEventService
{
private readonly HospitalityDbContext _context;
public EventService(HospitalityDbContext context)
{
_context = context;
}
public async Task<IEnumerable<EventListResponse>> GetAllEventsAsync()
{
return await _context.Events
.Select(e => new EventListResponse(
e.Id,
e.Name,
e.StartDate,
e.EndDate,
e.Location
))
.ToListAsync();
}
public async Task<EventResponse?> GetEventByIdAsync(Guid id)
{
var evt = await _context.Events
.Include(e => e.Groups)
.Include(e => e.Products)
.FirstOrDefaultAsync(e => e.Id == id);
if (evt == null) return null;
return new EventResponse(
evt.Id,
evt.Name,
evt.StartDate,
evt.EndDate,
evt.Location,
evt.Groups.Count,
evt.Products.Count
);
}
public async Task<EventResponse> CreateEventAsync(CreateEventRequest request)
{
var evt = new Event
{
Id = Guid.NewGuid(),
Name = request.Name,
StartDate = request.StartDate,
EndDate = request.EndDate,
Location = request.Location
};
_context.Events.Add(evt);
await _context.SaveChangesAsync();
return new EventResponse(
evt.Id,
evt.Name,
evt.StartDate,
evt.EndDate,
evt.Location,
0,
0
);
}
public async Task<EventResponse?> UpdateEventAsync(Guid id, UpdateEventRequest request)
{
var evt = await _context.Events.FindAsync(id);
if (evt == null) return null;
evt.Name = request.Name;
evt.StartDate = request.StartDate;
evt.EndDate = request.EndDate;
evt.Location = request.Location;
await _context.SaveChangesAsync();
var groupCount = await _context.Groups.CountAsync(g => g.EventId == id);
var productCount = await _context.Products.CountAsync(p => p.EventId == id);
return new EventResponse(
evt.Id,
evt.Name,
evt.StartDate,
evt.EndDate,
evt.Location,
groupCount,
productCount
);
}
public async Task<bool> DeleteEventAsync(Guid id)
{
var evt = await _context.Events.FindAsync(id);
if (evt == null) return false;
_context.Events.Remove(evt);
await _context.SaveChangesAsync();
return true;
}
}
@@ -0,0 +1,227 @@
using Hospitality.Backend.DTOs;
using Hospitality.Domain.Entities;
using Hospitality.Infrastructure.Data;
using Microsoft.EntityFrameworkCore;
namespace Hospitality.Backend.Services;
public class GroupService : IGroupService
{
private readonly HospitalityDbContext _context;
public GroupService(HospitalityDbContext context)
{
_context = context;
}
public async Task<IEnumerable<GroupResponse>> GetGroupsByEventIdAsync(Guid eventId)
{
return await _context.Groups
.Where(g => g.EventId == eventId)
.Select(g => new GroupResponse(
g.Id,
g.EventId,
g.Name,
g.ContactPersonName,
g.ContactEmail,
g.People.Count
))
.ToListAsync();
}
public async Task<GroupDetailResponse?> GetGroupByIdAsync(Guid id)
{
var group = await _context.Groups
.Include(g => g.People)
.FirstOrDefaultAsync(g => g.Id == id);
if (group == null) return null;
return new GroupDetailResponse(
group.Id,
group.EventId,
group.Name,
group.ContactPersonName,
group.ContactEmail,
group.People.Select(p => new PersonResponse(
p.Id,
p.GroupId,
p.QrCode,
p.Name,
p.Email,
p.PhoneNumber
)).ToList()
);
}
public async Task<GroupResponse> CreateGroupAsync(Guid eventId, CreateGroupRequest request)
{
var group = new Group
{
Id = Guid.NewGuid(),
EventId = eventId,
Name = request.Name,
ContactPersonName = request.ContactPersonName,
ContactEmail = request.ContactEmail
};
_context.Groups.Add(group);
await _context.SaveChangesAsync();
return new GroupResponse(
group.Id,
group.EventId,
group.Name,
group.ContactPersonName,
group.ContactEmail,
0
);
}
public async Task<GroupResponse?> UpdateGroupAsync(Guid id, UpdateGroupRequest request)
{
var group = await _context.Groups.FindAsync(id);
if (group == null) return null;
group.Name = request.Name;
group.ContactPersonName = request.ContactPersonName;
group.ContactEmail = request.ContactEmail;
await _context.SaveChangesAsync();
var peopleCount = await _context.People.CountAsync(p => p.GroupId == id);
return new GroupResponse(
group.Id,
group.EventId,
group.Name,
group.ContactPersonName,
group.ContactEmail,
peopleCount
);
}
public async Task<bool> DeleteGroupAsync(Guid id)
{
var group = await _context.Groups.FindAsync(id);
if (group == null) return false;
_context.Groups.Remove(group);
await _context.SaveChangesAsync();
return true;
}
public async Task<PersonResponse> AddPersonToGroupAsync(Guid groupId, CreatePersonRequest request)
{
var person = new Person
{
Id = Guid.NewGuid(),
GroupId = groupId,
QrCode = Guid.NewGuid(),
Name = request.Name,
Email = request.Email,
PhoneNumber = request.PhoneNumber
};
_context.People.Add(person);
await _context.SaveChangesAsync();
return new PersonResponse(
person.Id,
person.GroupId,
person.QrCode,
person.Name,
person.Email,
person.PhoneNumber
);
}
public async Task<PersonDetailResponse?> GetPersonByIdAsync(Guid id)
{
var person = await _context.People
.Include(p => p.Quotas)
.ThenInclude(q => q.Product)
.FirstOrDefaultAsync(p => p.Id == id);
if (person == null) return null;
return new PersonDetailResponse(
person.Id,
person.GroupId,
person.QrCode,
person.Name,
person.Email,
person.PhoneNumber,
person.Quotas.Select(q => new QuotaResponse(
q.ProductId,
q.Product.Name,
q.Product.Type.ToString(),
q.InitialAmount,
q.UsedAmount,
q.RemainingAmount
)).ToList()
);
}
public async Task<PersonResponse?> UpdatePersonAsync(Guid id, UpdatePersonRequest request)
{
var person = await _context.People.FindAsync(id);
if (person == null) return null;
person.Name = request.Name;
person.Email = request.Email;
person.PhoneNumber = request.PhoneNumber;
await _context.SaveChangesAsync();
return new PersonResponse(
person.Id,
person.GroupId,
person.QrCode,
person.Name,
person.Email,
person.PhoneNumber
);
}
public async Task<bool> DeletePersonAsync(Guid id)
{
var person = await _context.People.FindAsync(id);
if (person == null) return false;
_context.People.Remove(person);
await _context.SaveChangesAsync();
return true;
}
public async Task<bool> AssignQuotaAsync(Guid personId, Guid productId, int initialAmount)
{
var person = await _context.People.FindAsync(personId);
var product = await _context.Products.FindAsync(productId);
if (person == null || product == null) return false;
var existingQuota = await _context.PersonQuotas
.FirstOrDefaultAsync(q => q.PersonId == personId && q.ProductId == productId);
if (existingQuota != null)
{
existingQuota.InitialAmount = initialAmount;
}
else
{
var quota = new PersonQuota
{
Id = Guid.NewGuid(),
PersonId = personId,
ProductId = productId,
InitialAmount = initialAmount,
UsedAmount = 0
};
_context.PersonQuotas.Add(quota);
}
await _context.SaveChangesAsync();
return true;
}
}
@@ -0,0 +1,12 @@
using Hospitality.Backend.DTOs;
namespace Hospitality.Backend.Services;
public interface IEventService
{
Task<IEnumerable<EventListResponse>> GetAllEventsAsync();
Task<EventResponse?> GetEventByIdAsync(Guid id);
Task<EventResponse> CreateEventAsync(CreateEventRequest request);
Task<EventResponse?> UpdateEventAsync(Guid id, UpdateEventRequest request);
Task<bool> DeleteEventAsync(Guid id);
}
@@ -0,0 +1,18 @@
using Hospitality.Backend.DTOs;
namespace Hospitality.Backend.Services;
public interface IGroupService
{
Task<IEnumerable<GroupResponse>> GetGroupsByEventIdAsync(Guid eventId);
Task<GroupDetailResponse?> GetGroupByIdAsync(Guid id);
Task<GroupResponse> CreateGroupAsync(Guid eventId, CreateGroupRequest request);
Task<GroupResponse?> UpdateGroupAsync(Guid id, UpdateGroupRequest request);
Task<bool> DeleteGroupAsync(Guid id);
Task<PersonResponse> AddPersonToGroupAsync(Guid groupId, CreatePersonRequest request);
Task<PersonDetailResponse?> GetPersonByIdAsync(Guid id);
Task<PersonResponse?> UpdatePersonAsync(Guid id, UpdatePersonRequest request);
Task<bool> DeletePersonAsync(Guid id);
Task<bool> AssignQuotaAsync(Guid personId, Guid productId, int initialAmount);
}
@@ -0,0 +1,11 @@
using Hospitality.Backend.DTOs;
namespace Hospitality.Backend.Services;
public interface IProductService
{
Task<IEnumerable<ProductResponse>> GetProductsByEventIdAsync(Guid eventId);
Task<ProductResponse> CreateProductAsync(Guid eventId, CreateProductRequest request);
Task<ProductResponse?> UpdateProductAsync(Guid id, UpdateProductRequest request);
Task<bool> DeleteProductAsync(Guid id);
}
@@ -0,0 +1,9 @@
using Hospitality.Backend.DTOs;
namespace Hospitality.Backend.Services;
public interface IQrCodeService
{
Task<PersonDetailResponse?> GetPersonByQrCodeAsync(Guid qrCode);
Task<IEnumerable<QuotaResponse>> GetQuotasByQrCodeAsync(Guid qrCode);
}
@@ -0,0 +1,10 @@
using Hospitality.Backend.DTOs;
namespace Hospitality.Backend.Services;
public interface ITransactionService
{
Task<TransactionResponse?> RecordTransactionAsync(CreateTransactionRequest request);
Task<IEnumerable<TransactionResponse>> GetTransactionsByPersonIdAsync(Guid personId);
Task<IEnumerable<TransactionResponse>> GetTransactionsByEventIdAsync(Guid eventId);
}
@@ -0,0 +1,78 @@
using Hospitality.Backend.DTOs;
using Hospitality.Domain.Entities;
using Hospitality.Infrastructure.Data;
using Microsoft.EntityFrameworkCore;
namespace Hospitality.Backend.Services;
public class ProductService : IProductService
{
private readonly HospitalityDbContext _context;
public ProductService(HospitalityDbContext context)
{
_context = context;
}
public async Task<IEnumerable<ProductResponse>> GetProductsByEventIdAsync(Guid eventId)
{
return await _context.Products
.Where(p => p.EventId == eventId)
.Select(p => new ProductResponse(
p.Id,
p.EventId,
p.Name,
p.Type
))
.ToListAsync();
}
public async Task<ProductResponse> CreateProductAsync(Guid eventId, CreateProductRequest request)
{
var product = new Product
{
Id = Guid.NewGuid(),
EventId = eventId,
Name = request.Name,
Type = request.Type
};
_context.Products.Add(product);
await _context.SaveChangesAsync();
return new ProductResponse(
product.Id,
product.EventId,
product.Name,
product.Type
);
}
public async Task<ProductResponse?> UpdateProductAsync(Guid id, UpdateProductRequest request)
{
var product = await _context.Products.FindAsync(id);
if (product == null) return null;
product.Name = request.Name;
product.Type = request.Type;
await _context.SaveChangesAsync();
return new ProductResponse(
product.Id,
product.EventId,
product.Name,
product.Type
);
}
public async Task<bool> DeleteProductAsync(Guid id)
{
var product = await _context.Products.FindAsync(id);
if (product == null) return false;
_context.Products.Remove(product);
await _context.SaveChangesAsync();
return true;
}
}
@@ -0,0 +1,61 @@
using Hospitality.Backend.DTOs;
using Hospitality.Infrastructure.Data;
using Microsoft.EntityFrameworkCore;
namespace Hospitality.Backend.Services;
public class QrCodeService : IQrCodeService
{
private readonly HospitalityDbContext _context;
public QrCodeService(HospitalityDbContext context)
{
_context = context;
}
public async Task<PersonDetailResponse?> GetPersonByQrCodeAsync(Guid qrCode)
{
var person = await _context.People
.Include(p => p.Quotas)
.ThenInclude(q => q.Product)
.FirstOrDefaultAsync(p => p.QrCode == qrCode);
if (person == null) return null;
return new PersonDetailResponse(
person.Id,
person.GroupId,
person.QrCode,
person.Name,
person.Email,
person.PhoneNumber,
person.Quotas.Select(q => new QuotaResponse(
q.ProductId,
q.Product.Name,
q.Product.Type.ToString(),
q.InitialAmount,
q.UsedAmount,
q.RemainingAmount
)).ToList()
);
}
public async Task<IEnumerable<QuotaResponse>> GetQuotasByQrCodeAsync(Guid qrCode)
{
var quotas = await _context.PersonQuotas
.Include(q => q.Product)
.Include(q => q.Person)
.Where(q => q.Person.QrCode == qrCode)
.Select(q => new QuotaResponse(
q.ProductId,
q.Product.Name,
q.Product.Type.ToString(),
q.InitialAmount,
q.UsedAmount,
q.RemainingAmount
))
.ToListAsync();
return quotas;
}
}
@@ -0,0 +1,102 @@
using Hospitality.Backend.DTOs;
using Hospitality.Domain.Entities;
using Hospitality.Infrastructure.Data;
using Microsoft.EntityFrameworkCore;
namespace Hospitality.Backend.Services;
public class TransactionService : ITransactionService
{
private readonly HospitalityDbContext _context;
public TransactionService(HospitalityDbContext context)
{
_context = context;
}
public async Task<TransactionResponse?> RecordTransactionAsync(CreateTransactionRequest request)
{
var person = await _context.People
.FirstOrDefaultAsync(p => p.QrCode == request.QrCode);
if (person == null) return null;
var quota = await _context.PersonQuotas
.Include(q => q.Product)
.FirstOrDefaultAsync(q => q.PersonId == person.Id && q.ProductId == request.ProductId);
if (quota == null) return null;
if (quota.RemainingAmount < request.Amount)
{
throw new InvalidOperationException($"Insufficient quota. Remaining: {quota.RemainingAmount}, Requested: {request.Amount}");
}
quota.UsedAmount += request.Amount;
var transaction = new Transaction
{
Id = Guid.NewGuid(),
PersonId = person.Id,
ProductId = request.ProductId,
Amount = request.Amount,
Timestamp = DateTime.UtcNow,
StaffId = null
};
_context.Transactions.Add(transaction);
await _context.SaveChangesAsync();
return new TransactionResponse(
transaction.Id,
person.Id,
person.Name,
quota.Product.Id,
quota.Product.Name,
transaction.Amount,
transaction.Timestamp,
transaction.StaffId
);
}
public async Task<IEnumerable<TransactionResponse>> GetTransactionsByPersonIdAsync(Guid personId)
{
return await _context.Transactions
.Include(t => t.Person)
.Include(t => t.Product)
.Where(t => t.PersonId == personId)
.OrderByDescending(t => t.Timestamp)
.Select(t => new TransactionResponse(
t.Id,
t.PersonId,
t.Person.Name,
t.ProductId,
t.Product.Name,
t.Amount,
t.Timestamp,
t.StaffId
))
.ToListAsync();
}
public async Task<IEnumerable<TransactionResponse>> GetTransactionsByEventIdAsync(Guid eventId)
{
return await _context.Transactions
.Include(t => t.Person)
.ThenInclude(p => p.Group)
.Include(t => t.Product)
.Where(t => t.Product.EventId == eventId)
.OrderByDescending(t => t.Timestamp)
.Select(t => new TransactionResponse(
t.Id,
t.PersonId,
t.Person.Name,
t.ProductId,
t.Product.Name,
t.Amount,
t.Timestamp,
t.StaffId
))
.ToListAsync();
}
}