feature: Warning Alert Consumer, Orders API & Input Validation
This commit is contained in:
@@ -0,0 +1,114 @@
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
|
||||
public class OrderService : IOrderService
|
||||
{
|
||||
private static readonly Dictionary<OrderStatus, HashSet<OrderStatus>> _allowedTransitions = new()
|
||||
{
|
||||
[OrderStatus.Pending] = new() { OrderStatus.InProgress, OrderStatus.Cancelled },
|
||||
[OrderStatus.InProgress] = new() { OrderStatus.Resulted, OrderStatus.Cancelled },
|
||||
[OrderStatus.Resulted] = new(),
|
||||
[OrderStatus.Cancelled] = new(),
|
||||
};
|
||||
|
||||
private readonly AppDbContext _db;
|
||||
|
||||
public OrderService(AppDbContext db) => _db = db;
|
||||
|
||||
public async Task<Order> CreateAsync(Guid encounterId, CreateOrderRequest req)
|
||||
{
|
||||
var encounter = await _db.Encounters.FindAsync(encounterId);
|
||||
if (encounter is null)
|
||||
throw new NotFoundException("Encounter not found.", "ENCOUNTER_NOT_FOUND");
|
||||
|
||||
if (encounter.Status != EncounterStatus.Active)
|
||||
throw new ConflictException(
|
||||
"Cannot create orders for a non-active encounter.",
|
||||
"ENCOUNTER_NOT_ACTIVE");
|
||||
|
||||
var order = new Order
|
||||
{
|
||||
Id = Guid.NewGuid(),
|
||||
EncounterId = encounterId,
|
||||
OrderType = req.OrderType,
|
||||
Description = req.Description,
|
||||
OrderedBy = req.OrderedBy,
|
||||
Status = OrderStatus.Pending,
|
||||
OrderedAt = DateTimeOffset.UtcNow
|
||||
};
|
||||
_db.Orders.Add(order);
|
||||
await _db.SaveChangesAsync();
|
||||
return order;
|
||||
}
|
||||
|
||||
public async Task<PagedResult<Order>> ListByEncounterAsync(
|
||||
Guid encounterId, OrderStatus? status, int page, int pageSize)
|
||||
{
|
||||
var query = _db.Orders
|
||||
.AsNoTracking()
|
||||
.Where(o => o.EncounterId == encounterId);
|
||||
|
||||
if (status.HasValue)
|
||||
query = query.Where(o => o.Status == status.Value);
|
||||
|
||||
var total = await query.CountAsync();
|
||||
var orders = await query
|
||||
.OrderByDescending(o => o.OrderedAt)
|
||||
.Skip((page - 1) * pageSize)
|
||||
.Take(pageSize)
|
||||
.ToListAsync();
|
||||
|
||||
return new PagedResult<Order>(orders, page, pageSize, total);
|
||||
}
|
||||
|
||||
public async Task<Order> GetByIdAsync(Guid id)
|
||||
{
|
||||
var order = await _db.Orders
|
||||
.AsNoTracking()
|
||||
.Include(o => o.Encounter)
|
||||
.FirstOrDefaultAsync(o => o.Id == id);
|
||||
|
||||
if (order is null)
|
||||
throw new NotFoundException("Order not found.", "ORDER_NOT_FOUND");
|
||||
|
||||
return order;
|
||||
}
|
||||
|
||||
public async Task<Order> TransitionStatusAsync(Guid id, OrderStatus targetStatus)
|
||||
{
|
||||
var order = await _db.Orders.FindAsync(id);
|
||||
if (order is null)
|
||||
throw new NotFoundException("Order not found.", "ORDER_NOT_FOUND");
|
||||
|
||||
if (!_allowedTransitions[order.Status].Contains(targetStatus))
|
||||
throw new ConflictException(
|
||||
$"Transition to '{targetStatus}' is not permitted from status '{order.Status}'.",
|
||||
"ILLEGAL_ORDER_STATUS_TRANSITION");
|
||||
|
||||
order.Status = targetStatus;
|
||||
if (targetStatus == OrderStatus.Resulted)
|
||||
order.ResultedAt = DateTimeOffset.UtcNow;
|
||||
|
||||
await _db.SaveChangesAsync();
|
||||
return order;
|
||||
}
|
||||
|
||||
public async Task<Order> RecordResultAsync(Guid id, RecordOrderResultRequest req)
|
||||
{
|
||||
var order = await _db.Orders.FindAsync(id);
|
||||
if (order is null)
|
||||
throw new NotFoundException("Order not found.", "ORDER_NOT_FOUND");
|
||||
|
||||
if (order.Status == OrderStatus.Resulted)
|
||||
throw new ConflictException("Order already resulted.", "ORDER_ALREADY_RESULTED");
|
||||
|
||||
if (order.Status == OrderStatus.Cancelled)
|
||||
throw new ConflictException("Cannot result a cancelled order.", "ORDER_CANCELLED");
|
||||
|
||||
order.Status = OrderStatus.Resulted;
|
||||
order.ResultedAt = DateTimeOffset.UtcNow;
|
||||
order.ResultSummary = req.ResultSummary;
|
||||
|
||||
await _db.SaveChangesAsync();
|
||||
return order;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user