'GET', 'callback' => [$this, 'rest_today'], 'permission_callback' => function () { return is_user_logged_in(); }, ]); register_rest_route('aksrt/v1', '/signin', [ 'methods' => 'POST', 'callback' => [$this, 'rest_signin'], 'permission_callback' => function () { return is_user_logged_in(); }, ]); register_rest_route('aksrt/v1', '/signin/history', [ 'methods' => 'GET', 'callback' => [$this, 'rest_history'], 'permission_callback' => function () { return is_user_logged_in(); }, ]); register_rest_route('aksrt/v1', '/signin/details', [ 'methods' => 'GET', 'callback' => [$this, 'rest_details_html'], 'permission_callback' => function () { return is_user_logged_in(); }, ]); } /** GET /signin/today */ public function rest_today(\WP_REST_Request $request) { $user_id = get_current_user_id(); $signin_cfg = class_exists('\AKSRT\Settings\Settings') ? \AKSRT\Settings\Settings::get_signin_reward_settings() : [ 'enabled' => false, 'base' => 0, 'streak' => [], ]; $today_str = $this->today_date(); $rec = $this->get_record($user_id, $today_str); $current_streak = $this->compute_current_streak($user_id, $today_str); $next_streak = $current_streak + ($rec ? 0 : 1); return new \WP_REST_Response([ 'enabled' => !empty($signin_cfg['enabled']), 'signed' => (bool) $rec, 'streak' => $current_streak, 'reward_today' => $rec ? (int) $rec['reward'] : 0, 'reward_if_signin' => $rec ? 0 : $this->calc_reward($next_streak, $signin_cfg), 'date' => $today_str, ], 200); } /** POST /signin */ public function rest_signin(\WP_REST_Request $request) { global $wpdb; $user_id = get_current_user_id(); $signin_cfg = class_exists('\AKSRT\Settings\Settings') ? \AKSRT\Settings\Settings::get_signin_reward_settings() : [ 'enabled' => false, 'base' => 0, 'streak' => [], ]; if (empty($signin_cfg['enabled'])) { return new \WP_Error('aksrt_signin_disabled', __('签到功能未启用', 'aksrt'), ['status' => 403]); } $today_str = $this->today_date(); if ($this->get_record($user_id, $today_str)) { return new \WP_Error('aksrt_signin_dup', __('今日已签到', 'aksrt'), ['status' => 409]); } // 人机验证(若启用) if (function_exists('aksrt_require_captcha')) { $body = $request->get_json_params(); $cap_payload = is_array($body) && isset($body['captcha']) && is_array($body['captcha']) ? $body['captcha'] : (is_array($body) ? $body : []); $verify = aksrt_require_captcha('signin', $cap_payload); if ($verify !== true) { $msg = is_string($verify) ? $verify : __('人机验证失败', 'aksrt'); return new \WP_Error('aksrt_captcha_failed', $msg, ['status' => 400]); } } $current_streak = $this->compute_current_streak($user_id, $today_str); $new_streak = $current_streak + 1; $reward = $this->calc_reward($new_streak, $signin_cfg); $table = \AKSRT\Core\DBMigrator::table('signin_log'); if (!$table) { return new \WP_Error('aksrt_signin_db', __('数据表不存在', 'aksrt'), ['status' => 500]); } $inserted = $wpdb->insert( $table, [ 'user_id' => $user_id, 'signin_date' => $today_str, 'streak' => $new_streak, 'reward' => $reward, ], ['%d', '%s', '%d', '%d'] ); if ($inserted === false) { return new \WP_Error('aksrt_signin_dup', __('签到失败', 'aksrt'), ['status' => 409]); } do_action('aksrt_user_signed_in', $user_id, $reward); if ($reward > 0) { if (class_exists('AKSRT\\Credits\\PointsFree')) { $granted = PointsFree::grant_amount($user_id, $reward, 'sign_in', ['type' => 'user', 'id' => $user_id]); if (is_wp_error($granted)) { if (class_exists('AKSRT\\Credits\\Credits')) { Credits::add($user_id, $reward, 'signin', ['type' => 'user', 'id' => $user_id]); } } else { $reward = (int) $granted; } } elseif (class_exists('AKSRT\\Credits\\Credits')) { Credits::add($user_id, $reward, 'signin', ['type' => 'user', 'id' => $user_id]); } } // Keep lightweight meta for header/badge consumers (zibll checkin_all_day). update_user_meta($user_id, 'aksrt_signin_' . $today_str, 1); $all_day = (int) get_user_meta($user_id, 'aksrt_checkin_all_day', true); if ($all_day <= 0) { // Fallback to zibll key if migrated users only have that meta. $all_day = (int) get_user_meta($user_id, 'checkin_all_day', true); } $all_day = $all_day + 1; update_user_meta($user_id, 'aksrt_checkin_all_day', $all_day); update_user_meta($user_id, 'checkin_all_day', $all_day); // zibll alias update_user_meta($user_id, 'aksrt_checkin_continuous_day', $new_streak); update_user_meta($user_id, 'checkin_continuous_day', $new_streak); // zibll alias do_action('aksrt_checkin_done', $user_id); return new \WP_REST_Response([ 'success' => true, 'message' => sprintf( /* translators: 1: reward points 2: streak days */ __('签到成功,积分 +%1$d(连续 %2$d 天)', 'aksrt'), (int) $reward, (int) $new_streak ), 'streak' => $new_streak, 'reward' => $reward, 'balance' => class_exists('AKSRT\Credits\Credits') ? Credits::balance($user_id) : 0, ], 200); } /** GET /signin/history — last N days for calendar/modals. */ public function rest_history(\WP_REST_Request $request) { $user_id = get_current_user_id(); $days = max(7, min(90, (int) ($request->get_param('days') ?: 30))); $rows = $this->get_recent_signins($user_id, $days); $dates = []; foreach ($rows as $row) { if (!empty($row['signin_date'])) { $dates[] = (string) $row['signin_date']; } } $today = $this->today_date(); return new \WP_REST_Response([ 'dates' => $dates, 'streak' => $this->compute_current_streak($user_id, $today), 'total' => $this->get_total_days($user_id), 'signed' => (bool) $this->get_record($user_id, $today), 'all_day' => (int) get_user_meta($user_id, 'aksrt_checkin_all_day', true) ?: $this->get_total_days($user_id), ], 200); } /** GET /signin/details — HTML fragment for remote modal. */ public function rest_details_html(\WP_REST_Request $request) { $html = $this->render_details_modal_html(get_current_user_id()); return new \WP_REST_Response(['success' => true, 'html' => $html], 200); } /** admin-ajax HTML (remote modal fetch often hits admin-ajax). */ public function ajax_details_modal(): void { if (!is_user_logged_in()) { wp_send_json_error(['message' => __('请先登录', 'aksrt')], 401); } $html = $this->render_details_modal_html(get_current_user_id()); wp_send_json_success(['html' => $html]); } public function is_signed_today(int $user_id = 0): bool { $user_id = $user_id > 0 ? $user_id : get_current_user_id(); if ($user_id <= 0) { return false; } $today = $this->today_date(); if ((bool) get_user_meta($user_id, 'aksrt_signin_' . $today, true)) { return true; } return (bool) $this->get_record($user_id, $today); } public function get_total_days(int $user_id): int { global $wpdb; $table = \AKSRT\Core\DBMigrator::table('signin_log'); if (!$table) { return (int) get_user_meta($user_id, 'aksrt_checkin_all_day', true); } $n = (int) $wpdb->get_var($wpdb->prepare( "SELECT COUNT(*) FROM {$table} WHERE user_id = %d", $user_id )); return $n; } /** * zibll checkin_details_modal lite HTML. */ public function render_details_modal_html(int $user_id): string { $user = get_userdata($user_id); if (!$user) { return '
' . esc_html__('用户不存在', 'aksrt') . '