708 lines
21 KiB
PHP
708 lines
21 KiB
PHP
<?php
|
|
/**
|
|
* @package AKSRT
|
|
*/
|
|
|
|
namespace AKSRT\User;
|
|
|
|
defined('ABSPATH') || exit;
|
|
|
|
class Badges {
|
|
|
|
/**
|
|
* Owned badges storage (3.4.0-dev rewrite): user meta holds an
|
|
* associative map `slug => grant time (Y-m-d H:i:s)`, newest first.
|
|
*/
|
|
public const META_OWNED = 'aksrt_user_badges';
|
|
public const META_WEAR = 'aksrt_wear_badge';
|
|
|
|
private static $instance = null;
|
|
private static ?array $dictionary_cache = null;
|
|
/** @var array<int,array{owned_map:array<string,string>,owned:list<string>,wear:string}> */
|
|
private static array $user_context_cache = [];
|
|
/** @var array<string,array<string,mixed>> */
|
|
private static array $list_cache = [];
|
|
|
|
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_action('rest_api_init', [$this, 'register_routes']);
|
|
add_action('aksrt_badge_granted', [$this, 'on_badge_granted_notify'], 10, 3);
|
|
}
|
|
|
|
private static function clear_context(int $user_id = 0): void {
|
|
if ($user_id > 0) {
|
|
unset(self::$user_context_cache[$user_id]);
|
|
foreach (array_keys(self::$list_cache) as $key) {
|
|
if (strpos($key, $user_id . ':') === 0) {
|
|
unset(self::$list_cache[$key]);
|
|
}
|
|
}
|
|
do_action('aksrt_badges_changed', $user_id);
|
|
return;
|
|
}
|
|
self::$user_context_cache = [];
|
|
self::$list_cache = [];
|
|
}
|
|
|
|
public static function invalidate_cache(int $user_id = 0): void {
|
|
self::clear_context($user_id);
|
|
}
|
|
|
|
/**
|
|
* Notify user when a badge is granted (zibll user_add_medal message).
|
|
*/
|
|
public function on_badge_granted_notify(int $user_id, string $slug, int $actor = 0): void {
|
|
if ($user_id <= 0 || $slug === '') {
|
|
return;
|
|
}
|
|
$map = self::get_dictionary_map();
|
|
if (!isset($map[$slug])) {
|
|
return;
|
|
}
|
|
$label = (string) ($map[$slug]['label'] ?? $slug);
|
|
$title = sprintf(
|
|
/* translators: %s badge name */
|
|
__('恭喜您!获得新的徽章[%s]', 'aksrt'),
|
|
$label
|
|
);
|
|
$content = sprintf(
|
|
/* translators: %s badge name */
|
|
__('恭喜您获得徽章「%s」', 'aksrt'),
|
|
$label
|
|
);
|
|
if (class_exists('\AKSRT\Messages\Message')) {
|
|
\AKSRT\Messages\Message::get_instance()->send($user_id, 'medal', $content, [
|
|
'title' => $title,
|
|
'target_url' => function_exists('aksrt_get_user_center_url')
|
|
? aksrt_get_user_center_url()
|
|
: home_url('/'),
|
|
'slug' => $slug,
|
|
]);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Register the canonical JSON badge routes.
|
|
*/
|
|
public function register_routes(): void {
|
|
register_rest_route('aksrt/v1', '/users/(?P<id>\d+)/badges', [
|
|
'methods' => 'GET',
|
|
'callback' => [$this, 'rest_list'],
|
|
'permission_callback' => '__return_true',
|
|
'args' => [
|
|
'id' => [
|
|
'type' => 'integer',
|
|
'required' => true,
|
|
'sanitize_callback' => 'absint',
|
|
],
|
|
],
|
|
]);
|
|
|
|
|
|
register_rest_route('aksrt/v1', '/users/me/badges/wear', [
|
|
'methods' => 'POST',
|
|
'callback' => [$this, 'rest_wear'],
|
|
'permission_callback' => static function () {
|
|
return is_user_logged_in();
|
|
},
|
|
'args' => [
|
|
'slug' => [
|
|
'type' => 'string',
|
|
'required' => true,
|
|
'sanitize_callback' => 'sanitize_key',
|
|
],
|
|
],
|
|
]);
|
|
|
|
register_rest_route('aksrt/v1', '/users/(?P<id>\d+)/badges/grant', [
|
|
'methods' => 'POST',
|
|
'callback' => [$this, 'rest_grant'],
|
|
'permission_callback' => [$this, 'rest_can_manually_set'],
|
|
'args' => [
|
|
'slug' => [
|
|
'type' => 'string',
|
|
'required' => true,
|
|
'sanitize_callback' => 'sanitize_key',
|
|
],
|
|
'id' => [
|
|
'type' => 'integer',
|
|
'required' => true,
|
|
'sanitize_callback' => 'absint',
|
|
],
|
|
],
|
|
]);
|
|
|
|
register_rest_route('aksrt/v1', '/users/(?P<id>\d+)/badges/revoke', [
|
|
'methods' => 'POST',
|
|
'callback' => [$this, 'rest_revoke'],
|
|
'permission_callback' => [$this, 'rest_can_manually_set'],
|
|
'args' => [
|
|
'slug' => [
|
|
'type' => 'string',
|
|
'required' => true,
|
|
'sanitize_callback' => 'sanitize_key',
|
|
],
|
|
'id' => [
|
|
'type' => 'integer',
|
|
'required' => true,
|
|
'sanitize_callback' => 'absint',
|
|
],
|
|
],
|
|
]);
|
|
}
|
|
|
|
public function rest_can_manually_set(): bool {
|
|
return self::can_manually_set(get_current_user_id());
|
|
}
|
|
|
|
public static function enabled(): bool {
|
|
if (!class_exists('\AKSRT\Settings\Settings')) {
|
|
return false;
|
|
}
|
|
$settings = \AKSRT\Settings\Settings::get_user_identity_settings();
|
|
return !empty($settings['badge']['enabled']);
|
|
}
|
|
|
|
public static function public_display(): bool {
|
|
if (!class_exists('\AKSRT\Settings\Settings')) {
|
|
return false;
|
|
}
|
|
$settings = \AKSRT\Settings\Settings::get_user_identity_settings();
|
|
return !empty($settings['badge']['enabled']) && !empty($settings['badge']['public_display']);
|
|
}
|
|
|
|
/**
|
|
* @return list<array{slug:string,label:string,tone:string,desc:string,icon_url:string}>
|
|
*/
|
|
public static function get_dictionary(): array {
|
|
if (self::$dictionary_cache !== null) {
|
|
return self::$dictionary_cache;
|
|
}
|
|
if (!class_exists('\AKSRT\Settings\Settings')) {
|
|
return self::$dictionary_cache = [];
|
|
}
|
|
$settings = \AKSRT\Settings\Settings::get_user_identity_settings();
|
|
$dict = is_array($settings['badge']['dictionary'] ?? null) ? $settings['badge']['dictionary'] : [];
|
|
$out = [];
|
|
foreach ($dict as $item) {
|
|
$slug = sanitize_key((string) ($item['slug'] ?? ''));
|
|
if ($slug === '') {
|
|
continue;
|
|
}
|
|
$out[] = [
|
|
'slug' => $slug,
|
|
'label' => (string) ($item['label'] ?? $slug),
|
|
'tone' => (string) ($item['tone'] ?? 'neutral'),
|
|
'desc' => (string) ($item['desc'] ?? ''),
|
|
'icon_url' => (string) ($item['icon_url'] ?? ''),
|
|
];
|
|
}
|
|
return self::$dictionary_cache = $out;
|
|
}
|
|
|
|
/**
|
|
* @return array<string,array{slug:string,label:string,tone:string,desc:string,icon_url:string}>
|
|
*/
|
|
public static function get_dictionary_map(): array {
|
|
static $map = null;
|
|
if ($map !== null) {
|
|
return $map;
|
|
}
|
|
$map = [];
|
|
foreach (self::get_dictionary() as $item) {
|
|
$map[$item['slug']] = $item;
|
|
}
|
|
return $map;
|
|
}
|
|
|
|
/** @return array{owned_map:array<string,string>,owned:list<string>,wear:string} */
|
|
private static function user_context(int $user_id): array {
|
|
if ($user_id <= 0) {
|
|
return ['owned_map' => [], 'owned' => [], 'wear' => ''];
|
|
}
|
|
if (isset(self::$user_context_cache[$user_id])) {
|
|
return self::$user_context_cache[$user_id];
|
|
}
|
|
|
|
$raw = get_user_meta($user_id, self::META_OWNED, true);
|
|
$raw = is_array($raw) ? $raw : [];
|
|
$map = self::get_dictionary_map();
|
|
$owned_map = [];
|
|
foreach ($raw as $slug => $time) {
|
|
$slug = sanitize_key((string) $slug);
|
|
if ($slug === '' || !isset($map[$slug])) {
|
|
continue;
|
|
}
|
|
$owned_map[$slug] = is_string($time) ? $time : '';
|
|
}
|
|
$owned = array_keys($owned_map);
|
|
$wear = sanitize_key((string) get_user_meta($user_id, self::META_WEAR, true));
|
|
if ($wear === '') {
|
|
$wear = $owned[0] ?? '';
|
|
} elseif (!in_array($wear, $owned, true)) {
|
|
$wear = '';
|
|
}
|
|
|
|
return self::$user_context_cache[$user_id] = [
|
|
'owned_map' => $owned_map,
|
|
'owned' => $owned,
|
|
'wear' => $wear,
|
|
];
|
|
}
|
|
|
|
/**
|
|
* Owned badge map: slug => grant time (`Y-m-d H:i:s`), newest first.
|
|
*
|
|
* @return array<string,string>
|
|
*/
|
|
public static function get_owned_map(int $user_id): array {
|
|
return self::user_context($user_id)['owned_map'];
|
|
}
|
|
|
|
/**
|
|
* Owned badge slugs, newest first.
|
|
*
|
|
* @return list<string>
|
|
*/
|
|
public static function get_owned(int $user_id): array {
|
|
return self::user_context($user_id)['owned'];
|
|
}
|
|
|
|
/**
|
|
* Replace the owned set; existing slugs keep their grant time, new slugs
|
|
* are stamped with the current time. Order follows the input list.
|
|
*
|
|
* @param list<string> $slugs
|
|
*/
|
|
public static function set_owned(int $user_id, array $slugs): void {
|
|
if ($user_id <= 0) {
|
|
return;
|
|
}
|
|
$map = self::get_dictionary_map();
|
|
$existing = self::get_owned_map($user_id);
|
|
$now = current_time('Y-m-d H:i:s');
|
|
$clean = [];
|
|
foreach ($slugs as $slug) {
|
|
$slug = sanitize_key((string) $slug);
|
|
if ($slug === '' || !isset($map[$slug]) || isset($clean[$slug])) {
|
|
continue;
|
|
}
|
|
$clean[$slug] = $existing[$slug] ?? $now;
|
|
}
|
|
update_user_meta($user_id, self::META_OWNED, $clean);
|
|
|
|
$wear = self::get_wear($user_id);
|
|
if ($wear !== '' && !isset($clean[$wear])) {
|
|
delete_user_meta($user_id, self::META_WEAR);
|
|
}
|
|
self::clear_context($user_id);
|
|
}
|
|
|
|
public static function get_wear(int $user_id): string {
|
|
return self::user_context($user_id)['wear'];
|
|
}
|
|
|
|
/**
|
|
* @return true|\WP_Error
|
|
*/
|
|
public static function wear(int $user_id, string $slug) {
|
|
if ($user_id <= 0) {
|
|
return new \WP_Error('aksrt_badge_user', __('无效用户', 'aksrt'), ['status' => 400]);
|
|
}
|
|
if (!self::enabled()) {
|
|
return new \WP_Error('aksrt_badge_disabled', __('徽章系统未启用', 'aksrt'), ['status' => 403]);
|
|
}
|
|
$slug = sanitize_key($slug);
|
|
$map = self::get_dictionary_map();
|
|
if ($slug === '' || !isset($map[$slug])) {
|
|
return new \WP_Error('aksrt_badge_unknown', __('徽章不存在', 'aksrt'), ['status' => 404]);
|
|
}
|
|
$owned = self::get_owned($user_id);
|
|
if (!in_array($slug, $owned, true)) {
|
|
return new \WP_Error('aksrt_badge_not_owned', __('尚未获得该徽章', 'aksrt'), ['status' => 403]);
|
|
}
|
|
update_user_meta($user_id, self::META_WEAR, $slug);
|
|
self::clear_context($user_id);
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* @return true|\WP_Error
|
|
*/
|
|
public static function grant(int $user_id, string $slug, int $actor = 0) {
|
|
if ($user_id <= 0) {
|
|
return new \WP_Error('aksrt_badge_user', __('无效用户', 'aksrt'), ['status' => 400]);
|
|
}
|
|
if (!self::enabled()) {
|
|
return new \WP_Error('aksrt_badge_disabled', __('徽章系统未启用', 'aksrt'), ['status' => 403]);
|
|
}
|
|
$slug = sanitize_key($slug);
|
|
$map = self::get_dictionary_map();
|
|
if ($slug === '' || !isset($map[$slug])) {
|
|
return new \WP_Error('aksrt_badge_unknown', __('徽章不存在', 'aksrt'), ['status' => 404]);
|
|
}
|
|
$owned = self::get_owned($user_id);
|
|
if (in_array($slug, $owned, true)) {
|
|
return true;
|
|
}
|
|
// Newest first (zibll medal ordering); grant time stamped by set_owned.
|
|
self::set_owned($user_id, array_merge([$slug], $owned));
|
|
/**
|
|
* Fires after a badge is granted.
|
|
*
|
|
* @param int $user_id
|
|
* @param string $slug
|
|
* @param int $actor
|
|
*/
|
|
do_action('aksrt_badge_granted', $user_id, $slug, $actor);
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* @return true|\WP_Error
|
|
*/
|
|
public static function revoke(int $user_id, string $slug, int $actor = 0) {
|
|
if ($user_id <= 0) {
|
|
return new \WP_Error('aksrt_badge_user', __('无效用户', 'aksrt'), ['status' => 400]);
|
|
}
|
|
$slug = sanitize_key($slug);
|
|
$owned = self::get_owned($user_id);
|
|
$owned = array_values(array_filter($owned, static function ($s) use ($slug) {
|
|
return $s !== $slug;
|
|
}));
|
|
self::set_owned($user_id, $owned);
|
|
/**
|
|
* @param int $user_id
|
|
* @param string $slug
|
|
* @param int $actor
|
|
*/
|
|
do_action('aksrt_badge_revoked', $user_id, $slug, $actor);
|
|
return true;
|
|
}
|
|
|
|
public static function can_manually_set(int $actor = 0): bool {
|
|
$actor = $actor > 0 ? $actor : get_current_user_id();
|
|
if ($actor <= 0) {
|
|
return false;
|
|
}
|
|
if (user_can($actor, 'manage_options')) {
|
|
return true;
|
|
}
|
|
if (class_exists('\AKSRT\Settings\Capabilities')) {
|
|
return \AKSRT\Settings\Capabilities::user_can('medal_manually_set', $actor);
|
|
}
|
|
return false;
|
|
}
|
|
|
|
/**
|
|
* Public DTO for REST / modals: catalog grouped by category with per-item
|
|
* ownership state. Unearned badges are only exposed to the owner (or to
|
|
* managers via $include_private), matching zibll visibility rules.
|
|
*
|
|
* @return array{enabled:bool,public_display:bool,is_self:bool,can_wear:bool,user_id:int,wear:string,count:int,show_unearned:bool,categories:list<array{cat_name:string,items:list<array{slug:string,label:string,desc:string,icon_url:string,owned:bool,wear:bool,time:string}>}>}
|
|
*/
|
|
public static function list_for_user(int $user_id, bool $include_private = false): array {
|
|
$enabled = self::enabled();
|
|
$public = self::public_display();
|
|
$viewer = get_current_user_id();
|
|
$is_self = $viewer > 0 && $viewer === $user_id;
|
|
$is_manager = self::can_manually_set($viewer);
|
|
$cache_key = $user_id . ':' . ($include_private ? 'private' : 'public') . ':' . $viewer . ':' . (int) $enabled . ':' . (int) $public;
|
|
if (array_key_exists($cache_key, self::$list_cache)) {
|
|
return self::$list_cache[$cache_key];
|
|
}
|
|
$can_see = $include_private || $public || $is_self || $is_manager;
|
|
|
|
if (!$enabled || !$can_see || $user_id <= 0) {
|
|
return self::$list_cache[$cache_key] = [
|
|
'enabled' => $enabled,
|
|
'public_display' => $public,
|
|
'is_self' => $is_self,
|
|
'can_wear' => $is_self,
|
|
'user_id' => $user_id,
|
|
'wear' => '',
|
|
'count' => 0,
|
|
'show_unearned' => false,
|
|
'categories' => [],
|
|
];
|
|
}
|
|
|
|
$settings = \AKSRT\Settings\Settings::get_user_identity_settings();
|
|
$medal = is_array($settings['medal'] ?? null) ? $settings['medal'] : [];
|
|
$catalog = is_array($medal['categories'] ?? null) ? $medal['categories'] : [];
|
|
|
|
$context = self::user_context($user_id);
|
|
$owned_map = $context['owned_map'];
|
|
$wear = $context['wear'];
|
|
$show_private = $include_private || $is_self || $is_manager;
|
|
$show_unearned = $show_private;
|
|
|
|
$categories = [];
|
|
foreach ($catalog as $cat) {
|
|
$cat_name = (string) ($cat['cat_name'] ?? '');
|
|
$rows = is_array($cat['items'] ?? null) ? $cat['items'] : [];
|
|
$has_rows = [];
|
|
$no_rows = [];
|
|
foreach ($rows as $row) {
|
|
if (!is_array($row)) {
|
|
continue;
|
|
}
|
|
$slug = sanitize_key((string) ($row['slug'] ?? ''));
|
|
if ($slug === '') {
|
|
continue;
|
|
}
|
|
$has = isset($owned_map[$slug]);
|
|
if (!$has && !$show_unearned) {
|
|
continue;
|
|
}
|
|
$item = [
|
|
'slug' => $slug,
|
|
'label' => (string) ($row['name'] ?? $slug),
|
|
'desc' => (string) ($row['desc'] ?? ''),
|
|
'icon_url' => (string) ($row['icon'] ?? ''),
|
|
'owned' => $has,
|
|
'wear' => $has && $slug === $wear,
|
|
'time' => $has && $show_private ? (string) $owned_map[$slug] : '',
|
|
];
|
|
// Owned first within each category (zibll is-has / no-has order).
|
|
if ($has) {
|
|
$has_rows[] = $item;
|
|
} else {
|
|
$no_rows[] = $item;
|
|
}
|
|
}
|
|
$items = array_merge($has_rows, $no_rows);
|
|
if (!$items) {
|
|
continue;
|
|
}
|
|
$categories[] = [
|
|
'cat_name' => $cat_name,
|
|
'items' => $items,
|
|
];
|
|
}
|
|
|
|
return self::$list_cache[$cache_key] = [
|
|
'enabled' => true,
|
|
'public_display' => $public,
|
|
'is_self' => $is_self,
|
|
'can_wear' => $is_self,
|
|
'user_id' => $user_id,
|
|
'wear' => $wear,
|
|
'count' => count($owned_map),
|
|
'show_unearned' => $show_unearned,
|
|
'categories' => $categories,
|
|
];
|
|
}
|
|
|
|
public function rest_list(\WP_REST_Request $request) {
|
|
$user_id = (int) $request['id'];
|
|
if ($user_id <= 0 || !get_userdata($user_id)) {
|
|
return new \WP_Error('aksrt_badge_user', __('用户不存在', 'aksrt'), ['status' => 404]);
|
|
}
|
|
$data = self::list_for_user($user_id);
|
|
$response = rest_ensure_response(['success' => true, 'data' => $data]);
|
|
return self::rest_no_store($response);
|
|
}
|
|
|
|
public function rest_wear(\WP_REST_Request $request) {
|
|
$params = $request->get_json_params();
|
|
if (!is_array($params)) {
|
|
$params = $request->get_params();
|
|
}
|
|
$slug = sanitize_key((string) ($params['slug'] ?? ''));
|
|
$result = self::wear(get_current_user_id(), $slug);
|
|
if (is_wp_error($result)) {
|
|
return $result;
|
|
}
|
|
return self::rest_no_store(rest_ensure_response([
|
|
'success' => true,
|
|
'data' => self::list_for_user(get_current_user_id(), true),
|
|
'message' => __('已佩戴徽章', 'aksrt'),
|
|
]));
|
|
}
|
|
|
|
public function rest_grant(\WP_REST_Request $request) {
|
|
$user_id = (int) $request['id'];
|
|
if ($user_id <= 0 || !get_userdata($user_id)) {
|
|
return new \WP_Error('aksrt_badge_user', __('用户不存在', 'aksrt'), ['status' => 404]);
|
|
}
|
|
$params = $request->get_json_params();
|
|
if (!is_array($params)) {
|
|
$params = $request->get_params();
|
|
}
|
|
$slug = sanitize_key((string) ($params['slug'] ?? ''));
|
|
$result = self::grant($user_id, $slug, get_current_user_id());
|
|
if (is_wp_error($result)) {
|
|
return $result;
|
|
}
|
|
return self::rest_no_store(rest_ensure_response([
|
|
'success' => true,
|
|
'data' => self::list_for_user($user_id, true),
|
|
'message' => __('已授予徽章', 'aksrt'),
|
|
]));
|
|
}
|
|
|
|
public function rest_revoke(\WP_REST_Request $request) {
|
|
$user_id = (int) $request['id'];
|
|
if ($user_id <= 0 || !get_userdata($user_id)) {
|
|
return new \WP_Error('aksrt_badge_user', __('用户不存在', 'aksrt'), ['status' => 404]);
|
|
}
|
|
$params = $request->get_json_params();
|
|
if (!is_array($params)) {
|
|
$params = $request->get_params();
|
|
}
|
|
$slug = sanitize_key((string) ($params['slug'] ?? ''));
|
|
$result = self::revoke($user_id, $slug, get_current_user_id());
|
|
if (is_wp_error($result)) {
|
|
return $result;
|
|
}
|
|
return self::rest_no_store(rest_ensure_response([
|
|
'success' => true,
|
|
'data' => self::list_for_user($user_id, true),
|
|
'message' => __('已收回徽章', 'aksrt'),
|
|
]));
|
|
}
|
|
|
|
private static function rest_no_store(\WP_REST_Response $response): \WP_REST_Response {
|
|
$response->header('Cache-Control', 'no-store, private');
|
|
$response->header('Pragma', 'no-cache');
|
|
return $response;
|
|
}
|
|
|
|
|
|
/**
|
|
* Auto-grant medals whose get_type metrics meet get_val (skip manually_add).
|
|
*/
|
|
public static function auto_evaluate(int $user_id): void {
|
|
if ($user_id <= 0 || !self::enabled() || !class_exists('\AKSRT\Settings\Settings')) {
|
|
return;
|
|
}
|
|
$settings = \AKSRT\Settings\Settings::get_user_identity_settings();
|
|
$medal = is_array($settings['medal'] ?? null) ? $settings['medal'] : [];
|
|
$items = is_array($medal['items_by_slug'] ?? null) ? $medal['items_by_slug'] : [];
|
|
if (!$items) {
|
|
// Fall back to dictionary only — no get_type → nothing to auto-grant.
|
|
return;
|
|
}
|
|
$owned = self::get_owned($user_id);
|
|
$owned_map = array_fill_keys($owned, true);
|
|
foreach ($items as $slug => $item) {
|
|
if (!is_array($item)) {
|
|
continue;
|
|
}
|
|
$slug = sanitize_key((string) $slug);
|
|
if ($slug === '' || !empty($owned_map[$slug])) {
|
|
continue;
|
|
}
|
|
$type = sanitize_key((string) ($item['get_type'] ?? 'manually_add'));
|
|
if ($type === '' || $type === 'manually_add') {
|
|
continue;
|
|
}
|
|
$need = max(0, (float) ($item['get_val'] ?? 0));
|
|
$have = (float) self::metric_value($user_id, $type);
|
|
if ($have >= $need) {
|
|
self::grant($user_id, $slug, 0);
|
|
$owned_map[$slug] = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Numeric metric for a medal get_type.
|
|
*/
|
|
public static function metric_value(int $user_id, string $type): float {
|
|
switch ($type) {
|
|
case 'registration_time':
|
|
$user = get_userdata($user_id);
|
|
if (!$user) {
|
|
return 0.0;
|
|
}
|
|
$reg = get_date_from_gmt($user->user_registered, 'Y-m-d H:i:s');
|
|
$now = current_time('Y-m-d H:i:s');
|
|
return max(0.0, (strtotime($now) - strtotime($reg)) / 86400);
|
|
|
|
case 'checkin_all_day':
|
|
// Prefer dedicated check-in total meta if present.
|
|
$v = get_user_meta($user_id, 'aksrt_checkin_all_day', true);
|
|
if ($v === '' || $v === false) {
|
|
$v = get_user_meta($user_id, 'checkin_all_day', true);
|
|
}
|
|
return max(0.0, (float) $v);
|
|
|
|
case 'new_post':
|
|
return (float) count_user_posts($user_id, 'post', true);
|
|
|
|
case 'comment':
|
|
$counts = get_comments(['user_id' => $user_id, 'count' => true, 'status' => 'approve']);
|
|
return (float) $counts;
|
|
|
|
case 'followed':
|
|
if (class_exists('AKSRT\\User\\Follow')) {
|
|
return (float) Follow::get_instance()->get_follower_count($user_id);
|
|
}
|
|
return 0.0;
|
|
|
|
case 'like':
|
|
return (float) get_user_meta($user_id, 'aksrt_posts_like_sum', true);
|
|
|
|
case 'comment_like':
|
|
return (float) get_user_meta($user_id, 'aksrt_comment_like_sum', true);
|
|
|
|
case 'views':
|
|
$sum = (float) get_user_meta($user_id, 'aksrt_posts_views_sum', true);
|
|
if ($sum > 0) {
|
|
return $sum;
|
|
}
|
|
// Lazy backfill once from postmeta.
|
|
global $wpdb;
|
|
$sum = (float) $wpdb->get_var($wpdb->prepare(
|
|
"SELECT COALESCE(SUM(CAST(pm.meta_value AS UNSIGNED)),0)
|
|
FROM {$wpdb->posts} p
|
|
INNER JOIN {$wpdb->postmeta} pm ON pm.post_id = p.ID AND pm.meta_key = 'aksrt_post_views'
|
|
WHERE p.post_author = %d AND p.post_type = 'post' AND p.post_status = 'publish'",
|
|
$user_id
|
|
));
|
|
if ($sum > 0) {
|
|
update_user_meta($user_id, 'aksrt_posts_views_sum', $sum);
|
|
}
|
|
return $sum;
|
|
|
|
case 'pay_income':
|
|
return (float) get_user_meta($user_id, 'aksrt_pay_income_sum', true);
|
|
|
|
case 'pay_rebate':
|
|
return (float) get_user_meta($user_id, 'aksrt_pay_rebate_sum', true);
|
|
|
|
default:
|
|
return 0.0;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @param string $user_login
|
|
* @param \WP_User $user
|
|
*/
|
|
public function on_login($user_login, $user): void {
|
|
$user_id = $user instanceof \WP_User ? (int) $user->ID : 0;
|
|
if ($user_id > 0) {
|
|
self::auto_evaluate($user_id);
|
|
}
|
|
}
|
|
|
|
public function on_current_user_probe(): void {
|
|
$uid = get_current_user_id();
|
|
if ($uid > 0) {
|
|
self::auto_evaluate($uid);
|
|
}
|
|
}
|
|
|
|
}
|