feat(user-center): points buy, balance withdraw, and asset record tabs

Add cash purchase of points (points_pay_s), wallet withdraw requests via
AKSRT_Withdrawal, Zibll-style dual-card layout with tabbed balance/points
ledgers, and admin switches for buy-points packages and balance withdraw.
This commit is contained in:
kate522 committed 2026-07-16 15:38:55 +08:00
1 parent 0529ab5fb9
commit d57fc6f934
10 files changed
+780 -16

No files matched your search

+2
View File
@@ -30,6 +30,8 @@ class AKSRT_Domain_Model {
public const ORDER_PRODUCT_RESOURCE = 'resource';
public const ORDER_PRODUCT_VIP = 'vip';
public const ORDER_PRODUCT_RECHARGE = 'recharge';
/** Cash purchase of points (zibll order_type=9). */
public const ORDER_PRODUCT_POINTS_BUY = 'points_buy';
public const ORDER_PRODUCT_SHOP = 'shop_product';
/** Paid plate follow (zibll order_type=11). product_id = plate post ID. */
public const ORDER_PRODUCT_PLATE_FOLLOW = 'plate_follow';
@@ -18,7 +18,10 @@ if (class_exists('AKSRT_Charge_Card')) {
$opts = AKSRT_Charge_Card::get_instance()->get_public_options();
$card_pass_credits = is_array($opts['credits'] ?? null) ? $opts['credits'] : $card_pass_credits;
}
$points_transfer_on = class_exists('AKSRT_Settings') && !empty(AKSRT_Settings::get_all()['points_transfer_s']);
$all_settings = class_exists('AKSRT_Settings') ? AKSRT_Settings::get_all() : [];
$points_transfer_on = !empty($all_settings['points_transfer_s']);
$points_buy_on = class_exists('AKSRT_Balance_Service') && AKSRT_Balance_Service::get_instance()->points_buy_enabled();
$points_buy_view = $points_buy_on ? AKSRT_Balance_Service::get_instance()->get_points_buy_view((int) ($user_id ?? 0)) : [];
$currency = (string) ($currency_name ?? __('积分', 'aksrt'));
?>
<div class="uc-asset-card uc-asset-card--points zib-widget">
@@ -41,11 +44,68 @@ $currency = (string) ($currency_name ?? __('积分', 'aksrt'));
<?php if ($points_transfer_on) : ?>
<button type="button" class="but c-green mr6 hollow" data-aksrt-transfer-open="points"><?php esc_html_e('转账', 'aksrt'); ?></button>
<?php endif; ?>
<?php if ($points_buy_on) : ?>
<button type="button" class="but c-green" data-aksrt-points-buy-open><?php esc_html_e('购买积分', 'aksrt'); ?></button>
<?php endif; ?>
</div>
</div>
<p class="uc-balance-card__tip muted-2-color em09" data-signin-tip></p>
</div>
<?php if ($points_buy_on) :
$pb_packages = is_array($points_buy_view['packages'] ?? null) ? $points_buy_view['packages'] : [];
$pb_methods = is_array($points_buy_view['payment_methods'] ?? null) ? $points_buy_view['payment_methods'] : [];
$pb_rate = (int) ($points_buy_view['rate'] ?? 100);
$pb_custom = !empty($points_buy_view['custom_s']);
$pb_limit = (int) ($points_buy_view['custom_limit'] ?? 99999);
?>
<div class="uc-modal" id="aksrt-points-buy-modal" hidden data-aksrt-points-buy-modal>
<div class="uc-modal__overlay" data-aksrt-points-buy-close></div>
<div class="uc-modal__dialog" role="dialog" aria-modal="true">
<button type="button" class="uc-modal__close" data-aksrt-points-buy-close aria-label="<?php esc_attr_e('关闭', 'aksrt'); ?>"><i class="fas fa-times" aria-hidden="true"></i></button>
<h3 class="uc-modal__title"><i class="fas fa-coins mr6" aria-hidden="true"></i><?php esc_html_e('购买积分', 'aksrt'); ?></h3>
<?php if (!empty($points_buy_view['desc'])) : ?>
<div class="uc-modal__desc muted-box em09"><?php echo esc_html((string) $points_buy_view['desc']); ?></div>
<?php endif; ?>
<form data-aksrt-points-buy-form>
<?php if ($pb_packages) : ?>
<div class="muted-color mb6"><?php esc_html_e('请选择积分套餐', 'aksrt'); ?></div>
<div class="uc-charge-packages">
<?php foreach ($pb_packages as $idx => $pkg) : ?>
<label class="uc-charge-package<?php echo $idx === 0 ? ' is-active' : ''; ?>">
<input type="radio" name="points_package" value="<?php echo esc_attr((string) $pkg['key']); ?>" <?php checked($idx === 0); ?>>
<div class="uc-charge-package__credit"><?php echo esc_html((string) (int) $pkg['points']); ?> <span class="px12"><?php echo esc_html($currency); ?></span></div>
<div class="uc-charge-package__pay c-red"><span class="px12">¥</span><?php echo esc_html((string) $pkg['pay_price']); ?></div>
</label>
<?php endforeach; ?>
</div>
<?php endif; ?>
<?php if ($pb_custom) : ?>
<div class="muted-color mt16 mb6"><?php echo $pb_packages ? esc_html__('自定义积分数量', 'aksrt') : esc_html__('请输入购买积分数量', 'aksrt'); ?></div>
<input type="number" class="uc-card-pass__input" name="custom_points" min="<?php echo esc_attr((string) $pb_rate); ?>" max="<?php echo esc_attr((string) $pb_limit); ?>" step="1" data-aksrt-points-buy-custom>
<p class="muted-2-color em09 mt6"><?php printf(esc_html__('1 元 = %d 积分', 'aksrt'), $pb_rate); ?></p>
<?php if (!$pb_packages) : ?>
<input type="hidden" name="points_package" value="custom">
<?php endif; ?>
<?php endif; ?>
<?php if ($pb_methods) : ?>
<div class="muted-color mt16 mb6"><?php esc_html_e('支付方式', 'aksrt'); ?></div>
<div class="uc-charge-methods">
<?php foreach ($pb_methods as $mi => $m) : ?>
<label class="uc-charge-method">
<input type="radio" name="points_pay_method" value="<?php echo esc_attr((string) ($m['id'] ?? '')); ?>" <?php checked($mi === 0); ?>>
<span><?php echo esc_html((string) ($m['name'] ?? $m['id'] ?? '')); ?></span>
</label>
<?php endforeach; ?>
</div>
<?php endif; ?>
<p class="uc-modal__tip" data-aksrt-points-buy-tip></p>
<button type="submit" class="but jb-blue btn-block mt10"><?php esc_html_e('立即购买', 'aksrt'); ?></button>
</form>
</div>
</div>
<?php endif; ?>
<?php if (!empty($card_pass_credits['enabled'])) : ?>
<div class="uc-card-pass" data-aksrt-card-pass="credits">
<div class="uc-card-pass__title">
@@ -23,6 +23,9 @@ $recharge = class_exists('AKSRT_Settings') && method_exists('AKSRT_Settings', 'g
: [];
$transfer_on = !empty($recharge['transfer']['enabled']);
$exchange_on = !empty($recharge['exchange']['enabled']);
$all_settings = class_exists('AKSRT_Settings') ? AKSRT_Settings::get_all() : [];
$withdraw_on = !empty($all_settings['pay_balance_withdraw_s']);
$withdraw_min = max(1, (float) ($all_settings['pay_balance_withdraw_lowest_money'] ?? $all_settings['pay_rebate_withdraw_lowest_money'] ?? 50));
$card_pass_balance = ['enabled' => false, 'desc' => '', 'only_password' => true];
if (class_exists('AKSRT_Charge_Card')) {
$opts = AKSRT_Charge_Card::get_instance()->get_public_options();
@@ -56,6 +59,9 @@ $mark = (string) ($charge_view['currency_mark'] ?? '元');
<?php if ($transfer_on) : ?>
<button type="button" class="but c-blue-2 mr6 hollow" data-aksrt-transfer-open="balance"><?php esc_html_e('转账', 'aksrt'); ?></button>
<?php endif; ?>
<?php if ($withdraw_on) : ?>
<button type="button" class="but c-blue-2 mr6 hollow" data-aksrt-withdraw-open><?php esc_html_e('提现', 'aksrt'); ?></button>
<?php endif; ?>
<button type="button" class="but b-blue-2" data-aksrt-charge-open><?php esc_html_e('充值', 'aksrt'); ?></button>
</div>
</div>
@@ -200,6 +206,32 @@ $mark = (string) ($charge_view['currency_mark'] ?? '元');
</div>
<?php endif; ?>
<?php if ($withdraw_on) : ?>
<div class="uc-modal" id="aksrt-withdraw-modal" hidden data-aksrt-withdraw-modal>
<div class="uc-modal__overlay" data-aksrt-withdraw-close></div>
<div class="uc-modal__dialog" role="dialog" aria-modal="true">
<button type="button" class="uc-modal__close" data-aksrt-withdraw-close aria-label="<?php esc_attr_e('关闭', 'aksrt'); ?>"><i class="fas fa-times" aria-hidden="true"></i></button>
<h3 class="uc-modal__title"><?php esc_html_e('余额提现', 'aksrt'); ?></h3>
<form data-aksrt-withdraw-form>
<label class="muted-color em09"><?php esc_html_e('提现金额', 'aksrt'); ?></label>
<input type="number" class="uc-card-pass__input" name="amount" min="<?php echo esc_attr((string) $withdraw_min); ?>" step="0.01" data-aksrt-withdraw-amount>
<p class="muted-2-color em09 mt6"><?php printf(esc_html__('最低提现 %s 元,当前余额 %s', 'aksrt'), esc_html((string) $withdraw_min), esc_html($wallet_fmt)); ?></p>
<label class="muted-color em09 mt10"><?php esc_html_e('收款方式', 'aksrt'); ?></label>
<select class="uc-card-pass__input" name="method" data-aksrt-withdraw-method>
<option value="alipay"><?php esc_html_e('支付宝', 'aksrt'); ?></option>
<option value="wechat"><?php esc_html_e('微信', 'aksrt'); ?></option>
<option value="bank"><?php esc_html_e('银行卡', 'aksrt'); ?></option>
</select>
<label class="muted-color em09 mt10"><?php esc_html_e('收款账号信息', 'aksrt'); ?></label>
<textarea class="uc-card-pass__input" name="account" rows="3" data-aksrt-withdraw-account placeholder="<?php esc_attr_e('姓名 + 账号 / 卡号', 'aksrt'); ?>"></textarea>
<p class="uc-modal__tip" data-aksrt-withdraw-tip></p>
<button type="submit" class="but jb-blue btn-block" data-aksrt-withdraw-submit><?php esc_html_e('提交申请', 'aksrt'); ?></button>
</form>
</div>
</div>
<?php endif; ?>
<?php if (empty($skip_wallet_ledger)) : ?>
<div class="uc-transactions uc-transactions--wallet">
<div class="uc-transactions__header">
<h3 class="uc-transactions__title">
@@ -259,4 +291,6 @@ $mark = (string) ($charge_view['currency_mark'] ?? '元');
<p><?php esc_html_e('还没有任何余额流水', 'aksrt'); ?></p>
</div>
<?php endif; ?>
<?php endif; // skip_wallet_ledger ?>
</div>
@@ -2,7 +2,7 @@
/**
* User center assets panel orchestrator (wallet + points).
*
* Zibll user-tab-balance parity layout.
* Zibll user-tab-balance parity: dual cards + tabbed ledgers.
*
* @package AKSRT
*/
@@ -22,15 +22,16 @@ if ($show_wallet || $show_credits) :
<div class="uc-assets-row<?php echo ($show_wallet && $show_credits) ? ' uc-assets-row--dual' : ''; ?>">
<?php if ($show_wallet) : ?>
<div class="uc-assets-col">
<?php require $dir . '/credits-wallet.php'; ?>
<?php
// Ledger moved to tab section below.
$skip_wallet_ledger = true;
require $dir . '/credits-wallet.php';
?>
</div>
<?php endif; ?>
<?php if ($show_credits) : ?>
<div class="uc-assets-col">
<?php
// Points card only (ledger rendered once below).
require $dir . '/credits-balance.php';
?>
<?php require $dir . '/credits-balance.php'; ?>
</div>
<?php endif; ?>
</div>
@@ -42,6 +43,88 @@ if (!empty($show_vip)) {
}
if ($show_credits) {
require $dir . '/credits-signin.php';
require $dir . '/credits-transactions.php';
}
// Tabbed ledgers (Zibll record tabs).
if ($show_wallet || $show_credits) :
$active_record = isset($_GET['asset_tab']) ? sanitize_key(wp_unslash($_GET['asset_tab'])) : '';
if ($active_record === '' || !in_array($active_record, ['balance', 'points'], true)) {
$active_record = $show_wallet ? 'balance' : 'points';
}
?>
<div class="uc-asset-tabs zib-widget">
<ul class="uc-asset-tabs__nav list-inline tab-nav-theme font-bold">
<?php if ($show_wallet) : ?>
<li class="<?php echo $active_record === 'balance' ? 'active' : ''; ?>">
<a href="#record_tab_balance" data-aksrt-asset-tab="balance"><?php esc_html_e('余额记录', 'aksrt'); ?></a>
</li>
<?php endif; ?>
<?php if ($show_credits) : ?>
<li class="<?php echo $active_record === 'points' ? 'active' : ''; ?>">
<a href="#record_tab_points" data-aksrt-asset-tab="points"><?php esc_html_e('积分记录', 'aksrt'); ?></a>
</li>
<?php endif; ?>
</ul>
<div class="uc-asset-tabs__content tab-content">
<?php if ($show_wallet) : ?>
<div class="tab-pane<?php echo $active_record === 'balance' ? ' is-active' : ''; ?>" id="record_tab_balance" data-aksrt-asset-pane="balance"<?php echo $active_record === 'balance' ? '' : ' hidden'; ?>>
<?php
$skip_wallet_ledger = false;
// Render only the ledger portion by reusing balance log markup.
$user_id = (int) ($user_id ?? 0);
$wallet_page = max(1, isset($_GET['wallet_page']) ? (int) $_GET['wallet_page'] : 1);
$wlog = class_exists('AKSRT_Balance')
? AKSRT_Balance::get_log(['user_id' => $user_id, 'page' => $wallet_page, 'per_page' => 10])
: ['items' => [], 'total' => 0];
?>
<div class="uc-transactions uc-transactions--wallet">
<?php if (!empty($wlog['items'])) : ?>
<div class="uc-transactions__list">
<?php foreach ($wlog['items'] as $row) :
$delta = (float) $row['delta'];
$delta_class = $delta >= 0 ? 'is-positive' : 'is-negative';
$delta_txt = ($delta > 0 ? '+' : '') . (class_exists('AKSRT_Balance') ? AKSRT_Balance::format($delta) : number_format_i18n($delta, 2));
?>
<div class="uc-transactions__item">
<div class="uc-transactions__icon <?php echo esc_attr($delta_class); ?>">
<i class="fas fa-<?php echo $delta >= 0 ? 'arrow-up' : 'arrow-down'; ?>" aria-hidden="true"></i>
</div>
<div class="uc-transactions__info">
<div class="uc-transactions__reason">
<?php echo esc_html(class_exists('AKSRT_Balance') ? AKSRT_Balance::reason_label((string) $row['reason']) : (string) $row['reason']); ?>
<?php if (!empty($row['remark'])) : ?>
<span class="uc-transactions__remark">(<?php echo esc_html((string) $row['remark']); ?>)</span>
<?php endif; ?>
</div>
<div class="uc-transactions__time"><?php echo esc_html(get_date_from_gmt((string) $row['created_at'], 'Y-m-d H:i')); ?></div>
</div>
<div class="uc-transactions__amount">
<span class="uc-transactions__delta <?php echo esc_attr($delta_class); ?>"><?php echo esc_html($delta_txt); ?></span>
</div>
</div>
<?php endforeach; ?>
</div>
<?php
$total_pages = (int) ceil((int) ($wlog['total'] ?? 0) / 10);
if ($total_pages > 1 && function_exists('aksrt_render_profile_pagination')) {
aksrt_render_profile_pagination($total_pages, $wallet_page, 'wallet_page');
}
?>
<?php else : ?>
<div class="uc-transactions__empty">
<p><?php esc_html_e('还没有任何余额流水', 'aksrt'); ?></p>
</div>
<?php endif; ?>
</div>
</div>
<?php endif; ?>
<?php if ($show_credits) : ?>
<div class="tab-pane<?php echo $active_record === 'points' ? ' is-active' : ''; ?>" id="record_tab_points" data-aksrt-asset-pane="points"<?php echo $active_record === 'points' ? '' : ' hidden'; ?>>
<?php require $dir . '/credits-transactions.php'; ?>
</div>
<?php endif; ?>
</div>
</div>
<?php
endif;
?>