refactor(referral): drop admin referral-code ops page, self-serve codes
- 移除「用户 → 推广码运营」页面(trait、子菜单、admin SPA 跳转路由) - 移除管理端 REST:/revenue/codes 列表与 generate/disable/regenerate 变更接口, 清理 rest_codes/rest_mutate_code/can_manage_codes/list_codes 死代码 - 推广码改为注册即得:bind_registered_user 内生成唯一推广码,无需后台管理 - 推广配置统一收敛到「支付&付费 → 推广返佣」设置段(现状保留,无改动) - 契约测试固化:无管理路由/页面残留 + 注册即得码
This commit is contained in:
1 parent
3dd3f71c97
commit
a5f0484466
5 files changed
+23
-328
No files matched your search
@@ -116,6 +116,11 @@ final class ReferralAttribution {
|
||||
}
|
||||
|
||||
public function bind_registered_user(int $invitee_id): void {
|
||||
// 每个新用户注册即拥有唯一推广码(无需后台生成或管理)。
|
||||
if ($invitee_id > 0) {
|
||||
$this->generate_code($invitee_id, false);
|
||||
}
|
||||
|
||||
if ($invitee_id <= 0 || !$this->feature_enabled()) {
|
||||
self::$pending_registration_ref = '';
|
||||
return;
|
||||
@@ -353,39 +358,6 @@ final class ReferralAttribution {
|
||||
'callback' => [$this, 'rest_me'],
|
||||
'permission_callback' => static fn() => is_user_logged_in(),
|
||||
]);
|
||||
|
||||
register_rest_route('aksrt/v1', '/revenue/codes', [
|
||||
'methods' => \WP_REST_Server::READABLE,
|
||||
'callback' => [$this, 'rest_codes'],
|
||||
'permission_callback' => [$this, 'can_manage_codes'],
|
||||
'args' => [
|
||||
'page' => ['type' => 'integer', 'default' => 1, 'minimum' => 1],
|
||||
'per_page' => ['type' => 'integer', 'default' => 20, 'minimum' => 1, 'maximum' => 100],
|
||||
'search' => ['type' => 'string', 'default' => '', 'sanitize_callback' => 'sanitize_text_field'],
|
||||
'status' => [
|
||||
'type' => 'string',
|
||||
'default' => 'all',
|
||||
'enum' => ['all', self::STATUS_ACTIVE, self::STATUS_DISABLED, 'missing'],
|
||||
],
|
||||
],
|
||||
]);
|
||||
|
||||
foreach (['generate', 'disable', 'regenerate'] as $action) {
|
||||
register_rest_route('aksrt/v1', '/revenue/codes/(?P<user_id>\d+)/' . $action, [
|
||||
'methods' => \WP_REST_Server::CREATABLE,
|
||||
'callback' => function (\WP_REST_Request $request) use ($action) {
|
||||
return $this->rest_mutate_code($request, $action);
|
||||
},
|
||||
'permission_callback' => [$this, 'can_manage_codes'],
|
||||
'args' => [
|
||||
'user_id' => ['type' => 'integer', 'required' => true, 'minimum' => 1],
|
||||
],
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
public function can_manage_codes(): bool {
|
||||
return current_user_can('edit_users');
|
||||
}
|
||||
|
||||
public function rest_me(): \WP_REST_Response {
|
||||
@@ -418,127 +390,6 @@ final class ReferralAttribution {
|
||||
], 200);
|
||||
}
|
||||
|
||||
public function rest_codes(\WP_REST_Request $request): \WP_REST_Response {
|
||||
return new \WP_REST_Response($this->list_codes([
|
||||
'page' => (int) $request['page'],
|
||||
'per_page' => (int) $request['per_page'],
|
||||
'search' => (string) $request['search'],
|
||||
'status' => (string) $request['status'],
|
||||
]), 200);
|
||||
}
|
||||
|
||||
public function rest_mutate_code(\WP_REST_Request $request, string $action) {
|
||||
$user_id = (int) $request['user_id'];
|
||||
if (!get_userdata($user_id)) {
|
||||
return new \WP_Error('aksrt_ref_user_missing', __('用户不存在', 'aksrt'), ['status' => 404]);
|
||||
}
|
||||
|
||||
$record = $this->code_record($user_id);
|
||||
if ($action === 'generate') {
|
||||
if ($record['status'] !== 'missing') {
|
||||
return new \WP_Error('aksrt_ref_exists', __('该用户已有推广码', 'aksrt'), ['status' => 409]);
|
||||
}
|
||||
$code = $this->generate_code($user_id, false);
|
||||
} elseif ($action === 'disable') {
|
||||
if ($record['status'] !== self::STATUS_ACTIVE) {
|
||||
return new \WP_Error('aksrt_ref_not_active', __('推广码当前不可禁用', 'aksrt'), ['status' => 409]);
|
||||
}
|
||||
update_user_meta($user_id, self::META_STATUS, self::STATUS_DISABLED);
|
||||
if ($this->code_record($user_id)['status'] !== self::STATUS_DISABLED) {
|
||||
return new \WP_Error('aksrt_ref_disable_failed', __('推广码禁用失败', 'aksrt'), ['status' => 500]);
|
||||
}
|
||||
$code = $record['code'];
|
||||
} elseif ($action === 'regenerate') {
|
||||
if ($record['status'] === 'missing') {
|
||||
return new \WP_Error('aksrt_ref_missing', __('该用户尚未生成推广码', 'aksrt'), ['status' => 409]);
|
||||
}
|
||||
$code = $this->generate_code($user_id, true);
|
||||
} else {
|
||||
return new \WP_Error('aksrt_ref_action', __('不支持的推广码操作', 'aksrt'), ['status' => 400]);
|
||||
}
|
||||
|
||||
if ($code === '') {
|
||||
return new \WP_Error('aksrt_ref_generate_failed', __('推广码生成失败', 'aksrt'), ['status' => 500]);
|
||||
}
|
||||
return rest_ensure_response([
|
||||
'user_id' => $user_id,
|
||||
'ref' => $code,
|
||||
'status' => $action === 'disable' ? self::STATUS_DISABLED : self::STATUS_ACTIVE,
|
||||
]);
|
||||
}
|
||||
|
||||
private function list_codes(array $args): array {
|
||||
global $wpdb;
|
||||
$page = max(1, (int) ($args['page'] ?? 1));
|
||||
$per_page = max(1, min(100, (int) ($args['per_page'] ?? 20)));
|
||||
$status = (string) ($args['status'] ?? 'all');
|
||||
$search = trim((string) ($args['search'] ?? ''));
|
||||
$where = ['1=1'];
|
||||
$params = [];
|
||||
|
||||
if ($search !== '') {
|
||||
$like = '%' . $wpdb->esc_like($search) . '%';
|
||||
$where[] = '(u.user_login LIKE %s OR u.display_name LIKE %s OR u.user_email LIKE %s OR code.meta_value LIKE %s)';
|
||||
array_push($params, $like, $like, $like, $like);
|
||||
}
|
||||
if ($status === 'missing') {
|
||||
$where[] = "(code.meta_value IS NULL OR code.meta_value = '')";
|
||||
} elseif ($status === self::STATUS_DISABLED) {
|
||||
$where[] = "code.meta_value <> '' AND state.meta_value = 'disabled'";
|
||||
} elseif ($status === self::STATUS_ACTIVE) {
|
||||
$where[] = "code.meta_value <> '' AND (state.meta_value IS NULL OR state.meta_value <> 'disabled')";
|
||||
}
|
||||
|
||||
$where_sql = implode(' AND ', $where);
|
||||
$joins = $wpdb->prepare(
|
||||
"LEFT JOIN {$wpdb->usermeta} code ON code.user_id=u.ID AND code.meta_key=%s LEFT JOIN {$wpdb->usermeta} state ON state.user_id=u.ID AND state.meta_key=%s LEFT JOIN {$wpdb->usermeta} created ON created.user_id=u.ID AND created.meta_key=%s",
|
||||
self::META_CODE,
|
||||
self::META_STATUS,
|
||||
self::META_CREATED
|
||||
);
|
||||
$count_sql = "SELECT COUNT(DISTINCT u.ID) FROM {$wpdb->users} u {$joins} WHERE {$where_sql}";
|
||||
$total = (int) ($params
|
||||
? $wpdb->get_var($wpdb->prepare($count_sql, $params))
|
||||
: $wpdb->get_var($count_sql));
|
||||
|
||||
$relation_table = \AKSRT\Core\DBMigrator::table('affiliate_relation');
|
||||
$ledger_table = \AKSRT\Core\DBMigrator::table('commission_ledger');
|
||||
$stats_joins = "LEFT JOIN (SELECT promoter_id, COUNT(*) AS bound_count FROM {$relation_table} WHERE status='bound' GROUP BY promoter_id) relation_stats ON relation_stats.promoter_id=u.ID "
|
||||
. "LEFT JOIN (SELECT promoter_id, COUNT(DISTINCT source_user_id) AS effective_payers FROM {$ledger_table} WHERE original_amount > 0 GROUP BY promoter_id) payer_stats ON payer_stats.promoter_id=u.ID";
|
||||
$list_sql = "SELECT DISTINCT u.ID, u.user_login, u.display_name, u.user_email, code.meta_value AS ref, state.meta_value AS ref_status, created.meta_value AS created_at, "
|
||||
. "COALESCE(relation_stats.bound_count, 0) AS bound_count, COALESCE(payer_stats.effective_payers, 0) AS effective_payers "
|
||||
. "FROM {$wpdb->users} u {$joins} {$stats_joins} WHERE {$where_sql} ORDER BY u.ID DESC LIMIT %d OFFSET %d";
|
||||
$list_params = array_merge($params, [$per_page, ($page - 1) * $per_page]);
|
||||
$rows = $wpdb->get_results($wpdb->prepare($list_sql, $list_params), ARRAY_A);
|
||||
|
||||
$items = [];
|
||||
foreach ((array) $rows as $row) {
|
||||
$user_id = (int) $row['ID'];
|
||||
$ref = sanitize_key((string) ($row['ref'] ?? ''));
|
||||
$items[] = [
|
||||
'user' => [
|
||||
'id' => $user_id,
|
||||
'login' => (string) $row['user_login'],
|
||||
'name' => (string) $row['display_name'],
|
||||
'email' => (string) $row['user_email'],
|
||||
],
|
||||
'ref' => $ref,
|
||||
'status' => $ref === '' ? 'missing' : ((string) ($row['ref_status'] ?? '') === self::STATUS_DISABLED ? self::STATUS_DISABLED : self::STATUS_ACTIVE),
|
||||
'created_at' => (string) ($row['created_at'] ?? ''),
|
||||
'bound_count' => (int) ($row['bound_count'] ?? 0),
|
||||
'effective_payers' => (int) ($row['effective_payers'] ?? 0),
|
||||
];
|
||||
}
|
||||
|
||||
return [
|
||||
'items' => $items,
|
||||
'total' => $total,
|
||||
'page' => $page,
|
||||
'per_page' => $per_page,
|
||||
'max_pages' => max(1, (int) ceil($total / $per_page)),
|
||||
];
|
||||
}
|
||||
|
||||
private function generate_code(int $user_id, bool $force): string {
|
||||
if ($user_id <= 0) {
|
||||
return '';
|
||||
|
||||
Reference in new issue
Block a user