228 lines
7.6 KiB
PHP
228 lines
7.6 KiB
PHP
<?php
|
||
|
||
namespace App\Service;
|
||
|
||
use App\BaseApp\BaseService;
|
||
use App\Models\ai\AiApiKeyModel;
|
||
use App\Models\ai\AiPlatformModel;
|
||
use App\Service\common\ai\AiGenerationLogService;
|
||
use App\Service\common\ai\AiRuntimeConfigService;
|
||
use App\Service\common\FieldEncryptService;
|
||
|
||
/**
|
||
* AI 配置管理 Service:密钥 CRUD + 运行时选项读写
|
||
* 平台/模型以种子数据为主,密钥需后台录入后才能真正调用
|
||
*/
|
||
class AiConfigService extends BaseService
|
||
{
|
||
public function __construct()
|
||
{
|
||
parent::__construct();
|
||
$this->model = AiApiKeyModel::class;
|
||
$this->selectField = ['id', 'platform_id', 'name', 'remark', 'is_default', 'status', 'sort', 'created_at', 'updated_at'];
|
||
$this->queryField = [
|
||
'platform_id' => '=',
|
||
'name' => 'like',
|
||
'status' => '=',
|
||
];
|
||
$this->orderBy = [
|
||
'name' => 'sort',
|
||
'sort' => 'desc',
|
||
];
|
||
}
|
||
|
||
/**
|
||
* 获取 AI 管理页选项(平台卡片 + 当前激活组合)
|
||
*/
|
||
public function getRuntimeOptions(): array
|
||
{
|
||
return AiRuntimeConfigService::getInstance()->optionsForConfig();
|
||
}
|
||
|
||
/**
|
||
* 保存默认平台/模型/密钥
|
||
*/
|
||
public function saveRuntime(array $params): array
|
||
{
|
||
$provider = (string) ($params['provider'] ?? '');
|
||
$model = (string) ($params['model'] ?? '');
|
||
$apiKeyId = (int) ($params['api_key_id'] ?? 0);
|
||
return AiRuntimeConfigService::getInstance()->saveActiveConfig($provider, $model, $apiKeyId);
|
||
}
|
||
|
||
/**
|
||
* 密钥列表(卡片展示用,不返回明文密钥)
|
||
* 直接查表,避免单例 where 被 update 污染导致列表为空
|
||
*/
|
||
public function listKeys(): array
|
||
{
|
||
$items = AiApiKeyModel::where('deleted_at', 0)
|
||
->orderByDesc('sort')
|
||
->orderByDesc('id')
|
||
->get(['id', 'platform_id', 'name', 'remark', 'is_default', 'status', 'sort', 'api_key', 'created_at', 'updated_at'])
|
||
->toArray();
|
||
$platforms = AiPlatformModel::where('deleted_at', 0)
|
||
->get(['id', 'code', 'name', 'logo'])
|
||
->keyBy('id')
|
||
->toArray();
|
||
foreach ($items as &$item) {
|
||
$pid = (int) ($item['platform_id'] ?? 0);
|
||
$item['platform'] = $platforms[$pid] ?? null;
|
||
$item['has_key'] = trim((string) ($item['api_key'] ?? '')) !== '';
|
||
unset($item['api_key']);
|
||
}
|
||
unset($item);
|
||
return [
|
||
'page' => 1,
|
||
'size' => count($items),
|
||
'page_count' => 1,
|
||
'total' => count($items),
|
||
'items' => $items,
|
||
];
|
||
}
|
||
|
||
/**
|
||
* 平台下拉(密钥表单用)
|
||
*/
|
||
public function platformOptions(): array
|
||
{
|
||
return AiPlatformModel::where('deleted_at', 0)
|
||
->where('status', 1)
|
||
->orderByDesc('sort')
|
||
->orderBy('id')
|
||
->get(['id', 'code', 'name', 'logo'])
|
||
->toArray();
|
||
}
|
||
|
||
/**
|
||
* 新增密钥(明文入库前加密)
|
||
*/
|
||
public function createKey(array $params): mixed
|
||
{
|
||
$platformId = (int) ($params['platform_id'] ?? 0);
|
||
$name = trim((string) ($params['name'] ?? ''));
|
||
$apiKey = trim((string) ($params['api_key'] ?? ''));
|
||
if ($platformId <= 0) {
|
||
$this->utils->errorThrow('请选择所属平台');
|
||
}
|
||
if ($name === '') {
|
||
$this->utils->errorThrow('请填写密钥别名');
|
||
}
|
||
if ($apiKey === '') {
|
||
$this->utils->errorThrow('请填写 API Key');
|
||
}
|
||
$platform = AiPlatformModel::where('id', $platformId)
|
||
->where('deleted_at', 0)
|
||
->first();
|
||
if (!$platform) {
|
||
$this->utils->errorThrow('平台不存在');
|
||
}
|
||
$isDefault = (int) ($params['is_default'] ?? 0);
|
||
if ($isDefault === 1) {
|
||
// 同平台只保留一个默认密钥
|
||
AiApiKeyModel::where('platform_id', $platformId)
|
||
->where('deleted_at', 0)
|
||
->update(['is_default' => 0, 'updated_at' => time()]);
|
||
}
|
||
return $this->insert([
|
||
'platform_id' => $platformId,
|
||
'name' => $name,
|
||
'api_key' => FieldEncryptService::getInstance()->encryptForStorage($apiKey),
|
||
'remark' => trim((string) ($params['remark'] ?? '')),
|
||
'is_default' => $isDefault,
|
||
'status' => (int) ($params['status'] ?? 1),
|
||
'sort' => (int) ($params['sort'] ?? 0),
|
||
'created_at' => time(),
|
||
'updated_at' => 0,
|
||
'deleted_at' => 0,
|
||
]);
|
||
}
|
||
|
||
/**
|
||
* 更新密钥;api_key 为空表示不改密钥本身
|
||
*/
|
||
public function updateKey(int $id, array $params): mixed
|
||
{
|
||
$info = AiApiKeyModel::where('id', $id)->where('deleted_at', 0)->first();
|
||
if (!$info) {
|
||
$this->utils->notFound('密钥不存在');
|
||
}
|
||
$data = [];
|
||
if (array_key_exists('name', $params)) {
|
||
$name = trim((string) $params['name']);
|
||
if ($name === '') {
|
||
$this->utils->errorThrow('密钥别名不能为空');
|
||
}
|
||
$data['name'] = $name;
|
||
}
|
||
if (array_key_exists('remark', $params)) {
|
||
$data['remark'] = trim((string) $params['remark']);
|
||
}
|
||
if (array_key_exists('status', $params)) {
|
||
$data['status'] = (int) $params['status'];
|
||
}
|
||
if (array_key_exists('sort', $params)) {
|
||
$data['sort'] = (int) $params['sort'];
|
||
}
|
||
if (array_key_exists('platform_id', $params)) {
|
||
$platformId = (int) $params['platform_id'];
|
||
if ($platformId <= 0) {
|
||
$this->utils->errorThrow('请选择所属平台');
|
||
}
|
||
$data['platform_id'] = $platformId;
|
||
}
|
||
$platformId = (int) ($data['platform_id'] ?? $info->platform_id);
|
||
if (array_key_exists('is_default', $params)) {
|
||
$isDefault = (int) $params['is_default'];
|
||
$data['is_default'] = $isDefault;
|
||
if ($isDefault === 1) {
|
||
AiApiKeyModel::where('platform_id', $platformId)
|
||
->where('deleted_at', 0)
|
||
->where('id', '<>', $id)
|
||
->update(['is_default' => 0, 'updated_at' => time()]);
|
||
}
|
||
}
|
||
$apiKey = trim((string) ($params['api_key'] ?? ''));
|
||
if ($apiKey !== '') {
|
||
$data['api_key'] = FieldEncryptService::getInstance()->encryptForStorage($apiKey);
|
||
}
|
||
if (empty($data)) {
|
||
return true;
|
||
}
|
||
$data['updated_at'] = time();
|
||
// 直接 update,避免 BaseService::save 污染单例 where 导致后续列表为空
|
||
return AiApiKeyModel::where('id', $id)->where('deleted_at', 0)->update($data);
|
||
}
|
||
|
||
/**
|
||
* 软删除密钥
|
||
*/
|
||
public function deleteKeys(array $ids): mixed
|
||
{
|
||
$ids = array_values(array_filter(array_map('intval', $ids)));
|
||
if (empty($ids)) {
|
||
$this->utils->errorThrow('参数错误');
|
||
}
|
||
return AiApiKeyModel::whereIn('id', $ids)->where('deleted_at', 0)->update([
|
||
'deleted_at' => time(),
|
||
'updated_at' => time(),
|
||
]);
|
||
}
|
||
|
||
/**
|
||
* AI 生成历史列表
|
||
*/
|
||
public function generationList(): array
|
||
{
|
||
return AiGenerationLogService::getInstance()->getPageListForAdmin();
|
||
}
|
||
|
||
/**
|
||
* AI 生成历史详情
|
||
*/
|
||
public function generationDetail(int $id): array
|
||
{
|
||
return AiGenerationLogService::getInstance()->getDetailForAdmin($id);
|
||
}
|
||
}
|