114 lines
3.9 KiB
C#
114 lines
3.9 KiB
C#
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;
|
|
}
|
|
} |