wpmind/includes/API/Services/AbstractService.php
LinuxJoy b8bfb55dad feat: Phase 4.0 — ExactCache + MCP Gateway + API Gateway 设计方案
核心功能:
- ExactCache: 请求级精确缓存,批量写入优化 (shutdown hook)
- MCP Gateway: WordPress Abilities API 集成 (5 个能力)
- AbstractService: cache_ttl 三级语义 (负数禁用/零自动/正数指定)

API Gateway 模块设计 (Codex 三轮评审通过):
- 完整设计方案 + 5 个 P0 解决方案 (API Key/Pipeline/SSE/限流/错误映射)
- 11 阶段实施计划 (Phase 0-10),8 阶段中间件 Pipeline
- 3 张 DB 表设计 (api_keys/api_key_usage/api_audit_log)

其他:
- Provider 测试文档 (Qwen/Doubao 兼容性矩阵)
- GEO 设置页 badge 文本修正
- 版本号升级到 3.10.1

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-02-08 19:06:30 +08:00

287 lines
7.5 KiB
PHP
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<?php
/**
* Service 基类
*
* 提供所有 Service 共享的基础设施方法
*
* @package WPMind
* @subpackage API\Services
* @since 3.7.0
*/
declare(strict_types=1);
namespace WPMind\API\Services;
use WP_Error;
/**
* Service 基类
*
* @since 3.7.0
*/
abstract class AbstractService {
/**
* 解析 Provider'auto' -> 默认值,应用 filter
*
* @param string $provider 原始 provider 值
* @param string $context 上下文
* @return string
*/
protected function resolve_provider(string $provider, string $context = ''): string {
if ($provider === 'auto') {
$provider = get_option('wpmind_default_provider', 'openai');
}
return apply_filters('wpmind_select_provider', $provider, $context);
}
/**
* 获取故障转移链
*
* @param string $provider 首选 provider
* @return array
*/
protected function get_failover_chain(string $provider): array {
if (!class_exists('\\WPMind\\Failover\\FailoverManager')) {
return [$provider];
}
$failover = \WPMind\Failover\FailoverManager::instance();
return $failover->get_failover_chain($provider);
}
/**
* 获取端点配置
*
* @return array
*/
protected function get_endpoints(): array {
if (!class_exists('\\WPMind\\WPMind')) {
return [];
}
return \WPMind\WPMind::instance()->get_custom_endpoints();
}
/**
* 记录请求结果到 FailoverManager
*
* @param string $provider 服务商 ID
* @param bool $success 是否成功
* @param int $latency_ms 延迟毫秒
*/
protected function record_result(string $provider, bool $success, int $latency_ms = 0): void {
if (class_exists('\\WPMind\\Failover\\FailoverManager')) {
\WPMind\Failover\FailoverManager::instance()->record_result($provider, $success, $latency_ms);
}
}
/**
* 通用 failover 执行模板
*
* 遍历 failover 链,对每个 provider 执行回调,处理成功/失败/记录。
* 适用于 embed/transcribe/speech 等简单 failover 场景。
*
* @param string $type 请求类型(用于错误消息)
* @param string $provider 首选 provider
* @param string $context 上下文
* @param callable $execute_fn 执行函数 fn(string $provider, array $endpoint): array|WP_Error
* @param array $supported_providers 支持的 provider 列表(空数组表示不过滤)
* @return array|WP_Error
*/
protected function execute_with_failover(
string $type,
string $provider,
string $context,
callable $execute_fn,
array $supported_providers = []
) {
$failover_chain = $this->get_failover_chain($provider);
// 过滤出支持的 provider
if (!empty($supported_providers)) {
$failover_chain = array_values(array_filter($failover_chain, function ($p) use ($supported_providers) {
return in_array($p, $supported_providers, true);
}));
if (empty($failover_chain)) {
return new WP_Error(
"wpmind_{$type}_not_supported",
sprintf(__('没有可用的 %s 服务商', 'wpmind'), $type)
);
}
}
$endpoints = $this->get_endpoints();
$last_error = null;
foreach ($failover_chain as $try_provider) {
if (!isset($endpoints[$try_provider])) {
$last_error = new WP_Error('wpmind_provider_not_found',
sprintf(__('服务商 %s 未配置', 'wpmind'), $try_provider));
continue;
}
$endpoint = $endpoints[$try_provider];
$api_key = $endpoint['api_key'] ?? '';
if (empty($api_key)) {
$last_error = new WP_Error('wpmind_api_key_missing',
sprintf(__('服务商 %s 未配置 API Key', 'wpmind'), $try_provider));
continue;
}
$result = $execute_fn($try_provider, $endpoint);
if (!is_wp_error($result)) {
return $result;
}
$last_error = $result;
}
if ($last_error) {
do_action('wpmind_error', $last_error, $type, []);
return $last_error;
}
return new WP_Error("wpmind_{$type}_failed", sprintf(__('%s 请求失败', 'wpmind'), $type));
}
/**
* 获取当前模型
*
* @param string $provider 服务商
* @return string
*/
protected function get_current_model(string $provider): string {
if (!class_exists('\\WPMind\\WPMind')) {
return 'default';
}
$endpoints = $this->get_endpoints();
if (isset($endpoints[$provider]['models']) && is_array($endpoints[$provider]['models'])) {
return $endpoints[$provider]['models'][0] ?? 'default';
}
return 'default';
}
/**
* 获取默认模型
*
* @return string
*/
protected function get_default_model(): string {
$provider = get_option('wpmind_default_provider', 'openai');
return $this->get_current_model($provider);
}
/**
* 生成缓存键
*
* @param string $type 类型
* @param array $args 参数
* @param string $provider 服务商
* @param string $model 模型
* @return string
*/
protected function generate_cache_key(string $type, array $args, string $provider = '', string $model = ''): string {
if (class_exists('\WPMind\Cache\ExactCache')) {
$key = \WPMind\Cache\ExactCache::instance()->build_key($type, $args, $provider, $model);
} else {
$key = 'wpmind_' . $type . '_' . $provider . '_' . $model . '_' . md5(serialize($args));
}
return apply_filters('wpmind_cache_key', $key, $type, $args);
}
/**
* 读取缓存值(优先 Exact Cache失败回退 transient
*
* @param string $cache_key 缓存键
* @param int $cache_ttl TTL 秒数
* @return array{hit:bool,value:mixed}
*/
protected function get_cached_value(string $cache_key, int $cache_ttl): array {
$effective_ttl = $this->get_effective_cache_ttl($cache_ttl);
if ($effective_ttl <= 0) {
return ['hit' => false, 'value' => null];
}
if (class_exists('\WPMind\Cache\ExactCache')) {
$cached = \WPMind\Cache\ExactCache::instance()->get($cache_key);
if ($cached !== null) {
return ['hit' => true, 'value' => $cached];
}
return ['hit' => false, 'value' => null];
}
$cached = get_transient($cache_key);
if ($cached !== false) {
return ['hit' => true, 'value' => $cached];
}
return ['hit' => false, 'value' => null];
}
/**
* 写入缓存值(优先 Exact Cache失败回退 transient
*
* @param string $cache_key 缓存键
* @param mixed $value 缓存值
* @param int $cache_ttl TTL 秒数
* @param array $meta 元数据
* @return void
*/
protected function set_cached_value(string $cache_key, $value, int $cache_ttl, array $meta = []): void {
$effective_ttl = $this->get_effective_cache_ttl($cache_ttl);
if ($effective_ttl <= 0) {
return;
}
if (class_exists('\WPMind\Cache\ExactCache')) {
\WPMind\Cache\ExactCache::instance()->set($cache_key, $value, $effective_ttl, $meta);
return;
}
set_transient($cache_key, $value, $effective_ttl);
}
/**
* 计算生效缓存 TTL
*
* 行为说明:
* - cache_ttl > 0 : 使用调用方指定的 TTL
* - cache_ttl = 0 : 当 ExactCache 启用时使用其默认 TTL自动缓存
* 否则不缓存(向后兼容)
* - cache_ttl < 0 : 强制不缓存(显式禁用)
*
* 可通过 `wpmind_exact_cache_auto_cache` filter 关闭自动缓存行为。
*
* @param int $cache_ttl API 调用层传入 TTL
* @return int
*/
private function get_effective_cache_ttl(int $cache_ttl): int {
if ($cache_ttl < 0) {
return 0;
}
if ($cache_ttl > 0) {
return $cache_ttl;
}
if (!class_exists('\WPMind\Cache\ExactCache')) {
return 0;
}
$auto_cache = (bool) apply_filters('wpmind_exact_cache_auto_cache', true);
if (!$auto_cache) {
return 0;
}
return \WPMind\Cache\ExactCache::instance()->get_default_ttl();
}
}