Phase 2: Backend API Implementation - DTOs, Services, and Controllers
This commit is contained in:
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
);
|
||||
@@ -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
|
||||
);
|
||||
@@ -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
|
||||
);
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user