feat(admin): add analytics aggregator
This commit is contained in:
1 parent
3456b6edf4
commit
4c8d38e972
1 file changed
+89
@@ -0,0 +1,89 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Bazaar\Services;
|
||||||
|
|
||||||
|
use Bazaar\Core\Database;
|
||||||
|
|
||||||
|
final class Analytics
|
||||||
|
{
|
||||||
|
public function __construct(private readonly Database $db)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public function salesByDay(int $days = 14): array
|
||||||
|
{
|
||||||
|
$rows = $this->db->select(
|
||||||
|
"SELECT substr(COALESCE(paidAt, createdAt), 1, 10) AS day,
|
||||||
|
COUNT(*) AS orderCount,
|
||||||
|
COALESCE(SUM(totalCents), 0) AS revenueCents
|
||||||
|
FROM orders
|
||||||
|
WHERE status IN ('paid', 'fulfilled')
|
||||||
|
GROUP BY day
|
||||||
|
ORDER BY day ASC"
|
||||||
|
);
|
||||||
|
|
||||||
|
$byDay = [];
|
||||||
|
foreach ($rows as $row) {
|
||||||
|
$byDay[$row['day']] = [
|
||||||
|
'orderCount' => (int) $row['orderCount'],
|
||||||
|
'revenueCents' => (int) $row['revenueCents'],
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
$series = [];
|
||||||
|
for ($offset = $days - 1; $offset >= 0; $offset--) {
|
||||||
|
$day = gmdate('Y-m-d', strtotime("-{$offset} days"));
|
||||||
|
$series[] = [
|
||||||
|
'day' => $day,
|
||||||
|
'orderCount' => $byDay[$day]['orderCount'] ?? 0,
|
||||||
|
'revenueCents' => $byDay[$day]['revenueCents'] ?? 0,
|
||||||
|
];
|
||||||
|
}
|
||||||
|
return $series;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function topProducts(int $limit = 5): array
|
||||||
|
{
|
||||||
|
return $this->db->select(
|
||||||
|
"SELECT oi.productId,
|
||||||
|
oi.productName AS name,
|
||||||
|
SUM(oi.quantity) AS unitsSold,
|
||||||
|
SUM(oi.quantity * oi.unitPriceCents) AS revenueCents
|
||||||
|
FROM orderItems oi
|
||||||
|
JOIN orders o ON o.id = oi.orderId
|
||||||
|
WHERE o.status IN ('paid', 'fulfilled')
|
||||||
|
GROUP BY oi.productName
|
||||||
|
ORDER BY unitsSold DESC, revenueCents DESC
|
||||||
|
LIMIT ?",
|
||||||
|
[$limit]
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function averageOrderValueCents(): int
|
||||||
|
{
|
||||||
|
return (int) round((float) $this->db->scalar(
|
||||||
|
"SELECT COALESCE(AVG(totalCents), 0) FROM orders WHERE status IN ('paid', 'fulfilled')"
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function unitsSold(): int
|
||||||
|
{
|
||||||
|
return (int) $this->db->scalar(
|
||||||
|
"SELECT COALESCE(SUM(oi.quantity), 0)
|
||||||
|
FROM orderItems oi
|
||||||
|
JOIN orders o ON o.id = oi.orderId
|
||||||
|
WHERE o.status IN ('paid', 'fulfilled')"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function couponUsage(): array
|
||||||
|
{
|
||||||
|
return $this->db->select(
|
||||||
|
"SELECT couponCode, COUNT(*) AS orderCount, COALESCE(SUM(discountCents), 0) AS discountCents
|
||||||
|
FROM orders
|
||||||
|
WHERE couponCode IS NOT NULL AND couponCode != ''
|
||||||
|
GROUP BY couponCode
|
||||||
|
ORDER BY orderCount DESC"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in new issue
Block a user