12aadfd917
- Add 14 domain events for order lifecycle (OrderCreated, OrderPacked, ShipmentCreated, ParcelDelivered, etc.)
- Create SlackNotifierService with channels for orders, design, production, shipping, ops-alerts
- Create TrelloService to create cards, move cards between lists, attach files, check items
- Create CourierService to integrate with ShipLogic API for shipment creation and document retrieval
- Create PackingController to explicitly capture packing dimensions and weight
- Create ShippingController with multi-layer guards: packing validation, payment/approval verification, idempotency
- Create TrelloWebhookController to handle incoming Trello webhooks as intent signals
- Create CourierWebhookController to handle Shiplogic status updates
- Create event listeners for Slack notifications and Trello card updates
- Create EventServiceProvider to register all events and listeners
- Add database migration for packing, courier, and Trello data columns
- Create config files for slack, trello, and courier integration
- Update .env with integration secrets placeholders
- Add routes for /orders/{id}/pack, /orders/{id}/ship, /api/webhooks/trello, /api/webhooks/courier
Key architectural decisions:
- Packing is explicit ops action (not automatic from status)
- Shipment creation only after: Trello intent + packing confirmed + payment/approval rules met
- Courier API failures keep order in Ready to Ship state (safe retry)
- Trello and Slack are mirrors of backend state, not decision makers
- All side effects flow through event listeners, maintaining separation of concerns
91 lines
2.4 KiB
PHP
91 lines
2.4 KiB
PHP
<?php
|
|
|
|
namespace App\Services;
|
|
|
|
use Illuminate\Support\Facades\Http;
|
|
use Illuminate\Support\Facades\Log;
|
|
|
|
class SlackNotifierService
|
|
{
|
|
protected array $webhooks = [];
|
|
|
|
public function __construct()
|
|
{
|
|
$this->webhooks = [
|
|
'orders' => config('slack.webhooks.orders'),
|
|
'design' => config('slack.webhooks.design'),
|
|
'production' => config('slack.webhooks.production'),
|
|
'shipping' => config('slack.webhooks.shipping'),
|
|
'ops_alerts' => config('slack.webhooks.ops_alerts'),
|
|
];
|
|
}
|
|
|
|
/**
|
|
* Send notification to #orders channel
|
|
*/
|
|
public function orders(string $message, array $blocks = []): void
|
|
{
|
|
$this->send($this->webhooks['orders'], $message, $blocks);
|
|
}
|
|
|
|
/**
|
|
* Send notification to #design channel
|
|
*/
|
|
public function design(string $message, array $blocks = []): void
|
|
{
|
|
$this->send($this->webhooks['design'], $message, $blocks);
|
|
}
|
|
|
|
/**
|
|
* Send notification to #production channel
|
|
*/
|
|
public function production(string $message, array $blocks = []): void
|
|
{
|
|
$this->send($this->webhooks['production'], $message, $blocks);
|
|
}
|
|
|
|
/**
|
|
* Send notification to #shipping channel
|
|
*/
|
|
public function shipping(string $message, array $blocks = []): void
|
|
{
|
|
$this->send($this->webhooks['shipping'], $message, $blocks);
|
|
}
|
|
|
|
/**
|
|
* Send notification to #ops-alerts channel
|
|
*/
|
|
public function opsAlerts(string $message, array $blocks = []): void
|
|
{
|
|
$this->send($this->webhooks['ops_alerts'], $message, $blocks);
|
|
}
|
|
|
|
/**
|
|
* Send payload to Slack webhook
|
|
*/
|
|
protected function send(string $webhook, string $message, array $blocks = []): void
|
|
{
|
|
if (! $webhook) {
|
|
Log::warning('Slack webhook not configured for channel', ['message' => $message]);
|
|
|
|
return;
|
|
}
|
|
|
|
try {
|
|
$payload = ['text' => $message];
|
|
|
|
if (! empty($blocks)) {
|
|
$payload['blocks'] = $blocks;
|
|
}
|
|
|
|
Http::post($webhook, $payload);
|
|
} catch (\Exception $e) {
|
|
Log::error('Failed to send Slack notification', [
|
|
'webhook' => substr($webhook, 0, 20).'...',
|
|
'message' => $message,
|
|
'error' => $e->getMessage(),
|
|
]);
|
|
}
|
|
}
|
|
}
|