Files
tougao/application/common/CountryResolverService.php
wangjinlei ae221e6be6 1
2026-04-16 13:30:31 +08:00

109 lines
3.7 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
namespace app\common;
/**
* 使用本地 chat/completions 接口,从 affiliation 文本推断国家。
*
* 输出约定:返回数组 ['code' => 'US', 'en_name' => 'United States'],任一可为空。
*/
class CountryResolverService
{
private $chatUrl = '';
private $chatModel = '';
private $apiKey = '';
private $timeout = 60;
public function __construct(array $config = [])
{
if (isset($config['chat_url'])) $this->chatUrl = (string)$config['chat_url'];
if (isset($config['chat_model'])) $this->chatModel = (string)$config['chat_model'];
if (isset($config['api_key'])) $this->apiKey = (string)$config['api_key'];
if (isset($config['timeout'])) $this->timeout = max(5, intval($config['timeout']));
}
public function resolve(string $affiliation): array
{
$affiliation = trim($affiliation);
if ($affiliation === '' || $this->chatUrl === '' || $this->chatModel === '') {
return [];
}
$messages = [
[
'role' => 'system',
'content' => 'You extract the country from an academic affiliation string. Reply ONLY with minified JSON.',
],
[
'role' => 'user',
'content' => "Affiliation:\n" . $affiliation . "\n\nReturn JSON with keys:\n- code: ISO 3166-1 alpha-3 (preferred)\n- en_name: English country name (optional)\nIf uncertain, return {\"code\":\"\",\"en_name\":\"\"}.",
],
];
$payload = [
'model' => $this->chatModel,
'temperature' => 0,
'messages' => $messages,
];
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $this->chatUrl);
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($payload, JSON_UNESCAPED_UNICODE));
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, min(10, $this->timeout));
curl_setopt($ch, CURLOPT_TIMEOUT, $this->timeout);
$headers = [
'Content-Type: application/json',
];
if ($this->apiKey !== '') {
$headers[] = 'Authorization: Bearer ' . $this->apiKey;
}
curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
$raw = curl_exec($ch);
if ($raw === false) {
curl_close($ch);
return [];
}
$httpCode = intval(curl_getinfo($ch, CURLINFO_HTTP_CODE));
curl_close($ch);
if ($httpCode < 200 || $httpCode >= 300) {
return [];
}
$data = json_decode($raw, true);
if (!is_array($data)) return [];
// 兼容 OpenAI chat/completions 结构choices[0].message.content
$content = '';
if (isset($data['choices'][0]['message']['content'])) {
$content = (string)$data['choices'][0]['message']['content'];
} elseif (isset($data['content'])) {
$content = (string)$data['content'];
}
$content = trim($content);
if ($content === '') return [];
// 尝试提取 JSON允许模型包裹 ```json
if (preg_match('/\{.*\}/s', $content, $m)) {
$content = $m[0];
}
$obj = json_decode($content, true);
if (!is_array($obj)) return [];
$code = isset($obj['code']) ? strtoupper(trim((string)$obj['code'])) : '';
$enName = isset($obj['en_name']) ? trim((string)$obj['en_name']) : '';
// 防止模型乱回长段文本
if (strlen($code) > 8) $code = '';
if (strlen($enName) > 128) $enName = mb_substr($enName, 0, 128);
return ['code' => $code, 'en_name' => $enName];
}
}