Files
wordpress-theme-aksrt/inc/helpers/user-center-renderers/profile-revenue.php
T
kate522andClaude Fable 5 ea16f9c2fe perf(revenue): server-render the promotion link and stop settling on reads
Opening the 推广收益 tab was slow because every read path ran settlement
and two COUNT aggregates before the browser could show the link.

- profile-revenue.php: the link input now carries a server-rendered value from
  ReferralAttribution::create_link(), so 复制链接 works on first paint instead
  of after a REST round trip
- CommissionLedger::overview() no longer calls lazy_settle_due(); the
  lazy-settlement hook and its static flag are gone. Settlement runs only on
  the existing hourly aksrt_affiliate_settle_due cron, which uses a larger
  batch (100 vs 20) and would otherwise re-run up to 20 locked
  SELECT..FOR UPDATE + write cycles inside a GET request
- ReferralAttribution::rest_me() drops the lazy_settle_due() call and the
  stats() result; stats() itself is deleted — its two COUNTs (one of them
  COUNT(DISTINCT source_user_id)) had no consumer left once the 推广人数 row
  was removed
- credits.js: reads the link from the already-rendered input and only fetches
  /revenue/me for poster config and commission ratio. A failed fetch no longer
  blocks copying — it silently skips the poster enhancement
- tests: the lazy-settlement contract is inverted to assert the read path stays
  read-only and cron stays wired; new assertions pin the server-rendered value
  and the no-COUNT REST payload

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-09-27 23:17:32 +08:00

203 lines
14 KiB
PHP

