diff --git a/Admin.NET/Admin.NET.Core/Admin.NET.Core.csproj b/Admin.NET/Admin.NET.Core/Admin.NET.Core.csproj
index d8173c61..1e963046 100644
--- a/Admin.NET/Admin.NET.Core/Admin.NET.Core.csproj
+++ b/Admin.NET/Admin.NET.Core/Admin.NET.Core.csproj
@@ -40,12 +40,12 @@
-
-
+
+
-
+
diff --git a/Admin.NET/Admin.NET.Core/Enum/ErrorCodeEnum.cs b/Admin.NET/Admin.NET.Core/Enum/ErrorCodeEnum.cs
index 28d8a6f8..e004ba39 100644
--- a/Admin.NET/Admin.NET.Core/Enum/ErrorCodeEnum.cs
+++ b/Admin.NET/Admin.NET.Core/Enum/ErrorCodeEnum.cs
@@ -110,7 +110,7 @@ public enum ErrorCodeEnum
D1012,
///
- /// 所属机构不在自己的数据范围内
+ /// 没有权限操作该数据
///
[ErrorCodeItemMetadata("没有权限操作该数据")]
D1013,
diff --git a/Admin.NET/Admin.NET.Core/Enum/SysUserEventTypeEnum.cs b/Admin.NET/Admin.NET.Core/Enum/SysUserEventTypeEnum.cs
new file mode 100644
index 00000000..04cd8e78
--- /dev/null
+++ b/Admin.NET/Admin.NET.Core/Enum/SysUserEventTypeEnum.cs
@@ -0,0 +1,62 @@
+// Admin.NET 项目的版权、商标、专利和其他相关权利均受相应法律法规的保护。使用本项目应遵守相关法律法规和许可证的要求。
+//
+// 本项目主要遵循 MIT 许可证和 Apache 许可证(版本 2.0)进行分发和使用。许可证位于源代码树根目录中的 LICENSE-MIT 和 LICENSE-APACHE 文件。
+//
+// 不得利用本项目从事危害国家安全、扰乱社会秩序、侵犯他人合法权益等法律法规禁止的活动!任何基于本项目二次开发而产生的一切法律纠纷和责任,我们不承担任何责任!
+
+namespace Admin.NET.Core;
+
+///
+/// 事件类型-系统用户操作枚举
+///
+[Description("事件类型-系统用户操作枚举")]
+public enum SysUserEventTypeEnum
+{
+ ///
+ /// 增加用户
+ ///
+ [Description("增加用户")]
+ Add = 111,
+
+ ///
+ /// 更新用户
+ ///
+ [Description("更新用户")]
+ Update = 222,
+
+ ///
+ /// 授权用户角色
+ ///
+ [Description("授权用户角色")]
+ UpdateRole = 333,
+
+ ///
+ /// 删除用户
+ ///
+ [Description("删除用户")]
+ Delete = 444,
+
+ ///
+ /// 设置用户状态
+ ///
+ [Description("设置用户状态")]
+ SetStatus = 555,
+
+ ///
+ /// 修改密码
+ ///
+ [Description("修改密码")]
+ ChangePwd = 666,
+
+ ///
+ /// 重置密码
+ ///
+ [Description("重置密码")]
+ ResetPwd = 777,
+
+ ///
+ /// 解除登录锁定
+ ///
+ [Description("解除登录锁定")]
+ UnlockLogin = 888
+}
\ No newline at end of file
diff --git a/Admin.NET/Admin.NET.Core/Service/DataBase/SysDatabaseService.cs b/Admin.NET/Admin.NET.Core/Service/DataBase/SysDatabaseService.cs
index a9cf2a86..c283b841 100644
--- a/Admin.NET/Admin.NET.Core/Service/DataBase/SysDatabaseService.cs
+++ b/Admin.NET/Admin.NET.Core/Service/DataBase/SysDatabaseService.cs
@@ -49,6 +49,7 @@ public class SysDatabaseService : IDynamicApiController, ITransient
var visualTableList = new List();
var visualColumnList = new List();
var columnRelationList = new List();
+ var dbOptions = App.GetOptions().ConnectionConfigs.First(u => u.ConfigId.ToString() == SqlSugarConst.MainConfigId);
// 遍历所有实体获取所有库表结构
var random = new Random();
@@ -71,7 +72,7 @@ public class SysDatabaseService : IDynamicApiController, ITransient
var visualColumn = new VisualColumn
{
TableName = columnInfo.DbTableName,
- ColumnName = columnInfo.DbColumnName,
+ ColumnName = dbOptions.DbSettings.EnableUnderLine ? UtilMethods.ToUnderLine(columnInfo.DbColumnName) : columnInfo.DbColumnName,
DataType = columnInfo.PropertyInfo.PropertyType.Name,
DataLength = columnInfo.Length.ToString(),
ColumnDescription = columnInfo.ColumnDescription,
@@ -83,13 +84,14 @@ public class SysDatabaseService : IDynamicApiController, ITransient
{
var name1 = columnInfo.Navigat.GetName();
var name2 = columnInfo.Navigat.GetName2();
+ var targetColumnName = string.IsNullOrEmpty(name2) ? "Id" : name2;
var relation = new ColumnRelation
{
SourceTableName = columnInfo.DbTableName,
- SourceColumnName = name1,
+ SourceColumnName = dbOptions.DbSettings.EnableUnderLine ? UtilMethods.ToUnderLine(name1) : name1,
Type = columnInfo.Navigat.GetNavigateType() == NavigateType.OneToOne ? "ONE_TO_ONE" : "ONE_TO_MANY",
- TargetTableName = columnInfo.DbColumnName,
- TargetColumnName = string.IsNullOrEmpty(name2) ? "Id" : name2
+ TargetTableName = dbOptions.DbSettings.EnableUnderLine ? UtilMethods.ToUnderLine(columnInfo.DbColumnName) : columnInfo.DbColumnName,
+ TargetColumnName = dbOptions.DbSettings.EnableUnderLine ? UtilMethods.ToUnderLine(targetColumnName) : targetColumnName
};
columnRelationList.Add(relation);
}
diff --git a/Admin.NET/Admin.NET.Core/Service/DataBase/SysDbBackupService.cs b/Admin.NET/Admin.NET.Core/Service/DataBase/SysDbBackupService.cs
index 028dc68b..587b321a 100644
--- a/Admin.NET/Admin.NET.Core/Service/DataBase/SysDbBackupService.cs
+++ b/Admin.NET/Admin.NET.Core/Service/DataBase/SysDbBackupService.cs
@@ -30,6 +30,8 @@ public class SysDbBackupService : IDynamicApiController, ITransient
{
try
{
+ if (!Directory.Exists(backupDir))
+ Directory.CreateDirectory(backupDir);
var fileList = Directory.GetFiles(backupDir);
var dbBackupList = new List();
diff --git a/Admin.NET/Admin.NET.Core/Service/Dict/Dto/DictDataInput.cs b/Admin.NET/Admin.NET.Core/Service/Dict/Dto/DictDataInput.cs
index b9a1b183..fa757d05 100644
--- a/Admin.NET/Admin.NET.Core/Service/Dict/Dto/DictDataInput.cs
+++ b/Admin.NET/Admin.NET.Core/Service/Dict/Dto/DictDataInput.cs
@@ -6,13 +6,8 @@
namespace Admin.NET.Core.Service;
-public class DictDataInput : BaseIdInput
+public class DictDataInput : BaseStatusInput
{
- ///
- /// 状态
- ///
- [Dict("StatusEnum")]
- public StatusEnum Status { get; set; }
}
public class PageDictDataInput : BasePageInput
diff --git a/Admin.NET/Admin.NET.Core/Service/Dict/Dto/DictTypeInput.cs b/Admin.NET/Admin.NET.Core/Service/Dict/Dto/DictTypeInput.cs
index fc6327e7..f311da17 100644
--- a/Admin.NET/Admin.NET.Core/Service/Dict/Dto/DictTypeInput.cs
+++ b/Admin.NET/Admin.NET.Core/Service/Dict/Dto/DictTypeInput.cs
@@ -6,13 +6,8 @@
namespace Admin.NET.Core.Service;
-public class DictTypeInput : BaseIdInput
+public class DictTypeInput : BaseStatusInput
{
- ///
- /// 状态
- ///
- [Dict("StatusEnum")]
- public StatusEnum Status { get; set; }
}
public class PageDictTypeInput : BasePageInput
diff --git a/Admin.NET/Admin.NET.Core/Service/Notice/SysNoticeService.cs b/Admin.NET/Admin.NET.Core/Service/Notice/SysNoticeService.cs
index 139bff29..b4379d10 100644
--- a/Admin.NET/Admin.NET.Core/Service/Notice/SysNoticeService.cs
+++ b/Admin.NET/Admin.NET.Core/Service/Notice/SysNoticeService.cs
@@ -72,6 +72,9 @@ public class SysNoticeService : IDynamicApiController, ITransient
[DisplayName("更新通知公告")]
public async Task UpdateNotice(UpdateNoticeInput input)
{
+ if (input.CreateUserId != _userManager.UserId)
+ throw Oops.Oh(ErrorCodeEnum.D7003);
+
var notice = input.Adapt();
InitNoticeInfo(notice);
await _sysNoticeRep.UpdateAsync(notice);
@@ -87,6 +90,12 @@ public class SysNoticeService : IDynamicApiController, ITransient
[DisplayName("删除通知公告")]
public async Task DeleteNotice(DeleteNoticeInput input)
{
+ var sysNotice = await _sysNoticeRep.GetByIdAsync(input.Id);
+ if (sysNotice.CreateUserId != _userManager.UserId)
+ throw Oops.Oh(ErrorCodeEnum.D7003);
+ if (sysNotice.Status == NoticeStatusEnum.PUBLIC)
+ throw Oops.Oh(ErrorCodeEnum.D7001);
+
await _sysNoticeRep.DeleteAsync(u => u.Id == input.Id);
await _sysNoticeUserRep.DeleteAsync(u => u.NoticeId == input.Id);
@@ -100,6 +109,9 @@ public class SysNoticeService : IDynamicApiController, ITransient
[DisplayName("发布通知公告")]
public async Task Public(NoticeInput input)
{
+ if (!(await _sysNoticeRep.IsAnyAsync(u => u.Id == input.Id && u.CreateUserId == _userManager.UserId)))
+ throw Oops.Oh(ErrorCodeEnum.D7003);
+
// 更新发布状态和时间
await _sysNoticeRep.UpdateAsync(u => new SysNotice() { Status = NoticeStatusEnum.PUBLIC, PublicTime = DateTime.Now }, u => u.Id == input.Id);
diff --git a/Admin.NET/Admin.NET.Core/Service/User/Dto/SysUserEventArgs.cs b/Admin.NET/Admin.NET.Core/Service/User/Dto/SysUserEventArgs.cs
new file mode 100644
index 00000000..35c4c0f5
--- /dev/null
+++ b/Admin.NET/Admin.NET.Core/Service/User/Dto/SysUserEventArgs.cs
@@ -0,0 +1,30 @@
+// Admin.NET 项目的版权、商标、专利和其他相关权利均受相应法律法规的保护。使用本项目应遵守相关法律法规和许可证的要求。
+//
+// 本项目主要遵循 MIT 许可证和 Apache 许可证(版本 2.0)进行分发和使用。许可证位于源代码树根目录中的 LICENSE-MIT 和 LICENSE-APACHE 文件。
+//
+// 不得利用本项目从事危害国家安全、扰乱社会秩序、侵犯他人合法权益等法律法规禁止的活动!任何基于本项目二次开发而产生的一切法律纠纷和责任,我们不承担任何责任!
+
+namespace Admin.NET.Core.Service;
+
+///
+/// 系统用户操作事件参数
+///
+public class SysUserEventArgs : EventArgs
+{
+ ///
+ /// 事件类型
+ ///
+ [Required]
+ public SysUserEventTypeEnum EventType { get; set; }
+
+ ///
+ /// 接口输入参数
+ ///
+ public object Input { get; set; }
+
+ public SysUserEventArgs(SysUserEventTypeEnum eventType, object input)
+ {
+ this.EventType = eventType;
+ this.Input = input;
+ }
+}
\ No newline at end of file
diff --git a/Admin.NET/Admin.NET.Core/Service/User/Dto/UserInput.cs b/Admin.NET/Admin.NET.Core/Service/User/Dto/UserInput.cs
index 8d05eacf..834a013b 100644
--- a/Admin.NET/Admin.NET.Core/Service/User/Dto/UserInput.cs
+++ b/Admin.NET/Admin.NET.Core/Service/User/Dto/UserInput.cs
@@ -9,13 +9,8 @@ namespace Admin.NET.Core.Service;
///
/// 设置用户状态输入参数
///
-public class UserInput : BaseIdInput
+public class UserInput : BaseStatusInput
{
- ///
- /// 状态
- ///
- [Dict("StatusEnum")]
- public StatusEnum Status { get; set; }
}
///
diff --git a/Admin.NET/Admin.NET.Core/Service/User/SysUserEventHandler.cs b/Admin.NET/Admin.NET.Core/Service/User/SysUserEventHandler.cs
new file mode 100644
index 00000000..7544ad21
--- /dev/null
+++ b/Admin.NET/Admin.NET.Core/Service/User/SysUserEventHandler.cs
@@ -0,0 +1,32 @@
+// Admin.NET 项目的版权、商标、专利和其他相关权利均受相应法律法规的保护。使用本项目应遵守相关法律法规和许可证的要求。
+//
+// 本项目主要遵循 MIT 许可证和 Apache 许可证(版本 2.0)进行分发和使用。许可证位于源代码树根目录中的 LICENSE-MIT 和 LICENSE-APACHE 文件。
+//
+// 不得利用本项目从事危害国家安全、扰乱社会秩序、侵犯他人合法权益等法律法规禁止的活动!任何基于本项目二次开发而产生的一切法律纠纷和责任,我们不承担任何责任!
+
+namespace Admin.NET.Core.Service;
+
+///
+/// 系统用户事件处理类
+///
+public class SysUserEventHandler : ISingleton
+{
+ private event EventHandler Event;
+
+ ///
+ /// 订阅
+ ///
+ ///
+ public void Subscribe(EventHandler eventHandler) => Event += eventHandler;
+
+ ///
+ /// 发布事件
+ ///
+ ///
+ ///
+ ///
+ public void OnEvent(object sender, SysUserEventTypeEnum eventType, object input)
+ {
+ Event?.Invoke(sender, new SysUserEventArgs(eventType, input));
+ }
+}
\ No newline at end of file
diff --git a/Admin.NET/Admin.NET.Core/Service/User/SysUserService.cs b/Admin.NET/Admin.NET.Core/Service/User/SysUserService.cs
index dd25948e..90bd6fce 100644
--- a/Admin.NET/Admin.NET.Core/Service/User/SysUserService.cs
+++ b/Admin.NET/Admin.NET.Core/Service/User/SysUserService.cs
@@ -22,6 +22,7 @@ public class SysUserService : IDynamicApiController, ITransient
private readonly SysCacheService _sysCacheService;
private readonly SysUserLdapService _sysUserLdapService;
private readonly SqlSugarRepository _sysUserRep;
+ private readonly SysUserEventHandler _sysUserEventHandler;
public SysUserService(UserManager userManager,
SysOrgService sysOrgService,
@@ -32,7 +33,8 @@ public class SysUserService : IDynamicApiController, ITransient
SysOnlineUserService sysOnlineUserService,
SysCacheService sysCacheService,
SysUserLdapService sysUserLdapService,
- SqlSugarRepository sysUserRep)
+ SqlSugarRepository sysUserRep,
+ SysUserEventHandler sysUserEventHandler)
{
_userManager = userManager;
_sysOrgService = sysOrgService;
@@ -44,6 +46,7 @@ public class SysUserService : IDynamicApiController, ITransient
_sysCacheService = sysCacheService;
_sysUserLdapService = sysUserLdapService;
_sysUserRep = sysUserRep;
+ _sysUserEventHandler = sysUserEventHandler;
}
///
@@ -113,6 +116,9 @@ public class SysUserService : IDynamicApiController, ITransient
if (!string.IsNullOrWhiteSpace(input.DomainAccount))
await _sysUserLdapService.AddUserLdap(newUser.TenantId.Value, newUser.Id, newUser.Account, input.DomainAccount);
+ // 执行订阅事件
+ _sysUserEventHandler.OnEvent(this, SysUserEventTypeEnum.Add, input);
+
return newUser.Id;
}
@@ -144,6 +150,9 @@ public class SysUserService : IDynamicApiController, ITransient
await _sysOnlineUserService.ForceOffline(input.Id);
// 更新域账号
await _sysUserLdapService.AddUserLdap(user.TenantId.Value, user.Id, user.Account, input.DomainAccount);
+
+ // 执行订阅事件
+ _sysUserEventHandler.OnEvent(this, SysUserEventTypeEnum.Update, input);
}
///
@@ -195,6 +204,9 @@ public class SysUserService : IDynamicApiController, ITransient
// 删除域账号
await _sysUserLdapService.DeleteUserLdapByUserId(input.Id);
+
+ // 执行订阅事件
+ _sysUserEventHandler.OnEvent(this, SysUserEventTypeEnum.Delete, input);
}
///
@@ -241,7 +253,12 @@ public class SysUserService : IDynamicApiController, ITransient
await SetUserBalckList(user, input.Status);
user.Status = input.Status;
- return await _sysUserRep.AsUpdateable(user).UpdateColumns(u => new { u.Status }).ExecuteCommandAsync();
+ var rows = await _sysUserRep.AsUpdateable(user).UpdateColumns(u => new { u.Status }).ExecuteCommandAsync();
+
+ // 执行订阅事件
+ if (rows > 0) _sysUserEventHandler.OnEvent(this, SysUserEventTypeEnum.SetStatus, input);
+
+ return rows;
}
///
@@ -280,6 +297,9 @@ public class SysUserService : IDynamicApiController, ITransient
// throw Oops.Oh(ErrorCodeEnum.D1022);
await _sysUserRoleService.GrantUserRole(input);
+
+ // 执行订阅事件
+ _sysUserEventHandler.OnEvent(this, SysUserEventTypeEnum.UpdateRole, input);
}
///
@@ -322,7 +342,12 @@ public class SysUserService : IDynamicApiController, ITransient
}
user.LastChangePasswordTime = DateTime.Now;
- return await _sysUserRep.AsUpdateable(user).UpdateColumns(u => new { u.Password, u.LastChangePasswordTime }).ExecuteCommandAsync();
+ var rows = await _sysUserRep.AsUpdateable(user).UpdateColumns(u => new { u.Password, u.LastChangePasswordTime }).ExecuteCommandAsync();
+
+ // 执行订阅事件
+ if (rows > 0) _sysUserEventHandler.OnEvent(this, SysUserEventTypeEnum.ChangePwd, input);
+
+ return rows;
}
///
@@ -343,6 +368,9 @@ public class SysUserService : IDynamicApiController, ITransient
var keyPasswordErrorTimes = $"{CacheConst.KeyPasswordErrorTimes}{user.Account}";
_sysCacheService.Remove(keyPasswordErrorTimes);
+ // 执行订阅事件
+ _sysUserEventHandler.OnEvent(this, SysUserEventTypeEnum.ResetPwd, input);
+
return password;
}
@@ -359,6 +387,9 @@ public class SysUserService : IDynamicApiController, ITransient
// 清空密码错误次数
var keyPasswordErrorTimes = $"{CacheConst.KeyPasswordErrorTimes}{user.Account}";
_sysCacheService.Remove(keyPasswordErrorTimes);
+
+ // 执行订阅事件
+ _sysUserEventHandler.OnEvent(this, SysUserEventTypeEnum.UnlockLogin, input);
}
///
diff --git a/Admin.NET/Admin.NET.Core/Service/Wechat/SysWechatPayService.cs b/Admin.NET/Admin.NET.Core/Service/Wechat/SysWechatPayService.cs
index 7ccfd4ea..23bd09d9 100644
--- a/Admin.NET/Admin.NET.Core/Service/Wechat/SysWechatPayService.cs
+++ b/Admin.NET/Admin.NET.Core/Service/Wechat/SysWechatPayService.cs
@@ -368,7 +368,7 @@ public class SysWechatPayService : IDynamicApiController, ITransient
{
TransactionId = input.OutTradeNumber,
OutTradeNumber = request.OutTradeNumber,
- OutRefundNo = request.OutTradeNumber, //每笔付款只退一次,所以这里直接用付款单号
+ OutRefundNo = request.OutTradeNumber, // 每笔付款只退一次,所以这里直接用付款单号
Reason = request.Reason,
Refund = input.Refund,
Total = input.Total,
@@ -422,7 +422,7 @@ public class SysWechatPayService : IDynamicApiController, ITransient
{
MerchantId = _wechatPayOptions.MerchantId,
TransactionId = transactionId,
- WechatpayCertificateSerialNumber = _wechatPayOptions.MerchantCertificateSerialNumber
+ WechatpaySerialNumber = _wechatPayOptions.MerchantCertificateSerialNumber
};
var response = await _wechatTenpayClient.ExecuteGetPayTransactionByIdAsync(request);
if (response.TradeState == "SUCCESS" || response.TradeState == "CLOSED")
@@ -463,7 +463,7 @@ public class SysWechatPayService : IDynamicApiController, ITransient
{
MerchantId = _wechatPayOptions.MerchantId,
OutTradeNumber = outTradeNumber,
- WechatpayCertificateSerialNumber = _wechatPayOptions.MerchantCertificateSerialNumber
+ WechatpaySerialNumber = _wechatPayOptions.MerchantCertificateSerialNumber,
};
var response = await _wechatTenpayClient.ExecuteGetPayTransactionByOutTradeNumberAsync(request);
if (response.TradeState == "SUCCESS" || response.TradeState == "CLOSED")
diff --git a/Admin.NET/Admin.NET.Core/Utils/BaseImportInput.cs b/Admin.NET/Admin.NET.Core/Utils/BaseImportInput.cs
new file mode 100644
index 00000000..dbd6ad00
--- /dev/null
+++ b/Admin.NET/Admin.NET.Core/Utils/BaseImportInput.cs
@@ -0,0 +1,27 @@
+// Admin.NET 项目的版权、商标、专利和其他相关权利均受相应法律法规的保护。使用本项目应遵守相关法律法规和许可证的要求。
+//
+// 本项目主要遵循 MIT 许可证和 Apache 许可证(版本 2.0)进行分发和使用。许可证位于源代码树根目录中的 LICENSE-MIT 和 LICENSE-APACHE 文件。
+//
+// 不得利用本项目从事危害国家安全、扰乱社会秩序、侵犯他人合法权益等法律法规禁止的活动!任何基于本项目二次开发而产生的一切法律纠纷和责任,我们不承担任何责任!
+
+namespace Admin.NET.Core;
+
+///
+/// 数据导入输入参数
+///
+public class BaseImportInput
+{
+ ///
+ /// 记录Id
+ ///
+ [ImporterHeader(IsIgnore = true)]
+ [ExporterHeader(IsIgnore = true)]
+ public virtual long Id { get; set; }
+
+ ///
+ /// 错误信息
+ ///
+ [ImporterHeader(IsIgnore = true)]
+ [ExporterHeader("错误信息", ColumnIndex = 9999, IsBold = true, IsAutoFit = true)]
+ public virtual string Error { get; set; }
+}
\ No newline at end of file
diff --git a/Admin.NET/Admin.NET.Core/Utils/BaseStatusInput.cs b/Admin.NET/Admin.NET.Core/Utils/BaseStatusInput.cs
new file mode 100644
index 00000000..00ca9fcc
--- /dev/null
+++ b/Admin.NET/Admin.NET.Core/Utils/BaseStatusInput.cs
@@ -0,0 +1,19 @@
+// Admin.NET 项目的版权、商标、专利和其他相关权利均受相应法律法规的保护。使用本项目应遵守相关法律法规和许可证的要求。
+//
+// 本项目主要遵循 MIT 许可证和 Apache 许可证(版本 2.0)进行分发和使用。许可证位于源代码树根目录中的 LICENSE-MIT 和 LICENSE-APACHE 文件。
+//
+// 不得利用本项目从事危害国家安全、扰乱社会秩序、侵犯他人合法权益等法律法规禁止的活动!任何基于本项目二次开发而产生的一切法律纠纷和责任,我们不承担任何责任!
+
+namespace Admin.NET.Core;
+
+///
+/// 设置状态输入参数
+///
+public class BaseStatusInput : BaseIdInput
+{
+ ///
+ /// 状态
+ ///
+ [Dict(nameof(StatusEnum))]
+ public StatusEnum Status { get; set; }
+}
\ No newline at end of file
diff --git a/Admin.NET/Admin.NET.Core/Utils/CommonUtil.cs b/Admin.NET/Admin.NET.Core/Utils/CommonUtil.cs
index ab0910df..470c579f 100644
--- a/Admin.NET/Admin.NET.Core/Utils/CommonUtil.cs
+++ b/Admin.NET/Admin.NET.Core/Utils/CommonUtil.cs
@@ -248,6 +248,34 @@ public static class CommonUtil
return res.Data;
}
+ ///
+ /// 导入数据Excel
+ ///
+ ///
+ ///
+ ///
+ public static async Task> ImportExcelDataAsync([Required] IFormFile file) where T : class, new()
+ {
+ var sysFileService = App.GetRequiredService();
+ var newFile = await sysFileService.UploadFile(new UploadFileInput { File = file });
+ var filePath = Path.Combine(App.WebHostEnvironment.WebRootPath, newFile.FilePath!, newFile.Id + newFile.Suffix);
+
+ IImporter importer = new ExcelImporter();
+ var res = await importer.Import(filePath);
+
+ // 删除文件
+ _ = sysFileService.DeleteFile(new DeleteFileInput { Id = newFile.Id });
+
+ if (res == null)
+ throw Oops.Oh("导入数据为空");
+ if (res.Exception != null)
+ throw Oops.Oh("导入异常:" + res.Exception);
+ if (res.TemplateErrors?.Count > 0)
+ throw Oops.Oh("模板异常:" + res.TemplateErrors.Select(x => $"[{x.RequireColumnName}]{x.Message}").Join("\n"));
+
+ return res.Data.ToList();
+ }
+
// 例:List ls = CommonUtil.ParseList(importResult.Data);
///
/// 对象转换 含字典转换
diff --git a/Admin.NET/Admin.NET.Core/Utils/ExcelHelper.cs b/Admin.NET/Admin.NET.Core/Utils/ExcelHelper.cs
new file mode 100644
index 00000000..ebe54ea3
--- /dev/null
+++ b/Admin.NET/Admin.NET.Core/Utils/ExcelHelper.cs
@@ -0,0 +1,120 @@
+// Admin.NET 项目的版权、商标、专利和其他相关权利均受相应法律法规的保护。使用本项目应遵守相关法律法规和许可证的要求。
+//
+// 本项目主要遵循 MIT 许可证和 Apache 许可证(版本 2.0)进行分发和使用。许可证位于源代码树根目录中的 LICENSE-MIT 和 LICENSE-APACHE 文件。
+//
+// 不得利用本项目从事危害国家安全、扰乱社会秩序、侵犯他人合法权益等法律法规禁止的活动!任何基于本项目二次开发而产生的一切法律纠纷和责任,我们不承担任何责任!
+
+using OfficeOpenXml;
+
+namespace Admin.NET.Core;
+
+public class ExcelHelper
+{
+ ///
+ /// 数据导入
+ ///
+ ///
+ ///
+ ///
+ public static IActionResult ImportData(IFormFile file, Action, Action, List, List>> action) where IN : BaseImportInput, new() where T : EntityBaseId, new()
+ {
+ try
+ {
+ var result = CommonUtil.ImportExcelDataAsync(file).Result ?? throw Oops.Oh("有效数据为空");
+
+ var tasks = new List();
+ action.Invoke(result, (storageable, pageItems, rows) =>
+ {
+ // 标记校验信息
+ tasks.Add(Task.Run(() =>
+ {
+ if (storageable.TotalList.Any())
+ {
+ for (int i = 0; i < rows.Count; i++) pageItems[i].Id = rows[i].Id;
+
+ for (int i = 0; i < storageable.TotalList.Count; i++)
+ pageItems[i].Error = storageable.TotalList[i].StorageMessage;
+ }
+ }));
+ });
+
+ // 等待所有标记验证信息任务完成
+ Task.WhenAll(tasks).GetAwaiter().GetResult();
+
+ return ExportData(result);
+ }
+ catch (Exception ex)
+ {
+ App.HttpContext.Response.Headers["Content-Type"] = "application/json; charset=utf-8";
+ throw Oops.Oh(new AdminResult