Files
nl-admin-api/app/Service/AiConfigService.php

228 lines
7.6 KiB
PHP
Raw Normal View History

2026-08-10 15:51:00 +08:00
<?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);
}
}