<?php
/**
* User center commission and referral revenue panel.
*
* @package AKSRT
*/
defined('ABSPATH') || exit;
$context = is_array($args['context'] ?? null) ? $args['context'] : [];
$user_id = (int) ($context['user_id'] ?? get_current_user_id());
if ($user_id <= 0) {
echo '<div class="uc-panel-empty muted-2-color">' . esc_html__('请先登录', 'aksrt') . '</div>';
return;
}
$affiliate = class_exists('\AKSRT\Settings\Settings')
? \AKSRT\Settings\Settings::get_payment_affiliate_settings()
: [];
$affiliate_on = !empty($affiliate['enabled']);
$withdrawal = class_exists('\AKSRT\Settings\Settings')
? \AKSRT\Settings\Settings::get_withdrawal_settings()
: [];
$recharge = class_exists('\AKSRT\Settings\Settings') ? \AKSRT\Settings\Settings::get_payment_recharge_settings() : [];
$withdraw_on = $affiliate_on && !empty($recharge['enabled']);
if ($withdraw_on && class_exists('\AKSRT\Settings\Capabilities') && !\AKSRT\Settings\Capabilities::user_can('balance_withdraw', $user_id)) {
$withdraw_on = false;
}
$normalize_balance = static function ($value): \AKSRT\Ledger\AssetAmount {
try {
return \AKSRT\Ledger\AssetAmount::balance(is_string($value) ? $value : '');
} catch (\InvalidArgumentException $e) {
return \AKSRT\Ledger\AssetAmount::balance('0');
}
};
$signed_balance_minor = static function ($value) use ($normalize_balance): int {
$value = is_string($value) ? trim($value) : '';
$negative = str_starts_with($value, '-');
$amount = $normalize_balance($negative ? substr($value, 1) : $value)->minor();
return $negative ? -$amount : $amount;
};
$withdraw_min = $normalize_balance($withdrawal['min_amount'] ?? '50')->decimal();
$commission_stats = class_exists('\AKSRT\Payment\CommissionLedger')
? \AKSRT\Payment\CommissionLedger::get_instance()->overview($user_id)
: ['total' => 0, 'pending' => 0, 'settled' => 0, 'wallet' => 0];
$pending_commission = $normalize_balance($commission_stats['pending'] ?? '0')->decimal();
$commission_ratio = class_exists('\AKSRT\Settings\Settings')
? (int) \AKSRT\Settings\Settings::affiliate_ratio_for_user($user_id)
: 0;
$details_text = trim((string) ($affiliate['promotion_text'] ?? ''));
if ($details_text === '') {
$details_text = __('好友通过推广链接完成符合条件的订单后,系统会按当前佣金比例记录返佣,结算后进入可提现余额。', 'aksrt');
}
$details_title = trim((string) ($affiliate['details_title'] ?? ''));
if ($details_title === '') {
$details_title = __('返佣详解', 'aksrt');
}
/* 推广链接服务端直出:避免为了一个本地拼接的 URL 多绕一次 REST 往返。 */
$affiliate_link_url = '';
if (class_exists('\AKSRT\Payment\ReferralAttribution')) {
$affiliate_link_url = (string) (\AKSRT\Payment\ReferralAttribution::get_instance()->create_link($user_id)['url'] ?? '');
}
$revenue_request = is_array($context['revenue_request'] ?? null) ? $context['revenue_request'] : [];
$active_reward_tab = sanitize_key((string) ($revenue_request['reward_tab'] ?? ($_GET['reward_tab'] ?? 'detail')));
if (!in_array($active_reward_tab, ['detail', 'orders', 'users'], true)) {
$active_reward_tab = 'detail';
}
if (!$affiliate_on && $active_reward_tab === 'orders') {
$active_reward_tab = 'detail';
}
$base_url = function_exists('aksrt_get_user_center_tab_url')
? aksrt_get_user_center_tab_url('revenue')
: home_url('/user/revenue/');
$reward_url = static function (string $tab, array $extra = []) use ($base_url): string {
return add_query_arg(array_merge(['reward_tab' => $tab], $extra), $base_url);
};
$format_amount = static function ($amount) use ($normalize_balance): string {
return $normalize_balance($amount)->formatted();
};
$commission_page = max(1, (int) ($revenue_request['commission_page'] ?? ($_GET['commission_page'] ?? 1)));
$commission_view = class_exists('\AKSRT\Payment\CommissionLedger')
? \AKSRT\Payment\CommissionLedger::get_instance()->list_entries($user_id, [
'per_page' => 10,
'page' => $commission_page,
])
: ['items' => [], 'total' => 0, 'max_pages' => 1, 'page' => 1];
$commission_items = (array) ($commission_view['items'] ?? []);
$user_page = max(1, (int) ($_GET['user_page'] ?? 1));
$user_view = class_exists('\AKSRT\Payment\ReferralAttribution')
? \AKSRT\Payment\ReferralAttribution::get_instance()->list_referred_users($user_id, $user_page, 10)
: ['items' => [], 'total' => 0, 'max_pages' => 1, 'page' => 1];
$user_total_pages = max(1, (int) ($user_view['max_pages'] ?? 1));
if ($user_page > $user_total_pages && class_exists('\AKSRT\Payment\ReferralAttribution')) {
$user_page = $user_total_pages;
$user_view = \AKSRT\Payment\ReferralAttribution::get_instance()->list_referred_users($user_id, $user_page, 10);
}
$commission_total_pages = max(1, (int) ($commission_view['max_pages'] ?? ceil(max(1, (int) ($commission_view['total'] ?? 0)) / 10)));
if ($commission_page > $commission_total_pages) {
$commission_page = $commission_total_pages;
if (class_exists('\AKSRT\Payment\CommissionLedger')) {
$commission_view = \AKSRT\Payment\CommissionLedger::get_instance()->list_entries($user_id, [
'per_page' => 10,
'page' => $commission_page,
]);
}
}
$commission_items = (array) ($commission_view['items'] ?? []);
$status_labels = [
'pending' => __('待结算', 'aksrt'),
'settled' => __('已结算', 'aksrt'),
'cancelled' => __('已取消', 'aksrt'),
];
?>
<div class="uc-revenue-panel" data-aksrt-revenue-page>
<?php if ($affiliate_on) : ?>
<section class="uc-commission-summary" aria-label="<?php esc_attr_e('佣金概览', 'aksrt'); ?>">
<article class="uc-commission-card uc-commission-card--ratio">
<div class="uc-commission-card__body">
<span><?php esc_html_e('佣金比例', 'aksrt'); ?></span>
<strong><?php echo esc_html((string) $commission_ratio); ?><small>%</small></strong>
<p><?php echo $commission_ratio > 0 ? esc_html__('普通用户推广订单返佣比例', 'aksrt') : esc_html__('暂未参与', 'aksrt'); ?></p>
</div>
</article>
<article class="uc-commission-card uc-commission-card--balance">
<div class="uc-commission-card__body">
<span><?php esc_html_e('我的佣金', 'aksrt'); ?></span>
<strong><small>¥</small><?php echo esc_html($format_amount($pending_commission)); ?></strong>
<span><?php printf(esc_html__('累计佣金 ¥%s', 'aksrt'), esc_html($format_amount($commission_stats['total'] ?? 0))); ?></span>
</div>
</article>
</section>
<?php endif; ?>
<section class="uc-reward-area" aria-labelledby="uc-reward-area-title">
<div class="uc-reward-area__heading">
<h3 id="uc-reward-area-title"><?php esc_html_e('推广收益', 'aksrt'); ?></h3>
<span><?php echo esc_html($affiliate_on ? __('推广链接、佣金订单与推广用户', 'aksrt') : __('推广链接与推广用户', 'aksrt')); ?></span>
</div>
<nav class="uc-reward-tabs" aria-label="<?php esc_attr_e('收益明细', 'aksrt'); ?>">
<a <?php echo $active_reward_tab === 'detail' ? 'aria-current="page"' : ''; ?> class="<?php echo $active_reward_tab === 'detail' ? 'is-active' : ''; ?>" href="<?php echo esc_url($reward_url('detail')); ?>"><?php esc_html_e('推广详情', 'aksrt'); ?></a>
<?php if ($affiliate_on) : ?><a <?php echo $active_reward_tab === 'orders' ? 'aria-current="page"' : ''; ?> class="<?php echo $active_reward_tab === 'orders' ? 'is-active' : ''; ?>" href="<?php echo esc_url($reward_url('orders')); ?>"><?php esc_html_e('佣金明细', 'aksrt'); ?></a><?php endif; ?>
<a <?php echo $active_reward_tab === 'users' ? 'aria-current="page"' : ''; ?> class="<?php echo $active_reward_tab === 'users' ? 'is-active' : ''; ?>" href="<?php echo esc_url($reward_url('users')); ?>"><?php esc_html_e('推广用户', 'aksrt'); ?></a>
</nav>
<?php if ($active_reward_tab === 'detail') : ?>
<div class="uc-reward-pane uc-reward-pane--detail" data-aksrt-revenue-page>
<div class="uc-revenue-summary-list">
<div class="uc-revenue-summary-row uc-revenue-summary-row--link">
<span><?php esc_html_e('推广链接', 'aksrt'); ?></span>
<input type="text" readonly data-affiliate-link value="<?php echo esc_attr($affiliate_link_url); ?>" aria-label="<?php esc_attr_e('推广链接', 'aksrt'); ?>">
<button type="button" class="btn-primary btn-sm" data-affiliate-copy><?php esc_html_e('复制链接', 'aksrt'); ?></button>
<?php if (!empty($affiliate['poster']['enabled'])) : ?><button type="button" class="btn-outline btn-sm" data-affiliate-poster hidden><?php esc_html_e('推广海报', 'aksrt'); ?></button><?php endif; ?>
<small data-affiliate-feedback role="status" aria-live="polite"></small>
</div>
<?php if ($affiliate_on) : ?>
<div class="uc-revenue-summary-row"><span><?php esc_html_e('佣金比例', 'aksrt'); ?></span><strong data-affiliate-ratio><?php echo esc_html((string) $commission_ratio); ?>%</strong></div>
<div class="uc-revenue-summary-row uc-revenue-summary-row--total"><span><?php esc_html_e('累计佣金', 'aksrt'); ?></span><div><strong><?php printf(esc_html__('累计 %s 元', 'aksrt'), esc_html($format_amount($commission_stats['total'] ?? 0))); ?></strong><strong><?php printf(esc_html__('待结算 %s 元', 'aksrt'), esc_html($format_amount($commission_stats['pending'] ?? 0))); ?></strong></div></div>
<div class="uc-revenue-summary-row uc-revenue-summary-row--explain"><span><?php echo esc_html($details_title); ?></span><div class="uc-reward-richtext"><?php echo wp_kses_post(wpautop($details_text)); ?></div></div>
<?php endif; ?>
</div>
</div>
<?php elseif ($active_reward_tab === 'orders') : ?>
<div class="uc-reward-pane">
<div class="uc-reward-list-heading"><div><h4><?php esc_html_e('佣金订单', 'aksrt'); ?></h4><p><?php esc_html_e('每页显示 10 条佣金记录。', 'aksrt'); ?></p></div><strong><?php printf(esc_html__('%d 条', 'aksrt'), (int) ($commission_view['total'] ?? 0)); ?></strong></div>
<?php if ($commission_items) : ?>
<div class="uc-commission-list">
<?php foreach ($commission_items as $item) :
$status = sanitize_key((string) ($item['status'] ?? 'pending'));
$source_user = !empty($item['source_user_id']) ? get_userdata((int) $item['source_user_id']) : null;
$source_name = $source_user instanceof WP_User ? $source_user->display_name : __('推广用户', 'aksrt');
?>
<?php
$commission_minor = $signed_balance_minor($item['original_amount'] ?? '0') + $signed_balance_minor($item['adjustment_total'] ?? '0');
$commission_amount = \AKSRT\Ledger\AssetAmount::decimal_from_minor(max(0, $commission_minor));
?>
<article class="uc-commission-row"><span class="uc-commission-row__icon"><i class="fa fas fa-file-alt" aria-hidden="true"></i></span><div class="uc-commission-row__main"><strong><?php echo esc_html($source_name); ?></strong><span><?php printf(esc_html__('订单 %s', 'aksrt'), esc_html((string) ($item['order_number'] ?? '-'))); ?></span><time><?php echo esc_html((string) ($item['created_at'] ?? '')); ?></time></div><div class="uc-commission-row__value"><strong><?php echo esc_html($format_amount($commission_amount)); ?></strong><span class="is-<?php echo esc_attr($status); ?>"><?php echo esc_html($status_labels[$status] ?? $status); ?></span></div></article>
<?php endforeach; ?>
</div>
<?php if ($commission_total_pages > 1) : ?><nav class="uc-reward-pagination" aria-label="<?php esc_attr_e('佣金明细分页', 'aksrt'); ?>"><?php for ($i = 1; $i <= $commission_total_pages; $i++) : ?><a class="<?php echo $i === $commission_page ? 'is-active' : ''; ?>" href="<?php echo esc_url($reward_url('orders', ['commission_page' => $i])); ?>"><?php echo esc_html((string) $i); ?></a><?php endfor; ?></nav><?php endif; ?>
<?php else : ?><p class="uc-reward-empty"><?php esc_html_e('暂无佣金订单', 'aksrt'); ?></p><?php endif; ?>
</div>
<?php else : ?>
<div class="uc-reward-pane">
<div class="uc-reward-list-heading"><div><h4><?php esc_html_e('推广用户', 'aksrt'); ?></h4><p><?php esc_html_e('通过你的 ref 推广链接完成绑定的用户。', 'aksrt'); ?></p></div><strong><?php printf(esc_html__('%d 人', 'aksrt'), (int) ($user_view['total'] ?? 0)); ?></strong></div>
<?php if (!empty($user_view['items'])) : ?>
<div class="uc-revenue-user-list">
<?php foreach ($user_view['items'] as $item) : ?>
<article class="uc-revenue-user-row"><img src="<?php echo esc_url((string) ($item['avatar'] ?? '')); ?>" alt=""><div><strong><?php echo esc_html((string) ($item['name'] ?? __('推广用户', 'aksrt'))); ?></strong><small><?php echo esc_html((string) ($item['bound_at'] ?? '')); ?></small></div><span><?php esc_html_e('已绑定', 'aksrt'); ?></span></article>
<?php endforeach; ?>
</div>
<?php if ($user_total_pages > 1) : ?><nav class="uc-reward-pagination" aria-label="<?php esc_attr_e('推广用户分页', 'aksrt'); ?>"><?php for ($i = 1; $i <= $user_total_pages; $i++) : ?><a class="<?php echo $i === $user_page ? 'is-active' : ''; ?>" href="<?php echo esc_url($reward_url('users', ['user_page' => $i])); ?>"><?php echo esc_html((string) $i); ?></a><?php endfor; ?></nav><?php endif; ?>
<?php else : ?><p class="uc-reward-empty"><?php esc_html_e('暂无推广用户', 'aksrt'); ?></p><?php endif; ?>
</div>
<?php endif; ?>
</section>
</div>
<?php if ($withdraw_on) {
require __DIR__ . '/withdraw-modal.php';
} ?>
<?php require __DIR__ . '/affiliate-poster-modal.php'; ?>