完善系统参数配置和数据字典管理模块
This commit is contained in:
@@ -0,0 +1,120 @@
|
||||
using Model;
|
||||
using Model.Dto.System;
|
||||
using Model.Entity.System;
|
||||
using Service.Interface;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Service.Implement
|
||||
{
|
||||
/// <summary>
|
||||
/// 本地文件系统存储 Provider
|
||||
/// 落地策略:存到 BasePath/{Bucket?}/{yyyy}/{MM}/{dd}/{guid}{ext},URL = {Endpoint}/{Bucket?}/{yyyy}/{MM}/{dd}/{guid}{ext}
|
||||
/// wwwroot/uploads 由 Program.UseStaticFiles 提供访问;MinIO/OSS 实现见 IFileStorageProvider 扩展点
|
||||
/// </summary>
|
||||
public class LocalFileStorageProvider : IFileStorageProvider
|
||||
{
|
||||
public string ProviderName => "local";
|
||||
|
||||
public async Task<Result<FileUploadResultDto>> UploadAsync(Stream stream, string originalName, long size, FileStorageConfigEntity config)
|
||||
{
|
||||
if (config == null) return Result<FileUploadResultDto>.Error("存储配置不能为空");
|
||||
if (stream == null || !stream.CanRead) return Result<FileUploadResultDto>.Error("文件流不可读");
|
||||
try
|
||||
{
|
||||
var basePath = ResolveBasePath(config.BasePath);
|
||||
if (string.IsNullOrWhiteSpace(basePath))
|
||||
return Result<FileUploadResultDto>.Error("Local 存储必须配置 BasePath");
|
||||
|
||||
var ext = Path.GetExtension(originalName)?.ToLowerInvariant() ?? "";
|
||||
var now = DateTime.Now;
|
||||
var datePath = $"{now:yyyy}/{now:MM}/{now:dd}";
|
||||
var fileName = $"{Guid.NewGuid():N}{ext}";
|
||||
var storedFileName = string.IsNullOrWhiteSpace(config.Bucket)
|
||||
? $"{datePath}/{fileName}"
|
||||
: $"{config.Bucket}/{datePath}/{fileName}";
|
||||
|
||||
var dir = Path.Combine(basePath,
|
||||
string.IsNullOrWhiteSpace(config.Bucket) ? datePath : Path.Combine(config.Bucket, datePath));
|
||||
Directory.CreateDirectory(dir);
|
||||
var fullPath = Path.Combine(dir, fileName);
|
||||
|
||||
using (var fs = new FileStream(fullPath, FileMode.CreateNew, FileAccess.Write, FileShare.None))
|
||||
{
|
||||
await stream.CopyToAsync(fs);
|
||||
}
|
||||
|
||||
var url = BuildUrl(config.Endpoint, config.Bucket, storedFileName);
|
||||
return Result<FileUploadResultDto>.Success(new FileUploadResultDto
|
||||
{
|
||||
FileName = storedFileName,
|
||||
Url = url,
|
||||
Ext = ext,
|
||||
Size = size,
|
||||
Provider = ProviderName
|
||||
});
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
return Result<FileUploadResultDto>.Error("本地文件上传失败", ex);
|
||||
}
|
||||
}
|
||||
|
||||
public Task<Result> DeleteAsync(string storedFileName, FileStorageConfigEntity config)
|
||||
{
|
||||
if (config == null || string.IsNullOrWhiteSpace(storedFileName))
|
||||
return Task.FromResult(Result.Error("存储配置或文件名不能为空"));
|
||||
try
|
||||
{
|
||||
var basePath = ResolveBasePath(config.BasePath);
|
||||
var fullPath = Path.Combine(basePath, storedFileName.Replace('/', Path.DirectorySeparatorChar));
|
||||
if (File.Exists(fullPath)) File.Delete(fullPath);
|
||||
return Task.FromResult(Result.Success());
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
return Task.FromResult(Result.Error("删除本地文件失败", ex));
|
||||
}
|
||||
}
|
||||
|
||||
public Task<Result> TestConnectionAsync(FileStorageConfigEntity config)
|
||||
{
|
||||
if (config == null) return Task.FromResult(Result.Error("存储配置不能为空"));
|
||||
try
|
||||
{
|
||||
var basePath = ResolveBasePath(config.BasePath);
|
||||
if (string.IsNullOrWhiteSpace(basePath))
|
||||
return Task.FromResult(Result.Error("Local 存储必须配置 BasePath"));
|
||||
Directory.CreateDirectory(basePath);
|
||||
var testFile = Path.Combine(basePath, $".storage_test_{Guid.NewGuid():N}");
|
||||
File.WriteAllText(testFile, "ok");
|
||||
File.Delete(testFile);
|
||||
return Task.FromResult(Result.Success());
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
return Task.FromResult(Result.Error("Local 连接测试失败:" + ex.Message));
|
||||
}
|
||||
}
|
||||
|
||||
// ==================== 私有工具 ====================
|
||||
|
||||
private static string ResolveBasePath(string? basePath)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(basePath)) return string.Empty;
|
||||
// 相对路径以运行目录为基准(如 wwwroot/uploads)
|
||||
return Path.IsPathRooted(basePath) ? basePath : Path.Combine(AppContext.BaseDirectory, basePath);
|
||||
}
|
||||
|
||||
private static string BuildUrl(string? endpoint, string? bucket, string storedFileName)
|
||||
{
|
||||
var segs = new List<string>();
|
||||
if (!string.IsNullOrWhiteSpace(endpoint)) segs.Add(endpoint.TrimEnd('/'));
|
||||
if (!string.IsNullOrWhiteSpace(bucket)) segs.Add(bucket.Trim('/'));
|
||||
segs.Add(storedFileName);
|
||||
return string.Join('/', segs);
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user