'GET', 'callback' => [$this, 'get_status'], 'permission_callback' => [$this, 'check_logged_in'], ]); register_rest_route(self::API_NAMESPACE, '/ai/settings', [ 'methods' => 'POST', 'callback' => [$this, 'update_settings'], 'permission_callback' => [$this, 'check_permission'], ]); register_rest_route(self::API_NAMESPACE, '/ai/seo-generate', [ 'methods' => 'POST', 'callback' => [$this, 'seo_generate'], 'permission_callback' => [$this, 'check_logged_in'], ]); register_rest_route(self::API_NAMESPACE, '/ai/seo-suggest', [ 'methods' => 'POST', 'callback' => [$this, 'seo_suggest'], 'permission_callback' => [$this, 'check_write_assist'], ]); register_rest_route(self::API_NAMESPACE, '/ai/summary-suggest', [ 'methods' => 'POST', 'callback' => [$this, 'summary_suggest'], 'permission_callback' => [$this, 'check_write_assist'], ]); register_rest_route(self::API_NAMESPACE, '/ai/tags-suggest', [ 'methods' => 'POST', 'callback' => [$this, 'tags_suggest'], 'permission_callback' => [$this, 'check_write_assist'], ]); } public function check_logged_in(): bool { return is_user_logged_in(); } /** * 投稿/编辑写作辅助:已登录且可编辑文章或允许前台投稿。 */ public function check_write_assist(): bool { if (!is_user_logged_in()) { return false; } if (current_user_can('edit_posts')) { return true; } if (class_exists('AKSRT_Front_Submit')) { return (bool) AKSRT_Front_Submit::get_instance()->can_submit(); } return false; } public function get_status(): WP_REST_Response { if (!class_exists('AKSRT_AI')) { return $this->success(['data' => [ 'enabled' => false, 'provider' => 'none', 'configured' => false, 'available' => false, 'backend' => 'none', ]]); } return $this->success(['data' => AKSRT_AI::get_instance()->get_status()]); } public function update_settings(WP_REST_Request $request): WP_REST_Response { if (!class_exists('AKSRT_AI')) { return $this->error(__('AI 模块未加载', 'aksrt'), 500); } $params = $this->get_json_params($request); $allowed = [ 'enabled' => 'bool', 'provider' => 'string', 'api_key' => 'string', 'api_base' => 'url', 'model' => 'string', 'max_tokens' => 'int', 'rate_limit' => 'int', ]; $config = []; foreach ($allowed as $key => $type) { if (!isset($params[$key])) { continue; } switch ($type) { case 'bool': $config[$key] = !empty($params[$key]); break; case 'int': $config[$key] = (int) $params[$key]; break; case 'url': $config[$key] = esc_url_raw((string) $params[$key]); break; default: $config[$key] = sanitize_text_field((string) $params[$key]); } } $success = AKSRT_AI::get_instance()->update_config($config); if ($success) { return $this->success([ 'message' => __('AI 设置已保存', 'aksrt'), 'data' => AKSRT_AI::get_instance()->get_status(), ]); } return $this->error(__('保存失败', 'aksrt')); } /** * POST /ai/seo-generate — 从 post/term 实体生成 SEO 三字段。 */ public function seo_generate(WP_REST_Request $request): WP_REST_Response { if (!class_exists('AKSRT_AI_SEO')) { return $this->error(__('AI SEO 模块未加载', 'aksrt'), 500); } $params = $this->get_json_params($request); $type = sanitize_key((string) ($params['type'] ?? 'post')); $id = absint($params['id'] ?? 0); if (!in_array($type, ['post', 'term'], true) || $id <= 0) { return $this->error(__('参数错误', 'aksrt'), 422); } if ($type === 'post') { if (!current_user_can('edit_post', $id)) { return $this->error(__('权限不足,无法编辑此文章', 'aksrt'), 403); } } else { if (!current_user_can('edit_term', $id)) { return $this->error(__('权限不足,无法编辑此分类/标签', 'aksrt'), 403); } } $result = AKSRT_AI_SEO::get_instance()->generate_fields($type, $id); if (empty($result['success'])) { return $this->error((string) ($result['message'] ?? __('生成失败', 'aksrt'))); } return $this->success(['data' => $result['data']]); } public function seo_suggest(WP_REST_Request $request): WP_REST_Response { if (!class_exists('AKSRT_AI_SEO')) { return $this->error(__('AI SEO 模块未加载', 'aksrt'), 500); } $params = $this->get_json_params($request); $title = sanitize_text_field((string) ($params['title'] ?? '')); $content = wp_kses_post((string) ($params['content'] ?? '')); $post_id = absint($params['post_id'] ?? 0); if ($post_id > 0 && !current_user_can('edit_post', $post_id)) { return $this->error(__('权限不足', 'aksrt'), 403); } if ($title === '') { return $this->error(__('标题不能为空', 'aksrt')); } $result = AKSRT_AI_SEO::get_instance()->suggest($title, $content); if (!empty($result['success'])) { return $this->success(['data' => $result['data']]); } return $this->error((string) ($result['message'] ?? __('生成失败', 'aksrt'))); } public function summary_suggest(WP_REST_Request $request): WP_REST_Response { if (!class_exists('AKSRT_AI') || !class_exists('AKSRT_AI_Prompt')) { return $this->error(__('AI 模块未加载', 'aksrt'), 500); } $params = $this->get_json_params($request); $content = wp_kses_post((string) ($params['content'] ?? '')); $max_len = max(50, min(500, (int) ($params['max_length'] ?? 200))); if (trim(wp_strip_all_tags($content)) === '') { return $this->error(__('内容不能为空', 'aksrt')); } $prompt = AKSRT_AI_Prompt::get_instance()->build_summary_prompt($content, $max_len); $result = AKSRT_AI::get_instance()->generate_text($prompt, [ 'max_tokens' => min(600, $max_len * 2), 'temperature' => 0.5, ]); if (!empty($result['success'])) { return $this->success(['data' => ['summary' => $result['content']]]); } return $this->error((string) ($result['message'] ?? __('生成失败', 'aksrt'))); } public function tags_suggest(WP_REST_Request $request): WP_REST_Response { if (!class_exists('AKSRT_AI') || !class_exists('AKSRT_AI_Prompt')) { return $this->error(__('AI 模块未加载', 'aksrt'), 500); } $params = $this->get_json_params($request); $title = sanitize_text_field((string) ($params['title'] ?? '')); $content = wp_kses_post((string) ($params['content'] ?? '')); if ($title === '' && trim(wp_strip_all_tags($content)) === '') { return $this->error(__('标题或内容不能为空', 'aksrt')); } $prompt = AKSRT_AI_Prompt::get_instance()->build_tags_prompt($title, $content); $result = AKSRT_AI::get_instance()->generate_text($prompt, [ 'max_tokens' => 300, 'temperature' => 0.5, ]); if (!empty($result['success'])) { $tags = json_decode((string) $result['content'], true); if (!is_array($tags)) { $tags = array_filter(array_map('trim', explode(',', (string) $result['content']))); } $tags = array_values(array_map('sanitize_text_field', $tags)); return $this->success(['data' => ['tags' => $tags]]); } return $this->error((string) ($result['message'] ?? __('生成失败', 'aksrt'))); } }