BR 2.5 — Order Service
Источники
Задачи
Миграция БД
-
Liquibase changeset
XXX-br-2-5-status-model:- Расширить CHECK-constraint (миграция 005 уже включает
accepted/handed_over, но нужно добавитьin_delivery,delivered):ALTER TABLE orders DROP CONSTRAINT IF EXISTS chk_status; ALTER TABLE orders ADD CONSTRAINT chk_status CHECK (status IN ('new', 'accepted', 'ready', 'handed_over', 'in_delivery', 'delivered', 'closed', 'cancelled')); ALTER TABLE orders ADD COLUMN requires_kitchen BOOLEAN NOT NULL DEFAULT falseALTER TABLE orders ADD COLUMN accepted_by UUID NULLALTER TABLE orders ADD COLUMN courier_id UUID NULLALTER TABLE orders ADD COLUMN accepted_at TIMESTAMP NULLALTER TABLE orders ADD COLUMN ready_at TIMESTAMP NULLALTER TABLE orders ADD COLUMN handed_over_at TIMESTAMP NULLALTER TABLE orders ADD COLUMN in_delivery_at TIMESTAMP NULLALTER TABLE orders ADD COLUMN delivered_at TIMESTAMP NULL
- Расширить CHECK-constraint (миграция 005 уже включает
-
Data-migration для legacy:
UPDATE orders SET paid_at = completed_at WHERE status = 'closed' AND paid_at IS NULL(опционально — обсудить)
Entity
-
Order.java— добавить поляrequiresKitchen,acceptedBy,courierId,acceptedAt,readyAt,handedOverAt,inDeliveryAt,deliveredAt
State Machine
-
OrderStateMachine— явная Map allowed transitions:Map<String, Set<String>> = Map.of( "new", Set.of("accepted", "cancelled", "closed"), "accepted", Set.of("ready", "cancelled"), "ready", Set.of("handed_over", "cancelled", "closed"), "handed_over", Set.of("in_delivery"), "in_delivery", Set.of("delivered"), "delivered", Set.of("closed"), "closed", Set.of(), "cancelled", Set.of() );
Service
-
OrderService.startCooking(id, user)— new → accepted, проставить accepted_at/by, emitorder.cooking_started -
OrderService.markReady(id, user)— accepted → ready, emitorder.ready -
OrderService.pay(id, req, user)— не меняет статус, толькоpaid_at, emitorder.paid -
OrderService.close(id, user)— → closed, инвариантpaid_at IS NOT NULL(для новых переходов), emitorder.closed+order.completed(alias) -
OrderService.cancel(id, reason, user)— только еслиpaid_at IS NULL, иначеORDER_ALREADY_PAID -
OrderService.checkout(id, req, user)— shortcut pay+close дляtakeawayбезrequires_kitchen. Иначе 409ORDER_FLOW_MISMATCH. Для обратной совместимости: если зовут наdine_in/delivery— внутренне делать pay+close и логировать deprecation warning (вместо break). -
OrderService.handOverToCourier(id, courierId, user)— ready → handed_over, проверить чтоcourier_idимеет permissionorders.delivery -
OrderService.startDelivery(id, user)— handed_over → in_delivery, проверить permissionorders.delivery+ совпадение courier_id с user -
OrderService.confirmDelivery(id, user)— in_delivery → delivered, те же permission-проверки -
OrderService.createOrder(...)— при сохранении рассчитатьrequires_kitchenпо составу cart (запрос в Catalog Service или join черезproductsтаблицу Catalog — API call)
Controllers
-
OrderController— добавить endpoints:POST /orders/{id}/start-cookingPOST /orders/{id}/mark-readyPOST /orders/{id}/hand-over-to-courier(body:{ courier_id })POST /orders/{id}/start-deliveryPOST /orders/{id}/confirm-delivery
- Обновить
POST /orders/{id}/pay— убратьorder.status = ready - Обновить
POST /orders/{id}/complete— добавить инвариантpaid_at IS NOT NULL - Обновить
POST /orders/{id}/checkout(если был) — добавить проверку типа
Events
-
OrderEventPublisher— новые методы:publishCookingStarted(Order)publishReady(Order)publishClosed(Order)(+ продолжать publishCompleted как alias)publishHandedOver(Order)publishInDelivery(Order)publishDelivered(Order)
-
OrderEventPayloads— новые DTO-классыCookingStarted,Ready,Closed,HandedOver,InDelivery,Delivered
Permissions
-
orders.delivery— новая permission (добавить вPermissionCatalogAuth Service — отдельная sub-миграция в Auth Service) - Endpoints
start-delivery/confirm-deliveryтребуютorders.delivery(илиorders.editкак надпрокси)
Calculation of orders.requires_kitchen
-
CatalogServiceClient.areAnyItemsRequireKitchen(productIds)— HTTP-запрос в Catalog ServiceGET /internal/products/require-kitchen?ids=...(новый internal endpoint в Catalog Service — выделить подзадачу там) - Кэшировать результат на checkout’е в
orders.requires_kitchen
Verification
mvn compileзелёный- Миграции применяются без потери данных
- Smoke-сценарии:
- Takeaway без кухни:
POST /orders+POST /orders/{id}/checkout→ закрывается одним вызовом, status=closed - Dine-in с кухней: full flow через start-cooking → mark-ready → pay → close
- Delivery: full flow через hand-over-to-courier → start-delivery → confirm-delivery → close
POST /orders/{id}/completeбезpaid_at→ 409ORDER_NOT_PAIDPOST /orders/{id}/cancelна оплаченном заказе → 422ORDER_ALREADY_PAID- Все новые события приходят в Kafka с правильным payload
- Takeaway без кухни: