feat(Core): add optimize and complete code
This commit is contained in:
@@ -44,13 +44,19 @@ class AnalyticsService {
|
||||
$summary = $this->getSummary($filters);
|
||||
$salesChart = $this->getSalesChart($filters);
|
||||
$rulePerformance = $this->getRulePerformance($filters);
|
||||
$roiReport = $this->getRoiReport($filters);
|
||||
$upsellPerformance = $this->getUpsellPerformance($filters);
|
||||
$bannerPerformance = $this->getBannerPerformance($filters, $summary);
|
||||
$userBehavior = $this->getUserBehavior($filters);
|
||||
$insights = $this->getInsights($summary, $filters);
|
||||
$insights = $this->getInsights($summary, $filters, $roiReport);
|
||||
|
||||
$result = [
|
||||
'summary' => $summary,
|
||||
'sales_chart' => $salesChart,
|
||||
'rule_performance' => $rulePerformance,
|
||||
'roi_report' => $roiReport,
|
||||
'upsell_performance' => $upsellPerformance,
|
||||
'banner_performance' => $bannerPerformance,
|
||||
'user_behavior' => $userBehavior,
|
||||
'insights' => $insights,
|
||||
];
|
||||
@@ -152,6 +158,160 @@ class AnalyticsService {
|
||||
return $result;
|
||||
}
|
||||
|
||||
public function getRoiReport(array $filters = []) {
|
||||
$orders = $this->getOrdersInRange($filters);
|
||||
$attributedRevenue = 0;
|
||||
$attributedDiscount = 0;
|
||||
$attributedOrders = [];
|
||||
$ruleRows = [];
|
||||
$upsellRows = [];
|
||||
|
||||
foreach ($orders as $order) {
|
||||
$orderId = $order->get_id();
|
||||
foreach ($order->get_items() as $item) {
|
||||
$lineRevenue = (float) $item->get_total();
|
||||
$ruleIds = $this->parseIdList($item->get_meta('_sodino_rule_ids', true));
|
||||
$ruleDiscount = (float) $item->get_meta('_sodino_rule_discount', true);
|
||||
$upsellId = (int) $item->get_meta('_sodino_upsell_id', true);
|
||||
|
||||
if (!empty($ruleIds) || $upsellId > 0) {
|
||||
$attributedRevenue += $lineRevenue;
|
||||
$attributedDiscount += $ruleDiscount;
|
||||
$attributedOrders[$orderId] = true;
|
||||
}
|
||||
|
||||
foreach ($ruleIds as $ruleId) {
|
||||
if (!isset($ruleRows[$ruleId])) {
|
||||
$ruleRows[$ruleId] = [
|
||||
'rule_id' => $ruleId,
|
||||
'name' => $this->getRuleName($ruleId),
|
||||
'orders' => [],
|
||||
'revenue' => 0,
|
||||
'discount' => 0,
|
||||
];
|
||||
}
|
||||
|
||||
$allocation = count($ruleIds) > 0 ? $lineRevenue / count($ruleIds) : $lineRevenue;
|
||||
$discountAllocation = count($ruleIds) > 0 ? $ruleDiscount / count($ruleIds) : $ruleDiscount;
|
||||
$ruleRows[$ruleId]['orders'][$orderId] = true;
|
||||
$ruleRows[$ruleId]['revenue'] += $allocation;
|
||||
$ruleRows[$ruleId]['discount'] += $discountAllocation;
|
||||
}
|
||||
|
||||
if ($upsellId > 0) {
|
||||
if (!isset($upsellRows[$upsellId])) {
|
||||
$upsellRows[$upsellId] = [
|
||||
'upsell_id' => $upsellId,
|
||||
'title' => $this->getUpsellTitle($upsellId),
|
||||
'orders' => [],
|
||||
'revenue' => 0,
|
||||
];
|
||||
}
|
||||
|
||||
$upsellRows[$upsellId]['orders'][$orderId] = true;
|
||||
$upsellRows[$upsellId]['revenue'] += $lineRevenue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$ruleRows = array_map(function ($row) {
|
||||
$row['order_count'] = count($row['orders']);
|
||||
unset($row['orders']);
|
||||
$row['revenue'] = round($row['revenue'], 2);
|
||||
$row['discount'] = round($row['discount'], 2);
|
||||
return $row;
|
||||
}, array_values($ruleRows));
|
||||
|
||||
usort($ruleRows, function ($a, $b) {
|
||||
return $b['revenue'] <=> $a['revenue'];
|
||||
});
|
||||
|
||||
$upsellRows = array_map(function ($row) {
|
||||
$row['order_count'] = count($row['orders']);
|
||||
unset($row['orders']);
|
||||
$row['revenue'] = round($row['revenue'], 2);
|
||||
return $row;
|
||||
}, array_values($upsellRows));
|
||||
|
||||
usort($upsellRows, function ($a, $b) {
|
||||
return $b['revenue'] <=> $a['revenue'];
|
||||
});
|
||||
|
||||
return [
|
||||
'attributed_revenue' => round($attributedRevenue, 2),
|
||||
'attributed_discount' => round($attributedDiscount, 2),
|
||||
'attributed_order_count' => count($attributedOrders),
|
||||
'rule_rows' => $ruleRows,
|
||||
'upsell_rows' => $upsellRows,
|
||||
];
|
||||
}
|
||||
|
||||
public function getUpsellPerformance(array $filters = []) {
|
||||
$roi = $this->getRoiReport($filters);
|
||||
$orderRows = [];
|
||||
foreach ($roi['upsell_rows'] as $row) {
|
||||
$orderRows[(int) $row['upsell_id']] = $row;
|
||||
}
|
||||
|
||||
$repository = new \Sodino\Repositories\UpsellRepository();
|
||||
$rows = [];
|
||||
foreach ($repository->getAll() as $upsell) {
|
||||
$orderRow = $orderRows[(int) $upsell->id] ?? null;
|
||||
$impressions = max(0, (int) $upsell->impressions);
|
||||
$addToCartConversions = max(0, (int) $upsell->conversions);
|
||||
$orderCount = $orderRow ? (int) $orderRow['order_count'] : 0;
|
||||
$revenue = $orderRow ? (float) $orderRow['revenue'] : 0;
|
||||
|
||||
$rows[] = [
|
||||
'id' => (int) $upsell->id,
|
||||
'title' => $upsell->title,
|
||||
'impressions' => $impressions,
|
||||
'add_to_cart' => $addToCartConversions,
|
||||
'orders' => $orderCount,
|
||||
'revenue' => round($revenue, 2),
|
||||
'cart_conversion_rate' => $impressions > 0 ? round(($addToCartConversions / $impressions) * 100, 2) : 0,
|
||||
'order_conversion_rate' => $impressions > 0 ? round(($orderCount / $impressions) * 100, 2) : 0,
|
||||
];
|
||||
}
|
||||
|
||||
usort($rows, function ($a, $b) {
|
||||
return $b['revenue'] <=> $a['revenue'];
|
||||
});
|
||||
|
||||
return $rows;
|
||||
}
|
||||
|
||||
public function getBannerPerformance(array $filters = [], array $summary = []) {
|
||||
$repository = new \Sodino\Repositories\BannerRepository();
|
||||
$purchaseCount = (int) ($summary['purchase_count'] ?? $this->eventRepository->getCount(array_merge($filters, ['event_type' => 'purchase'])));
|
||||
$clickTotal = 0;
|
||||
foreach ($repository->getAll() as $banner) {
|
||||
$clickTotal += max(0, (int) $banner->clicks);
|
||||
}
|
||||
|
||||
$rows = [];
|
||||
foreach ($repository->getAll() as $banner) {
|
||||
$impressions = max(0, (int) $banner->impressions);
|
||||
$clicks = max(0, (int) $banner->clicks);
|
||||
$estimatedOrders = $clickTotal > 0 ? round(($clicks / $clickTotal) * $purchaseCount, 2) : 0;
|
||||
|
||||
$rows[] = [
|
||||
'id' => (int) $banner->id,
|
||||
'title' => $banner->title,
|
||||
'impressions' => $impressions,
|
||||
'clicks' => $clicks,
|
||||
'ctr' => $impressions > 0 ? round(($clicks / $impressions) * 100, 2) : 0,
|
||||
'estimated_orders' => $estimatedOrders,
|
||||
];
|
||||
}
|
||||
|
||||
usort($rows, function ($a, $b) {
|
||||
return $b['clicks'] <=> $a['clicks'];
|
||||
});
|
||||
|
||||
return $rows;
|
||||
}
|
||||
|
||||
public function getUserBehavior(array $filters = []) {
|
||||
$productViewCount = $this->eventRepository->getCount(array_merge($filters, ['event_type' => 'product_view']));
|
||||
$addToCartCount = $this->eventRepository->getCount(array_merge($filters, ['event_type' => 'add_to_cart']));
|
||||
@@ -179,9 +339,16 @@ class AnalyticsService {
|
||||
return $performance[0]['name'] ?? null;
|
||||
}
|
||||
|
||||
private function getInsights(array $summary, array $filters = []) {
|
||||
private function getInsights(array $summary, array $filters = [], array $roiReport = []) {
|
||||
$insights = [];
|
||||
|
||||
if (!empty($roiReport['attributed_revenue'])) {
|
||||
$insights[] = sprintf(
|
||||
__('سودینو %s فروش اثرگرفته ثبت کرده است.', 'sodino'),
|
||||
wp_strip_all_tags(wc_price((float) $roiReport['attributed_revenue']))
|
||||
);
|
||||
}
|
||||
|
||||
if (!empty($summary['best_rule'])) {
|
||||
$insights[] = sprintf('%s %s', __('قانون برتر:', 'sodino'), esc_html($summary['best_rule']));
|
||||
}
|
||||
@@ -208,24 +375,74 @@ class AnalyticsService {
|
||||
}
|
||||
|
||||
private function getDateRange($range, $start, $end) {
|
||||
$today = current_time('Y-m-d');
|
||||
$result = [
|
||||
'start' => date('Y-m-d', strtotime('-6 days')),
|
||||
'end' => date('Y-m-d'),
|
||||
'start' => date('Y-m-d', strtotime($today . ' -6 days')),
|
||||
'end' => $today,
|
||||
];
|
||||
|
||||
if ($range === '30d') {
|
||||
$result['start'] = date('Y-m-d', strtotime('-29 days'));
|
||||
$result['end'] = date('Y-m-d');
|
||||
$result['start'] = date('Y-m-d', strtotime($today . ' -29 days'));
|
||||
$result['end'] = $today;
|
||||
}
|
||||
|
||||
if ($range === 'custom' && !empty($start) && !empty($end)) {
|
||||
$result['start'] = date('Y-m-d', strtotime($start));
|
||||
$result['end'] = date('Y-m-d', strtotime($end));
|
||||
$startTimestamp = strtotime($start);
|
||||
$endTimestamp = strtotime($end);
|
||||
|
||||
if ($startTimestamp && $endTimestamp) {
|
||||
if ($endTimestamp < $startTimestamp) {
|
||||
$endTimestamp = $startTimestamp;
|
||||
}
|
||||
|
||||
$result['start'] = date('Y-m-d', $startTimestamp);
|
||||
$result['end'] = date('Y-m-d', $endTimestamp);
|
||||
}
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
private function getOrdersInRange(array $filters = []) {
|
||||
if (!function_exists('wc_get_orders')) {
|
||||
return [];
|
||||
}
|
||||
|
||||
$range = $this->getDateRange($filters['range'] ?? '7d', $filters['start_date'] ?? '', $filters['end_date'] ?? '');
|
||||
$start = $filters['from'] ?? $range['start'];
|
||||
$end = $filters['to'] ?? $range['end'];
|
||||
|
||||
return wc_get_orders([
|
||||
'limit' => -1,
|
||||
'status' => ['completed', 'processing'],
|
||||
'date_created' => $start . '...' . $end,
|
||||
'return' => 'objects',
|
||||
]);
|
||||
}
|
||||
|
||||
private function parseIdList($value) {
|
||||
$ids = [];
|
||||
foreach (explode(',', (string) $value) as $id) {
|
||||
$id = absint(trim($id));
|
||||
if ($id > 0) {
|
||||
$ids[] = $id;
|
||||
}
|
||||
}
|
||||
|
||||
return array_values(array_unique($ids));
|
||||
}
|
||||
|
||||
private function getRuleName($ruleId) {
|
||||
$rule = $this->ruleRepository->getById((int) $ruleId);
|
||||
return $rule ? $rule->name : sprintf(__('قانون #%d', 'sodino'), (int) $ruleId);
|
||||
}
|
||||
|
||||
private function getUpsellTitle($upsellId) {
|
||||
$repository = new \Sodino\Repositories\UpsellRepository();
|
||||
$upsell = $repository->getById((int) $upsellId);
|
||||
return $upsell ? $upsell->title : sprintf(__('آپسل #%d', 'sodino'), (int) $upsellId);
|
||||
}
|
||||
|
||||
public function getProductIdsByCategory($category_id) {
|
||||
$products = get_posts([
|
||||
'post_type' => 'product',
|
||||
|
||||
Reference in New Issue
Block a user