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

280 lines
10 KiB
PHP
Raw Normal View History

2026-08-10 18:32:06 +08:00
<?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;
}
}