diff --git a/aspnet-core/framework/exporter/LINGYUN.Abp.Exporter.Application.Contracts/FodyWeavers.xml b/aspnet-core/framework/exporter/LINGYUN.Abp.Exporter.Application.Contracts/FodyWeavers.xml
new file mode 100644
index 000000000..1715698cc
--- /dev/null
+++ b/aspnet-core/framework/exporter/LINGYUN.Abp.Exporter.Application.Contracts/FodyWeavers.xml
@@ -0,0 +1,3 @@
+
+
+
\ No newline at end of file
diff --git a/aspnet-core/framework/exporter/LINGYUN.Abp.Exporter.Application.Contracts/FodyWeavers.xsd b/aspnet-core/framework/exporter/LINGYUN.Abp.Exporter.Application.Contracts/FodyWeavers.xsd
new file mode 100644
index 000000000..3f3946e28
--- /dev/null
+++ b/aspnet-core/framework/exporter/LINGYUN.Abp.Exporter.Application.Contracts/FodyWeavers.xsd
@@ -0,0 +1,30 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 'true' to run assembly verification (PEVerify) on the target assembly after all weavers have been executed.
+
+
+
+
+ A comma-separated list of error codes that can be safely ignored in assembly verification.
+
+
+
+
+ 'false' to turn off automatic generation of the XML Schema file.
+
+
+
+
+
\ No newline at end of file
diff --git a/aspnet-core/framework/exporter/LINGYUN.Abp.Exporter.Application.Contracts/LINGYUN.Abp.Exporter.Application.Contracts.csproj b/aspnet-core/framework/exporter/LINGYUN.Abp.Exporter.Application.Contracts/LINGYUN.Abp.Exporter.Application.Contracts.csproj
new file mode 100644
index 000000000..bc998ef75
--- /dev/null
+++ b/aspnet-core/framework/exporter/LINGYUN.Abp.Exporter.Application.Contracts/LINGYUN.Abp.Exporter.Application.Contracts.csproj
@@ -0,0 +1,20 @@
+
+
+
+
+
+
+ netstandard2.0;netstandard2.1;net8.0
+ LINGYUN.Abp.Exporter.Application.Contracts
+ LINGYUN.Abp.Exporter.Application.Contracts
+ false
+ false
+ false
+
+
+
+
+
+
+
+
diff --git a/aspnet-core/framework/exporter/LINGYUN.Abp.Exporter.Application.Contracts/LINGYUN/Abp/Exporter/AbpExporterApplicationContractsModule.cs b/aspnet-core/framework/exporter/LINGYUN.Abp.Exporter.Application.Contracts/LINGYUN/Abp/Exporter/AbpExporterApplicationContractsModule.cs
new file mode 100644
index 000000000..49523b1fb
--- /dev/null
+++ b/aspnet-core/framework/exporter/LINGYUN.Abp.Exporter.Application.Contracts/LINGYUN/Abp/Exporter/AbpExporterApplicationContractsModule.cs
@@ -0,0 +1,9 @@
+using Volo.Abp.Application;
+using Volo.Abp.Modularity;
+
+namespace LINGYUN.Abp.Exporter;
+
+[DependsOn(typeof(AbpDddApplicationContractsModule))]
+public class AbpExporterApplicationContractsModule : AbpModule
+{
+}
diff --git a/aspnet-core/framework/exporter/LINGYUN.Abp.Exporter.Application.Contracts/LINGYUN/Abp/Exporter/IExporterAppService.cs b/aspnet-core/framework/exporter/LINGYUN.Abp.Exporter.Application.Contracts/LINGYUN/Abp/Exporter/IExporterAppService.cs
new file mode 100644
index 000000000..34e3177b6
--- /dev/null
+++ b/aspnet-core/framework/exporter/LINGYUN.Abp.Exporter.Application.Contracts/LINGYUN/Abp/Exporter/IExporterAppService.cs
@@ -0,0 +1,19 @@
+using System.Threading.Tasks;
+using Volo.Abp.Content;
+
+namespace LINGYUN.Abp.Exporter;
+///
+/// 数据导出服务接口
+///
+/// 导出的实体数据传输对象
+/// 实体数据过滤数据对象
+public interface IExporterAppService
+ where TEntityExportDto : class, new()
+{
+ ///
+ /// 导出数据(默认实现Excel)
+ ///
+ /// 数据过滤条件
+ ///
+ Task ExportAsync(TEntityListGetInput input);
+}
diff --git a/aspnet-core/framework/exporter/LINGYUN.Abp.Exporter.Application.Contracts/LINGYUN/Abp/Exporter/IImporterAppService.cs b/aspnet-core/framework/exporter/LINGYUN.Abp.Exporter.Application.Contracts/LINGYUN/Abp/Exporter/IImporterAppService.cs
new file mode 100644
index 000000000..d2a6f30c3
--- /dev/null
+++ b/aspnet-core/framework/exporter/LINGYUN.Abp.Exporter.Application.Contracts/LINGYUN/Abp/Exporter/IImporterAppService.cs
@@ -0,0 +1,17 @@
+using System.Threading.Tasks;
+
+namespace LINGYUN.Abp.Exporter;
+///
+/// 数据导入服务接口
+///
+///
+public interface IImporterAppService
+ where TEntityImportDto: class, new()
+{
+ ///
+ /// 导入数据(默认从Excel)
+ ///
+ /// 导入的数据文件流
+ ///
+ Task ImportAsync(TEntityImportDto input);
+}
diff --git a/aspnet-core/framework/exporter/LINGYUN.Abp.Exporter.Application.Contracts/README.md b/aspnet-core/framework/exporter/LINGYUN.Abp.Exporter.Application.Contracts/README.md
new file mode 100644
index 000000000..aac0f9666
--- /dev/null
+++ b/aspnet-core/framework/exporter/LINGYUN.Abp.Exporter.Application.Contracts/README.md
@@ -0,0 +1,103 @@
+# LINGYUN.Abp.Exporter.Application.Contracts
+
+数据导出/导入接口契约层,定义通用的数据导入/导入服务接口
+
+## 配置使用
+
+
+> 配置模块依赖
+```csharp
+
+[DependsOn(
+ typeof(AbpExporterApplicationContractsModule)
+ )]
+public class YouProjectModule : AbpModule
+{
+
+}
+```
+
+> 定义导出类型
+```csharp
+public class DemoClassDto
+{
+ public string Name { get; set; }
+ public string Remarks { get; set; }
+
+ public DemoClassDto()
+ {
+ }
+}
+```
+
+> 定义导入类型
+```csharp
+public class DemoClassImportInput
+{
+ [Required]
+ public IRemoteStreamContent Content { get; set; }
+
+ public DemoClassImportInput()
+ {
+ }
+}
+```
+
+> 定义数据过滤类型
+```csharp
+public class DemoClassExportListInput
+{
+ public string Filter { get; set; }
+
+ [Required]
+ public string FileName { get; set; }
+
+ public DemoClassExportListInput()
+ {
+ }
+}
+```
+
+> 导出数据
+```csharp
+ public class ExportDemoClassExportAppService : IExporterAppService
+ {
+ private readonly IExporterProvider _exporterProvider;
+
+ public ExportDemoClassExportAppService(IExporterProvider exporterProvider)
+ {
+ _exporterProvider = exporterProvider;
+ }
+
+ public async virtual Task ExportAsync(DemoClassExportListInput input)
+ {
+ var dtos = 通过仓储接口查询数据列表;
+
+ var stream = await _exporterProvider.ExportAsync(dtos);
+
+ return new RemoteStreamContent(stream, input.FileName);
+ }
+ }
+```
+
+> 导入数据
+```csharp
+ public class ExportDemoClassImportAppService : IImporterAppService
+ {
+ private readonly IImporterProvider _importerProvider;
+
+ public ExportDemoClassImportAppService(IImporterProvider importerProvider)
+ {
+ _importerProvider = importerProvider;
+ }
+
+ public async virtual Task ImportAsync(DemoClassImportInput input)
+ {
+ var stream = input.Content.GetStream();
+
+ var demos = await _importerProvider.ImportAsync(stream);
+
+ // 其他操作
+ }
+ }
+```
diff --git a/aspnet-core/framework/exporter/LINGYUN.Abp.Exporter.Application/FodyWeavers.xml b/aspnet-core/framework/exporter/LINGYUN.Abp.Exporter.Application/FodyWeavers.xml
new file mode 100644
index 000000000..1715698cc
--- /dev/null
+++ b/aspnet-core/framework/exporter/LINGYUN.Abp.Exporter.Application/FodyWeavers.xml
@@ -0,0 +1,3 @@
+
+
+
\ No newline at end of file
diff --git a/aspnet-core/framework/exporter/LINGYUN.Abp.Exporter.Application/FodyWeavers.xsd b/aspnet-core/framework/exporter/LINGYUN.Abp.Exporter.Application/FodyWeavers.xsd
new file mode 100644
index 000000000..3f3946e28
--- /dev/null
+++ b/aspnet-core/framework/exporter/LINGYUN.Abp.Exporter.Application/FodyWeavers.xsd
@@ -0,0 +1,30 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 'true' to run assembly verification (PEVerify) on the target assembly after all weavers have been executed.
+
+
+
+
+ A comma-separated list of error codes that can be safely ignored in assembly verification.
+
+
+
+
+ 'false' to turn off automatic generation of the XML Schema file.
+
+
+
+
+
\ No newline at end of file
diff --git a/aspnet-core/framework/exporter/LINGYUN.Abp.Exporter.Application/LINGYUN.Abp.Exporter.Application.csproj b/aspnet-core/framework/exporter/LINGYUN.Abp.Exporter.Application/LINGYUN.Abp.Exporter.Application.csproj
new file mode 100644
index 000000000..33ae7de47
--- /dev/null
+++ b/aspnet-core/framework/exporter/LINGYUN.Abp.Exporter.Application/LINGYUN.Abp.Exporter.Application.csproj
@@ -0,0 +1,25 @@
+
+
+
+
+
+
+ netstandard2.0;netstandard2.1;net8.0
+ LINGYUN.Abp.Exporter.Application
+ LINGYUN.Abp.Exporter.Application
+ false
+ false
+ false
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/aspnet-core/framework/exporter/LINGYUN.Abp.Exporter.Application/LINGYUN/Abp/Exporter/AbpExporterApplicationModule.cs b/aspnet-core/framework/exporter/LINGYUN.Abp.Exporter.Application/LINGYUN/Abp/Exporter/AbpExporterApplicationModule.cs
new file mode 100644
index 000000000..022b0a7d5
--- /dev/null
+++ b/aspnet-core/framework/exporter/LINGYUN.Abp.Exporter.Application/LINGYUN/Abp/Exporter/AbpExporterApplicationModule.cs
@@ -0,0 +1,12 @@
+using Volo.Abp.Application;
+using Volo.Abp.Modularity;
+
+namespace LINGYUN.Abp.Exporter;
+
+[DependsOn(
+ typeof(AbpDddApplicationModule),
+ typeof(AbpExporterCoreModule),
+ typeof(AbpExporterApplicationContractsModule))]
+public class AbpExporterApplicationModule : AbpModule
+{
+}
diff --git a/aspnet-core/framework/exporter/LINGYUN.Abp.Exporter.Application/LINGYUN/Abp/Exporter/ExporterAppService.cs b/aspnet-core/framework/exporter/LINGYUN.Abp.Exporter.Application/LINGYUN/Abp/Exporter/ExporterAppService.cs
new file mode 100644
index 000000000..2ccc95eb8
--- /dev/null
+++ b/aspnet-core/framework/exporter/LINGYUN.Abp.Exporter.Application/LINGYUN/Abp/Exporter/ExporterAppService.cs
@@ -0,0 +1,50 @@
+using System.Collections.Generic;
+using System.Threading.Tasks;
+using Volo.Abp.Application.Services;
+using Volo.Abp.Content;
+
+namespace LINGYUN.Abp.Exporter;
+
+public abstract class ExporterAppService : ApplicationService, IExporterAppService
+ where TEntityExportDto : class, new()
+{
+ private readonly IExporterProvider _exporterProvider;
+
+ protected ExporterAppService(IExporterProvider exporterProvider)
+ {
+ _exporterProvider = exporterProvider;
+ }
+
+ public async virtual Task ExportAsync(TEntityListGetInput input)
+ {
+ var fileName = GetExportFileName(input);
+
+ var entities = await GetListAsync(input);
+ var entitieDtoList = MapEntitiesToDto(entities);
+
+ var stream = await _exporterProvider.ExportAsync(entitieDtoList);
+
+ return new RemoteStreamContent(stream, fileName);
+ }
+ ///
+ /// 实现方法用以返回导出文件名
+ ///
+ ///
+ ///
+ protected abstract string GetExportFileName(TEntityListGetInput input);
+ ///
+ /// 实现方法用以查询需要导出的实体列表
+ ///
+ ///
+ ///
+ protected abstract Task> GetListAsync(TEntityListGetInput input);
+ ///
+ /// 实现方法用以实体数据传输对象映射
+ ///
+ ///
+ ///
+ protected virtual List MapEntitiesToDto(List entities)
+ {
+ return ObjectMapper.Map, List>(entities);
+ }
+}
diff --git a/aspnet-core/framework/exporter/LINGYUN.Abp.Exporter.Application/LINGYUN/Abp/Exporter/ImporterAppService.cs b/aspnet-core/framework/exporter/LINGYUN.Abp.Exporter.Application/LINGYUN/Abp/Exporter/ImporterAppService.cs
new file mode 100644
index 000000000..0e6735029
--- /dev/null
+++ b/aspnet-core/framework/exporter/LINGYUN.Abp.Exporter.Application/LINGYUN/Abp/Exporter/ImporterAppService.cs
@@ -0,0 +1,47 @@
+using System.Collections.Generic;
+using System.IO;
+using System.Threading.Tasks;
+using Volo.Abp.Application.Services;
+
+namespace LINGYUN.Abp.Exporter;
+
+public abstract class ImporterAppService : ApplicationService, IImporterAppService
+ where TEntityImportDto : class, new()
+{
+ private readonly IImporterProvider _importerProvider;
+ protected ImporterAppService(IImporterProvider importerProvider)
+ {
+ _importerProvider = importerProvider;
+ }
+
+ public async virtual Task ImportAsync(TEntityImportDto input)
+ {
+ var stream = await GetImportStream(input);
+ var entitieDtoList = await _importerProvider.ImportAsync(stream);
+
+ var entities = MapDtoToEntities([.. entitieDtoList]);
+
+ await SaveManyEntities(entities);
+ }
+ ///
+ /// 实现方法用以从导入数据中提取文件流
+ ///
+ ///
+ ///
+ protected abstract Task GetImportStream(TEntityImportDto input);
+ ///
+ /// 实现方法用以保存实体列表到持久化设施
+ ///
+ ///
+ ///
+ protected abstract Task SaveManyEntities(ICollection entities);
+ ///
+ /// 实现方法用以实体数据传输对象映射
+ ///
+ ///
+ ///
+ protected virtual List MapDtoToEntities(List entitieDtoList)
+ {
+ return ObjectMapper.Map, List>(entitieDtoList);
+ }
+}
diff --git a/aspnet-core/framework/exporter/LINGYUN.Abp.Exporter.Core/FodyWeavers.xml b/aspnet-core/framework/exporter/LINGYUN.Abp.Exporter.Core/FodyWeavers.xml
new file mode 100644
index 000000000..00e1d9a1c
--- /dev/null
+++ b/aspnet-core/framework/exporter/LINGYUN.Abp.Exporter.Core/FodyWeavers.xml
@@ -0,0 +1,3 @@
+
+
+
\ No newline at end of file
diff --git a/aspnet-core/framework/exporter/LINGYUN.Abp.Exporter.Core/FodyWeavers.xsd b/aspnet-core/framework/exporter/LINGYUN.Abp.Exporter.Core/FodyWeavers.xsd
new file mode 100644
index 000000000..3f3946e28
--- /dev/null
+++ b/aspnet-core/framework/exporter/LINGYUN.Abp.Exporter.Core/FodyWeavers.xsd
@@ -0,0 +1,30 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 'true' to run assembly verification (PEVerify) on the target assembly after all weavers have been executed.
+
+
+
+
+ A comma-separated list of error codes that can be safely ignored in assembly verification.
+
+
+
+
+ 'false' to turn off automatic generation of the XML Schema file.
+
+
+
+
+
\ No newline at end of file
diff --git a/aspnet-core/framework/exporter/LINGYUN.Abp.Exporter.Core/LINGYUN.Abp.Exporter.Core.csproj b/aspnet-core/framework/exporter/LINGYUN.Abp.Exporter.Core/LINGYUN.Abp.Exporter.Core.csproj
new file mode 100644
index 000000000..aa94a35e8
--- /dev/null
+++ b/aspnet-core/framework/exporter/LINGYUN.Abp.Exporter.Core/LINGYUN.Abp.Exporter.Core.csproj
@@ -0,0 +1,30 @@
+
+
+
+
+
+
+ netstandard2.0;netstandard2.1;net8.0
+ LINGYUN.Abp.Exporter.Core
+ LINGYUN.Abp.Exporter.Core
+ false
+ false
+ false
+ enable
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/aspnet-core/framework/exporter/LINGYUN.Abp.Exporter.Core/LINGYUN/Abp/Exporter/AbpExporterCoreModule.cs b/aspnet-core/framework/exporter/LINGYUN.Abp.Exporter.Core/LINGYUN/Abp/Exporter/AbpExporterCoreModule.cs
new file mode 100644
index 000000000..5ba31e424
--- /dev/null
+++ b/aspnet-core/framework/exporter/LINGYUN.Abp.Exporter.Core/LINGYUN/Abp/Exporter/AbpExporterCoreModule.cs
@@ -0,0 +1,31 @@
+using LINGYUN.Abp.Exporter.Localization;
+using Volo.Abp.Localization;
+using Volo.Abp.Localization.ExceptionHandling;
+using Volo.Abp.Modularity;
+using Volo.Abp.VirtualFileSystem;
+
+namespace LINGYUN.Abp.Exporter;
+
+[DependsOn(typeof(AbpLocalizationModule))]
+public class AbpExporterCoreModule : AbpModule
+{
+ public override void ConfigureServices(ServiceConfigurationContext context)
+ {
+ Configure(options =>
+ {
+ options.FileSets.AddEmbedded();
+ });
+
+ Configure(options =>
+ {
+ options.Resources
+ .Add("en")
+ .AddVirtualJson("/LINGYUN/Abp/Exporter/Localization/Resources");
+ });
+
+ Configure(options =>
+ {
+ options.MapCodeNamespace(ExporterErrorCodes.Namespace, typeof(AbpExporterResource));
+ });
+ }
+}
\ No newline at end of file
diff --git a/aspnet-core/framework/exporter/LINGYUN.Abp.Exporter.Core/LINGYUN/Abp/Exporter/ExporterErrorCodes.cs b/aspnet-core/framework/exporter/LINGYUN.Abp.Exporter.Core/LINGYUN/Abp/Exporter/ExporterErrorCodes.cs
new file mode 100644
index 000000000..9ad0cc137
--- /dev/null
+++ b/aspnet-core/framework/exporter/LINGYUN.Abp.Exporter.Core/LINGYUN/Abp/Exporter/ExporterErrorCodes.cs
@@ -0,0 +1,9 @@
+namespace LINGYUN.Abp.Exporter;
+public static class ExporterErrorCodes
+{
+ public const string Namespace = "AbpExporter";
+ ///
+ /// 导入数据过程中出现一些错误,请检查数据有效性!
+ ///
+ public const string ImportDataError = Namespace + ":010001";
+}
diff --git a/aspnet-core/framework/exporter/LINGYUN.Abp.Exporter.Core/LINGYUN/Abp/Exporter/IExporterProvider.cs b/aspnet-core/framework/exporter/LINGYUN.Abp.Exporter.Core/LINGYUN/Abp/Exporter/IExporterProvider.cs
new file mode 100644
index 000000000..0f31c7b70
--- /dev/null
+++ b/aspnet-core/framework/exporter/LINGYUN.Abp.Exporter.Core/LINGYUN/Abp/Exporter/IExporterProvider.cs
@@ -0,0 +1,20 @@
+using System.Collections.Generic;
+using System.IO;
+using System.Threading;
+using System.Threading.Tasks;
+
+namespace LINGYUN.Abp.Exporter;
+///
+/// 数据导出提供者
+///
+public interface IExporterProvider
+{
+ ///
+ /// 数据导出
+ ///
+ /// 导出数据
+ ///
+ ///
+ Task ExportAsync(ICollection data, CancellationToken cancellationToken = default)
+ where T : class, new();
+}
diff --git a/aspnet-core/framework/exporter/LINGYUN.Abp.Exporter.Core/LINGYUN/Abp/Exporter/IImporterProvider.cs b/aspnet-core/framework/exporter/LINGYUN.Abp.Exporter.Core/LINGYUN/Abp/Exporter/IImporterProvider.cs
new file mode 100644
index 000000000..f1b0673dd
--- /dev/null
+++ b/aspnet-core/framework/exporter/LINGYUN.Abp.Exporter.Core/LINGYUN/Abp/Exporter/IImporterProvider.cs
@@ -0,0 +1,19 @@
+using System.Collections.Generic;
+using System.IO;
+using System.Threading.Tasks;
+
+namespace LINGYUN.Abp.Exporter;
+///
+/// 数据导入提供者
+///
+public interface IImporterProvider
+{
+ ///
+ /// 数据导入
+ ///
+ ///
+ ///
+ ///
+ Task> ImportAsync(Stream stream)
+ where T : class, new();
+}
diff --git a/aspnet-core/framework/exporter/LINGYUN.Abp.Exporter.Core/LINGYUN/Abp/Exporter/Localization/AbpExporterResource.cs b/aspnet-core/framework/exporter/LINGYUN.Abp.Exporter.Core/LINGYUN/Abp/Exporter/Localization/AbpExporterResource.cs
new file mode 100644
index 000000000..e80ccb5f3
--- /dev/null
+++ b/aspnet-core/framework/exporter/LINGYUN.Abp.Exporter.Core/LINGYUN/Abp/Exporter/Localization/AbpExporterResource.cs
@@ -0,0 +1,8 @@
+using Volo.Abp.Localization;
+
+namespace LINGYUN.Abp.Exporter.Localization;
+
+[LocalizationResourceName("AbpExporter")]
+public class AbpExporterResource
+{
+}
diff --git a/aspnet-core/framework/exporter/LINGYUN.Abp.Exporter.Core/LINGYUN/Abp/Exporter/Localization/Resources/en.json b/aspnet-core/framework/exporter/LINGYUN.Abp.Exporter.Core/LINGYUN/Abp/Exporter/Localization/Resources/en.json
new file mode 100644
index 000000000..ff6718836
--- /dev/null
+++ b/aspnet-core/framework/exporter/LINGYUN.Abp.Exporter.Core/LINGYUN/Abp/Exporter/Localization/Resources/en.json
@@ -0,0 +1,6 @@
+{
+ "culture": "en",
+ "texts": {
+ "AbpExporter:010001": "There were some errors during the data import process. Please check the validity of the data!"
+ }
+}
\ No newline at end of file
diff --git a/aspnet-core/framework/exporter/LINGYUN.Abp.Exporter.Core/LINGYUN/Abp/Exporter/Localization/Resources/zh-Hans.json b/aspnet-core/framework/exporter/LINGYUN.Abp.Exporter.Core/LINGYUN/Abp/Exporter/Localization/Resources/zh-Hans.json
new file mode 100644
index 000000000..e867e5cdb
--- /dev/null
+++ b/aspnet-core/framework/exporter/LINGYUN.Abp.Exporter.Core/LINGYUN/Abp/Exporter/Localization/Resources/zh-Hans.json
@@ -0,0 +1,6 @@
+{
+ "culture": "zh-Hans",
+ "texts": {
+ "AbpExporter:010001": "导入数据过程中出现一些错误,请检查数据有效性!"
+ }
+}
\ No newline at end of file
diff --git a/aspnet-core/framework/exporter/LINGYUN.Abp.Exporter.Core/LINGYUN/Abp/Exporter/NullExporterProvider.cs b/aspnet-core/framework/exporter/LINGYUN.Abp.Exporter.Core/LINGYUN/Abp/Exporter/NullExporterProvider.cs
new file mode 100644
index 000000000..89457daf4
--- /dev/null
+++ b/aspnet-core/framework/exporter/LINGYUN.Abp.Exporter.Core/LINGYUN/Abp/Exporter/NullExporterProvider.cs
@@ -0,0 +1,17 @@
+using System.Collections.Generic;
+using System.IO;
+using System.Threading;
+using System.Threading.Tasks;
+using Volo.Abp.DependencyInjection;
+
+namespace LINGYUN.Abp.Exporter;
+
+[Dependency(TryRegister = true)]
+public class NullExporterProvider : IExporterProvider, ISingletonDependency
+{
+ private readonly static Stream _nullStreamCache = Stream.Null;
+ public Task ExportAsync(ICollection data, CancellationToken cancellationToken = default) where T : class, new()
+ {
+ return Task.FromResult(_nullStreamCache);
+ }
+}
diff --git a/aspnet-core/framework/exporter/LINGYUN.Abp.Exporter.Core/LINGYUN/Abp/Exporter/NullImporterProvider.cs b/aspnet-core/framework/exporter/LINGYUN.Abp.Exporter.Core/LINGYUN/Abp/Exporter/NullImporterProvider.cs
new file mode 100644
index 000000000..032428a4a
--- /dev/null
+++ b/aspnet-core/framework/exporter/LINGYUN.Abp.Exporter.Core/LINGYUN/Abp/Exporter/NullImporterProvider.cs
@@ -0,0 +1,18 @@
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Threading.Tasks;
+using Volo.Abp.DependencyInjection;
+
+namespace LINGYUN.Abp.Exporter;
+
+[Dependency(TryRegister = true)]
+public class NullImporterProvider : IImporterProvider, ISingletonDependency
+{
+ public Task> ImportAsync(Stream stream) where T : class, new()
+ {
+ IReadOnlyCollection empty = Array.Empty();
+
+ return Task.FromResult(empty);
+ }
+}
diff --git a/aspnet-core/framework/exporter/LINGYUN.Abp.Exporter.Core/README.md b/aspnet-core/framework/exporter/LINGYUN.Abp.Exporter.Core/README.md
new file mode 100644
index 000000000..4993d9489
--- /dev/null
+++ b/aspnet-core/framework/exporter/LINGYUN.Abp.Exporter.Core/README.md
@@ -0,0 +1,76 @@
+# LINGYUN.Abp.Exporter.Core
+
+数据导出/导入核心层
+
+## 配置使用
+
+
+```csharp
+
+ [DependsOn(
+ typeof(AbpExporterCoreModule)
+ )]
+ public class YouProjectModule : AbpModule
+ {
+
+ }
+```
+
+> 导出数据
+```csharp
+ public class ExportDemoClass
+ {
+ private readonly IExporterProvider _exporterProvider;
+
+ public ExportDemoClass(IExporterProvider exporterProvider)
+ {
+ _exporterProvider = exporterProvider;
+ }
+
+ public async virtual Task ExportAsync()
+ {
+ var dataItems = new object[]
+ {
+ new
+ {
+ Name = "name1",
+ Remakrs = "remarks1"
+ }
+ };
+
+ var stream = await _exporterProvider.ExportAsync(dataItems);
+
+ return new RemoteStreamContent(stream, "demo.xlsx");
+ }
+ }
+```
+
+> 导入数据
+```csharp
+ public class ImportDemoClass
+ {
+ private readonly IImporterProvider _importerProvider;
+
+ public ImportDemoClass(IImporterProvider importerProvider)
+ {
+ _importerProvider = importerProvider;
+ }
+
+ public async virtual Task ImportAsync(Stream stream)
+ {
+ var demos = await _importerProvider.ImportAsync(stream);
+
+ // 其他操作
+ }
+ }
+
+ public class DemoClass
+ {
+ public string Name { get; set; }
+ public string Remarks { get; set; }
+
+ public DemoClass()
+ {
+ }
+ }
+```
diff --git a/aspnet-core/framework/exporter/LINGYUN.Abp.Exporter.HttpApi/FodyWeavers.xml b/aspnet-core/framework/exporter/LINGYUN.Abp.Exporter.HttpApi/FodyWeavers.xml
new file mode 100644
index 000000000..1715698cc
--- /dev/null
+++ b/aspnet-core/framework/exporter/LINGYUN.Abp.Exporter.HttpApi/FodyWeavers.xml
@@ -0,0 +1,3 @@
+
+
+
\ No newline at end of file
diff --git a/aspnet-core/framework/exporter/LINGYUN.Abp.Exporter.HttpApi/LINGYUN.Abp.Exporter.HttpApi.csproj b/aspnet-core/framework/exporter/LINGYUN.Abp.Exporter.HttpApi/LINGYUN.Abp.Exporter.HttpApi.csproj
new file mode 100644
index 000000000..93bd013ba
--- /dev/null
+++ b/aspnet-core/framework/exporter/LINGYUN.Abp.Exporter.HttpApi/LINGYUN.Abp.Exporter.HttpApi.csproj
@@ -0,0 +1,24 @@
+
+
+
+
+
+
+ net8.0
+ LINGYUN.Abp.Exporter.HttpApi
+ LINGYUN.Abp.Exporter.HttpApi
+ false
+ false
+ false
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/aspnet-core/framework/exporter/LINGYUN.Abp.Exporter.HttpApi/LINGYUN/Abp/Exporter/AbpExporterHttpApiModule.cs b/aspnet-core/framework/exporter/LINGYUN.Abp.Exporter.HttpApi/LINGYUN/Abp/Exporter/AbpExporterHttpApiModule.cs
new file mode 100644
index 000000000..01313663a
--- /dev/null
+++ b/aspnet-core/framework/exporter/LINGYUN.Abp.Exporter.HttpApi/LINGYUN/Abp/Exporter/AbpExporterHttpApiModule.cs
@@ -0,0 +1,12 @@
+using Volo.Abp.Application;
+using Volo.Abp.Modularity;
+
+namespace LINGYUN.Abp.Exporter;
+
+[DependsOn(
+ typeof(AbpDddApplicationModule),
+ typeof(AbpExporterApplicationContractsModule))]
+public class AbpExporterHttpApiModule : AbpModule
+{
+
+}
diff --git a/aspnet-core/framework/exporter/LINGYUN.Abp.Exporter.HttpApi/LINGYUN/Abp/Exporter/ExporterController.cs b/aspnet-core/framework/exporter/LINGYUN.Abp.Exporter.HttpApi/LINGYUN/Abp/Exporter/ExporterController.cs
new file mode 100644
index 000000000..189363efa
--- /dev/null
+++ b/aspnet-core/framework/exporter/LINGYUN.Abp.Exporter.HttpApi/LINGYUN/Abp/Exporter/ExporterController.cs
@@ -0,0 +1,22 @@
+using Microsoft.AspNetCore.Mvc;
+using System.Threading.Tasks;
+using Volo.Abp.AspNetCore.Mvc;
+using Volo.Abp.Content;
+
+namespace LINGYUN.Abp.Exporter;
+public abstract class ExporterController : AbpControllerBase, IExporterAppService
+ where TEntityExportDto : class, new()
+{
+ private readonly IExporterAppService _exportService;
+ protected ExporterController(IExporterAppService exportService)
+ {
+ _exportService = exportService;
+ }
+
+ [HttpGet]
+ [Route("export")]
+ public virtual Task ExportAsync(TEntityListGetInput input)
+ {
+ return _exportService.ExportAsync(input);
+ }
+}
diff --git a/aspnet-core/framework/exporter/LINGYUN.Abp.Exporter.HttpApi/LINGYUN/Abp/Exporter/ImporterController.cs b/aspnet-core/framework/exporter/LINGYUN.Abp.Exporter.HttpApi/LINGYUN/Abp/Exporter/ImporterController.cs
new file mode 100644
index 000000000..42212ba56
--- /dev/null
+++ b/aspnet-core/framework/exporter/LINGYUN.Abp.Exporter.HttpApi/LINGYUN/Abp/Exporter/ImporterController.cs
@@ -0,0 +1,24 @@
+using Microsoft.AspNetCore.Mvc;
+using System.Threading.Tasks;
+using Volo.Abp.AspNetCore.Mvc;
+using Volo.Abp.Content;
+
+namespace LINGYUN.Abp.Exporter;
+
+public abstract class ImporterController : AbpControllerBase, IImporterAppService
+ where TEntityImportDto : class, new()
+{
+ private readonly IImporterAppService _importService;
+
+ protected ImporterController(IImporterAppService importService)
+ {
+ _importService = importService;
+ }
+
+ [HttpPost]
+ [Route("import")]
+ public virtual Task ImportAsync(IRemoteStreamContent input)
+ {
+ return _importService.ImportAsync(input);
+ }
+}
diff --git a/aspnet-core/framework/exporter/LINGYUN.Abp.Exporter.MagicodesIE.Excel/FodyWeavers.xml b/aspnet-core/framework/exporter/LINGYUN.Abp.Exporter.MagicodesIE.Excel/FodyWeavers.xml
new file mode 100644
index 000000000..1715698cc
--- /dev/null
+++ b/aspnet-core/framework/exporter/LINGYUN.Abp.Exporter.MagicodesIE.Excel/FodyWeavers.xml
@@ -0,0 +1,3 @@
+
+
+
\ No newline at end of file
diff --git a/aspnet-core/framework/exporter/LINGYUN.Abp.Exporter.MagicodesIE.Excel/LINGYUN.Abp.Exporter.MagicodesIE.Excel.csproj b/aspnet-core/framework/exporter/LINGYUN.Abp.Exporter.MagicodesIE.Excel/LINGYUN.Abp.Exporter.MagicodesIE.Excel.csproj
new file mode 100644
index 000000000..092ee9876
--- /dev/null
+++ b/aspnet-core/framework/exporter/LINGYUN.Abp.Exporter.MagicodesIE.Excel/LINGYUN.Abp.Exporter.MagicodesIE.Excel.csproj
@@ -0,0 +1,26 @@
+
+
+
+
+
+
+ netstandard2.0;netstandard2.1;net8.0
+ LINGYUN.Abp.Exporter.MagicodesIE.Excel
+ LINGYUN.Abp.Exporter.MagicodesIE.Excel
+ false
+ false
+ false
+ enable
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/aspnet-core/framework/exporter/LINGYUN.Abp.Exporter.MagicodesIE.Excel/LINGYUN/Abp/Exporter/AbpExporterMagicodesIEExcelModule.cs b/aspnet-core/framework/exporter/LINGYUN.Abp.Exporter.MagicodesIE.Excel/LINGYUN/Abp/Exporter/AbpExporterMagicodesIEExcelModule.cs
new file mode 100644
index 000000000..27c929fa8
--- /dev/null
+++ b/aspnet-core/framework/exporter/LINGYUN.Abp.Exporter.MagicodesIE.Excel/LINGYUN/Abp/Exporter/AbpExporterMagicodesIEExcelModule.cs
@@ -0,0 +1,30 @@
+using Magicodes.ExporterAndImporter.Core;
+using Magicodes.ExporterAndImporter.Excel;
+using Microsoft.Extensions.DependencyInjection;
+using Microsoft.Extensions.DependencyInjection.Extensions;
+using Volo.Abp.Modularity;
+
+namespace LINGYUN.Abp.Exporter.MiniExcel;
+
+[DependsOn(typeof(AbpExporterCoreModule))]
+public class AbpExporterMagicodesIEExcelModule : AbpModule
+{
+ public override void ConfigureServices(ServiceConfigurationContext context)
+ {
+ context.Services.AddScoped();
+
+ context.Services.AddScoped();
+
+ context.Services.AddScoped();
+
+ context.Services.Replace(
+ ServiceDescriptor.Transient(
+ typeof(IExporterProvider),
+ typeof(MagicodesIEExcelExporterProvider)));
+
+ context.Services.Replace(
+ ServiceDescriptor.Transient(
+ typeof(IImporterProvider),
+ typeof(MagicodesIEExcelImporterProvider)));
+ }
+}
\ No newline at end of file
diff --git a/aspnet-core/framework/exporter/LINGYUN.Abp.Exporter.MagicodesIE.Excel/LINGYUN/Abp/Exporter/AbpExporterMagicodesIEExcelOptions.cs b/aspnet-core/framework/exporter/LINGYUN.Abp.Exporter.MagicodesIE.Excel/LINGYUN/Abp/Exporter/AbpExporterMagicodesIEExcelOptions.cs
new file mode 100644
index 000000000..19857d592
--- /dev/null
+++ b/aspnet-core/framework/exporter/LINGYUN.Abp.Exporter.MagicodesIE.Excel/LINGYUN/Abp/Exporter/AbpExporterMagicodesIEExcelOptions.cs
@@ -0,0 +1,31 @@
+using Magicodes.ExporterAndImporter.Excel;
+using System;
+using System.Collections.Generic;
+
+namespace LINGYUN.Abp.Exporter;
+public class AbpExporterMagicodesIEExcelOptions
+{
+ ///
+ /// 数据导出设置
+ ///
+ public IDictionary> ExportSettingMapping { get; }
+ ///
+ /// 数据导入设置
+ ///
+ public IDictionary> ImportSettingMapping { get; }
+ public AbpExporterMagicodesIEExcelOptions()
+ {
+ ExportSettingMapping = new Dictionary>();
+ ImportSettingMapping = new Dictionary>();
+ }
+
+ public void MapExportSetting(Type dataType, Action exportSettingsSetup)
+ {
+ ExportSettingMapping[dataType] = exportSettingsSetup;
+ }
+
+ public void MapImportSetting(Type dataType, Action importSettingsSetup)
+ {
+ ImportSettingMapping[dataType] = importSettingsSetup;
+ }
+}
diff --git a/aspnet-core/framework/exporter/LINGYUN.Abp.Exporter.MagicodesIE.Excel/LINGYUN/Abp/Exporter/AbpImportHelper.cs b/aspnet-core/framework/exporter/LINGYUN.Abp.Exporter.MagicodesIE.Excel/LINGYUN/Abp/Exporter/AbpImportHelper.cs
new file mode 100644
index 000000000..a494896bd
--- /dev/null
+++ b/aspnet-core/framework/exporter/LINGYUN.Abp.Exporter.MagicodesIE.Excel/LINGYUN/Abp/Exporter/AbpImportHelper.cs
@@ -0,0 +1,23 @@
+using Magicodes.ExporterAndImporter.Excel;
+using Magicodes.ExporterAndImporter.Excel.Utility;
+using System;
+using System.IO;
+
+namespace LINGYUN.Abp.Exporter;
+public class AbpImportHelper : ImportHelper where T : class, new()
+{
+ public AbpImportHelper(string? filePath = null, string? labelingFilePath = null)
+ : base(filePath, labelingFilePath)
+ {
+ }
+
+ public AbpImportHelper(Stream stream, Stream? labelingFileStream)
+ : base(stream, labelingFileStream)
+ {
+ }
+
+ internal void ConfigureExcelImportSettings(Action? excelImportSettingsConfig)
+ {
+ excelImportSettingsConfig?.Invoke(ExcelImporterSettings);
+ }
+}
diff --git a/aspnet-core/framework/exporter/LINGYUN.Abp.Exporter.MagicodesIE.Excel/LINGYUN/Abp/Exporter/MagicodesIEExcelExporterProvider.cs b/aspnet-core/framework/exporter/LINGYUN.Abp.Exporter.MagicodesIE.Excel/LINGYUN/Abp/Exporter/MagicodesIEExcelExporterProvider.cs
new file mode 100644
index 000000000..6a5e96d48
--- /dev/null
+++ b/aspnet-core/framework/exporter/LINGYUN.Abp.Exporter.MagicodesIE.Excel/LINGYUN/Abp/Exporter/MagicodesIEExcelExporterProvider.cs
@@ -0,0 +1,62 @@
+using Magicodes.ExporterAndImporter.Excel;
+using Magicodes.ExporterAndImporter.Excel.Utility;
+using Microsoft.Extensions.Options;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using System.Threading;
+using System.Threading.Tasks;
+using Volo.Abp.DependencyInjection;
+
+namespace LINGYUN.Abp.Exporter;
+public class MagicodesIEExcelExporterProvider : IExporterProvider, ITransientDependency
+{
+ private readonly AbpExporterMagicodesIEExcelOptions _options;
+ private readonly IExcelExporter _excelExporter;
+
+ public MagicodesIEExcelExporterProvider(IOptions options)
+ {
+ _options = options.Value;
+ }
+
+ public async virtual Task ExportAsync(ICollection data, CancellationToken cancellationToken = default)
+ where T : class, new()
+ {
+ var fileBytes = new List();
+
+ var exportHelper = new ExportHelper();
+
+ // 由于Microsoft.IE.Excel官方此接口未暴露用户配置,做一次转换
+ if (_options.ExportSettingMapping.TryGetValue(typeof(T), out var exportSetting))
+ {
+ exportSetting?.Invoke(exportHelper.ExcelExporterSettings);
+ }
+
+ if (exportHelper.ExcelExporterSettings.MaxRowNumberOnASheet > 0 && data.Count > exportHelper.ExcelExporterSettings.MaxRowNumberOnASheet)
+ {
+ using (exportHelper.CurrentExcelPackage)
+ {
+ var num = data.Count / exportHelper.ExcelExporterSettings.MaxRowNumberOnASheet + ((data.Count % exportHelper.ExcelExporterSettings.MaxRowNumberOnASheet > 0) ? 1 : 0);
+ for (var i = 0; i < num; i++)
+ {
+ var dataItems2 = data.Skip(i * exportHelper.ExcelExporterSettings.MaxRowNumberOnASheet).Take(exportHelper.ExcelExporterSettings.MaxRowNumberOnASheet).ToList();
+ exportHelper.AddExcelWorksheet();
+ exportHelper.Export(dataItems2);
+ }
+
+ fileBytes.AddRange(await exportHelper.CurrentExcelPackage.GetAsByteArrayAsync());
+ }
+ }
+ else
+ {
+ using var excelPackage2 = exportHelper.Export(data);
+ fileBytes.AddRange(await excelPackage2.GetAsByteArrayAsync());
+ }
+
+ var memoryStream = new MemoryStream([.. fileBytes]);
+
+ memoryStream.Seek(0, SeekOrigin.Begin);
+
+ return memoryStream;
+ }
+}
diff --git a/aspnet-core/framework/exporter/LINGYUN.Abp.Exporter.MagicodesIE.Excel/LINGYUN/Abp/Exporter/MagicodesIEExcelImporterProvider.cs b/aspnet-core/framework/exporter/LINGYUN.Abp.Exporter.MagicodesIE.Excel/LINGYUN/Abp/Exporter/MagicodesIEExcelImporterProvider.cs
new file mode 100644
index 000000000..ed4e8a31b
--- /dev/null
+++ b/aspnet-core/framework/exporter/LINGYUN.Abp.Exporter.MagicodesIE.Excel/LINGYUN/Abp/Exporter/MagicodesIEExcelImporterProvider.cs
@@ -0,0 +1,74 @@
+using Magicodes.ExporterAndImporter.Core.Models;
+using Magicodes.ExporterAndImporter.Excel;
+using Microsoft.Extensions.Logging;
+using Microsoft.Extensions.Logging.Abstractions;
+using Microsoft.Extensions.Options;
+using System.Collections.Generic;
+using System.Collections.Immutable;
+using System.IO;
+using System.Threading.Tasks;
+using Volo.Abp;
+using Volo.Abp.DependencyInjection;
+using Volo.Abp.Json;
+
+namespace LINGYUN.Abp.Exporter;
+
+public class MagicodesIEExcelImporterProvider : IImporterProvider, ITransientDependency
+{
+ public ILogger Logger { protected get; set; }
+
+ private readonly AbpExporterMagicodesIEExcelOptions _options;
+ private readonly IJsonSerializer _jsonSerializer;
+ private readonly IExcelImporter _excelImporter;
+
+ public MagicodesIEExcelImporterProvider(
+ IJsonSerializer jsonSerializer,
+ IExcelImporter excelImporter,
+ IOptions options)
+ {
+ _jsonSerializer = jsonSerializer;
+ _excelImporter = excelImporter;
+ _options = options.Value;
+
+ Logger = NullLogger.Instance;
+ }
+
+ public async virtual Task> ImportAsync(Stream stream) where T : class, new()
+ {
+ using var importHelper = new AbpImportHelper(stream, null);
+
+ // 由于Microsoft.IE.Excel官方此接口未暴露用户配置,做一次转换
+ if (_options.ImportSettingMapping.TryGetValue(typeof(T), out var importSetting))
+ {
+ importHelper.ConfigureExcelImportSettings(importSetting);
+ }
+
+ var importResult = await importHelper.Import(null, null);
+
+ ThrowImportException(importResult);
+
+ return importResult.Data.ToImmutableList();
+ }
+
+ protected virtual void ThrowImportException(ImportResult importResult) where T : class, new()
+ {
+ if (importResult.HasError)
+ {
+ if (importResult.RowErrors.Count > 0)
+ {
+ var errorJson = _jsonSerializer.Serialize(importResult.RowErrors);
+ Logger.LogWarning("Row validation error occurred during data import process, details: {errorJson}", errorJson);
+ }
+
+ if (importResult.TemplateErrors.Count > 0)
+ {
+ var errorJson = _jsonSerializer.Serialize(importResult.TemplateErrors);
+ Logger.LogWarning("Template validation error occurred during data import process, details: {errorJson}", errorJson);
+ }
+
+ throw new BusinessException(
+ code: ExporterErrorCodes.ImportDataError,
+ innerException: importResult.Exception);
+ }
+ }
+}
diff --git a/aspnet-core/framework/exporter/LINGYUN.Abp.Exporter.MagicodesIE.Excel/README.md b/aspnet-core/framework/exporter/LINGYUN.Abp.Exporter.MagicodesIE.Excel/README.md
new file mode 100644
index 000000000..ed9fc65f9
--- /dev/null
+++ b/aspnet-core/framework/exporter/LINGYUN.Abp.Exporter.MagicodesIE.Excel/README.md
@@ -0,0 +1,41 @@
+# LINGYUN.Abp.Exporter.MagicodesIE.Excel
+
+数据导出接口的 [Magicodes.IE.Excel](https://github.com/dotnetcore/Magicodes.IE) 实现
+
+Import and export general library, support Dto import and export, template export, fancy export and dynamic export, support Excel, Csv, Word, Pdf and Html.
+
+详情请参考 [Magicodes.IE.Excel 文档](https://github.com/dotnetcore/Magicodes.IE/blob/master/docs/2.%E5%9F%BA%E7%A1%80%E6%95%99%E7%A8%8B%E4%B9%8B%E5%AF%BC%E5%87%BAExcel.md)
+
+## 配置使用
+
+
+```csharp
+
+ [DependsOn(
+ typeof(AbpExporterMagicodesIEExcelModule)
+ )]
+ public class YouProjectModule : AbpModule
+ {
+ Configure(options =>
+ {
+ // 配置类型数据导出
+ options.MapExportSetting(typeof(DemoClass), config =>
+ {
+ // 表头位置从第二行开始
+ config.HeaderRowIndex = 2;
+ // 其他配置
+ });
+ });
+ }
+
+ public class DemoClass
+ {
+ public string Name { get; set; }
+ public string Remarks { get; set; }
+
+ public DemoClass()
+ {
+ }
+ }
+```
+
diff --git a/aspnet-core/framework/exporter/LINGYUN.Abp.Exporter.MiniExcel/FodyWeavers.xml b/aspnet-core/framework/exporter/LINGYUN.Abp.Exporter.MiniExcel/FodyWeavers.xml
new file mode 100644
index 000000000..1715698cc
--- /dev/null
+++ b/aspnet-core/framework/exporter/LINGYUN.Abp.Exporter.MiniExcel/FodyWeavers.xml
@@ -0,0 +1,3 @@
+
+
+
\ No newline at end of file
diff --git a/aspnet-core/framework/exporter/LINGYUN.Abp.Exporter.MiniExcel/FodyWeavers.xsd b/aspnet-core/framework/exporter/LINGYUN.Abp.Exporter.MiniExcel/FodyWeavers.xsd
new file mode 100644
index 000000000..3f3946e28
--- /dev/null
+++ b/aspnet-core/framework/exporter/LINGYUN.Abp.Exporter.MiniExcel/FodyWeavers.xsd
@@ -0,0 +1,30 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 'true' to run assembly verification (PEVerify) on the target assembly after all weavers have been executed.
+
+
+
+
+ A comma-separated list of error codes that can be safely ignored in assembly verification.
+
+
+
+
+ 'false' to turn off automatic generation of the XML Schema file.
+
+
+
+
+
\ No newline at end of file
diff --git a/aspnet-core/framework/exporter/LINGYUN.Abp.Exporter.MiniExcel/LINGYUN.Abp.Exporter.MiniExcel.csproj b/aspnet-core/framework/exporter/LINGYUN.Abp.Exporter.MiniExcel/LINGYUN.Abp.Exporter.MiniExcel.csproj
new file mode 100644
index 000000000..2efc4c367
--- /dev/null
+++ b/aspnet-core/framework/exporter/LINGYUN.Abp.Exporter.MiniExcel/LINGYUN.Abp.Exporter.MiniExcel.csproj
@@ -0,0 +1,25 @@
+
+
+
+
+
+
+ netstandard2.0;netstandard2.1;net8.0
+ LINGYUN.Abp.Exporter.MiniExcel
+ LINGYUN.Abp.Exporter.MiniExcel
+ false
+ false
+ false
+ enable
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/aspnet-core/framework/exporter/LINGYUN.Abp.Exporter.MiniExcel/LINGYUN/Abp/Exporter/AbpExporterMiniExcelModule.cs b/aspnet-core/framework/exporter/LINGYUN.Abp.Exporter.MiniExcel/LINGYUN/Abp/Exporter/AbpExporterMiniExcelModule.cs
new file mode 100644
index 000000000..000e26636
--- /dev/null
+++ b/aspnet-core/framework/exporter/LINGYUN.Abp.Exporter.MiniExcel/LINGYUN/Abp/Exporter/AbpExporterMiniExcelModule.cs
@@ -0,0 +1,22 @@
+using Microsoft.Extensions.DependencyInjection;
+using Microsoft.Extensions.DependencyInjection.Extensions;
+using Volo.Abp.Modularity;
+
+namespace LINGYUN.Abp.Exporter.MiniExcel;
+
+[DependsOn(typeof(AbpExporterCoreModule))]
+public class AbpExporterMiniExcelModule : AbpModule
+{
+ public override void ConfigureServices(ServiceConfigurationContext context)
+ {
+ context.Services.Replace(
+ ServiceDescriptor.Transient(
+ typeof(IExporterProvider),
+ typeof(MiniExcelExporterProvider)));
+
+ context.Services.Replace(
+ ServiceDescriptor.Transient(
+ typeof(IImporterProvider),
+ typeof(MiniExcelImporterProvider)));
+ }
+}
\ No newline at end of file
diff --git a/aspnet-core/framework/exporter/LINGYUN.Abp.Exporter.MiniExcel/LINGYUN/Abp/Exporter/AbpExporterMiniExcelOptions.cs b/aspnet-core/framework/exporter/LINGYUN.Abp.Exporter.MiniExcel/LINGYUN/Abp/Exporter/AbpExporterMiniExcelOptions.cs
new file mode 100644
index 000000000..b51cddc8e
--- /dev/null
+++ b/aspnet-core/framework/exporter/LINGYUN.Abp.Exporter.MiniExcel/LINGYUN/Abp/Exporter/AbpExporterMiniExcelOptions.cs
@@ -0,0 +1,25 @@
+using MiniExcelLibs.OpenXml;
+using System;
+using System.Collections.Generic;
+
+namespace LINGYUN.Abp.Exporter;
+public class AbpExporterMiniExcelOptions
+{
+ public IDictionary> ExportSettingMapping { get; }
+ public IDictionary> ImportSettingMapping { get; }
+ public AbpExporterMiniExcelOptions()
+ {
+ ExportSettingMapping = new Dictionary>();
+ ImportSettingMapping = new Dictionary>();
+ }
+
+ public void MapExportSetting(Type dataType, Action settingMapping)
+ {
+ ExportSettingMapping[dataType] = settingMapping;
+ }
+
+ public void MapImportSetting(Type dataType, Action settingMapping)
+ {
+ ImportSettingMapping[dataType] = settingMapping;
+ }
+}
diff --git a/aspnet-core/framework/exporter/LINGYUN.Abp.Exporter.MiniExcel/LINGYUN/Abp/Exporter/MiniExcelExporterProvider.cs b/aspnet-core/framework/exporter/LINGYUN.Abp.Exporter.MiniExcel/LINGYUN/Abp/Exporter/MiniExcelExporterProvider.cs
new file mode 100644
index 000000000..258f333a1
--- /dev/null
+++ b/aspnet-core/framework/exporter/LINGYUN.Abp.Exporter.MiniExcel/LINGYUN/Abp/Exporter/MiniExcelExporterProvider.cs
@@ -0,0 +1,41 @@
+using Microsoft.Extensions.Options;
+using MiniExcelLibs;
+using MiniExcelLibs.OpenXml;
+using System.Collections.Generic;
+using System.IO;
+using System.Threading;
+using System.Threading.Tasks;
+using Volo.Abp.DependencyInjection;
+
+namespace LINGYUN.Abp.Exporter;
+public class MiniExcelExporterProvider : IExporterProvider, ITransientDependency
+{
+ private readonly AbpExporterMiniExcelOptions _options;
+
+ public MiniExcelExporterProvider(IOptions options)
+ {
+ _options = options.Value;
+ }
+
+ public async virtual Task ExportAsync(ICollection data, CancellationToken cancellationToken = default)
+ where T : class, new()
+ {
+ var memoryStream = new MemoryStream();
+ var xmlConfig = new OpenXmlConfiguration();
+
+ if (_options.ExportSettingMapping.TryGetValue(typeof(T), out var exportSetting))
+ {
+ exportSetting?.Invoke(xmlConfig);
+ }
+
+ await memoryStream.SaveAsAsync(
+ value: data,
+ excelType: ExcelType.XLSX,
+ configuration: xmlConfig,
+ cancellationToken: cancellationToken);
+
+ memoryStream.Seek(0, SeekOrigin.Begin);
+
+ return memoryStream;
+ }
+}
diff --git a/aspnet-core/framework/exporter/LINGYUN.Abp.Exporter.MiniExcel/LINGYUN/Abp/Exporter/MiniExcelImporterProvider.cs b/aspnet-core/framework/exporter/LINGYUN.Abp.Exporter.MiniExcel/LINGYUN/Abp/Exporter/MiniExcelImporterProvider.cs
new file mode 100644
index 000000000..3a0d7b3d4
--- /dev/null
+++ b/aspnet-core/framework/exporter/LINGYUN.Abp.Exporter.MiniExcel/LINGYUN/Abp/Exporter/MiniExcelImporterProvider.cs
@@ -0,0 +1,35 @@
+using Microsoft.Extensions.Options;
+using MiniExcelLibs;
+using MiniExcelLibs.OpenXml;
+using System.Collections.Generic;
+using System.Collections.Immutable;
+using System.IO;
+using System.Threading.Tasks;
+using Volo.Abp.DependencyInjection;
+
+namespace LINGYUN.Abp.Exporter;
+public class MiniExcelImporterProvider : IImporterProvider, ITransientDependency
+{
+ private readonly AbpExporterMiniExcelOptions _options;
+
+ public MiniExcelImporterProvider(IOptions options)
+ {
+ _options = options.Value;
+ }
+
+ public async virtual Task> ImportAsync(Stream stream) where T : class, new()
+ {
+ var xmlConfig = new OpenXmlConfiguration();
+
+ if (_options.ExportSettingMapping.TryGetValue(typeof(T), out var exportSetting))
+ {
+ exportSetting?.Invoke(xmlConfig);
+ }
+
+ var dataList = await stream.QueryAsync(
+ excelType: ExcelType.XLSX,
+ configuration: xmlConfig);
+
+ return dataList.ToImmutableList();
+ }
+}
diff --git a/aspnet-core/framework/exporter/LINGYUN.Abp.Exporter.MiniExcel/README.md b/aspnet-core/framework/exporter/LINGYUN.Abp.Exporter.MiniExcel/README.md
new file mode 100644
index 000000000..da5ceb973
--- /dev/null
+++ b/aspnet-core/framework/exporter/LINGYUN.Abp.Exporter.MiniExcel/README.md
@@ -0,0 +1,46 @@
+# LINGYUN.Abp.Exporter.MiniExcel
+
+数据导出接口的 [MiniExcel](https://github.com/mini-software/MiniExcel) 实现
+
+MiniExcel简单、高效避免OOM的.NET处理Excel查、写、填充数据工具。
+
+目前主流框架大多需要将数据全载入到内存方便操作,但这会导致内存消耗问题,MiniExcel 尝试以 Stream 角度写底层算法逻辑,能让原本1000多MB占用降低到几MB,避免内存不够情况。
+
+详情请参考 [MiniExcel 文档](https://github.com/mini-software/MiniExcel/blob/master/README.md)
+
+## 配置使用
+
+
+```csharp
+
+ [DependsOn(
+ typeof(AbpExporterMiniExcelModule)
+ )]
+ public class YouProjectModule : AbpModule
+ {
+ Configure(options =>
+ {
+ // 配置类型数据导出
+ options.MapExportSetting(typeof(DemoClass), config =>
+ {
+ config.DynamicColumns = new[]
+ {
+ // 忽略Name字段
+ new DynamicExcelColumn(nameof(DemoClass.Name)){ Ignore = true },
+ // 其他配置
+ };
+ });
+ });
+ }
+
+ public class DemoClass
+ {
+ public string Name { get; set; }
+ public string Remarks { get; set; }
+
+ public DemoClass()
+ {
+ }
+ }
+```
+