Merge pull request 'feat(users): normalize center routes and withdrawal limits' (#4) from worktree/fix-user-balance into main
Reviewed-on: #4
This commit is contained in:
@@ -313,6 +313,7 @@ export const pageDisplaySchemas = {
|
|||||||
user_home_tabs_mobile_hide: '',
|
user_home_tabs_mobile_hide: '',
|
||||||
user_home_sidebar: false,
|
user_home_sidebar: false,
|
||||||
user_home_cover: true,
|
user_home_cover: true,
|
||||||
|
user_home_route_identifier: 'nicename',
|
||||||
user_home_follow: true,
|
user_home_follow: true,
|
||||||
user_home_posts_per_page: 10,
|
user_home_posts_per_page: 10,
|
||||||
user_home_avatar: true,
|
user_home_avatar: true,
|
||||||
@@ -332,6 +333,7 @@ export const pageDisplaySchemas = {
|
|||||||
icon: 'fas fa-th-large',
|
icon: 'fas fa-th-large',
|
||||||
fields: [
|
fields: [
|
||||||
{ key: 'user_home_cover', label: '封面区域', type: 'switch', default: true, help: '用户主页顶部封面图' },
|
{ key: 'user_home_cover', label: '封面区域', type: 'switch', default: true, help: '用户主页顶部封面图' },
|
||||||
|
{ key: 'user_home_route_identifier', label: '主页 URL 标识', type: 'select', default: 'nicename', options: [{ label: '用户名 URL slug', value: 'nicename' }, { label: '数字用户 ID', value: 'uid' }] },
|
||||||
{ key: 'user_home_sidebar', label: '侧边栏(已弃用)', type: 'switch', default: false, help: '公开主页为通栏布局(对齐 Zibll author);运行时强制关闭。交易服务请去用户中心。' },
|
{ key: 'user_home_sidebar', label: '侧边栏(已弃用)', type: 'switch', default: false, help: '公开主页为通栏布局(对齐 Zibll author);运行时强制关闭。交易服务请去用户中心。' },
|
||||||
{ key: 'user_home_follow', label: '关注按钮', type: 'switch', default: true },
|
{ key: 'user_home_follow', label: '关注按钮', type: 'switch', default: true },
|
||||||
{ key: 'user_home_posts_per_page', label: '每页文章数', type: 'number', default: 10, min: 5, max: 50 },
|
{ key: 'user_home_posts_per_page', label: '每页文章数', type: 'number', default: 10, min: 5, max: 50 },
|
||||||
|
|||||||
@@ -24,7 +24,6 @@ export const rechargeSchema = {
|
|||||||
pay_balance_exchange_rate: 100,
|
pay_balance_exchange_rate: 100,
|
||||||
pay_balance_exchange_desc: '',
|
pay_balance_exchange_desc: '',
|
||||||
pay_balance_withdraw_s: false,
|
pay_balance_withdraw_s: false,
|
||||||
pay_balance_withdraw_lowest_money: 50,
|
|
||||||
},
|
},
|
||||||
groups: [
|
groups: [
|
||||||
{
|
{
|
||||||
@@ -94,7 +93,6 @@ export const rechargeSchema = {
|
|||||||
icon: 'fas fa-hand-holding-usd',
|
icon: 'fas fa-hand-holding-usd',
|
||||||
fields: [
|
fields: [
|
||||||
{ key: 'pay_balance_withdraw_s', label: '启用余额提现', type: 'switch', default: false, dependsOn: { key: 'pay_balance_s', value: true } },
|
{ key: 'pay_balance_withdraw_s', label: '启用余额提现', type: 'switch', default: false, dependsOn: { key: 'pay_balance_s', value: true } },
|
||||||
{ key: 'pay_balance_withdraw_lowest_money', label: '最低提现金额', type: 'number', default: 50, min: 1, max: 99999, dependsOn: { key: 'pay_balance_withdraw_s', value: true } },
|
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
|
|||||||
@@ -2,11 +2,13 @@ export const withdrawalSchema = {
|
|||||||
key: 'payment.withdrawal',
|
key: 'payment.withdrawal',
|
||||||
title: '提现管理',
|
title: '提现管理',
|
||||||
icon: 'fas fa-hand-holding-usd',
|
icon: 'fas fa-hand-holding-usd',
|
||||||
description: '配置提现门槛、手续费比例和提现说明。',
|
description: '配置提现门槛、手续费比例与每日限制。',
|
||||||
defaults: {
|
defaults: {
|
||||||
pay_rebate_withdraw_lowest_money: 50,
|
pay_rebate_withdraw_lowest_money: 50,
|
||||||
withdraw_service_charge: 0,
|
withdraw_service_charge: 5,
|
||||||
pay_rebate_withdraw_text_details: '<div>可提现金额达到50元后即可发起提现</div>\n<div>申请提现需后台人工处理,一般2-3小时,请耐心等待</div>\n<div>如有其它疑问,请与客服联系</div>',
|
withdraw_daily_count: 3,
|
||||||
|
withdraw_daily_limit: 50,
|
||||||
|
withdraw_reset_time: '00:00',
|
||||||
},
|
},
|
||||||
groups: [
|
groups: [
|
||||||
{
|
{
|
||||||
@@ -14,9 +16,11 @@ export const withdrawalSchema = {
|
|||||||
title: '提现设置',
|
title: '提现设置',
|
||||||
icon: 'fas fa-sliders-h',
|
icon: 'fas fa-sliders-h',
|
||||||
fields: [
|
fields: [
|
||||||
{ key: 'pay_rebate_withdraw_lowest_money', label: '最低提现金额', type: 'number', default: 50, min: 1, max: 99999 },
|
{ key: 'pay_rebate_withdraw_lowest_money', label: '最低提现金额', type: 'number', default: 50, min: 0, max: 99999 },
|
||||||
{ key: 'withdraw_service_charge', label: '提现手续费(%)', type: 'number', default: 0, min: 0, max: 50 },
|
{ key: 'withdraw_service_charge', label: '提现手续费(%)', type: 'number', default: 5, min: 0, max: 50 },
|
||||||
{ key: 'pay_rebate_withdraw_text_details', label: '提现说明', type: 'textarea', default: '<div>可提现金额达到50元后即可发起提现</div>\n<div>申请提现需后台人工处理,一般2-3小时,请耐心等待</div>\n<div>如有其它疑问,请与客服联系</div>' },
|
{ key: 'withdraw_daily_count', label: '每日可提现次数', type: 'number', default: 3, min: 0, max: 99999 },
|
||||||
|
{ key: 'withdraw_daily_limit', label: '每日提现上限', type: 'number', default: 50, min: 0, max: 999999 },
|
||||||
|
{ key: 'withdraw_reset_time', label: '重置时间', type: 'input', default: '00:00', help: '按站点 WordPress 时区重置,格式 HH:MM。' },
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
|
|||||||
+33
-5
@@ -930,11 +930,27 @@
|
|||||||
var amountEl = form.querySelector('[data-aksrt-withdraw-amount]');
|
var amountEl = form.querySelector('[data-aksrt-withdraw-amount]');
|
||||||
var methodEl = form.querySelector('[data-aksrt-withdraw-method]');
|
var methodEl = form.querySelector('[data-aksrt-withdraw-method]');
|
||||||
var accountEl = form.querySelector('[data-aksrt-withdraw-account]');
|
var accountEl = form.querySelector('[data-aksrt-withdraw-account]');
|
||||||
var amount = amountEl ? parseFloat(amountEl.value || '0') : 0;
|
var rawAmount = amountEl ? String(amountEl.value || '').trim() : '';
|
||||||
|
var amount = rawAmount ? Number(rawAmount) : 0;
|
||||||
|
var minAmount = amountEl ? Number(amountEl.getAttribute('data-withdraw-min') || '0') : 0;
|
||||||
|
var remainingAmount = amountEl ? Number(amountEl.getAttribute('data-withdraw-remaining-amount') || '0') : 0;
|
||||||
|
var remainingCount = amountEl ? Number(amountEl.getAttribute('data-withdraw-remaining-count') || '0') : 0;
|
||||||
var method = methodEl ? methodEl.value : 'alipay';
|
var method = methodEl ? methodEl.value : 'alipay';
|
||||||
var account = accountEl ? String(accountEl.value || '').trim() : '';
|
var account = accountEl ? String(accountEl.value || '').trim() : '';
|
||||||
if (!(amount > 0)) {
|
if (!Number.isInteger(amount) || !(amount > 0)) {
|
||||||
setTip('请输入提现金额', true);
|
setTip('提现金额必须为正整数', true);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (amount < minAmount) {
|
||||||
|
setTip('提现金额低于最低提现门槛', true);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (remainingCount <= 0) {
|
||||||
|
setTip('当日提现次数已用尽', true);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (amount > remainingAmount) {
|
||||||
|
setTip('本次提现后将超出单日提现总额上限', true);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (!account) {
|
if (!account) {
|
||||||
@@ -953,8 +969,20 @@
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
var data = (r.json && r.json.data) ? r.json.data : r.json;
|
var data = (r.json && r.json.data) ? r.json.data : r.json;
|
||||||
setTip((data && data.message) || '申请已提交,正在刷新…');
|
setTip((data && data.message) || '申请已提交');
|
||||||
window.location.reload();
|
closeModal(modal);
|
||||||
|
var panel = modal.closest ? modal.closest('.dashboard-panel') : null;
|
||||||
|
panel = panel || document.getElementById('panel-credits');
|
||||||
|
var rest = (window.aksrtData && (window.aksrtData.restUrl || window.aksrtData.apiUrl)) || (window.aksrtUserCenter && window.aksrtUserCenter.restUrl) || '';
|
||||||
|
if (panel && rest) {
|
||||||
|
fetch(rest.replace(/\/$/, '') + '/user-center/panel/credits', { credentials: 'same-origin', headers: { 'X-WP-Nonce': (window.aksrtData && (window.aksrtData.restNonce || window.aksrtData.nonce)) || '' } })
|
||||||
|
.then(function (response) { return response.json(); })
|
||||||
|
.then(function (payload) {
|
||||||
|
if (!payload || !payload.success || !payload.data || typeof payload.data.html !== 'string') return;
|
||||||
|
panel.innerHTML = payload.data.html;
|
||||||
|
if (window.AksrtFrontend && typeof window.AksrtFrontend.rehydrate === 'function') window.AksrtFrontend.rehydrate(panel, 'ajax');
|
||||||
|
});
|
||||||
|
}
|
||||||
}).catch(function () {
|
}).catch(function () {
|
||||||
busy = false;
|
busy = false;
|
||||||
setTip('网络错误,请重试', true);
|
setTip('网络错误,请重试', true);
|
||||||
|
|||||||
@@ -124,16 +124,17 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
function tabFromUrl() {
|
function tabFromUrl() {
|
||||||
var m = window.location.search.match(/[?&]tab=([^&]+)/);
|
var pathParts = window.location.pathname.replace(/\/+$/, '').split('/');
|
||||||
var name = m ? decodeURIComponent(m[1]) : (cfg.activeTab || defaultTab);
|
var pathTab = pathParts.length > 2 && pathParts[pathParts.length - 2] === 'user' ? pathParts[pathParts.length - 1] : '';
|
||||||
|
var name = pathTab || (cfg.activeTab || defaultTab);
|
||||||
|
if (name === 'balance') name = 'credits';
|
||||||
return tabAliases[name] || name;
|
return tabAliases[name] || name;
|
||||||
}
|
}
|
||||||
|
|
||||||
function updateUrl(name) {
|
function updateUrl(name) {
|
||||||
if (!window.history.replaceState) return;
|
if (!window.history.replaceState) return;
|
||||||
var u = new URL(window.location);
|
var pathTab = name === 'credits' ? 'balance' : name;
|
||||||
if (name === defaultTab) u.searchParams.delete('tab');
|
var u = new URL('/user/' + pathTab + '/', window.location.origin);
|
||||||
else u.searchParams.set('tab', name);
|
|
||||||
window.history.replaceState({}, '', u);
|
window.history.replaceState({}, '', u);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -106,13 +106,7 @@ if (!function_exists('aksrt_get_user_center_tab_url')) {
|
|||||||
if ('invite' === $tab || 'promotion' === $tab) {
|
if ('invite' === $tab || 'promotion' === $tab) {
|
||||||
$tab = 'revenue';
|
$tab = 'revenue';
|
||||||
}
|
}
|
||||||
$url = remove_query_arg([
|
return aksrt_get_user_center_url($tab);
|
||||||
'tab', 'posts_page', 'comments_page', 'favorites_page', 'credits_page',
|
|
||||||
'post_status', 'creator_page', 'creator_status', 'saved', 'profile_error',
|
|
||||||
'order_tab', 'otab', 'order_status', 'order_page', 'order_type', 'income_currency', 'income_status', 'income_page', 'section',
|
|
||||||
], aksrt_get_user_center_url());
|
|
||||||
|
|
||||||
return add_query_arg('tab', $tab, $url);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -282,7 +276,10 @@ if (!function_exists('aksrt_user_center_resolve_tabs')) {
|
|||||||
$default = in_array('vip', $keys, true)
|
$default = in_array('vip', $keys, true)
|
||||||
? 'vip'
|
? 'vip'
|
||||||
: (in_array('profile', $keys, true) ? 'profile' : ($keys[0] ?? 'profile'));
|
: (in_array('profile', $keys, true) ? 'profile' : ($keys[0] ?? 'profile'));
|
||||||
$active = isset($_GET['tab']) ? sanitize_key(wp_unslash($_GET['tab'])) : $default;
|
$active = (string) get_query_var('aksrt_user_center_tab') ?: $default;
|
||||||
|
if ($active === 'balance') {
|
||||||
|
$active = 'credits';
|
||||||
|
}
|
||||||
if ('signin' === $active || 'assets' === $active) {
|
if ('signin' === $active || 'assets' === $active) {
|
||||||
$active = 'credits';
|
$active = 'credits';
|
||||||
} elseif ('invite' === $active || 'promotion' === $active) {
|
} elseif ('invite' === $active || 'promotion' === $active) {
|
||||||
|
|||||||
@@ -32,13 +32,16 @@ $withdraw_on = !empty($all_settings['pay_balance_withdraw_s']);
|
|||||||
if ($withdraw_on && class_exists('\AKSRT\Settings\Capabilities') && !\AKSRT\Settings\Capabilities::user_can('balance_withdraw', $uid)) {
|
if ($withdraw_on && class_exists('\AKSRT\Settings\Capabilities') && !\AKSRT\Settings\Capabilities::user_can('balance_withdraw', $uid)) {
|
||||||
$withdraw_on = false;
|
$withdraw_on = false;
|
||||||
}
|
}
|
||||||
$withdraw_min = max(1, (float) ($all_settings['pay_balance_withdraw_lowest_money'] ?? $all_settings['pay_rebate_withdraw_lowest_money'] ?? 50));
|
$withdraw_settings = class_exists('\\AKSRT\\Settings\\Settings') ? \AKSRT\Settings\Settings::get_withdrawal_settings() : [];
|
||||||
|
$withdraw_min = max(0, (float) ($withdraw_settings['min_amount'] ?? 50));
|
||||||
|
$withdraw_usage = ['remaining_count' => 0, 'remaining_amount' => 0];
|
||||||
$withdraw_pending_hold = 0.0;
|
$withdraw_pending_hold = 0.0;
|
||||||
$withdraw_history = [];
|
$withdraw_history = [];
|
||||||
if ($withdraw_on && class_exists('\AKSRT\Payment\Withdrawal')) {
|
if ($withdraw_on && class_exists('\AKSRT\Payment\Withdrawal')) {
|
||||||
$wd_svc = \AKSRT\Payment\Withdrawal::get_instance();
|
$wd_svc = \AKSRT\Payment\Withdrawal::get_instance();
|
||||||
$withdraw_pending_hold = $wd_svc->sum_pending_hold((int) ($user_id ?? 0));
|
$withdraw_pending_hold = $wd_svc->sum_pending_hold((int) ($user_id ?? 0));
|
||||||
$withdraw_history = $wd_svc->get_user_withdrawals((int) ($user_id ?? 0), ['limit' => 30]);
|
$withdraw_history = $wd_svc->get_user_withdrawals((int) ($user_id ?? 0), ['limit' => 30]);
|
||||||
|
$withdraw_usage = $wd_svc->daily_usage((int) ($user_id ?? 0), $withdraw_settings);
|
||||||
}
|
}
|
||||||
$withdraw_processing = $withdraw_pending_hold > 0;
|
$withdraw_processing = $withdraw_pending_hold > 0;
|
||||||
$card_pass_balance = ['enabled' => false, 'desc' => '', 'only_password' => true];
|
$card_pass_balance = ['enabled' => false, 'desc' => '', 'only_password' => true];
|
||||||
@@ -66,7 +69,7 @@ $mark = (string) ($charge_view['currency_mark'] ?? '元');
|
|||||||
</div>
|
</div>
|
||||||
<?php if ($withdraw_on) : ?>
|
<?php if ($withdraw_on) : ?>
|
||||||
<button type="button" class="uc-asset-card__head-action muted-color px12" data-aksrt-withdraw-history-open>
|
<button type="button" class="uc-asset-card__head-action muted-color px12" data-aksrt-withdraw-history-open>
|
||||||
<?php echo $withdraw_processing ? esc_html__('提现处理中', 'aksrt') : esc_html__('提现记录', 'aksrt'); ?>
|
<?php esc_html_e('提现记录', 'aksrt'); ?>
|
||||||
<i class="fa fa-angle-right em12 ml3" aria-hidden="true"></i>
|
<i class="fa fa-angle-right em12 ml3" aria-hidden="true"></i>
|
||||||
</button>
|
</button>
|
||||||
<?php endif; ?>
|
<?php endif; ?>
|
||||||
@@ -81,12 +84,8 @@ $mark = (string) ($charge_view['currency_mark'] ?? '元');
|
|||||||
<button type="button" class="but c-blue-2 mr6 hollow" data-aksrt-transfer-open="balance"><?php esc_html_e('转账', 'aksrt'); ?></button>
|
<button type="button" class="but c-blue-2 mr6 hollow" data-aksrt-transfer-open="balance"><?php esc_html_e('转账', 'aksrt'); ?></button>
|
||||||
<?php endif; ?>
|
<?php endif; ?>
|
||||||
<?php if ($withdraw_on) : ?>
|
<?php if ($withdraw_on) : ?>
|
||||||
<?php if ($withdraw_processing) : ?>
|
|
||||||
<button type="button" class="but c-blue-2 mr6 hollow" data-aksrt-withdraw-history-open><?php esc_html_e('提现处理中', 'aksrt'); ?></button>
|
|
||||||
<?php else : ?>
|
|
||||||
<button type="button" class="but c-blue-2 mr6 hollow" data-aksrt-withdraw-open><?php esc_html_e('提现', 'aksrt'); ?></button>
|
<button type="button" class="but c-blue-2 mr6 hollow" data-aksrt-withdraw-open><?php esc_html_e('提现', 'aksrt'); ?></button>
|
||||||
<?php endif; ?>
|
<?php endif; ?>
|
||||||
<?php endif; ?>
|
|
||||||
<button type="button" class="but b-blue-2" data-aksrt-charge-open><?php esc_html_e('充值', 'aksrt'); ?></button>
|
<button type="button" class="but b-blue-2" data-aksrt-charge-open><?php esc_html_e('充值', 'aksrt'); ?></button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -100,9 +99,8 @@ $mark = (string) ($charge_view['currency_mark'] ?? '元');
|
|||||||
</button>
|
</button>
|
||||||
<?php endif; ?>
|
<?php endif; ?>
|
||||||
<?php if ($withdraw_on) : ?>
|
<?php if ($withdraw_on) : ?>
|
||||||
<div class="uc-asset-card__foot muted-2-color em09 flex jsb ac mt6">
|
<div class="uc-asset-card__foot muted-2-color em09 mt6">
|
||||||
<span><?php printf(esc_html__('处理中冻结 %s', 'aksrt'), esc_html(class_exists('\AKSRT\Credits\Balance') ? \AKSRT\Credits\Balance::format((float) $withdraw_pending_hold) : number_format_i18n((float) $withdraw_pending_hold, 2))); ?></span>
|
<span><?php printf(esc_html__('处理中冻结 %s', 'aksrt'), esc_html(class_exists('\AKSRT\Credits\Balance') ? \AKSRT\Credits\Balance::format((float) $withdraw_pending_hold) : number_format_i18n((float) $withdraw_pending_hold, 2))); ?></span>
|
||||||
<button type="button" class="muted-color" data-aksrt-withdraw-history-open><?php esc_html_e('提现记录', 'aksrt'); ?></button>
|
|
||||||
</div>
|
</div>
|
||||||
<?php endif; ?>
|
<?php endif; ?>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -1,19 +1,14 @@
|
|||||||
<?php
|
<?php
|
||||||
/**
|
/** Shared balance withdrawal modal. @package AKSRT */
|
||||||
* Shared balance withdrawal modal.
|
|
||||||
*
|
|
||||||
* @package AKSRT
|
|
||||||
*/
|
|
||||||
|
|
||||||
defined('ABSPATH') || exit;
|
defined('ABSPATH') || exit;
|
||||||
|
|
||||||
if (!empty($GLOBALS['aksrt_withdraw_modal_rendered'])) {
|
if (!empty($GLOBALS['aksrt_withdraw_modal_rendered'])) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
$GLOBALS['aksrt_withdraw_modal_rendered'] = true;
|
$GLOBALS['aksrt_withdraw_modal_rendered'] = true;
|
||||||
|
$withdraw_min = max(0, (float) ($withdraw_min ?? 50));
|
||||||
$withdraw_min = max(1, (float) ($withdraw_min ?? 50));
|
$withdraw_usage = is_array($withdraw_usage ?? null) ? $withdraw_usage : ['remaining_count' => 0, 'remaining_amount' => 0];
|
||||||
$wallet_fmt = (string) ($wallet_fmt ?? '0.00');
|
$withdraw_fee_rate = (int) ($withdraw_settings['service_charge_rate'] ?? 5);
|
||||||
?>
|
?>
|
||||||
<div class="uc-modal" id="aksrt-withdraw-modal" hidden data-aksrt-withdraw-modal>
|
<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__overlay" data-aksrt-withdraw-close></div>
|
||||||
@@ -22,16 +17,16 @@ $wallet_fmt = (string) ($wallet_fmt ?? '0.00');
|
|||||||
<h3 class="uc-modal__title"><?php esc_html_e('余额提现', 'aksrt'); ?></h3>
|
<h3 class="uc-modal__title"><?php esc_html_e('余额提现', 'aksrt'); ?></h3>
|
||||||
<form data-aksrt-withdraw-form>
|
<form data-aksrt-withdraw-form>
|
||||||
<label class="muted-color em09"><?php esc_html_e('提现金额', 'aksrt'); ?></label>
|
<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>
|
<input type="number" class="uc-card-pass__input" name="amount" min="<?php echo esc_attr((string) $withdraw_min); ?>" max="<?php echo esc_attr((string) ($withdraw_usage['remaining_amount'] ?? 0)); ?>" step="1" inputmode="numeric" data-aksrt-withdraw-amount data-withdraw-min="<?php echo esc_attr((string) $withdraw_min); ?>" data-withdraw-remaining-amount="<?php echo esc_attr((string) ($withdraw_usage['remaining_amount'] ?? 0)); ?>" data-withdraw-remaining-count="<?php echo esc_attr((string) ($withdraw_usage['remaining_count'] ?? 0)); ?>">
|
||||||
<p class="muted-2-color em09 mt6"><?php printf(esc_html__('最低提现 %s 元,当前余额 %s', 'aksrt'), esc_html((string) $withdraw_min), esc_html($wallet_fmt)); ?></p>
|
<p class="muted-2-color em09 mt6"><?php printf(esc_html__('最低提现%s 元,今日可提现%d次,提现手续费率%d%%', 'aksrt'), esc_html((string) $withdraw_min), (int) ($withdraw_usage['remaining_count'] ?? 0), $withdraw_fee_rate); ?></p>
|
||||||
<label class="muted-color em09 mt10"><?php esc_html_e('收款方式', 'aksrt'); ?></label>
|
<label class="muted-color em09 mt10"><?php esc_html_e('收款方式', 'aksrt'); ?></label>
|
||||||
<select class="uc-card-pass__input" name="method" data-aksrt-withdraw-method>
|
<select class="uc-card-pass__input" name="method" data-aksrt-withdraw-method>
|
||||||
<option value="alipay"><?php esc_html_e('支付宝', 'aksrt'); ?></option>
|
<option value="alipay"><?php esc_html_e('支付宝', 'aksrt'); ?></option>
|
||||||
<option value="wechat"><?php esc_html_e('微信', 'aksrt'); ?></option>
|
<option value="wechat"><?php esc_html_e('微信', 'aksrt'); ?></option>
|
||||||
<option value="bank"><?php esc_html_e('银行卡', 'aksrt'); ?></option>
|
<option value="bank"><?php esc_html_e('银行卡', 'aksrt'); ?></option>
|
||||||
</select>
|
</select>
|
||||||
<label class="muted-color em09 mt10"><?php esc_html_e('收款账号信息', 'aksrt'); ?></label>
|
<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>
|
<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>
|
<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>
|
<button type="submit" class="but jb-blue btn-block" data-aksrt-withdraw-submit><?php esc_html_e('提交申请', 'aksrt'); ?></button>
|
||||||
</form>
|
</form>
|
||||||
|
|||||||
@@ -14,7 +14,8 @@ defined('ABSPATH') || exit;
|
|||||||
// --- 3a. /user/{昵称} 自定义重写 ---
|
// --- 3a. /user/{昵称} 自定义重写 ---
|
||||||
add_filter('query_vars', function ($vars) {
|
add_filter('query_vars', function ($vars) {
|
||||||
$vars[] = 'aksrt_user_center';
|
$vars[] = 'aksrt_user_center';
|
||||||
$vars[] = 'user_nicename';
|
$vars[] = 'aksrt_user_center_tab';
|
||||||
|
$vars[] = 'aksrt_author_identifier';
|
||||||
$vars[] = 'aksrt_orders';
|
$vars[] = 'aksrt_orders';
|
||||||
$vars[] = 'aksrt_revenue';
|
$vars[] = 'aksrt_revenue';
|
||||||
return $vars;
|
return $vars;
|
||||||
@@ -24,6 +25,14 @@ add_action('template_redirect', function () {
|
|||||||
if (is_admin() || wp_doing_ajax() || is_feed() || is_embed() || is_preview()) {
|
if (is_admin() || wp_doing_ajax() || is_feed() || is_embed() || is_preview()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
$request_path = trim((string) parse_url($_SERVER['REQUEST_URI'] ?? '', PHP_URL_PATH), '/');
|
||||||
|
if ($request_path === 'profile' || str_starts_with($request_path, 'profile/')) {
|
||||||
|
global $wp_query;
|
||||||
|
if ($wp_query instanceof WP_Query) { $wp_query->set_404(); }
|
||||||
|
status_header(404);
|
||||||
|
nocache_headers();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
$resolved_path = aksrt_get_current_special_template_path();
|
$resolved_path = aksrt_get_current_special_template_path();
|
||||||
if ($resolved_path === '') {
|
if ($resolved_path === '') {
|
||||||
@@ -35,13 +44,36 @@ add_action('template_redirect', function () {
|
|||||||
exit;
|
exit;
|
||||||
}, 100);
|
}, 100);
|
||||||
|
|
||||||
|
// /user/{tab} only accepts registered user-center tabs; former public /user/{slug}
|
||||||
|
// addresses must not silently resolve to the user-center home.
|
||||||
|
add_action('template_redirect', function () {
|
||||||
|
if (is_admin() || wp_doing_ajax()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
$tab = (string) get_query_var('aksrt_user_center_tab');
|
||||||
|
if ($tab === '') {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
$tab = $tab === 'balance' ? 'credits' : sanitize_key($tab);
|
||||||
|
$allowed = class_exists('\\AKSRT\\User\\Center')
|
||||||
|
? array_keys(\AKSRT\User\Center::get_instance()->get_dashboard_tabs())
|
||||||
|
: ['profile', 'level', 'creator', 'comments', 'favorites', 'credits', 'vip', 'orders', 'auth', 'revenue', 'settings'];
|
||||||
|
if (!in_array($tab, $allowed, true)) {
|
||||||
|
global $wp_query;
|
||||||
|
if ($wp_query instanceof WP_Query) { $wp_query->set_404(); }
|
||||||
|
status_header(404);
|
||||||
|
nocache_headers();
|
||||||
|
}
|
||||||
|
}, 1);
|
||||||
|
|
||||||
add_action('init', function () {
|
add_action('init', function () {
|
||||||
add_rewrite_tag('%aksrt_user_center%', '([^&]+)');
|
add_rewrite_tag('%aksrt_user_center%', '([^&]+)');
|
||||||
add_rewrite_tag('%user_nicename%', '([^/]+)');
|
add_rewrite_tag('%aksrt_user_center_tab%', '([^/]+)');
|
||||||
|
add_rewrite_tag('%aksrt_author_identifier%', '([^/]+)');
|
||||||
add_rewrite_tag('%aksrt_orders%', '([0-1])');
|
add_rewrite_tag('%aksrt_orders%', '([0-1])');
|
||||||
add_rewrite_tag('%aksrt_revenue%', '([0-1])');
|
add_rewrite_tag('%aksrt_revenue%', '([0-1])');
|
||||||
add_rewrite_rule('^profile/?$', 'index.php?aksrt_user_center=1', 'top');
|
add_rewrite_rule('^user/([a-z0-9_-]+)/?$', 'index.php?aksrt_user_center=1&aksrt_user_center_tab=$matches[1]', 'top');
|
||||||
add_rewrite_rule('^user/([^/]+)/?$', 'index.php?user_nicename=$matches[1]', 'top');
|
add_rewrite_rule('^author/([^/]+)/?$', 'index.php?aksrt_author_identifier=$matches[1]', 'top');
|
||||||
// Commerce center aliases (work even before WP page is assigned).
|
// Commerce center aliases (work even before WP page is assigned).
|
||||||
add_rewrite_rule('^orders/?$', 'index.php?aksrt_orders=1', 'top');
|
add_rewrite_rule('^orders/?$', 'index.php?aksrt_orders=1', 'top');
|
||||||
add_rewrite_rule('^revenue/?$', 'index.php?aksrt_revenue=1', 'top');
|
add_rewrite_rule('^revenue/?$', 'index.php?aksrt_revenue=1', 'top');
|
||||||
@@ -56,9 +88,9 @@ add_filter('template_include', function ($template) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// A) /user/{昵称} even if special-path helper returned empty (user missing → 404).
|
// A) /user/{昵称} even if special-path helper returned empty (user missing → 404).
|
||||||
$nicename = (string) get_query_var('user_nicename');
|
$identifier = (string) get_query_var('aksrt_author_identifier');
|
||||||
if ($nicename !== '') {
|
if ($identifier !== '') {
|
||||||
$user = get_user_by('slug', $nicename);
|
$user = function_exists('aksrt_resolve_public_user') ? aksrt_resolve_public_user($identifier) : null;
|
||||||
if ($user instanceof WP_User) {
|
if ($user instanceof WP_User) {
|
||||||
$tpl = aksrt_locate_template_file('templates/pages/page-user-home.php');
|
$tpl = aksrt_locate_template_file('templates/pages/page-user-home.php');
|
||||||
if ($tpl) {
|
if ($tpl) {
|
||||||
@@ -78,18 +110,12 @@ add_filter('template_include', function ($template) {
|
|||||||
return $four ?: $template;
|
return $four ?: $template;
|
||||||
}
|
}
|
||||||
|
|
||||||
// B) profile slug page without rewrite var (permalink edge).
|
// B) ordinary assigned page templates.
|
||||||
if (is_page()) {
|
if (is_page()) {
|
||||||
$page_id = get_queried_object_id();
|
$page_id = get_queried_object_id();
|
||||||
if ($page_id) {
|
if ($page_id) {
|
||||||
$slug = (string) get_post_field('post_name', $page_id);
|
$slug = (string) get_post_field('post_name', $page_id);
|
||||||
$page_template = get_post_meta($page_id, '_wp_page_template', true);
|
$page_template = get_post_meta($page_id, '_wp_page_template', true);
|
||||||
if ($slug === 'profile' || $page_template === 'templates/pages/page-profile.php') {
|
|
||||||
$tpl = aksrt_locate_template_file('templates/pages/page-profile.php');
|
|
||||||
if ($tpl) {
|
|
||||||
return $tpl;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (is_string($page_template) && strpos($page_template, 'templates/pages/') === 0) {
|
if (is_string($page_template) && strpos($page_template, 'templates/pages/') === 0) {
|
||||||
$tpl = aksrt_locate_template_file($page_template);
|
$tpl = aksrt_locate_template_file($page_template);
|
||||||
if ($tpl) {
|
if ($tpl) {
|
||||||
@@ -157,13 +183,8 @@ add_action('init', function () {
|
|||||||
aksrt_ensure_template_page($requested_template_key);
|
aksrt_ensure_template_page($requested_template_key);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Always re-assert profile page template (blank WP page with slug profile is common).
|
|
||||||
if (function_exists('aksrt_ensure_template_page')) {
|
|
||||||
aksrt_ensure_template_page('profile');
|
|
||||||
}
|
|
||||||
|
|
||||||
// 统一 flush 检查 — bump when user-home rewrite / author override changes
|
// 统一 flush 检查 — bump when user-home rewrite / author override changes
|
||||||
$rewrite_version = AKSRT_VERSION . ':user-home-profile-routing-v5';
|
$rewrite_version = AKSRT_VERSION . ':user-center-author-routing-v6';
|
||||||
$flushed = get_option('aksrt_user_rules_flushed');
|
$flushed = get_option('aksrt_user_rules_flushed');
|
||||||
$rules = get_option('rewrite_rules');
|
$rules = get_option('rewrite_rules');
|
||||||
$need_flush = ($flushed !== $rewrite_version)
|
$need_flush = ($flushed !== $rewrite_version)
|
||||||
@@ -199,15 +220,15 @@ add_filter('author_link', function ($link, $author_id) {
|
|||||||
$user = get_userdata($author_id);
|
$user = get_userdata($author_id);
|
||||||
$slug = $user ? $user->user_nicename : ('user-' . max(0, $author_id));
|
$slug = $user ? $user->user_nicename : ('user-' . max(0, $author_id));
|
||||||
if (!get_option('permalink_structure')) {
|
if (!get_option('permalink_structure')) {
|
||||||
return add_query_arg('user_nicename', rawurlencode($slug), home_url('/'));
|
return add_query_arg('aksrt_author', rawurlencode($slug), home_url('/'));
|
||||||
}
|
}
|
||||||
return home_url('/user/' . trailingslashit($slug));
|
return home_url('/author/' . trailingslashit($slug));
|
||||||
}, 99, 2);
|
}, 99, 2);
|
||||||
|
|
||||||
// Accept ?user_nicename= when pretty rewrites are unavailable.
|
// Accept ?user_nicename= when pretty rewrites are unavailable.
|
||||||
add_filter('request', function ($query_vars) {
|
add_filter('request', function ($query_vars) {
|
||||||
if (empty($query_vars['user_nicename']) && !empty($_GET['user_nicename'])) {
|
if (empty($query_vars['aksrt_author_identifier']) && !empty($_GET['aksrt_author'])) {
|
||||||
$query_vars['user_nicename'] = sanitize_title(wp_unslash((string) $_GET['user_nicename']));
|
$query_vars['aksrt_author_identifier'] = sanitize_text_field(wp_unslash((string) $_GET['aksrt_author']));
|
||||||
}
|
}
|
||||||
if (empty($query_vars['aksrt_user_center']) && isset($_GET['aksrt_user_center'])) {
|
if (empty($query_vars['aksrt_user_center']) && isset($_GET['aksrt_user_center'])) {
|
||||||
$query_vars['aksrt_user_center'] = '1';
|
$query_vars['aksrt_user_center'] = '1';
|
||||||
@@ -248,7 +269,7 @@ add_action('template_redirect', function () {
|
|||||||
if (empty($_GET['author'])) {
|
if (empty($_GET['author'])) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (get_query_var('user_nicename') || get_query_var('aksrt_user_center')) {
|
if (get_query_var('aksrt_author_identifier') || get_query_var('aksrt_user_center')) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (is_author()) {
|
if (is_author()) {
|
||||||
|
|||||||
@@ -369,12 +369,12 @@ function aksrt_clear_template_page_cache(): void {
|
|||||||
function aksrt_get_user_home_template_path(): string {
|
function aksrt_get_user_home_template_path(): string {
|
||||||
global $_aksrt_special_templates, $aksrt_profile_user;
|
global $_aksrt_special_templates, $aksrt_profile_user;
|
||||||
|
|
||||||
$nicename = (string) get_query_var('user_nicename');
|
$identifier = (string) get_query_var('aksrt_author_identifier');
|
||||||
if ($nicename === '') {
|
if ($identifier === '') {
|
||||||
return '';
|
return '';
|
||||||
}
|
}
|
||||||
|
|
||||||
$user = get_user_by('slug', $nicename);
|
$user = function_exists('aksrt_resolve_public_user') ? aksrt_resolve_public_user($identifier) : null;
|
||||||
if (!$user instanceof WP_User) {
|
if (!$user instanceof WP_User) {
|
||||||
return '';
|
return '';
|
||||||
}
|
}
|
||||||
@@ -393,7 +393,7 @@ function aksrt_get_user_home_template_path(): string {
|
|||||||
* Resolve the active special-page template path for the current request.
|
* Resolve the active special-page template path for the current request.
|
||||||
*/
|
*/
|
||||||
function aksrt_get_current_special_template_path(): string {
|
function aksrt_get_current_special_template_path(): string {
|
||||||
if (get_query_var('user_nicename')) {
|
if (get_query_var('aksrt_author_identifier')) {
|
||||||
return aksrt_get_user_home_template_path();
|
return aksrt_get_user_home_template_path();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -19,28 +19,13 @@ if (!function_exists('aksrt_get_user_center_url')) :
|
|||||||
* @return string
|
* @return string
|
||||||
*/
|
*/
|
||||||
function aksrt_get_user_center_url(string $tab = ''): string {
|
function aksrt_get_user_center_url(string $tab = ''): string {
|
||||||
if (get_option('permalink_structure')) {
|
$tab = sanitize_key($tab ?: 'profile');
|
||||||
$url = home_url('/profile/');
|
$aliases = ['assets' => 'credits', 'signin' => 'credits', 'invite' => 'revenue', 'promotion' => 'revenue'];
|
||||||
} else {
|
$tab = $aliases[$tab] ?? $tab;
|
||||||
$url = add_query_arg('aksrt_user_center', '1', home_url('/'));
|
$path_tab = $tab === 'credits' ? 'balance' : $tab;
|
||||||
}
|
return get_option('permalink_structure')
|
||||||
|
? home_url('/user/' . trailingslashit($path_tab))
|
||||||
if ($tab !== '') {
|
: add_query_arg(['aksrt_user_center' => '1', 'aksrt_user_center_tab' => $tab], home_url('/'));
|
||||||
$tab = sanitize_key($tab);
|
|
||||||
// Deep-link aliases used by header wallet / VIP CTA.
|
|
||||||
$aliases = [
|
|
||||||
'assets' => 'credits',
|
|
||||||
'vip' => 'vip', // dedicated vip tab when registered; resolver falls back if missing
|
|
||||||
'signin' => 'credits',
|
|
||||||
'invite' => 'revenue',
|
|
||||||
'promotion' => 'revenue',
|
|
||||||
];
|
|
||||||
if (isset($aliases[$tab])) {
|
|
||||||
$tab = $aliases[$tab];
|
|
||||||
}
|
|
||||||
$url = add_query_arg('tab', $tab, $url);
|
|
||||||
}
|
|
||||||
return $url;
|
|
||||||
}
|
}
|
||||||
endif;
|
endif;
|
||||||
|
|
||||||
@@ -81,7 +66,7 @@ function aksrt_get_orders_page_url(): string {
|
|||||||
if (function_exists('aksrt_get_user_center_url')) {
|
if (function_exists('aksrt_get_user_center_url')) {
|
||||||
return aksrt_get_user_center_url('orders');
|
return aksrt_get_user_center_url('orders');
|
||||||
}
|
}
|
||||||
return home_url('/profile/?tab=orders');
|
return home_url('/user/orders/');
|
||||||
}
|
}
|
||||||
endif;
|
endif;
|
||||||
|
|
||||||
@@ -96,7 +81,7 @@ function aksrt_get_revenue_page_url(): string {
|
|||||||
if (function_exists('aksrt_get_user_center_url')) {
|
if (function_exists('aksrt_get_user_center_url')) {
|
||||||
return aksrt_get_user_center_url('revenue');
|
return aksrt_get_user_center_url('revenue');
|
||||||
}
|
}
|
||||||
return home_url('/profile/?tab=revenue');
|
return home_url('/user/revenue/');
|
||||||
}
|
}
|
||||||
endif;
|
endif;
|
||||||
|
|
||||||
@@ -122,29 +107,37 @@ if (!function_exists('aksrt_get_user_home_url')) :
|
|||||||
* @return string
|
* @return string
|
||||||
*/
|
*/
|
||||||
function aksrt_get_user_home_url($user = null): string {
|
function aksrt_get_user_home_url($user = null): string {
|
||||||
$slug = '';
|
$target = null;
|
||||||
|
$mode = class_exists('\\AKSRT\\Settings\\Settings') ? (string) (\AKSRT\Settings\Settings::get_user_home_settings()['route_identifier'] ?? 'nicename') : 'nicename';
|
||||||
if ($user instanceof WP_User) {
|
if ($user instanceof WP_User) {
|
||||||
$slug = $user->user_nicename;
|
$target = $user;
|
||||||
} elseif (is_int($user) && $user > 0) {
|
} elseif (is_int($user) && $user > 0) {
|
||||||
$u = get_userdata($user);
|
$target = get_userdata($user);
|
||||||
$slug = $u ? $u->user_nicename : '';
|
|
||||||
} elseif (is_numeric($user) && (int) $user > 0) {
|
} elseif (is_numeric($user) && (int) $user > 0) {
|
||||||
$u = get_userdata((int) $user);
|
$target = get_userdata((int) $user);
|
||||||
$slug = $u ? $u->user_nicename : '';
|
|
||||||
} elseif (is_string($user) && $user) {
|
} elseif (is_string($user) && $user) {
|
||||||
$slug = $user;
|
$target = get_user_by('slug', $user);
|
||||||
} else {
|
} else {
|
||||||
$current = wp_get_current_user();
|
$target = wp_get_current_user();
|
||||||
$slug = $current->ID ? $current->user_nicename : '';
|
|
||||||
}
|
}
|
||||||
if (!$slug) {
|
if (!$target instanceof WP_User || !$target->ID) {
|
||||||
return home_url('/');
|
return home_url('/');
|
||||||
}
|
}
|
||||||
// Plain permalinks: Apache 404s pretty /user/{slug}/ before WP boots.
|
$identifier = $mode === 'uid' ? (string) $target->ID : $target->user_nicename;
|
||||||
if (!get_option('permalink_structure')) {
|
if (!get_option('permalink_structure')) {
|
||||||
return add_query_arg('user_nicename', rawurlencode($slug), home_url('/'));
|
return add_query_arg('aksrt_author', rawurlencode($identifier), home_url('/'));
|
||||||
}
|
}
|
||||||
return home_url('/user/' . trailingslashit($slug));
|
return home_url('/author/' . trailingslashit($identifier));
|
||||||
|
}
|
||||||
|
endif;
|
||||||
|
|
||||||
|
if (!function_exists('aksrt_resolve_public_user')) :
|
||||||
|
function aksrt_resolve_public_user(string $identifier): ?WP_User {
|
||||||
|
$mode = class_exists('\\AKSRT\\Settings\\Settings') ? (string) (\AKSRT\Settings\Settings::get_user_home_settings()['route_identifier'] ?? 'nicename') : 'nicename';
|
||||||
|
if ($mode === 'uid') {
|
||||||
|
return ctype_digit($identifier) && (int) $identifier > 0 ? get_userdata((int) $identifier) ?: null : null;
|
||||||
|
}
|
||||||
|
return get_user_by('slug', sanitize_title($identifier)) ?: null;
|
||||||
}
|
}
|
||||||
endif;
|
endif;
|
||||||
|
|
||||||
@@ -154,7 +147,7 @@ if (!function_exists('aksrt_is_user_center_page')) :
|
|||||||
*/
|
*/
|
||||||
function aksrt_is_user_center_page(): bool {
|
function aksrt_is_user_center_page(): bool {
|
||||||
return (bool) get_query_var('aksrt_user_center')
|
return (bool) get_query_var('aksrt_user_center')
|
||||||
|| (is_page_template('templates/pages/page-profile.php') && !(bool) get_query_var('user_nicename'));
|
|| (is_page_template('templates/pages/page-profile.php') && !(bool) get_query_var('aksrt_author_identifier'));
|
||||||
}
|
}
|
||||||
endif;
|
endif;
|
||||||
|
|
||||||
@@ -163,7 +156,7 @@ if (!function_exists('aksrt_is_user_home_page')) :
|
|||||||
* 判断当前是否为公开用户主页
|
* 判断当前是否为公开用户主页
|
||||||
*/
|
*/
|
||||||
function aksrt_is_user_home_page(): bool {
|
function aksrt_is_user_home_page(): bool {
|
||||||
return (bool) get_query_var('user_nicename')
|
return (bool) get_query_var('aksrt_author_identifier')
|
||||||
|| is_page_template('templates/pages/page-user-home.php');
|
|| is_page_template('templates/pages/page-user-home.php');
|
||||||
}
|
}
|
||||||
endif;
|
endif;
|
||||||
|
|||||||
@@ -245,6 +245,7 @@ class SchemaPageDisplay {
|
|||||||
'option_key' => 'aksrt_settings',
|
'option_key' => 'aksrt_settings',
|
||||||
'groups' => [
|
'groups' => [
|
||||||
['key' => 'layout', 'title' => '主页布局', 'fields' => [
|
['key' => 'layout', 'title' => '主页布局', 'fields' => [
|
||||||
|
['key' => 'user_home_route_identifier', 'label' => '主页 URL 标识', 'type' => 'select', 'default' => 'nicename', 'options' => [['label' => '用户名 URL slug', 'value' => 'nicename'], ['label' => '数字用户 ID', 'value' => 'uid']]],
|
||||||
['key' => 'user_home_cover', 'label' => '封面区域', 'type' => 'switch', 'default' => true],
|
['key' => 'user_home_cover', 'label' => '封面区域', 'type' => 'switch', 'default' => true],
|
||||||
['key' => 'user_home_sidebar', 'label' => '侧边栏(已弃用)', 'type' => 'switch', 'default' => false, 'help' => '公开主页对齐 Zibll 作者页为通栏布局;交易/快捷入口仅在用户中心展示。此开关保留兼容,运行时强制关闭。'],
|
['key' => 'user_home_sidebar', 'label' => '侧边栏(已弃用)', 'type' => 'switch', 'default' => false, 'help' => '公开主页对齐 Zibll 作者页为通栏布局;交易/快捷入口仅在用户中心展示。此开关保留兼容,运行时强制关闭。'],
|
||||||
['key' => 'user_home_follow', 'label' => '关注按钮', 'type' => 'switch', 'default' => true],
|
['key' => 'user_home_follow', 'label' => '关注按钮', 'type' => 'switch', 'default' => true],
|
||||||
|
|||||||
@@ -97,7 +97,6 @@ class SchemaPayment {
|
|||||||
]],
|
]],
|
||||||
['key' => 'withdraw', 'title' => '余额提现', 'fields' => [
|
['key' => 'withdraw', 'title' => '余额提现', 'fields' => [
|
||||||
['key' => 'pay_balance_withdraw_s', 'label' => '启用余额提现', 'type' => 'switch', 'default' => false, 'dependsOn' => ['key' => 'pay_balance_s', 'value' => true]],
|
['key' => 'pay_balance_withdraw_s', 'label' => '启用余额提现', 'type' => 'switch', 'default' => false, 'dependsOn' => ['key' => 'pay_balance_s', 'value' => true]],
|
||||||
['key' => 'pay_balance_withdraw_lowest_money', 'label' => '最低提现金额', 'type' => 'number', 'default' => 50, 'min' => 1, 'max' => 99999, 'dependsOn' => ['key' => 'pay_balance_withdraw_s', 'value' => true]],
|
|
||||||
]],
|
]],
|
||||||
],
|
],
|
||||||
],
|
],
|
||||||
@@ -373,11 +372,11 @@ class SchemaPayment {
|
|||||||
'option_key' => 'aksrt_settings',
|
'option_key' => 'aksrt_settings',
|
||||||
'groups' => [
|
'groups' => [
|
||||||
['key' => 'basic', 'title' => '提现设置', 'fields' => [
|
['key' => 'basic', 'title' => '提现设置', 'fields' => [
|
||||||
['key' => 'pay_rebate_withdraw_lowest_money', 'label' => '最低提现金额', 'type' => 'number', 'default' => 50, 'min' => 1, 'max' => 99999],
|
['key' => 'pay_rebate_withdraw_lowest_money', 'label' => '最低提现金额', 'type' => 'number', 'default' => 50, 'min' => 0, 'max' => 99999],
|
||||||
['key' => 'withdraw_service_charge', 'label' => '提现手续费(%)', 'type' => 'number', 'default' => 0, 'min' => 0, 'max' => 50],
|
['key' => 'withdraw_service_charge', 'label' => '提现手续费(%)', 'type' => 'number', 'default' => 5, 'min' => 0, 'max' => 50],
|
||||||
['key' => 'pay_rebate_withdraw_text_details', 'label' => '提现说明', 'type' => 'textarea', 'default' => '<div>可提现金额达到50元后即可发起提现</div>
|
['key' => 'withdraw_daily_count', 'label' => '每日可提现次数', 'type' => 'number', 'default' => 3, 'min' => 0, 'max' => 99999, 'sanitize_type' => 'int'],
|
||||||
<div>申请提现需后台人工处理,一般2-3小时,请耐心等待</div>
|
['key' => 'withdraw_daily_limit', 'label' => '每日提现上限', 'type' => 'number', 'default' => 50, 'min' => 0, 'max' => 999999],
|
||||||
<div>如有其它疑问,请与客服联系</div>'],
|
['key' => 'withdraw_reset_time', 'label' => '重置时间', 'type' => 'input', 'default' => '00:00', 'help' => '按站点 WordPress 时区重置,格式 HH:MM。'],
|
||||||
]],
|
]],
|
||||||
],
|
],
|
||||||
],
|
],
|
||||||
|
|||||||
@@ -207,6 +207,24 @@ class SchemaProvider {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ($domain === 'payment' && $section === 'withdrawal') {
|
||||||
|
$stored = get_option('aksrt_settings', []);
|
||||||
|
$merged = is_array($stored) ? array_merge($stored, $data) : $data;
|
||||||
|
$count = $merged['withdraw_daily_count'] ?? 3;
|
||||||
|
$min = (float) ($merged['pay_rebate_withdraw_lowest_money'] ?? 50);
|
||||||
|
$limit = (float) ($merged['withdraw_daily_limit'] ?? 50);
|
||||||
|
$time = (string) ($merged['withdraw_reset_time'] ?? '00:00');
|
||||||
|
if (filter_var($count, FILTER_VALIDATE_INT) === false || (int) $count < 0) {
|
||||||
|
$errors[] = __('每日可提现次数必须是非负整数', 'aksrt');
|
||||||
|
}
|
||||||
|
if ($limit < $min) {
|
||||||
|
$errors[] = __('每日提现上限不得低于最低提现金额', 'aksrt');
|
||||||
|
}
|
||||||
|
if (preg_match('/^(?:[01]\\d|2[0-3]):[0-5]\\d$/', $time) !== 1) {
|
||||||
|
$errors[] = __('重置时间必须为 HH:MM', 'aksrt');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return ['valid' => empty($errors), 'errors' => $errors];
|
return ['valid' => empty($errors), 'errors' => $errors];
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -318,6 +336,10 @@ class SchemaProvider {
|
|||||||
$stored[$key] = $value;
|
$stored[$key] = $value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (($schema['key'] ?? '') === 'withdrawal') {
|
||||||
|
unset($stored['pay_rebate_withdraw_text_details'], $stored['pay_balance_withdraw_lowest_money']);
|
||||||
|
}
|
||||||
|
|
||||||
// Captcha: keep enabled_scenarios / disabled_scenarios mirrors in sync.
|
// Captcha: keep enabled_scenarios / disabled_scenarios mirrors in sync.
|
||||||
if ($option_key === 'aksrt_captcha_config' && function_exists('aksrt_captcha_scenes')) {
|
if ($option_key === 'aksrt_captcha_config' && function_exists('aksrt_captcha_scenes')) {
|
||||||
$all = array_keys(aksrt_captcha_scenes());
|
$all = array_keys(aksrt_captcha_scenes());
|
||||||
|
|||||||
@@ -165,7 +165,7 @@ trait OAuthPart2 {
|
|||||||
}
|
}
|
||||||
$user = wp_get_current_user();
|
$user = wp_get_current_user();
|
||||||
if ($user->ID) {
|
if ($user->ID) {
|
||||||
return home_url('/user/' . $user->user_nicename . '/');
|
return function_exists('aksrt_get_user_home_url') ? aksrt_get_user_home_url($user) : home_url('/');
|
||||||
}
|
}
|
||||||
return home_url('/');
|
return home_url('/');
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ class DBMigrator {
|
|||||||
use DBMigratorVip;
|
use DBMigratorVip;
|
||||||
|
|
||||||
/** Current schema version; changing it re-runs dbDelta. */
|
/** Current schema version; changing it re-runs dbDelta. */
|
||||||
const SCHEMA_VERSION = '1.21.0';
|
const SCHEMA_VERSION = '1.22.0';
|
||||||
|
|
||||||
const OPT_KEY = 'aksrt_db_schema_version';
|
const OPT_KEY = 'aksrt_db_schema_version';
|
||||||
|
|
||||||
|
|||||||
@@ -224,6 +224,7 @@ trait DBMigratorPart2 {
|
|||||||
id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
|
id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
|
||||||
user_id BIGINT UNSIGNED NOT NULL,
|
user_id BIGINT UNSIGNED NOT NULL,
|
||||||
amount DECIMAL(10,2) NOT NULL DEFAULT 0,
|
amount DECIMAL(10,2) NOT NULL DEFAULT 0,
|
||||||
|
gross_amount DECIMAL(10,2) NOT NULL DEFAULT 0,
|
||||||
method VARCHAR(32) NOT NULL DEFAULT '',
|
method VARCHAR(32) NOT NULL DEFAULT '',
|
||||||
account_info TEXT DEFAULT NULL,
|
account_info TEXT DEFAULT NULL,
|
||||||
status VARCHAR(20) NOT NULL DEFAULT 'pending',
|
status VARCHAR(20) NOT NULL DEFAULT 'pending',
|
||||||
@@ -232,6 +233,7 @@ trait DBMigratorPart2 {
|
|||||||
note TEXT DEFAULT NULL,
|
note TEXT DEFAULT NULL,
|
||||||
PRIMARY KEY (id),
|
PRIMARY KEY (id),
|
||||||
KEY idx_user (user_id),
|
KEY idx_user (user_id),
|
||||||
|
KEY idx_user_created (user_id, created_at),
|
||||||
KEY idx_status (status),
|
KEY idx_status (status),
|
||||||
KEY idx_created (created_at)
|
KEY idx_created (created_at)
|
||||||
) {$charset_collate};";
|
) {$charset_collate};";
|
||||||
|
|||||||
@@ -292,7 +292,7 @@ $hmb_js = get_template_directory() . '/assets/js/header-newadd.js';
|
|||||||
$is_resource_archive = function_exists('aksrt_is_resource_archive') && aksrt_is_resource_archive();
|
$is_resource_archive = function_exists('aksrt_is_resource_archive') && aksrt_is_resource_archive();
|
||||||
// /user/{nicename}/ 自定义重写路由(class-aksrt-user.php 注册)
|
// /user/{nicename}/ 自定义重写路由(class-aksrt-user.php 注册)
|
||||||
// 此路径下 is_page() / is_page_template() 均为 false,需手动识别以正确入队 CSS
|
// 此路径下 is_page() / is_page_template() 均为 false,需手动识别以正确入队 CSS
|
||||||
$is_profile_route = (bool) get_query_var('user_nicename');
|
$is_profile_route = (bool) get_query_var('aksrt_author_identifier');
|
||||||
$is_user_center_route = (bool) get_query_var('aksrt_user_center');
|
$is_user_center_route = (bool) get_query_var('aksrt_user_center');
|
||||||
$is_static_page = is_page() || is_singular('page') || $is_profile_route || $is_user_center_route;
|
$is_static_page = is_page() || is_singular('page') || $is_profile_route || $is_user_center_route;
|
||||||
$is_auth_route = (bool) get_query_var('aksrt_sign');
|
$is_auth_route = (bool) get_query_var('aksrt_sign');
|
||||||
|
|||||||
@@ -111,7 +111,7 @@ trait ScriptsPart2 {
|
|||||||
}
|
}
|
||||||
if ((function_exists('aksrt_is_user_center_page') && aksrt_is_user_center_page())
|
if ((function_exists('aksrt_is_user_center_page') && aksrt_is_user_center_page())
|
||||||
|| (function_exists('aksrt_is_user_home_page') && aksrt_is_user_home_page())
|
|| (function_exists('aksrt_is_user_home_page') && aksrt_is_user_home_page())
|
||||||
|| get_query_var('user_nicename')) {
|
|| get_query_var('aksrt_author_identifier')) {
|
||||||
$classes[] = 'aksrt-profile';
|
$classes[] = 'aksrt-profile';
|
||||||
}
|
}
|
||||||
// 区分本人中心与公开主页的 body class
|
// 区分本人中心与公开主页的 body class
|
||||||
|
|||||||
@@ -398,50 +398,37 @@ trait BalanceServiceRest {
|
|||||||
if (class_exists('\AKSRT\Settings\Capabilities') && !\AKSRT\Settings\Capabilities::user_can('balance_withdraw', $user_id)) {
|
if (class_exists('\AKSRT\Settings\Capabilities') && !\AKSRT\Settings\Capabilities::user_can('balance_withdraw', $user_id)) {
|
||||||
return new \WP_Error('aksrt_withdraw_forbidden', __('当前账号无权提现', 'aksrt'), ['status' => 403]);
|
return new \WP_Error('aksrt_withdraw_forbidden', __('当前账号无权提现', 'aksrt'), ['status' => 403]);
|
||||||
}
|
}
|
||||||
$amount = round((float) ($params['amount'] ?? 0), 2);
|
$raw_amount = $params['amount'] ?? 0;
|
||||||
|
if (filter_var($raw_amount, FILTER_VALIDATE_INT) === false || (int) $raw_amount <= 0) {
|
||||||
|
return new \WP_Error('aksrt_withdraw_amount', __('提现金额必须为正整数', 'aksrt'), ['status' => 400]);
|
||||||
|
}
|
||||||
|
$amount = (int) $raw_amount;
|
||||||
$method = sanitize_key((string) ($params['method'] ?? 'alipay'));
|
$method = sanitize_key((string) ($params['method'] ?? 'alipay'));
|
||||||
$account = sanitize_textarea_field((string) ($params['account_info'] ?? $params['account'] ?? ''));
|
$account = sanitize_textarea_field((string) ($params['account_info'] ?? $params['account'] ?? ''));
|
||||||
$lowest = max(1, (float) ($settings['pay_balance_withdraw_lowest_money'] ?? $settings['pay_rebate_withdraw_lowest_money'] ?? 50));
|
$withdraw_settings = class_exists('\\AKSRT\\Settings\\Settings')
|
||||||
|
? \AKSRT\Settings\Settings::get_withdrawal_settings()
|
||||||
|
: ['min_amount' => 50, 'service_charge_rate' => 5, 'daily_count' => 3, 'daily_limit' => 50, 'reset_time' => '00:00'];
|
||||||
|
$lowest = (float) $withdraw_settings['min_amount'];
|
||||||
if ($amount < $lowest) {
|
if ($amount < $lowest) {
|
||||||
return new \WP_Error('aksrt_withdraw_min', sprintf(__('最低提现 %s 元', 'aksrt'), (string) $lowest), ['status' => 400]);
|
return new \WP_Error('withdraw_min_amount', sprintf(__('提现金额低于最低提现门槛(%s 元)', 'aksrt'), (string) $lowest), ['status' => 400]);
|
||||||
}
|
}
|
||||||
if ($account === '') {
|
if ($account === '') {
|
||||||
return new \WP_Error('aksrt_withdraw_account', __('请填写收款账号信息', 'aksrt'), ['status' => 400]);
|
return new \WP_Error('aksrt_withdraw_account', __('请填写收款账号信息', 'aksrt'), ['status' => 400]);
|
||||||
}
|
}
|
||||||
// One open balance withdrawal at a time (Zibll parity).
|
$result = \AKSRT\Payment\Withdrawal::get_instance()->create_balance_request($user_id, $amount, $method !== '' ? $method : 'alipay', $account, $withdraw_settings);
|
||||||
if (\AKSRT\Payment\Withdrawal::get_instance()->has_open_withdrawal($user_id)) {
|
if (is_wp_error($result)) {
|
||||||
return new \WP_Error('aksrt_withdraw_pending', __('您有处理中的提现申请,请等待完成后再申请', 'aksrt'), ['status' => 409]);
|
return $result;
|
||||||
}
|
|
||||||
$wallet = Balance::balance($user_id);
|
|
||||||
if ($wallet < $amount) {
|
|
||||||
return new \WP_Error('aksrt_balance_insufficient', __('余额不足', 'aksrt'), ['status' => 402]);
|
|
||||||
}
|
|
||||||
$deduct = Balance::deduct($user_id, $amount, 'withdraw_hold', ['type' => 'withdraw'], __('余额提现申请冻结', 'aksrt'));
|
|
||||||
if (is_wp_error($deduct)) {
|
|
||||||
return $deduct;
|
|
||||||
}
|
|
||||||
$fee_rate = max(0, min(50, (int) ($settings['withdraw_service_charge'] ?? 0)));
|
|
||||||
$net = $fee_rate > 0 ? round($amount * (100 - $fee_rate) / 100, 2) : $amount;
|
|
||||||
$account_payload = wp_json_encode([
|
|
||||||
'account' => $account,
|
|
||||||
'gross_amount' => $amount,
|
|
||||||
'fee_rate' => $fee_rate,
|
|
||||||
'source' => 'balance',
|
|
||||||
], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES) ?: $account;
|
|
||||||
$wid = \AKSRT\Payment\Withdrawal::get_instance()->create($user_id, $net, $method !== '' ? $method : 'alipay', $account_payload);
|
|
||||||
if ($wid <= 0) {
|
|
||||||
Balance::add($user_id, $amount, 'withdraw_refund', [], __('提现申请失败退回', 'aksrt'));
|
|
||||||
return new \WP_Error('aksrt_withdraw_failed', __('提现申请失败', 'aksrt'), ['status' => 500]);
|
|
||||||
}
|
}
|
||||||
return rest_ensure_response([
|
return rest_ensure_response([
|
||||||
'success' => true,
|
'success' => true,
|
||||||
'data' => [
|
'data' => [
|
||||||
'message' => __('提现申请已提交,请等待处理', 'aksrt'),
|
'message' => __('提现申请已提交,请等待处理', 'aksrt'),
|
||||||
'withdrawal_id' => $wid,
|
'withdrawal_id' => $result['withdrawal_id'],
|
||||||
'amount' => $amount,
|
'amount' => $amount,
|
||||||
'net_amount' => $net,
|
'net_amount' => $result['net_amount'],
|
||||||
'wallet_balance' => Balance::balance($user_id),
|
'wallet_balance' => $result['wallet_balance'],
|
||||||
'reload' => true,
|
'remaining_count' => $result['usage']['remaining_count'],
|
||||||
|
'remaining_amount' => $result['usage']['remaining_amount'],
|
||||||
],
|
],
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|||||||
+112
-2
@@ -30,7 +30,7 @@ class Withdrawal {
|
|||||||
/**
|
/**
|
||||||
* 创建提现申请
|
* 创建提现申请
|
||||||
*/
|
*/
|
||||||
public function create(int $user_id, float $amount, string $method = '', string $account_info = ''): int {
|
public function create(int $user_id, float $amount, string $method = '', string $account_info = '', ?float $gross_amount = null): int {
|
||||||
global $wpdb;
|
global $wpdb;
|
||||||
|
|
||||||
if ($user_id <= 0 || $amount <= 0) {
|
if ($user_id <= 0 || $amount <= 0) {
|
||||||
@@ -40,15 +40,122 @@ class Withdrawal {
|
|||||||
$wpdb->insert($this->table(), [
|
$wpdb->insert($this->table(), [
|
||||||
'user_id' => $user_id,
|
'user_id' => $user_id,
|
||||||
'amount' => $amount,
|
'amount' => $amount,
|
||||||
|
'gross_amount' => $gross_amount === null ? $amount : $gross_amount,
|
||||||
'method' => sanitize_key($method),
|
'method' => sanitize_key($method),
|
||||||
'account_info' => sanitize_textarea_field($account_info),
|
'account_info' => sanitize_textarea_field($account_info),
|
||||||
'status' => self::STATUS_PENDING,
|
'status' => self::STATUS_PENDING,
|
||||||
'created_at' => current_time('mysql'),
|
'created_at' => current_time('mysql'),
|
||||||
], ['%d', '%f', '%s', '%s', '%s', '%s']);
|
], ['%d', '%f', '%f', '%s', '%s', '%s', '%s']);
|
||||||
|
|
||||||
return (int) $wpdb->insert_id;
|
return (int) $wpdb->insert_id;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Daily withdrawal window in the configured WordPress timezone.
|
||||||
|
* @return array{start:string,end:string}
|
||||||
|
*/
|
||||||
|
public function daily_window(string $reset_time = '00:00', ?\DateTimeInterface $now = null): array {
|
||||||
|
if (preg_match('/^(?:[01]\\d|2[0-3]):[0-5]\\d$/', $reset_time) !== 1) {
|
||||||
|
$reset_time = '00:00';
|
||||||
|
}
|
||||||
|
$tz = wp_timezone();
|
||||||
|
$local_now = $now ? \DateTimeImmutable::createFromInterface($now)->setTimezone($tz) : new \DateTimeImmutable('now', $tz);
|
||||||
|
$today_reset = new \DateTimeImmutable($local_now->format('Y-m-d') . ' ' . $reset_time . ':00', $tz);
|
||||||
|
$start = $local_now < $today_reset ? $today_reset->modify('-1 day') : $today_reset;
|
||||||
|
return ['start' => $start->format('Y-m-d H:i:s'), 'end' => $start->modify('+1 day')->format('Y-m-d H:i:s')];
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @return array{count:int,total:float,remaining_count:int,remaining_amount:float,window:array{start:string,end:string}} */
|
||||||
|
public function daily_usage(int $user_id, array $settings): array {
|
||||||
|
$window = $this->daily_window((string) ($settings['reset_time'] ?? '00:00'));
|
||||||
|
return $this->daily_usage_in_window($user_id, $settings, $window);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @return array{count:int,total:float,remaining_count:int,remaining_amount:float,window:array{start:string,end:string}} */
|
||||||
|
private function daily_usage_in_window(int $user_id, array $settings, array $window): array {
|
||||||
|
global $wpdb;
|
||||||
|
$row = $wpdb->get_row($wpdb->prepare(
|
||||||
|
"SELECT COUNT(*) AS count, COALESCE(SUM(CASE WHEN gross_amount > 0 THEN gross_amount ELSE amount END), 0) AS total FROM {$this->table()} WHERE user_id = %d AND created_at >= %s AND created_at < %s",
|
||||||
|
$user_id, $window['start'], $window['end']
|
||||||
|
), ARRAY_A);
|
||||||
|
$count = (int) ($row['count'] ?? 0);
|
||||||
|
$total = round((float) ($row['total'] ?? 0), 2);
|
||||||
|
$max_count = max(0, (int) ($settings['daily_count'] ?? 3));
|
||||||
|
$max_amount = max(0, (float) ($settings['daily_limit'] ?? 50));
|
||||||
|
return [
|
||||||
|
'count' => $count,
|
||||||
|
'total' => $total,
|
||||||
|
'remaining_count' => max(0, $max_count - $count),
|
||||||
|
'remaining_amount' => max(0, round($max_amount - $total, 2)),
|
||||||
|
'window' => $window,
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Atomically validates quotas, freezes wallet funds, writes the ledger and creates an order.
|
||||||
|
* @return array{withdrawal_id:int,net_amount:float,wallet_balance:float,usage:array}|\WP_Error
|
||||||
|
*/
|
||||||
|
public function create_balance_request(int $user_id, int $gross_amount, string $method, string $account, array $settings) {
|
||||||
|
global $wpdb;
|
||||||
|
if ($user_id <= 0 || $gross_amount <= 0) {
|
||||||
|
return new \WP_Error('aksrt_withdraw_amount', __('提现金额必须为正整数', 'aksrt'), ['status' => 400]);
|
||||||
|
}
|
||||||
|
if (!class_exists('\AKSRT\\Credits\\Balance')) {
|
||||||
|
return new \WP_Error('aksrt_withdraw_unavailable', __('提现服务不可用', 'aksrt'), ['status' => 500]);
|
||||||
|
}
|
||||||
|
$meta_table = $wpdb->usermeta;
|
||||||
|
$meta_key = \AKSRT\Credits\Balance::META_KEY;
|
||||||
|
if (!metadata_exists('user', $user_id, $meta_key)) {
|
||||||
|
add_user_meta($user_id, $meta_key, '0', true);
|
||||||
|
}
|
||||||
|
$wpdb->query('START TRANSACTION');
|
||||||
|
try {
|
||||||
|
$meta_id = $wpdb->get_var($wpdb->prepare("SELECT umeta_id FROM {$meta_table} WHERE user_id = %d AND meta_key = %s LIMIT 1 FOR UPDATE", $user_id, $meta_key));
|
||||||
|
if (!$meta_id) {
|
||||||
|
throw new \RuntimeException('wallet row unavailable');
|
||||||
|
}
|
||||||
|
$window = $this->daily_window((string) ($settings['reset_time'] ?? '00:00'));
|
||||||
|
$usage = $this->daily_usage_in_window($user_id, $settings, $window);
|
||||||
|
if ($usage['remaining_count'] <= 0) {
|
||||||
|
$wpdb->query('ROLLBACK');
|
||||||
|
return new \WP_Error('withdraw_count_limit', __('当日提现次数已用尽', 'aksrt'), ['status' => 409]);
|
||||||
|
}
|
||||||
|
if ($gross_amount > $usage['remaining_amount']) {
|
||||||
|
$wpdb->query('ROLLBACK');
|
||||||
|
return new \WP_Error('withdraw_amount_limit', __('本次提现后将超出单日提现总额上限', 'aksrt'), ['status' => 409]);
|
||||||
|
}
|
||||||
|
$before = (float) $wpdb->get_var($wpdb->prepare("SELECT meta_value FROM {$meta_table} WHERE umeta_id = %d", $meta_id));
|
||||||
|
if ($before < $gross_amount) {
|
||||||
|
$wpdb->query('ROLLBACK');
|
||||||
|
return new \WP_Error('aksrt_balance_insufficient', __('余额不足', 'aksrt'), ['status' => 402]);
|
||||||
|
}
|
||||||
|
$after = round($before - $gross_amount, 2);
|
||||||
|
if ($wpdb->update($meta_table, ['meta_value' => (string) $after], ['umeta_id' => $meta_id], ['%s'], ['%d']) !== 1) {
|
||||||
|
throw new \RuntimeException('wallet update failed');
|
||||||
|
}
|
||||||
|
$fee_rate = max(0, min(50, (int) ($settings['service_charge_rate'] ?? 5)));
|
||||||
|
$net = round($gross_amount * (100 - $fee_rate) / 100, 2);
|
||||||
|
$payload = wp_json_encode(['account' => $account, 'gross_amount' => $gross_amount, 'fee_rate' => $fee_rate, 'source' => 'balance'], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES) ?: $account;
|
||||||
|
$ok = $wpdb->insert($this->table(), [
|
||||||
|
'user_id' => $user_id, 'amount' => $net, 'gross_amount' => $gross_amount, 'method' => sanitize_key($method),
|
||||||
|
'account_info' => $payload, 'status' => self::STATUS_PENDING, 'created_at' => current_time('mysql'),
|
||||||
|
], ['%d', '%f', '%f', '%s', '%s', '%s', '%s']);
|
||||||
|
if (!$ok) { throw new \RuntimeException('withdrawal insert failed'); }
|
||||||
|
$withdrawal_id = (int) $wpdb->insert_id;
|
||||||
|
\AKSRT\Credits\Balance::log($user_id, -$gross_amount, $after, 'withdraw_hold', ['type' => 'withdraw', 'id' => $withdrawal_id], __('余额提现申请冻结', 'aksrt'));
|
||||||
|
$wpdb->query('COMMIT');
|
||||||
|
wp_cache_delete($user_id, 'user_meta');
|
||||||
|
$usage['count']++;
|
||||||
|
$usage['total'] = round($usage['total'] + $gross_amount, 2);
|
||||||
|
$usage['remaining_count']--;
|
||||||
|
$usage['remaining_amount'] = max(0, round($usage['remaining_amount'] - $gross_amount, 2));
|
||||||
|
return ['withdrawal_id' => $withdrawal_id, 'net_amount' => $net, 'wallet_balance' => $after, 'usage' => $usage];
|
||||||
|
} catch (\Throwable $e) {
|
||||||
|
$wpdb->query('ROLLBACK');
|
||||||
|
return new \WP_Error('aksrt_withdraw_failed', __('提现申请失败', 'aksrt'), ['status' => 500]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get single row.
|
* Get single row.
|
||||||
*
|
*
|
||||||
@@ -394,6 +501,9 @@ class Withdrawal {
|
|||||||
* @param array<string,mixed> $meta
|
* @param array<string,mixed> $meta
|
||||||
*/
|
*/
|
||||||
private function resolve_gross_amount(array $row, array $meta): float {
|
private function resolve_gross_amount(array $row, array $meta): float {
|
||||||
|
if ((float) ($row['gross_amount'] ?? 0) > 0) {
|
||||||
|
return max(0, round((float) $row['gross_amount'], 2));
|
||||||
|
}
|
||||||
if (isset($meta['gross_amount'])) {
|
if (isset($meta['gross_amount'])) {
|
||||||
return max(0, round((float) $meta['gross_amount'], 2));
|
return max(0, round((float) $meta['gross_amount'], 2));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -438,16 +438,18 @@ class PaymentRuntime {
|
|||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/** @return array{min_amount:float,service_charge_rate:int,daily_count:int,daily_limit:float,reset_time:string} */
|
||||||
* @return array{min_amount:int,service_charge_rate:int,text:string}
|
|
||||||
*/
|
|
||||||
public static function get_withdrawal_settings(): array {
|
public static function get_withdrawal_settings(): array {
|
||||||
$s = Settings::get_all();
|
$s = Settings::get_all();
|
||||||
|
|
||||||
return [
|
return [
|
||||||
'min_amount' => max(1, min(99999, (int) ($s['pay_rebate_withdraw_lowest_money'] ?? 50))),
|
'min_amount' => max(0, min(99999, (float) ($s['pay_rebate_withdraw_lowest_money'] ?? 50))),
|
||||||
'service_charge_rate' => max(0, min(50, (int) ($s['withdraw_service_charge'] ?? 0))),
|
'service_charge_rate' => max(0, min(50, (int) ($s['withdraw_service_charge'] ?? 5))),
|
||||||
'text' => sanitize_textarea_field((string) ($s['pay_rebate_withdraw_text_details'] ?? '')),
|
'daily_count' => max(0, min(99999, (int) ($s['withdraw_daily_count'] ?? 3))),
|
||||||
|
'daily_limit' => max(0, min(999999, (float) ($s['withdraw_daily_limit'] ?? 50))),
|
||||||
|
'reset_time' => preg_match('/^(?:[01]\\d|2[0-3]):[0-5]\\d$/', (string) ($s['withdraw_reset_time'] ?? '00:00'))
|
||||||
|
? (string) $s['withdraw_reset_time']
|
||||||
|
: '00:00',
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -54,6 +54,7 @@ class UserRuntime {
|
|||||||
$mobile_hide = $parse_csv('user_home_tabs_mobile_hide', '');
|
$mobile_hide = $parse_csv('user_home_tabs_mobile_hide', '');
|
||||||
|
|
||||||
return [
|
return [
|
||||||
|
'route_identifier' => (($settings['user_home_route_identifier'] ?? 'nicename') === 'uid') ? 'uid' : 'nicename',
|
||||||
'tabs' => $normalized_tabs,
|
'tabs' => $normalized_tabs,
|
||||||
'tabs_order' => $ordered,
|
'tabs_order' => $ordered,
|
||||||
'tabs_pc_hide' => $pc_hide,
|
'tabs_pc_hide' => $pc_hide,
|
||||||
|
|||||||
@@ -19,15 +19,6 @@ if (!is_user_logged_in()) {
|
|||||||
$current_user = wp_get_current_user();
|
$current_user = wp_get_current_user();
|
||||||
$user_id = (int) $current_user->ID;
|
$user_id = (int) $current_user->ID;
|
||||||
|
|
||||||
$requested_nicename = get_query_var('user_nicename');
|
|
||||||
if ($requested_nicename && $requested_nicename !== $current_user->user_nicename) {
|
|
||||||
$requested_user = get_user_by('slug', $requested_nicename);
|
|
||||||
wp_safe_redirect($requested_user && function_exists('aksrt_get_user_home_url')
|
|
||||||
? aksrt_get_user_home_url($requested_user)
|
|
||||||
: aksrt_get_user_center_url());
|
|
||||||
exit;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ('POST' === ($_SERVER['REQUEST_METHOD'] ?? '') && isset($_POST['action']) && 'aksrt_save_profile' === sanitize_key(wp_unslash($_POST['action']))) {
|
if ('POST' === ($_SERVER['REQUEST_METHOD'] ?? '') && isset($_POST['action']) && 'aksrt_save_profile' === sanitize_key(wp_unslash($_POST['action']))) {
|
||||||
$nonce = isset($_POST['_profile_nonce']) ? sanitize_text_field(wp_unslash($_POST['_profile_nonce'])) : '';
|
$nonce = isset($_POST['_profile_nonce']) ? sanitize_text_field(wp_unslash($_POST['_profile_nonce'])) : '';
|
||||||
if (!wp_verify_nonce($nonce, 'aksrt_profile_save')) {
|
if (!wp_verify_nonce($nonce, 'aksrt_profile_save')) {
|
||||||
|
|||||||
Reference in New Issue
Block a user