oss板块

This commit is contained in:
fuenmao
2026-08-24 18:16:08 +08:00
parent 0931e1f343
commit 4679bd6c13
13 changed files with 341 additions and 12 deletions
@@ -49,7 +49,10 @@ public class OnboardingContractDocumentService : IOnboardingContractDocumentServ
sourceBytes.ToArray(), sourceBytes.ToArray(),
variables.ToDictionary(x => x.VariableCode, x => x.VariableValue ?? string.Empty)); variables.ToDictionary(x => x.VariableCode, x => x.VariableValue ?? string.Empty));
var name = $"{task.TaskNo}_{contract.ContractName}.docx"; var name = $"{task.TaskNo}_{contract.ContractName}.docx";
var fileId = await _fileService.StoreGeneratedFile(source.Engine, name, rendered); var fileId = await _fileService.StoreGeneratedFile(
SysDictConst.FILE_ENGINE_ALIYUN_OSS,
name,
rendered);
contract.GeneratedDocxFileId = fileId; contract.GeneratedDocxFileId = fileId;
contract.GeneratedAt = DateTime.Now; contract.GeneratedAt = DateTime.Now;
@@ -262,6 +262,9 @@ public class OnboardingPublicSubmissionService : IOnboardingPublicSubmissionServ
private async Task<ObOnboardingTask> GetEditableTask(string token) private async Task<ObOnboardingTask> GetEditableTask(string token)
{ {
if (string.IsNullOrWhiteSpace(token))
throw Oops.Bah("入职链接 Token 不能为空");
var hash = Convert.ToHexString( var hash = Convert.ToHexString(
global::System.Security.Cryptography.SHA256.HashData( global::System.Security.Cryptography.SHA256.HashData(
global::System.Text.Encoding.UTF8.GetBytes(token))); global::System.Text.Encoding.UTF8.GetBytes(token)));
+6 -1
View File
@@ -1,4 +1,4 @@
// Copyright (c) 2022-Now 少林寺驻北固山办事处大神父王喇嘛 // Copyright (c) 2022-Now 少林寺驻北固山办事处大神父王喇嘛
// //
// SimpleAdmin 基于 Apache License Version 2.0 协议发布,可用于商业项目,但必须遵守以下补充条款: // SimpleAdmin 基于 Apache License Version 2.0 协议发布,可用于商业项目,但必须遵守以下补充条款:
// 1.请不要删除和修改根目录下的LICENSE文件。 // 1.请不要删除和修改根目录下的LICENSE文件。
@@ -68,6 +68,11 @@ public class SysDictConst
/// </summary> /// </summary>
public const string FILE_ENGINE_MINIO = "MINIO"; public const string FILE_ENGINE_MINIO = "MINIO";
/// <summary>
/// 阿里云 OSS,当前仅供入职业务文件使用。
/// </summary>
public const string FILE_ENGINE_ALIYUN_OSS = "ALIYUN_OSS";
#endregion 上传文件引擎 #endregion 上传文件引擎
#region 系统字典分类 #region 系统字典分类
@@ -0,0 +1,62 @@
namespace SimpleAdmin.System;
/// <summary>
/// 阿里云 OSS 及文档预览服务配置。
/// </summary>
public class OssSettingOptions
{
/// <summary>
/// OSS 访问密钥 Id。
/// </summary>
public string AccessKeyID { get; set; }
/// <summary>
/// OSS 访问密钥。
/// </summary>
public string AccessKeySecret { get; set; }
/// <summary>
/// 用于换取临时凭证的 RAM 角色 ARN。
/// </summary>
public string RoleArn { get; set; }
/// <summary>
/// STS 角色会话名称。
/// </summary>
public string RoleSessionName { get; set; }
/// <summary>
/// STS 服务端点。
/// </summary>
public string StsEndpoint { get; set; }
/// <summary>
/// OSS Bucket 名称。
/// </summary>
public string Bucket { get; set; }
/// <summary>
/// OSS 服务端点。
/// </summary>
public string EndPoint { get; set; }
/// <summary>
/// IMM 服务端点。
/// </summary>
public string IMMEndpoint { get; set; }
/// <summary>
/// IMM 项目名称。
/// </summary>
public string IMMProject { get; set; }
/// <summary>
/// OSS 自定义访问域名。
/// </summary>
public string OSSUrl { get; set; }
/// <summary>
/// 阿里云地域 Id。
/// </summary>
public string RegionId { get; set; }
}
@@ -8,6 +8,8 @@
// 5.请不得将本软件应用于危害国家安全、荣誉和利益的行为,不能以任何形式用于非法为目的的行为。 // 5.请不得将本软件应用于危害国家安全、荣誉和利益的行为,不能以任何形式用于非法为目的的行为。
// 6.任何基于本软件而产生的一切法律纠纷和责任,均于我司无关。 // 6.任何基于本软件而产生的一切法律纠纷和责任,均于我司无关。
using Aliyun.OSS;
namespace SimpleAdmin.System; namespace SimpleAdmin.System;
/// <summary> /// <summary>
@@ -95,6 +97,8 @@ public class FileService : DbRepository<SysFile>, IFileService
return GetFileStreamResult(devFile.StoragePath, devFile.Name); return GetFileStreamResult(devFile.StoragePath, devFile.Name);
if (devFile.Engine == SysDictConst.FILE_ENGINE_MINIO) if (devFile.Engine == SysDictConst.FILE_ENGINE_MINIO)
return await GetFileStreamResultFromMinio(devFile.ObjName, devFile.Name); return await GetFileStreamResultFromMinio(devFile.ObjName, devFile.Name);
if (devFile.Engine == SysDictConst.FILE_ENGINE_ALIYUN_OSS)
return await GetFileStreamResultFromAliyunOss(devFile);
return null; return null;
} }
return null; return null;
@@ -142,6 +146,9 @@ public class FileService : DbRepository<SysFile>, IFileService
} }
break; break;
case SysDictConst.FILE_ENGINE_ALIYUN_OSS:
(bucketName, objName, storageUrl) = await StorageAliyunOss(objectId, file);
break;
default: default:
throw Oops.Bah("不支持的文件引擎"); throw Oops.Bah("不支持的文件引擎");
@@ -154,13 +161,13 @@ public class FileService : DbRepository<SysFile>, IFileService
Engine = engine, Engine = engine,
Bucket = bucketName, Bucket = bucketName,
Name = file.FileName, Name = file.FileName,
Suffix = fileSuffix.Split(".")[1], Suffix = fileSuffix.TrimStart('.'),
ObjName = objName, ObjName = objName,
SizeKb = fileSizeKb, SizeKb = fileSizeKb,
SizeInfo = GetSizeInfo(fileSizeKb), SizeInfo = GetSizeInfo(fileSizeKb),
StoragePath = storageUrl StoragePath = storageUrl
}; };
if (engine != CateGoryConst.CONFIG_FILE_LOCAL)//如果不是本地,设置下载地址 if (engine != SysDictConst.FILE_ENGINE_LOCAL)//如果不是本地,设置下载地址
{ {
devFile.DownloadPath = storageUrl; devFile.DownloadPath = storageUrl;
} }
@@ -236,6 +243,111 @@ public class FileService : DbRepository<SysFile>, IFileService
return (fileObjectName, downloadUrl); return (fileObjectName, downloadUrl);
} }
/// <summary>
/// 将入职业务文件存储到阿里云 OSS。
/// </summary>
private async Task<(string bucketName, string objName, string downloadUrl)> StorageAliyunOss(
long fileId,
IFormFile file)
{
var setting = GetOssSetting();
var suffix = Path.GetExtension(file.FileName).ToLowerInvariant();
var objectName = $"onboarding/{DateTime.Now:yyyy/MM/dd}/{fileId}{suffix}";
var metadata = new ObjectMetadata();
if (!string.IsNullOrWhiteSpace(file.ContentType))
metadata.ContentType = file.ContentType;
await using var stream = file.OpenReadStream();
var client = CreateOssClient(setting);
client.PutObject(setting.Bucket, objectName, stream, metadata);
var downloadUrl = string.IsNullOrWhiteSpace(setting.OSSUrl)
? $"https://{setting.Bucket}.{setting.EndPoint.Trim().TrimEnd('/')}/{objectName}"
: $"{setting.OSSUrl.Trim().TrimEnd('/')}/{objectName}";
return (setting.Bucket, objectName, downloadUrl);
}
/// <summary>
/// 从阿里云 OSS 读取业务文件并通过统一下载接口返回。
/// </summary>
private async Task<FileStreamResult> GetFileStreamResultFromAliyunOss(SysFile file)
{
var setting = GetOssSetting();
var client = CreateOssClient(setting);
var ossObject = client.GetObject(file.Bucket, file.ObjName);
var memory = new MemoryStream();
await using (ossObject.Content)
{
await ossObject.Content.CopyToAsync(memory);
}
memory.Position = 0;
var fileName = HttpUtility.UrlEncode(file.Name, Encoding.GetEncoding("UTF-8"));
return new FileStreamResult(memory, "application/octet-stream")
{
FileDownloadName = fileName
};
}
/// <summary>
/// 读取并校验阿里云 OSS 配置。
/// </summary>
private static OssSettingOptions GetOssSetting()
{
var setting = App.GetConfig<OssSettingOptions>("OssSetting", true);
var configurationIncomplete = setting == null
|| string.IsNullOrWhiteSpace(setting.AccessKeyID)
|| string.IsNullOrWhiteSpace(setting.AccessKeySecret)
|| string.IsNullOrWhiteSpace(setting.Bucket)
|| string.IsNullOrWhiteSpace(setting.EndPoint);
if (configurationIncomplete)
throw Oops.Bah("阿里云 OSS 配置不完整");
return setting;
}
/// <summary>
/// 通过 STS 临时凭证创建阿里云 OSS 客户端。
/// </summary>
private static OssClient CreateOssClient(OssSettingOptions setting)
{
var endpoint = setting.EndPoint.Trim();
if (!endpoint.StartsWith("http://", StringComparison.OrdinalIgnoreCase)
&& !endpoint.StartsWith("https://", StringComparison.OrdinalIgnoreCase))
{
endpoint = "https://" + endpoint;
}
if (string.IsNullOrWhiteSpace(setting.RoleArn))
return new OssClient(endpoint, setting.AccessKeyID, setting.AccessKeySecret);
var stsConfig = new AlibabaCloud.OpenApiClient.Models.Config
{
AccessKeyId = setting.AccessKeyID,
AccessKeySecret = setting.AccessKeySecret,
Endpoint = setting.StsEndpoint
};
var stsClient = new AlibabaCloud.SDK.Sts20150401.Client(stsConfig);
var request = new AlibabaCloud.SDK.Sts20150401.Models.AssumeRoleRequest
{
RoleArn = setting.RoleArn,
RoleSessionName = string.IsNullOrWhiteSpace(setting.RoleSessionName)
? "onboardx-server"
: setting.RoleSessionName,
DurationSeconds = 3600
};
var credentials = stsClient.AssumeRole(request).Body.Credentials;
if (credentials == null)
throw Oops.Bah("获取阿里云 OSS 临时凭证失败");
return new OssClient(
endpoint,
credentials.AccessKeyId,
credentials.AccessKeySecret,
credentials.SecurityToken);
}
/// <summary> /// <summary>
/// 获取文件大小 /// 获取文件大小
/// </summary> /// </summary>
+3 -1
View File
@@ -1,7 +1,9 @@
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<ItemGroup> <ItemGroup>
<PackageReference Include="AlibabaCloud.SDK.Sts20150401" Version="1.2.0"/>
<PackageReference Include="Aliyun.OSS.SDK.NetCore" Version="2.14.0"/>
<PackageReference Include="IP2Region.Net" Version="2.0.2"/> <PackageReference Include="IP2Region.Net" Version="2.0.2"/>
<PackageReference Include="UAParser" Version="3.1.47"/> <PackageReference Include="UAParser" Version="3.1.47"/>
<PackageReference Include="Magicodes.IE.Excel" Version="2.7.5.1"/> <PackageReference Include="Magicodes.IE.Excel" Version="2.7.5.1"/>
+85
View File
@@ -506,6 +506,11 @@
MINIO MINIO
</summary> </summary>
</member> </member>
<member name="F:SimpleAdmin.System.SysDictConst.FILE_ENGINE_ALIYUN_OSS">
<summary>
阿里云 OSS,当前仅供入职业务文件使用。
</summary>
</member>
<member name="F:SimpleAdmin.System.SysDictConst.DICT_CATEGORY_FRM"> <member name="F:SimpleAdmin.System.SysDictConst.DICT_CATEGORY_FRM">
<summary> <summary>
系统 系统
@@ -2670,6 +2675,66 @@
对象映射 对象映射
</summary> </summary>
</member> </member>
<member name="T:SimpleAdmin.System.OssSettingOptions">
<summary>
阿里云 OSS 及文档预览服务配置。
</summary>
</member>
<member name="P:SimpleAdmin.System.OssSettingOptions.AccessKeyID">
<summary>
OSS 访问密钥 Id。
</summary>
</member>
<member name="P:SimpleAdmin.System.OssSettingOptions.AccessKeySecret">
<summary>
OSS 访问密钥。
</summary>
</member>
<member name="P:SimpleAdmin.System.OssSettingOptions.RoleArn">
<summary>
用于换取临时凭证的 RAM 角色 ARN。
</summary>
</member>
<member name="P:SimpleAdmin.System.OssSettingOptions.RoleSessionName">
<summary>
STS 角色会话名称。
</summary>
</member>
<member name="P:SimpleAdmin.System.OssSettingOptions.StsEndpoint">
<summary>
STS 服务端点。
</summary>
</member>
<member name="P:SimpleAdmin.System.OssSettingOptions.Bucket">
<summary>
OSS Bucket 名称。
</summary>
</member>
<member name="P:SimpleAdmin.System.OssSettingOptions.EndPoint">
<summary>
OSS 服务端点。
</summary>
</member>
<member name="P:SimpleAdmin.System.OssSettingOptions.IMMEndpoint">
<summary>
IMM 服务端点。
</summary>
</member>
<member name="P:SimpleAdmin.System.OssSettingOptions.IMMProject">
<summary>
IMM 项目名称。
</summary>
</member>
<member name="P:SimpleAdmin.System.OssSettingOptions.OSSUrl">
<summary>
OSS 自定义访问域名。
</summary>
</member>
<member name="P:SimpleAdmin.System.OssSettingOptions.RegionId">
<summary>
阿里云地域 Id。
</summary>
</member>
<member name="T:SimpleAdmin.System.SystemSettingsOptions"> <member name="T:SimpleAdmin.System.SystemSettingsOptions">
<summary> <summary>
系统配置 系统配置
@@ -3588,6 +3653,26 @@
<param name="file"></param> <param name="file"></param>
<returns></returns> <returns></returns>
</member> </member>
<member name="M:SimpleAdmin.System.FileService.StorageAliyunOss(System.Int64,Microsoft.AspNetCore.Http.IFormFile)">
<summary>
将入职业务文件存储到阿里云 OSS。
</summary>
</member>
<member name="M:SimpleAdmin.System.FileService.GetFileStreamResultFromAliyunOss(SimpleAdmin.System.SysFile)">
<summary>
从阿里云 OSS 读取业务文件并通过统一下载接口返回。
</summary>
</member>
<member name="M:SimpleAdmin.System.FileService.GetOssSetting">
<summary>
读取并校验阿里云 OSS 配置。
</summary>
</member>
<member name="M:SimpleAdmin.System.FileService.CreateOssClient(SimpleAdmin.System.OssSettingOptions)">
<summary>
通过 STS 临时凭证创建阿里云 OSS 客户端。
</summary>
</member>
<member name="M:SimpleAdmin.System.FileService.GetSizeInfo(System.Int64)"> <member name="M:SimpleAdmin.System.FileService.GetSizeInfo(System.Int64)">
<summary> <summary>
获取文件大小 获取文件大小
@@ -24,5 +24,18 @@
"InitSeedData": false, //是否初始化种子数据 "InitSeedData": false, //是否初始化种子数据
"SuperAdminViewAllData": true, //是否超级管理员可以查看所有数据 "SuperAdminViewAllData": true, //是否超级管理员可以查看所有数据
"UseMessageCenter": false //是否启用通知中心 "UseMessageCenter": false //是否启用通知中心
},
"OssSetting": {
"AccessKeyID": "LTAI5t9VQvLERXtMHC9BK31f",
"AccessKeySecret": "YomhludvaOmLZo1K0vUDoeBhWRjxWL",
"RoleArn": "acs:ram::1436008104961462:role/onboardx-sts",
"RoleSessionName": "onboardx-server",
"StsEndpoint": "sts.cn-beijing.aliyuncs.com",
"Bucket": "onboardx",
"EndPoint": "oss-cn-beijing.aliyuncs.com",
"IMMEndpoint": "",
"IMMProject": "",
"OSSUrl": "https://onboardx.oss-cn-beijing.aliyuncs.com",
"RegionId": "oss-cn-beijing"
} }
} }
+13
View File
@@ -24,5 +24,18 @@
"InitSeedData": false, //是否初始化种子数据 "InitSeedData": false, //是否初始化种子数据
"SuperAdminViewAllData": true, //是否超级管理员可以查看所有数据 "SuperAdminViewAllData": true, //是否超级管理员可以查看所有数据
"UseMessageCenter": false //是否启用通知中心 "UseMessageCenter": false //是否启用通知中心
},
"OssSetting": {
"AccessKeyID": "LTAI5t9VQvLERXtMHC9BK31f",
"AccessKeySecret": "YomhludvaOmLZo1K0vUDoeBhWRjxWL",
"RoleArn": "acs:ram::1436008104961462:role/onboardx-sts",
"RoleSessionName": "onboardx-server",
"StsEndpoint": "sts.cn-beijing.aliyuncs.com",
"Bucket": "onboardx",
"EndPoint": "oss-cn-beijing.aliyuncs.com",
"IMMEndpoint": "",
"IMMProject": "",
"OSSUrl": "https://onboardx.oss-cn-beijing.aliyuncs.com",
"RegionId": "oss-cn-beijing"
} }
} }
@@ -9,10 +9,32 @@ namespace SimpleAdmin.Web.Core;
public class OnboardingContractTemplateController public class OnboardingContractTemplateController
{ {
private readonly IOnboardingContractTemplateService _service; private readonly IOnboardingContractTemplateService _service;
private readonly IFileService _fileService;
public OnboardingContractTemplateController(IOnboardingContractTemplateService service) public OnboardingContractTemplateController(
IOnboardingContractTemplateService service,
IFileService fileService)
{ {
_service = service; _service = service;
_fileService = fileService;
}
/// <summary>
/// 将入职合同 DOCX 模板上传到阿里云 OSS。
/// </summary>
[HttpPost("file/upload-docx")]
[Consumes("multipart/form-data")]
[RequestSizeLimit(20 * 1024 * 1024)]
public async Task<long> UploadDocx([FromForm] IFormFile file)
{
if (file == null || file.Length == 0)
throw Oops.Bah("请选择需要上传的 DOCX 文件");
var suffix = Path.GetExtension(file.FileName);
if (!string.Equals(suffix, ".docx", StringComparison.OrdinalIgnoreCase))
throw Oops.Bah("合同模板仅支持 DOCX 文件");
return await _fileService.UploadFile(SysDictConst.FILE_ENGINE_ALIYUN_OSS, file);
} }
/// <summary> /// <summary>
@@ -59,11 +59,14 @@ public class OnboardingPublicSubmissionController
[HttpPost("upload-material")] [HttpPost("upload-material")]
[Consumes("multipart/form-data")] [Consumes("multipart/form-data")]
[RequestSizeLimit(20 * 1024 * 1024)] [RequestSizeLimit(20 * 1024 * 1024)]
public async Task UploadMaterial([FromForm] string token, [FromForm] long materialTypeId, IFormFile file) public async Task UploadMaterial(
[FromQuery] string token,
[FromQuery] long materialTypeId,
[FromForm] IFormFile file)
{ {
if (file == null || file.Length == 0) if (file == null || file.Length == 0)
{ {
throw new ArgumentException("请选择需要上传的文件"); throw Oops.Bah("请选择需要上传的文件");
} }
await _service.ValidateMaterialUpload(new OnboardingMaterialUploadValidationInput await _service.ValidateMaterialUpload(new OnboardingMaterialUploadValidationInput
@@ -74,7 +77,7 @@ public class OnboardingPublicSubmissionController
FileLength = file.Length FileLength = file.Length
}); });
var fileId = await _fileService.UploadFile(SysDictConst.FILE_ENGINE_LOCAL, file); var fileId = await _fileService.UploadFile(SysDictConst.FILE_ENGINE_ALIYUN_OSS, file);
await _service.SaveMaterial(new OnboardingSaveMaterialInput await _service.SaveMaterial(new OnboardingSaveMaterialInput
{ {
Token = token, Token = token,
+2 -1
View File
@@ -1,4 +1,4 @@
// Copyright (c) 2022-Now 少林寺驻北固山办事处大神父王喇嘛 // Copyright (c) 2022-Now 少林寺驻北固山办事处大神父王喇嘛
// //
// SimpleAdmin 基于 Apache License Version 2.0 协议发布,可用于商业项目,但必须遵守以下补充条款: // SimpleAdmin 基于 Apache License Version 2.0 协议发布,可用于商业项目,但必须遵守以下补充条款:
// 1.请不要删除和修改根目录下的LICENSE文件。 // 1.请不要删除和修改根目录下的LICENSE文件。
@@ -10,6 +10,7 @@
global using MoYu; global using MoYu;
global using MoYu.DynamicApiController; global using MoYu.DynamicApiController;
global using MoYu.FriendlyException;
global using MoYu.Logging; global using MoYu.Logging;
global using Microsoft.AspNetCore.Authorization; global using Microsoft.AspNetCore.Authorization;
global using Microsoft.AspNetCore.Builder; global using Microsoft.AspNetCore.Builder;
@@ -91,6 +91,11 @@
提供合同模板、版本、变量和适用范围相关 HTTP 接口。 提供合同模板、版本、变量和适用范围相关 HTTP 接口。
</summary> </summary>
</member> </member>
<member name="M:SimpleAdmin.Web.Core.OnboardingContractTemplateController.UploadDocx(Microsoft.AspNetCore.Http.IFormFile)">
<summary>
将入职合同 DOCX 模板上传到阿里云 OSS。
</summary>
</member>
<member name="M:SimpleAdmin.Web.Core.OnboardingContractTemplateController.Page(SimpleAdmin.Application.OnboardingContractTemplatePageInput)"> <member name="M:SimpleAdmin.Web.Core.OnboardingContractTemplateController.Page(SimpleAdmin.Application.OnboardingContractTemplatePageInput)">
<summary> <summary>
分页查询合同模板。 分页查询合同模板。