323 lines
14 KiB
PHP
323 lines
14 KiB
PHP
<?php
|
||
/**
|
||
* @package AKSRT
|
||
*/
|
||
|
||
namespace AKSRT\User;
|
||
|
||
defined('ABSPATH') || exit;
|
||
|
||
class CreditsAdmin {
|
||
|
||
private static $instance = null;
|
||
|
||
private function __construct() {}
|
||
|
||
public static function get_instance(): self {
|
||
if (null === self::$instance) self::$instance = new self();
|
||
return self::$instance;
|
||
}
|
||
|
||
public function init(): void {
|
||
add_filter('manage_users_columns', [$this, 'add_balance_column']);
|
||
add_filter('manage_users_custom_column', [$this, 'render_balance_column'], 10, 3);
|
||
add_filter('manage_users_sortable_columns', [$this, 'make_balance_sortable']);
|
||
add_action('pre_get_users', [$this, 'sort_users_by_credits']);
|
||
add_filter('user_row_actions', [$this, 'add_row_actions'], 30, 2);
|
||
|
||
add_action('show_user_profile', [$this, 'render_user_credits_section']);
|
||
add_action('edit_user_profile', [$this, 'render_user_credits_section']);
|
||
add_action('personal_options_update', [$this, 'handle_user_credits_update']);
|
||
add_action('edit_user_profile_update', [$this, 'handle_user_credits_update']);
|
||
}
|
||
|
||
private function can_manage_credits(): bool {
|
||
return current_user_can('manage_options');
|
||
}
|
||
|
||
// ========== 用户列表:积分余额列 ==========
|
||
|
||
public function add_balance_column(array $columns): array {
|
||
$currency_name = class_exists('\AKSRT\Settings\Settings') ? \AKSRT\Settings\Settings::get_currency_name() : __('积分', 'aksrt');
|
||
$columns['aksrt_credits_balance'] = sprintf(__('%s余额', 'aksrt'), $currency_name);
|
||
$columns['aksrt_user_commerce'] = __('资产/分成', 'aksrt');
|
||
return $columns;
|
||
}
|
||
|
||
public function render_balance_column(string $output, string $column_name, int $user_id): string {
|
||
if ($column_name === 'aksrt_user_commerce') {
|
||
return $this->render_commerce_column($user_id);
|
||
}
|
||
if ($column_name !== 'aksrt_credits_balance') {
|
||
return $output;
|
||
}
|
||
if (!class_exists('\AKSRT\Credits\Credits')) {
|
||
return '—';
|
||
}
|
||
|
||
$balance = \AKSRT\Credits\Credits::balance($user_id);
|
||
return '<strong>' . esc_html((string) $balance) . '</strong>';
|
||
}
|
||
|
||
/**
|
||
* Compact commerce snapshot: VIP, revenue, rebate, download logs link.
|
||
*/
|
||
private function render_commerce_column(int $user_id): string {
|
||
$bits = [];
|
||
if (function_exists('aksrt_get_user_vip_level')) {
|
||
$lv = (int) aksrt_get_user_vip_level($user_id);
|
||
if ($lv > 0) {
|
||
$bits[] = '<span class="aksrt-vip-badge">VIP' . esc_html((string) $lv) . '</span>';
|
||
}
|
||
}
|
||
if (class_exists('\AKSRT\Payment\Revenue')) {
|
||
$stats = \AKSRT\Payment\Revenue::get_instance()->get_stats($user_id);
|
||
$cash = (array) ($stats['cash'] ?? []);
|
||
$points = (array) ($stats['points'] ?? []);
|
||
if ((float) ($cash['total'] ?? 0) > 0 || (float) ($cash['pending'] ?? 0) > 0) {
|
||
$bits[] = sprintf(
|
||
/* translators: 1 total 2 pending */
|
||
esc_html__('现金分成 ¥%1$s(待结 ¥%2$s)', 'aksrt'),
|
||
number_format((float) ($cash['total'] ?? 0), 2),
|
||
number_format((float) ($cash['pending'] ?? 0), 2)
|
||
);
|
||
}
|
||
if ((float) ($points['total'] ?? 0) > 0 || (float) ($points['pending'] ?? 0) > 0) {
|
||
$bits[] = sprintf(
|
||
/* translators: 1 total 2 pending */
|
||
esc_html__('积分分成 %1$s(待结 %2$s)', 'aksrt'),
|
||
number_format((float) ($points['total'] ?? 0), 0),
|
||
number_format((float) ($points['pending'] ?? 0), 0)
|
||
);
|
||
}
|
||
if ($bits) {
|
||
$bits[] = '<a href="' . esc_url(admin_url('admin.php?page=aksrt_income&user_id=' . $user_id)) . '">' . esc_html__('分成明细', 'aksrt') . '</a>';
|
||
}
|
||
}
|
||
if (class_exists('\AKSRT\Payment\Rebate')) {
|
||
$list = \AKSRT\Payment\Rebate::get_instance()->get_list(['user_id' => $user_id, 'limit' => 1]);
|
||
$total_r = (int) ($list['total'] ?? 0);
|
||
if ($total_r > 0) {
|
||
$bits[] = sprintf(esc_html__('返佣 %d 笔', 'aksrt'), $total_r);
|
||
$bits[] = '<a href="' . esc_url(admin_url('admin.php?page=aksrt_rebate&user_id=' . $user_id)) . '">' . esc_html__('返佣', 'aksrt') . '</a>';
|
||
}
|
||
}
|
||
$bits[] = '<a href="' . esc_url(admin_url('admin.php?page=aksrt_download_logs&user_id=' . $user_id)) . '">' . esc_html__('下载记录', 'aksrt') . '</a>';
|
||
$bits[] = '<a href="' . esc_url(admin_url('admin.php?page=aksrt_orders&user_id=' . $user_id)) . '">' . esc_html__('订单', 'aksrt') . '</a>';
|
||
|
||
if (!$bits) {
|
||
return '—';
|
||
}
|
||
return '<div style="font-size:12px;line-height:1.5;">' . implode('<br>', $bits) . '</div>';
|
||
}
|
||
|
||
public function make_balance_sortable(array $columns): array {
|
||
$columns['aksrt_credits_balance'] = 'aksrt_credits_balance';
|
||
return $columns;
|
||
}
|
||
|
||
public function sort_users_by_credits($query): void {
|
||
if (!is_admin()) {
|
||
return;
|
||
}
|
||
if ($query->get('orderby') !== 'aksrt_credits_balance') {
|
||
return;
|
||
}
|
||
$query->set('meta_key', 'aksrt_credits');
|
||
$query->set('orderby', 'meta_value_num');
|
||
}
|
||
|
||
// ========== 用户列表:行操作 ==========
|
||
|
||
public function add_row_actions(array $actions, \WP_User $user): array {
|
||
if (!$this->can_manage_credits()) {
|
||
return $actions;
|
||
}
|
||
|
||
$edit_url = get_edit_user_link($user->ID);
|
||
if ($edit_url) {
|
||
$currency_name = class_exists('\AKSRT\Settings\Settings') ? \AKSRT\Settings\Settings::get_currency_name() : __('积分', 'aksrt');
|
||
$actions['aksrt_edit_credits'] = sprintf(
|
||
'<a href="%s#aksrt-user-credits">%s</a>',
|
||
esc_url($edit_url),
|
||
esc_html(sprintf(__('调整%s', 'aksrt'), $currency_name))
|
||
);
|
||
}
|
||
$actions['aksrt_user_dl_logs'] = sprintf(
|
||
'<a href="%s">%s</a>',
|
||
esc_url(admin_url('admin.php?page=aksrt_download_logs&user_id=' . (int) $user->ID)),
|
||
esc_html__('下载记录', 'aksrt')
|
||
);
|
||
|
||
return $actions;
|
||
}
|
||
|
||
// ========== 用户编辑页:积分管理区块 ==========
|
||
|
||
public function render_user_credits_section(\WP_User $user): void {
|
||
if (!class_exists('\AKSRT\Credits\Credits')) {
|
||
return;
|
||
}
|
||
|
||
$balance = \AKSRT\Credits\Credits::balance((int) $user->ID);
|
||
$can_manage = $this->can_manage_credits();
|
||
$currency_name = class_exists('\AKSRT\Settings\Settings') ? \AKSRT\Settings\Settings::get_currency_name() : __('积分', 'aksrt');
|
||
?>
|
||
<a id="aksrt-user-credits"></a>
|
||
<h2><?php echo esc_html(sprintf(__('%s管理(AKSRT)', 'aksrt'), $currency_name)); ?></h2>
|
||
<table class="form-table" role="presentation">
|
||
<tr>
|
||
<th><?php esc_html_e('当前余额', 'aksrt'); ?></th>
|
||
<td>
|
||
<strong style="font-size:18px;"><?php echo esc_html((string) $balance); ?></strong>
|
||
<span style="color:#666;margin-left:6px;"><?php echo esc_html($currency_name); ?></span>
|
||
</td>
|
||
</tr>
|
||
|
||
<?php if ($can_manage) : ?>
|
||
<tr>
|
||
<th><?php echo esc_html(sprintf(__('调整%s', 'aksrt'), $currency_name)); ?></th>
|
||
<td>
|
||
<?php wp_nonce_field('aksrt_user_credits_adjust_' . (int) $user->ID, '_aksrt_credits_nonce'); ?>
|
||
<fieldset>
|
||
<label for="aksrt_credits_delta" style="display:block;margin-bottom:6px;">
|
||
<?php esc_html_e('变动数量(正数=增加,负数=扣减)', 'aksrt'); ?>
|
||
</label>
|
||
<input
|
||
type="number"
|
||
id="aksrt_credits_delta"
|
||
name="aksrt_credits_delta"
|
||
class="regular-text"
|
||
min="-100000"
|
||
max="100000"
|
||
step="1"
|
||
value="0"
|
||
>
|
||
<p class="description" style="margin-bottom:12px;">
|
||
<?php esc_html_e('填写 0 不会执行任何变更。', 'aksrt'); ?>
|
||
</p>
|
||
|
||
<label for="aksrt_credits_reason" style="display:block;margin-bottom:6px;">
|
||
<?php esc_html_e('调整原因', 'aksrt'); ?>
|
||
</label>
|
||
<input
|
||
type="text"
|
||
id="aksrt_credits_reason"
|
||
name="aksrt_credits_reason"
|
||
class="regular-text"
|
||
maxlength="100"
|
||
placeholder="<?php echo esc_attr__('例如:手工补偿 / 运营赠送 / 退款修正', 'aksrt'); ?>"
|
||
>
|
||
<p class="description">
|
||
<?php esc_html_e('勾选后点击页面底部"更新用户"保存即可生效。', 'aksrt'); ?>
|
||
</p>
|
||
</fieldset>
|
||
</td>
|
||
</tr>
|
||
<?php endif; ?>
|
||
|
||
</table>
|
||
|
||
<?php
|
||
// ========== 近期积分流水 ==========
|
||
$log = \AKSRT\Credits\Credits::get_log(['user_id' => (int) $user->ID, 'page' => 1, 'per_page' => 20]);
|
||
$items = $log['items'] ?? [];
|
||
$reason_labels = [
|
||
'register' => __('注册赠送', 'aksrt'),
|
||
'signin' => __('每日签到', 'aksrt'),
|
||
'comment' => __('评论奖励', 'aksrt'),
|
||
'referral' => __('推广注册奖励', 'aksrt'),
|
||
'referral_new_user' => __('推广注册新用户奖励', 'aksrt'),
|
||
'download' => __('下载消费', 'aksrt'),
|
||
'admin' => __('管理员调整', 'aksrt'),
|
||
];
|
||
?>
|
||
<h3 style="margin-top:24px;"><?php echo esc_html(sprintf(__('近期%s流水', 'aksrt'), $currency_name)); ?></h3>
|
||
<?php if (!empty($items)) : ?>
|
||
<table class="widefat striped" style="max-width:800px;">
|
||
<thead>
|
||
<tr>
|
||
<th><?php esc_html_e('时间', 'aksrt'); ?></th>
|
||
<th><?php esc_html_e('类型', 'aksrt'); ?></th>
|
||
<th><?php esc_html_e('备注', 'aksrt'); ?></th>
|
||
<th><?php esc_html_e('变动', 'aksrt'); ?></th>
|
||
<th><?php esc_html_e('余额', 'aksrt'); ?></th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<?php foreach ($items as $row) :
|
||
$delta = (int) $row['delta'];
|
||
$reason_key = (string) ($row['reason'] ?? '');
|
||
$label = $reason_labels[$reason_key] ?? $reason_key;
|
||
?>
|
||
<tr>
|
||
<td><?php echo esc_html(get_date_from_gmt((string) $row['created_at'], 'Y-m-d H:i')); ?></td>
|
||
<td><?php echo esc_html($label); ?></td>
|
||
<td><?php echo esc_html((string) ($row['remark'] ?? '')); ?></td>
|
||
<td style="color:<?php echo $delta >= 0 ? '#16a34a' : '#ef4444'; ?>; font-weight:600;">
|
||
<?php echo esc_html(($delta > 0 ? '+' : '') . $delta); ?>
|
||
</td>
|
||
<td><?php echo esc_html((string) (int) $row['balance_after']); ?></td>
|
||
</tr>
|
||
<?php endforeach; ?>
|
||
</tbody>
|
||
</table>
|
||
<?php else : ?>
|
||
<p style="color:#888;"><?php echo esc_html(sprintf(__('暂无%s流水记录。', 'aksrt'), $currency_name)); ?></p>
|
||
<?php endif;
|
||
}
|
||
|
||
// ========== 处理积分调整 ==========
|
||
|
||
public function handle_user_credits_update(int $user_id): void {
|
||
if (!$this->can_manage_credits() || !class_exists('\AKSRT\Credits\Credits')) {
|
||
return;
|
||
}
|
||
if (!current_user_can('edit_user', $user_id)) {
|
||
return;
|
||
}
|
||
if (!isset($_POST['_aksrt_credits_nonce'])) {
|
||
return;
|
||
}
|
||
|
||
$nonce = sanitize_text_field((string) $_POST['_aksrt_credits_nonce']);
|
||
if (!wp_verify_nonce($nonce, 'aksrt_user_credits_adjust_' . $user_id)) {
|
||
return;
|
||
}
|
||
|
||
$delta = isset($_POST['aksrt_credits_delta']) ? (int) $_POST['aksrt_credits_delta'] : 0;
|
||
$reason = isset($_POST['aksrt_credits_reason']) ? sanitize_text_field((string) wp_unslash($_POST['aksrt_credits_reason'])) : '';
|
||
|
||
if ($delta === 0) {
|
||
return;
|
||
}
|
||
$currency_name = class_exists('\AKSRT\Settings\Settings') ? \AKSRT\Settings\Settings::get_currency_name() : __('积分', 'aksrt');
|
||
if ($reason === '') {
|
||
set_transient('aksrt_admin_notice_' . get_current_user_id(), [[
|
||
'type' => 'error',
|
||
'msg' => sprintf(__('调整%s时请填写原因。', 'aksrt'), $currency_name),
|
||
]], 30);
|
||
return;
|
||
}
|
||
|
||
if ($delta > 0) {
|
||
$result = \AKSRT\Credits\Credits::add($user_id, $delta, 'admin', ['type' => 'user', 'id' => get_current_user_id()], $reason);
|
||
} else {
|
||
$result = \AKSRT\Credits\Credits::deduct($user_id, abs($delta), 'admin', ['type' => 'user', 'id' => get_current_user_id()], $reason);
|
||
}
|
||
|
||
if (is_wp_error($result)) {
|
||
set_transient('aksrt_admin_notice_' . get_current_user_id(), [[
|
||
'type' => 'error',
|
||
'msg' => $result->get_error_message(),
|
||
]], 30);
|
||
return;
|
||
}
|
||
|
||
set_transient('aksrt_admin_notice_' . get_current_user_id(), [[
|
||
'type' => 'success',
|
||
'msg' => sprintf(__('%s调整成功,当前余额:%d', 'aksrt'), $currency_name, (int) $result),
|
||
]], 30);
|
||
}
|
||
}
|