feat: unify referral promotion tabs and compact earnings UI

This commit is contained in:
2026-08-16 20:40:21 +08:00
parent 8799063c6c
commit ff730ab593
7 changed files with 144 additions and 52 deletions
+48 -8
View File
@@ -99,8 +99,8 @@
} }
.uc-content .uc-commission-card { .uc-content .uc-commission-card {
min-height: 174px; min-height: 116px;
padding: 24px 26px; padding: 16px 22px;
border: 0; border: 0;
border-radius: 18px; border-radius: 18px;
box-shadow: 0 12px 28px rgba(15, 23, 42, .08); box-shadow: 0 12px 28px rgba(15, 23, 42, .08);
@@ -125,7 +125,7 @@
.uc-content .uc-commission-card__body { .uc-content .uc-commission-card__body {
display: grid; display: grid;
align-content: space-between; align-content: space-between;
min-height: 126px; min-height: 84px;
} }
.uc-content .uc-commission-card__body > span, .uc-content .uc-commission-card__body > span,
@@ -142,13 +142,13 @@
} }
.uc-content .uc-commission-card__body strong { .uc-content .uc-commission-card__body strong {
font-size: 42px; font-size: 32px;
line-height: 1; line-height: 1;
} }
.uc-content .uc-commission-card__body strong small { .uc-content .uc-commission-card__body strong small {
margin-left: 4px; margin-left: 4px;
font-size: 18px; font-size: 15px;
} }
.uc-content .uc-commission-card__body > span:last-child, .uc-content .uc-commission-card__body > span:last-child,
@@ -174,6 +174,36 @@
backdrop-filter: blur(4px); backdrop-filter: blur(4px);
} }
/* 三 Tab 推广收益:佣金详细页采用紧凑的一列信息清单。 */
.uc-content .uc-reward-area { display: grid; gap: 18px; padding: 24px 28px 30px; border-radius: 18px; background: var(--surface-1, #fff); box-shadow: 0 8px 26px rgba(15, 23, 42, .06); }
.uc-content .uc-reward-area__heading { display: flex; align-items: baseline; justify-content: space-between; gap: 12px; }
.uc-content .uc-reward-area__heading h3 { margin: 0; font-size: 20px; }
.uc-content .uc-reward-area__heading > span { color: var(--color-ink-4); font-size: 13px; }
.uc-content .uc-reward-tabs { display: flex; gap: 24px; border-bottom: 1px solid var(--color-line-soft, rgba(15, 23, 42, .08)); }
.uc-content .uc-reward-tabs a { position: relative; padding: 0 0 12px; color: var(--color-ink-4); font-size: 14px; text-decoration: none; }
.uc-content .uc-reward-tabs a.is-active { color: var(--color-primary); font-weight: 700; }
.uc-content .uc-reward-tabs a.is-active::after { position: absolute; right: 0; bottom: -1px; left: 0; height: 2px; border-radius: 99px; background: var(--color-primary); content: ""; }
.uc-content .uc-promotion-summary-list { display: grid; gap: 0; max-width: 920px; }
.uc-content .uc-promotion-summary-row { display: grid; grid-template-columns: 112px minmax(0, 1fr) auto; align-items: center; gap: 12px; min-height: 54px; padding: 8px 0; border-bottom: 1px solid var(--color-line-soft, rgba(15, 23, 42, .07)); }
.uc-content .uc-promotion-summary-row > span { color: var(--color-ink-3); font-size: 14px; }
.uc-content .uc-promotion-summary-row > strong { color: var(--color-ink); font-size: 16px; }
.uc-content .uc-promotion-summary-row > em { color: var(--color-ink-4); font-style: normal; }
.uc-content .uc-promotion-summary-row--link input { width: 100%; min-width: 0; height: 38px; padding: 0 10px; border: 1px solid var(--color-line-soft, rgba(15, 23, 42, .12)); border-radius: 7px; color: var(--color-primary); background: var(--surface-2, #f8fafc); }
.uc-content .uc-promotion-summary-row--link button { min-width: 64px; height: 38px; padding: 0 12px; border-radius: 7px; }
.uc-content .uc-promotion-summary-row--link small { grid-column: 2 / -1; min-height: 16px; color: var(--color-success, #16a34a); font-size: 12px; }
.uc-content .uc-promotion-summary-row--link small.is-error { color: var(--color-error, #dc2626); }
.uc-content .uc-promotion-summary-row--total > div { display: flex; flex-wrap: wrap; gap: 12px; }
.uc-content .uc-promotion-summary-row--total strong { padding: 7px 11px; border-radius: 7px; color: var(--color-primary); background: var(--surface-2, #f8fafc); font-size: 14px; }
.uc-content .uc-promotion-summary-row--explain { align-items: start; padding-top: 16px; border-bottom: 0; }
.uc-content .uc-promotion-summary-row--explain .uc-reward-richtext { grid-column: 2 / -1; color: var(--color-ink-3); font-size: 14px; line-height: 1.75; }
.uc-content .uc-promotion-summary-row--explain .uc-reward-richtext p { margin: 0; }
.uc-content .uc-promotion-user-list { display: grid; }
.uc-content .uc-promotion-user-row { display: flex; align-items: center; gap: 12px; padding: 13px 0; border-bottom: 1px solid var(--color-line-soft, rgba(15, 23, 42, .07)); }
.uc-content .uc-promotion-user-row img { width: 38px; height: 38px; border-radius: 50%; object-fit: cover; background: var(--surface-2); }
.uc-content .uc-promotion-user-row > div { display: grid; flex: 1; gap: 3px; min-width: 0; }
.uc-content .uc-promotion-user-row small { color: var(--color-ink-4); font-size: 12px; }
.uc-content .uc-promotion-user-row > span { color: var(--color-success, #16a34a); font-size: 12px; }
.uc-content .uc-commission-card__action:hover { .uc-content .uc-commission-card__action:hover {
background: rgba(255, 255, 255, .24); background: rgba(255, 255, 255, .24);
} }
@@ -293,16 +323,16 @@
@media (max-width: 560px) { @media (max-width: 560px) {
.uc-content .uc-commission-card { .uc-content .uc-commission-card {
min-height: 150px; min-height: 100px;
padding: 20px; padding: 20px;
} }
.uc-content .uc-commission-card__body { .uc-content .uc-commission-card__body {
min-height: 108px; min-height: 72px;
} }
.uc-content .uc-commission-card__body strong { .uc-content .uc-commission-card__body strong {
font-size: 36px; font-size: 28px;
} }
.uc-content .uc-affiliate-link-card { .uc-content .uc-affiliate-link-card {
@@ -321,4 +351,14 @@
.uc-content .uc-affiliate-stats { .uc-content .uc-affiliate-stats {
grid-template-columns: 1fr; grid-template-columns: 1fr;
} }
.uc-content .uc-reward-area { padding: 20px 16px 24px; }
.uc-content .uc-reward-area__heading { display: grid; gap: 4px; }
.uc-content .uc-reward-tabs { gap: 16px; overflow-x: auto; }
.uc-content .uc-promotion-summary-row { grid-template-columns: 92px minmax(0, 1fr) auto; gap: 8px; }
.uc-content .uc-promotion-summary-row--link { grid-template-columns: 1fr auto; }
.uc-content .uc-promotion-summary-row--link > span { grid-column: 1 / -1; }
.uc-content .uc-promotion-summary-row--link small { grid-column: 1 / -1; }
.uc-content .uc-promotion-summary-row--explain { grid-template-columns: 1fr; }
.uc-content .uc-promotion-summary-row--explain .uc-reward-richtext { grid-column: 1; }
} }
+3 -4
View File
@@ -599,10 +599,9 @@ if (!function_exists('aksrt_get_post_rebate_discount')) {
return 0.0; return 0.0;
} }
$uid = $user_id > 0 ? $user_id : (function_exists('get_current_user_id') ? (int) get_current_user_id() : 0); $uid = $user_id > 0 ? $user_id : (function_exists('get_current_user_id') ? (int) get_current_user_id() : 0);
$referrer_id = 0; $referrer_id = class_exists('\AKSRT\Payment\ReferralAttribution')
if (class_exists('\AKSRT\Credits\Invite') && method_exists(\AKSRT\Credits\Invite::get_instance(), 'get_referrer')) { ? \AKSRT\Payment\ReferralAttribution::get_instance()->resolve($uid)
$referrer_id = (int) \AKSRT\Credits\Invite::get_instance()->get_referrer($uid); : 0;
}
if ($referrer_id <= 0) { if ($referrer_id <= 0) {
return 0.0; return 0.0;
} }
@@ -46,7 +46,7 @@ if ($details_text === '') {
$revenue_request = is_array($context['revenue_request'] ?? null) ? $context['revenue_request'] : []; $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'))); $active_reward_tab = sanitize_key((string) ($revenue_request['reward_tab'] ?? ($_GET['reward_tab'] ?? 'detail')));
if (!in_array($active_reward_tab, ['detail', 'orders'], true)) { if (!in_array($active_reward_tab, ['detail', 'orders', 'users'], true)) {
$active_reward_tab = 'detail'; $active_reward_tab = 'detail';
} }
$base_url = function_exists('aksrt_get_user_center_tab_url') $base_url = function_exists('aksrt_get_user_center_tab_url')
@@ -67,6 +67,15 @@ $commission_view = class_exists('\AKSRT\Payment\CommissionLedger')
]) ])
: ['items' => [], 'total' => 0, 'max_pages' => 1, 'page' => 1]; : ['items' => [], 'total' => 0, 'max_pages' => 1, 'page' => 1];
$commission_items = (array) ($commission_view['items'] ?? []); $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))); $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) { if ($commission_page > $commission_total_pages) {
$commission_page = $commission_total_pages; $commission_page = $commission_total_pages;
@@ -104,44 +113,31 @@ $status_labels = [
<?php endif; ?> <?php endif; ?>
</article> </article>
</section> </section>
<section class="uc-affiliate-panel aksrt-affiliate-panel" data-aksrt-promotion-page aria-labelledby="uc-affiliate-title">
<header class="uc-panel-heading">
<h2 id="uc-affiliate-title"><?php esc_html_e('推荐奖励', 'aksrt'); ?></h2>
<p><?php echo esc_html($affiliate['promotion_text'] ?? __('分享推广链接,好友完成会员或付费内容订单后获得佣金。', 'aksrt')); ?></p>
</header>
<div class="uc-affiliate-link-card" data-affiliate-card>
<div>
<span class="muted-2-color"><?php esc_html_e('推广链接', 'aksrt'); ?></span>
<input type="text" readonly data-affiliate-link aria-label="<?php esc_attr_e('推广链接', 'aksrt'); ?>">
</div>
<button type="button" class="btn-primary btn-sm" data-affiliate-copy>
<i class="fas fa-copy" aria-hidden="true"></i><?php esc_html_e('复制链接', 'aksrt'); ?>
</button>
<p data-affiliate-feedback role="status" aria-live="polite"></p>
</div>
<div class="uc-affiliate-stats" data-affiliate-stats>
<article><span><?php esc_html_e('当前比例', 'aksrt'); ?></span><strong data-affiliate-ratio>—</strong></article>
<article><span><?php esc_html_e('推广人数', 'aksrt'); ?></span><strong data-affiliate-promoters>—</strong></article>
<article><span><?php esc_html_e('有效付费人数', 'aksrt'); ?></span><strong data-affiliate-payers>—</strong></article>
</div>
<p class="uc-affiliate-note"><?php esc_html_e('仅会员和付费内容订单参与返佣;首次触达后 30 天内绑定,禁止自邀和换绑。', 'aksrt'); ?></p>
<section class="uc-reward-area" aria-labelledby="uc-reward-area-title"> <section class="uc-reward-area" aria-labelledby="uc-reward-area-title">
<div class="uc-reward-area__heading"> <div class="uc-reward-area__heading">
<h3 id="uc-reward-area-title"><?php esc_html_e('佣金详情', 'aksrt'); ?></h3> <h3 id="uc-reward-area-title"><?php esc_html_e('推广收益', 'aksrt'); ?></h3>
<span><?php esc_html_e('推广订单与结算状态', 'aksrt'); ?></span> <span><?php esc_html_e('推广链接、佣金订单与推广用户', 'aksrt'); ?></span>
</div> </div>
<nav class="uc-reward-tabs" aria-label="<?php esc_attr_e('收益明细', 'aksrt'); ?>"> <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> <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>
<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> <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>
<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> </nav>
<?php if ($active_reward_tab === 'detail') : ?> <?php if ($active_reward_tab === 'detail') : ?>
<div class="uc-reward-pane uc-reward-pane--detail"> <div class="uc-reward-pane uc-reward-pane--detail" data-aksrt-promotion-page>
<section class="uc-reward-block uc-reward-block--details" aria-labelledby="uc-details-title"> <div class="uc-promotion-summary-list">
<div class="uc-reward-block__heading"><h4 id="uc-details-title"><?php esc_html_e('返佣详解', 'aksrt'); ?></h4></div> <div class="uc-promotion-summary-row uc-promotion-summary-row--link">
<div class="uc-reward-richtext"><?php echo wp_kses_post(wpautop($details_text)); ?></div> <span><?php esc_html_e('推广链接', 'aksrt'); ?></span>
</section> <input type="text" readonly data-affiliate-link aria-label="<?php esc_attr_e('推广链接', 'aksrt'); ?>">
<button type="button" class="btn-primary btn-sm" data-affiliate-copy><?php esc_html_e('复制', 'aksrt'); ?></button>
<small data-affiliate-feedback role="status" aria-live="polite"></small>
</div>
<div class="uc-promotion-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-promotion-summary-row"><span><?php esc_html_e('推广人数', 'aksrt'); ?></span><strong data-affiliate-promoters>—</strong><em><?php esc_html_e('人', 'aksrt'); ?></em></div>
<div class="uc-promotion-summary-row uc-promotion-summary-row--total"><span><?php esc_html_e('累计佣金', 'aksrt'); ?></span><div><strong>累计<?php echo esc_html($format_amount($commission_stats['total'] ?? 0)); ?>元</strong><strong>已提现<?php echo esc_html($format_amount($commission_stats['withdrawn'] ?? 0)); ?>元</strong></div></div>
<div class="uc-promotion-summary-row uc-promotion-summary-row--explain"><span><?php esc_html_e('返佣详解', 'aksrt'); ?></span><div class="uc-reward-richtext"><?php echo wp_kses_post(wpautop($details_text)); ?></div></div>
</div>
</div> </div>
<?php elseif ($active_reward_tab === 'orders') : ?> <?php elseif ($active_reward_tab === 'orders') : ?>
<div class="uc-reward-pane"> <div class="uc-reward-pane">
@@ -160,10 +156,19 @@ $status_labels = [
<?php else : ?><p class="uc-reward-empty"><?php esc_html_e('暂无佣金订单', 'aksrt'); ?></p><?php endif; ?> <?php else : ?><p class="uc-reward-empty"><?php esc_html_e('暂无佣金订单', 'aksrt'); ?></p><?php endif; ?>
</div> </div>
<?php else : ?> <?php else : ?>
<div class="uc-reward-pane"><p class="uc-reward-empty"><?php esc_html_e('暂无收益明细。', 'aksrt'); ?></p></div> <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-promotion-user-list">
<?php foreach ($user_view['items'] as $item) : ?>
<article class="uc-promotion-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; ?> <?php endif; ?>
</section> </section>
</section>
</div> </div>
<?php if ($withdraw_on) { <?php if ($withdraw_on) {
require __DIR__ . '/withdraw-modal.php'; require __DIR__ . '/withdraw-modal.php';
+3 -4
View File
@@ -376,10 +376,9 @@ trait PayHelpers {
if (empty($aff['enabled'])) { if (empty($aff['enabled'])) {
return $empty; return $empty;
} }
$referrer_id = 0; $referrer_id = class_exists('\AKSRT\Payment\ReferralAttribution')
if (class_exists('\AKSRT\Credits\Invite') && method_exists(\AKSRT\Credits\Invite::get_instance(), 'get_referrer')) { ? \AKSRT\Payment\ReferralAttribution::get_instance()->resolve($user_id)
$referrer_id = (int) \AKSRT\Credits\Invite::get_instance()->get_referrer($user_id); : 0;
}
if ($referrer_id <= 0) { if ($referrer_id <= 0) {
return $empty; return $empty;
} }
+2 -2
View File
@@ -74,10 +74,10 @@ class Invite {
], ],
]); ]);
} }
/** Capture ?ref=XYZ, with legacy ?invite=XYZ support. */ /** Capture the unified ?ref=XYZ referral parameter. */
public function capture_invite_cookie(): void { public function capture_invite_cookie(): void {
if (is_admin()) return; if (is_admin()) return;
$raw_code = $_GET['ref'] ?? $_GET['invite'] ?? ''; $raw_code = $_GET['ref'] ?? '';
if ($raw_code === '') return; if ($raw_code === '') return;
// 邀请注册功能关闭时不处理 // 邀请注册功能关闭时不处理
if (class_exists('\AKSRT\Settings\Settings') && !\AKSRT\Settings\Settings::invite_registration_enabled()) return; if (class_exists('\AKSRT\Settings\Settings') && !\AKSRT\Settings\Settings::invite_registration_enabled()) return;
+9 -1
View File
@@ -90,7 +90,15 @@ final class CommissionLedger {
public function overview(int $user_id): array { public function overview(int $user_id): array {
global $wpdb; $table=$this->table('commission_ledger'); global $wpdb; $table=$this->table('commission_ledger');
$row=$wpdb->get_row($wpdb->prepare("SELECT COALESCE(SUM(original_amount+adjustment_total),0) total, COALESCE(SUM(CASE WHEN status='pending' THEN original_amount+adjustment_total ELSE 0 END),0) pending, COALESCE(SUM(CASE WHEN status='settled' THEN original_amount+adjustment_total ELSE 0 END),0) settled FROM {$table} WHERE promoter_id=%d",$user_id),ARRAY_A) ?: []; $row=$wpdb->get_row($wpdb->prepare("SELECT COALESCE(SUM(original_amount+adjustment_total),0) total, COALESCE(SUM(CASE WHEN status='pending' THEN original_amount+adjustment_total ELSE 0 END),0) pending, COALESCE(SUM(CASE WHEN status='settled' THEN original_amount+adjustment_total ELSE 0 END),0) settled FROM {$table} WHERE promoter_id=%d",$user_id),ARRAY_A) ?: [];
return ['total'=>round((float)($row['total']??0),2),'pending'=>round((float)($row['pending']??0),2),'settled'=>round((float)($row['settled']??0),2),'wallet'=>class_exists('\\AKSRT\\Credits\\Balance')?\AKSRT\Credits\Balance::balance($user_id):0.0,'debt'=>round((float)get_user_meta($user_id,self::DEBT_META,true),2)]; $withdrawn = 0.0;
$withdrawal_table = $this->table('withdrawal');
if ($withdrawal_table !== '') {
$withdrawn = (float) $wpdb->get_var($wpdb->prepare(
"SELECT COALESCE(SUM(CASE WHEN status='completed' THEN CASE WHEN gross_amount > 0 THEN gross_amount ELSE amount END ELSE 0 END),0) FROM {$withdrawal_table} WHERE user_id=%d",
$user_id
));
}
return ['total'=>round((float)($row['total']??0),2),'pending'=>round((float)($row['pending']??0),2),'settled'=>round((float)($row['settled']??0),2),'wallet'=>class_exists('\\AKSRT\\Credits\\Balance')?\AKSRT\Credits\Balance::balance($user_id):0.0,'withdrawn'=>round($withdrawn,2),'debt'=>round((float)get_user_meta($user_id,self::DEBT_META,true),2)];
} }
public function list_entries(int $user_id, array $args=[]): array { public function list_entries(int $user_id, array $args=[]): array {
global $wpdb; $table=$this->table('commission_ledger'); $limit=max(1,min(100,(int)($args['per_page']??20))); $page=max(1,(int)($args['page']??1)); $offset=($page-1)*$limit; $where='promoter_id=%d'; $params=[$user_id]; $status=sanitize_key((string)($args['status']??'')); if(in_array($status,['pending','settled','cancelled'],true)){ $where.=' AND status=%s'; $params[]=$status; } $total=(int)$wpdb->get_var($wpdb->prepare("SELECT COUNT(*) FROM {$table} WHERE {$where}",$params)); $rows=$wpdb->get_results($wpdb->prepare("SELECT * FROM {$table} WHERE {$where} ORDER BY id DESC LIMIT %d OFFSET %d",array_merge($params,[$limit,$offset])),ARRAY_A); return ['items'=>$rows?:[],'total'=>$total,'page'=>$page,'per_page'=>$limit,'max_pages'=>max(1,(int)ceil($total/$limit))]; global $wpdb; $table=$this->table('commission_ledger'); $limit=max(1,min(100,(int)($args['per_page']??20))); $page=max(1,(int)($args['page']??1)); $offset=($page-1)*$limit; $where='promoter_id=%d'; $params=[$user_id]; $status=sanitize_key((string)($args['status']??'')); if(in_array($status,['pending','settled','cancelled'],true)){ $where.=' AND status=%s'; $params[]=$status; } $total=(int)$wpdb->get_var($wpdb->prepare("SELECT COUNT(*) FROM {$table} WHERE {$where}",$params)); $rows=$wpdb->get_results($wpdb->prepare("SELECT * FROM {$table} WHERE {$where} ORDER BY id DESC LIMIT %d OFFSET %d",array_merge($params,[$limit,$offset])),ARRAY_A); return ['items'=>$rows?:[],'total'=>$total,'page'=>$page,'per_page'=>$limit,'max_pages'=>max(1,(int)ceil($total/$limit))];
+41
View File
@@ -140,6 +140,47 @@ final class ReferralAttribution {
return ['promoters' => $promoters, 'effective_payers' => $payers]; return ['promoters' => $promoters, 'effective_payers' => $payers];
} }
/**
* Return users bound through the forward-only ref relation.
*
* @return array{items:array<int,array<string,mixed>>,total:int,page:int,per_page:int,max_pages:int}
*/
public function list_referred_users(int $promoter_id, int $page = 1, int $per_page = 10): array {
global $wpdb;
$table = \AKSRT\Core\DBMigrator::table('affiliate_relation');
$page = max(1, $page);
$per_page = max(1, min(50, $per_page));
if ($promoter_id <= 0 || $table === '') {
return ['items' => [], 'total' => 0, 'page' => $page, 'per_page' => $per_page, 'max_pages' => 1];
}
$total = (int) $wpdb->get_var($wpdb->prepare(
"SELECT COUNT(*) FROM {$table} WHERE promoter_id=%d AND status='bound' AND invitee_id > 0",
$promoter_id
));
$rows = $wpdb->get_results($wpdb->prepare(
"SELECT invitee_id, referral_code, bound_at, status FROM {$table} WHERE promoter_id=%d AND status='bound' AND invitee_id > 0 ORDER BY id DESC LIMIT %d OFFSET %d",
$promoter_id,
$per_page,
($page - 1) * $per_page
), ARRAY_A);
$items = [];
foreach ((array) $rows as $row) {
$invitee_id = (int) ($row['invitee_id'] ?? 0);
$user = $invitee_id > 0 ? get_userdata($invitee_id) : null;
if (!$user instanceof \WP_User) {
continue;
}
$items[] = [
'user_id' => $invitee_id,
'name' => $user->display_name,
'avatar' => get_avatar_url($invitee_id, ['size' => 64]),
'bound_at' => (string) ($row['bound_at'] ?? ''),
'status' => (string) ($row['status'] ?? 'bound'),
];
}
return ['items' => $items, 'total' => $total, 'page' => $page, 'per_page' => $per_page, 'max_pages' => max(1, (int) ceil($total / $per_page))];
}
public function register_routes(): void { public function register_routes(): void {
register_rest_route('aksrt/v1', '/promotion/me', [ register_rest_route('aksrt/v1', '/promotion/me', [
'methods' => \WP_REST_Server::READABLE, 'methods' => \WP_REST_Server::READABLE,