feat(ADM-018): completed feature

This commit is contained in:
chattie
2026-08-17 22:23:10 +02:00
parent cf1c69fc8b
commit d595b4871f
871 changed files with 47411 additions and 281 deletions

View File

@@ -0,0 +1,62 @@
import { OrderNotFoundError, OrderStateTransitionError } from '../domain/errors.js';
import type {
OrderEventPublisher,
OrderRepository,
OrderServicePort,
CreateOrderCommand,
} from '../domain/ports.js';
import { isTransitionAllowed, type OrderState, type OrderView } from '../domain/order.js';
export class OrderService implements OrderServicePort {
constructor(
private readonly repo: OrderRepository,
private readonly events: OrderEventPublisher,
) {}
async create(input: CreateOrderCommand): Promise<OrderView> {
const order = await this.repo.create({
...input,
idempotencyKey: input.idempotencyKey ?? null,
});
await this.events.emit({ type: 'OrderCreated', orderId: order.id, userId: order.userId });
return order;
}
async listOrders(): Promise<OrderView[]> {
return this.repo.findAll();
}
async transition(id: string, next: OrderState, userId: string): Promise<OrderView> {
const existing = await this.repo.findByIdAndUserId(id, userId);
if (!existing) throw new OrderNotFoundError();
if (!isTransitionAllowed(existing.state, next))
throw new OrderStateTransitionError(existing.state, next);
const updated = await this.repo.updateState(id, next);
if (!updated) throw new OrderNotFoundError();
if (next === 'PAID') await this.events.emit({ type: 'OrderPaid', orderId: id, userId });
if (next === 'CANCELLED')
await this.events.emit({ type: 'OrderCancelled', orderId: id, userId });
return updated;
}
async getOrder(id: string, userId: string): Promise<OrderView | undefined> {
return this.repo.findByIdAndUserId(id, userId);
}
async getOrderAdmin(id: string): Promise<OrderView | undefined> {
return this.repo.findById(id);
}
async transitionAdmin(id: string, next: OrderState): Promise<OrderView> {
const existing = await this.repo.findById(id);
if (!existing) throw new OrderNotFoundError();
if (!isTransitionAllowed(existing.state, next))
throw new OrderStateTransitionError(existing.state, next);
const updated = await this.repo.updateState(id, next);
if (!updated) throw new OrderNotFoundError();
if (next === 'PAID') await this.events.emit({ type: 'OrderPaid', orderId: id, userId: existing.userId });
if (next === 'CANCELLED')
await this.events.emit({ type: 'OrderCancelled', orderId: id, userId: existing.userId });
return updated;
}
}