Phase 2: Backend API Implementation - DTOs, Services, and Controllers
This commit is contained in:
@@ -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