false]; } /** * 发送微信模板消息 * * @param int $user_id 用户 ID * @param string $template 模板标识 * @param array $data 模板数据 * @return bool */ public function send(int $user_id, string $template, array $data = []): bool { $config = $this->get_config(); if (empty($config['enabled'])) { return false; } // 获取用户的微信 openid $openid = get_user_meta($user_id, 'aksrt_wechat_openid', true); if (empty($openid)) { return false; } // 获取 access token $access_token = $this->get_access_token($config); if (empty($access_token)) { if (defined('WP_DEBUG') && WP_DEBUG) { error_log('[AKSRT WeChat] Failed to get access token'); } return false; } // 构建模板消息 $template_id = $config['template_id'] ?? ''; if (empty($template_id)) { if (defined('WP_DEBUG') && WP_DEBUG) { error_log('[AKSRT WeChat] No template ID configured'); } return false; } $message = $this->build_template_message($template, $data, $openid, $template_id); // 调用微信模板消息 API $response = wp_remote_post('https://api.weixin.qq.com/cgi-bin/message/template/send?access_token=' . $access_token, [ 'headers' => ['Content-Type' => 'application/json'], 'body' => wp_json_encode($message), 'timeout' => 10, ]); if (is_wp_error($response)) { if (defined('WP_DEBUG') && WP_DEBUG) { error_log('[AKSRT WeChat] API error: ' . $response->get_error_message()); } return false; } $body = json_decode(wp_remote_retrieve_body($response), true); $errcode = (int) ($body['errcode'] ?? 0); if (defined('WP_DEBUG') && WP_DEBUG) { error_log(sprintf('[AKSRT WeChat] User: %d, Template: %s, Result: %s', $user_id, $template, wp_json_encode($body))); } return $errcode === 0; } /** * 获取微信 access token(带缓存) */ private function get_access_token(array $config): string { $cache_key = 'aksrt_wechat_token'; $cached = get_transient($cache_key); if (!empty($cached)) return (string) $cached; $app_id = $config['app_id'] ?? ''; $app_secret = $config['app_secret'] ?? ''; if (empty($app_id) || empty($app_secret)) return ''; $response = wp_remote_get(sprintf( 'https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=%s&secret=%s', urlencode($app_id), urlencode($app_secret) )); if (is_wp_error($response)) return ''; $body = json_decode(wp_remote_retrieve_body($response), true); $token = $body['access_token'] ?? ''; $expires = max(600, (int) ($body['expires_in'] ?? 7200) - 300); if (!empty($token)) { set_transient($cache_key, $token, $expires); } return (string) $token; } /** * 构建模板消息体 */ private function build_template_message(string $template, array $data, string $openid, string $template_id): array { $url = $data['url'] ?? home_url('/'); $values = $data['values'] ?? []; $template_data = []; foreach ($values as $key => $value) { $template_data[$key] = [ 'value' => (string) $value, 'color' => '#333333', ]; } return [ 'touser' => $openid, 'template_id' => $template_id, 'url' => esc_url($url), 'data' => $template_data, ]; } /** * 绑定用户微信 openid */ public function bind_user(int $user_id, string $openid): bool { if ($user_id <= 0 || empty($openid)) return false; return (bool) update_user_meta($user_id, 'aksrt_wechat_openid', sanitize_text_field($openid)); } /** * 解绑用户微信 */ public function unbind_user(int $user_id): bool { return (bool) delete_user_meta($user_id, 'aksrt_wechat_openid'); } /** * 监听站内通知,联动微信通知 */ public function maybe_send_wechat(int $user_id, string $type, array $data): void { $config = $this->get_config(); if (empty($config['enabled'])) return; // 仅特定类型通知发送微信 $allowed_types = ['comment_reply', 'system_notice', 'order_paid', 'credits_change']; if (!in_array($type, $allowed_types, true)) return; $this->send($user_id, $type, $data); } /** * AJAX: Get WeChat notification status (admin only). */ public function ajax_status(): void { if (!current_user_can('manage_options')) { wp_send_json_error(['message' => __('权限不足', 'aksrt')]); } wp_send_json_success($this->get_status()); } /** * Get WeChat service status (used by REST and AJAX). */ public function get_status(): array { $config = $this->get_config(); $has_token = false; if (!empty($config['app_id']) && !empty($config['app_secret'])) { $cached = get_transient('aksrt_wechat_token'); $has_token = !empty($cached); } // Count bound users global $wpdb; $bound_count = 0; if ($wpdb) { $bound_count = (int) $wpdb->get_var( "SELECT COUNT(*) FROM {$wpdb->usermeta} WHERE meta_key = 'aksrt_wechat_openid' AND meta_value != ''" ); } return [ 'enabled' => !empty($config['enabled']), 'appid' => $config['app_id'] ?? '', 'configured' => !empty($config['app_id']) && !empty($config['app_secret']), 'has_token' => $has_token, 'has_template' => !empty($config['template_id']), 'bound_users' => $bound_count, 'app_secret' => !empty($config['app_secret']) ? '***' : '', 'allowed_types' => ['comment_reply', 'system_notice', 'order_paid', 'credits_change'], ]; } /** * Get notification subscriptions for a user. */ public function get_user_subscriptions(int $user_id): array { $subs = get_user_meta($user_id, 'aksrt_wechat_subscriptions', true); if (!is_array($subs)) { $subs = [ 'comment_reply' => true, 'system_notice' => true, 'order_paid' => true, 'credits_change' => false, ]; } return $subs; } /** * Update notification subscriptions for a user. */ public function update_user_subscriptions(int $user_id, array $subscriptions): bool { $allowed = ['comment_reply', 'system_notice', 'order_paid', 'credits_change']; $clean = []; foreach ($allowed as $key) { $clean[$key] = !empty($subscriptions[$key]); } return (bool) update_user_meta($user_id, 'aksrt_wechat_subscriptions', $clean); } /** * Check if a user should receive a specific notification type via WeChat. */ public function should_notify(int $user_id, string $type): bool { $config = $this->get_config(); if (empty($config['enabled'])) return false; $openid = get_user_meta($user_id, 'aksrt_wechat_openid', true); if (empty($openid)) return false; $subs = $this->get_user_subscriptions($user_id); return !empty($subs[$type]); } }