Files
nl-admin-api/app/Service/OssConfigService.php
2026-08-10 18:32:06 +08:00

280 lines
10 KiB
PHP
Raw 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\Service;
use App\BaseApp\BaseService;
use App\Enum\OssDriverEnum;
use App\Models\oss\OssConfigModel;
use App\Service\common\FieldEncryptService;
/**
* OSS 配置管理:驱动选项、启用切换、配置 CRUD密钥入库加密
*/
class OssConfigService extends BaseService
{
public function __construct()
{
parent::__construct();
$this->model = OssConfigModel::class;
$this->selectField = [
'id', 'driver', 'name', 'endpoint', 'region', 'bucket', 'domain',
'path_prefix', 'is_active', 'status', 'remark', 'sort', 'created_at', 'updated_at',
];
$this->queryField = [
'driver' => '=',
'name' => 'like',
'status' => '=',
'is_active' => '=',
];
$this->orderBy = [
'name' => 'sort',
'sort' => 'desc',
];
}
/**
* 驱动枚举选项(前端表单 / 卡片文案)
*/
public function driverOptions(): array
{
return OssDriverEnum::options();
}
/**
* 运行时页数据:配置卡片 + 当前启用 ID不回显密钥明文
*/
public function getRuntimeOptions(): array
{
$items = $this->formatConfigRows(
OssConfigModel::where('deleted_at', 0)
->orderByDesc('sort')
->orderByDesc('id')
->get()
->toArray()
);
$activeId = 0;
foreach ($items as $item) {
if ((int) ($item['is_active'] ?? 0) === 1) {
$activeId = (int) $item['id'];
break;
}
}
if ($activeId <= 0) {
$activeId = (int) SystemConfigService::getInstance()->getValue('oss_active_config_id', 0);
}
return [
'drivers' => OssDriverEnum::options(),
'items' => $items,
'active_id' => $activeId,
];
}
/**
* 切换当前启用的 OSS 配置
* 为什么双写 is_active + system_config上传链路优先读 kv列表页靠 is_active 展示徽章
*/
public function saveRuntime(array $params): array
{
$id = (int) ($params['id'] ?? 0);
if ($id <= 0) {
$this->utils->errorThrow('请选择要启用的配置');
}
$info = OssConfigModel::where('id', $id)->where('deleted_at', 0)->first();
if (!$info) {
$this->utils->notFound('配置不存在');
}
if ((int) $info->status !== 1) {
$this->utils->errorThrow('该配置已禁用,无法启用');
}
$now = time();
OssConfigModel::where('deleted_at', 0)->update(['is_active' => 0, 'updated_at' => $now]);
OssConfigModel::where('id', $id)->update(['is_active' => 1, 'updated_at' => $now]);
SystemConfigService::getInstance()->setValue('oss_active_config_id', (string) $id, '当前启用的 OSS 配置 ID');
return ['id' => $id];
}
/**
* 配置列表(卡片,无密钥明文)
*/
public function listConfigs(): array
{
$items = $this->formatConfigRows(
OssConfigModel::where('deleted_at', 0)
->orderByDesc('sort')
->orderByDesc('id')
->get()
->toArray()
);
return [
'page' => 1,
'size' => count($items),
'page_count' => 1,
'total' => count($items),
'items' => $items,
];
}
/**
* 新增 OSS 配置;云驱动要求填写密钥
*/
public function createConfig(array $params): mixed
{
$driver = trim((string) ($params['driver'] ?? ''));
$name = trim((string) ($params['name'] ?? ''));
if (!OssDriverEnum::isValid($driver)) {
$this->utils->errorThrow('不支持的存储驱动');
}
if ($name === '') {
$this->utils->errorThrow('请填写配置别名');
}
$enum = OssDriverEnum::from($driver);
$accessKey = trim((string) ($params['access_key'] ?? ''));
$secretKey = trim((string) ($params['secret_key'] ?? ''));
if ($enum->needsSecret() && ($accessKey === '' || $secretKey === '')) {
$this->utils->errorThrow('该驱动需要填写 AccessKey 与 SecretKey');
}
$encrypt = FieldEncryptService::getInstance();
$isActive = (int) ($params['is_active'] ?? 0);
$now = time();
if ($isActive === 1) {
OssConfigModel::where('deleted_at', 0)->update(['is_active' => 0, 'updated_at' => $now]);
}
$id = OssConfigModel::insertGetId([
'driver' => $driver,
'name' => $name,
'access_key' => $accessKey !== '' ? $encrypt->encryptForStorage($accessKey) : '',
'secret_key' => $secretKey !== '' ? $encrypt->encryptForStorage($secretKey) : '',
'endpoint' => trim((string) ($params['endpoint'] ?? '')),
'region' => trim((string) ($params['region'] ?? '')),
'bucket' => trim((string) ($params['bucket'] ?? '')),
'domain' => trim((string) ($params['domain'] ?? '')),
'path_prefix' => trim((string) ($params['path_prefix'] ?? '')),
'extra_json' => null,
'is_active' => $isActive,
'status' => (int) ($params['status'] ?? 1),
'remark' => trim((string) ($params['remark'] ?? '')),
'sort' => (int) ($params['sort'] ?? 0),
'created_at' => $now,
'updated_at' => 0,
'deleted_at' => 0,
]);
if ($isActive === 1) {
SystemConfigService::getInstance()->setValue('oss_active_config_id', (string) $id, '当前启用的 OSS 配置 ID');
}
return ['id' => $id];
}
/**
* 更新配置access_key/secret_key 空串表示不改密钥
*/
public function updateConfig(int $id, array $params): mixed
{
$info = OssConfigModel::where('id', $id)->where('deleted_at', 0)->first();
if (!$info) {
$this->utils->notFound('配置不存在');
}
$data = [];
if (array_key_exists('driver', $params)) {
$driver = trim((string) $params['driver']);
if (!OssDriverEnum::isValid($driver)) {
$this->utils->errorThrow('不支持的存储驱动');
}
$data['driver'] = $driver;
}
if (array_key_exists('name', $params)) {
$name = trim((string) $params['name']);
if ($name === '') {
$this->utils->errorThrow('配置别名不能为空');
}
$data['name'] = $name;
}
foreach (['endpoint', 'region', 'bucket', 'domain', 'path_prefix', 'remark'] as $field) {
if (array_key_exists($field, $params)) {
$data[$field] = trim((string) $params[$field]);
}
}
if (array_key_exists('status', $params)) {
$data['status'] = (int) $params['status'];
}
if (array_key_exists('sort', $params)) {
$data['sort'] = (int) $params['sort'];
}
$encrypt = FieldEncryptService::getInstance();
$accessKey = trim((string) ($params['access_key'] ?? ''));
if ($accessKey !== '') {
$data['access_key'] = $encrypt->encryptForStorage($accessKey);
}
$secretKey = trim((string) ($params['secret_key'] ?? ''));
if ($secretKey !== '') {
$data['secret_key'] = $encrypt->encryptForStorage($secretKey);
}
$now = time();
if (array_key_exists('is_active', $params) && (int) $params['is_active'] === 1) {
OssConfigModel::where('deleted_at', 0)->where('id', '<>', $id)->update(['is_active' => 0, 'updated_at' => $now]);
$data['is_active'] = 1;
SystemConfigService::getInstance()->setValue('oss_active_config_id', (string) $id, '当前启用的 OSS 配置 ID');
} elseif (array_key_exists('is_active', $params)) {
$data['is_active'] = (int) $params['is_active'];
}
if (empty($data)) {
return true;
}
$data['updated_at'] = $now;
return OssConfigModel::where('id', $id)->where('deleted_at', 0)->update($data);
}
/**
* 软删除;若删的是当前启用项则回落到本地配置,避免上传无可用驱动
*/
public function deleteConfigs(array $ids): mixed
{
$ids = array_values(array_filter(array_map('intval', $ids)));
if (empty($ids)) {
$this->utils->errorThrow('参数错误');
}
$activeId = (int) SystemConfigService::getInstance()->getValue('oss_active_config_id', 0);
$deletingActive = ($activeId > 0 && in_array($activeId, $ids, true))
|| OssConfigModel::whereIn('id', $ids)->where('is_active', 1)->where('deleted_at', 0)->exists();
$fallbackId = 0;
if ($deletingActive) {
$local = OssConfigModel::where('driver', 'local')
->where('deleted_at', 0)
->where('status', 1)
->whereNotIn('id', $ids)
->orderBy('id')
->first();
if (!$local) {
$this->utils->errorThrow('不能删除当前启用的配置:请先切换,或保留至少一条本地存储');
}
$fallbackId = (int) $local->id;
}
$ok = OssConfigModel::whereIn('id', $ids)->where('deleted_at', 0)->update([
'deleted_at' => time(),
'updated_at' => time(),
'is_active' => 0,
]);
if ($fallbackId > 0) {
$this->saveRuntime(['id' => $fallbackId]);
}
return $ok;
}
/**
* 行格式化:补驱动名、密钥是否已配置标记
*/
private function formatConfigRows(array $rows): array
{
foreach ($rows as &$row) {
$driver = (string) ($row['driver'] ?? '');
$enum = OssDriverEnum::tryFrom($driver);
$row['driver_name'] = $enum?->label() ?? $driver;
$row['driver_desc'] = $enum?->description() ?? '';
$row['has_access_key'] = trim((string) ($row['access_key'] ?? '')) !== '';
$row['has_secret_key'] = trim((string) ($row['secret_key'] ?? '')) !== '';
unset($row['access_key'], $row['secret_key']);
}
unset($row);
return $rows;
}
}